X
18
8
8
7
2
1
2
1
3
3
3
2
1
2
4
1
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* $OpenBSD: wsevent.c,v 1.26 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: wsevent.c,v 1.16 2003/08/07 16:31:29 agc Exp $ */
/*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou
* for the NetBSD Project.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)event.c 8.1 (Berkeley) 6/11/93
*/
/*
* Internal "wscons_event" queue interface for the keyboard and mouse drivers.
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wseventvar.h>
void filt_wseventdetach(struct knote *);
int filt_wseventread(struct knote *, long);
const struct filterops wsevent_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_wseventdetach,
.f_event = filt_wseventread,
};
/*
* Initialize a wscons_event queue.
*/
int
wsevent_init(struct wseventvar *ev)
{
struct wscons_event *queue;
if (ev->q != NULL)
return (0);
queue = mallocarray(WSEVENT_QSIZE, sizeof(struct wscons_event),
M_DEVBUF, M_WAITOK | M_ZERO);
if (ev->q != NULL) {
free(queue, M_DEVBUF, WSEVENT_QSIZE * sizeof(struct wscons_event));
return (1);
}
ev->q = queue;
ev->get = ev->put = 0;
sigio_init(&ev->sigio);
return (0);
}
/*
* Tear down a wscons_event queue.
*/
void
wsevent_fini(struct wseventvar *ev)
{
if (ev->q == NULL) {
#ifdef DIAGNOSTIC
printf("wsevent_fini: already invoked\n");
#endif
return;
}
free(ev->q, M_DEVBUF, WSEVENT_QSIZE * sizeof(struct wscons_event));
ev->q = NULL;
sigio_free(&ev->sigio);
}
/*
* User-level interface: read, kqueue.
* (User cannot write an event queue.)
*/
int
wsevent_read(struct wseventvar *ev, struct uio *uio, int flags)
{
int s, error;
u_int cnt;
size_t n;
/*
* Make sure we can return at least 1.
*/
if (uio->uio_resid < sizeof(struct wscons_event))
return (EMSGSIZE); /* ??? */
s = splwsevent();
while (ev->get == ev->put) {
if (flags & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
ev->wanted = 1;
error = tsleep_nsec(ev, PWSEVENT | PCATCH,
"wsevent_read", INFSLP);
if (error) {
splx(s);
return (error);
}
}
/*
* Move wscons_event from tail end of queue (there is at least one
* there).
*/
if (ev->put < ev->get)
cnt = WSEVENT_QSIZE - ev->get; /* events in [get..QSIZE) */
else
cnt = ev->put - ev->get; /* events in [get..put) */
splx(s);
n = howmany(uio->uio_resid, sizeof(struct wscons_event));
if (cnt > n)
cnt = n;
error = uiomove((caddr_t)&ev->q[ev->get],
cnt * sizeof(struct wscons_event), uio);
n -= cnt;
/*
* If we do not wrap to 0, used up all our space, or had an error,
* stop. Otherwise move from front of queue to put index, if there
* is anything there to move.
*/
if ((ev->get = (ev->get + cnt) % WSEVENT_QSIZE) != 0 ||
n == 0 || error || (cnt = ev->put) == 0)
return (error);
if (cnt > n)
cnt = n;
error = uiomove((caddr_t)&ev->q[0],
cnt * sizeof(struct wscons_event), uio);
ev->get = cnt;
return (error);
}
int
wsevent_kqfilter(struct wseventvar *ev, struct knote *kn)
{
struct klist *klist;
int s;
klist = &ev->sel.si_note;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &wsevent_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = ev;
s = splwsevent();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
void
filt_wseventdetach(struct knote *kn)
{
struct wseventvar *ev = kn->kn_hook;
struct klist *klist = &ev->sel.si_note;
int s;
s = splwsevent();
klist_remove_locked(klist, kn);
splx(s);
}
int
filt_wseventread(struct knote *kn, long hint)
{
struct wseventvar *ev = kn->kn_hook;
if (ev->get == ev->put)
return (0);
if (ev->get < ev->put)
kn->kn_data = ev->put - ev->get;
else
kn->kn_data = (WSEVENT_QSIZE - ev->get) + ev->put;
return (1);
}
30
13
24
41
15
37
43
12
12
19
19
2
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/* $OpenBSD: mld6.c,v 1.60 2022/09/05 15:47:39 bluhm Exp $ */
/* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */
/*
* Copyright (C) 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1988 Stephen Deering.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Stephen Deering of Stanford University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)igmp.c 8.1 (Berkeley) 7/19/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/mld6.h>
#include <netinet6/mld6_var.h>
static struct ip6_pktopts ip6_opts;
int mld6_timers_are_running; /* [N] shortcut for fast timer */
void mld6_checktimer(struct ifnet *);
static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
void
mld6_init(void)
{
static u_int8_t hbh_buf[8];
struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
mld6_timers_are_running = 0;
/* ip6h_nxt will be fill in later */
hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
/* XXX: grotty hard coding... */
hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
hbh_buf[3] = 0;
hbh_buf[4] = IP6OPT_ROUTER_ALERT;
hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
memcpy(&hbh_buf[6], (caddr_t)&rtalert_code, sizeof(u_int16_t));
ip6_initpktopts(&ip6_opts);
ip6_opts.ip6po_hbh = hbh;
}
void
mld6_start_listening(struct in6_multi *in6m)
{
/* XXX: These are necessary for KAME's link-local hack */
struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
/*
* RFC2710 page 10:
* The node never sends a Report or Done for the link-scope all-nodes
* address.
* MLD messages are never sent for multicast addresses whose scope is 0
* (reserved) or 1 (node-local).
*/
all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx);
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) ||
__IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
__IPV6_ADDR_SCOPE_LINKLOCAL) {
in6m->in6m_timer = 0;
in6m->in6m_state = MLD_OTHERLISTENER;
} else {
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
in6m->in6m_timer =
MLD_RANDOM_DELAY(MLD_V1_MAX_RI *
PR_FASTHZ);
in6m->in6m_state = MLD_IREPORTEDLAST;
mld6_timers_are_running = 1;
}
}
void
mld6_stop_listening(struct in6_multi *in6m)
{
/* XXX: These are necessary for KAME's link-local hack */
struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
struct in6_addr all_routers = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx);
/* XXX: necessary when mrouting */
all_routers.s6_addr16[1] = htons(in6m->in6m_ifidx);
if (in6m->in6m_state == MLD_IREPORTEDLAST &&
(!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes)) &&
__IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
__IPV6_ADDR_SCOPE_INTFACELOCAL)
mld6_sendpkt(in6m, MLD_LISTENER_DONE, &all_routers);
}
void
mld6_input(struct mbuf *m, int off)
{
struct ip6_hdr *ip6;
struct mld_hdr *mldh;
struct ifnet *ifp;
struct in6_multi *in6m;
struct ifmaddr *ifma;
int timer; /* timer value in the MLD query header */
/* XXX: These are necessary for KAME's link-local hack */
struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
if (mldh == NULL) {
icmp6stat_inc(icp6s_tooshort);
return;
}
/* source address validation */
ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
#if 0
char src[INET6_ADDRSTRLEN], grp[INET6_ADDRSTRLEN];
log(LOG_ERR,
"mld_input: src %s is not link-local (grp=%s)\n",
inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)),
inet_ntop(AF_INET6, &mldh->mld_addr, grp, sizeof(grp)));
#endif
/*
* spec (RFC2710) does not explicitly
* specify to discard the packet from a non link-local
* source address. But we believe it's expected to do so.
*/
m_freem(m);
return;
}
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
m_freem(m);
return;
}
/*
* In the MLD6 specification, there are 3 states and a flag.
*
* In Non-Listener state, we simply don't have a membership record.
* In Delaying Listener state, our timer is running (in6m->in6m_timer)
* In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
*
* The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
* we have heard a report from another member, or MLD_IREPORTEDLAST
* if we sent the last report.
*/
switch(mldh->mld_type) {
case MLD_LISTENER_QUERY:
if (ifp->if_flags & IFF_LOOPBACK)
break;
if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
break; /* print error or log stat? */
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
mldh->mld_addr.s6_addr16[1] =
htons(ifp->if_index); /* XXX */
/*
* - Start the timers in all of our membership records
* that the query applies to for the interface on
* which the query arrived excl. those that belong
* to the "all-nodes" group (ff02::1).
* - Restart any timer that is already running but has
* A value longer than the requested timeout.
* - Use the value specified in the query message as
* the maximum timeout.
*/
/*
* XXX: System timer resolution is too low to handle Max
* Response Delay, so set 1 to the internal timer even if
* the calculated value equals to zero when Max Response
* Delay is positive.
*/
timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD_TIMER_SCALE;
if (timer == 0 && mldh->mld_maxdelay)
timer = 1;
all_nodes.s6_addr16[1] = htons(ifp->if_index);
TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
if (ifma->ifma_addr->sa_family != AF_INET6)
continue;
in6m = ifmatoin6m(ifma);
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) ||
__IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
__IPV6_ADDR_SCOPE_LINKLOCAL)
continue;
if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
&in6m->in6m_addr))
{
if (timer == 0) {
/* send a report immediately */
mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
NULL);
in6m->in6m_timer = 0; /* reset timer */
in6m->in6m_state = MLD_IREPORTEDLAST;
} else if (in6m->in6m_timer == 0 || /* idle */
in6m->in6m_timer > timer) {
in6m->in6m_timer =
MLD_RANDOM_DELAY(timer);
mld6_timers_are_running = 1;
}
}
}
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
break;
case MLD_LISTENER_REPORT:
/*
* For fast leave to work, we have to know that we are the
* last person to send a report for this group. Reports
* can potentially get looped back if we are a multicast
* router, so discard reports sourced by me.
* Note that it is impossible to check IFF_LOOPBACK flag of
* ifp for this purpose, since ip6_mloopback pass the physical
* interface to if_input_local().
*/
if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
break;
if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
break;
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
mldh->mld_addr.s6_addr16[1] =
htons(ifp->if_index); /* XXX */
/*
* If we belong to the group being reported, stop
* our timer for that group.
*/
IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
if (in6m) {
in6m->in6m_timer = 0; /* transit to idle state */
in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
}
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
break;
default: /* this is impossible */
#if 0
/*
* this case should be impossible because of filtering in
* icmp6_input(). But we explicitly disabled this part
* just in case.
*/
log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
#endif
break;
}
if_put(ifp);
m_freem(m);
}
void
mld6_fasttimeo(void)
{
struct ifnet *ifp;
/*
* Quick check to see if any work needs to be done, in order
* to minimize the overhead of fasttimo processing.
* Variable mld6_timers_are_running is read atomically, but without
* lock intentionally. In case it is not set due to MP races, we may
* miss to check the timers. Then run the loop at next fast timeout.
*/
if (!mld6_timers_are_running)
return;
NET_LOCK();
mld6_timers_are_running = 0;
TAILQ_FOREACH(ifp, &ifnet, if_list)
mld6_checktimer(ifp);
NET_UNLOCK();
}
void
mld6_checktimer(struct ifnet *ifp)
{
struct in6_multi *in6m;
struct ifmaddr *ifma;
NET_ASSERT_LOCKED();
TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
if (ifma->ifma_addr->sa_family != AF_INET6)
continue;
in6m = ifmatoin6m(ifma);
if (in6m->in6m_timer == 0) {
/* do nothing */
} else if (--in6m->in6m_timer == 0) {
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
in6m->in6m_state = MLD_IREPORTEDLAST;
} else {
mld6_timers_are_running = 1;
}
}
}
static void
mld6_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst)
{
struct mbuf *mh, *md;
struct mld_hdr *mldh;
struct ip6_hdr *ip6;
struct ip6_moptions im6o;
struct in6_ifaddr *ia6;
struct ifnet *ifp;
int ignflags;
ifp = if_get(in6m->in6m_ifidx);
if (ifp == NULL)
return;
/*
* At first, find a link local address on the outgoing interface
* to use as the source address of the MLD packet.
* We do not reject tentative addresses for MLD report to deal with
* the case where we first join a link-local address.
*/
ignflags = IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST;
if ((ia6 = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) {
if_put(ifp);
return;
}
if ((ia6->ia6_flags & IN6_IFF_TENTATIVE))
ia6 = NULL;
/*
* Allocate mbufs to store ip6 header and MLD header.
* We allocate 2 mbufs and make chain in advance because
* it is more convenient when inserting the hop-by-hop option later.
*/
MGETHDR(mh, M_DONTWAIT, MT_HEADER);
if (mh == NULL) {
if_put(ifp);
return;
}
MGET(md, M_DONTWAIT, MT_DATA);
if (md == NULL) {
m_free(mh);
if_put(ifp);
return;
}
mh->m_next = md;
mh->m_pkthdr.ph_ifidx = 0;
mh->m_pkthdr.ph_rtableid = ifp->if_rdomain;
mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
mh->m_len = sizeof(struct ip6_hdr);
m_align(mh, sizeof(struct ip6_hdr));
/* fill in the ip6 header */
ip6 = mtod(mh, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
/* ip6_plen will be set later */
ip6->ip6_nxt = IPPROTO_ICMPV6;
/* ip6_hlim will be set by im6o.im6o_hlim */
ip6->ip6_src = ia6 ? ia6->ia_addr.sin6_addr : in6addr_any;
ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
/* fill in the MLD header */
md->m_len = sizeof(struct mld_hdr);
mldh = mtod(md, struct mld_hdr *);
mldh->mld_type = type;
mldh->mld_code = 0;
mldh->mld_cksum = 0;
/* XXX: we assume the function will not be called for query messages */
mldh->mld_maxdelay = 0;
mldh->mld_reserved = 0;
mldh->mld_addr = in6m->in6m_addr;
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
mh->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
/* construct multicast option */
bzero(&im6o, sizeof(im6o));
im6o.im6o_ifidx = ifp->if_index;
im6o.im6o_hlim = 1;
/*
* Request loopback of the report if we are acting as a multicast
* router, so that the process-level routing daemon can hear it.
*/
#ifdef MROUTING
im6o.im6o_loop = (ip6_mrouter[ifp->if_rdomain] != NULL);
#endif
if_put(ifp);
icmp6stat_inc(icp6s_outhist + type);
ip6_output(mh, &ip6_opts, NULL, ia6 ? 0 : IPV6_UNSPECSRC, &im6o,
NULL);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/* $OpenBSD: video.c,v 1.57 2022/07/02 08:50:41 visa Exp $ */
/*
* Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
* Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/device.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/videoio.h>
#include <dev/video_if.h>
#include <uvm/uvm_extern.h>
#ifdef VIDEO_DEBUG
int video_debug = 1;
#define DPRINTF(l, x...) do { if ((l) <= video_debug) printf(x); } while (0)
#else
#define DPRINTF(l, x...)
#endif
struct video_softc {
struct device dev;
void *hw_hdl; /* hardware driver handle */
struct device *sc_dev; /* hardware device struct */
const struct video_hw_if *hw_if; /* hardware interface */
char sc_dying; /* device detached */
struct process *sc_owner; /* owner process */
uint8_t sc_open; /* device opened */
int sc_fsize;
uint8_t *sc_fbuffer;
caddr_t sc_fbuffer_mmap;
size_t sc_fbufferlen;
int sc_vidmode; /* access mode */
#define VIDMODE_NONE 0
#define VIDMODE_MMAP 1
#define VIDMODE_READ 2
int sc_frames_ready;
struct selinfo sc_rsel; /* read selector */
};
int videoprobe(struct device *, void *, void *);
void videoattach(struct device *, struct device *, void *);
int videodetach(struct device *, int);
int videoactivate(struct device *, int);
int videoprint(void *, const char *);
void video_intr(void *);
int video_stop(struct video_softc *);
int video_claim(struct video_softc *, struct process *);
const struct cfattach video_ca = {
sizeof(struct video_softc), videoprobe, videoattach,
videodetach, videoactivate
};
struct cfdriver video_cd = {
NULL, "video", DV_DULL
};
/*
* Global flag to control if video recording is enabled by kern.video.record.
*/
int video_record_enable = 0;
int
videoprobe(struct device *parent, void *match, void *aux)
{
return (1);
}
void
videoattach(struct device *parent, struct device *self, void *aux)
{
struct video_softc *sc = (void *)self;
struct video_attach_args *sa = aux;
printf("\n");
sc->hw_if = sa->hwif;
sc->hw_hdl = sa->hdl;
sc->sc_dev = parent;
sc->sc_fbufferlen = 0;
sc->sc_owner = NULL;
if (sc->hw_if->get_bufsize)
sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl);
if (sc->sc_fbufferlen == 0) {
printf("video: could not request frame buffer size\n");
return;
}
sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_DEVBUF, M_NOWAIT);
if (sc->sc_fbuffer == NULL) {
printf("video: could not allocate frame buffer\n");
return;
}
}
int
videoopen(dev_t dev, int flags, int fmt, struct proc *p)
{
int unit = VIDEOUNIT(dev);
struct video_softc *sc;
int error = 0;
KERNEL_ASSERT_LOCKED();
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL ||
sc->hw_if == NULL)
return (ENXIO);
if (sc->sc_open) {
DPRINTF(1, "%s: device already open\n", __func__);
return (0);
}
sc->sc_vidmode = VIDMODE_NONE;
sc->sc_frames_ready = 0;
if (sc->hw_if->open != NULL) {
error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
sc->sc_fbuffer, video_intr, sc);
}
if (error == 0) {
sc->sc_open = 1;
DPRINTF(1, "%s: set device to open\n", __func__);
}
return (error);
}
int
videoclose(dev_t dev, int flags, int fmt, struct proc *p)
{
struct video_softc *sc;
int error = 0;
KERNEL_ASSERT_LOCKED();
DPRINTF(1, "%s: last close\n", __func__);
sc = video_cd.cd_devs[VIDEOUNIT(dev)];
error = video_stop(sc);
sc->sc_open = 0;
return (error);
}
int
videoread(dev_t dev, struct uio *uio, int ioflag)
{
int unit = VIDEOUNIT(dev);
struct video_softc *sc;
int error;
size_t size;
KERNEL_ASSERT_LOCKED();
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if (sc->sc_dying)
return (EIO);
if (sc->sc_vidmode == VIDMODE_MMAP)
return (EBUSY);
if ((error = video_claim(sc, curproc->p_p)))
return (error);
/* start the stream if not already started */
if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
error = sc->hw_if->start_read(sc->hw_hdl);
if (error)
return (error);
sc->sc_vidmode = VIDMODE_READ;
}
DPRINTF(1, "resid=%zu\n", uio->uio_resid);
if (sc->sc_frames_ready < 1) {
/* block userland read until a frame is ready */
error = tsleep_nsec(sc, PWAIT | PCATCH, "vid_rd", INFSLP);
if (sc->sc_dying)
error = EIO;
if (error)
return (error);
}
/* move no more than 1 frame to userland, as per specification */
size = ulmin(uio->uio_resid, sc->sc_fsize);
if (!video_record_enable)
bzero(sc->sc_fbuffer, size);
error = uiomove(sc->sc_fbuffer, size, uio);
sc->sc_frames_ready--;
if (error)
return (error);
DPRINTF(1, "uiomove successfully done (%zu bytes)\n", size);
return (0);
}
int
videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
int unit = VIDEOUNIT(dev);
struct video_softc *sc;
struct v4l2_buffer *vb = (struct v4l2_buffer *)data;
int error;
KERNEL_ASSERT_LOCKED();
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
return (ENXIO);
DPRINTF(3, "video_ioctl(%zu, '%c', %zu)\n",
IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff);
error = EOPNOTSUPP;
switch (cmd) {
case VIDIOC_G_CTRL:
if (sc->hw_if->g_ctrl)
error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
(struct v4l2_control *)data);
break;
case VIDIOC_S_CTRL:
if (sc->hw_if->s_ctrl)
error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
(struct v4l2_control *)data);
break;
default:
error = (ENOTTY);
}
if (error != ENOTTY)
return (error);
if ((error = video_claim(sc, p->p_p)))
return (error);
/*
* The following IOCTLs can only be called by the device owner.
* For further shared IOCTLs please move it up.
*/
error = EOPNOTSUPP;
switch (cmd) {
case VIDIOC_QUERYCAP:
if (sc->hw_if->querycap)
error = (sc->hw_if->querycap)(sc->hw_hdl,
(struct v4l2_capability *)data);
break;
case VIDIOC_ENUM_FMT:
if (sc->hw_if->enum_fmt)
error = (sc->hw_if->enum_fmt)(sc->hw_hdl,
(struct v4l2_fmtdesc *)data);
break;
case VIDIOC_ENUM_FRAMESIZES:
if (sc->hw_if->enum_fsizes)
error = (sc->hw_if->enum_fsizes)(sc->hw_hdl,
(struct v4l2_frmsizeenum *)data);
break;
case VIDIOC_ENUM_FRAMEINTERVALS:
if (sc->hw_if->enum_fivals)
error = (sc->hw_if->enum_fivals)(sc->hw_hdl,
(struct v4l2_frmivalenum *)data);
break;
case VIDIOC_S_FMT:
if (!(flags & FWRITE))
return (EACCES);
if (sc->hw_if->s_fmt)
error = (sc->hw_if->s_fmt)(sc->hw_hdl,
(struct v4l2_format *)data);
break;
case VIDIOC_G_FMT:
if (sc->hw_if->g_fmt)
error = (sc->hw_if->g_fmt)(sc->hw_hdl,
(struct v4l2_format *)data);
break;
case VIDIOC_S_PARM:
if (sc->hw_if->s_parm)
error = (sc->hw_if->s_parm)(sc->hw_hdl,
(struct v4l2_streamparm *)data);
break;
case VIDIOC_G_PARM:
if (sc->hw_if->g_parm)
error = (sc->hw_if->g_parm)(sc->hw_hdl,
(struct v4l2_streamparm *)data);
break;
case VIDIOC_ENUMINPUT:
if (sc->hw_if->enum_input)
error = (sc->hw_if->enum_input)(sc->hw_hdl,
(struct v4l2_input *)data);
break;
case VIDIOC_S_INPUT:
if (sc->hw_if->s_input)
error = (sc->hw_if->s_input)(sc->hw_hdl,
(int)*data);
break;
case VIDIOC_G_INPUT:
if (sc->hw_if->g_input)
error = (sc->hw_if->g_input)(sc->hw_hdl,
(int *)data);
break;
case VIDIOC_REQBUFS:
if (sc->hw_if->reqbufs)
error = (sc->hw_if->reqbufs)(sc->hw_hdl,
(struct v4l2_requestbuffers *)data);
break;
case VIDIOC_QUERYBUF:
if (sc->hw_if->querybuf)
error = (sc->hw_if->querybuf)(sc->hw_hdl,
(struct v4l2_buffer *)data);
break;
case VIDIOC_QBUF:
if (sc->hw_if->qbuf)
error = (sc->hw_if->qbuf)(sc->hw_hdl,
(struct v4l2_buffer *)data);
break;
case VIDIOC_DQBUF:
if (!sc->hw_if->dqbuf)
break;
/* should have called mmap() before now */
if (sc->sc_vidmode != VIDMODE_MMAP) {
error = EINVAL;
break;
}
error = (sc->hw_if->dqbuf)(sc->hw_hdl,
(struct v4l2_buffer *)data);
if (!video_record_enable)
bzero(sc->sc_fbuffer_mmap + vb->m.offset, vb->length);
sc->sc_frames_ready--;
break;
case VIDIOC_STREAMON:
if (sc->hw_if->streamon)
error = (sc->hw_if->streamon)(sc->hw_hdl,
(int)*data);
break;
case VIDIOC_STREAMOFF:
if (sc->hw_if->streamoff)
error = (sc->hw_if->streamoff)(sc->hw_hdl,
(int)*data);
if (!error) {
/* Release device ownership and streaming buffers. */
error = video_stop(sc);
}
break;
case VIDIOC_TRY_FMT:
if (sc->hw_if->try_fmt)
error = (sc->hw_if->try_fmt)(sc->hw_hdl,
(struct v4l2_format *)data);
break;
case VIDIOC_QUERYCTRL:
if (sc->hw_if->queryctrl)
error = (sc->hw_if->queryctrl)(sc->hw_hdl,
(struct v4l2_queryctrl *)data);
break;
default:
error = (ENOTTY);
}
return (error);
}
paddr_t
videommap(dev_t dev, off_t off, int prot)
{
int unit = VIDEOUNIT(dev);
struct video_softc *sc;
caddr_t p;
paddr_t pa;
KERNEL_ASSERT_LOCKED();
DPRINTF(2, "%s: off=%lld, prot=%d\n", __func__, off, prot);
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL)
return (-1);
if (sc->sc_dying)
return (-1);
if (sc->hw_if->mappage == NULL)
return (-1);
p = sc->hw_if->mappage(sc->hw_hdl, off, prot);
if (p == NULL)
return (-1);
if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE)
panic("videommap: invalid page");
sc->sc_vidmode = VIDMODE_MMAP;
/* store frame buffer base address for later blanking */
if (off == 0)
sc->sc_fbuffer_mmap = p;
return (pa);
}
void
filt_videodetach(struct knote *kn)
{
struct video_softc *sc = kn->kn_hook;
int s;
s = splhigh();
klist_remove_locked(&sc->sc_rsel.si_note, kn);
splx(s);
}
int
filt_videoread(struct knote *kn, long hint)
{
struct video_softc *sc = kn->kn_hook;
if (sc->sc_frames_ready > 0)
return (1);
return (0);
}
const struct filterops video_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_videodetach,
.f_event = filt_videoread,
};
int
videokqfilter(dev_t dev, struct knote *kn)
{
int unit = VIDEOUNIT(dev);
struct video_softc *sc;
int s, error;
KERNEL_ASSERT_LOCKED();
if (unit >= video_cd.cd_ndevs ||
(sc = video_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if (sc->sc_dying)
return (ENXIO);
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &video_filtops;
kn->kn_hook = sc;
break;
default:
return (EINVAL);
}
if ((error = video_claim(sc, curproc->p_p)))
return (error);
/*
* Start the stream in read() mode if not already started. If
* the user wanted mmap() mode, he should have called mmap()
* before now.
*/
if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
if (sc->hw_if->start_read(sc->hw_hdl))
return (ENXIO);
sc->sc_vidmode = VIDMODE_READ;
}
s = splhigh();
klist_insert_locked(&sc->sc_rsel.si_note, kn);
splx(s);
return (0);
}
int
video_submatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
return (cf->cf_driver == &video_cd);
}
/*
* Called from hardware driver. This is where the MI video driver gets
* probed/attached to the hardware driver
*/
struct device *
video_attach_mi(const struct video_hw_if *rhwp, void *hdlp, struct device *dev)
{
struct video_attach_args arg;
arg.hwif = rhwp;
arg.hdl = hdlp;
return (config_found_sm(dev, &arg, videoprint, video_submatch));
}
void
video_intr(void *addr)
{
struct video_softc *sc = (struct video_softc *)addr;
DPRINTF(3, "video_intr sc=%p\n", sc);
if (sc->sc_vidmode != VIDMODE_NONE)
sc->sc_frames_ready++;
else
printf("%s: interrupt but no streams!\n", __func__);
if (sc->sc_vidmode == VIDMODE_READ)
wakeup(sc);
selwakeup(&sc->sc_rsel);
}
int
video_stop(struct video_softc *sc)
{
int error = 0;
DPRINTF(1, "%s: stream close\n", __func__);
if (sc->hw_if->close != NULL)
error = sc->hw_if->close(sc->hw_hdl);
sc->sc_vidmode = VIDMODE_NONE;
sc->sc_frames_ready = 0;
sc->sc_owner = NULL;
return (error);
}
int
video_claim(struct video_softc *sc, struct process *pr)
{
if (sc->sc_owner != NULL && sc->sc_owner != pr) {
DPRINTF(1, "%s: already owned=%p\n", __func__, sc->sc_owner);
return (EBUSY);
}
if (sc->sc_owner == NULL) {
sc->sc_owner = pr;
DPRINTF(1, "%s: new owner=%p\n", __func__, sc->sc_owner);
}
return (0);
}
int
videoprint(void *aux, const char *pnp)
{
if (pnp != NULL)
printf("video at %s", pnp);
return (UNCONF);
}
int
videodetach(struct device *self, int flags)
{
struct video_softc *sc = (struct video_softc *)self;
int s, maj, mn;
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == videoopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
s = splhigh();
klist_invalidate(&sc->sc_rsel.si_note);
splx(s);
free(sc->sc_fbuffer, M_DEVBUF, sc->sc_fbufferlen);
return (0);
}
int
videoactivate(struct device *self, int act)
{
struct video_softc *sc = (struct video_softc *)self;
switch (act) {
case DVACT_DEACTIVATE:
sc->sc_dying = 1;
break;
}
return (0);
}
18
18
18
18
18
17
1
256
352
78
41
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/* $OpenBSD: kern_task.c,v 1.33 2022/08/15 11:38:35 mvs Exp $ */
/*
* Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/kthread.h>
#include <sys/task.h>
#include <sys/proc.h>
#include <sys/witness.h>
#include "kcov.h"
#if NKCOV > 0
#include <sys/kcov.h>
#endif
#ifdef WITNESS
static struct lock_type taskq_lock_type = {
.lt_name = "taskq"
};
#define TASKQ_LOCK_FLAGS LO_WITNESS | LO_INITIALIZED | LO_SLEEPABLE | \
(LO_CLASS_RWLOCK << LO_CLASSSHIFT)
#endif /* WITNESS */
struct taskq_thread {
SLIST_ENTRY(taskq_thread)
tt_entry;
struct proc *tt_thread;
};
SLIST_HEAD(taskq_threads, taskq_thread);
struct taskq {
enum {
TQ_S_CREATED,
TQ_S_RUNNING,
TQ_S_DESTROYED
} tq_state;
unsigned int tq_running;
unsigned int tq_nthreads;
unsigned int tq_flags;
const char *tq_name;
struct mutex tq_mtx;
struct task_list tq_worklist;
struct taskq_threads tq_threads;
unsigned int tq_barriers;
unsigned int tq_bgen;
unsigned int tq_bthreads;
#ifdef WITNESS
struct lock_object tq_lock_object;
#endif
};
static const char taskq_sys_name[] = "systq";
struct taskq taskq_sys = {
.tq_state = TQ_S_CREATED,
.tq_running = 0,
.tq_nthreads = 1,
.tq_flags = 0,
.tq_name = taskq_sys_name,
.tq_mtx = MUTEX_INITIALIZER_FLAGS(IPL_HIGH,
taskq_sys_name, 0),
.tq_worklist = TAILQ_HEAD_INITIALIZER(taskq_sys.tq_worklist),
.tq_threads = SLIST_HEAD_INITIALIZER(taskq_sys.tq_threads),
.tq_barriers = 0,
.tq_bgen = 0,
.tq_bthreads = 0,
#ifdef WITNESS
.tq_lock_object = {
.lo_name = taskq_sys_name,
.lo_flags = TASKQ_LOCK_FLAGS,
},
#endif
};
static const char taskq_sys_mp_name[] = "systqmp";
struct taskq taskq_sys_mp = {
.tq_state = TQ_S_CREATED,
.tq_running = 0,
.tq_nthreads = 1,
.tq_flags = TASKQ_MPSAFE,
.tq_name = taskq_sys_mp_name,
.tq_mtx = MUTEX_INITIALIZER_FLAGS(IPL_HIGH,
taskq_sys_mp_name, 0),
.tq_worklist = TAILQ_HEAD_INITIALIZER(taskq_sys_mp.tq_worklist),
.tq_threads = SLIST_HEAD_INITIALIZER(taskq_sys_mp.tq_threads),
.tq_barriers = 0,
.tq_bgen = 0,
.tq_bthreads = 0,
#ifdef WITNESS
.tq_lock_object = {
.lo_name = taskq_sys_mp_name,
.lo_flags = TASKQ_LOCK_FLAGS,
},
#endif
};
struct taskq *const systq = &taskq_sys;
struct taskq *const systqmp = &taskq_sys_mp;
void taskq_init(void); /* called in init_main.c */
void taskq_create_thread(void *);
void taskq_barrier_task(void *);
int taskq_sleep(const volatile void *, struct mutex *, int,
const char *, int);
int taskq_next_work(struct taskq *, struct task *);
void taskq_thread(void *);
void
taskq_init(void)
{
WITNESS_INIT(&systq->tq_lock_object, &taskq_lock_type);
kthread_create_deferred(taskq_create_thread, systq);
WITNESS_INIT(&systqmp->tq_lock_object, &taskq_lock_type);
kthread_create_deferred(taskq_create_thread, systqmp);
}
struct taskq *
taskq_create(const char *name, unsigned int nthreads, int ipl,
unsigned int flags)
{
struct taskq *tq;
tq = malloc(sizeof(*tq), M_DEVBUF, M_WAITOK);
if (tq == NULL)
return (NULL);
tq->tq_state = TQ_S_CREATED;
tq->tq_running = 0;
tq->tq_nthreads = nthreads;
tq->tq_name = name;
tq->tq_flags = flags;
mtx_init_flags(&tq->tq_mtx, ipl, name, 0);
TAILQ_INIT(&tq->tq_worklist);
SLIST_INIT(&tq->tq_threads);
tq->tq_barriers = 0;
tq->tq_bgen = 0;
tq->tq_bthreads = 0;
#ifdef WITNESS
memset(&tq->tq_lock_object, 0, sizeof(tq->tq_lock_object));
tq->tq_lock_object.lo_name = name;
tq->tq_lock_object.lo_flags = TASKQ_LOCK_FLAGS;
witness_init(&tq->tq_lock_object, &taskq_lock_type);
#endif
/* try to create a thread to guarantee that tasks will be serviced */
kthread_create_deferred(taskq_create_thread, tq);
return (tq);
}
void
taskq_destroy(struct taskq *tq)
{
mtx_enter(&tq->tq_mtx);
switch (tq->tq_state) {
case TQ_S_CREATED:
/* tq is still referenced by taskq_create_thread */
tq->tq_state = TQ_S_DESTROYED;
mtx_leave(&tq->tq_mtx);
return;
case TQ_S_RUNNING:
tq->tq_state = TQ_S_DESTROYED;
break;
default:
panic("unexpected %s tq state %u", tq->tq_name, tq->tq_state);
}
while (tq->tq_running > 0) {
wakeup(tq);
msleep_nsec(&tq->tq_running, &tq->tq_mtx, PWAIT, "tqdestroy",
INFSLP);
}
mtx_leave(&tq->tq_mtx);
free(tq, M_DEVBUF, sizeof(*tq));
}
void
taskq_create_thread(void *arg)
{
struct taskq *tq = arg;
int rv;
mtx_enter(&tq->tq_mtx);
switch (tq->tq_state) {
case TQ_S_DESTROYED:
mtx_leave(&tq->tq_mtx);
free(tq, M_DEVBUF, sizeof(*tq));
return;
case TQ_S_CREATED:
tq->tq_state = TQ_S_RUNNING;
break;
default:
panic("unexpected %s tq state %d", tq->tq_name, tq->tq_state);
}
do {
tq->tq_running++;
mtx_leave(&tq->tq_mtx);
rv = kthread_create(taskq_thread, tq, NULL, tq->tq_name);
mtx_enter(&tq->tq_mtx);
if (rv != 0) {
printf("unable to create thread for \"%s\" taskq\n",
tq->tq_name);
tq->tq_running--;
/* could have been destroyed during kthread_create */
if (tq->tq_state == TQ_S_DESTROYED &&
tq->tq_running == 0)
wakeup_one(&tq->tq_running);
break;
}
} while (tq->tq_running < tq->tq_nthreads);
mtx_leave(&tq->tq_mtx);
}
void
taskq_barrier_task(void *p)
{
struct taskq *tq = p;
unsigned int gen;
mtx_enter(&tq->tq_mtx);
tq->tq_bthreads++;
wakeup(&tq->tq_bthreads);
gen = tq->tq_bgen;
do {
msleep_nsec(&tq->tq_bgen, &tq->tq_mtx,
PWAIT, "tqbarend", INFSLP);
} while (gen == tq->tq_bgen);
mtx_leave(&tq->tq_mtx);
}
static void
taskq_do_barrier(struct taskq *tq)
{
struct task t = TASK_INITIALIZER(taskq_barrier_task, tq);
struct proc *thread = curproc;
struct taskq_thread *tt;
mtx_enter(&tq->tq_mtx);
tq->tq_barriers++;
/* is the barrier being run from a task inside the taskq? */
SLIST_FOREACH(tt, &tq->tq_threads, tt_entry) {
if (tt->tt_thread == thread) {
tq->tq_bthreads++;
wakeup(&tq->tq_bthreads);
break;
}
}
while (tq->tq_bthreads < tq->tq_nthreads) {
/* shove the task into the queue for a worker to pick up */
SET(t.t_flags, TASK_ONQUEUE);
TAILQ_INSERT_TAIL(&tq->tq_worklist, &t, t_entry);
wakeup_one(tq);
msleep_nsec(&tq->tq_bthreads, &tq->tq_mtx,
PWAIT, "tqbar", INFSLP);
/*
* another thread running a barrier might have
* done this work for us.
*/
if (ISSET(t.t_flags, TASK_ONQUEUE))
TAILQ_REMOVE(&tq->tq_worklist, &t, t_entry);
}
if (--tq->tq_barriers == 0) {
/* we're the last one out */
tq->tq_bgen++;
wakeup(&tq->tq_bgen);
tq->tq_bthreads = 0;
} else {
unsigned int gen = tq->tq_bgen;
do {
msleep_nsec(&tq->tq_bgen, &tq->tq_mtx,
PWAIT, "tqbarwait", INFSLP);
} while (gen == tq->tq_bgen);
}
mtx_leave(&tq->tq_mtx);
}
void
taskq_barrier(struct taskq *tq)
{
WITNESS_CHECKORDER(&tq->tq_lock_object, LOP_NEWORDER, NULL);
taskq_do_barrier(tq);
}
void
taskq_del_barrier(struct taskq *tq, struct task *t)
{
WITNESS_CHECKORDER(&tq->tq_lock_object, LOP_NEWORDER, NULL);
if (task_del(tq, t))
return;
taskq_do_barrier(tq);
}
void
task_set(struct task *t, void (*fn)(void *), void *arg)
{
t->t_func = fn;
t->t_arg = arg;
t->t_flags = 0;
}
int
task_add(struct taskq *tq, struct task *w)
{
int rv = 0;
if (ISSET(w->t_flags, TASK_ONQUEUE))
return (0);
mtx_enter(&tq->tq_mtx);
if (!ISSET(w->t_flags, TASK_ONQUEUE)) {
rv = 1;
SET(w->t_flags, TASK_ONQUEUE);
TAILQ_INSERT_TAIL(&tq->tq_worklist, w, t_entry);
#if NKCOV > 0
w->t_process = curproc->p_p;
#endif
}
mtx_leave(&tq->tq_mtx);
if (rv)
wakeup_one(tq);
return (rv);
}
int
task_del(struct taskq *tq, struct task *w)
{
int rv = 0;
if (!ISSET(w->t_flags, TASK_ONQUEUE))
return (0);
mtx_enter(&tq->tq_mtx);
if (ISSET(w->t_flags, TASK_ONQUEUE)) {
rv = 1;
CLR(w->t_flags, TASK_ONQUEUE);
TAILQ_REMOVE(&tq->tq_worklist, w, t_entry);
}
mtx_leave(&tq->tq_mtx);
return (rv);
}
int
taskq_next_work(struct taskq *tq, struct task *work)
{
struct task *next;
mtx_enter(&tq->tq_mtx);
while ((next = TAILQ_FIRST(&tq->tq_worklist)) == NULL) {
if (tq->tq_state != TQ_S_RUNNING) {
mtx_leave(&tq->tq_mtx);
return (0);
}
msleep_nsec(tq, &tq->tq_mtx, PWAIT, "bored", INFSLP);
}
TAILQ_REMOVE(&tq->tq_worklist, next, t_entry);
CLR(next->t_flags, TASK_ONQUEUE);
*work = *next; /* copy to caller to avoid races */
next = TAILQ_FIRST(&tq->tq_worklist);
mtx_leave(&tq->tq_mtx);
if (next != NULL && tq->tq_nthreads > 1)
wakeup_one(tq);
return (1);
}
void
taskq_thread(void *xtq)
{
struct taskq_thread self = { .tt_thread = curproc };
struct taskq *tq = xtq;
struct task work;
int last;
if (ISSET(tq->tq_flags, TASKQ_MPSAFE))
KERNEL_UNLOCK();
mtx_enter(&tq->tq_mtx);
SLIST_INSERT_HEAD(&tq->tq_threads, &self, tt_entry);
mtx_leave(&tq->tq_mtx);
WITNESS_CHECKORDER(&tq->tq_lock_object, LOP_NEWORDER, NULL);
while (taskq_next_work(tq, &work)) {
WITNESS_LOCK(&tq->tq_lock_object, 0);
#if NKCOV > 0
kcov_remote_enter(KCOV_REMOTE_COMMON, work.t_process);
#endif
(*work.t_func)(work.t_arg);
#if NKCOV > 0
kcov_remote_leave(KCOV_REMOTE_COMMON, work.t_process);
#endif
WITNESS_UNLOCK(&tq->tq_lock_object, 0);
sched_pause(yield);
}
mtx_enter(&tq->tq_mtx);
SLIST_REMOVE(&tq->tq_threads, &self, taskq_thread, tt_entry);
last = (--tq->tq_running == 0);
mtx_leave(&tq->tq_mtx);
if (ISSET(tq->tq_flags, TASKQ_MPSAFE))
KERNEL_LOCK();
if (last)
wakeup_one(&tq->tq_running);
kthread_exit(0);
}
220
207
14
14
14
236
14
4
6
1
1
13
13
13
7
7
4
3
1
3
1
1
1
13
13
3
3
2
1
15
12
20
2
35
23
8
22
6
17
11
11
6
20
6
34
3
9
158
15
96
218
53
191
191
217
214
215
1
1
6
6
5
5
138
2
107
2
23
4
27
67
100
3
94
25
102
87
3
67
38
8
29
2
31
5
98
327
327
250
324
326
281
282
90
15
27
264
32
268
24
21
14
245
20
20
9
24
259
64
1
18
23
10
73
227
153
1
24
43
7
3
213
34
221
12
238
58
242
63
46
22
12
11
200
39
97
44
152
5
3
150
152
87
1
1
148
22
61
6
89
9
32
109
52
152
168
206
60
152
2
2
12
15
15
11
5
15
15
14
1
15
194
126
295
10
9
139
215
214
3
54
257
257
118
283
240
241
241
41
73
74
11
46
14
4
20
14
23
14
279
277
279
19
69
259
236
24
158
141
16
158
158
137
26
156
22
138
283
27
27
33
77
65
123
22
86
123
121
24
185
185
29
32
51
54
54
23
24
24
59
24
59
24
7
7
32
32
19
70
6
6
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
/* $OpenBSD: kern_event.c,v 1.193 2022/08/14 01:58:27 jsg Exp $ */
/*-
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/kern/kern_event.c,v 1.22 2001/02/23 20:32:42 jlemon Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/pledge.h>
#include <sys/malloc.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/fcntl.h>
#include <sys/queue.h>
#include <sys/event.h>
#include <sys/eventvar.h>
#include <sys/ktrace.h>
#include <sys/pool.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/vnode.h>
#include <sys/wait.h>
#ifdef DIAGNOSTIC
#define KLIST_ASSERT_LOCKED(kl) do { \
if ((kl)->kl_ops != NULL) \
(kl)->kl_ops->klo_assertlk((kl)->kl_arg); \
else \
KERNEL_ASSERT_LOCKED(); \
} while (0)
#else
#define KLIST_ASSERT_LOCKED(kl) ((void)(kl))
#endif
struct kqueue *kqueue_alloc(struct filedesc *);
void kqueue_terminate(struct proc *p, struct kqueue *);
void KQREF(struct kqueue *);
void KQRELE(struct kqueue *);
void kqueue_purge(struct proc *, struct kqueue *);
int kqueue_sleep(struct kqueue *, struct timespec *);
int kqueue_read(struct file *, struct uio *, int);
int kqueue_write(struct file *, struct uio *, int);
int kqueue_ioctl(struct file *fp, u_long com, caddr_t data,
struct proc *p);
int kqueue_kqfilter(struct file *fp, struct knote *kn);
int kqueue_stat(struct file *fp, struct stat *st, struct proc *p);
int kqueue_close(struct file *fp, struct proc *p);
void kqueue_wakeup(struct kqueue *kq);
#ifdef KQUEUE_DEBUG
void kqueue_do_check(struct kqueue *kq, const char *func, int line);
#define kqueue_check(kq) kqueue_do_check((kq), __func__, __LINE__)
#else
#define kqueue_check(kq) do {} while (0)
#endif
static int filter_attach(struct knote *kn);
static void filter_detach(struct knote *kn);
static int filter_event(struct knote *kn, long hint);
static int filter_modify(struct kevent *kev, struct knote *kn);
static int filter_process(struct knote *kn, struct kevent *kev);
static void kqueue_expand_hash(struct kqueue *kq);
static void kqueue_expand_list(struct kqueue *kq, int fd);
static void kqueue_task(void *);
static int klist_lock(struct klist *);
static void klist_unlock(struct klist *, int);
const struct fileops kqueueops = {
.fo_read = kqueue_read,
.fo_write = kqueue_write,
.fo_ioctl = kqueue_ioctl,
.fo_kqfilter = kqueue_kqfilter,
.fo_stat = kqueue_stat,
.fo_close = kqueue_close
};
void knote_attach(struct knote *kn);
void knote_detach(struct knote *kn);
void knote_drop(struct knote *kn, struct proc *p);
void knote_enqueue(struct knote *kn);
void knote_dequeue(struct knote *kn);
int knote_acquire(struct knote *kn, struct klist *, int);
void knote_release(struct knote *kn);
void knote_activate(struct knote *kn);
void knote_remove(struct proc *p, struct kqueue *kq, struct knlist **plist,
int idx, int purge);
void filt_kqdetach(struct knote *kn);
int filt_kqueue(struct knote *kn, long hint);
int filt_kqueuemodify(struct kevent *kev, struct knote *kn);
int filt_kqueueprocess(struct knote *kn, struct kevent *kev);
int filt_kqueue_common(struct knote *kn, struct kqueue *kq);
int filt_procattach(struct knote *kn);
void filt_procdetach(struct knote *kn);
int filt_proc(struct knote *kn, long hint);
int filt_fileattach(struct knote *kn);
void filt_timerexpire(void *knx);
int filt_timerattach(struct knote *kn);
void filt_timerdetach(struct knote *kn);
int filt_timermodify(struct kevent *kev, struct knote *kn);
int filt_timerprocess(struct knote *kn, struct kevent *kev);
void filt_seltruedetach(struct knote *kn);
const struct filterops kqread_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_kqdetach,
.f_event = filt_kqueue,
.f_modify = filt_kqueuemodify,
.f_process = filt_kqueueprocess,
};
const struct filterops proc_filtops = {
.f_flags = 0,
.f_attach = filt_procattach,
.f_detach = filt_procdetach,
.f_event = filt_proc,
};
const struct filterops file_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = filt_fileattach,
.f_detach = NULL,
.f_event = NULL,
};
const struct filterops timer_filtops = {
.f_flags = 0,
.f_attach = filt_timerattach,
.f_detach = filt_timerdetach,
.f_event = NULL,
.f_modify = filt_timermodify,
.f_process = filt_timerprocess,
};
struct pool knote_pool;
struct pool kqueue_pool;
struct mutex kqueue_klist_lock = MUTEX_INITIALIZER(IPL_MPFLOOR);
int kq_ntimeouts = 0;
int kq_timeoutmax = (4 * 1024);
#define KN_HASH(val, mask) (((val) ^ (val >> 8)) & (mask))
/*
* Table for for all system-defined filters.
*/
const struct filterops *const sysfilt_ops[] = {
&file_filtops, /* EVFILT_READ */
&file_filtops, /* EVFILT_WRITE */
NULL, /*&aio_filtops,*/ /* EVFILT_AIO */
&file_filtops, /* EVFILT_VNODE */
&proc_filtops, /* EVFILT_PROC */
&sig_filtops, /* EVFILT_SIGNAL */
&timer_filtops, /* EVFILT_TIMER */
&file_filtops, /* EVFILT_DEVICE */
&file_filtops, /* EVFILT_EXCEPT */
};
void
KQREF(struct kqueue *kq)
{
refcnt_take(&kq->kq_refcnt);
}
void
KQRELE(struct kqueue *kq)
{
struct filedesc *fdp;
if (refcnt_rele(&kq->kq_refcnt) == 0)
return;
fdp = kq->kq_fdp;
if (rw_status(&fdp->fd_lock) == RW_WRITE) {
LIST_REMOVE(kq, kq_next);
} else {
fdplock(fdp);
LIST_REMOVE(kq, kq_next);
fdpunlock(fdp);
}
KASSERT(TAILQ_EMPTY(&kq->kq_head));
KASSERT(kq->kq_nknotes == 0);
free(kq->kq_knlist, M_KEVENT, kq->kq_knlistsize *
sizeof(struct knlist));
hashfree(kq->kq_knhash, KN_HASHSIZE, M_KEVENT);
klist_free(&kq->kq_klist);
pool_put(&kqueue_pool, kq);
}
void
kqueue_init(void)
{
pool_init(&kqueue_pool, sizeof(struct kqueue), 0, IPL_MPFLOOR,
PR_WAITOK, "kqueuepl", NULL);
pool_init(&knote_pool, sizeof(struct knote), 0, IPL_MPFLOOR,
PR_WAITOK, "knotepl", NULL);
}
void
kqueue_init_percpu(void)
{
pool_cache_init(&knote_pool);
}
int
filt_fileattach(struct knote *kn)
{
struct file *fp = kn->kn_fp;
return fp->f_ops->fo_kqfilter(fp, kn);
}
int
kqueue_kqfilter(struct file *fp, struct knote *kn)
{
struct kqueue *kq = kn->kn_fp->f_data;
if (kn->kn_filter != EVFILT_READ)
return (EINVAL);
kn->kn_fop = &kqread_filtops;
klist_insert(&kq->kq_klist, kn);
return (0);
}
void
filt_kqdetach(struct knote *kn)
{
struct kqueue *kq = kn->kn_fp->f_data;
klist_remove(&kq->kq_klist, kn);
}
int
filt_kqueue_common(struct knote *kn, struct kqueue *kq)
{
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
kn->kn_data = kq->kq_count;
return (kn->kn_data > 0);
}
int
filt_kqueue(struct knote *kn, long hint)
{
struct kqueue *kq = kn->kn_fp->f_data;
int active;
mtx_enter(&kq->kq_lock);
active = filt_kqueue_common(kn, kq);
mtx_leave(&kq->kq_lock);
return (active);
}
int
filt_kqueuemodify(struct kevent *kev, struct knote *kn)
{
struct kqueue *kq = kn->kn_fp->f_data;
int active;
mtx_enter(&kq->kq_lock);
knote_assign(kev, kn);
active = filt_kqueue_common(kn, kq);
mtx_leave(&kq->kq_lock);
return (active);
}
int
filt_kqueueprocess(struct knote *kn, struct kevent *kev)
{
struct kqueue *kq = kn->kn_fp->f_data;
int active;
mtx_enter(&kq->kq_lock);
if (kev != NULL && (kn->kn_flags & EV_ONESHOT))
active = 1;
else
active = filt_kqueue_common(kn, kq);
if (active)
knote_submit(kn, kev);
mtx_leave(&kq->kq_lock);
return (active);
}
int
filt_procattach(struct knote *kn)
{
struct process *pr;
int s;
if ((curproc->p_p->ps_flags & PS_PLEDGE) &&
(curproc->p_p->ps_pledge & PLEDGE_PROC) == 0)
return pledge_fail(curproc, EPERM, PLEDGE_PROC);
if (kn->kn_id > PID_MAX)
return ESRCH;
pr = prfind(kn->kn_id);
if (pr == NULL)
return (ESRCH);
/* exiting processes can't be specified */
if (pr->ps_flags & PS_EXITING)
return (ESRCH);
kn->kn_ptr.p_process = pr;
kn->kn_flags |= EV_CLEAR; /* automatically set */
/*
* internal flag indicating registration done by kernel
*/
if (kn->kn_flags & EV_FLAG1) {
kn->kn_data = kn->kn_sdata; /* ppid */
kn->kn_fflags = NOTE_CHILD;
kn->kn_flags &= ~EV_FLAG1;
}
s = splhigh();
klist_insert_locked(&pr->ps_klist, kn);
splx(s);
return (0);
}
/*
* The knote may be attached to a different process, which may exit,
* leaving nothing for the knote to be attached to. So when the process
* exits, the knote is marked as DETACHED and also flagged as ONESHOT so
* it will be deleted when read out. However, as part of the knote deletion,
* this routine is called, so a check is needed to avoid actually performing
* a detach, because the original process does not exist any more.
*/
void
filt_procdetach(struct knote *kn)
{
struct kqueue *kq = kn->kn_kq;
struct process *pr = kn->kn_ptr.p_process;
int s, status;
mtx_enter(&kq->kq_lock);
status = kn->kn_status;
mtx_leave(&kq->kq_lock);
if (status & KN_DETACHED)
return;
s = splhigh();
klist_remove_locked(&pr->ps_klist, kn);
splx(s);
}
int
filt_proc(struct knote *kn, long hint)
{
struct kqueue *kq = kn->kn_kq;
u_int event;
/*
* mask off extra data
*/
event = (u_int)hint & NOTE_PCTRLMASK;
/*
* if the user is interested in this event, record it.
*/
if (kn->kn_sfflags & event)
kn->kn_fflags |= event;
/*
* process is gone, so flag the event as finished and remove it
* from the process's klist
*/
if (event == NOTE_EXIT) {
struct process *pr = kn->kn_ptr.p_process;
int s;
mtx_enter(&kq->kq_lock);
kn->kn_status |= KN_DETACHED;
mtx_leave(&kq->kq_lock);
s = splhigh();
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
kn->kn_data = W_EXITCODE(pr->ps_xexit, pr->ps_xsig);
klist_remove_locked(&pr->ps_klist, kn);
splx(s);
return (1);
}
/*
* process forked, and user wants to track the new process,
* so attach a new knote to it, and immediately report an
* event with the parent's pid.
*/
if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) {
struct kevent kev;
int error;
/*
* register knote with new process.
*/
memset(&kev, 0, sizeof(kev));
kev.ident = hint & NOTE_PDATAMASK; /* pid */
kev.filter = kn->kn_filter;
kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1;
kev.fflags = kn->kn_sfflags;
kev.data = kn->kn_id; /* parent */
kev.udata = kn->kn_udata; /* preserve udata */
error = kqueue_register(kq, &kev, 0, NULL);
if (error)
kn->kn_fflags |= NOTE_TRACKERR;
}
return (kn->kn_fflags != 0);
}
static void
filt_timer_timeout_add(struct knote *kn)
{
struct timeval tv;
struct timeout *to = kn->kn_hook;
int tticks;
tv.tv_sec = kn->kn_sdata / 1000;
tv.tv_usec = (kn->kn_sdata % 1000) * 1000;
tticks = tvtohz(&tv);
/* Remove extra tick from tvtohz() if timeout has fired before. */
if (timeout_triggered(to))
tticks--;
timeout_add(to, (tticks > 0) ? tticks : 1);
}
void
filt_timerexpire(void *knx)
{
struct knote *kn = knx;
struct kqueue *kq = kn->kn_kq;
kn->kn_data++;
mtx_enter(&kq->kq_lock);
knote_activate(kn);
mtx_leave(&kq->kq_lock);
if ((kn->kn_flags & EV_ONESHOT) == 0)
filt_timer_timeout_add(kn);
}
/*
* data contains amount of time to sleep, in milliseconds
*/
int
filt_timerattach(struct knote *kn)
{
struct timeout *to;
if (kq_ntimeouts > kq_timeoutmax)
return (ENOMEM);
kq_ntimeouts++;
kn->kn_flags |= EV_CLEAR; /* automatically set */
to = malloc(sizeof(*to), M_KEVENT, M_WAITOK);
timeout_set(to, filt_timerexpire, kn);
kn->kn_hook = to;
filt_timer_timeout_add(kn);
return (0);
}
void
filt_timerdetach(struct knote *kn)
{
struct timeout *to;
to = (struct timeout *)kn->kn_hook;
timeout_del_barrier(to);
free(to, M_KEVENT, sizeof(*to));
kq_ntimeouts--;
}
int
filt_timermodify(struct kevent *kev, struct knote *kn)
{
struct kqueue *kq = kn->kn_kq;
struct timeout *to = kn->kn_hook;
/* Reset the timer. Any pending events are discarded. */
timeout_del_barrier(to);
mtx_enter(&kq->kq_lock);
if (kn->kn_status & KN_QUEUED)
knote_dequeue(kn);
kn->kn_status &= ~KN_ACTIVE;
mtx_leave(&kq->kq_lock);
kn->kn_data = 0;
knote_assign(kev, kn);
/* Reinit timeout to invoke tick adjustment again. */
timeout_set(to, filt_timerexpire, kn);
filt_timer_timeout_add(kn);
return (0);
}
int
filt_timerprocess(struct knote *kn, struct kevent *kev)
{
int active, s;
s = splsoftclock();
active = (kn->kn_data != 0);
if (active)
knote_submit(kn, kev);
splx(s);
return (active);
}
/*
* filt_seltrue:
*
* This filter "event" routine simulates seltrue().
*/
int
filt_seltrue(struct knote *kn, long hint)
{
/*
* We don't know how much data can be read/written,
* but we know that it *can* be. This is about as
* good as select/poll does as well.
*/
kn->kn_data = 0;
return (1);
}
int
filt_seltruemodify(struct kevent *kev, struct knote *kn)
{
knote_assign(kev, kn);
return (kn->kn_fop->f_event(kn, 0));
}
int
filt_seltrueprocess(struct knote *kn, struct kevent *kev)
{
int active;
active = kn->kn_fop->f_event(kn, 0);
if (active)
knote_submit(kn, kev);
return (active);
}
/*
* This provides full kqfilter entry for device switch tables, which
* has same effect as filter using filt_seltrue() as filter method.
*/
void
filt_seltruedetach(struct knote *kn)
{
/* Nothing to do */
}
const struct filterops seltrue_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_seltruedetach,
.f_event = filt_seltrue,
.f_modify = filt_seltruemodify,
.f_process = filt_seltrueprocess,
};
int
seltrue_kqfilter(dev_t dev, struct knote *kn)
{
switch (kn->kn_filter) {
case EVFILT_READ:
case EVFILT_WRITE:
kn->kn_fop = &seltrue_filtops;
break;
default:
return (EINVAL);
}
/* Nothing more to do */
return (0);
}
static int
filt_dead(struct knote *kn, long hint)
{
if (kn->kn_filter == EVFILT_EXCEPT) {
/*
* Do not deliver event because there is no out-of-band data.
* However, let HUP condition pass for poll(2).
*/
if ((kn->kn_flags & __EV_POLL) == 0) {
kn->kn_flags |= EV_DISABLE;
return (0);
}
}
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
if (kn->kn_flags & __EV_POLL)
kn->kn_flags |= __EV_HUP;
kn->kn_data = 0;
return (1);
}
static void
filt_deaddetach(struct knote *kn)
{
/* Nothing to do */
}
const struct filterops dead_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_deaddetach,
.f_event = filt_dead,
.f_modify = filt_seltruemodify,
.f_process = filt_seltrueprocess,
};
static int
filt_badfd(struct knote *kn, long hint)
{
kn->kn_flags |= (EV_ERROR | EV_ONESHOT);
kn->kn_data = EBADF;
return (1);
}
/* For use with kqpoll. */
const struct filterops badfd_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_deaddetach,
.f_event = filt_badfd,
.f_modify = filt_seltruemodify,
.f_process = filt_seltrueprocess,
};
static int
filter_attach(struct knote *kn)
{
int error;
if (kn->kn_fop->f_flags & FILTEROP_MPSAFE) {
error = kn->kn_fop->f_attach(kn);
} else {
KERNEL_LOCK();
error = kn->kn_fop->f_attach(kn);
KERNEL_UNLOCK();
}
return (error);
}
static void
filter_detach(struct knote *kn)
{
if (kn->kn_fop->f_flags & FILTEROP_MPSAFE) {
kn->kn_fop->f_detach(kn);
} else {
KERNEL_LOCK();
kn->kn_fop->f_detach(kn);
KERNEL_UNLOCK();
}
}
static int
filter_event(struct knote *kn, long hint)
{
if ((kn->kn_fop->f_flags & FILTEROP_MPSAFE) == 0)
KERNEL_ASSERT_LOCKED();
return (kn->kn_fop->f_event(kn, hint));
}
static int
filter_modify(struct kevent *kev, struct knote *kn)
{
int active, s;
if (kn->kn_fop->f_flags & FILTEROP_MPSAFE) {
active = kn->kn_fop->f_modify(kev, kn);
} else {
KERNEL_LOCK();
if (kn->kn_fop->f_modify != NULL) {
active = kn->kn_fop->f_modify(kev, kn);
} else {
s = splhigh();
active = knote_modify(kev, kn);
splx(s);
}
KERNEL_UNLOCK();
}
return (active);
}
static int
filter_process(struct knote *kn, struct kevent *kev)
{
int active, s;
if (kn->kn_fop->f_flags & FILTEROP_MPSAFE) {
active = kn->kn_fop->f_process(kn, kev);
} else {
KERNEL_LOCK();
if (kn->kn_fop->f_process != NULL) {
active = kn->kn_fop->f_process(kn, kev);
} else {
s = splhigh();
active = knote_process(kn, kev);
splx(s);
}
KERNEL_UNLOCK();
}
return (active);
}
/*
* Initialize the current thread for poll/select system call.
* num indicates the number of serials that the system call may utilize.
* After this function, the valid range of serials is
* p_kq_serial <= x < p_kq_serial + num.
*/
void
kqpoll_init(unsigned int num)
{
struct proc *p = curproc;
struct filedesc *fdp;
if (p->p_kq == NULL) {
p->p_kq = kqueue_alloc(p->p_fd);
p->p_kq_serial = arc4random();
fdp = p->p_fd;
fdplock(fdp);
LIST_INSERT_HEAD(&fdp->fd_kqlist, p->p_kq, kq_next);
fdpunlock(fdp);
}
if (p->p_kq_serial + num < p->p_kq_serial) {
/* Serial is about to wrap. Clear all attached knotes. */
kqueue_purge(p, p->p_kq);
p->p_kq_serial = 0;
}
}
/*
* Finish poll/select system call.
* num must have the same value that was used with kqpoll_init().
*/
void
kqpoll_done(unsigned int num)
{
struct proc *p = curproc;
struct kqueue *kq = p->p_kq;
KASSERT(p->p_kq != NULL);
KASSERT(p->p_kq_serial + num >= p->p_kq_serial);
p->p_kq_serial += num;
/*
* Because of kn_pollid key, a thread can in principle allocate
* up to O(maxfiles^2) knotes by calling poll(2) repeatedly
* with suitably varying pollfd arrays.
* Prevent such a large allocation by clearing knotes eagerly
* if there are too many of them.
*
* A small multiple of kq_knlistsize should give enough margin
* that eager clearing is infrequent, or does not happen at all,
* with normal programs.
* A single pollfd entry can use up to three knotes.
* Typically there is no significant overlap of fd and events
* between different entries in the pollfd array.
*/
if (kq->kq_nknotes > 4 * kq->kq_knlistsize)
kqueue_purge(p, kq);
}
void
kqpoll_exit(void)
{
struct proc *p = curproc;
if (p->p_kq == NULL)
return;
kqueue_purge(p, p->p_kq);
kqueue_terminate(p, p->p_kq);
KASSERT(p->p_kq->kq_refcnt.r_refs == 1);
KQRELE(p->p_kq);
p->p_kq = NULL;
}
struct kqueue *
kqueue_alloc(struct filedesc *fdp)
{
struct kqueue *kq;
kq = pool_get(&kqueue_pool, PR_WAITOK | PR_ZERO);
refcnt_init(&kq->kq_refcnt);
kq->kq_fdp = fdp;
TAILQ_INIT(&kq->kq_head);
mtx_init(&kq->kq_lock, IPL_HIGH);
task_set(&kq->kq_task, kqueue_task, kq);
klist_init_mutex(&kq->kq_klist, &kqueue_klist_lock);
return (kq);
}
int
sys_kqueue(struct proc *p, void *v, register_t *retval)
{
struct filedesc *fdp = p->p_fd;
struct kqueue *kq;
struct file *fp;
int fd, error;
kq = kqueue_alloc(fdp);
fdplock(fdp);
error = falloc(p, &fp, &fd);
if (error)
goto out;
fp->f_flag = FREAD | FWRITE;
fp->f_type = DTYPE_KQUEUE;
fp->f_ops = &kqueueops;
fp->f_data = kq;
*retval = fd;
LIST_INSERT_HEAD(&fdp->fd_kqlist, kq, kq_next);
kq = NULL;
fdinsert(fdp, fd, 0, fp);
FRELE(fp, p);
out:
fdpunlock(fdp);
if (kq != NULL)
pool_put(&kqueue_pool, kq);
return (error);
}
int
sys_kevent(struct proc *p, void *v, register_t *retval)
{
struct kqueue_scan_state scan;
struct filedesc* fdp = p->p_fd;
struct sys_kevent_args /* {
syscallarg(int) fd;
syscallarg(const struct kevent *) changelist;
syscallarg(int) nchanges;
syscallarg(struct kevent *) eventlist;
syscallarg(int) nevents;
syscallarg(const struct timespec *) timeout;
} */ *uap = v;
struct kevent *kevp;
struct kqueue *kq;
struct file *fp;
struct timespec ts;
struct timespec *tsp = NULL;
int i, n, nerrors, error;
int ready, total;
struct kevent kev[KQ_NEVENTS];
if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_KQUEUE) {
error = EBADF;
goto done;
}
if (SCARG(uap, timeout) != NULL) {
error = copyin(SCARG(uap, timeout), &ts, sizeof(ts));
if (error)
goto done;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
if (ts.tv_sec < 0 || !timespecisvalid(&ts)) {
error = EINVAL;
goto done;
}
tsp = &ts;
}
kq = fp->f_data;
nerrors = 0;
while ((n = SCARG(uap, nchanges)) > 0) {
if (n > nitems(kev))
n = nitems(kev);
error = copyin(SCARG(uap, changelist), kev,
n * sizeof(struct kevent));
if (error)
goto done;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrevent(p, kev, n);
#endif
for (i = 0; i < n; i++) {
kevp = &kev[i];
kevp->flags &= ~EV_SYSFLAGS;
error = kqueue_register(kq, kevp, 0, p);
if (error || (kevp->flags & EV_RECEIPT)) {
if (SCARG(uap, nevents) != 0) {
kevp->flags = EV_ERROR;
kevp->data = error;
copyout(kevp, SCARG(uap, eventlist),
sizeof(*kevp));
SCARG(uap, eventlist)++;
SCARG(uap, nevents)--;
nerrors++;
} else {
goto done;
}
}
}
SCARG(uap, nchanges) -= n;
SCARG(uap, changelist) += n;
}
if (nerrors) {
*retval = nerrors;
error = 0;
goto done;
}
kqueue_scan_setup(&scan, kq);
FRELE(fp, p);
/*
* Collect as many events as we can. The timeout on successive
* loops is disabled (kqueue_scan() becomes non-blocking).
*/
total = 0;
error = 0;
while ((n = SCARG(uap, nevents) - total) > 0) {
if (n > nitems(kev))
n = nitems(kev);
ready = kqueue_scan(&scan, n, kev, tsp, p, &error);
if (ready == 0)
break;
error = copyout(kev, SCARG(uap, eventlist) + total,
sizeof(struct kevent) * ready);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrevent(p, kev, ready);
#endif
total += ready;
if (error || ready < n)
break;
}
kqueue_scan_finish(&scan);
*retval = total;
return (error);
done:
FRELE(fp, p);
return (error);
}
#ifdef KQUEUE_DEBUG
void
kqueue_do_check(struct kqueue *kq, const char *func, int line)
{
struct knote *kn;
int count = 0, nmarker = 0;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
TAILQ_FOREACH(kn, &kq->kq_head, kn_tqe) {
if (kn->kn_filter == EVFILT_MARKER) {
if ((kn->kn_status & KN_QUEUED) != 0)
panic("%s:%d: kq=%p kn=%p marker QUEUED",
func, line, kq, kn);
nmarker++;
} else {
if ((kn->kn_status & KN_ACTIVE) == 0)
panic("%s:%d: kq=%p kn=%p knote !ACTIVE",
func, line, kq, kn);
if ((kn->kn_status & KN_QUEUED) == 0)
panic("%s:%d: kq=%p kn=%p knote !QUEUED",
func, line, kq, kn);
if (kn->kn_kq != kq)
panic("%s:%d: kq=%p kn=%p kn_kq=%p != kq",
func, line, kq, kn, kn->kn_kq);
count++;
if (count > kq->kq_count)
goto bad;
}
}
if (count != kq->kq_count) {
bad:
panic("%s:%d: kq=%p kq_count=%d count=%d nmarker=%d",
func, line, kq, kq->kq_count, count, nmarker);
}
}
#endif
int
kqueue_register(struct kqueue *kq, struct kevent *kev, unsigned int pollid,
struct proc *p)
{
struct filedesc *fdp = kq->kq_fdp;
const struct filterops *fops = NULL;
struct file *fp = NULL;
struct knote *kn = NULL, *newkn = NULL;
struct knlist *list = NULL;
int active, error = 0;
KASSERT(pollid == 0 || (p != NULL && p->p_kq == kq));
if (kev->filter < 0) {
if (kev->filter + EVFILT_SYSCOUNT < 0)
return (EINVAL);
fops = sysfilt_ops[~kev->filter]; /* to 0-base index */
}
if (fops == NULL) {
/*
* XXX
* filter attach routine is responsible for ensuring that
* the identifier can be attached to it.
*/
return (EINVAL);
}
if (fops->f_flags & FILTEROP_ISFD) {
/* validate descriptor */
if (kev->ident > INT_MAX)
return (EBADF);
}
if (kev->flags & EV_ADD)
newkn = pool_get(&knote_pool, PR_WAITOK | PR_ZERO);
again:
if (fops->f_flags & FILTEROP_ISFD) {
if ((fp = fd_getfile(fdp, kev->ident)) == NULL) {
error = EBADF;
goto done;
}
mtx_enter(&kq->kq_lock);
if (kev->flags & EV_ADD)
kqueue_expand_list(kq, kev->ident);
if (kev->ident < kq->kq_knlistsize)
list = &kq->kq_knlist[kev->ident];
} else {
mtx_enter(&kq->kq_lock);
if (kev->flags & EV_ADD)
kqueue_expand_hash(kq);
if (kq->kq_knhashmask != 0) {
list = &kq->kq_knhash[
KN_HASH((u_long)kev->ident, kq->kq_knhashmask)];
}
}
if (list != NULL) {
SLIST_FOREACH(kn, list, kn_link) {
if (kev->filter == kn->kn_filter &&
kev->ident == kn->kn_id &&
pollid == kn->kn_pollid) {
if (!knote_acquire(kn, NULL, 0)) {
/* knote_acquire() has released
* kq_lock. */
if (fp != NULL) {
FRELE(fp, p);
fp = NULL;
}
goto again;
}
break;
}
}
}
KASSERT(kn == NULL || (kn->kn_status & KN_PROCESSING) != 0);
if (kn == NULL && ((kev->flags & EV_ADD) == 0)) {
mtx_leave(&kq->kq_lock);
error = ENOENT;
goto done;
}
/*
* kn now contains the matching knote, or NULL if no match.
*/
if (kev->flags & EV_ADD) {
if (kn == NULL) {
kn = newkn;
newkn = NULL;
kn->kn_status = KN_PROCESSING;
kn->kn_fp = fp;
kn->kn_kq = kq;
kn->kn_fop = fops;
/*
* apply reference count to knote structure, and
* do not release it at the end of this routine.
*/
fp = NULL;
kn->kn_sfflags = kev->fflags;
kn->kn_sdata = kev->data;
kev->fflags = 0;
kev->data = 0;
kn->kn_kevent = *kev;
kn->kn_pollid = pollid;
knote_attach(kn);
mtx_leave(&kq->kq_lock);
error = filter_attach(kn);
if (error != 0) {
knote_drop(kn, p);
goto done;
}
/*
* If this is a file descriptor filter, check if
* fd was closed while the knote was being added.
* knote_fdclose() has missed kn if the function
* ran before kn appeared in kq_knlist.
*/
if ((fops->f_flags & FILTEROP_ISFD) &&
fd_checkclosed(fdp, kev->ident, kn->kn_fp)) {
/*
* Drop the knote silently without error
* because another thread might already have
* seen it. This corresponds to the insert
* happening in full before the close.
*/
filter_detach(kn);
knote_drop(kn, p);
goto done;
}
/* Check if there is a pending event. */
active = filter_process(kn, NULL);
mtx_enter(&kq->kq_lock);
if (active)
knote_activate(kn);
} else if (kn->kn_fop == &badfd_filtops) {
/*
* Nothing expects this badfd knote any longer.
* Drop it to make room for the new knote and retry.
*/
KASSERT(kq == p->p_kq);
mtx_leave(&kq->kq_lock);
filter_detach(kn);
knote_drop(kn, p);
KASSERT(fp != NULL);
FRELE(fp, p);
fp = NULL;
goto again;
} else {
/*
* The user may change some filter values after the
* initial EV_ADD, but doing so will not reset any
* filters which have already been triggered.
*/
mtx_leave(&kq->kq_lock);
active = filter_modify(kev, kn);
mtx_enter(&kq->kq_lock);
if (active)
knote_activate(kn);
if (kev->flags & EV_ERROR) {
error = kev->data;
goto release;
}
}
} else if (kev->flags & EV_DELETE) {
mtx_leave(&kq->kq_lock);
filter_detach(kn);
knote_drop(kn, p);
goto done;
}
if ((kev->flags & EV_DISABLE) && ((kn->kn_status & KN_DISABLED) == 0))
kn->kn_status |= KN_DISABLED;
if ((kev->flags & EV_ENABLE) && (kn->kn_status & KN_DISABLED)) {
kn->kn_status &= ~KN_DISABLED;
mtx_leave(&kq->kq_lock);
/* Check if there is a pending event. */
active = filter_process(kn, NULL);
mtx_enter(&kq->kq_lock);
if (active)
knote_activate(kn);
}
release:
knote_release(kn);
mtx_leave(&kq->kq_lock);
done:
if (fp != NULL)
FRELE(fp, p);
if (newkn != NULL)
pool_put(&knote_pool, newkn);
return (error);
}
int
kqueue_sleep(struct kqueue *kq, struct timespec *tsp)
{
struct timespec elapsed, start, stop;
uint64_t nsecs;
int error;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
if (tsp != NULL) {
getnanouptime(&start);
nsecs = MIN(TIMESPEC_TO_NSEC(tsp), MAXTSLP);
} else
nsecs = INFSLP;
error = msleep_nsec(kq, &kq->kq_lock, PSOCK | PCATCH | PNORELOCK,
"kqread", nsecs);
if (tsp != NULL) {
getnanouptime(&stop);
timespecsub(&stop, &start, &elapsed);
timespecsub(tsp, &elapsed, tsp);
if (tsp->tv_sec < 0)
timespecclear(tsp);
}
return (error);
}
/*
* Scan the kqueue, blocking if necessary until the target time is reached.
* If tsp is NULL we block indefinitely. If tsp->ts_secs/nsecs are both
* 0 we do not block at all.
*/
int
kqueue_scan(struct kqueue_scan_state *scan, int maxevents,
struct kevent *kevp, struct timespec *tsp, struct proc *p, int *errorp)
{
struct kqueue *kq = scan->kqs_kq;
struct knote *kn;
int error = 0, nkev = 0;
int reinserted;
if (maxevents == 0)
goto done;
retry:
KASSERT(nkev == 0);
error = 0;
reinserted = 0;
/* msleep() with PCATCH requires kernel lock. */
KERNEL_LOCK();
mtx_enter(&kq->kq_lock);
if (kq->kq_state & KQ_DYING) {
mtx_leave(&kq->kq_lock);
KERNEL_UNLOCK();
error = EBADF;
goto done;
}
if (kq->kq_count == 0) {
/*
* Successive loops are only necessary if there are more
* ready events to gather, so they don't need to block.
*/
if ((tsp != NULL && !timespecisset(tsp)) ||
scan->kqs_nevent != 0) {
mtx_leave(&kq->kq_lock);
KERNEL_UNLOCK();
error = 0;
goto done;
}
kq->kq_state |= KQ_SLEEP;
error = kqueue_sleep(kq, tsp);
/* kqueue_sleep() has released kq_lock. */
KERNEL_UNLOCK();
if (error == 0 || error == EWOULDBLOCK)
goto retry;
/* don't restart after signals... */
if (error == ERESTART)
error = EINTR;
goto done;
}
/* The actual scan does not sleep on kq, so unlock the kernel. */
KERNEL_UNLOCK();
/*
* Put the end marker in the queue to limit the scan to the events
* that are currently active. This prevents events from being
* recollected if they reactivate during scan.
*
* If a partial scan has been performed already but no events have
* been collected, reposition the end marker to make any new events
* reachable.
*/
if (!scan->kqs_queued) {
TAILQ_INSERT_TAIL(&kq->kq_head, &scan->kqs_end, kn_tqe);
scan->kqs_queued = 1;
} else if (scan->kqs_nevent == 0) {
TAILQ_REMOVE(&kq->kq_head, &scan->kqs_end, kn_tqe);
TAILQ_INSERT_TAIL(&kq->kq_head, &scan->kqs_end, kn_tqe);
}
TAILQ_INSERT_HEAD(&kq->kq_head, &scan->kqs_start, kn_tqe);
while (nkev < maxevents) {
kn = TAILQ_NEXT(&scan->kqs_start, kn_tqe);
if (kn->kn_filter == EVFILT_MARKER) {
if (kn == &scan->kqs_end)
break;
/* Move start marker past another thread's marker. */
TAILQ_REMOVE(&kq->kq_head, &scan->kqs_start, kn_tqe);
TAILQ_INSERT_AFTER(&kq->kq_head, kn, &scan->kqs_start,
kn_tqe);
continue;
}
if (!knote_acquire(kn, NULL, 0)) {
/* knote_acquire() has released kq_lock. */
mtx_enter(&kq->kq_lock);
continue;
}
kqueue_check(kq);
TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
kn->kn_status &= ~KN_QUEUED;
kq->kq_count--;
kqueue_check(kq);
if (kn->kn_status & KN_DISABLED) {
knote_release(kn);
continue;
}
mtx_leave(&kq->kq_lock);
/* Drop expired kqpoll knotes. */
if (p->p_kq == kq &&
p->p_kq_serial > (unsigned long)kn->kn_udata) {
filter_detach(kn);
knote_drop(kn, p);
mtx_enter(&kq->kq_lock);
continue;
}
/*
* Invalidate knotes whose vnodes have been revoked.
* This is a workaround; it is tricky to clear existing
* knotes and prevent new ones from being registered
* with the current revocation mechanism.
*/
if ((kn->kn_fop->f_flags & FILTEROP_ISFD) &&
kn->kn_fp != NULL &&
kn->kn_fp->f_type == DTYPE_VNODE) {
struct vnode *vp = kn->kn_fp->f_data;
if (__predict_false(vp->v_op == &dead_vops &&
kn->kn_fop != &dead_filtops)) {
filter_detach(kn);
kn->kn_fop = &dead_filtops;
/*
* Check if the event should be delivered.
* Use f_event directly because this is
* a special situation.
*/
if (kn->kn_fop->f_event(kn, 0) == 0) {
filter_detach(kn);
knote_drop(kn, p);
mtx_enter(&kq->kq_lock);
continue;
}
}
}
memset(kevp, 0, sizeof(*kevp));
if (filter_process(kn, kevp) == 0) {
mtx_enter(&kq->kq_lock);
if ((kn->kn_status & KN_QUEUED) == 0)
kn->kn_status &= ~KN_ACTIVE;
knote_release(kn);
kqueue_check(kq);
continue;
}
/*
* Post-event action on the note
*/
if (kevp->flags & EV_ONESHOT) {
filter_detach(kn);
knote_drop(kn, p);
mtx_enter(&kq->kq_lock);
} else if (kevp->flags & (EV_CLEAR | EV_DISPATCH)) {
mtx_enter(&kq->kq_lock);
if (kevp->flags & EV_DISPATCH)
kn->kn_status |= KN_DISABLED;
if ((kn->kn_status & KN_QUEUED) == 0)
kn->kn_status &= ~KN_ACTIVE;
knote_release(kn);
} else {
mtx_enter(&kq->kq_lock);
if ((kn->kn_status & KN_QUEUED) == 0) {
kqueue_check(kq);
kq->kq_count++;
kn->kn_status |= KN_QUEUED;
TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
/* Wakeup is done after loop. */
reinserted = 1;
}
knote_release(kn);
}
kqueue_check(kq);
kevp++;
nkev++;
scan->kqs_nevent++;
}
TAILQ_REMOVE(&kq->kq_head, &scan->kqs_start, kn_tqe);
if (reinserted && kq->kq_count != 0)
kqueue_wakeup(kq);
mtx_leave(&kq->kq_lock);
if (scan->kqs_nevent == 0)
goto retry;
done:
*errorp = error;
return (nkev);
}
void
kqueue_scan_setup(struct kqueue_scan_state *scan, struct kqueue *kq)
{
memset(scan, 0, sizeof(*scan));
KQREF(kq);
scan->kqs_kq = kq;
scan->kqs_start.kn_filter = EVFILT_MARKER;
scan->kqs_start.kn_status = KN_PROCESSING;
scan->kqs_end.kn_filter = EVFILT_MARKER;
scan->kqs_end.kn_status = KN_PROCESSING;
}
void
kqueue_scan_finish(struct kqueue_scan_state *scan)
{
struct kqueue *kq = scan->kqs_kq;
KASSERT(scan->kqs_start.kn_filter == EVFILT_MARKER);
KASSERT(scan->kqs_start.kn_status == KN_PROCESSING);
KASSERT(scan->kqs_end.kn_filter == EVFILT_MARKER);
KASSERT(scan->kqs_end.kn_status == KN_PROCESSING);
if (scan->kqs_queued) {
scan->kqs_queued = 0;
mtx_enter(&kq->kq_lock);
TAILQ_REMOVE(&kq->kq_head, &scan->kqs_end, kn_tqe);
mtx_leave(&kq->kq_lock);
}
KQRELE(kq);
}
/*
* XXX
* This could be expanded to call kqueue_scan, if desired.
*/
int
kqueue_read(struct file *fp, struct uio *uio, int fflags)
{
return (ENXIO);
}
int
kqueue_write(struct file *fp, struct uio *uio, int fflags)
{
return (ENXIO);
}
int
kqueue_ioctl(struct file *fp, u_long com, caddr_t data, struct proc *p)
{
return (ENOTTY);
}
int
kqueue_stat(struct file *fp, struct stat *st, struct proc *p)
{
struct kqueue *kq = fp->f_data;
memset(st, 0, sizeof(*st));
st->st_size = kq->kq_count; /* unlocked read */
st->st_blksize = sizeof(struct kevent);
st->st_mode = S_IFIFO;
return (0);
}
void
kqueue_purge(struct proc *p, struct kqueue *kq)
{
int i;
mtx_enter(&kq->kq_lock);
for (i = 0; i < kq->kq_knlistsize; i++)
knote_remove(p, kq, &kq->kq_knlist, i, 1);
if (kq->kq_knhashmask != 0) {
for (i = 0; i < kq->kq_knhashmask + 1; i++)
knote_remove(p, kq, &kq->kq_knhash, i, 1);
}
mtx_leave(&kq->kq_lock);
}
void
kqueue_terminate(struct proc *p, struct kqueue *kq)
{
struct knote *kn;
int state;
mtx_enter(&kq->kq_lock);
/*
* Any remaining entries should be scan markers.
* They are removed when the ongoing scans finish.
*/
KASSERT(kq->kq_count == 0);
TAILQ_FOREACH(kn, &kq->kq_head, kn_tqe)
KASSERT(kn->kn_filter == EVFILT_MARKER);
kq->kq_state |= KQ_DYING;
state = kq->kq_state;
kqueue_wakeup(kq);
mtx_leave(&kq->kq_lock);
/*
* Any knotes that were attached to this kqueue were deleted
* by knote_fdclose() when this kqueue's file descriptor was closed.
*/
KASSERT(klist_empty(&kq->kq_klist));
if (state & KQ_TASK)
taskq_del_barrier(systqmp, &kq->kq_task);
}
int
kqueue_close(struct file *fp, struct proc *p)
{
struct kqueue *kq = fp->f_data;
fp->f_data = NULL;
kqueue_purge(p, kq);
kqueue_terminate(p, kq);
KQRELE(kq);
return (0);
}
static void
kqueue_task(void *arg)
{
struct kqueue *kq = arg;
mtx_enter(&kqueue_klist_lock);
KNOTE(&kq->kq_klist, 0);
mtx_leave(&kqueue_klist_lock);
}
void
kqueue_wakeup(struct kqueue *kq)
{
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
if (kq->kq_state & KQ_SLEEP) {
kq->kq_state &= ~KQ_SLEEP;
wakeup(kq);
}
if (!klist_empty(&kq->kq_klist)) {
/* Defer activation to avoid recursion. */
kq->kq_state |= KQ_TASK;
task_add(systqmp, &kq->kq_task);
}
}
static void
kqueue_expand_hash(struct kqueue *kq)
{
struct knlist *hash;
u_long hashmask;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
if (kq->kq_knhashmask == 0) {
mtx_leave(&kq->kq_lock);
hash = hashinit(KN_HASHSIZE, M_KEVENT, M_WAITOK, &hashmask);
mtx_enter(&kq->kq_lock);
if (kq->kq_knhashmask == 0) {
kq->kq_knhash = hash;
kq->kq_knhashmask = hashmask;
} else {
/* Another thread has allocated the hash. */
mtx_leave(&kq->kq_lock);
hashfree(hash, KN_HASHSIZE, M_KEVENT);
mtx_enter(&kq->kq_lock);
}
}
}
static void
kqueue_expand_list(struct kqueue *kq, int fd)
{
struct knlist *list, *olist;
int size, osize;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
if (kq->kq_knlistsize <= fd) {
size = kq->kq_knlistsize;
mtx_leave(&kq->kq_lock);
while (size <= fd)
size += KQEXTENT;
list = mallocarray(size, sizeof(*list), M_KEVENT, M_WAITOK);
mtx_enter(&kq->kq_lock);
if (kq->kq_knlistsize <= fd) {
memcpy(list, kq->kq_knlist,
kq->kq_knlistsize * sizeof(*list));
memset(&list[kq->kq_knlistsize], 0,
(size - kq->kq_knlistsize) * sizeof(*list));
olist = kq->kq_knlist;
osize = kq->kq_knlistsize;
kq->kq_knlist = list;
kq->kq_knlistsize = size;
mtx_leave(&kq->kq_lock);
free(olist, M_KEVENT, osize * sizeof(*list));
mtx_enter(&kq->kq_lock);
} else {
/* Another thread has expanded the list. */
mtx_leave(&kq->kq_lock);
free(list, M_KEVENT, size * sizeof(*list));
mtx_enter(&kq->kq_lock);
}
}
}
/*
* Acquire a knote, return non-zero on success, 0 on failure.
*
* If we cannot acquire the knote we sleep and return 0. The knote
* may be stale on return in this case and the caller must restart
* whatever loop they are in.
*
* If we are about to sleep and klist is non-NULL, the list is unlocked
* before sleep and remains unlocked on return.
*/
int
knote_acquire(struct knote *kn, struct klist *klist, int ls)
{
struct kqueue *kq = kn->kn_kq;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
KASSERT(kn->kn_filter != EVFILT_MARKER);
if (kn->kn_status & KN_PROCESSING) {
kn->kn_status |= KN_WAITING;
if (klist != NULL) {
mtx_leave(&kq->kq_lock);
klist_unlock(klist, ls);
/* XXX Timeout resolves potential loss of wakeup. */
tsleep_nsec(kn, 0, "kqepts", SEC_TO_NSEC(1));
} else {
msleep_nsec(kn, &kq->kq_lock, PNORELOCK, "kqepts",
SEC_TO_NSEC(1));
}
/* knote may be stale now */
return (0);
}
kn->kn_status |= KN_PROCESSING;
return (1);
}
/*
* Release an acquired knote, clearing KN_PROCESSING.
*/
void
knote_release(struct knote *kn)
{
MUTEX_ASSERT_LOCKED(&kn->kn_kq->kq_lock);
KASSERT(kn->kn_filter != EVFILT_MARKER);
KASSERT(kn->kn_status & KN_PROCESSING);
if (kn->kn_status & KN_WAITING) {
kn->kn_status &= ~KN_WAITING;
wakeup(kn);
}
kn->kn_status &= ~KN_PROCESSING;
/* kn should not be accessed anymore */
}
/*
* activate one knote.
*/
void
knote_activate(struct knote *kn)
{
MUTEX_ASSERT_LOCKED(&kn->kn_kq->kq_lock);
kn->kn_status |= KN_ACTIVE;
if ((kn->kn_status & (KN_QUEUED | KN_DISABLED)) == 0)
knote_enqueue(kn);
}
/*
* walk down a list of knotes, activating them if their event has triggered.
*/
void
knote(struct klist *list, long hint)
{
struct knote *kn, *kn0;
struct kqueue *kq;
KLIST_ASSERT_LOCKED(list);
SLIST_FOREACH_SAFE(kn, &list->kl_list, kn_selnext, kn0) {
if (filter_event(kn, hint)) {
kq = kn->kn_kq;
mtx_enter(&kq->kq_lock);
knote_activate(kn);
mtx_leave(&kq->kq_lock);
}
}
}
/*
* remove all knotes from a specified knlist
*/
void
knote_remove(struct proc *p, struct kqueue *kq, struct knlist **plist, int idx,
int purge)
{
struct knote *kn;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
/* Always fetch array pointer as another thread can resize kq_knlist. */
while ((kn = SLIST_FIRST(*plist + idx)) != NULL) {
KASSERT(kn->kn_kq == kq);
if (!purge) {
/* Skip pending badfd knotes. */
while (kn->kn_fop == &badfd_filtops) {
kn = SLIST_NEXT(kn, kn_link);
if (kn == NULL)
return;
KASSERT(kn->kn_kq == kq);
}
}
if (!knote_acquire(kn, NULL, 0)) {
/* knote_acquire() has released kq_lock. */
mtx_enter(&kq->kq_lock);
continue;
}
mtx_leave(&kq->kq_lock);
filter_detach(kn);
/*
* Notify poll(2) and select(2) when a monitored
* file descriptor is closed.
*
* This reuses the original knote for delivering the
* notification so as to avoid allocating memory.
*/
if (!purge && (kn->kn_flags & (__EV_POLL | __EV_SELECT)) &&
!(p->p_kq == kq &&
p->p_kq_serial > (unsigned long)kn->kn_udata) &&
kn->kn_fop != &badfd_filtops) {
KASSERT(kn->kn_fop->f_flags & FILTEROP_ISFD);
FRELE(kn->kn_fp, p);
kn->kn_fp = NULL;
kn->kn_fop = &badfd_filtops;
filter_event(kn, 0);
mtx_enter(&kq->kq_lock);
knote_activate(kn);
knote_release(kn);
continue;
}
knote_drop(kn, p);
mtx_enter(&kq->kq_lock);
}
}
/*
* remove all knotes referencing a specified fd
*/
void
knote_fdclose(struct proc *p, int fd)
{
struct filedesc *fdp = p->p_p->ps_fd;
struct kqueue *kq;
/*
* fdplock can be ignored if the file descriptor table is being freed
* because no other thread can access the fdp.
*/
if (fdp->fd_refcnt != 0)
fdpassertlocked(fdp);
LIST_FOREACH(kq, &fdp->fd_kqlist, kq_next) {
mtx_enter(&kq->kq_lock);
if (fd < kq->kq_knlistsize)
knote_remove(p, kq, &kq->kq_knlist, fd, 0);
mtx_leave(&kq->kq_lock);
}
}
/*
* handle a process exiting, including the triggering of NOTE_EXIT notes
* XXX this could be more efficient, doing a single pass down the klist
*/
void
knote_processexit(struct process *pr)
{
KERNEL_ASSERT_LOCKED();
KNOTE(&pr->ps_klist, NOTE_EXIT);
/* remove other knotes hanging off the process */
klist_invalidate(&pr->ps_klist);
}
void
knote_attach(struct knote *kn)
{
struct kqueue *kq = kn->kn_kq;
struct knlist *list;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
KASSERT(kn->kn_status & KN_PROCESSING);
if (kn->kn_fop->f_flags & FILTEROP_ISFD) {
KASSERT(kq->kq_knlistsize > kn->kn_id);
list = &kq->kq_knlist[kn->kn_id];
} else {
KASSERT(kq->kq_knhashmask != 0);
list = &kq->kq_knhash[KN_HASH(kn->kn_id, kq->kq_knhashmask)];
}
SLIST_INSERT_HEAD(list, kn, kn_link);
kq->kq_nknotes++;
}
void
knote_detach(struct knote *kn)
{
struct kqueue *kq = kn->kn_kq;
struct knlist *list;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
KASSERT(kn->kn_status & KN_PROCESSING);
kq->kq_nknotes--;
if (kn->kn_fop->f_flags & FILTEROP_ISFD)
list = &kq->kq_knlist[kn->kn_id];
else
list = &kq->kq_knhash[KN_HASH(kn->kn_id, kq->kq_knhashmask)];
SLIST_REMOVE(list, kn, knote, kn_link);
}
/*
* should be called at spl == 0, since we don't want to hold spl
* while calling FRELE and pool_put.
*/
void
knote_drop(struct knote *kn, struct proc *p)
{
struct kqueue *kq = kn->kn_kq;
KASSERT(kn->kn_filter != EVFILT_MARKER);
mtx_enter(&kq->kq_lock);
knote_detach(kn);
if (kn->kn_status & KN_QUEUED)
knote_dequeue(kn);
if (kn->kn_status & KN_WAITING) {
kn->kn_status &= ~KN_WAITING;
wakeup(kn);
}
mtx_leave(&kq->kq_lock);
if ((kn->kn_fop->f_flags & FILTEROP_ISFD) && kn->kn_fp != NULL)
FRELE(kn->kn_fp, p);
pool_put(&knote_pool, kn);
}
void
knote_enqueue(struct knote *kn)
{
struct kqueue *kq = kn->kn_kq;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
KASSERT(kn->kn_filter != EVFILT_MARKER);
KASSERT((kn->kn_status & KN_QUEUED) == 0);
kqueue_check(kq);
TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
kn->kn_status |= KN_QUEUED;
kq->kq_count++;
kqueue_check(kq);
kqueue_wakeup(kq);
}
void
knote_dequeue(struct knote *kn)
{
struct kqueue *kq = kn->kn_kq;
MUTEX_ASSERT_LOCKED(&kq->kq_lock);
KASSERT(kn->kn_filter != EVFILT_MARKER);
KASSERT(kn->kn_status & KN_QUEUED);
kqueue_check(kq);
TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
kn->kn_status &= ~KN_QUEUED;
kq->kq_count--;
kqueue_check(kq);
}
/*
* Assign parameters to the knote.
*
* The knote's object lock must be held.
*/
void
knote_assign(const struct kevent *kev, struct knote *kn)
{
if ((kn->kn_fop->f_flags & FILTEROP_MPSAFE) == 0)
KERNEL_ASSERT_LOCKED();
kn->kn_sfflags = kev->fflags;
kn->kn_sdata = kev->data;
kn->kn_udata = kev->udata;
}
/*
* Submit the knote's event for delivery.
*
* The knote's object lock must be held.
*/
void
knote_submit(struct knote *kn, struct kevent *kev)
{
if ((kn->kn_fop->f_flags & FILTEROP_MPSAFE) == 0)
KERNEL_ASSERT_LOCKED();
if (kev != NULL) {
*kev = kn->kn_kevent;
if (kn->kn_flags & EV_CLEAR) {
kn->kn_fflags = 0;
kn->kn_data = 0;
}
}
}
void
klist_init(struct klist *klist, const struct klistops *ops, void *arg)
{
SLIST_INIT(&klist->kl_list);
klist->kl_ops = ops;
klist->kl_arg = arg;
}
void
klist_free(struct klist *klist)
{
KASSERT(SLIST_EMPTY(&klist->kl_list));
}
void
klist_insert(struct klist *klist, struct knote *kn)
{
int ls;
ls = klist_lock(klist);
SLIST_INSERT_HEAD(&klist->kl_list, kn, kn_selnext);
klist_unlock(klist, ls);
}
void
klist_insert_locked(struct klist *klist, struct knote *kn)
{
KLIST_ASSERT_LOCKED(klist);
SLIST_INSERT_HEAD(&klist->kl_list, kn, kn_selnext);
}
void
klist_remove(struct klist *klist, struct knote *kn)
{
int ls;
ls = klist_lock(klist);
SLIST_REMOVE(&klist->kl_list, kn, knote, kn_selnext);
klist_unlock(klist, ls);
}
void
klist_remove_locked(struct klist *klist, struct knote *kn)
{
KLIST_ASSERT_LOCKED(klist);
SLIST_REMOVE(&klist->kl_list, kn, knote, kn_selnext);
}
/*
* Detach all knotes from klist. The knotes are rewired to indicate EOF.
*
* The caller of this function must not hold any locks that can block
* filterops callbacks that run with KN_PROCESSING.
* Otherwise this function might deadlock.
*/
void
klist_invalidate(struct klist *list)
{
struct knote *kn;
struct kqueue *kq;
struct proc *p = curproc;
int ls;
NET_ASSERT_UNLOCKED();
ls = klist_lock(list);
while ((kn = SLIST_FIRST(&list->kl_list)) != NULL) {
kq = kn->kn_kq;
mtx_enter(&kq->kq_lock);
if (!knote_acquire(kn, list, ls)) {
/* knote_acquire() has released kq_lock
* and klist lock. */
ls = klist_lock(list);
continue;
}
mtx_leave(&kq->kq_lock);
klist_unlock(list, ls);
filter_detach(kn);
if (kn->kn_fop->f_flags & FILTEROP_ISFD) {
kn->kn_fop = &dead_filtops;
filter_event(kn, 0);
mtx_enter(&kq->kq_lock);
knote_activate(kn);
knote_release(kn);
mtx_leave(&kq->kq_lock);
} else {
knote_drop(kn, p);
}
ls = klist_lock(list);
}
klist_unlock(list, ls);
}
static int
klist_lock(struct klist *list)
{
int ls = 0;
if (list->kl_ops != NULL) {
ls = list->kl_ops->klo_lock(list->kl_arg);
} else {
KERNEL_LOCK();
ls = splhigh();
}
return ls;
}
static void
klist_unlock(struct klist *list, int ls)
{
if (list->kl_ops != NULL) {
list->kl_ops->klo_unlock(list->kl_arg, ls);
} else {
splx(ls);
KERNEL_UNLOCK();
}
}
static void
klist_mutex_assertlk(void *arg)
{
struct mutex *mtx = arg;
(void)mtx;
MUTEX_ASSERT_LOCKED(mtx);
}
static int
klist_mutex_lock(void *arg)
{
struct mutex *mtx = arg;
mtx_enter(mtx);
return 0;
}
static void
klist_mutex_unlock(void *arg, int s)
{
struct mutex *mtx = arg;
mtx_leave(mtx);
}
static const struct klistops mutex_klistops = {
.klo_assertlk = klist_mutex_assertlk,
.klo_lock = klist_mutex_lock,
.klo_unlock = klist_mutex_unlock,
};
void
klist_init_mutex(struct klist *klist, struct mutex *mtx)
{
klist_init(klist, &mutex_klistops, mtx);
}
static void
klist_rwlock_assertlk(void *arg)
{
struct rwlock *rwl = arg;
(void)rwl;
rw_assert_wrlock(rwl);
}
static int
klist_rwlock_lock(void *arg)
{
struct rwlock *rwl = arg;
rw_enter_write(rwl);
return 0;
}
static void
klist_rwlock_unlock(void *arg, int s)
{
struct rwlock *rwl = arg;
rw_exit_write(rwl);
}
static const struct klistops rwlock_klistops = {
.klo_assertlk = klist_rwlock_assertlk,
.klo_lock = klist_rwlock_lock,
.klo_unlock = klist_rwlock_unlock,
};
void
klist_init_rwlock(struct klist *klist, struct rwlock *rwl)
{
klist_init(klist, &rwlock_klistops, rwl);
}
224
2
192
135
142
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* $OpenBSD: strncmp.c,v 1.11 2014/06/10 04:16:57 deraadt Exp $ */
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <lib/libkern/libkern.h>
int
strncmp(const char *s1, const char *s2, size_t n)
{
if (n == 0)
return (0);
do {
if (*s1 != *s2++)
return (*(unsigned char *)s1 - *(unsigned char *)--s2);
if (*s1++ == 0)
break;
} while (--n != 0);
return (0);
}
14
11
2
1
17
2
3
1
1
1
1
2
1
1
1
1
1
3
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* $OpenBSD: hotplug.c,v 1.22 2022/07/02 08:50:41 visa Exp $ */
/*
* Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Device attachment and detachment notifications.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/hotplug.h>
#include <sys/ioctl.h>
#include <sys/vnode.h>
#define HOTPLUG_MAXEVENTS 64
static int opened;
static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS];
static int evqueue_head, evqueue_tail, evqueue_count;
static struct selinfo hotplug_sel;
void filt_hotplugrdetach(struct knote *);
int filt_hotplugread(struct knote *, long);
const struct filterops hotplugread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_hotplugrdetach,
.f_event = filt_hotplugread,
};
#define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)
int hotplug_put_event(struct hotplug_event *);
int hotplug_get_event(struct hotplug_event *);
void hotplugattach(int);
void
hotplugattach(int count)
{
opened = 0;
evqueue_head = 0;
evqueue_tail = 0;
evqueue_count = 0;
}
void
hotplug_device_attach(enum devclass class, char *name)
{
struct hotplug_event he;
he.he_type = HOTPLUG_DEVAT;
he.he_devclass = class;
strlcpy(he.he_devname, name, sizeof(he.he_devname));
hotplug_put_event(&he);
}
void
hotplug_device_detach(enum devclass class, char *name)
{
struct hotplug_event he;
he.he_type = HOTPLUG_DEVDT;
he.he_devclass = class;
strlcpy(he.he_devname, name, sizeof(he.he_devname));
hotplug_put_event(&he);
}
int
hotplug_put_event(struct hotplug_event *he)
{
if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
printf("hotplug: event lost, queue full\n");
return (1);
}
evqueue[evqueue_head] = *he;
evqueue_head = EVQUEUE_NEXT(evqueue_head);
if (evqueue_count == HOTPLUG_MAXEVENTS)
evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
else
evqueue_count++;
wakeup(&evqueue);
selwakeup(&hotplug_sel);
return (0);
}
int
hotplug_get_event(struct hotplug_event *he)
{
int s;
if (evqueue_count == 0)
return (1);
s = splbio();
*he = evqueue[evqueue_tail];
evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
evqueue_count--;
splx(s);
return (0);
}
int
hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
{
if (minor(dev) != 0)
return (ENXIO);
if ((flag & FWRITE))
return (EPERM);
if (opened)
return (EBUSY);
opened = 1;
return (0);
}
int
hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct hotplug_event he;
while (hotplug_get_event(&he) == 0)
continue;
opened = 0;
return (0);
}
int
hotplugread(dev_t dev, struct uio *uio, int flags)
{
struct hotplug_event he;
int error;
if (uio->uio_resid != sizeof(he))
return (EINVAL);
again:
if (hotplug_get_event(&he) == 0)
return (uiomove(&he, sizeof(he), uio));
if (flags & IO_NDELAY)
return (EAGAIN);
error = tsleep_nsec(&evqueue, PRIBIO | PCATCH, "htplev", INFSLP);
if (error)
return (error);
goto again;
}
int
hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
switch (cmd) {
case FIOASYNC:
/* ignore */
case FIONBIO:
/* handled in the upper fs layer */
break;
default:
return (ENOTTY);
}
return (0);
}
int
hotplugkqfilter(dev_t dev, struct knote *kn)
{
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &hotplug_sel.si_note;
kn->kn_fop = &hotplugread_filtops;
break;
default:
return (EINVAL);
}
s = splbio();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
void
filt_hotplugrdetach(struct knote *kn)
{
int s;
s = splbio();
klist_remove_locked(&hotplug_sel.si_note, kn);
splx(s);
}
int
filt_hotplugread(struct knote *kn, long hint)
{
kn->kn_data = evqueue_count;
return (evqueue_count > 0);
}
215
215
2
2
2
2
2
217
216
216
217
217
217
217
144
181
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
/* $OpenBSD: bus_dma.c,v 1.51 2019/06/09 12:52:04 kettenis Exp $ */
/* $NetBSD: bus_dma.c,v 1.3 2003/05/07 21:33:58 fvdl Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
* Simulation Facility, NASA Ames Research Center.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* The following is included because _bus_dma_uiomove is derived from
* uiomove() in kern_subr.c.
*/
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <machine/bus.h>
#include <uvm/uvm_extern.h>
int _bus_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t,
struct proc *, int, paddr_t *, int *, int);
/*
* Common function for DMA map creation. May be called by bus-specific
* DMA map creation functions.
*/
int
_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp)
{
struct bus_dmamap *map;
void *mapstore;
size_t mapsize;
/*
* Allocate and initialize the DMA map. The end of the map
* is a variable-sized array of segments, so we allocate enough
* room for them in one shot.
*
* Note we don't preserve the WAITOK or NOWAIT flags. Preservation
* of ALLOCNOW notifies others that we've reserved these resources,
* and they are not to be freed.
*
* The bus_dmamap_t includes one bus_dma_segment_t, hence
* the (nsegments - 1).
*/
mapsize = sizeof(struct bus_dmamap) +
(sizeof(bus_dma_segment_t) * (nsegments - 1));
if ((mapstore = malloc(mapsize, M_DEVBUF,
(flags & BUS_DMA_NOWAIT) ?
(M_NOWAIT|M_ZERO) : (M_WAITOK|M_ZERO))) == NULL)
return (ENOMEM);
map = (struct bus_dmamap *)mapstore;
map->_dm_size = size;
map->_dm_segcnt = nsegments;
map->_dm_maxsegsz = maxsegsz;
map->_dm_boundary = boundary;
map->_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT);
*dmamp = map;
return (0);
}
/*
* Common function for DMA map destruction. May be called by bus-specific
* DMA map destruction functions.
*/
void
_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
{
size_t mapsize;
mapsize = sizeof(struct bus_dmamap) +
(sizeof(bus_dma_segment_t) * (map->_dm_segcnt - 1));
free(map, M_DEVBUF, mapsize);
}
/*
* Common function for loading a DMA map with a linear buffer. May
* be called by bus-specific DMA map load functions.
*/
int
_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
bus_size_t buflen, struct proc *p, int flags)
{
bus_addr_t lastaddr = 0;
int seg, error;
/*
* Make sure that on error condition we return "no valid mappings".
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
if (buflen > map->_dm_size)
return (EINVAL);
seg = 0;
error = _bus_dmamap_load_buffer(t, map, buf, buflen, p, flags,
&lastaddr, &seg, 1);
if (error == 0) {
map->dm_mapsize = buflen;
map->dm_nsegs = seg + 1;
}
return (error);
}
/*
* Like _bus_dmamap_load(), but for mbufs.
*/
int
_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0,
int flags)
{
paddr_t lastaddr = 0;
int seg, error, first;
struct mbuf *m;
/*
* Make sure that on error condition we return "no valid mappings".
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
#ifdef DIAGNOSTIC
if ((m0->m_flags & M_PKTHDR) == 0)
panic("_bus_dmamap_load_mbuf: no packet header");
#endif
if (m0->m_pkthdr.len > map->_dm_size)
return (EINVAL);
first = 1;
seg = 0;
error = 0;
for (m = m0; m != NULL && error == 0; m = m->m_next) {
if (m->m_len == 0)
continue;
error = _bus_dmamap_load_buffer(t, map, m->m_data, m->m_len,
NULL, flags, &lastaddr, &seg, first);
first = 0;
}
if (error == 0) {
map->dm_mapsize = m0->m_pkthdr.len;
map->dm_nsegs = seg + 1;
}
return (error);
}
/*
* Like _bus_dmamap_load(), but for uios.
*/
int
_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio,
int flags)
{
paddr_t lastaddr = 0;
int seg, i, error, first;
bus_size_t minlen, resid;
struct proc *p = NULL;
struct iovec *iov;
caddr_t addr;
/*
* Make sure that on error condition we return "no valid mappings".
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
resid = uio->uio_resid;
iov = uio->uio_iov;
if (uio->uio_segflg == UIO_USERSPACE) {
p = uio->uio_procp;
#ifdef DIAGNOSTIC
if (p == NULL)
panic("_bus_dmamap_load_uio: USERSPACE but no proc");
#endif
}
first = 1;
seg = 0;
error = 0;
for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) {
/*
* Now at the first iovec to load. Load each iovec
* until we have exhausted the residual count.
*/
minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len;
addr = (caddr_t)iov[i].iov_base;
error = _bus_dmamap_load_buffer(t, map, addr, minlen,
p, flags, &lastaddr, &seg, first);
first = 0;
resid -= minlen;
}
if (error == 0) {
map->dm_mapsize = uio->uio_resid;
map->dm_nsegs = seg + 1;
}
return (error);
}
/*
* Like _bus_dmamap_load(), but for raw memory allocated with
* bus_dmamem_alloc().
*/
int
_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs,
int nsegs, bus_size_t size, int flags)
{
bus_addr_t paddr, baddr, bmask, lastaddr = 0;
bus_size_t plen, sgsize, mapsize;
int first = 1;
int i, seg = 0;
/*
* Make sure that on error condition we return "no valid mappings".
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
if (nsegs > map->_dm_segcnt || size > map->_dm_size)
return (EINVAL);
mapsize = size;
bmask = ~(map->_dm_boundary - 1);
for (i = 0; i < nsegs && size > 0; i++) {
paddr = segs[i].ds_addr;
plen = MIN(segs[i].ds_len, size);
while (plen > 0) {
/*
* Compute the segment size, and adjust counts.
*/
sgsize = PAGE_SIZE - ((u_long)paddr & PGOFSET);
if (plen < sgsize)
sgsize = plen;
if (paddr > dma_constraint.ucr_high &&
(map->_dm_flags & BUS_DMA_64BIT) == 0)
panic("Non dma-reachable buffer at paddr %#lx(raw)",
paddr);
/*
* Make sure we don't cross any boundaries.
*/
if (map->_dm_boundary > 0) {
baddr = (paddr + map->_dm_boundary) & bmask;
if (sgsize > (baddr - paddr))
sgsize = (baddr - paddr);
}
/*
* Insert chunk into a segment, coalescing with
* previous segment if possible.
*/
if (first) {
map->dm_segs[seg].ds_addr = paddr;
map->dm_segs[seg].ds_len = sgsize;
first = 0;
} else {
if (paddr == lastaddr &&
(map->dm_segs[seg].ds_len + sgsize) <=
map->_dm_maxsegsz &&
(map->_dm_boundary == 0 ||
(map->dm_segs[seg].ds_addr & bmask) ==
(paddr & bmask)))
map->dm_segs[seg].ds_len += sgsize;
else {
if (++seg >= map->_dm_segcnt)
return (EINVAL);
map->dm_segs[seg].ds_addr = paddr;
map->dm_segs[seg].ds_len = sgsize;
}
}
paddr += sgsize;
plen -= sgsize;
size -= sgsize;
lastaddr = paddr;
}
}
map->dm_mapsize = mapsize;
map->dm_nsegs = seg + 1;
return (0);
}
/*
* Common function for unloading a DMA map. May be called by
* bus-specific DMA map unload functions.
*/
void
_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
/*
* No resources to free; just mark the mappings as
* invalid.
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
}
/*
* Common function for DMA map synchronization. May be called
* by bus-specific DMA map synchronization functions.
*/
void
_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t addr,
bus_size_t size, int op)
{
/* Nothing to do here. */
}
/*
* Common function for DMA-safe memory allocation. May be called
* by bus-specific DMA memory allocation functions.
*/
int
_bus_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
int flags)
{
/*
* XXX in the presence of decent (working) iommus and bouncebuffers
* we can then fallback this allocation to a range of { 0, -1 }.
* However for now we err on the side of caution and allocate dma
* memory under the 4gig boundary.
*/
return (_bus_dmamem_alloc_range(t, size, alignment, boundary,
segs, nsegs, rsegs, flags, (bus_addr_t)0, (bus_addr_t)0xffffffff));
}
/*
* Common function for freeing DMA-safe memory. May be called by
* bus-specific DMA memory free functions.
*/
void
_bus_dmamem_free(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs)
{
struct vm_page *m;
bus_addr_t addr;
struct pglist mlist;
int curseg;
/*
* Build a list of pages to free back to the VM system.
*/
TAILQ_INIT(&mlist);
for (curseg = 0; curseg < nsegs; curseg++) {
for (addr = segs[curseg].ds_addr;
addr < (segs[curseg].ds_addr + segs[curseg].ds_len);
addr += PAGE_SIZE) {
m = PHYS_TO_VM_PAGE(addr);
TAILQ_INSERT_TAIL(&mlist, m, pageq);
}
}
uvm_pglistfree(&mlist);
}
/*
* Common function for mapping DMA-safe memory. May be called by
* bus-specific DMA memory map functions.
*/
int
_bus_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
size_t size, caddr_t *kvap, int flags)
{
vaddr_t va, sva;
size_t ssize;
bus_addr_t addr;
int curseg, pmapflags = 0, error;
const struct kmem_dyn_mode *kd;
if (nsegs == 1 && (flags & BUS_DMA_NOCACHE) == 0) {
*kvap = (caddr_t)PMAP_DIRECT_MAP(segs[0].ds_addr);
return (0);
}
if (flags & BUS_DMA_NOCACHE)
pmapflags |= PMAP_NOCACHE;
size = round_page(size);
kd = flags & BUS_DMA_NOWAIT ? &kd_trylock : &kd_waitok;
va = (vaddr_t)km_alloc(size, &kv_any, &kp_none, kd);
if (va == 0)
return (ENOMEM);
*kvap = (caddr_t)va;
sva = va;
ssize = size;
for (curseg = 0; curseg < nsegs; curseg++) {
for (addr = segs[curseg].ds_addr;
addr < (segs[curseg].ds_addr + segs[curseg].ds_len);
addr += PAGE_SIZE, va += PAGE_SIZE, size -= PAGE_SIZE) {
if (size == 0)
panic("_bus_dmamem_map: size botch");
error = pmap_enter(pmap_kernel(), va, addr | pmapflags,
PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PMAP_WIRED | PMAP_CANFAIL);
if (error) {
pmap_update(pmap_kernel());
km_free((void *)sva, ssize, &kv_any, &kp_none);
return (error);
}
}
}
pmap_update(pmap_kernel());
return (0);
}
/*
* Common function for unmapping DMA-safe memory. May be called by
* bus-specific DMA memory unmapping functions.
*/
void
_bus_dmamem_unmap(bus_dma_tag_t t, caddr_t kva, size_t size)
{
#ifdef DIAGNOSTIC
if ((u_long)kva & PGOFSET)
panic("_bus_dmamem_unmap");
#endif
if (kva >= (caddr_t)PMAP_DIRECT_BASE && kva <= (caddr_t)PMAP_DIRECT_END)
return;
km_free(kva, round_page(size), &kv_any, &kp_none);
}
/*
* Common function for mmap(2)'ing DMA-safe memory. May be called by
* bus-specific DMA mmap(2)'ing functions.
*/
paddr_t
_bus_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, off_t off,
int prot, int flags)
{
int i, pmapflags = 0;
if (flags & BUS_DMA_NOCACHE)
pmapflags |= PMAP_NOCACHE;
for (i = 0; i < nsegs; i++) {
#ifdef DIAGNOSTIC
if (off & PGOFSET)
panic("_bus_dmamem_mmap: offset unaligned");
if (segs[i].ds_addr & PGOFSET)
panic("_bus_dmamem_mmap: segment unaligned");
if (segs[i].ds_len & PGOFSET)
panic("_bus_dmamem_mmap: segment size not multiple"
" of page size");
#endif
if (off >= segs[i].ds_len) {
off -= segs[i].ds_len;
continue;
}
return ((segs[i].ds_addr + off) | pmapflags);
}
/* Page not found. */
return (-1);
}
/**********************************************************************
* DMA utility functions
**********************************************************************/
/*
* Utility function to load a linear buffer. lastaddrp holds state
* between invocations (for multiple-buffer loads). segp contains
* the starting segment on entrance, and the ending segment on exit.
* first indicates if this is the first invocation of this function.
*/
int
_bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, int *segp,
int first)
{
bus_size_t sgsize;
bus_addr_t curaddr, lastaddr, baddr, bmask;
vaddr_t vaddr = (vaddr_t)buf;
int seg;
pmap_t pmap;
if (p != NULL)
pmap = p->p_vmspace->vm_map.pmap;
else
pmap = pmap_kernel();
lastaddr = *lastaddrp;
bmask = ~(map->_dm_boundary - 1);
for (seg = *segp; buflen > 0 ; ) {
/*
* Get the physical address for this segment.
*/
pmap_extract(pmap, vaddr, (paddr_t *)&curaddr);
if (curaddr > dma_constraint.ucr_high &&
(map->_dm_flags & BUS_DMA_64BIT) == 0)
panic("Non dma-reachable buffer at curaddr %#lx(raw)",
curaddr);
/*
* Compute the segment size, and adjust counts.
*/
sgsize = PAGE_SIZE - ((u_long)vaddr & PGOFSET);
if (buflen < sgsize)
sgsize = buflen;
/*
* Make sure we don't cross any boundaries.
*/
if (map->_dm_boundary > 0) {
baddr = (curaddr + map->_dm_boundary) & bmask;
if (sgsize > (baddr - curaddr))
sgsize = (baddr - curaddr);
}
/*
* Insert chunk into a segment, coalescing with
* previous segment if possible.
*/
if (first) {
map->dm_segs[seg].ds_addr = curaddr;
map->dm_segs[seg].ds_len = sgsize;
first = 0;
} else {
if (curaddr == lastaddr &&
(map->dm_segs[seg].ds_len + sgsize) <=
map->_dm_maxsegsz &&
(map->_dm_boundary == 0 ||
(map->dm_segs[seg].ds_addr & bmask) ==
(curaddr & bmask)))
map->dm_segs[seg].ds_len += sgsize;
else {
if (++seg >= map->_dm_segcnt)
break;
map->dm_segs[seg].ds_addr = curaddr;
map->dm_segs[seg].ds_len = sgsize;
}
}
lastaddr = curaddr + sgsize;
vaddr += sgsize;
buflen -= sgsize;
}
*segp = seg;
*lastaddrp = lastaddr;
/*
* Did we fit?
*/
if (buflen != 0)
return (EFBIG); /* XXX better return value here? */
return (0);
}
/*
* Allocate physical memory from the given physical address range.
* Called by DMA-safe memory allocation methods.
*/
int
_bus_dmamem_alloc_range(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
int flags, bus_addr_t low, bus_addr_t high)
{
paddr_t curaddr, lastaddr;
struct vm_page *m;
struct pglist mlist;
int curseg, error, plaflag;
/* Always round the size. */
size = round_page(size);
segs[0]._ds_boundary = boundary;
segs[0]._ds_align = alignment;
/*
* Allocate pages from the VM system.
*/
plaflag = flags & BUS_DMA_NOWAIT ? UVM_PLA_NOWAIT : UVM_PLA_WAITOK;
if (flags & BUS_DMA_ZERO)
plaflag |= UVM_PLA_ZERO;
TAILQ_INIT(&mlist);
error = uvm_pglistalloc(size, low, high, alignment, boundary,
&mlist, nsegs, plaflag);
if (error)
return (error);
/*
* Compute the location, size, and number of segments actually
* returned by the VM code.
*/
m = TAILQ_FIRST(&mlist);
curseg = 0;
lastaddr = segs[curseg].ds_addr = VM_PAGE_TO_PHYS(m);
segs[curseg].ds_len = PAGE_SIZE;
for (m = TAILQ_NEXT(m, pageq); m != NULL; m = TAILQ_NEXT(m, pageq)) {
curaddr = VM_PAGE_TO_PHYS(m);
#ifdef DIAGNOSTIC
if (curseg == nsegs) {
printf("uvm_pglistalloc returned too many\n");
panic("_bus_dmamem_alloc_range");
}
if (curaddr < low || curaddr >= high) {
printf("uvm_pglistalloc returned non-sensical"
" address 0x%lx\n", curaddr);
panic("_bus_dmamem_alloc_range");
}
#endif
if (curaddr == (lastaddr + PAGE_SIZE))
segs[curseg].ds_len += PAGE_SIZE;
else {
curseg++;
segs[curseg].ds_addr = curaddr;
segs[curseg].ds_len = PAGE_SIZE;
}
lastaddr = curaddr;
}
*rsegs = curseg + 1;
return (0);
}
10
87
96
36
60
20
6
71
7
69
126
23
2
21
4
2
11
143
78
65
13
114
111
2
3
3
114
4
111
119
57
77
69
1
3
1
3
117
114
6
3
9
9
2
3
3
2
120
15
15
258
2
254
1
29
27
3
3
27
571
1
576
464
93
33
1
27
460
4
112
144
2
431
38
555
4
6
31
4
555
12
565
566
4
549
292
283
570
571
144
507
69
43
26
199
314
146
56
5
5
56
154
21
133
129
30
144
6
2
145
3
142
17
48
7
30
4
91
4
1
1
96
3
83
43
47
9
96
9
39
35
4
96
2
1
1
22
2
17
1
2
26
6
20
80
58
5
58
12
38
38
1
9
33
5
8
50
3
2
3
5
42
2
5
3
1
3
14
8
36
92
24
8
32
91
2
92
64
29
14
24
152
93
57
44
45
3
1
1
12
1
3
2
34
3
1
20
1
1
1
7
18
7
16
27
15
6
15
25
4
2
23
4
15
2
1
16
3
11
5
2
6
2
8
7
3
1
6
1
1
6
2
3
3
1
7
4
5
7
23
25
2
17
538
532
3
3
539
534
298
304
5
309
801
2
648
75
5
4
2
3
36
21
7
4
4
8
1
1
6
1
2
3
2
2
4
2
4
3
3
2
7
2
23
17
67
52
188
2
118
27
4
2
2
2
2
2
2
2
2
2
3
2
1
3
2
2
4
69
1
43
25
15
6
153
154
4
5
4
3
5
136
5
42
3
5
6
3
31
4
17
24
24
11
16
4
16
5
18
3
2
23
72
209
27
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
/* $OpenBSD: uipc_socket.c,v 1.289 2022/09/05 14:56:08 bluhm Exp $ */
/* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/event.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/unpcb.h>
#include <sys/socketvar.h>
#include <sys/signalvar.h>
#include <sys/pool.h>
#include <sys/atomic.h>
#include <sys/rwlock.h>
#include <sys/time.h>
#include <sys/refcnt.h>
#ifdef DDB
#include <machine/db_machdep.h>
#endif
void sbsync(struct sockbuf *, struct mbuf *);
int sosplice(struct socket *, int, off_t, struct timeval *);
void sounsplice(struct socket *, struct socket *, int);
void soidle(void *);
void sotask(void *);
void soreaper(void *);
void soput(void *);
int somove(struct socket *, int);
void sorflush(struct socket *);
void filt_sordetach(struct knote *kn);
int filt_soread(struct knote *kn, long hint);
void filt_sowdetach(struct knote *kn);
int filt_sowrite(struct knote *kn, long hint);
int filt_soexcept(struct knote *kn, long hint);
int filt_solisten(struct knote *kn, long hint);
int filt_somodify(struct kevent *kev, struct knote *kn);
int filt_soprocess(struct knote *kn, struct kevent *kev);
const struct filterops solisten_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_sordetach,
.f_event = filt_solisten,
.f_modify = filt_somodify,
.f_process = filt_soprocess,
};
const struct filterops soread_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_sordetach,
.f_event = filt_soread,
.f_modify = filt_somodify,
.f_process = filt_soprocess,
};
const struct filterops sowrite_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_sowdetach,
.f_event = filt_sowrite,
.f_modify = filt_somodify,
.f_process = filt_soprocess,
};
const struct filterops soexcept_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_sordetach,
.f_event = filt_soexcept,
.f_modify = filt_somodify,
.f_process = filt_soprocess,
};
#ifndef SOMINCONN
#define SOMINCONN 80
#endif /* SOMINCONN */
int somaxconn = SOMAXCONN;
int sominconn = SOMINCONN;
struct pool socket_pool;
#ifdef SOCKET_SPLICE
struct pool sosplice_pool;
struct taskq *sosplice_taskq;
struct rwlock sosplice_lock = RWLOCK_INITIALIZER("sosplicelk");
#endif
void
soinit(void)
{
pool_init(&socket_pool, sizeof(struct socket), 0, IPL_SOFTNET, 0,
"sockpl", NULL);
#ifdef SOCKET_SPLICE
pool_init(&sosplice_pool, sizeof(struct sosplice), 0, IPL_SOFTNET, 0,
"sosppl", NULL);
#endif
}
struct socket *
soalloc(int prflags)
{
struct socket *so;
so = pool_get(&socket_pool, prflags);
if (so == NULL)
return (NULL);
rw_init_flags(&so->so_lock, "solock", RWL_DUPOK);
refcnt_init(&so->so_refcnt);
return (so);
}
/*
* Socket operation routines.
* These routines are called by the routines in
* sys_socket.c or from a system process, and
* implement the semantics of socket operations by
* switching out to the protocol specific routines.
*/
int
socreate(int dom, struct socket **aso, int type, int proto)
{
struct proc *p = curproc; /* XXX */
const struct protosw *prp;
struct socket *so;
int error;
if (proto)
prp = pffindproto(dom, proto, type);
else
prp = pffindtype(dom, type);
if (prp == NULL || prp->pr_usrreqs == NULL)
return (EPROTONOSUPPORT);
if (prp->pr_type != type)
return (EPROTOTYPE);
so = soalloc(PR_WAITOK | PR_ZERO);
klist_init(&so->so_rcv.sb_sel.si_note, &socket_klistops, so);
klist_init(&so->so_snd.sb_sel.si_note, &socket_klistops, so);
sigio_init(&so->so_sigio);
TAILQ_INIT(&so->so_q0);
TAILQ_INIT(&so->so_q);
so->so_type = type;
if (suser(p) == 0)
so->so_state = SS_PRIV;
so->so_ruid = p->p_ucred->cr_ruid;
so->so_euid = p->p_ucred->cr_uid;
so->so_rgid = p->p_ucred->cr_rgid;
so->so_egid = p->p_ucred->cr_gid;
so->so_cpid = p->p_p->ps_pid;
so->so_proto = prp;
so->so_snd.sb_timeo_nsecs = INFSLP;
so->so_rcv.sb_timeo_nsecs = INFSLP;
solock(so);
error = pru_attach(so, proto);
if (error) {
so->so_state |= SS_NOFDREF;
/* sofree() calls sounlock(). */
sofree(so, 0);
return (error);
}
sounlock(so);
*aso = so;
return (0);
}
int
sobind(struct socket *so, struct mbuf *nam, struct proc *p)
{
soassertlocked(so);
return pru_bind(so, nam, p);
}
int
solisten(struct socket *so, int backlog)
{
int error;
soassertlocked(so);
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING|SS_ISDISCONNECTING))
return (EINVAL);
#ifdef SOCKET_SPLICE
if (isspliced(so) || issplicedback(so))
return (EOPNOTSUPP);
#endif /* SOCKET_SPLICE */
error = pru_listen(so);
if (error)
return (error);
if (TAILQ_FIRST(&so->so_q) == NULL)
so->so_options |= SO_ACCEPTCONN;
if (backlog < 0 || backlog > somaxconn)
backlog = somaxconn;
if (backlog < sominconn)
backlog = sominconn;
so->so_qlimit = backlog;
return (0);
}
#define SOSP_FREEING_READ 1
#define SOSP_FREEING_WRITE 2
void
sofree(struct socket *so, int keep_lock)
{
int persocket = solock_persocket(so);
soassertlocked(so);
if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) {
if (!keep_lock)
sounlock(so);
return;
}
if (so->so_head) {
struct socket *head = so->so_head;
/*
* We must not decommission a socket that's on the accept(2)
* queue. If we do, then accept(2) may hang after select(2)
* indicated that the listening socket was ready.
*/
if (so->so_onq == &head->so_q) {
if (!keep_lock)
sounlock(so);
return;
}
if (persocket) {
/*
* Concurrent close of `head' could
* abort `so' due to re-lock.
*/
soref(so);
soref(head);
sounlock(so);
solock(head);
solock(so);
if (so->so_onq != &head->so_q0) {
sounlock(head);
sounlock(so);
sorele(head);
sorele(so);
return;
}
sorele(head);
sorele(so);
}
soqremque(so, 0);
if (persocket)
sounlock(head);
}
if (persocket) {
sounlock(so);
refcnt_finalize(&so->so_refcnt, "sofinal");
solock(so);
}
sigio_free(&so->so_sigio);
klist_free(&so->so_rcv.sb_sel.si_note);
klist_free(&so->so_snd.sb_sel.si_note);
#ifdef SOCKET_SPLICE
if (so->so_sp) {
if (issplicedback(so)) {
int freeing = SOSP_FREEING_WRITE;
if (so->so_sp->ssp_soback == so)
freeing |= SOSP_FREEING_READ;
sounsplice(so->so_sp->ssp_soback, so, freeing);
}
if (isspliced(so)) {
int freeing = SOSP_FREEING_READ;
if (so == so->so_sp->ssp_socket)
freeing |= SOSP_FREEING_WRITE;
sounsplice(so, so->so_sp->ssp_socket, freeing);
}
}
#endif /* SOCKET_SPLICE */
sbrelease(so, &so->so_snd);
sorflush(so);
if (!keep_lock)
sounlock(so);
#ifdef SOCKET_SPLICE
if (so->so_sp) {
/* Reuse splice idle, sounsplice() has been called before. */
timeout_set_proc(&so->so_sp->ssp_idleto, soreaper, so);
timeout_add(&so->so_sp->ssp_idleto, 0);
} else
#endif /* SOCKET_SPLICE */
{
pool_put(&socket_pool, so);
}
}
static inline uint64_t
solinger_nsec(struct socket *so)
{
if (so->so_linger == 0)
return INFSLP;
return SEC_TO_NSEC(so->so_linger);
}
/*
* Close a socket on last file table reference removal.
* Initiate disconnect if connected.
* Free socket when disconnect complete.
*/
int
soclose(struct socket *so, int flags)
{
struct socket *so2;
int error = 0;
solock(so);
/* Revoke async IO early. There is a final revocation in sofree(). */
sigio_free(&so->so_sigio);
if (so->so_state & SS_ISCONNECTED) {
if (so->so_pcb == NULL)
goto discard;
if ((so->so_state & SS_ISDISCONNECTING) == 0) {
error = sodisconnect(so);
if (error)
goto drop;
}
if (so->so_options & SO_LINGER) {
if ((so->so_state & SS_ISDISCONNECTING) &&
(flags & MSG_DONTWAIT))
goto drop;
while (so->so_state & SS_ISCONNECTED) {
error = sosleep_nsec(so, &so->so_timeo,
PSOCK | PCATCH, "netcls",
solinger_nsec(so));
if (error)
break;
}
}
}
drop:
if (so->so_pcb) {
int error2;
error2 = pru_detach(so);
if (error == 0)
error = error2;
}
if (so->so_options & SO_ACCEPTCONN) {
int persocket = solock_persocket(so);
if (persocket) {
/* Wait concurrent sonewconn() threads. */
while (so->so_newconn > 0) {
so->so_state |= SS_NEWCONN_WAIT;
sosleep_nsec(so, &so->so_newconn, PSOCK,
"netlck", INFSLP);
}
}
while ((so2 = TAILQ_FIRST(&so->so_q0)) != NULL) {
if (persocket)
solock(so2);
(void) soqremque(so2, 0);
if (persocket)
sounlock(so);
soabort(so2);
if (persocket)
solock(so);
}
while ((so2 = TAILQ_FIRST(&so->so_q)) != NULL) {
if (persocket)
solock(so2);
(void) soqremque(so2, 1);
if (persocket)
sounlock(so);
soabort(so2);
if (persocket)
solock(so);
}
}
discard:
if (so->so_state & SS_NOFDREF)
panic("soclose NOFDREF: so %p, so_type %d", so, so->so_type);
so->so_state |= SS_NOFDREF;
/* sofree() calls sounlock(). */
sofree(so, 0);
return (error);
}
void
soabort(struct socket *so)
{
soassertlocked(so);
pru_abort(so);
}
int
soaccept(struct socket *so, struct mbuf *nam)
{
int error = 0;
soassertlocked(so);
if ((so->so_state & SS_NOFDREF) == 0)
panic("soaccept !NOFDREF: so %p, so_type %d", so, so->so_type);
so->so_state &= ~SS_NOFDREF;
if ((so->so_state & SS_ISDISCONNECTED) == 0 ||
(so->so_proto->pr_flags & PR_ABRTACPTDIS) == 0)
error = pru_accept(so, nam);
else
error = ECONNABORTED;
return (error);
}
int
soconnect(struct socket *so, struct mbuf *nam)
{
int error;
soassertlocked(so);
if (so->so_options & SO_ACCEPTCONN)
return (EOPNOTSUPP);
/*
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first.
* This allows user to disconnect by connecting to, e.g.,
* a null address.
*/
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
(error = sodisconnect(so))))
error = EISCONN;
else
error = pru_connect(so, nam);
return (error);
}
int
soconnect2(struct socket *so1, struct socket *so2)
{
int persocket, error;
if ((persocket = solock_persocket(so1)))
solock_pair(so1, so2);
else
solock(so1);
error = pru_connect2(so1, so2);
if (persocket)
sounlock(so2);
sounlock(so1);
return (error);
}
int
sodisconnect(struct socket *so)
{
int error;
soassertlocked(so);
if ((so->so_state & SS_ISCONNECTED) == 0)
return (ENOTCONN);
if (so->so_state & SS_ISDISCONNECTING)
return (EALREADY);
error = pru_disconnect(so);
return (error);
}
int m_getuio(struct mbuf **, int, long, struct uio *);
#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
/*
* Send on a socket.
* If send must go all at once and message is larger than
* send buffering, then hard error.
* Lock against other senders.
* If must go all at once and not enough room now, then
* inform user that this would block and do nothing.
* Otherwise, if nonblocking, send as much as possible.
* The data to be sent is described by "uio" if nonzero,
* otherwise by the mbuf chain "top" (which must be null
* if uio is not). Data provided in mbuf chain must be small
* enough to send all at once.
*
* Returns nonzero on error, timeout or signal; callers
* must check for short counts if EINTR/ERESTART are returned.
* Data and control buffers are freed on return.
*/
int
sosend(struct socket *so, struct mbuf *addr, struct uio *uio, struct mbuf *top,
struct mbuf *control, int flags)
{
long space, clen = 0;
size_t resid;
int error;
int atomic = sosendallatonce(so) || top;
if (uio)
resid = uio->uio_resid;
else
resid = top->m_pkthdr.len;
/* MSG_EOR on a SOCK_STREAM socket is invalid. */
if (so->so_type == SOCK_STREAM && (flags & MSG_EOR)) {
m_freem(top);
m_freem(control);
return (EINVAL);
}
if (uio && uio->uio_procp)
uio->uio_procp->p_ru.ru_msgsnd++;
if (control) {
/*
* In theory clen should be unsigned (since control->m_len is).
* However, space must be signed, as it might be less than 0
* if we over-committed, and we must use a signed comparison
* of space and clen.
*/
clen = control->m_len;
/* reserve extra space for AF_UNIX's internalize */
if (so->so_proto->pr_domain->dom_family == AF_UNIX &&
clen >= CMSG_ALIGN(sizeof(struct cmsghdr)) &&
mtod(control, struct cmsghdr *)->cmsg_type == SCM_RIGHTS)
clen = CMSG_SPACE(
(clen - CMSG_ALIGN(sizeof(struct cmsghdr))) *
(sizeof(struct fdpass) / sizeof(int)));
}
#define snderr(errno) { error = errno; goto release; }
solock(so);
restart:
if ((error = sblock(so, &so->so_snd, SBLOCKWAIT(flags))) != 0)
goto out;
so->so_state |= SS_ISSENDING;
do {
if (so->so_state & SS_CANTSENDMORE)
snderr(EPIPE);
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
snderr(error);
}
if ((so->so_state & SS_ISCONNECTED) == 0) {
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
if (!(resid == 0 && clen != 0))
snderr(ENOTCONN);
} else if (addr == NULL)
snderr(EDESTADDRREQ);
}
space = sbspace(so, &so->so_snd);
if (flags & MSG_OOB)
space += 1024;
if (so->so_proto->pr_domain->dom_family == AF_UNIX) {
if (atomic && resid > so->so_snd.sb_hiwat)
snderr(EMSGSIZE);
} else {
if (clen > so->so_snd.sb_hiwat ||
(atomic && resid > so->so_snd.sb_hiwat - clen))
snderr(EMSGSIZE);
}
if (space < clen ||
(space - clen < resid &&
(atomic || space < so->so_snd.sb_lowat))) {
if (flags & MSG_DONTWAIT)
snderr(EWOULDBLOCK);
sbunlock(so, &so->so_snd);
error = sbwait(so, &so->so_snd);
so->so_state &= ~SS_ISSENDING;
if (error)
goto out;
goto restart;
}
space -= clen;
do {
if (uio == NULL) {
/*
* Data is prepackaged in "top".
*/
resid = 0;
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
} else {
sounlock(so);
error = m_getuio(&top, atomic, space, uio);
solock(so);
if (error)
goto release;
space -= top->m_pkthdr.len;
resid = uio->uio_resid;
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
}
if (resid == 0)
so->so_state &= ~SS_ISSENDING;
if (top && so->so_options & SO_ZEROIZE)
top->m_flags |= M_ZEROIZE;
if (flags & MSG_OOB)
error = pru_sendoob(so, top, addr, control);
else
error = pru_send(so, top, addr, control);
clen = 0;
control = NULL;
top = NULL;
if (error)
goto release;
} while (resid && space > 0);
} while (resid);
release:
so->so_state &= ~SS_ISSENDING;
sbunlock(so, &so->so_snd);
out:
sounlock(so);
m_freem(top);
m_freem(control);
return (error);
}
int
m_getuio(struct mbuf **mp, int atomic, long space, struct uio *uio)
{
struct mbuf *m, *top = NULL;
struct mbuf **nextp = ⊤
u_long len, mlen;
size_t resid = uio->uio_resid;
int error;
do {
if (top == NULL) {
MGETHDR(m, M_WAIT, MT_DATA);
mlen = MHLEN;
m->m_pkthdr.len = 0;
m->m_pkthdr.ph_ifidx = 0;
} else {
MGET(m, M_WAIT, MT_DATA);
mlen = MLEN;
}
/* chain mbuf together */
*nextp = m;
nextp = &m->m_next;
resid = ulmin(resid, space);
if (resid >= MINCLSIZE) {
MCLGETL(m, M_NOWAIT, ulmin(resid, MAXMCLBYTES));
if ((m->m_flags & M_EXT) == 0)
MCLGETL(m, M_NOWAIT, MCLBYTES);
if ((m->m_flags & M_EXT) == 0)
goto nopages;
mlen = m->m_ext.ext_size;
len = ulmin(mlen, resid);
/*
* For datagram protocols, leave room
* for protocol headers in first mbuf.
*/
if (atomic && m == top && len < mlen - max_hdr)
m->m_data += max_hdr;
} else {
nopages:
len = ulmin(mlen, resid);
/*
* For datagram protocols, leave room
* for protocol headers in first mbuf.
*/
if (atomic && m == top && len < mlen - max_hdr)
m_align(m, len);
}
error = uiomove(mtod(m, caddr_t), len, uio);
if (error) {
m_freem(top);
return (error);
}
/* adjust counters */
resid = uio->uio_resid;
space -= len;
m->m_len = len;
top->m_pkthdr.len += len;
/* Is there more space and more data? */
} while (space > 0 && resid > 0);
*mp = top;
return 0;
}
/*
* Following replacement or removal of the first mbuf on the first
* mbuf chain of a socket buffer, push necessary state changes back
* into the socket buffer so that other consumers see the values
* consistently. 'nextrecord' is the callers locally stored value of
* the original value of sb->sb_mb->m_nextpkt which must be restored
* when the lead mbuf changes. NOTE: 'nextrecord' may be NULL.
*/
void
sbsync(struct sockbuf *sb, struct mbuf *nextrecord)
{
/*
* First, update for the new value of nextrecord. If necessary,
* make it the first record.
*/
if (sb->sb_mb != NULL)
sb->sb_mb->m_nextpkt = nextrecord;
else
sb->sb_mb = nextrecord;
/*
* Now update any dependent socket buffer fields to reflect
* the new state. This is an inline of SB_EMPTY_FIXUP, with
* the addition of a second clause that takes care of the
* case where sb_mb has been updated, but remains the last
* record.
*/
if (sb->sb_mb == NULL) {
sb->sb_mbtail = NULL;
sb->sb_lastrecord = NULL;
} else if (sb->sb_mb->m_nextpkt == NULL)
sb->sb_lastrecord = sb->sb_mb;
}
/*
* Implement receive operations on a socket.
* We depend on the way that records are added to the sockbuf
* by sbappend*. In particular, each record (mbufs linked through m_next)
* must begin with an address if the protocol so specifies,
* followed by an optional mbuf or mbufs containing ancillary data,
* and then zero or more mbufs of data.
* In order to avoid blocking network for the entire time here, we release
* the solock() while doing the actual copy to user space.
* Although the sockbuf is locked, new data may still be appended,
* and thus we must maintain consistency of the sockbuf during that time.
*
* The caller may receive the data as a single mbuf chain by supplying
* an mbuf **mp0 for use in returning the chain. The uio is then used
* only for the count in uio_resid.
*/
int
soreceive(struct socket *so, struct mbuf **paddr, struct uio *uio,
struct mbuf **mp0, struct mbuf **controlp, int *flagsp,
socklen_t controllen)
{
struct mbuf *m, **mp;
struct mbuf *cm;
u_long len, offset, moff;
int flags, error, type, uio_error = 0;
const struct protosw *pr = so->so_proto;
struct mbuf *nextrecord;
size_t resid, orig_resid = uio->uio_resid;
mp = mp0;
if (paddr)
*paddr = NULL;
if (controlp)
*controlp = NULL;
if (flagsp)
flags = *flagsp &~ MSG_EOR;
else
flags = 0;
if (flags & MSG_OOB) {
m = m_get(M_WAIT, MT_DATA);
solock(so);
error = pru_rcvoob(so, m, flags & MSG_PEEK);
sounlock(so);
if (error)
goto bad;
do {
error = uiomove(mtod(m, caddr_t),
ulmin(uio->uio_resid, m->m_len), uio);
m = m_free(m);
} while (uio->uio_resid && error == 0 && m);
bad:
m_freem(m);
return (error);
}
if (mp)
*mp = NULL;
solock_shared(so);
restart:
if ((error = sblock(so, &so->so_rcv, SBLOCKWAIT(flags))) != 0) {
sounlock_shared(so);
return (error);
}
m = so->so_rcv.sb_mb;
#ifdef SOCKET_SPLICE
if (isspliced(so))
m = NULL;
#endif /* SOCKET_SPLICE */
/*
* If we have less data than requested, block awaiting more
* (subject to any timeout) if:
* 1. the current count is less than the low water mark,
* 2. MSG_WAITALL is set, and it is possible to do the entire
* receive operation at once if we block (resid <= hiwat), or
* 3. MSG_DONTWAIT is not set.
* If MSG_WAITALL is set but resid is larger than the receive buffer,
* we have to do the receive in sections, and thus risk returning
* a short count if a timeout or signal occurs after we start.
*/
if (m == NULL || (((flags & MSG_DONTWAIT) == 0 &&
so->so_rcv.sb_cc < uio->uio_resid) &&
(so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) &&
m->m_nextpkt == NULL && (pr->pr_flags & PR_ATOMIC) == 0)) {
#ifdef DIAGNOSTIC
if (m == NULL && so->so_rcv.sb_cc)
#ifdef SOCKET_SPLICE
if (!isspliced(so))
#endif /* SOCKET_SPLICE */
panic("receive 1: so %p, so_type %d, sb_cc %lu",
so, so->so_type, so->so_rcv.sb_cc);
#endif
if (so->so_error) {
if (m)
goto dontblock;
error = so->so_error;
if ((flags & MSG_PEEK) == 0)
so->so_error = 0;
goto release;
}
if (so->so_state & SS_CANTRCVMORE) {
if (m)
goto dontblock;
else if (so->so_rcv.sb_cc == 0)
goto release;
}
for (; m; m = m->m_next)
if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
m = so->so_rcv.sb_mb;
goto dontblock;
}
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
error = ENOTCONN;
goto release;
}
if (uio->uio_resid == 0 && controlp == NULL)
goto release;
if (flags & MSG_DONTWAIT) {
error = EWOULDBLOCK;
goto release;
}
SBLASTRECORDCHK(&so->so_rcv, "soreceive sbwait 1");
SBLASTMBUFCHK(&so->so_rcv, "soreceive sbwait 1");
sbunlock(so, &so->so_rcv);
error = sbwait(so, &so->so_rcv);
if (error) {
sounlock_shared(so);
return (error);
}
goto restart;
}
dontblock:
/*
* On entry here, m points to the first record of the socket buffer.
* From this point onward, we maintain 'nextrecord' as a cache of the
* pointer to the next record in the socket buffer. We must keep the
* various socket buffer pointers and local stack versions of the
* pointers in sync, pushing out modifications before operations that
* may sleep, and re-reading them afterwards.
*
* Otherwise, we will race with the network stack appending new data
* or records onto the socket buffer by using inconsistent/stale
* versions of the field, possibly resulting in socket buffer
* corruption.
*/
if (uio->uio_procp)
uio->uio_procp->p_ru.ru_msgrcv++;
KASSERT(m == so->so_rcv.sb_mb);
SBLASTRECORDCHK(&so->so_rcv, "soreceive 1");
SBLASTMBUFCHK(&so->so_rcv, "soreceive 1");
nextrecord = m->m_nextpkt;
if (pr->pr_flags & PR_ADDR) {
#ifdef DIAGNOSTIC
if (m->m_type != MT_SONAME)
panic("receive 1a: so %p, so_type %d, m %p, m_type %d",
so, so->so_type, m, m->m_type);
#endif
orig_resid = 0;
if (flags & MSG_PEEK) {
if (paddr)
*paddr = m_copym(m, 0, m->m_len, M_NOWAIT);
m = m->m_next;
} else {
sbfree(so, &so->so_rcv, m);
if (paddr) {
*paddr = m;
so->so_rcv.sb_mb = m->m_next;
m->m_next = NULL;
m = so->so_rcv.sb_mb;
} else {
so->so_rcv.sb_mb = m_free(m);
m = so->so_rcv.sb_mb;
}
sbsync(&so->so_rcv, nextrecord);
}
}
while (m && m->m_type == MT_CONTROL && error == 0) {
int skip = 0;
if (flags & MSG_PEEK) {
if (mtod(m, struct cmsghdr *)->cmsg_type ==
SCM_RIGHTS) {
/* don't leak internalized SCM_RIGHTS msgs */
skip = 1;
} else if (controlp)
*controlp = m_copym(m, 0, m->m_len, M_NOWAIT);
m = m->m_next;
} else {
sbfree(so, &so->so_rcv, m);
so->so_rcv.sb_mb = m->m_next;
m->m_nextpkt = m->m_next = NULL;
cm = m;
m = so->so_rcv.sb_mb;
sbsync(&so->so_rcv, nextrecord);
if (controlp) {
if (pr->pr_domain->dom_externalize) {
sounlock_shared(so);
error =
(*pr->pr_domain->dom_externalize)
(cm, controllen, flags);
solock_shared(so);
}
*controlp = cm;
} else {
/*
* Dispose of any SCM_RIGHTS message that went
* through the read path rather than recv.
*/
if (pr->pr_domain->dom_dispose)
pr->pr_domain->dom_dispose(cm);
m_free(cm);
}
}
if (m != NULL)
nextrecord = so->so_rcv.sb_mb->m_nextpkt;
else
nextrecord = so->so_rcv.sb_mb;
if (controlp && !skip)
controlp = &(*controlp)->m_next;
orig_resid = 0;
}
/* If m is non-NULL, we have some data to read. */
if (m) {
type = m->m_type;
if (type == MT_OOBDATA)
flags |= MSG_OOB;
if (m->m_flags & M_BCAST)
flags |= MSG_BCAST;
if (m->m_flags & M_MCAST)
flags |= MSG_MCAST;
}
SBLASTRECORDCHK(&so->so_rcv, "soreceive 2");
SBLASTMBUFCHK(&so->so_rcv, "soreceive 2");
moff = 0;
offset = 0;
while (m && uio->uio_resid > 0 && error == 0) {
if (m->m_type == MT_OOBDATA) {
if (type != MT_OOBDATA)
break;
} else if (type == MT_OOBDATA) {
break;
} else if (m->m_type == MT_CONTROL) {
/*
* If there is more than one control message in the
* stream, we do a short read. Next can be received
* or disposed by another system call.
*/
break;
#ifdef DIAGNOSTIC
} else if (m->m_type != MT_DATA && m->m_type != MT_HEADER) {
panic("receive 3: so %p, so_type %d, m %p, m_type %d",
so, so->so_type, m, m->m_type);
#endif
}
so->so_state &= ~SS_RCVATMARK;
len = uio->uio_resid;
if (so->so_oobmark && len > so->so_oobmark - offset)
len = so->so_oobmark - offset;
if (len > m->m_len - moff)
len = m->m_len - moff;
/*
* If mp is set, just pass back the mbufs.
* Otherwise copy them out via the uio, then free.
* Sockbuf must be consistent here (points to current mbuf,
* it points to next record) when we drop priority;
* we must note any additions to the sockbuf when we
* block interrupts again.
*/
if (mp == NULL && uio_error == 0) {
SBLASTRECORDCHK(&so->so_rcv, "soreceive uiomove");
SBLASTMBUFCHK(&so->so_rcv, "soreceive uiomove");
resid = uio->uio_resid;
sounlock_shared(so);
uio_error = uiomove(mtod(m, caddr_t) + moff, len, uio);
solock_shared(so);
if (uio_error)
uio->uio_resid = resid - len;
} else
uio->uio_resid -= len;
if (len == m->m_len - moff) {
if (m->m_flags & M_EOR)
flags |= MSG_EOR;
if (flags & MSG_PEEK) {
m = m->m_next;
moff = 0;
orig_resid = 0;
} else {
nextrecord = m->m_nextpkt;
sbfree(so, &so->so_rcv, m);
if (mp) {
*mp = m;
mp = &m->m_next;
so->so_rcv.sb_mb = m = m->m_next;
*mp = NULL;
} else {
so->so_rcv.sb_mb = m_free(m);
m = so->so_rcv.sb_mb;
}
/*
* If m != NULL, we also know that
* so->so_rcv.sb_mb != NULL.
*/
KASSERT(so->so_rcv.sb_mb == m);
if (m) {
m->m_nextpkt = nextrecord;
if (nextrecord == NULL)
so->so_rcv.sb_lastrecord = m;
} else {
so->so_rcv.sb_mb = nextrecord;
SB_EMPTY_FIXUP(&so->so_rcv);
}
SBLASTRECORDCHK(&so->so_rcv, "soreceive 3");
SBLASTMBUFCHK(&so->so_rcv, "soreceive 3");
}
} else {
if (flags & MSG_PEEK) {
moff += len;
orig_resid = 0;
} else {
if (mp)
*mp = m_copym(m, 0, len, M_WAIT);
m->m_data += len;
m->m_len -= len;
so->so_rcv.sb_cc -= len;
so->so_rcv.sb_datacc -= len;
}
}
if (so->so_oobmark) {
if ((flags & MSG_PEEK) == 0) {
so->so_oobmark -= len;
if (so->so_oobmark == 0) {
so->so_state |= SS_RCVATMARK;
break;
}
} else {
offset += len;
if (offset == so->so_oobmark)
break;
}
}
if (flags & MSG_EOR)
break;
/*
* If the MSG_WAITALL flag is set (for non-atomic socket),
* we must not quit until "uio->uio_resid == 0" or an error
* termination. If a signal/timeout occurs, return
* with a short count but without error.
* Keep sockbuf locked against other readers.
*/
while (flags & MSG_WAITALL && m == NULL && uio->uio_resid > 0 &&
!sosendallatonce(so) && !nextrecord) {
if (so->so_error || so->so_state & SS_CANTRCVMORE)
break;
SBLASTRECORDCHK(&so->so_rcv, "soreceive sbwait 2");
SBLASTMBUFCHK(&so->so_rcv, "soreceive sbwait 2");
error = sbwait(so, &so->so_rcv);
if (error) {
sbunlock(so, &so->so_rcv);
sounlock_shared(so);
return (0);
}
if ((m = so->so_rcv.sb_mb) != NULL)
nextrecord = m->m_nextpkt;
}
}
if (m && pr->pr_flags & PR_ATOMIC) {
flags |= MSG_TRUNC;
if ((flags & MSG_PEEK) == 0)
(void) sbdroprecord(so, &so->so_rcv);
}
if ((flags & MSG_PEEK) == 0) {
if (m == NULL) {
/*
* First part is an inline SB_EMPTY_FIXUP(). Second
* part makes sure sb_lastrecord is up-to-date if
* there is still data in the socket buffer.
*/
so->so_rcv.sb_mb = nextrecord;
if (so->so_rcv.sb_mb == NULL) {
so->so_rcv.sb_mbtail = NULL;
so->so_rcv.sb_lastrecord = NULL;
} else if (nextrecord->m_nextpkt == NULL)
so->so_rcv.sb_lastrecord = nextrecord;
}
SBLASTRECORDCHK(&so->so_rcv, "soreceive 4");
SBLASTMBUFCHK(&so->so_rcv, "soreceive 4");
if (pr->pr_flags & PR_WANTRCVD)
pru_rcvd(so);
}
if (orig_resid == uio->uio_resid && orig_resid &&
(flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
sbunlock(so, &so->so_rcv);
goto restart;
}
if (uio_error)
error = uio_error;
if (flagsp)
*flagsp |= flags;
release:
sbunlock(so, &so->so_rcv);
sounlock_shared(so);
return (error);
}
int
soshutdown(struct socket *so, int how)
{
int error = 0;
solock(so);
switch (how) {
case SHUT_RD:
sorflush(so);
break;
case SHUT_RDWR:
sorflush(so);
/* FALLTHROUGH */
case SHUT_WR:
error = pru_shutdown(so);
break;
default:
error = EINVAL;
break;
}
sounlock(so);
return (error);
}
void
sorflush(struct socket *so)
{
struct sockbuf *sb = &so->so_rcv;
struct mbuf *m;
const struct protosw *pr = so->so_proto;
int error;
sb->sb_flags |= SB_NOINTR;
error = sblock(so, sb, M_WAITOK);
/* with SB_NOINTR and M_WAITOK sblock() must not fail */
KASSERT(error == 0);
socantrcvmore(so);
m = sb->sb_mb;
memset(&sb->sb_startzero, 0,
(caddr_t)&sb->sb_endzero - (caddr_t)&sb->sb_startzero);
sb->sb_timeo_nsecs = INFSLP;
sbunlock(so, sb);
if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose)
(*pr->pr_domain->dom_dispose)(m);
m_purge(m);
}
#ifdef SOCKET_SPLICE
#define so_splicelen so_sp->ssp_len
#define so_splicemax so_sp->ssp_max
#define so_idletv so_sp->ssp_idletv
#define so_idleto so_sp->ssp_idleto
#define so_splicetask so_sp->ssp_task
int
sosplice(struct socket *so, int fd, off_t max, struct timeval *tv)
{
struct file *fp;
struct socket *sosp;
struct sosplice *sp;
struct taskq *tq;
int error = 0;
soassertlocked(so);
if (sosplice_taskq == NULL) {
rw_enter_write(&sosplice_lock);
if (sosplice_taskq == NULL) {
tq = taskq_create("sosplice", 1, IPL_SOFTNET,
TASKQ_MPSAFE);
/* Ensure the taskq is fully visible to other CPUs. */
membar_producer();
sosplice_taskq = tq;
}
rw_exit_write(&sosplice_lock);
}
if (sosplice_taskq == NULL)
return (ENOMEM);
if ((so->so_proto->pr_flags & PR_SPLICE) == 0)
return (EPROTONOSUPPORT);
if (so->so_options & SO_ACCEPTCONN)
return (EOPNOTSUPP);
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
(so->so_proto->pr_flags & PR_CONNREQUIRED))
return (ENOTCONN);
if (so->so_sp == NULL) {
sp = pool_get(&sosplice_pool, PR_WAITOK | PR_ZERO);
if (so->so_sp == NULL)
so->so_sp = sp;
else
pool_put(&sosplice_pool, sp);
}
/* If no fd is given, unsplice by removing existing link. */
if (fd < 0) {
/* Lock receive buffer. */
if ((error = sblock(so, &so->so_rcv, M_WAITOK)) != 0) {
return (error);
}
if (so->so_sp->ssp_socket)
sounsplice(so, so->so_sp->ssp_socket, 0);
sbunlock(so, &so->so_rcv);
return (0);
}
if (max && max < 0)
return (EINVAL);
if (tv && (tv->tv_sec < 0 || !timerisvalid(tv)))
return (EINVAL);
/* Find sosp, the drain socket where data will be spliced into. */
if ((error = getsock(curproc, fd, &fp)) != 0)
return (error);
sosp = fp->f_data;
if (sosp->so_proto->pr_usrreqs->pru_send !=
so->so_proto->pr_usrreqs->pru_send) {
error = EPROTONOSUPPORT;
goto frele;
}
if (sosp->so_sp == NULL) {
sp = pool_get(&sosplice_pool, PR_WAITOK | PR_ZERO);
if (sosp->so_sp == NULL)
sosp->so_sp = sp;
else
pool_put(&sosplice_pool, sp);
}
/* Lock both receive and send buffer. */
if ((error = sblock(so, &so->so_rcv, M_WAITOK)) != 0) {
goto frele;
}
if ((error = sblock(so, &sosp->so_snd, M_WAITOK)) != 0) {
sbunlock(so, &so->so_rcv);
goto frele;
}
if (so->so_sp->ssp_socket || sosp->so_sp->ssp_soback) {
error = EBUSY;
goto release;
}
if (sosp->so_options & SO_ACCEPTCONN) {
error = EOPNOTSUPP;
goto release;
}
if ((sosp->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0) {
error = ENOTCONN;
goto release;
}
/* Splice so and sosp together. */
so->so_sp->ssp_socket = sosp;
sosp->so_sp->ssp_soback = so;
so->so_splicelen = 0;
so->so_splicemax = max;
if (tv)
so->so_idletv = *tv;
else
timerclear(&so->so_idletv);
timeout_set_proc(&so->so_idleto, soidle, so);
task_set(&so->so_splicetask, sotask, so);
/*
* To prevent softnet interrupt from calling somove() while
* we sleep, the socket buffers are not marked as spliced yet.
*/
if (somove(so, M_WAIT)) {
so->so_rcv.sb_flags |= SB_SPLICE;
sosp->so_snd.sb_flags |= SB_SPLICE;
}
release:
sbunlock(sosp, &sosp->so_snd);
sbunlock(so, &so->so_rcv);
frele:
/*
* FRELE() must not be called with the socket lock held. It is safe to
* release the lock here as long as no other operation happen on the
* socket when sosplice() returns. The dance could be avoided by
* grabbing the socket lock inside this function.
*/
sounlock(so);
FRELE(fp, curproc);
solock(so);
return (error);
}
void
sounsplice(struct socket *so, struct socket *sosp, int freeing)
{
soassertlocked(so);
task_del(sosplice_taskq, &so->so_splicetask);
timeout_del(&so->so_idleto);
sosp->so_snd.sb_flags &= ~SB_SPLICE;
so->so_rcv.sb_flags &= ~SB_SPLICE;
so->so_sp->ssp_socket = sosp->so_sp->ssp_soback = NULL;
/* Do not wakeup a socket that is about to be freed. */
if ((freeing & SOSP_FREEING_READ) == 0 && soreadable(so))
sorwakeup(so);
if ((freeing & SOSP_FREEING_WRITE) == 0 && sowriteable(sosp))
sowwakeup(sosp);
}
void
soidle(void *arg)
{
struct socket *so = arg;
solock(so);
if (so->so_rcv.sb_flags & SB_SPLICE) {
so->so_error = ETIMEDOUT;
sounsplice(so, so->so_sp->ssp_socket, 0);
}
sounlock(so);
}
void
sotask(void *arg)
{
struct socket *so = arg;
solock(so);
if (so->so_rcv.sb_flags & SB_SPLICE) {
/*
* We may not sleep here as sofree() and unsplice() may be
* called from softnet interrupt context. This would remove
* the socket during somove().
*/
somove(so, M_DONTWAIT);
}
sounlock(so);
/* Avoid user land starvation. */
yield();
}
/*
* The socket splicing task or idle timeout may sleep while grabbing the net
* lock. As sofree() can be called anytime, sotask() or soidle() could access
* the socket memory of a freed socket after wakeup. So delay the pool_put()
* after all pending socket splicing tasks or timeouts have finished. Do this
* by scheduling it on the same threads.
*/
void
soreaper(void *arg)
{
struct socket *so = arg;
/* Reuse splice task, sounsplice() has been called before. */
task_set(&so->so_sp->ssp_task, soput, so);
task_add(sosplice_taskq, &so->so_sp->ssp_task);
}
void
soput(void *arg)
{
struct socket *so = arg;
pool_put(&sosplice_pool, so->so_sp);
pool_put(&socket_pool, so);
}
/*
* Move data from receive buffer of spliced source socket to send
* buffer of drain socket. Try to move as much as possible in one
* big chunk. It is a TCP only implementation.
* Return value 0 means splicing has been finished, 1 continue.
*/
int
somove(struct socket *so, int wait)
{
struct socket *sosp = so->so_sp->ssp_socket;
struct mbuf *m, **mp, *nextrecord;
u_long len, off, oobmark;
long space;
int error = 0, maxreached = 0;
unsigned int state;
soassertlocked(so);
nextpkt:
if (so->so_error) {
error = so->so_error;
goto release;
}
if (sosp->so_state & SS_CANTSENDMORE) {
error = EPIPE;
goto release;
}
if (sosp->so_error && sosp->so_error != ETIMEDOUT &&
sosp->so_error != EFBIG && sosp->so_error != ELOOP) {
error = sosp->so_error;
goto release;
}
if ((sosp->so_state & SS_ISCONNECTED) == 0)
goto release;
/* Calculate how many bytes can be copied now. */
len = so->so_rcv.sb_datacc;
if (so->so_splicemax) {
KASSERT(so->so_splicelen < so->so_splicemax);
if (so->so_splicemax <= so->so_splicelen + len) {
len = so->so_splicemax - so->so_splicelen;
maxreached = 1;
}
}
space = sbspace(sosp, &sosp->so_snd);
if (so->so_oobmark && so->so_oobmark < len &&
so->so_oobmark < space + 1024)
space += 1024;
if (space <= 0) {
maxreached = 0;
goto release;
}
if (space < len) {
maxreached = 0;
if (space < sosp->so_snd.sb_lowat)
goto release;
len = space;
}
sosp->so_state |= SS_ISSENDING;
SBLASTRECORDCHK(&so->so_rcv, "somove 1");
SBLASTMBUFCHK(&so->so_rcv, "somove 1");
m = so->so_rcv.sb_mb;
if (m == NULL)
goto release;
nextrecord = m->m_nextpkt;
/* Drop address and control information not used with splicing. */
if (so->so_proto->pr_flags & PR_ADDR) {
#ifdef DIAGNOSTIC
if (m->m_type != MT_SONAME)
panic("somove soname: so %p, so_type %d, m %p, "
"m_type %d", so, so->so_type, m, m->m_type);
#endif
m = m->m_next;
}
while (m && m->m_type == MT_CONTROL)
m = m->m_next;
if (m == NULL) {
sbdroprecord(so, &so->so_rcv);
if (so->so_proto->pr_flags & PR_WANTRCVD)
pru_rcvd(so);
goto nextpkt;
}
/*
* By splicing sockets connected to localhost, userland might create a
* loop. Dissolve splicing with error if loop is detected by counter.
*
* If we deal with looped broadcast/multicast packet we bail out with
* no error to suppress splice termination.
*/
if ((m->m_flags & M_PKTHDR) &&
((m->m_pkthdr.ph_loopcnt++ >= M_MAXLOOP) ||
((m->m_flags & M_LOOP) && (m->m_flags & (M_BCAST|M_MCAST))))) {
error = ELOOP;
goto release;
}
if (so->so_proto->pr_flags & PR_ATOMIC) {
if ((m->m_flags & M_PKTHDR) == 0)
panic("somove !PKTHDR: so %p, so_type %d, m %p, "
"m_type %d", so, so->so_type, m, m->m_type);
if (sosp->so_snd.sb_hiwat < m->m_pkthdr.len) {
error = EMSGSIZE;
goto release;
}
if (len < m->m_pkthdr.len)
goto release;
if (m->m_pkthdr.len < len) {
maxreached = 0;
len = m->m_pkthdr.len;
}
/*
* Throw away the name mbuf after it has been assured
* that the whole first record can be processed.
*/
m = so->so_rcv.sb_mb;
sbfree(so, &so->so_rcv, m);
so->so_rcv.sb_mb = m_free(m);
sbsync(&so->so_rcv, nextrecord);
}
/*
* Throw away the control mbufs after it has been assured
* that the whole first record can be processed.
*/
m = so->so_rcv.sb_mb;
while (m && m->m_type == MT_CONTROL) {
sbfree(so, &so->so_rcv, m);
so->so_rcv.sb_mb = m_free(m);
m = so->so_rcv.sb_mb;
sbsync(&so->so_rcv, nextrecord);
}
SBLASTRECORDCHK(&so->so_rcv, "somove 2");
SBLASTMBUFCHK(&so->so_rcv, "somove 2");
/* Take at most len mbufs out of receive buffer. */
for (off = 0, mp = &m; off <= len && *mp;
off += (*mp)->m_len, mp = &(*mp)->m_next) {
u_long size = len - off;
#ifdef DIAGNOSTIC
if ((*mp)->m_type != MT_DATA && (*mp)->m_type != MT_HEADER)
panic("somove type: so %p, so_type %d, m %p, "
"m_type %d", so, so->so_type, *mp, (*mp)->m_type);
#endif
if ((*mp)->m_len > size) {
/*
* Move only a partial mbuf at maximum splice length or
* if the drain buffer is too small for this large mbuf.
*/
if (!maxreached && so->so_snd.sb_datacc > 0) {
len -= size;
break;
}
*mp = m_copym(so->so_rcv.sb_mb, 0, size, wait);
if (*mp == NULL) {
len -= size;
break;
}
so->so_rcv.sb_mb->m_data += size;
so->so_rcv.sb_mb->m_len -= size;
so->so_rcv.sb_cc -= size;
so->so_rcv.sb_datacc -= size;
} else {
*mp = so->so_rcv.sb_mb;
sbfree(so, &so->so_rcv, *mp);
so->so_rcv.sb_mb = (*mp)->m_next;
sbsync(&so->so_rcv, nextrecord);
}
}
*mp = NULL;
SBLASTRECORDCHK(&so->so_rcv, "somove 3");
SBLASTMBUFCHK(&so->so_rcv, "somove 3");
SBCHECK(so, &so->so_rcv);
if (m == NULL)
goto release;
m->m_nextpkt = NULL;
if (m->m_flags & M_PKTHDR) {
m_resethdr(m);
m->m_pkthdr.len = len;
}
/* Send window update to source peer as receive buffer has changed. */
if (so->so_proto->pr_flags & PR_WANTRCVD)
pru_rcvd(so);
/* Receive buffer did shrink by len bytes, adjust oob. */
state = so->so_state;
so->so_state &= ~SS_RCVATMARK;
oobmark = so->so_oobmark;
so->so_oobmark = oobmark > len ? oobmark - len : 0;
if (oobmark) {
if (oobmark == len)
so->so_state |= SS_RCVATMARK;
if (oobmark >= len)
oobmark = 0;
}
/*
* Handle oob data. If any malloc fails, ignore error.
* TCP urgent data is not very reliable anyway.
*/
while (((state & SS_RCVATMARK) || oobmark) &&
(so->so_options & SO_OOBINLINE)) {
struct mbuf *o = NULL;
if (state & SS_RCVATMARK) {
o = m_get(wait, MT_DATA);
state &= ~SS_RCVATMARK;
} else if (oobmark) {
o = m_split(m, oobmark, wait);
if (o) {
error = pru_send(sosp, m, NULL, NULL);
if (error) {
if (sosp->so_state & SS_CANTSENDMORE)
error = EPIPE;
m_freem(o);
goto release;
}
len -= oobmark;
so->so_splicelen += oobmark;
m = o;
o = m_get(wait, MT_DATA);
}
oobmark = 0;
}
if (o) {
o->m_len = 1;
*mtod(o, caddr_t) = *mtod(m, caddr_t);
error = pru_sendoob(sosp, o, NULL, NULL);
if (error) {
if (sosp->so_state & SS_CANTSENDMORE)
error = EPIPE;
m_freem(m);
goto release;
}
len -= 1;
so->so_splicelen += 1;
if (oobmark) {
oobmark -= 1;
if (oobmark == 0)
state |= SS_RCVATMARK;
}
m_adj(m, 1);
}
}
/* Append all remaining data to drain socket. */
if (so->so_rcv.sb_cc == 0 || maxreached)
sosp->so_state &= ~SS_ISSENDING;
error = pru_send(sosp, m, NULL, NULL);
if (error) {
if (sosp->so_state & SS_CANTSENDMORE)
error = EPIPE;
goto release;
}
so->so_splicelen += len;
/* Move several packets if possible. */
if (!maxreached && nextrecord)
goto nextpkt;
release:
sosp->so_state &= ~SS_ISSENDING;
if (!error && maxreached && so->so_splicemax == so->so_splicelen)
error = EFBIG;
if (error)
so->so_error = error;
if (((so->so_state & SS_CANTRCVMORE) && so->so_rcv.sb_cc == 0) ||
(sosp->so_state & SS_CANTSENDMORE) || maxreached || error) {
sounsplice(so, sosp, 0);
return (0);
}
if (timerisset(&so->so_idletv))
timeout_add_tv(&so->so_idleto, &so->so_idletv);
return (1);
}
#endif /* SOCKET_SPLICE */
void
sorwakeup(struct socket *so)
{
soassertlocked(so);
#ifdef SOCKET_SPLICE
if (so->so_rcv.sb_flags & SB_SPLICE) {
/*
* TCP has a sendbuffer that can handle multiple packets
* at once. So queue the stream a bit to accumulate data.
* The sosplice thread will call somove() later and send
* the packets calling tcp_output() only once.
* In the UDP case, send out the packets immediately.
* Using a thread would make things slower.
*/
if (so->so_proto->pr_flags & PR_WANTRCVD)
task_add(sosplice_taskq, &so->so_splicetask);
else
somove(so, M_DONTWAIT);
}
if (isspliced(so))
return;
#endif
sowakeup(so, &so->so_rcv);
if (so->so_upcall)
(*(so->so_upcall))(so, so->so_upcallarg, M_DONTWAIT);
}
void
sowwakeup(struct socket *so)
{
soassertlocked(so);
#ifdef SOCKET_SPLICE
if (so->so_snd.sb_flags & SB_SPLICE)
task_add(sosplice_taskq, &so->so_sp->ssp_soback->so_splicetask);
if (issplicedback(so))
return;
#endif
sowakeup(so, &so->so_snd);
}
int
sosetopt(struct socket *so, int level, int optname, struct mbuf *m)
{
int error = 0;
soassertlocked(so);
if (level != SOL_SOCKET) {
if (so->so_proto->pr_ctloutput) {
error = (*so->so_proto->pr_ctloutput)(PRCO_SETOPT, so,
level, optname, m);
return (error);
}
error = ENOPROTOOPT;
} else {
switch (optname) {
case SO_BINDANY:
if ((error = suser(curproc)) != 0) /* XXX */
return (error);
break;
}
switch (optname) {
case SO_LINGER:
if (m == NULL || m->m_len != sizeof (struct linger) ||
mtod(m, struct linger *)->l_linger < 0 ||
mtod(m, struct linger *)->l_linger > SHRT_MAX)
return (EINVAL);
so->so_linger = mtod(m, struct linger *)->l_linger;
/* FALLTHROUGH */
case SO_BINDANY:
case SO_DEBUG:
case SO_KEEPALIVE:
case SO_USELOOPBACK:
case SO_BROADCAST:
case SO_REUSEADDR:
case SO_REUSEPORT:
case SO_OOBINLINE:
case SO_TIMESTAMP:
case SO_ZEROIZE:
if (m == NULL || m->m_len < sizeof (int))
return (EINVAL);
if (*mtod(m, int *))
so->so_options |= optname;
else
so->so_options &= ~optname;
break;
case SO_DONTROUTE:
if (m == NULL || m->m_len < sizeof (int))
return (EINVAL);
if (*mtod(m, int *))
error = EOPNOTSUPP;
break;
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
{
u_long cnt;
if (m == NULL || m->m_len < sizeof (int))
return (EINVAL);
cnt = *mtod(m, int *);
if ((long)cnt <= 0)
cnt = 1;
switch (optname) {
case SO_SNDBUF:
if (so->so_state & SS_CANTSENDMORE)
return (EINVAL);
if (sbcheckreserve(cnt, so->so_snd.sb_wat) ||
sbreserve(so, &so->so_snd, cnt))
return (ENOBUFS);
so->so_snd.sb_wat = cnt;
break;
case SO_RCVBUF:
if (so->so_state & SS_CANTRCVMORE)
return (EINVAL);
if (sbcheckreserve(cnt, so->so_rcv.sb_wat) ||
sbreserve(so, &so->so_rcv, cnt))
return (ENOBUFS);
so->so_rcv.sb_wat = cnt;
break;
case SO_SNDLOWAT:
so->so_snd.sb_lowat =
(cnt > so->so_snd.sb_hiwat) ?
so->so_snd.sb_hiwat : cnt;
break;
case SO_RCVLOWAT:
so->so_rcv.sb_lowat =
(cnt > so->so_rcv.sb_hiwat) ?
so->so_rcv.sb_hiwat : cnt;
break;
}
break;
}
case SO_SNDTIMEO:
case SO_RCVTIMEO:
{
struct timeval tv;
uint64_t nsecs;
if (m == NULL || m->m_len < sizeof (tv))
return (EINVAL);
memcpy(&tv, mtod(m, struct timeval *), sizeof tv);
if (!timerisvalid(&tv))
return (EINVAL);
nsecs = TIMEVAL_TO_NSEC(&tv);
if (nsecs == UINT64_MAX)
return (EDOM);
if (nsecs == 0)
nsecs = INFSLP;
switch (optname) {
case SO_SNDTIMEO:
so->so_snd.sb_timeo_nsecs = nsecs;
break;
case SO_RCVTIMEO:
so->so_rcv.sb_timeo_nsecs = nsecs;
break;
}
break;
}
case SO_RTABLE:
if (so->so_proto->pr_domain &&
so->so_proto->pr_domain->dom_protosw &&
so->so_proto->pr_ctloutput) {
const struct domain *dom =
so->so_proto->pr_domain;
level = dom->dom_protosw->pr_protocol;
error = (*so->so_proto->pr_ctloutput)
(PRCO_SETOPT, so, level, optname, m);
return (error);
}
error = ENOPROTOOPT;
break;
#ifdef SOCKET_SPLICE
case SO_SPLICE:
if (m == NULL) {
error = sosplice(so, -1, 0, NULL);
} else if (m->m_len < sizeof(int)) {
return (EINVAL);
} else if (m->m_len < sizeof(struct splice)) {
error = sosplice(so, *mtod(m, int *), 0, NULL);
} else {
error = sosplice(so,
mtod(m, struct splice *)->sp_fd,
mtod(m, struct splice *)->sp_max,
&mtod(m, struct splice *)->sp_idle);
}
break;
#endif /* SOCKET_SPLICE */
default:
error = ENOPROTOOPT;
break;
}
if (error == 0 && so->so_proto->pr_ctloutput) {
(*so->so_proto->pr_ctloutput)(PRCO_SETOPT, so,
level, optname, m);
}
}
return (error);
}
int
sogetopt(struct socket *so, int level, int optname, struct mbuf *m)
{
int error = 0;
soassertlocked(so);
if (level != SOL_SOCKET) {
if (so->so_proto->pr_ctloutput) {
m->m_len = 0;
error = (*so->so_proto->pr_ctloutput)(PRCO_GETOPT, so,
level, optname, m);
if (error)
return (error);
return (0);
} else
return (ENOPROTOOPT);
} else {
m->m_len = sizeof (int);
switch (optname) {
case SO_LINGER:
m->m_len = sizeof (struct linger);
mtod(m, struct linger *)->l_onoff =
so->so_options & SO_LINGER;
mtod(m, struct linger *)->l_linger = so->so_linger;
break;
case SO_BINDANY:
case SO_USELOOPBACK:
case SO_DEBUG:
case SO_KEEPALIVE:
case SO_REUSEADDR:
case SO_REUSEPORT:
case SO_BROADCAST:
case SO_OOBINLINE:
case SO_TIMESTAMP:
case SO_ZEROIZE:
*mtod(m, int *) = so->so_options & optname;
break;
case SO_DONTROUTE:
*mtod(m, int *) = 0;
break;
case SO_TYPE:
*mtod(m, int *) = so->so_type;
break;
case SO_ERROR:
*mtod(m, int *) = so->so_error;
so->so_error = 0;
break;
case SO_DOMAIN:
*mtod(m, int *) = so->so_proto->pr_domain->dom_family;
break;
case SO_PROTOCOL:
*mtod(m, int *) = so->so_proto->pr_protocol;
break;
case SO_SNDBUF:
*mtod(m, int *) = so->so_snd.sb_hiwat;
break;
case SO_RCVBUF:
*mtod(m, int *) = so->so_rcv.sb_hiwat;
break;
case SO_SNDLOWAT:
*mtod(m, int *) = so->so_snd.sb_lowat;
break;
case SO_RCVLOWAT:
*mtod(m, int *) = so->so_rcv.sb_lowat;
break;
case SO_SNDTIMEO:
case SO_RCVTIMEO:
{
struct timeval tv;
uint64_t nsecs = (optname == SO_SNDTIMEO ?
so->so_snd.sb_timeo_nsecs :
so->so_rcv.sb_timeo_nsecs);
m->m_len = sizeof(struct timeval);
memset(&tv, 0, sizeof(tv));
if (nsecs != INFSLP)
NSEC_TO_TIMEVAL(nsecs, &tv);
memcpy(mtod(m, struct timeval *), &tv, sizeof tv);
break;
}
case SO_RTABLE:
if (so->so_proto->pr_domain &&
so->so_proto->pr_domain->dom_protosw &&
so->so_proto->pr_ctloutput) {
const struct domain *dom =
so->so_proto->pr_domain;
level = dom->dom_protosw->pr_protocol;
error = (*so->so_proto->pr_ctloutput)
(PRCO_GETOPT, so, level, optname, m);
if (error)
return (error);
break;
}
return (ENOPROTOOPT);
#ifdef SOCKET_SPLICE
case SO_SPLICE:
{
off_t len;
m->m_len = sizeof(off_t);
len = so->so_sp ? so->so_sp->ssp_len : 0;
memcpy(mtod(m, off_t *), &len, sizeof(off_t));
break;
}
#endif /* SOCKET_SPLICE */
case SO_PEERCRED:
if (so->so_proto->pr_protocol == AF_UNIX) {
struct unpcb *unp = sotounpcb(so);
if (unp->unp_flags & UNP_FEIDS) {
m->m_len = sizeof(unp->unp_connid);
memcpy(mtod(m, caddr_t),
&(unp->unp_connid), m->m_len);
break;
}
return (ENOTCONN);
}
return (EOPNOTSUPP);
default:
return (ENOPROTOOPT);
}
return (0);
}
}
void
sohasoutofband(struct socket *so)
{
pgsigio(&so->so_sigio, SIGURG, 0);
KNOTE(&so->so_rcv.sb_sel.si_note, 0);
}
int
soo_kqfilter(struct file *fp, struct knote *kn)
{
struct socket *so = kn->kn_fp->f_data;
struct sockbuf *sb;
solock(so);
switch (kn->kn_filter) {
case EVFILT_READ:
if (so->so_options & SO_ACCEPTCONN)
kn->kn_fop = &solisten_filtops;
else
kn->kn_fop = &soread_filtops;
sb = &so->so_rcv;
break;
case EVFILT_WRITE:
kn->kn_fop = &sowrite_filtops;
sb = &so->so_snd;
break;
case EVFILT_EXCEPT:
kn->kn_fop = &soexcept_filtops;
sb = &so->so_rcv;
break;
default:
sounlock(so);
return (EINVAL);
}
klist_insert_locked(&sb->sb_sel.si_note, kn);
sounlock(so);
return (0);
}
void
filt_sordetach(struct knote *kn)
{
struct socket *so = kn->kn_fp->f_data;
klist_remove(&so->so_rcv.sb_sel.si_note, kn);
}
int
filt_soread(struct knote *kn, long hint)
{
struct socket *so = kn->kn_fp->f_data;
int rv = 0;
soassertlocked(so);
kn->kn_data = so->so_rcv.sb_cc;
#ifdef SOCKET_SPLICE
if (isspliced(so)) {
rv = 0;
} else
#endif /* SOCKET_SPLICE */
if (so->so_state & SS_CANTRCVMORE) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL) {
if (so->so_state & SS_ISDISCONNECTED)
kn->kn_flags |= __EV_HUP;
}
kn->kn_fflags = so->so_error;
rv = 1;
} else if (so->so_error) { /* temporary udp error */
rv = 1;
} else if (kn->kn_sfflags & NOTE_LOWAT) {
rv = (kn->kn_data >= kn->kn_sdata);
} else {
rv = (kn->kn_data >= so->so_rcv.sb_lowat);
}
return rv;
}
void
filt_sowdetach(struct knote *kn)
{
struct socket *so = kn->kn_fp->f_data;
klist_remove(&so->so_snd.sb_sel.si_note, kn);
}
int
filt_sowrite(struct knote *kn, long hint)
{
struct socket *so = kn->kn_fp->f_data;
int rv;
soassertlocked(so);
kn->kn_data = sbspace(so, &so->so_snd);
if (so->so_state & SS_CANTSENDMORE) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL) {
if (so->so_state & SS_ISDISCONNECTED)
kn->kn_flags |= __EV_HUP;
}
kn->kn_fflags = so->so_error;
rv = 1;
} else if (so->so_error) { /* temporary udp error */
rv = 1;
} else if (((so->so_state & SS_ISCONNECTED) == 0) &&
(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
rv = 0;
} else if (kn->kn_sfflags & NOTE_LOWAT) {
rv = (kn->kn_data >= kn->kn_sdata);
} else {
rv = (kn->kn_data >= so->so_snd.sb_lowat);
}
return (rv);
}
int
filt_soexcept(struct knote *kn, long hint)
{
struct socket *so = kn->kn_fp->f_data;
int rv = 0;
soassertlocked(so);
#ifdef SOCKET_SPLICE
if (isspliced(so)) {
rv = 0;
} else
#endif /* SOCKET_SPLICE */
if (kn->kn_sfflags & NOTE_OOB) {
if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
kn->kn_fflags |= NOTE_OOB;
kn->kn_data -= so->so_oobmark;
rv = 1;
}
}
if (kn->kn_flags & __EV_POLL) {
if (so->so_state & SS_ISDISCONNECTED) {
kn->kn_flags |= __EV_HUP;
rv = 1;
}
}
return rv;
}
int
filt_solisten(struct knote *kn, long hint)
{
struct socket *so = kn->kn_fp->f_data;
int active;
soassertlocked(so);
kn->kn_data = so->so_qlen;
active = (kn->kn_data != 0);
if (kn->kn_flags & (__EV_POLL | __EV_SELECT)) {
if (so->so_state & SS_ISDISCONNECTED) {
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
active = soreadable(so);
}
}
return (active);
}
int
filt_somodify(struct kevent *kev, struct knote *kn)
{
struct socket *so = kn->kn_fp->f_data;
int rv;
solock(so);
rv = knote_modify(kev, kn);
sounlock(so);
return (rv);
}
int
filt_soprocess(struct knote *kn, struct kevent *kev)
{
struct socket *so = kn->kn_fp->f_data;
int rv;
solock(so);
rv = knote_process(kn, kev);
sounlock(so);
return (rv);
}
void
klist_soassertlk(void *arg)
{
struct socket *so = arg;
soassertlocked(so);
}
int
klist_solock(void *arg)
{
struct socket *so = arg;
solock(so);
return (1);
}
void
klist_sounlock(void *arg, int ls)
{
struct socket *so = arg;
sounlock(so);
}
const struct klistops socket_klistops = {
.klo_assertlk = klist_soassertlk,
.klo_lock = klist_solock,
.klo_unlock = klist_sounlock,
};
#ifdef DDB
void
sobuf_print(struct sockbuf *,
int (*)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))));
void
sobuf_print(struct sockbuf *sb,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
(*pr)("\tsb_cc: %lu\n", sb->sb_cc);
(*pr)("\tsb_datacc: %lu\n", sb->sb_datacc);
(*pr)("\tsb_hiwat: %lu\n", sb->sb_hiwat);
(*pr)("\tsb_wat: %lu\n", sb->sb_wat);
(*pr)("\tsb_mbcnt: %lu\n", sb->sb_mbcnt);
(*pr)("\tsb_mbmax: %lu\n", sb->sb_mbmax);
(*pr)("\tsb_lowat: %ld\n", sb->sb_lowat);
(*pr)("\tsb_mb: %p\n", sb->sb_mb);
(*pr)("\tsb_mbtail: %p\n", sb->sb_mbtail);
(*pr)("\tsb_lastrecord: %p\n", sb->sb_lastrecord);
(*pr)("\tsb_sel: ...\n");
(*pr)("\tsb_flags: %i\n", sb->sb_flags);
(*pr)("\tsb_timeo_nsecs: %llu\n", sb->sb_timeo_nsecs);
}
void
so_print(void *v,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct socket *so = v;
(*pr)("socket %p\n", so);
(*pr)("so_type: %i\n", so->so_type);
(*pr)("so_options: 0x%04x\n", so->so_options); /* %b */
(*pr)("so_linger: %i\n", so->so_linger);
(*pr)("so_state: 0x%04x\n", so->so_state);
(*pr)("so_pcb: %p\n", so->so_pcb);
(*pr)("so_proto: %p\n", so->so_proto);
(*pr)("so_sigio: %p\n", so->so_sigio.sir_sigio);
(*pr)("so_head: %p\n", so->so_head);
(*pr)("so_onq: %p\n", so->so_onq);
(*pr)("so_q0: @%p first: %p\n", &so->so_q0, TAILQ_FIRST(&so->so_q0));
(*pr)("so_q: @%p first: %p\n", &so->so_q, TAILQ_FIRST(&so->so_q));
(*pr)("so_eq: next: %p\n", TAILQ_NEXT(so, so_qe));
(*pr)("so_q0len: %i\n", so->so_q0len);
(*pr)("so_qlen: %i\n", so->so_qlen);
(*pr)("so_qlimit: %i\n", so->so_qlimit);
(*pr)("so_timeo: %i\n", so->so_timeo);
(*pr)("so_obmark: %lu\n", so->so_oobmark);
(*pr)("so_sp: %p\n", so->so_sp);
if (so->so_sp != NULL) {
(*pr)("\tssp_socket: %p\n", so->so_sp->ssp_socket);
(*pr)("\tssp_soback: %p\n", so->so_sp->ssp_soback);
(*pr)("\tssp_len: %lld\n",
(unsigned long long)so->so_sp->ssp_len);
(*pr)("\tssp_max: %lld\n",
(unsigned long long)so->so_sp->ssp_max);
(*pr)("\tssp_idletv: %lld %ld\n", so->so_sp->ssp_idletv.tv_sec,
so->so_sp->ssp_idletv.tv_usec);
(*pr)("\tssp_idleto: %spending (@%i)\n",
timeout_pending(&so->so_sp->ssp_idleto) ? "" : "not ",
so->so_sp->ssp_idleto.to_time);
}
(*pr)("so_rcv:\n");
sobuf_print(&so->so_rcv, pr);
(*pr)("so_snd:\n");
sobuf_print(&so->so_snd, pr);
(*pr)("so_upcall: %p so_upcallarg: %p\n",
so->so_upcall, so->so_upcallarg);
(*pr)("so_euid: %d so_ruid: %d\n", so->so_euid, so->so_ruid);
(*pr)("so_egid: %d so_rgid: %d\n", so->so_egid, so->so_rgid);
(*pr)("so_cpid: %d\n", so->so_cpid);
}
#endif
2
1
1
3
1
2
3
1
2
1
34
32
1
10
6
6
1
1
24
24
4
1
2
1
1
2
1
4
3
3
2
2
1
1
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
/* $OpenBSD: pci.c,v 1.125 2022/06/17 10:08:36 kettenis Exp $ */
/* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */
/*
* Copyright (c) 1995, 1996 Christopher G. Demetriou. All rights reserved.
* Copyright (c) 1994 Charles Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* PCI bus autoconfiguration.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/ppbreg.h>
int pcimatch(struct device *, void *, void *);
void pciattach(struct device *, struct device *, void *);
int pcidetach(struct device *, int);
int pciactivate(struct device *, int);
void pci_suspend(struct pci_softc *);
void pci_powerdown(struct pci_softc *);
void pci_resume(struct pci_softc *);
struct msix_vector {
uint32_t mv_ma;
uint32_t mv_mau32;
uint32_t mv_md;
uint32_t mv_vc;
};
#define NMAPREG ((PCI_MAPREG_END - PCI_MAPREG_START) / \
sizeof(pcireg_t))
struct pci_dev {
struct device *pd_dev;
LIST_ENTRY(pci_dev) pd_next;
pcitag_t pd_tag; /* pci register tag */
pcireg_t pd_csr;
pcireg_t pd_bhlc;
pcireg_t pd_int;
pcireg_t pd_map[NMAPREG];
pcireg_t pd_mask[NMAPREG];
pcireg_t pd_msi_mc;
pcireg_t pd_msi_ma;
pcireg_t pd_msi_mau32;
pcireg_t pd_msi_md;
pcireg_t pd_msix_mc;
struct msix_vector *pd_msix_table;
int pd_pmcsr_state;
int pd_vga_decode;
};
#ifdef APERTURE
extern int allowaperture;
#endif
const struct cfattach pci_ca = {
sizeof(struct pci_softc), pcimatch, pciattach, pcidetach, pciactivate
};
struct cfdriver pci_cd = {
NULL, "pci", DV_DULL
};
int pci_ndomains;
struct proc *pci_vga_proc;
struct pci_softc *pci_vga_pci;
pcitag_t pci_vga_tag;
int pci_dopm;
int pciprint(void *, const char *);
int pcisubmatch(struct device *, void *, void *);
#ifdef PCI_MACHDEP_ENUMERATE_BUS
#define pci_enumerate_bus PCI_MACHDEP_ENUMERATE_BUS
#else
int pci_enumerate_bus(struct pci_softc *,
int (*)(struct pci_attach_args *), struct pci_attach_args *);
#endif
int pci_reserve_resources(struct pci_attach_args *);
int pci_primary_vga(struct pci_attach_args *);
/*
* Important note about PCI-ISA bridges:
*
* Callbacks are used to configure these devices so that ISA/EISA bridges
* can attach their child busses after PCI configuration is done.
*
* This works because:
* (1) there can be at most one ISA/EISA bridge per PCI bus, and
* (2) any ISA/EISA bridges must be attached to primary PCI
* busses (i.e. bus zero).
*
* That boils down to: there can only be one of these outstanding
* at a time, it is cleared when configuring PCI bus 0 before any
* subdevices have been found, and it is run after all subdevices
* of PCI bus 0 have been found.
*
* This is needed because there are some (legacy) PCI devices which
* can show up as ISA/EISA devices as well (the prime example of which
* are VGA controllers). If you attach ISA from a PCI-ISA/EISA bridge,
* and the bridge is seen before the video board is, the board can show
* up as an ISA device, and that can (bogusly) complicate the PCI device's
* attach code, or make the PCI device not be properly attached at all.
*
* We use the generic config_defer() facility to achieve this.
*/
int
pcimatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct pcibus_attach_args *pba = aux;
if (strcmp(pba->pba_busname, cf->cf_driver->cd_name))
return (0);
/* Check the locators */
if (cf->pcibuscf_bus != PCIBUS_UNK_BUS &&
cf->pcibuscf_bus != pba->pba_bus)
return (0);
/* sanity */
if (pba->pba_bus < 0 || pba->pba_bus > 255)
return (0);
/*
* XXX check other (hardware?) indicators
*/
return (1);
}
void
pciattach(struct device *parent, struct device *self, void *aux)
{
struct pcibus_attach_args *pba = aux;
struct pci_softc *sc = (struct pci_softc *)self;
pci_attach_hook(parent, self, pba);
printf("\n");
LIST_INIT(&sc->sc_devs);
sc->sc_iot = pba->pba_iot;
sc->sc_memt = pba->pba_memt;
sc->sc_dmat = pba->pba_dmat;
sc->sc_pc = pba->pba_pc;
sc->sc_flags = pba->pba_flags;
sc->sc_ioex = pba->pba_ioex;
sc->sc_memex = pba->pba_memex;
sc->sc_pmemex = pba->pba_pmemex;
sc->sc_busex = pba->pba_busex;
sc->sc_domain = pba->pba_domain;
sc->sc_bus = pba->pba_bus;
sc->sc_bridgetag = pba->pba_bridgetag;
sc->sc_bridgeih = pba->pba_bridgeih;
sc->sc_maxndevs = pci_bus_maxdevs(pba->pba_pc, pba->pba_bus);
sc->sc_intrswiz = pba->pba_intrswiz;
sc->sc_intrtag = pba->pba_intrtag;
/* Reserve our own bus number. */
if (sc->sc_busex)
extent_alloc_region(sc->sc_busex, sc->sc_bus, 1, EX_NOWAIT);
pci_enumerate_bus(sc, pci_reserve_resources, NULL);
/* Find the VGA device that's currently active. */
if (pci_enumerate_bus(sc, pci_primary_vga, NULL))
pci_vga_pci = sc;
pci_enumerate_bus(sc, NULL, NULL);
}
int
pcidetach(struct device *self, int flags)
{
return pci_detach_devices((struct pci_softc *)self, flags);
}
int
pciactivate(struct device *self, int act)
{
int rv = 0;
switch (act) {
case DVACT_SUSPEND:
rv = config_activate_children(self, act);
pci_suspend((struct pci_softc *)self);
break;
case DVACT_RESUME:
pci_resume((struct pci_softc *)self);
rv = config_activate_children(self, act);
break;
case DVACT_POWERDOWN:
rv = config_activate_children(self, act);
pci_powerdown((struct pci_softc *)self);
break;
default:
rv = config_activate_children(self, act);
break;
}
return (rv);
}
void
pci_suspend(struct pci_softc *sc)
{
struct pci_dev *pd;
pcireg_t bhlc, reg;
int off, i;
LIST_FOREACH(pd, &sc->sc_devs, pd_next) {
/*
* Only handle header type 0 here; PCI-PCI bridges and
* CardBus bridges need special handling, which will
* be done in their specific drivers.
*/
bhlc = pci_conf_read(sc->sc_pc, pd->pd_tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlc) != 0)
continue;
/* Save registers that may get lost. */
for (i = 0; i < NMAPREG; i++)
pd->pd_map[i] = pci_conf_read(sc->sc_pc, pd->pd_tag,
PCI_MAPREG_START + (i * 4));
pd->pd_csr = pci_conf_read(sc->sc_pc, pd->pd_tag,
PCI_COMMAND_STATUS_REG);
pd->pd_bhlc = pci_conf_read(sc->sc_pc, pd->pd_tag,
PCI_BHLC_REG);
pd->pd_int = pci_conf_read(sc->sc_pc, pd->pd_tag,
PCI_INTERRUPT_REG);
if (pci_get_capability(sc->sc_pc, pd->pd_tag,
PCI_CAP_MSI, &off, ®)) {
pd->pd_msi_ma = pci_conf_read(sc->sc_pc, pd->pd_tag,
off + PCI_MSI_MA);
if (reg & PCI_MSI_MC_C64) {
pd->pd_msi_mau32 = pci_conf_read(sc->sc_pc,
pd->pd_tag, off + PCI_MSI_MAU32);
pd->pd_msi_md = pci_conf_read(sc->sc_pc,
pd->pd_tag, off + PCI_MSI_MD64);
} else {
pd->pd_msi_md = pci_conf_read(sc->sc_pc,
pd->pd_tag, off + PCI_MSI_MD32);
}
pd->pd_msi_mc = reg;
}
pci_suspend_msix(sc->sc_pc, pd->pd_tag, sc->sc_memt,
&pd->pd_msix_mc, pd->pd_msix_table);
}
}
void
pci_powerdown(struct pci_softc *sc)
{
struct pci_dev *pd;
pcireg_t bhlc;
LIST_FOREACH(pd, &sc->sc_devs, pd_next) {
/*
* Only handle header type 0 here; PCI-PCI bridges and
* CardBus bridges need special handling, which will
* be done in their specific drivers.
*/
bhlc = pci_conf_read(sc->sc_pc, pd->pd_tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlc) != 0)
continue;
if (pci_dopm) {
/*
* Place the device into the lowest possible
* power state.
*/
pd->pd_pmcsr_state = pci_get_powerstate(sc->sc_pc,
pd->pd_tag);
pci_set_powerstate(sc->sc_pc, pd->pd_tag,
pci_min_powerstate(sc->sc_pc, pd->pd_tag));
}
}
}
void
pci_resume(struct pci_softc *sc)
{
struct pci_dev *pd;
pcireg_t bhlc, reg;
int off, i;
LIST_FOREACH(pd, &sc->sc_devs, pd_next) {
/*
* Only handle header type 0 here; PCI-PCI bridges and
* CardBus bridges need special handling, which will
* be done in their specific drivers.
*/
bhlc = pci_conf_read(sc->sc_pc, pd->pd_tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlc) != 0)
continue;
/* Restore power. */
if (pci_dopm)
pci_set_powerstate(sc->sc_pc, pd->pd_tag,
pd->pd_pmcsr_state);
/* Restore the registers saved above. */
for (i = 0; i < NMAPREG; i++)
pci_conf_write(sc->sc_pc, pd->pd_tag,
PCI_MAPREG_START + (i * 4), pd->pd_map[i]);
reg = pci_conf_read(sc->sc_pc, pd->pd_tag,
PCI_COMMAND_STATUS_REG);
pci_conf_write(sc->sc_pc, pd->pd_tag, PCI_COMMAND_STATUS_REG,
(reg & 0xffff0000) | (pd->pd_csr & 0x0000ffff));
pci_conf_write(sc->sc_pc, pd->pd_tag, PCI_BHLC_REG,
pd->pd_bhlc);
pci_conf_write(sc->sc_pc, pd->pd_tag, PCI_INTERRUPT_REG,
pd->pd_int);
if (pci_get_capability(sc->sc_pc, pd->pd_tag,
PCI_CAP_MSI, &off, ®)) {
pci_conf_write(sc->sc_pc, pd->pd_tag,
off + PCI_MSI_MA, pd->pd_msi_ma);
if (reg & PCI_MSI_MC_C64) {
pci_conf_write(sc->sc_pc, pd->pd_tag,
off + PCI_MSI_MAU32, pd->pd_msi_mau32);
pci_conf_write(sc->sc_pc, pd->pd_tag,
off + PCI_MSI_MD64, pd->pd_msi_md);
} else {
pci_conf_write(sc->sc_pc, pd->pd_tag,
off + PCI_MSI_MD32, pd->pd_msi_md);
}
pci_conf_write(sc->sc_pc, pd->pd_tag,
off + PCI_MSI_MC, pd->pd_msi_mc);
}
pci_resume_msix(sc->sc_pc, pd->pd_tag, sc->sc_memt,
pd->pd_msix_mc, pd->pd_msix_table);
}
}
int
pciprint(void *aux, const char *pnp)
{
struct pci_attach_args *pa = aux;
char devinfo[256];
if (pnp) {
pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo,
sizeof devinfo);
printf("%s at %s", devinfo, pnp);
}
printf(" dev %d function %d", pa->pa_device, pa->pa_function);
if (!pnp) {
pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo,
sizeof devinfo);
printf(" %s", devinfo);
}
return (UNCONF);
}
int
pcisubmatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct pci_attach_args *pa = aux;
if (cf->pcicf_dev != PCI_UNK_DEV &&
cf->pcicf_dev != pa->pa_device)
return (0);
if (cf->pcicf_function != PCI_UNK_FUNCTION &&
cf->pcicf_function != pa->pa_function)
return (0);
return ((*cf->cf_attach->ca_match)(parent, match, aux));
}
int
pci_probe_device(struct pci_softc *sc, pcitag_t tag,
int (*match)(struct pci_attach_args *), struct pci_attach_args *pap)
{
pci_chipset_tag_t pc = sc->sc_pc;
struct pci_attach_args pa;
struct pci_dev *pd;
pcireg_t id, class, intr, bhlcr, cap;
int pin, bus, device, function;
int off, ret = 0;
uint64_t addr;
pci_decompose_tag(pc, tag, &bus, &device, &function);
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
return (0);
id = pci_conf_read(pc, tag, PCI_ID_REG);
class = pci_conf_read(pc, tag, PCI_CLASS_REG);
/* Invalid vendor ID value? */
if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
return (0);
/* XXX Not invalid, but we've done this ~forever. */
if (PCI_VENDOR(id) == 0)
return (0);
pa.pa_iot = sc->sc_iot;
pa.pa_memt = sc->sc_memt;
pa.pa_dmat = sc->sc_dmat;
pa.pa_pc = pc;
pa.pa_ioex = sc->sc_ioex;
pa.pa_memex = sc->sc_memex;
pa.pa_pmemex = sc->sc_pmemex;
pa.pa_busex = sc->sc_busex;
pa.pa_domain = sc->sc_domain;
pa.pa_bus = bus;
pa.pa_device = device;
pa.pa_function = function;
pa.pa_tag = tag;
pa.pa_id = id;
pa.pa_class = class;
pa.pa_bridgetag = sc->sc_bridgetag;
pa.pa_bridgeih = sc->sc_bridgeih;
/* This is a simplification of the NetBSD code.
We don't support turning off I/O or memory
on broken hardware. <csapuntz@stanford.edu> */
pa.pa_flags = sc->sc_flags;
pa.pa_flags |= PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED;
if (sc->sc_bridgetag == NULL) {
pa.pa_intrswiz = 0;
pa.pa_intrtag = tag;
} else {
pa.pa_intrswiz = sc->sc_intrswiz + device;
pa.pa_intrtag = sc->sc_intrtag;
}
intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
pin = PCI_INTERRUPT_PIN(intr);
pa.pa_rawintrpin = pin;
if (pin == PCI_INTERRUPT_PIN_NONE) {
/* no interrupt */
pa.pa_intrpin = 0;
} else {
/*
* swizzle it based on the number of busses we're
* behind and our device number.
*/
pa.pa_intrpin = /* XXX */
((pin + pa.pa_intrswiz - 1) % 4) + 1;
}
pa.pa_intrline = PCI_INTERRUPT_LINE(intr);
if (pci_get_ht_capability(pc, tag, PCI_HT_CAP_MSI, &off, &cap)) {
/*
* XXX Should we enable MSI mapping ourselves on
* systems that have it disabled?
*/
if (cap & PCI_HT_MSI_ENABLED) {
if ((cap & PCI_HT_MSI_FIXED) == 0) {
addr = pci_conf_read(pc, tag,
off + PCI_HT_MSI_ADDR);
addr |= (uint64_t)pci_conf_read(pc, tag,
off + PCI_HT_MSI_ADDR_HI32) << 32;
} else
addr = PCI_HT_MSI_FIXED_ADDR;
/*
* XXX This will fail to enable MSI on systems
* that don't use the canonical address.
*/
if (addr == PCI_HT_MSI_FIXED_ADDR)
pa.pa_flags |= PCI_FLAGS_MSI_ENABLED;
}
}
/*
* Give the MD code a chance to alter pci_attach_args and/or
* skip devices.
*/
if (pci_probe_device_hook(pc, &pa) != 0)
return (0);
if (match != NULL) {
ret = (*match)(&pa);
if (ret != 0 && pap != NULL)
*pap = pa;
} else {
pcireg_t address, csr;
int i, reg, reg_start, reg_end;
int s;
pd = malloc(sizeof *pd, M_DEVBUF, M_ZERO | M_WAITOK);
pd->pd_tag = tag;
LIST_INSERT_HEAD(&sc->sc_devs, pd, pd_next);
switch (PCI_HDRTYPE_TYPE(bhlcr)) {
case 0:
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_END;
break;
case 1: /* PCI-PCI bridge */
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_PPB_END;
break;
case 2: /* PCI-CardBus bridge */
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_PCB_END;
break;
default:
return (0);
}
pd->pd_msix_table = pci_alloc_msix_table(sc->sc_pc, pd->pd_tag);
s = splhigh();
csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr &
~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE));
for (reg = reg_start, i = 0; reg < reg_end; reg += 4, i++) {
address = pci_conf_read(pc, tag, reg);
pci_conf_write(pc, tag, reg, 0xffffffff);
pd->pd_mask[i] = pci_conf_read(pc, tag, reg);
pci_conf_write(pc, tag, reg, address);
}
if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
splx(s);
if ((PCI_CLASS(class) == PCI_CLASS_DISPLAY &&
PCI_SUBCLASS(class) == PCI_SUBCLASS_DISPLAY_VGA) ||
(PCI_CLASS(class) == PCI_CLASS_PREHISTORIC &&
PCI_SUBCLASS(class) == PCI_SUBCLASS_PREHISTORIC_VGA))
pd->pd_vga_decode = 1;
pd->pd_dev = config_found_sm(&sc->sc_dev, &pa, pciprint,
pcisubmatch);
if (pd->pd_dev)
pci_dev_postattach(pd->pd_dev, &pa);
}
return (ret);
}
int
pci_detach_devices(struct pci_softc *sc, int flags)
{
struct pci_dev *pd, *next;
int ret;
ret = config_detach_children(&sc->sc_dev, flags);
if (ret != 0)
return (ret);
for (pd = LIST_FIRST(&sc->sc_devs); pd != NULL; pd = next) {
pci_free_msix_table(sc->sc_pc, pd->pd_tag, pd->pd_msix_table);
next = LIST_NEXT(pd, pd_next);
free(pd, M_DEVBUF, sizeof *pd);
}
LIST_INIT(&sc->sc_devs);
return (0);
}
int
pci_get_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
int *offset, pcireg_t *value)
{
pcireg_t reg;
unsigned int ofs;
reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
if (!(reg & PCI_STATUS_CAPLIST_SUPPORT))
return (0);
/* Determine the Capability List Pointer register to start with. */
reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
switch (PCI_HDRTYPE_TYPE(reg)) {
case 0: /* standard device header */
case 1: /* PCI-PCI bridge header */
ofs = PCI_CAPLISTPTR_REG;
break;
case 2: /* PCI-CardBus bridge header */
ofs = PCI_CARDBUS_CAPLISTPTR_REG;
break;
default:
return (0);
}
ofs = PCI_CAPLIST_PTR(pci_conf_read(pc, tag, ofs));
while (ofs != 0) {
/*
* Some devices, like parts of the NVIDIA C51 chipset,
* have a broken Capabilities List. So we need to do
* a sanity check here.
*/
if ((ofs & 3) || (ofs < 0x40))
return (0);
reg = pci_conf_read(pc, tag, ofs);
if (PCI_CAPLIST_CAP(reg) == capid) {
if (offset)
*offset = ofs;
if (value)
*value = reg;
return (1);
}
ofs = PCI_CAPLIST_NEXT(reg);
}
return (0);
}
int
pci_get_ht_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
int *offset, pcireg_t *value)
{
pcireg_t reg;
unsigned int ofs;
if (pci_get_capability(pc, tag, PCI_CAP_HT, &ofs, NULL) == 0)
return (0);
while (ofs != 0) {
#ifdef DIAGNOSTIC
if ((ofs & 3) || (ofs < 0x40))
panic("pci_get_ht_capability");
#endif
reg = pci_conf_read(pc, tag, ofs);
if (PCI_HT_CAP(reg) == capid) {
if (offset)
*offset = ofs;
if (value)
*value = reg;
return (1);
}
ofs = PCI_CAPLIST_NEXT(reg);
}
return (0);
}
int
pci_get_ext_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
int *offset, pcireg_t *value)
{
pcireg_t reg;
unsigned int ofs;
/* Make sure this is a PCI Express device. */
if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, NULL, NULL) == 0)
return (0);
/* Scan PCI Express extended capabilities. */
ofs = PCI_PCIE_ECAP;
while (ofs != 0) {
#ifdef DIAGNOSTIC
if ((ofs & 3) || (ofs < PCI_PCIE_ECAP))
panic("pci_get_ext_capability");
#endif
reg = pci_conf_read(pc, tag, ofs);
if (PCI_PCIE_ECAP_ID(reg) == capid) {
if (offset)
*offset = ofs;
if (value)
*value = reg;
return (1);
}
ofs = PCI_PCIE_ECAP_NEXT(reg);
}
return (0);
}
uint16_t
pci_requester_id(pci_chipset_tag_t pc, pcitag_t tag)
{
int bus, dev, func;
pci_decompose_tag(pc, tag, &bus, &dev, &func);
return ((bus << 8) | (dev << 3) | func);
}
int
pci_find_device(struct pci_attach_args *pa,
int (*match)(struct pci_attach_args *))
{
extern struct cfdriver pci_cd;
struct device *pcidev;
int i;
for (i = 0; i < pci_cd.cd_ndevs; i++) {
pcidev = pci_cd.cd_devs[i];
if (pcidev != NULL &&
pci_enumerate_bus((struct pci_softc *)pcidev,
match, pa) != 0)
return (1);
}
return (0);
}
int
pci_get_powerstate(pci_chipset_tag_t pc, pcitag_t tag)
{
pcireg_t reg;
int offset;
if (pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, 0)) {
reg = pci_conf_read(pc, tag, offset + PCI_PMCSR);
return (reg & PCI_PMCSR_STATE_MASK);
}
return (PCI_PMCSR_STATE_D0);
}
int
pci_set_powerstate(pci_chipset_tag_t pc, pcitag_t tag, int state)
{
pcireg_t reg;
int offset, ostate = state;
/*
* Warn the firmware that we are going to put the device
* into the given state.
*/
pci_set_powerstate_md(pc, tag, state, 1);
if (pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, 0)) {
if (state == PCI_PMCSR_STATE_D3) {
/*
* The PCI Power Management spec says we
* should disable I/O and memory space as well
* as bus mastering before we place the device
* into D3.
*/
reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
reg &= ~PCI_COMMAND_IO_ENABLE;
reg &= ~PCI_COMMAND_MEM_ENABLE;
reg &= ~PCI_COMMAND_MASTER_ENABLE;
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, reg);
}
reg = pci_conf_read(pc, tag, offset + PCI_PMCSR);
if ((reg & PCI_PMCSR_STATE_MASK) != state) {
ostate = reg & PCI_PMCSR_STATE_MASK;
pci_conf_write(pc, tag, offset + PCI_PMCSR,
(reg & ~PCI_PMCSR_STATE_MASK) | state);
if (state == PCI_PMCSR_STATE_D3 ||
ostate == PCI_PMCSR_STATE_D3)
delay(10 * 1000);
}
}
/*
* Warn the firmware that the device is now in the given
* state.
*/
pci_set_powerstate_md(pc, tag, state, 0);
return (ostate);
}
#ifndef PCI_MACHDEP_ENUMERATE_BUS
/*
* Generic PCI bus enumeration routine. Used unless machine-dependent
* code needs to provide something else.
*/
int
pci_enumerate_bus(struct pci_softc *sc,
int (*match)(struct pci_attach_args *), struct pci_attach_args *pap)
{
pci_chipset_tag_t pc = sc->sc_pc;
int device, function, nfunctions, ret;
int maxndevs = sc->sc_maxndevs;
const struct pci_quirkdata *qd;
pcireg_t id, bhlcr, cap;
pcitag_t tag;
/*
* PCIe downstream ports and root ports should only forward
* configuration requests for device number 0. However, not
* all hardware implements this correctly, and some devices
* will respond to other device numbers making the device show
* up 32 times. Prevent this by only scanning a single
* device.
*/
if (sc->sc_bridgetag && pci_get_capability(pc, *sc->sc_bridgetag,
PCI_CAP_PCIEXPRESS, NULL, &cap)) {
switch (PCI_PCIE_XCAP_TYPE(cap)) {
case PCI_PCIE_XCAP_TYPE_RP:
case PCI_PCIE_XCAP_TYPE_DOWN:
case PCI_PCIE_XCAP_TYPE_PCI2PCIE:
maxndevs = 1;
break;
}
}
for (device = 0; device < maxndevs; device++) {
tag = pci_make_tag(pc, sc->sc_bus, device, 0);
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
continue;
id = pci_conf_read(pc, tag, PCI_ID_REG);
/* Invalid vendor ID value? */
if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
continue;
/* XXX Not invalid, but we've done this ~forever. */
if (PCI_VENDOR(id) == 0)
continue;
qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id));
if (qd != NULL &&
(qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0)
nfunctions = 8;
else if (qd != NULL &&
(qd->quirks & PCI_QUIRK_MONOFUNCTION) != 0)
nfunctions = 1;
else
nfunctions = PCI_HDRTYPE_MULTIFN(bhlcr) ? 8 : 1;
for (function = 0; function < nfunctions; function++) {
tag = pci_make_tag(pc, sc->sc_bus, device, function);
ret = pci_probe_device(sc, tag, match, pap);
if (match != NULL && ret != 0)
return (ret);
}
}
return (0);
}
#endif /* PCI_MACHDEP_ENUMERATE_BUS */
int
pci_reserve_resources(struct pci_attach_args *pa)
{
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t tag = pa->pa_tag;
pcireg_t bhlc, blr, type, bir;
pcireg_t addr, mask;
bus_addr_t base, limit;
bus_size_t size;
int reg, reg_start, reg_end, reg_rom;
int bus, dev, func;
int sec, sub;
int flags;
int s;
pci_decompose_tag(pc, tag, &bus, &dev, &func);
bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
switch (PCI_HDRTYPE_TYPE(bhlc)) {
case 0:
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_END;
reg_rom = PCI_ROM_REG;
break;
case 1: /* PCI-PCI bridge */
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_PPB_END;
reg_rom = 0; /* 0x38 */
break;
case 2: /* PCI-CardBus bridge */
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_PCB_END;
reg_rom = 0;
break;
default:
return (0);
}
for (reg = reg_start; reg < reg_end; reg += 4) {
if (!pci_mapreg_probe(pc, tag, reg, &type))
continue;
if (pci_mapreg_info(pc, tag, reg, type, &base, &size, &flags))
continue;
if (base == 0)
continue;
switch (type) {
case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
if (ISSET(flags, BUS_SPACE_MAP_PREFETCHABLE) &&
pa->pa_pmemex && extent_alloc_region(pa->pa_pmemex,
base, size, EX_NOWAIT) == 0) {
break;
}
#ifdef __sparc64__
/*
* Certain SPARC T5 systems assign
* non-prefetchable 64-bit BARs of its onboard
* mpii(4) controllers addresses in the
* prefetchable memory range. This is
* (probably) safe, as reads from the device
* registers mapped by these BARs are
* side-effect free. So assume the firmware
* knows what it is doing.
*/
if (base >= 0x100000000 &&
pa->pa_pmemex && extent_alloc_region(pa->pa_pmemex,
base, size, EX_NOWAIT) == 0) {
break;
}
#endif
if (pa->pa_memex && extent_alloc_region(pa->pa_memex,
base, size, EX_NOWAIT)) {
printf("%d:%d:%d: mem address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
pci_conf_write(pc, tag, reg, 0);
if (type & PCI_MAPREG_MEM_TYPE_64BIT)
pci_conf_write(pc, tag, reg + 4, 0);
}
break;
case PCI_MAPREG_TYPE_IO:
if (pa->pa_ioex && extent_alloc_region(pa->pa_ioex,
base, size, EX_NOWAIT)) {
printf("%d:%d:%d: io address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
pci_conf_write(pc, tag, reg, 0);
}
break;
}
if (type & PCI_MAPREG_MEM_TYPE_64BIT)
reg += 4;
}
if (reg_rom != 0) {
s = splhigh();
addr = pci_conf_read(pc, tag, PCI_ROM_REG);
pci_conf_write(pc, tag, PCI_ROM_REG, ~PCI_ROM_ENABLE);
mask = pci_conf_read(pc, tag, PCI_ROM_REG);
pci_conf_write(pc, tag, PCI_ROM_REG, addr);
splx(s);
base = PCI_ROM_ADDR(addr);
size = PCI_ROM_SIZE(mask);
if (base != 0 && size != 0) {
if (pa->pa_pmemex && extent_alloc_region(pa->pa_pmemex,
base, size, EX_NOWAIT) &&
pa->pa_memex && extent_alloc_region(pa->pa_memex,
base, size, EX_NOWAIT)) {
printf("%d:%d:%d: rom address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
pci_conf_write(pc, tag, PCI_ROM_REG, 0);
}
}
}
if (PCI_HDRTYPE_TYPE(bhlc) != 1)
return (0);
/* Figure out the I/O address range of the bridge. */
blr = pci_conf_read(pc, tag, PPB_REG_IOSTATUS);
base = (blr & 0x000000f0) << 8;
limit = (blr & 0x000f000) | 0x00000fff;
blr = pci_conf_read(pc, tag, PPB_REG_IO_HI);
base |= (blr & 0x0000ffff) << 16;
limit |= (blr & 0xffff0000);
if (limit > base)
size = (limit - base + 1);
else
size = 0;
if (pa->pa_ioex && base > 0 && size > 0) {
if (extent_alloc_region(pa->pa_ioex, base, size, EX_NOWAIT)) {
printf("%d:%d:%d: bridge io address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
blr &= 0xffff0000;
blr |= 0x000000f0;
pci_conf_write(pc, tag, PPB_REG_IOSTATUS, blr);
}
}
/* Figure out the memory mapped I/O address range of the bridge. */
blr = pci_conf_read(pc, tag, PPB_REG_MEM);
base = (blr & 0x0000fff0) << 16;
limit = (blr & 0xfff00000) | 0x000fffff;
if (limit > base)
size = (limit - base + 1);
else
size = 0;
if (pa->pa_memex && base > 0 && size > 0) {
if (extent_alloc_region(pa->pa_memex, base, size, EX_NOWAIT)) {
printf("%d:%d:%d: bridge mem address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
pci_conf_write(pc, tag, PPB_REG_MEM, 0x0000fff0);
}
}
/* Figure out the prefetchable memory address range of the bridge. */
blr = pci_conf_read(pc, tag, PPB_REG_PREFMEM);
base = (blr & 0x0000fff0) << 16;
limit = (blr & 0xfff00000) | 0x000fffff;
#ifdef __LP64__
blr = pci_conf_read(pc, pa->pa_tag, PPB_REG_PREFBASE_HI32);
base |= ((uint64_t)blr) << 32;
blr = pci_conf_read(pc, pa->pa_tag, PPB_REG_PREFLIM_HI32);
limit |= ((uint64_t)blr) << 32;
#endif
if (limit > base)
size = (limit - base + 1);
else
size = 0;
if (pa->pa_pmemex && base > 0 && size > 0) {
if (extent_alloc_region(pa->pa_pmemex, base, size, EX_NOWAIT)) {
printf("%d:%d:%d: bridge mem address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
pci_conf_write(pc, tag, PPB_REG_PREFMEM, 0x0000fff0);
}
} else if (pa->pa_memex && base > 0 && size > 0) {
if (extent_alloc_region(pa->pa_memex, base, size, EX_NOWAIT)) {
printf("%d:%d:%d: bridge mem address conflict 0x%lx/0x%lx\n",
bus, dev, func, base, size);
pci_conf_write(pc, tag, PPB_REG_PREFMEM, 0x0000fff0);
}
}
/* Figure out the bus range handled by the bridge. */
bir = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
sec = PPB_BUSINFO_SECONDARY(bir);
sub = PPB_BUSINFO_SUBORDINATE(bir);
if (pa->pa_busex && sub >= sec && sub > 0) {
if (extent_alloc_region(pa->pa_busex, sec, sub - sec + 1,
EX_NOWAIT)) {
printf("%d:%d:%d: bridge bus conflict %d-%d\n",
bus, dev, func, sec, sub);
}
}
return (0);
}
/*
* Vital Product Data (PCI 2.2)
*/
int
pci_vpd_read(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count,
pcireg_t *data)
{
uint32_t reg;
int ofs, i, j;
KASSERT(data != NULL);
if ((offset + count) >= PCI_VPD_ADDRESS_MASK)
return (EINVAL);
if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, ®) == 0)
return (ENXIO);
for (i = 0; i < count; offset += sizeof(*data), i++) {
reg &= 0x0000ffff;
reg &= ~PCI_VPD_OPFLAG;
reg |= PCI_VPD_ADDRESS(offset);
pci_conf_write(pc, tag, ofs, reg);
/*
* PCI 2.2 does not specify how long we should poll
* for completion nor whether the operation can fail.
*/
j = 0;
do {
if (j++ == 20)
return (EIO);
delay(4);
reg = pci_conf_read(pc, tag, ofs);
} while ((reg & PCI_VPD_OPFLAG) == 0);
data[i] = pci_conf_read(pc, tag, PCI_VPD_DATAREG(ofs));
}
return (0);
}
int
pci_vpd_write(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count,
pcireg_t *data)
{
pcireg_t reg;
int ofs, i, j;
KASSERT(data != NULL);
KASSERT((offset + count) < 0x7fff);
if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, ®) == 0)
return (1);
for (i = 0; i < count; offset += sizeof(*data), i++) {
pci_conf_write(pc, tag, PCI_VPD_DATAREG(ofs), data[i]);
reg &= 0x0000ffff;
reg |= PCI_VPD_OPFLAG;
reg |= PCI_VPD_ADDRESS(offset);
pci_conf_write(pc, tag, ofs, reg);
/*
* PCI 2.2 does not specify how long we should poll
* for completion nor whether the operation can fail.
*/
j = 0;
do {
if (j++ == 20)
return (1);
delay(1);
reg = pci_conf_read(pc, tag, ofs);
} while (reg & PCI_VPD_OPFLAG);
}
return (0);
}
int
pci_matchbyid(struct pci_attach_args *pa, const struct pci_matchid *ids,
int nent)
{
const struct pci_matchid *pm;
int i;
for (i = 0, pm = ids; i < nent; i++, pm++)
if (PCI_VENDOR(pa->pa_id) == pm->pm_vid &&
PCI_PRODUCT(pa->pa_id) == pm->pm_pid)
return (1);
return (0);
}
void
pci_disable_legacy_vga(struct device *dev)
{
struct pci_softc *pci;
struct pci_dev *pd;
/* XXX Until we attach the drm drivers directly to pci. */
while (dev->dv_parent->dv_cfdata->cf_driver != &pci_cd)
dev = dev->dv_parent;
pci = (struct pci_softc *)dev->dv_parent;
LIST_FOREACH(pd, &pci->sc_devs, pd_next) {
if (pd->pd_dev == dev) {
pd->pd_vga_decode = 0;
break;
}
}
}
#ifdef USER_PCICONF
/*
* This is the user interface to PCI configuration space.
*/
#include <sys/pciio.h>
#include <sys/fcntl.h>
#ifdef DEBUG
#define PCIDEBUG(x) printf x
#else
#define PCIDEBUG(x)
#endif
void pci_disable_vga(pci_chipset_tag_t, pcitag_t);
void pci_enable_vga(pci_chipset_tag_t, pcitag_t);
void pci_route_vga(struct pci_softc *);
void pci_unroute_vga(struct pci_softc *);
int pciopen(dev_t dev, int oflags, int devtype, struct proc *p);
int pciclose(dev_t dev, int flag, int devtype, struct proc *p);
int pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p);
int
pciopen(dev_t dev, int oflags, int devtype, struct proc *p)
{
PCIDEBUG(("pciopen ndevs: %d\n" , pci_cd.cd_ndevs));
if (minor(dev) >= pci_ndomains) {
return ENXIO;
}
#ifndef APERTURE
if ((oflags & FWRITE) && securelevel > 0) {
return EPERM;
}
#else
if ((oflags & FWRITE) && securelevel > 0 && allowaperture == 0) {
return EPERM;
}
#endif
return (0);
}
int
pciclose(dev_t dev, int flag, int devtype, struct proc *p)
{
PCIDEBUG(("pciclose\n"));
pci_vga_proc = NULL;
return (0);
}
int
pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct pcisel *sel = (struct pcisel *)data;
struct pci_io *io;
struct pci_dev *pd;
struct pci_rom *rom;
int i, error;
pcitag_t tag;
struct pci_softc *pci;
pci_chipset_tag_t pc;
switch (cmd) {
case PCIOCREAD:
case PCIOCREADMASK:
break;
case PCIOCWRITE:
if (!(flag & FWRITE))
return EPERM;
break;
case PCIOCGETROMLEN:
case PCIOCGETROM:
case PCIOCGETVPD:
break;
case PCIOCGETVGA:
case PCIOCSETVGA:
if (pci_vga_pci == NULL)
return EINVAL;
break;
default:
return ENOTTY;
}
for (i = 0; i < pci_cd.cd_ndevs; i++) {
pci = pci_cd.cd_devs[i];
if (pci != NULL && pci->sc_domain == minor(dev) &&
pci->sc_bus == sel->pc_bus)
break;
}
if (i >= pci_cd.cd_ndevs)
return ENXIO;
/* Check bounds */
if (pci->sc_bus >= 256 ||
sel->pc_dev >= pci_bus_maxdevs(pci->sc_pc, pci->sc_bus) ||
sel->pc_func >= 8)
return EINVAL;
pc = pci->sc_pc;
LIST_FOREACH(pd, &pci->sc_devs, pd_next) {
int bus, dev, func;
pci_decompose_tag(pc, pd->pd_tag, &bus, &dev, &func);
if (bus == sel->pc_bus && dev == sel->pc_dev &&
func == sel->pc_func)
break;
}
if (pd == LIST_END(&pci->sc_devs))
return ENXIO;
tag = pci_make_tag(pc, sel->pc_bus, sel->pc_dev, sel->pc_func);
switch (cmd) {
case PCIOCREAD:
io = (struct pci_io *)data;
switch (io->pi_width) {
case 4:
/* Configuration space bounds check */
if (io->pi_reg < 0 ||
io->pi_reg >= pci_conf_size(pc, tag))
return EINVAL;
/* Make sure the register is properly aligned */
if (io->pi_reg & 0x3)
return EINVAL;
io->pi_data = pci_conf_read(pc, tag, io->pi_reg);
error = 0;
break;
default:
error = EINVAL;
break;
}
break;
case PCIOCWRITE:
io = (struct pci_io *)data;
switch (io->pi_width) {
case 4:
/* Configuration space bounds check */
if (io->pi_reg < 0 ||
io->pi_reg >= pci_conf_size(pc, tag))
return EINVAL;
/* Make sure the register is properly aligned */
if (io->pi_reg & 0x3)
return EINVAL;
pci_conf_write(pc, tag, io->pi_reg, io->pi_data);
error = 0;
break;
default:
error = EINVAL;
break;
}
break;
case PCIOCREADMASK:
io = (struct pci_io *)data;
if (io->pi_width != 4 || io->pi_reg & 0x3 ||
io->pi_reg < PCI_MAPREG_START ||
io->pi_reg >= PCI_MAPREG_END)
return (EINVAL);
i = (io->pi_reg - PCI_MAPREG_START) / 4;
io->pi_data = pd->pd_mask[i];
error = 0;
break;
case PCIOCGETROMLEN:
case PCIOCGETROM:
{
pcireg_t addr, mask, bhlc;
bus_space_handle_t h;
bus_size_t len, off;
char buf[256];
int s;
rom = (struct pci_rom *)data;
bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlc) != 0)
return (ENODEV);
s = splhigh();
addr = pci_conf_read(pc, tag, PCI_ROM_REG);
pci_conf_write(pc, tag, PCI_ROM_REG, ~PCI_ROM_ENABLE);
mask = pci_conf_read(pc, tag, PCI_ROM_REG);
pci_conf_write(pc, tag, PCI_ROM_REG, addr);
splx(s);
/*
* Section 6.2.5.2 `Expansion ROM Base Address Register',
*
* tells us that only the upper 21 bits are writable.
* This means that the size of a ROM must be a
* multiple of 2 KB. So reading the ROM in chunks of
* 256 bytes should work just fine.
*/
if ((PCI_ROM_ADDR(addr) == 0 ||
PCI_ROM_SIZE(mask) % sizeof(buf)) != 0)
return (ENODEV);
/* If we're just after the size, skip reading the ROM. */
if (cmd == PCIOCGETROMLEN) {
error = 0;
goto fail;
}
if (rom->pr_romlen < PCI_ROM_SIZE(mask)) {
error = ENOMEM;
goto fail;
}
error = bus_space_map(pci->sc_memt, PCI_ROM_ADDR(addr),
PCI_ROM_SIZE(mask), 0, &h);
if (error)
goto fail;
off = 0;
len = PCI_ROM_SIZE(mask);
while (len > 0 && error == 0) {
s = splhigh();
pci_conf_write(pc, tag, PCI_ROM_REG,
addr | PCI_ROM_ENABLE);
bus_space_read_region_1(pci->sc_memt, h, off,
buf, sizeof(buf));
pci_conf_write(pc, tag, PCI_ROM_REG, addr);
splx(s);
error = copyout(buf, rom->pr_rom + off, sizeof(buf));
off += sizeof(buf);
len -= sizeof(buf);
}
bus_space_unmap(pci->sc_memt, h, PCI_ROM_SIZE(mask));
fail:
rom->pr_romlen = PCI_ROM_SIZE(mask);
break;
}
case PCIOCGETVPD: {
struct pci_vpd_req *pv = (struct pci_vpd_req *)data;
pcireg_t *data;
size_t len;
unsigned int i;
int s;
CTASSERT(sizeof(*data) == sizeof(*pv->pv_data));
data = mallocarray(pv->pv_count, sizeof(*data), M_TEMP,
M_WAITOK|M_CANFAIL);
if (data == NULL) {
error = ENOMEM;
break;
}
s = splhigh();
error = pci_vpd_read(pc, tag, pv->pv_offset, pv->pv_count,
data);
splx(s);
len = pv->pv_count * sizeof(*pv->pv_data);
if (error == 0) {
for (i = 0; i < pv->pv_count; i++)
data[i] = letoh32(data[i]);
error = copyout(data, pv->pv_data, len);
}
free(data, M_TEMP, len);
break;
}
case PCIOCGETVGA:
{
struct pci_vga *vga = (struct pci_vga *)data;
struct pci_dev *pd;
int bus, dev, func;
vga->pv_decode = 0;
LIST_FOREACH(pd, &pci->sc_devs, pd_next) {
pci_decompose_tag(pc, pd->pd_tag, NULL, &dev, &func);
if (dev == sel->pc_dev && func == sel->pc_func) {
if (pd->pd_vga_decode)
vga->pv_decode = PCI_VGA_IO_ENABLE |
PCI_VGA_MEM_ENABLE;
break;
}
}
pci_decompose_tag(pci_vga_pci->sc_pc,
pci_vga_tag, &bus, &dev, &func);
vga->pv_sel.pc_bus = bus;
vga->pv_sel.pc_dev = dev;
vga->pv_sel.pc_func = func;
error = 0;
break;
}
case PCIOCSETVGA:
{
struct pci_vga *vga = (struct pci_vga *)data;
int bus, dev, func;
switch (vga->pv_lock) {
case PCI_VGA_UNLOCK:
case PCI_VGA_LOCK:
case PCI_VGA_TRYLOCK:
break;
default:
return (EINVAL);
}
if (vga->pv_lock == PCI_VGA_UNLOCK) {
if (pci_vga_proc != p)
return (EINVAL);
pci_vga_proc = NULL;
wakeup(&pci_vga_proc);
return (0);
}
while (pci_vga_proc != p && pci_vga_proc != NULL) {
if (vga->pv_lock == PCI_VGA_TRYLOCK)
return (EBUSY);
error = tsleep_nsec(&pci_vga_proc, PLOCK | PCATCH,
"vgalk", INFSLP);
if (error)
return (error);
}
pci_vga_proc = p;
pci_decompose_tag(pci_vga_pci->sc_pc,
pci_vga_tag, &bus, &dev, &func);
if (bus != vga->pv_sel.pc_bus || dev != vga->pv_sel.pc_dev ||
func != vga->pv_sel.pc_func) {
pci_disable_vga(pci_vga_pci->sc_pc, pci_vga_tag);
if (pci != pci_vga_pci) {
pci_unroute_vga(pci_vga_pci);
pci_route_vga(pci);
pci_vga_pci = pci;
}
pci_enable_vga(pc, tag);
pci_vga_tag = tag;
}
error = 0;
break;
}
default:
error = ENOTTY;
break;
}
return (error);
}
void
pci_disable_vga(pci_chipset_tag_t pc, pcitag_t tag)
{
pcireg_t csr;
csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
csr &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
}
void
pci_enable_vga(pci_chipset_tag_t pc, pcitag_t tag)
{
pcireg_t csr;
csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
csr |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE;
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
}
void
pci_route_vga(struct pci_softc *sc)
{
pci_chipset_tag_t pc = sc->sc_pc;
pcireg_t bc;
if (sc->sc_bridgetag == NULL)
return;
bc = pci_conf_read(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL);
bc |= PPB_BC_VGA_ENABLE;
pci_conf_write(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL, bc);
pci_route_vga((struct pci_softc *)sc->sc_dev.dv_parent->dv_parent);
}
void
pci_unroute_vga(struct pci_softc *sc)
{
pci_chipset_tag_t pc = sc->sc_pc;
pcireg_t bc;
if (sc->sc_bridgetag == NULL)
return;
bc = pci_conf_read(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL);
bc &= ~PPB_BC_VGA_ENABLE;
pci_conf_write(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL, bc);
pci_unroute_vga((struct pci_softc *)sc->sc_dev.dv_parent->dv_parent);
}
#endif /* USER_PCICONF */
int
pci_primary_vga(struct pci_attach_args *pa)
{
/* XXX For now, only handle the first PCI domain. */
if (pa->pa_domain != 0)
return (0);
if ((PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY ||
PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA) &&
(PCI_CLASS(pa->pa_class) != PCI_CLASS_PREHISTORIC ||
PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_PREHISTORIC_VGA))
return (0);
if ((pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG)
& (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
!= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
return (0);
pci_vga_tag = pa->pa_tag;
return (1);
}
#ifdef __HAVE_PCI_MSIX
struct msix_vector *
pci_alloc_msix_table(pci_chipset_tag_t pc, pcitag_t tag)
{
struct msix_vector *table;
pcireg_t reg;
int tblsz;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
return NULL;
tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
table = mallocarray(tblsz, sizeof(*table), M_DEVBUF, M_WAITOK);
return table;
}
void
pci_free_msix_table(pci_chipset_tag_t pc, pcitag_t tag,
struct msix_vector *table)
{
pcireg_t reg;
int tblsz;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
return;
tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
free(table, M_DEVBUF, tblsz * sizeof(*table));
}
void
pci_suspend_msix(pci_chipset_tag_t pc, pcitag_t tag,
bus_space_tag_t memt, pcireg_t *mc, struct msix_vector *table)
{
bus_space_handle_t memh;
pcireg_t reg;
int tblsz, i;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
return;
KASSERT(table != NULL);
if (pci_msix_table_map(pc, tag, memt, &memh))
return;
tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
for (i = 0; i < tblsz; i++) {
table[i].mv_ma = bus_space_read_4(memt, memh, PCI_MSIX_MA(i));
table[i].mv_mau32 = bus_space_read_4(memt, memh,
PCI_MSIX_MAU32(i));
table[i].mv_md = bus_space_read_4(memt, memh, PCI_MSIX_MD(i));
table[i].mv_vc = bus_space_read_4(memt, memh, PCI_MSIX_VC(i));
}
pci_msix_table_unmap(pc, tag, memt, memh);
*mc = reg;
}
void
pci_resume_msix(pci_chipset_tag_t pc, pcitag_t tag,
bus_space_tag_t memt, pcireg_t mc, struct msix_vector *table)
{
bus_space_handle_t memh;
pcireg_t reg;
int tblsz, i;
int off;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0)
return;
KASSERT(table != NULL);
if (pci_msix_table_map(pc, tag, memt, &memh))
return;
tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
for (i = 0; i < tblsz; i++) {
bus_space_write_4(memt, memh, PCI_MSIX_MA(i), table[i].mv_ma);
bus_space_write_4(memt, memh, PCI_MSIX_MAU32(i),
table[i].mv_mau32);
bus_space_write_4(memt, memh, PCI_MSIX_MD(i), table[i].mv_md);
bus_space_barrier(memt, memh, PCI_MSIX_MA(i), 16,
BUS_SPACE_BARRIER_WRITE);
bus_space_write_4(memt, memh, PCI_MSIX_VC(i), table[i].mv_vc);
bus_space_barrier(memt, memh, PCI_MSIX_VC(i), 4,
BUS_SPACE_BARRIER_WRITE);
}
pci_msix_table_unmap(pc, tag, memt, memh);
pci_conf_write(pc, tag, off, mc);
}
int
pci_intr_msix_count(struct pci_attach_args *pa)
{
pcireg_t reg;
if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0)
return (0);
if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSIX, NULL,
®) == 0)
return (0);
return (PCI_MSIX_MC_TBLSZ(reg) + 1);
}
#else /* __HAVE_PCI_MSIX */
struct msix_vector *
pci_alloc_msix_table(pci_chipset_tag_t pc, pcitag_t tag)
{
return NULL;
}
void
pci_free_msix_table(pci_chipset_tag_t pc, pcitag_t tag,
struct msix_vector *table)
{
}
void
pci_suspend_msix(pci_chipset_tag_t pc, pcitag_t tag,
bus_space_tag_t memt, pcireg_t *mc, struct msix_vector *table)
{
}
void
pci_resume_msix(pci_chipset_tag_t pc, pcitag_t tag,
bus_space_tag_t memt, pcireg_t mc, struct msix_vector *table)
{
}
int
pci_intr_msix_count(struct pci_attach_args *pa)
{
return (0);
}
#endif /* __HAVE_PCI_MSIX */
15
14
15
15
15
7
14
3
11
4
4
3
3
3
3
3
3
3
2
3
107
107
14
100
100
100
100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/* $OpenBSD: ufs_bmap.c,v 1.37 2021/12/12 09:14:59 visa Exp $ */
/* $NetBSD: ufs_bmap.c,v 1.3 1996/02/09 22:36:00 christos Exp $ */
/*
* Copyright (c) 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/specdev.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
/*
* Bmap converts a the logical block number of a file to its physical block
* number on the disk. The conversion is done by using the logical block
* number to index into the array of block pointers described by the dinode.
*/
int
ufs_bmap(void *v)
{
struct vop_bmap_args *ap = v;
/*
* Check for underlying vnode requests and ensure that logical
* to physical mapping is requested.
*/
if (ap->a_vpp != NULL)
*ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
if (ap->a_bnp == NULL)
return (0);
return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
ap->a_runp));
}
/*
* Indirect blocks are now on the vnode for the file. They are given negative
* logical block numbers. Indirect blocks are addressed by the negative
* address of the first data block to which they point. Double indirect blocks
* are addressed by one less than the address of the first indirect block to
* which they point. Triple indirect blocks are addressed by one less than
* the address of the first double indirect block to which they point.
*
* ufs_bmaparray does the bmap conversion, and if requested returns the
* array of logical blocks which must be traversed to get to a block.
* Each entry contains the offset into that block that gets you to the
* next block and the disk address of the block (if it is assigned).
*/
int
ufs_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, struct indir *ap,
int *nump, int *runp)
{
struct inode *ip;
struct buf *bp;
struct ufsmount *ump;
struct mount *mp;
struct vnode *devvp;
struct indir a[NIADDR+1], *xap;
daddr_t daddr, metalbn;
int error, maxrun = 0, num;
ip = VTOI(vp);
mp = vp->v_mount;
ump = VFSTOUFS(mp);
#ifdef DIAGNOSTIC
if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL))
panic("ufs_bmaparray: invalid arguments");
#endif
if (runp) {
/*
* XXX
* If MAXBSIZE is the largest transfer the disks can handle,
* we probably want maxrun to be 1 block less so that we
* don't create a block larger than the device can handle.
*/
*runp = 0;
maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1;
}
xap = ap == NULL ? a : ap;
if (!nump)
nump = #
if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0)
return (error);
num = *nump;
if (num == 0) {
*bnp = blkptrtodb(ump, DIP(ip, db[bn]));
if (*bnp == 0)
*bnp = -1;
else if (runp)
for (++bn; bn < NDADDR && *runp < maxrun &&
is_sequential(ump, DIP(ip, db[bn - 1]),
DIP(ip, db[bn]));
++bn, ++*runp);
return (0);
}
/* Get disk address out of indirect block array */
daddr = DIP(ip, ib[xap->in_off]);
devvp = VFSTOUFS(vp->v_mount)->um_devvp;
for (bp = NULL, ++xap; --num; ++xap) {
/*
* Exit the loop if there is no disk address assigned yet and
* the indirect block isn't in the cache, or if we were
* looking for an indirect block and we've found it.
*/
metalbn = xap->in_lbn;
if ((daddr == 0 && !incore(vp, metalbn)) || metalbn == bn)
break;
/*
* If we get here, we've either got the block in the cache
* or we have a disk address for it, go fetch it.
*/
if (bp)
brelse(bp);
xap->in_exists = 1;
bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, INFSLP);
if (bp->b_flags & (B_DONE | B_DELWRI)) {
;
}
#ifdef DIAGNOSTIC
else if (!daddr)
panic("ufs_bmaparray: indirect block not in cache");
#endif
else {
bp->b_blkno = blkptrtodb(ump, daddr);
bp->b_flags |= B_READ;
bcstats.pendingreads++;
bcstats.numreads++;
VOP_STRATEGY(bp->b_vp, bp);
curproc->p_ru.ru_inblock++; /* XXX */
if ((error = biowait(bp)) != 0) {
brelse(bp);
return (error);
}
}
#ifdef FFS2
if (ip->i_ump->um_fstype == UM_UFS2) {
daddr = ((int64_t *)bp->b_data)[xap->in_off];
if (num == 1 && daddr && runp)
for (bn = xap->in_off + 1;
bn < MNINDIR(ump) && *runp < maxrun &&
is_sequential(ump,
((int64_t *)bp->b_data)[bn - 1],
((int64_t *)bp->b_data)[bn]);
++bn, ++*runp);
continue;
}
#endif /* FFS2 */
daddr = ((int32_t *)bp->b_data)[xap->in_off];
if (num == 1 && daddr && runp)
for (bn = xap->in_off + 1;
bn < MNINDIR(ump) && *runp < maxrun &&
is_sequential(ump,
((int32_t *)bp->b_data)[bn - 1],
((int32_t *)bp->b_data)[bn]);
++bn, ++*runp);
}
if (bp)
brelse(bp);
daddr = blkptrtodb(ump, daddr);
*bnp = daddr == 0 ? -1 : daddr;
return (0);
}
/*
* Create an array of logical block number/offset pairs which represent the
* path of indirect blocks required to access a data block. The first "pair"
* contains the logical block number of the appropriate single, double or
* triple indirect block and the offset into the inode indirect block array.
* Note, the logical block number of the inode single/double/triple indirect
* block appears twice in the array, once with the offset into the i_ffs_ib and
* once with the offset into the page itself.
*/
int
ufs_getlbns(struct vnode *vp, daddr_t bn, struct indir *ap, int *nump)
{
daddr_t metalbn, realbn;
struct ufsmount *ump;
int64_t blockcnt;
int i, numlevels, off;
ump = VFSTOUFS(vp->v_mount);
if (nump)
*nump = 0;
numlevels = 0;
realbn = bn;
if (bn < 0)
bn = -bn;
#ifdef DIAGNOSTIC
if (realbn < 0 && realbn > -NDADDR) {
panic ("ufs_getlbns: Invalid indirect block %lld specified",
(long long)realbn);
}
#endif
/* The first NDADDR blocks are direct blocks. */
if (bn < NDADDR)
return (0);
/*
* Determine the number of levels of indirection. After this loop
* is done, blockcnt indicates the number of data blocks possible
* at the given level of indirection, and NIADDR - i is the number
* of levels of indirection needed to locate the requested block.
*/
for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
if (i == 0)
return (EFBIG);
blockcnt *= MNINDIR(ump);
if (bn < blockcnt)
break;
}
/* Calculate the address of the first meta-block. */
if (realbn >= 0)
metalbn = -(realbn - bn + NIADDR - i);
else
metalbn = -(-realbn - bn + NIADDR - i);
/*
* At each iteration, off is the offset into the bap array which is
* an array of disk addresses at the current level of indirection.
* The logical block number and the offset in that block are stored
* into the argument array.
*/
ap->in_lbn = metalbn;
ap->in_off = off = NIADDR - i;
ap->in_exists = 0;
ap++;
for (++numlevels; i <= NIADDR; i++) {
/* If searching for a meta-data block, quit when found. */
if (metalbn == realbn)
break;
blockcnt /= MNINDIR(ump);
off = (bn / blockcnt) % MNINDIR(ump);
++numlevels;
ap->in_lbn = metalbn;
ap->in_off = off;
ap->in_exists = 0;
++ap;
metalbn -= -1 + off * blockcnt;
}
#ifdef DIAGNOSTIC
if (realbn < 0 && metalbn != realbn) {
panic("ufs_getlbns: indirect block %lld not found",
(long long)realbn);
}
#endif
if (nump)
*nump = numlevels;
return (0);
}
217
2
215
2
215
19
207
210
17
217
217
217
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/* $OpenBSD: vioscsi.c,v 1.30 2022/04/16 19:19:59 naddy Exp $ */
/*
* Copyright (c) 2013 Google Inc.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/pv/vioscsireg.h>
#include <dev/pv/virtiovar.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
enum { vioscsi_debug = 0 };
#define DPRINTF(f...) do { if (vioscsi_debug) printf(f); } while (0)
/* Number of DMA segments for buffers that the device must support */
#define SEG_MAX (MAXPHYS/PAGE_SIZE + 1)
/* In the virtqueue, we need space for header and footer, too */
#define ALLOC_SEGS (SEG_MAX + 2)
struct vioscsi_req {
struct virtio_scsi_req_hdr vr_req;
struct virtio_scsi_res_hdr vr_res;
struct scsi_xfer *vr_xs;
bus_dmamap_t vr_control;
bus_dmamap_t vr_data;
SLIST_ENTRY(vioscsi_req) vr_list;
int vr_qe_index;
};
struct vioscsi_softc {
struct device sc_dev;
struct scsi_iopool sc_iopool;
struct mutex sc_vr_mtx;
struct virtqueue sc_vqs[3];
struct vioscsi_req *sc_reqs;
bus_dma_segment_t sc_reqs_segs[1];
SLIST_HEAD(, vioscsi_req) sc_freelist;
};
int vioscsi_match(struct device *, void *, void *);
void vioscsi_attach(struct device *, struct device *, void *);
int vioscsi_alloc_reqs(struct vioscsi_softc *,
struct virtio_softc *, int);
void vioscsi_scsi_cmd(struct scsi_xfer *);
int vioscsi_vq_done(struct virtqueue *);
void vioscsi_req_done(struct vioscsi_softc *, struct virtio_softc *,
struct vioscsi_req *);
void *vioscsi_req_get(void *);
void vioscsi_req_put(void *, void *);
const struct cfattach vioscsi_ca = {
sizeof(struct vioscsi_softc),
vioscsi_match,
vioscsi_attach,
};
struct cfdriver vioscsi_cd = {
NULL, "vioscsi", DV_DULL,
};
const struct scsi_adapter vioscsi_switch = {
vioscsi_scsi_cmd, NULL, NULL, NULL, NULL
};
const char *const vioscsi_vq_names[] = {
"control",
"event",
"request",
};
int
vioscsi_match(struct device *parent, void *self, void *aux)
{
struct virtio_softc *va = (struct virtio_softc *)aux;
if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_SCSI)
return (1);
return (0);
}
void
vioscsi_attach(struct device *parent, struct device *self, void *aux)
{
struct virtio_softc *vsc = (struct virtio_softc *)parent;
struct vioscsi_softc *sc = (struct vioscsi_softc *)self;
struct scsibus_attach_args saa;
int i, rv;
if (vsc->sc_child != NULL) {
printf(": parent already has a child\n");
return;
}
vsc->sc_child = &sc->sc_dev;
vsc->sc_ipl = IPL_BIO;
// TODO(matthew): Negotiate hotplug.
vsc->sc_vqs = sc->sc_vqs;
vsc->sc_nvqs = nitems(sc->sc_vqs);
virtio_negotiate_features(vsc, NULL);
uint32_t cmd_per_lun = virtio_read_device_config_4(vsc,
VIRTIO_SCSI_CONFIG_CMD_PER_LUN);
uint32_t seg_max = virtio_read_device_config_4(vsc,
VIRTIO_SCSI_CONFIG_SEG_MAX);
uint16_t max_target = virtio_read_device_config_2(vsc,
VIRTIO_SCSI_CONFIG_MAX_TARGET);
if (seg_max < SEG_MAX) {
printf("\nMax number of segments %d too small\n", seg_max);
goto err;
}
for (i = 0; i < nitems(sc->sc_vqs); i++) {
rv = virtio_alloc_vq(vsc, &sc->sc_vqs[i], i, MAXPHYS,
ALLOC_SEGS, vioscsi_vq_names[i]);
if (rv) {
printf(": failed to allocate virtqueue %d\n", i);
goto err;
}
sc->sc_vqs[i].vq_done = vioscsi_vq_done;
}
int qsize = sc->sc_vqs[2].vq_num;
printf(": qsize %d\n", qsize);
SLIST_INIT(&sc->sc_freelist);
mtx_init(&sc->sc_vr_mtx, IPL_BIO);
scsi_iopool_init(&sc->sc_iopool, sc, vioscsi_req_get, vioscsi_req_put);
int nreqs = vioscsi_alloc_reqs(sc, vsc, qsize);
if (nreqs == 0) {
printf("\nCan't alloc reqs\n");
goto err;
}
saa.saa_adapter = &vioscsi_switch;
saa.saa_adapter_softc = sc;
saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
saa.saa_adapter_buswidth = max_target;
saa.saa_luns = 8;
saa.saa_openings = (nreqs > cmd_per_lun) ? cmd_per_lun : nreqs;
saa.saa_pool = &sc->sc_iopool;
saa.saa_quirks = saa.saa_flags = 0;
saa.saa_wwpn = saa.saa_wwnn = 0;
config_found(self, &saa, scsiprint);
return;
err:
vsc->sc_child = VIRTIO_CHILD_ERROR;
return;
}
void
vioscsi_scsi_cmd(struct scsi_xfer *xs)
{
struct vioscsi_softc *sc = xs->sc_link->bus->sb_adapter_softc;
struct virtio_softc *vsc = (struct virtio_softc *)sc->sc_dev.dv_parent;
struct vioscsi_req *vr = xs->io;
struct virtio_scsi_req_hdr *req = &vr->vr_req;
struct virtqueue *vq = &sc->sc_vqs[2];
int slot = vr->vr_qe_index;
DPRINTF("vioscsi_scsi_cmd: enter\n");
// TODO(matthew): Support bidirectional SCSI commands?
if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT))
== (SCSI_DATA_IN | SCSI_DATA_OUT)) {
goto stuffup;
}
vr->vr_xs = xs;
/*
* "The only supported format for the LUN field is: first byte set to
* 1, second byte set to target, third and fourth byte representing a
* single level LUN structure, followed by four zero bytes."
*/
if (xs->sc_link->target >= 256 || xs->sc_link->lun >= 16384)
goto stuffup;
req->lun[0] = 1;
req->lun[1] = xs->sc_link->target;
req->lun[2] = 0x40 | (xs->sc_link->lun >> 8);
req->lun[3] = xs->sc_link->lun;
memset(req->lun + 4, 0, 4);
if ((size_t)xs->cmdlen > sizeof(req->cdb))
goto stuffup;
memset(req->cdb, 0, sizeof(req->cdb));
memcpy(req->cdb, &xs->cmd, xs->cmdlen);
int isread = !!(xs->flags & SCSI_DATA_IN);
int nsegs = 2;
if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
if (bus_dmamap_load(vsc->sc_dmat, vr->vr_data,
xs->data, xs->datalen, NULL,
((isread ? BUS_DMA_READ : BUS_DMA_WRITE) |
BUS_DMA_NOWAIT)))
goto stuffup;
nsegs += vr->vr_data->dm_nsegs;
}
/*
* Adjust reservation to the number needed, or virtio gets upset. Note
* that it may trim UP if 'xs' is being recycled w/o getting a new
* reservation!
*/
int s = splbio();
virtio_enqueue_trim(vq, slot, nsegs);
splx(s);
bus_dmamap_sync(vsc->sc_dmat, vr->vr_control,
offsetof(struct vioscsi_req, vr_req),
sizeof(struct virtio_scsi_req_hdr),
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(vsc->sc_dmat, vr->vr_control,
offsetof(struct vioscsi_req, vr_res),
sizeof(struct virtio_scsi_res_hdr),
BUS_DMASYNC_PREREAD);
if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT))
bus_dmamap_sync(vsc->sc_dmat, vr->vr_data, 0, xs->datalen,
isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
s = splbio();
virtio_enqueue_p(vq, slot, vr->vr_control,
offsetof(struct vioscsi_req, vr_req),
sizeof(struct virtio_scsi_req_hdr),
1);
if (xs->flags & SCSI_DATA_OUT)
virtio_enqueue(vq, slot, vr->vr_data, 1);
virtio_enqueue_p(vq, slot, vr->vr_control,
offsetof(struct vioscsi_req, vr_res),
sizeof(struct virtio_scsi_res_hdr),
0);
if (xs->flags & SCSI_DATA_IN)
virtio_enqueue(vq, slot, vr->vr_data, 0);
virtio_enqueue_commit(vsc, vq, slot, 1);
if (ISSET(xs->flags, SCSI_POLL)) {
DPRINTF("vioscsi_scsi_cmd: polling...\n");
int timeout = 1000;
do {
virtio_poll_intr(vsc);
if (vr->vr_xs != xs)
break;
delay(1000);
} while (--timeout > 0);
if (vr->vr_xs == xs) {
// TODO(matthew): Abort the request.
xs->error = XS_TIMEOUT;
xs->resid = xs->datalen;
DPRINTF("vioscsi_scsi_cmd: polling timeout\n");
scsi_done(xs);
}
DPRINTF("vioscsi_scsi_cmd: done (timeout=%d)\n", timeout);
}
splx(s);
return;
stuffup:
xs->error = XS_DRIVER_STUFFUP;
xs->resid = xs->datalen;
DPRINTF("vioscsi_scsi_cmd: stuffup\n");
scsi_done(xs);
}
void
vioscsi_req_done(struct vioscsi_softc *sc, struct virtio_softc *vsc,
struct vioscsi_req *vr)
{
struct scsi_xfer *xs = vr->vr_xs;
DPRINTF("vioscsi_req_done: enter vr: %p xs: %p\n", vr, xs);
int isread = !!(xs->flags & SCSI_DATA_IN);
bus_dmamap_sync(vsc->sc_dmat, vr->vr_control,
offsetof(struct vioscsi_req, vr_req),
sizeof(struct virtio_scsi_req_hdr),
BUS_DMASYNC_POSTWRITE);
bus_dmamap_sync(vsc->sc_dmat, vr->vr_control,
offsetof(struct vioscsi_req, vr_res),
sizeof(struct virtio_scsi_res_hdr),
BUS_DMASYNC_POSTREAD);
if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
bus_dmamap_sync(vsc->sc_dmat, vr->vr_data, 0, xs->datalen,
isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(vsc->sc_dmat, vr->vr_data);
}
if (vr->vr_res.response != VIRTIO_SCSI_S_OK) {
xs->error = XS_DRIVER_STUFFUP;
xs->resid = xs->datalen;
DPRINTF("vioscsi_req_done: stuffup: %d\n", vr->vr_res.response);
goto done;
}
size_t sense_len = MIN(sizeof(xs->sense), vr->vr_res.sense_len);
memcpy(&xs->sense, vr->vr_res.sense, sense_len);
xs->error = (sense_len == 0) ? XS_NOERROR : XS_SENSE;
xs->status = vr->vr_res.status;
xs->resid = vr->vr_res.residual;
DPRINTF("vioscsi_req_done: done %d, %d, %zd\n",
xs->error, xs->status, xs->resid);
done:
vr->vr_xs = NULL;
scsi_done(xs);
}
int
vioscsi_vq_done(struct virtqueue *vq)
{
struct virtio_softc *vsc = vq->vq_owner;
struct vioscsi_softc *sc = (struct vioscsi_softc *)vsc->sc_child;
struct vq_entry *qe;
struct vioscsi_req *vr;
int ret = 0;
DPRINTF("vioscsi_vq_done: enter\n");
for (;;) {
int r, s, slot;
s = splbio();
r = virtio_dequeue(vsc, vq, &slot, NULL);
splx(s);
if (r != 0)
break;
DPRINTF("vioscsi_vq_done: slot=%d\n", slot);
qe = &vq->vq_entries[slot];
vr = &sc->sc_reqs[qe->qe_vr_index];
vioscsi_req_done(sc, vsc, vr);
ret = 1;
}
DPRINTF("vioscsi_vq_done: exit %d\n", ret);
return (ret);
}
/*
* vioscso_req_get() provides the SCSI layer with all the
* resources necessary to start an I/O on the device.
*
* Since the size of the I/O is unknown at this time the
* resources allocated (a.k.a. reserved) must be sufficient
* to allow the maximum possible I/O size.
*
* When the I/O is actually attempted via vioscsi_scsi_cmd()
* excess resources will be returned via virtio_enqueue_trim().
*/
void *
vioscsi_req_get(void *cookie)
{
struct vioscsi_softc *sc = cookie;
struct vioscsi_req *vr = NULL;
mtx_enter(&sc->sc_vr_mtx);
vr = SLIST_FIRST(&sc->sc_freelist);
if (vr != NULL)
SLIST_REMOVE_HEAD(&sc->sc_freelist, vr_list);
mtx_leave(&sc->sc_vr_mtx);
DPRINTF("vioscsi_req_get: %p\n", vr);
return (vr);
}
void
vioscsi_req_put(void *cookie, void *io)
{
struct vioscsi_softc *sc = cookie;
struct vioscsi_req *vr = io;
DPRINTF("vioscsi_req_put: %p\n", vr);
mtx_enter(&sc->sc_vr_mtx);
/*
* Do *NOT* call virtio_dequeue_commit()!
*
* Descriptors are permanently associated with the vioscsi_req and
* should not be placed on the free list!
*/
SLIST_INSERT_HEAD(&sc->sc_freelist, vr, vr_list);
mtx_leave(&sc->sc_vr_mtx);
}
int
vioscsi_alloc_reqs(struct vioscsi_softc *sc, struct virtio_softc *vsc,
int qsize)
{
struct virtqueue *vq = &sc->sc_vqs[2];
struct vioscsi_req *vr;
struct vring_desc *vd;
size_t allocsize;
int i, r, nreqs, rsegs, slot;
void *vaddr;
if (vq->vq_indirect != NULL)
nreqs = qsize;
else
nreqs = qsize / ALLOC_SEGS;
allocsize = nreqs * sizeof(struct vioscsi_req);
r = bus_dmamem_alloc(vsc->sc_dmat, allocsize, 0, 0,
&sc->sc_reqs_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
if (r != 0) {
printf("bus_dmamem_alloc, size %zd, error %d\n",
allocsize, r);
return 0;
}
r = bus_dmamem_map(vsc->sc_dmat, &sc->sc_reqs_segs[0], 1,
allocsize, (caddr_t *)&vaddr, BUS_DMA_NOWAIT);
if (r != 0) {
printf("bus_dmamem_map failed, error %d\n", r);
bus_dmamem_free(vsc->sc_dmat, &sc->sc_reqs_segs[0], 1);
return 0;
}
sc->sc_reqs = vaddr;
memset(vaddr, 0, allocsize);
for (i = 0; i < nreqs; i++) {
/*
* Assign descriptors and create the DMA maps for each
* allocated request.
*/
vr = &sc->sc_reqs[i];
r = virtio_enqueue_prep(vq, &slot);
if (r == 0)
r = virtio_enqueue_reserve(vq, slot, ALLOC_SEGS);
if (r != 0)
return i;
if (vq->vq_indirect == NULL) {
/*
* The reserved slots must be a contiguous block
* starting at vq_desc[slot].
*/
vd = &vq->vq_desc[slot];
for (r = 0; r < ALLOC_SEGS - 1; r++) {
DPRINTF("vd[%d].next = %d should be %d\n",
r, vd[r].next, (slot + r + 1));
if (vd[r].next != (slot + r + 1))
return i;
}
if (r == (ALLOC_SEGS -1) && vd[r].next != 0)
return i;
DPRINTF("Reserved slots are contiguous as required!\n");
}
vr->vr_qe_index = slot;
vr->vr_req.id = slot;
vr->vr_req.task_attr = VIRTIO_SCSI_S_SIMPLE;
vq->vq_entries[slot].qe_vr_index = i;
r = bus_dmamap_create(vsc->sc_dmat,
offsetof(struct vioscsi_req, vr_xs), 1,
offsetof(struct vioscsi_req, vr_xs), 0,
BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &vr->vr_control);
if (r != 0) {
printf("bus_dmamap_create vr_control failed, error %d\n", r);
return i;
}
r = bus_dmamap_create(vsc->sc_dmat, MAXPHYS, SEG_MAX,
MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &vr->vr_data);
if (r != 0) {
printf("bus_dmamap_create vr_data failed, error %d\n", r );
return i;
}
r = bus_dmamap_load(vsc->sc_dmat, vr->vr_control,
vr, offsetof(struct vioscsi_req, vr_xs), NULL,
BUS_DMA_NOWAIT);
if (r != 0) {
printf("bus_dmamap_load vr_control failed, error %d\n", r );
return i;
}
SLIST_INSERT_HEAD(&sc->sc_freelist, vr, vr_list);
}
return nreqs;
}
15
2
9
4
11
6
8
5
16
11
5
1
12
8
1
1
7
2
8
1
7
7
884
883
15
15
18
17
1138
1137
25
25
478
476
1887
1879
989
988
466
468
134
135
12
179
178
134
134
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
/* $OpenBSD: kern_pledge.c,v 1.295 2022/09/05 16:37:47 mbuhl Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
* Copyright (c) 2015 Theo de Raadt <deraadt@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/mutex.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/namei.h>
#include <sys/socketvar.h>
#include <sys/vnode.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/ktrace.h>
#include <sys/acct.h>
#include <sys/swap.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
#include <sys/tty.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <sys/mtio.h>
#include <sys/audioio.h>
#include <sys/videoio.h>
#include <net/bpf.h>
#include <net/route.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#include <netinet/tcp.h>
#include <net/pfvar.h>
#include <sys/conf.h>
#include <sys/specdev.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/systm.h>
#include <dev/biovar.h>
#define PLEDGENAMES
#include <sys/pledge.h>
#include "audio.h"
#include "bpfilter.h"
#include "pf.h"
#include "video.h"
#include "pty.h"
#if defined(__amd64__)
#include "vmm.h"
#if NVMM > 0
#include <machine/conf.h>
#endif
#endif
#include "drm.h"
uint64_t pledgereq_flags(const char *req);
int parsepledges(struct proc *p, const char *kname,
const char *promises, u_int64_t *fp);
int canonpath(const char *input, char *buf, size_t bufsize);
void unveil_destroy(struct process *ps);
/* #define DEBUG_PLEDGE */
#ifdef DEBUG_PLEDGE
int debug_pledge = 1;
#define DPRINTF(x...) do { if (debug_pledge) printf(x); } while (0)
#define DNPRINTF(n,x...) do { if (debug_pledge >= (n)) printf(x); } while (0)
#else
#define DPRINTF(x...)
#define DNPRINTF(n,x...)
#endif
/*
* Ordered in blocks starting with least risky and most required.
*/
const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
/*
* Minimum required
*/
[SYS_exit] = PLEDGE_ALWAYS,
[SYS_kbind] = PLEDGE_ALWAYS,
[SYS_msyscall] = PLEDGE_ALWAYS,
[SYS___get_tcb] = PLEDGE_ALWAYS,
[SYS___set_tcb] = PLEDGE_ALWAYS,
[SYS_pledge] = PLEDGE_ALWAYS,
[SYS_sendsyslog] = PLEDGE_ALWAYS, /* stack protector reporting */
[SYS_thrkill] = PLEDGE_ALWAYS, /* raise, abort, stack pro */
[SYS_utrace] = PLEDGE_ALWAYS, /* ltrace(1) from ld.so */
/* "getting" information about self is considered safe */
[SYS_getuid] = PLEDGE_STDIO,
[SYS_geteuid] = PLEDGE_STDIO,
[SYS_getresuid] = PLEDGE_STDIO,
[SYS_getgid] = PLEDGE_STDIO,
[SYS_getegid] = PLEDGE_STDIO,
[SYS_getresgid] = PLEDGE_STDIO,
[SYS_getgroups] = PLEDGE_STDIO,
[SYS_getlogin_r] = PLEDGE_STDIO,
[SYS_getpgrp] = PLEDGE_STDIO,
[SYS_getpgid] = PLEDGE_STDIO,
[SYS_getppid] = PLEDGE_STDIO,
[SYS_getsid] = PLEDGE_STDIO,
[SYS_getthrid] = PLEDGE_STDIO,
[SYS_getrlimit] = PLEDGE_STDIO,
[SYS_getrtable] = PLEDGE_STDIO,
[SYS_gettimeofday] = PLEDGE_STDIO,
[SYS_getdtablecount] = PLEDGE_STDIO,
[SYS_getrusage] = PLEDGE_STDIO,
[SYS_issetugid] = PLEDGE_STDIO,
[SYS_clock_getres] = PLEDGE_STDIO,
[SYS_clock_gettime] = PLEDGE_STDIO,
[SYS_getpid] = PLEDGE_STDIO,
/*
* Almost exclusively read-only, Very narrow subset.
* Use of "route", "inet", "dns", "ps", or "vminfo"
* expands access.
*/
[SYS_sysctl] = PLEDGE_STDIO,
/* Support for malloc(3) family of operations */
[SYS_getentropy] = PLEDGE_STDIO,
[SYS_madvise] = PLEDGE_STDIO,
[SYS_minherit] = PLEDGE_STDIO,
[SYS_mmap] = PLEDGE_STDIO,
[SYS_mprotect] = PLEDGE_STDIO,
[SYS_mquery] = PLEDGE_STDIO,
[SYS_munmap] = PLEDGE_STDIO,
[SYS_msync] = PLEDGE_STDIO,
[SYS_break] = PLEDGE_STDIO,
[SYS_umask] = PLEDGE_STDIO,
/* read/write operations */
[SYS_read] = PLEDGE_STDIO,
[SYS_readv] = PLEDGE_STDIO,
[SYS_pread] = PLEDGE_STDIO,
[SYS_preadv] = PLEDGE_STDIO,
[SYS_write] = PLEDGE_STDIO,
[SYS_writev] = PLEDGE_STDIO,
[SYS_pwrite] = PLEDGE_STDIO,
[SYS_pwritev] = PLEDGE_STDIO,
[SYS_recvmsg] = PLEDGE_STDIO,
[SYS_recvmmsg] = PLEDGE_STDIO,
[SYS_recvfrom] = PLEDGE_STDIO,
[SYS_ftruncate] = PLEDGE_STDIO,
[SYS_lseek] = PLEDGE_STDIO,
[SYS_fpathconf] = PLEDGE_STDIO,
#if 1
[SYS_pad_mquery] = PLEDGE_STDIO,
[SYS_pad_mmap] = PLEDGE_STDIO,
[SYS_pad_pread] = PLEDGE_STDIO,
[SYS_pad_preadv] = PLEDGE_STDIO,
[SYS_pad_pwrite] = PLEDGE_STDIO,
[SYS_pad_pwritev] = PLEDGE_STDIO,
[SYS_pad_ftruncate] = PLEDGE_STDIO,
[SYS_pad_lseek] = PLEDGE_STDIO,
[SYS_pad_truncate] = PLEDGE_WPATH,
#endif
/*
* Address selection required a network pledge ("inet",
* "unix", "dns".
*/
[SYS_sendto] = PLEDGE_STDIO,
/*
* Address specification required a network pledge ("inet",
* "unix", "dns". SCM_RIGHTS requires "sendfd" or "recvfd".
*/
[SYS_sendmsg] = PLEDGE_STDIO,
[SYS_sendmmsg] = PLEDGE_STDIO,
/* Common signal operations */
[SYS_nanosleep] = PLEDGE_STDIO,
[SYS_sigaltstack] = PLEDGE_STDIO,
[SYS_sigprocmask] = PLEDGE_STDIO,
[SYS_sigsuspend] = PLEDGE_STDIO,
[SYS_sigaction] = PLEDGE_STDIO,
[SYS_sigreturn] = PLEDGE_STDIO,
[SYS_sigpending] = PLEDGE_STDIO,
[SYS_getitimer] = PLEDGE_STDIO,
[SYS_setitimer] = PLEDGE_STDIO,
/*
* To support event driven programming.
*/
[SYS_poll] = PLEDGE_STDIO,
[SYS_ppoll] = PLEDGE_STDIO,
[SYS_kevent] = PLEDGE_STDIO,
[SYS_kqueue] = PLEDGE_STDIO,
[SYS_select] = PLEDGE_STDIO,
[SYS_pselect] = PLEDGE_STDIO,
[SYS_fstat] = PLEDGE_STDIO,
[SYS_fsync] = PLEDGE_STDIO,
[SYS_setsockopt] = PLEDGE_STDIO, /* narrow whitelist */
[SYS_getsockopt] = PLEDGE_STDIO, /* narrow whitelist */
/* F_SETOWN requires PLEDGE_PROC */
[SYS_fcntl] = PLEDGE_STDIO,
[SYS_close] = PLEDGE_STDIO,
[SYS_dup] = PLEDGE_STDIO,
[SYS_dup2] = PLEDGE_STDIO,
[SYS_dup3] = PLEDGE_STDIO,
[SYS_closefrom] = PLEDGE_STDIO,
[SYS_shutdown] = PLEDGE_STDIO,
[SYS_fchdir] = PLEDGE_STDIO, /* XXX consider tightening */
[SYS_pipe] = PLEDGE_STDIO,
[SYS_pipe2] = PLEDGE_STDIO,
[SYS_socketpair] = PLEDGE_STDIO,
[SYS_wait4] = PLEDGE_STDIO,
/*
* Can kill self with "stdio". Killing another pid
* requires "proc"
*/
[SYS_kill] = PLEDGE_STDIO,
/*
* FIONREAD/FIONBIO for "stdio"
* Other ioctl are selectively allowed based upon other pledges.
*/
[SYS_ioctl] = PLEDGE_STDIO,
/*
* Path access/creation calls encounter many extensive
* checks done during pledge_namei()
*/
[SYS_open] = PLEDGE_STDIO,
[SYS_stat] = PLEDGE_STDIO,
[SYS_access] = PLEDGE_STDIO,
[SYS_readlink] = PLEDGE_STDIO,
[SYS___realpath] = PLEDGE_STDIO,
[SYS_adjtime] = PLEDGE_STDIO, /* setting requires "settime" */
[SYS_adjfreq] = PLEDGE_SETTIME,
[SYS_settimeofday] = PLEDGE_SETTIME,
/*
* Needed by threaded programs
* XXX should we have a new "threads"?
*/
[SYS___tfork] = PLEDGE_STDIO,
[SYS_sched_yield] = PLEDGE_STDIO,
[SYS_futex] = PLEDGE_STDIO,
[SYS___thrsleep] = PLEDGE_STDIO,
[SYS___thrwakeup] = PLEDGE_STDIO,
[SYS___threxit] = PLEDGE_STDIO,
[SYS___thrsigdivert] = PLEDGE_STDIO,
[SYS_fork] = PLEDGE_PROC,
[SYS_vfork] = PLEDGE_PROC,
[SYS_setpgid] = PLEDGE_PROC,
[SYS_setsid] = PLEDGE_PROC,
[SYS_setrlimit] = PLEDGE_PROC | PLEDGE_ID,
[SYS_getpriority] = PLEDGE_PROC | PLEDGE_ID,
[SYS_setpriority] = PLEDGE_PROC | PLEDGE_ID,
[SYS_setuid] = PLEDGE_ID,
[SYS_seteuid] = PLEDGE_ID,
[SYS_setreuid] = PLEDGE_ID,
[SYS_setresuid] = PLEDGE_ID,
[SYS_setgid] = PLEDGE_ID,
[SYS_setegid] = PLEDGE_ID,
[SYS_setregid] = PLEDGE_ID,
[SYS_setresgid] = PLEDGE_ID,
[SYS_setgroups] = PLEDGE_ID,
[SYS_setlogin] = PLEDGE_ID,
[SYS_setrtable] = PLEDGE_ID,
[SYS_unveil] = PLEDGE_UNVEIL,
[SYS_execve] = PLEDGE_EXEC,
[SYS_chdir] = PLEDGE_RPATH,
[SYS_openat] = PLEDGE_RPATH | PLEDGE_WPATH,
[SYS_fstatat] = PLEDGE_RPATH | PLEDGE_WPATH,
[SYS_faccessat] = PLEDGE_RPATH | PLEDGE_WPATH,
[SYS_readlinkat] = PLEDGE_RPATH | PLEDGE_WPATH,
[SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH | PLEDGE_TMPPATH,
[SYS_truncate] = PLEDGE_WPATH,
[SYS_rename] = PLEDGE_RPATH | PLEDGE_CPATH,
[SYS_rmdir] = PLEDGE_CPATH,
[SYS_renameat] = PLEDGE_CPATH,
[SYS_link] = PLEDGE_CPATH,
[SYS_linkat] = PLEDGE_CPATH,
[SYS_symlink] = PLEDGE_CPATH,
[SYS_symlinkat] = PLEDGE_CPATH,
[SYS_unlink] = PLEDGE_CPATH | PLEDGE_TMPPATH,
[SYS_unlinkat] = PLEDGE_CPATH,
[SYS_mkdir] = PLEDGE_CPATH,
[SYS_mkdirat] = PLEDGE_CPATH,
[SYS_mkfifo] = PLEDGE_DPATH,
[SYS_mkfifoat] = PLEDGE_DPATH,
[SYS_mknod] = PLEDGE_DPATH,
[SYS_mknodat] = PLEDGE_DPATH,
[SYS_revoke] = PLEDGE_TTY, /* also requires PLEDGE_RPATH */
/*
* Classify as RPATH|WPATH, because of path information leakage.
* WPATH due to unknown use of mk*temp(3) on non-/tmp paths..
*/
[SYS___getcwd] = PLEDGE_RPATH | PLEDGE_WPATH,
/* Classify as RPATH, because these leak path information */
[SYS_getdents] = PLEDGE_RPATH,
[SYS_getfsstat] = PLEDGE_RPATH,
[SYS_statfs] = PLEDGE_RPATH,
[SYS_fstatfs] = PLEDGE_RPATH,
[SYS_pathconf] = PLEDGE_RPATH,
[SYS_utimes] = PLEDGE_FATTR,
[SYS_futimes] = PLEDGE_FATTR,
[SYS_utimensat] = PLEDGE_FATTR,
[SYS_futimens] = PLEDGE_FATTR,
[SYS_chmod] = PLEDGE_FATTR,
[SYS_fchmod] = PLEDGE_FATTR,
[SYS_fchmodat] = PLEDGE_FATTR,
[SYS_chflags] = PLEDGE_FATTR,
[SYS_chflagsat] = PLEDGE_FATTR,
[SYS_fchflags] = PLEDGE_FATTR,
[SYS_chown] = PLEDGE_CHOWN,
[SYS_fchownat] = PLEDGE_CHOWN,
[SYS_lchown] = PLEDGE_CHOWN,
[SYS_fchown] = PLEDGE_CHOWN,
[SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_bind] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS,
[SYS_listen] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_accept] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX,
[SYS_flock] = PLEDGE_FLOCK,
[SYS_ypconnect] = PLEDGE_GETPW,
[SYS_swapctl] = PLEDGE_VMINFO,
};
static const struct {
char *name;
uint64_t flags;
} pledgereq[] = {
{ "audio", PLEDGE_AUDIO },
{ "bpf", PLEDGE_BPF },
{ "chown", PLEDGE_CHOWN | PLEDGE_CHOWNUID },
{ "cpath", PLEDGE_CPATH },
{ "disklabel", PLEDGE_DISKLABEL },
{ "dns", PLEDGE_DNS },
{ "dpath", PLEDGE_DPATH },
{ "drm", PLEDGE_DRM },
{ "error", PLEDGE_ERROR },
{ "exec", PLEDGE_EXEC },
{ "fattr", PLEDGE_FATTR | PLEDGE_CHOWN },
{ "flock", PLEDGE_FLOCK },
{ "getpw", PLEDGE_GETPW },
{ "id", PLEDGE_ID },
{ "inet", PLEDGE_INET },
{ "mcast", PLEDGE_MCAST },
{ "pf", PLEDGE_PF },
{ "proc", PLEDGE_PROC },
{ "prot_exec", PLEDGE_PROTEXEC },
{ "ps", PLEDGE_PS },
{ "recvfd", PLEDGE_RECVFD },
{ "route", PLEDGE_ROUTE },
{ "rpath", PLEDGE_RPATH },
{ "sendfd", PLEDGE_SENDFD },
{ "settime", PLEDGE_SETTIME },
{ "stdio", PLEDGE_STDIO },
{ "tape", PLEDGE_TAPE },
{ "tmppath", PLEDGE_TMPPATH },
{ "tty", PLEDGE_TTY },
{ "unix", PLEDGE_UNIX },
{ "unveil", PLEDGE_UNVEIL },
{ "video", PLEDGE_VIDEO },
{ "vminfo", PLEDGE_VMINFO },
{ "vmm", PLEDGE_VMM },
{ "wpath", PLEDGE_WPATH },
{ "wroute", PLEDGE_WROUTE },
};
int
parsepledges(struct proc *p, const char *kname, const char *promises, u_int64_t *fp)
{
size_t rbuflen;
char *rbuf, *rp, *pn;
u_int64_t flags = 0, f;
int error;
rbuf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
error = copyinstr(promises, rbuf, MAXPATHLEN,
&rbuflen);
if (error) {
free(rbuf, M_TEMP, MAXPATHLEN);
return (error);
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrstruct(p, kname, rbuf, rbuflen-1);
#endif
for (rp = rbuf; rp && *rp; rp = pn) {
pn = strchr(rp, ' '); /* find terminator */
if (pn) {
while (*pn == ' ')
*pn++ = '\0';
}
if ((f = pledgereq_flags(rp)) == 0) {
free(rbuf, M_TEMP, MAXPATHLEN);
return (EINVAL);
}
flags |= f;
}
free(rbuf, M_TEMP, MAXPATHLEN);
*fp = flags;
return 0;
}
int
sys_pledge(struct proc *p, void *v, register_t *retval)
{
struct sys_pledge_args /* {
syscallarg(const char *)promises;
syscallarg(const char *)execpromises;
} */ *uap = v;
struct process *pr = p->p_p;
uint64_t promises, execpromises;
int error;
int unveil_cleanup = 0;
/* Check for any error in user input */
if (SCARG(uap, promises)) {
error = parsepledges(p, "pledgereq",
SCARG(uap, promises), &promises);
if (error)
return (error);
}
if (SCARG(uap, execpromises)) {
error = parsepledges(p, "pledgeexecreq",
SCARG(uap, execpromises), &execpromises);
if (error)
return (error);
}
mtx_enter(&pr->ps_mtx);
/* Check for any error wrt current promises */
if (SCARG(uap, promises)) {
/* In "error" mode, ignore promise increase requests,
* but accept promise decrease requests */
if (ISSET(pr->ps_flags, PS_PLEDGE) &&
(pr->ps_pledge & PLEDGE_ERROR))
promises &= (pr->ps_pledge & PLEDGE_USERSET);
/* Only permit reductions */
if (ISSET(pr->ps_flags, PS_PLEDGE) &&
(((promises | pr->ps_pledge) != pr->ps_pledge))) {
mtx_leave(&pr->ps_mtx);
return (EPERM);
}
}
if (SCARG(uap, execpromises)) {
/* Only permit reductions */
if (ISSET(pr->ps_flags, PS_EXECPLEDGE) &&
(((execpromises | pr->ps_execpledge) != pr->ps_execpledge))) {
mtx_leave(&pr->ps_mtx);
return (EPERM);
}
}
/* Set up promises */
if (SCARG(uap, promises)) {
pr->ps_pledge = promises;
atomic_setbits_int(&pr->ps_flags, PS_PLEDGE);
if ((pr->ps_pledge & (PLEDGE_RPATH | PLEDGE_WPATH |
PLEDGE_CPATH | PLEDGE_DPATH | PLEDGE_TMPPATH | PLEDGE_EXEC |
PLEDGE_UNIX | PLEDGE_UNVEIL)) == 0)
unveil_cleanup = 1;
}
if (SCARG(uap, execpromises)) {
pr->ps_execpledge = execpromises;
atomic_setbits_int(&pr->ps_flags, PS_EXECPLEDGE);
}
mtx_leave(&pr->ps_mtx);
if (unveil_cleanup) {
/*
* Kill off unveil and drop unveil vnode refs if we no
* longer are holding any path-accessing pledge
*/
KERNEL_LOCK();
unveil_destroy(pr);
KERNEL_UNLOCK();
}
return (0);
}
int
pledge_syscall(struct proc *p, int code, uint64_t *tval)
{
p->p_pledge_syscall = code;
*tval = 0;
if (code < 0 || code > SYS_MAXSYSCALL - 1)
return (EINVAL);
if (pledge_syscalls[code] == PLEDGE_ALWAYS)
return (0);
if (p->p_p->ps_pledge & pledge_syscalls[code])
return (0);
*tval = pledge_syscalls[code];
return (EPERM);
}
int
pledge_fail(struct proc *p, int error, uint64_t code)
{
const char *codes = "";
int i;
/* Print first matching pledge */
for (i = 0; code && pledgenames[i].bits != 0; i++)
if (pledgenames[i].bits & code) {
codes = pledgenames[i].name;
break;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_PLEDGE))
ktrpledge(p, error, code, p->p_pledge_syscall);
#endif
if (p->p_p->ps_pledge & PLEDGE_ERROR)
return (ENOSYS);
KERNEL_LOCK();
log(LOG_ERR, "%s[%d]: pledge \"%s\", syscall %d\n",
p->p_p->ps_comm, p->p_p->ps_pid, codes, p->p_pledge_syscall);
p->p_p->ps_acflag |= APLEDGE;
/* Stop threads immediately, because this process is suspect */
if (P_HASSIBLING(p))
single_thread_set(p, SINGLE_SUSPEND, 1);
/* Send uncatchable SIGABRT for coredump */
sigabort(p);
p->p_p->ps_pledge = 0; /* Disable all PLEDGE_ flags */
KERNEL_UNLOCK();
return (error);
}
/*
* Need to make it more obvious that one cannot get through here
* without the right flags set
*/
int
pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
{
char path[PATH_MAX];
uint64_t pledge;
int error;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0 ||
(p->p_p->ps_flags & PS_COREDUMP))
return (0);
pledge = READ_ONCE(p->p_p->ps_pledge);
if (ni->ni_pledge == 0)
panic("pledge_namei: ni_pledge");
/*
* We set the BYPASSUNVEIL flag to skip unveil checks
* as necessary
*/
/* Doing a permitted execve() */
if ((ni->ni_pledge & PLEDGE_EXEC) &&
(pledge & PLEDGE_EXEC))
return (0);
error = canonpath(origpath, path, sizeof(path));
if (error)
return (error);
/* Detect what looks like a mkstemp(3) family operation */
if ((pledge & PLEDGE_TMPPATH) &&
(p->p_pledge_syscall == SYS_open) &&
(ni->ni_pledge & PLEDGE_CPATH) &&
strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
/* Allow unlinking of a mkstemp(3) file...
* Good opportunity for strict checks here.
*/
if ((pledge & PLEDGE_TMPPATH) &&
(p->p_pledge_syscall == SYS_unlink) &&
strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
/* Whitelisted paths */
switch (p->p_pledge_syscall) {
case SYS_access:
/* tzset() needs this. */
if (ni->ni_pledge == PLEDGE_RPATH &&
strcmp(path, "/etc/localtime") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
break;
case SYS_open:
/* daemon(3) or other such functions */
if ((ni->ni_pledge & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0 &&
strcmp(path, "/dev/null") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
/* readpassphrase(3), getpass(3) */
if ((pledge & PLEDGE_TTY) &&
(ni->ni_pledge & ~(PLEDGE_RPATH | PLEDGE_WPATH)) == 0 &&
strcmp(path, "/dev/tty") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
/* getpw* and friends need a few files */
if ((ni->ni_pledge == PLEDGE_RPATH) &&
(pledge & PLEDGE_GETPW)) {
if (strcmp(path, "/etc/spwd.db") == 0)
return (EPERM); /* don't call pledge_fail */
if (strcmp(path, "/etc/pwd.db") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if (strcmp(path, "/etc/group") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if (strcmp(path, "/etc/netid") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
}
/* DNS needs /etc/{resolv.conf,hosts,services,protocols}. */
if ((ni->ni_pledge == PLEDGE_RPATH) &&
(pledge & PLEDGE_DNS)) {
if (strcmp(path, "/etc/resolv.conf") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if (strcmp(path, "/etc/hosts") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if (strcmp(path, "/etc/services") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if (strcmp(path, "/etc/protocols") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
}
/* tzset() needs these. */
if ((ni->ni_pledge == PLEDGE_RPATH) &&
strncmp(path, "/usr/share/zoneinfo/",
sizeof("/usr/share/zoneinfo/") - 1) == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if ((ni->ni_pledge == PLEDGE_RPATH) &&
strcmp(path, "/etc/localtime") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
break;
case SYS_stat:
/* DNS needs /etc/{resolv.conf,hosts}. */
if ((ni->ni_pledge == PLEDGE_RPATH) &&
(pledge & PLEDGE_DNS)) {
if (strcmp(path, "/etc/resolv.conf") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
if (strcmp(path, "/etc/hosts") == 0) {
ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
return (0);
}
}
break;
}
/*
* Ensure each flag of ni_pledge has counterpart allowing it in
* ps_pledge.
*/
if (ni->ni_pledge & ~pledge)
return (pledge_fail(p, EPERM, (ni->ni_pledge & ~pledge)));
/* continue, and check unveil if present */
return (0);
}
/*
* Only allow reception of safe file descriptors.
*/
int
pledge_recvfd(struct proc *p, struct file *fp)
{
struct vnode *vp;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_p->ps_pledge & PLEDGE_RECVFD) == 0)
return pledge_fail(p, EPERM, PLEDGE_RECVFD);
switch (fp->f_type) {
case DTYPE_SOCKET:
case DTYPE_PIPE:
case DTYPE_DMABUF:
case DTYPE_SYNC:
return (0);
case DTYPE_VNODE:
vp = fp->f_data;
if (vp->v_type != VDIR)
return (0);
}
return pledge_fail(p, EINVAL, PLEDGE_RECVFD);
}
/*
* Only allow sending of safe file descriptors.
*/
int
pledge_sendfd(struct proc *p, struct file *fp)
{
struct vnode *vp;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_p->ps_pledge & PLEDGE_SENDFD) == 0)
return pledge_fail(p, EPERM, PLEDGE_SENDFD);
switch (fp->f_type) {
case DTYPE_SOCKET:
case DTYPE_PIPE:
case DTYPE_DMABUF:
case DTYPE_SYNC:
return (0);
case DTYPE_VNODE:
vp = fp->f_data;
if (vp->v_type != VDIR)
return (0);
break;
}
return pledge_fail(p, EINVAL, PLEDGE_SENDFD);
}
int
pledge_sysctl(struct proc *p, int miblen, int *mib, void *new)
{
char buf[80];
uint64_t pledge;
int i;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
pledge = READ_ONCE(p->p_p->ps_pledge);
if (new)
return pledge_fail(p, EFAULT, 0);
/* routing table observation */
if ((pledge & PLEDGE_ROUTE)) {
if ((miblen == 6 || miblen == 7) &&
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 &&
mib[4] == NET_RT_DUMP)
return (0);
if (miblen == 6 &&
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 &&
(mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
(mib[4] == NET_RT_TABLE || mib[4] == NET_RT_SOURCE))
return (0);
if (miblen == 7 && /* exposes MACs */
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 &&
(mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
mib[4] == NET_RT_FLAGS && mib[5] == RTF_LLINFO)
return (0);
}
if ((pledge & PLEDGE_WROUTE)) {
if (miblen == 4 &&
mib[0] == CTL_NET && mib[1] == PF_INET6 &&
mib[2] == IPPROTO_IPV6 && mib[3] == IPV6CTL_SOIIKEY)
return (0);
}
if (pledge & (PLEDGE_PS | PLEDGE_VMINFO)) {
if (miblen == 2 && /* kern.fscale */
mib[0] == CTL_KERN && mib[1] == KERN_FSCALE)
return (0);
if (miblen == 2 && /* kern.boottime */
mib[0] == CTL_KERN && mib[1] == KERN_BOOTTIME)
return (0);
if (miblen == 2 && /* kern.consdev */
mib[0] == CTL_KERN && mib[1] == KERN_CONSDEV)
return (0);
if (miblen == 2 && /* kern.cptime */
mib[0] == CTL_KERN && mib[1] == KERN_CPTIME)
return (0);
if (miblen == 3 && /* kern.cptime2 */
mib[0] == CTL_KERN && mib[1] == KERN_CPTIME2)
return (0);
if (miblen == 3 && /* kern.cpustats */
mib[0] == CTL_KERN && mib[1] == KERN_CPUSTATS)
return (0);
}
if ((pledge & PLEDGE_PS)) {
if (miblen == 4 && /* kern.procargs.* */
mib[0] == CTL_KERN && mib[1] == KERN_PROC_ARGS &&
(mib[3] == KERN_PROC_ARGV || mib[3] == KERN_PROC_ENV))
return (0);
if (miblen == 6 && /* kern.proc.* */
mib[0] == CTL_KERN && mib[1] == KERN_PROC)
return (0);
if (miblen == 3 && /* kern.proc_cwd.* */
mib[0] == CTL_KERN && mib[1] == KERN_PROC_CWD)
return (0);
if (miblen == 2 && /* kern.ccpu */
mib[0] == CTL_KERN && mib[1] == KERN_CCPU)
return (0);
if (miblen == 2 && /* vm.maxslp */
mib[0] == CTL_VM && mib[1] == VM_MAXSLP)
return (0);
}
if ((pledge & PLEDGE_VMINFO)) {
if (miblen == 2 && /* vm.uvmexp */
mib[0] == CTL_VM && mib[1] == VM_UVMEXP)
return (0);
if (miblen == 3 && /* vfs.generic.bcachestat */
mib[0] == CTL_VFS && mib[1] == VFS_GENERIC &&
mib[2] == VFS_BCACHESTAT)
return (0);
if (miblen == 3 && /* for sysconf(3) */
mib[0] == CTL_NET && mib[1] == PF_INET6)
return (0);
}
if ((pledge & (PLEDGE_INET | PLEDGE_UNIX))) {
if (miblen == 2 && /* kern.somaxconn */
mib[0] == CTL_KERN && mib[1] == KERN_SOMAXCONN)
return (0);
}
if ((pledge & (PLEDGE_ROUTE | PLEDGE_INET | PLEDGE_DNS))) {
if (miblen == 6 && /* getifaddrs() */
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 &&
(mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) &&
mib[4] == NET_RT_IFLIST)
return (0);
}
if ((pledge & PLEDGE_DISKLABEL)) {
if (miblen == 2 && /* kern.rawpartition */
mib[0] == CTL_KERN &&
mib[1] == KERN_RAWPARTITION)
return (0);
if (miblen == 2 && /* kern.maxpartitions */
mib[0] == CTL_KERN &&
mib[1] == KERN_MAXPARTITIONS)
return (0);
#ifdef CPU_CHR2BLK
if (miblen == 3 && /* machdep.chr2blk */
mib[0] == CTL_MACHDEP &&
mib[1] == CPU_CHR2BLK)
return (0);
#endif /* CPU_CHR2BLK */
}
if (miblen >= 3 && /* ntpd(8) to read sensors */
mib[0] == CTL_HW && mib[1] == HW_SENSORS)
return (0);
if (miblen == 6 && /* if_nameindex() */
mib[0] == CTL_NET && mib[1] == PF_ROUTE &&
mib[2] == 0 && mib[3] == 0 && mib[4] == NET_RT_IFNAMES)
return (0);
if (miblen == 2) {
switch (mib[0]) {
case CTL_KERN:
switch (mib[1]) {
case KERN_DOMAINNAME: /* getdomainname() */
case KERN_HOSTNAME: /* gethostname() */
case KERN_OSTYPE: /* uname() */
case KERN_OSRELEASE: /* uname() */
case KERN_OSVERSION: /* uname() */
case KERN_VERSION: /* uname() */
case KERN_CLOCKRATE: /* kern.clockrate */
case KERN_ARGMAX: /* kern.argmax */
case KERN_NGROUPS: /* kern.ngroups */
case KERN_SYSVSHM: /* kern.sysvshm */
case KERN_POSIX1: /* kern.posix1version */
return (0);
}
break;
case CTL_HW:
switch (mib[1]) {
case HW_MACHINE: /* uname() */
case HW_PAGESIZE: /* getpagesize() */
case HW_PHYSMEM64: /* hw.physmem */
case HW_NCPU: /* hw.ncpu */
case HW_NCPUONLINE: /* hw.ncpuonline */
case HW_USERMEM64: /* hw.usermem */
return (0);
}
break;
case CTL_VM:
switch (mib[1]) {
case VM_PSSTRINGS: /* setproctitle() */
case VM_LOADAVG: /* vm.loadavg / getloadavg(3) */
case VM_MALLOC_CONF: /* vm.malloc_conf */
return (0);
}
break;
default:
break;
}
}
#ifdef CPU_SSE
if (miblen == 2 && /* i386 libm tests for SSE */
mib[0] == CTL_MACHDEP && mib[1] == CPU_SSE)
return (0);
#endif /* CPU_SSE */
#ifdef CPU_ID_AA64ISAR0
if (miblen == 2 && /* arm64 libcrypto inspects CPU features */
mib[0] == CTL_MACHDEP && mib[1] == CPU_ID_AA64ISAR0)
return (0);
#endif /* CPU_ID_AA64ISAR0 */
snprintf(buf, sizeof(buf), "%s(%d): pledge sysctl %d:",
p->p_p->ps_comm, p->p_p->ps_pid, miblen);
for (i = 0; i < miblen; i++) {
char *p = buf + strlen(buf);
snprintf(p, sizeof(buf) - (p - buf), " %d", mib[i]);
}
log(LOG_ERR, "%s\n", buf);
return pledge_fail(p, EINVAL, 0);
}
int
pledge_chown(struct proc *p, uid_t uid, gid_t gid)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if (p->p_p->ps_pledge & PLEDGE_CHOWNUID)
return (0);
if (uid != -1 && uid != p->p_ucred->cr_uid)
return (EPERM);
if (gid != -1 && !groupmember(gid, p->p_ucred))
return (EPERM);
return (0);
}
int
pledge_adjtime(struct proc *p, const void *v)
{
const struct timeval *delta = v;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_p->ps_pledge & PLEDGE_SETTIME))
return (0);
if (delta)
return (EPERM);
return (0);
}
int
pledge_sendit(struct proc *p, const void *to)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS)))
return (0); /* may use address */
if (to == NULL)
return (0); /* behaves just like write */
return pledge_fail(p, EPERM, PLEDGE_INET);
}
int
pledge_ioctl(struct proc *p, long com, struct file *fp)
{
struct vnode *vp = NULL;
int error = EPERM;
uint64_t pledge;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
pledge = READ_ONCE(p->p_p->ps_pledge);
/*
* The ioctl's which are always allowed.
*/
switch (com) {
case FIONREAD:
case FIONBIO:
case FIOCLEX:
case FIONCLEX:
return (0);
}
/* fp != NULL was already checked */
if (fp->f_type == DTYPE_VNODE) {
vp = fp->f_data;
if (vp->v_type == VBAD)
return (ENOTTY);
}
if ((pledge & PLEDGE_INET)) {
switch (com) {
case SIOCATMARK:
case SIOCGIFGROUP:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
}
}
#if NBPFILTER > 0
if ((pledge & PLEDGE_BPF)) {
switch (com) {
case BIOCGSTATS: /* bpf: tcpdump privsep on ^C */
if (fp->f_type == DTYPE_VNODE &&
fp->f_ops->fo_ioctl == vn_ioctl &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == bpfopen)
return (0);
break;
}
}
#endif /* NBPFILTER > 0 */
if ((pledge & PLEDGE_TAPE)) {
switch (com) {
case MTIOCGET:
case MTIOCTOP:
/* for pax(1) and such, checking tapes... */
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR) {
if (vp->v_flag & VISTTY)
return (ENOTTY);
else
return (0);
}
break;
}
}
#if NDRM > 0
if ((pledge & PLEDGE_DRM)) {
if ((fp->f_type == DTYPE_VNODE) &&
(vp->v_type == VCHR) &&
(cdevsw[major(vp->v_rdev)].d_open == drmopen)) {
error = pledge_ioctl_drm(p, com, vp->v_rdev);
if (error == 0)
return 0;
}
}
#endif /* NDRM > 0 */
#if NAUDIO > 0
if ((pledge & PLEDGE_AUDIO)) {
switch (com) {
case AUDIO_GETPOS:
case AUDIO_GETPAR:
case AUDIO_SETPAR:
case AUDIO_START:
case AUDIO_STOP:
case AUDIO_MIXER_DEVINFO:
case AUDIO_MIXER_READ:
case AUDIO_MIXER_WRITE:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == audioopen)
return (0);
}
}
#endif /* NAUDIO > 0 */
if ((pledge & PLEDGE_DISKLABEL)) {
switch (com) {
case DIOCGDINFO:
case DIOCGPDINFO:
case DIOCRLDINFO:
case DIOCWDINFO:
case BIOCDISK:
case BIOCINQ:
case BIOCINSTALLBOOT:
case BIOCVOL:
if (fp->f_type == DTYPE_VNODE &&
((vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_type == D_DISK) ||
(vp->v_type == VBLK &&
bdevsw[major(vp->v_rdev)].d_type == D_DISK)))
return (0);
break;
case DIOCMAP:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_ioctl == diskmapioctl)
return (0);
break;
}
}
#if NVIDEO > 0
if ((pledge & PLEDGE_VIDEO)) {
switch (com) {
case VIDIOC_QUERYCAP:
case VIDIOC_TRY_FMT:
case VIDIOC_ENUM_FMT:
case VIDIOC_S_FMT:
case VIDIOC_QUERYCTRL:
case VIDIOC_G_CTRL:
case VIDIOC_S_CTRL:
case VIDIOC_G_PARM:
case VIDIOC_S_PARM:
case VIDIOC_REQBUFS:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
case VIDIOC_QUERYBUF:
case VIDIOC_STREAMON:
case VIDIOC_STREAMOFF:
case VIDIOC_ENUM_FRAMESIZES:
case VIDIOC_ENUM_FRAMEINTERVALS:
case VIDIOC_DQEVENT:
case VIDIOC_ENCODER_CMD:
case VIDIOC_EXPBUF:
case VIDIOC_G_CROP:
case VIDIOC_G_EXT_CTRLS:
case VIDIOC_G_FMT:
case VIDIOC_G_SELECTION:
case VIDIOC_QUERYMENU:
case VIDIOC_SUBSCRIBE_EVENT:
case VIDIOC_S_EXT_CTRLS:
case VIDIOC_S_SELECTION:
case VIDIOC_TRY_DECODER_CMD:
case VIDIOC_TRY_ENCODER_CMD:
if (fp->f_type == DTYPE_VNODE &&
vp->v_type == VCHR &&
cdevsw[major(vp->v_rdev)].d_open == videoopen)
return (0);
break;
}
}
#endif
#if NPF > 0
if ((pledge & PLEDGE_PF)) {
switch (com) {
case DIOCADDRULE:
case DIOCGETSTATUS:
case DIOCNATLOOK:
case DIOCRADDTABLES:
case DIOCRCLRADDRS:
case DIOCRCLRTABLES:
case DIOCRCLRTSTATS:
case DIOCRGETTSTATS:
case DIOCRSETADDRS:
case DIOCXBEGIN:
case DIOCXCOMMIT:
case DIOCKILLSRCNODES:
if ((fp->f_type == DTYPE_VNODE) &&
(vp->v_type == VCHR) &&
(cdevsw[major(vp->v_rdev)].d_open == pfopen))
return (0);
break;
}
}
#endif
if ((pledge & PLEDGE_TTY)) {
switch (com) {
#if NPTY > 0
case PTMGET:
if ((pledge & PLEDGE_RPATH) == 0)
break;
if ((pledge & PLEDGE_WPATH) == 0)
break;
if (fp->f_type != DTYPE_VNODE || vp->v_type != VCHR)
break;
if (cdevsw[major(vp->v_rdev)].d_open != ptmopen)
break;
return (0);
case TIOCUCNTL: /* vmd */
if ((pledge & PLEDGE_RPATH) == 0)
break;
if ((pledge & PLEDGE_WPATH) == 0)
break;
if (cdevsw[major(vp->v_rdev)].d_open != ptcopen)
break;
return (0);
#endif /* NPTY > 0 */
case TIOCSPGRP:
if ((pledge & PLEDGE_PROC) == 0)
break;
/* FALLTHROUGH */
case TIOCFLUSH: /* getty, telnet */
case TIOCSTART: /* emacs, etc */
case TIOCGPGRP:
case TIOCGETA:
case TIOCGWINSZ: /* ENOTTY return for non-tty */
case TIOCSTAT: /* csh */
if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
return (0);
return (ENOTTY);
case TIOCSWINSZ:
case TIOCEXT: /* mail, libedit .. */
case TIOCCBRK: /* cu */
case TIOCSBRK: /* cu */
case TIOCCDTR: /* cu */
case TIOCSDTR: /* cu */
case TIOCEXCL: /* cu */
case TIOCSETA: /* cu, ... */
case TIOCSETAW: /* cu, ... */
case TIOCSETAF: /* tcsetattr TCSAFLUSH, script */
case TIOCSCTTY: /* forkpty(3), login_tty(3), ... */
if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
return (0);
break;
}
}
if ((pledge & PLEDGE_ROUTE)) {
switch (com) {
case SIOCGIFADDR:
case SIOCGIFAFLAG_IN6:
case SIOCGIFALIFETIME_IN6:
case SIOCGIFDESCR:
case SIOCGIFFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFGMEMB:
case SIOCGIFRDOMAIN:
case SIOCGIFDSTADDR_IN6:
case SIOCGIFNETMASK_IN6:
case SIOCGIFXFLAGS:
case SIOCGNBRINFO_IN6:
case SIOCGIFINFO_IN6:
case SIOCGIFMEDIA:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
}
}
if ((pledge & PLEDGE_WROUTE)) {
switch (com) {
case SIOCAIFADDR:
case SIOCDIFADDR:
case SIOCAIFADDR_IN6:
case SIOCDIFADDR_IN6:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
case SIOCSIFMTU:
if (fp->f_type == DTYPE_SOCKET)
return (0);
break;
}
}
#if NVMM > 0
if ((pledge & PLEDGE_VMM)) {
if ((fp->f_type == DTYPE_VNODE) &&
(vp->v_type == VCHR) &&
(cdevsw[major(vp->v_rdev)].d_open == vmmopen)) {
error = pledge_ioctl_vmm(p, com);
if (error == 0)
return 0;
}
}
#endif
return pledge_fail(p, error, PLEDGE_TTY);
}
int
pledge_sockopt(struct proc *p, int set, int level, int optname)
{
uint64_t pledge;
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
pledge = READ_ONCE(p->p_p->ps_pledge);
/* Always allow these, which are too common to reject */
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_RCVBUF:
case SO_ERROR:
return (0);
}
break;
}
if ((pledge & PLEDGE_WROUTE)) {
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_RTABLE:
return (0);
}
}
}
if ((pledge & (PLEDGE_INET|PLEDGE_UNIX|PLEDGE_DNS)) == 0)
return pledge_fail(p, EPERM, PLEDGE_INET);
/* In use by some service libraries */
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_TIMESTAMP:
return (0);
}
break;
}
/* DNS resolver may do these requests */
if ((pledge & PLEDGE_DNS)) {
switch (level) {
case IPPROTO_IPV6:
switch (optname) {
case IPV6_RECVPKTINFO:
case IPV6_USE_MIN_MTU:
return (0);
}
}
}
if ((pledge & (PLEDGE_INET|PLEDGE_UNIX)) == 0)
return pledge_fail(p, EPERM, PLEDGE_INET);
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_RTABLE:
return pledge_fail(p, EINVAL, PLEDGE_WROUTE);
}
return (0);
}
if ((pledge & PLEDGE_INET) == 0)
return pledge_fail(p, EPERM, PLEDGE_INET);
switch (level) {
case IPPROTO_TCP:
switch (optname) {
case TCP_NODELAY:
case TCP_MD5SIG:
case TCP_SACK_ENABLE:
case TCP_MAXSEG:
case TCP_NOPUSH:
case TCP_INFO:
return (0);
}
break;
case IPPROTO_IP:
switch (optname) {
case IP_OPTIONS:
if (!set)
return (0);
break;
case IP_TOS:
case IP_TTL:
case IP_MINTTL:
case IP_IPDEFTTL:
case IP_PORTRANGE:
case IP_RECVDSTADDR:
case IP_RECVDSTPORT:
return (0);
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
if (pledge & PLEDGE_MCAST)
return (0);
break;
}
break;
case IPPROTO_ICMP:
break;
case IPPROTO_IPV6:
switch (optname) {
case IPV6_TCLASS:
case IPV6_UNICAST_HOPS:
case IPV6_MINHOPCOUNT:
case IPV6_RECVHOPLIMIT:
case IPV6_PORTRANGE:
case IPV6_RECVPKTINFO:
case IPV6_RECVDSTPORT:
case IPV6_V6ONLY:
return (0);
case IPV6_MULTICAST_IF:
case IPV6_MULTICAST_HOPS:
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
if (pledge & PLEDGE_MCAST)
return (0);
break;
}
break;
case IPPROTO_ICMPV6:
break;
}
return pledge_fail(p, EPERM, PLEDGE_INET);
}
int
pledge_socket(struct proc *p, int domain, unsigned int state)
{
uint64_t pledge;
if (!ISSET(p->p_p->ps_flags, PS_PLEDGE))
return 0;
pledge = READ_ONCE(p->p_p->ps_pledge);
if (ISSET(state, SS_DNS)) {
if (ISSET(pledge, PLEDGE_DNS))
return 0;
return pledge_fail(p, EPERM, PLEDGE_DNS);
}
switch (domain) {
case -1: /* accept on any domain */
return (0);
case AF_INET:
case AF_INET6:
if (ISSET(pledge, PLEDGE_INET))
return 0;
return pledge_fail(p, EPERM, PLEDGE_INET);
case AF_UNIX:
if (ISSET(pledge, PLEDGE_UNIX))
return 0;
return pledge_fail(p, EPERM, PLEDGE_UNIX);
}
return pledge_fail(p, EINVAL, PLEDGE_INET);
}
int
pledge_flock(struct proc *p)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_p->ps_pledge & PLEDGE_FLOCK))
return (0);
return (pledge_fail(p, EPERM, PLEDGE_FLOCK));
}
int
pledge_swapctl(struct proc *p, int cmd)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if (p->p_p->ps_pledge & PLEDGE_VMINFO) {
switch (cmd) {
case SWAP_NSWAP:
case SWAP_STATS:
return (0);
}
}
return pledge_fail(p, EPERM, PLEDGE_VMINFO);
}
/* bsearch over pledgereq. return flags value if found, 0 else */
uint64_t
pledgereq_flags(const char *req_name)
{
int base = 0, cmp, i, lim;
for (lim = nitems(pledgereq); lim != 0; lim >>= 1) {
i = base + (lim >> 1);
cmp = strcmp(req_name, pledgereq[i].name);
if (cmp == 0)
return (pledgereq[i].flags);
if (cmp > 0) { /* not found before, move right */
base = i + 1;
lim--;
} /* else move left */
}
return (0);
}
int
pledge_fcntl(struct proc *p, int cmd)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return (0);
if ((p->p_p->ps_pledge & PLEDGE_PROC) == 0 && cmd == F_SETOWN)
return pledge_fail(p, EPERM, PLEDGE_PROC);
return (0);
}
int
pledge_kill(struct proc *p, pid_t pid)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return 0;
if (p->p_p->ps_pledge & PLEDGE_PROC)
return 0;
if (pid == 0 || pid == p->p_p->ps_pid)
return 0;
return pledge_fail(p, EPERM, PLEDGE_PROC);
}
int
pledge_protexec(struct proc *p, int prot)
{
if ((p->p_p->ps_flags & PS_PLEDGE) == 0)
return 0;
/* Before kbind(2) call, ld.so and crt may create EXEC mappings */
if (p->p_p->ps_kbind_addr == 0 && p->p_p->ps_kbind_cookie == 0)
return 0;
if (!(p->p_p->ps_pledge & PLEDGE_PROTEXEC) && (prot & PROT_EXEC))
return pledge_fail(p, EPERM, PLEDGE_PROTEXEC);
return 0;
}
int
canonpath(const char *input, char *buf, size_t bufsize)
{
const char *p;
char *q;
/* can't canon relative paths, don't bother */
if (input[0] != '/') {
if (strlcpy(buf, input, bufsize) >= bufsize)
return ENAMETOOLONG;
return 0;
}
p = input;
q = buf;
while (*p && (q - buf < bufsize)) {
if (p[0] == '/' && (p[1] == '/' || p[1] == '\0')) {
p += 1;
} else if (p[0] == '/' && p[1] == '.' &&
(p[2] == '/' || p[2] == '\0')) {
p += 2;
} else if (p[0] == '/' && p[1] == '.' && p[2] == '.' &&
(p[3] == '/' || p[3] == '\0')) {
p += 3;
if (q != buf) /* "/../" at start of buf */
while (*--q != '/')
continue;
} else {
*q++ = *p++;
}
}
if ((*p == '\0') && (q - buf < bufsize)) {
*q = 0;
return 0;
} else
return ENAMETOOLONG;
}
9
9
9
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/* $OpenBSD: vm_machdep.c,v 1.46 2022/08/07 23:56:06 guenther Exp $ */
/* $NetBSD: vm_machdep.c,v 1.1 2003/04/26 18:39:33 fvdl Exp $ */
/*-
* Copyright (c) 1995 Charles M. Hannum. All rights reserved.
* Copyright (c) 1982, 1986 The Regents of the University of California.
* Copyright (c) 1989, 1990 William Jolitz
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department, and William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91
*/
/*
* Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/user.h>
#include <uvm/uvm_extern.h>
#include <machine/cpu.h>
#include <machine/fpu.h>
void setguardpage(struct proc *);
/*
* Finish a fork operation, with process p2 nearly set up.
* Copy and update the kernel stack and pcb, making the child
* ready to run, and marking it so that it can return differently
* than the parent.
*/
void
cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb,
void (*func)(void *), void *arg)
{
struct cpu_info *ci = curcpu();
struct pcb *pcb = &p2->p_addr->u_pcb;
struct pcb *pcb1 = &p1->p_addr->u_pcb;
struct trapframe *tf;
struct switchframe *sf;
/* Save the fpu h/w state to p1's pcb so that we can copy it. */
if (p1 != &proc0 && (ci->ci_pflags & CPUPF_USERXSTATE))
fpusave(&pcb1->pcb_savefpu);
p2->p_md.md_flags = p1->p_md.md_flags;
#ifdef DIAGNOSTIC
if (p1 != curproc && p1 != &proc0)
panic("cpu_fork: curproc");
#endif
*pcb = *pcb1;
/*
* Activate the address space.
*/
pmap_activate(p2);
/* Record where this process's kernel stack is */
pcb->pcb_kstack = (u_int64_t)p2->p_addr + USPACE - 16 -
(arc4random() & PAGE_MASK & ~_STACKALIGNBYTES);
/*
* Copy the trapframe.
*/
p2->p_md.md_regs = tf = (struct trapframe *)pcb->pcb_kstack - 1;
*tf = *p1->p_md.md_regs;
setguardpage(p2);
/*
* If specified, give the child a different stack and/or TCB
*/
if (stack != NULL)
tf->tf_rsp = (u_int64_t)stack;
if (tcb != NULL)
pcb->pcb_fsbase = (u_int64_t)tcb;
sf = (struct switchframe *)tf - 1;
sf->sf_r12 = (u_int64_t)func;
sf->sf_r13 = (u_int64_t)arg;
sf->sf_rip = (u_int64_t)proc_trampoline;
pcb->pcb_rsp = (u_int64_t)sf;
pcb->pcb_rbp = 0;
}
/*
* cpu_exit is called as the last action during exit.
*
* We clean up a little and then call sched_exit() with the old proc as an
* argument.
*/
void
cpu_exit(struct proc *p)
{
pmap_deactivate(p);
sched_exit(p);
}
/*
* Set a red zone in the kernel stack after the u. area.
*/
void
setguardpage(struct proc *p)
{
pmap_remove(pmap_kernel(), (vaddr_t)p->p_addr + PAGE_SIZE,
(vaddr_t)p->p_addr + 2 * PAGE_SIZE);
pmap_update(pmap_kernel());
}
struct kmem_va_mode kv_physwait = {
.kv_map = &phys_map,
.kv_wait = 1,
};
/*
* Map a user I/O request into kernel virtual address space.
* Note: the pages are already locked by uvm_vslock(), so we
* do not need to pass an access_type to pmap_enter().
*/
void
vmapbuf(struct buf *bp, vsize_t len)
{
vaddr_t faddr, taddr, off;
paddr_t fpa;
if ((bp->b_flags & B_PHYS) == 0)
panic("vmapbuf");
faddr = trunc_page((vaddr_t)(bp->b_saveaddr = bp->b_data));
off = (vaddr_t)bp->b_data - faddr;
len = round_page(off + len);
taddr = (vaddr_t)km_alloc(len, &kv_physwait, &kp_none, &kd_waitok);
bp->b_data = (caddr_t)(taddr + off);
/*
* The region is locked, so we expect that pmap_pte() will return
* non-NULL.
* XXX: unwise to expect this in a multithreaded environment.
* anything can happen to a pmap between the time we lock a
* region, release the pmap lock, and then relock it for
* the pmap_extract().
*
* no need to flush TLB since we expect nothing to be mapped
* where we we just allocated (TLB will be flushed when our
* mapping is removed).
*/
while (len) {
(void) pmap_extract(vm_map_pmap(&bp->b_proc->p_vmspace->vm_map),
faddr, &fpa);
pmap_kenter_pa(taddr, fpa, PROT_READ | PROT_WRITE);
faddr += PAGE_SIZE;
taddr += PAGE_SIZE;
len -= PAGE_SIZE;
}
pmap_update(pmap_kernel());
}
/*
* Unmap a previously-mapped user I/O request.
*/
void
vunmapbuf(struct buf *bp, vsize_t len)
{
vaddr_t addr, off;
if ((bp->b_flags & B_PHYS) == 0)
panic("vunmapbuf");
addr = trunc_page((vaddr_t)bp->b_data);
off = (vaddr_t)bp->b_data - addr;
len = round_page(off + len);
pmap_kremove(addr, len);
pmap_update(pmap_kernel());
km_free((void *)addr, len, &kv_physwait, &kp_none);
bp->b_data = bp->b_saveaddr;
bp->b_saveaddr = NULL;
}
void *
tcb_get(struct proc *p)
{
return ((void *)p->p_addr->u_pcb.pcb_fsbase);
}
void
tcb_set(struct proc *p, void *tcb)
{
KASSERT(p == curproc);
reset_segs();
p->p_addr->u_pcb.pcb_fsbase = (u_int64_t)tcb;
}
2
3
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
/*
* Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
*
* Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Author Rickard E. (Rik) Faith <faith@valinux.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/specdev.h>
#include <sys/vnode.h>
#include <machine/bus.h>
#ifdef __HAVE_ACPI
#include <dev/acpi/acpidev.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/dsdt.h>
#endif
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mount.h>
#include <linux/pseudo_fs.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include <drm/drm_cache.h>
#include <drm/drm_client.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_managed.h>
#include <drm/drm_mode_object.h>
#include <drm/drm_print.h>
#include <drm/drm_gem.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
#include "drm_legacy.h"
MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
MODULE_DESCRIPTION("DRM shared core routines");
MODULE_LICENSE("GPL and additional rights");
static DEFINE_SPINLOCK(drm_minor_lock);
static struct idr drm_minors_idr;
/*
* If the drm core fails to init for whatever reason,
* we should prevent any drivers from registering with it.
* It's best to check this at drm_dev_init(), as some drivers
* prefer to embed struct drm_device into their own device
* structure and call drm_dev_init() themselves.
*/
static bool drm_core_init_complete;
static struct dentry *drm_debugfs_root;
#ifdef notyet
DEFINE_STATIC_SRCU(drm_unplug_srcu);
#endif
/*
* Some functions are only called once on init regardless of how many times
* drm attaches. In linux this is handled via module_init()/module_exit()
*/
int drm_refcnt;
struct drm_softc {
struct device sc_dev;
struct drm_device *sc_drm;
int sc_allocated;
};
struct drm_attach_args {
struct drm_device *drm;
const struct drm_driver *driver;
char *busid;
bus_dma_tag_t dmat;
bus_space_tag_t bst;
size_t busid_len;
int is_agp;
struct pci_attach_args *pa;
int primary;
};
void drm_linux_init(void);
void drm_linux_exit(void);
int drm_linux_acpi_notify(struct aml_node *, int, void *);
int drm_dequeue_event(struct drm_device *, struct drm_file *, size_t,
struct drm_pending_event **);
int drmprint(void *, const char *);
int drmsubmatch(struct device *, void *, void *);
const struct pci_device_id *
drm_find_description(int, int, const struct pci_device_id *);
int drm_file_cmp(struct drm_file *, struct drm_file *);
SPLAY_PROTOTYPE(drm_file_tree, drm_file, link, drm_file_cmp);
#define DRMDEVCF_PRIMARY 0
#define drmdevcf_primary cf_loc[DRMDEVCF_PRIMARY] /* spec'd as primary? */
#define DRMDEVCF_PRIMARY_UNK -1
/*
* DRM Minors
* A DRM device can provide several char-dev interfaces on the DRM-Major. Each
* of them is represented by a drm_minor object. Depending on the capabilities
* of the device-driver, different interfaces are registered.
*
* Minors can be accessed via dev->$minor_name. This pointer is either
* NULL or a valid drm_minor pointer and stays valid as long as the device is
* valid. This means, DRM minors have the same life-time as the underlying
* device. However, this doesn't mean that the minor is active. Minors are
* registered and unregistered dynamically according to device-state.
*/
static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
unsigned int type)
{
switch (type) {
case DRM_MINOR_PRIMARY:
return &dev->primary;
case DRM_MINOR_RENDER:
return &dev->render;
default:
BUG();
}
}
static void drm_minor_alloc_release(struct drm_device *dev, void *data)
{
struct drm_minor *minor = data;
unsigned long flags;
WARN_ON(dev != minor->dev);
#ifdef __linux__
put_device(minor->kdev);
#endif
spin_lock_irqsave(&drm_minor_lock, flags);
idr_remove(&drm_minors_idr, minor->index);
spin_unlock_irqrestore(&drm_minor_lock, flags);
}
static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
{
struct drm_minor *minor;
unsigned long flags;
int r;
minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
if (!minor)
return -ENOMEM;
minor->type = type;
minor->dev = dev;
idr_preload(GFP_KERNEL);
spin_lock_irqsave(&drm_minor_lock, flags);
r = idr_alloc(&drm_minors_idr,
NULL,
64 * type,
64 * (type + 1),
GFP_NOWAIT);
spin_unlock_irqrestore(&drm_minor_lock, flags);
idr_preload_end();
if (r < 0)
return r;
minor->index = r;
r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
if (r)
return r;
#ifdef __linux__
minor->kdev = drm_sysfs_minor_alloc(minor);
if (IS_ERR(minor->kdev))
return PTR_ERR(minor->kdev);
#endif
*drm_minor_get_slot(dev, type) = minor;
return 0;
}
static int drm_minor_register(struct drm_device *dev, unsigned int type)
{
struct drm_minor *minor;
unsigned long flags;
#ifdef __linux__
int ret;
#endif
DRM_DEBUG("\n");
minor = *drm_minor_get_slot(dev, type);
if (!minor)
return 0;
#ifdef __linux__
ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
if (ret) {
DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
goto err_debugfs;
}
ret = device_add(minor->kdev);
if (ret)
goto err_debugfs;
#else
drm_debugfs_root = NULL;
#endif
/* replace NULL with @minor so lookups will succeed from now on */
spin_lock_irqsave(&drm_minor_lock, flags);
idr_replace(&drm_minors_idr, minor, minor->index);
spin_unlock_irqrestore(&drm_minor_lock, flags);
DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
#ifdef __linux__
err_debugfs:
drm_debugfs_cleanup(minor);
return ret;
#endif
}
static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
{
struct drm_minor *minor;
unsigned long flags;
minor = *drm_minor_get_slot(dev, type);
#ifdef __linux__
if (!minor || !device_is_registered(minor->kdev))
#else
if (!minor)
#endif
return;
/* replace @minor with NULL so lookups will fail from now on */
spin_lock_irqsave(&drm_minor_lock, flags);
idr_replace(&drm_minors_idr, NULL, minor->index);
spin_unlock_irqrestore(&drm_minor_lock, flags);
#ifdef __linux__
device_del(minor->kdev);
#endif
dev_set_drvdata(minor->kdev, NULL); /* safety belt */
drm_debugfs_cleanup(minor);
}
/*
* Looks up the given minor-ID and returns the respective DRM-minor object. The
* refence-count of the underlying device is increased so you must release this
* object with drm_minor_release().
*
* As long as you hold this minor, it is guaranteed that the object and the
* minor->dev pointer will stay valid! However, the device may get unplugged and
* unregistered while you hold the minor.
*/
struct drm_minor *drm_minor_acquire(unsigned int minor_id)
{
struct drm_minor *minor;
unsigned long flags;
spin_lock_irqsave(&drm_minor_lock, flags);
minor = idr_find(&drm_minors_idr, minor_id);
if (minor)
drm_dev_get(minor->dev);
spin_unlock_irqrestore(&drm_minor_lock, flags);
if (!minor) {
return ERR_PTR(-ENODEV);
} else if (drm_dev_is_unplugged(minor->dev)) {
drm_dev_put(minor->dev);
return ERR_PTR(-ENODEV);
}
return minor;
}
void drm_minor_release(struct drm_minor *minor)
{
drm_dev_put(minor->dev);
}
/**
* DOC: driver instance overview
*
* A device instance for a drm driver is represented by &struct drm_device. This
* is allocated and initialized with devm_drm_dev_alloc(), usually from
* bus-specific ->probe() callbacks implemented by the driver. The driver then
* needs to initialize all the various subsystems for the drm device like memory
* management, vblank handling, modesetting support and initial output
* configuration plus obviously initialize all the corresponding hardware bits.
* Finally when everything is up and running and ready for userspace the device
* instance can be published using drm_dev_register().
*
* There is also deprecated support for initializing device instances using
* bus-specific helpers and the &drm_driver.load callback. But due to
* backwards-compatibility needs the device instance have to be published too
* early, which requires unpretty global locking to make safe and is therefore
* only support for existing drivers not yet converted to the new scheme.
*
* When cleaning up a device instance everything needs to be done in reverse:
* First unpublish the device instance with drm_dev_unregister(). Then clean up
* any other resources allocated at device initialization and drop the driver's
* reference to &drm_device using drm_dev_put().
*
* Note that any allocation or resource which is visible to userspace must be
* released only when the final drm_dev_put() is called, and not when the
* driver is unbound from the underlying physical struct &device. Best to use
* &drm_device managed resources with drmm_add_action(), drmm_kmalloc() and
* related functions.
*
* devres managed resources like devm_kmalloc() can only be used for resources
* directly related to the underlying hardware device, and only used in code
* paths fully protected by drm_dev_enter() and drm_dev_exit().
*
* Display driver example
* ~~~~~~~~~~~~~~~~~~~~~~
*
* The following example shows a typical structure of a DRM display driver.
* The example focus on the probe() function and the other functions that is
* almost always present and serves as a demonstration of devm_drm_dev_alloc().
*
* .. code-block:: c
*
* struct driver_device {
* struct drm_device drm;
* void *userspace_facing;
* struct clk *pclk;
* };
*
* static const struct drm_driver driver_drm_driver = {
* [...]
* };
*
* static int driver_probe(struct platform_device *pdev)
* {
* struct driver_device *priv;
* struct drm_device *drm;
* int ret;
*
* priv = devm_drm_dev_alloc(&pdev->dev, &driver_drm_driver,
* struct driver_device, drm);
* if (IS_ERR(priv))
* return PTR_ERR(priv);
* drm = &priv->drm;
*
* ret = drmm_mode_config_init(drm);
* if (ret)
* return ret;
*
* priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL);
* if (!priv->userspace_facing)
* return -ENOMEM;
*
* priv->pclk = devm_clk_get(dev, "PCLK");
* if (IS_ERR(priv->pclk))
* return PTR_ERR(priv->pclk);
*
* // Further setup, display pipeline etc
*
* platform_set_drvdata(pdev, drm);
*
* drm_mode_config_reset(drm);
*
* ret = drm_dev_register(drm);
* if (ret)
* return ret;
*
* drm_fbdev_generic_setup(drm, 32);
*
* return 0;
* }
*
* // This function is called before the devm_ resources are released
* static int driver_remove(struct platform_device *pdev)
* {
* struct drm_device *drm = platform_get_drvdata(pdev);
*
* drm_dev_unregister(drm);
* drm_atomic_helper_shutdown(drm)
*
* return 0;
* }
*
* // This function is called on kernel restart and shutdown
* static void driver_shutdown(struct platform_device *pdev)
* {
* drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
* }
*
* static int __maybe_unused driver_pm_suspend(struct device *dev)
* {
* return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
* }
*
* static int __maybe_unused driver_pm_resume(struct device *dev)
* {
* drm_mode_config_helper_resume(dev_get_drvdata(dev));
*
* return 0;
* }
*
* static const struct dev_pm_ops driver_pm_ops = {
* SET_SYSTEM_SLEEP_PM_OPS(driver_pm_suspend, driver_pm_resume)
* };
*
* static struct platform_driver driver_driver = {
* .driver = {
* [...]
* .pm = &driver_pm_ops,
* },
* .probe = driver_probe,
* .remove = driver_remove,
* .shutdown = driver_shutdown,
* };
* module_platform_driver(driver_driver);
*
* Drivers that want to support device unplugging (USB, DT overlay unload) should
* use drm_dev_unplug() instead of drm_dev_unregister(). The driver must protect
* regions that is accessing device resources to prevent use after they're
* released. This is done using drm_dev_enter() and drm_dev_exit(). There is one
* shortcoming however, drm_dev_unplug() marks the drm_device as unplugged before
* drm_atomic_helper_shutdown() is called. This means that if the disable code
* paths are protected, they will not run on regular driver module unload,
* possibly leaving the hardware enabled.
*/
/**
* drm_put_dev - Unregister and release a DRM device
* @dev: DRM device
*
* Called at module unload time or when a PCI device is unplugged.
*
* Cleans up all DRM device, calling drm_lastclose().
*
* Note: Use of this function is deprecated. It will eventually go away
* completely. Please use drm_dev_unregister() and drm_dev_put() explicitly
* instead to make sure that the device isn't userspace accessible any more
* while teardown is in progress, ensuring that userspace can't access an
* inconsistent state.
*/
void drm_put_dev(struct drm_device *dev)
{
DRM_DEBUG("\n");
if (!dev) {
DRM_ERROR("cleanup called no dev\n");
return;
}
drm_dev_unregister(dev);
drm_dev_put(dev);
}
EXPORT_SYMBOL(drm_put_dev);
/**
* drm_dev_enter - Enter device critical section
* @dev: DRM device
* @idx: Pointer to index that will be passed to the matching drm_dev_exit()
*
* This function marks and protects the beginning of a section that should not
* be entered after the device has been unplugged. The section end is marked
* with drm_dev_exit(). Calls to this function can be nested.
*
* Returns:
* True if it is OK to enter the section, false otherwise.
*/
bool drm_dev_enter(struct drm_device *dev, int *idx)
{
#ifdef notyet
*idx = srcu_read_lock(&drm_unplug_srcu);
if (dev->unplugged) {
srcu_read_unlock(&drm_unplug_srcu, *idx);
return false;
}
#endif
return true;
}
EXPORT_SYMBOL(drm_dev_enter);
/**
* drm_dev_exit - Exit device critical section
* @idx: index returned from drm_dev_enter()
*
* This function marks the end of a section that should not be entered after
* the device has been unplugged.
*/
void drm_dev_exit(int idx)
{
#ifdef notyet
srcu_read_unlock(&drm_unplug_srcu, idx);
#endif
}
EXPORT_SYMBOL(drm_dev_exit);
/**
* drm_dev_unplug - unplug a DRM device
* @dev: DRM device
*
* This unplugs a hotpluggable DRM device, which makes it inaccessible to
* userspace operations. Entry-points can use drm_dev_enter() and
* drm_dev_exit() to protect device resources in a race free manner. This
* essentially unregisters the device like drm_dev_unregister(), but can be
* called while there are still open users of @dev.
*/
void drm_dev_unplug(struct drm_device *dev)
{
STUB();
#ifdef notyet
/*
* After synchronizing any critical read section is guaranteed to see
* the new value of ->unplugged, and any critical section which might
* still have seen the old value of ->unplugged is guaranteed to have
* finished.
*/
dev->unplugged = true;
synchronize_srcu(&drm_unplug_srcu);
drm_dev_unregister(dev);
/* Clear all CPU mappings pointing to this device */
unmap_mapping_range(dev->anon_inode->i_mapping, 0, 0, 1);
#endif
}
EXPORT_SYMBOL(drm_dev_unplug);
#ifdef __linux__
/*
* DRM internal mount
* We want to be able to allocate our own "struct address_space" to control
* memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
* stand-alone address_space objects, so we need an underlying inode. As there
* is no way to allocate an independent inode easily, we need a fake internal
* VFS mount-point.
*
* The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
* frees it again. You are allowed to use iget() and iput() to get references to
* the inode. But each drm_fs_inode_new() call must be paired with exactly one
* drm_fs_inode_free() call (which does not have to be the last iput()).
* We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
* between multiple inode-users. You could, technically, call
* iget() + drm_fs_inode_free() directly after alloc and sometime later do an
* iput(), but this way you'd end up with a new vfsmount for each inode.
*/
static int drm_fs_cnt;
static struct vfsmount *drm_fs_mnt;
static int drm_fs_init_fs_context(struct fs_context *fc)
{
return init_pseudo(fc, 0x010203ff) ? 0 : -ENOMEM;
}
static struct file_system_type drm_fs_type = {
.name = "drm",
.owner = THIS_MODULE,
.init_fs_context = drm_fs_init_fs_context,
.kill_sb = kill_anon_super,
};
static struct inode *drm_fs_inode_new(void)
{
struct inode *inode;
int r;
r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
if (r < 0) {
DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
return ERR_PTR(r);
}
inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
if (IS_ERR(inode))
simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
return inode;
}
static void drm_fs_inode_free(struct inode *inode)
{
if (inode) {
iput(inode);
simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
}
}
#endif /* __linux__ */
/**
* DOC: component helper usage recommendations
*
* DRM drivers that drive hardware where a logical device consists of a pile of
* independent hardware blocks are recommended to use the :ref:`component helper
* library<component>`. For consistency and better options for code reuse the
* following guidelines apply:
*
* - The entire device initialization procedure should be run from the
* &component_master_ops.master_bind callback, starting with
* devm_drm_dev_alloc(), then binding all components with
* component_bind_all() and finishing with drm_dev_register().
*
* - The opaque pointer passed to all components through component_bind_all()
* should point at &struct drm_device of the device instance, not some driver
* specific private structure.
*
* - The component helper fills the niche where further standardization of
* interfaces is not practical. When there already is, or will be, a
* standardized interface like &drm_bridge or &drm_panel, providing its own
* functions to find such components at driver load time, like
* drm_of_find_panel_or_bridge(), then the component helper should not be
* used.
*/
static void drm_dev_init_release(struct drm_device *dev, void *res)
{
drm_legacy_ctxbitmap_cleanup(dev);
drm_legacy_remove_map_hash(dev);
#ifdef __linux__
drm_fs_inode_free(dev->anon_inode);
put_device(dev->dev);
#endif
/* Prevent use-after-free in drm_managed_release when debugging is
* enabled. Slightly awkward, but can't really be helped. */
dev->dev = NULL;
mutex_destroy(&dev->master_mutex);
mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex);
drm_legacy_destroy_members(dev);
}
#ifdef notyet
static int drm_dev_init(struct drm_device *dev,
const struct drm_driver *driver,
struct device *parent)
{
struct inode *inode;
int ret;
if (!drm_core_init_complete) {
DRM_ERROR("DRM core is not initialized\n");
return -ENODEV;
}
if (WARN_ON(!parent))
return -EINVAL;
kref_init(&dev->ref);
dev->dev = get_device(parent);
dev->driver = driver;
INIT_LIST_HEAD(&dev->managed.resources);
spin_lock_init(&dev->managed.lock);
/* no per-device feature limits by default */
dev->driver_features = ~0u;
drm_legacy_init_members(dev);
INIT_LIST_HEAD(&dev->filelist);
INIT_LIST_HEAD(&dev->filelist_internal);
INIT_LIST_HEAD(&dev->clientlist);
INIT_LIST_HEAD(&dev->vblank_event_list);
spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->filelist_mutex);
mutex_init(&dev->clientlist_mutex);
mutex_init(&dev->master_mutex);
ret = drmm_add_action(dev, drm_dev_init_release, NULL);
if (ret)
return ret;
inode = drm_fs_inode_new();
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
goto err;
}
dev->anon_inode = inode;
if (drm_core_check_feature(dev, DRIVER_RENDER)) {
ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
if (ret)
goto err;
}
ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
if (ret)
goto err;
ret = drm_legacy_create_map_hash(dev);
if (ret)
goto err;
drm_legacy_ctxbitmap_init(dev);
if (drm_core_check_feature(dev, DRIVER_GEM)) {
ret = drm_gem_init(dev);
if (ret) {
DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
goto err;
}
}
ret = drm_dev_set_unique(dev, dev_name(parent));
if (ret)
goto err;
return 0;
err:
drm_managed_release(dev);
return ret;
}
static void devm_drm_dev_init_release(void *data)
{
drm_dev_put(data);
}
static int devm_drm_dev_init(struct device *parent,
struct drm_device *dev,
const struct drm_driver *driver)
{
int ret;
ret = drm_dev_init(dev, driver, parent);
if (ret)
return ret;
return devm_add_action_or_reset(parent,
devm_drm_dev_init_release, dev);
}
void *__devm_drm_dev_alloc(struct device *parent,
const struct drm_driver *driver,
size_t size, size_t offset)
{
void *container;
struct drm_device *drm;
int ret;
container = kzalloc(size, GFP_KERNEL);
if (!container)
return ERR_PTR(-ENOMEM);
drm = container + offset;
ret = devm_drm_dev_init(parent, drm, driver);
if (ret) {
kfree(container);
return ERR_PTR(ret);
}
drmm_add_final_kfree(drm, container);
return container;
}
EXPORT_SYMBOL(__devm_drm_dev_alloc);
/**
* drm_dev_alloc - Allocate new DRM device
* @driver: DRM driver to allocate device for
* @parent: Parent device object
*
* This is the deprecated version of devm_drm_dev_alloc(), which does not support
* subclassing through embedding the struct &drm_device in a driver private
* structure, and which does not support automatic cleanup through devres.
*
* RETURNS:
* Pointer to new DRM device, or ERR_PTR on failure.
*/
struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
struct device *parent)
{
struct drm_device *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
ret = drm_dev_init(dev, driver, parent);
if (ret) {
kfree(dev);
return ERR_PTR(ret);
}
drmm_add_final_kfree(dev, dev);
return dev;
}
EXPORT_SYMBOL(drm_dev_alloc);
#endif
static void drm_dev_release(struct kref *ref)
{
struct drm_device *dev = container_of(ref, struct drm_device, ref);
if (dev->driver->release)
dev->driver->release(dev);
drm_managed_release(dev);
kfree(dev->managed.final_kfree);
}
/**
* drm_dev_get - Take reference of a DRM device
* @dev: device to take reference of or NULL
*
* This increases the ref-count of @dev by one. You *must* already own a
* reference when calling this. Use drm_dev_put() to drop this reference
* again.
*
* This function never fails. However, this function does not provide *any*
* guarantee whether the device is alive or running. It only provides a
* reference to the object and the memory associated with it.
*/
void drm_dev_get(struct drm_device *dev)
{
if (dev)
kref_get(&dev->ref);
}
EXPORT_SYMBOL(drm_dev_get);
/**
* drm_dev_put - Drop reference of a DRM device
* @dev: device to drop reference of or NULL
*
* This decreases the ref-count of @dev by one. The device is destroyed if the
* ref-count drops to zero.
*/
void drm_dev_put(struct drm_device *dev)
{
if (dev)
kref_put(&dev->ref, drm_dev_release);
}
EXPORT_SYMBOL(drm_dev_put);
static int create_compat_control_link(struct drm_device *dev)
{
struct drm_minor *minor;
char *name;
int ret;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return 0;
minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY);
if (!minor)
return 0;
/*
* Some existing userspace out there uses the existing of the controlD*
* sysfs files to figure out whether it's a modeset driver. It only does
* readdir, hence a symlink is sufficient (and the least confusing
* option). Otherwise controlD* is entirely unused.
*
* Old controlD chardev have been allocated in the range
* 64-127.
*/
name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64);
if (!name)
return -ENOMEM;
ret = sysfs_create_link(minor->kdev->kobj.parent,
&minor->kdev->kobj,
name);
kfree(name);
return ret;
}
static void remove_compat_control_link(struct drm_device *dev)
{
struct drm_minor *minor;
char *name;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return;
minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY);
if (!minor)
return;
name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64);
if (!name)
return;
sysfs_remove_link(minor->kdev->kobj.parent, name);
kfree(name);
}
/**
* drm_dev_register - Register DRM device
* @dev: Device to register
* @flags: Flags passed to the driver's .load() function
*
* Register the DRM device @dev with the system, advertise device to user-space
* and start normal device operation. @dev must be initialized via drm_dev_init()
* previously.
*
* Never call this twice on any device!
*
* NOTE: To ensure backward compatibility with existing drivers method this
* function calls the &drm_driver.load method after registering the device
* nodes, creating race conditions. Usage of the &drm_driver.load methods is
* therefore deprecated, drivers must perform all initialization before calling
* drm_dev_register().
*
* RETURNS:
* 0 on success, negative error code on failure.
*/
int drm_dev_register(struct drm_device *dev, unsigned long flags)
{
const struct drm_driver *driver = dev->driver;
int ret;
if (!driver->load)
drm_mode_config_validate(dev);
WARN_ON(!dev->managed.final_kfree);
if (drm_dev_needs_global_mutex(dev))
mutex_lock(&drm_global_mutex);
ret = drm_minor_register(dev, DRM_MINOR_RENDER);
if (ret)
goto err_minors;
ret = drm_minor_register(dev, DRM_MINOR_PRIMARY);
if (ret)
goto err_minors;
ret = create_compat_control_link(dev);
if (ret)
goto err_minors;
dev->registered = true;
if (dev->driver->load) {
ret = dev->driver->load(dev, flags);
if (ret)
goto err_minors;
}
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_register_all(dev);
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver->name, driver->major, driver->minor,
driver->patchlevel, driver->date,
dev->dev ? dev_name(dev->dev) : "virtual device",
dev->primary->index);
goto out_unlock;
err_minors:
remove_compat_control_link(dev);
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
drm_minor_unregister(dev, DRM_MINOR_RENDER);
out_unlock:
if (drm_dev_needs_global_mutex(dev))
mutex_unlock(&drm_global_mutex);
return ret;
}
EXPORT_SYMBOL(drm_dev_register);
/**
* drm_dev_unregister - Unregister DRM device
* @dev: Device to unregister
*
* Unregister the DRM device from the system. This does the reverse of
* drm_dev_register() but does not deallocate the device. The caller must call
* drm_dev_put() to drop their final reference.
*
* A special form of unregistering for hotpluggable devices is drm_dev_unplug(),
* which can be called while there are still open users of @dev.
*
* This should be called first in the device teardown code to make sure
* userspace can't access the device instance any more.
*/
void drm_dev_unregister(struct drm_device *dev)
{
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_lastclose(dev);
dev->registered = false;
drm_client_dev_unregister(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_unregister_all(dev);
if (dev->driver->unload)
dev->driver->unload(dev);
drm_legacy_pci_agp_destroy(dev);
drm_legacy_rmmaps(dev);
remove_compat_control_link(dev);
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
drm_minor_unregister(dev, DRM_MINOR_RENDER);
}
EXPORT_SYMBOL(drm_dev_unregister);
/**
* drm_dev_set_unique - Set the unique name of a DRM device
* @dev: device of which to set the unique name
* @name: unique name
*
* Sets the unique name of a DRM device using the specified string. This is
* already done by drm_dev_init(), drivers should only override the default
* unique name for backwards compatibility reasons.
*
* Return: 0 on success or a negative error code on failure.
*/
int drm_dev_set_unique(struct drm_device *dev, const char *name)
{
drmm_kfree(dev, dev->unique);
dev->unique = drmm_kstrdup(dev, name, GFP_KERNEL);
return dev->unique ? 0 : -ENOMEM;
}
EXPORT_SYMBOL(drm_dev_set_unique);
/*
* DRM Core
* The DRM core module initializes all global DRM objects and makes them
* available to drivers. Once setup, drivers can probe their respective
* devices.
* Currently, core management includes:
* - The "DRM-Global" key/value database
* - Global ID management for connectors
* - DRM major number allocation
* - DRM minor management
* - DRM sysfs class
* - DRM debugfs root
*
* Furthermore, the DRM core provides dynamic char-dev lookups. For each
* interface registered on a DRM device, you can request minor numbers from DRM
* core. DRM core takes care of major-number management and char-dev
* registration. A stub ->open() callback forwards any open() requests to the
* registered minor.
*/
#ifdef __linux__
static int drm_stub_open(struct inode *inode, struct file *filp)
{
const struct file_operations *new_fops;
struct drm_minor *minor;
int err;
DRM_DEBUG("\n");
minor = drm_minor_acquire(iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
new_fops = fops_get(minor->dev->driver->fops);
if (!new_fops) {
err = -ENODEV;
goto out;
}
replace_fops(filp, new_fops);
if (filp->f_op->open)
err = filp->f_op->open(inode, filp);
else
err = 0;
out:
drm_minor_release(minor);
return err;
}
static const struct file_operations drm_stub_fops = {
.owner = THIS_MODULE,
.open = drm_stub_open,
.llseek = noop_llseek,
};
#endif /* __linux__ */
static void drm_core_exit(void)
{
#ifdef __linux__
unregister_chrdev(DRM_MAJOR, "drm");
debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
#endif
idr_destroy(&drm_minors_idr);
drm_connector_ida_destroy();
}
static int __init drm_core_init(void)
{
#ifdef __linux__
int ret;
#endif
drm_connector_ida_init();
idr_init(&drm_minors_idr);
drm_memcpy_init_early();
#ifdef __linux__
ret = drm_sysfs_init();
if (ret < 0) {
DRM_ERROR("Cannot create DRM class: %d\n", ret);
goto error;
}
drm_debugfs_root = debugfs_create_dir("dri", NULL);
ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
if (ret < 0)
goto error;
#endif
drm_core_init_complete = true;
DRM_DEBUG("Initialized\n");
return 0;
#ifdef __linux__
error:
drm_core_exit();
return ret;
#endif
}
#ifdef __linux__
module_init(drm_core_init);
module_exit(drm_core_exit);
#endif
void
drm_attach_platform(struct drm_driver *driver, bus_space_tag_t iot,
bus_dma_tag_t dmat, struct device *dev, struct drm_device *drm)
{
struct drm_attach_args arg;
memset(&arg, 0, sizeof(arg));
arg.driver = driver;
arg.bst = iot;
arg.dmat = dmat;
arg.drm = drm;
arg.busid = dev->dv_xname;
arg.busid_len = strlen(dev->dv_xname) + 1;
config_found_sm(dev, &arg, drmprint, drmsubmatch);
}
struct drm_device *
drm_attach_pci(const struct drm_driver *driver, struct pci_attach_args *pa,
int is_agp, int primary, struct device *dev, struct drm_device *drm)
{
struct drm_attach_args arg;
struct drm_softc *sc;
arg.drm = drm;
arg.driver = driver;
arg.dmat = pa->pa_dmat;
arg.bst = pa->pa_memt;
arg.is_agp = is_agp;
arg.primary = primary;
arg.pa = pa;
arg.busid_len = 20;
arg.busid = malloc(arg.busid_len + 1, M_DRM, M_NOWAIT);
if (arg.busid == NULL) {
printf("%s: no memory for drm\n", dev->dv_xname);
return (NULL);
}
snprintf(arg.busid, arg.busid_len, "pci:%04x:%02x:%02x.%1x",
pa->pa_domain, pa->pa_bus, pa->pa_device, pa->pa_function);
sc = (struct drm_softc *)config_found_sm(dev, &arg, drmprint, drmsubmatch);
if (sc == NULL)
return NULL;
return sc->sc_drm;
}
int
drmprint(void *aux, const char *pnp)
{
if (pnp != NULL)
printf("drm at %s", pnp);
return (UNCONF);
}
int
drmsubmatch(struct device *parent, void *match, void *aux)
{
extern struct cfdriver drm_cd;
struct cfdata *cf = match;
/* only allow drm to attach */
if (cf->cf_driver == &drm_cd)
return ((*cf->cf_attach->ca_match)(parent, match, aux));
return (0);
}
int
drm_pciprobe(struct pci_attach_args *pa, const struct pci_device_id *idlist)
{
const struct pci_device_id *id_entry;
id_entry = drm_find_description(PCI_VENDOR(pa->pa_id),
PCI_PRODUCT(pa->pa_id), idlist);
if (id_entry != NULL)
return 1;
return 0;
}
int
drm_probe(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct drm_attach_args *da = aux;
if (cf->drmdevcf_primary != DRMDEVCF_PRIMARY_UNK) {
/*
* If primary-ness of device specified, either match
* exactly (at high priority), or fail.
*/
if (cf->drmdevcf_primary != 0 && da->primary != 0)
return (10);
else
return (0);
}
/* If primary-ness unspecified, it wins. */
return (1);
}
void
drm_attach(struct device *parent, struct device *self, void *aux)
{
struct drm_softc *sc = (struct drm_softc *)self;
struct drm_attach_args *da = aux;
struct drm_device *dev = da->drm;
int ret;
if (drm_refcnt == 0) {
drm_linux_init();
drm_core_init();
}
drm_refcnt++;
if (dev == NULL) {
dev = malloc(sizeof(struct drm_device), M_DRM,
M_WAITOK | M_ZERO);
sc->sc_allocated = 1;
}
sc->sc_drm = dev;
dev->dev = self;
dev->dev_private = parent;
dev->driver = da->driver;
INIT_LIST_HEAD(&dev->managed.resources);
mtx_init(&dev->managed.lock, IPL_TTY);
/* no per-device feature limits by default */
dev->driver_features = ~0u;
dev->dmat = da->dmat;
dev->bst = da->bst;
dev->unique = da->busid;
if (da->pa) {
struct pci_attach_args *pa = da->pa;
pcireg_t subsys;
subsys = pci_conf_read(pa->pa_pc, pa->pa_tag,
PCI_SUBSYS_ID_REG);
dev->pdev = &dev->_pdev;
dev->pdev->vendor = PCI_VENDOR(pa->pa_id);
dev->pdev->device = PCI_PRODUCT(pa->pa_id);
dev->pdev->subsystem_vendor = PCI_VENDOR(subsys);
dev->pdev->subsystem_device = PCI_PRODUCT(subsys);
dev->pdev->revision = PCI_REVISION(pa->pa_class);
dev->pdev->class = (PCI_CLASS(pa->pa_class) << 16) |
(PCI_SUBCLASS(pa->pa_class) << 8) |
PCI_INTERFACE(pa->pa_class);
dev->pdev->devfn = PCI_DEVFN(pa->pa_device, pa->pa_function);
dev->pdev->bus = &dev->pdev->_bus;
dev->pdev->bus->pc = pa->pa_pc;
dev->pdev->bus->number = pa->pa_bus;
dev->pdev->bus->domain_nr = pa->pa_domain;
dev->pdev->bus->bridgetag = pa->pa_bridgetag;
if (pa->pa_bridgetag != NULL) {
dev->pdev->bus->self = malloc(sizeof(struct pci_dev),
M_DRM, M_WAITOK | M_ZERO);
dev->pdev->bus->self->pc = pa->pa_pc;
dev->pdev->bus->self->tag = *pa->pa_bridgetag;
}
dev->pdev->pc = pa->pa_pc;
dev->pdev->tag = pa->pa_tag;
dev->pdev->pci = (struct pci_softc *)parent->dv_parent;
#ifdef CONFIG_ACPI
dev->pdev->dev.node = acpi_find_pci(pa->pa_pc, pa->pa_tag);
aml_register_notify(dev->pdev->dev.node, NULL,
drm_linux_acpi_notify, NULL, ACPIDEV_NOPOLL);
#endif
}
mtx_init(&dev->quiesce_mtx, IPL_NONE);
mtx_init(&dev->event_lock, IPL_TTY);
rw_init(&dev->struct_mutex, "drmdevlk");
rw_init(&dev->filelist_mutex, "drmflist");
rw_init(&dev->clientlist_mutex, "drmclist");
rw_init(&dev->master_mutex, "drmmast");
ret = drmm_add_action(dev, drm_dev_init_release, NULL);
if (ret)
goto error;
SPLAY_INIT(&dev->files);
INIT_LIST_HEAD(&dev->filelist_internal);
INIT_LIST_HEAD(&dev->clientlist);
INIT_LIST_HEAD(&dev->vblank_event_list);
if (drm_core_check_feature(dev, DRIVER_RENDER)) {
ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
if (ret)
goto error;
}
ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
if (ret)
goto error;
#ifdef CONFIG_DRM_LEGACY
if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
#if IS_ENABLED(CONFIG_AGP)
if (da->is_agp)
dev->agp = drm_agp_init();
#endif
if (dev->agp != NULL) {
if (drm_mtrr_add(dev->agp->info.ai_aperture_base,
dev->agp->info.ai_aperture_size, DRM_MTRR_WC) == 0)
dev->agp->mtrr = 1;
}
}
#endif
if (dev->driver->gem_size > 0) {
KASSERT(dev->driver->gem_size >= sizeof(struct drm_gem_object));
/* XXX unique name */
pool_init(&dev->objpl, dev->driver->gem_size, 0, IPL_NONE, 0,
"drmobjpl", NULL);
}
if (drm_core_check_feature(dev, DRIVER_GEM)) {
ret = drm_gem_init(dev);
if (ret) {
DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
goto error;
}
}
drmm_add_final_kfree(dev, dev);
printf("\n");
return;
error:
drm_managed_release(dev);
dev->dev_private = NULL;
}
int
drm_detach(struct device *self, int flags)
{
struct drm_softc *sc = (struct drm_softc *)self;
struct drm_device *dev = sc->sc_drm;
drm_refcnt--;
if (drm_refcnt == 0) {
drm_core_exit();
drm_linux_exit();
}
drm_lastclose(dev);
if (drm_core_check_feature(dev, DRIVER_GEM)) {
if (dev->driver->gem_size > 0)
pool_destroy(&dev->objpl);
}
#ifdef CONFIG_DRM_LEGACY
if (dev->agp && dev->agp->mtrr) {
int retcode;
retcode = drm_mtrr_del(0, dev->agp->info.ai_aperture_base,
dev->agp->info.ai_aperture_size, DRM_MTRR_WC);
DRM_DEBUG("mtrr_del = %d", retcode);
}
free(dev->agp, M_DRM, 0);
#endif
if (dev->pdev && dev->pdev->bus)
free(dev->pdev->bus->self, M_DRM, sizeof(struct pci_dev));
if (sc->sc_allocated)
free(dev, M_DRM, sizeof(struct drm_device));
return 0;
}
void
drm_quiesce(struct drm_device *dev)
{
mtx_enter(&dev->quiesce_mtx);
dev->quiesce = 1;
while (dev->quiesce_count > 0) {
msleep_nsec(&dev->quiesce_count, &dev->quiesce_mtx,
PZERO, "drmqui", INFSLP);
}
mtx_leave(&dev->quiesce_mtx);
}
void
drm_wakeup(struct drm_device *dev)
{
mtx_enter(&dev->quiesce_mtx);
dev->quiesce = 0;
wakeup(&dev->quiesce);
mtx_leave(&dev->quiesce_mtx);
}
int
drm_activate(struct device *self, int act)
{
struct drm_softc *sc = (struct drm_softc *)self;
struct drm_device *dev = sc->sc_drm;
switch (act) {
case DVACT_QUIESCE:
drm_quiesce(dev);
break;
case DVACT_WAKEUP:
drm_wakeup(dev);
break;
}
return (0);
}
const struct cfattach drm_ca = {
sizeof(struct drm_softc), drm_probe, drm_attach,
drm_detach, drm_activate
};
struct cfdriver drm_cd = {
0, "drm", DV_DULL
};
const struct pci_device_id *
drm_find_description(int vendor, int device, const struct pci_device_id *idlist)
{
int i = 0;
for (i = 0; idlist[i].vendor != 0; i++) {
if ((idlist[i].vendor == vendor) &&
(idlist[i].device == device) &&
(idlist[i].subvendor == PCI_ANY_ID) &&
(idlist[i].subdevice == PCI_ANY_ID))
return &idlist[i];
}
return NULL;
}
int
drm_file_cmp(struct drm_file *f1, struct drm_file *f2)
{
return (f1->fminor < f2->fminor ? -1 : f1->fminor > f2->fminor);
}
SPLAY_GENERATE(drm_file_tree, drm_file, link, drm_file_cmp);
struct drm_file *
drm_find_file_by_minor(struct drm_device *dev, int minor)
{
struct drm_file key;
key.fminor = minor;
return (SPLAY_FIND(drm_file_tree, &dev->files, &key));
}
struct drm_device *
drm_get_device_from_kdev(dev_t kdev)
{
int unit = minor(kdev) & ((1 << CLONE_SHIFT) - 1);
/* control */
if (unit >= 64 && unit < 128)
unit -= 64;
/* render */
if (unit >= 128)
unit -= 128;
struct drm_softc *sc;
if (unit < drm_cd.cd_ndevs) {
sc = (struct drm_softc *)drm_cd.cd_devs[unit];
if (sc)
return sc->sc_drm;
}
return NULL;
}
void
filt_drmdetach(struct knote *kn)
{
struct drm_device *dev = kn->kn_hook;
int s;
s = spltty();
klist_remove_locked(&dev->note, kn);
splx(s);
}
int
filt_drmkms(struct knote *kn, long hint)
{
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
return (kn->kn_fflags != 0);
}
void
filt_drmreaddetach(struct knote *kn)
{
struct drm_file *file_priv = kn->kn_hook;
int s;
s = spltty();
klist_remove_locked(&file_priv->rsel.si_note, kn);
splx(s);
}
int
filt_drmread(struct knote *kn, long hint)
{
struct drm_file *file_priv = kn->kn_hook;
int val = 0;
if ((hint & NOTE_SUBMIT) == 0)
mtx_enter(&file_priv->minor->dev->event_lock);
val = !list_empty(&file_priv->event_list);
if ((hint & NOTE_SUBMIT) == 0)
mtx_leave(&file_priv->minor->dev->event_lock);
return (val);
}
const struct filterops drm_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_drmdetach,
.f_event = filt_drmkms,
};
const struct filterops drmread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_drmreaddetach,
.f_event = filt_drmread,
};
int
drmkqfilter(dev_t kdev, struct knote *kn)
{
struct drm_device *dev = NULL;
struct drm_file *file_priv = NULL;
int s;
dev = drm_get_device_from_kdev(kdev);
if (dev == NULL || dev->dev_private == NULL)
return (ENXIO);
switch (kn->kn_filter) {
case EVFILT_READ:
mutex_lock(&dev->struct_mutex);
file_priv = drm_find_file_by_minor(dev, minor(kdev));
mutex_unlock(&dev->struct_mutex);
if (file_priv == NULL)
return (ENXIO);
kn->kn_fop = &drmread_filtops;
kn->kn_hook = file_priv;
s = spltty();
klist_insert_locked(&file_priv->rsel.si_note, kn);
splx(s);
break;
case EVFILT_DEVICE:
kn->kn_fop = &drm_filtops;
kn->kn_hook = dev;
s = spltty();
klist_insert_locked(&dev->note, kn);
splx(s);
break;
default:
return (EINVAL);
}
return (0);
}
int
drmopen(dev_t kdev, int flags, int fmt, struct proc *p)
{
struct drm_device *dev = NULL;
struct drm_file *file_priv;
struct drm_minor *dm;
int ret = 0;
int dminor, realminor, minor_type;
int need_setup = 0;
dev = drm_get_device_from_kdev(kdev);
if (dev == NULL || dev->dev_private == NULL)
return (ENXIO);
DRM_DEBUG("open_count = %d\n", atomic_read(&dev->open_count));
if (flags & O_EXCL)
return (EBUSY); /* No exclusive opens */
if (drm_dev_needs_global_mutex(dev))
mutex_lock(&drm_global_mutex);
if (!atomic_fetch_inc(&dev->open_count))
need_setup = 1;
dminor = minor(kdev);
realminor = dminor & ((1 << CLONE_SHIFT) - 1);
if (realminor < 64)
minor_type = DRM_MINOR_PRIMARY;
else if (realminor >= 64 && realminor < 128)
minor_type = DRM_MINOR_CONTROL;
else
minor_type = DRM_MINOR_RENDER;
dm = *drm_minor_get_slot(dev, minor_type);
dm->index = minor(kdev);
file_priv = drm_file_alloc(dm);
if (IS_ERR(file_priv)) {
ret = ENOMEM;
goto err;
}
/* first opener automatically becomes master */
if (drm_is_primary_client(file_priv)) {
ret = drm_master_open(file_priv);
if (ret != 0)
goto out_file_free;
}
file_priv->filp = (void *)file_priv;
file_priv->fminor = minor(kdev);
mutex_lock(&dev->filelist_mutex);
SPLAY_INSERT(drm_file_tree, &dev->files, file_priv);
mutex_unlock(&dev->filelist_mutex);
if (need_setup) {
ret = drm_legacy_setup(dev);
if (ret)
goto out_file_free;
}
if (drm_dev_needs_global_mutex(dev))
mutex_unlock(&drm_global_mutex);
return 0;
out_file_free:
drm_file_free(file_priv);
err:
atomic_dec(&dev->open_count);
if (drm_dev_needs_global_mutex(dev))
mutex_unlock(&drm_global_mutex);
return (ret);
}
int
drmclose(dev_t kdev, int flags, int fmt, struct proc *p)
{
struct drm_device *dev = drm_get_device_from_kdev(kdev);
struct drm_file *file_priv;
int retcode = 0;
if (dev == NULL)
return (ENXIO);
if (drm_dev_needs_global_mutex(dev))
mutex_lock(&drm_global_mutex);
DRM_DEBUG("open_count = %d\n", atomic_read(&dev->open_count));
mutex_lock(&dev->filelist_mutex);
file_priv = drm_find_file_by_minor(dev, minor(kdev));
if (file_priv == NULL) {
DRM_ERROR("can't find authenticator\n");
retcode = EINVAL;
mutex_unlock(&dev->filelist_mutex);
goto done;
}
SPLAY_REMOVE(drm_file_tree, &dev->files, file_priv);
mutex_unlock(&dev->filelist_mutex);
drm_file_free(file_priv);
done:
if (atomic_dec_and_test(&dev->open_count))
drm_lastclose(dev);
if (drm_dev_needs_global_mutex(dev))
mutex_unlock(&drm_global_mutex);
return (retcode);
}
int
drmread(dev_t kdev, struct uio *uio, int ioflag)
{
struct drm_device *dev = drm_get_device_from_kdev(kdev);
struct drm_file *file_priv;
struct drm_pending_event *ev;
int error = 0;
if (dev == NULL)
return (ENXIO);
mutex_lock(&dev->filelist_mutex);
file_priv = drm_find_file_by_minor(dev, minor(kdev));
mutex_unlock(&dev->filelist_mutex);
if (file_priv == NULL)
return (ENXIO);
/*
* The semantics are a little weird here. We will wait until we
* have events to process, but as soon as we have events we will
* only deliver as many as we have.
* Note that events are atomic, if the read buffer will not fit in
* a whole event, we won't read any of it out.
*/
mtx_enter(&dev->event_lock);
while (error == 0 && list_empty(&file_priv->event_list)) {
if (ioflag & IO_NDELAY) {
mtx_leave(&dev->event_lock);
return (EAGAIN);
}
error = msleep_nsec(&file_priv->event_wait, &dev->event_lock,
PWAIT | PCATCH, "drmread", INFSLP);
}
if (error) {
mtx_leave(&dev->event_lock);
return (error);
}
while (drm_dequeue_event(dev, file_priv, uio->uio_resid, &ev)) {
MUTEX_ASSERT_UNLOCKED(&dev->event_lock);
/* XXX we always destroy the event on error. */
error = uiomove(ev->event, ev->event->length, uio);
kfree(ev);
if (error)
break;
mtx_enter(&dev->event_lock);
}
MUTEX_ASSERT_UNLOCKED(&dev->event_lock);
return (error);
}
/*
* Deqeue an event from the file priv in question. returning 1 if an
* event was found. We take the resid from the read as a parameter because
* we will only dequeue and event if the read buffer has space to fit the
* entire thing.
*
* We are called locked, but we will *unlock* the queue on return so that
* we may sleep to copyout the event.
*/
int
drm_dequeue_event(struct drm_device *dev, struct drm_file *file_priv,
size_t resid, struct drm_pending_event **out)
{
struct drm_pending_event *e = NULL;
int gotone = 0;
MUTEX_ASSERT_LOCKED(&dev->event_lock);
*out = NULL;
if (list_empty(&file_priv->event_list))
goto out;
e = list_first_entry(&file_priv->event_list,
struct drm_pending_event, link);
if (e->event->length > resid)
goto out;
file_priv->event_space += e->event->length;
list_del(&e->link);
*out = e;
gotone = 1;
out:
mtx_leave(&dev->event_lock);
return (gotone);
}
paddr_t
drmmmap(dev_t kdev, off_t offset, int prot)
{
return -1;
}
struct drm_dmamem *
drm_dmamem_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t alignment,
int nsegments, bus_size_t maxsegsz, int mapflags, int loadflags)
{
struct drm_dmamem *mem;
size_t strsize;
/*
* segs is the last member of the struct since we modify the size
* to allow extra segments if more than one are allowed.
*/
strsize = sizeof(*mem) + (sizeof(bus_dma_segment_t) * (nsegments - 1));
mem = malloc(strsize, M_DRM, M_NOWAIT | M_ZERO);
if (mem == NULL)
return (NULL);
mem->size = size;
if (bus_dmamap_create(dmat, size, nsegments, maxsegsz, 0,
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &mem->map) != 0)
goto strfree;
if (bus_dmamem_alloc(dmat, size, alignment, 0, mem->segs, nsegments,
&mem->nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
goto destroy;
if (bus_dmamem_map(dmat, mem->segs, mem->nsegs, size,
&mem->kva, BUS_DMA_NOWAIT | mapflags) != 0)
goto free;
if (bus_dmamap_load(dmat, mem->map, mem->kva, size,
NULL, BUS_DMA_NOWAIT | loadflags) != 0)
goto unmap;
return (mem);
unmap:
bus_dmamem_unmap(dmat, mem->kva, size);
free:
bus_dmamem_free(dmat, mem->segs, mem->nsegs);
destroy:
bus_dmamap_destroy(dmat, mem->map);
strfree:
free(mem, M_DRM, 0);
return (NULL);
}
void
drm_dmamem_free(bus_dma_tag_t dmat, struct drm_dmamem *mem)
{
if (mem == NULL)
return;
bus_dmamap_unload(dmat, mem->map);
bus_dmamem_unmap(dmat, mem->kva, mem->size);
bus_dmamem_free(dmat, mem->segs, mem->nsegs);
bus_dmamap_destroy(dmat, mem->map);
free(mem, M_DRM, 0);
}
struct drm_dma_handle *
drm_pci_alloc(struct drm_device *dev, size_t size, size_t align)
{
struct drm_dma_handle *dmah;
dmah = malloc(sizeof(*dmah), M_DRM, M_WAITOK);
dmah->mem = drm_dmamem_alloc(dev->dmat, size, align, 1, size,
BUS_DMA_NOCACHE, 0);
if (dmah->mem == NULL) {
free(dmah, M_DRM, sizeof(*dmah));
return NULL;
}
dmah->busaddr = dmah->mem->segs[0].ds_addr;
dmah->size = dmah->mem->size;
dmah->vaddr = dmah->mem->kva;
return (dmah);
}
void
drm_pci_free(struct drm_device *dev, struct drm_dma_handle *dmah)
{
if (dmah == NULL)
return;
drm_dmamem_free(dev->dmat, dmah->mem);
free(dmah, M_DRM, sizeof(*dmah));
}
/*
* Compute order. Can be made faster.
*/
int
drm_order(unsigned long size)
{
int order;
unsigned long tmp;
for (order = 0, tmp = size; tmp >>= 1; ++order)
;
if (size & ~(1 << order))
++order;
return order;
}
int
drm_getpciinfo(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_pciinfo *info = data;
if (dev->pdev == NULL)
return -ENOTTY;
info->domain = dev->pdev->bus->domain_nr;
info->bus = dev->pdev->bus->number;
info->dev = PCI_SLOT(dev->pdev->devfn);
info->func = PCI_FUNC(dev->pdev->devfn);
info->vendor_id = dev->pdev->vendor;
info->device_id = dev->pdev->device;
info->subvendor_id = dev->pdev->subsystem_vendor;
info->subdevice_id = dev->pdev->subsystem_device;
info->revision_id = 0;
return 0;
}
9015
9021
8928
206
9006
8974
8978
8896
201
1315
226
1147
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* $OpenBSD: syscall_mi.h,v 1.26 2022/06/29 12:06:11 jca Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_xxx.c 8.2 (Berkeley) 11/14/93
*/
#include <sys/param.h>
#include <sys/pledge.h>
#include <sys/tracepoint.h>
#include <uvm/uvm_extern.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include "dt.h"
#if NDT > 0
#include <dev/dt/dtvar.h>
#endif
/*
* The MD setup for a system call has been done; here's the MI part.
*/
static inline int
mi_syscall(struct proc *p, register_t code, const struct sysent *callp,
register_t *argp, register_t retval[2])
{
uint64_t tval;
int lock = !(callp->sy_flags & SY_NOLOCK);
int error, pledged;
/* refresh the thread's cache of the process's creds */
refreshcreds(p);
#ifdef SYSCALL_DEBUG
KERNEL_LOCK();
scdebug_call(p, code, argp);
KERNEL_UNLOCK();
#endif
TRACEPOINT(raw_syscalls, sys_enter, code, NULL);
#if NDT > 0
DT_ENTER(syscall, code, callp->sy_argsize, argp);
#endif
#ifdef KTRACE
if (KTRPOINT(p, KTR_SYSCALL)) {
KERNEL_LOCK();
ktrsyscall(p, code, callp->sy_argsize, argp);
KERNEL_UNLOCK();
}
#endif
/* SP must be within MAP_STACK space */
if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p),
"[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n",
uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial))
return (EPERM);
/* PC must be in un-writeable permitted text (sigtramp, libc, ld.so) */
if (!uvm_map_inentry(p, &p->p_pcinentry, PROC_PC(p),
"[%s]%d/%d pc=%lx inside %lx-%lx: bogus syscall\n",
uvm_map_inentry_pc, p->p_vmspace->vm_map.wserial))
return (EPERM);
pledged = (p->p_p->ps_flags & PS_PLEDGE);
if (pledged && (error = pledge_syscall(p, code, &tval))) {
KERNEL_LOCK();
error = pledge_fail(p, error, tval);
KERNEL_UNLOCK();
return (error);
}
if (lock)
KERNEL_LOCK();
error = (*callp->sy_call)(p, argp, retval);
if (lock)
KERNEL_UNLOCK();
return (error);
}
/*
* Finish MI stuff on return, after the registers have been set
*/
static inline void
mi_syscall_return(struct proc *p, register_t code, int error,
const register_t retval[2])
{
#ifdef SYSCALL_DEBUG
KERNEL_LOCK();
scdebug_ret(p, code, error, retval);
KERNEL_UNLOCK();
#endif
#if NDT > 0
DT_LEAVE(syscall, code, error, retval[0], retval[1]);
#endif
TRACEPOINT(raw_syscalls, sys_exit, code, NULL);
userret(p);
#ifdef KTRACE
if (KTRPOINT(p, KTR_SYSRET)) {
KERNEL_LOCK();
ktrsysret(p, code, error, retval);
KERNEL_UNLOCK();
}
#endif
}
/*
* Finish MI stuff for a new process/thread to return
*/
static inline void
mi_child_return(struct proc *p)
{
#if defined(SYSCALL_DEBUG) || defined(KTRACE) || NDT > 0
int code = (p->p_flag & P_THREAD) ? SYS___tfork :
(p->p_p->ps_flags & PS_PPWAIT) ? SYS_vfork : SYS_fork;
const register_t child_retval[2] = { 0, 1 };
#endif
TRACEPOINT(sched, on__cpu, NULL);
#ifdef SYSCALL_DEBUG
KERNEL_LOCK();
scdebug_ret(p, code, 0, child_retval);
KERNEL_UNLOCK();
#endif
#if NDT > 0
DT_LEAVE(syscall, code, 0, child_retval[0], child_retval[1]);
#endif
TRACEPOINT(raw_syscalls, sys_exit, code, NULL);
userret(p);
#ifdef KTRACE
if (KTRPOINT(p, KTR_SYSRET)) {
KERNEL_LOCK();
ktrsysret(p, code, 0, child_retval);
KERNEL_UNLOCK();
}
#endif
}
/*
* Do the specific processing necessary for an AST
*/
static inline void
mi_ast(struct proc *p, int resched)
{
if (p->p_flag & P_OWEUPC) {
KERNEL_LOCK();
ADDUPROF(p);
KERNEL_UNLOCK();
}
if (resched)
preempt();
/*
* XXX could move call to userret() here, but
* hppa calls ast() in syscall return and sh calls
* it after userret()
*/
}
36
36
35
36
58
58
58
118
3
115
56
60
115
115
115
115
58
220
46
178
4
159
74
74
28
60
9
15
15
36
36
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
/* $OpenBSD: uvm_km.c,v 1.151 2022/08/01 14:15:46 mpi Exp $ */
/* $NetBSD: uvm_km.c,v 1.42 2001/01/14 02:10:01 thorpej Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993, The Regents of the University of California.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* The Mach Operating System project at Carnegie-Mellon University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_kern.c 8.3 (Berkeley) 1/12/94
* from: Id: uvm_km.c,v 1.1.2.14 1998/02/06 05:19:27 chs Exp
*
*
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* All rights reserved.
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* uvm_km.c: handle kernel memory allocation and management
*/
/*
* overview of kernel memory management:
*
* the kernel virtual address space is mapped by "kernel_map." kernel_map
* starts at a machine-dependent address and is VM_KERNEL_SPACE_SIZE bytes
* large.
*
* the kernel_map has several "submaps." submaps can only appear in
* the kernel_map (user processes can't use them). submaps "take over"
* the management of a sub-range of the kernel's address space. submaps
* are typically allocated at boot time and are never released. kernel
* virtual address space that is mapped by a submap is locked by the
* submap's lock -- not the kernel_map's lock.
*
* thus, the useful feature of submaps is that they allow us to break
* up the locking and protection of the kernel address space into smaller
* chunks.
*
* The VM system has several standard kernel submaps:
* kmem_map: Contains only wired kernel memory for malloc(9).
* Note: All access to this map must be protected by splvm as
* calls to malloc(9) are allowed in interrupt handlers.
* exec_map: Memory to hold arguments to system calls are allocated from
* this map.
* XXX: This is primeraly used to artificially limit the number
* of concurrent processes doing an exec.
* phys_map: Buffers for vmapbuf (physio) are allocated from this map.
*
* the kernel allocates its private memory out of special uvm_objects whose
* reference count is set to UVM_OBJ_KERN (thus indicating that the objects
* are "special" and never die). all kernel objects should be thought of
* as large, fixed-sized, sparsely populated uvm_objects. each kernel
* object is equal to the size of kernel virtual address space (i.e.
* VM_KERNEL_SPACE_SIZE).
*
* most kernel private memory lives in kernel_object. the only exception
* to this is for memory that belongs to submaps that must be protected
* by splvm(). each of these submaps manages their own pages.
*
* note that just because a kernel object spans the entire kernel virtual
* address space doesn't mean that it has to be mapped into the entire space.
* large chunks of a kernel object's space go unused either because
* that area of kernel VM is unmapped, or there is some other type of
* object mapped into that range (e.g. a vnode). for submap's kernel
* objects, the only part of the object that can ever be populated is the
* offsets that are managed by the submap.
*
* note that the "offset" in a kernel object is always the kernel virtual
* address minus the vm_map_min(kernel_map).
* example:
* suppose kernel_map starts at 0xf8000000 and the kernel does a
* uvm_km_alloc(kernel_map, PAGE_SIZE) [allocate 1 wired down page in the
* kernel map]. if uvm_km_alloc returns virtual address 0xf8235000,
* then that means that the page at offset 0x235000 in kernel_object is
* mapped at 0xf8235000.
*
* kernel objects have one other special property: when the kernel virtual
* memory mapping them is unmapped, the backing memory in the object is
* freed right away. this is done with the uvm_km_pgremove() function.
* this has to be done because there is no backing store for kernel pages
* and no need to save them after they are no longer referenced.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <uvm/uvm.h>
/*
* global data structures
*/
struct vm_map *kernel_map = NULL;
/* Unconstraint range. */
struct uvm_constraint_range no_constraint = { 0x0, (paddr_t)-1 };
/*
* local data structures
*/
static struct vm_map kernel_map_store;
/*
* uvm_km_init: init kernel maps and objects to reflect reality (i.e.
* KVM already allocated for text, data, bss, and static data structures).
*
* => KVM is defined by [base.. base + VM_KERNEL_SPACE_SIZE].
* we assume that [base -> start] has already been allocated and that
* "end" is the end of the kernel image span.
*/
void
uvm_km_init(vaddr_t base, vaddr_t start, vaddr_t end)
{
/* kernel_object: for pageable anonymous kernel memory */
uao_init();
uvm.kernel_object = uao_create(VM_KERNEL_SPACE_SIZE, UAO_FLAG_KERNOBJ);
/*
* init the map and reserve already allocated kernel space
* before installing.
*/
uvm_map_setup(&kernel_map_store, pmap_kernel(), base, end,
#ifdef KVA_GUARDPAGES
VM_MAP_PAGEABLE | VM_MAP_GUARDPAGES
#else
VM_MAP_PAGEABLE
#endif
);
if (base != start && uvm_map(&kernel_map_store, &base, start - base,
NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_INHERIT_NONE, MADV_RANDOM, UVM_FLAG_FIXED)) != 0)
panic("uvm_km_init: could not reserve space for kernel");
kernel_map = &kernel_map_store;
}
/*
* uvm_km_suballoc: allocate a submap in the kernel map. once a submap
* is allocated all references to that area of VM must go through it. this
* allows the locking of VAs in kernel_map to be broken up into regions.
*
* => if `fixed' is true, *min specifies where the region described
* by the submap must start
* => if submap is non NULL we use that as the submap, otherwise we
* alloc a new map
*/
struct vm_map *
uvm_km_suballoc(struct vm_map *map, vaddr_t *min, vaddr_t *max, vsize_t size,
int flags, boolean_t fixed, struct vm_map *submap)
{
int mapflags = UVM_FLAG_NOMERGE | (fixed ? UVM_FLAG_FIXED : 0);
size = round_page(size); /* round up to pagesize */
/* first allocate a blank spot in the parent map */
if (uvm_map(map, min, size, NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_INHERIT_NONE, MADV_RANDOM, mapflags)) != 0) {
panic("uvm_km_suballoc: unable to allocate space in parent map");
}
/* set VM bounds (min is filled in by uvm_map) */
*max = *min + size;
/* add references to pmap and create or init the submap */
pmap_reference(vm_map_pmap(map));
if (submap == NULL) {
submap = uvm_map_create(vm_map_pmap(map), *min, *max, flags);
if (submap == NULL)
panic("uvm_km_suballoc: unable to create submap");
} else {
uvm_map_setup(submap, vm_map_pmap(map), *min, *max, flags);
}
/*
* now let uvm_map_submap plug in it...
*/
if (uvm_map_submap(map, *min, *max, submap) != 0)
panic("uvm_km_suballoc: submap allocation failed");
return(submap);
}
/*
* uvm_km_pgremove: remove pages from a kernel uvm_object.
*
* => when you unmap a part of anonymous kernel memory you want to toss
* the pages right away. (this gets called from uvm_unmap_...).
*/
void
uvm_km_pgremove(struct uvm_object *uobj, vaddr_t startva, vaddr_t endva)
{
const voff_t start = startva - vm_map_min(kernel_map);
const voff_t end = endva - vm_map_min(kernel_map);
struct vm_page *pp;
voff_t curoff;
int slot;
int swpgonlydelta = 0;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
KASSERT(rw_write_held(uobj->vmobjlock));
pmap_remove(pmap_kernel(), startva, endva);
for (curoff = start ; curoff < end ; curoff += PAGE_SIZE) {
pp = uvm_pagelookup(uobj, curoff);
if (pp && pp->pg_flags & PG_BUSY) {
uvm_pagewait(pp, uobj->vmobjlock, "km_pgrm");
rw_enter(uobj->vmobjlock, RW_WRITE);
curoff -= PAGE_SIZE; /* loop back to us */
continue;
}
/* free the swap slot, then the page */
slot = uao_dropswap(uobj, curoff >> PAGE_SHIFT);
if (pp != NULL) {
uvm_lock_pageq();
uvm_pagefree(pp);
uvm_unlock_pageq();
} else if (slot != 0) {
swpgonlydelta++;
}
}
if (swpgonlydelta > 0) {
KASSERT(uvmexp.swpgonly >= swpgonlydelta);
atomic_add_int(&uvmexp.swpgonly, -swpgonlydelta);
}
}
/*
* uvm_km_pgremove_intrsafe: like uvm_km_pgremove(), but for "intrsafe"
* objects
*
* => when you unmap a part of anonymous kernel memory you want to toss
* the pages right away. (this gets called from uvm_unmap_...).
* => none of the pages will ever be busy, and none of them will ever
* be on the active or inactive queues (because these objects are
* never allowed to "page").
*/
void
uvm_km_pgremove_intrsafe(vaddr_t start, vaddr_t end)
{
struct vm_page *pg;
vaddr_t va;
paddr_t pa;
for (va = start; va < end; va += PAGE_SIZE) {
if (!pmap_extract(pmap_kernel(), va, &pa))
continue;
pg = PHYS_TO_VM_PAGE(pa);
if (pg == NULL)
panic("uvm_km_pgremove_intrsafe: no page");
uvm_pagefree(pg);
}
pmap_kremove(start, end - start);
}
/*
* uvm_km_kmemalloc: lower level kernel memory allocator for malloc()
*
* => we map wired memory into the specified map using the obj passed in
* => NOTE: we can return NULL even if we can wait if there is not enough
* free VM space in the map... caller should be prepared to handle
* this case.
* => we return KVA of memory allocated
* => flags: NOWAIT, VALLOC - just allocate VA, TRYLOCK - fail if we can't
* lock the map
* => low, high, alignment, boundary, nsegs are the corresponding parameters
* to uvm_pglistalloc
* => flags: ZERO - correspond to uvm_pglistalloc flags
*/
vaddr_t
uvm_km_kmemalloc_pla(struct vm_map *map, struct uvm_object *obj, vsize_t size,
vsize_t valign, int flags, paddr_t low, paddr_t high, paddr_t alignment,
paddr_t boundary, int nsegs)
{
vaddr_t kva, loopva;
voff_t offset;
struct vm_page *pg;
struct pglist pgl;
int pla_flags;
KASSERT(vm_map_pmap(map) == pmap_kernel());
/* UVM_KMF_VALLOC => !UVM_KMF_ZERO */
KASSERT(!(flags & UVM_KMF_VALLOC) ||
!(flags & UVM_KMF_ZERO));
/* setup for call */
size = round_page(size);
kva = vm_map_min(map); /* hint */
if (nsegs == 0)
nsegs = atop(size);
/* allocate some virtual space */
if (__predict_false(uvm_map(map, &kva, size, obj, UVM_UNKNOWN_OFFSET,
valign, UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_INHERIT_NONE, MADV_RANDOM, (flags & UVM_KMF_TRYLOCK))) != 0)) {
return 0;
}
/* if all we wanted was VA, return now */
if (flags & UVM_KMF_VALLOC) {
return kva;
}
/* recover object offset from virtual address */
if (obj != NULL)
offset = kva - vm_map_min(kernel_map);
else
offset = 0;
/*
* now allocate and map in the memory... note that we are the only ones
* whom should ever get a handle on this area of VM.
*/
TAILQ_INIT(&pgl);
pla_flags = 0;
KASSERT(uvmexp.swpgonly <= uvmexp.swpages);
if ((flags & UVM_KMF_NOWAIT) ||
((flags & UVM_KMF_CANFAIL) &&
uvmexp.swpages - uvmexp.swpgonly <= atop(size)))
pla_flags |= UVM_PLA_NOWAIT;
else
pla_flags |= UVM_PLA_WAITOK;
if (flags & UVM_KMF_ZERO)
pla_flags |= UVM_PLA_ZERO;
if (uvm_pglistalloc(size, low, high, alignment, boundary, &pgl, nsegs,
pla_flags) != 0) {
/* Failed. */
uvm_unmap(map, kva, kva + size);
return (0);
}
if (obj != NULL)
rw_enter(obj->vmobjlock, RW_WRITE);
loopva = kva;
while (loopva != kva + size) {
pg = TAILQ_FIRST(&pgl);
TAILQ_REMOVE(&pgl, pg, pageq);
uvm_pagealloc_pg(pg, obj, offset, NULL);
atomic_clearbits_int(&pg->pg_flags, PG_BUSY);
UVM_PAGE_OWN(pg, NULL);
/*
* map it in: note that we call pmap_enter with the map and
* object unlocked in case we are kmem_map.
*/
if (obj == NULL) {
pmap_kenter_pa(loopva, VM_PAGE_TO_PHYS(pg),
PROT_READ | PROT_WRITE);
} else {
pmap_enter(map->pmap, loopva, VM_PAGE_TO_PHYS(pg),
PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PMAP_WIRED);
}
loopva += PAGE_SIZE;
offset += PAGE_SIZE;
}
KASSERT(TAILQ_EMPTY(&pgl));
pmap_update(pmap_kernel());
if (obj != NULL)
rw_exit(obj->vmobjlock);
return kva;
}
/*
* uvm_km_free: free an area of kernel memory
*/
void
uvm_km_free(struct vm_map *map, vaddr_t addr, vsize_t size)
{
uvm_unmap(map, trunc_page(addr), round_page(addr+size));
}
/*
* uvm_km_alloc1: allocate wired down memory in the kernel map.
*
* => we can sleep if needed
*/
vaddr_t
uvm_km_alloc1(struct vm_map *map, vsize_t size, vsize_t align, boolean_t zeroit)
{
vaddr_t kva, loopva;
voff_t offset;
struct vm_page *pg;
KASSERT(vm_map_pmap(map) == pmap_kernel());
size = round_page(size);
kva = vm_map_min(map); /* hint */
/* allocate some virtual space */
if (__predict_false(uvm_map(map, &kva, size, uvm.kernel_object,
UVM_UNKNOWN_OFFSET, align,
UVM_MAPFLAG(PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_INHERIT_NONE, MADV_RANDOM, 0)) != 0)) {
return 0;
}
/* recover object offset from virtual address */
offset = kva - vm_map_min(kernel_map);
/* now allocate the memory. we must be careful about released pages. */
loopva = kva;
while (size) {
rw_enter(uvm.kernel_object->vmobjlock, RW_WRITE);
/* allocate ram */
pg = uvm_pagealloc(uvm.kernel_object, offset, NULL, 0);
if (pg) {
atomic_clearbits_int(&pg->pg_flags, PG_BUSY);
UVM_PAGE_OWN(pg, NULL);
}
rw_exit(uvm.kernel_object->vmobjlock);
if (__predict_false(pg == NULL)) {
if (curproc == uvm.pagedaemon_proc) {
/*
* It is unfeasible for the page daemon to
* sleep for memory, so free what we have
* allocated and fail.
*/
uvm_unmap(map, kva, loopva - kva);
return (0);
} else {
uvm_wait("km_alloc1w"); /* wait for memory */
continue;
}
}
/*
* map it in; note we're never called with an intrsafe
* object, so we always use regular old pmap_enter().
*/
pmap_enter(map->pmap, loopva, VM_PAGE_TO_PHYS(pg),
PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PMAP_WIRED);
loopva += PAGE_SIZE;
offset += PAGE_SIZE;
size -= PAGE_SIZE;
}
pmap_update(map->pmap);
/*
* zero on request (note that "size" is now zero due to the above loop
* so we need to subtract kva from loopva to reconstruct the size).
*/
if (zeroit)
memset((caddr_t)kva, 0, loopva - kva);
return kva;
}
#if defined(__HAVE_PMAP_DIRECT)
/*
* uvm_km_page allocator, __HAVE_PMAP_DIRECT arch
* On architectures with machine memory direct mapped into a portion
* of KVM, we have very little work to do. Just get a physical page,
* and find and return its VA.
*/
void
uvm_km_page_init(void)
{
/* nothing */
}
void
uvm_km_page_lateinit(void)
{
/* nothing */
}
#else
/*
* uvm_km_page allocator, non __HAVE_PMAP_DIRECT archs
* This is a special allocator that uses a reserve of free pages
* to fulfill requests. It is fast and interrupt safe, but can only
* return page sized regions. Its primary use is as a backend for pool.
*
* The memory returned is allocated from the larger kernel_map, sparing
* pressure on the small interrupt-safe kmem_map. It is wired, but
* not zero filled.
*/
struct uvm_km_pages uvm_km_pages;
void uvm_km_createthread(void *);
void uvm_km_thread(void *);
struct uvm_km_free_page *uvm_km_doputpage(struct uvm_km_free_page *);
/*
* Allocate the initial reserve, and create the thread which will
* keep the reserve full. For bootstrapping, we allocate more than
* the lowat amount, because it may be a while before the thread is
* running.
*/
void
uvm_km_page_init(void)
{
int lowat_min;
int i;
int len, bulk;
vaddr_t addr;
mtx_init(&uvm_km_pages.mtx, IPL_VM);
if (!uvm_km_pages.lowat) {
/* based on physmem, calculate a good value here */
uvm_km_pages.lowat = physmem / 256;
lowat_min = physmem < atop(16 * 1024 * 1024) ? 32 : 128;
if (uvm_km_pages.lowat < lowat_min)
uvm_km_pages.lowat = lowat_min;
}
if (uvm_km_pages.lowat > UVM_KM_PAGES_LOWAT_MAX)
uvm_km_pages.lowat = UVM_KM_PAGES_LOWAT_MAX;
uvm_km_pages.hiwat = 4 * uvm_km_pages.lowat;
if (uvm_km_pages.hiwat > UVM_KM_PAGES_HIWAT_MAX)
uvm_km_pages.hiwat = UVM_KM_PAGES_HIWAT_MAX;
/* Allocate all pages in as few allocations as possible. */
len = 0;
bulk = uvm_km_pages.hiwat;
while (len < uvm_km_pages.hiwat && bulk > 0) {
bulk = MIN(bulk, uvm_km_pages.hiwat - len);
addr = vm_map_min(kernel_map);
if (uvm_map(kernel_map, &addr, (vsize_t)bulk << PAGE_SHIFT,
NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE, MAP_INHERIT_NONE,
MADV_RANDOM, UVM_KMF_TRYLOCK)) != 0) {
bulk /= 2;
continue;
}
for (i = len; i < len + bulk; i++, addr += PAGE_SIZE)
uvm_km_pages.page[i] = addr;
len += bulk;
}
uvm_km_pages.free = len;
for (i = len; i < UVM_KM_PAGES_HIWAT_MAX; i++)
uvm_km_pages.page[i] = 0;
/* tone down if really high */
if (uvm_km_pages.lowat > 512)
uvm_km_pages.lowat = 512;
}
void
uvm_km_page_lateinit(void)
{
kthread_create_deferred(uvm_km_createthread, NULL);
}
void
uvm_km_createthread(void *arg)
{
kthread_create(uvm_km_thread, NULL, &uvm_km_pages.km_proc, "kmthread");
}
/*
* Endless loop. We grab pages in increments of 16 pages, then
* quickly swap them into the list.
*/
void
uvm_km_thread(void *arg)
{
vaddr_t pg[16];
int i;
int allocmore = 0;
int flags;
struct uvm_km_free_page *fp = NULL;
KERNEL_UNLOCK();
for (;;) {
mtx_enter(&uvm_km_pages.mtx);
if (uvm_km_pages.free >= uvm_km_pages.lowat &&
uvm_km_pages.freelist == NULL) {
msleep_nsec(&uvm_km_pages.km_proc, &uvm_km_pages.mtx,
PVM, "kmalloc", INFSLP);
}
allocmore = uvm_km_pages.free < uvm_km_pages.lowat;
fp = uvm_km_pages.freelist;
uvm_km_pages.freelist = NULL;
uvm_km_pages.freelistlen = 0;
mtx_leave(&uvm_km_pages.mtx);
if (allocmore) {
/*
* If there was nothing on the freelist, then we
* must obtain at least one page to make progress.
* So, only use UVM_KMF_TRYLOCK for the first page
* if fp != NULL
*/
flags = UVM_MAPFLAG(PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE, MAP_INHERIT_NONE,
MADV_RANDOM, fp != NULL ? UVM_KMF_TRYLOCK : 0);
memset(pg, 0, sizeof(pg));
for (i = 0; i < nitems(pg); i++) {
pg[i] = vm_map_min(kernel_map);
if (uvm_map(kernel_map, &pg[i], PAGE_SIZE,
NULL, UVM_UNKNOWN_OFFSET, 0, flags) != 0) {
pg[i] = 0;
break;
}
/* made progress, so don't sleep for more */
flags = UVM_MAPFLAG(PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE, MAP_INHERIT_NONE,
MADV_RANDOM, UVM_KMF_TRYLOCK);
}
mtx_enter(&uvm_km_pages.mtx);
for (i = 0; i < nitems(pg); i++) {
if (uvm_km_pages.free ==
nitems(uvm_km_pages.page))
break;
else if (pg[i] != 0)
uvm_km_pages.page[uvm_km_pages.free++]
= pg[i];
}
wakeup(&uvm_km_pages.free);
mtx_leave(&uvm_km_pages.mtx);
/* Cleanup left-over pages (if any). */
for (; i < nitems(pg); i++) {
if (pg[i] != 0) {
uvm_unmap(kernel_map,
pg[i], pg[i] + PAGE_SIZE);
}
}
}
while (fp) {
fp = uvm_km_doputpage(fp);
}
}
}
struct uvm_km_free_page *
uvm_km_doputpage(struct uvm_km_free_page *fp)
{
vaddr_t va = (vaddr_t)fp;
struct vm_page *pg;
int freeva = 1;
struct uvm_km_free_page *nextfp = fp->next;
pg = uvm_atopg(va);
pmap_kremove(va, PAGE_SIZE);
pmap_update(kernel_map->pmap);
mtx_enter(&uvm_km_pages.mtx);
if (uvm_km_pages.free < uvm_km_pages.hiwat) {
uvm_km_pages.page[uvm_km_pages.free++] = va;
freeva = 0;
}
mtx_leave(&uvm_km_pages.mtx);
if (freeva)
uvm_unmap(kernel_map, va, va + PAGE_SIZE);
uvm_pagefree(pg);
return (nextfp);
}
#endif /* !__HAVE_PMAP_DIRECT */
void *
km_alloc(size_t sz, const struct kmem_va_mode *kv,
const struct kmem_pa_mode *kp, const struct kmem_dyn_mode *kd)
{
struct vm_map *map;
struct vm_page *pg;
struct pglist pgl;
int mapflags = 0;
vm_prot_t prot;
paddr_t pla_align;
int pla_flags;
int pla_maxseg;
vaddr_t va, sva = 0;
KASSERT(sz == round_page(sz));
TAILQ_INIT(&pgl);
if (kp->kp_nomem || kp->kp_pageable)
goto alloc_va;
pla_flags = kd->kd_waitok ? UVM_PLA_WAITOK : UVM_PLA_NOWAIT;
pla_flags |= UVM_PLA_TRYCONTIG;
if (kp->kp_zero)
pla_flags |= UVM_PLA_ZERO;
pla_align = kp->kp_align;
#ifdef __HAVE_PMAP_DIRECT
if (pla_align < kv->kv_align)
pla_align = kv->kv_align;
#endif
pla_maxseg = kp->kp_maxseg;
if (pla_maxseg == 0)
pla_maxseg = sz / PAGE_SIZE;
if (uvm_pglistalloc(sz, kp->kp_constraint->ucr_low,
kp->kp_constraint->ucr_high, pla_align, kp->kp_boundary,
&pgl, pla_maxseg, pla_flags)) {
return (NULL);
}
#ifdef __HAVE_PMAP_DIRECT
/*
* Only use direct mappings for single page or single segment
* allocations.
*/
if (kv->kv_singlepage || kp->kp_maxseg == 1) {
TAILQ_FOREACH(pg, &pgl, pageq) {
va = pmap_map_direct(pg);
if (pg == TAILQ_FIRST(&pgl))
sva = va;
}
return ((void *)sva);
}
#endif
alloc_va:
prot = PROT_READ | PROT_WRITE;
if (kp->kp_pageable) {
KASSERT(kp->kp_object);
KASSERT(!kv->kv_singlepage);
} else {
KASSERT(kp->kp_object == NULL);
}
if (kv->kv_singlepage) {
KASSERT(sz == PAGE_SIZE);
#ifdef __HAVE_PMAP_DIRECT
panic("km_alloc: DIRECT single page");
#else
mtx_enter(&uvm_km_pages.mtx);
while (uvm_km_pages.free == 0) {
if (kd->kd_waitok == 0) {
mtx_leave(&uvm_km_pages.mtx);
uvm_pglistfree(&pgl);
return NULL;
}
msleep_nsec(&uvm_km_pages.free, &uvm_km_pages.mtx,
PVM, "getpage", INFSLP);
}
va = uvm_km_pages.page[--uvm_km_pages.free];
if (uvm_km_pages.free < uvm_km_pages.lowat &&
curproc != uvm_km_pages.km_proc) {
if (kd->kd_slowdown)
*kd->kd_slowdown = 1;
wakeup(&uvm_km_pages.km_proc);
}
mtx_leave(&uvm_km_pages.mtx);
#endif
} else {
struct uvm_object *uobj = NULL;
if (kd->kd_trylock)
mapflags |= UVM_KMF_TRYLOCK;
if (kp->kp_object)
uobj = *kp->kp_object;
try_map:
map = *kv->kv_map;
va = vm_map_min(map);
if (uvm_map(map, &va, sz, uobj, kd->kd_prefer,
kv->kv_align, UVM_MAPFLAG(prot, prot, MAP_INHERIT_NONE,
MADV_RANDOM, mapflags))) {
if (kv->kv_wait && kd->kd_waitok) {
tsleep_nsec(map, PVM, "km_allocva", INFSLP);
goto try_map;
}
uvm_pglistfree(&pgl);
return (NULL);
}
}
sva = va;
TAILQ_FOREACH(pg, &pgl, pageq) {
if (kp->kp_pageable)
pmap_enter(pmap_kernel(), va, VM_PAGE_TO_PHYS(pg),
prot, prot | PMAP_WIRED);
else
pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), prot);
va += PAGE_SIZE;
}
pmap_update(pmap_kernel());
return ((void *)sva);
}
void
km_free(void *v, size_t sz, const struct kmem_va_mode *kv,
const struct kmem_pa_mode *kp)
{
vaddr_t sva, eva, va;
struct vm_page *pg;
struct pglist pgl;
sva = (vaddr_t)v;
eva = sva + sz;
if (kp->kp_nomem)
goto free_va;
#ifdef __HAVE_PMAP_DIRECT
if (kv->kv_singlepage || kp->kp_maxseg == 1) {
TAILQ_INIT(&pgl);
for (va = sva; va < eva; va += PAGE_SIZE) {
pg = pmap_unmap_direct(va);
TAILQ_INSERT_TAIL(&pgl, pg, pageq);
}
uvm_pglistfree(&pgl);
return;
}
#else
if (kv->kv_singlepage) {
struct uvm_km_free_page *fp = v;
mtx_enter(&uvm_km_pages.mtx);
fp->next = uvm_km_pages.freelist;
uvm_km_pages.freelist = fp;
if (uvm_km_pages.freelistlen++ > 16)
wakeup(&uvm_km_pages.km_proc);
mtx_leave(&uvm_km_pages.mtx);
return;
}
#endif
if (kp->kp_pageable) {
pmap_remove(pmap_kernel(), sva, eva);
pmap_update(pmap_kernel());
} else {
TAILQ_INIT(&pgl);
for (va = sva; va < eva; va += PAGE_SIZE) {
paddr_t pa;
if (!pmap_extract(pmap_kernel(), va, &pa))
continue;
pg = PHYS_TO_VM_PAGE(pa);
if (pg == NULL) {
panic("km_free: unmanaged page 0x%lx", pa);
}
TAILQ_INSERT_TAIL(&pgl, pg, pageq);
}
pmap_kremove(sva, sz);
pmap_update(pmap_kernel());
uvm_pglistfree(&pgl);
}
free_va:
uvm_unmap(*kv->kv_map, sva, eva);
if (kv->kv_wait)
wakeup(*kv->kv_map);
}
const struct kmem_va_mode kv_any = {
.kv_map = &kernel_map,
};
const struct kmem_va_mode kv_intrsafe = {
.kv_map = &kmem_map,
};
const struct kmem_va_mode kv_page = {
.kv_singlepage = 1
};
const struct kmem_pa_mode kp_dirty = {
.kp_constraint = &no_constraint
};
const struct kmem_pa_mode kp_dma = {
.kp_constraint = &dma_constraint
};
const struct kmem_pa_mode kp_dma_contig = {
.kp_constraint = &dma_constraint,
.kp_maxseg = 1
};
const struct kmem_pa_mode kp_dma_zero = {
.kp_constraint = &dma_constraint,
.kp_zero = 1
};
const struct kmem_pa_mode kp_zero = {
.kp_constraint = &no_constraint,
.kp_zero = 1
};
const struct kmem_pa_mode kp_pageable = {
.kp_object = &uvm.kernel_object,
.kp_pageable = 1
/* XXX - kp_nomem, maybe, but we'll need to fix km_free. */
};
const struct kmem_pa_mode kp_none = {
.kp_nomem = 1
};
const struct kmem_dyn_mode kd_waitok = {
.kd_waitok = 1,
.kd_prefer = UVM_UNKNOWN_OFFSET
};
const struct kmem_dyn_mode kd_nowait = {
.kd_prefer = UVM_UNKNOWN_OFFSET
};
const struct kmem_dyn_mode kd_trylock = {
.kd_trylock = 1,
.kd_prefer = UVM_UNKNOWN_OFFSET
};
258
259
257
259
259
260
88
175
174
258
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* $OpenBSD: in_cksum.c,v 1.9 2019/04/22 22:47:49 bluhm Exp $ */
/* $NetBSD: in_cksum.c,v 1.11 1996/04/08 19:55:37 jonathan Exp $ */
/*
* Copyright (c) 1988, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
/*
* Checksum routine for Internet Protocol family headers (Portable Version).
*
* This routine is very heavily used in the network
* code and should be modified for each CPU to be as fast as possible.
*/
#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
int
in_cksum(struct mbuf *m, int len)
{
uint16_t *w;
int sum = 0;
int mlen = 0;
int byte_swapped = 0;
union {
uint8_t c[2];
uint16_t s;
} s_util;
union {
uint16_t s[2];
uint32_t l;
} l_util;
for (;m && len; m = m->m_next) {
if (m->m_len == 0)
continue;
w = mtod(m, uint16_t *);
if (mlen == -1) {
/*
* The first byte of this mbuf is the continuation
* of a word spanning between this mbuf and the
* last mbuf.
*
* s_util.c[0] is already saved when scanning previous
* mbuf.
*/
s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
w = (uint16_t *)((uint8_t *)w + 1);
mlen = m->m_len - 1;
len--;
} else
mlen = m->m_len;
if (len < mlen)
mlen = len;
len -= mlen;
/*
* Force to even boundary.
*/
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(uint8_t *)w;
w = (uint16_t *)((uint8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(uint8_t *)w;
}
if (len)
panic("%s: out of data, len %d", __func__, len);
if (mlen == -1) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}
279
1117
28
1120
32
32
32
27
32
268
14
181
257
95
268
1145
268
268
260
839
124
125
82
67
1
2
3
125
125
125
125
3
3
32
7
31
32
31
26
1492
1491
1494
1494
1477
1475
32
32
32
32
32
32
6
1493
1090
5
736
1076
738
20
1494
215
1494
189
189
189
1
188
145
145
124
145
124
145
125
20
20
20
1477
1477
1476
140
1410
1083
1475
20
1178
1124
20
1474
1477
1476
1475
1176
1084
267
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
/* $OpenBSD: uvm_pmemrange.c,v 1.62 2022/06/02 18:00:53 kettenis Exp $ */
/*
* Copyright (c) 2009, 2010 Ariane van der Steldt <ariane@stack.nl>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/mount.h>
/*
* 2 trees: addr tree and size tree.
*
* The allocator keeps chunks of free pages (called a range).
* Two pages are part of the same range if:
* - all pages in between are part of that range,
* - they are of the same memory type (zeroed or non-zeroed),
* - they are part of the same pmemrange.
* A pmemrange is a range of memory which is part of the same vm_physseg
* and has a use-count.
*
* addr tree is vm_page[0].objt
* size tree is vm_page[1].objt
*
* The size tree is not used for memory ranges of 1 page, instead,
* single queue is vm_page[0].pageq
*
* vm_page[0].fpgsz describes the length of a free range. Two adjecent ranges
* are joined, unless:
* - they have pages in between them which are not free
* - they belong to different memtypes (zeroed vs dirty memory)
* - they are in different pmemrange areas (ISA vs non-ISA memory for instance)
* - they are not a continuation of the same array
* The latter issue is caused by vm_physseg ordering and splitting from the
* MD initialization machinery. The MD code is dependant on freelists and
* happens to split ISA memory from non-ISA memory.
* (Note: freelists die die die!)
*
* uvm_page_init guarantees that every vm_physseg contains an array of
* struct vm_page. Also, uvm_page_physload allocates an array of struct
* vm_page. This code depends on that array. The array may break across
* vm_physsegs boundaries.
*/
/*
* Validate the flags of the page. (Used in asserts.)
* Any free page must have the PQ_FREE flag set.
* Free pages may be zeroed.
* Pmap flags are left untouched.
*
* The PQ_FREE flag is not checked here: by not checking, we can easily use
* this check in pages which are freed.
*/
#define VALID_FLAGS(pg_flags) \
(((pg_flags) & ~(PQ_FREE|PG_ZERO|PG_PMAPMASK)) == 0x0)
/* Tree comparators. */
int uvm_pmemrange_addr_cmp(const struct uvm_pmemrange *,
const struct uvm_pmemrange *);
int uvm_pmemrange_use_cmp(struct uvm_pmemrange *, struct uvm_pmemrange *);
int uvm_pmr_pg_to_memtype(struct vm_page *);
#ifdef DDB
void uvm_pmr_print(void);
#endif
/*
* Memory types. The page flags are used to derive what the current memory
* type of a page is.
*/
int
uvm_pmr_pg_to_memtype(struct vm_page *pg)
{
if (pg->pg_flags & PG_ZERO)
return UVM_PMR_MEMTYPE_ZERO;
/* Default: dirty memory. */
return UVM_PMR_MEMTYPE_DIRTY;
}
/* Trees. */
RBT_GENERATE(uvm_pmr_addr, vm_page, objt, uvm_pmr_addr_cmp);
RBT_GENERATE(uvm_pmr_size, vm_page, objt, uvm_pmr_size_cmp);
RBT_GENERATE(uvm_pmemrange_addr, uvm_pmemrange, pmr_addr,
uvm_pmemrange_addr_cmp);
/* Validation. */
#ifdef DEBUG
void uvm_pmr_assertvalid(struct uvm_pmemrange *pmr);
#else
#define uvm_pmr_assertvalid(pmr) do {} while (0)
#endif
psize_t uvm_pmr_get1page(psize_t, int, struct pglist *,
paddr_t, paddr_t, int);
struct uvm_pmemrange *uvm_pmr_allocpmr(void);
struct vm_page *uvm_pmr_nfindsz(struct uvm_pmemrange *, psize_t, int);
struct vm_page *uvm_pmr_nextsz(struct uvm_pmemrange *,
struct vm_page *, int);
void uvm_pmr_pnaddr(struct uvm_pmemrange *pmr,
struct vm_page *pg, struct vm_page **pg_prev,
struct vm_page **pg_next);
struct vm_page *uvm_pmr_findnextsegment(struct uvm_pmemrange *,
struct vm_page *, paddr_t);
struct vm_page *uvm_pmr_findprevsegment(struct uvm_pmemrange *,
struct vm_page *, paddr_t);
psize_t uvm_pmr_remove_1strange(struct pglist *, paddr_t,
struct vm_page **, int);
psize_t uvm_pmr_remove_1strange_reverse(struct pglist *,
paddr_t *);
void uvm_pmr_split(paddr_t);
struct uvm_pmemrange *uvm_pmemrange_find(paddr_t);
struct uvm_pmemrange *uvm_pmemrange_use_insert(struct uvm_pmemrange_use *,
struct uvm_pmemrange *);
psize_t pow2divide(psize_t, psize_t);
struct vm_page *uvm_pmr_rootupdate(struct uvm_pmemrange *,
struct vm_page *, paddr_t, paddr_t, int);
/*
* Computes num/denom and rounds it up to the next power-of-2.
*
* This is a division function which calculates an approximation of
* num/denom, with result =~ num/denom. It is meant to be fast and doesn't
* have to be accurate.
*
* Providing too large a value makes the allocator slightly faster, at the
* risk of hitting the failure case more often. Providing too small a value
* makes the allocator a bit slower, but less likely to hit a failure case.
*/
psize_t
pow2divide(psize_t num, psize_t denom)
{
int rshift;
for (rshift = 0; num > denom; rshift++, denom <<= 1)
;
return (paddr_t)1 << rshift;
}
/*
* Predicate: lhs is a subrange or rhs.
*
* If rhs_low == 0: don't care about lower bound.
* If rhs_high == 0: don't care about upper bound.
*/
#define PMR_IS_SUBRANGE_OF(lhs_low, lhs_high, rhs_low, rhs_high) \
(((rhs_low) == 0 || (lhs_low) >= (rhs_low)) && \
((rhs_high) == 0 || (lhs_high) <= (rhs_high)))
/*
* Predicate: lhs intersects with rhs.
*
* If rhs_low == 0: don't care about lower bound.
* If rhs_high == 0: don't care about upper bound.
* Ranges don't intersect if they don't have any page in common, array
* semantics mean that < instead of <= should be used here.
*/
#define PMR_INTERSECTS_WITH(lhs_low, lhs_high, rhs_low, rhs_high) \
(((rhs_low) == 0 || (rhs_low) < (lhs_high)) && \
((rhs_high) == 0 || (lhs_low) < (rhs_high)))
/*
* Align to power-of-2 alignment.
*/
#define PMR_ALIGN(pgno, align) \
(((pgno) + ((align) - 1)) & ~((align) - 1))
#define PMR_ALIGN_DOWN(pgno, align) \
((pgno) & ~((align) - 1))
/*
* Comparator: sort by address ascending.
*/
int
uvm_pmemrange_addr_cmp(const struct uvm_pmemrange *lhs,
const struct uvm_pmemrange *rhs)
{
return lhs->low < rhs->low ? -1 : lhs->low > rhs->low;
}
/*
* Comparator: sort by use ascending.
*
* The higher the use value of a range, the more devices need memory in
* this range. Therefore allocate from the range with the lowest use first.
*/
int
uvm_pmemrange_use_cmp(struct uvm_pmemrange *lhs, struct uvm_pmemrange *rhs)
{
int result;
result = lhs->use < rhs->use ? -1 : lhs->use > rhs->use;
if (result == 0)
result = uvm_pmemrange_addr_cmp(lhs, rhs);
return result;
}
int
uvm_pmr_addr_cmp(const struct vm_page *lhs, const struct vm_page *rhs)
{
paddr_t lhs_addr, rhs_addr;
lhs_addr = VM_PAGE_TO_PHYS(lhs);
rhs_addr = VM_PAGE_TO_PHYS(rhs);
return (lhs_addr < rhs_addr ? -1 : lhs_addr > rhs_addr);
}
int
uvm_pmr_size_cmp(const struct vm_page *lhs, const struct vm_page *rhs)
{
psize_t lhs_size, rhs_size;
int cmp;
/* Using second tree, so we receive pg[1] instead of pg[0]. */
lhs_size = (lhs - 1)->fpgsz;
rhs_size = (rhs - 1)->fpgsz;
cmp = (lhs_size < rhs_size ? -1 : lhs_size > rhs_size);
if (cmp == 0)
cmp = uvm_pmr_addr_cmp(lhs - 1, rhs - 1);
return cmp;
}
/*
* Find the first range of free pages that is at least sz pages long.
*/
struct vm_page *
uvm_pmr_nfindsz(struct uvm_pmemrange *pmr, psize_t sz, int mti)
{
struct vm_page *node, *best;
KASSERT(sz >= 1);
if (sz == 1 && !TAILQ_EMPTY(&pmr->single[mti]))
return TAILQ_FIRST(&pmr->single[mti]);
node = RBT_ROOT(uvm_pmr_size, &pmr->size[mti]);
best = NULL;
while (node != NULL) {
if ((node - 1)->fpgsz >= sz) {
best = (node - 1);
node = RBT_LEFT(uvm_objtree, node);
} else
node = RBT_RIGHT(uvm_objtree, node);
}
return best;
}
/*
* Finds the next range. The next range has a size >= pg->fpgsz.
* Returns NULL if no more ranges are available.
*/
struct vm_page *
uvm_pmr_nextsz(struct uvm_pmemrange *pmr, struct vm_page *pg, int mt)
{
struct vm_page *npg;
KASSERT(pmr != NULL && pg != NULL);
if (pg->fpgsz == 1) {
if (TAILQ_NEXT(pg, pageq) != NULL)
return TAILQ_NEXT(pg, pageq);
else
npg = RBT_MIN(uvm_pmr_size, &pmr->size[mt]);
} else
npg = RBT_NEXT(uvm_pmr_size, pg + 1);
return npg == NULL ? NULL : npg - 1;
}
/*
* Finds the previous and next ranges relative to the (uninserted) pg range.
*
* *pg_prev == NULL if no previous range is available, that can join with
* pg.
* *pg_next == NULL if no next range is available, that can join with
* pg.
*/
void
uvm_pmr_pnaddr(struct uvm_pmemrange *pmr, struct vm_page *pg,
struct vm_page **pg_prev, struct vm_page **pg_next)
{
KASSERT(pg_prev != NULL && pg_next != NULL);
*pg_next = RBT_NFIND(uvm_pmr_addr, &pmr->addr, pg);
if (*pg_next == NULL)
*pg_prev = RBT_MAX(uvm_pmr_addr, &pmr->addr);
else
*pg_prev = RBT_PREV(uvm_pmr_addr, *pg_next);
KDASSERT(*pg_next == NULL ||
VM_PAGE_TO_PHYS(*pg_next) > VM_PAGE_TO_PHYS(pg));
KDASSERT(*pg_prev == NULL ||
VM_PAGE_TO_PHYS(*pg_prev) < VM_PAGE_TO_PHYS(pg));
/* Reset if not contig. */
if (*pg_prev != NULL &&
(atop(VM_PAGE_TO_PHYS(*pg_prev)) + (*pg_prev)->fpgsz
!= atop(VM_PAGE_TO_PHYS(pg)) ||
*pg_prev + (*pg_prev)->fpgsz != pg || /* Array broke. */
uvm_pmr_pg_to_memtype(*pg_prev) != uvm_pmr_pg_to_memtype(pg)))
*pg_prev = NULL;
if (*pg_next != NULL &&
(atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz
!= atop(VM_PAGE_TO_PHYS(*pg_next)) ||
pg + pg->fpgsz != *pg_next || /* Array broke. */
uvm_pmr_pg_to_memtype(*pg_next) != uvm_pmr_pg_to_memtype(pg)))
*pg_next = NULL;
return;
}
/*
* Remove a range from the address tree.
* Address tree maintains pmr counters.
*/
void
uvm_pmr_remove_addr(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
KDASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, pg) == pg);
KDASSERT(pg->pg_flags & PQ_FREE);
RBT_REMOVE(uvm_pmr_addr, &pmr->addr, pg);
pmr->nsegs--;
}
/*
* Remove a range from the size tree.
*/
void
uvm_pmr_remove_size(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
int memtype;
#ifdef DEBUG
struct vm_page *i;
#endif
KDASSERT(pg->fpgsz >= 1);
KDASSERT(pg->pg_flags & PQ_FREE);
memtype = uvm_pmr_pg_to_memtype(pg);
if (pg->fpgsz == 1) {
#ifdef DEBUG
TAILQ_FOREACH(i, &pmr->single[memtype], pageq) {
if (i == pg)
break;
}
KDASSERT(i == pg);
#endif
TAILQ_REMOVE(&pmr->single[memtype], pg, pageq);
} else {
KDASSERT(RBT_FIND(uvm_pmr_size, &pmr->size[memtype],
pg + 1) == pg + 1);
RBT_REMOVE(uvm_pmr_size, &pmr->size[memtype], pg + 1);
}
}
/* Remove from both trees. */
void
uvm_pmr_remove(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
uvm_pmr_assertvalid(pmr);
uvm_pmr_remove_size(pmr, pg);
uvm_pmr_remove_addr(pmr, pg);
uvm_pmr_assertvalid(pmr);
}
/*
* Insert the range described in pg.
* Returns the range thus created (which may be joined with the previous and
* next ranges).
* If no_join, the caller guarantees that the range cannot possibly join
* with adjecent ranges.
*/
struct vm_page *
uvm_pmr_insert_addr(struct uvm_pmemrange *pmr, struct vm_page *pg, int no_join)
{
struct vm_page *prev, *next;
#ifdef DEBUG
struct vm_page *i;
int mt;
#endif
KDASSERT(pg->pg_flags & PQ_FREE);
KDASSERT(pg->fpgsz >= 1);
#ifdef DEBUG
for (mt = 0; mt < UVM_PMR_MEMTYPE_MAX; mt++) {
TAILQ_FOREACH(i, &pmr->single[mt], pageq)
KDASSERT(i != pg);
if (pg->fpgsz > 1) {
KDASSERT(RBT_FIND(uvm_pmr_size, &pmr->size[mt],
pg + 1) == NULL);
}
KDASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, pg) == NULL);
}
#endif
if (!no_join) {
uvm_pmr_pnaddr(pmr, pg, &prev, &next);
if (next != NULL) {
uvm_pmr_remove_size(pmr, next);
uvm_pmr_remove_addr(pmr, next);
pg->fpgsz += next->fpgsz;
next->fpgsz = 0;
}
if (prev != NULL) {
uvm_pmr_remove_size(pmr, prev);
prev->fpgsz += pg->fpgsz;
pg->fpgsz = 0;
return prev;
}
}
RBT_INSERT(uvm_pmr_addr, &pmr->addr, pg);
pmr->nsegs++;
return pg;
}
/*
* Insert the range described in pg.
* Returns the range thus created (which may be joined with the previous and
* next ranges).
* Page must already be in the address tree.
*/
void
uvm_pmr_insert_size(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
int memtype;
#ifdef DEBUG
struct vm_page *i;
int mti;
#endif
KDASSERT(pg->fpgsz >= 1);
KDASSERT(pg->pg_flags & PQ_FREE);
memtype = uvm_pmr_pg_to_memtype(pg);
#ifdef DEBUG
for (mti = 0; mti < UVM_PMR_MEMTYPE_MAX; mti++) {
TAILQ_FOREACH(i, &pmr->single[mti], pageq)
KDASSERT(i != pg);
if (pg->fpgsz > 1) {
KDASSERT(RBT_FIND(uvm_pmr_size, &pmr->size[mti],
pg + 1) == NULL);
}
KDASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, pg) == pg);
}
for (i = pg; i < pg + pg->fpgsz; i++)
KASSERT(uvm_pmr_pg_to_memtype(i) == memtype);
#endif
if (pg->fpgsz == 1)
TAILQ_INSERT_TAIL(&pmr->single[memtype], pg, pageq);
else
RBT_INSERT(uvm_pmr_size, &pmr->size[memtype], pg + 1);
}
/* Insert in both trees. */
struct vm_page *
uvm_pmr_insert(struct uvm_pmemrange *pmr, struct vm_page *pg, int no_join)
{
uvm_pmr_assertvalid(pmr);
pg = uvm_pmr_insert_addr(pmr, pg, no_join);
uvm_pmr_insert_size(pmr, pg);
uvm_pmr_assertvalid(pmr);
return pg;
}
/*
* Find the last page that is part of this segment.
* => pg: the range at which to start the search.
* => boundary: the page number boundary specification (0 = no boundary).
* => pmr: the pmemrange of the page.
*
* This function returns 1 before the next range, so if you want to have the
* next range, you need to run TAILQ_NEXT(result, pageq) after calling.
* The reason is that this way, the length of the segment is easily
* calculated using: atop(result) - atop(pg) + 1.
* Hence this function also never returns NULL.
*/
struct vm_page *
uvm_pmr_findnextsegment(struct uvm_pmemrange *pmr,
struct vm_page *pg, paddr_t boundary)
{
paddr_t first_boundary;
struct vm_page *next;
struct vm_page *prev;
KDASSERT(pmr->low <= atop(VM_PAGE_TO_PHYS(pg)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(pg)));
if (boundary != 0) {
first_boundary =
PMR_ALIGN(atop(VM_PAGE_TO_PHYS(pg)) + 1, boundary);
} else
first_boundary = 0;
/*
* Increase next until it hits the first page of the next segment.
*
* While loop checks the following:
* - next != NULL we have not reached the end of pgl
* - boundary == 0 || next < first_boundary
* we do not cross a boundary
* - atop(prev) + 1 == atop(next)
* still in the same segment
* - low <= last
* - high > last still in the same memory range
* - memtype is equal allocator is unable to view different memtypes
* as part of the same segment
* - prev + 1 == next no array breakage occurs
*/
prev = pg;
next = TAILQ_NEXT(prev, pageq);
while (next != NULL &&
(boundary == 0 || atop(VM_PAGE_TO_PHYS(next)) < first_boundary) &&
atop(VM_PAGE_TO_PHYS(prev)) + 1 == atop(VM_PAGE_TO_PHYS(next)) &&
pmr->low <= atop(VM_PAGE_TO_PHYS(next)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(next)) &&
uvm_pmr_pg_to_memtype(prev) == uvm_pmr_pg_to_memtype(next) &&
prev + 1 == next) {
prev = next;
next = TAILQ_NEXT(prev, pageq);
}
/*
* End of this segment.
*/
return prev;
}
/*
* Find the first page that is part of this segment.
* => pg: the range at which to start the search.
* => boundary: the page number boundary specification (0 = no boundary).
* => pmr: the pmemrange of the page.
*
* This function returns 1 after the previous range, so if you want to have the
* previous range, you need to run TAILQ_NEXT(result, pageq) after calling.
* The reason is that this way, the length of the segment is easily
* calculated using: atop(pg) - atop(result) + 1.
* Hence this function also never returns NULL.
*/
struct vm_page *
uvm_pmr_findprevsegment(struct uvm_pmemrange *pmr,
struct vm_page *pg, paddr_t boundary)
{
paddr_t first_boundary;
struct vm_page *next;
struct vm_page *prev;
KDASSERT(pmr->low <= atop(VM_PAGE_TO_PHYS(pg)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(pg)));
if (boundary != 0) {
first_boundary =
PMR_ALIGN_DOWN(atop(VM_PAGE_TO_PHYS(pg)), boundary);
} else
first_boundary = 0;
/*
* Increase next until it hits the first page of the previous segment.
*
* While loop checks the following:
* - next != NULL we have not reached the end of pgl
* - boundary == 0 || next >= first_boundary
* we do not cross a boundary
* - atop(prev) - 1 == atop(next)
* still in the same segment
* - low <= last
* - high > last still in the same memory range
* - memtype is equal allocator is unable to view different memtypes
* as part of the same segment
* - prev - 1 == next no array breakage occurs
*/
prev = pg;
next = TAILQ_NEXT(prev, pageq);
while (next != NULL &&
(boundary == 0 || atop(VM_PAGE_TO_PHYS(next)) >= first_boundary) &&
atop(VM_PAGE_TO_PHYS(prev)) - 1 == atop(VM_PAGE_TO_PHYS(next)) &&
pmr->low <= atop(VM_PAGE_TO_PHYS(next)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(next)) &&
uvm_pmr_pg_to_memtype(prev) == uvm_pmr_pg_to_memtype(next) &&
prev - 1 == next) {
prev = next;
next = TAILQ_NEXT(prev, pageq);
}
/*
* Start of this segment.
*/
return prev;
}
/*
* Remove the first segment of contiguous pages from pgl.
* A segment ends if it crosses boundary (unless boundary = 0) or
* if it would enter a different uvm_pmemrange.
*
* Work: the page range that the caller is currently working with.
* May be null.
*
* If is_desperate is non-zero, the smallest segment is erased. Otherwise,
* the first segment is erased (which, if called by uvm_pmr_getpages(),
* probably is the smallest or very close to it).
*/
psize_t
uvm_pmr_remove_1strange(struct pglist *pgl, paddr_t boundary,
struct vm_page **work, int is_desperate)
{
struct vm_page *start, *end, *iter, *iter_end, *inserted, *lowest;
psize_t count;
struct uvm_pmemrange *pmr, *pmr_iter;
KASSERT(!TAILQ_EMPTY(pgl));
/*
* Initialize to first page.
* Unless desperate scan finds a better candidate, this is what'll be
* erased.
*/
start = TAILQ_FIRST(pgl);
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(start)));
end = uvm_pmr_findnextsegment(pmr, start, boundary);
/*
* If we are desperate, we _really_ want to get rid of the smallest
* element (rather than a close match to the smallest element).
*/
if (is_desperate) {
/* Linear search for smallest segment. */
pmr_iter = pmr;
for (iter = TAILQ_NEXT(end, pageq);
iter != NULL && start != end;
iter = TAILQ_NEXT(iter_end, pageq)) {
/*
* Only update pmr if it doesn't match current
* iteration.
*/
if (pmr->low > atop(VM_PAGE_TO_PHYS(iter)) ||
pmr->high <= atop(VM_PAGE_TO_PHYS(iter))) {
pmr_iter = uvm_pmemrange_find(atop(
VM_PAGE_TO_PHYS(iter)));
}
iter_end = uvm_pmr_findnextsegment(pmr_iter, iter,
boundary);
/*
* Current iteration is smaller than best match so
* far; update.
*/
if (VM_PAGE_TO_PHYS(iter_end) - VM_PAGE_TO_PHYS(iter) <
VM_PAGE_TO_PHYS(end) - VM_PAGE_TO_PHYS(start)) {
start = iter;
end = iter_end;
pmr = pmr_iter;
}
}
}
/*
* Calculate count and end of the list.
*/
count = atop(VM_PAGE_TO_PHYS(end) - VM_PAGE_TO_PHYS(start)) + 1;
lowest = start;
end = TAILQ_NEXT(end, pageq);
/*
* Actually remove the range of pages.
*
* Sadly, this cannot be done using pointer iteration:
* vm_physseg is not guaranteed to be sorted on address, hence
* uvm_page_init() may not have initialized its array sorted by
* page number.
*/
for (iter = start; iter != end; iter = iter_end) {
iter_end = TAILQ_NEXT(iter, pageq);
TAILQ_REMOVE(pgl, iter, pageq);
}
lowest->fpgsz = count;
inserted = uvm_pmr_insert(pmr, lowest, 0);
/*
* If the caller was working on a range and this function modified
* that range, update the pointer.
*/
if (work != NULL && *work != NULL &&
atop(VM_PAGE_TO_PHYS(inserted)) <= atop(VM_PAGE_TO_PHYS(*work)) &&
atop(VM_PAGE_TO_PHYS(inserted)) + inserted->fpgsz >
atop(VM_PAGE_TO_PHYS(*work)))
*work = inserted;
return count;
}
/*
* Remove the first segment of contiguous pages from a pgl
* with the list elements in reverse order of physaddr.
*
* A segment ends if it would enter a different uvm_pmemrange.
*
* Stores starting physical address of the segment in pstart.
*/
psize_t
uvm_pmr_remove_1strange_reverse(struct pglist *pgl, paddr_t *pstart)
{
struct vm_page *start, *end, *iter, *iter_end, *lowest;
psize_t count;
struct uvm_pmemrange *pmr;
KASSERT(!TAILQ_EMPTY(pgl));
start = TAILQ_FIRST(pgl);
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(start)));
end = uvm_pmr_findprevsegment(pmr, start, 0);
KASSERT(end <= start);
/*
* Calculate count and end of the list.
*/
count = atop(VM_PAGE_TO_PHYS(start) - VM_PAGE_TO_PHYS(end)) + 1;
lowest = end;
end = TAILQ_NEXT(end, pageq);
/*
* Actually remove the range of pages.
*
* Sadly, this cannot be done using pointer iteration:
* vm_physseg is not guaranteed to be sorted on address, hence
* uvm_page_init() may not have initialized its array sorted by
* page number.
*/
for (iter = start; iter != end; iter = iter_end) {
iter_end = TAILQ_NEXT(iter, pageq);
TAILQ_REMOVE(pgl, iter, pageq);
}
lowest->fpgsz = count;
(void) uvm_pmr_insert(pmr, lowest, 0);
*pstart = VM_PAGE_TO_PHYS(lowest);
return count;
}
/*
* Extract a number of pages from a segment of free pages.
* Called by uvm_pmr_getpages.
*
* Returns the segment that was created from pages left over at the tail
* of the remove set of pages, or NULL if no pages were left at the tail.
*/
struct vm_page *
uvm_pmr_extract_range(struct uvm_pmemrange *pmr, struct vm_page *pg,
paddr_t start, paddr_t end, struct pglist *result)
{
struct vm_page *after, *pg_i;
psize_t before_sz, after_sz;
#ifdef DEBUG
psize_t i;
#endif
KDASSERT(end > start);
KDASSERT(pmr->low <= atop(VM_PAGE_TO_PHYS(pg)));
KDASSERT(pmr->high >= atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz);
KDASSERT(atop(VM_PAGE_TO_PHYS(pg)) <= start);
KDASSERT(atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz >= end);
before_sz = start - atop(VM_PAGE_TO_PHYS(pg));
after_sz = atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz - end;
KDASSERT(before_sz + after_sz + (end - start) == pg->fpgsz);
uvm_pmr_assertvalid(pmr);
uvm_pmr_remove_size(pmr, pg);
if (before_sz == 0)
uvm_pmr_remove_addr(pmr, pg);
after = pg + before_sz + (end - start);
/* Add selected pages to result. */
for (pg_i = pg + before_sz; pg_i != after; pg_i++) {
KASSERT(pg_i->pg_flags & PQ_FREE);
pg_i->fpgsz = 0;
TAILQ_INSERT_TAIL(result, pg_i, pageq);
}
/* Before handling. */
if (before_sz > 0) {
pg->fpgsz = before_sz;
uvm_pmr_insert_size(pmr, pg);
}
/* After handling. */
if (after_sz > 0) {
#ifdef DEBUG
for (i = 0; i < after_sz; i++) {
KASSERT(!uvm_pmr_isfree(after + i));
}
#endif
KDASSERT(atop(VM_PAGE_TO_PHYS(after)) == end);
after->fpgsz = after_sz;
after = uvm_pmr_insert_addr(pmr, after, 1);
uvm_pmr_insert_size(pmr, after);
}
uvm_pmr_assertvalid(pmr);
return (after_sz > 0 ? after : NULL);
}
/*
* Indicate to the page daemon that a nowait call failed and it should
* recover at least some memory in the most restricted region (assumed
* to be dma_constraint).
*/
extern volatile int uvm_nowait_failed;
/*
* Acquire a number of pages.
*
* count: the number of pages returned
* start: lowest page number
* end: highest page number +1
* (start = end = 0: no limitation)
* align: power-of-2 alignment constraint (align = 1: no alignment)
* boundary: power-of-2 boundary (boundary = 0: no boundary)
* maxseg: maximum number of segments to return
* flags: UVM_PLA_* flags
* result: returned pages storage (uses pageq)
*/
int
uvm_pmr_getpages(psize_t count, paddr_t start, paddr_t end, paddr_t align,
paddr_t boundary, int maxseg, int flags, struct pglist *result)
{
struct uvm_pmemrange *pmr; /* Iterate memory ranges. */
struct vm_page *found, *f_next; /* Iterate chunks. */
psize_t fcount; /* Current found pages. */
int fnsegs; /* Current segment counter. */
int try, start_try;
psize_t search[3];
paddr_t fstart, fend; /* Pages to be taken from found. */
int memtype; /* Requested memtype. */
int memtype_init; /* Best memtype. */
int desperate; /* True if allocation failed. */
#ifdef DIAGNOSTIC
struct vm_page *diag_prev; /* Used during validation. */
#endif /* DIAGNOSTIC */
/*
* Validate arguments.
*/
KASSERT(count > 0);
KASSERT(start == 0 || end == 0 || start < end);
KASSERT(align >= 1);
KASSERT(powerof2(align));
KASSERT(maxseg > 0);
KASSERT(boundary == 0 || powerof2(boundary));
KASSERT(boundary == 0 || maxseg * boundary >= count);
KASSERT(TAILQ_EMPTY(result));
KASSERT(!(flags & UVM_PLA_WAITOK) ^ !(flags & UVM_PLA_NOWAIT));
/*
* TRYCONTIG is a noop if you only want a single segment.
* Remove it if that's the case: otherwise it'll deny the fast
* allocation.
*/
if (maxseg == 1 || count == 1)
flags &= ~UVM_PLA_TRYCONTIG;
/*
* Configure search.
*
* search[0] is one segment, only used in UVM_PLA_TRYCONTIG case.
* search[1] is multiple segments, chosen to fulfill the search in
* approximately even-sized segments.
* This is a good trade-off between slightly reduced allocation speed
* and less fragmentation.
* search[2] is the worst case, in which all segments are evaluated.
* This provides the least fragmentation, but makes the search
* possibly longer (although in the case it is selected, that no
* longer matters most).
*
* The exception is when maxseg == 1: since we can only fulfill that
* with one segment of size pages, only a single search type has to
* be attempted.
*/
if (maxseg == 1 || count == 1) {
start_try = 2;
search[2] = count;
} else if (maxseg >= count && (flags & UVM_PLA_TRYCONTIG) == 0) {
start_try = 2;
search[2] = 1;
} else {
start_try = 0;
search[0] = count;
search[1] = pow2divide(count, maxseg);
search[2] = 1;
if ((flags & UVM_PLA_TRYCONTIG) == 0)
start_try = 1;
if (search[1] >= search[0]) {
search[1] = search[0];
start_try = 1;
}
if (search[2] >= search[start_try]) {
start_try = 2;
}
}
/*
* Memory type: if zeroed memory is requested, traverse the zero set.
* Otherwise, traverse the dirty set.
*
* The memtype iterator is reinitialized to memtype_init on entrance
* of a pmemrange.
*/
if (flags & UVM_PLA_ZERO)
memtype_init = UVM_PMR_MEMTYPE_ZERO;
else
memtype_init = UVM_PMR_MEMTYPE_DIRTY;
/*
* Initially, we're not desperate.
*
* Note that if we return from a sleep, we are still desperate.
* Chances are that memory pressure is still high, so resetting
* seems over-optimistic to me.
*/
desperate = 0;
again:
uvm_lock_fpageq();
/*
* check to see if we need to generate some free pages waking
* the pagedaemon.
*/
if ((uvmexp.free - BUFPAGES_DEFICIT) < uvmexp.freemin ||
((uvmexp.free - BUFPAGES_DEFICIT) < uvmexp.freetarg &&
(uvmexp.inactive + BUFPAGES_INACT) < uvmexp.inactarg))
wakeup(&uvm.pagedaemon);
/*
* fail if any of these conditions is true:
* [1] there really are no free pages, or
* [2] only kernel "reserved" pages remain and
* the UVM_PLA_USERESERVE flag wasn't used.
* [3] only pagedaemon "reserved" pages remain and
* the requestor isn't the pagedaemon nor the syncer.
*/
if ((uvmexp.free <= (uvmexp.reserve_kernel + count)) &&
!(flags & UVM_PLA_USERESERVE)) {
uvm_unlock_fpageq();
return ENOMEM;
}
if ((uvmexp.free <= (uvmexp.reserve_pagedaemon + count)) &&
(curproc != uvm.pagedaemon_proc) && (curproc != syncerproc)) {
uvm_unlock_fpageq();
if (flags & UVM_PLA_WAITOK) {
uvm_wait("uvm_pmr_getpages");
goto again;
}
return ENOMEM;
}
retry: /* Return point after sleeping. */
fcount = 0;
fnsegs = 0;
retry_desperate:
/*
* If we just want any page(s), go for the really fast option.
*/
if (count <= maxseg && align == 1 && boundary == 0 &&
(flags & UVM_PLA_TRYCONTIG) == 0) {
fcount += uvm_pmr_get1page(count - fcount, memtype_init,
result, start, end, 0);
/*
* If we found sufficient pages, go to the success exit code.
*
* Otherwise, go immediately to fail, since we collected
* all we could anyway.
*/
if (fcount == count)
goto out;
else
goto fail;
}
/*
* The heart of the contig case.
*
* The code actually looks like this:
*
* foreach (struct pmemrange) {
* foreach (memtype) {
* foreach(try) {
* foreach (free range of memtype in pmemrange,
* starting at search[try]) {
* while (range has space left)
* take from range
* }
* }
* }
*
* if next pmemrange has higher usecount than current:
* enter desperate case (which will drain the pmemranges
* until empty prior to moving to the next one)
* }
*
* When desperate is activated, try always starts at the highest
* value. The memtype loop is using a goto ReScanMemtype.
* The try loop is using a goto ReScan.
* The 'range has space left' loop uses label DrainFound.
*
* Writing them all as loops would take up a lot of screen space in
* the form of indentation and some parts are easier to express
* using the labels.
*/
TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
/* Empty range. */
if (pmr->nsegs == 0)
continue;
/* Outside requested range. */
if (!PMR_INTERSECTS_WITH(pmr->low, pmr->high, start, end))
continue;
memtype = memtype_init;
rescan_memtype: /* Return point at memtype++. */
try = start_try;
rescan: /* Return point at try++. */
for (found = uvm_pmr_nfindsz(pmr, search[try], memtype);
found != NULL;
found = f_next) {
f_next = uvm_pmr_nextsz(pmr, found, memtype);
fstart = atop(VM_PAGE_TO_PHYS(found));
if (start != 0)
fstart = MAX(start, fstart);
drain_found:
/*
* Throw away the first segment if fnsegs == maxseg
*
* Note that f_next is still valid after this call,
* since we only allocated from entries before f_next.
* We don't revisit the entries we already extracted
* from unless we entered the desperate case.
*/
if (fnsegs == maxseg) {
fnsegs--;
fcount -=
uvm_pmr_remove_1strange(result, boundary,
&found, desperate);
}
fstart = PMR_ALIGN(fstart, align);
fend = atop(VM_PAGE_TO_PHYS(found)) + found->fpgsz;
if (end != 0)
fend = MIN(end, fend);
if (boundary != 0) {
fend =
MIN(fend, PMR_ALIGN(fstart + 1, boundary));
}
if (fstart >= fend)
continue;
if (fend - fstart > count - fcount)
fend = fstart + (count - fcount);
fcount += fend - fstart;
fnsegs++;
found = uvm_pmr_extract_range(pmr, found,
fstart, fend, result);
if (fcount == count)
goto out;
/*
* If there's still space left in found, try to
* fully drain it prior to continuing.
*/
if (found != NULL) {
fstart = fend;
goto drain_found;
}
}
/* Try a smaller search now. */
if (++try < nitems(search))
goto rescan;
/*
* Exhaust all memory types prior to going to the next memory
* segment.
* This means that zero-vs-dirty are eaten prior to moving
* to a pmemrange with a higher use-count.
*
* Code is basically a difficult way of writing:
* memtype = memtype_init;
* do {
* ...;
* memtype += 1;
* memtype %= MEMTYPE_MAX;
* } while (memtype != memtype_init);
*/
memtype += 1;
if (memtype == UVM_PMR_MEMTYPE_MAX)
memtype = 0;
if (memtype != memtype_init)
goto rescan_memtype;
/*
* If not desperate, enter desperate case prior to eating all
* the good stuff in the next range.
*/
if (!desperate && TAILQ_NEXT(pmr, pmr_use) != NULL &&
TAILQ_NEXT(pmr, pmr_use)->use != pmr->use)
break;
}
/*
* Not enough memory of the requested type available. Fall back to
* less good memory that we'll clean up better later.
*
* This algorithm is not very smart though, it just starts scanning
* a different typed range, but the nicer ranges of the previous
* iteration may fall out. Hence there is a small chance of a false
* negative.
*
* When desperate: scan all sizes starting at the smallest
* (start_try = 1) and do not consider UVM_PLA_TRYCONTIG (which may
* allow us to hit the fast path now).
*
* Also, because we will revisit entries we scanned before, we need
* to reset the page queue, or we may end up releasing entries in
* such a way as to invalidate f_next.
*/
if (!desperate) {
desperate = 1;
start_try = nitems(search) - 1;
flags &= ~UVM_PLA_TRYCONTIG;
while (!TAILQ_EMPTY(result))
uvm_pmr_remove_1strange(result, 0, NULL, 0);
fnsegs = 0;
fcount = 0;
goto retry_desperate;
}
fail:
/* Allocation failed. */
/* XXX: claim from memory reserve here */
while (!TAILQ_EMPTY(result))
uvm_pmr_remove_1strange(result, 0, NULL, 0);
if (flags & UVM_PLA_WAITOK) {
if (uvm_wait_pla(ptoa(start), ptoa(end) - 1, ptoa(count),
flags & UVM_PLA_FAILOK) == 0)
goto retry;
KASSERT(flags & UVM_PLA_FAILOK);
} else {
if (!(flags & UVM_PLA_NOWAKE)) {
uvm_nowait_failed = 1;
wakeup(&uvm.pagedaemon);
}
}
uvm_unlock_fpageq();
return ENOMEM;
out:
/* Allocation successful. */
uvmexp.free -= fcount;
uvm_unlock_fpageq();
/* Update statistics and zero pages if UVM_PLA_ZERO. */
#ifdef DIAGNOSTIC
fnsegs = 0;
fcount = 0;
diag_prev = NULL;
#endif /* DIAGNOSTIC */
TAILQ_FOREACH(found, result, pageq) {
atomic_clearbits_int(&found->pg_flags, PG_PMAPMASK);
if (found->pg_flags & PG_ZERO) {
uvm_lock_fpageq();
uvmexp.zeropages--;
if (uvmexp.zeropages < UVM_PAGEZERO_TARGET)
wakeup(&uvmexp.zeropages);
uvm_unlock_fpageq();
}
if (flags & UVM_PLA_ZERO) {
if (found->pg_flags & PG_ZERO)
uvmexp.pga_zerohit++;
else {
uvmexp.pga_zeromiss++;
uvm_pagezero(found);
}
}
atomic_clearbits_int(&found->pg_flags, PG_ZERO|PQ_FREE);
found->uobject = NULL;
found->uanon = NULL;
found->pg_version++;
/*
* Validate that the page matches range criterium.
*/
KDASSERT(start == 0 || atop(VM_PAGE_TO_PHYS(found)) >= start);
KDASSERT(end == 0 || atop(VM_PAGE_TO_PHYS(found)) < end);
#ifdef DIAGNOSTIC
/*
* Update fcount (# found pages) and
* fnsegs (# found segments) counters.
*/
if (diag_prev == NULL ||
/* new segment if it contains a hole */
atop(VM_PAGE_TO_PHYS(diag_prev)) + 1 !=
atop(VM_PAGE_TO_PHYS(found)) ||
/* new segment if it crosses boundary */
(atop(VM_PAGE_TO_PHYS(diag_prev)) & ~(boundary - 1)) !=
(atop(VM_PAGE_TO_PHYS(found)) & ~(boundary - 1)))
fnsegs++;
fcount++;
diag_prev = found;
#endif /* DIAGNOSTIC */
}
#ifdef DIAGNOSTIC
/*
* Panic on algorithm failure.
*/
if (fcount != count || fnsegs > maxseg) {
panic("pmemrange allocation error: "
"allocated %ld pages in %d segments, "
"but request was %ld pages in %d segments",
fcount, fnsegs, count, maxseg);
}
#endif /* DIAGNOSTIC */
return 0;
}
/*
* Free a number of contig pages (invoked by uvm_page_init).
*/
void
uvm_pmr_freepages(struct vm_page *pg, psize_t count)
{
struct uvm_pmemrange *pmr;
psize_t i, pmr_count;
struct vm_page *firstpg = pg;
for (i = 0; i < count; i++) {
KASSERT(atop(VM_PAGE_TO_PHYS(&pg[i])) ==
atop(VM_PAGE_TO_PHYS(pg)) + i);
if (!((pg[i].pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg[i].pg_flags))) {
printf("Flags: 0x%x, will panic now.\n",
pg[i].pg_flags);
}
KASSERT((pg[i].pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg[i].pg_flags));
atomic_setbits_int(&pg[i].pg_flags, PQ_FREE);
atomic_clearbits_int(&pg[i].pg_flags, PG_ZERO);
}
uvm_lock_fpageq();
for (i = count; i > 0; i -= pmr_count) {
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(pg)));
KASSERT(pmr != NULL);
pmr_count = MIN(i, pmr->high - atop(VM_PAGE_TO_PHYS(pg)));
pg->fpgsz = pmr_count;
uvm_pmr_insert(pmr, pg, 0);
uvmexp.free += pmr_count;
pg += pmr_count;
}
wakeup(&uvmexp.free);
if (uvmexp.zeropages < UVM_PAGEZERO_TARGET)
wakeup(&uvmexp.zeropages);
uvm_wakeup_pla(VM_PAGE_TO_PHYS(firstpg), ptoa(count));
uvm_unlock_fpageq();
}
/*
* Free all pages in the queue.
*/
void
uvm_pmr_freepageq(struct pglist *pgl)
{
struct vm_page *pg;
paddr_t pstart;
psize_t plen;
TAILQ_FOREACH(pg, pgl, pageq) {
if (!((pg->pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg->pg_flags))) {
printf("Flags: 0x%x, will panic now.\n",
pg->pg_flags);
}
KASSERT((pg->pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg->pg_flags));
atomic_setbits_int(&pg->pg_flags, PQ_FREE);
atomic_clearbits_int(&pg->pg_flags, PG_ZERO);
}
uvm_lock_fpageq();
while (!TAILQ_EMPTY(pgl)) {
pg = TAILQ_FIRST(pgl);
if (pg == TAILQ_NEXT(pg, pageq) + 1) {
/*
* If pg is one behind the position of the
* next page in the list in the page array,
* try going backwards instead of forward.
*/
plen = uvm_pmr_remove_1strange_reverse(pgl, &pstart);
} else {
pstart = VM_PAGE_TO_PHYS(TAILQ_FIRST(pgl));
plen = uvm_pmr_remove_1strange(pgl, 0, NULL, 0);
}
uvmexp.free += plen;
uvm_wakeup_pla(pstart, ptoa(plen));
}
wakeup(&uvmexp.free);
if (uvmexp.zeropages < UVM_PAGEZERO_TARGET)
wakeup(&uvmexp.zeropages);
uvm_unlock_fpageq();
return;
}
/*
* Store a pmemrange in the list.
*
* The list is sorted by use.
*/
struct uvm_pmemrange *
uvm_pmemrange_use_insert(struct uvm_pmemrange_use *useq,
struct uvm_pmemrange *pmr)
{
struct uvm_pmemrange *iter;
int cmp = 1;
TAILQ_FOREACH(iter, useq, pmr_use) {
cmp = uvm_pmemrange_use_cmp(pmr, iter);
if (cmp == 0)
return iter;
if (cmp == -1)
break;
}
if (iter == NULL)
TAILQ_INSERT_TAIL(useq, pmr, pmr_use);
else
TAILQ_INSERT_BEFORE(iter, pmr, pmr_use);
return NULL;
}
#ifdef DEBUG
/*
* Validation of the whole pmemrange.
* Called with fpageq locked.
*/
void
uvm_pmr_assertvalid(struct uvm_pmemrange *pmr)
{
struct vm_page *prev, *next, *i, *xref;
int lcv, mti;
/* Empty range */
if (pmr->nsegs == 0)
return;
/* Validate address tree. */
RBT_FOREACH(i, uvm_pmr_addr, &pmr->addr) {
/* Validate the range. */
KASSERT(i->fpgsz > 0);
KASSERT(atop(VM_PAGE_TO_PHYS(i)) >= pmr->low);
KASSERT(atop(VM_PAGE_TO_PHYS(i)) + i->fpgsz
<= pmr->high);
/* Validate each page in this range. */
for (lcv = 0; lcv < i->fpgsz; lcv++) {
/*
* Only the first page has a size specification.
* Rest is size 0.
*/
KASSERT(lcv == 0 || i[lcv].fpgsz == 0);
/*
* Flag check.
*/
KASSERT(VALID_FLAGS(i[lcv].pg_flags) &&
(i[lcv].pg_flags & PQ_FREE) == PQ_FREE);
/*
* Free pages are:
* - not wired
* - have no vm_anon
* - have no uvm_object
*/
KASSERT(i[lcv].wire_count == 0);
KASSERT(i[lcv].uanon == (void*)0xdeadbeef ||
i[lcv].uanon == NULL);
KASSERT(i[lcv].uobject == (void*)0xdeadbeef ||
i[lcv].uobject == NULL);
/*
* Pages in a single range always have the same
* memtype.
*/
KASSERT(uvm_pmr_pg_to_memtype(&i[0]) ==
uvm_pmr_pg_to_memtype(&i[lcv]));
}
/* Check that it shouldn't be joined with its predecessor. */
prev = RBT_PREV(uvm_pmr_addr, i);
if (prev != NULL) {
KASSERT(uvm_pmr_pg_to_memtype(i) !=
uvm_pmr_pg_to_memtype(prev) ||
atop(VM_PAGE_TO_PHYS(i)) >
atop(VM_PAGE_TO_PHYS(prev)) + prev->fpgsz ||
prev + prev->fpgsz != i);
}
/* Assert i is in the size tree as well. */
if (i->fpgsz == 1) {
TAILQ_FOREACH(xref,
&pmr->single[uvm_pmr_pg_to_memtype(i)], pageq) {
if (xref == i)
break;
}
KASSERT(xref == i);
} else {
KASSERT(RBT_FIND(uvm_pmr_size,
&pmr->size[uvm_pmr_pg_to_memtype(i)], i + 1) ==
i + 1);
}
}
/* Validate size tree. */
for (mti = 0; mti < UVM_PMR_MEMTYPE_MAX; mti++) {
for (i = uvm_pmr_nfindsz(pmr, 1, mti); i != NULL; i = next) {
next = uvm_pmr_nextsz(pmr, i, mti);
if (next != NULL) {
KASSERT(i->fpgsz <=
next->fpgsz);
}
/* Assert i is in the addr tree as well. */
KASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, i) == i);
/* Assert i is of the correct memory type. */
KASSERT(uvm_pmr_pg_to_memtype(i) == mti);
}
}
/* Validate nsegs statistic. */
lcv = 0;
RBT_FOREACH(i, uvm_pmr_addr, &pmr->addr)
lcv++;
KASSERT(pmr->nsegs == lcv);
}
#endif /* DEBUG */
/*
* Split pmr at split point pageno.
* Called with fpageq unlocked.
*
* Split is only applied if a pmemrange spans pageno.
*/
void
uvm_pmr_split(paddr_t pageno)
{
struct uvm_pmemrange *pmr, *drain;
struct vm_page *rebuild, *prev, *next;
psize_t prev_sz;
uvm_lock_fpageq();
pmr = uvm_pmemrange_find(pageno);
if (pmr == NULL || !(pmr->low < pageno)) {
/* No split required. */
uvm_unlock_fpageq();
return;
}
KASSERT(pmr->low < pageno);
KASSERT(pmr->high > pageno);
/*
* uvm_pmr_allocpmr() calls into malloc() which in turn calls into
* uvm_kmemalloc which calls into pmemrange, making the locking
* a bit hard, so we just race!
*/
uvm_unlock_fpageq();
drain = uvm_pmr_allocpmr();
uvm_lock_fpageq();
pmr = uvm_pmemrange_find(pageno);
if (pmr == NULL || !(pmr->low < pageno)) {
/*
* We lost the race since someone else ran this or a related
* function, however this should be triggered very rarely so
* we just leak the pmr.
*/
printf("uvm_pmr_split: lost one pmr\n");
uvm_unlock_fpageq();
return;
}
drain->low = pageno;
drain->high = pmr->high;
drain->use = pmr->use;
uvm_pmr_assertvalid(pmr);
uvm_pmr_assertvalid(drain);
KASSERT(drain->nsegs == 0);
RBT_FOREACH(rebuild, uvm_pmr_addr, &pmr->addr) {
if (atop(VM_PAGE_TO_PHYS(rebuild)) >= pageno)
break;
}
if (rebuild == NULL)
prev = RBT_MAX(uvm_pmr_addr, &pmr->addr);
else
prev = RBT_PREV(uvm_pmr_addr, rebuild);
KASSERT(prev == NULL || atop(VM_PAGE_TO_PHYS(prev)) < pageno);
/*
* Handle free chunk that spans the split point.
*/
if (prev != NULL &&
atop(VM_PAGE_TO_PHYS(prev)) + prev->fpgsz > pageno) {
psize_t before, after;
KASSERT(atop(VM_PAGE_TO_PHYS(prev)) < pageno);
uvm_pmr_remove(pmr, prev);
prev_sz = prev->fpgsz;
before = pageno - atop(VM_PAGE_TO_PHYS(prev));
after = atop(VM_PAGE_TO_PHYS(prev)) + prev_sz - pageno;
KASSERT(before > 0);
KASSERT(after > 0);
prev->fpgsz = before;
uvm_pmr_insert(pmr, prev, 1);
(prev + before)->fpgsz = after;
uvm_pmr_insert(drain, prev + before, 1);
}
/* Move free chunks that no longer fall in the range. */
for (; rebuild != NULL; rebuild = next) {
next = RBT_NEXT(uvm_pmr_addr, rebuild);
uvm_pmr_remove(pmr, rebuild);
uvm_pmr_insert(drain, rebuild, 1);
}
pmr->high = pageno;
uvm_pmr_assertvalid(pmr);
uvm_pmr_assertvalid(drain);
RBT_INSERT(uvm_pmemrange_addr, &uvm.pmr_control.addr, drain);
uvm_pmemrange_use_insert(&uvm.pmr_control.use, drain);
uvm_unlock_fpageq();
}
/*
* Increase the usage counter for the given range of memory.
*
* The more usage counters a given range of memory has, the more will be
* attempted not to allocate from it.
*
* Addresses here are in paddr_t, not page-numbers.
* The lowest and highest allowed address are specified.
*/
void
uvm_pmr_use_inc(paddr_t low, paddr_t high)
{
struct uvm_pmemrange *pmr;
paddr_t sz;
/* pmr uses page numbers, translate low and high. */
high++;
high = atop(trunc_page(high));
low = atop(round_page(low));
uvm_pmr_split(low);
uvm_pmr_split(high);
sz = 0;
uvm_lock_fpageq();
/* Increase use count on segments in range. */
RBT_FOREACH(pmr, uvm_pmemrange_addr, &uvm.pmr_control.addr) {
if (PMR_IS_SUBRANGE_OF(pmr->low, pmr->high, low, high)) {
TAILQ_REMOVE(&uvm.pmr_control.use, pmr, pmr_use);
pmr->use++;
sz += pmr->high - pmr->low;
uvm_pmemrange_use_insert(&uvm.pmr_control.use, pmr);
}
uvm_pmr_assertvalid(pmr);
}
uvm_unlock_fpageq();
KASSERT(sz >= high - low);
}
/*
* Allocate a pmemrange.
*
* If called from uvm_page_init, the uvm_pageboot_alloc is used.
* If called after uvm_init, malloc is used.
* (And if called in between, you're dead.)
*/
struct uvm_pmemrange *
uvm_pmr_allocpmr(void)
{
struct uvm_pmemrange *nw;
int i;
/* We're only ever hitting the !uvm.page_init_done case for now. */
if (!uvm.page_init_done) {
nw = (struct uvm_pmemrange *)
uvm_pageboot_alloc(sizeof(struct uvm_pmemrange));
} else {
nw = malloc(sizeof(struct uvm_pmemrange),
M_VMMAP, M_NOWAIT);
}
KASSERT(nw != NULL);
memset(nw, 0, sizeof(struct uvm_pmemrange));
RBT_INIT(uvm_pmr_addr, &nw->addr);
for (i = 0; i < UVM_PMR_MEMTYPE_MAX; i++) {
RBT_INIT(uvm_pmr_size, &nw->size[i]);
TAILQ_INIT(&nw->single[i]);
}
return nw;
}
/*
* Initialization of pmr.
* Called by uvm_page_init.
*
* Sets up pmemranges.
*/
void
uvm_pmr_init(void)
{
struct uvm_pmemrange *new_pmr;
int i;
TAILQ_INIT(&uvm.pmr_control.use);
RBT_INIT(uvm_pmemrange_addr, &uvm.pmr_control.addr);
TAILQ_INIT(&uvm.pmr_control.allocs);
/* By default, one range for the entire address space. */
new_pmr = uvm_pmr_allocpmr();
new_pmr->low = 0;
new_pmr->high = atop((paddr_t)-1) + 1;
RBT_INSERT(uvm_pmemrange_addr, &uvm.pmr_control.addr, new_pmr);
uvm_pmemrange_use_insert(&uvm.pmr_control.use, new_pmr);
for (i = 0; uvm_md_constraints[i] != NULL; i++) {
uvm_pmr_use_inc(uvm_md_constraints[i]->ucr_low,
uvm_md_constraints[i]->ucr_high);
}
}
/*
* Find the pmemrange that contains the given page number.
*
* (Manually traverses the binary tree, because that is cheaper on stack
* usage.)
*/
struct uvm_pmemrange *
uvm_pmemrange_find(paddr_t pageno)
{
struct uvm_pmemrange *pmr;
pmr = RBT_ROOT(uvm_pmemrange_addr, &uvm.pmr_control.addr);
while (pmr != NULL) {
if (pmr->low > pageno)
pmr = RBT_LEFT(uvm_pmemrange_addr, pmr);
else if (pmr->high <= pageno)
pmr = RBT_RIGHT(uvm_pmemrange_addr, pmr);
else
break;
}
return pmr;
}
#if defined(DDB) || defined(DEBUG)
/*
* Return true if the given page is in any of the free lists.
* Used by uvm_page_printit.
* This function is safe, even if the page is not on the freeq.
* Note: does not apply locking, only called from ddb.
*/
int
uvm_pmr_isfree(struct vm_page *pg)
{
struct vm_page *r;
struct uvm_pmemrange *pmr;
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(pg)));
if (pmr == NULL)
return 0;
r = RBT_NFIND(uvm_pmr_addr, &pmr->addr, pg);
if (r == NULL)
r = RBT_MAX(uvm_pmr_addr, &pmr->addr);
else if (r != pg)
r = RBT_PREV(uvm_pmr_addr, r);
if (r == NULL)
return 0; /* Empty tree. */
KDASSERT(atop(VM_PAGE_TO_PHYS(r)) <= atop(VM_PAGE_TO_PHYS(pg)));
return atop(VM_PAGE_TO_PHYS(r)) + r->fpgsz >
atop(VM_PAGE_TO_PHYS(pg));
}
#endif /* DEBUG */
/*
* Given a root of a tree, find a range which intersects start, end and
* is of the same memtype.
*
* Page must be in the address tree.
*/
struct vm_page*
uvm_pmr_rootupdate(struct uvm_pmemrange *pmr, struct vm_page *init_root,
paddr_t start, paddr_t end, int memtype)
{
int direction;
struct vm_page *root;
struct vm_page *high, *high_next;
struct vm_page *low, *low_next;
KDASSERT(pmr != NULL && init_root != NULL);
root = init_root;
/* Which direction to use for searching. */
if (start != 0 && atop(VM_PAGE_TO_PHYS(root)) + root->fpgsz <= start)
direction = 1;
else if (end != 0 && atop(VM_PAGE_TO_PHYS(root)) >= end)
direction = -1;
else /* nothing to do */
return root;
/* First, update root to fall within the chosen range. */
while (root && !PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(root)),
atop(VM_PAGE_TO_PHYS(root)) + root->fpgsz,
start, end)) {
if (direction == 1)
root = RBT_RIGHT(uvm_objtree, root);
else
root = RBT_LEFT(uvm_objtree, root);
}
if (root == NULL || uvm_pmr_pg_to_memtype(root) == memtype)
return root;
/*
* Root is valid, but of the wrong memtype.
*
* Try to find a range that has the given memtype in the subtree
* (memtype mismatches are costly, either because the conversion
* is expensive, or a later allocation will need to do the opposite
* conversion, which will be expensive).
*
*
* First, simply increase address until we hit something we can use.
* Cache the upper page, so we can page-walk later.
*/
high = root;
high_next = RBT_RIGHT(uvm_objtree, high);
while (high_next != NULL && PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(high_next)),
atop(VM_PAGE_TO_PHYS(high_next)) + high_next->fpgsz,
start, end)) {
high = high_next;
if (uvm_pmr_pg_to_memtype(high) == memtype)
return high;
high_next = RBT_RIGHT(uvm_objtree, high);
}
/*
* Second, decrease the address until we hit something we can use.
* Cache the lower page, so we can page-walk later.
*/
low = root;
low_next = RBT_LEFT(uvm_objtree, low);
while (low_next != NULL && PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(low_next)),
atop(VM_PAGE_TO_PHYS(low_next)) + low_next->fpgsz,
start, end)) {
low = low_next;
if (uvm_pmr_pg_to_memtype(low) == memtype)
return low;
low_next = RBT_LEFT(uvm_objtree, low);
}
if (low == high)
return NULL;
/* No hits. Walk the address tree until we find something usable. */
for (low = RBT_NEXT(uvm_pmr_addr, low);
low != high;
low = RBT_NEXT(uvm_pmr_addr, low)) {
KDASSERT(PMR_IS_SUBRANGE_OF(atop(VM_PAGE_TO_PHYS(low)),
atop(VM_PAGE_TO_PHYS(low)) + low->fpgsz,
start, end));
if (uvm_pmr_pg_to_memtype(low) == memtype)
return low;
}
/* Nothing found. */
return NULL;
}
/*
* Allocate any page, the fastest way. Page number constraints only.
*/
psize_t
uvm_pmr_get1page(psize_t count, int memtype_init, struct pglist *result,
paddr_t start, paddr_t end, int memtype_only)
{
struct uvm_pmemrange *pmr;
struct vm_page *found, *splitpg;
psize_t fcount;
int memtype;
fcount = 0;
TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
/* We're done. */
if (fcount == count)
break;
/* Outside requested range. */
if (!(start == 0 && end == 0) &&
!PMR_INTERSECTS_WITH(pmr->low, pmr->high, start, end))
continue;
/* Range is empty. */
if (pmr->nsegs == 0)
continue;
/* Loop over all memtypes, starting at memtype_init. */
memtype = memtype_init;
while (fcount != count) {
found = TAILQ_FIRST(&pmr->single[memtype]);
/*
* If found is outside the range, walk the list
* until we find something that intersects with
* boundaries.
*/
while (found && !PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(found)),
atop(VM_PAGE_TO_PHYS(found)) + 1,
start, end))
found = TAILQ_NEXT(found, pageq);
if (found == NULL) {
/*
* Check if the size tree contains a range
* that intersects with the boundaries. As the
* allocation is for any page, try the smallest
* range so that large ranges are preserved for
* more constrained cases. Only one entry is
* checked here, to avoid a brute-force search.
*
* Note that a size tree gives pg[1] instead of
* pg[0].
*/
found = RBT_MIN(uvm_pmr_size,
&pmr->size[memtype]);
if (found != NULL) {
found--;
if (!PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(found)),
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz, start, end))
found = NULL;
}
}
if (found == NULL) {
/*
* Try address-guided search to meet the page
* number constraints.
*/
found = RBT_ROOT(uvm_pmr_addr, &pmr->addr);
if (found != NULL) {
found = uvm_pmr_rootupdate(pmr, found,
start, end, memtype);
}
}
if (found != NULL) {
uvm_pmr_assertvalid(pmr);
uvm_pmr_remove_size(pmr, found);
/*
* If the page intersects the end, then it'll
* need splitting.
*
* Note that we don't need to split if the page
* intersects start: the drain function will
* simply stop on hitting start.
*/
if (end != 0 && atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz > end) {
psize_t splitsz =
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz - end;
uvm_pmr_remove_addr(pmr, found);
uvm_pmr_assertvalid(pmr);
found->fpgsz -= splitsz;
splitpg = found + found->fpgsz;
splitpg->fpgsz = splitsz;
uvm_pmr_insert(pmr, splitpg, 1);
/*
* At this point, splitpg and found
* actually should be joined.
* But we explicitly disable that,
* because we will start subtracting
* from found.
*/
KASSERT(start == 0 ||
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz > start);
uvm_pmr_insert_addr(pmr, found, 1);
}
/*
* Fetch pages from the end.
* If the range is larger than the requested
* number of pages, this saves us an addr-tree
* update.
*
* Since we take from the end and insert at
* the head, any ranges keep preserved.
*/
while (found->fpgsz > 0 && fcount < count &&
(start == 0 ||
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz > start)) {
found->fpgsz--;
fcount++;
TAILQ_INSERT_HEAD(result,
&found[found->fpgsz], pageq);
}
if (found->fpgsz > 0) {
uvm_pmr_insert_size(pmr, found);
KDASSERT(fcount == count);
uvm_pmr_assertvalid(pmr);
return fcount;
}
/*
* Delayed addr-tree removal.
*/
uvm_pmr_remove_addr(pmr, found);
uvm_pmr_assertvalid(pmr);
} else {
if (memtype_only)
break;
/*
* Skip to the next memtype.
*/
memtype += 1;
if (memtype == UVM_PMR_MEMTYPE_MAX)
memtype = 0;
if (memtype == memtype_init)
break;
}
}
}
/*
* Search finished.
*
* Ran out of ranges before enough pages were gathered, or we hit the
* case where found->fpgsz == count - fcount, in which case the
* above exit condition didn't trigger.
*
* On failure, caller will free the pages.
*/
return fcount;
}
#ifdef DDB
/*
* Print information about pmemrange.
* Does not do locking (so either call it from DDB or acquire fpageq lock
* before invoking.
*/
void
uvm_pmr_print(void)
{
struct uvm_pmemrange *pmr;
struct vm_page *pg;
psize_t size[UVM_PMR_MEMTYPE_MAX];
psize_t free;
int useq_len;
int mt;
printf("Ranges, use queue:\n");
useq_len = 0;
TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
useq_len++;
free = 0;
for (mt = 0; mt < UVM_PMR_MEMTYPE_MAX; mt++) {
pg = RBT_MAX(uvm_pmr_size, &pmr->size[mt]);
if (pg != NULL)
pg--;
else
pg = TAILQ_FIRST(&pmr->single[mt]);
size[mt] = (pg == NULL ? 0 : pg->fpgsz);
RBT_FOREACH(pg, uvm_pmr_addr, &pmr->addr)
free += pg->fpgsz;
}
printf("* [0x%lx-0x%lx] use=%d nsegs=%ld",
(unsigned long)pmr->low, (unsigned long)pmr->high,
pmr->use, (unsigned long)pmr->nsegs);
for (mt = 0; mt < UVM_PMR_MEMTYPE_MAX; mt++) {
printf(" maxsegsz[%d]=0x%lx", mt,
(unsigned long)size[mt]);
}
printf(" free=0x%lx\n", (unsigned long)free);
}
printf("#ranges = %d\n", useq_len);
}
#endif
/*
* uvm_wait_pla: wait (sleep) for the page daemon to free some pages
* in a specific physmem area.
*
* Returns ENOMEM if the pagedaemon failed to free any pages.
* If not failok, failure will lead to panic.
*
* Must be called with fpageq locked.
*/
int
uvm_wait_pla(paddr_t low, paddr_t high, paddr_t size, int failok)
{
struct uvm_pmalloc pma;
const char *wmsg = "pmrwait";
if (curproc == uvm.pagedaemon_proc) {
/*
* This is not that uncommon when the pagedaemon is trying
* to flush out a large mmapped file. VOP_WRITE will circle
* back through the buffer cache and try to get more memory.
* The pagedaemon starts by calling bufbackoff, but we can
* easily use up that reserve in a single scan iteration.
*/
uvm_unlock_fpageq();
if (bufbackoff(NULL, atop(size)) == 0) {
uvm_lock_fpageq();
return 0;
}
uvm_lock_fpageq();
/*
* XXX detect pagedaemon deadlock - see comment in
* uvm_wait(), as this is exactly the same issue.
*/
printf("pagedaemon: wait_pla deadlock detected!\n");
msleep_nsec(&uvmexp.free, &uvm.fpageqlock, PVM, wmsg,
MSEC_TO_NSEC(125));
#if defined(DEBUG)
/* DEBUG: panic so we can debug it */
panic("wait_pla pagedaemon deadlock");
#endif
return 0;
}
for (;;) {
pma.pm_constraint.ucr_low = low;
pma.pm_constraint.ucr_high = high;
pma.pm_size = size;
pma.pm_flags = UVM_PMA_LINKED;
TAILQ_INSERT_TAIL(&uvm.pmr_control.allocs, &pma, pmq);
wakeup(&uvm.pagedaemon); /* wake the daemon! */
while (pma.pm_flags & (UVM_PMA_LINKED | UVM_PMA_BUSY))
msleep_nsec(&pma, &uvm.fpageqlock, PVM, wmsg, INFSLP);
if (!(pma.pm_flags & UVM_PMA_FREED) &&
pma.pm_flags & UVM_PMA_FAIL) {
if (failok)
return ENOMEM;
printf("uvm_wait: failed to free %ld pages between "
"0x%lx-0x%lx\n", atop(size), low, high);
} else
return 0;
}
/* UNREACHABLE */
}
/*
* Wake up uvm_pmalloc sleepers.
*/
void
uvm_wakeup_pla(paddr_t low, psize_t len)
{
struct uvm_pmalloc *pma, *pma_next;
paddr_t high;
high = low + len;
/* Wake specific allocations waiting for this memory. */
for (pma = TAILQ_FIRST(&uvm.pmr_control.allocs); pma != NULL;
pma = pma_next) {
pma_next = TAILQ_NEXT(pma, pmq);
if (low < pma->pm_constraint.ucr_high &&
high > pma->pm_constraint.ucr_low) {
pma->pm_flags |= UVM_PMA_FREED;
if (!(pma->pm_flags & UVM_PMA_BUSY)) {
pma->pm_flags &= ~UVM_PMA_LINKED;
TAILQ_REMOVE(&uvm.pmr_control.allocs, pma,
pmq);
wakeup(pma);
}
}
}
}
void
uvm_pagezero_thread(void *arg)
{
struct pglist pgl;
struct vm_page *pg;
int count;
/* Run at the lowest possible priority. */
curproc->p_p->ps_nice = NZERO + PRIO_MAX;
KERNEL_UNLOCK();
TAILQ_INIT(&pgl);
for (;;) {
uvm_lock_fpageq();
while (uvmexp.zeropages >= UVM_PAGEZERO_TARGET ||
(count = uvm_pmr_get1page(16, UVM_PMR_MEMTYPE_DIRTY,
&pgl, 0, 0, 1)) == 0) {
msleep_nsec(&uvmexp.zeropages, &uvm.fpageqlock,
MAXPRI, "pgzero", INFSLP);
}
uvm_unlock_fpageq();
TAILQ_FOREACH(pg, &pgl, pageq) {
uvm_pagezero(pg);
atomic_setbits_int(&pg->pg_flags, PG_ZERO);
}
uvm_lock_fpageq();
while (!TAILQ_EMPTY(&pgl))
uvm_pmr_remove_1strange(&pgl, 0, NULL, 0);
uvmexp.zeropages += count;
uvm_unlock_fpageq();
yield();
}
}
1847
501
501
71
61
118
1398
502
1080
1078
270
362
145
137
138
1249
1251
1250
384
1078
763
72
72
252
230
71
120
215
192
214
251
5
227
72
188
166
48
94
1
1
1818
1857
650
654
13
670
666
10
665
32
32
6
31
6
28
2
1733
1730
2
1728
1842
1140
1858
32
20
574
501
218
1599
4
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
/* $OpenBSD: uvm_page.c,v 1.170 2022/08/29 02:58:13 jsg Exp $ */
/* $NetBSD: uvm_page.c,v 1.44 2000/11/27 08:40:04 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993, The Regents of the University of California.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* The Mach Operating System project at Carnegie-Mellon University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_page.c 8.3 (Berkeley) 3/21/94
* from: Id: uvm_page.c,v 1.1.2.18 1998/02/06 05:24:42 chs Exp
*
*
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* All rights reserved.
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* uvm_page.c: page ops.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sched.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/smr.h>
#include <uvm/uvm.h>
/*
* for object trees
*/
RBT_GENERATE(uvm_objtree, vm_page, objt, uvm_pagecmp);
int
uvm_pagecmp(const struct vm_page *a, const struct vm_page *b)
{
return a->offset < b->offset ? -1 : a->offset > b->offset;
}
/*
* global vars... XXXCDC: move to uvm. structure.
*/
/*
* physical memory config is stored in vm_physmem.
*/
struct vm_physseg vm_physmem[VM_PHYSSEG_MAX]; /* XXXCDC: uvm.physmem */
int vm_nphysseg = 0; /* XXXCDC: uvm.nphysseg */
/*
* Some supported CPUs in a given architecture don't support all
* of the things necessary to do idle page zero'ing efficiently.
* We therefore provide a way to disable it from machdep code here.
*/
/*
* local variables
*/
/*
* these variables record the values returned by vm_page_bootstrap,
* for debugging purposes. The implementation of uvm_pageboot_alloc
* and pmap_startup here also uses them internally.
*/
static vaddr_t virtual_space_start;
static vaddr_t virtual_space_end;
/*
* local prototypes
*/
static void uvm_pageinsert(struct vm_page *);
static void uvm_pageremove(struct vm_page *);
int uvm_page_owner_locked_p(struct vm_page *);
/*
* inline functions
*/
/*
* uvm_pageinsert: insert a page in the object
*
* => caller must lock object
* => call should have already set pg's object and offset pointers
* and bumped the version counter
*/
static inline void
uvm_pageinsert(struct vm_page *pg)
{
struct vm_page *dupe;
KASSERT(UVM_OBJ_IS_DUMMY(pg->uobject) ||
rw_write_held(pg->uobject->vmobjlock));
KASSERT((pg->pg_flags & PG_TABLED) == 0);
dupe = RBT_INSERT(uvm_objtree, &pg->uobject->memt, pg);
/* not allowed to insert over another page */
KASSERT(dupe == NULL);
atomic_setbits_int(&pg->pg_flags, PG_TABLED);
pg->uobject->uo_npages++;
}
/*
* uvm_page_remove: remove page from object
*
* => caller must lock object
*/
static inline void
uvm_pageremove(struct vm_page *pg)
{
KASSERT(UVM_OBJ_IS_DUMMY(pg->uobject) ||
rw_write_held(pg->uobject->vmobjlock));
KASSERT(pg->pg_flags & PG_TABLED);
RBT_REMOVE(uvm_objtree, &pg->uobject->memt, pg);
atomic_clearbits_int(&pg->pg_flags, PG_TABLED);
pg->uobject->uo_npages--;
pg->uobject = NULL;
pg->pg_version++;
}
/*
* uvm_page_init: init the page system. called from uvm_init().
*
* => we return the range of kernel virtual memory in kvm_startp/kvm_endp
*/
void
uvm_page_init(vaddr_t *kvm_startp, vaddr_t *kvm_endp)
{
vsize_t freepages, pagecount, n;
vm_page_t pagearray, curpg;
int lcv, i;
paddr_t paddr, pgno;
struct vm_physseg *seg;
/*
* init the page queues and page queue locks
*/
TAILQ_INIT(&uvm.page_active);
TAILQ_INIT(&uvm.page_inactive);
mtx_init(&uvm.pageqlock, IPL_VM);
mtx_init(&uvm.fpageqlock, IPL_VM);
uvm_pmr_init();
/*
* allocate vm_page structures.
*/
/*
* sanity check:
* before calling this function the MD code is expected to register
* some free RAM with the uvm_page_physload() function. our job
* now is to allocate vm_page structures for this memory.
*/
if (vm_nphysseg == 0)
panic("uvm_page_bootstrap: no memory pre-allocated");
/*
* first calculate the number of free pages...
*
* note that we use start/end rather than avail_start/avail_end.
* this allows us to allocate extra vm_page structures in case we
* want to return some memory to the pool after booting.
*/
freepages = 0;
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
freepages += (seg->end - seg->start);
/*
* we now know we have (PAGE_SIZE * freepages) bytes of memory we can
* use. for each page of memory we use we need a vm_page structure.
* thus, the total number of pages we can use is the total size of
* the memory divided by the PAGE_SIZE plus the size of the vm_page
* structure. we add one to freepages as a fudge factor to avoid
* truncation errors (since we can only allocate in terms of whole
* pages).
*/
pagecount = (((paddr_t)freepages + 1) << PAGE_SHIFT) /
(PAGE_SIZE + sizeof(struct vm_page));
pagearray = (vm_page_t)uvm_pageboot_alloc(pagecount *
sizeof(struct vm_page));
memset(pagearray, 0, pagecount * sizeof(struct vm_page));
/* init the vm_page structures and put them in the correct place. */
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++) {
n = seg->end - seg->start;
if (n > pagecount) {
panic("uvm_page_init: lost %ld page(s) in init",
(long)(n - pagecount));
/* XXXCDC: shouldn't happen? */
/* n = pagecount; */
}
/* set up page array pointers */
seg->pgs = pagearray;
pagearray += n;
pagecount -= n;
seg->lastpg = seg->pgs + (n - 1);
/* init and free vm_pages (we've already zeroed them) */
pgno = seg->start;
paddr = ptoa(pgno);
for (i = 0, curpg = seg->pgs; i < n;
i++, curpg++, pgno++, paddr += PAGE_SIZE) {
curpg->phys_addr = paddr;
VM_MDPAGE_INIT(curpg);
if (pgno >= seg->avail_start &&
pgno < seg->avail_end) {
uvmexp.npages++;
}
}
/* Add pages to free pool. */
uvm_pmr_freepages(&seg->pgs[seg->avail_start - seg->start],
seg->avail_end - seg->avail_start);
}
/*
* pass up the values of virtual_space_start and
* virtual_space_end (obtained by uvm_pageboot_alloc) to the upper
* layers of the VM.
*/
*kvm_startp = round_page(virtual_space_start);
*kvm_endp = trunc_page(virtual_space_end);
/* init locks for kernel threads */
mtx_init(&uvm.aiodoned_lock, IPL_BIO);
/*
* init reserve thresholds
* XXXCDC - values may need adjusting
*/
uvmexp.reserve_pagedaemon = 4;
uvmexp.reserve_kernel = 8;
uvmexp.anonminpct = 10;
uvmexp.vnodeminpct = 10;
uvmexp.vtextminpct = 5;
uvmexp.anonmin = uvmexp.anonminpct * 256 / 100;
uvmexp.vnodemin = uvmexp.vnodeminpct * 256 / 100;
uvmexp.vtextmin = uvmexp.vtextminpct * 256 / 100;
uvm.page_init_done = TRUE;
}
/*
* uvm_setpagesize: set the page size
*
* => sets page_shift and page_mask from uvmexp.pagesize.
*/
void
uvm_setpagesize(void)
{
if (uvmexp.pagesize == 0)
uvmexp.pagesize = DEFAULT_PAGE_SIZE;
uvmexp.pagemask = uvmexp.pagesize - 1;
if ((uvmexp.pagemask & uvmexp.pagesize) != 0)
panic("uvm_setpagesize: page size not a power of two");
for (uvmexp.pageshift = 0; ; uvmexp.pageshift++)
if ((1 << uvmexp.pageshift) == uvmexp.pagesize)
break;
}
/*
* uvm_pageboot_alloc: steal memory from physmem for bootstrapping
*/
vaddr_t
uvm_pageboot_alloc(vsize_t size)
{
#if defined(PMAP_STEAL_MEMORY)
vaddr_t addr;
/*
* defer bootstrap allocation to MD code (it may want to allocate
* from a direct-mapped segment). pmap_steal_memory should round
* off virtual_space_start/virtual_space_end.
*/
addr = pmap_steal_memory(size, &virtual_space_start,
&virtual_space_end);
return addr;
#else /* !PMAP_STEAL_MEMORY */
static boolean_t initialized = FALSE;
vaddr_t addr, vaddr;
paddr_t paddr;
/* round to page size */
size = round_page(size);
/* on first call to this function, initialize ourselves. */
if (initialized == FALSE) {
pmap_virtual_space(&virtual_space_start, &virtual_space_end);
/* round it the way we like it */
virtual_space_start = round_page(virtual_space_start);
virtual_space_end = trunc_page(virtual_space_end);
initialized = TRUE;
}
/* allocate virtual memory for this request */
if (virtual_space_start == virtual_space_end ||
(virtual_space_end - virtual_space_start) < size)
panic("uvm_pageboot_alloc: out of virtual space");
addr = virtual_space_start;
#ifdef PMAP_GROWKERNEL
/*
* If the kernel pmap can't map the requested space,
* then allocate more resources for it.
*/
if (uvm_maxkaddr < (addr + size)) {
uvm_maxkaddr = pmap_growkernel(addr + size);
if (uvm_maxkaddr < (addr + size))
panic("uvm_pageboot_alloc: pmap_growkernel() failed");
}
#endif
virtual_space_start += size;
/* allocate and mapin physical pages to back new virtual pages */
for (vaddr = round_page(addr) ; vaddr < addr + size ;
vaddr += PAGE_SIZE) {
if (!uvm_page_physget(&paddr))
panic("uvm_pageboot_alloc: out of memory");
/*
* Note this memory is no longer managed, so using
* pmap_kenter is safe.
*/
pmap_kenter_pa(vaddr, paddr, PROT_READ | PROT_WRITE);
}
pmap_update(pmap_kernel());
return addr;
#endif /* PMAP_STEAL_MEMORY */
}
#if !defined(PMAP_STEAL_MEMORY)
/*
* uvm_page_physget: "steal" one page from the vm_physmem structure.
*
* => attempt to allocate it off the end of a segment in which the "avail"
* values match the start/end values. if we can't do that, then we
* will advance both values (making them equal, and removing some
* vm_page structures from the non-avail area).
* => return false if out of memory.
*/
boolean_t
uvm_page_physget(paddr_t *paddrp)
{
int lcv;
struct vm_physseg *seg;
/* pass 1: try allocating from a matching end */
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST) || \
(VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
for (lcv = vm_nphysseg - 1, seg = vm_physmem + lcv; lcv >= 0;
lcv--, seg--)
#else
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
#endif
{
if (uvm.page_init_done == TRUE)
panic("uvm_page_physget: called _after_ bootstrap");
/* try from front */
if (seg->avail_start == seg->start &&
seg->avail_start < seg->avail_end) {
*paddrp = ptoa(seg->avail_start);
seg->avail_start++;
seg->start++;
/* nothing left? nuke it */
if (seg->avail_start == seg->end) {
if (vm_nphysseg == 1)
panic("uvm_page_physget: out of memory!");
vm_nphysseg--;
for (; lcv < vm_nphysseg; lcv++, seg++)
/* structure copy */
seg[0] = seg[1];
}
return TRUE;
}
/* try from rear */
if (seg->avail_end == seg->end &&
seg->avail_start < seg->avail_end) {
*paddrp = ptoa(seg->avail_end - 1);
seg->avail_end--;
seg->end--;
/* nothing left? nuke it */
if (seg->avail_end == seg->start) {
if (vm_nphysseg == 1)
panic("uvm_page_physget: out of memory!");
vm_nphysseg--;
for (; lcv < vm_nphysseg ; lcv++, seg++)
/* structure copy */
seg[0] = seg[1];
}
return TRUE;
}
}
/* pass2: forget about matching ends, just allocate something */
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST) || \
(VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
for (lcv = vm_nphysseg - 1, seg = vm_physmem + lcv; lcv >= 0;
lcv--, seg--)
#else
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
#endif
{
/* any room in this bank? */
if (seg->avail_start >= seg->avail_end)
continue; /* nope */
*paddrp = ptoa(seg->avail_start);
seg->avail_start++;
/* truncate! */
seg->start = seg->avail_start;
/* nothing left? nuke it */
if (seg->avail_start == seg->end) {
if (vm_nphysseg == 1)
panic("uvm_page_physget: out of memory!");
vm_nphysseg--;
for (; lcv < vm_nphysseg ; lcv++, seg++)
/* structure copy */
seg[0] = seg[1];
}
return TRUE;
}
return FALSE; /* whoops! */
}
#endif /* PMAP_STEAL_MEMORY */
/*
* uvm_page_physload: load physical memory into VM system
*
* => all args are PFs
* => all pages in start/end get vm_page structures
* => areas marked by avail_start/avail_end get added to the free page pool
* => we are limited to VM_PHYSSEG_MAX physical memory segments
*/
void
uvm_page_physload(paddr_t start, paddr_t end, paddr_t avail_start,
paddr_t avail_end, int flags)
{
int preload, lcv;
psize_t npages;
struct vm_page *pgs;
struct vm_physseg *ps, *seg;
#ifdef DIAGNOSTIC
if (uvmexp.pagesize == 0)
panic("uvm_page_physload: page size not set!");
if (start >= end)
panic("uvm_page_physload: start >= end");
#endif
/* do we have room? */
if (vm_nphysseg == VM_PHYSSEG_MAX) {
printf("uvm_page_physload: unable to load physical memory "
"segment\n");
printf("\t%d segments allocated, ignoring 0x%llx -> 0x%llx\n",
VM_PHYSSEG_MAX, (long long)start, (long long)end);
printf("\tincrease VM_PHYSSEG_MAX\n");
return;
}
/*
* check to see if this is a "preload" (i.e. uvm_mem_init hasn't been
* called yet, so malloc is not available).
*/
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg; lcv++, seg++) {
if (seg->pgs)
break;
}
preload = (lcv == vm_nphysseg);
/* if VM is already running, attempt to malloc() vm_page structures */
if (!preload) {
/*
* XXXCDC: need some sort of lockout for this case
* right now it is only used by devices so it should be alright.
*/
paddr_t paddr;
npages = end - start; /* # of pages */
pgs = km_alloc(round_page(npages * sizeof(*pgs)),
&kv_any, &kp_zero, &kd_waitok);
if (pgs == NULL) {
printf("uvm_page_physload: can not malloc vm_page "
"structs for segment\n");
printf("\tignoring 0x%lx -> 0x%lx\n", start, end);
return;
}
/* init phys_addr and free pages, XXX uvmexp.npages */
for (lcv = 0, paddr = ptoa(start); lcv < npages;
lcv++, paddr += PAGE_SIZE) {
pgs[lcv].phys_addr = paddr;
VM_MDPAGE_INIT(&pgs[lcv]);
if (atop(paddr) >= avail_start &&
atop(paddr) < avail_end) {
if (flags & PHYSLOAD_DEVICE) {
atomic_setbits_int(&pgs[lcv].pg_flags,
PG_DEV);
pgs[lcv].wire_count = 1;
} else {
#if defined(VM_PHYSSEG_NOADD)
panic("uvm_page_physload: tried to add RAM after vm_mem_init");
#endif
}
}
}
/* Add pages to free pool. */
if ((flags & PHYSLOAD_DEVICE) == 0) {
uvm_pmr_freepages(&pgs[avail_start - start],
avail_end - avail_start);
}
/* XXXCDC: need hook to tell pmap to rebuild pv_list, etc... */
} else {
/* gcc complains if these don't get init'd */
pgs = NULL;
npages = 0;
}
/* now insert us in the proper place in vm_physmem[] */
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_RANDOM)
/* random: put it at the end (easy!) */
ps = &vm_physmem[vm_nphysseg];
#elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
{
int x;
/* sort by address for binary search */
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg; lcv++, seg++)
if (start < seg->start)
break;
ps = seg;
/* move back other entries, if necessary ... */
for (x = vm_nphysseg, seg = vm_physmem + x - 1; x > lcv;
x--, seg--)
/* structure copy */
seg[1] = seg[0];
}
#elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST)
{
int x;
/* sort by largest segment first */
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg; lcv++, seg++)
if ((end - start) >
(seg->end - seg->start))
break;
ps = &vm_physmem[lcv];
/* move back other entries, if necessary ... */
for (x = vm_nphysseg, seg = vm_physmem + x - 1; x > lcv;
x--, seg--)
/* structure copy */
seg[1] = seg[0];
}
#else
panic("uvm_page_physload: unknown physseg strategy selected!");
#endif
ps->start = start;
ps->end = end;
ps->avail_start = avail_start;
ps->avail_end = avail_end;
if (preload) {
ps->pgs = NULL;
} else {
ps->pgs = pgs;
ps->lastpg = pgs + npages - 1;
}
vm_nphysseg++;
return;
}
#ifdef DDB /* XXXCDC: TMP TMP TMP DEBUG DEBUG DEBUG */
void uvm_page_physdump(void); /* SHUT UP GCC */
/* call from DDB */
void
uvm_page_physdump(void)
{
int lcv;
struct vm_physseg *seg;
printf("uvm_page_physdump: physical memory config [segs=%d of %d]:\n",
vm_nphysseg, VM_PHYSSEG_MAX);
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
printf("0x%llx->0x%llx [0x%llx->0x%llx]\n",
(long long)seg->start,
(long long)seg->end,
(long long)seg->avail_start,
(long long)seg->avail_end);
printf("STRATEGY = ");
switch (VM_PHYSSEG_STRAT) {
case VM_PSTRAT_RANDOM: printf("RANDOM\n"); break;
case VM_PSTRAT_BSEARCH: printf("BSEARCH\n"); break;
case VM_PSTRAT_BIGFIRST: printf("BIGFIRST\n"); break;
default: printf("<<UNKNOWN>>!!!!\n");
}
}
#endif
void
uvm_shutdown(void)
{
#ifdef UVM_SWAP_ENCRYPT
uvm_swap_finicrypt_all();
#endif
smr_flush();
}
/*
* Perform insert of a given page in the specified anon of obj.
* This is basically, uvm_pagealloc, but with the page already given.
*/
void
uvm_pagealloc_pg(struct vm_page *pg, struct uvm_object *obj, voff_t off,
struct vm_anon *anon)
{
int flags;
KASSERT(obj == NULL || anon == NULL);
KASSERT(anon == NULL || off == 0);
KASSERT(off == trunc_page(off));
KASSERT(obj == NULL || UVM_OBJ_IS_DUMMY(obj) ||
rw_write_held(obj->vmobjlock));
KASSERT(anon == NULL || anon->an_lock == NULL ||
rw_write_held(anon->an_lock));
flags = PG_BUSY | PG_FAKE;
pg->offset = off;
pg->uobject = obj;
pg->uanon = anon;
KASSERT(uvm_page_owner_locked_p(pg));
if (anon) {
anon->an_page = pg;
flags |= PQ_ANON;
} else if (obj)
uvm_pageinsert(pg);
atomic_setbits_int(&pg->pg_flags, flags);
#if defined(UVM_PAGE_TRKOWN)
pg->owner_tag = NULL;
#endif
UVM_PAGE_OWN(pg, "new alloc");
}
/*
* uvm_pglistalloc: allocate a list of pages
*
* => allocated pages are placed at the tail of rlist. rlist is
* assumed to be properly initialized by caller.
* => returns 0 on success or errno on failure
* => doesn't take into account clean non-busy pages on inactive list
* that could be used(?)
* => params:
* size the size of the allocation, rounded to page size.
* low the low address of the allowed allocation range.
* high the high address of the allowed allocation range.
* alignment memory must be aligned to this power-of-two boundary.
* boundary no segment in the allocation may cross this
* power-of-two boundary (relative to zero).
* => flags:
* UVM_PLA_NOWAIT fail if allocation fails
* UVM_PLA_WAITOK wait for memory to become avail
* UVM_PLA_ZERO return zeroed memory
*/
int
uvm_pglistalloc(psize_t size, paddr_t low, paddr_t high, paddr_t alignment,
paddr_t boundary, struct pglist *rlist, int nsegs, int flags)
{
KASSERT((alignment & (alignment - 1)) == 0);
KASSERT((boundary & (boundary - 1)) == 0);
KASSERT(!(flags & UVM_PLA_WAITOK) ^ !(flags & UVM_PLA_NOWAIT));
if (size == 0)
return EINVAL;
size = atop(round_page(size));
/*
* XXX uvm_pglistalloc is currently only used for kernel
* objects. Unlike the checks in uvm_pagealloc, below, here
* we are always allowed to use the kernel reserve.
*/
flags |= UVM_PLA_USERESERVE;
if ((high & PAGE_MASK) != PAGE_MASK) {
printf("uvm_pglistalloc: Upper boundary 0x%lx "
"not on pagemask.\n", (unsigned long)high);
}
/*
* Our allocations are always page granularity, so our alignment
* must be, too.
*/
if (alignment < PAGE_SIZE)
alignment = PAGE_SIZE;
low = atop(roundup(low, alignment));
/*
* high + 1 may result in overflow, in which case high becomes 0x0,
* which is the 'don't care' value.
* The only requirement in that case is that low is also 0x0, or the
* low<high assert will fail.
*/
high = atop(high + 1);
alignment = atop(alignment);
if (boundary < PAGE_SIZE && boundary != 0)
boundary = PAGE_SIZE;
boundary = atop(boundary);
return uvm_pmr_getpages(size, low, high, alignment, boundary, nsegs,
flags, rlist);
}
/*
* uvm_pglistfree: free a list of pages
*
* => pages should already be unmapped
*/
void
uvm_pglistfree(struct pglist *list)
{
uvm_pmr_freepageq(list);
}
/*
* interface used by the buffer cache to allocate a buffer at a time.
* The pages are allocated wired in DMA accessible memory
*/
int
uvm_pagealloc_multi(struct uvm_object *obj, voff_t off, vsize_t size,
int flags)
{
struct pglist plist;
struct vm_page *pg;
int i, r;
KASSERT(UVM_OBJ_IS_BUFCACHE(obj));
KERNEL_ASSERT_LOCKED();
TAILQ_INIT(&plist);
r = uvm_pglistalloc(size, dma_constraint.ucr_low,
dma_constraint.ucr_high, 0, 0, &plist, atop(round_page(size)),
flags);
if (r == 0) {
i = 0;
while ((pg = TAILQ_FIRST(&plist)) != NULL) {
pg->wire_count = 1;
atomic_setbits_int(&pg->pg_flags, PG_CLEAN | PG_FAKE);
KASSERT((pg->pg_flags & PG_DEV) == 0);
TAILQ_REMOVE(&plist, pg, pageq);
uvm_pagealloc_pg(pg, obj, off + ptoa(i++), NULL);
}
}
return r;
}
/*
* interface used by the buffer cache to reallocate a buffer at a time.
* The pages are reallocated wired outside the DMA accessible region.
*
*/
int
uvm_pagerealloc_multi(struct uvm_object *obj, voff_t off, vsize_t size,
int flags, struct uvm_constraint_range *where)
{
struct pglist plist;
struct vm_page *pg, *tpg;
int i, r;
voff_t offset;
KASSERT(UVM_OBJ_IS_BUFCACHE(obj));
KERNEL_ASSERT_LOCKED();
TAILQ_INIT(&plist);
if (size == 0)
panic("size 0 uvm_pagerealloc");
r = uvm_pglistalloc(size, where->ucr_low, where->ucr_high, 0,
0, &plist, atop(round_page(size)), flags);
if (r == 0) {
i = 0;
while((pg = TAILQ_FIRST(&plist)) != NULL) {
offset = off + ptoa(i++);
tpg = uvm_pagelookup(obj, offset);
KASSERT(tpg != NULL);
pg->wire_count = 1;
atomic_setbits_int(&pg->pg_flags, PG_CLEAN | PG_FAKE);
KASSERT((pg->pg_flags & PG_DEV) == 0);
TAILQ_REMOVE(&plist, pg, pageq);
uvm_pagecopy(tpg, pg);
KASSERT(tpg->wire_count == 1);
tpg->wire_count = 0;
uvm_lock_pageq();
uvm_pagefree(tpg);
uvm_unlock_pageq();
uvm_pagealloc_pg(pg, obj, offset, NULL);
}
}
return r;
}
/*
* uvm_pagealloc: allocate vm_page from a particular free list.
*
* => return null if no pages free
* => wake up pagedaemon if number of free pages drops below low water mark
* => only one of obj or anon can be non-null
* => caller must activate/deactivate page if it is not wired.
*/
struct vm_page *
uvm_pagealloc(struct uvm_object *obj, voff_t off, struct vm_anon *anon,
int flags)
{
struct vm_page *pg;
struct pglist pgl;
int pmr_flags;
KASSERT(obj == NULL || anon == NULL);
KASSERT(anon == NULL || off == 0);
KASSERT(off == trunc_page(off));
KASSERT(obj == NULL || UVM_OBJ_IS_DUMMY(obj) ||
rw_write_held(obj->vmobjlock));
KASSERT(anon == NULL || anon->an_lock == NULL ||
rw_write_held(anon->an_lock));
pmr_flags = UVM_PLA_NOWAIT;
/*
* We're allowed to use the kernel reserve if the page is
* being allocated to a kernel object.
*/
if ((flags & UVM_PGA_USERESERVE) ||
(obj != NULL && UVM_OBJ_IS_KERN_OBJECT(obj)))
pmr_flags |= UVM_PLA_USERESERVE;
if (flags & UVM_PGA_ZERO)
pmr_flags |= UVM_PLA_ZERO;
TAILQ_INIT(&pgl);
if (uvm_pmr_getpages(1, 0, 0, 1, 0, 1, pmr_flags, &pgl) != 0)
goto fail;
pg = TAILQ_FIRST(&pgl);
KASSERT(pg != NULL && TAILQ_NEXT(pg, pageq) == NULL);
uvm_pagealloc_pg(pg, obj, off, anon);
KASSERT((pg->pg_flags & PG_DEV) == 0);
if (flags & UVM_PGA_ZERO)
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
else
atomic_setbits_int(&pg->pg_flags, PG_CLEAN);
return pg;
fail:
return NULL;
}
/*
* uvm_pagerealloc: reallocate a page from one object to another
*/
void
uvm_pagerealloc(struct vm_page *pg, struct uvm_object *newobj, voff_t newoff)
{
/* remove it from the old object */
if (pg->uobject) {
uvm_pageremove(pg);
}
/* put it in the new object */
if (newobj) {
pg->uobject = newobj;
pg->offset = newoff;
pg->pg_version++;
uvm_pageinsert(pg);
}
}
/*
* uvm_pageclean: clean page
*
* => erase page's identity (i.e. remove from object)
* => caller must lock page queues if `pg' is managed
* => assumes all valid mappings of pg are gone
*/
void
uvm_pageclean(struct vm_page *pg)
{
u_int flags_to_clear = 0;
if ((pg->pg_flags & (PG_TABLED|PQ_ACTIVE|PQ_INACTIVE)) &&
(pg->uobject == NULL || !UVM_OBJ_IS_PMAP(pg->uobject)))
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
#ifdef DEBUG
if (pg->uobject == (void *)0xdeadbeef &&
pg->uanon == (void *)0xdeadbeef) {
panic("uvm_pagefree: freeing free page %p", pg);
}
#endif
KASSERT((pg->pg_flags & PG_DEV) == 0);
KASSERT(pg->uobject == NULL || UVM_OBJ_IS_DUMMY(pg->uobject) ||
rw_write_held(pg->uobject->vmobjlock));
KASSERT(pg->uobject != NULL || pg->uanon == NULL ||
rw_write_held(pg->uanon->an_lock));
/*
* if the page was an object page (and thus "TABLED"), remove it
* from the object.
*/
if (pg->pg_flags & PG_TABLED)
uvm_pageremove(pg);
/*
* now remove the page from the queues
*/
uvm_pagedequeue(pg);
/*
* if the page was wired, unwire it now.
*/
if (pg->wire_count) {
pg->wire_count = 0;
uvmexp.wired--;
}
if (pg->uanon) {
pg->uanon->an_page = NULL;
pg->uanon = NULL;
}
/* Clean page state bits. */
flags_to_clear |= PQ_ANON|PQ_AOBJ|PQ_ENCRYPT|PG_ZERO|PG_FAKE|PG_BUSY|
PG_RELEASED|PG_CLEAN|PG_CLEANCHK;
atomic_clearbits_int(&pg->pg_flags, flags_to_clear);
#ifdef DEBUG
pg->uobject = (void *)0xdeadbeef;
pg->offset = 0xdeadbeef;
pg->uanon = (void *)0xdeadbeef;
#endif
}
/*
* uvm_pagefree: free page
*
* => erase page's identity (i.e. remove from object)
* => put page on free list
* => caller must lock page queues if `pg' is managed
* => assumes all valid mappings of pg are gone
*/
void
uvm_pagefree(struct vm_page *pg)
{
if ((pg->pg_flags & (PG_TABLED|PQ_ACTIVE|PQ_INACTIVE)) &&
(pg->uobject == NULL || !UVM_OBJ_IS_PMAP(pg->uobject)))
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
uvm_pageclean(pg);
uvm_pmr_freepages(pg, 1);
}
/*
* uvm_page_unbusy: unbusy an array of pages.
*
* => pages must either all belong to the same object, or all belong to anons.
* => if pages are anon-owned, anons must have 0 refcount.
*/
void
uvm_page_unbusy(struct vm_page **pgs, int npgs)
{
struct vm_page *pg;
struct uvm_object *uobj;
int i;
for (i = 0; i < npgs; i++) {
pg = pgs[i];
if (pg == NULL || pg == PGO_DONTCARE) {
continue;
}
#if notyet
/*
* XXX swap case in uvm_aio_aiodone() is not holding the lock.
*
* This isn't compatible with the PG_RELEASED anon case below.
*/
KASSERT(uvm_page_owner_locked_p(pg));
#endif
KASSERT(pg->pg_flags & PG_BUSY);
if (pg->pg_flags & PG_WANTED) {
wakeup(pg);
}
if (pg->pg_flags & PG_RELEASED) {
uobj = pg->uobject;
if (uobj != NULL) {
uvm_lock_pageq();
pmap_page_protect(pg, PROT_NONE);
/* XXX won't happen right now */
if (pg->pg_flags & PQ_AOBJ)
uao_dropswap(uobj,
pg->offset >> PAGE_SHIFT);
uvm_pagefree(pg);
uvm_unlock_pageq();
} else {
rw_enter(pg->uanon->an_lock, RW_WRITE);
uvm_anon_release(pg->uanon);
}
} else {
atomic_clearbits_int(&pg->pg_flags, PG_WANTED|PG_BUSY);
UVM_PAGE_OWN(pg, NULL);
}
}
}
/*
* uvm_pagewait: wait for a busy page
*
* => page must be known PG_BUSY
* => object must be locked
* => object will be unlocked on return
*/
void
uvm_pagewait(struct vm_page *pg, struct rwlock *lock, const char *wmesg)
{
KASSERT(rw_lock_held(lock));
KASSERT((pg->pg_flags & PG_BUSY) != 0);
atomic_setbits_int(&pg->pg_flags, PG_WANTED);
rwsleep_nsec(pg, lock, PVM | PNORELOCK, wmesg, INFSLP);
}
#if defined(UVM_PAGE_TRKOWN)
/*
* uvm_page_own: set or release page ownership
*
* => this is a debugging function that keeps track of who sets PG_BUSY
* and where they do it. it can be used to track down problems
* such a thread setting "PG_BUSY" and never releasing it.
* => if "tag" is NULL then we are releasing page ownership
*/
void
uvm_page_own(struct vm_page *pg, char *tag)
{
/* gain ownership? */
if (tag) {
if (pg->owner_tag) {
printf("uvm_page_own: page %p already owned "
"by thread %d [%s]\n", pg,
pg->owner, pg->owner_tag);
panic("uvm_page_own");
}
pg->owner = (curproc) ? curproc->p_tid : (pid_t) -1;
pg->owner_tag = tag;
return;
}
/* drop ownership */
if (pg->owner_tag == NULL) {
printf("uvm_page_own: dropping ownership of an non-owned "
"page (%p)\n", pg);
panic("uvm_page_own");
}
pg->owner_tag = NULL;
return;
}
#endif
/*
* when VM_PHYSSEG_MAX is 1, we can simplify these functions
*/
#if VM_PHYSSEG_MAX > 1
/*
* vm_physseg_find: find vm_physseg structure that belongs to a PA
*/
int
vm_physseg_find(paddr_t pframe, int *offp)
{
struct vm_physseg *seg;
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
/* binary search for it */
int start, len, try;
/*
* if try is too large (thus target is less than than try) we reduce
* the length to trunc(len/2) [i.e. everything smaller than "try"]
*
* if the try is too small (thus target is greater than try) then
* we set the new start to be (try + 1). this means we need to
* reduce the length to (round(len/2) - 1).
*
* note "adjust" below which takes advantage of the fact that
* (round(len/2) - 1) == trunc((len - 1) / 2)
* for any value of len we may have
*/
for (start = 0, len = vm_nphysseg ; len != 0 ; len = len / 2) {
try = start + (len / 2); /* try in the middle */
seg = vm_physmem + try;
/* start past our try? */
if (pframe >= seg->start) {
/* was try correct? */
if (pframe < seg->end) {
if (offp)
*offp = pframe - seg->start;
return try; /* got it */
}
start = try + 1; /* next time, start here */
len--; /* "adjust" */
} else {
/*
* pframe before try, just reduce length of
* region, done in "for" loop
*/
}
}
return -1;
#else
/* linear search for it */
int lcv;
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++) {
if (pframe >= seg->start && pframe < seg->end) {
if (offp)
*offp = pframe - seg->start;
return lcv; /* got it */
}
}
return -1;
#endif
}
/*
* PHYS_TO_VM_PAGE: find vm_page for a PA. used by MI code to get vm_pages
* back from an I/O mapping (ugh!). used in some MD code as well.
*/
struct vm_page *
PHYS_TO_VM_PAGE(paddr_t pa)
{
paddr_t pf = atop(pa);
int off;
int psi;
psi = vm_physseg_find(pf, &off);
return (psi == -1) ? NULL : &vm_physmem[psi].pgs[off];
}
#endif /* VM_PHYSSEG_MAX > 1 */
/*
* uvm_pagelookup: look up a page
*/
struct vm_page *
uvm_pagelookup(struct uvm_object *obj, voff_t off)
{
/* XXX if stack is too much, handroll */
struct vm_page pg;
pg.offset = off;
return RBT_FIND(uvm_objtree, &obj->memt, &pg);
}
/*
* uvm_pagewire: wire the page, thus removing it from the daemon's grasp
*
* => caller must lock page queues
*/
void
uvm_pagewire(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg));
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
if (pg->wire_count == 0) {
uvm_pagedequeue(pg);
uvmexp.wired++;
}
pg->wire_count++;
}
/*
* uvm_pageunwire: unwire the page.
*
* => activate if wire count goes to zero.
* => caller must lock page queues
*/
void
uvm_pageunwire(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg));
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
pg->wire_count--;
if (pg->wire_count == 0) {
uvm_pageactivate(pg);
uvmexp.wired--;
}
}
/*
* uvm_pagedeactivate: deactivate page -- no pmaps have access to page
*
* => caller must lock page queues
* => caller must check to make sure page is not wired
* => object that page belongs to must be locked (so we can adjust pg->flags)
*/
void
uvm_pagedeactivate(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg));
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
if (pg->pg_flags & PQ_ACTIVE) {
TAILQ_REMOVE(&uvm.page_active, pg, pageq);
atomic_clearbits_int(&pg->pg_flags, PQ_ACTIVE);
uvmexp.active--;
}
if ((pg->pg_flags & PQ_INACTIVE) == 0) {
KASSERT(pg->wire_count == 0);
TAILQ_INSERT_TAIL(&uvm.page_inactive, pg, pageq);
atomic_setbits_int(&pg->pg_flags, PQ_INACTIVE);
uvmexp.inactive++;
pmap_clear_reference(pg);
/*
* update the "clean" bit. this isn't 100%
* accurate, and doesn't have to be. we'll
* re-sync it after we zap all mappings when
* scanning the inactive list.
*/
if ((pg->pg_flags & PG_CLEAN) != 0 &&
pmap_is_modified(pg))
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
}
}
/*
* uvm_pageactivate: activate page
*
* => caller must lock page queues
*/
void
uvm_pageactivate(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg));
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
uvm_pagedequeue(pg);
if (pg->wire_count == 0) {
TAILQ_INSERT_TAIL(&uvm.page_active, pg, pageq);
atomic_setbits_int(&pg->pg_flags, PQ_ACTIVE);
uvmexp.active++;
}
}
/*
* uvm_pagedequeue: remove a page from any paging queue
*/
void
uvm_pagedequeue(struct vm_page *pg)
{
if (pg->pg_flags & PQ_ACTIVE) {
TAILQ_REMOVE(&uvm.page_active, pg, pageq);
atomic_clearbits_int(&pg->pg_flags, PQ_ACTIVE);
uvmexp.active--;
}
if (pg->pg_flags & PQ_INACTIVE) {
TAILQ_REMOVE(&uvm.page_inactive, pg, pageq);
atomic_clearbits_int(&pg->pg_flags, PQ_INACTIVE);
uvmexp.inactive--;
}
}
/*
* uvm_pagezero: zero fill a page
*/
void
uvm_pagezero(struct vm_page *pg)
{
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
pmap_zero_page(pg);
}
/*
* uvm_pagecopy: copy a page
*/
void
uvm_pagecopy(struct vm_page *src, struct vm_page *dst)
{
atomic_clearbits_int(&dst->pg_flags, PG_CLEAN);
pmap_copy_page(src, dst);
}
/*
* uvm_page_owner_locked_p: return true if object associated with page is
* locked. this is a weak check for runtime assertions only.
*/
int
uvm_page_owner_locked_p(struct vm_page *pg)
{
if (pg->uobject != NULL) {
if (UVM_OBJ_IS_DUMMY(pg->uobject))
return 1;
return rw_write_held(pg->uobject->vmobjlock);
}
if (pg->uanon != NULL) {
return rw_write_held(pg->uanon->an_lock);
}
return 1;
}
/*
* uvm_pagecount: count the number of physical pages in the address range.
*/
psize_t
uvm_pagecount(struct uvm_constraint_range* constraint)
{
int lcv;
psize_t sz;
paddr_t low, high;
paddr_t ps_low, ps_high;
/* Algorithm uses page numbers. */
low = atop(constraint->ucr_low);
high = atop(constraint->ucr_high);
sz = 0;
for (lcv = 0; lcv < vm_nphysseg; lcv++) {
ps_low = MAX(low, vm_physmem[lcv].avail_start);
ps_high = MIN(high, vm_physmem[lcv].avail_end);
if (ps_low < ps_high)
sz += ps_high - ps_low;
}
return sz;
}
2
1
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/* $OpenBSD: if_enc.c,v 1.79 2022/08/29 07:51:45 bluhm Exp $ */
/*
* Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/if_enc.h>
#include <net/if_types.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
struct ifnet **enc_ifps; /* rdomain-mapped enc ifs */
u_int enc_max_rdomain;
struct ifnet **enc_allifps; /* unit-mapped enc ifs */
u_int enc_max_unit;
#define ENC_MAX_UNITS 4096 /* XXX n per rdomain */
void encattach(int);
int enc_clone_create(struct if_clone *, int);
int enc_clone_destroy(struct ifnet *);
int enc_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int enc_ioctl(struct ifnet *, u_long, caddr_t);
int enc_setif(struct ifnet *, u_int);
void enc_unsetif(struct ifnet *);
struct if_clone enc_cloner =
IF_CLONE_INITIALIZER("enc", enc_clone_create, enc_clone_destroy);
void
encattach(int count)
{
/* Create enc0 by default */
(void)enc_clone_create(&enc_cloner, 0);
if_clone_attach(&enc_cloner);
}
int
enc_clone_create(struct if_clone *ifc, int unit)
{
struct enc_softc *sc;
struct ifnet *ifp;
struct ifnet **new;
size_t oldlen;
int error;
if (unit > ENC_MAX_UNITS)
return (EINVAL);
if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
return (ENOBUFS);
sc->sc_unit = unit;
ifp = &sc->sc_if;
ifp->if_softc = sc;
ifp->if_type = IFT_ENC;
ifp->if_xflags = IFXF_CLONED;
ifp->if_output = enc_output;
ifp->if_ioctl = enc_ioctl;
ifp->if_hdrlen = ENC_HDRLEN;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
if_attach(ifp);
if (unit == 0)
if_addgroup(ifp, ifc->ifc_name);
/*
* enc(4) does not have a link-layer address but rtrequest()
* wants an ifa for every route entry. So let's setup a fake
* and empty ifa of type AF_LINK for this purpose.
*/
if_alloc_sadl(ifp);
refcnt_init_trace(&sc->sc_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
sc->sc_ifa.ifa_ifp = ifp;
sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
sc->sc_ifa.ifa_netmask = NULL;
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_ENC, ENC_HDRLEN);
#endif
NET_LOCK();
error = enc_setif(ifp, 0);
if (error != 0) {
NET_UNLOCK();
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (error);
}
if (enc_allifps == NULL || unit > enc_max_unit) {
if ((new = mallocarray(unit + 1, sizeof(struct ifnet *),
M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) {
NET_UNLOCK();
return (ENOBUFS);
}
if (enc_allifps != NULL) {
oldlen = sizeof(struct ifnet *) * (enc_max_unit + 1);
memcpy(new, enc_allifps, oldlen);
free(enc_allifps, M_DEVBUF, oldlen);
}
enc_allifps = new;
enc_max_unit = unit;
}
enc_allifps[unit] = ifp;
NET_UNLOCK();
return (0);
}
int
enc_clone_destroy(struct ifnet *ifp)
{
struct enc_softc *sc = ifp->if_softc;
/* Protect users from removing enc0 */
if (sc->sc_unit == 0)
return (EPERM);
NET_LOCK();
enc_allifps[sc->sc_unit] = NULL;
enc_unsetif(ifp);
NET_UNLOCK();
if_detach(ifp);
if (refcnt_rele(&sc->sc_ifa.ifa_refcnt) == 0) {
panic("%s: ifa refcnt has %u refs", __func__,
sc->sc_ifa.ifa_refcnt.r_refs);
}
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
int
enc_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
struct rtentry *rt)
{
m_freem(m); /* drop packet */
return (EAFNOSUPPORT);
}
int
enc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ifreq *ifr = (struct ifreq *)data;
int error;
switch (cmd) {
case SIOCSIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
break;
case SIOCSIFRDOMAIN:
if ((error = enc_setif(ifp, ifr->ifr_rdomainid)) != 0)
return (error);
/* FALLTHROUGH */
default:
return (ENOTTY);
}
return (0);
}
struct ifnet *
enc_getif(u_int rdomain, u_int unit)
{
struct ifnet *ifp;
NET_ASSERT_LOCKED();
/* Check if the caller wants to get a non-default enc interface */
if (unit > 0) {
if (unit > enc_max_unit)
return (NULL);
ifp = enc_allifps[unit];
if (ifp == NULL || ifp->if_rdomain != rdomain)
return (NULL);
return (ifp);
}
/* Otherwise return the default enc interface for this rdomain */
if (enc_ifps == NULL)
return (NULL);
else if (rdomain > RT_TABLEID_MAX)
return (NULL);
else if (rdomain > enc_max_rdomain)
return (NULL);
return (enc_ifps[rdomain]);
}
struct ifaddr *
enc_getifa(u_int rdomain, u_int unit)
{
struct ifnet *ifp;
struct enc_softc *sc;
ifp = enc_getif(rdomain, unit);
if (ifp == NULL)
return (NULL);
sc = ifp->if_softc;
return (&sc->sc_ifa);
}
int
enc_setif(struct ifnet *ifp, u_int rdomain)
{
struct ifnet **new;
size_t oldlen;
NET_ASSERT_LOCKED();
enc_unsetif(ifp);
/*
* There can only be one default encif per rdomain -
* Don't overwrite the existing enc iface that is stored
* for this rdomain, so only the first enc interface that
* was added for this rdomain becomes the default.
*/
if (enc_getif(rdomain, 0) != NULL)
return (0);
if (rdomain > RT_TABLEID_MAX)
return (EINVAL);
if (enc_ifps == NULL || rdomain > enc_max_rdomain) {
if ((new = mallocarray(rdomain + 1, sizeof(struct ifnet *),
M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
return (ENOBUFS);
if (enc_ifps != NULL) {
oldlen = sizeof(struct ifnet *) * (enc_max_rdomain + 1);
memcpy(new, enc_ifps, oldlen);
free(enc_ifps, M_DEVBUF, oldlen);
}
enc_ifps = new;
enc_max_rdomain = rdomain;
}
enc_ifps[rdomain] = ifp;
/* Indicate that this interface is the rdomain default */
ifp->if_link_state = LINK_STATE_UP;
return (0);
}
void
enc_unsetif(struct ifnet *ifp)
{
u_int rdomain = ifp->if_rdomain, i;
struct ifnet *oifp, *nifp;
if ((oifp = enc_getif(rdomain, 0)) == NULL || oifp != ifp)
return;
/* Clear slot for this rdomain */
enc_ifps[rdomain] = NULL;
ifp->if_link_state = LINK_STATE_UNKNOWN;
/*
* Now find the next available encif to be the default interface
* for this rdomain.
*/
for (i = 0; i < (enc_max_unit + 1); i++) {
nifp = enc_allifps[i];
if (nifp == NULL || nifp == ifp || nifp->if_rdomain != rdomain)
continue;
enc_ifps[rdomain] = nifp;
nifp->if_link_state = LINK_STATE_UP;
break;
}
}
641
641
639
83
83
82
83
82
83
107
108
65
65
16
16
16
16
280
280
184
11
280
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/* $OpenBSD: ffs_subr.c,v 1.34 2021/10/20 06:35:39 semarie Exp $ */
/* $NetBSD: ffs_subr.c,v 1.6 1996/03/17 02:16:23 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_subr.c 8.2 (Berkeley) 9/21/93
*/
#include <sys/param.h>
#include <ufs/ffs/fs.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ffs/ffs_extern.h>
/*
* Return buffer with the contents of block "offset" from the beginning of
* directory "ip". If "res" is non-zero, fill it in with a pointer to the
* remaining space in the directory.
*/
int
ffs_bufatoff(struct inode *ip, off_t offset, char **res, struct buf **bpp)
{
struct fs *fs;
struct vnode *vp;
struct buf *bp;
daddr_t lbn;
int bsize, error;
vp = ITOV(ip);
fs = ip->i_fs;
lbn = lblkno(fs, offset);
bsize = blksize(fs, ip, lbn);
*bpp = NULL;
if ((error = bread(vp, lbn, fs->fs_bsize, &bp)) != 0) {
brelse(bp);
return (error);
}
buf_adjcnt(bp, bsize);
if (res)
*res = (char *)bp->b_data + blkoff(fs, offset);
*bpp = bp;
return (0);
}
#else
/* Prototypes for userland */
void ffs_fragacct(struct fs *, int, int32_t[], int);
int ffs_isfreeblock(struct fs *, u_char *, daddr_t);
int ffs_isblock(struct fs *, u_char *, daddr_t);
void ffs_clrblock(struct fs *, u_char *, daddr_t);
void ffs_setblock(struct fs *, u_char *, daddr_t);
__dead void panic(const char *, ...);
#endif
/*
* Update the frsum fields to reflect addition or deletion
* of some frags.
*/
void
ffs_fragacct(struct fs *fs, int fragmap, int32_t fraglist[], int cnt)
{
int inblk;
int field, subfield;
int siz, pos;
inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1;
fragmap <<= 1;
for (siz = 1; siz < fs->fs_frag; siz++) {
if ((inblk & (1 << (siz + (fs->fs_frag % NBBY)))) == 0)
continue;
field = around[siz];
subfield = inside[siz];
for (pos = siz; pos <= fs->fs_frag; pos++) {
if ((fragmap & field) == subfield) {
fraglist[siz] += cnt;
pos += siz;
field <<= siz;
subfield <<= siz;
}
field <<= 1;
subfield <<= 1;
}
}
}
#if defined(_KERNEL) && defined(DIAGNOSTIC)
void
ffs_checkoverlap(struct buf *bp, struct inode *ip)
{
daddr_t start, last;
struct vnode *vp;
struct buf *ep;
start = bp->b_blkno;
last = start + btodb(bp->b_bcount) - 1;
LIST_FOREACH(ep, &bufhead, b_list) {
if (ep == bp || (ep->b_flags & B_INVAL) ||
ep->b_vp == NULLVP)
continue;
if (VOP_BMAP(ep->b_vp, 0, &vp, NULL, NULL))
continue;
if (vp != ip->i_devvp)
continue;
/* look for overlap */
if (ep->b_bcount == 0 || ep->b_blkno > last ||
ep->b_blkno + btodb(ep->b_bcount) <= start)
continue;
vprint("Disk overlap", vp);
(void)printf("\tstart %lld, end %lld overlap start %llu, "
"end %llu\n", (long long)start, (long long)last,
(long long)ep->b_blkno,
(long long)(ep->b_blkno + btodb(ep->b_bcount) - 1));
panic("Disk buffer overlap");
}
}
#endif /* DIAGNOSTIC */
/*
* block operations
*
* check if a block is available
*/
int
ffs_isblock(struct fs *fs, u_char *cp, daddr_t h)
{
u_char mask;
switch (fs->fs_frag) {
default:
case 8:
return (cp[h] == 0xff);
case 4:
mask = 0x0f << ((h & 0x1) << 2);
return ((cp[h >> 1] & mask) == mask);
case 2:
mask = 0x03 << ((h & 0x3) << 1);
return ((cp[h >> 2] & mask) == mask);
case 1:
mask = 0x01 << (h & 0x7);
return ((cp[h >> 3] & mask) == mask);
}
}
/*
* take a block out of the map
*/
void
ffs_clrblock(struct fs *fs, u_char *cp, daddr_t h)
{
switch (fs->fs_frag) {
default:
case 8:
cp[h] = 0;
return;
case 4:
cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
return;
case 2:
cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
return;
case 1:
cp[h >> 3] &= ~(0x01 << (h & 0x7));
return;
}
}
/*
* put a block into the map
*/
void
ffs_setblock(struct fs *fs, u_char *cp, daddr_t h)
{
switch (fs->fs_frag) {
default:
case 8:
cp[h] = 0xff;
return;
case 4:
cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
return;
case 2:
cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
return;
case 1:
cp[h >> 3] |= (0x01 << (h & 0x7));
return;
}
}
/*
* check if a block is free
*/
int
ffs_isfreeblock(struct fs *fs, u_char *cp, daddr_t h)
{
switch (fs->fs_frag) {
default:
case 8:
return (cp[h] == 0);
case 4:
return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0);
case 2:
return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0);
case 1:
return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0);
}
}
#ifdef _KERNEL
/*
* Initialize the vnode associated with a new inode, handle aliased
* vnodes.
*/
int
ffs_vinit(struct mount *mntp, struct vnode **vpp)
{
struct inode *ip;
struct vnode *vp, *nvp;
struct timeval mtv;
vp = *vpp;
ip = VTOI(vp);
switch(vp->v_type = IFTOVT(DIP(ip, mode))) {
case VCHR:
case VBLK:
vp->v_op = &ffs_specvops;
if ((nvp = checkalias(vp, DIP(ip, rdev), mntp)) != NULL) {
/*
* Discard unneeded vnode, but save its inode.
* Note that the lock is carried over in the inode
* to the replacement vnode.
*/
nvp->v_data = vp->v_data;
vp->v_data = NULL;
vp->v_op = &spec_vops;
#ifdef VFSLCKDEBUG
vp->v_flag &= ~VLOCKSWORK;
#endif
vrele(vp);
vgone(vp);
/*
* Reinitialize aliased inode.
*/
vp = nvp;
ip->i_vnode = vp;
}
break;
case VFIFO:
#ifdef FIFO
vp->v_op = &ffs_fifovops;
break;
#else
return (EOPNOTSUPP);
#endif
case VNON:
case VBAD:
case VSOCK:
case VLNK:
case VDIR:
case VREG:
break;
}
if (ip->i_number == ROOTINO)
vp->v_flag |= VROOT;
/*
* Initialize modrev times
*/
getmicrouptime(&mtv);
ip->i_modrev = (u_quad_t)mtv.tv_sec << 32;
ip->i_modrev |= (u_quad_t)mtv.tv_usec * 4294;
*vpp = vp;
return (0);
}
#endif /* _KERNEL */
32
6
6
23
7
17
17
9
5
7
14
7
14
1
13
11
9
7
13
20
13
8
1
11
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/* $OpenBSD: exec_script.c,v 1.48 2019/07/15 04:11:03 visa Exp $ */
/* $NetBSD: exec_script.c,v 1.13 1996/02/04 02:15:06 christos Exp $ */
/*
* Copyright (c) 1993, 1994 Christopher G. Demetriou
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/namei.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/exec.h>
#include <sys/exec_script.h>
/*
* exec_script_makecmds(): Check if it's an executable shell script.
*
* Given a proc pointer and an exec package pointer, see if the referent
* of the epp is in shell script. If it is, then set things up so that
* the script can be run. This involves preparing the address space
* and arguments for the shell which will run the script.
*
* This function is ultimately responsible for creating a set of vmcmds
* which can be used to build the process's vm space and inserting them
* into the exec package.
*/
int
exec_script_makecmds(struct proc *p, struct exec_package *epp)
{
int error, hdrlinelen, shellnamelen, shellarglen;
char *hdrstr = epp->ep_hdr;
char *cp, *shellname, *shellarg, *oldpnbuf;
char **shellargp = NULL, **tmpsap;
struct vnode *scriptvp;
uid_t script_uid = -1;
gid_t script_gid = -1;
u_short script_sbits;
/*
* remember the old vp and pnbuf for later, so we can restore
* them if check_exec() fails.
*/
scriptvp = epp->ep_vp;
oldpnbuf = epp->ep_ndp->ni_cnd.cn_pnbuf;
/*
* if the magic isn't that of a shell script, or we've already
* done shell script processing for this exec, punt on it.
*/
if ((epp->ep_flags & EXEC_INDIR) != 0 ||
epp->ep_hdrvalid < EXEC_SCRIPT_MAGICLEN ||
strncmp(hdrstr, EXEC_SCRIPT_MAGIC, EXEC_SCRIPT_MAGICLEN))
return ENOEXEC;
/*
* check that the shell spec is terminated by a newline,
* and that it isn't too large. Don't modify the
* buffer unless we're ready to commit to handling it.
* (The latter requirement means that we have to check
* for both spaces and tabs later on.)
*/
hdrlinelen = min(epp->ep_hdrvalid, MAXINTERP);
for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; cp < hdrstr + hdrlinelen;
cp++) {
if (*cp == '\n') {
*cp = '\0';
break;
}
}
if (cp >= hdrstr + hdrlinelen)
return ENOEXEC;
shellname = NULL;
shellarg = NULL;
shellarglen = 0;
/* strip spaces before the shell name */
for (cp = hdrstr + EXEC_SCRIPT_MAGICLEN; *cp == ' ' || *cp == '\t';
cp++)
;
/* collect the shell name; remember its length for later */
shellname = cp;
shellnamelen = 0;
if (*cp == '\0')
goto check_shell;
for ( /* cp = cp */ ; *cp != '\0' && *cp != ' ' && *cp != '\t'; cp++)
shellnamelen++;
if (*cp == '\0')
goto check_shell;
*cp++ = '\0';
/* skip spaces before any argument */
for ( /* cp = cp */ ; *cp == ' ' || *cp == '\t'; cp++)
;
if (*cp == '\0')
goto check_shell;
/*
* collect the shell argument. everything after the shell name
* is passed as ONE argument; that's the correct (historical)
* behaviour.
*/
shellarg = cp;
for ( /* cp = cp */ ; *cp != '\0'; cp++)
shellarglen++;
*cp++ = '\0';
check_shell:
/*
* MNT_NOSUID and STRC are already taken care of by check_exec,
* so we don't need to worry about them now or later.
*/
script_sbits = epp->ep_vap->va_mode & (VSUID | VSGID);
if (script_sbits != 0) {
script_uid = epp->ep_vap->va_uid;
script_gid = epp->ep_vap->va_gid;
}
/*
* if the script isn't readable, or it's set-id, then we've
* gotta supply a "/dev/fd/..." for the shell to read.
* Note that stupid shells (csh) do the wrong thing, and
* close all open fd's when they start. That kills this
* method of implementing "safe" set-id and x-only scripts.
*/
vn_lock(scriptvp, LK_EXCLUSIVE|LK_RETRY);
error = VOP_ACCESS(scriptvp, VREAD, p->p_ucred, p);
VOP_UNLOCK(scriptvp);
if (error == EACCES || script_sbits) {
struct file *fp;
#ifdef DIAGNOSTIC
if (epp->ep_flags & EXEC_HASFD)
panic("exec_script_makecmds: epp already has a fd");
#endif
fdplock(p->p_fd);
error = falloc(p, &fp, &epp->ep_fd);
if (error) {
fdpunlock(p->p_fd);
goto fail;
}
epp->ep_flags |= EXEC_HASFD;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = (caddr_t) scriptvp;
fp->f_flag = FREAD;
fdinsert(p->p_fd, epp->ep_fd, 0, fp);
fdpunlock(p->p_fd);
FRELE(fp, p);
}
/* set up the parameters for the recursive check_exec() call */
epp->ep_ndp->ni_dirfd = AT_FDCWD;
epp->ep_ndp->ni_dirp = shellname;
epp->ep_ndp->ni_segflg = UIO_SYSSPACE;
epp->ep_flags |= EXEC_INDIR;
/* and set up the fake args list, for later */
shellargp = mallocarray(4, sizeof(char *), M_EXEC, M_WAITOK);
tmpsap = shellargp;
*tmpsap = malloc(shellnamelen + 1, M_EXEC, M_WAITOK);
strlcpy(*tmpsap++, shellname, shellnamelen + 1);
if (shellarg != NULL) {
*tmpsap = malloc(shellarglen + 1, M_EXEC, M_WAITOK);
strlcpy(*tmpsap++, shellarg, shellarglen + 1);
}
*tmpsap = malloc(MAXPATHLEN, M_EXEC, M_WAITOK);
if ((epp->ep_flags & EXEC_HASFD) == 0) {
error = copyinstr(epp->ep_name, *tmpsap, MAXPATHLEN,
NULL);
if (error != 0) {
*(tmpsap + 1) = NULL;
goto fail;
}
} else
snprintf(*tmpsap, MAXPATHLEN, "/dev/fd/%d", epp->ep_fd);
tmpsap++;
*tmpsap = NULL;
/*
* mark the header we have as invalid; check_exec will read
* the header from the new executable
*/
epp->ep_hdrvalid = 0;
if ((error = check_exec(p, epp)) == 0) {
/* note that we've clobbered the header */
epp->ep_flags |= EXEC_DESTR;
/*
* It succeeded. Unlock the script and
* close it if we aren't using it any more.
* Also, set things up so that the fake args
* list will be used.
*/
if ((epp->ep_flags & EXEC_HASFD) == 0)
vn_close(scriptvp, FREAD, p->p_ucred, p);
/* free the old pathname buffer */
pool_put(&namei_pool, oldpnbuf);
epp->ep_flags |= (EXEC_HASARGL | EXEC_SKIPARG);
epp->ep_fa = shellargp;
/*
* set things up so that set-id scripts will be
* handled appropriately
*/
epp->ep_vap->va_mode |= script_sbits;
if (script_sbits & VSUID)
epp->ep_vap->va_uid = script_uid;
if (script_sbits & VSGID)
epp->ep_vap->va_gid = script_gid;
return (0);
}
/* XXX oldpnbuf not set for "goto fail" path */
epp->ep_ndp->ni_cnd.cn_pnbuf = oldpnbuf;
fail:
/* note that we've clobbered the header */
epp->ep_flags |= EXEC_DESTR;
/* kill the opened file descriptor, else close the file */
if (epp->ep_flags & EXEC_HASFD) {
epp->ep_flags &= ~EXEC_HASFD;
fdplock(p->p_fd);
/* fdrelease() unlocks p->p_fd. */
(void) fdrelease(p, epp->ep_fd);
} else
vn_close(scriptvp, FREAD, p->p_ucred, p);
pool_put(&namei_pool, epp->ep_ndp->ni_cnd.cn_pnbuf);
/* free the fake arg list, because we're not returning it */
if (shellargp != NULL) {
free(shellargp[0], M_EXEC, shellnamelen + 1);
if (shellargp[2] != NULL) {
free(shellargp[1], M_EXEC, shellarglen + 1);
free(shellargp[2], M_EXEC, MAXPATHLEN);
} else
free(shellargp[1], M_EXEC, MAXPATHLEN);
free(shellargp, M_EXEC, 4 * sizeof(char *));
}
/*
* free any vmspace-creation commands,
* and release their references
*/
kill_vmcmds(&epp->ep_vmcmds);
return error;
}
4
1
3
4
34
31
2
2
3
18
12
40
37
36
8
4
8
30
10
14
6
79
33
6
11
3
19
14
14
5
1
7
3
3
3
3
14
13
14
14
3
2
79
18
37
7
2
2
1
3
544
480
101
12
409
133
26
6
1
1
1
1
1
1
1
1
1
2
2
2
4
3
1
1
3
3
1
1
4
1
1
3
1
14
2
14
14
1
2
13
11
2
2
11
3
1
2
2
3
13
1
2
7
3
11
11
9
2
11
1
7
4
1
3
4
14
67
1
74
2
5
5
48
17
23
19
2
23
10
3
6
9
13
3
25
5
32
40
40
41
1
52
53
7
5
7
2
2
3
1
2
3
2
1
37
14
26
29
23
17
3
2
2
1
1
2
2
2
2
2
2
2
5
1
1
1
2
1
2
1
1
6
1
4
2
3
2
6
8
2
4
4
1
1
7
1
13
11
1
1
2
2
2
1
1
11
11
1
1
6
6
2
5
5
2
5
5
2
1
6
6
2
6
6
1
4
4
9
4
1
24
24
21
3
1
22
12
1
3
1
21
1
1
15
2
1
15
10
14
1
14
1
17
3
24
13
4
16
2
4
1
16
16
1
1
14
15
15
9
14
15
16
16
16
16
3
2
2
1
1
4
4
4
4
4
1
1
1
2
1
1
2
2
2
4
10
10
6
6
5
5
1
1
2
2
3
3
4
4
425
99
16
15
1
15
1
15
2
4
6
134
132
2
2
2
2
100
15
107
7
49
65
88
27
90
23
113
2
105
94
10
73
34
4
85
8
8
89
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
/* $OpenBSD: pf_ioctl.c,v 1.385 2022/08/06 15:57:58 bluhm Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002 - 2018 Henning Brauer <henning@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Effort sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
*
*/
#include "pfsync.h"
#include "pflog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/pool.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/syslog.h>
#include <uvm/uvm_extern.h>
#include <crypto/md5.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/hfsc.h>
#include <net/fq_codel.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#if NPFSYNC > 0
#include <netinet/ip_ipsp.h>
#include <net/if_pfsync.h>
#endif /* NPFSYNC > 0 */
struct pool pf_tag_pl;
void pfattach(int);
void pf_thread_create(void *);
int pfopen(dev_t, int, int, struct proc *);
int pfclose(dev_t, int, int, struct proc *);
int pfioctl(dev_t, u_long, caddr_t, int, struct proc *);
int pf_begin_rules(u_int32_t *, const char *);
void pf_rollback_rules(u_int32_t, char *);
void pf_remove_queues(void);
int pf_commit_queues(void);
void pf_free_queues(struct pf_queuehead *);
void pf_calc_chksum(struct pf_ruleset *);
void pf_hash_rule(MD5_CTX *, struct pf_rule *);
void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *);
int pf_commit_rules(u_int32_t, char *);
int pf_addr_setup(struct pf_ruleset *,
struct pf_addr_wrap *, sa_family_t);
struct pfi_kif *pf_kif_setup(struct pfi_kif *);
void pf_addr_copyout(struct pf_addr_wrap *);
void pf_trans_set_commit(void);
void pf_pool_copyin(struct pf_pool *, struct pf_pool *);
int pf_validate_range(u_int8_t, u_int16_t[2], int);
int pf_rule_copyin(struct pf_rule *, struct pf_rule *);
int pf_rule_checkaf(struct pf_rule *);
u_int16_t pf_qname2qid(char *, int);
void pf_qid2qname(u_int16_t, char *);
void pf_qid_unref(u_int16_t);
int pf_states_clr(struct pfioc_state_kill *);
int pf_states_get(struct pfioc_states *);
struct pf_rule pf_default_rule, pf_default_rule_new;
struct {
char statusif[IFNAMSIZ];
u_int32_t debug;
u_int32_t hostid;
u_int32_t reass;
u_int32_t mask;
} pf_trans_set;
#define PF_ORDER_HOST 0
#define PF_ORDER_NET 1
#define PF_TSET_STATUSIF 0x01
#define PF_TSET_DEBUG 0x02
#define PF_TSET_HOSTID 0x04
#define PF_TSET_REASS 0x08
#define TAGID_MAX 50000
TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags),
pf_qids = TAILQ_HEAD_INITIALIZER(pf_qids);
/*
* pf_lock protects consistency of PF data structures, which don't have
* their dedicated lock yet. The pf_lock currently protects:
* - rules,
* - radix tables,
* - source nodes
* All callers must grab pf_lock exclusively.
*
* pf_state_lock protects consistency of state table. Packets, which do state
* look up grab the lock as readers. If packet must create state, then it must
* grab the lock as writer. Whenever packet creates state it grabs pf_lock
* first then it locks pf_state_lock as the writer.
*/
struct rwlock pf_lock = RWLOCK_INITIALIZER("pf_lock");
struct rwlock pf_state_lock = RWLOCK_INITIALIZER("pf_state_lock");
struct rwlock pfioctl_rw = RWLOCK_INITIALIZER("pfioctl_rw");
#if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE)
#error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE
#endif
u_int16_t tagname2tag(struct pf_tags *, char *, int);
void tag2tagname(struct pf_tags *, u_int16_t, char *);
void tag_unref(struct pf_tags *, u_int16_t);
int pf_rtlabel_add(struct pf_addr_wrap *);
void pf_rtlabel_remove(struct pf_addr_wrap *);
void pf_rtlabel_copyout(struct pf_addr_wrap *);
void
pfattach(int num)
{
u_int32_t *timeout = pf_default_rule.timeout;
pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0,
IPL_SOFTNET, 0, "pfrule", NULL);
pool_init(&pf_src_tree_pl, sizeof(struct pf_src_node), 0,
IPL_SOFTNET, 0, "pfsrctr", NULL);
pool_init(&pf_sn_item_pl, sizeof(struct pf_sn_item), 0,
IPL_SOFTNET, 0, "pfsnitem", NULL);
pool_init(&pf_state_pl, sizeof(struct pf_state), 0,
IPL_SOFTNET, 0, "pfstate", NULL);
pool_init(&pf_state_key_pl, sizeof(struct pf_state_key), 0,
IPL_SOFTNET, 0, "pfstkey", NULL);
pool_init(&pf_state_item_pl, sizeof(struct pf_state_item), 0,
IPL_SOFTNET, 0, "pfstitem", NULL);
pool_init(&pf_rule_item_pl, sizeof(struct pf_rule_item), 0,
IPL_SOFTNET, 0, "pfruleitem", NULL);
pool_init(&pf_queue_pl, sizeof(struct pf_queuespec), 0,
IPL_SOFTNET, 0, "pfqueue", NULL);
pool_init(&pf_tag_pl, sizeof(struct pf_tagname), 0,
IPL_SOFTNET, 0, "pftag", NULL);
pool_init(&pf_pktdelay_pl, sizeof(struct pf_pktdelay), 0,
IPL_SOFTNET, 0, "pfpktdelay", NULL);
pool_init(&pf_anchor_pl, sizeof(struct pf_anchor), 0,
IPL_SOFTNET, 0, "pfanchor", NULL);
hfsc_initialize();
pfr_initialize();
pfi_initialize();
pf_osfp_initialize();
pf_syncookies_init();
pool_sethardlimit(pf_pool_limits[PF_LIMIT_STATES].pp,
pf_pool_limits[PF_LIMIT_STATES].limit, NULL, 0);
pool_sethardlimit(pf_pool_limits[PF_LIMIT_ANCHORS].pp,
pf_pool_limits[PF_LIMIT_ANCHORS].limit, NULL, 0);
if (physmem <= atop(100*1024*1024))
pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].limit =
PFR_KENTRY_HIWAT_SMALL;
RB_INIT(&tree_src_tracking);
RB_INIT(&pf_anchors);
pf_init_ruleset(&pf_main_ruleset);
TAILQ_INIT(&pf_queues[0]);
TAILQ_INIT(&pf_queues[1]);
pf_queues_active = &pf_queues[0];
pf_queues_inactive = &pf_queues[1];
/* default rule should never be garbage collected */
pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next;
pf_default_rule.action = PF_PASS;
pf_default_rule.nr = (u_int32_t)-1;
pf_default_rule.rtableid = -1;
/* initialize default timeouts */
timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
pf_default_rule.src.addr.type = PF_ADDR_ADDRMASK;
pf_default_rule.dst.addr.type = PF_ADDR_ADDRMASK;
pf_default_rule.rdr.addr.type = PF_ADDR_NONE;
pf_default_rule.nat.addr.type = PF_ADDR_NONE;
pf_default_rule.route.addr.type = PF_ADDR_NONE;
pf_normalize_init();
memset(&pf_status, 0, sizeof(pf_status));
pf_status.debug = LOG_ERR;
pf_status.reass = PF_REASS_ENABLED;
/* XXX do our best to avoid a conflict */
pf_status.hostid = arc4random();
pf_default_rule_new = pf_default_rule;
}
int
pfopen(dev_t dev, int flags, int fmt, struct proc *p)
{
if (minor(dev) >= 1)
return (ENXIO);
return (0);
}
int
pfclose(dev_t dev, int flags, int fmt, struct proc *p)
{
if (minor(dev) >= 1)
return (ENXIO);
return (0);
}
void
pf_rule_free(struct pf_rule *rule)
{
if (rule == NULL)
return;
pfi_kif_free(rule->kif);
pfi_kif_free(rule->rcv_kif);
pfi_kif_free(rule->rdr.kif);
pfi_kif_free(rule->nat.kif);
pfi_kif_free(rule->route.kif);
pool_put(&pf_rule_pl, rule);
}
void
pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule)
{
if (rulequeue != NULL) {
if (rule->states_cur == 0 && rule->src_nodes == 0) {
/*
* XXX - we need to remove the table *before* detaching
* the rule to make sure the table code does not delete
* the anchor under our feet.
*/
pf_tbladdr_remove(&rule->src.addr);
pf_tbladdr_remove(&rule->dst.addr);
pf_tbladdr_remove(&rule->rdr.addr);
pf_tbladdr_remove(&rule->nat.addr);
pf_tbladdr_remove(&rule->route.addr);
if (rule->overload_tbl)
pfr_detach_table(rule->overload_tbl);
}
TAILQ_REMOVE(rulequeue, rule, entries);
rule->entries.tqe_prev = NULL;
rule->nr = (u_int32_t)-1;
}
if (rule->states_cur > 0 || rule->src_nodes > 0 ||
rule->entries.tqe_prev != NULL)
return;
pf_tag_unref(rule->tag);
pf_tag_unref(rule->match_tag);
pf_rtlabel_remove(&rule->src.addr);
pf_rtlabel_remove(&rule->dst.addr);
pfi_dynaddr_remove(&rule->src.addr);
pfi_dynaddr_remove(&rule->dst.addr);
pfi_dynaddr_remove(&rule->rdr.addr);
pfi_dynaddr_remove(&rule->nat.addr);
pfi_dynaddr_remove(&rule->route.addr);
if (rulequeue == NULL) {
pf_tbladdr_remove(&rule->src.addr);
pf_tbladdr_remove(&rule->dst.addr);
pf_tbladdr_remove(&rule->rdr.addr);
pf_tbladdr_remove(&rule->nat.addr);
pf_tbladdr_remove(&rule->route.addr);
if (rule->overload_tbl)
pfr_detach_table(rule->overload_tbl);
}
pfi_kif_unref(rule->rcv_kif, PFI_KIF_REF_RULE);
pfi_kif_unref(rule->kif, PFI_KIF_REF_RULE);
pfi_kif_unref(rule->rdr.kif, PFI_KIF_REF_RULE);
pfi_kif_unref(rule->nat.kif, PFI_KIF_REF_RULE);
pfi_kif_unref(rule->route.kif, PFI_KIF_REF_RULE);
pf_remove_anchor(rule);
pool_put(&pf_rule_pl, rule);
}
void
pf_purge_rule(struct pf_rule *rule)
{
u_int32_t nr = 0;
struct pf_ruleset *ruleset;
KASSERT((rule != NULL) && (rule->ruleset != NULL));
ruleset = rule->ruleset;
pf_rm_rule(ruleset->rules.active.ptr, rule);
ruleset->rules.active.rcount--;
TAILQ_FOREACH(rule, ruleset->rules.active.ptr, entries)
rule->nr = nr++;
ruleset->rules.active.ticket++;
pf_calc_skip_steps(ruleset->rules.active.ptr);
pf_remove_if_empty_ruleset(ruleset);
if (ruleset == &pf_main_ruleset)
pf_calc_chksum(ruleset);
}
u_int16_t
tagname2tag(struct pf_tags *head, char *tagname, int create)
{
struct pf_tagname *tag, *p = NULL;
u_int16_t new_tagid = 1;
TAILQ_FOREACH(tag, head, entries)
if (strcmp(tagname, tag->name) == 0) {
tag->ref++;
return (tag->tag);
}
if (!create)
return (0);
/*
* to avoid fragmentation, we do a linear search from the beginning
* and take the first free slot we find. if there is none or the list
* is empty, append a new entry at the end.
*/
/* new entry */
TAILQ_FOREACH(p, head, entries) {
if (p->tag != new_tagid)
break;
new_tagid = p->tag + 1;
}
if (new_tagid > TAGID_MAX)
return (0);
/* allocate and fill new struct pf_tagname */
tag = pool_get(&pf_tag_pl, PR_NOWAIT | PR_ZERO);
if (tag == NULL)
return (0);
strlcpy(tag->name, tagname, sizeof(tag->name));
tag->tag = new_tagid;
tag->ref++;
if (p != NULL) /* insert new entry before p */
TAILQ_INSERT_BEFORE(p, tag, entries);
else /* either list empty or no free slot in between */
TAILQ_INSERT_TAIL(head, tag, entries);
return (tag->tag);
}
void
tag2tagname(struct pf_tags *head, u_int16_t tagid, char *p)
{
struct pf_tagname *tag;
TAILQ_FOREACH(tag, head, entries)
if (tag->tag == tagid) {
strlcpy(p, tag->name, PF_TAG_NAME_SIZE);
return;
}
}
void
tag_unref(struct pf_tags *head, u_int16_t tag)
{
struct pf_tagname *p, *next;
if (tag == 0)
return;
TAILQ_FOREACH_SAFE(p, head, entries, next) {
if (tag == p->tag) {
if (--p->ref == 0) {
TAILQ_REMOVE(head, p, entries);
pool_put(&pf_tag_pl, p);
}
break;
}
}
}
u_int16_t
pf_tagname2tag(char *tagname, int create)
{
return (tagname2tag(&pf_tags, tagname, create));
}
void
pf_tag2tagname(u_int16_t tagid, char *p)
{
tag2tagname(&pf_tags, tagid, p);
}
void
pf_tag_ref(u_int16_t tag)
{
struct pf_tagname *t;
TAILQ_FOREACH(t, &pf_tags, entries)
if (t->tag == tag)
break;
if (t != NULL)
t->ref++;
}
void
pf_tag_unref(u_int16_t tag)
{
tag_unref(&pf_tags, tag);
}
int
pf_rtlabel_add(struct pf_addr_wrap *a)
{
if (a->type == PF_ADDR_RTLABEL &&
(a->v.rtlabel = rtlabel_name2id(a->v.rtlabelname)) == 0)
return (-1);
return (0);
}
void
pf_rtlabel_remove(struct pf_addr_wrap *a)
{
if (a->type == PF_ADDR_RTLABEL)
rtlabel_unref(a->v.rtlabel);
}
void
pf_rtlabel_copyout(struct pf_addr_wrap *a)
{
const char *name;
if (a->type == PF_ADDR_RTLABEL && a->v.rtlabel) {
if ((name = rtlabel_id2name(a->v.rtlabel)) == NULL)
strlcpy(a->v.rtlabelname, "?",
sizeof(a->v.rtlabelname));
else
strlcpy(a->v.rtlabelname, name,
sizeof(a->v.rtlabelname));
}
}
u_int16_t
pf_qname2qid(char *qname, int create)
{
return (tagname2tag(&pf_qids, qname, create));
}
void
pf_qid2qname(u_int16_t qid, char *p)
{
tag2tagname(&pf_qids, qid, p);
}
void
pf_qid_unref(u_int16_t qid)
{
tag_unref(&pf_qids, (u_int16_t)qid);
}
int
pf_begin_rules(u_int32_t *ticket, const char *anchor)
{
struct pf_ruleset *rs;
struct pf_rule *rule;
if ((rs = pf_find_or_create_ruleset(anchor)) == NULL)
return (EINVAL);
while ((rule = TAILQ_FIRST(rs->rules.inactive.ptr)) != NULL) {
pf_rm_rule(rs->rules.inactive.ptr, rule);
rs->rules.inactive.rcount--;
}
*ticket = ++rs->rules.inactive.ticket;
rs->rules.inactive.open = 1;
return (0);
}
void
pf_rollback_rules(u_int32_t ticket, char *anchor)
{
struct pf_ruleset *rs;
struct pf_rule *rule;
rs = pf_find_ruleset(anchor);
if (rs == NULL || !rs->rules.inactive.open ||
rs->rules.inactive.ticket != ticket)
return;
while ((rule = TAILQ_FIRST(rs->rules.inactive.ptr)) != NULL) {
pf_rm_rule(rs->rules.inactive.ptr, rule);
rs->rules.inactive.rcount--;
}
rs->rules.inactive.open = 0;
/* queue defs only in the main ruleset */
if (anchor[0])
return;
pf_free_queues(pf_queues_inactive);
}
void
pf_free_queues(struct pf_queuehead *where)
{
struct pf_queuespec *q, *qtmp;
TAILQ_FOREACH_SAFE(q, where, entries, qtmp) {
TAILQ_REMOVE(where, q, entries);
pfi_kif_unref(q->kif, PFI_KIF_REF_RULE);
pool_put(&pf_queue_pl, q);
}
}
void
pf_remove_queues(void)
{
struct pf_queuespec *q;
struct ifnet *ifp;
/* put back interfaces in normal queueing mode */
TAILQ_FOREACH(q, pf_queues_active, entries) {
if (q->parent_qid != 0)
continue;
ifp = q->kif->pfik_ifp;
if (ifp == NULL)
continue;
ifq_attach(&ifp->if_snd, ifq_priq_ops, NULL);
}
}
struct pf_queue_if {
struct ifnet *ifp;
const struct ifq_ops *ifqops;
const struct pfq_ops *pfqops;
void *disc;
struct pf_queue_if *next;
};
static inline struct pf_queue_if *
pf_ifp2q(struct pf_queue_if *list, struct ifnet *ifp)
{
struct pf_queue_if *qif = list;
while (qif != NULL) {
if (qif->ifp == ifp)
return (qif);
qif = qif->next;
}
return (qif);
}
int
pf_create_queues(void)
{
struct pf_queuespec *q;
struct ifnet *ifp;
struct pf_queue_if *list = NULL, *qif;
int error;
/*
* Find root queues and allocate traffic conditioner
* private data for these interfaces
*/
TAILQ_FOREACH(q, pf_queues_active, entries) {
if (q->parent_qid != 0)
continue;
ifp = q->kif->pfik_ifp;
if (ifp == NULL)
continue;
qif = malloc(sizeof(*qif), M_TEMP, M_WAITOK);
qif->ifp = ifp;
if (q->flags & PFQS_ROOTCLASS) {
qif->ifqops = ifq_hfsc_ops;
qif->pfqops = pfq_hfsc_ops;
} else {
qif->ifqops = ifq_fqcodel_ops;
qif->pfqops = pfq_fqcodel_ops;
}
qif->disc = qif->pfqops->pfq_alloc(ifp);
qif->next = list;
list = qif;
}
/* and now everything */
TAILQ_FOREACH(q, pf_queues_active, entries) {
ifp = q->kif->pfik_ifp;
if (ifp == NULL)
continue;
qif = pf_ifp2q(list, ifp);
KASSERT(qif != NULL);
error = qif->pfqops->pfq_addqueue(qif->disc, q);
if (error != 0)
goto error;
}
/* find root queues in old list to disable them if necessary */
TAILQ_FOREACH(q, pf_queues_inactive, entries) {
if (q->parent_qid != 0)
continue;
ifp = q->kif->pfik_ifp;
if (ifp == NULL)
continue;
qif = pf_ifp2q(list, ifp);
if (qif != NULL)
continue;
ifq_attach(&ifp->if_snd, ifq_priq_ops, NULL);
}
/* commit the new queues */
while (list != NULL) {
qif = list;
list = qif->next;
ifp = qif->ifp;
ifq_attach(&ifp->if_snd, qif->ifqops, qif->disc);
free(qif, M_TEMP, sizeof(*qif));
}
return (0);
error:
while (list != NULL) {
qif = list;
list = qif->next;
qif->pfqops->pfq_free(qif->disc);
free(qif, M_TEMP, sizeof(*qif));
}
return (error);
}
int
pf_commit_queues(void)
{
struct pf_queuehead *qswap;
int error;
/* swap */
qswap = pf_queues_active;
pf_queues_active = pf_queues_inactive;
pf_queues_inactive = qswap;
error = pf_create_queues();
if (error != 0) {
pf_queues_inactive = pf_queues_active;
pf_queues_active = qswap;
return (error);
}
pf_free_queues(pf_queues_inactive);
return (0);
}
const struct pfq_ops *
pf_queue_manager(struct pf_queuespec *q)
{
if (q->flags & PFQS_FLOWQUEUE)
return pfq_fqcodel_ops;
return (/* pfq_default_ops */ NULL);
}
#define PF_MD5_UPD(st, elm) \
MD5Update(ctx, (u_int8_t *) &(st)->elm, sizeof((st)->elm))
#define PF_MD5_UPD_STR(st, elm) \
MD5Update(ctx, (u_int8_t *) (st)->elm, strlen((st)->elm))
#define PF_MD5_UPD_HTONL(st, elm, stor) do { \
(stor) = htonl((st)->elm); \
MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int32_t));\
} while (0)
#define PF_MD5_UPD_HTONS(st, elm, stor) do { \
(stor) = htons((st)->elm); \
MD5Update(ctx, (u_int8_t *) &(stor), sizeof(u_int16_t));\
} while (0)
void
pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr)
{
PF_MD5_UPD(pfr, addr.type);
switch (pfr->addr.type) {
case PF_ADDR_DYNIFTL:
PF_MD5_UPD(pfr, addr.v.ifname);
PF_MD5_UPD(pfr, addr.iflags);
break;
case PF_ADDR_TABLE:
if (strncmp(pfr->addr.v.tblname, PF_OPTIMIZER_TABLE_PFX,
strlen(PF_OPTIMIZER_TABLE_PFX)))
PF_MD5_UPD(pfr, addr.v.tblname);
break;
case PF_ADDR_ADDRMASK:
/* XXX ignore af? */
PF_MD5_UPD(pfr, addr.v.a.addr.addr32);
PF_MD5_UPD(pfr, addr.v.a.mask.addr32);
break;
case PF_ADDR_RTLABEL:
PF_MD5_UPD(pfr, addr.v.rtlabelname);
break;
}
PF_MD5_UPD(pfr, port[0]);
PF_MD5_UPD(pfr, port[1]);
PF_MD5_UPD(pfr, neg);
PF_MD5_UPD(pfr, port_op);
}
void
pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule)
{
u_int16_t x;
u_int32_t y;
pf_hash_rule_addr(ctx, &rule->src);
pf_hash_rule_addr(ctx, &rule->dst);
PF_MD5_UPD_STR(rule, label);
PF_MD5_UPD_STR(rule, ifname);
PF_MD5_UPD_STR(rule, rcv_ifname);
PF_MD5_UPD_STR(rule, match_tagname);
PF_MD5_UPD_HTONS(rule, match_tag, x); /* dup? */
PF_MD5_UPD_HTONL(rule, os_fingerprint, y);
PF_MD5_UPD_HTONL(rule, prob, y);
PF_MD5_UPD_HTONL(rule, uid.uid[0], y);
PF_MD5_UPD_HTONL(rule, uid.uid[1], y);
PF_MD5_UPD(rule, uid.op);
PF_MD5_UPD_HTONL(rule, gid.gid[0], y);
PF_MD5_UPD_HTONL(rule, gid.gid[1], y);
PF_MD5_UPD(rule, gid.op);
PF_MD5_UPD_HTONL(rule, rule_flag, y);
PF_MD5_UPD(rule, action);
PF_MD5_UPD(rule, direction);
PF_MD5_UPD(rule, af);
PF_MD5_UPD(rule, quick);
PF_MD5_UPD(rule, ifnot);
PF_MD5_UPD(rule, rcvifnot);
PF_MD5_UPD(rule, match_tag_not);
PF_MD5_UPD(rule, keep_state);
PF_MD5_UPD(rule, proto);
PF_MD5_UPD(rule, type);
PF_MD5_UPD(rule, code);
PF_MD5_UPD(rule, flags);
PF_MD5_UPD(rule, flagset);
PF_MD5_UPD(rule, allow_opts);
PF_MD5_UPD(rule, rt);
PF_MD5_UPD(rule, tos);
}
int
pf_commit_rules(u_int32_t ticket, char *anchor)
{
struct pf_ruleset *rs;
struct pf_rule *rule;
struct pf_rulequeue *old_rules;
u_int32_t old_rcount;
/* Make sure any expired rules get removed from active rules first. */
pf_purge_expired_rules();
rs = pf_find_ruleset(anchor);
if (rs == NULL || !rs->rules.inactive.open ||
ticket != rs->rules.inactive.ticket)
return (EBUSY);
if (rs == &pf_main_ruleset)
pf_calc_chksum(rs);
/* Swap rules, keep the old. */
old_rules = rs->rules.active.ptr;
old_rcount = rs->rules.active.rcount;
rs->rules.active.ptr = rs->rules.inactive.ptr;
rs->rules.active.rcount = rs->rules.inactive.rcount;
rs->rules.inactive.ptr = old_rules;
rs->rules.inactive.rcount = old_rcount;
rs->rules.active.ticket = rs->rules.inactive.ticket;
pf_calc_skip_steps(rs->rules.active.ptr);
/* Purge the old rule list. */
while ((rule = TAILQ_FIRST(old_rules)) != NULL)
pf_rm_rule(old_rules, rule);
rs->rules.inactive.rcount = 0;
rs->rules.inactive.open = 0;
pf_remove_if_empty_ruleset(rs);
/* queue defs only in the main ruleset */
if (anchor[0])
return (0);
return (pf_commit_queues());
}
void
pf_calc_chksum(struct pf_ruleset *rs)
{
MD5_CTX ctx;
struct pf_rule *rule;
u_int8_t digest[PF_MD5_DIGEST_LENGTH];
MD5Init(&ctx);
if (rs->rules.inactive.rcount) {
TAILQ_FOREACH(rule, rs->rules.inactive.ptr, entries) {
pf_hash_rule(&ctx, rule);
}
}
MD5Final(digest, &ctx);
memcpy(pf_status.pf_chksum, digest, sizeof(pf_status.pf_chksum));
}
int
pf_addr_setup(struct pf_ruleset *ruleset, struct pf_addr_wrap *addr,
sa_family_t af)
{
if (pfi_dynaddr_setup(addr, af, PR_WAITOK) ||
pf_tbladdr_setup(ruleset, addr, PR_WAITOK) ||
pf_rtlabel_add(addr))
return (EINVAL);
return (0);
}
struct pfi_kif *
pf_kif_setup(struct pfi_kif *kif_buf)
{
struct pfi_kif *kif;
if (kif_buf == NULL)
return (NULL);
KASSERT(kif_buf->pfik_name[0] != '\0');
kif = pfi_kif_get(kif_buf->pfik_name, &kif_buf);
if (kif_buf != NULL)
pfi_kif_free(kif_buf);
pfi_kif_ref(kif, PFI_KIF_REF_RULE);
return (kif);
}
void
pf_addr_copyout(struct pf_addr_wrap *addr)
{
pfi_dynaddr_copyout(addr);
pf_tbladdr_copyout(addr);
pf_rtlabel_copyout(addr);
}
int
pf_states_clr(struct pfioc_state_kill *psk)
{
struct pf_state *s, *nexts;
struct pf_state *head, *tail;
u_int killed = 0;
int error;
NET_LOCK();
/* lock against the gc removing an item from the list */
error = rw_enter(&pf_state_list.pfs_rwl, RW_READ|RW_INTR);
if (error != 0)
goto unlock;
/* get a snapshot view of the ends of the list to traverse between */
mtx_enter(&pf_state_list.pfs_mtx);
head = TAILQ_FIRST(&pf_state_list.pfs_list);
tail = TAILQ_LAST(&pf_state_list.pfs_list, pf_state_queue);
mtx_leave(&pf_state_list.pfs_mtx);
s = NULL;
nexts = head;
PF_LOCK();
PF_STATE_ENTER_WRITE();
while (s != tail) {
s = nexts;
nexts = TAILQ_NEXT(s, entry_list);
if (s->timeout == PFTM_UNLINKED)
continue;
if (!psk->psk_ifname[0] || !strcmp(psk->psk_ifname,
s->kif->pfik_name)) {
#if NPFSYNC > 0
/* don't send out individual delete messages */
SET(s->state_flags, PFSTATE_NOSYNC);
#endif /* NPFSYNC > 0 */
pf_remove_state(s);
killed++;
}
}
PF_STATE_EXIT_WRITE();
#if NPFSYNC > 0
pfsync_clear_states(pf_status.hostid, psk->psk_ifname);
#endif /* NPFSYNC > 0 */
PF_UNLOCK();
rw_exit(&pf_state_list.pfs_rwl);
psk->psk_killed = killed;
unlock:
NET_UNLOCK();
return (error);
}
int
pf_states_get(struct pfioc_states *ps)
{
struct pf_state *head, *tail;
struct pf_state *next, *state;
struct pfsync_state *p, pstore;
u_int32_t nr = 0;
int error;
if (ps->ps_len == 0) {
nr = pf_status.states;
ps->ps_len = sizeof(struct pfsync_state) * nr;
return (0);
}
p = ps->ps_states;
/* lock against the gc removing an item from the list */
error = rw_enter(&pf_state_list.pfs_rwl, RW_READ|RW_INTR);
if (error != 0)
return (error);
/* get a snapshot view of the ends of the list to traverse between */
mtx_enter(&pf_state_list.pfs_mtx);
head = TAILQ_FIRST(&pf_state_list.pfs_list);
tail = TAILQ_LAST(&pf_state_list.pfs_list, pf_state_queue);
mtx_leave(&pf_state_list.pfs_mtx);
state = NULL;
next = head;
while (state != tail) {
state = next;
next = TAILQ_NEXT(state, entry_list);
if (state->timeout == PFTM_UNLINKED)
continue;
if ((nr+1) * sizeof(*p) > ps->ps_len)
break;
pf_state_export(&pstore, state);
error = copyout(&pstore, p, sizeof(*p));
if (error)
goto fail;
p++;
nr++;
}
ps->ps_len = sizeof(struct pfsync_state) * nr;
fail:
rw_exit(&pf_state_list.pfs_rwl);
return (error);
}
int
pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
{
int error = 0;
/* XXX keep in sync with switch() below */
if (securelevel > 1)
switch (cmd) {
case DIOCGETRULES:
case DIOCGETRULE:
case DIOCGETSTATE:
case DIOCSETSTATUSIF:
case DIOCGETSTATUS:
case DIOCCLRSTATUS:
case DIOCNATLOOK:
case DIOCSETDEBUG:
case DIOCGETSTATES:
case DIOCGETTIMEOUT:
case DIOCGETLIMIT:
case DIOCGETRULESETS:
case DIOCGETRULESET:
case DIOCGETQUEUES:
case DIOCGETQUEUE:
case DIOCGETQSTATS:
case DIOCRGETTABLES:
case DIOCRGETTSTATS:
case DIOCRCLRTSTATS:
case DIOCRCLRADDRS:
case DIOCRADDADDRS:
case DIOCRDELADDRS:
case DIOCRSETADDRS:
case DIOCRGETADDRS:
case DIOCRGETASTATS:
case DIOCRCLRASTATS:
case DIOCRTSTADDRS:
case DIOCOSFPGET:
case DIOCGETSRCNODES:
case DIOCCLRSRCNODES:
case DIOCIGETIFACES:
case DIOCSETIFFLAG:
case DIOCCLRIFFLAG:
case DIOCGETSYNFLWATS:
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
case DIOCRDELTABLES:
case DIOCRSETTFLAGS:
if (((struct pfioc_table *)addr)->pfrio_flags &
PFR_FLAG_DUMMY)
break; /* dummy operation ok */
return (EPERM);
default:
return (EPERM);
}
if (!(flags & FWRITE))
switch (cmd) {
case DIOCGETRULES:
case DIOCGETSTATE:
case DIOCGETSTATUS:
case DIOCGETSTATES:
case DIOCGETTIMEOUT:
case DIOCGETLIMIT:
case DIOCGETRULESETS:
case DIOCGETRULESET:
case DIOCGETQUEUES:
case DIOCGETQUEUE:
case DIOCGETQSTATS:
case DIOCNATLOOK:
case DIOCRGETTABLES:
case DIOCRGETTSTATS:
case DIOCRGETADDRS:
case DIOCRGETASTATS:
case DIOCRTSTADDRS:
case DIOCOSFPGET:
case DIOCGETSRCNODES:
case DIOCIGETIFACES:
case DIOCGETSYNFLWATS:
break;
case DIOCRCLRTABLES:
case DIOCRADDTABLES:
case DIOCRDELTABLES:
case DIOCRCLRTSTATS:
case DIOCRCLRADDRS:
case DIOCRADDADDRS:
case DIOCRDELADDRS:
case DIOCRSETADDRS:
case DIOCRSETTFLAGS:
if (((struct pfioc_table *)addr)->pfrio_flags &
PFR_FLAG_DUMMY) {
flags |= FWRITE; /* need write lock for dummy */
break; /* dummy operation ok */
}
return (EACCES);
case DIOCGETRULE:
if (((struct pfioc_rule *)addr)->action ==
PF_GET_CLR_CNTR)
return (EACCES);
break;
default:
return (EACCES);
}
if (flags & FWRITE)
rw_enter_write(&pfioctl_rw);
else
rw_enter_read(&pfioctl_rw);
switch (cmd) {
case DIOCSTART:
NET_LOCK();
PF_LOCK();
if (pf_status.running)
error = EEXIST;
else {
pf_status.running = 1;
pf_status.since = getuptime();
if (pf_status.stateid == 0) {
pf_status.stateid = gettime();
pf_status.stateid = pf_status.stateid << 32;
}
timeout_add_sec(&pf_purge_to, 1);
pf_create_queues();
DPFPRINTF(LOG_NOTICE, "pf: started");
}
PF_UNLOCK();
NET_UNLOCK();
break;
case DIOCSTOP:
NET_LOCK();
PF_LOCK();
if (!pf_status.running)
error = ENOENT;
else {
pf_status.running = 0;
pf_status.since = getuptime();
pf_remove_queues();
DPFPRINTF(LOG_NOTICE, "pf: stopped");
}
PF_UNLOCK();
NET_UNLOCK();
break;
case DIOCGETQUEUES: {
struct pfioc_queue *pq = (struct pfioc_queue *)addr;
struct pf_queuespec *qs;
u_int32_t nr = 0;
NET_LOCK();
PF_LOCK();
pq->ticket = pf_main_ruleset.rules.active.ticket;
/* save state to not run over them all each time? */
qs = TAILQ_FIRST(pf_queues_active);
while (qs != NULL) {
qs = TAILQ_NEXT(qs, entries);
nr++;
}
pq->nr = nr;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETQUEUE: {
struct pfioc_queue *pq = (struct pfioc_queue *)addr;
struct pf_queuespec *qs;
u_int32_t nr = 0;
NET_LOCK();
PF_LOCK();
if (pq->ticket != pf_main_ruleset.rules.active.ticket) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
/* save state to not run over them all each time? */
qs = TAILQ_FIRST(pf_queues_active);
while ((qs != NULL) && (nr++ < pq->nr))
qs = TAILQ_NEXT(qs, entries);
if (qs == NULL) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
memcpy(&pq->queue, qs, sizeof(pq->queue));
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETQSTATS: {
struct pfioc_qstats *pq = (struct pfioc_qstats *)addr;
struct pf_queuespec *qs;
u_int32_t nr;
int nbytes;
NET_LOCK();
PF_LOCK();
if (pq->ticket != pf_main_ruleset.rules.active.ticket) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
nbytes = pq->nbytes;
nr = 0;
/* save state to not run over them all each time? */
qs = TAILQ_FIRST(pf_queues_active);
while ((qs != NULL) && (nr++ < pq->nr))
qs = TAILQ_NEXT(qs, entries);
if (qs == NULL) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
memcpy(&pq->queue, qs, sizeof(pq->queue));
/* It's a root flow queue but is not an HFSC root class */
if ((qs->flags & PFQS_FLOWQUEUE) && qs->parent_qid == 0 &&
!(qs->flags & PFQS_ROOTCLASS))
error = pfq_fqcodel_ops->pfq_qstats(qs, pq->buf,
&nbytes);
else
error = pfq_hfsc_ops->pfq_qstats(qs, pq->buf,
&nbytes);
if (error == 0)
pq->nbytes = nbytes;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCADDQUEUE: {
struct pfioc_queue *q = (struct pfioc_queue *)addr;
struct pf_queuespec *qs;
qs = pool_get(&pf_queue_pl, PR_WAITOK|PR_LIMITFAIL|PR_ZERO);
if (qs == NULL) {
error = ENOMEM;
goto fail;
}
NET_LOCK();
PF_LOCK();
if (q->ticket != pf_main_ruleset.rules.inactive.ticket) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
pool_put(&pf_queue_pl, qs);
goto fail;
}
memcpy(qs, &q->queue, sizeof(*qs));
qs->qid = pf_qname2qid(qs->qname, 1);
if (qs->qid == 0) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
pool_put(&pf_queue_pl, qs);
goto fail;
}
if (qs->parent[0] && (qs->parent_qid =
pf_qname2qid(qs->parent, 0)) == 0) {
error = ESRCH;
PF_UNLOCK();
NET_UNLOCK();
pool_put(&pf_queue_pl, qs);
goto fail;
}
qs->kif = pfi_kif_get(qs->ifname, NULL);
if (qs->kif == NULL) {
error = ESRCH;
PF_UNLOCK();
NET_UNLOCK();
pool_put(&pf_queue_pl, qs);
goto fail;
}
/* XXX resolve bw percentage specs */
pfi_kif_ref(qs->kif, PFI_KIF_REF_RULE);
TAILQ_INSERT_TAIL(pf_queues_inactive, qs, entries);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCADDRULE: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *rule, *tail;
rule = pool_get(&pf_rule_pl, PR_WAITOK|PR_LIMITFAIL|PR_ZERO);
if (rule == NULL) {
error = ENOMEM;
goto fail;
}
if ((error = pf_rule_copyin(&pr->rule, rule))) {
pf_rule_free(rule);
rule = NULL;
goto fail;
}
if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
error = EINVAL;
pf_rule_free(rule);
rule = NULL;
goto fail;
}
if ((error = pf_rule_checkaf(rule))) {
pf_rule_free(rule);
rule = NULL;
goto fail;
}
if (rule->src.addr.type == PF_ADDR_NONE ||
rule->dst.addr.type == PF_ADDR_NONE) {
error = EINVAL;
pf_rule_free(rule);
rule = NULL;
goto fail;
}
if (rule->rt && !rule->direction) {
error = EINVAL;
pf_rule_free(rule);
rule = NULL;
goto fail;
}
NET_LOCK();
PF_LOCK();
pr->anchor[sizeof(pr->anchor) - 1] = '\0';
ruleset = pf_find_ruleset(pr->anchor);
if (ruleset == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
pf_rule_free(rule);
goto fail;
}
if (pr->ticket != ruleset->rules.inactive.ticket) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
pf_rule_free(rule);
goto fail;
}
rule->cuid = p->p_ucred->cr_ruid;
rule->cpid = p->p_p->ps_pid;
tail = TAILQ_LAST(ruleset->rules.inactive.ptr,
pf_rulequeue);
if (tail)
rule->nr = tail->nr + 1;
else
rule->nr = 0;
rule->kif = pf_kif_setup(rule->kif);
rule->rcv_kif = pf_kif_setup(rule->rcv_kif);
rule->rdr.kif = pf_kif_setup(rule->rdr.kif);
rule->nat.kif = pf_kif_setup(rule->nat.kif);
rule->route.kif = pf_kif_setup(rule->route.kif);
if (rule->overload_tblname[0]) {
if ((rule->overload_tbl = pfr_attach_table(ruleset,
rule->overload_tblname, PR_WAITOK)) == NULL)
error = EINVAL;
else
rule->overload_tbl->pfrkt_flags |= PFR_TFLAG_ACTIVE;
}
if (pf_addr_setup(ruleset, &rule->src.addr, rule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &rule->rdr.addr, rule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &rule->nat.addr, rule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &rule->route.addr, rule->af))
error = EINVAL;
if (pf_anchor_setup(rule, ruleset, pr->anchor_call))
error = EINVAL;
if (error) {
pf_rm_rule(NULL, rule);
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
TAILQ_INSERT_TAIL(ruleset->rules.inactive.ptr,
rule, entries);
rule->ruleset = ruleset;
ruleset->rules.inactive.rcount++;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETRULES: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *tail;
NET_LOCK();
PF_LOCK();
pr->anchor[sizeof(pr->anchor) - 1] = '\0';
ruleset = pf_find_ruleset(pr->anchor);
if (ruleset == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
tail = TAILQ_LAST(ruleset->rules.active.ptr, pf_rulequeue);
if (tail)
pr->nr = tail->nr + 1;
else
pr->nr = 0;
pr->ticket = ruleset->rules.active.ticket;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETRULE: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *rule;
int i;
NET_LOCK();
PF_LOCK();
pr->anchor[sizeof(pr->anchor) - 1] = '\0';
ruleset = pf_find_ruleset(pr->anchor);
if (ruleset == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
if (pr->ticket != ruleset->rules.active.ticket) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
rule = TAILQ_FIRST(ruleset->rules.active.ptr);
while ((rule != NULL) && (rule->nr != pr->nr))
rule = TAILQ_NEXT(rule, entries);
if (rule == NULL) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
memcpy(&pr->rule, rule, sizeof(struct pf_rule));
memset(&pr->rule.entries, 0, sizeof(pr->rule.entries));
pr->rule.kif = NULL;
pr->rule.nat.kif = NULL;
pr->rule.rdr.kif = NULL;
pr->rule.route.kif = NULL;
pr->rule.rcv_kif = NULL;
pr->rule.anchor = NULL;
pr->rule.overload_tbl = NULL;
pr->rule.pktrate.limit /= PF_THRESHOLD_MULT;
memset(&pr->rule.gcle, 0, sizeof(pr->rule.gcle));
pr->rule.ruleset = NULL;
if (pf_anchor_copyout(ruleset, rule, pr)) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
pf_addr_copyout(&pr->rule.src.addr);
pf_addr_copyout(&pr->rule.dst.addr);
pf_addr_copyout(&pr->rule.rdr.addr);
pf_addr_copyout(&pr->rule.nat.addr);
pf_addr_copyout(&pr->rule.route.addr);
for (i = 0; i < PF_SKIP_COUNT; ++i)
if (rule->skip[i].ptr == NULL)
pr->rule.skip[i].nr = (u_int32_t)-1;
else
pr->rule.skip[i].nr =
rule->skip[i].ptr->nr;
if (pr->action == PF_GET_CLR_CNTR) {
rule->evaluations = 0;
rule->packets[0] = rule->packets[1] = 0;
rule->bytes[0] = rule->bytes[1] = 0;
rule->states_tot = 0;
}
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCCHANGERULE: {
struct pfioc_rule *pcr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *oldrule = NULL, *newrule = NULL;
u_int32_t nr = 0;
if (pcr->action < PF_CHANGE_ADD_HEAD ||
pcr->action > PF_CHANGE_GET_TICKET) {
error = EINVAL;
goto fail;
}
if (pcr->action == PF_CHANGE_GET_TICKET) {
NET_LOCK();
PF_LOCK();
ruleset = pf_find_ruleset(pcr->anchor);
if (ruleset == NULL)
error = EINVAL;
else
pcr->ticket = ++ruleset->rules.active.ticket;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
if (pcr->action != PF_CHANGE_REMOVE) {
newrule = pool_get(&pf_rule_pl,
PR_WAITOK|PR_LIMITFAIL|PR_ZERO);
if (newrule == NULL) {
error = ENOMEM;
goto fail;
}
if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
error = EINVAL;
pool_put(&pf_rule_pl, newrule);
goto fail;
}
error = pf_rule_copyin(&pcr->rule, newrule);
if (error != 0) {
pf_rule_free(newrule);
newrule = NULL;
goto fail;
}
if ((error = pf_rule_checkaf(newrule))) {
pf_rule_free(newrule);
newrule = NULL;
goto fail;
}
if (newrule->rt && !newrule->direction) {
pf_rule_free(newrule);
error = EINVAL;
newrule = NULL;
goto fail;
}
}
NET_LOCK();
PF_LOCK();
ruleset = pf_find_ruleset(pcr->anchor);
if (ruleset == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
pf_rule_free(newrule);
goto fail;
}
if (pcr->ticket != ruleset->rules.active.ticket) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
pf_rule_free(newrule);
goto fail;
}
if (pcr->action != PF_CHANGE_REMOVE) {
KASSERT(newrule != NULL);
newrule->cuid = p->p_ucred->cr_ruid;
newrule->cpid = p->p_p->ps_pid;
newrule->kif = pf_kif_setup(newrule->kif);
newrule->rcv_kif = pf_kif_setup(newrule->rcv_kif);
newrule->rdr.kif = pf_kif_setup(newrule->rdr.kif);
newrule->nat.kif = pf_kif_setup(newrule->nat.kif);
newrule->route.kif = pf_kif_setup(newrule->route.kif);
if (newrule->overload_tblname[0]) {
newrule->overload_tbl = pfr_attach_table(
ruleset, newrule->overload_tblname,
PR_WAITOK);
if (newrule->overload_tbl == NULL)
error = EINVAL;
else
newrule->overload_tbl->pfrkt_flags |=
PFR_TFLAG_ACTIVE;
}
if (pf_addr_setup(ruleset, &newrule->src.addr,
newrule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &newrule->dst.addr,
newrule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &newrule->rdr.addr,
newrule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &newrule->nat.addr,
newrule->af))
error = EINVAL;
if (pf_addr_setup(ruleset, &newrule->route.addr,
newrule->af))
error = EINVAL;
if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call))
error = EINVAL;
if (error) {
pf_rm_rule(NULL, newrule);
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
}
if (pcr->action == PF_CHANGE_ADD_HEAD)
oldrule = TAILQ_FIRST(ruleset->rules.active.ptr);
else if (pcr->action == PF_CHANGE_ADD_TAIL)
oldrule = TAILQ_LAST(ruleset->rules.active.ptr,
pf_rulequeue);
else {
oldrule = TAILQ_FIRST(ruleset->rules.active.ptr);
while ((oldrule != NULL) && (oldrule->nr != pcr->nr))
oldrule = TAILQ_NEXT(oldrule, entries);
if (oldrule == NULL) {
if (newrule != NULL)
pf_rm_rule(NULL, newrule);
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
}
if (pcr->action == PF_CHANGE_REMOVE) {
pf_rm_rule(ruleset->rules.active.ptr, oldrule);
ruleset->rules.active.rcount--;
} else {
if (oldrule == NULL)
TAILQ_INSERT_TAIL(
ruleset->rules.active.ptr,
newrule, entries);
else if (pcr->action == PF_CHANGE_ADD_HEAD ||
pcr->action == PF_CHANGE_ADD_BEFORE)
TAILQ_INSERT_BEFORE(oldrule, newrule, entries);
else
TAILQ_INSERT_AFTER(
ruleset->rules.active.ptr,
oldrule, newrule, entries);
ruleset->rules.active.rcount++;
newrule->ruleset = ruleset;
}
nr = 0;
TAILQ_FOREACH(oldrule, ruleset->rules.active.ptr, entries)
oldrule->nr = nr++;
ruleset->rules.active.ticket++;
pf_calc_skip_steps(ruleset->rules.active.ptr);
pf_remove_if_empty_ruleset(ruleset);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCCLRSTATES:
error = pf_states_clr((struct pfioc_state_kill *)addr);
break;
case DIOCKILLSTATES: {
struct pf_state *s, *nexts;
struct pf_state_item *si, *sit;
struct pf_state_key *sk, key;
struct pf_addr *srcaddr, *dstaddr;
u_int16_t srcport, dstport;
struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr;
u_int i, killed = 0;
const int dirs[] = { PF_IN, PF_OUT };
int sidx, didx;
if (psk->psk_pfcmp.id) {
if (psk->psk_pfcmp.creatorid == 0)
psk->psk_pfcmp.creatorid = pf_status.hostid;
NET_LOCK();
PF_LOCK();
PF_STATE_ENTER_WRITE();
if ((s = pf_find_state_byid(&psk->psk_pfcmp))) {
pf_remove_state(s);
psk->psk_killed = 1;
}
PF_STATE_EXIT_WRITE();
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
if (psk->psk_af && psk->psk_proto &&
psk->psk_src.port_op == PF_OP_EQ &&
psk->psk_dst.port_op == PF_OP_EQ) {
key.af = psk->psk_af;
key.proto = psk->psk_proto;
key.rdomain = psk->psk_rdomain;
NET_LOCK();
PF_LOCK();
PF_STATE_ENTER_WRITE();
for (i = 0; i < nitems(dirs); i++) {
if (dirs[i] == PF_IN) {
sidx = 0;
didx = 1;
} else {
sidx = 1;
didx = 0;
}
pf_addrcpy(&key.addr[sidx],
&psk->psk_src.addr.v.a.addr, key.af);
pf_addrcpy(&key.addr[didx],
&psk->psk_dst.addr.v.a.addr, key.af);
key.port[sidx] = psk->psk_src.port[0];
key.port[didx] = psk->psk_dst.port[0];
sk = RB_FIND(pf_state_tree, &pf_statetbl, &key);
if (sk == NULL)
continue;
TAILQ_FOREACH_SAFE(si, &sk->states, entry, sit)
if (((si->s->key[PF_SK_WIRE]->af ==
si->s->key[PF_SK_STACK]->af &&
sk == (dirs[i] == PF_IN ?
si->s->key[PF_SK_WIRE] :
si->s->key[PF_SK_STACK])) ||
(si->s->key[PF_SK_WIRE]->af !=
si->s->key[PF_SK_STACK]->af &&
dirs[i] == PF_IN &&
(sk == si->s->key[PF_SK_STACK] ||
sk == si->s->key[PF_SK_WIRE]))) &&
(!psk->psk_ifname[0] ||
(si->s->kif != pfi_all &&
!strcmp(psk->psk_ifname,
si->s->kif->pfik_name)))) {
pf_remove_state(si->s);
killed++;
}
}
if (killed)
psk->psk_killed = killed;
PF_STATE_EXIT_WRITE();
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
NET_LOCK();
PF_LOCK();
PF_STATE_ENTER_WRITE();
for (s = RB_MIN(pf_state_tree_id, &tree_id); s;
s = nexts) {
nexts = RB_NEXT(pf_state_tree_id, &tree_id, s);
if (s->direction == PF_OUT) {
sk = s->key[PF_SK_STACK];
srcaddr = &sk->addr[1];
dstaddr = &sk->addr[0];
srcport = sk->port[1];
dstport = sk->port[0];
} else {
sk = s->key[PF_SK_WIRE];
srcaddr = &sk->addr[0];
dstaddr = &sk->addr[1];
srcport = sk->port[0];
dstport = sk->port[1];
}
if ((!psk->psk_af || sk->af == psk->psk_af)
&& (!psk->psk_proto || psk->psk_proto ==
sk->proto) && psk->psk_rdomain == sk->rdomain &&
pf_match_addr(psk->psk_src.neg,
&psk->psk_src.addr.v.a.addr,
&psk->psk_src.addr.v.a.mask,
srcaddr, sk->af) &&
pf_match_addr(psk->psk_dst.neg,
&psk->psk_dst.addr.v.a.addr,
&psk->psk_dst.addr.v.a.mask,
dstaddr, sk->af) &&
(psk->psk_src.port_op == 0 ||
pf_match_port(psk->psk_src.port_op,
psk->psk_src.port[0], psk->psk_src.port[1],
srcport)) &&
(psk->psk_dst.port_op == 0 ||
pf_match_port(psk->psk_dst.port_op,
psk->psk_dst.port[0], psk->psk_dst.port[1],
dstport)) &&
(!psk->psk_label[0] || (s->rule.ptr->label[0] &&
!strcmp(psk->psk_label, s->rule.ptr->label))) &&
(!psk->psk_ifname[0] || !strcmp(psk->psk_ifname,
s->kif->pfik_name))) {
pf_remove_state(s);
killed++;
}
}
psk->psk_killed = killed;
PF_STATE_EXIT_WRITE();
PF_UNLOCK();
NET_UNLOCK();
break;
}
#if NPFSYNC > 0
case DIOCADDSTATE: {
struct pfioc_state *ps = (struct pfioc_state *)addr;
struct pfsync_state *sp = &ps->state;
if (sp->timeout >= PFTM_MAX) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfsync_state_import(sp, PFSYNC_SI_IOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
#endif /* NPFSYNC > 0 */
case DIOCGETSTATE: {
struct pfioc_state *ps = (struct pfioc_state *)addr;
struct pf_state *s;
struct pf_state_cmp id_key;
memset(&id_key, 0, sizeof(id_key));
id_key.id = ps->state.id;
id_key.creatorid = ps->state.creatorid;
NET_LOCK();
PF_STATE_ENTER_READ();
s = pf_find_state_byid(&id_key);
s = pf_state_ref(s);
PF_STATE_EXIT_READ();
NET_UNLOCK();
if (s == NULL) {
error = ENOENT;
goto fail;
}
pf_state_export(&ps->state, s);
pf_state_unref(s);
break;
}
case DIOCGETSTATES:
error = pf_states_get((struct pfioc_states *)addr);
break;
case DIOCGETSTATUS: {
struct pf_status *s = (struct pf_status *)addr;
NET_LOCK();
PF_LOCK();
memcpy(s, &pf_status, sizeof(struct pf_status));
pfi_update_status(s->ifname, s);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETSTATUSIF: {
struct pfioc_iface *pi = (struct pfioc_iface *)addr;
NET_LOCK();
PF_LOCK();
if (pi->pfiio_name[0] == 0) {
memset(pf_status.ifname, 0, IFNAMSIZ);
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
strlcpy(pf_trans_set.statusif, pi->pfiio_name, IFNAMSIZ);
pf_trans_set.mask |= PF_TSET_STATUSIF;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCCLRSTATUS: {
struct pfioc_iface *pi = (struct pfioc_iface *)addr;
NET_LOCK();
PF_LOCK();
/* if ifname is specified, clear counters there only */
if (pi->pfiio_name[0]) {
pfi_update_status(pi->pfiio_name, NULL);
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
memset(pf_status.counters, 0, sizeof(pf_status.counters));
memset(pf_status.fcounters, 0, sizeof(pf_status.fcounters));
memset(pf_status.scounters, 0, sizeof(pf_status.scounters));
pf_status.since = getuptime();
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCNATLOOK: {
struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr;
struct pf_state_key *sk;
struct pf_state *state;
struct pf_state_key_cmp key;
int m = 0, direction = pnl->direction;
int sidx, didx;
switch (pnl->af) {
case AF_INET:
break;
#ifdef INET6
case AF_INET6:
break;
#endif /* INET6 */
default:
error = EAFNOSUPPORT;
goto fail;
}
/* NATLOOK src and dst are reversed, so reverse sidx/didx */
sidx = (direction == PF_IN) ? 1 : 0;
didx = (direction == PF_IN) ? 0 : 1;
if (!pnl->proto ||
PF_AZERO(&pnl->saddr, pnl->af) ||
PF_AZERO(&pnl->daddr, pnl->af) ||
((pnl->proto == IPPROTO_TCP ||
pnl->proto == IPPROTO_UDP) &&
(!pnl->dport || !pnl->sport)) ||
pnl->rdomain > RT_TABLEID_MAX)
error = EINVAL;
else {
key.af = pnl->af;
key.proto = pnl->proto;
key.rdomain = pnl->rdomain;
pf_addrcpy(&key.addr[sidx], &pnl->saddr, pnl->af);
key.port[sidx] = pnl->sport;
pf_addrcpy(&key.addr[didx], &pnl->daddr, pnl->af);
key.port[didx] = pnl->dport;
NET_LOCK();
PF_STATE_ENTER_READ();
state = pf_find_state_all(&key, direction, &m);
state = pf_state_ref(state);
PF_STATE_EXIT_READ();
NET_UNLOCK();
if (m > 1)
error = E2BIG; /* more than one state */
else if (state != NULL) {
sk = state->key[sidx];
pf_addrcpy(&pnl->rsaddr, &sk->addr[sidx],
sk->af);
pnl->rsport = sk->port[sidx];
pf_addrcpy(&pnl->rdaddr, &sk->addr[didx],
sk->af);
pnl->rdport = sk->port[didx];
pnl->rrdomain = sk->rdomain;
} else
error = ENOENT;
pf_state_unref(state);
}
break;
}
case DIOCSETTIMEOUT: {
struct pfioc_tm *pt = (struct pfioc_tm *)addr;
if (pt->timeout < 0 || pt->timeout >= PFTM_MAX ||
pt->seconds < 0) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0)
pt->seconds = 1;
pf_default_rule_new.timeout[pt->timeout] = pt->seconds;
pt->seconds = pf_default_rule.timeout[pt->timeout];
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETTIMEOUT: {
struct pfioc_tm *pt = (struct pfioc_tm *)addr;
if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
pt->seconds = pf_default_rule.timeout[pt->timeout];
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETLIMIT: {
struct pfioc_limit *pl = (struct pfioc_limit *)addr;
if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
pl->limit = pf_pool_limits[pl->index].limit;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETLIMIT: {
struct pfioc_limit *pl = (struct pfioc_limit *)addr;
NET_LOCK();
PF_LOCK();
if (pl->index < 0 || pl->index >= PF_LIMIT_MAX ||
pf_pool_limits[pl->index].pp == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
if (((struct pool *)pf_pool_limits[pl->index].pp)->pr_nout >
pl->limit) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
/* Fragments reference mbuf clusters. */
if (pl->index == PF_LIMIT_FRAGS && pl->limit > nmbclust) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
pf_pool_limits[pl->index].limit_new = pl->limit;
pl->limit = pf_pool_limits[pl->index].limit;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETDEBUG: {
u_int32_t *level = (u_int32_t *)addr;
NET_LOCK();
PF_LOCK();
pf_trans_set.debug = *level;
pf_trans_set.mask |= PF_TSET_DEBUG;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETRULESETS: {
struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr;
struct pf_ruleset *ruleset;
struct pf_anchor *anchor;
NET_LOCK();
PF_LOCK();
pr->path[sizeof(pr->path) - 1] = '\0';
if ((ruleset = pf_find_ruleset(pr->path)) == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
pr->nr = 0;
if (ruleset == &pf_main_ruleset) {
/* XXX kludge for pf_main_ruleset */
RB_FOREACH(anchor, pf_anchor_global, &pf_anchors)
if (anchor->parent == NULL)
pr->nr++;
} else {
RB_FOREACH(anchor, pf_anchor_node,
&ruleset->anchor->children)
pr->nr++;
}
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETRULESET: {
struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr;
struct pf_ruleset *ruleset;
struct pf_anchor *anchor;
u_int32_t nr = 0;
NET_LOCK();
PF_LOCK();
pr->path[sizeof(pr->path) - 1] = '\0';
if ((ruleset = pf_find_ruleset(pr->path)) == NULL) {
error = EINVAL;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
pr->name[0] = '\0';
if (ruleset == &pf_main_ruleset) {
/* XXX kludge for pf_main_ruleset */
RB_FOREACH(anchor, pf_anchor_global, &pf_anchors)
if (anchor->parent == NULL && nr++ == pr->nr) {
strlcpy(pr->name, anchor->name,
sizeof(pr->name));
break;
}
} else {
RB_FOREACH(anchor, pf_anchor_node,
&ruleset->anchor->children)
if (nr++ == pr->nr) {
strlcpy(pr->name, anchor->name,
sizeof(pr->name));
break;
}
}
PF_UNLOCK();
NET_UNLOCK();
if (!pr->name[0])
error = EBUSY;
break;
}
case DIOCRCLRTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != 0) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel,
io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRADDTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
goto fail;
}
error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size,
&io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL);
break;
}
case DIOCRDELTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_del_tables(io->pfrio_buffer, io->pfrio_size,
&io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRGETTABLES: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_get_tables(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRGETTSTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_tstats)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_get_tstats(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRCLRTSTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_clr_tstats(io->pfrio_buffer, io->pfrio_size,
&io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRSETTFLAGS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_table)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_set_tflags(io->pfrio_buffer, io->pfrio_size,
io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange,
&io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRCLRADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != 0) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel,
io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRADDADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
error = pfr_add_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
break;
}
case DIOCRDELADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_del_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRSETADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_set_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd,
&io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags |
PFR_FLAG_USERIOCTL, 0);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRGETADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRGETASTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_astats)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_get_astats(&io->pfrio_table, io->pfrio_buffer,
&io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRCLRASTATS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_clr_astats(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRTSTADDRS: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags |
PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCRINADEFINE: {
struct pfioc_table *io = (struct pfioc_table *)addr;
if (io->pfrio_esize != sizeof(struct pfr_addr)) {
error = ENODEV;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfr_ina_define(&io->pfrio_table, io->pfrio_buffer,
io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr,
io->pfrio_ticket, io->pfrio_flags | PFR_FLAG_USERIOCTL);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCOSFPADD: {
struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
error = pf_osfp_add(io);
break;
}
case DIOCOSFPGET: {
struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
error = pf_osfp_get(io);
break;
}
case DIOCXBEGIN: {
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe;
struct pfr_table *table;
int i;
if (io->esize != sizeof(*ioe)) {
error = ENODEV;
goto fail;
}
ioe = malloc(sizeof(*ioe), M_TEMP, M_WAITOK);
table = malloc(sizeof(*table), M_TEMP, M_WAITOK);
NET_LOCK();
PF_LOCK();
pf_default_rule_new = pf_default_rule;
PF_UNLOCK();
NET_UNLOCK();
memset(&pf_trans_set, 0, sizeof(pf_trans_set));
for (i = 0; i < io->size; i++) {
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EFAULT;
goto fail;
}
if (strnlen(ioe->anchor, sizeof(ioe->anchor)) ==
sizeof(ioe->anchor)) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = ENAMETOOLONG;
goto fail;
}
NET_LOCK();
PF_LOCK();
switch (ioe->type) {
case PF_TRANS_TABLE:
memset(table, 0, sizeof(*table));
strlcpy(table->pfrt_anchor, ioe->anchor,
sizeof(table->pfrt_anchor));
if ((error = pfr_ina_begin(table,
&ioe->ticket, NULL, 0))) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
goto fail;
}
break;
case PF_TRANS_RULESET:
if ((error = pf_begin_rules(&ioe->ticket,
ioe->anchor))) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
goto fail;
}
break;
default:
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EINVAL;
goto fail;
}
PF_UNLOCK();
NET_UNLOCK();
if (copyout(ioe, io->array+i, sizeof(io->array[i]))) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EFAULT;
goto fail;
}
}
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
break;
}
case DIOCXROLLBACK: {
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe;
struct pfr_table *table;
int i;
if (io->esize != sizeof(*ioe)) {
error = ENODEV;
goto fail;
}
ioe = malloc(sizeof(*ioe), M_TEMP, M_WAITOK);
table = malloc(sizeof(*table), M_TEMP, M_WAITOK);
for (i = 0; i < io->size; i++) {
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EFAULT;
goto fail;
}
if (strnlen(ioe->anchor, sizeof(ioe->anchor)) ==
sizeof(ioe->anchor)) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = ENAMETOOLONG;
goto fail;
}
NET_LOCK();
PF_LOCK();
switch (ioe->type) {
case PF_TRANS_TABLE:
memset(table, 0, sizeof(*table));
strlcpy(table->pfrt_anchor, ioe->anchor,
sizeof(table->pfrt_anchor));
if ((error = pfr_ina_rollback(table,
ioe->ticket, NULL, 0))) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
goto fail; /* really bad */
}
break;
case PF_TRANS_RULESET:
pf_rollback_rules(ioe->ticket, ioe->anchor);
break;
default:
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EINVAL;
goto fail; /* really bad */
}
PF_UNLOCK();
NET_UNLOCK();
}
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
break;
}
case DIOCXCOMMIT: {
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe;
struct pfr_table *table;
struct pf_ruleset *rs;
int i;
if (io->esize != sizeof(*ioe)) {
error = ENODEV;
goto fail;
}
ioe = malloc(sizeof(*ioe), M_TEMP, M_WAITOK);
table = malloc(sizeof(*table), M_TEMP, M_WAITOK);
/* first makes sure everything will succeed */
for (i = 0; i < io->size; i++) {
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EFAULT;
goto fail;
}
if (strnlen(ioe->anchor, sizeof(ioe->anchor)) ==
sizeof(ioe->anchor)) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = ENAMETOOLONG;
goto fail;
}
NET_LOCK();
PF_LOCK();
switch (ioe->type) {
case PF_TRANS_TABLE:
rs = pf_find_ruleset(ioe->anchor);
if (rs == NULL || !rs->topen || ioe->ticket !=
rs->tticket) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EBUSY;
goto fail;
}
break;
case PF_TRANS_RULESET:
rs = pf_find_ruleset(ioe->anchor);
if (rs == NULL ||
!rs->rules.inactive.open ||
rs->rules.inactive.ticket !=
ioe->ticket) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EBUSY;
goto fail;
}
break;
default:
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EINVAL;
goto fail;
}
PF_UNLOCK();
NET_UNLOCK();
}
NET_LOCK();
PF_LOCK();
/*
* Checked already in DIOCSETLIMIT, but check again as the
* situation might have changed.
*/
for (i = 0; i < PF_LIMIT_MAX; i++) {
if (((struct pool *)pf_pool_limits[i].pp)->pr_nout >
pf_pool_limits[i].limit_new) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EBUSY;
goto fail;
}
}
/* now do the commit - no errors should happen here */
for (i = 0; i < io->size; i++) {
PF_UNLOCK();
NET_UNLOCK();
if (copyin(io->array+i, ioe, sizeof(*ioe))) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EFAULT;
goto fail;
}
if (strnlen(ioe->anchor, sizeof(ioe->anchor)) ==
sizeof(ioe->anchor)) {
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = ENAMETOOLONG;
goto fail;
}
NET_LOCK();
PF_LOCK();
switch (ioe->type) {
case PF_TRANS_TABLE:
memset(table, 0, sizeof(*table));
strlcpy(table->pfrt_anchor, ioe->anchor,
sizeof(table->pfrt_anchor));
if ((error = pfr_ina_commit(table, ioe->ticket,
NULL, NULL, 0))) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
goto fail; /* really bad */
}
break;
case PF_TRANS_RULESET:
if ((error = pf_commit_rules(ioe->ticket,
ioe->anchor))) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
goto fail; /* really bad */
}
break;
default:
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EINVAL;
goto fail; /* really bad */
}
}
for (i = 0; i < PF_LIMIT_MAX; i++) {
if (pf_pool_limits[i].limit_new !=
pf_pool_limits[i].limit &&
pool_sethardlimit(pf_pool_limits[i].pp,
pf_pool_limits[i].limit_new, NULL, 0) != 0) {
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
error = EBUSY;
goto fail; /* really bad */
}
pf_pool_limits[i].limit = pf_pool_limits[i].limit_new;
}
for (i = 0; i < PFTM_MAX; i++) {
int old = pf_default_rule.timeout[i];
pf_default_rule.timeout[i] =
pf_default_rule_new.timeout[i];
if (pf_default_rule.timeout[i] == PFTM_INTERVAL &&
pf_default_rule.timeout[i] < old)
task_add(net_tq(0), &pf_purge_task);
}
pfi_xcommit();
pf_trans_set_commit();
PF_UNLOCK();
NET_UNLOCK();
free(table, M_TEMP, sizeof(*table));
free(ioe, M_TEMP, sizeof(*ioe));
break;
}
case DIOCGETSRCNODES: {
struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr;
struct pf_src_node *n, *p, *pstore;
u_int32_t nr = 0;
size_t space = psn->psn_len;
pstore = malloc(sizeof(*pstore), M_TEMP, M_WAITOK);
NET_LOCK();
PF_LOCK();
if (space == 0) {
RB_FOREACH(n, pf_src_tree, &tree_src_tracking)
nr++;
psn->psn_len = sizeof(struct pf_src_node) * nr;
PF_UNLOCK();
NET_UNLOCK();
free(pstore, M_TEMP, sizeof(*pstore));
goto fail;
}
p = psn->psn_src_nodes;
RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
int secs = getuptime(), diff;
if ((nr + 1) * sizeof(*p) > psn->psn_len)
break;
memcpy(pstore, n, sizeof(*pstore));
memset(&pstore->entry, 0, sizeof(pstore->entry));
pstore->rule.ptr = NULL;
pstore->kif = NULL;
pstore->rule.nr = n->rule.ptr->nr;
pstore->creation = secs - pstore->creation;
if (pstore->expire > secs)
pstore->expire -= secs;
else
pstore->expire = 0;
/* adjust the connection rate estimate */
diff = secs - n->conn_rate.last;
if (diff >= n->conn_rate.seconds)
pstore->conn_rate.count = 0;
else
pstore->conn_rate.count -=
n->conn_rate.count * diff /
n->conn_rate.seconds;
error = copyout(pstore, p, sizeof(*p));
if (error) {
PF_UNLOCK();
NET_UNLOCK();
free(pstore, M_TEMP, sizeof(*pstore));
goto fail;
}
p++;
nr++;
}
psn->psn_len = sizeof(struct pf_src_node) * nr;
PF_UNLOCK();
NET_UNLOCK();
free(pstore, M_TEMP, sizeof(*pstore));
break;
}
case DIOCCLRSRCNODES: {
struct pf_src_node *n;
struct pf_state *state;
NET_LOCK();
PF_LOCK();
PF_STATE_ENTER_WRITE();
RB_FOREACH(state, pf_state_tree_id, &tree_id)
pf_src_tree_remove_state(state);
PF_STATE_EXIT_WRITE();
RB_FOREACH(n, pf_src_tree, &tree_src_tracking)
n->expire = 1;
pf_purge_expired_src_nodes();
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCKILLSRCNODES: {
struct pf_src_node *sn;
struct pf_state *s;
struct pfioc_src_node_kill *psnk =
(struct pfioc_src_node_kill *)addr;
u_int killed = 0;
NET_LOCK();
PF_LOCK();
RB_FOREACH(sn, pf_src_tree, &tree_src_tracking) {
if (pf_match_addr(psnk->psnk_src.neg,
&psnk->psnk_src.addr.v.a.addr,
&psnk->psnk_src.addr.v.a.mask,
&sn->addr, sn->af) &&
pf_match_addr(psnk->psnk_dst.neg,
&psnk->psnk_dst.addr.v.a.addr,
&psnk->psnk_dst.addr.v.a.mask,
&sn->raddr, sn->af)) {
/* Handle state to src_node linkage */
if (sn->states != 0) {
PF_ASSERT_LOCKED();
PF_STATE_ENTER_WRITE();
RB_FOREACH(s, pf_state_tree_id,
&tree_id)
pf_state_rm_src_node(s, sn);
PF_STATE_EXIT_WRITE();
}
sn->expire = 1;
killed++;
}
}
if (killed > 0)
pf_purge_expired_src_nodes();
psnk->psnk_killed = killed;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETHOSTID: {
u_int32_t *hostid = (u_int32_t *)addr;
NET_LOCK();
PF_LOCK();
if (*hostid == 0)
pf_trans_set.hostid = arc4random();
else
pf_trans_set.hostid = *hostid;
pf_trans_set.mask |= PF_TSET_HOSTID;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCOSFPFLUSH:
pf_osfp_flush();
break;
case DIOCIGETIFACES: {
struct pfioc_iface *io = (struct pfioc_iface *)addr;
struct pfi_kif *kif_buf;
int apfiio_size = io->pfiio_size;
if (io->pfiio_esize != sizeof(struct pfi_kif)) {
error = ENODEV;
goto fail;
}
if ((kif_buf = mallocarray(sizeof(*kif_buf), apfiio_size,
M_TEMP, M_WAITOK|M_CANFAIL)) == NULL) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
pfi_get_ifaces(io->pfiio_name, kif_buf, &io->pfiio_size);
PF_UNLOCK();
NET_UNLOCK();
if (copyout(kif_buf, io->pfiio_buffer, sizeof(*kif_buf) *
io->pfiio_size))
error = EFAULT;
free(kif_buf, M_TEMP, sizeof(*kif_buf) * apfiio_size);
break;
}
case DIOCSETIFFLAG: {
struct pfioc_iface *io = (struct pfioc_iface *)addr;
if (io == NULL) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfi_set_flags(io->pfiio_name, io->pfiio_flags);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCCLRIFFLAG: {
struct pfioc_iface *io = (struct pfioc_iface *)addr;
if (io == NULL) {
error = EINVAL;
goto fail;
}
NET_LOCK();
PF_LOCK();
error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETREASS: {
u_int32_t *reass = (u_int32_t *)addr;
NET_LOCK();
PF_LOCK();
pf_trans_set.reass = *reass;
pf_trans_set.mask |= PF_TSET_REASS;
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETSYNFLWATS: {
struct pfioc_synflwats *io = (struct pfioc_synflwats *)addr;
NET_LOCK();
PF_LOCK();
error = pf_syncookies_setwats(io->hiwat, io->lowat);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCGETSYNFLWATS: {
struct pfioc_synflwats *io = (struct pfioc_synflwats *)addr;
NET_LOCK();
PF_LOCK();
error = pf_syncookies_getwats(io);
PF_UNLOCK();
NET_UNLOCK();
break;
}
case DIOCSETSYNCOOKIES: {
u_int8_t *mode = (u_int8_t *)addr;
NET_LOCK();
PF_LOCK();
error = pf_syncookies_setmode(*mode);
PF_UNLOCK();
NET_UNLOCK();
break;
}
default:
error = ENODEV;
break;
}
fail:
if (flags & FWRITE)
rw_exit_write(&pfioctl_rw);
else
rw_exit_read(&pfioctl_rw);
return (error);
}
void
pf_trans_set_commit(void)
{
if (pf_trans_set.mask & PF_TSET_STATUSIF)
strlcpy(pf_status.ifname, pf_trans_set.statusif, IFNAMSIZ);
if (pf_trans_set.mask & PF_TSET_DEBUG)
pf_status.debug = pf_trans_set.debug;
if (pf_trans_set.mask & PF_TSET_HOSTID)
pf_status.hostid = pf_trans_set.hostid;
if (pf_trans_set.mask & PF_TSET_REASS)
pf_status.reass = pf_trans_set.reass;
}
void
pf_pool_copyin(struct pf_pool *from, struct pf_pool *to)
{
memmove(to, from, sizeof(*to));
to->kif = NULL;
to->addr.p.tbl = NULL;
}
int
pf_validate_range(u_int8_t op, u_int16_t port[2], int order)
{
u_int16_t a = (order == PF_ORDER_NET) ? ntohs(port[0]) : port[0];
u_int16_t b = (order == PF_ORDER_NET) ? ntohs(port[1]) : port[1];
if ((op == PF_OP_RRG && a > b) || /* 34:12, i.e. none */
(op == PF_OP_IRG && a >= b) || /* 34><12, i.e. none */
(op == PF_OP_XRG && a > b)) /* 34<>22, i.e. all */
return 1;
return 0;
}
int
pf_rule_copyin(struct pf_rule *from, struct pf_rule *to)
{
int i;
if (from->scrub_flags & PFSTATE_SETPRIO &&
(from->set_prio[0] > IFQ_MAXPRIO ||
from->set_prio[1] > IFQ_MAXPRIO))
return (EINVAL);
to->src = from->src;
to->src.addr.p.tbl = NULL;
to->dst = from->dst;
to->dst.addr.p.tbl = NULL;
if (pf_validate_range(to->src.port_op, to->src.port, PF_ORDER_NET))
return (EINVAL);
if (pf_validate_range(to->dst.port_op, to->dst.port, PF_ORDER_NET))
return (EINVAL);
/* XXX union skip[] */
strlcpy(to->label, from->label, sizeof(to->label));
strlcpy(to->ifname, from->ifname, sizeof(to->ifname));
strlcpy(to->rcv_ifname, from->rcv_ifname, sizeof(to->rcv_ifname));
strlcpy(to->qname, from->qname, sizeof(to->qname));
strlcpy(to->pqname, from->pqname, sizeof(to->pqname));
strlcpy(to->tagname, from->tagname, sizeof(to->tagname));
strlcpy(to->match_tagname, from->match_tagname,
sizeof(to->match_tagname));
strlcpy(to->overload_tblname, from->overload_tblname,
sizeof(to->overload_tblname));
pf_pool_copyin(&from->nat, &to->nat);
pf_pool_copyin(&from->rdr, &to->rdr);
pf_pool_copyin(&from->route, &to->route);
if (pf_validate_range(to->rdr.port_op, to->rdr.proxy_port,
PF_ORDER_HOST))
return (EINVAL);
to->kif = (to->ifname[0]) ?
pfi_kif_alloc(to->ifname, M_WAITOK) : NULL;
to->rcv_kif = (to->rcv_ifname[0]) ?
pfi_kif_alloc(to->rcv_ifname, M_WAITOK) : NULL;
to->rdr.kif = (to->rdr.ifname[0]) ?
pfi_kif_alloc(to->rdr.ifname, M_WAITOK) : NULL;
to->nat.kif = (to->nat.ifname[0]) ?
pfi_kif_alloc(to->nat.ifname, M_WAITOK) : NULL;
to->route.kif = (to->route.ifname[0]) ?
pfi_kif_alloc(to->route.ifname, M_WAITOK) : NULL;
to->os_fingerprint = from->os_fingerprint;
to->rtableid = from->rtableid;
if (to->rtableid >= 0 && !rtable_exists(to->rtableid))
return (EBUSY);
to->onrdomain = from->onrdomain;
if (to->onrdomain != -1 && (to->onrdomain < 0 ||
to->onrdomain > RT_TABLEID_MAX))
return (EINVAL);
for (i = 0; i < PFTM_MAX; i++)
to->timeout[i] = from->timeout[i];
to->states_tot = from->states_tot;
to->max_states = from->max_states;
to->max_src_nodes = from->max_src_nodes;
to->max_src_states = from->max_src_states;
to->max_src_conn = from->max_src_conn;
to->max_src_conn_rate.limit = from->max_src_conn_rate.limit;
to->max_src_conn_rate.seconds = from->max_src_conn_rate.seconds;
pf_init_threshold(&to->pktrate, from->pktrate.limit,
from->pktrate.seconds);
if (to->qname[0] != 0) {
if ((to->qid = pf_qname2qid(to->qname, 0)) == 0)
return (EBUSY);
if (to->pqname[0] != 0) {
if ((to->pqid = pf_qname2qid(to->pqname, 0)) == 0)
return (EBUSY);
} else
to->pqid = to->qid;
}
to->rt_listid = from->rt_listid;
to->prob = from->prob;
to->return_icmp = from->return_icmp;
to->return_icmp6 = from->return_icmp6;
to->max_mss = from->max_mss;
if (to->tagname[0])
if ((to->tag = pf_tagname2tag(to->tagname, 1)) == 0)
return (EBUSY);
if (to->match_tagname[0])
if ((to->match_tag = pf_tagname2tag(to->match_tagname, 1)) == 0)
return (EBUSY);
to->scrub_flags = from->scrub_flags;
to->delay = from->delay;
to->uid = from->uid;
to->gid = from->gid;
to->rule_flag = from->rule_flag;
to->action = from->action;
to->direction = from->direction;
to->log = from->log;
to->logif = from->logif;
#if NPFLOG > 0
if (!to->log)
to->logif = 0;
#endif /* NPFLOG > 0 */
to->quick = from->quick;
to->ifnot = from->ifnot;
to->rcvifnot = from->rcvifnot;
to->match_tag_not = from->match_tag_not;
to->keep_state = from->keep_state;
to->af = from->af;
to->naf = from->naf;
to->proto = from->proto;
to->type = from->type;
to->code = from->code;
to->flags = from->flags;
to->flagset = from->flagset;
to->min_ttl = from->min_ttl;
to->allow_opts = from->allow_opts;
to->rt = from->rt;
to->return_ttl = from->return_ttl;
to->tos = from->tos;
to->set_tos = from->set_tos;
to->anchor_relative = from->anchor_relative; /* XXX */
to->anchor_wildcard = from->anchor_wildcard; /* XXX */
to->flush = from->flush;
to->divert.addr = from->divert.addr;
to->divert.port = from->divert.port;
to->divert.type = from->divert.type;
to->prio = from->prio;
to->set_prio[0] = from->set_prio[0];
to->set_prio[1] = from->set_prio[1];
return (0);
}
int
pf_rule_checkaf(struct pf_rule *r)
{
switch (r->af) {
case 0:
if (r->rule_flag & PFRULE_AFTO)
return (EPFNOSUPPORT);
break;
case AF_INET:
if ((r->rule_flag & PFRULE_AFTO) && r->naf != AF_INET6)
return (EPFNOSUPPORT);
break;
#ifdef INET6
case AF_INET6:
if ((r->rule_flag & PFRULE_AFTO) && r->naf != AF_INET)
return (EPFNOSUPPORT);
break;
#endif /* INET6 */
default:
return (EPFNOSUPPORT);
}
if ((r->rule_flag & PFRULE_AFTO) == 0 && r->naf != 0)
return (EPFNOSUPPORT);
return (0);
}
int
pf_sysctl(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
struct pf_status pfs;
NET_LOCK_SHARED();
PF_LOCK();
memcpy(&pfs, &pf_status, sizeof(struct pf_status));
pfi_update_status(pfs.ifname, &pfs);
PF_UNLOCK();
NET_UNLOCK_SHARED();
return sysctl_rdstruct(oldp, oldlenp, newp, &pfs, sizeof(pfs));
}
1
125
2
2
250
14
50
12
16
17
3
4
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/* $OpenBSD: protosw.h,v 1.55 2022/09/05 14:56:09 bluhm Exp $ */
/* $NetBSD: protosw.h,v 1.10 1996/04/09 20:55:32 cgd Exp $ */
/*-
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)protosw.h 8.1 (Berkeley) 6/2/93
*/
/*
* Protocol switch table.
*
* Each protocol has a handle initializing one of these structures,
* which is used for protocol-protocol and system-protocol communication.
*
* A protocol is called through the pr_init entry before any other.
* Thereafter it is called every 200ms through the pr_fasttimo entry and
* every 500ms through the pr_slowtimo for timer based actions.
*
* Protocols pass data between themselves as chains of mbufs using
* the pr_input and pr_send hooks. Pr_input passes data up (towards
* UNIX) and pr_send passes it down (towards the imps); control
* information passes up and down on pr_ctlinput and pr_ctloutput.
* The protocol is responsible for the space occupied by any the
* arguments to these entries and must dispose it.
*
* The userreq routine interfaces protocols to the system and is
* described below.
*/
struct mbuf;
struct sockaddr;
struct socket;
struct domain;
struct proc;
struct stat;
struct ifnet;
struct pr_usrreqs {
int (*pru_attach)(struct socket *, int);
int (*pru_detach)(struct socket *);
void (*pru_lock)(struct socket *);
void (*pru_unlock)(struct socket *);
int (*pru_bind)(struct socket *, struct mbuf *, struct proc *);
int (*pru_listen)(struct socket *);
int (*pru_connect)(struct socket *, struct mbuf *);
int (*pru_accept)(struct socket *, struct mbuf *);
int (*pru_disconnect)(struct socket *);
int (*pru_shutdown)(struct socket *);
int (*pru_rcvd)(struct socket *);
int (*pru_send)(struct socket *, struct mbuf *, struct mbuf *,
struct mbuf *);
int (*pru_abort)(struct socket *);
int (*pru_control)(struct socket *, u_long, caddr_t,
struct ifnet *);
int (*pru_sense)(struct socket *, struct stat *);
int (*pru_rcvoob)(struct socket *, struct mbuf *, int);
int (*pru_sendoob)(struct socket *, struct mbuf *, struct mbuf *,
struct mbuf *);
int (*pru_sockaddr)(struct socket *, struct mbuf *);
int (*pru_peeraddr)(struct socket *, struct mbuf *);
int (*pru_connect2)(struct socket *, struct socket *);
};
struct protosw {
short pr_type; /* socket type used for */
const struct domain *pr_domain; /* domain protocol a member of */
short pr_protocol; /* protocol number */
short pr_flags; /* see below */
/* protocol-protocol hooks */
/* input to protocol (from below) */
int (*pr_input)(struct mbuf **, int *, int, int);
/* control input (from below) */
void (*pr_ctlinput)(int, struct sockaddr *, u_int, void *);
/* control output (from above) */
int (*pr_ctloutput)(int, struct socket *, int, int, struct mbuf *);
/* user-protocol hooks */
const struct pr_usrreqs *pr_usrreqs;
/* utility hooks */
void (*pr_init)(void); /* initialization hook */
void (*pr_fasttimo)(void); /* fast timeout (200ms) */
void (*pr_slowtimo)(void); /* slow timeout (500ms) */
/* sysctl for protocol */
int (*pr_sysctl)(int *, u_int, void *, size_t *, void *, size_t);
};
#define PR_SLOWHZ 2 /* 2 slow timeouts per second */
#define PR_FASTHZ 5 /* 5 fast timeouts per second */
/*
* Values for pr_flags.
* PR_ADDR requires PR_ATOMIC;
* PR_ADDR and PR_CONNREQUIRED are mutually exclusive.
*/
#define PR_ATOMIC 0x01 /* exchange atomic messages only */
#define PR_ADDR 0x02 /* addresses given with messages */
#define PR_CONNREQUIRED 0x04 /* connection required by protocol */
#define PR_WANTRCVD 0x08 /* want PRU_RCVD calls */
#define PR_RIGHTS 0x10 /* passes capabilities */
#define PR_ABRTACPTDIS 0x20 /* abort on accept(2) to disconnected
socket */
#define PR_SPLICE 0x40 /* socket splicing is possible */
/*
* The arguments to usrreq are:
* (*protosw[].pr_usrreq)(up, req, m, nam, opt);
* where up is a (struct socket *), req is one of these requests,
* m is a optional mbuf chain containing a message,
* nam is an optional mbuf chain containing an address,
* and opt is a pointer to a socketopt structure or nil.
* The protocol is responsible for disposal of the mbuf chain m,
* the caller is responsible for any space held by nam and opt.
* A non-zero return from usrreq gives an
* UNIX error number which should be passed to higher level software.
*/
#define PRU_ATTACH 0 /* attach protocol to up */
#define PRU_DETACH 1 /* detach protocol from up */
#define PRU_BIND 2 /* bind socket to address */
#define PRU_LISTEN 3 /* listen for connection */
#define PRU_CONNECT 4 /* establish connection to peer */
#define PRU_ACCEPT 5 /* accept connection from peer */
#define PRU_DISCONNECT 6 /* disconnect from peer */
#define PRU_SHUTDOWN 7 /* won't send any more data */
#define PRU_RCVD 8 /* have taken data; more room now */
#define PRU_SEND 9 /* send this data */
#define PRU_ABORT 10 /* abort (fast DISCONNECT, DETACH) */
#define PRU_CONTROL 11 /* control operations on protocol */
#define PRU_SENSE 12 /* return status into m */
#define PRU_RCVOOB 13 /* retrieve out of band data */
#define PRU_SENDOOB 14 /* send out of band data */
#define PRU_SOCKADDR 15 /* fetch socket's address */
#define PRU_PEERADDR 16 /* fetch peer's address */
#define PRU_CONNECT2 17 /* connect two sockets */
/* begin for protocols internal use */
#define PRU_FASTTIMO 18 /* 200ms timeout */
#define PRU_SLOWTIMO 19 /* 500ms timeout */
#define PRU_PROTORCV 20 /* receive from below */
#define PRU_PROTOSEND 21 /* send to below */
#define PRU_NREQ 22
#ifdef PRUREQUESTS
const char *prurequests[] = {
"ATTACH", "DETACH", "BIND", "LISTEN",
"CONNECT", "ACCEPT", "DISCONNECT", "SHUTDOWN",
"RCVD", "SEND", "ABORT", "CONTROL",
"SENSE", "RCVOOB", "SENDOOB", "SOCKADDR",
"PEERADDR", "CONNECT2", "FASTTIMO", "SLOWTIMO",
"PROTORCV", "PROTOSEND",
};
#endif
/*
* The arguments to the ctlinput routine are
* (*protosw[].pr_ctlinput)(cmd, sa, arg);
* where cmd is one of the commands below, sa is a pointer to a sockaddr,
* and arg is an optional caddr_t argument used within a protocol family.
*/
#define PRC_IFDOWN 0 /* interface transition */
#define PRC_ROUTEDEAD 1 /* select new route if possible ??? */
#define PRC_MTUINC 2 /* increase in mtu to host */
#define PRC_QUENCH2 3 /* DEC congestion bit says slow down */
#define PRC_QUENCH 4 /* some one said to slow down */
#define PRC_MSGSIZE 5 /* message size forced drop */
#define PRC_HOSTDEAD 6 /* host appears to be down */
#define PRC_HOSTUNREACH 7 /* deprecated (use PRC_UNREACH_HOST) */
#define PRC_UNREACH_NET 8 /* no route to network */
#define PRC_UNREACH_HOST 9 /* no route to host */
#define PRC_UNREACH_PROTOCOL 10 /* dst says bad protocol */
#define PRC_UNREACH_PORT 11 /* bad port # */
/* was PRC_UNREACH_NEEDFRAG 12 (use PRC_MSGSIZE) */
#define PRC_UNREACH_SRCFAIL 13 /* source route failed */
#define PRC_REDIRECT_NET 14 /* net routing redirect */
#define PRC_REDIRECT_HOST 15 /* host routing redirect */
#define PRC_REDIRECT_TOSNET 16 /* redirect for type of service & net */
#define PRC_REDIRECT_TOSHOST 17 /* redirect for tos & host */
#define PRC_TIMXCEED_INTRANS 18 /* packet lifetime expired in transit */
#define PRC_TIMXCEED_REASS 19 /* lifetime expired on reass q */
#define PRC_PARAMPROB 20 /* header incorrect */
#define PRC_NCMDS 21
#define PRC_IS_REDIRECT(cmd) \
((cmd) >= PRC_REDIRECT_NET && (cmd) <= PRC_REDIRECT_TOSHOST)
#ifdef PRCREQUESTS
char *prcrequests[] = {
"IFDOWN", "ROUTEDEAD", "MTUINC", "DEC-BIT-QUENCH2",
"QUENCH", "MSGSIZE", "HOSTDEAD", "#7",
"NET-UNREACH", "HOST-UNREACH", "PROTO-UNREACH", "PORT-UNREACH",
"#12", "SRCFAIL-UNREACH", "NET-REDIRECT", "HOST-REDIRECT",
"TOSNET-REDIRECT", "TOSHOST-REDIRECT", "TX-INTRANS", "TX-REASS",
"PARAMPROB"
};
#endif
/*
* The arguments to ctloutput are:
* (*protosw[].pr_ctloutput)(req, so, level, optname, optval);
* req is one of the actions listed below, so is a (struct socket *),
* level is an indication of which protocol layer the option is intended.
* optname is a protocol dependent socket option request,
* optval is a pointer to a mbuf-chain pointer, for value-return results.
* The protocol is responsible for disposal of the mbuf chain *optval
* if supplied,
* the caller is responsible for any space held by *optval, when returned.
* A non-zero return from usrreq gives an
* UNIX error number which should be passed to higher level software.
*/
#define PRCO_GETOPT 0
#define PRCO_SETOPT 1
#define PRCO_NCMDS 2
#ifdef PRCOREQUESTS
char *prcorequests[] = {
"GETOPT", "SETOPT",
};
#endif
#ifdef _KERNEL
#include <sys/mbuf.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
struct ifnet;
struct sockaddr;
const struct protosw *pffindproto(int, int, int);
const struct protosw *pffindtype(int, int);
void pfctlinput(int, struct sockaddr *);
extern u_char ip_protox[];
extern const struct protosw inetsw[];
#ifdef INET6
extern u_char ip6_protox[];
extern const struct protosw inet6sw[];
#endif /* INET6 */
static inline int
pru_attach(struct socket *so, int proto)
{
return (*so->so_proto->pr_usrreqs->pru_attach)(so, proto);
}
static inline int
pru_detach(struct socket *so)
{
return (*so->so_proto->pr_usrreqs->pru_detach)(so);
}
static inline void
pru_lock(struct socket *so)
{
(*so->so_proto->pr_usrreqs->pru_lock)(so);
}
static inline void
pru_unlock(struct socket *so)
{
(*so->so_proto->pr_usrreqs->pru_unlock)(so);
}
static inline int
pru_bind(struct socket *so, struct mbuf *nam, struct proc *p)
{
if (so->so_proto->pr_usrreqs->pru_bind)
return (*so->so_proto->pr_usrreqs->pru_bind)(so, nam, p);
return (EOPNOTSUPP);
}
static inline int
pru_listen(struct socket *so)
{
if (so->so_proto->pr_usrreqs->pru_listen)
return (*so->so_proto->pr_usrreqs->pru_listen)(so);
return (EOPNOTSUPP);
}
static inline int
pru_connect(struct socket *so, struct mbuf *nam)
{
if (so->so_proto->pr_usrreqs->pru_connect)
return (*so->so_proto->pr_usrreqs->pru_connect)(so, nam);
return (EOPNOTSUPP);
}
static inline int
pru_accept(struct socket *so, struct mbuf *nam)
{
if (so->so_proto->pr_usrreqs->pru_accept)
return (*so->so_proto->pr_usrreqs->pru_accept)(so, nam);
return (EOPNOTSUPP);
}
static inline int
pru_disconnect(struct socket *so)
{
if (so->so_proto->pr_usrreqs->pru_disconnect)
return (*so->so_proto->pr_usrreqs->pru_disconnect)(so);
return (EOPNOTSUPP);
}
static inline int
pru_shutdown(struct socket *so)
{
return (*so->so_proto->pr_usrreqs->pru_shutdown)(so);
}
static inline int
pru_rcvd(struct socket *so)
{
if (so->so_proto->pr_usrreqs->pru_rcvd)
return (*so->so_proto->pr_usrreqs->pru_rcvd)(so);
return (EOPNOTSUPP);
}
static inline int
pru_send(struct socket *so, struct mbuf *top, struct mbuf *addr,
struct mbuf *control)
{
return (*so->so_proto->pr_usrreqs->pru_send)(so, top, addr, control);
}
static inline int
pru_abort(struct socket *so)
{
return (*so->so_proto->pr_usrreqs->pru_abort)(so);
}
static inline int
pru_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp)
{
if (so->so_proto->pr_usrreqs->pru_control)
return (*so->so_proto->pr_usrreqs->pru_control)(so,
cmd, data, ifp);
return (EOPNOTSUPP);
}
static inline int
pru_sense(struct socket *so, struct stat *ub)
{
if (so->so_proto->pr_usrreqs->pru_sense)
return (*so->so_proto->pr_usrreqs->pru_sense)(so, ub);
return (0);
}
static inline int
pru_rcvoob(struct socket *so, struct mbuf *m, int flags)
{
if (so->so_proto->pr_usrreqs->pru_rcvoob)
return (*so->so_proto->pr_usrreqs->pru_rcvoob)(so, m, flags);
return (EOPNOTSUPP);
}
static inline int
pru_sendoob(struct socket *so, struct mbuf *top, struct mbuf *addr,
struct mbuf *control)
{
if (so->so_proto->pr_usrreqs->pru_sendoob)
return (*so->so_proto->pr_usrreqs->pru_sendoob)(so,
top, addr, control);
m_freem(top);
m_freem(control);
return (EOPNOTSUPP);
}
static inline int
pru_sockaddr(struct socket *so, struct mbuf *addr)
{
return (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, addr);
}
static inline int
pru_peeraddr(struct socket *so, struct mbuf *addr)
{
return (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, addr);
}
static inline int
pru_connect2(struct socket *so1, struct socket *so2)
{
if (so1->so_proto->pr_usrreqs->pru_connect2)
return (*so1->so_proto->pr_usrreqs->pru_connect2)(so1, so2);
return (EOPNOTSUPP);
}
#endif
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/* $OpenBSD: in6_var.h,v 1.74 2022/01/02 22:36:04 jsg Exp $ */
/* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1985, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_var.h 8.1 (Berkeley) 6/10/93
*/
#ifndef _NETINET6_IN6_VAR_H_
#define _NETINET6_IN6_VAR_H_
/*
* Interface address, Internet version. One of these structures
* is allocated for each interface with an Internet address.
* The ifaddr structure contains the protocol-independent part
* of the structure and is assumed to be first.
*/
/*
* pltime/vltime are just for future reference (required to implements 2
* hour rule for hosts). they should never be modified by nd6_timeout or
* anywhere else.
* userland -> kernel: accept pltime/vltime
* kernel -> userland: throw up everything
* in kernel: modify preferred/expire only
*/
struct in6_addrlifetime {
time_t ia6t_expire; /* valid lifetime expiration time */
time_t ia6t_preferred; /* preferred lifetime expiration time */
u_int32_t ia6t_vltime; /* valid lifetime */
u_int32_t ia6t_pltime; /* prefix lifetime */
};
#ifdef _KERNEL
struct nd_ifinfo;
struct in6_ifextra {
struct nd_ifinfo *nd_ifinfo;
void *rs_lhcookie;
int nprefixes;
int ndefrouters;
};
struct in6_ifaddr {
struct ifaddr ia_ifa; /* protocol-independent info */
#define ia_ifp ia_ifa.ifa_ifp
#define ia_flags ia_ifa.ifa_flags
struct sockaddr_in6 ia_addr; /* interface address */
struct sockaddr_in6 ia_dstaddr; /* space for destination addr */
struct sockaddr_in6 ia_prefixmask; /* prefix mask */
TAILQ_ENTRY(in6_ifaddr) ia_list; /* list of IP6 addresses */
int ia6_flags;
struct in6_addrlifetime ia6_lifetime;
time_t ia6_updatetime;
/* multicast addresses joined from the kernel */
LIST_HEAD(, in6_multi_mship) ia6_memberships;
};
#endif /* _KERNEL */
/*
* IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12).
*/
struct in6_ifstat {
u_int64_t ifs6_in_receive; /* # of total input datagram */
u_int64_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */
u_int64_t ifs6_in_toobig; /* # of datagrams exceeded MTU */
u_int64_t ifs6_in_noroute; /* # of datagrams with no route */
u_int64_t ifs6_in_addrerr; /* # of datagrams with invalid dst */
u_int64_t ifs6_in_protounknown; /* # of datagrams with unknown proto */
/* NOTE: increment on final dst if */
u_int64_t ifs6_in_truncated; /* # of truncated datagrams */
u_int64_t ifs6_in_discard; /* # of discarded datagrams */
/* NOTE: fragment timeout is not here */
u_int64_t ifs6_in_deliver; /* # of datagrams delivered to ULP */
/* NOTE: increment on final dst if */
u_int64_t ifs6_out_forward; /* # of datagrams forwarded */
/* NOTE: increment on outgoing if */
u_int64_t ifs6_out_request; /* # of outgoing datagrams from ULP */
/* NOTE: does not include forwards */
u_int64_t ifs6_out_discard; /* # of discarded datagrams */
u_int64_t ifs6_out_fragok; /* # of datagrams fragmented */
u_int64_t ifs6_out_fragfail; /* # of datagrams failed on fragment */
u_int64_t ifs6_out_fragcreat; /* # of fragment datagrams */
/* NOTE: this is # after fragment */
u_int64_t ifs6_reass_reqd; /* # of incoming fragmented packets */
/* NOTE: increment on final dst if */
u_int64_t ifs6_reass_ok; /* # of reassembled packets */
/* NOTE: this is # after reass */
/* NOTE: increment on final dst if */
u_int64_t ifs6_reass_fail; /* # of reass failures */
/* NOTE: may not be packet count */
/* NOTE: increment on final dst if */
u_int64_t ifs6_in_mcast; /* # of inbound multicast datagrams */
u_int64_t ifs6_out_mcast; /* # of outbound multicast datagrams */
};
/*
* ICMPv6 interface statistics, as defined in RFC2466 Ipv6IfIcmpEntry.
* XXX: I'm not sure if this file is the right place for this structure...
*/
struct icmp6_ifstat {
/*
* Input statistics
*/
/* ipv6IfIcmpInMsgs, total # of input messages */
u_int64_t ifs6_in_msg;
/* ipv6IfIcmpInErrors, # of input error messages */
u_int64_t ifs6_in_error;
/* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */
u_int64_t ifs6_in_dstunreach;
/* ipv6IfIcmpInAdminProhibs, # of input administratively prohibited errs */
u_int64_t ifs6_in_adminprohib;
/* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */
u_int64_t ifs6_in_timeexceed;
/* ipv6IfIcmpInParmProblems, # of input parameter problem errors */
u_int64_t ifs6_in_paramprob;
/* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */
u_int64_t ifs6_in_pkttoobig;
/* ipv6IfIcmpInEchos, # of input echo requests */
u_int64_t ifs6_in_echo;
/* ipv6IfIcmpInEchoReplies, # of input echo replies */
u_int64_t ifs6_in_echoreply;
/* ipv6IfIcmpInRouterSolicits, # of input router solicitations */
u_int64_t ifs6_in_routersolicit;
/* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */
u_int64_t ifs6_in_routeradvert;
/* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */
u_int64_t ifs6_in_neighborsolicit;
/* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advertisements */
u_int64_t ifs6_in_neighboradvert;
/* ipv6IfIcmpInRedirects, # of input redirects */
u_int64_t ifs6_in_redirect;
/* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */
u_int64_t ifs6_in_mldquery;
/* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */
u_int64_t ifs6_in_mldreport;
/* ipv6IfIcmpInGroupMembReductions, # of input MLD done */
u_int64_t ifs6_in_mlddone;
/*
* Output statistics. We should solve unresolved routing problem...
*/
/* ipv6IfIcmpOutMsgs, total # of output messages */
u_int64_t ifs6_out_msg;
/* ipv6IfIcmpOutErrors, # of output error messages */
u_int64_t ifs6_out_error;
/* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */
u_int64_t ifs6_out_dstunreach;
/* ipv6IfIcmpOutAdminProhibs, # of output administratively prohibited errs */
u_int64_t ifs6_out_adminprohib;
/* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */
u_int64_t ifs6_out_timeexceed;
/* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */
u_int64_t ifs6_out_paramprob;
/* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */
u_int64_t ifs6_out_pkttoobig;
/* ipv6IfIcmpOutEchos, # of output echo requests */
u_int64_t ifs6_out_echo;
/* ipv6IfIcmpOutEchoReplies, # of output echo replies */
u_int64_t ifs6_out_echoreply;
/* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */
u_int64_t ifs6_out_routersolicit;
/* ipv6IfIcmpOutRouterAdvertisements, # of output router advertisements */
u_int64_t ifs6_out_routeradvert;
/* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */
u_int64_t ifs6_out_neighborsolicit;
/* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advertisements */
u_int64_t ifs6_out_neighboradvert;
/* ipv6IfIcmpOutRedirects, # of output redirects */
u_int64_t ifs6_out_redirect;
/* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */
u_int64_t ifs6_out_mldquery;
/* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */
u_int64_t ifs6_out_mldreport;
/* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */
u_int64_t ifs6_out_mlddone;
};
struct in6_ifreq {
char ifr_name[IFNAMSIZ];
union {
struct sockaddr_in6 ifru_addr;
struct sockaddr_in6 ifru_dstaddr;
short ifru_flags;
int ifru_flags6;
int ifru_metric;
caddr_t ifru_data;
struct in6_addrlifetime ifru_lifetime;
struct in6_ifstat ifru_stat;
struct icmp6_ifstat ifru_icmp6stat;
} ifr_ifru;
};
struct in6_aliasreq {
char ifra_name[IFNAMSIZ];
union {
struct sockaddr_in6 ifrau_addr;
int ifrau_align;
} ifra_ifrau;
#ifndef ifra_addr
#define ifra_addr ifra_ifrau.ifrau_addr
#endif
struct sockaddr_in6 ifra_dstaddr;
struct sockaddr_in6 ifra_prefixmask;
int ifra_flags;
struct in6_addrlifetime ifra_lifetime;
};
/*
* Given a pointer to an in6_ifaddr (ifaddr),
* return a pointer to the addr as a sockaddr_in6
*/
#define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr))
#define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr))
#define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr))
#define IA6_SIN6(ia) (&((ia)->ia_addr))
#define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr))
#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr)
#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr)
#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq)
#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq)
#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq)
#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq)
#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
#define SIOCGIFINFO_IN6 _IOWR('i', 108, struct in6_ndireq)
#define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo)
#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq)
#define SIOCGETSGCNT_IN6 _IOWR('u', 106, struct sioc_sg_req6)
#define SIOCGETMIFCNT_IN6 _IOWR('u', 107, struct sioc_mif_req6)
#define IN6_IFF_ANYCAST 0x01 /* anycast address */
#define IN6_IFF_TENTATIVE 0x02 /* tentative address */
#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */
#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */
#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */
#define IN6_IFF_AUTOCONF 0x40 /* autoconfigurable address. */
#define IN6_IFF_TEMPORARY 0x80 /* RFC 4941 temporary address */
#ifdef _KERNEL
#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
(((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
(((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \
(((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \
(((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
#define IN6_ARE_SCOPE_CMP(a,b) ((a)-(b))
#define IN6_ARE_SCOPE_EQUAL(a,b) ((a)==(b))
/*
* Multi-cast membership entry. One for each group/ifp that a PCB
* belongs to.
*/
struct in6_multi_mship {
struct in6_multi *i6mm_maddr; /* Multicast address pointer */
LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */
};
struct in6_multi {
struct ifmaddr in6m_ifma; /* Protocol-independent info */
#define in6m_refcnt in6m_ifma.ifma_refcnt
#define in6m_ifidx in6m_ifma.ifma_ifidx
struct sockaddr_in6 in6m_sin; /* IPv6 multicast address */
#define in6m_addr in6m_sin.sin6_addr
u_int in6m_state; /* state of membership */
u_int in6m_timer; /* MLD6 membership report timer */
};
static __inline struct in6_multi *
ifmatoin6m(struct ifmaddr *ifma)
{
return ((struct in6_multi *)(ifma));
}
/*
* Macros for looking up the in6_multi record for a given IP6 multicast
* address on a given interface. If no matching record is found, "in6m"
* returns NULL.
*/
#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
/* struct in6_addr addr; */ \
/* struct ifnet *ifp; */ \
/* struct in6_multi *in6m; */ \
do { \
struct ifmaddr *ifma; \
\
(in6m) = NULL; \
TAILQ_FOREACH(ifma, &(ifp)->if_maddrlist, ifma_list) \
if (ifma->ifma_addr->sa_family == AF_INET6 && \
IN6_ARE_ADDR_EQUAL(&ifmatoin6m(ifma)->in6m_addr, \
&(addr))) { \
(in6m) = ifmatoin6m(ifma); \
break; \
} \
} while (/* CONSTCOND */ 0)
struct in6_multi *in6_addmulti(struct in6_addr *, struct ifnet *, int *);
void in6_delmulti(struct in6_multi *);
int in6_hasmulti(struct in6_addr *, struct ifnet *);
struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *);
void in6_leavegroup(struct in6_multi_mship *);
int in6_control(struct socket *, u_long, caddr_t, struct ifnet *);
int in6_ioctl(u_long, caddr_t, struct ifnet *, int);
int in6_update_ifa(struct ifnet *, struct in6_aliasreq *,
struct in6_ifaddr *);
void in6_purgeaddr(struct ifaddr *);
int in6if_do_dad(struct ifnet *);
void *in6_domifattach(struct ifnet *);
void in6_domifdetach(struct ifnet *, void *);
struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int);
struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, struct in6_addr *);
int in6_addr2scopeid(unsigned int, struct in6_addr *);
int in6_matchlen(struct in6_addr *, struct in6_addr *);
void in6_prefixlen2mask(struct in6_addr *, int);
void in6_purgeprefix(struct ifnet *);
#endif /* _KERNEL */
#endif /* _NETINET6_IN6_VAR_H_ */
9
2
2
1060
129
71
90
9
3306
3315
3302
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* $OpenBSD: subr_xxx.c,v 1.17 2019/05/17 03:53:08 visa Exp $ */
/* $NetBSD: subr_xxx.c,v 1.10 1996/02/04 02:16:51 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)subr_xxx.c 8.1 (Berkeley) 6/10/93
*/
/*
* Miscellaneous trivial functions, including many
* that are often inline-expanded or done in assembler.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/smr.h>
/*
* Unsupported device function (e.g. writing to read-only device).
*/
int
enodev(void)
{
return (ENODEV);
}
/*
* Unconfigured device function; driver not configured.
*/
int
enxio(void)
{
return (ENXIO);
}
/*
* Unsupported ioctl function.
*/
int
enoioctl(void)
{
return (ENOTTY);
}
/*
* Unsupported system function.
* This is used for an otherwise-reasonable operation
* that is not supported by the current system binary.
*/
int
enosys(void)
{
return (ENOSYS);
}
/*
* Return error for operation not supported
* on a specific object or file type.
*/
int
eopnotsupp(void *v)
{
return (EOPNOTSUPP);
}
/*
* Generic null operation, always returns success.
*/
int
nullop(void *v)
{
return (0);
}
struct bdevsw *
bdevsw_lookup(dev_t dev)
{
return (&bdevsw[major(dev)]);
}
struct cdevsw *
cdevsw_lookup(dev_t dev)
{
return (&cdevsw[major(dev)]);
}
/*
* Convert a character device number to a block device number.
*/
dev_t
chrtoblk(dev_t dev)
{
int blkmaj;
if (major(dev) >= nchrdev || major(dev) >= nchrtoblktbl)
return (NODEV);
blkmaj = chrtoblktbl[major(dev)];
if (blkmaj == NODEV)
return (NODEV);
return (makedev(blkmaj, minor(dev)));
}
/*
* Convert a block device number to a character device number.
*/
dev_t
blktochr(dev_t dev)
{
int blkmaj = major(dev);
int i;
if (blkmaj >= nblkdev)
return (NODEV);
for (i = 0; i < nchrtoblktbl; i++)
if (blkmaj == chrtoblktbl[i])
return (makedev(i, minor(dev)));
return (NODEV);
}
/*
* Check that we're in a context where it's okay to sleep.
*/
void
assertwaitok(void)
{
if (panicstr || db_active)
return;
splassert(IPL_NONE);
SMR_ASSERT_NONCRITICAL();
#ifdef DIAGNOSTIC
if (curcpu()->ci_mutex_level != 0)
panic("assertwaitok: non-zero mutex count: %d",
curcpu()->ci_mutex_level);
#endif
}
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/* $OpenBSD: mpls_raw.c,v 1.19 2022/02/22 01:15:02 guenther Exp $ */
/*
* Copyright (C) 1999, 2000 and 2001 AYAME Project, WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/errno.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netmpls/mpls.h>
int mpls_defttl = 255;
int mpls_push_expnull_ip = 0;
int mpls_push_expnull_ip6 = 0;
int mpls_mapttl_ip = 1;
int mpls_mapttl_ip6 = 0;
const struct sysctl_bounded_args mplsctl_vars[] = {
{ MPLSCTL_DEFTTL, &mpls_defttl, 0, 255 },
{ MPLSCTL_MAPTTL_IP, &mpls_mapttl_ip, 0, 1 },
{ MPLSCTL_MAPTTL_IP6, &mpls_mapttl_ip6, 0, 1 },
};
int
mpls_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
return sysctl_bounded_arr(mplsctl_vars, nitems(mplsctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
}
528
527
505
52
52
52
52
450
158
527
122
122
76
51
122
122
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* $OpenBSD: ufs_inode.c,v 1.44 2020/02/27 09:10:31 mpi Exp $ */
/* $NetBSD: ufs_inode.c,v 1.7 1996/05/11 18:27:52 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_inode.c 8.7 (Berkeley) 7/22/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#ifdef UFS_DIRHASH
#include <ufs/ufs/dir.h>
#include <ufs/ufs/dirhash.h>
#endif
/*
* Last reference to an inode. If necessary, write or delete it.
*/
int
ufs_inactive(void *v)
{
struct vop_inactive_args *ap = v;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
mode_t mode;
int error = 0;
#ifdef DIAGNOSTIC
extern int prtactive;
if (prtactive && vp->v_usecount != 0)
vprint("ufs_inactive: pushing active", vp);
#endif
/*
* Ignore inodes related to stale file handles.
*/
if (ip->i_din1 == NULL || DIP(ip, mode) == 0)
goto out;
if (DIP(ip, nlink) <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
if (getinoquota(ip) == 0)
(void)ufs_quota_free_inode(ip, NOCRED);
error = UFS_TRUNCATE(ip, (off_t)0, 0, NOCRED);
DIP_ASSIGN(ip, rdev, 0);
mode = DIP(ip, mode);
DIP_ASSIGN(ip, mode, 0);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
/*
* Setting the mode to zero needs to wait for the inode to be
* written just as does a change to the link count. So, rather
* than creating a new entry point to do the same thing, we
* just use softdep_change_linkcnt(). Also, we can't let
* softdep co-opt us to help on its worklist, as we may end up
* trying to recycle vnodes and getting to this same point a
* couple of times, blowing the kernel stack. However, this
* could be optimized by checking if we are coming from
* vrele(), vput() or vclean() (by checking for VXLOCK) and
* just avoiding the co-opt to happen in the last case.
*/
if (DOINGSOFTDEP(vp))
softdep_change_linkcnt(ip, 1);
UFS_INODE_FREE(ip, ip->i_number, mode);
}
if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
UFS_UPDATE(ip, 0);
}
out:
VOP_UNLOCK(vp);
/*
* If we are done with the inode, reclaim it
* so that it can be reused immediately.
*/
if (ip->i_din1 == NULL || DIP(ip, mode) == 0)
vrecycle(vp, ap->a_p);
return (error);
}
/*
* Reclaim an inode so that it can be used for other purposes.
*/
int
ufs_reclaim(struct vnode *vp)
{
struct inode *ip;
#ifdef DIAGNOSTIC
extern int prtactive;
if (prtactive && vp->v_usecount != 0)
vprint("ufs_reclaim: pushing active", vp);
#endif
ip = VTOI(vp);
/*
* Stop deferring timestamp writes
*/
if (ip->i_flag & IN_LAZYMOD) {
ip->i_flag |= IN_MODIFIED;
UFS_UPDATE(ip, 0);
}
/*
* Remove the inode from its hash chain.
*/
ufs_ihashrem(ip);
/*
* Purge old data structures associated with the inode.
*/
cache_purge(vp);
if (ip->i_devvp) {
vrele(ip->i_devvp);
}
#ifdef UFS_DIRHASH
if (ip->i_dirhash != NULL)
ufsdirhash_free(ip);
#endif
ufs_quota_delete(ip);
return (0);
}
120
119
120
101
120
1
27
7
91
99
21
2
15
93
17
37
65
50
54
49
56
103
14
99
9
20
8
21
21
7
26
5
28
25
27
84
1
1
2
68
41
1
105
8
120
105
106
106
9
9
5
22
18
4
2
2
20
3
4
1
1
1
4
288
35
189
14
22
1
1
1
1
1
1
2
1
2
2
2
2
2
2
2
1
1
1
1
8
4
3
3
3
3
2
1
3
2
1
2
2
1
32
2
2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
3
1
14
4
2
3
1
8
4
6
2
2
3
5
1
4
38
16
2
15
9
1
121
93
42
5
16
6
4
1
6
5
5
2
10
3
15
7
38
5
6
3
16
6
16
15
34
5
6
10
10
11
4
8
56
22
20
26
10
2
2
2
1
1
1
1
33
27
6
3
52
17
35
2
113
47
79
45
34
2
11
23
47
3
105
37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
/* $OpenBSD: ip_output.c,v 1.382 2022/08/12 17:04:16 bluhm Exp $ */
/* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_enc.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp_var.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#ifdef IPSEC
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
#endif /* IPSEC */
int ip_pcbopts(struct mbuf **, struct mbuf *);
int ip_multicast_if(struct ip_mreqn *, u_int, unsigned int *);
int ip_setmoptions(int, struct ip_moptions **, struct mbuf *, u_int);
void ip_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in *);
static __inline u_int16_t __attribute__((__unused__))
in_cksum_phdr(u_int32_t, u_int32_t, u_int32_t);
void in_delayed_cksum(struct mbuf *);
int in_ifcap_cksum(struct mbuf *, struct ifnet *, int);
int ip_output_ipsec_lookup(struct mbuf *m, int hlen, struct inpcb *inp,
struct tdb **, int ipsecflowinfo);
void ip_output_ipsec_pmtu_update(struct tdb *, struct route *, struct in_addr,
int, int);
int ip_output_ipsec_send(struct tdb *, struct mbuf *, struct route *, int);
/*
* IP output. The packet in mbuf chain m contains a skeletal IP
* header (with len, off, ttl, proto, tos, src, dst).
* The mbuf chain containing the packet will be freed.
* The mbuf opt, if present, will not be freed.
*/
int
ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
struct ip_moptions *imo, struct inpcb *inp, u_int32_t ipsecflowinfo)
{
struct ip *ip;
struct ifnet *ifp = NULL;
struct mbuf_list fml;
int hlen = sizeof (struct ip);
int error = 0;
struct route iproute;
struct sockaddr_in *dst;
struct tdb *tdb = NULL;
u_long mtu;
#if NPF > 0
u_int orig_rtableid;
#endif
NET_ASSERT_LOCKED();
#ifdef IPSEC
if (inp && (inp->inp_flags & INP_IPV6) != 0)
panic("ip_output: IPv6 pcb is passed");
#endif /* IPSEC */
#ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0)
panic("ip_output no HDR");
#endif
if (opt)
m = ip_insertoptions(m, opt, &hlen);
ip = mtod(m, struct ip *);
/*
* Fill in IP header.
*/
if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) {
ip->ip_v = IPVERSION;
ip->ip_off &= htons(IP_DF);
ip->ip_id = htons(ip_randomid());
ip->ip_hl = hlen >> 2;
ipstat_inc(ips_localout);
} else {
hlen = ip->ip_hl << 2;
}
/*
* We should not send traffic to 0/8 say both Stevens and RFCs
* 5735 section 3 and 1122 sections 3.2.1.3 and 3.3.6.
*/
if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == 0) {
error = ENETUNREACH;
goto bad;
}
#if NPF > 0
orig_rtableid = m->m_pkthdr.ph_rtableid;
reroute:
#endif
/*
* Do a route lookup now in case we need the source address to
* do an SPD lookup in IPsec; for most packets, the source address
* is set at a higher level protocol. ICMPs and other packets
* though (e.g., traceroute) have a source address of zeroes.
*/
if (ro == NULL) {
ro = &iproute;
memset(ro, 0, sizeof(*ro));
}
dst = satosin(&ro->ro_dst);
/*
* If there is a cached route, check that it is to the same
* destination and is still up. If not, free it and try again.
*/
if (!rtisvalid(ro->ro_rt) ||
dst->sin_addr.s_addr != ip->ip_dst.s_addr ||
ro->ro_tableid != m->m_pkthdr.ph_rtableid) {
rtfree(ro->ro_rt);
ro->ro_rt = NULL;
}
if (ro->ro_rt == NULL) {
dst->sin_family = AF_INET;
dst->sin_len = sizeof(*dst);
dst->sin_addr = ip->ip_dst;
ro->ro_tableid = m->m_pkthdr.ph_rtableid;
}
if ((IN_MULTICAST(ip->ip_dst.s_addr) ||
(ip->ip_dst.s_addr == INADDR_BROADCAST)) &&
imo != NULL && (ifp = if_get(imo->imo_ifidx)) != NULL) {
mtu = ifp->if_mtu;
if (ip->ip_src.s_addr == INADDR_ANY) {
struct in_ifaddr *ia;
IFP_TO_IA(ifp, ia);
if (ia != NULL)
ip->ip_src = ia->ia_addr.sin_addr;
}
} else {
struct in_ifaddr *ia;
if (ro->ro_rt == NULL)
ro->ro_rt = rtalloc_mpath(&ro->ro_dst,
&ip->ip_src.s_addr, ro->ro_tableid);
if (ro->ro_rt == NULL) {
ipstat_inc(ips_noroute);
error = EHOSTUNREACH;
goto bad;
}
ia = ifatoia(ro->ro_rt->rt_ifa);
if (ISSET(ro->ro_rt->rt_flags, RTF_LOCAL))
ifp = if_get(rtable_loindex(m->m_pkthdr.ph_rtableid));
else
ifp = if_get(ro->ro_rt->rt_ifidx);
/*
* We aren't using rtisvalid() here because the UP/DOWN state
* machine is broken with some Ethernet drivers like em(4).
* As a result we might try to use an invalid cached route
* entry while an interface is being detached.
*/
if (ifp == NULL) {
ipstat_inc(ips_noroute);
error = EHOSTUNREACH;
goto bad;
}
if ((mtu = ro->ro_rt->rt_mtu) == 0)
mtu = ifp->if_mtu;
if (ro->ro_rt->rt_flags & RTF_GATEWAY)
dst = satosin(ro->ro_rt->rt_gateway);
/* Set the source IP address */
if (ip->ip_src.s_addr == INADDR_ANY && ia)
ip->ip_src = ia->ia_addr.sin_addr;
}
#ifdef IPSEC
if (ipsec_in_use || inp != NULL) {
/* Do we have any pending SAs to apply ? */
error = ip_output_ipsec_lookup(m, hlen, inp, &tdb,
ipsecflowinfo);
if (error) {
/* Should silently drop packet */
if (error == -EINVAL)
error = 0;
goto bad;
}
if (tdb != NULL) {
/*
* If it needs TCP/UDP hardware-checksumming, do the
* computation now.
*/
in_proto_cksum_out(m, NULL);
}
}
#endif /* IPSEC */
if (IN_MULTICAST(ip->ip_dst.s_addr) ||
(ip->ip_dst.s_addr == INADDR_BROADCAST)) {
m->m_flags |= (ip->ip_dst.s_addr == INADDR_BROADCAST) ?
M_BCAST : M_MCAST;
/*
* IP destination address is multicast. Make sure "dst"
* still points to the address in "ro". (It may have been
* changed to point to a gateway address, above.)
*/
dst = satosin(&ro->ro_dst);
/*
* See if the caller provided any multicast options
*/
if (imo != NULL)
ip->ip_ttl = imo->imo_ttl;
else
ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
/*
* if we don't know the outgoing ifp yet, we can't generate
* output
*/
if (!ifp) {
ipstat_inc(ips_noroute);
error = EHOSTUNREACH;
goto bad;
}
/*
* Confirm that the outgoing interface supports multicast,
* but only if the packet actually is going out on that
* interface (i.e., no IPsec is applied).
*/
if ((((m->m_flags & M_MCAST) &&
(ifp->if_flags & IFF_MULTICAST) == 0) ||
((m->m_flags & M_BCAST) &&
(ifp->if_flags & IFF_BROADCAST) == 0)) && (tdb == NULL)) {
ipstat_inc(ips_noroute);
error = ENETUNREACH;
goto bad;
}
/*
* If source address not specified yet, use address
* of outgoing interface.
*/
if (ip->ip_src.s_addr == INADDR_ANY) {
struct in_ifaddr *ia;
IFP_TO_IA(ifp, ia);
if (ia != NULL)
ip->ip_src = ia->ia_addr.sin_addr;
}
if ((imo == NULL || imo->imo_loop) &&
in_hasmulti(&ip->ip_dst, ifp)) {
/*
* If we belong to the destination multicast group
* on the outgoing interface, and the caller did not
* forbid loopback, loop back a copy.
* Can't defer TCP/UDP checksumming, do the
* computation now.
*/
in_proto_cksum_out(m, NULL);
ip_mloopback(ifp, m, dst);
}
#ifdef MROUTING
else {
/*
* If we are acting as a multicast router, perform
* multicast forwarding as if the packet had just
* arrived on the interface to which we are about
* to send. The multicast forwarding function
* recursively calls this function, using the
* IP_FORWARDING flag to prevent infinite recursion.
*
* Multicasts that are looped back by ip_mloopback(),
* above, will be forwarded by the ip_input() routine,
* if necessary.
*/
if (ipmforwarding && ip_mrouter[ifp->if_rdomain] &&
(flags & IP_FORWARDING) == 0) {
int rv;
KERNEL_LOCK();
rv = ip_mforward(m, ifp);
KERNEL_UNLOCK();
if (rv != 0)
goto bad;
}
}
#endif
/*
* Multicasts with a time-to-live of zero may be looped-
* back, above, but must not be transmitted on a network.
* Also, multicasts addressed to the loopback interface
* are not sent -- the above call to ip_mloopback() will
* loop back a copy if this host actually belongs to the
* destination group on the loopback interface.
*/
if (ip->ip_ttl == 0 || (ifp->if_flags & IFF_LOOPBACK) != 0)
goto bad;
goto sendit;
}
/*
* Look for broadcast address and verify user is allowed to send
* such a packet; if the packet is going in an IPsec tunnel, skip
* this check.
*/
if ((tdb == NULL) && ((dst->sin_addr.s_addr == INADDR_BROADCAST) ||
(ro && ro->ro_rt && ISSET(ro->ro_rt->rt_flags, RTF_BROADCAST)))) {
if ((ifp->if_flags & IFF_BROADCAST) == 0) {
error = EADDRNOTAVAIL;
goto bad;
}
if ((flags & IP_ALLOWBROADCAST) == 0) {
error = EACCES;
goto bad;
}
/* Don't allow broadcast messages to be fragmented */
if (ntohs(ip->ip_len) > ifp->if_mtu) {
error = EMSGSIZE;
goto bad;
}
m->m_flags |= M_BCAST;
} else
m->m_flags &= ~M_BCAST;
sendit:
/*
* If we're doing Path MTU discovery, we need to set DF unless
* the route's MTU is locked.
*/
if ((flags & IP_MTUDISC) && ro && ro->ro_rt &&
(ro->ro_rt->rt_locks & RTV_MTU) == 0)
ip->ip_off |= htons(IP_DF);
#ifdef IPSEC
/*
* Check if the packet needs encapsulation.
*/
if (tdb != NULL) {
/* Callee frees mbuf */
error = ip_output_ipsec_send(tdb, m, ro,
(flags & IP_FORWARDING) ? 1 : 0);
goto done;
}
#endif /* IPSEC */
/*
* Packet filter
*/
#if NPF > 0
if (pf_test(AF_INET, (flags & IP_FORWARDING) ? PF_FWD : PF_OUT,
ifp, &m) != PF_PASS) {
error = EACCES;
goto bad;
}
if (m == NULL)
goto done;
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
(PF_TAG_REROUTE | PF_TAG_GENERATED))
/* already rerun the route lookup, go on */
m->m_pkthdr.pf.flags &= ~(PF_TAG_GENERATED | PF_TAG_REROUTE);
else if (m->m_pkthdr.pf.flags & PF_TAG_REROUTE) {
/* tag as generated to skip over pf_test on rerun */
m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
ro = NULL;
if_put(ifp); /* drop reference since target changed */
ifp = NULL;
goto reroute;
}
#endif
in_proto_cksum_out(m, ifp);
#ifdef IPSEC
if (ipsec_in_use && (flags & IP_FORWARDING) && (ipforwarding == 2) &&
(m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) == NULL)) {
error = EHOSTUNREACH;
goto bad;
}
#endif
/*
* If small enough for interface, can just send directly.
*/
if (ntohs(ip->ip_len) <= mtu) {
ip->ip_sum = 0;
if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4))
m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
else {
ipstat_inc(ips_outswcsum);
ip->ip_sum = in_cksum(m, hlen);
}
error = ifp->if_output(ifp, m, sintosa(dst), ro->ro_rt);
goto done;
}
/*
* Too large for interface; fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
if (ip->ip_off & htons(IP_DF)) {
#ifdef IPSEC
if (ip_mtudisc)
ipsec_adjust_mtu(m, ifp->if_mtu);
#endif
error = EMSGSIZE;
#if NPF > 0
/* pf changed routing table, use orig rtable for path MTU */
if (ro->ro_tableid != orig_rtableid) {
rtfree(ro->ro_rt);
ro->ro_tableid = orig_rtableid;
ro->ro_rt = icmp_mtudisc_clone(
satosin(&ro->ro_dst)->sin_addr, ro->ro_tableid, 0);
}
#endif
/*
* This case can happen if the user changed the MTU
* of an interface after enabling IP on it. Because
* most netifs don't keep track of routes pointing to
* them, there is no way for one to update all its
* routes when the MTU is changed.
*/
if (rtisvalid(ro->ro_rt) &&
ISSET(ro->ro_rt->rt_flags, RTF_HOST) &&
!(ro->ro_rt->rt_locks & RTV_MTU) &&
(ro->ro_rt->rt_mtu > ifp->if_mtu)) {
ro->ro_rt->rt_mtu = ifp->if_mtu;
}
ipstat_inc(ips_cantfrag);
goto bad;
}
error = ip_fragment(m, &fml, ifp, mtu);
if (error)
goto done;
while ((m = ml_dequeue(&fml)) != NULL) {
error = ifp->if_output(ifp, m, sintosa(dst), ro->ro_rt);
if (error)
break;
}
if (error)
ml_purge(&fml);
else
ipstat_inc(ips_fragmented);
done:
if (ro == &iproute && ro->ro_rt)
rtfree(ro->ro_rt);
if_put(ifp);
#ifdef IPSEC
tdb_unref(tdb);
#endif /* IPSEC */
return (error);
bad:
m_freem(m);
goto done;
}
#ifdef IPSEC
int
ip_output_ipsec_lookup(struct mbuf *m, int hlen, struct inpcb *inp,
struct tdb **tdbout, int ipsecflowinfo)
{
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct tdb *tdb;
struct ipsec_ids *ids = NULL;
int error;
/* Do we have any pending SAs to apply ? */
if (ipsecflowinfo)
ids = ipsp_ids_lookup(ipsecflowinfo);
error = ipsp_spd_lookup(m, AF_INET, hlen, IPSP_DIRECTION_OUT,
NULL, inp, &tdb, ids);
ipsp_ids_free(ids);
if (error || tdb == NULL) {
*tdbout = NULL;
return error;
}
/* Loop detection */
for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) {
if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE)
continue;
tdbi = (struct tdb_ident *)(mtag + 1);
if (tdbi->spi == tdb->tdb_spi &&
tdbi->proto == tdb->tdb_sproto &&
tdbi->rdomain == tdb->tdb_rdomain &&
!memcmp(&tdbi->dst, &tdb->tdb_dst,
sizeof(union sockaddr_union))) {
/* no IPsec needed */
tdb_unref(tdb);
*tdbout = NULL;
return 0;
}
}
*tdbout = tdb;
return 0;
}
void
ip_output_ipsec_pmtu_update(struct tdb *tdb, struct route *ro,
struct in_addr dst, int rtableid, int transportmode)
{
struct rtentry *rt = NULL;
int rt_mtucloned = 0;
/* Find a host route to store the mtu in */
if (ro != NULL)
rt = ro->ro_rt;
/* but don't add a PMTU route for transport mode SAs */
if (transportmode)
rt = NULL;
else if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0) {
rt = icmp_mtudisc_clone(dst, rtableid, 1);
rt_mtucloned = 1;
}
DPRINTF("spi %08x mtu %d rt %p cloned %d",
ntohl(tdb->tdb_spi), tdb->tdb_mtu, rt, rt_mtucloned);
if (rt != NULL) {
rt->rt_mtu = tdb->tdb_mtu;
if (ro != NULL && ro->ro_rt != NULL) {
rtfree(ro->ro_rt);
ro->ro_rt = rtalloc(&ro->ro_dst, RT_RESOLVE, rtableid);
}
if (rt_mtucloned)
rtfree(rt);
}
}
int
ip_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route *ro, int fwd)
{
#if NPF > 0
struct ifnet *encif;
#endif
struct ip *ip;
struct in_addr dst;
int error, rtableid;
#if NPF > 0
/*
* Packet filter
*/
if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL ||
pf_test(AF_INET, fwd ? PF_FWD : PF_OUT, encif, &m) != PF_PASS) {
m_freem(m);
return EACCES;
}
if (m == NULL)
return 0;
/*
* PF_TAG_REROUTE handling or not...
* Packet is entering IPsec so the routing is
* already overruled by the IPsec policy.
* Until now the change was not reconsidered.
* What's the behaviour?
*/
in_proto_cksum_out(m, encif);
#endif
/* Check if we are allowed to fragment */
ip = mtod(m, struct ip *);
dst = ip->ip_dst;
rtableid = m->m_pkthdr.ph_rtableid;
if (ip_mtudisc && (ip->ip_off & htons(IP_DF)) && tdb->tdb_mtu &&
ntohs(ip->ip_len) > tdb->tdb_mtu &&
tdb->tdb_mtutimeout > gettime()) {
int transportmode;
transportmode = (tdb->tdb_dst.sa.sa_family == AF_INET) &&
(tdb->tdb_dst.sin.sin_addr.s_addr == dst.s_addr);
ip_output_ipsec_pmtu_update(tdb, ro, dst, rtableid,
transportmode);
ipsec_adjust_mtu(m, tdb->tdb_mtu);
m_freem(m);
return EMSGSIZE;
}
/* propagate IP_DF for v4-over-v6 */
if (ip_mtudisc && ip->ip_off & htons(IP_DF))
SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
/*
* Clear these -- they'll be set in the recursive invocation
* as needed.
*/
m->m_flags &= ~(M_MCAST | M_BCAST);
/* Callee frees mbuf */
KERNEL_LOCK();
error = ipsp_process_packet(m, tdb, AF_INET, 0);
KERNEL_UNLOCK();
if (error) {
ipsecstat_inc(ipsec_odrops);
tdbstat_inc(tdb, tdb_odrops);
}
if (ip_mtudisc && error == EMSGSIZE)
ip_output_ipsec_pmtu_update(tdb, ro, dst, rtableid, 0);
return error;
}
#endif /* IPSEC */
int
ip_fragment(struct mbuf *m0, struct mbuf_list *fml, struct ifnet *ifp,
u_long mtu)
{
struct mbuf *m;
struct ip *ip;
int firstlen, hlen, tlen, len, off;
int error;
ml_init(fml);
ml_enqueue(fml, m0);
ip = mtod(m0, struct ip *);
hlen = ip->ip_hl << 2;
tlen = m0->m_pkthdr.len;
len = (mtu - hlen) &~ 7;
if (len < 8) {
error = EMSGSIZE;
goto bad;
}
firstlen = len;
/*
* If we are doing fragmentation, we can't defer TCP/UDP
* checksumming; compute the checksum and clear the flag.
*/
in_proto_cksum_out(m0, NULL);
/*
* Loop through length of segment after first fragment,
* make new header and copy data of each part and link onto chain.
*/
for (off = hlen + firstlen; off < tlen; off += len) {
struct ip *mhip;
int mhlen;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
ml_enqueue(fml, m);
if ((error = m_dup_pkthdr(m, m0, M_DONTWAIT)) != 0)
goto bad;
m->m_data += max_linkhdr;
mhip = mtod(m, struct ip *);
*mhip = *ip;
if (hlen > sizeof(struct ip)) {
mhlen = ip_optcopy(ip, mhip) + sizeof(struct ip);
mhip->ip_hl = mhlen >> 2;
} else
mhlen = sizeof(struct ip);
m->m_len = mhlen;
mhip->ip_off = ((off - hlen) >> 3) +
(ntohs(ip->ip_off) & ~IP_MF);
if (ip->ip_off & htons(IP_MF))
mhip->ip_off |= IP_MF;
if (off + len >= tlen)
len = tlen - off;
else
mhip->ip_off |= IP_MF;
mhip->ip_off = htons(mhip->ip_off);
m->m_pkthdr.len = mhlen + len;
mhip->ip_len = htons(m->m_pkthdr.len);
m->m_next = m_copym(m0, off, len, M_NOWAIT);
if (m->m_next == NULL) {
error = ENOBUFS;
goto bad;
}
mhip->ip_sum = 0;
if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4))
m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
else {
ipstat_inc(ips_outswcsum);
mhip->ip_sum = in_cksum(m, mhlen);
}
}
/*
* Update first fragment by trimming what's been copied out
* and updating header, then send each fragment (in order).
*/
m = m0;
m_adj(m, hlen + firstlen - tlen);
ip->ip_off |= htons(IP_MF);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_sum = 0;
if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4))
m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
else {
ipstat_inc(ips_outswcsum);
ip->ip_sum = in_cksum(m, hlen);
}
ipstat_add(ips_ofragments, ml_len(fml));
return (0);
bad:
ipstat_inc(ips_odropped);
ml_purge(fml);
return (error);
}
/*
* Insert IP options into preformed packet.
* Adjust IP destination as required for IP source routing,
* as indicated by a non-zero in_addr at the start of the options.
*/
struct mbuf *
ip_insertoptions(struct mbuf *m, struct mbuf *opt, int *phlen)
{
struct ipoption *p = mtod(opt, struct ipoption *);
struct mbuf *n;
struct ip *ip = mtod(m, struct ip *);
unsigned int optlen;
optlen = opt->m_len - sizeof(p->ipopt_dst);
if (optlen + ntohs(ip->ip_len) > IP_MAXPACKET)
return (m); /* XXX should fail */
/* check if options will fit to IP header */
if ((optlen + sizeof(struct ip)) > (0x0f << 2)) {
*phlen = sizeof(struct ip);
return (m);
}
if (p->ipopt_dst.s_addr)
ip->ip_dst = p->ipopt_dst;
if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) {
MGETHDR(n, M_DONTWAIT, MT_HEADER);
if (n == NULL)
return (m);
M_MOVE_HDR(n, m);
n->m_pkthdr.len += optlen;
m->m_len -= sizeof(struct ip);
m->m_data += sizeof(struct ip);
n->m_next = m;
m = n;
m->m_len = optlen + sizeof(struct ip);
m->m_data += max_linkhdr;
memcpy(mtod(m, caddr_t), ip, sizeof(struct ip));
} else {
m->m_data -= optlen;
m->m_len += optlen;
m->m_pkthdr.len += optlen;
memmove(mtod(m, caddr_t), (caddr_t)ip, sizeof(struct ip));
}
ip = mtod(m, struct ip *);
memcpy(ip + 1, p->ipopt_list, optlen);
*phlen = sizeof(struct ip) + optlen;
ip->ip_len = htons(ntohs(ip->ip_len) + optlen);
return (m);
}
/*
* Copy options from ip to jp,
* omitting those not copied during fragmentation.
*/
int
ip_optcopy(struct ip *ip, struct ip *jp)
{
u_char *cp, *dp;
int opt, optlen, cnt;
cp = (u_char *)(ip + 1);
dp = (u_char *)(jp + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[0];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP) {
/* Preserve for IP mcast tunnel's LSRR alignment. */
*dp++ = IPOPT_NOP;
optlen = 1;
continue;
}
#ifdef DIAGNOSTIC
if (cnt < IPOPT_OLEN + sizeof(*cp))
panic("malformed IPv4 option passed to ip_optcopy");
#endif
optlen = cp[IPOPT_OLEN];
#ifdef DIAGNOSTIC
if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
panic("malformed IPv4 option passed to ip_optcopy");
#endif
/* bogus lengths should have been caught by ip_dooptions */
if (optlen > cnt)
optlen = cnt;
if (IPOPT_COPIED(opt)) {
memcpy(dp, cp, optlen);
dp += optlen;
}
}
for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
*dp++ = IPOPT_EOL;
return (optlen);
}
/*
* IP socket option processing.
*/
int
ip_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
int optval = 0;
struct proc *p = curproc; /* XXX */
int error = 0;
u_int rtableid, rtid = 0;
if (level != IPPROTO_IP)
return (EINVAL);
rtableid = p->p_p->ps_rtableid;
switch (op) {
case PRCO_SETOPT:
switch (optname) {
case IP_OPTIONS:
return (ip_pcbopts(&inp->inp_options, m));
case IP_TOS:
case IP_TTL:
case IP_MINTTL:
case IP_RECVOPTS:
case IP_RECVRETOPTS:
case IP_RECVDSTADDR:
case IP_RECVIF:
case IP_RECVTTL:
case IP_RECVDSTPORT:
case IP_RECVRTABLE:
case IP_IPSECFLOWINFO:
if (m == NULL || m->m_len != sizeof(int))
error = EINVAL;
else {
optval = *mtod(m, int *);
switch (optname) {
case IP_TOS:
inp->inp_ip.ip_tos = optval;
break;
case IP_TTL:
if (optval > 0 && optval <= MAXTTL)
inp->inp_ip.ip_ttl = optval;
else if (optval == -1)
inp->inp_ip.ip_ttl = ip_defttl;
else
error = EINVAL;
break;
case IP_MINTTL:
if (optval >= 0 && optval <= MAXTTL)
inp->inp_ip_minttl = optval;
else
error = EINVAL;
break;
#define OPTSET(bit) \
if (optval) \
inp->inp_flags |= bit; \
else \
inp->inp_flags &= ~bit;
case IP_RECVOPTS:
OPTSET(INP_RECVOPTS);
break;
case IP_RECVRETOPTS:
OPTSET(INP_RECVRETOPTS);
break;
case IP_RECVDSTADDR:
OPTSET(INP_RECVDSTADDR);
break;
case IP_RECVIF:
OPTSET(INP_RECVIF);
break;
case IP_RECVTTL:
OPTSET(INP_RECVTTL);
break;
case IP_RECVDSTPORT:
OPTSET(INP_RECVDSTPORT);
break;
case IP_RECVRTABLE:
OPTSET(INP_RECVRTABLE);
break;
case IP_IPSECFLOWINFO:
OPTSET(INP_IPSECFLOWINFO);
break;
}
}
break;
#undef OPTSET
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
error = ip_setmoptions(optname, &inp->inp_moptions, m,
inp->inp_rtableid);
break;
case IP_PORTRANGE:
if (m == NULL || m->m_len != sizeof(int))
error = EINVAL;
else {
optval = *mtod(m, int *);
switch (optval) {
case IP_PORTRANGE_DEFAULT:
inp->inp_flags &= ~(INP_LOWPORT);
inp->inp_flags &= ~(INP_HIGHPORT);
break;
case IP_PORTRANGE_HIGH:
inp->inp_flags &= ~(INP_LOWPORT);
inp->inp_flags |= INP_HIGHPORT;
break;
case IP_PORTRANGE_LOW:
inp->inp_flags &= ~(INP_HIGHPORT);
inp->inp_flags |= INP_LOWPORT;
break;
default:
error = EINVAL;
break;
}
}
break;
case IP_AUTH_LEVEL:
case IP_ESP_TRANS_LEVEL:
case IP_ESP_NETWORK_LEVEL:
case IP_IPCOMP_LEVEL:
#ifndef IPSEC
error = EOPNOTSUPP;
#else
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
optval = *mtod(m, int *);
if (optval < IPSEC_LEVEL_BYPASS ||
optval > IPSEC_LEVEL_UNIQUE) {
error = EINVAL;
break;
}
switch (optname) {
case IP_AUTH_LEVEL:
if (optval < IPSEC_AUTH_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_AUTH] = optval;
break;
case IP_ESP_TRANS_LEVEL:
if (optval < IPSEC_ESP_TRANS_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_ESP_TRANS] = optval;
break;
case IP_ESP_NETWORK_LEVEL:
if (optval < IPSEC_ESP_NETWORK_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_ESP_NETWORK] = optval;
break;
case IP_IPCOMP_LEVEL:
if (optval < IPSEC_IPCOMP_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_IPCOMP] = optval;
break;
}
#endif
break;
case IP_IPSEC_LOCAL_ID:
case IP_IPSEC_REMOTE_ID:
error = EOPNOTSUPP;
break;
case SO_RTABLE:
if (m == NULL || m->m_len < sizeof(u_int)) {
error = EINVAL;
break;
}
rtid = *mtod(m, u_int *);
if (inp->inp_rtableid == rtid)
break;
/* needs privileges to switch when already set */
if (rtableid != rtid && rtableid != 0 &&
(error = suser(p)) != 0)
break;
/* table must exist */
if (!rtable_exists(rtid)) {
error = EINVAL;
break;
}
if (inp->inp_lport) {
error = EBUSY;
break;
}
inp->inp_rtableid = rtid;
in_pcbrehash(inp);
break;
case IP_PIPEX:
if (m != NULL && m->m_len == sizeof(int))
inp->inp_pipex = *mtod(m, int *);
else
error = EINVAL;
break;
default:
error = ENOPROTOOPT;
break;
}
break;
case PRCO_GETOPT:
switch (optname) {
case IP_OPTIONS:
case IP_RETOPTS:
if (inp->inp_options) {
m->m_len = inp->inp_options->m_len;
memcpy(mtod(m, caddr_t),
mtod(inp->inp_options, caddr_t), m->m_len);
} else
m->m_len = 0;
break;
case IP_TOS:
case IP_TTL:
case IP_MINTTL:
case IP_RECVOPTS:
case IP_RECVRETOPTS:
case IP_RECVDSTADDR:
case IP_RECVIF:
case IP_RECVTTL:
case IP_RECVDSTPORT:
case IP_RECVRTABLE:
case IP_IPSECFLOWINFO:
case IP_IPDEFTTL:
m->m_len = sizeof(int);
switch (optname) {
case IP_TOS:
optval = inp->inp_ip.ip_tos;
break;
case IP_TTL:
optval = inp->inp_ip.ip_ttl;
break;
case IP_MINTTL:
optval = inp->inp_ip_minttl;
break;
case IP_IPDEFTTL:
optval = ip_defttl;
break;
#define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0)
case IP_RECVOPTS:
optval = OPTBIT(INP_RECVOPTS);
break;
case IP_RECVRETOPTS:
optval = OPTBIT(INP_RECVRETOPTS);
break;
case IP_RECVDSTADDR:
optval = OPTBIT(INP_RECVDSTADDR);
break;
case IP_RECVIF:
optval = OPTBIT(INP_RECVIF);
break;
case IP_RECVTTL:
optval = OPTBIT(INP_RECVTTL);
break;
case IP_RECVDSTPORT:
optval = OPTBIT(INP_RECVDSTPORT);
break;
case IP_RECVRTABLE:
optval = OPTBIT(INP_RECVRTABLE);
break;
case IP_IPSECFLOWINFO:
optval = OPTBIT(INP_IPSECFLOWINFO);
break;
}
*mtod(m, int *) = optval;
break;
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
error = ip_getmoptions(optname, inp->inp_moptions, m);
break;
case IP_PORTRANGE:
m->m_len = sizeof(int);
if (inp->inp_flags & INP_HIGHPORT)
optval = IP_PORTRANGE_HIGH;
else if (inp->inp_flags & INP_LOWPORT)
optval = IP_PORTRANGE_LOW;
else
optval = 0;
*mtod(m, int *) = optval;
break;
case IP_AUTH_LEVEL:
case IP_ESP_TRANS_LEVEL:
case IP_ESP_NETWORK_LEVEL:
case IP_IPCOMP_LEVEL:
#ifndef IPSEC
m->m_len = sizeof(int);
*mtod(m, int *) = IPSEC_LEVEL_NONE;
#else
m->m_len = sizeof(int);
switch (optname) {
case IP_AUTH_LEVEL:
optval = inp->inp_seclevel[SL_AUTH];
break;
case IP_ESP_TRANS_LEVEL:
optval = inp->inp_seclevel[SL_ESP_TRANS];
break;
case IP_ESP_NETWORK_LEVEL:
optval = inp->inp_seclevel[SL_ESP_NETWORK];
break;
case IP_IPCOMP_LEVEL:
optval = inp->inp_seclevel[SL_IPCOMP];
break;
}
*mtod(m, int *) = optval;
#endif
break;
case IP_IPSEC_LOCAL_ID:
case IP_IPSEC_REMOTE_ID:
error = EOPNOTSUPP;
break;
case SO_RTABLE:
m->m_len = sizeof(u_int);
*mtod(m, u_int *) = inp->inp_rtableid;
break;
case IP_PIPEX:
m->m_len = sizeof(int);
*mtod(m, int *) = inp->inp_pipex;
break;
default:
error = ENOPROTOOPT;
break;
}
break;
}
return (error);
}
/*
* Set up IP options in pcb for insertion in output packets.
* Store in mbuf with pointer in pcbopt, adding pseudo-option
* with destination address if source routed.
*/
int
ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
{
struct mbuf *n;
struct ipoption *p;
int cnt, off, optlen;
u_char *cp;
u_char opt;
/* turn off any old options */
m_freem(*pcbopt);
*pcbopt = NULL;
if (m == NULL || m->m_len == 0) {
/*
* Only turning off any previous options.
*/
return (0);
}
if (m->m_len % sizeof(int32_t) ||
m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
return (EINVAL);
/* Don't sleep because NET_LOCK() is hold. */
if ((n = m_get(M_NOWAIT, MT_SOOPTS)) == NULL)
return (ENOBUFS);
p = mtod(n, struct ipoption *);
memset(p, 0, sizeof (*p)); /* 0 = IPOPT_EOL, needed for padding */
n->m_len = sizeof(struct in_addr);
off = 0;
cnt = m->m_len;
cp = mtod(m, u_char *);
while (cnt > 0) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_NOP || opt == IPOPT_EOL) {
optlen = 1;
} else {
if (cnt < IPOPT_OLEN + sizeof(*cp))
goto bad;
optlen = cp[IPOPT_OLEN];
if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
goto bad;
}
switch (opt) {
default:
memcpy(p->ipopt_list + off, cp, optlen);
break;
case IPOPT_LSRR:
case IPOPT_SSRR:
/*
* user process specifies route as:
* ->A->B->C->D
* D must be our final destination (but we can't
* check that since we may not have connected yet).
* A is first hop destination, which doesn't appear in
* actual IP option, but is stored before the options.
*/
if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
goto bad;
/*
* Optlen is smaller because first address is popped.
* Cnt and cp will be adjusted a bit later to reflect
* this.
*/
optlen -= sizeof(struct in_addr);
p->ipopt_list[off + IPOPT_OPTVAL] = opt;
p->ipopt_list[off + IPOPT_OLEN] = optlen;
/*
* Move first hop before start of options.
*/
memcpy(&p->ipopt_dst, cp + IPOPT_OFFSET,
sizeof(struct in_addr));
cp += sizeof(struct in_addr);
cnt -= sizeof(struct in_addr);
/*
* Then copy rest of options
*/
memcpy(p->ipopt_list + off + IPOPT_OFFSET,
cp + IPOPT_OFFSET, optlen - IPOPT_OFFSET);
break;
}
off += optlen;
cp += optlen;
cnt -= optlen;
if (opt == IPOPT_EOL)
break;
}
/* pad options to next word, since p was zeroed just adjust off */
off = (off + sizeof(int32_t) - 1) & ~(sizeof(int32_t) - 1);
n->m_len += off;
if (n->m_len > sizeof(*p)) {
bad:
m_freem(n);
return (EINVAL);
}
*pcbopt = n;
return (0);
}
/*
* Lookup the interface based on the information in the ip_mreqn struct.
*/
int
ip_multicast_if(struct ip_mreqn *mreq, u_int rtableid, unsigned int *ifidx)
{
struct sockaddr_in sin;
struct rtentry *rt;
/*
* In case userland provides the imr_ifindex use this as interface.
* If no interface address was provided, use the interface of
* the route to the given multicast address.
*/
if (mreq->imr_ifindex != 0) {
*ifidx = mreq->imr_ifindex;
} else if (mreq->imr_address.s_addr == INADDR_ANY) {
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = mreq->imr_multiaddr;
rt = rtalloc(sintosa(&sin), RT_RESOLVE, rtableid);
if (!rtisvalid(rt)) {
rtfree(rt);
return EADDRNOTAVAIL;
}
*ifidx = rt->rt_ifidx;
rtfree(rt);
} else {
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = mreq->imr_address;
rt = rtalloc(sintosa(&sin), 0, rtableid);
if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
rtfree(rt);
return EADDRNOTAVAIL;
}
*ifidx = rt->rt_ifidx;
rtfree(rt);
}
return 0;
}
/*
* Set the IP multicast options in response to user setsockopt().
*/
int
ip_setmoptions(int optname, struct ip_moptions **imop, struct mbuf *m,
u_int rtableid)
{
struct in_addr addr;
struct in_ifaddr *ia;
struct ip_mreqn mreqn;
struct ifnet *ifp = NULL;
struct ip_moptions *imo = *imop;
struct in_multi **immp;
struct sockaddr_in sin;
unsigned int ifidx;
int i, error = 0;
u_char loop;
if (imo == NULL) {
/*
* No multicast option buffer attached to the pcb;
* allocate one and initialize to default values.
*/
imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK|M_ZERO);
immp = mallocarray(IP_MIN_MEMBERSHIPS, sizeof(*immp), M_IPMOPTS,
M_WAITOK|M_ZERO);
*imop = imo;
imo->imo_ifidx = 0;
imo->imo_ttl = IP_DEFAULT_MULTICAST_TTL;
imo->imo_loop = IP_DEFAULT_MULTICAST_LOOP;
imo->imo_num_memberships = 0;
imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
imo->imo_membership = immp;
}
switch (optname) {
case IP_MULTICAST_IF:
/*
* Select the interface for outgoing multicast packets.
*/
if (m == NULL) {
error = EINVAL;
break;
}
if (m->m_len == sizeof(struct in_addr)) {
addr = *(mtod(m, struct in_addr *));
} else if (m->m_len == sizeof(struct ip_mreq) ||
m->m_len == sizeof(struct ip_mreqn)) {
memset(&mreqn, 0, sizeof(mreqn));
memcpy(&mreqn, mtod(m, void *), m->m_len);
/*
* If an interface index is given use this
* index to set the imo_ifidx but check first
* that the interface actually exists.
* In the other case just set the addr to
* the imr_address and fall through to the
* regular code.
*/
if (mreqn.imr_ifindex != 0) {
ifp = if_get(mreqn.imr_ifindex);
if (ifp == NULL ||
ifp->if_rdomain != rtable_l2(rtableid)) {
error = EADDRNOTAVAIL;
if_put(ifp);
break;
}
imo->imo_ifidx = ifp->if_index;
if_put(ifp);
break;
} else
addr = mreqn.imr_address;
} else {
error = EINVAL;
break;
}
/*
* INADDR_ANY is used to remove a previous selection.
* When no interface is selected, a default one is
* chosen every time a multicast packet is sent.
*/
if (addr.s_addr == INADDR_ANY) {
imo->imo_ifidx = 0;
break;
}
/*
* The selected interface is identified by its local
* IP address. Find the interface and confirm that
* it supports multicasting.
*/
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = addr;
ia = ifatoia(ifa_ifwithaddr(sintosa(&sin), rtableid));
if (ia == NULL ||
(ia->ia_ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
break;
}
imo->imo_ifidx = ia->ia_ifp->if_index;
break;
case IP_MULTICAST_TTL:
/*
* Set the IP time-to-live for outgoing multicast packets.
*/
if (m == NULL || m->m_len != 1) {
error = EINVAL;
break;
}
imo->imo_ttl = *(mtod(m, u_char *));
break;
case IP_MULTICAST_LOOP:
/*
* Set the loopback flag for outgoing multicast packets.
* Must be zero or one.
*/
if (m == NULL || m->m_len != 1 ||
(loop = *(mtod(m, u_char *))) > 1) {
error = EINVAL;
break;
}
imo->imo_loop = loop;
break;
case IP_ADD_MEMBERSHIP:
/*
* Add a multicast group membership.
* Group must be a valid IP multicast address.
*/
if (m == NULL || !(m->m_len == sizeof(struct ip_mreq) ||
m->m_len == sizeof(struct ip_mreqn))) {
error = EINVAL;
break;
}
memset(&mreqn, 0, sizeof(mreqn));
memcpy(&mreqn, mtod(m, void *), m->m_len);
if (!IN_MULTICAST(mreqn.imr_multiaddr.s_addr)) {
error = EINVAL;
break;
}
error = ip_multicast_if(&mreqn, rtableid, &ifidx);
if (error)
break;
/*
* See if we found an interface, and confirm that it
* supports multicast.
*/
ifp = if_get(ifidx);
if (ifp == NULL || ifp->if_rdomain != rtable_l2(rtableid) ||
(ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
if_put(ifp);
break;
}
/*
* See if the membership already exists or if all the
* membership slots are full.
*/
for (i = 0; i < imo->imo_num_memberships; ++i) {
if (imo->imo_membership[i]->inm_ifidx == ifidx &&
imo->imo_membership[i]->inm_addr.s_addr
== mreqn.imr_multiaddr.s_addr)
break;
}
if (i < imo->imo_num_memberships) {
error = EADDRINUSE;
if_put(ifp);
break;
}
if (imo->imo_num_memberships == imo->imo_max_memberships) {
struct in_multi **nmships, **omships;
size_t newmax;
/*
* Resize the vector to next power-of-two minus 1. If
* the size would exceed the maximum then we know we've
* really run out of entries. Otherwise, we reallocate
* the vector.
*/
nmships = NULL;
omships = imo->imo_membership;
newmax = ((imo->imo_max_memberships + 1) * 2) - 1;
if (newmax <= IP_MAX_MEMBERSHIPS) {
nmships = mallocarray(newmax, sizeof(*nmships),
M_IPMOPTS, M_NOWAIT|M_ZERO);
if (nmships != NULL) {
memcpy(nmships, omships,
sizeof(*omships) *
imo->imo_max_memberships);
free(omships, M_IPMOPTS,
sizeof(*omships) *
imo->imo_max_memberships);
imo->imo_membership = nmships;
imo->imo_max_memberships = newmax;
}
}
if (nmships == NULL) {
error = ENOBUFS;
if_put(ifp);
break;
}
}
/*
* Everything looks good; add a new record to the multicast
* address list for the given interface.
*/
if ((imo->imo_membership[i] =
in_addmulti(&mreqn.imr_multiaddr, ifp)) == NULL) {
error = ENOBUFS;
if_put(ifp);
break;
}
++imo->imo_num_memberships;
if_put(ifp);
break;
case IP_DROP_MEMBERSHIP:
/*
* Drop a multicast group membership.
* Group must be a valid IP multicast address.
*/
if (m == NULL || !(m->m_len == sizeof(struct ip_mreq) ||
m->m_len == sizeof(struct ip_mreqn))) {
error = EINVAL;
break;
}
memset(&mreqn, 0, sizeof(mreqn));
memcpy(&mreqn, mtod(m, void *), m->m_len);
if (!IN_MULTICAST(mreqn.imr_multiaddr.s_addr)) {
error = EINVAL;
break;
}
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
*/
error = ip_multicast_if(&mreqn, rtableid, &ifidx);
if (error)
break;
/*
* Find the membership in the membership array.
*/
for (i = 0; i < imo->imo_num_memberships; ++i) {
if ((ifidx == 0 ||
imo->imo_membership[i]->inm_ifidx == ifidx) &&
imo->imo_membership[i]->inm_addr.s_addr ==
mreqn.imr_multiaddr.s_addr)
break;
}
if (i == imo->imo_num_memberships) {
error = EADDRNOTAVAIL;
break;
}
/*
* Give up the multicast address record to which the
* membership points.
*/
in_delmulti(imo->imo_membership[i]);
/*
* Remove the gap in the membership array.
*/
for (++i; i < imo->imo_num_memberships; ++i)
imo->imo_membership[i-1] = imo->imo_membership[i];
--imo->imo_num_memberships;
break;
default:
error = EOPNOTSUPP;
break;
}
/*
* If all options have default values, no need to keep the data.
*/
if (imo->imo_ifidx == 0 &&
imo->imo_ttl == IP_DEFAULT_MULTICAST_TTL &&
imo->imo_loop == IP_DEFAULT_MULTICAST_LOOP &&
imo->imo_num_memberships == 0) {
free(imo->imo_membership , M_IPMOPTS,
imo->imo_max_memberships * sizeof(struct in_multi *));
free(*imop, M_IPMOPTS, sizeof(**imop));
*imop = NULL;
}
return (error);
}
/*
* Return the IP multicast options in response to user getsockopt().
*/
int
ip_getmoptions(int optname, struct ip_moptions *imo, struct mbuf *m)
{
u_char *ttl;
u_char *loop;
struct in_addr *addr;
struct in_ifaddr *ia;
struct ifnet *ifp;
switch (optname) {
case IP_MULTICAST_IF:
addr = mtod(m, struct in_addr *);
m->m_len = sizeof(struct in_addr);
if (imo == NULL || (ifp = if_get(imo->imo_ifidx)) == NULL)
addr->s_addr = INADDR_ANY;
else {
IFP_TO_IA(ifp, ia);
addr->s_addr = (ia == NULL) ? INADDR_ANY
: ia->ia_addr.sin_addr.s_addr;
if_put(ifp);
}
return (0);
case IP_MULTICAST_TTL:
ttl = mtod(m, u_char *);
m->m_len = 1;
*ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL
: imo->imo_ttl;
return (0);
case IP_MULTICAST_LOOP:
loop = mtod(m, u_char *);
m->m_len = 1;
*loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP
: imo->imo_loop;
return (0);
default:
return (EOPNOTSUPP);
}
}
/*
* Discard the IP multicast options.
*/
void
ip_freemoptions(struct ip_moptions *imo)
{
int i;
if (imo != NULL) {
for (i = 0; i < imo->imo_num_memberships; ++i)
in_delmulti(imo->imo_membership[i]);
free(imo->imo_membership, M_IPMOPTS,
imo->imo_max_memberships * sizeof(struct in_multi *));
free(imo, M_IPMOPTS, sizeof(*imo));
}
}
/*
* Routine called from ip_output() to loop back a copy of an IP multicast
* packet to the input queue of a specified interface.
*/
void
ip_mloopback(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in *dst)
{
struct ip *ip;
struct mbuf *copym;
copym = m_dup_pkt(m, max_linkhdr, M_DONTWAIT);
if (copym != NULL) {
/*
* We don't bother to fragment if the IP length is greater
* than the interface's MTU. Can this possibly matter?
*/
ip = mtod(copym, struct ip *);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(copym, ip->ip_hl << 2);
if_input_local(ifp, copym, dst->sin_family);
}
}
/*
* Compute significant parts of the IPv4 checksum pseudo-header
* for use in a delayed TCP/UDP checksum calculation.
*/
static __inline u_int16_t __attribute__((__unused__))
in_cksum_phdr(u_int32_t src, u_int32_t dst, u_int32_t lenproto)
{
u_int32_t sum;
sum = lenproto +
(u_int16_t)(src >> 16) +
(u_int16_t)(src /*& 0xffff*/) +
(u_int16_t)(dst >> 16) +
(u_int16_t)(dst /*& 0xffff*/);
sum = (u_int16_t)(sum >> 16) + (u_int16_t)(sum /*& 0xffff*/);
if (sum > 0xffff)
sum -= 0xffff;
return (sum);
}
/*
* Process a delayed payload checksum calculation.
*/
void
in_delayed_cksum(struct mbuf *m)
{
struct ip *ip;
u_int16_t csum, offset;
ip = mtod(m, struct ip *);
offset = ip->ip_hl << 2;
csum = in4_cksum(m, 0, offset, m->m_pkthdr.len - offset);
if (csum == 0 && ip->ip_p == IPPROTO_UDP)
csum = 0xffff;
switch (ip->ip_p) {
case IPPROTO_TCP:
offset += offsetof(struct tcphdr, th_sum);
break;
case IPPROTO_UDP:
offset += offsetof(struct udphdr, uh_sum);
break;
case IPPROTO_ICMP:
offset += offsetof(struct icmp, icmp_cksum);
break;
default:
return;
}
if ((offset + sizeof(u_int16_t)) > m->m_len)
m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
else
*(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
}
void
in_proto_cksum_out(struct mbuf *m, struct ifnet *ifp)
{
struct ip *ip = mtod(m, struct ip *);
/* some hw and in_delayed_cksum need the pseudo header cksum */
if (m->m_pkthdr.csum_flags &
(M_TCP_CSUM_OUT|M_UDP_CSUM_OUT|M_ICMP_CSUM_OUT)) {
u_int16_t csum = 0, offset;
offset = ip->ip_hl << 2;
if (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT|M_UDP_CSUM_OUT))
csum = in_cksum_phdr(ip->ip_src.s_addr,
ip->ip_dst.s_addr, htonl(ntohs(ip->ip_len) -
offset + ip->ip_p));
if (ip->ip_p == IPPROTO_TCP)
offset += offsetof(struct tcphdr, th_sum);
else if (ip->ip_p == IPPROTO_UDP)
offset += offsetof(struct udphdr, uh_sum);
else if (ip->ip_p == IPPROTO_ICMP)
offset += offsetof(struct icmp, icmp_cksum);
if ((offset + sizeof(u_int16_t)) > m->m_len)
m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
else
*(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
}
if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
if (!in_ifcap_cksum(m, ifp, IFCAP_CSUM_TCPv4) ||
ip->ip_hl != 5) {
tcpstat_inc(tcps_outswcsum);
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
}
} else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
if (!in_ifcap_cksum(m, ifp, IFCAP_CSUM_UDPv4) ||
ip->ip_hl != 5) {
udpstat_inc(udps_outswcsum);
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
}
} else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) {
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */
}
}
int
in_ifcap_cksum(struct mbuf *m, struct ifnet *ifp, int ifcap)
{
if ((ifp == NULL) ||
!ISSET(ifp->if_capabilities, ifcap) ||
(ifp->if_bridgeidx != 0))
return (0);
/*
* Simplex interface sends packet back without hardware cksum.
* Keep this check in sync with the condition where ether_resolve()
* calls if_input_local().
*/
if (ISSET(m->m_flags, M_BCAST) &&
ISSET(ifp->if_flags, IFF_SIMPLEX) &&
!m->m_pkthdr.pf.routed)
return (0);
return (1);
}
45
36
2
38
38
2
1
4
8
8
1
7
8
8
8
45
36
10
58
48
7
5
4
8
8
1
7
3
4
3
3
2
28
2
28
28
28
38
38
5
1
1
4
4
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
/* $OpenBSD: uvm_aobj.c,v 1.107 2022/08/29 02:58:13 jsg Exp $ */
/* $NetBSD: uvm_aobj.c,v 1.39 2001/02/18 21:19:08 chs Exp $ */
/*
* Copyright (c) 1998 Chuck Silvers, Charles D. Cranor and
* Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* from: Id: uvm_aobj.c,v 1.1.2.5 1998/02/06 05:14:38 chs Exp
*/
/*
* uvm_aobj.c: anonymous memory uvm_object pager
*
* author: Chuck Silvers <chuq@chuq.com>
* started: Jan-1998
*
* - design mostly from Chuck Cranor
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/stdint.h>
#include <sys/atomic.h>
#include <uvm/uvm.h>
/*
* An anonymous UVM object (aobj) manages anonymous-memory. In addition to
* keeping the list of resident pages, it may also keep a list of allocated
* swap blocks. Depending on the size of the object, this list is either
* stored in an array (small objects) or in a hash table (large objects).
*/
/*
* Note: for hash tables, we break the address space of the aobj into blocks
* of UAO_SWHASH_CLUSTER_SIZE pages, which shall be a power of two.
*/
#define UAO_SWHASH_CLUSTER_SHIFT 4
#define UAO_SWHASH_CLUSTER_SIZE (1 << UAO_SWHASH_CLUSTER_SHIFT)
/* Get the "tag" for this page index. */
#define UAO_SWHASH_ELT_TAG(idx) ((idx) >> UAO_SWHASH_CLUSTER_SHIFT)
#define UAO_SWHASH_ELT_PAGESLOT_IDX(idx) \
((idx) & (UAO_SWHASH_CLUSTER_SIZE - 1))
/* Given an ELT and a page index, find the swap slot. */
#define UAO_SWHASH_ELT_PAGESLOT(elt, idx) \
((elt)->slots[UAO_SWHASH_ELT_PAGESLOT_IDX(idx)])
/* Given an ELT, return its pageidx base. */
#define UAO_SWHASH_ELT_PAGEIDX_BASE(elt) \
((elt)->tag << UAO_SWHASH_CLUSTER_SHIFT)
/* The hash function. */
#define UAO_SWHASH_HASH(aobj, idx) \
(&(aobj)->u_swhash[(((idx) >> UAO_SWHASH_CLUSTER_SHIFT) \
& (aobj)->u_swhashmask)])
/*
* The threshold which determines whether we will use an array or a
* hash table to store the list of allocated swap blocks.
*/
#define UAO_SWHASH_THRESHOLD (UAO_SWHASH_CLUSTER_SIZE * 4)
#define UAO_USES_SWHASH(aobj) \
((aobj)->u_pages > UAO_SWHASH_THRESHOLD)
/* The number of buckets in a hash, with an upper bound. */
#define UAO_SWHASH_MAXBUCKETS 256
#define UAO_SWHASH_BUCKETS(pages) \
(min((pages) >> UAO_SWHASH_CLUSTER_SHIFT, UAO_SWHASH_MAXBUCKETS))
/*
* uao_swhash_elt: when a hash table is being used, this structure defines
* the format of an entry in the bucket list.
*/
struct uao_swhash_elt {
LIST_ENTRY(uao_swhash_elt) list; /* the hash list */
voff_t tag; /* our 'tag' */
int count; /* our number of active slots */
int slots[UAO_SWHASH_CLUSTER_SIZE]; /* the slots */
};
/*
* uao_swhash: the swap hash table structure
*/
LIST_HEAD(uao_swhash, uao_swhash_elt);
/*
* uao_swhash_elt_pool: pool of uao_swhash_elt structures
*/
struct pool uao_swhash_elt_pool;
/*
* uvm_aobj: the actual anon-backed uvm_object
*
* => the uvm_object is at the top of the structure, this allows
* (struct uvm_aobj *) == (struct uvm_object *)
* => only one of u_swslots and u_swhash is used in any given aobj
*/
struct uvm_aobj {
struct uvm_object u_obj; /* has: pgops, memt, #pages, #refs */
int u_pages; /* number of pages in entire object */
int u_flags; /* the flags (see uvm_aobj.h) */
/*
* Either an array or hashtable (array of bucket heads) of
* offset -> swapslot mappings for the aobj.
*/
#define u_swslots u_swap.slot_array
#define u_swhash u_swap.slot_hash
union swslots {
int *slot_array;
struct uao_swhash *slot_hash;
} u_swap;
u_long u_swhashmask; /* mask for hashtable */
LIST_ENTRY(uvm_aobj) u_list; /* global list of aobjs */
};
struct pool uvm_aobj_pool;
static struct uao_swhash_elt *uao_find_swhash_elt(struct uvm_aobj *, int,
boolean_t);
static int uao_find_swslot(struct uvm_object *, int);
static boolean_t uao_flush(struct uvm_object *, voff_t,
voff_t, int);
static void uao_free(struct uvm_aobj *);
static int uao_get(struct uvm_object *, voff_t,
vm_page_t *, int *, int, vm_prot_t,
int, int);
static boolean_t uao_pagein(struct uvm_aobj *, int, int);
static boolean_t uao_pagein_page(struct uvm_aobj *, int);
void uao_dropswap_range(struct uvm_object *, voff_t, voff_t);
void uao_shrink_flush(struct uvm_object *, int, int);
int uao_shrink_hash(struct uvm_object *, int);
int uao_shrink_array(struct uvm_object *, int);
int uao_shrink_convert(struct uvm_object *, int);
int uao_grow_hash(struct uvm_object *, int);
int uao_grow_array(struct uvm_object *, int);
int uao_grow_convert(struct uvm_object *, int);
/*
* aobj_pager
*
* note that some functions (e.g. put) are handled elsewhere
*/
const struct uvm_pagerops aobj_pager = {
.pgo_reference = uao_reference,
.pgo_detach = uao_detach,
.pgo_flush = uao_flush,
.pgo_get = uao_get,
};
/*
* uao_list: global list of active aobjs, locked by uao_list_lock
*
* Lock ordering: generally the locking order is object lock, then list lock.
* in the case of swap off we have to iterate over the list, and thus the
* ordering is reversed. In that case we must use trylocking to prevent
* deadlock.
*/
static LIST_HEAD(aobjlist, uvm_aobj) uao_list = LIST_HEAD_INITIALIZER(uao_list);
static struct mutex uao_list_lock = MUTEX_INITIALIZER(IPL_MPFLOOR);
/*
* functions
*/
/*
* hash table/array related functions
*/
/*
* uao_find_swhash_elt: find (or create) a hash table entry for a page
* offset.
*/
static struct uao_swhash_elt *
uao_find_swhash_elt(struct uvm_aobj *aobj, int pageidx, boolean_t create)
{
struct uao_swhash *swhash;
struct uao_swhash_elt *elt;
voff_t page_tag;
swhash = UAO_SWHASH_HASH(aobj, pageidx); /* first hash to get bucket */
page_tag = UAO_SWHASH_ELT_TAG(pageidx); /* tag to search for */
/*
* now search the bucket for the requested tag
*/
LIST_FOREACH(elt, swhash, list) {
if (elt->tag == page_tag)
return elt;
}
if (!create)
return NULL;
/*
* allocate a new entry for the bucket and init/insert it in
*/
elt = pool_get(&uao_swhash_elt_pool, PR_NOWAIT | PR_ZERO);
/*
* XXX We cannot sleep here as the hash table might disappear
* from under our feet. And we run the risk of deadlocking
* the pagedeamon. In fact this code will only be called by
* the pagedaemon and allocation will only fail if we
* exhausted the pagedeamon reserve. In that case we're
* doomed anyway, so panic.
*/
if (elt == NULL)
panic("%s: can't allocate entry", __func__);
LIST_INSERT_HEAD(swhash, elt, list);
elt->tag = page_tag;
return elt;
}
/*
* uao_find_swslot: find the swap slot number for an aobj/pageidx
*/
static inline int
uao_find_swslot(struct uvm_object *uobj, int pageidx)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
/*
* if noswap flag is set, then we never return a slot
*/
if (aobj->u_flags & UAO_FLAG_NOSWAP)
return 0;
/*
* if hashing, look in hash table.
*/
if (UAO_USES_SWHASH(aobj)) {
struct uao_swhash_elt *elt =
uao_find_swhash_elt(aobj, pageidx, FALSE);
if (elt)
return UAO_SWHASH_ELT_PAGESLOT(elt, pageidx);
else
return 0;
}
/*
* otherwise, look in the array
*/
return aobj->u_swslots[pageidx];
}
/*
* uao_set_swslot: set the swap slot for a page in an aobj.
*
* => setting a slot to zero frees the slot
* => object must be locked by caller
* => we return the old slot number, or -1 if we failed to allocate
* memory to record the new slot number
*/
int
uao_set_swslot(struct uvm_object *uobj, int pageidx, int slot)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
int oldslot;
KASSERT(rw_write_held(uobj->vmobjlock) || uobj->uo_refs == 0);
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
/*
* if noswap flag is set, then we can't set a slot
*/
if (aobj->u_flags & UAO_FLAG_NOSWAP) {
if (slot == 0)
return 0; /* a clear is ok */
/* but a set is not */
printf("uao_set_swslot: uobj = %p\n", uobj);
panic("uao_set_swslot: attempt to set a slot on a NOSWAP object");
}
/*
* are we using a hash table? if so, add it in the hash.
*/
if (UAO_USES_SWHASH(aobj)) {
/*
* Avoid allocating an entry just to free it again if
* the page had not swap slot in the first place, and
* we are freeing.
*/
struct uao_swhash_elt *elt =
uao_find_swhash_elt(aobj, pageidx, slot ? TRUE : FALSE);
if (elt == NULL) {
KASSERT(slot == 0);
return 0;
}
oldslot = UAO_SWHASH_ELT_PAGESLOT(elt, pageidx);
UAO_SWHASH_ELT_PAGESLOT(elt, pageidx) = slot;
/*
* now adjust the elt's reference counter and free it if we've
* dropped it to zero.
*/
if (slot) {
if (oldslot == 0)
elt->count++;
} else {
if (oldslot)
elt->count--;
if (elt->count == 0) {
LIST_REMOVE(elt, list);
pool_put(&uao_swhash_elt_pool, elt);
}
}
} else {
/* we are using an array */
oldslot = aobj->u_swslots[pageidx];
aobj->u_swslots[pageidx] = slot;
}
return oldslot;
}
/*
* end of hash/array functions
*/
/*
* uao_free: free all resources held by an aobj, and then free the aobj
*
* => the aobj should be dead
*/
static void
uao_free(struct uvm_aobj *aobj)
{
struct uvm_object *uobj = &aobj->u_obj;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
KASSERT(rw_write_held(uobj->vmobjlock));
uao_dropswap_range(uobj, 0, 0);
rw_exit(uobj->vmobjlock);
if (UAO_USES_SWHASH(aobj)) {
/*
* free the hash table itself.
*/
hashfree(aobj->u_swhash, UAO_SWHASH_BUCKETS(aobj->u_pages), M_UVMAOBJ);
} else {
free(aobj->u_swslots, M_UVMAOBJ, aobj->u_pages * sizeof(int));
}
/*
* finally free the aobj itself
*/
uvm_obj_destroy(uobj);
pool_put(&uvm_aobj_pool, aobj);
}
/*
* pager functions
*/
#ifdef TMPFS
/*
* Shrink an aobj to a given number of pages. The procedure is always the same:
* assess the necessity of data structure conversion (hash to array), secure
* resources, flush pages and drop swap slots.
*
*/
void
uao_shrink_flush(struct uvm_object *uobj, int startpg, int endpg)
{
KASSERT(startpg < endpg);
KASSERT(uobj->uo_refs == 1);
uao_flush(uobj, (voff_t)startpg << PAGE_SHIFT,
(voff_t)endpg << PAGE_SHIFT, PGO_FREE);
uao_dropswap_range(uobj, startpg, endpg);
}
int
uao_shrink_hash(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
struct uao_swhash *new_swhash;
struct uao_swhash_elt *elt;
unsigned long new_hashmask;
int i;
KASSERT(UAO_USES_SWHASH(aobj));
/*
* If the size of the hash table doesn't change, all we need to do is
* to adjust the page count.
*/
if (UAO_SWHASH_BUCKETS(aobj->u_pages) == UAO_SWHASH_BUCKETS(pages)) {
uao_shrink_flush(uobj, pages, aobj->u_pages);
aobj->u_pages = pages;
return 0;
}
new_swhash = hashinit(UAO_SWHASH_BUCKETS(pages), M_UVMAOBJ,
M_WAITOK | M_CANFAIL, &new_hashmask);
if (new_swhash == NULL)
return ENOMEM;
uao_shrink_flush(uobj, pages, aobj->u_pages);
/*
* Even though the hash table size is changing, the hash of the buckets
* we are interested in copying should not change.
*/
for (i = 0; i < UAO_SWHASH_BUCKETS(aobj->u_pages); i++) {
while (LIST_EMPTY(&aobj->u_swhash[i]) == 0) {
elt = LIST_FIRST(&aobj->u_swhash[i]);
LIST_REMOVE(elt, list);
LIST_INSERT_HEAD(&new_swhash[i], elt, list);
}
}
hashfree(aobj->u_swhash, UAO_SWHASH_BUCKETS(aobj->u_pages), M_UVMAOBJ);
aobj->u_swhash = new_swhash;
aobj->u_pages = pages;
aobj->u_swhashmask = new_hashmask;
return 0;
}
int
uao_shrink_convert(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
struct uao_swhash_elt *elt;
int i, *new_swslots;
new_swslots = mallocarray(pages, sizeof(int), M_UVMAOBJ,
M_WAITOK | M_CANFAIL | M_ZERO);
if (new_swslots == NULL)
return ENOMEM;
uao_shrink_flush(uobj, pages, aobj->u_pages);
/* Convert swap slots from hash to array. */
for (i = 0; i < pages; i++) {
elt = uao_find_swhash_elt(aobj, i, FALSE);
if (elt != NULL) {
new_swslots[i] = UAO_SWHASH_ELT_PAGESLOT(elt, i);
if (new_swslots[i] != 0)
elt->count--;
if (elt->count == 0) {
LIST_REMOVE(elt, list);
pool_put(&uao_swhash_elt_pool, elt);
}
}
}
hashfree(aobj->u_swhash, UAO_SWHASH_BUCKETS(aobj->u_pages), M_UVMAOBJ);
aobj->u_swslots = new_swslots;
aobj->u_pages = pages;
return 0;
}
int
uao_shrink_array(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
int i, *new_swslots;
new_swslots = mallocarray(pages, sizeof(int), M_UVMAOBJ,
M_WAITOK | M_CANFAIL | M_ZERO);
if (new_swslots == NULL)
return ENOMEM;
uao_shrink_flush(uobj, pages, aobj->u_pages);
for (i = 0; i < pages; i++)
new_swslots[i] = aobj->u_swslots[i];
free(aobj->u_swslots, M_UVMAOBJ, aobj->u_pages * sizeof(int));
aobj->u_swslots = new_swslots;
aobj->u_pages = pages;
return 0;
}
int
uao_shrink(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
KASSERT(pages < aobj->u_pages);
/*
* Distinguish between three possible cases:
* 1. aobj uses hash and must be converted to array.
* 2. aobj uses array and array size needs to be adjusted.
* 3. aobj uses hash and hash size needs to be adjusted.
*/
if (pages > UAO_SWHASH_THRESHOLD)
return uao_shrink_hash(uobj, pages); /* case 3 */
else if (aobj->u_pages > UAO_SWHASH_THRESHOLD)
return uao_shrink_convert(uobj, pages); /* case 1 */
else
return uao_shrink_array(uobj, pages); /* case 2 */
}
/*
* Grow an aobj to a given number of pages. Right now we only adjust the swap
* slots. We could additionally handle page allocation directly, so that they
* don't happen through uvm_fault(). That would allow us to use another
* mechanism for the swap slots other than malloc(). It is thus mandatory that
* the caller of these functions does not allow faults to happen in case of
* growth error.
*/
int
uao_grow_array(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
int i, *new_swslots;
KASSERT(aobj->u_pages <= UAO_SWHASH_THRESHOLD);
new_swslots = mallocarray(pages, sizeof(int), M_UVMAOBJ,
M_WAITOK | M_CANFAIL | M_ZERO);
if (new_swslots == NULL)
return ENOMEM;
for (i = 0; i < aobj->u_pages; i++)
new_swslots[i] = aobj->u_swslots[i];
free(aobj->u_swslots, M_UVMAOBJ, aobj->u_pages * sizeof(int));
aobj->u_swslots = new_swslots;
aobj->u_pages = pages;
return 0;
}
int
uao_grow_hash(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
struct uao_swhash *new_swhash;
struct uao_swhash_elt *elt;
unsigned long new_hashmask;
int i;
KASSERT(pages > UAO_SWHASH_THRESHOLD);
/*
* If the size of the hash table doesn't change, all we need to do is
* to adjust the page count.
*/
if (UAO_SWHASH_BUCKETS(aobj->u_pages) == UAO_SWHASH_BUCKETS(pages)) {
aobj->u_pages = pages;
return 0;
}
KASSERT(UAO_SWHASH_BUCKETS(aobj->u_pages) < UAO_SWHASH_BUCKETS(pages));
new_swhash = hashinit(UAO_SWHASH_BUCKETS(pages), M_UVMAOBJ,
M_WAITOK | M_CANFAIL, &new_hashmask);
if (new_swhash == NULL)
return ENOMEM;
for (i = 0; i < UAO_SWHASH_BUCKETS(aobj->u_pages); i++) {
while (LIST_EMPTY(&aobj->u_swhash[i]) == 0) {
elt = LIST_FIRST(&aobj->u_swhash[i]);
LIST_REMOVE(elt, list);
LIST_INSERT_HEAD(&new_swhash[i], elt, list);
}
}
hashfree(aobj->u_swhash, UAO_SWHASH_BUCKETS(aobj->u_pages), M_UVMAOBJ);
aobj->u_swhash = new_swhash;
aobj->u_pages = pages;
aobj->u_swhashmask = new_hashmask;
return 0;
}
int
uao_grow_convert(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
struct uao_swhash *new_swhash;
struct uao_swhash_elt *elt;
unsigned long new_hashmask;
int i, *old_swslots;
new_swhash = hashinit(UAO_SWHASH_BUCKETS(pages), M_UVMAOBJ,
M_WAITOK | M_CANFAIL, &new_hashmask);
if (new_swhash == NULL)
return ENOMEM;
/* Set these now, so we can use uao_find_swhash_elt(). */
old_swslots = aobj->u_swslots;
aobj->u_swhash = new_swhash;
aobj->u_swhashmask = new_hashmask;
for (i = 0; i < aobj->u_pages; i++) {
if (old_swslots[i] != 0) {
elt = uao_find_swhash_elt(aobj, i, TRUE);
elt->count++;
UAO_SWHASH_ELT_PAGESLOT(elt, i) = old_swslots[i];
}
}
free(old_swslots, M_UVMAOBJ, aobj->u_pages * sizeof(int));
aobj->u_pages = pages;
return 0;
}
int
uao_grow(struct uvm_object *uobj, int pages)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
KASSERT(pages > aobj->u_pages);
/*
* Distinguish between three possible cases:
* 1. aobj uses hash and hash size needs to be adjusted.
* 2. aobj uses array and array size needs to be adjusted.
* 3. aobj uses array and must be converted to hash.
*/
if (pages <= UAO_SWHASH_THRESHOLD)
return uao_grow_array(uobj, pages); /* case 2 */
else if (aobj->u_pages > UAO_SWHASH_THRESHOLD)
return uao_grow_hash(uobj, pages); /* case 1 */
else
return uao_grow_convert(uobj, pages);
}
#endif /* TMPFS */
/*
* uao_create: create an aobj of the given size and return its uvm_object.
*
* => for normal use, flags are zero or UAO_FLAG_CANFAIL.
* => for the kernel object, the flags are:
* UAO_FLAG_KERNOBJ - allocate the kernel object (can only happen once)
* UAO_FLAG_KERNSWAP - enable swapping of kernel object (" ")
*/
struct uvm_object *
uao_create(vsize_t size, int flags)
{
static struct uvm_aobj kernel_object_store;
static struct rwlock bootstrap_kernel_object_lock;
static int kobj_alloced = 0;
int pages = round_page(size) >> PAGE_SHIFT;
struct uvm_aobj *aobj;
int refs;
/*
* Allocate a new aobj, unless kernel object is requested.
*/
if (flags & UAO_FLAG_KERNOBJ) {
KASSERT(!kobj_alloced);
aobj = &kernel_object_store;
aobj->u_pages = pages;
aobj->u_flags = UAO_FLAG_NOSWAP;
refs = UVM_OBJ_KERN;
kobj_alloced = UAO_FLAG_KERNOBJ;
} else if (flags & UAO_FLAG_KERNSWAP) {
KASSERT(kobj_alloced == UAO_FLAG_KERNOBJ);
aobj = &kernel_object_store;
kobj_alloced = UAO_FLAG_KERNSWAP;
} else {
aobj = pool_get(&uvm_aobj_pool, PR_WAITOK);
aobj->u_pages = pages;
aobj->u_flags = 0;
refs = 1;
}
/*
* allocate hash/array if necessary
*/
if (flags == 0 || (flags & (UAO_FLAG_KERNSWAP | UAO_FLAG_CANFAIL))) {
int mflags;
if (flags)
mflags = M_NOWAIT;
else
mflags = M_WAITOK;
/* allocate hash table or array depending on object size */
if (UAO_USES_SWHASH(aobj)) {
aobj->u_swhash = hashinit(UAO_SWHASH_BUCKETS(pages),
M_UVMAOBJ, mflags, &aobj->u_swhashmask);
if (aobj->u_swhash == NULL) {
if (flags & UAO_FLAG_CANFAIL) {
pool_put(&uvm_aobj_pool, aobj);
return NULL;
}
panic("uao_create: hashinit swhash failed");
}
} else {
aobj->u_swslots = mallocarray(pages, sizeof(int),
M_UVMAOBJ, mflags|M_ZERO);
if (aobj->u_swslots == NULL) {
if (flags & UAO_FLAG_CANFAIL) {
pool_put(&uvm_aobj_pool, aobj);
return NULL;
}
panic("uao_create: malloc swslots failed");
}
}
if (flags & UAO_FLAG_KERNSWAP) {
aobj->u_flags &= ~UAO_FLAG_NOSWAP; /* clear noswap */
return &aobj->u_obj;
/* done! */
}
}
/*
* Initialise UVM object.
*/
uvm_obj_init(&aobj->u_obj, &aobj_pager, refs);
if (flags & UAO_FLAG_KERNOBJ) {
/* Use a temporary static lock for kernel_object. */
rw_init(&bootstrap_kernel_object_lock, "kobjlk");
uvm_obj_setlock(&aobj->u_obj, &bootstrap_kernel_object_lock);
}
/*
* now that aobj is ready, add it to the global list
*/
mtx_enter(&uao_list_lock);
LIST_INSERT_HEAD(&uao_list, aobj, u_list);
mtx_leave(&uao_list_lock);
return &aobj->u_obj;
}
/*
* uao_init: set up aobj pager subsystem
*
* => called at boot time from uvm_pager_init()
*/
void
uao_init(void)
{
/*
* NOTE: Pages for this pool must not come from a pageable
* kernel map!
*/
pool_init(&uao_swhash_elt_pool, sizeof(struct uao_swhash_elt), 0,
IPL_NONE, PR_WAITOK, "uaoeltpl", NULL);
pool_init(&uvm_aobj_pool, sizeof(struct uvm_aobj), 0,
IPL_NONE, PR_WAITOK, "aobjpl", NULL);
}
/*
* uao_reference: hold a reference to an anonymous UVM object.
*/
void
uao_reference(struct uvm_object *uobj)
{
/* Kernel object is persistent. */
if (UVM_OBJ_IS_KERN_OBJECT(uobj))
return;
atomic_inc_int(&uobj->uo_refs);
}
/*
* uao_detach: drop a reference to an anonymous UVM object.
*/
void
uao_detach(struct uvm_object *uobj)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
struct vm_page *pg;
/*
* Detaching from kernel_object is a NOP.
*/
if (UVM_OBJ_IS_KERN_OBJECT(uobj))
return;
/*
* Drop the reference. If it was the last one, destroy the object.
*/
if (atomic_dec_int_nv(&uobj->uo_refs) > 0) {
return;
}
/*
* Remove the aobj from the global list.
*/
mtx_enter(&uao_list_lock);
LIST_REMOVE(aobj, u_list);
mtx_leave(&uao_list_lock);
/*
* Free all the pages left in the aobj. For each page, when the
* page is no longer busy (and thus after any disk I/O that it is
* involved in is complete), release any swap resources and free
* the page itself.
*/
rw_enter(uobj->vmobjlock, RW_WRITE);
while ((pg = RBT_ROOT(uvm_objtree, &uobj->memt)) != NULL) {
pmap_page_protect(pg, PROT_NONE);
if (pg->pg_flags & PG_BUSY) {
uvm_pagewait(pg, uobj->vmobjlock, "uao_det");
rw_enter(uobj->vmobjlock, RW_WRITE);
continue;
}
uao_dropswap(&aobj->u_obj, pg->offset >> PAGE_SHIFT);
uvm_lock_pageq();
uvm_pagefree(pg);
uvm_unlock_pageq();
}
/*
* Finally, free the anonymous UVM object itself.
*/
uao_free(aobj);
}
/*
* uao_flush: flush pages out of a uvm object
*
* => if PGO_CLEANIT is not set, then we will not block.
* => if PGO_ALLPAGE is set, then all pages in the object are valid targets
* for flushing.
* => NOTE: we are allowed to lock the page queues, so the caller
* must not be holding the lock on them [e.g. pagedaemon had
* better not call us with the queues locked]
* => we return TRUE unless we encountered some sort of I/O error
* XXXJRT currently never happens, as we never directly initiate
* XXXJRT I/O
*/
boolean_t
uao_flush(struct uvm_object *uobj, voff_t start, voff_t stop, int flags)
{
struct uvm_aobj *aobj = (struct uvm_aobj *) uobj;
struct vm_page *pg;
voff_t curoff;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
KASSERT(rw_write_held(uobj->vmobjlock));
if (flags & PGO_ALLPAGES) {
start = 0;
stop = (voff_t)aobj->u_pages << PAGE_SHIFT;
} else {
start = trunc_page(start);
stop = round_page(stop);
if (stop > ((voff_t)aobj->u_pages << PAGE_SHIFT)) {
printf("uao_flush: strange, got an out of range "
"flush (fixed)\n");
stop = (voff_t)aobj->u_pages << PAGE_SHIFT;
}
}
/*
* Don't need to do any work here if we're not freeing
* or deactivating pages.
*/
if ((flags & (PGO_DEACTIVATE|PGO_FREE)) == 0) {
return TRUE;
}
curoff = start;
for (;;) {
if (curoff < stop) {
pg = uvm_pagelookup(uobj, curoff);
curoff += PAGE_SIZE;
if (pg == NULL)
continue;
} else {
break;
}
/* Make sure page is unbusy, else wait for it. */
if (pg->pg_flags & PG_BUSY) {
uvm_pagewait(pg, uobj->vmobjlock, "uaoflsh");
rw_enter(uobj->vmobjlock, RW_WRITE);
curoff -= PAGE_SIZE;
continue;
}
switch (flags & (PGO_CLEANIT|PGO_FREE|PGO_DEACTIVATE)) {
/*
* XXX In these first 3 cases, we always just
* XXX deactivate the page. We may want to
* XXX handle the different cases more specifically
* XXX in the future.
*/
case PGO_CLEANIT|PGO_FREE:
/* FALLTHROUGH */
case PGO_CLEANIT|PGO_DEACTIVATE:
/* FALLTHROUGH */
case PGO_DEACTIVATE:
deactivate_it:
if (pg->wire_count != 0)
continue;
uvm_lock_pageq();
pmap_page_protect(pg, PROT_NONE);
uvm_pagedeactivate(pg);
uvm_unlock_pageq();
continue;
case PGO_FREE:
/*
* If there are multiple references to
* the object, just deactivate the page.
*/
if (uobj->uo_refs > 1)
goto deactivate_it;
/* XXX skip the page if it's wired */
if (pg->wire_count != 0)
continue;
/*
* free the swap slot and the page.
*/
pmap_page_protect(pg, PROT_NONE);
/*
* freeing swapslot here is not strictly necessary.
* however, leaving it here doesn't save much
* because we need to update swap accounting anyway.
*/
uao_dropswap(uobj, pg->offset >> PAGE_SHIFT);
uvm_lock_pageq();
uvm_pagefree(pg);
uvm_unlock_pageq();
continue;
default:
panic("uao_flush: weird flags");
}
}
return TRUE;
}
/*
* uao_get: fetch me a page
*
* we have three cases:
* 1: page is resident -> just return the page.
* 2: page is zero-fill -> allocate a new page and zero it.
* 3: page is swapped out -> fetch the page from swap.
*
* cases 1 can be handled with PGO_LOCKED, cases 2 and 3 cannot.
* so, if the "center" page hits case 3 (or any page, with PGO_ALLPAGES),
* then we will need to return VM_PAGER_UNLOCK.
*
* => flags: PGO_ALLPAGES: get all of the pages
* PGO_LOCKED: fault data structures are locked
* => NOTE: offset is the offset of pps[0], _NOT_ pps[centeridx]
* => NOTE: caller must check for released pages!!
*/
static int
uao_get(struct uvm_object *uobj, voff_t offset, struct vm_page **pps,
int *npagesp, int centeridx, vm_prot_t access_type, int advice, int flags)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
voff_t current_offset;
vm_page_t ptmp;
int lcv, gotpages, maxpages, swslot, rv, pageidx;
boolean_t done;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
KASSERT(rw_write_held(uobj->vmobjlock));
/*
* get number of pages
*/
maxpages = *npagesp;
if (flags & PGO_LOCKED) {
/*
* step 1a: get pages that are already resident. only do
* this if the data structures are locked (i.e. the first
* time through).
*/
done = TRUE; /* be optimistic */
gotpages = 0; /* # of pages we got so far */
for (lcv = 0, current_offset = offset ; lcv < maxpages ;
lcv++, current_offset += PAGE_SIZE) {
/* do we care about this page? if not, skip it */
if (pps[lcv] == PGO_DONTCARE)
continue;
ptmp = uvm_pagelookup(uobj, current_offset);
/*
* if page is new, attempt to allocate the page,
* zero-fill'd.
*/
if (ptmp == NULL && uao_find_swslot(uobj,
current_offset >> PAGE_SHIFT) == 0) {
ptmp = uvm_pagealloc(uobj, current_offset,
NULL, UVM_PGA_ZERO);
if (ptmp) {
/* new page */
atomic_clearbits_int(&ptmp->pg_flags,
PG_BUSY|PG_FAKE);
atomic_setbits_int(&ptmp->pg_flags,
PQ_AOBJ);
UVM_PAGE_OWN(ptmp, NULL);
}
}
/*
* to be useful must get a non-busy page
*/
if (ptmp == NULL ||
(ptmp->pg_flags & PG_BUSY) != 0) {
if (lcv == centeridx ||
(flags & PGO_ALLPAGES) != 0)
/* need to do a wait or I/O! */
done = FALSE;
continue;
}
/*
* useful page: plug it in our result array
*/
atomic_setbits_int(&ptmp->pg_flags, PG_BUSY);
UVM_PAGE_OWN(ptmp, "uao_get1");
pps[lcv] = ptmp;
gotpages++;
}
/*
* step 1b: now we've either done everything needed or we
* to unlock and do some waiting or I/O.
*/
*npagesp = gotpages;
if (done)
/* bingo! */
return VM_PAGER_OK;
else
/* EEK! Need to unlock and I/O */
return VM_PAGER_UNLOCK;
}
/*
* step 2: get non-resident or busy pages.
* data structures are unlocked.
*/
for (lcv = 0, current_offset = offset ; lcv < maxpages ;
lcv++, current_offset += PAGE_SIZE) {
/*
* - skip over pages we've already gotten or don't want
* - skip over pages we don't _have_ to get
*/
if (pps[lcv] != NULL ||
(lcv != centeridx && (flags & PGO_ALLPAGES) == 0))
continue;
pageidx = current_offset >> PAGE_SHIFT;
/*
* we have yet to locate the current page (pps[lcv]). we
* first look for a page that is already at the current offset.
* if we find a page, we check to see if it is busy or
* released. if that is the case, then we sleep on the page
* until it is no longer busy or released and repeat the lookup.
* if the page we found is neither busy nor released, then we
* busy it (so we own it) and plug it into pps[lcv]. this
* 'break's the following while loop and indicates we are
* ready to move on to the next page in the "lcv" loop above.
*
* if we exit the while loop with pps[lcv] still set to NULL,
* then it means that we allocated a new busy/fake/clean page
* ptmp in the object and we need to do I/O to fill in the data.
*/
/* top of "pps" while loop */
while (pps[lcv] == NULL) {
/* look for a resident page */
ptmp = uvm_pagelookup(uobj, current_offset);
/* not resident? allocate one now (if we can) */
if (ptmp == NULL) {
ptmp = uvm_pagealloc(uobj, current_offset,
NULL, 0);
/* out of RAM? */
if (ptmp == NULL) {
rw_exit(uobj->vmobjlock);
uvm_wait("uao_getpage");
rw_enter(uobj->vmobjlock, RW_WRITE);
/* goto top of pps while loop */
continue;
}
/*
* safe with PQ's unlocked: because we just
* alloc'd the page
*/
atomic_setbits_int(&ptmp->pg_flags, PQ_AOBJ);
/*
* got new page ready for I/O. break pps while
* loop. pps[lcv] is still NULL.
*/
break;
}
/* page is there, see if we need to wait on it */
if ((ptmp->pg_flags & PG_BUSY) != 0) {
uvm_pagewait(ptmp, uobj->vmobjlock, "uao_get");
rw_enter(uobj->vmobjlock, RW_WRITE);
continue; /* goto top of pps while loop */
}
/*
* if we get here then the page is resident and
* unbusy. we busy it now (so we own it).
*/
/* we own it, caller must un-busy */
atomic_setbits_int(&ptmp->pg_flags, PG_BUSY);
UVM_PAGE_OWN(ptmp, "uao_get2");
pps[lcv] = ptmp;
}
/*
* if we own the valid page at the correct offset, pps[lcv] will
* point to it. nothing more to do except go to the next page.
*/
if (pps[lcv])
continue; /* next lcv */
/*
* we have a "fake/busy/clean" page that we just allocated.
* do the needed "i/o", either reading from swap or zeroing.
*/
swslot = uao_find_swslot(uobj, pageidx);
/* just zero the page if there's nothing in swap. */
if (swslot == 0) {
/* page hasn't existed before, just zero it. */
uvm_pagezero(ptmp);
} else {
/*
* page in the swapped-out page.
* unlock object for i/o, relock when done.
*/
rw_exit(uobj->vmobjlock);
rv = uvm_swap_get(ptmp, swslot, PGO_SYNCIO);
rw_enter(uobj->vmobjlock, RW_WRITE);
/*
* I/O done. check for errors.
*/
if (rv != VM_PAGER_OK) {
/*
* remove the swap slot from the aobj
* and mark the aobj as having no real slot.
* don't free the swap slot, thus preventing
* it from being used again.
*/
swslot = uao_set_swslot(&aobj->u_obj, pageidx,
SWSLOT_BAD);
uvm_swap_markbad(swslot, 1);
if (ptmp->pg_flags & PG_WANTED)
wakeup(ptmp);
atomic_clearbits_int(&ptmp->pg_flags,
PG_WANTED|PG_BUSY);
UVM_PAGE_OWN(ptmp, NULL);
uvm_lock_pageq();
uvm_pagefree(ptmp);
uvm_unlock_pageq();
rw_exit(uobj->vmobjlock);
return rv;
}
}
/*
* we got the page! clear the fake flag (indicates valid
* data now in page) and plug into our result array. note
* that page is still busy.
*
* it is the callers job to:
* => check if the page is released
* => unbusy the page
* => activate the page
*/
atomic_clearbits_int(&ptmp->pg_flags, PG_FAKE);
pmap_clear_modify(ptmp); /* ... and clean */
pps[lcv] = ptmp;
} /* lcv loop */
rw_exit(uobj->vmobjlock);
return VM_PAGER_OK;
}
/*
* uao_dropswap: release any swap resources from this aobj page.
*
* => aobj must be locked or have a reference count of 0.
*/
int
uao_dropswap(struct uvm_object *uobj, int pageidx)
{
int slot;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
slot = uao_set_swslot(uobj, pageidx, 0);
if (slot) {
uvm_swap_free(slot, 1);
}
return slot;
}
/*
* page in every page in every aobj that is paged-out to a range of swslots.
*
* => aobj must be locked and is returned locked.
* => returns TRUE if pagein was aborted due to lack of memory.
*/
boolean_t
uao_swap_off(int startslot, int endslot)
{
struct uvm_aobj *aobj;
/*
* Walk the list of all anonymous UVM objects. Grab the first.
*/
mtx_enter(&uao_list_lock);
if ((aobj = LIST_FIRST(&uao_list)) == NULL) {
mtx_leave(&uao_list_lock);
return FALSE;
}
uao_reference(&aobj->u_obj);
do {
struct uvm_aobj *nextaobj;
boolean_t rv;
/*
* Prefetch the next object and immediately hold a reference
* on it, so neither the current nor the next entry could
* disappear while we are iterating.
*/
if ((nextaobj = LIST_NEXT(aobj, u_list)) != NULL) {
uao_reference(&nextaobj->u_obj);
}
mtx_leave(&uao_list_lock);
/*
* Page in all pages in the swap slot range.
*/
rw_enter(aobj->u_obj.vmobjlock, RW_WRITE);
rv = uao_pagein(aobj, startslot, endslot);
rw_exit(aobj->u_obj.vmobjlock);
/* Drop the reference of the current object. */
uao_detach(&aobj->u_obj);
if (rv) {
if (nextaobj) {
uao_detach(&nextaobj->u_obj);
}
return rv;
}
aobj = nextaobj;
mtx_enter(&uao_list_lock);
} while (aobj);
/*
* done with traversal, unlock the list
*/
mtx_leave(&uao_list_lock);
return FALSE;
}
/*
* page in any pages from aobj in the given range.
*
* => returns TRUE if pagein was aborted due to lack of memory.
*/
static boolean_t
uao_pagein(struct uvm_aobj *aobj, int startslot, int endslot)
{
boolean_t rv;
if (UAO_USES_SWHASH(aobj)) {
struct uao_swhash_elt *elt;
int bucket;
restart:
for (bucket = aobj->u_swhashmask; bucket >= 0; bucket--) {
for (elt = LIST_FIRST(&aobj->u_swhash[bucket]);
elt != NULL;
elt = LIST_NEXT(elt, list)) {
int i;
for (i = 0; i < UAO_SWHASH_CLUSTER_SIZE; i++) {
int slot = elt->slots[i];
/*
* if the slot isn't in range, skip it.
*/
if (slot < startslot ||
slot >= endslot) {
continue;
}
/*
* process the page,
* the start over on this object
* since the swhash elt
* may have been freed.
*/
rv = uao_pagein_page(aobj,
UAO_SWHASH_ELT_PAGEIDX_BASE(elt) + i);
if (rv) {
return rv;
}
goto restart;
}
}
}
} else {
int i;
for (i = 0; i < aobj->u_pages; i++) {
int slot = aobj->u_swslots[i];
/*
* if the slot isn't in range, skip it
*/
if (slot < startslot || slot >= endslot) {
continue;
}
/*
* process the page.
*/
rv = uao_pagein_page(aobj, i);
if (rv) {
return rv;
}
}
}
return FALSE;
}
/*
* uao_pagein_page: page in a single page from an anonymous UVM object.
*
* => Returns TRUE if pagein was aborted due to lack of memory.
*/
static boolean_t
uao_pagein_page(struct uvm_aobj *aobj, int pageidx)
{
struct uvm_object *uobj = &aobj->u_obj;
struct vm_page *pg;
int rv, slot, npages;
pg = NULL;
npages = 1;
KASSERT(rw_write_held(uobj->vmobjlock));
rv = uao_get(&aobj->u_obj, (voff_t)pageidx << PAGE_SHIFT,
&pg, &npages, 0, PROT_READ | PROT_WRITE, 0, 0);
/*
* relock and finish up.
*/
rw_enter(uobj->vmobjlock, RW_WRITE);
switch (rv) {
case VM_PAGER_OK:
break;
case VM_PAGER_ERROR:
case VM_PAGER_REFAULT:
/*
* nothing more to do on errors.
* VM_PAGER_REFAULT can only mean that the anon was freed,
* so again there's nothing to do.
*/
return FALSE;
}
/*
* ok, we've got the page now.
* mark it as dirty, clear its swslot and un-busy it.
*/
slot = uao_set_swslot(&aobj->u_obj, pageidx, 0);
uvm_swap_free(slot, 1);
atomic_clearbits_int(&pg->pg_flags, PG_BUSY|PG_CLEAN|PG_FAKE);
UVM_PAGE_OWN(pg, NULL);
/*
* deactivate the page (to put it on a page queue).
*/
pmap_clear_reference(pg);
uvm_lock_pageq();
uvm_pagedeactivate(pg);
uvm_unlock_pageq();
return FALSE;
}
/*
* uao_dropswap_range: drop swapslots in the range.
*
* => aobj must be locked and is returned locked.
* => start is inclusive. end is exclusive.
*/
void
uao_dropswap_range(struct uvm_object *uobj, voff_t start, voff_t end)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
int swpgonlydelta = 0;
KASSERT(UVM_OBJ_IS_AOBJ(uobj));
KASSERT(rw_write_held(uobj->vmobjlock));
if (end == 0) {
end = INT64_MAX;
}
if (UAO_USES_SWHASH(aobj)) {
int i, hashbuckets = aobj->u_swhashmask + 1;
voff_t taghi;
voff_t taglo;
taglo = UAO_SWHASH_ELT_TAG(start);
taghi = UAO_SWHASH_ELT_TAG(end);
for (i = 0; i < hashbuckets; i++) {
struct uao_swhash_elt *elt, *next;
for (elt = LIST_FIRST(&aobj->u_swhash[i]);
elt != NULL;
elt = next) {
int startidx, endidx;
int j;
next = LIST_NEXT(elt, list);
if (elt->tag < taglo || taghi < elt->tag) {
continue;
}
if (elt->tag == taglo) {
startidx =
UAO_SWHASH_ELT_PAGESLOT_IDX(start);
} else {
startidx = 0;
}
if (elt->tag == taghi) {
endidx =
UAO_SWHASH_ELT_PAGESLOT_IDX(end);
} else {
endidx = UAO_SWHASH_CLUSTER_SIZE;
}
for (j = startidx; j < endidx; j++) {
int slot = elt->slots[j];
KASSERT(uvm_pagelookup(&aobj->u_obj,
(voff_t)(UAO_SWHASH_ELT_PAGEIDX_BASE(elt)
+ j) << PAGE_SHIFT) == NULL);
if (slot > 0) {
uvm_swap_free(slot, 1);
swpgonlydelta++;
KASSERT(elt->count > 0);
elt->slots[j] = 0;
elt->count--;
}
}
if (elt->count == 0) {
LIST_REMOVE(elt, list);
pool_put(&uao_swhash_elt_pool, elt);
}
}
}
} else {
int i;
if (aobj->u_pages < end) {
end = aobj->u_pages;
}
for (i = start; i < end; i++) {
int slot = aobj->u_swslots[i];
if (slot > 0) {
uvm_swap_free(slot, 1);
swpgonlydelta++;
}
}
}
/*
* adjust the counter of pages only in swap for all
* the swap slots we've freed.
*/
if (swpgonlydelta > 0) {
KASSERT(uvmexp.swpgonly >= swpgonlydelta);
atomic_add_int(&uvmexp.swpgonly, -swpgonlydelta);
}
}
3097
3093
3081
2792
2278
108
2204
2281
265
407
638
34
7
25
24
1
25
19
18
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/* $OpenBSD: kern_subr.c,v 1.51 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_subr.c,v 1.15 1996/04/09 17:21:56 ragge Exp $ */
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/malloc.h>
#include <sys/queue.h>
int
uiomove(void *cp, size_t n, struct uio *uio)
{
struct iovec *iov;
size_t cnt;
int error = 0;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
panic("uiomove: mode");
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
panic("uiomove: proc");
#endif
if (n > uio->uio_resid)
n = uio->uio_resid;
while (n > 0) {
iov = uio->uio_iov;
cnt = iov->iov_len;
if (cnt == 0) {
KASSERT(uio->uio_iovcnt > 0);
uio->uio_iov++;
uio->uio_iovcnt--;
continue;
}
if (cnt > n)
cnt = n;
switch (uio->uio_segflg) {
case UIO_USERSPACE:
sched_pause(preempt);
if (uio->uio_rw == UIO_READ)
error = copyout(cp, iov->iov_base, cnt);
else
error = copyin(iov->iov_base, cp, cnt);
if (error)
return (error);
break;
case UIO_SYSSPACE:
if (uio->uio_rw == UIO_READ)
error = kcopy(cp, iov->iov_base, cnt);
else
error = kcopy(iov->iov_base, cp, cnt);
if (error)
return(error);
}
iov->iov_base = (caddr_t)iov->iov_base + cnt;
iov->iov_len -= cnt;
uio->uio_resid -= cnt;
uio->uio_offset += cnt;
cp = (caddr_t)cp + cnt;
n -= cnt;
}
return (error);
}
/*
* Give next character to user as result of read.
*/
int
ureadc(int c, struct uio *uio)
{
struct iovec *iov;
if (uio->uio_resid == 0)
#ifdef DIAGNOSTIC
panic("ureadc: zero resid");
#else
return (EINVAL);
#endif
again:
if (uio->uio_iovcnt <= 0)
#ifdef DIAGNOSTIC
panic("ureadc: non-positive iovcnt");
#else
return (EINVAL);
#endif
iov = uio->uio_iov;
if (iov->iov_len <= 0) {
uio->uio_iovcnt--;
uio->uio_iov++;
goto again;
}
switch (uio->uio_segflg) {
case UIO_USERSPACE:
{
char tmp = c;
if (copyout(&tmp, iov->iov_base, sizeof(char)) != 0)
return (EFAULT);
}
break;
case UIO_SYSSPACE:
*(char *)iov->iov_base = c;
break;
}
iov->iov_base = (caddr_t)iov->iov_base + 1;
iov->iov_len--;
uio->uio_resid--;
uio->uio_offset++;
return (0);
}
/*
* General routine to allocate a hash table.
*/
void *
hashinit(int elements, int type, int flags, u_long *hashmask)
{
u_long hashsize, i;
LIST_HEAD(generic, generic) *hashtbl;
if (elements <= 0)
panic("hashinit: bad cnt");
if ((elements & (elements - 1)) == 0)
hashsize = elements;
else
for (hashsize = 1; hashsize < elements; hashsize <<= 1)
continue;
hashtbl = mallocarray(hashsize, sizeof(*hashtbl), type, flags);
if (hashtbl == NULL)
return NULL;
for (i = 0; i < hashsize; i++)
LIST_INIT(&hashtbl[i]);
*hashmask = hashsize - 1;
return (hashtbl);
}
void
hashfree(void *hash, int elements, int type)
{
u_long hashsize;
LIST_HEAD(generic, generic) *hashtbl = hash;
if (elements <= 0)
panic("hashfree: bad cnt");
if ((elements & (elements - 1)) == 0)
hashsize = elements;
else
for (hashsize = 1; hashsize < elements; hashsize <<= 1)
continue;
free(hashtbl, type, sizeof(*hashtbl) * hashsize);
}
/*
* "startup hook" types, functions, and variables.
*/
struct hook_desc_head startuphook_list =
TAILQ_HEAD_INITIALIZER(startuphook_list);
void *
hook_establish(struct hook_desc_head *head, int tail, void (*fn)(void *),
void *arg)
{
struct hook_desc *hdp;
hdp = malloc(sizeof(*hdp), M_DEVBUF, M_NOWAIT);
if (hdp == NULL)
return (NULL);
hdp->hd_fn = fn;
hdp->hd_arg = arg;
if (tail)
TAILQ_INSERT_TAIL(head, hdp, hd_list);
else
TAILQ_INSERT_HEAD(head, hdp, hd_list);
return (hdp);
}
void
hook_disestablish(struct hook_desc_head *head, void *vhook)
{
struct hook_desc *hdp;
#ifdef DIAGNOSTIC
for (hdp = TAILQ_FIRST(head); hdp != NULL;
hdp = TAILQ_NEXT(hdp, hd_list))
if (hdp == vhook)
break;
if (hdp == NULL)
return;
#endif
hdp = vhook;
TAILQ_REMOVE(head, hdp, hd_list);
free(hdp, M_DEVBUF, sizeof(*hdp));
}
/*
* Run hooks. Startup hooks are invoked right after scheduler_start but
* before root is mounted. Shutdown hooks are invoked immediately before the
* system is halted or rebooted, i.e. after file systems unmounted,
* after crash dump done, etc.
*/
void
dohooks(struct hook_desc_head *head, int flags)
{
struct hook_desc *hdp, *hdp_temp;
if ((flags & HOOK_REMOVE) == 0) {
TAILQ_FOREACH_SAFE(hdp, head, hd_list, hdp_temp) {
(*hdp->hd_fn)(hdp->hd_arg);
}
} else {
while ((hdp = TAILQ_FIRST(head)) != NULL) {
TAILQ_REMOVE(head, hdp, hd_list);
(*hdp->hd_fn)(hdp->hd_arg);
if ((flags & HOOK_FREE) != 0)
free(hdp, M_DEVBUF, sizeof(*hdp));
}
}
}
116
51
67
7
1
30
12
1
5
4
7
11
42
1
50
8
43
67
50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/* $OpenBSD: udp6_output.c,v 1.59 2022/02/22 01:35:41 guenther Exp $ */
/* $KAME: udp6_output.c,v 1.21 2001/02/07 11:51:54 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
/*
* UDP protocol implementation.
* Per RFC 768, August, 1980.
*/
int
udp6_output(struct inpcb *in6p, struct mbuf *m, struct mbuf *addr6,
struct mbuf *control)
{
u_int32_t ulen = m->m_pkthdr.len;
u_int32_t plen = sizeof(struct udphdr) + ulen;
int error = 0, priv = 0, hlen, flags;
struct ip6_hdr *ip6;
struct udphdr *udp6;
struct in6_addr *laddr, *faddr;
struct ip6_pktopts *optp, opt;
struct sockaddr_in6 tmp, valid;
struct proc *p = curproc; /* XXX */
u_short fport;
if ((in6p->inp_socket->so_state & SS_PRIV) != 0)
priv = 1;
if (control) {
if ((error = ip6_setpktopts(control, &opt,
in6p->inp_outputopts6, priv, IPPROTO_UDP)) != 0)
goto release;
optp = &opt;
} else
optp = in6p->inp_outputopts6;
if (addr6) {
struct sockaddr_in6 *sin6;
if ((error = in6_nam2sin6(addr6, &sin6)))
goto release;
if (sin6->sin6_port == 0) {
error = EADDRNOTAVAIL;
goto release;
}
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
error = EADDRNOTAVAIL;
goto release;
}
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_faddr6)) {
error = EISCONN;
goto release;
}
/* protect *sin6 from overwrites */
tmp = *sin6;
sin6 = &tmp;
faddr = &sin6->sin6_addr;
fport = sin6->sin6_port; /* allow 0 port */
/* KAME hack: embed scopeid */
if (in6_embedscope(&sin6->sin6_addr, sin6, in6p) != 0) {
error = EINVAL;
goto release;
}
error = in6_pcbselsrc(&laddr, sin6, in6p, optp);
if (error)
goto release;
if (in6p->inp_lport == 0){
error = in_pcbbind(in6p, NULL, p);
if (error)
goto release;
}
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_laddr6) &&
!IN6_ARE_ADDR_EQUAL(&in6p->inp_laddr6, laddr)) {
valid.sin6_addr = *laddr;
valid.sin6_port = in6p->inp_lport;
valid.sin6_scope_id = 0;
valid.sin6_family = AF_INET6;
valid.sin6_len = sizeof(valid);
error = in6_pcbaddrisavail(in6p, &valid, 0, p);
if (error)
goto release;
}
} else {
if (IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_faddr6)) {
error = ENOTCONN;
goto release;
}
laddr = &in6p->inp_laddr6;
faddr = &in6p->inp_faddr6;
fport = in6p->inp_fport;
}
hlen = sizeof(struct ip6_hdr);
/*
* Calculate data length and get a mbuf
* for UDP and IP6 headers.
*/
M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT);
if (m == NULL) {
error = ENOBUFS;
goto releaseopt;
}
/*
* Stuff checksum and output datagram.
*/
udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
udp6->uh_sport = in6p->inp_lport; /* lport is always set in the PCB */
udp6->uh_dport = fport;
if (plen <= 0xffff)
udp6->uh_ulen = htons((u_short)plen);
else
udp6->uh_ulen = 0;
udp6->uh_sum = 0;
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = in6p->inp_flowinfo & IPV6_FLOWINFO_MASK;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
#if 0 /* ip6_plen will be filled in ip6_output. */
ip6->ip6_plen = htons((u_short)plen);
#endif
ip6->ip6_nxt = IPPROTO_UDP;
ip6->ip6_hlim = in6_selecthlim(in6p);
ip6->ip6_src = *laddr;
ip6->ip6_dst = *faddr;
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
flags = 0;
if (in6p->inp_flags & IN6P_MINMTU)
flags |= IPV6_MINMTU;
udpstat_inc(udps_opackets);
/* force routing table */
m->m_pkthdr.ph_rtableid = in6p->inp_rtableid;
#if NPF > 0
if (in6p->inp_socket->so_state & SS_ISCONNECTED)
pf_mbuf_link_inpcb(m, in6p);
#endif
error = ip6_output(m, optp, &in6p->inp_route6,
flags, in6p->inp_moptions6, in6p);
goto releaseopt;
release:
m_freem(m);
releaseopt:
if (control) {
ip6_clearpktopts(&opt, -1);
m_freem(control);
}
return (error);
}
12
2
11
4
2
2
6
6
12
6
6
2
4
4
1
5
5
5
6
2
62
4
7
1
2
1
2
22
3
3
6
24
7
15
1
1
1
1
1
1
5
2
1
1
2
3
1
2
1
2
1
1
6
3
2
2
1
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
/* $OpenBSD: vnd.c,v 1.178 2022/09/01 12:28:53 deraadt Exp $ */
/* $NetBSD: vnd.c,v 1.26 1996/03/30 23:06:11 christos Exp $ */
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* There is a security issue involved with this driver.
*
* Once mounted all access to the contents of the "mapped" file via
* the special file is controlled by the permissions on the special
* file, the protection of the mapped file is ignored (effectively,
* by using root credentials in all transactions).
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/errno.h>
#include <sys/limits.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/dkio.h>
#include <sys/specdev.h>
#include <crypto/blf.h>
#include <dev/vndioctl.h>
#ifdef VNDDEBUG
int vnddebug = 0x00;
#define VDB_FOLLOW 0x01
#define VDB_INIT 0x02
#define VDB_IO 0x04
#define DNPRINTF(f, p...) do { if ((f) & vnddebug) printf(p); } while (0)
#else
#define DNPRINTF(f, p...) /* nothing */
#endif /* VNDDEBUG */
struct vnd_softc {
struct device sc_dev;
struct disk sc_dk;
char sc_file[VNDNLEN]; /* file we're covering */
int sc_flags; /* flags */
size_t sc_size; /* size of vnd in sectors */
size_t sc_secsize; /* sector size in bytes */
size_t sc_nsectors; /* # of sectors per track */
size_t sc_ntracks; /* # of tracks per cylinder */
struct vnode *sc_vp; /* vnode */
struct ucred *sc_cred; /* credentials */
blf_ctx *sc_keyctx; /* key context */
};
/* sc_flags */
#define VNF_INITED 0x0001
#define VNF_HAVELABEL 0x0002
#define VNF_READONLY 0x0004
#define VNDRW(v) ((v)->sc_flags & VNF_READONLY ? FREAD : FREAD|FWRITE)
struct vnd_softc *vnd_softc;
int numvnd = 0;
/* called by main() at boot time */
void vndattach(int);
void vndclear(struct vnd_softc *);
int vndsetcred(struct proc *p, struct vnode *, struct vnd_ioctl *,
struct ucred **);
int vndgetdisklabel(dev_t, struct vnd_softc *, struct disklabel *, int);
void vndencrypt(struct vnd_softc *, caddr_t, size_t, daddr_t, int);
void vndencryptbuf(struct vnd_softc *, struct buf *, int);
size_t vndbdevsize(struct vnode *, struct proc *);
void
vndencrypt(struct vnd_softc *sc, caddr_t addr, size_t size, daddr_t off,
int encrypt)
{
int i, bsize;
u_char iv[8];
bsize = dbtob(1);
for (i = 0; i < size/bsize; i++) {
memset(iv, 0, sizeof(iv));
memcpy(iv, &off, sizeof(off));
blf_ecb_encrypt(sc->sc_keyctx, iv, sizeof(iv));
if (encrypt)
blf_cbc_encrypt(sc->sc_keyctx, iv, addr, bsize);
else
blf_cbc_decrypt(sc->sc_keyctx, iv, addr, bsize);
addr += bsize;
off++;
}
}
void
vndencryptbuf(struct vnd_softc *sc, struct buf *bp, int encrypt)
{
vndencrypt(sc, bp->b_data, bp->b_bcount, bp->b_blkno, encrypt);
}
void
vndattach(int num)
{
char *mem;
int i;
if (num <= 0)
return;
mem = mallocarray(num, sizeof(struct vnd_softc), M_DEVBUF,
M_NOWAIT | M_ZERO);
if (mem == NULL) {
printf("WARNING: no memory for vnode disks\n");
return;
}
vnd_softc = (struct vnd_softc *)mem;
for (i = 0; i < num; i++) {
struct vnd_softc *sc = &vnd_softc[i];
sc->sc_dev.dv_unit = i;
snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname),
"vnd%d", i);
disk_construct(&sc->sc_dk);
device_ref(&sc->sc_dev);
}
numvnd = num;
}
int
vndopen(dev_t dev, int flags, int mode, struct proc *p)
{
int unit = DISKUNIT(dev);
struct vnd_softc *sc;
int error = 0, part;
DNPRINTF(VDB_FOLLOW, "vndopen(%x, %x, %x, %p)\n", dev, flags, mode, p);
if (unit >= numvnd)
return (ENXIO);
sc = &vnd_softc[unit];
if ((error = disk_lock(&sc->sc_dk)) != 0)
return (error);
if ((flags & FWRITE) && (sc->sc_flags & VNF_READONLY)) {
error = EROFS;
goto bad;
}
if ((sc->sc_flags & VNF_INITED) &&
(sc->sc_flags & VNF_HAVELABEL) == 0 &&
sc->sc_dk.dk_openmask == 0) {
sc->sc_flags |= VNF_HAVELABEL;
vndgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0);
}
part = DISKPART(dev);
error = disk_openpart(&sc->sc_dk, part, mode,
(sc->sc_flags & VNF_HAVELABEL) != 0);
bad:
disk_unlock(&sc->sc_dk);
return (error);
}
/*
* Load the label information on the named device
*/
int
vndgetdisklabel(dev_t dev, struct vnd_softc *sc, struct disklabel *lp,
int spoofonly)
{
memset(lp, 0, sizeof(struct disklabel));
lp->d_secsize = sc->sc_secsize;
lp->d_nsectors = sc->sc_nsectors;
lp->d_ntracks = sc->sc_ntracks;
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
if (lp->d_secpercyl)
lp->d_ncylinders = sc->sc_size / lp->d_secpercyl;
strncpy(lp->d_typename, "vnd device", sizeof(lp->d_typename));
lp->d_type = DTYPE_VND;
strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
DL_SETDSIZE(lp, sc->sc_size);
lp->d_flags = 0;
lp->d_version = 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
/* Call the generic disklabel extraction routine */
return readdisklabel(DISKLABELDEV(dev), vndstrategy, lp, spoofonly);
}
int
vndclose(dev_t dev, int flags, int mode, struct proc *p)
{
int unit = DISKUNIT(dev);
struct vnd_softc *sc;
int part;
DNPRINTF(VDB_FOLLOW, "vndclose(%x, %x, %x, %p)\n", dev, flags, mode, p);
if (unit >= numvnd)
return (ENXIO);
sc = &vnd_softc[unit];
disk_lock_nointr(&sc->sc_dk);
part = DISKPART(dev);
disk_closepart(&sc->sc_dk, part, mode);
#if 0
if (sc->sc_dk.dk_openmask == 0)
sc->sc_flags &= ~VNF_HAVELABEL;
#endif
disk_unlock(&sc->sc_dk);
return (0);
}
void
vndstrategy(struct buf *bp)
{
int unit = DISKUNIT(bp->b_dev);
struct vnd_softc *sc;
struct partition *p;
off_t off;
long origbcount;
int s;
DNPRINTF(VDB_FOLLOW, "vndstrategy(%p): unit %d\n", bp, unit);
if (unit >= numvnd) {
bp->b_error = ENXIO;
goto bad;
}
sc = &vnd_softc[unit];
if ((sc->sc_flags & VNF_HAVELABEL) == 0) {
bp->b_error = ENXIO;
goto bad;
}
/*
* Many of the distrib scripts assume they can issue arbitrary
* sized requests to raw vnd devices irrespective of the
* emulated disk geometry.
*
* To continue supporting this, round the block count up to a
* multiple of d_secsize for bounds_check_with_label(), and
* then restore afterwards.
*
* We only do this for non-encrypted vnd, because encryption
* requires operating on blocks at a time.
*/
origbcount = bp->b_bcount;
if (sc->sc_keyctx == NULL) {
u_int32_t secsize = sc->sc_dk.dk_label->d_secsize;
bp->b_bcount = ((origbcount + secsize - 1) & ~(secsize - 1));
#ifdef DIAGNOSTIC
if (bp->b_bcount != origbcount) {
struct process *curpr = curproc->p_p;
printf("%s: sloppy %s from proc %d (%s): "
"blkno %lld bcount %ld\n", sc->sc_dev.dv_xname,
(bp->b_flags & B_READ) ? "read" : "write",
curpr->ps_pid, curpr->ps_comm,
(long long)bp->b_blkno, origbcount);
}
#endif
}
if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1) {
bp->b_resid = bp->b_bcount = origbcount;
goto done;
}
if (origbcount < bp->b_bcount)
bp->b_bcount = origbcount;
p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
(u_int64_t)bp->b_blkno * DEV_BSIZE;
if (sc->sc_keyctx && !(bp->b_flags & B_READ))
vndencryptbuf(sc, bp, 1);
/*
* Use IO_NOLIMIT because upper layer has already checked I/O
* for limits, so there is no need to do it again.
*
* We use IO_NOCACHE because this data should be cached at the
* upper layer, so there is no need to cache it again.
*/
bp->b_error = vn_rdwr((bp->b_flags & B_READ) ? UIO_READ : UIO_WRITE,
sc->sc_vp, bp->b_data, bp->b_bcount, off, UIO_SYSSPACE,
IO_NOCACHE | IO_SYNC | IO_NOLIMIT, sc->sc_cred, &bp->b_resid, curproc);
if (bp->b_error)
bp->b_flags |= B_ERROR;
/* Data in buffer cache needs to be in clear */
if (sc->sc_keyctx)
vndencryptbuf(sc, bp, 0);
goto done;
bad:
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
done:
s = splbio();
biodone(bp);
splx(s);
}
/* ARGSUSED */
int
vndread(dev_t dev, struct uio *uio, int flags)
{
return (physio(vndstrategy, dev, B_READ, minphys, uio));
}
/* ARGSUSED */
int
vndwrite(dev_t dev, struct uio *uio, int flags)
{
return (physio(vndstrategy, dev, B_WRITE, minphys, uio));
}
size_t
vndbdevsize(struct vnode *vp, struct proc *p)
{
struct partinfo pi;
struct bdevsw *bsw;
dev_t dev;
dev = vp->v_rdev;
bsw = bdevsw_lookup(dev);
if (bsw->d_ioctl == NULL)
return (0);
if (bsw->d_ioctl(dev, DIOCGPART, (caddr_t)&pi, FREAD, p))
return (0);
DNPRINTF(VDB_INIT, "vndbdevsize: size %llu secsize %u\n",
DL_GETPSIZE(pi.part), pi.disklab->d_secsize);
return (DL_GETPSIZE(pi.part));
}
/* ARGSUSED */
int
vndioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
int unit = DISKUNIT(dev);
struct disklabel *lp;
struct vnd_softc *sc;
struct vnd_ioctl *vio;
struct vnd_user *vnu;
struct vattr vattr;
int error, part, pmask;
DNPRINTF(VDB_FOLLOW, "vndioctl(%x, %lx, %p, %x, %p): unit %d\n",
dev, cmd, addr, flag, p, unit);
error = suser(p);
if (error)
return (error);
if (unit >= numvnd)
return (ENXIO);
sc = &vnd_softc[unit];
vio = (struct vnd_ioctl *)addr;
switch (cmd) {
case VNDIOCSET:
{
char name[VNDNLEN], key[BLF_MAXUTILIZED];
struct nameidata nd;
struct ucred *cred = NULL;
size_t size;
int vplocked;
int rw;
if (sc->sc_flags & VNF_INITED)
return (EBUSY);
/* Geometry eventually has to fit into label fields */
if (vio->vnd_secsize > UINT_MAX ||
vio->vnd_secsize == 0 ||
vio->vnd_ntracks > UINT_MAX ||
vio->vnd_nsectors > UINT_MAX)
return (EINVAL);
if ((error = copyinstr(vio->vnd_file, name,
sizeof(name), NULL)))
return (error);
if (vio->vnd_keylen > 0) {
if (vio->vnd_keylen > sizeof(key))
vio->vnd_keylen = sizeof(key);
if ((error = copyin(vio->vnd_key, key,
vio->vnd_keylen)) != 0)
return (error);
}
/*
* Open for read and write first. This lets vn_open() weed out
* directories, sockets, etc. so we don't have to worry about
* them.
*/
NDINIT(&nd, 0, 0, UIO_SYSSPACE, name, p);
nd.ni_unveil = UNVEIL_READ | UNVEIL_WRITE;
rw = FREAD|FWRITE;
error = vn_open(&nd, FREAD|FWRITE, 0);
if (error == EROFS) {
NDINIT(&nd, 0, 0, UIO_SYSSPACE, name, p);
nd.ni_unveil = UNVEIL_READ | UNVEIL_WRITE;
rw = FREAD;
error = vn_open(&nd, FREAD, 0);
}
if (error)
return (error);
vplocked = 1;
error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p);
if (error) {
fail:
if (vplocked)
VOP_UNLOCK(nd.ni_vp);
vn_close(nd.ni_vp, rw, p->p_ucred, p);
if (cred != NULL)
crfree(cred);
return (error);
}
/* Cannot put a vnd on top of a vnd */
if (major(vattr.va_fsid) == major(dev)) {
error = EINVAL;
goto fail;
}
if ((error = vndsetcred(p, nd.ni_vp, vio, &cred)) != 0)
goto fail;
VOP_UNLOCK(nd.ni_vp);
vplocked = 0;
if (nd.ni_vp->v_type == VBLK) {
size = vndbdevsize(nd.ni_vp, p);
/* XXX is size 0 ok? */
} else
size = vattr.va_size / vio->vnd_secsize;
if ((error = disk_lock(&sc->sc_dk)) != 0)
goto fail;
if (sc->sc_flags & VNF_INITED) {
disk_unlock(&sc->sc_dk);
error = EBUSY;
goto fail;
}
/* Set geometry for device. */
sc->sc_secsize = vio->vnd_secsize;
sc->sc_ntracks = vio->vnd_ntracks;
sc->sc_nsectors = vio->vnd_nsectors;
sc->sc_size = size;
if (rw == FREAD)
sc->sc_flags |= VNF_READONLY;
else
sc->sc_flags &= ~VNF_READONLY;
memcpy(sc->sc_file, name, sizeof(sc->sc_file));
if (vio->vnd_keylen > 0) {
sc->sc_keyctx = malloc(sizeof(*sc->sc_keyctx), M_DEVBUF,
M_WAITOK);
blf_key(sc->sc_keyctx, key, vio->vnd_keylen);
explicit_bzero(key, vio->vnd_keylen);
} else
sc->sc_keyctx = NULL;
sc->sc_vp = nd.ni_vp;
sc->sc_cred = cred;
vio->vnd_size = sc->sc_size * sc->sc_secsize;
sc->sc_flags |= VNF_INITED;
DNPRINTF(VDB_INIT, "vndioctl: SET vp %p size %llx\n",
sc->sc_vp, (unsigned long long)sc->sc_size);
/* Attach the disk. */
sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
disk_attach(&sc->sc_dev, &sc->sc_dk);
disk_unlock(&sc->sc_dk);
break;
}
case VNDIOCCLR:
if ((error = disk_lock(&sc->sc_dk)) != 0)
return (error);
if ((sc->sc_flags & VNF_INITED) == 0) {
disk_unlock(&sc->sc_dk);
return (ENXIO);
}
/*
* Don't unconfigure if any other partitions are open
* or if both the character and block flavors of this
* partition are open.
*/
part = DISKPART(dev);
pmask = (1 << part);
if ((sc->sc_dk.dk_openmask & ~pmask) ||
((sc->sc_dk.dk_bopenmask & pmask) &&
(sc->sc_dk.dk_copenmask & pmask))) {
disk_unlock(&sc->sc_dk);
return (EBUSY);
}
vndclear(sc);
DNPRINTF(VDB_INIT, "vndioctl: CLRed\n");
/* Free crypto key */
if (sc->sc_keyctx) {
explicit_bzero(sc->sc_keyctx, sizeof(*sc->sc_keyctx));
free(sc->sc_keyctx, M_DEVBUF, sizeof(*sc->sc_keyctx));
}
/* Detach the disk. */
disk_detach(&sc->sc_dk);
disk_unlock(&sc->sc_dk);
break;
case VNDIOCGET:
vnu = (struct vnd_user *)addr;
if (vnu->vnu_unit == -1)
vnu->vnu_unit = unit;
if (vnu->vnu_unit >= numvnd)
return (ENXIO);
if (vnu->vnu_unit < 0)
return (EINVAL);
sc = &vnd_softc[vnu->vnu_unit];
if (sc->sc_flags & VNF_INITED) {
error = VOP_GETATTR(sc->sc_vp, &vattr, p->p_ucred, p);
if (error)
return (error);
strlcpy(vnu->vnu_file, sc->sc_file,
sizeof(vnu->vnu_file));
vnu->vnu_dev = vattr.va_fsid;
vnu->vnu_ino = vattr.va_fileid;
} else {
vnu->vnu_dev = 0;
vnu->vnu_ino = 0;
}
break;
case DIOCRLDINFO:
if ((sc->sc_flags & VNF_HAVELABEL) == 0)
return (ENOTTY);
lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
vndgetdisklabel(dev, sc, lp, 0);
*(sc->sc_dk.dk_label) = *lp;
free(lp, M_TEMP, sizeof(*lp));
return (0);
case DIOCGPDINFO:
if ((sc->sc_flags & VNF_HAVELABEL) == 0)
return (ENOTTY);
vndgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
return (0);
case DIOCGDINFO:
if ((sc->sc_flags & VNF_HAVELABEL) == 0)
return (ENOTTY);
*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
return (0);
case DIOCGPART:
if ((sc->sc_flags & VNF_HAVELABEL) == 0)
return (ENOTTY);
((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
return (0);
case DIOCWDINFO:
case DIOCSDINFO:
if ((sc->sc_flags & VNF_HAVELABEL) == 0)
return (ENOTTY);
if ((flag & FWRITE) == 0)
return (EBADF);
if ((error = disk_lock(&sc->sc_dk)) != 0)
return (error);
error = setdisklabel(sc->sc_dk.dk_label,
(struct disklabel *)addr, /* sc->sc_dk.dk_openmask */ 0);
if (error == 0) {
if (cmd == DIOCWDINFO)
error = writedisklabel(DISKLABELDEV(dev),
vndstrategy, sc->sc_dk.dk_label);
}
disk_unlock(&sc->sc_dk);
return (error);
default:
return (ENOTTY);
}
return (0);
}
/*
* Duplicate the current processes' credentials. Since we are called only
* as the result of a SET ioctl and only root can do that, any future access
* to this "disk" is essentially as root. Note that credentials may change
* if some other uid can write directly to the mapped file (NFS).
*/
int
vndsetcred(struct proc *p, struct vnode *vp, struct vnd_ioctl *vio,
struct ucred **newcredp)
{
void *buf;
size_t size;
struct ucred *new;
int error;
new = crdup(p->p_ucred);
buf = malloc(DEV_BSIZE, M_TEMP, M_WAITOK);
size = DEV_BSIZE;
/* XXX: Horrible kludge to establish credentials for NFS */
error = vn_rdwr(UIO_READ, vp, buf, size, 0, UIO_SYSSPACE, 0,
new, NULL, curproc);
free(buf, M_TEMP, DEV_BSIZE);
if (error == 0)
*newcredp = new;
else
crfree(new);
return (error);
}
void
vndclear(struct vnd_softc *sc)
{
struct vnode *vp = sc->sc_vp;
struct proc *p = curproc; /* XXX */
DNPRINTF(VDB_FOLLOW, "vndclear(%p): vp %p\n", sc, vp);
if (vp == NULL)
panic("vndioctl: null vp");
(void) vn_close(vp, VNDRW(sc), sc->sc_cred, p);
crfree(sc->sc_cred);
sc->sc_flags = 0;
sc->sc_vp = NULL;
sc->sc_cred = NULL;
sc->sc_size = 0;
memset(sc->sc_file, 0, sizeof(sc->sc_file));
}
daddr_t
vndsize(dev_t dev)
{
/* We don't support swapping to vnd anymore. */
return (-1);
}
int
vnddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
/* Not implemented. */
return (ENXIO);
}
1156
1156
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/* $OpenBSD: uvm_unix.c,v 1.71 2020/10/21 21:24:57 deraadt Exp $ */
/* $NetBSD: uvm_unix.c,v 1.18 2000/09/13 15:00:25 thorpej Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993 The Regents of the University of California.
* Copyright (c) 1988 University of Utah.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: vm_unix.c 1.1 89/11/07$
* @(#)vm_unix.c 8.1 (Berkeley) 6/11/93
* from: Id: uvm_unix.c,v 1.1.2.2 1997/08/25 18:52:30 chuck Exp
*/
/*
* uvm_unix.c: traditional sbrk/grow interface to vm.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <uvm/uvm.h>
/*
* sys_obreak: set break
*/
int
sys_obreak(struct proc *p, void *v, register_t *retval)
{
struct sys_obreak_args /* {
syscallarg(char *) nsize;
} */ *uap = v;
struct vmspace *vm = p->p_vmspace;
vaddr_t new, old, base;
int error;
base = (vaddr_t)vm->vm_daddr;
new = round_page((vaddr_t)SCARG(uap, nsize));
if (new < base || (new - base) > lim_cur(RLIMIT_DATA))
return (ENOMEM);
old = round_page(base + ptoa(vm->vm_dsize));
if (new == old)
return (0);
/* grow or shrink? */
if (new > old) {
error = uvm_map(&vm->vm_map, &old, new - old, NULL,
UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_INHERIT_COPY,
MADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_COPYONW));
if (error) {
uprintf("sbrk: grow %ld failed, error = %d\n",
new - old, error);
return (ENOMEM);
}
vm->vm_dsize += atop(new - old);
} else {
uvm_unmap(&vm->vm_map, new, old);
vm->vm_dsize -= atop(old - new);
}
return (0);
}
/*
* uvm_grow: enlarge the "stack segment" to include sp.
*/
void
uvm_grow(struct proc *p, vaddr_t sp)
{
struct vmspace *vm = p->p_vmspace;
vm_map_t map = &vm->vm_map;
int si;
/* For user defined stacks (from sendsig). */
if (sp < (vaddr_t)vm->vm_maxsaddr)
return;
#ifdef MACHINE_STACK_GROWS_UP
if (sp >= (vaddr_t)vm->vm_minsaddr)
return;
#endif
vm_map_lock(map);
/* For common case of already allocated (from trap). */
#ifdef MACHINE_STACK_GROWS_UP
if (sp < (vaddr_t)vm->vm_maxsaddr + ptoa(vm->vm_ssize))
#else
if (sp >= (vaddr_t)vm->vm_minsaddr - ptoa(vm->vm_ssize))
#endif
goto out;
/* Really need to check vs limit and increment stack size if ok. */
#ifdef MACHINE_STACK_GROWS_UP
si = atop(sp - (vaddr_t)vm->vm_maxsaddr) - vm->vm_ssize + 1;
#else
si = atop((vaddr_t)vm->vm_minsaddr - sp) - vm->vm_ssize;
#endif
if (vm->vm_ssize + si <= atop(lim_cur(RLIMIT_STACK)))
vm->vm_ssize += si;
out:
vm_map_unlock(map);
}
#ifndef SMALL_KERNEL
#define WALK_CHUNK 32
/*
* Not all the pages in an amap may be present. When dumping core,
* we don't want to force all the pages to be present: it's a waste
* of time and memory when we already know what they contain (zeros)
* and the ELF format at least can adequately represent them as a
* segment with memory size larger than its file size.
*
* So, we walk the amap with calls to amap_lookups() and scan the
* resulting pointers to find ranges of zero or more present pages
* followed by at least one absent page or the end of the amap.
* When then pass that range to the walk callback with 'start'
* pointing to the start of the present range, 'realend' pointing
* to the first absent page (or the end of the entry), and 'end'
* pointing to the page past the last absent page (or the end of
* the entry).
*
* Note that if the first page of the amap is empty then the callback
* must be invoked with 'start' == 'realend' so it can present that
* first range of absent pages.
*/
int
uvm_coredump_walk_amap(struct vm_map_entry *entry, int *nsegmentp,
uvm_coredump_walk_cb *walk, void *cookie)
{
struct vm_anon *anons[WALK_CHUNK];
vaddr_t pos, start, realend, end, entry_end;
vm_prot_t prot;
int nsegment, absent, npages, i, error;
prot = entry->protection;
nsegment = *nsegmentp;
start = entry->start;
entry_end = MIN(entry->end, VM_MAXUSER_ADDRESS);
absent = 0;
for (pos = start; pos < entry_end; pos += npages << PAGE_SHIFT) {
npages = (entry_end - pos) >> PAGE_SHIFT;
if (npages > WALK_CHUNK)
npages = WALK_CHUNK;
amap_lookups(&entry->aref, pos - entry->start, anons, npages);
for (i = 0; i < npages; i++) {
if ((anons[i] == NULL) == absent)
continue;
if (!absent) {
/* going from present to absent: set realend */
realend = pos + (i << PAGE_SHIFT);
absent = 1;
continue;
}
/* going from absent to present: invoke callback */
end = pos + (i << PAGE_SHIFT);
if (start != end) {
error = (*walk)(start, realend, end, prot,
nsegment, cookie);
if (error)
return error;
nsegment++;
}
start = realend = end;
absent = 0;
}
}
if (!absent)
realend = entry_end;
error = (*walk)(start, realend, entry_end, prot, nsegment, cookie);
*nsegmentp = nsegment + 1;
return error;
}
/*
* Common logic for whether a map entry should be included in a coredump
*/
static inline int
uvm_should_coredump(struct proc *p, struct vm_map_entry *entry)
{
if (!(entry->protection & PROT_WRITE) &&
entry->aref.ar_amap == NULL &&
entry->start != p->p_p->ps_sigcode &&
entry->start != p->p_p->ps_timekeep)
return 0;
/*
* Skip ranges marked as unreadable, as uiomove(UIO_USERSPACE)
* will fail on them. Maybe this really should be a test of
* entry->max_protection, but doing
* uvm_map_extract(UVM_EXTRACT_FIXPROT)
* on each such page would suck.
*/
if ((entry->protection & PROT_READ) == 0)
return 0;
/* Skip ranges excluded from coredumps. */
if (UVM_ET_ISCONCEAL(entry))
return 0;
/* Don't dump mmaped devices. */
if (entry->object.uvm_obj != NULL &&
UVM_OBJ_IS_DEVICE(entry->object.uvm_obj))
return 0;
if (entry->start >= VM_MAXUSER_ADDRESS)
return 0;
return 1;
}
/* do nothing callback for uvm_coredump_walk_amap() */
static int
noop(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot,
int nsegment, void *cookie)
{
return 0;
}
/*
* Walk the VA space for a process to identify what to write to
* a coredump. First the number of contiguous ranges is counted,
* then the 'setup' callback is invoked to prepare for actually
* recording the ranges, then the VA is walked again, invoking
* the 'walk' callback for each range. The number of ranges walked
* is guaranteed to match the count seen by the 'setup' callback.
*/
int
uvm_coredump_walkmap(struct proc *p, uvm_coredump_setup_cb *setup,
uvm_coredump_walk_cb *walk, void *cookie)
{
struct vmspace *vm = p->p_vmspace;
struct vm_map *map = &vm->vm_map;
struct vm_map_entry *entry;
vaddr_t end;
int refed_amaps = 0;
int nsegment, error;
/*
* Walk the map once to count the segments. If an amap is
* referenced more than once than take *another* reference
* and treat the amap as exactly one segment instead of
* checking page presence inside it. On the second pass
* we'll recognize which amaps we did that for by the ref
* count being >1...and decrement it then.
*/
nsegment = 0;
RBT_FOREACH(entry, uvm_map_addr, &map->addr) {
/* should never happen for a user process */
if (UVM_ET_ISSUBMAP(entry)) {
panic("%s: user process with submap?", __func__);
}
if (! uvm_should_coredump(p, entry))
continue;
if (entry->aref.ar_amap != NULL) {
if (entry->aref.ar_amap->am_ref == 1) {
uvm_coredump_walk_amap(entry, &nsegment,
&noop, cookie);
continue;
}
/*
* Multiple refs currently, so take another and
* treat it as a single segment
*/
entry->aref.ar_amap->am_ref++;
refed_amaps++;
}
nsegment++;
}
/*
* Okay, we have a count in nsegment. Prepare to
* walk it again, then invoke the setup callback.
*/
entry = RBT_MIN(uvm_map_addr, &map->addr);
error = (*setup)(nsegment, cookie);
if (error)
goto cleanup;
/*
* Setup went okay, so do the second walk, invoking the walk
* callback on the counted segments and cleaning up references
* as we go.
*/
nsegment = 0;
for (; entry != NULL; entry = RBT_NEXT(uvm_map_addr, entry)) {
if (! uvm_should_coredump(p, entry))
continue;
if (entry->aref.ar_amap != NULL &&
entry->aref.ar_amap->am_ref == 1) {
error = uvm_coredump_walk_amap(entry, &nsegment,
walk, cookie);
if (error)
break;
continue;
}
end = entry->end;
if (end > VM_MAXUSER_ADDRESS)
end = VM_MAXUSER_ADDRESS;
error = (*walk)(entry->start, end, end, entry->protection,
nsegment, cookie);
if (error)
break;
nsegment++;
if (entry->aref.ar_amap != NULL &&
entry->aref.ar_amap->am_ref > 1) {
/* multiple refs, so we need to drop one */
entry->aref.ar_amap->am_ref--;
refed_amaps--;
}
}
if (error) {
cleanup:
/* clean up the extra references from where we left off */
if (refed_amaps > 0) {
for (; entry != NULL;
entry = RBT_NEXT(uvm_map_addr, entry)) {
if (entry->aref.ar_amap == NULL ||
entry->aref.ar_amap->am_ref == 1)
continue;
if (! uvm_should_coredump(p, entry))
continue;
entry->aref.ar_amap->am_ref--;
if (refed_amaps-- == 0)
break;
}
}
}
return error;
}
#endif /* !SMALL_KERNEL */
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
/* $OpenBSD: fd.c,v 1.108 2022/04/06 18:59:28 naddy Exp $ */
/* $NetBSD: fd.c,v 1.90 1996/05/12 23:12:03 mycroft Exp $ */
/*-
* Copyright (c) 1993, 1994, 1995, 1996 Charles Hannum.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Don Ahn.
*
* Portions Copyright (c) 1993, 1994 by
* jc@irbs.UUCP (John Capo)
* vak@zebub.msk.su (Serge Vakulenko)
* ache@astral.msk.su (Andrew A. Chernov)
* joerg_wunsch@uriah.sax.de (Joerg Wunsch)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)fd.c 7.4 (Berkeley) 5/25/91
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/mtio.h>
#include <sys/syslog.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/timeout.h>
#include <sys/dkio.h>
#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/ioctl_fd.h>
#include <dev/isa/isavar.h>
#include <dev/isa/isadmavar.h>
#include <dev/isa/fdreg.h>
#if defined(__i386__) || defined(__amd64__) /* XXX */
#include <i386/isa/nvram.h>
#endif
#include <dev/isa/fdlink.h>
/* XXX misuse a flag to identify format operation */
#define B_FORMAT B_XXX
/* fd_type struct now in ioctl_fd.h */
/* The order of entries in the following table is important -- BEWARE! */
struct fd_type fd_types[] = {
{ 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB" }, /* 1.44MB diskette */
{ 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB" }, /* 1.2 MB AT-diskettes */
{ 9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */
{ 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */
{ 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB" }, /* 3.5" 720kB diskette */
{ 9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x" }, /* 720kB in 1.2MB drive */
{ 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x" }, /* 360kB in 720kB drive */
{ 36,2,72,2,0xff,0xaf,0x1b,0x54,80,5760,1,FDC_500KBPS,"2.88MB" }, /* 2.88MB diskette */
{ 8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,"1.2MB/[1024bytes/sector]" } /* 1.2 MB japanese format */
};
/* software state, per disk (with up to 4 disks per ctlr) */
struct fd_softc {
struct device sc_dev;
struct disk sc_dk;
struct fd_type *sc_deftype; /* default type descriptor */
struct fd_type *sc_type; /* current type descriptor */
daddr_t sc_blkno; /* starting block number */
int sc_bcount; /* byte count left */
int sc_opts; /* user-set options */
int sc_skip; /* bytes already transferred */
int sc_nblks; /* number of blocks currently transferring */
int sc_nbytes; /* number of bytes currently transferring */
int sc_drive; /* physical unit number */
int sc_flags;
#define FD_OPEN 0x01 /* it's open */
#define FD_MOTOR 0x02 /* motor should be on */
#define FD_MOTOR_WAIT 0x04 /* motor coming up */
int sc_cylin; /* where we think the head is */
TAILQ_ENTRY(fd_softc) sc_drivechain;
int sc_ops; /* I/O ops since last switch */
struct bufq sc_bufq; /* pending I/O */
struct buf *sc_bp; /* the current I/O */
struct timeout fd_motor_on_to;
struct timeout fd_motor_off_to;
struct timeout fdtimeout_to;
};
/* floppy driver configuration */
int fdprobe(struct device *, void *, void *);
void fdattach(struct device *, struct device *, void *);
int fdactivate(struct device *, int);
const struct cfattach fd_ca = {
sizeof(struct fd_softc), fdprobe, fdattach, NULL, fdactivate
};
struct cfdriver fd_cd = {
NULL, "fd", DV_DISK
};
int fdgetdisklabel(dev_t, struct fd_softc *, struct disklabel *, int);
int fd_get_parms(struct fd_softc *);
void fdstrategy(struct buf *);
void fdstart(struct fd_softc *);
int fdintr(struct fdc_softc *);
void fd_set_motor(struct fdc_softc *fdc, int reset);
void fd_motor_off(void *arg);
void fd_motor_on(void *arg);
void fdfinish(struct fd_softc *fd, struct buf *bp);
int fdformat(dev_t, struct fd_formb *, struct proc *);
static __inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
void fdretry(struct fd_softc *);
void fdtimeout(void *);
int
fdgetdisklabel(dev_t dev, struct fd_softc *fd, struct disklabel *lp,
int spoofonly)
{
bzero(lp, sizeof(struct disklabel));
lp->d_type = DTYPE_FLOPPY;
lp->d_secsize = FD_BSIZE(fd);
lp->d_secpercyl = fd->sc_type->seccyl;
lp->d_nsectors = fd->sc_type->sectrac;
lp->d_ncylinders = fd->sc_type->tracks;
lp->d_ntracks = fd->sc_type->heads; /* Go figure... */
DL_SETDSIZE(lp, fd->sc_type->size);
strncpy(lp->d_typename, "floppy disk", sizeof(lp->d_typename));
strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
lp->d_version = 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
/*
* Call the generic disklabel extraction routine. If there's
* not a label there, fake it.
*/
return readdisklabel(DISKLABELDEV(dev), fdstrategy, lp, spoofonly);
}
int
fdprobe(struct device *parent, void *match, void *aux)
{
struct fdc_softc *fdc = (void *)parent;
struct cfdata *cf = match;
struct fdc_attach_args *fa = aux;
int drive = fa->fa_drive;
bus_space_tag_t iot = fdc->sc_iot;
bus_space_handle_t ioh = fdc->sc_ioh;
int n;
if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive)
return 0;
/*
* XXX
* This is to work around some odd interactions between this driver
* and SMC Ethernet cards.
*/
if (cf->cf_loc[0] == -1 && drive >= 2)
return 0;
/*
* We want to keep the flags config gave us.
*/
fa->fa_flags = cf->cf_flags;
/* select drive and turn on motor */
bus_space_write_1(iot, ioh, fdout, drive | FDO_FRST | FDO_MOEN(drive));
/* wait for motor to spin up */
tsleep_nsec(fdc, 0, "fdprobe", MSEC_TO_NSEC(250));
out_fdc(iot, ioh, NE7CMD_RECAL);
out_fdc(iot, ioh, drive);
/* wait for recalibrate */
tsleep_nsec(fdc, 0, "fdprobe", MSEC_TO_NSEC(2000));
out_fdc(iot, ioh, NE7CMD_SENSEI);
n = fdcresult(fdc);
#ifdef FD_DEBUG
{
int i;
printf("fdprobe: status");
for (i = 0; i < n; i++)
printf(" %x", fdc->sc_status[i]);
printf("\n");
}
#endif
/* turn off motor */
tsleep_nsec(fdc, 0, "fdprobe", MSEC_TO_NSEC(250));
bus_space_write_1(iot, ioh, fdout, FDO_FRST);
/* flags & 0x20 forces the drive to be found even if it won't probe */
if (!(fa->fa_flags & 0x20) && (n != 2 || (fdc->sc_status[0] & 0xf8) != 0x20))
return 0;
return 1;
}
/*
* Controller is working, and drive responded. Attach it.
*/
void
fdattach(struct device *parent, struct device *self, void *aux)
{
struct fdc_softc *fdc = (void *)parent;
struct fd_softc *fd = (void *)self;
struct fdc_attach_args *fa = aux;
struct fd_type *type = fa->fa_deftype;
int drive = fa->fa_drive;
if (!type || (fa->fa_flags & 0x10)) {
/* The config has overridden this. */
switch (fa->fa_flags & 0x07) {
case 1: /* 2.88MB */
type = &fd_types[7];
break;
case 2: /* 1.44MB */
type = &fd_types[0];
break;
case 3: /* 1.2MB */
type = &fd_types[1];
break;
case 4: /* 720K */
type = &fd_types[4];
break;
case 5: /* 360K */
type = &fd_types[3];
break;
case 6: /* 1.2 MB japanese format */
type = &fd_types[8];
break;
#ifdef __alpha__
default:
/* 1.44MB, how to detect others?
* idea from NetBSD -- jay@rootaction.net
*/
type = &fd_types[0];
#endif
}
}
if (type)
printf(": %s %d cyl, %d head, %d sec\n", type->name,
type->tracks, type->heads, type->sectrac);
else
printf(": density unknown\n");
fd->sc_cylin = -1;
fd->sc_drive = drive;
fd->sc_deftype = type;
fdc->sc_type[drive] = FDC_TYPE_DISK;
fdc->sc_link.fdlink.sc_fd[drive] = fd;
/*
* Initialize and attach the disk structure.
*/
fd->sc_dk.dk_flags = DKF_NOLABELREAD;
fd->sc_dk.dk_name = fd->sc_dev.dv_xname;
bufq_init(&fd->sc_bufq, BUFQ_DEFAULT);
disk_attach(&fd->sc_dev, &fd->sc_dk);
/* Setup timeout structures */
timeout_set(&fd->fd_motor_on_to, fd_motor_on, fd);
timeout_set(&fd->fd_motor_off_to, fd_motor_off, fd);
timeout_set(&fd->fdtimeout_to, fdtimeout, fd);
}
int
fdactivate(struct device *self, int act)
{
struct fd_softc *fd = (void *)self;
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
int rv = 0;
switch (act) {
case DVACT_SUSPEND:
if (fdc->sc_state != DEVIDLE) {
timeout_del(&fd->fd_motor_on_to);
timeout_del(&fd->fd_motor_off_to);
timeout_del(&fd->fdtimeout_to);
fdc->sc_state = IOTIMEDOUT;
fdc->sc_errors = 4;
}
break;
case DVACT_POWERDOWN:
fd_motor_off(self);
break;
}
return (rv);
}
/*
* Translate nvram type into internal data structure. Return NULL for
* none/unknown/unusable.
*/
struct fd_type *
fd_nvtotype(char *fdc, int nvraminfo, int drive)
{
#ifdef __alpha__
/* Alpha: assume 1.44MB, idea from NetBSD sys/dev/isa/fd.c
* -- jay@rootaction.net
*/
return &fd_types[0]; /* 1.44MB */
#else
int type;
type = (drive == 0 ? nvraminfo : nvraminfo << 4) & 0xf0;
switch (type) {
case NVRAM_DISKETTE_NONE:
return NULL;
case NVRAM_DISKETTE_12M:
return &fd_types[1];
case NVRAM_DISKETTE_TYPE5:
case NVRAM_DISKETTE_TYPE6:
return &fd_types[7];
case NVRAM_DISKETTE_144M:
return &fd_types[0];
case NVRAM_DISKETTE_360K:
return &fd_types[3];
case NVRAM_DISKETTE_720K:
return &fd_types[4];
default:
printf("%s: drive %d: unknown device type 0x%x\n",
fdc, drive, type);
return NULL;
}
#endif
}
static __inline struct fd_type *
fd_dev_to_type(struct fd_softc *fd, dev_t dev)
{
int type = FDTYPE(dev);
if (type > (sizeof(fd_types) / sizeof(fd_types[0])))
return NULL;
return type ? &fd_types[type - 1] : fd->sc_deftype;
}
void
fdstrategy(struct buf *bp)
{
struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(bp->b_dev)];
int sz;
int s;
int fd_bsize = FD_BSIZE(fd);
int bf = fd_bsize / DEV_BSIZE;
/* Valid unit, controller, and request? */
if (bp->b_blkno < 0 ||
(((bp->b_blkno % bf) != 0 ||
(bp->b_bcount % fd_bsize) != 0) &&
(bp->b_flags & B_FORMAT) == 0)) {
bp->b_error = EINVAL;
goto bad;
}
/* If it's a null transfer, return immediately. */
if (bp->b_bcount == 0)
goto done;
sz = howmany(bp->b_bcount, DEV_BSIZE);
if (bp->b_blkno + sz > fd->sc_type->size * bf) {
sz = fd->sc_type->size * bf - bp->b_blkno;
if (sz == 0)
/* If exactly at end of disk, return EOF. */
goto done;
if (sz < 0) {
/* If past end of disk, return EINVAL. */
bp->b_error = EINVAL;
goto bad;
}
/* Otherwise, truncate request. */
bp->b_bcount = sz << DEV_BSHIFT;
}
bp->b_resid = bp->b_bcount;
#ifdef FD_DEBUG
printf("fdstrategy: b_blkno %lld b_bcount %d blkno %lld sz %d\n",
(long long)bp->b_blkno, bp->b_bcount,
(long long)fd->sc_blkno, sz);
#endif
/* Queue I/O */
bufq_queue(&fd->sc_bufq, bp);
/* Queue transfer on drive, activate drive and controller if idle. */
s = splbio();
timeout_del(&fd->fd_motor_off_to); /* a good idea */
if (fd->sc_bp == NULL)
fdstart(fd);
#ifdef DIAGNOSTIC
else {
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
if (fdc->sc_state == DEVIDLE) {
printf("fdstrategy: controller inactive\n");
fdcstart(fdc);
}
}
#endif
splx(s);
return;
bad:
bp->b_flags |= B_ERROR;
done:
/* Toss transfer; we're done early. */
bp->b_resid = bp->b_bcount;
s = splbio();
biodone(bp);
splx(s);
}
void
fdstart(struct fd_softc *fd)
{
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
int active = !TAILQ_EMPTY(&fdc->sc_link.fdlink.sc_drives);
/* Link into controller queue. */
fd->sc_bp = bufq_dequeue(&fd->sc_bufq);
TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
/* If controller not already active, start it. */
if (!active)
fdcstart(fdc);
}
void
fdfinish(struct fd_softc *fd, struct buf *bp)
{
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
splassert(IPL_BIO);
fd->sc_skip = 0;
fd->sc_bp = bufq_dequeue(&fd->sc_bufq);
/*
* Move this drive to the end of the queue to give others a `fair'
* chance. We only force a switch if N operations are completed while
* another drive is waiting to be serviced, since there is a long motor
* startup delay whenever we switch.
*/
if (TAILQ_NEXT(fd, sc_drivechain) != NULL && ++fd->sc_ops >= 8) {
fd->sc_ops = 0;
TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
if (fd->sc_bp != NULL) {
TAILQ_INSERT_TAIL(&fdc->sc_link.fdlink.sc_drives, fd,
sc_drivechain);
}
}
biodone(bp);
/* turn off motor 5s from now */
timeout_add_sec(&fd->fd_motor_off_to, 5);
fdc->sc_state = DEVIDLE;
}
int
fdread(dev_t dev, struct uio *uio, int flags)
{
return (physio(fdstrategy, dev, B_READ, minphys, uio));
}
int
fdwrite(dev_t dev, struct uio *uio, int flags)
{
return (physio(fdstrategy, dev, B_WRITE, minphys, uio));
}
void
fd_set_motor(struct fdc_softc *fdc, int reset)
{
struct fd_softc *fd;
u_char status;
int n;
if ((fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives)) != NULL)
status = fd->sc_drive;
else
status = 0;
if (!reset)
status |= FDO_FRST | FDO_FDMAEN;
for (n = 0; n < 4; n++)
if ((fd = fdc->sc_link.fdlink.sc_fd[n])
&& (fd->sc_flags & FD_MOTOR))
status |= FDO_MOEN(n);
bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout, status);
}
void
fd_motor_off(void *arg)
{
struct fd_softc *fd = arg;
int s;
s = splbio();
fd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
fd_set_motor((struct fdc_softc *)fd->sc_dev.dv_parent, 0);
splx(s);
}
void
fd_motor_on(void *arg)
{
struct fd_softc *fd = arg;
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
int s;
s = splbio();
fd->sc_flags &= ~FD_MOTOR_WAIT;
if ((TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives) == fd)
&& (fdc->sc_state == MOTORWAIT))
(void) fdintr(fdc);
splx(s);
}
int
fdopen(dev_t dev, int flags, int fmt, struct proc *p)
{
int unit, pmask;
struct fd_softc *fd;
struct fd_type *type;
unit = FDUNIT(dev);
if (unit >= fd_cd.cd_ndevs)
return ENXIO;
fd = fd_cd.cd_devs[unit];
if (fd == 0)
return ENXIO;
type = fd_dev_to_type(fd, dev);
if (type == NULL)
return ENXIO;
if ((fd->sc_flags & FD_OPEN) != 0 &&
fd->sc_type != type)
return EBUSY;
fd->sc_type = type;
fd->sc_cylin = -1;
fd->sc_flags |= FD_OPEN;
/*
* Only update the disklabel if we're not open anywhere else.
*/
if (fd->sc_dk.dk_openmask == 0)
fdgetdisklabel(dev, fd, fd->sc_dk.dk_label, 0);
pmask = (1 << FDPART(dev));
switch (fmt) {
case S_IFCHR:
fd->sc_dk.dk_copenmask |= pmask;
break;
case S_IFBLK:
fd->sc_dk.dk_bopenmask |= pmask;
break;
}
fd->sc_dk.dk_openmask =
fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask;
return 0;
}
int
fdclose(dev_t dev, int flags, int fmt, struct proc *p)
{
struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
int pmask = (1 << FDPART(dev));
fd->sc_flags &= ~FD_OPEN;
fd->sc_opts &= ~FDOPT_NORETRY;
switch (fmt) {
case S_IFCHR:
fd->sc_dk.dk_copenmask &= ~pmask;
break;
case S_IFBLK:
fd->sc_dk.dk_bopenmask &= ~pmask;
break;
}
fd->sc_dk.dk_openmask =
fd->sc_dk.dk_copenmask | fd->sc_dk.dk_bopenmask;
return (0);
}
daddr_t
fdsize(dev_t dev)
{
/* Swapping to floppies would not make sense. */
return -1;
}
int
fddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
/* Not implemented. */
return ENXIO;
}
/*
* Called from the controller.
*/
int
fdintr(struct fdc_softc *fdc)
{
#define st0 fdc->sc_status[0]
#define cyl fdc->sc_status[1]
struct fd_softc *fd;
struct buf *bp;
bus_space_tag_t iot = fdc->sc_iot;
bus_space_handle_t ioh = fdc->sc_ioh;
bus_space_handle_t ioh_ctl = fdc->sc_ioh_ctl;
int read, head, sec, i, nblks, cylin;
struct fd_type *type;
struct fd_formb *finfo = NULL;
int fd_bsize;
loop:
/* Is there a transfer to this drive? If not, deactivate drive. */
fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives);
if (fd == NULL) {
fdc->sc_state = DEVIDLE;
return 1;
}
fd_bsize = FD_BSIZE(fd);
bp = fd->sc_bp;
if (bp == NULL) {
fd->sc_ops = 0;
TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
goto loop;
}
if (bp->b_flags & B_FORMAT)
finfo = (struct fd_formb *)bp->b_data;
cylin = ((bp->b_blkno * DEV_BSIZE) + (bp->b_bcount - bp->b_resid)) /
(fd_bsize * fd->sc_type->seccyl);
switch (fdc->sc_state) {
case DEVIDLE:
fdc->sc_errors = 0;
fd->sc_skip = 0;
fd->sc_bcount = bp->b_bcount;
fd->sc_blkno = bp->b_blkno / (fd_bsize / DEV_BSIZE);
timeout_del(&fd->fd_motor_off_to);
if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
fdc->sc_state = MOTORWAIT;
return 1;
}
if ((fd->sc_flags & FD_MOTOR) == 0) {
/* Turn on the motor, being careful about pairing. */
struct fd_softc *ofd =
fdc->sc_link.fdlink.sc_fd[fd->sc_drive ^ 1];
if (ofd && ofd->sc_flags & FD_MOTOR) {
timeout_del(&ofd->fd_motor_off_to);
ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
}
fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
fd_set_motor(fdc, 0);
fdc->sc_state = MOTORWAIT;
/* Allow .25s for motor to stabilize. */
timeout_add_msec(&fd->fd_motor_on_to, 250);
return 1;
}
/* Make sure the right drive is selected. */
fd_set_motor(fdc, 0);
/* FALLTHROUGH */
case DOSEEK:
doseek:
if (fd->sc_cylin == cylin)
goto doio;
out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */
out_fdc(iot, ioh, fd->sc_type->steprate);
out_fdc(iot, ioh, 6); /* XXX head load time == 6ms */
out_fdc(iot, ioh, NE7CMD_SEEK); /* seek function */
out_fdc(iot, ioh, fd->sc_drive); /* drive number */
out_fdc(iot, ioh, cylin * fd->sc_type->step);
fd->sc_cylin = -1;
fdc->sc_state = SEEKWAIT;
fd->sc_dk.dk_seek++;
disk_busy(&fd->sc_dk);
timeout_add_sec(&fd->fdtimeout_to, 4);
return 1;
case DOIO:
doio:
type = fd->sc_type;
if (finfo)
fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
(char *)finfo;
sec = fd->sc_blkno % type->seccyl;
nblks = type->seccyl - sec;
nblks = min(nblks, fd->sc_bcount / fd_bsize);
nblks = min(nblks, FDC_MAXIOSIZE / fd_bsize);
fd->sc_nblks = nblks;
fd->sc_nbytes = finfo ? bp->b_bcount : nblks * fd_bsize;
head = sec / type->sectrac;
sec -= head * type->sectrac;
#ifdef DIAGNOSTIC
{int block;
block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec;
if (block != fd->sc_blkno) {
panic("fdintr: block %d != blkno %llu", block, fd->sc_blkno);
}}
#endif
read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
isadma_start(bp->b_data + fd->sc_skip, fd->sc_nbytes,
fdc->sc_drq, read);
bus_space_write_1(iot, ioh_ctl, fdctl, type->rate);
#ifdef FD_DEBUG
printf("fdintr: %s drive %d track %d head %d sec %d nblks %d\n",
read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head,
sec, nblks);
#endif
if (finfo) {
/* formatting */
if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) {
fdc->sc_errors = 4;
fdretry(fd);
goto loop;
}
out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
out_fdc(iot, ioh, finfo->fd_formb_secshift);
out_fdc(iot, ioh, finfo->fd_formb_nsecs);
out_fdc(iot, ioh, finfo->fd_formb_gaplen);
out_fdc(iot, ioh, finfo->fd_formb_fillbyte);
} else {
if (read)
out_fdc(iot, ioh, NE7CMD_READ); /* READ */
else
out_fdc(iot, ioh, NE7CMD_WRITE);/* WRITE */
out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
out_fdc(iot, ioh, fd->sc_cylin); /* track */
out_fdc(iot, ioh, head);
out_fdc(iot, ioh, sec + 1); /* sec +1 */
out_fdc(iot, ioh, type->secsize); /* sec size */
out_fdc(iot, ioh, type->sectrac); /* secs/track */
out_fdc(iot, ioh, type->gap1); /* gap1 size */
out_fdc(iot, ioh, type->datalen); /* data len */
}
fdc->sc_state = IOCOMPLETE;
disk_busy(&fd->sc_dk);
/* allow 2 seconds for operation */
timeout_add_sec(&fd->fdtimeout_to, 2);
return 1; /* will return later */
case SEEKWAIT:
timeout_del(&fd->fdtimeout_to);
fdc->sc_state = SEEKCOMPLETE;
/* allow 1/50 second for heads to settle */
timeout_add_msec(&fdc->fdcpseudointr_to, 20);
return 1;
case SEEKCOMPLETE:
disk_unbusy(&fd->sc_dk, 0, 0, 0); /* no data on seek */
/* Make sure seek really happened. */
out_fdc(iot, ioh, NE7CMD_SENSEI);
if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
cyl != cylin * fd->sc_type->step) {
#ifdef FD_DEBUG
fdcstatus(&fd->sc_dev, 2, "seek failed");
#endif
fdretry(fd);
goto loop;
}
fd->sc_cylin = cylin;
goto doio;
case IOTIMEDOUT:
isadma_abort(fdc->sc_drq);
case SEEKTIMEDOUT:
case RECALTIMEDOUT:
case RESETTIMEDOUT:
fdretry(fd);
goto loop;
case IOCOMPLETE: /* IO DONE, post-analyze */
timeout_del(&fd->fdtimeout_to);
disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
fd->sc_blkno, (bp->b_flags & B_READ));
if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) {
isadma_abort(fdc->sc_drq);
#ifdef FD_DEBUG
fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
"read failed" : "write failed");
printf("blkno %lld nblks %d\n",
(long long)fd->sc_blkno, fd->sc_nblks);
#endif
fdretry(fd);
goto loop;
}
read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
isadma_done(fdc->sc_drq);
if (fdc->sc_errors) {
diskerr(bp, "fd", "soft error", LOG_PRINTF,
fd->sc_skip / fd_bsize, (struct disklabel *)NULL);
printf("\n");
fdc->sc_errors = 0;
}
fd->sc_blkno += fd->sc_nblks;
fd->sc_skip += fd->sc_nbytes;
fd->sc_bcount -= fd->sc_nbytes;
bp->b_resid -= fd->sc_nbytes;
if (!finfo && fd->sc_bcount > 0) {
cylin = fd->sc_blkno / fd->sc_type->seccyl;
goto doseek;
}
fdfinish(fd, bp);
goto loop;
case DORESET:
/* try a reset, keep motor on */
fd_set_motor(fdc, 1);
delay(100);
fd_set_motor(fdc, 0);
fdc->sc_state = RESETCOMPLETE;
timeout_add_msec(&fd->fdtimeout_to, 500);
return 1; /* will return later */
case RESETCOMPLETE:
timeout_del(&fd->fdtimeout_to);
/* clear the controller output buffer */
for (i = 0; i < 4; i++) {
out_fdc(iot, ioh, NE7CMD_SENSEI);
(void) fdcresult(fdc);
}
/* FALLTHROUGH */
case DORECAL:
out_fdc(iot, ioh, NE7CMD_RECAL); /* recal function */
out_fdc(iot, ioh, fd->sc_drive);
fdc->sc_state = RECALWAIT;
timeout_add_sec(&fd->fdtimeout_to, 5);
return 1; /* will return later */
case RECALWAIT:
timeout_del(&fd->fdtimeout_to);
fdc->sc_state = RECALCOMPLETE;
/* allow 1/30 second for heads to settle */
timeout_add_msec(&fdc->fdcpseudointr_to, 1000 / 30);
return 1; /* will return later */
case RECALCOMPLETE:
out_fdc(iot, ioh, NE7CMD_SENSEI);
if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
#ifdef FD_DEBUG
fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
#endif
fdretry(fd);
goto loop;
}
fd->sc_cylin = 0;
goto doseek;
case MOTORWAIT:
if (fd->sc_flags & FD_MOTOR_WAIT)
return 1; /* time's not up yet */
goto doseek;
default:
fdcstatus(&fd->sc_dev, 0, "stray interrupt");
return 1;
}
#ifdef DIAGNOSTIC
panic("fdintr: impossible");
#endif
#undef st0
#undef cyl
}
void
fdtimeout(void *arg)
{
struct fd_softc *fd = arg;
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
int s;
s = splbio();
#ifdef DEBUG
log(LOG_ERR,"fdtimeout: state %d\n", fdc->sc_state);
#endif
fdcstatus(&fd->sc_dev, 0, "timeout");
if (fd->sc_bp != NULL)
fdc->sc_state++;
else
fdc->sc_state = DEVIDLE;
(void) fdintr(fdc);
splx(s);
}
void
fdretry(struct fd_softc *fd)
{
struct fdc_softc *fdc = (void *)fd->sc_dev.dv_parent;
struct buf *bp = fd->sc_bp;
if (fd->sc_opts & FDOPT_NORETRY)
goto fail;
switch (fdc->sc_errors) {
case 0:
/* try again */
fdc->sc_state = DOSEEK;
break;
case 1: case 2: case 3:
/* didn't work; try recalibrating */
fdc->sc_state = DORECAL;
break;
case 4:
/* still no go; reset the bastard */
fdc->sc_state = DORESET;
break;
default:
fail:
diskerr(bp, "fd", "hard error", LOG_PRINTF,
fd->sc_skip / FD_BSIZE(fd), (struct disklabel *)NULL);
printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n",
fdc->sc_status[0], NE7_ST0BITS,
fdc->sc_status[1], NE7_ST1BITS,
fdc->sc_status[2], NE7_ST2BITS,
fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]);
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
bp->b_resid = bp->b_bcount;
fdfinish(fd, bp);
}
fdc->sc_errors++;
}
int
fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
struct disklabel *lp;
int error;
switch (cmd) {
case MTIOCTOP:
if (((struct mtop *)addr)->mt_op != MTOFFL)
return EIO;
return (0);
case DIOCRLDINFO:
lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
fdgetdisklabel(dev, fd, lp, 0);
bcopy(lp, fd->sc_dk.dk_label, sizeof(*lp));
free(lp, M_TEMP, sizeof(*lp));
return 0;
case DIOCGPDINFO:
fdgetdisklabel(dev, fd, (struct disklabel *)addr, 1);
return 0;
case DIOCGDINFO:
*(struct disklabel *)addr = *(fd->sc_dk.dk_label);
return 0;
case DIOCGPART:
((struct partinfo *)addr)->disklab = fd->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&fd->sc_dk.dk_label->d_partitions[FDPART(dev)];
return 0;
case DIOCWDINFO:
case DIOCSDINFO:
if ((flag & FWRITE) == 0)
return EBADF;
error = setdisklabel(fd->sc_dk.dk_label,
(struct disklabel *)addr, 0);
if (error == 0) {
if (cmd == DIOCWDINFO)
error = writedisklabel(DISKLABELDEV(dev),
fdstrategy, fd->sc_dk.dk_label);
}
return error;
case FD_FORM:
if((flag & FWRITE) == 0)
return EBADF; /* must be opened for writing */
else if(((struct fd_formb *)addr)->format_version !=
FD_FORMAT_VERSION)
return EINVAL; /* wrong version of formatting prog */
else
return fdformat(dev, (struct fd_formb *)addr, p);
break;
case FD_GTYPE: /* get drive type */
*(struct fd_type *)addr = *fd->sc_type;
return 0;
case FD_GOPTS: /* get drive options */
*(int *)addr = fd->sc_opts;
return 0;
case FD_SOPTS: /* set drive options */
fd->sc_opts = *(int *)addr;
return 0;
default:
return ENOTTY;
}
#ifdef DIAGNOSTIC
panic("fdioctl: impossible");
#endif
}
int
fdformat(dev_t dev, struct fd_formb *finfo, struct proc *p)
{
int rv = 0;
struct fd_softc *fd = fd_cd.cd_devs[FDUNIT(dev)];
struct fd_type *type = fd->sc_type;
struct buf *bp;
int fd_bsize = FD_BSIZE(fd);
/* set up a buffer header for fdstrategy() */
bp = malloc(sizeof(*bp), M_TEMP, M_NOWAIT | M_ZERO);
if (bp == NULL)
return ENOBUFS;
bp->b_flags = B_BUSY | B_PHYS | B_FORMAT | B_RAW;
bp->b_proc = p;
bp->b_dev = dev;
/*
* calculate a fake blkno, so fdstrategy() would initiate a
* seek to the requested cylinder
*/
bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
+ finfo->head * type->sectrac) * fd_bsize / DEV_BSIZE;
bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
bp->b_data = (caddr_t)finfo;
#ifdef DEBUG
printf("fdformat: blkno %llx count %lx\n", bp->b_blkno, bp->b_bcount);
#endif
/* now do the format */
fdstrategy(bp);
/* ...and wait for it to complete */
rv = biowait(bp);
free(bp, M_TEMP, sizeof(*bp));
return (rv);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#ifndef __DRM_VMA_MANAGER_H__
#define __DRM_VMA_MANAGER_H__
/*
* Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <drm/drm_mm.h>
#include <linux/mm.h>
#include <linux/rbtree.h>
#include <linux/spinlock.h>
#include <linux/types.h>
/* We make up offsets for buffer objects so we can recognize them at
* mmap time. pgoff in mmap is an unsigned long, so we need to make sure
* that the faked up offset will fit
*/
#if BITS_PER_LONG == 64
#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 256)
#else
#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1)
#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16)
#endif
struct drm_file;
struct drm_vma_offset_file {
struct rb_node vm_rb;
struct drm_file *vm_tag;
unsigned long vm_count;
};
struct drm_vma_offset_node {
struct mutex vm_lock;
struct drm_mm_node vm_node;
struct rb_root vm_files;
void *driver_private;
};
struct drm_vma_offset_manager {
struct mutex vm_lock;
struct drm_mm vm_addr_space_mm;
};
void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
unsigned long page_offset, unsigned long size);
void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr);
struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
unsigned long start,
unsigned long pages);
int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
struct drm_vma_offset_node *node, unsigned long pages);
void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
struct drm_vma_offset_node *node);
int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag);
void drm_vma_node_revoke(struct drm_vma_offset_node *node,
struct drm_file *tag);
bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
struct drm_file *tag);
/**
* drm_vma_offset_exact_lookup_locked() - Look up node by exact address
* @mgr: Manager object
* @start: Start address (page-based, not byte-based)
* @pages: Size of object (page-based)
*
* Same as drm_vma_offset_lookup_locked() but does not allow any offset into the node.
* It only returns the exact object with the given start address.
*
* RETURNS:
* Node at exact start address @start.
*/
static inline struct drm_vma_offset_node *
drm_vma_offset_exact_lookup_locked(struct drm_vma_offset_manager *mgr,
unsigned long start,
unsigned long pages)
{
struct drm_vma_offset_node *node;
node = drm_vma_offset_lookup_locked(mgr, start, pages);
return (node && node->vm_node.start == start) ? node : NULL;
}
/**
* drm_vma_offset_lock_lookup() - Lock lookup for extended private use
* @mgr: Manager object
*
* Lock VMA manager for extended lookups. Only locked VMA function calls
* are allowed while holding this lock. All other contexts are blocked from VMA
* until the lock is released via drm_vma_offset_unlock_lookup().
*
* Use this if you need to take a reference to the objects returned by
* drm_vma_offset_lookup_locked() before releasing this lock again.
*
* This lock must not be used for anything else than extended lookups. You must
* not call any other VMA helpers while holding this lock.
*
* Note: You're in atomic-context while holding this lock!
*/
static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
{
read_lock(&mgr->vm_lock);
}
/**
* drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use
* @mgr: Manager object
*
* Release lookup-lock. See drm_vma_offset_lock_lookup() for more information.
*/
static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
{
read_unlock(&mgr->vm_lock);
}
/**
* drm_vma_node_reset() - Initialize or reset node object
* @node: Node to initialize or reset
*
* Reset a node to its initial state. This must be called before using it with
* any VMA offset manager.
*
* This must not be called on an already allocated node, or you will leak
* memory.
*/
static inline void drm_vma_node_reset(struct drm_vma_offset_node *node)
{
memset(node, 0, sizeof(*node));
node->vm_files = RB_ROOT;
mtx_init(&node->vm_lock, IPL_NONE);
}
/**
* drm_vma_node_start() - Return start address for page-based addressing
* @node: Node to inspect
*
* Return the start address of the given node. This can be used as offset into
* the linear VM space that is provided by the VMA offset manager. Note that
* this can only be used for page-based addressing. If you need a proper offset
* for user-space mappings, you must apply "<< PAGE_SHIFT" or use the
* drm_vma_node_offset_addr() helper instead.
*
* RETURNS:
* Start address of @node for page-based addressing. 0 if the node does not
* have an offset allocated.
*/
static inline unsigned long drm_vma_node_start(const struct drm_vma_offset_node *node)
{
return node->vm_node.start;
}
/**
* drm_vma_node_size() - Return size (page-based)
* @node: Node to inspect
*
* Return the size as number of pages for the given node. This is the same size
* that was passed to drm_vma_offset_add(). If no offset is allocated for the
* node, this is 0.
*
* RETURNS:
* Size of @node as number of pages. 0 if the node does not have an offset
* allocated.
*/
static inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node)
{
return node->vm_node.size;
}
/**
* drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps
* @node: Linked offset node
*
* Same as drm_vma_node_start() but returns the address as a valid offset that
* can be used for user-space mappings during mmap().
* This must not be called on unlinked nodes.
*
* RETURNS:
* Offset of @node for byte-based addressing. 0 if the node does not have an
* object allocated.
*/
static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
{
return ((__u64)node->vm_node.start) << PAGE_SHIFT;
}
/**
* drm_vma_node_unmap() - Unmap offset node
* @node: Offset node
* @file_mapping: Address space to unmap @node from
*
* Unmap all userspace mappings for a given offset node. The mappings must be
* associated with the @file_mapping address-space. If no offset exists
* nothing is done.
*
* This call is unlocked. The caller must guarantee that drm_vma_offset_remove()
* is not called on this node concurrently.
*/
#ifdef __linux__
static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
struct address_space *file_mapping)
{
if (drm_mm_node_allocated(&node->vm_node))
unmap_mapping_range(file_mapping,
drm_vma_node_offset_addr(node),
drm_vma_node_size(node) << PAGE_SHIFT, 1);
}
#endif
/**
* drm_vma_node_verify_access() - Access verification helper for TTM
* @node: Offset node
* @tag: Tag of file to check
*
* This checks whether @tag is granted access to @node. It is the same as
* drm_vma_node_is_allowed() but suitable as drop-in helper for TTM
* verify_access() callbacks.
*
* RETURNS:
* 0 if access is granted, -EACCES otherwise.
*/
static inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node,
struct drm_file *tag)
{
return drm_vma_node_is_allowed(node, tag) ? 0 : -EACCES;
}
#endif /* __DRM_VMA_MANAGER_H__ */
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
/* $OpenBSD: if_ppp.c,v 1.117 2020/08/21 22:59:27 kn Exp $ */
/* $NetBSD: if_ppp.c,v 1.39 1997/05/17 21:11:59 christos Exp $ */
/*
* if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver.
*
* Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Based on:
* @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89
*
* Copyright (c) 1987, 1989, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Serial Line interface
*
* Rick Adams
* Center for Seismic Studies
* 1300 N 17th Street, Suite 1450
* Arlington, Virginia 22209
* (703)276-7900
* rick@seismo.ARPA
* seismo!rick
*
* Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
* Converted to 4.3BSD Beta by Chris Torek.
* Other changes made at Berkeley, based in part on code by Kirk Smith.
*
* Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
* Added VJ tcp header compression; more unified ioctls
*
* Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
* Cleaned up a lot of the mbuf-related code to fix bugs that
* caused system crashes and packet corruption. Changed pppstart
* so that it doesn't just give up with a collision if the whole
* packet doesn't fit in the output ring buffer.
*
* Added priority queueing for interactive IP packets, following
* the model of if_sl.c, plus hooks for bpf.
* Paul Mackerras (paulus@cs.anu.edu.au).
*/
/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
/* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */
#include "ppp.h"
#if NPPP > 0
#define VJC
#define PPP_COMPRESS
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/bpf.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include "bpfilter.h"
#ifdef VJC
#include <net/slcompress.h>
#endif
#include <net/ppp_defs.h>
#include <net/if_ppp.h>
#include <net/if_pppvar.h>
#ifdef PPP_COMPRESS
#define PACKETPTR struct mbuf *
#include <net/ppp-comp.h>
#endif
static int pppsioctl(struct ifnet *, u_long, caddr_t);
static void ppp_requeue(struct ppp_softc *);
static void ppp_ccp(struct ppp_softc *, struct mbuf *m, int rcvd);
static void ppp_ccp_closed(struct ppp_softc *);
static void ppp_inproc(struct ppp_softc *, struct mbuf *);
static void pppdumpm(struct mbuf *m0);
static void ppp_ifstart(struct ifnet *ifp);
int ppp_clone_create(struct if_clone *, int);
int ppp_clone_destroy(struct ifnet *);
void ppp_pkt_list_init(struct ppp_pkt_list *, u_int);
int ppp_pkt_enqueue(struct ppp_pkt_list *, struct ppp_pkt *);
struct ppp_pkt *ppp_pkt_dequeue(struct ppp_pkt_list *);
struct mbuf *ppp_pkt_mbuf(struct ppp_pkt *);
/*
* We steal two bits in the mbuf m_flags, to mark high-priority packets
* for output, and received packets following lost/corrupted packets.
*/
#define M_ERRMARK M_LINK0 /* steal a bit in mbuf m_flags */
#ifdef PPP_COMPRESS
/*
* List of compressors we know about.
*/
extern struct compressor ppp_bsd_compress;
extern struct compressor ppp_deflate, ppp_deflate_draft;
struct compressor *ppp_compressors[] = {
#if DO_BSD_COMPRESS && defined(PPP_BSDCOMP)
&ppp_bsd_compress,
#endif
#if DO_DEFLATE && defined(PPP_DEFLATE)
&ppp_deflate,
&ppp_deflate_draft,
#endif
NULL
};
#endif /* PPP_COMPRESS */
LIST_HEAD(, ppp_softc) ppp_softc_list;
struct if_clone ppp_cloner =
IF_CLONE_INITIALIZER("ppp", ppp_clone_create, ppp_clone_destroy);
/*
* Called from boot code to establish ppp interfaces.
*/
void
pppattach(void)
{
LIST_INIT(&ppp_softc_list);
if_clone_attach(&ppp_cloner);
}
int
ppp_clone_create(struct if_clone *ifc, int unit)
{
struct ppp_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
sc->sc_unit = unit;
ifp = &sc->sc_if;
snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d",
ifc->ifc_name, unit);
sc->sc_if.if_softc = sc;
sc->sc_if.if_mtu = PPP_MTU;
sc->sc_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
sc->sc_if.if_type = IFT_PPP;
sc->sc_if.if_hdrlen = PPP_HDRLEN;
sc->sc_if.if_ioctl = pppsioctl;
sc->sc_if.if_output = pppoutput;
sc->sc_if.if_start = ppp_ifstart;
sc->sc_if.if_rtrequest = p2p_rtrequest;
mq_init(&sc->sc_inq, IFQ_MAXLEN, IPL_NET);
ppp_pkt_list_init(&sc->sc_rawq, IFQ_MAXLEN);
if_attach(&sc->sc_if);
if_alloc_sadl(&sc->sc_if);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_PPP, PPP_HDRLEN);
#endif
NET_LOCK();
LIST_INSERT_HEAD(&ppp_softc_list, sc, sc_list);
NET_UNLOCK();
return (0);
}
int
ppp_clone_destroy(struct ifnet *ifp)
{
struct ppp_softc *sc = ifp->if_softc;
if (sc->sc_devp != NULL)
return (EBUSY);
NET_LOCK();
LIST_REMOVE(sc, sc_list);
NET_UNLOCK();
if_detach(ifp);
free(sc, M_DEVBUF, 0);
return (0);
}
/*
* Allocate a ppp interface unit and initialize it.
*/
struct ppp_softc *
pppalloc(pid_t pid)
{
int i;
struct ppp_softc *sc;
NET_LOCK();
LIST_FOREACH(sc, &ppp_softc_list, sc_list) {
if (sc->sc_xfer == pid) {
sc->sc_xfer = 0;
NET_UNLOCK();
return sc;
}
}
LIST_FOREACH(sc, &ppp_softc_list, sc_list) {
if (sc->sc_devp == NULL)
break;
}
NET_UNLOCK();
if (sc == NULL)
return NULL;
sc->sc_flags = 0;
sc->sc_mru = PPP_MRU;
sc->sc_relinq = NULL;
bzero((char *)&sc->sc_stats, sizeof(sc->sc_stats));
#ifdef VJC
sc->sc_comp = malloc(sizeof(struct slcompress), M_DEVBUF, M_NOWAIT);
if (sc->sc_comp)
sl_compress_init(sc->sc_comp);
#endif
#ifdef PPP_COMPRESS
sc->sc_xc_state = NULL;
sc->sc_rc_state = NULL;
#endif /* PPP_COMPRESS */
for (i = 0; i < NUM_NP; ++i)
sc->sc_npmode[i] = NPMODE_ERROR;
ml_init(&sc->sc_npqueue);
sc->sc_last_sent = sc->sc_last_recv = getuptime();
return sc;
}
/*
* Deallocate a ppp unit.
*/
void
pppdealloc(struct ppp_softc *sc)
{
struct ppp_pkt *pkt;
NET_LOCK();
if_down(&sc->sc_if);
sc->sc_if.if_flags &= ~IFF_RUNNING;
sc->sc_devp = NULL;
sc->sc_xfer = 0;
while ((pkt = ppp_pkt_dequeue(&sc->sc_rawq)) != NULL)
ppp_pkt_free(pkt);
mq_purge(&sc->sc_inq);
ml_purge(&sc->sc_npqueue);
m_freem(sc->sc_togo);
sc->sc_togo = NULL;
#ifdef PPP_COMPRESS
ppp_ccp_closed(sc);
sc->sc_xc_state = NULL;
sc->sc_rc_state = NULL;
#endif /* PPP_COMPRESS */
#if NBPFILTER > 0
if (sc->sc_pass_filt.bf_insns != 0) {
free(sc->sc_pass_filt.bf_insns, M_DEVBUF, 0);
sc->sc_pass_filt.bf_insns = 0;
sc->sc_pass_filt.bf_len = 0;
}
if (sc->sc_active_filt.bf_insns != 0) {
free(sc->sc_active_filt.bf_insns, M_DEVBUF, 0);
sc->sc_active_filt.bf_insns = 0;
sc->sc_active_filt.bf_len = 0;
}
#endif
#ifdef VJC
if (sc->sc_comp != 0) {
free(sc->sc_comp, M_DEVBUF, 0);
sc->sc_comp = 0;
}
#endif
NET_UNLOCK();
}
/*
* Ioctl routine for generic ppp devices.
*/
int
pppioctl(struct ppp_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
int s, error, flags, mru, npx;
u_int nb;
struct ppp_option_data *odp;
struct compressor **cp;
struct npioctl *npi;
time_t t;
#if NBPFILTER > 0
struct bpf_program *bp, *nbp;
struct bpf_insn *newcode, *oldcode;
int newcodelen;
#endif
#ifdef PPP_COMPRESS
u_char ccp_option[CCP_MAX_OPTION_LENGTH];
#endif
switch (cmd) {
case FIONREAD:
*(int *)data = mq_len(&sc->sc_inq);
break;
case PPPIOCGUNIT:
*(int *)data = sc->sc_unit; /* XXX */
break;
case PPPIOCGFLAGS:
*(u_int *)data = sc->sc_flags;
break;
case PPPIOCSFLAGS:
if ((error = suser(p)) != 0)
return (error);
flags = *(int *)data & SC_MASK;
#ifdef PPP_COMPRESS
if (sc->sc_flags & SC_CCP_OPEN && !(flags & SC_CCP_OPEN))
ppp_ccp_closed(sc);
#endif
s = splnet();
sc->sc_flags = (sc->sc_flags & ~SC_MASK) | flags;
splx(s);
break;
case PPPIOCSMRU:
if ((error = suser(p)) != 0)
return (error);
mru = *(int *)data;
if (mru >= PPP_MRU && mru <= PPP_MAXMRU)
sc->sc_mru = mru;
break;
case PPPIOCGMRU:
*(int *)data = sc->sc_mru;
break;
#ifdef VJC
case PPPIOCSMAXCID:
if ((error = suser(p)) != 0)
return (error);
if (sc->sc_comp)
sl_compress_setup(sc->sc_comp, *(int *)data);
break;
#endif
case PPPIOCXFERUNIT:
if ((error = suser(p)) != 0)
return (error);
sc->sc_xfer = p->p_p->ps_pid;
break;
#ifdef PPP_COMPRESS
case PPPIOCSCOMPRESS:
if ((error = suser(p)) != 0)
return (error);
odp = (struct ppp_option_data *) data;
nb = odp->length;
if (nb > sizeof(ccp_option))
nb = sizeof(ccp_option);
if ((error = copyin(odp->ptr, ccp_option, nb)) != 0)
return (error);
/* preliminary check on the length byte */
if (ccp_option[1] < 2)
return (EINVAL);
for (cp = ppp_compressors; *cp != NULL; ++cp)
if ((*cp)->compress_proto == ccp_option[0]) {
/*
* Found a handler for the protocol - try to allocate
* a compressor or decompressor.
*/
error = 0;
if (odp->transmit) {
if (sc->sc_xc_state != NULL) {
(*sc->sc_xcomp->comp_free)(
sc->sc_xc_state);
}
sc->sc_xcomp = *cp;
sc->sc_xc_state = (*cp)->comp_alloc(ccp_option,
nb);
if (sc->sc_xc_state == NULL) {
if (sc->sc_flags & SC_DEBUG)
printf(
"%s: comp_alloc failed\n",
sc->sc_if.if_xname);
error = ENOBUFS;
}
s = splnet();
sc->sc_flags &= ~SC_COMP_RUN;
splx(s);
} else {
if (sc->sc_rc_state != NULL) {
(*sc->sc_rcomp->decomp_free)(
sc->sc_rc_state);
}
sc->sc_rcomp = *cp;
sc->sc_rc_state = (*cp)->decomp_alloc(
ccp_option, nb);
if (sc->sc_rc_state == NULL) {
if (sc->sc_flags & SC_DEBUG) {
printf(
"%s: decomp_alloc failed\n",
sc->sc_if.if_xname);
}
error = ENOBUFS;
}
s = splnet();
sc->sc_flags &= ~SC_DECOMP_RUN;
splx(s);
}
return (error);
}
if (sc->sc_flags & SC_DEBUG) {
printf("%s: no compressor for [%x %x %x], %x\n",
sc->sc_if.if_xname, ccp_option[0], ccp_option[1],
ccp_option[2], nb);
}
return (EINVAL); /* no handler found */
#endif /* PPP_COMPRESS */
case PPPIOCGNPMODE:
case PPPIOCSNPMODE:
npi = (struct npioctl *)data;
switch (npi->protocol) {
case PPP_IP:
npx = NP_IP;
break;
default:
return EINVAL;
}
if (cmd == PPPIOCGNPMODE) {
npi->mode = sc->sc_npmode[npx];
} else {
if ((error = suser(p)) != 0)
return (error);
if (npi->mode != sc->sc_npmode[npx]) {
sc->sc_npmode[npx] = npi->mode;
if (npi->mode != NPMODE_QUEUE) {
ppp_requeue(sc);
(*sc->sc_start)(sc);
}
}
}
break;
case PPPIOCGIDLE:
t = getuptime();
((struct ppp_idle *)data)->xmit_idle = t - sc->sc_last_sent;
((struct ppp_idle *)data)->recv_idle = t - sc->sc_last_recv;
break;
#if NBPFILTER > 0
case PPPIOCSPASS:
case PPPIOCSACTIVE:
nbp = (struct bpf_program *) data;
if ((unsigned) nbp->bf_len > BPF_MAXINSNS)
return EINVAL;
newcodelen = nbp->bf_len * sizeof(struct bpf_insn);
if (nbp->bf_len != 0) {
newcode = mallocarray(nbp->bf_len,
sizeof(struct bpf_insn), M_DEVBUF, M_WAITOK);
if ((error = copyin((caddr_t)nbp->bf_insns,
(caddr_t)newcode, newcodelen)) != 0) {
free(newcode, M_DEVBUF, 0);
return error;
}
if (!bpf_validate(newcode, nbp->bf_len)) {
free(newcode, M_DEVBUF, 0);
return EINVAL;
}
} else
newcode = 0;
bp = (cmd == PPPIOCSPASS) ?
&sc->sc_pass_filt : &sc->sc_active_filt;
oldcode = bp->bf_insns;
s = splnet();
bp->bf_len = nbp->bf_len;
bp->bf_insns = newcode;
splx(s);
if (oldcode != 0)
free(oldcode, M_DEVBUF, 0);
break;
#endif
default:
return (-1);
}
return (0);
}
/*
* Process an ioctl request to the ppp network interface.
*/
static int
pppsioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ppp_softc *sc = ifp->if_softc;
struct ifaddr *ifa = (struct ifaddr *)data;
struct ifreq *ifr = (struct ifreq *)data;
struct ppp_stats *psp;
#ifdef PPP_COMPRESS
struct ppp_comp_stats *pcp;
#endif
int s = splnet(), error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_RUNNING) == 0)
ifp->if_flags &= ~IFF_UP;
break;
case SIOCSIFADDR:
if (ifa->ifa_addr->sa_family != AF_INET)
error = EAFNOSUPPORT;
break;
case SIOCSIFDSTADDR:
if (ifa->ifa_addr->sa_family != AF_INET)
error = EAFNOSUPPORT;
break;
case SIOCSIFMTU:
sc->sc_if.if_mtu = ifr->ifr_mtu;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
case SIOCGPPPSTATS:
psp = &((struct ifpppstatsreq *) data)->stats;
bzero(psp, sizeof(*psp));
psp->p = sc->sc_stats;
#if defined(VJC) && !defined(SL_NO_STATS)
if (sc->sc_comp) {
psp->vj.vjs_packets = sc->sc_comp->sls_packets;
psp->vj.vjs_compressed = sc->sc_comp->sls_compressed;
psp->vj.vjs_searches = sc->sc_comp->sls_searches;
psp->vj.vjs_misses = sc->sc_comp->sls_misses;
psp->vj.vjs_uncompressedin =
sc->sc_comp->sls_uncompressedin;
psp->vj.vjs_compressedin =
sc->sc_comp->sls_compressedin;
psp->vj.vjs_errorin = sc->sc_comp->sls_errorin;
psp->vj.vjs_tossed = sc->sc_comp->sls_tossed;
}
#endif /* VJC */
break;
#ifdef PPP_COMPRESS
case SIOCGPPPCSTATS:
pcp = &((struct ifpppcstatsreq *) data)->stats;
bzero(pcp, sizeof(*pcp));
if (sc->sc_xc_state != NULL)
(*sc->sc_xcomp->comp_stat)(sc->sc_xc_state, &pcp->c);
if (sc->sc_rc_state != NULL)
(*sc->sc_rcomp->decomp_stat)(sc->sc_rc_state, &pcp->d);
break;
#endif /* PPP_COMPRESS */
default:
error = ENOTTY;
}
splx(s);
return (error);
}
/*
* Queue a packet. Start transmission if not active.
* Packet is placed in Information field of PPP frame.
*/
int
pppoutput(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
struct rtentry *rtp)
{
struct ppp_softc *sc = ifp->if_softc;
int protocol, address, control;
u_char *cp;
int error;
enum NPmode mode;
int len;
if (sc->sc_devp == NULL || (ifp->if_flags & IFF_RUNNING) == 0
|| ((ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC)) {
error = ENETDOWN; /* sort of */
goto bad;
}
#ifdef DIAGNOSTIC
if (ifp->if_rdomain != rtable_l2(m0->m_pkthdr.ph_rtableid)) {
printf("%s: trying to send packet on wrong domain. "
"if %d vs. mbuf %d, AF %d\n", ifp->if_xname,
ifp->if_rdomain, rtable_l2(m0->m_pkthdr.ph_rtableid),
dst->sa_family);
}
#endif
/*
* Compute PPP header.
*/
switch (dst->sa_family) {
case AF_INET:
address = PPP_ALLSTATIONS;
control = PPP_UI;
protocol = PPP_IP;
mode = sc->sc_npmode[NP_IP];
break;
case AF_UNSPEC:
address = PPP_ADDRESS(dst->sa_data);
control = PPP_CONTROL(dst->sa_data);
protocol = PPP_PROTOCOL(dst->sa_data);
mode = NPMODE_PASS;
break;
default:
printf("%s: af%d not supported\n", ifp->if_xname,
dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
/*
* Drop this packet, or return an error, if necessary.
*/
if (mode == NPMODE_ERROR) {
error = ENETDOWN;
goto bad;
}
if (mode == NPMODE_DROP) {
error = 0;
goto bad;
}
/*
* Add PPP header. If no space in first mbuf, allocate another.
*/
M_PREPEND(m0, PPP_HDRLEN, M_DONTWAIT);
if (m0 == NULL) {
error = ENOBUFS;
goto bad;
}
cp = mtod(m0, u_char *);
*cp++ = address;
*cp++ = control;
*cp++ = protocol >> 8;
*cp++ = protocol & 0xff;
if ((m0->m_flags & M_PKTHDR) == 0)
panic("mbuf packet without packet header!");
len = m0->m_pkthdr.len;
if (sc->sc_flags & SC_LOG_OUTPKT) {
printf("%s output: ", ifp->if_xname);
pppdumpm(m0);
}
if ((protocol & 0x8000) == 0) {
#if NBPFILTER > 0
/*
* Apply the pass and active filters to the packet,
* but only if it is a data packet.
*/
*mtod(m0, u_char *) = 1; /* indicates outbound */
if (sc->sc_pass_filt.bf_insns != 0 &&
bpf_filter(sc->sc_pass_filt.bf_insns, (u_char *)m0,
len, 0) == 0) {
error = 0; /* drop this packet */
goto bad;
}
/*
* Update the time we sent the most recent packet.
*/
if (sc->sc_active_filt.bf_insns == 0 ||
bpf_filter(sc->sc_active_filt.bf_insns, (u_char *)m0,
len, 0))
sc->sc_last_sent = getuptime();
*mtod(m0, u_char *) = address;
#else
/*
* Update the time we sent the most recent packet.
*/
sc->sc_last_sent = getuptime();
#endif
}
#if NBPFILTER > 0
/* See if bpf wants to look at the packet. */
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
#endif
/*
* Put the packet on the appropriate queue.
*/
if (mode == NPMODE_QUEUE) {
/* XXX we should limit the number of packets on this queue */
ml_enqueue(&sc->sc_npqueue, m0);
} else {
error = ifq_enqueue(&sc->sc_if.if_snd, m0);
if (error) {
sc->sc_if.if_oerrors++;
sc->sc_stats.ppp_oerrors++;
return (error);
}
(*sc->sc_start)(sc);
}
ifp->if_opackets++;
ifp->if_obytes += len;
return (0);
bad:
m_freem(m0);
return (error);
}
/*
* After a change in the NPmode for some NP, move packets from the
* npqueue to the send queue or the fast queue as appropriate.
*/
static void
ppp_requeue(struct ppp_softc *sc)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
enum NPmode mode;
int error;
while ((m = ml_dequeue(&sc->sc_npqueue)) != NULL) {
switch (PPP_PROTOCOL(mtod(m, u_char *))) {
case PPP_IP:
mode = sc->sc_npmode[NP_IP];
break;
default:
mode = NPMODE_PASS;
}
switch (mode) {
case NPMODE_PASS:
error = ifq_enqueue(&sc->sc_if.if_snd, m);
if (error) {
sc->sc_if.if_oerrors++;
sc->sc_stats.ppp_oerrors++;
}
break;
case NPMODE_DROP:
case NPMODE_ERROR:
m_freem(m);
break;
case NPMODE_QUEUE:
ml_enqueue(&ml, m);
break;
}
}
sc->sc_npqueue = ml;
}
/*
* Transmitter has finished outputting some stuff;
*/
void
ppp_restart(struct ppp_softc *sc)
{
int s = splnet();
sc->sc_flags &= ~SC_TBUSY;
schednetisr(NETISR_PPP);
splx(s);
}
/*
* Get a packet to send.
*/
struct mbuf *
ppp_dequeue(struct ppp_softc *sc)
{
struct mbuf *m, *mp;
u_char *cp;
int address, control, protocol;
/*
* Grab a packet to send: first try the fast queue, then the
* normal queue.
*/
m = ifq_dequeue(&sc->sc_if.if_snd);
if (m == NULL)
return NULL;
++sc->sc_stats.ppp_opackets;
/*
* Extract the ppp header of the new packet.
* The ppp header will be in one mbuf.
*/
cp = mtod(m, u_char *);
address = PPP_ADDRESS(cp);
control = PPP_CONTROL(cp);
protocol = PPP_PROTOCOL(cp);
switch (protocol) {
case PPP_IP:
#ifdef VJC
/*
* If the packet is a TCP/IP packet, see if we can compress it.
*/
if ((sc->sc_flags & SC_COMP_TCP) && sc->sc_comp != NULL) {
struct ip *ip;
int type;
mp = m;
ip = (struct ip *)(cp + PPP_HDRLEN);
if (mp->m_len <= PPP_HDRLEN) {
mp = mp->m_next;
if (mp == NULL)
break;
ip = mtod(mp, struct ip *);
}
/*
* this code assumes the IP/TCP header is in one
* non-shared mbuf.
*/
if (ip->ip_p == IPPROTO_TCP) {
type = sl_compress_tcp(mp, ip, sc->sc_comp,
!(sc->sc_flags & SC_NO_TCP_CCID));
switch (type) {
case TYPE_UNCOMPRESSED_TCP:
protocol = PPP_VJC_UNCOMP;
break;
case TYPE_COMPRESSED_TCP:
protocol = PPP_VJC_COMP;
cp = mtod(m, u_char *);
cp[0] = address; /* header has moved */
cp[1] = control;
cp[2] = 0;
break;
}
/* update protocol in PPP header */
cp[3] = protocol;
}
}
#endif /* VJC */
break;
#ifdef PPP_COMPRESS
case PPP_CCP:
ppp_ccp(sc, m, 0);
break;
#endif /* PPP_COMPRESS */
}
#ifdef PPP_COMPRESS
if (protocol != PPP_LCP && protocol != PPP_CCP &&
sc->sc_xc_state && (sc->sc_flags & SC_COMP_RUN)) {
struct mbuf *mcomp = NULL;
int slen;
slen = 0;
for (mp = m; mp != NULL; mp = mp->m_next)
slen += mp->m_len;
(*sc->sc_xcomp->compress)(sc->sc_xc_state, &mcomp, m, slen,
(sc->sc_flags & SC_CCP_UP ?
sc->sc_if.if_mtu + PPP_HDRLEN : 0));
if (mcomp != NULL) {
if (sc->sc_flags & SC_CCP_UP) {
/* Send the compressed packet instead. */
m_freem(m);
m = mcomp;
cp = mtod(m, u_char *);
protocol = cp[3];
} else {
/*
* Can't transmit compressed packets until
* CCP is up.
*/
m_freem(mcomp);
}
}
}
#endif /* PPP_COMPRESS */
/*
* Compress the address/control and protocol, if possible.
*/
if (sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS &&
control == PPP_UI && protocol != PPP_ALLSTATIONS &&
protocol != PPP_LCP) {
/* can compress address/control */
m->m_data += 2;
m->m_len -= 2;
}
if (sc->sc_flags & SC_COMP_PROT && protocol < 0xFF) {
/* can compress protocol */
if (mtod(m, u_char *) == cp) {
cp[2] = cp[1]; /* move address/control up */
cp[1] = cp[0];
}
++m->m_data;
--m->m_len;
}
return m;
}
/*
* Software interrupt routine.
*/
void
pppintr(void)
{
struct ppp_softc *sc;
int s;
struct ppp_pkt *pkt;
struct mbuf *m;
NET_ASSERT_LOCKED();
LIST_FOREACH(sc, &ppp_softc_list, sc_list) {
if (!(sc->sc_flags & SC_TBUSY) &&
(!ifq_empty(&sc->sc_if.if_snd))) {
s = splnet();
sc->sc_flags |= SC_TBUSY;
splx(s);
(*sc->sc_start)(sc);
}
while ((pkt = ppp_pkt_dequeue(&sc->sc_rawq)) != NULL) {
m = ppp_pkt_mbuf(pkt);
if (m == NULL)
continue;
ppp_inproc(sc, m);
}
}
}
#ifdef PPP_COMPRESS
/*
* Handle a CCP packet. `rcvd' is 1 if the packet was received,
* 0 if it is about to be transmitted.
*/
static void
ppp_ccp(struct ppp_softc *sc, struct mbuf *m, int rcvd)
{
u_char *dp, *ep;
struct mbuf *mp;
int slen, s;
/*
* Get a pointer to the data after the PPP header.
*/
if (m->m_len <= PPP_HDRLEN) {
mp = m->m_next;
if (mp == NULL)
return;
dp = mtod(mp, u_char *);
} else {
mp = m;
dp = mtod(mp, u_char *) + PPP_HDRLEN;
}
ep = mtod(mp, u_char *) + mp->m_len;
if (dp + CCP_HDRLEN > ep)
return;
slen = CCP_LENGTH(dp);
if (dp + slen > ep) {
if (sc->sc_flags & SC_DEBUG) {
printf("if_ppp/ccp: not enough data in mbuf"
" (%p+%x > %p+%x)\n", dp, slen,
mtod(mp, u_char *), mp->m_len);
}
return;
}
switch (CCP_CODE(dp)) {
case CCP_CONFREQ:
case CCP_TERMREQ:
case CCP_TERMACK:
/* CCP must be going down - disable compression */
if (sc->sc_flags & SC_CCP_UP) {
s = splnet();
sc->sc_flags &=
~(SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
splx(s);
}
break;
case CCP_CONFACK:
if (sc->sc_flags & SC_CCP_OPEN &&
!(sc->sc_flags & SC_CCP_UP) &&
slen >= CCP_HDRLEN + CCP_OPT_MINLEN &&
slen >= CCP_OPT_LENGTH(dp + CCP_HDRLEN) + CCP_HDRLEN) {
if (!rcvd) {
/* we're agreeing to send compressed packets. */
if (sc->sc_xc_state != NULL &&
(*sc->sc_xcomp->comp_init)(sc->sc_xc_state,
dp + CCP_HDRLEN, slen - CCP_HDRLEN,
sc->sc_unit, 0, sc->sc_flags & SC_DEBUG)) {
s = splnet();
sc->sc_flags |= SC_COMP_RUN;
splx(s);
}
} else {
/* peer agrees to send compressed packets */
if (sc->sc_rc_state != NULL &&
(*sc->sc_rcomp->decomp_init)(
sc->sc_rc_state, dp + CCP_HDRLEN,
slen - CCP_HDRLEN, sc->sc_unit, 0,
sc->sc_mru, sc->sc_flags & SC_DEBUG)) {
s = splnet();
sc->sc_flags |= SC_DECOMP_RUN;
sc->sc_flags &=
~(SC_DC_ERROR | SC_DC_FERROR);
splx(s);
}
}
}
break;
case CCP_RESETACK:
if (sc->sc_flags & SC_CCP_UP) {
if (!rcvd) {
if (sc->sc_xc_state &&
(sc->sc_flags & SC_COMP_RUN)) {
(*sc->sc_xcomp->comp_reset)(
sc->sc_xc_state);
}
} else {
if (sc->sc_rc_state &&
(sc->sc_flags & SC_DECOMP_RUN)) {
(*sc->sc_rcomp->decomp_reset)(
sc->sc_rc_state);
s = splnet();
sc->sc_flags &= ~SC_DC_ERROR;
splx(s);
}
}
}
break;
}
}
/*
* CCP is down; free (de)compressor state if necessary.
*/
static void
ppp_ccp_closed(struct ppp_softc *sc)
{
if (sc->sc_xc_state) {
(*sc->sc_xcomp->comp_free)(sc->sc_xc_state);
sc->sc_xc_state = NULL;
}
if (sc->sc_rc_state) {
(*sc->sc_rcomp->decomp_free)(sc->sc_rc_state);
sc->sc_rc_state = NULL;
}
}
#endif /* PPP_COMPRESS */
/*
* PPP packet input routine.
* The caller has checked and removed the FCS and has inserted
* the address/control bytes and the protocol high byte if they
* were omitted.
*/
void
ppppktin(struct ppp_softc *sc, struct ppp_pkt *pkt, int lost)
{
pkt->p_hdr.ph_errmark = lost;
if (ppp_pkt_enqueue(&sc->sc_rawq, pkt) == 0)
schednetisr(NETISR_PPP);
}
/*
* Process a received PPP packet, doing decompression as necessary.
*/
#define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \
TYPE_UNCOMPRESSED_TCP)
static void
ppp_inproc(struct ppp_softc *sc, struct mbuf *m)
{
struct ifnet *ifp = &sc->sc_if;
int s, ilen, xlen, proto, rv;
u_char *cp, adrs, ctrl;
struct mbuf *mp, *dmp = NULL;
u_char *iphdr;
u_int hlen;
sc->sc_stats.ppp_ipackets++;
if (sc->sc_flags & SC_LOG_INPKT) {
ilen = 0;
for (mp = m; mp != NULL; mp = mp->m_next)
ilen += mp->m_len;
printf("%s: got %d bytes\n", ifp->if_xname, ilen);
pppdumpm(m);
}
cp = mtod(m, u_char *);
adrs = PPP_ADDRESS(cp);
ctrl = PPP_CONTROL(cp);
proto = PPP_PROTOCOL(cp);
if (m->m_flags & M_ERRMARK) {
m->m_flags &= ~M_ERRMARK;
s = splnet();
sc->sc_flags |= SC_VJ_RESET;
splx(s);
}
#ifdef PPP_COMPRESS
/*
* Decompress this packet if necessary, update the receiver's
* dictionary, or take appropriate action on a CCP packet.
*/
if (proto == PPP_COMP && sc->sc_rc_state &&
(sc->sc_flags & SC_DECOMP_RUN) && !(sc->sc_flags & SC_DC_ERROR) &&
!(sc->sc_flags & SC_DC_FERROR)) {
/* decompress this packet */
rv = (*sc->sc_rcomp->decompress)(sc->sc_rc_state, m, &dmp);
if (rv == DECOMP_OK) {
m_freem(m);
if (dmp == NULL) {
/*
* no error, but no decompressed packet
* produced
*/
return;
}
m = dmp;
cp = mtod(m, u_char *);
proto = PPP_PROTOCOL(cp);
} else {
/*
* An error has occurred in decompression.
* Pass the compressed packet up to pppd, which may
* take CCP down or issue a Reset-Req.
*/
if (sc->sc_flags & SC_DEBUG) {
printf("%s: decompress failed %d\n",
ifp->if_xname, rv);
}
s = splnet();
sc->sc_flags |= SC_VJ_RESET;
if (rv == DECOMP_ERROR)
sc->sc_flags |= SC_DC_ERROR;
else
sc->sc_flags |= SC_DC_FERROR;
splx(s);
}
} else {
if (sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN)) {
(*sc->sc_rcomp->incomp)(sc->sc_rc_state, m);
}
if (proto == PPP_CCP) {
ppp_ccp(sc, m, 1);
}
}
#endif
ilen = 0;
for (mp = m; mp != NULL; mp = mp->m_next)
ilen += mp->m_len;
#ifdef VJC
if (sc->sc_flags & SC_VJ_RESET) {
/*
* If we've missed a packet, we must toss subsequent compressed
* packets which don't have an explicit connection ID.
*/
if (sc->sc_comp)
sl_uncompress_tcp(NULL, 0, TYPE_ERROR, sc->sc_comp);
s = splnet();
sc->sc_flags &= ~SC_VJ_RESET;
splx(s);
}
/*
* See if we have a VJ-compressed packet to uncompress.
*/
if (proto == PPP_VJC_COMP) {
if ((sc->sc_flags & SC_REJ_COMP_TCP) || sc->sc_comp == 0)
goto bad;
xlen = sl_uncompress_tcp_core(cp + PPP_HDRLEN,
m->m_len - PPP_HDRLEN, ilen - PPP_HDRLEN,
TYPE_COMPRESSED_TCP, sc->sc_comp, &iphdr, &hlen);
if (xlen <= 0) {
if (sc->sc_flags & SC_DEBUG) {
printf("%s: VJ uncompress failed "
"on type comp\n", ifp->if_xname);
}
goto bad;
}
/* Copy the PPP and IP headers into a new mbuf. */
MGETHDR(mp, M_DONTWAIT, MT_DATA);
if (mp == NULL)
goto bad;
mp->m_len = 0;
mp->m_next = NULL;
if (hlen + PPP_HDRLEN > MHLEN) {
MCLGET(mp, M_DONTWAIT);
if (m_trailingspace(mp) < hlen + PPP_HDRLEN) {
m_freem(mp);
/* lose if big headers and no clusters */
goto bad;
}
}
if (m->m_flags & M_PKTHDR)
M_MOVE_HDR(mp, m);
cp = mtod(mp, u_char *);
cp[0] = adrs;
cp[1] = ctrl;
cp[2] = 0;
cp[3] = PPP_IP;
proto = PPP_IP;
bcopy(iphdr, cp + PPP_HDRLEN, hlen);
mp->m_len = hlen + PPP_HDRLEN;
/*
* Trim the PPP and VJ headers off the old mbuf
* and stick the new and old mbufs together.
*/
m->m_data += PPP_HDRLEN + xlen;
m->m_len -= PPP_HDRLEN + xlen;
if (m->m_len <= m_trailingspace(mp)) {
bcopy(mtod(m, u_char *),
mtod(mp, u_char *) + mp->m_len, m->m_len);
mp->m_len += m->m_len;
mp->m_next = m_free(m);
} else
mp->m_next = m;
m = mp;
ilen += hlen - xlen;
} else if (proto == PPP_VJC_UNCOMP) {
if ((sc->sc_flags & SC_REJ_COMP_TCP) || sc->sc_comp == 0)
goto bad;
xlen = sl_uncompress_tcp_core(cp + PPP_HDRLEN,
m->m_len - PPP_HDRLEN, ilen - PPP_HDRLEN,
TYPE_UNCOMPRESSED_TCP, sc->sc_comp, &iphdr, &hlen);
if (xlen < 0) {
if (sc->sc_flags & SC_DEBUG) {
printf("%s: VJ uncompress failed "
"on type uncomp\n", ifp->if_xname);
}
goto bad;
}
proto = PPP_IP;
cp[3] = PPP_IP;
}
#endif /* VJC */
m->m_pkthdr.len = ilen;
m->m_pkthdr.ph_ifidx = ifp->if_index;
/* mark incoming routing table */
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
if ((proto & 0x8000) == 0) {
#if NBPFILTER > 0
/*
* See whether we want to pass this packet, and
* if it counts as link activity.
*/
adrs = *mtod(m, u_char *); /* save address field */
*mtod(m, u_char *) = 0; /* indicate inbound */
if (sc->sc_pass_filt.bf_insns != 0 &&
bpf_filter(sc->sc_pass_filt.bf_insns, (u_char *) m,
ilen, 0) == 0) {
/* drop this packet */
m_freem(m);
return;
}
if (sc->sc_active_filt.bf_insns == 0 ||
bpf_filter(sc->sc_active_filt.bf_insns, (u_char *)m,
ilen, 0))
sc->sc_last_recv = getuptime();
*mtod(m, u_char *) = adrs;
#else
/*
* Record the time that we received this packet.
*/
sc->sc_last_recv = getuptime();
#endif
}
#if NBPFILTER > 0
/* See if bpf wants to look at the packet. */
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif
rv = 0;
switch (proto) {
case PPP_IP:
/*
* IP packet - take off the ppp header and pass it up to IP.
*/
if ((ifp->if_flags & IFF_UP) == 0 ||
sc->sc_npmode[NP_IP] != NPMODE_PASS) {
/* interface is down - drop the packet. */
m_freem(m);
return;
}
m->m_pkthdr.len -= PPP_HDRLEN;
m->m_data += PPP_HDRLEN;
m->m_len -= PPP_HDRLEN;
ipv4_input(ifp, m);
rv = 1;
break;
default:
/*
* Some other protocol - place on input queue for read().
*/
if (mq_enqueue(&sc->sc_inq, m) != 0) {
if_congestion();
rv = 0; /* failure */
} else
rv = 2; /* input queue */
break;
}
if (rv == 0) {
/* failure */
if (sc->sc_flags & SC_DEBUG)
printf("%s: input queue full\n", ifp->if_xname);
ifp->if_iqdrops++;
goto dropped;
}
ifp->if_ipackets++;
ifp->if_ibytes += ilen;
if (rv == 2)
(*sc->sc_ctlp)(sc);
return;
bad:
m_freem(m);
dropped:
sc->sc_if.if_ierrors++;
sc->sc_stats.ppp_ierrors++;
}
#define MAX_DUMP_BYTES 128
static void
pppdumpm(struct mbuf *m0)
{
char buf[3*MAX_DUMP_BYTES+4];
char *bp = buf;
struct mbuf *m;
static char digits[] = "0123456789abcdef";
for (m = m0; m; m = m->m_next) {
int l = m->m_len;
u_char *rptr = mtod(m, u_char *);
while (l--) {
if (bp > buf + sizeof(buf) - 4)
goto done;
/* convert byte to ascii hex */
*bp++ = digits[*rptr >> 4];
*bp++ = digits[*rptr++ & 0xf];
}
if (m->m_next) {
if (bp > buf + sizeof(buf) - 3)
goto done;
*bp++ = '|';
} else
*bp++ = ' ';
}
done:
if (m)
*bp++ = '>';
*bp = 0;
printf("%s\n", buf);
}
static void
ppp_ifstart(struct ifnet *ifp)
{
struct ppp_softc *sc;
sc = ifp->if_softc;
(*sc->sc_start)(sc);
}
void
ppp_pkt_list_init(struct ppp_pkt_list *pl, u_int limit)
{
mtx_init(&pl->pl_mtx, IPL_TTY);
pl->pl_head = pl->pl_tail = NULL;
pl->pl_count = 0;
pl->pl_limit = limit;
}
int
ppp_pkt_enqueue(struct ppp_pkt_list *pl, struct ppp_pkt *pkt)
{
int drop = 0;
mtx_enter(&pl->pl_mtx);
if (pl->pl_count < pl->pl_limit) {
if (pl->pl_tail == NULL)
pl->pl_head = pl->pl_tail = pkt;
else {
PKT_NEXTPKT(pl->pl_tail) = pkt;
pl->pl_tail = pkt;
}
PKT_NEXTPKT(pkt) = NULL;
pl->pl_count++;
} else
drop = 1;
mtx_leave(&pl->pl_mtx);
if (drop)
ppp_pkt_free(pkt);
return (drop);
}
struct ppp_pkt *
ppp_pkt_dequeue(struct ppp_pkt_list *pl)
{
struct ppp_pkt *pkt;
mtx_enter(&pl->pl_mtx);
pkt = pl->pl_head;
if (pkt != NULL) {
pl->pl_head = PKT_NEXTPKT(pkt);
if (pl->pl_head == NULL)
pl->pl_tail = NULL;
pl->pl_count--;
}
mtx_leave(&pl->pl_mtx);
return (pkt);
}
struct mbuf *
ppp_pkt_mbuf(struct ppp_pkt *pkt0)
{
extern struct pool ppp_pkts;
struct mbuf *m0 = NULL, **mp = &m0, *m;
struct ppp_pkt *pkt = pkt0;
size_t len = 0;
do {
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
goto fail;
MEXTADD(m, pkt, sizeof(*pkt), M_EXTWR,
MEXTFREE_POOL, &ppp_pkts);
m->m_data += sizeof(pkt->p_hdr);
m->m_len = PKT_LEN(pkt);
len += m->m_len;
*mp = m;
mp = &m->m_next;
pkt = PKT_NEXT(pkt);
} while (pkt != NULL);
m0->m_pkthdr.len = len;
if (pkt0->p_hdr.ph_errmark)
m0->m_flags |= M_ERRMARK;
return (m0);
fail:
m_freem(m0);
ppp_pkt_free(pkt0);
return (NULL);
}
#endif /* NPPP > 0 */
19
2
16
17
11
1
1
9
2
3
6
7
6
1
3
6
3
5
5
55
53
51
4
1
5
1
1
4
4
6
5
4
2
2
2
1
1
2
2
2
4
2
10
3
9
4
9
3
1
2
6
1
2
2
1
2
6
6
3
3
5
8
8
4
3
1
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
/* $OpenBSD: wsmux.c,v 1.56 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: wsmux.c,v 1.37 2005/04/30 03:47:12 augustss Exp $ */
/*
* Copyright (c) 1998, 2005 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Author: Lennart Augustsson <augustss@carlstedt.se>
* Carlstedt Research & Technology
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "wsmux.h"
#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmouse.h"
/*
* wscons mux device.
*
* The mux device is a collection of real mice and keyboards and acts as
* a merge point for all the events from the different real devices.
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/device.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wseventvar.h>
#include <dev/wscons/wsmuxvar.h>
#define WSMUX_MAXDEPTH 8
#ifdef WSMUX_DEBUG
#define DPRINTF(x) if (wsmuxdebug) printf x
#define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x
int wsmuxdebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
/*
* The wsmux pseudo device is used to multiplex events from several wsmouse,
* wskbd, and/or wsmux devices together.
* The devices connected together form a tree with muxes in the interior
* and real devices (mouse and kbd) at the leaves. The special case of
* a tree with one node (mux or other) is supported as well.
* Only the device at the root of the tree can be opened (if a non-root
* device is opened the subtree rooted at that point is severed from the
* containing tree). When the root is opened it allocates a wseventvar
* struct which all the nodes in the tree will send their events too.
* An ioctl() performed on the root is propagated to all the nodes.
* There are also ioctl() operations to add and remove nodes from a tree.
*/
int wsmux_mux_open(struct wsevsrc *, struct wseventvar *);
int wsmux_mux_close(struct wsevsrc *);
int wsmux_do_open(struct wsmux_softc *, struct wseventvar *);
void wsmux_do_close(struct wsmux_softc *);
#if NWSDISPLAY > 0
int wsmux_evsrc_set_display(struct device *, struct device *);
#else
#define wsmux_evsrc_set_display NULL
#endif
int wsmux_do_displayioctl(struct device *dev, u_long cmd, caddr_t data,
int flag, struct proc *p);
int wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *);
int wsmux_add_mux(int, struct wsmux_softc *);
int wsmux_depth(struct wsmux_softc *);
void wsmuxattach(int);
void wsmux_detach_sc_locked(struct wsmux_softc *, struct wsevsrc *);
struct wssrcops wsmux_srcops = {
.type = WSMUX_MUX,
.dopen = wsmux_mux_open,
.dclose = wsmux_mux_close,
.dioctl = wsmux_do_ioctl,
.ddispioctl = wsmux_do_displayioctl,
.dsetdisplay = wsmux_evsrc_set_display,
};
/*
* Lock used by wsmux_add_mux() to grant exclusive access to the tree of
* stacked wsmux devices.
*/
struct rwlock wsmux_tree_lock = RWLOCK_INITIALIZER("wsmuxtreelk");
/* From upper level */
void
wsmuxattach(int n)
{
}
/* Keep track of all muxes that have been allocated */
int nwsmux = 0;
struct wsmux_softc **wsmuxdevs = NULL;
/* Return mux n, create if necessary */
struct wsmux_softc *
wsmux_getmux(int n)
{
struct wsmux_softc *sc;
struct wsmux_softc **new, **old;
int i;
if (n >= WSMUX_MAXDEV)
return (NULL);
/* Make sure there is room for mux n in the table */
if (n >= nwsmux) {
old = wsmuxdevs;
new = mallocarray(n + 1, sizeof (*wsmuxdevs),
M_DEVBUF, M_NOWAIT);
if (new == NULL) {
printf("wsmux_getmux: no memory for mux %d\n", n);
return (NULL);
}
if (old != NULL)
bcopy(old, new, nwsmux * sizeof(*wsmuxdevs));
for (i = nwsmux; i < (n + 1); i++)
new[i] = NULL;
if (old != NULL)
free(old, M_DEVBUF, nwsmux * sizeof(*wsmuxdevs));
wsmuxdevs = new;
nwsmux = n + 1;
}
sc = wsmuxdevs[n];
if (sc == NULL) {
sc = wsmux_create("wsmux", n);
if (sc == NULL)
printf("wsmux: attach out of memory\n");
wsmuxdevs[n] = sc;
}
return (sc);
}
/*
* open() of the pseudo device from device table.
*/
int
wsmuxopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct wsmux_softc *sc;
struct wseventvar *evar;
int error, unit;
unit = minor(dev);
sc = wsmux_getmux(unit);
if (sc == NULL)
return (ENXIO);
DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc));
if ((flags & (FREAD | FWRITE)) == FWRITE) {
/* Not opening for read, only ioctl is available. */
return (0);
}
if (sc->sc_base.me_parent != NULL) {
/* Grab the mux out of the greedy hands of the parent mux. */
DPRINTF(("%s: detach\n", __func__));
wsmux_detach_sc(&sc->sc_base);
}
if (sc->sc_base.me_evp != NULL)
/* Already open. */
return (EBUSY);
evar = &sc->sc_base.me_evar;
if (wsevent_init(evar))
return (EBUSY);
#ifdef WSDISPLAY_COMPAT_RAWKBD
sc->sc_rawkbd = 0;
#endif
error = wsmux_do_open(sc, evar);
if (error)
wsevent_fini(evar);
return (error);
}
/*
* Open of a mux via the parent mux.
*/
int
wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
{
struct wsmux_softc *sc = (struct wsmux_softc *)me;
#ifdef DIAGNOSTIC
if (sc->sc_base.me_parent == NULL) {
printf("wsmux_mux_open: no parent\n");
return (EINVAL);
}
#endif
return (wsmux_do_open(sc, evar));
}
/* Common part of opening a mux. */
int
wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
{
struct wsevsrc *me;
#ifdef DIAGNOSTIC
int error;
#endif
/* The device could already be attached to a mux. */
if (sc->sc_base.me_evp != NULL)
return (EBUSY);
sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
/* Open all children. */
rw_enter_read(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
DPRINTF(("%s: %s: m=%p dev=%s\n", __func__,
sc->sc_base.me_dv.dv_xname, me,
me->me_dv.dv_xname));
#ifdef DIAGNOSTIC
if (me->me_evp != NULL) {
printf("wsmuxopen: dev already in use\n");
continue;
}
if (me->me_parent != sc) {
printf("wsmux_do_open: bad child=%p\n", me);
continue;
}
error = wsevsrc_open(me, evar);
if (error) {
DPRINTF(("%s: open failed %d\n", __func__, error));
}
#else
/* ignore errors, failing children will not be marked open */
(void)wsevsrc_open(me, evar);
#endif
}
rw_exit_read(&sc->sc_lock);
return (0);
}
/*
* close() of the pseudo device from device table.
*/
int
wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct wsmux_softc *sc =
(struct wsmux_softc *)wsmuxdevs[minor(dev)];
struct wseventvar *evar = sc->sc_base.me_evp;
if ((flags & (FREAD | FWRITE)) == FWRITE)
/* Not open for read */
return (0);
wsmux_do_close(sc);
sc->sc_base.me_evp = NULL;
wsevent_fini(evar);
return (0);
}
/*
* Close of a mux via the parent mux.
*/
int
wsmux_mux_close(struct wsevsrc *me)
{
struct wsmux_softc *sc = (struct wsmux_softc *)me;
wsmux_do_close(sc);
sc->sc_base.me_evp = NULL;
return (0);
}
/* Common part of closing a mux. */
void
wsmux_do_close(struct wsmux_softc *sc)
{
struct wsevsrc *me;
DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc));
/* Close all the children. */
rw_enter_read(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
DPRINTF(("%s %s: m=%p dev=%s\n", __func__,
sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
#ifdef DIAGNOSTIC
if (me->me_parent != sc) {
printf("wsmuxclose: bad child=%p\n", me);
continue;
}
#endif
(void)wsevsrc_close(me);
}
rw_exit_read(&sc->sc_lock);
}
/*
* read() of the pseudo device from device table.
*/
int
wsmuxread(dev_t dev, struct uio *uio, int flags)
{
struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
struct wseventvar *evar;
int error;
evar = sc->sc_base.me_evp;
if (evar == NULL) {
#ifdef DIAGNOSTIC
/* XXX can we get here? */
printf("wsmuxread: not open\n");
#endif
return (EINVAL);
}
DPRINTFN(5, ("%s: %s event read evar=%p\n", __func__,
sc->sc_base.me_dv.dv_xname, evar));
error = wsevent_read(evar, uio, flags);
DPRINTFN(5, ("%s: %s event read ==> error=%d\n", __func__,
sc->sc_base.me_dv.dv_xname, error));
return (error);
}
/*
* ioctl of the pseudo device from device table.
*/
int
wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
return wsmux_do_ioctl(&wsmuxdevs[minor(dev)]->sc_base.me_dv, cmd, data, flag, p);
}
/*
* ioctl of a mux via the parent mux, continuation of wsmuxioctl().
*/
int
wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmux_softc *sc = (struct wsmux_softc *)dv;
struct wsevsrc *me;
int error, ok;
int s, put, get, n;
struct wseventvar *evar;
struct wscons_event *ev;
struct wsmux_device_list *l;
DPRINTF(("%s: %s: enter sc=%p, cmd=%08lx\n", __func__,
sc->sc_base.me_dv.dv_xname, sc, cmd));
switch (cmd) {
case WSMUXIO_INJECTEVENT:
case WSMUXIO_ADD_DEVICE:
case WSMUXIO_REMOVE_DEVICE:
#ifdef WSDISPLAY_COMPAT_RAWKBD
case WSKBDIO_SETMODE:
#endif
if ((flag & FWRITE) == 0)
return (EACCES);
}
switch (cmd) {
case WSMUXIO_INJECTEVENT:
/* Inject an event, e.g., from moused. */
DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
evar = sc->sc_base.me_evp;
if (evar == NULL) {
/* No event sink, so ignore it. */
DPRINTF(("%s: event ignored\n", __func__));
return (0);
}
s = spltty();
get = evar->get;
put = evar->put;
ev = &evar->q[put];
if (++put % WSEVENT_QSIZE == get) {
put--;
splx(s);
return (ENOSPC);
}
if (put >= WSEVENT_QSIZE)
put = 0;
*ev = *(struct wscons_event *)data;
nanotime(&ev->time);
evar->put = put;
WSEVENT_WAKEUP(evar);
splx(s);
return (0);
case WSMUXIO_ADD_DEVICE:
#define d ((struct wsmux_device *)data)
DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
d->type, d->idx));
if (d->idx < 0)
return (ENXIO);
switch (d->type) {
#if NWSMOUSE > 0
case WSMUX_MOUSE:
return (wsmouse_add_mux(d->idx, sc));
#endif
#if NWSKBD > 0
case WSMUX_KBD:
return (wskbd_add_mux(d->idx, sc));
#endif
case WSMUX_MUX:
return (wsmux_add_mux(d->idx, sc));
default:
return (EINVAL);
}
case WSMUXIO_REMOVE_DEVICE:
DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
d->type, d->idx));
/* Locate the device */
rw_enter_write(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
if (me->me_ops->type == d->type &&
me->me_dv.dv_unit == d->idx) {
DPRINTF(("%s: detach\n", __func__));
wsmux_detach_sc_locked(sc, me);
rw_exit_write(&sc->sc_lock);
return (0);
}
}
rw_exit_write(&sc->sc_lock);
return (EINVAL);
#undef d
case WSMUXIO_LIST_DEVICES:
DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
l = (struct wsmux_device_list *)data;
n = 0;
rw_enter_read(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
if (n >= WSMUX_MAXDEV)
break;
l->devices[n].type = me->me_ops->type;
l->devices[n].idx = me->me_dv.dv_unit;
n++;
}
rw_exit_read(&sc->sc_lock);
l->ndevices = n;
return (0);
#ifdef WSDISPLAY_COMPAT_RAWKBD
case WSKBDIO_SETMODE:
sc->sc_rawkbd = *(int *)data;
DPRINTF(("%s: save rawkbd = %d\n", __func__, sc->sc_rawkbd));
break;
#endif
case FIONBIO:
DPRINTF(("%s: FIONBIO\n", sc->sc_base.me_dv.dv_xname));
return (0);
case FIOASYNC:
DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
evar->async = *(int *)data != 0;
return (0);
case FIOGETOWN:
case TIOCGPGRP:
DPRINTF(("%s: getown (%lu)\n", sc->sc_base.me_dv.dv_xname,
cmd));
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
sigio_getown(&evar->sigio, cmd, data);
return (0);
case FIOSETOWN:
case TIOCSPGRP:
DPRINTF(("%s: setown (%lu)\n", sc->sc_base.me_dv.dv_xname,
cmd));
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
return (sigio_setown(&evar->sigio, cmd, data));
default:
DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
break;
}
if (sc->sc_base.me_evp == NULL
#if NWSDISPLAY > 0
&& sc->sc_displaydv == NULL
#endif
)
return (EACCES);
/*
* If children are attached: return 0 if any of the ioctl() succeeds,
* otherwise the last error.
*/
error = ENOTTY;
ok = 0;
rw_enter_read(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
#ifdef DIAGNOSTIC
/* XXX check evp? */
if (me->me_parent != sc) {
printf("wsmux_do_ioctl: bad child %p\n", me);
continue;
}
#endif
error = wsevsrc_ioctl(me, cmd, data, flag, p);
DPRINTF(("%s: %s: me=%p dev=%s ==> %d\n", __func__,
sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
error));
if (!error)
ok = 1;
}
rw_exit_read(&sc->sc_lock);
if (ok)
error = 0;
return (error);
}
int
wsmuxkqfilter(dev_t dev, struct knote *kn)
{
struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
if (sc->sc_base.me_evp == NULL)
return (ENXIO);
return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
}
/*
* Add mux unit as a child to muxsc.
*/
int
wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
{
struct wsmux_softc *sc, *m;
int error;
int depth = 0;
sc = wsmux_getmux(unit);
if (sc == NULL)
return (ENXIO);
rw_enter_write(&wsmux_tree_lock);
DPRINTF(("%s: %s(%p) to %s(%p)\n", __func__,
sc->sc_base.me_dv.dv_xname, sc,
muxsc->sc_base.me_dv.dv_xname, muxsc));
if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) {
error = EBUSY;
goto out;
}
/* The mux we are adding must not be an ancestor of itself. */
for (m = muxsc; m != NULL; m = m->sc_base.me_parent) {
if (m == sc) {
error = EINVAL;
goto out;
}
depth++;
}
/*
* Limit the number of stacked wsmux devices to avoid exhausting
* the kernel stack during wsmux_do_open().
*/
if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH) {
error = EBUSY;
goto out;
}
error = wsmux_attach_sc(muxsc, &sc->sc_base);
out:
rw_exit_write(&wsmux_tree_lock);
return (error);
}
/* Create a new mux softc. */
struct wsmux_softc *
wsmux_create(const char *name, int unit)
{
struct wsmux_softc *sc;
DPRINTF(("%s: allocating\n", __func__));
sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO);
if (sc == NULL)
return (NULL);
TAILQ_INIT(&sc->sc_cld);
rw_init_flags(&sc->sc_lock, "wsmuxlk", RWL_DUPOK);
snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
"%s%d", name, unit);
sc->sc_base.me_dv.dv_unit = unit;
sc->sc_base.me_ops = &wsmux_srcops;
sc->sc_kbd_layout = KB_NONE;
return (sc);
}
/* Attach me as a child to sc. */
int
wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
{
int error;
if (sc == NULL)
return (EINVAL);
rw_enter_write(&sc->sc_lock);
DPRINTF(("%s: %s(%p): type=%d\n", __func__,
sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
#ifdef DIAGNOSTIC
if (me->me_parent != NULL) {
rw_exit_write(&sc->sc_lock);
printf("wsmux_attach_sc: busy\n");
return (EBUSY);
}
#endif
me->me_parent = sc;
TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
error = 0;
#if NWSDISPLAY > 0
if (sc->sc_displaydv != NULL) {
/* This is a display mux, so attach the new device to it. */
DPRINTF(("%s: %s: set display %p\n", __func__,
sc->sc_base.me_dv.dv_xname, sc->sc_displaydv));
if (me->me_ops->dsetdisplay != NULL) {
error = wsevsrc_set_display(me, sc->sc_displaydv);
/* Ignore that the console already has a display. */
if (error == EBUSY)
error = 0;
if (!error) {
#ifdef WSDISPLAY_COMPAT_RAWKBD
DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
me->me_dv.dv_xname, sc->sc_rawkbd));
(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
&sc->sc_rawkbd, FWRITE, 0);
#endif
}
}
}
#endif
if (sc->sc_base.me_evp != NULL) {
/* Mux is open, try to open the subdevice. */
error = wsevsrc_open(me, sc->sc_base.me_evp);
} else {
/* Mux is closed, ensure that the subdevice is also closed. */
if (me->me_evp != NULL)
error = EBUSY;
}
if (error) {
me->me_parent = NULL;
TAILQ_REMOVE(&sc->sc_cld, me, me_next);
}
rw_exit_write(&sc->sc_lock);
DPRINTF(("%s: %s(%p) done, error=%d\n", __func__,
sc->sc_base.me_dv.dv_xname, sc, error));
return (error);
}
/* Remove me from the parent. */
void
wsmux_detach_sc(struct wsevsrc *me)
{
struct wsmux_softc *sc = me->me_parent;
if (sc == NULL) {
printf("wsmux_detach_sc: %s has no parent\n",
me->me_dv.dv_xname);
return;
}
rw_enter_write(&sc->sc_lock);
wsmux_detach_sc_locked(sc, me);
rw_exit_write(&sc->sc_lock);
}
void
wsmux_detach_sc_locked(struct wsmux_softc *sc, struct wsevsrc *me)
{
rw_assert_wrlock(&sc->sc_lock);
DPRINTF(("%s: %s(%p) parent=%p\n", __func__,
me->me_dv.dv_xname, me, sc));
if (me->me_parent != sc) {
/* Device detached or attached to another mux while sleeping. */
return;
}
#if NWSDISPLAY > 0
if (sc->sc_displaydv != NULL) {
if (me->me_ops->dsetdisplay != NULL)
/* ignore error, there's nothing we can do */
(void)wsevsrc_set_display(me, NULL);
} else
#endif
if (me->me_evp != NULL) {
DPRINTF(("%s: close\n", __func__));
/* mux device is open, so close multiplexee */
(void)wsevsrc_close(me);
}
TAILQ_REMOVE(&sc->sc_cld, me, me_next);
me->me_parent = NULL;
DPRINTF(("%s: done sc=%p\n", __func__, sc));
}
/*
* Display ioctl() of a mux via the parent mux.
*/
int
wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmux_softc *sc = (struct wsmux_softc *)dv;
struct wsevsrc *me;
int error, ok;
DPRINTF(("%s: %s: sc=%p, cmd=%08lx\n", __func__,
sc->sc_base.me_dv.dv_xname, sc, cmd));
#ifdef WSDISPLAY_COMPAT_RAWKBD
if (cmd == WSKBDIO_SETMODE) {
sc->sc_rawkbd = *(int *)data;
DPRINTF(("%s: rawkbd = %d\n", __func__, sc->sc_rawkbd));
}
#endif
/*
* Return 0 if any of the ioctl() succeeds, otherwise the last error.
* Return -1 if no mux component accepts the ioctl.
*/
error = -1;
ok = 0;
rw_enter_read(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
DPRINTF(("%s: me=%p\n", __func__, me));
#ifdef DIAGNOSTIC
if (me->me_parent != sc) {
printf("wsmux_displayioctl: bad child %p\n", me);
continue;
}
#endif
if (me->me_ops->ddispioctl != NULL) {
error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
DPRINTF(("%s: me=%p dev=%s ==> %d\n", __func__,
me, me->me_dv.dv_xname, error));
if (!error)
ok = 1;
}
}
rw_exit_read(&sc->sc_lock);
if (ok)
error = 0;
return (error);
}
#if NWSDISPLAY > 0
/*
* Set display of a mux via the parent mux.
*/
int
wsmux_evsrc_set_display(struct device *dv, struct device *displaydv)
{
struct wsmux_softc *sc = (struct wsmux_softc *)dv;
DPRINTF(("%s: %s: displaydv=%p\n", __func__,
sc->sc_base.me_dv.dv_xname, displaydv));
if (displaydv != NULL) {
if (sc->sc_displaydv != NULL)
return (EBUSY);
} else {
if (sc->sc_displaydv == NULL)
return (ENXIO);
}
return wsmux_set_display(sc, displaydv);
}
int
wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
{
struct device *odisplaydv;
struct wsevsrc *me;
struct wsmux_softc *nsc = displaydv ? sc : NULL;
int error, ok;
rw_enter_read(&sc->sc_lock);
odisplaydv = sc->sc_displaydv;
sc->sc_displaydv = displaydv;
if (displaydv) {
DPRINTF(("%s: connecting to %s\n",
sc->sc_base.me_dv.dv_xname, displaydv->dv_xname));
}
ok = 0;
error = 0;
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
#ifdef DIAGNOSTIC
if (me->me_parent != sc) {
printf("wsmux_set_display: bad child parent %p\n", me);
continue;
}
#endif
if (me->me_ops->dsetdisplay != NULL) {
error = wsevsrc_set_display(me,
nsc ? nsc->sc_displaydv : NULL);
DPRINTF(("%s: m=%p dev=%s error=%d\n", __func__,
me, me->me_dv.dv_xname, error));
if (!error) {
ok = 1;
#ifdef WSDISPLAY_COMPAT_RAWKBD
DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
me->me_dv.dv_xname, sc->sc_rawkbd));
(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
&sc->sc_rawkbd, FWRITE, 0);
#endif
}
}
}
if (ok)
error = 0;
if (displaydv == NULL) {
DPRINTF(("%s: disconnecting from %s\n",
sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname));
}
rw_exit_read(&sc->sc_lock);
return (error);
}
#endif /* NWSDISPLAY > 0 */
uint32_t
wsmux_get_layout(struct wsmux_softc *sc)
{
return sc->sc_kbd_layout;
}
void
wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout)
{
if ((layout & (KB_DEFAULT | KB_NOENCODING)) == 0)
sc->sc_kbd_layout = layout;
}
/*
* Returns the depth of the longest chain of nested wsmux devices starting
* from sc.
*/
int
wsmux_depth(struct wsmux_softc *sc)
{
struct wsevsrc *me;
int depth;
int maxdepth = 0;
rw_assert_anylock(&wsmux_tree_lock);
rw_enter_read(&sc->sc_lock);
TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
if (me->me_ops->type != WSMUX_MUX)
continue;
depth = wsmux_depth((struct wsmux_softc *)me);
if (depth > maxdepth)
maxdepth = depth;
}
rw_exit_read(&sc->sc_lock);
return (maxdepth + 1);
}
50
1
33
1
4
14
10
2
1
4
14
2
1
1
2
3
5
2
32
37
4
34
4
4
2
2
21
21
13
13
13
13
28
100
80
20
28
74
14
6
2
6
4
168
158
1
4
3
8
118
50
12
2
2
2
1
3
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
/* $OpenBSD: udp_usrreq.c,v 1.302 2022/09/05 14:56:09 bluhm Exp $ */
/* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/domain.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#ifdef IPSEC
#include <netinet/ip_ipsp.h>
#include <netinet/ip_esp.h>
#endif
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h>
#include <netinet6/ip6protosw.h>
#endif /* INET6 */
#include "pf.h"
#if NPF > 0
#include <net/pfvar.h>
#endif
#ifdef PIPEX
#include <netinet/if_ether.h>
#include <net/pipex.h>
#endif
/*
* UDP protocol implementation.
* Per RFC 768, August, 1980.
*/
int udpcksum = 1;
u_int udp_sendspace = 9216; /* really max datagram size */
u_int udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
/* 40 1K datagrams */
const struct pr_usrreqs udp_usrreqs = {
.pru_attach = udp_attach,
.pru_detach = udp_detach,
.pru_lock = udp_lock,
.pru_unlock = udp_unlock,
.pru_bind = udp_bind,
.pru_connect = udp_connect,
.pru_disconnect = udp_disconnect,
.pru_shutdown = udp_shutdown,
.pru_send = udp_send,
.pru_abort = udp_abort,
.pru_control = in_control,
.pru_sockaddr = in_sockaddr,
.pru_peeraddr = in_peeraddr,
};
#ifdef INET6
const struct pr_usrreqs udp6_usrreqs = {
.pru_attach = udp_attach,
.pru_detach = udp_detach,
.pru_lock = udp_lock,
.pru_unlock = udp_unlock,
.pru_bind = udp_bind,
.pru_connect = udp_connect,
.pru_disconnect = udp_disconnect,
.pru_shutdown = udp_shutdown,
.pru_send = udp_send,
.pru_abort = udp_abort,
.pru_control = in6_control,
.pru_sockaddr = in6_sockaddr,
.pru_peeraddr = in6_peeraddr,
};
#endif
const struct sysctl_bounded_args udpctl_vars[] = {
{ UDPCTL_CHECKSUM, &udpcksum, 0, 1 },
{ UDPCTL_RECVSPACE, &udp_recvspace, 0, INT_MAX },
{ UDPCTL_SENDSPACE, &udp_sendspace, 0, INT_MAX },
};
struct inpcbtable udbtable;
struct cpumem *udpcounters;
void udp_sbappend(struct inpcb *, struct mbuf *, struct ip *,
struct ip6_hdr *, int, struct udphdr *, struct sockaddr *,
u_int32_t);
int udp_output(struct inpcb *, struct mbuf *, struct mbuf *, struct mbuf *);
void udp_notify(struct inpcb *, int);
int udp_sysctl_udpstat(void *, size_t *, void *);
#ifndef UDB_INITIAL_HASH_SIZE
#define UDB_INITIAL_HASH_SIZE 128
#endif
void
udp_init(void)
{
udpcounters = counters_alloc(udps_ncounters);
in_pcbinit(&udbtable, UDB_INITIAL_HASH_SIZE);
}
int
udp_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
int iphlen = *offp;
struct ip *ip = NULL;
struct udphdr *uh;
struct inpcb *inp = NULL;
struct ip save_ip;
int len;
u_int16_t savesum;
union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef INET6
struct sockaddr_in6 sin6;
#endif /* INET6 */
} srcsa, dstsa;
struct ip6_hdr *ip6 = NULL;
u_int32_t ipsecflowinfo = 0;
udpstat_inc(udps_ipackets);
IP6_EXTHDR_GET(uh, struct udphdr *, m, iphlen, sizeof(struct udphdr));
if (!uh) {
udpstat_inc(udps_hdrops);
return IPPROTO_DONE;
}
/* Check for illegal destination port 0 */
if (uh->uh_dport == 0) {
udpstat_inc(udps_noport);
goto bad;
}
/*
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
len = ntohs((u_int16_t)uh->uh_ulen);
switch (af) {
case AF_INET:
if (m->m_pkthdr.len - iphlen != len) {
if (len > (m->m_pkthdr.len - iphlen) ||
len < sizeof(struct udphdr)) {
udpstat_inc(udps_badlen);
goto bad;
}
m_adj(m, len - (m->m_pkthdr.len - iphlen));
}
ip = mtod(m, struct ip *);
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
save_ip = *ip;
break;
#ifdef INET6
case AF_INET6:
/* jumbograms */
if (len == 0 && m->m_pkthdr.len - iphlen > 0xffff)
len = m->m_pkthdr.len - iphlen;
if (len != m->m_pkthdr.len - iphlen) {
udpstat_inc(udps_badlen);
goto bad;
}
ip6 = mtod(m, struct ip6_hdr *);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
/*
* Checksum extended UDP header and data.
* from W.R.Stevens: check incoming udp cksums even if
* udpcksum is not set.
*/
savesum = uh->uh_sum;
if (uh->uh_sum == 0) {
udpstat_inc(udps_nosum);
#ifdef INET6
/*
* In IPv6, the UDP checksum is ALWAYS used.
*/
if (ip6)
goto bad;
#endif /* INET6 */
} else {
if ((m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_OK) == 0) {
if (m->m_pkthdr.csum_flags & M_UDP_CSUM_IN_BAD) {
udpstat_inc(udps_badsum);
goto bad;
}
udpstat_inc(udps_inswcsum);
if (ip)
uh->uh_sum = in4_cksum(m, IPPROTO_UDP,
iphlen, len);
#ifdef INET6
else if (ip6)
uh->uh_sum = in6_cksum(m, IPPROTO_UDP,
iphlen, len);
#endif /* INET6 */
if (uh->uh_sum != 0) {
udpstat_inc(udps_badsum);
goto bad;
}
}
}
#ifdef IPSEC
if (udpencap_enable && udpencap_port && esp_enable &&
#if NPF > 0
!(m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) &&
#endif
uh->uh_dport == htons(udpencap_port)) {
u_int32_t spi;
int skip = iphlen + sizeof(struct udphdr);
if (m->m_pkthdr.len - skip < sizeof(u_int32_t)) {
/* packet too short */
m_freem(m);
return IPPROTO_DONE;
}
m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
/*
* decapsulate if the SPI is not zero, otherwise pass
* to userland
*/
if (spi != 0) {
int protoff;
if ((m = *mp = m_pullup(m, skip)) == NULL) {
udpstat_inc(udps_hdrops);
return IPPROTO_DONE;
}
/* remove the UDP header */
bcopy(mtod(m, u_char *),
mtod(m, u_char *) + sizeof(struct udphdr), iphlen);
m_adj(m, sizeof(struct udphdr));
skip -= sizeof(struct udphdr);
espstat_inc(esps_udpencin);
protoff = af == AF_INET ? offsetof(struct ip, ip_p) :
offsetof(struct ip6_hdr, ip6_nxt);
return ipsec_common_input(mp, skip, protoff,
af, IPPROTO_ESP, 1);
}
}
#endif /* IPSEC */
switch (af) {
case AF_INET:
bzero(&srcsa, sizeof(struct sockaddr_in));
srcsa.sin.sin_len = sizeof(struct sockaddr_in);
srcsa.sin.sin_family = AF_INET;
srcsa.sin.sin_port = uh->uh_sport;
srcsa.sin.sin_addr = ip->ip_src;
bzero(&dstsa, sizeof(struct sockaddr_in));
dstsa.sin.sin_len = sizeof(struct sockaddr_in);
dstsa.sin.sin_family = AF_INET;
dstsa.sin.sin_port = uh->uh_dport;
dstsa.sin.sin_addr = ip->ip_dst;
break;
#ifdef INET6
case AF_INET6:
bzero(&srcsa, sizeof(struct sockaddr_in6));
srcsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
srcsa.sin6.sin6_family = AF_INET6;
srcsa.sin6.sin6_port = uh->uh_sport;
#if 0 /*XXX inbound flowinfo */
srcsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ip6->ip6_flow;
#endif
/* KAME hack: recover scopeid */
in6_recoverscope(&srcsa.sin6, &ip6->ip6_src);
bzero(&dstsa, sizeof(struct sockaddr_in6));
dstsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
dstsa.sin6.sin6_family = AF_INET6;
dstsa.sin6.sin6_port = uh->uh_dport;
#if 0 /*XXX inbound flowinfo */
dstsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ip6->ip6_flow;
#endif
/* KAME hack: recover scopeid */
in6_recoverscope(&dstsa.sin6, &ip6->ip6_dst);
break;
#endif /* INET6 */
}
if (m->m_flags & (M_BCAST|M_MCAST)) {
SIMPLEQ_HEAD(, inpcb) inpcblist;
/*
* Deliver a multicast or broadcast datagram to *all* sockets
* for which the local and remote addresses and ports match
* those of the incoming datagram. This allows more than
* one process to receive multi/broadcasts on the same port.
* (This really ought to be done for unicast datagrams as
* well, but that would cause problems with existing
* applications that open both address-specific sockets and
* a wildcard socket listening to the same port -- they would
* end up receiving duplicates of every unicast datagram.
* Those applications open the multiple sockets to overcome an
* inadequacy of the UDP socket interface, but for backwards
* compatibility we avoid the problem here rather than
* fixing the interface. Maybe 4.5BSD will remedy this?)
*/
/*
* Locate pcb(s) for datagram.
* (Algorithm copied from raw_intr().)
*/
SIMPLEQ_INIT(&inpcblist);
rw_enter_write(&udbtable.inpt_notify);
mtx_enter(&udbtable.inpt_mtx);
TAILQ_FOREACH(inp, &udbtable.inpt_queue, inp_queue) {
if (inp->inp_socket->so_state & SS_CANTRCVMORE)
continue;
#ifdef INET6
/* don't accept it if AF does not match */
if (ip6 && !(inp->inp_flags & INP_IPV6))
continue;
if (!ip6 && (inp->inp_flags & INP_IPV6))
continue;
#endif
if (rtable_l2(inp->inp_rtableid) !=
rtable_l2(m->m_pkthdr.ph_rtableid))
continue;
if (inp->inp_lport != uh->uh_dport)
continue;
#ifdef INET6
if (ip6) {
if (inp->inp_ip6_minhlim &&
inp->inp_ip6_minhlim > ip6->ip6_hlim)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
if (!IN6_ARE_ADDR_EQUAL(
&inp->inp_laddr6, &ip6->ip6_dst))
continue;
} else
#endif /* INET6 */
{
if (inp->inp_ip_minttl &&
inp->inp_ip_minttl > ip->ip_ttl)
continue;
if (inp->inp_laddr.s_addr != INADDR_ANY) {
if (inp->inp_laddr.s_addr !=
ip->ip_dst.s_addr)
continue;
}
}
#ifdef INET6
if (ip6) {
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
if (!IN6_ARE_ADDR_EQUAL(
&inp->inp_faddr6, &ip6->ip6_src) ||
inp->inp_fport != uh->uh_sport)
continue;
} else
#endif /* INET6 */
if (inp->inp_faddr.s_addr != INADDR_ANY) {
if (inp->inp_faddr.s_addr !=
ip->ip_src.s_addr ||
inp->inp_fport != uh->uh_sport)
continue;
}
in_pcbref(inp);
SIMPLEQ_INSERT_TAIL(&inpcblist, inp, inp_notify);
/*
* Don't look for additional matches if this one does
* not have either the SO_REUSEPORT or SO_REUSEADDR
* socket options set. This heuristic avoids searching
* through all pcbs in the common case of a non-shared
* port. It assumes that an application will never
* clear these options after setting them.
*/
if ((inp->inp_socket->so_options & (SO_REUSEPORT |
SO_REUSEADDR)) == 0)
break;
}
mtx_leave(&udbtable.inpt_mtx);
if (SIMPLEQ_EMPTY(&inpcblist)) {
rw_exit_write(&udbtable.inpt_notify);
/*
* No matching pcb found; discard datagram.
* (No need to send an ICMP Port Unreachable
* for a broadcast or multicast datgram.)
*/
udpstat_inc(udps_noportbcast);
goto bad;
}
while ((inp = SIMPLEQ_FIRST(&inpcblist)) != NULL) {
struct mbuf *n;
SIMPLEQ_REMOVE_HEAD(&inpcblist, inp_notify);
if (SIMPLEQ_EMPTY(&inpcblist))
n = m;
else
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n != NULL) {
udp_sbappend(inp, n, ip, ip6, iphlen, uh,
&srcsa.sa, 0);
}
in_pcbunref(inp);
}
rw_exit_write(&udbtable.inpt_notify);
return IPPROTO_DONE;
}
/*
* Locate pcb for datagram.
*/
#if NPF > 0
inp = pf_inp_lookup(m);
#endif
if (inp == NULL) {
#ifdef INET6
if (ip6)
inp = in6_pcblookup(&udbtable, &ip6->ip6_src,
uh->uh_sport, &ip6->ip6_dst, uh->uh_dport,
m->m_pkthdr.ph_rtableid);
else
#endif /* INET6 */
inp = in_pcblookup(&udbtable, ip->ip_src, uh->uh_sport,
ip->ip_dst, uh->uh_dport, m->m_pkthdr.ph_rtableid);
}
if (inp == NULL) {
udpstat_inc(udps_pcbhashmiss);
#ifdef INET6
if (ip6) {
inp = in6_pcblookup_listen(&udbtable, &ip6->ip6_dst,
uh->uh_dport, m, m->m_pkthdr.ph_rtableid);
} else
#endif /* INET6 */
inp = in_pcblookup_listen(&udbtable, ip->ip_dst,
uh->uh_dport, m, m->m_pkthdr.ph_rtableid);
}
#ifdef IPSEC
if (ipsec_in_use) {
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct tdb *tdb;
int error;
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
if (mtag != NULL) {
tdbi = (struct tdb_ident *)(mtag + 1);
tdb = gettdb(tdbi->rdomain, tdbi->spi,
&tdbi->dst, tdbi->proto);
} else
tdb = NULL;
error = ipsp_spd_lookup(m, af, iphlen, IPSP_DIRECTION_IN,
tdb, inp, NULL, NULL);
if (error) {
udpstat_inc(udps_nosec);
tdb_unref(tdb);
goto bad;
}
/* create ipsec options, id is not modified after creation */
if (tdb && tdb->tdb_ids)
ipsecflowinfo = tdb->tdb_ids->id_flow;
tdb_unref(tdb);
}
#endif /*IPSEC */
if (inp == NULL) {
udpstat_inc(udps_noport);
if (m->m_flags & (M_BCAST | M_MCAST)) {
udpstat_inc(udps_noportbcast);
goto bad;
}
#ifdef INET6
if (ip6) {
uh->uh_sum = savesum;
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_NOPORT,0);
} else
#endif /* INET6 */
{
*ip = save_ip;
uh->uh_sum = savesum;
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT,
0, 0);
}
return IPPROTO_DONE;
}
KASSERT(sotoinpcb(inp->inp_socket) == inp);
soassertlocked(inp->inp_socket);
#ifdef INET6
if (ip6 && inp->inp_ip6_minhlim &&
inp->inp_ip6_minhlim > ip6->ip6_hlim) {
goto bad;
} else
#endif
if (ip && inp->inp_ip_minttl &&
inp->inp_ip_minttl > ip->ip_ttl) {
goto bad;
}
#if NPF > 0
if (inp->inp_socket->so_state & SS_ISCONNECTED)
pf_inp_link(m, inp);
#endif
#ifdef PIPEX
if (pipex_enable && inp->inp_pipex) {
struct pipex_session *session;
int off = iphlen + sizeof(struct udphdr);
if ((session = pipex_l2tp_lookup_session(m, off)) != NULL) {
m = *mp = pipex_l2tp_input(m, off, session,
ipsecflowinfo);
pipex_rele_session(session);
if (m == NULL) {
in_pcbunref(inp);
return IPPROTO_DONE;
}
}
}
#endif
udp_sbappend(inp, m, ip, ip6, iphlen, uh, &srcsa.sa, ipsecflowinfo);
in_pcbunref(inp);
return IPPROTO_DONE;
bad:
m_freem(m);
in_pcbunref(inp);
return IPPROTO_DONE;
}
void
udp_sbappend(struct inpcb *inp, struct mbuf *m, struct ip *ip,
struct ip6_hdr *ip6, int hlen, struct udphdr *uh,
struct sockaddr *srcaddr, u_int32_t ipsecflowinfo)
{
struct socket *so = inp->inp_socket;
struct mbuf *opts = NULL;
hlen += sizeof(*uh);
if (inp->inp_upcall != NULL) {
m = (*inp->inp_upcall)(inp->inp_upcall_arg, m,
ip, ip6, uh, hlen);
if (m == NULL)
return;
}
#ifdef INET6
if (ip6 && (inp->inp_flags & IN6P_CONTROLOPTS ||
so->so_options & SO_TIMESTAMP))
ip6_savecontrol(inp, m, &opts);
#endif /* INET6 */
if (ip && (inp->inp_flags & INP_CONTROLOPTS ||
so->so_options & SO_TIMESTAMP))
ip_savecontrol(inp, &opts, ip, m);
#ifdef INET6
if (ip6 && (inp->inp_flags & IN6P_RECVDSTPORT)) {
struct mbuf **mp = &opts;
while (*mp)
mp = &(*mp)->m_next;
*mp = sbcreatecontrol((caddr_t)&uh->uh_dport, sizeof(u_int16_t),
IPV6_RECVDSTPORT, IPPROTO_IPV6);
}
#endif /* INET6 */
if (ip && (inp->inp_flags & INP_RECVDSTPORT)) {
struct mbuf **mp = &opts;
while (*mp)
mp = &(*mp)->m_next;
*mp = sbcreatecontrol((caddr_t)&uh->uh_dport, sizeof(u_int16_t),
IP_RECVDSTPORT, IPPROTO_IP);
}
#ifdef IPSEC
if (ipsecflowinfo && (inp->inp_flags & INP_IPSECFLOWINFO)) {
struct mbuf **mp = &opts;
while (*mp)
mp = &(*mp)->m_next;
*mp = sbcreatecontrol((caddr_t)&ipsecflowinfo,
sizeof(u_int32_t), IP_IPSECFLOWINFO, IPPROTO_IP);
}
#endif
m_adj(m, hlen);
mtx_enter(&inp->inp_mtx);
if (sbappendaddr(so, &so->so_rcv, srcaddr, m, opts) == 0) {
mtx_leave(&inp->inp_mtx);
udpstat_inc(udps_fullsock);
m_freem(m);
m_freem(opts);
return;
}
mtx_leave(&inp->inp_mtx);
sorwakeup(so);
}
/*
* Notify a udp user of an asynchronous error;
* just wake up so that he can collect error status.
*/
void
udp_notify(struct inpcb *inp, int errno)
{
inp->inp_socket->so_error = errno;
sorwakeup(inp->inp_socket);
sowwakeup(inp->inp_socket);
}
#ifdef INET6
void
udp6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *d)
{
struct udphdr uh;
struct sockaddr_in6 sa6;
struct ip6_hdr *ip6;
struct mbuf *m;
int off;
void *cmdarg;
struct ip6ctlparam *ip6cp = NULL;
struct udp_portonly {
u_int16_t uh_sport;
u_int16_t uh_dport;
} *uhp;
struct inpcb *inp;
void (*notify)(struct inpcb *, int) = udp_notify;
if (sa == NULL)
return;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
if (PRC_IS_REDIRECT(cmd))
notify = in_rtchange, d = NULL;
else if (cmd == PRC_HOSTDEAD)
d = NULL;
else if (cmd == PRC_MSGSIZE)
; /* special code is present, see below */
else if (inet6ctlerrmap[cmd] == 0)
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
m = ip6cp->ip6c_m;
ip6 = ip6cp->ip6c_ip6;
off = ip6cp->ip6c_off;
cmdarg = ip6cp->ip6c_cmdarg;
} else {
m = NULL;
ip6 = NULL;
cmdarg = NULL;
/* XXX: translate addresses into internal form */
sa6 = *satosin6(sa);
if (in6_embedscope(&sa6.sin6_addr, &sa6, NULL)) {
/* should be impossible */
return;
}
}
if (ip6cp && ip6cp->ip6c_finaldst) {
bzero(&sa6, sizeof(sa6));
sa6.sin6_family = AF_INET6;
sa6.sin6_len = sizeof(sa6);
sa6.sin6_addr = *ip6cp->ip6c_finaldst;
/* XXX: assuming M is valid in this case */
sa6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
ip6cp->ip6c_finaldst);
if (in6_embedscope(ip6cp->ip6c_finaldst, &sa6, NULL)) {
/* should be impossible */
return;
}
} else {
/* XXX: translate addresses into internal form */
sa6 = *satosin6(sa);
if (in6_embedscope(&sa6.sin6_addr, &sa6, NULL)) {
/* should be impossible */
return;
}
}
if (ip6) {
/*
* XXX: We assume that when IPV6 is non NULL,
* M and OFF are valid.
*/
struct sockaddr_in6 sa6_src;
/* check if we can safely examine src and dst ports */
if (m->m_pkthdr.len < off + sizeof(*uhp))
return;
bzero(&uh, sizeof(uh));
m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh);
bzero(&sa6_src, sizeof(sa6_src));
sa6_src.sin6_family = AF_INET6;
sa6_src.sin6_len = sizeof(sa6_src);
sa6_src.sin6_addr = ip6->ip6_src;
sa6_src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
&ip6->ip6_src);
if (in6_embedscope(&sa6_src.sin6_addr, &sa6_src, NULL)) {
/* should be impossible */
return;
}
if (cmd == PRC_MSGSIZE) {
/*
* Check to see if we have a valid UDP socket
* corresponding to the address in the ICMPv6 message
* payload.
*/
inp = in6_pcblookup(&udbtable, &sa6.sin6_addr,
uh.uh_dport, &sa6_src.sin6_addr, uh.uh_sport,
rdomain);
#if 0
/*
* As the use of sendto(2) is fairly popular,
* we may want to allow non-connected pcb too.
* But it could be too weak against attacks...
* We should at least check if the local address (= s)
* is really ours.
*/
if (inp == NULL) {
inp = in6_pcblookup_listen(&udbtable,
&sa6_src.sin6_addr, uh.uh_sport, NULL,
rdomain))
}
#endif
/*
* Depending on the value of "valid" and routing table
* size (mtudisc_{hi,lo}wat), we will:
* - recalculate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update((struct ip6ctlparam *)d,
inp != NULL);
in_pcbunref(inp);
/*
* regardless of if we called icmp6_mtudisc_update(),
* we need to call in6_pcbnotify(), to notify path
* MTU change to the userland (2292bis-02), because
* some unconnected sockets may share the same
* destination and want to know the path MTU.
*/
}
in6_pcbnotify(&udbtable, &sa6, uh.uh_dport,
&sa6_src, uh.uh_sport, rdomain, cmd, cmdarg, notify);
} else {
in6_pcbnotify(&udbtable, &sa6, 0,
&sa6_any, 0, rdomain, cmd, cmdarg, notify);
}
}
#endif
void
udp_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
{
struct ip *ip = v;
struct udphdr *uhp;
struct in_addr faddr;
struct inpcb *inp;
void (*notify)(struct inpcb *, int) = udp_notify;
int errno;
if (sa == NULL)
return;
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in))
return;
faddr = satosin(sa)->sin_addr;
if (faddr.s_addr == INADDR_ANY)
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
errno = inetctlerrmap[cmd];
if (PRC_IS_REDIRECT(cmd))
notify = in_rtchange, ip = 0;
else if (cmd == PRC_HOSTDEAD)
ip = 0;
else if (errno == 0)
return;
if (ip) {
uhp = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
#ifdef IPSEC
/* PMTU discovery for udpencap */
if (cmd == PRC_MSGSIZE && ip_mtudisc && udpencap_enable &&
udpencap_port && uhp->uh_sport == htons(udpencap_port)) {
udpencap_ctlinput(cmd, sa, rdomain, v);
return;
}
#endif
inp = in_pcblookup(&udbtable,
ip->ip_dst, uhp->uh_dport, ip->ip_src, uhp->uh_sport,
rdomain);
if (inp && inp->inp_socket != NULL)
notify(inp, errno);
in_pcbunref(inp);
} else
in_pcbnotifyall(&udbtable, sa, rdomain, errno, notify);
}
int
udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,
struct mbuf *control)
{
struct sockaddr_in *sin = NULL;
struct udpiphdr *ui;
u_int32_t ipsecflowinfo = 0;
struct sockaddr_in src_sin;
int len = m->m_pkthdr.len;
struct in_addr laddr;
int error = 0;
#ifdef DIAGNOSTIC
if ((inp->inp_flags & INP_IPV6) != 0)
panic("IPv6 inpcb to %s", __func__);
#endif
/*
* Compute the packet length of the IP header, and
* punt if the length looks bogus.
*/
if ((len + sizeof(struct udpiphdr)) > IP_MAXPACKET) {
error = EMSGSIZE;
goto release;
}
memset(&src_sin, 0, sizeof(src_sin));
if (control) {
u_int clen;
struct cmsghdr *cm;
caddr_t cmsgs;
/*
* XXX: Currently, we assume all the optional information is
* stored in a single mbuf.
*/
if (control->m_next) {
error = EINVAL;
goto release;
}
clen = control->m_len;
cmsgs = mtod(control, caddr_t);
do {
if (clen < CMSG_LEN(0)) {
error = EINVAL;
goto release;
}
cm = (struct cmsghdr *)cmsgs;
if (cm->cmsg_len < CMSG_LEN(0) ||
CMSG_ALIGN(cm->cmsg_len) > clen) {
error = EINVAL;
goto release;
}
#ifdef IPSEC
if ((inp->inp_flags & INP_IPSECFLOWINFO) != 0 &&
cm->cmsg_len == CMSG_LEN(sizeof(ipsecflowinfo)) &&
cm->cmsg_level == IPPROTO_IP &&
cm->cmsg_type == IP_IPSECFLOWINFO) {
ipsecflowinfo = *(u_int32_t *)CMSG_DATA(cm);
} else
#endif
if (cm->cmsg_len == CMSG_LEN(sizeof(struct in_addr)) &&
cm->cmsg_level == IPPROTO_IP &&
cm->cmsg_type == IP_SENDSRCADDR) {
memcpy(&src_sin.sin_addr, CMSG_DATA(cm),
sizeof(struct in_addr));
src_sin.sin_family = AF_INET;
src_sin.sin_len = sizeof(src_sin);
/* no check on reuse when sin->sin_port == 0 */
if ((error = in_pcbaddrisavail(inp, &src_sin,
0, curproc)))
goto release;
}
clen -= CMSG_ALIGN(cm->cmsg_len);
cmsgs += CMSG_ALIGN(cm->cmsg_len);
} while (clen);
}
if (addr) {
if ((error = in_nam2sin(addr, &sin)))
goto release;
if (sin->sin_port == 0) {
error = EADDRNOTAVAIL;
goto release;
}
if (inp->inp_faddr.s_addr != INADDR_ANY) {
error = EISCONN;
goto release;
}
error = in_pcbselsrc(&laddr, sin, inp);
if (error)
goto release;
if (inp->inp_lport == 0) {
error = in_pcbbind(inp, NULL, curproc);
if (error)
goto release;
}
if (src_sin.sin_len > 0 &&
src_sin.sin_addr.s_addr != INADDR_ANY &&
src_sin.sin_addr.s_addr != inp->inp_laddr.s_addr) {
src_sin.sin_port = inp->inp_lport;
if (inp->inp_laddr.s_addr != INADDR_ANY &&
(error =
in_pcbaddrisavail(inp, &src_sin, 0, curproc)))
goto release;
laddr = src_sin.sin_addr;
}
} else {
if (inp->inp_faddr.s_addr == INADDR_ANY) {
error = ENOTCONN;
goto release;
}
laddr = inp->inp_laddr;
}
/*
* Calculate data length and get a mbuf
* for UDP and IP headers.
*/
M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
if (m == NULL) {
error = ENOBUFS;
goto bail;
}
/*
* Fill in mbuf with extended UDP header
* and addresses and length put into network format.
*/
ui = mtod(m, struct udpiphdr *);
bzero(ui->ui_x1, sizeof ui->ui_x1);
ui->ui_pr = IPPROTO_UDP;
ui->ui_len = htons((u_int16_t)len + sizeof (struct udphdr));
ui->ui_src = laddr;
ui->ui_dst = sin ? sin->sin_addr : inp->inp_faddr;
ui->ui_sport = inp->inp_lport;
ui->ui_dport = sin ? sin->sin_port : inp->inp_fport;
ui->ui_ulen = ui->ui_len;
((struct ip *)ui)->ip_len = htons(sizeof (struct udpiphdr) + len);
((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl;
((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos;
if (udpcksum)
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
udpstat_inc(udps_opackets);
/* force routing table */
m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
#if NPF > 0
if (inp->inp_socket->so_state & SS_ISCONNECTED)
pf_mbuf_link_inpcb(m, inp);
#endif
error = ip_output(m, inp->inp_options, &inp->inp_route,
(inp->inp_socket->so_options & SO_BROADCAST), inp->inp_moptions,
inp, ipsecflowinfo);
bail:
m_freem(control);
return (error);
release:
m_freem(m);
goto bail;
}
int
udp_attach(struct socket *so, int proto)
{
int error;
if (so->so_pcb != NULL)
return EINVAL;
if ((error = soreserve(so, udp_sendspace, udp_recvspace)))
return error;
NET_ASSERT_LOCKED();
if ((error = in_pcballoc(so, &udbtable)))
return error;
#ifdef INET6
if (sotoinpcb(so)->inp_flags & INP_IPV6)
sotoinpcb(so)->inp_ipv6.ip6_hlim = ip6_defhlim;
else
#endif /* INET6 */
sotoinpcb(so)->inp_ip.ip_ttl = ip_defttl;
return 0;
}
int
udp_detach(struct socket *so)
{
struct inpcb *inp;
soassertlocked(so);
inp = sotoinpcb(so);
if (inp == NULL)
return (EINVAL);
in_pcbdetach(inp);
return (0);
}
void
udp_lock(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
NET_ASSERT_LOCKED();
mtx_enter(&inp->inp_mtx);
}
void
udp_unlock(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
NET_ASSERT_LOCKED();
mtx_leave(&inp->inp_mtx);
}
int
udp_bind(struct socket *so, struct mbuf *addr, struct proc *p)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return in_pcbbind(inp, addr, p);
}
int
udp_connect(struct socket *so, struct mbuf *addr)
{
struct inpcb *inp = sotoinpcb(so);
int error;
soassertlocked(so);
#ifdef INET6
if (inp->inp_flags & INP_IPV6) {
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
return (EISCONN);
error = in6_pcbconnect(inp, addr);
} else
#endif /* INET6 */
{
if (inp->inp_faddr.s_addr != INADDR_ANY)
return (EISCONN);
error = in_pcbconnect(inp, addr);
}
if (error)
return (error);
soisconnected(so);
return (0);
}
int
udp_disconnect(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
#ifdef INET6
if (inp->inp_flags & INP_IPV6) {
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
return (ENOTCONN);
} else
#endif /* INET6 */
{
if (inp->inp_faddr.s_addr == INADDR_ANY)
return (ENOTCONN);
}
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
inp->inp_laddr6 = in6addr_any;
else
#endif /* INET6 */
inp->inp_laddr.s_addr = INADDR_ANY;
in_pcbdisconnect(inp);
so->so_state &= ~SS_ISCONNECTED; /* XXX */
return (0);
}
int
udp_shutdown(struct socket *so)
{
soassertlocked(so);
socantsendmore(so);
return (0);
}
int
udp_send(struct socket *so, struct mbuf *m, struct mbuf *addr,
struct mbuf *control)
{
struct inpcb *inp = sotoinpcb(so);
int error;
soassertlocked(so);
#ifdef PIPEX
if (inp->inp_pipex) {
struct pipex_session *session;
if (addr != NULL)
session =
pipex_l2tp_userland_lookup_session(m,
mtod(addr, struct sockaddr *));
else
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
session =
pipex_l2tp_userland_lookup_session_ipv6(
m, inp->inp_faddr6);
else
#endif
session =
pipex_l2tp_userland_lookup_session_ipv4(
m, inp->inp_faddr);
if (session != NULL) {
m = pipex_l2tp_userland_output(m, session);
pipex_rele_session(session);
if (m == NULL) {
m_freem(control);
return (ENOMEM);
}
}
}
#endif
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
error = udp6_output(inp, m, addr, control);
else
#endif
error = udp_output(inp, m, addr, control);
return (error);
}
int
udp_abort(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
soisdisconnected(so);
in_pcbdetach(inp);
return (0);
}
/*
* Sysctl for udp variables.
*/
int
udp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case UDPCTL_BADDYNAMIC:
NET_LOCK();
error = sysctl_struct(oldp, oldlenp, newp, newlen,
baddynamicports.udp, sizeof(baddynamicports.udp));
NET_UNLOCK();
return (error);
case UDPCTL_ROOTONLY:
if (newp && securelevel > 0)
return (EPERM);
NET_LOCK();
error = sysctl_struct(oldp, oldlenp, newp, newlen,
rootonlyports.udp, sizeof(rootonlyports.udp));
NET_UNLOCK();
return (error);
case UDPCTL_STATS:
if (newp != NULL)
return (EPERM);
return (udp_sysctl_udpstat(oldp, oldlenp, newp));
default:
NET_LOCK();
error = sysctl_bounded_arr(udpctl_vars, nitems(udpctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
/* NOTREACHED */
}
int
udp_sysctl_udpstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[udps_ncounters];
struct udpstat udpstat;
u_long *words = (u_long *)&udpstat;
int i;
CTASSERT(sizeof(udpstat) == (nitems(counters) * sizeof(u_long)));
memset(&udpstat, 0, sizeof udpstat);
counters_read(udpcounters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&udpstat, sizeof(udpstat)));
}
1141
1137
2
3
2
1134
557
2
3
608
556
609
692
268
205
298
2
35
16
18
14
11
33
12
9
19
23
14
20
9
2
9
15
459
298
13
4
4
3
1
7
2
2
7
2
1
2
5
5
2
2
4
1
2
4
4
7
1
1
113
2
17
2
2
2
2
10
16
3
2
4
80
32
68
121
45
68
19
29
29
122
22
148
8
12
13
56
3
50
64
5
9
3
6
10
15
11
29
7
9
28
3
8
10
17
17
14
26
33
16
2
12
3
6
5
56
22
28
76
14
6
6
6
6
6
6
8
6
4
3
5
10
4
7
4
7
9
11
8
10
3
11
6
6
12
1
1
4
2
11
12
6
6
12
1
12
12
12
6
6
12
3
5
12
6
11
10
12
9
4
5
9
9
5
14
4
33
2
4
27
2
1
8
4
4
4
4
4
1
4
3
12
12
8
4
12
1
6
6
6
5
5
5
3
5
3
5
5
5
5
6
5
5
5
5
3
3
4
4
5
5
5
5
22
1
21
34
35
8
11
16
25
4
3
2
2
3
5
4
4
9
12
16
4
2
3
24
15
10
11
16
12
3
12
12
8
10
1
12
11
7
1
6
5
11
3
12
7
9
12
16
2
2
2
2
1
1
3
3
1
2
3
2
2
18
2
2
1
1
12
2
9
8
2
15
2
2
2
3
6
2
2
1
11
2
2
1
3
3
3
2
1
10
5
5
23
2
9
5
2
2
5
5
2
6
3
2
2
3
3
17
2
2
5
2
2
6
2
2
2
2
2
2
6
5
3
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
/* $OpenBSD: kern_sysctl.c,v 1.406 2022/08/16 13:29:52 visa Exp $ */
/* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */
/*-
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Mike Karels at Berkeley Software Design, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94
*/
/*
* sysctl system call.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/unistd.h>
#include <sys/buf.h>
#include <sys/tty.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/sysctl.h>
#include <sys/msgbuf.h>
#include <sys/vmmeter.h>
#include <sys/namei.h>
#include <sys/exec.h>
#include <sys/mbuf.h>
#include <sys/percpu.h>
#include <sys/sensors.h>
#include <sys/pipe.h>
#include <sys/eventvar.h>
#include <sys/socketvar.h>
#include <sys/socket.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/pledge.h>
#include <sys/timetc.h>
#include <sys/evcount.h>
#include <sys/un.h>
#include <sys/unpcb.h>
#include <sys/sched.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/wait.h>
#include <sys/witness.h>
#include <uvm/uvm_extern.h>
#include <dev/cons.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet6/ip6_var.h>
#ifdef DDB
#include <ddb/db_var.h>
#endif
#ifdef SYSVMSG
#include <sys/msg.h>
#endif
#ifdef SYSVSEM
#include <sys/sem.h>
#endif
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#include "audio.h"
#include "dt.h"
#include "pf.h"
#include "video.h"
extern struct forkstat forkstat;
extern struct nchstats nchstats;
extern int fscale;
extern fixpt_t ccpu;
extern long numvnodes;
extern int allowdt;
extern int audio_record_enable;
extern int video_record_enable;
int allowkmem;
int sysctl_diskinit(int, struct proc *);
int sysctl_proc_args(int *, u_int, void *, size_t *, struct proc *);
int sysctl_proc_cwd(int *, u_int, void *, size_t *, struct proc *);
int sysctl_proc_nobroadcastkill(int *, u_int, void *, size_t, void *, size_t *,
struct proc *);
int sysctl_proc_vmmap(int *, u_int, void *, size_t *, struct proc *);
int sysctl_intrcnt(int *, u_int, void *, size_t *);
int sysctl_sensors(int *, u_int, void *, size_t *, void *, size_t);
int sysctl_cptime2(int *, u_int, void *, size_t *, void *, size_t);
int sysctl_audio(int *, u_int, void *, size_t *, void *, size_t);
int sysctl_video(int *, u_int, void *, size_t *, void *, size_t);
int sysctl_cpustats(int *, u_int, void *, size_t *, void *, size_t);
int sysctl_utc_offset(void *, size_t *, void *, size_t);
void fill_file(struct kinfo_file *, struct file *, struct filedesc *, int,
struct vnode *, struct process *, struct proc *, struct socket *, int);
void fill_kproc(struct process *, struct kinfo_proc *, struct proc *, int);
int (*cpu_cpuspeed)(int *);
/*
* Lock to avoid too many processes vslocking a large amount of memory
* at the same time.
*/
struct rwlock sysctl_lock = RWLOCK_INITIALIZER("sysctllk");
struct rwlock sysctl_disklock = RWLOCK_INITIALIZER("sysctldlk");
int
sys_sysctl(struct proc *p, void *v, register_t *retval)
{
struct sys_sysctl_args /* {
syscallarg(const int *) name;
syscallarg(u_int) namelen;
syscallarg(void *) old;
syscallarg(size_t *) oldlenp;
syscallarg(void *) new;
syscallarg(size_t) newlen;
} */ *uap = v;
int error, dolock = 1;
size_t savelen = 0, oldlen = 0;
sysctlfn *fn;
int name[CTL_MAXNAME];
if (SCARG(uap, new) != NULL &&
(error = suser(p)))
return (error);
/*
* all top-level sysctl names are non-terminal
*/
if (SCARG(uap, namelen) > CTL_MAXNAME || SCARG(uap, namelen) < 2)
return (EINVAL);
error = copyin(SCARG(uap, name), name,
SCARG(uap, namelen) * sizeof(int));
if (error)
return (error);
error = pledge_sysctl(p, SCARG(uap, namelen),
name, SCARG(uap, new));
if (error)
return (error);
switch (name[0]) {
case CTL_KERN:
fn = kern_sysctl;
break;
case CTL_HW:
fn = hw_sysctl;
break;
case CTL_VM:
fn = uvm_sysctl;
break;
case CTL_NET:
fn = net_sysctl;
break;
case CTL_FS:
fn = fs_sysctl;
break;
case CTL_VFS:
fn = vfs_sysctl;
break;
case CTL_MACHDEP:
fn = cpu_sysctl;
break;
#ifdef DEBUG_SYSCTL
case CTL_DEBUG:
fn = debug_sysctl;
break;
#endif
#ifdef DDB
case CTL_DDB:
fn = ddb_sysctl;
break;
#endif
default:
return (EOPNOTSUPP);
}
if (SCARG(uap, oldlenp) &&
(error = copyin(SCARG(uap, oldlenp), &oldlen, sizeof(oldlen))))
return (error);
if (SCARG(uap, old) != NULL) {
if ((error = rw_enter(&sysctl_lock, RW_WRITE|RW_INTR)) != 0)
return (error);
if (dolock) {
if (atop(oldlen) > uvmexp.wiredmax - uvmexp.wired) {
rw_exit_write(&sysctl_lock);
return (ENOMEM);
}
error = uvm_vslock(p, SCARG(uap, old), oldlen,
PROT_READ | PROT_WRITE);
if (error) {
rw_exit_write(&sysctl_lock);
return (error);
}
}
savelen = oldlen;
}
error = (*fn)(&name[1], SCARG(uap, namelen) - 1, SCARG(uap, old),
&oldlen, SCARG(uap, new), SCARG(uap, newlen), p);
if (SCARG(uap, old) != NULL) {
if (dolock)
uvm_vsunlock(p, SCARG(uap, old), savelen);
rw_exit_write(&sysctl_lock);
}
if (error)
return (error);
if (SCARG(uap, oldlenp))
error = copyout(&oldlen, SCARG(uap, oldlenp), sizeof(oldlen));
return (error);
}
/*
* Attributes stored in the kernel.
*/
char hostname[MAXHOSTNAMELEN];
int hostnamelen;
char domainname[MAXHOSTNAMELEN];
int domainnamelen;
long hostid;
char *disknames = NULL;
size_t disknameslen;
struct diskstats *diskstats = NULL;
size_t diskstatslen;
int securelevel;
/* morally const values reported by sysctl_bounded_arr */
static int arg_max = ARG_MAX;
static int openbsd = OpenBSD;
static int posix_version = _POSIX_VERSION;
static int ngroups_max = NGROUPS_MAX;
static int int_zero = 0;
static int int_one = 1;
static int maxpartitions = MAXPARTITIONS;
static int raw_part = RAW_PART;
extern int somaxconn, sominconn;
extern int nosuidcoredump;
extern int maxlocksperuid;
extern int uvm_wxabort;
extern int global_ptrace;
const struct sysctl_bounded_args kern_vars[] = {
{KERN_OSREV, &openbsd, SYSCTL_INT_READONLY},
{KERN_MAXVNODES, &maxvnodes, 0, INT_MAX},
{KERN_MAXPROC, &maxprocess, 0, INT_MAX},
{KERN_MAXFILES, &maxfiles, 0, INT_MAX},
{KERN_NFILES, &numfiles, SYSCTL_INT_READONLY},
{KERN_TTYCOUNT, &tty_count, SYSCTL_INT_READONLY},
{KERN_ARGMAX, &arg_max, SYSCTL_INT_READONLY},
{KERN_POSIX1, &posix_version, SYSCTL_INT_READONLY},
{KERN_NGROUPS, &ngroups_max, SYSCTL_INT_READONLY},
{KERN_JOB_CONTROL, &int_one, SYSCTL_INT_READONLY},
{KERN_SAVED_IDS, &int_one, SYSCTL_INT_READONLY},
{KERN_MAXPARTITIONS, &maxpartitions, SYSCTL_INT_READONLY},
{KERN_RAWPARTITION, &raw_part, SYSCTL_INT_READONLY},
{KERN_MAXTHREAD, &maxthread, 0, INT_MAX},
{KERN_NTHREADS, &nthreads, SYSCTL_INT_READONLY},
{KERN_SOMAXCONN, &somaxconn, 0, SHRT_MAX},
{KERN_SOMINCONN, &sominconn, 0, SHRT_MAX},
{KERN_NOSUIDCOREDUMP, &nosuidcoredump, 0, 3},
{KERN_FSYNC, &int_one, SYSCTL_INT_READONLY},
{KERN_SYSVMSG,
#ifdef SYSVMSG
&int_one,
#else
&int_zero,
#endif
SYSCTL_INT_READONLY},
{KERN_SYSVSEM,
#ifdef SYSVSEM
&int_one,
#else
&int_zero,
#endif
SYSCTL_INT_READONLY},
{KERN_SYSVSHM,
#ifdef SYSVSHM
&int_one,
#else
&int_zero,
#endif
SYSCTL_INT_READONLY},
{KERN_FSCALE, &fscale, SYSCTL_INT_READONLY},
{KERN_CCPU, &ccpu, SYSCTL_INT_READONLY},
{KERN_NPROCS, &nprocesses, SYSCTL_INT_READONLY},
{KERN_SPLASSERT, &splassert_ctl, 0, 3},
{KERN_MAXLOCKSPERUID, &maxlocksperuid, 0, INT_MAX},
{KERN_WXABORT, &uvm_wxabort, 0, 1},
{KERN_NETLIVELOCKS, &int_zero, SYSCTL_INT_READONLY},
#ifdef PTRACE
{KERN_GLOBAL_PTRACE, &global_ptrace, 0, 1},
#endif
};
int
kern_sysctl_dirs(int top_name, int *name, u_int namelen,
void *oldp, size_t *oldlenp, void *newp, size_t newlen, struct proc *p)
{
switch (top_name) {
#ifndef SMALL_KERNEL
case KERN_PROC:
return (sysctl_doproc(name, namelen, oldp, oldlenp));
case KERN_PROC_ARGS:
return (sysctl_proc_args(name, namelen, oldp, oldlenp, p));
case KERN_PROC_CWD:
return (sysctl_proc_cwd(name, namelen, oldp, oldlenp, p));
case KERN_PROC_NOBROADCASTKILL:
return (sysctl_proc_nobroadcastkill(name, namelen,
newp, newlen, oldp, oldlenp, p));
case KERN_PROC_VMMAP:
return (sysctl_proc_vmmap(name, namelen, oldp, oldlenp, p));
case KERN_FILE:
return (sysctl_file(name, namelen, oldp, oldlenp, p));
#endif
#if defined(GPROF) || defined(DDBPROF)
case KERN_PROF:
return (sysctl_doprof(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
case KERN_MALLOCSTATS:
return (sysctl_malloc(name, namelen, oldp, oldlenp,
newp, newlen, p));
case KERN_TTY:
return (sysctl_tty(name, namelen, oldp, oldlenp,
newp, newlen));
case KERN_POOL:
return (sysctl_dopool(name, namelen, oldp, oldlenp));
#if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
case KERN_SYSVIPC_INFO:
return (sysctl_sysvipc(name, namelen, oldp, oldlenp));
#endif
#ifdef SYSVSEM
case KERN_SEMINFO:
return (sysctl_sysvsem(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
#ifdef SYSVSHM
case KERN_SHMINFO:
return (sysctl_sysvshm(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
#ifndef SMALL_KERNEL
case KERN_INTRCNT:
return (sysctl_intrcnt(name, namelen, oldp, oldlenp));
case KERN_WATCHDOG:
return (sysctl_wdog(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
#ifndef SMALL_KERNEL
case KERN_EVCOUNT:
return (evcount_sysctl(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
case KERN_TIMECOUNTER:
return (sysctl_tc(name, namelen, oldp, oldlenp, newp, newlen));
case KERN_CPTIME2:
return (sysctl_cptime2(name, namelen, oldp, oldlenp,
newp, newlen));
#ifdef WITNESS
case KERN_WITNESSWATCH:
return witness_sysctl_watch(oldp, oldlenp, newp, newlen);
case KERN_WITNESS:
return witness_sysctl(name, namelen, oldp, oldlenp,
newp, newlen);
#endif
#if NAUDIO > 0
case KERN_AUDIO:
return (sysctl_audio(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
#if NVIDEO > 0
case KERN_VIDEO:
return (sysctl_video(name, namelen, oldp, oldlenp,
newp, newlen));
#endif
case KERN_CPUSTATS:
return (sysctl_cpustats(name, namelen, oldp, oldlenp,
newp, newlen));
default:
return (ENOTDIR); /* overloaded */
}
}
/*
* kernel related system variables.
*/
int
kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
int error, level, inthostid, stackgap;
dev_t dev;
extern int pool_debug;
/* dispatch the non-terminal nodes first */
if (namelen != 1) {
return kern_sysctl_dirs(name[0], name + 1, namelen - 1,
oldp, oldlenp, newp, newlen, p);
}
switch (name[0]) {
case KERN_OSTYPE:
return (sysctl_rdstring(oldp, oldlenp, newp, ostype));
case KERN_OSRELEASE:
return (sysctl_rdstring(oldp, oldlenp, newp, osrelease));
case KERN_OSVERSION:
return (sysctl_rdstring(oldp, oldlenp, newp, osversion));
case KERN_VERSION:
return (sysctl_rdstring(oldp, oldlenp, newp, version));
case KERN_NUMVNODES: /* XXX numvnodes is a long */
return (sysctl_rdint(oldp, oldlenp, newp, numvnodes));
case KERN_SECURELVL:
level = securelevel;
if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &level)) ||
newp == NULL)
return (error);
if ((securelevel > 0 || level < -1) &&
level < securelevel && p->p_p->ps_pid != 1)
return (EPERM);
securelevel = level;
return (0);
#if NDT > 0
case KERN_ALLOWDT:
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&allowdt));
#endif
case KERN_ALLOWKMEM:
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&allowkmem));
case KERN_HOSTNAME:
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
hostname, sizeof(hostname));
if (newp && !error)
hostnamelen = newlen;
return (error);
case KERN_DOMAINNAME:
if (securelevel >= 1 && domainnamelen && newp)
error = EPERM;
else
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
domainname, sizeof(domainname));
if (newp && !error)
domainnamelen = newlen;
return (error);
case KERN_HOSTID:
inthostid = hostid; /* XXX assumes sizeof long <= sizeof int */
error = sysctl_int(oldp, oldlenp, newp, newlen, &inthostid);
hostid = inthostid;
return (error);
case KERN_CLOCKRATE:
return (sysctl_clockrate(oldp, oldlenp, newp));
case KERN_BOOTTIME: {
struct timeval bt;
memset(&bt, 0, sizeof bt);
microboottime(&bt);
return (sysctl_rdstruct(oldp, oldlenp, newp, &bt, sizeof bt));
}
case KERN_MBSTAT: {
extern struct cpumem *mbstat;
uint64_t counters[MBSTAT_COUNT];
struct mbstat mbs;
unsigned int i;
memset(&mbs, 0, sizeof(mbs));
counters_read(mbstat, counters, MBSTAT_COUNT);
for (i = 0; i < MBSTAT_TYPES; i++)
mbs.m_mtypes[i] = counters[i];
mbs.m_drops = counters[MBSTAT_DROPS];
mbs.m_wait = counters[MBSTAT_WAIT];
mbs.m_drain = counters[MBSTAT_DRAIN];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&mbs, sizeof(mbs)));
}
case KERN_MSGBUFSIZE:
case KERN_CONSBUFSIZE: {
struct msgbuf *mp;
mp = (name[0] == KERN_MSGBUFSIZE) ? msgbufp : consbufp;
/*
* deal with cases where the message buffer has
* become corrupted.
*/
if (!mp || mp->msg_magic != MSG_MAGIC)
return (ENXIO);
return (sysctl_rdint(oldp, oldlenp, newp, mp->msg_bufs));
}
case KERN_CONSBUF:
if ((error = suser(p)))
return (error);
/* FALLTHROUGH */
case KERN_MSGBUF: {
struct msgbuf *mp;
mp = (name[0] == KERN_MSGBUF) ? msgbufp : consbufp;
/* see note above */
if (!mp || mp->msg_magic != MSG_MAGIC)
return (ENXIO);
return (sysctl_rdstruct(oldp, oldlenp, newp, mp,
mp->msg_bufs + offsetof(struct msgbuf, msg_bufc)));
}
case KERN_CPTIME:
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
long cp_time[CPUSTATES];
int i, n = 0;
memset(cp_time, 0, sizeof(cp_time));
CPU_INFO_FOREACH(cii, ci) {
if (!cpu_is_online(ci))
continue;
n++;
for (i = 0; i < CPUSTATES; i++)
cp_time[i] += ci->ci_schedstate.spc_cp_time[i];
}
for (i = 0; i < CPUSTATES; i++)
cp_time[i] /= n;
return (sysctl_rdstruct(oldp, oldlenp, newp, &cp_time,
sizeof(cp_time)));
}
case KERN_NCHSTATS:
return (sysctl_rdstruct(oldp, oldlenp, newp, &nchstats,
sizeof(struct nchstats)));
case KERN_FORKSTAT:
return (sysctl_rdstruct(oldp, oldlenp, newp, &forkstat,
sizeof(struct forkstat)));
case KERN_STACKGAPRANDOM:
stackgap = stackgap_random;
error = sysctl_int(oldp, oldlenp, newp, newlen, &stackgap);
if (error)
return (error);
/*
* Safety harness.
*/
if ((stackgap < ALIGNBYTES && stackgap != 0) ||
!powerof2(stackgap) || stackgap >= MAXSSIZ)
return (EINVAL);
stackgap_random = stackgap;
return (0);
case KERN_MAXCLUSTERS: {
int val = nmbclust;
error = sysctl_int(oldp, oldlenp, newp, newlen, &val);
if (error == 0 && val != nmbclust)
error = nmbclust_update(val);
return (error);
}
case KERN_CACHEPCT: {
u_int64_t dmapages;
int opct, pgs;
opct = bufcachepercent;
error = sysctl_int(oldp, oldlenp, newp, newlen,
&bufcachepercent);
if (error)
return(error);
if (bufcachepercent > 90 || bufcachepercent < 5) {
bufcachepercent = opct;
return (EINVAL);
}
dmapages = uvm_pagecount(&dma_constraint);
if (bufcachepercent != opct) {
pgs = bufcachepercent * dmapages / 100;
bufadjust(pgs); /* adjust bufpages */
bufhighpages = bufpages; /* set high water mark */
}
return(0);
}
case KERN_CONSDEV:
if (cn_tab != NULL)
dev = cn_tab->cn_dev;
else
dev = NODEV;
return sysctl_rdstruct(oldp, oldlenp, newp, &dev, sizeof(dev));
case KERN_POOL_DEBUG: {
int old_pool_debug = pool_debug;
error = sysctl_int(oldp, oldlenp, newp, newlen,
&pool_debug);
if (error == 0 && pool_debug != old_pool_debug)
pool_reclaim_all();
return (error);
}
#if NPF > 0
case KERN_PFSTATUS:
return (pf_sysctl(oldp, oldlenp, newp, newlen));
#endif
case KERN_TIMEOUT_STATS:
return (timeout_sysctl(oldp, oldlenp, newp, newlen));
case KERN_UTC_OFFSET:
return (sysctl_utc_offset(oldp, oldlenp, newp, newlen));
default:
return (sysctl_bounded_arr(kern_vars, nitems(kern_vars), name,
namelen, oldp, oldlenp, newp, newlen));
}
/* NOTREACHED */
}
/*
* hardware related system variables.
*/
char *hw_vendor, *hw_prod, *hw_uuid, *hw_serial, *hw_ver;
int allowpowerdown = 1;
int hw_power = 1;
/* morally const values reported by sysctl_bounded_arr */
static int byte_order = BYTE_ORDER;
static int page_size = PAGE_SIZE;
const struct sysctl_bounded_args hw_vars[] = {
{HW_NCPU, &ncpus, SYSCTL_INT_READONLY},
{HW_NCPUFOUND, &ncpusfound, SYSCTL_INT_READONLY},
{HW_BYTEORDER, &byte_order, SYSCTL_INT_READONLY},
{HW_PAGESIZE, &page_size, SYSCTL_INT_READONLY},
{HW_DISKCOUNT, &disk_count, SYSCTL_INT_READONLY},
{HW_POWER, &hw_power, SYSCTL_INT_READONLY},
};
int
hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
extern char machine[], cpu_model[];
int err, cpuspeed;
/* all sysctl names at this level except sensors are terminal */
if (name[0] != HW_SENSORS && namelen != 1)
return (ENOTDIR); /* overloaded */
switch (name[0]) {
case HW_MACHINE:
return (sysctl_rdstring(oldp, oldlenp, newp, machine));
case HW_MODEL:
return (sysctl_rdstring(oldp, oldlenp, newp, cpu_model));
case HW_NCPUONLINE:
return (sysctl_rdint(oldp, oldlenp, newp,
sysctl_hwncpuonline()));
case HW_PHYSMEM:
return (sysctl_rdint(oldp, oldlenp, newp, ptoa(physmem)));
case HW_USERMEM:
return (sysctl_rdint(oldp, oldlenp, newp,
ptoa(physmem - uvmexp.wired)));
case HW_DISKNAMES:
err = sysctl_diskinit(0, p);
if (err)
return err;
if (disknames)
return (sysctl_rdstring(oldp, oldlenp, newp,
disknames));
else
return (sysctl_rdstring(oldp, oldlenp, newp, ""));
case HW_DISKSTATS:
err = sysctl_diskinit(1, p);
if (err)
return err;
return (sysctl_rdstruct(oldp, oldlenp, newp, diskstats,
disk_count * sizeof(struct diskstats)));
case HW_CPUSPEED:
if (!cpu_cpuspeed)
return (EOPNOTSUPP);
err = cpu_cpuspeed(&cpuspeed);
if (err)
return err;
return (sysctl_rdint(oldp, oldlenp, newp, cpuspeed));
#ifndef SMALL_KERNEL
case HW_SENSORS:
return (sysctl_sensors(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
case HW_SETPERF:
return (sysctl_hwsetperf(oldp, oldlenp, newp, newlen));
case HW_PERFPOLICY:
return (sysctl_hwperfpolicy(oldp, oldlenp, newp, newlen));
#endif /* !SMALL_KERNEL */
case HW_VENDOR:
if (hw_vendor)
return (sysctl_rdstring(oldp, oldlenp, newp,
hw_vendor));
else
return (EOPNOTSUPP);
case HW_PRODUCT:
if (hw_prod)
return (sysctl_rdstring(oldp, oldlenp, newp, hw_prod));
else
return (EOPNOTSUPP);
case HW_VERSION:
if (hw_ver)
return (sysctl_rdstring(oldp, oldlenp, newp, hw_ver));
else
return (EOPNOTSUPP);
case HW_SERIALNO:
if (hw_serial)
return (sysctl_rdstring(oldp, oldlenp, newp,
hw_serial));
else
return (EOPNOTSUPP);
case HW_UUID:
if (hw_uuid)
return (sysctl_rdstring(oldp, oldlenp, newp, hw_uuid));
else
return (EOPNOTSUPP);
case HW_PHYSMEM64:
return (sysctl_rdquad(oldp, oldlenp, newp,
ptoa((psize_t)physmem)));
case HW_USERMEM64:
return (sysctl_rdquad(oldp, oldlenp, newp,
ptoa((psize_t)physmem - uvmexp.wired)));
case HW_ALLOWPOWERDOWN:
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&allowpowerdown));
#ifdef __HAVE_CPU_TOPOLOGY
case HW_SMT:
return (sysctl_hwsmt(oldp, oldlenp, newp, newlen));
#endif
default:
return sysctl_bounded_arr(hw_vars, nitems(hw_vars), name,
namelen, oldp, oldlenp, newp, newlen);
}
/* NOTREACHED */
}
#ifdef DEBUG_SYSCTL
/*
* Debugging related system variables.
*/
extern struct ctldebug debug_vfs_busyprt;
struct ctldebug debug1, debug2, debug3, debug4;
struct ctldebug debug5, debug6, debug7, debug8, debug9;
struct ctldebug debug10, debug11, debug12, debug13, debug14;
struct ctldebug debug15, debug16, debug17, debug18, debug19;
static struct ctldebug *debugvars[CTL_DEBUG_MAXID] = {
&debug_vfs_busyprt,
&debug1, &debug2, &debug3, &debug4,
&debug5, &debug6, &debug7, &debug8, &debug9,
&debug10, &debug11, &debug12, &debug13, &debug14,
&debug15, &debug16, &debug17, &debug18, &debug19,
};
int
debug_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
struct ctldebug *cdp;
/* all sysctl names at this level are name and field */
if (namelen != 2)
return (ENOTDIR); /* overloaded */
if (name[0] < 0 || name[0] >= nitems(debugvars))
return (EOPNOTSUPP);
cdp = debugvars[name[0]];
if (cdp->debugname == 0)
return (EOPNOTSUPP);
switch (name[1]) {
case CTL_DEBUG_NAME:
return (sysctl_rdstring(oldp, oldlenp, newp, cdp->debugname));
case CTL_DEBUG_VALUE:
return (sysctl_int(oldp, oldlenp, newp, newlen, cdp->debugvar));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
#endif /* DEBUG_SYSCTL */
/*
* Reads, or writes that lower the value
*/
int
sysctl_int_lower(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
int *valp)
{
unsigned int oval = *valp, val = *valp;
int error;
if (newp == NULL)
return (sysctl_rdint(oldp, oldlenp, newp, val));
if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)))
return (error);
if (val > oval)
return (EPERM); /* do not allow raising */
*(unsigned int *)valp = val;
return (0);
}
/*
* Validate parameters and get old / set new parameters
* for an integer-valued sysctl function.
*/
int
sysctl_int(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int *valp)
{
int error = 0;
if (oldp && *oldlenp < sizeof(int))
return (ENOMEM);
if (newp && newlen != sizeof(int))
return (EINVAL);
*oldlenp = sizeof(int);
if (oldp)
error = copyout(valp, oldp, sizeof(int));
if (error == 0 && newp)
error = copyin(newp, valp, sizeof(int));
return (error);
}
/*
* As above, but read-only.
*/
int
sysctl_rdint(void *oldp, size_t *oldlenp, void *newp, int val)
{
int error = 0;
if (oldp && *oldlenp < sizeof(int))
return (ENOMEM);
if (newp)
return (EPERM);
*oldlenp = sizeof(int);
if (oldp)
error = copyout((caddr_t)&val, oldp, sizeof(int));
return (error);
}
/*
* Selects between sysctl_rdint and sysctl_int according to securelevel.
*/
int
sysctl_securelevel_int(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
int *valp)
{
if (securelevel > 0)
return (sysctl_rdint(oldp, oldlenp, newp, *valp));
return (sysctl_int(oldp, oldlenp, newp, newlen, valp));
}
/*
* Read-only or bounded integer values.
*/
int
sysctl_int_bounded(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
int *valp, int minimum, int maximum)
{
int val = *valp;
int error;
/* read only */
if (newp == NULL || minimum > maximum)
return (sysctl_rdint(oldp, oldlenp, newp, val));
if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)))
return (error);
/* outside limits */
if (val < minimum || maximum < val)
return (EINVAL);
*valp = val;
return (0);
}
/*
* Array of read-only or bounded integer values.
*/
int
sysctl_bounded_arr(const struct sysctl_bounded_args *valpp, u_int valplen,
int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
u_int i;
if (namelen != 1)
return (ENOTDIR);
for (i = 0; i < valplen; ++i) {
if (valpp[i].mib == name[0]) {
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
valpp[i].var, valpp[i].minimum, valpp[i].maximum));
}
}
return (EOPNOTSUPP);
}
/*
* Validate parameters and get old / set new parameters
* for an integer-valued sysctl function.
*/
int
sysctl_quad(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
int64_t *valp)
{
int error = 0;
if (oldp && *oldlenp < sizeof(int64_t))
return (ENOMEM);
if (newp && newlen != sizeof(int64_t))
return (EINVAL);
*oldlenp = sizeof(int64_t);
if (oldp)
error = copyout(valp, oldp, sizeof(int64_t));
if (error == 0 && newp)
error = copyin(newp, valp, sizeof(int64_t));
return (error);
}
/*
* As above, but read-only.
*/
int
sysctl_rdquad(void *oldp, size_t *oldlenp, void *newp, int64_t val)
{
int error = 0;
if (oldp && *oldlenp < sizeof(int64_t))
return (ENOMEM);
if (newp)
return (EPERM);
*oldlenp = sizeof(int64_t);
if (oldp)
error = copyout((caddr_t)&val, oldp, sizeof(int64_t));
return (error);
}
/*
* Validate parameters and get old / set new parameters
* for a string-valued sysctl function.
*/
int
sysctl_string(void *oldp, size_t *oldlenp, void *newp, size_t newlen, char *str,
size_t maxlen)
{
return sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, 0);
}
int
sysctl_tstring(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
char *str, size_t maxlen)
{
return sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, 1);
}
int
sysctl__string(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
char *str, size_t maxlen, int trunc)
{
size_t len;
int error = 0;
len = strlen(str) + 1;
if (oldp && *oldlenp < len) {
if (trunc == 0 || *oldlenp == 0)
return (ENOMEM);
}
if (newp && newlen >= maxlen)
return (EINVAL);
if (oldp) {
if (trunc && *oldlenp < len) {
len = *oldlenp;
error = copyout(str, oldp, len - 1);
if (error == 0)
error = copyout("", (char *)oldp + len - 1, 1);
} else {
error = copyout(str, oldp, len);
}
}
*oldlenp = len;
if (error == 0 && newp) {
error = copyin(newp, str, newlen);
str[newlen] = 0;
}
return (error);
}
/*
* As above, but read-only.
*/
int
sysctl_rdstring(void *oldp, size_t *oldlenp, void *newp, const char *str)
{
size_t len;
int error = 0;
len = strlen(str) + 1;
if (oldp && *oldlenp < len)
return (ENOMEM);
if (newp)
return (EPERM);
*oldlenp = len;
if (oldp)
error = copyout(str, oldp, len);
return (error);
}
/*
* Validate parameters and get old / set new parameters
* for a structure oriented sysctl function.
*/
int
sysctl_struct(void *oldp, size_t *oldlenp, void *newp, size_t newlen, void *sp,
size_t len)
{
int error = 0;
if (oldp && *oldlenp < len)
return (ENOMEM);
if (newp && newlen > len)
return (EINVAL);
if (oldp) {
*oldlenp = len;
error = copyout(sp, oldp, len);
}
if (error == 0 && newp)
error = copyin(newp, sp, len);
return (error);
}
/*
* Validate parameters and get old parameters
* for a structure oriented sysctl function.
*/
int
sysctl_rdstruct(void *oldp, size_t *oldlenp, void *newp, const void *sp,
size_t len)
{
int error = 0;
if (oldp && *oldlenp < len)
return (ENOMEM);
if (newp)
return (EPERM);
*oldlenp = len;
if (oldp)
error = copyout(sp, oldp, len);
return (error);
}
#ifndef SMALL_KERNEL
void
fill_file(struct kinfo_file *kf, struct file *fp, struct filedesc *fdp,
int fd, struct vnode *vp, struct process *pr, struct proc *p,
struct socket *so, int show_pointers)
{
struct vattr va;
memset(kf, 0, sizeof(*kf));
kf->fd_fd = fd; /* might not really be an fd */
if (fp != NULL) {
if (show_pointers)
kf->f_fileaddr = PTRTOINT64(fp);
kf->f_flag = fp->f_flag;
kf->f_iflags = fp->f_iflags;
kf->f_type = fp->f_type;
kf->f_count = fp->f_count;
if (show_pointers)
kf->f_ucred = PTRTOINT64(fp->f_cred);
kf->f_uid = fp->f_cred->cr_uid;
kf->f_gid = fp->f_cred->cr_gid;
if (show_pointers)
kf->f_ops = PTRTOINT64(fp->f_ops);
if (show_pointers)
kf->f_data = PTRTOINT64(fp->f_data);
kf->f_usecount = 0;
if (suser(p) == 0 || p->p_ucred->cr_uid == fp->f_cred->cr_uid) {
mtx_enter(&fp->f_mtx);
kf->f_offset = fp->f_offset;
kf->f_rxfer = fp->f_rxfer;
kf->f_rwfer = fp->f_wxfer;
kf->f_seek = fp->f_seek;
kf->f_rbytes = fp->f_rbytes;
kf->f_wbytes = fp->f_wbytes;
mtx_leave(&fp->f_mtx);
} else
kf->f_offset = -1;
} else if (vp != NULL) {
/* fake it */
kf->f_type = DTYPE_VNODE;
kf->f_flag = FREAD;
if (fd == KERN_FILE_TRACE)
kf->f_flag |= FWRITE;
} else if (so != NULL) {
/* fake it */
kf->f_type = DTYPE_SOCKET;
}
/* information about the object associated with this file */
switch (kf->f_type) {
case DTYPE_VNODE:
if (fp != NULL)
vp = (struct vnode *)fp->f_data;
if (show_pointers)
kf->v_un = PTRTOINT64(vp->v_un.vu_socket);
kf->v_type = vp->v_type;
kf->v_tag = vp->v_tag;
kf->v_flag = vp->v_flag;
if (show_pointers)
kf->v_data = PTRTOINT64(vp->v_data);
if (show_pointers)
kf->v_mount = PTRTOINT64(vp->v_mount);
if (vp->v_mount)
strlcpy(kf->f_mntonname,
vp->v_mount->mnt_stat.f_mntonname,
sizeof(kf->f_mntonname));
if (VOP_GETATTR(vp, &va, p->p_ucred, p) == 0) {
kf->va_fileid = va.va_fileid;
kf->va_mode = MAKEIMODE(va.va_type, va.va_mode);
kf->va_size = va.va_size;
kf->va_rdev = va.va_rdev;
kf->va_fsid = va.va_fsid & 0xffffffff;
kf->va_nlink = va.va_nlink;
}
break;
case DTYPE_SOCKET: {
int locked = 0;
if (so == NULL) {
so = (struct socket *)fp->f_data;
/* if so is passed as parameter it is already locked */
switch (so->so_proto->pr_domain->dom_family) {
case AF_INET:
case AF_INET6:
NET_LOCK();
locked = 1;
break;
}
}
kf->so_type = so->so_type;
kf->so_state = so->so_state;
if (show_pointers)
kf->so_pcb = PTRTOINT64(so->so_pcb);
else
kf->so_pcb = -1;
kf->so_protocol = so->so_proto->pr_protocol;
kf->so_family = so->so_proto->pr_domain->dom_family;
kf->so_rcv_cc = so->so_rcv.sb_cc;
kf->so_snd_cc = so->so_snd.sb_cc;
if (isspliced(so)) {
if (show_pointers)
kf->so_splice =
PTRTOINT64(so->so_sp->ssp_socket);
kf->so_splicelen = so->so_sp->ssp_len;
} else if (issplicedback(so))
kf->so_splicelen = -1;
if (so->so_pcb == NULL) {
if (locked)
NET_UNLOCK();
break;
}
switch (kf->so_family) {
case AF_INET: {
struct inpcb *inpcb = so->so_pcb;
NET_ASSERT_LOCKED();
if (show_pointers)
kf->inp_ppcb = PTRTOINT64(inpcb->inp_ppcb);
kf->inp_lport = inpcb->inp_lport;
kf->inp_laddru[0] = inpcb->inp_laddr.s_addr;
kf->inp_fport = inpcb->inp_fport;
kf->inp_faddru[0] = inpcb->inp_faddr.s_addr;
kf->inp_rtableid = inpcb->inp_rtableid;
if (so->so_type == SOCK_RAW)
kf->inp_proto = inpcb->inp_ip.ip_p;
if (so->so_proto->pr_protocol == IPPROTO_TCP) {
struct tcpcb *tcpcb = (void *)inpcb->inp_ppcb;
kf->t_rcv_wnd = tcpcb->rcv_wnd;
kf->t_snd_wnd = tcpcb->snd_wnd;
kf->t_snd_cwnd = tcpcb->snd_cwnd;
kf->t_state = tcpcb->t_state;
}
break;
}
case AF_INET6: {
struct inpcb *inpcb = so->so_pcb;
NET_ASSERT_LOCKED();
if (show_pointers)
kf->inp_ppcb = PTRTOINT64(inpcb->inp_ppcb);
kf->inp_lport = inpcb->inp_lport;
kf->inp_laddru[0] = inpcb->inp_laddr6.s6_addr32[0];
kf->inp_laddru[1] = inpcb->inp_laddr6.s6_addr32[1];
kf->inp_laddru[2] = inpcb->inp_laddr6.s6_addr32[2];
kf->inp_laddru[3] = inpcb->inp_laddr6.s6_addr32[3];
kf->inp_fport = inpcb->inp_fport;
kf->inp_faddru[0] = inpcb->inp_faddr6.s6_addr32[0];
kf->inp_faddru[1] = inpcb->inp_faddr6.s6_addr32[1];
kf->inp_faddru[2] = inpcb->inp_faddr6.s6_addr32[2];
kf->inp_faddru[3] = inpcb->inp_faddr6.s6_addr32[3];
kf->inp_rtableid = inpcb->inp_rtableid;
if (so->so_type == SOCK_RAW)
kf->inp_proto = inpcb->inp_ipv6.ip6_nxt;
if (so->so_proto->pr_protocol == IPPROTO_TCP) {
struct tcpcb *tcpcb = (void *)inpcb->inp_ppcb;
kf->t_rcv_wnd = tcpcb->rcv_wnd;
kf->t_snd_wnd = tcpcb->snd_wnd;
kf->t_state = tcpcb->t_state;
}
break;
}
case AF_UNIX: {
struct unpcb *unpcb = so->so_pcb;
kf->f_msgcount = unpcb->unp_msgcount;
if (show_pointers) {
kf->unp_conn = PTRTOINT64(unpcb->unp_conn);
kf->unp_refs = PTRTOINT64(
SLIST_FIRST(&unpcb->unp_refs));
kf->unp_nextref = PTRTOINT64(
SLIST_NEXT(unpcb, unp_nextref));
kf->v_un = PTRTOINT64(unpcb->unp_vnode);
kf->unp_addr = PTRTOINT64(unpcb->unp_addr);
}
if (unpcb->unp_addr != NULL) {
struct sockaddr_un *un = mtod(unpcb->unp_addr,
struct sockaddr_un *);
memcpy(kf->unp_path, un->sun_path, un->sun_len
- offsetof(struct sockaddr_un,sun_path));
}
break;
}
}
if (locked)
NET_UNLOCK();
break;
}
case DTYPE_PIPE: {
struct pipe *pipe = (struct pipe *)fp->f_data;
if (show_pointers)
kf->pipe_peer = PTRTOINT64(pipe->pipe_peer);
kf->pipe_state = pipe->pipe_state;
break;
}
case DTYPE_KQUEUE: {
struct kqueue *kqi = (struct kqueue *)fp->f_data;
kf->kq_count = kqi->kq_count;
kf->kq_state = kqi->kq_state;
break;
}
}
/* per-process information for KERN_FILE_BY[PU]ID */
if (pr != NULL) {
kf->p_pid = pr->ps_pid;
kf->p_uid = pr->ps_ucred->cr_uid;
kf->p_gid = pr->ps_ucred->cr_gid;
kf->p_tid = -1;
strlcpy(kf->p_comm, pr->ps_comm, sizeof(kf->p_comm));
}
if (fdp != NULL) {
fdplock(fdp);
kf->fd_ofileflags = fdp->fd_ofileflags[fd];
fdpunlock(fdp);
}
}
/*
* Get file structures.
*/
int
sysctl_file(int *name, u_int namelen, char *where, size_t *sizep,
struct proc *p)
{
struct kinfo_file *kf;
struct filedesc *fdp;
struct file *fp;
struct process *pr;
size_t buflen, elem_size, elem_count, outsize;
char *dp = where;
int arg, i, error = 0, needed = 0, matched;
u_int op;
int show_pointers;
if (namelen > 4)
return (ENOTDIR);
if (namelen < 4 || name[2] > sizeof(*kf))
return (EINVAL);
buflen = where != NULL ? *sizep : 0;
op = name[0];
arg = name[1];
elem_size = name[2];
elem_count = name[3];
outsize = MIN(sizeof(*kf), elem_size);
if (elem_size < 1)
return (EINVAL);
show_pointers = suser(curproc) == 0;
kf = malloc(sizeof(*kf), M_TEMP, M_WAITOK);
#define FILLIT2(fp, fdp, i, vp, pr, so) do { \
if (buflen >= elem_size && elem_count > 0) { \
fill_file(kf, fp, fdp, i, vp, pr, p, so, show_pointers);\
error = copyout(kf, dp, outsize); \
if (error) \
break; \
dp += elem_size; \
buflen -= elem_size; \
elem_count--; \
} \
needed += elem_size; \
} while (0)
#define FILLIT(fp, fdp, i, vp, pr) \
FILLIT2(fp, fdp, i, vp, pr, NULL)
#define FILLSO(so) \
FILLIT2(NULL, NULL, 0, NULL, NULL, so)
switch (op) {
case KERN_FILE_BYFILE:
/* use the inp-tables to pick up closed connections, too */
if (arg == DTYPE_SOCKET) {
struct inpcb *inp;
NET_LOCK();
mtx_enter(&tcbtable.inpt_mtx);
TAILQ_FOREACH(inp, &tcbtable.inpt_queue, inp_queue)
FILLSO(inp->inp_socket);
mtx_leave(&tcbtable.inpt_mtx);
mtx_enter(&udbtable.inpt_mtx);
TAILQ_FOREACH(inp, &udbtable.inpt_queue, inp_queue)
FILLSO(inp->inp_socket);
mtx_leave(&udbtable.inpt_mtx);
mtx_enter(&rawcbtable.inpt_mtx);
TAILQ_FOREACH(inp, &rawcbtable.inpt_queue, inp_queue)
FILLSO(inp->inp_socket);
mtx_leave(&rawcbtable.inpt_mtx);
#ifdef INET6
mtx_enter(&rawin6pcbtable.inpt_mtx);
TAILQ_FOREACH(inp, &rawin6pcbtable.inpt_queue,
inp_queue)
FILLSO(inp->inp_socket);
mtx_leave(&rawin6pcbtable.inpt_mtx);
#endif
NET_UNLOCK();
}
fp = NULL;
while ((fp = fd_iterfile(fp, p)) != NULL) {
if ((arg == 0 || fp->f_type == arg)) {
int af, skip = 0;
if (arg == DTYPE_SOCKET && fp->f_type == arg) {
af = ((struct socket *)fp->f_data)->
so_proto->pr_domain->dom_family;
if (af == AF_INET || af == AF_INET6)
skip = 1;
}
if (!skip)
FILLIT(fp, NULL, 0, NULL, NULL);
}
}
break;
case KERN_FILE_BYPID:
/* A arg of -1 indicates all processes */
if (arg < -1) {
error = EINVAL;
break;
}
matched = 0;
LIST_FOREACH(pr, &allprocess, ps_list) {
/*
* skip system, exiting, embryonic and undead
* processes
*/
if (pr->ps_flags & (PS_SYSTEM | PS_EMBRYO | PS_EXITING))
continue;
if (arg > 0 && pr->ps_pid != (pid_t)arg) {
/* not the pid we are looking for */
continue;
}
matched = 1;
fdp = pr->ps_fd;
if (pr->ps_textvp)
FILLIT(NULL, NULL, KERN_FILE_TEXT, pr->ps_textvp, pr);
if (fdp->fd_cdir)
FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pr);
if (fdp->fd_rdir)
FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pr);
if (pr->ps_tracevp)
FILLIT(NULL, NULL, KERN_FILE_TRACE, pr->ps_tracevp, pr);
for (i = 0; i < fdp->fd_nfiles; i++) {
if ((fp = fd_getfile(fdp, i)) == NULL)
continue;
FILLIT(fp, fdp, i, NULL, pr);
FRELE(fp, p);
}
}
if (!matched)
error = ESRCH;
break;
case KERN_FILE_BYUID:
LIST_FOREACH(pr, &allprocess, ps_list) {
/*
* skip system, exiting, embryonic and undead
* processes
*/
if (pr->ps_flags & (PS_SYSTEM | PS_EMBRYO | PS_EXITING))
continue;
if (arg >= 0 && pr->ps_ucred->cr_uid != (uid_t)arg) {
/* not the uid we are looking for */
continue;
}
fdp = pr->ps_fd;
if (fdp->fd_cdir)
FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pr);
if (fdp->fd_rdir)
FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pr);
if (pr->ps_tracevp)
FILLIT(NULL, NULL, KERN_FILE_TRACE, pr->ps_tracevp, pr);
for (i = 0; i < fdp->fd_nfiles; i++) {
if ((fp = fd_getfile(fdp, i)) == NULL)
continue;
FILLIT(fp, fdp, i, NULL, pr);
FRELE(fp, p);
}
}
break;
default:
error = EINVAL;
break;
}
free(kf, M_TEMP, sizeof(*kf));
if (!error) {
if (where == NULL)
needed += KERN_FILESLOP * elem_size;
else if (*sizep < needed)
error = ENOMEM;
*sizep = needed;
}
return (error);
}
/*
* try over estimating by 5 procs
*/
#define KERN_PROCSLOP 5
int
sysctl_doproc(int *name, u_int namelen, char *where, size_t *sizep)
{
struct kinfo_proc *kproc = NULL;
struct proc *p;
struct process *pr;
char *dp;
int arg, buflen, doingzomb, elem_size, elem_count;
int error, needed, op;
int dothreads = 0;
int show_pointers;
dp = where;
buflen = where != NULL ? *sizep : 0;
needed = error = 0;
if (namelen != 4 || name[2] <= 0 || name[3] < 0 ||
name[2] > sizeof(*kproc))
return (EINVAL);
op = name[0];
arg = name[1];
elem_size = name[2];
elem_count = name[3];
dothreads = op & KERN_PROC_SHOW_THREADS;
op &= ~KERN_PROC_SHOW_THREADS;
show_pointers = suser(curproc) == 0;
if (where != NULL)
kproc = malloc(sizeof(*kproc), M_TEMP, M_WAITOK);
pr = LIST_FIRST(&allprocess);
doingzomb = 0;
again:
for (; pr != NULL; pr = LIST_NEXT(pr, ps_list)) {
/* XXX skip processes in the middle of being zapped */
if (pr->ps_pgrp == NULL)
continue;
/*
* Skip embryonic processes.
*/
if (pr->ps_flags & PS_EMBRYO)
continue;
/*
* TODO - make more efficient (see notes below).
*/
switch (op) {
case KERN_PROC_PID:
/* could do this with just a lookup */
if (pr->ps_pid != (pid_t)arg)
continue;
break;
case KERN_PROC_PGRP:
/* could do this by traversing pgrp */
if (pr->ps_pgrp->pg_id != (pid_t)arg)
continue;
break;
case KERN_PROC_SESSION:
if (pr->ps_session->s_leader == NULL ||
pr->ps_session->s_leader->ps_pid != (pid_t)arg)
continue;
break;
case KERN_PROC_TTY:
if ((pr->ps_flags & PS_CONTROLT) == 0 ||
pr->ps_session->s_ttyp == NULL ||
pr->ps_session->s_ttyp->t_dev != (dev_t)arg)
continue;
break;
case KERN_PROC_UID:
if (pr->ps_ucred->cr_uid != (uid_t)arg)
continue;
break;
case KERN_PROC_RUID:
if (pr->ps_ucred->cr_ruid != (uid_t)arg)
continue;
break;
case KERN_PROC_ALL:
if (pr->ps_flags & PS_SYSTEM)
continue;
break;
case KERN_PROC_KTHREAD:
/* no filtering */
break;
default:
error = EINVAL;
goto err;
}
if (buflen >= elem_size && elem_count > 0) {
fill_kproc(pr, kproc, NULL, show_pointers);
error = copyout(kproc, dp, elem_size);
if (error)
goto err;
dp += elem_size;
buflen -= elem_size;
elem_count--;
}
needed += elem_size;
/* Skip per-thread entries if not required by op */
if (!dothreads)
continue;
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
if (buflen >= elem_size && elem_count > 0) {
fill_kproc(pr, kproc, p, show_pointers);
error = copyout(kproc, dp, elem_size);
if (error)
goto err;
dp += elem_size;
buflen -= elem_size;
elem_count--;
}
needed += elem_size;
}
}
if (doingzomb == 0) {
pr = LIST_FIRST(&zombprocess);
doingzomb++;
goto again;
}
if (where != NULL) {
*sizep = dp - where;
if (needed > *sizep) {
error = ENOMEM;
goto err;
}
} else {
needed += KERN_PROCSLOP * elem_size;
*sizep = needed;
}
err:
if (kproc)
free(kproc, M_TEMP, sizeof(*kproc));
return (error);
}
/*
* Fill in a kproc structure for the specified process.
*/
void
fill_kproc(struct process *pr, struct kinfo_proc *ki, struct proc *p,
int show_pointers)
{
struct session *s = pr->ps_session;
struct tty *tp;
struct vmspace *vm = pr->ps_vmspace;
struct timespec booted, st, ut, utc;
int isthread;
isthread = p != NULL;
if (!isthread)
p = pr->ps_mainproc; /* XXX */
FILL_KPROC(ki, strlcpy, p, pr, pr->ps_ucred, pr->ps_pgrp,
p, pr, s, vm, pr->ps_limit, pr->ps_sigacts, isthread,
show_pointers);
/* stuff that's too painful to generalize into the macros */
if (pr->ps_pptr)
ki->p_ppid = pr->ps_ppid;
if (s->s_leader)
ki->p_sid = s->s_leader->ps_pid;
if ((pr->ps_flags & PS_CONTROLT) && (tp = s->s_ttyp)) {
ki->p_tdev = tp->t_dev;
ki->p_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : -1;
if (show_pointers)
ki->p_tsess = PTRTOINT64(tp->t_session);
} else {
ki->p_tdev = NODEV;
ki->p_tpgid = -1;
}
/* fixups that can only be done in the kernel */
if ((pr->ps_flags & PS_ZOMBIE) == 0) {
if ((pr->ps_flags & PS_EMBRYO) == 0 && vm != NULL)
ki->p_vm_rssize = vm_resident_count(vm);
calctsru(isthread ? &p->p_tu : &pr->ps_tu, &ut, &st, NULL);
ki->p_uutime_sec = ut.tv_sec;
ki->p_uutime_usec = ut.tv_nsec/1000;
ki->p_ustime_sec = st.tv_sec;
ki->p_ustime_usec = st.tv_nsec/1000;
/* Convert starting uptime to a starting UTC time. */
nanoboottime(&booted);
timespecadd(&booted, &pr->ps_start, &utc);
ki->p_ustart_sec = utc.tv_sec;
ki->p_ustart_usec = utc.tv_nsec / 1000;
#ifdef MULTIPROCESSOR
if (p->p_cpu != NULL)
ki->p_cpuid = CPU_INFO_UNIT(p->p_cpu);
#endif
}
/* get %cpu and schedule state: just one thread or sum of all? */
if (isthread) {
ki->p_pctcpu = p->p_pctcpu;
ki->p_stat = p->p_stat;
} else {
ki->p_pctcpu = 0;
ki->p_stat = (pr->ps_flags & PS_ZOMBIE) ? SDEAD : SIDL;
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
ki->p_pctcpu += p->p_pctcpu;
/* find best state: ONPROC > RUN > STOP > SLEEP > .. */
if (p->p_stat == SONPROC || ki->p_stat == SONPROC)
ki->p_stat = SONPROC;
else if (p->p_stat == SRUN || ki->p_stat == SRUN)
ki->p_stat = SRUN;
else if (p->p_stat == SSTOP || ki->p_stat == SSTOP)
ki->p_stat = SSTOP;
else if (p->p_stat == SSLEEP)
ki->p_stat = SSLEEP;
}
}
}
int
sysctl_proc_args(int *name, u_int namelen, void *oldp, size_t *oldlenp,
struct proc *cp)
{
struct process *vpr;
pid_t pid;
struct ps_strings pss;
struct iovec iov;
struct uio uio;
int error, cnt, op;
size_t limit;
char **rargv, **vargv; /* reader vs. victim */
char *rarg, *varg, *buf;
struct vmspace *vm;
vaddr_t ps_strings;
if (namelen > 2)
return (ENOTDIR);
if (namelen < 2)
return (EINVAL);
pid = name[0];
op = name[1];
switch (op) {
case KERN_PROC_ARGV:
case KERN_PROC_NARGV:
case KERN_PROC_ENV:
case KERN_PROC_NENV:
break;
default:
return (EOPNOTSUPP);
}
if ((vpr = prfind(pid)) == NULL)
return (ESRCH);
if (oldp == NULL) {
if (op == KERN_PROC_NARGV || op == KERN_PROC_NENV)
*oldlenp = sizeof(int);
else
*oldlenp = ARG_MAX; /* XXX XXX XXX */
return (0);
}
/* Either system process or exiting/zombie */
if (vpr->ps_flags & (PS_SYSTEM | PS_EXITING))
return (EINVAL);
/* Execing - danger. */
if ((vpr->ps_flags & PS_INEXEC))
return (EBUSY);
/* Only owner or root can get env */
if ((op == KERN_PROC_NENV || op == KERN_PROC_ENV) &&
(vpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid &&
(error = suser(cp)) != 0))
return (error);
ps_strings = vpr->ps_strings;
vm = vpr->ps_vmspace;
uvmspace_addref(vm);
vpr = NULL;
buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
iov.iov_base = &pss;
iov.iov_len = sizeof(pss);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)ps_strings;
uio.uio_resid = sizeof(pss);
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = cp;
if ((error = uvm_io(&vm->vm_map, &uio, 0)) != 0)
goto out;
if (op == KERN_PROC_NARGV) {
error = sysctl_rdint(oldp, oldlenp, NULL, pss.ps_nargvstr);
goto out;
}
if (op == KERN_PROC_NENV) {
error = sysctl_rdint(oldp, oldlenp, NULL, pss.ps_nenvstr);
goto out;
}
if (op == KERN_PROC_ARGV) {
cnt = pss.ps_nargvstr;
vargv = pss.ps_argvstr;
} else {
cnt = pss.ps_nenvstr;
vargv = pss.ps_envstr;
}
/* -1 to have space for a terminating NUL */
limit = *oldlenp - 1;
*oldlenp = 0;
rargv = oldp;
/*
* *oldlenp - number of bytes copied out into readers buffer.
* limit - maximal number of bytes allowed into readers buffer.
* rarg - pointer into readers buffer where next arg will be stored.
* rargv - pointer into readers buffer where the next rarg pointer
* will be stored.
* vargv - pointer into victim address space where the next argument
* will be read.
*/
/* space for cnt pointers and a NULL */
rarg = (char *)(rargv + cnt + 1);
*oldlenp += (cnt + 1) * sizeof(char **);
while (cnt > 0 && *oldlenp < limit) {
size_t len, vstrlen;
/* Write to readers argv */
if ((error = copyout(&rarg, rargv, sizeof(rarg))) != 0)
goto out;
/* read the victim argv */
iov.iov_base = &varg;
iov.iov_len = sizeof(varg);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)(vaddr_t)vargv;
uio.uio_resid = sizeof(varg);
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = cp;
if ((error = uvm_io(&vm->vm_map, &uio, 0)) != 0)
goto out;
if (varg == NULL)
break;
/*
* read the victim arg. We must jump through hoops to avoid
* crossing a page boundary too much and returning an error.
*/
more:
len = PAGE_SIZE - (((vaddr_t)varg) & PAGE_MASK);
/* leave space for the terminating NUL */
iov.iov_base = buf;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)(vaddr_t)varg;
uio.uio_resid = len;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = cp;
if ((error = uvm_io(&vm->vm_map, &uio, 0)) != 0)
goto out;
for (vstrlen = 0; vstrlen < len; vstrlen++) {
if (buf[vstrlen] == '\0')
break;
}
/* Don't overflow readers buffer. */
if (*oldlenp + vstrlen + 1 >= limit) {
error = ENOMEM;
goto out;
}
if ((error = copyout(buf, rarg, vstrlen)) != 0)
goto out;
*oldlenp += vstrlen;
rarg += vstrlen;
/* The string didn't end in this page? */
if (vstrlen == len) {
varg += vstrlen;
goto more;
}
/* End of string. Terminate it with a NUL */
buf[0] = '\0';
if ((error = copyout(buf, rarg, 1)) != 0)
goto out;
*oldlenp += 1;
rarg += 1;
vargv++;
rargv++;
cnt--;
}
if (*oldlenp >= limit) {
error = ENOMEM;
goto out;
}
/* Write the terminating null */
rarg = NULL;
error = copyout(&rarg, rargv, sizeof(rarg));
out:
uvmspace_free(vm);
free(buf, M_TEMP, PAGE_SIZE);
return (error);
}
int
sysctl_proc_cwd(int *name, u_int namelen, void *oldp, size_t *oldlenp,
struct proc *cp)
{
struct process *findpr;
struct vnode *vp;
pid_t pid;
int error;
size_t lenused, len;
char *path, *bp, *bend;
if (namelen > 1)
return (ENOTDIR);
if (namelen < 1)
return (EINVAL);
pid = name[0];
if ((findpr = prfind(pid)) == NULL)
return (ESRCH);
if (oldp == NULL) {
*oldlenp = MAXPATHLEN * 4;
return (0);
}
/* Either system process or exiting/zombie */
if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING))
return (EINVAL);
/* Only owner or root can get cwd */
if (findpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid &&
(error = suser(cp)) != 0)
return (error);
len = *oldlenp;
if (len > MAXPATHLEN * 4)
len = MAXPATHLEN * 4;
else if (len < 2)
return (ERANGE);
*oldlenp = 0;
/* snag a reference to the vnode before we can sleep */
vp = findpr->ps_fd->fd_cdir;
vref(vp);
path = malloc(len, M_TEMP, M_WAITOK);
bp = &path[len];
bend = bp;
*(--bp) = '\0';
/* Same as sys__getcwd */
error = vfs_getcwd_common(vp, NULL,
&bp, path, len / 2, GETCWD_CHECK_ACCESS, cp);
if (error == 0) {
*oldlenp = lenused = bend - bp;
error = copyout(bp, oldp, lenused);
}
vrele(vp);
free(path, M_TEMP, len);
return (error);
}
int
sysctl_proc_nobroadcastkill(int *name, u_int namelen, void *newp, size_t newlen,
void *oldp, size_t *oldlenp, struct proc *cp)
{
struct process *findpr;
pid_t pid;
int error, flag;
if (namelen > 1)
return (ENOTDIR);
if (namelen < 1)
return (EINVAL);
pid = name[0];
if ((findpr = prfind(pid)) == NULL)
return (ESRCH);
/* Either system process or exiting/zombie */
if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING))
return (EINVAL);
/* Only root can change PS_NOBROADCASTKILL */
if (newp != NULL && (error = suser(cp)) != 0)
return (error);
/* get the PS_NOBROADCASTKILL flag */
flag = findpr->ps_flags & PS_NOBROADCASTKILL ? 1 : 0;
error = sysctl_int(oldp, oldlenp, newp, newlen, &flag);
if (error == 0 && newp) {
if (flag)
atomic_setbits_int(&findpr->ps_flags,
PS_NOBROADCASTKILL);
else
atomic_clearbits_int(&findpr->ps_flags,
PS_NOBROADCASTKILL);
}
return (error);
}
/* Arbitrary but reasonable limit for one iteration. */
#define VMMAP_MAXLEN MAXPHYS
int
sysctl_proc_vmmap(int *name, u_int namelen, void *oldp, size_t *oldlenp,
struct proc *cp)
{
struct process *findpr;
pid_t pid;
int error;
size_t oldlen, len;
struct kinfo_vmentry *kve, *ukve;
u_long *ustart, start;
if (namelen > 1)
return (ENOTDIR);
if (namelen < 1)
return (EINVAL);
/* Provide max buffer length as hint. */
if (oldp == NULL) {
if (oldlenp == NULL)
return (EINVAL);
else {
*oldlenp = VMMAP_MAXLEN;
return (0);
}
}
pid = name[0];
if (pid == cp->p_p->ps_pid) {
/* Self process mapping. */
findpr = cp->p_p;
} else if (pid > 0) {
if ((findpr = prfind(pid)) == NULL)
return (ESRCH);
/* Either system process or exiting/zombie */
if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING))
return (EINVAL);
#if 1
/* XXX Allow only root for now */
if ((error = suser(cp)) != 0)
return (error);
#else
/* Only owner or root can get vmmap */
if (findpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid &&
(error = suser(cp)) != 0)
return (error);
#endif
} else {
/* Only root can get kernel_map */
if ((error = suser(cp)) != 0)
return (error);
findpr = NULL;
}
/* Check the given size. */
oldlen = *oldlenp;
if (oldlen == 0 || oldlen % sizeof(*kve) != 0)
return (EINVAL);
/* Deny huge allocation. */
if (oldlen > VMMAP_MAXLEN)
return (EINVAL);
/*
* Iterate from the given address passed as the first element's
* kve_start via oldp.
*/
ukve = (struct kinfo_vmentry *)oldp;
ustart = &ukve->kve_start;
error = copyin(ustart, &start, sizeof(start));
if (error != 0)
return (error);
/* Allocate wired memory to not block. */
kve = malloc(oldlen, M_TEMP, M_WAITOK);
/* Set the base address and read entries. */
kve[0].kve_start = start;
len = oldlen;
error = fill_vmmap(findpr, kve, &len);
if (error != 0 && error != ENOMEM)
goto done;
if (len == 0)
goto done;
KASSERT(len <= oldlen);
KASSERT((len % sizeof(struct kinfo_vmentry)) == 0);
error = copyout(kve, oldp, len);
done:
*oldlenp = len;
free(kve, M_TEMP, oldlen);
return (error);
}
#endif
/*
* Initialize disknames/diskstats for export by sysctl. If update is set,
* then we simply update the disk statistics information.
*/
int
sysctl_diskinit(int update, struct proc *p)
{
struct diskstats *sdk;
struct disk *dk;
const char *duid;
int error, changed = 0;
KERNEL_ASSERT_LOCKED();
if ((error = rw_enter(&sysctl_disklock, RW_WRITE|RW_INTR)) != 0)
return error;
/* Run in a loop, disks may change while malloc sleeps. */
while (disk_change) {
int tlen;
disk_change = 0;
tlen = 0;
TAILQ_FOREACH(dk, &disklist, dk_link) {
if (dk->dk_name)
tlen += strlen(dk->dk_name);
tlen += 18; /* label uid + separators */
}
tlen++;
/*
* The sysctl_disklock ensures that no other process can
* allocate disknames and diskstats while our malloc sleeps.
*/
free(disknames, M_SYSCTL, disknameslen);
free(diskstats, M_SYSCTL, diskstatslen);
diskstats = NULL;
disknames = NULL;
diskstats = mallocarray(disk_count, sizeof(struct diskstats),
M_SYSCTL, M_WAITOK|M_ZERO);
diskstatslen = disk_count * sizeof(struct diskstats);
disknames = malloc(tlen, M_SYSCTL, M_WAITOK|M_ZERO);
disknameslen = tlen;
disknames[0] = '\0';
changed = 1;
}
if (changed) {
int l;
l = 0;
sdk = diskstats;
TAILQ_FOREACH(dk, &disklist, dk_link) {
duid = NULL;
if (dk->dk_label && !duid_iszero(dk->dk_label->d_uid))
duid = duid_format(dk->dk_label->d_uid);
snprintf(disknames + l, disknameslen - l, "%s:%s,",
dk->dk_name ? dk->dk_name : "",
duid ? duid : "");
l += strlen(disknames + l);
strlcpy(sdk->ds_name, dk->dk_name,
sizeof(sdk->ds_name));
mtx_enter(&dk->dk_mtx);
sdk->ds_busy = dk->dk_busy;
sdk->ds_rxfer = dk->dk_rxfer;
sdk->ds_wxfer = dk->dk_wxfer;
sdk->ds_seek = dk->dk_seek;
sdk->ds_rbytes = dk->dk_rbytes;
sdk->ds_wbytes = dk->dk_wbytes;
sdk->ds_attachtime = dk->dk_attachtime;
sdk->ds_timestamp = dk->dk_timestamp;
sdk->ds_time = dk->dk_time;
mtx_leave(&dk->dk_mtx);
sdk++;
}
/* Eliminate trailing comma */
if (l != 0)
disknames[l - 1] = '\0';
} else if (update) {
/* Just update, number of drives hasn't changed */
sdk = diskstats;
TAILQ_FOREACH(dk, &disklist, dk_link) {
strlcpy(sdk->ds_name, dk->dk_name,
sizeof(sdk->ds_name));
mtx_enter(&dk->dk_mtx);
sdk->ds_busy = dk->dk_busy;
sdk->ds_rxfer = dk->dk_rxfer;
sdk->ds_wxfer = dk->dk_wxfer;
sdk->ds_seek = dk->dk_seek;
sdk->ds_rbytes = dk->dk_rbytes;
sdk->ds_wbytes = dk->dk_wbytes;
sdk->ds_attachtime = dk->dk_attachtime;
sdk->ds_timestamp = dk->dk_timestamp;
sdk->ds_time = dk->dk_time;
mtx_leave(&dk->dk_mtx);
sdk++;
}
}
rw_exit_write(&sysctl_disklock);
return 0;
}
#if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
int
sysctl_sysvipc(int *name, u_int namelen, void *where, size_t *sizep)
{
#ifdef SYSVSEM
struct sem_sysctl_info *semsi;
#endif
#ifdef SYSVSHM
struct shm_sysctl_info *shmsi;
#endif
size_t infosize, dssize, tsize, buflen, bufsiz;
int i, nds, error, ret;
void *buf;
if (namelen != 1)
return (EINVAL);
buflen = *sizep;
switch (*name) {
case KERN_SYSVIPC_MSG_INFO:
#ifdef SYSVMSG
return (sysctl_sysvmsg(name, namelen, where, sizep));
#else
return (EOPNOTSUPP);
#endif
case KERN_SYSVIPC_SEM_INFO:
#ifdef SYSVSEM
infosize = sizeof(semsi->seminfo);
nds = seminfo.semmni;
dssize = sizeof(semsi->semids[0]);
break;
#else
return (EOPNOTSUPP);
#endif
case KERN_SYSVIPC_SHM_INFO:
#ifdef SYSVSHM
infosize = sizeof(shmsi->shminfo);
nds = shminfo.shmmni;
dssize = sizeof(shmsi->shmids[0]);
break;
#else
return (EOPNOTSUPP);
#endif
default:
return (EINVAL);
}
tsize = infosize + (nds * dssize);
/* Return just the total size required. */
if (where == NULL) {
*sizep = tsize;
return (0);
}
/* Not enough room for even the info struct. */
if (buflen < infosize) {
*sizep = 0;
return (ENOMEM);
}
bufsiz = min(tsize, buflen);
buf = malloc(bufsiz, M_TEMP, M_WAITOK|M_ZERO);
switch (*name) {
#ifdef SYSVSEM
case KERN_SYSVIPC_SEM_INFO:
semsi = (struct sem_sysctl_info *)buf;
semsi->seminfo = seminfo;
break;
#endif
#ifdef SYSVSHM
case KERN_SYSVIPC_SHM_INFO:
shmsi = (struct shm_sysctl_info *)buf;
shmsi->shminfo = shminfo;
break;
#endif
}
buflen -= infosize;
ret = 0;
if (buflen > 0) {
/* Fill in the IPC data structures. */
for (i = 0; i < nds; i++) {
if (buflen < dssize) {
ret = ENOMEM;
break;
}
switch (*name) {
#ifdef SYSVSEM
case KERN_SYSVIPC_SEM_INFO:
if (sema[i] != NULL)
memcpy(&semsi->semids[i], sema[i],
dssize);
else
memset(&semsi->semids[i], 0, dssize);
break;
#endif
#ifdef SYSVSHM
case KERN_SYSVIPC_SHM_INFO:
if (shmsegs[i] != NULL)
memcpy(&shmsi->shmids[i], shmsegs[i],
dssize);
else
memset(&shmsi->shmids[i], 0, dssize);
break;
#endif
}
buflen -= dssize;
}
}
*sizep -= buflen;
error = copyout(buf, where, *sizep);
free(buf, M_TEMP, bufsiz);
/* If copyout succeeded, use return code set earlier. */
return (error ? error : ret);
}
#endif /* SYSVMSG || SYSVSEM || SYSVSHM */
#ifndef SMALL_KERNEL
int
sysctl_intrcnt(int *name, u_int namelen, void *oldp, size_t *oldlenp)
{
return (evcount_sysctl(name, namelen, oldp, oldlenp, NULL, 0));
}
int
sysctl_sensors(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
struct ksensor *ks;
struct sensor *us;
struct ksensordev *ksd;
struct sensordev *usd;
int dev, numt, ret;
enum sensor_type type;
if (namelen != 1 && namelen != 3)
return (ENOTDIR);
dev = name[0];
if (namelen == 1) {
ret = sensordev_get(dev, &ksd);
if (ret)
return (ret);
/* Grab a copy, to clear the kernel pointers */
usd = malloc(sizeof(*usd), M_TEMP, M_WAITOK|M_ZERO);
usd->num = ksd->num;
strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
usd->sensors_count = ksd->sensors_count;
ret = sysctl_rdstruct(oldp, oldlenp, newp, usd,
sizeof(struct sensordev));
free(usd, M_TEMP, sizeof(*usd));
return (ret);
}
type = name[1];
numt = name[2];
ret = sensor_find(dev, type, numt, &ks);
if (ret)
return (ret);
/* Grab a copy, to clear the kernel pointers */
us = malloc(sizeof(*us), M_TEMP, M_WAITOK|M_ZERO);
memcpy(us->desc, ks->desc, sizeof(us->desc));
us->tv = ks->tv;
us->value = ks->value;
us->type = ks->type;
us->status = ks->status;
us->numt = ks->numt;
us->flags = ks->flags;
ret = sysctl_rdstruct(oldp, oldlenp, newp, us,
sizeof(struct sensor));
free(us, M_TEMP, sizeof(*us));
return (ret);
}
#endif /* SMALL_KERNEL */
int
sysctl_cptime2(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
int found = 0;
if (namelen != 1)
return (ENOTDIR);
CPU_INFO_FOREACH(cii, ci) {
if (name[0] == CPU_INFO_UNIT(ci)) {
found = 1;
break;
}
}
if (!found)
return (ENOENT);
return (sysctl_rdstruct(oldp, oldlenp, newp,
&ci->ci_schedstate.spc_cp_time,
sizeof(ci->ci_schedstate.spc_cp_time)));
}
#if NAUDIO > 0
int
sysctl_audio(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
if (namelen != 1)
return (ENOTDIR);
if (name[0] != KERN_AUDIO_RECORD)
return (ENOENT);
return (sysctl_int(oldp, oldlenp, newp, newlen, &audio_record_enable));
}
#endif
#if NVIDEO > 0
int
sysctl_video(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
if (namelen != 1)
return (ENOTDIR);
if (name[0] != KERN_VIDEO_RECORD)
return (ENOENT);
return (sysctl_int(oldp, oldlenp, newp, newlen, &video_record_enable));
}
#endif
int
sysctl_cpustats(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
CPU_INFO_ITERATOR cii;
struct cpustats cs;
struct cpu_info *ci;
int found = 0;
if (namelen != 1)
return (ENOTDIR);
CPU_INFO_FOREACH(cii, ci) {
if (name[0] == CPU_INFO_UNIT(ci)) {
found = 1;
break;
}
}
if (!found)
return (ENOENT);
memcpy(&cs.cs_time, &ci->ci_schedstate.spc_cp_time, sizeof(cs.cs_time));
cs.cs_flags = 0;
if (cpu_is_online(ci))
cs.cs_flags |= CPUSTATS_ONLINE;
return (sysctl_rdstruct(oldp, oldlenp, newp, &cs, sizeof(cs)));
}
int
sysctl_utc_offset(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
struct timespec adjusted, now;
int adjustment_seconds, error, new_offset_minutes, old_offset_minutes;
old_offset_minutes = utc_offset / 60; /* seconds -> minutes */
new_offset_minutes = old_offset_minutes;
error = sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&new_offset_minutes);
if (error)
return error;
if (new_offset_minutes < -24 * 60 || new_offset_minutes > 24 * 60)
return EINVAL;
if (new_offset_minutes == old_offset_minutes)
return 0;
utc_offset = new_offset_minutes * 60; /* minutes -> seconds */
adjustment_seconds = (new_offset_minutes - old_offset_minutes) * 60;
nanotime(&now);
adjusted = now;
adjusted.tv_sec -= adjustment_seconds;
tc_setrealtimeclock(&adjusted);
resettodr();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* $OpenBSD: if_dl.h,v 1.12 2017/05/04 15:00:24 bluhm Exp $ */
/* $NetBSD: if_dl.h,v 1.8 1995/03/26 20:30:13 jtc Exp $ */
/*
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)if_dl.h 8.1 (Berkeley) 6/10/93
*/
/*
* A Link-Level Sockaddr may specify the interface in one of two
* ways: either by means of a system-provided index number (computed
* anew and possibly differently on every reboot), or by a human-readable
* string such as "il0" (for managerial convenience).
*
* Census taking actions, such as something akin to SIOCGCONF would return
* both the index and the human name.
*
* High volume transactions (such as giving a link-level ``from'' address
* in a recvfrom or recvmsg call) may be likely only to provide the indexed
* form, (which requires fewer copy operations and less space).
*
* The form and interpretation of the link-level address is purely a matter
* of convention between the device driver and its consumers; however, it is
* expected that all drivers for an interface of a given if_type will agree.
*/
#ifndef _NET_IF_DL_H_
#define _NET_IF_DL_H_
/*
* Structure of a Link-Level sockaddr:
*/
struct sockaddr_dl {
u_char sdl_len; /* Total length of sockaddr */
u_char sdl_family; /* AF_LINK */
u_int16_t sdl_index; /* if != 0, system given index for interface */
u_char sdl_type; /* interface type */
u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */
u_char sdl_alen; /* link level address length */
u_char sdl_slen; /* link layer selector length, mostly 0 */
char sdl_data[24]; /* minimum work area, can be larger;
contains both if name and ll address;
big enough for IFNAMSIZ plus 8byte ll addr */
};
#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))
#ifdef _KERNEL
static inline struct sockaddr_dl *
satosdl(struct sockaddr *sa)
{
return ((struct sockaddr_dl *)(sa));
}
static inline struct sockaddr *
sdltosa(struct sockaddr_dl *sdl)
{
return ((struct sockaddr *)(sdl));
}
#else /* _KERNEL */
__BEGIN_DECLS
char *link_ntoa(const struct sockaddr_dl *);
__END_DECLS
#endif /* _KERNEL */
#endif /* _NET_IF_DL_H_ */
58
1
29
28
30
28
11
11
6
5
5
1
4
5
31
31
31
58
57
58
28
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
/* $OpenBSD: tcp_subr.c,v 1.188 2022/09/03 22:11:09 bluhm Exp $ */
/* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/timeout.h>
#include <sys/protosw.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#ifdef INET6
#include <netinet6/ip6protosw.h>
#endif /* INET6 */
#include <crypto/md5.h>
#include <crypto/sha2.h>
/*
* Locks used to protect struct members in this file:
* I immutable after creation
* T tcp_timer_mtx global tcp timer data structures
*/
struct mutex tcp_timer_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
/* patchable/settable parameters for tcp */
int tcp_mssdflt = TCP_MSS;
int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
/* values controllable via sysctl */
int tcp_do_rfc1323 = 1;
int tcp_do_sack = 1; /* RFC 2018 selective ACKs */
int tcp_ack_on_push = 0; /* set to enable immediate ACK-on-PUSH */
#ifdef TCP_ECN
int tcp_do_ecn = 0; /* RFC3168 ECN enabled/disabled? */
#endif
int tcp_do_rfc3390 = 2; /* Increase TCP's Initial Window to 10*mss */
#ifndef TCB_INITIAL_HASH_SIZE
#define TCB_INITIAL_HASH_SIZE 128
#endif
int tcp_reass_limit = NMBCLUSTERS / 8; /* hardlimit for tcpqe_pool */
int tcp_sackhole_limit = 32*1024; /* hardlimit for sackhl_pool */
struct pool tcpcb_pool;
struct pool tcpqe_pool;
struct pool sackhl_pool;
struct cpumem *tcpcounters; /* tcp statistics */
u_char tcp_secret[16]; /* [I] */
SHA2_CTX tcp_secret_ctx; /* [I] */
tcp_seq tcp_iss; /* [T] updated by timer and connection */
uint32_t tcp_now; /* [T] incremented by slow timer */
/*
* Tcp initialization
*/
void
tcp_init(void)
{
tcp_iss = 1; /* wrong */
tcp_now = 1;
pool_init(&tcpcb_pool, sizeof(struct tcpcb), 0, IPL_SOFTNET, 0,
"tcpcb", NULL);
pool_init(&tcpqe_pool, sizeof(struct tcpqent), 0, IPL_SOFTNET, 0,
"tcpqe", NULL);
pool_sethardlimit(&tcpqe_pool, tcp_reass_limit, NULL, 0);
pool_init(&sackhl_pool, sizeof(struct sackhole), 0, IPL_SOFTNET, 0,
"sackhl", NULL);
pool_sethardlimit(&sackhl_pool, tcp_sackhole_limit, NULL, 0);
in_pcbinit(&tcbtable, TCB_INITIAL_HASH_SIZE);
tcpcounters = counters_alloc(tcps_ncounters);
arc4random_buf(tcp_secret, sizeof(tcp_secret));
SHA512Init(&tcp_secret_ctx);
SHA512Update(&tcp_secret_ctx, tcp_secret, sizeof(tcp_secret));
#ifdef INET6
/*
* Since sizeof(struct ip6_hdr) > sizeof(struct ip), we
* do max length checks/computations only on the former.
*/
if (max_protohdr < (sizeof(struct ip6_hdr) + sizeof(struct tcphdr)))
max_protohdr = (sizeof(struct ip6_hdr) + sizeof(struct tcphdr));
if ((max_linkhdr + sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) >
MHLEN)
panic("tcp_init");
icmp6_mtudisc_callback_register(tcp6_mtudisc_callback);
#endif /* INET6 */
/* Initialize the compressed state engine. */
syn_cache_init();
/* Initialize timer state. */
tcp_timer_init();
}
/*
* Create template to be used to send tcp packets on a connection.
* Call after host entry created, allocates an mbuf and fills
* in a skeletal tcp/ip header, minimizing the amount of work
* necessary when the connection is used.
*
* To support IPv6 in addition to IPv4 and considering that the sizes of
* the IPv4 and IPv6 headers are not the same, we now use a separate pointer
* for the TCP header. Also, we made the former tcpiphdr header pointer
* into just an IP overlay pointer, with casting as appropriate for v6. rja
*/
struct mbuf *
tcp_template(struct tcpcb *tp)
{
struct inpcb *inp = tp->t_inpcb;
struct mbuf *m;
struct tcphdr *th;
CTASSERT(sizeof(struct ip) + sizeof(struct tcphdr) <= MHLEN);
CTASSERT(sizeof(struct ip6_hdr) + sizeof(struct tcphdr) <= MHLEN);
if ((m = tp->t_template) == 0) {
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == NULL)
return (0);
switch (tp->pf) {
case 0: /*default to PF_INET*/
case AF_INET:
m->m_len = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
m->m_len = sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
}
m->m_len += sizeof (struct tcphdr);
}
switch(tp->pf) {
case AF_INET:
{
struct ipovly *ipovly;
ipovly = mtod(m, struct ipovly *);
bzero(ipovly->ih_x1, sizeof ipovly->ih_x1);
ipovly->ih_pr = IPPROTO_TCP;
ipovly->ih_len = htons(sizeof (struct tcphdr));
ipovly->ih_src = inp->inp_laddr;
ipovly->ih_dst = inp->inp_faddr;
th = (struct tcphdr *)(mtod(m, caddr_t) +
sizeof(struct ip));
}
break;
#ifdef INET6
case AF_INET6:
{
struct ip6_hdr *ip6;
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_src = inp->inp_laddr6;
ip6->ip6_dst = inp->inp_faddr6;
ip6->ip6_flow = htonl(0x60000000) |
(inp->inp_flowinfo & IPV6_FLOWLABEL_MASK);
ip6->ip6_nxt = IPPROTO_TCP;
ip6->ip6_plen = htons(sizeof(struct tcphdr)); /*XXX*/
ip6->ip6_hlim = in6_selecthlim(inp); /*XXX*/
th = (struct tcphdr *)(mtod(m, caddr_t) +
sizeof(struct ip6_hdr));
}
break;
#endif /* INET6 */
}
th->th_sport = inp->inp_lport;
th->th_dport = inp->inp_fport;
th->th_seq = 0;
th->th_ack = 0;
th->th_x2 = 0;
th->th_off = 5;
th->th_flags = 0;
th->th_win = 0;
th->th_urp = 0;
th->th_sum = 0;
return (m);
}
/*
* Send a single message to the TCP at address specified by
* the given TCP/IP header. If m == 0, then we make a copy
* of the tcpiphdr at ti and send directly to the addressed host.
* This is used to force keep alive messages out using the TCP
* template for a connection tp->t_template. If flags are given
* then we send a message back to the TCP which originated the
* segment ti, and discard the mbuf containing it and any other
* attached mbufs.
*
* In any case the ack and sequence number of the transmitted
* segment are as specified by the parameters.
*/
void
tcp_respond(struct tcpcb *tp, caddr_t template, struct tcphdr *th0,
tcp_seq ack, tcp_seq seq, int flags, u_int rtableid, uint32_t now)
{
int tlen;
int win = 0;
struct mbuf *m = NULL;
struct tcphdr *th;
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif
int af; /* af on wire */
if (tp) {
struct socket *so = tp->t_inpcb->inp_socket;
win = sbspace(so, &so->so_rcv);
/*
* If this is called with an unconnected
* socket/tp/pcb (tp->pf is 0), we lose.
*/
af = tp->pf;
} else
af = (((struct ip *)template)->ip_v == 6) ? AF_INET6 : AF_INET;
m = m_gethdr(M_DONTWAIT, MT_HEADER);
if (m == NULL)
return;
m->m_data += max_linkhdr;
tlen = 0;
#define xchg(a,b,type) do { type t; t=a; a=b; b=t; } while (0)
switch (af) {
#ifdef INET6
case AF_INET6:
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)(ip6 + 1);
tlen = sizeof(*ip6) + sizeof(*th);
if (th0) {
bcopy(template, ip6, sizeof(*ip6));
bcopy(th0, th, sizeof(*th));
xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr);
} else {
bcopy(template, ip6, tlen);
}
break;
#endif /* INET6 */
case AF_INET:
ip = mtod(m, struct ip *);
th = (struct tcphdr *)(ip + 1);
tlen = sizeof(*ip) + sizeof(*th);
if (th0) {
bcopy(template, ip, sizeof(*ip));
bcopy(th0, th, sizeof(*th));
xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, u_int32_t);
} else {
bcopy(template, ip, tlen);
}
break;
}
if (th0)
xchg(th->th_dport, th->th_sport, u_int16_t);
else
flags = TH_ACK;
#undef xchg
th->th_seq = htonl(seq);
th->th_ack = htonl(ack);
th->th_x2 = 0;
th->th_off = sizeof (struct tcphdr) >> 2;
th->th_flags = flags;
if (tp)
win >>= tp->rcv_scale;
if (win > TCP_MAXWIN)
win = TCP_MAXWIN;
th->th_win = htons((u_int16_t)win);
th->th_urp = 0;
if (tp && (tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
(flags & TH_RST) == 0 && (tp->t_flags & TF_RCVD_TSTMP)) {
u_int32_t *lp = (u_int32_t *)(th + 1);
/* Form timestamp option as shown in appendix A of RFC 1323. */
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
*lp++ = htonl(now + tp->ts_modulate);
*lp = htonl(tp->ts_recent);
tlen += TCPOLEN_TSTAMP_APPA;
th->th_off = (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_APPA) >> 2;
}
m->m_len = tlen;
m->m_pkthdr.len = tlen;
m->m_pkthdr.ph_ifidx = 0;
m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
/* force routing table */
if (tp)
m->m_pkthdr.ph_rtableid = tp->t_inpcb->inp_rtableid;
else
m->m_pkthdr.ph_rtableid = rtableid;
switch (af) {
#ifdef INET6
case AF_INET6:
ip6->ip6_flow = htonl(0x60000000);
ip6->ip6_nxt = IPPROTO_TCP;
ip6->ip6_hlim = in6_selecthlim(tp ? tp->t_inpcb : NULL); /*XXX*/
ip6->ip6_plen = tlen - sizeof(struct ip6_hdr);
ip6->ip6_plen = htons(ip6->ip6_plen);
ip6_output(m, tp ? tp->t_inpcb->inp_outputopts6 : NULL,
tp ? &tp->t_inpcb->inp_route6 : NULL,
0, NULL,
tp ? tp->t_inpcb : NULL);
break;
#endif /* INET6 */
case AF_INET:
ip->ip_len = htons(tlen);
ip->ip_ttl = ip_defttl;
ip->ip_tos = 0;
ip_output(m, NULL,
tp ? &tp->t_inpcb->inp_route : NULL,
ip_mtudisc ? IP_MTUDISC : 0, NULL,
tp ? tp->t_inpcb : NULL, 0);
break;
}
}
/*
* Create a new TCP control block, making an
* empty reassembly queue and hooking it to the argument
* protocol control block.
*/
struct tcpcb *
tcp_newtcpcb(struct inpcb *inp)
{
struct tcpcb *tp;
int i;
tp = pool_get(&tcpcb_pool, PR_NOWAIT|PR_ZERO);
if (tp == NULL)
return (NULL);
TAILQ_INIT(&tp->t_segq);
tp->t_maxseg = tcp_mssdflt;
tp->t_maxopd = 0;
for (i = 0; i < TCPT_NTIMERS; i++)
TCP_TIMER_INIT(tp, i);
tp->sack_enable = tcp_do_sack;
tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
tp->t_inpcb = inp;
/*
* Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
* rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
* reasonable initial retransmit time.
*/
tp->t_srtt = TCPTV_SRTTBASE;
tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ <<
(TCP_RTTVAR_SHIFT + TCP_RTT_BASE_SHIFT - 1);
tp->t_rttmin = TCPTV_MIN;
TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
TCPTV_MIN, TCPTV_REXMTMAX);
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->t_pmtud_mtu_sent = 0;
tp->t_pmtud_mss_acked = 0;
#ifdef INET6
/* we disallow IPv4 mapped address completely. */
if ((inp->inp_flags & INP_IPV6) == 0)
tp->pf = PF_INET;
else
tp->pf = PF_INET6;
#else
tp->pf = PF_INET;
#endif
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
inp->inp_ipv6.ip6_hlim = ip6_defhlim;
else
#endif /* INET6 */
inp->inp_ip.ip_ttl = ip_defttl;
inp->inp_ppcb = (caddr_t)tp;
return (tp);
}
/*
* Drop a TCP connection, reporting
* the specified error. If connection is synchronized,
* then send a RST to peer.
*/
struct tcpcb *
tcp_drop(struct tcpcb *tp, int errno)
{
struct socket *so = tp->t_inpcb->inp_socket;
if (TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_state = TCPS_CLOSED;
(void) tcp_output(tp);
tcpstat_inc(tcps_drops);
} else
tcpstat_inc(tcps_conndrops);
if (errno == ETIMEDOUT && tp->t_softerror)
errno = tp->t_softerror;
so->so_error = errno;
return (tcp_close(tp));
}
/*
* Close a TCP control block:
* discard all space held by the tcp
* discard internet protocol block
* wake up any sleepers
*/
struct tcpcb *
tcp_close(struct tcpcb *tp)
{
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct sackhole *p, *q;
/* free the reassembly queue, if any */
tcp_freeq(tp);
tcp_canceltimers(tp);
syn_cache_cleanup(tp);
/* Free SACK holes. */
q = p = tp->snd_holes;
while (p != 0) {
q = p->next;
pool_put(&sackhl_pool, p);
p = q;
}
m_free(tp->t_template);
/* Free tcpcb after all pending timers have been run. */
TCP_TIMER_ARM(tp, TCPT_REAPER, 1);
inp->inp_ppcb = NULL;
soisdisconnected(so);
in_pcbdetach(inp);
return (NULL);
}
int
tcp_freeq(struct tcpcb *tp)
{
struct tcpqent *qe;
int rv = 0;
while ((qe = TAILQ_FIRST(&tp->t_segq)) != NULL) {
TAILQ_REMOVE(&tp->t_segq, qe, tcpqe_q);
m_freem(qe->tcpqe_m);
pool_put(&tcpqe_pool, qe);
rv = 1;
}
return (rv);
}
/*
* Compute proper scaling value for receiver window from buffer space
*/
void
tcp_rscale(struct tcpcb *tp, u_long hiwat)
{
tp->request_r_scale = 0;
while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
TCP_MAXWIN << tp->request_r_scale < hiwat)
tp->request_r_scale++;
}
/*
* Notify a tcp user of an asynchronous error;
* store error as soft error, but wake up user
* (for now, won't do anything until can select for soft error).
*/
void
tcp_notify(struct inpcb *inp, int error)
{
struct tcpcb *tp = intotcpcb(inp);
struct socket *so = inp->inp_socket;
/*
* Ignore some errors if we are hooked up.
* If connection hasn't completed, has retransmitted several times,
* and receives a second error, give up now. This is better
* than waiting a long time to establish a connection that
* can never complete.
*/
if (tp->t_state == TCPS_ESTABLISHED &&
(error == EHOSTUNREACH || error == ENETUNREACH ||
error == EHOSTDOWN)) {
return;
} else if (TCPS_HAVEESTABLISHED(tp->t_state) == 0 &&
tp->t_rxtshift > 3 && tp->t_softerror)
so->so_error = error;
else
tp->t_softerror = error;
wakeup((caddr_t) &so->so_timeo);
sorwakeup(so);
sowwakeup(so);
}
#ifdef INET6
void
tcp6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *d)
{
struct tcphdr th;
struct tcpcb *tp;
void (*notify)(struct inpcb *, int) = tcp_notify;
struct ip6_hdr *ip6;
const struct sockaddr_in6 *sa6_src = NULL;
struct sockaddr_in6 *sa6 = satosin6(sa);
struct inpcb *inp;
struct mbuf *m;
tcp_seq seq;
int off;
struct {
u_int16_t th_sport;
u_int16_t th_dport;
u_int32_t th_seq;
} *thp;
CTASSERT(sizeof(*thp) <= sizeof(th));
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6) ||
IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) ||
IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr))
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
else if (cmd == PRC_QUENCH) {
/*
* Don't honor ICMP Source Quench messages meant for
* TCP connections.
*/
/* XXX there's no PRC_QUENCH in IPv6 */
return;
} else if (PRC_IS_REDIRECT(cmd))
notify = in_rtchange, d = NULL;
else if (cmd == PRC_MSGSIZE)
; /* special code is present, see below */
else if (cmd == PRC_HOSTDEAD)
d = NULL;
else if (inet6ctlerrmap[cmd] == 0)
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d;
m = ip6cp->ip6c_m;
ip6 = ip6cp->ip6c_ip6;
off = ip6cp->ip6c_off;
sa6_src = ip6cp->ip6c_src;
} else {
m = NULL;
ip6 = NULL;
sa6_src = &sa6_any;
}
if (ip6) {
/*
* XXX: We assume that when ip6 is non NULL,
* M and OFF are valid.
*/
/* check if we can safely examine src and dst ports */
if (m->m_pkthdr.len < off + sizeof(*thp))
return;
bzero(&th, sizeof(th));
m_copydata(m, off, sizeof(*thp), &th);
/*
* Check to see if we have a valid TCP connection
* corresponding to the address in the ICMPv6 message
* payload.
*/
inp = in6_pcblookup(&tcbtable, &sa6->sin6_addr,
th.th_dport, &sa6_src->sin6_addr, th.th_sport, rdomain);
if (cmd == PRC_MSGSIZE) {
/*
* Depending on the value of "valid" and routing table
* size (mtudisc_{hi,lo}wat), we will:
* - recalculate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update((struct ip6ctlparam *)d,
inp != NULL);
in_pcbunref(inp);
return;
}
if (inp) {
seq = ntohl(th.th_seq);
if (inp->inp_socket &&
(tp = intotcpcb(inp)) &&
SEQ_GEQ(seq, tp->snd_una) &&
SEQ_LT(seq, tp->snd_max))
notify(inp, inet6ctlerrmap[cmd]);
} else if (inet6ctlerrmap[cmd] == EHOSTUNREACH ||
inet6ctlerrmap[cmd] == ENETUNREACH ||
inet6ctlerrmap[cmd] == EHOSTDOWN)
syn_cache_unreach((struct sockaddr *)sa6_src,
sa, &th, rdomain);
in_pcbunref(inp);
} else {
in6_pcbnotify(&tcbtable, sa6, 0,
sa6_src, 0, rdomain, cmd, NULL, notify);
}
}
#endif
void
tcp_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
{
struct ip *ip = v;
struct tcphdr *th;
struct tcpcb *tp;
struct inpcb *inp;
struct in_addr faddr;
tcp_seq seq;
u_int mtu;
void (*notify)(struct inpcb *, int) = tcp_notify;
int errno;
if (sa->sa_family != AF_INET)
return;
faddr = satosin(sa)->sin_addr;
if (faddr.s_addr == INADDR_ANY)
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
errno = inetctlerrmap[cmd];
if (cmd == PRC_QUENCH)
/*
* Don't honor ICMP Source Quench messages meant for
* TCP connections.
*/
return;
else if (PRC_IS_REDIRECT(cmd))
notify = in_rtchange, ip = 0;
else if (cmd == PRC_MSGSIZE && ip_mtudisc && ip) {
/*
* Verify that the packet in the icmp payload refers
* to an existing TCP connection.
*/
th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
seq = ntohl(th->th_seq);
inp = in_pcblookup(&tcbtable,
ip->ip_dst, th->th_dport, ip->ip_src, th->th_sport,
rdomain);
if (inp && (tp = intotcpcb(inp)) &&
SEQ_GEQ(seq, tp->snd_una) &&
SEQ_LT(seq, tp->snd_max)) {
struct icmp *icp;
icp = (struct icmp *)((caddr_t)ip -
offsetof(struct icmp, icmp_ip));
/*
* If the ICMP message advertises a Next-Hop MTU
* equal or larger than the maximum packet size we have
* ever sent, drop the message.
*/
mtu = (u_int)ntohs(icp->icmp_nextmtu);
if (mtu >= tp->t_pmtud_mtu_sent) {
in_pcbunref(inp);
return;
}
if (mtu >= tcp_hdrsz(tp) + tp->t_pmtud_mss_acked) {
/*
* Calculate new MTU, and create corresponding
* route (traditional PMTUD).
*/
tp->t_flags &= ~TF_PMTUD_PEND;
icmp_mtudisc(icp, inp->inp_rtableid);
} else {
/*
* Record the information got in the ICMP
* message; act on it later.
* If we had already recorded an ICMP message,
* replace the old one only if the new message
* refers to an older TCP segment
*/
if (tp->t_flags & TF_PMTUD_PEND) {
if (SEQ_LT(tp->t_pmtud_th_seq, seq)) {
in_pcbunref(inp);
return;
}
} else
tp->t_flags |= TF_PMTUD_PEND;
tp->t_pmtud_th_seq = seq;
tp->t_pmtud_nextmtu = icp->icmp_nextmtu;
tp->t_pmtud_ip_len = icp->icmp_ip.ip_len;
tp->t_pmtud_ip_hl = icp->icmp_ip.ip_hl;
in_pcbunref(inp);
return;
}
} else {
/* ignore if we don't have a matching connection */
in_pcbunref(inp);
return;
}
in_pcbunref(inp);
notify = tcp_mtudisc, ip = 0;
} else if (cmd == PRC_MTUINC)
notify = tcp_mtudisc_increase, ip = 0;
else if (cmd == PRC_HOSTDEAD)
ip = 0;
else if (errno == 0)
return;
if (ip) {
th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
inp = in_pcblookup(&tcbtable,
ip->ip_dst, th->th_dport, ip->ip_src, th->th_sport,
rdomain);
if (inp) {
seq = ntohl(th->th_seq);
if (inp->inp_socket &&
(tp = intotcpcb(inp)) &&
SEQ_GEQ(seq, tp->snd_una) &&
SEQ_LT(seq, tp->snd_max))
notify(inp, errno);
} else if (inetctlerrmap[cmd] == EHOSTUNREACH ||
inetctlerrmap[cmd] == ENETUNREACH ||
inetctlerrmap[cmd] == EHOSTDOWN) {
struct sockaddr_in sin;
bzero(&sin, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = th->th_sport;
sin.sin_addr = ip->ip_src;
syn_cache_unreach(sintosa(&sin), sa, th, rdomain);
}
in_pcbunref(inp);
} else
in_pcbnotifyall(&tcbtable, sa, rdomain, errno, notify);
}
#ifdef INET6
/*
* Path MTU Discovery handlers.
*/
void
tcp6_mtudisc_callback(struct sockaddr_in6 *sin6, u_int rdomain)
{
in6_pcbnotify(&tcbtable, sin6, 0,
&sa6_any, 0, rdomain, PRC_MSGSIZE, NULL, tcp_mtudisc);
}
#endif /* INET6 */
/*
* On receipt of path MTU corrections, flush old route and replace it
* with the new one. Retransmit all unacknowledged packets, to ensure
* that all packets will be received.
*/
void
tcp_mtudisc(struct inpcb *inp, int errno)
{
struct tcpcb *tp = intotcpcb(inp);
struct rtentry *rt;
int orig_maxseg, change = 0;
if (tp == NULL)
return;
orig_maxseg = tp->t_maxseg;
rt = in_pcbrtentry(inp);
if (rt != NULL) {
unsigned int orig_mtulock = (rt->rt_locks & RTV_MTU);
/*
* If this was not a host route, remove and realloc.
*/
if ((rt->rt_flags & RTF_HOST) == 0) {
in_rtchange(inp, errno);
if ((rt = in_pcbrtentry(inp)) == NULL)
return;
}
if (orig_mtulock < (rt->rt_locks & RTV_MTU))
change = 1;
}
tcp_mss(tp, -1);
if (orig_maxseg > tp->t_maxseg)
change = 1;
/*
* Resend unacknowledged packets
*/
tp->snd_nxt = tp->snd_una;
if (change || errno > 0)
tcp_output(tp);
}
void
tcp_mtudisc_increase(struct inpcb *inp, int errno)
{
struct tcpcb *tp = intotcpcb(inp);
struct rtentry *rt = in_pcbrtentry(inp);
if (tp != 0 && rt != 0) {
/*
* If this was a host route, remove and realloc.
*/
if (rt->rt_flags & RTF_HOST)
in_rtchange(inp, errno);
/* also takes care of congestion window */
tcp_mss(tp, -1);
}
}
/*
* Generate new ISNs with a method based on RFC1948
*/
#define TCP_ISS_CONN_INC 4096
void
tcp_set_iss_tsm(struct tcpcb *tp)
{
SHA2_CTX ctx;
union {
uint8_t bytes[SHA512_DIGEST_LENGTH];
uint32_t words[2];
} digest;
u_int rdomain = rtable_l2(tp->t_inpcb->inp_rtableid);
tcp_seq iss;
mtx_enter(&tcp_timer_mtx);
tcp_iss += TCP_ISS_CONN_INC;
iss = tcp_iss;
mtx_leave(&tcp_timer_mtx);
ctx = tcp_secret_ctx;
SHA512Update(&ctx, &rdomain, sizeof(rdomain));
SHA512Update(&ctx, &tp->t_inpcb->inp_lport, sizeof(u_short));
SHA512Update(&ctx, &tp->t_inpcb->inp_fport, sizeof(u_short));
if (tp->pf == AF_INET6) {
SHA512Update(&ctx, &tp->t_inpcb->inp_laddr6,
sizeof(struct in6_addr));
SHA512Update(&ctx, &tp->t_inpcb->inp_faddr6,
sizeof(struct in6_addr));
} else {
SHA512Update(&ctx, &tp->t_inpcb->inp_laddr,
sizeof(struct in_addr));
SHA512Update(&ctx, &tp->t_inpcb->inp_faddr,
sizeof(struct in_addr));
}
SHA512Final(digest.bytes, &ctx);
tp->iss = digest.words[0] + iss;
tp->ts_modulate = digest.words[1];
}
#ifdef TCP_SIGNATURE
int
tcp_signature_tdb_attach(void)
{
return (0);
}
int
tcp_signature_tdb_init(struct tdb *tdbp, const struct xformsw *xsp,
struct ipsecinit *ii)
{
if ((ii->ii_authkeylen < 1) || (ii->ii_authkeylen > 80))
return (EINVAL);
tdbp->tdb_amxkey = malloc(ii->ii_authkeylen, M_XDATA, M_NOWAIT);
if (tdbp->tdb_amxkey == NULL)
return (ENOMEM);
memcpy(tdbp->tdb_amxkey, ii->ii_authkey, ii->ii_authkeylen);
tdbp->tdb_amxkeylen = ii->ii_authkeylen;
return (0);
}
int
tcp_signature_tdb_zeroize(struct tdb *tdbp)
{
if (tdbp->tdb_amxkey) {
explicit_bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
free(tdbp->tdb_amxkey, M_XDATA, tdbp->tdb_amxkeylen);
tdbp->tdb_amxkey = NULL;
}
return (0);
}
int
tcp_signature_tdb_input(struct mbuf **mp, struct tdb *tdbp, int skip,
int protoff)
{
m_freemp(mp);
return (IPPROTO_DONE);
}
int
tcp_signature_tdb_output(struct mbuf *m, struct tdb *tdbp, int skip,
int protoff)
{
m_freem(m);
return (EINVAL);
}
int
tcp_signature_apply(caddr_t fstate, caddr_t data, unsigned int len)
{
MD5Update((MD5_CTX *)fstate, (char *)data, len);
return 0;
}
int
tcp_signature(struct tdb *tdb, int af, struct mbuf *m, struct tcphdr *th,
int iphlen, int doswap, char *sig)
{
MD5_CTX ctx;
int len;
struct tcphdr th0;
MD5Init(&ctx);
switch(af) {
case 0:
case AF_INET: {
struct ippseudo ippseudo;
struct ip *ip;
ip = mtod(m, struct ip *);
ippseudo.ippseudo_src = ip->ip_src;
ippseudo.ippseudo_dst = ip->ip_dst;
ippseudo.ippseudo_pad = 0;
ippseudo.ippseudo_p = IPPROTO_TCP;
ippseudo.ippseudo_len = htons(m->m_pkthdr.len - iphlen);
MD5Update(&ctx, (char *)&ippseudo,
sizeof(struct ippseudo));
break;
}
#ifdef INET6
case AF_INET6: {
struct ip6_hdr_pseudo ip6pseudo;
struct ip6_hdr *ip6;
ip6 = mtod(m, struct ip6_hdr *);
bzero(&ip6pseudo, sizeof(ip6pseudo));
ip6pseudo.ip6ph_src = ip6->ip6_src;
ip6pseudo.ip6ph_dst = ip6->ip6_dst;
in6_clearscope(&ip6pseudo.ip6ph_src);
in6_clearscope(&ip6pseudo.ip6ph_dst);
ip6pseudo.ip6ph_nxt = IPPROTO_TCP;
ip6pseudo.ip6ph_len = htonl(m->m_pkthdr.len - iphlen);
MD5Update(&ctx, (char *)&ip6pseudo,
sizeof(ip6pseudo));
break;
}
#endif
}
th0 = *th;
th0.th_sum = 0;
if (doswap) {
th0.th_seq = htonl(th0.th_seq);
th0.th_ack = htonl(th0.th_ack);
th0.th_win = htons(th0.th_win);
th0.th_urp = htons(th0.th_urp);
}
MD5Update(&ctx, (char *)&th0, sizeof(th0));
len = m->m_pkthdr.len - iphlen - th->th_off * sizeof(uint32_t);
if (len > 0 &&
m_apply(m, iphlen + th->th_off * sizeof(uint32_t), len,
tcp_signature_apply, (caddr_t)&ctx))
return (-1);
MD5Update(&ctx, tdb->tdb_amxkey, tdb->tdb_amxkeylen);
MD5Final(sig, &ctx);
return (0);
}
#endif /* TCP_SIGNATURE */
241
12
2
1
229
229
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
/* $OpenBSD: subr_autoconf.c,v 1.96 2022/04/07 09:37:32 tb Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratories.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL)
*
* @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/device.h>
#include <sys/hotplug.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/mutex.h>
#include <sys/atomic.h>
#include <sys/reboot.h>
#include "hotplug.h"
#include "mpath.h"
/*
* Autoconfiguration subroutines.
*/
/*
* ioconf.c exports exactly two names: cfdata and cfroots. All system
* devices and drivers are found via these tables.
*/
extern short cfroots[];
#define ROOT ((struct device *)NULL)
struct matchinfo {
cfmatch_t fn;
struct device *parent;
void *match, *aux;
int indirect, pri;
};
#ifndef AUTOCONF_VERBOSE
#define AUTOCONF_VERBOSE 0
#endif /* AUTOCONF_VERBOSE */
int autoconf_verbose = AUTOCONF_VERBOSE; /* trace probe calls */
static void mapply(struct matchinfo *, struct cfdata *);
struct deferred_config {
TAILQ_ENTRY(deferred_config) dc_queue;
struct device *dc_dev;
void (*dc_func)(struct device *);
};
TAILQ_HEAD(, deferred_config) deferred_config_queue;
TAILQ_HEAD(, deferred_config) mountroot_config_queue;
void *config_rootsearch(cfmatch_t, char *, void *);
void config_process_deferred_children(struct device *);
struct devicelist alldevs; /* list of all devices */
volatile int config_pending; /* semaphore for mountroot */
struct mutex autoconf_attdet_mtx = MUTEX_INITIALIZER(IPL_HIGH);
/*
* If > 0, devices are being attached and any thread which tries to
* detach will sleep; if < 0 devices are being detached and any
* thread which tries to attach will sleep.
*/
int autoconf_attdet;
/*
* Initialize autoconfiguration data structures. This occurs before console
* initialization as that might require use of this subsystem. Furthermore
* this means that malloc et al. isn't yet available.
*/
void
config_init(void)
{
TAILQ_INIT(&deferred_config_queue);
TAILQ_INIT(&mountroot_config_queue);
TAILQ_INIT(&alldevs);
}
/*
* Apply the matching function and choose the best. This is used
* a few times and we want to keep the code small.
*/
void
mapply(struct matchinfo *m, struct cfdata *cf)
{
int pri;
void *match;
if (m->indirect)
match = config_make_softc(m->parent, cf);
else
match = cf;
if (autoconf_verbose) {
printf(">>> probing for %s", cf->cf_driver->cd_name);
if (cf->cf_fstate == FSTATE_STAR)
printf("*\n");
else
printf("%d\n", cf->cf_unit);
}
if (m->fn != NULL)
pri = (*m->fn)(m->parent, match, m->aux);
else {
if (cf->cf_attach->ca_match == NULL) {
panic("mapply: no match function for '%s' device",
cf->cf_driver->cd_name);
}
pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux);
}
if (autoconf_verbose)
printf(">>> %s probe returned %d\n", cf->cf_driver->cd_name,
pri);
if (pri > m->pri) {
if (m->indirect && m->match) {
cf = ((struct device *)m->match)->dv_cfdata;
free(m->match, M_DEVBUF, cf->cf_attach->ca_devsize);
}
m->match = match;
m->pri = pri;
} else {
if (m->indirect)
free(match, M_DEVBUF, cf->cf_attach->ca_devsize);
}
}
/*
* Iterate over all potential children of some device, calling the given
* function (default being the child's match function) for each one.
* Nonzero returns are matches; the highest value returned is considered
* the best match. Return the `found child' if we got a match, or NULL
* otherwise. The `aux' pointer is simply passed on through.
*
* Note that this function is designed so that it can be used to apply
* an arbitrary function to all potential children (its return value
* can be ignored).
*/
void *
config_search(cfmatch_t fn, struct device *parent, void *aux)
{
struct cfdata *cf;
short *p;
struct matchinfo m;
m.fn = fn;
m.parent = parent;
m.match = NULL;
m.aux = aux;
m.indirect = parent && (parent->dv_cfdata->cf_driver->cd_mode & CD_INDIRECT);
m.pri = 0;
for (cf = cfdata; cf->cf_driver; cf++) {
/*
* Skip cf if no longer eligible, otherwise scan
* through parents for one matching `parent',
* and try match function.
*/
if (cf->cf_fstate == FSTATE_FOUND)
continue;
if (cf->cf_fstate == FSTATE_DNOTFOUND ||
cf->cf_fstate == FSTATE_DSTAR)
continue;
if (boothowto & RB_UNHIBERNATE) {
if (cf->cf_driver->cd_mode & CD_SKIPHIBERNATE)
continue;
if (cf->cf_driver->cd_class == DV_IFNET)
continue;
if (cf->cf_driver->cd_class == DV_TAPE)
continue;
}
for (p = cf->cf_parents; *p >= 0; p++)
if (parent->dv_cfdata == &cfdata[*p])
mapply(&m, cf);
}
if (autoconf_verbose) {
if (m.match) {
if (m.indirect)
cf = ((struct device *)m.match)->dv_cfdata;
else
cf = (struct cfdata *)m.match;
printf(">>> %s probe won\n",
cf->cf_driver->cd_name);
} else
printf(">>> no winning probe\n");
}
return (m.match);
}
/*
* Iterate over all potential children of some device, calling the given
* function for each one.
*
* Note that this function is designed so that it can be used to apply
* an arbitrary function to all potential children (its return value
* can be ignored).
*/
void
config_scan(cfscan_t fn, struct device *parent)
{
struct cfdata *cf;
short *p;
void *match;
int indirect;
indirect = parent && (parent->dv_cfdata->cf_driver->cd_mode & CD_INDIRECT);
for (cf = cfdata; cf->cf_driver; cf++) {
/*
* Skip cf if no longer eligible, otherwise scan
* through parents for one matching `parent',
* and try match function.
*/
if (cf->cf_fstate == FSTATE_FOUND)
continue;
if (cf->cf_fstate == FSTATE_DNOTFOUND ||
cf->cf_fstate == FSTATE_DSTAR)
continue;
for (p = cf->cf_parents; *p >= 0; p++)
if (parent->dv_cfdata == &cfdata[*p]) {
match = indirect?
config_make_softc(parent, cf) :
(void *)cf;
(*fn)(parent, match);
}
}
}
/*
* Find the given root device.
* This is much like config_search, but there is no parent.
*/
void *
config_rootsearch(cfmatch_t fn, char *rootname, void *aux)
{
struct cfdata *cf;
short *p;
struct matchinfo m;
m.fn = fn;
m.parent = ROOT;
m.match = NULL;
m.aux = aux;
m.indirect = 0;
m.pri = 0;
/*
* Look at root entries for matching name. We do not bother
* with found-state here since only one instance of each possible
* root child should ever be searched.
*/
for (p = cfroots; *p >= 0; p++) {
cf = &cfdata[*p];
if (cf->cf_fstate == FSTATE_DNOTFOUND ||
cf->cf_fstate == FSTATE_DSTAR)
continue;
if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
mapply(&m, cf);
}
return (m.match);
}
const char *msgs[3] = { "", " not configured\n", " unsupported\n" };
/*
* The given `aux' argument describes a device that has been found
* on the given parent, but not necessarily configured. Locate the
* configuration data for that device (using the submatch function
* provided, or using candidates' cd_match configuration driver
* functions) and attach it, and return true. If the device was
* not configured, call the given `print' function and return 0.
*/
struct device *
config_found_sm(struct device *parent, void *aux, cfprint_t print,
cfmatch_t submatch)
{
void *match;
if ((match = config_search(submatch, parent, aux)) != NULL)
return (config_attach(parent, match, aux, print));
if (print)
printf("%s", msgs[(*print)(aux, parent->dv_xname)]);
return (NULL);
}
/*
* As above, but for root devices.
*/
struct device *
config_rootfound(char *rootname, void *aux)
{
void *match;
if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
return (config_attach(ROOT, match, aux, (cfprint_t)NULL));
printf("root device %s not configured\n", rootname);
return (NULL);
}
/*
* Attach a found device. Allocates memory for device variables.
*/
struct device *
config_attach(struct device *parent, void *match, void *aux, cfprint_t print)
{
struct cfdata *cf;
struct device *dev;
struct cfdriver *cd;
const struct cfattach *ca;
mtx_enter(&autoconf_attdet_mtx);
while (autoconf_attdet < 0)
msleep_nsec(&autoconf_attdet, &autoconf_attdet_mtx,
PWAIT, "autoconf", INFSLP);
autoconf_attdet++;
mtx_leave(&autoconf_attdet_mtx);
if (parent && (parent->dv_cfdata->cf_driver->cd_mode & CD_INDIRECT)) {
dev = match;
cf = dev->dv_cfdata;
} else {
cf = match;
dev = config_make_softc(parent, cf);
}
cd = cf->cf_driver;
ca = cf->cf_attach;
KASSERT(cd->cd_devs != NULL);
KASSERT(dev->dv_unit < cd->cd_ndevs);
KASSERT(cd->cd_devs[dev->dv_unit] == NULL);
cd->cd_devs[dev->dv_unit] = dev;
/*
* If this is a "STAR" device and we used the last unit, prepare for
* another one.
*/
if (cf->cf_fstate == FSTATE_STAR) {
if (dev->dv_unit == cf->cf_unit)
cf->cf_unit++;
} else
cf->cf_fstate = FSTATE_FOUND;
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
device_ref(dev);
if (parent == ROOT)
printf("%s at root", dev->dv_xname);
else {
printf("%s at %s", dev->dv_xname, parent->dv_xname);
if (print)
(void) (*print)(aux, NULL);
}
/*
* Before attaching, clobber any unfound devices that are
* otherwise identical, or bump the unit number on all starred
* cfdata for this device.
*/
for (cf = cfdata; cf->cf_driver; cf++) {
if (cf->cf_driver == cd &&
cf->cf_unit == dev->dv_unit) {
if (cf->cf_fstate == FSTATE_NOTFOUND)
cf->cf_fstate = FSTATE_FOUND;
if (cf->cf_fstate == FSTATE_STAR)
cf->cf_unit++;
}
}
device_register(dev, aux);
(*ca->ca_attach)(parent, dev, aux);
config_process_deferred_children(dev);
#if NHOTPLUG > 0
if (!cold)
hotplug_device_attach(cd->cd_class, dev->dv_xname);
#endif
mtx_enter(&autoconf_attdet_mtx);
if (--autoconf_attdet == 0)
wakeup(&autoconf_attdet);
mtx_leave(&autoconf_attdet_mtx);
return (dev);
}
struct device *
config_make_softc(struct device *parent, struct cfdata *cf)
{
struct device *dev;
struct cfdriver *cd;
const struct cfattach *ca;
cd = cf->cf_driver;
ca = cf->cf_attach;
if (ca->ca_devsize < sizeof(struct device))
panic("config_make_softc");
/* get memory for all device vars */
dev = malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT|M_ZERO);
if (dev == NULL)
panic("config_make_softc: allocation for device softc failed");
dev->dv_class = cd->cd_class;
dev->dv_cfdata = cf;
dev->dv_flags = DVF_ACTIVE; /* always initially active */
/* If this is a STAR device, search for a free unit number */
if (cf->cf_fstate == FSTATE_STAR) {
for (dev->dv_unit = cf->cf_starunit1;
dev->dv_unit < cf->cf_unit; dev->dv_unit++)
if (cd->cd_ndevs == 0 ||
dev->dv_unit >= cd->cd_ndevs ||
cd->cd_devs[dev->dv_unit] == NULL)
break;
} else
dev->dv_unit = cf->cf_unit;
/* Build the device name into dv_xname. */
if (snprintf(dev->dv_xname, sizeof(dev->dv_xname), "%s%d",
cd->cd_name, dev->dv_unit) >= sizeof(dev->dv_xname))
panic("config_make_softc: device name too long");
dev->dv_parent = parent;
/* put this device in the devices array */
if (dev->dv_unit >= cd->cd_ndevs) {
/*
* Need to expand the array.
*/
int old = cd->cd_ndevs, new;
void **nsp;
if (old == 0)
new = MINALLOCSIZE / sizeof(void *);
else
new = old * 2;
while (new <= dev->dv_unit)
new *= 2;
cd->cd_ndevs = new;
nsp = mallocarray(new, sizeof(void *), M_DEVBUF, M_NOWAIT|M_ZERO);
if (nsp == NULL)
panic("config_make_softc: %sing dev array",
old != 0 ? "expand" : "creat");
if (old != 0) {
bcopy(cd->cd_devs, nsp, old * sizeof(void *));
free(cd->cd_devs, M_DEVBUF, old * sizeof(void *));
}
cd->cd_devs = nsp;
}
if (cd->cd_devs[dev->dv_unit])
panic("config_make_softc: duplicate %s", dev->dv_xname);
dev->dv_ref = 1;
return (dev);
}
/*
* Detach a device. Optionally forced (e.g. because of hardware
* removal) and quiet. Returns zero if successful, non-zero
* (an error code) otherwise.
*
* Note that this code wants to be run from a process context, so
* that the detach can sleep to allow processes which have a device
* open to run and unwind their stacks.
*/
int
config_detach(struct device *dev, int flags)
{
struct cfdata *cf;
const struct cfattach *ca;
struct cfdriver *cd;
int rv = 0, i;
#ifdef DIAGNOSTIC
struct device *d;
#endif
#if NHOTPLUG > 0
char devname[16];
#endif
mtx_enter(&autoconf_attdet_mtx);
while (autoconf_attdet > 0)
msleep_nsec(&autoconf_attdet, &autoconf_attdet_mtx,
PWAIT, "autoconf", INFSLP);
autoconf_attdet--;
mtx_leave(&autoconf_attdet_mtx);
#if NHOTPLUG > 0
strlcpy(devname, dev->dv_xname, sizeof(devname));
#endif
cf = dev->dv_cfdata;
#ifdef DIAGNOSTIC
if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR)
panic("config_detach: bad device fstate");
#endif
ca = cf->cf_attach;
cd = cf->cf_driver;
/*
* Ensure the device is deactivated. If the device has an
* activation entry point and DVF_ACTIVE is still set, the
* device is busy, and the detach fails.
*/
rv = config_deactivate(dev);
/*
* Try to detach the device. If that's not possible, then
* we either panic() (for the forced but failed case), or
* return an error.
*/
if (rv == 0) {
if (ca->ca_detach != NULL)
rv = (*ca->ca_detach)(dev, flags);
else
rv = EOPNOTSUPP;
}
if (rv != 0) {
if ((flags & DETACH_FORCE) == 0)
goto done;
else
panic("config_detach: forced detach of %s failed (%d)",
dev->dv_xname, rv);
}
/*
* The device has now been successfully detached.
*/
#ifdef DIAGNOSTIC
/*
* Sanity: If you're successfully detached, you should have no
* children. (Note that because children must be attached
* after parents, we only need to search the latter part of
* the list.)
*/
i = 0;
for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
d = TAILQ_NEXT(d, dv_list)) {
if (d->dv_parent == dev) {
printf("config_detach: %s attached at %s\n",
d->dv_xname, dev->dv_xname);
i = 1;
}
}
if (i != 0)
panic("config_detach: detached device (%s) has children",
dev->dv_xname);
#endif
/*
* Mark cfdata to show that the unit can be reused, if possible.
* Note that we can only re-use a starred unit number if the unit
* being detached had the last assigned unit number.
*/
for (cf = cfdata; cf->cf_driver; cf++) {
if (cf->cf_driver == cd) {
if (cf->cf_fstate == FSTATE_FOUND &&
cf->cf_unit == dev->dv_unit)
cf->cf_fstate = FSTATE_NOTFOUND;
if (cf->cf_fstate == FSTATE_STAR &&
cf->cf_unit == dev->dv_unit + 1)
cf->cf_unit--;
}
}
/*
* Unlink from device list.
*/
TAILQ_REMOVE(&alldevs, dev, dv_list);
device_unref(dev);
/*
* Remove from cfdriver's array, tell the world, and free softc.
*/
cd->cd_devs[dev->dv_unit] = NULL;
if ((flags & DETACH_QUIET) == 0)
printf("%s detached\n", dev->dv_xname);
device_unref(dev);
/*
* If the device now has no units in use, deallocate its softc array.
*/
for (i = 0; i < cd->cd_ndevs; i++)
if (cd->cd_devs[i] != NULL)
break;
if (i == cd->cd_ndevs) { /* nothing found; deallocate */
free(cd->cd_devs, M_DEVBUF, cd->cd_ndevs * sizeof(void *));
cd->cd_devs = NULL;
cd->cd_ndevs = 0;
cf->cf_unit = 0;
}
#if NHOTPLUG > 0
if (!cold)
hotplug_device_detach(cd->cd_class, devname);
#endif
/*
* Return success.
*/
done:
mtx_enter(&autoconf_attdet_mtx);
if (++autoconf_attdet == 0)
wakeup(&autoconf_attdet);
mtx_leave(&autoconf_attdet_mtx);
return (rv);
}
int
config_deactivate(struct device *dev)
{
int rv = 0, oflags = dev->dv_flags;
if (dev->dv_flags & DVF_ACTIVE) {
dev->dv_flags &= ~DVF_ACTIVE;
rv = config_suspend(dev, DVACT_DEACTIVATE);
if (rv)
dev->dv_flags = oflags;
}
return (rv);
}
/*
* Defer the configuration of the specified device until all
* of its parent's devices have been attached.
*/
void
config_defer(struct device *dev, void (*func)(struct device *))
{
struct deferred_config *dc;
if (dev->dv_parent == NULL)
panic("config_defer: can't defer config of a root device");
#ifdef DIAGNOSTIC
for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL;
dc = TAILQ_NEXT(dc, dc_queue)) {
if (dc->dc_dev == dev)
panic("config_defer: deferred twice");
}
#endif
if ((dc = malloc(sizeof(*dc), M_DEVBUF, M_NOWAIT)) == NULL)
panic("config_defer: can't allocate defer structure");
dc->dc_dev = dev;
dc->dc_func = func;
TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue);
config_pending_incr();
}
/*
* Defer the configuration of the specified device until after
* root file system is mounted.
*/
void
config_mountroot(struct device *dev, void (*func)(struct device *))
{
struct deferred_config *dc;
/*
* No need to defer if root file system is already mounted.
*/
if (rootvp != NULL) {
(*func)(dev);
return;
}
#ifdef DIAGNOSTIC
for (dc = TAILQ_FIRST(&mountroot_config_queue); dc != NULL;
dc = TAILQ_NEXT(dc, dc_queue)) {
if (dc->dc_dev == dev)
panic("config_mountroot: deferred twice");
}
#endif
if ((dc = malloc(sizeof(*dc), M_DEVBUF, M_NOWAIT)) == NULL)
panic("config_mountroot: can't allocate defer structure");
dc->dc_dev = dev;
dc->dc_func = func;
TAILQ_INSERT_TAIL(&mountroot_config_queue, dc, dc_queue);
}
/*
* Process the deferred configuration queue for a device.
*/
void
config_process_deferred_children(struct device *parent)
{
struct deferred_config *dc, *ndc;
for (dc = TAILQ_FIRST(&deferred_config_queue);
dc != NULL; dc = ndc) {
ndc = TAILQ_NEXT(dc, dc_queue);
if (dc->dc_dev->dv_parent == parent) {
TAILQ_REMOVE(&deferred_config_queue, dc, dc_queue);
(*dc->dc_func)(dc->dc_dev);
free(dc, M_DEVBUF, sizeof(*dc));
config_pending_decr();
}
}
}
/*
* Process the deferred configuration queue after the root file
* system is mounted .
*/
void
config_process_deferred_mountroot(void)
{
struct deferred_config *dc;
while ((dc = TAILQ_FIRST(&mountroot_config_queue)) != NULL) {
TAILQ_REMOVE(&mountroot_config_queue, dc, dc_queue);
(*dc->dc_func)(dc->dc_dev);
free(dc, M_DEVBUF, sizeof(*dc));
}
}
/*
* Manipulate the config_pending semaphore.
*/
void
config_pending_incr(void)
{
config_pending++;
}
void
config_pending_decr(void)
{
#ifdef DIAGNOSTIC
if (config_pending == 0)
panic("config_pending_decr: config_pending == 0");
#endif
config_pending--;
if (config_pending == 0)
wakeup((void *)&config_pending);
}
int
config_detach_children(struct device *parent, int flags)
{
struct device *dev, *next_dev;
int rv = 0;
/*
* The config_detach routine may sleep, meaning devices
* may be added to the queue. However, all devices will
* be added to the tail of the queue, the queue won't
* be re-organized, and the subtree of parent here should be locked
* for purposes of adding/removing children.
*
* Note that we can not afford trying to walk the device list
* once - our ``next'' device might be a child of the device
* we are about to detach, so it would disappear.
* Just play it safe and restart from the parent.
*/
for (dev = TAILQ_LAST(&alldevs, devicelist);
dev != NULL; dev = next_dev) {
if (dev->dv_parent == parent) {
if ((rv = config_detach(dev, flags)) != 0)
return (rv);
next_dev = TAILQ_LAST(&alldevs, devicelist);
} else {
next_dev = TAILQ_PREV(dev, devicelist, dv_list);
}
}
return (0);
}
int
config_suspend(struct device *dev, int act)
{
const struct cfattach *ca = dev->dv_cfdata->cf_attach;
int r;
device_ref(dev);
if (ca->ca_activate)
r = (*ca->ca_activate)(dev, act);
else
r = config_activate_children(dev, act);
device_unref(dev);
return (r);
}
int
config_suspend_all(int act)
{
struct device *mainbus = device_mainbus();
struct device *mpath = device_mpath();
int rv = 0;
switch (act) {
case DVACT_QUIESCE:
case DVACT_SUSPEND:
case DVACT_POWERDOWN:
if (mpath) {
rv = config_suspend(mpath, act);
if (rv)
return rv;
}
if (mainbus)
rv = config_suspend(mainbus, act);
break;
case DVACT_RESUME:
case DVACT_WAKEUP:
if (mainbus) {
rv = config_suspend(mainbus, act);
if (rv)
return rv;
}
if (mpath)
rv = config_suspend(mpath, act);
break;
}
return (rv);
}
/*
* Call the ca_activate for each of our children, letting each
* decide whether they wish to do the same for their children
* and more.
*/
int
config_activate_children(struct device *parent, int act)
{
struct device *d;
int rv = 0;
for (d = TAILQ_NEXT(parent, dv_list); d != NULL;
d = TAILQ_NEXT(d, dv_list)) {
if (d->dv_parent != parent)
continue;
switch (act) {
case DVACT_QUIESCE:
case DVACT_SUSPEND:
case DVACT_RESUME:
case DVACT_WAKEUP:
case DVACT_POWERDOWN:
rv = config_suspend(d, act);
break;
case DVACT_DEACTIVATE:
rv = config_deactivate(d);
break;
}
if (rv == 0)
continue;
/*
* Found a device that refuses the action.
* If we were being asked to suspend, we can
* try to resume all previous devices.
*/
#ifdef DIAGNOSTIC
printf("config_activate_children: device %s failed %d\n",
d->dv_xname, act);
#endif
if (act == DVACT_RESUME)
printf("failing resume cannot be handled\n");
if (act == DVACT_POWERDOWN)
return (rv);
if (act != DVACT_SUSPEND)
return (rv);
d = TAILQ_PREV(d, devicelist, dv_list);
for (; d != NULL && d != parent;
d = TAILQ_PREV(d, devicelist, dv_list)) {
if (d->dv_parent != parent)
continue;
printf("resume %s\n", d->dv_xname);
config_suspend(d, DVACT_RESUME);
}
return (rv);
}
return (rv);
}
/*
* Lookup a device in the cfdriver device array. Does not return a
* device if it is not active.
*
* Increments ref count on the device by one, reflecting the
* new reference created on the stack.
*
* Context: process only
*/
struct device *
device_lookup(struct cfdriver *cd, int unit)
{
struct device *dv = NULL;
if (unit >= 0 && unit < cd->cd_ndevs)
dv = (struct device *)(cd->cd_devs[unit]);
if (!dv)
return (NULL);
if (!(dv->dv_flags & DVF_ACTIVE))
dv = NULL;
if (dv != NULL)
device_ref(dv);
return (dv);
}
struct device *
device_mainbus(void)
{
extern struct cfdriver mainbus_cd;
if (mainbus_cd.cd_ndevs < 1)
return (NULL);
return (mainbus_cd.cd_devs[0]);
}
struct device *
device_mpath(void)
{
#if NMPATH > 0
extern struct cfdriver mpath_cd;
if (mpath_cd.cd_ndevs < 1)
return (NULL);
return (mpath_cd.cd_devs[0]);
#else
return (NULL);
#endif
}
/*
* Increments the ref count on the device structure. The device
* structure is freed when the ref count hits 0.
*
* Context: process or interrupt
*/
void
device_ref(struct device *dv)
{
atomic_inc_int(&dv->dv_ref);
}
/*
* Decrement the ref count on the device structure.
*
* free's the structure when the ref count hits zero.
*
* Context: process or interrupt
*/
void
device_unref(struct device *dv)
{
const struct cfattach *ca;
if (atomic_dec_int_nv(&dv->dv_ref) == 0) {
ca = dv->dv_cfdata->cf_attach;
free(dv, M_DEVBUF, ca->ca_devsize);
}
}
4
4
4
157
141
111
112
2
102
103
14
14
12
3
14
14
2
8
8
123
124
246
40
206
199
203
238
32
2
2
11
10
75
6
59
39
75
62
7
75
6
73
73
72
73
72
2
72
31
74
39
72
2
95
35
93
1
94
90
58
135
12
79
116
79
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/* $OpenBSD: tty_subr.c,v 1.36 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: tty_subr.c,v 1.13 1996/02/09 19:00:43 christos Exp $ */
/*
* Copyright (c) 1993, 1994 Theo de Raadt
* All rights reserved.
*
* Per Lindqvist <pgd@compuram.bbt.se> supplied an almost fully working
* set of true clist functions that this is very loosely based on.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/malloc.h>
/*
* If TTY_QUOTE functionality isn't required by a line discipline,
* it can free c_cq and set it to NULL. This speeds things up,
* and also does not use any extra memory. This is useful for (say)
* a SLIP line discipline that wants a 32K ring buffer for data
* but doesn't need quoting.
*/
#define QMEM(n) ((((n)-1)/NBBY)+1)
void clrbits(u_char *, int, int);
/*
* Initialize a particular clist. Ok, they are really ring buffers,
* of the specified length, with/without quoting support.
*/
void
clalloc(struct clist *clp, int size, int quot)
{
clp->c_cs = malloc(size, M_TTYS, M_WAITOK|M_ZERO);
if (quot)
clp->c_cq = malloc(QMEM(size), M_TTYS, M_WAITOK|M_ZERO);
else
clp->c_cq = NULL;
clp->c_cf = clp->c_cl = NULL;
clp->c_ce = clp->c_cs + size;
clp->c_cn = size;
clp->c_cc = 0;
}
void
clfree(struct clist *clp)
{
if (clp->c_cs) {
explicit_bzero(clp->c_cs, clp->c_cn);
free(clp->c_cs, M_TTYS, clp->c_cn);
}
if (clp->c_cq) {
explicit_bzero(clp->c_cq, QMEM(clp->c_cn));
free(clp->c_cq, M_TTYS, QMEM(clp->c_cn));
}
clp->c_cs = clp->c_cq = NULL;
}
/*
* Get a character from a clist.
*/
int
getc(struct clist *clp)
{
int c = -1;
int s;
s = spltty();
if (clp->c_cc == 0)
goto out;
c = *clp->c_cf & 0xff;
*clp->c_cf = 0;
if (clp->c_cq) {
if (isset(clp->c_cq, clp->c_cf - clp->c_cs))
c |= TTY_QUOTE;
clrbit(clp->c_cq, clp->c_cf - clp->c_cs);
}
if (++clp->c_cf == clp->c_ce)
clp->c_cf = clp->c_cs;
if (--clp->c_cc == 0)
clp->c_cf = clp->c_cl = NULL;
out:
splx(s);
return c;
}
/*
* Copy clist to buffer.
* Return number of bytes moved.
*/
int
q_to_b(struct clist *clp, u_char *cp, int count)
{
int cc;
u_char *p = cp;
int s;
s = spltty();
/* optimize this while loop */
while (count > 0 && clp->c_cc > 0) {
cc = clp->c_cl - clp->c_cf;
if (clp->c_cf >= clp->c_cl)
cc = clp->c_ce - clp->c_cf;
if (cc > count)
cc = count;
memcpy(p, clp->c_cf, cc);
memset(clp->c_cf, 0, cc);
if (clp->c_cq)
clrbits(clp->c_cq, clp->c_cf - clp->c_cs, cc);
count -= cc;
p += cc;
clp->c_cc -= cc;
clp->c_cf += cc;
if (clp->c_cf == clp->c_ce)
clp->c_cf = clp->c_cs;
}
if (clp->c_cc == 0)
clp->c_cf = clp->c_cl = NULL;
splx(s);
return p - cp;
}
/*
* Return count of contiguous characters in clist.
* Stop counting if flag&character is non-null.
*/
int
ndqb(struct clist *clp, int flag)
{
int count = 0;
int i;
int cc;
int s;
s = spltty();
if ((cc = clp->c_cc) == 0)
goto out;
if (flag == 0) {
count = clp->c_cl - clp->c_cf;
if (count <= 0)
count = clp->c_ce - clp->c_cf;
goto out;
}
i = clp->c_cf - clp->c_cs;
if (flag & TTY_QUOTE) {
while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) ||
isset(clp->c_cq, i))) {
count++;
if (i == clp->c_cn)
break;
}
} else {
while (cc-- > 0 && !(clp->c_cs[i++] & flag)) {
count++;
if (i == clp->c_cn)
break;
}
}
out:
splx(s);
return count;
}
/*
* Flush count bytes from clist.
*/
void
ndflush(struct clist *clp, int count)
{
int cc;
int s;
s = spltty();
if (count == clp->c_cc) {
clp->c_cc = 0;
clp->c_cf = clp->c_cl = NULL;
goto out;
}
/* optimize this while loop */
while (count > 0 && clp->c_cc > 0) {
cc = clp->c_cl - clp->c_cf;
if (clp->c_cf >= clp->c_cl)
cc = clp->c_ce - clp->c_cf;
if (cc > count)
cc = count;
count -= cc;
clp->c_cc -= cc;
clp->c_cf += cc;
if (clp->c_cf == clp->c_ce)
clp->c_cf = clp->c_cs;
}
if (clp->c_cc == 0)
clp->c_cf = clp->c_cl = NULL;
out:
splx(s);
}
/*
* Put a character into the output queue.
*/
int
putc(int c, struct clist *clp)
{
int i;
int s;
s = spltty();
if (clp->c_cc == clp->c_cn) {
splx(s);
return -1;
}
if (clp->c_cc == 0) {
if (!clp->c_cs)
panic("%s: tty has no clist", __func__);
clp->c_cf = clp->c_cl = clp->c_cs;
}
*clp->c_cl = c & 0xff;
i = clp->c_cl - clp->c_cs;
if (clp->c_cq) {
if (c & TTY_QUOTE)
setbit(clp->c_cq, i);
else
clrbit(clp->c_cq, i);
}
clp->c_cc++;
clp->c_cl++;
if (clp->c_cl == clp->c_ce)
clp->c_cl = clp->c_cs;
splx(s);
return 0;
}
/*
* optimized version of
*
* for (i = 0; i < len; i++)
* clrbit(cp, off + i);
*/
void
clrbits(u_char *cp, int off, int len)
{
int sby, sbi, eby, ebi;
int i;
u_char mask;
if (len==1) {
clrbit(cp, off);
return;
}
sby = off / NBBY;
sbi = off % NBBY;
eby = (off+len) / NBBY;
ebi = (off+len) % NBBY;
if (sby == eby) {
mask = ((1 << (ebi - sbi)) - 1) << sbi;
cp[sby] &= ~mask;
} else {
mask = (1<<sbi) - 1;
cp[sby++] &= mask;
for (i = sby; i < eby; i++)
cp[i] = 0x00;
mask = (1<<ebi) - 1;
if (mask) /* if no mask, eby may be 1 too far */
cp[eby] &= ~mask;
}
}
/*
* Copy buffer to clist.
* Return number of bytes not transferred.
*/
int
b_to_q(u_char *cp, int count, struct clist *clp)
{
int cc;
u_char *p = cp;
int s;
if (count <= 0)
return 0;
s = spltty();
if (clp->c_cc == clp->c_cn)
goto out;
if (clp->c_cc == 0) {
if (!clp->c_cs)
panic("%s: tty has no clist", __func__);
clp->c_cf = clp->c_cl = clp->c_cs;
}
/* optimize this while loop */
while (count > 0 && clp->c_cc < clp->c_cn) {
cc = clp->c_ce - clp->c_cl;
if (clp->c_cf > clp->c_cl)
cc = clp->c_cf - clp->c_cl;
if (cc > count)
cc = count;
memcpy(clp->c_cl, p, cc);
if (clp->c_cq)
clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc);
p += cc;
count -= cc;
clp->c_cc += cc;
clp->c_cl += cc;
if (clp->c_cl == clp->c_ce)
clp->c_cl = clp->c_cs;
}
out:
splx(s);
return count;
}
/*
* Given a non-NULL pointer into the clist return the pointer
* to the next character in the list or return NULL if no more chars.
*
* Callers must not allow getc's to happen between firstc's and nextc's
* so that the pointer becomes invalid. Note that interrupts are NOT
* masked.
*/
u_char *
nextc(struct clist *clp, u_char *cp, int *c, int *ccp)
{
if (clp->c_cf == cp) {
/*
* First time initialization.
*/
*ccp = clp->c_cc;
}
if (*ccp == 0 || cp == NULL)
return NULL;
if (--(*ccp) == 0)
return NULL;
if (++cp == clp->c_ce)
cp = clp->c_cs;
*c = *cp & 0xff;
if (clp->c_cq) {
if (isset(clp->c_cq, cp - clp->c_cs))
*c |= TTY_QUOTE;
}
return cp;
}
/*
* Given a non-NULL pointer into the clist return the pointer
* to the first character in the list or return NULL if no more chars.
*
* Callers must not allow getc's to happen between firstc's and nextc's
* so that the pointer becomes invalid. Note that interrupts are NOT
* masked.
*
* *c is set to the NEXT character
*/
u_char *
firstc(struct clist *clp, int *c, int *ccp)
{
u_char *cp;
*ccp = clp->c_cc;
if (*ccp == 0)
return NULL;
cp = clp->c_cf;
*c = *cp & 0xff;
if (clp->c_cq) {
if (isset(clp->c_cq, cp - clp->c_cs))
*c |= TTY_QUOTE;
}
return clp->c_cf;
}
/*
* Remove the last character in the clist and return it.
*/
int
unputc(struct clist *clp)
{
unsigned int c = -1;
int s;
s = spltty();
if (clp->c_cc == 0)
goto out;
if (clp->c_cl == clp->c_cs)
clp->c_cl = clp->c_ce - 1;
else
--clp->c_cl;
clp->c_cc--;
c = *clp->c_cl & 0xff;
*clp->c_cl = 0;
if (clp->c_cq) {
if (isset(clp->c_cq, clp->c_cl - clp->c_cs))
c |= TTY_QUOTE;
clrbit(clp->c_cq, clp->c_cl - clp->c_cs);
}
if (clp->c_cc == 0)
clp->c_cf = clp->c_cl = NULL;
out:
splx(s);
return c;
}
/*
* Put the chars in the from queue on the end of the to queue.
*/
void
catq(struct clist *from, struct clist *to)
{
int c;
int s;
s = spltty();
if (from->c_cc == 0) { /* nothing to move */
splx(s);
return;
}
/*
* if `to' queue is empty and the queues are the same max size,
* it is more efficient to just swap the clist structures.
*/
if (to->c_cc == 0 && from->c_cn == to->c_cn) {
struct clist tmp;
tmp = *from;
*from = *to;
*to = tmp;
splx(s);
return;
}
splx(s);
while ((c = getc(from)) != -1)
putc(c, to);
}
10
2
1
2
5
315
314
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
/* $OpenBSD: ip_carp.c,v 1.354 2021/03/10 10:21:48 jsg Exp $ */
/*
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
* Copyright (c) 2003 Ryan McBride. All rights reserved.
* Copyright (c) 2006-2008 Marco Pfatschbacher. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* TODO:
* - iface reconfigure
* - support for hardware checksum calculations;
*
*/
#include "ether.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/timeout.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/refcnt.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <crypto/sha1.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip_ipsp.h>
#include <net/if_dl.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/in6_ifattach.h>
#endif
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include "vlan.h"
#if NVLAN > 0
#include <net/if_vlan_var.h>
#endif
#include <netinet/ip_carp.h>
struct carp_mc_entry {
LIST_ENTRY(carp_mc_entry) mc_entries;
union {
struct ether_multi *mcu_enm;
} mc_u;
struct sockaddr_storage mc_addr;
};
#define mc_enm mc_u.mcu_enm
enum { HMAC_ORIG=0, HMAC_NOV6LL=1, HMAC_MAX=2 };
struct carp_vhost_entry {
SRPL_ENTRY(carp_vhost_entry) vhost_entries;
struct refcnt vhost_refcnt;
struct carp_softc *parent_sc;
int vhe_leader;
int vhid;
int advskew;
enum { INIT = 0, BACKUP, MASTER } state;
struct timeout ad_tmo; /* advertisement timeout */
struct timeout md_tmo; /* master down timeout */
struct timeout md6_tmo; /* master down timeout */
u_int64_t vhe_replay_cookie;
/* authentication */
#define CARP_HMAC_PAD 64
unsigned char vhe_pad[CARP_HMAC_PAD];
SHA1_CTX vhe_sha1[HMAC_MAX];
u_int8_t vhe_enaddr[ETHER_ADDR_LEN];
};
void carp_vh_ref(void *, void *);
void carp_vh_unref(void *, void *);
struct srpl_rc carp_vh_rc =
SRPL_RC_INITIALIZER(carp_vh_ref, carp_vh_unref, NULL);
struct carp_softc {
struct arpcom sc_ac;
#define sc_if sc_ac.ac_if
#define sc_carpdevidx sc_ac.ac_if.if_carpdevidx
struct task sc_atask;
struct task sc_ltask;
struct task sc_dtask;
struct ip_moptions sc_imo;
#ifdef INET6
struct ip6_moptions sc_im6o;
#endif /* INET6 */
SRPL_ENTRY(carp_softc) sc_list;
struct refcnt sc_refcnt;
int sc_suppress;
int sc_bow_out;
int sc_demote_cnt;
int sc_sendad_errors;
#define CARP_SENDAD_MAX_ERRORS(sc) (3 * (sc)->sc_vhe_count)
int sc_sendad_success;
#define CARP_SENDAD_MIN_SUCCESS(sc) (3 * (sc)->sc_vhe_count)
char sc_curlladdr[ETHER_ADDR_LEN];
SRPL_HEAD(, carp_vhost_entry) carp_vhosts;
int sc_vhe_count;
u_int8_t sc_vhids[CARP_MAXNODES];
u_int8_t sc_advskews[CARP_MAXNODES];
u_int8_t sc_balancing;
int sc_naddrs;
int sc_naddrs6;
int sc_advbase; /* seconds */
/* authentication */
unsigned char sc_key[CARP_KEY_LEN];
u_int32_t sc_hashkey[2];
u_int32_t sc_lsmask; /* load sharing mask */
int sc_lscount; /* # load sharing interfaces (max 32) */
int sc_delayed_arp; /* delayed ARP request countdown */
int sc_realmac; /* using real mac */
struct in_addr sc_peer;
LIST_HEAD(__carp_mchead, carp_mc_entry) carp_mc_listhead;
struct carp_vhost_entry *cur_vhe; /* current active vhe */
};
void carp_sc_ref(void *, void *);
void carp_sc_unref(void *, void *);
struct srpl_rc carp_sc_rc =
SRPL_RC_INITIALIZER(carp_sc_ref, carp_sc_unref, NULL);
int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, LOG_CRIT }; /* XXX for now */
struct cpumem *carpcounters;
int carp_send_all_recur = 0;
#define CARP_LOG(l, sc, s) \
do { \
if (carp_opts[CARPCTL_LOG] >= l) { \
if (sc) \
log(l, "%s: ", \
(sc)->sc_if.if_xname); \
else \
log(l, "carp: "); \
addlog s; \
addlog("\n"); \
} \
} while (0)
void carp_hmac_prepare(struct carp_softc *);
void carp_hmac_prepare_ctx(struct carp_vhost_entry *, u_int8_t);
void carp_hmac_generate(struct carp_vhost_entry *, u_int32_t *,
unsigned char *, u_int8_t);
int carp_hmac_verify(struct carp_vhost_entry *, u_int32_t *,
unsigned char *);
void carp_proto_input_c(struct ifnet *, struct mbuf *,
struct carp_header *, int, sa_family_t);
int carp_proto_input_if(struct ifnet *, struct mbuf **, int *, int);
#ifdef INET6
int carp6_proto_input_if(struct ifnet *, struct mbuf **, int *, int);
#endif
void carpattach(int);
void carpdetach(void *);
void carp_prepare_ad(struct mbuf *, struct carp_vhost_entry *,
struct carp_header *);
void carp_send_ad_all(void);
void carp_vhe_send_ad_all(struct carp_softc *);
void carp_timer_ad(void *);
void carp_send_ad(struct carp_vhost_entry *);
void carp_send_arp(struct carp_softc *);
void carp_timer_down(void *);
void carp_master_down(struct carp_vhost_entry *);
int carp_ioctl(struct ifnet *, u_long, caddr_t);
int carp_vhids_ioctl(struct carp_softc *, struct carpreq *);
int carp_check_dup_vhids(struct carp_softc *, struct srpl *,
struct carpreq *);
void carp_ifgroup_ioctl(struct ifnet *, u_long, caddr_t);
void carp_ifgattr_ioctl(struct ifnet *, u_long, caddr_t);
void carp_start(struct ifnet *);
int carp_enqueue(struct ifnet *, struct mbuf *);
void carp_transmit(struct carp_softc *, struct ifnet *, struct mbuf *);
void carp_setrun_all(struct carp_softc *, sa_family_t);
void carp_setrun(struct carp_vhost_entry *, sa_family_t);
void carp_set_state_all(struct carp_softc *, int);
void carp_set_state(struct carp_vhost_entry *, int);
void carp_multicast_cleanup(struct carp_softc *);
int carp_set_ifp(struct carp_softc *, struct ifnet *);
void carp_set_enaddr(struct carp_softc *);
void carp_set_vhe_enaddr(struct carp_vhost_entry *);
void carp_addr_updated(void *);
int carp_set_addr(struct carp_softc *, struct sockaddr_in *);
int carp_join_multicast(struct carp_softc *);
#ifdef INET6
void carp_send_na(struct carp_softc *);
int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *);
int carp_join_multicast6(struct carp_softc *);
#endif
int carp_clone_create(struct if_clone *, int);
int carp_clone_destroy(struct ifnet *);
int carp_ether_addmulti(struct carp_softc *, struct ifreq *);
int carp_ether_delmulti(struct carp_softc *, struct ifreq *);
void carp_ether_purgemulti(struct carp_softc *);
int carp_group_demote_count(struct carp_softc *);
void carp_update_lsmask(struct carp_softc *);
int carp_new_vhost(struct carp_softc *, int, int);
void carp_destroy_vhosts(struct carp_softc *);
void carp_del_all_timeouts(struct carp_softc *);
int carp_vhe_match(struct carp_softc *, uint64_t);
struct if_clone carp_cloner =
IF_CLONE_INITIALIZER("carp", carp_clone_create, carp_clone_destroy);
#define carp_cksum(_m, _l) ((u_int16_t)in_cksum((_m), (_l)))
#define CARP_IFQ_PRIO 6
void
carp_hmac_prepare(struct carp_softc *sc)
{
struct carp_vhost_entry *vhe;
u_int8_t i;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
for (i = 0; i < HMAC_MAX; i++) {
carp_hmac_prepare_ctx(vhe, i);
}
}
}
void
carp_hmac_prepare_ctx(struct carp_vhost_entry *vhe, u_int8_t ctx)
{
struct carp_softc *sc = vhe->parent_sc;
u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT;
u_int8_t vhid = vhe->vhid & 0xff;
SHA1_CTX sha1ctx;
u_int32_t kmd[5];
struct ifaddr *ifa;
int i, found;
struct in_addr last, cur, in;
#ifdef INET6
struct in6_addr last6, cur6, in6;
#endif /* INET6 */
/* compute ipad from key */
memset(vhe->vhe_pad, 0, sizeof(vhe->vhe_pad));
bcopy(sc->sc_key, vhe->vhe_pad, sizeof(sc->sc_key));
for (i = 0; i < sizeof(vhe->vhe_pad); i++)
vhe->vhe_pad[i] ^= 0x36;
/* precompute first part of inner hash */
SHA1Init(&vhe->vhe_sha1[ctx]);
SHA1Update(&vhe->vhe_sha1[ctx], vhe->vhe_pad, sizeof(vhe->vhe_pad));
SHA1Update(&vhe->vhe_sha1[ctx], (void *)&version, sizeof(version));
SHA1Update(&vhe->vhe_sha1[ctx], (void *)&type, sizeof(type));
/* generate a key for the arpbalance hash, before the vhid is hashed */
if (vhe->vhe_leader) {
bcopy(&vhe->vhe_sha1[ctx], &sha1ctx, sizeof(sha1ctx));
SHA1Final((unsigned char *)kmd, &sha1ctx);
sc->sc_hashkey[0] = kmd[0] ^ kmd[1];
sc->sc_hashkey[1] = kmd[2] ^ kmd[3];
}
/* the rest of the precomputation */
if (!sc->sc_realmac && vhe->vhe_leader &&
memcmp(sc->sc_ac.ac_enaddr, vhe->vhe_enaddr, ETHER_ADDR_LEN) != 0)
SHA1Update(&vhe->vhe_sha1[ctx], sc->sc_ac.ac_enaddr,
ETHER_ADDR_LEN);
SHA1Update(&vhe->vhe_sha1[ctx], (void *)&vhid, sizeof(vhid));
/* Hash the addresses from smallest to largest, not interface order */
cur.s_addr = 0;
do {
found = 0;
last = cur;
cur.s_addr = 0xffffffff;
TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
if (ntohl(in.s_addr) > ntohl(last.s_addr) &&
ntohl(in.s_addr) < ntohl(cur.s_addr)) {
cur.s_addr = in.s_addr;
found++;
}
}
if (found)
SHA1Update(&vhe->vhe_sha1[ctx],
(void *)&cur, sizeof(cur));
} while (found);
#ifdef INET6
memset(&cur6, 0x00, sizeof(cur6));
do {
found = 0;
last6 = cur6;
memset(&cur6, 0xff, sizeof(cur6));
TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
if (IN6_IS_SCOPE_EMBED(&in6)) {
if (ctx == HMAC_NOV6LL)
continue;
in6.s6_addr16[1] = 0;
}
if (memcmp(&in6, &last6, sizeof(in6)) > 0 &&
memcmp(&in6, &cur6, sizeof(in6)) < 0) {
cur6 = in6;
found++;
}
}
if (found)
SHA1Update(&vhe->vhe_sha1[ctx],
(void *)&cur6, sizeof(cur6));
} while (found);
#endif /* INET6 */
/* convert ipad to opad */
for (i = 0; i < sizeof(vhe->vhe_pad); i++)
vhe->vhe_pad[i] ^= 0x36 ^ 0x5c;
}
void
carp_hmac_generate(struct carp_vhost_entry *vhe, u_int32_t counter[2],
unsigned char md[20], u_int8_t ctx)
{
SHA1_CTX sha1ctx;
/* fetch first half of inner hash */
bcopy(&vhe->vhe_sha1[ctx], &sha1ctx, sizeof(sha1ctx));
SHA1Update(&sha1ctx, (void *)counter, sizeof(vhe->vhe_replay_cookie));
SHA1Final(md, &sha1ctx);
/* outer hash */
SHA1Init(&sha1ctx);
SHA1Update(&sha1ctx, vhe->vhe_pad, sizeof(vhe->vhe_pad));
SHA1Update(&sha1ctx, md, 20);
SHA1Final(md, &sha1ctx);
}
int
carp_hmac_verify(struct carp_vhost_entry *vhe, u_int32_t counter[2],
unsigned char md[20])
{
unsigned char md2[20];
u_int8_t i;
for (i = 0; i < HMAC_MAX; i++) {
carp_hmac_generate(vhe, counter, md2, i);
if (!timingsafe_bcmp(md, md2, sizeof(md2)))
return (0);
}
return (1);
}
int
carp_proto_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct ifnet *ifp;
ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
m_freemp(mp);
return IPPROTO_DONE;
}
proto = carp_proto_input_if(ifp, mp, offp, proto);
if_put(ifp);
return proto;
}
/*
* process input packet.
* we have rearranged checks order compared to the rfc,
* but it seems more efficient this way or not possible otherwise.
*/
int
carp_proto_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m = *mp;
struct ip *ip = mtod(m, struct ip *);
struct carp_softc *sc = NULL;
struct carp_header *ch;
int iplen, len, ismulti;
carpstat_inc(carps_ipackets);
if (!carp_opts[CARPCTL_ALLOW]) {
m_freem(m);
return IPPROTO_DONE;
}
ismulti = IN_MULTICAST(ip->ip_dst.s_addr);
/* check if received on a valid carp interface */
switch (ifp->if_type) {
case IFT_CARP:
break;
case IFT_ETHER:
if (ismulti || !SRPL_EMPTY_LOCKED(&ifp->if_carp))
break;
/* FALLTHROUGH */
default:
carpstat_inc(carps_badif);
CARP_LOG(LOG_INFO, sc,
("packet received on non-carp interface: %s",
ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
/* verify that the IP TTL is 255. */
if (ip->ip_ttl != CARP_DFLTTL) {
carpstat_inc(carps_badttl);
CARP_LOG(LOG_NOTICE, sc, ("received ttl %d != %d on %s",
ip->ip_ttl, CARP_DFLTTL, ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
/*
* verify that the received packet length is
* equal to the CARP header
*/
iplen = ip->ip_hl << 2;
len = iplen + sizeof(*ch);
if (len > m->m_pkthdr.len) {
carpstat_inc(carps_badlen);
CARP_LOG(LOG_INFO, sc, ("packet too short %d on %s",
m->m_pkthdr.len, ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
if ((m = *mp = m_pullup(m, len)) == NULL) {
carpstat_inc(carps_hdrops);
return IPPROTO_DONE;
}
ip = mtod(m, struct ip *);
ch = (struct carp_header *)(mtod(m, caddr_t) + iplen);
/* verify the CARP checksum */
m->m_data += iplen;
if (carp_cksum(m, len - iplen)) {
carpstat_inc(carps_badsum);
CARP_LOG(LOG_INFO, sc, ("checksum failed on %s",
ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
m->m_data -= iplen;
KERNEL_LOCK();
carp_proto_input_c(ifp, m, ch, ismulti, AF_INET);
KERNEL_UNLOCK();
return IPPROTO_DONE;
}
#ifdef INET6
int
carp6_proto_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct ifnet *ifp;
ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
m_freemp(mp);
return IPPROTO_DONE;
}
proto = carp6_proto_input_if(ifp, mp, offp, proto);
if_put(ifp);
return proto;
}
int
carp6_proto_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m = *mp;
struct carp_softc *sc = NULL;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct carp_header *ch;
u_int len;
carpstat_inc(carps_ipackets6);
if (!carp_opts[CARPCTL_ALLOW]) {
m_freem(m);
return IPPROTO_DONE;
}
/* check if received on a valid carp interface */
if (ifp->if_type != IFT_CARP) {
carpstat_inc(carps_badif);
CARP_LOG(LOG_INFO, sc, ("packet received on non-carp interface: %s",
ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
/* verify that the IP TTL is 255 */
if (ip6->ip6_hlim != CARP_DFLTTL) {
carpstat_inc(carps_badttl);
CARP_LOG(LOG_NOTICE, sc, ("received ttl %d != %d on %s",
ip6->ip6_hlim, CARP_DFLTTL, ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
/* verify that we have a complete carp packet */
len = m->m_len;
if ((m = *mp = m_pullup(m, *offp + sizeof(*ch))) == NULL) {
carpstat_inc(carps_badlen);
CARP_LOG(LOG_INFO, sc, ("packet size %u too small", len));
return IPPROTO_DONE;
}
ch = (struct carp_header *)(mtod(m, caddr_t) + *offp);
/* verify the CARP checksum */
m->m_data += *offp;
if (carp_cksum(m, sizeof(*ch))) {
carpstat_inc(carps_badsum);
CARP_LOG(LOG_INFO, sc, ("checksum failed, on %s",
ifp->if_xname));
m_freem(m);
return IPPROTO_DONE;
}
m->m_data -= *offp;
KERNEL_LOCK();
carp_proto_input_c(ifp, m, ch, 1, AF_INET6);
KERNEL_UNLOCK();
return IPPROTO_DONE;
}
#endif /* INET6 */
void
carp_proto_input_c(struct ifnet *ifp, struct mbuf *m, struct carp_header *ch,
int ismulti, sa_family_t af)
{
struct carp_softc *sc;
struct ifnet *ifp0;
struct carp_vhost_entry *vhe;
struct timeval sc_tv, ch_tv;
struct srpl *cif;
KERNEL_ASSERT_LOCKED(); /* touching if_carp + carp_vhosts */
ifp0 = if_get(ifp->if_carpdevidx);
if (ifp->if_type == IFT_CARP) {
/*
* If the parent of this carp(4) got destroyed while
* `m' was being processed, silently drop it.
*/
if (ifp0 == NULL)
goto rele;
cif = &ifp0->if_carp;
} else
cif = &ifp->if_carp;
SRPL_FOREACH_LOCKED(sc, cif, sc_list) {
if (af == AF_INET &&
ismulti != IN_MULTICAST(sc->sc_peer.s_addr))
continue;
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
if (vhe->vhid == ch->carp_vhid)
goto found;
}
}
found:
if (!sc || (sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
(IFF_UP|IFF_RUNNING)) {
carpstat_inc(carps_badvhid);
goto rele;
}
getmicrotime(&sc->sc_if.if_lastchange);
/* verify the CARP version. */
if (ch->carp_version != CARP_VERSION) {
carpstat_inc(carps_badver);
sc->sc_if.if_ierrors++;
CARP_LOG(LOG_NOTICE, sc, ("invalid version %d != %d",
ch->carp_version, CARP_VERSION));
goto rele;
}
/* verify the hash */
if (carp_hmac_verify(vhe, ch->carp_counter, ch->carp_md)) {
carpstat_inc(carps_badauth);
sc->sc_if.if_ierrors++;
CARP_LOG(LOG_INFO, sc, ("incorrect hash"));
goto rele;
}
if (!memcmp(&vhe->vhe_replay_cookie, ch->carp_counter,
sizeof(ch->carp_counter))) {
struct ifnet *ifp2;
ifp2 = if_get(sc->sc_carpdevidx);
/* Do not log duplicates from non simplex interfaces */
if (ifp2 && ifp2->if_flags & IFF_SIMPLEX) {
carpstat_inc(carps_badauth);
sc->sc_if.if_ierrors++;
CARP_LOG(LOG_WARNING, sc,
("replay or network loop detected"));
}
if_put(ifp2);
goto rele;
}
sc_tv.tv_sec = sc->sc_advbase;
sc_tv.tv_usec = vhe->advskew * 1000000 / 256;
ch_tv.tv_sec = ch->carp_advbase;
ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256;
switch (vhe->state) {
case INIT:
break;
case MASTER:
/*
* If we receive an advertisement from a master who's going to
* be more frequent than us, and whose demote count is not higher
* than ours, go into BACKUP state. If his demote count is lower,
* also go into BACKUP.
*/
if (((timercmp(&sc_tv, &ch_tv, >) ||
timercmp(&sc_tv, &ch_tv, ==)) &&
(ch->carp_demote <= carp_group_demote_count(sc))) ||
ch->carp_demote < carp_group_demote_count(sc)) {
timeout_del(&vhe->ad_tmo);
carp_set_state(vhe, BACKUP);
carp_setrun(vhe, 0);
}
break;
case BACKUP:
/*
* If we're pre-empting masters who advertise slower than us,
* and do not have a better demote count, treat them as down.
*
*/
if (carp_opts[CARPCTL_PREEMPT] &&
timercmp(&sc_tv, &ch_tv, <) &&
ch->carp_demote >= carp_group_demote_count(sc)) {
carp_master_down(vhe);
break;
}
/*
* Take over masters advertising with a higher demote count,
* regardless of CARPCTL_PREEMPT.
*/
if (ch->carp_demote > carp_group_demote_count(sc)) {
carp_master_down(vhe);
break;
}
/*
* If the master is going to advertise at such a low frequency
* that he's guaranteed to time out, we'd might as well just
* treat him as timed out now.
*/
sc_tv.tv_sec = sc->sc_advbase * 3;
if (sc->sc_advbase && timercmp(&sc_tv, &ch_tv, <)) {
carp_master_down(vhe);
break;
}
/*
* Otherwise, we reset the counter and wait for the next
* advertisement.
*/
carp_setrun(vhe, af);
break;
}
rele:
if_put(ifp0);
m_freem(m);
return;
}
int
carp_sysctl_carpstat(void *oldp, size_t *oldlenp, void *newp)
{
struct carpstats carpstat;
CTASSERT(sizeof(carpstat) == (carps_ncounters * sizeof(uint64_t)));
memset(&carpstat, 0, sizeof carpstat);
counters_read(carpcounters, (uint64_t *)&carpstat, carps_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp,
&carpstat, sizeof(carpstat)));
}
int
carp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case CARPCTL_STATS:
return (carp_sysctl_carpstat(oldp, oldlenp, newp));
default:
if (name[0] <= 0 || name[0] >= CARPCTL_MAXID)
return (ENOPROTOOPT);
NET_LOCK();
error = sysctl_int(oldp, oldlenp, newp, newlen,
&carp_opts[name[0]]);
NET_UNLOCK();
return (error);
}
}
/*
* Interface side of the CARP implementation.
*/
/* ARGSUSED */
void
carpattach(int n)
{
if_creategroup("carp"); /* keep around even if empty */
if_clone_attach(&carp_cloner);
carpcounters = counters_alloc(carps_ncounters);
}
int
carp_clone_create(struct if_clone *ifc, int unit)
{
struct carp_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
refcnt_init(&sc->sc_refcnt);
SRPL_INIT(&sc->carp_vhosts);
sc->sc_vhe_count = 0;
if (carp_new_vhost(sc, 0, 0)) {
free(sc, M_DEVBUF, sizeof(*sc));
return (ENOMEM);
}
task_set(&sc->sc_atask, carp_addr_updated, sc);
task_set(&sc->sc_ltask, carp_carpdev_state, sc);
task_set(&sc->sc_dtask, carpdetach, sc);
sc->sc_suppress = 0;
sc->sc_advbase = CARP_DFLTINTV;
sc->sc_naddrs = sc->sc_naddrs6 = 0;
#ifdef INET6
sc->sc_im6o.im6o_hlim = CARP_DFLTTL;
#endif /* INET6 */
sc->sc_imo.imo_membership = mallocarray(IP_MIN_MEMBERSHIPS,
sizeof(struct in_multi *), M_IPMOPTS, M_WAITOK|M_ZERO);
sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
LIST_INIT(&sc->carp_mc_listhead);
ifp = &sc->sc_if;
ifp->if_softc = sc;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
unit);
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = carp_ioctl;
ifp->if_start = carp_start;
ifp->if_enqueue = carp_enqueue;
ifp->if_xflags = IFXF_CLONED;
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
ifp->if_type = IFT_CARP;
ifp->if_sadl->sdl_type = IFT_CARP;
ifp->if_output = carp_output;
ifp->if_priority = IF_CARP_DEFAULT_PRIORITY;
ifp->if_link_state = LINK_STATE_INVALID;
/* Hook carp_addr_updated to cope with address and route changes. */
if_addrhook_add(&sc->sc_if, &sc->sc_atask);
return (0);
}
int
carp_new_vhost(struct carp_softc *sc, int vhid, int advskew)
{
struct carp_vhost_entry *vhe, *vhe0;
vhe = malloc(sizeof(*vhe), M_DEVBUF, M_NOWAIT | M_ZERO);
if (vhe == NULL)
return (ENOMEM);
refcnt_init(&vhe->vhost_refcnt);
carp_sc_ref(NULL, sc); /* give a sc ref to the vhe */
vhe->parent_sc = sc;
vhe->vhid = vhid;
vhe->advskew = advskew;
vhe->state = INIT;
timeout_set_proc(&vhe->ad_tmo, carp_timer_ad, vhe);
timeout_set_proc(&vhe->md_tmo, carp_timer_down, vhe);
timeout_set_proc(&vhe->md6_tmo, carp_timer_down, vhe);
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
/* mark the first vhe as leader */
if (SRPL_EMPTY_LOCKED(&sc->carp_vhosts)) {
vhe->vhe_leader = 1;
SRPL_INSERT_HEAD_LOCKED(&carp_vh_rc, &sc->carp_vhosts,
vhe, vhost_entries);
sc->sc_vhe_count = 1;
return (0);
}
SRPL_FOREACH_LOCKED(vhe0, &sc->carp_vhosts, vhost_entries) {
if (SRPL_NEXT_LOCKED(vhe0, vhost_entries) == NULL)
break;
}
SRPL_INSERT_AFTER_LOCKED(&carp_vh_rc, vhe0, vhe, vhost_entries);
sc->sc_vhe_count++;
return (0);
}
int
carp_clone_destroy(struct ifnet *ifp)
{
struct carp_softc *sc = ifp->if_softc;
if_addrhook_del(&sc->sc_if, &sc->sc_atask);
NET_LOCK();
carpdetach(sc);
NET_UNLOCK();
ether_ifdetach(ifp);
if_detach(ifp);
carp_destroy_vhosts(ifp->if_softc);
refcnt_finalize(&sc->sc_refcnt, "carpdtor");
free(sc->sc_imo.imo_membership, M_IPMOPTS,
sc->sc_imo.imo_max_memberships * sizeof(struct in_multi *));
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
void
carp_del_all_timeouts(struct carp_softc *sc)
{
struct carp_vhost_entry *vhe;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
timeout_del(&vhe->ad_tmo);
timeout_del(&vhe->md_tmo);
timeout_del(&vhe->md6_tmo);
}
}
void
carpdetach(void *arg)
{
struct carp_softc *sc = arg;
struct ifnet *ifp0;
struct srpl *cif;
carp_del_all_timeouts(sc);
if (sc->sc_demote_cnt)
carp_group_demote_adj(&sc->sc_if, -sc->sc_demote_cnt, "detach");
sc->sc_suppress = 0;
sc->sc_sendad_errors = 0;
carp_set_state_all(sc, INIT);
sc->sc_if.if_flags &= ~IFF_UP;
carp_setrun_all(sc, 0);
carp_multicast_cleanup(sc);
ifp0 = if_get(sc->sc_carpdevidx);
if (ifp0 == NULL)
return;
KERNEL_ASSERT_LOCKED(); /* touching if_carp */
cif = &ifp0->if_carp;
SRPL_REMOVE_LOCKED(&carp_sc_rc, cif, sc, carp_softc, sc_list);
sc->sc_carpdevidx = 0;
if_linkstatehook_del(ifp0, &sc->sc_ltask);
if_detachhook_del(ifp0, &sc->sc_dtask);
ifpromisc(ifp0, 0);
if_put(ifp0);
}
void
carp_destroy_vhosts(struct carp_softc *sc)
{
/* XXX bow out? */
struct carp_vhost_entry *vhe;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
while ((vhe = SRPL_FIRST_LOCKED(&sc->carp_vhosts)) != NULL) {
SRPL_REMOVE_LOCKED(&carp_vh_rc, &sc->carp_vhosts, vhe,
carp_vhost_entry, vhost_entries);
carp_vh_unref(NULL, vhe); /* drop last ref */
}
sc->sc_vhe_count = 0;
}
void
carp_prepare_ad(struct mbuf *m, struct carp_vhost_entry *vhe,
struct carp_header *ch)
{
if (!vhe->vhe_replay_cookie) {
arc4random_buf(&vhe->vhe_replay_cookie,
sizeof(vhe->vhe_replay_cookie));
}
bcopy(&vhe->vhe_replay_cookie, ch->carp_counter,
sizeof(ch->carp_counter));
/*
* For the time being, do not include the IPv6 linklayer addresses
* in the HMAC.
*/
carp_hmac_generate(vhe, ch->carp_counter, ch->carp_md, HMAC_NOV6LL);
}
void
carp_send_ad_all(void)
{
struct ifnet *ifp0;
struct srpl *cif;
struct carp_softc *vh;
KERNEL_ASSERT_LOCKED(); /* touching if_carp */
if (carp_send_all_recur > 0)
return;
++carp_send_all_recur;
TAILQ_FOREACH(ifp0, &ifnet, if_list) {
if (ifp0->if_type != IFT_ETHER)
continue;
cif = &ifp0->if_carp;
SRPL_FOREACH_LOCKED(vh, cif, sc_list) {
if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) ==
(IFF_UP|IFF_RUNNING)) {
carp_vhe_send_ad_all(vh);
}
}
}
--carp_send_all_recur;
}
void
carp_vhe_send_ad_all(struct carp_softc *sc)
{
struct carp_vhost_entry *vhe;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
if (vhe->state == MASTER)
carp_send_ad(vhe);
}
}
void
carp_timer_ad(void *v)
{
NET_LOCK();
carp_send_ad(v);
NET_UNLOCK();
}
void
carp_send_ad(struct carp_vhost_entry *vhe)
{
struct carp_header ch;
struct timeval tv;
struct carp_softc *sc = vhe->parent_sc;
struct carp_header *ch_ptr;
struct mbuf *m;
int error, len, advbase, advskew;
struct ifnet *ifp;
struct ifaddr *ifa;
struct sockaddr sa;
NET_ASSERT_LOCKED();
if ((ifp = if_get(sc->sc_carpdevidx)) == NULL) {
sc->sc_if.if_oerrors++;
return;
}
/* bow out if we've gone to backup (the carp interface is going down) */
if (sc->sc_bow_out) {
advbase = 255;
advskew = 255;
} else {
advbase = sc->sc_advbase;
advskew = vhe->advskew;
tv.tv_sec = advbase;
if (advbase == 0 && advskew == 0)
tv.tv_usec = 1 * 1000000 / 256;
else
tv.tv_usec = advskew * 1000000 / 256;
}
ch.carp_version = CARP_VERSION;
ch.carp_type = CARP_ADVERTISEMENT;
ch.carp_vhid = vhe->vhid;
ch.carp_demote = carp_group_demote_count(sc) & 0xff;
ch.carp_advbase = advbase;
ch.carp_advskew = advskew;
ch.carp_authlen = 7; /* XXX DEFINE */
ch.carp_cksum = 0;
sc->cur_vhe = vhe; /* we need the vhe later on the output path */
if (sc->sc_naddrs) {
struct ip *ip;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
sc->sc_if.if_oerrors++;
carpstat_inc(carps_onomem);
/* XXX maybe less ? */
goto retry_later;
}
len = sizeof(*ip) + sizeof(ch);
m->m_pkthdr.pf.prio = CARP_IFQ_PRIO;
m->m_pkthdr.ph_rtableid = sc->sc_if.if_rdomain;
m->m_pkthdr.len = len;
m->m_len = len;
m_align(m, len);
ip = mtod(m, struct ip *);
ip->ip_v = IPVERSION;
ip->ip_hl = sizeof(*ip) >> 2;
ip->ip_tos = IPTOS_LOWDELAY;
ip->ip_len = htons(len);
ip->ip_id = htons(ip_randomid());
ip->ip_off = htons(IP_DF);
ip->ip_ttl = CARP_DFLTTL;
ip->ip_p = IPPROTO_CARP;
ip->ip_sum = 0;
memset(&sa, 0, sizeof(sa));
sa.sa_family = AF_INET;
/* Prefer addresses on the parent interface as source for AD. */
ifa = ifaof_ifpforaddr(&sa, ifp);
if (ifa == NULL)
ifa = ifaof_ifpforaddr(&sa, &sc->sc_if);
KASSERT(ifa != NULL);
ip->ip_src.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
ip->ip_dst.s_addr = sc->sc_peer.s_addr;
if (IN_MULTICAST(ip->ip_dst.s_addr))
m->m_flags |= M_MCAST;
ch_ptr = (struct carp_header *)(ip + 1);
bcopy(&ch, ch_ptr, sizeof(ch));
carp_prepare_ad(m, vhe, ch_ptr);
m->m_data += sizeof(*ip);
ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip));
m->m_data -= sizeof(*ip);
getmicrotime(&sc->sc_if.if_lastchange);
carpstat_inc(carps_opackets);
error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo,
NULL, 0);
if (error &&
/* when unicast, the peer's down is not our fault */
!(!IN_MULTICAST(sc->sc_peer.s_addr) && error == EHOSTDOWN)){
if (error == ENOBUFS)
carpstat_inc(carps_onomem);
else
CARP_LOG(LOG_WARNING, sc,
("ip_output failed: %d", error));
sc->sc_if.if_oerrors++;
if (sc->sc_sendad_errors < INT_MAX)
sc->sc_sendad_errors++;
if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS(sc))
carp_group_demote_adj(&sc->sc_if, 1,
"> snderrors");
sc->sc_sendad_success = 0;
} else {
if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS(sc)) {
if (++sc->sc_sendad_success >=
CARP_SENDAD_MIN_SUCCESS(sc)) {
carp_group_demote_adj(&sc->sc_if, -1,
"< snderrors");
sc->sc_sendad_errors = 0;
}
} else
sc->sc_sendad_errors = 0;
}
if (vhe->vhe_leader) {
if (sc->sc_delayed_arp > 0)
sc->sc_delayed_arp--;
if (sc->sc_delayed_arp == 0) {
carp_send_arp(sc);
sc->sc_delayed_arp = -1;
}
}
}
#ifdef INET6
if (sc->sc_naddrs6) {
struct ip6_hdr *ip6;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
sc->sc_if.if_oerrors++;
carpstat_inc(carps_onomem);
/* XXX maybe less ? */
goto retry_later;
}
len = sizeof(*ip6) + sizeof(ch);
m->m_pkthdr.pf.prio = CARP_IFQ_PRIO;
m->m_pkthdr.ph_rtableid = sc->sc_if.if_rdomain;
m->m_pkthdr.len = len;
m->m_len = len;
m_align(m, len);
m->m_flags |= M_MCAST;
ip6 = mtod(m, struct ip6_hdr *);
memset(ip6, 0, sizeof(*ip6));
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_hlim = CARP_DFLTTL;
ip6->ip6_nxt = IPPROTO_CARP;
/* set the source address */
memset(&sa, 0, sizeof(sa));
sa.sa_family = AF_INET6;
/* Prefer addresses on the parent interface as source for AD. */
ifa = ifaof_ifpforaddr(&sa, ifp);
if (ifa == NULL)
ifa = ifaof_ifpforaddr(&sa, &sc->sc_if);
KASSERT(ifa != NULL);
bcopy(ifatoia6(ifa)->ia_addr.sin6_addr.s6_addr,
&ip6->ip6_src, sizeof(struct in6_addr));
/* set the multicast destination */
ip6->ip6_dst.s6_addr16[0] = htons(0xff02);
ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
ip6->ip6_dst.s6_addr8[15] = 0x12;
ch_ptr = (struct carp_header *)(ip6 + 1);
bcopy(&ch, ch_ptr, sizeof(ch));
carp_prepare_ad(m, vhe, ch_ptr);
m->m_data += sizeof(*ip6);
ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6));
m->m_data -= sizeof(*ip6);
getmicrotime(&sc->sc_if.if_lastchange);
carpstat_inc(carps_opackets6);
error = ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL);
if (error) {
if (error == ENOBUFS)
carpstat_inc(carps_onomem);
else
CARP_LOG(LOG_WARNING, sc,
("ip6_output failed: %d", error));
sc->sc_if.if_oerrors++;
if (sc->sc_sendad_errors < INT_MAX)
sc->sc_sendad_errors++;
if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS(sc))
carp_group_demote_adj(&sc->sc_if, 1,
"> snd6errors");
sc->sc_sendad_success = 0;
} else {
if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS(sc)) {
if (++sc->sc_sendad_success >=
CARP_SENDAD_MIN_SUCCESS(sc)) {
carp_group_demote_adj(&sc->sc_if, -1,
"< snd6errors");
sc->sc_sendad_errors = 0;
}
} else
sc->sc_sendad_errors = 0;
}
}
#endif /* INET6 */
retry_later:
sc->cur_vhe = NULL;
if (advbase != 255 || advskew != 255)
timeout_add_tv(&vhe->ad_tmo, &tv);
if_put(ifp);
}
/*
* Broadcast a gratuitous ARP request containing
* the virtual router MAC address for each IP address
* associated with the virtual router.
*/
void
carp_send_arp(struct carp_softc *sc)
{
struct ifaddr *ifa;
in_addr_t in;
TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
in = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
arprequest(&sc->sc_if, &in, &in, sc->sc_ac.ac_enaddr);
}
}
#ifdef INET6
void
carp_send_na(struct carp_softc *sc)
{
struct ifaddr *ifa;
struct in6_addr *in6;
static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
in6 = &ifatoia6(ifa)->ia_addr.sin6_addr;
nd6_na_output(&sc->sc_if, &mcast, in6,
ND_NA_FLAG_OVERRIDE |
(ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), 1, NULL);
}
}
#endif /* INET6 */
void
carp_update_lsmask(struct carp_softc *sc)
{
struct carp_vhost_entry *vhe;
int count;
if (sc->sc_balancing == CARP_BAL_NONE)
return;
sc->sc_lsmask = 0;
count = 0;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
if (vhe->state == MASTER && count < sizeof(sc->sc_lsmask) * 8)
sc->sc_lsmask |= 1 << count;
count++;
}
sc->sc_lscount = count;
CARP_LOG(LOG_DEBUG, sc, ("carp_update_lsmask: %x", sc->sc_lsmask));
}
int
carp_iamatch(struct ifnet *ifp)
{
struct carp_softc *sc = ifp->if_softc;
struct carp_vhost_entry *vhe;
struct srp_ref sr;
int match = 0;
vhe = SRPL_FIRST(&sr, &sc->carp_vhosts);
if (vhe->state == MASTER)
match = 1;
SRPL_LEAVE(&sr);
return (match);
}
int
carp_ourether(struct ifnet *ifp, uint8_t *ena)
{
struct srpl *cif = &ifp->if_carp;
struct carp_softc *sc;
struct srp_ref sr;
int match = 0;
uint64_t dst = ether_addr_to_e64((struct ether_addr *)ena);
KASSERT(ifp->if_type == IFT_ETHER);
SRPL_FOREACH(sc, &sr, cif, sc_list) {
if ((sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
(IFF_UP|IFF_RUNNING))
continue;
if (carp_vhe_match(sc, dst)) {
match = 1;
break;
}
}
SRPL_LEAVE(&sr);
return (match);
}
int
carp_vhe_match(struct carp_softc *sc, uint64_t dst)
{
struct carp_vhost_entry *vhe;
struct srp_ref sr;
int active = 0;
vhe = SRPL_FIRST(&sr, &sc->carp_vhosts);
active = (vhe->state == MASTER || sc->sc_balancing >= CARP_BAL_IP);
SRPL_LEAVE(&sr);
return (active && (dst ==
ether_addr_to_e64((struct ether_addr *)sc->sc_ac.ac_enaddr)));
}
struct mbuf *
carp_input(struct ifnet *ifp0, struct mbuf *m, uint64_t dst)
{
struct srpl *cif;
struct carp_softc *sc;
struct srp_ref sr;
cif = &ifp0->if_carp;
SRPL_FOREACH(sc, &sr, cif, sc_list) {
if ((sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
(IFF_UP|IFF_RUNNING))
continue;
if (carp_vhe_match(sc, dst)) {
/*
* These packets look like layer 2 multicast but they
* are unicast at layer 3. With help of the tag the
* mbuf's M_MCAST flag can be removed by carp_lsdrop()
* after we have passed layer 2.
*/
if (sc->sc_balancing == CARP_BAL_IP) {
struct m_tag *mtag;
mtag = m_tag_get(PACKET_TAG_CARP_BAL_IP, 0,
M_NOWAIT);
if (mtag == NULL) {
m_freem(m);
goto out;
}
m_tag_prepend(m, mtag);
}
break;
}
}
if (sc == NULL) {
SRPL_LEAVE(&sr);
if (!ETH64_IS_MULTICAST(dst))
return (m);
/*
* XXX Should really check the list of multicast addresses
* for each CARP interface _before_ copying.
*/
SRPL_FOREACH(sc, &sr, cif, sc_list) {
struct mbuf *m0;
if (!(sc->sc_if.if_flags & IFF_UP))
continue;
m0 = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
if (m0 == NULL)
continue;
if_vinput(&sc->sc_if, m0);
}
SRPL_LEAVE(&sr);
return (m);
}
if_vinput(&sc->sc_if, m);
out:
SRPL_LEAVE(&sr);
return (NULL);
}
int
carp_lsdrop(struct ifnet *ifp, struct mbuf *m, sa_family_t af, u_int32_t *src,
u_int32_t *dst, int drop)
{
struct carp_softc *sc;
u_int32_t fold;
struct m_tag *mtag;
if (ifp->if_type != IFT_CARP)
return 0;
sc = ifp->if_softc;
if (sc->sc_balancing == CARP_BAL_NONE)
return 0;
/*
* Remove M_MCAST flag from mbuf of balancing ip traffic, since the fact
* that it is layer 2 multicast does not implicate that it is also layer
* 3 multicast.
*/
if (m->m_flags & M_MCAST &&
(mtag = m_tag_find(m, PACKET_TAG_CARP_BAL_IP, NULL))) {
m_tag_delete(m, mtag);
m->m_flags &= ~M_MCAST;
}
/*
* Return without making a drop decision. This allows to clear the
* M_MCAST flag and do nothing else.
*/
if (!drop)
return 0;
/*
* Never drop carp advertisements.
* XXX Bad idea to pass all broadcast / multicast traffic?
*/
if (m->m_flags & (M_BCAST|M_MCAST))
return 0;
fold = src[0] ^ dst[0];
#ifdef INET6
if (af == AF_INET6) {
int i;
for (i = 1; i < 4; i++)
fold ^= src[i] ^ dst[i];
}
#endif
if (sc->sc_lscount == 0) /* just to be safe */
return 1;
return ((1 << (ntohl(fold) % sc->sc_lscount)) & sc->sc_lsmask) == 0;
}
void
carp_timer_down(void *v)
{
NET_LOCK();
carp_master_down(v);
NET_UNLOCK();
}
void
carp_master_down(struct carp_vhost_entry *vhe)
{
struct carp_softc *sc = vhe->parent_sc;
NET_ASSERT_LOCKED();
switch (vhe->state) {
case INIT:
printf("%s: master_down event in INIT state\n",
sc->sc_if.if_xname);
break;
case MASTER:
break;
case BACKUP:
carp_set_state(vhe, MASTER);
carp_send_ad(vhe);
if (sc->sc_balancing == CARP_BAL_NONE && vhe->vhe_leader) {
carp_send_arp(sc);
/* Schedule a delayed ARP to deal w/ some L3 switches */
sc->sc_delayed_arp = 2;
#ifdef INET6
carp_send_na(sc);
#endif /* INET6 */
}
carp_setrun(vhe, 0);
carpstat_inc(carps_preempt);
break;
}
}
void
carp_setrun_all(struct carp_softc *sc, sa_family_t af)
{
struct carp_vhost_entry *vhe;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhost */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
carp_setrun(vhe, af);
}
}
/*
* When in backup state, af indicates whether to reset the master down timer
* for v4 or v6. If it's set to zero, reset the ones which are already pending.
*/
void
carp_setrun(struct carp_vhost_entry *vhe, sa_family_t af)
{
struct ifnet *ifp;
struct timeval tv;
struct carp_softc *sc = vhe->parent_sc;
if ((ifp = if_get(sc->sc_carpdevidx)) == NULL) {
sc->sc_if.if_flags &= ~IFF_RUNNING;
carp_set_state_all(sc, INIT);
return;
}
if (memcmp(((struct arpcom *)ifp)->ac_enaddr,
sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN) == 0)
sc->sc_realmac = 1;
else
sc->sc_realmac = 0;
if_put(ifp);
if (sc->sc_if.if_flags & IFF_UP && vhe->vhid > 0 &&
(sc->sc_naddrs || sc->sc_naddrs6) && !sc->sc_suppress) {
sc->sc_if.if_flags |= IFF_RUNNING;
} else {
sc->sc_if.if_flags &= ~IFF_RUNNING;
return;
}
switch (vhe->state) {
case INIT:
carp_set_state(vhe, BACKUP);
carp_setrun(vhe, 0);
break;
case BACKUP:
timeout_del(&vhe->ad_tmo);
tv.tv_sec = 3 * sc->sc_advbase;
if (sc->sc_advbase == 0 && vhe->advskew == 0)
tv.tv_usec = 3 * 1000000 / 256;
else if (sc->sc_advbase == 0)
tv.tv_usec = 3 * vhe->advskew * 1000000 / 256;
else
tv.tv_usec = vhe->advskew * 1000000 / 256;
if (vhe->vhe_leader)
sc->sc_delayed_arp = -1;
switch (af) {
case AF_INET:
timeout_add_tv(&vhe->md_tmo, &tv);
break;
#ifdef INET6
case AF_INET6:
timeout_add_tv(&vhe->md6_tmo, &tv);
break;
#endif /* INET6 */
default:
if (sc->sc_naddrs)
timeout_add_tv(&vhe->md_tmo, &tv);
if (sc->sc_naddrs6)
timeout_add_tv(&vhe->md6_tmo, &tv);
break;
}
break;
case MASTER:
tv.tv_sec = sc->sc_advbase;
if (sc->sc_advbase == 0 && vhe->advskew == 0)
tv.tv_usec = 1 * 1000000 / 256;
else
tv.tv_usec = vhe->advskew * 1000000 / 256;
timeout_add_tv(&vhe->ad_tmo, &tv);
break;
}
}
void
carp_multicast_cleanup(struct carp_softc *sc)
{
struct ip_moptions *imo = &sc->sc_imo;
#ifdef INET6
struct ip6_moptions *im6o = &sc->sc_im6o;
#endif
u_int16_t n = imo->imo_num_memberships;
/* Clean up our own multicast memberships */
while (n-- > 0) {
if (imo->imo_membership[n] != NULL) {
in_delmulti(imo->imo_membership[n]);
imo->imo_membership[n] = NULL;
}
}
imo->imo_num_memberships = 0;
imo->imo_ifidx = 0;
#ifdef INET6
while (!LIST_EMPTY(&im6o->im6o_memberships)) {
struct in6_multi_mship *imm =
LIST_FIRST(&im6o->im6o_memberships);
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
}
im6o->im6o_ifidx = 0;
#endif
/* And any other multicast memberships */
carp_ether_purgemulti(sc);
}
int
carp_set_ifp(struct carp_softc *sc, struct ifnet *ifp0)
{
struct srpl *cif;
struct carp_softc *vr, *last = NULL, *after = NULL;
int myself = 0, error = 0;
KASSERT(ifp0->if_index != sc->sc_carpdevidx);
KERNEL_ASSERT_LOCKED(); /* touching if_carp */
if ((ifp0->if_flags & IFF_MULTICAST) == 0)
return (EADDRNOTAVAIL);
if (ifp0->if_type != IFT_ETHER)
return (EINVAL);
cif = &ifp0->if_carp;
if (carp_check_dup_vhids(sc, cif, NULL))
return (EINVAL);
if ((error = ifpromisc(ifp0, 1)))
return (error);
/* detach from old interface */
if (sc->sc_carpdevidx != 0)
carpdetach(sc);
/* attach carp interface to physical interface */
if_detachhook_add(ifp0, &sc->sc_dtask);
if_linkstatehook_add(ifp0, &sc->sc_ltask);
sc->sc_carpdevidx = ifp0->if_index;
sc->sc_if.if_capabilities = ifp0->if_capabilities &
IFCAP_CSUM_MASK;
SRPL_FOREACH_LOCKED(vr, cif, sc_list) {
struct carp_vhost_entry *vrhead, *schead;
last = vr;
if (vr == sc)
myself = 1;
vrhead = SRPL_FIRST_LOCKED(&vr->carp_vhosts);
schead = SRPL_FIRST_LOCKED(&sc->carp_vhosts);
if (vrhead->vhid < schead->vhid)
after = vr;
}
if (!myself) {
/* We're trying to keep things in order */
if (last == NULL) {
SRPL_INSERT_HEAD_LOCKED(&carp_sc_rc, cif,
sc, sc_list);
} else if (after == NULL) {
SRPL_INSERT_AFTER_LOCKED(&carp_sc_rc, last,
sc, sc_list);
} else {
SRPL_INSERT_AFTER_LOCKED(&carp_sc_rc, after,
sc, sc_list);
}
}
if (sc->sc_naddrs || sc->sc_naddrs6)
sc->sc_if.if_flags |= IFF_UP;
carp_set_enaddr(sc);
carp_carpdev_state(sc);
return (0);
}
void
carp_set_vhe_enaddr(struct carp_vhost_entry *vhe)
{
struct carp_softc *sc = vhe->parent_sc;
if (vhe->vhid != 0 && sc->sc_carpdevidx != 0) {
if (vhe->vhe_leader && sc->sc_balancing == CARP_BAL_IP)
vhe->vhe_enaddr[0] = 1;
else
vhe->vhe_enaddr[0] = 0;
vhe->vhe_enaddr[1] = 0;
vhe->vhe_enaddr[2] = 0x5e;
vhe->vhe_enaddr[3] = 0;
vhe->vhe_enaddr[4] = 1;
vhe->vhe_enaddr[5] = vhe->vhid;
} else
memset(vhe->vhe_enaddr, 0, ETHER_ADDR_LEN);
}
void
carp_set_enaddr(struct carp_softc *sc)
{
struct carp_vhost_entry *vhe;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries)
carp_set_vhe_enaddr(vhe);
vhe = SRPL_FIRST_LOCKED(&sc->carp_vhosts);
/*
* Use the carp lladdr if the running one isn't manually set.
* Only compare static parts of the lladdr.
*/
if ((memcmp(sc->sc_ac.ac_enaddr + 1, vhe->vhe_enaddr + 1,
ETHER_ADDR_LEN - 2) == 0) ||
(!sc->sc_ac.ac_enaddr[0] && !sc->sc_ac.ac_enaddr[1] &&
!sc->sc_ac.ac_enaddr[2] && !sc->sc_ac.ac_enaddr[3] &&
!sc->sc_ac.ac_enaddr[4] && !sc->sc_ac.ac_enaddr[5]))
bcopy(vhe->vhe_enaddr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
/* Make sure the enaddr has changed before further twiddling. */
if (memcmp(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN) != 0) {
bcopy(sc->sc_ac.ac_enaddr, LLADDR(sc->sc_if.if_sadl),
ETHER_ADDR_LEN);
bcopy(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN);
#ifdef INET6
/*
* (re)attach a link-local address which matches
* our new MAC address.
*/
if (sc->sc_naddrs6)
in6_ifattach_linklocal(&sc->sc_if, NULL);
#endif
carp_set_state_all(sc, INIT);
carp_setrun_all(sc, 0);
}
}
void
carp_addr_updated(void *v)
{
struct carp_softc *sc = (struct carp_softc *) v;
struct ifaddr *ifa;
int new_naddrs = 0, new_naddrs6 = 0;
TAILQ_FOREACH(ifa, &sc->sc_if.if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family == AF_INET)
new_naddrs++;
#ifdef INET6
else if (ifa->ifa_addr->sa_family == AF_INET6)
new_naddrs6++;
#endif /* INET6 */
}
/* We received address changes from if_addrhooks callback */
if (new_naddrs != sc->sc_naddrs || new_naddrs6 != sc->sc_naddrs6) {
sc->sc_naddrs = new_naddrs;
sc->sc_naddrs6 = new_naddrs6;
/* Re-establish multicast membership removed by in_control */
if (IN_MULTICAST(sc->sc_peer.s_addr)) {
if (!in_hasmulti(&sc->sc_peer, &sc->sc_if)) {
struct in_multi **imm =
sc->sc_imo.imo_membership;
u_int16_t maxmem =
sc->sc_imo.imo_max_memberships;
memset(&sc->sc_imo, 0, sizeof(sc->sc_imo));
sc->sc_imo.imo_membership = imm;
sc->sc_imo.imo_max_memberships = maxmem;
if (sc->sc_carpdevidx != 0 &&
sc->sc_naddrs > 0)
carp_join_multicast(sc);
}
}
if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) {
sc->sc_if.if_flags &= ~IFF_UP;
carp_set_state_all(sc, INIT);
} else
carp_hmac_prepare(sc);
}
carp_setrun_all(sc, 0);
}
int
carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin)
{
struct in_addr *in = &sin->sin_addr;
int error;
KASSERT(sc->sc_carpdevidx != 0);
/* XXX is this necessary? */
if (in->s_addr == INADDR_ANY) {
carp_setrun_all(sc, 0);
return (0);
}
if (sc->sc_naddrs == 0 && (error = carp_join_multicast(sc)) != 0)
return (error);
carp_set_state_all(sc, INIT);
return (0);
}
int
carp_join_multicast(struct carp_softc *sc)
{
struct ip_moptions *imo = &sc->sc_imo;
struct in_multi *imm;
struct in_addr addr;
if (!IN_MULTICAST(sc->sc_peer.s_addr))
return (0);
addr.s_addr = sc->sc_peer.s_addr;
if ((imm = in_addmulti(&addr, &sc->sc_if)) == NULL)
return (ENOBUFS);
imo->imo_membership[0] = imm;
imo->imo_num_memberships = 1;
imo->imo_ifidx = sc->sc_if.if_index;
imo->imo_ttl = CARP_DFLTTL;
imo->imo_loop = 0;
return (0);
}
#ifdef INET6
int
carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
{
int error;
KASSERT(sc->sc_carpdevidx != 0);
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
carp_setrun_all(sc, 0);
return (0);
}
if (sc->sc_naddrs6 == 0 && (error = carp_join_multicast6(sc)) != 0)
return (error);
carp_set_state_all(sc, INIT);
return (0);
}
int
carp_join_multicast6(struct carp_softc *sc)
{
struct in6_multi_mship *imm, *imm2;
struct ip6_moptions *im6o = &sc->sc_im6o;
struct sockaddr_in6 addr6;
int error;
/* Join IPv6 CARP multicast group */
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_len = sizeof(addr6);
addr6.sin6_addr.s6_addr16[0] = htons(0xff02);
addr6.sin6_addr.s6_addr16[1] = htons(sc->sc_if.if_index);
addr6.sin6_addr.s6_addr8[15] = 0x12;
if ((imm = in6_joingroup(&sc->sc_if,
&addr6.sin6_addr, &error)) == NULL) {
return (error);
}
/* join solicited multicast address */
memset(&addr6.sin6_addr, 0, sizeof(addr6.sin6_addr));
addr6.sin6_addr.s6_addr16[0] = htons(0xff02);
addr6.sin6_addr.s6_addr16[1] = htons(sc->sc_if.if_index);
addr6.sin6_addr.s6_addr32[1] = 0;
addr6.sin6_addr.s6_addr32[2] = htonl(1);
addr6.sin6_addr.s6_addr32[3] = 0;
addr6.sin6_addr.s6_addr8[12] = 0xff;
if ((imm2 = in6_joingroup(&sc->sc_if,
&addr6.sin6_addr, &error)) == NULL) {
in6_leavegroup(imm);
return (error);
}
/* apply v6 multicast membership */
im6o->im6o_ifidx = sc->sc_if.if_index;
if (imm)
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm,
i6mm_chain);
if (imm2)
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm2,
i6mm_chain);
return (0);
}
#endif /* INET6 */
int
carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
struct proc *p = curproc; /* XXX */
struct carp_softc *sc = ifp->if_softc;
struct carp_vhost_entry *vhe;
struct carpreq carpr;
struct ifaddr *ifa = (struct ifaddr *)addr;
struct ifreq *ifr = (struct ifreq *)addr;
struct ifnet *ifp0 = NULL;
int i, error = 0;
switch (cmd) {
case SIOCSIFADDR:
if (sc->sc_carpdevidx == 0)
return (EINVAL);
switch (ifa->ifa_addr->sa_family) {
case AF_INET:
sc->sc_if.if_flags |= IFF_UP;
error = carp_set_addr(sc, satosin(ifa->ifa_addr));
break;
#ifdef INET6
case AF_INET6:
sc->sc_if.if_flags |= IFF_UP;
error = carp_set_addr6(sc, satosin6(ifa->ifa_addr));
break;
#endif /* INET6 */
default:
error = EAFNOSUPPORT;
break;
}
break;
case SIOCSIFFLAGS:
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
vhe = SRPL_FIRST_LOCKED(&sc->carp_vhosts);
if (vhe->state != INIT && !(ifr->ifr_flags & IFF_UP)) {
carp_del_all_timeouts(sc);
/* we need the interface up to bow out */
sc->sc_if.if_flags |= IFF_UP;
sc->sc_bow_out = 1;
carp_vhe_send_ad_all(sc);
sc->sc_bow_out = 0;
sc->sc_if.if_flags &= ~IFF_UP;
carp_set_state_all(sc, INIT);
carp_setrun_all(sc, 0);
} else if (vhe->state == INIT && (ifr->ifr_flags & IFF_UP)) {
sc->sc_if.if_flags |= IFF_UP;
carp_setrun_all(sc, 0);
}
break;
case SIOCSVH:
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
vhe = SRPL_FIRST_LOCKED(&sc->carp_vhosts);
if ((error = suser(p)) != 0)
break;
if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr)))
break;
error = 1;
if (carpr.carpr_carpdev[0] != '\0' &&
(ifp0 = if_unit(carpr.carpr_carpdev)) == NULL)
return (EINVAL);
if (carpr.carpr_peer.s_addr == 0)
sc->sc_peer.s_addr = INADDR_CARP_GROUP;
else
sc->sc_peer.s_addr = carpr.carpr_peer.s_addr;
if (ifp0 != NULL && ifp0->if_index != sc->sc_carpdevidx) {
if ((error = carp_set_ifp(sc, ifp0))) {
if_put(ifp0);
return (error);
}
}
if_put(ifp0);
if (vhe->state != INIT && carpr.carpr_state != vhe->state) {
switch (carpr.carpr_state) {
case BACKUP:
timeout_del(&vhe->ad_tmo);
carp_set_state_all(sc, BACKUP);
carp_setrun_all(sc, 0);
break;
case MASTER:
KERNEL_ASSERT_LOCKED();
/* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts,
vhost_entries)
carp_master_down(vhe);
break;
default:
break;
}
}
if ((error = carp_vhids_ioctl(sc, &carpr)))
return (error);
if (carpr.carpr_advbase >= 0) {
if (carpr.carpr_advbase > 255) {
error = EINVAL;
break;
}
sc->sc_advbase = carpr.carpr_advbase;
error--;
}
if (memcmp(sc->sc_advskews, carpr.carpr_advskews,
sizeof(sc->sc_advskews))) {
i = 0;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts,
vhost_entries)
vhe->advskew = carpr.carpr_advskews[i++];
bcopy(carpr.carpr_advskews, sc->sc_advskews,
sizeof(sc->sc_advskews));
}
if (sc->sc_balancing != carpr.carpr_balancing) {
if (carpr.carpr_balancing > CARP_BAL_MAXID) {
error = EINVAL;
break;
}
sc->sc_balancing = carpr.carpr_balancing;
carp_set_enaddr(sc);
carp_update_lsmask(sc);
}
bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key));
if (error > 0)
error = EINVAL;
else {
error = 0;
carp_hmac_prepare(sc);
carp_setrun_all(sc, 0);
}
break;
case SIOCGVH:
memset(&carpr, 0, sizeof(carpr));
if ((ifp0 = if_get(sc->sc_carpdevidx)) != NULL)
strlcpy(carpr.carpr_carpdev, ifp0->if_xname, IFNAMSIZ);
if_put(ifp0);
i = 0;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
carpr.carpr_vhids[i] = vhe->vhid;
carpr.carpr_advskews[i] = vhe->advskew;
carpr.carpr_states[i] = vhe->state;
i++;
}
carpr.carpr_advbase = sc->sc_advbase;
carpr.carpr_balancing = sc->sc_balancing;
if (suser(p) == 0)
bcopy(sc->sc_key, carpr.carpr_key,
sizeof(carpr.carpr_key));
carpr.carpr_peer.s_addr = sc->sc_peer.s_addr;
error = copyout(&carpr, ifr->ifr_data, sizeof(carpr));
break;
case SIOCADDMULTI:
error = carp_ether_addmulti(sc, ifr);
break;
case SIOCDELMULTI:
error = carp_ether_delmulti(sc, ifr);
break;
case SIOCAIFGROUP:
case SIOCDIFGROUP:
if (sc->sc_demote_cnt)
carp_ifgroup_ioctl(ifp, cmd, addr);
break;
case SIOCSIFGATTR:
carp_ifgattr_ioctl(ifp, cmd, addr);
break;
default:
error = ENOTTY;
}
if (memcmp(sc->sc_ac.ac_enaddr, sc->sc_curlladdr, ETHER_ADDR_LEN) != 0)
carp_set_enaddr(sc);
return (error);
}
int
carp_check_dup_vhids(struct carp_softc *sc, struct srpl *cif,
struct carpreq *carpr)
{
struct carp_softc *vr;
struct carp_vhost_entry *vhe, *vhe0;
int i;
KERNEL_ASSERT_LOCKED(); /* touching if_carp + carp_vhosts */
SRPL_FOREACH_LOCKED(vr, cif, sc_list) {
if (vr == sc)
continue;
SRPL_FOREACH_LOCKED(vhe, &vr->carp_vhosts, vhost_entries) {
if (carpr) {
for (i = 0; carpr->carpr_vhids[i]; i++) {
if (vhe->vhid == carpr->carpr_vhids[i])
return (EINVAL);
}
}
SRPL_FOREACH_LOCKED(vhe0, &sc->carp_vhosts,
vhost_entries) {
if (vhe->vhid == vhe0->vhid)
return (EINVAL);
}
}
}
return (0);
}
int
carp_vhids_ioctl(struct carp_softc *sc, struct carpreq *carpr)
{
int i, j;
u_int8_t taken_vhids[256];
if (carpr->carpr_vhids[0] == 0 ||
!memcmp(sc->sc_vhids, carpr->carpr_vhids, sizeof(sc->sc_vhids)))
return (0);
memset(taken_vhids, 0, sizeof(taken_vhids));
for (i = 0; carpr->carpr_vhids[i]; i++) {
struct ifnet *ifp;
if (taken_vhids[carpr->carpr_vhids[i]])
return (EINVAL);
taken_vhids[carpr->carpr_vhids[i]] = 1;
if ((ifp = if_get(sc->sc_carpdevidx)) != NULL) {
struct srpl *cif;
cif = &ifp->if_carp;
if (carp_check_dup_vhids(sc, cif, carpr)) {
if_put(ifp);
return (EINVAL);
}
}
if_put(ifp);
if (carpr->carpr_advskews[i] >= 255)
return (EINVAL);
}
/* set sane balancing defaults */
if (i <= 1)
carpr->carpr_balancing = CARP_BAL_NONE;
else if (carpr->carpr_balancing == CARP_BAL_NONE &&
sc->sc_balancing == CARP_BAL_NONE)
carpr->carpr_balancing = CARP_BAL_IP;
/* destroy all */
carp_del_all_timeouts(sc);
carp_destroy_vhosts(sc);
memset(sc->sc_vhids, 0, sizeof(sc->sc_vhids));
/* sort vhosts list by vhid */
for (j = 1; j <= 255; j++) {
for (i = 0; carpr->carpr_vhids[i]; i++) {
if (carpr->carpr_vhids[i] != j)
continue;
if (carp_new_vhost(sc, carpr->carpr_vhids[i],
carpr->carpr_advskews[i]))
return (ENOMEM);
sc->sc_vhids[i] = carpr->carpr_vhids[i];
sc->sc_advskews[i] = carpr->carpr_advskews[i];
}
}
carp_set_enaddr(sc);
carp_set_state_all(sc, INIT);
return (0);
}
void
carp_ifgroup_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)addr;
struct ifg_list *ifgl;
int *dm, adj;
if (!strcmp(ifgr->ifgr_group, IFG_ALL))
return;
adj = ((struct carp_softc *)ifp->if_softc)->sc_demote_cnt;
if (cmd == SIOCDIFGROUP)
adj = adj * -1;
TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
if (!strcmp(ifgl->ifgl_group->ifg_group, ifgr->ifgr_group)) {
dm = &ifgl->ifgl_group->ifg_carp_demoted;
if (*dm + adj >= 0)
*dm += adj;
else
*dm = 0;
}
}
void
carp_ifgattr_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)addr;
struct carp_softc *sc = ifp->if_softc;
if (ifgr->ifgr_attrib.ifg_carp_demoted > 0 && (sc->sc_if.if_flags &
(IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING))
carp_vhe_send_ad_all(sc);
}
void
carp_start(struct ifnet *ifp)
{
struct carp_softc *sc = ifp->if_softc;
struct ifnet *ifp0;
struct mbuf *m;
if ((ifp0 = if_get(sc->sc_carpdevidx)) == NULL) {
ifq_purge(&ifp->if_snd);
return;
}
while ((m = ifq_dequeue(&ifp->if_snd)) != NULL)
carp_transmit(sc, ifp0, m);
if_put(ifp0);
}
void
carp_transmit(struct carp_softc *sc, struct ifnet *ifp0, struct mbuf *m)
{
struct ifnet *ifp = &sc->sc_if;
#if NBPFILTER > 0
{
caddr_t if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT);
}
#endif /* NBPFILTER > 0 */
if (!ISSET(ifp0->if_flags, IFF_RUNNING)) {
counters_inc(ifp->if_counters, ifc_oerrors);
m_freem(m);
return;
}
/*
* Do not leak the multicast address when sending
* advertisements in 'ip' and 'ip-stealth' balancing
* modes.
*/
if (sc->sc_balancing == CARP_BAL_IP ||
sc->sc_balancing == CARP_BAL_IPSTEALTH) {
struct ether_header *eh = mtod(m, struct ether_header *);
memcpy(eh->ether_shost, sc->sc_ac.ac_enaddr,
sizeof(eh->ether_shost));
}
if (if_enqueue(ifp0, m))
counters_inc(ifp->if_counters, ifc_oerrors);
}
int
carp_enqueue(struct ifnet *ifp, struct mbuf *m)
{
struct carp_softc *sc = ifp->if_softc;
struct ifnet *ifp0;
/* no ifq_is_priq, cos hfsc on carp doesn't make sense */
/*
* If the parent of this carp(4) got destroyed while
* `m' was being processed, silently drop it.
*/
if ((ifp0 = if_get(sc->sc_carpdevidx)) == NULL) {
m_freem(m);
return (0);
}
counters_pkt(ifp->if_counters,
ifc_opackets, ifc_obytes, m->m_pkthdr.len);
carp_transmit(sc, ifp0, m);
if_put(ifp0);
return (0);
}
int
carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
struct rtentry *rt)
{
struct carp_softc *sc = ((struct carp_softc *)ifp->if_softc);
struct carp_vhost_entry *vhe;
struct srp_ref sr;
int ismaster;
if (sc->cur_vhe == NULL) {
vhe = SRPL_FIRST(&sr, &sc->carp_vhosts);
ismaster = (vhe->state == MASTER);
SRPL_LEAVE(&sr);
} else {
ismaster = (sc->cur_vhe->state == MASTER);
}
if ((sc->sc_balancing == CARP_BAL_NONE && !ismaster)) {
m_freem(m);
return (ENETUNREACH);
}
return (ether_output(ifp, m, sa, rt));
}
void
carp_set_state_all(struct carp_softc *sc, int state)
{
struct carp_vhost_entry *vhe;
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
SRPL_FOREACH_LOCKED(vhe, &sc->carp_vhosts, vhost_entries) {
if (vhe->state == state)
continue;
carp_set_state(vhe, state);
}
}
void
carp_set_state(struct carp_vhost_entry *vhe, int state)
{
struct carp_softc *sc = vhe->parent_sc;
static const char *carp_states[] = { CARP_STATES };
int loglevel;
struct carp_vhost_entry *vhe0;
KASSERT(vhe->state != state);
if (vhe->state == INIT || state == INIT)
loglevel = LOG_WARNING;
else
loglevel = LOG_CRIT;
if (sc->sc_vhe_count > 1)
CARP_LOG(loglevel, sc,
("state transition (vhid %d): %s -> %s", vhe->vhid,
carp_states[vhe->state], carp_states[state]));
else
CARP_LOG(loglevel, sc,
("state transition: %s -> %s",
carp_states[vhe->state], carp_states[state]));
vhe->state = state;
carp_update_lsmask(sc);
KERNEL_ASSERT_LOCKED(); /* touching carp_vhosts */
sc->sc_if.if_link_state = LINK_STATE_INVALID;
SRPL_FOREACH_LOCKED(vhe0, &sc->carp_vhosts, vhost_entries) {
/*
* Link must be up if at least one vhe is in state MASTER to
* bring or keep route up.
*/
if (vhe0->state == MASTER) {
sc->sc_if.if_link_state = LINK_STATE_UP;
break;
} else if (vhe0->state == BACKUP) {
sc->sc_if.if_link_state = LINK_STATE_DOWN;
}
}
if_link_state_change(&sc->sc_if);
}
void
carp_group_demote_adj(struct ifnet *ifp, int adj, char *reason)
{
struct ifg_list *ifgl;
int *dm, need_ad;
struct carp_softc *nil = NULL;
if (ifp->if_type == IFT_CARP) {
dm = &((struct carp_softc *)ifp->if_softc)->sc_demote_cnt;
if (*dm + adj >= 0)
*dm += adj;
else
*dm = 0;
}
need_ad = 0;
TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
if (!strcmp(ifgl->ifgl_group->ifg_group, IFG_ALL))
continue;
dm = &ifgl->ifgl_group->ifg_carp_demoted;
if (*dm + adj >= 0)
*dm += adj;
else
*dm = 0;
if (adj > 0 && *dm == 1)
need_ad = 1;
CARP_LOG(LOG_ERR, nil,
("%s demoted group %s by %d to %d (%s)",
ifp->if_xname, ifgl->ifgl_group->ifg_group,
adj, *dm, reason));
}
if (need_ad)
carp_send_ad_all();
}
int
carp_group_demote_count(struct carp_softc *sc)
{
struct ifg_list *ifgl;
int count = 0;
TAILQ_FOREACH(ifgl, &sc->sc_if.if_groups, ifgl_next)
count += ifgl->ifgl_group->ifg_carp_demoted;
if (count == 0 && sc->sc_demote_cnt)
count = sc->sc_demote_cnt;
return (count > 255 ? 255 : count);
}
void
carp_carpdev_state(void *v)
{
struct carp_softc *sc = v;
struct ifnet *ifp0;
int suppressed = sc->sc_suppress;
if ((ifp0 = if_get(sc->sc_carpdevidx)) == NULL)
return;
if (ifp0->if_link_state == LINK_STATE_DOWN ||
!(ifp0->if_flags & IFF_UP)) {
sc->sc_if.if_flags &= ~IFF_RUNNING;
carp_del_all_timeouts(sc);
carp_set_state_all(sc, INIT);
sc->sc_suppress = 1;
carp_setrun_all(sc, 0);
if (!suppressed)
carp_group_demote_adj(&sc->sc_if, 1, "carpdev");
} else if (suppressed) {
carp_set_state_all(sc, INIT);
sc->sc_suppress = 0;
carp_setrun_all(sc, 0);
carp_group_demote_adj(&sc->sc_if, -1, "carpdev");
}
if_put(ifp0);
}
int
carp_ether_addmulti(struct carp_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp0;
struct carp_mc_entry *mc;
u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
int error;
ifp0 = if_get(sc->sc_carpdevidx);
if (ifp0 == NULL)
return (EINVAL);
error = ether_addmulti(ifr, (struct arpcom *)&sc->sc_ac);
if (error != ENETRESET) {
if_put(ifp0);
return (error);
}
/*
* This is new multicast address. We have to tell parent
* about it. Also, remember this multicast address so that
* we can delete them on unconfigure.
*/
mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT);
if (mc == NULL) {
error = ENOMEM;
goto alloc_failed;
}
/*
* As ether_addmulti() returns ENETRESET, following two
* statement shouldn't fail.
*/
(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, mc->mc_enm);
memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
LIST_INSERT_HEAD(&sc->carp_mc_listhead, mc, mc_entries);
error = (*ifp0->if_ioctl)(ifp0, SIOCADDMULTI, (caddr_t)ifr);
if (error != 0)
goto ioctl_failed;
if_put(ifp0);
return (error);
ioctl_failed:
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF, sizeof(*mc));
alloc_failed:
(void)ether_delmulti(ifr, (struct arpcom *)&sc->sc_ac);
if_put(ifp0);
return (error);
}
int
carp_ether_delmulti(struct carp_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp0;
struct ether_multi *enm;
struct carp_mc_entry *mc;
u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
int error;
ifp0 = if_get(sc->sc_carpdevidx);
if (ifp0 == NULL)
return (EINVAL);
/*
* Find a key to lookup carp_mc_entry. We have to do this
* before calling ether_delmulti for obvious reason.
*/
if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
goto rele;
ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, enm);
if (enm == NULL) {
error = EINVAL;
goto rele;
}
LIST_FOREACH(mc, &sc->carp_mc_listhead, mc_entries)
if (mc->mc_enm == enm)
break;
/* We won't delete entries we didn't add */
if (mc == NULL) {
error = EINVAL;
goto rele;
}
error = ether_delmulti(ifr, (struct arpcom *)&sc->sc_ac);
if (error != ENETRESET)
goto rele;
/* We no longer use this multicast address. Tell parent so. */
error = (*ifp0->if_ioctl)(ifp0, SIOCDELMULTI, (caddr_t)ifr);
if (error == 0) {
/* And forget about this address. */
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF, sizeof(*mc));
} else
(void)ether_addmulti(ifr, (struct arpcom *)&sc->sc_ac);
rele:
if_put(ifp0);
return (error);
}
/*
* Delete any multicast address we have asked to add from parent
* interface. Called when the carp is being unconfigured.
*/
void
carp_ether_purgemulti(struct carp_softc *sc)
{
struct ifnet *ifp0; /* Parent. */
struct carp_mc_entry *mc;
union {
struct ifreq ifreq;
struct {
char ifr_name[IFNAMSIZ];
struct sockaddr_storage ifr_ss;
} ifreq_storage;
} u;
struct ifreq *ifr = &u.ifreq;
if ((ifp0 = if_get(sc->sc_carpdevidx)) == NULL)
return;
memcpy(ifr->ifr_name, ifp0->if_xname, IFNAMSIZ);
while ((mc = LIST_FIRST(&sc->carp_mc_listhead)) != NULL) {
memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
(void)(*ifp0->if_ioctl)(ifp0, SIOCDELMULTI, (caddr_t)ifr);
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF, sizeof(*mc));
}
if_put(ifp0);
}
void
carp_vh_ref(void *null, void *v)
{
struct carp_vhost_entry *vhe = v;
refcnt_take(&vhe->vhost_refcnt);
}
void
carp_vh_unref(void *null, void *v)
{
struct carp_vhost_entry *vhe = v;
if (refcnt_rele(&vhe->vhost_refcnt)) {
carp_sc_unref(NULL, vhe->parent_sc);
free(vhe, M_DEVBUF, sizeof(*vhe));
}
}
void
carp_sc_ref(void *null, void *s)
{
struct carp_softc *sc = s;
refcnt_take(&sc->sc_refcnt);
}
void
carp_sc_unref(void *null, void *s)
{
struct carp_softc *sc = s;
refcnt_rele_wake(&sc->sc_refcnt);
}
162
60
2
9
8
16
28
3
3
11
39
1
115
2
2
111
2
21
90
1
89
5
1
72
88
12
1
4
1
101
101
99
17
5
3
101
100
9
9
3
6
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
/* $OpenBSD: in6_pcb.c,v 1.123 2022/09/03 22:43:38 mvs Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
/*
* Copyright (c) 1982, 1986, 1990, 1993, 1995
* Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "pf.h"
#include "stoeplitz.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet6/in6_var.h>
#if NSTOEPLITZ > 0
#include <net/toeplitz.h>
#endif
const struct in6_addr zeroin6_addr;
struct inpcb *in6_pcbhash_lookup(struct inpcbtable *, u_int,
const struct in6_addr *, u_short, const struct in6_addr *, u_short);
struct inpcbhead *
in6_pcbhash(struct inpcbtable *table, u_int rdomain,
const struct in6_addr *faddr, u_short fport,
const struct in6_addr *laddr, u_short lport)
{
SIPHASH_CTX ctx;
u_int32_t nrdom = htonl(rdomain);
SipHash24_Init(&ctx, &table->inpt_key);
SipHash24_Update(&ctx, &nrdom, sizeof(nrdom));
SipHash24_Update(&ctx, faddr, sizeof(*faddr));
SipHash24_Update(&ctx, &fport, sizeof(fport));
SipHash24_Update(&ctx, laddr, sizeof(*laddr));
SipHash24_Update(&ctx, &lport, sizeof(lport));
return (&table->inpt_hashtbl[SipHash24_End(&ctx) & table->inpt_mask]);
}
int
in6_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in6 *sin6, int wild,
struct proc *p)
{
struct socket *so = inp->inp_socket;
struct inpcbtable *table = inp->inp_table;
u_short lport = sin6->sin6_port;
int reuseport = (so->so_options & SO_REUSEPORT);
wild |= INPLOOKUP_IPV6;
/* KAME hack: embed scopeid */
if (in6_embedscope(&sin6->sin6_addr, sin6, inp) != 0)
return (EINVAL);
/* this must be cleared for ifa_ifwithaddr() */
sin6->sin6_scope_id = 0;
/* reject IPv4 mapped address, we have no support for it */
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
return (EADDRNOTAVAIL);
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
/*
* Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
* allow complete duplication of binding if
* SO_REUSEPORT is set, or if SO_REUSEADDR is set
* and a multicast address is bound on both
* new and duplicated sockets.
*/
if (so->so_options & (SO_REUSEADDR|SO_REUSEPORT))
reuseport = SO_REUSEADDR | SO_REUSEPORT;
} else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
struct ifaddr *ifa = NULL;
sin6->sin6_port = 0; /*
* Yechhhh, because of upcoming
* call to ifa_ifwithaddr(), which
* does bcmp's over the PORTS as
* well. (What about flow?)
*/
sin6->sin6_flowinfo = 0;
if (!(so->so_options & SO_BINDANY) &&
(ifa = ifa_ifwithaddr(sin6tosa(sin6),
inp->inp_rtableid)) == NULL)
return (EADDRNOTAVAIL);
sin6->sin6_port = lport;
/*
* bind to an anycast address might accidentally
* cause sending a packet with an anycast source
* address, so we forbid it.
*
* We should allow to bind to a deprecated address,
* since the application dare to use it.
* But, can we assume that they are careful enough
* to check if the address is deprecated or not?
* Maybe, as a safeguard, we should have a setsockopt
* flag to control the bind(2) behavior against
* deprecated addresses (default: forbid bind(2)).
*/
if (ifa && ifatoia6(ifa)->ia6_flags & (IN6_IFF_ANYCAST|
IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED|IN6_IFF_DETACHED))
return (EADDRNOTAVAIL);
}
if (lport) {
struct inpcb *t;
int error = 0;
if (so->so_euid && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
t = in_pcblookup_local(table, &sin6->sin6_addr, lport,
INPLOOKUP_WILDCARD | INPLOOKUP_IPV6,
inp->inp_rtableid);
if (t && (so->so_euid != t->inp_socket->so_euid))
error = EADDRINUSE;
in_pcbunref(t);
if (error)
return (error);
}
t = in_pcblookup_local(table, &sin6->sin6_addr, lport,
wild, inp->inp_rtableid);
if (t && (reuseport & t->inp_socket->so_options) == 0)
error = EADDRINUSE;
in_pcbunref(t);
if (error)
return (error);
}
return (0);
}
/*
* Connect from a socket to a specified address.
* Both address and port must be specified in argument sin6.
* Eventually, flow labels will have to be dealt with here, as well.
*
* If don't have a local address for this socket yet,
* then pick one.
*/
int
in6_pcbconnect(struct inpcb *inp, struct mbuf *nam)
{
struct in6_addr *in6a = NULL;
struct sockaddr_in6 *sin6;
struct inpcb *t;
int error;
struct sockaddr_in6 tmp;
KASSERT(inp->inp_flags & INP_IPV6);
if ((error = in6_nam2sin6(nam, &sin6)))
return (error);
if (sin6->sin6_port == 0)
return (EADDRNOTAVAIL);
/* reject IPv4 mapped address, we have no support for it */
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
return (EADDRNOTAVAIL);
/* protect *sin6 from overwrites */
tmp = *sin6;
sin6 = &tmp;
/* KAME hack: embed scopeid */
if (in6_embedscope(&sin6->sin6_addr, sin6, inp) != 0)
return EINVAL;
/* this must be cleared for ifa_ifwithaddr() */
sin6->sin6_scope_id = 0;
/* Source address selection. */
/*
* XXX: in6_selectsrc might replace the bound local address
* with the address specified by setsockopt(IPV6_PKTINFO).
* Is it the intended behavior?
*/
error = in6_pcbselsrc(&in6a, sin6, inp, inp->inp_outputopts6);
if (error)
return (error);
inp->inp_ipv6.ip6_hlim = (u_int8_t)in6_selecthlim(inp);
t = in6_pcblookup(inp->inp_table, &sin6->sin6_addr, sin6->sin6_port,
IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) ? in6a : &inp->inp_laddr6,
inp->inp_lport, inp->inp_rtableid);
if (t != NULL) {
in_pcbunref(t);
return (EADDRINUSE);
}
KASSERT(IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) || inp->inp_lport);
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) {
if (inp->inp_lport == 0) {
error = in_pcbbind(inp, NULL, curproc);
if (error)
return (error);
t = in6_pcblookup(inp->inp_table, &sin6->sin6_addr,
sin6->sin6_port, in6a, inp->inp_lport,
inp->inp_rtableid);
if (t != NULL) {
inp->inp_lport = 0;
in_pcbunref(t);
return (EADDRINUSE);
}
}
inp->inp_laddr6 = *in6a;
}
inp->inp_faddr6 = sin6->sin6_addr;
inp->inp_fport = sin6->sin6_port;
inp->inp_flowinfo &= ~IPV6_FLOWLABEL_MASK;
if (ip6_auto_flowlabel)
inp->inp_flowinfo |=
(htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK);
#if NSTOEPLITZ > 0
inp->inp_flowid = stoeplitz_ip6port(&inp->inp_faddr6,
&inp->inp_laddr6, inp->inp_fport, inp->inp_lport);
#endif
in_pcbrehash(inp);
return (0);
}
/*
* Get the local address/port, and put it in a sockaddr_in6.
* This services the getsockname(2) call.
*/
void
in6_setsockaddr(struct inpcb *inp, struct mbuf *nam)
{
struct sockaddr_in6 *sin6;
nam->m_len = sizeof(struct sockaddr_in6);
sin6 = mtod(nam,struct sockaddr_in6 *);
bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
sin6->sin6_port = inp->inp_lport;
sin6->sin6_addr = inp->inp_laddr6;
/* KAME hack: recover scopeid */
in6_recoverscope(sin6, &inp->inp_laddr6);
}
/*
* Get the foreign address/port, and put it in a sockaddr_in6.
* This services the getpeername(2) call.
*/
void
in6_setpeeraddr(struct inpcb *inp, struct mbuf *nam)
{
struct sockaddr_in6 *sin6;
nam->m_len = sizeof(struct sockaddr_in6);
sin6 = mtod(nam,struct sockaddr_in6 *);
bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
sin6->sin6_port = inp->inp_fport;
sin6->sin6_addr = inp->inp_faddr6;
/* KAME hack: recover scopeid */
in6_recoverscope(sin6, &inp->inp_faddr6);
}
int
in6_sockaddr(struct socket *so, struct mbuf *nam)
{
struct inpcb *in6p;
in6p = sotoinpcb(so);
in6_setsockaddr(in6p, nam);
return (0);
}
int
in6_peeraddr(struct socket *so, struct mbuf *nam)
{
struct inpcb *in6p;
in6p = sotoinpcb(so);
in6_setpeeraddr(in6p, nam);
return (0);
}
/*
* Pass some notification to all connections of a protocol
* associated with address dst. The local address and/or port numbers
* may be specified to limit the search. The "usual action" will be
* taken, depending on the ctlinput cmd. The caller must filter any
* cmds that are uninteresting (e.g., no error in the map).
* Call the protocol specific routine (if any) to report
* any errors for each matching socket.
*
* Also perform input-side security policy check
* once PCB to be notified has been located.
*/
void
in6_pcbnotify(struct inpcbtable *table, struct sockaddr_in6 *dst,
uint fport_arg, const struct sockaddr_in6 *src, uint lport_arg,
u_int rtable, int cmd, void *cmdarg, void (*notify)(struct inpcb *, int))
{
SIMPLEQ_HEAD(, inpcb) inpcblist;
struct inpcb *inp;
u_short fport = fport_arg, lport = lport_arg;
struct sockaddr_in6 sa6_src;
int errno;
u_int32_t flowinfo;
u_int rdomain;
if ((unsigned)cmd >= PRC_NCMDS)
return;
if (IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr))
return;
if (IN6_IS_ADDR_V4MAPPED(&dst->sin6_addr)) {
#ifdef DIAGNOSTIC
printf("%s: Huh? Thought we never got "
"called with mapped!\n", __func__);
#endif
return;
}
/*
* note that src can be NULL when we get notify by local fragmentation.
*/
sa6_src = (src == NULL) ? sa6_any : *src;
flowinfo = sa6_src.sin6_flowinfo;
/*
* Redirects go to all references to the destination,
* and use in_rtchange to invalidate the route cache.
* Dead host indications: also use in_rtchange to invalidate
* the cache, and deliver the error to all the sockets.
* Otherwise, if we have knowledge of the local port and address,
* deliver only to that socket.
*/
if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
fport = 0;
lport = 0;
sa6_src.sin6_addr = in6addr_any;
if (cmd != PRC_HOSTDEAD)
notify = in_rtchange;
}
errno = inet6ctlerrmap[cmd];
if (notify == NULL)
return;
SIMPLEQ_INIT(&inpcblist);
rdomain = rtable_l2(rtable);
rw_enter_write(&table->inpt_notify);
mtx_enter(&table->inpt_mtx);
TAILQ_FOREACH(inp, &table->inpt_queue, inp_queue) {
if ((inp->inp_flags & INP_IPV6) == 0)
continue;
/*
* Under the following condition, notify of redirects
* to the pcb, without making address matches against inpcb.
* - redirect notification is arrived.
* - the inpcb is unconnected.
* - the inpcb is caching !RTF_HOST routing entry.
* - the ICMPv6 notification is from the gateway cached in the
* inpcb. i.e. ICMPv6 notification is from nexthop gateway
* the inpcb used very recently.
*
* This is to improve interaction between netbsd/openbsd
* redirect handling code, and inpcb route cache code.
* without the clause, !RTF_HOST routing entry (which carries
* gateway used by inpcb right before the ICMPv6 redirect)
* will be cached forever in unconnected inpcb.
*
* There still is a question regarding to what is TRT:
* - On bsdi/freebsd, RTF_HOST (cloned) routing entry will be
* generated on packet output. inpcb will always cache
* RTF_HOST routing entry so there's no need for the clause
* (ICMPv6 redirect will update RTF_HOST routing entry,
* and inpcb is caching it already).
* However, bsdi/freebsd are vulnerable to local DoS attacks
* due to the cloned routing entries.
* - Specwise, "destination cache" is mentioned in RFC2461.
* Jinmei says that it implies bsdi/freebsd behavior, itojun
* is not really convinced.
* - Having hiwat/lowat on # of cloned host route (redirect/
* pmtud) may be a good idea. netbsd/openbsd has it. see
* icmp6_mtudisc_update().
*/
if ((PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) &&
IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
inp->inp_route.ro_rt &&
!(inp->inp_route.ro_rt->rt_flags & RTF_HOST)) {
struct sockaddr_in6 *dst6;
dst6 = satosin6(&inp->inp_route.ro_dst);
if (IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr,
&dst->sin6_addr))
goto do_notify;
}
/*
* Detect if we should notify the error. If no source and
* destination ports are specified, but non-zero flowinfo and
* local address match, notify the error. This is the case
* when the error is delivered with an encrypted buffer
* by ESP. Otherwise, just compare addresses and ports
* as usual.
*/
if (lport == 0 && fport == 0 && flowinfo &&
inp->inp_socket != NULL &&
flowinfo == (inp->inp_flowinfo & IPV6_FLOWLABEL_MASK) &&
IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &sa6_src.sin6_addr))
goto do_notify;
else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6,
&dst->sin6_addr) ||
rtable_l2(inp->inp_rtableid) != rdomain ||
inp->inp_socket == NULL ||
(lport && inp->inp_lport != lport) ||
(!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) &&
!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6,
&sa6_src.sin6_addr)) ||
(fport && inp->inp_fport != fport)) {
continue;
}
do_notify:
in_pcbref(inp);
SIMPLEQ_INSERT_TAIL(&inpcblist, inp, inp_notify);
}
mtx_leave(&table->inpt_mtx);
while ((inp = SIMPLEQ_FIRST(&inpcblist)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&inpcblist, inp_notify);
(*notify)(inp, errno);
in_pcbunref(inp);
}
rw_exit_write(&table->inpt_notify);
}
struct inpcb *
in6_pcbhash_lookup(struct inpcbtable *table, u_int rdomain,
const struct in6_addr *faddr, u_short fport,
const struct in6_addr *laddr, u_short lport)
{
struct inpcbhead *head;
struct inpcb *inp;
NET_ASSERT_LOCKED();
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
head = in6_pcbhash(table, rdomain, faddr, fport, laddr, lport);
LIST_FOREACH(inp, head, inp_hash) {
if (!ISSET(inp->inp_flags, INP_IPV6))
continue;
if (inp->inp_fport == fport && inp->inp_lport == lport &&
IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, faddr) &&
IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr) &&
rtable_l2(inp->inp_rtableid) == rdomain) {
break;
}
}
if (inp != NULL) {
/*
* Move this PCB to the head of hash chain so that
* repeated accesses are quicker. This is analogous to
* the historic single-entry PCB cache.
*/
if (inp != LIST_FIRST(head)) {
LIST_REMOVE(inp, inp_hash);
LIST_INSERT_HEAD(head, inp, inp_hash);
}
}
return (inp);
}
struct inpcb *
in6_pcblookup(struct inpcbtable *table, const struct in6_addr *faddr,
u_int fport, const struct in6_addr *laddr, u_int lport, u_int rtable)
{
struct inpcb *inp;
u_int rdomain;
rdomain = rtable_l2(rtable);
mtx_enter(&table->inpt_mtx);
inp = in6_pcbhash_lookup(table, rdomain, faddr, fport, laddr, lport);
in_pcbref(inp);
mtx_leave(&table->inpt_mtx);
#ifdef DIAGNOSTIC
if (inp == NULL && in_pcbnotifymiss) {
printf("%s: faddr= fport=%d laddr= lport=%d rdom=%u\n",
__func__, ntohs(fport), ntohs(lport), rdomain);
}
#endif
return (inp);
}
struct inpcb *
in6_pcblookup_listen(struct inpcbtable *table, struct in6_addr *laddr,
u_int lport, struct mbuf *m, u_int rtable)
{
const struct in6_addr *key1, *key2;
struct inpcb *inp;
u_int rdomain;
key1 = laddr;
key2 = &zeroin6_addr;
#if NPF > 0
if (m && m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
struct pf_divert *divert;
divert = pf_find_divert(m);
KASSERT(divert != NULL);
switch (divert->type) {
case PF_DIVERT_TO:
key1 = key2 = &divert->addr.v6;
lport = divert->port;
break;
case PF_DIVERT_REPLY:
return (NULL);
default:
panic("%s: unknown divert type %d, mbuf %p, divert %p",
__func__, divert->type, m, divert);
}
} else if (m && m->m_pkthdr.pf.flags & PF_TAG_TRANSLATE_LOCALHOST) {
/*
* Redirected connections should not be treated the same
* as connections directed to ::1 since localhost
* can only be accessed from the host itself.
*/
key1 = &zeroin6_addr;
key2 = laddr;
}
#endif
rdomain = rtable_l2(rtable);
mtx_enter(&table->inpt_mtx);
inp = in6_pcbhash_lookup(table, rdomain, &zeroin6_addr, 0, key1, lport);
if (inp == NULL && ! IN6_ARE_ADDR_EQUAL(key1, key2)) {
inp = in6_pcbhash_lookup(table, rdomain,
&zeroin6_addr, 0, key2, lport);
}
in_pcbref(inp);
mtx_leave(&table->inpt_mtx);
#ifdef DIAGNOSTIC
if (inp == NULL && in_pcbnotifymiss) {
printf("%s: laddr= lport=%d rdom=%u\n",
__func__, ntohs(lport), rdomain);
}
#endif
return (inp);
}
61
2
19
41
1
40
2
2
12
134
3
126
1
129
25
23
33
34
21
25
1
4
20
2
1
2
6
1
5
10
15
15
5
10
10
5
1
9
2
3
11
11
11
14
14
268
3
260
2
256
1
104
161
38
260
26
8
4
10
5
15
1
1
10
1
11
11
2
2
2
1
3
2
6
3
5
217
231
3
219
11
2
11
214
187
37
5
213
11
54
49
49
49
4
51
13
1
51
12
3
40
13
491
474
3
325
5
166
164
8
359
3
122
461
22
206
136
163
465
13
475
42
3
30
71
3
2
14
52
36
32
15
45
2
4
51
14
57
35
2
17
3
2
18
28
40
6
39
23
1
39
26
17
33
4
3
9
9
26
23
24
24
140
134
4
122
12
124
10
133
118
29
121
3
11
29
100
3
7
5
33
95
14
3
15
2
14
130
68
65
808
1
170
618
31
3
641
803
194
28
169
13
3
14
189
30
2
3
26
20
1
2
17
541
6
3
584
18
8
531
30
13
50
28
27
4
33
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
/* $OpenBSD: uipc_syscalls.c,v 1.204 2022/09/03 21:13:48 mbuhl Exp $ */
/* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/namei.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/event.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/signalvar.h>
#include <sys/pledge.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <sys/unistd.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/domain.h>
#include <netinet/in.h>
#include <net/rtable.h>
int copyaddrout(struct proc *, struct mbuf *, struct sockaddr *, socklen_t,
socklen_t *);
int
sys_socket(struct proc *p, void *v, register_t *retval)
{
struct sys_socket_args /* {
syscallarg(int) domain;
syscallarg(int) type;
syscallarg(int) protocol;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct socket *so;
struct file *fp;
int type = SCARG(uap, type);
int domain = SCARG(uap, domain);
int fd, cloexec, nonblock, fflag, error;
unsigned int ss = 0;
if ((type & SOCK_DNS) && !(domain == AF_INET || domain == AF_INET6))
return (EINVAL);
if (ISSET(type, SOCK_DNS))
ss |= SS_DNS;
error = pledge_socket(p, domain, ss);
if (error)
return (error);
type &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_DNS);
cloexec = (SCARG(uap, type) & SOCK_CLOEXEC) ? UF_EXCLOSE : 0;
nonblock = SCARG(uap, type) & SOCK_NONBLOCK;
fflag = FREAD | FWRITE | (nonblock ? FNONBLOCK : 0);
error = socreate(SCARG(uap, domain), &so, type, SCARG(uap, protocol));
if (error)
return (error);
fdplock(fdp);
error = falloc(p, &fp, &fd);
if (error) {
fdpunlock(fdp);
soclose(so, MSG_DONTWAIT);
} else {
fp->f_flag = fflag;
fp->f_type = DTYPE_SOCKET;
fp->f_ops = &socketops;
so->so_state |= ss;
fp->f_data = so;
fdinsert(fdp, fd, cloexec, fp);
fdpunlock(fdp);
FRELE(fp, p);
*retval = fd;
}
return (error);
}
static inline int
isdnssocket(struct socket *so)
{
return (so->so_state & SS_DNS);
}
/* For SS_DNS sockets, only allow port DNS (port 53) */
static int
dns_portcheck(struct proc *p, struct socket *so, void *nam, size_t namelen)
{
int error = EINVAL;
switch (so->so_proto->pr_domain->dom_family) {
case AF_INET:
if (namelen < sizeof(struct sockaddr_in))
break;
if (((struct sockaddr_in *)nam)->sin_port == htons(53))
error = 0;
break;
#ifdef INET6
case AF_INET6:
if (namelen < sizeof(struct sockaddr_in6))
break;
if (((struct sockaddr_in6 *)nam)->sin6_port == htons(53))
error = 0;
#endif
}
if (error && p->p_p->ps_flags & PS_PLEDGE)
return (pledge_fail(p, EPERM, PLEDGE_DNS));
return error;
}
int
sys_bind(struct proc *p, void *v, register_t *retval)
{
struct sys_bind_args /* {
syscallarg(int) s;
syscallarg(const struct sockaddr *) name;
syscallarg(socklen_t) namelen;
} */ *uap = v;
struct file *fp;
struct mbuf *nam;
struct socket *so;
int error;
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
so = fp->f_data;
error = pledge_socket(p, so->so_proto->pr_domain->dom_family,
so->so_state);
if (error)
goto out;
if (so->so_state & SS_YP) {
error = ENOTSOCK;
goto out;
}
error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen),
MT_SONAME);
if (error)
goto out;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(nam, caddr_t), SCARG(uap, namelen));
#endif
solock(so);
error = sobind(so, nam, p);
sounlock(so);
m_freem(nam);
out:
FRELE(fp, p);
return (error);
}
int
sys_listen(struct proc *p, void *v, register_t *retval)
{
struct sys_listen_args /* {
syscallarg(int) s;
syscallarg(int) backlog;
} */ *uap = v;
struct file *fp;
struct socket *so;
int error;
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
so = fp->f_data;
if (so->so_state & SS_YP)
return ENOTSOCK;
solock(so);
error = solisten(so, SCARG(uap, backlog));
sounlock(so);
FRELE(fp, p);
return (error);
}
int
sys_accept(struct proc *p, void *v, register_t *retval)
{
struct sys_accept_args /* {
syscallarg(int) s;
syscallarg(struct sockaddr *) name;
syscallarg(socklen_t *) anamelen;
} */ *uap = v;
return (doaccept(p, SCARG(uap, s), SCARG(uap, name),
SCARG(uap, anamelen), SOCK_NONBLOCK_INHERIT, retval));
}
int
sys_accept4(struct proc *p, void *v, register_t *retval)
{
struct sys_accept4_args /* {
syscallarg(int) s;
syscallarg(struct sockaddr *) name;
syscallarg(socklen_t *) anamelen;
syscallarg(socklen_t *) int flags;
} */ *uap = v;
if (SCARG(uap, flags) & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return (EINVAL);
return (doaccept(p, SCARG(uap, s), SCARG(uap, name),
SCARG(uap, anamelen), SCARG(uap, flags), retval));
}
int
doaccept(struct proc *p, int sock, struct sockaddr *name, socklen_t *anamelen,
int flags, register_t *retval)
{
struct filedesc *fdp = p->p_fd;
struct file *fp, *headfp;
struct mbuf *nam;
socklen_t namelen;
int error, tmpfd;
struct socket *head, *so;
int cloexec, nflag, persocket;
cloexec = (flags & SOCK_CLOEXEC) ? UF_EXCLOSE : 0;
if (name && (error = copyin(anamelen, &namelen, sizeof (namelen))))
return (error);
if ((error = getsock(p, sock, &fp)) != 0)
return (error);
headfp = fp;
fdplock(fdp);
error = falloc(p, &fp, &tmpfd);
fdpunlock(fdp);
if (error) {
FRELE(headfp, p);
return (error);
}
nam = m_get(M_WAIT, MT_SONAME);
head = headfp->f_data;
solock(head);
persocket = solock_persocket(head);
if (isdnssocket(head) || (head->so_options & SO_ACCEPTCONN) == 0) {
error = EINVAL;
goto out_unlock;
}
if ((headfp->f_flag & FNONBLOCK) && head->so_qlen == 0) {
if (head->so_state & SS_CANTRCVMORE)
error = ECONNABORTED;
else
error = EWOULDBLOCK;
goto out_unlock;
}
while (head->so_qlen == 0 && head->so_error == 0) {
if (head->so_state & SS_CANTRCVMORE) {
head->so_error = ECONNABORTED;
break;
}
error = sosleep_nsec(head, &head->so_timeo, PSOCK | PCATCH,
"netcon", INFSLP);
if (error)
goto out_unlock;
}
if (head->so_error) {
error = head->so_error;
head->so_error = 0;
goto out_unlock;
}
/*
* Do not sleep after we have taken the socket out of the queue.
*/
so = TAILQ_FIRST(&head->so_q);
if (persocket)
solock(so);
if (soqremque(so, 1) == 0)
panic("accept");
/* Figure out whether the new socket should be non-blocking. */
nflag = flags & SOCK_NONBLOCK_INHERIT ? (headfp->f_flag & FNONBLOCK)
: (flags & SOCK_NONBLOCK ? FNONBLOCK : 0);
/* connection has been removed from the listen queue */
KNOTE(&head->so_rcv.sb_sel.si_note, 0);
if (persocket)
sounlock(head);
fp->f_type = DTYPE_SOCKET;
fp->f_flag = FREAD | FWRITE | nflag;
fp->f_ops = &socketops;
fp->f_data = so;
error = soaccept(so, nam);
if (persocket)
sounlock(so);
else
sounlock(head);
if (error)
goto out;
if (name != NULL) {
error = copyaddrout(p, nam, name, namelen, anamelen);
if (error)
goto out;
}
fdplock(fdp);
fdinsert(fdp, tmpfd, cloexec, fp);
fdpunlock(fdp);
FRELE(fp, p);
*retval = tmpfd;
m_freem(nam);
FRELE(headfp, p);
return 0;
out_unlock:
sounlock(head);
out:
fdplock(fdp);
fdremove(fdp, tmpfd);
fdpunlock(fdp);
closef(fp, p);
m_freem(nam);
FRELE(headfp, p);
return (error);
}
int
sys_connect(struct proc *p, void *v, register_t *retval)
{
struct sys_connect_args /* {
syscallarg(int) s;
syscallarg(const struct sockaddr *) name;
syscallarg(socklen_t) namelen;
} */ *uap = v;
struct file *fp;
struct socket *so;
struct mbuf *nam;
int error, interrupted = 0;
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
so = fp->f_data;
error = pledge_socket(p, so->so_proto->pr_domain->dom_family,
so->so_state);
if (error)
goto out;
if (so->so_state & SS_YP) {
error = ENOTSOCK;
goto out;
}
error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen),
MT_SONAME);
if (error)
goto out;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(nam, caddr_t), SCARG(uap, namelen));
#endif
solock(so);
if (isdnssocket(so)) {
error = dns_portcheck(p, so, mtod(nam, void *), nam->m_len);
if (error)
goto unlock;
}
if (so->so_state & SS_ISCONNECTING) {
error = EALREADY;
goto unlock;
}
error = soconnect(so, nam);
if (error)
goto bad;
if ((fp->f_flag & FNONBLOCK) && (so->so_state & SS_ISCONNECTING)) {
error = EINPROGRESS;
goto unlock;
}
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
error = sosleep_nsec(so, &so->so_timeo, PSOCK | PCATCH,
"netcon2", INFSLP);
if (error) {
if (error == EINTR || error == ERESTART)
interrupted = 1;
break;
}
}
if (error == 0) {
error = so->so_error;
so->so_error = 0;
}
bad:
if (!interrupted)
so->so_state &= ~SS_ISCONNECTING;
unlock:
sounlock(so);
m_freem(nam);
out:
FRELE(fp, p);
if (error == ERESTART)
error = EINTR;
return (error);
}
int
sys_socketpair(struct proc *p, void *v, register_t *retval)
{
struct sys_socketpair_args /* {
syscallarg(int) domain;
syscallarg(int) type;
syscallarg(int) protocol;
syscallarg(int *) rsv;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct file *fp1 = NULL, *fp2 = NULL;
struct socket *so1, *so2;
int type, cloexec, nonblock, fflag, error, sv[2];
type = SCARG(uap, type) & ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
cloexec = (SCARG(uap, type) & SOCK_CLOEXEC) ? UF_EXCLOSE : 0;
nonblock = SCARG(uap, type) & SOCK_NONBLOCK;
fflag = FREAD | FWRITE | (nonblock ? FNONBLOCK : 0);
error = socreate(SCARG(uap, domain), &so1, type, SCARG(uap, protocol));
if (error)
return (error);
error = socreate(SCARG(uap, domain), &so2, type, SCARG(uap, protocol));
if (error)
goto free1;
error = soconnect2(so1, so2);
if (error != 0)
goto free2;
if ((SCARG(uap, type) & SOCK_TYPE_MASK) == SOCK_DGRAM) {
/*
* Datagram socket connection is asymmetric.
*/
error = soconnect2(so2, so1);
if (error != 0)
goto free2;
}
fdplock(fdp);
if ((error = falloc(p, &fp1, &sv[0])) != 0)
goto free3;
fp1->f_flag = fflag;
fp1->f_type = DTYPE_SOCKET;
fp1->f_ops = &socketops;
fp1->f_data = so1;
if ((error = falloc(p, &fp2, &sv[1])) != 0)
goto free4;
fp2->f_flag = fflag;
fp2->f_type = DTYPE_SOCKET;
fp2->f_ops = &socketops;
fp2->f_data = so2;
error = copyout(sv, SCARG(uap, rsv), 2 * sizeof (int));
if (error == 0) {
fdinsert(fdp, sv[0], cloexec, fp1);
fdinsert(fdp, sv[1], cloexec, fp2);
fdpunlock(fdp);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrfds(p, sv, 2);
#endif
FRELE(fp1, p);
FRELE(fp2, p);
return (0);
}
fdremove(fdp, sv[1]);
free4:
fdremove(fdp, sv[0]);
free3:
fdpunlock(fdp);
if (fp2 != NULL) {
closef(fp2, p);
so2 = NULL;
}
if (fp1 != NULL) {
closef(fp1, p);
so1 = NULL;
}
free2:
if (so2 != NULL)
(void)soclose(so2, 0);
free1:
if (so1 != NULL)
(void)soclose(so1, 0);
return (error);
}
int
sys_sendto(struct proc *p, void *v, register_t *retval)
{
struct sys_sendto_args /* {
syscallarg(int) s;
syscallarg(const void *) buf;
syscallarg(size_t) len;
syscallarg(int) flags;
syscallarg(const struct sockaddr *) to;
syscallarg(socklen_t) tolen;
} */ *uap = v;
struct msghdr msg;
struct iovec aiov;
msg.msg_name = (caddr_t)SCARG(uap, to);
msg.msg_namelen = SCARG(uap, tolen);
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_flags = 0;
aiov.iov_base = (char *)SCARG(uap, buf);
aiov.iov_len = SCARG(uap, len);
return (sendit(p, SCARG(uap, s), &msg, SCARG(uap, flags), retval));
}
int
sys_sendmsg(struct proc *p, void *v, register_t *retval)
{
struct sys_sendmsg_args /* {
syscallarg(int) s;
syscallarg(const struct msghdr *) msg;
syscallarg(int) flags;
} */ *uap = v;
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *iov;
int error;
error = copyin(SCARG(uap, msg), &msg, sizeof (msg));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrmsghdr(p, &msg);
#endif
if (msg.msg_iovlen > IOV_MAX)
return (EMSGSIZE);
if (msg.msg_iovlen > UIO_SMALLIOV)
iov = mallocarray(msg.msg_iovlen, sizeof(struct iovec),
M_IOV, M_WAITOK);
else
iov = aiov;
if (msg.msg_iovlen &&
(error = copyin(msg.msg_iov, iov,
msg.msg_iovlen * sizeof (struct iovec))))
goto done;
#ifdef KTRACE
if (msg.msg_iovlen && KTRPOINT(p, KTR_STRUCT))
ktriovec(p, iov, msg.msg_iovlen);
#endif
msg.msg_iov = iov;
msg.msg_flags = 0;
error = sendit(p, SCARG(uap, s), &msg, SCARG(uap, flags), retval);
done:
if (iov != aiov)
free(iov, M_IOV, sizeof(struct iovec) * msg.msg_iovlen);
return (error);
}
int
sys_sendmmsg(struct proc *p, void *v, register_t *retval)
{
struct sys_sendmmsg_args /* {
syscallarg(int) s;
syscallarg(struct mmsghdr *) mmsg;
syscallarg(unsigned int) vlen;
syscallarg(int) flags;
} */ *uap = v;
struct mmsghdr mmsg, *mmsgp;
struct iovec aiov[UIO_SMALLIOV], *iov = aiov, *uiov;
size_t iovlen = UIO_SMALLIOV;
register_t retsnd;
unsigned int vlen, dgrams;
int error = 0, flags, s;
s = SCARG(uap, s);
flags = SCARG(uap, flags);
/* Arbitrarily capped at 1024 datagrams. */
vlen = SCARG(uap, vlen);
if (vlen > 1024)
vlen = 1024;
mmsgp = SCARG(uap, mmsg);
for (dgrams = 0; dgrams < vlen; dgrams++) {
error = copyin(&mmsgp[dgrams], &mmsg, sizeof(mmsg));
if (error)
break;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrmmsghdr(p, &mmsg);
#endif
if (mmsg.msg_hdr.msg_iovlen > IOV_MAX) {
error = EMSGSIZE;
break;
}
if (mmsg.msg_hdr.msg_iovlen > iovlen) {
if (iov != aiov)
free(iov, M_IOV, iovlen *
sizeof(struct iovec));
iovlen = mmsg.msg_hdr.msg_iovlen;
iov = mallocarray(iovlen, sizeof(struct iovec),
M_IOV, M_WAITOK);
}
if (mmsg.msg_hdr.msg_iovlen > 0) {
error = copyin(mmsg.msg_hdr.msg_iov, iov,
mmsg.msg_hdr.msg_iovlen * sizeof(struct iovec));
if (error)
break;
}
#ifdef KTRACE
if (mmsg.msg_hdr.msg_iovlen && KTRPOINT(p, KTR_STRUCT))
ktriovec(p, iov, mmsg.msg_hdr.msg_iovlen);
#endif
uiov = mmsg.msg_hdr.msg_iov;
mmsg.msg_hdr.msg_iov = iov;
mmsg.msg_hdr.msg_flags = 0;
error = sendit(p, s, &mmsg.msg_hdr, flags, &retsnd);
if (error)
break;
mmsg.msg_hdr.msg_iov = uiov;
mmsg.msg_len = retsnd;
error = copyout(&mmsg, &mmsgp[dgrams], sizeof(mmsg));
if (error)
break;
}
if (iov != aiov)
free(iov, M_IOV, sizeof(struct iovec) * iovlen);
*retval = dgrams;
if (error && dgrams > 0)
error = 0;
return (error);
}
int
sendit(struct proc *p, int s, struct msghdr *mp, int flags, register_t *retsize)
{
struct file *fp;
struct uio auio;
struct iovec *iov;
int i;
struct mbuf *to, *control;
struct socket *so;
size_t len;
int error;
#ifdef KTRACE
struct iovec *ktriov = NULL;
int iovlen = 0;
#endif
to = NULL;
if ((error = getsock(p, s, &fp)) != 0)
return (error);
so = fp->f_data;
if (fp->f_flag & FNONBLOCK)
flags |= MSG_DONTWAIT;
error = pledge_sendit(p, mp->msg_name);
if (error)
goto bad;
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_procp = p;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
/* Don't allow sum > SSIZE_MAX */
if (iov->iov_len > SSIZE_MAX ||
(auio.uio_resid += iov->iov_len) > SSIZE_MAX) {
error = EINVAL;
goto bad;
}
}
if (mp->msg_name) {
error = sockargs(&to, mp->msg_name, mp->msg_namelen,
MT_SONAME);
if (error)
goto bad;
if (isdnssocket(so)) {
error = dns_portcheck(p, so, mtod(to, caddr_t),
mp->msg_namelen);
if (error)
goto bad;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(to, caddr_t), mp->msg_namelen);
#endif
}
if (mp->msg_control) {
if (mp->msg_controllen < CMSG_ALIGN(sizeof(struct cmsghdr))) {
error = EINVAL;
goto bad;
}
error = sockargs(&control, mp->msg_control,
mp->msg_controllen, MT_CONTROL);
if (error)
goto bad;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT) && mp->msg_controllen)
ktrcmsghdr(p, mtod(control, char *),
mp->msg_controllen);
#endif
} else
control = NULL;
#ifdef KTRACE
if (KTRPOINT(p, KTR_GENIO)) {
ktriov = mallocarray(auio.uio_iovcnt, sizeof(struct iovec),
M_TEMP, M_WAITOK);
iovlen = auio.uio_iovcnt * sizeof (struct iovec);
memcpy(ktriov, auio.uio_iov, iovlen);
}
#endif
len = auio.uio_resid;
error = sosend(so, to, &auio, NULL, control, flags);
if (error) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
if (error == EPIPE && (flags & MSG_NOSIGNAL) == 0) {
KERNEL_LOCK();
ptsignal(p, SIGPIPE, STHREAD);
KERNEL_UNLOCK();
}
}
if (error == 0) {
*retsize = len - auio.uio_resid;
mtx_enter(&fp->f_mtx);
fp->f_wxfer++;
fp->f_wbytes += *retsize;
mtx_leave(&fp->f_mtx);
}
#ifdef KTRACE
if (ktriov != NULL) {
if (error == 0)
ktrgenio(p, s, UIO_WRITE, ktriov, *retsize);
free(ktriov, M_TEMP, iovlen);
}
#endif
bad:
FRELE(fp, p);
m_freem(to);
return (error);
}
int
sys_recvfrom(struct proc *p, void *v, register_t *retval)
{
struct sys_recvfrom_args /* {
syscallarg(int) s;
syscallarg(void *) buf;
syscallarg(size_t) len;
syscallarg(int) flags;
syscallarg(struct sockaddr *) from;
syscallarg(socklen_t *) fromlenaddr;
} */ *uap = v;
struct msghdr msg;
struct iovec aiov;
int error;
if (SCARG(uap, fromlenaddr)) {
error = copyin(SCARG(uap, fromlenaddr),
&msg.msg_namelen, sizeof (msg.msg_namelen));
if (error)
return (error);
} else
msg.msg_namelen = 0;
msg.msg_name = (caddr_t)SCARG(uap, from);
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = SCARG(uap, buf);
aiov.iov_len = SCARG(uap, len);
msg.msg_control = NULL;
msg.msg_flags = SCARG(uap, flags);
return (recvit(p, SCARG(uap, s), &msg,
(caddr_t)SCARG(uap, fromlenaddr), retval));
}
int
sys_recvmsg(struct proc *p, void *v, register_t *retval)
{
struct sys_recvmsg_args /* {
syscallarg(int) s;
syscallarg(struct msghdr *) msg;
syscallarg(int) flags;
} */ *uap = v;
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *uiov, *iov;
int error;
error = copyin(SCARG(uap, msg), &msg, sizeof (msg));
if (error)
return (error);
if (msg.msg_iovlen > IOV_MAX)
return (EMSGSIZE);
if (msg.msg_iovlen > UIO_SMALLIOV)
iov = mallocarray(msg.msg_iovlen, sizeof(struct iovec),
M_IOV, M_WAITOK);
else
iov = aiov;
msg.msg_flags = SCARG(uap, flags);
if (msg.msg_iovlen > 0) {
error = copyin(msg.msg_iov, iov,
msg.msg_iovlen * sizeof(struct iovec));
if (error)
goto done;
}
uiov = msg.msg_iov;
msg.msg_iov = iov;
if ((error = recvit(p, SCARG(uap, s), &msg, NULL, retval)) == 0) {
msg.msg_iov = uiov;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT)) {
ktrmsghdr(p, &msg);
if (msg.msg_iovlen)
ktriovec(p, iov, msg.msg_iovlen);
}
#endif
error = copyout(&msg, SCARG(uap, msg), sizeof(msg));
}
done:
if (iov != aiov)
free(iov, M_IOV, sizeof(struct iovec) * msg.msg_iovlen);
return (error);
}
int
sys_recvmmsg(struct proc *p, void *v, register_t *retval)
{
struct sys_recvmmsg_args /* {
syscallarg(int) s;
syscallarg(struct mmsghdr *) mmsg;
syscallarg(unsigned int) vlen;
syscallarg(int) flags;
syscallarg(struct timespec *) timeout;
} */ *uap = v;
struct mmsghdr mmsg, *mmsgp;
struct timespec ts, now, *timeout;
struct iovec aiov[UIO_SMALLIOV], *uiov, *iov = aiov;
size_t iovlen = UIO_SMALLIOV;
register_t retrec;
unsigned int vlen, dgrams;
int error = 0, flags, s;
timeout = SCARG(uap, timeout);
if (timeout != NULL) {
error = copyin(timeout, &ts, sizeof(ts));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
if (!timespecisvalid(&ts))
return (EINVAL);
getnanotime(&now);
timespecadd(&now, &ts, &ts);
}
s = SCARG(uap, s);
flags = SCARG(uap, flags);
/* Arbitrarily capped at 1024 datagrams. */
vlen = SCARG(uap, vlen);
if (vlen > 1024)
vlen = 1024;
mmsgp = SCARG(uap, mmsg);
for (dgrams = 0; dgrams < vlen;) {
error = copyin(&mmsgp[dgrams], &mmsg, sizeof(mmsg));
if (error)
break;
if (mmsg.msg_hdr.msg_iovlen > IOV_MAX) {
error = EMSGSIZE;
break;
}
if (mmsg.msg_hdr.msg_iovlen > iovlen) {
if (iov != aiov)
free(iov, M_IOV, iovlen *
sizeof(struct iovec));
iovlen = mmsg.msg_hdr.msg_iovlen;
iov = mallocarray(iovlen, sizeof(struct iovec),
M_IOV, M_WAITOK);
}
if (mmsg.msg_hdr.msg_iovlen > 0) {
error = copyin(mmsg.msg_hdr.msg_iov, iov,
mmsg.msg_hdr.msg_iovlen * sizeof(struct iovec));
if (error)
break;
}
uiov = mmsg.msg_hdr.msg_iov;
mmsg.msg_hdr.msg_iov = iov;
mmsg.msg_hdr.msg_flags = flags & ~MSG_WAITFORONE;
error = recvit(p, s, &mmsg.msg_hdr, NULL, &retrec);
if (error) {
if (error == EAGAIN && dgrams > 0)
error = 0;
break;
}
if (flags & MSG_WAITFORONE)
flags |= MSG_DONTWAIT;
mmsg.msg_hdr.msg_iov = uiov;
mmsg.msg_len = retrec;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT)) {
ktrmmsghdr(p, &mmsg);
if (mmsg.msg_hdr.msg_iovlen)
ktriovec(p, iov, mmsg.msg_hdr.msg_iovlen);
}
#endif
error = copyout(&mmsg, &mmsgp[dgrams], sizeof(mmsg));
if (error)
break;
dgrams++;
if (mmsg.msg_hdr.msg_flags & MSG_OOB)
break;
if (timeout != NULL) {
getnanotime(&now);
timespecsub(&now, &ts, &now);
if (now.tv_sec > 0)
break;
}
}
if (iov != aiov)
free(iov, M_IOV, iovlen * sizeof(struct iovec));
*retval = dgrams;
/*
* If we succeeded at least once, return 0, hopefully so->so_error
* will catch it next time.
*/
if (error && dgrams > 0) {
struct file *fp;
struct socket *so;
if (getsock(p, s, &fp) == 0) {
so = (struct socket *)fp->f_data;
so->so_error = error;
FRELE(fp, p);
}
error = 0;
}
return (error);
}
int
recvit(struct proc *p, int s, struct msghdr *mp, caddr_t namelenp,
register_t *retsize)
{
struct file *fp;
struct uio auio;
struct iovec *iov;
int i;
size_t len;
int error;
struct mbuf *from = NULL, *control = NULL;
#ifdef KTRACE
struct iovec *ktriov = NULL;
int iovlen = 0, kmsgflags;
#endif
if ((error = getsock(p, s, &fp)) != 0)
return (error);
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_rw = UIO_READ;
auio.uio_procp = p;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
/* Don't allow sum > SSIZE_MAX */
if (iov->iov_len > SSIZE_MAX ||
(auio.uio_resid += iov->iov_len) > SSIZE_MAX) {
error = EINVAL;
goto out;
}
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_GENIO)) {
ktriov = mallocarray(auio.uio_iovcnt, sizeof(struct iovec),
M_TEMP, M_WAITOK);
iovlen = auio.uio_iovcnt * sizeof (struct iovec);
memcpy(ktriov, auio.uio_iov, iovlen);
}
kmsgflags = mp->msg_flags;
#endif
len = auio.uio_resid;
if (fp->f_flag & FNONBLOCK)
mp->msg_flags |= MSG_DONTWAIT;
error = soreceive(fp->f_data, &from, &auio, NULL,
mp->msg_control ? &control : NULL,
&mp->msg_flags,
mp->msg_control ? mp->msg_controllen : 0);
if (error) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
}
#ifdef KTRACE
if (ktriov != NULL) {
if (error == 0)
ktrgenio(p, s, UIO_READ, ktriov, len - auio.uio_resid);
free(ktriov, M_TEMP, iovlen);
}
#endif
if (error)
goto out;
*retsize = len - auio.uio_resid;
if (mp->msg_name) {
socklen_t alen;
if (from == NULL)
alen = 0;
else {
alen = from->m_len;
error = copyout(mtod(from, caddr_t), mp->msg_name,
MIN(alen, mp->msg_namelen));
if (error)
goto out;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(from, caddr_t), alen);
#endif
}
mp->msg_namelen = alen;
if (namelenp &&
(error = copyout(&alen, namelenp, sizeof(alen)))) {
goto out;
}
}
if (mp->msg_control) {
len = mp->msg_controllen;
if (len <= 0 || control == NULL)
len = 0;
else {
struct mbuf *m = control;
caddr_t cp = mp->msg_control;
do {
i = m->m_len;
if (len < i) {
mp->msg_flags |= MSG_CTRUNC;
i = len;
}
error = copyout(mtod(m, caddr_t), cp, i);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT) && error == 0 && i) {
/* msg_flags potentially incorrect */
int rmsgflags = mp->msg_flags;
mp->msg_flags = kmsgflags;
ktrcmsghdr(p, mtod(m, char *), i);
mp->msg_flags = rmsgflags;
}
#endif
if (m->m_next)
i = ALIGN(i);
cp += i;
len -= i;
if (error != 0 || len <= 0)
break;
} while ((m = m->m_next) != NULL);
len = cp - (caddr_t)mp->msg_control;
}
mp->msg_controllen = len;
}
if (!error) {
mtx_enter(&fp->f_mtx);
fp->f_rxfer++;
fp->f_rbytes += *retsize;
mtx_leave(&fp->f_mtx);
}
out:
FRELE(fp, p);
m_freem(from);
m_freem(control);
return (error);
}
int
sys_shutdown(struct proc *p, void *v, register_t *retval)
{
struct sys_shutdown_args /* {
syscallarg(int) s;
syscallarg(int) how;
} */ *uap = v;
struct file *fp;
int error;
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
error = soshutdown(fp->f_data, SCARG(uap, how));
FRELE(fp, p);
return (error);
}
int
sys_setsockopt(struct proc *p, void *v, register_t *retval)
{
struct sys_setsockopt_args /* {
syscallarg(int) s;
syscallarg(int) level;
syscallarg(int) name;
syscallarg(const void *) val;
syscallarg(socklen_t) valsize;
} */ *uap = v;
struct file *fp;
struct mbuf *m = NULL;
struct socket *so;
int error;
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
error = pledge_sockopt(p, 1, SCARG(uap, level), SCARG(uap, name));
if (error)
goto bad;
if (SCARG(uap, valsize) > MCLBYTES) {
error = EINVAL;
goto bad;
}
if (SCARG(uap, val)) {
m = m_get(M_WAIT, MT_SOOPTS);
if (SCARG(uap, valsize) > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
error = ENOBUFS;
goto bad;
}
}
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
error = copyin(SCARG(uap, val), mtod(m, caddr_t),
SCARG(uap, valsize));
if (error) {
goto bad;
}
m->m_len = SCARG(uap, valsize);
}
so = fp->f_data;
solock(so);
error = sosetopt(so, SCARG(uap, level), SCARG(uap, name), m);
sounlock(so);
bad:
m_freem(m);
FRELE(fp, p);
return (error);
}
int
sys_getsockopt(struct proc *p, void *v, register_t *retval)
{
struct sys_getsockopt_args /* {
syscallarg(int) s;
syscallarg(int) level;
syscallarg(int) name;
syscallarg(void *) val;
syscallarg(socklen_t *) avalsize;
} */ *uap = v;
struct file *fp;
struct mbuf *m = NULL;
socklen_t valsize;
struct socket *so;
int error;
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
error = pledge_sockopt(p, 0, SCARG(uap, level), SCARG(uap, name));
if (error)
goto out;
if (SCARG(uap, val)) {
error = copyin(SCARG(uap, avalsize),
&valsize, sizeof (valsize));
if (error)
goto out;
} else
valsize = 0;
m = m_get(M_WAIT, MT_SOOPTS);
so = fp->f_data;
solock(so);
error = sogetopt(so, SCARG(uap, level), SCARG(uap, name), m);
sounlock(so);
if (error == 0 && SCARG(uap, val) && valsize && m != NULL) {
if (valsize > m->m_len)
valsize = m->m_len;
error = copyout(mtod(m, caddr_t), SCARG(uap, val), valsize);
if (error == 0)
error = copyout(&valsize,
SCARG(uap, avalsize), sizeof (valsize));
}
m_free(m);
out:
FRELE(fp, p);
return (error);
}
/*
* Get socket name.
*/
int
sys_getsockname(struct proc *p, void *v, register_t *retval)
{
struct sys_getsockname_args /* {
syscallarg(int) fdes;
syscallarg(struct sockaddr *) asa;
syscallarg(socklen_t *) alen;
} */ *uap = v;
struct file *fp;
struct socket *so;
struct mbuf *m = NULL;
socklen_t len;
int error;
if ((error = getsock(p, SCARG(uap, fdes), &fp)) != 0)
return (error);
error = copyin(SCARG(uap, alen), &len, sizeof (len));
if (error)
goto bad;
so = fp->f_data;
if (so->so_state & SS_YP) {
error = ENOTSOCK;
goto bad;
}
error = pledge_socket(p, -1, so->so_state);
if (error)
goto bad;
if (so->so_state & SS_YP) {
error = ENOTSOCK;
goto bad;
}
m = m_getclr(M_WAIT, MT_SONAME);
solock(so);
error = pru_sockaddr(so, m);
sounlock(so);
if (error)
goto bad;
error = copyaddrout(p, m, SCARG(uap, asa), len, SCARG(uap, alen));
bad:
FRELE(fp, p);
m_freem(m);
return (error);
}
/*
* Get name of peer for connected socket.
*/
int
sys_getpeername(struct proc *p, void *v, register_t *retval)
{
struct sys_getpeername_args /* {
syscallarg(int) fdes;
syscallarg(struct sockaddr *) asa;
syscallarg(socklen_t *) alen;
} */ *uap = v;
struct file *fp;
struct socket *so;
struct mbuf *m = NULL;
socklen_t len;
int error;
if ((error = getsock(p, SCARG(uap, fdes), &fp)) != 0)
return (error);
so = fp->f_data;
error = pledge_socket(p, -1, so->so_state);
if (error)
goto bad;
if (so->so_state & SS_YP) {
error = ENOTSOCK;
goto bad;
}
if ((so->so_state & SS_ISCONNECTED) == 0) {
error = ENOTCONN;
goto bad;
}
error = copyin(SCARG(uap, alen), &len, sizeof (len));
if (error)
goto bad;
m = m_getclr(M_WAIT, MT_SONAME);
solock(so);
error = pru_peeraddr(so, m);
sounlock(so);
if (error)
goto bad;
error = copyaddrout(p, m, SCARG(uap, asa), len, SCARG(uap, alen));
bad:
FRELE(fp, p);
m_freem(m);
return (error);
}
int
sockargs(struct mbuf **mp, const void *buf, size_t buflen, int type)
{
struct sockaddr *sa;
struct mbuf *m;
int error;
/*
* We can't allow socket names > UCHAR_MAX in length, since that
* will overflow sa_len. Also, control data more than MCLBYTES in
* length is just too much.
* Memory for sa_len and sa_family must exist.
*/
if ((buflen > (type == MT_SONAME ? UCHAR_MAX : MCLBYTES)) ||
(type == MT_SONAME && buflen < offsetof(struct sockaddr, sa_data)))
return (EINVAL);
/* Allocate an mbuf to hold the arguments. */
m = m_get(M_WAIT, type);
if (buflen > MLEN) {
MCLGET(m, M_WAITOK);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return ENOBUFS;
}
}
m->m_len = buflen;
error = copyin(buf, mtod(m, caddr_t), buflen);
if (error) {
(void) m_free(m);
return (error);
}
*mp = m;
if (type == MT_SONAME) {
sa = mtod(m, struct sockaddr *);
sa->sa_len = buflen;
}
return (0);
}
int
getsock(struct proc *p, int fdes, struct file **fpp)
{
struct file *fp;
fp = fd_getfile(p->p_fd, fdes);
if (fp == NULL)
return (EBADF);
if (fp->f_type != DTYPE_SOCKET) {
FRELE(fp, p);
return (ENOTSOCK);
}
*fpp = fp;
return (0);
}
int
sys_setrtable(struct proc *p, void *v, register_t *retval)
{
struct sys_setrtable_args /* {
syscallarg(int) rtableid;
} */ *uap = v;
u_int ps_rtableid = p->p_p->ps_rtableid;
int rtableid, error;
rtableid = SCARG(uap, rtableid);
if (ps_rtableid == rtableid)
return (0);
if (ps_rtableid != 0 && (error = suser(p)) != 0)
return (error);
if (rtableid < 0 || !rtable_exists((u_int)rtableid))
return (EINVAL);
p->p_p->ps_rtableid = (u_int)rtableid;
return (0);
}
int
sys_getrtable(struct proc *p, void *v, register_t *retval)
{
*retval = (int)p->p_p->ps_rtableid;
return (0);
}
int
copyaddrout(struct proc *p, struct mbuf *name, struct sockaddr *sa,
socklen_t buflen, socklen_t *outlen)
{
int error;
socklen_t namelen = name->m_len;
/* SHOULD COPY OUT A CHAIN HERE */
error = copyout(mtod(name, caddr_t), sa, MIN(buflen, namelen));
if (error == 0) {
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(name, caddr_t), namelen);
#endif
error = copyout(&namelen, outlen, sizeof(*outlen));
}
return (error);
}
#ifndef SMALL_KERNEL
int
ypsockargs(struct mbuf **mp, const void *buf, size_t buflen, int type)
{
struct sockaddr *sa;
struct mbuf *m;
/*
* We can't allow socket names > UCHAR_MAX in length, since that
* will overflow sa_len. Also, control data more than MCLBYTES in
* length is just too much.
* Memory for sa_len and sa_family must exist.
*/
if ((buflen > (type == MT_SONAME ? UCHAR_MAX : MCLBYTES)) ||
(type == MT_SONAME && buflen < offsetof(struct sockaddr, sa_data)))
return (EINVAL);
/* Allocate an mbuf to hold the arguments. */
m = m_get(M_WAIT, type);
if (buflen > MLEN) {
MCLGET(m, M_WAITOK);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return ENOBUFS;
}
}
m->m_len = buflen;
bcopy(buf, mtod(m, caddr_t), buflen);
*mp = m;
if (type == MT_SONAME) {
sa = mtod(m, struct sockaddr *);
sa->sa_len = buflen;
}
return (0);
}
#endif /* SMALL_KERNEL */
int
sys_ypconnect(struct proc *p, void *v, register_t *retval)
{
#ifdef SMALL_KERNEL
return EAFNOSUPPORT;
#else
struct sys_ypconnect_args /* {
syscallarg(int) type;
} */ *uap = v;
struct nameidata nid;
struct vattr va;
struct uio uio;
struct iovec iov;
struct filedesc *fdp = p->p_fd;
struct socket *so;
struct file *fp;
struct flock fl;
char *name;
struct mbuf *nam = NULL;
int error, fd = -1;
struct ypbinding {
u_short ypbind_port;
int status;
in_addr_t in;
u_short ypserv_udp_port;
u_short garbage;
u_short ypserv_tcp_port;
} __packed data;
struct sockaddr_in ypsin;
if (!domainname[0] || strchr(domainname, '/'))
return EAFNOSUPPORT;
switch (SCARG(uap, type)) {
case SOCK_STREAM:
case SOCK_DGRAM:
break;
default:
return EAFNOSUPPORT;
}
if (p->p_p->ps_flags & PS_CHROOT)
return EACCES;
name = pool_get(&namei_pool, PR_WAITOK);
snprintf(name, MAXPATHLEN, "/var/yp/binding/%s.2", domainname);
NDINIT(&nid, 0, NOFOLLOW|LOCKLEAF|KERNELPATH, UIO_SYSSPACE, name, p);
nid.ni_pledge = PLEDGE_RPATH;
KERNEL_LOCK();
error = namei(&nid);
pool_put(&namei_pool, name);
if (error)
goto out;
error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p);
if (error)
goto verror;
if (nid.ni_vp->v_type != VREG || va.va_size != sizeof data) {
error = EFTYPE;
goto verror;
}
/*
* Check that a lock is held on the file (hopefully by ypbind),
* otherwise the file might be old
*/
fl.l_start = 0;
fl.l_len = 0;
fl.l_pid = 0;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
error = VOP_ADVLOCK(nid.ni_vp, fdp, F_GETLK, &fl, F_POSIX);
if (error)
goto verror;
if (fl.l_type == F_UNLCK) {
error = EOWNERDEAD;
goto verror;
}
iov.iov_base = &data;
iov.iov_len = sizeof data;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = 0;
uio.uio_resid = iov.iov_len;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = p;
error = VOP_READ(nid.ni_vp, &uio, 0, p->p_ucred);
if (error) {
verror:
if (nid.ni_vp)
vput(nid.ni_vp);
out:
KERNEL_UNLOCK();
return (error);
}
vput(nid.ni_vp);
KERNEL_UNLOCK();
bzero(&ypsin, sizeof ypsin);
ypsin.sin_len = sizeof ypsin;
ypsin.sin_family = AF_INET;
if (SCARG(uap, type) == SOCK_STREAM)
ypsin.sin_port = data.ypserv_tcp_port;
else
ypsin.sin_port = data.ypserv_udp_port;
if (ntohs(ypsin.sin_port) >= IPPORT_RESERVED || ntohs(ypsin.sin_port) == 20)
return EPERM;
memcpy(&ypsin.sin_addr.s_addr, &data.in, sizeof ypsin.sin_addr.s_addr);
error = socreate(AF_INET, &so, SCARG(uap, type), 0);
if (error)
return (error);
error = ypsockargs(&nam, &ypsin, sizeof ypsin, MT_SONAME);
if (error) {
soclose(so, MSG_DONTWAIT);
return (error);
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(nam, caddr_t), sizeof(struct sockaddr_in));
#endif
solock(so);
error = soconnect(so, nam);
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
error = sosleep_nsec(so, &so->so_timeo, PSOCK | PCATCH,
"netcon2", INFSLP);
if (error)
break;
}
m_freem(nam);
so->so_state |= SS_YP; /* impose some restrictions */
sounlock(so);
if (error) {
soclose(so, MSG_DONTWAIT);
return (error);
}
fdplock(fdp);
error = falloc(p, &fp, &fd);
if (error) {
fdpunlock(fdp);
soclose(so, MSG_DONTWAIT);
return (error);
}
fp->f_flag = FREAD | FWRITE | FNONBLOCK;
fp->f_type = DTYPE_SOCKET;
fp->f_ops = &socketops;
fp->f_data = so;
fdinsert(fdp, fd, UF_EXCLOSE, fp);
fdpunlock(fdp);
FRELE(fp, p);
*retval = fd;
return (error);
#endif /* SMALL_KERNEL */
}
14
14
3
3
3
14
14
14
14
24
23
27
27
3
3
3
14
14
14
14
17
14
14
14
14
14
6
8
14
13
1
6
8
5
9
2
898
899
78
78
78
21
15
6
12
9
100
67
33
2
2
2
657
286
376
2
653
1
96
98
86
24
14
24
24
17
17
17
17
17
17
17
17
63
51
6
27
8
18
5
13
89
89
15
28
55
55
30
19
12
3
5
3
30
30
63
42
19
51
49
58
10
10
6
10
1
4
3
3
2
1
2
19
19
61
61
3
56
76
6
14
2
87
6
488
31
48
437
520
129
438
1
8
1
3
4
7
2
6
2
1
2
1
440
62
3
52
2
23
3
238
1
1
1
1
1
2
1
4
1
14
14
8
6
14
10
4
12
2
2
10
13
4
4
1
4
4
2
1
1
1
1
1
2
1
1
1
1
2
2
1
1
1
1
8
1
3
2
1
2
1
1
1
1
1
1
1
1
3
47
215
55
11
293
3
6
294
3
63
21
2
4
1
1
1
1
1
1
1
1
1
1
1
21
5
5
5
17
5
9
11
15
3
4
8
7
14
14
83
59
23
83
83
9
23
1
1
20
1
12
16
20
23
6
17
17
8
1
2
1
2
4
4
1
2
1
4
1
1
1
2
2
2
51
27
3
39
4
4
4
4
4
5
5
1
2
1
4
3
2
27
42
1
1
1
1
1
1
1
14
17
85
1
1
1
93
42
155
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
/* $OpenBSD: if.c,v 1.664 2022/09/02 13:12:31 mvs Exp $ */
/* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1980, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)if.c 8.3 (Berkeley) 1/4/94
*/
#include "bpfilter.h"
#include "bridge.h"
#include "carp.h"
#include "ether.h"
#include "pf.h"
#include "pfsync.h"
#include "ppp.h"
#include "pppoe.h"
#include "if_wg.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/timeout.h>
#include <sys/protosw.h>
#include <sys/kernel.h>
#include <sys/ioctl.h>
#include <sys/domain.h>
#include <sys/task.h>
#include <sys/atomic.h>
#include <sys/percpu.h>
#include <sys/proc.h>
#include <sys/stdint.h> /* uintptr_t */
#include <sys/rwlock.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/igmp.h>
#ifdef MROUTING
#include <netinet/ip_mroute.h>
#endif
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet6/in6_ifattach.h>
#include <netinet6/nd6.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
#ifdef MPLS
#include <netmpls/mpls.h>
#endif
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#if NBRIDGE > 0
#include <net/if_bridge.h>
#endif
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <sys/device.h>
void if_attachsetup(struct ifnet *);
void if_attachdomain(struct ifnet *);
void if_attach_common(struct ifnet *);
void if_remove(struct ifnet *);
int if_createrdomain(int, struct ifnet *);
int if_setrdomain(struct ifnet *, int);
void if_slowtimo(void *);
void if_detached_qstart(struct ifqueue *);
int if_detached_ioctl(struct ifnet *, u_long, caddr_t);
int ifioctl_get(u_long, caddr_t);
int ifconf(caddr_t);
static int
if_sffpage_check(const caddr_t);
int if_getgroup(caddr_t, struct ifnet *);
int if_getgroupmembers(caddr_t);
int if_getgroupattribs(caddr_t);
int if_setgroupattribs(caddr_t);
int if_getgrouplist(caddr_t);
void if_linkstate(struct ifnet *);
void if_linkstate_task(void *);
int if_clone_list(struct if_clonereq *);
struct if_clone *if_clone_lookup(const char *, int *);
int if_group_egress_build(void);
void if_watchdog_task(void *);
void if_netisr(void *);
#ifdef DDB
void ifa_print_all(void);
#endif
void if_qstart_compat(struct ifqueue *);
/*
* interface index map
*
* the kernel maintains a mapping of interface indexes to struct ifnet
* pointers.
*
* the map is an array of struct ifnet pointers prefixed by an if_map
* structure. the if_map structure stores the length of its array.
*
* as interfaces are attached to the system, the map is grown on demand
* up to USHRT_MAX entries.
*
* interface index 0 is reserved and represents no interface. this
* supports the use of the interface index as the scope for IPv6 link
* local addresses, where scope 0 means no scope has been specified.
* it also supports the use of interface index as the unique identifier
* for network interfaces in SNMP applications as per RFC2863. therefore
* if_get(0) returns NULL.
*/
void if_ifp_dtor(void *, void *);
void if_map_dtor(void *, void *);
struct ifnet *if_ref(struct ifnet *);
/*
* struct if_map
*
* bounded array of ifnet srp pointers used to fetch references of live
* interfaces with if_get().
*/
struct if_map {
unsigned long limit;
/* followed by limit ifnet srp pointers */
};
/*
* struct if_idxmap
*
* infrastructure to manage updates and accesses to the current if_map.
*/
struct if_idxmap {
unsigned int serial;
unsigned int count;
struct srp map;
struct rwlock lock;
unsigned char *usedidx; /* bitmap of indices in use */
};
void if_idxmap_init(unsigned int);
void if_idxmap_alloc(struct ifnet *);
void if_idxmap_insert(struct ifnet *);
void if_idxmap_remove(struct ifnet *);
TAILQ_HEAD(, ifg_group) ifg_head = TAILQ_HEAD_INITIALIZER(ifg_head);
LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
int if_cloners_count;
struct rwlock if_cloners_lock = RWLOCK_INITIALIZER("clonelk");
/* hooks should only be added, deleted, and run from a process context */
struct mutex if_hooks_mtx = MUTEX_INITIALIZER(IPL_NONE);
void if_hooks_run(struct task_list *);
int ifq_congestion;
int netisr;
#define NET_TASKQ 4
struct taskq *nettqmp[NET_TASKQ];
struct task if_input_task_locked = TASK_INITIALIZER(if_netisr, NULL);
/*
* Serialize socket operations to ensure no new sleeping points
* are introduced in IP output paths.
*/
struct rwlock netlock = RWLOCK_INITIALIZER("netlock");
/*
* Network interface utility routines.
*/
void
ifinit(void)
{
unsigned int i;
/*
* most machines boot with 4 or 5 interfaces, so size the initial map
* to accommodate this
*/
if_idxmap_init(8);
for (i = 0; i < NET_TASKQ; i++) {
nettqmp[i] = taskq_create("softnet", 1, IPL_NET, TASKQ_MPSAFE);
if (nettqmp[i] == NULL)
panic("unable to create network taskq %d", i);
}
}
static struct if_idxmap if_idxmap = {
0,
0,
SRP_INITIALIZER(),
RWLOCK_INITIALIZER("idxmaplk"),
NULL,
};
struct srp_gc if_ifp_gc = SRP_GC_INITIALIZER(if_ifp_dtor, NULL);
struct srp_gc if_map_gc = SRP_GC_INITIALIZER(if_map_dtor, NULL);
struct ifnet_head ifnet = TAILQ_HEAD_INITIALIZER(ifnet);
void
if_idxmap_init(unsigned int limit)
{
struct if_map *if_map;
struct srp *map;
unsigned int i;
if_idxmap.serial = 1; /* skip ifidx 0 so it can return NULL */
if_map = malloc(sizeof(*if_map) + limit * sizeof(*map),
M_IFADDR, M_WAITOK);
if_map->limit = limit;
map = (struct srp *)(if_map + 1);
for (i = 0; i < limit; i++)
srp_init(&map[i]);
if_idxmap.usedidx = malloc(howmany(limit, NBBY),
M_IFADDR, M_WAITOK | M_ZERO);
/* this is called early so there's nothing to race with */
srp_update_locked(&if_map_gc, &if_idxmap.map, if_map);
}
void
if_idxmap_alloc(struct ifnet *ifp)
{
struct if_map *if_map;
struct srp *map;
unsigned int index, i;
refcnt_init(&ifp->if_refcnt);
rw_enter_write(&if_idxmap.lock);
if (++if_idxmap.count >= USHRT_MAX)
panic("too many interfaces");
if_map = srp_get_locked(&if_idxmap.map);
map = (struct srp *)(if_map + 1);
index = if_idxmap.serial++ & USHRT_MAX;
if (index >= if_map->limit) {
struct if_map *nif_map;
struct srp *nmap;
unsigned int nlimit;
struct ifnet *nifp;
unsigned char *nusedidx;
nlimit = if_map->limit * 2;
nif_map = malloc(sizeof(*nif_map) + nlimit * sizeof(*nmap),
M_IFADDR, M_WAITOK);
nmap = (struct srp *)(nif_map + 1);
nif_map->limit = nlimit;
for (i = 0; i < if_map->limit; i++) {
srp_init(&nmap[i]);
nifp = srp_get_locked(&map[i]);
if (nifp != NULL) {
srp_update_locked(&if_ifp_gc, &nmap[i],
if_ref(nifp));
}
}
while (i < nlimit) {
srp_init(&nmap[i]);
i++;
}
nusedidx = malloc(howmany(nlimit, NBBY),
M_IFADDR, M_WAITOK | M_ZERO);
memcpy(nusedidx, if_idxmap.usedidx,
howmany(if_map->limit, NBBY));
free(if_idxmap.usedidx, M_IFADDR,
howmany(if_map->limit, NBBY));
if_idxmap.usedidx = nusedidx;
srp_update_locked(&if_map_gc, &if_idxmap.map, nif_map);
if_map = nif_map;
map = nmap;
}
/* pick the next free index */
for (i = 0; i < USHRT_MAX; i++) {
if (index != 0 && isclr(if_idxmap.usedidx, index))
break;
index = if_idxmap.serial++ & USHRT_MAX;
}
KASSERT(index != 0 && index < if_map->limit);
KASSERT(isclr(if_idxmap.usedidx, index));
setbit(if_idxmap.usedidx, index);
ifp->if_index = index;
rw_exit_write(&if_idxmap.lock);
}
void
if_idxmap_insert(struct ifnet *ifp)
{
struct if_map *if_map;
struct srp *map;
unsigned int index = ifp->if_index;
rw_enter_write(&if_idxmap.lock);
if_map = srp_get_locked(&if_idxmap.map);
map = (struct srp *)(if_map + 1);
KASSERT(index != 0 && index < if_map->limit);
KASSERT(isset(if_idxmap.usedidx, index));
/* commit */
srp_update_locked(&if_ifp_gc, &map[index], if_ref(ifp));
rw_exit_write(&if_idxmap.lock);
}
void
if_idxmap_remove(struct ifnet *ifp)
{
struct if_map *if_map;
struct srp *map;
unsigned int index;
index = ifp->if_index;
rw_enter_write(&if_idxmap.lock);
if_map = srp_get_locked(&if_idxmap.map);
KASSERT(index < if_map->limit);
map = (struct srp *)(if_map + 1);
KASSERT(ifp == (struct ifnet *)srp_get_locked(&map[index]));
srp_update_locked(&if_ifp_gc, &map[index], NULL);
if_idxmap.count--;
KASSERT(isset(if_idxmap.usedidx, index));
clrbit(if_idxmap.usedidx, index);
/* end of if_idxmap modifications */
rw_exit_write(&if_idxmap.lock);
}
void
if_ifp_dtor(void *null, void *ifp)
{
if_put(ifp);
}
void
if_map_dtor(void *null, void *m)
{
struct if_map *if_map = m;
struct srp *map = (struct srp *)(if_map + 1);
unsigned int i;
/*
* dont need to serialize the use of update_locked since this is
* the last reference to this map. there's nothing to race against.
*/
for (i = 0; i < if_map->limit; i++)
srp_update_locked(&if_ifp_gc, &map[i], NULL);
free(if_map, M_IFADDR, sizeof(*if_map) + if_map->limit * sizeof(*map));
}
/*
* Attach an interface to the
* list of "active" interfaces.
*/
void
if_attachsetup(struct ifnet *ifp)
{
unsigned long ifidx;
NET_ASSERT_LOCKED();
if_addgroup(ifp, IFG_ALL);
if_attachdomain(ifp);
#if NPF > 0
pfi_attach_ifnet(ifp);
#endif
timeout_set(&ifp->if_slowtimo, if_slowtimo, ifp);
if_slowtimo(ifp);
if_idxmap_insert(ifp);
KASSERT(if_get(0) == NULL);
ifidx = ifp->if_index;
task_set(&ifp->if_watchdogtask, if_watchdog_task, (void *)ifidx);
task_set(&ifp->if_linkstatetask, if_linkstate_task, (void *)ifidx);
/* Announce the interface. */
rtm_ifannounce(ifp, IFAN_ARRIVAL);
}
/*
* Allocate the link level name for the specified interface. This
* is an attachment helper. It must be called after ifp->if_addrlen
* is initialized, which may not be the case when if_attach() is
* called.
*/
void
if_alloc_sadl(struct ifnet *ifp)
{
unsigned int socksize;
int namelen, masklen;
struct sockaddr_dl *sdl;
/*
* If the interface already has a link name, release it
* now. This is useful for interfaces that can change
* link types, and thus switch link names often.
*/
if_free_sadl(ifp);
namelen = strlen(ifp->if_xname);
masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
socksize = masklen + ifp->if_addrlen;
#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
if (socksize < sizeof(*sdl))
socksize = sizeof(*sdl);
socksize = ROUNDUP(socksize);
sdl = malloc(socksize, M_IFADDR, M_WAITOK|M_ZERO);
sdl->sdl_len = socksize;
sdl->sdl_family = AF_LINK;
bcopy(ifp->if_xname, sdl->sdl_data, namelen);
sdl->sdl_nlen = namelen;
sdl->sdl_alen = ifp->if_addrlen;
sdl->sdl_index = ifp->if_index;
sdl->sdl_type = ifp->if_type;
ifp->if_sadl = sdl;
}
/*
* Free the link level name for the specified interface. This is
* a detach helper. This is called from if_detach() or from
* link layer type specific detach functions.
*/
void
if_free_sadl(struct ifnet *ifp)
{
if (ifp->if_sadl == NULL)
return;
free(ifp->if_sadl, M_IFADDR, ifp->if_sadl->sdl_len);
ifp->if_sadl = NULL;
}
void
if_attachdomain(struct ifnet *ifp)
{
const struct domain *dp;
int i, s;
s = splnet();
/* address family dependent data region */
bzero(ifp->if_afdata, sizeof(ifp->if_afdata));
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_ifattach)
ifp->if_afdata[dp->dom_family] =
(*dp->dom_ifattach)(ifp);
}
splx(s);
}
void
if_attachhead(struct ifnet *ifp)
{
if_attach_common(ifp);
NET_LOCK();
TAILQ_INSERT_HEAD(&ifnet, ifp, if_list);
if_attachsetup(ifp);
NET_UNLOCK();
}
void
if_attach(struct ifnet *ifp)
{
if_attach_common(ifp);
NET_LOCK();
TAILQ_INSERT_TAIL(&ifnet, ifp, if_list);
if_attachsetup(ifp);
NET_UNLOCK();
}
void
if_attach_queues(struct ifnet *ifp, unsigned int nqs)
{
struct ifqueue **map;
struct ifqueue *ifq;
int i;
KASSERT(ifp->if_ifqs == ifp->if_snd.ifq_ifqs);
KASSERT(nqs != 0);
map = mallocarray(sizeof(*map), nqs, M_DEVBUF, M_WAITOK);
ifp->if_snd.ifq_softc = NULL;
map[0] = &ifp->if_snd;
for (i = 1; i < nqs; i++) {
ifq = malloc(sizeof(*ifq), M_DEVBUF, M_WAITOK|M_ZERO);
ifq_set_maxlen(ifq, ifp->if_snd.ifq_maxlen);
ifq_init(ifq, ifp, i);
map[i] = ifq;
}
ifp->if_ifqs = map;
ifp->if_nifqs = nqs;
}
void
if_attach_iqueues(struct ifnet *ifp, unsigned int niqs)
{
struct ifiqueue **map;
struct ifiqueue *ifiq;
unsigned int i;
KASSERT(niqs != 0);
map = mallocarray(niqs, sizeof(*map), M_DEVBUF, M_WAITOK);
ifp->if_rcv.ifiq_softc = NULL;
map[0] = &ifp->if_rcv;
for (i = 1; i < niqs; i++) {
ifiq = malloc(sizeof(*ifiq), M_DEVBUF, M_WAITOK|M_ZERO);
ifiq_init(ifiq, ifp, i);
map[i] = ifiq;
}
ifp->if_iqs = map;
ifp->if_niqs = niqs;
}
void
if_attach_common(struct ifnet *ifp)
{
KASSERT(ifp->if_ioctl != NULL);
TAILQ_INIT(&ifp->if_addrlist);
TAILQ_INIT(&ifp->if_maddrlist);
TAILQ_INIT(&ifp->if_groups);
if (!ISSET(ifp->if_xflags, IFXF_MPSAFE)) {
KASSERTMSG(ifp->if_qstart == NULL,
"%s: if_qstart set without MPSAFE set", ifp->if_xname);
ifp->if_qstart = if_qstart_compat;
} else {
KASSERTMSG(ifp->if_start == NULL,
"%s: if_start set with MPSAFE set", ifp->if_xname);
KASSERTMSG(ifp->if_qstart != NULL,
"%s: if_qstart not set with MPSAFE set", ifp->if_xname);
}
if_idxmap_alloc(ifp);
ifq_init(&ifp->if_snd, ifp, 0);
ifp->if_snd.ifq_ifqs[0] = &ifp->if_snd;
ifp->if_ifqs = ifp->if_snd.ifq_ifqs;
ifp->if_nifqs = 1;
if (ifp->if_txmit == 0)
ifp->if_txmit = IF_TXMIT_DEFAULT;
ifiq_init(&ifp->if_rcv, ifp, 0);
ifp->if_rcv.ifiq_ifiqs[0] = &ifp->if_rcv;
ifp->if_iqs = ifp->if_rcv.ifiq_ifiqs;
ifp->if_niqs = 1;
TAILQ_INIT(&ifp->if_addrhooks);
TAILQ_INIT(&ifp->if_linkstatehooks);
TAILQ_INIT(&ifp->if_detachhooks);
if (ifp->if_rtrequest == NULL)
ifp->if_rtrequest = if_rtrequest_dummy;
if (ifp->if_enqueue == NULL)
ifp->if_enqueue = if_enqueue_ifq;
#if NBPFILTER > 0
if (ifp->if_bpf_mtap == NULL)
ifp->if_bpf_mtap = bpf_mtap_ether;
#endif
ifp->if_llprio = IFQ_DEFPRIO;
}
void
if_attach_ifq(struct ifnet *ifp, const struct ifq_ops *newops, void *args)
{
/*
* only switch the ifq_ops on the first ifq on an interface.
*
* the only ifq_ops we provide priq and hfsc, and hfsc only
* works on a single ifq. because the code uses the ifq_ops
* on the first ifq (if_snd) to select a queue for an mbuf,
* by switching only the first one we change both the algorithm
* and force the routing of all new packets to it.
*/
ifq_attach(&ifp->if_snd, newops, args);
}
void
if_start(struct ifnet *ifp)
{
KASSERT(ifp->if_qstart == if_qstart_compat);
if_qstart_compat(&ifp->if_snd);
}
void
if_qstart_compat(struct ifqueue *ifq)
{
struct ifnet *ifp = ifq->ifq_if;
int s;
/*
* the stack assumes that an interface can have multiple
* transmit rings, but a lot of drivers are still written
* so that interfaces and send rings have a 1:1 mapping.
* this provides compatibility between the stack and the older
* drivers by translating from the only queue they have
* (ifp->if_snd) back to the interface and calling if_start.
*/
KERNEL_LOCK();
s = splnet();
(*ifp->if_start)(ifp);
splx(s);
KERNEL_UNLOCK();
}
int
if_enqueue(struct ifnet *ifp, struct mbuf *m)
{
CLR(m->m_pkthdr.csum_flags, M_TIMESTAMP);
#if NPF > 0
if (m->m_pkthdr.pf.delay > 0)
return (pf_delay_pkt(m, ifp->if_index));
#endif
#if NBRIDGE > 0
if (ifp->if_bridgeidx && (m->m_flags & M_PROTO1) == 0) {
int error;
error = bridge_enqueue(ifp, m);
return (error);
}
#endif
#if NPF > 0
pf_pkt_addr_changed(m);
#endif /* NPF > 0 */
return ((*ifp->if_enqueue)(ifp, m));
}
int
if_enqueue_ifq(struct ifnet *ifp, struct mbuf *m)
{
struct ifqueue *ifq = &ifp->if_snd;
int error;
if (ifp->if_nifqs > 1) {
unsigned int idx;
/*
* use the operations on the first ifq to pick which of
* the array gets this mbuf.
*/
idx = ifq_idx(&ifp->if_snd, ifp->if_nifqs, m);
ifq = ifp->if_ifqs[idx];
}
error = ifq_enqueue(ifq, m);
if (error)
return (error);
ifq_start(ifq);
return (0);
}
void
if_input(struct ifnet *ifp, struct mbuf_list *ml)
{
ifiq_input(&ifp->if_rcv, ml);
}
int
if_input_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af)
{
int keepflags;
#if NBPFILTER > 0
/*
* Only send packets to bpf if they are destined to local
* addresses.
*
* if_input_local() is also called for SIMPLEX interfaces to
* duplicate packets for local use. But don't dup them to bpf.
*/
if (ifp->if_flags & IFF_LOOPBACK) {
caddr_t if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_af(if_bpf, af, m, BPF_DIRECTION_OUT);
}
#endif
keepflags = m->m_flags & (M_BCAST|M_MCAST);
m_resethdr(m);
m->m_flags |= M_LOOP | keepflags;
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
ifp->if_opackets++;
ifp->if_obytes += m->m_pkthdr.len;
ifp->if_ipackets++;
ifp->if_ibytes += m->m_pkthdr.len;
switch (af) {
case AF_INET:
ipv4_input(ifp, m);
break;
#ifdef INET6
case AF_INET6:
ipv6_input(ifp, m);
break;
#endif /* INET6 */
#ifdef MPLS
case AF_MPLS:
mpls_input(ifp, m);
break;
#endif /* MPLS */
default:
printf("%s: can't handle af%d\n", ifp->if_xname, af);
m_freem(m);
return (EAFNOSUPPORT);
}
return (0);
}
int
if_output_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af)
{
struct ifiqueue *ifiq;
unsigned int flow = 0;
m->m_pkthdr.ph_family = af;
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
flow = m->m_pkthdr.ph_flowid;
ifiq = ifp->if_iqs[flow % ifp->if_niqs];
return (ifiq_enqueue(ifiq, m) == 0 ? 0 : ENOBUFS);
}
void
if_input_process(struct ifnet *ifp, struct mbuf_list *ml)
{
struct mbuf *m;
if (ml_empty(ml))
return;
if (!ISSET(ifp->if_xflags, IFXF_CLONED))
enqueue_randomness(ml_len(ml) ^ (uintptr_t)MBUF_LIST_FIRST(ml));
/*
* We grab the shared netlock for packet processing in the softnet
* threads. Packets can regrab the exclusive lock via queues.
* ioctl, sysctl, and socket syscall may use shared lock if access is
* read only or MP safe. Usually they hold the exclusive net lock.
*/
NET_LOCK_SHARED();
while ((m = ml_dequeue(ml)) != NULL)
(*ifp->if_input)(ifp, m);
NET_UNLOCK_SHARED();
}
void
if_vinput(struct ifnet *ifp, struct mbuf *m)
{
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
counters_pkt(ifp->if_counters,
ifc_ipackets, ifc_ibytes, m->m_pkthdr.len);
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf) {
if ((*ifp->if_bpf_mtap)(if_bpf, m, BPF_DIRECTION_IN)) {
m_freem(m);
return;
}
}
#endif
if (__predict_true(!ISSET(ifp->if_xflags, IFXF_MONITOR)))
(*ifp->if_input)(ifp, m);
else
m_freem(m);
}
void
if_netisr(void *unused)
{
int n, t = 0;
NET_LOCK();
while ((n = netisr) != 0) {
/* Like sched_pause() but with a rwlock dance. */
if (curcpu()->ci_schedstate.spc_schedflags & SPCF_SHOULDYIELD) {
NET_UNLOCK();
yield();
NET_LOCK();
}
atomic_clearbits_int(&netisr, n);
#if NETHER > 0
if (n & (1 << NETISR_ARP)) {
KERNEL_LOCK();
arpintr();
KERNEL_UNLOCK();
}
#endif
if (n & (1 << NETISR_IP))
ipintr();
#ifdef INET6
if (n & (1 << NETISR_IPV6))
ip6intr();
#endif
#if NPPP > 0
if (n & (1 << NETISR_PPP)) {
KERNEL_LOCK();
pppintr();
KERNEL_UNLOCK();
}
#endif
#if NBRIDGE > 0
if (n & (1 << NETISR_BRIDGE))
bridgeintr();
#endif
#ifdef PIPEX
if (n & (1 << NETISR_PIPEX))
pipexintr();
#endif
#if NPPPOE > 0
if (n & (1 << NETISR_PPPOE)) {
KERNEL_LOCK();
pppoeintr();
KERNEL_UNLOCK();
}
#endif
t |= n;
}
#if NPFSYNC > 0
if (t & (1 << NETISR_PFSYNC)) {
KERNEL_LOCK();
pfsyncintr();
KERNEL_UNLOCK();
}
#endif
NET_UNLOCK();
}
void
if_hooks_run(struct task_list *hooks)
{
struct task *t, *nt;
struct task cursor = { .t_func = NULL };
void (*func)(void *);
void *arg;
mtx_enter(&if_hooks_mtx);
for (t = TAILQ_FIRST(hooks); t != NULL; t = nt) {
if (t->t_func == NULL) { /* skip cursors */
nt = TAILQ_NEXT(t, t_entry);
continue;
}
func = t->t_func;
arg = t->t_arg;
TAILQ_INSERT_AFTER(hooks, t, &cursor, t_entry);
mtx_leave(&if_hooks_mtx);
(*func)(arg);
mtx_enter(&if_hooks_mtx);
nt = TAILQ_NEXT(&cursor, t_entry); /* avoid _Q_INVALIDATE */
TAILQ_REMOVE(hooks, &cursor, t_entry);
}
mtx_leave(&if_hooks_mtx);
}
void
if_remove(struct ifnet *ifp)
{
/* Remove the interface from the list of all interfaces. */
NET_LOCK();
TAILQ_REMOVE(&ifnet, ifp, if_list);
NET_UNLOCK();
/* Remove the interface from the interface index map. */
if_idxmap_remove(ifp);
/* Sleep until the last reference is released. */
refcnt_finalize(&ifp->if_refcnt, "ifrm");
}
void
if_deactivate(struct ifnet *ifp)
{
/*
* Call detach hooks from head to tail. To make sure detach
* hooks are executed in the reverse order they were added, all
* the hooks have to be added to the head!
*/
NET_LOCK();
if_hooks_run(&ifp->if_detachhooks);
NET_UNLOCK();
}
void
if_detachhook_add(struct ifnet *ifp, struct task *t)
{
mtx_enter(&if_hooks_mtx);
TAILQ_INSERT_HEAD(&ifp->if_detachhooks, t, t_entry);
mtx_leave(&if_hooks_mtx);
}
void
if_detachhook_del(struct ifnet *ifp, struct task *t)
{
mtx_enter(&if_hooks_mtx);
TAILQ_REMOVE(&ifp->if_detachhooks, t, t_entry);
mtx_leave(&if_hooks_mtx);
}
/*
* Detach an interface from everything in the kernel. Also deallocate
* private resources.
*/
void
if_detach(struct ifnet *ifp)
{
struct ifaddr *ifa;
struct ifg_list *ifg;
const struct domain *dp;
int i, s;
/* Undo pseudo-driver changes. */
if_deactivate(ifp);
/* Other CPUs must not have a reference before we start destroying. */
if_remove(ifp);
ifp->if_qstart = if_detached_qstart;
/* Wait until the start routines finished. */
ifq_barrier(&ifp->if_snd);
ifq_clr_oactive(&ifp->if_snd);
#if NBPFILTER > 0
bpfdetach(ifp);
#endif
NET_LOCK();
s = splnet();
ifp->if_ioctl = if_detached_ioctl;
ifp->if_watchdog = NULL;
/* Remove the watchdog timeout & task */
timeout_del(&ifp->if_slowtimo);
task_del(net_tq(ifp->if_index), &ifp->if_watchdogtask);
/* Remove the link state task */
task_del(net_tq(ifp->if_index), &ifp->if_linkstatetask);
rti_delete(ifp);
#if NETHER > 0 && defined(NFSCLIENT)
if (ifp->if_index == revarp_ifidx)
revarp_ifidx = 0;
#endif
#ifdef MROUTING
vif_delete(ifp);
#endif
in_ifdetach(ifp);
#ifdef INET6
in6_ifdetach(ifp);
#endif
#if NPF > 0
pfi_detach_ifnet(ifp);
#endif
while ((ifg = TAILQ_FIRST(&ifp->if_groups)) != NULL)
if_delgroup(ifp, ifg->ifgl_group->ifg_group);
if_free_sadl(ifp);
/* We should not have any address left at this point. */
if (!TAILQ_EMPTY(&ifp->if_addrlist)) {
#ifdef DIAGNOSTIC
printf("%s: address list non empty\n", ifp->if_xname);
#endif
while ((ifa = TAILQ_FIRST(&ifp->if_addrlist)) != NULL) {
ifa_del(ifp, ifa);
ifa->ifa_ifp = NULL;
ifafree(ifa);
}
}
KASSERT(TAILQ_EMPTY(&ifp->if_addrhooks));
KASSERT(TAILQ_EMPTY(&ifp->if_linkstatehooks));
KASSERT(TAILQ_EMPTY(&ifp->if_detachhooks));
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_ifdetach && ifp->if_afdata[dp->dom_family])
(*dp->dom_ifdetach)(ifp,
ifp->if_afdata[dp->dom_family]);
}
/* Announce that the interface is gone. */
rtm_ifannounce(ifp, IFAN_DEPARTURE);
splx(s);
NET_UNLOCK();
if (ifp->if_counters != NULL)
if_counters_free(ifp);
for (i = 0; i < ifp->if_nifqs; i++)
ifq_destroy(ifp->if_ifqs[i]);
if (ifp->if_ifqs != ifp->if_snd.ifq_ifqs) {
for (i = 1; i < ifp->if_nifqs; i++) {
free(ifp->if_ifqs[i], M_DEVBUF,
sizeof(struct ifqueue));
}
free(ifp->if_ifqs, M_DEVBUF,
sizeof(struct ifqueue *) * ifp->if_nifqs);
}
for (i = 0; i < ifp->if_niqs; i++)
ifiq_destroy(ifp->if_iqs[i]);
if (ifp->if_iqs != ifp->if_rcv.ifiq_ifiqs) {
for (i = 1; i < ifp->if_niqs; i++) {
free(ifp->if_iqs[i], M_DEVBUF,
sizeof(struct ifiqueue));
}
free(ifp->if_iqs, M_DEVBUF,
sizeof(struct ifiqueue *) * ifp->if_niqs);
}
}
/*
* Returns true if ``ifp0'' is connected to the interface with index ``ifidx''.
*/
int
if_isconnected(const struct ifnet *ifp0, unsigned int ifidx)
{
struct ifnet *ifp;
int connected = 0;
ifp = if_get(ifidx);
if (ifp == NULL)
return (0);
if (ifp0->if_index == ifp->if_index)
connected = 1;
#if NBRIDGE > 0
if (ifp0->if_bridgeidx != 0 && ifp0->if_bridgeidx == ifp->if_bridgeidx)
connected = 1;
#endif
#if NCARP > 0
if ((ifp0->if_type == IFT_CARP &&
ifp0->if_carpdevidx == ifp->if_index) ||
(ifp->if_type == IFT_CARP && ifp->if_carpdevidx == ifp0->if_index))
connected = 1;
#endif
if_put(ifp);
return (connected);
}
/*
* Create a clone network interface.
*/
int
if_clone_create(const char *name, int rdomain)
{
struct if_clone *ifc;
struct ifnet *ifp;
int unit, ret;
ifc = if_clone_lookup(name, &unit);
if (ifc == NULL)
return (EINVAL);
rw_enter_write(&if_cloners_lock);
if ((ifp = if_unit(name)) != NULL) {
ret = EEXIST;
goto unlock;
}
ret = (*ifc->ifc_create)(ifc, unit);
if (ret != 0 || (ifp = if_unit(name)) == NULL)
goto unlock;
NET_LOCK();
if_addgroup(ifp, ifc->ifc_name);
if (rdomain != 0)
if_setrdomain(ifp, rdomain);
NET_UNLOCK();
unlock:
rw_exit_write(&if_cloners_lock);
if_put(ifp);
return (ret);
}
/*
* Destroy a clone network interface.
*/
int
if_clone_destroy(const char *name)
{
struct if_clone *ifc;
struct ifnet *ifp;
int ret;
ifc = if_clone_lookup(name, NULL);
if (ifc == NULL)
return (EINVAL);
if (ifc->ifc_destroy == NULL)
return (EOPNOTSUPP);
rw_enter_write(&if_cloners_lock);
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (strcmp(ifp->if_xname, name) == 0)
break;
}
if (ifp == NULL) {
rw_exit_write(&if_cloners_lock);
return (ENXIO);
}
NET_LOCK();
if (ifp->if_flags & IFF_UP) {
int s;
s = splnet();
if_down(ifp);
splx(s);
}
NET_UNLOCK();
ret = (*ifc->ifc_destroy)(ifp);
rw_exit_write(&if_cloners_lock);
return (ret);
}
/*
* Look up a network interface cloner.
*/
struct if_clone *
if_clone_lookup(const char *name, int *unitp)
{
struct if_clone *ifc;
const char *cp;
int unit;
/* separate interface name from unit */
for (cp = name;
cp - name < IFNAMSIZ && *cp && (*cp < '0' || *cp > '9');
cp++)
continue;
if (cp == name || cp - name == IFNAMSIZ || !*cp)
return (NULL); /* No name or unit number */
if (cp - name < IFNAMSIZ-1 && *cp == '0' && cp[1] != '\0')
return (NULL); /* unit number 0 padded */
LIST_FOREACH(ifc, &if_cloners, ifc_list) {
if (strlen(ifc->ifc_name) == cp - name &&
!strncmp(name, ifc->ifc_name, cp - name))
break;
}
if (ifc == NULL)
return (NULL);
unit = 0;
while (cp - name < IFNAMSIZ && *cp) {
if (*cp < '0' || *cp > '9' ||
unit > (INT_MAX - (*cp - '0')) / 10) {
/* Bogus unit number. */
return (NULL);
}
unit = (unit * 10) + (*cp++ - '0');
}
if (unitp != NULL)
*unitp = unit;
return (ifc);
}
/*
* Register a network interface cloner.
*/
void
if_clone_attach(struct if_clone *ifc)
{
/*
* we are called at kernel boot by main(), when pseudo devices are
* being attached. The main() is the only guy which may alter the
* if_cloners. While system is running and main() is done with
* initialization, the if_cloners becomes immutable.
*/
KASSERT(pdevinit_done == 0);
LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
if_cloners_count++;
}
/*
* Provide list of interface cloners to userspace.
*/
int
if_clone_list(struct if_clonereq *ifcr)
{
char outbuf[IFNAMSIZ], *dst;
struct if_clone *ifc;
int count, error = 0;
if ((dst = ifcr->ifcr_buffer) == NULL) {
/* Just asking how many there are. */
ifcr->ifcr_total = if_cloners_count;
return (0);
}
if (ifcr->ifcr_count < 0)
return (EINVAL);
ifcr->ifcr_total = if_cloners_count;
count = MIN(if_cloners_count, ifcr->ifcr_count);
LIST_FOREACH(ifc, &if_cloners, ifc_list) {
if (count == 0)
break;
bzero(outbuf, sizeof outbuf);
strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
error = copyout(outbuf, dst, IFNAMSIZ);
if (error)
break;
count--;
dst += IFNAMSIZ;
}
return (error);
}
/*
* set queue congestion marker
*/
void
if_congestion(void)
{
extern int ticks;
ifq_congestion = ticks;
}
int
if_congested(void)
{
extern int ticks;
int diff;
diff = ticks - ifq_congestion;
if (diff < 0) {
ifq_congestion = ticks - hz;
return (0);
}
return (diff <= (hz / 100));
}
#define equal(a1, a2) \
(bcmp((caddr_t)(a1), (caddr_t)(a2), \
(a1)->sa_len) == 0)
/*
* Locate an interface based on a complete address.
*/
struct ifaddr *
ifa_ifwithaddr(struct sockaddr *addr, u_int rtableid)
{
struct ifnet *ifp;
struct ifaddr *ifa;
u_int rdomain;
rdomain = rtable_l2(rtableid);
KERNEL_LOCK();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rdomain)
continue;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != addr->sa_family)
continue;
if (equal(addr, ifa->ifa_addr)) {
KERNEL_UNLOCK();
return (ifa);
}
}
}
KERNEL_UNLOCK();
return (NULL);
}
/*
* Locate the point to point interface with a given destination address.
*/
struct ifaddr *
ifa_ifwithdstaddr(struct sockaddr *addr, u_int rdomain)
{
struct ifnet *ifp;
struct ifaddr *ifa;
rdomain = rtable_l2(rdomain);
KERNEL_LOCK();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rdomain)
continue;
if (ifp->if_flags & IFF_POINTOPOINT) {
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family !=
addr->sa_family || ifa->ifa_dstaddr == NULL)
continue;
if (equal(addr, ifa->ifa_dstaddr)) {
KERNEL_UNLOCK();
return (ifa);
}
}
}
}
KERNEL_UNLOCK();
return (NULL);
}
/*
* Find an interface address specific to an interface best matching
* a given address.
*/
struct ifaddr *
ifaof_ifpforaddr(struct sockaddr *addr, struct ifnet *ifp)
{
struct ifaddr *ifa;
char *cp, *cp2, *cp3;
char *cplim;
struct ifaddr *ifa_maybe = NULL;
u_int af = addr->sa_family;
if (af >= AF_MAX)
return (NULL);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != af)
continue;
if (ifa_maybe == NULL)
ifa_maybe = ifa;
if (ifa->ifa_netmask == 0 || ifp->if_flags & IFF_POINTOPOINT) {
if (equal(addr, ifa->ifa_addr) ||
(ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
return (ifa);
continue;
}
cp = addr->sa_data;
cp2 = ifa->ifa_addr->sa_data;
cp3 = ifa->ifa_netmask->sa_data;
cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
for (; cp3 < cplim; cp3++)
if ((*cp++ ^ *cp2++) & *cp3)
break;
if (cp3 == cplim)
return (ifa);
}
return (ifa_maybe);
}
void
if_rtrequest_dummy(struct ifnet *ifp, int req, struct rtentry *rt)
{
}
/*
* Default action when installing a local route on a point-to-point
* interface.
*/
void
p2p_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
{
struct ifnet *lo0ifp;
struct ifaddr *ifa, *lo0ifa;
switch (req) {
case RTM_ADD:
if (!ISSET(rt->rt_flags, RTF_LOCAL))
break;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (memcmp(rt_key(rt), ifa->ifa_addr,
rt_key(rt)->sa_len) == 0)
break;
}
if (ifa == NULL)
break;
KASSERT(ifa == rt->rt_ifa);
lo0ifp = if_get(rtable_loindex(ifp->if_rdomain));
KASSERT(lo0ifp != NULL);
TAILQ_FOREACH(lo0ifa, &lo0ifp->if_addrlist, ifa_list) {
if (lo0ifa->ifa_addr->sa_family ==
ifa->ifa_addr->sa_family)
break;
}
if_put(lo0ifp);
if (lo0ifa == NULL)
break;
rt->rt_flags &= ~RTF_LLINFO;
break;
case RTM_DELETE:
case RTM_RESOLVE:
default:
break;
}
}
int
p2p_bpf_mtap(caddr_t if_bpf, const struct mbuf *m, u_int dir)
{
#if NBPFILTER > 0
return (bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, m, dir));
#else
return (0);
#endif
}
void
p2p_input(struct ifnet *ifp, struct mbuf *m)
{
void (*input)(struct ifnet *, struct mbuf *);
switch (m->m_pkthdr.ph_family) {
case AF_INET:
input = ipv4_input;
break;
#ifdef INET6
case AF_INET6:
input = ipv6_input;
break;
#endif
#ifdef MPLS
case AF_MPLS:
input = mpls_input;
break;
#endif
default:
m_freem(m);
return;
}
(*input)(ifp, m);
}
/*
* Bring down all interfaces
*/
void
if_downall(void)
{
struct ifreq ifrq; /* XXX only partly built */
struct ifnet *ifp;
NET_LOCK();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if ((ifp->if_flags & IFF_UP) == 0)
continue;
if_down(ifp);
ifrq.ifr_flags = ifp->if_flags;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
}
NET_UNLOCK();
}
/*
* Mark an interface down and notify protocols of
* the transition.
*/
void
if_down(struct ifnet *ifp)
{
NET_ASSERT_LOCKED();
ifp->if_flags &= ~IFF_UP;
getmicrotime(&ifp->if_lastchange);
ifq_purge(&ifp->if_snd);
if_linkstate(ifp);
}
/*
* Mark an interface up and notify protocols of
* the transition.
*/
void
if_up(struct ifnet *ifp)
{
NET_ASSERT_LOCKED();
ifp->if_flags |= IFF_UP;
getmicrotime(&ifp->if_lastchange);
#ifdef INET6
/* Userland expects the kernel to set ::1 on default lo(4). */
if (ifp->if_index == rtable_loindex(ifp->if_rdomain))
in6_ifattach(ifp);
#endif
if_linkstate(ifp);
}
/*
* Notify userland, the routing table and hooks owner of
* a link-state transition.
*/
void
if_linkstate_task(void *xifidx)
{
unsigned int ifidx = (unsigned long)xifidx;
struct ifnet *ifp;
KERNEL_LOCK();
NET_LOCK();
ifp = if_get(ifidx);
if (ifp != NULL)
if_linkstate(ifp);
if_put(ifp);
NET_UNLOCK();
KERNEL_UNLOCK();
}
void
if_linkstate(struct ifnet *ifp)
{
NET_ASSERT_LOCKED();
rtm_ifchg(ifp);
rt_if_track(ifp);
if_hooks_run(&ifp->if_linkstatehooks);
}
void
if_linkstatehook_add(struct ifnet *ifp, struct task *t)
{
mtx_enter(&if_hooks_mtx);
TAILQ_INSERT_HEAD(&ifp->if_linkstatehooks, t, t_entry);
mtx_leave(&if_hooks_mtx);
}
void
if_linkstatehook_del(struct ifnet *ifp, struct task *t)
{
mtx_enter(&if_hooks_mtx);
TAILQ_REMOVE(&ifp->if_linkstatehooks, t, t_entry);
mtx_leave(&if_hooks_mtx);
}
/*
* Schedule a link state change task.
*/
void
if_link_state_change(struct ifnet *ifp)
{
task_add(net_tq(ifp->if_index), &ifp->if_linkstatetask);
}
/*
* Handle interface watchdog timer routine. Called
* from softclock, we decrement timer (if set) and
* call the appropriate interface routine on expiration.
*/
void
if_slowtimo(void *arg)
{
struct ifnet *ifp = arg;
int s = splnet();
if (ifp->if_watchdog) {
if (ifp->if_timer > 0 && --ifp->if_timer == 0)
task_add(net_tq(ifp->if_index), &ifp->if_watchdogtask);
timeout_add_sec(&ifp->if_slowtimo, IFNET_SLOWTIMO);
}
splx(s);
}
void
if_watchdog_task(void *xifidx)
{
unsigned int ifidx = (unsigned long)xifidx;
struct ifnet *ifp;
int s;
ifp = if_get(ifidx);
if (ifp == NULL)
return;
KERNEL_LOCK();
s = splnet();
if (ifp->if_watchdog)
(*ifp->if_watchdog)(ifp);
splx(s);
KERNEL_UNLOCK();
if_put(ifp);
}
/*
* Map interface name to interface structure pointer.
*/
struct ifnet *
if_unit(const char *name)
{
struct ifnet *ifp;
KERNEL_ASSERT_LOCKED();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (strcmp(ifp->if_xname, name) == 0) {
if_ref(ifp);
return (ifp);
}
}
return (NULL);
}
/*
* Map interface index to interface structure pointer.
*/
struct ifnet *
if_get(unsigned int index)
{
struct srp_ref sr;
struct if_map *if_map;
struct srp *map;
struct ifnet *ifp = NULL;
if_map = srp_enter(&sr, &if_idxmap.map);
if (index < if_map->limit) {
map = (struct srp *)(if_map + 1);
ifp = srp_follow(&sr, &map[index]);
if (ifp != NULL) {
KASSERT(ifp->if_index == index);
if_ref(ifp);
}
}
srp_leave(&sr);
return (ifp);
}
struct ifnet *
if_ref(struct ifnet *ifp)
{
refcnt_take(&ifp->if_refcnt);
return (ifp);
}
void
if_put(struct ifnet *ifp)
{
if (ifp == NULL)
return;
refcnt_rele_wake(&ifp->if_refcnt);
}
int
if_setlladdr(struct ifnet *ifp, const uint8_t *lladdr)
{
if (ifp->if_sadl == NULL)
return (EINVAL);
memcpy(((struct arpcom *)ifp)->ac_enaddr, lladdr, ETHER_ADDR_LEN);
memcpy(LLADDR(ifp->if_sadl), lladdr, ETHER_ADDR_LEN);
return (0);
}
int
if_createrdomain(int rdomain, struct ifnet *ifp)
{
int error;
struct ifnet *loifp;
char loifname[IFNAMSIZ];
unsigned int unit = rdomain;
if ((error = rtable_add(rdomain)) != 0)
return (error);
if (!rtable_empty(rdomain))
return (EEXIST);
/* Create rdomain including its loopback if with unit == rdomain */
snprintf(loifname, sizeof(loifname), "lo%u", unit);
error = if_clone_create(loifname, 0);
if ((loifp = if_unit(loifname)) == NULL)
return (ENXIO);
if (error && (ifp != loifp || error != EEXIST)) {
if_put(loifp);
return (error);
}
rtable_l2set(rdomain, rdomain, loifp->if_index);
loifp->if_rdomain = rdomain;
if_put(loifp);
return (0);
}
int
if_setrdomain(struct ifnet *ifp, int rdomain)
{
struct ifreq ifr;
int error, up = 0, s;
if (rdomain < 0 || rdomain > RT_TABLEID_MAX)
return (EINVAL);
if (rdomain != ifp->if_rdomain &&
(ifp->if_flags & IFF_LOOPBACK) &&
(ifp->if_index == rtable_loindex(ifp->if_rdomain)))
return (EPERM);
if (!rtable_exists(rdomain))
return (ESRCH);
/* make sure that the routing table is a real rdomain */
if (rdomain != rtable_l2(rdomain))
return (EINVAL);
if (rdomain != ifp->if_rdomain) {
s = splnet();
/*
* We are tearing down the world.
* Take down the IF so:
* 1. everything that cares gets a message
* 2. the automagic IPv6 bits are recreated
*/
if (ifp->if_flags & IFF_UP) {
up = 1;
if_down(ifp);
}
rti_delete(ifp);
#ifdef MROUTING
vif_delete(ifp);
#endif
in_ifdetach(ifp);
#ifdef INET6
in6_ifdetach(ifp);
#endif
splx(s);
}
/* Let devices like enc(4) or mpe(4) know about the change */
ifr.ifr_rdomainid = rdomain;
if ((error = (*ifp->if_ioctl)(ifp, SIOCSIFRDOMAIN,
(caddr_t)&ifr)) != ENOTTY)
return (error);
error = 0;
/* Add interface to the specified rdomain */
ifp->if_rdomain = rdomain;
/* If we took down the IF, bring it back */
if (up) {
s = splnet();
if_up(ifp);
splx(s);
}
return (0);
}
/*
* Interface ioctls.
*/
int
ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
{
struct ifnet *ifp;
struct ifreq *ifr = (struct ifreq *)data;
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
struct if_afreq *ifar = (struct if_afreq *)data;
char ifdescrbuf[IFDESCRSIZE];
char ifrtlabelbuf[RTLABEL_LEN];
int s, error = 0, oif_xflags;
size_t bytesdone;
unsigned short oif_flags;
switch (cmd) {
case SIOCIFCREATE:
if ((error = suser(p)) != 0)
return (error);
error = if_clone_create(ifr->ifr_name, 0);
return (error);
case SIOCIFDESTROY:
if ((error = suser(p)) != 0)
return (error);
error = if_clone_destroy(ifr->ifr_name);
return (error);
case SIOCSIFGATTR:
if ((error = suser(p)) != 0)
return (error);
NET_LOCK();
error = if_setgroupattribs(data);
NET_UNLOCK();
return (error);
case SIOCGIFCONF:
case SIOCIFGCLONERS:
case SIOCGIFGMEMB:
case SIOCGIFGATTR:
case SIOCGIFGLIST:
case SIOCGIFFLAGS:
case SIOCGIFXFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
case SIOCGIFHARDMTU:
case SIOCGIFDATA:
case SIOCGIFDESCR:
case SIOCGIFRTLABEL:
case SIOCGIFPRIORITY:
case SIOCGIFRDOMAIN:
case SIOCGIFGROUP:
case SIOCGIFLLPRIO:
return (ifioctl_get(cmd, data));
}
ifp = if_unit(ifr->ifr_name);
if (ifp == NULL)
return (ENXIO);
oif_flags = ifp->if_flags;
oif_xflags = ifp->if_xflags;
switch (cmd) {
case SIOCIFAFATTACH:
case SIOCIFAFDETACH:
if ((error = suser(p)) != 0)
break;
NET_LOCK();
switch (ifar->ifar_af) {
case AF_INET:
/* attach is a noop for AF_INET */
if (cmd == SIOCIFAFDETACH)
in_ifdetach(ifp);
break;
#ifdef INET6
case AF_INET6:
if (cmd == SIOCIFAFATTACH)
error = in6_ifattach(ifp);
else
in6_ifdetach(ifp);
break;
#endif /* INET6 */
default:
error = EAFNOSUPPORT;
}
NET_UNLOCK();
break;
case SIOCSIFXFLAGS:
if ((error = suser(p)) != 0)
break;
NET_LOCK();
#ifdef INET6
if ((ISSET(ifr->ifr_flags, IFXF_AUTOCONF6) ||
ISSET(ifr->ifr_flags, IFXF_AUTOCONF6TEMP)) &&
!ISSET(ifp->if_xflags, IFXF_AUTOCONF6) &&
!ISSET(ifp->if_xflags, IFXF_AUTOCONF6TEMP)) {
error = in6_ifattach(ifp);
if (error != 0) {
NET_UNLOCK();
break;
}
}
if (ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
!ISSET(ifp->if_xflags, IFXF_INET6_NOSOII))
ifp->if_xflags |= IFXF_INET6_NOSOII;
if (!ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
ISSET(ifp->if_xflags, IFXF_INET6_NOSOII))
ifp->if_xflags &= ~IFXF_INET6_NOSOII;
#endif /* INET6 */
#ifdef MPLS
if (ISSET(ifr->ifr_flags, IFXF_MPLS) &&
!ISSET(ifp->if_xflags, IFXF_MPLS)) {
s = splnet();
ifp->if_xflags |= IFXF_MPLS;
ifp->if_ll_output = ifp->if_output;
ifp->if_output = mpls_output;
splx(s);
}
if (ISSET(ifp->if_xflags, IFXF_MPLS) &&
!ISSET(ifr->ifr_flags, IFXF_MPLS)) {
s = splnet();
ifp->if_xflags &= ~IFXF_MPLS;
ifp->if_output = ifp->if_ll_output;
ifp->if_ll_output = NULL;
splx(s);
}
#endif /* MPLS */
#ifndef SMALL_KERNEL
if (ifp->if_capabilities & IFCAP_WOL) {
if (ISSET(ifr->ifr_flags, IFXF_WOL) &&
!ISSET(ifp->if_xflags, IFXF_WOL)) {
s = splnet();
ifp->if_xflags |= IFXF_WOL;
error = ifp->if_wol(ifp, 1);
splx(s);
}
if (ISSET(ifp->if_xflags, IFXF_WOL) &&
!ISSET(ifr->ifr_flags, IFXF_WOL)) {
s = splnet();
ifp->if_xflags &= ~IFXF_WOL;
error = ifp->if_wol(ifp, 0);
splx(s);
}
} else if (ISSET(ifr->ifr_flags, IFXF_WOL)) {
ifr->ifr_flags &= ~IFXF_WOL;
error = ENOTSUP;
}
if (ISSET(ifp->if_capabilities, IFCAP_TSO) &&
ISSET(ifr->ifr_flags, IFXF_TSO) !=
ISSET(ifp->if_xflags, IFXF_TSO)) {
struct ifreq ifrq;
s = splnet();
if (ISSET(ifr->ifr_flags, IFXF_TSO))
ifp->if_xflags |= IFXF_TSO;
else
ifp->if_xflags &= ~IFXF_TSO;
NET_ASSERT_LOCKED(); /* for ioctl */
KERNEL_ASSERT_LOCKED(); /* for if_flags */
if (ISSET(ifp->if_flags, IFF_UP)) {
/* go down for a moment... */
ifp->if_flags &= ~IFF_UP;
ifrq.ifr_flags = ifp->if_flags;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS,
(caddr_t)&ifrq);
/* ... and up again */
ifp->if_flags |= IFF_UP;
ifrq.ifr_flags = ifp->if_flags;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS,
(caddr_t)&ifrq);
}
splx(s);
} else if (!ISSET(ifp->if_capabilities, IFCAP_TSO) &&
ISSET(ifr->ifr_flags, IFXF_TSO)) {
ifr->ifr_flags &= ~IFXF_TSO;
error = ENOTSUP;
}
#endif
if (error == 0)
ifp->if_xflags = (ifp->if_xflags & IFXF_CANTCHANGE) |
(ifr->ifr_flags & ~IFXF_CANTCHANGE);
if (!ISSET(ifp->if_flags, IFF_UP) &&
((!ISSET(oif_xflags, IFXF_AUTOCONF4) &&
ISSET(ifp->if_xflags, IFXF_AUTOCONF4)) ||
(!ISSET(oif_xflags, IFXF_AUTOCONF6) &&
ISSET(ifp->if_xflags, IFXF_AUTOCONF6)) ||
(!ISSET(oif_xflags, IFXF_AUTOCONF6TEMP) &&
ISSET(ifp->if_xflags, IFXF_AUTOCONF6TEMP)))) {
ifr->ifr_flags = ifp->if_flags | IFF_UP;
goto forceup;
}
NET_UNLOCK();
break;
case SIOCSIFFLAGS:
if ((error = suser(p)) != 0)
break;
NET_LOCK();
forceup:
ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
(ifr->ifr_flags & ~IFF_CANTCHANGE);
error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, data);
if (error != 0) {
ifp->if_flags = oif_flags;
if (cmd == SIOCSIFXFLAGS)
ifp->if_xflags = oif_xflags;
} else if (ISSET(oif_flags ^ ifp->if_flags, IFF_UP)) {
s = splnet();
if (ISSET(ifp->if_flags, IFF_UP))
if_up(ifp);
else
if_down(ifp);
splx(s);
}
NET_UNLOCK();
break;
case SIOCSIFMETRIC:
if ((error = suser(p)) != 0)
break;
NET_LOCK();
ifp->if_metric = ifr->ifr_metric;
NET_UNLOCK();
break;
case SIOCSIFMTU:
if ((error = suser(p)) != 0)
break;
NET_LOCK();
error = (*ifp->if_ioctl)(ifp, cmd, data);
NET_UNLOCK();
if (error == 0)
rtm_ifchg(ifp);
break;
case SIOCSIFDESCR:
if ((error = suser(p)) != 0)
break;
error = copyinstr(ifr->ifr_data, ifdescrbuf,
IFDESCRSIZE, &bytesdone);
if (error == 0) {
(void)memset(ifp->if_description, 0, IFDESCRSIZE);
strlcpy(ifp->if_description, ifdescrbuf, IFDESCRSIZE);
}
break;
case SIOCSIFRTLABEL:
if ((error = suser(p)) != 0)
break;
error = copyinstr(ifr->ifr_data, ifrtlabelbuf,
RTLABEL_LEN, &bytesdone);
if (error == 0) {
rtlabel_unref(ifp->if_rtlabelid);
ifp->if_rtlabelid = rtlabel_name2id(ifrtlabelbuf);
}
break;
case SIOCSIFPRIORITY:
if ((error = suser(p)) != 0)
break;
if (ifr->ifr_metric < 0 || ifr->ifr_metric > 15) {
error = EINVAL;
break;
}
ifp->if_priority = ifr->ifr_metric;
break;
case SIOCSIFRDOMAIN:
if ((error = suser(p)) != 0)
break;
error = if_createrdomain(ifr->ifr_rdomainid, ifp);
if (!error || error == EEXIST) {
NET_LOCK();
error = if_setrdomain(ifp, ifr->ifr_rdomainid);
NET_UNLOCK();
}
break;
case SIOCAIFGROUP:
if ((error = suser(p)))
break;
NET_LOCK();
error = if_addgroup(ifp, ifgr->ifgr_group);
if (error == 0) {
error = (*ifp->if_ioctl)(ifp, cmd, data);
if (error == ENOTTY)
error = 0;
}
NET_UNLOCK();
break;
case SIOCDIFGROUP:
if ((error = suser(p)))
break;
NET_LOCK();
error = (*ifp->if_ioctl)(ifp, cmd, data);
if (error == ENOTTY)
error = 0;
if (error == 0)
error = if_delgroup(ifp, ifgr->ifgr_group);
NET_UNLOCK();
break;
case SIOCSIFLLADDR:
if ((error = suser(p)))
break;
if ((ifp->if_sadl == NULL) ||
(ifr->ifr_addr.sa_len != ETHER_ADDR_LEN) ||
(ETHER_IS_MULTICAST(ifr->ifr_addr.sa_data))) {
error = EINVAL;
break;
}
NET_LOCK();
switch (ifp->if_type) {
case IFT_ETHER:
case IFT_CARP:
case IFT_XETHER:
case IFT_ISO88025:
error = (*ifp->if_ioctl)(ifp, cmd, data);
if (error == ENOTTY)
error = 0;
if (error == 0)
error = if_setlladdr(ifp,
ifr->ifr_addr.sa_data);
break;
default:
error = ENODEV;
}
if (error == 0)
ifnewlladdr(ifp);
NET_UNLOCK();
if (error == 0)
rtm_ifchg(ifp);
break;
case SIOCSIFLLPRIO:
if ((error = suser(p)))
break;
if (ifr->ifr_llprio < IFQ_MINPRIO ||
ifr->ifr_llprio > IFQ_MAXPRIO) {
error = EINVAL;
break;
}
NET_LOCK();
ifp->if_llprio = ifr->ifr_llprio;
NET_UNLOCK();
break;
case SIOCGIFSFFPAGE:
error = suser(p);
if (error != 0)
break;
error = if_sffpage_check(data);
if (error != 0)
break;
/* don't take NET_LOCK because i2c reads take a long time */
error = ((*ifp->if_ioctl)(ifp, cmd, data));
break;
case SIOCSIFMEDIA:
if ((error = suser(p)) != 0)
break;
/* FALLTHROUGH */
case SIOCGIFMEDIA:
/* net lock is not needed */
error = ((*ifp->if_ioctl)(ifp, cmd, data));
break;
case SIOCSETKALIVE:
case SIOCDIFPHYADDR:
case SIOCSLIFPHYADDR:
case SIOCSLIFPHYRTABLE:
case SIOCSLIFPHYTTL:
case SIOCSLIFPHYDF:
case SIOCSLIFPHYECN:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSVNETID:
case SIOCDVNETID:
case SIOCSVNETFLOWID:
case SIOCSTXHPRIO:
case SIOCSRXHPRIO:
case SIOCSIFPAIR:
case SIOCSIFPARENT:
case SIOCDIFPARENT:
case SIOCSETMPWCFG:
case SIOCSETLABEL:
case SIOCDELLABEL:
case SIOCSPWE3CTRLWORD:
case SIOCSPWE3FAT:
case SIOCSPWE3NEIGHBOR:
case SIOCDPWE3NEIGHBOR:
#if NBRIDGE > 0
case SIOCBRDGADD:
case SIOCBRDGDEL:
case SIOCBRDGSIFFLGS:
case SIOCBRDGSCACHE:
case SIOCBRDGADDS:
case SIOCBRDGDELS:
case SIOCBRDGSADDR:
case SIOCBRDGSTO:
case SIOCBRDGDADDR:
case SIOCBRDGFLUSH:
case SIOCBRDGADDL:
case SIOCBRDGSIFPROT:
case SIOCBRDGARL:
case SIOCBRDGFRL:
case SIOCBRDGSPRI:
case SIOCBRDGSHT:
case SIOCBRDGSFD:
case SIOCBRDGSMA:
case SIOCBRDGSIFPRIO:
case SIOCBRDGSIFCOST:
case SIOCBRDGSTXHC:
case SIOCBRDGSPROTO:
#endif
if ((error = suser(p)) != 0)
break;
/* FALLTHROUGH */
default:
error = pru_control(so, cmd, data, ifp);
if (error != EOPNOTSUPP)
break;
switch (cmd) {
case SIOCAIFADDR:
case SIOCDIFADDR:
case SIOCSIFADDR:
case SIOCSIFNETMASK:
case SIOCSIFDSTADDR:
case SIOCSIFBRDADDR:
#ifdef INET6
case SIOCAIFADDR_IN6:
case SIOCDIFADDR_IN6:
#endif
error = suser(p);
break;
default:
error = 0;
break;
}
if (error)
break;
NET_LOCK();
error = ((*ifp->if_ioctl)(ifp, cmd, data));
NET_UNLOCK();
break;
}
if (oif_flags != ifp->if_flags || oif_xflags != ifp->if_xflags) {
/* if_up() and if_down() already sent an update, skip here */
if (((oif_flags ^ ifp->if_flags) & IFF_UP) == 0)
rtm_ifchg(ifp);
}
if (((oif_flags ^ ifp->if_flags) & IFF_UP) != 0)
getmicrotime(&ifp->if_lastchange);
if_put(ifp);
return (error);
}
int
ifioctl_get(u_long cmd, caddr_t data)
{
struct ifnet *ifp;
struct ifreq *ifr = (struct ifreq *)data;
char ifdescrbuf[IFDESCRSIZE];
char ifrtlabelbuf[RTLABEL_LEN];
int error = 0;
size_t bytesdone;
const char *label;
switch(cmd) {
case SIOCGIFCONF:
NET_LOCK_SHARED();
error = ifconf(data);
NET_UNLOCK_SHARED();
return (error);
case SIOCIFGCLONERS:
error = if_clone_list((struct if_clonereq *)data);
return (error);
case SIOCGIFGMEMB:
NET_LOCK_SHARED();
error = if_getgroupmembers(data);
NET_UNLOCK_SHARED();
return (error);
case SIOCGIFGATTR:
NET_LOCK_SHARED();
error = if_getgroupattribs(data);
NET_UNLOCK_SHARED();
return (error);
case SIOCGIFGLIST:
NET_LOCK_SHARED();
error = if_getgrouplist(data);
NET_UNLOCK_SHARED();
return (error);
}
ifp = if_unit(ifr->ifr_name);
if (ifp == NULL)
return (ENXIO);
NET_LOCK_SHARED();
switch(cmd) {
case SIOCGIFFLAGS:
ifr->ifr_flags = ifp->if_flags;
if (ifq_is_oactive(&ifp->if_snd))
ifr->ifr_flags |= IFF_OACTIVE;
break;
case SIOCGIFXFLAGS:
ifr->ifr_flags = ifp->if_xflags & ~(IFXF_MPSAFE|IFXF_CLONED);
break;
case SIOCGIFMETRIC:
ifr->ifr_metric = ifp->if_metric;
break;
case SIOCGIFMTU:
ifr->ifr_mtu = ifp->if_mtu;
break;
case SIOCGIFHARDMTU:
ifr->ifr_hardmtu = ifp->if_hardmtu;
break;
case SIOCGIFDATA: {
struct if_data ifdata;
if_getdata(ifp, &ifdata);
error = copyout(&ifdata, ifr->ifr_data, sizeof(ifdata));
break;
}
case SIOCGIFDESCR:
strlcpy(ifdescrbuf, ifp->if_description, IFDESCRSIZE);
error = copyoutstr(ifdescrbuf, ifr->ifr_data, IFDESCRSIZE,
&bytesdone);
break;
case SIOCGIFRTLABEL:
if (ifp->if_rtlabelid &&
(label = rtlabel_id2name(ifp->if_rtlabelid)) != NULL) {
strlcpy(ifrtlabelbuf, label, RTLABEL_LEN);
error = copyoutstr(ifrtlabelbuf, ifr->ifr_data,
RTLABEL_LEN, &bytesdone);
} else
error = ENOENT;
break;
case SIOCGIFPRIORITY:
ifr->ifr_metric = ifp->if_priority;
break;
case SIOCGIFRDOMAIN:
ifr->ifr_rdomainid = ifp->if_rdomain;
break;
case SIOCGIFGROUP:
error = if_getgroup(data, ifp);
break;
case SIOCGIFLLPRIO:
ifr->ifr_llprio = ifp->if_llprio;
break;
default:
panic("invalid ioctl %lu", cmd);
}
NET_UNLOCK_SHARED();
if_put(ifp);
return (error);
}
static int
if_sffpage_check(const caddr_t data)
{
const struct if_sffpage *sff = (const struct if_sffpage *)data;
switch (sff->sff_addr) {
case IFSFF_ADDR_EEPROM:
case IFSFF_ADDR_DDM:
break;
default:
return (EINVAL);
}
return (0);
}
int
if_txhprio_l2_check(int hdrprio)
{
switch (hdrprio) {
case IF_HDRPRIO_PACKET:
return (0);
default:
if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
return (0);
break;
}
return (EINVAL);
}
int
if_txhprio_l3_check(int hdrprio)
{
switch (hdrprio) {
case IF_HDRPRIO_PACKET:
case IF_HDRPRIO_PAYLOAD:
return (0);
default:
if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
return (0);
break;
}
return (EINVAL);
}
int
if_rxhprio_l2_check(int hdrprio)
{
switch (hdrprio) {
case IF_HDRPRIO_PACKET:
case IF_HDRPRIO_OUTER:
return (0);
default:
if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
return (0);
break;
}
return (EINVAL);
}
int
if_rxhprio_l3_check(int hdrprio)
{
switch (hdrprio) {
case IF_HDRPRIO_PACKET:
case IF_HDRPRIO_PAYLOAD:
case IF_HDRPRIO_OUTER:
return (0);
default:
if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
return (0);
break;
}
return (EINVAL);
}
/*
* Return interface configuration
* of system. List may be used
* in later ioctl's (above) to get
* other information.
*/
int
ifconf(caddr_t data)
{
struct ifconf *ifc = (struct ifconf *)data;
struct ifnet *ifp;
struct ifaddr *ifa;
struct ifreq ifr, *ifrp;
int space = ifc->ifc_len, error = 0;
/* If ifc->ifc_len is 0, fill it in with the needed size and return. */
if (space == 0) {
TAILQ_FOREACH(ifp, &ifnet, if_list) {
struct sockaddr *sa;
if (TAILQ_EMPTY(&ifp->if_addrlist))
space += sizeof (ifr);
else
TAILQ_FOREACH(ifa,
&ifp->if_addrlist, ifa_list) {
sa = ifa->ifa_addr;
if (sa->sa_len > sizeof(*sa))
space += sa->sa_len -
sizeof(*sa);
space += sizeof(ifr);
}
}
ifc->ifc_len = space;
return (0);
}
ifrp = ifc->ifc_req;
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (space < sizeof(ifr))
break;
bcopy(ifp->if_xname, ifr.ifr_name, IFNAMSIZ);
if (TAILQ_EMPTY(&ifp->if_addrlist)) {
bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
sizeof(ifr));
if (error)
break;
space -= sizeof (ifr), ifrp++;
} else
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
struct sockaddr *sa = ifa->ifa_addr;
if (space < sizeof(ifr))
break;
if (sa->sa_len <= sizeof(*sa)) {
ifr.ifr_addr = *sa;
error = copyout((caddr_t)&ifr,
(caddr_t)ifrp, sizeof (ifr));
ifrp++;
} else {
space -= sa->sa_len - sizeof(*sa);
if (space < sizeof (ifr))
break;
error = copyout((caddr_t)&ifr,
(caddr_t)ifrp,
sizeof(ifr.ifr_name));
if (error == 0)
error = copyout((caddr_t)sa,
(caddr_t)&ifrp->ifr_addr,
sa->sa_len);
ifrp = (struct ifreq *)(sa->sa_len +
(caddr_t)&ifrp->ifr_addr);
}
if (error)
break;
space -= sizeof (ifr);
}
}
ifc->ifc_len -= space;
return (error);
}
void
if_counters_alloc(struct ifnet *ifp)
{
KASSERT(ifp->if_counters == NULL);
ifp->if_counters = counters_alloc(ifc_ncounters);
}
void
if_counters_free(struct ifnet *ifp)
{
KASSERT(ifp->if_counters != NULL);
counters_free(ifp->if_counters, ifc_ncounters);
ifp->if_counters = NULL;
}
void
if_getdata(struct ifnet *ifp, struct if_data *data)
{
unsigned int i;
*data = ifp->if_data;
if (ifp->if_counters != NULL) {
uint64_t counters[ifc_ncounters];
counters_read(ifp->if_counters, counters, nitems(counters));
data->ifi_ipackets += counters[ifc_ipackets];
data->ifi_ierrors += counters[ifc_ierrors];
data->ifi_opackets += counters[ifc_opackets];
data->ifi_oerrors += counters[ifc_oerrors];
data->ifi_collisions += counters[ifc_collisions];
data->ifi_ibytes += counters[ifc_ibytes];
data->ifi_obytes += counters[ifc_obytes];
data->ifi_imcasts += counters[ifc_imcasts];
data->ifi_omcasts += counters[ifc_omcasts];
data->ifi_iqdrops += counters[ifc_iqdrops];
data->ifi_oqdrops += counters[ifc_oqdrops];
data->ifi_noproto += counters[ifc_noproto];
}
for (i = 0; i < ifp->if_nifqs; i++) {
struct ifqueue *ifq = ifp->if_ifqs[i];
ifq_add_data(ifq, data);
}
for (i = 0; i < ifp->if_niqs; i++) {
struct ifiqueue *ifiq = ifp->if_iqs[i];
ifiq_add_data(ifiq, data);
}
}
/*
* Dummy functions replaced in ifnet during detach (if protocols decide to
* fiddle with the if during detach.
*/
void
if_detached_qstart(struct ifqueue *ifq)
{
ifq_purge(ifq);
}
int
if_detached_ioctl(struct ifnet *ifp, u_long a, caddr_t b)
{
return ENODEV;
}
/*
* Create interface group without members
*/
struct ifg_group *
if_creategroup(const char *groupname)
{
struct ifg_group *ifg;
if ((ifg = malloc(sizeof(*ifg), M_TEMP, M_NOWAIT)) == NULL)
return (NULL);
strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group));
ifg->ifg_refcnt = 1;
ifg->ifg_carp_demoted = 0;
TAILQ_INIT(&ifg->ifg_members);
#if NPF > 0
pfi_attach_ifgroup(ifg);
#endif
TAILQ_INSERT_TAIL(&ifg_head, ifg, ifg_next);
return (ifg);
}
/*
* Add a group to an interface
*/
int
if_addgroup(struct ifnet *ifp, const char *groupname)
{
struct ifg_list *ifgl;
struct ifg_group *ifg = NULL;
struct ifg_member *ifgm;
size_t namelen;
namelen = strlen(groupname);
if (namelen == 0 || namelen >= IFNAMSIZ ||
(groupname[namelen - 1] >= '0' && groupname[namelen - 1] <= '9'))
return (EINVAL);
TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
return (EEXIST);
if ((ifgl = malloc(sizeof(*ifgl), M_TEMP, M_NOWAIT)) == NULL)
return (ENOMEM);
if ((ifgm = malloc(sizeof(*ifgm), M_TEMP, M_NOWAIT)) == NULL) {
free(ifgl, M_TEMP, sizeof(*ifgl));
return (ENOMEM);
}
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, groupname))
break;
if (ifg == NULL) {
ifg = if_creategroup(groupname);
if (ifg == NULL) {
free(ifgl, M_TEMP, sizeof(*ifgl));
free(ifgm, M_TEMP, sizeof(*ifgm));
return (ENOMEM);
}
} else
ifg->ifg_refcnt++;
KASSERT(ifg->ifg_refcnt != 0);
ifgl->ifgl_group = ifg;
ifgm->ifgm_ifp = ifp;
TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next);
TAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next);
#if NPF > 0
pfi_group_addmember(groupname);
#endif
return (0);
}
/*
* Remove a group from an interface
*/
int
if_delgroup(struct ifnet *ifp, const char *groupname)
{
struct ifg_list *ifgl;
struct ifg_member *ifgm;
TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
break;
if (ifgl == NULL)
return (ENOENT);
TAILQ_REMOVE(&ifp->if_groups, ifgl, ifgl_next);
TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next)
if (ifgm->ifgm_ifp == ifp)
break;
if (ifgm != NULL) {
TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next);
free(ifgm, M_TEMP, sizeof(*ifgm));
}
#if NPF > 0
pfi_group_delmember(groupname);
#endif
KASSERT(ifgl->ifgl_group->ifg_refcnt != 0);
if (--ifgl->ifgl_group->ifg_refcnt == 0) {
TAILQ_REMOVE(&ifg_head, ifgl->ifgl_group, ifg_next);
#if NPF > 0
pfi_detach_ifgroup(ifgl->ifgl_group);
#endif
free(ifgl->ifgl_group, M_TEMP, sizeof(*ifgl->ifgl_group));
}
free(ifgl, M_TEMP, sizeof(*ifgl));
return (0);
}
/*
* Stores all groups from an interface in memory pointed
* to by data
*/
int
if_getgroup(caddr_t data, struct ifnet *ifp)
{
int len, error;
struct ifg_list *ifgl;
struct ifg_req ifgrq, *ifgp;
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
if (ifgr->ifgr_len == 0) {
TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
ifgr->ifgr_len += sizeof(struct ifg_req);
return (0);
}
len = ifgr->ifgr_len;
ifgp = ifgr->ifgr_groups;
TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
if (len < sizeof(ifgrq))
return (EINVAL);
bzero(&ifgrq, sizeof ifgrq);
strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
sizeof(ifgrq.ifgrq_group));
if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
sizeof(struct ifg_req))))
return (error);
len -= sizeof(ifgrq);
ifgp++;
}
return (0);
}
/*
* Stores all members of a group in memory pointed to by data
*/
int
if_getgroupmembers(caddr_t data)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
struct ifg_group *ifg;
struct ifg_member *ifgm;
struct ifg_req ifgrq, *ifgp;
int len, error;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
break;
if (ifg == NULL)
return (ENOENT);
if (ifgr->ifgr_len == 0) {
TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
ifgr->ifgr_len += sizeof(ifgrq);
return (0);
}
len = ifgr->ifgr_len;
ifgp = ifgr->ifgr_groups;
TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) {
if (len < sizeof(ifgrq))
return (EINVAL);
bzero(&ifgrq, sizeof ifgrq);
strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname,
sizeof(ifgrq.ifgrq_member));
if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
sizeof(struct ifg_req))))
return (error);
len -= sizeof(ifgrq);
ifgp++;
}
return (0);
}
int
if_getgroupattribs(caddr_t data)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
struct ifg_group *ifg;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
break;
if (ifg == NULL)
return (ENOENT);
ifgr->ifgr_attrib.ifg_carp_demoted = ifg->ifg_carp_demoted;
return (0);
}
int
if_setgroupattribs(caddr_t data)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
struct ifg_group *ifg;
struct ifg_member *ifgm;
int demote;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
break;
if (ifg == NULL)
return (ENOENT);
demote = ifgr->ifgr_attrib.ifg_carp_demoted;
if (demote + ifg->ifg_carp_demoted > 0xff ||
demote + ifg->ifg_carp_demoted < 0)
return (EINVAL);
ifg->ifg_carp_demoted += demote;
TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
ifgm->ifgm_ifp->if_ioctl(ifgm->ifgm_ifp, SIOCSIFGATTR, data);
return (0);
}
/*
* Stores all groups in memory pointed to by data
*/
int
if_getgrouplist(caddr_t data)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
struct ifg_group *ifg;
struct ifg_req ifgrq, *ifgp;
int len, error;
if (ifgr->ifgr_len == 0) {
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
ifgr->ifgr_len += sizeof(ifgrq);
return (0);
}
len = ifgr->ifgr_len;
ifgp = ifgr->ifgr_groups;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next) {
if (len < sizeof(ifgrq))
return (EINVAL);
bzero(&ifgrq, sizeof ifgrq);
strlcpy(ifgrq.ifgrq_group, ifg->ifg_group,
sizeof(ifgrq.ifgrq_group));
if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
sizeof(struct ifg_req))))
return (error);
len -= sizeof(ifgrq);
ifgp++;
}
return (0);
}
void
if_group_routechange(struct sockaddr *dst, struct sockaddr *mask)
{
switch (dst->sa_family) {
case AF_INET:
if (satosin(dst)->sin_addr.s_addr == INADDR_ANY &&
mask && (mask->sa_len == 0 ||
satosin(mask)->sin_addr.s_addr == INADDR_ANY))
if_group_egress_build();
break;
#ifdef INET6
case AF_INET6:
if (IN6_ARE_ADDR_EQUAL(&(satosin6(dst))->sin6_addr,
&in6addr_any) && mask && (mask->sa_len == 0 ||
IN6_ARE_ADDR_EQUAL(&(satosin6(mask))->sin6_addr,
&in6addr_any)))
if_group_egress_build();
break;
#endif
}
}
int
if_group_egress_build(void)
{
struct ifnet *ifp;
struct ifg_group *ifg;
struct ifg_member *ifgm, *next;
struct sockaddr_in sa_in;
#ifdef INET6
struct sockaddr_in6 sa_in6;
#endif
struct rtentry *rt;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, IFG_EGRESS))
break;
if (ifg != NULL)
TAILQ_FOREACH_SAFE(ifgm, &ifg->ifg_members, ifgm_next, next)
if_delgroup(ifgm->ifgm_ifp, IFG_EGRESS);
bzero(&sa_in, sizeof(sa_in));
sa_in.sin_len = sizeof(sa_in);
sa_in.sin_family = AF_INET;
rt = rtable_lookup(0, sintosa(&sa_in), sintosa(&sa_in), NULL, RTP_ANY);
while (rt != NULL) {
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL) {
if_addgroup(ifp, IFG_EGRESS);
if_put(ifp);
}
rt = rtable_iterate(rt);
}
#ifdef INET6
bcopy(&sa6_any, &sa_in6, sizeof(sa_in6));
rt = rtable_lookup(0, sin6tosa(&sa_in6), sin6tosa(&sa_in6), NULL,
RTP_ANY);
while (rt != NULL) {
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL) {
if_addgroup(ifp, IFG_EGRESS);
if_put(ifp);
}
rt = rtable_iterate(rt);
}
#endif /* INET6 */
return (0);
}
/*
* Set/clear promiscuous mode on interface ifp based on the truth value
* of pswitch. The calls are reference counted so that only the first
* "on" request actually has an effect, as does the final "off" request.
* Results are undefined if the "off" and "on" requests are not matched.
*/
int
ifpromisc(struct ifnet *ifp, int pswitch)
{
struct ifreq ifr;
unsigned short oif_flags;
int oif_pcount, error;
NET_ASSERT_LOCKED(); /* modifying if_flags and if_pcount */
oif_flags = ifp->if_flags;
oif_pcount = ifp->if_pcount;
if (pswitch) {
if (ifp->if_pcount++ != 0)
return (0);
ifp->if_flags |= IFF_PROMISC;
} else {
if (--ifp->if_pcount > 0)
return (0);
ifp->if_flags &= ~IFF_PROMISC;
}
if ((ifp->if_flags & IFF_UP) == 0)
return (0);
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = ifp->if_flags;
error = ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
if (error) {
ifp->if_flags = oif_flags;
ifp->if_pcount = oif_pcount;
}
return (error);
}
void
ifa_add(struct ifnet *ifp, struct ifaddr *ifa)
{
TAILQ_INSERT_TAIL(&ifp->if_addrlist, ifa, ifa_list);
}
void
ifa_del(struct ifnet *ifp, struct ifaddr *ifa)
{
TAILQ_REMOVE(&ifp->if_addrlist, ifa, ifa_list);
}
void
ifa_update_broadaddr(struct ifnet *ifp, struct ifaddr *ifa, struct sockaddr *sa)
{
if (ifa->ifa_broadaddr->sa_len != sa->sa_len)
panic("ifa_update_broadaddr does not support dynamic length");
bcopy(sa, ifa->ifa_broadaddr, sa->sa_len);
}
#ifdef DDB
/* debug function, can be called from ddb> */
void
ifa_print_all(void)
{
struct ifnet *ifp;
struct ifaddr *ifa;
TAILQ_FOREACH(ifp, &ifnet, if_list) {
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
char addr[INET6_ADDRSTRLEN];
switch (ifa->ifa_addr->sa_family) {
case AF_INET:
printf("%s", inet_ntop(AF_INET,
&satosin(ifa->ifa_addr)->sin_addr,
addr, sizeof(addr)));
break;
#ifdef INET6
case AF_INET6:
printf("%s", inet_ntop(AF_INET6,
&(satosin6(ifa->ifa_addr))->sin6_addr,
addr, sizeof(addr)));
break;
#endif
}
printf(" on %s\n", ifp->if_xname);
}
}
}
#endif /* DDB */
void
ifnewlladdr(struct ifnet *ifp)
{
#ifdef INET6
struct ifaddr *ifa;
#endif
struct ifreq ifrq;
short up;
NET_ASSERT_LOCKED(); /* for ioctl and in6 */
KERNEL_ASSERT_LOCKED(); /* for if_flags */
up = ifp->if_flags & IFF_UP;
if (up) {
/* go down for a moment... */
ifp->if_flags &= ~IFF_UP;
ifrq.ifr_flags = ifp->if_flags;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
}
ifp->if_flags |= IFF_UP;
ifrq.ifr_flags = ifp->if_flags;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
#ifdef INET6
/*
* Update the link-local address. Don't do it if we're
* a router to avoid confusing hosts on the network.
*/
if (!ip6_forwarding) {
ifa = &in6ifa_ifpforlinklocal(ifp, 0)->ia_ifa;
if (ifa) {
in6_purgeaddr(ifa);
if_hooks_run(&ifp->if_addrhooks);
in6_ifattach(ifp);
}
}
#endif
if (!up) {
/* go back down */
ifp->if_flags &= ~IFF_UP;
ifrq.ifr_flags = ifp->if_flags;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
}
}
void
if_addrhook_add(struct ifnet *ifp, struct task *t)
{
mtx_enter(&if_hooks_mtx);
TAILQ_INSERT_TAIL(&ifp->if_addrhooks, t, t_entry);
mtx_leave(&if_hooks_mtx);
}
void
if_addrhook_del(struct ifnet *ifp, struct task *t)
{
mtx_enter(&if_hooks_mtx);
TAILQ_REMOVE(&ifp->if_addrhooks, t, t_entry);
mtx_leave(&if_hooks_mtx);
}
void
if_addrhooks_run(struct ifnet *ifp)
{
if_hooks_run(&ifp->if_addrhooks);
}
void
if_rxr_init(struct if_rxring *rxr, u_int lwm, u_int hwm)
{
extern int ticks;
memset(rxr, 0, sizeof(*rxr));
rxr->rxr_adjusted = ticks;
rxr->rxr_cwm = rxr->rxr_lwm = lwm;
rxr->rxr_hwm = hwm;
}
static inline void
if_rxr_adjust_cwm(struct if_rxring *rxr)
{
extern int ticks;
if (rxr->rxr_alive >= rxr->rxr_lwm)
return;
else if (rxr->rxr_cwm < rxr->rxr_hwm)
rxr->rxr_cwm++;
rxr->rxr_adjusted = ticks;
}
void
if_rxr_livelocked(struct if_rxring *rxr)
{
extern int ticks;
if (ticks - rxr->rxr_adjusted >= 1) {
if (rxr->rxr_cwm > rxr->rxr_lwm)
rxr->rxr_cwm--;
rxr->rxr_adjusted = ticks;
}
}
u_int
if_rxr_get(struct if_rxring *rxr, u_int max)
{
extern int ticks;
u_int diff;
if (ticks - rxr->rxr_adjusted >= 1) {
/* we're free to try for an adjustment */
if_rxr_adjust_cwm(rxr);
}
if (rxr->rxr_alive >= rxr->rxr_cwm)
return (0);
diff = min(rxr->rxr_cwm - rxr->rxr_alive, max);
rxr->rxr_alive += diff;
return (diff);
}
int
if_rxr_info_ioctl(struct if_rxrinfo *uifri, u_int t, struct if_rxring_info *e)
{
struct if_rxrinfo kifri;
int error;
u_int n;
error = copyin(uifri, &kifri, sizeof(kifri));
if (error)
return (error);
n = min(t, kifri.ifri_total);
kifri.ifri_total = t;
if (n > 0) {
error = copyout(e, kifri.ifri_entries, sizeof(*e) * n);
if (error)
return (error);
}
return (copyout(&kifri, uifri, sizeof(kifri)));
}
int
if_rxr_ioctl(struct if_rxrinfo *ifri, const char *name, u_int size,
struct if_rxring *rxr)
{
struct if_rxring_info ifr;
memset(&ifr, 0, sizeof(ifr));
if (name != NULL)
strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
ifr.ifr_size = size;
ifr.ifr_info = *rxr;
return (if_rxr_info_ioctl(ifri, 1, &ifr));
}
/*
* Network stack input queues.
*/
void
niq_init(struct niqueue *niq, u_int maxlen, u_int isr)
{
mq_init(&niq->ni_q, maxlen, IPL_NET);
niq->ni_isr = isr;
}
int
niq_enqueue(struct niqueue *niq, struct mbuf *m)
{
int rv;
rv = mq_enqueue(&niq->ni_q, m);
if (rv == 0)
schednetisr(niq->ni_isr);
else
if_congestion();
return (rv);
}
int
niq_enlist(struct niqueue *niq, struct mbuf_list *ml)
{
int rv;
rv = mq_enlist(&niq->ni_q, ml);
if (rv == 0)
schednetisr(niq->ni_isr);
else
if_congestion();
return (rv);
}
__dead void
unhandled_af(int af)
{
panic("unhandled af %d", af);
}
struct taskq *
net_tq(unsigned int ifindex)
{
struct taskq *t = NULL;
static int nettaskqs;
if (nettaskqs == 0)
nettaskqs = min(NET_TASKQ, ncpus);
t = nettqmp[ifindex % nettaskqs];
return (t);
}
26
1
20
1
3
19
2
18
3
1
2
1
115
7
37
2
1
1
1
27
10
69
13
1
1
11
11
4
4
1
6
3
1
2
1
6
2
4
1
1
3
31
1
25
4
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
/* $OpenBSD: raw_ip.c,v 1.147 2022/09/03 22:43:38 mvs Exp $ */
/* $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_mroute.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_icmp.h>
#include <net/pfvar.h>
#include "pf.h"
struct inpcbtable rawcbtable;
/*
* Nominal space allocated to a raw ip socket.
*/
#define RIPSNDQ 8192
#define RIPRCVQ 8192
/*
* Raw interface to IP protocol.
*/
const struct pr_usrreqs rip_usrreqs = {
.pru_attach = rip_attach,
.pru_detach = rip_detach,
.pru_bind = rip_bind,
.pru_connect = rip_connect,
.pru_disconnect = rip_disconnect,
.pru_shutdown = rip_shutdown,
.pru_send = rip_send,
.pru_abort = rip_abort,
.pru_control = in_control,
.pru_sockaddr = in_sockaddr,
.pru_peeraddr = in_peeraddr,
};
/*
* Initialize raw connection block q.
*/
void
rip_init(void)
{
in_pcbinit(&rawcbtable, 1);
}
struct mbuf *rip_chkhdr(struct mbuf *, struct mbuf *);
int
rip_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
struct ip *ip = mtod(m, struct ip *);
struct inpcb *inp;
SIMPLEQ_HEAD(, inpcb) inpcblist;
struct in_addr *key;
struct counters_ref ref;
uint64_t *counters;
struct sockaddr_in ripsrc;
KASSERT(af == AF_INET);
memset(&ripsrc, 0, sizeof(ripsrc));
ripsrc.sin_family = AF_INET;
ripsrc.sin_len = sizeof(ripsrc);
ripsrc.sin_addr = ip->ip_src;
key = &ip->ip_dst;
#if NPF > 0
if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
struct pf_divert *divert;
divert = pf_find_divert(m);
KASSERT(divert != NULL);
switch (divert->type) {
case PF_DIVERT_TO:
key = &divert->addr.v4;
break;
case PF_DIVERT_REPLY:
break;
default:
panic("%s: unknown divert type %d, mbuf %p, divert %p",
__func__, divert->type, m, divert);
}
}
#endif
SIMPLEQ_INIT(&inpcblist);
rw_enter_write(&rawcbtable.inpt_notify);
mtx_enter(&rawcbtable.inpt_mtx);
TAILQ_FOREACH(inp, &rawcbtable.inpt_queue, inp_queue) {
if (inp->inp_socket->so_state & SS_CANTRCVMORE)
continue;
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
continue;
#endif
if (rtable_l2(inp->inp_rtableid) !=
rtable_l2(m->m_pkthdr.ph_rtableid))
continue;
if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
continue;
if (inp->inp_laddr.s_addr &&
inp->inp_laddr.s_addr != key->s_addr)
continue;
if (inp->inp_faddr.s_addr &&
inp->inp_faddr.s_addr != ip->ip_src.s_addr)
continue;
in_pcbref(inp);
SIMPLEQ_INSERT_TAIL(&inpcblist, inp, inp_notify);
}
mtx_leave(&rawcbtable.inpt_mtx);
if (SIMPLEQ_EMPTY(&inpcblist)) {
rw_exit_write(&rawcbtable.inpt_notify);
if (ip->ip_p != IPPROTO_ICMP)
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PROTOCOL,
0, 0);
else
m_freem(m);
counters = counters_enter(&ref, ipcounters);
counters[ips_noproto]++;
counters[ips_delivered]--;
counters_leave(&ref, ipcounters);
return IPPROTO_DONE;
}
while ((inp = SIMPLEQ_FIRST(&inpcblist)) != NULL) {
struct mbuf *n, *opts = NULL;
SIMPLEQ_REMOVE_HEAD(&inpcblist, inp_notify);
if (SIMPLEQ_EMPTY(&inpcblist))
n = m;
else
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n != NULL) {
if (inp->inp_flags & INP_CONTROLOPTS ||
inp->inp_socket->so_options & SO_TIMESTAMP)
ip_savecontrol(inp, &opts, ip, n);
if (sbappendaddr(inp->inp_socket,
&inp->inp_socket->so_rcv,
sintosa(&ripsrc), n, opts) == 0) {
/* should notify about lost packet */
m_freem(n);
m_freem(opts);
} else
sorwakeup(inp->inp_socket);
}
in_pcbunref(inp);
}
rw_exit_write(&rawcbtable.inpt_notify);
return IPPROTO_DONE;
}
/*
* Generate IP header and pass packet to ip_output.
* Tack on options user may have setup with control call.
*/
int
rip_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
struct mbuf *control)
{
struct sockaddr_in *dst = satosin(dstaddr);
struct ip *ip;
struct inpcb *inp;
int flags, error;
inp = sotoinpcb(so);
flags = IP_ALLOWBROADCAST;
/*
* If the user handed us a complete IP packet, use it.
* Otherwise, allocate an mbuf for a header and fill it in.
*/
if ((inp->inp_flags & INP_HDRINCL) == 0) {
if ((m->m_pkthdr.len + sizeof(struct ip)) > IP_MAXPACKET) {
m_freem(m);
return (EMSGSIZE);
}
M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
if (!m)
return (ENOBUFS);
ip = mtod(m, struct ip *);
ip->ip_tos = inp->inp_ip.ip_tos;
ip->ip_off = htons(0);
ip->ip_p = inp->inp_ip.ip_p;
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_src.s_addr = INADDR_ANY;
ip->ip_dst = dst->sin_addr;
ip->ip_ttl = inp->inp_ip.ip_ttl ? inp->inp_ip.ip_ttl : MAXTTL;
} else {
if (m->m_pkthdr.len > IP_MAXPACKET) {
m_freem(m);
return (EMSGSIZE);
}
m = rip_chkhdr(m, inp->inp_options);
if (m == NULL)
return (EINVAL);
ip = mtod(m, struct ip *);
if (ip->ip_id == 0)
ip->ip_id = htons(ip_randomid());
dst->sin_addr = ip->ip_dst;
/* XXX prevent ip_output from overwriting header fields */
flags |= IP_RAWOUTPUT;
ipstat_inc(ips_rawout);
}
if (ip->ip_src.s_addr == INADDR_ANY) {
error = in_pcbselsrc(&ip->ip_src, dst, inp);
if (error != 0)
return (error);
}
#ifdef INET6
/*
* A thought: Even though raw IP shouldn't be able to set IPv6
* multicast options, if it does, the last parameter to
* ip_output should be guarded against v6/v4 problems.
*/
#endif
/* force routing table */
m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
#if NPF > 0
if (inp->inp_socket->so_state & SS_ISCONNECTED &&
ip->ip_p != IPPROTO_ICMP)
pf_mbuf_link_inpcb(m, inp);
#endif
error = ip_output(m, inp->inp_options, &inp->inp_route, flags,
inp->inp_moptions, inp, 0);
return (error);
}
struct mbuf *
rip_chkhdr(struct mbuf *m, struct mbuf *options)
{
struct ip *ip;
int hlen, opt, optlen, cnt;
u_char *cp;
if (m->m_pkthdr.len < sizeof(struct ip)) {
m_freem(m);
return NULL;
}
m = m_pullup(m, sizeof (struct ip));
if (m == NULL)
return NULL;
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
/* Don't allow packet length sizes that will crash. */
if (hlen < sizeof (struct ip) ||
ntohs(ip->ip_len) < hlen ||
ntohs(ip->ip_len) != m->m_pkthdr.len) {
m_freem(m);
return NULL;
}
m = m_pullup(m, hlen);
if (m == NULL)
return NULL;
ip = mtod(m, struct ip *);
if (ip->ip_v != IPVERSION) {
m_freem(m);
return NULL;
}
/*
* Don't allow both user specified and setsockopt options.
* If options are present verify them.
*/
if (hlen != sizeof(struct ip)) {
if (options) {
m_freem(m);
return NULL;
} else {
cp = (u_char *)(ip + 1);
cnt = hlen - sizeof(struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
optlen = 1;
else {
if (cnt < IPOPT_OLEN + sizeof(*cp)) {
m_freem(m);
return NULL;
}
optlen = cp[IPOPT_OLEN];
if (optlen < IPOPT_OLEN + sizeof(*cp) ||
optlen > cnt) {
m_freem(m);
return NULL;
}
}
}
}
}
return m;
}
/*
* Raw IP socket option processing.
*/
int
rip_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
int error;
if (level != IPPROTO_IP)
return (EINVAL);
switch (optname) {
case IP_HDRINCL:
error = 0;
if (op == PRCO_SETOPT) {
if (m == NULL || m->m_len < sizeof (int))
error = EINVAL;
else if (*mtod(m, int *))
inp->inp_flags |= INP_HDRINCL;
else
inp->inp_flags &= ~INP_HDRINCL;
} else {
m->m_len = sizeof(int);
*mtod(m, int *) = inp->inp_flags & INP_HDRINCL;
}
return (error);
case MRT_INIT:
case MRT_DONE:
case MRT_ADD_VIF:
case MRT_DEL_VIF:
case MRT_ADD_MFC:
case MRT_DEL_MFC:
case MRT_VERSION:
case MRT_ASSERT:
case MRT_API_SUPPORT:
case MRT_API_CONFIG:
#ifdef MROUTING
switch (op) {
case PRCO_SETOPT:
error = ip_mrouter_set(so, optname, m);
break;
case PRCO_GETOPT:
error = ip_mrouter_get(so, optname, m);
break;
default:
error = EINVAL;
break;
}
return (error);
#else
return (EOPNOTSUPP);
#endif
}
return (ip_ctloutput(op, so, level, optname, m));
}
u_long rip_sendspace = RIPSNDQ;
u_long rip_recvspace = RIPRCVQ;
int
rip_attach(struct socket *so, int proto)
{
struct inpcb *inp;
int error;
if (so->so_pcb)
panic("rip_attach");
if ((so->so_state & SS_PRIV) == 0)
return EACCES;
if (proto < 0 || proto >= IPPROTO_MAX)
return EPROTONOSUPPORT;
if ((error = soreserve(so, rip_sendspace, rip_recvspace)))
return error;
NET_ASSERT_LOCKED();
if ((error = in_pcballoc(so, &rawcbtable)))
return error;
inp = sotoinpcb(so);
inp->inp_ip.ip_p = proto;
return 0;
}
int
rip_detach(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
if (inp == NULL)
return (EINVAL);
#ifdef MROUTING
if (so == ip_mrouter[inp->inp_rtableid])
ip_mrouter_done(so);
#endif
in_pcbdetach(inp);
return (0);
}
int
rip_bind(struct socket *so, struct mbuf *nam, struct proc *p)
{
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in *addr;
int error;
soassertlocked(so);
if ((error = in_nam2sin(nam, &addr)))
return (error);
if (!((so->so_options & SO_BINDANY) ||
addr->sin_addr.s_addr == INADDR_ANY ||
addr->sin_addr.s_addr == INADDR_BROADCAST ||
in_broadcast(addr->sin_addr, inp->inp_rtableid) ||
ifa_ifwithaddr(sintosa(addr), inp->inp_rtableid)))
return (EADDRNOTAVAIL);
inp->inp_laddr = addr->sin_addr;
return (0);
}
int
rip_connect(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in *addr;
int error;
soassertlocked(so);
if ((error = in_nam2sin(nam, &addr)))
return (error);
inp->inp_faddr = addr->sin_addr;
soisconnected(so);
return (0);
}
int
rip_disconnect(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
if ((so->so_state & SS_ISCONNECTED) == 0)
return (ENOTCONN);
soisdisconnected(so);
inp->inp_faddr.s_addr = INADDR_ANY;
return (0);
}
int
rip_shutdown(struct socket *so)
{
/*
* Mark the connection as being incapable of further input.
*/
soassertlocked(so);
socantsendmore(so);
return (0);
}
int
rip_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in dst;
int error;
soassertlocked(so);
/*
* Ship a packet out. The appropriate raw output
* routine handles any massaging necessary.
*/
memset(&dst, 0, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_len = sizeof(dst);
if (so->so_state & SS_ISCONNECTED) {
if (nam) {
error = EISCONN;
goto out;
}
dst.sin_addr = inp->inp_faddr;
} else {
struct sockaddr_in *addr;
if (nam == NULL) {
error = ENOTCONN;
goto out;
}
if ((error = in_nam2sin(nam, &addr)))
goto out;
dst.sin_addr = addr->sin_addr;
}
#ifdef IPSEC
/* XXX Find an IPsec TDB */
#endif
error = rip_output(m, so, sintosa(&dst), NULL);
m = NULL;
out:
m_freem(control);
m_freem(m);
return (error);
}
int
rip_abort(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
soisdisconnected(so);
#ifdef MROUTING
if (so == ip_mrouter[inp->inp_rtableid])
ip_mrouter_done(so);
#endif
in_pcbdetach(inp);
return (0);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
/* $OpenBSD: ulpt.c,v 1.58 2021/01/29 17:12:19 sthen Exp $ */
/* $NetBSD: ulpt.c,v 1.57 2003/01/05 10:19:42 scw Exp $ */
/* $FreeBSD: src/sys/dev/usb/ulpt.c,v 1.24 1999/11/17 22:33:44 n_hibma Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Printer Class spec:
* https://www.usb.org/sites/default/files/usbprint11a021811.pdf
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usb_quirks.h>
#define LPTPRI (PZERO+8)
#define ULPT_BSIZE 16384
#ifdef ULPT_DEBUG
#define DPRINTF(x) do { if (ulptdebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (ulptdebug>(n)) printf x; } while (0)
int ulptdebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define UR_GET_DEVICE_ID 0
#define UR_GET_PORT_STATUS 1
#define UR_SOFT_RESET 2
#define LPS_NERR 0x08 /* printer no error */
#define LPS_SELECT 0x10 /* printer selected */
#define LPS_NOPAPER 0x20 /* printer out of paper */
#define LPS_INVERT (LPS_SELECT|LPS_NERR)
#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER)
struct ulpt_softc {
struct device sc_dev;
struct usbd_device *sc_udev; /* device */
struct usbd_interface *sc_iface;/* interface */
int sc_ifaceno;
int sc_out;
struct usbd_pipe *sc_out_pipe; /* bulk out pipe */
int sc_in;
struct usbd_pipe *sc_in_pipe; /* bulk in pipe */
struct usbd_xfer *sc_in_xfer1;
struct usbd_xfer *sc_in_xfer2;
u_char sc_junk[64]; /* somewhere to dump input */
u_char sc_state;
#define ULPT_OPEN 0x01 /* device is open */
#define ULPT_OBUSY 0x02 /* printer is busy doing output */
#define ULPT_INIT 0x04 /* waiting to initialize for open */
u_char sc_flags;
#define ULPT_NOPRIME 0x40 /* don't prime on open */
#define ULPT_EFIRMWARE 0x80 /* error loading firmware */
u_char sc_laststatus;
int sc_refcnt;
struct ulpt_fwdev *sc_fwdev;
};
void ulpt_disco(void *);
int ulpt_do_write(struct ulpt_softc *, struct uio *uio, int);
int ulpt_status(struct ulpt_softc *);
void ulpt_reset(struct ulpt_softc *);
int ulpt_statusmsg(u_char, struct ulpt_softc *);
/*
* Printers which need firmware uploads.
*/
void ulpt_load_firmware(struct device *);
usbd_status ulpt_ucode_loader_hp(struct ulpt_softc *);
struct ulpt_fwdev {
struct usb_devno uv_dev;
char *ucode_name;
usbd_status (*ucode_loader)(struct ulpt_softc *);
} ulpt_fwdevs[] = {
{
{ USB_VENDOR_HP, USB_PRODUCT_HP_1000 },
"ulpt-hp1000",
ulpt_ucode_loader_hp
},
{
{ USB_VENDOR_HP, USB_PRODUCT_HP_1005 },
"ulpt-hp1005",
ulpt_ucode_loader_hp
},
{
{ USB_VENDOR_HP, USB_PRODUCT_HP_1018 },
"ulpt-hp1018",
ulpt_ucode_loader_hp
},
{
{ USB_VENDOR_HP, USB_PRODUCT_HP_1020 },
"ulpt-hp1020",
ulpt_ucode_loader_hp
},
};
#if 0
void ieee1284_print_id(char *);
#endif
#define ULPTUNIT(s) (minor(s) & 0x1f)
#define ULPTFLAGS(s) (minor(s) & 0xe0)
int ulpt_match(struct device *, void *, void *);
void ulpt_attach(struct device *, struct device *, void *);
int ulpt_detach(struct device *, int);
struct cfdriver ulpt_cd = {
NULL, "ulpt", DV_DULL
};
const struct cfattach ulpt_ca = {
sizeof(struct ulpt_softc), ulpt_match, ulpt_attach, ulpt_detach
};
int
ulpt_match(struct device *parent, void *match, void *aux)
{
struct usb_attach_arg *uaa = aux;
usb_interface_descriptor_t *id;
DPRINTFN(10,("ulpt_match\n"));
if (uaa->iface == NULL)
return (UMATCH_NONE);
id = usbd_get_interface_descriptor(uaa->iface);
if (id != NULL &&
id->bInterfaceClass == UICLASS_PRINTER &&
id->bInterfaceSubClass == UISUBCLASS_PRINTER &&
((id->bInterfaceProtocol == UIPROTO_PRINTER_UNI) ||
(id->bInterfaceProtocol == UIPROTO_PRINTER_BI) ||
(id->bInterfaceProtocol == UIPROTO_PRINTER_1284)))
return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
return (UMATCH_NONE);
}
void
ulpt_load_firmware(struct device *self)
{
struct ulpt_softc *sc = (struct ulpt_softc *)self;
usbd_status err;
err = (sc->sc_fwdev->ucode_loader)(sc);
if (err != USBD_NORMAL_COMPLETION) {
sc->sc_flags |= ULPT_EFIRMWARE;
printf("%s: could not load firmware '%s'\n",
sc->sc_dev.dv_xname, sc->sc_fwdev->ucode_name);
} else
sc->sc_flags &= ~ULPT_EFIRMWARE;
}
#define ulpt_lookup(v, p) \
((struct ulpt_fwdev *)usb_lookup(ulpt_fwdevs, v, p))
void
ulpt_attach(struct device *parent, struct device *self, void *aux)
{
struct ulpt_softc *sc = (struct ulpt_softc *)self;
struct usb_attach_arg *uaa = aux;
struct usbd_device *dev = uaa->device;
struct usbd_interface *iface = uaa->iface;
usb_interface_descriptor_t *ifcd = usbd_get_interface_descriptor(iface);
usb_interface_descriptor_t *id, *iend;
usb_config_descriptor_t *cdesc;
usbd_status err;
usb_endpoint_descriptor_t *ed;
int i, altno;
DPRINTFN(10,("ulpt_attach: sc=%p\n", sc));
//printf("%s: iclass %d/%d\n", sc->sc_dev.dv_xname,
// ifcd->bInterfaceClass, ifcd->bInterfaceSubClass);
/* XXX
* Stepping through the alternate settings needs to be abstracted out.
*/
cdesc = usbd_get_config_descriptor(dev);
if (cdesc == NULL) {
printf("%s: failed to get configuration descriptor\n",
sc->sc_dev.dv_xname);
return;
}
iend = (usb_interface_descriptor_t *)
((char *)cdesc + UGETW(cdesc->wTotalLength));
#ifdef DIAGNOSTIC
if (ifcd < (usb_interface_descriptor_t *)cdesc ||
ifcd >= iend)
panic("ulpt: iface desc out of range");
#endif
/* Step through all the descriptors looking for bidir mode */
for (id = ifcd, altno = 0;
id < iend;
id = (void *)((char *)id + id->bLength)) {
if (id->bDescriptorType == UDESC_INTERFACE &&
id->bInterfaceNumber == ifcd->bInterfaceNumber) {
if (id->bInterfaceClass == UICLASS_PRINTER &&
id->bInterfaceSubClass == UISUBCLASS_PRINTER &&
(id->bInterfaceProtocol == UIPROTO_PRINTER_BI /*||
id->bInterfaceProtocol == UIPROTO_PRINTER_1284*/))
goto found;
altno++;
}
}
id = ifcd; /* not found, use original */
found:
if (id != ifcd) {
/* Found a new bidir setting */
DPRINTF(("ulpt_attach: set altno = %d\n", altno));
err = usbd_set_interface(iface, altno);
if (err) {
printf("%s: setting alternate interface failed\n",
sc->sc_dev.dv_xname);
usbd_deactivate(sc->sc_udev);
return;
}
}
sc->sc_in = -1;
sc->sc_out = -1;
id = usbd_get_interface_descriptor(iface);
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(iface, i);
if (ed == NULL) {
printf("%s: couldn't get ep %d\n",
sc->sc_dev.dv_xname, i);
return;
}
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
sc->sc_in = ed->bEndpointAddress;
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
sc->sc_out = ed->bEndpointAddress;
}
}
if (sc->sc_out == -1) {
printf("%s: could not find bulk out endpoint\n",
sc->sc_dev.dv_xname);
usbd_deactivate(sc->sc_udev);
return;
}
if (usbd_get_quirks(dev)->uq_flags & UQ_BROKEN_BIDIR) {
/* This device doesn't handle reading properly. */
sc->sc_in = -1;
}
printf("%s: using %s-directional mode\n", sc->sc_dev.dv_xname,
sc->sc_in >= 0 ? "bi" : "uni");
DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_out));
sc->sc_iface = iface;
sc->sc_ifaceno = id->bInterfaceNumber;
sc->sc_udev = dev;
/* maybe the device needs firmware */
sc->sc_fwdev = ulpt_lookup(uaa->vendor, uaa->product);
if (sc->sc_fwdev)
config_mountroot(self, ulpt_load_firmware);
#if 0
/*
* This code is disabled because for some mysterious reason it causes
* printing not to work. But only sometimes, and mostly with
* UHCI and less often with OHCI. *sigh*
*/
{
usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
usb_device_request_t req;
int len, alen;
req.bmRequestType = UT_READ_CLASS_INTERFACE;
req.bRequest = UR_GET_DEVICE_ID;
USETW(req.wValue, cd->bConfigurationValue);
USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting);
USETW(req.wLength, DEVINFOSIZE - 1);
err = usbd_do_request_flags(dev, &req, devinfop, USBD_SHORT_XFER_OK,
&alen, USBD_DEFAULT_TIMEOUT);
if (err) {
printf("%s: cannot get device id\n", sc->sc_dev.dv_xname);
} else if (alen <= 2) {
printf("%s: empty device id, no printer connected?\n",
sc->sc_dev.dv_xname);
} else {
/* devinfop now contains an IEEE-1284 device ID */
len = ((devinfop[0] & 0xff) << 8) | (devinfop[1] & 0xff);
if (len > DEVINFOSIZE - 3)
len = DEVINFOSIZE - 3;
devinfo[len] = 0;
printf("%s: device id <", sc->sc_dev.dv_xname);
ieee1284_print_id(devinfop+2);
printf(">\n");
}
}
#endif
}
int
ulpt_detach(struct device *self, int flags)
{
struct ulpt_softc *sc = (struct ulpt_softc *)self;
int s;
int maj, mn;
DPRINTF(("ulpt_detach: sc=%p\n", sc));
if (sc->sc_out_pipe != NULL)
usbd_abort_pipe(sc->sc_out_pipe);
if (sc->sc_in_pipe != NULL)
usbd_abort_pipe(sc->sc_in_pipe);
s = splusb();
if (--sc->sc_refcnt >= 0) {
/* There is noone to wake, aborting the pipe is enough */
/* Wait for processes to go away. */
usb_detach_wait(&sc->sc_dev);
}
splx(s);
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == ulptopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
vdevgone(maj, mn | ULPT_NOPRIME , mn | ULPT_NOPRIME, VCHR);
return (0);
}
int
ulpt_status(struct ulpt_softc *sc)
{
usb_device_request_t req;
usbd_status err;
u_char status;
req.bmRequestType = UT_READ_CLASS_INTERFACE;
req.bRequest = UR_GET_PORT_STATUS;
USETW(req.wValue, 0);
USETW(req.wIndex, sc->sc_ifaceno);
USETW(req.wLength, 1);
err = usbd_do_request(sc->sc_udev, &req, &status);
DPRINTFN(1, ("ulpt_status: status=0x%02x err=%d\n", status, err));
if (!err)
return (status);
else
return (0);
}
void
ulpt_reset(struct ulpt_softc *sc)
{
usb_device_request_t req;
DPRINTFN(1, ("ulpt_reset\n"));
req.bRequest = UR_SOFT_RESET;
USETW(req.wValue, 0);
USETW(req.wIndex, sc->sc_ifaceno);
USETW(req.wLength, 0);
/*
* There was a mistake in the USB printer 1.0 spec that gave the
* request type as UT_WRITE_CLASS_OTHER; it should have been
* UT_WRITE_CLASS_INTERFACE. Many printers use the old one,
* so we try both.
*/
req.bmRequestType = UT_WRITE_CLASS_OTHER;
if (usbd_do_request(sc->sc_udev, &req, 0)) { /* 1.0 */
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
(void)usbd_do_request(sc->sc_udev, &req, 0); /* 1.1 */
}
}
static void
ulpt_input(struct usbd_xfer *xfer, void *priv, usbd_status status)
{
struct ulpt_softc *sc = priv;
DPRINTFN(2,("ulpt_input: got some data\n"));
/* Do it again. */
if (xfer == sc->sc_in_xfer1)
usbd_transfer(sc->sc_in_xfer2);
else
usbd_transfer(sc->sc_in_xfer1);
}
int ulptusein = 1;
/*
* Reset the printer, then wait until it's selected and not busy.
*/
int
ulptopen(dev_t dev, int flag, int mode, struct proc *p)
{
u_char flags = ULPTFLAGS(dev);
struct ulpt_softc *sc;
usbd_status err;
int error;
if (ULPTUNIT(dev) >= ulpt_cd.cd_ndevs)
return (ENXIO);
sc = ulpt_cd.cd_devs[ULPTUNIT(dev)];
if (sc == NULL)
return (ENXIO);
if (sc == NULL || sc->sc_iface == NULL || usbd_is_dying(sc->sc_udev))
return (ENXIO);
if (sc->sc_state)
return (EBUSY);
/* If a previous attempt to load firmware failed, retry. */
if (sc->sc_flags & ULPT_EFIRMWARE) {
ulpt_load_firmware(&sc->sc_dev);
if (sc->sc_flags & ULPT_EFIRMWARE)
return (EIO);
}
sc->sc_state = ULPT_INIT;
sc->sc_flags = flags;
DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags));
error = 0;
sc->sc_refcnt++;
if ((flags & ULPT_NOPRIME) == 0) {
ulpt_reset(sc);
if (usbd_is_dying(sc->sc_udev)) {
error = ENXIO;
sc->sc_state = 0;
goto done;
}
}
err = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe);
if (err) {
sc->sc_state = 0;
error = EIO;
goto done;
}
if (ulptusein && sc->sc_in != -1) {
DPRINTF(("ulpt_open: open input pipe\n"));
err = usbd_open_pipe(sc->sc_iface, sc->sc_in,0,&sc->sc_in_pipe);
if (err) {
error = EIO;
usbd_close_pipe(sc->sc_out_pipe);
sc->sc_out_pipe = NULL;
sc->sc_state = 0;
goto done;
}
sc->sc_in_xfer1 = usbd_alloc_xfer(sc->sc_udev);
sc->sc_in_xfer2 = usbd_alloc_xfer(sc->sc_udev);
if (sc->sc_in_xfer1 == NULL || sc->sc_in_xfer2 == NULL) {
error = ENOMEM;
if (sc->sc_in_xfer1 != NULL) {
usbd_free_xfer(sc->sc_in_xfer1);
sc->sc_in_xfer1 = NULL;
}
if (sc->sc_in_xfer2 != NULL) {
usbd_free_xfer(sc->sc_in_xfer2);
sc->sc_in_xfer2 = NULL;
}
usbd_close_pipe(sc->sc_out_pipe);
sc->sc_out_pipe = NULL;
usbd_close_pipe(sc->sc_in_pipe);
sc->sc_in_pipe = NULL;
sc->sc_state = 0;
goto done;
}
usbd_setup_xfer(sc->sc_in_xfer1, sc->sc_in_pipe, sc,
sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK,
USBD_NO_TIMEOUT, ulpt_input);
usbd_setup_xfer(sc->sc_in_xfer2, sc->sc_in_pipe, sc,
sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK,
USBD_NO_TIMEOUT, ulpt_input);
usbd_transfer(sc->sc_in_xfer1); /* ignore failed start */
}
sc->sc_state = ULPT_OPEN;
done:
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
DPRINTF(("ulptopen: done, error=%d\n", error));
return (error);
}
int
ulpt_statusmsg(u_char status, struct ulpt_softc *sc)
{
u_char new;
status = (status ^ LPS_INVERT) & LPS_MASK;
new = status & ~sc->sc_laststatus;
sc->sc_laststatus = status;
if (new & LPS_SELECT)
log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
else if (new & LPS_NOPAPER)
log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
else if (new & LPS_NERR)
log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
return (status);
}
int
ulptclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct ulpt_softc *sc;
sc = ulpt_cd.cd_devs[ULPTUNIT(dev)];
if (sc->sc_state != ULPT_OPEN)
/* We are being forced to close before the open completed. */
return (0);
if (sc->sc_out_pipe != NULL) {
usbd_close_pipe(sc->sc_out_pipe);
sc->sc_out_pipe = NULL;
}
if (sc->sc_in_pipe != NULL) {
usbd_close_pipe(sc->sc_in_pipe);
sc->sc_in_pipe = NULL;
if (sc->sc_in_xfer1 != NULL) {
usbd_free_xfer(sc->sc_in_xfer1);
sc->sc_in_xfer1 = NULL;
}
if (sc->sc_in_xfer2 != NULL) {
usbd_free_xfer(sc->sc_in_xfer2);
sc->sc_in_xfer2 = NULL;
}
}
sc->sc_state = 0;
DPRINTF(("ulptclose: closed\n"));
return (0);
}
int
ulpt_do_write(struct ulpt_softc *sc, struct uio *uio, int flags)
{
size_t n;
int error = 0;
void *bufp;
struct usbd_xfer *xfer;
usbd_status err;
DPRINTF(("ulptwrite\n"));
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == NULL)
return (ENOMEM);
bufp = usbd_alloc_buffer(xfer, ULPT_BSIZE);
if (bufp == NULL) {
usbd_free_xfer(xfer);
return (ENOMEM);
}
while ((n = ulmin(ULPT_BSIZE, uio->uio_resid)) != 0) {
ulpt_statusmsg(ulpt_status(sc), sc);
error = uiomove(bufp, n, uio);
if (error)
break;
DPRINTFN(1, ("ulptwrite: transfer %zu bytes\n", n));
usbd_setup_xfer(xfer, sc->sc_out_pipe, 0, bufp, n,
USBD_NO_COPY | USBD_SYNCHRONOUS | USBD_CATCH, 0, NULL);
err = usbd_transfer(xfer);
if (err) {
usbd_clear_endpoint_stall(sc->sc_out_pipe);
DPRINTF(("ulptwrite: error=%d\n", err));
error = EIO;
break;
}
}
usbd_free_xfer(xfer);
return (error);
}
int
ulptwrite(dev_t dev, struct uio *uio, int flags)
{
struct ulpt_softc *sc;
int error;
sc = ulpt_cd.cd_devs[ULPTUNIT(dev)];
if (usbd_is_dying(sc->sc_udev) || (sc->sc_flags & ULPT_EFIRMWARE))
return (EIO);
sc->sc_refcnt++;
error = ulpt_do_write(sc, uio, flags);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
usbd_status
ulpt_ucode_loader_hp(struct ulpt_softc *sc)
{
usbd_status error;
int load_error;
uint8_t *ucode;
uint32_t len;
size_t ucode_size;
const char *ucode_name = sc->sc_fwdev->ucode_name;
int offset = 0, remain;
struct usbd_xfer *xfer;
void *bufp;
/* open microcode file */
load_error = loadfirmware(ucode_name, &ucode, &ucode_size);
if (load_error != 0) {
printf("%s: failed loadfirmware of file %s (error %d)\n",
sc->sc_dev.dv_xname, ucode_name, load_error);
return (USBD_INVAL);
}
/* upload microcode */
error = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe);
if (error)
goto free_ucode;
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == NULL)
goto close_pipe;
bufp = usbd_alloc_buffer(xfer, ULPT_BSIZE);
if (bufp == NULL) {
error = USBD_NOMEM;
goto free_xfer;
}
remain = ucode_size;
while (remain > 0) {
len = min(remain, ULPT_BSIZE);
memcpy(bufp, &ucode[offset], len);
usbd_setup_xfer(xfer, sc->sc_out_pipe, 0, bufp, len,
USBD_NO_COPY | USBD_SYNCHRONOUS, 0, NULL);
error = usbd_transfer(xfer);
if (error != USBD_NORMAL_COMPLETION) {
usbd_clear_endpoint_stall(sc->sc_out_pipe);
printf("%s: ucode upload error=%s!\n",
sc->sc_dev.dv_xname, usbd_errstr(error));
break;
}
DPRINTF(("%s: uploaded %d bytes ucode\n",
sc->sc_dev.dv_xname, len));
offset += len;
remain -= len;
}
free_xfer:
usbd_free_xfer(xfer);
close_pipe:
usbd_close_pipe(sc->sc_out_pipe);
sc->sc_out_pipe = NULL;
free_ucode:
free(ucode, M_DEVBUF, ucode_size);
return (error);
}
#if 0
/* XXX This does not belong here. */
/*
* Print select parts of a IEEE 1284 device ID.
*/
void
ieee1284_print_id(char *str)
{
char *p, *q;
for (p = str-1; p; p = strchr(p, ';')) {
p++; /* skip ';' */
if (strncmp(p, "MFG:", 4) == 0 ||
strncmp(p, "MANUFACTURER:", 14) == 0 ||
strncmp(p, "MDL:", 4) == 0 ||
strncmp(p, "MODEL:", 6) == 0) {
q = strchr(p, ';');
if (q)
printf("%.*s", (int)(q - p + 1), p);
}
}
}
#endif
47
47
46
2
3
3
46
2
2
2
2
2
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
/* $OpenBSD: subr_log.c,v 1.75 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: subr_log.c,v 1.11 1996/03/30 22:24:44 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)subr_log.c 8.1 (Berkeley) 6/10/93
*/
/*
* Error log buffer for kernel printf's.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/ioctl.h>
#include <sys/msgbuf.h>
#include <sys/file.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/fcntl.h>
#include <sys/mutex.h>
#include <sys/timeout.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <dev/cons.h>
#define LOG_RDPRI (PZERO + 1)
#define LOG_TICK 50 /* log tick interval in msec */
#define LOG_ASYNC 0x04
#define LOG_RDWAIT 0x08
/*
* Locking:
* L log_mtx
*/
struct logsoftc {
int sc_state; /* [L] see above for possibilities */
struct selinfo sc_selp; /* process waiting on select call */
struct sigio_ref sc_sigio; /* async I/O registration */
int sc_need_wakeup; /* if set, wake up waiters */
struct timeout sc_tick; /* wakeup poll timeout */
} logsoftc;
int log_open; /* also used in log() */
int msgbufmapped; /* is the message buffer mapped */
struct msgbuf *msgbufp; /* the mapped buffer, itself. */
struct msgbuf *consbufp; /* console message buffer. */
struct file *syslogf;
struct rwlock syslogf_rwlock = RWLOCK_INITIALIZER("syslogf");
/*
* Lock that serializes access to log message buffers.
* This should be kept as a leaf lock in order not to constrain where
* printf(9) can be used.
*/
struct mutex log_mtx =
MUTEX_INITIALIZER_FLAGS(IPL_HIGH, "logmtx", MTX_NOWITNESS);
void filt_logrdetach(struct knote *kn);
int filt_logread(struct knote *kn, long hint);
const struct filterops logread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_logrdetach,
.f_event = filt_logread,
};
int dosendsyslog(struct proc *, const char *, size_t, int, enum uio_seg);
void logtick(void *);
size_t msgbuf_getlen(struct msgbuf *);
void msgbuf_putchar_locked(struct msgbuf *, const char);
void
initmsgbuf(caddr_t buf, size_t bufsize)
{
struct msgbuf *mbp;
long new_bufs;
/* Sanity-check the given size. */
if (bufsize < sizeof(struct msgbuf))
return;
mbp = msgbufp = (struct msgbuf *)buf;
new_bufs = bufsize - offsetof(struct msgbuf, msg_bufc);
if ((mbp->msg_magic != MSG_MAGIC) || (mbp->msg_bufs != new_bufs) ||
(mbp->msg_bufr < 0) || (mbp->msg_bufr >= mbp->msg_bufs) ||
(mbp->msg_bufx < 0) || (mbp->msg_bufx >= mbp->msg_bufs)) {
/*
* If the buffer magic number is wrong, has changed
* size (which shouldn't happen often), or is
* internally inconsistent, initialize it.
*/
memset(buf, 0, bufsize);
mbp->msg_magic = MSG_MAGIC;
mbp->msg_bufs = new_bufs;
}
/*
* Always start new buffer data on a new line.
* Avoid using log_mtx because mutexes do not work during early boot
* on some architectures.
*/
if (mbp->msg_bufx > 0 && mbp->msg_bufc[mbp->msg_bufx - 1] != '\n')
msgbuf_putchar_locked(mbp, '\n');
/* mark it as ready for use. */
msgbufmapped = 1;
}
void
initconsbuf(void)
{
/* Set up a buffer to collect /dev/console output */
consbufp = malloc(CONSBUFSIZE, M_TTYS, M_WAITOK | M_ZERO);
consbufp->msg_magic = MSG_MAGIC;
consbufp->msg_bufs = CONSBUFSIZE - offsetof(struct msgbuf, msg_bufc);
}
void
msgbuf_putchar(struct msgbuf *mbp, const char c)
{
if (mbp->msg_magic != MSG_MAGIC)
/* Nothing we can do */
return;
mtx_enter(&log_mtx);
msgbuf_putchar_locked(mbp, c);
mtx_leave(&log_mtx);
}
void
msgbuf_putchar_locked(struct msgbuf *mbp, const char c)
{
mbp->msg_bufc[mbp->msg_bufx++] = c;
if (mbp->msg_bufx < 0 || mbp->msg_bufx >= mbp->msg_bufs)
mbp->msg_bufx = 0;
/* If the buffer is full, keep the most recent data. */
if (mbp->msg_bufr == mbp->msg_bufx) {
if (++mbp->msg_bufr >= mbp->msg_bufs)
mbp->msg_bufr = 0;
mbp->msg_bufd++;
}
}
size_t
msgbuf_getlen(struct msgbuf *mbp)
{
long len;
mtx_enter(&log_mtx);
len = mbp->msg_bufx - mbp->msg_bufr;
if (len < 0)
len += mbp->msg_bufs;
mtx_leave(&log_mtx);
return (len);
}
int
logopen(dev_t dev, int flags, int mode, struct proc *p)
{
if (log_open)
return (EBUSY);
log_open = 1;
sigio_init(&logsoftc.sc_sigio);
timeout_set(&logsoftc.sc_tick, logtick, NULL);
timeout_add_msec(&logsoftc.sc_tick, LOG_TICK);
return (0);
}
int
logclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct file *fp;
rw_enter_write(&syslogf_rwlock);
fp = syslogf;
syslogf = NULL;
rw_exit(&syslogf_rwlock);
if (fp)
FRELE(fp, p);
log_open = 0;
timeout_del(&logsoftc.sc_tick);
logsoftc.sc_state = 0;
sigio_free(&logsoftc.sc_sigio);
return (0);
}
int
logread(dev_t dev, struct uio *uio, int flag)
{
struct sleep_state sls;
struct msgbuf *mbp = msgbufp;
size_t l, rpos;
int error = 0;
mtx_enter(&log_mtx);
while (mbp->msg_bufr == mbp->msg_bufx) {
if (flag & IO_NDELAY) {
error = EWOULDBLOCK;
goto out;
}
logsoftc.sc_state |= LOG_RDWAIT;
mtx_leave(&log_mtx);
/*
* Set up and enter sleep manually instead of using msleep()
* to keep log_mtx as a leaf lock.
*/
sleep_setup(&sls, mbp, LOG_RDPRI | PCATCH, "klog", 0);
error = sleep_finish(&sls, logsoftc.sc_state & LOG_RDWAIT);
mtx_enter(&log_mtx);
if (error)
goto out;
}
if (mbp->msg_bufd > 0) {
char buf[64];
long ndropped;
ndropped = mbp->msg_bufd;
mtx_leave(&log_mtx);
l = snprintf(buf, sizeof(buf),
"<%d>klog: dropped %ld byte%s, message buffer full\n",
LOG_KERN|LOG_WARNING, ndropped,
ndropped == 1 ? "" : "s");
error = uiomove(buf, ulmin(l, sizeof(buf) - 1), uio);
mtx_enter(&log_mtx);
if (error)
goto out;
mbp->msg_bufd -= ndropped;
}
while (uio->uio_resid > 0) {
if (mbp->msg_bufx >= mbp->msg_bufr)
l = mbp->msg_bufx - mbp->msg_bufr;
else
l = mbp->msg_bufs - mbp->msg_bufr;
l = ulmin(l, uio->uio_resid);
if (l == 0)
break;
rpos = mbp->msg_bufr;
mtx_leave(&log_mtx);
/* Ignore that concurrent readers may consume the same data. */
error = uiomove(&mbp->msg_bufc[rpos], l, uio);
mtx_enter(&log_mtx);
if (error)
break;
mbp->msg_bufr += l;
if (mbp->msg_bufr < 0 || mbp->msg_bufr >= mbp->msg_bufs)
mbp->msg_bufr = 0;
}
out:
mtx_leave(&log_mtx);
return (error);
}
int
logkqfilter(dev_t dev, struct knote *kn)
{
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &logsoftc.sc_selp.si_note;
kn->kn_fop = &logread_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = (void *)msgbufp;
s = splhigh();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
void
filt_logrdetach(struct knote *kn)
{
int s;
s = splhigh();
klist_remove_locked(&logsoftc.sc_selp.si_note, kn);
splx(s);
}
int
filt_logread(struct knote *kn, long hint)
{
struct msgbuf *mbp = kn->kn_hook;
kn->kn_data = msgbuf_getlen(mbp);
return (kn->kn_data != 0);
}
void
logwakeup(void)
{
/*
* The actual wakeup has to be deferred because logwakeup() can be
* called in very varied contexts.
* Keep the print routines usable in as many situations as possible
* by not using locking here.
*/
/*
* Ensure that preceding stores become visible to other CPUs
* before the flag.
*/
membar_producer();
logsoftc.sc_need_wakeup = 1;
}
void
logtick(void *arg)
{
int state;
if (!log_open)
return;
if (!logsoftc.sc_need_wakeup)
goto out;
logsoftc.sc_need_wakeup = 0;
/*
* sc_need_wakeup has to be cleared before handling the wakeup.
* Visiting log_mtx ensures the proper order.
*/
mtx_enter(&log_mtx);
state = logsoftc.sc_state;
if (logsoftc.sc_state & LOG_RDWAIT)
logsoftc.sc_state &= ~LOG_RDWAIT;
mtx_leave(&log_mtx);
selwakeup(&logsoftc.sc_selp);
if (state & LOG_ASYNC)
pgsigio(&logsoftc.sc_sigio, SIGIO, 0);
if (state & LOG_RDWAIT)
wakeup(msgbufp);
out:
timeout_add_msec(&logsoftc.sc_tick, LOG_TICK);
}
int
logioctl(dev_t dev, u_long com, caddr_t data, int flag, struct proc *p)
{
struct file *fp, *newfp;
int error;
switch (com) {
/* return number of characters immediately available */
case FIONREAD:
*(int *)data = (int)msgbuf_getlen(msgbufp);
break;
case FIONBIO:
break;
case FIOASYNC:
mtx_enter(&log_mtx);
if (*(int *)data)
logsoftc.sc_state |= LOG_ASYNC;
else
logsoftc.sc_state &= ~LOG_ASYNC;
mtx_leave(&log_mtx);
break;
case FIOSETOWN:
case TIOCSPGRP:
return (sigio_setown(&logsoftc.sc_sigio, com, data));
case FIOGETOWN:
case TIOCGPGRP:
sigio_getown(&logsoftc.sc_sigio, com, data);
break;
case LIOCSFD:
if ((error = suser(p)) != 0)
return (error);
if ((error = getsock(p, *(int *)data, &newfp)) != 0)
return (error);
rw_enter_write(&syslogf_rwlock);
fp = syslogf;
syslogf = newfp;
rw_exit(&syslogf_rwlock);
if (fp)
FRELE(fp, p);
break;
default:
return (ENOTTY);
}
return (0);
}
/*
* If syslogd is not running, temporarily store a limited amount of messages
* in kernel. After log stash is full, drop messages and count them. When
* syslogd is available again, next log message will flush the stashed
* messages and insert a message with drop count. Calls to malloc(9) and
* copyin(9) may sleep, protect data structures with rwlock.
*/
#define LOGSTASH_SIZE 100
struct logstash_message {
char *lgs_buffer;
size_t lgs_size;
} logstash_messages[LOGSTASH_SIZE];
struct logstash_message *logstash_in = &logstash_messages[0];
struct logstash_message *logstash_out = &logstash_messages[0];
struct rwlock logstash_rwlock = RWLOCK_INITIALIZER("logstash");
int logstash_dropped, logstash_error, logstash_pid;
int logstash_insert(const char *, size_t, int, pid_t);
void logstash_remove(void);
int logstash_sendsyslog(struct proc *);
static inline int
logstash_full(void)
{
rw_assert_anylock(&logstash_rwlock);
return logstash_out->lgs_buffer != NULL &&
logstash_in == logstash_out;
}
static inline void
logstash_increment(struct logstash_message **msg)
{
rw_assert_wrlock(&logstash_rwlock);
KASSERT((*msg) >= &logstash_messages[0]);
KASSERT((*msg) < &logstash_messages[LOGSTASH_SIZE]);
if ((*msg) == &logstash_messages[LOGSTASH_SIZE - 1])
(*msg) = &logstash_messages[0];
else
(*msg)++;
}
int
logstash_insert(const char *buf, size_t nbyte, int logerror, pid_t pid)
{
int error;
rw_enter_write(&logstash_rwlock);
if (logstash_full()) {
if (logstash_dropped == 0) {
logstash_error = logerror;
logstash_pid = pid;
}
logstash_dropped++;
rw_exit(&logstash_rwlock);
return (0);
}
logstash_in->lgs_buffer = malloc(nbyte, M_LOG, M_WAITOK);
error = copyin(buf, logstash_in->lgs_buffer, nbyte);
if (error) {
free(logstash_in->lgs_buffer, M_LOG, nbyte);
logstash_in->lgs_buffer = NULL;
rw_exit(&logstash_rwlock);
return (error);
}
logstash_in->lgs_size = nbyte;
logstash_increment(&logstash_in);
rw_exit(&logstash_rwlock);
return (0);
}
void
logstash_remove(void)
{
rw_assert_wrlock(&logstash_rwlock);
KASSERT(logstash_out->lgs_buffer != NULL);
free(logstash_out->lgs_buffer, M_LOG, logstash_out->lgs_size);
logstash_out->lgs_buffer = NULL;
logstash_increment(&logstash_out);
/* Insert dropped message in sequence where messages were dropped. */
if (logstash_dropped) {
size_t l, nbyte;
char buf[80];
l = snprintf(buf, sizeof(buf),
"<%d>sendsyslog: dropped %d message%s, error %d, pid %d",
LOG_KERN|LOG_WARNING, logstash_dropped,
logstash_dropped == 1 ? "" : "s",
logstash_error, logstash_pid);
logstash_dropped = 0;
logstash_error = 0;
logstash_pid = 0;
/* Cannot fail, we have just freed a slot. */
KASSERT(!logstash_full());
nbyte = ulmin(l, sizeof(buf) - 1);
logstash_in->lgs_buffer = malloc(nbyte, M_LOG, M_WAITOK);
memcpy(logstash_in->lgs_buffer, buf, nbyte);
logstash_in->lgs_size = nbyte;
logstash_increment(&logstash_in);
}
}
int
logstash_sendsyslog(struct proc *p)
{
int error;
rw_enter_write(&logstash_rwlock);
while (logstash_out->lgs_buffer != NULL) {
error = dosendsyslog(p, logstash_out->lgs_buffer,
logstash_out->lgs_size, 0, UIO_SYSSPACE);
if (error) {
rw_exit(&logstash_rwlock);
return (error);
}
logstash_remove();
}
rw_exit(&logstash_rwlock);
return (0);
}
/*
* Send syslog(3) message from userland to socketpair(2) created by syslogd(8).
* Store message in kernel log stash for later if syslogd(8) is not available
* or sending fails. Send to console if LOG_CONS is set and syslogd(8) socket
* does not exist.
*/
int
sys_sendsyslog(struct proc *p, void *v, register_t *retval)
{
struct sys_sendsyslog_args /* {
syscallarg(const char *) buf;
syscallarg(size_t) nbyte;
syscallarg(int) flags;
} */ *uap = v;
size_t nbyte;
int error;
nbyte = SCARG(uap, nbyte);
if (nbyte > LOG_MAXLINE)
nbyte = LOG_MAXLINE;
logstash_sendsyslog(p);
error = dosendsyslog(p, SCARG(uap, buf), nbyte, SCARG(uap, flags),
UIO_USERSPACE);
if (error && error != EFAULT)
logstash_insert(SCARG(uap, buf), nbyte, error, p->p_p->ps_pid);
return (error);
}
int
dosendsyslog(struct proc *p, const char *buf, size_t nbyte, int flags,
enum uio_seg sflg)
{
#ifdef KTRACE
struct iovec ktriov;
#endif
struct file *fp;
char pri[6], *kbuf;
struct iovec aiov;
struct uio auio;
size_t i, len;
int error;
/* Global variable syslogf may change during sleep, use local copy. */
rw_enter_read(&syslogf_rwlock);
fp = syslogf;
if (fp)
FREF(fp);
rw_exit(&syslogf_rwlock);
if (fp == NULL) {
if (!ISSET(flags, LOG_CONS))
return (ENOTCONN);
/*
* Strip off syslog priority when logging to console.
* LOG_PRIMASK | LOG_FACMASK is 0x03ff, so at most 4
* decimal digits may appear in priority as <1023>.
*/
len = MIN(nbyte, sizeof(pri));
if (sflg == UIO_USERSPACE) {
if ((error = copyin(buf, pri, len)))
return (error);
} else
memcpy(pri, buf, len);
if (0 < len && pri[0] == '<') {
for (i = 1; i < len; i++) {
if (pri[i] < '0' || pri[i] > '9')
break;
}
if (i < len && pri[i] == '>') {
i++;
/* There must be at least one digit <0>. */
if (i >= 3) {
buf += i;
nbyte -= i;
}
}
}
}
aiov.iov_base = (char *)buf;
aiov.iov_len = nbyte;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_segflg = sflg;
auio.uio_rw = UIO_WRITE;
auio.uio_procp = p;
auio.uio_offset = 0;
auio.uio_resid = aiov.iov_len;
#ifdef KTRACE
if (sflg == UIO_USERSPACE && KTRPOINT(p, KTR_GENIO))
ktriov = aiov;
else
ktriov.iov_len = 0;
#endif
len = auio.uio_resid;
if (fp) {
int flags = (fp->f_flag & FNONBLOCK) ? MSG_DONTWAIT : 0;
error = sosend(fp->f_data, NULL, &auio, NULL, NULL, flags);
if (error == 0)
len -= auio.uio_resid;
} else {
KERNEL_LOCK();
if (constty || cn_devvp) {
error = cnwrite(0, &auio, 0);
if (error == 0)
len -= auio.uio_resid;
aiov.iov_base = "\r\n";
aiov.iov_len = 2;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_procp = p;
auio.uio_offset = 0;
auio.uio_resid = aiov.iov_len;
cnwrite(0, &auio, 0);
} else {
/* XXX console redirection breaks down... */
if (sflg == UIO_USERSPACE) {
kbuf = malloc(len, M_TEMP, M_WAITOK);
error = copyin(aiov.iov_base, kbuf, len);
} else {
kbuf = aiov.iov_base;
error = 0;
}
if (error == 0)
for (i = 0; i < len; i++) {
if (kbuf[i] == '\0')
break;
cnputc(kbuf[i]);
auio.uio_resid--;
}
if (sflg == UIO_USERSPACE)
free(kbuf, M_TEMP, len);
if (error == 0)
len -= auio.uio_resid;
cnputc('\n');
}
KERNEL_UNLOCK();
}
#ifdef KTRACE
if (error == 0 && ktriov.iov_len != 0)
ktrgenio(p, -1, UIO_WRITE, &ktriov, len);
#endif
if (fp)
FRELE(fp, p);
else if (error != EFAULT)
error = ENOTCONN;
return (error);
}
482
481
479
7
3
4
2
2
11
347
81
110
1
23
97
22
112
95
26
20
28
6
22
79
2
482
482
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
/* $OpenBSD: ffs_balloc.c,v 1.45 2019/07/19 00:24:31 cheloha Exp $ */
/* $NetBSD: ffs_balloc.c,v 1.3 1996/02/09 22:22:21 christos Exp $ */
/*
* Copyright (c) 2002 Networks Associates Technology, Inc.
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Marshall
* Kirk McKusick and Network Associates Laboratories, the Security
* Research Division of Network Associates, Inc. under DARPA/SPAWAR
* contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
* research program.
*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
int ffs1_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **);
#ifdef FFS2
int ffs2_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **);
#endif
/*
* Balloc defines the structure of file system storage
* by allocating the physical blocks on a device given
* the inode and the logical block number in a file.
*/
int
ffs1_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred,
int flags, struct buf **bpp)
{
daddr_t lbn, nb, newb, pref;
struct fs *fs;
struct buf *bp, *nbp;
struct vnode *vp;
struct proc *p;
struct indir indirs[NIADDR + 2];
int32_t *bap;
int deallocated, osize, nsize, num, i, error;
int32_t *allocib, *blkp, *allocblk, allociblk[NIADDR+1];
int unwindidx = -1;
vp = ITOV(ip);
fs = ip->i_fs;
p = curproc;
lbn = lblkno(fs, startoffset);
size = blkoff(fs, startoffset) + size;
if (size > fs->fs_bsize)
panic("ffs1_balloc: blk too big");
if (bpp != NULL)
*bpp = NULL;
if (lbn < 0)
return (EFBIG);
/*
* If the next write will extend the file into a new block,
* and the file is currently composed of a fragment
* this fragment has to be extended to be a full block.
*/
nb = lblkno(fs, ip->i_ffs1_size);
if (nb < NDADDR && nb < lbn) {
osize = blksize(fs, ip, nb);
if (osize < fs->fs_bsize && osize > 0) {
error = ffs_realloccg(ip, nb,
ffs1_blkpref(ip, nb, (int)nb, &ip->i_ffs1_db[0]),
osize, (int)fs->fs_bsize, cred, bpp, &newb);
if (error)
return (error);
if (DOINGSOFTDEP(vp))
softdep_setup_allocdirect(ip, nb, newb,
ip->i_ffs1_db[nb], fs->fs_bsize, osize,
bpp ? *bpp : NULL);
ip->i_ffs1_size = lblktosize(fs, nb + 1);
uvm_vnp_setsize(vp, ip->i_ffs1_size);
ip->i_ffs1_db[nb] = newb;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (bpp != NULL) {
if (flags & B_SYNC)
bwrite(*bpp);
else
bawrite(*bpp);
}
}
}
/*
* The first NDADDR blocks are direct blocks
*/
if (lbn < NDADDR) {
nb = ip->i_ffs1_db[lbn];
if (nb != 0 && ip->i_ffs1_size >= lblktosize(fs, lbn + 1)) {
/*
* The block is an already-allocated direct block
* and the file already extends past this block,
* thus this must be a whole block.
* Just read the block (if requested).
*/
if (bpp != NULL) {
error = bread(vp, lbn, fs->fs_bsize, bpp);
if (error) {
brelse(*bpp);
return (error);
}
}
return (0);
}
if (nb != 0) {
/*
* Consider need to reallocate a fragment.
*/
osize = fragroundup(fs, blkoff(fs, ip->i_ffs1_size));
nsize = fragroundup(fs, size);
if (nsize <= osize) {
/*
* The existing block is already
* at least as big as we want.
* Just read the block (if requested).
*/
if (bpp != NULL) {
error = bread(vp, lbn, fs->fs_bsize,
bpp);
if (error) {
brelse(*bpp);
return (error);
}
buf_adjcnt((*bpp), osize);
}
return (0);
} else {
/*
* The existing block is smaller than we
* want, grow it.
*/
error = ffs_realloccg(ip, lbn,
ffs1_blkpref(ip, lbn, (int)lbn,
&ip->i_ffs1_db[0]),
osize, nsize, cred, bpp, &newb);
if (error)
return (error);
if (DOINGSOFTDEP(vp))
softdep_setup_allocdirect(ip, lbn,
newb, nb, nsize, osize,
bpp ? *bpp : NULL);
}
} else {
/*
* The block was not previously allocated,
* allocate a new block or fragment.
*/
if (ip->i_ffs1_size < lblktosize(fs, lbn + 1))
nsize = fragroundup(fs, size);
else
nsize = fs->fs_bsize;
error = ffs_alloc(ip, lbn,
ffs1_blkpref(ip, lbn, (int)lbn, &ip->i_ffs1_db[0]),
nsize, cred, &newb);
if (error)
return (error);
if (bpp != NULL) {
*bpp = getblk(vp, lbn, fs->fs_bsize, 0, INFSLP);
if (nsize < fs->fs_bsize)
(*bpp)->b_bcount = nsize;
(*bpp)->b_blkno = fsbtodb(fs, newb);
if (flags & B_CLRBUF)
clrbuf(*bpp);
}
if (DOINGSOFTDEP(vp))
softdep_setup_allocdirect(ip, lbn, newb, 0,
nsize, 0, bpp ? *bpp : NULL);
}
ip->i_ffs1_db[lbn] = newb;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
return (0);
}
/*
* Determine the number of levels of indirection.
*/
pref = 0;
if ((error = ufs_getlbns(vp, lbn, indirs, &num)) != 0)
return(error);
#ifdef DIAGNOSTIC
if (num < 1)
panic ("ffs1_balloc: ufs_bmaparray returned indirect block");
#endif
/*
* Fetch the first indirect block allocating if necessary.
*/
--num;
nb = ip->i_ffs1_ib[indirs[0].in_off];
allocib = NULL;
allocblk = allociblk;
if (nb == 0) {
pref = ffs1_blkpref(ip, lbn, -indirs[0].in_off - 1, NULL);
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
cred, &newb);
if (error)
goto fail;
nb = newb;
*allocblk++ = nb;
bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, INFSLP);
bp->b_blkno = fsbtodb(fs, nb);
clrbuf(bp);
if (DOINGSOFTDEP(vp)) {
softdep_setup_allocdirect(ip, NDADDR + indirs[0].in_off,
newb, 0, fs->fs_bsize, 0, bp);
bdwrite(bp);
} else {
/*
* Write synchronously so that indirect blocks
* never point at garbage.
*/
if ((error = bwrite(bp)) != 0)
goto fail;
}
allocib = &ip->i_ffs1_ib[indirs[0].in_off];
*allocib = nb;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
/*
* Fetch through the indirect blocks, allocating as necessary.
*/
for (i = 1;;) {
error = bread(vp, indirs[i].in_lbn, (int)fs->fs_bsize, &bp);
if (error) {
brelse(bp);
goto fail;
}
bap = (int32_t *)bp->b_data;
nb = bap[indirs[i].in_off];
if (i == num)
break;
i++;
if (nb != 0) {
brelse(bp);
continue;
}
if (pref == 0)
pref = ffs1_blkpref(ip, lbn, i - num - 1, NULL);
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred,
&newb);
if (error) {
brelse(bp);
goto fail;
}
nb = newb;
*allocblk++ = nb;
nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, INFSLP);
nbp->b_blkno = fsbtodb(fs, nb);
clrbuf(nbp);
if (DOINGSOFTDEP(vp)) {
softdep_setup_allocindir_meta(nbp, ip, bp,
indirs[i - 1].in_off, nb);
bdwrite(nbp);
} else {
/*
* Write synchronously so that indirect blocks
* never point at garbage.
*/
if ((error = bwrite(nbp)) != 0) {
brelse(bp);
goto fail;
}
}
bap[indirs[i - 1].in_off] = nb;
if (allocib == NULL && unwindidx < 0)
unwindidx = i - 1;
/*
* If required, write synchronously, otherwise use
* delayed write.
*/
if (flags & B_SYNC) {
bwrite(bp);
} else {
bdwrite(bp);
}
}
/*
* Get the data block, allocating if necessary.
*/
if (nb == 0) {
pref = ffs1_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred,
&newb);
if (error) {
brelse(bp);
goto fail;
}
nb = newb;
*allocblk++ = nb;
if (bpp != NULL) {
nbp = getblk(vp, lbn, fs->fs_bsize, 0, INFSLP);
nbp->b_blkno = fsbtodb(fs, nb);
if (flags & B_CLRBUF)
clrbuf(nbp);
*bpp = nbp;
}
if (DOINGSOFTDEP(vp))
softdep_setup_allocindir_page(ip, lbn, bp,
indirs[i].in_off, nb, 0, bpp ? *bpp : NULL);
bap[indirs[i].in_off] = nb;
/*
* If required, write synchronously, otherwise use
* delayed write.
*/
if (flags & B_SYNC) {
bwrite(bp);
} else {
bdwrite(bp);
}
return (0);
}
brelse(bp);
if (bpp != NULL) {
if (flags & B_CLRBUF) {
error = bread(vp, lbn, (int)fs->fs_bsize, &nbp);
if (error) {
brelse(nbp);
goto fail;
}
} else {
nbp = getblk(vp, lbn, fs->fs_bsize, 0, INFSLP);
nbp->b_blkno = fsbtodb(fs, nb);
}
*bpp = nbp;
}
return (0);
fail:
/*
* If we have failed to allocate any blocks, simply return the error.
* This is the usual case and avoids the need to fsync the file.
*/
if (allocblk == allociblk && allocib == NULL && unwindidx == -1)
return (error);
/*
* If we have failed part way through block allocation, we have to
* deallocate any indirect blocks that we have allocated. We have to
* fsync the file before we start to get rid of all of its
* dependencies so that we do not leave them dangling. We have to sync
* it at the end so that the softdep code does not find any untracked
* changes. Although this is really slow, running out of disk space is
* not expected to be a common occurrence. The error return from fsync
* is ignored as we already have an error to return to the user.
*/
VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p);
for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
ffs_blkfree(ip, *blkp, fs->fs_bsize);
deallocated += fs->fs_bsize;
}
if (allocib != NULL) {
*allocib = 0;
} else if (unwindidx >= 0) {
int r;
r = bread(vp, indirs[unwindidx].in_lbn, (int)fs->fs_bsize, &bp);
if (r)
panic("Could not unwind indirect block, error %d", r);
bap = (int32_t *)bp->b_data;
bap[indirs[unwindidx].in_off] = 0;
if (flags & B_SYNC) {
bwrite(bp);
} else {
bdwrite(bp);
}
}
if (deallocated) {
/*
* Restore user's disk quota because allocation failed.
*/
(void)ufs_quota_free_blocks(ip, btodb(deallocated), cred);
ip->i_ffs1_blocks -= btodb(deallocated);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p);
return (error);
}
#ifdef FFS2
int
ffs2_balloc(struct inode *ip, off_t off, int size, struct ucred *cred,
int flags, struct buf **bpp)
{
daddr_t lbn, lastlbn, nb, newb, *blkp;
daddr_t pref, *allocblk, allociblk[NIADDR + 1];
daddr_t *bap, *allocib;
int deallocated, osize, nsize, num, i, error, unwindidx, r;
struct buf *bp, *nbp;
struct indir indirs[NIADDR + 2];
struct fs *fs;
struct vnode *vp;
struct proc *p;
vp = ITOV(ip);
fs = ip->i_fs;
p = curproc;
unwindidx = -1;
lbn = lblkno(fs, off);
size = blkoff(fs, off) + size;
if (size > fs->fs_bsize)
panic("ffs2_balloc: block too big");
if (bpp != NULL)
*bpp = NULL;
if (lbn < 0)
return (EFBIG);
/*
* If the next write will extend the file into a new block, and the
* file is currently composed of a fragment, this fragment has to be
* extended to be a full block.
*/
lastlbn = lblkno(fs, ip->i_ffs2_size);
if (lastlbn < NDADDR && lastlbn < lbn) {
nb = lastlbn;
osize = blksize(fs, ip, nb);
if (osize < fs->fs_bsize && osize > 0) {
error = ffs_realloccg(ip, nb, ffs2_blkpref(ip,
lastlbn, nb, &ip->i_ffs2_db[0]), osize,
(int) fs->fs_bsize, cred, bpp, &newb);
if (error)
return (error);
if (DOINGSOFTDEP(vp))
softdep_setup_allocdirect(ip, nb, newb,
ip->i_ffs2_db[nb], fs->fs_bsize, osize,
bpp ? *bpp : NULL);
ip->i_ffs2_size = lblktosize(fs, nb + 1);
uvm_vnp_setsize(vp, ip->i_ffs2_size);
ip->i_ffs2_db[nb] = newb;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (bpp) {
if (flags & B_SYNC)
bwrite(*bpp);
else
bawrite(*bpp);
}
}
}
/*
* The first NDADDR blocks are direct.
*/
if (lbn < NDADDR) {
nb = ip->i_ffs2_db[lbn];
if (nb != 0 && ip->i_ffs2_size >= lblktosize(fs, lbn + 1)) {
/*
* The direct block is already allocated and the file
* extends past this block, thus this must be a whole
* block. Just read it, if requested.
*/
if (bpp != NULL) {
error = bread(vp, lbn, fs->fs_bsize, bpp);
if (error) {
brelse(*bpp);
return (error);
}
}
return (0);
}
if (nb != 0) {
/*
* Consider the need to allocate a fragment.
*/
osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_size));
nsize = fragroundup(fs, size);
if (nsize <= osize) {
/*
* The existing block is already at least as
* big as we want. Just read it, if requested.
*/
if (bpp != NULL) {
error = bread(vp, lbn, fs->fs_bsize,
bpp);
if (error) {
brelse(*bpp);
return (error);
}
buf_adjcnt((*bpp), osize);
}
return (0);
} else {
/*
* The existing block is smaller than we want,
* grow it.
*/
error = ffs_realloccg(ip, lbn,
ffs2_blkpref(ip, lbn, (int) lbn,
&ip->i_ffs2_db[0]), osize, nsize, cred,
bpp, &newb);
if (error)
return (error);
if (DOINGSOFTDEP(vp))
softdep_setup_allocdirect(ip, lbn,
newb, nb, nsize, osize,
bpp ? *bpp : NULL);
}
} else {
/*
* The block was not previously allocated, allocate a
* new block or fragment.
*/
if (ip->i_ffs2_size < lblktosize(fs, lbn + 1))
nsize = fragroundup(fs, size);
else
nsize = fs->fs_bsize;
error = ffs_alloc(ip, lbn, ffs2_blkpref(ip, lbn,
(int) lbn, &ip->i_ffs2_db[0]), nsize, cred, &newb);
if (error)
return (error);
if (bpp != NULL) {
bp = getblk(vp, lbn, fs->fs_bsize, 0, INFSLP);
if (nsize < fs->fs_bsize)
bp->b_bcount = nsize;
bp->b_blkno = fsbtodb(fs, newb);
if (flags & B_CLRBUF)
clrbuf(bp);
*bpp = bp;
}
if (DOINGSOFTDEP(vp))
softdep_setup_allocdirect(ip, lbn, newb, 0,
nsize, 0, bpp ? *bpp : NULL);
}
ip->i_ffs2_db[lbn] = newb;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
return (0);
}
/*
* Determine the number of levels of indirection.
*/
pref = 0;
error = ufs_getlbns(vp, lbn, indirs, &num);
if (error)
return (error);
#ifdef DIAGNOSTIC
if (num < 1)
panic("ffs2_balloc: ufs_bmaparray returned indirect block");
#endif
/*
* Fetch the first indirect block allocating it necessary.
*/
--num;
nb = ip->i_ffs2_ib[indirs[0].in_off];
allocib = NULL;
allocblk = allociblk;
if (nb == 0) {
pref = ffs2_blkpref(ip, lbn, -indirs[0].in_off - 1, NULL);
error = ffs_alloc(ip, lbn, pref, (int) fs->fs_bsize, cred,
&newb);
if (error)
goto fail;
nb = newb;
*allocblk++ = nb;
bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, INFSLP);
bp->b_blkno = fsbtodb(fs, nb);
clrbuf(bp);
if (DOINGSOFTDEP(vp)) {
softdep_setup_allocdirect(ip, NDADDR + indirs[0].in_off,
newb, 0, fs->fs_bsize, 0, bp);
bdwrite(bp);
} else {
/*
* Write synchronously so that indirect blocks never
* point at garbage.
*/
error = bwrite(bp);
if (error)
goto fail;
}
unwindidx = 0;
allocib = &ip->i_ffs2_ib[indirs[0].in_off];
*allocib = nb;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
/*
* Fetch through the indirect blocks, allocating as necessary.
*/
for (i = 1;;) {
error = bread(vp, indirs[i].in_lbn, (int)fs->fs_bsize, &bp);
if (error) {
brelse(bp);
goto fail;
}
bap = (int64_t *) bp->b_data;
nb = bap[indirs[i].in_off];
if (i == num)
break;
i++;
if (nb != 0) {
brelse(bp);
continue;
}
if (pref == 0)
pref = ffs2_blkpref(ip, lbn, i - num - 1, NULL);
error = ffs_alloc(ip, lbn, pref, (int) fs->fs_bsize, cred,
&newb);
if (error) {
brelse(bp);
goto fail;
}
nb = newb;
*allocblk++ = nb;
nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, INFSLP);
nbp->b_blkno = fsbtodb(fs, nb);
clrbuf(nbp);
if (DOINGSOFTDEP(vp)) {
softdep_setup_allocindir_meta(nbp, ip, bp,
indirs[i - 1].in_off, nb);
bdwrite(nbp);
} else {
/*
* Write synchronously so that indirect blocks never
* point at garbage.
*/
error = bwrite(nbp);
if (error) {
brelse(bp);
goto fail;
}
}
if (unwindidx < 0)
unwindidx = i - 1;
bap[indirs[i - 1].in_off] = nb;
/*
* If required, write synchronously, otherwise use delayed
* write.
*/
if (flags & B_SYNC)
bwrite(bp);
else
bdwrite(bp);
}
/*
* Get the data block, allocating if necessary.
*/
if (nb == 0) {
pref = ffs2_blkpref(ip, lbn, indirs[num].in_off, &bap[0]);
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred,
&newb);
if (error) {
brelse(bp);
goto fail;
}
nb = newb;
*allocblk++ = nb;
if (bpp != NULL) {
nbp = getblk(vp, lbn, fs->fs_bsize, 0, INFSLP);
nbp->b_blkno = fsbtodb(fs, nb);
if (flags & B_CLRBUF)
clrbuf(nbp);
*bpp = nbp;
}
if (DOINGSOFTDEP(vp))
softdep_setup_allocindir_page(ip, lbn, bp,
indirs[num].in_off, nb, 0, bpp ? *bpp : NULL);
bap[indirs[num].in_off] = nb;
if (allocib == NULL && unwindidx < 0)
unwindidx = i - 1;
/*
* If required, write synchronously, otherwise use delayed
* write.
*/
if (flags & B_SYNC)
bwrite(bp);
else
bdwrite(bp);
return (0);
}
brelse(bp);
if (bpp != NULL) {
if (flags & B_CLRBUF) {
error = bread(vp, lbn, (int)fs->fs_bsize, &nbp);
if (error) {
brelse(nbp);
goto fail;
}
} else {
nbp = getblk(vp, lbn, fs->fs_bsize, 0, INFSLP);
nbp->b_blkno = fsbtodb(fs, nb);
clrbuf(nbp);
}
*bpp = nbp;
}
return (0);
fail:
/*
* If we have failed to allocate any blocks, simply return the error.
* This is the usual case and avoids the need to fsync the file.
*/
if (allocblk == allociblk && allocib == NULL && unwindidx == -1)
return (error);
/*
* If we have failed part way through block allocation, we have to
* deallocate any indirect blocks that we have allocated. We have to
* fsync the file before we start to get rid of all of its
* dependencies so that we do not leave them dangling. We have to sync
* it at the end so that the softdep code does not find any untracked
* changes. Although this is really slow, running out of disk space is
* not expected to be a common occurrence. The error return from fsync
* is ignored as we already have an error to return to the user.
*/
VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p);
if (unwindidx >= 0) {
/*
* First write out any buffers we've created to resolve their
* softdeps. This must be done in reverse order of creation so
* that we resolve the dependencies in one pass.
* Write the cylinder group buffers for these buffers too.
*/
for (i = num; i >= unwindidx; i--) {
if (i == 0)
break;
bp = getblk(vp, indirs[i].in_lbn, (int) fs->fs_bsize,
0, INFSLP);
if (bp->b_flags & B_DELWRI) {
nb = fsbtodb(fs, cgtod(fs, dtog(fs,
dbtofsb(fs, bp->b_blkno))));
bwrite(bp);
bp = getblk(ip->i_devvp, nb,
(int) fs->fs_cgsize, 0, INFSLP);
if (bp->b_flags & B_DELWRI)
bwrite(bp);
else {
bp->b_flags |= B_INVAL;
brelse(bp);
}
} else {
bp->b_flags |= B_INVAL;
brelse(bp);
}
}
if (DOINGSOFTDEP(vp) && unwindidx == 0) {
ip->i_flag |= IN_CHANGE | IN_UPDATE;
ffs_update(ip, 1);
}
/*
* Now that any dependencies that we created have been
* resolved, we can undo the partial allocation.
*/
if (unwindidx == 0) {
*allocib = 0;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (DOINGSOFTDEP(vp))
ffs_update(ip, 1);
} else {
r = bread(vp, indirs[unwindidx].in_lbn,
(int)fs->fs_bsize, &bp);
if (r)
panic("ffs2_balloc: unwind failed");
bap = (int64_t *) bp->b_data;
bap[indirs[unwindidx].in_off] = 0;
bwrite(bp);
}
for (i = unwindidx + 1; i <= num; i++) {
bp = getblk(vp, indirs[i].in_lbn, (int)fs->fs_bsize, 0,
INFSLP);
bp->b_flags |= B_INVAL;
brelse(bp);
}
}
for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
ffs_blkfree(ip, *blkp, fs->fs_bsize);
deallocated += fs->fs_bsize;
}
if (deallocated) {
/*
* Restore user's disk quota because allocation failed.
*/
(void) ufs_quota_free_blocks(ip, btodb(deallocated), cred);
ip->i_ffs2_blocks -= btodb(deallocated);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p);
return (error);
}
#endif /* FFS2 */
/*
* Balloc defines the structure of file system storage by allocating the
* physical blocks given the inode and the logical block number in a file.
*/
int
ffs_balloc(struct inode *ip, off_t off, int size, struct ucred *cred,
int flags, struct buf **bpp)
{
#ifdef FFS2
if (ip->i_fs->fs_magic == FS_UFS2_MAGIC)
return (ffs2_balloc(ip, off, size, cred, flags, bpp));
else
#endif
return (ffs1_balloc(ip, off, size, cred, flags, bpp));
}
1078
1077
72
72
33
50
72
624
692
694
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/* $OpenBSD: uvm_anon.c,v 1.54 2021/03/26 13:40:05 mpi Exp $ */
/* $NetBSD: uvm_anon.c,v 1.10 2000/11/25 06:27:59 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* uvm_anon.c: uvm anon ops
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/kernel.h>
#include <sys/atomic.h>
#include <uvm/uvm.h>
#include <uvm/uvm_swap.h>
struct pool uvm_anon_pool;
void
uvm_anon_init(void)
{
pool_init(&uvm_anon_pool, sizeof(struct vm_anon), 0, IPL_MPFLOOR,
PR_WAITOK, "anonpl", NULL);
pool_sethiwat(&uvm_anon_pool, uvmexp.free / 16);
}
/*
* uvm_analloc: allocate a new anon.
*
* => anon will have no lock associated.
*/
struct vm_anon *
uvm_analloc(void)
{
struct vm_anon *anon;
anon = pool_get(&uvm_anon_pool, PR_NOWAIT);
if (anon) {
anon->an_lock = NULL;
anon->an_ref = 1;
anon->an_page = NULL;
anon->an_swslot = 0;
}
return anon;
}
/*
* uvm_anfree_list: free a single anon structure
*
* => anon must be removed from the amap (if anon was in an amap).
* => amap must be locked, if anon was owned by amap.
* => we may lock the pageq's.
*/
void
uvm_anfree_list(struct vm_anon *anon, struct pglist *pgl)
{
struct vm_page *pg = anon->an_page;
KASSERT(anon->an_lock == NULL || rw_write_held(anon->an_lock));
KASSERT(anon->an_ref == 0);
/*
* Dispose of the page, if it is resident.
*/
if (pg != NULL) {
KASSERT(anon->an_lock != NULL);
/*
* If the page is busy, mark it as PG_RELEASED, so
* that uvm_anon_release(9) would release it later.
*/
if ((pg->pg_flags & PG_BUSY) != 0) {
atomic_setbits_int(&pg->pg_flags, PG_RELEASED);
rw_obj_hold(anon->an_lock);
return;
}
pmap_page_protect(pg, PROT_NONE);
if (pgl != NULL) {
/*
* clean page, and put on on pglist
* for later freeing.
*/
uvm_lock_pageq();
uvm_pageclean(pg);
uvm_unlock_pageq();
TAILQ_INSERT_HEAD(pgl, pg, pageq);
} else {
uvm_lock_pageq(); /* lock out pagedaemon */
uvm_pagefree(pg); /* bye bye */
uvm_unlock_pageq(); /* free the daemon */
}
} else {
if (anon->an_swslot != 0) {
/* This page is no longer only in swap. */
KASSERT(uvmexp.swpgonly > 0);
atomic_dec_int(&uvmexp.swpgonly);
}
}
anon->an_lock = NULL;
/*
* Free any swap resources, leave a page replacement hint.
*/
uvm_anon_dropswap(anon);
KASSERT(anon->an_page == NULL);
KASSERT(anon->an_swslot == 0);
pool_put(&uvm_anon_pool, anon);
}
/*
* uvm_anwait: wait for memory to become available to allocate an anon.
*/
void
uvm_anwait(void)
{
struct vm_anon *anon;
/* XXX: Want something like pool_wait()? */
anon = pool_get(&uvm_anon_pool, PR_WAITOK);
pool_put(&uvm_anon_pool, anon);
}
/*
* uvm_anon_pagein: fetch an anon's page.
*
* => anon must be locked, and is unlocked upon return.
* => returns true if pagein was aborted due to lack of memory.
*/
boolean_t
uvm_anon_pagein(struct vm_amap *amap, struct vm_anon *anon)
{
struct vm_page *pg;
int rv;
KASSERT(rw_write_held(anon->an_lock));
KASSERT(anon->an_lock == amap->am_lock);
/*
* Get the page of the anon.
*/
rv = uvmfault_anonget(NULL, amap, anon);
switch (rv) {
case VM_PAGER_OK:
KASSERT(rw_write_held(anon->an_lock));
break;
case VM_PAGER_ERROR:
case VM_PAGER_REFAULT:
/*
* Nothing more to do on errors.
* VM_PAGER_REFAULT means that the anon was freed.
*/
return FALSE;
default:
#ifdef DIAGNOSTIC
panic("anon_pagein: uvmfault_anonget -> %d", rv);
#else
return FALSE;
#endif
}
/*
* Mark the page as dirty and clear its swslot.
*/
pg = anon->an_page;
if (anon->an_swslot > 0) {
uvm_swap_free(anon->an_swslot, 1);
}
anon->an_swslot = 0;
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
/*
* Deactivate the page (to put it on a page queue).
*/
pmap_clear_reference(pg);
pmap_page_protect(pg, PROT_NONE);
uvm_lock_pageq();
uvm_pagedeactivate(pg);
uvm_unlock_pageq();
rw_exit(anon->an_lock);
return FALSE;
}
/*
* uvm_anon_dropswap: release any swap resources from this anon.
*
* => anon must be locked or have a reference count of 0.
*/
void
uvm_anon_dropswap(struct vm_anon *anon)
{
KASSERT(anon->an_ref == 0 || rw_lock_held(anon->an_lock));
if (anon->an_swslot == 0)
return;
uvm_swap_free(anon->an_swslot, 1);
anon->an_swslot = 0;
}
/*
* uvm_anon_release: release an anon and its page.
*
* => anon should not have any references.
* => anon must be locked.
*/
void
uvm_anon_release(struct vm_anon *anon)
{
struct vm_page *pg = anon->an_page;
struct rwlock *lock;
KASSERT(rw_write_held(anon->an_lock));
KASSERT(pg != NULL);
KASSERT((pg->pg_flags & PG_RELEASED) != 0);
KASSERT((pg->pg_flags & PG_BUSY) != 0);
KASSERT(pg->uobject == NULL);
KASSERT(pg->uanon == anon);
KASSERT(anon->an_ref == 0);
uvm_lock_pageq();
uvm_pagefree(pg);
uvm_unlock_pageq();
KASSERT(anon->an_page == NULL);
lock = anon->an_lock;
uvm_anfree(anon);
rw_exit(lock);
/* Note: extra reference is held for PG_RELEASED case. */
rw_obj_free(lock);
}
58
154
155
13
91
10
9
9
9
9
9
9
26
9
20
26
58
161
2177
1487
725
145
41
15
25
105
171
43
2174
1484
721
145
41
15
26
105
2125
1507
1506
731
139
67
56
11
51
11
72
89
829
827
1
2
3
826
826
2
569
560
35
567
4
570
80
79
79
78
1
1
158
4
152
5
12
7
89
94
115
32
7
26
35
34
45
45
43
2
202
200
194
8
4
194
7
198
201
200
9
9
1
8
8
8
112
112
54
17
6
11
42
30
14
22
10
53
70
2
127
129
129
4
4
4
4
4
4
4
35
36
6
35
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
/* $OpenBSD: uipc_socket2.c,v 1.128 2022/09/05 14:56:09 bluhm Exp $ */
/* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/signalvar.h>
#include <sys/event.h>
#include <sys/pool.h>
/*
* Primitive routines for operating on sockets and socket buffers
*/
u_long sb_max = SB_MAX; /* patchable */
extern struct pool mclpools[];
extern struct pool mbpool;
/*
* Procedures to manipulate state flags of socket
* and do appropriate wakeups. Normal sequence from the
* active (originating) side is that soisconnecting() is
* called during processing of connect() call,
* resulting in an eventual call to soisconnected() if/when the
* connection is established. When the connection is torn down
* soisdisconnecting() is called during processing of disconnect() call,
* and soisdisconnected() is called when the connection to the peer
* is totally severed. The semantics of these routines are such that
* connectionless protocols can call soisconnected() and soisdisconnected()
* only, bypassing the in-progress calls when setting up a ``connection''
* takes no time.
*
* From the passive side, a socket is created with
* two queues of sockets: so_q0 for connections in progress
* and so_q for connections already made and awaiting user acceptance.
* As a protocol is preparing incoming connections, it creates a socket
* structure queued on so_q0 by calling sonewconn(). When the connection
* is established, soisconnected() is called, and transfers the
* socket structure to so_q, making it available to accept().
*
* If a socket is closed with sockets on either
* so_q0 or so_q, these sockets are dropped.
*
* If higher level protocols are implemented in
* the kernel, the wakeups done here will sometimes
* cause software-interrupt process scheduling.
*/
void
soisconnecting(struct socket *so)
{
soassertlocked(so);
so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING);
so->so_state |= SS_ISCONNECTING;
}
void
soisconnected(struct socket *so)
{
struct socket *head = so->so_head;
soassertlocked(so);
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING);
so->so_state |= SS_ISCONNECTED;
if (head != NULL && so->so_onq == &head->so_q0) {
int persocket = solock_persocket(so);
if (persocket) {
soref(so);
soref(head);
sounlock(so);
solock(head);
solock(so);
if (so->so_onq != &head->so_q0) {
sounlock(head);
sorele(head);
sorele(so);
return;
}
sorele(head);
sorele(so);
}
soqremque(so, 0);
soqinsque(head, so, 1);
sorwakeup(head);
wakeup_one(&head->so_timeo);
if (persocket)
sounlock(head);
} else {
wakeup(&so->so_timeo);
sorwakeup(so);
sowwakeup(so);
}
}
void
soisdisconnecting(struct socket *so)
{
soassertlocked(so);
so->so_state &= ~SS_ISCONNECTING;
so->so_state |= (SS_ISDISCONNECTING|SS_CANTRCVMORE|SS_CANTSENDMORE);
wakeup(&so->so_timeo);
sowwakeup(so);
sorwakeup(so);
}
void
soisdisconnected(struct socket *so)
{
soassertlocked(so);
so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE|SS_ISDISCONNECTED);
wakeup(&so->so_timeo);
sowwakeup(so);
sorwakeup(so);
}
/*
* When an attempt at a new connection is noted on a socket
* which accepts connections, sonewconn is called. If the
* connection is possible (subject to space constraints, etc.)
* then we allocate a new structure, properly linked into the
* data structure of the original socket, and return this.
* Connstatus may be 0 or SS_ISCONNECTED.
*/
struct socket *
sonewconn(struct socket *head, int connstatus)
{
struct socket *so;
int persocket = solock_persocket(head);
int error;
/*
* XXXSMP as long as `so' and `head' share the same lock, we
* can call soreserve() and pr_attach() below w/o explicitly
* locking `so'.
*/
soassertlocked(head);
if (m_pool_used() > 95)
return (NULL);
if (head->so_qlen + head->so_q0len > head->so_qlimit * 3)
return (NULL);
so = soalloc(PR_NOWAIT | PR_ZERO);
if (so == NULL)
return (NULL);
so->so_type = head->so_type;
so->so_options = head->so_options &~ SO_ACCEPTCONN;
so->so_linger = head->so_linger;
so->so_state = head->so_state | SS_NOFDREF;
so->so_proto = head->so_proto;
so->so_timeo = head->so_timeo;
so->so_euid = head->so_euid;
so->so_ruid = head->so_ruid;
so->so_egid = head->so_egid;
so->so_rgid = head->so_rgid;
so->so_cpid = head->so_cpid;
/*
* Lock order will be `head' -> `so' while these sockets are linked.
*/
if (persocket)
solock(so);
/*
* Inherit watermarks but those may get clamped in low mem situations.
*/
if (soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat)) {
if (persocket)
sounlock(so);
pool_put(&socket_pool, so);
return (NULL);
}
so->so_snd.sb_wat = head->so_snd.sb_wat;
so->so_snd.sb_lowat = head->so_snd.sb_lowat;
so->so_snd.sb_timeo_nsecs = head->so_snd.sb_timeo_nsecs;
so->so_rcv.sb_wat = head->so_rcv.sb_wat;
so->so_rcv.sb_lowat = head->so_rcv.sb_lowat;
so->so_rcv.sb_timeo_nsecs = head->so_rcv.sb_timeo_nsecs;
klist_init(&so->so_rcv.sb_sel.si_note, &socket_klistops, so);
klist_init(&so->so_snd.sb_sel.si_note, &socket_klistops, so);
sigio_init(&so->so_sigio);
sigio_copy(&so->so_sigio, &head->so_sigio);
soqinsque(head, so, 0);
/*
* We need to unlock `head' because PCB layer could release
* solock() to enforce desired lock order.
*/
if (persocket) {
head->so_newconn++;
sounlock(head);
}
error = pru_attach(so, 0);
if (persocket) {
sounlock(so);
solock(head);
solock(so);
if ((head->so_newconn--) == 0) {
if ((head->so_state & SS_NEWCONN_WAIT) != 0) {
head->so_state &= ~SS_NEWCONN_WAIT;
wakeup(&head->so_newconn);
}
}
}
if (error) {
soqremque(so, 0);
if (persocket)
sounlock(so);
sigio_free(&so->so_sigio);
klist_free(&so->so_rcv.sb_sel.si_note);
klist_free(&so->so_snd.sb_sel.si_note);
pool_put(&socket_pool, so);
return (NULL);
}
if (connstatus) {
so->so_state |= connstatus;
soqremque(so, 0);
soqinsque(head, so, 1);
sorwakeup(head);
wakeup(&head->so_timeo);
}
if (persocket)
sounlock(so);
return (so);
}
void
soqinsque(struct socket *head, struct socket *so, int q)
{
soassertlocked(head);
soassertlocked(so);
KASSERT(so->so_onq == NULL);
so->so_head = head;
if (q == 0) {
head->so_q0len++;
so->so_onq = &head->so_q0;
} else {
head->so_qlen++;
so->so_onq = &head->so_q;
}
TAILQ_INSERT_TAIL(so->so_onq, so, so_qe);
}
int
soqremque(struct socket *so, int q)
{
struct socket *head = so->so_head;
soassertlocked(so);
soassertlocked(head);
if (q == 0) {
if (so->so_onq != &head->so_q0)
return (0);
head->so_q0len--;
} else {
if (so->so_onq != &head->so_q)
return (0);
head->so_qlen--;
}
TAILQ_REMOVE(so->so_onq, so, so_qe);
so->so_onq = NULL;
so->so_head = NULL;
return (1);
}
/*
* Socantsendmore indicates that no more data will be sent on the
* socket; it would normally be applied to a socket when the user
* informs the system that no more data is to be sent, by the protocol
* code (in case PRU_SHUTDOWN). Socantrcvmore indicates that no more data
* will be received, and will normally be applied to the socket by a
* protocol when it detects that the peer will send no more data.
* Data queued for reading in the socket may yet be read.
*/
void
socantsendmore(struct socket *so)
{
soassertlocked(so);
so->so_state |= SS_CANTSENDMORE;
sowwakeup(so);
}
void
socantrcvmore(struct socket *so)
{
soassertlocked(so);
so->so_state |= SS_CANTRCVMORE;
sorwakeup(so);
}
void
solock(struct socket *so)
{
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
NET_LOCK();
break;
default:
rw_enter_write(&so->so_lock);
break;
}
}
void
solock_shared(struct socket *so)
{
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
if (so->so_proto->pr_usrreqs->pru_lock != NULL) {
NET_LOCK_SHARED();
pru_lock(so);
} else
NET_LOCK();
break;
default:
rw_enter_write(&so->so_lock);
break;
}
}
int
solock_persocket(struct socket *so)
{
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
return 0;
default:
return 1;
}
}
void
solock_pair(struct socket *so1, struct socket *so2)
{
KASSERT(so1 != so2);
KASSERT(so1->so_type == so2->so_type);
KASSERT(solock_persocket(so1));
if (so1 < so2) {
solock(so1);
solock(so2);
} else {
solock(so2);
solock(so1);
}
}
void
sounlock(struct socket *so)
{
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
NET_UNLOCK();
break;
default:
rw_exit_write(&so->so_lock);
break;
}
}
void
sounlock_shared(struct socket *so)
{
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
if (so->so_proto->pr_usrreqs->pru_unlock != NULL) {
pru_unlock(so);
NET_UNLOCK_SHARED();
} else
NET_UNLOCK();
break;
default:
rw_exit_write(&so->so_lock);
break;
}
}
void
soassertlocked(struct socket *so)
{
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
NET_ASSERT_LOCKED();
break;
default:
rw_assert_wrlock(&so->so_lock);
break;
}
}
int
sosleep_nsec(struct socket *so, void *ident, int prio, const char *wmesg,
uint64_t nsecs)
{
int ret;
switch (so->so_proto->pr_domain->dom_family) {
case PF_INET:
case PF_INET6:
if (so->so_proto->pr_usrreqs->pru_unlock != NULL &&
rw_status(&netlock) == RW_READ) {
pru_unlock(so);
}
ret = rwsleep_nsec(ident, &netlock, prio, wmesg, nsecs);
if (so->so_proto->pr_usrreqs->pru_lock != NULL &&
rw_status(&netlock) == RW_READ) {
pru_lock(so);
}
break;
default:
ret = rwsleep_nsec(ident, &so->so_lock, prio, wmesg, nsecs);
break;
}
return ret;
}
/*
* Wait for data to arrive at/drain from a socket buffer.
*/
int
sbwait(struct socket *so, struct sockbuf *sb)
{
int prio = (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH;
soassertlocked(so);
sb->sb_flags |= SB_WAIT;
return sosleep_nsec(so, &sb->sb_cc, prio, "netio", sb->sb_timeo_nsecs);
}
int
sblock(struct socket *so, struct sockbuf *sb, int wait)
{
int error, prio = (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH;
soassertlocked(so);
if ((sb->sb_flags & SB_LOCK) == 0) {
sb->sb_flags |= SB_LOCK;
return (0);
}
if (wait & M_NOWAIT)
return (EWOULDBLOCK);
while (sb->sb_flags & SB_LOCK) {
sb->sb_flags |= SB_WANT;
error = sosleep_nsec(so, &sb->sb_flags, prio, "netlck", INFSLP);
if (error)
return (error);
}
sb->sb_flags |= SB_LOCK;
return (0);
}
void
sbunlock(struct socket *so, struct sockbuf *sb)
{
soassertlocked(so);
sb->sb_flags &= ~SB_LOCK;
if (sb->sb_flags & SB_WANT) {
sb->sb_flags &= ~SB_WANT;
wakeup(&sb->sb_flags);
}
}
/*
* Wakeup processes waiting on a socket buffer.
* Do asynchronous notification via SIGIO
* if the socket buffer has the SB_ASYNC flag set.
*/
void
sowakeup(struct socket *so, struct sockbuf *sb)
{
soassertlocked(so);
if (sb->sb_flags & SB_WAIT) {
sb->sb_flags &= ~SB_WAIT;
wakeup(&sb->sb_cc);
}
if (sb->sb_flags & SB_ASYNC)
pgsigio(&so->so_sigio, SIGIO, 0);
KNOTE(&sb->sb_sel.si_note, 0);
}
/*
* Socket buffer (struct sockbuf) utility routines.
*
* Each socket contains two socket buffers: one for sending data and
* one for receiving data. Each buffer contains a queue of mbufs,
* information about the number of mbufs and amount of data in the
* queue, and other fields allowing select() statements and notification
* on data availability to be implemented.
*
* Data stored in a socket buffer is maintained as a list of records.
* Each record is a list of mbufs chained together with the m_next
* field. Records are chained together with the m_nextpkt field. The upper
* level routine soreceive() expects the following conventions to be
* observed when placing information in the receive buffer:
*
* 1. If the protocol requires each message be preceded by the sender's
* name, then a record containing that name must be present before
* any associated data (mbuf's must be of type MT_SONAME).
* 2. If the protocol supports the exchange of ``access rights'' (really
* just additional data associated with the message), and there are
* ``rights'' to be received, then a record containing this data
* should be present (mbuf's must be of type MT_CONTROL).
* 3. If a name or rights record exists, then it must be followed by
* a data record, perhaps of zero length.
*
* Before using a new socket structure it is first necessary to reserve
* buffer space to the socket, by calling sbreserve(). This should commit
* some of the available buffer space in the system buffer pool for the
* socket (currently, it does nothing but enforce limits). The space
* should be released by calling sbrelease() when the socket is destroyed.
*/
int
soreserve(struct socket *so, u_long sndcc, u_long rcvcc)
{
soassertlocked(so);
if (sbreserve(so, &so->so_snd, sndcc))
goto bad;
if (sbreserve(so, &so->so_rcv, rcvcc))
goto bad2;
so->so_snd.sb_wat = sndcc;
so->so_rcv.sb_wat = rcvcc;
if (so->so_rcv.sb_lowat == 0)
so->so_rcv.sb_lowat = 1;
if (so->so_snd.sb_lowat == 0)
so->so_snd.sb_lowat = MCLBYTES;
if (so->so_snd.sb_lowat > so->so_snd.sb_hiwat)
so->so_snd.sb_lowat = so->so_snd.sb_hiwat;
return (0);
bad2:
sbrelease(so, &so->so_snd);
bad:
return (ENOBUFS);
}
/*
* Allot mbufs to a sockbuf.
* Attempt to scale mbmax so that mbcnt doesn't become limiting
* if buffering efficiency is near the normal case.
*/
int
sbreserve(struct socket *so, struct sockbuf *sb, u_long cc)
{
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
soassertlocked(so);
if (cc == 0 || cc > sb_max)
return (1);
sb->sb_hiwat = cc;
sb->sb_mbmax = max(3 * MAXMCLBYTES, cc * 8);
if (sb->sb_lowat > sb->sb_hiwat)
sb->sb_lowat = sb->sb_hiwat;
return (0);
}
/*
* In low memory situation, do not accept any greater than normal request.
*/
int
sbcheckreserve(u_long cnt, u_long defcnt)
{
if (cnt > defcnt && sbchecklowmem())
return (ENOBUFS);
return (0);
}
int
sbchecklowmem(void)
{
static int sblowmem;
unsigned int used = m_pool_used();
if (used < 60)
sblowmem = 0;
else if (used > 80)
sblowmem = 1;
return (sblowmem);
}
/*
* Free mbufs held by a socket, and reserved mbuf space.
*/
void
sbrelease(struct socket *so, struct sockbuf *sb)
{
sbflush(so, sb);
sb->sb_hiwat = sb->sb_mbmax = 0;
}
/*
* Routines to add and remove
* data from an mbuf queue.
*
* The routines sbappend() or sbappendrecord() are normally called to
* append new mbufs to a socket buffer, after checking that adequate
* space is available, comparing the function sbspace() with the amount
* of data to be added. sbappendrecord() differs from sbappend() in
* that data supplied is treated as the beginning of a new record.
* To place a sender's address, optional access rights, and data in a
* socket receive buffer, sbappendaddr() should be used. To place
* access rights and data in a socket receive buffer, sbappendrights()
* should be used. In either case, the new data begins a new record.
* Note that unlike sbappend() and sbappendrecord(), these routines check
* for the caller that there will be enough space to store the data.
* Each fails if there is not enough space, or if it cannot find mbufs
* to store additional information in.
*
* Reliable protocols may use the socket send buffer to hold data
* awaiting acknowledgement. Data is normally copied from a socket
* send buffer in a protocol with m_copym for output to a peer,
* and then removing the data from the socket buffer with sbdrop()
* or sbdroprecord() when the data is acknowledged by the peer.
*/
#ifdef SOCKBUF_DEBUG
void
sblastrecordchk(struct sockbuf *sb, const char *where)
{
struct mbuf *m = sb->sb_mb;
while (m && m->m_nextpkt)
m = m->m_nextpkt;
if (m != sb->sb_lastrecord) {
printf("sblastrecordchk: sb_mb %p sb_lastrecord %p last %p\n",
sb->sb_mb, sb->sb_lastrecord, m);
printf("packet chain:\n");
for (m = sb->sb_mb; m != NULL; m = m->m_nextpkt)
printf("\t%p\n", m);
panic("sblastrecordchk from %s", where);
}
}
void
sblastmbufchk(struct sockbuf *sb, const char *where)
{
struct mbuf *m = sb->sb_mb;
struct mbuf *n;
while (m && m->m_nextpkt)
m = m->m_nextpkt;
while (m && m->m_next)
m = m->m_next;
if (m != sb->sb_mbtail) {
printf("sblastmbufchk: sb_mb %p sb_mbtail %p last %p\n",
sb->sb_mb, sb->sb_mbtail, m);
printf("packet tree:\n");
for (m = sb->sb_mb; m != NULL; m = m->m_nextpkt) {
printf("\t");
for (n = m; n != NULL; n = n->m_next)
printf("%p ", n);
printf("\n");
}
panic("sblastmbufchk from %s", where);
}
}
#endif /* SOCKBUF_DEBUG */
#define SBLINKRECORD(sb, m0) \
do { \
if ((sb)->sb_lastrecord != NULL) \
(sb)->sb_lastrecord->m_nextpkt = (m0); \
else \
(sb)->sb_mb = (m0); \
(sb)->sb_lastrecord = (m0); \
} while (/*CONSTCOND*/0)
/*
* Append mbuf chain m to the last record in the
* socket buffer sb. The additional space associated
* the mbuf chain is recorded in sb. Empty mbufs are
* discarded and mbufs are compacted where possible.
*/
void
sbappend(struct socket *so, struct sockbuf *sb, struct mbuf *m)
{
struct mbuf *n;
if (m == NULL)
return;
soassertlocked(so);
SBLASTRECORDCHK(sb, "sbappend 1");
if ((n = sb->sb_lastrecord) != NULL) {
/*
* XXX Would like to simply use sb_mbtail here, but
* XXX I need to verify that I won't miss an EOR that
* XXX way.
*/
do {
if (n->m_flags & M_EOR) {
sbappendrecord(so, sb, m); /* XXXXXX!!!! */
return;
}
} while (n->m_next && (n = n->m_next));
} else {
/*
* If this is the first record in the socket buffer, it's
* also the last record.
*/
sb->sb_lastrecord = m;
}
sbcompress(so, sb, m, n);
SBLASTRECORDCHK(sb, "sbappend 2");
}
/*
* This version of sbappend() should only be used when the caller
* absolutely knows that there will never be more than one record
* in the socket buffer, that is, a stream protocol (such as TCP).
*/
void
sbappendstream(struct socket *so, struct sockbuf *sb, struct mbuf *m)
{
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
soassertlocked(so);
KDASSERT(m->m_nextpkt == NULL);
KASSERT(sb->sb_mb == sb->sb_lastrecord);
SBLASTMBUFCHK(sb, __func__);
sbcompress(so, sb, m, sb->sb_mbtail);
sb->sb_lastrecord = sb->sb_mb;
SBLASTRECORDCHK(sb, __func__);
}
#ifdef SOCKBUF_DEBUG
void
sbcheck(struct socket *so, struct sockbuf *sb)
{
struct mbuf *m, *n;
u_long len = 0, mbcnt = 0;
for (m = sb->sb_mb; m; m = m->m_nextpkt) {
for (n = m; n; n = n->m_next) {
len += n->m_len;
mbcnt += MSIZE;
if (n->m_flags & M_EXT)
mbcnt += n->m_ext.ext_size;
if (m != n && n->m_nextpkt)
panic("sbcheck nextpkt");
}
}
if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
printf("cc %lu != %lu || mbcnt %lu != %lu\n", len, sb->sb_cc,
mbcnt, sb->sb_mbcnt);
panic("sbcheck");
}
}
#endif
/*
* As above, except the mbuf chain
* begins a new record.
*/
void
sbappendrecord(struct socket *so, struct sockbuf *sb, struct mbuf *m0)
{
struct mbuf *m;
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
soassertlocked(so);
if (m0 == NULL)
return;
/*
* Put the first mbuf on the queue.
* Note this permits zero length records.
*/
sballoc(so, sb, m0);
SBLASTRECORDCHK(sb, "sbappendrecord 1");
SBLINKRECORD(sb, m0);
m = m0->m_next;
m0->m_next = NULL;
if (m && (m0->m_flags & M_EOR)) {
m0->m_flags &= ~M_EOR;
m->m_flags |= M_EOR;
}
sbcompress(so, sb, m, m0);
SBLASTRECORDCHK(sb, "sbappendrecord 2");
}
/*
* Append address and data, and optionally, control (ancillary) data
* to the receive queue of a socket. If present,
* m0 must include a packet header with total length.
* Returns 0 if no space in sockbuf or insufficient mbufs.
*/
int
sbappendaddr(struct socket *so, struct sockbuf *sb, const struct sockaddr *asa,
struct mbuf *m0, struct mbuf *control)
{
struct mbuf *m, *n, *nlast;
int space = asa->sa_len;
soassertlocked(so);
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
panic("sbappendaddr");
if (m0)
space += m0->m_pkthdr.len;
for (n = control; n; n = n->m_next) {
space += n->m_len;
if (n->m_next == NULL) /* keep pointer to last control buf */
break;
}
if (space > sbspace(so, sb))
return (0);
if (asa->sa_len > MLEN)
return (0);
MGET(m, M_DONTWAIT, MT_SONAME);
if (m == NULL)
return (0);
m->m_len = asa->sa_len;
memcpy(mtod(m, caddr_t), asa, asa->sa_len);
if (n)
n->m_next = m0; /* concatenate data to control */
else
control = m0;
m->m_next = control;
SBLASTRECORDCHK(sb, "sbappendaddr 1");
for (n = m; n->m_next != NULL; n = n->m_next)
sballoc(so, sb, n);
sballoc(so, sb, n);
nlast = n;
SBLINKRECORD(sb, m);
sb->sb_mbtail = nlast;
SBLASTMBUFCHK(sb, "sbappendaddr");
SBLASTRECORDCHK(sb, "sbappendaddr 2");
return (1);
}
int
sbappendcontrol(struct socket *so, struct sockbuf *sb, struct mbuf *m0,
struct mbuf *control)
{
struct mbuf *m, *mlast, *n;
int space = 0;
if (control == NULL)
panic("sbappendcontrol");
for (m = control; ; m = m->m_next) {
space += m->m_len;
if (m->m_next == NULL)
break;
}
n = m; /* save pointer to last control buffer */
for (m = m0; m; m = m->m_next)
space += m->m_len;
if (space > sbspace(so, sb))
return (0);
n->m_next = m0; /* concatenate data to control */
SBLASTRECORDCHK(sb, "sbappendcontrol 1");
for (m = control; m->m_next != NULL; m = m->m_next)
sballoc(so, sb, m);
sballoc(so, sb, m);
mlast = m;
SBLINKRECORD(sb, control);
sb->sb_mbtail = mlast;
SBLASTMBUFCHK(sb, "sbappendcontrol");
SBLASTRECORDCHK(sb, "sbappendcontrol 2");
return (1);
}
/*
* Compress mbuf chain m into the socket
* buffer sb following mbuf n. If n
* is null, the buffer is presumed empty.
*/
void
sbcompress(struct socket *so, struct sockbuf *sb, struct mbuf *m,
struct mbuf *n)
{
int eor = 0;
struct mbuf *o;
while (m) {
eor |= m->m_flags & M_EOR;
if (m->m_len == 0 &&
(eor == 0 ||
(((o = m->m_next) || (o = n)) &&
o->m_type == m->m_type))) {
if (sb->sb_lastrecord == m)
sb->sb_lastrecord = m->m_next;
m = m_free(m);
continue;
}
if (n && (n->m_flags & M_EOR) == 0 &&
/* m_trailingspace() checks buffer writeability */
m->m_len <= ((n->m_flags & M_EXT)? n->m_ext.ext_size :
MCLBYTES) / 4 && /* XXX Don't copy too much */
m->m_len <= m_trailingspace(n) &&
n->m_type == m->m_type) {
memcpy(mtod(n, caddr_t) + n->m_len, mtod(m, caddr_t),
m->m_len);
n->m_len += m->m_len;
sb->sb_cc += m->m_len;
if (m->m_type != MT_CONTROL && m->m_type != MT_SONAME)
sb->sb_datacc += m->m_len;
m = m_free(m);
continue;
}
if (n)
n->m_next = m;
else
sb->sb_mb = m;
sb->sb_mbtail = m;
sballoc(so, sb, m);
n = m;
m->m_flags &= ~M_EOR;
m = m->m_next;
n->m_next = NULL;
}
if (eor) {
if (n)
n->m_flags |= eor;
else
printf("semi-panic: sbcompress");
}
SBLASTMBUFCHK(sb, __func__);
}
/*
* Free all mbufs in a sockbuf.
* Check that all resources are reclaimed.
*/
void
sbflush(struct socket *so, struct sockbuf *sb)
{
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
KASSERT((sb->sb_flags & SB_LOCK) == 0);
while (sb->sb_mbcnt)
sbdrop(so, sb, (int)sb->sb_cc);
KASSERT(sb->sb_cc == 0);
KASSERT(sb->sb_datacc == 0);
KASSERT(sb->sb_mb == NULL);
KASSERT(sb->sb_mbtail == NULL);
KASSERT(sb->sb_lastrecord == NULL);
}
/*
* Drop data from (the front of) a sockbuf.
*/
void
sbdrop(struct socket *so, struct sockbuf *sb, int len)
{
struct mbuf *m, *mn;
struct mbuf *next;
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
soassertlocked(so);
next = (m = sb->sb_mb) ? m->m_nextpkt : NULL;
while (len > 0) {
if (m == NULL) {
if (next == NULL)
panic("sbdrop");
m = next;
next = m->m_nextpkt;
continue;
}
if (m->m_len > len) {
m->m_len -= len;
m->m_data += len;
sb->sb_cc -= len;
if (m->m_type != MT_CONTROL && m->m_type != MT_SONAME)
sb->sb_datacc -= len;
break;
}
len -= m->m_len;
sbfree(so, sb, m);
mn = m_free(m);
m = mn;
}
while (m && m->m_len == 0) {
sbfree(so, sb, m);
mn = m_free(m);
m = mn;
}
if (m) {
sb->sb_mb = m;
m->m_nextpkt = next;
} else
sb->sb_mb = next;
/*
* First part is an inline SB_EMPTY_FIXUP(). Second part
* makes sure sb_lastrecord is up-to-date if we dropped
* part of the last record.
*/
m = sb->sb_mb;
if (m == NULL) {
sb->sb_mbtail = NULL;
sb->sb_lastrecord = NULL;
} else if (m->m_nextpkt == NULL)
sb->sb_lastrecord = m;
}
/*
* Drop a record off the front of a sockbuf
* and move the next record to the front.
*/
void
sbdroprecord(struct socket *so, struct sockbuf *sb)
{
struct mbuf *m, *mn;
m = sb->sb_mb;
if (m) {
sb->sb_mb = m->m_nextpkt;
do {
sbfree(so, sb, m);
mn = m_free(m);
} while ((m = mn) != NULL);
}
SB_EMPTY_FIXUP(sb);
}
/*
* Create a "control" mbuf containing the specified data
* with the specified type for presentation on a socket buffer.
*/
struct mbuf *
sbcreatecontrol(const void *p, size_t size, int type, int level)
{
struct cmsghdr *cp;
struct mbuf *m;
if (CMSG_SPACE(size) > MCLBYTES) {
printf("sbcreatecontrol: message too large %zu\n", size);
return (NULL);
}
if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
return (NULL);
if (CMSG_SPACE(size) > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return NULL;
}
}
cp = mtod(m, struct cmsghdr *);
memset(cp, 0, CMSG_SPACE(size));
memcpy(CMSG_DATA(cp), p, size);
m->m_len = CMSG_SPACE(size);
cp->cmsg_len = CMSG_LEN(size);
cp->cmsg_level = level;
cp->cmsg_type = type;
return (m);
}
6
1
2
3
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* $OpenBSD: ip_gre.c,v 1.84 2022/09/03 22:43:38 mvs Exp $ */
/* $NetBSD: ip_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Heiko W.Rupp <hwr@pilhuhn.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* decapsulate tunneled packets and send them on
* output half is in net/if_gre.[ch]
* This currently handles IPPROTO_GRE, IPPROTO_MOBILE
*/
#include "gre.h"
#if NGRE > 0
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_gre.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#ifdef PIPEX
#include <net/pipex.h>
#endif
const struct pr_usrreqs gre_usrreqs = {
.pru_attach = rip_attach,
.pru_detach = rip_detach,
.pru_bind = rip_bind,
.pru_connect = rip_connect,
.pru_disconnect = rip_disconnect,
.pru_shutdown = rip_shutdown,
.pru_send = gre_send,
.pru_abort = rip_abort,
.pru_control = in_control,
.pru_sockaddr = in_sockaddr,
.pru_peeraddr = in_peeraddr,
};
int
gre_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
#ifdef PIPEX
struct inpcb *inp = sotoinpcb(so);
if (inp->inp_pipex) {
struct sockaddr_in *sin4;
struct in_addr *ina_dst;
ina_dst = NULL;
if ((so->so_state & SS_ISCONNECTED) != 0) {
inp = sotoinpcb(so);
if (inp)
ina_dst = &inp->inp_laddr;
} else if (nam) {
if (in_nam2sin(nam, &sin4) == 0)
ina_dst = &sin4->sin_addr;
}
if (ina_dst != NULL) {
struct pipex_session *session;
session = pipex_pptp_userland_lookup_session_ipv4(m,
*ina_dst);
if(session != NULL) {
m = pipex_pptp_userland_output(m, session);
pipex_rele_session(session);
}
}
if (m == NULL) {
m_freem(control);
return (ENOMEM);
}
}
#endif
return rip_send(so, m, nam, control);
}
#endif /* if NGRE > 0 */
2
2
6
3
2
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/* $OpenBSD: bio.c,v 1.17 2015/08/26 22:28:57 deraadt Exp $ */
/*
* Copyright (c) 2002 Niklas Hallqvist. All rights reserved.
* Copyright (c) 2012 Joel Sing <jsing@openbsd.org>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* A device controller ioctl tunnelling device. */
#include <sys/param.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <dev/biovar.h>
struct bio_mapping {
LIST_ENTRY(bio_mapping) bm_link;
struct device *bm_dev;
int (*bm_ioctl)(struct device *, u_long, caddr_t);
};
LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios);
void bioattach(int);
int bioclose(dev_t, int, int, struct proc *);
int bioioctl(dev_t, u_long, caddr_t, int, struct proc *);
int bioopen(dev_t, int, int, struct proc *);
int bio_delegate_ioctl(struct bio_mapping *, u_long, caddr_t);
struct bio_mapping *bio_lookup(char *);
int bio_validate(void *);
void
bioattach(int nunits)
{
}
int
bioopen(dev_t dev, int flags, int mode, struct proc *p)
{
return (0);
}
int
bioclose(dev_t dev, int flags, int mode, struct proc *p)
{
return (0);
}
int
bioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct bio_locate *locate;
struct bio *bio;
char name[16];
int error;
switch (cmd) {
case BIOCLOCATE:
locate = (struct bio_locate *)addr;
error = copyinstr(locate->bl_name, name, sizeof name, NULL);
if (error != 0)
return (error);
locate->bl_bio.bio_cookie = bio_lookup(name);
if (locate->bl_bio.bio_cookie == NULL)
return (ENOENT);
break;
case BIOCINQ:
case BIOCDISK:
case BIOCVOL:
case BIOCALARM:
case BIOCBLINK:
case BIOCSETSTATE:
case BIOCCREATERAID:
case BIOCDELETERAID:
case BIOCDISCIPLINE:
case BIOCPATROL:
bio = (struct bio *)addr;
if (!bio_validate(bio->bio_cookie))
return (ENOENT);
return (bio_delegate_ioctl(
(struct bio_mapping *)bio->bio_cookie, cmd, addr));
default:
return (ENXIO);
}
return (0);
}
int
bio_register(struct device *dev, int (*ioctl)(struct device *, u_long, caddr_t))
{
struct bio_mapping *bm;
bm = malloc(sizeof *bm, M_DEVBUF, M_NOWAIT);
if (bm == NULL)
return (ENOMEM);
bm->bm_dev = dev;
bm->bm_ioctl = ioctl;
LIST_INSERT_HEAD(&bios, bm, bm_link);
return (0);
}
void
bio_unregister(struct device *dev)
{
struct bio_mapping *bm, *next;
for (bm = LIST_FIRST(&bios); bm != NULL; bm = next) {
next = LIST_NEXT(bm, bm_link);
if (dev == bm->bm_dev) {
LIST_REMOVE(bm, bm_link);
free(bm, M_DEVBUF, sizeof(*bm));
}
}
}
struct bio_mapping *
bio_lookup(char *name)
{
struct bio_mapping *bm;
LIST_FOREACH(bm, &bios, bm_link)
if (strcmp(name, bm->bm_dev->dv_xname) == 0)
return (bm);
return (NULL);
}
int
bio_validate(void *cookie)
{
struct bio_mapping *bm;
LIST_FOREACH(bm, &bios, bm_link)
if (bm == cookie)
return (1);
return (0);
}
int
bio_delegate_ioctl(struct bio_mapping *bm, u_long cmd, caddr_t addr)
{
return (bm->bm_ioctl(bm->bm_dev, cmd, addr));
}
void
bio_info(struct bio_status *bs, int print, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bio_status(bs, print, BIO_MSG_INFO, fmt, &ap);
va_end(ap);
}
void
bio_warn(struct bio_status *bs, int print, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bio_status(bs, print, BIO_MSG_WARN, fmt, &ap);
va_end(ap);
}
void
bio_error(struct bio_status *bs, int print, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bio_status(bs, print, BIO_MSG_ERROR, fmt, &ap);
va_end(ap);
}
void
bio_status_init(struct bio_status *bs, struct device *dv)
{
bzero(bs, sizeof(struct bio_status));
bs->bs_status = BIO_STATUS_UNKNOWN;
strlcpy(bs->bs_controller, dv->dv_xname, sizeof(bs->bs_controller));
}
void
bio_status(struct bio_status *bs, int print, int msg_type, const char *fmt,
va_list *ap)
{
int idx;
if (bs->bs_msg_count >= BIO_MSG_COUNT) {
printf("%s: insufficient message buffers\n", bs->bs_controller);
return;
}
idx = bs->bs_msg_count++;
bs->bs_msgs[idx].bm_type = msg_type;
vsnprintf(bs->bs_msgs[idx].bm_msg, BIO_MSG_LEN, fmt, *ap);
if (print)
printf("%s: %s\n", bs->bs_controller, bs->bs_msgs[idx].bm_msg);
}
611
609
1
606
605
12
1
3
4
9
9
9
3
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
/* $OpenBSD: uvm_glue.c,v 1.83 2022/03/12 08:11:07 mpi Exp $ */
/* $NetBSD: uvm_glue.c,v 1.44 2001/02/06 19:54:44 eeh Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993, The Regents of the University of California.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* The Mach Operating System project at Carnegie-Mellon University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_glue.c 8.6 (Berkeley) 1/5/94
* from: Id: uvm_glue.c,v 1.1.2.8 1998/02/07 01:16:54 chs Exp
*
*
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* All rights reserved.
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* uvm_glue.c: glue functions
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/buf.h>
#include <sys/user.h>
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#include <sys/sched.h>
#include <uvm/uvm.h>
/*
* uvm_kernacc: can the kernel access a region of memory
*
* - called from malloc [DIAGNOSTIC], and /dev/kmem driver (mem.c)
*/
boolean_t
uvm_kernacc(caddr_t addr, size_t len, int rw)
{
boolean_t rv;
vaddr_t saddr, eaddr;
vm_prot_t prot = rw == B_READ ? PROT_READ : PROT_WRITE;
saddr = trunc_page((vaddr_t)addr);
eaddr = round_page((vaddr_t)addr + len);
vm_map_lock_read(kernel_map);
rv = uvm_map_checkprot(kernel_map, saddr, eaddr, prot);
vm_map_unlock_read(kernel_map);
return rv;
}
/*
* uvm_vslock: wire user memory for I/O
*
* - called from sys_sysctl
*/
int
uvm_vslock(struct proc *p, caddr_t addr, size_t len, vm_prot_t access_type)
{
struct vm_map *map = &p->p_vmspace->vm_map;
vaddr_t start, end;
start = trunc_page((vaddr_t)addr);
end = round_page((vaddr_t)addr + len);
if (end <= start)
return (EINVAL);
return uvm_fault_wire(map, start, end, access_type);
}
/*
* uvm_vsunlock: unwire user memory wired by uvm_vslock()
*
* - called from sys_sysctl
*/
void
uvm_vsunlock(struct proc *p, caddr_t addr, size_t len)
{
vaddr_t start, end;
start = trunc_page((vaddr_t)addr);
end = round_page((vaddr_t)addr + len);
KASSERT(end > start);
uvm_fault_unwire(&p->p_vmspace->vm_map, start, end);
}
/*
* uvm_vslock_device: wire user memory, make sure it's device reachable
* and bounce if necessary.
*
* - called from physio
*/
int
uvm_vslock_device(struct proc *p, void *addr, size_t len,
vm_prot_t access_type, void **retp)
{
struct vm_map *map = &p->p_vmspace->vm_map;
struct vm_page *pg;
struct pglist pgl;
int npages;
vaddr_t start, end, off;
vaddr_t sva, va;
vsize_t sz;
int error, mapv, i;
start = trunc_page((vaddr_t)addr);
end = round_page((vaddr_t)addr + len);
sz = end - start;
off = (vaddr_t)addr - start;
if (end <= start)
return (EINVAL);
vm_map_lock_read(map);
retry:
mapv = map->timestamp;
vm_map_unlock_read(map);
if ((error = uvm_fault_wire(map, start, end, access_type)))
return (error);
vm_map_lock_read(map);
if (mapv != map->timestamp)
goto retry;
npages = atop(sz);
for (i = 0; i < npages; i++) {
paddr_t pa;
if (!pmap_extract(map->pmap, start + ptoa(i), &pa)) {
error = EFAULT;
goto out_unwire;
}
if (!PADDR_IS_DMA_REACHABLE(pa))
break;
}
if (i == npages) {
*retp = NULL;
return (0);
}
va = (vaddr_t)km_alloc(sz, &kv_any, &kp_none, &kd_nowait);
if (va == 0) {
error = ENOMEM;
goto out_unwire;
}
sva = va;
TAILQ_INIT(&pgl);
error = uvm_pglistalloc(npages * PAGE_SIZE, dma_constraint.ucr_low,
dma_constraint.ucr_high, 0, 0, &pgl, npages, UVM_PLA_WAITOK);
if (error)
goto out_unmap;
while ((pg = TAILQ_FIRST(&pgl)) != NULL) {
TAILQ_REMOVE(&pgl, pg, pageq);
pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), PROT_READ | PROT_WRITE);
va += PAGE_SIZE;
}
pmap_update(pmap_kernel());
KASSERT(va == sva + sz);
*retp = (void *)(sva + off);
if ((error = copyin(addr, *retp, len)) == 0)
return 0;
uvm_km_pgremove_intrsafe(sva, sva + sz);
pmap_kremove(sva, sz);
pmap_update(pmap_kernel());
out_unmap:
km_free((void *)sva, sz, &kv_any, &kp_none);
out_unwire:
uvm_fault_unwire_locked(map, start, end);
vm_map_unlock_read(map);
return (error);
}
/*
* uvm_vsunlock_device: unwire user memory wired by uvm_vslock_device()
*
* - called from physio
*/
void
uvm_vsunlock_device(struct proc *p, void *addr, size_t len, void *map)
{
vaddr_t start, end;
vaddr_t kva;
vsize_t sz;
start = trunc_page((vaddr_t)addr);
end = round_page((vaddr_t)addr + len);
KASSERT(end > start);
sz = end - start;
if (map)
copyout(map, addr, len);
uvm_fault_unwire_locked(&p->p_vmspace->vm_map, start, end);
vm_map_unlock_read(&p->p_vmspace->vm_map);
if (!map)
return;
kva = trunc_page((vaddr_t)map);
uvm_km_pgremove_intrsafe(kva, kva + sz);
pmap_kremove(kva, sz);
pmap_update(pmap_kernel());
uvm_km_free(kernel_map, kva, sz);
}
/*
* uvm_uarea_alloc: allocate the u-area for a new thread
*/
vaddr_t
uvm_uarea_alloc(void)
{
vaddr_t uaddr;
uaddr = uvm_km_kmemalloc_pla(kernel_map, uvm.kernel_object, USPACE,
USPACE_ALIGN, UVM_KMF_ZERO,
no_constraint.ucr_low, no_constraint.ucr_high,
0, 0, USPACE/PAGE_SIZE);
return (uaddr);
}
/*
* uvm_uarea_free: free a dead thread's stack
*
* - the thread passed to us is a dead thread; we
* are running on a different context now (the reaper).
*/
void
uvm_uarea_free(struct proc *p)
{
uvm_km_free(kernel_map, (vaddr_t)p->p_addr, USPACE);
p->p_addr = NULL;
}
/*
* uvm_exit: exit a virtual address space
*/
void
uvm_exit(struct process *pr)
{
struct vmspace *vm = pr->ps_vmspace;
pr->ps_vmspace = NULL;
uvmspace_free(vm);
}
/*
* uvm_init_limit: init per-process VM limits
*
* - called for process 0 and then inherited by all others.
*/
void
uvm_init_limits(struct plimit *limit0)
{
/*
* Set up the initial limits on process VM. Set the maximum
* resident set size to be all of (reasonably) available memory.
* This causes any single, large process to start random page
* replacement once it fills memory.
*/
limit0->pl_rlimit[RLIMIT_STACK].rlim_cur = DFLSSIZ;
limit0->pl_rlimit[RLIMIT_STACK].rlim_max = MAXSSIZ;
limit0->pl_rlimit[RLIMIT_DATA].rlim_cur = DFLDSIZ;
limit0->pl_rlimit[RLIMIT_DATA].rlim_max = MAXDSIZ;
limit0->pl_rlimit[RLIMIT_RSS].rlim_cur = ptoa(uvmexp.free);
}
#ifdef DEBUG
int enableswap = 1;
int swapdebug = 0;
#define SDB_FOLLOW 1
#define SDB_SWAPIN 2
#define SDB_SWAPOUT 4
#endif
/*
* swapout_threads: find threads that can be swapped
*
* - called by the pagedaemon
* - try and swap at least one process
* - processes that are sleeping or stopped for maxslp or more seconds
* are swapped... otherwise the longest-sleeping or stopped process
* is swapped, otherwise the longest resident process...
*/
void
uvm_swapout_threads(void)
{
struct process *pr;
struct proc *p, *slpp;
struct process *outpr;
int outpri;
int didswap = 0;
extern int maxslp;
/* XXXCDC: should move off to uvmexp. or uvm., also in uvm_meter */
#ifdef DEBUG
if (!enableswap)
return;
#endif
/*
* outpr/outpri : stop/sleep process whose most active thread has
* the largest sleeptime < maxslp
*/
outpr = NULL;
outpri = 0;
LIST_FOREACH(pr, &allprocess, ps_list) {
if (pr->ps_flags & (PS_SYSTEM | PS_EXITING))
continue;
/*
* slpp: the sleeping or stopped thread in pr with
* the smallest p_slptime
*/
slpp = NULL;
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
switch (p->p_stat) {
case SRUN:
case SONPROC:
goto next_process;
case SSLEEP:
case SSTOP:
if (slpp == NULL ||
slpp->p_slptime < p->p_slptime)
slpp = p;
continue;
}
}
if (slpp != NULL) {
if (slpp->p_slptime >= maxslp) {
pmap_collect(pr->ps_vmspace->vm_map.pmap);
didswap++;
} else if (slpp->p_slptime > outpri) {
outpr = pr;
outpri = slpp->p_slptime;
}
}
next_process: ;
}
/*
* If we didn't get rid of any real duds, toss out the next most
* likely sleeping/stopped or running candidate. We only do this
* if we are real low on memory since we don't gain much by doing
* it.
*/
if (didswap == 0 && uvmexp.free <= atop(round_page(USPACE)) &&
outpr != NULL) {
#ifdef DEBUG
if (swapdebug & SDB_SWAPOUT)
printf("swapout_threads: no duds, try procpr %p\n",
outpr);
#endif
pmap_collect(outpr->ps_vmspace->vm_map.pmap);
}
}
/*
* uvm_atopg: convert KVAs back to their page structures.
*/
struct vm_page *
uvm_atopg(vaddr_t kva)
{
struct vm_page *pg;
paddr_t pa;
boolean_t rv;
rv = pmap_extract(pmap_kernel(), kva, &pa);
KASSERT(rv);
pg = PHYS_TO_VM_PAGE(pa);
KASSERT(pg != NULL);
return (pg);
}
void
uvm_pause(void)
{
static unsigned int toggle;
if (toggle++ > 128) {
toggle = 0;
KERNEL_UNLOCK();
KERNEL_LOCK();
}
sched_pause(preempt);
}
#ifndef SMALL_KERNEL
int
fill_vmmap(struct process *pr, struct kinfo_vmentry *kve,
size_t *lenp)
{
struct vm_map *map;
if (pr != NULL)
map = &pr->ps_vmspace->vm_map;
else
map = kernel_map;
return uvm_map_fill_vmmap(map, kve, lenp);
}
#endif
522
4
70
4
457
501
20
165
7
503
95
449
16
54
1
111
4
22
196
9
155
3
34
189
4
144
64
183
2
2
186
1175
1
338
7
35
2113
7
1480
3
636
2086
60
1951
97
81
2078
7
54
2102
1891
12
1452
1
1
1402
497
1693
129
2
85
9
8
6
13
1847
1495
438
1876
71
48
2
13
9
4
16
64
1
47
18
64
61
59
49
10
10
10
56
9
3
11
2
1
34
37
43
42
42
38
9
10
10
16
46
56
55
55
50
40
14
38
3
51
9
32
1
16
12
3
31
1253
1252
149
157
2
1
155
157
1
123
38
2
154
1
13
10
4
130
144
8
148
14
123
37
142
142
30
131
11
2
1
2
154
154
51
141
93
107
74
98
138
16
154
83
83
24
14
38
7
38
14
4
83
83
83
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
/* $OpenBSD: sys_generic.c,v 1.150 2022/08/16 13:32:16 visa Exp $ */
/* $NetBSD: sys_generic.c,v 1.24 1996/03/29 00:25:32 cgd Exp $ */
/*
* Copyright (c) 1996 Theo de Raadt
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)sys_generic.c 8.5 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/socketvar.h>
#include <sys/signalvar.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/poll.h>
#include <sys/eventvar.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <sys/pledge.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
/*
* Debug values:
* 1 - print implementation errors, things that should not happen.
* 2 - print ppoll(2) information, somewhat verbose
* 3 - print pselect(2) and ppoll(2) information, very verbose
*/
int kqpoll_debug = 0;
#define DPRINTFN(v, x...) if (kqpoll_debug > v) { \
printf("%s(%d): ", curproc->p_p->ps_comm, curproc->p_tid); \
printf(x); \
}
int pselregister(struct proc *, fd_set *[], fd_set *[], int, int *, int *);
int pselcollect(struct proc *, struct kevent *, fd_set *[], int *);
void ppollregister(struct proc *, struct pollfd *, int, int *, int *);
int ppollcollect(struct proc *, struct kevent *, struct pollfd *, u_int);
int pollout(struct pollfd *, struct pollfd *, u_int);
int dopselect(struct proc *, int, fd_set *, fd_set *, fd_set *,
struct timespec *, const sigset_t *, register_t *);
int doppoll(struct proc *, struct pollfd *, u_int, struct timespec *,
const sigset_t *, register_t *);
int
iovec_copyin(const struct iovec *uiov, struct iovec **iovp, struct iovec *aiov,
unsigned int iovcnt, size_t *residp)
{
#ifdef KTRACE
struct proc *p = curproc;
#endif
struct iovec *iov;
int error, i;
size_t resid = 0;
if (iovcnt > UIO_SMALLIOV) {
if (iovcnt > IOV_MAX)
return (EINVAL);
iov = mallocarray(iovcnt, sizeof(*iov), M_IOV, M_WAITOK);
} else if (iovcnt > 0) {
iov = aiov;
} else {
return (EINVAL);
}
*iovp = iov;
if ((error = copyin(uiov, iov, iovcnt * sizeof(*iov))))
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktriovec(p, iov, iovcnt);
#endif
for (i = 0; i < iovcnt; i++) {
resid += iov->iov_len;
/*
* Writes return ssize_t because -1 is returned on error.
* Therefore we must restrict the length to SSIZE_MAX to
* avoid garbage return values. Note that the addition is
* guaranteed to not wrap because SSIZE_MAX * 2 < SIZE_MAX.
*/
if (iov->iov_len > SSIZE_MAX || resid > SSIZE_MAX)
return (EINVAL);
iov++;
}
if (residp != NULL)
*residp = resid;
return (0);
}
void
iovec_free(struct iovec *iov, unsigned int iovcnt)
{
if (iovcnt > UIO_SMALLIOV)
free(iov, M_IOV, iovcnt * sizeof(*iov));
}
/*
* Read system call.
*/
int
sys_read(struct proc *p, void *v, register_t *retval)
{
struct sys_read_args /* {
syscallarg(int) fd;
syscallarg(void *) buf;
syscallarg(size_t) nbyte;
} */ *uap = v;
struct iovec iov;
struct uio auio;
iov.iov_base = SCARG(uap, buf);
iov.iov_len = SCARG(uap, nbyte);
if (iov.iov_len > SSIZE_MAX)
return (EINVAL);
auio.uio_iov = &iov;
auio.uio_iovcnt = 1;
auio.uio_resid = iov.iov_len;
return (dofilereadv(p, SCARG(uap, fd), &auio, 0, retval));
}
/*
* Scatter read system call.
*/
int
sys_readv(struct proc *p, void *v, register_t *retval)
{
struct sys_readv_args /* {
syscallarg(int) fd;
syscallarg(const struct iovec *) iovp;
syscallarg(int) iovcnt;
} */ *uap = v;
struct iovec aiov[UIO_SMALLIOV], *iov = NULL;
int error, iovcnt = SCARG(uap, iovcnt);
struct uio auio;
size_t resid;
error = iovec_copyin(SCARG(uap, iovp), &iov, aiov, iovcnt, &resid);
if (error)
goto done;
auio.uio_iov = iov;
auio.uio_iovcnt = iovcnt;
auio.uio_resid = resid;
error = dofilereadv(p, SCARG(uap, fd), &auio, 0, retval);
done:
iovec_free(iov, iovcnt);
return (error);
}
int
dofilereadv(struct proc *p, int fd, struct uio *uio, int flags,
register_t *retval)
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
long cnt, error = 0;
u_int iovlen;
#ifdef KTRACE
struct iovec *ktriov = NULL;
#endif
KASSERT(uio->uio_iov != NULL && uio->uio_iovcnt > 0);
iovlen = uio->uio_iovcnt * sizeof(struct iovec);
if ((fp = fd_getfile_mode(fdp, fd, FREAD)) == NULL)
return (EBADF);
/* Checks for positioned read. */
if (flags & FO_POSITION) {
struct vnode *vp = fp->f_data;
if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO ||
(vp->v_flag & VISTTY)) {
error = ESPIPE;
goto done;
}
if (uio->uio_offset < 0 && vp->v_type != VCHR) {
error = EINVAL;
goto done;
}
}
uio->uio_rw = UIO_READ;
uio->uio_segflg = UIO_USERSPACE;
uio->uio_procp = p;
#ifdef KTRACE
/*
* if tracing, save a copy of iovec
*/
if (KTRPOINT(p, KTR_GENIO)) {
ktriov = malloc(iovlen, M_TEMP, M_WAITOK);
memcpy(ktriov, uio->uio_iov, iovlen);
}
#endif
cnt = uio->uio_resid;
error = (*fp->f_ops->fo_read)(fp, uio, flags);
if (error) {
if (uio->uio_resid != cnt && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
}
cnt -= uio->uio_resid;
mtx_enter(&fp->f_mtx);
fp->f_rxfer++;
fp->f_rbytes += cnt;
mtx_leave(&fp->f_mtx);
#ifdef KTRACE
if (ktriov != NULL) {
if (error == 0)
ktrgenio(p, fd, UIO_READ, ktriov, cnt);
free(ktriov, M_TEMP, iovlen);
}
#endif
*retval = cnt;
done:
FRELE(fp, p);
return (error);
}
/*
* Write system call
*/
int
sys_write(struct proc *p, void *v, register_t *retval)
{
struct sys_write_args /* {
syscallarg(int) fd;
syscallarg(const void *) buf;
syscallarg(size_t) nbyte;
} */ *uap = v;
struct iovec iov;
struct uio auio;
iov.iov_base = (void *)SCARG(uap, buf);
iov.iov_len = SCARG(uap, nbyte);
if (iov.iov_len > SSIZE_MAX)
return (EINVAL);
auio.uio_iov = &iov;
auio.uio_iovcnt = 1;
auio.uio_resid = iov.iov_len;
return (dofilewritev(p, SCARG(uap, fd), &auio, 0, retval));
}
/*
* Gather write system call
*/
int
sys_writev(struct proc *p, void *v, register_t *retval)
{
struct sys_writev_args /* {
syscallarg(int) fd;
syscallarg(const struct iovec *) iovp;
syscallarg(int) iovcnt;
} */ *uap = v;
struct iovec aiov[UIO_SMALLIOV], *iov = NULL;
int error, iovcnt = SCARG(uap, iovcnt);
struct uio auio;
size_t resid;
error = iovec_copyin(SCARG(uap, iovp), &iov, aiov, iovcnt, &resid);
if (error)
goto done;
auio.uio_iov = iov;
auio.uio_iovcnt = iovcnt;
auio.uio_resid = resid;
error = dofilewritev(p, SCARG(uap, fd), &auio, 0, retval);
done:
iovec_free(iov, iovcnt);
return (error);
}
int
dofilewritev(struct proc *p, int fd, struct uio *uio, int flags,
register_t *retval)
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
long cnt, error = 0;
u_int iovlen;
#ifdef KTRACE
struct iovec *ktriov = NULL;
#endif
KASSERT(uio->uio_iov != NULL && uio->uio_iovcnt > 0);
iovlen = uio->uio_iovcnt * sizeof(struct iovec);
if ((fp = fd_getfile_mode(fdp, fd, FWRITE)) == NULL)
return (EBADF);
/* Checks for positioned write. */
if (flags & FO_POSITION) {
struct vnode *vp = fp->f_data;
if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO ||
(vp->v_flag & VISTTY)) {
error = ESPIPE;
goto done;
}
if (uio->uio_offset < 0 && vp->v_type != VCHR) {
error = EINVAL;
goto done;
}
}
uio->uio_rw = UIO_WRITE;
uio->uio_segflg = UIO_USERSPACE;
uio->uio_procp = p;
#ifdef KTRACE
/*
* if tracing, save a copy of iovec
*/
if (KTRPOINT(p, KTR_GENIO)) {
ktriov = malloc(iovlen, M_TEMP, M_WAITOK);
memcpy(ktriov, uio->uio_iov, iovlen);
}
#endif
cnt = uio->uio_resid;
error = (*fp->f_ops->fo_write)(fp, uio, flags);
if (error) {
if (uio->uio_resid != cnt && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
if (error == EPIPE) {
KERNEL_LOCK();
ptsignal(p, SIGPIPE, STHREAD);
KERNEL_UNLOCK();
}
}
cnt -= uio->uio_resid;
mtx_enter(&fp->f_mtx);
fp->f_wxfer++;
fp->f_wbytes += cnt;
mtx_leave(&fp->f_mtx);
#ifdef KTRACE
if (ktriov != NULL) {
if (error == 0)
ktrgenio(p, fd, UIO_WRITE, ktriov, cnt);
free(ktriov, M_TEMP, iovlen);
}
#endif
*retval = cnt;
done:
FRELE(fp, p);
return (error);
}
/*
* Ioctl system call
*/
int
sys_ioctl(struct proc *p, void *v, register_t *retval)
{
struct sys_ioctl_args /* {
syscallarg(int) fd;
syscallarg(u_long) com;
syscallarg(void *) data;
} */ *uap = v;
struct file *fp;
struct filedesc *fdp = p->p_fd;
u_long com = SCARG(uap, com);
int error = 0;
u_int size = 0;
caddr_t data, memp = NULL;
int tmp;
#define STK_PARAMS 128
long long stkbuf[STK_PARAMS / sizeof(long long)];
if ((fp = fd_getfile_mode(fdp, SCARG(uap, fd), FREAD|FWRITE)) == NULL)
return (EBADF);
if (fp->f_type == DTYPE_SOCKET) {
struct socket *so = fp->f_data;
if (so->so_state & SS_DNS) {
error = EINVAL;
goto out;
}
}
error = pledge_ioctl(p, com, fp);
if (error)
goto out;
switch (com) {
case FIONCLEX:
case FIOCLEX:
fdplock(fdp);
if (com == FIONCLEX)
fdp->fd_ofileflags[SCARG(uap, fd)] &= ~UF_EXCLOSE;
else
fdp->fd_ofileflags[SCARG(uap, fd)] |= UF_EXCLOSE;
fdpunlock(fdp);
goto out;
}
/*
* Interpret high order word to find amount of data to be
* copied to/from the user's address space.
*/
size = IOCPARM_LEN(com);
if (size > IOCPARM_MAX) {
error = ENOTTY;
goto out;
}
if (size > sizeof (stkbuf)) {
memp = malloc(size, M_IOCTLOPS, M_WAITOK);
data = memp;
} else
data = (caddr_t)stkbuf;
if (com&IOC_IN) {
if (size) {
error = copyin(SCARG(uap, data), data, size);
if (error) {
goto out;
}
} else
*(caddr_t *)data = SCARG(uap, data);
} else if ((com&IOC_OUT) && size)
/*
* Zero the buffer so the user always
* gets back something deterministic.
*/
memset(data, 0, size);
else if (com&IOC_VOID)
*(caddr_t *)data = SCARG(uap, data);
switch (com) {
case FIONBIO:
if ((tmp = *(int *)data) != 0)
atomic_setbits_int(&fp->f_flag, FNONBLOCK);
else
atomic_clearbits_int(&fp->f_flag, FNONBLOCK);
error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
break;
case FIOASYNC:
if ((tmp = *(int *)data) != 0)
atomic_setbits_int(&fp->f_flag, FASYNC);
else
atomic_clearbits_int(&fp->f_flag, FASYNC);
error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (caddr_t)&tmp, p);
break;
default:
error = (*fp->f_ops->fo_ioctl)(fp, com, data, p);
break;
}
/*
* Copy any data to user, size was
* already set and checked above.
*/
if (error == 0 && (com&IOC_OUT) && size)
error = copyout(data, SCARG(uap, data), size);
out:
FRELE(fp, p);
free(memp, M_IOCTLOPS, size);
return (error);
}
/*
* Select system call.
*/
int
sys_select(struct proc *p, void *v, register_t *retval)
{
struct sys_select_args /* {
syscallarg(int) nd;
syscallarg(fd_set *) in;
syscallarg(fd_set *) ou;
syscallarg(fd_set *) ex;
syscallarg(struct timeval *) tv;
} */ *uap = v;
struct timespec ts, *tsp = NULL;
int error;
if (SCARG(uap, tv) != NULL) {
struct timeval tv;
if ((error = copyin(SCARG(uap, tv), &tv, sizeof tv)) != 0)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimeval(p, &tv);
#endif
if (tv.tv_sec < 0 || !timerisvalid(&tv))
return (EINVAL);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
tsp = &ts;
}
return (dopselect(p, SCARG(uap, nd), SCARG(uap, in), SCARG(uap, ou),
SCARG(uap, ex), tsp, NULL, retval));
}
int
sys_pselect(struct proc *p, void *v, register_t *retval)
{
struct sys_pselect_args /* {
syscallarg(int) nd;
syscallarg(fd_set *) in;
syscallarg(fd_set *) ou;
syscallarg(fd_set *) ex;
syscallarg(const struct timespec *) ts;
syscallarg(const sigset_t *) mask;
} */ *uap = v;
struct timespec ts, *tsp = NULL;
sigset_t ss, *ssp = NULL;
int error;
if (SCARG(uap, ts) != NULL) {
if ((error = copyin(SCARG(uap, ts), &ts, sizeof ts)) != 0)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
if (ts.tv_sec < 0 || !timespecisvalid(&ts))
return (EINVAL);
tsp = &ts;
}
if (SCARG(uap, mask) != NULL) {
if ((error = copyin(SCARG(uap, mask), &ss, sizeof ss)) != 0)
return (error);
ssp = &ss;
}
return (dopselect(p, SCARG(uap, nd), SCARG(uap, in), SCARG(uap, ou),
SCARG(uap, ex), tsp, ssp, retval));
}
int
dopselect(struct proc *p, int nd, fd_set *in, fd_set *ou, fd_set *ex,
struct timespec *timeout, const sigset_t *sigmask, register_t *retval)
{
struct kqueue_scan_state scan;
struct timespec zerots = {};
fd_mask bits[6];
fd_set *pibits[3], *pobits[3];
int error, ncollected = 0, nevents = 0;
u_int ni;
if (nd < 0)
return (EINVAL);
if (nd > p->p_fd->fd_nfiles) {
/* forgiving; slightly wrong */
nd = p->p_fd->fd_nfiles;
}
ni = howmany(nd, NFDBITS) * sizeof(fd_mask);
if (ni > sizeof(bits[0])) {
caddr_t mbits;
mbits = mallocarray(6, ni, M_TEMP, M_WAITOK|M_ZERO);
pibits[0] = (fd_set *)&mbits[ni * 0];
pibits[1] = (fd_set *)&mbits[ni * 1];
pibits[2] = (fd_set *)&mbits[ni * 2];
pobits[0] = (fd_set *)&mbits[ni * 3];
pobits[1] = (fd_set *)&mbits[ni * 4];
pobits[2] = (fd_set *)&mbits[ni * 5];
} else {
memset(bits, 0, sizeof(bits));
pibits[0] = (fd_set *)&bits[0];
pibits[1] = (fd_set *)&bits[1];
pibits[2] = (fd_set *)&bits[2];
pobits[0] = (fd_set *)&bits[3];
pobits[1] = (fd_set *)&bits[4];
pobits[2] = (fd_set *)&bits[5];
}
kqpoll_init(nd);
#define getbits(name, x) \
if (name && (error = copyin(name, pibits[x], ni))) \
goto done;
getbits(in, 0);
getbits(ou, 1);
getbits(ex, 2);
#undef getbits
#ifdef KTRACE
if (ni > 0 && KTRPOINT(p, KTR_STRUCT)) {
if (in) ktrfdset(p, pibits[0], ni);
if (ou) ktrfdset(p, pibits[1], ni);
if (ex) ktrfdset(p, pibits[2], ni);
}
#endif
if (sigmask)
dosigsuspend(p, *sigmask &~ sigcantmask);
/* Register kqueue events */
error = pselregister(p, pibits, pobits, nd, &nevents, &ncollected);
if (error != 0)
goto done;
/*
* The poll/select family of syscalls has been designed to
* block when file descriptors are not available, even if
* there's nothing to wait for.
*/
if (nevents == 0 && ncollected == 0) {
uint64_t nsecs = INFSLP;
if (timeout != NULL) {
if (!timespecisset(timeout))
goto done;
nsecs = MAX(1, MIN(TIMESPEC_TO_NSEC(timeout), MAXTSLP));
}
error = tsleep_nsec(&nowake, PSOCK | PCATCH, "kqsel", nsecs);
/* select is not restarted after signals... */
if (error == ERESTART)
error = EINTR;
if (error == EWOULDBLOCK)
error = 0;
goto done;
}
/* Do not block if registering found pending events. */
if (ncollected > 0)
timeout = &zerots;
/* Collect at most `nevents' possibly waiting in kqueue_scan() */
kqueue_scan_setup(&scan, p->p_kq);
while (nevents > 0) {
struct kevent kev[KQ_NEVENTS];
int i, ready, count;
/* Maximum number of events per iteration */
count = MIN(nitems(kev), nevents);
ready = kqueue_scan(&scan, count, kev, timeout, p, &error);
/* Convert back events that are ready. */
for (i = 0; i < ready && error == 0; i++)
error = pselcollect(p, &kev[i], pobits, &ncollected);
/*
* Stop if there was an error or if we had enough
* space to collect all events that were ready.
*/
if (error || ready < count)
break;
nevents -= ready;
}
kqueue_scan_finish(&scan);
*retval = ncollected;
done:
#define putbits(name, x) \
if (name && (error2 = copyout(pobits[x], name, ni))) \
error = error2;
if (error == 0) {
int error2;
putbits(in, 0);
putbits(ou, 1);
putbits(ex, 2);
#undef putbits
#ifdef KTRACE
if (ni > 0 && KTRPOINT(p, KTR_STRUCT)) {
if (in) ktrfdset(p, pobits[0], ni);
if (ou) ktrfdset(p, pobits[1], ni);
if (ex) ktrfdset(p, pobits[2], ni);
}
#endif
}
if (pibits[0] != (fd_set *)&bits[0])
free(pibits[0], M_TEMP, 6 * ni);
kqpoll_done(nd);
return (error);
}
/*
* Convert fd_set into kqueue events and register them on the
* per-thread queue.
*/
int
pselregister(struct proc *p, fd_set *pibits[3], fd_set *pobits[3], int nfd,
int *nregistered, int *ncollected)
{
static const int evf[] = { EVFILT_READ, EVFILT_WRITE, EVFILT_EXCEPT };
static const int evff[] = { 0, 0, NOTE_OOB };
int msk, i, j, fd, nevents = 0, error = 0;
struct kevent kev;
fd_mask bits;
for (msk = 0; msk < 3; msk++) {
for (i = 0; i < nfd; i += NFDBITS) {
bits = pibits[msk]->fds_bits[i / NFDBITS];
while ((j = ffs(bits)) && (fd = i + --j) < nfd) {
bits &= ~(1 << j);
DPRINTFN(2, "select fd %d mask %d serial %lu\n",
fd, msk, p->p_kq_serial);
EV_SET(&kev, fd, evf[msk],
EV_ADD|EV_ENABLE|__EV_SELECT,
evff[msk], 0, (void *)(p->p_kq_serial));
error = kqueue_register(p->p_kq, &kev, 0, p);
switch (error) {
case 0:
nevents++;
/* FALLTHROUGH */
case EOPNOTSUPP:/* No underlying kqfilter */
case EINVAL: /* Unimplemented filter */
case EPERM: /* Specific to FIFO and
* __EV_SELECT */
error = 0;
break;
case EPIPE: /* Specific to pipes */
KASSERT(kev.filter == EVFILT_WRITE);
FD_SET(kev.ident, pobits[1]);
(*ncollected)++;
error = 0;
break;
case ENXIO: /* Device has been detached */
default:
goto bad;
}
}
}
}
*nregistered = nevents;
return (0);
bad:
DPRINTFN(0, "select fd %u filt %d error %d\n", (int)kev.ident,
kev.filter, error);
return (error);
}
/*
* Convert given kqueue event into corresponding select(2) bit.
*/
int
pselcollect(struct proc *p, struct kevent *kevp, fd_set *pobits[3],
int *ncollected)
{
if ((unsigned long)kevp->udata != p->p_kq_serial) {
panic("%s: spurious kevp %p fd %d udata 0x%lx serial 0x%lx",
__func__, kevp, (int)kevp->ident,
(unsigned long)kevp->udata, p->p_kq_serial);
}
if (kevp->flags & EV_ERROR) {
DPRINTFN(2, "select fd %d filt %d error %d\n",
(int)kevp->ident, kevp->filter, (int)kevp->data);
return (kevp->data);
}
switch (kevp->filter) {
case EVFILT_READ:
FD_SET(kevp->ident, pobits[0]);
break;
case EVFILT_WRITE:
FD_SET(kevp->ident, pobits[1]);
break;
case EVFILT_EXCEPT:
FD_SET(kevp->ident, pobits[2]);
break;
default:
KASSERT(0);
}
(*ncollected)++;
DPRINTFN(2, "select fd %d filt %d\n", (int)kevp->ident, kevp->filter);
return (0);
}
/*
* Do a wakeup when a selectable event occurs.
*/
void
selwakeup(struct selinfo *sip)
{
KERNEL_LOCK();
KNOTE(&sip->si_note, NOTE_SUBMIT);
KERNEL_UNLOCK();
}
/*
* Only copyout the revents field.
*/
int
pollout(struct pollfd *pl, struct pollfd *upl, u_int nfds)
{
int error = 0;
u_int i = 0;
while (!error && i++ < nfds) {
error = copyout(&pl->revents, &upl->revents,
sizeof(upl->revents));
pl++;
upl++;
}
return (error);
}
/*
* We are using the same mechanism as select only we encode/decode args
* differently.
*/
int
sys_poll(struct proc *p, void *v, register_t *retval)
{
struct sys_poll_args /* {
syscallarg(struct pollfd *) fds;
syscallarg(u_int) nfds;
syscallarg(int) timeout;
} */ *uap = v;
struct timespec ts, *tsp = NULL;
int msec = SCARG(uap, timeout);
if (msec != INFTIM) {
if (msec < 0)
return (EINVAL);
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec - (ts.tv_sec * 1000)) * 1000000;
tsp = &ts;
}
return (doppoll(p, SCARG(uap, fds), SCARG(uap, nfds), tsp, NULL,
retval));
}
int
sys_ppoll(struct proc *p, void *v, register_t *retval)
{
struct sys_ppoll_args /* {
syscallarg(struct pollfd *) fds;
syscallarg(u_int) nfds;
syscallarg(const struct timespec *) ts;
syscallarg(const sigset_t *) mask;
} */ *uap = v;
int error;
struct timespec ts, *tsp = NULL;
sigset_t ss, *ssp = NULL;
if (SCARG(uap, ts) != NULL) {
if ((error = copyin(SCARG(uap, ts), &ts, sizeof ts)) != 0)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
if (ts.tv_sec < 0 || !timespecisvalid(&ts))
return (EINVAL);
tsp = &ts;
}
if (SCARG(uap, mask) != NULL) {
if ((error = copyin(SCARG(uap, mask), &ss, sizeof ss)) != 0)
return (error);
ssp = &ss;
}
return (doppoll(p, SCARG(uap, fds), SCARG(uap, nfds), tsp, ssp,
retval));
}
int
doppoll(struct proc *p, struct pollfd *fds, u_int nfds,
struct timespec *timeout, const sigset_t *sigmask, register_t *retval)
{
struct kqueue_scan_state scan;
struct timespec zerots = {};
struct pollfd pfds[4], *pl = pfds;
int error, ncollected = 0, nevents = 0;
size_t sz;
/* Standards say no more than MAX_OPEN; this is possibly better. */
if (nfds > min((int)lim_cur(RLIMIT_NOFILE), maxfiles))
return (EINVAL);
/* optimize for the default case, of a small nfds value */
if (nfds > nitems(pfds)) {
pl = mallocarray(nfds, sizeof(*pl), M_TEMP,
M_WAITOK | M_CANFAIL);
if (pl == NULL)
return (EINVAL);
}
kqpoll_init(nfds);
sz = nfds * sizeof(*pl);
if ((error = copyin(fds, pl, sz)) != 0)
goto bad;
if (sigmask)
dosigsuspend(p, *sigmask &~ sigcantmask);
/* Register kqueue events */
ppollregister(p, pl, nfds, &nevents, &ncollected);
/*
* The poll/select family of syscalls has been designed to
* block when file descriptors are not available, even if
* there's nothing to wait for.
*/
if (nevents == 0 && ncollected == 0) {
uint64_t nsecs = INFSLP;
if (timeout != NULL) {
if (!timespecisset(timeout))
goto done;
nsecs = MAX(1, MIN(TIMESPEC_TO_NSEC(timeout), MAXTSLP));
}
error = tsleep_nsec(&nowake, PSOCK | PCATCH, "kqpoll", nsecs);
if (error == ERESTART)
error = EINTR;
if (error == EWOULDBLOCK)
error = 0;
goto done;
}
/* Do not block if registering found pending events. */
if (ncollected > 0)
timeout = &zerots;
/* Collect at most `nevents' possibly waiting in kqueue_scan() */
kqueue_scan_setup(&scan, p->p_kq);
while (nevents > 0) {
struct kevent kev[KQ_NEVENTS];
int i, ready, count;
/* Maximum number of events per iteration */
count = MIN(nitems(kev), nevents);
ready = kqueue_scan(&scan, count, kev, timeout, p, &error);
/* Convert back events that are ready. */
for (i = 0; i < ready; i++)
ncollected += ppollcollect(p, &kev[i], pl, nfds);
/*
* Stop if there was an error or if we had enough
* place to collect all events that were ready.
*/
if (error || ready < count)
break;
nevents -= ready;
}
kqueue_scan_finish(&scan);
*retval = ncollected;
done:
/*
* NOTE: poll(2) is not restarted after a signal and EWOULDBLOCK is
* ignored (since the whole point is to see what would block).
*/
switch (error) {
case EINTR:
error = pollout(pl, fds, nfds);
if (error == 0)
error = EINTR;
break;
case EWOULDBLOCK:
case 0:
error = pollout(pl, fds, nfds);
break;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrpollfd(p, pl, nfds);
#endif /* KTRACE */
bad:
if (pl != pfds)
free(pl, M_TEMP, sz);
kqpoll_done(nfds);
return (error);
}
int
ppollregister_evts(struct proc *p, struct kevent *kevp, int nkev,
struct pollfd *pl, unsigned int pollid)
{
int i, error, nevents = 0;
KASSERT(pl->revents == 0);
for (i = 0; i < nkev; i++, kevp++) {
again:
error = kqueue_register(p->p_kq, kevp, pollid, p);
switch (error) {
case 0:
nevents++;
break;
case EOPNOTSUPP:/* No underlying kqfilter */
case EINVAL: /* Unimplemented filter */
break;
case EBADF: /* Bad file descriptor */
pl->revents |= POLLNVAL;
break;
case EPERM: /* Specific to FIFO */
KASSERT(kevp->filter == EVFILT_WRITE);
if (nkev == 1) {
/*
* If this is the only filter make sure
* POLLHUP is passed to userland.
*/
kevp->filter = EVFILT_EXCEPT;
goto again;
}
break;
case EPIPE: /* Specific to pipes */
KASSERT(kevp->filter == EVFILT_WRITE);
pl->revents |= POLLHUP;
break;
default:
DPRINTFN(0, "poll err %lu fd %d revents %02x serial"
" %lu filt %d ERROR=%d\n",
((unsigned long)kevp->udata - p->p_kq_serial),
pl->fd, pl->revents, p->p_kq_serial, kevp->filter,
error);
/* FALLTHROUGH */
case ENXIO: /* Device has been detached */
pl->revents |= POLLERR;
break;
}
}
return (nevents);
}
/*
* Convert pollfd into kqueue events and register them on the
* per-thread queue.
*
* At most 3 events can correspond to a single pollfd.
*/
void
ppollregister(struct proc *p, struct pollfd *pl, int nfds, int *nregistered,
int *ncollected)
{
int i, nkev, nevt, forcehup;
struct kevent kev[3], *kevp;
for (i = 0; i < nfds; i++) {
pl[i].events &= ~POLL_NOHUP;
pl[i].revents = 0;
if (pl[i].fd < 0)
continue;
/*
* POLLHUP checking is implicit in the event filters.
* However, the checking must be even if no events are
* requested.
*/
forcehup = ((pl[i].events & ~POLLHUP) == 0);
DPRINTFN(1, "poll set %d/%d fd %d events %02x serial %lu\n",
i+1, nfds, pl[i].fd, pl[i].events, p->p_kq_serial);
nevt = 0;
nkev = 0;
kevp = kev;
if (pl[i].events & (POLLIN | POLLRDNORM)) {
EV_SET(kevp, pl[i].fd, EVFILT_READ,
EV_ADD|EV_ENABLE|__EV_POLL, 0, 0,
(void *)(p->p_kq_serial + i));
nkev++;
kevp++;
}
if (pl[i].events & (POLLOUT | POLLWRNORM)) {
EV_SET(kevp, pl[i].fd, EVFILT_WRITE,
EV_ADD|EV_ENABLE|__EV_POLL, 0, 0,
(void *)(p->p_kq_serial + i));
nkev++;
kevp++;
}
if ((pl[i].events & (POLLPRI | POLLRDBAND)) || forcehup) {
int evff = forcehup ? 0 : NOTE_OOB;
EV_SET(kevp, pl[i].fd, EVFILT_EXCEPT,
EV_ADD|EV_ENABLE|__EV_POLL, evff, 0,
(void *)(p->p_kq_serial + i));
nkev++;
kevp++;
}
if (nkev == 0)
continue;
*nregistered += ppollregister_evts(p, kev, nkev, &pl[i], i);
if (pl[i].revents != 0)
(*ncollected)++;
}
DPRINTFN(1, "poll registered = %d, collected = %d\n", *nregistered,
*ncollected);
}
/*
* Convert given kqueue event into corresponding poll(2) revents bit.
*/
int
ppollcollect(struct proc *p, struct kevent *kevp, struct pollfd *pl, u_int nfds)
{
static struct timeval poll_errintvl = { 5, 0 };
static struct timeval poll_lasterr;
int already_seen;
unsigned long i;
/* Extract poll array index */
i = (unsigned long)kevp->udata - p->p_kq_serial;
if (i >= nfds) {
panic("%s: spurious kevp %p nfds %u udata 0x%lx serial 0x%lx",
__func__, kevp, nfds,
(unsigned long)kevp->udata, p->p_kq_serial);
}
if ((int)kevp->ident != pl[i].fd) {
panic("%s: kevp %p %lu/%d mismatch fd %d!=%d serial 0x%lx",
__func__, kevp, i + 1, nfds, (int)kevp->ident, pl[i].fd,
p->p_kq_serial);
}
/*
* A given descriptor may already have generated an error
* against another filter during kqueue_register().
*
* Make sure to set the appropriate flags but do not
* increment `*retval' more than once.
*/
already_seen = (pl[i].revents != 0);
/* POLLNVAL preempts other events. */
if ((kevp->flags & EV_ERROR) && kevp->data == EBADF) {
pl[i].revents = POLLNVAL;
goto done;
} else if (pl[i].revents & POLLNVAL) {
goto done;
}
switch (kevp->filter) {
case EVFILT_READ:
if (kevp->flags & __EV_HUP)
pl[i].revents |= POLLHUP;
if (pl[i].events & (POLLIN | POLLRDNORM))
pl[i].revents |= pl[i].events & (POLLIN | POLLRDNORM);
break;
case EVFILT_WRITE:
/* POLLHUP and POLLOUT/POLLWRNORM are mutually exclusive */
if (kevp->flags & __EV_HUP) {
pl[i].revents |= POLLHUP;
} else if (pl[i].events & (POLLOUT | POLLWRNORM)) {
pl[i].revents |= pl[i].events & (POLLOUT | POLLWRNORM);
}
break;
case EVFILT_EXCEPT:
if (kevp->flags & __EV_HUP) {
if (pl[i].events != 0 && pl[i].events != POLLOUT)
DPRINTFN(0, "weird events %x\n", pl[i].events);
pl[i].revents |= POLLHUP;
break;
}
if (pl[i].events & (POLLPRI | POLLRDBAND))
pl[i].revents |= pl[i].events & (POLLPRI | POLLRDBAND);
break;
default:
KASSERT(0);
}
done:
DPRINTFN(1, "poll get %lu/%d fd %d revents %02x serial %lu filt %d\n",
i+1, nfds, pl[i].fd, pl[i].revents, (unsigned long)kevp->udata,
kevp->filter);
/*
* Make noise about unclaimed events as they might indicate a bug
* and can result in spurious-looking wakeups of poll(2).
*
* Live-locking within the system call should not happen because
* the scan loop in doppoll() has an upper limit for the number
* of events to process.
*/
if (pl[i].revents == 0 && ratecheck(&poll_lasterr, &poll_errintvl)) {
printf("%s[%d]: poll index %lu fd %d events 0x%x "
"filter %d/0x%x unclaimed\n",
p->p_p->ps_comm, p->p_tid, i, pl[i].fd,
pl[i].events, kevp->filter, kevp->flags);
}
if (!already_seen && (pl[i].revents != 0))
return (1);
return (0);
}
/*
* utrace system call
*/
int
sys_utrace(struct proc *curp, void *v, register_t *retval)
{
#ifdef KTRACE
struct sys_utrace_args /* {
syscallarg(const char *) label;
syscallarg(const void *) addr;
syscallarg(size_t) len;
} */ *uap = v;
return (ktruser(curp, SCARG(uap, label), SCARG(uap, addr),
SCARG(uap, len)));
#else
return (0);
#endif
}
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
/* $OpenBSD: lpt.c,v 1.15 2020/01/15 16:43:13 cheloha Exp $ */
/* $NetBSD: lpt.c,v 1.42 1996/10/21 22:41:14 thorpej Exp $ */
/*
* Copyright (c) 1993, 1994 Charles Hannum.
* Copyright (c) 1990 William F. Jolitz, TeleMuse
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This software is a component of "386BSD" developed by
* William F. Jolitz, TeleMuse.
* 4. Neither the name of the developer nor the name "386BSD"
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
* AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
* SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
* THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
* NOT MAKE USE OF THIS WORK.
*
* FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
* BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
* REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
* (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
* JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
* LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
* ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
* OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Device Driver for AT parallel printer port
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/uio.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/syslog.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ic/lptreg.h>
#include <dev/ic/lptvar.h>
#include "lpt.h"
#define TIMEOUT 16000 /* wait up to 16 seconds for a ready */
#define STEP 250 /* 1/4 seconds */
#define LPTPRI (PZERO+8)
#define LPT_BSIZE 1024
#if !defined(DEBUG) || !defined(notdef)
#define LPRINTF(a)
#else
#define LPRINTF(a) if (lptdebug) printf a
int lptdebug = 1;
#endif
/* XXX does not belong here */
cdev_decl(lpt);
struct cfdriver lpt_cd = {
NULL, "lpt", DV_TTY
};
#define LPTUNIT(s) (minor(s) & 0x1f)
#define LPTFLAGS(s) (minor(s) & 0xe0)
#define LPS_INVERT (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK)
#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK|LPS_NOPAPER)
#define NOT_READY() \
((bus_space_read_1(sc->sc_iot, sc->sc_ioh, lpt_status) ^ LPS_INVERT) & LPS_MASK)
#define NOT_READY_ERR() \
lpt_not_ready(bus_space_read_1(sc->sc_iot, sc->sc_ioh, lpt_status), sc)
int lpt_not_ready(u_int8_t, struct lpt_softc *);
void lptwakeup(void *arg);
int lptpushbytes(struct lpt_softc *);
/*
* Internal routine to lptprobe to do port tests of one byte value.
*/
int
lpt_port_test(bus_space_tag_t iot, bus_space_handle_t ioh, bus_addr_t base,
bus_size_t off, u_int8_t data, u_int8_t mask)
{
int timeout;
u_int8_t temp;
data &= mask;
bus_space_write_1(iot, ioh, off, data);
timeout = 1000;
do {
delay(10);
temp = bus_space_read_1(iot, ioh, off) & mask;
} while (temp != data && --timeout);
LPRINTF(("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", base + off,
data, temp, timeout));
return (temp == data);
}
void
lpt_attach_common(struct lpt_softc *sc)
{
printf("\n");
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
timeout_set(&sc->sc_wakeup_tmo, lptwakeup, sc);
}
void
lpt_detach_common(struct lpt_softc *sc)
{
timeout_del(&sc->sc_wakeup_tmo);
if (sc->sc_state != 0) {
sc->sc_state = 0;
wakeup(sc);
}
}
/*
* Reset the printer, then wait until it's selected and not busy.
*/
int
lptopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = LPTUNIT(dev);
u_int8_t flags = LPTFLAGS(dev);
struct lpt_softc *sc;
u_int8_t control;
int error;
int spin;
if (unit >= lpt_cd.cd_ndevs)
return ENXIO;
sc = lpt_cd.cd_devs[unit];
if (!sc)
return ENXIO;
sc->sc_flags = (sc->sc_flags & LPT_POLLED) | flags;
if ((sc->sc_flags & (LPT_POLLED|LPT_NOINTR)) == LPT_POLLED)
return ENXIO;
#ifdef DIAGNOSTIC
if (sc->sc_state)
printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname,
sc->sc_state);
#endif
if (sc->sc_state)
return EBUSY;
sc->sc_state = LPT_INIT;
LPRINTF(("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags));
if ((flags & LPT_NOPRIME) == 0) {
/* assert INIT for 100 usec to start up printer */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_SELECT);
delay(100);
}
control = LPC_SELECT | LPC_NINIT;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
/* wait till ready (printer running diagnostics) */
for (spin = 0; NOT_READY_ERR(); spin += STEP) {
if (spin >= TIMEOUT) {
sc->sc_state = 0;
return EBUSY;
}
/* wait 1/4 second, give up if we get a signal */
error = tsleep_nsec(sc, LPTPRI | PCATCH, "lptopen",
MSEC_TO_NSEC(STEP));
if (sc->sc_state == 0)
return (EIO);
if (error != EWOULDBLOCK) {
sc->sc_state = 0;
return error;
}
}
if ((flags & LPT_NOINTR) == 0)
control |= LPC_IENABLE;
if (flags & LPT_AUTOLF)
control |= LPC_AUTOLF;
sc->sc_control = control;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
sc->sc_inbuf = geteblk(LPT_BSIZE);
sc->sc_count = 0;
sc->sc_state = LPT_OPEN;
if ((sc->sc_flags & LPT_NOINTR) == 0)
lptwakeup(sc);
LPRINTF(("%s: opened\n", sc->sc_dev.dv_xname));
return 0;
}
int
lpt_not_ready(u_int8_t status, struct lpt_softc *sc)
{
u_int8_t new;
status = (status ^ LPS_INVERT) & LPS_MASK;
new = status & ~sc->sc_laststatus;
sc->sc_laststatus = status;
if (new & LPS_SELECT)
log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
else if (new & LPS_NOPAPER)
log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
else if (new & LPS_NERR)
log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
return status;
}
void
lptwakeup(void *arg)
{
struct lpt_softc *sc = arg;
int s;
s = spltty();
lptintr(sc);
splx(s);
if (sc->sc_state != 0)
timeout_add_msec(&sc->sc_wakeup_tmo, STEP);
}
/*
* Close the device, and free the local line buffer.
*/
int
lptclose(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = LPTUNIT(dev);
struct lpt_softc *sc = lpt_cd.cd_devs[unit];
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
if (sc->sc_count)
(void) lptpushbytes(sc);
if ((sc->sc_flags & LPT_NOINTR) == 0)
timeout_del(&sc->sc_wakeup_tmo);
bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
sc->sc_state = 0;
bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
brelse(sc->sc_inbuf);
LPRINTF(("%s: closed\n", sc->sc_dev.dv_xname));
return 0;
}
int
lptpushbytes(struct lpt_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
int error;
if (sc->sc_flags & LPT_NOINTR) {
int msecs, spin;
u_int8_t control = sc->sc_control;
while (sc->sc_count > 0) {
spin = 0;
if (sc->sc_state == 0)
return (EIO);
while (NOT_READY()) {
if (++spin < sc->sc_spinmax)
continue;
msecs = 0;
/* adapt busy-wait algorithm */
sc->sc_spinmax++;
while (NOT_READY_ERR()) {
/* exponential backoff */
msecs = msecs + msecs + 10;
if (msecs > TIMEOUT)
msecs = TIMEOUT;
error = tsleep_nsec(sc,
LPTPRI | PCATCH, "lptpsh",
MSEC_TO_NSEC(msecs));
if (sc->sc_state == 0)
error = EIO;
if (error != EWOULDBLOCK)
return error;
}
break;
}
bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
bus_space_write_1(iot, ioh, lpt_control,
control | LPC_STROBE);
sc->sc_count--;
bus_space_write_1(iot, ioh, lpt_control, control);
/* adapt busy-wait algorithm */
if (spin*2 + 16 < sc->sc_spinmax)
sc->sc_spinmax--;
}
} else {
int s;
while (sc->sc_count > 0) {
/* if the printer is ready for a char, give it one */
if ((sc->sc_state & LPT_OBUSY) == 0) {
LPRINTF(("%s: write %d\n", sc->sc_dev.dv_xname,
sc->sc_count));
s = spltty();
(void) lptintr(sc);
splx(s);
}
if (sc->sc_state == 0)
return (EIO);
error = tsleep_nsec(sc, LPTPRI | PCATCH,
"lptwrite2", INFSLP);
if (sc->sc_state == 0)
error = EIO;
if (error)
return error;
}
}
return 0;
}
/*
* Copy a line from user space to a local buffer, then call putc to get the
* chars moved to the output queue.
*/
int
lptwrite(dev_t dev, struct uio *uio, int flags)
{
struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)];
size_t n;
int error = 0;
while ((n = ulmin(LPT_BSIZE, uio->uio_resid)) != 0) {
error = uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio);
if (error != 0)
return error;
sc->sc_count = n;
error = lptpushbytes(sc);
if (error) {
/*
* Return accurate residual if interrupted or timed
* out.
*/
uio->uio_resid += sc->sc_count;
sc->sc_count = 0;
return error;
}
}
return 0;
}
/*
* Handle printer interrupts which occur when the printer is ready to accept
* another char.
*/
int
lptintr(void *arg)
{
struct lpt_softc *sc = arg;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
if (((sc->sc_state & LPT_OPEN) == 0 && sc->sc_count == 0) ||
(sc->sc_flags & LPT_NOINTR))
return 0;
/* is printer online and ready for output */
if (NOT_READY() && NOT_READY_ERR())
return -1;
if (sc->sc_count) {
u_int8_t control = sc->sc_control;
/* send char */
bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
delay (50);
bus_space_write_1(iot, ioh, lpt_control, control | LPC_STROBE);
sc->sc_count--;
bus_space_write_1(iot, ioh, lpt_control, control);
sc->sc_state |= LPT_OBUSY;
} else
sc->sc_state &= ~LPT_OBUSY;
if (sc->sc_count == 0) {
/* none, wake up the top half to get more */
wakeup((caddr_t)sc);
}
return 1;
}
int
lpt_activate(struct device *self, int act)
{
struct lpt_softc *sc = (struct lpt_softc *)self;
switch (act) {
case DVACT_SUSPEND:
timeout_del(&sc->sc_wakeup_tmo);
break;
case DVACT_RESUME:
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
if (sc->sc_state) {
int spin;
if ((sc->sc_flags & LPT_NOPRIME) == 0) {
/* assert INIT for 100 usec to start up printer */
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
lpt_control, LPC_SELECT);
delay(100);
}
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control,
LPC_SELECT | LPC_NINIT);
/* wait till ready (printer running diagnostics) */
for (spin = 0; NOT_READY_ERR(); spin += STEP) {
if (spin >= TIMEOUT) {
sc->sc_state = 0;
goto fail;
}
/* wait 1/4 second, give up if we get a signal */
delay(STEP * 1000);
}
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
lpt_control, sc->sc_control);
wakeup(sc);
}
fail:
break;
}
return (0);
}
549
548
29
3
543
548
547
28
2
542
532
530
532
531
358
357
358
358
668
180
743
817
63
669
815
63
178
743
123
8468
8450
419
8185
273
94
203
266
27
266
17
102
188
191
188
543
547
543
544
537
29
29
3
20
6
27
15
10
25
19
6
8
18
22
3
18
8
12
23
4
228
228
186
46
228
46
10
37
46
186
186
186
183
3
185
37
38
188
222
222
221
214
10
220
196
4
26
218
3
214
10
221
43
182
139
98
227
173
118
1
137
37
174
174
174
174
183
33
170
160
69
201
122
130
89
70
130
119
70
70
70
141
141
253
243
23
253
27
243
540
141
60
60
405
404
312
141
55
139
1850
2
1854
7756
7750
7765
7755
9022
9019
7762
8
7
7
109
57
51
212
212
13
13
203
214
198
29
5
58
126
36
5
217
218
11
208
217
174
212
3
107
22
11
212
24
9
21
42
42
9
30
24
30
9
38
27
7
1
1
2
7
7
2
3
64
1
63
2
60
43
59
59
9
13
5
10
7
31
8
18
7
5
5
1
5
3
19
18
19
19
5
5
5
5
5
432
432
406
46
432
43
1
33
9
41
1
34
2
36
33
9
38
33
32
23
32
22
11
28
24
29
6
32
19
5
5
10
5
5
19
16
16
17
16
15
22
3
23
23
4
15
15
2221
1981
2224
36
2217
11
1
6
10
5
25
2
11
12
23
11
5
5
5
5
5
57
1
53
33
50
51
38
22
29
32
10
14
6
14
3
4
7
43
9
2
198
337
206
206
549
19
548
548
390
3
206
9
544
297
131
186
297
186
131
297
297
3
3
3
3
3
317
28
316
297
297
297
28
296
28
19
242
15
4
9
9
3
7
13
3
13
2
11
5
12
2543
520
522
2212
1807
8452
1
2364
498
2221
1809
8453
1
36
3
3
2
1
3
541
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
/* $OpenBSD: uvm_map.c,v 1.294 2022/08/15 15:53:45 jsg Exp $ */
/* $NetBSD: uvm_map.c,v 1.86 2000/11/27 08:40:03 chs Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993, The Regents of the University of California.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* The Mach Operating System project at Carnegie-Mellon University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_map.c 8.3 (Berkeley) 1/12/94
* from: Id: uvm_map.c,v 1.1.2.27 1998/02/07 01:16:54 chs Exp
*
*
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* All rights reserved.
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* uvm_map.c: uvm map operations
*/
/* #define DEBUG */
/* #define VMMAP_DEBUG */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/acct.h>
#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/sysctl.h>
#include <sys/signalvar.h>
#include <sys/syslog.h>
#include <sys/user.h>
#include <sys/tracepoint.h>
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#include <uvm/uvm.h>
#ifdef DDB
#include <uvm/uvm_ddb.h>
#endif
#include <uvm/uvm_addr.h>
vsize_t uvmspace_dused(struct vm_map*, vaddr_t, vaddr_t);
int uvm_mapent_isjoinable(struct vm_map*,
struct vm_map_entry*, struct vm_map_entry*);
struct vm_map_entry *uvm_mapent_merge(struct vm_map*, struct vm_map_entry*,
struct vm_map_entry*, struct uvm_map_deadq*);
struct vm_map_entry *uvm_mapent_tryjoin(struct vm_map*,
struct vm_map_entry*, struct uvm_map_deadq*);
struct vm_map_entry *uvm_map_mkentry(struct vm_map*, struct vm_map_entry*,
struct vm_map_entry*, vaddr_t, vsize_t, int,
struct uvm_map_deadq*, struct vm_map_entry*);
struct vm_map_entry *uvm_mapent_alloc(struct vm_map*, int);
void uvm_mapent_free(struct vm_map_entry*);
void uvm_unmap_kill_entry(struct vm_map*,
struct vm_map_entry*);
void uvm_unmap_kill_entry_withlock(struct vm_map *,
struct vm_map_entry *, int);
void uvm_unmap_detach_intrsafe(struct uvm_map_deadq *);
void uvm_mapent_mkfree(struct vm_map*,
struct vm_map_entry*, struct vm_map_entry**,
struct uvm_map_deadq*, boolean_t);
void uvm_map_pageable_pgon(struct vm_map*,
struct vm_map_entry*, struct vm_map_entry*,
vaddr_t, vaddr_t);
int uvm_map_pageable_wire(struct vm_map*,
struct vm_map_entry*, struct vm_map_entry*,
vaddr_t, vaddr_t, int);
void uvm_map_setup_entries(struct vm_map*);
void uvm_map_setup_md(struct vm_map*);
void uvm_map_teardown(struct vm_map*);
void uvm_map_vmspace_update(struct vm_map*,
struct uvm_map_deadq*, int);
void uvm_map_kmem_grow(struct vm_map*,
struct uvm_map_deadq*, vsize_t, int);
void uvm_map_freelist_update_clear(struct vm_map*,
struct uvm_map_deadq*);
void uvm_map_freelist_update_refill(struct vm_map *, int);
void uvm_map_freelist_update(struct vm_map*,
struct uvm_map_deadq*, vaddr_t, vaddr_t,
vaddr_t, vaddr_t, int);
struct vm_map_entry *uvm_map_fix_space(struct vm_map*, struct vm_map_entry*,
vaddr_t, vaddr_t, int);
int uvm_map_findspace(struct vm_map*,
struct vm_map_entry**, struct vm_map_entry**,
vaddr_t*, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
vsize_t uvm_map_addr_augment_get(struct vm_map_entry*);
void uvm_map_addr_augment(struct vm_map_entry*);
int uvm_map_inentry_recheck(u_long, vaddr_t,
struct p_inentry *);
boolean_t uvm_map_inentry_fix(struct proc *, struct p_inentry *,
vaddr_t, int (*)(vm_map_entry_t), u_long);
/*
* Tree management functions.
*/
static inline void uvm_mapent_copy(struct vm_map_entry*,
struct vm_map_entry*);
static inline int uvm_mapentry_addrcmp(const struct vm_map_entry*,
const struct vm_map_entry*);
void uvm_mapent_free_insert(struct vm_map*,
struct uvm_addr_state*, struct vm_map_entry*);
void uvm_mapent_free_remove(struct vm_map*,
struct uvm_addr_state*, struct vm_map_entry*);
void uvm_mapent_addr_insert(struct vm_map*,
struct vm_map_entry*);
void uvm_mapent_addr_remove(struct vm_map*,
struct vm_map_entry*);
void uvm_map_splitentry(struct vm_map*,
struct vm_map_entry*, struct vm_map_entry*,
vaddr_t);
vsize_t uvm_map_boundary(struct vm_map*, vaddr_t, vaddr_t);
/*
* uvm_vmspace_fork helper functions.
*/
struct vm_map_entry *uvm_mapent_clone(struct vm_map*, vaddr_t, vsize_t,
vsize_t, vm_prot_t, vm_prot_t,
struct vm_map_entry*, struct uvm_map_deadq*, int,
int);
struct vm_map_entry *uvm_mapent_share(struct vm_map*, vaddr_t, vsize_t,
vsize_t, vm_prot_t, vm_prot_t, struct vm_map*,
struct vm_map_entry*, struct uvm_map_deadq*);
struct vm_map_entry *uvm_mapent_forkshared(struct vmspace*, struct vm_map*,
struct vm_map*, struct vm_map_entry*,
struct uvm_map_deadq*);
struct vm_map_entry *uvm_mapent_forkcopy(struct vmspace*, struct vm_map*,
struct vm_map*, struct vm_map_entry*,
struct uvm_map_deadq*);
struct vm_map_entry *uvm_mapent_forkzero(struct vmspace*, struct vm_map*,
struct vm_map*, struct vm_map_entry*,
struct uvm_map_deadq*);
/*
* Tree validation.
*/
#ifdef VMMAP_DEBUG
void uvm_tree_assert(struct vm_map*, int, char*,
char*, int);
#define UVM_ASSERT(map, cond, file, line) \
uvm_tree_assert((map), (cond), #cond, (file), (line))
void uvm_tree_sanity(struct vm_map*, char*, int);
void uvm_tree_size_chk(struct vm_map*, char*, int);
void vmspace_validate(struct vm_map*);
#else
#define uvm_tree_sanity(_map, _file, _line) do {} while (0)
#define uvm_tree_size_chk(_map, _file, _line) do {} while (0)
#define vmspace_validate(_map) do {} while (0)
#endif
/*
* The kernel map will initially be VM_MAP_KSIZE_INIT bytes.
* Every time that gets cramped, we grow by at least VM_MAP_KSIZE_DELTA bytes.
*
* We attempt to grow by UVM_MAP_KSIZE_ALLOCMUL times the allocation size
* each time.
*/
#define VM_MAP_KSIZE_INIT (512 * (vaddr_t)PAGE_SIZE)
#define VM_MAP_KSIZE_DELTA (256 * (vaddr_t)PAGE_SIZE)
#define VM_MAP_KSIZE_ALLOCMUL 4
/* auto-allocate address lower bound */
#define VMMAP_MIN_ADDR PAGE_SIZE
#ifdef DEADBEEF0
#define UVMMAP_DEADBEEF ((unsigned long)DEADBEEF0)
#else
#define UVMMAP_DEADBEEF ((unsigned long)0xdeadd0d0)
#endif
#ifdef DEBUG
int uvm_map_printlocks = 0;
#define LPRINTF(_args) \
do { \
if (uvm_map_printlocks) \
printf _args; \
} while (0)
#else
#define LPRINTF(_args) do {} while (0)
#endif
static struct mutex uvm_kmapent_mtx;
static struct timeval uvm_kmapent_last_warn_time;
static struct timeval uvm_kmapent_warn_rate = { 10, 0 };
const char vmmapbsy[] = "vmmapbsy";
/*
* pool for vmspace structures.
*/
struct pool uvm_vmspace_pool;
/*
* pool for dynamically-allocated map entries.
*/
struct pool uvm_map_entry_pool;
struct pool uvm_map_entry_kmem_pool;
/*
* This global represents the end of the kernel virtual address
* space. If we want to exceed this, we must grow the kernel
* virtual address space dynamically.
*
* Note, this variable is locked by kernel_map's lock.
*/
vaddr_t uvm_maxkaddr;
/*
* Locking predicate.
*/
#define UVM_MAP_REQ_WRITE(_map) \
do { \
if ((_map)->ref_count > 0) { \
if (((_map)->flags & VM_MAP_INTRSAFE) == 0) \
rw_assert_wrlock(&(_map)->lock); \
else \
MUTEX_ASSERT_LOCKED(&(_map)->mtx); \
} \
} while (0)
#define vm_map_modflags(map, set, clear) \
do { \
mtx_enter(&(map)->flags_lock); \
(map)->flags = ((map)->flags | (set)) & ~(clear); \
mtx_leave(&(map)->flags_lock); \
} while (0)
/*
* Tree describing entries by address.
*
* Addresses are unique.
* Entries with start == end may only exist if they are the first entry
* (sorted by address) within a free-memory tree.
*/
static inline int
uvm_mapentry_addrcmp(const struct vm_map_entry *e1,
const struct vm_map_entry *e2)
{
return e1->start < e2->start ? -1 : e1->start > e2->start;
}
/*
* Copy mapentry.
*/
static inline void
uvm_mapent_copy(struct vm_map_entry *src, struct vm_map_entry *dst)
{
caddr_t csrc, cdst;
size_t sz;
csrc = (caddr_t)src;
cdst = (caddr_t)dst;
csrc += offsetof(struct vm_map_entry, uvm_map_entry_start_copy);
cdst += offsetof(struct vm_map_entry, uvm_map_entry_start_copy);
sz = offsetof(struct vm_map_entry, uvm_map_entry_stop_copy) -
offsetof(struct vm_map_entry, uvm_map_entry_start_copy);
memcpy(cdst, csrc, sz);
}
/*
* Handle free-list insertion.
*/
void
uvm_mapent_free_insert(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry *entry)
{
const struct uvm_addr_functions *fun;
#ifdef VMMAP_DEBUG
vaddr_t min, max, bound;
#endif
#ifdef VMMAP_DEBUG
/*
* Boundary check.
* Boundaries are folded if they go on the same free list.
*/
min = VMMAP_FREE_START(entry);
max = VMMAP_FREE_END(entry);
while (min < max) {
bound = uvm_map_boundary(map, min, max);
KASSERT(uvm_map_uaddr(map, min) == uaddr);
min = bound;
}
#endif
KDASSERT((entry->fspace & (vaddr_t)PAGE_MASK) == 0);
KASSERT((entry->etype & UVM_ET_FREEMAPPED) == 0);
UVM_MAP_REQ_WRITE(map);
/* Actual insert: forward to uaddr pointer. */
if (uaddr != NULL) {
fun = uaddr->uaddr_functions;
KDASSERT(fun != NULL);
if (fun->uaddr_free_insert != NULL)
(*fun->uaddr_free_insert)(map, uaddr, entry);
entry->etype |= UVM_ET_FREEMAPPED;
}
/* Update fspace augmentation. */
uvm_map_addr_augment(entry);
}
/*
* Handle free-list removal.
*/
void
uvm_mapent_free_remove(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry *entry)
{
const struct uvm_addr_functions *fun;
KASSERT((entry->etype & UVM_ET_FREEMAPPED) != 0 || uaddr == NULL);
KASSERT(uvm_map_uaddr_e(map, entry) == uaddr);
UVM_MAP_REQ_WRITE(map);
if (uaddr != NULL) {
fun = uaddr->uaddr_functions;
if (fun->uaddr_free_remove != NULL)
(*fun->uaddr_free_remove)(map, uaddr, entry);
entry->etype &= ~UVM_ET_FREEMAPPED;
}
}
/*
* Handle address tree insertion.
*/
void
uvm_mapent_addr_insert(struct vm_map *map, struct vm_map_entry *entry)
{
struct vm_map_entry *res;
if (!RBT_CHECK(uvm_map_addr, entry, UVMMAP_DEADBEEF))
panic("uvm_mapent_addr_insert: entry still in addr list");
KDASSERT(entry->start <= entry->end);
KDASSERT((entry->start & (vaddr_t)PAGE_MASK) == 0 &&
(entry->end & (vaddr_t)PAGE_MASK) == 0);
TRACEPOINT(uvm, map_insert,
entry->start, entry->end, entry->protection, NULL);
UVM_MAP_REQ_WRITE(map);
res = RBT_INSERT(uvm_map_addr, &map->addr, entry);
if (res != NULL) {
panic("uvm_mapent_addr_insert: map %p entry %p "
"(0x%lx-0x%lx G=0x%lx F=0x%lx) insert collision "
"with entry %p (0x%lx-0x%lx G=0x%lx F=0x%lx)",
map, entry,
entry->start, entry->end, entry->guard, entry->fspace,
res, res->start, res->end, res->guard, res->fspace);
}
}
/*
* Handle address tree removal.
*/
void
uvm_mapent_addr_remove(struct vm_map *map, struct vm_map_entry *entry)
{
struct vm_map_entry *res;
TRACEPOINT(uvm, map_remove,
entry->start, entry->end, entry->protection, NULL);
UVM_MAP_REQ_WRITE(map);
res = RBT_REMOVE(uvm_map_addr, &map->addr, entry);
if (res != entry)
panic("uvm_mapent_addr_remove");
RBT_POISON(uvm_map_addr, entry, UVMMAP_DEADBEEF);
}
/*
* uvm_map_reference: add reference to a map
*
* => map need not be locked
*/
void
uvm_map_reference(struct vm_map *map)
{
atomic_inc_int(&map->ref_count);
}
void
uvm_map_lock_entry(struct vm_map_entry *entry)
{
if (entry->aref.ar_amap != NULL) {
amap_lock(entry->aref.ar_amap);
}
if (UVM_ET_ISOBJ(entry)) {
rw_enter(entry->object.uvm_obj->vmobjlock, RW_WRITE);
}
}
void
uvm_map_unlock_entry(struct vm_map_entry *entry)
{
if (UVM_ET_ISOBJ(entry)) {
rw_exit(entry->object.uvm_obj->vmobjlock);
}
if (entry->aref.ar_amap != NULL) {
amap_unlock(entry->aref.ar_amap);
}
}
/*
* Calculate the dused delta.
*/
vsize_t
uvmspace_dused(struct vm_map *map, vaddr_t min, vaddr_t max)
{
struct vmspace *vm;
vsize_t sz;
vaddr_t lmax;
vaddr_t stack_begin, stack_end; /* Position of stack. */
KASSERT(map->flags & VM_MAP_ISVMSPACE);
vm = (struct vmspace *)map;
stack_begin = MIN((vaddr_t)vm->vm_maxsaddr, (vaddr_t)vm->vm_minsaddr);
stack_end = MAX((vaddr_t)vm->vm_maxsaddr, (vaddr_t)vm->vm_minsaddr);
sz = 0;
while (min != max) {
lmax = max;
if (min < stack_begin && lmax > stack_begin)
lmax = stack_begin;
else if (min < stack_end && lmax > stack_end)
lmax = stack_end;
if (min >= stack_begin && min < stack_end) {
/* nothing */
} else
sz += lmax - min;
min = lmax;
}
return sz >> PAGE_SHIFT;
}
/*
* Find the entry describing the given address.
*/
struct vm_map_entry*
uvm_map_entrybyaddr(struct uvm_map_addr *atree, vaddr_t addr)
{
struct vm_map_entry *iter;
iter = RBT_ROOT(uvm_map_addr, atree);
while (iter != NULL) {
if (iter->start > addr)
iter = RBT_LEFT(uvm_map_addr, iter);
else if (VMMAP_FREE_END(iter) <= addr)
iter = RBT_RIGHT(uvm_map_addr, iter);
else
return iter;
}
return NULL;
}
/*
* DEAD_ENTRY_PUSH(struct vm_map_deadq *deadq, struct vm_map_entry *entry)
*
* Push dead entries into a linked list.
* Since the linked list abuses the address tree for storage, the entry
* may not be linked in a map.
*
* *head must be initialized to NULL before the first call to this macro.
* uvm_unmap_detach(*head, 0) will remove dead entries.
*/
static inline void
dead_entry_push(struct uvm_map_deadq *deadq, struct vm_map_entry *entry)
{
TAILQ_INSERT_TAIL(deadq, entry, dfree.deadq);
}
#define DEAD_ENTRY_PUSH(_headptr, _entry) \
dead_entry_push((_headptr), (_entry))
/*
* Test if memory starting at addr with sz bytes is free.
*
* Fills in *start_ptr and *end_ptr to be the first and last entry describing
* the space.
* If called with prefilled *start_ptr and *end_ptr, they are to be correct.
*/
int
uvm_map_isavail(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **start_ptr, struct vm_map_entry **end_ptr,
vaddr_t addr, vsize_t sz)
{
struct uvm_addr_state *free;
struct uvm_map_addr *atree;
struct vm_map_entry *i, *i_end;
if (addr + sz < addr)
return 0;
/*
* Kernel memory above uvm_maxkaddr is considered unavailable.
*/
if ((map->flags & VM_MAP_ISVMSPACE) == 0) {
if (addr + sz > uvm_maxkaddr)
return 0;
}
atree = &map->addr;
/*
* Fill in first, last, so they point at the entries containing the
* first and last address of the range.
* Note that if they are not NULL, we don't perform the lookup.
*/
KDASSERT(atree != NULL && start_ptr != NULL && end_ptr != NULL);
if (*start_ptr == NULL) {
*start_ptr = uvm_map_entrybyaddr(atree, addr);
if (*start_ptr == NULL)
return 0;
} else
KASSERT(*start_ptr == uvm_map_entrybyaddr(atree, addr));
if (*end_ptr == NULL) {
if (VMMAP_FREE_END(*start_ptr) >= addr + sz)
*end_ptr = *start_ptr;
else {
*end_ptr = uvm_map_entrybyaddr(atree, addr + sz - 1);
if (*end_ptr == NULL)
return 0;
}
} else
KASSERT(*end_ptr == uvm_map_entrybyaddr(atree, addr + sz - 1));
/* Validation. */
KDASSERT(*start_ptr != NULL && *end_ptr != NULL);
KDASSERT((*start_ptr)->start <= addr &&
VMMAP_FREE_END(*start_ptr) > addr &&
(*end_ptr)->start < addr + sz &&
VMMAP_FREE_END(*end_ptr) >= addr + sz);
/*
* Check the none of the entries intersects with <addr, addr+sz>.
* Also, if the entry belong to uaddr_exe or uaddr_brk_stack, it is
* considered unavailable unless called by those allocators.
*/
i = *start_ptr;
i_end = RBT_NEXT(uvm_map_addr, *end_ptr);
for (; i != i_end;
i = RBT_NEXT(uvm_map_addr, i)) {
if (i->start != i->end && i->end > addr)
return 0;
/*
* uaddr_exe and uaddr_brk_stack may only be used
* by these allocators and the NULL uaddr (i.e. no
* uaddr).
* Reject if this requirement is not met.
*/
if (uaddr != NULL) {
free = uvm_map_uaddr_e(map, i);
if (uaddr != free && free != NULL &&
(free == map->uaddr_exe ||
free == map->uaddr_brk_stack))
return 0;
}
}
return -1;
}
/*
* Invoke each address selector until an address is found.
* Will not invoke uaddr_exe.
*/
int
uvm_map_findspace(struct vm_map *map, struct vm_map_entry**first,
struct vm_map_entry**last, vaddr_t *addr, vsize_t sz,
vaddr_t pmap_align, vaddr_t pmap_offset, vm_prot_t prot, vaddr_t hint)
{
struct uvm_addr_state *uaddr;
int i;
/*
* Allocation for sz bytes at any address,
* using the addr selectors in order.
*/
for (i = 0; i < nitems(map->uaddr_any); i++) {
uaddr = map->uaddr_any[i];
if (uvm_addr_invoke(map, uaddr, first, last,
addr, sz, pmap_align, pmap_offset, prot, hint) == 0)
return 0;
}
/* Fall back to brk() and stack() address selectors. */
uaddr = map->uaddr_brk_stack;
if (uvm_addr_invoke(map, uaddr, first, last,
addr, sz, pmap_align, pmap_offset, prot, hint) == 0)
return 0;
return ENOMEM;
}
/* Calculate entry augmentation value. */
vsize_t
uvm_map_addr_augment_get(struct vm_map_entry *entry)
{
vsize_t augment;
struct vm_map_entry *left, *right;
augment = entry->fspace;
if ((left = RBT_LEFT(uvm_map_addr, entry)) != NULL)
augment = MAX(augment, left->fspace_augment);
if ((right = RBT_RIGHT(uvm_map_addr, entry)) != NULL)
augment = MAX(augment, right->fspace_augment);
return augment;
}
/*
* Update augmentation data in entry.
*/
void
uvm_map_addr_augment(struct vm_map_entry *entry)
{
vsize_t augment;
while (entry != NULL) {
/* Calculate value for augmentation. */
augment = uvm_map_addr_augment_get(entry);
/*
* Descend update.
* Once we find an entry that already has the correct value,
* stop, since it means all its parents will use the correct
* value too.
*/
if (entry->fspace_augment == augment)
return;
entry->fspace_augment = augment;
entry = RBT_PARENT(uvm_map_addr, entry);
}
}
/*
* uvm_mapanon: establish a valid mapping in map for an anon
*
* => *addr and sz must be a multiple of PAGE_SIZE.
* => *addr is ignored, except if flags contains UVM_FLAG_FIXED.
* => map must be unlocked.
*
* => align: align vaddr, must be a power-of-2.
* Align is only a hint and will be ignored if the alignment fails.
*/
int
uvm_mapanon(struct vm_map *map, vaddr_t *addr, vsize_t sz,
vsize_t align, unsigned int flags)
{
struct vm_map_entry *first, *last, *entry, *new;
struct uvm_map_deadq dead;
vm_prot_t prot;
vm_prot_t maxprot;
vm_inherit_t inherit;
int advice;
int error;
vaddr_t pmap_align, pmap_offset;
vaddr_t hint;
KASSERT((map->flags & VM_MAP_ISVMSPACE) == VM_MAP_ISVMSPACE);
KASSERT(map != kernel_map);
KASSERT((map->flags & UVM_FLAG_HOLE) == 0);
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
splassert(IPL_NONE);
KASSERT((flags & UVM_FLAG_TRYLOCK) == 0);
/*
* We use pmap_align and pmap_offset as alignment and offset variables.
*
* Because the align parameter takes precedence over pmap prefer,
* the pmap_align will need to be set to align, with pmap_offset = 0,
* if pmap_prefer will not align.
*/
pmap_align = MAX(align, PAGE_SIZE);
pmap_offset = 0;
/* Decode parameters. */
prot = UVM_PROTECTION(flags);
maxprot = UVM_MAXPROTECTION(flags);
advice = UVM_ADVICE(flags);
inherit = UVM_INHERIT(flags);
error = 0;
hint = trunc_page(*addr);
TAILQ_INIT(&dead);
KASSERT((sz & (vaddr_t)PAGE_MASK) == 0);
KASSERT((align & (align - 1)) == 0);
/* Check protection. */
if ((prot & maxprot) != prot)
return EACCES;
/*
* Before grabbing the lock, allocate a map entry for later
* use to ensure we don't wait for memory while holding the
* vm_map_lock.
*/
new = uvm_mapent_alloc(map, flags);
if (new == NULL)
return ENOMEM;
vm_map_lock(map);
first = last = NULL;
if (flags & UVM_FLAG_FIXED) {
/*
* Fixed location.
*
* Note: we ignore align, pmap_prefer.
* Fill in first, last and *addr.
*/
KASSERT((*addr & PAGE_MASK) == 0);
/* Check that the space is available. */
if (flags & UVM_FLAG_UNMAP) {
if ((flags & UVM_FLAG_STACK) &&
!uvm_map_is_stack_remappable(map, *addr, sz)) {
error = EINVAL;
goto unlock;
}
uvm_unmap_remove(map, *addr, *addr + sz, &dead, FALSE, TRUE);
}
if (!uvm_map_isavail(map, NULL, &first, &last, *addr, sz)) {
error = ENOMEM;
goto unlock;
}
} else if (*addr != 0 && (*addr & PAGE_MASK) == 0 &&
(align == 0 || (*addr & (align - 1)) == 0) &&
uvm_map_isavail(map, NULL, &first, &last, *addr, sz)) {
/*
* Address used as hint.
*
* Note: we enforce the alignment restriction,
* but ignore pmap_prefer.
*/
} else if ((prot & PROT_EXEC) != 0 && map->uaddr_exe != NULL) {
/* Run selection algorithm for executables. */
error = uvm_addr_invoke(map, map->uaddr_exe, &first, &last,
addr, sz, pmap_align, pmap_offset, prot, hint);
if (error != 0)
goto unlock;
} else {
/* Update freelists from vmspace. */
uvm_map_vmspace_update(map, &dead, flags);
error = uvm_map_findspace(map, &first, &last, addr, sz,
pmap_align, pmap_offset, prot, hint);
if (error != 0)
goto unlock;
}
/* Double-check if selected address doesn't cause overflow. */
if (*addr + sz < *addr) {
error = ENOMEM;
goto unlock;
}
/* If we only want a query, return now. */
if (flags & UVM_FLAG_QUERY) {
error = 0;
goto unlock;
}
/*
* Create new entry.
* first and last may be invalidated after this call.
*/
entry = uvm_map_mkentry(map, first, last, *addr, sz, flags, &dead,
new);
if (entry == NULL) {
error = ENOMEM;
goto unlock;
}
new = NULL;
KDASSERT(entry->start == *addr && entry->end == *addr + sz);
entry->object.uvm_obj = NULL;
entry->offset = 0;
entry->protection = prot;
entry->max_protection = maxprot;
entry->inheritance = inherit;
entry->wired_count = 0;
entry->advice = advice;
if (prot & PROT_WRITE)
map->wserial++;
if (flags & UVM_FLAG_SYSCALL) {
entry->etype |= UVM_ET_SYSCALL;
map->wserial++;
}
if (flags & UVM_FLAG_STACK) {
entry->etype |= UVM_ET_STACK;
if (flags & (UVM_FLAG_FIXED | UVM_FLAG_UNMAP))
map->sserial++;
}
if (flags & UVM_FLAG_COPYONW) {
entry->etype |= UVM_ET_COPYONWRITE;
if ((flags & UVM_FLAG_OVERLAY) == 0)
entry->etype |= UVM_ET_NEEDSCOPY;
}
if (flags & UVM_FLAG_CONCEAL)
entry->etype |= UVM_ET_CONCEAL;
if (flags & UVM_FLAG_OVERLAY) {
entry->aref.ar_pageoff = 0;
entry->aref.ar_amap = amap_alloc(sz, M_WAITOK, 0);
}
/* Update map and process statistics. */
map->size += sz;
if (prot != PROT_NONE) {
((struct vmspace *)map)->vm_dused +=
uvmspace_dused(map, *addr, *addr + sz);
}
unlock:
vm_map_unlock(map);
/*
* Remove dead entries.
*
* Dead entries may be the result of merging.
* uvm_map_mkentry may also create dead entries, when it attempts to
* destroy free-space entries.
*/
uvm_unmap_detach(&dead, 0);
if (new)
uvm_mapent_free(new);
return error;
}
/*
* uvm_map: establish a valid mapping in map
*
* => *addr and sz must be a multiple of PAGE_SIZE.
* => map must be unlocked.
* => <uobj,uoffset> value meanings (4 cases):
* [1] <NULL,uoffset> == uoffset is a hint for PMAP_PREFER
* [2] <NULL,UVM_UNKNOWN_OFFSET> == don't PMAP_PREFER
* [3] <uobj,uoffset> == normal mapping
* [4] <uobj,UVM_UNKNOWN_OFFSET> == uvm_map finds offset based on VA
*
* case [4] is for kernel mappings where we don't know the offset until
* we've found a virtual address. note that kernel object offsets are
* always relative to vm_map_min(kernel_map).
*
* => align: align vaddr, must be a power-of-2.
* Align is only a hint and will be ignored if the alignment fails.
*/
int
uvm_map(struct vm_map *map, vaddr_t *addr, vsize_t sz,
struct uvm_object *uobj, voff_t uoffset,
vsize_t align, unsigned int flags)
{
struct vm_map_entry *first, *last, *entry, *new;
struct uvm_map_deadq dead;
vm_prot_t prot;
vm_prot_t maxprot;
vm_inherit_t inherit;
int advice;
int error;
vaddr_t pmap_align, pmap_offset;
vaddr_t hint;
if ((map->flags & VM_MAP_INTRSAFE) == 0)
splassert(IPL_NONE);
else
splassert(IPL_VM);
/*
* We use pmap_align and pmap_offset as alignment and offset variables.
*
* Because the align parameter takes precedence over pmap prefer,
* the pmap_align will need to be set to align, with pmap_offset = 0,
* if pmap_prefer will not align.
*/
if (uoffset == UVM_UNKNOWN_OFFSET) {
pmap_align = MAX(align, PAGE_SIZE);
pmap_offset = 0;
} else {
pmap_align = MAX(PMAP_PREFER_ALIGN(), PAGE_SIZE);
pmap_offset = PMAP_PREFER_OFFSET(uoffset);
if (align == 0 ||
(align <= pmap_align && (pmap_offset & (align - 1)) == 0)) {
/* pmap_offset satisfies align, no change. */
} else {
/* Align takes precedence over pmap prefer. */
pmap_align = align;
pmap_offset = 0;
}
}
/* Decode parameters. */
prot = UVM_PROTECTION(flags);
maxprot = UVM_MAXPROTECTION(flags);
advice = UVM_ADVICE(flags);
inherit = UVM_INHERIT(flags);
error = 0;
hint = trunc_page(*addr);
TAILQ_INIT(&dead);
KASSERT((sz & (vaddr_t)PAGE_MASK) == 0);
KASSERT((align & (align - 1)) == 0);
/* Holes are incompatible with other types of mappings. */
if (flags & UVM_FLAG_HOLE) {
KASSERT(uobj == NULL && (flags & UVM_FLAG_FIXED) &&
(flags & (UVM_FLAG_OVERLAY | UVM_FLAG_COPYONW)) == 0);
}
/* Unset hint for kernel_map non-fixed allocations. */
if (!(map->flags & VM_MAP_ISVMSPACE) && !(flags & UVM_FLAG_FIXED))
hint = 0;
/* Check protection. */
if ((prot & maxprot) != prot)
return EACCES;
if (map == kernel_map &&
(prot & (PROT_WRITE | PROT_EXEC)) == (PROT_WRITE | PROT_EXEC))
panic("uvm_map: kernel map W^X violation requested");
/*
* Before grabbing the lock, allocate a map entry for later
* use to ensure we don't wait for memory while holding the
* vm_map_lock.
*/
new = uvm_mapent_alloc(map, flags);
if (new == NULL)
return ENOMEM;
if (flags & UVM_FLAG_TRYLOCK) {
if (vm_map_lock_try(map) == FALSE) {
error = EFAULT;
goto out;
}
} else {
vm_map_lock(map);
}
first = last = NULL;
if (flags & UVM_FLAG_FIXED) {
/*
* Fixed location.
*
* Note: we ignore align, pmap_prefer.
* Fill in first, last and *addr.
*/
KASSERT((*addr & PAGE_MASK) == 0);
/*
* Grow pmap to include allocated address.
* If the growth fails, the allocation will fail too.
*/
if ((map->flags & VM_MAP_ISVMSPACE) == 0 &&
uvm_maxkaddr < (*addr + sz)) {
uvm_map_kmem_grow(map, &dead,
*addr + sz - uvm_maxkaddr, flags);
}
/* Check that the space is available. */
if (flags & UVM_FLAG_UNMAP)
uvm_unmap_remove(map, *addr, *addr + sz, &dead, FALSE, TRUE);
if (!uvm_map_isavail(map, NULL, &first, &last, *addr, sz)) {
error = ENOMEM;
goto unlock;
}
} else if (*addr != 0 && (*addr & PAGE_MASK) == 0 &&
(map->flags & VM_MAP_ISVMSPACE) == VM_MAP_ISVMSPACE &&
(align == 0 || (*addr & (align - 1)) == 0) &&
uvm_map_isavail(map, NULL, &first, &last, *addr, sz)) {
/*
* Address used as hint.
*
* Note: we enforce the alignment restriction,
* but ignore pmap_prefer.
*/
} else if ((prot & PROT_EXEC) != 0 && map->uaddr_exe != NULL) {
/* Run selection algorithm for executables. */
error = uvm_addr_invoke(map, map->uaddr_exe, &first, &last,
addr, sz, pmap_align, pmap_offset, prot, hint);
/* Grow kernel memory and try again. */
if (error != 0 && (map->flags & VM_MAP_ISVMSPACE) == 0) {
uvm_map_kmem_grow(map, &dead, sz, flags);
error = uvm_addr_invoke(map, map->uaddr_exe,
&first, &last, addr, sz,
pmap_align, pmap_offset, prot, hint);
}
if (error != 0)
goto unlock;
} else {
/* Update freelists from vmspace. */
if (map->flags & VM_MAP_ISVMSPACE)
uvm_map_vmspace_update(map, &dead, flags);
error = uvm_map_findspace(map, &first, &last, addr, sz,
pmap_align, pmap_offset, prot, hint);
/* Grow kernel memory and try again. */
if (error != 0 && (map->flags & VM_MAP_ISVMSPACE) == 0) {
uvm_map_kmem_grow(map, &dead, sz, flags);
error = uvm_map_findspace(map, &first, &last, addr, sz,
pmap_align, pmap_offset, prot, hint);
}
if (error != 0)
goto unlock;
}
/* Double-check if selected address doesn't cause overflow. */
if (*addr + sz < *addr) {
error = ENOMEM;
goto unlock;
}
KASSERT((map->flags & VM_MAP_ISVMSPACE) == VM_MAP_ISVMSPACE ||
uvm_maxkaddr >= *addr + sz);
/* If we only want a query, return now. */
if (flags & UVM_FLAG_QUERY) {
error = 0;
goto unlock;
}
if (uobj == NULL)
uoffset = 0;
else if (uoffset == UVM_UNKNOWN_OFFSET) {
KASSERT(UVM_OBJ_IS_KERN_OBJECT(uobj));
uoffset = *addr - vm_map_min(kernel_map);
}
/*
* Create new entry.
* first and last may be invalidated after this call.
*/
entry = uvm_map_mkentry(map, first, last, *addr, sz, flags, &dead,
new);
if (entry == NULL) {
error = ENOMEM;
goto unlock;
}
new = NULL;
KDASSERT(entry->start == *addr && entry->end == *addr + sz);
entry->object.uvm_obj = uobj;
entry->offset = uoffset;
entry->protection = prot;
entry->max_protection = maxprot;
entry->inheritance = inherit;
entry->wired_count = 0;
entry->advice = advice;
if (prot & PROT_WRITE)
map->wserial++;
if (flags & UVM_FLAG_SYSCALL) {
entry->etype |= UVM_ET_SYSCALL;
map->wserial++;
}
if (flags & UVM_FLAG_STACK) {
entry->etype |= UVM_ET_STACK;
if (flags & UVM_FLAG_UNMAP)
map->sserial++;
}
if (uobj)
entry->etype |= UVM_ET_OBJ;
else if (flags & UVM_FLAG_HOLE)
entry->etype |= UVM_ET_HOLE;
if (flags & UVM_FLAG_NOFAULT)
entry->etype |= UVM_ET_NOFAULT;
if (flags & UVM_FLAG_WC)
entry->etype |= UVM_ET_WC;
if (flags & UVM_FLAG_COPYONW) {
entry->etype |= UVM_ET_COPYONWRITE;
if ((flags & UVM_FLAG_OVERLAY) == 0)
entry->etype |= UVM_ET_NEEDSCOPY;
}
if (flags & UVM_FLAG_CONCEAL)
entry->etype |= UVM_ET_CONCEAL;
if (flags & UVM_FLAG_OVERLAY) {
entry->aref.ar_pageoff = 0;
entry->aref.ar_amap = amap_alloc(sz, M_WAITOK, 0);
}
/* Update map and process statistics. */
if (!(flags & UVM_FLAG_HOLE)) {
map->size += sz;
if ((map->flags & VM_MAP_ISVMSPACE) && uobj == NULL &&
prot != PROT_NONE) {
((struct vmspace *)map)->vm_dused +=
uvmspace_dused(map, *addr, *addr + sz);
}
}
/*
* Try to merge entry.
*
* Userland allocations are kept separated most of the time.
* Forego the effort of merging what most of the time can't be merged
* and only try the merge if it concerns a kernel entry.
*/
if ((flags & UVM_FLAG_NOMERGE) == 0 &&
(map->flags & VM_MAP_ISVMSPACE) == 0)
uvm_mapent_tryjoin(map, entry, &dead);
unlock:
vm_map_unlock(map);
/*
* Remove dead entries.
*
* Dead entries may be the result of merging.
* uvm_map_mkentry may also create dead entries, when it attempts to
* destroy free-space entries.
*/
if (map->flags & VM_MAP_INTRSAFE)
uvm_unmap_detach_intrsafe(&dead);
else
uvm_unmap_detach(&dead, 0);
out:
if (new)
uvm_mapent_free(new);
return error;
}
/*
* True iff e1 and e2 can be joined together.
*/
int
uvm_mapent_isjoinable(struct vm_map *map, struct vm_map_entry *e1,
struct vm_map_entry *e2)
{
KDASSERT(e1 != NULL && e2 != NULL);
/* Must be the same entry type and not have free memory between. */
if (e1->etype != e2->etype || e1->end != e2->start)
return 0;
/* Submaps are never joined. */
if (UVM_ET_ISSUBMAP(e1))
return 0;
/* Never merge wired memory. */
if (VM_MAPENT_ISWIRED(e1) || VM_MAPENT_ISWIRED(e2))
return 0;
/* Protection, inheritance and advice must be equal. */
if (e1->protection != e2->protection ||
e1->max_protection != e2->max_protection ||
e1->inheritance != e2->inheritance ||
e1->advice != e2->advice)
return 0;
/* If uvm_object: object itself and offsets within object must match. */
if (UVM_ET_ISOBJ(e1)) {
if (e1->object.uvm_obj != e2->object.uvm_obj)
return 0;
if (e1->offset + (e1->end - e1->start) != e2->offset)
return 0;
}
/*
* Cannot join shared amaps.
* Note: no need to lock amap to look at refs, since we don't care
* about its exact value.
* If it is 1 (i.e. we have the only reference) it will stay there.
*/
if (e1->aref.ar_amap && amap_refs(e1->aref.ar_amap) != 1)
return 0;
if (e2->aref.ar_amap && amap_refs(e2->aref.ar_amap) != 1)
return 0;
/* Apparently, e1 and e2 match. */
return 1;
}
/*
* Join support function.
*
* Returns the merged entry on success.
* Returns NULL if the merge failed.
*/
struct vm_map_entry*
uvm_mapent_merge(struct vm_map *map, struct vm_map_entry *e1,
struct vm_map_entry *e2, struct uvm_map_deadq *dead)
{
struct uvm_addr_state *free;
/*
* Merging is not supported for map entries that
* contain an amap in e1. This should never happen
* anyway, because only kernel entries are merged.
* These do not contain amaps.
* e2 contains no real information in its amap,
* so it can be erased immediately.
*/
KASSERT(e1->aref.ar_amap == NULL);
/*
* Don't drop obj reference:
* uvm_unmap_detach will do this for us.
*/
free = uvm_map_uaddr_e(map, e1);
uvm_mapent_free_remove(map, free, e1);
free = uvm_map_uaddr_e(map, e2);
uvm_mapent_free_remove(map, free, e2);
uvm_mapent_addr_remove(map, e2);
e1->end = e2->end;
e1->guard = e2->guard;
e1->fspace = e2->fspace;
uvm_mapent_free_insert(map, free, e1);
DEAD_ENTRY_PUSH(dead, e2);
return e1;
}
/*
* Attempt forward and backward joining of entry.
*
* Returns entry after joins.
* We are guaranteed that the amap of entry is either non-existent or
* has never been used.
*/
struct vm_map_entry*
uvm_mapent_tryjoin(struct vm_map *map, struct vm_map_entry *entry,
struct uvm_map_deadq *dead)
{
struct vm_map_entry *other;
struct vm_map_entry *merged;
/* Merge with previous entry. */
other = RBT_PREV(uvm_map_addr, entry);
if (other && uvm_mapent_isjoinable(map, other, entry)) {
merged = uvm_mapent_merge(map, other, entry, dead);
if (merged)
entry = merged;
}
/*
* Merge with next entry.
*
* Because amap can only extend forward and the next entry
* probably contains sensible info, only perform forward merging
* in the absence of an amap.
*/
other = RBT_NEXT(uvm_map_addr, entry);
if (other && entry->aref.ar_amap == NULL &&
other->aref.ar_amap == NULL &&
uvm_mapent_isjoinable(map, entry, other)) {
merged = uvm_mapent_merge(map, entry, other, dead);
if (merged)
entry = merged;
}
return entry;
}
/*
* Kill entries that are no longer in a map.
*/
void
uvm_unmap_detach(struct uvm_map_deadq *deadq, int flags)
{
struct vm_map_entry *entry, *tmp;
int waitok = flags & UVM_PLA_WAITOK;
TAILQ_FOREACH_SAFE(entry, deadq, dfree.deadq, tmp) {
/* Drop reference to amap, if we've got one. */
if (entry->aref.ar_amap)
amap_unref(entry->aref.ar_amap,
entry->aref.ar_pageoff,
atop(entry->end - entry->start),
flags & AMAP_REFALL);
/* Skip entries for which we have to grab the kernel lock. */
if (UVM_ET_ISSUBMAP(entry) || UVM_ET_ISOBJ(entry))
continue;
TAILQ_REMOVE(deadq, entry, dfree.deadq);
uvm_mapent_free(entry);
}
if (TAILQ_EMPTY(deadq))
return;
KERNEL_LOCK();
while ((entry = TAILQ_FIRST(deadq)) != NULL) {
if (waitok)
uvm_pause();
/* Drop reference to our backing object, if we've got one. */
if (UVM_ET_ISSUBMAP(entry)) {
/* ... unlikely to happen, but play it safe */
uvm_map_deallocate(entry->object.sub_map);
} else if (UVM_ET_ISOBJ(entry) &&
entry->object.uvm_obj->pgops->pgo_detach) {
entry->object.uvm_obj->pgops->pgo_detach(
entry->object.uvm_obj);
}
/* Step to next. */
TAILQ_REMOVE(deadq, entry, dfree.deadq);
uvm_mapent_free(entry);
}
KERNEL_UNLOCK();
}
void
uvm_unmap_detach_intrsafe(struct uvm_map_deadq *deadq)
{
struct vm_map_entry *entry;
while ((entry = TAILQ_FIRST(deadq)) != NULL) {
KASSERT(entry->aref.ar_amap == NULL);
KASSERT(!UVM_ET_ISSUBMAP(entry));
KASSERT(!UVM_ET_ISOBJ(entry));
TAILQ_REMOVE(deadq, entry, dfree.deadq);
uvm_mapent_free(entry);
}
}
/*
* Create and insert new entry.
*
* Returned entry contains new addresses and is inserted properly in the tree.
* first and last are (probably) no longer valid.
*/
struct vm_map_entry*
uvm_map_mkentry(struct vm_map *map, struct vm_map_entry *first,
struct vm_map_entry *last, vaddr_t addr, vsize_t sz, int flags,
struct uvm_map_deadq *dead, struct vm_map_entry *new)
{
struct vm_map_entry *entry, *prev;
struct uvm_addr_state *free;
vaddr_t min, max; /* free space boundaries for new entry */
KDASSERT(map != NULL);
KDASSERT(first != NULL);
KDASSERT(last != NULL);
KDASSERT(dead != NULL);
KDASSERT(sz > 0);
KDASSERT(addr + sz > addr);
KDASSERT(first->end <= addr && VMMAP_FREE_END(first) > addr);
KDASSERT(last->start < addr + sz && VMMAP_FREE_END(last) >= addr + sz);
KDASSERT(uvm_map_isavail(map, NULL, &first, &last, addr, sz));
uvm_tree_sanity(map, __FILE__, __LINE__);
min = addr + sz;
max = VMMAP_FREE_END(last);
/* Initialize new entry. */
if (new == NULL)
entry = uvm_mapent_alloc(map, flags);
else
entry = new;
if (entry == NULL)
return NULL;
entry->offset = 0;
entry->etype = 0;
entry->wired_count = 0;
entry->aref.ar_pageoff = 0;
entry->aref.ar_amap = NULL;
entry->start = addr;
entry->end = min;
entry->guard = 0;
entry->fspace = 0;
/* Reset free space in first. */
free = uvm_map_uaddr_e(map, first);
uvm_mapent_free_remove(map, free, first);
first->guard = 0;
first->fspace = 0;
/*
* Remove all entries that are fully replaced.
* We are iterating using last in reverse order.
*/
for (; first != last; last = prev) {
prev = RBT_PREV(uvm_map_addr, last);
KDASSERT(last->start == last->end);
free = uvm_map_uaddr_e(map, last);
uvm_mapent_free_remove(map, free, last);
uvm_mapent_addr_remove(map, last);
DEAD_ENTRY_PUSH(dead, last);
}
/* Remove first if it is entirely inside <addr, addr+sz>. */
if (first->start == addr) {
uvm_mapent_addr_remove(map, first);
DEAD_ENTRY_PUSH(dead, first);
} else {
uvm_map_fix_space(map, first, VMMAP_FREE_START(first),
addr, flags);
}
/* Finally, link in entry. */
uvm_mapent_addr_insert(map, entry);
uvm_map_fix_space(map, entry, min, max, flags);
uvm_tree_sanity(map, __FILE__, __LINE__);
return entry;
}
/*
* uvm_mapent_alloc: allocate a map entry
*/
struct vm_map_entry *
uvm_mapent_alloc(struct vm_map *map, int flags)
{
struct vm_map_entry *me, *ne;
int pool_flags;
int i;
pool_flags = PR_WAITOK;
if (flags & UVM_FLAG_TRYLOCK)
pool_flags = PR_NOWAIT;
if (map->flags & VM_MAP_INTRSAFE || cold) {
mtx_enter(&uvm_kmapent_mtx);
if (SLIST_EMPTY(&uvm.kentry_free)) {
ne = km_alloc(PAGE_SIZE, &kv_page, &kp_dirty,
&kd_nowait);
if (ne == NULL)
panic("uvm_mapent_alloc: cannot allocate map "
"entry");
for (i = 0; i < PAGE_SIZE / sizeof(*ne); i++) {
SLIST_INSERT_HEAD(&uvm.kentry_free,
&ne[i], daddrs.addr_kentry);
}
if (ratecheck(&uvm_kmapent_last_warn_time,
&uvm_kmapent_warn_rate))
printf("uvm_mapent_alloc: out of static "
"map entries\n");
}
me = SLIST_FIRST(&uvm.kentry_free);
SLIST_REMOVE_HEAD(&uvm.kentry_free, daddrs.addr_kentry);
uvmexp.kmapent++;
mtx_leave(&uvm_kmapent_mtx);
me->flags = UVM_MAP_STATIC;
} else if (map == kernel_map) {
splassert(IPL_NONE);
me = pool_get(&uvm_map_entry_kmem_pool, pool_flags);
if (me == NULL)
goto out;
me->flags = UVM_MAP_KMEM;
} else {
splassert(IPL_NONE);
me = pool_get(&uvm_map_entry_pool, pool_flags);
if (me == NULL)
goto out;
me->flags = 0;
}
RBT_POISON(uvm_map_addr, me, UVMMAP_DEADBEEF);
out:
return me;
}
/*
* uvm_mapent_free: free map entry
*
* => XXX: static pool for kernel map?
*/
void
uvm_mapent_free(struct vm_map_entry *me)
{
if (me->flags & UVM_MAP_STATIC) {
mtx_enter(&uvm_kmapent_mtx);
SLIST_INSERT_HEAD(&uvm.kentry_free, me, daddrs.addr_kentry);
uvmexp.kmapent--;
mtx_leave(&uvm_kmapent_mtx);
} else if (me->flags & UVM_MAP_KMEM) {
splassert(IPL_NONE);
pool_put(&uvm_map_entry_kmem_pool, me);
} else {
splassert(IPL_NONE);
pool_put(&uvm_map_entry_pool, me);
}
}
/*
* uvm_map_lookup_entry: find map entry at or before an address.
*
* => map must at least be read-locked by caller
* => entry is returned in "entry"
* => return value is true if address is in the returned entry
* ET_HOLE entries are considered to not contain a mapping, ergo FALSE is
* returned for those mappings.
*/
boolean_t
uvm_map_lookup_entry(struct vm_map *map, vaddr_t address,
struct vm_map_entry **entry)
{
*entry = uvm_map_entrybyaddr(&map->addr, address);
return *entry != NULL && !UVM_ET_ISHOLE(*entry) &&
(*entry)->start <= address && (*entry)->end > address;
}
/*
* Stack must be in a MAP_STACK entry. PROT_NONE indicates stack not yet
* grown -- then uvm_map_check_region_range() should not cache the entry
* because growth won't be seen.
*/
int
uvm_map_inentry_sp(vm_map_entry_t entry)
{
if ((entry->etype & UVM_ET_STACK) == 0) {
if (entry->protection == PROT_NONE)
return (-1); /* don't update range */
return (0);
}
return (1);
}
/*
* The system call must not come from a writeable entry, W^X is violated.
* (Would be nice if we can spot aliasing, which is also kind of bad)
*
* The system call must come from an syscall-labeled entry (which are
* the text regions of the main program, sigtramp, ld.so, or libc).
*/
int
uvm_map_inentry_pc(vm_map_entry_t entry)
{
if (entry->protection & PROT_WRITE)
return (0); /* not permitted */
if ((entry->etype & UVM_ET_SYSCALL) == 0)
return (0); /* not permitted */
return (1);
}
int
uvm_map_inentry_recheck(u_long serial, vaddr_t addr, struct p_inentry *ie)
{
return (serial != ie->ie_serial || ie->ie_start == 0 ||
addr < ie->ie_start || addr >= ie->ie_end);
}
/*
* Inside a vm_map find the reg address and verify it via function.
* Remember low and high addresses of region if valid and return TRUE,
* else return FALSE.
*/
boolean_t
uvm_map_inentry_fix(struct proc *p, struct p_inentry *ie, vaddr_t addr,
int (*fn)(vm_map_entry_t), u_long serial)
{
vm_map_t map = &p->p_vmspace->vm_map;
vm_map_entry_t entry;
int ret;
if (addr < map->min_offset || addr >= map->max_offset)
return (FALSE);
/* lock map */
vm_map_lock_read(map);
/* lookup */
if (!uvm_map_lookup_entry(map, trunc_page(addr), &entry)) {
vm_map_unlock_read(map);
return (FALSE);
}
ret = (*fn)(entry);
if (ret == 0) {
vm_map_unlock_read(map);
return (FALSE);
} else if (ret == 1) {
ie->ie_start = entry->start;
ie->ie_end = entry->end;
ie->ie_serial = serial;
} else {
/* do not update, re-check later */
}
vm_map_unlock_read(map);
return (TRUE);
}
boolean_t
uvm_map_inentry(struct proc *p, struct p_inentry *ie, vaddr_t addr,
const char *fmt, int (*fn)(vm_map_entry_t), u_long serial)
{
union sigval sv;
boolean_t ok = TRUE;
if (uvm_map_inentry_recheck(serial, addr, ie)) {
ok = uvm_map_inentry_fix(p, ie, addr, fn, serial);
if (!ok) {
KERNEL_LOCK();
printf(fmt, p->p_p->ps_comm, p->p_p->ps_pid, p->p_tid,
addr, ie->ie_start, ie->ie_end-1);
p->p_p->ps_acflag |= AMAP;
sv.sival_ptr = (void *)PROC_PC(p);
trapsignal(p, SIGSEGV, 0, SEGV_ACCERR, sv);
KERNEL_UNLOCK();
}
}
return (ok);
}
/*
* Check whether the given address range can be converted to a MAP_STACK
* mapping.
*
* Must be called with map locked.
*/
boolean_t
uvm_map_is_stack_remappable(struct vm_map *map, vaddr_t addr, vaddr_t sz)
{
vaddr_t end = addr + sz;
struct vm_map_entry *first, *iter, *prev = NULL;
if (!uvm_map_lookup_entry(map, addr, &first)) {
printf("map stack 0x%lx-0x%lx of map %p failed: no mapping\n",
addr, end, map);
return FALSE;
}
/*
* Check that the address range exists and is contiguous.
*/
for (iter = first; iter != NULL && iter->start < end;
prev = iter, iter = RBT_NEXT(uvm_map_addr, iter)) {
/*
* Make sure that we do not have holes in the range.
*/
#if 0
if (prev != NULL) {
printf("prev->start 0x%lx, prev->end 0x%lx, "
"iter->start 0x%lx, iter->end 0x%lx\n",
prev->start, prev->end, iter->start, iter->end);
}
#endif
if (prev != NULL && prev->end != iter->start) {
printf("map stack 0x%lx-0x%lx of map %p failed: "
"hole in range\n", addr, end, map);
return FALSE;
}
if (iter->start == iter->end || UVM_ET_ISHOLE(iter)) {
printf("map stack 0x%lx-0x%lx of map %p failed: "
"hole in range\n", addr, end, map);
return FALSE;
}
}
return TRUE;
}
/*
* Remap the middle-pages of an existing mapping as a stack range.
* If there exists a previous contiguous mapping with the given range
* [addr, addr + sz), with protection PROT_READ|PROT_WRITE, then the
* mapping is dropped, and a new anon mapping is created and marked as
* a stack.
*
* Must be called with map unlocked.
*/
int
uvm_map_remap_as_stack(struct proc *p, vaddr_t addr, vaddr_t sz)
{
vm_map_t map = &p->p_vmspace->vm_map;
vaddr_t start, end;
int error;
int flags = UVM_MAPFLAG(PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_INHERIT_COPY, MADV_NORMAL,
UVM_FLAG_STACK | UVM_FLAG_FIXED | UVM_FLAG_UNMAP |
UVM_FLAG_COPYONW);
start = round_page(addr);
end = trunc_page(addr + sz);
#ifdef MACHINE_STACK_GROWS_UP
if (end == addr + sz)
end -= PAGE_SIZE;
#else
if (start == addr)
start += PAGE_SIZE;
#endif
if (start < map->min_offset || end >= map->max_offset || end < start)
return EINVAL;
error = uvm_mapanon(map, &start, end - start, 0, flags);
if (error != 0)
printf("map stack for pid %d failed\n", p->p_p->ps_pid);
return error;
}
/*
* uvm_map_pie: return a random load address for a PIE executable
* properly aligned.
*/
#ifndef VM_PIE_MAX_ADDR
#define VM_PIE_MAX_ADDR (VM_MAXUSER_ADDRESS / 4)
#endif
#ifndef VM_PIE_MIN_ADDR
#define VM_PIE_MIN_ADDR VM_MIN_ADDRESS
#endif
#ifndef VM_PIE_MIN_ALIGN
#define VM_PIE_MIN_ALIGN PAGE_SIZE
#endif
vaddr_t
uvm_map_pie(vaddr_t align)
{
vaddr_t addr, space, min;
align = MAX(align, VM_PIE_MIN_ALIGN);
/* round up to next alignment */
min = (VM_PIE_MIN_ADDR + align - 1) & ~(align - 1);
if (align >= VM_PIE_MAX_ADDR || min >= VM_PIE_MAX_ADDR)
return (align);
space = (VM_PIE_MAX_ADDR - min) / align;
space = MIN(space, (u_int32_t)-1);
addr = (vaddr_t)arc4random_uniform((u_int32_t)space) * align;
addr += min;
return (addr);
}
void
uvm_unmap(struct vm_map *map, vaddr_t start, vaddr_t end)
{
struct uvm_map_deadq dead;
KASSERT((start & (vaddr_t)PAGE_MASK) == 0 &&
(end & (vaddr_t)PAGE_MASK) == 0);
TAILQ_INIT(&dead);
vm_map_lock(map);
uvm_unmap_remove(map, start, end, &dead, FALSE, TRUE);
vm_map_unlock(map);
if (map->flags & VM_MAP_INTRSAFE)
uvm_unmap_detach_intrsafe(&dead);
else
uvm_unmap_detach(&dead, 0);
}
/*
* Mark entry as free.
*
* entry will be put on the dead list.
* The free space will be merged into the previous or a new entry,
* unless markfree is false.
*/
void
uvm_mapent_mkfree(struct vm_map *map, struct vm_map_entry *entry,
struct vm_map_entry **prev_ptr, struct uvm_map_deadq *dead,
boolean_t markfree)
{
struct uvm_addr_state *free;
struct vm_map_entry *prev;
vaddr_t addr; /* Start of freed range. */
vaddr_t end; /* End of freed range. */
prev = *prev_ptr;
if (prev == entry)
*prev_ptr = prev = NULL;
if (prev == NULL ||
VMMAP_FREE_END(prev) != entry->start)
prev = RBT_PREV(uvm_map_addr, entry);
/* Entry is describing only free memory and has nothing to drain into. */
if (prev == NULL && entry->start == entry->end && markfree) {
*prev_ptr = entry;
return;
}
addr = entry->start;
end = VMMAP_FREE_END(entry);
free = uvm_map_uaddr_e(map, entry);
uvm_mapent_free_remove(map, free, entry);
uvm_mapent_addr_remove(map, entry);
DEAD_ENTRY_PUSH(dead, entry);
if (markfree) {
if (prev) {
free = uvm_map_uaddr_e(map, prev);
uvm_mapent_free_remove(map, free, prev);
}
*prev_ptr = uvm_map_fix_space(map, prev, addr, end, 0);
}
}
/*
* Unwire and release referenced amap and object from map entry.
*/
void
uvm_unmap_kill_entry_withlock(struct vm_map *map, struct vm_map_entry *entry,
int needlock)
{
/* Unwire removed map entry. */
if (VM_MAPENT_ISWIRED(entry)) {
KERNEL_LOCK();
entry->wired_count = 0;
uvm_fault_unwire_locked(map, entry->start, entry->end);
KERNEL_UNLOCK();
}
if (needlock)
uvm_map_lock_entry(entry);
/* Entry-type specific code. */
if (UVM_ET_ISHOLE(entry)) {
/* Nothing to be done for holes. */
} else if (map->flags & VM_MAP_INTRSAFE) {
KASSERT(vm_map_pmap(map) == pmap_kernel());
uvm_km_pgremove_intrsafe(entry->start, entry->end);
} else if (UVM_ET_ISOBJ(entry) &&
UVM_OBJ_IS_KERN_OBJECT(entry->object.uvm_obj)) {
KASSERT(vm_map_pmap(map) == pmap_kernel());
/*
* Note: kernel object mappings are currently used in
* two ways:
* [1] "normal" mappings of pages in the kernel object
* [2] uvm_km_valloc'd allocations in which we
* pmap_enter in some non-kernel-object page
* (e.g. vmapbuf).
*
* for case [1], we need to remove the mapping from
* the pmap and then remove the page from the kernel
* object (because, once pages in a kernel object are
* unmapped they are no longer needed, unlike, say,
* a vnode where you might want the data to persist
* until flushed out of a queue).
*
* for case [2], we need to remove the mapping from
* the pmap. there shouldn't be any pages at the
* specified offset in the kernel object [but it
* doesn't hurt to call uvm_km_pgremove just to be
* safe?]
*
* uvm_km_pgremove currently does the following:
* for pages in the kernel object range:
* - drops the swap slot
* - uvm_pagefree the page
*
* note there is version of uvm_km_pgremove() that
* is used for "intrsafe" objects.
*/
/*
* remove mappings from pmap and drop the pages
* from the object. offsets are always relative
* to vm_map_min(kernel_map).
*/
uvm_km_pgremove(entry->object.uvm_obj, entry->start,
entry->end);
} else {
/* remove mappings the standard way. */
pmap_remove(map->pmap, entry->start, entry->end);
}
if (needlock)
uvm_map_unlock_entry(entry);
}
void
uvm_unmap_kill_entry(struct vm_map *map, struct vm_map_entry *entry)
{
uvm_unmap_kill_entry_withlock(map, entry, 0);
}
/*
* Remove all entries from start to end.
*
* If remove_holes, then remove ET_HOLE entries as well.
* If markfree, entry will be properly marked free, otherwise, no replacement
* entry will be put in the tree (corrupting the tree).
*/
void
uvm_unmap_remove(struct vm_map *map, vaddr_t start, vaddr_t end,
struct uvm_map_deadq *dead, boolean_t remove_holes,
boolean_t markfree)
{
struct vm_map_entry *prev_hint, *next, *entry;
start = MAX(start, map->min_offset);
end = MIN(end, map->max_offset);
if (start >= end)
return;
if ((map->flags & VM_MAP_INTRSAFE) == 0)
splassert(IPL_NONE);
else
splassert(IPL_VM);
/* Find first affected entry. */
entry = uvm_map_entrybyaddr(&map->addr, start);
KDASSERT(entry != NULL && entry->start <= start);
if (entry->end <= start && markfree)
entry = RBT_NEXT(uvm_map_addr, entry);
else
UVM_MAP_CLIP_START(map, entry, start);
/*
* Iterate entries until we reach end address.
* prev_hint hints where the freed space can be appended to.
*/
prev_hint = NULL;
for (; entry != NULL && entry->start < end; entry = next) {
KDASSERT(entry->start >= start);
if (entry->end > end || !markfree)
UVM_MAP_CLIP_END(map, entry, end);
KDASSERT(entry->start >= start && entry->end <= end);
next = RBT_NEXT(uvm_map_addr, entry);
/* Don't remove holes unless asked to do so. */
if (UVM_ET_ISHOLE(entry)) {
if (!remove_holes) {
prev_hint = entry;
continue;
}
}
/* A stack has been removed.. */
if (UVM_ET_ISSTACK(entry) && (map->flags & VM_MAP_ISVMSPACE))
map->sserial++;
/* Kill entry. */
uvm_unmap_kill_entry_withlock(map, entry, 1);
/* Update space usage. */
if ((map->flags & VM_MAP_ISVMSPACE) &&
entry->object.uvm_obj == NULL &&
entry->protection != PROT_NONE &&
!UVM_ET_ISHOLE(entry)) {
((struct vmspace *)map)->vm_dused -=
uvmspace_dused(map, entry->start, entry->end);
}
if (!UVM_ET_ISHOLE(entry))
map->size -= entry->end - entry->start;
/* Actual removal of entry. */
uvm_mapent_mkfree(map, entry, &prev_hint, dead, markfree);
}
pmap_update(vm_map_pmap(map));
#ifdef VMMAP_DEBUG
if (markfree) {
for (entry = uvm_map_entrybyaddr(&map->addr, start);
entry != NULL && entry->start < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
KDASSERT(entry->end <= start ||
entry->start == entry->end ||
UVM_ET_ISHOLE(entry));
}
} else {
vaddr_t a;
for (a = start; a < end; a += PAGE_SIZE)
KDASSERT(uvm_map_entrybyaddr(&map->addr, a) == NULL);
}
#endif
}
/*
* Mark all entries from first until end (exclusive) as pageable.
*
* Lock must be exclusive on entry and will not be touched.
*/
void
uvm_map_pageable_pgon(struct vm_map *map, struct vm_map_entry *first,
struct vm_map_entry *end, vaddr_t start_addr, vaddr_t end_addr)
{
struct vm_map_entry *iter;
for (iter = first; iter != end;
iter = RBT_NEXT(uvm_map_addr, iter)) {
KDASSERT(iter->start >= start_addr && iter->end <= end_addr);
if (!VM_MAPENT_ISWIRED(iter) || UVM_ET_ISHOLE(iter))
continue;
iter->wired_count = 0;
uvm_fault_unwire_locked(map, iter->start, iter->end);
}
}
/*
* Mark all entries from first until end (exclusive) as wired.
*
* Lockflags determines the lock state on return from this function.
* Lock must be exclusive on entry.
*/
int
uvm_map_pageable_wire(struct vm_map *map, struct vm_map_entry *first,
struct vm_map_entry *end, vaddr_t start_addr, vaddr_t end_addr,
int lockflags)
{
struct vm_map_entry *iter;
#ifdef DIAGNOSTIC
unsigned int timestamp_save;
#endif
int error;
/*
* Wire pages in two passes:
*
* 1: holding the write lock, we create any anonymous maps that need
* to be created. then we clip each map entry to the region to
* be wired and increment its wiring count.
*
* 2: we downgrade to a read lock, and call uvm_fault_wire to fault
* in the pages for any newly wired area (wired_count == 1).
*
* downgrading to a read lock for uvm_fault_wire avoids a possible
* deadlock with another thread that may have faulted on one of
* the pages to be wired (it would mark the page busy, blocking
* us, then in turn block on the map lock that we hold).
* because we keep the read lock on the map, the copy-on-write
* status of the entries we modify here cannot change.
*/
for (iter = first; iter != end;
iter = RBT_NEXT(uvm_map_addr, iter)) {
KDASSERT(iter->start >= start_addr && iter->end <= end_addr);
if (UVM_ET_ISHOLE(iter) || iter->start == iter->end ||
iter->protection == PROT_NONE)
continue;
/*
* Perform actions of vm_map_lookup that need the write lock.
* - create an anonymous map for copy-on-write
* - anonymous map for zero-fill
* Skip submaps.
*/
if (!VM_MAPENT_ISWIRED(iter) && !UVM_ET_ISSUBMAP(iter) &&
UVM_ET_ISNEEDSCOPY(iter) &&
((iter->protection & PROT_WRITE) ||
iter->object.uvm_obj == NULL)) {
amap_copy(map, iter, M_WAITOK,
UVM_ET_ISSTACK(iter) ? FALSE : TRUE,
iter->start, iter->end);
}
iter->wired_count++;
}
/*
* Pass 2.
*/
#ifdef DIAGNOSTIC
timestamp_save = map->timestamp;
#endif
vm_map_busy(map);
vm_map_downgrade(map);
error = 0;
for (iter = first; error == 0 && iter != end;
iter = RBT_NEXT(uvm_map_addr, iter)) {
if (UVM_ET_ISHOLE(iter) || iter->start == iter->end ||
iter->protection == PROT_NONE)
continue;
error = uvm_fault_wire(map, iter->start, iter->end,
iter->protection);
}
if (error) {
/*
* uvm_fault_wire failure
*
* Reacquire lock and undo our work.
*/
vm_map_upgrade(map);
vm_map_unbusy(map);
#ifdef DIAGNOSTIC
if (timestamp_save != map->timestamp)
panic("uvm_map_pageable_wire: stale map");
#endif
/*
* first is no longer needed to restart loops.
* Use it as iterator to unmap successful mappings.
*/
for (; first != iter;
first = RBT_NEXT(uvm_map_addr, first)) {
if (UVM_ET_ISHOLE(first) ||
first->start == first->end ||
first->protection == PROT_NONE)
continue;
first->wired_count--;
if (!VM_MAPENT_ISWIRED(first)) {
uvm_fault_unwire_locked(map,
first->start, first->end);
}
}
/* decrease counter in the rest of the entries */
for (; iter != end;
iter = RBT_NEXT(uvm_map_addr, iter)) {
if (UVM_ET_ISHOLE(iter) || iter->start == iter->end ||
iter->protection == PROT_NONE)
continue;
iter->wired_count--;
}
if ((lockflags & UVM_LK_EXIT) == 0)
vm_map_unlock(map);
return error;
}
/* We are currently holding a read lock. */
if ((lockflags & UVM_LK_EXIT) == 0) {
vm_map_unbusy(map);
vm_map_unlock_read(map);
} else {
vm_map_upgrade(map);
vm_map_unbusy(map);
#ifdef DIAGNOSTIC
if (timestamp_save != map->timestamp)
panic("uvm_map_pageable_wire: stale map");
#endif
}
return 0;
}
/*
* uvm_map_pageable: set pageability of a range in a map.
*
* Flags:
* UVM_LK_ENTER: map is already locked by caller
* UVM_LK_EXIT: don't unlock map on exit
*
* The full range must be in use (entries may not have fspace != 0).
* UVM_ET_HOLE counts as unmapped.
*/
int
uvm_map_pageable(struct vm_map *map, vaddr_t start, vaddr_t end,
boolean_t new_pageable, int lockflags)
{
struct vm_map_entry *first, *last, *tmp;
int error;
start = trunc_page(start);
end = round_page(end);
if (start > end)
return EINVAL;
if (start == end)
return 0; /* nothing to do */
if (start < map->min_offset)
return EFAULT; /* why? see first XXX below */
if (end > map->max_offset)
return EINVAL; /* why? see second XXX below */
KASSERT(map->flags & VM_MAP_PAGEABLE);
if ((lockflags & UVM_LK_ENTER) == 0)
vm_map_lock(map);
/*
* Find first entry.
*
* Initial test on start is different, because of the different
* error returned. Rest is tested further down.
*/
first = uvm_map_entrybyaddr(&map->addr, start);
if (first->end <= start || UVM_ET_ISHOLE(first)) {
/*
* XXX if the first address is not mapped, it is EFAULT?
*/
error = EFAULT;
goto out;
}
/* Check that the range has no holes. */
for (last = first; last != NULL && last->start < end;
last = RBT_NEXT(uvm_map_addr, last)) {
if (UVM_ET_ISHOLE(last) ||
(last->end < end && VMMAP_FREE_END(last) != last->end)) {
/*
* XXX unmapped memory in range, why is it EINVAL
* instead of EFAULT?
*/
error = EINVAL;
goto out;
}
}
/*
* Last ended at the first entry after the range.
* Move back one step.
*
* Note that last may be NULL.
*/
if (last == NULL) {
last = RBT_MAX(uvm_map_addr, &map->addr);
if (last->end < end) {
error = EINVAL;
goto out;
}
} else {
KASSERT(last != first);
last = RBT_PREV(uvm_map_addr, last);
}
/* Wire/unwire pages here. */
if (new_pageable) {
/*
* Mark pageable.
* entries that are not wired are untouched.
*/
if (VM_MAPENT_ISWIRED(first))
UVM_MAP_CLIP_START(map, first, start);
/*
* Split last at end.
* Make tmp be the first entry after what is to be touched.
* If last is not wired, don't touch it.
*/
if (VM_MAPENT_ISWIRED(last)) {
UVM_MAP_CLIP_END(map, last, end);
tmp = RBT_NEXT(uvm_map_addr, last);
} else
tmp = last;
uvm_map_pageable_pgon(map, first, tmp, start, end);
error = 0;
out:
if ((lockflags & UVM_LK_EXIT) == 0)
vm_map_unlock(map);
return error;
} else {
/*
* Mark entries wired.
* entries are always touched (because recovery needs this).
*/
if (!VM_MAPENT_ISWIRED(first))
UVM_MAP_CLIP_START(map, first, start);
/*
* Split last at end.
* Make tmp be the first entry after what is to be touched.
* If last is not wired, don't touch it.
*/
if (!VM_MAPENT_ISWIRED(last)) {
UVM_MAP_CLIP_END(map, last, end);
tmp = RBT_NEXT(uvm_map_addr, last);
} else
tmp = last;
return uvm_map_pageable_wire(map, first, tmp, start, end,
lockflags);
}
}
/*
* uvm_map_pageable_all: special case of uvm_map_pageable - affects
* all mapped regions.
*
* Map must not be locked.
* If no flags are specified, all ragions are unwired.
*/
int
uvm_map_pageable_all(struct vm_map *map, int flags, vsize_t limit)
{
vsize_t size;
struct vm_map_entry *iter;
KASSERT(map->flags & VM_MAP_PAGEABLE);
vm_map_lock(map);
if (flags == 0) {
uvm_map_pageable_pgon(map, RBT_MIN(uvm_map_addr, &map->addr),
NULL, map->min_offset, map->max_offset);
vm_map_modflags(map, 0, VM_MAP_WIREFUTURE);
vm_map_unlock(map);
return 0;
}
if (flags & MCL_FUTURE)
vm_map_modflags(map, VM_MAP_WIREFUTURE, 0);
if (!(flags & MCL_CURRENT)) {
vm_map_unlock(map);
return 0;
}
/*
* Count number of pages in all non-wired entries.
* If the number exceeds the limit, abort.
*/
size = 0;
RBT_FOREACH(iter, uvm_map_addr, &map->addr) {
if (VM_MAPENT_ISWIRED(iter) || UVM_ET_ISHOLE(iter))
continue;
size += iter->end - iter->start;
}
if (atop(size) + uvmexp.wired > uvmexp.wiredmax) {
vm_map_unlock(map);
return ENOMEM;
}
/* XXX non-pmap_wired_count case must be handled by caller */
#ifdef pmap_wired_count
if (limit != 0 &&
size + ptoa(pmap_wired_count(vm_map_pmap(map))) > limit) {
vm_map_unlock(map);
return ENOMEM;
}
#endif
/*
* uvm_map_pageable_wire will release lock
*/
return uvm_map_pageable_wire(map, RBT_MIN(uvm_map_addr, &map->addr),
NULL, map->min_offset, map->max_offset, 0);
}
/*
* Initialize map.
*
* Allocates sufficient entries to describe the free memory in the map.
*/
void
uvm_map_setup(struct vm_map *map, pmap_t pmap, vaddr_t min, vaddr_t max,
int flags)
{
int i;
KASSERT((min & (vaddr_t)PAGE_MASK) == 0);
KASSERT((max & (vaddr_t)PAGE_MASK) == 0 ||
(max & (vaddr_t)PAGE_MASK) == (vaddr_t)PAGE_MASK);
/*
* Update parameters.
*
* This code handles (vaddr_t)-1 and other page mask ending addresses
* properly.
* We lose the top page if the full virtual address space is used.
*/
if (max & (vaddr_t)PAGE_MASK) {
max += 1;
if (max == 0) /* overflow */
max -= PAGE_SIZE;
}
RBT_INIT(uvm_map_addr, &map->addr);
map->uaddr_exe = NULL;
for (i = 0; i < nitems(map->uaddr_any); ++i)
map->uaddr_any[i] = NULL;
map->uaddr_brk_stack = NULL;
map->pmap = pmap;
map->size = 0;
map->ref_count = 0;
map->min_offset = min;
map->max_offset = max;
map->b_start = map->b_end = 0; /* Empty brk() area by default. */
map->s_start = map->s_end = 0; /* Empty stack area by default. */
map->flags = flags;
map->timestamp = 0;
if (flags & VM_MAP_ISVMSPACE)
rw_init_flags(&map->lock, "vmmaplk", RWL_DUPOK);
else
rw_init(&map->lock, "kmmaplk");
mtx_init(&map->mtx, IPL_VM);
mtx_init(&map->flags_lock, IPL_VM);
/* Configure the allocators. */
if (flags & VM_MAP_ISVMSPACE)
uvm_map_setup_md(map);
else
map->uaddr_any[3] = &uaddr_kbootstrap;
/*
* Fill map entries.
* We do not need to write-lock the map here because only the current
* thread sees it right now. Initialize ref_count to 0 above to avoid
* bogus triggering of lock-not-held assertions.
*/
uvm_map_setup_entries(map);
uvm_tree_sanity(map, __FILE__, __LINE__);
map->ref_count = 1;
}
/*
* Destroy the map.
*
* This is the inverse operation to uvm_map_setup.
*/
void
uvm_map_teardown(struct vm_map *map)
{
struct uvm_map_deadq dead_entries;
struct vm_map_entry *entry, *tmp;
#ifdef VMMAP_DEBUG
size_t numq, numt;
#endif
int i;
KERNEL_ASSERT_LOCKED();
KERNEL_UNLOCK();
KERNEL_ASSERT_UNLOCKED();
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
/* Remove address selectors. */
uvm_addr_destroy(map->uaddr_exe);
map->uaddr_exe = NULL;
for (i = 0; i < nitems(map->uaddr_any); i++) {
uvm_addr_destroy(map->uaddr_any[i]);
map->uaddr_any[i] = NULL;
}
uvm_addr_destroy(map->uaddr_brk_stack);
map->uaddr_brk_stack = NULL;
/*
* Remove entries.
*
* The following is based on graph breadth-first search.
*
* In color terms:
* - the dead_entries set contains all nodes that are reachable
* (i.e. both the black and the grey nodes)
* - any entry not in dead_entries is white
* - any entry that appears in dead_entries before entry,
* is black, the rest is grey.
* The set [entry, end] is also referred to as the wavefront.
*
* Since the tree is always a fully connected graph, the breadth-first
* search guarantees that each vmmap_entry is visited exactly once.
* The vm_map is broken down in linear time.
*/
TAILQ_INIT(&dead_entries);
if ((entry = RBT_ROOT(uvm_map_addr, &map->addr)) != NULL)
DEAD_ENTRY_PUSH(&dead_entries, entry);
while (entry != NULL) {
sched_pause(yield);
uvm_unmap_kill_entry(map, entry);
if ((tmp = RBT_LEFT(uvm_map_addr, entry)) != NULL)
DEAD_ENTRY_PUSH(&dead_entries, tmp);
if ((tmp = RBT_RIGHT(uvm_map_addr, entry)) != NULL)
DEAD_ENTRY_PUSH(&dead_entries, tmp);
/* Update wave-front. */
entry = TAILQ_NEXT(entry, dfree.deadq);
}
#ifdef VMMAP_DEBUG
numt = numq = 0;
RBT_FOREACH(entry, uvm_map_addr, &map->addr)
numt++;
TAILQ_FOREACH(entry, &dead_entries, dfree.deadq)
numq++;
KASSERT(numt == numq);
#endif
uvm_unmap_detach(&dead_entries, UVM_PLA_WAITOK);
KERNEL_LOCK();
pmap_destroy(map->pmap);
map->pmap = NULL;
}
/*
* Populate map with free-memory entries.
*
* Map must be initialized and empty.
*/
void
uvm_map_setup_entries(struct vm_map *map)
{
KDASSERT(RBT_EMPTY(uvm_map_addr, &map->addr));
uvm_map_fix_space(map, NULL, map->min_offset, map->max_offset, 0);
}
/*
* Split entry at given address.
*
* orig: entry that is to be split.
* next: a newly allocated map entry that is not linked.
* split: address at which the split is done.
*/
void
uvm_map_splitentry(struct vm_map *map, struct vm_map_entry *orig,
struct vm_map_entry *next, vaddr_t split)
{
struct uvm_addr_state *free, *free_before;
vsize_t adj;
if ((split & PAGE_MASK) != 0) {
panic("uvm_map_splitentry: split address 0x%lx "
"not on page boundary!", split);
}
KDASSERT(map != NULL && orig != NULL && next != NULL);
uvm_tree_sanity(map, __FILE__, __LINE__);
KASSERT(orig->start < split && VMMAP_FREE_END(orig) > split);
#ifdef VMMAP_DEBUG
KDASSERT(RBT_FIND(uvm_map_addr, &map->addr, orig) == orig);
KDASSERT(RBT_FIND(uvm_map_addr, &map->addr, next) != next);
#endif /* VMMAP_DEBUG */
/*
* Free space will change, unlink from free space tree.
*/
free = uvm_map_uaddr_e(map, orig);
uvm_mapent_free_remove(map, free, orig);
adj = split - orig->start;
uvm_mapent_copy(orig, next);
if (split >= orig->end) {
next->etype = 0;
next->offset = 0;
next->wired_count = 0;
next->start = next->end = split;
next->guard = 0;
next->fspace = VMMAP_FREE_END(orig) - split;
next->aref.ar_amap = NULL;
next->aref.ar_pageoff = 0;
orig->guard = MIN(orig->guard, split - orig->end);
orig->fspace = split - VMMAP_FREE_START(orig);
} else {
orig->fspace = 0;
orig->guard = 0;
orig->end = next->start = split;
if (next->aref.ar_amap) {
amap_splitref(&orig->aref, &next->aref, adj);
}
if (UVM_ET_ISSUBMAP(orig)) {
uvm_map_reference(next->object.sub_map);
next->offset += adj;
} else if (UVM_ET_ISOBJ(orig)) {
if (next->object.uvm_obj->pgops &&
next->object.uvm_obj->pgops->pgo_reference) {
KERNEL_LOCK();
next->object.uvm_obj->pgops->pgo_reference(
next->object.uvm_obj);
KERNEL_UNLOCK();
}
next->offset += adj;
}
}
/*
* Link next into address tree.
* Link orig and next into free-space tree.
*
* Don't insert 'next' into the addr tree until orig has been linked,
* in case the free-list looks at adjecent entries in the addr tree
* for its decisions.
*/
if (orig->fspace > 0)
free_before = free;
else
free_before = uvm_map_uaddr_e(map, orig);
uvm_mapent_free_insert(map, free_before, orig);
uvm_mapent_addr_insert(map, next);
uvm_mapent_free_insert(map, free, next);
uvm_tree_sanity(map, __FILE__, __LINE__);
}
#ifdef VMMAP_DEBUG
void
uvm_tree_assert(struct vm_map *map, int test, char *test_str,
char *file, int line)
{
char* map_special;
if (test)
return;
if (map == kernel_map)
map_special = " (kernel_map)";
else if (map == kmem_map)
map_special = " (kmem_map)";
else
map_special = "";
panic("uvm_tree_sanity %p%s (%s %d): %s", map, map_special, file,
line, test_str);
}
/*
* Check that map is sane.
*/
void
uvm_tree_sanity(struct vm_map *map, char *file, int line)
{
struct vm_map_entry *iter;
vaddr_t addr;
vaddr_t min, max, bound; /* Bounds checker. */
struct uvm_addr_state *free;
addr = vm_map_min(map);
RBT_FOREACH(iter, uvm_map_addr, &map->addr) {
/*
* Valid start, end.
* Catch overflow for end+fspace.
*/
UVM_ASSERT(map, iter->end >= iter->start, file, line);
UVM_ASSERT(map, VMMAP_FREE_END(iter) >= iter->end, file, line);
/* May not be empty. */
UVM_ASSERT(map, iter->start < VMMAP_FREE_END(iter),
file, line);
/* Addresses for entry must lie within map boundaries. */
UVM_ASSERT(map, iter->start >= vm_map_min(map) &&
VMMAP_FREE_END(iter) <= vm_map_max(map), file, line);
/* Tree may not have gaps. */
UVM_ASSERT(map, iter->start == addr, file, line);
addr = VMMAP_FREE_END(iter);
/*
* Free space may not cross boundaries, unless the same
* free list is used on both sides of the border.
*/
min = VMMAP_FREE_START(iter);
max = VMMAP_FREE_END(iter);
while (min < max &&
(bound = uvm_map_boundary(map, min, max)) != max) {
UVM_ASSERT(map,
uvm_map_uaddr(map, bound - 1) ==
uvm_map_uaddr(map, bound),
file, line);
min = bound;
}
free = uvm_map_uaddr_e(map, iter);
if (free) {
UVM_ASSERT(map, (iter->etype & UVM_ET_FREEMAPPED) != 0,
file, line);
} else {
UVM_ASSERT(map, (iter->etype & UVM_ET_FREEMAPPED) == 0,
file, line);
}
}
UVM_ASSERT(map, addr == vm_map_max(map), file, line);
}
void
uvm_tree_size_chk(struct vm_map *map, char *file, int line)
{
struct vm_map_entry *iter;
vsize_t size;
size = 0;
RBT_FOREACH(iter, uvm_map_addr, &map->addr) {
if (!UVM_ET_ISHOLE(iter))
size += iter->end - iter->start;
}
if (map->size != size)
printf("map size = 0x%lx, should be 0x%lx\n", map->size, size);
UVM_ASSERT(map, map->size == size, file, line);
vmspace_validate(map);
}
/*
* This function validates the statistics on vmspace.
*/
void
vmspace_validate(struct vm_map *map)
{
struct vmspace *vm;
struct vm_map_entry *iter;
vaddr_t imin, imax;
vaddr_t stack_begin, stack_end; /* Position of stack. */
vsize_t stack, heap; /* Measured sizes. */
if (!(map->flags & VM_MAP_ISVMSPACE))
return;
vm = (struct vmspace *)map;
stack_begin = MIN((vaddr_t)vm->vm_maxsaddr, (vaddr_t)vm->vm_minsaddr);
stack_end = MAX((vaddr_t)vm->vm_maxsaddr, (vaddr_t)vm->vm_minsaddr);
stack = heap = 0;
RBT_FOREACH(iter, uvm_map_addr, &map->addr) {
imin = imax = iter->start;
if (UVM_ET_ISHOLE(iter) || iter->object.uvm_obj != NULL ||
iter->prot != PROT_NONE)
continue;
/*
* Update stack, heap.
* Keep in mind that (theoretically) the entries of
* userspace and stack may be joined.
*/
while (imin != iter->end) {
/*
* Set imax to the first boundary crossed between
* imin and stack addresses.
*/
imax = iter->end;
if (imin < stack_begin && imax > stack_begin)
imax = stack_begin;
else if (imin < stack_end && imax > stack_end)
imax = stack_end;
if (imin >= stack_begin && imin < stack_end)
stack += imax - imin;
else
heap += imax - imin;
imin = imax;
}
}
heap >>= PAGE_SHIFT;
if (heap != vm->vm_dused) {
printf("vmspace stack range: 0x%lx-0x%lx\n",
stack_begin, stack_end);
panic("vmspace_validate: vmspace.vm_dused invalid, "
"expected %ld pgs, got %ld pgs in map %p",
heap, vm->vm_dused,
map);
}
}
#endif /* VMMAP_DEBUG */
/*
* uvm_map_init: init mapping system at boot time. note that we allocate
* and init the static pool of structs vm_map_entry for the kernel here.
*/
void
uvm_map_init(void)
{
static struct vm_map_entry kernel_map_entry[MAX_KMAPENT];
int lcv;
/* now set up static pool of kernel map entries ... */
mtx_init(&uvm_kmapent_mtx, IPL_VM);
SLIST_INIT(&uvm.kentry_free);
for (lcv = 0 ; lcv < MAX_KMAPENT ; lcv++) {
SLIST_INSERT_HEAD(&uvm.kentry_free,
&kernel_map_entry[lcv], daddrs.addr_kentry);
}
/* initialize the map-related pools. */
pool_init(&uvm_vmspace_pool, sizeof(struct vmspace), 0,
IPL_NONE, PR_WAITOK, "vmsppl", NULL);
pool_init(&uvm_map_entry_pool, sizeof(struct vm_map_entry), 0,
IPL_VM, PR_WAITOK, "vmmpepl", NULL);
pool_init(&uvm_map_entry_kmem_pool, sizeof(struct vm_map_entry), 0,
IPL_VM, 0, "vmmpekpl", NULL);
pool_sethiwat(&uvm_map_entry_pool, 8192);
uvm_addr_init();
}
#if defined(DDB)
/*
* DDB hooks
*/
/*
* uvm_map_printit: actually prints the map
*/
void
uvm_map_printit(struct vm_map *map, boolean_t full,
int (*pr)(const char *, ...))
{
struct vmspace *vm;
struct vm_map_entry *entry;
struct uvm_addr_state *free;
int in_free, i;
char buf[8];
(*pr)("MAP %p: [0x%lx->0x%lx]\n", map, map->min_offset,map->max_offset);
(*pr)("\tbrk() allocate range: 0x%lx-0x%lx\n",
map->b_start, map->b_end);
(*pr)("\tstack allocate range: 0x%lx-0x%lx\n",
map->s_start, map->s_end);
(*pr)("\tsz=%u, ref=%d, version=%u, flags=0x%x\n",
map->size, map->ref_count, map->timestamp,
map->flags);
(*pr)("\tpmap=%p(resident=%d)\n", map->pmap,
pmap_resident_count(map->pmap));
/* struct vmspace handling. */
if (map->flags & VM_MAP_ISVMSPACE) {
vm = (struct vmspace *)map;
(*pr)("\tvm_refcnt=%d vm_shm=%p vm_rssize=%u vm_swrss=%u\n",
vm->vm_refcnt, vm->vm_shm, vm->vm_rssize, vm->vm_swrss);
(*pr)("\tvm_tsize=%u vm_dsize=%u\n",
vm->vm_tsize, vm->vm_dsize);
(*pr)("\tvm_taddr=%p vm_daddr=%p\n",
vm->vm_taddr, vm->vm_daddr);
(*pr)("\tvm_maxsaddr=%p vm_minsaddr=%p\n",
vm->vm_maxsaddr, vm->vm_minsaddr);
}
if (!full)
goto print_uaddr;
RBT_FOREACH(entry, uvm_map_addr, &map->addr) {
(*pr)(" - %p: 0x%lx->0x%lx: obj=%p/0x%llx, amap=%p/%d\n",
entry, entry->start, entry->end, entry->object.uvm_obj,
(long long)entry->offset, entry->aref.ar_amap,
entry->aref.ar_pageoff);
(*pr)("\tsubmap=%c, cow=%c, nc=%c, stack=%c, "
"syscall=%c, prot(max)=%d/%d, inh=%d, "
"wc=%d, adv=%d\n",
(entry->etype & UVM_ET_SUBMAP) ? 'T' : 'F',
(entry->etype & UVM_ET_COPYONWRITE) ? 'T' : 'F',
(entry->etype & UVM_ET_NEEDSCOPY) ? 'T' : 'F',
(entry->etype & UVM_ET_STACK) ? 'T' : 'F',
(entry->etype & UVM_ET_SYSCALL) ? 'T' : 'F',
entry->protection, entry->max_protection,
entry->inheritance, entry->wired_count, entry->advice);
free = uvm_map_uaddr_e(map, entry);
in_free = (free != NULL);
(*pr)("\thole=%c, free=%c, guard=0x%lx, "
"free=0x%lx-0x%lx\n",
(entry->etype & UVM_ET_HOLE) ? 'T' : 'F',
in_free ? 'T' : 'F',
entry->guard,
VMMAP_FREE_START(entry), VMMAP_FREE_END(entry));
(*pr)("\tfspace_augment=%lu\n", entry->fspace_augment);
(*pr)("\tfreemapped=%c, uaddr=%p\n",
(entry->etype & UVM_ET_FREEMAPPED) ? 'T' : 'F', free);
if (free) {
(*pr)("\t\t(0x%lx-0x%lx %s)\n",
free->uaddr_minaddr, free->uaddr_maxaddr,
free->uaddr_functions->uaddr_name);
}
}
print_uaddr:
uvm_addr_print(map->uaddr_exe, "exe", full, pr);
for (i = 0; i < nitems(map->uaddr_any); i++) {
snprintf(&buf[0], sizeof(buf), "any[%d]", i);
uvm_addr_print(map->uaddr_any[i], &buf[0], full, pr);
}
uvm_addr_print(map->uaddr_brk_stack, "brk/stack", full, pr);
}
/*
* uvm_object_printit: actually prints the object
*/
void
uvm_object_printit(struct uvm_object *uobj, boolean_t full,
int (*pr)(const char *, ...))
{
struct vm_page *pg;
int cnt = 0;
(*pr)("OBJECT %p: pgops=%p, npages=%d, ",
uobj, uobj->pgops, uobj->uo_npages);
if (UVM_OBJ_IS_KERN_OBJECT(uobj))
(*pr)("refs=<SYSTEM>\n");
else
(*pr)("refs=%d\n", uobj->uo_refs);
if (!full) {
return;
}
(*pr)(" PAGES <pg,offset>:\n ");
RBT_FOREACH(pg, uvm_objtree, &uobj->memt) {
(*pr)("<%p,0x%llx> ", pg, (long long)pg->offset);
if ((cnt % 3) == 2) {
(*pr)("\n ");
}
cnt++;
}
if ((cnt % 3) != 2) {
(*pr)("\n");
}
}
/*
* uvm_page_printit: actually print the page
*/
static const char page_flagbits[] =
"\20\1BUSY\2WANTED\3TABLED\4CLEAN\5CLEANCHK\6RELEASED\7FAKE\10RDONLY"
"\11ZERO\12DEV\15PAGER1\21FREE\22INACTIVE\23ACTIVE\25ANON\26AOBJ"
"\27ENCRYPT\31PMAP0\32PMAP1\33PMAP2\34PMAP3\35PMAP4\36PMAP5";
void
uvm_page_printit(struct vm_page *pg, boolean_t full,
int (*pr)(const char *, ...))
{
struct vm_page *tpg;
struct uvm_object *uobj;
struct pglist *pgl;
(*pr)("PAGE %p:\n", pg);
(*pr)(" flags=%b, vers=%d, wire_count=%d, pa=0x%llx\n",
pg->pg_flags, page_flagbits, pg->pg_version, pg->wire_count,
(long long)pg->phys_addr);
(*pr)(" uobject=%p, uanon=%p, offset=0x%llx\n",
pg->uobject, pg->uanon, (long long)pg->offset);
#if defined(UVM_PAGE_TRKOWN)
if (pg->pg_flags & PG_BUSY)
(*pr)(" owning thread = %d, tag=%s",
pg->owner, pg->owner_tag);
else
(*pr)(" page not busy, no owner");
#else
(*pr)(" [page ownership tracking disabled]");
#endif
(*pr)("\tvm_page_md %p\n", &pg->mdpage);
if (!full)
return;
/* cross-verify object/anon */
if ((pg->pg_flags & PQ_FREE) == 0) {
if (pg->pg_flags & PQ_ANON) {
if (pg->uanon == NULL || pg->uanon->an_page != pg)
(*pr)(" >>> ANON DOES NOT POINT HERE <<< (%p)\n",
(pg->uanon) ? pg->uanon->an_page : NULL);
else
(*pr)(" anon backpointer is OK\n");
} else {
uobj = pg->uobject;
if (uobj) {
(*pr)(" checking object list\n");
RBT_FOREACH(tpg, uvm_objtree, &uobj->memt) {
if (tpg == pg) {
break;
}
}
if (tpg)
(*pr)(" page found on object list\n");
else
(*pr)(" >>> PAGE NOT FOUND "
"ON OBJECT LIST! <<<\n");
}
}
}
/* cross-verify page queue */
if (pg->pg_flags & PQ_FREE) {
if (uvm_pmr_isfree(pg))
(*pr)(" page found in uvm_pmemrange\n");
else
(*pr)(" >>> page not found in uvm_pmemrange <<<\n");
pgl = NULL;
} else if (pg->pg_flags & PQ_INACTIVE) {
pgl = &uvm.page_inactive;
} else if (pg->pg_flags & PQ_ACTIVE) {
pgl = &uvm.page_active;
} else {
pgl = NULL;
}
if (pgl) {
(*pr)(" checking pageq list\n");
TAILQ_FOREACH(tpg, pgl, pageq) {
if (tpg == pg) {
break;
}
}
if (tpg)
(*pr)(" page found on pageq list\n");
else
(*pr)(" >>> PAGE NOT FOUND ON PAGEQ LIST! <<<\n");
}
}
#endif
/*
* uvm_map_protect: change map protection
*
* => set_max means set max_protection.
* => map must be unlocked.
*/
int
uvm_map_protect(struct vm_map *map, vaddr_t start, vaddr_t end,
vm_prot_t new_prot, boolean_t set_max)
{
struct vm_map_entry *first, *iter;
vm_prot_t old_prot;
vm_prot_t mask;
vsize_t dused;
int error;
if (start > end)
return EINVAL;
start = MAX(start, map->min_offset);
end = MIN(end, map->max_offset);
if (start >= end)
return 0;
dused = 0;
error = 0;
vm_map_lock(map);
/*
* Set up first and last.
* - first will contain first entry at or after start.
*/
first = uvm_map_entrybyaddr(&map->addr, start);
KDASSERT(first != NULL);
if (first->end <= start)
first = RBT_NEXT(uvm_map_addr, first);
/* First, check for protection violations. */
for (iter = first; iter != NULL && iter->start < end;
iter = RBT_NEXT(uvm_map_addr, iter)) {
/* Treat memory holes as free space. */
if (iter->start == iter->end || UVM_ET_ISHOLE(iter))
continue;
old_prot = iter->protection;
if (old_prot == PROT_NONE && new_prot != old_prot) {
dused += uvmspace_dused(
map, MAX(start, iter->start), MIN(end, iter->end));
}
if (UVM_ET_ISSUBMAP(iter)) {
error = EINVAL;
goto out;
}
if ((new_prot & iter->max_protection) != new_prot) {
error = EACCES;
goto out;
}
if (map == kernel_map &&
(new_prot & (PROT_WRITE | PROT_EXEC)) == (PROT_WRITE | PROT_EXEC))
panic("uvm_map_protect: kernel map W^X violation requested");
}
/* Check limits. */
if (dused > 0 && (map->flags & VM_MAP_ISVMSPACE)) {
vsize_t limit = lim_cur(RLIMIT_DATA);
dused = ptoa(dused);
if (limit < dused ||
limit - dused < ptoa(((struct vmspace *)map)->vm_dused)) {
error = ENOMEM;
goto out;
}
}
/* Fix protections. */
for (iter = first; iter != NULL && iter->start < end;
iter = RBT_NEXT(uvm_map_addr, iter)) {
/* Treat memory holes as free space. */
if (iter->start == iter->end || UVM_ET_ISHOLE(iter))
continue;
old_prot = iter->protection;
/*
* Skip adapting protection iff old and new protection
* are equal.
*/
if (set_max) {
if (old_prot == (new_prot & old_prot) &&
iter->max_protection == new_prot)
continue;
} else {
if (old_prot == new_prot)
continue;
}
UVM_MAP_CLIP_START(map, iter, start);
UVM_MAP_CLIP_END(map, iter, end);
if (set_max) {
iter->max_protection = new_prot;
iter->protection &= new_prot;
} else
iter->protection = new_prot;
/*
* update physical map if necessary. worry about copy-on-write
* here -- CHECK THIS XXX
*/
if (iter->protection != old_prot) {
mask = UVM_ET_ISCOPYONWRITE(iter) ?
~PROT_WRITE : PROT_MASK;
/* XXX should only wserial++ if no split occurs */
if (iter->protection & PROT_WRITE)
map->wserial++;
if (map->flags & VM_MAP_ISVMSPACE) {
if (old_prot == PROT_NONE) {
((struct vmspace *)map)->vm_dused +=
uvmspace_dused(map, iter->start,
iter->end);
}
if (iter->protection == PROT_NONE) {
((struct vmspace *)map)->vm_dused -=
uvmspace_dused(map, iter->start,
iter->end);
}
}
/* update pmap */
if ((iter->protection & mask) == PROT_NONE &&
VM_MAPENT_ISWIRED(iter)) {
/*
* TODO(ariane) this is stupid. wired_count
* is 0 if not wired, otherwise anything
* larger than 0 (incremented once each time
* wire is called).
* Mostly to be able to undo the damage on
* failure. Not the actually be a wired
* refcounter...
* Originally: iter->wired_count--;
* (don't we have to unwire this in the pmap
* as well?)
*/
iter->wired_count = 0;
}
uvm_map_lock_entry(iter);
pmap_protect(map->pmap, iter->start, iter->end,
iter->protection & mask);
uvm_map_unlock_entry(iter);
}
/*
* If the map is configured to lock any future mappings,
* wire this entry now if the old protection was PROT_NONE
* and the new protection is not PROT_NONE.
*/
if ((map->flags & VM_MAP_WIREFUTURE) != 0 &&
VM_MAPENT_ISWIRED(iter) == 0 &&
old_prot == PROT_NONE &&
new_prot != PROT_NONE) {
if (uvm_map_pageable(map, iter->start, iter->end,
FALSE, UVM_LK_ENTER | UVM_LK_EXIT) != 0) {
/*
* If locking the entry fails, remember the
* error if it's the first one. Note we
* still continue setting the protection in
* the map, but it will return the resource
* storage condition regardless.
*
* XXX Ignore what the actual error is,
* XXX just call it a resource shortage
* XXX so that it doesn't get confused
* XXX what uvm_map_protect() itself would
* XXX normally return.
*/
error = ENOMEM;
}
}
}
pmap_update(map->pmap);
out:
vm_map_unlock(map);
return error;
}
/*
* uvmspace_alloc: allocate a vmspace structure.
*
* - structure includes vm_map and pmap
* - XXX: no locking on this structure
* - refcnt set to 1, rest must be init'd by caller
*/
struct vmspace *
uvmspace_alloc(vaddr_t min, vaddr_t max, boolean_t pageable,
boolean_t remove_holes)
{
struct vmspace *vm;
vm = pool_get(&uvm_vmspace_pool, PR_WAITOK | PR_ZERO);
uvmspace_init(vm, NULL, min, max, pageable, remove_holes);
return (vm);
}
/*
* uvmspace_init: initialize a vmspace structure.
*
* - XXX: no locking on this structure
* - refcnt set to 1, rest must be init'd by caller
*/
void
uvmspace_init(struct vmspace *vm, struct pmap *pmap, vaddr_t min, vaddr_t max,
boolean_t pageable, boolean_t remove_holes)
{
KASSERT(pmap == NULL || pmap == pmap_kernel());
if (pmap)
pmap_reference(pmap);
else
pmap = pmap_create();
uvm_map_setup(&vm->vm_map, pmap, min, max,
(pageable ? VM_MAP_PAGEABLE : 0) | VM_MAP_ISVMSPACE);
vm->vm_refcnt = 1;
if (remove_holes)
pmap_remove_holes(vm);
}
/*
* uvmspace_share: share a vmspace between two processes
*
* - used for vfork
*/
struct vmspace *
uvmspace_share(struct process *pr)
{
struct vmspace *vm = pr->ps_vmspace;
uvmspace_addref(vm);
return vm;
}
/*
* uvmspace_exec: the process wants to exec a new program
*
* - XXX: no locking on vmspace
*/
void
uvmspace_exec(struct proc *p, vaddr_t start, vaddr_t end)
{
struct process *pr = p->p_p;
struct vmspace *nvm, *ovm = pr->ps_vmspace;
struct vm_map *map = &ovm->vm_map;
struct uvm_map_deadq dead_entries;
KASSERT((start & (vaddr_t)PAGE_MASK) == 0);
KASSERT((end & (vaddr_t)PAGE_MASK) == 0 ||
(end & (vaddr_t)PAGE_MASK) == (vaddr_t)PAGE_MASK);
pmap_unuse_final(p); /* before stack addresses go away */
TAILQ_INIT(&dead_entries);
/* see if more than one process is using this vmspace... */
if (ovm->vm_refcnt == 1) {
/*
* If pr is the only process using its vmspace then
* we can safely recycle that vmspace for the program
* that is being exec'd.
*/
#ifdef SYSVSHM
/*
* SYSV SHM semantics require us to kill all segments on an exec
*/
if (ovm->vm_shm)
shmexit(ovm);
#endif
/*
* POSIX 1003.1b -- "lock future mappings" is revoked
* when a process execs another program image.
*/
vm_map_lock(map);
vm_map_modflags(map, 0, VM_MAP_WIREFUTURE|VM_MAP_SYSCALL_ONCE);
/*
* now unmap the old program
*
* Instead of attempting to keep the map valid, we simply
* nuke all entries and ask uvm_map_setup to reinitialize
* the map to the new boundaries.
*
* uvm_unmap_remove will actually nuke all entries for us
* (as in, not replace them with free-memory entries).
*/
uvm_unmap_remove(map, map->min_offset, map->max_offset,
&dead_entries, TRUE, FALSE);
KDASSERT(RBT_EMPTY(uvm_map_addr, &map->addr));
/* Nuke statistics and boundaries. */
memset(&ovm->vm_startcopy, 0,
(caddr_t) (ovm + 1) - (caddr_t) &ovm->vm_startcopy);
if (end & (vaddr_t)PAGE_MASK) {
end += 1;
if (end == 0) /* overflow */
end -= PAGE_SIZE;
}
/* Setup new boundaries and populate map with entries. */
map->min_offset = start;
map->max_offset = end;
uvm_map_setup_entries(map);
vm_map_unlock(map);
/* but keep MMU holes unavailable */
pmap_remove_holes(ovm);
} else {
/*
* pr's vmspace is being shared, so we can't reuse
* it for pr since it is still being used for others.
* allocate a new vmspace for pr
*/
nvm = uvmspace_alloc(start, end,
(map->flags & VM_MAP_PAGEABLE) ? TRUE : FALSE, TRUE);
/* install new vmspace and drop our ref to the old one. */
pmap_deactivate(p);
p->p_vmspace = pr->ps_vmspace = nvm;
pmap_activate(p);
uvmspace_free(ovm);
}
/* Release dead entries */
uvm_unmap_detach(&dead_entries, 0);
}
/*
* uvmspace_addref: add a reference to a vmspace.
*/
void
uvmspace_addref(struct vmspace *vm)
{
KERNEL_ASSERT_LOCKED();
KASSERT(vm->vm_refcnt > 0);
vm->vm_refcnt++;
}
/*
* uvmspace_free: free a vmspace data structure
*/
void
uvmspace_free(struct vmspace *vm)
{
KERNEL_ASSERT_LOCKED();
if (--vm->vm_refcnt == 0) {
/*
* lock the map, to wait out all other references to it. delete
* all of the mappings and pages they hold, then call the pmap
* module to reclaim anything left.
*/
#ifdef SYSVSHM
/* Get rid of any SYSV shared memory segments. */
if (vm->vm_shm != NULL)
shmexit(vm);
#endif
uvm_map_teardown(&vm->vm_map);
pool_put(&uvm_vmspace_pool, vm);
}
}
/*
* uvm_share: Map the address range [srcaddr, srcaddr + sz) in
* srcmap to the address range [dstaddr, dstaddr + sz) in
* dstmap.
*
* The whole address range in srcmap must be backed by an object
* (no holes).
*
* If successful, the address ranges share memory and the destination
* address range uses the protection flags in prot.
*
* This routine assumes that sz is a multiple of PAGE_SIZE and
* that dstaddr and srcaddr are page-aligned.
*/
int
uvm_share(struct vm_map *dstmap, vaddr_t dstaddr, vm_prot_t prot,
struct vm_map *srcmap, vaddr_t srcaddr, vsize_t sz)
{
int ret = 0;
vaddr_t unmap_end;
vaddr_t dstva;
vsize_t s_off, len, n = sz, remain;
struct vm_map_entry *first = NULL, *last = NULL;
struct vm_map_entry *src_entry, *psrc_entry = NULL;
struct uvm_map_deadq dead;
if (srcaddr >= srcmap->max_offset || sz > srcmap->max_offset - srcaddr)
return EINVAL;
TAILQ_INIT(&dead);
vm_map_lock(dstmap);
vm_map_lock_read(srcmap);
if (!uvm_map_isavail(dstmap, NULL, &first, &last, dstaddr, sz)) {
ret = ENOMEM;
goto exit_unlock;
}
if (!uvm_map_lookup_entry(srcmap, srcaddr, &src_entry)) {
ret = EINVAL;
goto exit_unlock;
}
dstva = dstaddr;
unmap_end = dstaddr;
for (; src_entry != NULL;
psrc_entry = src_entry,
src_entry = RBT_NEXT(uvm_map_addr, src_entry)) {
/* hole in address space, bail out */
if (psrc_entry != NULL && psrc_entry->end != src_entry->start)
break;
if (src_entry->start >= srcaddr + sz)
break;
if (UVM_ET_ISSUBMAP(src_entry))
panic("uvm_share: encountered a submap (illegal)");
if (!UVM_ET_ISCOPYONWRITE(src_entry) &&
UVM_ET_ISNEEDSCOPY(src_entry))
panic("uvm_share: non-copy_on_write map entries "
"marked needs_copy (illegal)");
/*
* srcaddr > map entry start? means we are in the middle of a
* map, so we calculate the offset to use in the source map.
*/
if (srcaddr > src_entry->start)
s_off = srcaddr - src_entry->start;
else if (srcaddr == src_entry->start)
s_off = 0;
else
panic("uvm_share: map entry start > srcaddr");
remain = src_entry->end - src_entry->start - s_off;
/* Determine how many bytes to share in this pass */
if (n < remain)
len = n;
else
len = remain;
if (uvm_mapent_share(dstmap, dstva, len, s_off, prot, prot,
srcmap, src_entry, &dead) == NULL)
break;
n -= len;
dstva += len;
srcaddr += len;
unmap_end = dstva + len;
if (n == 0)
goto exit_unlock;
}
ret = EINVAL;
uvm_unmap_remove(dstmap, dstaddr, unmap_end, &dead, FALSE, TRUE);
exit_unlock:
vm_map_unlock_read(srcmap);
vm_map_unlock(dstmap);
uvm_unmap_detach(&dead, 0);
return ret;
}
/*
* Clone map entry into other map.
*
* Mapping will be placed at dstaddr, for the same length.
* Space must be available.
* Reference counters are incremented.
*/
struct vm_map_entry *
uvm_mapent_clone(struct vm_map *dstmap, vaddr_t dstaddr, vsize_t dstlen,
vsize_t off, vm_prot_t prot, vm_prot_t maxprot,
struct vm_map_entry *old_entry, struct uvm_map_deadq *dead,
int mapent_flags, int amap_share_flags)
{
struct vm_map_entry *new_entry, *first, *last;
KDASSERT(!UVM_ET_ISSUBMAP(old_entry));
/* Create new entry (linked in on creation). Fill in first, last. */
first = last = NULL;
if (!uvm_map_isavail(dstmap, NULL, &first, &last, dstaddr, dstlen)) {
panic("uvm_mapent_clone: no space in map for "
"entry in empty map");
}
new_entry = uvm_map_mkentry(dstmap, first, last,
dstaddr, dstlen, mapent_flags, dead, NULL);
if (new_entry == NULL)
return NULL;
/* old_entry -> new_entry */
new_entry->object = old_entry->object;
new_entry->offset = old_entry->offset;
new_entry->aref = old_entry->aref;
new_entry->etype |= old_entry->etype & ~UVM_ET_FREEMAPPED;
new_entry->protection = prot;
new_entry->max_protection = maxprot;
new_entry->inheritance = old_entry->inheritance;
new_entry->advice = old_entry->advice;
/* gain reference to object backing the map (can't be a submap). */
if (new_entry->aref.ar_amap) {
new_entry->aref.ar_pageoff += off >> PAGE_SHIFT;
amap_ref(new_entry->aref.ar_amap, new_entry->aref.ar_pageoff,
(new_entry->end - new_entry->start) >> PAGE_SHIFT,
amap_share_flags);
}
if (UVM_ET_ISOBJ(new_entry) &&
new_entry->object.uvm_obj->pgops->pgo_reference) {
new_entry->offset += off;
new_entry->object.uvm_obj->pgops->pgo_reference
(new_entry->object.uvm_obj);
}
return new_entry;
}
struct vm_map_entry *
uvm_mapent_share(struct vm_map *dstmap, vaddr_t dstaddr, vsize_t dstlen,
vsize_t off, vm_prot_t prot, vm_prot_t maxprot, struct vm_map *old_map,
struct vm_map_entry *old_entry, struct uvm_map_deadq *dead)
{
/*
* If old_entry refers to a copy-on-write region that has not yet been
* written to (needs_copy flag is set), then we need to allocate a new
* amap for old_entry.
*
* If we do not do this, and the process owning old_entry does a copy-on
* write later, old_entry and new_entry will refer to different memory
* regions, and the memory between the processes is no longer shared.
*
* [in other words, we need to clear needs_copy]
*/
if (UVM_ET_ISNEEDSCOPY(old_entry)) {
/* get our own amap, clears needs_copy */
amap_copy(old_map, old_entry, M_WAITOK, FALSE, 0, 0);
/* XXXCDC: WAITOK??? */
}
return uvm_mapent_clone(dstmap, dstaddr, dstlen, off,
prot, maxprot, old_entry, dead, 0, AMAP_SHARED);
}
/*
* share the mapping: this means we want the old and
* new entries to share amaps and backing objects.
*/
struct vm_map_entry *
uvm_mapent_forkshared(struct vmspace *new_vm, struct vm_map *new_map,
struct vm_map *old_map,
struct vm_map_entry *old_entry, struct uvm_map_deadq *dead)
{
struct vm_map_entry *new_entry;
new_entry = uvm_mapent_share(new_map, old_entry->start,
old_entry->end - old_entry->start, 0, old_entry->protection,
old_entry->max_protection, old_map, old_entry, dead);
/*
* pmap_copy the mappings: this routine is optional
* but if it is there it will reduce the number of
* page faults in the new proc.
*/
if (!UVM_ET_ISHOLE(new_entry))
pmap_copy(new_map->pmap, old_map->pmap, new_entry->start,
(new_entry->end - new_entry->start), new_entry->start);
return (new_entry);
}
/*
* copy-on-write the mapping (using mmap's
* MAP_PRIVATE semantics)
*
* allocate new_entry, adjust reference counts.
* (note that new references are read-only).
*/
struct vm_map_entry *
uvm_mapent_forkcopy(struct vmspace *new_vm, struct vm_map *new_map,
struct vm_map *old_map,
struct vm_map_entry *old_entry, struct uvm_map_deadq *dead)
{
struct vm_map_entry *new_entry;
boolean_t protect_child;
new_entry = uvm_mapent_clone(new_map, old_entry->start,
old_entry->end - old_entry->start, 0, old_entry->protection,
old_entry->max_protection, old_entry, dead, 0, 0);
new_entry->etype |=
(UVM_ET_COPYONWRITE|UVM_ET_NEEDSCOPY);
/*
* the new entry will need an amap. it will either
* need to be copied from the old entry or created
* from scratch (if the old entry does not have an
* amap). can we defer this process until later
* (by setting "needs_copy") or do we need to copy
* the amap now?
*
* we must copy the amap now if any of the following
* conditions hold:
* 1. the old entry has an amap and that amap is
* being shared. this means that the old (parent)
* process is sharing the amap with another
* process. if we do not clear needs_copy here
* we will end up in a situation where both the
* parent and child process are referring to the
* same amap with "needs_copy" set. if the
* parent write-faults, the fault routine will
* clear "needs_copy" in the parent by allocating
* a new amap. this is wrong because the
* parent is supposed to be sharing the old amap
* and the new amap will break that.
*
* 2. if the old entry has an amap and a non-zero
* wire count then we are going to have to call
* amap_cow_now to avoid page faults in the
* parent process. since amap_cow_now requires
* "needs_copy" to be clear we might as well
* clear it here as well.
*
*/
if (old_entry->aref.ar_amap != NULL &&
((amap_flags(old_entry->aref.ar_amap) &
AMAP_SHARED) != 0 ||
VM_MAPENT_ISWIRED(old_entry))) {
amap_copy(new_map, new_entry, M_WAITOK, FALSE,
0, 0);
/* XXXCDC: M_WAITOK ... ok? */
}
/*
* if the parent's entry is wired down, then the
* parent process does not want page faults on
* access to that memory. this means that we
* cannot do copy-on-write because we can't write
* protect the old entry. in this case we
* resolve all copy-on-write faults now, using
* amap_cow_now. note that we have already
* allocated any needed amap (above).
*/
if (VM_MAPENT_ISWIRED(old_entry)) {
/*
* resolve all copy-on-write faults now
* (note that there is nothing to do if
* the old mapping does not have an amap).
* XXX: is it worthwhile to bother with
* pmap_copy in this case?
*/
if (old_entry->aref.ar_amap)
amap_cow_now(new_map, new_entry);
} else {
if (old_entry->aref.ar_amap) {
/*
* setup mappings to trigger copy-on-write faults
* we must write-protect the parent if it has
* an amap and it is not already "needs_copy"...
* if it is already "needs_copy" then the parent
* has already been write-protected by a previous
* fork operation.
*
* if we do not write-protect the parent, then
* we must be sure to write-protect the child
* after the pmap_copy() operation.
*
* XXX: pmap_copy should have some way of telling
* us that it didn't do anything so we can avoid
* calling pmap_protect needlessly.
*/
if (!UVM_ET_ISNEEDSCOPY(old_entry)) {
if (old_entry->max_protection & PROT_WRITE) {
uvm_map_lock_entry(old_entry);
pmap_protect(old_map->pmap,
old_entry->start,
old_entry->end,
old_entry->protection &
~PROT_WRITE);
uvm_map_unlock_entry(old_entry);
pmap_update(old_map->pmap);
}
old_entry->etype |= UVM_ET_NEEDSCOPY;
}
/* parent must now be write-protected */
protect_child = FALSE;
} else {
/*
* we only need to protect the child if the
* parent has write access.
*/
if (old_entry->max_protection & PROT_WRITE)
protect_child = TRUE;
else
protect_child = FALSE;
}
/*
* copy the mappings
* XXX: need a way to tell if this does anything
*/
if (!UVM_ET_ISHOLE(new_entry))
pmap_copy(new_map->pmap, old_map->pmap,
new_entry->start,
(old_entry->end - old_entry->start),
old_entry->start);
/* protect the child's mappings if necessary */
if (protect_child) {
pmap_protect(new_map->pmap, new_entry->start,
new_entry->end,
new_entry->protection &
~PROT_WRITE);
}
}
return (new_entry);
}
/*
* zero the mapping: the new entry will be zero initialized
*/
struct vm_map_entry *
uvm_mapent_forkzero(struct vmspace *new_vm, struct vm_map *new_map,
struct vm_map *old_map,
struct vm_map_entry *old_entry, struct uvm_map_deadq *dead)
{
struct vm_map_entry *new_entry;
new_entry = uvm_mapent_clone(new_map, old_entry->start,
old_entry->end - old_entry->start, 0, old_entry->protection,
old_entry->max_protection, old_entry, dead, 0, 0);
new_entry->etype |=
(UVM_ET_COPYONWRITE|UVM_ET_NEEDSCOPY);
if (new_entry->aref.ar_amap) {
amap_unref(new_entry->aref.ar_amap, new_entry->aref.ar_pageoff,
atop(new_entry->end - new_entry->start), 0);
new_entry->aref.ar_amap = NULL;
new_entry->aref.ar_pageoff = 0;
}
if (UVM_ET_ISOBJ(new_entry)) {
if (new_entry->object.uvm_obj->pgops->pgo_detach)
new_entry->object.uvm_obj->pgops->pgo_detach(
new_entry->object.uvm_obj);
new_entry->object.uvm_obj = NULL;
new_entry->etype &= ~UVM_ET_OBJ;
}
return (new_entry);
}
/*
* uvmspace_fork: fork a process' main map
*
* => create a new vmspace for child process from parent.
* => parent's map must not be locked.
*/
struct vmspace *
uvmspace_fork(struct process *pr)
{
struct vmspace *vm1 = pr->ps_vmspace;
struct vmspace *vm2;
struct vm_map *old_map = &vm1->vm_map;
struct vm_map *new_map;
struct vm_map_entry *old_entry, *new_entry;
struct uvm_map_deadq dead;
vm_map_lock(old_map);
vm2 = uvmspace_alloc(old_map->min_offset, old_map->max_offset,
(old_map->flags & VM_MAP_PAGEABLE) ? TRUE : FALSE, FALSE);
memcpy(&vm2->vm_startcopy, &vm1->vm_startcopy,
(caddr_t) (vm1 + 1) - (caddr_t) &vm1->vm_startcopy);
vm2->vm_dused = 0; /* Statistic managed by us. */
new_map = &vm2->vm_map;
vm_map_lock(new_map);
/* go entry-by-entry */
TAILQ_INIT(&dead);
RBT_FOREACH(old_entry, uvm_map_addr, &old_map->addr) {
if (old_entry->start == old_entry->end)
continue;
/* first, some sanity checks on the old entry */
if (UVM_ET_ISSUBMAP(old_entry)) {
panic("fork: encountered a submap during fork "
"(illegal)");
}
if (!UVM_ET_ISCOPYONWRITE(old_entry) &&
UVM_ET_ISNEEDSCOPY(old_entry)) {
panic("fork: non-copy_on_write map entry marked "
"needs_copy (illegal)");
}
/* Apply inheritance. */
switch (old_entry->inheritance) {
case MAP_INHERIT_SHARE:
new_entry = uvm_mapent_forkshared(vm2, new_map,
old_map, old_entry, &dead);
break;
case MAP_INHERIT_COPY:
new_entry = uvm_mapent_forkcopy(vm2, new_map,
old_map, old_entry, &dead);
break;
case MAP_INHERIT_ZERO:
new_entry = uvm_mapent_forkzero(vm2, new_map,
old_map, old_entry, &dead);
break;
default:
continue;
}
/* Update process statistics. */
if (!UVM_ET_ISHOLE(new_entry))
new_map->size += new_entry->end - new_entry->start;
if (!UVM_ET_ISOBJ(new_entry) && !UVM_ET_ISHOLE(new_entry) &&
new_entry->protection != PROT_NONE) {
vm2->vm_dused += uvmspace_dused(
new_map, new_entry->start, new_entry->end);
}
}
vm_map_unlock(old_map);
vm_map_unlock(new_map);
/*
* This can actually happen, if multiple entries described a
* space in which an entry was inherited.
*/
uvm_unmap_detach(&dead, 0);
#ifdef SYSVSHM
if (vm1->vm_shm)
shmfork(vm1, vm2);
#endif
return vm2;
}
/*
* uvm_map_hint: return the beginning of the best area suitable for
* creating a new mapping with "prot" protection.
*/
vaddr_t
uvm_map_hint(struct vmspace *vm, vm_prot_t prot, vaddr_t minaddr,
vaddr_t maxaddr)
{
vaddr_t addr;
vaddr_t spacing;
#ifdef __i386__
/*
* If executable skip first two pages, otherwise start
* after data + heap region.
*/
if ((prot & PROT_EXEC) != 0 &&
(vaddr_t)vm->vm_daddr >= I386_MAX_EXE_ADDR) {
addr = (PAGE_SIZE*2) +
(arc4random() & (I386_MAX_EXE_ADDR / 2 - 1));
return (round_page(addr));
}
#endif
#if defined (__LP64__)
spacing = MIN(4UL * 1024 * 1024 * 1024, MAXDSIZ) - 1;
#else
spacing = MIN(1 * 1024 * 1024 * 1024, MAXDSIZ) - 1;
#endif
/*
* Start malloc/mmap after the brk.
*/
addr = (vaddr_t)vm->vm_daddr + BRKSIZ;
addr = MAX(addr, minaddr);
if (addr < maxaddr) {
while (spacing > maxaddr - addr)
spacing >>= 1;
}
addr += arc4random() & spacing;
return (round_page(addr));
}
/*
* uvm_map_submap: punch down part of a map into a submap
*
* => only the kernel_map is allowed to be submapped
* => the purpose of submapping is to break up the locking granularity
* of a larger map
* => the range specified must have been mapped previously with a uvm_map()
* call [with uobj==NULL] to create a blank map entry in the main map.
* [And it had better still be blank!]
* => maps which contain submaps should never be copied or forked.
* => to remove a submap, use uvm_unmap() on the main map
* and then uvm_map_deallocate() the submap.
* => main map must be unlocked.
* => submap must have been init'd and have a zero reference count.
* [need not be locked as we don't actually reference it]
*/
int
uvm_map_submap(struct vm_map *map, vaddr_t start, vaddr_t end,
struct vm_map *submap)
{
struct vm_map_entry *entry;
int result;
if (start > map->max_offset || end > map->max_offset ||
start < map->min_offset || end < map->min_offset)
return EINVAL;
vm_map_lock(map);
if (uvm_map_lookup_entry(map, start, &entry)) {
UVM_MAP_CLIP_START(map, entry, start);
UVM_MAP_CLIP_END(map, entry, end);
} else
entry = NULL;
if (entry != NULL &&
entry->start == start && entry->end == end &&
entry->object.uvm_obj == NULL && entry->aref.ar_amap == NULL &&
!UVM_ET_ISCOPYONWRITE(entry) && !UVM_ET_ISNEEDSCOPY(entry)) {
entry->etype |= UVM_ET_SUBMAP;
entry->object.sub_map = submap;
entry->offset = 0;
uvm_map_reference(submap);
result = 0;
} else
result = EINVAL;
vm_map_unlock(map);
return result;
}
/*
* uvm_map_checkprot: check protection in map
*
* => must allow specific protection in a fully allocated region.
* => map mut be read or write locked by caller.
*/
boolean_t
uvm_map_checkprot(struct vm_map *map, vaddr_t start, vaddr_t end,
vm_prot_t protection)
{
struct vm_map_entry *entry;
if (start < map->min_offset || end > map->max_offset || start > end)
return FALSE;
if (start == end)
return TRUE;
/*
* Iterate entries.
*/
for (entry = uvm_map_entrybyaddr(&map->addr, start);
entry != NULL && entry->start < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
/* Fail if a hole is found. */
if (UVM_ET_ISHOLE(entry) ||
(entry->end < end && entry->end != VMMAP_FREE_END(entry)))
return FALSE;
/* Check protection. */
if ((entry->protection & protection) != protection)
return FALSE;
}
return TRUE;
}
/*
* uvm_map_create: create map
*/
vm_map_t
uvm_map_create(pmap_t pmap, vaddr_t min, vaddr_t max, int flags)
{
vm_map_t map;
map = malloc(sizeof *map, M_VMMAP, M_WAITOK);
uvm_map_setup(map, pmap, min, max, flags);
return (map);
}
/*
* uvm_map_deallocate: drop reference to a map
*
* => caller must not lock map
* => we will zap map if ref count goes to zero
*/
void
uvm_map_deallocate(vm_map_t map)
{
int c;
struct uvm_map_deadq dead;
c = atomic_dec_int_nv(&map->ref_count);
if (c > 0) {
return;
}
/*
* all references gone. unmap and free.
*
* No lock required: we are only one to access this map.
*/
TAILQ_INIT(&dead);
uvm_tree_sanity(map, __FILE__, __LINE__);
uvm_unmap_remove(map, map->min_offset, map->max_offset, &dead,
TRUE, FALSE);
pmap_destroy(map->pmap);
KASSERT(RBT_EMPTY(uvm_map_addr, &map->addr));
free(map, M_VMMAP, sizeof *map);
uvm_unmap_detach(&dead, 0);
}
/*
* uvm_map_inherit: set inheritance code for range of addrs in map.
*
* => map must be unlocked
* => note that the inherit code is used during a "fork". see fork
* code for details.
*/
int
uvm_map_inherit(struct vm_map *map, vaddr_t start, vaddr_t end,
vm_inherit_t new_inheritance)
{
struct vm_map_entry *entry;
switch (new_inheritance) {
case MAP_INHERIT_NONE:
case MAP_INHERIT_COPY:
case MAP_INHERIT_SHARE:
case MAP_INHERIT_ZERO:
break;
default:
return (EINVAL);
}
if (start > end)
return EINVAL;
start = MAX(start, map->min_offset);
end = MIN(end, map->max_offset);
if (start >= end)
return 0;
vm_map_lock(map);
entry = uvm_map_entrybyaddr(&map->addr, start);
if (entry->end > start)
UVM_MAP_CLIP_START(map, entry, start);
else
entry = RBT_NEXT(uvm_map_addr, entry);
while (entry != NULL && entry->start < end) {
UVM_MAP_CLIP_END(map, entry, end);
entry->inheritance = new_inheritance;
entry = RBT_NEXT(uvm_map_addr, entry);
}
vm_map_unlock(map);
return (0);
}
/*
* uvm_map_syscall: permit system calls for range of addrs in map.
*
* => map must be unlocked
*/
int
uvm_map_syscall(struct vm_map *map, vaddr_t start, vaddr_t end)
{
struct vm_map_entry *entry;
if (start > end)
return EINVAL;
start = MAX(start, map->min_offset);
end = MIN(end, map->max_offset);
if (start >= end)
return 0;
if (map->flags & VM_MAP_SYSCALL_ONCE) /* only allowed once */
return (EPERM);
vm_map_lock(map);
entry = uvm_map_entrybyaddr(&map->addr, start);
if (entry->end > start)
UVM_MAP_CLIP_START(map, entry, start);
else
entry = RBT_NEXT(uvm_map_addr, entry);
while (entry != NULL && entry->start < end) {
UVM_MAP_CLIP_END(map, entry, end);
entry->etype |= UVM_ET_SYSCALL;
entry = RBT_NEXT(uvm_map_addr, entry);
}
map->wserial++;
map->flags |= VM_MAP_SYSCALL_ONCE;
vm_map_unlock(map);
return (0);
}
/*
* uvm_map_advice: set advice code for range of addrs in map.
*
* => map must be unlocked
*/
int
uvm_map_advice(struct vm_map *map, vaddr_t start, vaddr_t end, int new_advice)
{
struct vm_map_entry *entry;
switch (new_advice) {
case MADV_NORMAL:
case MADV_RANDOM:
case MADV_SEQUENTIAL:
break;
default:
return (EINVAL);
}
if (start > end)
return EINVAL;
start = MAX(start, map->min_offset);
end = MIN(end, map->max_offset);
if (start >= end)
return 0;
vm_map_lock(map);
entry = uvm_map_entrybyaddr(&map->addr, start);
if (entry != NULL && entry->end > start)
UVM_MAP_CLIP_START(map, entry, start);
else if (entry!= NULL)
entry = RBT_NEXT(uvm_map_addr, entry);
/*
* XXXJRT: disallow holes?
*/
while (entry != NULL && entry->start < end) {
UVM_MAP_CLIP_END(map, entry, end);
entry->advice = new_advice;
entry = RBT_NEXT(uvm_map_addr, entry);
}
vm_map_unlock(map);
return (0);
}
/*
* uvm_map_extract: extract a mapping from a map and put it somewhere
* in the kernel_map, setting protection to max_prot.
*
* => map should be unlocked (we will write lock it and kernel_map)
* => returns 0 on success, error code otherwise
* => start must be page aligned
* => len must be page sized
* => flags:
* UVM_EXTRACT_FIXPROT: set prot to maxprot as we go
* Mappings are QREF's.
*/
int
uvm_map_extract(struct vm_map *srcmap, vaddr_t start, vsize_t len,
vaddr_t *dstaddrp, int flags)
{
struct uvm_map_deadq dead;
struct vm_map_entry *first, *entry, *newentry, *tmp1, *tmp2;
vaddr_t dstaddr;
vaddr_t end;
vaddr_t cp_start;
vsize_t cp_len, cp_off;
int error;
TAILQ_INIT(&dead);
end = start + len;
/*
* Sanity check on the parameters.
* Also, since the mapping may not contain gaps, error out if the
* mapped area is not in source map.
*/
if ((start & (vaddr_t)PAGE_MASK) != 0 ||
(end & (vaddr_t)PAGE_MASK) != 0 || end < start)
return EINVAL;
if (start < srcmap->min_offset || end > srcmap->max_offset)
return EINVAL;
/* Initialize dead entries. Handle len == 0 case. */
if (len == 0)
return 0;
/* Acquire lock on srcmap. */
vm_map_lock(srcmap);
/* Lock srcmap, lookup first and last entry in <start,len>. */
first = uvm_map_entrybyaddr(&srcmap->addr, start);
/* Check that the range is contiguous. */
for (entry = first; entry != NULL && entry->end < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
if (VMMAP_FREE_END(entry) != entry->end ||
UVM_ET_ISHOLE(entry)) {
error = EINVAL;
goto fail;
}
}
if (entry == NULL || UVM_ET_ISHOLE(entry)) {
error = EINVAL;
goto fail;
}
/*
* Handle need-copy flag.
*/
for (entry = first; entry != NULL && entry->start < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
if (UVM_ET_ISNEEDSCOPY(entry))
amap_copy(srcmap, entry, M_NOWAIT,
UVM_ET_ISSTACK(entry) ? FALSE : TRUE, start, end);
if (UVM_ET_ISNEEDSCOPY(entry)) {
/*
* amap_copy failure
*/
error = ENOMEM;
goto fail;
}
}
/* Lock destination map (kernel_map). */
vm_map_lock(kernel_map);
if (uvm_map_findspace(kernel_map, &tmp1, &tmp2, &dstaddr, len,
MAX(PAGE_SIZE, PMAP_PREFER_ALIGN()), PMAP_PREFER_OFFSET(start),
PROT_NONE, 0) != 0) {
error = ENOMEM;
goto fail2;
}
*dstaddrp = dstaddr;
/*
* We now have srcmap and kernel_map locked.
* dstaddr contains the destination offset in dstmap.
*/
/* step 1: start looping through map entries, performing extraction. */
for (entry = first; entry != NULL && entry->start < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
KDASSERT(!UVM_ET_ISNEEDSCOPY(entry));
if (UVM_ET_ISHOLE(entry))
continue;
/* Calculate uvm_mapent_clone parameters. */
cp_start = entry->start;
if (cp_start < start) {
cp_off = start - cp_start;
cp_start = start;
} else
cp_off = 0;
cp_len = MIN(entry->end, end) - cp_start;
newentry = uvm_mapent_clone(kernel_map,
cp_start - start + dstaddr, cp_len, cp_off,
entry->protection, entry->max_protection,
entry, &dead, flags, AMAP_SHARED | AMAP_REFALL);
if (newentry == NULL) {
error = ENOMEM;
goto fail2_unmap;
}
kernel_map->size += cp_len;
if (flags & UVM_EXTRACT_FIXPROT)
newentry->protection = newentry->max_protection;
/*
* Step 2: perform pmap copy.
* (Doing this in the loop saves one RB traversal.)
*/
pmap_copy(kernel_map->pmap, srcmap->pmap,
cp_start - start + dstaddr, cp_len, cp_start);
}
pmap_update(kernel_map->pmap);
error = 0;
/* Unmap copied entries on failure. */
fail2_unmap:
if (error) {
uvm_unmap_remove(kernel_map, dstaddr, dstaddr + len, &dead,
FALSE, TRUE);
}
/* Release maps, release dead entries. */
fail2:
vm_map_unlock(kernel_map);
fail:
vm_map_unlock(srcmap);
uvm_unmap_detach(&dead, 0);
return error;
}
/*
* uvm_map_clean: clean out a map range
*
* => valid flags:
* if (flags & PGO_CLEANIT): dirty pages are cleaned first
* if (flags & PGO_SYNCIO): dirty pages are written synchronously
* if (flags & PGO_DEACTIVATE): any cached pages are deactivated after clean
* if (flags & PGO_FREE): any cached pages are freed after clean
* => returns an error if any part of the specified range isn't mapped
* => never a need to flush amap layer since the anonymous memory has
* no permanent home, but may deactivate pages there
* => called from sys_msync() and sys_madvise()
* => caller must not write-lock map (read OK).
* => we may sleep while cleaning if SYNCIO [with map read-locked]
*/
int
uvm_map_clean(struct vm_map *map, vaddr_t start, vaddr_t end, int flags)
{
struct vm_map_entry *first, *entry;
struct vm_amap *amap;
struct vm_anon *anon;
struct vm_page *pg;
struct uvm_object *uobj;
vaddr_t cp_start, cp_end;
int refs;
int error;
boolean_t rv;
KASSERT((flags & (PGO_FREE|PGO_DEACTIVATE)) !=
(PGO_FREE|PGO_DEACTIVATE));
if (start > end || start < map->min_offset || end > map->max_offset)
return EINVAL;
vm_map_lock_read(map);
first = uvm_map_entrybyaddr(&map->addr, start);
/* Make a first pass to check for holes. */
for (entry = first; entry != NULL && entry->start < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
if (UVM_ET_ISSUBMAP(entry)) {
vm_map_unlock_read(map);
return EINVAL;
}
if (UVM_ET_ISSUBMAP(entry) ||
UVM_ET_ISHOLE(entry) ||
(entry->end < end &&
VMMAP_FREE_END(entry) != entry->end)) {
vm_map_unlock_read(map);
return EFAULT;
}
}
error = 0;
for (entry = first; entry != NULL && entry->start < end;
entry = RBT_NEXT(uvm_map_addr, entry)) {
amap = entry->aref.ar_amap; /* top layer */
if (UVM_ET_ISOBJ(entry))
uobj = entry->object.uvm_obj;
else
uobj = NULL;
/*
* No amap cleaning necessary if:
* - there's no amap
* - we're not deactivating or freeing pages.
*/
if (amap == NULL || (flags & (PGO_DEACTIVATE|PGO_FREE)) == 0)
goto flush_object;
cp_start = MAX(entry->start, start);
cp_end = MIN(entry->end, end);
amap_lock(amap);
for (; cp_start != cp_end; cp_start += PAGE_SIZE) {
anon = amap_lookup(&entry->aref,
cp_start - entry->start);
if (anon == NULL)
continue;
KASSERT(anon->an_lock == amap->am_lock);
pg = anon->an_page;
if (pg == NULL) {
continue;
}
KASSERT(pg->pg_flags & PQ_ANON);
switch (flags & (PGO_CLEANIT|PGO_FREE|PGO_DEACTIVATE)) {
/*
* XXX In these first 3 cases, we always just
* XXX deactivate the page. We may want to
* XXX handle the different cases more
* XXX specifically, in the future.
*/
case PGO_CLEANIT|PGO_FREE:
case PGO_CLEANIT|PGO_DEACTIVATE:
case PGO_DEACTIVATE:
deactivate_it:
/* skip the page if it's wired */
if (pg->wire_count != 0)
break;
uvm_lock_pageq();
KASSERT(pg->uanon == anon);
/* zap all mappings for the page. */
pmap_page_protect(pg, PROT_NONE);
/* ...and deactivate the page. */
uvm_pagedeactivate(pg);
uvm_unlock_pageq();
break;
case PGO_FREE:
/*
* If there are multiple references to
* the amap, just deactivate the page.
*/
if (amap_refs(amap) > 1)
goto deactivate_it;
/* XXX skip the page if it's wired */
if (pg->wire_count != 0) {
break;
}
amap_unadd(&entry->aref,
cp_start - entry->start);
refs = --anon->an_ref;
if (refs == 0)
uvm_anfree(anon);
break;
default:
panic("uvm_map_clean: weird flags");
}
}
amap_unlock(amap);
flush_object:
cp_start = MAX(entry->start, start);
cp_end = MIN(entry->end, end);
/*
* flush pages if we've got a valid backing object.
*
* Don't PGO_FREE if we don't have write permission
* and don't flush if this is a copy-on-write object
* since we can't know our permissions on it.
*/
if (uobj != NULL &&
((flags & PGO_FREE) == 0 ||
((entry->max_protection & PROT_WRITE) != 0 &&
(entry->etype & UVM_ET_COPYONWRITE) == 0))) {
rw_enter(uobj->vmobjlock, RW_WRITE);
rv = uobj->pgops->pgo_flush(uobj,
cp_start - entry->start + entry->offset,
cp_end - entry->start + entry->offset, flags);
rw_exit(uobj->vmobjlock);
if (rv == FALSE)
error = EFAULT;
}
}
vm_map_unlock_read(map);
return error;
}
/*
* UVM_MAP_CLIP_END implementation
*/
void
uvm_map_clip_end(struct vm_map *map, struct vm_map_entry *entry, vaddr_t addr)
{
struct vm_map_entry *tmp;
KASSERT(entry->start < addr && VMMAP_FREE_END(entry) > addr);
tmp = uvm_mapent_alloc(map, 0);
/* Invoke splitentry. */
uvm_map_splitentry(map, entry, tmp, addr);
}
/*
* UVM_MAP_CLIP_START implementation
*
* Clippers are required to not change the pointers to the entry they are
* clipping on.
* Since uvm_map_splitentry turns the original entry into the lowest
* entry (address wise) we do a swap between the new entry and the original
* entry, prior to calling uvm_map_splitentry.
*/
void
uvm_map_clip_start(struct vm_map *map, struct vm_map_entry *entry, vaddr_t addr)
{
struct vm_map_entry *tmp;
struct uvm_addr_state *free;
/* Unlink original. */
free = uvm_map_uaddr_e(map, entry);
uvm_mapent_free_remove(map, free, entry);
uvm_mapent_addr_remove(map, entry);
/* Copy entry. */
KASSERT(entry->start < addr && VMMAP_FREE_END(entry) > addr);
tmp = uvm_mapent_alloc(map, 0);
uvm_mapent_copy(entry, tmp);
/* Put new entry in place of original entry. */
uvm_mapent_addr_insert(map, tmp);
uvm_mapent_free_insert(map, free, tmp);
/* Invoke splitentry. */
uvm_map_splitentry(map, tmp, entry, addr);
}
/*
* Boundary fixer.
*/
static inline vaddr_t uvm_map_boundfix(vaddr_t, vaddr_t, vaddr_t);
static inline vaddr_t
uvm_map_boundfix(vaddr_t min, vaddr_t max, vaddr_t bound)
{
return (min < bound && max > bound) ? bound : max;
}
/*
* Choose free list based on address at start of free space.
*
* The uvm_addr_state returned contains addr and is the first of:
* - uaddr_exe
* - uaddr_brk_stack
* - uaddr_any
*/
struct uvm_addr_state*
uvm_map_uaddr(struct vm_map *map, vaddr_t addr)
{
struct uvm_addr_state *uaddr;
int i;
/* Special case the first page, to prevent mmap from returning 0. */
if (addr < VMMAP_MIN_ADDR)
return NULL;
/* Upper bound for kernel maps at uvm_maxkaddr. */
if ((map->flags & VM_MAP_ISVMSPACE) == 0) {
if (addr >= uvm_maxkaddr)
return NULL;
}
/* Is the address inside the exe-only map? */
if (map->uaddr_exe != NULL && addr >= map->uaddr_exe->uaddr_minaddr &&
addr < map->uaddr_exe->uaddr_maxaddr)
return map->uaddr_exe;
/* Check if the space falls inside brk/stack area. */
if ((addr >= map->b_start && addr < map->b_end) ||
(addr >= map->s_start && addr < map->s_end)) {
if (map->uaddr_brk_stack != NULL &&
addr >= map->uaddr_brk_stack->uaddr_minaddr &&
addr < map->uaddr_brk_stack->uaddr_maxaddr) {
return map->uaddr_brk_stack;
} else
return NULL;
}
/*
* Check the other selectors.
*
* These selectors are only marked as the owner, if they have insert
* functions.
*/
for (i = 0; i < nitems(map->uaddr_any); i++) {
uaddr = map->uaddr_any[i];
if (uaddr == NULL)
continue;
if (uaddr->uaddr_functions->uaddr_free_insert == NULL)
continue;
if (addr >= uaddr->uaddr_minaddr &&
addr < uaddr->uaddr_maxaddr)
return uaddr;
}
return NULL;
}
/*
* Choose free list based on address at start of free space.
*
* The uvm_addr_state returned contains addr and is the first of:
* - uaddr_exe
* - uaddr_brk_stack
* - uaddr_any
*/
struct uvm_addr_state*
uvm_map_uaddr_e(struct vm_map *map, struct vm_map_entry *entry)
{
return uvm_map_uaddr(map, VMMAP_FREE_START(entry));
}
/*
* Returns the first free-memory boundary that is crossed by [min-max].
*/
vsize_t
uvm_map_boundary(struct vm_map *map, vaddr_t min, vaddr_t max)
{
struct uvm_addr_state *uaddr;
int i;
/* Never return first page. */
max = uvm_map_boundfix(min, max, VMMAP_MIN_ADDR);
/* Treat the maxkaddr special, if the map is a kernel_map. */
if ((map->flags & VM_MAP_ISVMSPACE) == 0)
max = uvm_map_boundfix(min, max, uvm_maxkaddr);
/* Check for exe-only boundaries. */
if (map->uaddr_exe != NULL) {
max = uvm_map_boundfix(min, max, map->uaddr_exe->uaddr_minaddr);
max = uvm_map_boundfix(min, max, map->uaddr_exe->uaddr_maxaddr);
}
/* Check for exe-only boundaries. */
if (map->uaddr_brk_stack != NULL) {
max = uvm_map_boundfix(min, max,
map->uaddr_brk_stack->uaddr_minaddr);
max = uvm_map_boundfix(min, max,
map->uaddr_brk_stack->uaddr_maxaddr);
}
/* Check other boundaries. */
for (i = 0; i < nitems(map->uaddr_any); i++) {
uaddr = map->uaddr_any[i];
if (uaddr != NULL) {
max = uvm_map_boundfix(min, max, uaddr->uaddr_minaddr);
max = uvm_map_boundfix(min, max, uaddr->uaddr_maxaddr);
}
}
/* Boundaries at stack and brk() area. */
max = uvm_map_boundfix(min, max, map->s_start);
max = uvm_map_boundfix(min, max, map->s_end);
max = uvm_map_boundfix(min, max, map->b_start);
max = uvm_map_boundfix(min, max, map->b_end);
return max;
}
/*
* Update map allocation start and end addresses from proc vmspace.
*/
void
uvm_map_vmspace_update(struct vm_map *map,
struct uvm_map_deadq *dead, int flags)
{
struct vmspace *vm;
vaddr_t b_start, b_end, s_start, s_end;
KASSERT(map->flags & VM_MAP_ISVMSPACE);
KASSERT(offsetof(struct vmspace, vm_map) == 0);
/*
* Derive actual allocation boundaries from vmspace.
*/
vm = (struct vmspace *)map;
b_start = (vaddr_t)vm->vm_daddr;
b_end = b_start + BRKSIZ;
s_start = MIN((vaddr_t)vm->vm_maxsaddr, (vaddr_t)vm->vm_minsaddr);
s_end = MAX((vaddr_t)vm->vm_maxsaddr, (vaddr_t)vm->vm_minsaddr);
#ifdef DIAGNOSTIC
if ((b_start & (vaddr_t)PAGE_MASK) != 0 ||
(b_end & (vaddr_t)PAGE_MASK) != 0 ||
(s_start & (vaddr_t)PAGE_MASK) != 0 ||
(s_end & (vaddr_t)PAGE_MASK) != 0) {
panic("uvm_map_vmspace_update: vmspace %p invalid bounds: "
"b=0x%lx-0x%lx s=0x%lx-0x%lx",
vm, b_start, b_end, s_start, s_end);
}
#endif
if (__predict_true(map->b_start == b_start && map->b_end == b_end &&
map->s_start == s_start && map->s_end == s_end))
return;
uvm_map_freelist_update(map, dead, b_start, b_end,
s_start, s_end, flags);
}
/*
* Grow kernel memory.
*
* This function is only called for kernel maps when an allocation fails.
*
* If the map has a gap that is large enough to accommodate alloc_sz, this
* function will make sure map->free will include it.
*/
void
uvm_map_kmem_grow(struct vm_map *map, struct uvm_map_deadq *dead,
vsize_t alloc_sz, int flags)
{
vsize_t sz;
vaddr_t end;
struct vm_map_entry *entry;
/* Kernel memory only. */
KASSERT((map->flags & VM_MAP_ISVMSPACE) == 0);
/* Destroy free list. */
uvm_map_freelist_update_clear(map, dead);
/* Include the guard page in the hard minimum requirement of alloc_sz. */
if (map->flags & VM_MAP_GUARDPAGES)
alloc_sz += PAGE_SIZE;
/*
* Grow by ALLOCMUL * alloc_sz, but at least VM_MAP_KSIZE_DELTA.
*
* Don't handle the case where the multiplication overflows:
* if that happens, the allocation is probably too big anyway.
*/
sz = MAX(VM_MAP_KSIZE_ALLOCMUL * alloc_sz, VM_MAP_KSIZE_DELTA);
/*
* Walk forward until a gap large enough for alloc_sz shows up.
*
* We assume the kernel map has no boundaries.
* uvm_maxkaddr may be zero.
*/
end = MAX(uvm_maxkaddr, map->min_offset);
entry = uvm_map_entrybyaddr(&map->addr, end);
while (entry && entry->fspace < alloc_sz)
entry = RBT_NEXT(uvm_map_addr, entry);
if (entry) {
end = MAX(VMMAP_FREE_START(entry), end);
end += MIN(sz, map->max_offset - end);
} else
end = map->max_offset;
/* Reserve pmap entries. */
#ifdef PMAP_GROWKERNEL
uvm_maxkaddr = pmap_growkernel(end);
#else
uvm_maxkaddr = MAX(uvm_maxkaddr, end);
#endif
/* Rebuild free list. */
uvm_map_freelist_update_refill(map, flags);
}
/*
* Freelist update subfunction: unlink all entries from freelists.
*/
void
uvm_map_freelist_update_clear(struct vm_map *map, struct uvm_map_deadq *dead)
{
struct uvm_addr_state *free;
struct vm_map_entry *entry, *prev, *next;
prev = NULL;
for (entry = RBT_MIN(uvm_map_addr, &map->addr); entry != NULL;
entry = next) {
next = RBT_NEXT(uvm_map_addr, entry);
free = uvm_map_uaddr_e(map, entry);
uvm_mapent_free_remove(map, free, entry);
if (prev != NULL && entry->start == entry->end) {
prev->fspace += VMMAP_FREE_END(entry) - entry->end;
uvm_mapent_addr_remove(map, entry);
DEAD_ENTRY_PUSH(dead, entry);
} else
prev = entry;
}
}
/*
* Freelist update subfunction: refill the freelists with entries.
*/
void
uvm_map_freelist_update_refill(struct vm_map *map, int flags)
{
struct vm_map_entry *entry;
vaddr_t min, max;
RBT_FOREACH(entry, uvm_map_addr, &map->addr) {
min = VMMAP_FREE_START(entry);
max = VMMAP_FREE_END(entry);
entry->fspace = 0;
entry = uvm_map_fix_space(map, entry, min, max, flags);
}
uvm_tree_sanity(map, __FILE__, __LINE__);
}
/*
* Change {a,b}_{start,end} allocation ranges and associated free lists.
*/
void
uvm_map_freelist_update(struct vm_map *map, struct uvm_map_deadq *dead,
vaddr_t b_start, vaddr_t b_end, vaddr_t s_start, vaddr_t s_end, int flags)
{
KDASSERT(b_end >= b_start && s_end >= s_start);
/* Clear all free lists. */
uvm_map_freelist_update_clear(map, dead);
/* Apply new bounds. */
map->b_start = b_start;
map->b_end = b_end;
map->s_start = s_start;
map->s_end = s_end;
/* Refill free lists. */
uvm_map_freelist_update_refill(map, flags);
}
/*
* Assign a uvm_addr_state to the specified pointer in vm_map.
*
* May sleep.
*/
void
uvm_map_set_uaddr(struct vm_map *map, struct uvm_addr_state **which,
struct uvm_addr_state *newval)
{
struct uvm_map_deadq dead;
/* Pointer which must be in this map. */
KASSERT(which != NULL);
KASSERT((void*)map <= (void*)(which) &&
(void*)(which) < (void*)(map + 1));
vm_map_lock(map);
TAILQ_INIT(&dead);
uvm_map_freelist_update_clear(map, &dead);
uvm_addr_destroy(*which);
*which = newval;
uvm_map_freelist_update_refill(map, 0);
vm_map_unlock(map);
uvm_unmap_detach(&dead, 0);
}
/*
* Correct space insert.
*
* Entry must not be on any freelist.
*/
struct vm_map_entry*
uvm_map_fix_space(struct vm_map *map, struct vm_map_entry *entry,
vaddr_t min, vaddr_t max, int flags)
{
struct uvm_addr_state *free, *entfree;
vaddr_t lmax;
KASSERT(entry == NULL || (entry->etype & UVM_ET_FREEMAPPED) == 0);
KDASSERT(min <= max);
KDASSERT((entry != NULL && VMMAP_FREE_END(entry) == min) ||
min == map->min_offset);
/*
* During the function, entfree will always point at the uaddr state
* for entry.
*/
entfree = (entry == NULL ? NULL :
uvm_map_uaddr_e(map, entry));
while (min != max) {
/* Claim guard page for entry. */
if ((map->flags & VM_MAP_GUARDPAGES) && entry != NULL &&
VMMAP_FREE_END(entry) == entry->end &&
entry->start != entry->end) {
if (max - min == 2 * PAGE_SIZE) {
/*
* If the free-space gap is exactly 2 pages,
* we make the guard 2 pages instead of 1.
* Because in a guarded map, an area needs
* at least 2 pages to allocate from:
* one page for the allocation and one for
* the guard.
*/
entry->guard = 2 * PAGE_SIZE;
min = max;
} else {
entry->guard = PAGE_SIZE;
min += PAGE_SIZE;
}
continue;
}
/*
* Handle the case where entry has a 2-page guard, but the
* space after entry is freed.
*/
if (entry != NULL && entry->fspace == 0 &&
entry->guard > PAGE_SIZE) {
entry->guard = PAGE_SIZE;
min = VMMAP_FREE_START(entry);
}
lmax = uvm_map_boundary(map, min, max);
free = uvm_map_uaddr(map, min);
/*
* Entries are merged if they point at the same uvm_free().
* Exception to that rule: if min == uvm_maxkaddr, a new
* entry is started regardless (otherwise the allocators
* will get confused).
*/
if (entry != NULL && free == entfree &&
!((map->flags & VM_MAP_ISVMSPACE) == 0 &&
min == uvm_maxkaddr)) {
KDASSERT(VMMAP_FREE_END(entry) == min);
entry->fspace += lmax - min;
} else {
/*
* Commit entry to free list: it'll not be added to
* anymore.
* We'll start a new entry and add to that entry
* instead.
*/
if (entry != NULL)
uvm_mapent_free_insert(map, entfree, entry);
/* New entry for new uaddr. */
entry = uvm_mapent_alloc(map, flags);
KDASSERT(entry != NULL);
entry->end = entry->start = min;
entry->guard = 0;
entry->fspace = lmax - min;
entry->object.uvm_obj = NULL;
entry->offset = 0;
entry->etype = 0;
entry->protection = entry->max_protection = 0;
entry->inheritance = 0;
entry->wired_count = 0;
entry->advice = 0;
entry->aref.ar_pageoff = 0;
entry->aref.ar_amap = NULL;
uvm_mapent_addr_insert(map, entry);
entfree = free;
}
min = lmax;
}
/* Finally put entry on the uaddr state. */
if (entry != NULL)
uvm_mapent_free_insert(map, entfree, entry);
return entry;
}
/*
* MQuery style of allocation.
*
* This allocator searches forward until sufficient space is found to map
* the given size.
*
* XXX: factor in offset (via pmap_prefer) and protection?
*/
int
uvm_map_mquery(struct vm_map *map, vaddr_t *addr_p, vsize_t sz, voff_t offset,
int flags)
{
struct vm_map_entry *entry, *last;
vaddr_t addr;
vaddr_t tmp, pmap_align, pmap_offset;
int error;
addr = *addr_p;
vm_map_lock_read(map);
/* Configure pmap prefer. */
if (offset != UVM_UNKNOWN_OFFSET) {
pmap_align = MAX(PAGE_SIZE, PMAP_PREFER_ALIGN());
pmap_offset = PMAP_PREFER_OFFSET(offset);
} else {
pmap_align = PAGE_SIZE;
pmap_offset = 0;
}
/* Align address to pmap_prefer unless FLAG_FIXED is set. */
if (!(flags & UVM_FLAG_FIXED) && offset != UVM_UNKNOWN_OFFSET) {
tmp = (addr & ~(pmap_align - 1)) | pmap_offset;
if (tmp < addr)
tmp += pmap_align;
addr = tmp;
}
/* First, check if the requested range is fully available. */
entry = uvm_map_entrybyaddr(&map->addr, addr);
last = NULL;
if (uvm_map_isavail(map, NULL, &entry, &last, addr, sz)) {
error = 0;
goto out;
}
if (flags & UVM_FLAG_FIXED) {
error = EINVAL;
goto out;
}
error = ENOMEM; /* Default error from here. */
/*
* At this point, the memory at <addr, sz> is not available.
* The reasons are:
* [1] it's outside the map,
* [2] it starts in used memory (and therefore needs to move
* toward the first free page in entry),
* [3] it starts in free memory but bumps into used memory.
*
* Note that for case [2], the forward moving is handled by the
* for loop below.
*/
if (entry == NULL) {
/* [1] Outside the map. */
if (addr >= map->max_offset)
goto out;
else
entry = RBT_MIN(uvm_map_addr, &map->addr);
} else if (VMMAP_FREE_START(entry) <= addr) {
/* [3] Bumped into used memory. */
entry = RBT_NEXT(uvm_map_addr, entry);
}
/* Test if the next entry is sufficient for the allocation. */
for (; entry != NULL;
entry = RBT_NEXT(uvm_map_addr, entry)) {
if (entry->fspace == 0)
continue;
addr = VMMAP_FREE_START(entry);
restart: /* Restart address checks on address change. */
tmp = (addr & ~(pmap_align - 1)) | pmap_offset;
if (tmp < addr)
tmp += pmap_align;
addr = tmp;
if (addr >= VMMAP_FREE_END(entry))
continue;
/* Skip brk() allocation addresses. */
if (addr + sz > map->b_start && addr < map->b_end) {
if (VMMAP_FREE_END(entry) > map->b_end) {
addr = map->b_end;
goto restart;
} else
continue;
}
/* Skip stack allocation addresses. */
if (addr + sz > map->s_start && addr < map->s_end) {
if (VMMAP_FREE_END(entry) > map->s_end) {
addr = map->s_end;
goto restart;
} else
continue;
}
last = NULL;
if (uvm_map_isavail(map, NULL, &entry, &last, addr, sz)) {
error = 0;
goto out;
}
}
out:
vm_map_unlock_read(map);
if (error == 0)
*addr_p = addr;
return error;
}
boolean_t
vm_map_lock_try_ln(struct vm_map *map, char *file, int line)
{
boolean_t rv;
if (map->flags & VM_MAP_INTRSAFE) {
rv = mtx_enter_try(&map->mtx);
} else {
mtx_enter(&map->flags_lock);
if (map->flags & VM_MAP_BUSY) {
mtx_leave(&map->flags_lock);
return (FALSE);
}
mtx_leave(&map->flags_lock);
rv = (rw_enter(&map->lock, RW_WRITE|RW_NOSLEEP) == 0);
/* check if the lock is busy and back out if we won the race */
if (rv) {
mtx_enter(&map->flags_lock);
if (map->flags & VM_MAP_BUSY) {
rw_exit(&map->lock);
rv = FALSE;
}
mtx_leave(&map->flags_lock);
}
}
if (rv) {
map->timestamp++;
LPRINTF(("map lock: %p (at %s %d)\n", map, file, line));
uvm_tree_sanity(map, file, line);
uvm_tree_size_chk(map, file, line);
}
return (rv);
}
void
vm_map_lock_ln(struct vm_map *map, char *file, int line)
{
if ((map->flags & VM_MAP_INTRSAFE) == 0) {
do {
mtx_enter(&map->flags_lock);
tryagain:
while (map->flags & VM_MAP_BUSY) {
map->flags |= VM_MAP_WANTLOCK;
msleep_nsec(&map->flags, &map->flags_lock,
PVM, vmmapbsy, INFSLP);
}
mtx_leave(&map->flags_lock);
} while (rw_enter(&map->lock, RW_WRITE|RW_SLEEPFAIL) != 0);
/* check if the lock is busy and back out if we won the race */
mtx_enter(&map->flags_lock);
if (map->flags & VM_MAP_BUSY) {
rw_exit(&map->lock);
goto tryagain;
}
mtx_leave(&map->flags_lock);
} else {
mtx_enter(&map->mtx);
}
map->timestamp++;
LPRINTF(("map lock: %p (at %s %d)\n", map, file, line));
uvm_tree_sanity(map, file, line);
uvm_tree_size_chk(map, file, line);
}
void
vm_map_lock_read_ln(struct vm_map *map, char *file, int line)
{
if ((map->flags & VM_MAP_INTRSAFE) == 0)
rw_enter_read(&map->lock);
else
mtx_enter(&map->mtx);
LPRINTF(("map lock: %p (at %s %d)\n", map, file, line));
uvm_tree_sanity(map, file, line);
uvm_tree_size_chk(map, file, line);
}
void
vm_map_unlock_ln(struct vm_map *map, char *file, int line)
{
uvm_tree_sanity(map, file, line);
uvm_tree_size_chk(map, file, line);
LPRINTF(("map unlock: %p (at %s %d)\n", map, file, line));
if ((map->flags & VM_MAP_INTRSAFE) == 0)
rw_exit(&map->lock);
else
mtx_leave(&map->mtx);
}
void
vm_map_unlock_read_ln(struct vm_map *map, char *file, int line)
{
/* XXX: RO */ uvm_tree_sanity(map, file, line);
/* XXX: RO */ uvm_tree_size_chk(map, file, line);
LPRINTF(("map unlock: %p (at %s %d)\n", map, file, line));
if ((map->flags & VM_MAP_INTRSAFE) == 0)
rw_exit_read(&map->lock);
else
mtx_leave(&map->mtx);
}
void
vm_map_downgrade_ln(struct vm_map *map, char *file, int line)
{
uvm_tree_sanity(map, file, line);
uvm_tree_size_chk(map, file, line);
LPRINTF(("map unlock: %p (at %s %d)\n", map, file, line));
LPRINTF(("map lock: %p (at %s %d)\n", map, file, line));
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
if ((map->flags & VM_MAP_INTRSAFE) == 0)
rw_enter(&map->lock, RW_DOWNGRADE);
}
void
vm_map_upgrade_ln(struct vm_map *map, char *file, int line)
{
/* XXX: RO */ uvm_tree_sanity(map, file, line);
/* XXX: RO */ uvm_tree_size_chk(map, file, line);
LPRINTF(("map unlock: %p (at %s %d)\n", map, file, line));
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
if ((map->flags & VM_MAP_INTRSAFE) == 0) {
rw_exit_read(&map->lock);
rw_enter_write(&map->lock);
}
LPRINTF(("map lock: %p (at %s %d)\n", map, file, line));
uvm_tree_sanity(map, file, line);
}
void
vm_map_busy_ln(struct vm_map *map, char *file, int line)
{
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
mtx_enter(&map->flags_lock);
map->flags |= VM_MAP_BUSY;
mtx_leave(&map->flags_lock);
}
void
vm_map_unbusy_ln(struct vm_map *map, char *file, int line)
{
int oflags;
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
mtx_enter(&map->flags_lock);
oflags = map->flags;
map->flags &= ~(VM_MAP_BUSY|VM_MAP_WANTLOCK);
mtx_leave(&map->flags_lock);
if (oflags & VM_MAP_WANTLOCK)
wakeup(&map->flags);
}
#ifndef SMALL_KERNEL
int
uvm_map_fill_vmmap(struct vm_map *map, struct kinfo_vmentry *kve,
size_t *lenp)
{
struct vm_map_entry *entry;
vaddr_t start;
int cnt, maxcnt, error = 0;
KASSERT(*lenp > 0);
KASSERT((*lenp % sizeof(*kve)) == 0);
cnt = 0;
maxcnt = *lenp / sizeof(*kve);
KASSERT(maxcnt > 0);
/*
* Return only entries whose address is above the given base
* address. This allows userland to iterate without knowing the
* number of entries beforehand.
*/
start = (vaddr_t)kve[0].kve_start;
vm_map_lock(map);
RBT_FOREACH(entry, uvm_map_addr, &map->addr) {
if (cnt == maxcnt) {
error = ENOMEM;
break;
}
if (start != 0 && entry->start < start)
continue;
kve->kve_start = entry->start;
kve->kve_end = entry->end;
kve->kve_guard = entry->guard;
kve->kve_fspace = entry->fspace;
kve->kve_fspace_augment = entry->fspace_augment;
kve->kve_offset = entry->offset;
kve->kve_wired_count = entry->wired_count;
kve->kve_etype = entry->etype;
kve->kve_protection = entry->protection;
kve->kve_max_protection = entry->max_protection;
kve->kve_advice = entry->advice;
kve->kve_inheritance = entry->inheritance;
kve->kve_flags = entry->flags;
kve++;
cnt++;
}
vm_map_unlock(map);
KASSERT(cnt <= maxcnt);
*lenp = sizeof(*kve) * cnt;
return error;
}
#endif
RBT_GENERATE_AUGMENT(uvm_map_addr, vm_map_entry, daddrs.addr_entry,
uvm_mapentry_addrcmp, uvm_map_addr_augment);
/*
* MD code: vmspace allocator setup.
*/
#ifdef __i386__
void
uvm_map_setup_md(struct vm_map *map)
{
vaddr_t min, max;
min = map->min_offset;
max = map->max_offset;
/*
* Ensure the selectors will not try to manage page 0;
* it's too special.
*/
if (min < VMMAP_MIN_ADDR)
min = VMMAP_MIN_ADDR;
#if 0 /* Cool stuff, not yet */
/* Executable code is special. */
map->uaddr_exe = uaddr_rnd_create(min, I386_MAX_EXE_ADDR);
/* Place normal allocations beyond executable mappings. */
map->uaddr_any[3] = uaddr_pivot_create(2 * I386_MAX_EXE_ADDR, max);
#else /* Crappy stuff, for now */
map->uaddr_any[0] = uaddr_rnd_create(min, max);
#endif
#ifndef SMALL_KERNEL
map->uaddr_brk_stack = uaddr_stack_brk_create(min, max);
#endif /* !SMALL_KERNEL */
}
#elif __LP64__
void
uvm_map_setup_md(struct vm_map *map)
{
vaddr_t min, max;
min = map->min_offset;
max = map->max_offset;
/*
* Ensure the selectors will not try to manage page 0;
* it's too special.
*/
if (min < VMMAP_MIN_ADDR)
min = VMMAP_MIN_ADDR;
#if 0 /* Cool stuff, not yet */
map->uaddr_any[3] = uaddr_pivot_create(MAX(min, 0x100000000ULL), max);
#else /* Crappy stuff, for now */
map->uaddr_any[0] = uaddr_rnd_create(min, max);
#endif
#ifndef SMALL_KERNEL
map->uaddr_brk_stack = uaddr_stack_brk_create(min, max);
#endif /* !SMALL_KERNEL */
}
#else /* non-i386, 32 bit */
void
uvm_map_setup_md(struct vm_map *map)
{
vaddr_t min, max;
min = map->min_offset;
max = map->max_offset;
/*
* Ensure the selectors will not try to manage page 0;
* it's too special.
*/
if (min < VMMAP_MIN_ADDR)
min = VMMAP_MIN_ADDR;
#if 0 /* Cool stuff, not yet */
map->uaddr_any[3] = uaddr_pivot_create(min, max);
#else /* Crappy stuff, for now */
map->uaddr_any[0] = uaddr_rnd_create(min, max);
#endif
#ifndef SMALL_KERNEL
map->uaddr_brk_stack = uaddr_stack_brk_create(min, max);
#endif /* !SMALL_KERNEL */
}
#endif
215
215
215
215
208
208
215
215
215
215
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
/* $OpenBSD: kern_bufq.c,v 1.34 2022/08/14 01:58:27 jsg Exp $ */
/*
* Copyright (c) 2010 Thordur I. Bjornsson <thib@openbsd.org>
* Copyright (c) 2010 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/buf.h>
#include <sys/errno.h>
#include <sys/queue.h>
SLIST_HEAD(, bufq) bufqs = SLIST_HEAD_INITIALIZER(bufqs);
struct mutex bufqs_mtx = MUTEX_INITIALIZER(IPL_NONE);
int bufqs_stop;
struct bufq_impl {
void *(*impl_create)(void);
void (*impl_destroy)(void *);
void (*impl_queue)(void *, struct buf *);
struct buf *(*impl_dequeue)(void *);
void (*impl_requeue)(void *, struct buf *);
int (*impl_peek)(void *);
};
void *bufq_fifo_create(void);
void bufq_fifo_destroy(void *);
void bufq_fifo_queue(void *, struct buf *);
struct buf *bufq_fifo_dequeue(void *);
void bufq_fifo_requeue(void *, struct buf *);
int bufq_fifo_peek(void *);
void *bufq_nscan_create(void);
void bufq_nscan_destroy(void *);
void bufq_nscan_queue(void *, struct buf *);
struct buf *bufq_nscan_dequeue(void *);
void bufq_nscan_requeue(void *, struct buf *);
int bufq_nscan_peek(void *);
const struct bufq_impl bufq_impls[BUFQ_HOWMANY] = {
{
bufq_fifo_create,
bufq_fifo_destroy,
bufq_fifo_queue,
bufq_fifo_dequeue,
bufq_fifo_requeue,
bufq_fifo_peek
},
{
bufq_nscan_create,
bufq_nscan_destroy,
bufq_nscan_queue,
bufq_nscan_dequeue,
bufq_nscan_requeue,
bufq_nscan_peek
}
};
int
bufq_init(struct bufq *bq, int type)
{
u_int hi = BUFQ_HI, low = BUFQ_LOW;
if (type >= BUFQ_HOWMANY)
panic("bufq_init: type %i unknown", type);
/*
* Ensure that writes can't consume the entire amount of kva
* available the buffer cache if we only have a limited amount
* of kva available to us.
*/
if (hi >= (bcstats.kvaslots / 16)) {
hi = bcstats.kvaslots / 16;
if (hi < 2)
hi = 2;
low = hi / 2;
}
mtx_init(&bq->bufq_mtx, IPL_BIO);
bq->bufq_hi = hi;
bq->bufq_low = low;
bq->bufq_type = type;
bq->bufq_impl = &bufq_impls[type];
bq->bufq_data = bq->bufq_impl->impl_create();
if (bq->bufq_data == NULL) {
/*
* we should actually return failure so disks attaching after
* boot in low memory situations dont panic the system.
*/
panic("bufq init fail");
}
mtx_enter(&bufqs_mtx);
while (bufqs_stop) {
msleep_nsec(&bufqs_stop, &bufqs_mtx, PRIBIO, "bqinit", INFSLP);
}
SLIST_INSERT_HEAD(&bufqs, bq, bufq_entries);
mtx_leave(&bufqs_mtx);
return (0);
}
int
bufq_switch(struct bufq *bq, int type)
{
void *data;
void *odata;
int otype;
struct buf *bp;
int ret;
mtx_enter(&bq->bufq_mtx);
ret = (bq->bufq_type == type);
mtx_leave(&bq->bufq_mtx);
if (ret)
return (0);
data = bufq_impls[type].impl_create();
if (data == NULL)
return (ENOMEM);
mtx_enter(&bq->bufq_mtx);
if (bq->bufq_type != type) { /* might have changed during create */
odata = bq->bufq_data;
otype = bq->bufq_type;
while ((bp = bufq_impls[otype].impl_dequeue(odata)) != NULL)
bufq_impls[type].impl_queue(data, bp);
bq->bufq_data = data;
bq->bufq_type = type;
bq->bufq_impl = &bufq_impls[type];
} else {
otype = type;
odata = data;
}
mtx_leave(&bq->bufq_mtx);
bufq_impls[otype].impl_destroy(odata);
return (0);
}
void
bufq_destroy(struct bufq *bq)
{
bufq_drain(bq);
bq->bufq_impl->impl_destroy(bq->bufq_data);
bq->bufq_data = NULL;
mtx_enter(&bufqs_mtx);
while (bufqs_stop) {
msleep_nsec(&bufqs_stop, &bufqs_mtx, PRIBIO, "bqdest", INFSLP);
}
SLIST_REMOVE(&bufqs, bq, bufq, bufq_entries);
mtx_leave(&bufqs_mtx);
}
void
bufq_queue(struct bufq *bq, struct buf *bp)
{
mtx_enter(&bq->bufq_mtx);
while (bq->bufq_stop) {
msleep_nsec(&bq->bufq_stop, &bq->bufq_mtx, PRIBIO, "bqqueue",
INFSLP);
}
bp->b_bq = bq;
bq->bufq_outstanding++;
bq->bufq_impl->impl_queue(bq->bufq_data, bp);
mtx_leave(&bq->bufq_mtx);
}
struct buf *
bufq_dequeue(struct bufq *bq)
{
struct buf *bp;
mtx_enter(&bq->bufq_mtx);
bp = bq->bufq_impl->impl_dequeue(bq->bufq_data);
mtx_leave(&bq->bufq_mtx);
return (bp);
}
void
bufq_requeue(struct bufq *bq, struct buf *bp)
{
mtx_enter(&bq->bufq_mtx);
bq->bufq_impl->impl_requeue(bq->bufq_data, bp);
mtx_leave(&bq->bufq_mtx);
}
int
bufq_peek(struct bufq *bq)
{
int rv;
mtx_enter(&bq->bufq_mtx);
rv = bq->bufq_impl->impl_peek(bq->bufq_data);
mtx_leave(&bq->bufq_mtx);
return (rv);
}
void
bufq_drain(struct bufq *bq)
{
struct buf *bp;
int s;
while ((bp = bufq_dequeue(bq)) != NULL) {
bp->b_error = ENXIO;
bp->b_flags |= B_ERROR;
s = splbio();
biodone(bp);
splx(s);
}
}
void
bufq_wait(struct bufq *bq)
{
if (bq->bufq_hi) {
assertwaitok();
mtx_enter(&bq->bufq_mtx);
while (bq->bufq_outstanding >= bq->bufq_hi) {
bq->bufq_waiting++;
msleep_nsec(&bq->bufq_waiting, &bq->bufq_mtx,
PRIBIO, "bqwait", INFSLP);
bq->bufq_waiting--;
}
mtx_leave(&bq->bufq_mtx);
}
}
void
bufq_done(struct bufq *bq, struct buf *bp)
{
mtx_enter(&bq->bufq_mtx);
KASSERT(bq->bufq_outstanding > 0);
bq->bufq_outstanding--;
if (bq->bufq_stop && bq->bufq_outstanding == 0)
wakeup(&bq->bufq_outstanding);
if (bq->bufq_waiting && bq->bufq_outstanding < bq->bufq_low)
wakeup(&bq->bufq_waiting);
mtx_leave(&bq->bufq_mtx);
bp->b_bq = NULL;
}
void
bufq_quiesce(void)
{
struct bufq *bq;
mtx_enter(&bufqs_mtx);
bufqs_stop = 1;
mtx_leave(&bufqs_mtx);
/*
* We can safely walk the list since it can't be modified as
* long as bufqs_stop is non-zero.
*/
SLIST_FOREACH(bq, &bufqs, bufq_entries) {
mtx_enter(&bq->bufq_mtx);
bq->bufq_stop = 1;
while (bq->bufq_outstanding) {
msleep_nsec(&bq->bufq_outstanding, &bq->bufq_mtx,
PRIBIO, "bqquies", INFSLP);
}
mtx_leave(&bq->bufq_mtx);
}
}
void
bufq_restart(void)
{
struct bufq *bq;
mtx_enter(&bufqs_mtx);
SLIST_FOREACH(bq, &bufqs, bufq_entries) {
mtx_enter(&bq->bufq_mtx);
bq->bufq_stop = 0;
wakeup(&bq->bufq_stop);
mtx_leave(&bq->bufq_mtx);
}
bufqs_stop = 0;
wakeup(&bufqs_stop);
mtx_leave(&bufqs_mtx);
}
/*
* fifo implementation
*/
void *
bufq_fifo_create(void)
{
struct bufq_fifo_head *head;
head = malloc(sizeof(*head), M_DEVBUF, M_NOWAIT | M_ZERO);
if (head == NULL)
return (NULL);
SIMPLEQ_INIT(head);
return (head);
}
void
bufq_fifo_destroy(void *data)
{
struct bufq_fifo_head *head = data;
free(head, M_DEVBUF, sizeof(*head));
}
void
bufq_fifo_queue(void *data, struct buf *bp)
{
struct bufq_fifo_head *head = data;
SIMPLEQ_INSERT_TAIL(head, bp, b_bufq.bufq_data_fifo.bqf_entries);
}
struct buf *
bufq_fifo_dequeue(void *data)
{
struct bufq_fifo_head *head = data;
struct buf *bp;
bp = SIMPLEQ_FIRST(head);
if (bp != NULL)
SIMPLEQ_REMOVE_HEAD(head, b_bufq.bufq_data_fifo.bqf_entries);
return (bp);
}
void
bufq_fifo_requeue(void *data, struct buf *bp)
{
struct bufq_fifo_head *head = data;
SIMPLEQ_INSERT_HEAD(head, bp, b_bufq.bufq_data_fifo.bqf_entries);
}
int
bufq_fifo_peek(void *data)
{
struct bufq_fifo_head *head = data;
return (SIMPLEQ_FIRST(head) != NULL);
}
/*
* nscan implementation
*/
#define BUF_INORDER(ba, bb) ((ba)->b_blkno < (bb)->b_blkno)
#define dsentries b_bufq.bufq_data_nscan.bqf_entries
struct bufq_nscan_data {
struct bufq_nscan_head sorted;
struct bufq_nscan_head fifo;
int leftoverroom; /* Remaining number of buffer inserts allowed */
};
void bufq_nscan_resort(struct bufq_nscan_data *data);
void bufq_simple_nscan(struct bufq_nscan_head *, struct buf *);
void
bufq_simple_nscan(struct bufq_nscan_head *head, struct buf *bp)
{
struct buf *cur, *prev;
prev = NULL;
/*
* We look for the first slot where we would fit, then insert
* after the element we just passed.
*/
SIMPLEQ_FOREACH(cur, head, dsentries) {
if (BUF_INORDER(bp, cur))
break;
prev = cur;
}
if (prev)
SIMPLEQ_INSERT_AFTER(head, prev, bp, dsentries);
else
SIMPLEQ_INSERT_HEAD(head, bp, dsentries);
}
/*
* Take N elements from the fifo queue and sort them
*/
void
bufq_nscan_resort(struct bufq_nscan_data *data)
{
struct bufq_nscan_head *fifo = &data->fifo;
struct bufq_nscan_head *sorted = &data->sorted;
int count, segmentsize = BUFQ_NSCAN_N;
struct buf *bp;
for (count = 0; count < segmentsize; count++) {
bp = SIMPLEQ_FIRST(fifo);
if (!bp)
break;
SIMPLEQ_REMOVE_HEAD(fifo, dsentries);
bufq_simple_nscan(sorted, bp);
}
data->leftoverroom = segmentsize - count;
}
void *
bufq_nscan_create(void)
{
struct bufq_nscan_data *data;
data = malloc(sizeof(*data), M_DEVBUF, M_NOWAIT | M_ZERO);
if (!data)
return NULL;
SIMPLEQ_INIT(&data->sorted);
SIMPLEQ_INIT(&data->fifo);
return data;
}
void
bufq_nscan_destroy(void *vdata)
{
struct bufq_nscan_data *data = vdata;
free(data, M_DEVBUF, sizeof(*data));
}
void
bufq_nscan_queue(void *vdata, struct buf *bp)
{
struct bufq_nscan_data *data = vdata;
/*
* If the previous sorted segment was small, we will continue
* packing in bufs as long as they're in order.
*/
if (data->leftoverroom) {
struct buf *next = SIMPLEQ_FIRST(&data->sorted);
if (next && BUF_INORDER(next, bp)) {
bufq_simple_nscan(&data->sorted, bp);
data->leftoverroom--;
return;
}
}
SIMPLEQ_INSERT_TAIL(&data->fifo, bp, dsentries);
}
struct buf *
bufq_nscan_dequeue(void *vdata)
{
struct bufq_nscan_data *data = vdata;
struct bufq_nscan_head *sorted = &data->sorted;
struct buf *bp;
if (SIMPLEQ_FIRST(sorted) == NULL)
bufq_nscan_resort(data);
bp = SIMPLEQ_FIRST(sorted);
if (bp != NULL)
SIMPLEQ_REMOVE_HEAD(sorted, dsentries);
return (bp);
}
void
bufq_nscan_requeue(void *vdata, struct buf *bp)
{
struct bufq_nscan_data *data = vdata;
SIMPLEQ_INSERT_HEAD(&data->fifo, bp, dsentries);
}
int
bufq_nscan_peek(void *vdata)
{
struct bufq_nscan_data *data = vdata;
return (SIMPLEQ_FIRST(&data->sorted) != NULL) ||
(SIMPLEQ_FIRST(&data->fifo) != NULL);
}
13
3
1
230
1138
1139
1
32
1027
1472
1914
1912
1912
491
757
1914
214
40
1685
1566
230
173
1716
15
1163
1717
11
3
13
1
1719
1722
1722
1723
881
980
1139
481
1137
995
569
651
369
627
597
242
197
58
22
54
4
759
547
242
759
204
46
55
166
1
20
150
35
23
166
184
1
1
150
2
39
174
7
7
568
575
209
579
112
2
644
750
1
658
654
8
4
609
674
132
11
16
120
669
15
4
669
667
8
668
56
56
750
574
209
202
582
1913
178
230
1809
56
1779
1778
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
/* $OpenBSD: uvm_fault.c,v 1.132 2022/08/31 01:27:04 guenther Exp $ */
/* $NetBSD: uvm_fault.c,v 1.51 2000/08/06 00:22:53 thorpej Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* from: Id: uvm_fault.c,v 1.1.2.23 1998/02/06 05:29:05 chs Exp
*/
/*
* uvm_fault.c: fault handler
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/percpu.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/tracepoint.h>
#include <uvm/uvm.h>
/*
*
* a word on page faults:
*
* types of page faults we handle:
*
* CASE 1: upper layer faults CASE 2: lower layer faults
*
* CASE 1A CASE 1B CASE 2A CASE 2B
* read/write1 write>1 read/write +-cow_write/zero
* | | | |
* +--|--+ +--|--+ +-----+ + | + | +-----+
* amap | V | | ---------> new | | | | ^ |
* +-----+ +-----+ +-----+ + | + | +--|--+
* | | |
* +-----+ +-----+ +--|--+ | +--|--+
* uobj | d/c | | d/c | | V | +----+ |
* +-----+ +-----+ +-----+ +-----+
*
* d/c = don't care
*
* case [0]: layerless fault
* no amap or uobj is present. this is an error.
*
* case [1]: upper layer fault [anon active]
* 1A: [read] or [write with anon->an_ref == 1]
* I/O takes place in upper level anon and uobj is not touched.
* 1B: [write with anon->an_ref > 1]
* new anon is alloc'd and data is copied off ["COW"]
*
* case [2]: lower layer fault [uobj]
* 2A: [read on non-NULL uobj] or [write to non-copy_on_write area]
* I/O takes place directly in object.
* 2B: [write to copy_on_write] or [read on NULL uobj]
* data is "promoted" from uobj to a new anon.
* if uobj is null, then we zero fill.
*
* we follow the standard UVM locking protocol ordering:
*
* MAPS => AMAP => UOBJ => ANON => PAGE QUEUES (PQ)
* we hold a PG_BUSY page if we unlock for I/O
*
*
* the code is structured as follows:
*
* - init the "IN" params in the ufi structure
* ReFault: (ERESTART returned to the loop in uvm_fault)
* - do lookups [locks maps], check protection, handle needs_copy
* - check for case 0 fault (error)
* - establish "range" of fault
* - if we have an amap lock it and extract the anons
* - if sequential advice deactivate pages behind us
* - at the same time check pmap for unmapped areas and anon for pages
* that we could map in (and do map it if found)
* - check object for resident pages that we could map in
* - if (case 2) goto Case2
* - >>> handle case 1
* - ensure source anon is resident in RAM
* - if case 1B alloc new anon and copy from source
* - map the correct page in
* Case2:
* - >>> handle case 2
* - ensure source page is resident (if uobj)
* - if case 2B alloc new anon and copy from source (could be zero
* fill if uobj == NULL)
* - map the correct page in
* - done!
*
* note on paging:
* if we have to do I/O we place a PG_BUSY page in the correct object,
* unlock everything, and do the I/O. when I/O is done we must reverify
* the state of the world before assuming that our data structures are
* valid. [because mappings could change while the map is unlocked]
*
* alternative 1: unbusy the page in question and restart the page fault
* from the top (ReFault). this is easy but does not take advantage
* of the information that we already have from our previous lookup,
* although it is possible that the "hints" in the vm_map will help here.
*
* alternative 2: the system already keeps track of a "version" number of
* a map. [i.e. every time you write-lock a map (e.g. to change a
* mapping) you bump the version number up by one...] so, we can save
* the version number of the map before we release the lock and start I/O.
* then when I/O is done we can relock and check the version numbers
* to see if anything changed. this might save us some over 1 because
* we don't have to unbusy the page and may be less compares(?).
*
* alternative 3: put in backpointers or a way to "hold" part of a map
* in place while I/O is in progress. this could be complex to
* implement (especially with structures like amap that can be referenced
* by multiple map entries, and figuring out what should wait could be
* complex as well...).
*
* we use alternative 2. given that we are multi-threaded now we may want
* to reconsider the choice.
*/
/*
* local data structures
*/
struct uvm_advice {
int nback;
int nforw;
};
/*
* page range array: set up in uvmfault_init().
*/
static struct uvm_advice uvmadvice[MADV_MASK + 1];
#define UVM_MAXRANGE 16 /* must be max() of nback+nforw+1 */
/*
* private prototypes
*/
static void uvmfault_amapcopy(struct uvm_faultinfo *);
static inline void uvmfault_anonflush(struct vm_anon **, int);
void uvmfault_unlockmaps(struct uvm_faultinfo *, boolean_t);
void uvmfault_update_stats(struct uvm_faultinfo *);
/*
* inline functions
*/
/*
* uvmfault_anonflush: try and deactivate pages in specified anons
*
* => does not have to deactivate page if it is busy
*/
static inline void
uvmfault_anonflush(struct vm_anon **anons, int n)
{
int lcv;
struct vm_page *pg;
for (lcv = 0; lcv < n; lcv++) {
if (anons[lcv] == NULL)
continue;
KASSERT(rw_lock_held(anons[lcv]->an_lock));
pg = anons[lcv]->an_page;
if (pg && (pg->pg_flags & PG_BUSY) == 0) {
uvm_lock_pageq();
if (pg->wire_count == 0) {
pmap_page_protect(pg, PROT_NONE);
uvm_pagedeactivate(pg);
}
uvm_unlock_pageq();
}
}
}
/*
* normal functions
*/
/*
* uvmfault_init: compute proper values for the uvmadvice[] array.
*/
void
uvmfault_init(void)
{
int npages;
npages = atop(16384);
if (npages > 0) {
KASSERT(npages <= UVM_MAXRANGE / 2);
uvmadvice[MADV_NORMAL].nforw = npages;
uvmadvice[MADV_NORMAL].nback = npages - 1;
}
npages = atop(32768);
if (npages > 0) {
KASSERT(npages <= UVM_MAXRANGE / 2);
uvmadvice[MADV_SEQUENTIAL].nforw = npages - 1;
uvmadvice[MADV_SEQUENTIAL].nback = npages;
}
}
/*
* uvmfault_amapcopy: clear "needs_copy" in a map.
*
* => called with VM data structures unlocked (usually, see below)
* => we get a write lock on the maps and clear needs_copy for a VA
* => if we are out of RAM we sleep (waiting for more)
*/
static void
uvmfault_amapcopy(struct uvm_faultinfo *ufi)
{
for (;;) {
/*
* no mapping? give up.
*/
if (uvmfault_lookup(ufi, TRUE) == FALSE)
return;
/*
* copy if needed.
*/
if (UVM_ET_ISNEEDSCOPY(ufi->entry))
amap_copy(ufi->map, ufi->entry, M_NOWAIT,
UVM_ET_ISSTACK(ufi->entry) ? FALSE : TRUE,
ufi->orig_rvaddr, ufi->orig_rvaddr + 1);
/*
* didn't work? must be out of RAM. unlock and sleep.
*/
if (UVM_ET_ISNEEDSCOPY(ufi->entry)) {
uvmfault_unlockmaps(ufi, TRUE);
uvm_wait("fltamapcopy");
continue;
}
/*
* got it! unlock and return.
*/
uvmfault_unlockmaps(ufi, TRUE);
return;
}
/*NOTREACHED*/
}
/*
* uvmfault_anonget: get data in an anon into a non-busy, non-released
* page in that anon.
*
* => Map, amap and thus anon should be locked by caller.
* => If we fail, we unlock everything and error is returned.
* => If we are successful, return with everything still locked.
* => We do not move the page on the queues [gets moved later]. If we
* allocate a new page [we_own], it gets put on the queues. Either way,
* the result is that the page is on the queues at return time
*/
int
uvmfault_anonget(struct uvm_faultinfo *ufi, struct vm_amap *amap,
struct vm_anon *anon)
{
struct vm_page *pg;
int error;
KASSERT(rw_lock_held(anon->an_lock));
KASSERT(anon->an_lock == amap->am_lock);
/* Increment the counters.*/
counters_inc(uvmexp_counters, flt_anget);
if (anon->an_page) {
curproc->p_ru.ru_minflt++;
} else {
curproc->p_ru.ru_majflt++;
}
error = 0;
/*
* Loop until we get the anon data, or fail.
*/
for (;;) {
boolean_t we_own, locked;
/*
* Note: 'we_own' will become true if we set PG_BUSY on a page.
*/
we_own = FALSE;
pg = anon->an_page;
/*
* Is page resident? Make sure it is not busy/released.
*/
if (pg) {
KASSERT(pg->pg_flags & PQ_ANON);
KASSERT(pg->uanon == anon);
/*
* if the page is busy, we drop all the locks and
* try again.
*/
if ((pg->pg_flags & (PG_BUSY|PG_RELEASED)) == 0)
return (VM_PAGER_OK);
atomic_setbits_int(&pg->pg_flags, PG_WANTED);
counters_inc(uvmexp_counters, flt_pgwait);
/*
* The last unlock must be an atomic unlock and wait
* on the owner of page.
*/
if (pg->uobject) {
/* Owner of page is UVM object. */
uvmfault_unlockall(ufi, amap, NULL);
rwsleep_nsec(pg, pg->uobject->vmobjlock,
PVM | PNORELOCK, "anonget1", INFSLP);
} else {
/* Owner of page is anon. */
uvmfault_unlockall(ufi, NULL, NULL);
rwsleep_nsec(pg, anon->an_lock, PVM | PNORELOCK,
"anonget2", INFSLP);
}
} else {
/*
* No page, therefore allocate one.
*/
pg = uvm_pagealloc(NULL, 0, anon, 0);
if (pg == NULL) {
/* Out of memory. Wait a little. */
uvmfault_unlockall(ufi, amap, NULL);
counters_inc(uvmexp_counters, flt_noram);
uvm_wait("flt_noram1");
} else {
/* PG_BUSY bit is set. */
we_own = TRUE;
uvmfault_unlockall(ufi, amap, NULL);
/*
* Pass a PG_BUSY+PG_FAKE+PG_CLEAN page into
* the uvm_swap_get() function with all data
* structures unlocked. Note that it is OK
* to read an_swslot here, because we hold
* PG_BUSY on the page.
*/
counters_inc(uvmexp_counters, pageins);
error = uvm_swap_get(pg, anon->an_swslot,
PGO_SYNCIO);
/*
* We clean up after the I/O below in the
* 'we_own' case.
*/
}
}
/*
* Re-lock the map and anon.
*/
locked = uvmfault_relock(ufi);
if (locked || we_own) {
rw_enter(anon->an_lock, RW_WRITE);
}
/*
* If we own the page (i.e. we set PG_BUSY), then we need
* to clean up after the I/O. There are three cases to
* consider:
*
* 1) Page was released during I/O: free anon and ReFault.
* 2) I/O not OK. Free the page and cause the fault to fail.
* 3) I/O OK! Activate the page and sync with the non-we_own
* case (i.e. drop anon lock if not locked).
*/
if (we_own) {
if (pg->pg_flags & PG_WANTED) {
wakeup(pg);
}
/*
* if we were RELEASED during I/O, then our anon is
* no longer part of an amap. we need to free the
* anon and try again.
*/
if (pg->pg_flags & PG_RELEASED) {
pmap_page_protect(pg, PROT_NONE);
KASSERT(anon->an_ref == 0);
/*
* Released while we had unlocked amap.
*/
if (locked)
uvmfault_unlockall(ufi, NULL, NULL);
uvm_anon_release(anon); /* frees page for us */
counters_inc(uvmexp_counters, flt_pgrele);
return (VM_PAGER_REFAULT); /* refault! */
}
if (error != VM_PAGER_OK) {
KASSERT(error != VM_PAGER_PEND);
/* remove page from anon */
anon->an_page = NULL;
/*
* Remove the swap slot from the anon and
* mark the anon as having no real slot.
* Do not free the swap slot, thus preventing
* it from being used again.
*/
uvm_swap_markbad(anon->an_swslot, 1);
anon->an_swslot = SWSLOT_BAD;
/*
* Note: page was never !PG_BUSY, so it
* cannot be mapped and thus no need to
* pmap_page_protect() it.
*/
uvm_lock_pageq();
uvm_pagefree(pg);
uvm_unlock_pageq();
if (locked) {
uvmfault_unlockall(ufi, NULL, NULL);
}
rw_exit(anon->an_lock);
return (VM_PAGER_ERROR);
}
/*
* We have successfully read the page, activate it.
*/
pmap_clear_modify(pg);
uvm_lock_pageq();
uvm_pageactivate(pg);
uvm_unlock_pageq();
atomic_clearbits_int(&pg->pg_flags,
PG_WANTED|PG_BUSY|PG_FAKE);
UVM_PAGE_OWN(pg, NULL);
}
/*
* We were not able to re-lock the map - restart the fault.
*/
if (!locked) {
if (we_own) {
rw_exit(anon->an_lock);
}
return (VM_PAGER_REFAULT);
}
/*
* Verify that no one has touched the amap and moved
* the anon on us.
*/
if (ufi != NULL && amap_lookup(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start) != anon) {
uvmfault_unlockall(ufi, amap, NULL);
return (VM_PAGER_REFAULT);
}
/*
* Retry..
*/
counters_inc(uvmexp_counters, flt_anretry);
continue;
}
/*NOTREACHED*/
}
/*
* Update statistics after fault resolution.
* - maxrss
*/
void
uvmfault_update_stats(struct uvm_faultinfo *ufi)
{
struct vm_map *map;
struct proc *p;
vsize_t res;
map = ufi->orig_map;
/*
* If this is a nested pmap (eg, a virtual machine pmap managed
* by vmm(4) on amd64/i386), don't do any updating, just return.
*
* pmap_nested() on other archs is #defined to 0, so this is a
* no-op.
*/
if (pmap_nested(map->pmap))
return;
/* Update the maxrss for the process. */
if (map->flags & VM_MAP_ISVMSPACE) {
p = curproc;
KASSERT(p != NULL && &p->p_vmspace->vm_map == map);
res = pmap_resident_count(map->pmap);
/* Convert res from pages to kilobytes. */
res <<= (PAGE_SHIFT - 10);
if (p->p_ru.ru_maxrss < res)
p->p_ru.ru_maxrss = res;
}
}
/*
* F A U L T - m a i n e n t r y p o i n t
*/
/*
* uvm_fault: page fault handler
*
* => called from MD code to resolve a page fault
* => VM data structures usually should be unlocked. however, it is
* possible to call here with the main map locked if the caller
* gets a write lock, sets it recursive, and then calls us (c.f.
* uvm_map_pageable). this should be avoided because it keeps
* the map locked off during I/O.
* => MUST NEVER BE CALLED IN INTERRUPT CONTEXT
*/
#define MASK(entry) (UVM_ET_ISCOPYONWRITE(entry) ? \
~PROT_WRITE : PROT_MASK)
struct uvm_faultctx {
/*
* the following members are set up by uvm_fault_check() and
* read-only after that.
*/
vm_prot_t enter_prot;
vm_prot_t access_type;
vaddr_t startva;
int npages;
int centeridx;
boolean_t narrow;
boolean_t wired;
paddr_t pa_flags;
};
int uvm_fault_check(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_anon ***);
int uvm_fault_upper(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_anon **, vm_fault_t);
boolean_t uvm_fault_upper_lookup(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct vm_anon **, struct vm_page **);
int uvm_fault_lower(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_page **, vm_fault_t);
int
uvm_fault(vm_map_t orig_map, vaddr_t vaddr, vm_fault_t fault_type,
vm_prot_t access_type)
{
struct uvm_faultinfo ufi;
struct uvm_faultctx flt;
boolean_t shadowed;
struct vm_anon *anons_store[UVM_MAXRANGE], **anons;
struct vm_page *pages[UVM_MAXRANGE];
int error;
counters_inc(uvmexp_counters, faults);
TRACEPOINT(uvm, fault, vaddr, fault_type, access_type, NULL);
/*
* init the IN parameters in the ufi
*/
ufi.orig_map = orig_map;
ufi.orig_rvaddr = trunc_page(vaddr);
ufi.orig_size = PAGE_SIZE; /* can't get any smaller than this */
if (fault_type == VM_FAULT_WIRE)
flt.narrow = TRUE; /* don't look for neighborhood
* pages on wire */
else
flt.narrow = FALSE; /* normal fault */
flt.access_type = access_type;
error = ERESTART;
while (error == ERESTART) { /* ReFault: */
anons = anons_store;
error = uvm_fault_check(&ufi, &flt, &anons);
if (error != 0)
continue;
/* True if there is an anon at the faulting address */
shadowed = uvm_fault_upper_lookup(&ufi, &flt, anons, pages);
if (shadowed == TRUE) {
/* case 1: fault on an anon in our amap */
error = uvm_fault_upper(&ufi, &flt, anons, fault_type);
} else {
struct uvm_object *uobj = ufi.entry->object.uvm_obj;
/*
* if the desired page is not shadowed by the amap and
* we have a backing object, then we check to see if
* the backing object would prefer to handle the fault
* itself (rather than letting us do it with the usual
* pgo_get hook). the backing object signals this by
* providing a pgo_fault routine.
*/
if (uobj != NULL && uobj->pgops->pgo_fault != NULL) {
KERNEL_LOCK();
rw_enter(uobj->vmobjlock, RW_WRITE);
error = uobj->pgops->pgo_fault(&ufi,
flt.startva, pages, flt.npages,
flt.centeridx, fault_type, flt.access_type,
PGO_LOCKED);
KERNEL_UNLOCK();
if (error == VM_PAGER_OK)
error = 0;
else if (error == VM_PAGER_REFAULT)
error = ERESTART;
else
error = EACCES;
} else {
/* case 2: fault on backing obj or zero fill */
error = uvm_fault_lower(&ufi, &flt, pages,
fault_type);
}
}
}
return error;
}
/*
* uvm_fault_check: check prot, handle needs-copy, etc.
*
* 1. lookup entry.
* 2. check protection.
* 3. adjust fault condition (mainly for simulated fault).
* 4. handle needs-copy (lazy amap copy).
* 5. establish range of interest for neighbor fault (aka pre-fault).
* 6. look up anons (if amap exists).
* 7. flush pages (if MADV_SEQUENTIAL)
*
* => called with nothing locked.
* => if we fail (result != 0) we unlock everything.
* => initialize/adjust many members of flt.
*/
int
uvm_fault_check(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_anon ***ranons)
{
struct vm_amap *amap;
struct uvm_object *uobj;
int nback, nforw;
/*
* lookup and lock the maps
*/
if (uvmfault_lookup(ufi, FALSE) == FALSE) {
return EFAULT;
}
/* locked: maps(read) */
#ifdef DIAGNOSTIC
if ((ufi->map->flags & VM_MAP_PAGEABLE) == 0)
panic("uvm_fault: fault on non-pageable map (%p, 0x%lx)",
ufi->map, ufi->orig_rvaddr);
#endif
/*
* check protection
*/
if ((ufi->entry->protection & flt->access_type) != flt->access_type) {
uvmfault_unlockmaps(ufi, FALSE);
return EACCES;
}
/*
* "enter_prot" is the protection we want to enter the page in at.
* for certain pages (e.g. copy-on-write pages) this protection can
* be more strict than ufi->entry->protection. "wired" means either
* the entry is wired or we are fault-wiring the pg.
*/
flt->enter_prot = ufi->entry->protection;
flt->pa_flags = UVM_ET_ISWC(ufi->entry) ? PMAP_WC : 0;
flt->wired = VM_MAPENT_ISWIRED(ufi->entry) || (flt->narrow == TRUE);
if (flt->wired)
flt->access_type = flt->enter_prot; /* full access for wired */
/* handle "needs_copy" case. */
if (UVM_ET_ISNEEDSCOPY(ufi->entry)) {
if ((flt->access_type & PROT_WRITE) ||
(ufi->entry->object.uvm_obj == NULL)) {
/* need to clear */
uvmfault_unlockmaps(ufi, FALSE);
uvmfault_amapcopy(ufi);
counters_inc(uvmexp_counters, flt_amcopy);
return ERESTART;
} else {
/*
* ensure that we pmap_enter page R/O since
* needs_copy is still true
*/
flt->enter_prot &= ~PROT_WRITE;
}
}
/*
* identify the players
*/
amap = ufi->entry->aref.ar_amap; /* upper layer */
uobj = ufi->entry->object.uvm_obj; /* lower layer */
/*
* check for a case 0 fault. if nothing backing the entry then
* error now.
*/
if (amap == NULL && uobj == NULL) {
uvmfault_unlockmaps(ufi, FALSE);
return EFAULT;
}
/*
* for a case 2B fault waste no time on adjacent pages because
* they are likely already entered.
*/
if (uobj != NULL && amap != NULL &&
(flt->access_type & PROT_WRITE) != 0) {
/* wide fault (!narrow) */
flt->narrow = TRUE;
}
/*
* establish range of interest based on advice from mapper
* and then clip to fit map entry. note that we only want
* to do this the first time through the fault. if we
* ReFault we will disable this by setting "narrow" to true.
*/
if (flt->narrow == FALSE) {
/* wide fault (!narrow) */
nback = min(uvmadvice[ufi->entry->advice].nback,
(ufi->orig_rvaddr - ufi->entry->start) >> PAGE_SHIFT);
flt->startva = ufi->orig_rvaddr - ((vsize_t)nback << PAGE_SHIFT);
nforw = min(uvmadvice[ufi->entry->advice].nforw,
((ufi->entry->end - ufi->orig_rvaddr) >> PAGE_SHIFT) - 1);
/*
* note: "-1" because we don't want to count the
* faulting page as forw
*/
flt->npages = nback + nforw + 1;
flt->centeridx = nback;
flt->narrow = TRUE; /* ensure only once per-fault */
} else {
/* narrow fault! */
nback = nforw = 0;
flt->startva = ufi->orig_rvaddr;
flt->npages = 1;
flt->centeridx = 0;
}
/*
* if we've got an amap then lock it and extract current anons.
*/
if (amap) {
amap_lock(amap);
amap_lookups(&ufi->entry->aref,
flt->startva - ufi->entry->start, *ranons, flt->npages);
} else {
*ranons = NULL; /* to be safe */
}
/*
* for MADV_SEQUENTIAL mappings we want to deactivate the back pages
* now and then forget about them (for the rest of the fault).
*/
if (ufi->entry->advice == MADV_SEQUENTIAL && nback != 0) {
/* flush back-page anons? */
if (amap)
uvmfault_anonflush(*ranons, nback);
/*
* flush object?
*/
if (uobj) {
voff_t uoff;
uoff = (flt->startva - ufi->entry->start) + ufi->entry->offset;
rw_enter(uobj->vmobjlock, RW_WRITE);
(void) uobj->pgops->pgo_flush(uobj, uoff, uoff +
((vsize_t)nback << PAGE_SHIFT), PGO_DEACTIVATE);
rw_exit(uobj->vmobjlock);
}
/* now forget about the backpages */
if (amap)
*ranons += nback;
flt->startva += ((vsize_t)nback << PAGE_SHIFT);
flt->npages -= nback;
flt->centeridx = 0;
}
return 0;
}
/*
* uvm_fault_upper_lookup: look up existing h/w mapping and amap.
*
* iterate range of interest:
* 1. check if h/w mapping exists. if yes, we don't care
* 2. check if anon exists. if not, page is lower.
* 3. if anon exists, enter h/w mapping for neighbors.
*
* => called with amap locked (if exists).
*/
boolean_t
uvm_fault_upper_lookup(struct uvm_faultinfo *ufi,
const struct uvm_faultctx *flt, struct vm_anon **anons,
struct vm_page **pages)
{
struct vm_amap *amap = ufi->entry->aref.ar_amap;
struct vm_anon *anon;
boolean_t shadowed;
vaddr_t currva;
paddr_t pa;
int lcv;
/* locked: maps(read), amap(if there) */
KASSERT(amap == NULL ||
rw_write_held(amap->am_lock));
/*
* map in the backpages and frontpages we found in the amap in hopes
* of preventing future faults. we also init the pages[] array as
* we go.
*/
currva = flt->startva;
shadowed = FALSE;
for (lcv = 0; lcv < flt->npages; lcv++, currva += PAGE_SIZE) {
/*
* dont play with VAs that are already mapped
* except for center)
*/
if (lcv != flt->centeridx &&
pmap_extract(ufi->orig_map->pmap, currva, &pa)) {
pages[lcv] = PGO_DONTCARE;
continue;
}
/*
* unmapped or center page. check if any anon at this level.
*/
if (amap == NULL || anons[lcv] == NULL) {
pages[lcv] = NULL;
continue;
}
/*
* check for present page and map if possible.
*/
pages[lcv] = PGO_DONTCARE;
if (lcv == flt->centeridx) { /* save center for later! */
shadowed = TRUE;
continue;
}
anon = anons[lcv];
KASSERT(anon->an_lock == amap->am_lock);
if (anon->an_page &&
(anon->an_page->pg_flags & (PG_RELEASED|PG_BUSY)) == 0) {
uvm_lock_pageq();
uvm_pageactivate(anon->an_page); /* reactivate */
uvm_unlock_pageq();
counters_inc(uvmexp_counters, flt_namap);
/*
* Since this isn't the page that's actually faulting,
* ignore pmap_enter() failures; it's not critical
* that we enter these right now.
*/
(void) pmap_enter(ufi->orig_map->pmap, currva,
VM_PAGE_TO_PHYS(anon->an_page) | flt->pa_flags,
(anon->an_ref > 1) ?
(flt->enter_prot & ~PROT_WRITE) : flt->enter_prot,
PMAP_CANFAIL |
(VM_MAPENT_ISWIRED(ufi->entry) ? PMAP_WIRED : 0));
}
}
if (flt->npages > 1)
pmap_update(ufi->orig_map->pmap);
return shadowed;
}
/*
* uvm_fault_upper: handle upper fault.
*
* 1. acquire anon lock.
* 2. get anon. let uvmfault_anonget do the dirty work.
* 3. if COW, promote data to new anon
* 4. enter h/w mapping
*/
int
uvm_fault_upper(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_anon **anons, vm_fault_t fault_type)
{
struct vm_amap *amap = ufi->entry->aref.ar_amap;
struct vm_anon *oanon, *anon = anons[flt->centeridx];
struct vm_page *pg = NULL;
int error, ret;
/* locked: maps(read), amap, anon */
KASSERT(rw_write_held(amap->am_lock));
KASSERT(anon->an_lock == amap->am_lock);
/*
* no matter if we have case 1A or case 1B we are going to need to
* have the anon's memory resident. ensure that now.
*/
/*
* let uvmfault_anonget do the dirty work.
* if it fails (!OK) it will unlock everything for us.
* if it succeeds, locks are still valid and locked.
* also, if it is OK, then the anon's page is on the queues.
* if the page is on loan from a uvm_object, then anonget will
* lock that object for us if it does not fail.
*/
error = uvmfault_anonget(ufi, amap, anon);
switch (error) {
case VM_PAGER_OK:
break;
case VM_PAGER_REFAULT:
return ERESTART;
case VM_PAGER_ERROR:
/*
* An error occurred while trying to bring in the
* page -- this is the only error we return right
* now.
*/
return EACCES; /* XXX */
default:
#ifdef DIAGNOSTIC
panic("uvm_fault: uvmfault_anonget -> %d", error);
#else
return EACCES;
#endif
}
KASSERT(rw_write_held(amap->am_lock));
KASSERT(anon->an_lock == amap->am_lock);
/*
* if we are case 1B then we will need to allocate a new blank
* anon to transfer the data into. note that we have a lock
* on anon, so no one can busy or release the page until we are done.
* also note that the ref count can't drop to zero here because
* it is > 1 and we are only dropping one ref.
*
* in the (hopefully very rare) case that we are out of RAM we
* will unlock, wait for more RAM, and refault.
*
* if we are out of anon VM we wait for RAM to become available.
*/
if ((flt->access_type & PROT_WRITE) != 0 && anon->an_ref > 1) {
counters_inc(uvmexp_counters, flt_acow);
oanon = anon; /* oanon = old */
anon = uvm_analloc();
if (anon) {
anon->an_lock = amap->am_lock;
pg = uvm_pagealloc(NULL, 0, anon, 0);
}
/* check for out of RAM */
if (anon == NULL || pg == NULL) {
uvmfault_unlockall(ufi, amap, NULL);
if (anon == NULL)
counters_inc(uvmexp_counters, flt_noanon);
else {
anon->an_lock = NULL;
anon->an_ref--;
uvm_anfree(anon);
counters_inc(uvmexp_counters, flt_noram);
}
if (uvm_swapisfull())
return ENOMEM;
/* out of RAM, wait for more */
if (anon == NULL)
uvm_anwait();
else
uvm_wait("flt_noram3");
return ERESTART;
}
/* got all resources, replace anon with nanon */
uvm_pagecopy(oanon->an_page, pg); /* pg now !PG_CLEAN */
/* un-busy! new page */
atomic_clearbits_int(&pg->pg_flags, PG_BUSY|PG_FAKE);
UVM_PAGE_OWN(pg, NULL);
ret = amap_add(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start, anon, 1);
KASSERT(ret == 0);
/* deref: can not drop to zero here by defn! */
oanon->an_ref--;
#if defined(MULTIPROCESSOR) && !defined(__HAVE_PMAP_MPSAFE_ENTER_COW)
/*
* If there are multiple threads, either uvm or the
* pmap has to make sure no threads see the old RO
* mapping once any have seen the new RW mapping.
* uvm does it by inserting the new mapping RO and
* letting it fault again.
* This is only a problem on MP systems.
*/
if (P_HASSIBLING(curproc)) {
flt->enter_prot &= ~PROT_WRITE;
flt->access_type &= ~PROT_WRITE;
}
#endif
/*
* note: anon is _not_ locked, but we have the sole references
* to in from amap.
* thus, no one can get at it until we are done with it.
*/
} else {
counters_inc(uvmexp_counters, flt_anon);
oanon = anon;
pg = anon->an_page;
if (anon->an_ref > 1) /* disallow writes to ref > 1 anons */
flt->enter_prot = flt->enter_prot & ~PROT_WRITE;
}
/*
* now map the page in .
*/
if (pmap_enter(ufi->orig_map->pmap, ufi->orig_rvaddr,
VM_PAGE_TO_PHYS(pg) | flt->pa_flags, flt->enter_prot,
flt->access_type | PMAP_CANFAIL | (flt->wired ? PMAP_WIRED : 0)) != 0) {
/*
* No need to undo what we did; we can simply think of
* this as the pmap throwing away the mapping information.
*
* We do, however, have to go through the ReFault path,
* as the map may change while we're asleep.
*/
uvmfault_unlockall(ufi, amap, NULL);
if (uvm_swapisfull()) {
/* XXX instrumentation */
return ENOMEM;
}
/* XXX instrumentation */
uvm_wait("flt_pmfail1");
return ERESTART;
}
/*
* ... update the page queues.
*/
uvm_lock_pageq();
if (fault_type == VM_FAULT_WIRE) {
uvm_pagewire(pg);
/*
* since the now-wired page cannot be paged out,
* release its swap resources for others to use.
* since an anon with no swap cannot be PG_CLEAN,
* clear its clean flag now.
*/
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
uvm_anon_dropswap(anon);
} else {
/* activate it */
uvm_pageactivate(pg);
}
uvm_unlock_pageq();
/*
* done case 1! finish up by unlocking everything and returning success
*/
uvmfault_unlockall(ufi, amap, NULL);
pmap_update(ufi->orig_map->pmap);
return 0;
}
/*
* uvm_fault_lower_lookup: look up on-memory uobj pages.
*
* 1. get on-memory pages.
* 2. if failed, give up (get only center page later).
* 3. if succeeded, enter h/w mapping of neighbor pages.
*/
struct vm_page *
uvm_fault_lower_lookup(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct vm_page **pages)
{
struct uvm_object *uobj = ufi->entry->object.uvm_obj;
struct vm_page *uobjpage = NULL;
int lcv, gotpages;
vaddr_t currva;
rw_enter(uobj->vmobjlock, RW_WRITE);
counters_inc(uvmexp_counters, flt_lget);
gotpages = flt->npages;
(void) uobj->pgops->pgo_get(uobj,
ufi->entry->offset + (flt->startva - ufi->entry->start),
pages, &gotpages, flt->centeridx,
flt->access_type & MASK(ufi->entry), ufi->entry->advice,
PGO_LOCKED);
/*
* check for pages to map, if we got any
*/
if (gotpages == 0) {
return NULL;
}
currva = flt->startva;
for (lcv = 0; lcv < flt->npages; lcv++, currva += PAGE_SIZE) {
if (pages[lcv] == NULL ||
pages[lcv] == PGO_DONTCARE)
continue;
KASSERT((pages[lcv]->pg_flags & PG_RELEASED) == 0);
/*
* if center page is resident and not
* PG_BUSY, then pgo_get made it PG_BUSY
* for us and gave us a handle to it.
* remember this page as "uobjpage."
* (for later use).
*/
if (lcv == flt->centeridx) {
uobjpage = pages[lcv];
continue;
}
/*
* note: calling pgo_get with locked data
* structures returns us pages which are
* neither busy nor released, so we don't
* need to check for this. we can just
* directly enter the page (after moving it
* to the head of the active queue [useful?]).
*/
uvm_lock_pageq();
uvm_pageactivate(pages[lcv]); /* reactivate */
uvm_unlock_pageq();
counters_inc(uvmexp_counters, flt_nomap);
/*
* Since this page isn't the page that's
* actually faulting, ignore pmap_enter()
* failures; it's not critical that we
* enter these right now.
*/
(void) pmap_enter(ufi->orig_map->pmap, currva,
VM_PAGE_TO_PHYS(pages[lcv]) | flt->pa_flags,
flt->enter_prot & MASK(ufi->entry),
PMAP_CANFAIL |
(flt->wired ? PMAP_WIRED : 0));
/*
* NOTE: page can't be PG_WANTED because
* we've held the lock the whole time
* we've had the handle.
*/
atomic_clearbits_int(&pages[lcv]->pg_flags, PG_BUSY);
UVM_PAGE_OWN(pages[lcv], NULL);
}
pmap_update(ufi->orig_map->pmap);
return uobjpage;
}
/*
* uvm_fault_lower: handle lower fault.
*
*/
int
uvm_fault_lower(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_page **pages, vm_fault_t fault_type)
{
struct vm_amap *amap = ufi->entry->aref.ar_amap;
struct uvm_object *uobj = ufi->entry->object.uvm_obj;
boolean_t promote, locked;
int result;
struct vm_page *uobjpage, *pg = NULL;
struct vm_anon *anon = NULL;
voff_t uoff;
/*
* now, if the desired page is not shadowed by the amap and we have
* a backing object that does not have a special fault routine, then
* we ask (with pgo_get) the object for resident pages that we care
* about and attempt to map them in. we do not let pgo_get block
* (PGO_LOCKED).
*/
if (uobj == NULL) {
/* zero fill; don't care neighbor pages */
uobjpage = NULL;
} else {
uobjpage = uvm_fault_lower_lookup(ufi, flt, pages);
}
/*
* note that at this point we are done with any front or back pages.
* we are now going to focus on the center page (i.e. the one we've
* faulted on). if we have faulted on the bottom (uobj)
* layer [i.e. case 2] and the page was both present and available,
* then we've got a pointer to it as "uobjpage" and we've already
* made it BUSY.
*/
/*
* locked:
*/
KASSERT(amap == NULL ||
rw_write_held(amap->am_lock));
KASSERT(uobj == NULL ||
rw_write_held(uobj->vmobjlock));
/*
* note that uobjpage can not be PGO_DONTCARE at this point. we now
* set uobjpage to PGO_DONTCARE if we are doing a zero fill. if we
* have a backing object, check and see if we are going to promote
* the data up to an anon during the fault.
*/
if (uobj == NULL) {
uobjpage = PGO_DONTCARE;
promote = TRUE; /* always need anon here */
} else {
KASSERT(uobjpage != PGO_DONTCARE);
promote = (flt->access_type & PROT_WRITE) &&
UVM_ET_ISCOPYONWRITE(ufi->entry);
}
/*
* if uobjpage is not null then we do not need to do I/O to get the
* uobjpage.
*
* if uobjpage is null, then we need to ask the pager to
* get the data for us. once we have the data, we need to reverify
* the state the world. we are currently not holding any resources.
*/
if (uobjpage) {
/* update rusage counters */
curproc->p_ru.ru_minflt++;
} else {
int gotpages;
/* update rusage counters */
curproc->p_ru.ru_majflt++;
uvmfault_unlockall(ufi, amap, NULL);
counters_inc(uvmexp_counters, flt_get);
gotpages = 1;
uoff = (ufi->orig_rvaddr - ufi->entry->start) + ufi->entry->offset;
result = uobj->pgops->pgo_get(uobj, uoff, &uobjpage, &gotpages,
0, flt->access_type & MASK(ufi->entry), ufi->entry->advice,
PGO_SYNCIO);
/*
* recover from I/O
*/
if (result != VM_PAGER_OK) {
KASSERT(result != VM_PAGER_PEND);
if (result == VM_PAGER_AGAIN) {
tsleep_nsec(&nowake, PVM, "fltagain2",
MSEC_TO_NSEC(5));
return ERESTART;
}
if (!UVM_ET_ISNOFAULT(ufi->entry))
return (EIO);
uobjpage = PGO_DONTCARE;
uobj = NULL;
promote = TRUE;
}
/* re-verify the state of the world. */
locked = uvmfault_relock(ufi);
if (locked && amap != NULL)
amap_lock(amap);
/* might be changed */
if (uobjpage != PGO_DONTCARE) {
uobj = uobjpage->uobject;
rw_enter(uobj->vmobjlock, RW_WRITE);
}
/*
* Re-verify that amap slot is still free. if there is
* a problem, we clean up.
*/
if (locked && amap && amap_lookup(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start)) {
if (locked)
uvmfault_unlockall(ufi, amap, NULL);
locked = FALSE;
}
/* didn't get the lock? release the page and retry. */
if (locked == FALSE && uobjpage != PGO_DONTCARE) {
uvm_lock_pageq();
/* make sure it is in queues */
uvm_pageactivate(uobjpage);
uvm_unlock_pageq();
if (uobjpage->pg_flags & PG_WANTED)
/* still holding object lock */
wakeup(uobjpage);
atomic_clearbits_int(&uobjpage->pg_flags,
PG_BUSY|PG_WANTED);
UVM_PAGE_OWN(uobjpage, NULL);
}
if (locked == FALSE) {
if (uobjpage != PGO_DONTCARE)
rw_exit(uobj->vmobjlock);
return ERESTART;
}
/*
* we have the data in uobjpage which is PG_BUSY
*/
}
/*
* notes:
* - at this point uobjpage can not be NULL
* - at this point uobjpage could be PG_WANTED (handle later)
*/
if (promote == FALSE) {
/*
* we are not promoting. if the mapping is COW ensure that we
* don't give more access than we should (e.g. when doing a read
* fault on a COPYONWRITE mapping we want to map the COW page in
* R/O even though the entry protection could be R/W).
*
* set "pg" to the page we want to map in (uobjpage, usually)
*/
counters_inc(uvmexp_counters, flt_obj);
if (UVM_ET_ISCOPYONWRITE(ufi->entry))
flt->enter_prot &= ~PROT_WRITE;
pg = uobjpage; /* map in the actual object */
/* assert(uobjpage != PGO_DONTCARE) */
/*
* we are faulting directly on the page.
*/
} else {
/*
* if we are going to promote the data to an anon we
* allocate a blank anon here and plug it into our amap.
*/
#ifdef DIAGNOSTIC
if (amap == NULL)
panic("uvm_fault: want to promote data, but no anon");
#endif
anon = uvm_analloc();
if (anon) {
/*
* In `Fill in data...' below, if
* uobjpage == PGO_DONTCARE, we want
* a zero'd, dirty page, so have
* uvm_pagealloc() do that for us.
*/
anon->an_lock = amap->am_lock;
pg = uvm_pagealloc(NULL, 0, anon,
(uobjpage == PGO_DONTCARE) ? UVM_PGA_ZERO : 0);
}
/*
* out of memory resources?
*/
if (anon == NULL || pg == NULL) {
/*
* arg! must unbusy our page and fail or sleep.
*/
if (uobjpage != PGO_DONTCARE) {
uvm_lock_pageq();
uvm_pageactivate(uobjpage);
uvm_unlock_pageq();
if (uobjpage->pg_flags & PG_WANTED)
wakeup(uobjpage);
atomic_clearbits_int(&uobjpage->pg_flags,
PG_BUSY|PG_WANTED);
UVM_PAGE_OWN(uobjpage, NULL);
}
/* unlock and fail ... */
uvmfault_unlockall(ufi, amap, uobj);
if (anon == NULL)
counters_inc(uvmexp_counters, flt_noanon);
else {
anon->an_lock = NULL;
anon->an_ref--;
uvm_anfree(anon);
counters_inc(uvmexp_counters, flt_noram);
}
if (uvm_swapisfull())
return (ENOMEM);
/* out of RAM, wait for more */
if (anon == NULL)
uvm_anwait();
else
uvm_wait("flt_noram5");
return ERESTART;
}
/*
* fill in the data
*/
if (uobjpage != PGO_DONTCARE) {
counters_inc(uvmexp_counters, flt_prcopy);
/* copy page [pg now dirty] */
uvm_pagecopy(uobjpage, pg);
/*
* promote to shared amap? make sure all sharing
* procs see it
*/
if ((amap_flags(amap) & AMAP_SHARED) != 0) {
pmap_page_protect(uobjpage, PROT_NONE);
}
/* dispose of uobjpage. drop handle to uobj as well. */
if (uobjpage->pg_flags & PG_WANTED)
wakeup(uobjpage);
atomic_clearbits_int(&uobjpage->pg_flags,
PG_BUSY|PG_WANTED);
UVM_PAGE_OWN(uobjpage, NULL);
uvm_lock_pageq();
uvm_pageactivate(uobjpage);
uvm_unlock_pageq();
rw_exit(uobj->vmobjlock);
uobj = NULL;
} else {
counters_inc(uvmexp_counters, flt_przero);
/*
* Page is zero'd and marked dirty by uvm_pagealloc()
* above.
*/
}
if (amap_add(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start, anon, 0)) {
uvmfault_unlockall(ufi, amap, uobj);
uvm_anfree(anon);
counters_inc(uvmexp_counters, flt_noamap);
if (uvm_swapisfull())
return (ENOMEM);
amap_populate(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start);
return ERESTART;
}
}
/* note: pg is either the uobjpage or the new page in the new anon */
/*
* all resources are present. we can now map it in and free our
* resources.
*/
if (amap == NULL)
KASSERT(anon == NULL);
else {
KASSERT(rw_write_held(amap->am_lock));
KASSERT(anon == NULL || anon->an_lock == amap->am_lock);
}
if (pmap_enter(ufi->orig_map->pmap, ufi->orig_rvaddr,
VM_PAGE_TO_PHYS(pg) | flt->pa_flags, flt->enter_prot,
flt->access_type | PMAP_CANFAIL | (flt->wired ? PMAP_WIRED : 0)) != 0) {
/*
* No need to undo what we did; we can simply think of
* this as the pmap throwing away the mapping information.
*
* We do, however, have to go through the ReFault path,
* as the map may change while we're asleep.
*/
if (pg->pg_flags & PG_WANTED)
wakeup(pg);
atomic_clearbits_int(&pg->pg_flags, PG_BUSY|PG_FAKE|PG_WANTED);
UVM_PAGE_OWN(pg, NULL);
uvmfault_unlockall(ufi, amap, uobj);
if (uvm_swapisfull()) {
/* XXX instrumentation */
return (ENOMEM);
}
/* XXX instrumentation */
uvm_wait("flt_pmfail2");
return ERESTART;
}
if (fault_type == VM_FAULT_WIRE) {
uvm_lock_pageq();
uvm_pagewire(pg);
uvm_unlock_pageq();
if (pg->pg_flags & PQ_AOBJ) {
/*
* since the now-wired page cannot be paged out,
* release its swap resources for others to use.
* since an aobj page with no swap cannot be clean,
* mark it dirty now.
*
* use pg->uobject here. if the page is from a
* tmpfs vnode, the pages are backed by its UAO and
* not the vnode.
*/
KASSERT(uobj != NULL);
KASSERT(uobj->vmobjlock == pg->uobject->vmobjlock);
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
uao_dropswap(uobj, pg->offset >> PAGE_SHIFT);
}
} else {
/* activate it */
uvm_lock_pageq();
uvm_pageactivate(pg);
uvm_unlock_pageq();
}
if (pg->pg_flags & PG_WANTED)
wakeup(pg);
atomic_clearbits_int(&pg->pg_flags, PG_BUSY|PG_FAKE|PG_WANTED);
UVM_PAGE_OWN(pg, NULL);
uvmfault_unlockall(ufi, amap, uobj);
pmap_update(ufi->orig_map->pmap);
return (0);
}
/*
* uvm_fault_wire: wire down a range of virtual addresses in a map.
*
* => map may be read-locked by caller, but MUST NOT be write-locked.
* => if map is read-locked, any operations which may cause map to
* be write-locked in uvm_fault() must be taken care of by
* the caller. See uvm_map_pageable().
*/
int
uvm_fault_wire(vm_map_t map, vaddr_t start, vaddr_t end, vm_prot_t access_type)
{
vaddr_t va;
int rv;
/*
* now fault it in a page at a time. if the fault fails then we have
* to undo what we have done. note that in uvm_fault PROT_NONE
* is replaced with the max protection if fault_type is VM_FAULT_WIRE.
*/
for (va = start ; va < end ; va += PAGE_SIZE) {
rv = uvm_fault(map, va, VM_FAULT_WIRE, access_type);
if (rv) {
if (va != start) {
uvm_fault_unwire(map, start, va);
}
return (rv);
}
}
return (0);
}
/*
* uvm_fault_unwire(): unwire range of virtual space.
*/
void
uvm_fault_unwire(vm_map_t map, vaddr_t start, vaddr_t end)
{
vm_map_lock_read(map);
uvm_fault_unwire_locked(map, start, end);
vm_map_unlock_read(map);
}
/*
* uvm_fault_unwire_locked(): the guts of uvm_fault_unwire().
*
* => map must be at least read-locked.
*/
void
uvm_fault_unwire_locked(vm_map_t map, vaddr_t start, vaddr_t end)
{
vm_map_entry_t entry, oentry = NULL, next;
pmap_t pmap = vm_map_pmap(map);
vaddr_t va;
paddr_t pa;
struct vm_page *pg;
KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
/*
* we assume that the area we are unwiring has actually been wired
* in the first place. this means that we should be able to extract
* the PAs from the pmap.
*/
/*
* find the beginning map entry for the region.
*/
KASSERT(start >= vm_map_min(map) && end <= vm_map_max(map));
if (uvm_map_lookup_entry(map, start, &entry) == FALSE)
panic("uvm_fault_unwire_locked: address not in map");
for (va = start; va < end ; va += PAGE_SIZE) {
if (pmap_extract(pmap, va, &pa) == FALSE)
continue;
/*
* find the map entry for the current address.
*/
KASSERT(va >= entry->start);
while (entry && va >= entry->end) {
next = RBT_NEXT(uvm_map_addr, entry);
entry = next;
}
if (entry == NULL)
return;
if (va < entry->start)
continue;
/*
* lock it.
*/
if (entry != oentry) {
if (oentry != NULL) {
uvm_map_unlock_entry(oentry);
}
uvm_map_lock_entry(entry);
oentry = entry;
}
/*
* if the entry is no longer wired, tell the pmap.
*/
if (VM_MAPENT_ISWIRED(entry) == 0)
pmap_unwire(pmap, va);
pg = PHYS_TO_VM_PAGE(pa);
if (pg) {
uvm_lock_pageq();
uvm_pageunwire(pg);
uvm_unlock_pageq();
}
}
if (oentry != NULL) {
uvm_map_unlock_entry(oentry);
}
}
/*
* uvmfault_unlockmaps: unlock the maps
*/
void
uvmfault_unlockmaps(struct uvm_faultinfo *ufi, boolean_t write_locked)
{
/*
* ufi can be NULL when this isn't really a fault,
* but merely paging in anon data.
*/
if (ufi == NULL) {
return;
}
uvmfault_update_stats(ufi);
if (write_locked) {
vm_map_unlock(ufi->map);
} else {
vm_map_unlock_read(ufi->map);
}
}
/*
* uvmfault_unlockall: unlock everything passed in.
*
* => maps must be read-locked (not write-locked).
*/
void
uvmfault_unlockall(struct uvm_faultinfo *ufi, struct vm_amap *amap,
struct uvm_object *uobj)
{
if (uobj)
rw_exit(uobj->vmobjlock);
if (amap != NULL)
amap_unlock(amap);
uvmfault_unlockmaps(ufi, FALSE);
}
/*
* uvmfault_lookup: lookup a virtual address in a map
*
* => caller must provide a uvm_faultinfo structure with the IN
* params properly filled in
* => we will lookup the map entry (handling submaps) as we go
* => if the lookup is a success we will return with the maps locked
* => if "write_lock" is TRUE, we write_lock the map, otherwise we only
* get a read lock.
* => note that submaps can only appear in the kernel and they are
* required to use the same virtual addresses as the map they
* are referenced by (thus address translation between the main
* map and the submap is unnecessary).
*/
boolean_t
uvmfault_lookup(struct uvm_faultinfo *ufi, boolean_t write_lock)
{
vm_map_t tmpmap;
/*
* init ufi values for lookup.
*/
ufi->map = ufi->orig_map;
ufi->size = ufi->orig_size;
/*
* keep going down levels until we are done. note that there can
* only be two levels so we won't loop very long.
*/
while (1) {
if (ufi->orig_rvaddr < ufi->map->min_offset ||
ufi->orig_rvaddr >= ufi->map->max_offset)
return FALSE;
/* lock map */
if (write_lock) {
vm_map_lock(ufi->map);
} else {
vm_map_lock_read(ufi->map);
}
/* lookup */
if (!uvm_map_lookup_entry(ufi->map, ufi->orig_rvaddr,
&ufi->entry)) {
uvmfault_unlockmaps(ufi, write_lock);
return FALSE;
}
/* reduce size if necessary */
if (ufi->entry->end - ufi->orig_rvaddr < ufi->size)
ufi->size = ufi->entry->end - ufi->orig_rvaddr;
/*
* submap? replace map with the submap and lookup again.
* note: VAs in submaps must match VAs in main map.
*/
if (UVM_ET_ISSUBMAP(ufi->entry)) {
tmpmap = ufi->entry->object.sub_map;
uvmfault_unlockmaps(ufi, write_lock);
ufi->map = tmpmap;
continue;
}
/*
* got it!
*/
ufi->mapv = ufi->map->timestamp;
return TRUE;
} /* while loop */
/*NOTREACHED*/
}
/*
* uvmfault_relock: attempt to relock the same version of the map
*
* => fault data structures should be unlocked before calling.
* => if a success (TRUE) maps will be locked after call.
*/
boolean_t
uvmfault_relock(struct uvm_faultinfo *ufi)
{
/*
* ufi can be NULL when this isn't really a fault,
* but merely paging in anon data.
*/
if (ufi == NULL) {
return TRUE;
}
counters_inc(uvmexp_counters, flt_relck);
/*
* relock map. fail if version mismatch (in which case nothing
* gets locked).
*/
vm_map_lock_read(ufi->map);
if (ufi->mapv != ufi->map->timestamp) {
vm_map_unlock_read(ufi->map);
return FALSE;
}
counters_inc(uvmexp_counters, flt_relckok);
return TRUE; /* got it! */
}
25
25
25
154
93
88
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/* $OpenBSD: vfs_default.c,v 1.51 2022/04/27 14:52:25 claudio Exp $ */
/*
* Portions of this code are:
*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <sys/pool.h>
#include <sys/event.h>
#include <sys/specdev.h>
int filt_generic_readwrite(struct knote *, long);
void filt_generic_detach(struct knote *);
/*
* Eliminate all activity associated with the requested vnode
* and with all vnodes aliased to the requested vnode.
*/
int
vop_generic_revoke(void *v)
{
struct vop_revoke_args *ap = v;
struct vnode *vp, *vq;
struct proc *p = curproc;
#ifdef DIAGNOSTIC
if ((ap->a_flags & REVOKEALL) == 0)
panic("vop_generic_revoke");
#endif
vp = ap->a_vp;
while (vp->v_type == VBLK && vp->v_specinfo != NULL &&
vp->v_specmountpoint != NULL) {
struct mount *mp = vp->v_specmountpoint;
/*
* If we have a mount point associated with the vnode, we must
* flush it out now, as to not leave a dangling zombie mount
* point laying around in VFS.
*/
if (!vfs_busy(mp, VB_WRITE|VB_WAIT)) {
dounmount(mp, MNT_FORCE | MNT_DOOMED, p);
break;
}
}
if (vp->v_flag & VALIASED) {
/*
* If a vgone (or vclean) is already in progress,
* wait until it is done and return.
*/
mtx_enter(&vnode_mtx);
if (vp->v_lflag & VXLOCK) {
vp->v_lflag |= VXWANT;
msleep_nsec(vp, &vnode_mtx, PINOD,
"vop_generic_revokeall", INFSLP);
mtx_leave(&vnode_mtx);
return(0);
}
/*
* Ensure that vp will not be vgone'd while we
* are eliminating its aliases.
*/
vp->v_lflag |= VXLOCK;
mtx_leave(&vnode_mtx);
while (vp->v_flag & VALIASED) {
SLIST_FOREACH(vq, vp->v_hashchain, v_specnext) {
if (vq->v_rdev != vp->v_rdev ||
vq->v_type != vp->v_type || vp == vq)
continue;
vgonel(vq, p);
break;
}
}
/*
* Remove the lock so that vgone below will
* really eliminate the vnode after which time
* vgone will awaken any sleepers.
*/
mtx_enter(&vnode_mtx);
vp->v_lflag &= ~VXLOCK;
mtx_leave(&vnode_mtx);
}
vgonel(vp, p);
return (0);
}
int
vop_generic_badop(void *v)
{
panic("%s", __func__);
}
int
vop_generic_bmap(void *v)
{
struct vop_bmap_args *ap = v;
if (ap->a_vpp)
*ap->a_vpp = ap->a_vp;
if (ap->a_bnp)
*ap->a_bnp = ap->a_bn;
if (ap->a_runp)
*ap->a_runp = 0;
return (0);
}
int
vop_generic_bwrite(void *v)
{
struct vop_bwrite_args *ap = v;
return (bwrite(ap->a_bp));
}
int
vop_generic_abortop(void *v)
{
struct vop_abortop_args *ap = v;
if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
pool_put(&namei_pool, ap->a_cnp->cn_pnbuf);
return (0);
}
const struct filterops generic_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_generic_detach,
.f_event = filt_generic_readwrite,
};
int
vop_generic_kqfilter(void *v)
{
struct vop_kqfilter_args *ap = v;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
case EVFILT_WRITE:
kn->kn_fop = &generic_filtops;
break;
default:
return (EINVAL);
}
return (0);
}
/* Trivial lookup routine that always fails. */
int
vop_generic_lookup(void *v)
{
struct vop_lookup_args *ap = v;
*ap->a_vpp = NULL;
return (ENOTDIR);
}
void
filt_generic_detach(struct knote *kn)
{
}
int
filt_generic_readwrite(struct knote *kn, long hint)
{
/*
* filesystem is gone, so set the EOF flag and schedule
* the knote for deletion.
*/
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
kn->kn_data = 0;
return (1);
}
4
3
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
/* $OpenBSD: mpls_input.c,v 1.78 2021/07/22 11:07:17 mvs Exp $ */
/*
* Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif /* INET6 */
#include <netmpls/mpls.h>
#ifdef MPLS_DEBUG
#define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
#define MPLS_TTL_GET(l) (ntohl((l) & MPLS_TTL_MASK))
#endif
struct mbuf *mpls_do_error(struct mbuf *, int, int, int);
void mpls_input_local(struct rtentry *, struct mbuf *);
void
mpls_input(struct ifnet *ifp, struct mbuf *m)
{
struct sockaddr_mpls *smpls;
struct sockaddr_mpls sa_mpls;
struct shim_hdr *shim;
struct rtentry *rt;
struct rt_mpls *rt_mpls;
uint8_t ttl;
int hasbos;
if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
m_freem(m);
return;
}
/* drop all broadcast and multicast packets */
if (m->m_flags & (M_BCAST | M_MCAST)) {
m_freem(m);
return;
}
if (m->m_len < sizeof(*shim)) {
m = m_pullup(m, sizeof(*shim));
if (m == NULL)
return;
}
shim = mtod(m, struct shim_hdr *);
#ifdef MPLS_DEBUG
printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n",
ifp->if_xname, MPLS_LABEL_GET(shim->shim_label),
MPLS_TTL_GET(shim->shim_label),
MPLS_BOS_ISSET(shim->shim_label));
#endif
/* check and decrement TTL */
ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
if (ttl <= 1) {
/* TTL exceeded */
m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0);
if (m == NULL)
return;
shim = mtod(m, struct shim_hdr *);
ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
} else
ttl--;
hasbos = MPLS_BOS_ISSET(shim->shim_label);
bzero(&sa_mpls, sizeof(sa_mpls));
smpls = &sa_mpls;
smpls->smpls_family = AF_MPLS;
smpls->smpls_len = sizeof(*smpls);
smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) {
m = mpls_shim_pop(m);
if (m == NULL)
return;
if (!hasbos) {
/*
* RFC 4182 relaxes the position of the
* explicit NULL labels. They no longer need
* to be at the beginning of the stack.
* In this case the label is ignored and the decision
* is made based on the lower one.
*/
shim = mtod(m, struct shim_hdr *);
smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
hasbos = MPLS_BOS_ISSET(shim->shim_label);
} else {
switch (ntohl(smpls->smpls_label)) {
case MPLS_LABEL_IPV4NULL:
do_v4:
if (mpls_mapttl_ip) {
m = mpls_ip_adjttl(m, ttl);
if (m == NULL)
return;
}
ipv4_input(ifp, m);
return;
#ifdef INET6
case MPLS_LABEL_IPV6NULL:
do_v6:
if (mpls_mapttl_ip6) {
m = mpls_ip6_adjttl(m, ttl);
if (m == NULL)
return;
}
ipv6_input(ifp, m);
return;
#endif /* INET6 */
case MPLS_LABEL_IMPLNULL:
if (m->m_len < sizeof(u_char) &&
(m = m_pullup(m, sizeof(u_char))) == NULL)
return;
switch (*mtod(m, u_char *) >> 4) {
case IPVERSION:
goto do_v4;
#ifdef INET6
case IPV6_VERSION >> 4:
goto do_v6;
#endif
default:
m_freem(m);
return;
}
default:
/* Other cases are not handled for now */
m_freem(m);
return;
}
}
}
ifp = NULL;
rt = rtalloc(smplstosa(smpls), RT_RESOLVE, m->m_pkthdr.ph_rtableid);
if (!rtisvalid(rt)) {
/* no entry for this label */
#ifdef MPLS_DEBUG
printf("MPLS_DEBUG: label not found\n");
#endif
m_freem(m);
goto done;
}
rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
#ifdef MPLS_DEBUG
printf("MPLS_DEBUG: no MPLS information attached\n");
#endif
m_freem(m);
goto done;
}
switch (rt_mpls->mpls_operation) {
case MPLS_OP_POP:
if (ISSET(rt->rt_flags, RTF_LOCAL)) {
mpls_input_local(rt, m);
goto done;
}
m = mpls_shim_pop(m);
if (m == NULL)
goto done;
if (!hasbos)
/* just forward to gw */
break;
/* last label popped so decide where to push it to */
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
m_freem(m);
goto done;
}
KASSERT(rt->rt_gateway);
switch(rt->rt_gateway->sa_family) {
case AF_INET:
if ((m = mpls_ip_adjttl(m, ttl)) == NULL)
goto done;
break;
#ifdef INET6
case AF_INET6:
if ((m = mpls_ip6_adjttl(m, ttl)) == NULL)
goto done;
break;
#endif
case AF_LINK:
break;
default:
m_freem(m);
goto done;
}
/* shortcut sending out the packet */
if (!ISSET(ifp->if_xflags, IFXF_MPLS))
(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
else
(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
goto done;
case MPLS_OP_PUSH:
/* this does not make much sense but it does not hurt */
m = mpls_shim_push(m, rt_mpls);
break;
case MPLS_OP_SWAP:
m = mpls_shim_swap(m, rt_mpls);
break;
default:
m_freem(m);
goto done;
}
if (m == NULL)
goto done;
/* refetch label and write back TTL */
shim = mtod(m, struct shim_hdr *);
shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl);
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
m_freem(m);
goto done;
}
#ifdef MPLS_DEBUG
printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n",
ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family,
MPLS_LABEL_GET(smpls->smpls_label),
MPLS_LABEL_GET(rt_mpls->mpls_label));
#endif
/* Output iface is not MPLS-enabled */
if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
#ifdef MPLS_DEBUG
printf("MPLS_DEBUG: interface %s not mpls enabled\n",
ifp->if_xname);
#endif
m_freem(m);
goto done;
}
(*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt);
done:
if_put(ifp);
rtfree(rt);
}
void
mpls_input_local(struct rtentry *rt, struct mbuf *m)
{
struct ifnet *ifp;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
m_freem(m);
return;
}
/* shortcut sending out the packet */
if (!ISSET(ifp->if_xflags, IFXF_MPLS))
(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
else
(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
if_put(ifp);
}
struct mbuf *
mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl)
{
struct ip *ip;
uint16_t old, new;
uint32_t x;
if (m->m_len < sizeof(*ip)) {
m = m_pullup(m, sizeof(*ip));
if (m == NULL)
return (NULL);
}
ip = mtod(m, struct ip *);
old = htons(ip->ip_ttl << 8);
new = htons(ttl << 8);
x = ip->ip_sum + old - new;
ip->ip_ttl = ttl;
/* see pf_cksum_fixup() */
ip->ip_sum = (x) + (x >> 16);
return (m);
}
#ifdef INET6
struct mbuf *
mpls_ip6_adjttl(struct mbuf *m, u_int8_t ttl)
{
struct ip6_hdr *ip6;
if (m->m_len < sizeof(*ip6)) {
m = m_pullup(m, sizeof(*ip6));
if (m == NULL)
return (NULL);
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_hlim = ttl;
return (m);
}
#endif /* INET6 */
struct mbuf *
mpls_do_error(struct mbuf *m, int type, int code, int destmtu)
{
struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];
struct sockaddr_mpls sa_mpls;
struct sockaddr_mpls *smpls;
struct rtentry *rt = NULL;
struct shim_hdr *shim;
struct in_ifaddr *ia;
struct icmp *icp;
struct ip *ip;
int nstk, error;
for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
if (m->m_len < sizeof(*shim) &&
(m = m_pullup(m, sizeof(*shim))) == NULL)
return (NULL);
stack[nstk] = *mtod(m, struct shim_hdr *);
m_adj(m, sizeof(*shim));
if (MPLS_BOS_ISSET(stack[nstk].shim_label))
break;
}
shim = &stack[0];
if (m->m_len < sizeof(u_char) &&
(m = m_pullup(m, sizeof(u_char))) == NULL)
return (NULL);
switch (*mtod(m, u_char *) >> 4) {
case IPVERSION:
if (m->m_len < sizeof(*ip) &&
(m = m_pullup(m, sizeof(*ip))) == NULL)
return (NULL);
m = icmp_do_error(m, type, code, 0, destmtu);
if (m == NULL)
return (NULL);
if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,
(nstk + 1) * sizeof(*shim)))
return (NULL);
/* set ip_src to something usable, based on the MPLS label */
bzero(&sa_mpls, sizeof(sa_mpls));
smpls = &sa_mpls;
smpls->smpls_family = AF_MPLS;
smpls->smpls_len = sizeof(*smpls);
smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
rt = rtalloc(smplstosa(smpls), RT_RESOLVE, 0);
if (!rtisvalid(rt)) {
rtfree(rt);
/* no entry for this label */
m_freem(m);
return (NULL);
}
if (rt->rt_ifa->ifa_addr->sa_family == AF_INET)
ia = ifatoia(rt->rt_ifa);
else {
/* XXX this needs fixing, if the MPLS is on an IP
* less interface we need to find some other IP to
* use as source.
*/
rtfree(rt);
m_freem(m);
return (NULL);
}
/* It is safe to dereference ``ia'' iff ``rt'' is valid. */
error = icmp_reflect(m, NULL, ia);
rtfree(rt);
if (error)
return (NULL);
ip = mtod(m, struct ip *);
/* stuff to fix up which is normally done in ip_output */
ip->ip_v = IPVERSION;
ip->ip_id = htons(ip_randomid());
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, sizeof(*ip));
/* stolen from icmp_send() */
icp = (struct icmp *)(mtod(m, caddr_t) + sizeof(*ip));
icp->icmp_cksum = 0;
icp->icmp_cksum = in4_cksum(m, 0, sizeof(*ip),
ntohs(ip->ip_len) - sizeof(*ip));
break;
#ifdef INET6
case IPV6_VERSION >> 4:
#endif
default:
m_freem(m);
return (NULL);
}
/* add mpls stack back to new packet */
M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);
if (m == NULL)
return (NULL);
m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);
/* change TTL to default */
shim = mtod(m, struct shim_hdr *);
shim->shim_label =
(shim->shim_label & ~MPLS_TTL_MASK) | htonl(mpls_defttl);
return (m);
}
11
10
2
1
1
1
14
11
4
3
2
5
1
1
3
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/* $OpenBSD: mem.c,v 1.35 2021/03/24 14:26:39 bluhm Exp $ */
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1986, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)mem.c 8.3 (Berkeley) 1/12/94
*/
/*
* Memory special file
*/
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/filio.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/ioccom.h>
#include <sys/malloc.h>
#include <sys/memrange.h>
#include <machine/cpu.h>
#include <uvm/uvm_extern.h>
caddr_t zeropage;
extern int start, end, etext;
/* open counter for aperture */
#ifdef APERTURE
static int ap_open_count = 0;
extern int allowaperture;
#define VGA_START 0xA0000
#define BIOS_END 0xFFFFF
#endif
#ifdef MTRR
struct mem_range_softc mem_range_softc;
int mem_ioctl(dev_t, u_long, caddr_t, int, struct proc *);
int mem_range_attr_get(struct mem_range_desc *, int *);
int mem_range_attr_set(struct mem_range_desc *, int *);
#endif
int
mmopen(dev_t dev, int flag, int mode, struct proc *p)
{
extern int allowkmem;
switch (minor(dev)) {
case 0:
case 1:
if (securelevel <= 0 || allowkmem)
break;
return (EPERM);
case 2:
case 12:
break;
#ifdef APERTURE
case 4:
if (suser(p) != 0 || !allowaperture)
return (EPERM);
/* authorize only one simultaneous open() unless
* allowaperture=3 */
if (ap_open_count > 0 && allowaperture < 3)
return (EPERM);
ap_open_count++;
break;
#endif
default:
return (ENXIO);
}
return (0);
}
int
mmclose(dev_t dev, int flag, int mode, struct proc *p)
{
#ifdef APERTURE
if (minor(dev) == 4)
ap_open_count = 0;
#endif
return (0);
}
int
mmrw(dev_t dev, struct uio *uio, int flags)
{
extern vaddr_t kern_end;
vaddr_t v;
size_t c;
struct iovec *iov;
int error = 0;
while (uio->uio_resid > 0 && error == 0) {
iov = uio->uio_iov;
if (iov->iov_len == 0) {
uio->uio_iov++;
uio->uio_iovcnt--;
if (uio->uio_iovcnt < 0)
panic("mmrw");
continue;
}
switch (minor(dev)) {
/* minor device 0 is physical memory */
case 0:
v = PMAP_DIRECT_MAP(uio->uio_offset);
error = uiomove((caddr_t)v, uio->uio_resid, uio);
continue;
/* minor device 1 is kernel memory */
case 1:
v = uio->uio_offset;
c = ulmin(iov->iov_len, MAXPHYS);
if (v >= (vaddr_t)&start && v < kern_end - c) {
if (v < (vaddr_t)&etext - c &&
uio->uio_rw == UIO_WRITE)
return EFAULT;
} else if ((!uvm_kernacc((caddr_t)v, c,
uio->uio_rw == UIO_READ ? B_READ : B_WRITE)) &&
(v < PMAP_DIRECT_BASE || v > PMAP_DIRECT_END - c))
return (EFAULT);
error = uiomove((caddr_t)v, c, uio);
continue;
/* minor device 2 is /dev/null */
case 2:
if (uio->uio_rw == UIO_WRITE)
uio->uio_resid = 0;
return (0);
/* minor device 12 is /dev/zero */
case 12:
if (uio->uio_rw == UIO_WRITE) {
c = iov->iov_len;
break;
}
if (zeropage == NULL)
zeropage =
malloc(PAGE_SIZE, M_TEMP, M_WAITOK|M_ZERO);
c = ulmin(iov->iov_len, PAGE_SIZE);
error = uiomove(zeropage, c, uio);
continue;
default:
return (ENXIO);
}
iov->iov_base += c;
iov->iov_len -= c;
uio->uio_offset += c;
uio->uio_resid -= c;
}
return (error);
}
paddr_t
mmmmap(dev_t dev, off_t off, int prot)
{
struct proc *p = curproc; /* XXX */
switch (minor(dev)) {
/* minor device 0 is physical memory */
case 0:
if (suser(p) != 0 && amd64_pa_used(off))
return -1;
return off;
#ifdef APERTURE
/* minor device 4 is aperture driver */
case 4:
/* Check if a write combining mapping is requested. */
if (off >= MEMRANGE_WC_RANGE)
off = (off - MEMRANGE_WC_RANGE) | PMAP_WC;
switch (allowaperture) {
case 1:
/* Allow mapping of the VGA framebuffer & BIOS only */
if ((off >= VGA_START && off <= BIOS_END) ||
!amd64_pa_used(off))
return off;
else
return -1;
case 2:
case 3:
/* Allow mapping of the whole 1st megabyte
for x86emu */
if (off <= BIOS_END || !amd64_pa_used(off))
return off;
else
return -1;
default:
return -1;
}
#endif
default:
return -1;
}
}
int
mmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
switch (cmd) {
case FIONBIO:
case FIOASYNC:
/* handled by fd layer */
return 0;
}
#ifdef MTRR
switch (minor(dev)) {
case 0:
case 4:
return mem_ioctl(dev, cmd, data, flags, p);
}
#endif
return (ENODEV);
}
#ifdef MTRR
/*
* Operations for changing memory attributes.
*
* This is basically just an ioctl shim for mem_range_attr_get
* and mem_range_attr_set.
*/
int
mem_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
int nd, error = 0;
struct mem_range_op *mo = (struct mem_range_op *)data;
struct mem_range_desc *md;
/* is this for us? */
if ((cmd != MEMRANGE_GET) &&
(cmd != MEMRANGE_SET))
return (ENOTTY);
/* any chance we can handle this? */
if (mem_range_softc.mr_op == NULL)
return (EOPNOTSUPP);
/* do we have any descriptors? */
if (mem_range_softc.mr_ndesc == 0)
return (ENXIO);
switch (cmd) {
case MEMRANGE_GET:
nd = imin(mo->mo_arg[0], mem_range_softc.mr_ndesc);
if (nd > 0) {
md = mallocarray(nd, sizeof(struct mem_range_desc),
M_MEMDESC, M_WAITOK);
error = mem_range_attr_get(md, &nd);
if (!error)
error = copyout(md, mo->mo_desc,
nd * sizeof(struct mem_range_desc));
free(md, M_MEMDESC, nd * sizeof(struct mem_range_desc));
} else {
nd = mem_range_softc.mr_ndesc;
}
mo->mo_arg[0] = nd;
break;
case MEMRANGE_SET:
md = malloc(sizeof(struct mem_range_desc), M_MEMDESC, M_WAITOK);
error = copyin(mo->mo_desc, md, sizeof(struct mem_range_desc));
/* clamp description string */
md->mr_owner[sizeof(md->mr_owner) - 1] = 0;
if (error == 0)
error = mem_range_attr_set(md, &mo->mo_arg[0]);
free(md, M_MEMDESC, sizeof(struct mem_range_desc));
break;
}
return (error);
}
/*
* Implementation-neutral, kernel-callable functions for manipulating
* memory range attributes.
*/
int
mem_range_attr_get(struct mem_range_desc *mrd, int *arg)
{
/* can we handle this? */
if (mem_range_softc.mr_op == NULL)
return (EOPNOTSUPP);
if (*arg == 0) {
*arg = mem_range_softc.mr_ndesc;
} else {
memcpy(mrd, mem_range_softc.mr_desc, (*arg) * sizeof(struct mem_range_desc));
}
return (0);
}
int
mem_range_attr_set(struct mem_range_desc *mrd, int *arg)
{
/* can we handle this? */
if (mem_range_softc.mr_op == NULL)
return (EOPNOTSUPP);
return (mem_range_softc.mr_op->set(&mem_range_softc, mrd, arg));
}
#endif /* MTRR */
268
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* $OpenBSD: uvm_pmemrange.h,v 1.14 2016/09/16 02:47:09 dlg Exp $ */
/*
* Copyright (c) 2009 Ariane van der Steldt <ariane@stack.nl>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* uvm_pmemrange.h: describe and manage free physical memory.
*/
#ifndef _UVM_UVM_PMEMRANGE_H_
#define _UVM_UVM_PMEMRANGE_H_
RBT_HEAD(uvm_pmr_addr, vm_page);
RBT_HEAD(uvm_pmr_size, vm_page);
/*
* Page types available:
* - DIRTY: this page may contain random data.
* - ZERO: this page has been zeroed.
*/
#define UVM_PMR_MEMTYPE_DIRTY 0
#define UVM_PMR_MEMTYPE_ZERO 1
#define UVM_PMR_MEMTYPE_MAX 2
/*
* An address range of memory.
*/
struct uvm_pmemrange {
struct uvm_pmr_addr addr; /* Free page chunks, sorted by addr. */
struct uvm_pmr_size size[UVM_PMR_MEMTYPE_MAX];
/* Free page chunks, sorted by size. */
TAILQ_HEAD(, vm_page) single[UVM_PMR_MEMTYPE_MAX];
/* single page regions (uses pageq) */
paddr_t low; /* Start of address range (pgno). */
paddr_t high; /* End +1 (pgno). */
int use; /* Use counter. */
psize_t nsegs; /* Current range count. */
TAILQ_ENTRY(uvm_pmemrange) pmr_use;
/* pmr, sorted by use */
RBT_ENTRY(uvm_pmemrange) pmr_addr;
/* pmr, sorted by address */
};
/*
* Description of failing memory allocation.
*
* Two ways new pages can become available:
* [1] page daemon drops them (we notice because they are freed)
* [2] a process calls free
*
* The buffer cache and page daemon can decide that they don't have the
* ability to make pages available in the requested range. In that case,
* the FAIL bit will be set.
* XXX There's a possibility that a page is no longer on the queues but
* XXX has not yet been freed, or that a page was busy.
* XXX Also, wired pages are not considered for paging, so they could
* XXX cause a failure that may be recoverable.
*/
struct uvm_pmalloc {
TAILQ_ENTRY(uvm_pmalloc) pmq;
/*
* Allocation request parameters.
*/
struct uvm_constraint_range pm_constraint;
psize_t pm_size;
/*
* State flags.
*/
int pm_flags;
};
/*
* uvm_pmalloc flags.
*/
#define UVM_PMA_LINKED 0x01 /* uvm_pmalloc is on list */
#define UVM_PMA_BUSY 0x02 /* entry is busy with fpageq unlocked */
#define UVM_PMA_FAIL 0x10 /* page daemon cannot free pages */
#define UVM_PMA_FREED 0x20 /* at least one page in the range was freed */
RBT_HEAD(uvm_pmemrange_addr, uvm_pmemrange);
TAILQ_HEAD(uvm_pmemrange_use, uvm_pmemrange);
/*
* pmr control structure. Contained in uvm.pmr_control.
*/
struct uvm_pmr_control {
struct uvm_pmemrange_addr addr;
struct uvm_pmemrange_use use;
/* Only changed while fpageq is locked. */
TAILQ_HEAD(, uvm_pmalloc) allocs;
};
void uvm_pmr_freepages(struct vm_page *, psize_t);
void uvm_pmr_freepageq(struct pglist *);
int uvm_pmr_getpages(psize_t, paddr_t, paddr_t, paddr_t, paddr_t,
int, int, struct pglist *);
void uvm_pmr_init(void);
int uvm_wait_pla(paddr_t, paddr_t, paddr_t, int);
void uvm_wakeup_pla(paddr_t, psize_t);
#if defined(DDB) || defined(DEBUG)
int uvm_pmr_isfree(struct vm_page *pg);
#endif
/*
* Internal tree logic.
*/
int uvm_pmr_addr_cmp(const struct vm_page *, const struct vm_page *);
int uvm_pmr_size_cmp(const struct vm_page *, const struct vm_page *);
RBT_PROTOTYPE(uvm_pmr_addr, vm_page, objt, uvm_pmr_addr_cmp);
RBT_PROTOTYPE(uvm_pmr_size, vm_page, objt, uvm_pmr_size_cmp);
RBT_PROTOTYPE(uvm_pmemrange_addr, uvm_pmemrange, pmr_addr,
uvm_pmemrange_addr_cmp);
struct vm_page *uvm_pmr_insert_addr(struct uvm_pmemrange *,
struct vm_page *, int);
void uvm_pmr_insert_size(struct uvm_pmemrange *,
struct vm_page *);
struct vm_page *uvm_pmr_insert(struct uvm_pmemrange *,
struct vm_page *, int);
void uvm_pmr_remove_addr(struct uvm_pmemrange *,
struct vm_page *);
void uvm_pmr_remove_size(struct uvm_pmemrange *,
struct vm_page *);
void uvm_pmr_remove(struct uvm_pmemrange *,
struct vm_page *);
struct vm_page *uvm_pmr_extract_range(struct uvm_pmemrange *,
struct vm_page *, paddr_t, paddr_t,
struct pglist *);
#endif /* _UVM_UVM_PMEMRANGE_H_ */
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
/* $OpenBSD: ch.c,v 1.71 2022/01/11 23:10:11 jsg Exp $ */
/* $NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 thorpej Exp $ */
/*
* Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
* All rights reserved.
*
* Partially based on an autochanger driver written by Stefan Grefen
* and on an autochanger driver written by the Systems Programming Group
* at the University of Utah Computer Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by Jason R. Thorpe
* for And Communications, http://www.and.com/
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/chio.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_changer.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
#define CHRETRIES 2
#define CHUNIT(x) (minor((x)))
struct ch_softc {
struct device sc_dev; /* generic device info */
struct scsi_link *sc_link; /* link in the SCSI bus */
int sc_picker; /* current picker */
/*
* The following information is obtained from the
* element address assignment page.
*/
int sc_firsts[4]; /* firsts, indexed by CHET_* */
int sc_counts[4]; /* counts, indexed by CHET_* */
/*
* The following mask defines the legal combinations
* of elements for the MOVE MEDIUM command.
*/
u_int8_t sc_movemask[4];
/*
* As above, but for EXCHANGE MEDIUM.
*/
u_int8_t sc_exchangemask[4];
int flags; /* misc. info */
/*
* Quirks; see below.
*/
int sc_settledelay; /* delay for settle */
};
/* sc_flags */
#define CHF_ROTATE 0x01 /* picker can rotate */
/* Autoconfiguration glue */
int chmatch(struct device *, void *, void *);
void chattach(struct device *, struct device *, void *);
const struct cfattach ch_ca = {
sizeof(struct ch_softc), chmatch, chattach
};
struct cfdriver ch_cd = {
NULL, "ch", DV_DULL
};
const struct scsi_inquiry_pattern ch_patterns[] = {
{T_CHANGER, T_REMOV,
"", "", ""},
};
int ch_move(struct ch_softc *, struct changer_move *);
int ch_exchange(struct ch_softc *, struct changer_exchange *);
int ch_position(struct ch_softc *, struct changer_position *);
int ch_usergetelemstatus(struct ch_softc *,
struct changer_element_status_request *);
int ch_getelemstatus(struct ch_softc *, int, int, caddr_t, size_t, int);
int ch_get_params(struct ch_softc *, int);
int ch_interpret_sense(struct scsi_xfer *xs);
void ch_get_quirks(struct ch_softc *, struct scsi_inquiry_data *);
/*
* SCSI changer quirks.
*/
struct chquirk {
struct scsi_inquiry_pattern cq_match; /* device id pattern */
int cq_settledelay; /* settle delay, in seconds */
};
struct chquirk chquirks[] = {
{{T_CHANGER, T_REMOV,
"SPECTRA", "9000", "0200"},
75},
};
int
chmatch(struct device *parent, void *match, void *aux)
{
struct scsi_attach_args *sa = aux;
struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
int priority;
(void)scsi_inqmatch(inq, ch_patterns, nitems(ch_patterns),
sizeof(ch_patterns[0]), &priority);
return priority;
}
void
chattach(struct device *parent, struct device *self, void *aux)
{
struct ch_softc *sc = (struct ch_softc *)self;
struct scsi_attach_args *sa = aux;
struct scsi_link *link = sa->sa_sc_link;
/* Glue into the SCSI bus */
sc->sc_link = link;
link->interpret_sense = ch_interpret_sense;
link->device_softc = sc;
link->openings = 1;
printf("\n");
/*
* Store our our device's quirks.
*/
ch_get_quirks(sc, &link->inqdata);
}
int
chopen(dev_t dev, int flags, int fmt, struct proc *p)
{
struct ch_softc *sc;
int oldcounts[4];
int i, unit, error = 0;
unit = CHUNIT(dev);
if ((unit >= ch_cd.cd_ndevs) ||
((sc = ch_cd.cd_devs[unit]) == NULL))
return ENXIO;
/*
* Only allow one open at a time.
*/
if (ISSET(sc->sc_link->flags, SDEV_OPEN))
return EBUSY;
SET(sc->sc_link->flags, SDEV_OPEN);
/*
* Absorb any unit attention errors. We must notice
* "Not ready" errors as a changer will report "In the
* process of getting ready" any time it must rescan
* itself to determine the state of the changer.
*/
error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
if (error)
goto bad;
/*
* Get information about the device. Save old information
* so we can decide whether to be verbose about new parameters.
*/
for (i = 0; i < 4; i++) {
oldcounts[i] = sc->sc_counts[i];
}
error = ch_get_params(sc, scsi_autoconf);
if (error)
goto bad;
for (i = 0; i < 4; i++) {
if (oldcounts[i] != sc->sc_counts[i]) {
break;
}
}
if (i < 4) {
#ifdef CHANGER_DEBUG
#define PLURAL(c) (c) == 1 ? "" : "s"
printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
sc->sc_dev.dv_xname,
sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
#undef PLURAL
printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
sc->sc_dev.dv_xname,
sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
sc->sc_dev.dv_xname,
sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
#endif /* CHANGER_DEBUG */
}
/* Default the current picker. */
sc->sc_picker = sc->sc_firsts[CHET_MT];
return 0;
bad:
CLR(sc->sc_link->flags, SDEV_OPEN);
return error;
}
int
chclose(dev_t dev, int flags, int fmt, struct proc *p)
{
struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
CLR(sc->sc_link->flags, SDEV_OPEN);
return 0;
}
int
chioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
int error = 0;
/*
* If this command can change the device's state, we must
* have the device open for writing.
*/
switch (cmd) {
case CHIOGPICKER:
case CHIOGPARAMS:
case CHIOGSTATUS:
break;
default:
if (!ISSET(flags, FWRITE))
return EBADF;
}
switch (cmd) {
case CHIOMOVE:
error = ch_move(sc, (struct changer_move *)data);
break;
case CHIOEXCHANGE:
error = ch_exchange(sc, (struct changer_exchange *)data);
break;
case CHIOPOSITION:
error = ch_position(sc, (struct changer_position *)data);
break;
case CHIOGPICKER:
*(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
break;
case CHIOSPICKER: {
int new_picker = *(int *)data;
if (new_picker > (sc->sc_counts[CHET_MT] - 1))
return EINVAL;
sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
break; }
case CHIOGPARAMS: {
struct changer_params *cp = (struct changer_params *)data;
cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
cp->cp_npickers = sc->sc_counts[CHET_MT];
cp->cp_nslots = sc->sc_counts[CHET_ST];
cp->cp_nportals = sc->sc_counts[CHET_IE];
cp->cp_ndrives = sc->sc_counts[CHET_DT];
break; }
case CHIOGSTATUS: {
struct changer_element_status_request *cesr =
(struct changer_element_status_request *)data;
error = ch_usergetelemstatus(sc, cesr);
break; }
/* Implement prevent/allow? */
default:
error = scsi_do_ioctl(sc->sc_link, cmd, data, flags);
break;
}
return error;
}
int
ch_move(struct ch_softc *sc, struct changer_move *cm)
{
struct scsi_move_medium *cmd;
struct scsi_xfer *xs;
int error;
u_int16_t fromelem, toelem;
/*
* Check arguments.
*/
if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
return EINVAL;
if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
(cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
return ENODEV;
/*
* Check the request against the changer's capabilities.
*/
if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
return EINVAL;
/*
* Calculate the source and destination elements.
*/
fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
/*
* Build the SCSI command.
*/
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->retries = CHRETRIES;
xs->timeout = 100000;
cmd = (struct scsi_move_medium *)&xs->cmd;
cmd->opcode = MOVE_MEDIUM;
_lto2b(sc->sc_picker, cmd->tea);
_lto2b(fromelem, cmd->src);
_lto2b(toelem, cmd->dst);
if (ISSET(cm->cm_flags, CM_INVERT))
SET(cmd->flags, MOVE_MEDIUM_INVERT);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
int
ch_exchange(struct ch_softc *sc, struct changer_exchange *ce)
{
struct scsi_exchange_medium *cmd;
struct scsi_xfer *xs;
int error;
u_int16_t src, dst1, dst2;
/*
* Check arguments.
*/
if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
(ce->ce_sdsttype > CHET_DT))
return EINVAL;
if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
(ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
(ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
return ENODEV;
/*
* Check the request against the changer's capabilities.
*/
if (((sc->sc_exchangemask[ce->ce_srctype] &
(1 << ce->ce_fdsttype)) == 0) ||
((sc->sc_exchangemask[ce->ce_fdsttype] &
(1 << ce->ce_sdsttype)) == 0))
return EINVAL;
/*
* Calculate the source and destination elements.
*/
src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
/*
* Build the SCSI command.
*/
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->retries = CHRETRIES;
xs->timeout = 100000;
cmd = (struct scsi_exchange_medium *)&xs->cmd;
cmd->opcode = EXCHANGE_MEDIUM;
_lto2b(sc->sc_picker, cmd->tea);
_lto2b(src, cmd->src);
_lto2b(dst1, cmd->fdst);
_lto2b(dst2, cmd->sdst);
if (ISSET(ce->ce_flags, CE_INVERT1))
SET(cmd->flags, EXCHANGE_MEDIUM_INV1);
if (ISSET(ce->ce_flags, CE_INVERT2))
SET(cmd->flags, EXCHANGE_MEDIUM_INV2);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
int
ch_position(struct ch_softc *sc, struct changer_position *cp)
{
struct scsi_position_to_element *cmd;
struct scsi_xfer *xs;
int error;
u_int16_t dst;
/*
* Check arguments.
*/
if (cp->cp_type > CHET_DT)
return EINVAL;
if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
return ENODEV;
/*
* Calculate the destination element.
*/
dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
/*
* Build the SCSI command.
*/
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->retries = CHRETRIES;
xs->timeout = 100000;
cmd = (struct scsi_position_to_element *)&xs->cmd;
cmd->opcode = POSITION_TO_ELEMENT;
_lto2b(sc->sc_picker, cmd->tea);
_lto2b(dst, cmd->dst);
if (ISSET(cp->cp_flags, CP_INVERT))
SET(cmd->flags, POSITION_TO_ELEMENT_INVERT);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Copy a volume tag to a volume_tag struct, converting SCSI byte order
* to host native byte order in the volume serial number. The volume
* label as returned by the changer is transferred to user mode as
* nul-terminated string. Volume labels are truncated at the first
* space, as suggested by SCSI-2.
*/
static void
copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
{
int i;
for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
char c = voltag->vif[i];
if (c && c != ' ')
uvoltag->cv_volid[i] = c;
else
break;
}
uvoltag->cv_volid[i] = '\0';
uvoltag->cv_serial = _2btol(voltag->vsn);
}
/*
* Copy an an element status descriptor to a user-mode
* changer_element_status structure.
*/
static void
copy_element_status(struct ch_softc *sc, int flags,
struct read_element_status_descriptor *desc,
struct changer_element_status *ces)
{
u_int16_t eaddr = _2btol(desc->eaddr);
u_int16_t et;
for (et = CHET_MT; et <= CHET_DT; et++) {
if ((sc->sc_firsts[et] <= eaddr)
&& ((sc->sc_firsts[et] + sc->sc_counts[et])
> eaddr)) {
ces->ces_addr = eaddr - sc->sc_firsts[et];
ces->ces_type = et;
break;
}
}
ces->ces_flags = desc->flags1;
ces->ces_sensecode = desc->sense_code;
ces->ces_sensequal = desc->sense_qual;
if (desc->flags2 & READ_ELEMENT_STATUS_INVERT)
ces->ces_flags |= READ_ELEMENT_STATUS_EXCEPT;
if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) {
eaddr = _2btol(desc->ssea);
/* convert source address to logical format */
for (et = CHET_MT; et <= CHET_DT; et++) {
if ((sc->sc_firsts[et] <= eaddr)
&& ((sc->sc_firsts[et] + sc->sc_counts[et])
> eaddr)) {
ces->ces_source_addr =
eaddr - sc->sc_firsts[et];
ces->ces_source_type = et;
ces->ces_flags |= READ_ELEMENT_STATUS_ACCESS;
break;
}
}
if (!(ces->ces_flags & READ_ELEMENT_STATUS_ACCESS))
printf("ch: warning: could not map element source "
"address %ud to a valid element type\n",
eaddr);
}
if (ISSET(flags, READ_ELEMENT_STATUS_PVOLTAG))
copy_voltag(&ces->ces_pvoltag, &desc->pvoltag);
if (ISSET(flags, READ_ELEMENT_STATUS_AVOLTAG))
copy_voltag(&ces->ces_avoltag, &desc->avoltag);
}
/*
* Perform a READ ELEMENT STATUS on behalf of the user, and return to
* the user only the data the user is interested in (i.e. an array of
* changer_element_status structures)
*/
int
ch_usergetelemstatus(struct ch_softc *sc,
struct changer_element_status_request *cesr)
{
struct changer_element_status *user_data = NULL;
struct read_element_status_header *st_hdr;
struct read_element_status_page_header *pg_hdr;
caddr_t desc;
caddr_t data = NULL;
size_t size, desclen, udsize;
int avail, chet, i, want_voltags;
int error = 0;
chet = cesr->cesr_type;
want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
/*
* If there are no elements of the requested type in the changer,
* the request is invalid.
*/
if (sc->sc_counts[chet] == 0)
return EINVAL;
/*
* Request one descriptor for the given element type. This
* is used to determine the size of the descriptor so that
* we can allocate enough storage for all of them. We assume
* that the first one can fit into 1k.
*/
size = 1024;
data = dma_alloc(size, PR_WAITOK);
error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, size,
want_voltags);
if (error)
goto done;
st_hdr = (struct read_element_status_header *)data;
pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
desclen = _2btol(pg_hdr->edl);
dma_free(data, size);
/*
* Reallocate storage for descriptors and get them from the
* device.
*/
size = sizeof(struct read_element_status_header) +
sizeof(struct read_element_status_page_header) +
(desclen * sc->sc_counts[chet]);
data = dma_alloc(size, PR_WAITOK);
error = ch_getelemstatus(sc, sc->sc_firsts[chet],
sc->sc_counts[chet], data, size, want_voltags);
if (error)
goto done;
/*
* Fill in the user status array.
*/
st_hdr = (struct read_element_status_header *)data;
pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
avail = _2btol(st_hdr->count);
if (avail != sc->sc_counts[chet]) {
error = EINVAL;
goto done;
}
user_data = mallocarray(avail, sizeof(struct changer_element_status),
M_DEVBUF, M_WAITOK | M_ZERO);
udsize = avail * sizeof(struct changer_element_status);
desc = (caddr_t)(pg_hdr + 1);
for (i = 0; i < avail; ++i) {
struct changer_element_status *ces = &(user_data[i]);
copy_element_status(sc, pg_hdr->flags,
(struct read_element_status_descriptor *)desc, ces);
desc += desclen;
}
/* Copy array out to userspace. */
error = copyout(user_data, cesr->cesr_data, udsize);
done:
if (data != NULL)
dma_free(data, size);
if (user_data != NULL)
free(user_data, M_DEVBUF, udsize);
return error;
}
int
ch_getelemstatus(struct ch_softc *sc, int first, int count, caddr_t data,
size_t datalen, int voltag)
{
struct scsi_read_element_status *cmd;
struct scsi_xfer *xs;
int error;
/*
* Build SCSI command.
*/
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = data;
xs->datalen = datalen;
xs->retries = CHRETRIES;
xs->timeout = 100000;
cmd = (struct scsi_read_element_status *)&xs->cmd;
cmd->opcode = READ_ELEMENT_STATUS;
_lto2b(first, cmd->sea);
_lto2b(count, cmd->count);
_lto3b(datalen, cmd->len);
if (voltag)
SET(cmd->byte2, READ_ELEMENT_STATUS_VOLTAG);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Ask the device about itself and fill in the parameters in our
* softc.
*/
int
ch_get_params(struct ch_softc *sc, int flags)
{
union scsi_mode_sense_buf *data;
struct page_element_address_assignment *ea;
struct page_device_capabilities *cap;
u_int8_t *moves, *exchanges;
int big, error, from;
data = dma_alloc(sizeof(*data), PR_NOWAIT);
if (data == NULL)
return ENOMEM;
/*
* Grab info from the element address assignment page (0x1d).
*/
error = scsi_do_mode_sense(sc->sc_link, EA_PAGE, data,
(void **)&ea, sizeof(*ea), flags, &big);
if (error == 0 && ea == NULL)
error = EIO;
if (error != 0) {
#ifdef CHANGER_DEBUG
printf("%s: could not sense element address page\n",
sc->sc_dev.dv_xname);
#endif /* CHANGER_DEBUG */
dma_free(data, sizeof(*data));
return error;
}
sc->sc_firsts[CHET_MT] = _2btol(ea->mtea);
sc->sc_counts[CHET_MT] = _2btol(ea->nmte);
sc->sc_firsts[CHET_ST] = _2btol(ea->fsea);
sc->sc_counts[CHET_ST] = _2btol(ea->nse);
sc->sc_firsts[CHET_IE] = _2btol(ea->fieea);
sc->sc_counts[CHET_IE] = _2btol(ea->niee);
sc->sc_firsts[CHET_DT] = _2btol(ea->fdtea);
sc->sc_counts[CHET_DT] = _2btol(ea->ndte);
/* XXX Ask for transport geometry page. */
/*
* Grab info from the capabilities page (0x1f).
*/
error = scsi_do_mode_sense(sc->sc_link, CAP_PAGE, data,
(void **)&cap, sizeof(*cap), flags, &big);
if (error == 0 && cap == NULL)
error = EIO;
if (error != 0) {
#ifdef CHANGER_DEBUG
printf("%s: could not sense capabilities page\n",
sc->sc_dev.dv_xname);
#endif /* CHANGER_DEBUG */
dma_free(data, sizeof(*data));
return error;
}
bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
moves = &cap->move_from_mt;
exchanges = &cap->exchange_with_mt;
for (from = CHET_MT; from <= CHET_DT; ++from) {
sc->sc_movemask[from] = moves[from];
sc->sc_exchangemask[from] = exchanges[from];
}
SET(sc->sc_link->flags, SDEV_MEDIA_LOADED);
dma_free(data, sizeof(*data));
return 0;
}
void
ch_get_quirks(struct ch_softc *sc, struct scsi_inquiry_data *inqbuf)
{
const struct chquirk *match;
int priority;
sc->sc_settledelay = 0;
match = (const struct chquirk *)scsi_inqmatch(inqbuf,
(caddr_t)chquirks,
sizeof(chquirks) / sizeof(chquirks[0]),
sizeof(chquirks[0]), &priority);
if (priority != 0) {
sc->sc_settledelay = match->cq_settledelay;
}
}
/*
* Look at the returned sense and act on the error to determine
* the unix error number to pass back... (0 = report no error)
* (-1 = continue processing)
*/
int
ch_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
struct scsi_link *link = xs->sc_link;
u_int8_t serr, skey;
serr = sense->error_code & SSD_ERRCODE;
skey = sense->flags & SSD_KEY;
if (!ISSET(link->flags, SDEV_OPEN) ||
(serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
return scsi_interpret_sense(xs);
switch (skey) {
/*
* We do custom processing in ch for the unit becoming ready
* case. in this case we do not allow xs->retries to be
* decremented only on the "Unit Becoming Ready" case. This is
* because tape changers report "Unit Becoming Ready" when they
* rescan their state (i.e. when the door got opened) and can
* take a long time for large units. Rather than having a
* massive timeout for all operations (which would cause other
* problems) we allow changers to wait (but be interruptible
* with Ctrl-C) forever as long as they are reporting that they
* are becoming ready. all other cases are handled as per the
* default.
*/
case SKEY_NOT_READY:
if (ISSET(xs->flags, SCSI_IGNORE_NOT_READY))
return 0;
switch (ASC_ASCQ(sense)) {
case SENSE_NOT_READY_BECOMING_READY:
SC_DEBUG(link, SDEV_DB1, ("not ready: busy (%#x)\n",
sense->add_sense_code_qual));
/* don't count this as a retry */
xs->retries++;
return scsi_delay(xs, 1);
default:
return scsi_interpret_sense(xs);
}
default:
return scsi_interpret_sense(xs);
}
}
14
14
14
14
1
1
1
1
14
14
14
14
14
14
14
14
14
17
14
14
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
/* $OpenBSD: kstat.c,v 1.2 2022/01/31 05:09:17 dlg Exp $ */
/*
* Copyright (c) 2020 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/time.h>
/* for kstat_set_cpu */
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/kstat.h>
RBT_HEAD(kstat_id_tree, kstat);
static inline int
kstat_id_cmp(const struct kstat *a, const struct kstat *b)
{
if (a->ks_id > b->ks_id)
return (1);
if (a->ks_id < b->ks_id)
return (-1);
return (0);
}
RBT_PROTOTYPE(kstat_id_tree, kstat, ks_id_entry, kstat_id_cmp);
RBT_HEAD(kstat_pv_tree, kstat);
static inline int
kstat_pv_cmp(const struct kstat *a, const struct kstat *b)
{
int rv;
rv = strcmp(a->ks_provider, b->ks_provider);
if (rv != 0)
return (rv);
if (a->ks_instance > b->ks_instance)
return (1);
if (a->ks_instance < b->ks_instance)
return (-1);
rv = strcmp(a->ks_name, b->ks_name);
if (rv != 0)
return (rv);
if (a->ks_unit > b->ks_unit)
return (1);
if (a->ks_unit < b->ks_unit)
return (-1);
return (0);
}
RBT_PROTOTYPE(kstat_pv_tree, kstat, ks_pv_entry, kstat_pv_cmp);
RBT_HEAD(kstat_nm_tree, kstat);
static inline int
kstat_nm_cmp(const struct kstat *a, const struct kstat *b)
{
int rv;
rv = strcmp(a->ks_name, b->ks_name);
if (rv != 0)
return (rv);
if (a->ks_unit > b->ks_unit)
return (1);
if (a->ks_unit < b->ks_unit)
return (-1);
rv = strcmp(a->ks_provider, b->ks_provider);
if (rv != 0)
return (rv);
if (a->ks_instance > b->ks_instance)
return (1);
if (a->ks_instance < b->ks_instance)
return (-1);
return (0);
}
RBT_PROTOTYPE(kstat_nm_tree, kstat, ks_nm_entry, kstat_nm_cmp);
struct kstat_lock_ops {
void (*enter)(void *);
void (*leave)(void *);
};
#define kstat_enter(_ks) (_ks)->ks_lock_ops->enter((_ks)->ks_lock)
#define kstat_leave(_ks) (_ks)->ks_lock_ops->leave((_ks)->ks_lock)
const struct kstat_lock_ops kstat_rlock_ops = {
(void (*)(void *))rw_enter_read,
(void (*)(void *))rw_exit_read,
};
const struct kstat_lock_ops kstat_wlock_ops = {
(void (*)(void *))rw_enter_write,
(void (*)(void *))rw_exit_write,
};
const struct kstat_lock_ops kstat_mutex_ops = {
(void (*)(void *))mtx_enter,
(void (*)(void *))mtx_leave,
};
void kstat_cpu_enter(void *);
void kstat_cpu_leave(void *);
const struct kstat_lock_ops kstat_cpu_ops = {
kstat_cpu_enter,
kstat_cpu_leave,
};
struct rwlock kstat_lock = RWLOCK_INITIALIZER("kstat");
/*
* The global state is versioned so changes to the set of kstats
* can be detected. This is an int so it can be read atomically on
* any arch, which is a ridiculous optimisation, really.
*/
unsigned int kstat_version = 0;
/*
* kstat structures have a unique identifier so they can be found
* quickly. Identifiers are 64bit in the hope that it won't wrap
* during the runtime of a system. The identifiers start at 1 so that
* 0 can be used as the first value for userland to iterate with.
*/
uint64_t kstat_next_id = 1;
struct kstat_id_tree kstat_id_tree = RBT_INITIALIZER();
struct kstat_pv_tree kstat_pv_tree = RBT_INITIALIZER();
struct kstat_nm_tree kstat_nm_tree = RBT_INITIALIZER();
struct pool kstat_pool;
struct rwlock kstat_default_lock = RWLOCK_INITIALIZER("kstatlk");
int kstat_read(struct kstat *);
int kstat_copy(struct kstat *, void *);
int
kstatattach(int num)
{
/* XXX install system stats here */
return (0);
}
int
kstatopen(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
int
kstatclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
int
kstatioc_enter(struct kstat_req *ksreq)
{
int error;
error = rw_enter(&kstat_lock, RW_READ | RW_INTR);
if (error != 0)
return (error);
if (!ISSET(ksreq->ks_rflags, KSTATIOC_F_IGNVER) &&
ksreq->ks_version != kstat_version) {
error = EINVAL;
goto error;
}
return (0);
error:
rw_exit(&kstat_lock);
return (error);
}
int
kstatioc_leave(struct kstat_req *ksreq, struct kstat *ks)
{
void *buf = NULL;
size_t klen = 0, ulen = 0;
struct timespec updated;
int error = 0;
if (ks == NULL) {
error = ENOENT;
goto error;
}
switch (ks->ks_state) {
case KSTAT_S_CREATED:
ksreq->ks_updated = ks->ks_created;
ksreq->ks_interval.tv_sec = 0;
ksreq->ks_interval.tv_nsec = 0;
ksreq->ks_datalen = 0;
ksreq->ks_dataver = 0;
break;
case KSTAT_S_INSTALLED:
ksreq->ks_dataver = ks->ks_dataver;
ksreq->ks_interval = ks->ks_interval;
if (ksreq->ks_data == NULL) {
/* userland doesn't want actual data, so shortcut */
kstat_enter(ks);
ksreq->ks_datalen = ks->ks_datalen;
ksreq->ks_updated = ks->ks_updated;
kstat_leave(ks);
break;
}
klen = ks->ks_datalen; /* KSTAT_F_REALLOC */
buf = malloc(klen, M_TEMP, M_WAITOK|M_CANFAIL);
if (buf == NULL) {
error = ENOMEM;
goto error;
}
kstat_enter(ks);
error = (*ks->ks_read)(ks);
if (error == 0) {
updated = ks->ks_updated;
/* KSTAT_F_REALLOC */
KASSERTMSG(ks->ks_datalen == klen,
"kstat doesnt support resized data yet");
error = (*ks->ks_copy)(ks, buf);
}
kstat_leave(ks);
if (error != 0)
goto error;
ulen = ksreq->ks_datalen;
ksreq->ks_datalen = klen; /* KSTAT_F_REALLOC */
ksreq->ks_updated = updated;
break;
default:
panic("ks %p unexpected state %u", ks, ks->ks_state);
}
ksreq->ks_version = kstat_version;
ksreq->ks_id = ks->ks_id;
if (strlcpy(ksreq->ks_provider, ks->ks_provider,
sizeof(ksreq->ks_provider)) >= sizeof(ksreq->ks_provider))
panic("kstat %p provider string has grown", ks);
ksreq->ks_instance = ks->ks_instance;
if (strlcpy(ksreq->ks_name, ks->ks_name,
sizeof(ksreq->ks_name)) >= sizeof(ksreq->ks_name))
panic("kstat %p name string has grown", ks);
ksreq->ks_unit = ks->ks_unit;
ksreq->ks_created = ks->ks_created;
ksreq->ks_type = ks->ks_type;
ksreq->ks_state = ks->ks_state;
error:
rw_exit(&kstat_lock);
if (buf != NULL) {
if (error == 0)
error = copyout(buf, ksreq->ks_data, min(klen, ulen));
free(buf, M_TEMP, klen);
}
return (error);
}
int
kstatioc_find_id(struct kstat_req *ksreq)
{
struct kstat *ks, key;
int error;
error = kstatioc_enter(ksreq);
if (error != 0)
return (error);
key.ks_id = ksreq->ks_id;
ks = RBT_FIND(kstat_id_tree, &kstat_id_tree, &key);
return (kstatioc_leave(ksreq, ks));
}
int
kstatioc_nfind_id(struct kstat_req *ksreq)
{
struct kstat *ks, key;
int error;
error = kstatioc_enter(ksreq);
if (error != 0)
return (error);
key.ks_id = ksreq->ks_id;
ks = RBT_NFIND(kstat_id_tree, &kstat_id_tree, &key);
return (kstatioc_leave(ksreq, ks));
}
int
kstatioc_find_pv(struct kstat_req *ksreq)
{
struct kstat *ks, key;
int error;
error = kstatioc_enter(ksreq);
if (error != 0)
return (error);
key.ks_provider = ksreq->ks_provider;
key.ks_instance = ksreq->ks_instance;
key.ks_name = ksreq->ks_name;
key.ks_unit = ksreq->ks_unit;
ks = RBT_FIND(kstat_pv_tree, &kstat_pv_tree, &key);
return (kstatioc_leave(ksreq, ks));
}
int
kstatioc_nfind_pv(struct kstat_req *ksreq)
{
struct kstat *ks, key;
int error;
error = kstatioc_enter(ksreq);
if (error != 0)
return (error);
key.ks_provider = ksreq->ks_provider;
key.ks_instance = ksreq->ks_instance;
key.ks_name = ksreq->ks_name;
key.ks_unit = ksreq->ks_unit;
ks = RBT_NFIND(kstat_pv_tree, &kstat_pv_tree, &key);
return (kstatioc_leave(ksreq, ks));
}
int
kstatioc_find_nm(struct kstat_req *ksreq)
{
struct kstat *ks, key;
int error;
error = kstatioc_enter(ksreq);
if (error != 0)
return (error);
key.ks_name = ksreq->ks_name;
key.ks_unit = ksreq->ks_unit;
key.ks_provider = ksreq->ks_provider;
key.ks_instance = ksreq->ks_instance;
ks = RBT_FIND(kstat_nm_tree, &kstat_nm_tree, &key);
return (kstatioc_leave(ksreq, ks));
}
int
kstatioc_nfind_nm(struct kstat_req *ksreq)
{
struct kstat *ks, key;
int error;
error = kstatioc_enter(ksreq);
if (error != 0)
return (error);
key.ks_name = ksreq->ks_name;
key.ks_unit = ksreq->ks_unit;
key.ks_provider = ksreq->ks_provider;
key.ks_instance = ksreq->ks_instance;
ks = RBT_NFIND(kstat_nm_tree, &kstat_nm_tree, &key);
return (kstatioc_leave(ksreq, ks));
}
int
kstatioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct kstat_req *ksreq = (struct kstat_req *)data;
int error = 0;
KERNEL_UNLOCK();
switch (cmd) {
case KSTATIOC_VERSION:
*(unsigned int *)data = kstat_version;
break;
case KSTATIOC_FIND_ID:
error = kstatioc_find_id(ksreq);
break;
case KSTATIOC_NFIND_ID:
error = kstatioc_nfind_id(ksreq);
break;
case KSTATIOC_FIND_PROVIDER:
error = kstatioc_find_pv(ksreq);
break;
case KSTATIOC_NFIND_PROVIDER:
error = kstatioc_nfind_pv(ksreq);
break;
case KSTATIOC_FIND_NAME:
error = kstatioc_find_nm(ksreq);
break;
case KSTATIOC_NFIND_NAME:
error = kstatioc_nfind_nm(ksreq);
break;
default:
error = ENOTTY;
break;
}
KERNEL_LOCK();
return (error);
}
void
kstat_init(void)
{
static int initialized = 0;
if (initialized)
return;
pool_init(&kstat_pool, sizeof(struct kstat), 0, IPL_NONE,
PR_WAITOK | PR_RWLOCK, "kstatmem", NULL);
initialized = 1;
}
int
kstat_strcheck(const char *str)
{
size_t i, l;
l = strlen(str);
if (l == 0 || l >= KSTAT_STRLEN)
return (-1);
for (i = 0; i < l; i++) {
int ch = str[i];
if (ch >= 'a' && ch <= 'z')
continue;
if (ch >= 'A' && ch <= 'Z')
continue;
if (ch >= '0' && ch <= '9')
continue;
switch (ch) {
case '-':
case '_':
case '.':
break;
default:
return (-1);
}
}
return (0);
}
struct kstat *
kstat_create(const char *provider, unsigned int instance,
const char *name, unsigned int unit,
unsigned int type, unsigned int flags)
{
struct kstat *ks, *oks;
if (kstat_strcheck(provider) == -1)
panic("invalid provider string");
if (kstat_strcheck(name) == -1)
panic("invalid name string");
kstat_init();
ks = pool_get(&kstat_pool, PR_WAITOK|PR_ZERO);
ks->ks_provider = provider;
ks->ks_instance = instance;
ks->ks_name = name;
ks->ks_unit = unit;
ks->ks_flags = flags;
ks->ks_type = type;
ks->ks_state = KSTAT_S_CREATED;
getnanouptime(&ks->ks_created);
ks->ks_updated = ks->ks_created;
ks->ks_lock = &kstat_default_lock;
ks->ks_lock_ops = &kstat_wlock_ops;
ks->ks_read = kstat_read;
ks->ks_copy = kstat_copy;
rw_enter_write(&kstat_lock);
ks->ks_id = kstat_next_id;
oks = RBT_INSERT(kstat_pv_tree, &kstat_pv_tree, ks);
if (oks == NULL) {
/* commit */
kstat_next_id++;
kstat_version++;
oks = RBT_INSERT(kstat_nm_tree, &kstat_nm_tree, ks);
if (oks != NULL)
panic("kstat name collision! (%llu)", ks->ks_id);
oks = RBT_INSERT(kstat_id_tree, &kstat_id_tree, ks);
if (oks != NULL)
panic("kstat id collision! (%llu)", ks->ks_id);
}
rw_exit_write(&kstat_lock);
if (oks != NULL) {
pool_put(&kstat_pool, ks);
return (NULL);
}
return (ks);
}
void
kstat_set_rlock(struct kstat *ks, struct rwlock *rwl)
{
KASSERT(ks->ks_state == KSTAT_S_CREATED);
ks->ks_lock = rwl;
ks->ks_lock_ops = &kstat_rlock_ops;
}
void
kstat_set_wlock(struct kstat *ks, struct rwlock *rwl)
{
KASSERT(ks->ks_state == KSTAT_S_CREATED);
ks->ks_lock = rwl;
ks->ks_lock_ops = &kstat_wlock_ops;
}
void
kstat_set_mutex(struct kstat *ks, struct mutex *mtx)
{
KASSERT(ks->ks_state == KSTAT_S_CREATED);
ks->ks_lock = mtx;
ks->ks_lock_ops = &kstat_mutex_ops;
}
void
kstat_cpu_enter(void *p)
{
struct cpu_info *ci = p;
sched_peg_curproc(ci);
}
void
kstat_cpu_leave(void *p)
{
atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
}
void
kstat_set_cpu(struct kstat *ks, struct cpu_info *ci)
{
KASSERT(ks->ks_state == KSTAT_S_CREATED);
ks->ks_lock = ci;
ks->ks_lock_ops = &kstat_cpu_ops;
}
int
kstat_read_nop(struct kstat *ks)
{
return (0);
}
void
kstat_install(struct kstat *ks)
{
if (!ISSET(ks->ks_flags, KSTAT_F_REALLOC)) {
KASSERTMSG(ks->ks_copy != NULL || ks->ks_data != NULL,
"kstat %p %s:%u:%s:%u must provide ks_copy or ks_data", ks,
ks->ks_provider, ks->ks_instance, ks->ks_name, ks->ks_unit);
KASSERT(ks->ks_datalen > 0);
}
rw_enter_write(&kstat_lock);
ks->ks_state = KSTAT_S_INSTALLED;
rw_exit_write(&kstat_lock);
}
void
kstat_remove(struct kstat *ks)
{
rw_enter_write(&kstat_lock);
KASSERTMSG(ks->ks_state == KSTAT_S_INSTALLED,
"kstat %p %s:%u:%s:%u is not installed", ks,
ks->ks_provider, ks->ks_instance, ks->ks_name, ks->ks_unit);
ks->ks_state = KSTAT_S_CREATED;
rw_exit_write(&kstat_lock);
}
void
kstat_destroy(struct kstat *ks)
{
rw_enter_write(&kstat_lock);
RBT_REMOVE(kstat_id_tree, &kstat_id_tree, ks);
RBT_REMOVE(kstat_pv_tree, &kstat_pv_tree, ks);
RBT_REMOVE(kstat_nm_tree, &kstat_nm_tree, ks);
kstat_version++;
rw_exit_write(&kstat_lock);
pool_put(&kstat_pool, ks);
}
int
kstat_read(struct kstat *ks)
{
getnanouptime(&ks->ks_updated);
return (0);
}
int
kstat_copy(struct kstat *ks, void *buf)
{
memcpy(buf, ks->ks_data, ks->ks_datalen);
return (0);
}
RBT_GENERATE(kstat_id_tree, kstat, ks_id_entry, kstat_id_cmp);
RBT_GENERATE(kstat_pv_tree, kstat, ks_pv_entry, kstat_pv_cmp);
RBT_GENERATE(kstat_nm_tree, kstat, ks_nm_entry, kstat_nm_cmp);
void
kstat_kv_init(struct kstat_kv *kv, const char *name, enum kstat_kv_type type)
{
memset(kv, 0, sizeof(*kv));
strlcpy(kv->kv_key, name, sizeof(kv->kv_key)); /* XXX truncated? */
kv->kv_type = type;
kv->kv_unit = KSTAT_KV_U_NONE;
}
void
kstat_kv_unit_init(struct kstat_kv *kv, const char *name,
enum kstat_kv_type type, enum kstat_kv_unit unit)
{
switch (type) {
case KSTAT_KV_T_COUNTER64:
case KSTAT_KV_T_COUNTER32:
case KSTAT_KV_T_UINT64:
case KSTAT_KV_T_INT64:
case KSTAT_KV_T_UINT32:
case KSTAT_KV_T_INT32:
break;
default:
panic("kv unit init %s: unit for non-integer type", name);
}
memset(kv, 0, sizeof(*kv));
strlcpy(kv->kv_key, name, sizeof(kv->kv_key)); /* XXX truncated? */
kv->kv_type = type;
kv->kv_unit = unit;
}
876
874
28
878
780
582
625
20
19
2
20
1
624
5
4
625
599
600
624
3
623
569
2
223
224
3
6
16
16
8
624
189
3
188
40
7
12
149
153
11
30
596
1
600
439
179
3
11
84
1
2
8
57
49
3
4
30
78
1
94
48
388
74
495
8
625
623
107
107
107
107
11
11
11
11
103
103
100
2
4
4
4
4
1
102
103
103
103
1
4
5
1
4
46
46
2
45
46
46
46
17
17
17
17
23
23
23
3
23
23
4
1
3
2
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
/* $OpenBSD: ufs_lookup.c,v 1.59 2022/01/11 03:13:59 jsg Exp $ */
/* $NetBSD: ufs_lookup.c,v 1.7 1996/02/09 22:36:06 christos Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_lookup.c 8.9 (Berkeley) 8/11/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/namei.h>
#include <sys/buf.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#ifdef UFS_DIRHASH
#include <ufs/ufs/dirhash.h>
#endif
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
extern struct nchstats nchstats;
#ifdef DIAGNOSTIC
int dirchk = 1;
#else
int dirchk = 0;
#endif
#define OFSFMT(ip) ((ip)->i_ump->um_maxsymlinklen == 0)
/*
* Convert a component of a pathname into a pointer to a locked inode.
* This is a very central and rather complicated routine.
* If the file system is not maintained in a strict tree hierarchy,
* this can result in a deadlock situation (see comments in code below).
*
* The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
* on whether the name is to be looked up, created, renamed, or deleted.
* When CREATE, RENAME, or DELETE is specified, information usable in
* creating, renaming, or deleting a directory entry may be calculated.
* If flag has LOCKPARENT or'ed into it and the target of the pathname
* exists, lookup returns both the target and its parent directory locked.
* When creating or renaming and LOCKPARENT is specified, the target may
* not be ".". When deleting and LOCKPARENT is specified, the target may
* be "."., but the caller must check to ensure it does an vrele and vput
* instead of two vputs.
*
* Overall outline of ufs_lookup:
*
* check accessibility of directory
* look for name in cache, if found, then if at end of path
* and deleting or creating, drop it, else return name
* search for name in directory, to found or notfound
* notfound:
* if creating, return locked directory, leaving info on available slots
* else return error
* found:
* if at end of path and deleting, return information to allow delete
* if at end of path and rewriting (RENAME and LOCKPARENT), lock target
* inode and return info to allow rewrite
* if not at end, add name to cache; if at end and neither creating
* nor deleting, add name to cache
*/
int
ufs_lookup(void *v)
{
struct vop_lookup_args *ap = v;
struct vnode *vdp; /* vnode for directory being searched */
struct inode *dp; /* inode for directory being searched */
struct buf *bp; /* a buffer of directory entries */
struct direct *ep; /* the current directory entry */
int entryoffsetinblock; /* offset of ep in bp's buffer */
enum {NONE, COMPACT, FOUND} slotstatus;
doff_t slotoffset; /* offset of area with free space */
int slotsize; /* size of area at slotoffset */
int slotfreespace; /* amount of space free in slot */
int slotneeded; /* size of the entry we're seeking */
int numdirpasses; /* strategy for directory search */
doff_t endsearch; /* offset to end directory search */
doff_t prevoff; /* prev entry dp->i_offset */
struct vnode *pdp; /* saved dp during symlink work */
struct vnode *tdp; /* returned by VFS_VGET */
doff_t enduseful; /* pointer past last used dir slot */
u_long bmask; /* block offset mask */
int lockparent; /* 1 => lockparent flag is set */
int wantparent; /* 1 => wantparent or lockparent flag */
int namlen, error;
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
struct ucred *cred = cnp->cn_cred;
int flags;
int nameiop = cnp->cn_nameiop;
cnp->cn_flags &= ~PDIRUNLOCK;
flags = cnp->cn_flags;
bp = NULL;
slotoffset = -1;
*vpp = NULL;
vdp = ap->a_dvp;
dp = VTOI(vdp);
lockparent = flags & LOCKPARENT;
wantparent = flags & (LOCKPARENT|WANTPARENT);
/*
* Check accessibility of directory.
*/
if ((DIP(dp, mode) & IFMT) != IFDIR)
return (ENOTDIR);
if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
return (error);
if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return (EROFS);
/*
* We now have a segment name to search for, and a directory to search.
*
* Before tediously performing a linear scan of the directory,
* check the name cache to see if the directory/name pair
* we are looking for is known already.
*/
if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
return (error);
/*
* Suppress search for slots unless creating
* file and at end of pathname, in which case
* we watch for a place to put the new file in
* case it doesn't already exist.
*/
slotstatus = FOUND;
slotfreespace = slotsize = slotneeded = 0;
if ((nameiop == CREATE || nameiop == RENAME) &&
(flags & ISLASTCN)) {
slotstatus = NONE;
slotneeded = (sizeof(struct direct) - MAXNAMLEN +
cnp->cn_namelen + 3) &~ 3;
}
/*
* If there is cached information on a previous search of
* this directory, pick up where we last left off.
* We cache only lookups as these are the most common
* and have the greatest payoff. Caching CREATE has little
* benefit as it usually must search the entire directory
* to determine that the entry does not exist. Caching the
* location of the last DELETE or RENAME has not reduced
* profiling time and hence has been removed in the interest
* of simplicity.
*/
bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
#ifdef UFS_DIRHASH
/*
* Use dirhash for fast operations on large directories. The logic
* to determine whether to hash the directory is contained within
* ufsdirhash_build(); a zero return means that it decided to hash
* this directory and it successfully built up the hash table.
*/
if (ufsdirhash_build(dp) == 0) {
/* Look for a free slot if needed. */
enduseful = DIP(dp, size);
if (slotstatus != FOUND) {
slotoffset = ufsdirhash_findfree(dp, slotneeded,
&slotsize);
if (slotoffset >= 0) {
slotstatus = COMPACT;
enduseful = ufsdirhash_enduseful(dp);
if (enduseful < 0)
enduseful = DIP(dp, size);
}
}
/* Look up the component. */
numdirpasses = 1;
entryoffsetinblock = 0; /* silence compiler warning */
switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
&dp->i_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) {
case 0:
ep = (struct direct *)((char *)bp->b_data +
(dp->i_offset & bmask));
goto foundentry;
case ENOENT:
#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
dp->i_offset = roundup2(DIP(dp, size), DIRBLKSIZ);
goto notfound;
default:
/* Something failed; just do a linear search. */
break;
}
}
#endif /* UFS_DIRHASH */
if (nameiop != LOOKUP || dp->i_diroff == 0 ||
dp->i_diroff >= DIP(dp, size)) {
entryoffsetinblock = 0;
dp->i_offset = 0;
numdirpasses = 1;
} else {
dp->i_offset = dp->i_diroff;
if ((entryoffsetinblock = dp->i_offset & bmask) &&
(error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, NULL, &bp)))
return (error);
numdirpasses = 2;
nchstats.ncs_2passes++;
}
prevoff = dp->i_offset;
endsearch = roundup(DIP(dp, size), DIRBLKSIZ);
enduseful = 0;
searchloop:
while (dp->i_offset < endsearch) {
/*
* If necessary, get the next directory block.
*/
if ((dp->i_offset & bmask) == 0) {
if (bp != NULL)
brelse(bp);
error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, NULL,
&bp);
if (error)
return (error);
entryoffsetinblock = 0;
}
/*
* If still looking for a slot, and at a DIRBLKSIZE
* boundary, have to start looking for free space again.
*/
if (slotstatus == NONE &&
(entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
slotoffset = -1;
slotfreespace = 0;
}
/*
* Get pointer to next entry.
* Full validation checks are slow, so we only check
* enough to insure forward progress through the
* directory. Complete checks can be run by patching
* "dirchk" to be true.
*/
ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
if (ep->d_reclen == 0 ||
(dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) {
int i;
ufs_dirbad(dp, dp->i_offset, "mangled entry");
i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
dp->i_offset += i;
entryoffsetinblock += i;
continue;
}
/*
* If an appropriate sized slot has not yet been found,
* check to see if one is available. Also accumulate space
* in the current block so that we can determine if
* compaction is viable.
*/
if (slotstatus != FOUND) {
int size = ep->d_reclen;
if (ep->d_ino != 0)
size -= DIRSIZ(OFSFMT(dp), ep);
if (size > 0) {
if (size >= slotneeded) {
slotstatus = FOUND;
slotoffset = dp->i_offset;
slotsize = ep->d_reclen;
} else if (slotstatus == NONE) {
slotfreespace += size;
if (slotoffset == -1)
slotoffset = dp->i_offset;
if (slotfreespace >= slotneeded) {
slotstatus = COMPACT;
slotsize = dp->i_offset +
ep->d_reclen - slotoffset;
}
}
}
}
/*
* Check for a name match.
*/
if (ep->d_ino) {
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (OFSFMT(dp))
namlen = ep->d_type;
else
namlen = ep->d_namlen;
# else
namlen = ep->d_namlen;
# endif
if (namlen == cnp->cn_namelen &&
!memcmp(cnp->cn_nameptr, ep->d_name, namlen)) {
#ifdef UFS_DIRHASH
foundentry:
#endif
/*
* Save directory entry's inode number and
* reclen in ndp->ni_ufs area, and release
* directory buffer.
*/
dp->i_ino = ep->d_ino;
dp->i_reclen = ep->d_reclen;
goto found;
}
}
prevoff = dp->i_offset;
dp->i_offset += ep->d_reclen;
entryoffsetinblock += ep->d_reclen;
if (ep->d_ino)
enduseful = dp->i_offset;
}
#ifdef UFS_DIRHASH
notfound:
#endif
/*
* If we started in the middle of the directory and failed
* to find our target, we must check the beginning as well.
*/
if (numdirpasses == 2) {
numdirpasses--;
dp->i_offset = 0;
endsearch = dp->i_diroff;
goto searchloop;
}
if (bp != NULL)
brelse(bp);
/*
* If creating, and at end of pathname and current
* directory has not been removed, then can consider
* allowing file to be created.
*/
if ((nameiop == CREATE || nameiop == RENAME) &&
(flags & ISLASTCN) && dp->i_effnlink != 0) {
/*
* Access for write is interpreted as allowing
* creation of files in the directory.
*/
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
if (error)
return (error);
/*
* Return an indication of where the new directory
* entry should be put. If we didn't find a slot,
* then set dp->i_count to 0 indicating
* that the new slot belongs at the end of the
* directory. If we found a slot, then the new entry
* can be put in the range from dp->i_offset to
* dp->i_offset + dp->i_count.
*/
if (slotstatus == NONE) {
dp->i_offset = roundup(DIP(dp, size), DIRBLKSIZ);
dp->i_count = 0;
enduseful = dp->i_offset;
} else if (nameiop == DELETE) {
dp->i_offset = slotoffset;
if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
dp->i_count = 0;
else
dp->i_count = dp->i_offset - prevoff;
} else {
dp->i_offset = slotoffset;
dp->i_count = slotsize;
if (enduseful < slotoffset + slotsize)
enduseful = slotoffset + slotsize;
}
dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
/*
* We return with the directory locked, so that
* the parameters we set up above will still be
* valid if we actually decide to do a direnter().
* We return ni_vp == NULL to indicate that the entry
* does not currently exist; we leave a pointer to
* the (locked) directory inode in ndp->ni_dvp.
* The pathname buffer is saved so that the name
* can be obtained later.
*
* NB - if the directory is unlocked, then this
* information cannot be used.
*/
cnp->cn_flags |= SAVENAME;
if (!lockparent) {
VOP_UNLOCK(vdp);
cnp->cn_flags |= PDIRUNLOCK;
}
return (EJUSTRETURN);
}
/*
* Insert name into cache (as non-existent) if appropriate.
*/
if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
cache_enter(vdp, *vpp, cnp);
return (ENOENT);
found:
if (numdirpasses == 2)
nchstats.ncs_pass2++;
/*
* Check that directory length properly reflects presence
* of this entry.
*/
if (dp->i_offset + DIRSIZ(OFSFMT(dp), ep) > DIP(dp, size)) {
ufs_dirbad(dp, dp->i_offset, "i_ffs_size too small");
DIP_ASSIGN(dp, size, dp->i_offset + DIRSIZ(OFSFMT(dp), ep));
dp->i_flag |= IN_CHANGE | IN_UPDATE;
}
brelse(bp);
/*
* Found component in pathname.
* If the final component of path name, save information
* in the cache as to where the entry was found.
*/
if ((flags & ISLASTCN) && nameiop == LOOKUP)
dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
/*
* If deleting, and at end of pathname, return
* parameters which can be used to remove file.
* If the wantparent flag isn't set, we return only
* the directory (in ndp->ni_dvp), otherwise we go
* on and lock the inode, being careful with ".".
*/
if (nameiop == DELETE && (flags & ISLASTCN)) {
/*
* Write access to directory required to delete files.
*/
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
if (error)
return (error);
/*
* Return pointer to current entry in dp->i_offset,
* and distance past previous entry (if there
* is a previous entry in this block) in dp->i_count.
* Save directory inode pointer in ndp->ni_dvp for dirremove().
*/
if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
dp->i_count = 0;
else
dp->i_count = dp->i_offset - prevoff;
if (dp->i_number == dp->i_ino) {
vref(vdp);
*vpp = vdp;
return (0);
}
error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
if (error)
return (error);
/*
* If directory is "sticky", then user must own
* the directory, or the file in it, else she
* may not delete it (unless she's root). This
* implements append-only directories.
*/
if ((DIP(dp, mode) & ISVTX) &&
cred->cr_uid != 0 &&
cred->cr_uid != DIP(dp, uid) &&
!vnoperm(vdp) &&
DIP(VTOI(tdp), uid) != cred->cr_uid) {
vput(tdp);
return (EPERM);
}
*vpp = tdp;
if (!lockparent) {
VOP_UNLOCK(vdp);
cnp->cn_flags |= PDIRUNLOCK;
}
return (0);
}
/*
* If rewriting (RENAME), return the inode and the
* information required to rewrite the present directory
* Must get inode of directory entry to verify it's a
* regular file, or empty directory.
*/
if (nameiop == RENAME && wantparent &&
(flags & ISLASTCN)) {
error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
if (error)
return (error);
/*
* Careful about locking second inode.
* This can only occur if the target is ".".
*/
if (dp->i_number == dp->i_ino)
return (EISDIR);
error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
if (error)
return (error);
*vpp = tdp;
cnp->cn_flags |= SAVENAME;
if (!lockparent) {
VOP_UNLOCK(vdp);
cnp->cn_flags |= PDIRUNLOCK;
}
return (0);
}
/*
* Step through the translation in the name. We do not `vput' the
* directory because we may need it again if a symbolic link
* is relative to the current directory. Instead we save it
* unlocked as "pdp". We must get the target inode before unlocking
* the directory to insure that the inode will not be removed
* before we get it. We prevent deadlock by always fetching
* inodes from the root, moving down the directory tree. Thus
* when following backward pointers ".." we must unlock the
* parent directory before getting the requested directory.
* There is a potential race condition here if both the current
* and parent directories are removed before the VFS_VGET for the
* inode associated with ".." returns. We hope that this occurs
* infrequently since we cannot avoid this race condition without
* implementing a sophisticated deadlock detection algorithm.
* Note also that this simple deadlock detection scheme will not
* work if the file system has any hard links other than ".."
* that point backwards in the directory structure.
*/
pdp = vdp;
if (flags & ISDOTDOT) {
VOP_UNLOCK(pdp); /* race to get the inode */
cnp->cn_flags |= PDIRUNLOCK;
error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
if (error) {
if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0)
cnp->cn_flags &= ~PDIRUNLOCK;
return (error);
}
if (lockparent && (flags & ISLASTCN)) {
if ((error = vn_lock(pdp, LK_EXCLUSIVE))) {
vput(tdp);
return (error);
}
cnp->cn_flags &= ~PDIRUNLOCK;
}
*vpp = tdp;
} else if (dp->i_number == dp->i_ino) {
vref(vdp); /* we want ourself, ie "." */
*vpp = vdp;
} else {
error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
if (error)
return (error);
if (!lockparent || !(flags & ISLASTCN)) {
VOP_UNLOCK(pdp);
cnp->cn_flags |= PDIRUNLOCK;
}
*vpp = tdp;
}
/*
* Insert name into cache if appropriate.
*/
if (cnp->cn_flags & MAKEENTRY)
cache_enter(vdp, *vpp, cnp);
return (0);
}
void
ufs_dirbad(struct inode *ip, doff_t offset, char *how)
{
struct mount *mp;
mp = ITOV(ip)->v_mount;
(void)printf("%s: bad dir ino %u at offset %d: %s\n",
mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
panic("bad dir");
}
/*
* Do consistency checking on a directory entry:
* record length must be multiple of 4
* entry must fit in rest of its DIRBLKSIZ block
* record must be large enough to contain entry
* name is not longer than MAXNAMLEN
* name must be as long as advertised, and null terminated
*/
int
ufs_dirbadentry(struct vnode *vdp, struct direct *ep, int entryoffsetinblock)
{
struct inode *dp;
int i;
int namlen;
dp = VTOI(vdp);
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (OFSFMT(dp))
namlen = ep->d_type;
else
namlen = ep->d_namlen;
# else
namlen = ep->d_namlen;
# endif
if ((ep->d_reclen & 0x3) != 0 ||
ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
ep->d_reclen < DIRSIZ(OFSFMT(dp), ep) || namlen > MAXNAMLEN) {
/*return (1); */
printf("First bad\n");
goto bad;
}
if (ep->d_ino == 0)
return (0);
for (i = 0; i < namlen; i++)
if (ep->d_name[i] == '\0') {
/*return (1); */
printf("Second bad\n");
goto bad;
}
if (ep->d_name[i])
goto bad;
return (0);
bad:
return (1);
}
/*
* Construct a new directory entry after a call to namei, using the
* parameters that it left in the componentname argument cnp. The
* argument ip is the inode to which the new directory entry will refer.
*/
void
ufs_makedirentry(struct inode *ip, struct componentname *cnp,
struct direct *newdirp)
{
#ifdef DIAGNOSTIC
if ((cnp->cn_flags & SAVENAME) == 0)
panic("ufs_makedirentry: missing name");
#endif
newdirp->d_ino = ip->i_number;
newdirp->d_namlen = cnp->cn_namelen;
memset(newdirp->d_name + (cnp->cn_namelen & ~(DIR_ROUNDUP-1)),
0, DIR_ROUNDUP);
memcpy(newdirp->d_name, cnp->cn_nameptr, cnp->cn_namelen);
if (OFSFMT(ip)) {
newdirp->d_type = 0;
# if (BYTE_ORDER == LITTLE_ENDIAN)
{ u_char tmp = newdirp->d_namlen;
newdirp->d_namlen = newdirp->d_type;
newdirp->d_type = tmp; }
# endif
} else
newdirp->d_type = IFTODT(DIP(ip, mode));
}
/*
* Write a directory entry after a call to namei, using the parameters
* that it left in nameidata. The argument dirp is the new directory
* entry contents. Dvp is a pointer to the directory to be written,
* which was left locked by namei. Remaining parameters (dp->i_offset,
* dp->i_count) indicate how the space for the new entry is to be obtained.
* Non-null bp indicates that a directory is being created (for the
* soft dependency code).
*/
int
ufs_direnter(struct vnode *dvp, struct vnode *tvp, struct direct *dirp,
struct componentname *cnp, struct buf *newdirbp)
{
struct ucred *cr;
struct proc *p;
int newentrysize;
struct inode *dp;
struct buf *bp;
u_int dsize;
struct direct *ep, *nep;
int error, ret, blkoff, loc, spacefree, flags;
char *dirbuf;
error = 0;
cr = cnp->cn_cred;
p = cnp->cn_proc;
dp = VTOI(dvp);
newentrysize = DIRSIZ(OFSFMT(dp), dirp);
if (dp->i_count == 0) {
/*
* If dp->i_count is 0, then namei could find no
* space in the directory. Here, dp->i_offset will
* be on a directory block boundary and we will write the
* new entry into a fresh block.
*/
if (dp->i_offset & (DIRBLKSIZ - 1))
panic("ufs_direnter: newblk");
flags = B_CLRBUF;
if (!DOINGSOFTDEP(dvp))
flags |= B_SYNC;
if ((error = UFS_BUF_ALLOC(dp, (off_t)dp->i_offset, DIRBLKSIZ,
cr, flags, &bp)) != 0) {
if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
bdwrite(newdirbp);
return (error);
}
DIP_ASSIGN(dp, size, dp->i_offset + DIRBLKSIZ);
dp->i_flag |= IN_CHANGE | IN_UPDATE;
uvm_vnp_setsize(dvp, DIP(dp, size));
dirp->d_reclen = DIRBLKSIZ;
blkoff = dp->i_offset &
(VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1);
memcpy(bp->b_data + blkoff, dirp, newentrysize);
#ifdef UFS_DIRHASH
if (dp->i_dirhash != NULL) {
ufsdirhash_newblk(dp, dp->i_offset);
ufsdirhash_add(dp, dirp, dp->i_offset);
ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff,
dp->i_offset);
}
#endif
if (DOINGSOFTDEP(dvp)) {
/*
* Ensure that the entire newly allocated block is a
* valid directory so that future growth within the
* block does not have to ensure that the block is
* written before the inode.
*/
blkoff += DIRBLKSIZ;
while (blkoff < bp->b_bcount) {
((struct direct *)
(bp->b_data + blkoff))->d_reclen = DIRBLKSIZ;
blkoff += DIRBLKSIZ;
}
if (softdep_setup_directory_add(bp, dp, dp->i_offset,
dirp->d_ino, newdirbp, 1) == 0) {
bdwrite(bp);
return (UFS_UPDATE(dp, 0));
}
/* We have just allocated a directory block in an
* indirect block. Rather than tracking when it gets
* claimed by the inode, we simply do a VOP_FSYNC
* now to ensure that it is there (in case the user
* does a future fsync). Note that we have to unlock
* the inode for the entry that we just entered, as
* the VOP_FSYNC may need to lock other inodes which
* can lead to deadlock if we also hold a lock on
* the newly entered node.
*/
if ((error = VOP_BWRITE(bp)))
return (error);
if (tvp != NULL)
VOP_UNLOCK(tvp);
error = VOP_FSYNC(dvp, p->p_ucred, MNT_WAIT, p);
if (tvp != NULL)
vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
return (error);
}
error = VOP_BWRITE(bp);
ret = UFS_UPDATE(dp, !DOINGSOFTDEP(dvp));
if (error == 0)
return (ret);
return (error);
}
/*
* If dp->i_count is non-zero, then namei found space for the new
* entry in the range dp->i_offset to dp->i_offset + dp->i_count
* in the directory. To use this space, we may have to compact
* the entries located there, by copying them together towards the
* beginning of the block, leaving the free space in one usable
* chunk at the end.
*/
/*
* Increase size of directory if entry eats into new space.
* This should never push the size past a new multiple of
* DIRBLKSIZE.
*
* N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
*/
if (dp->i_offset + dp->i_count > DIP(dp, size))
DIP_ASSIGN(dp, size, dp->i_offset + dp->i_count);
/*
* Get the block containing the space for the new directory entry.
*/
if ((error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, &dirbuf, &bp))
!= 0) {
if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
bdwrite(newdirbp);
return (error);
}
/*
* Find space for the new entry. In the simple case, the entry at
* offset base will have the space. If it does not, then namei
* arranged that compacting the region dp->i_offset to
* dp->i_offset + dp->i_count would yield the space.
*/
ep = (struct direct *)dirbuf;
dsize = ep->d_ino ? DIRSIZ(OFSFMT(dp), ep) : 0;
spacefree = ep->d_reclen - dsize;
for (loc = ep->d_reclen; loc < dp->i_count; ) {
nep = (struct direct *)(dirbuf + loc);
/* Trim the existing slot (NB: dsize may be zero). */
ep->d_reclen = dsize;
ep = (struct direct *)((char *)ep + dsize);
/* Read nep->d_reclen now as the memmove() may clobber it. */
loc += nep->d_reclen;
if (nep->d_ino == 0) {
/*
* A mid-block unused entry. Such entries are
* never created by the kernel, but fsck_ffs
* can create them (and it doesn't fix them).
*
* Add up the free space, and initialise the
* relocated entry since we don't memmove it.
*/
spacefree += nep->d_reclen;
ep->d_ino = 0;
dsize = 0;
continue;
}
dsize = DIRSIZ(OFSFMT(dp), nep);
spacefree += nep->d_reclen - dsize;
#ifdef UFS_DIRHASH
if (dp->i_dirhash != NULL)
ufsdirhash_move(dp, nep,
dp->i_offset + ((char *)nep - dirbuf),
dp->i_offset + ((char *)ep - dirbuf));
#endif
if (DOINGSOFTDEP(dvp))
softdep_change_directoryentry_offset(dp, dirbuf,
(caddr_t)nep, (caddr_t)ep, dsize);
else
memmove(ep, nep, dsize);
}
/*
* Here, `ep' points to a directory entry containing `dsize' in-use
* bytes followed by `spacefree' unused bytes. If ep->d_ino == 0,
* then the entry is completely unused (dsize == 0). The value
* of ep->d_reclen is always indeterminate.
*
* Update the pointer fields in the previous entry (if any),
* copy in the new entry, and write out the block.
*/
if (ep->d_ino == 0) {
if (spacefree + dsize < newentrysize)
panic("ufs_direnter: compact1");
dirp->d_reclen = spacefree + dsize;
} else {
if (spacefree < newentrysize)
panic("ufs_direnter: compact2");
dirp->d_reclen = spacefree;
ep->d_reclen = dsize;
ep = (struct direct *)((char *)ep + dsize);
}
#ifdef UFS_DIRHASH
if (dp->i_dirhash != NULL && (ep->d_ino == 0 ||
dirp->d_reclen == spacefree))
ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf));
#endif
memcpy(ep, dirp, newentrysize);
#ifdef UFS_DIRHASH
if (dp->i_dirhash != NULL)
ufsdirhash_checkblock(dp, dirbuf -
(dp->i_offset & (DIRBLKSIZ - 1)),
dp->i_offset & ~(DIRBLKSIZ - 1));
#endif
if (DOINGSOFTDEP(dvp)) {
(void)softdep_setup_directory_add(bp, dp,
dp->i_offset + (caddr_t)ep - dirbuf,
dirp->d_ino, newdirbp, 0);
bdwrite(bp);
} else {
error = VOP_BWRITE(bp);
}
dp->i_flag |= IN_CHANGE | IN_UPDATE;
/*
* If all went well, and the directory can be shortened, proceed
* with the truncation. Note that we have to unlock the inode for
* the entry that we just entered, as the truncation may need to
* lock other inodes which can lead to deadlock if we also hold a
* lock on the newly entered node.
*/
if (error == 0 && dp->i_endoff && dp->i_endoff < DIP(dp, size)) {
if (tvp != NULL)
VOP_UNLOCK(tvp);
error = UFS_TRUNCATE(dp, (off_t)dp->i_endoff, IO_SYNC, cr);
#ifdef UFS_DIRHASH
if (error == 0 && dp->i_dirhash != NULL)
ufsdirhash_dirtrunc(dp, dp->i_endoff);
#endif
if (tvp != NULL)
vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
}
return (error);
}
/*
* Remove a directory entry after a call to namei, using
* the parameters which it left in nameidata. The entry
* dp->i_offset contains the offset into the directory of the
* entry to be eliminated. The dp->i_count field contains the
* size of the previous record in the directory. If this
* is 0, the first entry is being deleted, so we need only
* zero the inode number to mark the entry as free. If the
* entry is not the first in the directory, we must reclaim
* the space of the now empty record by adding the record size
* to the size of the previous entry.
*/
int
ufs_dirremove(struct vnode *dvp, struct inode *ip, int flags, int isrmdir)
{
struct inode *dp;
struct direct *ep;
struct buf *bp;
int error;
dp = VTOI(dvp);
if ((error = UFS_BUFATOFF(dp,
(off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0)
return (error);
#ifdef UFS_DIRHASH
/*
* Remove the dirhash entry. This is complicated by the fact
* that `ep' is the previous entry when dp->i_count != 0.
*/
if (dp->i_dirhash != NULL)
ufsdirhash_remove(dp, (dp->i_count == 0) ? ep :
(struct direct *)((char *)ep + ep->d_reclen), dp->i_offset);
#endif
if (dp->i_count == 0) {
/*
* First entry in block: set d_ino to zero.
*/
ep->d_ino = 0;
} else {
/*
* Collapse new free space into previous entry.
*/
ep->d_reclen += dp->i_reclen;
}
#ifdef UFS_DIRHASH
if (dp->i_dirhash != NULL)
ufsdirhash_checkblock(dp, (char *)ep -
((dp->i_offset - dp->i_count) & (DIRBLKSIZ - 1)),
dp->i_offset & ~(DIRBLKSIZ - 1));
#endif
if (DOINGSOFTDEP(dvp)) {
if (ip) {
ip->i_effnlink--;
softdep_change_linkcnt(ip, 0);
softdep_setup_remove(bp, dp, ip, isrmdir);
}
if (softdep_slowdown(dvp)) {
error = bwrite(bp);
} else {
bdwrite(bp);
error = 0;
}
} else {
if (ip) {
ip->i_effnlink--;
DIP_ADD(ip, nlink, -1);
ip->i_flag |= IN_CHANGE;
}
if (DOINGASYNC(dvp) && dp->i_count != 0) {
bdwrite(bp);
error = 0;
} else
error = bwrite(bp);
}
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return (error);
}
/*
* Rewrite an existing directory entry to point at the inode
* supplied. The parameters describing the directory entry are
* set up by a call to namei.
*/
int
ufs_dirrewrite(struct inode *dp, struct inode *oip, ufsino_t newinum,
int newtype, int isrmdir)
{
struct buf *bp;
struct direct *ep;
struct vnode *vdp = ITOV(dp);
int error;
error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, (char **)&ep, &bp);
if (error)
return (error);
ep->d_ino = newinum;
if (!OFSFMT(dp))
ep->d_type = newtype;
oip->i_effnlink--;
if (DOINGSOFTDEP(vdp)) {
softdep_change_linkcnt(oip, 0);
softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir);
bdwrite(bp);
} else {
DIP_ADD(oip, nlink, -1);
oip->i_flag |= IN_CHANGE;
if (DOINGASYNC(vdp)) {
bdwrite(bp);
error = 0;
} else {
error = VOP_BWRITE(bp);
}
}
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return (error);
}
/*
* Check if a directory is empty or not.
* Inode supplied must be locked.
*
* Using a struct dirtemplate here is not precisely
* what we want, but better than using a struct direct.
*
* NB: does not handle corrupted directories.
*/
int
ufs_dirempty(struct inode *ip, ufsino_t parentino, struct ucred *cred)
{
off_t off, m;
struct dirtemplate dbuf;
struct direct *dp = (struct direct *)&dbuf;
int error, namlen;
size_t count;
#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
m = DIP(ip, size);
for (off = 0; off < m; off += dp->d_reclen) {
error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
UIO_SYSSPACE, IO_NODELOCKED, cred, &count, curproc);
/*
* Since we read MINDIRSIZ, residual must
* be 0 unless we're at end of file.
*/
if (error || count != 0)
return (0);
/* avoid infinite loops */
if (dp->d_reclen == 0)
return (0);
/* skip empty entries */
if (dp->d_ino == 0)
continue;
/* accept only "." and ".." */
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (OFSFMT(ip))
namlen = dp->d_type;
else
namlen = dp->d_namlen;
# else
namlen = dp->d_namlen;
# endif
if (namlen > 2)
return (0);
if (dp->d_name[0] != '.')
return (0);
/*
* At this point namlen must be 1 or 2.
* 1 implies ".", 2 implies ".." if second
* char is also "."
*/
if (namlen == 1 && dp->d_ino == ip->i_number)
continue;
if (dp->d_name[1] == '.' && dp->d_ino == parentino)
continue;
return (0);
}
return (1);
}
/*
* Check if source directory is in the path of the target directory.
* Target is supplied locked, source is unlocked.
* The target is always vput before returning.
*/
int
ufs_checkpath(struct inode *source, struct inode *target, struct ucred *cred)
{
struct vnode *nextvp, *vp;
int error, rootino, namlen;
struct dirtemplate dirbuf;
vp = ITOV(target);
if (target->i_number == source->i_number) {
error = EEXIST;
goto out;
}
rootino = ROOTINO;
error = 0;
if (target->i_number == rootino)
goto out;
for (;;) {
if (vp->v_type != VDIR) {
error = ENOTDIR;
break;
}
error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
IO_NODELOCKED, cred, NULL, curproc);
if (error != 0)
break;
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (OFSFMT(VTOI(vp)))
namlen = dirbuf.dotdot_type;
else
namlen = dirbuf.dotdot_namlen;
# else
namlen = dirbuf.dotdot_namlen;
# endif
if (namlen != 2 ||
dirbuf.dotdot_name[0] != '.' ||
dirbuf.dotdot_name[1] != '.') {
error = ENOTDIR;
break;
}
if (dirbuf.dotdot_ino == source->i_number) {
error = EINVAL;
break;
}
if (dirbuf.dotdot_ino == rootino)
break;
VOP_UNLOCK(vp);
error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &nextvp);
vrele(vp);
if (error) {
vp = NULL;
break;
}
vp = nextvp;
}
out:
if (error == ENOTDIR)
printf("checkpath: .. not a directory\n");
if (vp != NULL)
vput(vp);
return (error);
}
3
11
2
2
2
7
3
13
9
1
2
4
5
2
3
6
85
84
1
2
3
2
74
6
1
2
1
75
5
36
28
3
11
31
4
7
36
41
9
66
1
70
36
42
1
70
247
123
365
331
38
365
1
155
145
7
2
194
4
27
208
2
11
11
2
218
217
16
10
26
1
8
2
15
14
3
12
2
7
1
1
1
1
7
4
4
14
6
20
1
5
2
1
2
9
12
10
22
3
7
1
2
9
13
4
40
1
5
3
1
1
15
14
2
3
8
2
2
3
2
7
8
15
15
1
9
5
1
1
8
4
8
6
5
13
2
7
4
8
3
8
5
4
9
2
2
5
5
3
1
2
5
8
5
10
10
5
5
14
4
18
2
1
3
13
9
1
4
4
9
5
14
1
3
1
9
11
1
1
9
11
5
1
4
5
17
2
7
3
5
6
4
7
19
2
7
9
4
13
2
3
5
9
16
16
9
4
12
1
3
1
4
5
3
13
16
16
9
2
7
11
2
3
6
8
1
4
11
8
11
7
7
47
17
65
7
12
17
21
11
6
16
25
1
7
1
7
58
43
6
48
8
1
1
40
23
15
1
1
1
4
4
10
22
20
9
8
15
1
4
1
5
13
12
32
28
579
578
66
61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
/* $OpenBSD: vfs_syscalls.c,v 1.360 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_syscalls.c 8.28 (Berkeley) 12/10/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/filedesc.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/lock.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/pledge.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/ktrace.h>
#include <sys/unistd.h>
#include <sys/specdev.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/syscallargs.h>
extern int suid_clear;
static int change_dir(struct nameidata *, struct proc *);
void checkdirs(struct vnode *);
int copyout_statfs(struct statfs *, void *, struct proc *);
int doopenat(struct proc *, int, const char *, int, mode_t, register_t *);
int domknodat(struct proc *, int, const char *, mode_t, dev_t);
int dolinkat(struct proc *, int, const char *, int, const char *, int);
int dosymlinkat(struct proc *, const char *, int, const char *);
int dounlinkat(struct proc *, int, const char *, int);
int dofaccessat(struct proc *, int, const char *, int, int);
int dofstatat(struct proc *, int, const char *, struct stat *, int);
int doreadlinkat(struct proc *, int, const char *, char *, size_t,
register_t *);
int dochflagsat(struct proc *, int, const char *, u_int, int);
int dovchflags(struct proc *, struct vnode *, u_int);
int dofchmodat(struct proc *, int, const char *, mode_t, int);
int dofchownat(struct proc *, int, const char *, uid_t, gid_t, int);
int dorenameat(struct proc *, int, const char *, int, const char *);
int domkdirat(struct proc *, int, const char *, mode_t);
int doutimensat(struct proc *, int, const char *, struct timespec [2], int);
int dovutimens(struct proc *, struct vnode *, struct timespec [2]);
int dofutimens(struct proc *, int, struct timespec [2]);
int dounmount_leaf(struct mount *, int, struct proc *);
/*
* Virtual File System System Calls
*/
/*
* Mount a file system.
*/
int
sys_mount(struct proc *p, void *v, register_t *retval)
{
struct sys_mount_args /* {
syscallarg(const char *) type;
syscallarg(const char *) path;
syscallarg(int) flags;
syscallarg(void *) data;
} */ *uap = v;
struct vnode *vp;
struct mount *mp;
int error, mntflag = 0;
char fstypename[MFSNAMELEN];
char fspath[MNAMELEN];
struct nameidata nd;
struct vfsconf *vfsp;
int flags = SCARG(uap, flags);
void *args = NULL;
if ((error = suser(p)))
return (error);
/*
* Mount points must fit in MNAMELEN, not MAXPATHLEN.
*/
error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL);
if (error)
return(error);
/*
* Get vnode to be covered
*/
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p);
if ((error = namei(&nd)) != 0)
goto fail;
vp = nd.ni_vp;
if (flags & MNT_UPDATE) {
if ((vp->v_flag & VROOT) == 0) {
vput(vp);
error = EINVAL;
goto fail;
}
mp = vp->v_mount;
vfsp = mp->mnt_vfc;
args = malloc(vfsp->vfc_datasize, M_TEMP, M_WAITOK | M_ZERO);
error = copyin(SCARG(uap, data), args, vfsp->vfc_datasize);
if (error) {
vput(vp);
goto fail;
}
mntflag = mp->mnt_flag;
/*
* We only allow the filesystem to be reloaded if it
* is currently mounted read-only.
*/
if ((flags & MNT_RELOAD) &&
((mp->mnt_flag & MNT_RDONLY) == 0)) {
vput(vp);
error = EOPNOTSUPP; /* Needs translation */
goto fail;
}
if ((error = vfs_busy(mp, VB_READ|VB_NOWAIT)) != 0) {
vput(vp);
goto fail;
}
mp->mnt_flag |= flags & (MNT_RELOAD | MNT_UPDATE);
goto update;
}
/*
* Do not allow disabling of permission checks unless exec and access to
* device files is disabled too.
*/
if ((flags & MNT_NOPERM) &&
(flags & (MNT_NODEV | MNT_NOEXEC)) != (MNT_NODEV | MNT_NOEXEC)) {
vput(vp);
error = EPERM;
goto fail;
}
if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, INFSLP)) != 0) {
vput(vp);
goto fail;
}
if (vp->v_type != VDIR) {
vput(vp);
goto fail;
}
error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL);
if (error) {
vput(vp);
goto fail;
}
vfsp = vfs_byname(fstypename);
if (vfsp == NULL) {
vput(vp);
error = EOPNOTSUPP;
goto fail;
}
args = malloc(vfsp->vfc_datasize, M_TEMP, M_WAITOK | M_ZERO);
error = copyin(SCARG(uap, data), args, vfsp->vfc_datasize);
if (error) {
vput(vp);
goto fail;
}
if (vp->v_mountedhere != NULL) {
vput(vp);
error = EBUSY;
goto fail;
}
/*
* Allocate and initialize the file system.
*/
mp = vfs_mount_alloc(vp, vfsp);
mp->mnt_stat.f_owner = p->p_ucred->cr_uid;
update:
/* Ensure that the parent mountpoint does not get unmounted. */
error = vfs_busy(vp->v_mount, VB_READ|VB_NOWAIT|VB_DUPOK);
if (error) {
if (mp->mnt_flag & MNT_UPDATE) {
mp->mnt_flag = mntflag;
vfs_unbusy(mp);
} else {
vfs_unbusy(mp);
vfs_mount_free(mp);
}
vput(vp);
goto fail;
}
/*
* Set the mount level flags.
*/
if (flags & MNT_RDONLY)
mp->mnt_flag |= MNT_RDONLY;
else if (mp->mnt_flag & MNT_RDONLY)
mp->mnt_flag |= MNT_WANTRDWR;
mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_WXALLOWED | MNT_NODEV |
MNT_SYNCHRONOUS | MNT_ASYNC | MNT_SOFTDEP | MNT_NOATIME |
MNT_NOPERM | MNT_FORCE);
mp->mnt_flag |= flags & (MNT_NOSUID | MNT_NOEXEC | MNT_WXALLOWED |
MNT_NODEV | MNT_SYNCHRONOUS | MNT_ASYNC | MNT_SOFTDEP |
MNT_NOATIME | MNT_NOPERM | MNT_FORCE);
/*
* Mount the filesystem.
*/
error = VFS_MOUNT(mp, fspath, args, &nd, p);
if (!error) {
mp->mnt_stat.f_ctime = gettime();
}
if (mp->mnt_flag & MNT_UPDATE) {
vfs_unbusy(vp->v_mount);
vput(vp);
if (mp->mnt_flag & MNT_WANTRDWR)
mp->mnt_flag &= ~MNT_RDONLY;
mp->mnt_flag &= ~MNT_OP_FLAGS;
if (error)
mp->mnt_flag = mntflag;
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
if (mp->mnt_syncer == NULL)
error = vfs_allocate_syncvnode(mp);
} else {
if (mp->mnt_syncer != NULL)
vgone(mp->mnt_syncer);
mp->mnt_syncer = NULL;
}
vfs_unbusy(mp);
goto fail;
}
mp->mnt_flag &= ~MNT_OP_FLAGS;
vp->v_mountedhere = mp;
/*
* Put the new filesystem on the mount list after root.
*/
cache_purge(vp);
if (!error) {
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
checkdirs(vp);
vfs_unbusy(vp->v_mount);
VOP_UNLOCK(vp);
if ((mp->mnt_flag & MNT_RDONLY) == 0)
error = vfs_allocate_syncvnode(mp);
vfs_unbusy(mp);
(void) VFS_STATFS(mp, &mp->mnt_stat, p);
if ((error = VFS_START(mp, 0, p)) != 0)
vrele(vp);
} else {
mp->mnt_vnodecovered->v_mountedhere = NULL;
vfs_unbusy(mp);
vfs_mount_free(mp);
vfs_unbusy(vp->v_mount);
vput(vp);
}
fail:
if (args)
free(args, M_TEMP, vfsp->vfc_datasize);
return (error);
}
/*
* Scan all active processes to see if any of them have a current
* or root directory onto which the new filesystem has just been
* mounted. If so, replace them with the new mount point, keeping
* track of how many were replaced. That's the number of references
* the old vnode had that we've replaced, so finish by vrele()'ing
* it that many times. This puts off any possible sleeping until
* we've finished walking the allprocess list.
*/
void
checkdirs(struct vnode *olddp)
{
struct filedesc *fdp;
struct vnode *newdp;
struct process *pr;
u_int free_count = 0;
if (olddp->v_usecount == 1)
return;
if (VFS_ROOT(olddp->v_mountedhere, &newdp))
panic("mount: lost mount");
LIST_FOREACH(pr, &allprocess, ps_list) {
fdp = pr->ps_fd;
if (fdp->fd_cdir == olddp) {
free_count++;
vref(newdp);
fdp->fd_cdir = newdp;
}
if (fdp->fd_rdir == olddp) {
free_count++;
vref(newdp);
fdp->fd_rdir = newdp;
}
}
if (rootvnode == olddp) {
free_count++;
vref(newdp);
rootvnode = newdp;
}
while (free_count-- > 0)
vrele(olddp);
vput(newdp);
}
/*
* Unmount a file system.
*
* Note: unmount takes a path to the vnode mounted on as argument,
* not special file (as before).
*/
int
sys_unmount(struct proc *p, void *v, register_t *retval)
{
struct sys_unmount_args /* {
syscallarg(const char *) path;
syscallarg(int) flags;
} */ *uap = v;
struct vnode *vp;
struct mount *mp;
int error;
struct nameidata nd;
if ((error = suser(p)) != 0)
return (error);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
SCARG(uap, path), p);
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
mp = vp->v_mount;
/*
* Don't allow unmounting the root file system.
*/
if (mp->mnt_flag & MNT_ROOTFS) {
vput(vp);
return (EINVAL);
}
/*
* Must be the root of the filesystem
*/
if ((vp->v_flag & VROOT) == 0) {
vput(vp);
return (EINVAL);
}
vput(vp);
if (vfs_busy(mp, VB_WRITE|VB_WAIT))
return (EBUSY);
return (dounmount(mp, SCARG(uap, flags) & MNT_FORCE, p));
}
/*
* Do the actual file system unmount.
*/
int
dounmount(struct mount *mp, int flags, struct proc *p)
{
SLIST_HEAD(, mount) mplist;
struct mount *nmp;
int error;
SLIST_INIT(&mplist);
SLIST_INSERT_HEAD(&mplist, mp, mnt_dounmount);
/*
* Collect nested mount points. This takes advantage of the mount list
* being ordered - nested mount points come after their parent.
*/
while ((mp = TAILQ_NEXT(mp, mnt_list)) != NULL) {
SLIST_FOREACH(nmp, &mplist, mnt_dounmount) {
if (mp->mnt_vnodecovered == NULLVP ||
mp->mnt_vnodecovered->v_mount != nmp)
continue;
if ((flags & MNT_FORCE) == 0) {
error = EBUSY;
goto err;
}
error = vfs_busy(mp, VB_WRITE|VB_WAIT|VB_DUPOK);
if (error) {
if ((flags & MNT_DOOMED)) {
/*
* If the mount point was busy due to
* being unmounted, it has been removed
* from the mount list already.
* Restart the iteration from the last
* collected busy entry.
*/
mp = SLIST_FIRST(&mplist);
break;
}
goto err;
}
SLIST_INSERT_HEAD(&mplist, mp, mnt_dounmount);
break;
}
}
/*
* Nested mount points cannot appear during this loop as mounting
* requires a read lock for the parent mount point.
*/
while ((mp = SLIST_FIRST(&mplist)) != NULL) {
SLIST_REMOVE(&mplist, mp, mount, mnt_dounmount);
error = dounmount_leaf(mp, flags, p);
if (error)
goto err;
}
return (0);
err:
while ((mp = SLIST_FIRST(&mplist)) != NULL) {
SLIST_REMOVE(&mplist, mp, mount, mnt_dounmount);
vfs_unbusy(mp);
}
return (error);
}
int
dounmount_leaf(struct mount *mp, int flags, struct proc *p)
{
struct vnode *coveredvp;
struct vnode *vp, *nvp;
int error;
int hadsyncer = 0;
mp->mnt_flag &=~ MNT_ASYNC;
cache_purgevfs(mp); /* remove cache entries for this file sys */
if (mp->mnt_syncer != NULL) {
hadsyncer = 1;
vgone(mp->mnt_syncer);
mp->mnt_syncer = NULL;
}
/*
* Before calling file system unmount, make sure
* all unveils to vnodes in here are dropped.
*/
TAILQ_FOREACH_SAFE(vp , &mp->mnt_vnodelist, v_mntvnodes, nvp) {
unveil_removevnode(vp);
}
if (((mp->mnt_flag & MNT_RDONLY) ||
(error = VFS_SYNC(mp, MNT_WAIT, 0, p->p_ucred, p)) == 0) ||
(flags & MNT_FORCE))
error = VFS_UNMOUNT(mp, flags, p);
if (error && !(flags & MNT_DOOMED)) {
if ((mp->mnt_flag & MNT_RDONLY) == 0 && hadsyncer)
(void) vfs_allocate_syncvnode(mp);
vfs_unbusy(mp);
return (error);
}
TAILQ_REMOVE(&mountlist, mp, mnt_list);
if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) {
coveredvp->v_mountedhere = NULL;
vrele(coveredvp);
}
if (!TAILQ_EMPTY(&mp->mnt_vnodelist))
panic("unmount: dangling vnode");
vfs_unbusy(mp);
vfs_mount_free(mp);
return (0);
}
/*
* Sync each mounted filesystem.
*/
int
sys_sync(struct proc *p, void *v, register_t *retval)
{
struct mount *mp;
int asyncflag;
TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
if (vfs_busy(mp, VB_READ|VB_NOWAIT))
continue;
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
asyncflag = mp->mnt_flag & MNT_ASYNC;
mp->mnt_flag &= ~MNT_ASYNC;
uvm_vnp_sync(mp);
VFS_SYNC(mp, MNT_NOWAIT, 0, p->p_ucred, p);
if (asyncflag)
mp->mnt_flag |= MNT_ASYNC;
}
vfs_unbusy(mp);
}
return (0);
}
/*
* Change filesystem quotas.
*/
int
sys_quotactl(struct proc *p, void *v, register_t *retval)
{
struct sys_quotactl_args /* {
syscallarg(const char *) path;
syscallarg(int) cmd;
syscallarg(int) uid;
syscallarg(char *) arg;
} */ *uap = v;
struct mount *mp;
int error;
struct nameidata nd;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
if ((error = namei(&nd)) != 0)
return (error);
mp = nd.ni_vp->v_mount;
vrele(nd.ni_vp);
return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid),
SCARG(uap, arg), p));
}
int
copyout_statfs(struct statfs *sp, void *uaddr, struct proc *p)
{
size_t co_sz1 = offsetof(struct statfs, f_fsid);
size_t co_off2 = co_sz1 + sizeof(fsid_t);
size_t co_sz2 = sizeof(struct statfs) - co_off2;
char *s, *d;
int error;
/* Don't let non-root see filesystem id (for NFS security) */
if (suser(p)) {
fsid_t fsid;
s = (char *)sp;
d = (char *)uaddr;
memset(&fsid, 0, sizeof(fsid));
if ((error = copyout(s, d, co_sz1)) != 0)
return (error);
if ((error = copyout(&fsid, d + co_sz1, sizeof(fsid))) != 0)
return (error);
return (copyout(s + co_off2, d + co_off2, co_sz2));
}
return (copyout(sp, uaddr, sizeof(*sp)));
}
/*
* Get filesystem statistics.
*/
int
sys_statfs(struct proc *p, void *v, register_t *retval)
{
struct sys_statfs_args /* {
syscallarg(const char *) path;
syscallarg(struct statfs *) buf;
} */ *uap = v;
struct mount *mp;
struct statfs *sp;
int error;
struct nameidata nd;
NDINIT(&nd, LOOKUP, FOLLOW | BYPASSUNVEIL, UIO_USERSPACE,
SCARG(uap, path), p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
return (error);
mp = nd.ni_vp->v_mount;
sp = &mp->mnt_stat;
vrele(nd.ni_vp);
if ((error = VFS_STATFS(mp, sp, p)) != 0)
return (error);
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
return (copyout_statfs(sp, SCARG(uap, buf), p));
}
/*
* Get filesystem statistics.
*/
int
sys_fstatfs(struct proc *p, void *v, register_t *retval)
{
struct sys_fstatfs_args /* {
syscallarg(int) fd;
syscallarg(struct statfs *) buf;
} */ *uap = v;
struct file *fp;
struct mount *mp;
struct statfs *sp;
int error;
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
mp = ((struct vnode *)fp->f_data)->v_mount;
if (!mp) {
FRELE(fp, p);
return (ENOENT);
}
sp = &mp->mnt_stat;
error = VFS_STATFS(mp, sp, p);
FRELE(fp, p);
if (error)
return (error);
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
return (copyout_statfs(sp, SCARG(uap, buf), p));
}
/*
* Get statistics on all filesystems.
*/
int
sys_getfsstat(struct proc *p, void *v, register_t *retval)
{
struct sys_getfsstat_args /* {
syscallarg(struct statfs *) buf;
syscallarg(size_t) bufsize;
syscallarg(int) flags;
} */ *uap = v;
struct mount *mp;
struct statfs *sp;
struct statfs *sfsp;
size_t count, maxcount;
int error, flags = SCARG(uap, flags);
maxcount = SCARG(uap, bufsize) / sizeof(struct statfs);
sfsp = SCARG(uap, buf);
count = 0;
TAILQ_FOREACH(mp, &mountlist, mnt_list) {
if (vfs_busy(mp, VB_READ|VB_NOWAIT))
continue;
if (sfsp && count < maxcount) {
sp = &mp->mnt_stat;
/* Refresh stats unless MNT_NOWAIT is specified */
if (flags != MNT_NOWAIT &&
flags != MNT_LAZY &&
(flags == MNT_WAIT ||
flags == 0) &&
(error = VFS_STATFS(mp, sp, p))) {
vfs_unbusy(mp);
continue;
}
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
#if notyet
if (mp->mnt_flag & MNT_SOFTDEP)
sp->f_eflags = STATFS_SOFTUPD;
#endif
error = (copyout_statfs(sp, sfsp, p));
if (error) {
vfs_unbusy(mp);
return (error);
}
sfsp++;
}
count++;
vfs_unbusy(mp);
}
if (sfsp && count > maxcount)
*retval = maxcount;
else
*retval = count;
return (0);
}
/*
* Change current working directory to a given file descriptor.
*/
int
sys_fchdir(struct proc *p, void *v, register_t *retval)
{
struct sys_fchdir_args /* {
syscallarg(int) fd;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct vnode *vp, *tdp, *old_cdir;
struct mount *mp;
struct file *fp;
int error;
if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
return (EBADF);
vp = fp->f_data;
if (fp->f_type != DTYPE_VNODE || vp->v_type != VDIR) {
FRELE(fp, p);
return (ENOTDIR);
}
vref(vp);
FRELE(fp, p);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
while (!error && (mp = vp->v_mountedhere) != NULL) {
if (vfs_busy(mp, VB_READ|VB_WAIT))
continue;
error = VFS_ROOT(mp, &tdp);
vfs_unbusy(mp);
if (error)
break;
vput(vp);
vp = tdp;
}
if (error) {
vput(vp);
return (error);
}
VOP_UNLOCK(vp);
old_cdir = fdp->fd_cdir;
fdp->fd_cdir = vp;
vrele(old_cdir);
return (0);
}
/*
* Change current working directory (``.'').
*/
int
sys_chdir(struct proc *p, void *v, register_t *retval)
{
struct sys_chdir_args /* {
syscallarg(const char *) path;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct vnode *old_cdir;
int error;
struct nameidata nd;
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
SCARG(uap, path), p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = change_dir(&nd, p)) != 0)
return (error);
old_cdir = fdp->fd_cdir;
fdp->fd_cdir = nd.ni_vp;
vrele(old_cdir);
return (0);
}
/*
* Change notion of root (``/'') directory.
*/
int
sys_chroot(struct proc *p, void *v, register_t *retval)
{
struct sys_chroot_args /* {
syscallarg(const char *) path;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct vnode *old_cdir, *old_rdir;
int error;
struct nameidata nd;
if ((error = suser(p)) != 0)
return (error);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
SCARG(uap, path), p);
if ((error = change_dir(&nd, p)) != 0)
return (error);
if (fdp->fd_rdir != NULL) {
/*
* A chroot() done inside a changed root environment does
* an automatic chdir to avoid the out-of-tree experience.
*/
vref(nd.ni_vp);
old_rdir = fdp->fd_rdir;
old_cdir = fdp->fd_cdir;
fdp->fd_rdir = fdp->fd_cdir = nd.ni_vp;
vrele(old_rdir);
vrele(old_cdir);
} else
fdp->fd_rdir = nd.ni_vp;
atomic_setbits_int(&p->p_p->ps_flags, PS_CHROOT);
return (0);
}
/*
* Common routine for chroot and chdir.
*/
static int
change_dir(struct nameidata *ndp, struct proc *p)
{
struct vnode *vp;
int error;
if ((error = namei(ndp)) != 0)
return (error);
vp = ndp->ni_vp;
if (vp->v_type != VDIR)
error = ENOTDIR;
else
error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
if (error)
vput(vp);
else
VOP_UNLOCK(vp);
return (error);
}
int
sys___realpath(struct proc *p, void *v, register_t *retval)
{
struct sys___realpath_args /* {
syscallarg(const char *) pathname;
syscallarg(char *) resolved;
} */ *uap = v;
char *pathname;
char *rpbuf;
struct nameidata nd;
size_t pathlen;
int error = 0;
if (SCARG(uap, pathname) == NULL)
return (EINVAL);
pathname = pool_get(&namei_pool, PR_WAITOK);
rpbuf = pool_get(&namei_pool, PR_WAITOK);
if ((error = copyinstr(SCARG(uap, pathname), pathname, MAXPATHLEN,
&pathlen)))
goto end;
if (pathlen == 1) { /* empty string "" */
error = ENOENT;
goto end;
}
if (pathlen < 2) {
error = EINVAL;
goto end;
}
/* Get cwd for relative path if needed, prepend to rpbuf */
rpbuf[0] = '\0';
if (pathname[0] != '/') {
int cwdlen = MAXPATHLEN * 4; /* for vfs_getcwd_common */
char *cwdbuf, *bp;
cwdbuf = malloc(cwdlen, M_TEMP, M_WAITOK);
/* vfs_getcwd_common fills this in backwards */
bp = &cwdbuf[cwdlen - 1];
*bp = '\0';
error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, cwdbuf,
cwdlen/2, GETCWD_CHECK_ACCESS, p);
if (error) {
free(cwdbuf, M_TEMP, cwdlen);
goto end;
}
if (strlcpy(rpbuf, bp, MAXPATHLEN) >= MAXPATHLEN) {
free(cwdbuf, M_TEMP, cwdlen);
error = ENAMETOOLONG;
goto end;
}
free(cwdbuf, M_TEMP, cwdlen);
}
NDINIT(&nd, LOOKUP, FOLLOW | SAVENAME | REALPATH, UIO_SYSSPACE,
pathname, p);
nd.ni_cnd.cn_rpbuf = rpbuf;
nd.ni_cnd.cn_rpi = strlen(rpbuf);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
goto end;
/* release reference from namei */
if (nd.ni_vp)
vrele(nd.ni_vp);
error = copyoutstr(nd.ni_cnd.cn_rpbuf, SCARG(uap, resolved),
MAXPATHLEN, NULL);
#ifdef KTRACE
if (KTRPOINT(p, KTR_NAMEI))
ktrnamei(p, nd.ni_cnd.cn_rpbuf);
#endif
pool_put(&namei_pool, nd.ni_cnd.cn_pnbuf);
end:
pool_put(&namei_pool, rpbuf);
pool_put(&namei_pool, pathname);
return (error);
}
int
sys_unveil(struct proc *p, void *v, register_t *retval)
{
struct sys_unveil_args /* {
syscallarg(const char *) path;
syscallarg(const char *) permissions;
} */ *uap = v;
struct process *pr = p->p_p;
char *pathname, *c;
struct nameidata nd;
size_t pathlen;
char permissions[5];
int error, allow;
if (SCARG(uap, path) == NULL && SCARG(uap, permissions) == NULL) {
pr->ps_uvdone = 1;
return (0);
}
if (pr->ps_uvdone != 0)
return EPERM;
error = copyinstr(SCARG(uap, permissions), permissions,
sizeof(permissions), NULL);
if (error)
return (error);
pathname = pool_get(&namei_pool, PR_WAITOK);
error = copyinstr(SCARG(uap, path), pathname, MAXPATHLEN, &pathlen);
if (error)
goto end;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrstruct(p, "unveil", permissions, strlen(permissions));
#endif
if (pathlen < 2) {
error = EINVAL;
goto end;
}
/* find root "/" or "//" */
for (c = pathname; *c != '\0'; c++) {
if (*c != '/')
break;
}
if (*c == '\0')
/* root directory */
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | SAVENAME,
UIO_SYSSPACE, pathname, p);
else
NDINIT(&nd, CREATE, FOLLOW | LOCKLEAF | LOCKPARENT | SAVENAME,
UIO_SYSSPACE, pathname, p);
nd.ni_pledge = PLEDGE_UNVEIL;
if ((error = namei(&nd)) != 0)
goto end;
/*
* XXX Any access to the file or directory will allow us to
* pledge path it
*/
allow = ((nd.ni_vp &&
(VOP_ACCESS(nd.ni_vp, VREAD, p->p_ucred, p) == 0 ||
VOP_ACCESS(nd.ni_vp, VWRITE, p->p_ucred, p) == 0 ||
VOP_ACCESS(nd.ni_vp, VEXEC, p->p_ucred, p) == 0)) ||
(nd.ni_dvp &&
(VOP_ACCESS(nd.ni_dvp, VREAD, p->p_ucred, p) == 0 ||
VOP_ACCESS(nd.ni_dvp, VWRITE, p->p_ucred, p) == 0 ||
VOP_ACCESS(nd.ni_dvp, VEXEC, p->p_ucred, p) == 0)));
/* release lock from namei, but keep ref */
if (nd.ni_vp)
VOP_UNLOCK(nd.ni_vp);
if (nd.ni_dvp && nd.ni_dvp != nd.ni_vp)
VOP_UNLOCK(nd.ni_dvp);
if (allow)
error = unveil_add(p, &nd, permissions);
else
error = EPERM;
/* release vref from namei, but not vref from unveil_add */
if (nd.ni_vp)
vrele(nd.ni_vp);
if (nd.ni_dvp)
vrele(nd.ni_dvp);
pool_put(&namei_pool, nd.ni_cnd.cn_pnbuf);
end:
pool_put(&namei_pool, pathname);
return (error);
}
/*
* Check permissions, allocate an open file structure,
* and call the device open routine if any.
*/
int
sys_open(struct proc *p, void *v, register_t *retval)
{
struct sys_open_args /* {
syscallarg(const char *) path;
syscallarg(int) flags;
syscallarg(mode_t) mode;
} */ *uap = v;
return (doopenat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, flags),
SCARG(uap, mode), retval));
}
int
sys_openat(struct proc *p, void *v, register_t *retval)
{
struct sys_openat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(int) flags;
syscallarg(mode_t) mode;
} */ *uap = v;
return (doopenat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, flags), SCARG(uap, mode), retval));
}
int
doopenat(struct proc *p, int fd, const char *path, int oflags, mode_t mode,
register_t *retval)
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct vnode *vp;
struct vattr vattr;
int flags, cloexec, cmode;
int type, indx, error, localtrunc = 0;
struct flock lf;
struct nameidata nd;
uint64_t ni_pledge = 0;
u_char ni_unveil = 0;
if (oflags & (O_EXLOCK | O_SHLOCK)) {
error = pledge_flock(p);
if (error != 0)
return (error);
}
cloexec = (oflags & O_CLOEXEC) ? UF_EXCLOSE : 0;
fdplock(fdp);
if ((error = falloc(p, &fp, &indx)) != 0) {
fdpunlock(fdp);
return (error);
}
fdpunlock(fdp);
flags = FFLAGS(oflags);
if (flags & FREAD) {
ni_pledge |= PLEDGE_RPATH;
ni_unveil |= UNVEIL_READ;
}
if (flags & FWRITE) {
ni_pledge |= PLEDGE_WPATH;
ni_unveil |= UNVEIL_WRITE;
}
if (oflags & O_CREAT) {
ni_pledge |= PLEDGE_CPATH;
ni_unveil |= UNVEIL_CREATE;
}
cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT;
if ((p->p_p->ps_flags & PS_PLEDGE))
cmode &= ACCESSPERMS;
NDINITAT(&nd, 0, 0, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = ni_pledge;
nd.ni_unveil = ni_unveil;
p->p_dupfd = -1; /* XXX check for fdopen */
if ((flags & O_TRUNC) && (flags & (O_EXLOCK | O_SHLOCK))) {
localtrunc = 1;
flags &= ~O_TRUNC; /* Must do truncate ourselves */
}
if ((error = vn_open(&nd, flags, cmode)) != 0) {
fdplock(fdp);
if (error == ENODEV &&
p->p_dupfd >= 0 && /* XXX from fdopen */
(error =
dupfdopen(p, indx, flags)) == 0) {
fdpunlock(fdp);
closef(fp, p);
*retval = indx;
return (error);
}
if (error == ERESTART)
error = EINTR;
fdremove(fdp, indx);
fdpunlock(fdp);
closef(fp, p);
return (error);
}
p->p_dupfd = 0;
vp = nd.ni_vp;
fp->f_flag = flags & FMASK;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = vp;
if (flags & (O_EXLOCK | O_SHLOCK)) {
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
if (flags & O_EXLOCK)
lf.l_type = F_WRLCK;
else
lf.l_type = F_RDLCK;
type = F_FLOCK;
if ((flags & FNONBLOCK) == 0)
type |= F_WAIT;
VOP_UNLOCK(vp);
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type);
if (error) {
fdplock(fdp);
/* closef will vn_close the file for us. */
fdremove(fdp, indx);
fdpunlock(fdp);
closef(fp, p);
return (error);
}
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
atomic_setbits_int(&fp->f_iflags, FIF_HASLOCK);
}
if (localtrunc) {
if ((fp->f_flag & FWRITE) == 0)
error = EACCES;
else if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_RDONLY))
error = EROFS;
else if (vp->v_type == VDIR)
error = EISDIR;
else if ((error = vn_writechk(vp)) == 0) {
VATTR_NULL(&vattr);
vattr.va_size = 0;
error = VOP_SETATTR(vp, &vattr, fp->f_cred, p);
}
if (error) {
VOP_UNLOCK(vp);
fdplock(fdp);
/* closef will close the file for us. */
fdremove(fdp, indx);
fdpunlock(fdp);
closef(fp, p);
return (error);
}
}
VOP_UNLOCK(vp);
*retval = indx;
fdplock(fdp);
fdinsert(fdp, indx, cloexec, fp);
fdpunlock(fdp);
FRELE(fp, p);
return (error);
}
/*
* Open a new created file (in /tmp) suitable for mmaping.
*/
int
sys___tmpfd(struct proc *p, void *v, register_t *retval)
{
struct sys___tmpfd_args /* {
syscallarg(int) flags;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct vnode *vp;
int oflags = SCARG(uap, flags);
int flags, cloexec, cmode;
int indx, error;
unsigned int i;
struct nameidata nd;
char path[64];
static const char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
/* most flags are hardwired */
oflags = O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | (oflags & O_CLOEXEC);
cloexec = (oflags & O_CLOEXEC) ? UF_EXCLOSE : 0;
fdplock(fdp);
if ((error = falloc(p, &fp, &indx)) != 0) {
fdpunlock(fdp);
return (error);
}
fdpunlock(fdp);
flags = FFLAGS(oflags);
arc4random_buf(path, sizeof(path));
memcpy(path, "/tmp/", 5);
for (i = 5; i < sizeof(path) - 1; i++)
path[i] = letters[(unsigned char)path[i] & 63];
path[sizeof(path)-1] = 0;
cmode = 0600;
NDINITAT(&nd, 0, KERNELPATH, UIO_SYSSPACE, AT_FDCWD, path, p);
if ((error = vn_open(&nd, flags, cmode)) != 0) {
if (error == ERESTART)
error = EINTR;
fdplock(fdp);
fdremove(fdp, indx);
fdpunlock(fdp);
closef(fp, p);
return (error);
}
vp = nd.ni_vp;
fp->f_flag = flags & FMASK;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = vp;
VOP_UNLOCK(vp);
*retval = indx;
fdplock(fdp);
fdinsert(fdp, indx, cloexec, fp);
fdpunlock(fdp);
FRELE(fp, p);
/* unlink it */
/* XXX
* there is a wee race here, although it is mostly inconsequential.
* perhaps someday we can create a file like object without a name...
*/
NDINITAT(&nd, DELETE, KERNELPATH | LOCKPARENT | LOCKLEAF, UIO_SYSSPACE,
AT_FDCWD, path, p);
if ((error = namei(&nd)) != 0) {
printf("can't unlink temp file! %d\n", error);
error = 0;
} else {
vp = nd.ni_vp;
uvm_vnp_uncache(vp);
error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
if (error) {
printf("error removing vop: %d\n", error);
error = 0;
}
}
return (error);
}
/*
* Get file handle system call
*/
int
sys_getfh(struct proc *p, void *v, register_t *retval)
{
struct sys_getfh_args /* {
syscallarg(const char *) fname;
syscallarg(fhandle_t *) fhp;
} */ *uap = v;
struct vnode *vp;
fhandle_t fh;
int error;
struct nameidata nd;
/*
* Must be super user
*/
error = suser(p);
if (error)
return (error);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
SCARG(uap, fname), p);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
memset(&fh, 0, sizeof(fh));
fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
error = VFS_VPTOFH(vp, &fh.fh_fid);
vput(vp);
if (error)
return (error);
error = copyout(&fh, SCARG(uap, fhp), sizeof(fh));
return (error);
}
/*
* Open a file given a file handle.
*
* Check permissions, allocate an open file structure,
* and call the device open routine if any.
*/
int
sys_fhopen(struct proc *p, void *v, register_t *retval)
{
struct sys_fhopen_args /* {
syscallarg(const fhandle_t *) fhp;
syscallarg(int) flags;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct vnode *vp = NULL;
struct mount *mp;
struct ucred *cred = p->p_ucred;
int flags, cloexec;
int type, indx, error=0;
struct flock lf;
struct vattr va;
fhandle_t fh;
/*
* Must be super user
*/
if ((error = suser(p)))
return (error);
flags = FFLAGS(SCARG(uap, flags));
if ((flags & (FREAD | FWRITE)) == 0)
return (EINVAL);
if ((flags & O_CREAT))
return (EINVAL);
cloexec = (flags & O_CLOEXEC) ? UF_EXCLOSE : 0;
fdplock(fdp);
if ((error = falloc(p, &fp, &indx)) != 0) {
fdpunlock(fdp);
fp = NULL;
goto bad;
}
fdpunlock(fdp);
if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0)
goto bad;
if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) {
error = ESTALE;
goto bad;
}
if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp)) != 0) {
vp = NULL; /* most likely unnecessary sanity for bad: */
goto bad;
}
/* Now do an effective vn_open */
if (vp->v_type == VSOCK) {
error = EOPNOTSUPP;
goto bad;
}
if ((flags & O_DIRECTORY) && vp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
if (flags & FREAD) {
if ((error = VOP_ACCESS(vp, VREAD, cred, p)) != 0)
goto bad;
}
if (flags & (FWRITE | O_TRUNC)) {
if (vp->v_type == VDIR) {
error = EISDIR;
goto bad;
}
if ((error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0 ||
(error = vn_writechk(vp)) != 0)
goto bad;
}
if (flags & O_TRUNC) {
VATTR_NULL(&va);
va.va_size = 0;
if ((error = VOP_SETATTR(vp, &va, cred, p)) != 0)
goto bad;
}
if ((error = VOP_OPEN(vp, flags, cred, p)) != 0)
goto bad;
if (flags & FWRITE)
vp->v_writecount++;
/* done with modified vn_open, now finish what sys_open does. */
fp->f_flag = flags & FMASK;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = vp;
if (flags & (O_EXLOCK | O_SHLOCK)) {
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
if (flags & O_EXLOCK)
lf.l_type = F_WRLCK;
else
lf.l_type = F_RDLCK;
type = F_FLOCK;
if ((flags & FNONBLOCK) == 0)
type |= F_WAIT;
VOP_UNLOCK(vp);
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type);
if (error) {
vp = NULL; /* closef will vn_close the file */
goto bad;
}
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
atomic_setbits_int(&fp->f_iflags, FIF_HASLOCK);
}
VOP_UNLOCK(vp);
*retval = indx;
fdplock(fdp);
fdinsert(fdp, indx, cloexec, fp);
fdpunlock(fdp);
FRELE(fp, p);
return (0);
bad:
if (fp) {
fdplock(fdp);
fdremove(fdp, indx);
fdpunlock(fdp);
closef(fp, p);
if (vp != NULL)
vput(vp);
}
return (error);
}
int
sys_fhstat(struct proc *p, void *v, register_t *retval)
{
struct sys_fhstat_args /* {
syscallarg(const fhandle_t *) fhp;
syscallarg(struct stat *) sb;
} */ *uap = v;
struct stat sb;
int error;
fhandle_t fh;
struct mount *mp;
struct vnode *vp;
/*
* Must be super user
*/
if ((error = suser(p)))
return (error);
if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0)
return (error);
if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL)
return (ESTALE);
if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp)))
return (error);
error = vn_stat(vp, &sb, p);
vput(vp);
if (error)
return (error);
error = copyout(&sb, SCARG(uap, sb), sizeof(sb));
return (error);
}
int
sys_fhstatfs(struct proc *p, void *v, register_t *retval)
{
struct sys_fhstatfs_args /* {
syscallarg(const fhandle_t *) fhp;
syscallarg(struct statfs *) buf;
} */ *uap = v;
struct statfs *sp;
fhandle_t fh;
struct mount *mp;
struct vnode *vp;
int error;
/*
* Must be super user
*/
if ((error = suser(p)))
return (error);
if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0)
return (error);
if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL)
return (ESTALE);
if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp)))
return (error);
mp = vp->v_mount;
sp = &mp->mnt_stat;
vput(vp);
if ((error = VFS_STATFS(mp, sp, p)) != 0)
return (error);
sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
return (copyout(sp, SCARG(uap, buf), sizeof(*sp)));
}
/*
* Create a special file or named pipe.
*/
int
sys_mknod(struct proc *p, void *v, register_t *retval)
{
struct sys_mknod_args /* {
syscallarg(const char *) path;
syscallarg(mode_t) mode;
syscallarg(int) dev;
} */ *uap = v;
return (domknodat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, mode),
SCARG(uap, dev)));
}
int
sys_mknodat(struct proc *p, void *v, register_t *retval)
{
struct sys_mknodat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(mode_t) mode;
syscallarg(dev_t) dev;
} */ *uap = v;
return (domknodat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, mode), SCARG(uap, dev)));
}
int
domknodat(struct proc *p, int fd, const char *path, mode_t mode, dev_t dev)
{
struct vnode *vp;
struct vattr vattr;
int error;
struct nameidata nd;
if (dev == VNOVAL)
return (EINVAL);
NDINITAT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_DPATH;
nd.ni_unveil = UNVEIL_CREATE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
if (!S_ISFIFO(mode) || dev != 0) {
if (!vnoperm(nd.ni_dvp) && (error = suser(p)) != 0)
goto out;
if (p->p_fd->fd_rdir) {
error = EINVAL;
goto out;
}
}
if (vp != NULL)
error = EEXIST;
else {
VATTR_NULL(&vattr);
vattr.va_mode = (mode & ALLPERMS) &~ p->p_fd->fd_cmask;
if ((p->p_p->ps_flags & PS_PLEDGE))
vattr.va_mode &= ACCESSPERMS;
vattr.va_rdev = dev;
switch (mode & S_IFMT) {
case S_IFMT: /* used by badsect to flag bad sectors */
vattr.va_type = VBAD;
break;
case S_IFCHR:
vattr.va_type = VCHR;
break;
case S_IFBLK:
vattr.va_type = VBLK;
break;
case S_IFIFO:
#ifndef FIFO
error = EOPNOTSUPP;
break;
#else
if (dev == 0) {
vattr.va_type = VFIFO;
break;
}
/* FALLTHROUGH */
#endif /* FIFO */
default:
error = EINVAL;
break;
}
}
out:
if (!error) {
error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
vput(nd.ni_dvp);
} else {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
if (vp)
vrele(vp);
}
return (error);
}
/*
* Create a named pipe.
*/
int
sys_mkfifo(struct proc *p, void *v, register_t *retval)
{
struct sys_mkfifo_args /* {
syscallarg(const char *) path;
syscallarg(mode_t) mode;
} */ *uap = v;
return (domknodat(p, AT_FDCWD, SCARG(uap, path),
(SCARG(uap, mode) & ALLPERMS) | S_IFIFO, 0));
}
int
sys_mkfifoat(struct proc *p, void *v, register_t *retval)
{
struct sys_mkfifoat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(mode_t) mode;
} */ *uap = v;
return (domknodat(p, SCARG(uap, fd), SCARG(uap, path),
(SCARG(uap, mode) & ALLPERMS) | S_IFIFO, 0));
}
/*
* Make a hard file link.
*/
int
sys_link(struct proc *p, void *v, register_t *retval)
{
struct sys_link_args /* {
syscallarg(const char *) path;
syscallarg(const char *) link;
} */ *uap = v;
return (dolinkat(p, AT_FDCWD, SCARG(uap, path), AT_FDCWD,
SCARG(uap, link), AT_SYMLINK_FOLLOW));
}
int
sys_linkat(struct proc *p, void *v, register_t *retval)
{
struct sys_linkat_args /* {
syscallarg(int) fd1;
syscallarg(const char *) path1;
syscallarg(int) fd2;
syscallarg(const char *) path2;
syscallarg(int) flag;
} */ *uap = v;
return (dolinkat(p, SCARG(uap, fd1), SCARG(uap, path1),
SCARG(uap, fd2), SCARG(uap, path2), SCARG(uap, flag)));
}
int
dolinkat(struct proc *p, int fd1, const char *path1, int fd2,
const char *path2, int flag)
{
struct vnode *vp;
struct nameidata nd;
int error, follow;
int flags;
if (flag & ~AT_SYMLINK_FOLLOW)
return (EINVAL);
follow = (flag & AT_SYMLINK_FOLLOW) ? FOLLOW : NOFOLLOW;
NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd1, path1, p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
flags = LOCKPARENT;
if (vp->v_type == VDIR) {
flags |= STRIPSLASHES;
}
NDINITAT(&nd, CREATE, flags, UIO_USERSPACE, fd2, path2, p);
nd.ni_pledge = PLEDGE_CPATH;
nd.ni_unveil = UNVEIL_CREATE;
if ((error = namei(&nd)) != 0)
goto out;
if (nd.ni_vp) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == nd.ni_vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(nd.ni_vp);
error = EEXIST;
goto out;
}
error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
out:
vrele(vp);
return (error);
}
/*
* Make a symbolic link.
*/
int
sys_symlink(struct proc *p, void *v, register_t *retval)
{
struct sys_symlink_args /* {
syscallarg(const char *) path;
syscallarg(const char *) link;
} */ *uap = v;
return (dosymlinkat(p, SCARG(uap, path), AT_FDCWD, SCARG(uap, link)));
}
int
sys_symlinkat(struct proc *p, void *v, register_t *retval)
{
struct sys_symlinkat_args /* {
syscallarg(const char *) path;
syscallarg(int) fd;
syscallarg(const char *) link;
} */ *uap = v;
return (dosymlinkat(p, SCARG(uap, path), SCARG(uap, fd),
SCARG(uap, link)));
}
int
dosymlinkat(struct proc *p, const char *upath, int fd, const char *link)
{
struct vattr vattr;
char *path;
int error;
struct nameidata nd;
path = pool_get(&namei_pool, PR_WAITOK);
error = copyinstr(upath, path, MAXPATHLEN, NULL);
if (error)
goto out;
NDINITAT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, fd, link, p);
nd.ni_pledge = PLEDGE_CPATH;
nd.ni_unveil = UNVEIL_CREATE;
if ((error = namei(&nd)) != 0)
goto out;
if (nd.ni_vp) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == nd.ni_vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(nd.ni_vp);
error = EEXIST;
goto out;
}
VATTR_NULL(&vattr);
vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path);
out:
pool_put(&namei_pool, path);
return (error);
}
/*
* Delete a name from the filesystem.
*/
int
sys_unlink(struct proc *p, void *v, register_t *retval)
{
struct sys_unlink_args /* {
syscallarg(const char *) path;
} */ *uap = v;
return (dounlinkat(p, AT_FDCWD, SCARG(uap, path), 0));
}
int
sys_unlinkat(struct proc *p, void *v, register_t *retval)
{
struct sys_unlinkat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(int) flag;
} */ *uap = v;
return (dounlinkat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, flag)));
}
int
dounlinkat(struct proc *p, int fd, const char *path, int flag)
{
struct vnode *vp;
int error;
struct nameidata nd;
if (flag & ~AT_REMOVEDIR)
return (EINVAL);
NDINITAT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE,
fd, path, p);
nd.ni_pledge = PLEDGE_CPATH;
nd.ni_unveil = UNVEIL_CREATE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
if (flag & AT_REMOVEDIR) {
if (vp->v_type != VDIR) {
error = ENOTDIR;
goto out;
}
/*
* No rmdir "." please.
*/
if (nd.ni_dvp == vp) {
error = EINVAL;
goto out;
}
/*
* A mounted on directory cannot be deleted.
*/
if (vp->v_mountedhere != NULL) {
error = EBUSY;
goto out;
}
}
/*
* The root of a mounted filesystem cannot be deleted.
*/
if (vp->v_flag & VROOT)
error = EBUSY;
out:
if (!error) {
if (flag & AT_REMOVEDIR) {
error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
} else {
(void)uvm_vnp_uncache(vp);
error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
}
} else {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vput(vp);
}
return (error);
}
/*
* Reposition read/write file offset.
*/
int
sys_lseek(struct proc *p, void *v, register_t *retval)
{
struct sys_lseek_args /* {
syscallarg(int) fd;
syscallarg(off_t) offset;
syscallarg(int) whence;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
struct file *fp;
off_t offset;
int error;
if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
return (EBADF);
if (fp->f_ops->fo_seek == NULL) {
error = ESPIPE;
goto bad;
}
offset = SCARG(uap, offset);
error = (*fp->f_ops->fo_seek)(fp, &offset, SCARG(uap, whence), p);
if (error)
goto bad;
*(off_t *)retval = offset;
mtx_enter(&fp->f_mtx);
fp->f_seek++;
mtx_leave(&fp->f_mtx);
error = 0;
bad:
FRELE(fp, p);
return (error);
}
#if 1
int
sys_pad_lseek(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_lseek_args *uap = v;
struct sys_lseek_args unpad;
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, offset) = SCARG(uap, offset);
SCARG(&unpad, whence) = SCARG(uap, whence);
return sys_lseek(p, &unpad, retval);
}
#endif
/*
* Check access permissions.
*/
int
sys_access(struct proc *p, void *v, register_t *retval)
{
struct sys_access_args /* {
syscallarg(const char *) path;
syscallarg(int) amode;
} */ *uap = v;
return (dofaccessat(p, AT_FDCWD, SCARG(uap, path),
SCARG(uap, amode), 0));
}
int
sys_faccessat(struct proc *p, void *v, register_t *retval)
{
struct sys_faccessat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(int) amode;
syscallarg(int) flag;
} */ *uap = v;
return (dofaccessat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, amode), SCARG(uap, flag)));
}
int
dofaccessat(struct proc *p, int fd, const char *path, int amode, int flag)
{
struct vnode *vp;
struct ucred *newcred, *oldcred;
struct nameidata nd;
int error;
if (amode & ~(R_OK | W_OK | X_OK))
return (EINVAL);
if (flag & ~AT_EACCESS)
return (EINVAL);
newcred = NULL;
oldcred = p->p_ucred;
/*
* If access as real ids was requested and they really differ,
* give the thread new creds with them reset
*/
if ((flag & AT_EACCESS) == 0 &&
(oldcred->cr_uid != oldcred->cr_ruid ||
(oldcred->cr_gid != oldcred->cr_rgid))) {
p->p_ucred = newcred = crdup(oldcred);
newcred->cr_uid = newcred->cr_ruid;
newcred->cr_gid = newcred->cr_rgid;
}
NDINITAT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
goto out;
vp = nd.ni_vp;
/* Flags == 0 means only check for existence. */
if (amode) {
int vflags = 0;
if (amode & R_OK)
vflags |= VREAD;
if (amode & W_OK)
vflags |= VWRITE;
if (amode & X_OK)
vflags |= VEXEC;
error = VOP_ACCESS(vp, vflags, p->p_ucred, p);
if (!error && (vflags & VWRITE))
error = vn_writechk(vp);
}
vput(vp);
out:
if (newcred != NULL) {
p->p_ucred = oldcred;
crfree(newcred);
}
return (error);
}
/*
* Get file status; this version follows links.
*/
int
sys_stat(struct proc *p, void *v, register_t *retval)
{
struct sys_stat_args /* {
syscallarg(const char *) path;
syscallarg(struct stat *) ub;
} */ *uap = v;
return (dofstatat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, ub), 0));
}
int
sys_fstatat(struct proc *p, void *v, register_t *retval)
{
struct sys_fstatat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(struct stat *) buf;
syscallarg(int) flag;
} */ *uap = v;
return (dofstatat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, buf), SCARG(uap, flag)));
}
int
dofstatat(struct proc *p, int fd, const char *path, struct stat *buf, int flag)
{
struct stat sb;
int error, follow;
struct nameidata nd;
if (flag & ~AT_SYMLINK_NOFOLLOW)
return (EINVAL);
follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
NDINITAT(&nd, LOOKUP, follow | LOCKLEAF, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
return (error);
error = vn_stat(nd.ni_vp, &sb, p);
vput(nd.ni_vp);
if (error)
return (error);
/* Don't let non-root see generation numbers (for NFS security) */
if (suser(p))
sb.st_gen = 0;
error = copyout(&sb, buf, sizeof(sb));
#ifdef KTRACE
if (error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrstat(p, &sb);
#endif
return (error);
}
/*
* Get file status; this version does not follow links.
*/
int
sys_lstat(struct proc *p, void *v, register_t *retval)
{
struct sys_lstat_args /* {
syscallarg(const char *) path;
syscallarg(struct stat *) ub;
} */ *uap = v;
return (dofstatat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, ub),
AT_SYMLINK_NOFOLLOW));
}
/*
* Get configurable pathname variables.
*/
int
sys_pathconf(struct proc *p, void *v, register_t *retval)
{
struct sys_pathconf_args /* {
syscallarg(const char *) path;
syscallarg(int) name;
} */ *uap = v;
int error;
struct nameidata nd;
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
SCARG(uap, path), p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
return (error);
error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), retval);
vput(nd.ni_vp);
return (error);
}
/*
* Return target name of a symbolic link.
*/
int
sys_readlink(struct proc *p, void *v, register_t *retval)
{
struct sys_readlink_args /* {
syscallarg(const char *) path;
syscallarg(char *) buf;
syscallarg(size_t) count;
} */ *uap = v;
return (doreadlinkat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, buf),
SCARG(uap, count), retval));
}
int
sys_readlinkat(struct proc *p, void *v, register_t *retval)
{
struct sys_readlinkat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(char *) buf;
syscallarg(size_t) count;
} */ *uap = v;
return (doreadlinkat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, buf), SCARG(uap, count), retval));
}
int
doreadlinkat(struct proc *p, int fd, const char *path, char *buf,
size_t count, register_t *retval)
{
struct vnode *vp;
struct iovec aiov;
struct uio auio;
int error;
struct nameidata nd;
NDINITAT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
if (vp->v_type != VLNK)
error = EINVAL;
else {
aiov.iov_base = buf;
aiov.iov_len = count;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_procp = p;
auio.uio_resid = count;
error = VOP_READLINK(vp, &auio, p->p_ucred);
*retval = count - auio.uio_resid;
}
vput(vp);
return (error);
}
/*
* Change flags of a file given a path name.
*/
int
sys_chflags(struct proc *p, void *v, register_t *retval)
{
struct sys_chflags_args /* {
syscallarg(const char *) path;
syscallarg(u_int) flags;
} */ *uap = v;
return (dochflagsat(p, AT_FDCWD, SCARG(uap, path),
SCARG(uap, flags), 0));
}
int
sys_chflagsat(struct proc *p, void *v, register_t *retval)
{
struct sys_chflagsat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(u_int) flags;
syscallarg(int) atflags;
} */ *uap = v;
return (dochflagsat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, flags), SCARG(uap, atflags)));
}
int
dochflagsat(struct proc *p, int fd, const char *path, u_int flags, int atflags)
{
struct nameidata nd;
int error, follow;
if (atflags & ~AT_SYMLINK_NOFOLLOW)
return (EINVAL);
follow = (atflags & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_FATTR | PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_WRITE;
if ((error = namei(&nd)) != 0)
return (error);
return (dovchflags(p, nd.ni_vp, flags));
}
/*
* Change flags of a file given a file descriptor.
*/
int
sys_fchflags(struct proc *p, void *v, register_t *retval)
{
struct sys_fchflags_args /* {
syscallarg(int) fd;
syscallarg(u_int) flags;
} */ *uap = v;
struct file *fp;
struct vnode *vp;
int error;
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
vp = fp->f_data;
vref(vp);
FRELE(fp, p);
return (dovchflags(p, vp, SCARG(uap, flags)));
}
int
dovchflags(struct proc *p, struct vnode *vp, u_int flags)
{
struct vattr vattr;
int error;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount && vp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else if (flags == VNOVAL)
error = EINVAL;
else {
if (suser(p)) {
if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p))
!= 0)
goto out;
if (vattr.va_type == VCHR || vattr.va_type == VBLK) {
error = EINVAL;
goto out;
}
}
VATTR_NULL(&vattr);
vattr.va_flags = flags;
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
out:
vput(vp);
return (error);
}
/*
* Change mode of a file given path name.
*/
int
sys_chmod(struct proc *p, void *v, register_t *retval)
{
struct sys_chmod_args /* {
syscallarg(const char *) path;
syscallarg(mode_t) mode;
} */ *uap = v;
return (dofchmodat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, mode), 0));
}
int
sys_fchmodat(struct proc *p, void *v, register_t *retval)
{
struct sys_fchmodat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(mode_t) mode;
syscallarg(int) flag;
} */ *uap = v;
return (dofchmodat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, mode), SCARG(uap, flag)));
}
int
dofchmodat(struct proc *p, int fd, const char *path, mode_t mode, int flag)
{
struct vnode *vp;
struct vattr vattr;
int error, follow;
struct nameidata nd;
if (mode & ~(S_IFMT | ALLPERMS))
return (EINVAL);
if ((p->p_p->ps_flags & PS_PLEDGE))
mode &= ACCESSPERMS;
if (flag & ~AT_SYMLINK_NOFOLLOW)
return (EINVAL);
follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_FATTR | PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_WRITE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else {
VATTR_NULL(&vattr);
vattr.va_mode = mode & ALLPERMS;
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
vput(vp);
return (error);
}
/*
* Change mode of a file given a file descriptor.
*/
int
sys_fchmod(struct proc *p, void *v, register_t *retval)
{
struct sys_fchmod_args /* {
syscallarg(int) fd;
syscallarg(mode_t) mode;
} */ *uap = v;
struct vattr vattr;
struct vnode *vp;
struct file *fp;
mode_t mode = SCARG(uap, mode);
int error;
if (mode & ~(S_IFMT | ALLPERMS))
return (EINVAL);
if ((p->p_p->ps_flags & PS_PLEDGE))
mode &= ACCESSPERMS;
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
vp = fp->f_data;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount && vp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else {
VATTR_NULL(&vattr);
vattr.va_mode = mode & ALLPERMS;
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
VOP_UNLOCK(vp);
FRELE(fp, p);
return (error);
}
/*
* Set ownership given a path name.
*/
int
sys_chown(struct proc *p, void *v, register_t *retval)
{
struct sys_chown_args /* {
syscallarg(const char *) path;
syscallarg(uid_t) uid;
syscallarg(gid_t) gid;
} */ *uap = v;
return (dofchownat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, uid),
SCARG(uap, gid), 0));
}
int
sys_fchownat(struct proc *p, void *v, register_t *retval)
{
struct sys_fchownat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(uid_t) uid;
syscallarg(gid_t) gid;
syscallarg(int) flag;
} */ *uap = v;
return (dofchownat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, uid), SCARG(uap, gid), SCARG(uap, flag)));
}
int
dofchownat(struct proc *p, int fd, const char *path, uid_t uid, gid_t gid,
int flag)
{
struct vnode *vp;
struct vattr vattr;
int error, follow;
struct nameidata nd;
mode_t mode;
if (flag & ~AT_SYMLINK_NOFOLLOW)
return (EINVAL);
follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_CHOWN | PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_WRITE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else {
if ((error = pledge_chown(p, uid, gid)))
goto out;
if ((uid != -1 || gid != -1) &&
!vnoperm(vp) &&
(suser(p) || suid_clear)) {
error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
if (error)
goto out;
mode = vattr.va_mode & ~(VSUID | VSGID);
if (mode == vattr.va_mode)
mode = VNOVAL;
} else
mode = VNOVAL;
VATTR_NULL(&vattr);
vattr.va_uid = uid;
vattr.va_gid = gid;
vattr.va_mode = mode;
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
out:
vput(vp);
return (error);
}
/*
* Set ownership given a path name, without following links.
*/
int
sys_lchown(struct proc *p, void *v, register_t *retval)
{
struct sys_lchown_args /* {
syscallarg(const char *) path;
syscallarg(uid_t) uid;
syscallarg(gid_t) gid;
} */ *uap = v;
struct vnode *vp;
struct vattr vattr;
int error;
struct nameidata nd;
mode_t mode;
uid_t uid = SCARG(uap, uid);
gid_t gid = SCARG(uap, gid);
NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
nd.ni_pledge = PLEDGE_CHOWN | PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_WRITE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else {
if ((error = pledge_chown(p, uid, gid)))
goto out;
if ((uid != -1 || gid != -1) &&
!vnoperm(vp) &&
(suser(p) || suid_clear)) {
error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
if (error)
goto out;
mode = vattr.va_mode & ~(VSUID | VSGID);
if (mode == vattr.va_mode)
mode = VNOVAL;
} else
mode = VNOVAL;
VATTR_NULL(&vattr);
vattr.va_uid = uid;
vattr.va_gid = gid;
vattr.va_mode = mode;
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
out:
vput(vp);
return (error);
}
/*
* Set ownership given a file descriptor.
*/
int
sys_fchown(struct proc *p, void *v, register_t *retval)
{
struct sys_fchown_args /* {
syscallarg(int) fd;
syscallarg(uid_t) uid;
syscallarg(gid_t) gid;
} */ *uap = v;
struct vnode *vp;
struct vattr vattr;
int error;
struct file *fp;
mode_t mode;
uid_t uid = SCARG(uap, uid);
gid_t gid = SCARG(uap, gid);
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
vp = fp->f_data;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_RDONLY))
error = EROFS;
else {
if ((error = pledge_chown(p, uid, gid)))
goto out;
if ((uid != -1 || gid != -1) &&
!vnoperm(vp) &&
(suser(p) || suid_clear)) {
error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
if (error)
goto out;
mode = vattr.va_mode & ~(VSUID | VSGID);
if (mode == vattr.va_mode)
mode = VNOVAL;
} else
mode = VNOVAL;
VATTR_NULL(&vattr);
vattr.va_uid = uid;
vattr.va_gid = gid;
vattr.va_mode = mode;
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
out:
VOP_UNLOCK(vp);
FRELE(fp, p);
return (error);
}
/*
* Set the access and modification times given a path name.
*/
int
sys_utimes(struct proc *p, void *v, register_t *retval)
{
struct sys_utimes_args /* {
syscallarg(const char *) path;
syscallarg(const struct timeval *) tptr;
} */ *uap = v;
struct timespec ts[2];
struct timeval tv[2];
const struct timeval *tvp;
int error;
tvp = SCARG(uap, tptr);
if (tvp != NULL) {
error = copyin(tvp, tv, sizeof(tv));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrabstimeval(p, &tv);
#endif
if (!timerisvalid(&tv[0]) || !timerisvalid(&tv[1]))
return (EINVAL);
TIMEVAL_TO_TIMESPEC(&tv[0], &ts[0]);
TIMEVAL_TO_TIMESPEC(&tv[1], &ts[1]);
} else
ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
return (doutimensat(p, AT_FDCWD, SCARG(uap, path), ts, 0));
}
int
sys_utimensat(struct proc *p, void *v, register_t *retval)
{
struct sys_utimensat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(const struct timespec *) times;
syscallarg(int) flag;
} */ *uap = v;
struct timespec ts[2];
const struct timespec *tsp;
int error, i;
tsp = SCARG(uap, times);
if (tsp != NULL) {
error = copyin(tsp, ts, sizeof(ts));
if (error)
return (error);
for (i = 0; i < nitems(ts); i++) {
if (ts[i].tv_nsec == UTIME_NOW)
continue;
if (ts[i].tv_nsec == UTIME_OMIT)
continue;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrabstimespec(p, &ts[i]);
#endif
if (!timespecisvalid(&ts[i]))
return (EINVAL);
}
} else
ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
return (doutimensat(p, SCARG(uap, fd), SCARG(uap, path), ts,
SCARG(uap, flag)));
}
int
doutimensat(struct proc *p, int fd, const char *path,
struct timespec ts[2], int flag)
{
struct vnode *vp;
int error, follow;
struct nameidata nd;
if (flag & ~AT_SYMLINK_NOFOLLOW)
return (EINVAL);
follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW;
NDINITAT(&nd, LOOKUP, follow, UIO_USERSPACE, fd, path, p);
nd.ni_pledge = PLEDGE_FATTR | PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_WRITE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
return (dovutimens(p, vp, ts));
}
int
dovutimens(struct proc *p, struct vnode *vp, struct timespec ts[2])
{
struct vattr vattr;
struct timespec now;
int error;
#ifdef KTRACE
/* if they're both UTIME_NOW, then don't report either */
if ((ts[0].tv_nsec != UTIME_NOW || ts[1].tv_nsec != UTIME_NOW) &&
KTRPOINT(p, KTR_STRUCT)) {
ktrabstimespec(p, &ts[0]);
ktrabstimespec(p, &ts[1]);
}
#endif
VATTR_NULL(&vattr);
/* make sure ctime is updated even if neither mtime nor atime is */
vattr.va_vaflags = VA_UTIMES_CHANGE;
if (ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) {
if (ts[0].tv_nsec == UTIME_NOW && ts[1].tv_nsec == UTIME_NOW)
vattr.va_vaflags |= VA_UTIMES_NULL;
getnanotime(&now);
if (ts[0].tv_nsec == UTIME_NOW)
ts[0] = now;
if (ts[1].tv_nsec == UTIME_NOW)
ts[1] = now;
}
if (ts[0].tv_nsec != UTIME_OMIT)
vattr.va_atime = ts[0];
if (ts[1].tv_nsec != UTIME_OMIT)
vattr.va_mtime = ts[1];
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else
error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
vput(vp);
return (error);
}
/*
* Set the access and modification times given a file descriptor.
*/
int
sys_futimes(struct proc *p, void *v, register_t *retval)
{
struct sys_futimes_args /* {
syscallarg(int) fd;
syscallarg(const struct timeval *) tptr;
} */ *uap = v;
struct timeval tv[2];
struct timespec ts[2];
const struct timeval *tvp;
int error;
tvp = SCARG(uap, tptr);
if (tvp != NULL) {
error = copyin(tvp, tv, sizeof(tv));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT)) {
ktrabstimeval(p, &tv[0]);
ktrabstimeval(p, &tv[1]);
}
#endif
if (!timerisvalid(&tv[0]) || !timerisvalid(&tv[1]))
return (EINVAL);
TIMEVAL_TO_TIMESPEC(&tv[0], &ts[0]);
TIMEVAL_TO_TIMESPEC(&tv[1], &ts[1]);
} else
ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
return (dofutimens(p, SCARG(uap, fd), ts));
}
int
sys_futimens(struct proc *p, void *v, register_t *retval)
{
struct sys_futimens_args /* {
syscallarg(int) fd;
syscallarg(const struct timespec *) times;
} */ *uap = v;
struct timespec ts[2];
const struct timespec *tsp;
int error, i;
tsp = SCARG(uap, times);
if (tsp != NULL) {
error = copyin(tsp, ts, sizeof(ts));
if (error)
return (error);
for (i = 0; i < nitems(ts); i++) {
if (ts[i].tv_nsec == UTIME_NOW)
continue;
if (ts[i].tv_nsec == UTIME_OMIT)
continue;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrabstimespec(p, &ts[i]);
#endif
if (!timespecisvalid(&ts[i]))
return (EINVAL);
}
} else
ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
return (dofutimens(p, SCARG(uap, fd), ts));
}
int
dofutimens(struct proc *p, int fd, struct timespec ts[2])
{
struct file *fp;
struct vnode *vp;
int error;
if ((error = getvnode(p, fd, &fp)) != 0)
return (error);
vp = fp->f_data;
vref(vp);
FRELE(fp, p);
return (dovutimens(p, vp, ts));
}
/*
* Truncate a file given a vnode.
*/
int
dotruncate(struct proc *p, struct vnode *vp, off_t len)
{
struct vattr vattr;
int error;
if (len < 0)
return EINVAL;
if (vp->v_type == VDIR)
return EISDIR;
if ((error = vn_writechk(vp)) != 0)
return error;
if (vp->v_type == VREG && len > lim_cur_proc(p, RLIMIT_FSIZE)) {
if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0)
return error;
if (len > vattr.va_size) {
/* if extending over the limit, send signal and fail */
psignal(p, SIGXFSZ);
return EFBIG;
}
}
VATTR_NULL(&vattr);
vattr.va_size = len;
return VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
/*
* Truncate a file given its path name.
*/
int
sys_truncate(struct proc *p, void *v, register_t *retval)
{
struct sys_truncate_args /* {
syscallarg(const char *) path;
syscallarg(off_t) length;
} */ *uap = v;
struct vnode *vp;
int error;
struct nameidata nd;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
nd.ni_pledge = PLEDGE_FATTR | PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_WRITE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if ((error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0)
error = dotruncate(p, vp, SCARG(uap, length));
vput(vp);
return (error);
}
/*
* Truncate a file given a file descriptor.
*/
int
sys_ftruncate(struct proc *p, void *v, register_t *retval)
{
struct sys_ftruncate_args /* {
syscallarg(int) fd;
syscallarg(off_t) length;
} */ *uap = v;
struct vnode *vp;
struct file *fp;
int error;
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
if ((fp->f_flag & FWRITE) == 0) {
error = EINVAL;
goto bad;
}
vp = fp->f_data;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
error = dotruncate(p, vp, SCARG(uap, length));
VOP_UNLOCK(vp);
bad:
FRELE(fp, p);
return (error);
}
#if 1
int
sys_pad_truncate(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_truncate_args *uap = v;
struct sys_truncate_args unpad;
SCARG(&unpad, path) = SCARG(uap, path);
SCARG(&unpad, length) = SCARG(uap, length);
return sys_truncate(p, &unpad, retval);
}
int
sys_pad_ftruncate(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_ftruncate_args *uap = v;
struct sys_ftruncate_args unpad;
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, length) = SCARG(uap, length);
return sys_ftruncate(p, &unpad, retval);
}
#endif
/*
* Sync an open file.
*/
int
sys_fsync(struct proc *p, void *v, register_t *retval)
{
struct sys_fsync_args /* {
syscallarg(int) fd;
} */ *uap = v;
struct vnode *vp;
struct file *fp;
int error;
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
vp = fp->f_data;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p);
#ifdef FFS_SOFTUPDATES
if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP))
error = softdep_fsync(vp);
#endif
VOP_UNLOCK(vp);
FRELE(fp, p);
return (error);
}
/*
* Rename files. Source and destination must either both be directories,
* or both not be directories. If target is a directory, it must be empty.
*/
int
sys_rename(struct proc *p, void *v, register_t *retval)
{
struct sys_rename_args /* {
syscallarg(const char *) from;
syscallarg(const char *) to;
} */ *uap = v;
return (dorenameat(p, AT_FDCWD, SCARG(uap, from), AT_FDCWD,
SCARG(uap, to)));
}
int
sys_renameat(struct proc *p, void *v, register_t *retval)
{
struct sys_renameat_args /* {
syscallarg(int) fromfd;
syscallarg(const char *) from;
syscallarg(int) tofd;
syscallarg(const char *) to;
} */ *uap = v;
return (dorenameat(p, SCARG(uap, fromfd), SCARG(uap, from),
SCARG(uap, tofd), SCARG(uap, to)));
}
int
dorenameat(struct proc *p, int fromfd, const char *from, int tofd,
const char *to)
{
struct vnode *tvp, *fvp, *tdvp;
struct nameidata fromnd, tond;
int error;
int flags;
NDINITAT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE,
fromfd, from, p);
fromnd.ni_pledge = PLEDGE_RPATH | PLEDGE_CPATH;
fromnd.ni_unveil = UNVEIL_READ | UNVEIL_CREATE;
if ((error = namei(&fromnd)) != 0)
return (error);
fvp = fromnd.ni_vp;
flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
/*
* rename("foo/", "bar/"); is OK
*/
if (fvp->v_type == VDIR)
flags |= STRIPSLASHES;
NDINITAT(&tond, RENAME, flags, UIO_USERSPACE, tofd, to, p);
tond.ni_pledge = PLEDGE_CPATH;
tond.ni_unveil = UNVEIL_CREATE;
if ((error = namei(&tond)) != 0) {
VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
vrele(fromnd.ni_dvp);
vrele(fvp);
goto out1;
}
tdvp = tond.ni_dvp;
tvp = tond.ni_vp;
if (tvp != NULL) {
if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
error = ENOTDIR;
goto out;
} else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
error = EISDIR;
goto out;
}
}
if (fvp == tdvp)
error = EINVAL;
/*
* If source is the same as the destination (that is the
* same inode number)
*/
if (fvp == tvp)
error = -1;
out:
if (!error) {
if (tvp) {
(void)uvm_vnp_uncache(tvp);
}
error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
} else {
VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd);
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
vrele(fromnd.ni_dvp);
vrele(fvp);
}
vrele(tond.ni_startdir);
pool_put(&namei_pool, tond.ni_cnd.cn_pnbuf);
out1:
if (fromnd.ni_startdir)
vrele(fromnd.ni_startdir);
pool_put(&namei_pool, fromnd.ni_cnd.cn_pnbuf);
if (error == -1)
return (0);
return (error);
}
/*
* Make a directory file.
*/
int
sys_mkdir(struct proc *p, void *v, register_t *retval)
{
struct sys_mkdir_args /* {
syscallarg(const char *) path;
syscallarg(mode_t) mode;
} */ *uap = v;
return (domkdirat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, mode)));
}
int
sys_mkdirat(struct proc *p, void *v, register_t *retval)
{
struct sys_mkdirat_args /* {
syscallarg(int) fd;
syscallarg(const char *) path;
syscallarg(mode_t) mode;
} */ *uap = v;
return (domkdirat(p, SCARG(uap, fd), SCARG(uap, path),
SCARG(uap, mode)));
}
int
domkdirat(struct proc *p, int fd, const char *path, mode_t mode)
{
struct vnode *vp;
struct vattr vattr;
int error;
struct nameidata nd;
NDINITAT(&nd, CREATE, LOCKPARENT | STRIPSLASHES, UIO_USERSPACE,
fd, path, p);
nd.ni_pledge = PLEDGE_CPATH;
nd.ni_unveil = UNVEIL_CREATE;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
if (vp != NULL) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(vp);
return (EEXIST);
}
VATTR_NULL(&vattr);
vattr.va_type = VDIR;
vattr.va_mode = (mode & ACCESSPERMS) &~ p->p_fd->fd_cmask;
error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
if (!error)
vput(nd.ni_vp);
return (error);
}
/*
* Remove a directory file.
*/
int
sys_rmdir(struct proc *p, void *v, register_t *retval)
{
struct sys_rmdir_args /* {
syscallarg(const char *) path;
} */ *uap = v;
return (dounlinkat(p, AT_FDCWD, SCARG(uap, path), AT_REMOVEDIR));
}
/*
* Read a block of directory entries in a file system independent format.
*/
int
sys_getdents(struct proc *p, void *v, register_t *retval)
{
struct sys_getdents_args /* {
syscallarg(int) fd;
syscallarg(void *) buf;
syscallarg(size_t) buflen;
} */ *uap = v;
struct vnode *vp;
struct file *fp;
struct uio auio;
struct iovec aiov;
size_t buflen;
int error, eofflag;
buflen = SCARG(uap, buflen);
if (buflen > INT_MAX)
return (EINVAL);
if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
return (error);
if ((fp->f_flag & FREAD) == 0) {
error = EBADF;
goto bad;
}
vp = fp->f_data;
if (vp->v_type != VDIR) {
error = EINVAL;
goto bad;
}
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (fp->f_offset < 0) {
VOP_UNLOCK(vp);
error = EINVAL;
goto bad;
}
aiov.iov_base = SCARG(uap, buf);
aiov.iov_len = buflen;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_procp = p;
auio.uio_resid = buflen;
auio.uio_offset = fp->f_offset;
error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag);
mtx_enter(&fp->f_mtx);
fp->f_offset = auio.uio_offset;
mtx_leave(&fp->f_mtx);
VOP_UNLOCK(vp);
if (error)
goto bad;
*retval = buflen - auio.uio_resid;
bad:
FRELE(fp, p);
return (error);
}
/*
* Set the mode mask for creation of filesystem nodes.
*/
int
sys_umask(struct proc *p, void *v, register_t *retval)
{
struct sys_umask_args /* {
syscallarg(mode_t) newmask;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
fdplock(fdp);
*retval = fdp->fd_cmask;
fdp->fd_cmask = SCARG(uap, newmask) & ACCESSPERMS;
fdpunlock(fdp);
return (0);
}
/*
* Void all references to file by ripping underlying filesystem
* away from vnode.
*/
int
sys_revoke(struct proc *p, void *v, register_t *retval)
{
struct sys_revoke_args /* {
syscallarg(const char *) path;
} */ *uap = v;
struct vnode *vp;
struct vattr vattr;
int error;
struct nameidata nd;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
nd.ni_pledge = PLEDGE_RPATH | PLEDGE_TTY;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0)
return (error);
vp = nd.ni_vp;
if (vp->v_type != VCHR || (u_int)major(vp->v_rdev) >= nchrdev ||
cdevsw[major(vp->v_rdev)].d_type != D_TTY) {
error = ENOTTY;
goto out;
}
if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0)
goto out;
if (p->p_ucred->cr_uid != vattr.va_uid &&
(error = suser(p)))
goto out;
if (vp->v_usecount > 1 || (vp->v_flag & (VALIASED)))
VOP_REVOKE(vp, REVOKEALL);
out:
vrele(vp);
return (error);
}
/*
* Convert a user file descriptor to a kernel file entry.
*
* On return *fpp is FREF:ed.
*/
int
getvnode(struct proc *p, int fd, struct file **fpp)
{
struct file *fp;
struct vnode *vp;
if ((fp = fd_getfile(p->p_fd, fd)) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_VNODE) {
FRELE(fp, p);
return (EINVAL);
}
vp = fp->f_data;
if (vp->v_type == VBAD) {
FRELE(fp, p);
return (EBADF);
}
*fpp = fp;
return (0);
}
/*
* Positional read system call.
*/
int
sys_pread(struct proc *p, void *v, register_t *retval)
{
struct sys_pread_args /* {
syscallarg(int) fd;
syscallarg(void *) buf;
syscallarg(size_t) nbyte;
syscallarg(off_t) offset;
} */ *uap = v;
struct iovec iov;
struct uio auio;
iov.iov_base = SCARG(uap, buf);
iov.iov_len = SCARG(uap, nbyte);
if (iov.iov_len > SSIZE_MAX)
return (EINVAL);
auio.uio_iov = &iov;
auio.uio_iovcnt = 1;
auio.uio_resid = iov.iov_len;
auio.uio_offset = SCARG(uap, offset);
return (dofilereadv(p, SCARG(uap, fd), &auio, FO_POSITION, retval));
}
/*
* Positional scatter read system call.
*/
int
sys_preadv(struct proc *p, void *v, register_t *retval)
{
struct sys_preadv_args /* {
syscallarg(int) fd;
syscallarg(const struct iovec *) iovp;
syscallarg(int) iovcnt;
syscallarg(off_t) offset;
} */ *uap = v;
struct iovec aiov[UIO_SMALLIOV], *iov = NULL;
int error, iovcnt = SCARG(uap, iovcnt);
struct uio auio;
size_t resid;
error = iovec_copyin(SCARG(uap, iovp), &iov, aiov, iovcnt, &resid);
if (error)
goto done;
auio.uio_iov = iov;
auio.uio_iovcnt = iovcnt;
auio.uio_resid = resid;
auio.uio_offset = SCARG(uap, offset);
error = dofilereadv(p, SCARG(uap, fd), &auio, FO_POSITION, retval);
done:
iovec_free(iov, iovcnt);
return (error);
}
/*
* Positional write system call.
*/
int
sys_pwrite(struct proc *p, void *v, register_t *retval)
{
struct sys_pwrite_args /* {
syscallarg(int) fd;
syscallarg(const void *) buf;
syscallarg(size_t) nbyte;
syscallarg(off_t) offset;
} */ *uap = v;
struct iovec iov;
struct uio auio;
iov.iov_base = (void *)SCARG(uap, buf);
iov.iov_len = SCARG(uap, nbyte);
if (iov.iov_len > SSIZE_MAX)
return (EINVAL);
auio.uio_iov = &iov;
auio.uio_iovcnt = 1;
auio.uio_resid = iov.iov_len;
auio.uio_offset = SCARG(uap, offset);
return (dofilewritev(p, SCARG(uap, fd), &auio, FO_POSITION, retval));
}
/*
* Positional gather write system call.
*/
int
sys_pwritev(struct proc *p, void *v, register_t *retval)
{
struct sys_pwritev_args /* {
syscallarg(int) fd;
syscallarg(const struct iovec *) iovp;
syscallarg(int) iovcnt;
syscallarg(off_t) offset;
} */ *uap = v;
struct iovec aiov[UIO_SMALLIOV], *iov = NULL;
int error, iovcnt = SCARG(uap, iovcnt);
struct uio auio;
size_t resid;
error = iovec_copyin(SCARG(uap, iovp), &iov, aiov, iovcnt, &resid);
if (error)
goto done;
auio.uio_iov = iov;
auio.uio_iovcnt = iovcnt;
auio.uio_resid = resid;
auio.uio_offset = SCARG(uap, offset);
error = dofilewritev(p, SCARG(uap, fd), &auio, FO_POSITION, retval);
done:
iovec_free(iov, iovcnt);
return (error);
}
#if 1
int
sys_pad_pread(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_pread_args *uap = v;
struct sys_pread_args unpad;
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, buf) = SCARG(uap, buf);
SCARG(&unpad, nbyte) = SCARG(uap, nbyte);
SCARG(&unpad, offset) = SCARG(uap, offset);
return sys_pread(p, &unpad, retval);
}
int
sys_pad_preadv(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_preadv_args *uap = v;
struct sys_preadv_args unpad;
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, iovp) = SCARG(uap, iovp);
SCARG(&unpad, iovcnt) = SCARG(uap, iovcnt);
SCARG(&unpad, offset) = SCARG(uap, offset);
return sys_preadv(p, &unpad, retval);
}
int
sys_pad_pwrite(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_pwrite_args *uap = v;
struct sys_pwrite_args unpad;
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, buf) = SCARG(uap, buf);
SCARG(&unpad, nbyte) = SCARG(uap, nbyte);
SCARG(&unpad, offset) = SCARG(uap, offset);
return sys_pwrite(p, &unpad, retval);
}
int
sys_pad_pwritev(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_pwritev_args *uap = v;
struct sys_pwritev_args unpad;
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, iovp) = SCARG(uap, iovp);
SCARG(&unpad, iovcnt) = SCARG(uap, iovcnt);
SCARG(&unpad, offset) = SCARG(uap, offset);
return sys_pwritev(p, &unpad, retval);
}
#endif
14
1
7
7
4
9
9
9
5
4
5
14
12
9
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* $OpenBSD: kern_physio.c,v 1.47 2020/02/20 16:26:01 krw Exp $ */
/* $NetBSD: kern_physio.c,v 1.28 1997/05/19 10:43:28 pk Exp $ */
/*-
* Copyright (c) 1994 Christopher G. Demetriou
* Copyright (c) 1982, 1986, 1990, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_physio.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/pool.h>
#include <uvm/uvm_extern.h>
/*
* The routines implemented in this file are described in:
* Leffler, et al.: The Design and Implementation of the 4.3BSD
* UNIX Operating System (Addison Welley, 1989)
* on pages 231-233.
*/
/*
* Do "physical I/O" on behalf of a user. "Physical I/O" is I/O directly
* from the raw device to user buffers, and bypasses the buffer cache.
*
* Comments in brackets are from Leffler, et al.'s pseudo-code implementation.
*/
int
physio(void (*strategy)(struct buf *), dev_t dev, int flags,
void (*minphys)(struct buf *), struct uio *uio)
{
struct iovec *iovp;
struct proc *p = curproc;
long done, todo;
int error, i, s;
struct buf *bp;
if ((uio->uio_offset % DEV_BSIZE) != 0)
return (EINVAL);
error = 0;
flags &= B_READ | B_WRITE;
/* Create a buffer. */
s = splbio();
bp = pool_get(&bufpool, PR_WAITOK | PR_ZERO);
/* [set up the fixed part of the buffer for a transfer] */
bp->b_vnbufs.le_next = NOLIST;
bp->b_dev = dev;
bp->b_error = 0;
bp->b_proc = p;
bp->b_flags = B_BUSY;
LIST_INIT(&bp->b_dep);
splx(s);
/*
* [while there are data to transfer and no I/O error]
* Note that I/O errors are handled with a 'goto' at the bottom
* of the 'while' loop.
*/
for (i = 0; i < uio->uio_iovcnt; i++) {
iovp = &uio->uio_iov[i];
while (iovp->iov_len > 0) {
void *map = NULL;
/*
* [mark the buffer busy for physical I/O]
* (i.e. set B_PHYS (because it's an I/O to user
* memory), and B_RAW, because B_RAW is to be
* "Set by physio for raw transfers.", in addition
* to the "busy" and read/write flag.)
*/
CLR(bp->b_flags, B_DONE | B_ERROR);
bp->b_flags |= (B_BUSY | B_PHYS | B_RAW | flags);
/* [set up the buffer for a maximum-sized transfer] */
bp->b_blkno = btodb(uio->uio_offset);
/*
* Because iov_len is size_t (unsigned) but b_bcount is
* long (signed), an overflow is possible. Therefore
* limit b_bcount to LONG_MAX before calling the provided
* minphys.
*/
if (iovp->iov_len > LONG_MAX)
bp->b_bcount = LONG_MAX;
else
bp->b_bcount = iovp->iov_len;
/*
* [call minphys to bound the transfer size]
* and remember the amount of data to transfer,
* for later comparison.
*/
(*minphys)(bp);
todo = bp->b_bcount;
KASSERTMSG(todo >= 0, "minphys broken");
/*
* [lock the part of the user address space involved
* in the transfer]
* Beware vmapbuf(); it clobbers b_data and
* saves it in b_saveaddr. However, vunmapbuf()
* restores it.
*/
error = uvm_vslock_device(p, iovp->iov_base, todo,
(flags & B_READ) ?
PROT_READ | PROT_WRITE : PROT_READ, &map);
if (error)
goto done;
if (map) {
bp->b_data = map;
} else {
bp->b_data = iovp->iov_base;
vmapbuf(bp, todo);
}
/* [call strategy to start the transfer] */
(*strategy)(bp);
/*
* Note that the raise/wait/lower/get error
* steps below would be done by biowait(), but
* we want to unlock the address space before
* we lower the priority.
*
* [raise the priority level to splbio]
*/
s = splbio();
/* [wait for the transfer to complete] */
while ((bp->b_flags & B_DONE) == 0)
tsleep_nsec(bp, PRIBIO + 1, "physio", INFSLP);
/* Mark it busy again, so nobody else will use it. */
bp->b_flags |= B_BUSY;
/* [lower the priority level] */
splx(s);
/*
* [unlock the part of the address space previously
* locked]
*/
if (!map)
vunmapbuf(bp, todo);
uvm_vsunlock_device(p, iovp->iov_base, todo, map);
/* remember error value (save a splbio/splx pair) */
if (bp->b_flags & B_ERROR)
error = (bp->b_error ? bp->b_error : EIO);
/*
* [deduct the transfer size from the total number
* of data to transfer]
*/
KASSERTMSG(bp->b_resid <= LONG_MAX, "strategy broken");
done = bp->b_bcount - bp->b_resid;
KASSERTMSG(done >= 0, "strategy broken");
KASSERTMSG(done <= todo, "strategy broken");
iovp->iov_len -= done;
iovp->iov_base = (caddr_t)iovp->iov_base + done;
uio->uio_offset += done;
uio->uio_resid -= done;
/*
* Now, check for an error.
* Also, handle weird end-of-disk semantics.
*/
if (error || done < todo)
goto done;
}
}
done:
/*
* [clean up the state of the buffer]
*/
s = splbio();
/* XXXCDC: is this necessary? */
if (bp->b_vp)
brelvp(bp);
splx(s);
pool_put(&bufpool, bp);
return (error);
}
/*
* Leffler, et al., says on p. 231:
* "The minphys() routine is called by physio() to adjust the
* size of each I/O transfer before the latter is passed to
* the strategy routine..."
*
* so, just adjust the buffer's count accounting to MAXPHYS here,
* and return the new count;
*/
void
minphys(struct buf *bp)
{
if (bp->b_bcount > MAXPHYS)
bp->b_bcount = MAXPHYS;
}
25
9
7
9
5
10
117
15
9
96
2
15
9
77
73
4
74
1
67
5
9
8
8
10
2
13
1
1
1
2
2
1
2
4
1
2
2
1
2
24
15
18
14
17
2
4
2
11
12
11
1
4
10
2
3
10
11
3
8
3
1
7
5
5
5
4
4
1
6
2
1
1
1
1
3
10
10
4
7
7
3
10
4
6
3
6
4
9
1
20
20
20
2
3
3
7
19
42
42
23
42
42
42
1
20
20
19
33
33
7
13
17
39
39
30
31
19
19
30
51
26
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
/* $OpenBSD: in.c,v 1.176 2022/08/29 07:51:45 bluhm Exp $ */
/* $NetBSD: in.c,v 1.26 1996/02/13 23:41:39 christos Exp $ */
/*
* Copyright (C) 2001 WIDE Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in.c 8.2 (Berkeley) 11/15/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/igmp_var.h>
#ifdef MROUTING
#include <netinet/ip_mroute.h>
#endif
#include "ether.h"
void in_socktrim(struct sockaddr_in *);
int in_ioctl_set_ifaddr(u_long, caddr_t, struct ifnet *, int);
int in_ioctl_change_ifaddr(u_long, caddr_t, struct ifnet *, int);
int in_ioctl_get(u_long, caddr_t, struct ifnet *);
void in_purgeaddr(struct ifaddr *);
int in_addhost(struct in_ifaddr *, struct sockaddr_in *);
int in_scrubhost(struct in_ifaddr *, struct sockaddr_in *);
int in_insert_prefix(struct in_ifaddr *);
void in_remove_prefix(struct in_ifaddr *);
/*
* Determine whether an IP address is in a reserved set of addresses
* that may not be forwarded, or whether datagrams to that destination
* may be forwarded.
*/
int
in_canforward(struct in_addr in)
{
u_int32_t net;
if (IN_MULTICAST(in.s_addr))
return (0);
if (IN_CLASSA(in.s_addr)) {
net = in.s_addr & IN_CLASSA_NET;
if (net == 0 ||
net == htonl(IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
return (0);
}
return (1);
}
/*
* Trim a mask in a sockaddr
*/
void
in_socktrim(struct sockaddr_in *ap)
{
char *cplim = (char *) &ap->sin_addr;
char *cp = (char *) (&ap->sin_addr + 1);
ap->sin_len = 0;
while (--cp >= cplim)
if (*cp) {
(ap)->sin_len = cp - (char *) (ap) + 1;
break;
}
}
int
in_mask2len(struct in_addr *mask)
{
int x, y;
u_char *p;
p = (u_char *)mask;
for (x = 0; x < sizeof(*mask); x++) {
if (p[x] != 0xff)
break;
}
y = 0;
if (x < sizeof(*mask)) {
for (y = 0; y < 8; y++) {
if ((p[x] & (0x80 >> y)) == 0)
break;
}
}
return x * 8 + y;
}
void
in_len2mask(struct in_addr *mask, int len)
{
int i;
u_char *p;
p = (u_char *)mask;
bzero(mask, sizeof(*mask));
for (i = 0; i < len / 8; i++)
p[i] = 0xff;
if (len % 8)
p[i] = (0xff00 >> (len % 8)) & 0xff;
}
int
in_nam2sin(const struct mbuf *nam, struct sockaddr_in **sin)
{
struct sockaddr *sa = mtod(nam, struct sockaddr *);
if (nam->m_len < offsetof(struct sockaddr, sa_data))
return EINVAL;
if (sa->sa_family != AF_INET)
return EAFNOSUPPORT;
if (sa->sa_len != nam->m_len)
return EINVAL;
if (sa->sa_len != sizeof(struct sockaddr_in))
return EINVAL;
*sin = satosin(sa);
return 0;
}
int
in_sa2sin(struct sockaddr *sa, struct sockaddr_in **sin)
{
if (sa->sa_family != AF_INET)
return EAFNOSUPPORT;
if (sa->sa_len != sizeof(struct sockaddr_in))
return EINVAL;
*sin = satosin(sa);
return 0;
}
int
in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp)
{
int privileged;
int error;
privileged = 0;
if ((so->so_state & SS_PRIV) != 0)
privileged++;
switch (cmd) {
#ifdef MROUTING
case SIOCGETVIFCNT:
case SIOCGETSGCNT:
error = mrt_ioctl(so, cmd, data);
break;
#endif /* MROUTING */
default:
error = in_ioctl(cmd, data, ifp, privileged);
break;
}
return error;
}
int
in_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
{
struct ifreq *ifr = (struct ifreq *)data;
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
struct sockaddr_in *sin = NULL, oldaddr;
int error = 0;
if (ifp == NULL)
return (ENXIO);
switch (cmd) {
case SIOCGIFADDR:
case SIOCGIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCGIFBRDADDR:
return in_ioctl_get(cmd, data, ifp);
case SIOCSIFADDR:
return in_ioctl_set_ifaddr(cmd, data, ifp, privileged);
case SIOCAIFADDR:
case SIOCDIFADDR:
return in_ioctl_change_ifaddr(cmd, data, ifp, privileged);
case SIOCSIFNETMASK:
case SIOCSIFDSTADDR:
case SIOCSIFBRDADDR:
break;
default:
return (EOPNOTSUPP);
}
if (ifr->ifr_addr.sa_family == AF_INET) {
error = in_sa2sin(&ifr->ifr_addr, &sin);
if (error)
return (error);
}
NET_LOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
/* find first address or exact match */
if (ia == NULL)
ia = ifatoia(ifa);
if (sin == NULL || sin->sin_addr.s_addr == INADDR_ANY)
break;
if (ifatoia(ifa)->ia_addr.sin_addr.s_addr ==
sin->sin_addr.s_addr) {
ia = ifatoia(ifa);
break;
}
}
if (ia == NULL) {
error = EADDRNOTAVAIL;
goto err;
}
switch (cmd) {
case SIOCSIFDSTADDR:
if (!privileged) {
error = EPERM;
break;
}
if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
error = EINVAL;
break;
}
error = in_sa2sin(&ifr->ifr_dstaddr, &sin);
if (error)
break;
oldaddr = ia->ia_dstaddr;
ia->ia_dstaddr = *sin;
error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, (caddr_t)ia);
if (error) {
ia->ia_dstaddr = oldaddr;
break;
}
in_scrubhost(ia, &oldaddr);
in_addhost(ia, &ia->ia_dstaddr);
break;
case SIOCSIFBRDADDR:
if (!privileged) {
error = EPERM;
break;
}
if ((ifp->if_flags & IFF_BROADCAST) == 0) {
error = EINVAL;
break;
}
error = in_sa2sin(&ifr->ifr_broadaddr, &sin);
if (error)
break;
ifa_update_broadaddr(ifp, &ia->ia_ifa, sintosa(sin));
break;
case SIOCSIFNETMASK:
if (!privileged) {
error = EPERM;
break;
}
if (ifr->ifr_addr.sa_len < 8) {
error = EINVAL;
break;
}
/* do not check inet family or strict len */
sin = satosin(&ifr->ifr_addr);
if (ntohl(sin->sin_addr.s_addr) &
(~ntohl(sin->sin_addr.s_addr) >> 1)) {
/* non-contiguous netmask */
error = EINVAL;
break;
}
ia->ia_netmask = ia->ia_sockmask.sin_addr.s_addr =
sin->sin_addr.s_addr;
break;
}
err:
NET_UNLOCK();
return (error);
}
int
in_ioctl_set_ifaddr(u_long cmd, caddr_t data, struct ifnet *ifp,
int privileged)
{
struct ifreq *ifr = (struct ifreq *)data;
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
struct sockaddr_in *sin;
int error = 0;
int newifaddr;
if (cmd != SIOCSIFADDR)
panic("%s: invalid ioctl %lu", __func__, cmd);
if (!privileged)
return (EPERM);
error = in_sa2sin(&ifr->ifr_addr, &sin);
if (error)
return (error);
NET_LOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
/* find first address */
ia = ifatoia(ifa);
break;
}
if (ia == NULL) {
ia = malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
refcnt_init_trace(&ia->ia_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
ia->ia_addr.sin_family = AF_INET;
ia->ia_addr.sin_len = sizeof(ia->ia_addr);
ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
ia->ia_sockmask.sin_len = 8;
if (ifp->if_flags & IFF_BROADCAST) {
ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
ia->ia_broadaddr.sin_family = AF_INET;
}
ia->ia_ifp = ifp;
newifaddr = 1;
} else
newifaddr = 0;
in_ifscrub(ifp, ia);
error = in_ifinit(ifp, ia, sin, newifaddr);
if (!error)
if_addrhooks_run(ifp);
NET_UNLOCK();
return error;
}
int
in_ioctl_change_ifaddr(u_long cmd, caddr_t data, struct ifnet *ifp,
int privileged)
{
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
struct in_aliasreq *ifra = (struct in_aliasreq *)data;
struct sockaddr_in *sin = NULL, *dstsin = NULL, *broadsin = NULL;
struct sockaddr_in *masksin = NULL;
int error = 0;
int newifaddr;
if (ifra->ifra_addr.sin_family == AF_INET) {
error = in_sa2sin(sintosa(&ifra->ifra_addr), &sin);
if (error)
return (error);
}
NET_LOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
/* find first address, if no exact match wanted */
if (sin == NULL || sin->sin_addr.s_addr ==
ifatoia(ifa)->ia_addr.sin_addr.s_addr) {
ia = ifatoia(ifa);
break;
}
}
switch (cmd) {
case SIOCAIFADDR: {
int needinit = 0;
if (!privileged) {
error = EPERM;
break;
}
if (ifra->ifra_mask.sin_len) {
if (ifra->ifra_mask.sin_len < 8) {
error = EINVAL;
break;
}
/* do not check inet family or strict len */
masksin = &ifra->ifra_mask;
if (ntohl(masksin->sin_addr.s_addr) &
(~ntohl(masksin->sin_addr.s_addr) >> 1)) {
/* non-contiguous netmask */
error = EINVAL;
break;
}
}
if ((ifp->if_flags & IFF_POINTOPOINT) &&
ifra->ifra_dstaddr.sin_family == AF_INET) {
error = in_sa2sin(sintosa(&ifra->ifra_dstaddr),
&dstsin);
if (error)
break;
}
if ((ifp->if_flags & IFF_BROADCAST) &&
ifra->ifra_broadaddr.sin_family == AF_INET) {
error = in_sa2sin(sintosa(&ifra->ifra_broadaddr),
&broadsin);
if (error)
break;
}
if (ia == NULL) {
ia = malloc(sizeof *ia, M_IFADDR, M_WAITOK | M_ZERO);
refcnt_init_trace(&ia->ia_ifa.ifa_refcnt,
DT_REFCNT_IDX_IFADDR);
ia->ia_addr.sin_family = AF_INET;
ia->ia_addr.sin_len = sizeof(ia->ia_addr);
ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
ia->ia_sockmask.sin_len = 8;
if (ifp->if_flags & IFF_BROADCAST) {
ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
ia->ia_broadaddr.sin_family = AF_INET;
}
ia->ia_ifp = ifp;
newifaddr = 1;
} else
newifaddr = 0;
if (sin == NULL) {
sin = &ia->ia_addr;
} else if (newifaddr ||
sin->sin_addr.s_addr != ia->ia_addr.sin_addr.s_addr) {
needinit = 1;
}
if (masksin != NULL) {
in_ifscrub(ifp, ia);
ia->ia_netmask = ia->ia_sockmask.sin_addr.s_addr =
masksin->sin_addr.s_addr;
needinit = 1;
}
if (dstsin != NULL) {
in_ifscrub(ifp, ia);
ia->ia_dstaddr = *dstsin;
needinit = 1;
}
if (broadsin != NULL) {
if (newifaddr)
ia->ia_broadaddr = *broadsin;
else
ifa_update_broadaddr(ifp, &ia->ia_ifa,
sintosa(broadsin));
}
if (needinit) {
error = in_ifinit(ifp, ia, sin, newifaddr);
if (error)
break;
}
if_addrhooks_run(ifp);
break;
}
case SIOCDIFADDR:
if (!privileged) {
error = EPERM;
break;
}
if (ia == NULL) {
error = EADDRNOTAVAIL;
break;
}
/*
* Even if the individual steps were safe, shouldn't
* these kinds of changes happen atomically? What
* should happen to a packet that was routed after
* the scrub but before the other steps?
*/
in_purgeaddr(&ia->ia_ifa);
if_addrhooks_run(ifp);
break;
default:
panic("%s: invalid ioctl %lu", __func__, cmd);
}
NET_UNLOCK();
return (error);
}
int
in_ioctl_get(u_long cmd, caddr_t data, struct ifnet *ifp)
{
struct ifreq *ifr = (struct ifreq *)data;
struct ifaddr *ifa;
struct in_ifaddr *ia = NULL;
struct sockaddr *sa;
struct sockaddr_in *sin = NULL;
int error = 0;
sa = &ifr->ifr_addr;
if (sa->sa_family == AF_INET) {
sa->sa_len = sizeof(struct sockaddr_in);
error = in_sa2sin(sa, &sin);
if (error)
return (error);
}
NET_LOCK_SHARED();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
/* find first address or exact match */
if (ia == NULL)
ia = ifatoia(ifa);
if (sin == NULL || sin->sin_addr.s_addr == INADDR_ANY)
break;
if (ifatoia(ifa)->ia_addr.sin_addr.s_addr ==
sin->sin_addr.s_addr) {
ia = ifatoia(ifa);
break;
}
}
if (ia == NULL) {
error = EADDRNOTAVAIL;
goto err;
}
switch(cmd) {
case SIOCGIFADDR:
*satosin(&ifr->ifr_addr) = ia->ia_addr;
break;
case SIOCGIFBRDADDR:
if ((ifp->if_flags & IFF_BROADCAST) == 0) {
error = EINVAL;
break;
}
*satosin(&ifr->ifr_dstaddr) = ia->ia_broadaddr;
break;
case SIOCGIFDSTADDR:
if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
error = EINVAL;
break;
}
*satosin(&ifr->ifr_dstaddr) = ia->ia_dstaddr;
break;
case SIOCGIFNETMASK:
*satosin(&ifr->ifr_addr) = ia->ia_sockmask;
break;
default:
panic("%s: invalid ioctl %lu", __func__, cmd);
}
err:
NET_UNLOCK_SHARED();
return (error);
}
/*
* Delete any existing route for an interface.
*/
void
in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia)
{
if (ISSET(ifp->if_flags, IFF_POINTOPOINT))
in_scrubhost(ia, &ia->ia_dstaddr);
else if (!ISSET(ifp->if_flags, IFF_LOOPBACK))
in_remove_prefix(ia);
}
/*
* Initialize an interface's internet address
* and routing table entry.
*/
int
in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin,
int newaddr)
{
u_int32_t i = sin->sin_addr.s_addr;
struct sockaddr_in oldaddr;
int error = 0, rterror;
NET_ASSERT_LOCKED();
/*
* Always remove the address from the tree to make sure its
* position gets updated in case the key changes.
*/
if (!newaddr) {
rt_ifa_dellocal(&ia->ia_ifa);
ifa_del(ifp, &ia->ia_ifa);
}
oldaddr = ia->ia_addr;
ia->ia_addr = *sin;
if (ia->ia_netmask == 0) {
if (IN_CLASSA(i))
ia->ia_netmask = IN_CLASSA_NET;
else if (IN_CLASSB(i))
ia->ia_netmask = IN_CLASSB_NET;
else
ia->ia_netmask = IN_CLASSC_NET;
ia->ia_sockmask.sin_addr.s_addr = ia->ia_netmask;
}
/*
* Give the interface a chance to initialize
* if this is its first address,
* and to validate the address if necessary.
*/
if ((error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
ia->ia_addr = oldaddr;
}
/*
* Add the address to the local list and the global tree. If an
* error occurred, put back the original address.
*/
ifa_add(ifp, &ia->ia_ifa);
rterror = rt_ifa_addlocal(&ia->ia_ifa);
if (rterror) {
if (!newaddr)
ifa_del(ifp, &ia->ia_ifa);
if (!error)
error = rterror;
goto out;
}
if (error)
goto out;
ia->ia_net = i & ia->ia_netmask;
in_socktrim(&ia->ia_sockmask);
/*
* Add route for the network.
*/
ia->ia_ifa.ifa_metric = ifp->if_metric;
if (ISSET(ifp->if_flags, IFF_BROADCAST)) {
if (IN_RFC3021_SUBNET(ia->ia_netmask))
ia->ia_broadaddr.sin_addr.s_addr = 0;
else {
ia->ia_broadaddr.sin_addr.s_addr =
ia->ia_net | ~ia->ia_netmask;
}
}
if (ISSET(ifp->if_flags, IFF_POINTOPOINT)) {
/* XXX We should not even call in_ifinit() in this case. */
if (ia->ia_dstaddr.sin_family != AF_INET)
goto out;
error = in_addhost(ia, &ia->ia_dstaddr);
} else if (!ISSET(ifp->if_flags, IFF_LOOPBACK)) {
error = in_insert_prefix(ia);
}
/*
* If the interface supports multicast, join the "all hosts"
* multicast group on that interface.
*/
if ((ifp->if_flags & IFF_MULTICAST) && ia->ia_allhosts == NULL) {
struct in_addr addr;
addr.s_addr = INADDR_ALLHOSTS_GROUP;
ia->ia_allhosts = in_addmulti(&addr, ifp);
}
out:
if (error && newaddr)
in_purgeaddr(&ia->ia_ifa);
return (error);
}
void
in_purgeaddr(struct ifaddr *ifa)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct in_ifaddr *ia = ifatoia(ifa);
NET_ASSERT_LOCKED();
in_ifscrub(ifp, ia);
rt_ifa_dellocal(&ia->ia_ifa);
rt_ifa_purge(&ia->ia_ifa);
ifa_del(ifp, &ia->ia_ifa);
if (ia->ia_allhosts != NULL) {
in_delmulti(ia->ia_allhosts);
ia->ia_allhosts = NULL;
}
ia->ia_ifp = NULL;
ifafree(&ia->ia_ifa);
}
int
in_addhost(struct in_ifaddr *ia, struct sockaddr_in *dst)
{
return rt_ifa_add(&ia->ia_ifa, RTF_HOST | RTF_MPATH,
sintosa(dst), ia->ia_ifa.ifa_ifp->if_rdomain);
}
int
in_scrubhost(struct in_ifaddr *ia, struct sockaddr_in *dst)
{
return rt_ifa_del(&ia->ia_ifa, RTF_HOST,
sintosa(dst), ia->ia_ifa.ifa_ifp->if_rdomain);
}
/*
* Insert the cloning and broadcast routes for this subnet.
*/
int
in_insert_prefix(struct in_ifaddr *ia)
{
struct ifaddr *ifa = &ia->ia_ifa;
int error;
error = rt_ifa_add(ifa, RTF_CLONING | RTF_CONNECTED | RTF_MPATH,
ifa->ifa_addr, ifa->ifa_ifp->if_rdomain);
if (error)
return (error);
if (ia->ia_broadaddr.sin_addr.s_addr != 0) {
error = rt_ifa_add(ifa, RTF_HOST | RTF_BROADCAST | RTF_MPATH,
ifa->ifa_broadaddr, ifa->ifa_ifp->if_rdomain);
}
return (error);
}
void
in_remove_prefix(struct in_ifaddr *ia)
{
struct ifaddr *ifa = &ia->ia_ifa;
rt_ifa_del(ifa, RTF_CLONING | RTF_CONNECTED,
ifa->ifa_addr, ifa->ifa_ifp->if_rdomain);
if (ia->ia_broadaddr.sin_addr.s_addr != 0) {
rt_ifa_del(ifa, RTF_HOST | RTF_BROADCAST,
ifa->ifa_broadaddr, ifa->ifa_ifp->if_rdomain);
}
}
/*
* Return 1 if the address is a local broadcast address.
*/
int
in_broadcast(struct in_addr in, u_int rtableid)
{
struct ifnet *ifn;
struct ifaddr *ifa;
u_int rdomain;
rdomain = rtable_l2(rtableid);
#define ia (ifatoia(ifa))
TAILQ_FOREACH(ifn, &ifnet, if_list) {
if (ifn->if_rdomain != rdomain)
continue;
if ((ifn->if_flags & IFF_BROADCAST) == 0)
continue;
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list)
if (ifa->ifa_addr->sa_family == AF_INET &&
in.s_addr != ia->ia_addr.sin_addr.s_addr &&
in.s_addr == ia->ia_broadaddr.sin_addr.s_addr)
return 1;
}
return (0);
#undef ia
}
/*
* Add an address to the list of IP multicast addresses for a given interface.
*/
struct in_multi *
in_addmulti(struct in_addr *ap, struct ifnet *ifp)
{
struct in_multi *inm;
struct ifreq ifr;
/*
* See if address already in list.
*/
IN_LOOKUP_MULTI(*ap, ifp, inm);
if (inm != NULL) {
/*
* Found it; just increment the reference count.
*/
++inm->inm_refcnt;
} else {
/*
* New address; allocate a new multicast record
* and link it into the interface's multicast list.
*/
inm = malloc(sizeof(*inm), M_IPMADDR, M_WAITOK | M_ZERO);
inm->inm_sin.sin_len = sizeof(struct sockaddr_in);
inm->inm_sin.sin_family = AF_INET;
inm->inm_sin.sin_addr = *ap;
inm->inm_refcnt = 1;
inm->inm_ifidx = ifp->if_index;
inm->inm_ifma.ifma_addr = sintosa(&inm->inm_sin);
/*
* Ask the network driver to update its multicast reception
* filter appropriately for the new address.
*/
memset(&ifr, 0, sizeof(ifr));
memcpy(&ifr.ifr_addr, &inm->inm_sin, sizeof(inm->inm_sin));
if ((*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
free(inm, M_IPMADDR, sizeof(*inm));
return (NULL);
}
TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &inm->inm_ifma,
ifma_list);
/*
* Let IGMP know that we have joined a new IP multicast group.
*/
igmp_joingroup(inm, ifp);
}
return (inm);
}
/*
* Delete a multicast address record.
*/
void
in_delmulti(struct in_multi *inm)
{
struct ifreq ifr;
struct ifnet *ifp;
NET_ASSERT_LOCKED();
if (--inm->inm_refcnt != 0)
return;
ifp = if_get(inm->inm_ifidx);
if (ifp != NULL) {
/*
* No remaining claims to this record; let IGMP know that
* we are leaving the multicast group.
*/
igmp_leavegroup(inm, ifp);
/*
* Notify the network driver to update its multicast
* reception filter.
*/
memset(&ifr, 0, sizeof(ifr));
satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in);
satosin(&ifr.ifr_addr)->sin_family = AF_INET;
satosin(&ifr.ifr_addr)->sin_addr = inm->inm_addr;
KERNEL_LOCK();
(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
KERNEL_UNLOCK();
TAILQ_REMOVE(&ifp->if_maddrlist, &inm->inm_ifma, ifma_list);
}
if_put(ifp);
free(inm, M_IPMADDR, sizeof(*inm));
}
/*
* Return 1 if the multicast group represented by ``ap'' has been
* joined by interface ``ifp'', 0 otherwise.
*/
int
in_hasmulti(struct in_addr *ap, struct ifnet *ifp)
{
struct in_multi *inm;
int joined;
IN_LOOKUP_MULTI(*ap, ifp, inm);
joined = (inm != NULL);
return (joined);
}
void
in_ifdetach(struct ifnet *ifp)
{
struct ifaddr *ifa, *next;
/* nuke any of IPv4 addresses we have */
TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, ifa_list, next) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
in_purgeaddr(ifa);
if_addrhooks_run(ifp);
}
if (ifp->if_xflags & IFXF_AUTOCONF4)
ifp->if_xflags &= ~IFXF_AUTOCONF4;
}
void
in_prefixlen2mask(struct in_addr *maskp, int plen)
{
if (plen == 0)
maskp->s_addr = 0;
else
maskp->s_addr = htonl(0xffffffff << (32 - plen));
}
24
24
10
14
14
14
8
6
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* $OpenBSD: mpls_output.c,v 1.28 2019/09/03 10:39:08 jsg Exp $ */
/*
* Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2008 Michele Marchetto <michele@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netmpls/mpls.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif
#ifdef MPLS_DEBUG
#define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
#endif
void mpls_do_cksum(struct mbuf *);
u_int8_t mpls_getttl(struct mbuf *, sa_family_t);
int
mpls_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
struct sockaddr_mpls *smpls;
struct sockaddr_mpls sa_mpls;
struct shim_hdr *shim;
struct rt_mpls *rt_mpls;
int error;
u_int8_t ttl;
if (rt == NULL || (dst->sa_family != AF_INET &&
dst->sa_family != AF_INET6 && dst->sa_family != AF_MPLS)) {
if (!ISSET(ifp->if_xflags, IFXF_MPLS))
return (ifp->if_output(ifp, m, dst, rt));
else
return (ifp->if_ll_output(ifp, m, dst, rt));
}
/* need to calculate checksums now if necessary */
mpls_do_cksum(m);
/* initialize sockaddr_mpls */
bzero(&sa_mpls, sizeof(sa_mpls));
smpls = &sa_mpls;
smpls->smpls_family = AF_MPLS;
smpls->smpls_len = sizeof(*smpls);
ttl = mpls_getttl(m, dst->sa_family);
rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
/* no MPLS information for this entry */
if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
#ifdef MPLS_DEBUG
printf("MPLS_DEBUG: interface not mpls enabled\n");
#endif
error = ENETUNREACH;
goto bad;
}
return (ifp->if_ll_output(ifp, m, dst, rt));
}
/* to be honest here only the push operation makes sense */
switch (rt_mpls->mpls_operation) {
case MPLS_OP_PUSH:
m = mpls_shim_push(m, rt_mpls);
break;
case MPLS_OP_POP:
m = mpls_shim_pop(m);
break;
case MPLS_OP_SWAP:
m = mpls_shim_swap(m, rt_mpls);
break;
default:
error = EINVAL;
goto bad;
}
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
/* refetch label */
shim = mtod(m, struct shim_hdr *);
/* mark first label with BOS flag */
if (dst->sa_family != AF_MPLS)
shim->shim_label |= MPLS_BOS_MASK;
/* write back TTL */
shim->shim_label &= ~MPLS_TTL_MASK;
shim->shim_label |= htonl(ttl);
#ifdef MPLS_DEBUG
printf("MPLS: sending on %s outshim %x outlabel %d\n",
ifp->if_xname, ntohl(shim->shim_label),
MPLS_LABEL_GET(rt_mpls->mpls_label));
#endif
/* Output iface is not MPLS-enabled */
if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
#ifdef MPLS_DEBUG
printf("MPLS_DEBUG: interface not mpls enabled\n");
#endif
error = ENETUNREACH;
goto bad;
}
/* reset broadcast and multicast flags, this is a P2P tunnel */
m->m_flags &= ~(M_BCAST | M_MCAST);
smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
error = ifp->if_ll_output(ifp, m, smplstosa(smpls), rt);
return (error);
bad:
m_freem(m);
return (error);
}
void
mpls_do_cksum(struct mbuf *m)
{
struct ip *ip;
u_int16_t hlen;
in_proto_cksum_out(m, NULL);
if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) {
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
ip->ip_sum = in_cksum(m, hlen);
m->m_pkthdr.csum_flags &= ~M_IPV4_CSUM_OUT;
}
}
u_int8_t
mpls_getttl(struct mbuf *m, sa_family_t af)
{
struct mbuf *n;
int loc, off;
u_int8_t ttl = mpls_defttl;
/* If the AF is MPLS then inherit the TTL from the present label. */
if (af == AF_MPLS)
loc = 3;
else {
switch (*mtod(m, uint8_t *) >> 4) {
case 4:
if (!mpls_mapttl_ip)
return (ttl);
loc = offsetof(struct ip, ip_ttl);
break;
#ifdef INET6
case 6:
if (!mpls_mapttl_ip6)
return (ttl);
loc = offsetof(struct ip6_hdr, ip6_hlim);
break;
#endif
default:
return (ttl);
}
}
n = m_getptr(m, loc, &off);
if (n == NULL)
return (ttl);
ttl = *(mtod(n, uint8_t *) + off);
return (ttl);
}
5
5
2
2
3
3
3
4
3
7
7
1
133
132
1
131
1
2
26
5
1
2
1
2
3
2
1
1
2
1
1
1
1
1
1
2
1
1
196
198
197
197
2
198
2
103
84
102
100
156
163
164
2
163
22
22
22
21
22
131
131
2
1
1
1
125
81
43
1
124
119
1
1
1
89
27
4
17
94
3
2
39
34
58
31
3
10
6
2
5
22
75
1
7
69
69
1
13
4
9
5
1
1
18
1
7
1
6
4
8
2
6
2
1
5
2
3
5
31
14
4
2
8
2
4
8
14
7
14
15
15
13
2
15
14
14
12
1
4
1
2
2
1
4
18
119
117
116
2
2
115
112
93
84
2
7
1
4
3
6
124
125
125
101
56
100
123
125
102
125
9
32
32
32
22
10
22
10
10
10
47
46
26
13
34
34
49
82
82
33
33
27
27
6
1
1
3
1
6
6
6
6
4
4
4
4
4
1
4
4
4
3
2
31
2
2
9
17
9
7
1
7
4
2
2
1
2
2
1
5
3
11
16
3
8
1
2
2
6
2
2
5
2
1
6
6
5
1
1
3
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
/* $OpenBSD: rtsock.c,v 1.354 2022/09/05 10:31:25 mvs Exp $ */
/* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)rtsock.c 8.6 (Berkeley) 2/11/95
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/pool.h>
#include <sys/protosw.h>
#include <sys/srp.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#ifdef MPLS
#include <netmpls/mpls.h>
#endif
#ifdef IPSEC
#include <netinet/ip_ipsp.h>
#include <net/if_enc.h>
#endif
#ifdef BFD
#include <net/bfd.h>
#endif
#include <sys/stdarg.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#define ROUTESNDQ 8192
#define ROUTERCVQ 8192
const struct sockaddr route_src = { 2, PF_ROUTE, };
struct walkarg {
int w_op, w_arg, w_tmemsize;
size_t w_given, w_needed;
caddr_t w_where, w_tmem;
};
void route_prinit(void);
void rcb_ref(void *, void *);
void rcb_unref(void *, void *);
int route_output(struct mbuf *, struct socket *);
int route_ctloutput(int, struct socket *, int, int, struct mbuf *);
int route_attach(struct socket *, int);
int route_detach(struct socket *);
int route_disconnect(struct socket *);
int route_shutdown(struct socket *);
int route_rcvd(struct socket *);
int route_send(struct socket *, struct mbuf *, struct mbuf *,
struct mbuf *);
int route_abort(struct socket *);
int route_sockaddr(struct socket *, struct mbuf *);
int route_peeraddr(struct socket *, struct mbuf *);
void route_input(struct mbuf *m0, struct socket *, sa_family_t);
int route_arp_conflict(struct rtentry *, struct rt_addrinfo *);
int route_cleargateway(struct rtentry *, void *, unsigned int);
void rtm_senddesync_timer(void *);
void rtm_senddesync(struct socket *);
int rtm_sendup(struct socket *, struct mbuf *);
int rtm_getifa(struct rt_addrinfo *, unsigned int);
int rtm_output(struct rt_msghdr *, struct rtentry **, struct rt_addrinfo *,
uint8_t, unsigned int);
struct rt_msghdr *rtm_report(struct rtentry *, u_char, int, int);
struct mbuf *rtm_msg1(int, struct rt_addrinfo *);
int rtm_msg2(int, int, struct rt_addrinfo *, caddr_t,
struct walkarg *);
int rtm_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *);
int rtm_validate_proposal(struct rt_addrinfo *);
void rtm_setmetrics(u_long, const struct rt_metrics *,
struct rt_kmetrics *);
void rtm_getmetrics(const struct rtentry *,
struct rt_metrics *);
int sysctl_iflist(int, struct walkarg *);
int sysctl_ifnames(struct walkarg *);
int sysctl_rtable_rtstat(void *, size_t *, void *);
int rt_setsource(unsigned int, struct sockaddr *);
/*
* Locks used to protect struct members
* I immutable after creation
* s solock
*/
struct rtpcb {
struct socket *rop_socket; /* [I] */
SRPL_ENTRY(rtpcb) rop_list;
struct refcnt rop_refcnt;
struct timeout rop_timeout;
unsigned int rop_msgfilter; /* [s] */
unsigned int rop_flagfilter; /* [s] */
unsigned int rop_flags; /* [s] */
u_int rop_rtableid; /* [s] */
unsigned short rop_proto; /* [I] */
u_char rop_priority; /* [s] */
};
#define sotortpcb(so) ((struct rtpcb *)(so)->so_pcb)
struct rtptable {
SRPL_HEAD(, rtpcb) rtp_list;
struct srpl_rc rtp_rc;
struct rwlock rtp_lk;
unsigned int rtp_count;
};
struct pool rtpcb_pool;
struct rtptable rtptable;
/*
* These flags and timeout are used for indicating to userland (via a
* RTM_DESYNC msg) when the route socket has overflowed and messages
* have been lost.
*/
#define ROUTECB_FLAG_DESYNC 0x1 /* Route socket out of memory */
#define ROUTECB_FLAG_FLUSH 0x2 /* Wait until socket is empty before
queueing more packets */
#define ROUTE_DESYNC_RESEND_TIMEOUT 200 /* In ms */
void
route_prinit(void)
{
srpl_rc_init(&rtptable.rtp_rc, rcb_ref, rcb_unref, NULL);
rw_init(&rtptable.rtp_lk, "rtsock");
SRPL_INIT(&rtptable.rtp_list);
pool_init(&rtpcb_pool, sizeof(struct rtpcb), 0,
IPL_SOFTNET, PR_WAITOK, "rtpcb", NULL);
}
void
rcb_ref(void *null, void *v)
{
struct rtpcb *rop = v;
refcnt_take(&rop->rop_refcnt);
}
void
rcb_unref(void *null, void *v)
{
struct rtpcb *rop = v;
refcnt_rele_wake(&rop->rop_refcnt);
}
int
route_attach(struct socket *so, int proto)
{
struct rtpcb *rop;
int error;
error = soreserve(so, ROUTESNDQ, ROUTERCVQ);
if (error)
return (error);
/*
* use the rawcb but allocate a rtpcb, this
* code does not care about the additional fields
* and works directly on the raw socket.
*/
rop = pool_get(&rtpcb_pool, PR_WAITOK|PR_ZERO);
so->so_pcb = rop;
/* Init the timeout structure */
timeout_set_proc(&rop->rop_timeout, rtm_senddesync_timer, so);
refcnt_init(&rop->rop_refcnt);
rop->rop_socket = so;
rop->rop_proto = proto;
rop->rop_rtableid = curproc->p_p->ps_rtableid;
soisconnected(so);
so->so_options |= SO_USELOOPBACK;
rw_enter(&rtptable.rtp_lk, RW_WRITE);
SRPL_INSERT_HEAD_LOCKED(&rtptable.rtp_rc, &rtptable.rtp_list, rop,
rop_list);
rtptable.rtp_count++;
rw_exit(&rtptable.rtp_lk);
return (0);
}
int
route_detach(struct socket *so)
{
struct rtpcb *rop;
soassertlocked(so);
rop = sotortpcb(so);
if (rop == NULL)
return (EINVAL);
rw_enter(&rtptable.rtp_lk, RW_WRITE);
rtptable.rtp_count--;
SRPL_REMOVE_LOCKED(&rtptable.rtp_rc, &rtptable.rtp_list, rop, rtpcb,
rop_list);
rw_exit(&rtptable.rtp_lk);
sounlock(so);
/* wait for all references to drop */
refcnt_finalize(&rop->rop_refcnt, "rtsockrefs");
timeout_del_barrier(&rop->rop_timeout);
solock(so);
so->so_pcb = NULL;
KASSERT((so->so_state & SS_NOFDREF) == 0);
pool_put(&rtpcb_pool, rop);
return (0);
}
int
route_disconnect(struct socket *so)
{
soisdisconnected(so);
return (0);
}
int
route_shutdown(struct socket *so)
{
socantsendmore(so);
return (0);
}
int
route_rcvd(struct socket *so)
{
struct rtpcb *rop = sotortpcb(so);
soassertlocked(so);
/*
* If we are in a FLUSH state, check if the buffer is
* empty so that we can clear the flag.
*/
if (((rop->rop_flags & ROUTECB_FLAG_FLUSH) != 0) &&
((sbspace(rop->rop_socket, &rop->rop_socket->so_rcv) ==
rop->rop_socket->so_rcv.sb_hiwat)))
rop->rop_flags &= ~ROUTECB_FLAG_FLUSH;
return (0);
}
int
route_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
int error;
soassertlocked(so);
if (control && control->m_len) {
error = EOPNOTSUPP;
goto out;
}
if (nam) {
error = EISCONN;
goto out;
}
error = route_output(m, so);
m = NULL;
out:
m_freem(control);
m_freem(m);
return (error);
}
int
route_abort(struct socket *so)
{
soisdisconnected(so);
return (0);
}
int
route_sockaddr(struct socket *so, struct mbuf *nam)
{
return (EINVAL);
}
int
route_peeraddr(struct socket *so, struct mbuf *nam)
{
/* minimal support, just implement a fake peer address */
bcopy(&route_src, mtod(nam, caddr_t), route_src.sa_len);
nam->m_len = route_src.sa_len;
return (0);
}
int
route_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
struct rtpcb *rop = sotortpcb(so);
int error = 0;
unsigned int tid, prio;
if (level != AF_ROUTE)
return (EINVAL);
switch (op) {
case PRCO_SETOPT:
switch (optname) {
case ROUTE_MSGFILTER:
if (m == NULL || m->m_len != sizeof(unsigned int))
error = EINVAL;
else
rop->rop_msgfilter = *mtod(m, unsigned int *);
break;
case ROUTE_TABLEFILTER:
if (m == NULL || m->m_len != sizeof(unsigned int)) {
error = EINVAL;
break;
}
tid = *mtod(m, unsigned int *);
if (tid != RTABLE_ANY && !rtable_exists(tid))
error = ENOENT;
else
rop->rop_rtableid = tid;
break;
case ROUTE_PRIOFILTER:
if (m == NULL || m->m_len != sizeof(unsigned int)) {
error = EINVAL;
break;
}
prio = *mtod(m, unsigned int *);
if (prio > RTP_MAX)
error = EINVAL;
else
rop->rop_priority = prio;
break;
case ROUTE_FLAGFILTER:
if (m == NULL || m->m_len != sizeof(unsigned int))
error = EINVAL;
else
rop->rop_flagfilter = *mtod(m, unsigned int *);
break;
default:
error = ENOPROTOOPT;
break;
}
break;
case PRCO_GETOPT:
switch (optname) {
case ROUTE_MSGFILTER:
m->m_len = sizeof(unsigned int);
*mtod(m, unsigned int *) = rop->rop_msgfilter;
break;
case ROUTE_TABLEFILTER:
m->m_len = sizeof(unsigned int);
*mtod(m, unsigned int *) = rop->rop_rtableid;
break;
case ROUTE_PRIOFILTER:
m->m_len = sizeof(unsigned int);
*mtod(m, unsigned int *) = rop->rop_priority;
break;
case ROUTE_FLAGFILTER:
m->m_len = sizeof(unsigned int);
*mtod(m, unsigned int *) = rop->rop_flagfilter;
break;
default:
error = ENOPROTOOPT;
break;
}
}
return (error);
}
void
rtm_senddesync_timer(void *xso)
{
struct socket *so = xso;
solock(so);
rtm_senddesync(so);
sounlock(so);
}
void
rtm_senddesync(struct socket *so)
{
struct rtpcb *rop = sotortpcb(so);
struct mbuf *desync_mbuf;
soassertlocked(so);
/*
* Dying socket is disconnected by upper layer and there is
* no reason to send packet. Also we shouldn't reschedule
* timeout(9), otherwise timeout_del_barrier(9) can't help us.
*/
if ((so->so_state & SS_ISCONNECTED) == 0 ||
(so->so_state & SS_CANTRCVMORE))
return;
/* If we are in a DESYNC state, try to send a RTM_DESYNC packet */
if ((rop->rop_flags & ROUTECB_FLAG_DESYNC) == 0)
return;
/*
* If we fail to alloc memory or if sbappendaddr()
* fails, re-add timeout and try again.
*/
desync_mbuf = rtm_msg1(RTM_DESYNC, NULL);
if (desync_mbuf != NULL) {
if (sbappendaddr(so, &so->so_rcv, &route_src,
desync_mbuf, NULL) != 0) {
rop->rop_flags &= ~ROUTECB_FLAG_DESYNC;
sorwakeup(rop->rop_socket);
return;
}
m_freem(desync_mbuf);
}
/* Re-add timeout to try sending msg again */
timeout_add_msec(&rop->rop_timeout, ROUTE_DESYNC_RESEND_TIMEOUT);
}
void
route_input(struct mbuf *m0, struct socket *so0, sa_family_t sa_family)
{
struct socket *so;
struct rtpcb *rop;
struct rt_msghdr *rtm;
struct mbuf *m = m0;
struct srp_ref sr;
/* ensure that we can access the rtm_type via mtod() */
if (m->m_len < offsetof(struct rt_msghdr, rtm_type) + 1) {
m_freem(m);
return;
}
SRPL_FOREACH(rop, &sr, &rtptable.rtp_list, rop_list) {
/*
* If route socket is bound to an address family only send
* messages that match the address family. Address family
* agnostic messages are always sent.
*/
if (sa_family != AF_UNSPEC && rop->rop_proto != AF_UNSPEC &&
rop->rop_proto != sa_family)
continue;
so = rop->rop_socket;
solock(so);
/*
* Check to see if we don't want our own messages and
* if we can receive anything.
*/
if ((so0 == so && !(so0->so_options & SO_USELOOPBACK)) ||
!(so->so_state & SS_ISCONNECTED) ||
(so->so_state & SS_CANTRCVMORE))
goto next;
/* filter messages that the process does not want */
rtm = mtod(m, struct rt_msghdr *);
/* but RTM_DESYNC can't be filtered */
if (rtm->rtm_type != RTM_DESYNC) {
if (rop->rop_msgfilter != 0 &&
!(rop->rop_msgfilter & (1U << rtm->rtm_type)))
goto next;
if (ISSET(rop->rop_flagfilter, rtm->rtm_flags))
goto next;
}
switch (rtm->rtm_type) {
case RTM_IFANNOUNCE:
case RTM_DESYNC:
/* no tableid */
break;
case RTM_RESOLVE:
case RTM_NEWADDR:
case RTM_DELADDR:
case RTM_IFINFO:
case RTM_80211INFO:
case RTM_BFD:
/* check against rdomain id */
if (rop->rop_rtableid != RTABLE_ANY &&
rtable_l2(rop->rop_rtableid) != rtm->rtm_tableid)
goto next;
break;
default:
if (rop->rop_priority != 0 &&
rop->rop_priority < rtm->rtm_priority)
goto next;
/* check against rtable id */
if (rop->rop_rtableid != RTABLE_ANY &&
rop->rop_rtableid != rtm->rtm_tableid)
goto next;
break;
}
/*
* Check to see if the flush flag is set. If so, don't queue
* any more messages until the flag is cleared.
*/
if ((rop->rop_flags & ROUTECB_FLAG_FLUSH) != 0)
goto next;
rtm_sendup(so, m);
next:
sounlock(so);
}
SRPL_LEAVE(&sr);
m_freem(m);
}
int
rtm_sendup(struct socket *so, struct mbuf *m0)
{
struct rtpcb *rop = sotortpcb(so);
struct mbuf *m;
soassertlocked(so);
m = m_copym(m0, 0, M_COPYALL, M_NOWAIT);
if (m == NULL)
return (ENOMEM);
if (sbspace(so, &so->so_rcv) < (2 * MSIZE) ||
sbappendaddr(so, &so->so_rcv, &route_src, m, NULL) == 0) {
/* Flag socket as desync'ed and flush required */
rop->rop_flags |= ROUTECB_FLAG_DESYNC | ROUTECB_FLAG_FLUSH;
rtm_senddesync(so);
m_freem(m);
return (ENOBUFS);
}
sorwakeup(so);
return (0);
}
struct rt_msghdr *
rtm_report(struct rtentry *rt, u_char type, int seq, int tableid)
{
struct rt_msghdr *rtm;
struct rt_addrinfo info;
struct sockaddr_rtlabel sa_rl;
struct sockaddr_in6 sa_mask;
#ifdef BFD
struct sockaddr_bfd sa_bfd;
#endif
struct ifnet *ifp = NULL;
int len;
bzero(&info, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(rt);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
info.rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl);
#ifdef BFD
if (rt->rt_flags & RTF_BFD) {
KERNEL_LOCK();
info.rti_info[RTAX_BFD] = bfd2sa(rt, &sa_bfd);
KERNEL_UNLOCK();
}
#endif
#ifdef MPLS
if (rt->rt_flags & RTF_MPLS) {
struct sockaddr_mpls sa_mpls;
bzero(&sa_mpls, sizeof(sa_mpls));
sa_mpls.smpls_family = AF_MPLS;
sa_mpls.smpls_len = sizeof(sa_mpls);
sa_mpls.smpls_label = ((struct rt_mpls *)
rt->rt_llinfo)->mpls_label;
info.rti_info[RTAX_SRC] = (struct sockaddr *)&sa_mpls;
info.rti_mpls = ((struct rt_mpls *)
rt->rt_llinfo)->mpls_operation;
}
#endif
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL) {
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
info.rti_info[RTAX_IFA] = rtable_getsource(tableid,
info.rti_info[RTAX_DST]->sa_family);
if (info.rti_info[RTAX_IFA] == NULL)
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
if (ifp->if_flags & IFF_POINTOPOINT)
info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr;
}
if_put(ifp);
/* RTAX_GENMASK, RTAX_AUTHOR, RTAX_SRCMASK ignored */
/* build new route message */
len = rtm_msg2(type, RTM_VERSION, &info, NULL, NULL);
rtm = malloc(len, M_RTABLE, M_WAITOK | M_ZERO);
rtm_msg2(type, RTM_VERSION, &info, (caddr_t)rtm, NULL);
rtm->rtm_type = type;
rtm->rtm_index = rt->rt_ifidx;
rtm->rtm_tableid = tableid;
rtm->rtm_priority = rt->rt_priority & RTP_MASK;
rtm->rtm_flags = rt->rt_flags;
rtm->rtm_pid = curproc->p_p->ps_pid;
rtm->rtm_seq = seq;
rtm_getmetrics(rt, &rtm->rtm_rmx);
rtm->rtm_addrs = info.rti_addrs;
#ifdef MPLS
rtm->rtm_mpls = info.rti_mpls;
#endif
return rtm;
}
int
route_output(struct mbuf *m, struct socket *so)
{
struct rt_msghdr *rtm = NULL;
struct rtentry *rt = NULL;
struct rt_addrinfo info;
struct ifnet *ifp;
int len, seq, useloopback, error = 0;
u_int tableid;
u_int8_t prio;
u_char vers, type;
if (m == NULL || ((m->m_len < sizeof(int32_t)) &&
(m = m_pullup(m, sizeof(int32_t))) == 0))
return (ENOBUFS);
if ((m->m_flags & M_PKTHDR) == 0)
panic("route_output");
useloopback = so->so_options & SO_USELOOPBACK;
/*
* The socket can't be closed concurrently because the file
* descriptor reference is still held.
*/
sounlock(so);
len = m->m_pkthdr.len;
if (len < offsetof(struct rt_msghdr, rtm_hdrlen) + 1 ||
len != mtod(m, struct rt_msghdr *)->rtm_msglen) {
error = EINVAL;
goto fail;
}
vers = mtod(m, struct rt_msghdr *)->rtm_version;
switch (vers) {
case RTM_VERSION:
if (len < sizeof(struct rt_msghdr)) {
error = EINVAL;
goto fail;
}
if (len > RTM_MAXSIZE) {
error = EMSGSIZE;
goto fail;
}
rtm = malloc(len, M_RTABLE, M_WAITOK);
m_copydata(m, 0, len, rtm);
break;
default:
error = EPROTONOSUPPORT;
goto fail;
}
/* Verify that the caller is sending an appropriate message early */
switch (rtm->rtm_type) {
case RTM_ADD:
case RTM_DELETE:
case RTM_GET:
case RTM_CHANGE:
case RTM_PROPOSAL:
case RTM_SOURCE:
break;
default:
error = EOPNOTSUPP;
goto fail;
}
/*
* Verify that the header length is valid.
* All messages from userland start with a struct rt_msghdr.
*/
if (rtm->rtm_hdrlen == 0) /* old client */
rtm->rtm_hdrlen = sizeof(struct rt_msghdr);
if (rtm->rtm_hdrlen < sizeof(struct rt_msghdr) ||
len < rtm->rtm_hdrlen) {
error = EINVAL;
goto fail;
}
rtm->rtm_pid = curproc->p_p->ps_pid;
/*
* Verify that the caller has the appropriate privilege; RTM_GET
* is the only operation the non-superuser is allowed.
*/
if (rtm->rtm_type != RTM_GET && suser(curproc) != 0) {
error = EACCES;
goto fail;
}
tableid = rtm->rtm_tableid;
if (!rtable_exists(tableid)) {
if (rtm->rtm_type == RTM_ADD) {
if ((error = rtable_add(tableid)) != 0)
goto fail;
} else {
error = EINVAL;
goto fail;
}
}
/* Do not let userland play with kernel-only flags. */
if ((rtm->rtm_flags & (RTF_LOCAL|RTF_BROADCAST)) != 0) {
error = EINVAL;
goto fail;
}
/* make sure that kernel-only bits are not set */
rtm->rtm_priority &= RTP_MASK;
rtm->rtm_flags &= ~(RTF_DONE|RTF_CLONED|RTF_CACHED);
rtm->rtm_fmask &= RTF_FMASK;
if (rtm->rtm_priority != 0) {
if (rtm->rtm_priority > RTP_MAX ||
rtm->rtm_priority == RTP_LOCAL) {
error = EINVAL;
goto fail;
}
prio = rtm->rtm_priority;
} else if (rtm->rtm_type != RTM_ADD)
prio = RTP_ANY;
else if (rtm->rtm_flags & RTF_STATIC)
prio = 0;
else
prio = RTP_DEFAULT;
bzero(&info, sizeof(info));
info.rti_addrs = rtm->rtm_addrs;
if ((error = rtm_xaddrs(rtm->rtm_hdrlen + (caddr_t)rtm,
len + (caddr_t)rtm, &info)) != 0)
goto fail;
info.rti_flags = rtm->rtm_flags;
if (rtm->rtm_type != RTM_SOURCE &&
rtm->rtm_type != RTM_PROPOSAL &&
(info.rti_info[RTAX_DST] == NULL ||
info.rti_info[RTAX_DST]->sa_family >= AF_MAX ||
(info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX) ||
info.rti_info[RTAX_GENMASK] != NULL)) {
error = EINVAL;
goto fail;
}
#ifdef MPLS
info.rti_mpls = rtm->rtm_mpls;
#endif
if (info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK &&
(info.rti_flags & RTF_CLONING) == 0) {
info.rti_flags |= RTF_LLINFO;
}
/*
* Validate RTM_PROPOSAL and pass it along or error out.
*/
if (rtm->rtm_type == RTM_PROPOSAL) {
if (rtm_validate_proposal(&info) == -1) {
error = EINVAL;
goto fail;
}
/*
* If this is a solicitation proposal forward request to
* all interfaces. Most handlers will ignore it but at least
* umb(4) will send a response to this event.
*/
if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
NET_LOCK();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
ifp->if_rtrequest(ifp, RTM_PROPOSAL, NULL);
}
NET_UNLOCK();
}
} else if (rtm->rtm_type == RTM_SOURCE) {
if (info.rti_info[RTAX_IFA] == NULL) {
error = EINVAL;
goto fail;
}
if ((error =
rt_setsource(tableid, info.rti_info[RTAX_IFA])) != 0)
goto fail;
} else {
error = rtm_output(rtm, &rt, &info, prio, tableid);
if (!error) {
type = rtm->rtm_type;
seq = rtm->rtm_seq;
free(rtm, M_RTABLE, len);
rtm = rtm_report(rt, type, seq, tableid);
len = rtm->rtm_msglen;
}
}
rtfree(rt);
if (error) {
rtm->rtm_errno = error;
} else {
rtm->rtm_flags |= RTF_DONE;
}
/*
* Check to see if we don't want our own messages.
*/
if (!useloopback) {
if (rtptable.rtp_count == 0) {
/* no other listener and no loopback of messages */
goto fail;
}
}
if (m_copyback(m, 0, len, rtm, M_NOWAIT)) {
m_freem(m);
m = NULL;
} else if (m->m_pkthdr.len > len)
m_adj(m, len - m->m_pkthdr.len);
free(rtm, M_RTABLE, len);
if (m)
route_input(m, so, info.rti_info[RTAX_DST] ?
info.rti_info[RTAX_DST]->sa_family : AF_UNSPEC);
solock(so);
return (error);
fail:
free(rtm, M_RTABLE, len);
m_freem(m);
solock(so);
return (error);
}
int
rtm_output(struct rt_msghdr *rtm, struct rtentry **prt,
struct rt_addrinfo *info, uint8_t prio, unsigned int tableid)
{
struct rtentry *rt = *prt;
struct ifnet *ifp = NULL;
int plen, newgate = 0, error = 0;
switch (rtm->rtm_type) {
case RTM_ADD:
if (info->rti_info[RTAX_GATEWAY] == NULL) {
error = EINVAL;
break;
}
rt = rtable_match(tableid, info->rti_info[RTAX_DST], NULL);
if ((error = route_arp_conflict(rt, info))) {
rtfree(rt);
rt = NULL;
break;
}
/*
* We cannot go through a delete/create/insert cycle for
* cached route because this can lead to races in the
* receive path. Instead we update the L2 cache.
*/
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_CACHED)) {
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
rtfree(rt);
rt = NULL;
error = ESRCH;
break;
}
goto change;
}
rtfree(rt);
rt = NULL;
NET_LOCK();
if ((error = rtm_getifa(info, tableid)) != 0) {
NET_UNLOCK();
break;
}
error = rtrequest(RTM_ADD, info, prio, &rt, tableid);
NET_UNLOCK();
if (error == 0)
rtm_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx,
&rt->rt_rmx);
break;
case RTM_DELETE:
rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
prio);
if (rt == NULL) {
error = ESRCH;
break;
}
/*
* If we got multipath routes, we require users to specify
* a matching gateway.
*/
if (ISSET(rt->rt_flags, RTF_MPATH) &&
info->rti_info[RTAX_GATEWAY] == NULL) {
error = ESRCH;
break;
}
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
rtfree(rt);
rt = NULL;
error = ESRCH;
break;
}
/*
* Invalidate the cache of automagically created and
* referenced L2 entries to make sure that ``rt_gwroute''
* pointer stays valid for other CPUs.
*/
if ((ISSET(rt->rt_flags, RTF_CACHED))) {
NET_LOCK();
ifp->if_rtrequest(ifp, RTM_INVALIDATE, rt);
/* Reset the MTU of the gateway route. */
rtable_walk(tableid, rt_key(rt)->sa_family, NULL,
route_cleargateway, rt);
NET_UNLOCK();
break;
}
/*
* Make sure that local routes are only modified by the
* kernel.
*/
if (ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST)) {
error = EINVAL;
break;
}
rtfree(rt);
rt = NULL;
NET_LOCK();
error = rtrequest_delete(info, prio, ifp, &rt, tableid);
NET_UNLOCK();
break;
case RTM_CHANGE:
rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
prio);
/*
* If we got multipath routes, we require users to specify
* a matching gateway.
*/
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_MPATH) &&
(info->rti_info[RTAX_GATEWAY] == NULL)) {
rtfree(rt);
rt = NULL;
}
/*
* If RTAX_GATEWAY is the argument we're trying to
* change, try to find a compatible route.
*/
if ((rt == NULL) && (info->rti_info[RTAX_GATEWAY] != NULL)) {
rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], NULL, prio);
/* Ensure we don't pick a multipath one. */
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_MPATH)) {
rtfree(rt);
rt = NULL;
}
}
if (rt == NULL) {
error = ESRCH;
break;
}
/*
* Make sure that local routes are only modified by the
* kernel.
*/
if (ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST)) {
error = EINVAL;
break;
}
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
rtfree(rt);
rt = NULL;
error = ESRCH;
break;
}
/*
* RTM_CHANGE needs a perfect match.
*/
plen = rtable_satoplen(info->rti_info[RTAX_DST]->sa_family,
info->rti_info[RTAX_NETMASK]);
if (rt_plen(rt) != plen) {
error = ESRCH;
break;
}
if (info->rti_info[RTAX_GATEWAY] != NULL)
if (rt->rt_gateway == NULL ||
bcmp(rt->rt_gateway,
info->rti_info[RTAX_GATEWAY],
info->rti_info[RTAX_GATEWAY]->sa_len)) {
newgate = 1;
}
/*
* Check reachable gateway before changing the route.
* New gateway could require new ifaddr, ifp;
* flags may also be different; ifp may be specified
* by ll sockaddr when protocol address is ambiguous.
*/
if (newgate || info->rti_info[RTAX_IFP] != NULL ||
info->rti_info[RTAX_IFA] != NULL) {
struct ifaddr *ifa = NULL;
NET_LOCK();
if ((error = rtm_getifa(info, tableid)) != 0) {
NET_UNLOCK();
break;
}
ifa = info->rti_ifa;
if (rt->rt_ifa != ifa) {
ifp->if_rtrequest(ifp, RTM_DELETE, rt);
ifafree(rt->rt_ifa);
rt->rt_ifa = ifaref(ifa);
rt->rt_ifidx = ifa->ifa_ifp->if_index;
/* recheck link state after ifp change */
rt_if_linkstate_change(rt, ifa->ifa_ifp,
tableid);
}
NET_UNLOCK();
}
change:
if (info->rti_info[RTAX_GATEWAY] != NULL) {
/* When updating the gateway, make sure it is valid. */
if (!newgate && rt->rt_gateway->sa_family !=
info->rti_info[RTAX_GATEWAY]->sa_family) {
error = EINVAL;
break;
}
NET_LOCK();
error = rt_setgate(rt,
info->rti_info[RTAX_GATEWAY], tableid);
NET_UNLOCK();
if (error)
break;
}
#ifdef MPLS
if (rtm->rtm_flags & RTF_MPLS) {
NET_LOCK();
error = rt_mpls_set(rt,
info->rti_info[RTAX_SRC], info->rti_mpls);
NET_UNLOCK();
if (error)
break;
} else if (newgate || (rtm->rtm_fmask & RTF_MPLS)) {
NET_LOCK();
/* if gateway changed remove MPLS information */
rt_mpls_clear(rt);
NET_UNLOCK();
}
#endif
#ifdef BFD
if (ISSET(rtm->rtm_flags, RTF_BFD)) {
KERNEL_LOCK();
error = bfdset(rt);
KERNEL_UNLOCK();
if (error)
break;
} else if (!ISSET(rtm->rtm_flags, RTF_BFD) &&
ISSET(rtm->rtm_fmask, RTF_BFD)) {
KERNEL_LOCK();
bfdclear(rt);
KERNEL_UNLOCK();
}
#endif
NET_LOCK();
/* Hack to allow some flags to be toggled */
if (rtm->rtm_fmask) {
/* MPLS flag it is set by rt_mpls_set() */
rtm->rtm_fmask &= ~RTF_MPLS;
rtm->rtm_flags &= ~RTF_MPLS;
rt->rt_flags =
(rt->rt_flags & ~rtm->rtm_fmask) |
(rtm->rtm_flags & rtm->rtm_fmask);
}
rtm_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, &rt->rt_rmx);
ifp->if_rtrequest(ifp, RTM_ADD, rt);
if (info->rti_info[RTAX_LABEL] != NULL) {
char *rtlabel = ((struct sockaddr_rtlabel *)
info->rti_info[RTAX_LABEL])->sr_label;
rtlabel_unref(rt->rt_labelid);
rt->rt_labelid = rtlabel_name2id(rtlabel);
}
if_group_routechange(info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK]);
rt->rt_locks &= ~(rtm->rtm_inits);
rt->rt_locks |= (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks);
NET_UNLOCK();
break;
case RTM_GET:
rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
prio);
if (rt == NULL)
error = ESRCH;
break;
}
if_put(ifp);
*prt = rt;
return (error);
}
struct ifaddr *
ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway,
unsigned int rtableid)
{
struct ifaddr *ifa;
if ((flags & RTF_GATEWAY) == 0) {
/*
* If we are adding a route to an interface,
* and the interface is a pt to pt link
* we should search for the destination
* as our clue to the interface. Otherwise
* we can use the local address.
*/
ifa = NULL;
if (flags & RTF_HOST)
ifa = ifa_ifwithdstaddr(dst, rtableid);
if (ifa == NULL)
ifa = ifa_ifwithaddr(gateway, rtableid);
} else {
/*
* If we are adding a route to a remote net
* or host, the gateway may still be on the
* other end of a pt to pt link.
*/
ifa = ifa_ifwithdstaddr(gateway, rtableid);
}
if (ifa == NULL) {
if (gateway->sa_family == AF_LINK) {
struct sockaddr_dl *sdl = satosdl(gateway);
struct ifnet *ifp = if_get(sdl->sdl_index);
if (ifp != NULL)
ifa = ifaof_ifpforaddr(dst, ifp);
if_put(ifp);
} else {
struct rtentry *rt;
rt = rtalloc(gateway, RT_RESOLVE, rtable_l2(rtableid));
if (rt != NULL)
ifa = rt->rt_ifa;
rtfree(rt);
}
}
if (ifa == NULL)
return (NULL);
if (ifa->ifa_addr->sa_family != dst->sa_family) {
struct ifaddr *oifa = ifa;
ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
if (ifa == NULL)
ifa = oifa;
}
return (ifa);
}
int
rtm_getifa(struct rt_addrinfo *info, unsigned int rtid)
{
struct ifnet *ifp = NULL;
/*
* The "returned" `ifa' is guaranteed to be alive only if
* the NET_LOCK() is held.
*/
NET_ASSERT_LOCKED();
/*
* ifp may be specified by sockaddr_dl when protocol address
* is ambiguous
*/
if (info->rti_info[RTAX_IFP] != NULL) {
struct sockaddr_dl *sdl;
sdl = satosdl(info->rti_info[RTAX_IFP]);
ifp = if_get(sdl->sdl_index);
}
#ifdef IPSEC
/*
* If the destination is a PF_KEY address, we'll look
* for the existence of a encap interface number or address
* in the options list of the gateway. By default, we'll return
* enc0.
*/
if (info->rti_info[RTAX_DST] &&
info->rti_info[RTAX_DST]->sa_family == PF_KEY)
info->rti_ifa = enc_getifa(rtid, 0);
#endif
if (info->rti_ifa == NULL && info->rti_info[RTAX_IFA] != NULL)
info->rti_ifa = ifa_ifwithaddr(info->rti_info[RTAX_IFA], rtid);
if (info->rti_ifa == NULL) {
struct sockaddr *sa;
if ((sa = info->rti_info[RTAX_IFA]) == NULL)
if ((sa = info->rti_info[RTAX_GATEWAY]) == NULL)
sa = info->rti_info[RTAX_DST];
if (sa != NULL && ifp != NULL)
info->rti_ifa = ifaof_ifpforaddr(sa, ifp);
else if (info->rti_info[RTAX_DST] != NULL &&
info->rti_info[RTAX_GATEWAY] != NULL)
info->rti_ifa = ifa_ifwithroute(info->rti_flags,
info->rti_info[RTAX_DST],
info->rti_info[RTAX_GATEWAY],
rtid);
else if (sa != NULL)
info->rti_ifa = ifa_ifwithroute(info->rti_flags,
sa, sa, rtid);
}
if_put(ifp);
if (info->rti_ifa == NULL)
return (ENETUNREACH);
return (0);
}
int
route_cleargateway(struct rtentry *rt, void *arg, unsigned int rtableid)
{
struct rtentry *nhrt = arg;
if (ISSET(rt->rt_flags, RTF_GATEWAY) && rt->rt_gwroute == nhrt &&
!ISSET(rt->rt_locks, RTV_MTU))
rt->rt_mtu = 0;
return (0);
}
/*
* Check if the user request to insert an ARP entry does not conflict
* with existing ones.
*
* Only two entries are allowed for a given IP address: a private one
* (priv) and a public one (pub).
*/
int
route_arp_conflict(struct rtentry *rt, struct rt_addrinfo *info)
{
int proxy = (info->rti_flags & RTF_ANNOUNCE);
if ((info->rti_flags & RTF_LLINFO) == 0 ||
(info->rti_info[RTAX_DST]->sa_family != AF_INET))
return (0);
if (rt == NULL || !ISSET(rt->rt_flags, RTF_LLINFO))
return (0);
/* If the entry is cached, it can be updated. */
if (ISSET(rt->rt_flags, RTF_CACHED))
return (0);
/*
* Same destination, not cached and both "priv" or "pub" conflict.
* If a second entry exists, it always conflict.
*/
if ((ISSET(rt->rt_flags, RTF_ANNOUNCE) == proxy) ||
ISSET(rt->rt_flags, RTF_MPATH))
return (EEXIST);
/* No conflict but an entry exist so we need to force mpath. */
info->rti_flags |= RTF_MPATH;
return (0);
}
void
rtm_setmetrics(u_long which, const struct rt_metrics *in,
struct rt_kmetrics *out)
{
int64_t expire;
if (which & RTV_MTU)
out->rmx_mtu = in->rmx_mtu;
if (which & RTV_EXPIRE) {
expire = in->rmx_expire;
if (expire != 0) {
expire -= gettime();
expire += getuptime();
}
out->rmx_expire = expire;
}
}
void
rtm_getmetrics(const struct rtentry *rt, struct rt_metrics *out)
{
const struct rt_kmetrics *in = &rt->rt_rmx;
int64_t expire;
expire = in->rmx_expire;
if (expire == 0)
expire = rt_timer_get_expire(rt);
if (expire != 0) {
expire -= getuptime();
expire += gettime();
}
bzero(out, sizeof(*out));
out->rmx_locks = in->rmx_locks;
out->rmx_mtu = in->rmx_mtu;
out->rmx_expire = expire;
out->rmx_pksent = in->rmx_pksent;
}
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
int
rtm_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
{
struct sockaddr *sa;
int i;
/*
* Parse address bits, split address storage in chunks, and
* set info pointers. Use sa_len for traversing the memory
* and check that we stay within in the limit.
*/
bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info));
for (i = 0; i < sizeof(rtinfo->rti_addrs) * 8; i++) {
if ((rtinfo->rti_addrs & (1U << i)) == 0)
continue;
if (i >= RTAX_MAX || cp + sizeof(socklen_t) > cplim)
return (EINVAL);
sa = (struct sockaddr *)cp;
if (cp + sa->sa_len > cplim)
return (EINVAL);
rtinfo->rti_info[i] = sa;
ADVANCE(cp, sa);
}
/*
* Check that the address family is suitable for the route address
* type. Check that each address has a size that fits its family
* and its length is within the size. Strings within addresses must
* be NUL terminated.
*/
for (i = 0; i < RTAX_MAX; i++) {
size_t len, maxlen, size;
sa = rtinfo->rti_info[i];
if (sa == NULL)
continue;
maxlen = size = 0;
switch (i) {
case RTAX_DST:
case RTAX_GATEWAY:
case RTAX_SRC:
switch (sa->sa_family) {
case AF_INET:
size = sizeof(struct sockaddr_in);
break;
case AF_LINK:
size = sizeof(struct sockaddr_dl);
break;
#ifdef INET6
case AF_INET6:
size = sizeof(struct sockaddr_in6);
break;
#endif
#ifdef MPLS
case AF_MPLS:
size = sizeof(struct sockaddr_mpls);
break;
#endif
}
break;
case RTAX_IFP:
if (sa->sa_family != AF_LINK)
return (EAFNOSUPPORT);
/*
* XXX Should be sizeof(struct sockaddr_dl), but
* route(8) has a bug and provides less memory.
* arp(8) has another bug and uses sizeof pointer.
*/
size = 4;
break;
case RTAX_IFA:
switch (sa->sa_family) {
case AF_INET:
size = sizeof(struct sockaddr_in);
break;
#ifdef INET6
case AF_INET6:
size = sizeof(struct sockaddr_in6);
break;
#endif
default:
return (EAFNOSUPPORT);
}
break;
case RTAX_LABEL:
sa->sa_family = AF_UNSPEC;
maxlen = RTLABEL_LEN;
size = sizeof(struct sockaddr_rtlabel);
break;
#ifdef BFD
case RTAX_BFD:
sa->sa_family = AF_UNSPEC;
size = sizeof(struct sockaddr_bfd);
break;
#endif
case RTAX_DNS:
/* more validation in rtm_validate_proposal */
if (sa->sa_len > sizeof(struct sockaddr_rtdns))
return (EINVAL);
if (sa->sa_len < offsetof(struct sockaddr_rtdns,
sr_dns))
return (EINVAL);
switch (sa->sa_family) {
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
break;
default:
return (EAFNOSUPPORT);
}
break;
case RTAX_STATIC:
sa->sa_family = AF_UNSPEC;
maxlen = RTSTATIC_LEN;
size = sizeof(struct sockaddr_rtstatic);
break;
case RTAX_SEARCH:
sa->sa_family = AF_UNSPEC;
maxlen = RTSEARCH_LEN;
size = sizeof(struct sockaddr_rtsearch);
break;
}
if (size) {
/* memory for the full struct must be provided */
if (sa->sa_len < size)
return (EINVAL);
}
if (maxlen) {
/* this should not happen */
if (2 + maxlen > size)
return (EINVAL);
/* strings must be NUL terminated within the struct */
len = strnlen(sa->sa_data, maxlen);
if (len >= maxlen || 2 + len >= sa->sa_len)
return (EINVAL);
break;
}
}
return (0);
}
struct mbuf *
rtm_msg1(int type, struct rt_addrinfo *rtinfo)
{
struct rt_msghdr *rtm;
struct mbuf *m;
int i;
struct sockaddr *sa;
int len, dlen, hlen;
switch (type) {
case RTM_DELADDR:
case RTM_NEWADDR:
hlen = sizeof(struct ifa_msghdr);
break;
case RTM_IFINFO:
hlen = sizeof(struct if_msghdr);
break;
case RTM_IFANNOUNCE:
hlen = sizeof(struct if_announcemsghdr);
break;
#ifdef BFD
case RTM_BFD:
hlen = sizeof(struct bfd_msghdr);
break;
#endif
case RTM_80211INFO:
hlen = sizeof(struct if_ieee80211_msghdr);
break;
default:
hlen = sizeof(struct rt_msghdr);
break;
}
len = hlen;
for (i = 0; i < RTAX_MAX; i++) {
if (rtinfo == NULL || (sa = rtinfo->rti_info[i]) == NULL)
continue;
len += ROUNDUP(sa->sa_len);
}
if (len > MCLBYTES)
panic("rtm_msg1");
m = m_gethdr(M_DONTWAIT, MT_DATA);
if (m && len > MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
m = NULL;
}
}
if (m == NULL)
return (m);
m->m_pkthdr.len = m->m_len = len;
m->m_pkthdr.ph_ifidx = 0;
rtm = mtod(m, struct rt_msghdr *);
bzero(rtm, len);
len = hlen;
for (i = 0; i < RTAX_MAX; i++) {
if (rtinfo == NULL || (sa = rtinfo->rti_info[i]) == NULL)
continue;
rtinfo->rti_addrs |= (1U << i);
dlen = ROUNDUP(sa->sa_len);
if (m_copyback(m, len, sa->sa_len, sa, M_NOWAIT)) {
m_freem(m);
return (NULL);
}
len += dlen;
}
rtm->rtm_msglen = len;
rtm->rtm_hdrlen = hlen;
rtm->rtm_version = RTM_VERSION;
rtm->rtm_type = type;
return (m);
}
int
rtm_msg2(int type, int vers, struct rt_addrinfo *rtinfo, caddr_t cp,
struct walkarg *w)
{
int i;
int len, dlen, hlen, second_time = 0;
caddr_t cp0;
rtinfo->rti_addrs = 0;
again:
switch (type) {
case RTM_DELADDR:
case RTM_NEWADDR:
len = sizeof(struct ifa_msghdr);
break;
case RTM_IFINFO:
len = sizeof(struct if_msghdr);
break;
default:
len = sizeof(struct rt_msghdr);
break;
}
hlen = len;
if ((cp0 = cp) != NULL)
cp += len;
for (i = 0; i < RTAX_MAX; i++) {
struct sockaddr *sa;
if ((sa = rtinfo->rti_info[i]) == NULL)
continue;
rtinfo->rti_addrs |= (1U << i);
dlen = ROUNDUP(sa->sa_len);
if (cp) {
bcopy(sa, cp, sa->sa_len);
bzero(cp + sa->sa_len, dlen - sa->sa_len);
cp += dlen;
}
len += dlen;
}
/* align message length to the next natural boundary */
len = ALIGN(len);
if (cp == 0 && w != NULL && !second_time) {
w->w_needed += len;
if (w->w_needed <= w->w_given && w->w_where) {
if (w->w_tmemsize < len) {
free(w->w_tmem, M_RTABLE, w->w_tmemsize);
w->w_tmem = malloc(len, M_RTABLE,
M_NOWAIT | M_ZERO);
if (w->w_tmem)
w->w_tmemsize = len;
}
if (w->w_tmem) {
cp = w->w_tmem;
second_time = 1;
goto again;
} else
w->w_where = 0;
}
}
if (cp && w) /* clear the message header */
bzero(cp0, hlen);
if (cp) {
struct rt_msghdr *rtm = (struct rt_msghdr *)cp0;
rtm->rtm_version = RTM_VERSION;
rtm->rtm_type = type;
rtm->rtm_msglen = len;
rtm->rtm_hdrlen = hlen;
}
return (len);
}
void
rtm_send(struct rtentry *rt, int cmd, int error, unsigned int rtableid)
{
struct rt_addrinfo info;
struct ifnet *ifp;
struct sockaddr_rtlabel sa_rl;
struct sockaddr_in6 sa_mask;
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(rt);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
if (!ISSET(rt->rt_flags, RTF_HOST))
info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
info.rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl);
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL) {
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
info.rti_info[RTAX_IFA] = rtable_getsource(rtableid,
info.rti_info[RTAX_DST]->sa_family);
if (info.rti_info[RTAX_IFA] == NULL)
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
}
rtm_miss(cmd, &info, rt->rt_flags, rt->rt_priority, rt->rt_ifidx, error,
rtableid);
if_put(ifp);
}
/*
* This routine is called to generate a message from the routing
* socket indicating that a redirect has occurred, a routing lookup
* has failed, or that a protocol has detected timeouts to a particular
* destination.
*/
void
rtm_miss(int type, struct rt_addrinfo *rtinfo, int flags, uint8_t prio,
u_int ifidx, int error, u_int tableid)
{
struct rt_msghdr *rtm;
struct mbuf *m;
struct sockaddr *sa = rtinfo->rti_info[RTAX_DST];
if (rtptable.rtp_count == 0)
return;
m = rtm_msg1(type, rtinfo);
if (m == NULL)
return;
rtm = mtod(m, struct rt_msghdr *);
rtm->rtm_flags = RTF_DONE | flags;
rtm->rtm_priority = prio;
rtm->rtm_errno = error;
rtm->rtm_tableid = tableid;
rtm->rtm_addrs = rtinfo->rti_addrs;
rtm->rtm_index = ifidx;
route_input(m, NULL, sa ? sa->sa_family : AF_UNSPEC);
}
/*
* This routine is called to generate a message from the routing
* socket indicating that the status of a network interface has changed.
*/
void
rtm_ifchg(struct ifnet *ifp)
{
struct rt_addrinfo info;
struct if_msghdr *ifm;
struct mbuf *m;
if (rtptable.rtp_count == 0)
return;
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
m = rtm_msg1(RTM_IFINFO, &info);
if (m == NULL)
return;
ifm = mtod(m, struct if_msghdr *);
ifm->ifm_index = ifp->if_index;
ifm->ifm_tableid = ifp->if_rdomain;
ifm->ifm_flags = ifp->if_flags;
ifm->ifm_xflags = ifp->if_xflags;
if_getdata(ifp, &ifm->ifm_data);
ifm->ifm_addrs = info.rti_addrs;
route_input(m, NULL, AF_UNSPEC);
}
/*
* This is called to generate messages from the routing socket
* indicating a network interface has had addresses associated with it.
* if we ever reverse the logic and replace messages TO the routing
* socket indicate a request to configure interfaces, then it will
* be unnecessary as the routing socket will automatically generate
* copies of it.
*/
void
rtm_addr(int cmd, struct ifaddr *ifa)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct mbuf *m;
struct rt_addrinfo info;
struct ifa_msghdr *ifam;
if (rtptable.rtp_count == 0)
return;
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_IFA] = ifa->ifa_addr;
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
info.rti_info[RTAX_BRD] = ifa->ifa_dstaddr;
if ((m = rtm_msg1(cmd, &info)) == NULL)
return;
ifam = mtod(m, struct ifa_msghdr *);
ifam->ifam_index = ifp->if_index;
ifam->ifam_metric = ifa->ifa_metric;
ifam->ifam_flags = ifa->ifa_flags;
ifam->ifam_addrs = info.rti_addrs;
ifam->ifam_tableid = ifp->if_rdomain;
route_input(m, NULL,
ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC);
}
/*
* This is called to generate routing socket messages indicating
* network interface arrival and departure.
*/
void
rtm_ifannounce(struct ifnet *ifp, int what)
{
struct if_announcemsghdr *ifan;
struct mbuf *m;
if (rtptable.rtp_count == 0)
return;
m = rtm_msg1(RTM_IFANNOUNCE, NULL);
if (m == NULL)
return;
ifan = mtod(m, struct if_announcemsghdr *);
ifan->ifan_index = ifp->if_index;
strlcpy(ifan->ifan_name, ifp->if_xname, sizeof(ifan->ifan_name));
ifan->ifan_what = what;
route_input(m, NULL, AF_UNSPEC);
}
#ifdef BFD
/*
* This is used to generate routing socket messages indicating
* the state of a BFD session.
*/
void
rtm_bfd(struct bfd_config *bfd)
{
struct bfd_msghdr *bfdm;
struct sockaddr_bfd sa_bfd;
struct mbuf *m;
struct rt_addrinfo info;
if (rtptable.rtp_count == 0)
return;
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(bfd->bc_rt);
info.rti_info[RTAX_IFA] = bfd->bc_rt->rt_ifa->ifa_addr;
m = rtm_msg1(RTM_BFD, &info);
if (m == NULL)
return;
bfdm = mtod(m, struct bfd_msghdr *);
bfdm->bm_addrs = info.rti_addrs;
KERNEL_ASSERT_LOCKED();
bfd2sa(bfd->bc_rt, &sa_bfd);
memcpy(&bfdm->bm_sa, &sa_bfd, sizeof(sa_bfd));
route_input(m, NULL, info.rti_info[RTAX_DST]->sa_family);
}
#endif /* BFD */
/*
* This is used to generate routing socket messages indicating
* the state of an ieee80211 interface.
*/
void
rtm_80211info(struct ifnet *ifp, struct if_ieee80211_data *ifie)
{
struct if_ieee80211_msghdr *ifim;
struct mbuf *m;
if (rtptable.rtp_count == 0)
return;
m = rtm_msg1(RTM_80211INFO, NULL);
if (m == NULL)
return;
ifim = mtod(m, struct if_ieee80211_msghdr *);
ifim->ifim_index = ifp->if_index;
ifim->ifim_tableid = ifp->if_rdomain;
memcpy(&ifim->ifim_ifie, ifie, sizeof(ifim->ifim_ifie));
route_input(m, NULL, AF_UNSPEC);
}
/*
* This is used to generate routing socket messages indicating
* the address selection proposal from an interface.
*/
void
rtm_proposal(struct ifnet *ifp, struct rt_addrinfo *rtinfo, int flags,
uint8_t prio)
{
struct rt_msghdr *rtm;
struct mbuf *m;
m = rtm_msg1(RTM_PROPOSAL, rtinfo);
if (m == NULL)
return;
rtm = mtod(m, struct rt_msghdr *);
rtm->rtm_flags = RTF_DONE | flags;
rtm->rtm_priority = prio;
rtm->rtm_tableid = ifp->if_rdomain;
rtm->rtm_index = ifp->if_index;
rtm->rtm_addrs = rtinfo->rti_addrs;
route_input(m, NULL, rtinfo->rti_info[RTAX_DNS]->sa_family);
}
/*
* This is used in dumping the kernel table via sysctl().
*/
int
sysctl_dumpentry(struct rtentry *rt, void *v, unsigned int id)
{
struct walkarg *w = v;
int error = 0, size;
struct rt_addrinfo info;
struct ifnet *ifp;
#ifdef BFD
struct sockaddr_bfd sa_bfd;
#endif
struct sockaddr_rtlabel sa_rl;
struct sockaddr_in6 sa_mask;
if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg))
return 0;
if (w->w_op == NET_RT_DUMP && w->w_arg) {
u_int8_t prio = w->w_arg & RTP_MASK;
if (w->w_arg < 0) {
prio = (-w->w_arg) & RTP_MASK;
/* Show all routes that are not this priority */
if (prio == (rt->rt_priority & RTP_MASK))
return 0;
} else {
if (prio != (rt->rt_priority & RTP_MASK) &&
prio != RTP_ANY)
return 0;
}
}
bzero(&info, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(rt);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL) {
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
info.rti_info[RTAX_IFA] =
rtable_getsource(id, info.rti_info[RTAX_DST]->sa_family);
if (info.rti_info[RTAX_IFA] == NULL)
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
if (ifp->if_flags & IFF_POINTOPOINT)
info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr;
}
if_put(ifp);
info.rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl);
#ifdef BFD
if (rt->rt_flags & RTF_BFD) {
KERNEL_ASSERT_LOCKED();
info.rti_info[RTAX_BFD] = bfd2sa(rt, &sa_bfd);
}
#endif
#ifdef MPLS
if (rt->rt_flags & RTF_MPLS) {
struct sockaddr_mpls sa_mpls;
bzero(&sa_mpls, sizeof(sa_mpls));
sa_mpls.smpls_family = AF_MPLS;
sa_mpls.smpls_len = sizeof(sa_mpls);
sa_mpls.smpls_label = ((struct rt_mpls *)
rt->rt_llinfo)->mpls_label;
info.rti_info[RTAX_SRC] = (struct sockaddr *)&sa_mpls;
info.rti_mpls = ((struct rt_mpls *)
rt->rt_llinfo)->mpls_operation;
}
#endif
size = rtm_msg2(RTM_GET, RTM_VERSION, &info, NULL, w);
if (w->w_where && w->w_tmem && w->w_needed <= w->w_given) {
struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem;
rtm->rtm_pid = curproc->p_p->ps_pid;
rtm->rtm_flags = RTF_DONE | rt->rt_flags;
rtm->rtm_priority = rt->rt_priority & RTP_MASK;
rtm_getmetrics(rt, &rtm->rtm_rmx);
/* Do not account the routing table's reference. */
rtm->rtm_rmx.rmx_refcnt = refcnt_read(&rt->rt_refcnt) - 1;
rtm->rtm_index = rt->rt_ifidx;
rtm->rtm_addrs = info.rti_addrs;
rtm->rtm_tableid = id;
#ifdef MPLS
rtm->rtm_mpls = info.rti_mpls;
#endif
if ((error = copyout(rtm, w->w_where, size)) != 0)
w->w_where = NULL;
else
w->w_where += size;
}
return (error);
}
int
sysctl_iflist(int af, struct walkarg *w)
{
struct ifnet *ifp;
struct ifaddr *ifa;
struct rt_addrinfo info;
int len, error = 0;
bzero(&info, sizeof(info));
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (w->w_arg && w->w_arg != ifp->if_index)
continue;
/* Copy the link-layer address first */
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
len = rtm_msg2(RTM_IFINFO, RTM_VERSION, &info, 0, w);
if (w->w_where && w->w_tmem && w->w_needed <= w->w_given) {
struct if_msghdr *ifm;
ifm = (struct if_msghdr *)w->w_tmem;
ifm->ifm_index = ifp->if_index;
ifm->ifm_tableid = ifp->if_rdomain;
ifm->ifm_flags = ifp->if_flags;
if_getdata(ifp, &ifm->ifm_data);
ifm->ifm_addrs = info.rti_addrs;
error = copyout(ifm, w->w_where, len);
if (error)
return (error);
w->w_where += len;
}
info.rti_info[RTAX_IFP] = NULL;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
KASSERT(ifa->ifa_addr->sa_family != AF_LINK);
if (af && af != ifa->ifa_addr->sa_family)
continue;
info.rti_info[RTAX_IFA] = ifa->ifa_addr;
info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
info.rti_info[RTAX_BRD] = ifa->ifa_dstaddr;
len = rtm_msg2(RTM_NEWADDR, RTM_VERSION, &info, 0, w);
if (w->w_where && w->w_tmem &&
w->w_needed <= w->w_given) {
struct ifa_msghdr *ifam;
ifam = (struct ifa_msghdr *)w->w_tmem;
ifam->ifam_index = ifa->ifa_ifp->if_index;
ifam->ifam_flags = ifa->ifa_flags;
ifam->ifam_metric = ifa->ifa_metric;
ifam->ifam_addrs = info.rti_addrs;
error = copyout(w->w_tmem, w->w_where, len);
if (error)
return (error);
w->w_where += len;
}
}
info.rti_info[RTAX_IFA] = info.rti_info[RTAX_NETMASK] =
info.rti_info[RTAX_BRD] = NULL;
}
return (0);
}
int
sysctl_ifnames(struct walkarg *w)
{
struct if_nameindex_msg ifn;
struct ifnet *ifp;
int error = 0;
/* XXX ignore tableid for now */
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (w->w_arg && w->w_arg != ifp->if_index)
continue;
w->w_needed += sizeof(ifn);
if (w->w_where && w->w_needed <= w->w_given) {
memset(&ifn, 0, sizeof(ifn));
ifn.if_index = ifp->if_index;
strlcpy(ifn.if_name, ifp->if_xname,
sizeof(ifn.if_name));
error = copyout(&ifn, w->w_where, sizeof(ifn));
if (error)
return (error);
w->w_where += sizeof(ifn);
}
}
return (0);
}
int
sysctl_source(int af, u_int tableid, struct walkarg *w)
{
struct sockaddr *sa;
int size, error = 0;
sa = rtable_getsource(tableid, af);
if (sa) {
switch (sa->sa_family) {
case AF_INET:
size = sizeof(struct sockaddr_in);
break;
#ifdef INET6
case AF_INET6:
size = sizeof(struct sockaddr_in6);
break;
#endif
default:
return (0);
}
w->w_needed += size;
if (w->w_where && w->w_needed <= w->w_given) {
if ((error = copyout(sa, w->w_where, size)))
return (error);
w->w_where += size;
}
}
return (0);
}
int
sysctl_rtable(int *name, u_int namelen, void *where, size_t *given, void *new,
size_t newlen)
{
int i, error = EINVAL;
u_char af;
struct walkarg w;
struct rt_tableinfo tableinfo;
u_int tableid = 0;
if (new)
return (EPERM);
if (namelen < 3 || namelen > 4)
return (EINVAL);
af = name[0];
bzero(&w, sizeof(w));
w.w_where = where;
w.w_given = *given;
w.w_op = name[1];
w.w_arg = name[2];
if (namelen == 4) {
tableid = name[3];
if (!rtable_exists(tableid))
return (ENOENT);
} else
tableid = curproc->p_p->ps_rtableid;
switch (w.w_op) {
case NET_RT_DUMP:
case NET_RT_FLAGS:
NET_LOCK();
for (i = 1; i <= AF_MAX; i++) {
if (af != 0 && af != i)
continue;
error = rtable_walk(tableid, i, NULL, sysctl_dumpentry,
&w);
if (error == EAFNOSUPPORT)
error = 0;
if (error)
break;
}
NET_UNLOCK();
break;
case NET_RT_IFLIST:
NET_LOCK();
error = sysctl_iflist(af, &w);
NET_UNLOCK();
break;
case NET_RT_STATS:
return (sysctl_rtable_rtstat(where, given, new));
case NET_RT_TABLE:
tableid = w.w_arg;
if (!rtable_exists(tableid))
return (ENOENT);
memset(&tableinfo, 0, sizeof tableinfo);
tableinfo.rti_tableid = tableid;
tableinfo.rti_domainid = rtable_l2(tableid);
error = sysctl_rdstruct(where, given, new,
&tableinfo, sizeof(tableinfo));
return (error);
case NET_RT_IFNAMES:
NET_LOCK();
error = sysctl_ifnames(&w);
NET_UNLOCK();
break;
case NET_RT_SOURCE:
tableid = w.w_arg;
if (!rtable_exists(tableid))
return (ENOENT);
NET_LOCK();
for (i = 1; i <= AF_MAX; i++) {
if (af != 0 && af != i)
continue;
error = sysctl_source(i, tableid, &w);
if (error == EAFNOSUPPORT)
error = 0;
if (error)
break;
}
NET_UNLOCK();
break;
}
free(w.w_tmem, M_RTABLE, w.w_tmemsize);
if (where) {
*given = w.w_where - (caddr_t)where;
if (w.w_needed > w.w_given)
return (ENOMEM);
} else if (w.w_needed == 0) {
*given = 0;
} else {
*given = roundup(w.w_needed + MAX(w.w_needed / 10, 1024),
PAGE_SIZE);
}
return (error);
}
int
sysctl_rtable_rtstat(void *oldp, size_t *oldlenp, void *newp)
{
extern struct cpumem *rtcounters;
uint64_t counters[rts_ncounters];
struct rtstat rtstat;
uint32_t *words = (uint32_t *)&rtstat;
int i;
CTASSERT(sizeof(rtstat) == (nitems(counters) * sizeof(uint32_t)));
memset(&rtstat, 0, sizeof rtstat);
counters_read(rtcounters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (uint32_t)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp, &rtstat, sizeof(rtstat)));
}
int
rtm_validate_proposal(struct rt_addrinfo *info)
{
if (info->rti_addrs & ~(RTA_NETMASK | RTA_IFA | RTA_DNS | RTA_STATIC |
RTA_SEARCH)) {
return -1;
}
if (ISSET(info->rti_addrs, RTA_NETMASK)) {
struct sockaddr *sa = info->rti_info[RTAX_NETMASK];
if (sa == NULL)
return -1;
switch (sa->sa_family) {
case AF_INET:
if (sa->sa_len != sizeof(struct sockaddr_in))
return -1;
break;
case AF_INET6:
if (sa->sa_len != sizeof(struct sockaddr_in6))
return -1;
break;
default:
return -1;
}
}
if (ISSET(info->rti_addrs, RTA_IFA)) {
struct sockaddr *sa = info->rti_info[RTAX_IFA];
if (sa == NULL)
return -1;
switch (sa->sa_family) {
case AF_INET:
if (sa->sa_len != sizeof(struct sockaddr_in))
return -1;
break;
case AF_INET6:
if (sa->sa_len != sizeof(struct sockaddr_in6))
return -1;
break;
default:
return -1;
}
}
if (ISSET(info->rti_addrs, RTA_DNS)) {
struct sockaddr_rtdns *rtdns =
(struct sockaddr_rtdns *)info->rti_info[RTAX_DNS];
if (rtdns == NULL)
return -1;
if (rtdns->sr_len > sizeof(*rtdns))
return -1;
if (rtdns->sr_len < offsetof(struct sockaddr_rtdns, sr_dns))
return -1;
switch (rtdns->sr_family) {
case AF_INET:
if ((rtdns->sr_len - offsetof(struct sockaddr_rtdns,
sr_dns)) % sizeof(struct in_addr) != 0)
return -1;
break;
#ifdef INET6
case AF_INET6:
if ((rtdns->sr_len - offsetof(struct sockaddr_rtdns,
sr_dns)) % sizeof(struct in6_addr) != 0)
return -1;
break;
#endif
default:
return -1;
}
}
if (ISSET(info->rti_addrs, RTA_STATIC)) {
struct sockaddr_rtstatic *rtstatic =
(struct sockaddr_rtstatic *)info->rti_info[RTAX_STATIC];
if (rtstatic == NULL)
return -1;
if (rtstatic->sr_len > sizeof(*rtstatic))
return -1;
if (rtstatic->sr_len <=
offsetof(struct sockaddr_rtstatic, sr_static))
return -1;
}
if (ISSET(info->rti_addrs, RTA_SEARCH)) {
struct sockaddr_rtsearch *rtsearch =
(struct sockaddr_rtsearch *)info->rti_info[RTAX_SEARCH];
if (rtsearch == NULL)
return -1;
if (rtsearch->sr_len > sizeof(*rtsearch))
return -1;
if (rtsearch->sr_len <=
offsetof(struct sockaddr_rtsearch, sr_search))
return -1;
}
return 0;
}
int
rt_setsource(unsigned int rtableid, struct sockaddr *src)
{
struct ifaddr *ifa;
int error;
/*
* If source address is 0.0.0.0 or ::
* use automatic source selection
*/
switch(src->sa_family) {
case AF_INET:
if(satosin(src)->sin_addr.s_addr == INADDR_ANY) {
rtable_setsource(rtableid, AF_INET, NULL);
return (0);
}
break;
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr)) {
rtable_setsource(rtableid, AF_INET6, NULL);
return (0);
}
break;
#endif
default:
return (EAFNOSUPPORT);
}
KERNEL_LOCK();
/*
* Check if source address is assigned to an interface in the
* same rdomain
*/
if ((ifa = ifa_ifwithaddr(src, rtableid)) == NULL) {
KERNEL_UNLOCK();
return (EINVAL);
}
error = rtable_setsource(rtableid, src->sa_family, ifa->ifa_addr);
KERNEL_UNLOCK();
return (error);
}
/*
* Definitions of protocols supported in the ROUTE domain.
*/
const struct pr_usrreqs route_usrreqs = {
.pru_attach = route_attach,
.pru_detach = route_detach,
.pru_disconnect = route_disconnect,
.pru_shutdown = route_shutdown,
.pru_rcvd = route_rcvd,
.pru_send = route_send,
.pru_abort = route_abort,
.pru_sockaddr = route_sockaddr,
.pru_peeraddr = route_peeraddr,
};
const struct protosw routesw[] = {
{
.pr_type = SOCK_RAW,
.pr_domain = &routedomain,
.pr_flags = PR_ATOMIC|PR_ADDR|PR_WANTRCVD,
.pr_ctloutput = route_ctloutput,
.pr_usrreqs = &route_usrreqs,
.pr_init = route_prinit,
.pr_sysctl = sysctl_rtable
}
};
const struct domain routedomain = {
.dom_family = PF_ROUTE,
.dom_name = "route",
.dom_init = route_init,
.dom_protosw = routesw,
.dom_protoswNPROTOSW = &routesw[nitems(routesw)]
};
24
30
30
6
30
28
6
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/* $OpenBSD: chacha_private.h,v 1.4 2020/07/22 13:54:30 tobhe Exp $ */
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
#include <sys/systm.h>
typedef unsigned char u8;
typedef unsigned int u32;
typedef struct
{
u32 input[16]; /* could be compressed */
} chacha_ctx;
#define U8C(v) (v##U)
#define U32C(v) (v##U)
#define U8V(v) ((u8)(v) & U8C(0xFF))
#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
#define ROTL32(v, n) \
(U32V((v) << (n)) | ((v) >> (32 - (n))))
#define U8TO32_LITTLE(p) \
(((u32)((p)[0]) ) | \
((u32)((p)[1]) << 8) | \
((u32)((p)[2]) << 16) | \
((u32)((p)[3]) << 24))
#define U32TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
} while (0)
#define ROTATE(v,c) (ROTL32(v,c))
#define XOR(v,w) ((v) ^ (w))
#define PLUS(v,w) (U32V((v) + (w)))
#define PLUSONE(v) (PLUS((v),1))
#define QUARTERROUND(a,b,c,d) \
a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
static const char sigma[16] = "expand 32-byte k";
static const char tau[16] = "expand 16-byte k";
static inline void
hchacha20(u32 derived_key[8], const u8 nonce[16], const u8 key[32])
{
int i;
uint32_t x[] = {
U8TO32_LITTLE(sigma + 0),
U8TO32_LITTLE(sigma + 4),
U8TO32_LITTLE(sigma + 8),
U8TO32_LITTLE(sigma + 12),
U8TO32_LITTLE(key + 0),
U8TO32_LITTLE(key + 4),
U8TO32_LITTLE(key + 8),
U8TO32_LITTLE(key + 12),
U8TO32_LITTLE(key + 16),
U8TO32_LITTLE(key + 20),
U8TO32_LITTLE(key + 24),
U8TO32_LITTLE(key + 28),
U8TO32_LITTLE(nonce + 0),
U8TO32_LITTLE(nonce + 4),
U8TO32_LITTLE(nonce + 8),
U8TO32_LITTLE(nonce + 12)
};
for (i = 20;i > 0;i -= 2) {
QUARTERROUND( x[0], x[4], x[8],x[12])
QUARTERROUND( x[1], x[5], x[9],x[13])
QUARTERROUND( x[2], x[6],x[10],x[14])
QUARTERROUND( x[3], x[7],x[11],x[15])
QUARTERROUND( x[0], x[5],x[10],x[15])
QUARTERROUND( x[1], x[6],x[11],x[12])
QUARTERROUND( x[2], x[7], x[8],x[13])
QUARTERROUND( x[3], x[4], x[9],x[14])
}
memcpy(derived_key + 0, x + 0, sizeof(u32) * 4);
memcpy(derived_key + 4, x + 12, sizeof(u32) * 4);
}
static void
chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits)
{
const char *constants;
x->input[4] = U8TO32_LITTLE(k + 0);
x->input[5] = U8TO32_LITTLE(k + 4);
x->input[6] = U8TO32_LITTLE(k + 8);
x->input[7] = U8TO32_LITTLE(k + 12);
if (kbits == 256) { /* recommended */
k += 16;
constants = sigma;
} else { /* kbits == 128 */
constants = tau;
}
x->input[8] = U8TO32_LITTLE(k + 0);
x->input[9] = U8TO32_LITTLE(k + 4);
x->input[10] = U8TO32_LITTLE(k + 8);
x->input[11] = U8TO32_LITTLE(k + 12);
x->input[0] = U8TO32_LITTLE(constants + 0);
x->input[1] = U8TO32_LITTLE(constants + 4);
x->input[2] = U8TO32_LITTLE(constants + 8);
x->input[3] = U8TO32_LITTLE(constants + 12);
}
static void
chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter)
{
x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0);
x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4);
x->input[14] = U8TO32_LITTLE(iv + 0);
x->input[15] = U8TO32_LITTLE(iv + 4);
}
static void
chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
{
u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
u8 *ctarget = NULL;
u8 tmp[64];
u_int i;
if (!bytes) return;
j0 = x->input[0];
j1 = x->input[1];
j2 = x->input[2];
j3 = x->input[3];
j4 = x->input[4];
j5 = x->input[5];
j6 = x->input[6];
j7 = x->input[7];
j8 = x->input[8];
j9 = x->input[9];
j10 = x->input[10];
j11 = x->input[11];
j12 = x->input[12];
j13 = x->input[13];
j14 = x->input[14];
j15 = x->input[15];
for (;;) {
if (bytes < 64) {
for (i = 0;i < bytes;++i) tmp[i] = m[i];
m = tmp;
ctarget = c;
c = tmp;
}
x0 = j0;
x1 = j1;
x2 = j2;
x3 = j3;
x4 = j4;
x5 = j5;
x6 = j6;
x7 = j7;
x8 = j8;
x9 = j9;
x10 = j10;
x11 = j11;
x12 = j12;
x13 = j13;
x14 = j14;
x15 = j15;
for (i = 20;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
QUARTERROUND( x3, x7,x11,x15)
QUARTERROUND( x0, x5,x10,x15)
QUARTERROUND( x1, x6,x11,x12)
QUARTERROUND( x2, x7, x8,x13)
QUARTERROUND( x3, x4, x9,x14)
}
x0 = PLUS(x0,j0);
x1 = PLUS(x1,j1);
x2 = PLUS(x2,j2);
x3 = PLUS(x3,j3);
x4 = PLUS(x4,j4);
x5 = PLUS(x5,j5);
x6 = PLUS(x6,j6);
x7 = PLUS(x7,j7);
x8 = PLUS(x8,j8);
x9 = PLUS(x9,j9);
x10 = PLUS(x10,j10);
x11 = PLUS(x11,j11);
x12 = PLUS(x12,j12);
x13 = PLUS(x13,j13);
x14 = PLUS(x14,j14);
x15 = PLUS(x15,j15);
#ifndef KEYSTREAM_ONLY
x0 = XOR(x0,U8TO32_LITTLE(m + 0));
x1 = XOR(x1,U8TO32_LITTLE(m + 4));
x2 = XOR(x2,U8TO32_LITTLE(m + 8));
x3 = XOR(x3,U8TO32_LITTLE(m + 12));
x4 = XOR(x4,U8TO32_LITTLE(m + 16));
x5 = XOR(x5,U8TO32_LITTLE(m + 20));
x6 = XOR(x6,U8TO32_LITTLE(m + 24));
x7 = XOR(x7,U8TO32_LITTLE(m + 28));
x8 = XOR(x8,U8TO32_LITTLE(m + 32));
x9 = XOR(x9,U8TO32_LITTLE(m + 36));
x10 = XOR(x10,U8TO32_LITTLE(m + 40));
x11 = XOR(x11,U8TO32_LITTLE(m + 44));
x12 = XOR(x12,U8TO32_LITTLE(m + 48));
x13 = XOR(x13,U8TO32_LITTLE(m + 52));
x14 = XOR(x14,U8TO32_LITTLE(m + 56));
x15 = XOR(x15,U8TO32_LITTLE(m + 60));
#endif
j12 = PLUSONE(j12);
if (!j12) {
j13 = PLUSONE(j13);
/* stopping at 2^70 bytes per nonce is user's responsibility */
}
U32TO8_LITTLE(c + 0,x0);
U32TO8_LITTLE(c + 4,x1);
U32TO8_LITTLE(c + 8,x2);
U32TO8_LITTLE(c + 12,x3);
U32TO8_LITTLE(c + 16,x4);
U32TO8_LITTLE(c + 20,x5);
U32TO8_LITTLE(c + 24,x6);
U32TO8_LITTLE(c + 28,x7);
U32TO8_LITTLE(c + 32,x8);
U32TO8_LITTLE(c + 36,x9);
U32TO8_LITTLE(c + 40,x10);
U32TO8_LITTLE(c + 44,x11);
U32TO8_LITTLE(c + 48,x12);
U32TO8_LITTLE(c + 52,x13);
U32TO8_LITTLE(c + 56,x14);
U32TO8_LITTLE(c + 60,x15);
if (bytes <= 64) {
if (bytes < 64) {
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
}
x->input[12] = j12;
x->input[13] = j13;
return;
}
bytes -= 64;
c += 64;
#ifndef KEYSTREAM_ONLY
m += 64;
#endif
}
}
4
2
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* $OpenBSD: disksubr.c,v 1.78 2017/02/28 10:49:37 natano Exp $ */
/* $NetBSD: disksubr.c,v 1.21 1996/05/03 19:42:03 christos Exp $ */
/*
* Copyright (c) 1996 Theo de Raadt
* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#ifdef DEBUG
#include <sys/systm.h>
#endif
#include <machine/biosvar.h>
bios_diskinfo_t *bios_getdiskinfo(dev_t dev);
/*
* Attempt to read a disk label from a device
* using the indicated strategy routine.
* The label must be partly set up before this:
* secpercyl, secsize and anything required for a block i/o read
* operation in the driver's strategy/start routines
* must be filled in before calling us.
*
* If dos partition table requested, attempt to load it and
* find disklabel inside a DOS partition.
*
* We would like to check if each MBR has a valid DOSMBR_SIGNATURE, but
* we cannot because it doesn't always exist. So.. we assume the
* MBR is valid.
*/
int
readdisklabel(dev_t dev, void (*strat)(struct buf *),
struct disklabel *lp, int spoofonly)
{
bios_diskinfo_t *pdi;
struct buf *bp = NULL;
dev_t devno;
int error;
if ((error = initdisklabel(lp)))
goto done;
/* Look for any BIOS geometry information we should honour. */
devno = chrtoblk(dev);
if (devno == NODEV)
devno = dev;
pdi = bios_getdiskinfo(MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno),
RAW_PART));
if (pdi != NULL && pdi->bios_heads > 0 && pdi->bios_sectors > 0) {
#ifdef DEBUG
printf("Disk GEOM %u/%u/%u -> BIOS GEOM %u/%u/%llu\n",
lp->d_ntracks, lp->d_nsectors, lp->d_ncylinders,
pdi->bios_heads, pdi->bios_sectors,
DL_GETDSIZE(lp) / (pdi->bios_heads * pdi->bios_sectors));
#endif
lp->d_ntracks = pdi->bios_heads;
lp->d_nsectors = pdi->bios_sectors;
lp->d_secpercyl = pdi->bios_sectors * pdi->bios_heads;
lp->d_ncylinders = DL_GETDSIZE(lp) / lp->d_secpercyl;
}
/* get a buffer and initialize it */
bp = geteblk(lp->d_secsize);
bp->b_dev = dev;
error = readdoslabel(bp, strat, lp, NULL, spoofonly);
if (error == 0)
goto done;
#if defined(CD9660)
error = iso_disklabelspoof(dev, strat, lp);
if (error == 0)
goto done;
#endif
#if defined(UDF)
error = udf_disklabelspoof(dev, strat, lp);
if (error == 0)
goto done;
#endif
done:
if (bp) {
bp->b_flags |= B_INVAL;
brelse(bp);
}
disk_change = 1;
return (error);
}
/*
* Write disk label back to device after modification.
*/
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp)
{
daddr_t partoff = -1;
int error = EIO;
int offset;
struct disklabel *dlp;
struct buf *bp = NULL;
/* get a buffer and initialize it */
bp = geteblk(lp->d_secsize);
bp->b_dev = dev;
if (readdoslabel(bp, strat, lp, &partoff, 1) != 0)
goto done;
/* Read it in, slap the new label in, and write it back out */
error = readdisksector(bp, strat, lp, DL_BLKTOSEC(lp, partoff +
DOS_LABELSECTOR));
if (error)
goto done;
offset = DL_BLKOFFSET(lp, partoff + DOS_LABELSECTOR);
dlp = (struct disklabel *)(bp->b_data + offset);
*dlp = *lp;
CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
SET(bp->b_flags, B_BUSY | B_WRITE | B_RAW);
(*strat)(bp);
error = biowait(bp);
done:
if (bp) {
bp->b_flags |= B_INVAL;
brelse(bp);
}
disk_change = 1;
return (error);
}
6
4
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/* $OpenBSD: uvm_device.c,v 1.66 2021/12/15 12:53:53 mpi Exp $ */
/* $NetBSD: uvm_device.c,v 1.30 2000/11/25 06:27:59 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* from: Id: uvm_device.c,v 1.1.2.9 1998/02/06 05:11:47 chs Exp
*/
/*
* uvm_device.c: the device pager.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <uvm/uvm.h>
#include <uvm/uvm_device.h>
#include "drm.h"
/*
* private global data structure
*
* we keep a list of active device objects in the system.
*/
LIST_HEAD(, uvm_device) udv_list = LIST_HEAD_INITIALIZER(udv_list);
struct mutex udv_lock = MUTEX_INITIALIZER(IPL_NONE);
/*
* functions
*/
static void udv_reference(struct uvm_object *);
static void udv_detach(struct uvm_object *);
static int udv_fault(struct uvm_faultinfo *, vaddr_t,
vm_page_t *, int, int, vm_fault_t,
vm_prot_t, int);
static boolean_t udv_flush(struct uvm_object *, voff_t, voff_t,
int);
/*
* master pager structure
*/
const struct uvm_pagerops uvm_deviceops = {
.pgo_reference = udv_reference,
.pgo_detach = udv_detach,
.pgo_fault = udv_fault,
.pgo_flush = udv_flush,
};
/*
* the ops!
*/
/*
* udv_attach
*
* get a VM object that is associated with a device. allocate a new
* one if needed.
*
* => nothing should be locked so that we can sleep here.
*
* The last two arguments (off and size) are only used for access checking.
*/
struct uvm_object *
udv_attach(dev_t device, vm_prot_t accessprot, voff_t off, vsize_t size)
{
struct uvm_device *udv, *lcv;
paddr_t (*mapfn)(dev_t, off_t, int);
#if NDRM > 0
struct uvm_object *obj;
#endif
/*
* before we do anything, ensure this device supports mmap
*/
mapfn = cdevsw[major(device)].d_mmap;
if (mapfn == NULL ||
mapfn == (paddr_t (*)(dev_t, off_t, int)) enodev ||
mapfn == (paddr_t (*)(dev_t, off_t, int)) nullop)
return(NULL);
/*
* Negative offsets on the object are not allowed.
*/
if (off < 0)
return(NULL);
#if NDRM > 0
obj = udv_attach_drm(device, accessprot, off, size);
if (obj)
return(obj);
#endif
/*
* Check that the specified range of the device allows the
* desired protection.
*
* XXX clobbers off and size, but nothing else here needs them.
*/
while (size != 0) {
if ((*mapfn)(device, off, accessprot) == -1)
return (NULL);
off += PAGE_SIZE; size -= PAGE_SIZE;
}
/*
* keep looping until we get it
*/
for (;;) {
/*
* first, attempt to find it on the main list
*/
mtx_enter(&udv_lock);
LIST_FOREACH(lcv, &udv_list, u_list) {
if (device == lcv->u_device)
break;
}
/*
* got it on main list. put a hold on it and unlock udv_lock.
*/
if (lcv) {
/*
* if someone else has a hold on it, sleep and start
* over again. Else, we need take HOLD flag so we
* don't have to re-order locking here.
*/
if (lcv->u_flags & UVM_DEVICE_HOLD) {
lcv->u_flags |= UVM_DEVICE_WANTED;
msleep_nsec(lcv, &udv_lock, PVM | PNORELOCK,
"udv_attach", INFSLP);
continue;
}
/* we are now holding it */
lcv->u_flags |= UVM_DEVICE_HOLD;
mtx_leave(&udv_lock);
/*
* bump reference count, unhold, return.
*/
rw_enter(lcv->u_obj.vmobjlock, RW_WRITE);
lcv->u_obj.uo_refs++;
rw_exit(lcv->u_obj.vmobjlock);
mtx_enter(&udv_lock);
if (lcv->u_flags & UVM_DEVICE_WANTED)
wakeup(lcv);
lcv->u_flags &= ~(UVM_DEVICE_WANTED|UVM_DEVICE_HOLD);
mtx_leave(&udv_lock);
return(&lcv->u_obj);
}
/*
* Did not find it on main list. Need to allocate a new one.
*/
mtx_leave(&udv_lock);
/* NOTE: we could sleep in the following malloc() */
udv = malloc(sizeof(*udv), M_TEMP, M_WAITOK);
uvm_obj_init(&udv->u_obj, &uvm_deviceops, 1);
mtx_enter(&udv_lock);
/*
* now we have to double check to make sure no one added it
* to the list while we were sleeping...
*/
LIST_FOREACH(lcv, &udv_list, u_list) {
if (device == lcv->u_device)
break;
}
/*
* did we lose a race to someone else?
* free our memory and retry.
*/
if (lcv) {
mtx_leave(&udv_lock);
uvm_obj_destroy(&udv->u_obj);
free(udv, M_TEMP, sizeof(*udv));
continue;
}
/*
* we have it! init the data structures, add to list
* and return.
*/
udv->u_flags = 0;
udv->u_device = device;
LIST_INSERT_HEAD(&udv_list, udv, u_list);
mtx_leave(&udv_lock);
return(&udv->u_obj);
}
/*NOTREACHED*/
}
/*
* udv_reference
*
* add a reference to a VM object. Note that the reference count must
* already be one (the passed in reference) so there is no chance of the
* udv being released or locked out here.
*/
static void
udv_reference(struct uvm_object *uobj)
{
rw_enter(uobj->vmobjlock, RW_WRITE);
uobj->uo_refs++;
rw_exit(uobj->vmobjlock);
}
/*
* udv_detach
*
* remove a reference to a VM object.
*/
static void
udv_detach(struct uvm_object *uobj)
{
struct uvm_device *udv = (struct uvm_device *)uobj;
KERNEL_ASSERT_LOCKED();
/*
* loop until done
*/
again:
rw_enter(uobj->vmobjlock, RW_WRITE);
if (uobj->uo_refs > 1) {
uobj->uo_refs--;
rw_exit(uobj->vmobjlock);
return;
}
KASSERT(uobj->uo_npages == 0 && RBT_EMPTY(uvm_objtree, &uobj->memt));
/*
* is it being held? if so, wait until others are done.
*/
mtx_enter(&udv_lock);
if (udv->u_flags & UVM_DEVICE_HOLD) {
udv->u_flags |= UVM_DEVICE_WANTED;
rw_exit(uobj->vmobjlock);
msleep_nsec(udv, &udv_lock, PVM | PNORELOCK, "udv_detach",
INFSLP);
goto again;
}
/*
* got it! nuke it now.
*/
LIST_REMOVE(udv, u_list);
if (udv->u_flags & UVM_DEVICE_WANTED)
wakeup(udv);
mtx_leave(&udv_lock);
rw_exit(uobj->vmobjlock);
uvm_obj_destroy(uobj);
free(udv, M_TEMP, sizeof(*udv));
}
/*
* udv_flush
*
* flush pages out of a uvm object. a no-op for devices.
*/
static boolean_t
udv_flush(struct uvm_object *uobj, voff_t start, voff_t stop, int flags)
{
return(TRUE);
}
/*
* udv_fault: non-standard fault routine for device "pages"
*
* => rather than having a "get" function, we have a fault routine
* since we don't return vm_pages we need full control over the
* pmap_enter map in
* => on return, we unlock all fault data structures
* => flags: PGO_ALLPAGES: get all of the pages
* PGO_LOCKED: fault data structures are locked
* XXX: currently PGO_LOCKED is always required ... consider removing
* it as a flag
* => NOTE: vaddr is the VA of pps[0] in ufi->entry, _NOT_ pps[centeridx]
*/
static int
udv_fault(struct uvm_faultinfo *ufi, vaddr_t vaddr, vm_page_t *pps, int npages,
int centeridx, vm_fault_t fault_type, vm_prot_t access_type, int flags)
{
struct vm_map_entry *entry = ufi->entry;
struct uvm_object *uobj = entry->object.uvm_obj;
struct uvm_device *udv = (struct uvm_device *)uobj;
vaddr_t curr_va;
off_t curr_offset;
paddr_t paddr;
int lcv, retval;
dev_t device;
paddr_t (*mapfn)(dev_t, off_t, int);
vm_prot_t mapprot;
KERNEL_ASSERT_LOCKED();
/*
* we do not allow device mappings to be mapped copy-on-write
* so we kill any attempt to do so here.
*/
if (UVM_ET_ISCOPYONWRITE(entry)) {
uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj);
return(VM_PAGER_ERROR);
}
/*
* get device map function.
*/
device = udv->u_device;
mapfn = cdevsw[major(device)].d_mmap;
/*
* now we must determine the offset in udv to use and the VA to
* use for pmap_enter. note that we always use orig_map's pmap
* for pmap_enter (even if we have a submap). since virtual
* addresses in a submap must match the main map, this is ok.
*/
/* udv offset = (offset from start of entry) + entry's offset */
curr_offset = entry->offset + (vaddr - entry->start);
/* pmap va = vaddr (virtual address of pps[0]) */
curr_va = vaddr;
/*
* loop over the page range entering in as needed
*/
retval = VM_PAGER_OK;
for (lcv = 0 ; lcv < npages ; lcv++, curr_offset += PAGE_SIZE,
curr_va += PAGE_SIZE) {
if ((flags & PGO_ALLPAGES) == 0 && lcv != centeridx)
continue;
if (pps[lcv] == PGO_DONTCARE)
continue;
paddr = (*mapfn)(device, curr_offset, access_type);
if (paddr == -1) {
retval = VM_PAGER_ERROR;
break;
}
mapprot = ufi->entry->protection;
if (pmap_enter(ufi->orig_map->pmap, curr_va, paddr,
mapprot, PMAP_CANFAIL | mapprot) != 0) {
/*
* pmap_enter() didn't have the resource to
* enter this mapping. Unlock everything,
* wait for the pagedaemon to free up some
* pages, and then tell uvm_fault() to start
* the fault again.
*
* XXX Needs some rethinking for the PGO_ALLPAGES
* XXX case.
*/
uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap,
uobj);
/* sync what we have so far */
pmap_update(ufi->orig_map->pmap);
uvm_wait("udv_fault");
return (VM_PAGER_REFAULT);
}
}
uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj);
pmap_update(ufi->orig_map->pmap);
return (retval);
}
1
1
107
100
8
20
15
17
143
40
58
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/* $OpenBSD: if_loop.c,v 1.91 2020/07/22 02:16:01 dlg Exp $ */
/* $NetBSD: if_loop.c,v 1.15 1996/05/07 02:40:33 thorpej Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)if_loop.c 8.1 (Berkeley) 6/10/93
*/
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
/*
* Loopback interface driver for protocol testing and timing.
*/
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/rtable.h>
#include <net/route.h>
#include <netinet/in.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif
#ifdef MPLS
#include <netmpls/mpls.h>
#endif
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#define LOMTU 32768
int loioctl(struct ifnet *, u_long, caddr_t);
void loopattach(int);
void lortrequest(struct ifnet *, int, struct rtentry *);
void loinput(struct ifnet *, struct mbuf *);
int looutput(struct ifnet *,
struct mbuf *, struct sockaddr *, struct rtentry *);
int loop_clone_create(struct if_clone *, int);
int loop_clone_destroy(struct ifnet *);
struct if_clone loop_cloner =
IF_CLONE_INITIALIZER("lo", loop_clone_create, loop_clone_destroy);
void
loopattach(int n)
{
if (loop_clone_create(&loop_cloner, 0))
panic("unable to create lo0");
if_clone_attach(&loop_cloner);
}
int
loop_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
ifp = malloc(sizeof(*ifp), M_DEVBUF, M_WAITOK|M_ZERO);
snprintf(ifp->if_xname, sizeof ifp->if_xname, "lo%d", unit);
ifp->if_softc = NULL;
ifp->if_mtu = LOMTU;
ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST;
ifp->if_xflags = IFXF_CLONED;
ifp->if_rtrequest = lortrequest;
ifp->if_ioctl = loioctl;
ifp->if_input = loinput;
ifp->if_output = looutput;
ifp->if_type = IFT_LOOP;
ifp->if_hdrlen = sizeof(u_int32_t);
if (unit == 0) {
if_attachhead(ifp);
if_addgroup(ifp, ifc->ifc_name);
rtable_l2set(0, 0, ifp->if_index);
} else
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
#endif
return (0);
}
int
loop_clone_destroy(struct ifnet *ifp)
{
struct ifnet *p;
unsigned int rdomain = 0;
if (ifp->if_index == rtable_loindex(ifp->if_rdomain)) {
/* rdomain 0 always needs a loopback */
if (ifp->if_rdomain == 0)
return (EPERM);
/* if there is any other interface in this rdomain, deny */
NET_LOCK();
TAILQ_FOREACH(p, &ifnet, if_list) {
if (p->if_rdomain != ifp->if_rdomain)
continue;
if (p->if_index == ifp->if_index)
continue;
NET_UNLOCK();
return (EBUSY);
}
NET_UNLOCK();
rdomain = ifp->if_rdomain;
}
if_detach(ifp);
free(ifp, M_DEVBUF, sizeof(*ifp));
if (rdomain)
rtable_l2set(rdomain, 0, 0);
return (0);
}
void
loinput(struct ifnet *ifp, struct mbuf *m)
{
int error;
if ((m->m_flags & M_PKTHDR) == 0)
panic("%s: no header mbuf", __func__);
error = if_input_local(ifp, m, m->m_pkthdr.ph_family);
if (error)
ifp->if_ierrors++;
}
int
looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
if ((m->m_flags & M_PKTHDR) == 0)
panic("%s: no header mbuf", __func__);
if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
m_freem(m);
return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
}
/*
* Do not call if_input_local() directly. Queue the packet to avoid
* stack overflow and make TCP handshake over loopback work.
*/
return (if_output_local(ifp, m, dst->sa_family));
}
void
lortrequest(struct ifnet *ifp, int cmd, struct rtentry *rt)
{
if (rt && rt->rt_mtu == 0)
rt->rt_mtu = LOMTU;
}
/*
* Process an ioctl request.
*/
int
loioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ifreq *ifr;
int error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
break;
case SIOCSIFADDR:
ifp->if_flags |= IFF_RUNNING;
if_up(ifp); /* send up RTM_IFINFO */
/*
* Everything else is done at a higher level.
*/
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
case SIOCSIFMTU:
ifr = (struct ifreq *)data;
ifp->if_mtu = ifr->ifr_mtu;
break;
default:
error = ENOTTY;
}
return (error);
}
6
5
3
3
10
2
2
1
1
1
1
10
10
12
12
2
1
1
1
1
1
9
9
12
1
1
10
1
1
1
10
9
10
1
8
2
1
5
11
1
10
2
1
2
1
47
36
12
1
1
11
36
33
31
2
24
15
11
1
21
21
2
21
56
55
56
54
56
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
/* $OpenBSD: pf_norm.c,v 1.224 2022/08/22 20:35:39 bluhm Exp $ */
/*
* Copyright 2001 Niels Provos <provos@citi.umich.edu>
* Copyright 2009 Henning Brauer <henning@openbsd.org>
* Copyright 2011-2018 Alexander Bluhm <bluhm@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "pflog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/pool.h>
#include <sys/syslog.h>
#include <sys/mutex.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_pflog.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_fsm.h>
#include <netinet/udp.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
struct pf_frent {
TAILQ_ENTRY(pf_frent) fr_next;
struct mbuf *fe_m;
u_int16_t fe_hdrlen; /* ipv4 header length with ip options
ipv6, extension, fragment header */
u_int16_t fe_extoff; /* last extension header offset or 0 */
u_int16_t fe_len; /* fragment length */
u_int16_t fe_off; /* fragment offset */
u_int16_t fe_mff; /* more fragment flag */
};
RB_HEAD(pf_frag_tree, pf_fragment);
struct pf_frnode {
struct pf_addr fn_src; /* ip source address */
struct pf_addr fn_dst; /* ip destination address */
sa_family_t fn_af; /* address family */
u_int8_t fn_proto; /* protocol for fragments in fn_tree */
u_int8_t fn_direction; /* pf packet direction */
u_int32_t fn_fragments; /* number of entries in fn_tree */
u_int32_t fn_gen; /* fr_gen of newest entry in fn_tree */
RB_ENTRY(pf_frnode) fn_entry;
struct pf_frag_tree fn_tree; /* matching fragments, lookup by id */
};
struct pf_fragment {
struct pf_frent *fr_firstoff[PF_FRAG_ENTRY_POINTS];
/* pointers to queue element */
u_int8_t fr_entries[PF_FRAG_ENTRY_POINTS];
/* count entries between pointers */
RB_ENTRY(pf_fragment) fr_entry;
TAILQ_ENTRY(pf_fragment) frag_next;
TAILQ_HEAD(pf_fragq, pf_frent) fr_queue;
u_int32_t fr_id; /* fragment id for reassemble */
int32_t fr_timeout;
u_int32_t fr_gen; /* generation number (per pf_frnode) */
u_int16_t fr_maxlen; /* maximum length of single fragment */
u_int16_t fr_holes; /* number of holes in the queue */
struct pf_frnode *fr_node; /* ip src/dst/proto/af for fragments */
};
struct pf_fragment_tag {
u_int16_t ft_hdrlen; /* header length of reassembled pkt */
u_int16_t ft_extoff; /* last extension header offset or 0 */
u_int16_t ft_maxlen; /* maximum fragment payload length */
};
TAILQ_HEAD(pf_fragqueue, pf_fragment) pf_fragqueue;
static __inline int pf_frnode_compare(struct pf_frnode *,
struct pf_frnode *);
RB_HEAD(pf_frnode_tree, pf_frnode) pf_frnode_tree;
RB_PROTOTYPE(pf_frnode_tree, pf_frnode, fn_entry, pf_frnode_compare);
RB_GENERATE(pf_frnode_tree, pf_frnode, fn_entry, pf_frnode_compare);
static __inline int pf_frag_compare(struct pf_fragment *,
struct pf_fragment *);
RB_PROTOTYPE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare);
RB_GENERATE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare);
/* Private prototypes */
void pf_flush_fragments(void);
void pf_free_fragment(struct pf_fragment *);
struct pf_fragment *pf_find_fragment(struct pf_frnode *, u_int32_t);
struct pf_frent *pf_create_fragment(u_short *);
int pf_frent_holes(struct pf_frent *);
static inline int pf_frent_index(struct pf_frent *);
int pf_frent_insert(struct pf_fragment *,
struct pf_frent *, struct pf_frent *);
void pf_frent_remove(struct pf_fragment *,
struct pf_frent *);
struct pf_frent *pf_frent_previous(struct pf_fragment *,
struct pf_frent *);
struct pf_fragment *pf_fillup_fragment(struct pf_frnode *, u_int32_t,
struct pf_frent *, u_short *);
struct mbuf *pf_join_fragment(struct pf_fragment *);
int pf_reassemble(struct mbuf **, int, u_short *);
#ifdef INET6
int pf_reassemble6(struct mbuf **, struct ip6_frag *,
u_int16_t, u_int16_t, int, u_short *);
#endif /* INET6 */
/* Globals */
struct pool pf_frent_pl, pf_frag_pl, pf_frnode_pl;
struct pool pf_state_scrub_pl;
int pf_nfrents;
struct mutex pf_frag_mtx;
#define PF_FRAG_LOCK_INIT() mtx_init(&pf_frag_mtx, IPL_SOFTNET)
#define PF_FRAG_LOCK() mtx_enter(&pf_frag_mtx)
#define PF_FRAG_UNLOCK() mtx_leave(&pf_frag_mtx)
void
pf_normalize_init(void)
{
pool_init(&pf_frent_pl, sizeof(struct pf_frent), 0,
IPL_SOFTNET, 0, "pffrent", NULL);
pool_init(&pf_frnode_pl, sizeof(struct pf_frnode), 0,
IPL_SOFTNET, 0, "pffrnode", NULL);
pool_init(&pf_frag_pl, sizeof(struct pf_fragment), 0,
IPL_SOFTNET, 0, "pffrag", NULL);
pool_init(&pf_state_scrub_pl, sizeof(struct pf_state_scrub), 0,
IPL_SOFTNET, 0, "pfstscr", NULL);
pool_sethiwat(&pf_frag_pl, PFFRAG_FRAG_HIWAT);
pool_sethardlimit(&pf_frent_pl, PFFRAG_FRENT_HIWAT, NULL, 0);
TAILQ_INIT(&pf_fragqueue);
PF_FRAG_LOCK_INIT();
}
static __inline int
pf_frnode_compare(struct pf_frnode *a, struct pf_frnode *b)
{
int diff;
if ((diff = a->fn_proto - b->fn_proto) != 0)
return (diff);
if ((diff = a->fn_af - b->fn_af) != 0)
return (diff);
if ((diff = pf_addr_compare(&a->fn_src, &b->fn_src, a->fn_af)) != 0)
return (diff);
if ((diff = pf_addr_compare(&a->fn_dst, &b->fn_dst, a->fn_af)) != 0)
return (diff);
return (0);
}
static __inline int
pf_frag_compare(struct pf_fragment *a, struct pf_fragment *b)
{
int diff;
if ((diff = a->fr_id - b->fr_id) != 0)
return (diff);
return (0);
}
void
pf_purge_expired_fragments(void)
{
struct pf_fragment *frag;
int32_t expire;
PF_ASSERT_UNLOCKED();
expire = getuptime() - pf_default_rule.timeout[PFTM_FRAG];
PF_FRAG_LOCK();
while ((frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue)) != NULL) {
if (frag->fr_timeout > expire)
break;
DPFPRINTF(LOG_NOTICE, "expiring %d(%p)", frag->fr_id, frag);
pf_free_fragment(frag);
}
PF_FRAG_UNLOCK();
}
/*
* Try to flush old fragments to make space for new ones
*/
void
pf_flush_fragments(void)
{
struct pf_fragment *frag;
int goal;
goal = pf_nfrents * 9 / 10;
DPFPRINTF(LOG_NOTICE, "trying to free > %d frents", pf_nfrents - goal);
while (goal < pf_nfrents) {
if ((frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue)) == NULL)
break;
pf_free_fragment(frag);
}
}
/*
* Remove a fragment from the fragment queue, free its fragment entries,
* and free the fragment itself.
*/
void
pf_free_fragment(struct pf_fragment *frag)
{
struct pf_frent *frent;
struct pf_frnode *frnode;
frnode = frag->fr_node;
RB_REMOVE(pf_frag_tree, &frnode->fn_tree, frag);
KASSERT(frnode->fn_fragments >= 1);
frnode->fn_fragments--;
if (frnode->fn_fragments == 0) {
KASSERT(RB_EMPTY(&frnode->fn_tree));
RB_REMOVE(pf_frnode_tree, &pf_frnode_tree, frnode);
pool_put(&pf_frnode_pl, frnode);
}
TAILQ_REMOVE(&pf_fragqueue, frag, frag_next);
/* Free all fragment entries */
while ((frent = TAILQ_FIRST(&frag->fr_queue)) != NULL) {
TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
m_freem(frent->fe_m);
pool_put(&pf_frent_pl, frent);
pf_nfrents--;
}
pool_put(&pf_frag_pl, frag);
}
struct pf_fragment *
pf_find_fragment(struct pf_frnode *key, u_int32_t id)
{
struct pf_fragment *frag, idkey;
struct pf_frnode *frnode;
u_int32_t stale;
frnode = RB_FIND(pf_frnode_tree, &pf_frnode_tree, key);
if (frnode == NULL)
return (NULL);
KASSERT(frnode->fn_fragments >= 1);
idkey.fr_id = id;
frag = RB_FIND(pf_frag_tree, &frnode->fn_tree, &idkey);
if (frag == NULL)
return (NULL);
/*
* Limit the number of fragments we accept for each (proto,src,dst,af)
* combination (aka pf_frnode), so we can deal better with a high rate
* of fragments. Problem analysis is in RFC 4963.
* Store the current generation for each pf_frnode in fn_gen and on
* lookup discard 'stale' fragments (pf_fragment, based on the fr_gen
* member). Instead of adding another button interpret the pf fragment
* timeout in multiples of 200 fragments. This way the default of 60s
* means: pf_fragment objects older than 60*200 = 12,000 generations
* are considered stale.
*/
stale = pf_default_rule.timeout[PFTM_FRAG] * PF_FRAG_STALE;
if ((frnode->fn_gen - frag->fr_gen) >= stale) {
DPFPRINTF(LOG_NOTICE, "stale fragment %d(%p), gen %u, num %u",
frag->fr_id, frag, frag->fr_gen, frnode->fn_fragments);
pf_free_fragment(frag);
return (NULL);
}
TAILQ_REMOVE(&pf_fragqueue, frag, frag_next);
TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next);
return (frag);
}
struct pf_frent *
pf_create_fragment(u_short *reason)
{
struct pf_frent *frent;
frent = pool_get(&pf_frent_pl, PR_NOWAIT);
if (frent == NULL) {
pf_flush_fragments();
frent = pool_get(&pf_frent_pl, PR_NOWAIT);
if (frent == NULL) {
REASON_SET(reason, PFRES_MEMORY);
return (NULL);
}
}
pf_nfrents++;
return (frent);
}
/*
* Calculate the additional holes that were created in the fragment
* queue by inserting this fragment. A fragment in the middle
* creates one more hole by splitting. For each connected side,
* it loses one hole.
* Fragment entry must be in the queue when calling this function.
*/
int
pf_frent_holes(struct pf_frent *frent)
{
struct pf_frent *prev = TAILQ_PREV(frent, pf_fragq, fr_next);
struct pf_frent *next = TAILQ_NEXT(frent, fr_next);
int holes = 1;
if (prev == NULL) {
if (frent->fe_off == 0)
holes--;
} else {
KASSERT(frent->fe_off != 0);
if (frent->fe_off == prev->fe_off + prev->fe_len)
holes--;
}
if (next == NULL) {
if (!frent->fe_mff)
holes--;
} else {
KASSERT(frent->fe_mff);
if (next->fe_off == frent->fe_off + frent->fe_len)
holes--;
}
return holes;
}
static inline int
pf_frent_index(struct pf_frent *frent)
{
/*
* We have an array of 16 entry points to the queue. A full size
* 65535 octet IP packet can have 8192 fragments. So the queue
* traversal length is at most 512 and at most 16 entry points are
* checked. We need 128 additional bytes on a 64 bit architecture.
*/
CTASSERT(((u_int16_t)0xffff &~ 7) / (0x10000 / PF_FRAG_ENTRY_POINTS) ==
16 - 1);
CTASSERT(((u_int16_t)0xffff >> 3) / PF_FRAG_ENTRY_POINTS == 512 - 1);
return frent->fe_off / (0x10000 / PF_FRAG_ENTRY_POINTS);
}
int
pf_frent_insert(struct pf_fragment *frag, struct pf_frent *frent,
struct pf_frent *prev)
{
CTASSERT(PF_FRAG_ENTRY_LIMIT <= 0xff);
int index;
/*
* A packet has at most 65536 octets. With 16 entry points, each one
* spawns 4096 octets. We limit these to 64 fragments each, which
* means on average every fragment must have at least 64 octets.
*/
index = pf_frent_index(frent);
if (frag->fr_entries[index] >= PF_FRAG_ENTRY_LIMIT)
return ENOBUFS;
frag->fr_entries[index]++;
if (prev == NULL) {
TAILQ_INSERT_HEAD(&frag->fr_queue, frent, fr_next);
} else {
KASSERT(prev->fe_off + prev->fe_len <= frent->fe_off);
TAILQ_INSERT_AFTER(&frag->fr_queue, prev, frent, fr_next);
}
if (frag->fr_firstoff[index] == NULL) {
KASSERT(prev == NULL || pf_frent_index(prev) < index);
frag->fr_firstoff[index] = frent;
} else {
if (frent->fe_off < frag->fr_firstoff[index]->fe_off) {
KASSERT(prev == NULL || pf_frent_index(prev) < index);
frag->fr_firstoff[index] = frent;
} else {
KASSERT(prev != NULL);
KASSERT(pf_frent_index(prev) == index);
}
}
frag->fr_holes += pf_frent_holes(frent);
return 0;
}
void
pf_frent_remove(struct pf_fragment *frag, struct pf_frent *frent)
{
#ifdef DIAGNOSTIC
struct pf_frent *prev = TAILQ_PREV(frent, pf_fragq, fr_next);
#endif
struct pf_frent *next = TAILQ_NEXT(frent, fr_next);
int index;
frag->fr_holes -= pf_frent_holes(frent);
index = pf_frent_index(frent);
KASSERT(frag->fr_firstoff[index] != NULL);
if (frag->fr_firstoff[index]->fe_off == frent->fe_off) {
if (next == NULL) {
frag->fr_firstoff[index] = NULL;
} else {
KASSERT(frent->fe_off + frent->fe_len <= next->fe_off);
if (pf_frent_index(next) == index) {
frag->fr_firstoff[index] = next;
} else {
frag->fr_firstoff[index] = NULL;
}
}
} else {
KASSERT(frag->fr_firstoff[index]->fe_off < frent->fe_off);
KASSERT(prev != NULL);
KASSERT(prev->fe_off + prev->fe_len <= frent->fe_off);
KASSERT(pf_frent_index(prev) == index);
}
TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
KASSERT(frag->fr_entries[index] > 0);
frag->fr_entries[index]--;
}
struct pf_frent *
pf_frent_previous(struct pf_fragment *frag, struct pf_frent *frent)
{
struct pf_frent *prev, *next;
int index;
/*
* If there are no fragments after frag, take the final one. Assume
* that the global queue is not empty.
*/
prev = TAILQ_LAST(&frag->fr_queue, pf_fragq);
KASSERT(prev != NULL);
if (prev->fe_off <= frent->fe_off)
return prev;
/*
* We want to find a fragment entry that is before frag, but still
* close to it. Find the first fragment entry that is in the same
* entry point or in the first entry point after that. As we have
* already checked that there are entries behind frag, this will
* succeed.
*/
for (index = pf_frent_index(frent); index < PF_FRAG_ENTRY_POINTS;
index++) {
prev = frag->fr_firstoff[index];
if (prev != NULL)
break;
}
KASSERT(prev != NULL);
/*
* In prev we may have a fragment from the same entry point that is
* before frent, or one that is just one position behind frent.
* In the latter case, we go back one step and have the predecessor.
* There may be none if the new fragment will be the first one.
*/
if (prev->fe_off > frent->fe_off) {
prev = TAILQ_PREV(prev, pf_fragq, fr_next);
if (prev == NULL)
return NULL;
KASSERT(prev->fe_off <= frent->fe_off);
return prev;
}
/*
* In prev is the first fragment of the entry point. The offset
* of frag is behind it. Find the closest previous fragment.
*/
for (next = TAILQ_NEXT(prev, fr_next); next != NULL;
next = TAILQ_NEXT(next, fr_next)) {
if (next->fe_off > frent->fe_off)
break;
prev = next;
}
return prev;
}
struct pf_fragment *
pf_fillup_fragment(struct pf_frnode *key, u_int32_t id,
struct pf_frent *frent, u_short *reason)
{
struct pf_frent *after, *next, *prev;
struct pf_fragment *frag;
struct pf_frnode *frnode;
u_int16_t total;
/* No empty fragments */
if (frent->fe_len == 0) {
DPFPRINTF(LOG_NOTICE, "bad fragment: len 0");
goto bad_fragment;
}
/* All fragments are 8 byte aligned */
if (frent->fe_mff && (frent->fe_len & 0x7)) {
DPFPRINTF(LOG_NOTICE, "bad fragment: mff and len %d",
frent->fe_len);
goto bad_fragment;
}
/* Respect maximum length, IP_MAXPACKET == IPV6_MAXPACKET */
if (frent->fe_off + frent->fe_len > IP_MAXPACKET) {
DPFPRINTF(LOG_NOTICE, "bad fragment: max packet %d",
frent->fe_off + frent->fe_len);
goto bad_fragment;
}
DPFPRINTF(LOG_INFO, key->fn_af == AF_INET ?
"reass frag %d @ %d-%d" : "reass frag %#08x @ %d-%d",
id, frent->fe_off, frent->fe_off + frent->fe_len);
/* Fully buffer all of the fragments in this fragment queue */
frag = pf_find_fragment(key, id);
/* Create a new reassembly queue for this packet */
if (frag == NULL) {
frag = pool_get(&pf_frag_pl, PR_NOWAIT);
if (frag == NULL) {
pf_flush_fragments();
frag = pool_get(&pf_frag_pl, PR_NOWAIT);
if (frag == NULL) {
REASON_SET(reason, PFRES_MEMORY);
goto drop_fragment;
}
}
frnode = RB_FIND(pf_frnode_tree, &pf_frnode_tree, key);
if (frnode == NULL) {
frnode = pool_get(&pf_frnode_pl, PR_NOWAIT);
if (frnode == NULL) {
pf_flush_fragments();
frnode = pool_get(&pf_frnode_pl, PR_NOWAIT);
if (frnode == NULL) {
REASON_SET(reason, PFRES_MEMORY);
pool_put(&pf_frag_pl, frag);
goto drop_fragment;
}
}
*frnode = *key;
RB_INIT(&frnode->fn_tree);
frnode->fn_fragments = 0;
frnode->fn_gen = 0;
}
memset(frag->fr_firstoff, 0, sizeof(frag->fr_firstoff));
memset(frag->fr_entries, 0, sizeof(frag->fr_entries));
TAILQ_INIT(&frag->fr_queue);
frag->fr_id = id;
frag->fr_timeout = getuptime();
frag->fr_gen = frnode->fn_gen++;
frag->fr_maxlen = frent->fe_len;
frag->fr_holes = 1;
frag->fr_node = frnode;
/* RB_INSERT cannot fail as pf_find_fragment() found nothing */
RB_INSERT(pf_frag_tree, &frnode->fn_tree, frag);
frnode->fn_fragments++;
if (frnode->fn_fragments == 1)
RB_INSERT(pf_frnode_tree, &pf_frnode_tree, frnode);
TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next);
/* We do not have a previous fragment, cannot fail. */
pf_frent_insert(frag, frent, NULL);
return (frag);
}
KASSERT(!TAILQ_EMPTY(&frag->fr_queue));
KASSERT(frag->fr_node);
/* Remember maximum fragment len for refragmentation */
if (frent->fe_len > frag->fr_maxlen)
frag->fr_maxlen = frent->fe_len;
/* Maximum data we have seen already */
total = TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_off +
TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_len;
/* Non terminal fragments must have more fragments flag */
if (frent->fe_off + frent->fe_len < total && !frent->fe_mff)
goto free_ipv6_fragment;
/* Check if we saw the last fragment already */
if (!TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_mff) {
if (frent->fe_off + frent->fe_len > total ||
(frent->fe_off + frent->fe_len == total && frent->fe_mff))
goto free_ipv6_fragment;
} else {
if (frent->fe_off + frent->fe_len == total && !frent->fe_mff)
goto free_ipv6_fragment;
}
/* Find neighbors for newly inserted fragment */
prev = pf_frent_previous(frag, frent);
if (prev == NULL) {
after = TAILQ_FIRST(&frag->fr_queue);
KASSERT(after != NULL);
} else {
after = TAILQ_NEXT(prev, fr_next);
}
if (prev != NULL && prev->fe_off + prev->fe_len > frent->fe_off) {
u_int16_t precut;
#ifdef INET6
if (frag->fr_node->fn_af == AF_INET6)
goto free_ipv6_fragment;
#endif /* INET6 */
precut = prev->fe_off + prev->fe_len - frent->fe_off;
if (precut >= frent->fe_len) {
DPFPRINTF(LOG_NOTICE, "new frag overlapped");
goto drop_fragment;
}
DPFPRINTF(LOG_NOTICE, "frag head overlap %d", precut);
m_adj(frent->fe_m, precut);
frent->fe_off += precut;
frent->fe_len -= precut;
}
for (; after != NULL && frent->fe_off + frent->fe_len > after->fe_off;
after = next) {
u_int16_t aftercut;
#ifdef INET6
if (frag->fr_node->fn_af == AF_INET6)
goto free_ipv6_fragment;
#endif /* INET6 */
aftercut = frent->fe_off + frent->fe_len - after->fe_off;
if (aftercut < after->fe_len) {
int old_index, new_index;
DPFPRINTF(LOG_NOTICE, "frag tail overlap %d", aftercut);
m_adj(after->fe_m, aftercut);
old_index = pf_frent_index(after);
after->fe_off += aftercut;
after->fe_len -= aftercut;
new_index = pf_frent_index(after);
if (old_index != new_index) {
DPFPRINTF(LOG_DEBUG, "frag index %d, new %d",
old_index, new_index);
/* Fragment switched queue as fe_off changed */
after->fe_off -= aftercut;
after->fe_len += aftercut;
/* Remove restored fragment from old queue */
pf_frent_remove(frag, after);
after->fe_off += aftercut;
after->fe_len -= aftercut;
/* Insert into correct queue */
if (pf_frent_insert(frag, after, prev)) {
DPFPRINTF(LOG_WARNING,
"fragment requeue limit exceeded");
m_freem(after->fe_m);
pool_put(&pf_frent_pl, after);
pf_nfrents--;
/* There is not way to recover */
goto free_fragment;
}
}
break;
}
/* This fragment is completely overlapped, lose it */
DPFPRINTF(LOG_NOTICE, "old frag overlapped");
next = TAILQ_NEXT(after, fr_next);
pf_frent_remove(frag, after);
m_freem(after->fe_m);
pool_put(&pf_frent_pl, after);
pf_nfrents--;
}
/* If part of the queue gets too long, there is not way to recover. */
if (pf_frent_insert(frag, frent, prev)) {
DPFPRINTF(LOG_WARNING, "fragment queue limit exceeded");
goto free_fragment;
}
return (frag);
free_ipv6_fragment:
if (frag->fr_node->fn_af == AF_INET)
goto bad_fragment;
/*
* RFC 5722, Errata 3089: When reassembling an IPv6 datagram, if one
* or more its constituent fragments is determined to be an overlapping
* fragment, the entire datagram (and any constituent fragments) MUST
* be silently discarded.
*/
DPFPRINTF(LOG_NOTICE, "flush overlapping fragments");
free_fragment:
pf_free_fragment(frag);
bad_fragment:
REASON_SET(reason, PFRES_FRAG);
drop_fragment:
pool_put(&pf_frent_pl, frent);
pf_nfrents--;
return (NULL);
}
struct mbuf *
pf_join_fragment(struct pf_fragment *frag)
{
struct mbuf *m, *m2;
struct pf_frent *frent;
frent = TAILQ_FIRST(&frag->fr_queue);
TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
m = frent->fe_m;
/* Strip off any trailing bytes */
if ((frent->fe_hdrlen + frent->fe_len) < m->m_pkthdr.len)
m_adj(m, (frent->fe_hdrlen + frent->fe_len) - m->m_pkthdr.len);
/* Magic from ip_input */
m2 = m->m_next;
m->m_next = NULL;
m_cat(m, m2);
pool_put(&pf_frent_pl, frent);
pf_nfrents--;
while ((frent = TAILQ_FIRST(&frag->fr_queue)) != NULL) {
TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
m2 = frent->fe_m;
/* Strip off ip header */
m_adj(m2, frent->fe_hdrlen);
/* Strip off any trailing bytes */
if (frent->fe_len < m2->m_pkthdr.len)
m_adj(m2, frent->fe_len - m2->m_pkthdr.len);
pool_put(&pf_frent_pl, frent);
pf_nfrents--;
m_removehdr(m2);
m_cat(m, m2);
}
/* Remove from fragment queue */
pf_free_fragment(frag);
return (m);
}
int
pf_reassemble(struct mbuf **m0, int dir, u_short *reason)
{
struct mbuf *m = *m0;
struct ip *ip = mtod(m, struct ip *);
struct pf_frent *frent;
struct pf_fragment *frag;
struct pf_frnode key;
u_int16_t total, hdrlen;
/* Get an entry for the fragment queue */
if ((frent = pf_create_fragment(reason)) == NULL)
return (PF_DROP);
frent->fe_m = m;
frent->fe_hdrlen = ip->ip_hl << 2;
frent->fe_extoff = 0;
frent->fe_len = ntohs(ip->ip_len) - (ip->ip_hl << 2);
frent->fe_off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3;
frent->fe_mff = ntohs(ip->ip_off) & IP_MF;
key.fn_src.v4 = ip->ip_src;
key.fn_dst.v4 = ip->ip_dst;
key.fn_af = AF_INET;
key.fn_proto = ip->ip_p;
key.fn_direction = dir;
if ((frag = pf_fillup_fragment(&key, ip->ip_id, frent, reason))
== NULL)
return (PF_DROP);
/* The mbuf is part of the fragment entry, no direct free or access */
m = *m0 = NULL;
if (frag->fr_holes) {
DPFPRINTF(LOG_DEBUG, "frag %d, holes %d",
frag->fr_id, frag->fr_holes);
return (PF_PASS); /* drop because *m0 is NULL, no error */
}
/* We have all the data */
frent = TAILQ_FIRST(&frag->fr_queue);
KASSERT(frent != NULL);
total = TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_off +
TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_len;
hdrlen = frent->fe_hdrlen;
m = *m0 = pf_join_fragment(frag);
frag = NULL;
m_calchdrlen(m);
ip = mtod(m, struct ip *);
ip->ip_len = htons(hdrlen + total);
ip->ip_off &= ~(IP_MF|IP_OFFMASK);
if (hdrlen + total > IP_MAXPACKET) {
DPFPRINTF(LOG_NOTICE, "drop: too big: %d", total);
ip->ip_len = 0;
REASON_SET(reason, PFRES_SHORT);
/* PF_DROP requires a valid mbuf *m0 in pf_test() */
return (PF_DROP);
}
DPFPRINTF(LOG_INFO, "complete: %p(%d)", m, ntohs(ip->ip_len));
return (PF_PASS);
}
#ifdef INET6
int
pf_reassemble6(struct mbuf **m0, struct ip6_frag *fraghdr,
u_int16_t hdrlen, u_int16_t extoff, int dir, u_short *reason)
{
struct mbuf *m = *m0;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct m_tag *mtag;
struct pf_fragment_tag *ftag;
struct pf_frent *frent;
struct pf_fragment *frag;
struct pf_frnode key;
int off;
u_int16_t total, maxlen;
u_int8_t proto;
/* Get an entry for the fragment queue */
if ((frent = pf_create_fragment(reason)) == NULL)
return (PF_DROP);
frent->fe_m = m;
frent->fe_hdrlen = hdrlen;
frent->fe_extoff = extoff;
frent->fe_len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - hdrlen;
frent->fe_off = ntohs(fraghdr->ip6f_offlg & IP6F_OFF_MASK);
frent->fe_mff = fraghdr->ip6f_offlg & IP6F_MORE_FRAG;
key.fn_src.v6 = ip6->ip6_src;
key.fn_dst.v6 = ip6->ip6_dst;
key.fn_af = AF_INET6;
/* Only the first fragment's protocol is relevant */
key.fn_proto = 0;
key.fn_direction = dir;
if ((frag = pf_fillup_fragment(&key, fraghdr->ip6f_ident, frent,
reason)) == NULL)
return (PF_DROP);
/* The mbuf is part of the fragment entry, no direct free or access */
m = *m0 = NULL;
if (frag->fr_holes) {
DPFPRINTF(LOG_DEBUG, "frag %#08x, holes %d",
frag->fr_id, frag->fr_holes);
return (PF_PASS); /* drop because *m0 is NULL, no error */
}
/* We have all the data */
frent = TAILQ_FIRST(&frag->fr_queue);
KASSERT(frent != NULL);
extoff = frent->fe_extoff;
maxlen = frag->fr_maxlen;
total = TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_off +
TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_len;
hdrlen = frent->fe_hdrlen - sizeof(struct ip6_frag);
m = *m0 = pf_join_fragment(frag);
frag = NULL;
/* Take protocol from first fragment header */
if ((m = m_getptr(m, hdrlen + offsetof(struct ip6_frag, ip6f_nxt),
&off)) == NULL)
panic("%s: short frag mbuf chain", __func__);
proto = *(mtod(m, caddr_t) + off);
m = *m0;
/* Delete frag6 header */
if (frag6_deletefraghdr(m, hdrlen) != 0)
goto fail;
m_calchdrlen(m);
if ((mtag = m_tag_get(PACKET_TAG_PF_REASSEMBLED, sizeof(struct
pf_fragment_tag), M_NOWAIT)) == NULL)
goto fail;
ftag = (struct pf_fragment_tag *)(mtag + 1);
ftag->ft_hdrlen = hdrlen;
ftag->ft_extoff = extoff;
ftag->ft_maxlen = maxlen;
m_tag_prepend(m, mtag);
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(hdrlen - sizeof(struct ip6_hdr) + total);
if (extoff) {
/* Write protocol into next field of last extension header */
if ((m = m_getptr(m, extoff + offsetof(struct ip6_ext,
ip6e_nxt), &off)) == NULL)
panic("%s: short ext mbuf chain", __func__);
*(mtod(m, caddr_t) + off) = proto;
m = *m0;
} else
ip6->ip6_nxt = proto;
if (hdrlen - sizeof(struct ip6_hdr) + total > IPV6_MAXPACKET) {
DPFPRINTF(LOG_NOTICE, "drop: too big: %d", total);
ip6->ip6_plen = 0;
REASON_SET(reason, PFRES_SHORT);
/* PF_DROP requires a valid mbuf *m0 in pf_test6() */
return (PF_DROP);
}
DPFPRINTF(LOG_INFO, "complete: %p(%d)", m, ntohs(ip6->ip6_plen));
return (PF_PASS);
fail:
REASON_SET(reason, PFRES_MEMORY);
/* PF_DROP requires a valid mbuf *m0 in pf_test6(), will free later */
return (PF_DROP);
}
int
pf_refragment6(struct mbuf **m0, struct m_tag *mtag, struct sockaddr_in6 *dst,
struct ifnet *ifp, struct rtentry *rt)
{
struct mbuf *m = *m0;
struct mbuf_list fml;
struct pf_fragment_tag *ftag = (struct pf_fragment_tag *)(mtag + 1);
u_int32_t mtu;
u_int16_t hdrlen, extoff, maxlen;
u_int8_t proto;
int error;
hdrlen = ftag->ft_hdrlen;
extoff = ftag->ft_extoff;
maxlen = ftag->ft_maxlen;
m_tag_delete(m, mtag);
mtag = NULL;
ftag = NULL;
/* Checksum must be calculated for the whole packet */
in6_proto_cksum_out(m, NULL);
if (extoff) {
int off;
/* Use protocol from next field of last extension header */
if ((m = m_getptr(m, extoff + offsetof(struct ip6_ext,
ip6e_nxt), &off)) == NULL)
panic("%s: short ext mbuf chain", __func__);
proto = *(mtod(m, caddr_t) + off);
*(mtod(m, caddr_t) + off) = IPPROTO_FRAGMENT;
m = *m0;
} else {
struct ip6_hdr *hdr;
hdr = mtod(m, struct ip6_hdr *);
proto = hdr->ip6_nxt;
hdr->ip6_nxt = IPPROTO_FRAGMENT;
}
/*
* Maxlen may be less than 8 iff there was only a single
* fragment. As it was fragmented before, add a fragment
* header also for a single fragment. If total or maxlen
* is less than 8, ip6_fragment() will return EMSGSIZE and
* we drop the packet.
*/
mtu = hdrlen + sizeof(struct ip6_frag) + maxlen;
error = ip6_fragment(m, &fml, hdrlen, proto, mtu);
*m0 = NULL; /* ip6_fragment() has consumed original packet. */
if (error) {
DPFPRINTF(LOG_NOTICE, "refragment error %d", error);
return (PF_DROP);
}
while ((m = ml_dequeue(&fml)) != NULL) {
m->m_pkthdr.pf.flags |= PF_TAG_REFRAGMENTED;
if (ifp == NULL) {
ip6_forward(m, NULL, 0);
} else if ((u_long)m->m_pkthdr.len <= ifp->if_mtu) {
ifp->if_output(ifp, m, sin6tosa(dst), rt);
} else {
icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu);
}
}
return (PF_PASS);
}
#endif /* INET6 */
int
pf_normalize_ip(struct pf_pdesc *pd, u_short *reason)
{
struct ip *h = mtod(pd->m, struct ip *);
u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3;
u_int16_t mff = (ntohs(h->ip_off) & IP_MF);
if (!fragoff && !mff)
goto no_fragment;
/* Clear IP_DF if we're in no-df mode */
if (pf_status.reass & PF_REASS_NODF && h->ip_off & htons(IP_DF))
h->ip_off &= htons(~IP_DF);
/* We're dealing with a fragment now. Don't allow fragments
* with IP_DF to enter the cache. If the flag was cleared by
* no-df above, fine. Otherwise drop it.
*/
if (h->ip_off & htons(IP_DF)) {
DPFPRINTF(LOG_NOTICE, "bad fragment: IP_DF");
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
if (!pf_status.reass)
return (PF_PASS); /* no reassembly */
/* Returns PF_DROP or m is NULL or completely reassembled mbuf */
PF_FRAG_LOCK();
if (pf_reassemble(&pd->m, pd->dir, reason) != PF_PASS) {
PF_FRAG_UNLOCK();
return (PF_DROP);
}
PF_FRAG_UNLOCK();
if (pd->m == NULL)
return (PF_PASS); /* packet has been reassembled, no error */
h = mtod(pd->m, struct ip *);
no_fragment:
/* At this point, only IP_DF is allowed in ip_off */
if (h->ip_off & ~htons(IP_DF))
h->ip_off &= htons(IP_DF);
return (PF_PASS);
}
#ifdef INET6
int
pf_normalize_ip6(struct pf_pdesc *pd, u_short *reason)
{
struct ip6_frag frag;
if (pd->fragoff == 0)
goto no_fragment;
if (!pf_pull_hdr(pd->m, pd->fragoff, &frag, sizeof(frag), NULL, reason,
AF_INET6))
return (PF_DROP);
if (!pf_status.reass)
return (PF_PASS); /* no reassembly */
/* Returns PF_DROP or m is NULL or completely reassembled mbuf */
PF_FRAG_LOCK();
if (pf_reassemble6(&pd->m, &frag, pd->fragoff + sizeof(frag),
pd->extoff, pd->dir, reason) != PF_PASS) {
PF_FRAG_UNLOCK();
return (PF_DROP);
}
PF_FRAG_UNLOCK();
if (pd->m == NULL)
return (PF_PASS); /* packet has been reassembled, no error */
no_fragment:
return (PF_PASS);
}
#endif /* INET6 */
int
pf_normalize_tcp(struct pf_pdesc *pd)
{
struct tcphdr *th = &pd->hdr.tcp;
u_short reason;
u_int8_t flags;
u_int rewrite = 0;
flags = th->th_flags;
if (flags & TH_SYN) {
/* Illegal packet */
if (flags & TH_RST)
goto tcp_drop;
if (flags & TH_FIN) /* XXX why clear instead of drop? */
flags &= ~TH_FIN;
} else {
/* Illegal packet */
if (!(flags & (TH_ACK|TH_RST)))
goto tcp_drop;
}
if (!(flags & TH_ACK)) {
/* These flags are only valid if ACK is set */
if (flags & (TH_FIN|TH_PUSH|TH_URG))
goto tcp_drop;
}
/* If flags changed, or reserved data set, then adjust */
if (flags != th->th_flags || th->th_x2 != 0) {
/* hack: set 4-bit th_x2 = 0 */
u_int8_t *th_off = (u_int8_t*)(&th->th_ack+1);
pf_patch_8(pd, th_off, th->th_off << 4, PF_HI);
pf_patch_8(pd, &th->th_flags, flags, PF_LO);
rewrite = 1;
}
/* Remove urgent pointer, if TH_URG is not set */
if (!(flags & TH_URG) && th->th_urp) {
pf_patch_16(pd, &th->th_urp, 0);
rewrite = 1;
}
/* copy back packet headers if we sanitized */
if (rewrite) {
m_copyback(pd->m, pd->off, sizeof(*th), th, M_NOWAIT);
}
return (PF_PASS);
tcp_drop:
REASON_SET(&reason, PFRES_NORM);
return (PF_DROP);
}
int
pf_normalize_tcp_init(struct pf_pdesc *pd, struct pf_state_peer *src)
{
struct tcphdr *th = &pd->hdr.tcp;
u_int32_t tsval, tsecr;
int olen;
u_int8_t opts[MAX_TCPOPTLEN], *opt;
KASSERT(src->scrub == NULL);
src->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT);
if (src->scrub == NULL)
return (1);
memset(src->scrub, 0, sizeof(*src->scrub));
switch (pd->af) {
case AF_INET: {
struct ip *h = mtod(pd->m, struct ip *);
src->scrub->pfss_ttl = h->ip_ttl;
break;
}
#ifdef INET6
case AF_INET6: {
struct ip6_hdr *h = mtod(pd->m, struct ip6_hdr *);
src->scrub->pfss_ttl = h->ip6_hlim;
break;
}
#endif /* INET6 */
default:
unhandled_af(pd->af);
}
/*
* All normalizations below are only begun if we see the start of
* the connections. They must all set an enabled bit in pfss_flags
*/
if ((th->th_flags & TH_SYN) == 0)
return (0);
olen = (th->th_off << 2) - sizeof(*th);
if (olen < TCPOLEN_TIMESTAMP || !pf_pull_hdr(pd->m,
pd->off + sizeof(*th), opts, olen, NULL, NULL, pd->af))
return (0);
opt = opts;
while ((opt = pf_find_tcpopt(opt, opts, olen,
TCPOPT_TIMESTAMP, TCPOLEN_TIMESTAMP)) != NULL) {
src->scrub->pfss_flags |= PFSS_TIMESTAMP;
src->scrub->pfss_ts_mod = arc4random();
/* note PFSS_PAWS not set yet */
memcpy(&tsval, &opt[2], sizeof(u_int32_t));
memcpy(&tsecr, &opt[6], sizeof(u_int32_t));
src->scrub->pfss_tsval0 = ntohl(tsval);
src->scrub->pfss_tsval = ntohl(tsval);
src->scrub->pfss_tsecr = ntohl(tsecr);
getmicrouptime(&src->scrub->pfss_last);
opt += opt[1];
}
return (0);
}
void
pf_normalize_tcp_cleanup(struct pf_state *state)
{
if (state->src.scrub)
pool_put(&pf_state_scrub_pl, state->src.scrub);
if (state->dst.scrub)
pool_put(&pf_state_scrub_pl, state->dst.scrub);
/* Someday... flush the TCP segment reassembly descriptors. */
}
int
pf_normalize_tcp_stateful(struct pf_pdesc *pd, u_short *reason,
struct pf_state *state, struct pf_state_peer *src,
struct pf_state_peer *dst, int *writeback)
{
struct tcphdr *th = &pd->hdr.tcp;
struct timeval uptime;
u_int tsval_from_last;
u_int32_t tsval, tsecr;
int copyback = 0;
int got_ts = 0;
int olen;
u_int8_t opts[MAX_TCPOPTLEN], *opt;
KASSERT(src->scrub || dst->scrub);
/*
* Enforce the minimum TTL seen for this connection. Negate a common
* technique to evade an intrusion detection system and confuse
* firewall state code.
*/
switch (pd->af) {
case AF_INET:
if (src->scrub) {
struct ip *h = mtod(pd->m, struct ip *);
if (h->ip_ttl > src->scrub->pfss_ttl)
src->scrub->pfss_ttl = h->ip_ttl;
h->ip_ttl = src->scrub->pfss_ttl;
}
break;
#ifdef INET6
case AF_INET6:
if (src->scrub) {
struct ip6_hdr *h = mtod(pd->m, struct ip6_hdr *);
if (h->ip6_hlim > src->scrub->pfss_ttl)
src->scrub->pfss_ttl = h->ip6_hlim;
h->ip6_hlim = src->scrub->pfss_ttl;
}
break;
#endif /* INET6 */
default:
unhandled_af(pd->af);
}
olen = (th->th_off << 2) - sizeof(*th);
if (olen >= TCPOLEN_TIMESTAMP &&
((src->scrub && (src->scrub->pfss_flags & PFSS_TIMESTAMP)) ||
(dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP))) &&
pf_pull_hdr(pd->m, pd->off + sizeof(*th), opts, olen, NULL, NULL,
pd->af)) {
/* Modulate the timestamps. Can be used for NAT detection, OS
* uptime determination or reboot detection.
*/
opt = opts;
while ((opt = pf_find_tcpopt(opt, opts, olen,
TCPOPT_TIMESTAMP, TCPOLEN_TIMESTAMP)) != NULL) {
u_int8_t *ts = opt + 2;
u_int8_t *tsr = opt + 6;
if (got_ts) {
/* Huh? Multiple timestamps!? */
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: %s: multiple TS??", __func__);
pf_print_state(state);
addlog("\n");
}
REASON_SET(reason, PFRES_TS);
return (PF_DROP);
}
memcpy(&tsval, ts, sizeof(u_int32_t));
memcpy(&tsecr, tsr, sizeof(u_int32_t));
/* modulate TS */
if (tsval && src->scrub &&
(src->scrub->pfss_flags & PFSS_TIMESTAMP)) {
/* tsval used further on */
tsval = ntohl(tsval);
pf_patch_32_unaligned(pd,
ts, htonl(tsval + src->scrub->pfss_ts_mod),
PF_ALGNMNT(ts - opts));
copyback = 1;
}
/* modulate TS reply if any (!0) */
if (tsecr && dst->scrub &&
(dst->scrub->pfss_flags & PFSS_TIMESTAMP)) {
/* tsecr used further on */
tsecr = ntohl(tsecr) - dst->scrub->pfss_ts_mod;
pf_patch_32_unaligned(pd,
tsr, htonl(tsecr), PF_ALGNMNT(tsr - opts));
copyback = 1;
}
got_ts = 1;
opt += opt[1];
}
if (copyback) {
/* Copyback the options, caller copies back header */
*writeback = 1;
m_copyback(pd->m, pd->off + sizeof(*th), olen, opts, M_NOWAIT);
}
}
/*
* Must invalidate PAWS checks on connections idle for too long.
* The fastest allowed timestamp clock is 1ms. That turns out to
* be about 24 days before it wraps. XXX Right now our lowerbound
* TS echo check only works for the first 12 days of a connection
* when the TS has exhausted half its 32bit space
*/
#define TS_MAX_IDLE (24*24*60*60)
#define TS_MAX_CONN (12*24*60*60) /* XXX remove when better tsecr check */
getmicrouptime(&uptime);
if (src->scrub && (src->scrub->pfss_flags & PFSS_PAWS) &&
(uptime.tv_sec - src->scrub->pfss_last.tv_sec > TS_MAX_IDLE ||
getuptime() - state->creation > TS_MAX_CONN)) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: src idled out of PAWS ");
pf_print_state(state);
addlog("\n");
}
src->scrub->pfss_flags =
(src->scrub->pfss_flags & ~PFSS_PAWS) | PFSS_PAWS_IDLED;
}
if (dst->scrub && (dst->scrub->pfss_flags & PFSS_PAWS) &&
uptime.tv_sec - dst->scrub->pfss_last.tv_sec > TS_MAX_IDLE) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: dst idled out of PAWS ");
pf_print_state(state);
addlog("\n");
}
dst->scrub->pfss_flags =
(dst->scrub->pfss_flags & ~PFSS_PAWS) | PFSS_PAWS_IDLED;
}
if (got_ts && src->scrub && dst->scrub &&
(src->scrub->pfss_flags & PFSS_PAWS) &&
(dst->scrub->pfss_flags & PFSS_PAWS)) {
/* Validate that the timestamps are "in-window".
* RFC1323 describes TCP Timestamp options that allow
* measurement of RTT (round trip time) and PAWS
* (protection against wrapped sequence numbers). PAWS
* gives us a set of rules for rejecting packets on
* long fat pipes (packets that were somehow delayed
* in transit longer than the time it took to send the
* full TCP sequence space of 4Gb). We can use these
* rules and infer a few others that will let us treat
* the 32bit timestamp and the 32bit echoed timestamp
* as sequence numbers to prevent a blind attacker from
* inserting packets into a connection.
*
* RFC1323 tells us:
* - The timestamp on this packet must be greater than
* or equal to the last value echoed by the other
* endpoint. The RFC says those will be discarded
* since it is a dup that has already been acked.
* This gives us a lowerbound on the timestamp.
* timestamp >= other last echoed timestamp
* - The timestamp will be less than or equal to
* the last timestamp plus the time between the
* last packet and now. The RFC defines the max
* clock rate as 1ms. We will allow clocks to be
* up to 10% fast and will allow a total difference
* or 30 seconds due to a route change. And this
* gives us an upperbound on the timestamp.
* timestamp <= last timestamp + max ticks
* We have to be careful here. Windows will send an
* initial timestamp of zero and then initialize it
* to a random value after the 3whs; presumably to
* avoid a DoS by having to call an expensive RNG
* during a SYN flood. Proof MS has at least one
* good security geek.
*
* - The TCP timestamp option must also echo the other
* endpoints timestamp. The timestamp echoed is the
* one carried on the earliest unacknowledged segment
* on the left edge of the sequence window. The RFC
* states that the host will reject any echoed
* timestamps that were larger than any ever sent.
* This gives us an upperbound on the TS echo.
* tescr <= largest_tsval
* - The lowerbound on the TS echo is a little more
* tricky to determine. The other endpoint's echoed
* values will not decrease. But there may be
* network conditions that re-order packets and
* cause our view of them to decrease. For now the
* only lowerbound we can safely determine is that
* the TS echo will never be less than the original
* TS. XXX There is probably a better lowerbound.
* Remove TS_MAX_CONN with better lowerbound check.
* tescr >= other original TS
*
* It is also important to note that the fastest
* timestamp clock of 1ms will wrap its 32bit space in
* 24 days. So we just disable TS checking after 24
* days of idle time. We actually must use a 12d
* connection limit until we can come up with a better
* lowerbound to the TS echo check.
*/
struct timeval delta_ts;
int ts_fudge;
/*
* PFTM_TS_DIFF is how many seconds of leeway to allow
* a host's timestamp. This can happen if the previous
* packet got delayed in transit for much longer than
* this packet.
*/
if ((ts_fudge = state->rule.ptr->timeout[PFTM_TS_DIFF]) == 0)
ts_fudge = pf_default_rule.timeout[PFTM_TS_DIFF];
/* Calculate max ticks since the last timestamp */
#define TS_MAXFREQ 1100 /* RFC max TS freq of 1Khz + 10% skew */
#define TS_MICROSECS 1000000 /* microseconds per second */
timersub(&uptime, &src->scrub->pfss_last, &delta_ts);
tsval_from_last = (delta_ts.tv_sec + ts_fudge) * TS_MAXFREQ;
tsval_from_last += delta_ts.tv_usec / (TS_MICROSECS/TS_MAXFREQ);
if ((src->state >= TCPS_ESTABLISHED &&
dst->state >= TCPS_ESTABLISHED) &&
(SEQ_LT(tsval, dst->scrub->pfss_tsecr) ||
SEQ_GT(tsval, src->scrub->pfss_tsval + tsval_from_last) ||
(tsecr && (SEQ_GT(tsecr, dst->scrub->pfss_tsval) ||
SEQ_LT(tsecr, dst->scrub->pfss_tsval0))))) {
/* Bad RFC1323 implementation or an insertion attack.
*
* - Solaris 2.6 and 2.7 are known to send another ACK
* after the FIN,FIN|ACK,ACK closing that carries
* an old timestamp.
*/
DPFPRINTF(LOG_NOTICE, "Timestamp failed %c%c%c%c",
SEQ_LT(tsval, dst->scrub->pfss_tsecr) ? '0' : ' ',
SEQ_GT(tsval, src->scrub->pfss_tsval +
tsval_from_last) ? '1' : ' ',
SEQ_GT(tsecr, dst->scrub->pfss_tsval) ? '2' : ' ',
SEQ_LT(tsecr, dst->scrub->pfss_tsval0)? '3' : ' ');
DPFPRINTF(LOG_NOTICE, " tsval: %u tsecr: %u "
"+ticks: %u idle: %llu.%06lus", tsval, tsecr,
tsval_from_last, (long long)delta_ts.tv_sec,
delta_ts.tv_usec);
DPFPRINTF(LOG_NOTICE, " src->tsval: %u tsecr: %u",
src->scrub->pfss_tsval, src->scrub->pfss_tsecr);
DPFPRINTF(LOG_NOTICE, " dst->tsval: %u tsecr: %u "
"tsval0: %u", dst->scrub->pfss_tsval,
dst->scrub->pfss_tsecr, dst->scrub->pfss_tsval0);
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: ");
pf_print_state(state);
pf_print_flags(th->th_flags);
addlog("\n");
}
REASON_SET(reason, PFRES_TS);
return (PF_DROP);
}
/* XXX I'd really like to require tsecr but it's optional */
} else if (!got_ts && (th->th_flags & TH_RST) == 0 &&
((src->state == TCPS_ESTABLISHED && dst->state == TCPS_ESTABLISHED)
|| pd->p_len > 0 || (th->th_flags & TH_SYN)) &&
src->scrub && dst->scrub &&
(src->scrub->pfss_flags & PFSS_PAWS) &&
(dst->scrub->pfss_flags & PFSS_PAWS)) {
/* Didn't send a timestamp. Timestamps aren't really useful
* when:
* - connection opening or closing (often not even sent).
* but we must not let an attacker to put a FIN on a
* data packet to sneak it through our ESTABLISHED check.
* - on a TCP reset. RFC suggests not even looking at TS.
* - on an empty ACK. The TS will not be echoed so it will
* probably not help keep the RTT calculation in sync and
* there isn't as much danger when the sequence numbers
* got wrapped. So some stacks don't include TS on empty
* ACKs :-(
*
* To minimize the disruption to mostly RFC1323 conformant
* stacks, we will only require timestamps on data packets.
*
* And what do ya know, we cannot require timestamps on data
* packets. There appear to be devices that do legitimate
* TCP connection hijacking. There are HTTP devices that allow
* a 3whs (with timestamps) and then buffer the HTTP request.
* If the intermediate device has the HTTP response cache, it
* will spoof the response but not bother timestamping its
* packets. So we can look for the presence of a timestamp in
* the first data packet and if there, require it in all future
* packets.
*/
if (pd->p_len > 0 && (src->scrub->pfss_flags & PFSS_DATA_TS)) {
/*
* Hey! Someone tried to sneak a packet in. Or the
* stack changed its RFC1323 behavior?!?!
*/
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: did not receive expected RFC1323 "
"timestamp");
pf_print_state(state);
pf_print_flags(th->th_flags);
addlog("\n");
}
REASON_SET(reason, PFRES_TS);
return (PF_DROP);
}
}
/*
* We will note if a host sends his data packets with or without
* timestamps. And require all data packets to contain a timestamp
* if the first does. PAWS implicitly requires that all data packets be
* timestamped. But I think there are middle-man devices that hijack
* TCP streams immediately after the 3whs and don't timestamp their
* packets (seen in a WWW accelerator or cache).
*/
if (pd->p_len > 0 && src->scrub && (src->scrub->pfss_flags &
(PFSS_TIMESTAMP|PFSS_DATA_TS|PFSS_DATA_NOTS)) == PFSS_TIMESTAMP) {
if (got_ts)
src->scrub->pfss_flags |= PFSS_DATA_TS;
else {
src->scrub->pfss_flags |= PFSS_DATA_NOTS;
if (pf_status.debug >= LOG_NOTICE && dst->scrub &&
(dst->scrub->pfss_flags & PFSS_TIMESTAMP)) {
/* Don't warn if other host rejected RFC1323 */
log(LOG_NOTICE,
"pf: broken RFC1323 stack did not "
"timestamp data packet. Disabled PAWS "
"security.");
pf_print_state(state);
pf_print_flags(th->th_flags);
addlog("\n");
}
}
}
/*
* Update PAWS values
*/
if (got_ts && src->scrub && PFSS_TIMESTAMP == (src->scrub->pfss_flags &
(PFSS_PAWS_IDLED|PFSS_TIMESTAMP))) {
getmicrouptime(&src->scrub->pfss_last);
if (SEQ_GEQ(tsval, src->scrub->pfss_tsval) ||
(src->scrub->pfss_flags & PFSS_PAWS) == 0)
src->scrub->pfss_tsval = tsval;
if (tsecr) {
if (SEQ_GEQ(tsecr, src->scrub->pfss_tsecr) ||
(src->scrub->pfss_flags & PFSS_PAWS) == 0)
src->scrub->pfss_tsecr = tsecr;
if ((src->scrub->pfss_flags & PFSS_PAWS) == 0 &&
(SEQ_LT(tsval, src->scrub->pfss_tsval0) ||
src->scrub->pfss_tsval0 == 0)) {
/* tsval0 MUST be the lowest timestamp */
src->scrub->pfss_tsval0 = tsval;
}
/* Only fully initialized after a TS gets echoed */
if ((src->scrub->pfss_flags & PFSS_PAWS) == 0)
src->scrub->pfss_flags |= PFSS_PAWS;
}
}
/* I have a dream.... TCP segment reassembly.... */
return (0);
}
int
pf_normalize_mss(struct pf_pdesc *pd, u_int16_t maxmss)
{
int olen, optsoff;
u_int8_t opts[MAX_TCPOPTLEN], *opt;
olen = (pd->hdr.tcp.th_off << 2) - sizeof(struct tcphdr);
optsoff = pd->off + sizeof(struct tcphdr);
if (olen < TCPOLEN_MAXSEG ||
!pf_pull_hdr(pd->m, optsoff, opts, olen, NULL, NULL, pd->af))
return (0);
opt = opts;
while ((opt = pf_find_tcpopt(opt, opts, olen,
TCPOPT_MAXSEG, TCPOLEN_MAXSEG)) != NULL) {
u_int16_t mss;
u_int8_t *mssp = opt + 2;
memcpy(&mss, mssp, sizeof(mss));
if (ntohs(mss) > maxmss) {
size_t mssoffopts = mssp - opts;
pf_patch_16_unaligned(pd, &mss,
htons(maxmss), PF_ALGNMNT(mssoffopts));
m_copyback(pd->m, optsoff + mssoffopts,
sizeof(mss), &mss, M_NOWAIT);
m_copyback(pd->m, pd->off,
sizeof(struct tcphdr), &pd->hdr.tcp, M_NOWAIT);
}
opt += opt[1];
}
return (0);
}
void
pf_scrub(struct mbuf *m, u_int16_t flags, sa_family_t af, u_int8_t min_ttl,
u_int8_t tos)
{
struct ip *h = mtod(m, struct ip *);
#ifdef INET6
struct ip6_hdr *h6 = mtod(m, struct ip6_hdr *);
#endif /* INET6 */
/* Clear IP_DF if no-df was requested */
if (flags & PFSTATE_NODF && af == AF_INET && h->ip_off & htons(IP_DF))
h->ip_off &= htons(~IP_DF);
/* Enforce a minimum ttl, may cause endless packet loops */
if (min_ttl && af == AF_INET && h->ip_ttl < min_ttl)
h->ip_ttl = min_ttl;
#ifdef INET6
if (min_ttl && af == AF_INET6 && h6->ip6_hlim < min_ttl)
h6->ip6_hlim = min_ttl;
#endif /* INET6 */
/* Enforce tos */
if (flags & PFSTATE_SETTOS) {
if (af == AF_INET)
h->ip_tos = tos | (h->ip_tos & IPTOS_ECN_MASK);
#ifdef INET6
if (af == AF_INET6) {
/* drugs are unable to explain such idiocy */
h6->ip6_flow &= ~htonl(0x0fc00000);
h6->ip6_flow |= htonl(((u_int32_t)tos) << 20);
}
#endif /* INET6 */
}
/* random-id, but not for fragments */
if (flags & PFSTATE_RANDOMID && af == AF_INET &&
!(h->ip_off & ~htons(IP_DF)))
h->ip_id = htons(ip_randomid());
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* $OpenBSD: radio.c,v 1.13 2022/04/06 18:59:27 naddy Exp $ */
/* $RuOBSD: radio.c,v 1.7 2001/12/04 06:03:05 tm Exp $ */
/*
* Copyright (c) 2001 Maxim Tsyplakov <tm@oganer.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* This is /dev/radio driver for OpenBSD */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/device.h>
#include <sys/vnode.h>
#include <sys/radioio.h>
#include <sys/conf.h>
#include <dev/audio_if.h>
#include <dev/radio_if.h>
#include <dev/radiovar.h>
int radioprobe(struct device *, void *, void *);
void radioattach(struct device *, struct device *, void *);
int radiodetach(struct device *, int);
int radioactivate(struct device *, int);
int radioprint(void *, const char *);
const struct cfattach radio_ca = {
sizeof(struct radio_softc), radioprobe, radioattach,
radiodetach, radioactivate
};
struct cfdriver radio_cd = {
NULL, "radio", DV_DULL
};
int
radioprobe(struct device *parent, void *match, void *aux)
{
struct audio_attach_args *sa = aux;
return (sa->type == AUDIODEV_TYPE_RADIO) ? 1 : 0;
}
void
radioattach(struct device *parent, struct device *self, void *aux)
{
struct radio_softc *sc = (void *) self;
struct audio_attach_args *sa = aux;
printf("\n");
sc->hw_if = sa->hwif;
sc->hw_hdl = sa->hdl;
sc->sc_dev = parent;
}
int
radioopen(dev_t dev, int flags, int fmt, struct proc *p)
{
int unit;
struct radio_softc *sc;
unit = RADIOUNIT(dev);
if (unit >= radio_cd.cd_ndevs ||
(sc = radio_cd.cd_devs[unit]) == NULL ||
sc->hw_if == NULL)
return (ENXIO);
if (sc->hw_if->open != NULL)
return (sc->hw_if->open(sc->hw_hdl, flags, fmt, p));
else
return (0);
}
int
radioclose(dev_t dev, int flags, int fmt, struct proc *p)
{
struct radio_softc *sc;
sc = radio_cd.cd_devs[RADIOUNIT(dev)];
if (sc->hw_if->close != NULL)
return (sc->hw_if->close(sc->hw_hdl, flags, fmt, p));
else
return (0);
}
int
radioioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
struct radio_softc *sc;
int unit, error;
unit = RADIOUNIT(dev);
if (unit >= radio_cd.cd_ndevs ||
(sc = radio_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
return (ENXIO);
error = EOPNOTSUPP;
switch (cmd) {
case RIOCGINFO:
if (sc->hw_if->get_info)
error = (sc->hw_if->get_info)(sc->hw_hdl,
(struct radio_info *)data);
break;
case RIOCSINFO:
if (!(flags & FWRITE))
return (EACCES);
if (sc->hw_if->set_info)
error = (sc->hw_if->set_info)(sc->hw_hdl,
(struct radio_info *)data);
break;
case RIOCSSRCH:
if (!(flags & FWRITE))
return (EACCES);
if (sc->hw_if->search)
error = (sc->hw_if->search)(sc->hw_hdl,
*(int *)data);
break;
default:
error = (ENOTTY);
}
return (error);
}
/*
* Called from hardware driver. This is where the MI radio driver gets
* probed/attached to the hardware driver
*/
struct device *
radio_attach_mi(const struct radio_hw_if *rhwp, void *hdlp, struct device *dev)
{
struct audio_attach_args arg;
arg.type = AUDIODEV_TYPE_RADIO;
arg.hwif = rhwp;
arg.hdl = hdlp;
return (config_found(dev, &arg, radioprint));
}
int
radioprint(void *aux, const char *pnp)
{
if (pnp != NULL)
printf("radio at %s", pnp);
return (UNCONF);
}
int
radiodetach(struct device *self, int flags)
{
/*struct radio_softc *sc = (struct radio_softc *)self;*/
int maj, mn;
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == radioopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
return (0);
}
int
radioactivate(struct device *self, int act)
{
struct radio_softc *sc = (struct radio_softc *)self;
switch (act) {
case DVACT_DEACTIVATE:
sc->sc_dying = 1;
break;
}
return (0);
}
3
1
1
2
1
5
1
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* $OpenBSD: ksyms.c,v 1.34 2022/01/08 22:54:49 guenther Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <millert@openbsd.org>
* Copyright (c) 2001 Artur Grabowski <art@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/exec.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/exec_elf.h>
extern char *esym; /* end of symbol table */
#if defined(__sparc64__) || defined(__mips__) || defined(__amd64__) || \
defined(__i386__) || defined(__powerpc64__)
extern char *ssym; /* end of kernel */
#else
extern long end; /* end of kernel */
#endif
static caddr_t ksym_head;
static caddr_t ksym_syms;
static size_t ksym_head_size;
static size_t ksym_syms_size;
void ksymsattach(int);
void
ksymsattach(int num)
{
#if defined(__sparc64__) || defined(__mips__) || defined(__amd64__) || \
defined(__i386__) || defined(__powerpc64__)
if (esym <= ssym) {
printf("/dev/ksyms: Symbol table not valid.\n");
return;
}
#else
if (esym <= (char *)&end) {
printf("/dev/ksyms: Symbol table not valid.\n");
return;
}
#endif
do {
#if defined(__sparc64__) || defined(__mips__) || defined(__amd64__) || \
defined(__i386__) || defined(__powerpc64__)
caddr_t symtab = ssym;
#else
caddr_t symtab = (caddr_t)&end;
#endif
Elf_Ehdr *elf;
Elf_Shdr *shdr;
int i;
elf = (Elf_Ehdr *)symtab;
if (memcmp(elf->e_ident, ELFMAG, SELFMAG) != 0 ||
elf->e_ident[EI_CLASS] != ELFCLASS ||
elf->e_machine != ELF_TARG_MACH)
break;
shdr = (Elf_Shdr *)&symtab[elf->e_shoff];
for (i = 0; i < elf->e_shnum; i++) {
if (shdr[i].sh_type == SHT_SYMTAB) {
break;
}
}
/*
* No symbol table found.
*/
if (i == elf->e_shnum)
break;
/*
* No additional header.
*/
ksym_head_size = 0;
ksym_syms = symtab;
ksym_syms_size = (size_t)(esym - symtab);
return;
} while (0);
}
int
ksymsopen(dev_t dev, int flag, int mode, struct proc *p)
{
/* There are no non-zero minor devices */
if (minor(dev) != 0)
return (ENXIO);
/* This device is read-only */
if ((flag & FWRITE))
return (EPERM);
/* ksym_syms must be initialized */
if (ksym_syms == NULL)
return (ENXIO);
return (0);
}
int
ksymsclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
int
ksymsread(dev_t dev, struct uio *uio, int flags)
{
int error;
size_t len;
caddr_t v;
size_t off;
if (uio->uio_offset < 0)
return (EINVAL);
while (uio->uio_resid > 0) {
if (uio->uio_offset >= ksym_head_size + ksym_syms_size)
break;
if (uio->uio_offset < ksym_head_size) {
v = ksym_head + uio->uio_offset;
len = ksym_head_size - uio->uio_offset;
} else {
off = uio->uio_offset - ksym_head_size;
v = ksym_syms + off;
len = ksym_syms_size - off;
}
if (len > uio->uio_resid)
len = uio->uio_resid;
if ((error = uiomove(v, len, uio)) != 0)
return (error);
}
return (0);
}
6
2
4
4
4
2
2
2
216
1
215
1
215
215
214
17
208
215
5
5
5
5
1
2
2
1
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
/* $OpenBSD: sd.c,v 1.332 2022/09/01 13:45:27 krw Exp $ */
/* $NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $ */
/*-
* Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Originally written by Julian Elischer (julian@dialix.oz.au)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
*
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
*
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
* Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
*/
#include <sys/stdint.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <sys/mutex.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/conf.h>
#include <sys/scsiio.h>
#include <sys/dkio.h>
#include <sys/reboot.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
#include <scsi/sdvar.h>
#include <ufs/ffs/fs.h> /* for BBSIZE and SBSIZE */
#include <sys/vnode.h>
int sdmatch(struct device *, void *, void *);
void sdattach(struct device *, struct device *, void *);
int sdactivate(struct device *, int);
int sddetach(struct device *, int);
void sdminphys(struct buf *);
int sdgetdisklabel(dev_t, struct sd_softc *, struct disklabel *, int);
void sdstart(struct scsi_xfer *);
int sd_interpret_sense(struct scsi_xfer *);
int sd_read_cap_10(struct sd_softc *, int);
int sd_read_cap_16(struct sd_softc *, int);
int sd_read_cap(struct sd_softc *, int);
int sd_thin_pages(struct sd_softc *, int);
int sd_vpd_block_limits(struct sd_softc *, int);
int sd_vpd_thin(struct sd_softc *, int);
int sd_thin_params(struct sd_softc *, int);
int sd_get_parms(struct sd_softc *, int);
int sd_flush(struct sd_softc *, int);
void viscpy(u_char *, u_char *, int);
int sd_ioctl_inquiry(struct sd_softc *, struct dk_inquiry *);
int sd_ioctl_cache(struct sd_softc *, long, struct dk_cache *);
int sd_cmd_rw6(struct scsi_generic *, int, u_int64_t, u_int32_t);
int sd_cmd_rw10(struct scsi_generic *, int, u_int64_t, u_int32_t);
int sd_cmd_rw12(struct scsi_generic *, int, u_int64_t, u_int32_t);
int sd_cmd_rw16(struct scsi_generic *, int, u_int64_t, u_int32_t);
void sd_buf_done(struct scsi_xfer *);
const struct cfattach sd_ca = {
sizeof(struct sd_softc), sdmatch, sdattach,
sddetach, sdactivate
};
struct cfdriver sd_cd = {
NULL, "sd", DV_DISK
};
const struct scsi_inquiry_pattern sd_patterns[] = {
{T_DIRECT, T_FIXED,
"", "", ""},
{T_DIRECT, T_REMOV,
"", "", ""},
{T_RDIRECT, T_FIXED,
"", "", ""},
{T_RDIRECT, T_REMOV,
"", "", ""},
{T_OPTICAL, T_FIXED,
"", "", ""},
{T_OPTICAL, T_REMOV,
"", "", ""},
};
#define sdlookup(unit) (struct sd_softc *)disk_lookup(&sd_cd, (unit))
int
sdmatch(struct device *parent, void *match, void *aux)
{
struct scsi_attach_args *sa = aux;
struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
int priority;
(void)scsi_inqmatch(inq, sd_patterns, nitems(sd_patterns),
sizeof(sd_patterns[0]), &priority);
return priority;
}
/*
* The routine called by the low level scsi routine when it discovers
* a device suitable for this driver.
*/
void
sdattach(struct device *parent, struct device *self, void *aux)
{
struct dk_cache dkc;
struct sd_softc *sc = (struct sd_softc *)self;
struct scsi_attach_args *sa = aux;
struct disk_parms *dp = &sc->params;
struct scsi_link *link = sa->sa_sc_link;
int error, sd_autoconf;
int sortby = BUFQ_DEFAULT;
SC_DEBUG(link, SDEV_DB2, ("sdattach:\n"));
sd_autoconf = scsi_autoconf | SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE;
/*
* Store information needed to contact our base driver.
*/
sc->sc_link = link;
link->interpret_sense = sd_interpret_sense;
link->device_softc = sc;
if (ISSET(link->flags, SDEV_ATAPI) && ISSET(link->flags,
SDEV_REMOVABLE))
SET(link->quirks, SDEV_NOSYNCCACHE);
/*
* Use the subdriver to request information regarding the drive. We
* cannot use interrupts yet, so the request must specify this.
*/
printf("\n");
scsi_xsh_set(&sc->sc_xsh, link, sdstart);
/* Spin up non-UMASS devices ready or not. */
if (!ISSET(link->flags, SDEV_UMASS))
scsi_start(link, SSS_START, sd_autoconf);
/*
* Some devices (e.g. BlackBerry Pearl) won't admit they have
* media loaded unless its been locked in.
*/
if (ISSET(link->flags, SDEV_REMOVABLE))
scsi_prevent(link, PR_PREVENT, sd_autoconf);
/* Check that it is still responding and ok. */
error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES * 3,
sd_autoconf);
if (error == 0)
error = sd_get_parms(sc, sd_autoconf);
if (ISSET(link->flags, SDEV_REMOVABLE))
scsi_prevent(link, PR_ALLOW, sd_autoconf);
if (error == 0) {
printf("%s: %lluMB, %u bytes/sector, %llu sectors",
sc->sc_dev.dv_xname,
dp->disksize / (1048576 / dp->secsize), dp->secsize,
dp->disksize);
if (ISSET(sc->flags, SDF_THIN)) {
sortby = BUFQ_FIFO;
printf(", thin");
}
if (ISSET(link->flags, SDEV_READONLY))
printf(", readonly");
printf("\n");
}
/*
* Initialize disk structures.
*/
sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
bufq_init(&sc->sc_bufq, sortby);
/*
* Enable write cache by default.
*/
memset(&dkc, 0, sizeof(dkc));
if (sd_ioctl_cache(sc, DIOCGCACHE, &dkc) == 0 && dkc.wrcache == 0) {
dkc.wrcache = 1;
sd_ioctl_cache(sc, DIOCSCACHE, &dkc);
}
/* Attach disk. */
disk_attach(&sc->sc_dev, &sc->sc_dk);
}
int
sdactivate(struct device *self, int act)
{
struct scsi_link *link;
struct sd_softc *sc = (struct sd_softc *)self;
if (ISSET(sc->flags, SDF_DYING))
return ENXIO;
link = sc->sc_link;
switch (act) {
case DVACT_SUSPEND:
/*
* We flush the cache, since we our next step before
* DVACT_POWERDOWN might be a hibernate operation.
*/
if (ISSET(sc->flags, SDF_DIRTY))
sd_flush(sc, SCSI_AUTOCONF);
break;
case DVACT_POWERDOWN:
/*
* Stop the disk. Stopping the disk should flush the
* cache, but we are paranoid so we flush the cache
* first. We're cold at this point, so we poll for
* completion.
*/
if (ISSET(sc->flags, SDF_DIRTY))
sd_flush(sc, SCSI_AUTOCONF);
if (ISSET(boothowto, RB_POWERDOWN))
scsi_start(link, SSS_STOP,
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_NOT_READY | SCSI_AUTOCONF);
break;
case DVACT_RESUME:
scsi_start(link, SSS_START,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF);
break;
case DVACT_DEACTIVATE:
SET(sc->flags, SDF_DYING);
scsi_xsh_del(&sc->sc_xsh);
break;
}
return 0;
}
int
sddetach(struct device *self, int flags)
{
struct sd_softc *sc = (struct sd_softc *)self;
bufq_drain(&sc->sc_bufq);
disk_gone(sdopen, self->dv_unit);
/* Detach disk. */
bufq_destroy(&sc->sc_bufq);
disk_detach(&sc->sc_dk);
return 0;
}
/*
* Open the device. Make sure the partition info is as up-to-date as can be.
*/
int
sdopen(dev_t dev, int flag, int fmt, struct proc *p)
{
struct scsi_link *link;
struct sd_softc *sc;
int error = 0, part, rawopen, unit;
unit = DISKUNIT(dev);
part = DISKPART(dev);
rawopen = (part == RAW_PART) && (fmt == S_IFCHR);
sc = sdlookup(unit);
if (sc == NULL)
return ENXIO;
if (ISSET(sc->flags, SDF_DYING)) {
device_unref(&sc->sc_dev);
return ENXIO;
}
link = sc->sc_link;
SC_DEBUG(link, SDEV_DB1,
("sdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
sd_cd.cd_ndevs, part));
if (ISSET(flag, FWRITE) && ISSET(link->flags, SDEV_READONLY)) {
device_unref(&sc->sc_dev);
return EACCES;
}
if ((error = disk_lock(&sc->sc_dk)) != 0) {
device_unref(&sc->sc_dev);
return error;
}
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
if (sc->sc_dk.dk_openmask != 0) {
/*
* If any partition is open, but the disk has been invalidated,
* disallow further opens of non-raw partition.
*/
if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
if (rawopen)
goto out;
error = EIO;
goto bad;
}
} else {
/* Spin up non-UMASS devices ready or not. */
if (!ISSET(link->flags, SDEV_UMASS))
scsi_start(link, SSS_START, (rawopen ? SCSI_SILENT :
0) | SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
/*
* Use sd_interpret_sense() for sense errors.
*
* But only after spinning the disk up! Just in case a broken
* device returns "Initialization command required." and causes
* a loop of scsi_start() calls.
*/
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
SET(link->flags, SDEV_OPEN);
/*
* Try to prevent the unloading of a removable device while
* it's open. But allow the open to proceed if the device can't
* be locked in.
*/
if (ISSET(link->flags, SDEV_REMOVABLE)) {
scsi_prevent(link, PR_PREVENT, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
}
/* Check that it is still responding and ok. */
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
error = scsi_test_unit_ready(link,
TEST_READY_RETRIES, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
if (error) {
if (rawopen) {
error = 0;
goto out;
} else
goto bad;
}
/* Load the physical device parameters. */
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
SET(link->flags, SDEV_MEDIA_LOADED);
if (sd_get_parms(sc, (rawopen ? SCSI_SILENT : 0)) == -1) {
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
CLR(link->flags, SDEV_MEDIA_LOADED);
error = ENXIO;
goto bad;
}
SC_DEBUG(link, SDEV_DB3, ("Params loaded\n"));
/* Load the partition info if not already loaded. */
error = sdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0);
if (error == EIO || error == ENXIO)
goto bad;
SC_DEBUG(link, SDEV_DB3, ("Disklabel loaded\n"));
}
out:
if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
goto bad;
SC_DEBUG(link, SDEV_DB3, ("open complete\n"));
/* It's OK to fall through because dk_openmask is now non-zero. */
bad:
if (sc->sc_dk.dk_openmask == 0) {
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
if (ISSET(link->flags, SDEV_REMOVABLE))
scsi_prevent(link, PR_ALLOW, SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
CLR(link->flags, SDEV_OPEN | SDEV_MEDIA_LOADED);
}
die:
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return error;
}
/*
* Close the device. Only called if we are the last occurrence of an open
* device. Convenient now but usually a pain.
*/
int
sdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
struct scsi_link *link;
struct sd_softc *sc;
int part = DISKPART(dev);
int error = 0;
sc = sdlookup(DISKUNIT(dev));
if (sc == NULL)
return ENXIO;
if (ISSET(sc->flags, SDF_DYING)) {
device_unref(&sc->sc_dev);
return ENXIO;
}
link = sc->sc_link;
disk_lock_nointr(&sc->sc_dk);
disk_closepart(&sc->sc_dk, part, fmt);
if ((ISSET(flag, FWRITE) || sc->sc_dk.dk_openmask == 0) &&
ISSET(sc->flags, SDF_DIRTY))
sd_flush(sc, 0);
if (sc->sc_dk.dk_openmask == 0) {
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
if (ISSET(link->flags, SDEV_REMOVABLE))
scsi_prevent(link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_NOT_READY | SCSI_SILENT);
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
CLR(link->flags, SDEV_OPEN | SDEV_MEDIA_LOADED);
if (ISSET(link->flags, SDEV_EJECTING)) {
scsi_start(link, SSS_STOP|SSS_LOEJ, 0);
if (ISSET(sc->flags, SDF_DYING)) {
error = ENXIO;
goto die;
}
CLR(link->flags, SDEV_EJECTING);
}
scsi_xsh_del(&sc->sc_xsh);
}
die:
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return error;
}
/*
* Actually translate the requested transfer into one the physical driver
* can understand. The transfer is described by a buf and will include
* only one physical transfer.
*/
void
sdstrategy(struct buf *bp)
{
struct scsi_link *link;
struct sd_softc *sc;
int s;
sc = sdlookup(DISKUNIT(bp->b_dev));
if (sc == NULL) {
bp->b_error = ENXIO;
goto bad;
}
if (ISSET(sc->flags, SDF_DYING)) {
bp->b_error = ENXIO;
goto bad;
}
link = sc->sc_link;
SC_DEBUG(link, SDEV_DB2, ("sdstrategy: %ld bytes @ blk %lld\n",
bp->b_bcount, (long long)bp->b_blkno));
/*
* If the device has been made invalid, error out.
*/
if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
if (ISSET(link->flags, SDEV_OPEN))
bp->b_error = EIO;
else
bp->b_error = ENODEV;
goto bad;
}
/* Validate the request. */
if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
goto done;
/* Place it in the queue of disk activities for this disk. */
bufq_queue(&sc->sc_bufq, bp);
/*
* Tell the device to get going on the transfer if it's
* not doing anything, otherwise just wait for completion
*/
scsi_xsh_add(&sc->sc_xsh);
device_unref(&sc->sc_dev);
return;
bad:
SET(bp->b_flags, B_ERROR);
bp->b_resid = bp->b_bcount;
done:
s = splbio();
biodone(bp);
splx(s);
if (sc != NULL)
device_unref(&sc->sc_dev);
}
int
sd_cmd_rw6(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw *cmd = (struct scsi_rw *)generic;
cmd->opcode = read ? READ_COMMAND : WRITE_COMMAND;
_lto3b(secno, cmd->addr);
cmd->length = nsecs;
return sizeof(*cmd);
}
int
sd_cmd_rw10(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw_10 *cmd = (struct scsi_rw_10 *)generic;
cmd->opcode = read ? READ_10 : WRITE_10;
_lto4b(secno, cmd->addr);
_lto2b(nsecs, cmd->length);
return sizeof(*cmd);
}
int
sd_cmd_rw12(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw_12 *cmd = (struct scsi_rw_12 *)generic;
cmd->opcode = read ? READ_12 : WRITE_12;
_lto4b(secno, cmd->addr);
_lto4b(nsecs, cmd->length);
return sizeof(*cmd);
}
int
sd_cmd_rw16(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw_16 *cmd = (struct scsi_rw_16 *)generic;
cmd->opcode = read ? READ_16 : WRITE_16;
_lto8b(secno, cmd->addr);
_lto4b(nsecs, cmd->length);
return sizeof(*cmd);
}
/*
* sdstart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
* It dequeues the buf and creates a scsi command to perform the
* transfer in the buf. The transfer request will call scsi_done
* on completion, which will in turn call this routine again
* so that the next queued transfer is performed.
* The bufs are queued by the strategy routine (sdstrategy)
*
* This routine is also called after other non-queued requests
* have been made of the scsi driver, to ensure that the queue
* continues to be drained.
*/
void
sdstart(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct sd_softc *sc = link->device_softc;
struct buf *bp;
struct partition *p;
u_int64_t secno;
u_int32_t nsecs;
int read;
if (ISSET(sc->flags, SDF_DYING)) {
scsi_xs_put(xs);
return;
}
if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
bufq_drain(&sc->sc_bufq);
scsi_xs_put(xs);
return;
}
bp = bufq_dequeue(&sc->sc_bufq);
if (bp == NULL) {
scsi_xs_put(xs);
return;
}
read = ISSET(bp->b_flags, B_READ);
SET(xs->flags, (read ? SCSI_DATA_IN : SCSI_DATA_OUT));
xs->timeout = 60000;
xs->data = bp->b_data;
xs->datalen = bp->b_bcount;
xs->done = sd_buf_done;
xs->cookie = bp;
xs->bp = bp;
p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
secno = DL_GETPOFFSET(p) + DL_BLKTOSEC(sc->sc_dk.dk_label, bp->b_blkno);
nsecs = howmany(bp->b_bcount, sc->sc_dk.dk_label->d_secsize);
if (!ISSET(link->flags, SDEV_ATAPI | SDEV_UMASS) &&
(SID_ANSII_REV(&link->inqdata) < SCSI_REV_2) &&
((secno & 0x1fffff) == secno) &&
((nsecs & 0xff) == nsecs))
xs->cmdlen = sd_cmd_rw6(&xs->cmd, read, secno, nsecs);
else if (sc->params.disksize > UINT32_MAX)
xs->cmdlen = sd_cmd_rw16(&xs->cmd, read, secno, nsecs);
else if (nsecs <= UINT16_MAX)
xs->cmdlen = sd_cmd_rw10(&xs->cmd, read, secno, nsecs);
else
xs->cmdlen = sd_cmd_rw12(&xs->cmd, read, secno, nsecs);
disk_busy(&sc->sc_dk);
if (!read)
SET(sc->flags, SDF_DIRTY);
scsi_xs_exec(xs);
/* Move onto the next io. */
if (bufq_peek(&sc->sc_bufq))
scsi_xsh_add(&sc->sc_xsh);
}
void
sd_buf_done(struct scsi_xfer *xs)
{
struct sd_softc *sc = xs->sc_link->device_softc;
struct buf *bp = xs->cookie;
int error, s;
switch (xs->error) {
case XS_NOERROR:
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
bp->b_resid = xs->resid;
break;
case XS_SENSE:
case XS_SHORTSENSE:
SC_DEBUG_SENSE(xs);
error = sd_interpret_sense(xs);
if (error == 0) {
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
bp->b_resid = xs->resid;
break;
}
if (error != ERESTART) {
bp->b_error = error;
SET(bp->b_flags, B_ERROR);
xs->retries = 0;
}
goto retry;
case XS_BUSY:
if (xs->retries) {
if (scsi_delay(xs, 1) != ERESTART)
xs->retries = 0;
}
goto retry;
case XS_TIMEOUT:
retry:
if (xs->retries--) {
scsi_xs_exec(xs);
return;
}
/* FALLTHROUGH */
default:
if (bp->b_error == 0)
bp->b_error = EIO;
SET(bp->b_flags, B_ERROR);
bp->b_resid = bp->b_bcount;
break;
}
disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid, bp->b_blkno,
bp->b_flags & B_READ);
s = splbio();
biodone(bp);
splx(s);
scsi_xs_put(xs);
}
void
sdminphys(struct buf *bp)
{
struct scsi_link *link;
struct sd_softc *sc;
long max;
sc = sdlookup(DISKUNIT(bp->b_dev));
if (sc == NULL)
return; /* XXX - right way to fail this? */
if (ISSET(sc->flags, SDF_DYING)) {
device_unref(&sc->sc_dev);
return;
}
link = sc->sc_link;
/*
* If the device is ancient, we want to make sure that
* the transfer fits into a 6-byte cdb.
*
* XXX Note that the SCSI-I spec says that 256-block transfers
* are allowed in a 6-byte read/write, and are specified
* by setting the "length" to 0. However, we're conservative
* here, allowing only 255-block transfers in case an
* ancient device gets confused by length == 0. A length of 0
* in a 10-byte read/write actually means 0 blocks.
*/
if (!ISSET(link->flags, SDEV_ATAPI | SDEV_UMASS) &&
SID_ANSII_REV(&link->inqdata) < SCSI_REV_2) {
max = sc->sc_dk.dk_label->d_secsize * 0xff;
if (bp->b_bcount > max)
bp->b_bcount = max;
}
if (link->bus->sb_adapter->dev_minphys != NULL)
(*link->bus->sb_adapter->dev_minphys)(bp, link);
else
minphys(bp);
device_unref(&sc->sc_dev);
}
int
sdread(dev_t dev, struct uio *uio, int ioflag)
{
return physio(sdstrategy, dev, B_READ, sdminphys, uio);
}
int
sdwrite(dev_t dev, struct uio *uio, int ioflag)
{
return physio(sdstrategy, dev, B_WRITE, sdminphys, uio);
}
/*
* Perform special action on behalf of the user. Knows about the internals of
* this device
*/
int
sdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct scsi_link *link;
struct sd_softc *sc;
struct disklabel *lp;
int error = 0;
int part = DISKPART(dev);
sc = sdlookup(DISKUNIT(dev));
if (sc == NULL)
return ENXIO;
if (ISSET(sc->flags, SDF_DYING)) {
device_unref(&sc->sc_dev);
return ENXIO;
}
link = sc->sc_link;
SC_DEBUG(link, SDEV_DB2, ("sdioctl 0x%lx\n", cmd));
/*
* If the device is not valid, abandon ship.
*/
if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
switch (cmd) {
case DIOCLOCK:
case DIOCEJECT:
case SCIOCIDENTIFY:
case SCIOCCOMMAND:
case SCIOCDEBUG:
if (part == RAW_PART)
break;
/* FALLTHROUGH */
default:
if (!ISSET(link->flags, SDEV_OPEN)) {
error = ENODEV;
goto exit;
} else {
error = EIO;
goto exit;
}
}
}
switch (cmd) {
case DIOCRLDINFO:
lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
sdgetdisklabel(dev, sc, lp, 0);
memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
free(lp, M_TEMP, sizeof(*lp));
goto exit;
case DIOCGPDINFO:
sdgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
goto exit;
case DIOCGDINFO:
*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
goto exit;
case DIOCGPART:
((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
goto exit;
case DIOCWDINFO:
case DIOCSDINFO:
if (!ISSET(flag, FWRITE)) {
error = EBADF;
goto exit;
}
if ((error = disk_lock(&sc->sc_dk)) != 0)
goto exit;
error = setdisklabel(sc->sc_dk.dk_label,
(struct disklabel *)addr, sc->sc_dk.dk_openmask);
if (error == 0) {
if (cmd == DIOCWDINFO)
error = writedisklabel(DISKLABELDEV(dev),
sdstrategy, sc->sc_dk.dk_label);
}
disk_unlock(&sc->sc_dk);
goto exit;
case DIOCLOCK:
error = scsi_prevent(link,
(*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
goto exit;
case MTIOCTOP:
if (((struct mtop *)addr)->mt_op != MTOFFL) {
error = EIO;
goto exit;
}
/* FALLTHROUGH */
case DIOCEJECT:
if (!ISSET(link->flags, SDEV_REMOVABLE)) {
error = ENOTTY;
goto exit;
}
SET(link->flags, SDEV_EJECTING);
goto exit;
case DIOCINQ:
error = scsi_do_ioctl(link, cmd, addr, flag);
if (error == ENOTTY)
error = sd_ioctl_inquiry(sc,
(struct dk_inquiry *)addr);
goto exit;
case DIOCSCACHE:
if (!ISSET(flag, FWRITE)) {
error = EBADF;
goto exit;
}
/* FALLTHROUGH */
case DIOCGCACHE:
error = sd_ioctl_cache(sc, cmd, (struct dk_cache *)addr);
goto exit;
case DIOCCACHESYNC:
if (!ISSET(flag, FWRITE)) {
error = EBADF;
goto exit;
}
if (ISSET(sc->flags, SDF_DIRTY) || *(int *)addr != 0)
error = sd_flush(sc, 0);
goto exit;
default:
if (part != RAW_PART) {
error = ENOTTY;
goto exit;
}
error = scsi_do_ioctl(link, cmd, addr, flag);
}
exit:
device_unref(&sc->sc_dev);
return error;
}
int
sd_ioctl_inquiry(struct sd_softc *sc, struct dk_inquiry *di)
{
struct scsi_link *link;
struct scsi_vpd_serial *vpd;
vpd = dma_alloc(sizeof(*vpd), PR_WAITOK | PR_ZERO);
if (ISSET(sc->flags, SDF_DYING)) {
dma_free(vpd, sizeof(*vpd));
return ENXIO;
}
link = sc->sc_link;
bzero(di, sizeof(struct dk_inquiry));
scsi_strvis(di->vendor, link->inqdata.vendor,
sizeof(link->inqdata.vendor));
scsi_strvis(di->product, link->inqdata.product,
sizeof(link->inqdata.product));
scsi_strvis(di->revision, link->inqdata.revision,
sizeof(link->inqdata.revision));
/* the serial vpd page is optional */
if (scsi_inquire_vpd(link, vpd, sizeof(*vpd), SI_PG_SERIAL, 0) == 0)
scsi_strvis(di->serial, vpd->serial, sizeof(vpd->serial));
else
strlcpy(di->serial, "(unknown)", sizeof(vpd->serial));
dma_free(vpd, sizeof(*vpd));
return 0;
}
int
sd_ioctl_cache(struct sd_softc *sc, long cmd, struct dk_cache *dkc)
{
struct scsi_link *link;
union scsi_mode_sense_buf *buf;
struct page_caching_mode *mode = NULL;
u_int wrcache, rdcache;
int big, rv;
if (ISSET(sc->flags, SDF_DYING))
return ENXIO;
link = sc->sc_link;
if (ISSET(link->flags, SDEV_UMASS))
return EOPNOTSUPP;
/* See if the adapter has special handling. */
rv = scsi_do_ioctl(link, cmd, (caddr_t)dkc, 0);
if (rv != ENOTTY)
return rv;
buf = dma_alloc(sizeof(*buf), PR_WAITOK);
if (buf == NULL)
return ENOMEM;
if (ISSET(sc->flags, SDF_DYING)) {
rv = ENXIO;
goto done;
}
rv = scsi_do_mode_sense(link, PAGE_CACHING_MODE, buf, (void **)&mode,
sizeof(*mode) - 4, scsi_autoconf | SCSI_SILENT, &big);
if (rv == 0 && mode == NULL)
rv = EIO;
if (rv != 0)
goto done;
wrcache = (ISSET(mode->flags, PG_CACHE_FL_WCE) ? 1 : 0);
rdcache = (ISSET(mode->flags, PG_CACHE_FL_RCD) ? 0 : 1);
switch (cmd) {
case DIOCGCACHE:
dkc->wrcache = wrcache;
dkc->rdcache = rdcache;
break;
case DIOCSCACHE:
if (dkc->wrcache == wrcache && dkc->rdcache == rdcache)
break;
if (dkc->wrcache)
SET(mode->flags, PG_CACHE_FL_WCE);
else
CLR(mode->flags, PG_CACHE_FL_WCE);
if (dkc->rdcache)
CLR(mode->flags, PG_CACHE_FL_RCD);
else
SET(mode->flags, PG_CACHE_FL_RCD);
if (ISSET(sc->flags, SDF_DYING)) {
rv = ENXIO;
goto done;
}
if (big) {
rv = scsi_mode_select_big(link, SMS_PF,
&buf->hdr_big, scsi_autoconf | SCSI_SILENT, 20000);
} else {
rv = scsi_mode_select(link, SMS_PF,
&buf->hdr, scsi_autoconf | SCSI_SILENT, 20000);
}
break;
}
done:
dma_free(buf, sizeof(*buf));
return rv;
}
/*
* Load the label information on the named device.
*/
int
sdgetdisklabel(dev_t dev, struct sd_softc *sc, struct disklabel *lp,
int spoofonly)
{
char packname[sizeof(lp->d_packname) + 1];
char product[17], vendor[9];
struct scsi_link *link;
size_t len;
if (ISSET(sc->flags, SDF_DYING))
return ENXIO;
link = sc->sc_link;
bzero(lp, sizeof(struct disklabel));
lp->d_secsize = sc->params.secsize;
lp->d_ntracks = sc->params.heads;
lp->d_nsectors = sc->params.sectors;
lp->d_ncylinders = sc->params.cyls;
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
if (lp->d_secpercyl == 0) {
lp->d_secpercyl = 100;
/* As long as it's not 0 - readdisklabel divides by it. */
}
lp->d_type = DTYPE_SCSI;
if ((link->inqdata.device & SID_TYPE) == T_OPTICAL)
strncpy(lp->d_typename, "SCSI optical",
sizeof(lp->d_typename));
else
strncpy(lp->d_typename, "SCSI disk",
sizeof(lp->d_typename));
/*
* Try to fit '<vendor> <product>' into d_packname. If that doesn't fit
* then leave out '<vendor> ' and use only as much of '<product>' as
* does fit.
*/
viscpy(vendor, link->inqdata.vendor, 8);
viscpy(product, link->inqdata.product, 16);
len = snprintf(packname, sizeof(packname), "%s %s", vendor, product);
if (len > sizeof(lp->d_packname)) {
strlcpy(packname, product, sizeof(packname));
len = strlen(packname);
}
/*
* It is safe to use len as the count of characters to copy because
* packname is sizeof(lp->d_packname)+1, the string in packname is
* always null terminated and len does not count the terminating null.
* d_packname is not a null terminated string.
*/
memcpy(lp->d_packname, packname, len);
DL_SETDSIZE(lp, sc->params.disksize);
lp->d_version = 1;
lp->d_flags = 0;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
/*
* Call the generic disklabel extraction routine.
*/
return readdisklabel(DISKLABELDEV(dev), sdstrategy, lp, spoofonly);
}
/*
* Check Errors.
*/
int
sd_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
struct scsi_link *link = xs->sc_link;
int retval;
u_int8_t serr = sense->error_code & SSD_ERRCODE;
/*
* Let the generic code handle everything except a few categories of
* LUN not ready errors on open devices.
*/
if ((!ISSET(link->flags, SDEV_OPEN)) ||
(serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED) ||
((sense->flags & SSD_KEY) != SKEY_NOT_READY) ||
(sense->extra_len < 6))
return scsi_interpret_sense(xs);
if (ISSET(xs->flags, SCSI_IGNORE_NOT_READY))
return 0;
switch (ASC_ASCQ(sense)) {
case SENSE_NOT_READY_BECOMING_READY:
SC_DEBUG(link, SDEV_DB1, ("becoming ready.\n"));
retval = scsi_delay(xs, 5);
break;
case SENSE_NOT_READY_INIT_REQUIRED:
SC_DEBUG(link, SDEV_DB1, ("spinning up\n"));
retval = scsi_start(link, SSS_START,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_NOSLEEP);
if (retval == 0)
retval = ERESTART;
else if (retval == ENOMEM)
/* Can't issue the command. Fall back on a delay. */
retval = scsi_delay(xs, 5);
else
SC_DEBUG(link, SDEV_DB1, ("spin up failed (%#x)\n",
retval));
break;
default:
retval = scsi_interpret_sense(xs);
break;
}
return retval;
}
daddr_t
sdsize(dev_t dev)
{
struct disklabel *lp;
struct sd_softc *sc;
daddr_t size;
int part, omask;
sc = sdlookup(DISKUNIT(dev));
if (sc == NULL)
return -1;
if (ISSET(sc->flags, SDF_DYING)) {
size = -1;
goto exit;
}
part = DISKPART(dev);
omask = sc->sc_dk.dk_openmask & (1 << part);
if (omask == 0 && sdopen(dev, 0, S_IFBLK, NULL) != 0) {
size = -1;
goto exit;
}
lp = sc->sc_dk.dk_label;
if (ISSET(sc->flags, SDF_DYING)) {
size = -1;
goto exit;
}
if (!ISSET(sc->sc_link->flags, SDEV_MEDIA_LOADED))
size = -1;
else if (lp->d_partitions[part].p_fstype != FS_SWAP)
size = -1;
else
size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
if (omask == 0 && sdclose(dev, 0, S_IFBLK, NULL) != 0)
size = -1;
exit:
device_unref(&sc->sc_dev);
return size;
}
/* #define SD_DUMP_NOT_TRUSTED if you just want to watch. */
static int sddoingadump;
/*
* Dump all of physical memory into the partition specified, starting
* at offset 'dumplo' into the partition.
*/
int
sddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
struct sd_softc *sc;
struct disklabel *lp;
struct scsi_xfer *xs;
u_int64_t nsects; /* partition sectors */
u_int64_t sectoff; /* partition offset */
u_int64_t totwrt; /* sectors left */
int part, rv, unit;
u_int32_t sectorsize;
u_int32_t nwrt; /* sectors to write */
/* Check if recursive dump; if so, punt. */
if (sddoingadump)
return EFAULT;
if (blkno < 0)
return EINVAL;
/* Mark as active early. */
sddoingadump = 1;
unit = DISKUNIT(dev); /* Decompose unit & partition. */
part = DISKPART(dev);
/* Check for acceptable drive number. */
if (unit >= sd_cd.cd_ndevs || (sc = sd_cd.cd_devs[unit]) == NULL)
return ENXIO;
/*
* XXX Can't do this check, since the media might have been
* XXX marked `invalid' by successful unmounting of all
* XXX filesystems.
*/
#if 0
/* Make sure it was initialized. */
if (!ISSET(sc->sc_link->flags, SDEV_MEDIA_LOADED))
return ENXIO;
#endif /* 0 */
/* Convert to disk sectors. Request must be a multiple of size. */
lp = sc->sc_dk.dk_label;
sectorsize = lp->d_secsize;
if ((size % sectorsize) != 0)
return EFAULT;
if ((blkno % DL_BLKSPERSEC(lp)) != 0)
return EFAULT;
totwrt = size / sectorsize;
blkno = DL_BLKTOSEC(lp, blkno);
nsects = DL_GETPSIZE(&lp->d_partitions[part]);
sectoff = DL_GETPOFFSET(&lp->d_partitions[part]);
/* Check transfer bounds against partition size. */
if ((blkno + totwrt) > nsects)
return EINVAL;
/* Offset block number to start of partition. */
blkno += sectoff;
while (totwrt > 0) {
if (totwrt > UINT32_MAX)
nwrt = UINT32_MAX;
else
nwrt = totwrt;
#ifndef SD_DUMP_NOT_TRUSTED
xs = scsi_xs_get(sc->sc_link, SCSI_NOSLEEP);
if (xs == NULL)
return ENOMEM;
xs->timeout = 10000;
SET(xs->flags, SCSI_DATA_OUT);
xs->data = va;
xs->datalen = nwrt * sectorsize;
xs->cmdlen = sd_cmd_rw10(&xs->cmd, 0, blkno, nwrt); /* XXX */
rv = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (rv != 0)
return ENXIO;
#else /* SD_DUMP_NOT_TRUSTED */
/* Let's just talk about this first. */
printf("sd%d: dump addr 0x%x, blk %lld\n", unit, va,
(long long)blkno);
delay(500 * 1000); /* 1/2 a second */
#endif /* ~SD_DUMP_NOT_TRUSTED */
/* Update block count. */
totwrt -= nwrt;
blkno += nwrt;
va += sectorsize * nwrt;
}
sddoingadump = 0;
return 0;
}
/*
* Copy up to len chars from src to dst, ignoring non-printables.
* Must be room for len+1 chars in dst so we can write the NUL.
* Does not assume src is NUL-terminated.
*/
void
viscpy(u_char *dst, u_char *src, int len)
{
while (len > 0 && *src != '\0') {
if (*src < 0x20 || *src >= 0x80) {
src++;
continue;
}
*dst++ = *src++;
len--;
}
*dst = '\0';
}
int
sd_read_cap_10(struct sd_softc *sc, int flags)
{
struct scsi_read_cap_data *rdcap;
int rv;
rdcap = dma_alloc(sizeof(*rdcap), (ISSET(flags, SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (rdcap == NULL)
return -1;
if (ISSET(sc->flags, SDF_DYING)) {
rv = -1;
goto done;
}
rv = scsi_read_cap_10(sc->sc_link, rdcap, flags);
if (rv == 0) {
if (_4btol(rdcap->addr) == 0) {
rv = -1;
goto done;
}
sc->params.disksize = _4btol(rdcap->addr) + 1ll;
sc->params.secsize = _4btol(rdcap->length);
CLR(sc->flags, SDF_THIN);
}
done:
dma_free(rdcap, sizeof(*rdcap));
return rv;
}
int
sd_read_cap_16(struct sd_softc *sc, int flags)
{
struct scsi_read_cap_data_16 *rdcap;
int rv;
rdcap = dma_alloc(sizeof(*rdcap), (ISSET(flags, SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (rdcap == NULL)
return -1;
if (ISSET(sc->flags, SDF_DYING)) {
rv = -1;
goto done;
}
rv = scsi_read_cap_16(sc->sc_link, rdcap, flags);
if (rv == 0) {
if (_8btol(rdcap->addr) == 0) {
rv = -1;
goto done;
}
sc->params.disksize = _8btol(rdcap->addr) + 1ll;
sc->params.secsize = _4btol(rdcap->length);
if (ISSET(_2btol(rdcap->lowest_aligned), READ_CAP_16_TPE))
SET(sc->flags, SDF_THIN);
else
CLR(sc->flags, SDF_THIN);
}
done:
dma_free(rdcap, sizeof(*rdcap));
return rv;
}
int
sd_read_cap(struct sd_softc *sc, int flags)
{
int rv;
CLR(flags, SCSI_IGNORE_ILLEGAL_REQUEST);
/*
* post-SPC2 (i.e. post-SCSI-3) devices can start with 16 byte
* read capacity commands. Older devices start with the 10 byte
* version and move up to the 16 byte version if the device
* says it has more sectors than can be reported via the 10 byte
* read capacity.
*/
if (SID_ANSII_REV(&sc->sc_link->inqdata) > SCSI_REV_SPC2) {
rv = sd_read_cap_16(sc, flags);
if (rv != 0)
rv = sd_read_cap_10(sc, flags);
} else {
rv = sd_read_cap_10(sc, flags);
if (rv == 0 && sc->params.disksize == 0x100000000ll)
rv = sd_read_cap_16(sc, flags);
}
return rv;
}
int
sd_thin_pages(struct sd_softc *sc, int flags)
{
struct scsi_vpd_hdr *pg;
u_int8_t *pages;
size_t len = 0;
int i, rv, score = 0;
pg = dma_alloc(sizeof(*pg), (ISSET(flags, SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (pg == NULL)
return ENOMEM;
if (ISSET(sc->flags, SDF_DYING)) {
rv = ENXIO;
goto done;
}
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_SUPPORTED, flags);
if (rv != 0)
goto done;
len = _2btol(pg->page_length);
dma_free(pg, sizeof(*pg));
pg = dma_alloc(sizeof(*pg) + len, (ISSET(flags, SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (pg == NULL)
return ENOMEM;
if (ISSET(sc->flags, SDF_DYING)) {
rv = ENXIO;
goto done;
}
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg) + len,
SI_PG_SUPPORTED, flags);
if (rv != 0)
goto done;
pages = (u_int8_t *)(pg + 1);
if (pages[0] != SI_PG_SUPPORTED) {
rv = EIO;
goto done;
}
for (i = 1; i < len; i++) {
switch (pages[i]) {
case SI_PG_DISK_LIMITS:
case SI_PG_DISK_THIN:
score++;
break;
}
}
if (score < 2)
rv = EOPNOTSUPP;
done:
dma_free(pg, sizeof(*pg) + len);
return rv;
}
int
sd_vpd_block_limits(struct sd_softc *sc, int flags)
{
struct scsi_vpd_disk_limits *pg;
int rv;
pg = dma_alloc(sizeof(*pg), (ISSET(flags, SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (pg == NULL)
return ENOMEM;
if (ISSET(sc->flags, SDF_DYING)) {
rv = ENXIO;
goto done;
}
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_DISK_LIMITS, flags);
if (rv != 0)
goto done;
if (_2btol(pg->hdr.page_length) == SI_PG_DISK_LIMITS_LEN_THIN) {
sc->params.unmap_sectors = _4btol(pg->max_unmap_lba_count);
sc->params.unmap_descs = _4btol(pg->max_unmap_desc_count);
} else
rv = EOPNOTSUPP;
done:
dma_free(pg, sizeof(*pg));
return rv;
}
int
sd_vpd_thin(struct sd_softc *sc, int flags)
{
struct scsi_vpd_disk_thin *pg;
int rv;
pg = dma_alloc(sizeof(*pg), (ISSET(flags, SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (pg == NULL)
return ENOMEM;
if (ISSET(sc->flags, SDF_DYING)) {
rv = ENXIO;
goto done;
}
rv = scsi_inquire_vpd(sc->sc_link, pg, sizeof(*pg),
SI_PG_DISK_THIN, flags);
if (rv != 0)
goto done;
#ifdef notyet
if (ISSET(pg->flags, VPD_DISK_THIN_TPU))
sc->sc_delete = sd_unmap;
else if (ISSET(pg->flags, VPD_DISK_THIN_TPWS)) {
sc->sc_delete = sd_write_same_16;
sc->params.unmap_descs = 1; /* WRITE SAME 16 only does one */
} else
rv = EOPNOTSUPP;
#endif /* notyet */
done:
dma_free(pg, sizeof(*pg));
return rv;
}
int
sd_thin_params(struct sd_softc *sc, int flags)
{
int rv;
rv = sd_thin_pages(sc, flags);
if (rv != 0)
return rv;
rv = sd_vpd_block_limits(sc, flags);
if (rv != 0)
return rv;
rv = sd_vpd_thin(sc, flags);
if (rv != 0)
return rv;
return 0;
}
/*
* Fill out the disk parameter structure. Return 0 if the structure is correctly
* filled in, otherwise return -1.
*
* The caller is responsible for clearing the SDEV_MEDIA_LOADED flag if the
* structure cannot be completed.
*/
int
sd_get_parms(struct sd_softc *sc, int flags)
{
struct disk_parms dp;
struct scsi_link *link = sc->sc_link;
union scsi_mode_sense_buf *buf = NULL;
struct page_rigid_geometry *rigid = NULL;
struct page_flex_geometry *flex = NULL;
struct page_reduced_geometry *reduced = NULL;
u_char *page0 = NULL;
int big, err = 0;
if (sd_read_cap(sc, flags) != 0)
return -1;
if (ISSET(sc->flags, SDF_THIN) && sd_thin_params(sc, flags) != 0) {
/* we dont know the unmap limits, so we cant use thin shizz */
CLR(sc->flags, SDF_THIN);
}
/*
* Work on a copy of the values initialized by sd_read_cap() and
* sd_thin_params().
*/
dp = sc->params;
buf = dma_alloc(sizeof(*buf), PR_NOWAIT);
if (buf == NULL)
goto validate;
if (ISSET(sc->flags, SDF_DYING))
goto die;
/*
* Ask for page 0 (vendor specific) mode sense data to find
* READONLY info. The only thing USB devices will ask for.
*
* page0 == NULL is a valid situation.
*/
err = scsi_do_mode_sense(link, 0, buf, (void **)&page0, 1,
flags | SCSI_SILENT, &big);
if (ISSET(sc->flags, SDF_DYING))
goto die;
if (err == 0) {
if (big && buf->hdr_big.dev_spec & SMH_DSP_WRITE_PROT)
SET(link->flags, SDEV_READONLY);
else if (!big && buf->hdr.dev_spec & SMH_DSP_WRITE_PROT)
SET(link->flags, SDEV_READONLY);
else
CLR(link->flags, SDEV_READONLY);
}
/*
* Many UMASS devices choke when asked about their geometry. Most
* don't have a meaningful geometry anyway, so just fake it if
* sd_read_cap() worked.
*/
if (ISSET(link->flags, SDEV_UMASS) && dp.disksize > 0)
goto validate;
switch (link->inqdata.device & SID_TYPE) {
case T_OPTICAL:
/* No more information needed or available. */
break;
case T_RDIRECT:
/* T_RDIRECT supports only PAGE_REDUCED_GEOMETRY (6). */
err = scsi_do_mode_sense(link, PAGE_REDUCED_GEOMETRY, buf,
(void **)&reduced, sizeof(*reduced), flags | SCSI_SILENT,
&big);
if (err == 0) {
scsi_parse_blkdesc(link, buf, big, NULL, NULL,
&dp.secsize);
if (reduced != NULL) {
if (dp.disksize == 0)
dp.disksize = _5btol(reduced->sectors);
if (dp.secsize == 0)
dp.secsize = _2btol(reduced->bytes_s);
}
}
break;
default:
/*
* NOTE: Some devices leave off the last four bytes of
* PAGE_RIGID_GEOMETRY and PAGE_FLEX_GEOMETRY mode sense pages.
* The only information in those four bytes is RPM information
* so accept the page. The extra bytes will be zero and RPM will
* end up with the default value of 3600.
*/
err = 0;
if (!ISSET(link->flags, SDEV_ATAPI) ||
!ISSET(link->flags, SDEV_REMOVABLE))
err = scsi_do_mode_sense(link, PAGE_RIGID_GEOMETRY, buf,
(void **)&rigid, sizeof(*rigid) - 4,
flags | SCSI_SILENT, &big);
if (err == 0) {
scsi_parse_blkdesc(link, buf, big, NULL, NULL,
&dp.secsize);
if (rigid != NULL) {
dp.heads = rigid->nheads;
dp.cyls = _3btol(rigid->ncyl);
if (dp.heads * dp.cyls > 0)
dp.sectors = dp.disksize / (dp.heads *
dp.cyls);
}
} else {
if (ISSET(sc->flags, SDF_DYING))
goto die;
err = scsi_do_mode_sense(link, PAGE_FLEX_GEOMETRY, buf,
(void **)&flex, sizeof(*flex) - 4,
flags | SCSI_SILENT, &big);
if (err == 0) {
scsi_parse_blkdesc(link, buf, big, NULL, NULL,
&dp.secsize);
if (flex != NULL) {
dp.sectors = flex->ph_sec_tr;
dp.heads = flex->nheads;
dp.cyls = _2btol(flex->ncyl);
if (dp.secsize == 0)
dp.secsize =
_2btol(flex->bytes_s);
if (dp.disksize == 0)
dp.disksize =
(u_int64_t)dp.cyls *
dp.heads * dp.sectors;
}
}
}
break;
}
validate:
if (buf) {
dma_free(buf, sizeof(*buf));
buf = NULL;
}
if (dp.disksize == 0)
goto die;
/*
* Restrict secsize values to powers of two between 512 and 64k.
*/
switch (dp.secsize) {
case 0:
dp.secsize = DEV_BSIZE;
break;
case 0x200: /* == 512, == DEV_BSIZE on all architectures. */
case 0x400:
case 0x800:
case 0x1000:
case 0x2000:
case 0x4000:
case 0x8000:
case 0x10000:
break;
default:
SC_DEBUG(sc->sc_link, SDEV_DB1,
("sd_get_parms: bad secsize: %#x\n", dp.secsize));
return -1;
}
/*
* XXX THINK ABOUT THIS!! Using values such that sectors * heads *
* cyls is <= disk_size can lead to wasted space. We need a more
* careful calculation/validation to make everything work out
* optimally.
*/
if (dp.disksize > 0xffffffff && (dp.heads * dp.sectors) < 0xffff) {
dp.heads = 511;
dp.sectors = 255;
dp.cyls = 0;
}
/*
* Use standard geometry values for anything we still don't
* know.
*/
if (dp.heads == 0)
dp.heads = 255;
if (dp.sectors == 0)
dp.sectors = 63;
if (dp.cyls == 0) {
dp.cyls = dp.disksize / (dp.heads * dp.sectors);
if (dp.cyls == 0) {
/* Put everything into one cylinder. */
dp.heads = dp.cyls = 1;
dp.sectors = dp.disksize;
}
}
#ifdef SCSIDEBUG
if (dp.disksize != (u_int64_t)dp.cyls * dp.heads * dp.sectors) {
sc_print_addr(sc->sc_link);
printf("disksize (%llu) != cyls (%u) * heads (%u) * "
"sectors/track (%u) (%llu)\n", dp.disksize, dp.cyls,
dp.heads, dp.sectors,
(u_int64_t)dp.cyls * dp.heads * dp.sectors);
}
#endif /* SCSIDEBUG */
sc->params = dp;
return 0;
die:
dma_free(buf, sizeof(*buf));
return -1;
}
int
sd_flush(struct sd_softc *sc, int flags)
{
struct scsi_link *link;
struct scsi_xfer *xs;
struct scsi_synchronize_cache *cmd;
int error;
if (ISSET(sc->flags, SDF_DYING))
return ENXIO;
link = sc->sc_link;
if (ISSET(link->quirks, SDEV_NOSYNCCACHE))
return 0;
/*
* Issue a SYNCHRONIZE CACHE. Address 0, length 0 means "all remaining
* blocks starting at address 0". Ignore ILLEGAL REQUEST in the event
* that the command is not supported by the device.
*/
xs = scsi_xs_get(link, flags);
if (xs == NULL) {
SC_DEBUG(link, SDEV_DB1, ("cache sync failed to get xs\n"));
return EIO;
}
cmd = (struct scsi_synchronize_cache *)&xs->cmd;
cmd->opcode = SYNCHRONIZE_CACHE;
xs->cmdlen = sizeof(*cmd);
xs->timeout = 100000;
SET(xs->flags, SCSI_IGNORE_ILLEGAL_REQUEST);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error)
SC_DEBUG(link, SDEV_DB1, ("cache sync failed\n"));
else
CLR(sc->flags, SDF_DIRTY);
return error;
}
46
3
2
1
2
12
2
5
2
19
2
2
6
10
1
4
3
4
1
1
2
2
2
1
1
4
4
4
5
1
2
2
4
4
1
1
1
5
5
5
5
2
5
3
31
37
4
12
12
1
2
1
6
5
5
1
17
11
17
17
16
5
1
4
17
18
19
2
2
31
21
30
23
18
5
12
11
2
17
13
3
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
/* $OpenBSD: ip6_mroute.c,v 1.134 2022/08/09 21:10:03 kn Exp $ */
/* $NetBSD: ip6_mroute.c,v 1.59 2003/12/10 09:28:38 itojun Exp $ */
/* $KAME: ip6_mroute.c,v 1.45 2001/03/25 08:38:51 itojun Exp $ */
/*
* Copyright (C) 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* BSDI ip_mroute.c,v 2.10 1996/11/14 00:29:52 jch Exp */
/*
* Copyright (c) 1989 Stephen Deering
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Stephen Deering of Stanford University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93
*/
/*
* IP multicast forwarding procedures
*
* Written by David Waitzman, BBN Labs, August 1988.
* Modified by Steve Deering, Stanford, February 1989.
* Modified by Mark J. Steiglitz, Stanford, May, 1991
* Modified by Van Jacobson, LBL, January 1993
* Modified by Ajit Thyagarajan, PARC, August 1993
* Modified by Bill Fenner, PARC, April 1994
*
* MROUTING Revision: 3.5.1.2
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/kernel.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/ip6_mroute.h>
#include <netinet/in_pcb.h>
/* #define MCAST_DEBUG */
#ifdef MCAST_DEBUG
int mcast6_debug = 1;
#define DPRINTF(fmt, args...) \
do { \
if (mcast6_debug) \
printf("%s:%d " fmt "\n", \
__func__, __LINE__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
int ip6_mdq(struct mbuf *, struct ifnet *, struct rtentry *);
void phyint_send6(struct ifnet *, struct ip6_hdr *, struct mbuf *);
/*
* Globals. All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static,
* except for netstat or debugging purposes.
*/
struct socket *ip6_mrouter[RT_TABLEID_MAX + 1];
struct rttimer_queue ip6_mrouterq;
int ip6_mrouter_ver = 0;
int ip6_mrtproto; /* for netstat only */
struct mrt6stat mrt6stat;
#define NO_RTE_FOUND 0x1
#define RTE_FOUND 0x2
/*
* Macros to compute elapsed time efficiently
* Borrowed from Van Jacobson's scheduling code
*/
#define TV_DELTA(a, b, delta) do { \
int xxs; \
\
delta = (a).tv_usec - (b).tv_usec; \
if ((xxs = (a).tv_sec - (b).tv_sec)) { \
switch (xxs) { \
case 2: \
delta += 1000000; \
/* FALLTHROUGH */ \
case 1: \
delta += 1000000; \
break; \
default: \
delta += (1000000 * xxs); \
} \
} \
} while (0)
#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \
(a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec)
int get_sg6_cnt(struct sioc_sg_req6 *, unsigned int);
int get_mif6_cnt(struct sioc_mif_req6 *, unsigned int);
int ip6_mrouter_init(struct socket *, int, int);
int add_m6if(struct socket *, struct mif6ctl *);
int del_m6if(struct socket *, mifi_t *);
int add_m6fc(struct socket *, struct mf6cctl *);
int del_m6fc(struct socket *, struct mf6cctl *);
struct ifnet *mrt6_iflookupbymif(mifi_t, unsigned int);
struct rtentry *mf6c_find(struct ifnet *, struct in6_addr *,
struct in6_addr *, unsigned int);
struct rtentry *mrt6_mcast_add(struct ifnet *, struct sockaddr *,
struct sockaddr *);
void mrt6_mcast_del(struct rtentry *, unsigned int);
/*
* Handle MRT setsockopt commands to modify the multicast routing tables.
*/
int
ip6_mrouter_set(int cmd, struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
if (cmd != MRT6_INIT && so != ip6_mrouter[inp->inp_rtableid])
return (EPERM);
switch (cmd) {
case MRT6_INIT:
if (m == NULL || m->m_len < sizeof(int))
return (EINVAL);
return (ip6_mrouter_init(so, *mtod(m, int *), cmd));
case MRT6_DONE:
return (ip6_mrouter_done(so));
case MRT6_ADD_MIF:
if (m == NULL || m->m_len < sizeof(struct mif6ctl))
return (EINVAL);
return (add_m6if(so, mtod(m, struct mif6ctl *)));
case MRT6_DEL_MIF:
if (m == NULL || m->m_len < sizeof(mifi_t))
return (EINVAL);
return (del_m6if(so, mtod(m, mifi_t *)));
case MRT6_ADD_MFC:
if (m == NULL || m->m_len < sizeof(struct mf6cctl))
return (EINVAL);
return (add_m6fc(so, mtod(m, struct mf6cctl *)));
case MRT6_DEL_MFC:
if (m == NULL || m->m_len < sizeof(struct mf6cctl))
return (EINVAL);
return (del_m6fc(so, mtod(m, struct mf6cctl *)));
default:
return (EOPNOTSUPP);
}
}
/*
* Handle MRT getsockopt commands
*/
int
ip6_mrouter_get(int cmd, struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
if (so != ip6_mrouter[inp->inp_rtableid])
return (EPERM);
switch (cmd) {
default:
return EOPNOTSUPP;
}
}
/*
* Handle ioctl commands to obtain information from the cache
*/
int
mrt6_ioctl(struct socket *so, u_long cmd, caddr_t data)
{
struct inpcb *inp = sotoinpcb(so);
int error;
if (inp == NULL)
return (ENOTCONN);
switch (cmd) {
case SIOCGETSGCNT_IN6:
NET_LOCK_SHARED();
error = get_sg6_cnt((struct sioc_sg_req6 *)data,
inp->inp_rtableid);
NET_UNLOCK_SHARED();
break;
case SIOCGETMIFCNT_IN6:
NET_LOCK_SHARED();
error = get_mif6_cnt((struct sioc_mif_req6 *)data,
inp->inp_rtableid);
NET_UNLOCK_SHARED();
break;
default:
error = ENOTTY;
break;
}
return error;
}
/*
* returns the packet, byte, rpf-failure count for the source group provided
*/
int
get_sg6_cnt(struct sioc_sg_req6 *req, unsigned int rtableid)
{
struct rtentry *rt;
struct mf6c *mf6c;
rt = mf6c_find(NULL, &req->src.sin6_addr, &req->grp.sin6_addr,
rtableid);
if (rt == NULL) {
req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff;
return EADDRNOTAVAIL;
}
req->pktcnt = req->bytecnt = req->wrong_if = 0;
do {
mf6c = (struct mf6c *)rt->rt_llinfo;
if (mf6c == NULL)
continue;
req->pktcnt += mf6c->mf6c_pkt_cnt;
req->bytecnt += mf6c->mf6c_byte_cnt;
req->wrong_if += mf6c->mf6c_wrong_if;
} while ((rt = rtable_iterate(rt)) != NULL);
return 0;
}
/*
* returns the input and output packet and byte counts on the mif provided
*/
int
get_mif6_cnt(struct sioc_mif_req6 *req, unsigned int rtableid)
{
struct ifnet *ifp;
struct mif6 *m6;
if ((ifp = mrt6_iflookupbymif(req->mifi, rtableid)) == NULL)
return EINVAL;
m6 = (struct mif6 *)ifp->if_mcast6;
req->icount = m6->m6_pkt_in;
req->ocount = m6->m6_pkt_out;
req->ibytes = m6->m6_bytes_in;
req->obytes = m6->m6_bytes_out;
return 0;
}
int
mrt6_sysctl_mif(void *oldp, size_t *oldlenp)
{
struct ifnet *ifp;
caddr_t where = oldp;
size_t needed, given;
struct mif6 *mifp;
struct mif6info minfo;
given = *oldlenp;
needed = 0;
memset(&minfo, 0, sizeof minfo);
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if ((mifp = (struct mif6 *)ifp->if_mcast6) == NULL)
continue;
minfo.m6_mifi = mifp->m6_mifi;
minfo.m6_flags = mifp->m6_flags;
minfo.m6_lcl_addr = mifp->m6_lcl_addr;
minfo.m6_ifindex = ifp->if_index;
minfo.m6_pkt_in = mifp->m6_pkt_in;
minfo.m6_pkt_out = mifp->m6_pkt_out;
minfo.m6_bytes_in = mifp->m6_bytes_in;
minfo.m6_bytes_out = mifp->m6_bytes_out;
minfo.m6_rate_limit = mifp->m6_rate_limit;
needed += sizeof(minfo);
if (where && needed <= given) {
int error;
error = copyout(&minfo, where, sizeof(minfo));
if (error)
return (error);
where += sizeof(minfo);
}
}
if (where) {
*oldlenp = needed;
if (given < needed)
return (ENOMEM);
} else
*oldlenp = (11 * needed) / 10;
return (0);
}
struct mf6csysctlarg {
struct mf6cinfo *ms6a_minfos;
size_t ms6a_len;
size_t ms6a_needed;
};
int
mrt6_rtwalk_mf6csysctl(struct rtentry *rt, void *arg, unsigned int rtableid)
{
struct mf6c *mf6c = (struct mf6c *)rt->rt_llinfo;
struct mf6csysctlarg *msa = arg;
struct ifnet *ifp;
struct mif6 *m6;
struct mf6cinfo *minfo;
int new = 0;
/* Skip entries being removed. */
if (mf6c == NULL)
return 0;
/* Skip non-multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
return 0;
/* User just asked for the output size. */
if (msa->ms6a_minfos == NULL) {
msa->ms6a_needed += sizeof(*minfo);
return 0;
}
/* Skip route with invalid interfaces. */
if ((ifp = if_get(rt->rt_ifidx)) == NULL)
return 0;
if ((m6 = (struct mif6 *)ifp->if_mcast6) == NULL) {
if_put(ifp);
return 0;
}
for (minfo = msa->ms6a_minfos;
(uint8_t *)minfo < ((uint8_t *)msa->ms6a_minfos + msa->ms6a_len);
minfo++) {
/* Find a new entry or update old entry. */
if (!IN6_ARE_ADDR_EQUAL(&minfo->mf6c_origin.sin6_addr,
&satosin6(rt->rt_gateway)->sin6_addr) ||
!IN6_ARE_ADDR_EQUAL(&minfo->mf6c_mcastgrp.sin6_addr,
&satosin6(rt_key(rt))->sin6_addr)) {
if (!IN6_IS_ADDR_UNSPECIFIED(
&minfo->mf6c_origin.sin6_addr) ||
!IN6_IS_ADDR_UNSPECIFIED(
&minfo->mf6c_mcastgrp.sin6_addr))
continue;
new = 1;
}
minfo->mf6c_origin = *satosin6(rt->rt_gateway);
minfo->mf6c_mcastgrp = *satosin6(rt_key(rt));
minfo->mf6c_parent = mf6c->mf6c_parent;
minfo->mf6c_pkt_cnt += mf6c->mf6c_pkt_cnt;
minfo->mf6c_byte_cnt += mf6c->mf6c_byte_cnt;
IF_SET(m6->m6_mifi, &minfo->mf6c_ifset);
break;
}
if (new != 0)
msa->ms6a_needed += sizeof(*minfo);
if_put(ifp);
return 0;
}
int
mrt6_sysctl_mfc(void *oldp, size_t *oldlenp)
{
unsigned int rtableid;
int error;
struct mf6csysctlarg msa;
if (oldp != NULL && *oldlenp > MAXPHYS)
return EINVAL;
if (oldp != NULL)
msa.ms6a_minfos = malloc(*oldlenp, M_TEMP, M_WAITOK | M_ZERO);
else
msa.ms6a_minfos = NULL;
msa.ms6a_len = *oldlenp;
msa.ms6a_needed = 0;
for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) {
rtable_walk(rtableid, AF_INET6, NULL, mrt6_rtwalk_mf6csysctl,
&msa);
}
if (msa.ms6a_minfos != NULL && msa.ms6a_needed > 0 &&
(error = copyout(msa.ms6a_minfos, oldp, msa.ms6a_needed)) != 0) {
free(msa.ms6a_minfos, M_TEMP, *oldlenp);
return error;
}
free(msa.ms6a_minfos, M_TEMP, *oldlenp);
*oldlenp = msa.ms6a_needed;
return 0;
}
/*
* Enable multicast routing
*/
int
ip6_mrouter_init(struct socket *so, int v, int cmd)
{
struct inpcb *inp = sotoinpcb(so);
unsigned int rtableid = inp->inp_rtableid;
if (so->so_type != SOCK_RAW ||
so->so_proto->pr_protocol != IPPROTO_ICMPV6)
return (EOPNOTSUPP);
if (v != 1)
return (ENOPROTOOPT);
if (ip6_mrouter[rtableid] != NULL)
return (EADDRINUSE);
ip6_mrouter[rtableid] = so;
ip6_mrouter_ver = cmd;
return (0);
}
int
mrouter6_rtwalk_delete(struct rtentry *rt, void *arg, unsigned int rtableid)
{
/* Skip non-multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
return 0;
return EEXIST;
}
/*
* Disable multicast routing
*/
int
ip6_mrouter_done(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
struct ifnet *ifp;
unsigned int rtableid = inp->inp_rtableid;
int error;
NET_ASSERT_LOCKED();
/* Delete all remaining installed multicast routes. */
do {
struct rtentry *rt = NULL;
error = rtable_walk(rtableid, AF_INET6, &rt,
mrouter6_rtwalk_delete, NULL);
if (rt != NULL && error == EEXIST) {
mrt6_mcast_del(rt, rtableid);
error = EAGAIN;
}
rtfree(rt);
} while (error == EAGAIN);
/* Unregister all interfaces in the domain. */
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rtableid)
continue;
ip6_mrouter_detach(ifp);
}
ip6_mrouter[inp->inp_rtableid] = NULL;
ip6_mrouter_ver = 0;
return 0;
}
void
ip6_mrouter_detach(struct ifnet *ifp)
{
struct mif6 *m6 = (struct mif6 *)ifp->if_mcast6;
struct in6_ifreq ifr;
if (m6 == NULL)
return;
ifp->if_mcast6 = NULL;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sin6_family = AF_INET6;
ifr.ifr_addr.sin6_addr = in6addr_any;
KERNEL_LOCK();
(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
KERNEL_UNLOCK();
free(m6, M_MRTABLE, sizeof(*m6));
}
/*
* Add a mif to the mif table
*/
int
add_m6if(struct socket *so, struct mif6ctl *mifcp)
{
struct inpcb *inp = sotoinpcb(so);
struct mif6 *mifp;
struct ifnet *ifp;
struct in6_ifreq ifr;
int error;
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
if (mifcp->mif6c_mifi >= MAXMIFS)
return EINVAL;
if (mrt6_iflookupbymif(mifcp->mif6c_mifi, rtableid) != NULL)
return EADDRINUSE; /* XXX: is it appropriate? */
{
ifp = if_get(mifcp->mif6c_pifi);
if (ifp == NULL)
return ENXIO;
/* Make sure the interface supports multicast */
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
if_put(ifp);
return EOPNOTSUPP;
}
/*
* Enable promiscuous reception of all IPv6 multicasts
* from the interface.
*/
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sin6_family = AF_INET6;
ifr.ifr_addr.sin6_addr = in6addr_any;
error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
if (error) {
if_put(ifp);
return error;
}
}
mifp = malloc(sizeof(*mifp), M_MRTABLE, M_WAITOK | M_ZERO);
ifp->if_mcast6 = (caddr_t)mifp;
mifp->m6_mifi = mifcp->mif6c_mifi;
mifp->m6_flags = mifcp->mif6c_flags;
#ifdef notyet
/* scaling up here allows division by 1024 in critical code */
mifp->m6_rate_limit = mifcp->mif6c_rate_limit * 1024 / 1000;
#endif
if_put(ifp);
return 0;
}
/*
* Delete a mif from the mif table
*/
int
del_m6if(struct socket *so, mifi_t *mifip)
{
struct inpcb *inp = sotoinpcb(so);
struct ifnet *ifp;
NET_ASSERT_LOCKED();
if (*mifip >= MAXMIFS)
return EINVAL;
if ((ifp = mrt6_iflookupbymif(*mifip, inp->inp_rtableid)) == NULL)
return EINVAL;
ip6_mrouter_detach(ifp);
return 0;
}
int
mf6c_add_route(struct ifnet *ifp, struct sockaddr *origin,
struct sockaddr *group, struct mf6cctl *mf6cc, int wait)
{
struct rtentry *rt;
struct mf6c *mf6c;
unsigned int rtableid = ifp->if_rdomain;
#ifdef MCAST_DEBUG
char bsrc[INET6_ADDRSTRLEN], bdst[INET6_ADDRSTRLEN];
#endif /* MCAST_DEBUG */
rt = mrt6_mcast_add(ifp, origin, group);
if (rt == NULL)
return ENOENT;
mf6c = malloc(sizeof(*mf6c), M_MRTABLE, wait | M_ZERO);
if (mf6c == NULL) {
DPRINTF("origin %s group %s parent %d (%s) malloc failed",
inet_ntop(AF_INET6, origin, bsrc, sizeof(bsrc)),
inet_ntop(AF_INET6, group, bdst, sizeof(bdst)),
mf6cc->mf6cc_parent, ifp->if_xname);
mrt6_mcast_del(rt, rtableid);
rtfree(rt);
return ENOMEM;
}
rt->rt_llinfo = (caddr_t)mf6c;
rt_timer_add(rt, &ip6_mrouterq, rtableid);
mf6c->mf6c_parent = mf6cc->mf6cc_parent;
rtfree(rt);
return 0;
}
void
mf6c_update(struct mf6cctl *mf6cc, int wait, unsigned int rtableid)
{
struct rtentry *rt;
struct mf6c *mf6c;
struct ifnet *ifp;
struct sockaddr_in6 osin6, gsin6;
mifi_t mifi;
#ifdef MCAST_DEBUG
char bdst[INET6_ADDRSTRLEN];
#endif /* MCAST_DEBUG */
memset(&osin6, 0, sizeof(osin6));
osin6.sin6_family = AF_INET6;
osin6.sin6_len = sizeof(osin6);
osin6.sin6_addr = mf6cc->mf6cc_origin.sin6_addr;
memset(&gsin6, 0, sizeof(gsin6));
gsin6.sin6_family = AF_INET6;
gsin6.sin6_len = sizeof(gsin6);
gsin6.sin6_addr = mf6cc->mf6cc_mcastgrp.sin6_addr;
for (mifi = 0; mifi < MAXMIFS; mifi++) {
if (mifi == mf6cc->mf6cc_parent)
continue;
/* Test for mif existence and then update the entry. */
if ((ifp = mrt6_iflookupbymif(mifi, rtableid)) == NULL)
continue;
rt = mf6c_find(ifp, &mf6cc->mf6cc_origin.sin6_addr,
&mf6cc->mf6cc_mcastgrp.sin6_addr, rtableid);
/* mif not configured or removed. */
if (!IF_ISSET(mifi, &mf6cc->mf6cc_ifset)) {
/* Route doesn't exist, nothing to do. */
if (rt == NULL)
continue;
DPRINTF("del route (group %s) for mif %d (%s)",
inet_ntop(AF_INET6,
&mf6cc->mf6cc_mcastgrp.sin6_addr, bdst,
sizeof(bdst)), mifi, ifp->if_xname);
mrt6_mcast_del(rt, rtableid);
rtfree(rt);
continue;
}
/* Route exists, look for changes. */
if (rt != NULL) {
mf6c = (struct mf6c *)rt->rt_llinfo;
/* Skip route being deleted. */
if (mf6c == NULL) {
rtfree(rt);
continue;
}
/* No new changes to apply. */
if (mf6cc->mf6cc_parent == mf6c->mf6c_parent) {
rtfree(rt);
continue;
}
DPRINTF("update route (group %s) for mif %d (%s)",
inet_ntop(AF_INET6,
&mf6cc->mf6cc_mcastgrp.sin6_addr, bdst,
sizeof(bdst)), mifi, ifp->if_xname);
mf6c->mf6c_parent = mf6cc->mf6cc_parent;
rtfree(rt);
continue;
}
DPRINTF("add route (group %s) for mif %d (%s)",
inet_ntop(AF_INET6, &mf6cc->mf6cc_mcastgrp.sin6_addr,
bdst, sizeof(bdst)), mifi, ifp->if_xname);
mf6c_add_route(ifp, sin6tosa(&osin6), sin6tosa(&gsin6),
mf6cc, wait);
}
/* Create route for the parent interface. */
if ((ifp = mrt6_iflookupbymif(mf6cc->mf6cc_parent,
rtableid)) == NULL) {
DPRINTF("failed to find upstream interface %d",
mf6cc->mf6cc_parent);
return;
}
/* We already have a route, nothing to do here. */
if ((rt = mf6c_find(ifp, &mf6cc->mf6cc_origin.sin6_addr,
&mf6cc->mf6cc_mcastgrp.sin6_addr, rtableid)) != NULL) {
rtfree(rt);
return;
}
DPRINTF("add upstream route (group %s) for if %s",
inet_ntop(AF_INET6, &mf6cc->mf6cc_mcastgrp.sin6_addr,
bdst, sizeof(bdst)), ifp->if_xname);
mf6c_add_route(ifp, sin6tosa(&osin6), sin6tosa(&gsin6), mf6cc, wait);
}
int
mf6c_add(struct mf6cctl *mfccp, struct in6_addr *origin,
struct in6_addr *group, int vidx, unsigned int rtableid, int wait)
{
struct ifnet *ifp;
struct mif6 *m6;
struct mf6cctl mf6cc;
ifp = mrt6_iflookupbymif(vidx, rtableid);
if (ifp == NULL ||
(m6 = (struct mif6 *)ifp->if_mcast6) == NULL)
return ENOENT;
memset(&mf6cc, 0, sizeof(mf6cc));
if (mfccp == NULL) {
mf6cc.mf6cc_origin.sin6_family = AF_INET6;
mf6cc.mf6cc_origin.sin6_len = sizeof(mf6cc.mf6cc_origin);
mf6cc.mf6cc_origin.sin6_addr = *origin;
mf6cc.mf6cc_mcastgrp.sin6_family = AF_INET6;
mf6cc.mf6cc_mcastgrp.sin6_len = sizeof(mf6cc.mf6cc_mcastgrp);
mf6cc.mf6cc_mcastgrp.sin6_addr = *group;
mf6cc.mf6cc_parent = vidx;
} else
memcpy(&mf6cc, mfccp, sizeof(mf6cc));
mf6c_update(&mf6cc, wait, rtableid);
return 0;
}
int
add_m6fc(struct socket *so, struct mf6cctl *mfccp)
{
struct inpcb *inp = sotoinpcb(so);
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
return mf6c_add(mfccp, &mfccp->mf6cc_origin.sin6_addr,
&mfccp->mf6cc_mcastgrp.sin6_addr, mfccp->mf6cc_parent,
rtableid, M_WAITOK);
}
int
del_m6fc(struct socket *so, struct mf6cctl *mfccp)
{
struct inpcb *inp = sotoinpcb(so);
struct rtentry *rt;
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
while ((rt = mf6c_find(NULL, &mfccp->mf6cc_origin.sin6_addr,
&mfccp->mf6cc_mcastgrp.sin6_addr, rtableid)) != NULL) {
mrt6_mcast_del(rt, rtableid);
rtfree(rt);
}
return 0;
}
int
socket6_send(struct socket *s, struct mbuf *mm, struct sockaddr_in6 *src)
{
if (s) {
if (sbappendaddr(s, &s->so_rcv, sin6tosa(src), mm, NULL) != 0) {
sorwakeup(s);
return 0;
}
}
m_freem(mm);
return -1;
}
/*
* IPv6 multicast forwarding function. This function assumes that the packet
* pointed to by "ip6" has arrived on (or is about to be sent to) the interface
* pointed to by "ifp", and the packet is to be relayed to other networks
* that have members of the packet's destination IPv6 multicast group.
*
* The packet is returned unscathed to the caller, unless it is
* erroneous, in which case a non-zero return value tells the caller to
* discard it.
*/
int
ip6_mforward(struct ip6_hdr *ip6, struct ifnet *ifp, struct mbuf *m)
{
struct rtentry *rt;
struct mif6 *mifp;
struct mbuf *mm;
struct sockaddr_in6 sin6;
unsigned int rtableid = ifp->if_rdomain;
NET_ASSERT_LOCKED();
/*
* Don't forward a packet with Hop limit of zero or one,
* or a packet destined to a local-only group.
*/
if (ip6->ip6_hlim <= 1 || IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) ||
IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst))
return 0;
ip6->ip6_hlim--;
/*
* Source address check: do not forward packets with unspecified
* source. It was discussed in July 2000, on ipngwg mailing list.
* This is rather more serious than unicast cases, because some
* MLD packets can be sent with the unspecified source address
* (although such packets must normally set 1 to the hop limit field).
*/
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
ip6stat_inc(ip6s_cantforward);
if (ip6_log_time + ip6_log_interval < getuptime()) {
char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN];
ip6_log_time = getuptime();
inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src));
inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst));
log(LOG_DEBUG, "cannot forward "
"from %s to %s nxt %d received on interface %u\n",
src, dst, ip6->ip6_nxt, m->m_pkthdr.ph_ifidx);
}
return 0;
}
/*
* Determine forwarding mifs from the forwarding cache table
*/
rt = mf6c_find(NULL, &ip6->ip6_src, &ip6->ip6_dst, rtableid);
/* Entry exists, so forward if necessary */
if (rt) {
return (ip6_mdq(m, ifp, rt));
} else {
/*
* If we don't have a route for packet's origin,
* Make a copy of the packet &
* send message to routing daemon
*/
mrt6stat.mrt6s_no_route++;
{
struct mrt6msg *im;
if ((mifp = (struct mif6 *)ifp->if_mcast6) == NULL)
return EHOSTUNREACH;
/*
* Make a copy of the header to send to the user
* level process
*/
mm = m_copym(m, 0, sizeof(struct ip6_hdr), M_NOWAIT);
if (mm == NULL)
return ENOBUFS;
/*
* Send message to routing daemon
*/
(void)memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(sin6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = ip6->ip6_src;
im = NULL;
switch (ip6_mrouter_ver) {
case MRT6_INIT:
im = mtod(mm, struct mrt6msg *);
im->im6_msgtype = MRT6MSG_NOCACHE;
im->im6_mbz = 0;
im->im6_mif = mifp->m6_mifi;
break;
default:
m_freem(mm);
return EINVAL;
}
if (socket6_send(ip6_mrouter[rtableid], mm,
&sin6) < 0) {
log(LOG_WARNING, "ip6_mforward: ip6_mrouter "
"socket queue full\n");
mrt6stat.mrt6s_upq_sockfull++;
return ENOBUFS;
}
mrt6stat.mrt6s_upcalls++;
mf6c_add(NULL, &ip6->ip6_src, &ip6->ip6_dst,
mifp->m6_mifi, rtableid, M_NOWAIT);
}
return 0;
}
}
void
mf6c_expire_route(struct rtentry *rt, u_int rtableid)
{
struct mf6c *mf6c = (struct mf6c *)rt->rt_llinfo;
#ifdef MCAST_DEBUG
char bsrc[INET6_ADDRSTRLEN], bdst[INET6_ADDRSTRLEN];
#endif /* MCAST_DEBUG */
/* Skip entry being deleted. */
if (mf6c == NULL)
return;
DPRINTF("origin %s group %s interface %d expire %s",
inet_ntop(AF_INET6, &satosin6(rt->rt_gateway)->sin6_addr,
bsrc, sizeof(bsrc)),
inet_ntop(AF_INET6, &satosin6(rt_key(rt))->sin6_addr,
bdst, sizeof(bdst)), rt->rt_ifidx,
mf6c->mf6c_expire ? "yes" : "no");
if (mf6c->mf6c_expire == 0) {
mf6c->mf6c_expire = 1;
rt_timer_add(rt, &ip6_mrouterq, rtableid);
return;
}
mrt6_mcast_del(rt, rtableid);
}
/*
* Packet forwarding routine once entry in the cache is made
*/
int
ip6_mdq(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt)
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct mif6 *m6, *mifp = (struct mif6 *)ifp->if_mcast6;
struct mf6c *mf6c = (struct mf6c *)rt->rt_llinfo;
struct ifnet *ifn;
int plen = m->m_pkthdr.len;
if (mifp == NULL || mf6c == NULL) {
rtfree(rt);
return EHOSTUNREACH;
}
/*
* Don't forward if it didn't arrive from the parent mif
* for its origin.
*/
if (mifp->m6_mifi != mf6c->mf6c_parent) {
/* came in the wrong interface */
mrt6stat.mrt6s_wrong_if++;
mf6c->mf6c_wrong_if++;
rtfree(rt);
return 0;
} /* if wrong iif */
/* If I sourced this packet, it counts as output, else it was input. */
if (m->m_pkthdr.ph_ifidx == 0) {
/* XXX: is ph_ifidx really 0 when output?? */
mifp->m6_pkt_out++;
mifp->m6_bytes_out += plen;
} else {
mifp->m6_pkt_in++;
mifp->m6_bytes_in += plen;
}
/*
* For each mif, forward a copy of the packet if there are group
* members downstream on the interface.
*/
do {
/* Don't consider non multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
continue;
mf6c = (struct mf6c *)rt->rt_llinfo;
if (mf6c == NULL)
continue;
mf6c->mf6c_pkt_cnt++;
mf6c->mf6c_byte_cnt += m->m_pkthdr.len;
/* Don't let this route expire. */
mf6c->mf6c_expire = 0;
if ((ifn = if_get(rt->rt_ifidx)) == NULL)
continue;
/* Sanity check: did we configure this? */
if ((m6 = (struct mif6 *)ifn->if_mcast6) == NULL) {
if_put(ifn);
continue;
}
/* Don't send in the upstream interface. */
if (mf6c->mf6c_parent == m6->m6_mifi) {
if_put(ifn);
continue;
}
/*
* check if the outgoing packet is going to break
* a scope boundary.
*/
if ((mifp->m6_flags & MIFF_REGISTER) == 0 &&
(m6->m6_flags & MIFF_REGISTER) == 0 &&
(in6_addr2scopeid(ifp->if_index, &ip6->ip6_dst) !=
in6_addr2scopeid(ifn->if_index, &ip6->ip6_dst) ||
in6_addr2scopeid(ifp->if_index, &ip6->ip6_src) !=
in6_addr2scopeid(ifn->if_index, &ip6->ip6_src))) {
if_put(ifn);
ip6stat_inc(ip6s_badscope);
continue;
}
m6->m6_pkt_out++;
m6->m6_bytes_out += plen;
phyint_send6(ifn, ip6, m);
if_put(ifn);
} while ((rt = rtable_iterate(rt)) != NULL);
return 0;
}
void
phyint_send6(struct ifnet *ifp, struct ip6_hdr *ip6, struct mbuf *m)
{
struct mbuf *mb_copy;
struct sockaddr_in6 *dst6, sin6;
int error = 0;
NET_ASSERT_LOCKED();
/*
* Make a new reference to the packet; make sure that
* the IPv6 header is actually copied, not just referenced,
* so that ip6_output() only scribbles on the copy.
*/
mb_copy = m_dup_pkt(m, max_linkhdr, M_NOWAIT);
if (mb_copy == NULL)
return;
/* set MCAST flag to the outgoing packet */
mb_copy->m_flags |= M_MCAST;
/*
* If we sourced the packet, call ip6_output since we may divide
* the packet into fragments when the packet is too big for the
* outgoing interface.
* Otherwise, we can simply send the packet to the interface
* sending queue.
*/
if (m->m_pkthdr.ph_ifidx == 0) {
struct ip6_moptions im6o;
im6o.im6o_ifidx = ifp->if_index;
/* XXX: ip6_output will override ip6->ip6_hlim */
im6o.im6o_hlim = ip6->ip6_hlim;
im6o.im6o_loop = 1;
error = ip6_output(mb_copy, NULL, NULL, IPV6_FORWARDING, &im6o,
NULL);
return;
}
/*
* If we belong to the destination multicast group
* on the outgoing interface, loop back a copy.
*/
dst6 = &sin6;
memset(&sin6, 0, sizeof(sin6));
if (in6_hasmulti(&ip6->ip6_dst, ifp)) {
dst6->sin6_len = sizeof(struct sockaddr_in6);
dst6->sin6_family = AF_INET6;
dst6->sin6_addr = ip6->ip6_dst;
ip6_mloopback(ifp, m, dst6);
}
/*
* Put the packet into the sending queue of the outgoing interface
* if it would fit in the MTU of the interface.
*/
if (mb_copy->m_pkthdr.len <= ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) {
dst6->sin6_len = sizeof(struct sockaddr_in6);
dst6->sin6_family = AF_INET6;
dst6->sin6_addr = ip6->ip6_dst;
error = ifp->if_output(ifp, mb_copy, sin6tosa(dst6), NULL);
} else {
if (ip6_mcast_pmtu)
icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0,
ifp->if_mtu);
else {
m_freem(mb_copy); /* simply discard the packet */
}
}
}
struct ifnet *
mrt6_iflookupbymif(mifi_t mifi, unsigned int rtableid)
{
struct mif6 *m6;
struct ifnet *ifp;
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rtableid)
continue;
if ((m6 = (struct mif6 *)ifp->if_mcast6) == NULL)
continue;
if (m6->m6_mifi != mifi)
continue;
return ifp;
}
return NULL;
}
struct rtentry *
mf6c_find(struct ifnet *ifp, struct in6_addr *origin, struct in6_addr *group,
unsigned int rtableid)
{
struct rtentry *rt;
struct sockaddr_in6 msin6;
memset(&msin6, 0, sizeof(msin6));
msin6.sin6_family = AF_INET6;
msin6.sin6_len = sizeof(msin6);
msin6.sin6_addr = *group;
rt = rtalloc(sin6tosa(&msin6), 0, rtableid);
do {
if (!rtisvalid(rt)) {
rtfree(rt);
return NULL;
}
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
continue;
/* Return first occurrence if interface is not specified. */
if (ifp == NULL)
return rt;
if (rt->rt_ifidx == ifp->if_index)
return rt;
} while ((rt = rtable_iterate(rt)) != NULL);
return NULL;
}
struct rtentry *
mrt6_mcast_add(struct ifnet *ifp, struct sockaddr *origin,
struct sockaddr *group)
{
struct ifaddr *ifa;
int rv;
unsigned int rtableid = ifp->if_rdomain;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family == AF_INET6)
break;
}
if (ifa == NULL) {
DPRINTF("ifa == NULL");
return NULL;
}
rv = rt_ifa_add(ifa, RTF_HOST | RTF_MULTICAST | RTF_MPATH, group,
ifp->if_rdomain);
if (rv != 0) {
DPRINTF("rt_ifa_add failed %d", rv);
return NULL;
}
return mf6c_find(ifp, NULL, &satosin6(group)->sin6_addr, rtableid);
}
void
mrt6_mcast_del(struct rtentry *rt, unsigned int rtableid)
{
struct ifnet *ifp;
int error;
/* Remove all timers related to this route. */
rt_timer_remove_all(rt);
free(rt->rt_llinfo, M_MRTABLE, sizeof(struct mf6c));
rt->rt_llinfo = NULL;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return;
error = rtdeletemsg(rt, ifp, rtableid);
if_put(ifp);
if (error)
DPRINTF("delete route error %d\n", error);
}
381
381
381
229
46
174
2
11
12
178
120
55
12
62
147
139
2
10
57
103
109
57
64
272
272
381
381
166
166
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
/* $OpenBSD: ufs_quota.c,v 1.47 2020/06/24 22:03:45 cheloha Exp $ */
/* $NetBSD: ufs_quota.c,v 1.8 1996/02/09 22:36:09 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1990, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Robert Elz at The University of Melbourne.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_quota.c 8.5 (Berkeley) 8/19/94
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/ktrace.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <sys/queue.h>
#include <crypto/siphash.h>
/*
* The following structure records disk usage for a user or group on a
* filesystem. There is one allocated for each quota that exists on any
* filesystem for the current user or group. A cache is kept of recently
* used entries.
*/
struct dquot {
LIST_ENTRY(dquot) dq_hash; /* hash list */
TAILQ_ENTRY(dquot) dq_freelist; /* free list */
u_int16_t dq_flags; /* flags, see below */
u_int16_t dq_type; /* quota type of this dquot */
u_int32_t dq_cnt; /* count of active references */
u_int32_t dq_id; /* identifier this applies to */
struct vnode *dq_vp; /* file backing this quota */
struct ucred *dq_cred; /* credentials for writing file */
struct dqblk dq_dqb; /* actual usage & quotas */
};
/*
* Flag values.
*/
#define DQ_LOCK 0x01 /* this quota locked (no MODS) */
#define DQ_WANT 0x02 /* wakeup on unlock */
#define DQ_MOD 0x04 /* this quota modified since read */
#define DQ_FAKE 0x08 /* no limits here, just usage */
#define DQ_BLKS 0x10 /* has been warned about blk limit */
#define DQ_INODS 0x20 /* has been warned about inode limit */
/*
* Shorthand notation.
*/
#define dq_bhardlimit dq_dqb.dqb_bhardlimit
#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
#define dq_curblocks dq_dqb.dqb_curblocks
#define dq_ihardlimit dq_dqb.dqb_ihardlimit
#define dq_isoftlimit dq_dqb.dqb_isoftlimit
#define dq_curinodes dq_dqb.dqb_curinodes
#define dq_btime dq_dqb.dqb_btime
#define dq_itime dq_dqb.dqb_itime
/*
* If the system has never checked for a quota for this file, then it is
* set to NODQUOT. Once a write attempt is made the inode pointer is set
* to reference a dquot structure.
*/
#define NODQUOT NULL
void dqref(struct dquot *);
void dqrele(struct vnode *, struct dquot *);
int dqsync(struct vnode *, struct dquot *);
#ifdef DIAGNOSTIC
void chkdquot(struct inode *);
#endif
int getquota(struct mount *, u_long, int, caddr_t);
int quotaon(struct proc *, struct mount *, int, caddr_t);
int setquota(struct mount *, u_long, int, caddr_t);
int setuse(struct mount *, u_long, int, caddr_t);
int chkdqchg(struct inode *, long, struct ucred *, int);
int chkiqchg(struct inode *, long, struct ucred *, int);
int dqget(struct vnode *, u_long, struct ufsmount *, int,
struct dquot **);
int quotaon_vnode(struct vnode *, void *);
int quotaoff_vnode(struct vnode *, void *);
int qsync_vnode(struct vnode *, void *);
/*
* Quota name to error message mapping.
*/
static char *quotatypes[] = INITQFNAMES;
/*
* Obtain a reference to a dquot.
*/
void
dqref(struct dquot *dq)
{
dq->dq_cnt++;
}
/*
* Set up the quotas for an inode.
*
* This routine completely defines the semantics of quotas.
* If other criterion want to be used to establish quotas, the
* MAXQUOTAS value in quotas.h should be increased, and the
* additional dquots set up here.
*/
int
getinoquota(struct inode *ip)
{
struct ufsmount *ump;
struct vnode *vp = ITOV(ip);
int error;
ump = ip->i_ump;
/*
* Set up the user quota based on file uid.
* EINVAL means that quotas are not enabled.
*/
if (ip->i_dquot[USRQUOTA] == NODQUOT &&
(error =
dqget(vp, DIP(ip, uid), ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
error != EINVAL)
return (error);
/*
* Set up the group quota based on file gid.
* EINVAL means that quotas are not enabled.
*/
if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
(error =
dqget(vp, DIP(ip, gid), ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
error != EINVAL)
return (error);
return (0);
}
/*
* Update disk usage, and take corrective action.
*/
int
ufs_quota_alloc_blocks2(struct inode *ip, daddr_t change,
struct ucred *cred, enum ufs_quota_flags flags)
{
struct dquot *dq;
int i;
int error;
#ifdef DIAGNOSTIC
chkdquot(ip);
#endif
if (change == 0)
return (0);
if ((flags & UFS_QUOTA_FORCE) == 0 &&
(cred != NOCRED && cred->cr_uid != 0)) {
for (i = 0; i < MAXQUOTAS; i++) {
if (flags & (1 << i))
continue;
if ((dq = ip->i_dquot[i]) == NODQUOT)
continue;
if ((error = chkdqchg(ip, change, cred, i)) != 0)
return (error);
}
}
for (i = 0; i < MAXQUOTAS; i++) {
if (flags & (1 << i))
continue;
if ((dq = ip->i_dquot[i]) == NODQUOT)
continue;
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+1, "chkdq", INFSLP);
}
dq->dq_curblocks += change;
dq->dq_flags |= DQ_MOD;
}
return (0);
}
int
ufs_quota_free_blocks2(struct inode *ip, daddr_t change,
struct ucred *cred, enum ufs_quota_flags flags)
{
struct dquot *dq;
int i;
#ifdef DIAGNOSTIC
if (!VOP_ISLOCKED(ITOV(ip)))
panic ("ufs_quota_free_blocks2: vnode is not locked");
#endif
if (change == 0)
return (0);
for (i = 0; i < MAXQUOTAS; i++) {
if (flags & (1 << i))
continue;
if ((dq = ip->i_dquot[i]) == NODQUOT)
continue;
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+1, "chkdq", INFSLP);
}
if (dq->dq_curblocks >= change)
dq->dq_curblocks -= change;
else
dq->dq_curblocks = 0;
dq->dq_flags &= ~DQ_BLKS;
dq->dq_flags |= DQ_MOD;
}
return (0);
}
/*
* Check for a valid change to a users allocation.
* Issue an error message if appropriate.
*/
int
chkdqchg(struct inode *ip, long change, struct ucred *cred, int type)
{
struct dquot *dq = ip->i_dquot[type];
long ncurblocks = dq->dq_curblocks + change;
/*
* If user would exceed their hard limit, disallow space allocation.
*/
if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
if ((dq->dq_flags & DQ_BLKS) == 0 &&
DIP(ip, uid) == cred->cr_uid) {
uprintf("\n%s: write failed, %s disk limit reached\n",
ITOV(ip)->v_mount->mnt_stat.f_mntonname,
quotatypes[type]);
dq->dq_flags |= DQ_BLKS;
}
return (EDQUOT);
}
/*
* If user is over their soft limit for too long, disallow space
* allocation. Reset time limit as they cross their soft limit.
*/
if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
if (dq->dq_curblocks < dq->dq_bsoftlimit) {
dq->dq_btime = gettime() + ip->i_ump->um_btime[type];
if (DIP(ip, uid) == cred->cr_uid)
uprintf("\n%s: warning, %s %s\n",
ITOV(ip)->v_mount->mnt_stat.f_mntonname,
quotatypes[type], "disk quota exceeded");
return (0);
}
if (gettime() > dq->dq_btime) {
if ((dq->dq_flags & DQ_BLKS) == 0 &&
DIP(ip, uid) == cred->cr_uid) {
uprintf("\n%s: write failed, %s %s\n",
ITOV(ip)->v_mount->mnt_stat.f_mntonname,
quotatypes[type],
"disk quota exceeded for too long");
dq->dq_flags |= DQ_BLKS;
}
return (EDQUOT);
}
}
return (0);
}
/*
* Check the inode limit, applying corrective action.
*/
int
ufs_quota_alloc_inode2(struct inode *ip, struct ucred *cred,
enum ufs_quota_flags flags)
{
struct dquot *dq;
int i;
int error;
#ifdef DIAGNOSTIC
chkdquot(ip);
#endif
if ((flags & UFS_QUOTA_FORCE) == 0 && cred->cr_uid != 0) {
for (i = 0; i < MAXQUOTAS; i++) {
if (flags & (1 << i))
continue;
if ((dq = ip->i_dquot[i]) == NODQUOT)
continue;
if ((error = chkiqchg(ip, 1, cred, i)) != 0)
return (error);
}
}
for (i = 0; i < MAXQUOTAS; i++) {
if (flags & (1 << i))
continue;
if ((dq = ip->i_dquot[i]) == NODQUOT)
continue;
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+1, "chkiq", INFSLP);
}
dq->dq_curinodes++;
dq->dq_flags |= DQ_MOD;
}
return (0);
}
int
ufs_quota_free_inode2(struct inode *ip, struct ucred *cred,
enum ufs_quota_flags flags)
{
struct dquot *dq;
int i;
#ifdef DIAGNOSTIC
if (!VOP_ISLOCKED(ITOV(ip)))
panic ("ufs_quota_free_blocks2: vnode is not locked");
#endif
for (i = 0; i < MAXQUOTAS; i++) {
if (flags & (1 << i))
continue;
if ((dq = ip->i_dquot[i]) == NODQUOT)
continue;
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+1, "chkiq", INFSLP);
}
if (dq->dq_curinodes > 0)
dq->dq_curinodes--;
dq->dq_flags &= ~DQ_INODS;
dq->dq_flags |= DQ_MOD;
}
return (0);
}
/*
* Check for a valid change to a users allocation.
* Issue an error message if appropriate.
*/
int
chkiqchg(struct inode *ip, long change, struct ucred *cred, int type)
{
struct dquot *dq = ip->i_dquot[type];
long ncurinodes = dq->dq_curinodes + change;
/*
* If user would exceed their hard limit, disallow inode allocation.
*/
if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
if ((dq->dq_flags & DQ_INODS) == 0 &&
DIP(ip, uid) == cred->cr_uid) {
uprintf("\n%s: write failed, %s inode limit reached\n",
ITOV(ip)->v_mount->mnt_stat.f_mntonname,
quotatypes[type]);
dq->dq_flags |= DQ_INODS;
}
return (EDQUOT);
}
/*
* If user is over their soft limit for too long, disallow inode
* allocation. Reset time limit as they cross their soft limit.
*/
if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
if (dq->dq_curinodes < dq->dq_isoftlimit) {
dq->dq_itime = gettime() + ip->i_ump->um_itime[type];
if (DIP(ip, uid) == cred->cr_uid)
uprintf("\n%s: warning, %s %s\n",
ITOV(ip)->v_mount->mnt_stat.f_mntonname,
quotatypes[type], "inode quota exceeded");
return (0);
}
if (gettime() > dq->dq_itime) {
if ((dq->dq_flags & DQ_INODS) == 0 &&
DIP(ip, uid) == cred->cr_uid) {
uprintf("\n%s: write failed, %s %s\n",
ITOV(ip)->v_mount->mnt_stat.f_mntonname,
quotatypes[type],
"inode quota exceeded for too long");
dq->dq_flags |= DQ_INODS;
}
return (EDQUOT);
}
}
return (0);
}
#ifdef DIAGNOSTIC
/*
* On filesystems with quotas enabled, it is an error for a file to change
* size and not to have a dquot structure associated with it.
*/
void
chkdquot(struct inode *ip)
{
struct ufsmount *ump = ip->i_ump;
int i;
struct vnode *vp = ITOV(ip);
if (!VOP_ISLOCKED(vp))
panic ("chkdquot: vnode is not locked");
for (i = 0; i < MAXQUOTAS; i++) {
if (ump->um_quotas[i] == NULLVP ||
(ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
continue;
if (ip->i_dquot[i] == NODQUOT) {
vprint("chkdquot: missing dquot", ITOV(ip));
panic("missing dquot");
}
}
}
#endif
/*
* Code to process quotactl commands.
*/
int
quotaon_vnode(struct vnode *vp, void *arg)
{
int error;
if (vp->v_type == VNON || vp->v_writecount == 0)
return (0);
if (vget(vp, LK_EXCLUSIVE)) {
return (0);
}
error = getinoquota(VTOI(vp));
vput(vp);
return (error);
}
/*
* Q_QUOTAON - set up a quota file for a particular file system.
*/
int
quotaon(struct proc *p, struct mount *mp, int type, caddr_t fname)
{
struct ufsmount *ump = VFSTOUFS(mp);
struct vnode *vp, **vpp;
struct dquot *dq;
int error;
struct nameidata nd;
#ifdef DIAGNOSTIC
if (!vfs_isbusy(mp))
panic ("quotaon: mount point not busy");
#endif
vpp = &ump->um_quotas[type];
NDINIT(&nd, 0, 0, UIO_USERSPACE, fname, p);
if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0)
return (error);
vp = nd.ni_vp;
VOP_UNLOCK(vp);
if (vp->v_type != VREG) {
(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
return (EACCES);
}
/*
* Update the vnode and ucred for quota file updates
*/
if (*vpp != vp) {
quotaoff(p, mp, type);
*vpp = vp;
crhold(p->p_ucred);
ump->um_cred[type] = p->p_ucred;
} else {
struct ucred *ocred = ump->um_cred[type];
(void) vn_close(vp, FREAD|FWRITE, ocred, p);
if (ocred != p->p_ucred) {
crhold(p->p_ucred);
ump->um_cred[type] = p->p_ucred;
crfree(ocred);
}
}
ump->um_qflags[type] |= QTF_OPENING;
mp->mnt_flag |= MNT_QUOTA;
vp->v_flag |= VSYSTEM;
/*
* Set up the time limits for this quota.
*/
ump->um_btime[type] = MAX_DQ_TIME;
ump->um_itime[type] = MAX_IQ_TIME;
if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
if (dq->dq_btime > 0)
ump->um_btime[type] = dq->dq_btime;
if (dq->dq_itime > 0)
ump->um_itime[type] = dq->dq_itime;
dqrele(NULLVP, dq);
}
/*
* Search vnodes associated with this mount point,
* adding references to quota file being opened.
* NB: only need to add dquot's for inodes being modified.
*/
error = vfs_mount_foreach_vnode(mp, quotaon_vnode, NULL);
ump->um_qflags[type] &= ~QTF_OPENING;
if (error)
quotaoff(p, mp, type);
return (error);
}
struct quotaoff_arg {
struct proc *p;
int type;
};
int
quotaoff_vnode(struct vnode *vp, void *arg)
{
struct quotaoff_arg *qa = (struct quotaoff_arg *)arg;
struct inode *ip;
struct dquot *dq;
if (vp->v_type == VNON)
return (0);
if (vget(vp, LK_EXCLUSIVE))
return (0);
ip = VTOI(vp);
dq = ip->i_dquot[qa->type];
ip->i_dquot[qa->type] = NODQUOT;
dqrele(vp, dq);
vput(vp);
return (0);
}
/*
* Q_QUOTAOFF - turn off disk quotas for a filesystem.
*/
int
quotaoff(struct proc *p, struct mount *mp, int type)
{
struct vnode *qvp;
struct ufsmount *ump = VFSTOUFS(mp);
struct quotaoff_arg qa;
int error;
#ifdef DIAGNOSTIC
if (!vfs_isbusy(mp))
panic ("quotaoff: mount point not busy");
#endif
if ((qvp = ump->um_quotas[type]) == NULLVP)
return (0);
ump->um_qflags[type] |= QTF_CLOSING;
/*
* Search vnodes associated with this mount point,
* deleting any references to quota file being closed.
*/
qa.p = p;
qa.type = type;
vfs_mount_foreach_vnode(mp, quotaoff_vnode, &qa);
error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
ump->um_quotas[type] = NULLVP;
crfree(ump->um_cred[type]);
ump->um_cred[type] = NOCRED;
ump->um_qflags[type] &= ~QTF_CLOSING;
for (type = 0; type < MAXQUOTAS; type++)
if (ump->um_quotas[type] != NULLVP)
break;
if (type == MAXQUOTAS)
mp->mnt_flag &= ~MNT_QUOTA;
return (error);
}
/*
* Q_GETQUOTA - return current values in a dqblk structure.
*/
int
getquota(struct mount *mp, u_long id, int type, caddr_t addr)
{
struct dquot *dq;
int error;
if ((error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) != 0)
return (error);
error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
#ifdef KTRACE
if (error == 0) {
struct proc *p = curproc;
if (KTRPOINT(p, KTR_STRUCT))
ktrquota(p, &dq->dq_dqb);
}
#endif
dqrele(NULLVP, dq);
return (error);
}
/*
* Q_SETQUOTA - assign an entire dqblk structure.
*/
int
setquota(struct mount *mp, u_long id, int type, caddr_t addr)
{
struct dquot *dq;
struct dquot *ndq;
struct ufsmount *ump = VFSTOUFS(mp);
struct dqblk newlim;
int error;
error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
if (error)
return (error);
#ifdef KTRACE
{
struct proc *p = curproc;
if (KTRPOINT(p, KTR_STRUCT))
ktrquota(p, &newlim);
}
#endif
if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
return (error);
dq = ndq;
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+1, "setquota", INFSLP);
}
/*
* Copy all but the current values.
* Reset time limit if previously had no soft limit or were
* under it, but now have a soft limit and are over it.
*/
newlim.dqb_curblocks = dq->dq_curblocks;
newlim.dqb_curinodes = dq->dq_curinodes;
if (dq->dq_id != 0) {
newlim.dqb_btime = dq->dq_btime;
newlim.dqb_itime = dq->dq_itime;
}
if (newlim.dqb_bsoftlimit &&
dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
(dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
newlim.dqb_btime = gettime() + ump->um_btime[type];
if (newlim.dqb_isoftlimit &&
dq->dq_curinodes >= newlim.dqb_isoftlimit &&
(dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
newlim.dqb_itime = gettime() + ump->um_itime[type];
dq->dq_dqb = newlim;
if (dq->dq_curblocks < dq->dq_bsoftlimit)
dq->dq_flags &= ~DQ_BLKS;
if (dq->dq_curinodes < dq->dq_isoftlimit)
dq->dq_flags &= ~DQ_INODS;
if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
dq->dq_flags |= DQ_FAKE;
else
dq->dq_flags &= ~DQ_FAKE;
dq->dq_flags |= DQ_MOD;
dqrele(NULLVP, dq);
return (0);
}
/*
* Q_SETUSE - set current inode and block usage.
*/
int
setuse(struct mount *mp, u_long id, int type, caddr_t addr)
{
struct dquot *dq;
struct ufsmount *ump = VFSTOUFS(mp);
struct dquot *ndq;
struct dqblk usage;
int error;
error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
if (error)
return (error);
#ifdef KTRACE
{
struct proc *p = curproc;
if (KTRPOINT(p, KTR_STRUCT))
ktrquota(p, &usage);
}
#endif
if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
return (error);
dq = ndq;
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+1, "setuse", INFSLP);
}
/*
* Reset time limit if have a soft limit and were
* previously under it, but are now over it.
*/
if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
usage.dqb_curblocks >= dq->dq_bsoftlimit)
dq->dq_btime = gettime() + ump->um_btime[type];
if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
usage.dqb_curinodes >= dq->dq_isoftlimit)
dq->dq_itime = gettime() + ump->um_itime[type];
dq->dq_curblocks = usage.dqb_curblocks;
dq->dq_curinodes = usage.dqb_curinodes;
if (dq->dq_curblocks < dq->dq_bsoftlimit)
dq->dq_flags &= ~DQ_BLKS;
if (dq->dq_curinodes < dq->dq_isoftlimit)
dq->dq_flags &= ~DQ_INODS;
dq->dq_flags |= DQ_MOD;
dqrele(NULLVP, dq);
return (0);
}
int
qsync_vnode(struct vnode *vp, void *arg)
{
int i;
struct dquot *dq;
if (vp->v_type == VNON)
return (0);
if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT))
return (0);
for (i = 0; i < MAXQUOTAS; i++) {
dq = VTOI(vp)->i_dquot[i];
if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
dqsync(vp, dq);
}
vput(vp);
return (0);
}
/*
* Q_SYNC - sync quota files to disk.
*/
int
qsync(struct mount *mp)
{
struct ufsmount *ump = VFSTOUFS(mp);
int i;
/*
* Check if the mount point has any quotas.
* If not, simply return.
*/
for (i = 0; i < MAXQUOTAS; i++)
if (ump->um_quotas[i] != NULLVP)
break;
if (i == MAXQUOTAS)
return (0);
/*
* Search vnodes associated with this mount point,
* synchronizing any modified dquot structures.
*/
vfs_mount_foreach_vnode(mp, qsync_vnode, NULL);
return (0);
}
/*
* Code pertaining to management of the in-core dquot data structures.
*/
LIST_HEAD(dqhash, dquot) *dqhashtbl;
SIPHASH_KEY dqhashkey;
u_long dqhash;
/*
* Dquot free list.
*/
#define DQUOTINC 5 /* minimum free dquots desired */
TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
long numdquot, desireddquot = DQUOTINC;
/*
* Initialize the quota system.
*/
void
ufs_quota_init(void)
{
dqhashtbl = hashinit(initialvnodes, M_DQUOT, M_WAITOK, &dqhash);
arc4random_buf(&dqhashkey, sizeof(dqhashkey));
TAILQ_INIT(&dqfreelist);
}
/*
* Obtain a dquot structure for the specified identifier and quota file
* reading the information from the file if necessary.
*/
int
dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
struct dquot **dqp)
{
SIPHASH_CTX ctx;
struct dquot *dq;
struct dqhash *dqh;
struct vnode *dqvp;
struct iovec aiov;
struct uio auio;
int error;
dqvp = ump->um_quotas[type];
if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
*dqp = NODQUOT;
return (EINVAL);
}
/*
* Check the cache first.
*/
SipHash24_Init(&ctx, &dqhashkey);
SipHash24_Update(&ctx, &dqvp, sizeof(dqvp));
SipHash24_Update(&ctx, &id, sizeof(id));
dqh = &dqhashtbl[SipHash24_End(&ctx) & dqhash];
LIST_FOREACH(dq, dqh, dq_hash) {
if (dq->dq_id != id ||
dq->dq_vp != dqvp)
continue;
/*
* Cache hit with no references. Take
* the structure off the free list.
*/
if (dq->dq_cnt == 0)
TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
dqref(dq);
*dqp = dq;
return (0);
}
/*
* Not in cache, allocate a new one.
*/
if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
numdquot < MAXQUOTAS * initialvnodes)
desireddquot += DQUOTINC;
if (numdquot < desireddquot) {
dq = malloc(sizeof *dq, M_DQUOT, M_WAITOK | M_ZERO);
numdquot++;
} else {
if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
tablefull("dquot");
*dqp = NODQUOT;
return (EUSERS);
}
if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
panic("free dquot isn't");
TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
LIST_REMOVE(dq, dq_hash);
crfree(dq->dq_cred);
dq->dq_cred = NOCRED;
}
/*
* Initialize the contents of the dquot structure.
*/
if (vp != dqvp)
vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
LIST_INSERT_HEAD(dqh, dq, dq_hash);
dqref(dq);
dq->dq_flags = DQ_LOCK;
dq->dq_id = id;
dq->dq_vp = dqvp;
dq->dq_type = type;
crhold(ump->um_cred[type]);
dq->dq_cred = ump->um_cred[type];
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = (caddr_t)&dq->dq_dqb;
aiov.iov_len = sizeof (struct dqblk);
auio.uio_resid = sizeof (struct dqblk);
auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_procp = NULL;
error = VOP_READ(dqvp, &auio, 0, dq->dq_cred);
if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
memset(&dq->dq_dqb, 0, sizeof(struct dqblk));
if (vp != dqvp)
VOP_UNLOCK(dqvp);
if (dq->dq_flags & DQ_WANT)
wakeup(dq);
dq->dq_flags = 0;
/*
* I/O error in reading quota file, release
* quota structure and reflect problem to caller.
*/
if (error) {
LIST_REMOVE(dq, dq_hash);
dqrele(vp, dq);
*dqp = NODQUOT;
return (error);
}
/*
* Check for no limit to enforce.
* Initialize time values if necessary.
*/
if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
dq->dq_flags |= DQ_FAKE;
if (dq->dq_id != 0) {
if (dq->dq_btime == 0)
dq->dq_btime = gettime() + ump->um_btime[type];
if (dq->dq_itime == 0)
dq->dq_itime = gettime() + ump->um_itime[type];
}
*dqp = dq;
return (0);
}
/*
* Release a reference to a dquot.
*/
void
dqrele(struct vnode *vp, struct dquot *dq)
{
if (dq == NODQUOT)
return;
if (dq->dq_cnt > 1) {
dq->dq_cnt--;
return;
}
if (dq->dq_flags & DQ_MOD)
(void) dqsync(vp, dq);
if (--dq->dq_cnt > 0)
return;
TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
}
/*
* Update the disk quota in the quota file.
*/
int
dqsync(struct vnode *vp, struct dquot *dq)
{
struct vnode *dqvp;
struct iovec aiov;
struct uio auio;
int error;
if (dq == NODQUOT)
panic("dqsync: dquot");
if ((dq->dq_flags & DQ_MOD) == 0)
return (0);
if ((dqvp = dq->dq_vp) == NULLVP)
panic("dqsync: file");
if (vp != dqvp)
vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
while (dq->dq_flags & DQ_LOCK) {
dq->dq_flags |= DQ_WANT;
tsleep_nsec(dq, PINOD+2, "dqsync", INFSLP);
if ((dq->dq_flags & DQ_MOD) == 0) {
if (vp != dqvp)
VOP_UNLOCK(dqvp);
return (0);
}
}
dq->dq_flags |= DQ_LOCK;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = (caddr_t)&dq->dq_dqb;
aiov.iov_len = sizeof (struct dqblk);
auio.uio_resid = sizeof (struct dqblk);
auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_procp = NULL;
error = VOP_WRITE(dqvp, &auio, 0, dq->dq_cred);
if (auio.uio_resid && error == 0)
error = EIO;
if (dq->dq_flags & DQ_WANT)
wakeup(dq);
dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
if (vp != dqvp)
VOP_UNLOCK(dqvp);
return (error);
}
int
ufs_quota_delete(struct inode *ip)
{
struct vnode *vp = ITOV(ip);
int i;
for (i = 0; i < MAXQUOTAS; i++) {
if (ip->i_dquot[i] != NODQUOT) {
dqrele(vp, ip->i_dquot[i]);
ip->i_dquot[i] = NODQUOT;
}
}
return (0);
}
/*
* Do operations associated with quotas
*/
int
ufs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
struct proc *p)
{
int cmd, type, error;
if (uid == -1)
uid = p->p_ucred->cr_ruid;
cmd = cmds >> SUBCMDSHIFT;
switch (cmd) {
case Q_SYNC:
break;
case Q_GETQUOTA:
if (uid == p->p_ucred->cr_ruid)
break;
/* FALLTHROUGH */
default:
if ((error = suser(p)) != 0)
return (error);
}
type = cmds & SUBCMDMASK;
if ((u_int)type >= MAXQUOTAS)
return (EINVAL);
if (vfs_busy(mp, VB_READ|VB_NOWAIT))
return (0);
switch (cmd) {
case Q_QUOTAON:
error = quotaon(p, mp, type, arg);
break;
case Q_QUOTAOFF:
error = quotaoff(p, mp, type);
break;
case Q_SETQUOTA:
error = setquota(mp, uid, type, arg) ;
break;
case Q_SETUSE:
error = setuse(mp, uid, type, arg);
break;
case Q_GETQUOTA:
error = getquota(mp, uid, type, arg);
break;
case Q_SYNC:
error = qsync(mp);
break;
default:
error = EINVAL;
break;
}
vfs_unbusy(mp);
return (error);
}
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* $OpenBSD: in_var.h,v 1.41 2018/10/18 15:23:04 cheloha Exp $ */
/* $NetBSD: in_var.h,v 1.16 1996/02/13 23:42:15 christos Exp $ */
/*
* Copyright (c) 1985, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_var.h 8.1 (Berkeley) 6/10/93
*/
#ifndef _NETINET_IN_VAR_H_
#define _NETINET_IN_VAR_H_
#include <sys/queue.h>
#ifdef _KERNEL
/*
* Interface address, Internet version. One of these structures
* is allocated for each interface with an Internet address.
* The ifaddr structure contains the protocol-independent part
* of the structure and is assumed to be first.
*/
struct in_ifaddr {
struct ifaddr ia_ifa; /* protocol-independent info */
#define ia_ifp ia_ifa.ifa_ifp
#define ia_flags ia_ifa.ifa_flags
/* ia_net{,mask} in host order */
u_int32_t ia_net; /* network number of interface */
u_int32_t ia_netmask; /* mask of net part */
TAILQ_ENTRY(in_ifaddr) ia_list; /* list of internet addresses */
struct sockaddr_in ia_addr; /* reserve space for interface name */
struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
#define ia_broadaddr ia_dstaddr
struct sockaddr_in ia_sockmask; /* reserve space for general netmask */
struct in_multi *ia_allhosts; /* multicast address record for
the allhosts multicast group */
};
#endif
struct in_aliasreq {
char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
union {
struct sockaddr_in ifrau_addr;
int ifrau_align;
} ifra_ifrau;
#ifndef ifra_addr
#define ifra_addr ifra_ifrau.ifrau_addr
#endif
struct sockaddr_in ifra_dstaddr;
#define ifra_broadaddr ifra_dstaddr
struct sockaddr_in ifra_mask;
};
#ifdef _KERNEL
/*
* Macro for finding the internet address structure (in_ifaddr) corresponding
* to a given interface (ifnet structure).
*/
#define IFP_TO_IA(ifp, ia) \
/* struct ifnet *ifp; */ \
/* struct in_ifaddr *ia; */ \
do { \
struct ifaddr *ifa; \
NET_ASSERT_LOCKED(); \
TAILQ_FOREACH(ifa, &(ifp)->if_addrlist, ifa_list) { \
if (ifa->ifa_addr->sa_family == AF_INET) \
break; \
} \
(ia) = ifatoia(ifa); \
} while (/* CONSTCOND */ 0)
#endif
/*
* Per-interface router version information.
*/
struct router_info {
unsigned int rti_ifidx;
int rti_type; /* type of router on this interface */
int rti_age; /* time since last v1 query */
LIST_ENTRY(router_info) rti_list;
};
#ifdef _KERNEL
/*
* Internet multicast address structure. There is one of these for each IP
* multicast group to which this host belongs on a given network interface.
*/
struct in_multi {
struct ifmaddr inm_ifma; /* Protocol-independent info */
#define inm_refcnt inm_ifma.ifma_refcnt
#define inm_ifidx inm_ifma.ifma_ifidx
struct sockaddr_in inm_sin; /* IPv4 multicast address */
#define inm_addr inm_sin.sin_addr
u_int inm_state; /* state of membership */
u_int inm_timer; /* IGMP membership report timer */
struct router_info *inm_rti; /* router version info */
};
static __inline struct in_multi *
ifmatoinm(struct ifmaddr *ifma)
{
return ((struct in_multi *)(ifma));
}
/*
* Macro for looking up the in_multi record for a given IP multicast
* address on a given interface. If no matching record is found, "inm"
* returns NULL.
*/
#define IN_LOOKUP_MULTI(addr, ifp, inm) \
/* struct in_addr addr; */ \
/* struct ifnet *ifp; */ \
/* struct in_multi *inm; */ \
do { \
struct ifmaddr *ifma; \
\
(inm) = NULL; \
NET_ASSERT_LOCKED(); \
TAILQ_FOREACH(ifma, &(ifp)->if_maddrlist, ifma_list) \
if (ifma->ifma_addr->sa_family == AF_INET && \
ifmatoinm(ifma)->inm_addr.s_addr == (addr).s_addr) {\
(inm) = ifmatoinm(ifma); \
break; \
} \
} while (/* CONSTCOND */ 0)
int in_ifinit(struct ifnet *,
struct in_ifaddr *, struct sockaddr_in *, int);
struct in_multi *in_addmulti(struct in_addr *, struct ifnet *);
void in_delmulti(struct in_multi *);
int in_hasmulti(struct in_addr *, struct ifnet *);
void in_ifscrub(struct ifnet *, struct in_ifaddr *);
int in_control(struct socket *, u_long, caddr_t, struct ifnet *);
int in_ioctl(u_long, caddr_t, struct ifnet *, int);
void in_prefixlen2mask(struct in_addr *, int);
#endif
#endif /* _NETINET_IN_VAR_H_ */
88
89
19
5
3
5
5
5
11
12
5
5
5
12
12
518
510
7
2
5
3
8
7
1
1
8958
8957
8956
8955
8941
8900
182
578
578
571
182
175
1
178
11
1
180
517
58
58
58
58
58
1
14
58
58
58
58
58
27
22
27
12
20
2
22
22
195
183
4
2
2
41
14
22
5
2
2
1
2
2
14
3
16
15
1
9
8
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
/* $OpenBSD: kern_sig.c,v 1.299 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */
/*
* Copyright (c) 1997 Theo de Raadt. All rights reserved.
* Copyright (c) 1982, 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_sig.c 8.7 (Berkeley) 4/18/94
*/
#include <sys/param.h>
#include <sys/signalvar.h>
#include <sys/queue.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/event.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/acct.h>
#include <sys/fcntl.h>
#include <sys/filedesc.h>
#include <sys/wait.h>
#include <sys/ktrace.h>
#include <sys/stat.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/sched.h>
#include <sys/user.h>
#include <sys/syslog.h>
#include <sys/ttycom.h>
#include <sys/pledge.h>
#include <sys/witness.h>
#include <sys/exec_elf.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
#include <machine/tcb.h>
int nosuidcoredump = 1;
int filt_sigattach(struct knote *kn);
void filt_sigdetach(struct knote *kn);
int filt_signal(struct knote *kn, long hint);
const struct filterops sig_filtops = {
.f_flags = 0,
.f_attach = filt_sigattach,
.f_detach = filt_sigdetach,
.f_event = filt_signal,
};
/*
* The array below categorizes the signals and their default actions.
*/
const int sigprop[NSIG] = {
0, /* unused */
SA_KILL, /* SIGHUP */
SA_KILL, /* SIGINT */
SA_KILL|SA_CORE, /* SIGQUIT */
SA_KILL|SA_CORE, /* SIGILL */
SA_KILL|SA_CORE, /* SIGTRAP */
SA_KILL|SA_CORE, /* SIGABRT */
SA_KILL|SA_CORE, /* SIGEMT */
SA_KILL|SA_CORE, /* SIGFPE */
SA_KILL, /* SIGKILL */
SA_KILL|SA_CORE, /* SIGBUS */
SA_KILL|SA_CORE, /* SIGSEGV */
SA_KILL|SA_CORE, /* SIGSYS */
SA_KILL, /* SIGPIPE */
SA_KILL, /* SIGALRM */
SA_KILL, /* SIGTERM */
SA_IGNORE, /* SIGURG */
SA_STOP, /* SIGSTOP */
SA_STOP|SA_TTYSTOP, /* SIGTSTP */
SA_IGNORE|SA_CONT, /* SIGCONT */
SA_IGNORE, /* SIGCHLD */
SA_STOP|SA_TTYSTOP, /* SIGTTIN */
SA_STOP|SA_TTYSTOP, /* SIGTTOU */
SA_IGNORE, /* SIGIO */
SA_KILL, /* SIGXCPU */
SA_KILL, /* SIGXFSZ */
SA_KILL, /* SIGVTALRM */
SA_KILL, /* SIGPROF */
SA_IGNORE, /* SIGWINCH */
SA_IGNORE, /* SIGINFO */
SA_KILL, /* SIGUSR1 */
SA_KILL, /* SIGUSR2 */
SA_IGNORE, /* SIGTHR */
};
#define CONTSIGMASK (sigmask(SIGCONT))
#define STOPSIGMASK (sigmask(SIGSTOP) | sigmask(SIGTSTP) | \
sigmask(SIGTTIN) | sigmask(SIGTTOU))
void setsigvec(struct proc *, int, struct sigaction *);
void proc_stop(struct proc *p, int);
void proc_stop_sweep(void *);
void *proc_stop_si;
void setsigctx(struct proc *, int, struct sigctx *);
void postsig_done(struct proc *, int, sigset_t, int);
void postsig(struct proc *, int, struct sigctx *);
int cansignal(struct proc *, struct process *, int);
struct pool sigacts_pool; /* memory pool for sigacts structures */
void sigio_del(struct sigiolst *);
void sigio_unlink(struct sigio_ref *, struct sigiolst *);
struct mutex sigio_lock = MUTEX_INITIALIZER(IPL_HIGH);
/*
* Can thread p, send the signal signum to process qr?
*/
int
cansignal(struct proc *p, struct process *qr, int signum)
{
struct process *pr = p->p_p;
struct ucred *uc = p->p_ucred;
struct ucred *quc = qr->ps_ucred;
if (uc->cr_uid == 0)
return (1); /* root can always signal */
if (pr == qr)
return (1); /* process can always signal itself */
/* optimization: if the same creds then the tests below will pass */
if (uc == quc)
return (1);
if (signum == SIGCONT && qr->ps_session == pr->ps_session)
return (1); /* SIGCONT in session */
/*
* Using kill(), only certain signals can be sent to setugid
* child processes
*/
if (qr->ps_flags & PS_SUGID) {
switch (signum) {
case 0:
case SIGKILL:
case SIGINT:
case SIGTERM:
case SIGALRM:
case SIGSTOP:
case SIGTTIN:
case SIGTTOU:
case SIGTSTP:
case SIGHUP:
case SIGUSR1:
case SIGUSR2:
if (uc->cr_ruid == quc->cr_ruid ||
uc->cr_uid == quc->cr_ruid)
return (1);
}
return (0);
}
if (uc->cr_ruid == quc->cr_ruid ||
uc->cr_ruid == quc->cr_svuid ||
uc->cr_uid == quc->cr_ruid ||
uc->cr_uid == quc->cr_svuid)
return (1);
return (0);
}
/*
* Initialize signal-related data structures.
*/
void
signal_init(void)
{
proc_stop_si = softintr_establish(IPL_SOFTCLOCK, proc_stop_sweep,
NULL);
if (proc_stop_si == NULL)
panic("signal_init failed to register softintr");
pool_init(&sigacts_pool, sizeof(struct sigacts), 0, IPL_NONE,
PR_WAITOK, "sigapl", NULL);
}
/*
* Initialize a new sigaltstack structure.
*/
void
sigstkinit(struct sigaltstack *ss)
{
ss->ss_flags = SS_DISABLE;
ss->ss_size = 0;
ss->ss_sp = NULL;
}
/*
* Create an initial sigacts structure, using the same signal state
* as pr.
*/
struct sigacts *
sigactsinit(struct process *pr)
{
struct sigacts *ps;
ps = pool_get(&sigacts_pool, PR_WAITOK);
memcpy(ps, pr->ps_sigacts, sizeof(struct sigacts));
return (ps);
}
/*
* Release a sigacts structure.
*/
void
sigactsfree(struct sigacts *ps)
{
pool_put(&sigacts_pool, ps);
}
int
sys_sigaction(struct proc *p, void *v, register_t *retval)
{
struct sys_sigaction_args /* {
syscallarg(int) signum;
syscallarg(const struct sigaction *) nsa;
syscallarg(struct sigaction *) osa;
} */ *uap = v;
struct sigaction vec;
#ifdef KTRACE
struct sigaction ovec;
#endif
struct sigaction *sa;
const struct sigaction *nsa;
struct sigaction *osa;
struct sigacts *ps = p->p_p->ps_sigacts;
int signum;
int bit, error;
signum = SCARG(uap, signum);
nsa = SCARG(uap, nsa);
osa = SCARG(uap, osa);
if (signum <= 0 || signum >= NSIG ||
(nsa && (signum == SIGKILL || signum == SIGSTOP)))
return (EINVAL);
sa = &vec;
if (osa) {
mtx_enter(&p->p_p->ps_mtx);
sa->sa_handler = ps->ps_sigact[signum];
sa->sa_mask = ps->ps_catchmask[signum];
bit = sigmask(signum);
sa->sa_flags = 0;
if ((ps->ps_sigonstack & bit) != 0)
sa->sa_flags |= SA_ONSTACK;
if ((ps->ps_sigintr & bit) == 0)
sa->sa_flags |= SA_RESTART;
if ((ps->ps_sigreset & bit) != 0)
sa->sa_flags |= SA_RESETHAND;
if ((ps->ps_siginfo & bit) != 0)
sa->sa_flags |= SA_SIGINFO;
if (signum == SIGCHLD) {
if ((ps->ps_sigflags & SAS_NOCLDSTOP) != 0)
sa->sa_flags |= SA_NOCLDSTOP;
if ((ps->ps_sigflags & SAS_NOCLDWAIT) != 0)
sa->sa_flags |= SA_NOCLDWAIT;
}
mtx_leave(&p->p_p->ps_mtx);
if ((sa->sa_mask & bit) == 0)
sa->sa_flags |= SA_NODEFER;
sa->sa_mask &= ~bit;
error = copyout(sa, osa, sizeof (vec));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ovec = vec;
#endif
}
if (nsa) {
error = copyin(nsa, sa, sizeof (vec));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrsigaction(p, sa);
#endif
setsigvec(p, signum, sa);
}
#ifdef KTRACE
if (osa && KTRPOINT(p, KTR_STRUCT))
ktrsigaction(p, &ovec);
#endif
return (0);
}
void
setsigvec(struct proc *p, int signum, struct sigaction *sa)
{
struct sigacts *ps = p->p_p->ps_sigacts;
int bit;
bit = sigmask(signum);
mtx_enter(&p->p_p->ps_mtx);
ps->ps_sigact[signum] = sa->sa_handler;
if ((sa->sa_flags & SA_NODEFER) == 0)
sa->sa_mask |= sigmask(signum);
ps->ps_catchmask[signum] = sa->sa_mask &~ sigcantmask;
if (signum == SIGCHLD) {
if (sa->sa_flags & SA_NOCLDSTOP)
atomic_setbits_int(&ps->ps_sigflags, SAS_NOCLDSTOP);
else
atomic_clearbits_int(&ps->ps_sigflags, SAS_NOCLDSTOP);
/*
* If the SA_NOCLDWAIT flag is set or the handler
* is SIG_IGN we reparent the dying child to PID 1
* (init) which will reap the zombie. Because we use
* init to do our dirty work we never set SAS_NOCLDWAIT
* for PID 1.
* XXX exit1 rework means this is unnecessary?
*/
if (initprocess->ps_sigacts != ps &&
((sa->sa_flags & SA_NOCLDWAIT) ||
sa->sa_handler == SIG_IGN))
atomic_setbits_int(&ps->ps_sigflags, SAS_NOCLDWAIT);
else
atomic_clearbits_int(&ps->ps_sigflags, SAS_NOCLDWAIT);
}
if ((sa->sa_flags & SA_RESETHAND) != 0)
ps->ps_sigreset |= bit;
else
ps->ps_sigreset &= ~bit;
if ((sa->sa_flags & SA_SIGINFO) != 0)
ps->ps_siginfo |= bit;
else
ps->ps_siginfo &= ~bit;
if ((sa->sa_flags & SA_RESTART) == 0)
ps->ps_sigintr |= bit;
else
ps->ps_sigintr &= ~bit;
if ((sa->sa_flags & SA_ONSTACK) != 0)
ps->ps_sigonstack |= bit;
else
ps->ps_sigonstack &= ~bit;
/*
* Set bit in ps_sigignore for signals that are set to SIG_IGN,
* and for signals set to SIG_DFL where the default is to ignore.
* However, don't put SIGCONT in ps_sigignore,
* as we have to restart the process.
*/
if (sa->sa_handler == SIG_IGN ||
(sigprop[signum] & SA_IGNORE && sa->sa_handler == SIG_DFL)) {
atomic_clearbits_int(&p->p_siglist, bit);
atomic_clearbits_int(&p->p_p->ps_siglist, bit);
if (signum != SIGCONT)
ps->ps_sigignore |= bit; /* easier in psignal */
ps->ps_sigcatch &= ~bit;
} else {
ps->ps_sigignore &= ~bit;
if (sa->sa_handler == SIG_DFL)
ps->ps_sigcatch &= ~bit;
else
ps->ps_sigcatch |= bit;
}
mtx_leave(&p->p_p->ps_mtx);
}
/*
* Initialize signal state for process 0;
* set to ignore signals that are ignored by default.
*/
void
siginit(struct sigacts *ps)
{
int i;
for (i = 0; i < NSIG; i++)
if (sigprop[i] & SA_IGNORE && i != SIGCONT)
ps->ps_sigignore |= sigmask(i);
ps->ps_sigflags = SAS_NOCLDWAIT | SAS_NOCLDSTOP;
}
/*
* Reset signals for an exec by the specified thread.
*/
void
execsigs(struct proc *p)
{
struct sigacts *ps;
int nc, mask;
ps = p->p_p->ps_sigacts;
mtx_enter(&p->p_p->ps_mtx);
/*
* Reset caught signals. Held signals remain held
* through p_sigmask (unless they were caught,
* and are now ignored by default).
*/
while (ps->ps_sigcatch) {
nc = ffs((long)ps->ps_sigcatch);
mask = sigmask(nc);
ps->ps_sigcatch &= ~mask;
if (sigprop[nc] & SA_IGNORE) {
if (nc != SIGCONT)
ps->ps_sigignore |= mask;
atomic_clearbits_int(&p->p_siglist, mask);
atomic_clearbits_int(&p->p_p->ps_siglist, mask);
}
ps->ps_sigact[nc] = SIG_DFL;
}
/*
* Reset stack state to the user stack.
* Clear set of signals caught on the signal stack.
*/
sigstkinit(&p->p_sigstk);
atomic_clearbits_int(&ps->ps_sigflags, SAS_NOCLDWAIT);
if (ps->ps_sigact[SIGCHLD] == SIG_IGN)
ps->ps_sigact[SIGCHLD] = SIG_DFL;
mtx_leave(&p->p_p->ps_mtx);
}
/*
* Manipulate signal mask.
* Note that we receive new mask, not pointer,
* and return old mask as return value;
* the library stub does the rest.
*/
int
sys_sigprocmask(struct proc *p, void *v, register_t *retval)
{
struct sys_sigprocmask_args /* {
syscallarg(int) how;
syscallarg(sigset_t) mask;
} */ *uap = v;
int error = 0;
sigset_t mask;
KASSERT(p == curproc);
*retval = p->p_sigmask;
mask = SCARG(uap, mask) &~ sigcantmask;
switch (SCARG(uap, how)) {
case SIG_BLOCK:
atomic_setbits_int(&p->p_sigmask, mask);
break;
case SIG_UNBLOCK:
atomic_clearbits_int(&p->p_sigmask, mask);
break;
case SIG_SETMASK:
p->p_sigmask = mask;
break;
default:
error = EINVAL;
break;
}
return (error);
}
int
sys_sigpending(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_siglist | p->p_p->ps_siglist;
return (0);
}
/*
* Temporarily replace calling proc's signal mask for the duration of a
* system call. Original signal mask will be restored by userret().
*/
void
dosigsuspend(struct proc *p, sigset_t newmask)
{
KASSERT(p == curproc);
p->p_oldmask = p->p_sigmask;
atomic_setbits_int(&p->p_flag, P_SIGSUSPEND);
p->p_sigmask = newmask;
}
/*
* Suspend thread until signal, providing mask to be set
* in the meantime. Note nonstandard calling convention:
* libc stub passes mask, not pointer, to save a copyin.
*/
int
sys_sigsuspend(struct proc *p, void *v, register_t *retval)
{
struct sys_sigsuspend_args /* {
syscallarg(int) mask;
} */ *uap = v;
dosigsuspend(p, SCARG(uap, mask) &~ sigcantmask);
while (tsleep_nsec(&nowake, PPAUSE|PCATCH, "sigsusp", INFSLP) == 0)
continue;
/* always return EINTR rather than ERESTART... */
return (EINTR);
}
int
sigonstack(size_t stack)
{
const struct sigaltstack *ss = &curproc->p_sigstk;
return (ss->ss_flags & SS_DISABLE ? 0 :
(stack - (size_t)ss->ss_sp < ss->ss_size));
}
int
sys_sigaltstack(struct proc *p, void *v, register_t *retval)
{
struct sys_sigaltstack_args /* {
syscallarg(const struct sigaltstack *) nss;
syscallarg(struct sigaltstack *) oss;
} */ *uap = v;
struct sigaltstack ss;
const struct sigaltstack *nss;
struct sigaltstack *oss;
int onstack = sigonstack(PROC_STACK(p));
int error;
nss = SCARG(uap, nss);
oss = SCARG(uap, oss);
if (oss != NULL) {
ss = p->p_sigstk;
if (onstack)
ss.ss_flags |= SS_ONSTACK;
if ((error = copyout(&ss, oss, sizeof(ss))))
return (error);
}
if (nss == NULL)
return (0);
error = copyin(nss, &ss, sizeof(ss));
if (error)
return (error);
if (onstack)
return (EPERM);
if (ss.ss_flags & ~SS_DISABLE)
return (EINVAL);
if (ss.ss_flags & SS_DISABLE) {
p->p_sigstk.ss_flags = ss.ss_flags;
return (0);
}
if (ss.ss_size < MINSIGSTKSZ)
return (ENOMEM);
error = uvm_map_remap_as_stack(p, (vaddr_t)ss.ss_sp, ss.ss_size);
if (error)
return (error);
p->p_sigstk = ss;
return (0);
}
int
sys_kill(struct proc *cp, void *v, register_t *retval)
{
struct sys_kill_args /* {
syscallarg(int) pid;
syscallarg(int) signum;
} */ *uap = v;
struct process *pr;
int pid = SCARG(uap, pid);
int signum = SCARG(uap, signum);
int error;
int zombie = 0;
if ((error = pledge_kill(cp, pid)) != 0)
return (error);
if (((u_int)signum) >= NSIG)
return (EINVAL);
if (pid > 0) {
if ((pr = prfind(pid)) == NULL) {
if ((pr = zombiefind(pid)) == NULL)
return (ESRCH);
else
zombie = 1;
}
if (!cansignal(cp, pr, signum))
return (EPERM);
/* kill single process */
if (signum && !zombie)
prsignal(pr, signum);
return (0);
}
switch (pid) {
case -1: /* broadcast signal */
return (killpg1(cp, signum, 0, 1));
case 0: /* signal own process group */
return (killpg1(cp, signum, 0, 0));
default: /* negative explicit process group */
return (killpg1(cp, signum, -pid, 0));
}
}
int
sys_thrkill(struct proc *cp, void *v, register_t *retval)
{
struct sys_thrkill_args /* {
syscallarg(pid_t) tid;
syscallarg(int) signum;
syscallarg(void *) tcb;
} */ *uap = v;
struct proc *p;
int tid = SCARG(uap, tid);
int signum = SCARG(uap, signum);
void *tcb;
if (((u_int)signum) >= NSIG)
return (EINVAL);
if (tid > THREAD_PID_OFFSET) {
if ((p = tfind(tid - THREAD_PID_OFFSET)) == NULL)
return (ESRCH);
/* can only kill threads in the same process */
if (p->p_p != cp->p_p)
return (ESRCH);
} else if (tid == 0)
p = cp;
else
return (EINVAL);
/* optionally require the target thread to have the given tcb addr */
tcb = SCARG(uap, tcb);
if (tcb != NULL && tcb != TCB_GET(p))
return (ESRCH);
if (signum)
ptsignal(p, signum, STHREAD);
return (0);
}
/*
* Common code for kill process group/broadcast kill.
* cp is calling process.
*/
int
killpg1(struct proc *cp, int signum, int pgid, int all)
{
struct process *pr;
struct pgrp *pgrp;
int nfound = 0;
if (all) {
/*
* broadcast
*/
LIST_FOREACH(pr, &allprocess, ps_list) {
if (pr->ps_pid <= 1 ||
pr->ps_flags & (PS_SYSTEM | PS_NOBROADCASTKILL) ||
pr == cp->p_p || !cansignal(cp, pr, signum))
continue;
nfound++;
if (signum)
prsignal(pr, signum);
}
} else {
if (pgid == 0)
/*
* zero pgid means send to my process group.
*/
pgrp = cp->p_p->ps_pgrp;
else {
pgrp = pgfind(pgid);
if (pgrp == NULL)
return (ESRCH);
}
LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
if (pr->ps_pid <= 1 || pr->ps_flags & PS_SYSTEM ||
!cansignal(cp, pr, signum))
continue;
nfound++;
if (signum)
prsignal(pr, signum);
}
}
return (nfound ? 0 : ESRCH);
}
#define CANDELIVER(uid, euid, pr) \
(euid == 0 || \
(uid) == (pr)->ps_ucred->cr_ruid || \
(uid) == (pr)->ps_ucred->cr_svuid || \
(uid) == (pr)->ps_ucred->cr_uid || \
(euid) == (pr)->ps_ucred->cr_ruid || \
(euid) == (pr)->ps_ucred->cr_svuid || \
(euid) == (pr)->ps_ucred->cr_uid)
#define CANSIGIO(cr, pr) \
CANDELIVER((cr)->cr_ruid, (cr)->cr_uid, (pr))
/*
* Send a signal to a process group. If checktty is 1,
* limit to members which have a controlling terminal.
*/
void
pgsignal(struct pgrp *pgrp, int signum, int checkctty)
{
struct process *pr;
if (pgrp)
LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist)
if (checkctty == 0 || pr->ps_flags & PS_CONTROLT)
prsignal(pr, signum);
}
/*
* Send a SIGIO or SIGURG signal to a process or process group using stored
* credentials rather than those of the current process.
*/
void
pgsigio(struct sigio_ref *sir, int sig, int checkctty)
{
struct process *pr;
struct sigio *sigio;
if (sir->sir_sigio == NULL)
return;
KERNEL_LOCK();
mtx_enter(&sigio_lock);
sigio = sir->sir_sigio;
if (sigio == NULL)
goto out;
if (sigio->sio_pgid > 0) {
if (CANSIGIO(sigio->sio_ucred, sigio->sio_proc))
prsignal(sigio->sio_proc, sig);
} else if (sigio->sio_pgid < 0) {
LIST_FOREACH(pr, &sigio->sio_pgrp->pg_members, ps_pglist) {
if (CANSIGIO(sigio->sio_ucred, pr) &&
(checkctty == 0 || (pr->ps_flags & PS_CONTROLT)))
prsignal(pr, sig);
}
}
out:
mtx_leave(&sigio_lock);
KERNEL_UNLOCK();
}
/*
* Recalculate the signal mask and reset the signal disposition after
* usermode frame for delivery is formed.
*/
void
postsig_done(struct proc *p, int signum, sigset_t catchmask, int reset)
{
p->p_ru.ru_nsignals++;
atomic_setbits_int(&p->p_sigmask, catchmask);
if (reset != 0) {
sigset_t mask = sigmask(signum);
struct sigacts *ps = p->p_p->ps_sigacts;
mtx_enter(&p->p_p->ps_mtx);
ps->ps_sigcatch &= ~mask;
if (signum != SIGCONT && sigprop[signum] & SA_IGNORE)
ps->ps_sigignore |= mask;
ps->ps_sigact[signum] = SIG_DFL;
mtx_leave(&p->p_p->ps_mtx);
}
}
/*
* Send a signal caused by a trap to the current thread
* If it will be caught immediately, deliver it with correct code.
* Otherwise, post it normally.
*/
void
trapsignal(struct proc *p, int signum, u_long trapno, int code,
union sigval sigval)
{
struct process *pr = p->p_p;
struct sigctx ctx;
int mask;
switch (signum) {
case SIGILL:
case SIGBUS:
case SIGSEGV:
pr->ps_acflag |= ATRAP;
break;
}
mask = sigmask(signum);
setsigctx(p, signum, &ctx);
if ((pr->ps_flags & PS_TRACED) == 0 && ctx.sig_catch != 0 &&
(p->p_sigmask & mask) == 0) {
siginfo_t si;
initsiginfo(&si, signum, trapno, code, sigval);
#ifdef KTRACE
if (KTRPOINT(p, KTR_PSIG)) {
ktrpsig(p, signum, ctx.sig_action,
p->p_sigmask, code, &si);
}
#endif
if (sendsig(ctx.sig_action, signum, p->p_sigmask, &si,
ctx.sig_info, ctx.sig_onstack)) {
KERNEL_LOCK();
sigexit(p, SIGILL);
/* NOTREACHED */
}
postsig_done(p, signum, ctx.sig_catchmask, ctx.sig_reset);
} else {
p->p_sisig = signum;
p->p_sitrapno = trapno; /* XXX for core dump/debugger */
p->p_sicode = code;
p->p_sigval = sigval;
/*
* If traced, stop if signal is masked, and stay stopped
* until released by the debugger. If our parent process
* is waiting for us, don't hang as we could deadlock.
*/
if (((pr->ps_flags & (PS_TRACED | PS_PPWAIT)) == PS_TRACED) &&
signum != SIGKILL && (p->p_sigmask & mask) != 0) {
int s;
single_thread_set(p, SINGLE_SUSPEND, 0);
pr->ps_xsig = signum;
SCHED_LOCK(s);
proc_stop(p, 1);
SCHED_UNLOCK(s);
signum = pr->ps_xsig;
single_thread_clear(p, 0);
/*
* If we are no longer being traced, or the parent
* didn't give us a signal, skip sending the signal.
*/
if ((pr->ps_flags & PS_TRACED) == 0 ||
signum == 0)
return;
/* update signal info */
p->p_sisig = signum;
mask = sigmask(signum);
}
/*
* Signals like SIGBUS and SIGSEGV should not, when
* generated by the kernel, be ignorable or blockable.
* If it is and we're not being traced, then just kill
* the process.
* After vfs_shutdown(9), init(8) cannot receive signals
* because new code pages of the signal handler cannot be
* mapped from halted storage. init(8) may not die or the
* kernel panics. Better loop between signal handler and
* page fault trap until the machine is halted.
*/
if ((pr->ps_flags & PS_TRACED) == 0 &&
(sigprop[signum] & SA_KILL) &&
((p->p_sigmask & mask) || ctx.sig_ignore) &&
pr->ps_pid != 1) {
KERNEL_LOCK();
sigexit(p, signum);
/* NOTREACHED */
}
KERNEL_LOCK();
ptsignal(p, signum, STHREAD);
KERNEL_UNLOCK();
}
}
/*
* Send the signal to the process. If the signal has an action, the action
* is usually performed by the target process rather than the caller; we add
* the signal to the set of pending signals for the process.
*
* Exceptions:
* o When a stop signal is sent to a sleeping process that takes the
* default action, the process is stopped without awakening it.
* o SIGCONT restarts stopped processes (or puts them back to sleep)
* regardless of the signal action (eg, blocked or ignored).
*
* Other ignored signals are discarded immediately.
*/
void
psignal(struct proc *p, int signum)
{
ptsignal(p, signum, SPROCESS);
}
/*
* type = SPROCESS process signal, can be diverted (sigwait())
* type = STHREAD thread signal, but should be propagated if unhandled
* type = SPROPAGATED propagated to this thread, so don't propagate again
*/
void
ptsignal(struct proc *p, int signum, enum signal_type type)
{
int s, prop;
sig_t action;
int mask;
int *siglist;
struct process *pr = p->p_p;
struct proc *q;
int wakeparent = 0;
KERNEL_ASSERT_LOCKED();
#ifdef DIAGNOSTIC
if ((u_int)signum >= NSIG || signum == 0)
panic("psignal signal number");
#endif
/* Ignore signal if the target process is exiting */
if (pr->ps_flags & PS_EXITING)
return;
mask = sigmask(signum);
if (type == SPROCESS) {
/* Accept SIGKILL to coredumping processes */
if (pr->ps_flags & PS_COREDUMP && signum == SIGKILL) {
atomic_setbits_int(&pr->ps_siglist, mask);
return;
}
/*
* If the current thread can process the signal
* immediately (it's unblocked) then have it take it.
*/
q = curproc;
if (q != NULL && q->p_p == pr && (q->p_flag & P_WEXIT) == 0 &&
(q->p_sigmask & mask) == 0)
p = q;
else {
/*
* A process-wide signal can be diverted to a
* different thread that's in sigwait() for this
* signal. If there isn't such a thread, then
* pick a thread that doesn't have it blocked so
* that the stop/kill consideration isn't
* delayed. Otherwise, mark it pending on the
* main thread.
*/
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
/* ignore exiting threads */
if (q->p_flag & P_WEXIT)
continue;
/* skip threads that have the signal blocked */
if ((q->p_sigmask & mask) != 0)
continue;
/* okay, could send to this thread */
p = q;
/*
* sigsuspend, sigwait, ppoll/pselect, etc?
* Definitely go to this thread, as it's
* already blocked in the kernel.
*/
if (q->p_flag & P_SIGSUSPEND)
break;
}
}
}
if (type != SPROPAGATED)
KNOTE(&pr->ps_klist, NOTE_SIGNAL | signum);
prop = sigprop[signum];
/*
* If proc is traced, always give parent a chance.
*/
if (pr->ps_flags & PS_TRACED) {
action = SIG_DFL;
} else {
sigset_t sigcatch, sigignore;
/*
* If the signal is being ignored,
* then we forget about it immediately.
* (Note: we don't set SIGCONT in ps_sigignore,
* and if it is set to SIG_IGN,
* action will be SIG_DFL here.)
*/
mtx_enter(&pr->ps_mtx);
sigignore = pr->ps_sigacts->ps_sigignore;
sigcatch = pr->ps_sigacts->ps_sigcatch;
mtx_leave(&pr->ps_mtx);
if (sigignore & mask)
return;
if (p->p_sigmask & mask) {
action = SIG_HOLD;
} else if (sigcatch & mask) {
action = SIG_CATCH;
} else {
action = SIG_DFL;
if (prop & SA_KILL && pr->ps_nice > NZERO)
pr->ps_nice = NZERO;
/*
* If sending a tty stop signal to a member of an
* orphaned process group, discard the signal here if
* the action is default; don't stop the process below
* if sleeping, and don't clear any pending SIGCONT.
*/
if (prop & SA_TTYSTOP && pr->ps_pgrp->pg_jobc == 0)
return;
}
}
/*
* If delivered to process, mark as pending there. Continue and stop
* signals will be propagated to all threads. So they are always
* marked at thread level.
*/
siglist = (type == SPROCESS) ? &pr->ps_siglist : &p->p_siglist;
if (prop & SA_CONT) {
siglist = &p->p_siglist;
atomic_clearbits_int(siglist, STOPSIGMASK);
}
if (prop & SA_STOP) {
siglist = &p->p_siglist;
atomic_clearbits_int(siglist, CONTSIGMASK);
atomic_clearbits_int(&p->p_flag, P_CONTINUED);
}
/*
* XXX delay processing of SA_STOP signals unless action == SIG_DFL?
*/
if (prop & (SA_CONT | SA_STOP) && type != SPROPAGATED)
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link)
if (q != p)
ptsignal(q, signum, SPROPAGATED);
/*
* Defer further processing for signals which are held,
* except that stopped processes must be continued by SIGCONT.
*/
if (action == SIG_HOLD && ((prop & SA_CONT) == 0 ||
p->p_stat != SSTOP)) {
atomic_setbits_int(siglist, mask);
return;
}
SCHED_LOCK(s);
switch (p->p_stat) {
case SSLEEP:
/*
* If process is sleeping uninterruptibly
* we can't interrupt the sleep... the signal will
* be noticed when the process returns through
* trap() or syscall().
*/
if ((p->p_flag & P_SINTR) == 0)
goto out;
/*
* Process is sleeping and traced... make it runnable
* so it can discover the signal in cursig() and stop
* for the parent.
*/
if (pr->ps_flags & PS_TRACED)
goto run;
/*
* If SIGCONT is default (or ignored) and process is
* asleep, we are finished; the process should not
* be awakened.
*/
if ((prop & SA_CONT) && action == SIG_DFL) {
mask = 0;
goto out;
}
/*
* When a sleeping process receives a stop
* signal, process immediately if possible.
*/
if ((prop & SA_STOP) && action == SIG_DFL) {
/*
* If a child holding parent blocked,
* stopping could cause deadlock.
*/
if (pr->ps_flags & PS_PPWAIT)
goto out;
mask = 0;
pr->ps_xsig = signum;
proc_stop(p, 0);
goto out;
}
/*
* All other (caught or default) signals
* cause the process to run.
*/
goto runfast;
/* NOTREACHED */
case SSTOP:
/*
* If traced process is already stopped,
* then no further action is necessary.
*/
if (pr->ps_flags & PS_TRACED)
goto out;
/*
* Kill signal always sets processes running.
*/
if (signum == SIGKILL) {
atomic_clearbits_int(&p->p_flag, P_SUSPSIG);
goto runfast;
}
if (prop & SA_CONT) {
/*
* If SIGCONT is default (or ignored), we continue the
* process but don't leave the signal in p_siglist, as
* it has no further action. If SIGCONT is held, we
* continue the process and leave the signal in
* p_siglist. If the process catches SIGCONT, let it
* handle the signal itself. If it isn't waiting on
* an event, then it goes back to run state.
* Otherwise, process goes back to sleep state.
*/
atomic_setbits_int(&p->p_flag, P_CONTINUED);
atomic_clearbits_int(&p->p_flag, P_SUSPSIG);
wakeparent = 1;
if (action == SIG_DFL)
atomic_clearbits_int(siglist, mask);
if (action == SIG_CATCH)
goto runfast;
if (p->p_wchan == NULL)
goto run;
p->p_stat = SSLEEP;
goto out;
}
if (prop & SA_STOP) {
/*
* Already stopped, don't need to stop again.
* (If we did the shell could get confused.)
*/
mask = 0;
goto out;
}
/*
* If process is sleeping interruptibly, then simulate a
* wakeup so that when it is continued, it will be made
* runnable and can look at the signal. But don't make
* the process runnable, leave it stopped.
*/
if (p->p_flag & P_SINTR)
unsleep(p);
goto out;
case SONPROC:
/* set siglist before issuing the ast */
atomic_setbits_int(siglist, mask);
mask = 0;
signotify(p);
/* FALLTHROUGH */
default:
/*
* SRUN, SIDL, SDEAD do nothing with the signal,
* other than kicking ourselves if we are running.
* It will either never be noticed, or noticed very soon.
*/
goto out;
}
/* NOTREACHED */
runfast:
/*
* Raise priority to at least PUSER.
*/
if (p->p_usrpri > PUSER)
p->p_usrpri = PUSER;
run:
setrunnable(p);
out:
/* finally adjust siglist */
if (mask)
atomic_setbits_int(siglist, mask);
SCHED_UNLOCK(s);
if (wakeparent)
wakeup(pr->ps_pptr);
}
/* fill the signal context which should be used by postsig() and issignal() */
void
setsigctx(struct proc *p, int signum, struct sigctx *sctx)
{
struct sigacts *ps = p->p_p->ps_sigacts;
sigset_t mask;
mtx_enter(&p->p_p->ps_mtx);
mask = sigmask(signum);
sctx->sig_action = ps->ps_sigact[signum];
sctx->sig_catchmask = ps->ps_catchmask[signum];
sctx->sig_reset = (ps->ps_sigreset & mask) != 0;
sctx->sig_info = (ps->ps_siginfo & mask) != 0;
sctx->sig_intr = (ps->ps_sigintr & mask) != 0;
sctx->sig_onstack = (ps->ps_sigonstack & mask) != 0;
sctx->sig_ignore = (ps->ps_sigignore & mask) != 0;
sctx->sig_catch = (ps->ps_sigcatch & mask) != 0;
mtx_leave(&p->p_p->ps_mtx);
}
/*
* Determine signal that should be delivered to process p, the current
* process, 0 if none.
*
* If the current process has received a signal (should be caught or cause
* termination, should interrupt current syscall), return the signal number.
* Stop signals with default action are processed immediately, then cleared;
* they aren't returned. This is checked after each entry to the system for
* a syscall or trap. The normal call sequence is
*
* while (signum = cursig(curproc, &ctx))
* postsig(signum, &ctx);
*
* Assumes that if the P_SINTR flag is set, we're holding both the
* kernel and scheduler locks.
*/
int
cursig(struct proc *p, struct sigctx *sctx)
{
struct process *pr = p->p_p;
int signum, mask, prop;
int dolock = (p->p_flag & P_SINTR) == 0;
sigset_t ps_siglist;
int s;
KASSERT(p == curproc);
for (;;) {
ps_siglist = READ_ONCE(pr->ps_siglist);
membar_consumer();
mask = SIGPENDING(p);
if (pr->ps_flags & PS_PPWAIT)
mask &= ~STOPSIGMASK;
if (mask == 0) /* no signal to send */
return (0);
signum = ffs((long)mask);
mask = sigmask(signum);
/* take the signal! */
if (atomic_cas_uint(&pr->ps_siglist, ps_siglist,
ps_siglist & ~mask) != ps_siglist) {
/* lost race taking the process signal, restart */
continue;
}
atomic_clearbits_int(&p->p_siglist, mask);
setsigctx(p, signum, sctx);
/*
* We should see pending but ignored signals
* only if PS_TRACED was on when they were posted.
*/
if (sctx->sig_ignore && (pr->ps_flags & PS_TRACED) == 0)
continue;
/*
* If traced, always stop, and stay stopped until released
* by the debugger. If our parent process is waiting for
* us, don't hang as we could deadlock.
*/
if (((pr->ps_flags & (PS_TRACED | PS_PPWAIT)) == PS_TRACED) &&
signum != SIGKILL) {
single_thread_set(p, SINGLE_SUSPEND, 0);
pr->ps_xsig = signum;
if (dolock)
SCHED_LOCK(s);
proc_stop(p, 1);
if (dolock)
SCHED_UNLOCK(s);
/*
* re-take the signal before releasing
* the other threads. Must check the continue
* conditions below and only take the signal if
* those are not true.
*/
signum = pr->ps_xsig;
mask = sigmask(signum);
setsigctx(p, signum, sctx);
if (!((pr->ps_flags & PS_TRACED) == 0 ||
signum == 0 ||
(p->p_sigmask & mask) != 0)) {
atomic_clearbits_int(&p->p_siglist, mask);
atomic_clearbits_int(&pr->ps_siglist, mask);
}
single_thread_clear(p, 0);
/*
* If we are no longer being traced, or the parent
* didn't give us a signal, look for more signals.
*/
if ((pr->ps_flags & PS_TRACED) == 0 ||
signum == 0)
continue;
/*
* If the new signal is being masked, look for other
* signals.
*/
if ((p->p_sigmask & mask) != 0)
continue;
}
prop = sigprop[signum];
/*
* Decide whether the signal should be returned.
* Return the signal's number, or fall through
* to clear it from the pending mask.
*/
switch ((long)sctx->sig_action) {
case (long)SIG_DFL:
/*
* Don't take default actions on system processes.
*/
if (pr->ps_pid <= 1) {
#ifdef DIAGNOSTIC
/*
* Are you sure you want to ignore SIGSEGV
* in init? XXX
*/
printf("Process (pid %d) got signal"
" %d\n", pr->ps_pid, signum);
#endif
break; /* == ignore */
}
/*
* If there is a pending stop signal to process
* with default action, stop here,
* then clear the signal. However,
* if process is member of an orphaned
* process group, ignore tty stop signals.
*/
if (prop & SA_STOP) {
if (pr->ps_flags & PS_TRACED ||
(pr->ps_pgrp->pg_jobc == 0 &&
prop & SA_TTYSTOP))
break; /* == ignore */
pr->ps_xsig = signum;
if (dolock)
SCHED_LOCK(s);
proc_stop(p, 1);
if (dolock)
SCHED_UNLOCK(s);
break;
} else if (prop & SA_IGNORE) {
/*
* Except for SIGCONT, shouldn't get here.
* Default action is to ignore; drop it.
*/
break; /* == ignore */
} else
goto keep;
/* NOTREACHED */
case (long)SIG_IGN:
/*
* Masking above should prevent us ever trying
* to take action on an ignored signal other
* than SIGCONT, unless process is traced.
*/
if ((prop & SA_CONT) == 0 &&
(pr->ps_flags & PS_TRACED) == 0)
printf("%s\n", __func__);
break; /* == ignore */
default:
/*
* This signal has an action, let
* postsig() process it.
*/
goto keep;
}
}
/* NOTREACHED */
keep:
atomic_setbits_int(&p->p_siglist, mask); /*leave the signal for later */
return (signum);
}
/*
* Put the argument process into the stopped state and notify the parent
* via wakeup. Signals are handled elsewhere. The process must not be
* on the run queue.
*/
void
proc_stop(struct proc *p, int sw)
{
struct process *pr = p->p_p;
#ifdef MULTIPROCESSOR
SCHED_ASSERT_LOCKED();
#endif
p->p_stat = SSTOP;
atomic_clearbits_int(&pr->ps_flags, PS_WAITED);
atomic_setbits_int(&pr->ps_flags, PS_STOPPED);
atomic_setbits_int(&p->p_flag, P_SUSPSIG);
/*
* We need this soft interrupt to be handled fast.
* Extra calls to softclock don't hurt.
*/
softintr_schedule(proc_stop_si);
if (sw)
mi_switch();
}
/*
* Called from a soft interrupt to send signals to the parents of stopped
* processes.
* We can't do this in proc_stop because it's called with nasty locks held
* and we would need recursive scheduler lock to deal with that.
*/
void
proc_stop_sweep(void *v)
{
struct process *pr;
LIST_FOREACH(pr, &allprocess, ps_list) {
if ((pr->ps_flags & PS_STOPPED) == 0)
continue;
atomic_clearbits_int(&pr->ps_flags, PS_STOPPED);
if ((pr->ps_pptr->ps_sigacts->ps_sigflags & SAS_NOCLDSTOP) == 0)
prsignal(pr->ps_pptr, SIGCHLD);
wakeup(pr->ps_pptr);
}
}
/*
* Take the action for the specified signal
* from the current set of pending signals.
*/
void
postsig(struct proc *p, int signum, struct sigctx *sctx)
{
u_long trapno;
int mask, returnmask;
siginfo_t si;
union sigval sigval;
int code;
KASSERT(signum != 0);
mask = sigmask(signum);
atomic_clearbits_int(&p->p_siglist, mask);
sigval.sival_ptr = NULL;
if (p->p_sisig != signum) {
trapno = 0;
code = SI_USER;
sigval.sival_ptr = NULL;
} else {
trapno = p->p_sitrapno;
code = p->p_sicode;
sigval = p->p_sigval;
}
initsiginfo(&si, signum, trapno, code, sigval);
#ifdef KTRACE
if (KTRPOINT(p, KTR_PSIG)) {
ktrpsig(p, signum, sctx->sig_action, p->p_flag & P_SIGSUSPEND ?
p->p_oldmask : p->p_sigmask, code, &si);
}
#endif
if (sctx->sig_action == SIG_DFL) {
/*
* Default action, where the default is to kill
* the process. (Other cases were ignored above.)
*/
KERNEL_LOCK();
sigexit(p, signum);
/* NOTREACHED */
} else {
/*
* If we get here, the signal must be caught.
*/
#ifdef DIAGNOSTIC
if (sctx->sig_action == SIG_IGN || (p->p_sigmask & mask))
panic("postsig action");
#endif
/*
* Set the new mask value and also defer further
* occurrences of this signal.
*
* Special case: user has done a sigpause. Here the
* current mask is not of interest, but rather the
* mask from before the sigpause is what we want
* restored after the signal processing is completed.
*/
if (p->p_flag & P_SIGSUSPEND) {
atomic_clearbits_int(&p->p_flag, P_SIGSUSPEND);
returnmask = p->p_oldmask;
} else {
returnmask = p->p_sigmask;
}
if (p->p_sisig == signum) {
p->p_sisig = 0;
p->p_sitrapno = 0;
p->p_sicode = SI_USER;
p->p_sigval.sival_ptr = NULL;
}
if (sendsig(sctx->sig_action, signum, returnmask, &si,
sctx->sig_info, sctx->sig_onstack)) {
KERNEL_LOCK();
sigexit(p, SIGILL);
/* NOTREACHED */
}
postsig_done(p, signum, sctx->sig_catchmask, sctx->sig_reset);
}
}
/*
* Force the current process to exit with the specified signal, dumping core
* if appropriate. We bypass the normal tests for masked and caught signals,
* allowing unrecoverable failures to terminate the process without changing
* signal state. Mark the accounting record with the signal termination.
* If dumping core, save the signal number for the debugger. Calls exit and
* does not return.
*/
void
sigexit(struct proc *p, int signum)
{
/* Mark process as going away */
atomic_setbits_int(&p->p_flag, P_WEXIT);
p->p_p->ps_acflag |= AXSIG;
if (sigprop[signum] & SA_CORE) {
p->p_sisig = signum;
/* if there are other threads, pause them */
if (P_HASSIBLING(p))
single_thread_set(p, SINGLE_SUSPEND, 1);
if (coredump(p) == 0)
signum |= WCOREFLAG;
}
exit1(p, 0, signum, EXIT_NORMAL);
/* NOTREACHED */
}
/*
* Send uncatchable SIGABRT for coredump.
*/
void
sigabort(struct proc *p)
{
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = SIG_DFL;
setsigvec(p, SIGABRT, &sa);
atomic_clearbits_int(&p->p_sigmask, sigmask(SIGABRT));
psignal(p, SIGABRT);
}
/*
* Return 1 if `sig', a given signal, is ignored or masked for `p', a given
* thread, and 0 otherwise.
*/
int
sigismasked(struct proc *p, int sig)
{
struct process *pr = p->p_p;
int rv;
mtx_enter(&pr->ps_mtx);
rv = (pr->ps_sigacts->ps_sigignore & sigmask(sig)) ||
(p->p_sigmask & sigmask(sig));
mtx_leave(&pr->ps_mtx);
return !!rv;
}
struct coredump_iostate {
struct proc *io_proc;
struct vnode *io_vp;
struct ucred *io_cred;
off_t io_offset;
};
/*
* Dump core, into a file named "progname.core", unless the process was
* setuid/setgid.
*/
int
coredump(struct proc *p)
{
#ifdef SMALL_KERNEL
return EPERM;
#else
struct process *pr = p->p_p;
struct vnode *vp;
struct ucred *cred = p->p_ucred;
struct vmspace *vm = p->p_vmspace;
struct nameidata nd;
struct vattr vattr;
struct coredump_iostate io;
int error, len, incrash = 0;
char *name;
const char *dir = "/var/crash";
atomic_setbits_int(&pr->ps_flags, PS_COREDUMP);
/* Don't dump if will exceed file size limit. */
if (USPACE + ptoa(vm->vm_dsize + vm->vm_ssize) >= lim_cur(RLIMIT_CORE))
return (EFBIG);
name = pool_get(&namei_pool, PR_WAITOK);
/*
* If the process has inconsistent uids, nosuidcoredump
* determines coredump placement policy.
*/
if (((pr->ps_flags & PS_SUGID) && (error = suser(p))) ||
((pr->ps_flags & PS_SUGID) && nosuidcoredump)) {
if (nosuidcoredump == 3) {
/*
* If the program directory does not exist, dumps of
* that core will silently fail.
*/
len = snprintf(name, MAXPATHLEN, "%s/%s/%u.core",
dir, pr->ps_comm, pr->ps_pid);
incrash = KERNELPATH;
} else if (nosuidcoredump == 2) {
len = snprintf(name, MAXPATHLEN, "%s/%s.core",
dir, pr->ps_comm);
incrash = KERNELPATH;
} else {
pool_put(&namei_pool, name);
return (EPERM);
}
} else
len = snprintf(name, MAXPATHLEN, "%s.core", pr->ps_comm);
if (len >= MAXPATHLEN) {
pool_put(&namei_pool, name);
return (EACCES);
}
/*
* Control the UID used to write out. The normal case uses
* the real UID. If the sugid case is going to write into the
* controlled directory, we do so as root.
*/
if (incrash == 0) {
cred = crdup(cred);
cred->cr_uid = cred->cr_ruid;
cred->cr_gid = cred->cr_rgid;
} else {
if (p->p_fd->fd_rdir) {
vrele(p->p_fd->fd_rdir);
p->p_fd->fd_rdir = NULL;
}
p->p_ucred = crdup(p->p_ucred);
crfree(cred);
cred = p->p_ucred;
crhold(cred);
cred->cr_uid = 0;
cred->cr_gid = 0;
}
/* incrash should be 0 or KERNELPATH only */
NDINIT(&nd, 0, incrash, UIO_SYSSPACE, name, p);
error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW | O_NONBLOCK,
S_IRUSR | S_IWUSR);
if (error)
goto out;
/*
* Don't dump to non-regular files, files with links, or files
* owned by someone else.
*/
vp = nd.ni_vp;
if ((error = VOP_GETATTR(vp, &vattr, cred, p)) != 0) {
VOP_UNLOCK(vp);
vn_close(vp, FWRITE, cred, p);
goto out;
}
if (vp->v_type != VREG || vattr.va_nlink != 1 ||
vattr.va_mode & ((VREAD | VWRITE) >> 3 | (VREAD | VWRITE) >> 6) ||
vattr.va_uid != cred->cr_uid) {
error = EACCES;
VOP_UNLOCK(vp);
vn_close(vp, FWRITE, cred, p);
goto out;
}
VATTR_NULL(&vattr);
vattr.va_size = 0;
VOP_SETATTR(vp, &vattr, cred, p);
pr->ps_acflag |= ACORE;
io.io_proc = p;
io.io_vp = vp;
io.io_cred = cred;
io.io_offset = 0;
VOP_UNLOCK(vp);
vref(vp);
error = vn_close(vp, FWRITE, cred, p);
if (error == 0)
error = coredump_elf(p, &io);
vrele(vp);
out:
crfree(cred);
pool_put(&namei_pool, name);
return (error);
#endif
}
#ifndef SMALL_KERNEL
int
coredump_write(void *cookie, enum uio_seg segflg, const void *data, size_t len)
{
struct coredump_iostate *io = cookie;
off_t coffset = 0;
size_t csize;
int chunk, error;
csize = len;
do {
if (sigmask(SIGKILL) &
(io->io_proc->p_siglist | io->io_proc->p_p->ps_siglist))
return (EINTR);
/* Rest of the loop sleeps with lock held, so... */
yield();
chunk = MIN(csize, MAXPHYS);
error = vn_rdwr(UIO_WRITE, io->io_vp,
(caddr_t)data + coffset, chunk,
io->io_offset + coffset, segflg,
IO_UNIT, io->io_cred, NULL, io->io_proc);
if (error) {
struct process *pr = io->io_proc->p_p;
if (error == ENOSPC)
log(LOG_ERR,
"coredump of %s(%d) failed, filesystem full\n",
pr->ps_comm, pr->ps_pid);
else
log(LOG_ERR,
"coredump of %s(%d), write failed: errno %d\n",
pr->ps_comm, pr->ps_pid, error);
return (error);
}
coffset += chunk;
csize -= chunk;
} while (csize > 0);
io->io_offset += len;
return (0);
}
void
coredump_unmap(void *cookie, vaddr_t start, vaddr_t end)
{
struct coredump_iostate *io = cookie;
uvm_unmap(&io->io_proc->p_vmspace->vm_map, start, end);
}
#endif /* !SMALL_KERNEL */
/*
* Nonexistent system call-- signal process (may want to handle it).
* Flag error in case process won't see signal immediately (blocked or ignored).
*/
int
sys_nosys(struct proc *p, void *v, register_t *retval)
{
ptsignal(p, SIGSYS, STHREAD);
return (ENOSYS);
}
int
sys___thrsigdivert(struct proc *p, void *v, register_t *retval)
{
static int sigwaitsleep;
struct sys___thrsigdivert_args /* {
syscallarg(sigset_t) sigmask;
syscallarg(siginfo_t *) info;
syscallarg(const struct timespec *) timeout;
} */ *uap = v;
struct sigctx ctx;
sigset_t mask = SCARG(uap, sigmask) &~ sigcantmask;
siginfo_t si;
uint64_t nsecs = INFSLP;
int timeinvalid = 0;
int error = 0;
memset(&si, 0, sizeof(si));
if (SCARG(uap, timeout) != NULL) {
struct timespec ts;
if ((error = copyin(SCARG(uap, timeout), &ts, sizeof(ts))) != 0)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
if (!timespecisvalid(&ts))
timeinvalid = 1;
else
nsecs = TIMESPEC_TO_NSEC(&ts);
}
dosigsuspend(p, p->p_sigmask &~ mask);
for (;;) {
si.si_signo = cursig(p, &ctx);
if (si.si_signo != 0) {
sigset_t smask = sigmask(si.si_signo);
if (smask & mask) {
atomic_clearbits_int(&p->p_siglist, smask);
error = 0;
break;
}
}
/* per-POSIX, delay this error until after the above */
if (timeinvalid)
error = EINVAL;
/* per-POSIX, return immediately if timeout is zero-valued */
if (nsecs == 0)
error = EAGAIN;
if (error != 0)
break;
error = tsleep_nsec(&sigwaitsleep, PPAUSE|PCATCH, "sigwait",
nsecs);
}
if (error == 0) {
*retval = si.si_signo;
if (SCARG(uap, info) != NULL)
error = copyout(&si, SCARG(uap, info), sizeof(si));
} else if (error == ERESTART && SCARG(uap, timeout) != NULL) {
/*
* Restarting is wrong if there's a timeout, as it'll be
* for the same interval again
*/
error = EINTR;
}
return (error);
}
void
initsiginfo(siginfo_t *si, int sig, u_long trapno, int code, union sigval val)
{
memset(si, 0, sizeof(*si));
si->si_signo = sig;
si->si_code = code;
if (code == SI_USER) {
si->si_value = val;
} else {
switch (sig) {
case SIGSEGV:
case SIGILL:
case SIGBUS:
case SIGFPE:
si->si_addr = val.sival_ptr;
si->si_trapno = trapno;
break;
case SIGXFSZ:
break;
}
}
}
int
filt_sigattach(struct knote *kn)
{
struct process *pr = curproc->p_p;
int s;
if (kn->kn_id >= NSIG)
return EINVAL;
kn->kn_ptr.p_process = pr;
kn->kn_flags |= EV_CLEAR; /* automatically set */
s = splhigh();
klist_insert_locked(&pr->ps_klist, kn);
splx(s);
return (0);
}
void
filt_sigdetach(struct knote *kn)
{
struct process *pr = kn->kn_ptr.p_process;
int s;
s = splhigh();
klist_remove_locked(&pr->ps_klist, kn);
splx(s);
}
/*
* signal knotes are shared with proc knotes, so we apply a mask to
* the hint in order to differentiate them from process hints. This
* could be avoided by using a signal-specific knote list, but probably
* isn't worth the trouble.
*/
int
filt_signal(struct knote *kn, long hint)
{
if (hint & NOTE_SIGNAL) {
hint &= ~NOTE_SIGNAL;
if (kn->kn_id == hint)
kn->kn_data++;
}
return (kn->kn_data != 0);
}
void
userret(struct proc *p)
{
struct sigctx ctx;
int signum;
/* send SIGPROF or SIGVTALRM if their timers interrupted this thread */
if (p->p_flag & P_PROFPEND) {
atomic_clearbits_int(&p->p_flag, P_PROFPEND);
KERNEL_LOCK();
psignal(p, SIGPROF);
KERNEL_UNLOCK();
}
if (p->p_flag & P_ALRMPEND) {
atomic_clearbits_int(&p->p_flag, P_ALRMPEND);
KERNEL_LOCK();
psignal(p, SIGVTALRM);
KERNEL_UNLOCK();
}
if (SIGPENDING(p) != 0) {
while ((signum = cursig(p, &ctx)) != 0)
postsig(p, signum, &ctx);
}
/*
* If P_SIGSUSPEND is still set here, then we still need to restore
* the original sigmask before returning to userspace. Also, this
* might unmask some pending signals, so we need to check a second
* time for signals to post.
*/
if (p->p_flag & P_SIGSUSPEND) {
atomic_clearbits_int(&p->p_flag, P_SIGSUSPEND);
p->p_sigmask = p->p_oldmask;
while ((signum = cursig(p, &ctx)) != 0)
postsig(p, signum, &ctx);
}
if (p->p_flag & P_SUSPSINGLE)
single_thread_check(p, 0);
WITNESS_WARN(WARN_PANIC, NULL, "userret: returning");
p->p_cpu->ci_schedstate.spc_curpriority = p->p_usrpri;
}
int
single_thread_check_locked(struct proc *p, int deep, int s)
{
struct process *pr = p->p_p;
SCHED_ASSERT_LOCKED();
if (pr->ps_single != NULL && pr->ps_single != p) {
do {
/* if we're in deep, we need to unwind to the edge */
if (deep) {
if (pr->ps_flags & PS_SINGLEUNWIND)
return (ERESTART);
if (pr->ps_flags & PS_SINGLEEXIT)
return (EINTR);
}
if (atomic_dec_int_nv(&pr->ps_singlecount) == 0)
wakeup(&pr->ps_singlecount);
if (pr->ps_flags & PS_SINGLEEXIT) {
SCHED_UNLOCK(s);
KERNEL_LOCK();
exit1(p, 0, 0, EXIT_THREAD_NOCHECK);
/* NOTREACHED */
}
/* not exiting and don't need to unwind, so suspend */
p->p_stat = SSTOP;
mi_switch();
} while (pr->ps_single != NULL);
}
return (0);
}
int
single_thread_check(struct proc *p, int deep)
{
int s, error;
SCHED_LOCK(s);
error = single_thread_check_locked(p, deep, s);
SCHED_UNLOCK(s);
return error;
}
/*
* Stop other threads in the process. The mode controls how and
* where the other threads should stop:
* - SINGLE_SUSPEND: stop wherever they are, will later either be told to exit
* (by setting to SINGLE_EXIT) or be released (via single_thread_clear())
* - SINGLE_UNWIND: just unwind to kernel boundary, will be told to exit
* or released as with SINGLE_SUSPEND
* - SINGLE_EXIT: unwind to kernel boundary and exit
*/
int
single_thread_set(struct proc *p, enum single_thread_mode mode, int wait)
{
struct process *pr = p->p_p;
struct proc *q;
int error, s;
KASSERT(curproc == p);
SCHED_LOCK(s);
error = single_thread_check_locked(p, (mode == SINGLE_UNWIND), s);
if (error) {
SCHED_UNLOCK(s);
return error;
}
switch (mode) {
case SINGLE_SUSPEND:
break;
case SINGLE_UNWIND:
atomic_setbits_int(&pr->ps_flags, PS_SINGLEUNWIND);
break;
case SINGLE_EXIT:
atomic_setbits_int(&pr->ps_flags, PS_SINGLEEXIT);
atomic_clearbits_int(&pr->ps_flags, PS_SINGLEUNWIND);
break;
#ifdef DIAGNOSTIC
default:
panic("single_thread_mode = %d", mode);
#endif
}
pr->ps_singlecount = 0;
membar_producer();
pr->ps_single = p;
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
if (q == p)
continue;
if (q->p_flag & P_WEXIT) {
if (mode == SINGLE_EXIT) {
if (q->p_stat == SSTOP) {
setrunnable(q);
atomic_inc_int(&pr->ps_singlecount);
}
}
continue;
}
atomic_setbits_int(&q->p_flag, P_SUSPSINGLE);
switch (q->p_stat) {
case SIDL:
case SRUN:
atomic_inc_int(&pr->ps_singlecount);
break;
case SSLEEP:
/* if it's not interruptible, then just have to wait */
if (q->p_flag & P_SINTR) {
/* merely need to suspend? just stop it */
if (mode == SINGLE_SUSPEND) {
q->p_stat = SSTOP;
break;
}
/* need to unwind or exit, so wake it */
setrunnable(q);
}
atomic_inc_int(&pr->ps_singlecount);
break;
case SSTOP:
if (mode == SINGLE_EXIT) {
setrunnable(q);
atomic_inc_int(&pr->ps_singlecount);
}
break;
case SDEAD:
break;
case SONPROC:
atomic_inc_int(&pr->ps_singlecount);
signotify(q);
break;
}
}
SCHED_UNLOCK(s);
if (wait)
single_thread_wait(pr, 1);
return 0;
}
/*
* Wait for other threads to stop. If recheck is false then the function
* returns non-zero if the caller needs to restart the check else 0 is
* returned. If recheck is true the return value is always 0.
*/
int
single_thread_wait(struct process *pr, int recheck)
{
struct sleep_state sls;
int wait;
/* wait until they're all suspended */
wait = pr->ps_singlecount > 0;
while (wait) {
sleep_setup(&sls, &pr->ps_singlecount, PWAIT, "suspend", 0);
wait = pr->ps_singlecount > 0;
sleep_finish(&sls, wait);
if (!recheck)
break;
}
return wait;
}
void
single_thread_clear(struct proc *p, int flag)
{
struct process *pr = p->p_p;
struct proc *q;
int s;
KASSERT(pr->ps_single == p);
KASSERT(curproc == p);
SCHED_LOCK(s);
pr->ps_single = NULL;
atomic_clearbits_int(&pr->ps_flags, PS_SINGLEUNWIND | PS_SINGLEEXIT);
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
if (q == p || (q->p_flag & P_SUSPSINGLE) == 0)
continue;
atomic_clearbits_int(&q->p_flag, P_SUSPSINGLE);
/*
* if the thread was only stopped for single threading
* then clearing that either makes it runnable or puts
* it back into some sleep queue
*/
if (q->p_stat == SSTOP && (q->p_flag & flag) == 0) {
if (q->p_wchan == NULL)
setrunnable(q);
else
q->p_stat = SSLEEP;
}
}
SCHED_UNLOCK(s);
}
void
sigio_del(struct sigiolst *rmlist)
{
struct sigio *sigio;
while ((sigio = LIST_FIRST(rmlist)) != NULL) {
LIST_REMOVE(sigio, sio_pgsigio);
crfree(sigio->sio_ucred);
free(sigio, M_SIGIO, sizeof(*sigio));
}
}
void
sigio_unlink(struct sigio_ref *sir, struct sigiolst *rmlist)
{
struct sigio *sigio;
MUTEX_ASSERT_LOCKED(&sigio_lock);
sigio = sir->sir_sigio;
if (sigio != NULL) {
KASSERT(sigio->sio_myref == sir);
sir->sir_sigio = NULL;
if (sigio->sio_pgid > 0)
sigio->sio_proc = NULL;
else
sigio->sio_pgrp = NULL;
LIST_REMOVE(sigio, sio_pgsigio);
LIST_INSERT_HEAD(rmlist, sigio, sio_pgsigio);
}
}
void
sigio_free(struct sigio_ref *sir)
{
struct sigiolst rmlist;
if (sir->sir_sigio == NULL)
return;
LIST_INIT(&rmlist);
mtx_enter(&sigio_lock);
sigio_unlink(sir, &rmlist);
mtx_leave(&sigio_lock);
sigio_del(&rmlist);
}
void
sigio_freelist(struct sigiolst *sigiolst)
{
struct sigiolst rmlist;
struct sigio *sigio;
if (LIST_EMPTY(sigiolst))
return;
LIST_INIT(&rmlist);
mtx_enter(&sigio_lock);
while ((sigio = LIST_FIRST(sigiolst)) != NULL)
sigio_unlink(sigio->sio_myref, &rmlist);
mtx_leave(&sigio_lock);
sigio_del(&rmlist);
}
int
sigio_setown(struct sigio_ref *sir, u_long cmd, caddr_t data)
{
struct sigiolst rmlist;
struct proc *p = curproc;
struct pgrp *pgrp = NULL;
struct process *pr = NULL;
struct sigio *sigio;
int error;
pid_t pgid = *(int *)data;
if (pgid == 0) {
sigio_free(sir);
return (0);
}
if (cmd == TIOCSPGRP) {
if (pgid < 0)
return (EINVAL);
pgid = -pgid;
}
sigio = malloc(sizeof(*sigio), M_SIGIO, M_WAITOK);
sigio->sio_pgid = pgid;
sigio->sio_ucred = crhold(p->p_ucred);
sigio->sio_myref = sir;
LIST_INIT(&rmlist);
/*
* The kernel lock, and not sleeping between prfind()/pgfind() and
* linking of the sigio ensure that the process or process group does
* not disappear unexpectedly.
*/
KERNEL_LOCK();
mtx_enter(&sigio_lock);
if (pgid > 0) {
pr = prfind(pgid);
if (pr == NULL) {
error = ESRCH;
goto fail;
}
/*
* Policy - Don't allow a process to FSETOWN a process
* in another session.
*
* Remove this test to allow maximum flexibility or
* restrict FSETOWN to the current process or process
* group for maximum safety.
*/
if (pr->ps_session != p->p_p->ps_session) {
error = EPERM;
goto fail;
}
if ((pr->ps_flags & PS_EXITING) != 0) {
error = ESRCH;
goto fail;
}
} else /* if (pgid < 0) */ {
pgrp = pgfind(-pgid);
if (pgrp == NULL) {
error = ESRCH;
goto fail;
}
/*
* Policy - Don't allow a process to FSETOWN a process
* in another session.
*
* Remove this test to allow maximum flexibility or
* restrict FSETOWN to the current process or process
* group for maximum safety.
*/
if (pgrp->pg_session != p->p_p->ps_session) {
error = EPERM;
goto fail;
}
}
if (pgid > 0) {
sigio->sio_proc = pr;
LIST_INSERT_HEAD(&pr->ps_sigiolst, sigio, sio_pgsigio);
} else {
sigio->sio_pgrp = pgrp;
LIST_INSERT_HEAD(&pgrp->pg_sigiolst, sigio, sio_pgsigio);
}
sigio_unlink(sir, &rmlist);
sir->sir_sigio = sigio;
mtx_leave(&sigio_lock);
KERNEL_UNLOCK();
sigio_del(&rmlist);
return (0);
fail:
mtx_leave(&sigio_lock);
KERNEL_UNLOCK();
crfree(sigio->sio_ucred);
free(sigio, M_SIGIO, sizeof(*sigio));
return (error);
}
void
sigio_getown(struct sigio_ref *sir, u_long cmd, caddr_t data)
{
struct sigio *sigio;
pid_t pgid = 0;
mtx_enter(&sigio_lock);
sigio = sir->sir_sigio;
if (sigio != NULL)
pgid = sigio->sio_pgid;
mtx_leave(&sigio_lock);
if (cmd == TIOCGPGRP)
pgid = -pgid;
*(int *)data = pgid;
}
void
sigio_copy(struct sigio_ref *dst, struct sigio_ref *src)
{
struct sigiolst rmlist;
struct sigio *newsigio, *sigio;
sigio_free(dst);
if (src->sir_sigio == NULL)
return;
newsigio = malloc(sizeof(*newsigio), M_SIGIO, M_WAITOK);
LIST_INIT(&rmlist);
mtx_enter(&sigio_lock);
sigio = src->sir_sigio;
if (sigio == NULL) {
mtx_leave(&sigio_lock);
free(newsigio, M_SIGIO, sizeof(*newsigio));
return;
}
newsigio->sio_pgid = sigio->sio_pgid;
newsigio->sio_ucred = crhold(sigio->sio_ucred);
newsigio->sio_myref = dst;
if (newsigio->sio_pgid > 0) {
newsigio->sio_proc = sigio->sio_proc;
LIST_INSERT_HEAD(&newsigio->sio_proc->ps_sigiolst, newsigio,
sio_pgsigio);
} else {
newsigio->sio_pgrp = sigio->sio_pgrp;
LIST_INSERT_HEAD(&newsigio->sio_pgrp->pg_sigiolst, newsigio,
sio_pgsigio);
}
sigio_unlink(dst, &rmlist);
dst->sir_sigio = newsigio;
mtx_leave(&sigio_lock);
sigio_del(&rmlist);
}
2
23
41
2
39
78
68
69
7
26
6
71
98
57
69
25
2
21
29
62
62
11
15
70
23
51
41
2
4
39
2
39
18
11
11
8
3
3
4
14
740
678
53
28
5
874
817
1
58
832
776
1
42
10
10
4
4
9
10
24
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
/* $OpenBSD: kern_unveil.c,v 1.54 2022/08/14 01:58:27 jsg Exp $ */
/*
* Copyright (c) 2017-2019 Bob Beck <beck@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/acct.h>
#include <sys/mount.h>
#include <sys/filedesc.h>
#include <sys/proc.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/tree.h>
#include <sys/lock.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include <sys/systm.h>
#include <sys/pledge.h>
struct unvname {
char *un_name;
size_t un_namesize;
u_char un_flags;
RBT_ENTRY(unvnmae) un_rbt;
};
RBT_HEAD(unvname_rbt, unvname);
struct unveil {
struct vnode *uv_vp;
ssize_t uv_cover;
struct unvname_rbt uv_names;
struct rwlock uv_lock;
u_char uv_flags;
};
/* #define DEBUG_UNVEIL */
#ifdef DEBUG_UNVEIL
#define DPRINTF(x...) do { printf(x); } while (0)
#else
#define DPRINTF(x...)
#endif
#define UNVEIL_MAX_VNODES 128
#define UNVEIL_MAX_NAMES 128
static inline int
unvname_compare(const struct unvname *n1, const struct unvname *n2)
{
if (n1->un_namesize == n2->un_namesize)
return (memcmp(n1->un_name, n2->un_name, n1->un_namesize));
else
return (n1->un_namesize - n2->un_namesize);
}
struct unvname *
unvname_new(const char *name, size_t size, u_char flags)
{
struct unvname *ret = malloc(sizeof(struct unvname), M_PROC, M_WAITOK);
ret->un_name = malloc(size, M_PROC, M_WAITOK);
memcpy(ret->un_name, name, size);
ret->un_namesize = size;
ret->un_flags = flags;
return ret;
}
void
unvname_delete(struct unvname *name)
{
free(name->un_name, M_PROC, name->un_namesize);
free(name, M_PROC, sizeof(struct unvname));
}
RBT_PROTOTYPE(unvname_rbt, unvname, un_rbt, unvname_compare);
RBT_GENERATE(unvname_rbt, unvname, un_rbt, unvname_compare);
int
unveil_delete_names(struct unveil *uv)
{
struct unvname *unvn, *next;
int ret = 0;
rw_enter_write(&uv->uv_lock);
RBT_FOREACH_SAFE(unvn, unvname_rbt, &uv->uv_names, next) {
RBT_REMOVE(unvname_rbt, &uv->uv_names, unvn);
unvname_delete(unvn);
ret++;
}
rw_exit_write(&uv->uv_lock);
DPRINTF("deleted %d names\n", ret);
return ret;
}
int
unveil_add_name_unlocked(struct unveil *uv, char *name, u_char flags)
{
struct unvname *unvn;
unvn = unvname_new(name, strlen(name) + 1, flags);
if (RBT_INSERT(unvname_rbt, &uv->uv_names, unvn) != NULL) {
/* Name already present. */
unvname_delete(unvn);
return 0;
}
DPRINTF("added name %s underneath vnode %p\n", name, uv->uv_vp);
return 1;
}
int
unveil_add_name(struct unveil *uv, char *name, u_char flags)
{
int ret;
rw_enter_write(&uv->uv_lock);
ret = unveil_add_name_unlocked(uv, name, flags);
rw_exit_write(&uv->uv_lock);
return ret;
}
struct unvname *
unveil_namelookup(struct unveil *uv, char *name)
{
struct unvname n, *ret = NULL;
rw_enter_read(&uv->uv_lock);
DPRINTF("%s: looking up name %s (%p) in vnode %p\n",
__func__, name, name, uv->uv_vp);
KASSERT(uv->uv_vp != NULL);
n.un_name = name;
n.un_namesize = strlen(name) + 1;
ret = RBT_FIND(unvname_rbt, &uv->uv_names, &n);
rw_exit_read(&uv->uv_lock);
DPRINTF("%s: %s name %s in vnode %p\n", __func__,
(ret == NULL) ? "no match for" : "matched",
name, uv->uv_vp);
return ret;
}
void
unveil_destroy(struct process *ps)
{
size_t i;
for (i = 0; ps->ps_uvpaths != NULL && i < ps->ps_uvvcount; i++) {
struct unveil *uv = ps->ps_uvpaths + i;
struct vnode *vp = uv->uv_vp;
/* skip any vnodes zapped by unveil_removevnode */
if (vp != NULL) {
vp->v_uvcount--;
DPRINTF("unveil: %s(%d): removing vnode %p uvcount %d "
"in position %ld\n",
ps->ps_comm, ps->ps_pid, vp, vp->v_uvcount, i);
vrele(vp);
}
ps->ps_uvncount -= unveil_delete_names(uv);
uv->uv_vp = NULL;
uv->uv_flags = 0;
}
KASSERT(ps->ps_uvncount == 0);
free(ps->ps_uvpaths, M_PROC, UNVEIL_MAX_VNODES *
sizeof(struct unveil));
ps->ps_uvvcount = 0;
ps->ps_uvpaths = NULL;
}
void
unveil_copy(struct process *parent, struct process *child)
{
size_t i;
child->ps_uvdone = parent->ps_uvdone;
if (parent->ps_uvvcount == 0)
return;
child->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES,
sizeof(struct unveil), M_PROC, M_WAITOK|M_ZERO);
child->ps_uvncount = 0;
for (i = 0; parent->ps_uvpaths != NULL && i < parent->ps_uvvcount;
i++) {
struct unveil *from = parent->ps_uvpaths + i;
struct unveil *to = child->ps_uvpaths + i;
struct unvname *unvn, *next;
to->uv_vp = from->uv_vp;
if (to->uv_vp != NULL) {
vref(to->uv_vp);
to->uv_vp->v_uvcount++;
}
rw_init(&to->uv_lock, "unveil");
RBT_INIT(unvname_rbt, &to->uv_names);
rw_enter_read(&from->uv_lock);
RBT_FOREACH_SAFE(unvn, unvname_rbt, &from->uv_names, next) {
if (unveil_add_name_unlocked(&child->ps_uvpaths[i],
unvn->un_name, unvn->un_flags))
child->ps_uvncount++;
}
rw_exit_read(&from->uv_lock);
to->uv_flags = from->uv_flags;
to->uv_cover = from->uv_cover;
}
child->ps_uvvcount = parent->ps_uvvcount;
}
/*
* Walk up from vnode dp, until we find a matching unveil, or the root vnode
* returns -1 if no unveil to be found above dp or if dp is the root vnode.
*/
ssize_t
unveil_find_cover(struct vnode *dp, struct proc *p)
{
struct vnode *vp = NULL, *parent = NULL, *root;
ssize_t ret = -1;
int error;
/* use the correct root to stop at, chrooted or not.. */
root = p->p_fd->fd_rdir ? p->p_fd->fd_rdir : rootvnode;
vp = dp;
while (vp != root) {
struct componentname cn = {
.cn_nameiop = LOOKUP,
.cn_flags = ISLASTCN | ISDOTDOT | RDONLY,
.cn_proc = p,
.cn_cred = p->p_ucred,
.cn_pnbuf = NULL,
.cn_nameptr = "..",
.cn_namelen = 2,
.cn_consume = 0
};
/*
* If we are at the root of a filesystem, and we are
* still mounted somewhere, take the .. in the above
* filesystem.
*/
if (vp != root && (vp->v_flag & VROOT)) {
if (vp->v_mount == NULL)
return -1;
vp = vp->v_mount->mnt_vnodecovered ?
vp->v_mount->mnt_vnodecovered : vp;
}
if (vget(vp, LK_EXCLUSIVE|LK_RETRY) != 0)
return -1;
/* Get parent vnode of vp using lookup of '..' */
/* This returns with vp unlocked but ref'ed*/
error = VOP_LOOKUP(vp, &parent, &cn);
if (error) {
if (!(cn.cn_flags & PDIRUNLOCK))
vput(vp);
else {
/*
* This corner case should not happen because
* we have not set LOCKPARENT in the flags
*/
DPRINTF("vnode %p PDIRUNLOCK on error\n", vp);
vrele(vp);
}
break;
}
vrele(vp);
(void) unveil_lookup(parent, p->p_p, &ret);
vput(parent);
if (ret >= 0)
break;
if (vp == parent) {
ret = -1;
break;
}
vp = parent;
parent = NULL;
}
return ret;
}
struct unveil *
unveil_lookup(struct vnode *vp, struct process *pr, ssize_t *position)
{
struct unveil *uv = pr->ps_uvpaths;
ssize_t i;
if (position != NULL)
*position = -1;
if (vp->v_uvcount == 0)
return NULL;
for (i = 0; i < pr->ps_uvvcount; i++) {
if (vp == uv[i].uv_vp) {
KASSERT(uv[i].uv_vp->v_uvcount > 0);
KASSERT(uv[i].uv_vp->v_usecount > 0);
if (position != NULL)
*position = i;
return &uv[i];
}
}
return NULL;
}
int
unveil_parsepermissions(const char *permissions, u_char *perms)
{
size_t i = 0;
char c;
*perms = UNVEIL_USERSET;
while ((c = permissions[i++]) != '\0') {
switch (c) {
case 'r':
*perms |= UNVEIL_READ;
break;
case 'w':
*perms |= UNVEIL_WRITE;
break;
case 'x':
*perms |= UNVEIL_EXEC;
break;
case 'c':
*perms |= UNVEIL_CREATE;
break;
default:
return -1;
}
}
return 0;
}
int
unveil_setflags(u_char *flags, u_char nflags)
{
#if 0
if (((~(*flags)) & nflags) != 0) {
DPRINTF("Flags escalation %llX -> %llX\n", *flags, nflags);
return 1;
}
#endif
*flags = nflags;
return 1;
}
struct unveil *
unveil_add_vnode(struct proc *p, struct vnode *vp)
{
struct process *pr = p->p_p;
struct unveil *uv = NULL;
ssize_t i;
KASSERT(pr->ps_uvvcount < UNVEIL_MAX_VNODES);
uv = &pr->ps_uvpaths[pr->ps_uvvcount++];
rw_init(&uv->uv_lock, "unveil");
RBT_INIT(unvname_rbt, &uv->uv_names);
uv->uv_vp = vp;
uv->uv_flags = 0;
/* find out what we are covered by */
uv->uv_cover = unveil_find_cover(vp, p);
/*
* Find anyone covered by what we are covered by
* and re-check what covers them (we could have
* interposed a cover)
*/
for (i = 0; i < pr->ps_uvvcount - 1; i++) {
if (pr->ps_uvpaths[i].uv_cover == uv->uv_cover)
pr->ps_uvpaths[i].uv_cover =
unveil_find_cover(pr->ps_uvpaths[i].uv_vp, p);
}
return (uv);
}
int
unveil_add(struct proc *p, struct nameidata *ndp, const char *permissions)
{
struct process *pr = p->p_p;
struct vnode *vp;
struct unveil *uv;
int directory_add;
int ret = EINVAL;
u_char flags;
KASSERT(ISSET(ndp->ni_cnd.cn_flags, HASBUF)); /* must have SAVENAME */
if (unveil_parsepermissions(permissions, &flags) == -1)
goto done;
if (pr->ps_uvpaths == NULL) {
pr->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES,
sizeof(struct unveil), M_PROC, M_WAITOK|M_ZERO);
}
if (pr->ps_uvvcount >= UNVEIL_MAX_VNODES ||
pr->ps_uvncount >= UNVEIL_MAX_NAMES) {
ret = E2BIG;
goto done;
}
/* Are we a directory? or something else */
directory_add = ndp->ni_vp != NULL && ndp->ni_vp->v_type == VDIR;
if (directory_add)
vp = ndp->ni_vp;
else
vp = ndp->ni_dvp;
KASSERT(vp->v_type == VDIR);
vref(vp);
vp->v_uvcount++;
if ((uv = unveil_lookup(vp, pr, NULL)) != NULL) {
/*
* We already have unveiled this directory
* vnode
*/
vp->v_uvcount--;
vrele(vp);
/*
* If we are adding a directory which was already
* unveiled containing only specific terminals,
* unrestrict it.
*/
if (directory_add) {
DPRINTF("unveil: %s(%d): updating directory vnode %p"
" to unrestricted uvcount %d\n",
pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
if (!unveil_setflags(&uv->uv_flags, flags))
ret = EPERM;
else
ret = 0;
goto done;
}
/*
* If we are adding a terminal that is already unveiled, just
* replace the flags and we are done
*/
if (!directory_add) {
struct unvname *tname;
if ((tname = unveil_namelookup(uv,
ndp->ni_cnd.cn_nameptr)) != NULL) {
DPRINTF("unveil: %s(%d): changing flags for %s"
"in vnode %p, uvcount %d\n",
pr->ps_comm, pr->ps_pid, tname->un_name, vp,
vp->v_uvcount);
if (!unveil_setflags(&tname->un_flags, flags))
ret = EPERM;
else
ret = 0;
goto done;
}
}
} else {
/*
* New unveil involving this directory vnode.
*/
uv = unveil_add_vnode(p, vp);
}
/*
* At this stage with have a unveil in uv with a vnode for a
* directory. If the component we are adding is a directory,
* we are done. Otherwise, we add the component name the name
* list in uv.
*/
if (directory_add) {
uv->uv_flags = flags;
ret = 0;
DPRINTF("unveil: %s(%d): added unrestricted directory vnode %p"
", uvcount %d\n",
pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
goto done;
}
if (unveil_add_name(uv, ndp->ni_cnd.cn_nameptr, flags))
pr->ps_uvncount++;
ret = 0;
DPRINTF("unveil: %s(%d): added name %s beneath %s vnode %p,"
" uvcount %d\n",
pr->ps_comm, pr->ps_pid, ndp->ni_cnd.cn_nameptr,
uv->uv_flags ? "unrestricted" : "restricted",
vp, vp->v_uvcount);
done:
return ret;
}
/*
* XXX this will probably change.
* XXX collapse down later once debug surely unneeded
*/
int
unveil_flagmatch(struct nameidata *ni, u_char flags)
{
if (flags == 0) {
DPRINTF("All operations forbidden for 0 flags\n");
return 0;
}
if (ni->ni_unveil & UNVEIL_READ) {
if ((flags & UNVEIL_READ) == 0) {
DPRINTF("unveil lacks UNVEIL_READ\n");
return 0;
}
}
if (ni->ni_unveil & UNVEIL_WRITE) {
if ((flags & UNVEIL_WRITE) == 0) {
DPRINTF("unveil lacks UNVEIL_WRITE\n");
return 0;
}
}
if (ni->ni_unveil & UNVEIL_EXEC) {
if ((flags & UNVEIL_EXEC) == 0) {
DPRINTF("unveil lacks UNVEIL_EXEC\n");
return 0;
}
}
if (ni->ni_unveil & UNVEIL_CREATE) {
if ((flags & UNVEIL_CREATE) == 0) {
DPRINTF("unveil lacks UNVEIL_CREATE\n");
return 0;
}
}
return 1;
}
/*
* When traversing up towards the root figure out the proper unveil for
* the parent directory.
*/
struct unveil *
unveil_covered(struct unveil *uv, struct vnode *dvp, struct proc *p)
{
if (uv && uv->uv_vp == dvp) {
/* if at the root, chrooted or not, return the current uv */
if (dvp == (p->p_fd->fd_rdir ? p->p_fd->fd_rdir : rootvnode))
return uv;
if (uv->uv_cover >=0) {
KASSERT(uv->uv_cover < p->p_p->ps_uvvcount);
return &p->p_p->ps_uvpaths[uv->uv_cover];
}
return NULL;
}
return uv;
}
/*
* Start a relative path lookup. Ensure we find whatever unveil covered
* where we start from, either by having a saved current working directory
* unveil, or by walking up and finding a cover the hard way if we are
* doing a non AT_FDCWD relative lookup. Caller passes a NULL dp
* if we are using AT_FDCWD.
*/
void
unveil_start_relative(struct proc *p, struct nameidata *ni, struct vnode *dp)
{
struct process *pr = p->p_p;
struct unveil *uv = NULL;
ssize_t uvi;
if (pr->ps_uvpaths == NULL)
return;
uv = unveil_lookup(dp, pr, NULL);
if (uv == NULL) {
uvi = unveil_find_cover(dp, p);
if (uvi >= 0) {
KASSERT(uvi < pr->ps_uvvcount);
uv = &pr->ps_uvpaths[uvi];
}
}
/*
* Store this match for later use. Flags are checked at the end.
*/
if (uv) {
DPRINTF("unveil: %s(%d): relative unveil at %p matches",
pr->ps_comm, pr->ps_pid, uv);
ni->ni_unveil_match = uv;
}
}
/*
* unveil checking - for component directories in a namei lookup.
*/
void
unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp)
{
struct process *pr = p->p_p;
struct unveil *uv = NULL;
if (ni->ni_pledge == PLEDGE_UNVEIL || pr->ps_uvpaths == NULL)
return;
if (ni->ni_cnd.cn_flags & BYPASSUNVEIL)
return;
if (ni->ni_cnd.cn_flags & ISDOTDOT) {
/*
* adjust unveil match as necessary
*/
uv = unveil_covered(ni->ni_unveil_match, dp, p);
/* clear the match when we DOTDOT above it */
if (ni->ni_unveil_match && ni->ni_unveil_match->uv_vp == dp)
ni->ni_unveil_match = NULL;
} else
uv = unveil_lookup(dp, pr, NULL);
if (uv != NULL) {
/* update match */
ni->ni_unveil_match = uv;
DPRINTF("unveil: %s(%d): component directory match for "
"vnode %p\n", pr->ps_comm, pr->ps_pid, dp);
}
}
/*
* unveil checking - only done after namei lookup has succeeded on
* the last component of a namei lookup.
*/
int
unveil_check_final(struct proc *p, struct nameidata *ni)
{
struct process *pr = p->p_p;
struct unveil *uv = NULL, *nuv;
struct unvname *tname = NULL;
if (ni->ni_pledge == PLEDGE_UNVEIL || pr->ps_uvpaths == NULL)
return (0);
if (ni->ni_cnd.cn_flags & BYPASSUNVEIL) {
DPRINTF("unveil: %s(%d): BYPASSUNVEIL.\n",
pr->ps_comm, pr->ps_pid);
return (0);
}
if (ni->ni_vp != NULL && ni->ni_vp->v_type == VDIR) {
/* We are matching a directory terminal component */
uv = unveil_lookup(ni->ni_vp, pr, NULL);
if (uv == NULL || (uv->uv_flags & UNVEIL_USERSET) == 0) {
DPRINTF("unveil: %s(%d) no match for vnode %p\n",
pr->ps_comm, pr->ps_pid, ni->ni_vp);
if (uv != NULL)
ni->ni_unveil_match = uv;
goto done;
}
if (!unveil_flagmatch(ni, uv->uv_flags)) {
DPRINTF("unveil: %s(%d) flag mismatch for directory"
" vnode %p\n",
pr->ps_comm, pr->ps_pid, ni->ni_vp);
pr->ps_acflag |= AUNVEIL;
if (uv->uv_flags & UNVEIL_MASK)
return EACCES;
else
return ENOENT;
}
/* directory and flags match, success */
DPRINTF("unveil: %s(%d): matched directory \"%s\" at vnode %p\n",
pr->ps_comm, pr->ps_pid, ni->ni_cnd.cn_nameptr,
uv->uv_vp);
return (0);
}
/* Otherwise, we are matching a non-terminal component */
uv = unveil_lookup(ni->ni_dvp, pr, NULL);
if (uv == NULL) {
DPRINTF("unveil: %s(%d) no match for directory vnode %p\n",
pr->ps_comm, pr->ps_pid, ni->ni_dvp);
goto done;
}
if ((tname = unveil_namelookup(uv, ni->ni_cnd.cn_nameptr)) == NULL) {
DPRINTF("unveil: %s(%d) no match for terminal '%s' in "
"directory vnode %p\n",
pr->ps_comm, pr->ps_pid,
ni->ni_cnd.cn_nameptr, ni->ni_dvp);
/* no specific name, so check unveil directory flags */
if (!unveil_flagmatch(ni, uv->uv_flags)) {
DPRINTF("unveil: %s(%d) terminal "
"'%s' flags mismatch in directory "
"vnode %p\n",
pr->ps_comm, pr->ps_pid,
ni->ni_cnd.cn_nameptr, ni->ni_dvp);
/*
* If dir has user set restrictions fail with
* EACCES or ENOENT. Otherwise, use any covering
* match that we found above this dir.
*/
if (uv->uv_flags & UNVEIL_USERSET) {
pr->ps_acflag |= AUNVEIL;
if (uv->uv_flags & UNVEIL_MASK)
return EACCES;
else
return ENOENT;
}
/* start backtrack from this node */
ni->ni_unveil_match = uv;
goto done;
}
/* directory flags match, success */
DPRINTF("unveil: %s(%d): matched \"%s\" underneath vnode %p\n",
pr->ps_comm, pr->ps_pid, ni->ni_cnd.cn_nameptr,
uv->uv_vp);
return (0);
}
if (!unveil_flagmatch(ni, tname->un_flags)) {
/* do flags match for matched name */
DPRINTF("unveil: %s(%d) flag mismatch for terminal '%s'\n",
pr->ps_comm, pr->ps_pid, tname->un_name);
pr->ps_acflag |= AUNVEIL;
return EACCES;
}
/* name and flags match. success */
DPRINTF("unveil: %s(%d) matched terminal '%s'\n",
pr->ps_comm, pr->ps_pid, tname->un_name);
return (0);
done:
/*
* last component did not match, check previous matches if
* access is allowed or not.
*/
for (uv = ni->ni_unveil_match; uv != NULL; uv = nuv) {
if (unveil_flagmatch(ni, uv->uv_flags)) {
DPRINTF("unveil: %s(%d): matched \"%s\" underneath/at "
"vnode %p\n", pr->ps_comm, pr->ps_pid,
ni->ni_cnd.cn_nameptr, uv->uv_vp);
return (0);
}
/* if node has any flags set then this is an access violation */
if (uv->uv_flags & UNVEIL_USERSET) {
DPRINTF("unveil: %s(%d) flag mismatch for vnode %p\n",
pr->ps_comm, pr->ps_pid, uv->uv_vp);
pr->ps_acflag |= AUNVEIL;
if (uv->uv_flags & UNVEIL_MASK)
return EACCES;
else
return ENOENT;
}
DPRINTF("unveil: %s(%d) check cover for vnode %p, uv_cover %zd\n",
pr->ps_comm, pr->ps_pid, uv->uv_vp, uv->uv_cover);
nuv = unveil_covered(uv, uv->uv_vp, p);
if (nuv == uv)
break;
}
pr->ps_acflag |= AUNVEIL;
return ENOENT;
}
/*
* Scan all active processes to see if any of them have a unveil
* to this vnode. If so, NULL the vnode in their unveil list,
* vrele, drop the reference, and mark their unveil list
* as needing to have the hole shrunk the next time the process
* uses it for lookup.
*/
void
unveil_removevnode(struct vnode *vp)
{
struct process *pr;
if (vp->v_uvcount == 0)
return;
DPRINTF("%s: found vnode %p with count %d\n",
__func__, vp, vp->v_uvcount);
vref(vp); /* make sure it is held till we are done */
LIST_FOREACH(pr, &allprocess, ps_list) {
struct unveil * uv;
if ((uv = unveil_lookup(vp, pr, NULL)) != NULL &&
uv->uv_vp != NULL) {
uv->uv_vp = NULL;
uv->uv_flags = 0;
DPRINTF("%s: vnode %p now count %d\n",
__func__, vp, vp->v_uvcount);
if (vp->v_uvcount > 0) {
vrele(vp);
vp->v_uvcount--;
} else
panic("vp %p, v_uvcount of %d should be 0",
vp, vp->v_uvcount);
}
}
KASSERT(vp->v_uvcount == 0);
vrele(vp); /* release our ref */
}
7
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* $OpenBSD: pvclock.c,v 1.8 2021/11/05 11:38:29 mpi Exp $ */
/*
* Copyright (c) 2018 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if !defined(__i386__) && !defined(__amd64__)
#error pvclock(4) is only supported on i386 and amd64
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/timetc.h>
#include <sys/timeout.h>
#include <sys/malloc.h>
#include <sys/atomic.h>
#include <machine/cpu.h>
#include <machine/atomic.h>
#include <uvm/uvm_extern.h>
#include <dev/pv/pvvar.h>
#include <dev/pv/pvreg.h>
uint pvclock_lastcount;
struct pvclock_softc {
struct device sc_dev;
void *sc_time;
paddr_t sc_paddr;
struct timecounter *sc_tc;
};
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
int pvclock_match(struct device *, void *, void *);
void pvclock_attach(struct device *, struct device *, void *);
int pvclock_activate(struct device *, int);
uint pvclock_get_timecount(struct timecounter *);
void pvclock_read_time_info(struct pvclock_softc *,
struct pvclock_time_info *);
static inline uint32_t
pvclock_read_begin(const struct pvclock_time_info *);
static inline int
pvclock_read_done(const struct pvclock_time_info *, uint32_t);
const struct cfattach pvclock_ca = {
sizeof(struct pvclock_softc),
pvclock_match,
pvclock_attach,
NULL,
pvclock_activate
};
struct cfdriver pvclock_cd = {
NULL,
"pvclock",
DV_DULL
};
struct timecounter pvclock_timecounter = {
.tc_get_timecount = pvclock_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_name = NULL,
.tc_quality = -2000,
.tc_priv = NULL,
.tc_user = 0,
};
int
pvclock_match(struct device *parent, void *match, void *aux)
{
struct pv_attach_args *pva = aux;
struct pvbus_hv *hv;
/*
* pvclock is provided by different hypervisors, we currently
* only support the "kvmclock".
*/
hv = &pva->pva_hv[PVBUS_KVM];
if (hv->hv_base == 0)
hv = &pva->pva_hv[PVBUS_OPENBSD];
if (hv->hv_base != 0) {
/*
* We only implement support for the 2nd version of pvclock.
* The first version is basically the same but with different
* non-standard MSRs and it is deprecated.
*/
if ((hv->hv_features & (1 << KVM_FEATURE_CLOCKSOURCE2)) == 0)
return (0);
/*
* Only the "stable" clock with a sync'ed TSC is supported.
* In this case the host guarantees that the TSC is constant
* and invariant, either by the underlying TSC or by passing
* on a synchronized value.
*/
if ((hv->hv_features &
(1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)) == 0)
return (0);
return (1);
}
return (0);
}
void
pvclock_attach(struct device *parent, struct device *self, void *aux)
{
struct pvclock_softc *sc = (struct pvclock_softc *)self;
struct pvclock_time_info *ti;
paddr_t pa;
uint32_t version;
uint8_t flags;
if ((sc->sc_time = km_alloc(PAGE_SIZE,
&kv_any, &kp_zero, &kd_nowait)) == NULL) {
printf(": time page allocation failed\n");
return;
}
if (!pmap_extract(pmap_kernel(), (vaddr_t)sc->sc_time, &pa)) {
printf(": time page PA extraction failed\n");
km_free(sc->sc_time, PAGE_SIZE, &kv_any, &kp_zero);
return;
}
wrmsr(KVM_MSR_SYSTEM_TIME, pa | PVCLOCK_SYSTEM_TIME_ENABLE);
sc->sc_paddr = pa;
ti = sc->sc_time;
do {
version = pvclock_read_begin(ti);
flags = ti->ti_flags;
} while (!pvclock_read_done(ti, version));
sc->sc_tc = &pvclock_timecounter;
sc->sc_tc->tc_name = DEVNAME(sc);
sc->sc_tc->tc_frequency = 1000000000ULL;
sc->sc_tc->tc_priv = sc;
pvclock_lastcount = 0;
/* Better than HPET but below TSC */
sc->sc_tc->tc_quality = 1500;
if ((flags & PVCLOCK_FLAG_TSC_STABLE) == 0) {
/* if tsc is not stable, set a lower priority */
/* Better than i8254 but below HPET */
sc->sc_tc->tc_quality = 500;
}
tc_init(sc->sc_tc);
printf("\n");
}
int
pvclock_activate(struct device *self, int act)
{
struct pvclock_softc *sc = (struct pvclock_softc *)self;
int rv = 0;
paddr_t pa = sc->sc_paddr;
switch (act) {
case DVACT_POWERDOWN:
wrmsr(KVM_MSR_SYSTEM_TIME, pa & ~PVCLOCK_SYSTEM_TIME_ENABLE);
break;
case DVACT_RESUME:
wrmsr(KVM_MSR_SYSTEM_TIME, pa | PVCLOCK_SYSTEM_TIME_ENABLE);
break;
}
return (rv);
}
static inline uint32_t
pvclock_read_begin(const struct pvclock_time_info *ti)
{
uint32_t version = ti->ti_version & ~0x1;
virtio_membar_sync();
return (version);
}
static inline int
pvclock_read_done(const struct pvclock_time_info *ti,
uint32_t version)
{
virtio_membar_sync();
return (ti->ti_version == version);
}
uint
pvclock_get_timecount(struct timecounter *tc)
{
struct pvclock_softc *sc = tc->tc_priv;
struct pvclock_time_info *ti;
uint64_t tsc_timestamp, system_time, delta, ctr;
uint32_t version, mul_frac;
int8_t shift;
uint8_t flags;
ti = sc->sc_time;
do {
version = pvclock_read_begin(ti);
system_time = ti->ti_system_time;
tsc_timestamp = ti->ti_tsc_timestamp;
mul_frac = ti->ti_tsc_to_system_mul;
shift = ti->ti_tsc_shift;
flags = ti->ti_flags;
} while (!pvclock_read_done(ti, version));
/*
* The algorithm is described in
* linux/Documentation/virtual/kvm/msr.txt
*/
delta = rdtsc() - tsc_timestamp;
if (shift < 0)
delta >>= -shift;
else
delta <<= shift;
ctr = ((delta * mul_frac) >> 32) + system_time;
if ((flags & PVCLOCK_FLAG_TSC_STABLE) != 0)
return (ctr);
if (ctr < pvclock_lastcount)
return (pvclock_lastcount);
atomic_swap_uint(&pvclock_lastcount, ctr);
return (ctr);
}
2
2
2
2
2
2
217
217
210
17
219
218
217
218
2
218
2
2
216
217
217
2
2
2
2
2
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
/* $OpenBSD: virtio.c,v 1.21 2022/01/09 05:42:58 jsg Exp $ */
/* $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */
/*
* Copyright (c) 2012 Stefan Fritsch, Alexander Fiveg.
* Copyright (c) 2010 Minoura Makoto.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/mutex.h>
#include <sys/atomic.h>
#include <sys/malloc.h>
#include <dev/pv/virtioreg.h>
#include <dev/pv/virtiovar.h>
#if VIRTIO_DEBUG
#define VIRTIO_ASSERT(x) KASSERT(x)
#else
#define VIRTIO_ASSERT(x)
#endif
void virtio_init_vq(struct virtio_softc *,
struct virtqueue *);
void vq_free_entry(struct virtqueue *, struct vq_entry *);
struct vq_entry *vq_alloc_entry(struct virtqueue *);
struct cfdriver virtio_cd = {
NULL, "virtio", DV_DULL
};
static const char * const virtio_device_name[] = {
"Unknown (0)", /* 0 */
"Network", /* 1 */
"Block", /* 2 */
"Console", /* 3 */
"Entropy", /* 4 */
"Memory Balloon", /* 5 */
"IO Memory", /* 6 */
"Rpmsg", /* 7 */
"SCSI host", /* 8 */
"9P Transport", /* 9 */
"mac80211 wlan" /* 10 */
};
#define NDEVNAMES (sizeof(virtio_device_name)/sizeof(char*))
const char *
virtio_device_string(int id)
{
return id < NDEVNAMES ? virtio_device_name[id] : "Unknown";
}
#if VIRTIO_DEBUG
static const struct virtio_feature_name transport_feature_names[] = {
{ VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty"},
{ VIRTIO_F_RING_INDIRECT_DESC, "RingIndirectDesc"},
{ VIRTIO_F_RING_EVENT_IDX, "RingEventIdx"},
{ VIRTIO_F_BAD_FEATURE, "BadFeature"},
{ VIRTIO_F_VERSION_1, "Version1"},
{ 0, NULL}
};
void
virtio_log_features(uint64_t host, uint64_t neg,
const struct virtio_feature_name *guest_feature_names)
{
const struct virtio_feature_name *namep;
int i;
char c;
uint32_t bit;
for (i = 0; i < 64; i++) {
if (i == 30) {
/*
* VIRTIO_F_BAD_FEATURE is only used for
* checking correct negotiation
*/
continue;
}
bit = 1 << i;
if ((host&bit) == 0)
continue;
namep = (i < 24 || i > 37) ? guest_feature_names :
transport_feature_names;
while (namep->bit && namep->bit != bit)
namep++;
c = (neg&bit) ? '+' : '-';
if (namep->name)
printf(" %c%s", c, namep->name);
else
printf(" %cUnknown(%d)", c, i);
}
}
#endif
/*
* Reset the device.
*/
/*
* To reset the device to a known state, do following:
* virtio_reset(sc); // this will stop the device activity
* <dequeue finished requests>; // virtio_dequeue() still can be called
* <revoke pending requests in the vqs if any>;
* virtio_reinit_start(sc); // dequeue prohibited
* <some other initialization>;
* virtio_reinit_end(sc); // device activated; enqueue allowed
* Once attached, features are assumed to not change again.
*/
void
virtio_reset(struct virtio_softc *sc)
{
virtio_device_reset(sc);
sc->sc_active_features = 0;
}
void
virtio_reinit_start(struct virtio_softc *sc)
{
int i;
virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
virtio_negotiate_features(sc, NULL);
for (i = 0; i < sc->sc_nvqs; i++) {
int n;
struct virtqueue *vq = &sc->sc_vqs[i];
n = virtio_read_queue_size(sc, vq->vq_index);
if (n == 0) /* vq disappeared */
continue;
if (n != vq->vq_num) {
panic("%s: virtqueue size changed, vq index %d",
sc->sc_dev.dv_xname, vq->vq_index);
}
virtio_init_vq(sc, vq);
virtio_setup_queue(sc, vq, vq->vq_dmamap->dm_segs[0].ds_addr);
}
}
void
virtio_reinit_end(struct virtio_softc *sc)
{
virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK);
}
/*
* dmamap sync operations for a virtqueue.
*/
static inline void
vq_sync_descs(struct virtio_softc *sc, struct virtqueue *vq, int ops)
{
/* availoffset == sizeof(vring_desc)*vq_num */
bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, 0, vq->vq_availoffset,
ops);
}
static inline void
vq_sync_aring(struct virtio_softc *sc, struct virtqueue *vq, int ops)
{
bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_availoffset,
offsetof(struct vring_avail, ring) + vq->vq_num * sizeof(uint16_t),
ops);
}
static inline void
vq_sync_uring(struct virtio_softc *sc, struct virtqueue *vq, int ops)
{
bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_usedoffset,
offsetof(struct vring_used, ring) + vq->vq_num *
sizeof(struct vring_used_elem), ops);
}
static inline void
vq_sync_indirect(struct virtio_softc *sc, struct virtqueue *vq, int slot,
int ops)
{
int offset = vq->vq_indirectoffset +
sizeof(struct vring_desc) * vq->vq_maxnsegs * slot;
bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, offset,
sizeof(struct vring_desc) * vq->vq_maxnsegs, ops);
}
/*
* Scan vq, bus_dmamap_sync for the vqs (not for the payload),
* and calls (*vq_done)() if some entries are consumed.
* For use in transport specific irq handlers.
*/
int
virtio_check_vqs(struct virtio_softc *sc)
{
struct virtqueue *vq;
int i, r = 0;
/* going backwards is better for if_vio */
for (i = sc->sc_nvqs - 1; i >= 0; i--) {
vq = &sc->sc_vqs[i];
if (vq->vq_queued) {
vq->vq_queued = 0;
vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE);
}
vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD);
if (vq->vq_used_idx != vq->vq_used->idx) {
if (vq->vq_done)
r |= (vq->vq_done)(vq);
}
}
return r;
}
/*
* Initialize vq structure.
*/
void
virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq)
{
int i, j;
int vq_size = vq->vq_num;
memset(vq->vq_vaddr, 0, vq->vq_bytesize);
/* build the indirect descriptor chain */
if (vq->vq_indirect != NULL) {
struct vring_desc *vd;
for (i = 0; i < vq_size; i++) {
vd = vq->vq_indirect;
vd += vq->vq_maxnsegs * i;
for (j = 0; j < vq->vq_maxnsegs-1; j++)
vd[j].next = j + 1;
}
}
/* free slot management */
SLIST_INIT(&vq->vq_freelist);
/*
* virtio_enqueue_trim needs monotonely raising entries, therefore
* initialize in reverse order
*/
for (i = vq_size - 1; i >= 0; i--) {
SLIST_INSERT_HEAD(&vq->vq_freelist, &vq->vq_entries[i],
qe_list);
vq->vq_entries[i].qe_index = i;
}
/* enqueue/dequeue status */
vq->vq_avail_idx = 0;
vq->vq_used_idx = 0;
vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
vq_sync_uring(sc, vq, BUS_DMASYNC_PREREAD);
vq->vq_queued = 1;
}
/*
* Allocate/free a vq.
*
* maxnsegs denotes how much space should be allocated for indirect
* descriptors. maxnsegs == 1 can be used to disable use indirect
* descriptors for this queue.
*/
int
virtio_alloc_vq(struct virtio_softc *sc, struct virtqueue *vq, int index,
int maxsegsize, int maxnsegs, const char *name)
{
int vq_size, allocsize1, allocsize2, allocsize3, allocsize = 0;
int rsegs, r, hdrlen;
#define VIRTQUEUE_ALIGN(n) (((n)+(VIRTIO_PAGE_SIZE-1))& \
~(VIRTIO_PAGE_SIZE-1))
memset(vq, 0, sizeof(*vq));
vq_size = virtio_read_queue_size(sc, index);
if (vq_size == 0) {
printf("virtqueue not exist, index %d for %s\n", index, name);
goto err;
}
if (((vq_size - 1) & vq_size) != 0)
panic("vq_size not power of two: %d", vq_size);
hdrlen = virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX) ? 3 : 2;
/* allocsize1: descriptor table + avail ring + pad */
allocsize1 = VIRTQUEUE_ALIGN(sizeof(struct vring_desc) * vq_size
+ sizeof(uint16_t) * (hdrlen + vq_size));
/* allocsize2: used ring + pad */
allocsize2 = VIRTQUEUE_ALIGN(sizeof(uint16_t) * hdrlen
+ sizeof(struct vring_used_elem) * vq_size);
/* allocsize3: indirect table */
if (sc->sc_indirect && maxnsegs > 1)
allocsize3 = sizeof(struct vring_desc) * maxnsegs * vq_size;
else
allocsize3 = 0;
allocsize = allocsize1 + allocsize2 + allocsize3;
/* alloc and map the memory */
r = bus_dmamem_alloc(sc->sc_dmat, allocsize, VIRTIO_PAGE_SIZE, 0,
&vq->vq_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
if (r != 0) {
printf("virtqueue %d for %s allocation failed, error %d\n",
index, name, r);
goto err;
}
r = bus_dmamem_map(sc->sc_dmat, &vq->vq_segs[0], 1, allocsize,
(caddr_t*)&vq->vq_vaddr, BUS_DMA_NOWAIT);
if (r != 0) {
printf("virtqueue %d for %s map failed, error %d\n", index,
name, r);
goto err;
}
r = bus_dmamap_create(sc->sc_dmat, allocsize, 1, allocsize, 0,
BUS_DMA_NOWAIT, &vq->vq_dmamap);
if (r != 0) {
printf("virtqueue %d for %s dmamap creation failed, "
"error %d\n", index, name, r);
goto err;
}
r = bus_dmamap_load(sc->sc_dmat, vq->vq_dmamap, vq->vq_vaddr,
allocsize, NULL, BUS_DMA_NOWAIT);
if (r != 0) {
printf("virtqueue %d for %s dmamap load failed, error %d\n",
index, name, r);
goto err;
}
/* remember addresses and offsets for later use */
vq->vq_owner = sc;
vq->vq_num = vq_size;
vq->vq_mask = vq_size - 1;
vq->vq_index = index;
vq->vq_desc = vq->vq_vaddr;
vq->vq_availoffset = sizeof(struct vring_desc)*vq_size;
vq->vq_avail = (struct vring_avail*)(((char*)vq->vq_desc) +
vq->vq_availoffset);
vq->vq_usedoffset = allocsize1;
vq->vq_used = (struct vring_used*)(((char*)vq->vq_desc) +
vq->vq_usedoffset);
if (allocsize3 > 0) {
vq->vq_indirectoffset = allocsize1 + allocsize2;
vq->vq_indirect = (void*)(((char*)vq->vq_desc)
+ vq->vq_indirectoffset);
}
vq->vq_bytesize = allocsize;
vq->vq_maxnsegs = maxnsegs;
/* free slot management */
vq->vq_entries = mallocarray(vq_size, sizeof(struct vq_entry),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (vq->vq_entries == NULL) {
r = ENOMEM;
goto err;
}
virtio_init_vq(sc, vq);
virtio_setup_queue(sc, vq, vq->vq_dmamap->dm_segs[0].ds_addr);
#if VIRTIO_DEBUG
printf("\nallocated %u byte for virtqueue %d for %s, size %d\n",
allocsize, index, name, vq_size);
if (allocsize3 > 0)
printf("using %d byte (%d entries) indirect descriptors\n",
allocsize3, maxnsegs * vq_size);
#endif
return 0;
err:
if (vq->vq_dmamap)
bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap);
if (vq->vq_vaddr)
bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, allocsize);
if (vq->vq_segs[0].ds_addr)
bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1);
memset(vq, 0, sizeof(*vq));
return -1;
}
int
virtio_free_vq(struct virtio_softc *sc, struct virtqueue *vq)
{
struct vq_entry *qe;
int i = 0;
/* device must be already deactivated */
/* confirm the vq is empty */
SLIST_FOREACH(qe, &vq->vq_freelist, qe_list) {
i++;
}
if (i != vq->vq_num) {
printf("%s: freeing non-empty vq, index %d\n",
sc->sc_dev.dv_xname, vq->vq_index);
return EBUSY;
}
/* tell device that there's no virtqueue any longer */
virtio_setup_queue(sc, vq, 0);
free(vq->vq_entries, M_DEVBUF, 0);
bus_dmamap_unload(sc->sc_dmat, vq->vq_dmamap);
bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap);
bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, vq->vq_bytesize);
bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1);
memset(vq, 0, sizeof(*vq));
return 0;
}
/*
* Free descriptor management.
*/
struct vq_entry *
vq_alloc_entry(struct virtqueue *vq)
{
struct vq_entry *qe;
if (SLIST_EMPTY(&vq->vq_freelist))
return NULL;
qe = SLIST_FIRST(&vq->vq_freelist);
SLIST_REMOVE_HEAD(&vq->vq_freelist, qe_list);
return qe;
}
void
vq_free_entry(struct virtqueue *vq, struct vq_entry *qe)
{
SLIST_INSERT_HEAD(&vq->vq_freelist, qe, qe_list);
}
/*
* Enqueue several dmamaps as a single request.
*/
/*
* Typical usage:
* <queue size> number of followings are stored in arrays
* - command blocks (in dmamem) should be pre-allocated and mapped
* - dmamaps for command blocks should be pre-allocated and loaded
* - dmamaps for payload should be pre-allocated
* r = virtio_enqueue_prep(sc, vq, &slot); // allocate a slot
* if (r) // currently 0 or EAGAIN
* return r;
* r = bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..);
* if (r) {
* virtio_enqueue_abort(sc, vq, slot);
* bus_dmamap_unload(dmat, dmamap_payload[slot]);
* return r;
* }
* r = virtio_enqueue_reserve(sc, vq, slot,
* dmamap_payload[slot]->dm_nsegs+1);
* // ^ +1 for command
* if (r) { // currently 0 or EAGAIN
* bus_dmamap_unload(dmat, dmamap_payload[slot]);
* return r; // do not call abort()
* }
* <setup and prepare commands>
* bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE);
* bus_dmamap_sync(dmat, dmamap_payload[slot],...);
* virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], 0);
* virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite);
* virtio_enqueue_commit(sc, vq, slot, 1);
*
* Alternative usage with statically allocated slots:
* <during initialization>
* // while not out of slots, do
* virtio_enqueue_prep(sc, vq, &slot); // allocate a slot
* virtio_enqueue_reserve(sc, vq, slot, max_segs); // reserve all slots
* that may ever be needed
*
* <when enqueuing a request>
* // Don't call virtio_enqueue_prep()
* bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..);
* bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE);
* bus_dmamap_sync(dmat, dmamap_payload[slot],...);
* virtio_enqueue_trim(sc, vq, slot, num_segs_needed);
* virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], 0);
* virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite);
* virtio_enqueue_commit(sc, vq, slot, 1);
*
* <when dequeuing>
* // don't call virtio_dequeue_commit()
*/
/*
* enqueue_prep: allocate a slot number
*/
int
virtio_enqueue_prep(struct virtqueue *vq, int *slotp)
{
struct vq_entry *qe1;
VIRTIO_ASSERT(slotp != NULL);
qe1 = vq_alloc_entry(vq);
if (qe1 == NULL)
return EAGAIN;
/* next slot is not allocated yet */
qe1->qe_next = -1;
*slotp = qe1->qe_index;
return 0;
}
/*
* enqueue_reserve: allocate remaining slots and build the descriptor chain.
* Calls virtio_enqueue_abort() on failure.
*/
int
virtio_enqueue_reserve(struct virtqueue *vq, int slot, int nsegs)
{
struct vq_entry *qe1 = &vq->vq_entries[slot];
VIRTIO_ASSERT(qe1->qe_next == -1);
VIRTIO_ASSERT(1 <= nsegs && nsegs <= vq->vq_num);
if (vq->vq_indirect != NULL && nsegs > 1 && nsegs <= vq->vq_maxnsegs) {
struct vring_desc *vd;
int i;
qe1->qe_indirect = 1;
vd = &vq->vq_desc[qe1->qe_index];
vd->addr = vq->vq_dmamap->dm_segs[0].ds_addr +
vq->vq_indirectoffset;
vd->addr += sizeof(struct vring_desc) * vq->vq_maxnsegs *
qe1->qe_index;
vd->len = sizeof(struct vring_desc) * nsegs;
vd->flags = VRING_DESC_F_INDIRECT;
vd = vq->vq_indirect;
vd += vq->vq_maxnsegs * qe1->qe_index;
qe1->qe_desc_base = vd;
for (i = 0; i < nsegs-1; i++)
vd[i].flags = VRING_DESC_F_NEXT;
vd[i].flags = 0;
qe1->qe_next = 0;
return 0;
} else {
struct vring_desc *vd;
struct vq_entry *qe;
int i, s;
qe1->qe_indirect = 0;
vd = &vq->vq_desc[0];
qe1->qe_desc_base = vd;
qe1->qe_next = qe1->qe_index;
s = slot;
for (i = 0; i < nsegs - 1; i++) {
qe = vq_alloc_entry(vq);
if (qe == NULL) {
vd[s].flags = 0;
virtio_enqueue_abort(vq, slot);
return EAGAIN;
}
vd[s].flags = VRING_DESC_F_NEXT;
vd[s].next = qe->qe_index;
s = qe->qe_index;
}
vd[s].flags = 0;
return 0;
}
}
/*
* enqueue: enqueue a single dmamap.
*/
int
virtio_enqueue(struct virtqueue *vq, int slot, bus_dmamap_t dmamap, int write)
{
struct vq_entry *qe1 = &vq->vq_entries[slot];
struct vring_desc *vd = qe1->qe_desc_base;
int i;
int s = qe1->qe_next;
VIRTIO_ASSERT(s >= 0);
VIRTIO_ASSERT(dmamap->dm_nsegs > 0);
if (dmamap->dm_nsegs > vq->vq_maxnsegs) {
#if VIRTIO_DEBUG
for (i = 0; i < dmamap->dm_nsegs; i++) {
printf(" %d (%d): %p %lx \n", i, write,
(void *)dmamap->dm_segs[i].ds_addr,
dmamap->dm_segs[i].ds_len);
}
#endif
panic("dmamap->dm_nseg %d > vq->vq_maxnsegs %d",
dmamap->dm_nsegs, vq->vq_maxnsegs);
}
for (i = 0; i < dmamap->dm_nsegs; i++) {
vd[s].addr = dmamap->dm_segs[i].ds_addr;
vd[s].len = dmamap->dm_segs[i].ds_len;
if (!write)
vd[s].flags |= VRING_DESC_F_WRITE;
s = vd[s].next;
}
qe1->qe_next = s;
return 0;
}
int
virtio_enqueue_p(struct virtqueue *vq, int slot, bus_dmamap_t dmamap,
bus_addr_t start, bus_size_t len, int write)
{
struct vq_entry *qe1 = &vq->vq_entries[slot];
struct vring_desc *vd = qe1->qe_desc_base;
int s = qe1->qe_next;
VIRTIO_ASSERT(s >= 0);
/* XXX todo: handle more segments */
VIRTIO_ASSERT(dmamap->dm_nsegs == 1);
VIRTIO_ASSERT((dmamap->dm_segs[0].ds_len > start) &&
(dmamap->dm_segs[0].ds_len >= start + len));
vd[s].addr = dmamap->dm_segs[0].ds_addr + start;
vd[s].len = len;
if (!write)
vd[s].flags |= VRING_DESC_F_WRITE;
qe1->qe_next = vd[s].next;
return 0;
}
static void
publish_avail_idx(struct virtio_softc *sc, struct virtqueue *vq)
{
vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
virtio_membar_producer();
vq->vq_avail->idx = vq->vq_avail_idx;
vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE);
vq->vq_queued = 1;
}
/*
* enqueue_commit: add it to the aring.
*/
void
virtio_enqueue_commit(struct virtio_softc *sc, struct virtqueue *vq, int slot,
int notifynow)
{
struct vq_entry *qe1;
if (slot < 0)
goto notify;
vq_sync_descs(sc, vq, BUS_DMASYNC_PREWRITE);
qe1 = &vq->vq_entries[slot];
if (qe1->qe_indirect)
vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_PREWRITE);
vq->vq_avail->ring[(vq->vq_avail_idx++) & vq->vq_mask] = slot;
notify:
if (notifynow) {
if (virtio_has_feature(vq->vq_owner, VIRTIO_F_RING_EVENT_IDX)) {
uint16_t o = vq->vq_avail->idx;
uint16_t n = vq->vq_avail_idx;
uint16_t t;
publish_avail_idx(sc, vq);
virtio_membar_sync();
t = VQ_AVAIL_EVENT(vq) + 1;
if ((uint16_t)(n - t) < (uint16_t)(n - o))
sc->sc_ops->kick(sc, vq->vq_index);
} else {
publish_avail_idx(sc, vq);
virtio_membar_sync();
if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY))
sc->sc_ops->kick(sc, vq->vq_index);
}
}
}
/*
* enqueue_abort: rollback.
*/
int
virtio_enqueue_abort(struct virtqueue *vq, int slot)
{
struct vq_entry *qe = &vq->vq_entries[slot];
struct vring_desc *vd;
int s;
if (qe->qe_next < 0) {
vq_free_entry(vq, qe);
return 0;
}
s = slot;
vd = &vq->vq_desc[0];
while (vd[s].flags & VRING_DESC_F_NEXT) {
s = vd[s].next;
vq_free_entry(vq, qe);
qe = &vq->vq_entries[s];
}
vq_free_entry(vq, qe);
return 0;
}
/*
* enqueue_trim: adjust buffer size to given # of segments, a.k.a.
* descriptors.
*/
void
virtio_enqueue_trim(struct virtqueue *vq, int slot, int nsegs)
{
struct vq_entry *qe1 = &vq->vq_entries[slot];
struct vring_desc *vd = &vq->vq_desc[0];
int i;
if ((vd[slot].flags & VRING_DESC_F_INDIRECT) == 0) {
qe1->qe_next = qe1->qe_index;
/*
* N.B.: the vq_entries are ASSUMED to be a contiguous
* block with slot being the index to the first one.
*/
} else {
qe1->qe_next = 0;
vd = &vq->vq_desc[qe1->qe_index];
vd->len = sizeof(struct vring_desc) * nsegs;
vd = qe1->qe_desc_base;
slot = 0;
}
for (i = 0; i < nsegs -1 ; i++) {
vd[slot].flags = VRING_DESC_F_NEXT;
slot++;
}
vd[slot].flags = 0;
}
/*
* Dequeue a request.
*/
/*
* dequeue: dequeue a request from uring; dmamap_sync for uring is
* already done in the interrupt handler.
*/
int
virtio_dequeue(struct virtio_softc *sc, struct virtqueue *vq,
int *slotp, int *lenp)
{
uint16_t slot, usedidx;
struct vq_entry *qe;
if (vq->vq_used_idx == vq->vq_used->idx)
return ENOENT;
usedidx = vq->vq_used_idx++;
usedidx &= vq->vq_mask;
virtio_membar_consumer();
slot = vq->vq_used->ring[usedidx].id;
qe = &vq->vq_entries[slot];
if (qe->qe_indirect)
vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_POSTWRITE);
if (slotp)
*slotp = slot;
if (lenp)
*lenp = vq->vq_used->ring[usedidx].len;
return 0;
}
/*
* dequeue_commit: complete dequeue; the slot is recycled for future use.
* if you forget to call this the slot will be leaked.
*
* Don't call this if you use statically allocated slots
* and virtio_dequeue_trim().
*/
int
virtio_dequeue_commit(struct virtqueue *vq, int slot)
{
struct vq_entry *qe = &vq->vq_entries[slot];
struct vring_desc *vd = &vq->vq_desc[0];
int s = slot;
while (vd[s].flags & VRING_DESC_F_NEXT) {
s = vd[s].next;
vq_free_entry(vq, qe);
qe = &vq->vq_entries[s];
}
vq_free_entry(vq, qe);
return 0;
}
/*
* Increase the event index in order to delay interrupts.
* Returns 0 on success; returns 1 if the used ring has already advanced
* too far, and the caller must process the queue again (otherwise, no
* more interrupts will happen).
*/
int
virtio_postpone_intr(struct virtqueue *vq, uint16_t nslots)
{
uint16_t idx;
idx = vq->vq_used_idx + nslots;
/* set the new event index: avail_ring->used_event = idx */
VQ_USED_EVENT(vq) = idx;
virtio_membar_sync();
vq_sync_aring(vq->vq_owner, vq, BUS_DMASYNC_PREWRITE);
vq->vq_queued++;
if (nslots < virtio_nused(vq))
return 1;
return 0;
}
/*
* Postpone interrupt until 3/4 of the available descriptors have been
* consumed.
*/
int
virtio_postpone_intr_smart(struct virtqueue *vq)
{
uint16_t nslots;
nslots = (uint16_t)(vq->vq_avail->idx - vq->vq_used_idx) * 3 / 4;
return virtio_postpone_intr(vq, nslots);
}
/*
* Postpone interrupt until all of the available descriptors have been
* consumed.
*/
int
virtio_postpone_intr_far(struct virtqueue *vq)
{
uint16_t nslots;
nslots = (uint16_t)(vq->vq_avail->idx - vq->vq_used_idx);
return virtio_postpone_intr(vq, nslots);
}
/*
* Start/stop vq interrupt. No guarantee.
*/
void
virtio_stop_vq_intr(struct virtio_softc *sc, struct virtqueue *vq)
{
if (virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX)) {
/*
* No way to disable the interrupt completely with
* RingEventIdx. Instead advance used_event by half
* the possible value. This won't happen soon and
* is far enough in the past to not trigger a spurious
* interrupt.
*/
VQ_USED_EVENT(vq) = vq->vq_used_idx + 0x8000;
} else {
vq->vq_avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
}
vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
vq->vq_queued++;
}
int
virtio_start_vq_intr(struct virtio_softc *sc, struct virtqueue *vq)
{
/*
* If event index feature is negotiated, enabling
* interrupts is done through setting the latest
* consumed index in the used_event field
*/
if (virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX))
VQ_USED_EVENT(vq) = vq->vq_used_idx;
else
vq->vq_avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
virtio_membar_sync();
vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
vq->vq_queued++;
if (vq->vq_used_idx != vq->vq_used->idx)
return 1;
return 0;
}
/*
* Returns a number of slots in the used ring available to
* be supplied to the avail ring.
*/
int
virtio_nused(struct virtqueue *vq)
{
uint16_t n;
n = (uint16_t)(vq->vq_used->idx - vq->vq_used_idx);
VIRTIO_ASSERT(n <= vq->vq_num);
return n;
}
#if VIRTIO_DEBUG
void
virtio_vq_dump(struct virtqueue *vq)
{
/* Common fields */
printf(" + vq num: %d\n", vq->vq_num);
printf(" + vq mask: 0x%X\n", vq->vq_mask);
printf(" + vq index: %d\n", vq->vq_index);
printf(" + vq used idx: %d\n", vq->vq_used_idx);
printf(" + vq avail idx: %d\n", vq->vq_avail_idx);
printf(" + vq queued: %d\n",vq->vq_queued);
/* Avail ring fields */
printf(" + avail flags: 0x%X\n", vq->vq_avail->flags);
printf(" + avail idx: %d\n", vq->vq_avail->idx);
printf(" + avail event: %d\n", VQ_AVAIL_EVENT(vq));
/* Used ring fields */
printf(" + used flags: 0x%X\n",vq->vq_used->flags);
printf(" + used idx: %d\n",vq->vq_used->idx);
printf(" + used event: %d\n", VQ_USED_EVENT(vq));
printf(" +++++++++++++++++++++++++++\n");
}
#endif
16
106
486
24
2
3
3
2
3
443
108
107
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/* $OpenBSD: sys_socket.c,v 1.54 2022/09/02 13:12:31 mvs Exp $ */
/* $NetBSD: sys_socket.c,v 1.13 1995/08/12 23:59:09 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)sys_socket.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <net/if.h>
const struct fileops socketops = {
.fo_read = soo_read,
.fo_write = soo_write,
.fo_ioctl = soo_ioctl,
.fo_kqfilter = soo_kqfilter,
.fo_stat = soo_stat,
.fo_close = soo_close
};
int
soo_read(struct file *fp, struct uio *uio, int fflags)
{
struct socket *so = (struct socket *)fp->f_data;
int flags = 0;
if (fp->f_flag & FNONBLOCK)
flags |= MSG_DONTWAIT;
return (soreceive(so, NULL, uio, NULL, NULL, &flags, 0));
}
int
soo_write(struct file *fp, struct uio *uio, int fflags)
{
struct socket *so = (struct socket *)fp->f_data;
int flags = 0;
if (fp->f_flag & FNONBLOCK)
flags |= MSG_DONTWAIT;
return (sosend(so, NULL, uio, NULL, NULL, flags));
}
int
soo_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p)
{
struct socket *so = (struct socket *)fp->f_data;
int error = 0;
switch (cmd) {
case FIONBIO:
break;
case FIOASYNC:
solock(so);
if (*(int *)data) {
so->so_rcv.sb_flags |= SB_ASYNC;
so->so_snd.sb_flags |= SB_ASYNC;
} else {
so->so_rcv.sb_flags &= ~SB_ASYNC;
so->so_snd.sb_flags &= ~SB_ASYNC;
}
sounlock(so);
break;
case FIONREAD:
*(int *)data = so->so_rcv.sb_datacc;
break;
case FIOSETOWN:
case SIOCSPGRP:
case TIOCSPGRP:
error = sigio_setown(&so->so_sigio, cmd, data);
break;
case FIOGETOWN:
case SIOCGPGRP:
case TIOCGPGRP:
sigio_getown(&so->so_sigio, cmd, data);
break;
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
break;
default:
/*
* Interface/routing/protocol specific ioctls:
* interface and routing ioctls should have a
* different entry since a socket's unnecessary
*/
if (IOCGROUP(cmd) == 'i') {
KERNEL_LOCK();
error = ifioctl(so, cmd, data, p);
KERNEL_UNLOCK();
return (error);
}
if (IOCGROUP(cmd) == 'r')
return (EOPNOTSUPP);
KERNEL_LOCK();
error = pru_control(so, cmd, data, NULL);
KERNEL_UNLOCK();
break;
}
return (error);
}
int
soo_stat(struct file *fp, struct stat *ub, struct proc *p)
{
struct socket *so = fp->f_data;
memset(ub, 0, sizeof (*ub));
ub->st_mode = S_IFSOCK;
solock(so);
if ((so->so_state & SS_CANTRCVMORE) == 0 || so->so_rcv.sb_cc != 0)
ub->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
if ((so->so_state & SS_CANTSENDMORE) == 0)
ub->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
ub->st_uid = so->so_euid;
ub->st_gid = so->so_egid;
(void)pru_sense(so, ub);
sounlock(so);
return (0);
}
int
soo_close(struct file *fp, struct proc *p)
{
int flags, error = 0;
if (fp->f_data) {
flags = (fp->f_flag & FNONBLOCK) ? MSG_DONTWAIT : 0;
error = soclose(fp->f_data, flags);
}
fp->f_data = NULL;
return (error);
}
7
7
7
7
7
1
3
10
9
7
7
1
7
7
7
7
8
7
7
7
7
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
/* $OpenBSD: wskbdutil.c,v 1.19 2021/12/30 06:55:11 anton Exp $ */
/* $NetBSD: wskbdutil.c,v 1.7 1999/12/21 11:59:13 drochner Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Juergen Hannken-Illjes.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
static struct compose_tab_s {
keysym_t elem[2];
keysym_t result;
} compose_tab[] = {
{ { KS_plus, KS_plus }, KS_numbersign },
{ { KS_a, KS_a }, KS_at },
{ { KS_parenleft, KS_parenleft }, KS_bracketleft },
{ { KS_slash, KS_slash }, KS_backslash },
{ { KS_parenright, KS_parenright }, KS_bracketright },
{ { KS_parenleft, KS_minus }, KS_braceleft },
{ { KS_slash, KS_minus }, KS_bar },
{ { KS_parenright, KS_minus }, KS_braceright },
{ { KS_exclam, KS_exclam }, KS_exclamdown },
{ { KS_c, KS_slash }, KS_cent },
{ { KS_l, KS_minus }, KS_sterling },
{ { KS_y, KS_minus }, KS_yen },
{ { KS_s, KS_o }, KS_section },
{ { KS_x, KS_o }, KS_currency },
{ { KS_c, KS_o }, KS_copyright },
{ { KS_less, KS_less }, KS_guillemotleft },
{ { KS_greater, KS_greater }, KS_guillemotright },
{ { KS_question, KS_question }, KS_questiondown },
{ { KS_dead_acute, KS_space }, KS_apostrophe },
{ { KS_dead_grave, KS_space }, KS_grave },
{ { KS_dead_tilde, KS_space }, KS_asciitilde },
{ { KS_dead_circumflex, KS_space }, KS_asciicircum },
{ { KS_dead_diaeresis, KS_space }, KS_quotedbl },
{ { KS_dead_cedilla, KS_space }, KS_comma },
{ { KS_dead_circumflex, KS_A }, KS_Acircumflex },
{ { KS_dead_diaeresis, KS_A }, KS_Adiaeresis },
{ { KS_dead_grave, KS_A }, KS_Agrave },
{ { KS_dead_abovering, KS_A }, KS_Aring },
{ { KS_dead_tilde, KS_A }, KS_Atilde },
{ { KS_dead_cedilla, KS_C }, KS_Ccedilla },
{ { KS_dead_acute, KS_E }, KS_Eacute },
{ { KS_dead_circumflex, KS_E }, KS_Ecircumflex },
{ { KS_dead_diaeresis, KS_E }, KS_Ediaeresis },
{ { KS_dead_grave, KS_E }, KS_Egrave },
{ { KS_dead_acute, KS_I }, KS_Iacute },
{ { KS_dead_circumflex, KS_I }, KS_Icircumflex },
{ { KS_dead_diaeresis, KS_I }, KS_Idiaeresis },
{ { KS_dead_grave, KS_I }, KS_Igrave },
{ { KS_dead_tilde, KS_N }, KS_Ntilde },
{ { KS_dead_acute, KS_O }, KS_Oacute },
{ { KS_dead_circumflex, KS_O }, KS_Ocircumflex },
{ { KS_dead_diaeresis, KS_O }, KS_Odiaeresis },
{ { KS_dead_grave, KS_O }, KS_Ograve },
{ { KS_dead_tilde, KS_O }, KS_Otilde },
{ { KS_dead_acute, KS_U }, KS_Uacute },
{ { KS_dead_circumflex, KS_U }, KS_Ucircumflex },
{ { KS_dead_diaeresis, KS_U }, KS_Udiaeresis },
{ { KS_dead_grave, KS_U }, KS_Ugrave },
{ { KS_dead_acute, KS_Y }, KS_Yacute },
{ { KS_dead_acute, KS_a }, KS_aacute },
{ { KS_dead_circumflex, KS_a }, KS_acircumflex },
{ { KS_dead_diaeresis, KS_a }, KS_adiaeresis },
{ { KS_dead_grave, KS_a }, KS_agrave },
{ { KS_dead_abovering, KS_a }, KS_aring },
{ { KS_dead_tilde, KS_a }, KS_atilde },
{ { KS_dead_cedilla, KS_c }, KS_ccedilla },
{ { KS_dead_acute, KS_e }, KS_eacute },
{ { KS_dead_circumflex, KS_e }, KS_ecircumflex },
{ { KS_dead_diaeresis, KS_e }, KS_ediaeresis },
{ { KS_dead_grave, KS_e }, KS_egrave },
{ { KS_dead_acute, KS_i }, KS_iacute },
{ { KS_dead_circumflex, KS_i }, KS_icircumflex },
{ { KS_dead_diaeresis, KS_i }, KS_idiaeresis },
{ { KS_dead_grave, KS_i }, KS_igrave },
{ { KS_dead_tilde, KS_n }, KS_ntilde },
{ { KS_dead_acute, KS_o }, KS_oacute },
{ { KS_dead_circumflex, KS_o }, KS_ocircumflex },
{ { KS_dead_diaeresis, KS_o }, KS_odiaeresis },
{ { KS_dead_grave, KS_o }, KS_ograve },
{ { KS_dead_tilde, KS_o }, KS_otilde },
{ { KS_dead_acute, KS_u }, KS_uacute },
{ { KS_dead_circumflex, KS_u }, KS_ucircumflex },
{ { KS_dead_diaeresis, KS_u }, KS_udiaeresis },
{ { KS_dead_grave, KS_u }, KS_ugrave },
{ { KS_dead_acute, KS_y }, KS_yacute },
{ { KS_dead_diaeresis, KS_y }, KS_ydiaeresis },
{ { KS_quotedbl, KS_A }, KS_Adiaeresis },
{ { KS_quotedbl, KS_E }, KS_Ediaeresis },
{ { KS_quotedbl, KS_I }, KS_Idiaeresis },
{ { KS_quotedbl, KS_O }, KS_Odiaeresis },
{ { KS_quotedbl, KS_U }, KS_Udiaeresis },
{ { KS_quotedbl, KS_a }, KS_adiaeresis },
{ { KS_quotedbl, KS_e }, KS_ediaeresis },
{ { KS_quotedbl, KS_i }, KS_idiaeresis },
{ { KS_quotedbl, KS_o }, KS_odiaeresis },
{ { KS_quotedbl, KS_u }, KS_udiaeresis },
{ { KS_quotedbl, KS_y }, KS_ydiaeresis },
{ { KS_acute, KS_A }, KS_Aacute },
{ { KS_asciicircum, KS_A }, KS_Acircumflex },
{ { KS_grave, KS_A }, KS_Agrave },
{ { KS_asterisk, KS_A }, KS_Aring },
{ { KS_asciitilde, KS_A }, KS_Atilde },
{ { KS_cedilla, KS_C }, KS_Ccedilla },
{ { KS_acute, KS_E }, KS_Eacute },
{ { KS_asciicircum, KS_E }, KS_Ecircumflex },
{ { KS_grave, KS_E }, KS_Egrave },
{ { KS_acute, KS_I }, KS_Iacute },
{ { KS_asciicircum, KS_I }, KS_Icircumflex },
{ { KS_grave, KS_I }, KS_Igrave },
{ { KS_asciitilde, KS_N }, KS_Ntilde },
{ { KS_acute, KS_O }, KS_Oacute },
{ { KS_asciicircum, KS_O }, KS_Ocircumflex },
{ { KS_grave, KS_O }, KS_Ograve },
{ { KS_asciitilde, KS_O }, KS_Otilde },
{ { KS_acute, KS_U }, KS_Uacute },
{ { KS_asciicircum, KS_U }, KS_Ucircumflex },
{ { KS_grave, KS_U }, KS_Ugrave },
{ { KS_acute, KS_Y }, KS_Yacute },
{ { KS_acute, KS_a }, KS_aacute },
{ { KS_asciicircum, KS_a }, KS_acircumflex },
{ { KS_grave, KS_a }, KS_agrave },
{ { KS_asterisk, KS_a }, KS_aring },
{ { KS_asciitilde, KS_a }, KS_atilde },
{ { KS_cedilla, KS_c }, KS_ccedilla },
{ { KS_acute, KS_e }, KS_eacute },
{ { KS_asciicircum, KS_e }, KS_ecircumflex },
{ { KS_grave, KS_e }, KS_egrave },
{ { KS_acute, KS_i }, KS_iacute },
{ { KS_asciicircum, KS_i }, KS_icircumflex },
{ { KS_grave, KS_i }, KS_igrave },
{ { KS_asciitilde, KS_n }, KS_ntilde },
{ { KS_acute, KS_o }, KS_oacute },
{ { KS_asciicircum, KS_o }, KS_ocircumflex },
{ { KS_grave, KS_o }, KS_ograve },
{ { KS_asciitilde, KS_o }, KS_otilde },
{ { KS_acute, KS_u }, KS_uacute },
{ { KS_asciicircum, KS_u }, KS_ucircumflex },
{ { KS_grave, KS_u }, KS_ugrave },
{ { KS_acute, KS_y }, KS_yacute },
{ { KS_dead_caron, KS_space }, KS_L2_caron },
{ { KS_dead_caron, KS_S }, KS_L2_Scaron },
{ { KS_dead_caron, KS_Z }, KS_L2_Zcaron },
{ { KS_dead_caron, KS_s }, KS_L2_scaron },
{ { KS_dead_caron, KS_z }, KS_L2_zcaron }
};
#define COMPOSE_SIZE nitems(compose_tab)
static int compose_tab_inorder = 0;
keysym_t ksym_upcase(keysym_t);
void fillmapentry(const keysym_t *, int, struct wscons_keymap *);
static inline int
compose_tab_cmp(struct compose_tab_s *i, struct compose_tab_s *j)
{
if (i->elem[0] == j->elem[0])
return(i->elem[1] - j->elem[1]);
else
return(i->elem[0] - j->elem[0]);
}
keysym_t
wskbd_compose_value(keysym_t *compose_buf)
{
int i, j, r;
struct compose_tab_s v;
if (!compose_tab_inorder) {
/* Insertion sort. */
for (i = 1; i < COMPOSE_SIZE; i++) {
v = compose_tab[i];
/* find correct slot, moving others up */
for (j = i; --j >= 0 &&
compose_tab_cmp(&v, &compose_tab[j]) < 0;)
compose_tab[j + 1] = compose_tab[j];
compose_tab[j + 1] = v;
}
compose_tab_inorder = 1;
}
for (j = 0, i = COMPOSE_SIZE; i != 0; i /= 2) {
if (compose_tab[j + i/2].elem[0] == compose_buf[0]) {
if (compose_tab[j + i/2].elem[1] == compose_buf[1])
return(compose_tab[j + i/2].result);
r = compose_tab[j + i/2].elem[1] < compose_buf[1];
} else
r = compose_tab[j + i/2].elem[0] < compose_buf[0];
if (r) {
j += i/2 + 1;
i--;
}
}
return(KS_voidSymbol);
}
static const u_char latin1_to_upper[256] = {
/* 0 8 1 9 2 a 3 b 4 c 5 d 6 e 7 f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 3 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 3 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 4 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 4 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5 */
0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 6 */
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 6 */
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 7 */
'X', 'Y', 'Z', 0x00, 0x00, 0x00, 0x00, 0x00, /* 7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d */
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* e */
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* e */
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0x00, /* f */
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* f */
};
keysym_t
ksym_upcase(keysym_t ksym)
{
if (ksym >= KS_f1 && ksym <= KS_f20)
return(KS_F1 - KS_f1 + ksym);
if (KS_GROUP(ksym) == KS_GROUP_Ascii && ksym <= 0xff &&
latin1_to_upper[ksym] != 0x00)
return(latin1_to_upper[ksym]);
return(ksym);
}
void
fillmapentry(const keysym_t *kp, int len, struct wscons_keymap *mapentry)
{
switch (len) {
case 0:
mapentry->group1[0] = KS_voidSymbol;
mapentry->group1[1] = KS_voidSymbol;
mapentry->group2[0] = KS_voidSymbol;
mapentry->group2[1] = KS_voidSymbol;
break;
case 1:
mapentry->group1[0] = kp[0];
mapentry->group1[1] = ksym_upcase(kp[0]);
mapentry->group2[0] = mapentry->group1[0];
mapentry->group2[1] = mapentry->group1[1];
break;
case 2:
mapentry->group1[0] = kp[0];
mapentry->group1[1] = kp[1];
mapentry->group2[0] = mapentry->group1[0];
mapentry->group2[1] = mapentry->group1[1];
break;
case 3:
mapentry->group1[0] = kp[0];
mapentry->group1[1] = kp[1];
mapentry->group2[0] = kp[2];
mapentry->group2[1] = ksym_upcase(kp[2]);
break;
case 4:
mapentry->group1[0] = kp[0];
mapentry->group1[1] = kp[1];
mapentry->group2[0] = kp[2];
mapentry->group2[1] = kp[3];
break;
}
}
void
wskbd_get_mapentry(const struct wskbd_mapdata *mapdata, int kc,
struct wscons_keymap *mapentry)
{
kbd_t cur;
const keysym_t *kp;
const struct wscons_keydesc *mp;
int l;
keysym_t ksg;
mapentry->command = KS_voidSymbol;
mapentry->group1[0] = KS_voidSymbol;
mapentry->group1[1] = KS_voidSymbol;
mapentry->group2[0] = KS_voidSymbol;
mapentry->group2[1] = KS_voidSymbol;
for (cur = mapdata->layout & ~KB_HANDLEDBYWSKBD; cur != 0; ) {
mp = mapdata->keydesc;
while (mp->map_size > 0) {
if (mp->name == cur)
break;
mp++;
}
/* If map not found, return */
if (mp->map_size <= 0)
return;
for (kp = mp->map; kp < mp->map + mp->map_size; kp++) {
ksg = KS_GROUP(*kp);
if (ksg == KS_GROUP_Keycode &&
KS_VALUE(*kp) == kc) {
/* First skip keycode and possible command */
kp++;
if (KS_GROUP(*kp) == KS_GROUP_Command ||
*kp == KS_Cmd || *kp == KS_Cmd1 || *kp == KS_Cmd2)
mapentry->command = *kp++;
for (l = 0; kp + l < mp->map + mp->map_size;
l++) {
ksg = KS_GROUP(kp[l]);
if (ksg == KS_GROUP_Keycode)
break;
}
if (l > 4)
panic("wskbd_get_mapentry: %d(%d): bad entry",
mp->name, *kp);
fillmapentry(kp, l, mapentry);
return;
}
}
cur = mp->base;
}
}
struct wscons_keymap *
wskbd_init_keymap(int maplen)
{
struct wscons_keymap *map;
int i;
map = mallocarray(maplen, sizeof(*map), M_DEVBUF, M_WAITOK);
for (i = 0; i < maplen; i++) {
map[i].command = KS_voidSymbol;
map[i].group1[0] = KS_voidSymbol;
map[i].group1[1] = KS_voidSymbol;
map[i].group2[0] = KS_voidSymbol;
map[i].group2[1] = KS_voidSymbol;
}
return map;
}
int
wskbd_load_keymap(const struct wskbd_mapdata *mapdata, kbd_t layout,
struct wscons_keymap **map, int *maplen)
{
int i, s, kc, stack_ptr;
const keysym_t *kp;
const struct wscons_keydesc *mp, *stack[10];
kbd_t cur;
keysym_t ksg;
for (cur = layout & ~KB_HANDLEDBYWSKBD, stack_ptr = 0;
cur != 0; stack_ptr++) {
mp = mapdata->keydesc;
while (mp->map_size > 0) {
if (cur == 0 || mp->name == cur) {
break;
}
mp++;
}
if (stack_ptr == nitems(stack))
panic("wskbd_load_keymap: %d: recursion too deep",
mapdata->layout);
if (mp->map_size <= 0)
return(EINVAL);
stack[stack_ptr] = mp;
cur = mp->base;
}
for (i = 0, s = stack_ptr - 1; s >= 0; s--) {
mp = stack[s];
for (kp = mp->map; kp < mp->map + mp->map_size; kp++) {
ksg = KS_GROUP(*kp);
if (ksg == KS_GROUP_Keycode && KS_VALUE(*kp) > i)
i = KS_VALUE(*kp);
}
}
*map = wskbd_init_keymap(i + 1);
*maplen = i + 1;
for (s = stack_ptr - 1; s >= 0; s--) {
mp = stack[s];
for (kp = mp->map; kp < mp->map + mp->map_size; ) {
ksg = KS_GROUP(*kp);
if (ksg != KS_GROUP_Keycode)
panic("wskbd_load_keymap: %d(%d): bad entry",
mp->name, *kp);
kc = KS_VALUE(*kp);
kp++;
if (KS_GROUP(*kp) == KS_GROUP_Command ||
*kp == KS_Cmd || *kp == KS_Cmd1 || *kp == KS_Cmd2) {
(*map)[kc].command = *kp;
kp++;
}
for (i = 0; kp + i < mp->map + mp->map_size; i++) {
ksg = KS_GROUP(kp[i]);
if (ksg == KS_GROUP_Keycode)
break;
}
if (i > 4)
panic("wskbd_load_keymap: %d(%d): bad entry",
mp->name, *kp);
fillmapentry(kp, i, &(*map)[kc]);
kp += i;
}
}
return(0);
}
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
/* $OpenBSD: wd.c,v 1.129 2022/04/06 18:59:27 naddy Exp $ */
/* $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum and by Onno van der Linden.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#if 0
#include "rnd.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mutex.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/syslog.h>
#include <sys/timeout.h>
#include <sys/vnode.h>
#include <sys/dkio.h>
#include <sys/reboot.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <dev/ata/atareg.h>
#include <dev/ata/atavar.h>
#include <dev/ata/wdvar.h>
#include <dev/ic/wdcreg.h>
#include <dev/ic/wdcvar.h>
#if 0
#include "locators.h"
#endif
#define LBA48_THRESHOLD (0xfffffff) /* 128GB / DEV_BSIZE */
#define WDIORETRIES_SINGLE 4 /* number of retries before single-sector */
#define WDIORETRIES 5 /* number of retries before giving up */
#define RECOVERYTIME_MSEC 500 /* time to wait before retrying a cmd */
#define DEBUG_INTR 0x01
#define DEBUG_XFERS 0x02
#define DEBUG_STATUS 0x04
#define DEBUG_FUNCS 0x08
#define DEBUG_PROBE 0x10
#ifdef WDCDEBUG
extern int wdcdebug_wd_mask; /* init'ed in ata_wdc.c */
#define WDCDEBUG_PRINT(args, level) do { \
if ((wdcdebug_wd_mask & (level)) != 0) \
printf args; \
} while (0)
#else
#define WDCDEBUG_PRINT(args, level)
#endif
#define sc_drive sc_wdc_bio.drive
#define sc_mode sc_wdc_bio.mode
#define sc_multi sc_wdc_bio.multi
int wdprobe(struct device *, void *, void *);
void wdattach(struct device *, struct device *, void *);
int wddetach(struct device *, int);
int wdactivate(struct device *, int);
int wdprint(void *, char *);
const struct cfattach wd_ca = {
sizeof(struct wd_softc), wdprobe, wdattach,
wddetach, wdactivate
};
struct cfdriver wd_cd = {
NULL, "wd", DV_DISK
};
void wdgetdefaultlabel(struct wd_softc *, struct disklabel *);
int wdgetdisklabel(dev_t dev, struct wd_softc *, struct disklabel *, int);
void wdstrategy(struct buf *);
void wdstart(void *);
void __wdstart(struct wd_softc*, struct buf *);
void wdrestart(void *);
int wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *);
int wd_flushcache(struct wd_softc *, int);
void wd_standby(struct wd_softc *, int);
/* XXX: these should go elsewhere */
cdev_decl(wd);
bdev_decl(wd);
#define wdlookup(unit) (struct wd_softc *)disk_lookup(&wd_cd, (unit))
int
wdprobe(struct device *parent, void *match_, void *aux)
{
struct ata_atapi_attach *aa_link = aux;
struct cfdata *match = match_;
if (aa_link == NULL)
return 0;
if (aa_link->aa_type != T_ATA)
return 0;
if (match->cf_loc[0] != -1 &&
match->cf_loc[0] != aa_link->aa_channel)
return 0;
if (match->cf_loc[1] != -1 &&
match->cf_loc[1] != aa_link->aa_drv_data->drive)
return 0;
return 1;
}
void
wdattach(struct device *parent, struct device *self, void *aux)
{
struct wd_softc *wd = (void *)self;
struct ata_atapi_attach *aa_link= aux;
struct wdc_command wdc_c;
int i, blank;
char buf[41], c, *p, *q;
WDCDEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE);
wd->openings = aa_link->aa_openings;
wd->drvp = aa_link->aa_drv_data;
strlcpy(wd->drvp->drive_name, wd->sc_dev.dv_xname,
sizeof(wd->drvp->drive_name));
wd->drvp->cf_flags = wd->sc_dev.dv_cfdata->cf_flags;
if ((NERRS_MAX - 2) > 0)
wd->drvp->n_dmaerrs = NERRS_MAX - 2;
else
wd->drvp->n_dmaerrs = 0;
/* read our drive info */
if (wd_get_params(wd, at_poll, &wd->sc_params) != 0) {
printf("%s: IDENTIFY failed\n", wd->sc_dev.dv_xname);
return;
}
for (blank = 0, p = wd->sc_params.atap_model, q = buf, i = 0;
i < sizeof(wd->sc_params.atap_model); i++) {
c = *p++;
if (c == '\0')
break;
if (c != ' ') {
if (blank) {
*q++ = ' ';
blank = 0;
}
*q++ = c;
} else
blank = 1;
}
*q++ = '\0';
printf(": <%s>\n", buf);
wdc_probe_caps(wd->drvp, &wd->sc_params);
wdc_print_caps(wd->drvp);
if ((wd->sc_params.atap_multi & 0xff) > 1) {
wd->sc_multi = wd->sc_params.atap_multi & 0xff;
} else {
wd->sc_multi = 1;
}
printf("%s: %d-sector PIO,", wd->sc_dev.dv_xname, wd->sc_multi);
/* use 48-bit LBA if enabled */
if ((wd->sc_params.atap_cmd2_en & ATAPI_CMD2_48AD) != 0)
wd->sc_flags |= WDF_LBA48;
/* Prior to ATA-4, LBA was optional. */
if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0)
wd->sc_flags |= WDF_LBA;
#if 0
/* ATA-4 requires LBA. */
if (wd->sc_params.atap_ataversion != 0xffff &&
wd->sc_params.atap_ataversion >= WDC_VER_ATA4)
wd->sc_flags |= WDF_LBA;
#endif
if ((wd->sc_flags & WDF_LBA48) != 0) {
wd->sc_capacity =
(((u_int64_t)wd->sc_params.atap_max_lba[3] << 48) |
((u_int64_t)wd->sc_params.atap_max_lba[2] << 32) |
((u_int64_t)wd->sc_params.atap_max_lba[1] << 16) |
(u_int64_t)wd->sc_params.atap_max_lba[0]);
printf(" LBA48, %lluMB, %llu sectors\n",
wd->sc_capacity / (1048576 / DEV_BSIZE),
wd->sc_capacity);
} else if ((wd->sc_flags & WDF_LBA) != 0) {
wd->sc_capacity =
(wd->sc_params.atap_capacity[1] << 16) |
wd->sc_params.atap_capacity[0];
printf(" LBA, %lluMB, %llu sectors\n",
wd->sc_capacity / (1048576 / DEV_BSIZE),
wd->sc_capacity);
} else {
wd->sc_capacity =
wd->sc_params.atap_cylinders *
wd->sc_params.atap_heads *
wd->sc_params.atap_sectors;
printf(" CHS, %lluMB, %d cyl, %d head, %d sec, %llu sectors\n",
wd->sc_capacity / (1048576 / DEV_BSIZE),
wd->sc_params.atap_cylinders,
wd->sc_params.atap_heads,
wd->sc_params.atap_sectors,
wd->sc_capacity);
}
WDCDEBUG_PRINT(("%s: atap_dmatiming_mimi=%d, atap_dmatiming_recom=%d\n",
self->dv_xname, wd->sc_params.atap_dmatiming_mimi,
wd->sc_params.atap_dmatiming_recom), DEBUG_PROBE);
/* use read look ahead if supported */
if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_AHEAD) {
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = SET_FEATURES;
wdc_c.r_features = WDSF_READAHEAD_EN;
wdc_c.timeout = 1000;
wdc_c.flags = at_poll;
if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
printf("%s: enable look ahead command didn't "
"complete\n", wd->sc_dev.dv_xname);
}
}
/* use write cache if supported */
if (wd->sc_params.atap_cmd_set1 & WDC_CMD1_CACHE) {
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = SET_FEATURES;
wdc_c.r_features = WDSF_EN_WR_CACHE;
wdc_c.timeout = 1000;
wdc_c.flags = at_poll;
if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
printf("%s: enable write cache command didn't "
"complete\n", wd->sc_dev.dv_xname);
}
}
/*
* FREEZE LOCK the drive so malicious users can't lock it on us.
* As there is no harm in issuing this to drives that don't
* support the security feature set we just send it, and don't
* bother checking if the drive sends a command abort to tell us it
* doesn't support it.
*/
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = WDCC_SEC_FREEZE_LOCK;
wdc_c.timeout = 1000;
wdc_c.flags = at_poll;
if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
printf("%s: freeze lock command didn't complete\n",
wd->sc_dev.dv_xname);
}
/*
* Initialize disk structures.
*/
wd->sc_dk.dk_name = wd->sc_dev.dv_xname;
bufq_init(&wd->sc_bufq, BUFQ_DEFAULT);
timeout_set(&wd->sc_restart_timeout, wdrestart, wd);
/* Attach disk. */
disk_attach(&wd->sc_dev, &wd->sc_dk);
wd->sc_wdc_bio.lp = wd->sc_dk.dk_label;
}
int
wdactivate(struct device *self, int act)
{
struct wd_softc *wd = (void *)self;
int rv = 0;
switch (act) {
case DVACT_SUSPEND:
break;
case DVACT_POWERDOWN:
wd_flushcache(wd, AT_POLL);
if (boothowto & RB_POWERDOWN)
wd_standby(wd, AT_POLL);
break;
case DVACT_RESUME:
/*
* Do two resets separated by a small delay. The
* first wakes the controller, the second resets
* the channel.
*/
wdc_disable_intr(wd->drvp->chnl_softc);
wdc_reset_channel(wd->drvp, 1);
delay(10000);
wdc_reset_channel(wd->drvp, 0);
wdc_enable_intr(wd->drvp->chnl_softc);
wd_get_params(wd, at_poll, &wd->sc_params);
break;
}
return (rv);
}
int
wddetach(struct device *self, int flags)
{
struct wd_softc *sc = (struct wd_softc *)self;
timeout_del(&sc->sc_restart_timeout);
bufq_drain(&sc->sc_bufq);
disk_gone(wdopen, self->dv_unit);
/* Detach disk. */
bufq_destroy(&sc->sc_bufq);
disk_detach(&sc->sc_dk);
return (0);
}
/*
* Read/write routine for a buffer. Validates the arguments and schedules the
* transfer. Does not wait for the transfer to complete.
*/
void
wdstrategy(struct buf *bp)
{
struct wd_softc *wd;
int s;
wd = wdlookup(DISKUNIT(bp->b_dev));
if (wd == NULL) {
bp->b_error = ENXIO;
goto bad;
}
WDCDEBUG_PRINT(("wdstrategy (%s)\n", wd->sc_dev.dv_xname),
DEBUG_XFERS);
/* If device invalidated (e.g. media change, door open), error. */
if ((wd->sc_flags & WDF_LOADED) == 0) {
bp->b_error = EIO;
goto bad;
}
/* Validate the request. */
if (bounds_check_with_label(bp, wd->sc_dk.dk_label) == -1)
goto done;
/* Check that the number of sectors can fit in a byte. */
if ((bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) {
bp->b_error = EINVAL;
goto bad;
}
/* Queue transfer on drive, activate drive and controller if idle. */
bufq_queue(&wd->sc_bufq, bp);
s = splbio();
wdstart(wd);
splx(s);
device_unref(&wd->sc_dev);
return;
bad:
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
done:
s = splbio();
biodone(bp);
splx(s);
if (wd != NULL)
device_unref(&wd->sc_dev);
}
/*
* Queue a drive for I/O.
*/
void
wdstart(void *arg)
{
struct wd_softc *wd = arg;
struct buf *bp = NULL;
WDCDEBUG_PRINT(("wdstart %s\n", wd->sc_dev.dv_xname),
DEBUG_XFERS);
while (wd->openings > 0) {
/* Is there a buf for us ? */
if ((bp = bufq_dequeue(&wd->sc_bufq)) == NULL)
return;
/*
* Make the command. First lock the device
*/
wd->openings--;
wd->retries = 0;
__wdstart(wd, bp);
}
}
void
__wdstart(struct wd_softc *wd, struct buf *bp)
{
struct disklabel *lp;
u_int64_t nsecs;
lp = wd->sc_dk.dk_label;
wd->sc_wdc_bio.blkno = DL_BLKTOSEC(lp, bp->b_blkno + DL_SECTOBLK(lp,
DL_GETPOFFSET(&lp->d_partitions[DISKPART(bp->b_dev)])));
wd->sc_wdc_bio.blkdone =0;
wd->sc_bp = bp;
/*
* If we're retrying, retry in single-sector mode. This will give us
* the sector number of the problem, and will eventually allow the
* transfer to succeed.
*/
if (wd->retries >= WDIORETRIES_SINGLE)
wd->sc_wdc_bio.flags = ATA_SINGLE;
else
wd->sc_wdc_bio.flags = 0;
nsecs = howmany(bp->b_bcount, lp->d_secsize);
if ((wd->sc_flags & WDF_LBA48) &&
/* use LBA48 only if really need */
((wd->sc_wdc_bio.blkno + nsecs - 1 >= LBA48_THRESHOLD) ||
(nsecs > 0xff)))
wd->sc_wdc_bio.flags |= ATA_LBA48;
if (wd->sc_flags & WDF_LBA)
wd->sc_wdc_bio.flags |= ATA_LBA;
if (bp->b_flags & B_READ)
wd->sc_wdc_bio.flags |= ATA_READ;
wd->sc_wdc_bio.bcount = bp->b_bcount;
wd->sc_wdc_bio.databuf = bp->b_data;
wd->sc_wdc_bio.wd = wd;
/* Instrumentation. */
disk_busy(&wd->sc_dk);
switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) {
case WDC_TRY_AGAIN:
timeout_add_sec(&wd->sc_restart_timeout, 1);
break;
case WDC_QUEUED:
break;
case WDC_COMPLETE:
/*
* This code is never executed because we never set
* the ATA_POLL flag above
*/
#if 0
if (wd->sc_wdc_bio.flags & ATA_POLL)
wddone(wd);
#endif
break;
default:
panic("__wdstart: bad return code from wdc_ata_bio()");
}
}
void
wddone(void *v)
{
struct wd_softc *wd = v;
struct buf *bp = wd->sc_bp;
char buf[256], *errbuf = buf;
WDCDEBUG_PRINT(("wddone %s\n", wd->sc_dev.dv_xname),
DEBUG_XFERS);
bp->b_resid = wd->sc_wdc_bio.bcount;
errbuf[0] = '\0';
switch (wd->sc_wdc_bio.error) {
case ERR_NODEV:
bp->b_flags |= B_ERROR;
bp->b_error = ENXIO;
break;
case ERR_DMA:
errbuf = "DMA error";
goto retry;
case ERR_DF:
errbuf = "device fault";
goto retry;
case TIMEOUT:
errbuf = "device timeout";
goto retry;
case ERROR:
/* Don't care about media change bits */
if (wd->sc_wdc_bio.r_error != 0 &&
(wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0)
goto noerror;
ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf,
sizeof buf);
retry:
/* Just reset and retry. Can we do more ? */
wdc_reset_channel(wd->drvp, 0);
diskerr(bp, "wd", errbuf, LOG_PRINTF,
wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label);
if (wd->retries++ < WDIORETRIES) {
printf(", retrying\n");
timeout_add_msec(&wd->sc_restart_timeout,
RECOVERYTIME_MSEC);
return;
}
printf("\n");
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
break;
case NOERROR:
noerror: if ((wd->sc_wdc_bio.flags & ATA_CORR) || wd->retries > 0)
printf("%s: soft error (corrected)\n",
wd->sc_dev.dv_xname);
}
disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid),
bp->b_blkno, (bp->b_flags & B_READ));
biodone(bp);
wd->openings++;
wdstart(wd);
}
void
wdrestart(void *v)
{
struct wd_softc *wd = v;
struct buf *bp = wd->sc_bp;
struct channel_softc *chnl;
int s;
WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname),
DEBUG_XFERS);
chnl = (struct channel_softc *)(wd->drvp->chnl_softc);
if (chnl->dying)
return;
s = splbio();
disk_unbusy(&wd->sc_dk, 0, 0, (bp->b_flags & B_READ));
__wdstart(v, bp);
splx(s);
}
int
wdread(dev_t dev, struct uio *uio, int flags)
{
WDCDEBUG_PRINT(("wdread\n"), DEBUG_XFERS);
return (physio(wdstrategy, dev, B_READ, minphys, uio));
}
int
wdwrite(dev_t dev, struct uio *uio, int flags)
{
WDCDEBUG_PRINT(("wdwrite\n"), DEBUG_XFERS);
return (physio(wdstrategy, dev, B_WRITE, minphys, uio));
}
int
wdopen(dev_t dev, int flag, int fmt, struct proc *p)
{
struct wd_softc *wd;
struct channel_softc *chnl;
int unit, part;
int error;
WDCDEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS);
unit = DISKUNIT(dev);
wd = wdlookup(unit);
if (wd == NULL)
return ENXIO;
chnl = (struct channel_softc *)(wd->drvp->chnl_softc);
if (chnl->dying)
return (ENXIO);
/*
* If this is the first open of this device, add a reference
* to the adapter.
*/
if ((error = disk_lock(&wd->sc_dk)) != 0)
goto bad4;
if (wd->sc_dk.dk_openmask != 0) {
/*
* If any partition is open, but the disk has been invalidated,
* disallow further opens.
*/
if ((wd->sc_flags & WDF_LOADED) == 0) {
error = EIO;
goto bad3;
}
} else {
if ((wd->sc_flags & WDF_LOADED) == 0) {
wd->sc_flags |= WDF_LOADED;
/* Load the physical device parameters. */
wd_get_params(wd, AT_WAIT, &wd->sc_params);
/* Load the partition info if not already loaded. */
if (wdgetdisklabel(dev, wd,
wd->sc_dk.dk_label, 0) == EIO) {
error = EIO;
goto bad;
}
}
}
part = DISKPART(dev);
if ((error = disk_openpart(&wd->sc_dk, part, fmt, 1)) != 0)
goto bad;
disk_unlock(&wd->sc_dk);
device_unref(&wd->sc_dev);
return 0;
bad:
if (wd->sc_dk.dk_openmask == 0) {
}
bad3:
disk_unlock(&wd->sc_dk);
bad4:
device_unref(&wd->sc_dev);
return error;
}
int
wdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
struct wd_softc *wd;
int part = DISKPART(dev);
wd = wdlookup(DISKUNIT(dev));
if (wd == NULL)
return ENXIO;
WDCDEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS);
disk_lock_nointr(&wd->sc_dk);
disk_closepart(&wd->sc_dk, part, fmt);
if (wd->sc_dk.dk_openmask == 0) {
wd_flushcache(wd, AT_WAIT);
/* XXXX Must wait for I/O to complete! */
}
disk_unlock(&wd->sc_dk);
device_unref(&wd->sc_dev);
return (0);
}
void
wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp)
{
WDCDEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS);
bzero(lp, sizeof(struct disklabel));
lp->d_secsize = DEV_BSIZE;
DL_SETDSIZE(lp, wd->sc_capacity);
lp->d_ntracks = wd->sc_params.atap_heads;
lp->d_nsectors = wd->sc_params.atap_sectors;
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
lp->d_ncylinders = DL_GETDSIZE(lp) / lp->d_secpercyl;
if (wd->drvp->ata_vers == -1) {
lp->d_type = DTYPE_ST506;
strncpy(lp->d_typename, "ST506/MFM/RLL", sizeof lp->d_typename);
} else {
lp->d_type = DTYPE_ESDI;
strncpy(lp->d_typename, "ESDI/IDE disk", sizeof lp->d_typename);
}
/* XXX - user viscopy() like sd.c */
strncpy(lp->d_packname, wd->sc_params.atap_model, sizeof lp->d_packname);
lp->d_flags = 0;
lp->d_version = 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
}
/*
* Fabricate a default disk label, and try to read the correct one.
*/
int
wdgetdisklabel(dev_t dev, struct wd_softc *wd, struct disklabel *lp,
int spoofonly)
{
int error;
WDCDEBUG_PRINT(("wdgetdisklabel\n"), DEBUG_FUNCS);
wdgetdefaultlabel(wd, lp);
if (wd->drvp->state > RECAL)
wd->drvp->drive_flags |= DRIVE_RESET;
error = readdisklabel(DISKLABELDEV(dev), wdstrategy, lp,
spoofonly);
if (wd->drvp->state > RECAL)
wd->drvp->drive_flags |= DRIVE_RESET;
return (error);
}
int
wdioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p)
{
struct wd_softc *wd;
struct disklabel *lp;
int error = 0;
WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS);
wd = wdlookup(DISKUNIT(dev));
if (wd == NULL)
return ENXIO;
if ((wd->sc_flags & WDF_LOADED) == 0) {
error = EIO;
goto exit;
}
switch (xfer) {
case DIOCRLDINFO:
lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
wdgetdisklabel(dev, wd, lp, 0);
bcopy(lp, wd->sc_dk.dk_label, sizeof(*lp));
free(lp, M_TEMP, sizeof(*lp));
goto exit;
case DIOCGPDINFO:
wdgetdisklabel(dev, wd, (struct disklabel *)addr, 1);
goto exit;
case DIOCGDINFO:
*(struct disklabel *)addr = *(wd->sc_dk.dk_label);
goto exit;
case DIOCGPART:
((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&wd->sc_dk.dk_label->d_partitions[DISKPART(dev)];
goto exit;
case DIOCWDINFO:
case DIOCSDINFO:
if ((flag & FWRITE) == 0) {
error = EBADF;
goto exit;
}
if ((error = disk_lock(&wd->sc_dk)) != 0)
goto exit;
error = setdisklabel(wd->sc_dk.dk_label,
(struct disklabel *)addr, wd->sc_dk.dk_openmask);
if (error == 0) {
if (wd->drvp->state > RECAL)
wd->drvp->drive_flags |= DRIVE_RESET;
if (xfer == DIOCWDINFO)
error = writedisklabel(DISKLABELDEV(dev),
wdstrategy, wd->sc_dk.dk_label);
}
disk_unlock(&wd->sc_dk);
goto exit;
case DIOCCACHESYNC:
if ((flag & FWRITE) == 0) {
error = EBADF;
goto exit;
}
error = wd_flushcache(wd, AT_WAIT);
goto exit;
default:
error = wdc_ioctl(wd->drvp, xfer, addr, flag, p);
goto exit;
}
#ifdef DIAGNOSTIC
panic("wdioctl: impossible");
#endif
exit:
device_unref(&wd->sc_dev);
return (error);
}
#ifdef B_FORMAT
int
wdformat(struct buf *bp)
{
bp->b_flags |= B_FORMAT;
return wdstrategy(bp);
}
#endif
daddr_t
wdsize(dev_t dev)
{
struct wd_softc *wd;
struct disklabel *lp;
int part, omask;
daddr_t size;
WDCDEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS);
wd = wdlookup(DISKUNIT(dev));
if (wd == NULL)
return (-1);
part = DISKPART(dev);
omask = wd->sc_dk.dk_openmask & (1 << part);
if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) {
size = -1;
goto exit;
}
lp = wd->sc_dk.dk_label;
size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0)
size = -1;
exit:
device_unref(&wd->sc_dev);
return (size);
}
/* #define WD_DUMP_NOT_TRUSTED if you just want to watch */
static int wddoingadump = 0;
static int wddumprecalibrated = 0;
static int wddumpmulti = 1;
/*
* Dump core after a system crash.
*/
int
wddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
struct wd_softc *wd; /* disk unit to do the I/O */
struct disklabel *lp; /* disk's disklabel */
int unit, part;
int nblks; /* total number of sectors left to write */
int nwrt; /* sectors to write with current i/o. */
int err;
char errbuf[256];
/* Check if recursive dump; if so, punt. */
if (wddoingadump)
return EFAULT;
wddoingadump = 1;
unit = DISKUNIT(dev);
wd = wdlookup(unit);
if (wd == NULL)
return ENXIO;
part = DISKPART(dev);
/* Make sure it was initialized. */
if (wd->drvp->state < READY)
return ENXIO;
/* Convert to disk sectors. Request must be a multiple of size. */
lp = wd->sc_dk.dk_label;
if ((size % lp->d_secsize) != 0)
return EFAULT;
nblks = size / lp->d_secsize;
blkno = blkno / (lp->d_secsize / DEV_BSIZE);
/* Check transfer bounds against partition size. */
if ((blkno < 0) || ((blkno + nblks) > DL_GETPSIZE(&lp->d_partitions[part])))
return EINVAL;
/* Offset block number to start of partition. */
blkno += DL_GETPOFFSET(&lp->d_partitions[part]);
/* Recalibrate, if first dump transfer. */
if (wddumprecalibrated == 0) {
wddumpmulti = wd->sc_multi;
wddumprecalibrated = 1;
wd->drvp->state = RECAL;
}
while (nblks > 0) {
nwrt = min(nblks, wddumpmulti);
wd->sc_wdc_bio.blkno = blkno;
wd->sc_wdc_bio.flags = ATA_POLL;
if (wd->sc_flags & WDF_LBA48)
wd->sc_wdc_bio.flags |= ATA_LBA48;
if (wd->sc_flags & WDF_LBA)
wd->sc_wdc_bio.flags |= ATA_LBA;
wd->sc_wdc_bio.bcount = nwrt * lp->d_secsize;
wd->sc_wdc_bio.databuf = va;
wd->sc_wdc_bio.wd = wd;
#ifndef WD_DUMP_NOT_TRUSTED
switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) {
case WDC_TRY_AGAIN:
panic("wddump: try again");
break;
case WDC_QUEUED:
panic("wddump: polled command has been queued");
break;
case WDC_COMPLETE:
break;
}
switch(wd->sc_wdc_bio.error) {
case TIMEOUT:
printf("wddump: device timed out");
err = EIO;
break;
case ERR_DF:
printf("wddump: drive fault");
err = EIO;
break;
case ERR_DMA:
printf("wddump: DMA error");
err = EIO;
break;
case ERROR:
errbuf[0] = '\0';
ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf,
sizeof errbuf);
printf("wddump: %s", errbuf);
err = EIO;
break;
case NOERROR:
err = 0;
break;
default:
panic("wddump: unknown error type");
}
if (err != 0) {
printf("\n");
return err;
}
#else /* WD_DUMP_NOT_TRUSTED */
/* Let's just talk about this first... */
printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n",
unit, va, cylin, head, sector);
delay(500 * 1000); /* half a second */
#endif
/* update block count */
nblks -= nwrt;
blkno += nwrt;
va += nwrt * lp->d_secsize;
}
wddoingadump = 0;
return 0;
}
int
wd_get_params(struct wd_softc *wd, u_int8_t flags, struct ataparams *params)
{
switch (ata_get_params(wd->drvp, flags, params)) {
case CMD_AGAIN:
return 1;
case CMD_ERR:
/* If we already have drive parameters, reuse them. */
if (wd->sc_params.atap_cylinders != 0) {
if (params != &wd->sc_params)
bcopy(&wd->sc_params, params,
sizeof(struct ataparams));
return 0;
}
/*
* We `know' there's a drive here; just assume it's old.
* This geometry is only used to read the MBR and print a
* (false) attach message.
*/
bzero(params, sizeof(struct ataparams));
strncpy(params->atap_model, "ST506",
sizeof params->atap_model);
params->atap_config = ATA_CFG_FIXED;
params->atap_cylinders = 1024;
params->atap_heads = 8;
params->atap_sectors = 17;
params->atap_multi = 1;
params->atap_capabilities1 = params->atap_capabilities2 = 0;
wd->drvp->ata_vers = -1; /* Mark it as pre-ATA */
return 0;
case CMD_OK:
return 0;
default:
panic("wd_get_params: bad return code from ata_get_params");
/* NOTREACHED */
}
}
int
wd_flushcache(struct wd_softc *wd, int flags)
{
struct wdc_command wdc_c;
if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */
return EIO;
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = (wd->sc_flags & WDF_LBA48 ? WDCC_FLUSHCACHE_EXT :
WDCC_FLUSHCACHE);
wdc_c.r_st_bmask = WDCS_DRDY;
wdc_c.r_st_pmask = WDCS_DRDY;
wdc_c.flags = flags;
wdc_c.timeout = 30000; /* 30s timeout */
if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
printf("%s: flush cache command didn't complete\n",
wd->sc_dev.dv_xname);
return EIO;
}
if (wdc_c.flags & ERR_NODEV)
return ENODEV;
if (wdc_c.flags & AT_TIMEOU) {
printf("%s: flush cache command timeout\n",
wd->sc_dev.dv_xname);
return EIO;
}
if (wdc_c.flags & AT_ERROR) {
if (wdc_c.r_error == WDCE_ABRT) /* command not supported */
return ENODEV;
printf("%s: flush cache command: error 0x%x\n",
wd->sc_dev.dv_xname, wdc_c.r_error);
return EIO;
}
if (wdc_c.flags & AT_DF) {
printf("%s: flush cache command: drive fault\n",
wd->sc_dev.dv_xname);
return EIO;
}
return 0;
}
void
wd_standby(struct wd_softc *wd, int flags)
{
struct wdc_command wdc_c;
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = WDCC_STANDBY_IMMED;
wdc_c.r_st_bmask = WDCS_DRDY;
wdc_c.r_st_pmask = WDCS_DRDY;
wdc_c.flags = flags;
wdc_c.timeout = 30000; /* 30s timeout */
if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
printf("%s: standby command didn't complete\n",
wd->sc_dev.dv_xname);
}
if (wdc_c.flags & AT_TIMEOU) {
printf("%s: standby command timeout\n",
wd->sc_dev.dv_xname);
}
if (wdc_c.flags & AT_DF) {
printf("%s: standby command: drive fault\n",
wd->sc_dev.dv_xname);
}
/*
* Ignore error register, it shouldn't report anything else
* than COMMAND ABORTED, which means the device doesn't support
* standby
*/
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
/* $OpenBSD: bktr_os.c,v 1.37 2022/07/02 08:50:42 visa Exp $ */
/* $FreeBSD: src/sys/dev/bktr/bktr_os.c,v 1.20 2000/10/20 08:16:53 roger Exp $ */
/*
* This is part of the Driver for Video Capture Cards (Frame grabbers)
* and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879
* chipset.
* Copyright Roger Hardiman and Amancio Hasty.
*
* bktr_os : This has all the Operating System dependant code,
* probe/attach and open/close/ioctl/read/mmap
* memory allocation
* PCI bus interfacing
*
*
*/
/*
* 1. Redistributions of source code must retain the
* Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Amancio Hasty and
* Roger Hardiman
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define FIFO_RISC_DISABLED 0
#define ALL_INTS_DISABLED 0
#include "radio.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/signalvar.h>
#include <sys/mman.h>
#include <sys/vnode.h>
#if NRADIO > 0
#include <sys/radioio.h>
#include <dev/radio_if.h>
#endif
#include <uvm/uvm_extern.h>
#include <sys/device.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#ifdef BKTR_DEBUG
int bktr_debug = 1;
#define DPR(x) (bktr_debug ? printf x : 0)
#else
#define DPR(x)
#endif
#include <dev/ic/bt8xx.h> /* OpenBSD location for .h files */
#include <dev/pci/bktr/bktr_reg.h>
#include <dev/pci/bktr/bktr_tuner.h>
#include <dev/pci/bktr/bktr_audio.h>
#include <dev/pci/bktr/bktr_core.h>
#include <dev/pci/bktr/bktr_os.h>
#define IPL_VIDEO IPL_BIO /* XXX */
static int bktr_intr(void *arg) { return common_bktr_intr(arg); }
#define bktr_open bktropen
#define bktr_close bktrclose
#define bktr_read bktrread
#define bktr_write bktrwrite
#define bktr_ioctl bktrioctl
#define bktr_mmap bktrmmap
int bktr_open(dev_t, int, int, struct proc *);
int bktr_close(dev_t, int, int, struct proc *);
int bktr_read(dev_t, struct uio *, int);
int bktr_write(dev_t, struct uio *, int);
int bktr_ioctl(dev_t, ioctl_cmd_t, caddr_t, int, struct proc *);
paddr_t bktr_mmap(dev_t, off_t, int);
static int bktr_probe(struct device *, void *, void *);
static void bktr_attach(struct device *, struct device *, void *);
const struct cfattach bktr_ca = {
sizeof(struct bktr_softc), bktr_probe, bktr_attach
};
struct cfdriver bktr_cd = {
NULL, "bktr", DV_DULL
};
#if NRADIO > 0
/* for radio(4) */
int bktr_get_info(void *, struct radio_info *);
int bktr_set_info(void *, struct radio_info *);
const struct radio_hw_if bktr_hw_if = {
NULL, /* open */
NULL, /* close */
bktr_get_info,
bktr_set_info,
NULL /* search */
};
#endif
int
bktr_probe(struct device *parent, void *match, void *aux)
{
struct pci_attach_args *pa = aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_BROOKTREE &&
(PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT848 ||
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT849 ||
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT878 ||
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT879))
return 1;
return 0;
}
/*
* the attach routine.
*/
static void
bktr_attach(struct device *parent, struct device *self, void *aux)
{
bktr_ptr_t bktr;
u_int latency;
u_int fun;
unsigned int rev;
struct pci_attach_args *pa = aux;
pci_intr_handle_t ih;
const char *intrstr;
int retval;
int unit;
bktr = (bktr_ptr_t)self;
unit = bktr->bktr_dev.dv_unit;
bktr->dmat = pa->pa_dmat;
/* Enable Back-to-Back
XXX: check if all old DMA is stopped first (e.g. after warm
boot) */
fun = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
DPR((" fun=%b", fun, PCI_COMMAND_STATUS_BITS));
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
fun | PCI_COMMAND_BACKTOBACK_ENABLE);
/*
* map memory
*/
retval = pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM |
PCI_MAPREG_MEM_TYPE_32BIT, 0, &bktr->memt, &bktr->memh, NULL,
&bktr->obmemsz, 0);
DPR(("pci_mapreg_map: memt %lx, memh %lx, size %x\n",
bktr->memt, bktr->memh, bktr->obmemsz));
if (retval) {
printf("%s: can't map mem space\n", bktr_name(bktr));
return;
}
/*
* Disable the brooktree device
*/
OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED);
OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED);
/*
* map interrupt
*/
if (pci_intr_map(pa, &ih)) {
printf("%s: can't map interrupt\n",
bktr_name(bktr));
return;
}
intrstr = pci_intr_string(pa->pa_pc, ih);
bktr->ih = pci_intr_establish(pa->pa_pc, ih, IPL_VIDEO,
bktr_intr, bktr, bktr->bktr_dev.dv_xname);
if (bktr->ih == NULL) {
printf("%s: can't establish interrupt",
bktr_name(bktr));
if (intrstr != NULL)
printf(" at %s", intrstr);
printf("\n");
return;
}
if (intrstr != NULL)
printf(": %s\n", intrstr);
/*
* PCI latency timer. 32 is a good value for 4 bus mastering slots, if
* you have more than four, then 16 would probably be a better value.
*/
#ifndef BROOKTREE_DEF_LATENCY_VALUE
#define BROOKTREE_DEF_LATENCY_VALUE 0x10
#endif
latency = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_LATENCY_TIMER);
latency = (latency >> 8) & 0xff;
if (!latency) {
if (bootverbose) {
printf("%s: PCI bus latency was 0 changing to %d",
bktr_name(bktr), BROOKTREE_DEF_LATENCY_VALUE);
}
latency = BROOKTREE_DEF_LATENCY_VALUE;
pci_conf_write(pa->pa_pc, pa->pa_tag,
PCI_LATENCY_TIMER, latency<<8);
}
/* read the pci id and determine the card type */
fun = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ID_REG);
rev = PCI_REVISION(pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CLASS_REG));
common_bktr_attach(bktr, unit, fun, rev);
#if NRADIO > 0
if (bktr->card.tuner->pllControl[3] != 0x00)
radio_attach_mi(&bktr_hw_if, bktr, &bktr->bktr_dev);
#endif
}
/*
* Special Memory Allocation
*/
vaddr_t
get_bktr_mem(bktr_ptr_t bktr, bus_dmamap_t *dmapp, unsigned int size)
{
bus_dma_tag_t dmat = bktr->dmat;
bus_dma_segment_t seg;
bus_size_t align;
int rseg;
caddr_t kva;
/*
* Allocate a DMA area
*/
align = 1 << 24;
if (bus_dmamem_alloc(dmat, size, align, 0, &seg, 1,
&rseg, BUS_DMA_NOWAIT)) {
align = PAGE_SIZE;
if (bus_dmamem_alloc(dmat, size, align, 0, &seg, 1,
&rseg, BUS_DMA_NOWAIT)) {
printf("%s: Unable to dmamem_alloc of %d bytes\n",
bktr_name(bktr), size);
return 0;
}
}
if (bus_dmamem_map(dmat, &seg, rseg, size,
&kva, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) {
printf("%s: Unable to dmamem_map of %d bytes\n",
bktr_name(bktr), size);
bus_dmamem_free(dmat, &seg, rseg);
return 0;
}
/*
* Create and locd the DMA map for the DMA area
*/
if (bus_dmamap_create(dmat, size, 1, size, 0, BUS_DMA_NOWAIT, dmapp)) {
printf("%s: Unable to dmamap_create of %d bytes\n",
bktr_name(bktr), size);
bus_dmamem_unmap(dmat, kva, size);
bus_dmamem_free(dmat, &seg, rseg);
return 0;
}
if (bus_dmamap_load(dmat, *dmapp, kva, size, NULL, BUS_DMA_NOWAIT)) {
printf("%s: Unable to dmamap_load of %d bytes\n",
bktr_name(bktr), size);
bus_dmamem_unmap(dmat, kva, size);
bus_dmamem_free(dmat, &seg, rseg);
bus_dmamap_destroy(dmat, *dmapp);
return 0;
}
return (vaddr_t)kva;
}
void
free_bktr_mem(bktr_ptr_t bktr, bus_dmamap_t dmap, vaddr_t kva)
{
bus_dma_tag_t dmat = bktr->dmat;
bus_dmamem_unmap(dmat, (caddr_t)kva, dmap->dm_mapsize);
bus_dmamem_free(dmat, dmap->dm_segs, 1);
bus_dmamap_destroy(dmat, dmap);
}
/*---------------------------------------------------------
**
** BrookTree 848 character device driver routines
**
**---------------------------------------------------------
*/
#define VIDEO_DEV 0x00
#define TUNER_DEV 0x01
#define VBI_DEV 0x02
#define UNIT(x) ((minor((x)) < 16) ? minor((x)) : ((minor((x)) - 16) / 2))
#define FUNCTION(x) ((minor((x)) < 16) ? VIDEO_DEV : ((minor((x)) & 0x1) ? \
VBI_DEV : TUNER_DEV))
/*
*
*/
int
bktr_open(dev_t dev, int flags, int fmt, struct proc *p)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
/* unit out of range */
if ((unit >= bktr_cd.cd_ndevs) || (bktr_cd.cd_devs[unit] == NULL))
return(ENXIO);
bktr = bktr_cd.cd_devs[unit];
if (!(bktr->flags & METEOR_INITIALIZED)) /* device not found */
return(ENXIO);
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_open(bktr));
case TUNER_DEV:
return(tuner_open(bktr));
case VBI_DEV:
return(vbi_open(bktr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_close(dev_t dev, int flags, int fmt, struct proc *p)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
bktr = bktr_cd.cd_devs[unit];
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_close(bktr));
case TUNER_DEV:
return(tuner_close(bktr));
case VBI_DEV:
return(vbi_close(bktr));
}
return(ENXIO);
}
/*
*
*/
int
bktr_read(dev_t dev, struct uio *uio, int ioflag)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
bktr = bktr_cd.cd_devs[unit];
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_read(bktr, unit, dev, uio));
case VBI_DEV:
return(vbi_read(bktr, uio, ioflag));
}
return(ENXIO);
}
/*
*
*/
int
bktr_write(dev_t dev, struct uio *uio, int ioflag)
{
/* operation not supported */
return(EOPNOTSUPP);
}
/*
*
*/
int
bktr_ioctl(dev_t dev, ioctl_cmd_t cmd, caddr_t arg, int flag, struct proc* pr)
{
bktr_ptr_t bktr;
int unit;
unit = UNIT(dev);
bktr = bktr_cd.cd_devs[unit];
if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */
return(ENOMEM);
switch (FUNCTION(dev)) {
case VIDEO_DEV:
return(video_ioctl(bktr, unit, cmd, arg, pr));
case TUNER_DEV:
return(tuner_ioctl(bktr, unit, cmd, arg, pr));
}
return(ENXIO);
}
/*
*
*/
paddr_t
bktr_mmap(dev_t dev, off_t offset, int nprot)
{
int unit;
bktr_ptr_t bktr;
unit = UNIT(dev);
if (FUNCTION(dev) > 0) /* only allow mmap on /dev/bktr[n] */
return(-1);
bktr = bktr_cd.cd_devs[unit];
if (offset < 0)
return(-1);
if (offset >= bktr->alloc_pages * PAGE_SIZE)
return(-1);
return (bus_dmamem_mmap(bktr->dmat, bktr->dm_mem->dm_segs, 1,
offset, nprot, BUS_DMA_WAITOK));
}
#if NRADIO > 0
int
bktr_set_info(void *v, struct radio_info *ri)
{
struct bktr_softc *sc = v;
struct TVTUNER *tv = &sc->tuner;
u_int32_t freq;
u_int32_t chan;
if (ri->mute) {
/* mute the audio stream by switching the mux */
set_audio(sc, AUDIO_MUTE);
} else {
/* unmute the audio stream */
set_audio(sc, AUDIO_UNMUTE);
init_audio_devices(sc);
}
set_audio(sc, AUDIO_INTERN); /* use internal audio */
temp_mute(sc, TRUE);
if (ri->tuner_mode == RADIO_TUNER_MODE_TV) {
if (ri->chan) {
if (ri->chan < MIN_TV_CHAN)
ri->chan = MIN_TV_CHAN;
if (ri->chan > MAX_TV_CHAN)
ri->chan = MAX_TV_CHAN;
chan = ri->chan;
ri->chan = tv_channel(sc, chan);
tv->tuner_mode = BT848_TUNER_MODE_TV;
} else {
ri->chan = tv->channel;
}
} else {
if (ri->freq) {
if (ri->freq < MIN_FM_FREQ)
ri->freq = MIN_FM_FREQ;
if (ri->freq > MAX_FM_FREQ)
ri->freq = MAX_FM_FREQ;
freq = ri->freq / 10;
ri->freq = tv_freq(sc, freq, FM_RADIO_FREQUENCY) * 10;
tv->tuner_mode = BT848_TUNER_MODE_RADIO;
} else {
ri->freq = tv->frequency;
}
}
if (ri->chnlset >= CHNLSET_MIN && ri->chnlset <= CHNLSET_MAX)
tv->chnlset = ri->chnlset;
else
tv->chnlset = DEFAULT_CHNLSET;
temp_mute(sc, FALSE);
return (0);
}
int
bktr_get_info(void *v, struct radio_info *ri)
{
struct bktr_softc *sc = v;
struct TVTUNER *tv = &sc->tuner;
int status;
status = get_tuner_status(sc);
#define STATUSBIT_STEREO 0x10
ri->mute = (int)sc->audio_mute_state ? 1 : 0;
ri->caps = RADIO_CAPS_DETECT_STEREO | RADIO_CAPS_HW_AFC;
ri->info = (status & STATUSBIT_STEREO) ? RADIO_INFO_STEREO : 0;
/* not yet supported */
ri->volume = ri->rfreq = ri->lock = 0;
switch (tv->tuner_mode) {
case BT848_TUNER_MODE_TV:
ri->tuner_mode = RADIO_TUNER_MODE_TV;
ri->freq = tv->frequency * 1000 / 16;
break;
case BT848_TUNER_MODE_RADIO:
ri->tuner_mode = RADIO_TUNER_MODE_RADIO;
ri->freq = tv->frequency * 10;
break;
}
/*
* The field ri->stereo is used to forcible switch to
* mono/stereo, not as an indicator of received signal quality.
* The ri->info is for that purpose.
*/
ri->stereo = 1; /* Can't switch to mono, always stereo */
ri->chan = tv->channel;
ri->chnlset = tv->chnlset;
return (0);
}
#endif /* NRADIO */
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* $OpenBSD: nvram.c,v 1.7 2016/08/03 17:29:18 jcs Exp $ */
/*
* Copyright (c) 2004 Joshua Stein <jcs@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <dev/ic/mc146818reg.h>
/* checksum is calculated over bytes 2 to 31 and stored in byte 32 */
#define NVRAM_CSUM_START (MC_NVRAM_START + 2)
#define NVRAM_CSUM_END (MC_NVRAM_START + 31)
#define NVRAM_CSUM_LOC (MC_NVRAM_START + 32)
#define NVRAM_SIZE (128 - MC_NVRAM_START)
/* #define NVRAM_DEBUG 1 */
void nvramattach(int);
int nvramopen(dev_t dev, int flag, int mode, struct proc *p);
int nvramclose(dev_t dev, int flag, int mode, struct proc *p);
int nvramread(dev_t dev, struct uio *uio, int flags);
int nvram_csum_valid(void);
int nvram_get_byte(int byteno);
static int nvram_initialized;
void
nvramattach(int num)
{
if (num > 1)
return;
if (nvram_initialized || nvram_csum_valid()) {
#ifdef NVRAM_DEBUG
printf("nvram: initialized\n");
#endif
nvram_initialized = 1;
}
}
int
nvramopen(dev_t dev, int flag, int mode, struct proc *p)
{
/* TODO: re-calc checksum on every open? */
if ((minor(dev) != 0) || (!nvram_initialized))
return (ENXIO);
if ((flag & FWRITE))
return (EPERM);
return (0);
}
int
nvramclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
int
nvramread(dev_t dev, struct uio *uio, int flags)
{
u_char buf[NVRAM_SIZE];
off_t pos = uio->uio_offset;
u_char *tmp;
size_t count = ulmin(sizeof(buf), uio->uio_resid);
int ret;
if (!nvram_initialized)
return (ENXIO);
if (uio->uio_offset < 0)
return (EINVAL);
if (uio->uio_resid == 0)
return (0);
#ifdef NVRAM_DEBUG
printf("attempting to read %zu bytes at offset %lld\n", count, pos);
#endif
for (tmp = buf; count-- > 0 && pos < NVRAM_SIZE; ++pos, ++tmp)
*tmp = nvram_get_byte(pos);
#ifdef NVRAM_DEBUG
printf("nvramread read %td bytes (%s)\n", (tmp - buf), tmp);
#endif
ret = uiomove(buf, (tmp - buf), uio);
uio->uio_offset += uio->uio_resid;
return (ret);
}
int
nvram_get_byte(int byteno)
{
if (!nvram_initialized)
return (ENXIO);
return (mc146818_read(NULL, byteno + MC_NVRAM_START) & 0xff);
}
int
nvram_csum_valid(void)
{
u_short csum = 0;
u_short csumexpect;
int nreg;
for (nreg = NVRAM_CSUM_START; nreg <= NVRAM_CSUM_END; nreg++)
csum += mc146818_read(NULL, nreg);
csumexpect = mc146818_read(NULL, NVRAM_CSUM_LOC) << 8 |
mc146818_read(NULL, NVRAM_CSUM_LOC + 1);
#ifdef NVRAM_DEBUG
printf("nvram: checksum is %x, expecting %x\n", (csum & 0xffff),
csumexpect);
#endif
return ((csum & 0xffff) == csumexpect);
}
41
42
42
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* $OpenBSD: if_pflog.c,v 1.97 2021/01/20 23:25:19 bluhm Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* This code was written by John Ioannidis for BSD/OS in Athens, Greece,
* in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
* and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis, Niels Provos.
* Copyright (c) 2002 - 2010 Henning Brauer
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include "bpfilter.h"
#include "pflog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/stdint.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#include <net/if_pflog.h>
#define PFLOGMTU (32768 + MHLEN + MLEN)
#ifdef PFLOGDEBUG
#define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0)
#else
#define DPRINTF(x)
#endif
void pflogattach(int);
int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int pflogioctl(struct ifnet *, u_long, caddr_t);
int pflog_clone_create(struct if_clone *, int);
int pflog_clone_destroy(struct ifnet *);
struct pflog_softc *pflog_getif(int);
struct if_clone pflog_cloner =
IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
LIST_HEAD(, pflog_softc) pflog_ifs = LIST_HEAD_INITIALIZER(pflog_ifs);
void
pflogattach(int npflog)
{
if_clone_attach(&pflog_cloner);
}
int
pflog_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
struct pflog_softc *pflogif;
pflogif = malloc(sizeof(*pflogif), M_DEVBUF, M_WAITOK|M_ZERO);
pflogif->sc_unit = unit;
ifp = &pflogif->sc_if;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
ifp->if_softc = pflogif;
ifp->if_mtu = PFLOGMTU;
ifp->if_ioctl = pflogioctl;
ifp->if_output = pflogoutput;
ifp->if_xflags = IFXF_CLONED;
ifp->if_type = IFT_PFLOG;
ifp->if_hdrlen = PFLOG_HDRLEN;
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
#endif
NET_LOCK();
LIST_INSERT_HEAD(&pflog_ifs, pflogif, sc_entry);
NET_UNLOCK();
return (0);
}
int
pflog_clone_destroy(struct ifnet *ifp)
{
struct pflog_softc *pflogif = ifp->if_softc;
NET_LOCK();
LIST_REMOVE(pflogif, sc_entry);
NET_UNLOCK();
if_detach(ifp);
free(pflogif, M_DEVBUF, sizeof(*pflogif));
return (0);
}
int
pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
m_freem(m); /* drop packet */
return (EAFNOSUPPORT);
}
int
pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
break;
default:
return (ENOTTY);
}
return (0);
}
struct pflog_softc *
pflog_getif(int unit)
{
struct pflog_softc *pflogif;
NET_ASSERT_LOCKED();
LIST_FOREACH(pflogif, &pflog_ifs, sc_entry) {
if (pflogif->sc_unit == unit)
break;
}
return pflogif;
}
int
pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
struct pf_rule *am, struct pf_ruleset *ruleset, struct pf_rule *trigger)
{
#if NBPFILTER > 0
struct pflog_softc *pflogif;
struct ifnet *ifn;
caddr_t if_bpf;
struct pfloghdr hdr;
if (rm == NULL || pd == NULL || pd->kif == NULL || pd->m == NULL)
return (-1);
if (trigger == NULL)
trigger = rm;
pflogif = pflog_getif(trigger->logif);
if (pflogif == NULL)
return (0);
ifn = &pflogif->sc_if;
if_bpf = ifn->if_bpf;
if (!if_bpf)
return (0);
bzero(&hdr, sizeof(hdr));
hdr.length = PFLOG_REAL_HDRLEN;
hdr.action = rm->action;
hdr.reason = reason;
memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname));
if (am == NULL) {
hdr.rulenr = htonl(rm->nr);
hdr.subrulenr = -1;
} else {
hdr.rulenr = htonl(am->nr);
hdr.subrulenr = htonl(rm->nr);
if (ruleset != NULL && ruleset->anchor != NULL)
strlcpy(hdr.ruleset, ruleset->anchor->name,
sizeof(hdr.ruleset));
}
if (trigger->log & PF_LOG_USER && !pd->lookup.done)
pd->lookup.done = pf_socket_lookup(pd);
if (trigger->log & PF_LOG_USER && pd->lookup.done > 0) {
hdr.uid = pd->lookup.uid;
hdr.pid = pd->lookup.pid;
} else {
hdr.uid = -1;
hdr.pid = NO_PID;
}
hdr.rule_uid = rm->cuid;
hdr.rule_pid = rm->cpid;
hdr.dir = pd->dir;
hdr.af = pd->af;
if (pd->src != NULL && pd->dst != NULL) {
if (pd->af != pd->naf ||
pf_addr_compare(pd->src, &pd->nsaddr, pd->naf) != 0 ||
pf_addr_compare(pd->dst, &pd->ndaddr, pd->naf) != 0 ||
pd->osport != pd->nsport ||
pd->odport != pd->ndport) {
hdr.rewritten = 1;
}
}
hdr.naf = pd->naf;
pf_addrcpy(&hdr.saddr, &pd->nsaddr, pd->naf);
pf_addrcpy(&hdr.daddr, &pd->ndaddr, pd->naf);
hdr.sport = pd->nsport;
hdr.dport = pd->ndport;
ifn->if_opackets++;
ifn->if_obytes += pd->m->m_pkthdr.len;
bpf_mtap_hdr(if_bpf, &hdr, sizeof(hdr), pd->m, BPF_DIRECTION_OUT);
#endif
return (0);
}
4
4
86
87
1
3
3
1
6
2
4
8
11
4
9
3
6
1
6
1
1
3
2
2
1
12
2
2
2
2
2
4
4
2
12
3
8
3
4
11
11
1
1
9
1
4
4
4
3
3
3
2
1
2
1
45
46
43
35
35
1
6
4
2
2
2
1
27
22
34
14
1
1
5
15
15
19
15
5
19
7
7
8
4
3
1
3
1
1
2
2
2
2
2
7
3
2
5
1
5
5
3
4
3
3
4
3
4
5
5
5
4
5
5
1
1
2
1
3
15
15
5
15
15
5
5
5
5
5
5
5
5
5
5
5
5
5
5
3
1
2
1
1
1
1
1
1
3
1
2
1
5
5
2
2
2
2
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302
6303
6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315
6316
6317
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
6614
6615
6616
6617
6618
6619
6620
6621
6622
6623
6624
6625
6626
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
6676
6677
6678
6679
6680
6681
6682
6683
6684
6685
6686
6687
6688
6689
6690
6691
6692
6693
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723
6724
6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
6779
6780
6781
6782
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809
6810
6811
6812
6813
6814
6815
6816
6817
6818
6819
6820
6821
6822
6823
6824
6825
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
6847
6848
6849
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
6918
6919
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
6993
6994
6995
6996
6997
6998
6999
7000
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
7092
7093
7094
7095
7096
7097
7098
7099
7100
7101
7102
7103
7104
7105
7106
7107
7108
7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
7180
7181
7182
7183
7184
7185
7186
7187
7188
7189
7190
7191
7192
7193
7194
7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297
7298
7299
7300
7301
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
7499
7500
7501
7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809
7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829
7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852
7853
7854
7855
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
7912
7913
7914
7915
7916
7917
7918
7919
7920
7921
7922
7923
7924
7925
7926
7927
7928
7929
7930
7931
7932
7933
7934
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944
7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
8021
8022
8023
8024
8025
8026
8027
8028
8029
8030
8031
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
8047
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
8070
8071
8072
8073
8074
8075
8076
8077
8078
8079
8080
8081
8082
8083
8084
8085
8086
8087
8088
8089
8090
8091
8092
8093
8094
8095
8096
8097
8098
8099
8100
8101
8102
8103
8104
8105
8106
8107
8108
8109
8110
8111
8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
8140
8141
8142
8143
8144
8145
8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
8265
8266
8267
8268
8269
8270
8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402
8403
8404
8405
8406
8407
8408
8409
8410
8411
8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
8477
8478
8479
8480
8481
8482
8483
8484
8485
8486
8487
8488
8489
8490
8491
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
8521
8522
8523
8524
8525
8526
8527
8528
8529
8530
8531
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
8556
8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576
8577
8578
8579
8580
8581
8582
8583
8584
8585
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
8600
8601
8602
8603
8604
8605
8606
8607
8608
8609
8610
8611
8612
8613
8614
8615
8616
8617
8618
8619
8620
8621
8622
8623
8624
8625
8626
8627
8628
8629
8630
8631
8632
8633
8634
8635
8636
8637
8638
8639
8640
8641
8642
8643
8644
8645
8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
8667
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693
8694
8695
8696
8697
8698
8699
8700
8701
8702
8703
8704
8705
8706
8707
8708
8709
8710
8711
8712
8713
8714
8715
8716
8717
8718
8719
8720
8721
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
8743
8744
8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
8774
8775
8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
8789
8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
8845
8846
8847
8848
8849
8850
8851
8852
8853
8854
8855
8856
8857
8858
8859
8860
8861
8862
8863
8864
8865
8866
8867
8868
8869
8870
8871
8872
8873
8874
8875
8876
8877
8878
8879
8880
8881
8882
8883
8884
8885
8886
8887
8888
8889
8890
8891
8892
8893
8894
8895
8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
8917
8918
8919
8920
8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082
9083
9084
9085
9086
9087
9088
9089
9090
9091
9092
9093
9094
9095
9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
9167
9168
9169
9170
9171
9172
9173
9174
9175
9176
9177
9178
9179
9180
9181
9182
9183
9184
9185
9186
9187
9188
9189
9190
9191
9192
9193
9194
9195
9196
9197
9198
9199
9200
9201
9202
9203
9204
9205
9206
9207
9208
9209
9210
9211
9212
9213
9214
9215
9216
9217
9218
9219
9220
9221
9222
9223
9224
9225
9226
9227
9228
9229
9230
9231
9232
9233
9234
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
9249
9250
9251
9252
9253
9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
9269
9270
9271
9272
9273
9274
9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286
9287
9288
9289
9290
9291
9292
9293
9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
9309
9310
9311
9312
9313
9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
/* $OpenBSD: vmm.c,v 1.322 2022/09/02 17:46:37 dv Exp $ */
/*
* Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/refcnt.h>
#include <sys/rwlock.h>
#include <sys/pledge.h>
#include <sys/memrange.h>
#include <sys/tracepoint.h>
#include <uvm/uvm_extern.h>
#include <machine/fpu.h>
#include <machine/pmap.h>
#include <machine/biosvar.h>
#include <machine/segments.h>
#include <machine/cpufunc.h>
#include <machine/vmmvar.h>
#include <dev/isa/isareg.h>
#include <dev/pv/pvreg.h>
#ifdef MP_LOCKDEBUG
#include <ddb/db_output.h>
extern int __mp_lock_spinout;
#endif /* MP_LOCKDEBUG */
/* #define VMM_DEBUG */
void *l1tf_flush_region;
#ifdef VMM_DEBUG
#define DPRINTF(x...) do { printf(x); } while(0)
#else
#define DPRINTF(x...)
#endif /* VMM_DEBUG */
#define DEVNAME(s) ((s)->sc_dev.dv_xname)
#define CTRL_DUMP(x,y,z) printf(" %s: Can set:%s Can clear:%s\n", #z , \
vcpu_vmx_check_cap(x, IA32_VMX_##y ##_CTLS, \
IA32_VMX_##z, 1) ? "Yes" : "No", \
vcpu_vmx_check_cap(x, IA32_VMX_##y ##_CTLS, \
IA32_VMX_##z, 0) ? "Yes" : "No");
#define VMX_EXIT_INFO_HAVE_RIP 0x1
#define VMX_EXIT_INFO_HAVE_REASON 0x2
#define VMX_EXIT_INFO_COMPLETE \
(VMX_EXIT_INFO_HAVE_RIP | VMX_EXIT_INFO_HAVE_REASON)
/*
* Virtual Machine
*
* Methods used to protect vm struct members:
* a atomic operations
* I immutable after create
* K kernel lock
* r reference count
* v vcpu list rwlock (vm_vcpu_list)
* V vmm_softc's vm_lock
*/
struct vm {
struct vmspace *vm_vmspace; /* [K] */
vm_map_t vm_map; /* [K] */
uint32_t vm_id; /* [I] */
pid_t vm_creator_pid; /* [I] */
size_t vm_nmemranges; /* [I] */
size_t vm_memory_size; /* [I] */
char vm_name[VMM_MAX_NAME_LEN];
struct vm_mem_range vm_memranges[VMM_MAX_MEM_RANGES];
struct refcnt vm_refcnt; /* [a] */
struct vcpu_head vm_vcpu_list; /* [v] */
uint32_t vm_vcpu_ct; /* [v] */
u_int vm_vcpus_running; /* [a] */
struct rwlock vm_vcpu_lock;
SLIST_ENTRY(vm) vm_link; /* [V] */
};
SLIST_HEAD(vmlist_head, vm);
/*
* Virtual Machine Monitor
*
* Methods used to protect struct members in the global vmm device:
* a atomic opererations
* I immutable operations
* K kernel lock
* p virtual process id (vpid/asid) rwlock
* r reference count
* v vm list rwlock (vm_lock)
*/
struct vmm_softc {
struct device sc_dev; /* [r] */
/* Suspend/Resume Synchronization */
struct refcnt sc_refcnt;
volatile unsigned int sc_status; /* [a] */
#define VMM_SUSPENDED (unsigned int) 0
#define VMM_ACTIVE (unsigned int) 1
/* Capabilities */
uint32_t nr_vmx_cpus; /* [I] */
uint32_t nr_svm_cpus; /* [I] */
uint32_t nr_rvi_cpus; /* [I] */
uint32_t nr_ept_cpus; /* [I] */
/* Managed VMs */
struct vmlist_head vm_list; /* [v] */
int mode; /* [I] */
size_t vcpu_ct; /* [v] */
size_t vcpu_max; /* [I] */
struct rwlock vm_lock;
size_t vm_ct; /* [v] no. of in-memory VMs */
size_t vm_idx; /* [a] next unique VM index */
struct rwlock vpid_lock;
uint16_t max_vpid; /* [I] */
uint8_t vpids[512]; /* [p] bitmap of VPID/ASIDs */
};
void vmx_dump_vmcs_field(uint16_t, const char *);
int vmm_enabled(void);
int vmm_probe(struct device *, void *, void *);
void vmm_attach(struct device *, struct device *, void *);
int vmm_activate(struct device *, int);
int vmmopen(dev_t, int, int, struct proc *);
int vmmioctl(dev_t, u_long, caddr_t, int, struct proc *);
int vmmclose(dev_t, int, int, struct proc *);
int vmm_quiesce_vmx(void);
int vmm_start(void);
int vmm_stop(void);
size_t vm_create_check_mem_ranges(struct vm_create_params *);
int vm_create(struct vm_create_params *, struct proc *);
int vm_run(struct vm_run_params *);
int vm_terminate(struct vm_terminate_params *);
int vm_get_info(struct vm_info_params *);
int vm_resetcpu(struct vm_resetcpu_params *);
int vm_intr_pending(struct vm_intr_params *);
int vm_rwregs(struct vm_rwregs_params *, int);
int vm_mprotect_ept(struct vm_mprotect_ept_params *);
int vm_rwvmparams(struct vm_rwvmparams_params *, int);
int vm_find(uint32_t, struct vm **);
int vcpu_readregs_vmx(struct vcpu *, uint64_t, int, struct vcpu_reg_state *);
int vcpu_readregs_svm(struct vcpu *, uint64_t, struct vcpu_reg_state *);
int vcpu_writeregs_vmx(struct vcpu *, uint64_t, int, struct vcpu_reg_state *);
int vcpu_writeregs_svm(struct vcpu *, uint64_t, struct vcpu_reg_state *);
int vcpu_reset_regs(struct vcpu *, struct vcpu_reg_state *);
int vcpu_reset_regs_vmx(struct vcpu *, struct vcpu_reg_state *);
int vcpu_reset_regs_svm(struct vcpu *, struct vcpu_reg_state *);
int vcpu_reload_vmcs_vmx(struct vcpu *);
int vcpu_init(struct vcpu *);
int vcpu_init_vmx(struct vcpu *);
int vcpu_init_svm(struct vcpu *);
int vcpu_must_stop(struct vcpu *);
int vcpu_run_vmx(struct vcpu *, struct vm_run_params *);
int vcpu_run_svm(struct vcpu *, struct vm_run_params *);
void vcpu_deinit(struct vcpu *);
void vcpu_deinit_vmx(struct vcpu *);
void vcpu_deinit_svm(struct vcpu *);
int vm_impl_init(struct vm *, struct proc *);
int vm_impl_init_vmx(struct vm *, struct proc *);
int vm_impl_init_svm(struct vm *, struct proc *);
void vm_impl_deinit(struct vm *);
void vm_impl_deinit_vmx(struct vm *);
void vm_impl_deinit_svm(struct vm *);
void vm_teardown(struct vm **);
int vcpu_vmx_check_cap(struct vcpu *, uint32_t, uint32_t, int);
int vcpu_vmx_compute_ctrl(uint64_t, uint16_t, uint32_t, uint32_t, uint32_t *);
int vmx_get_exit_info(uint64_t *, uint64_t *);
int vmx_load_pdptes(struct vcpu *);
int vmx_handle_exit(struct vcpu *);
int svm_handle_exit(struct vcpu *);
int svm_handle_msr(struct vcpu *);
int vmm_handle_xsetbv(struct vcpu *, uint64_t *);
int vmx_handle_xsetbv(struct vcpu *);
int svm_handle_xsetbv(struct vcpu *);
int vmm_handle_cpuid(struct vcpu *);
int vmx_handle_rdmsr(struct vcpu *);
int vmx_handle_wrmsr(struct vcpu *);
int vmx_handle_cr0_write(struct vcpu *, uint64_t);
int vmx_handle_cr4_write(struct vcpu *, uint64_t);
int vmx_handle_cr(struct vcpu *);
int svm_handle_inout(struct vcpu *);
int vmx_handle_inout(struct vcpu *);
int svm_handle_hlt(struct vcpu *);
int vmx_handle_hlt(struct vcpu *);
int vmm_inject_ud(struct vcpu *);
int vmm_inject_gp(struct vcpu *);
int vmm_inject_db(struct vcpu *);
void vmx_handle_intr(struct vcpu *);
void vmx_handle_intwin(struct vcpu *);
void vmx_handle_misc_enable_msr(struct vcpu *);
int vmm_get_guest_memtype(struct vm *, paddr_t);
int vmx_get_guest_faulttype(void);
int svm_get_guest_faulttype(struct vmcb *);
int vmx_get_exit_qualification(uint64_t *);
int vmm_get_guest_cpu_cpl(struct vcpu *);
int vmm_get_guest_cpu_mode(struct vcpu *);
int svm_fault_page(struct vcpu *, paddr_t);
int vmx_fault_page(struct vcpu *, paddr_t);
int vmx_handle_np_fault(struct vcpu *);
int svm_handle_np_fault(struct vcpu *);
int vmx_mprotect_ept(vm_map_t, paddr_t, paddr_t, int);
pt_entry_t *vmx_pmap_find_pte_ept(pmap_t, paddr_t);
int vmm_alloc_vpid(uint16_t *);
void vmm_free_vpid(uint16_t);
const char *vcpu_state_decode(u_int);
const char *vmx_exit_reason_decode(uint32_t);
const char *svm_exit_reason_decode(uint32_t);
const char *vmx_instruction_error_decode(uint32_t);
void svm_setmsrbr(struct vcpu *, uint32_t);
void svm_setmsrbw(struct vcpu *, uint32_t);
void svm_setmsrbrw(struct vcpu *, uint32_t);
void vmx_setmsrbr(struct vcpu *, uint32_t);
void vmx_setmsrbw(struct vcpu *, uint32_t);
void vmx_setmsrbrw(struct vcpu *, uint32_t);
void svm_set_clean(struct vcpu *, uint32_t);
void svm_set_dirty(struct vcpu *, uint32_t);
int vmm_gpa_is_valid(struct vcpu *vcpu, paddr_t gpa, size_t obj_size);
void vmm_init_pvclock(struct vcpu *, paddr_t);
int vmm_update_pvclock(struct vcpu *);
int vmm_pat_is_valid(uint64_t);
#ifdef MULTIPROCESSOR
static int vmx_remote_vmclear(struct cpu_info*, struct vcpu *);
#endif
#ifdef VMM_DEBUG
void dump_vcpu(struct vcpu *);
void vmx_vcpu_dump_regs(struct vcpu *);
void vmx_dump_vmcs(struct vcpu *);
const char *msr_name_decode(uint32_t);
void vmm_segment_desc_decode(uint64_t);
void vmm_decode_cr0(uint64_t);
void vmm_decode_cr3(uint64_t);
void vmm_decode_cr4(uint64_t);
void vmm_decode_msr_value(uint64_t, uint64_t);
void vmm_decode_apicbase_msr_value(uint64_t);
void vmm_decode_ia32_fc_value(uint64_t);
void vmm_decode_mtrrcap_value(uint64_t);
void vmm_decode_perf_status_value(uint64_t);
void vmm_decode_perf_ctl_value(uint64_t);
void vmm_decode_mtrrdeftype_value(uint64_t);
void vmm_decode_efer_value(uint64_t);
void vmm_decode_rflags(uint64_t);
void vmm_decode_misc_enable_value(uint64_t);
const char *vmm_decode_cpu_mode(struct vcpu *);
extern int mtrr2mrt(int);
struct vmm_reg_debug_info {
uint64_t vrdi_bit;
const char *vrdi_present;
const char *vrdi_absent;
};
#endif /* VMM_DEBUG */
extern uint64_t tsc_frequency;
extern int tsc_is_invariant;
const char *vmm_hv_signature = VMM_HV_SIGNATURE;
const struct kmem_pa_mode vmm_kp_contig = {
.kp_constraint = &no_constraint,
.kp_maxseg = 1,
.kp_align = 4096,
.kp_zero = 1,
};
struct cfdriver vmm_cd = {
NULL, "vmm", DV_DULL, CD_SKIPHIBERNATE
};
const struct cfattach vmm_ca = {
sizeof(struct vmm_softc), vmm_probe, vmm_attach, NULL, vmm_activate
};
/*
* Helper struct to easily get the VMCS field IDs needed in vmread/vmwrite
* to access the individual fields of the guest segment registers. This
* struct is indexed by VCPU_REGS_* id.
*/
const struct {
uint64_t selid;
uint64_t limitid;
uint64_t arid;
uint64_t baseid;
} vmm_vmx_sreg_vmcs_fields[] = {
{ VMCS_GUEST_IA32_CS_SEL, VMCS_GUEST_IA32_CS_LIMIT,
VMCS_GUEST_IA32_CS_AR, VMCS_GUEST_IA32_CS_BASE },
{ VMCS_GUEST_IA32_DS_SEL, VMCS_GUEST_IA32_DS_LIMIT,
VMCS_GUEST_IA32_DS_AR, VMCS_GUEST_IA32_DS_BASE },
{ VMCS_GUEST_IA32_ES_SEL, VMCS_GUEST_IA32_ES_LIMIT,
VMCS_GUEST_IA32_ES_AR, VMCS_GUEST_IA32_ES_BASE },
{ VMCS_GUEST_IA32_FS_SEL, VMCS_GUEST_IA32_FS_LIMIT,
VMCS_GUEST_IA32_FS_AR, VMCS_GUEST_IA32_FS_BASE },
{ VMCS_GUEST_IA32_GS_SEL, VMCS_GUEST_IA32_GS_LIMIT,
VMCS_GUEST_IA32_GS_AR, VMCS_GUEST_IA32_GS_BASE },
{ VMCS_GUEST_IA32_SS_SEL, VMCS_GUEST_IA32_SS_LIMIT,
VMCS_GUEST_IA32_SS_AR, VMCS_GUEST_IA32_SS_BASE },
{ VMCS_GUEST_IA32_LDTR_SEL, VMCS_GUEST_IA32_LDTR_LIMIT,
VMCS_GUEST_IA32_LDTR_AR, VMCS_GUEST_IA32_LDTR_BASE },
{ VMCS_GUEST_IA32_TR_SEL, VMCS_GUEST_IA32_TR_LIMIT,
VMCS_GUEST_IA32_TR_AR, VMCS_GUEST_IA32_TR_BASE }
};
/* Pools for VMs and VCPUs */
struct pool vm_pool;
struct pool vcpu_pool;
struct vmm_softc *vmm_softc;
/* IDT information used when populating host state area */
extern vaddr_t idt_vaddr;
extern struct gate_descriptor *idt;
/* Constants used in "CR access exit" */
#define CR_WRITE 0
#define CR_READ 1
#define CR_CLTS 2
#define CR_LMSW 3
/*
* vmm_enabled
*
* Checks if we have at least one CPU with either VMX or SVM.
* Returns 1 if we have at least one of either type, but not both, 0 otherwise.
*/
int
vmm_enabled(void)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
int found_vmx = 0, found_svm = 0;
/* Check if we have at least one CPU with either VMX or SVM */
CPU_INFO_FOREACH(cii, ci) {
if (ci->ci_vmm_flags & CI_VMM_VMX)
found_vmx = 1;
if (ci->ci_vmm_flags & CI_VMM_SVM)
found_svm = 1;
}
/* Don't support both SVM and VMX at the same time */
if (found_vmx && found_svm)
return (0);
if (found_vmx || found_svm)
return 1;
return 0;
}
int
vmm_probe(struct device *parent, void *match, void *aux)
{
const char **busname = (const char **)aux;
if (strcmp(*busname, vmm_cd.cd_name) != 0)
return (0);
return (1);
}
/*
* vmm_attach
*
* Calculates how many of each type of CPU we have, prints this into dmesg
* during attach. Initializes various locks, pools, and list structures for the
* VMM.
*/
void
vmm_attach(struct device *parent, struct device *self, void *aux)
{
struct vmm_softc *sc = (struct vmm_softc *)self;
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
sc->sc_status = VMM_ACTIVE;
refcnt_init(&sc->sc_refcnt);
refcnt_rele(&sc->sc_refcnt);
sc->nr_vmx_cpus = 0;
sc->nr_svm_cpus = 0;
sc->nr_rvi_cpus = 0;
sc->nr_ept_cpus = 0;
sc->vcpu_ct = 0;
sc->vcpu_max = VMM_MAX_VCPUS;
sc->vm_ct = 0;
sc->vm_idx = 0;
/* Calculate CPU features */
CPU_INFO_FOREACH(cii, ci) {
if (ci->ci_vmm_flags & CI_VMM_VMX)
sc->nr_vmx_cpus++;
if (ci->ci_vmm_flags & CI_VMM_SVM)
sc->nr_svm_cpus++;
if (ci->ci_vmm_flags & CI_VMM_RVI)
sc->nr_rvi_cpus++;
if (ci->ci_vmm_flags & CI_VMM_EPT)
sc->nr_ept_cpus++;
}
SLIST_INIT(&sc->vm_list);
rw_init(&sc->vm_lock, "vm_list");
if (sc->nr_ept_cpus) {
printf(": VMX/EPT");
sc->mode = VMM_MODE_EPT;
} else if (sc->nr_vmx_cpus) {
printf(": VMX");
sc->mode = VMM_MODE_VMX;
} else if (sc->nr_rvi_cpus) {
printf(": SVM/RVI");
sc->mode = VMM_MODE_RVI;
} else if (sc->nr_svm_cpus) {
printf(": SVM");
sc->mode = VMM_MODE_SVM;
} else {
printf(": unknown");
sc->mode = VMM_MODE_UNKNOWN;
}
if (sc->mode == VMM_MODE_EPT || sc->mode == VMM_MODE_VMX) {
if (!(curcpu()->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr)) {
l1tf_flush_region = km_alloc(VMX_L1D_FLUSH_SIZE,
&kv_any, &vmm_kp_contig, &kd_waitok);
if (!l1tf_flush_region) {
printf(" (failing, no memory)");
sc->mode = VMM_MODE_UNKNOWN;
} else {
printf(" (using slow L1TF mitigation)");
memset(l1tf_flush_region, 0xcc,
VMX_L1D_FLUSH_SIZE);
}
}
}
printf("\n");
if (sc->mode == VMM_MODE_SVM || sc->mode == VMM_MODE_RVI) {
sc->max_vpid = curcpu()->ci_vmm_cap.vcc_svm.svm_max_asid;
} else {
sc->max_vpid = 0xFFF;
}
bzero(&sc->vpids, sizeof(sc->vpids));
rw_init(&sc->vpid_lock, "vpid");
pool_init(&vm_pool, sizeof(struct vm), 0, IPL_MPFLOOR, PR_WAITOK,
"vmpool", NULL);
pool_init(&vcpu_pool, sizeof(struct vcpu), 64, IPL_MPFLOOR, PR_WAITOK,
"vcpupl", NULL);
vmm_softc = sc;
}
/*
* vmm_quiesce_vmx
*
* Prepare the host for suspend by flushing all VMCS states.
*/
int
vmm_quiesce_vmx(void)
{
struct vm *vm;
struct vcpu *vcpu;
int err;
/*
* We should be only called from a quiescing device state so we
* don't expect to sleep here. If we can't get all our locks,
* something is wrong.
*/
if ((err = rw_enter(&vmm_softc->vm_lock, RW_WRITE | RW_NOSLEEP)))
return (err);
/* Iterate over each vm... */
SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) {
if ((err = rw_enter(&vm->vm_vcpu_lock, RW_READ | RW_NOSLEEP)))
break;
/* Iterate over each vcpu... */
SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link) {
err = rw_enter(&vcpu->vc_lock, RW_WRITE | RW_NOSLEEP);
if (err)
break;
/* We can skip unlaunched VMCS. Nothing to flush. */
if (atomic_load_int(&vcpu->vc_vmx_vmcs_state)
!= VMCS_LAUNCHED) {
DPRINTF("%s: skipping vcpu %d for vm %d\n",
__func__, vcpu->vc_id, vm->vm_id);
rw_exit_write(&vcpu->vc_lock);
continue;
}
#ifdef MULTIPROCESSOR
if (vcpu->vc_last_pcpu != curcpu()) {
/* Remote cpu vmclear via ipi. */
err = vmx_remote_vmclear(vcpu->vc_last_pcpu,
vcpu);
if (err)
printf("%s: failed to remote vmclear "
"vcpu %d of vm %d\n", __func__,
vcpu->vc_id, vm->vm_id);
} else
#endif
{
/* Local cpu vmclear instruction. */
if ((err = vmclear(&vcpu->vc_control_pa)))
printf("%s: failed to locally vmclear "
"vcpu %d of vm %d\n", __func__,
vcpu->vc_id, vm->vm_id);
atomic_swap_uint(&vcpu->vc_vmx_vmcs_state,
VMCS_CLEARED);
}
rw_exit_write(&vcpu->vc_lock);
if (err)
break;
DPRINTF("%s: cleared vcpu %d for vm %d\n", __func__,
vcpu->vc_id, vm->vm_id);
}
rw_exit_read(&vm->vm_vcpu_lock);
if (err)
break;
}
rw_exit_write(&vmm_softc->vm_lock);
if (err)
return (err);
return (0);
}
/*
* vmm_activate
*/
int
vmm_activate(struct device *self, int act)
{
struct cpu_info *ci = curcpu();
unsigned int old_state;
switch (act) {
case DVACT_QUIESCE:
/* Block device users as we're suspending operation. */
old_state = atomic_cas_uint(&vmm_softc->sc_status, VMM_ACTIVE,
VMM_SUSPENDED);
if (old_state != VMM_ACTIVE)
DPRINTF("%s: invalid device state on quiesce (%d)\n",
__func__, old_state);
/* Wait for any device users to finish. */
while (refcnt_read(&vmm_softc->sc_refcnt) > 0)
tsleep_nsec(&vmm_softc, PPAUSE, "vmm", MSEC_TO_NSEC(1));
/* If we're not in vmm mode, nothing to do. */
if ((ci->ci_flags & CPUF_VMM) == 0)
break;
/* Intel systems need extra steps to sync vcpu state. */
if (vmm_softc->mode == VMM_MODE_EPT ||
vmm_softc->mode == VMM_MODE_VMX)
if (vmm_quiesce_vmx())
DPRINTF("%s: vmx quiesce failed\n", __func__);
/* Stop virtualization mode on all cpus. */
vmm_stop();
break;
case DVACT_WAKEUP:
/* Restart virtualization mode on all cpu's. */
if (vmm_softc->vm_ct > 0)
vmm_start();
/* Set the device back to active. */
old_state = atomic_cas_uint(&vmm_softc->sc_status,
VMM_SUSPENDED, VMM_ACTIVE);
if (old_state != VMM_SUSPENDED)
DPRINTF("%s: invalid device state on wakeup (%d)\n",
__func__, old_state);
/* Notify any waiting device users. */
wakeup(&vmm_softc);
break;
}
return (0);
}
/*
* vmmopen
*
* Called during open of /dev/vmm.
*
* Parameters:
* dev, flag, mode, p: These come from the character device and are
* all unused for this function
*
* Return values:
* ENODEV: if vmm(4) didn't attach or no supported CPUs detected
* 0: successful open
*/
int
vmmopen(dev_t dev, int flag, int mode, struct proc *p)
{
/* Don't allow open if we didn't attach */
if (vmm_softc == NULL)
return (ENODEV);
/* Don't allow open if we didn't detect any supported CPUs */
if (vmm_softc->mode != VMM_MODE_EPT && vmm_softc->mode != VMM_MODE_RVI)
return (ENODEV);
return 0;
}
/*
* vmmioctl
*
* Main ioctl dispatch routine for /dev/vmm. Parses ioctl type and calls
* appropriate lower level handler routine. Returns result to ioctl caller.
*/
int
vmmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
int ret;
KERNEL_UNLOCK();
refcnt_take(&vmm_softc->sc_refcnt);
while (atomic_load_int(&vmm_softc->sc_status) != VMM_ACTIVE) {
refcnt_rele(&vmm_softc->sc_refcnt);
/* Wait for the signal that we're running again. */
ret = tsleep_nsec(&vmm_softc, PWAIT | PCATCH, "vmm",
MSEC_TO_NSEC(1));
if (ret != ERESTART && ret != EINTR && ret != EWOULDBLOCK
&& ret != 0) {
printf("%s: unhandled wakeup (%d) for device\n",
__func__, ret);
ret = EBUSY;
goto out;
}
refcnt_take(&vmm_softc->sc_refcnt);
}
switch (cmd) {
case VMM_IOC_CREATE:
if ((ret = vmm_start()) != 0) {
vmm_stop();
break;
}
ret = vm_create((struct vm_create_params *)data, p);
break;
case VMM_IOC_RUN:
ret = vm_run((struct vm_run_params *)data);
break;
case VMM_IOC_INFO:
ret = vm_get_info((struct vm_info_params *)data);
break;
case VMM_IOC_TERM:
ret = vm_terminate((struct vm_terminate_params *)data);
break;
case VMM_IOC_RESETCPU:
ret = vm_resetcpu((struct vm_resetcpu_params *)data);
break;
case VMM_IOC_INTR:
ret = vm_intr_pending((struct vm_intr_params *)data);
break;
case VMM_IOC_READREGS:
ret = vm_rwregs((struct vm_rwregs_params *)data, 0);
break;
case VMM_IOC_WRITEREGS:
ret = vm_rwregs((struct vm_rwregs_params *)data, 1);
break;
case VMM_IOC_MPROTECT_EPT:
ret = vm_mprotect_ept((struct vm_mprotect_ept_params *)data);
break;
case VMM_IOC_READVMPARAMS:
ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 0);
break;
case VMM_IOC_WRITEVMPARAMS:
ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 1);
break;
default:
DPRINTF("%s: unknown ioctl code 0x%lx\n", __func__, cmd);
ret = ENOTTY;
}
refcnt_rele(&vmm_softc->sc_refcnt);
out:
KERNEL_LOCK();
return (ret);
}
/*
* pledge_ioctl_vmm
*
* Restrict the allowed ioctls in a pledged process context.
* Is called from pledge_ioctl().
*/
int
pledge_ioctl_vmm(struct proc *p, long com)
{
switch (com) {
case VMM_IOC_CREATE:
case VMM_IOC_INFO:
/* The "parent" process in vmd forks and manages VMs */
if (p->p_p->ps_pledge & PLEDGE_PROC)
return (0);
break;
case VMM_IOC_TERM:
/* XXX VM processes should only terminate themselves */
case VMM_IOC_RUN:
case VMM_IOC_RESETCPU:
case VMM_IOC_INTR:
case VMM_IOC_READREGS:
case VMM_IOC_WRITEREGS:
case VMM_IOC_MPROTECT_EPT:
case VMM_IOC_READVMPARAMS:
case VMM_IOC_WRITEVMPARAMS:
return (0);
}
return (EPERM);
}
/*
* vmmclose
*
* Called when /dev/vmm is closed. Presently unused.
*/
int
vmmclose(dev_t dev, int flag, int mode, struct proc *p)
{
return 0;
}
/*
* vm_find_vcpu
*
* Lookup VMM VCPU by ID number
*
* Parameters:
* vm: vm structure
* id: index id of vcpu
*
* Returns pointer to vcpu structure if successful, NULL otherwise
*/
static struct vcpu *
vm_find_vcpu(struct vm *vm, uint32_t id)
{
struct vcpu *vcpu;
if (vm == NULL)
return NULL;
rw_enter_read(&vm->vm_vcpu_lock);
SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link) {
refcnt_take(&vcpu->vc_refcnt);
if (vcpu->vc_id == id)
break;
refcnt_rele_wake(&vcpu->vc_refcnt);
}
rw_exit_read(&vm->vm_vcpu_lock);
return vcpu;
}
/*
* vm_resetcpu
*
* Resets the vcpu defined in 'vrp' to power-on-init register state
*
* Parameters:
* vrp: ioctl structure defining the vcpu to reset (see vmmvar.h)
*
* Returns 0 if successful, or various error codes on failure:
* ENOENT if the VM id contained in 'vrp' refers to an unknown VM or
* if vrp describes an unknown vcpu for this VM
* EBUSY if the indicated VCPU is not stopped
* EIO if the indicated VCPU failed to reset
*/
int
vm_resetcpu(struct vm_resetcpu_params *vrp)
{
struct vm *vm;
struct vcpu *vcpu;
int error, ret = 0;
/* Find the desired VM */
error = vm_find(vrp->vrp_vm_id, &vm);
/* Not found? exit. */
if (error != 0) {
DPRINTF("%s: vm id %u not found\n", __func__,
vrp->vrp_vm_id);
return (error);
}
vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id);
if (vcpu == NULL) {
DPRINTF("%s: vcpu id %u of vm %u not found\n", __func__,
vrp->vrp_vcpu_id, vrp->vrp_vm_id);
ret = ENOENT;
goto out;
}
rw_enter_write(&vcpu->vc_lock);
if (vcpu->vc_state != VCPU_STATE_STOPPED ||
refcnt_shared(&vcpu->vc_refcnt)) {
ret = EBUSY;
} else {
if (vcpu_reset_regs(vcpu, &vrp->vrp_init_state)) {
printf("%s: failed\n", __func__);
#ifdef VMM_DEBUG
dump_vcpu(vcpu);
#endif /* VMM_DEBUG */
ret = EIO;
}
}
rw_exit_write(&vcpu->vc_lock);
out:
if (vcpu != NULL)
refcnt_rele_wake(&vcpu->vc_refcnt);
refcnt_rele_wake(&vm->vm_refcnt);
return (ret);
}
/*
* vm_intr_pending
*
* IOCTL handler routine for VMM_IOC_INTR messages, sent from vmd when an
* interrupt is pending and needs acknowledgment
*
* Parameters:
* vip: Describes the vm/vcpu for which the interrupt is pending
*
* Return values:
* 0: if successful
* ENOENT: if the VM/VCPU defined by 'vip' cannot be found
*/
int
vm_intr_pending(struct vm_intr_params *vip)
{
struct vm *vm;
struct vcpu *vcpu;
int error, ret = 0;
/* Find the desired VM */
error = vm_find(vip->vip_vm_id, &vm);
/* Not found? exit. */
if (error != 0)
return (error);
vcpu = vm_find_vcpu(vm, vip->vip_vcpu_id);
if (vcpu == NULL) {
ret = ENOENT;
goto out;
}
rw_enter_write(&vcpu->vc_lock);
vcpu->vc_intr = vip->vip_intr;
rw_exit_write(&vcpu->vc_lock);
refcnt_rele_wake(&vcpu->vc_refcnt);
out:
refcnt_rele_wake(&vm->vm_refcnt);
return (ret);
}
/*
* vm_rwvmparams
*
* IOCTL handler to read/write the current vmm params like pvclock gpa, pvclock
* version, etc.
*
* Parameters:
* vrwp: Describes the VM and VCPU to get/set the params from
* dir: 0 for reading, 1 for writing
*
* Return values:
* 0: if successful
* ENOENT: if the VM/VCPU defined by 'vpp' cannot be found
* EINVAL: if an error occurred reading the registers of the guest
*/
int
vm_rwvmparams(struct vm_rwvmparams_params *vpp, int dir) {
struct vm *vm;
struct vcpu *vcpu;
int error, ret = 0;
/* Find the desired VM */
error = vm_find(vpp->vpp_vm_id, &vm);
/* Not found? exit. */
if (error != 0)
return (error);
vcpu = vm_find_vcpu(vm, vpp->vpp_vcpu_id);
if (vcpu == NULL) {
ret = ENOENT;
goto out;
}
if (dir == 0) {
if (vpp->vpp_mask & VM_RWVMPARAMS_PVCLOCK_VERSION)
vpp->vpp_pvclock_version = vcpu->vc_pvclock_version;
if (vpp->vpp_mask & VM_RWVMPARAMS_PVCLOCK_SYSTEM_GPA)
vpp->vpp_pvclock_system_gpa = \
vcpu->vc_pvclock_system_gpa;
} else {
if (vpp->vpp_mask & VM_RWVMPARAMS_PVCLOCK_VERSION)
vcpu->vc_pvclock_version = vpp->vpp_pvclock_version;
if (vpp->vpp_mask & VM_RWVMPARAMS_PVCLOCK_SYSTEM_GPA) {
vmm_init_pvclock(vcpu, vpp->vpp_pvclock_system_gpa);
}
}
refcnt_rele_wake(&vcpu->vc_refcnt);
out:
refcnt_rele_wake(&vm->vm_refcnt);
return (ret);
}
/*
* vm_readregs
*
* IOCTL handler to read/write the current register values of a guest VCPU.
* The VCPU must not be running.
*
* Parameters:
* vrwp: Describes the VM and VCPU to get/set the registers from. The
* register values are returned here as well.
* dir: 0 for reading, 1 for writing
*
* Return values:
* 0: if successful
* ENOENT: if the VM/VCPU defined by 'vrwp' cannot be found
* EINVAL: if an error occurred accessing the registers of the guest
* EPERM: if the vm cannot be accessed from the calling process
*/
int
vm_rwregs(struct vm_rwregs_params *vrwp, int dir)
{
struct vm *vm;
struct vcpu *vcpu;
struct vcpu_reg_state *vrs = &vrwp->vrwp_regs;
int error, ret = 0;
/* Find the desired VM */
error = vm_find(vrwp->vrwp_vm_id, &vm);
/* Not found? exit. */
if (error != 0)
return (error);
vcpu = vm_find_vcpu(vm, vrwp->vrwp_vcpu_id);
if (vcpu == NULL) {
ret = ENOENT;
goto out;
}
rw_enter_write(&vcpu->vc_lock);
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
ret = (dir == 0) ?
vcpu_readregs_vmx(vcpu, vrwp->vrwp_mask, 1, vrs) :
vcpu_writeregs_vmx(vcpu, vrwp->vrwp_mask, 1, vrs);
else if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI)
ret = (dir == 0) ?
vcpu_readregs_svm(vcpu, vrwp->vrwp_mask, vrs) :
vcpu_writeregs_svm(vcpu, vrwp->vrwp_mask, vrs);
else {
DPRINTF("%s: unknown vmm mode", __func__);
ret = EINVAL;
}
rw_exit_write(&vcpu->vc_lock);
refcnt_rele_wake(&vcpu->vc_refcnt);
out:
refcnt_rele_wake(&vm->vm_refcnt);
return (ret);
}
/*
* vm_mprotect_ept
*
* IOCTL handler to sets the access protections of the ept
*
* Parameters:
* vmep: describes the memory for which the protect will be applied..
*
* Return values:
* 0: if successful
* ENOENT: if the VM defined by 'vmep' cannot be found
* EINVAL: if the sgpa or size is not page aligned, the prot is invalid,
* size is too large (512GB), there is wraparound
* (like start = 512GB-1 and end = 512GB-2),
* the address specified is not within the vm's mem range
* or the address lies inside reserved (MMIO) memory
*/
int
vm_mprotect_ept(struct vm_mprotect_ept_params *vmep)
{
struct vm *vm;
struct vcpu *vcpu;
vaddr_t sgpa;
size_t size;
vm_prot_t prot;
uint64_t msr;
int ret = 0, memtype;
/* If not EPT or RVI, nothing to do here */
if (!(vmm_softc->mode == VMM_MODE_EPT
|| vmm_softc->mode == VMM_MODE_RVI))
return (0);
/* Find the desired VM */
ret = vm_find(vmep->vmep_vm_id, &vm);
/* Not found? exit. */
if (ret != 0) {
DPRINTF("%s: vm id %u not found\n", __func__,
vmep->vmep_vm_id);
return (ret);
}
vcpu = vm_find_vcpu(vm, vmep->vmep_vcpu_id);
if (vcpu == NULL) {
DPRINTF("%s: vcpu id %u of vm %u not found\n", __func__,
vmep->vmep_vcpu_id, vmep->vmep_vm_id);
ret = ENOENT;
goto out_nolock;
}
rw_enter_write(&vcpu->vc_lock);
if (vcpu->vc_state != VCPU_STATE_STOPPED) {
DPRINTF("%s: mprotect_ept %u on vm %u attempted "
"while vcpu was in state %u (%s)\n", __func__,
vmep->vmep_vcpu_id, vmep->vmep_vm_id, vcpu->vc_state,
vcpu_state_decode(vcpu->vc_state));
ret = EBUSY;
goto out;
}
/* Only proceed if the pmap is in the correct mode */
KASSERT((vmm_softc->mode == VMM_MODE_EPT &&
vm->vm_map->pmap->pm_type == PMAP_TYPE_EPT) ||
(vmm_softc->mode == VMM_MODE_RVI &&
vm->vm_map->pmap->pm_type == PMAP_TYPE_RVI));
sgpa = vmep->vmep_sgpa;
size = vmep->vmep_size;
prot = vmep->vmep_prot;
/* No W^X permissions */
if ((prot & PROT_MASK) != prot &&
(prot & (PROT_WRITE | PROT_EXEC)) == (PROT_WRITE | PROT_EXEC)) {
DPRINTF("%s: W+X permission requested\n", __func__);
ret = EINVAL;
goto out;
}
/* No Write only permissions */
if ((prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) == PROT_WRITE) {
DPRINTF("%s: No Write only permissions\n", __func__);
ret = EINVAL;
goto out;
}
/* No empty permissions */
if (prot == 0) {
DPRINTF("%s: No empty permissions\n", __func__);
ret = EINVAL;
goto out;
}
/* No execute only on EPT CPUs that don't have that capability */
if (vmm_softc->mode == VMM_MODE_EPT) {
msr = rdmsr(IA32_VMX_EPT_VPID_CAP);
if (prot == PROT_EXEC &&
(msr & IA32_EPT_VPID_CAP_XO_TRANSLATIONS) == 0) {
DPRINTF("%s: Execute only permissions unsupported,"
" adding read permission\n", __func__);
prot |= PROT_READ;
}
}
/* Must be page aligned */
if ((sgpa & PAGE_MASK) || (size & PAGE_MASK) || size == 0) {
ret = EINVAL;
goto out;
}
/* size must be less then 512GB */
if (size >= NBPD_L4) {
ret = EINVAL;
goto out;
}
/* no wraparound */
if (sgpa + size < sgpa) {
ret = EINVAL;
goto out;
}
/*
* Specifying addresses within the PCI MMIO space is forbidden.
* Disallow addresses that start inside the MMIO space:
* [VMM_PCI_MMIO_BAR_BASE .. VMM_PCI_MMIO_BAR_END]
*/
if (sgpa >= VMM_PCI_MMIO_BAR_BASE && sgpa <= VMM_PCI_MMIO_BAR_END) {
ret = EINVAL;
goto out;
}
/*
* ... and disallow addresses that end inside the MMIO space:
* (VMM_PCI_MMIO_BAR_BASE .. VMM_PCI_MMIO_BAR_END]
*/
if (sgpa + size > VMM_PCI_MMIO_BAR_BASE &&
sgpa + size <= VMM_PCI_MMIO_BAR_END) {
ret = EINVAL;
goto out;
}
memtype = vmm_get_guest_memtype(vm, sgpa);
if (memtype == VMM_MEM_TYPE_UNKNOWN) {
ret = EINVAL;
goto out;
}
if (vmm_softc->mode == VMM_MODE_EPT)
ret = vmx_mprotect_ept(vm->vm_map, sgpa, sgpa + size, prot);
else if (vmm_softc->mode == VMM_MODE_RVI) {
pmap_write_protect(vm->vm_map->pmap, sgpa, sgpa + size, prot);
/* XXX requires a invlpga */
ret = 0;
} else
ret = EINVAL;
out:
if (vcpu != NULL) {
rw_exit_write(&vcpu->vc_lock);
refcnt_rele_wake(&vcpu->vc_refcnt);
}
out_nolock:
refcnt_rele_wake(&vm->vm_refcnt);
return (ret);
}
/*
* vmx_mprotect_ept
*
* apply the ept protections to the requested pages, faulting in the page if
* required.
*/
int
vmx_mprotect_ept(vm_map_t vm_map, paddr_t sgpa, paddr_t egpa, int prot)
{
struct vmx_invept_descriptor vid;
pmap_t pmap;
pt_entry_t *pte;
paddr_t addr;
int ret = 0;
pmap = vm_map->pmap;
KERNEL_LOCK();
for (addr = sgpa; addr < egpa; addr += PAGE_SIZE) {
pte = vmx_pmap_find_pte_ept(pmap, addr);
if (pte == NULL) {
ret = uvm_fault(vm_map, addr, VM_FAULT_WIRE,
PROT_READ | PROT_WRITE | PROT_EXEC);
if (ret)
printf("%s: uvm_fault returns %d, GPA=0x%llx\n",
__func__, ret, (uint64_t)addr);
pte = vmx_pmap_find_pte_ept(pmap, addr);
if (pte == NULL) {
KERNEL_UNLOCK();
return EFAULT;
}
}
if (prot & PROT_READ)
*pte |= EPT_R;
else
*pte &= ~EPT_R;
if (prot & PROT_WRITE)
*pte |= EPT_W;
else
*pte &= ~EPT_W;
if (prot & PROT_EXEC)
*pte |= EPT_X;
else
*pte &= ~EPT_X;
}
/*
* SDM 3C: 28.3.3.4 Guidelines for Use of the INVEPT Instruction
* the first bullet point seems to say we should call invept.
*
* Software should use the INVEPT instruction with the “single-context”
* INVEPT type after making any of the following changes to an EPT
* paging-structure entry (the INVEPT descriptor should contain an
* EPTP value that references — directly or indirectly
* — the modified EPT paging structure):
* — Changing any of the privilege bits 2:0 from 1 to 0.
* */
if (pmap->eptp != 0) {
memset(&vid, 0, sizeof(vid));
vid.vid_eptp = pmap->eptp;
DPRINTF("%s: flushing EPT TLB for EPTP 0x%llx\n", __func__,
vid.vid_eptp);
invept(IA32_VMX_INVEPT_SINGLE_CTX, &vid);
}
KERNEL_UNLOCK();
return ret;
}
/*
* vmx_pmap_find_pte_ept
*
* find the page table entry specified by addr in the pmap supplied.
*/
pt_entry_t *
vmx_pmap_find_pte_ept(pmap_t pmap, paddr_t addr)
{
int l4idx, l3idx, l2idx, l1idx;
pd_entry_t *pd;
paddr_t pdppa;
pt_entry_t *ptes, *pte;
l4idx = (addr & L4_MASK) >> L4_SHIFT; /* PML4E idx */
l3idx = (addr & L3_MASK) >> L3_SHIFT; /* PDPTE idx */
l2idx = (addr & L2_MASK) >> L2_SHIFT; /* PDE idx */
l1idx = (addr & L1_MASK) >> L1_SHIFT; /* PTE idx */
pd = (pd_entry_t *)pmap->pm_pdir;
if (pd == NULL)
return NULL;
/*
* l4idx should always be 0 since we don't support more than 512GB
* guest physical memory.
*/
if (l4idx > 0)
return NULL;
/*
* l3idx should always be < MAXDSIZ/1GB because we don't support more
* than MAXDSIZ guest phys mem.
*/
if (l3idx >= MAXDSIZ / ((paddr_t)1024 * 1024 * 1024))
return NULL;
pdppa = pd[l4idx] & PG_FRAME;
if (pdppa == 0)
return NULL;
ptes = (pt_entry_t *)PMAP_DIRECT_MAP(pdppa);
pdppa = ptes[l3idx] & PG_FRAME;
if (pdppa == 0)
return NULL;
ptes = (pt_entry_t *)PMAP_DIRECT_MAP(pdppa);
pdppa = ptes[l2idx] & PG_FRAME;
if (pdppa == 0)
return NULL;
ptes = (pt_entry_t *)PMAP_DIRECT_MAP(pdppa);
pte = &ptes[l1idx];
if (*pte == 0)
return NULL;
return pte;
}
/*
* vm_find
*
* Function to find an existing VM by its identifier.
* Must be called under the global vm_lock.
*
* Parameters:
* id: The VM identifier.
* *res: A pointer to the VM or NULL if not found
*
* Return values:
* 0: if successful
* ENOENT: if the VM defined by 'id' cannot be found
* EPERM: if the VM cannot be accessed by the current process
*/
int
vm_find(uint32_t id, struct vm **res)
{
struct proc *p = curproc;
struct vm *vm;
int ret = ENOENT;
*res = NULL;
rw_enter_read(&vmm_softc->vm_lock);
SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) {
refcnt_take(&vm->vm_refcnt);
if (vm->vm_id == id) {
/*
* In the pledged VM process, only allow to find
* the VM that is running in the current process.
* The managing vmm parent process can lookup all
* all VMs and is indicated by PLEDGE_PROC.
*/
if (((p->p_p->ps_pledge &
(PLEDGE_VMM | PLEDGE_PROC)) == PLEDGE_VMM) &&
(vm->vm_creator_pid != p->p_p->ps_pid))
ret = EPERM;
else {
*res = vm;
ret = 0;
}
break;
}
refcnt_rele_wake(&vm->vm_refcnt);
}
rw_exit_read(&vmm_softc->vm_lock);
if (ret == EPERM)
return (pledge_fail(p, EPERM, PLEDGE_VMM));
return (ret);
}
/*
* vmm_start
*
* Starts VMM mode on the system
*/
int
vmm_start(void)
{
struct cpu_info *self = curcpu();
#ifdef MULTIPROCESSOR
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
#ifdef MP_LOCKDEBUG
int nticks;
#endif /* MP_LOCKDEBUG */
#endif /* MULTIPROCESSOR */
/* VMM is already running */
if (self->ci_flags & CPUF_VMM)
return (0);
/* Start VMM on this CPU */
start_vmm_on_cpu(self);
if (!(self->ci_flags & CPUF_VMM)) {
printf("%s: failed to enter VMM mode\n",
self->ci_dev->dv_xname);
return (EIO);
}
#ifdef MULTIPROCESSOR
/* Broadcast start VMM IPI */
x86_broadcast_ipi(X86_IPI_START_VMM);
CPU_INFO_FOREACH(cii, ci) {
if (ci == self)
continue;
#ifdef MP_LOCKDEBUG
nticks = __mp_lock_spinout;
#endif /* MP_LOCKDEBUG */
while (!(ci->ci_flags & CPUF_VMM)) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spun out", __func__);
db_enter();
nticks = __mp_lock_spinout;
}
#endif /* MP_LOCKDEBUG */
}
}
#endif /* MULTIPROCESSOR */
return (0);
}
/*
* vmm_stop
*
* Stops VMM mode on the system
*/
int
vmm_stop(void)
{
struct cpu_info *self = curcpu();
#ifdef MULTIPROCESSOR
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
#ifdef MP_LOCKDEBUG
int nticks;
#endif /* MP_LOCKDEBUG */
#endif /* MULTIPROCESSOR */
/* VMM is not running */
if (!(self->ci_flags & CPUF_VMM))
return (0);
/* Stop VMM on this CPU */
stop_vmm_on_cpu(self);
if (self->ci_flags & CPUF_VMM) {
printf("%s: failed to exit VMM mode\n",
self->ci_dev->dv_xname);
return (EIO);
}
#ifdef MULTIPROCESSOR
/* Stop VMM on other CPUs */
x86_broadcast_ipi(X86_IPI_STOP_VMM);
CPU_INFO_FOREACH(cii, ci) {
if (ci == self)
continue;
#ifdef MP_LOCKDEBUG
nticks = __mp_lock_spinout;
#endif /* MP_LOCKDEBUG */
while ((ci->ci_flags & CPUF_VMM)) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spunout", __func__);
db_enter();
nticks = __mp_lock_spinout;
}
#endif /* MP_LOCKDEBUG */
}
}
#endif /* MULTIPROCESSOR */
return (0);
}
/*
* start_vmm_on_cpu
*
* Starts VMM mode on 'ci' by executing the appropriate CPU-specific insn
* sequence to enter VMM mode (eg, VMXON)
*/
void
start_vmm_on_cpu(struct cpu_info *ci)
{
uint64_t msr;
uint32_t cr4;
/* No VMM mode? exit. */
if ((ci->ci_vmm_flags & CI_VMM_VMX) == 0 &&
(ci->ci_vmm_flags & CI_VMM_SVM) == 0)
return;
/*
* AMD SVM
*/
if (ci->ci_vmm_flags & CI_VMM_SVM) {
msr = rdmsr(MSR_EFER);
msr |= EFER_SVME;
wrmsr(MSR_EFER, msr);
}
/*
* Intel VMX
*/
if (ci->ci_vmm_flags & CI_VMM_VMX) {
if (ci->ci_vmxon_region == 0)
return;
else {
bzero(ci->ci_vmxon_region, PAGE_SIZE);
ci->ci_vmxon_region->vr_revision =
ci->ci_vmm_cap.vcc_vmx.vmx_vmxon_revision;
/* Set CR4.VMXE */
cr4 = rcr4();
cr4 |= CR4_VMXE;
lcr4(cr4);
/* Enable VMX */
msr = rdmsr(MSR_IA32_FEATURE_CONTROL);
if (msr & IA32_FEATURE_CONTROL_LOCK) {
if (!(msr & IA32_FEATURE_CONTROL_VMX_EN))
return;
} else {
msr |= IA32_FEATURE_CONTROL_VMX_EN |
IA32_FEATURE_CONTROL_LOCK;
wrmsr(MSR_IA32_FEATURE_CONTROL, msr);
}
/* Enter VMX mode */
if (vmxon((uint64_t *)&ci->ci_vmxon_region_pa))
return;
}
}
atomic_setbits_int(&ci->ci_flags, CPUF_VMM);
}
/*
* stop_vmm_on_cpu
*
* Stops VMM mode on 'ci' by executing the appropriate CPU-specific insn
* sequence to exit VMM mode (eg, VMXOFF)
*/
void
stop_vmm_on_cpu(struct cpu_info *ci)
{
uint64_t msr;
uint32_t cr4;
if (!(ci->ci_flags & CPUF_VMM))
return;
/*
* AMD SVM
*/
if (ci->ci_vmm_flags & CI_VMM_SVM) {
msr = rdmsr(MSR_EFER);
msr &= ~EFER_SVME;
wrmsr(MSR_EFER, msr);
}
/*
* Intel VMX
*/
if (ci->ci_vmm_flags & CI_VMM_VMX) {
if (vmxoff())
panic("VMXOFF failed");
cr4 = rcr4();
cr4 &= ~CR4_VMXE;
lcr4(cr4);
}
atomic_clearbits_int(&ci->ci_flags, CPUF_VMM);
}
/*
* vmclear_on_cpu
*
* Flush and clear VMCS on 'ci' by executing vmclear.
*
*/
void
vmclear_on_cpu(struct cpu_info *ci)
{
if ((ci->ci_flags & CPUF_VMM) && (ci->ci_vmm_flags & CI_VMM_VMX)) {
if (vmclear(&ci->ci_vmcs_pa))
panic("VMCLEAR ipi failed");
atomic_swap_ulong(&ci->ci_vmcs_pa, VMX_VMCS_PA_CLEAR);
}
}
#ifdef MULTIPROCESSOR
static int
vmx_remote_vmclear(struct cpu_info *ci, struct vcpu *vcpu)
{
#ifdef MP_LOCKDEBUG
int nticks = __mp_lock_spinout;
#endif /* MP_LOCKDEBUG */
rw_enter_write(&ci->ci_vmcs_lock);
atomic_swap_ulong(&ci->ci_vmcs_pa, vcpu->vc_control_pa);
x86_send_ipi(ci, X86_IPI_VMCLEAR_VMM);
while (ci->ci_vmcs_pa != VMX_VMCS_PA_CLEAR) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spun out\n", __func__);
db_enter();
nticks = __mp_lock_spinout;
}
#endif /* MP_LOCKDEBUG */
}
atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_CLEARED);
rw_exit_write(&ci->ci_vmcs_lock);
return (0);
}
#endif /* MULTIPROCESSOR */
/*
* vm_create_check_mem_ranges
*
* Make sure that the guest physical memory ranges given by the user process
* do not overlap and are in ascending order.
*
* The last physical address may not exceed VMM_MAX_VM_MEM_SIZE.
*
* Return Values:
* The total memory size in MB if the checks were successful
* 0: One of the memory ranges was invalid, or VMM_MAX_VM_MEM_SIZE was
* exceeded
*/
size_t
vm_create_check_mem_ranges(struct vm_create_params *vcp)
{
size_t i, memsize = 0;
struct vm_mem_range *vmr, *pvmr;
const paddr_t maxgpa = VMM_MAX_VM_MEM_SIZE;
if (vcp->vcp_nmemranges == 0 ||
vcp->vcp_nmemranges > VMM_MAX_MEM_RANGES)
return (0);
for (i = 0; i < vcp->vcp_nmemranges; i++) {
vmr = &vcp->vcp_memranges[i];
/* Only page-aligned addresses and sizes are permitted */
if ((vmr->vmr_gpa & PAGE_MASK) || (vmr->vmr_va & PAGE_MASK) ||
(vmr->vmr_size & PAGE_MASK) || vmr->vmr_size == 0)
return (0);
/* Make sure that VMM_MAX_VM_MEM_SIZE is not exceeded */
if (vmr->vmr_gpa >= maxgpa ||
vmr->vmr_size > maxgpa - vmr->vmr_gpa)
return (0);
/*
* Make sure that all virtual addresses are within the address
* space of the process and that they do not wrap around.
* Calling uvm_share() when creating the VM will take care of
* further checks.
*/
if (vmr->vmr_va < VM_MIN_ADDRESS ||
vmr->vmr_va >= VM_MAXUSER_ADDRESS ||
vmr->vmr_size >= VM_MAXUSER_ADDRESS - vmr->vmr_va)
return (0);
/*
* Specifying ranges within the PCI MMIO space is forbidden.
* Disallow ranges that start inside the MMIO space:
* [VMM_PCI_MMIO_BAR_BASE .. VMM_PCI_MMIO_BAR_END]
*/
if (vmr->vmr_gpa >= VMM_PCI_MMIO_BAR_BASE &&
vmr->vmr_gpa <= VMM_PCI_MMIO_BAR_END)
return (0);
/*
* ... and disallow ranges that end inside the MMIO space:
* (VMM_PCI_MMIO_BAR_BASE .. VMM_PCI_MMIO_BAR_END]
*/
if (vmr->vmr_gpa + vmr->vmr_size > VMM_PCI_MMIO_BAR_BASE &&
vmr->vmr_gpa + vmr->vmr_size <= VMM_PCI_MMIO_BAR_END)
return (0);
/*
* Make sure that guest physical memory ranges do not overlap
* and that they are ascending.
*/
if (i > 0 && pvmr->vmr_gpa + pvmr->vmr_size > vmr->vmr_gpa)
return (0);
memsize += vmr->vmr_size;
pvmr = vmr;
}
if (memsize % (1024 * 1024) != 0)
return (0);
return (memsize);
}
/*
* vm_create
*
* Creates the in-memory VMM structures for the VM defined by 'vcp'. The
* parent of this VM shall be the process defined by 'p'.
* This function does not start the VCPU(s) - see vm_start.
*
* Return Values:
* 0: the create operation was successful
* ENOMEM: out of memory
* various other errors from vcpu_init/vm_impl_init
*/
int
vm_create(struct vm_create_params *vcp, struct proc *p)
{
int i, ret;
size_t memsize;
struct vm *vm;
struct vcpu *vcpu;
if (!(curcpu()->ci_flags & CPUF_VMM))
return (EINVAL);
memsize = vm_create_check_mem_ranges(vcp);
if (memsize == 0)
return (EINVAL);
/* XXX - support UP only (for now) */
if (vcp->vcp_ncpus != 1)
return (EINVAL);
/* Bail early if we're already at vcpu capacity. */
rw_enter_read(&vmm_softc->vm_lock);
if (vmm_softc->vcpu_ct + vcp->vcp_ncpus > vmm_softc->vcpu_max) {
DPRINTF("%s: maximum vcpus (%lu) reached\n", __func__,
vmm_softc->vcpu_max);
rw_exit_read(&vmm_softc->vm_lock);
return (ENOMEM);
}
rw_exit_read(&vmm_softc->vm_lock);
/* Instantiate and configure the new vm. */
vm = pool_get(&vm_pool, PR_WAITOK | PR_ZERO);
refcnt_init(&vm->vm_refcnt); /* Do not release yet. */
SLIST_INIT(&vm->vm_vcpu_list);
rw_init(&vm->vm_vcpu_lock, "vcpu_list");
vm->vm_creator_pid = p->p_p->ps_pid;
vm->vm_nmemranges = vcp->vcp_nmemranges;
memcpy(vm->vm_memranges, vcp->vcp_memranges,
vm->vm_nmemranges * sizeof(vm->vm_memranges[0]));
vm->vm_memory_size = memsize;
strncpy(vm->vm_name, vcp->vcp_name, VMM_MAX_NAME_LEN - 1);
if (vm_impl_init(vm, p)) {
printf("failed to init arch-specific features for vm %p\n", vm);
vm_teardown(&vm);
return (ENOMEM);
}
vm->vm_vcpu_ct = 0;
vm->vm_vcpus_running = 0;
/* Initialize each VCPU defined in 'vcp' */
for (i = 0; i < vcp->vcp_ncpus; i++) {
vcpu = pool_get(&vcpu_pool, PR_WAITOK | PR_ZERO);
refcnt_init(&vcpu->vc_refcnt);
refcnt_rele(&vcpu->vc_refcnt);
vcpu->vc_parent = vm;
if ((ret = vcpu_init(vcpu)) != 0) {
printf("failed to init vcpu %d for vm %p\n", i, vm);
vm_teardown(&vm);
return (ret);
}
vcpu->vc_id = vm->vm_vcpu_ct;
vm->vm_vcpu_ct++;
SLIST_INSERT_HEAD(&vm->vm_vcpu_list, vcpu, vc_vcpu_link);
}
/* XXX init various other hardware parts (vlapic, vioapic, etc) */
/* Attempt to register the vm now that it's configured. */
rw_enter_write(&vmm_softc->vm_lock);
if (vmm_softc->vcpu_ct + vm->vm_vcpu_ct > vmm_softc->vcpu_max) {
/* Someone already took our capacity. */
printf("%s: maximum vcpus (%lu) reached\n", __func__,
vmm_softc->vcpu_max);
rw_exit_write(&vmm_softc->vm_lock);
vm_teardown(&vm);
return (ENOMEM);
}
/* Update the global index and identify the vm. */
vmm_softc->vm_idx++;
vm->vm_id = vmm_softc->vm_idx;
vcp->vcp_id = vm->vm_id;
/* Publish the vm into the list and update list count. */
SLIST_INSERT_HEAD(&vmm_softc->vm_list, vm, vm_link);
vmm_softc->vm_ct++;
vmm_softc->vcpu_ct += vm->vm_vcpu_ct;
refcnt_rele(&vm->vm_refcnt); /* No need for wake. */
rw_exit_write(&vmm_softc->vm_lock);
return (0);
}
/*
* vm_impl_init_vmx
*
* Intel VMX specific VM initialization routine
*
* Parameters:
* vm: the VM being initialized
* p: vmd process owning the VM
*
* Return values:
* 0: the initialization was successful
* ENOMEM: the initialization failed (lack of resources)
*/
int
vm_impl_init_vmx(struct vm *vm, struct proc *p)
{
int i, ret;
vaddr_t mingpa, maxgpa;
struct vm_mem_range *vmr;
/* If not EPT, nothing to do here */
if (vmm_softc->mode != VMM_MODE_EPT)
return (0);
vmr = &vm->vm_memranges[0];
mingpa = vmr->vmr_gpa;
vmr = &vm->vm_memranges[vm->vm_nmemranges - 1];
maxgpa = vmr->vmr_gpa + vmr->vmr_size;
/*
* uvmspace_alloc (currently) always returns a valid vmspace
*/
vm->vm_vmspace = uvmspace_alloc(mingpa, maxgpa, TRUE, FALSE);
vm->vm_map = &vm->vm_vmspace->vm_map;
/* Map the new map with an anon */
DPRINTF("%s: created vm_map @ %p\n", __func__, vm->vm_map);
for (i = 0; i < vm->vm_nmemranges; i++) {
vmr = &vm->vm_memranges[i];
ret = uvm_share(vm->vm_map, vmr->vmr_gpa,
PROT_READ | PROT_WRITE | PROT_EXEC,
&p->p_vmspace->vm_map, vmr->vmr_va, vmr->vmr_size);
if (ret) {
printf("%s: uvm_share failed (%d)\n", __func__, ret);
/* uvmspace_free calls pmap_destroy for us */
uvmspace_free(vm->vm_vmspace);
vm->vm_vmspace = NULL;
return (ENOMEM);
}
}
pmap_convert(vm->vm_map->pmap, PMAP_TYPE_EPT);
return (0);
}
/*
* vm_impl_init_svm
*
* AMD SVM specific VM initialization routine
*
* Parameters:
* vm: the VM being initialized
* p: vmd process owning the VM
*
* Return values:
* 0: the initialization was successful
* ENOMEM: the initialization failed (lack of resources)
*/
int
vm_impl_init_svm(struct vm *vm, struct proc *p)
{
int i, ret;
vaddr_t mingpa, maxgpa;
struct vm_mem_range *vmr;
/* If not RVI, nothing to do here */
if (vmm_softc->mode != VMM_MODE_RVI)
return (0);
vmr = &vm->vm_memranges[0];
mingpa = vmr->vmr_gpa;
vmr = &vm->vm_memranges[vm->vm_nmemranges - 1];
maxgpa = vmr->vmr_gpa + vmr->vmr_size;
/*
* uvmspace_alloc (currently) always returns a valid vmspace
*/
vm->vm_vmspace = uvmspace_alloc(mingpa, maxgpa, TRUE, FALSE);
vm->vm_map = &vm->vm_vmspace->vm_map;
/* Map the new map with an anon */
DPRINTF("%s: created vm_map @ %p\n", __func__, vm->vm_map);
for (i = 0; i < vm->vm_nmemranges; i++) {
vmr = &vm->vm_memranges[i];
ret = uvm_share(vm->vm_map, vmr->vmr_gpa,
PROT_READ | PROT_WRITE | PROT_EXEC,
&p->p_vmspace->vm_map, vmr->vmr_va, vmr->vmr_size);
if (ret) {
printf("%s: uvm_share failed (%d)\n", __func__, ret);
/* uvmspace_free calls pmap_destroy for us */
uvmspace_free(vm->vm_vmspace);
vm->vm_vmspace = NULL;
return (ENOMEM);
}
}
/* Convert pmap to RVI */
pmap_convert(vm->vm_map->pmap, PMAP_TYPE_RVI);
return (0);
}
/*
* vm_impl_init
*
* Calls the architecture-specific VM init routine
*
* Parameters:
* vm: the VM being initialized
* p: vmd process owning the VM
*
* Return values (from architecture-specific init routines):
* 0: the initialization was successful
* ENOMEM: the initialization failed (lack of resources)
*/
int
vm_impl_init(struct vm *vm, struct proc *p)
{
int ret;
KERNEL_LOCK();
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
ret = vm_impl_init_vmx(vm, p);
else if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI)
ret = vm_impl_init_svm(vm, p);
else
panic("%s: unknown vmm mode: %d", __func__, vmm_softc->mode);
KERNEL_UNLOCK();
return (ret);
}
/*
* vm_impl_deinit_vmx
*
* Intel VMX specific VM deinitialization routine
*
* Parameters:
* vm: VM to deinit
*/
void
vm_impl_deinit_vmx(struct vm *vm)
{
/* Unused */
}
/*
* vm_impl_deinit_svm
*
* AMD SVM specific VM deinitialization routine
*
* Parameters:
* vm: VM to deinit
*/
void
vm_impl_deinit_svm(struct vm *vm)
{
/* Unused */
}
/*
* vm_impl_deinit
*
* Calls the architecture-specific VM init routine
*
* Parameters:
* vm: VM to deinit
*/
void
vm_impl_deinit(struct vm *vm)
{
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
vm_impl_deinit_vmx(vm);
else if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI)
vm_impl_deinit_svm(vm);
else
panic("%s: unknown vmm mode: %d", __func__, vmm_softc->mode);
}
/*
* vcpu_reload_vmcs_vmx
*
* (Re)load the VMCS on the current cpu. Must be called with the VMCS write
* lock acquired. If the VMCS is determined to be loaded on a remote cpu, an
* ipi will be used to remotely flush it before loading the VMCS locally.
*
* Parameters:
* vcpu: Pointer to the vcpu needing its VMCS
*
* Return values:
* 0: if successful
* EINVAL: an error occurred during flush or reload
*/
int
vcpu_reload_vmcs_vmx(struct vcpu *vcpu)
{
struct cpu_info *ci, *last_ci;
rw_assert_wrlock(&vcpu->vc_lock);
ci = curcpu();
last_ci = vcpu->vc_last_pcpu;
if (last_ci == NULL) {
/* First launch */
if (vmclear(&vcpu->vc_control_pa))
return (EINVAL);
atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_CLEARED);
#ifdef MULTIPROCESSOR
} else if (last_ci != ci) {
/* We've moved CPUs at some point, so remote VMCLEAR */
if (vmx_remote_vmclear(last_ci, vcpu))
return (EINVAL);
KASSERT(vcpu->vc_vmx_vmcs_state == VMCS_CLEARED);
#endif /* MULTIPROCESSOR */
}
if (vmptrld(&vcpu->vc_control_pa)) {
printf("%s: vmptrld\n", __func__);
return (EINVAL);
}
return (0);
}
/*
* vcpu_readregs_vmx
*
* Reads 'vcpu's registers
*
* Parameters:
* vcpu: the vcpu to read register values from
* regmask: the types of registers to read
* loadvmcs: bit to indicate whether the VMCS has to be loaded first
* vrs: output parameter where register values are stored
*
* Return values:
* 0: if successful
* EINVAL: an error reading registers occurred
*/
int
vcpu_readregs_vmx(struct vcpu *vcpu, uint64_t regmask, int loadvmcs,
struct vcpu_reg_state *vrs)
{
int i, ret = 0;
uint64_t sel, limit, ar;
uint64_t *gprs = vrs->vrs_gprs;
uint64_t *crs = vrs->vrs_crs;
uint64_t *msrs = vrs->vrs_msrs;
uint64_t *drs = vrs->vrs_drs;
struct vcpu_segment_info *sregs = vrs->vrs_sregs;
struct vmx_msr_store *msr_store;
if (loadvmcs) {
if (vcpu_reload_vmcs_vmx(vcpu))
return (EINVAL);
}
#ifdef VMM_DEBUG
/* VMCS should be loaded... */
paddr_t pa = 0ULL;
if (vmptrst(&pa))
panic("%s: vmptrst", __func__);
KASSERT(pa == vcpu->vc_control_pa);
#endif /* VMM_DEBUG */
if (regmask & VM_RWREGS_GPRS) {
gprs[VCPU_REGS_RAX] = vcpu->vc_gueststate.vg_rax;
gprs[VCPU_REGS_RBX] = vcpu->vc_gueststate.vg_rbx;
gprs[VCPU_REGS_RCX] = vcpu->vc_gueststate.vg_rcx;
gprs[VCPU_REGS_RDX] = vcpu->vc_gueststate.vg_rdx;
gprs[VCPU_REGS_RSI] = vcpu->vc_gueststate.vg_rsi;
gprs[VCPU_REGS_RDI] = vcpu->vc_gueststate.vg_rdi;
gprs[VCPU_REGS_R8] = vcpu->vc_gueststate.vg_r8;
gprs[VCPU_REGS_R9] = vcpu->vc_gueststate.vg_r9;
gprs[VCPU_REGS_R10] = vcpu->vc_gueststate.vg_r10;
gprs[VCPU_REGS_R11] = vcpu->vc_gueststate.vg_r11;
gprs[VCPU_REGS_R12] = vcpu->vc_gueststate.vg_r12;
gprs[VCPU_REGS_R13] = vcpu->vc_gueststate.vg_r13;
gprs[VCPU_REGS_R14] = vcpu->vc_gueststate.vg_r14;
gprs[VCPU_REGS_R15] = vcpu->vc_gueststate.vg_r15;
gprs[VCPU_REGS_RBP] = vcpu->vc_gueststate.vg_rbp;
gprs[VCPU_REGS_RIP] = vcpu->vc_gueststate.vg_rip;
if (vmread(VMCS_GUEST_IA32_RSP, &gprs[VCPU_REGS_RSP]))
goto errout;
if (vmread(VMCS_GUEST_IA32_RFLAGS, &gprs[VCPU_REGS_RFLAGS]))
goto errout;
}
if (regmask & VM_RWREGS_SREGS) {
for (i = 0; i < nitems(vmm_vmx_sreg_vmcs_fields); i++) {
if (vmread(vmm_vmx_sreg_vmcs_fields[i].selid, &sel))
goto errout;
if (vmread(vmm_vmx_sreg_vmcs_fields[i].limitid, &limit))
goto errout;
if (vmread(vmm_vmx_sreg_vmcs_fields[i].arid, &ar))
goto errout;
if (vmread(vmm_vmx_sreg_vmcs_fields[i].baseid,
&sregs[i].vsi_base))
goto errout;
sregs[i].vsi_sel = sel;
sregs[i].vsi_limit = limit;
sregs[i].vsi_ar = ar;
}
if (vmread(VMCS_GUEST_IA32_GDTR_LIMIT, &limit))
goto errout;
if (vmread(VMCS_GUEST_IA32_GDTR_BASE,
&vrs->vrs_gdtr.vsi_base))
goto errout;
vrs->vrs_gdtr.vsi_limit = limit;
if (vmread(VMCS_GUEST_IA32_IDTR_LIMIT, &limit))
goto errout;
if (vmread(VMCS_GUEST_IA32_IDTR_BASE,
&vrs->vrs_idtr.vsi_base))
goto errout;
vrs->vrs_idtr.vsi_limit = limit;
}
if (regmask & VM_RWREGS_CRS) {
crs[VCPU_REGS_CR2] = vcpu->vc_gueststate.vg_cr2;
crs[VCPU_REGS_XCR0] = vcpu->vc_gueststate.vg_xcr0;
if (vmread(VMCS_GUEST_IA32_CR0, &crs[VCPU_REGS_CR0]))
goto errout;
if (vmread(VMCS_GUEST_IA32_CR3, &crs[VCPU_REGS_CR3]))
goto errout;
if (vmread(VMCS_GUEST_IA32_CR4, &crs[VCPU_REGS_CR4]))
goto errout;
if (vmread(VMCS_GUEST_PDPTE0, &crs[VCPU_REGS_PDPTE0]))
goto errout;
if (vmread(VMCS_GUEST_PDPTE1, &crs[VCPU_REGS_PDPTE1]))
goto errout;
if (vmread(VMCS_GUEST_PDPTE2, &crs[VCPU_REGS_PDPTE2]))
goto errout;
if (vmread(VMCS_GUEST_PDPTE3, &crs[VCPU_REGS_PDPTE3]))
goto errout;
}
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
if (regmask & VM_RWREGS_MSRS) {
for (i = 0; i < VCPU_REGS_NMSRS; i++) {
msrs[i] = msr_store[i].vms_data;
}
}
if (regmask & VM_RWREGS_DRS) {
drs[VCPU_REGS_DR0] = vcpu->vc_gueststate.vg_dr0;
drs[VCPU_REGS_DR1] = vcpu->vc_gueststate.vg_dr1;
drs[VCPU_REGS_DR2] = vcpu->vc_gueststate.vg_dr2;
drs[VCPU_REGS_DR3] = vcpu->vc_gueststate.vg_dr3;
drs[VCPU_REGS_DR6] = vcpu->vc_gueststate.vg_dr6;
if (vmread(VMCS_GUEST_IA32_DR7, &drs[VCPU_REGS_DR7]))
goto errout;
}
goto out;
errout:
ret = EINVAL;
out:
return (ret);
}
/*
* vcpu_readregs_svm
*
* Reads 'vcpu's registers
*
* Parameters:
* vcpu: the vcpu to read register values from
* regmask: the types of registers to read
* vrs: output parameter where register values are stored
*
* Return values:
* 0: if successful
*/
int
vcpu_readregs_svm(struct vcpu *vcpu, uint64_t regmask,
struct vcpu_reg_state *vrs)
{
uint64_t *gprs = vrs->vrs_gprs;
uint64_t *crs = vrs->vrs_crs;
uint64_t *msrs = vrs->vrs_msrs;
uint64_t *drs = vrs->vrs_drs;
uint32_t attr;
struct vcpu_segment_info *sregs = vrs->vrs_sregs;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
if (regmask & VM_RWREGS_GPRS) {
gprs[VCPU_REGS_RAX] = vcpu->vc_gueststate.vg_rax;
gprs[VCPU_REGS_RBX] = vcpu->vc_gueststate.vg_rbx;
gprs[VCPU_REGS_RCX] = vcpu->vc_gueststate.vg_rcx;
gprs[VCPU_REGS_RDX] = vcpu->vc_gueststate.vg_rdx;
gprs[VCPU_REGS_RSI] = vcpu->vc_gueststate.vg_rsi;
gprs[VCPU_REGS_RDI] = vcpu->vc_gueststate.vg_rdi;
gprs[VCPU_REGS_R8] = vcpu->vc_gueststate.vg_r8;
gprs[VCPU_REGS_R9] = vcpu->vc_gueststate.vg_r9;
gprs[VCPU_REGS_R10] = vcpu->vc_gueststate.vg_r10;
gprs[VCPU_REGS_R11] = vcpu->vc_gueststate.vg_r11;
gprs[VCPU_REGS_R12] = vcpu->vc_gueststate.vg_r12;
gprs[VCPU_REGS_R13] = vcpu->vc_gueststate.vg_r13;
gprs[VCPU_REGS_R14] = vcpu->vc_gueststate.vg_r14;
gprs[VCPU_REGS_R15] = vcpu->vc_gueststate.vg_r15;
gprs[VCPU_REGS_RBP] = vcpu->vc_gueststate.vg_rbp;
gprs[VCPU_REGS_RIP] = vmcb->v_rip;
gprs[VCPU_REGS_RSP] = vmcb->v_rsp;
gprs[VCPU_REGS_RFLAGS] = vmcb->v_rflags;
}
if (regmask & VM_RWREGS_SREGS) {
sregs[VCPU_REGS_CS].vsi_sel = vmcb->v_cs.vs_sel;
sregs[VCPU_REGS_CS].vsi_limit = vmcb->v_cs.vs_lim;
attr = vmcb->v_cs.vs_attr;
sregs[VCPU_REGS_CS].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_CS].vsi_base = vmcb->v_cs.vs_base;
sregs[VCPU_REGS_DS].vsi_sel = vmcb->v_ds.vs_sel;
sregs[VCPU_REGS_DS].vsi_limit = vmcb->v_ds.vs_lim;
attr = vmcb->v_ds.vs_attr;
sregs[VCPU_REGS_DS].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_DS].vsi_base = vmcb->v_ds.vs_base;
sregs[VCPU_REGS_ES].vsi_sel = vmcb->v_es.vs_sel;
sregs[VCPU_REGS_ES].vsi_limit = vmcb->v_es.vs_lim;
attr = vmcb->v_es.vs_attr;
sregs[VCPU_REGS_ES].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_ES].vsi_base = vmcb->v_es.vs_base;
sregs[VCPU_REGS_FS].vsi_sel = vmcb->v_fs.vs_sel;
sregs[VCPU_REGS_FS].vsi_limit = vmcb->v_fs.vs_lim;
attr = vmcb->v_fs.vs_attr;
sregs[VCPU_REGS_FS].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_FS].vsi_base = vmcb->v_fs.vs_base;
sregs[VCPU_REGS_GS].vsi_sel = vmcb->v_gs.vs_sel;
sregs[VCPU_REGS_GS].vsi_limit = vmcb->v_gs.vs_lim;
attr = vmcb->v_gs.vs_attr;
sregs[VCPU_REGS_GS].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_GS].vsi_base = vmcb->v_gs.vs_base;
sregs[VCPU_REGS_SS].vsi_sel = vmcb->v_ss.vs_sel;
sregs[VCPU_REGS_SS].vsi_limit = vmcb->v_ss.vs_lim;
attr = vmcb->v_ss.vs_attr;
sregs[VCPU_REGS_SS].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_SS].vsi_base = vmcb->v_ss.vs_base;
sregs[VCPU_REGS_LDTR].vsi_sel = vmcb->v_ldtr.vs_sel;
sregs[VCPU_REGS_LDTR].vsi_limit = vmcb->v_ldtr.vs_lim;
attr = vmcb->v_ldtr.vs_attr;
sregs[VCPU_REGS_LDTR].vsi_ar = (attr & 0xff) | ((attr << 4)
& 0xf000);
sregs[VCPU_REGS_LDTR].vsi_base = vmcb->v_ldtr.vs_base;
sregs[VCPU_REGS_TR].vsi_sel = vmcb->v_tr.vs_sel;
sregs[VCPU_REGS_TR].vsi_limit = vmcb->v_tr.vs_lim;
attr = vmcb->v_tr.vs_attr;
sregs[VCPU_REGS_TR].vsi_ar = (attr & 0xff) | ((attr << 4) &
0xf000);
sregs[VCPU_REGS_TR].vsi_base = vmcb->v_tr.vs_base;
vrs->vrs_gdtr.vsi_limit = vmcb->v_gdtr.vs_lim;
vrs->vrs_gdtr.vsi_base = vmcb->v_gdtr.vs_base;
vrs->vrs_idtr.vsi_limit = vmcb->v_idtr.vs_lim;
vrs->vrs_idtr.vsi_base = vmcb->v_idtr.vs_base;
}
if (regmask & VM_RWREGS_CRS) {
crs[VCPU_REGS_CR0] = vmcb->v_cr0;
crs[VCPU_REGS_CR3] = vmcb->v_cr3;
crs[VCPU_REGS_CR4] = vmcb->v_cr4;
crs[VCPU_REGS_CR2] = vcpu->vc_gueststate.vg_cr2;
crs[VCPU_REGS_XCR0] = vcpu->vc_gueststate.vg_xcr0;
}
if (regmask & VM_RWREGS_MSRS) {
msrs[VCPU_REGS_EFER] = vmcb->v_efer;
msrs[VCPU_REGS_STAR] = vmcb->v_star;
msrs[VCPU_REGS_LSTAR] = vmcb->v_lstar;
msrs[VCPU_REGS_CSTAR] = vmcb->v_cstar;
msrs[VCPU_REGS_SFMASK] = vmcb->v_sfmask;
msrs[VCPU_REGS_KGSBASE] = vmcb->v_kgsbase;
}
if (regmask & VM_RWREGS_DRS) {
drs[VCPU_REGS_DR0] = vcpu->vc_gueststate.vg_dr0;
drs[VCPU_REGS_DR1] = vcpu->vc_gueststate.vg_dr1;
drs[VCPU_REGS_DR2] = vcpu->vc_gueststate.vg_dr2;
drs[VCPU_REGS_DR3] = vcpu->vc_gueststate.vg_dr3;
drs[VCPU_REGS_DR6] = vmcb->v_dr6;
drs[VCPU_REGS_DR7] = vmcb->v_dr7;
}
return (0);
}
/*
* vcpu_writeregs_vmx
*
* Writes VCPU registers
*
* Parameters:
* vcpu: the vcpu that has to get its registers written to
* regmask: the types of registers to write
* loadvmcs: bit to indicate whether the VMCS has to be loaded first
* vrs: the register values to write
*
* Return values:
* 0: if successful
* EINVAL an error writing registers occurred
*/
int
vcpu_writeregs_vmx(struct vcpu *vcpu, uint64_t regmask, int loadvmcs,
struct vcpu_reg_state *vrs)
{
int i, ret = 0;
uint16_t sel;
uint64_t limit, ar;
uint64_t *gprs = vrs->vrs_gprs;
uint64_t *crs = vrs->vrs_crs;
uint64_t *msrs = vrs->vrs_msrs;
uint64_t *drs = vrs->vrs_drs;
struct vcpu_segment_info *sregs = vrs->vrs_sregs;
struct vmx_msr_store *msr_store;
if (loadvmcs) {
if (vcpu_reload_vmcs_vmx(vcpu))
return (EINVAL);
}
#ifdef VMM_DEBUG
/* VMCS should be loaded... */
paddr_t pa = 0ULL;
if (vmptrst(&pa))
panic("%s: vmptrst", __func__);
KASSERT(pa == vcpu->vc_control_pa);
#endif /* VMM_DEBUG */
if (regmask & VM_RWREGS_GPRS) {
vcpu->vc_gueststate.vg_rax = gprs[VCPU_REGS_RAX];
vcpu->vc_gueststate.vg_rbx = gprs[VCPU_REGS_RBX];
vcpu->vc_gueststate.vg_rcx = gprs[VCPU_REGS_RCX];
vcpu->vc_gueststate.vg_rdx = gprs[VCPU_REGS_RDX];
vcpu->vc_gueststate.vg_rsi = gprs[VCPU_REGS_RSI];
vcpu->vc_gueststate.vg_rdi = gprs[VCPU_REGS_RDI];
vcpu->vc_gueststate.vg_r8 = gprs[VCPU_REGS_R8];
vcpu->vc_gueststate.vg_r9 = gprs[VCPU_REGS_R9];
vcpu->vc_gueststate.vg_r10 = gprs[VCPU_REGS_R10];
vcpu->vc_gueststate.vg_r11 = gprs[VCPU_REGS_R11];
vcpu->vc_gueststate.vg_r12 = gprs[VCPU_REGS_R12];
vcpu->vc_gueststate.vg_r13 = gprs[VCPU_REGS_R13];
vcpu->vc_gueststate.vg_r14 = gprs[VCPU_REGS_R14];
vcpu->vc_gueststate.vg_r15 = gprs[VCPU_REGS_R15];
vcpu->vc_gueststate.vg_rbp = gprs[VCPU_REGS_RBP];
vcpu->vc_gueststate.vg_rip = gprs[VCPU_REGS_RIP];
if (vmwrite(VMCS_GUEST_IA32_RIP, gprs[VCPU_REGS_RIP]))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_RSP, gprs[VCPU_REGS_RSP]))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_RFLAGS, gprs[VCPU_REGS_RFLAGS]))
goto errout;
}
if (regmask & VM_RWREGS_SREGS) {
for (i = 0; i < nitems(vmm_vmx_sreg_vmcs_fields); i++) {
sel = sregs[i].vsi_sel;
limit = sregs[i].vsi_limit;
ar = sregs[i].vsi_ar;
if (vmwrite(vmm_vmx_sreg_vmcs_fields[i].selid, sel))
goto errout;
if (vmwrite(vmm_vmx_sreg_vmcs_fields[i].limitid, limit))
goto errout;
if (vmwrite(vmm_vmx_sreg_vmcs_fields[i].arid, ar))
goto errout;
if (vmwrite(vmm_vmx_sreg_vmcs_fields[i].baseid,
sregs[i].vsi_base))
goto errout;
}
if (vmwrite(VMCS_GUEST_IA32_GDTR_LIMIT,
vrs->vrs_gdtr.vsi_limit))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_GDTR_BASE,
vrs->vrs_gdtr.vsi_base))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_IDTR_LIMIT,
vrs->vrs_idtr.vsi_limit))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_IDTR_BASE,
vrs->vrs_idtr.vsi_base))
goto errout;
}
if (regmask & VM_RWREGS_CRS) {
vcpu->vc_gueststate.vg_xcr0 = crs[VCPU_REGS_XCR0];
if (vmwrite(VMCS_GUEST_IA32_CR0, crs[VCPU_REGS_CR0]))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_CR3, crs[VCPU_REGS_CR3]))
goto errout;
if (vmwrite(VMCS_GUEST_IA32_CR4, crs[VCPU_REGS_CR4]))
goto errout;
if (vmwrite(VMCS_GUEST_PDPTE0, crs[VCPU_REGS_PDPTE0]))
goto errout;
if (vmwrite(VMCS_GUEST_PDPTE1, crs[VCPU_REGS_PDPTE1]))
goto errout;
if (vmwrite(VMCS_GUEST_PDPTE2, crs[VCPU_REGS_PDPTE2]))
goto errout;
if (vmwrite(VMCS_GUEST_PDPTE3, crs[VCPU_REGS_PDPTE3]))
goto errout;
}
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
if (regmask & VM_RWREGS_MSRS) {
for (i = 0; i < VCPU_REGS_NMSRS; i++) {
msr_store[i].vms_data = msrs[i];
}
}
if (regmask & VM_RWREGS_DRS) {
vcpu->vc_gueststate.vg_dr0 = drs[VCPU_REGS_DR0];
vcpu->vc_gueststate.vg_dr1 = drs[VCPU_REGS_DR1];
vcpu->vc_gueststate.vg_dr2 = drs[VCPU_REGS_DR2];
vcpu->vc_gueststate.vg_dr3 = drs[VCPU_REGS_DR3];
vcpu->vc_gueststate.vg_dr6 = drs[VCPU_REGS_DR6];
if (vmwrite(VMCS_GUEST_IA32_DR7, drs[VCPU_REGS_DR7]))
goto errout;
}
goto out;
errout:
ret = EINVAL;
out:
if (loadvmcs) {
if (vmclear(&vcpu->vc_control_pa))
ret = EINVAL;
atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_CLEARED);
}
return (ret);
}
/*
* vcpu_writeregs_svm
*
* Writes 'vcpu's registers
*
* Parameters:
* vcpu: the vcpu that has to get its registers written to
* regmask: the types of registers to write
* vrs: the register values to write
*
* Return values:
* 0: if successful
* EINVAL an error writing registers occurred
*/
int
vcpu_writeregs_svm(struct vcpu *vcpu, uint64_t regmask,
struct vcpu_reg_state *vrs)
{
uint64_t *gprs = vrs->vrs_gprs;
uint64_t *crs = vrs->vrs_crs;
uint16_t attr;
uint64_t *msrs = vrs->vrs_msrs;
uint64_t *drs = vrs->vrs_drs;
struct vcpu_segment_info *sregs = vrs->vrs_sregs;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
if (regmask & VM_RWREGS_GPRS) {
vcpu->vc_gueststate.vg_rax = gprs[VCPU_REGS_RAX];
vcpu->vc_gueststate.vg_rbx = gprs[VCPU_REGS_RBX];
vcpu->vc_gueststate.vg_rcx = gprs[VCPU_REGS_RCX];
vcpu->vc_gueststate.vg_rdx = gprs[VCPU_REGS_RDX];
vcpu->vc_gueststate.vg_rsi = gprs[VCPU_REGS_RSI];
vcpu->vc_gueststate.vg_rdi = gprs[VCPU_REGS_RDI];
vcpu->vc_gueststate.vg_r8 = gprs[VCPU_REGS_R8];
vcpu->vc_gueststate.vg_r9 = gprs[VCPU_REGS_R9];
vcpu->vc_gueststate.vg_r10 = gprs[VCPU_REGS_R10];
vcpu->vc_gueststate.vg_r11 = gprs[VCPU_REGS_R11];
vcpu->vc_gueststate.vg_r12 = gprs[VCPU_REGS_R12];
vcpu->vc_gueststate.vg_r13 = gprs[VCPU_REGS_R13];
vcpu->vc_gueststate.vg_r14 = gprs[VCPU_REGS_R14];
vcpu->vc_gueststate.vg_r15 = gprs[VCPU_REGS_R15];
vcpu->vc_gueststate.vg_rbp = gprs[VCPU_REGS_RBP];
vcpu->vc_gueststate.vg_rip = gprs[VCPU_REGS_RIP];
vmcb->v_rip = gprs[VCPU_REGS_RIP];
vmcb->v_rsp = gprs[VCPU_REGS_RSP];
vmcb->v_rflags = gprs[VCPU_REGS_RFLAGS];
}
if (regmask & VM_RWREGS_SREGS) {
vmcb->v_cs.vs_sel = sregs[VCPU_REGS_CS].vsi_sel;
vmcb->v_cs.vs_lim = sregs[VCPU_REGS_CS].vsi_limit;
attr = sregs[VCPU_REGS_CS].vsi_ar;
vmcb->v_cs.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_cs.vs_base = sregs[VCPU_REGS_CS].vsi_base;
vmcb->v_ds.vs_sel = sregs[VCPU_REGS_DS].vsi_sel;
vmcb->v_ds.vs_lim = sregs[VCPU_REGS_DS].vsi_limit;
attr = sregs[VCPU_REGS_DS].vsi_ar;
vmcb->v_ds.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_ds.vs_base = sregs[VCPU_REGS_DS].vsi_base;
vmcb->v_es.vs_sel = sregs[VCPU_REGS_ES].vsi_sel;
vmcb->v_es.vs_lim = sregs[VCPU_REGS_ES].vsi_limit;
attr = sregs[VCPU_REGS_ES].vsi_ar;
vmcb->v_es.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_es.vs_base = sregs[VCPU_REGS_ES].vsi_base;
vmcb->v_fs.vs_sel = sregs[VCPU_REGS_FS].vsi_sel;
vmcb->v_fs.vs_lim = sregs[VCPU_REGS_FS].vsi_limit;
attr = sregs[VCPU_REGS_FS].vsi_ar;
vmcb->v_fs.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_fs.vs_base = sregs[VCPU_REGS_FS].vsi_base;
vmcb->v_gs.vs_sel = sregs[VCPU_REGS_GS].vsi_sel;
vmcb->v_gs.vs_lim = sregs[VCPU_REGS_GS].vsi_limit;
attr = sregs[VCPU_REGS_GS].vsi_ar;
vmcb->v_gs.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_gs.vs_base = sregs[VCPU_REGS_GS].vsi_base;
vmcb->v_ss.vs_sel = sregs[VCPU_REGS_SS].vsi_sel;
vmcb->v_ss.vs_lim = sregs[VCPU_REGS_SS].vsi_limit;
attr = sregs[VCPU_REGS_SS].vsi_ar;
vmcb->v_ss.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_ss.vs_base = sregs[VCPU_REGS_SS].vsi_base;
vmcb->v_ldtr.vs_sel = sregs[VCPU_REGS_LDTR].vsi_sel;
vmcb->v_ldtr.vs_lim = sregs[VCPU_REGS_LDTR].vsi_limit;
attr = sregs[VCPU_REGS_LDTR].vsi_ar;
vmcb->v_ldtr.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_ldtr.vs_base = sregs[VCPU_REGS_LDTR].vsi_base;
vmcb->v_tr.vs_sel = sregs[VCPU_REGS_TR].vsi_sel;
vmcb->v_tr.vs_lim = sregs[VCPU_REGS_TR].vsi_limit;
attr = sregs[VCPU_REGS_TR].vsi_ar;
vmcb->v_tr.vs_attr = (attr & 0xff) | ((attr >> 4) & 0xf00);
vmcb->v_tr.vs_base = sregs[VCPU_REGS_TR].vsi_base;
vmcb->v_gdtr.vs_lim = vrs->vrs_gdtr.vsi_limit;
vmcb->v_gdtr.vs_base = vrs->vrs_gdtr.vsi_base;
vmcb->v_idtr.vs_lim = vrs->vrs_idtr.vsi_limit;
vmcb->v_idtr.vs_base = vrs->vrs_idtr.vsi_base;
}
if (regmask & VM_RWREGS_CRS) {
vmcb->v_cr0 = crs[VCPU_REGS_CR0];
vmcb->v_cr3 = crs[VCPU_REGS_CR3];
vmcb->v_cr4 = crs[VCPU_REGS_CR4];
vcpu->vc_gueststate.vg_cr2 = crs[VCPU_REGS_CR2];
vcpu->vc_gueststate.vg_xcr0 = crs[VCPU_REGS_XCR0];
}
if (regmask & VM_RWREGS_MSRS) {
vmcb->v_efer |= msrs[VCPU_REGS_EFER];
vmcb->v_star = msrs[VCPU_REGS_STAR];
vmcb->v_lstar = msrs[VCPU_REGS_LSTAR];
vmcb->v_cstar = msrs[VCPU_REGS_CSTAR];
vmcb->v_sfmask = msrs[VCPU_REGS_SFMASK];
vmcb->v_kgsbase = msrs[VCPU_REGS_KGSBASE];
}
if (regmask & VM_RWREGS_DRS) {
vcpu->vc_gueststate.vg_dr0 = drs[VCPU_REGS_DR0];
vcpu->vc_gueststate.vg_dr1 = drs[VCPU_REGS_DR1];
vcpu->vc_gueststate.vg_dr2 = drs[VCPU_REGS_DR2];
vcpu->vc_gueststate.vg_dr3 = drs[VCPU_REGS_DR3];
vmcb->v_dr6 = drs[VCPU_REGS_DR6];
vmcb->v_dr7 = drs[VCPU_REGS_DR7];
}
return (0);
}
/*
* vcpu_reset_regs_svm
*
* Initializes 'vcpu's registers to supplied state
*
* Parameters:
* vcpu: the vcpu whose register state is to be initialized
* vrs: the register state to set
*
* Return values:
* 0: registers init'ed successfully
* EINVAL: an error occurred setting register state
*/
int
vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
{
struct vmcb *vmcb;
int ret;
uint16_t asid;
vmcb = (struct vmcb *)vcpu->vc_control_va;
/*
* Intercept controls
*
* External Interrupt exiting (SVM_INTERCEPT_INTR)
* External NMI exiting (SVM_INTERCEPT_NMI)
* CPUID instruction (SVM_INTERCEPT_CPUID)
* HLT instruction (SVM_INTERCEPT_HLT)
* I/O instructions (SVM_INTERCEPT_INOUT)
* MSR access (SVM_INTERCEPT_MSR)
* shutdown events (SVM_INTERCEPT_SHUTDOWN)
*
* VMRUN instruction (SVM_INTERCEPT_VMRUN)
* VMMCALL instruction (SVM_INTERCEPT_VMMCALL)
* VMLOAD instruction (SVM_INTERCEPT_VMLOAD)
* VMSAVE instruction (SVM_INTERCEPT_VMSAVE)
* STGI instruction (SVM_INTERCEPT_STGI)
* CLGI instruction (SVM_INTERCEPT_CLGI)
* SKINIT instruction (SVM_INTERCEPT_SKINIT)
* ICEBP instruction (SVM_INTERCEPT_ICEBP)
* MWAIT instruction (SVM_INTERCEPT_MWAIT_UNCOND)
* MWAIT instruction (SVM_INTERCEPT_MWAIT_COND)
* MONITOR instruction (SVM_INTERCEPT_MONITOR)
* RDTSCP instruction (SVM_INTERCEPT_RDTSCP)
* INVLPGA instruction (SVM_INTERCEPT_INVLPGA)
* XSETBV instruction (SVM_INTERCEPT_XSETBV) (if available)
*/
vmcb->v_intercept1 = SVM_INTERCEPT_INTR | SVM_INTERCEPT_NMI |
SVM_INTERCEPT_CPUID | SVM_INTERCEPT_HLT | SVM_INTERCEPT_INOUT |
SVM_INTERCEPT_MSR | SVM_INTERCEPT_SHUTDOWN;
vmcb->v_intercept2 = SVM_INTERCEPT_VMRUN | SVM_INTERCEPT_VMMCALL |
SVM_INTERCEPT_VMLOAD | SVM_INTERCEPT_VMSAVE | SVM_INTERCEPT_STGI |
SVM_INTERCEPT_CLGI | SVM_INTERCEPT_SKINIT | SVM_INTERCEPT_ICEBP |
SVM_INTERCEPT_MWAIT_UNCOND | SVM_INTERCEPT_MONITOR |
SVM_INTERCEPT_MWAIT_COND | SVM_INTERCEPT_RDTSCP |
SVM_INTERCEPT_INVLPGA;
if (xsave_mask)
vmcb->v_intercept2 |= SVM_INTERCEPT_XSETBV;
/* Setup I/O bitmap */
memset((uint8_t *)vcpu->vc_svm_ioio_va, 0xFF, 3 * PAGE_SIZE);
vmcb->v_iopm_pa = (uint64_t)(vcpu->vc_svm_ioio_pa);
/* Setup MSR bitmap */
memset((uint8_t *)vcpu->vc_msr_bitmap_va, 0xFF, 2 * PAGE_SIZE);
vmcb->v_msrpm_pa = (uint64_t)(vcpu->vc_msr_bitmap_pa);
svm_setmsrbrw(vcpu, MSR_IA32_FEATURE_CONTROL);
svm_setmsrbrw(vcpu, MSR_SYSENTER_CS);
svm_setmsrbrw(vcpu, MSR_SYSENTER_ESP);
svm_setmsrbrw(vcpu, MSR_SYSENTER_EIP);
svm_setmsrbrw(vcpu, MSR_STAR);
svm_setmsrbrw(vcpu, MSR_LSTAR);
svm_setmsrbrw(vcpu, MSR_CSTAR);
svm_setmsrbrw(vcpu, MSR_SFMASK);
svm_setmsrbrw(vcpu, MSR_FSBASE);
svm_setmsrbrw(vcpu, MSR_GSBASE);
svm_setmsrbrw(vcpu, MSR_KERNELGSBASE);
/* EFER is R/O so we can ensure the guest always has SVME */
svm_setmsrbr(vcpu, MSR_EFER);
/* allow reading TSC */
svm_setmsrbr(vcpu, MSR_TSC);
/* Guest VCPU ASID */
if (vmm_alloc_vpid(&asid)) {
DPRINTF("%s: could not allocate asid\n", __func__);
ret = EINVAL;
goto exit;
}
vmcb->v_asid = asid;
vcpu->vc_vpid = asid;
/* TLB Control - First time in, flush all*/
vmcb->v_tlb_control = SVM_TLB_CONTROL_FLUSH_ALL;
/* INTR masking */
vmcb->v_intr_masking = 1;
/* PAT */
vmcb->v_g_pat = PATENTRY(0, PAT_WB) | PATENTRY(1, PAT_WC) |
PATENTRY(2, PAT_UCMINUS) | PATENTRY(3, PAT_UC) |
PATENTRY(4, PAT_WB) | PATENTRY(5, PAT_WC) |
PATENTRY(6, PAT_UCMINUS) | PATENTRY(7, PAT_UC);
/* NPT */
if (vmm_softc->mode == VMM_MODE_RVI) {
vmcb->v_np_enable = 1;
vmcb->v_n_cr3 = vcpu->vc_parent->vm_map->pmap->pm_pdirpa;
}
/* Enable SVME in EFER (must always be set) */
vmcb->v_efer |= EFER_SVME;
ret = vcpu_writeregs_svm(vcpu, VM_RWREGS_ALL, vrs);
/* xcr0 power on default sets bit 0 (x87 state) */
vcpu->vc_gueststate.vg_xcr0 = XCR0_X87 & xsave_mask;
vcpu->vc_parent->vm_map->pmap->eptp = 0;
exit:
return ret;
}
/*
* svm_setmsrbr
*
* Allow read access to the specified msr on the supplied vcpu.
*
* Parameters:
* vcpu: the VCPU to allow access
* msr: the MSR number to allow access to
*/
void
svm_setmsrbr(struct vcpu *vcpu, uint32_t msr)
{
uint8_t *msrs;
uint16_t idx;
msrs = (uint8_t *)vcpu->vc_msr_bitmap_va;
/*
* MSR Read bitmap layout:
* Pentium MSRs (0x0 - 0x1fff) @ 0x0
* Gen6 and Syscall MSRs (0xc0000000 - 0xc0001fff) @ 0x800
* Gen7 and Gen8 MSRs (0xc0010000 - 0xc0011fff) @ 0x1000
*
* Read enable bit is low order bit of 2-bit pair
* per MSR (eg, MSR 0x0 write bit is at bit 0 @ 0x0)
*/
if (msr <= 0x1fff) {
idx = SVM_MSRIDX(msr);
msrs[idx] &= ~(SVM_MSRBIT_R(msr));
} else if (msr >= 0xc0000000 && msr <= 0xc0001fff) {
idx = SVM_MSRIDX(msr - 0xc0000000) + 0x800;
msrs[idx] &= ~(SVM_MSRBIT_R(msr - 0xc0000000));
} else if (msr >= 0xc0010000 && msr <= 0xc0011fff) {
idx = SVM_MSRIDX(msr - 0xc0010000) + 0x1000;
msrs[idx] &= ~(SVM_MSRBIT_R(msr - 0xc0010000));
} else {
printf("%s: invalid msr 0x%x\n", __func__, msr);
return;
}
}
/*
* svm_setmsrbw
*
* Allow write access to the specified msr on the supplied vcpu
*
* Parameters:
* vcpu: the VCPU to allow access
* msr: the MSR number to allow access to
*/
void
svm_setmsrbw(struct vcpu *vcpu, uint32_t msr)
{
uint8_t *msrs;
uint16_t idx;
msrs = (uint8_t *)vcpu->vc_msr_bitmap_va;
/*
* MSR Write bitmap layout:
* Pentium MSRs (0x0 - 0x1fff) @ 0x0
* Gen6 and Syscall MSRs (0xc0000000 - 0xc0001fff) @ 0x800
* Gen7 and Gen8 MSRs (0xc0010000 - 0xc0011fff) @ 0x1000
*
* Write enable bit is high order bit of 2-bit pair
* per MSR (eg, MSR 0x0 write bit is at bit 1 @ 0x0)
*/
if (msr <= 0x1fff) {
idx = SVM_MSRIDX(msr);
msrs[idx] &= ~(SVM_MSRBIT_W(msr));
} else if (msr >= 0xc0000000 && msr <= 0xc0001fff) {
idx = SVM_MSRIDX(msr - 0xc0000000) + 0x800;
msrs[idx] &= ~(SVM_MSRBIT_W(msr - 0xc0000000));
} else if (msr >= 0xc0010000 && msr <= 0xc0011fff) {
idx = SVM_MSRIDX(msr - 0xc0010000) + 0x1000;
msrs[idx] &= ~(SVM_MSRBIT_W(msr - 0xc0010000));
} else {
printf("%s: invalid msr 0x%x\n", __func__, msr);
return;
}
}
/*
* svm_setmsrbrw
*
* Allow read/write access to the specified msr on the supplied vcpu
*
* Parameters:
* vcpu: the VCPU to allow access
* msr: the MSR number to allow access to
*/
void
svm_setmsrbrw(struct vcpu *vcpu, uint32_t msr)
{
svm_setmsrbr(vcpu, msr);
svm_setmsrbw(vcpu, msr);
}
/*
* vmx_setmsrbr
*
* Allow read access to the specified msr on the supplied vcpu.
*
* Parameters:
* vcpu: the VCPU to allow access
* msr: the MSR number to allow access to
*/
void
vmx_setmsrbr(struct vcpu *vcpu, uint32_t msr)
{
uint8_t *msrs;
uint16_t idx;
msrs = (uint8_t *)vcpu->vc_msr_bitmap_va;
/*
* MSR Read bitmap layout:
* "Low" MSRs (0x0 - 0x1fff) @ 0x0
* "High" MSRs (0xc0000000 - 0xc0001fff) @ 0x400
*/
if (msr <= 0x1fff) {
idx = VMX_MSRIDX(msr);
msrs[idx] &= ~(VMX_MSRBIT(msr));
} else if (msr >= 0xc0000000 && msr <= 0xc0001fff) {
idx = VMX_MSRIDX(msr - 0xc0000000) + 0x400;
msrs[idx] &= ~(VMX_MSRBIT(msr - 0xc0000000));
} else
printf("%s: invalid msr 0x%x\n", __func__, msr);
}
/*
* vmx_setmsrbw
*
* Allow write access to the specified msr on the supplied vcpu
*
* Parameters:
* vcpu: the VCPU to allow access
* msr: the MSR number to allow access to
*/
void
vmx_setmsrbw(struct vcpu *vcpu, uint32_t msr)
{
uint8_t *msrs;
uint16_t idx;
msrs = (uint8_t *)vcpu->vc_msr_bitmap_va;
/*
* MSR Write bitmap layout:
* "Low" MSRs (0x0 - 0x1fff) @ 0x800
* "High" MSRs (0xc0000000 - 0xc0001fff) @ 0xc00
*/
if (msr <= 0x1fff) {
idx = VMX_MSRIDX(msr) + 0x800;
msrs[idx] &= ~(VMX_MSRBIT(msr));
} else if (msr >= 0xc0000000 && msr <= 0xc0001fff) {
idx = VMX_MSRIDX(msr - 0xc0000000) + 0xc00;
msrs[idx] &= ~(VMX_MSRBIT(msr - 0xc0000000));
} else
printf("%s: invalid msr 0x%x\n", __func__, msr);
}
/*
* vmx_setmsrbrw
*
* Allow read/write access to the specified msr on the supplied vcpu
*
* Parameters:
* vcpu: the VCPU to allow access
* msr: the MSR number to allow access to
*/
void
vmx_setmsrbrw(struct vcpu *vcpu, uint32_t msr)
{
vmx_setmsrbr(vcpu, msr);
vmx_setmsrbw(vcpu, msr);
}
/*
* svm_set_clean
*
* Sets (mark as unmodified) the VMCB clean bit set in 'value'.
* For example, to set the clean bit for the VMCB intercepts (bit position 0),
* the caller provides 'SVM_CLEANBITS_I' (0x1) for the 'value' argument.
* Multiple cleanbits can be provided in 'value' at the same time (eg,
* "SVM_CLEANBITS_I | SVM_CLEANBITS_TPR").
*
* Note that this function does not clear any bits; to clear bits in the
* vmcb cleanbits bitfield, use 'svm_set_dirty'.
*
* Parameters:
* vmcs: the VCPU whose VMCB clean value should be set
* value: the value(s) to enable in the cleanbits mask
*/
void
svm_set_clean(struct vcpu *vcpu, uint32_t value)
{
struct vmcb *vmcb;
/* If no cleanbits support, do nothing */
if (!curcpu()->ci_vmm_cap.vcc_svm.svm_vmcb_clean)
return;
vmcb = (struct vmcb *)vcpu->vc_control_va;
vmcb->v_vmcb_clean_bits |= value;
}
/*
* svm_set_dirty
*
* Clears (mark as modified) the VMCB clean bit set in 'value'.
* For example, to clear the bit for the VMCB intercepts (bit position 0)
* the caller provides 'SVM_CLEANBITS_I' (0x1) for the 'value' argument.
* Multiple dirty bits can be provided in 'value' at the same time (eg,
* "SVM_CLEANBITS_I | SVM_CLEANBITS_TPR").
*
* Parameters:
* vmcs: the VCPU whose VMCB dirty value should be set
* value: the value(s) to dirty in the cleanbits mask
*/
void
svm_set_dirty(struct vcpu *vcpu, uint32_t value)
{
struct vmcb *vmcb;
/* If no cleanbits support, do nothing */
if (!curcpu()->ci_vmm_cap.vcc_svm.svm_vmcb_clean)
return;
vmcb = (struct vmcb *)vcpu->vc_control_va;
vmcb->v_vmcb_clean_bits &= ~value;
}
/*
* vcpu_reset_regs_vmx
*
* Initializes 'vcpu's registers to supplied state
*
* Parameters:
* vcpu: the vcpu whose register state is to be initialized
* vrs: the register state to set
*
* Return values:
* 0: registers init'ed successfully
* EINVAL: an error occurred setting register state
*/
int
vcpu_reset_regs_vmx(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
{
int ret = 0, ug = 0;
uint32_t cr0, cr4;
uint32_t pinbased, procbased, procbased2, exit, entry;
uint32_t want1, want0;
uint64_t ctrlval, cr3;
uint16_t ctrl, vpid;
struct vmx_msr_store *msr_store;
rw_assert_wrlock(&vcpu->vc_lock);
cr0 = vrs->vrs_crs[VCPU_REGS_CR0];
if (vcpu_reload_vmcs_vmx(vcpu)) {
DPRINTF("%s: error reloading VMCS\n", __func__);
ret = EINVAL;
goto exit;
}
#ifdef VMM_DEBUG
/* VMCS should be loaded... */
paddr_t pa = 0ULL;
if (vmptrst(&pa))
panic("%s: vmptrst", __func__);
KASSERT(pa == vcpu->vc_control_pa);
#endif /* VMM_DEBUG */
/* Compute Basic Entry / Exit Controls */
vcpu->vc_vmx_basic = rdmsr(IA32_VMX_BASIC);
vcpu->vc_vmx_entry_ctls = rdmsr(IA32_VMX_ENTRY_CTLS);
vcpu->vc_vmx_exit_ctls = rdmsr(IA32_VMX_EXIT_CTLS);
vcpu->vc_vmx_pinbased_ctls = rdmsr(IA32_VMX_PINBASED_CTLS);
vcpu->vc_vmx_procbased_ctls = rdmsr(IA32_VMX_PROCBASED_CTLS);
/* Compute True Entry / Exit Controls (if applicable) */
if (vcpu->vc_vmx_basic & IA32_VMX_TRUE_CTLS_AVAIL) {
vcpu->vc_vmx_true_entry_ctls = rdmsr(IA32_VMX_TRUE_ENTRY_CTLS);
vcpu->vc_vmx_true_exit_ctls = rdmsr(IA32_VMX_TRUE_EXIT_CTLS);
vcpu->vc_vmx_true_pinbased_ctls =
rdmsr(IA32_VMX_TRUE_PINBASED_CTLS);
vcpu->vc_vmx_true_procbased_ctls =
rdmsr(IA32_VMX_TRUE_PROCBASED_CTLS);
}
/* Compute Secondary Procbased Controls (if applicable) */
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1))
vcpu->vc_vmx_procbased2_ctls = rdmsr(IA32_VMX_PROCBASED2_CTLS);
/*
* Pinbased ctrls
*
* We must be able to set the following:
* IA32_VMX_EXTERNAL_INT_EXITING - exit on host interrupt
* IA32_VMX_NMI_EXITING - exit on host NMI
*/
want1 = IA32_VMX_EXTERNAL_INT_EXITING |
IA32_VMX_NMI_EXITING;
want0 = 0;
if (vcpu->vc_vmx_basic & IA32_VMX_TRUE_CTLS_AVAIL) {
ctrl = IA32_VMX_TRUE_PINBASED_CTLS;
ctrlval = vcpu->vc_vmx_true_pinbased_ctls;
} else {
ctrl = IA32_VMX_PINBASED_CTLS;
ctrlval = vcpu->vc_vmx_pinbased_ctls;
}
if (vcpu_vmx_compute_ctrl(ctrlval, ctrl, want1, want0, &pinbased)) {
DPRINTF("%s: error computing pinbased controls\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_PINBASED_CTLS, pinbased)) {
DPRINTF("%s: error setting pinbased controls\n", __func__);
ret = EINVAL;
goto exit;
}
/*
* Procbased ctrls
*
* We must be able to set the following:
* IA32_VMX_HLT_EXITING - exit on HLT instruction
* IA32_VMX_MWAIT_EXITING - exit on MWAIT instruction
* IA32_VMX_UNCONDITIONAL_IO_EXITING - exit on I/O instructions
* IA32_VMX_USE_MSR_BITMAPS - exit on various MSR accesses
* IA32_VMX_CR8_LOAD_EXITING - guest TPR access
* IA32_VMX_CR8_STORE_EXITING - guest TPR access
* IA32_VMX_USE_TPR_SHADOW - guest TPR access (shadow)
* IA32_VMX_MONITOR_EXITING - exit on MONITOR instruction
*
* If we have EPT, we must be able to clear the following
* IA32_VMX_CR3_LOAD_EXITING - don't care about guest CR3 accesses
* IA32_VMX_CR3_STORE_EXITING - don't care about guest CR3 accesses
*/
want1 = IA32_VMX_HLT_EXITING |
IA32_VMX_MWAIT_EXITING |
IA32_VMX_UNCONDITIONAL_IO_EXITING |
IA32_VMX_USE_MSR_BITMAPS |
IA32_VMX_CR8_LOAD_EXITING |
IA32_VMX_CR8_STORE_EXITING |
IA32_VMX_MONITOR_EXITING |
IA32_VMX_USE_TPR_SHADOW;
want0 = 0;
if (vmm_softc->mode == VMM_MODE_EPT) {
want1 |= IA32_VMX_ACTIVATE_SECONDARY_CONTROLS;
want0 |= IA32_VMX_CR3_LOAD_EXITING |
IA32_VMX_CR3_STORE_EXITING;
}
if (vcpu->vc_vmx_basic & IA32_VMX_TRUE_CTLS_AVAIL) {
ctrl = IA32_VMX_TRUE_PROCBASED_CTLS;
ctrlval = vcpu->vc_vmx_true_procbased_ctls;
} else {
ctrl = IA32_VMX_PROCBASED_CTLS;
ctrlval = vcpu->vc_vmx_procbased_ctls;
}
if (vcpu_vmx_compute_ctrl(ctrlval, ctrl, want1, want0, &procbased)) {
DPRINTF("%s: error computing procbased controls\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_PROCBASED_CTLS, procbased)) {
DPRINTF("%s: error setting procbased controls\n", __func__);
ret = EINVAL;
goto exit;
}
/*
* Secondary Procbased ctrls
*
* We want to be able to set the following, if available:
* IA32_VMX_ENABLE_VPID - use VPIDs where available
*
* If we have EPT, we must be able to set the following:
* IA32_VMX_ENABLE_EPT - enable EPT
*
* If we have unrestricted guest capability, we must be able to set
* the following:
* IA32_VMX_UNRESTRICTED_GUEST - enable unrestricted guest (if caller
* specified CR0_PG | CR0_PE in %cr0 in the 'vrs' parameter)
*/
want1 = 0;
/* XXX checking for 2ndary controls can be combined here */
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1)) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_VPID, 1)) {
want1 |= IA32_VMX_ENABLE_VPID;
vcpu->vc_vmx_vpid_enabled = 1;
}
}
if (vmm_softc->mode == VMM_MODE_EPT)
want1 |= IA32_VMX_ENABLE_EPT;
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1)) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_UNRESTRICTED_GUEST, 1)) {
if ((cr0 & (CR0_PE | CR0_PG)) == 0) {
want1 |= IA32_VMX_UNRESTRICTED_GUEST;
ug = 1;
}
}
}
want0 = ~want1;
ctrlval = vcpu->vc_vmx_procbased2_ctls;
ctrl = IA32_VMX_PROCBASED2_CTLS;
if (vcpu_vmx_compute_ctrl(ctrlval, ctrl, want1, want0, &procbased2)) {
DPRINTF("%s: error computing secondary procbased controls\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_PROCBASED2_CTLS, procbased2)) {
DPRINTF("%s: error setting secondary procbased controls\n",
__func__);
ret = EINVAL;
goto exit;
}
/*
* Exit ctrls
*
* We must be able to set the following:
* IA32_VMX_SAVE_DEBUG_CONTROLS
* IA32_VMX_HOST_SPACE_ADDRESS_SIZE - exit to long mode
* IA32_VMX_ACKNOWLEDGE_INTERRUPT_ON_EXIT - ack interrupt on exit
*/
want1 = IA32_VMX_HOST_SPACE_ADDRESS_SIZE |
IA32_VMX_ACKNOWLEDGE_INTERRUPT_ON_EXIT |
IA32_VMX_SAVE_DEBUG_CONTROLS;
want0 = 0;
if (vcpu->vc_vmx_basic & IA32_VMX_TRUE_CTLS_AVAIL) {
ctrl = IA32_VMX_TRUE_EXIT_CTLS;
ctrlval = vcpu->vc_vmx_true_exit_ctls;
} else {
ctrl = IA32_VMX_EXIT_CTLS;
ctrlval = vcpu->vc_vmx_exit_ctls;
}
if (vcpu_vmx_compute_ctrl(ctrlval, ctrl, want1, want0, &exit)) {
DPRINTF("%s: error computing exit controls\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_EXIT_CTLS, exit)) {
DPRINTF("%s: error setting exit controls\n", __func__);
ret = EINVAL;
goto exit;
}
/*
* Entry ctrls
*
* We must be able to set the following:
* IA32_VMX_IA32E_MODE_GUEST (if no unrestricted guest)
* IA32_VMX_LOAD_DEBUG_CONTROLS
* We must be able to clear the following:
* IA32_VMX_ENTRY_TO_SMM - enter to SMM
* IA32_VMX_DEACTIVATE_DUAL_MONITOR_TREATMENT
* IA32_VMX_LOAD_IA32_PERF_GLOBAL_CTRL_ON_ENTRY
*/
want1 = IA32_VMX_LOAD_DEBUG_CONTROLS;
if (vrs->vrs_msrs[VCPU_REGS_EFER] & EFER_LMA)
want1 |= IA32_VMX_IA32E_MODE_GUEST;
want0 = IA32_VMX_ENTRY_TO_SMM |
IA32_VMX_DEACTIVATE_DUAL_MONITOR_TREATMENT |
IA32_VMX_LOAD_IA32_PERF_GLOBAL_CTRL_ON_ENTRY;
if (vcpu->vc_vmx_basic & IA32_VMX_TRUE_CTLS_AVAIL) {
ctrl = IA32_VMX_TRUE_ENTRY_CTLS;
ctrlval = vcpu->vc_vmx_true_entry_ctls;
} else {
ctrl = IA32_VMX_ENTRY_CTLS;
ctrlval = vcpu->vc_vmx_entry_ctls;
}
if (vcpu_vmx_compute_ctrl(ctrlval, ctrl, want1, want0, &entry)) {
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_ENTRY_CTLS, entry)) {
ret = EINVAL;
goto exit;
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1)) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_VPID, 1)) {
/* We may sleep during allocation, so reload VMCS. */
vcpu->vc_last_pcpu = curcpu();
ret = vmm_alloc_vpid(&vpid);
if (vcpu_reload_vmcs_vmx(vcpu)) {
printf("%s: failed to reload vmcs\n", __func__);
ret = EINVAL;
goto exit;
}
if (ret) {
DPRINTF("%s: could not allocate VPID\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_VPID, vpid)) {
DPRINTF("%s: error setting guest VPID\n",
__func__);
ret = EINVAL;
goto exit;
}
vcpu->vc_vpid = vpid;
}
}
/*
* Determine which bits in CR0 have to be set to a fixed
* value as per Intel SDM A.7.
* CR0 bits in the vrs parameter must match these.
*/
want1 = (curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed0) &
(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed1);
want0 = ~(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed0) &
~(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed1);
/*
* CR0_FIXED0 and CR0_FIXED1 may report the CR0_PG and CR0_PE bits as
* fixed to 1 even if the CPU supports the unrestricted guest
* feature. Update want1 and want0 accordingly to allow
* any value for CR0_PG and CR0_PE in vrs->vrs_crs[VCPU_REGS_CR0] if
* the CPU has the unrestricted guest capability.
*/
if (ug) {
want1 &= ~(CR0_PG | CR0_PE);
want0 &= ~(CR0_PG | CR0_PE);
}
/*
* VMX may require some bits to be set that userland should not have
* to care about. Set those here.
*/
if (want1 & CR0_NE)
cr0 |= CR0_NE;
if ((cr0 & want1) != want1) {
ret = EINVAL;
goto exit;
}
if ((~cr0 & want0) != want0) {
ret = EINVAL;
goto exit;
}
vcpu->vc_vmx_cr0_fixed1 = want1;
vcpu->vc_vmx_cr0_fixed0 = want0;
/*
* Determine which bits in CR4 have to be set to a fixed
* value as per Intel SDM A.8.
* CR4 bits in the vrs parameter must match these, except
* CR4_VMXE - we add that here since it must always be set.
*/
want1 = (curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0) &
(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1);
want0 = ~(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0) &
~(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1);
cr4 = vrs->vrs_crs[VCPU_REGS_CR4] | CR4_VMXE;
if ((cr4 & want1) != want1) {
ret = EINVAL;
goto exit;
}
if ((~cr4 & want0) != want0) {
ret = EINVAL;
goto exit;
}
cr3 = vrs->vrs_crs[VCPU_REGS_CR3];
/* Restore PDPTEs if 32-bit PAE paging is being used */
if (cr3 && (cr4 & CR4_PAE) &&
!(vrs->vrs_msrs[VCPU_REGS_EFER] & EFER_LMA)) {
if (vmwrite(VMCS_GUEST_PDPTE0,
vrs->vrs_crs[VCPU_REGS_PDPTE0])) {
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_PDPTE1,
vrs->vrs_crs[VCPU_REGS_PDPTE1])) {
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_PDPTE2,
vrs->vrs_crs[VCPU_REGS_PDPTE2])) {
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_PDPTE3,
vrs->vrs_crs[VCPU_REGS_PDPTE3])) {
ret = EINVAL;
goto exit;
}
}
vrs->vrs_crs[VCPU_REGS_CR0] = cr0;
vrs->vrs_crs[VCPU_REGS_CR4] = cr4;
/*
* Select host MSRs to be loaded on exit
*/
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_load_va;
msr_store[0].vms_index = MSR_EFER;
msr_store[0].vms_data = rdmsr(MSR_EFER);
msr_store[1].vms_index = MSR_STAR;
msr_store[1].vms_data = rdmsr(MSR_STAR);
msr_store[2].vms_index = MSR_LSTAR;
msr_store[2].vms_data = rdmsr(MSR_LSTAR);
msr_store[3].vms_index = MSR_CSTAR;
msr_store[3].vms_data = rdmsr(MSR_CSTAR);
msr_store[4].vms_index = MSR_SFMASK;
msr_store[4].vms_data = rdmsr(MSR_SFMASK);
msr_store[5].vms_index = MSR_KERNELGSBASE;
msr_store[5].vms_data = rdmsr(MSR_KERNELGSBASE);
msr_store[6].vms_index = MSR_MISC_ENABLE;
msr_store[6].vms_data = rdmsr(MSR_MISC_ENABLE);
/*
* Select guest MSRs to be loaded on entry / saved on exit
*/
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
msr_store[VCPU_REGS_EFER].vms_index = MSR_EFER;
msr_store[VCPU_REGS_STAR].vms_index = MSR_STAR;
msr_store[VCPU_REGS_LSTAR].vms_index = MSR_LSTAR;
msr_store[VCPU_REGS_CSTAR].vms_index = MSR_CSTAR;
msr_store[VCPU_REGS_SFMASK].vms_index = MSR_SFMASK;
msr_store[VCPU_REGS_KGSBASE].vms_index = MSR_KERNELGSBASE;
msr_store[VCPU_REGS_MISC_ENABLE].vms_index = MSR_MISC_ENABLE;
/*
* Initialize MSR_MISC_ENABLE as it can't be read and populated from vmd
* and some of the content is based on the host.
*/
msr_store[VCPU_REGS_MISC_ENABLE].vms_data = rdmsr(MSR_MISC_ENABLE);
msr_store[VCPU_REGS_MISC_ENABLE].vms_data &=
~(MISC_ENABLE_TCC | MISC_ENABLE_PERF_MON_AVAILABLE |
MISC_ENABLE_EIST_ENABLED | MISC_ENABLE_ENABLE_MONITOR_FSM |
MISC_ENABLE_xTPR_MESSAGE_DISABLE);
msr_store[VCPU_REGS_MISC_ENABLE].vms_data |=
MISC_ENABLE_BTS_UNAVAILABLE | MISC_ENABLE_PEBS_UNAVAILABLE;
/*
* Currently we have the same count of entry/exit MSRs loads/stores
* but this is not an architectural requirement.
*/
if (vmwrite(VMCS_EXIT_MSR_STORE_COUNT, VMX_NUM_MSR_STORE)) {
DPRINTF("%s: error setting guest MSR exit store count\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_EXIT_MSR_LOAD_COUNT, VMX_NUM_MSR_STORE)) {
DPRINTF("%s: error setting guest MSR exit load count\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_ENTRY_MSR_LOAD_COUNT, VMX_NUM_MSR_STORE)) {
DPRINTF("%s: error setting guest MSR entry load count\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_EXIT_STORE_MSR_ADDRESS,
vcpu->vc_vmx_msr_exit_save_pa)) {
DPRINTF("%s: error setting guest MSR exit store address\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_EXIT_LOAD_MSR_ADDRESS,
vcpu->vc_vmx_msr_exit_load_pa)) {
DPRINTF("%s: error setting guest MSR exit load address\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_ENTRY_LOAD_MSR_ADDRESS,
vcpu->vc_vmx_msr_exit_save_pa)) {
DPRINTF("%s: error setting guest MSR entry load address\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_MSR_BITMAP_ADDRESS,
vcpu->vc_msr_bitmap_pa)) {
DPRINTF("%s: error setting guest MSR bitmap address\n",
__func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_CR4_MASK, CR4_VMXE)) {
DPRINTF("%s: error setting guest CR4 mask\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_CR0_MASK, CR0_NE)) {
DPRINTF("%s: error setting guest CR0 mask\n", __func__);
ret = EINVAL;
goto exit;
}
/*
* Set up the VMCS for the register state we want during VCPU start.
* This matches what the CPU state would be after a bootloader
* transition to 'start'.
*/
ret = vcpu_writeregs_vmx(vcpu, VM_RWREGS_ALL, 0, vrs);
/*
* Set up the MSR bitmap
*/
memset((uint8_t *)vcpu->vc_msr_bitmap_va, 0xFF, PAGE_SIZE);
vmx_setmsrbrw(vcpu, MSR_IA32_FEATURE_CONTROL);
vmx_setmsrbrw(vcpu, MSR_SYSENTER_CS);
vmx_setmsrbrw(vcpu, MSR_SYSENTER_ESP);
vmx_setmsrbrw(vcpu, MSR_SYSENTER_EIP);
vmx_setmsrbrw(vcpu, MSR_EFER);
vmx_setmsrbrw(vcpu, MSR_STAR);
vmx_setmsrbrw(vcpu, MSR_LSTAR);
vmx_setmsrbrw(vcpu, MSR_CSTAR);
vmx_setmsrbrw(vcpu, MSR_SFMASK);
vmx_setmsrbrw(vcpu, MSR_FSBASE);
vmx_setmsrbrw(vcpu, MSR_GSBASE);
vmx_setmsrbrw(vcpu, MSR_KERNELGSBASE);
vmx_setmsrbr(vcpu, MSR_MISC_ENABLE);
/* XXX CR0 shadow */
/* XXX CR4 shadow */
/* xcr0 power on default sets bit 0 (x87 state) */
vcpu->vc_gueststate.vg_xcr0 = XCR0_X87 & xsave_mask;
/* XXX PAT shadow */
vcpu->vc_shadow_pat = rdmsr(MSR_CR_PAT);
/* Flush the VMCS */
if (vmclear(&vcpu->vc_control_pa)) {
DPRINTF("%s: vmclear failed\n", __func__);
ret = EINVAL;
}
atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_CLEARED);
exit:
return (ret);
}
/*
* vcpu_init_vmx
*
* Intel VMX specific VCPU initialization routine.
*
* This function allocates various per-VCPU memory regions, sets up initial
* VCPU VMCS controls, and sets initial register values.
*
* Parameters:
* vcpu: the VCPU structure being initialized
*
* Return values:
* 0: the VCPU was initialized successfully
* ENOMEM: insufficient resources
* EINVAL: an error occurred during VCPU initialization
*/
int
vcpu_init_vmx(struct vcpu *vcpu)
{
struct vmcs *vmcs;
uint64_t msr, eptp;
uint32_t cr0, cr4;
int ret = 0;
/* Allocate VMCS VA */
vcpu->vc_control_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page, &kp_zero,
&kd_waitok);
vcpu->vc_vmx_vmcs_state = VMCS_CLEARED;
if (!vcpu->vc_control_va)
return (ENOMEM);
/* Compute VMCS PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_control_va,
(paddr_t *)&vcpu->vc_control_pa)) {
ret = ENOMEM;
goto exit;
}
/* Allocate MSR bitmap VA */
vcpu->vc_msr_bitmap_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page, &kp_zero,
&kd_waitok);
if (!vcpu->vc_msr_bitmap_va) {
ret = ENOMEM;
goto exit;
}
/* Compute MSR bitmap PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_msr_bitmap_va,
(paddr_t *)&vcpu->vc_msr_bitmap_pa)) {
ret = ENOMEM;
goto exit;
}
/* Allocate MSR exit load area VA */
vcpu->vc_vmx_msr_exit_load_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page,
&kp_zero, &kd_waitok);
if (!vcpu->vc_vmx_msr_exit_load_va) {
ret = ENOMEM;
goto exit;
}
/* Compute MSR exit load area PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_vmx_msr_exit_load_va,
&vcpu->vc_vmx_msr_exit_load_pa)) {
ret = ENOMEM;
goto exit;
}
/* Allocate MSR exit save area VA */
vcpu->vc_vmx_msr_exit_save_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page,
&kp_zero, &kd_waitok);
if (!vcpu->vc_vmx_msr_exit_save_va) {
ret = ENOMEM;
goto exit;
}
/* Compute MSR exit save area PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_vmx_msr_exit_save_va,
&vcpu->vc_vmx_msr_exit_save_pa)) {
ret = ENOMEM;
goto exit;
}
/* Allocate MSR entry load area VA */
vcpu->vc_vmx_msr_entry_load_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page,
&kp_zero, &kd_waitok);
if (!vcpu->vc_vmx_msr_entry_load_va) {
ret = ENOMEM;
goto exit;
}
/* Compute MSR entry load area PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_vmx_msr_entry_load_va,
&vcpu->vc_vmx_msr_entry_load_pa)) {
ret = ENOMEM;
goto exit;
}
vmcs = (struct vmcs *)vcpu->vc_control_va;
vmcs->vmcs_revision = curcpu()->ci_vmm_cap.vcc_vmx.vmx_vmxon_revision;
/*
* Load the VMCS onto this PCPU so we can write registers
*/
if (vmptrld(&vcpu->vc_control_pa)) {
ret = EINVAL;
goto exit;
}
/* Configure EPT Pointer */
eptp = vcpu->vc_parent->vm_map->pmap->pm_pdirpa;
msr = rdmsr(IA32_VMX_EPT_VPID_CAP);
if (msr & IA32_EPT_VPID_CAP_PAGE_WALK_4) {
/* Page walk length 4 supported */
eptp |= ((IA32_EPT_PAGE_WALK_LENGTH - 1) << 3);
} else {
DPRINTF("EPT page walk length 4 not supported\n");
ret = EINVAL;
goto exit;
}
if (msr & IA32_EPT_VPID_CAP_WB) {
/* WB cache type supported */
eptp |= IA32_EPT_PAGING_CACHE_TYPE_WB;
} else
DPRINTF("%s: no WB cache type available, guest VM will run "
"uncached\n", __func__);
DPRINTF("Guest EPTP = 0x%llx\n", eptp);
if (vmwrite(VMCS_GUEST_IA32_EPTP, eptp)) {
DPRINTF("%s: error setting guest EPTP\n", __func__);
ret = EINVAL;
goto exit;
}
vcpu->vc_parent->vm_map->pmap->eptp = eptp;
/* Host CR0 */
cr0 = rcr0() & ~CR0_TS;
if (vmwrite(VMCS_HOST_IA32_CR0, cr0)) {
DPRINTF("%s: error writing host CR0\n", __func__);
ret = EINVAL;
goto exit;
}
/* Host CR4 */
cr4 = rcr4();
if (vmwrite(VMCS_HOST_IA32_CR4, cr4)) {
DPRINTF("%s: error writing host CR4\n", __func__);
ret = EINVAL;
goto exit;
}
/* Host Segment Selectors */
if (vmwrite(VMCS_HOST_IA32_CS_SEL, GSEL(GCODE_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host CS selector\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_HOST_IA32_DS_SEL, GSEL(GDATA_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host DS selector\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_HOST_IA32_ES_SEL, GSEL(GDATA_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host ES selector\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_HOST_IA32_FS_SEL, GSEL(GDATA_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host FS selector\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_HOST_IA32_GS_SEL, GSEL(GDATA_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host GS selector\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_HOST_IA32_SS_SEL, GSEL(GDATA_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host SS selector\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_HOST_IA32_TR_SEL, GSYSSEL(GPROC0_SEL, SEL_KPL))) {
DPRINTF("%s: error writing host TR selector\n", __func__);
ret = EINVAL;
goto exit;
}
/* Host IDTR base */
if (vmwrite(VMCS_HOST_IA32_IDTR_BASE, idt_vaddr)) {
DPRINTF("%s: error writing host IDTR base\n", __func__);
ret = EINVAL;
goto exit;
}
/* VMCS link */
if (vmwrite(VMCS_LINK_POINTER, VMX_VMCS_PA_CLEAR)) {
DPRINTF("%s: error writing VMCS link pointer\n", __func__);
ret = EINVAL;
goto exit;
}
/* Flush the initial VMCS */
if (vmclear(&vcpu->vc_control_pa)) {
DPRINTF("%s: vmclear failed\n", __func__);
ret = EINVAL;
}
exit:
if (ret)
vcpu_deinit_vmx(vcpu);
return (ret);
}
/*
* vcpu_reset_regs
*
* Resets a vcpu's registers to the provided state
*
* Parameters:
* vcpu: the vcpu whose registers shall be reset
* vrs: the desired register state
*
* Return values:
* 0: the vcpu's registers were successfully reset
* !0: the vcpu's registers could not be reset (see arch-specific reset
* function for various values that can be returned here)
*/
int
vcpu_reset_regs(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
{
int ret;
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
ret = vcpu_reset_regs_vmx(vcpu, vrs);
else if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI)
ret = vcpu_reset_regs_svm(vcpu, vrs);
else
panic("%s: unknown vmm mode: %d", __func__, vmm_softc->mode);
return (ret);
}
/*
* vcpu_init_svm
*
* AMD SVM specific VCPU initialization routine.
*
* This function allocates various per-VCPU memory regions, sets up initial
* VCPU VMCB controls, and sets initial register values.
*
* Parameters:
* vcpu: the VCPU structure being initialized
*
* Return values:
* 0: the VCPU was initialized successfully
* ENOMEM: insufficient resources
* EINVAL: an error occurred during VCPU initialization
*/
int
vcpu_init_svm(struct vcpu *vcpu)
{
int ret = 0;
/* Allocate VMCB VA */
vcpu->vc_control_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page, &kp_zero,
&kd_waitok);
if (!vcpu->vc_control_va)
return (ENOMEM);
/* Compute VMCB PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_control_va,
(paddr_t *)&vcpu->vc_control_pa)) {
ret = ENOMEM;
goto exit;
}
DPRINTF("%s: VMCB va @ 0x%llx, pa @ 0x%llx\n", __func__,
(uint64_t)vcpu->vc_control_va,
(uint64_t)vcpu->vc_control_pa);
/* Allocate MSR bitmap VA (2 pages) */
vcpu->vc_msr_bitmap_va = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any,
&vmm_kp_contig, &kd_waitok);
if (!vcpu->vc_msr_bitmap_va) {
ret = ENOMEM;
goto exit;
}
/* Compute MSR bitmap PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_msr_bitmap_va,
(paddr_t *)&vcpu->vc_msr_bitmap_pa)) {
ret = ENOMEM;
goto exit;
}
DPRINTF("%s: MSR bitmap va @ 0x%llx, pa @ 0x%llx\n", __func__,
(uint64_t)vcpu->vc_msr_bitmap_va,
(uint64_t)vcpu->vc_msr_bitmap_pa);
/* Allocate host state area VA */
vcpu->vc_svm_hsa_va = (vaddr_t)km_alloc(PAGE_SIZE, &kv_page,
&kp_zero, &kd_waitok);
if (!vcpu->vc_svm_hsa_va) {
ret = ENOMEM;
goto exit;
}
/* Compute host state area PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_svm_hsa_va,
&vcpu->vc_svm_hsa_pa)) {
ret = ENOMEM;
goto exit;
}
DPRINTF("%s: HSA va @ 0x%llx, pa @ 0x%llx\n", __func__,
(uint64_t)vcpu->vc_svm_hsa_va,
(uint64_t)vcpu->vc_svm_hsa_pa);
/* Allocate IOIO area VA (3 pages) */
vcpu->vc_svm_ioio_va = (vaddr_t)km_alloc(3 * PAGE_SIZE, &kv_any,
&vmm_kp_contig, &kd_waitok);
if (!vcpu->vc_svm_ioio_va) {
ret = ENOMEM;
goto exit;
}
/* Compute IOIO area PA */
if (!pmap_extract(pmap_kernel(), vcpu->vc_svm_ioio_va,
&vcpu->vc_svm_ioio_pa)) {
ret = ENOMEM;
goto exit;
}
DPRINTF("%s: IOIO va @ 0x%llx, pa @ 0x%llx\n", __func__,
(uint64_t)vcpu->vc_svm_ioio_va,
(uint64_t)vcpu->vc_svm_ioio_pa);
exit:
if (ret)
vcpu_deinit_svm(vcpu);
return (ret);
}
/*
* vcpu_init
*
* Calls the architecture-specific VCPU init routine
*/
int
vcpu_init(struct vcpu *vcpu)
{
int ret = 0;
vcpu->vc_virt_mode = vmm_softc->mode;
vcpu->vc_state = VCPU_STATE_STOPPED;
vcpu->vc_vpid = 0;
vcpu->vc_pvclock_system_gpa = 0;
vcpu->vc_last_pcpu = NULL;
rw_init(&vcpu->vc_lock, "vcpu");
/* Shadow PAT MSR, starting with host's value. */
vcpu->vc_shadow_pat = rdmsr(MSR_CR_PAT);
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
ret = vcpu_init_vmx(vcpu);
else if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI)
ret = vcpu_init_svm(vcpu);
else
panic("%s: unknown vmm mode: %d", __func__, vmm_softc->mode);
return (ret);
}
/*
* vcpu_deinit_vmx
*
* Deinitializes the vcpu described by 'vcpu'
*
* Parameters:
* vcpu: the vcpu to be deinited
*/
void
vcpu_deinit_vmx(struct vcpu *vcpu)
{
if (vcpu->vc_control_va) {
km_free((void *)vcpu->vc_control_va, PAGE_SIZE,
&kv_page, &kp_zero);
vcpu->vc_control_va = 0;
}
if (vcpu->vc_vmx_msr_exit_save_va) {
km_free((void *)vcpu->vc_vmx_msr_exit_save_va,
PAGE_SIZE, &kv_page, &kp_zero);
vcpu->vc_vmx_msr_exit_save_va = 0;
}
if (vcpu->vc_vmx_msr_exit_load_va) {
km_free((void *)vcpu->vc_vmx_msr_exit_load_va,
PAGE_SIZE, &kv_page, &kp_zero);
vcpu->vc_vmx_msr_exit_load_va = 0;
}
if (vcpu->vc_vmx_msr_entry_load_va) {
km_free((void *)vcpu->vc_vmx_msr_entry_load_va,
PAGE_SIZE, &kv_page, &kp_zero);
vcpu->vc_vmx_msr_entry_load_va = 0;
}
if (vcpu->vc_vmx_vpid_enabled)
vmm_free_vpid(vcpu->vc_vpid);
}
/*
* vcpu_deinit_svm
*
* Deinitializes the vcpu described by 'vcpu'
*
* Parameters:
* vcpu: the vcpu to be deinited
*/
void
vcpu_deinit_svm(struct vcpu *vcpu)
{
if (vcpu->vc_control_va) {
km_free((void *)vcpu->vc_control_va, PAGE_SIZE, &kv_page,
&kp_zero);
vcpu->vc_control_va = 0;
}
if (vcpu->vc_msr_bitmap_va) {
km_free((void *)vcpu->vc_msr_bitmap_va, 2 * PAGE_SIZE, &kv_any,
&vmm_kp_contig);
vcpu->vc_msr_bitmap_va = 0;
}
if (vcpu->vc_svm_hsa_va) {
km_free((void *)vcpu->vc_svm_hsa_va, PAGE_SIZE, &kv_page,
&kp_zero);
vcpu->vc_svm_hsa_va = 0;
}
if (vcpu->vc_svm_ioio_va) {
km_free((void *)vcpu->vc_svm_ioio_va, 3 * PAGE_SIZE, &kv_any,
&vmm_kp_contig);
vcpu->vc_svm_ioio_va = 0;
}
vmm_free_vpid(vcpu->vc_vpid);
}
/*
* vcpu_deinit
*
* Calls the architecture-specific VCPU deinit routine
*
* Parameters:
* vcpu: the vcpu to be deinited
*/
void
vcpu_deinit(struct vcpu *vcpu)
{
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
vcpu_deinit_vmx(vcpu);
else if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI)
vcpu_deinit_svm(vcpu);
else
panic("%s: unknown vmm mode: %d", __func__, vmm_softc->mode);
}
/*
* vm_teardown
*
* Tears down (destroys) the vm indicated by 'vm'.
*
* Assumes the vm is already removed from the global vm list (or was never
* added).
*
* Parameters:
* vm: vm to be torn down
*/
void
vm_teardown(struct vm **target)
{
size_t nvcpu = 0;
struct vcpu *vcpu, *tmp;
struct vm *vm = *target;
struct vmspace *vm_vmspace;
KERNEL_ASSERT_UNLOCKED();
refcnt_finalize(&vm->vm_refcnt, "vmteardown");
/* Free VCPUs */
rw_enter_write(&vm->vm_vcpu_lock);
SLIST_FOREACH_SAFE(vcpu, &vm->vm_vcpu_list, vc_vcpu_link, tmp) {
refcnt_take(&vcpu->vc_refcnt);
refcnt_finalize(&vcpu->vc_refcnt, "vcputeardown");
SLIST_REMOVE(&vm->vm_vcpu_list, vcpu, vcpu, vc_vcpu_link);
vcpu_deinit(vcpu);
pool_put(&vcpu_pool, vcpu);
nvcpu++;
}
rw_exit_write(&vm->vm_vcpu_lock);
vm_impl_deinit(vm);
/* teardown guest vmspace */
KERNEL_LOCK();
vm_vmspace = vm->vm_vmspace;
if (vm_vmspace != NULL) {
vm->vm_vmspace = NULL;
uvmspace_free(vm_vmspace);
}
KERNEL_UNLOCK();
pool_put(&vm_pool, vm);
*target = NULL;
}
/*
* vcpu_vmx_check_cap
*
* Checks if the 'cap' bit in the 'msr' MSR can be set or cleared (set = 1
* or set = 0, respectively).
*
* When considering 'msr', we check to see if true controls are available,
* and use those if so.
*
* Returns 1 of 'cap' can be set/cleared as requested, 0 otherwise.
*/
int
vcpu_vmx_check_cap(struct vcpu *vcpu, uint32_t msr, uint32_t cap, int set)
{
uint64_t ctl;
if (vcpu->vc_vmx_basic & IA32_VMX_TRUE_CTLS_AVAIL) {
switch (msr) {
case IA32_VMX_PINBASED_CTLS:
ctl = vcpu->vc_vmx_true_pinbased_ctls;
break;
case IA32_VMX_PROCBASED_CTLS:
ctl = vcpu->vc_vmx_true_procbased_ctls;
break;
case IA32_VMX_PROCBASED2_CTLS:
ctl = vcpu->vc_vmx_procbased2_ctls;
break;
case IA32_VMX_ENTRY_CTLS:
ctl = vcpu->vc_vmx_true_entry_ctls;
break;
case IA32_VMX_EXIT_CTLS:
ctl = vcpu->vc_vmx_true_exit_ctls;
break;
default:
return (0);
}
} else {
switch (msr) {
case IA32_VMX_PINBASED_CTLS:
ctl = vcpu->vc_vmx_pinbased_ctls;
break;
case IA32_VMX_PROCBASED_CTLS:
ctl = vcpu->vc_vmx_procbased_ctls;
break;
case IA32_VMX_PROCBASED2_CTLS:
ctl = vcpu->vc_vmx_procbased2_ctls;
break;
case IA32_VMX_ENTRY_CTLS:
ctl = vcpu->vc_vmx_entry_ctls;
break;
case IA32_VMX_EXIT_CTLS:
ctl = vcpu->vc_vmx_exit_ctls;
break;
default:
return (0);
}
}
if (set) {
/* Check bit 'cap << 32', must be !0 */
return (ctl & ((uint64_t)cap << 32)) != 0;
} else {
/* Check bit 'cap', must be 0 */
return (ctl & cap) == 0;
}
}
/*
* vcpu_vmx_compute_ctrl
*
* Computes the appropriate control value, given the supplied parameters
* and CPU capabilities.
*
* Intel has made somewhat of a mess of this computation - it is described
* using no fewer than three different approaches, spread across many
* pages of the SDM. Further compounding the problem is the fact that now
* we have "true controls" for each type of "control", and each needs to
* be examined to get the calculation right, but only if "true" controls
* are present on the CPU we're on.
*
* Parameters:
* ctrlval: the control value, as read from the CPU MSR
* ctrl: which control is being set (eg, pinbased, procbased, etc)
* want0: the set of desired 0 bits
* want1: the set of desired 1 bits
* out: (out) the correct value to write into the VMCS for this VCPU,
* for the 'ctrl' desired.
*
* Returns 0 if successful, or EINVAL if the supplied parameters define
* an unworkable control setup.
*/
int
vcpu_vmx_compute_ctrl(uint64_t ctrlval, uint16_t ctrl, uint32_t want1,
uint32_t want0, uint32_t *out)
{
int i, set, clear;
*out = 0;
/*
* The Intel SDM gives three formulae for determining which bits to
* set/clear for a given control and desired functionality. Formula
* 1 is the simplest but disallows use of newer features that are
* enabled by functionality in later CPUs.
*
* Formulas 2 and 3 allow such extra functionality. We use formula
* 2 - this requires us to know the identity of controls in the
* "default1" class for each control register, but allows us to not
* have to pass along and/or query both sets of capability MSRs for
* each control lookup. This makes the code slightly longer,
* however.
*/
for (i = 0; i < 32; i++) {
/* Figure out if we can set and / or clear this bit */
set = (ctrlval & (1ULL << (i + 32))) != 0;
clear = ((1ULL << i) & ((uint64_t)ctrlval)) == 0;
/* If the bit can't be set nor cleared, something's wrong */
if (!set && !clear)
return (EINVAL);
/*
* Formula 2.c.i - "If the relevant VMX capability MSR
* reports that a control has a single setting, use that
* setting."
*/
if (set && !clear) {
if (want0 & (1ULL << i))
return (EINVAL);
else
*out |= (1ULL << i);
} else if (clear && !set) {
if (want1 & (1ULL << i))
return (EINVAL);
else
*out &= ~(1ULL << i);
} else {
/*
* 2.c.ii - "If the relevant VMX capability MSR
* reports that a control can be set to 0 or 1
* and that control's meaning is known to the VMM,
* set the control based on the functionality desired."
*/
if (want1 & (1ULL << i))
*out |= (1ULL << i);
else if (want0 & (1 << i))
*out &= ~(1ULL << i);
else {
/*
* ... assuming the control's meaning is not
* known to the VMM ...
*
* 2.c.iii - "If the relevant VMX capability
* MSR reports that a control can be set to 0
* or 1 and the control is not in the default1
* class, set the control to 0."
*
* 2.c.iv - "If the relevant VMX capability
* MSR reports that a control can be set to 0
* or 1 and the control is in the default1
* class, set the control to 1."
*/
switch (ctrl) {
case IA32_VMX_PINBASED_CTLS:
case IA32_VMX_TRUE_PINBASED_CTLS:
/*
* A.3.1 - default1 class of pinbased
* controls comprises bits 1,2,4
*/
switch (i) {
case 1:
case 2:
case 4:
*out |= (1ULL << i);
break;
default:
*out &= ~(1ULL << i);
break;
}
break;
case IA32_VMX_PROCBASED_CTLS:
case IA32_VMX_TRUE_PROCBASED_CTLS:
/*
* A.3.2 - default1 class of procbased
* controls comprises bits 1, 4-6, 8,
* 13-16, 26
*/
switch (i) {
case 1:
case 4 ... 6:
case 8:
case 13 ... 16:
case 26:
*out |= (1ULL << i);
break;
default:
*out &= ~(1ULL << i);
break;
}
break;
/*
* Unknown secondary procbased controls
* can always be set to 0
*/
case IA32_VMX_PROCBASED2_CTLS:
*out &= ~(1ULL << i);
break;
case IA32_VMX_EXIT_CTLS:
case IA32_VMX_TRUE_EXIT_CTLS:
/*
* A.4 - default1 class of exit
* controls comprises bits 0-8, 10,
* 11, 13, 14, 16, 17
*/
switch (i) {
case 0 ... 8:
case 10 ... 11:
case 13 ... 14:
case 16 ... 17:
*out |= (1ULL << i);
break;
default:
*out &= ~(1ULL << i);
break;
}
break;
case IA32_VMX_ENTRY_CTLS:
case IA32_VMX_TRUE_ENTRY_CTLS:
/*
* A.5 - default1 class of entry
* controls comprises bits 0-8, 12
*/
switch (i) {
case 0 ... 8:
case 12:
*out |= (1ULL << i);
break;
default:
*out &= ~(1ULL << i);
break;
}
break;
}
}
}
}
return (0);
}
/*
* vm_get_info
*
* Returns information about the VM indicated by 'vip'. The 'vip_size' field
* in the 'vip' parameter is used to indicate the size of the caller's buffer.
* If insufficient space exists in that buffer, the required size needed is
* returned in vip_size and the number of VM information structures returned
* in vip_info_count is set to 0. The caller should then try the ioctl again
* after allocating a sufficiently large buffer.
*
* Parameters:
* vip: information structure identifying the VM to query
*
* Return values:
* 0: the operation succeeded
* ENOMEM: memory allocation error during processing
* EFAULT: error copying data to user process
*/
int
vm_get_info(struct vm_info_params *vip)
{
struct vm_info_result *out;
struct vm *vm;
struct vcpu *vcpu;
int i = 0, j;
size_t need, vm_ct;
rw_enter_read(&vmm_softc->vm_lock);
vm_ct = vmm_softc->vm_ct;
rw_exit_read(&vmm_softc->vm_lock);
need = vm_ct * sizeof(struct vm_info_result);
if (vip->vip_size < need) {
vip->vip_info_ct = 0;
vip->vip_size = need;
return (0);
}
out = malloc(need, M_DEVBUF, M_NOWAIT|M_ZERO);
if (out == NULL) {
vip->vip_info_ct = 0;
return (ENOMEM);
}
vip->vip_info_ct = vm_ct;
rw_enter_read(&vmm_softc->vm_lock);
SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) {
refcnt_take(&vm->vm_refcnt);
out[i].vir_memory_size = vm->vm_memory_size;
out[i].vir_used_size =
pmap_resident_count(vm->vm_map->pmap) * PAGE_SIZE;
out[i].vir_ncpus = vm->vm_vcpu_ct;
out[i].vir_id = vm->vm_id;
out[i].vir_creator_pid = vm->vm_creator_pid;
strlcpy(out[i].vir_name, vm->vm_name, VMM_MAX_NAME_LEN);
rw_enter_read(&vm->vm_vcpu_lock);
for (j = 0; j < vm->vm_vcpu_ct; j++) {
out[i].vir_vcpu_state[j] = VCPU_STATE_UNKNOWN;
SLIST_FOREACH(vcpu, &vm->vm_vcpu_list,
vc_vcpu_link) {
refcnt_take(&vcpu->vc_refcnt);
if (vcpu->vc_id == j)
out[i].vir_vcpu_state[j] =
vcpu->vc_state;
refcnt_rele_wake(&vcpu->vc_refcnt);
}
}
rw_exit_read(&vm->vm_vcpu_lock);
refcnt_rele_wake(&vm->vm_refcnt);
i++;
if (i == vm_ct)
break; /* Truncate to keep within bounds of 'out'. */
}
rw_exit_read(&vmm_softc->vm_lock);
if (copyout(out, vip->vip_info, need) == EFAULT) {
free(out, M_DEVBUF, need);
return (EFAULT);
}
free(out, M_DEVBUF, need);
return (0);
}
/*
* vm_terminate
*
* Terminates the VM indicated by 'vtp'.
*
* Parameters:
* vtp: structure defining the VM to terminate
*
* Return values:
* 0: the VM was terminated
* !0: the VM could not be located
*/
int
vm_terminate(struct vm_terminate_params *vtp)
{
struct vm *vm;
struct vcpu *vcpu;
u_int old, next;
int error, nvcpu, vm_id;
/*
* Find desired VM
*/
error = vm_find(vtp->vtp_vm_id, &vm);
if (error)
return (error);
/* Stop all vcpu's for the vm. */
rw_enter_read(&vm->vm_vcpu_lock);
SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link) {
refcnt_take(&vcpu->vc_refcnt);
do {
old = vcpu->vc_state;
if (old == VCPU_STATE_RUNNING)
next = VCPU_STATE_REQTERM;
else if (old == VCPU_STATE_STOPPED)
next = VCPU_STATE_TERMINATED;
else /* must be REQTERM or TERMINATED */
break;
} while (old != atomic_cas_uint(&vcpu->vc_state, old, next));
refcnt_rele_wake(&vcpu->vc_refcnt);
}
rw_exit_read(&vm->vm_vcpu_lock);
/* Pop the vm out of the global vm list. */
rw_enter_write(&vmm_softc->vm_lock);
SLIST_REMOVE(&vmm_softc->vm_list, vm, vm, vm_link);
rw_exit_write(&vmm_softc->vm_lock);
vm_id = vm->vm_id;
nvcpu = vm->vm_vcpu_ct;
vm_teardown(&vm);
if (vm_id > 0) {
rw_enter_write(&vmm_softc->vm_lock);
vmm_softc->vm_ct--;
vmm_softc->vcpu_ct -= nvcpu;
if (vmm_softc->vm_ct < 1)
vmm_stop();
rw_exit_write(&vmm_softc->vm_lock);
}
return (0);
}
/*
* vm_run
*
* Run the vm / vcpu specified by 'vrp'
*
* Parameters:
* vrp: structure defining the VM to run
*
* Return value:
* ENOENT: the VM defined in 'vrp' could not be located
* EBUSY: the VM defined in 'vrp' is already running
* EFAULT: error copying data from userspace (vmd) on return from previous
* exit.
* EAGAIN: help is needed from vmd(8) (device I/O or exit vmm(4) cannot
* handle in-kernel.)
* 0: the run loop exited and no help is needed from vmd(8)
*/
int
vm_run(struct vm_run_params *vrp)
{
struct vm *vm;
struct vcpu *vcpu;
int ret = 0;
u_int old, next;
/*
* Find desired VM
*/
ret = vm_find(vrp->vrp_vm_id, &vm);
if (ret)
return (ret);
vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id);
if (vcpu == NULL) {
ret = ENOENT;
goto out;
}
/*
* Attempt to transition from VCPU_STATE_STOPPED -> VCPU_STATE_RUNNING.
* Failure to make the transition indicates the VCPU is busy.
*/
rw_enter_write(&vcpu->vc_lock);
old = VCPU_STATE_STOPPED;
next = VCPU_STATE_RUNNING;
if (atomic_cas_uint(&vcpu->vc_state, old, next) != old) {
ret = EBUSY;
goto out_unlock;
}
atomic_inc_int(&vm->vm_vcpus_running);
/*
* We may be returning from userland helping us from the last exit.
* If so (vrp_continue == 1), copy in the exit data from vmd. The
* exit data will be consumed before the next entry (this typically
* comprises VCPU register changes as the result of vmd(8)'s actions).
*/
if (vrp->vrp_continue) {
if (copyin(vrp->vrp_exit, &vcpu->vc_exit,
sizeof(struct vm_exit)) == EFAULT) {
ret = EFAULT;
goto out_unlock;
}
}
/* Run the VCPU specified in vrp */
if (vcpu->vc_virt_mode == VMM_MODE_VMX ||
vcpu->vc_virt_mode == VMM_MODE_EPT) {
ret = vcpu_run_vmx(vcpu, vrp);
} else if (vcpu->vc_virt_mode == VMM_MODE_SVM ||
vcpu->vc_virt_mode == VMM_MODE_RVI) {
ret = vcpu_run_svm(vcpu, vrp);
}
atomic_dec_int(&vm->vm_vcpus_running);
if (ret == 0 || ret == EAGAIN) {
/* If we are exiting, populate exit data so vmd can help. */
vrp->vrp_exit_reason = (ret == 0) ? VM_EXIT_NONE
: vcpu->vc_gueststate.vg_exit_reason;
vrp->vrp_irqready = vcpu->vc_irqready;
vcpu->vc_state = VCPU_STATE_STOPPED;
if (copyout(&vcpu->vc_exit, vrp->vrp_exit,
sizeof(struct vm_exit)) == EFAULT) {
ret = EFAULT;
} else
ret = 0;
} else {
vrp->vrp_exit_reason = VM_EXIT_TERMINATED;
vcpu->vc_state = VCPU_STATE_TERMINATED;
}
out_unlock:
rw_exit_write(&vcpu->vc_lock);
out:
if (vcpu != NULL)
refcnt_rele_wake(&vcpu->vc_refcnt);
refcnt_rele_wake(&vm->vm_refcnt);
return (ret);
}
/*
* vcpu_must_stop
*
* Check if we need to (temporarily) stop running the VCPU for some reason,
* such as:
* - the VM was requested to terminate
* - the proc running this VCPU has pending signals
*
* Parameters:
* vcpu: the VCPU to check
*
* Return values:
* 1: the VM owning this VCPU should stop
* 0: no stop is needed
*/
int
vcpu_must_stop(struct vcpu *vcpu)
{
struct proc *p = curproc;
if (vcpu->vc_state == VCPU_STATE_REQTERM)
return (1);
if (SIGPENDING(p) != 0)
return (1);
return (0);
}
/*
* vmm_fpurestore
*
* Restore the guest's FPU state, saving the existing userland thread's
* FPU context if necessary. Must be called with interrupts disabled.
*/
int
vmm_fpurestore(struct vcpu *vcpu)
{
struct cpu_info *ci = curcpu();
rw_assert_wrlock(&vcpu->vc_lock);
/* save vmm's FPU state if we haven't already */
if (ci->ci_pflags & CPUPF_USERXSTATE) {
ci->ci_pflags &= ~CPUPF_USERXSTATE;
fpusavereset(&curproc->p_addr->u_pcb.pcb_savefpu);
}
if (vcpu->vc_fpuinited) {
if (xrstor_user(&vcpu->vc_g_fpu, xsave_mask)) {
DPRINTF("%s: guest attempted to set invalid %s\n",
__func__, "xsave/xrstor state");
return EINVAL;
}
}
if (xsave_mask) {
/* Restore guest %xcr0 */
if (xsetbv_user(0, vcpu->vc_gueststate.vg_xcr0)) {
DPRINTF("%s: guest attempted to set invalid bits in "
"xcr0 (guest %%xcr0=0x%llx, host %%xcr0=0x%llx)\n",
__func__, vcpu->vc_gueststate.vg_xcr0, xsave_mask);
return EINVAL;
}
}
return 0;
}
/*
* vmm_fpusave
*
* Save the guest's FPU state. Must be called with interrupts disabled.
*/
void
vmm_fpusave(struct vcpu *vcpu)
{
rw_assert_wrlock(&vcpu->vc_lock);
if (xsave_mask) {
/* Save guest %xcr0 */
vcpu->vc_gueststate.vg_xcr0 = xgetbv(0);
/* Restore host %xcr0 */
xsetbv(0, xsave_mask);
}
/*
* Save full copy of FPU state - guest content is always
* a subset of host's save area (see xsetbv exit handler)
*/
fpusavereset(&vcpu->vc_g_fpu);
vcpu->vc_fpuinited = 1;
}
/*
* vmm_translate_gva
*
* Translates a guest virtual address to a guest physical address by walking
* the currently active page table (if needed).
*
* Note - this function can possibly alter the supplied VCPU state.
* Specifically, it may inject exceptions depending on the current VCPU
* configuration, and may alter %cr2 on #PF. Consequently, this function
* should only be used as part of instruction emulation.
*
* Parameters:
* vcpu: The VCPU this translation should be performed for (guest MMU settings
* are gathered from this VCPU)
* va: virtual address to translate
* pa: pointer to paddr_t variable that will receive the translated physical
* address. 'pa' is unchanged on error.
* mode: one of PROT_READ, PROT_WRITE, PROT_EXEC indicating the mode in which
* the address should be translated
*
* Return values:
* 0: the address was successfully translated - 'pa' contains the physical
* address currently mapped by 'va'.
* EFAULT: the PTE for 'VA' is unmapped. A #PF will be injected in this case
* and %cr2 set in the vcpu structure.
* EINVAL: an error occurred reading paging table structures
*/
int
vmm_translate_gva(struct vcpu *vcpu, uint64_t va, uint64_t *pa, int mode)
{
int level, shift, pdidx;
uint64_t pte, pt_paddr, pte_paddr, mask, low_mask, high_mask;
uint64_t shift_width, pte_size, *hva;
paddr_t hpa;
struct vcpu_reg_state vrs;
level = 0;
if (vmm_softc->mode == VMM_MODE_EPT ||
vmm_softc->mode == VMM_MODE_VMX) {
if (vcpu_readregs_vmx(vcpu, VM_RWREGS_ALL, 1, &vrs))
return (EINVAL);
} else if (vmm_softc->mode == VMM_MODE_RVI ||
vmm_softc->mode == VMM_MODE_SVM) {
if (vcpu_readregs_svm(vcpu, VM_RWREGS_ALL, &vrs))
return (EINVAL);
} else {
printf("%s: unknown vmm mode", __func__);
return (EINVAL);
}
DPRINTF("%s: guest %%cr0=0x%llx, %%cr3=0x%llx\n", __func__,
vrs.vrs_crs[VCPU_REGS_CR0], vrs.vrs_crs[VCPU_REGS_CR3]);
if (!(vrs.vrs_crs[VCPU_REGS_CR0] & CR0_PG)) {
DPRINTF("%s: unpaged, va=pa=0x%llx\n", __func__,
va);
*pa = va;
return (0);
}
pt_paddr = vrs.vrs_crs[VCPU_REGS_CR3];
if (vrs.vrs_crs[VCPU_REGS_CR0] & CR0_PE) {
if (vrs.vrs_crs[VCPU_REGS_CR4] & CR4_PAE) {
pte_size = sizeof(uint64_t);
shift_width = 9;
if (vrs.vrs_msrs[VCPU_REGS_EFER] & EFER_LMA) {
level = 4;
mask = L4_MASK;
shift = L4_SHIFT;
} else {
level = 3;
mask = L3_MASK;
shift = L3_SHIFT;
}
} else {
level = 2;
shift_width = 10;
mask = 0xFFC00000;
shift = 22;
pte_size = sizeof(uint32_t);
}
} else {
return (EINVAL);
}
DPRINTF("%s: pte size=%lld level=%d mask=0x%llx, shift=%d, "
"shift_width=%lld\n", __func__, pte_size, level, mask, shift,
shift_width);
/* XXX: Check for R bit in segment selector and set A bit */
for (;level > 0; level--) {
pdidx = (va & mask) >> shift;
pte_paddr = (pt_paddr) + (pdidx * pte_size);
DPRINTF("%s: read pte level %d @ GPA 0x%llx\n", __func__,
level, pte_paddr);
if (!pmap_extract(vcpu->vc_parent->vm_map->pmap, pte_paddr,
&hpa)) {
DPRINTF("%s: cannot extract HPA for GPA 0x%llx\n",
__func__, pte_paddr);
return (EINVAL);
}
hpa = hpa | (pte_paddr & 0xFFF);
hva = (uint64_t *)PMAP_DIRECT_MAP(hpa);
DPRINTF("%s: GPA 0x%llx -> HPA 0x%llx -> HVA 0x%llx\n",
__func__, pte_paddr, (uint64_t)hpa, (uint64_t)hva);
if (pte_size == 8)
pte = *hva;
else
pte = *(uint32_t *)hva;
DPRINTF("%s: PTE @ 0x%llx = 0x%llx\n", __func__, pte_paddr,
pte);
/* XXX: Set CR2 */
if (!(pte & PG_V))
return (EFAULT);
/* XXX: Check for SMAP */
if ((mode == PROT_WRITE) && !(pte & PG_RW))
return (EPERM);
if ((vcpu->vc_exit.cpl > 0) && !(pte & PG_u))
return (EPERM);
pte = pte | PG_U;
if (mode == PROT_WRITE)
pte = pte | PG_M;
*hva = pte;
/* XXX: EINVAL if in 32bit and PG_PS is 1 but CR4.PSE is 0 */
if (pte & PG_PS)
break;
if (level > 1) {
pt_paddr = pte & PG_FRAME;
shift -= shift_width;
mask = mask >> shift_width;
}
}
low_mask = ((uint64_t)1ULL << shift) - 1;
high_mask = (((uint64_t)1ULL << ((pte_size * 8) - 1)) - 1) ^ low_mask;
*pa = (pte & high_mask) | (va & low_mask);
DPRINTF("%s: final GPA for GVA 0x%llx = 0x%llx\n", __func__,
va, *pa);
return (0);
}
/*
* vcpu_run_vmx
*
* VMX main loop used to run a VCPU.
*
* Parameters:
* vcpu: The VCPU to run
* vrp: run parameters
*
* Return values:
* 0: The run loop exited and no help is needed from vmd
* EAGAIN: The run loop exited and help from vmd is needed
* EINVAL: an error occurred
*/
int
vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *vrp)
{
int ret = 0, exitinfo;
struct region_descriptor gdt;
struct cpu_info *ci = curcpu();
uint64_t exit_reason, cr3, insn_error;
struct schedstate_percpu *spc;
struct vmx_invvpid_descriptor vid;
uint64_t eii, procbased, int_st;
uint16_t irq, ldt_sel;
u_long s;
struct region_descriptor gdtr, idtr;
rw_assert_wrlock(&vcpu->vc_lock);
if (vcpu_reload_vmcs_vmx(vcpu)) {
printf("%s: failed (re)loading vmcs\n", __func__);
return (EINVAL);
}
/*
* If we are returning from userspace (vmd) because we exited
* last time, fix up any needed vcpu state first. Which state
* needs to be fixed up depends on what vmd populated in the
* exit data structure.
*/
irq = vrp->vrp_irq;
if (vrp->vrp_continue) {
switch (vcpu->vc_gueststate.vg_exit_reason) {
case VMX_EXIT_IO:
if (vcpu->vc_exit.vei.vei_dir == VEI_DIR_IN)
vcpu->vc_gueststate.vg_rax =
vcpu->vc_exit.vei.vei_data;
break;
case VMX_EXIT_EPT_VIOLATION:
ret = vcpu_writeregs_vmx(vcpu, VM_RWREGS_GPRS, 0,
&vcpu->vc_exit.vrs);
if (ret) {
printf("%s: vm %d vcpu %d failed to update "
"registers\n", __func__,
vcpu->vc_parent->vm_id, vcpu->vc_id);
return (EINVAL);
}
break;
case VM_EXIT_NONE:
case VMX_EXIT_HLT:
case VMX_EXIT_INT_WINDOW:
case VMX_EXIT_EXTINT:
case VMX_EXIT_CPUID:
case VMX_EXIT_XSETBV:
break;
#ifdef VMM_DEBUG
case VMX_EXIT_TRIPLE_FAULT:
DPRINTF("%s: vm %d vcpu %d triple fault\n",
__func__, vcpu->vc_parent->vm_id,
vcpu->vc_id);
vmx_vcpu_dump_regs(vcpu);
dump_vcpu(vcpu);
vmx_dump_vmcs(vcpu);
break;
case VMX_EXIT_ENTRY_FAILED_GUEST_STATE:
DPRINTF("%s: vm %d vcpu %d failed entry "
"due to invalid guest state\n",
__func__, vcpu->vc_parent->vm_id,
vcpu->vc_id);
vmx_vcpu_dump_regs(vcpu);
dump_vcpu(vcpu);
return (EINVAL);
default:
DPRINTF("%s: unimplemented exit type %d (%s)\n",
__func__,
vcpu->vc_gueststate.vg_exit_reason,
vmx_exit_reason_decode(
vcpu->vc_gueststate.vg_exit_reason));
vmx_vcpu_dump_regs(vcpu);
dump_vcpu(vcpu);
break;
#endif /* VMM_DEBUG */
}
memset(&vcpu->vc_exit, 0, sizeof(vcpu->vc_exit));
}
setregion(&gdt, ci->ci_gdt, GDT_SIZE - 1);
if (gdt.rd_base == 0) {
printf("%s: setregion\n", __func__);
return (EINVAL);
}
/* Host GDTR base */
if (vmwrite(VMCS_HOST_IA32_GDTR_BASE, gdt.rd_base)) {
printf("%s: vmwrite(0x%04X, 0x%llx)\n", __func__,
VMCS_HOST_IA32_GDTR_BASE, gdt.rd_base);
return (EINVAL);
}
/* Host TR base */
if (vmwrite(VMCS_HOST_IA32_TR_BASE, (uint64_t)ci->ci_tss)) {
printf("%s: vmwrite(0x%04X, 0x%llx)\n", __func__,
VMCS_HOST_IA32_TR_BASE, (uint64_t)ci->ci_tss);
return (EINVAL);
}
/* Host CR3 */
cr3 = rcr3();
if (vmwrite(VMCS_HOST_IA32_CR3, cr3)) {
printf("%s: vmwrite(0x%04X, 0x%llx)\n", __func__,
VMCS_HOST_IA32_CR3, cr3);
return (EINVAL);
}
/* Handle vmd(8) injected interrupts */
/* Is there an interrupt pending injection? */
if (irq != 0xFFFF) {
if (vmread(VMCS_GUEST_INTERRUPTIBILITY_ST, &int_st)) {
printf("%s: can't get interruptibility state\n",
__func__);
return (EINVAL);
}
/* Interruptibility state 0x3 covers NMIs and STI */
if (!(int_st & 0x3) && vcpu->vc_irqready) {
eii = (irq & 0xFF);
eii |= (1ULL << 31); /* Valid */
eii |= (0ULL << 8); /* Hardware Interrupt */
if (vmwrite(VMCS_ENTRY_INTERRUPTION_INFO, eii)) {
printf("vcpu_run_vmx: can't vector "
"interrupt to guest\n");
return (EINVAL);
}
irq = 0xFFFF;
}
} else if (!vcpu->vc_intr) {
/*
* Disable window exiting
*/
if (vmread(VMCS_PROCBASED_CTLS, &procbased)) {
printf("%s: can't read procbased ctls on exit\n",
__func__);
return (EINVAL);
} else {
procbased &= ~IA32_VMX_INTERRUPT_WINDOW_EXITING;
if (vmwrite(VMCS_PROCBASED_CTLS, procbased)) {
printf("%s: can't write procbased ctls "
"on exit\n", __func__);
return (EINVAL);
}
}
}
while (ret == 0) {
#ifdef VMM_DEBUG
paddr_t pa = 0ULL;
vmptrst(&pa);
KASSERT(pa == vcpu->vc_control_pa);
#endif /* VMM_DEBUG */
vmm_update_pvclock(vcpu);
/* Inject event if present */
if (vcpu->vc_event != 0) {
eii = (vcpu->vc_event & 0xFF);
eii |= (1ULL << 31); /* Valid */
/* Set the "Send error code" flag for certain vectors */
switch (vcpu->vc_event & 0xFF) {
case VMM_EX_DF:
case VMM_EX_TS:
case VMM_EX_NP:
case VMM_EX_SS:
case VMM_EX_GP:
case VMM_EX_PF:
case VMM_EX_AC:
eii |= (1ULL << 11);
}
eii |= (3ULL << 8); /* Hardware Exception */
if (vmwrite(VMCS_ENTRY_INTERRUPTION_INFO, eii)) {
printf("%s: can't vector event to guest\n",
__func__);
ret = EINVAL;
break;
}
if (vmwrite(VMCS_ENTRY_EXCEPTION_ERROR_CODE, 0)) {
printf("%s: can't write error code to guest\n",
__func__);
ret = EINVAL;
break;
}
vcpu->vc_event = 0;
}
if (vcpu->vc_vmx_vpid_enabled) {
/* Invalidate old TLB mappings */
vid.vid_vpid = vcpu->vc_parent->vm_id;
vid.vid_addr = 0;
invvpid(IA32_VMX_INVVPID_SINGLE_CTX_GLB, &vid);
}
/* Start / resume the VCPU */
/* Disable interrupts and save the current host FPU state. */
s = intr_disable();
if ((ret = vmm_fpurestore(vcpu))) {
intr_restore(s);
break;
}
sgdt(&gdtr);
sidt(&idtr);
sldt(&ldt_sel);
TRACEPOINT(vmm, guest_enter, vcpu, vrp);
ret = vmx_enter_guest(&vcpu->vc_control_pa,
&vcpu->vc_gueststate,
(vcpu->vc_vmx_vmcs_state == VMCS_LAUNCHED),
ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr);
bare_lgdt(&gdtr);
lidt(&idtr);
lldt(ldt_sel);
/*
* On exit, interrupts are disabled, and we are running with
* the guest FPU state still possibly on the CPU. Save the FPU
* state before re-enabling interrupts.
*/
vmm_fpusave(vcpu);
intr_restore(s);
atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_LAUNCHED);
exit_reason = VM_EXIT_NONE;
/* If we exited successfully ... */
if (ret == 0) {
exitinfo = vmx_get_exit_info(
&vcpu->vc_gueststate.vg_rip, &exit_reason);
if (!(exitinfo & VMX_EXIT_INFO_HAVE_RIP)) {
printf("%s: cannot read guest rip\n", __func__);
ret = EINVAL;
break;
}
if (!(exitinfo & VMX_EXIT_INFO_HAVE_REASON)) {
printf("%s: cant read exit reason\n", __func__);
ret = EINVAL;
break;
}
vcpu->vc_gueststate.vg_exit_reason = exit_reason;
TRACEPOINT(vmm, guest_exit, vcpu, vrp, exit_reason);
/* Update our state */
if (vmread(VMCS_GUEST_IA32_RFLAGS,
&vcpu->vc_gueststate.vg_rflags)) {
printf("%s: can't read guest rflags during "
"exit\n", __func__);
ret = EINVAL;
break;
}
/*
* Handle the exit. This will alter "ret" to EAGAIN if
* the exit handler determines help from vmd is needed.
*/
ret = vmx_handle_exit(vcpu);
if (vcpu->vc_gueststate.vg_rflags & PSL_I)
vcpu->vc_irqready = 1;
else
vcpu->vc_irqready = 0;
/*
* If not ready for interrupts, but interrupts pending,
* enable interrupt window exiting.
*/
if (vcpu->vc_irqready == 0 && vcpu->vc_intr) {
if (vmread(VMCS_PROCBASED_CTLS, &procbased)) {
printf("%s: can't read procbased ctls "
"on intwin exit\n", __func__);
ret = EINVAL;
break;
}
procbased |= IA32_VMX_INTERRUPT_WINDOW_EXITING;
if (vmwrite(VMCS_PROCBASED_CTLS, procbased)) {
printf("%s: can't write procbased ctls "
"on intwin exit\n", __func__);
ret = EINVAL;
break;
}
}
/*
* Exit to vmd if we are terminating, failed to enter,
* or need help (device I/O)
*/
if (ret || vcpu_must_stop(vcpu))
break;
if (vcpu->vc_intr && vcpu->vc_irqready) {
ret = EAGAIN;
break;
}
/* Check if we should yield - don't hog the {p,v}pu */
spc = &ci->ci_schedstate;
if (spc->spc_schedflags & SPCF_SHOULDYIELD)
break;
} else {
/*
* We failed vmresume or vmlaunch for some reason,
* typically due to invalid vmcs state or other
* reasons documented in SDM Vol 3C 30.4.
*/
switch (ret) {
case VMX_FAIL_LAUNCH_INVALID_VMCS:
printf("%s: failed %s with invalid vmcs\n",
__func__,
(vcpu->vc_vmx_vmcs_state == VMCS_LAUNCHED
? "vmresume" : "vmlaunch"));
break;
case VMX_FAIL_LAUNCH_VALID_VMCS:
printf("%s: failed %s with valid vmcs\n",
__func__,
(vcpu->vc_vmx_vmcs_state == VMCS_LAUNCHED
? "vmresume" : "vmlaunch"));
break;
default:
printf("%s: failed %s for unknown reason\n",
__func__,
(vcpu->vc_vmx_vmcs_state == VMCS_LAUNCHED
? "vmresume" : "vmlaunch"));
}
ret = EINVAL;
/* Try to translate a vmfail error code, if possible. */
if (vmread(VMCS_INSTRUCTION_ERROR, &insn_error)) {
printf("%s: can't read insn error field\n",
__func__);
} else
printf("%s: error code = %lld, %s\n", __func__,
insn_error,
vmx_instruction_error_decode(insn_error));
#ifdef VMM_DEBUG
vmx_vcpu_dump_regs(vcpu);
dump_vcpu(vcpu);
#endif /* VMM_DEBUG */
}
}
vcpu->vc_last_pcpu = curcpu();
/* Copy the VCPU register state to the exit structure */
if (vcpu_readregs_vmx(vcpu, VM_RWREGS_ALL, 0, &vcpu->vc_exit.vrs))
ret = EINVAL;
vcpu->vc_exit.cpl = vmm_get_guest_cpu_cpl(vcpu);
return (ret);
}
/*
* vmx_handle_intr
*
* Handle host (external) interrupts. We read which interrupt fired by
* extracting the vector from the VMCS and dispatch the interrupt directly
* to the host using vmm_dispatch_intr.
*/
void
vmx_handle_intr(struct vcpu *vcpu)
{
uint8_t vec;
uint64_t eii;
struct gate_descriptor *idte;
vaddr_t handler;
if (vmread(VMCS_EXIT_INTERRUPTION_INFO, &eii)) {
printf("%s: can't obtain intr info\n", __func__);
return;
}
vec = eii & 0xFF;
/* XXX check "error valid" code in eii, abort if 0 */
idte=&idt[vec];
handler = idte->gd_looffset + ((uint64_t)idte->gd_hioffset << 16);
vmm_dispatch_intr(handler);
}
/*
* svm_handle_hlt
*
* Handle HLT exits
*
* Parameters
* vcpu: The VCPU that executed the HLT instruction
*
* Return Values:
* EIO: The guest halted with interrupts disabled
* EAGAIN: Normal return to vmd - vmd should halt scheduling this VCPU
* until a virtual interrupt is ready to inject
*/
int
svm_handle_hlt(struct vcpu *vcpu)
{
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
uint64_t rflags = vmcb->v_rflags;
/* All HLT insns are 1 byte */
vcpu->vc_gueststate.vg_rip += 1;
if (!(rflags & PSL_I)) {
DPRINTF("%s: guest halted with interrupts disabled\n",
__func__);
return (EIO);
}
return (EAGAIN);
}
/*
* vmx_handle_hlt
*
* Handle HLT exits. HLTing the CPU with interrupts disabled will terminate
* the guest (no NMIs handled) by returning EIO to vmd.
*
* Parameters:
* vcpu: The VCPU that executed the HLT instruction
*
* Return Values:
* EINVAL: An error occurred extracting information from the VMCS, or an
* invalid HLT instruction was encountered
* EIO: The guest halted with interrupts disabled
* EAGAIN: Normal return to vmd - vmd should halt scheduling this VCPU
* until a virtual interrupt is ready to inject
*
*/
int
vmx_handle_hlt(struct vcpu *vcpu)
{
uint64_t insn_length, rflags;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
printf("%s: can't obtain instruction length\n", __func__);
return (EINVAL);
}
if (vmread(VMCS_GUEST_IA32_RFLAGS, &rflags)) {
printf("%s: can't obtain guest rflags\n", __func__);
return (EINVAL);
}
if (insn_length != 1) {
DPRINTF("%s: HLT with instruction length %lld not supported\n",
__func__, insn_length);
return (EINVAL);
}
if (!(rflags & PSL_I)) {
DPRINTF("%s: guest halted with interrupts disabled\n",
__func__);
return (EIO);
}
vcpu->vc_gueststate.vg_rip += insn_length;
return (EAGAIN);
}
/*
* vmx_get_exit_info
*
* Returns exit information containing the current guest RIP and exit reason
* in rip and exit_reason. The return value is a bitmask indicating whether
* reading the RIP and exit reason was successful.
*/
int
vmx_get_exit_info(uint64_t *rip, uint64_t *exit_reason)
{
int rv = 0;
if (vmread(VMCS_GUEST_IA32_RIP, rip) == 0) {
rv |= VMX_EXIT_INFO_HAVE_RIP;
if (vmread(VMCS_EXIT_REASON, exit_reason) == 0)
rv |= VMX_EXIT_INFO_HAVE_REASON;
}
return (rv);
}
/*
* svm_handle_exit
*
* Handle exits from the VM by decoding the exit reason and calling various
* subhandlers as needed.
*/
int
svm_handle_exit(struct vcpu *vcpu)
{
uint64_t exit_reason, rflags;
int update_rip, ret = 0;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
update_rip = 0;
exit_reason = vcpu->vc_gueststate.vg_exit_reason;
rflags = vcpu->vc_gueststate.vg_rflags;
switch (exit_reason) {
case SVM_VMEXIT_VINTR:
if (!(rflags & PSL_I)) {
DPRINTF("%s: impossible interrupt window exit "
"config\n", __func__);
ret = EINVAL;
break;
}
/*
* Guest is now ready for interrupts, so disable interrupt
* window exiting.
*/
vmcb->v_irq = 0;
vmcb->v_intr_vector = 0;
vmcb->v_intercept1 &= ~SVM_INTERCEPT_VINTR;
svm_set_dirty(vcpu, SVM_CLEANBITS_TPR | SVM_CLEANBITS_I);
update_rip = 0;
break;
case SVM_VMEXIT_INTR:
update_rip = 0;
break;
case SVM_VMEXIT_SHUTDOWN:
update_rip = 0;
ret = EAGAIN;
break;
case SVM_VMEXIT_NPF:
ret = svm_handle_np_fault(vcpu);
break;
case SVM_VMEXIT_CPUID:
ret = vmm_handle_cpuid(vcpu);
update_rip = 1;
break;
case SVM_VMEXIT_MSR:
ret = svm_handle_msr(vcpu);
update_rip = 1;
break;
case SVM_VMEXIT_XSETBV:
ret = svm_handle_xsetbv(vcpu);
update_rip = 1;
break;
case SVM_VMEXIT_IOIO:
if (svm_handle_inout(vcpu) == 0)
ret = EAGAIN;
update_rip = 1;
break;
case SVM_VMEXIT_HLT:
ret = svm_handle_hlt(vcpu);
update_rip = 1;
break;
case SVM_VMEXIT_MWAIT:
case SVM_VMEXIT_MWAIT_CONDITIONAL:
case SVM_VMEXIT_MONITOR:
case SVM_VMEXIT_VMRUN:
case SVM_VMEXIT_VMMCALL:
case SVM_VMEXIT_VMLOAD:
case SVM_VMEXIT_VMSAVE:
case SVM_VMEXIT_STGI:
case SVM_VMEXIT_CLGI:
case SVM_VMEXIT_SKINIT:
case SVM_VMEXIT_RDTSCP:
case SVM_VMEXIT_ICEBP:
case SVM_VMEXIT_INVLPGA:
ret = vmm_inject_ud(vcpu);
update_rip = 0;
break;
default:
DPRINTF("%s: unhandled exit 0x%llx (pa=0x%llx)\n", __func__,
exit_reason, (uint64_t)vcpu->vc_control_pa);
return (EINVAL);
}
if (update_rip) {
vmcb->v_rip = vcpu->vc_gueststate.vg_rip;
if (rflags & PSL_T) {
if (vmm_inject_db(vcpu)) {
printf("%s: can't inject #DB exception to "
"guest", __func__);
return (EINVAL);
}
}
}
/* Enable SVME in EFER (must always be set) */
vmcb->v_efer |= EFER_SVME;
svm_set_dirty(vcpu, SVM_CLEANBITS_CR);
return (ret);
}
/*
* vmx_handle_exit
*
* Handle exits from the VM by decoding the exit reason and calling various
* subhandlers as needed.
*/
int
vmx_handle_exit(struct vcpu *vcpu)
{
uint64_t exit_reason, rflags, istate;
int update_rip, ret = 0;
update_rip = 0;
exit_reason = vcpu->vc_gueststate.vg_exit_reason;
rflags = vcpu->vc_gueststate.vg_rflags;
switch (exit_reason) {
case VMX_EXIT_INT_WINDOW:
if (!(rflags & PSL_I)) {
DPRINTF("%s: impossible interrupt window exit "
"config\n", __func__);
ret = EINVAL;
break;
}
ret = EAGAIN;
update_rip = 0;
break;
case VMX_EXIT_EPT_VIOLATION:
ret = vmx_handle_np_fault(vcpu);
break;
case VMX_EXIT_CPUID:
ret = vmm_handle_cpuid(vcpu);
update_rip = 1;
break;
case VMX_EXIT_IO:
if (vmx_handle_inout(vcpu) == 0)
ret = EAGAIN;
update_rip = 1;
break;
case VMX_EXIT_EXTINT:
vmx_handle_intr(vcpu);
update_rip = 0;
break;
case VMX_EXIT_CR_ACCESS:
ret = vmx_handle_cr(vcpu);
update_rip = 1;
break;
case VMX_EXIT_HLT:
ret = vmx_handle_hlt(vcpu);
update_rip = 1;
break;
case VMX_EXIT_RDMSR:
ret = vmx_handle_rdmsr(vcpu);
update_rip = 1;
break;
case VMX_EXIT_WRMSR:
ret = vmx_handle_wrmsr(vcpu);
update_rip = 1;
break;
case VMX_EXIT_XSETBV:
ret = vmx_handle_xsetbv(vcpu);
update_rip = 1;
break;
case VMX_EXIT_MWAIT:
case VMX_EXIT_MONITOR:
case VMX_EXIT_VMXON:
case VMX_EXIT_VMWRITE:
case VMX_EXIT_VMREAD:
case VMX_EXIT_VMLAUNCH:
case VMX_EXIT_VMRESUME:
case VMX_EXIT_VMPTRLD:
case VMX_EXIT_VMPTRST:
case VMX_EXIT_VMCLEAR:
case VMX_EXIT_VMCALL:
case VMX_EXIT_VMFUNC:
case VMX_EXIT_VMXOFF:
case VMX_EXIT_INVVPID:
case VMX_EXIT_INVEPT:
ret = vmm_inject_ud(vcpu);
update_rip = 0;
break;
case VMX_EXIT_TRIPLE_FAULT:
#ifdef VMM_DEBUG
DPRINTF("%s: vm %d vcpu %d triple fault\n", __func__,
vcpu->vc_parent->vm_id, vcpu->vc_id);
vmx_vcpu_dump_regs(vcpu);
dump_vcpu(vcpu);
vmx_dump_vmcs(vcpu);
#endif /* VMM_DEBUG */
ret = EAGAIN;
update_rip = 0;
break;
default:
#ifdef VMM_DEBUG
DPRINTF("%s: unhandled exit 0x%llx (%s)\n", __func__,
exit_reason, vmx_exit_reason_decode(exit_reason));
#endif /* VMM_DEBUG */
return (EINVAL);
}
if (update_rip) {
if (vmwrite(VMCS_GUEST_IA32_RIP,
vcpu->vc_gueststate.vg_rip)) {
printf("%s: can't advance rip\n", __func__);
return (EINVAL);
}
if (vmread(VMCS_GUEST_INTERRUPTIBILITY_ST,
&istate)) {
printf("%s: can't read interruptibility state\n",
__func__);
return (EINVAL);
}
/* Interruptibility state 0x3 covers NMIs and STI */
istate &= ~0x3;
if (vmwrite(VMCS_GUEST_INTERRUPTIBILITY_ST,
istate)) {
printf("%s: can't write interruptibility state\n",
__func__);
return (EINVAL);
}
if (rflags & PSL_T) {
if (vmm_inject_db(vcpu)) {
printf("%s: can't inject #DB exception to "
"guest", __func__);
return (EINVAL);
}
}
}
return (ret);
}
/*
* vmm_inject_gp
*
* Injects an #GP exception into the guest VCPU.
*
* Parameters:
* vcpu: vcpu to inject into
*
* Return values:
* Always 0
*/
int
vmm_inject_gp(struct vcpu *vcpu)
{
DPRINTF("%s: injecting #GP at guest %%rip 0x%llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
vcpu->vc_event = VMM_EX_GP;
return (0);
}
/*
* vmm_inject_ud
*
* Injects an #UD exception into the guest VCPU.
*
* Parameters:
* vcpu: vcpu to inject into
*
* Return values:
* Always 0
*/
int
vmm_inject_ud(struct vcpu *vcpu)
{
DPRINTF("%s: injecting #UD at guest %%rip 0x%llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
vcpu->vc_event = VMM_EX_UD;
return (0);
}
/*
* vmm_inject_db
*
* Injects a #DB exception into the guest VCPU.
*
* Parameters:
* vcpu: vcpu to inject into
*
* Return values:
* Always 0
*/
int
vmm_inject_db(struct vcpu *vcpu)
{
DPRINTF("%s: injecting #DB at guest %%rip 0x%llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
vcpu->vc_event = VMM_EX_DB;
return (0);
}
/*
* vmm_get_guest_memtype
*
* Returns the type of memory 'gpa' refers to in the context of vm 'vm'
*/
int
vmm_get_guest_memtype(struct vm *vm, paddr_t gpa)
{
int i;
struct vm_mem_range *vmr;
if (gpa >= VMM_PCI_MMIO_BAR_BASE && gpa <= VMM_PCI_MMIO_BAR_END) {
DPRINTF("guest mmio access @ 0x%llx\n", (uint64_t)gpa);
return (VMM_MEM_TYPE_MMIO);
}
/* XXX Use binary search? */
for (i = 0; i < vm->vm_nmemranges; i++) {
vmr = &vm->vm_memranges[i];
/*
* vm_memranges are ascending. gpa can no longer be in one of
* the memranges
*/
if (gpa < vmr->vmr_gpa)
break;
if (gpa < vmr->vmr_gpa + vmr->vmr_size)
return (VMM_MEM_TYPE_REGULAR);
}
DPRINTF("guest memtype @ 0x%llx unknown\n", (uint64_t)gpa);
return (VMM_MEM_TYPE_UNKNOWN);
}
/*
* vmx_get_exit_qualification
*
* Return the current VMCS' exit qualification information
*/
int
vmx_get_exit_qualification(uint64_t *exit_qualification)
{
if (vmread(VMCS_GUEST_EXIT_QUALIFICATION, exit_qualification)) {
printf("%s: can't extract exit qual\n", __func__);
return (EINVAL);
}
return (0);
}
/*
* vmx_get_guest_faulttype
*
* Determines the type (R/W/X) of the last fault on the VCPU last run on
* this PCPU.
*/
int
vmx_get_guest_faulttype(void)
{
uint64_t exit_qual;
uint64_t presentmask = IA32_VMX_EPT_FAULT_WAS_READABLE |
IA32_VMX_EPT_FAULT_WAS_WRITABLE | IA32_VMX_EPT_FAULT_WAS_EXECABLE;
vm_prot_t prot, was_prot;
if (vmx_get_exit_qualification(&exit_qual))
return (-1);
if ((exit_qual & presentmask) == 0)
return VM_FAULT_INVALID;
was_prot = 0;
if (exit_qual & IA32_VMX_EPT_FAULT_WAS_READABLE)
was_prot |= PROT_READ;
if (exit_qual & IA32_VMX_EPT_FAULT_WAS_WRITABLE)
was_prot |= PROT_WRITE;
if (exit_qual & IA32_VMX_EPT_FAULT_WAS_EXECABLE)
was_prot |= PROT_EXEC;
prot = 0;
if (exit_qual & IA32_VMX_EPT_FAULT_READ)
prot = PROT_READ;
else if (exit_qual & IA32_VMX_EPT_FAULT_WRITE)
prot = PROT_WRITE;
else if (exit_qual & IA32_VMX_EPT_FAULT_EXEC)
prot = PROT_EXEC;
if ((was_prot & prot) == 0)
return VM_FAULT_PROTECT;
return (-1);
}
/*
* svm_get_guest_faulttype
*
* Determines the type (R/W/X) of the last fault on the VCPU last run on
* this PCPU.
*/
int
svm_get_guest_faulttype(struct vmcb *vmcb)
{
if (!(vmcb->v_exitinfo1 & 0x1))
return VM_FAULT_INVALID;
return VM_FAULT_PROTECT;
}
/*
* svm_fault_page
*
* Request a new page to be faulted into the UVM map of the VM owning 'vcpu'
* at address 'gpa'.
*/
int
svm_fault_page(struct vcpu *vcpu, paddr_t gpa)
{
int ret;
ret = uvm_fault(vcpu->vc_parent->vm_map, gpa, VM_FAULT_WIRE,
PROT_READ | PROT_WRITE | PROT_EXEC);
if (ret)
printf("%s: uvm_fault returns %d, GPA=0x%llx, rip=0x%llx\n",
__func__, ret, (uint64_t)gpa, vcpu->vc_gueststate.vg_rip);
return (ret);
}
/*
* svm_handle_np_fault
*
* High level nested paging handler for SVM. Verifies that a fault is for a
* valid memory region, then faults a page, or aborts otherwise.
*/
int
svm_handle_np_fault(struct vcpu *vcpu)
{
uint64_t gpa;
int gpa_memtype, ret = 0;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
struct vm_exit_eptviolation *vee = &vcpu->vc_exit.vee;
struct cpu_info *ci = curcpu();
memset(vee, 0, sizeof(*vee));
gpa = vmcb->v_exitinfo2;
gpa_memtype = vmm_get_guest_memtype(vcpu->vc_parent, gpa);
switch (gpa_memtype) {
case VMM_MEM_TYPE_REGULAR:
vee->vee_fault_type = VEE_FAULT_HANDLED;
ret = svm_fault_page(vcpu, gpa);
break;
case VMM_MEM_TYPE_MMIO:
vee->vee_fault_type = VEE_FAULT_MMIO_ASSIST;
if (ci->ci_vmm_cap.vcc_svm.svm_decode_assist) {
vee->vee_insn_len = vmcb->v_n_bytes_fetched;
memcpy(&vee->vee_insn_bytes, vmcb->v_guest_ins_bytes,
sizeof(vee->vee_insn_bytes));
vee->vee_insn_info |= VEE_BYTES_VALID;
}
ret = EAGAIN;
break;
default:
printf("unknown memory type %d for GPA 0x%llx\n",
gpa_memtype, gpa);
return (EINVAL);
}
return (ret);
}
/*
* vmx_fault_page
*
* Request a new page to be faulted into the UVM map of the VM owning 'vcpu'
* at address 'gpa'.
*
* Parameters:
* vcpu: guest VCPU requiring the page to be faulted into the UVM map
* gpa: guest physical address that triggered the fault
*
* Return Values:
* 0: if successful
* EINVAL: if fault type could not be determined or VMCS reload fails
* EAGAIN: if a protection fault occurred, ie writing to a read-only page
* errno: if uvm_fault(9) fails to wire in the page
*/
int
vmx_fault_page(struct vcpu *vcpu, paddr_t gpa)
{
int fault_type, ret;
fault_type = vmx_get_guest_faulttype();
switch (fault_type) {
case -1:
printf("%s: invalid fault type\n", __func__);
return (EINVAL);
case VM_FAULT_PROTECT:
vcpu->vc_exit.vee.vee_fault_type = VEE_FAULT_PROTECT;
return (EAGAIN);
default:
vcpu->vc_exit.vee.vee_fault_type = VEE_FAULT_HANDLED;
break;
}
/* We may sleep during uvm_fault(9), so reload VMCS. */
vcpu->vc_last_pcpu = curcpu();
ret = uvm_fault(vcpu->vc_parent->vm_map, gpa, VM_FAULT_WIRE,
PROT_READ | PROT_WRITE | PROT_EXEC);
if (vcpu_reload_vmcs_vmx(vcpu)) {
printf("%s: failed to reload vmcs\n", __func__);
return (EINVAL);
}
if (ret)
printf("%s: uvm_fault returns %d, GPA=0x%llx, rip=0x%llx\n",
__func__, ret, (uint64_t)gpa, vcpu->vc_gueststate.vg_rip);
return (ret);
}
/*
* vmx_handle_np_fault
*
* High level nested paging handler for VMX. Verifies that a fault is for a
* valid memory region, then faults a page, or aborts otherwise.
*/
int
vmx_handle_np_fault(struct vcpu *vcpu)
{
uint64_t insn_len = 0, gpa;
int gpa_memtype, ret = 0;
struct vm_exit_eptviolation *vee = &vcpu->vc_exit.vee;
memset(vee, 0, sizeof(*vee));
if (vmread(VMCS_GUEST_PHYSICAL_ADDRESS, &gpa)) {
printf("%s: cannot extract faulting pa\n", __func__);
return (EINVAL);
}
gpa_memtype = vmm_get_guest_memtype(vcpu->vc_parent, gpa);
switch (gpa_memtype) {
case VMM_MEM_TYPE_REGULAR:
vee->vee_fault_type = VEE_FAULT_HANDLED;
ret = vmx_fault_page(vcpu, gpa);
break;
case VMM_MEM_TYPE_MMIO:
vee->vee_fault_type = VEE_FAULT_MMIO_ASSIST;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_len) ||
insn_len == 0 || insn_len > 15) {
printf("%s: failed to extract instruction length\n",
__func__);
ret = EINVAL;
} else {
vee->vee_insn_len = (uint32_t)insn_len;
vee->vee_insn_info |= VEE_LEN_VALID;
ret = EAGAIN;
}
break;
default:
printf("unknown memory type %d for GPA 0x%llx\n",
gpa_memtype, gpa);
return (EINVAL);
}
return (ret);
}
/*
* vmm_get_guest_cpu_cpl
*
* Determines current CPL of 'vcpu'. On VMX/Intel, this is gathered from the
* VMCS field for the DPL of SS (this seems odd, but is documented that way
* in the SDM). For SVM/AMD, this is gathered directly from the VMCB's 'cpl'
* field, as per the APM.
*
* Parameters:
* vcpu: guest VCPU for which CPL is to be checked
*
* Return Values:
* -1: the CPL could not be determined
* 0-3 indicating the current CPL. For real mode operation, 0 is returned.
*/
int
vmm_get_guest_cpu_cpl(struct vcpu *vcpu)
{
int mode;
struct vmcb *vmcb;
uint64_t ss_ar;
mode = vmm_get_guest_cpu_mode(vcpu);
if (mode == VMM_CPU_MODE_UNKNOWN)
return (-1);
if (mode == VMM_CPU_MODE_REAL)
return (0);
if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI) {
vmcb = (struct vmcb *)vcpu->vc_control_va;
return (vmcb->v_cpl);
} else if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT) {
if (vmread(VMCS_GUEST_IA32_SS_AR, &ss_ar))
return (-1);
return ((ss_ar & 0x60) >> 5);
} else
return (-1);
}
/*
* vmm_get_guest_cpu_mode
*
* Determines current CPU mode of 'vcpu'.
*
* Parameters:
* vcpu: guest VCPU for which mode is to be checked
*
* Return Values:
* One of VMM_CPU_MODE_*, or VMM_CPU_MODE_UNKNOWN if the mode could not be
* ascertained.
*/
int
vmm_get_guest_cpu_mode(struct vcpu *vcpu)
{
uint64_t cr0, efer, cs_ar;
uint8_t l, dib;
struct vmcb *vmcb;
struct vmx_msr_store *msr_store;
if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI) {
vmcb = (struct vmcb *)vcpu->vc_control_va;
cr0 = vmcb->v_cr0;
efer = vmcb->v_efer;
cs_ar = vmcb->v_cs.vs_attr;
cs_ar = (cs_ar & 0xff) | ((cs_ar << 4) & 0xf000);
} else if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT) {
if (vmread(VMCS_GUEST_IA32_CR0, &cr0))
return (VMM_CPU_MODE_UNKNOWN);
if (vmread(VMCS_GUEST_IA32_CS_AR, &cs_ar))
return (VMM_CPU_MODE_UNKNOWN);
msr_store =
(struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
efer = msr_store[VCPU_REGS_EFER].vms_data;
} else
return (VMM_CPU_MODE_UNKNOWN);
l = (cs_ar & 0x2000) >> 13;
dib = (cs_ar & 0x4000) >> 14;
/* Check CR0.PE */
if (!(cr0 & CR0_PE))
return (VMM_CPU_MODE_REAL);
/* Check EFER */
if (efer & EFER_LMA) {
/* Could be compat or long mode, check CS.L */
if (l)
return (VMM_CPU_MODE_LONG);
else
return (VMM_CPU_MODE_COMPAT);
}
/* Check prot vs prot32 */
if (dib)
return (VMM_CPU_MODE_PROT32);
else
return (VMM_CPU_MODE_PROT);
}
/*
* svm_handle_inout
*
* Exit handler for IN/OUT instructions.
*
* Parameters:
* vcpu: The VCPU where the IN/OUT instruction occurred
*
* Return values:
* 0: if successful
* EINVAL: an invalid IN/OUT instruction was encountered
*/
int
svm_handle_inout(struct vcpu *vcpu)
{
uint64_t insn_length, exit_qual;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
insn_length = vmcb->v_exitinfo2 - vmcb->v_rip;
if (insn_length != 1 && insn_length != 2) {
DPRINTF("%s: IN/OUT instruction with length %lld not "
"supported\n", __func__, insn_length);
return (EINVAL);
}
exit_qual = vmcb->v_exitinfo1;
/* Bit 0 - direction */
if (exit_qual & 0x1)
vcpu->vc_exit.vei.vei_dir = VEI_DIR_IN;
else
vcpu->vc_exit.vei.vei_dir = VEI_DIR_OUT;
/* Bit 2 - string instruction? */
vcpu->vc_exit.vei.vei_string = (exit_qual & 0x4) >> 2;
/* Bit 3 - REP prefix? */
vcpu->vc_exit.vei.vei_rep = (exit_qual & 0x8) >> 3;
/* Bits 4:6 - size of exit */
if (exit_qual & 0x10)
vcpu->vc_exit.vei.vei_size = 1;
else if (exit_qual & 0x20)
vcpu->vc_exit.vei.vei_size = 2;
else if (exit_qual & 0x40)
vcpu->vc_exit.vei.vei_size = 4;
/* Bit 16:31 - port */
vcpu->vc_exit.vei.vei_port = (exit_qual & 0xFFFF0000) >> 16;
/* Data */
vcpu->vc_exit.vei.vei_data = vmcb->v_rax;
TRACEPOINT(vmm, inout, vcpu, vcpu->vc_exit.vei.vei_port,
vcpu->vc_exit.vei.vei_dir, vcpu->vc_exit.vei.vei_data);
vcpu->vc_gueststate.vg_rip += insn_length;
return (0);
}
/*
* vmx_handle_inout
*
* Exit handler for IN/OUT instructions.
*
* Parameters:
* vcpu: The VCPU where the IN/OUT instruction occurred
*
* Return values:
* 0: if successful
* EINVAL: invalid IN/OUT instruction or vmread failures occurred
*/
int
vmx_handle_inout(struct vcpu *vcpu)
{
uint64_t insn_length, exit_qual;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
printf("%s: can't obtain instruction length\n", __func__);
return (EINVAL);
}
if (insn_length != 1 && insn_length != 2) {
DPRINTF("%s: IN/OUT instruction with length %lld not "
"supported\n", __func__, insn_length);
return (EINVAL);
}
if (vmx_get_exit_qualification(&exit_qual)) {
printf("%s: can't get exit qual\n", __func__);
return (EINVAL);
}
/* Bits 0:2 - size of exit */
vcpu->vc_exit.vei.vei_size = (exit_qual & 0x7) + 1;
/* Bit 3 - direction */
if ((exit_qual & 0x8) >> 3)
vcpu->vc_exit.vei.vei_dir = VEI_DIR_IN;
else
vcpu->vc_exit.vei.vei_dir = VEI_DIR_OUT;
/* Bit 4 - string instruction? */
vcpu->vc_exit.vei.vei_string = (exit_qual & 0x10) >> 4;
/* Bit 5 - REP prefix? */
vcpu->vc_exit.vei.vei_rep = (exit_qual & 0x20) >> 5;
/* Bit 6 - Operand encoding */
vcpu->vc_exit.vei.vei_encoding = (exit_qual & 0x40) >> 6;
/* Bit 16:31 - port */
vcpu->vc_exit.vei.vei_port = (exit_qual & 0xFFFF0000) >> 16;
/* Data */
vcpu->vc_exit.vei.vei_data = (uint32_t)vcpu->vc_gueststate.vg_rax;
TRACEPOINT(vmm, inout, vcpu, vcpu->vc_exit.vei.vei_port,
vcpu->vc_exit.vei.vei_dir, vcpu->vc_exit.vei.vei_data);
vcpu->vc_gueststate.vg_rip += insn_length;
return (0);
}
/*
* vmx_load_pdptes
*
* Update the PDPTEs in the VMCS with the values currently indicated by the
* guest CR3. This is used for 32-bit PAE guests when enabling paging.
*
* Parameters
* vcpu: The vcpu whose PDPTEs should be loaded
*
* Return values:
* 0: if successful
* EINVAL: if the PDPTEs could not be loaded
* ENOMEM: memory allocation failure
*/
int
vmx_load_pdptes(struct vcpu *vcpu)
{
uint64_t cr3, cr3_host_phys;
vaddr_t cr3_host_virt;
pd_entry_t *pdptes;
int ret;
if (vmread(VMCS_GUEST_IA32_CR3, &cr3)) {
printf("%s: can't read guest cr3\n", __func__);
return (EINVAL);
}
if (!pmap_extract(vcpu->vc_parent->vm_map->pmap, (vaddr_t)cr3,
(paddr_t *)&cr3_host_phys)) {
DPRINTF("%s: nonmapped guest CR3, setting PDPTEs to 0\n",
__func__);
if (vmwrite(VMCS_GUEST_PDPTE0, 0)) {
printf("%s: can't write guest PDPTE0\n", __func__);
return (EINVAL);
}
if (vmwrite(VMCS_GUEST_PDPTE1, 0)) {
printf("%s: can't write guest PDPTE1\n", __func__);
return (EINVAL);
}
if (vmwrite(VMCS_GUEST_PDPTE2, 0)) {
printf("%s: can't write guest PDPTE2\n", __func__);
return (EINVAL);
}
if (vmwrite(VMCS_GUEST_PDPTE3, 0)) {
printf("%s: can't write guest PDPTE3\n", __func__);
return (EINVAL);
}
return (0);
}
ret = 0;
/* We may sleep during km_alloc(9), so reload VMCS. */
vcpu->vc_last_pcpu = curcpu();
cr3_host_virt = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any, &kp_none,
&kd_waitok);
if (vcpu_reload_vmcs_vmx(vcpu)) {
printf("%s: failed to reload vmcs\n", __func__);
ret = EINVAL;
goto exit;
}
if (!cr3_host_virt) {
printf("%s: can't allocate address for guest CR3 mapping\n",
__func__);
return (ENOMEM);
}
pmap_kenter_pa(cr3_host_virt, cr3_host_phys, PROT_READ);
pdptes = (pd_entry_t *)cr3_host_virt;
if (vmwrite(VMCS_GUEST_PDPTE0, pdptes[0])) {
printf("%s: can't write guest PDPTE0\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_PDPTE1, pdptes[1])) {
printf("%s: can't write guest PDPTE1\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_PDPTE2, pdptes[2])) {
printf("%s: can't write guest PDPTE2\n", __func__);
ret = EINVAL;
goto exit;
}
if (vmwrite(VMCS_GUEST_PDPTE3, pdptes[3])) {
printf("%s: can't write guest PDPTE3\n", __func__);
ret = EINVAL;
goto exit;
}
exit:
pmap_kremove(cr3_host_virt, PAGE_SIZE);
/* km_free(9) might sleep, so we need to reload VMCS. */
vcpu->vc_last_pcpu = curcpu();
km_free((void *)cr3_host_virt, PAGE_SIZE, &kv_any, &kp_none);
if (vcpu_reload_vmcs_vmx(vcpu)) {
printf("%s: failed to reload vmcs after km_free\n", __func__);
ret = EINVAL;
}
return (ret);
}
/*
* vmx_handle_cr0_write
*
* Write handler for CR0. This function ensures valid values are written into
* CR0 for the cpu/vmm mode in use (cr0 must-be-0 and must-be-1 bits, etc).
*
* Parameters
* vcpu: The vcpu taking the cr0 write exit
* r: The guest's desired (incoming) cr0 value
*
* Return values:
* 0: if successful
* EINVAL: if an error occurred
*/
int
vmx_handle_cr0_write(struct vcpu *vcpu, uint64_t r)
{
struct vmx_msr_store *msr_store;
struct vmx_invvpid_descriptor vid;
uint64_t ectls, oldcr0, cr4, mask;
int ret;
/* Check must-be-0 bits */
mask = vcpu->vc_vmx_cr0_fixed1;
if (~r & mask) {
/* Inject #GP, let the guest handle it */
DPRINTF("%s: guest set invalid bits in %%cr0. Zeros "
"mask=0x%llx, data=0x%llx\n", __func__,
vcpu->vc_vmx_cr0_fixed1, r);
vmm_inject_gp(vcpu);
return (0);
}
/* Check must-be-1 bits */
mask = vcpu->vc_vmx_cr0_fixed0;
if ((r & mask) != mask) {
/* Inject #GP, let the guest handle it */
DPRINTF("%s: guest set invalid bits in %%cr0. Ones "
"mask=0x%llx, data=0x%llx\n", __func__,
vcpu->vc_vmx_cr0_fixed0, r);
vmm_inject_gp(vcpu);
return (0);
}
if (r & 0xFFFFFFFF00000000ULL) {
DPRINTF("%s: setting bits 63:32 of %%cr0 is invalid,"
" inject #GP, cr0=0x%llx\n", __func__, r);
vmm_inject_gp(vcpu);
return (0);
}
if ((r & CR0_PG) && (r & CR0_PE) == 0) {
DPRINTF("%s: PG flag set when the PE flag is clear,"
" inject #GP, cr0=0x%llx\n", __func__, r);
vmm_inject_gp(vcpu);
return (0);
}
if ((r & CR0_NW) && (r & CR0_CD) == 0) {
DPRINTF("%s: NW flag set when the CD flag is clear,"
" inject #GP, cr0=0x%llx\n", __func__, r);
vmm_inject_gp(vcpu);
return (0);
}
if (vmread(VMCS_GUEST_IA32_CR0, &oldcr0)) {
printf("%s: can't read guest cr0\n", __func__);
return (EINVAL);
}
/* CR0 must always have NE set */
r |= CR0_NE;
if (vmwrite(VMCS_GUEST_IA32_CR0, r)) {
printf("%s: can't write guest cr0\n", __func__);
return (EINVAL);
}
/* If the guest hasn't enabled paging ... */
if (!(r & CR0_PG) && (oldcr0 & CR0_PG)) {
/* Paging was disabled (prev. enabled) - Flush TLB */
if ((vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT) &&
vcpu->vc_vmx_vpid_enabled) {
vid.vid_vpid = vcpu->vc_parent->vm_id;
vid.vid_addr = 0;
invvpid(IA32_VMX_INVVPID_SINGLE_CTX_GLB, &vid);
}
} else if (!(oldcr0 & CR0_PG) && (r & CR0_PG)) {
/*
* Since the guest has enabled paging, then the IA32_VMX_IA32E_MODE_GUEST
* control must be set to the same as EFER_LME.
*/
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
if (vmread(VMCS_ENTRY_CTLS, &ectls)) {
printf("%s: can't read entry controls", __func__);
return (EINVAL);
}
if (msr_store[VCPU_REGS_EFER].vms_data & EFER_LME)
ectls |= IA32_VMX_IA32E_MODE_GUEST;
else
ectls &= ~IA32_VMX_IA32E_MODE_GUEST;
if (vmwrite(VMCS_ENTRY_CTLS, ectls)) {
printf("%s: can't write entry controls", __func__);
return (EINVAL);
}
if (vmread(VMCS_GUEST_IA32_CR4, &cr4)) {
printf("%s: can't read guest cr4\n", __func__);
return (EINVAL);
}
/* Load PDPTEs if PAE guest enabling paging */
if (cr4 & CR4_PAE) {
ret = vmx_load_pdptes(vcpu);
if (ret) {
printf("%s: updating PDPTEs failed\n", __func__);
return (ret);
}
}
}
return (0);
}
/*
* vmx_handle_cr4_write
*
* Write handler for CR4. This function ensures valid values are written into
* CR4 for the cpu/vmm mode in use (cr4 must-be-0 and must-be-1 bits, etc).
*
* Parameters
* vcpu: The vcpu taking the cr4 write exit
* r: The guest's desired (incoming) cr4 value
*
* Return values:
* 0: if successful
* EINVAL: if an error occurred
*/
int
vmx_handle_cr4_write(struct vcpu *vcpu, uint64_t r)
{
uint64_t mask;
/* Check must-be-0 bits */
mask = ~(curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1);
if (r & mask) {
/* Inject #GP, let the guest handle it */
DPRINTF("%s: guest set invalid bits in %%cr4. Zeros "
"mask=0x%llx, data=0x%llx\n", __func__,
curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1,
r);
vmm_inject_gp(vcpu);
return (0);
}
/* Check must-be-1 bits */
mask = curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0;
if ((r & mask) != mask) {
/* Inject #GP, let the guest handle it */
DPRINTF("%s: guest set invalid bits in %%cr4. Ones "
"mask=0x%llx, data=0x%llx\n", __func__,
curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0,
r);
vmm_inject_gp(vcpu);
return (0);
}
/* CR4_VMXE must always be enabled */
r |= CR4_VMXE;
if (vmwrite(VMCS_GUEST_IA32_CR4, r)) {
printf("%s: can't write guest cr4\n", __func__);
return (EINVAL);
}
return (0);
}
/*
* vmx_handle_cr
*
* Handle reads/writes to control registers (except CR3)
*/
int
vmx_handle_cr(struct vcpu *vcpu)
{
uint64_t insn_length, exit_qual, r;
uint8_t crnum, dir, reg;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
printf("%s: can't obtain instruction length\n", __func__);
return (EINVAL);
}
if (vmx_get_exit_qualification(&exit_qual)) {
printf("%s: can't get exit qual\n", __func__);
return (EINVAL);
}
/* Low 4 bits of exit_qual represent the CR number */
crnum = exit_qual & 0xf;
/*
* Bits 5:4 indicate the direction of operation (or special CR-modifying
* instruction)
*/
dir = (exit_qual & 0x30) >> 4;
/* Bits 11:8 encode the source/target register */
reg = (exit_qual & 0xf00) >> 8;
switch (dir) {
case CR_WRITE:
if (crnum == 0 || crnum == 4) {
switch (reg) {
case 0: r = vcpu->vc_gueststate.vg_rax; break;
case 1: r = vcpu->vc_gueststate.vg_rcx; break;
case 2: r = vcpu->vc_gueststate.vg_rdx; break;
case 3: r = vcpu->vc_gueststate.vg_rbx; break;
case 4: if (vmread(VMCS_GUEST_IA32_RSP, &r)) {
printf("%s: unable to read guest "
"RSP\n", __func__);
return (EINVAL);
}
break;
case 5: r = vcpu->vc_gueststate.vg_rbp; break;
case 6: r = vcpu->vc_gueststate.vg_rsi; break;
case 7: r = vcpu->vc_gueststate.vg_rdi; break;
case 8: r = vcpu->vc_gueststate.vg_r8; break;
case 9: r = vcpu->vc_gueststate.vg_r9; break;
case 10: r = vcpu->vc_gueststate.vg_r10; break;
case 11: r = vcpu->vc_gueststate.vg_r11; break;
case 12: r = vcpu->vc_gueststate.vg_r12; break;
case 13: r = vcpu->vc_gueststate.vg_r13; break;
case 14: r = vcpu->vc_gueststate.vg_r14; break;
case 15: r = vcpu->vc_gueststate.vg_r15; break;
}
DPRINTF("%s: mov to cr%d @ %llx, data=0x%llx\n",
__func__, crnum, vcpu->vc_gueststate.vg_rip, r);
}
if (crnum == 0)
vmx_handle_cr0_write(vcpu, r);
if (crnum == 4)
vmx_handle_cr4_write(vcpu, r);
break;
case CR_READ:
DPRINTF("%s: mov from cr%d @ %llx\n", __func__, crnum,
vcpu->vc_gueststate.vg_rip);
break;
case CR_CLTS:
DPRINTF("%s: clts instruction @ %llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
break;
case CR_LMSW:
DPRINTF("%s: lmsw instruction @ %llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
break;
default:
DPRINTF("%s: unknown cr access @ %llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
}
vcpu->vc_gueststate.vg_rip += insn_length;
return (0);
}
/*
* vmx_handle_rdmsr
*
* Handler for rdmsr instructions. Bitmap MSRs are allowed implicit access
* and won't end up here. This handler is primarily intended to catch otherwise
* unknown MSR access for possible later inclusion in the bitmap list. For
* each MSR access that ends up here, we log the access (when VMM_DEBUG is
* enabled)
*
* Parameters:
* vcpu: vcpu structure containing instruction info causing the exit
*
* Return value:
* 0: The operation was successful
* EINVAL: An error occurred
*/
int
vmx_handle_rdmsr(struct vcpu *vcpu)
{
uint64_t insn_length;
uint64_t *rax, *rdx;
uint64_t *rcx;
int ret;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
printf("%s: can't obtain instruction length\n", __func__);
return (EINVAL);
}
if (insn_length != 2) {
DPRINTF("%s: RDMSR with instruction length %lld not "
"supported\n", __func__, insn_length);
return (EINVAL);
}
rax = &vcpu->vc_gueststate.vg_rax;
rcx = &vcpu->vc_gueststate.vg_rcx;
rdx = &vcpu->vc_gueststate.vg_rdx;
switch (*rcx) {
case MSR_BIOS_SIGN:
case MSR_PLATFORM_ID:
/* Ignored */
*rax = 0;
*rdx = 0;
break;
case MSR_CR_PAT:
*rax = (vcpu->vc_shadow_pat & 0xFFFFFFFFULL);
*rdx = (vcpu->vc_shadow_pat >> 32);
break;
default:
/* Unsupported MSRs causes #GP exception, don't advance %rip */
DPRINTF("%s: unsupported rdmsr (msr=0x%llx), injecting #GP\n",
__func__, *rcx);
ret = vmm_inject_gp(vcpu);
return (ret);
}
vcpu->vc_gueststate.vg_rip += insn_length;
return (0);
}
/*
* vmx_handle_xsetbv
*
* VMX-specific part of the xsetbv instruction exit handler
*
* Parameters:
* vcpu: vcpu structure containing instruction info causing the exit
*
* Return value:
* 0: The operation was successful
* EINVAL: An error occurred
*/
int
vmx_handle_xsetbv(struct vcpu *vcpu)
{
uint64_t insn_length, *rax;
int ret;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
printf("%s: can't obtain instruction length\n", __func__);
return (EINVAL);
}
/* All XSETBV instructions are 3 bytes */
if (insn_length != 3) {
DPRINTF("%s: XSETBV with instruction length %lld not "
"supported\n", __func__, insn_length);
return (EINVAL);
}
rax = &vcpu->vc_gueststate.vg_rax;
ret = vmm_handle_xsetbv(vcpu, rax);
vcpu->vc_gueststate.vg_rip += insn_length;
return ret;
}
/*
* svm_handle_xsetbv
*
* SVM-specific part of the xsetbv instruction exit handler
*
* Parameters:
* vcpu: vcpu structure containing instruction info causing the exit
*
* Return value:
* 0: The operation was successful
* EINVAL: An error occurred
*/
int
svm_handle_xsetbv(struct vcpu *vcpu)
{
uint64_t insn_length, *rax;
int ret;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
/* All XSETBV instructions are 3 bytes */
insn_length = 3;
rax = &vmcb->v_rax;
ret = vmm_handle_xsetbv(vcpu, rax);
vcpu->vc_gueststate.vg_rip += insn_length;
return ret;
}
/*
* vmm_handle_xsetbv
*
* Handler for xsetbv instructions. We allow the guest VM to set xcr0 values
* limited to the xsave_mask in use in the host.
*
* Parameters:
* vcpu: vcpu structure containing instruction info causing the exit
* rax: pointer to guest %rax
*
* Return value:
* 0: The operation was successful
* EINVAL: An error occurred
*/
int
vmm_handle_xsetbv(struct vcpu *vcpu, uint64_t *rax)
{
uint64_t *rdx, *rcx, val;
rcx = &vcpu->vc_gueststate.vg_rcx;
rdx = &vcpu->vc_gueststate.vg_rdx;
if (vmm_get_guest_cpu_cpl(vcpu) != 0) {
DPRINTF("%s: guest cpl not zero\n", __func__);
return (vmm_inject_gp(vcpu));
}
if (*rcx != 0) {
DPRINTF("%s: guest specified invalid xcr register number "
"%lld\n", __func__, *rcx);
return (vmm_inject_gp(vcpu));
}
val = *rax + (*rdx << 32);
if (val & ~xsave_mask) {
DPRINTF("%s: guest specified xcr0 outside xsave_mask %lld\n",
__func__, val);
return (vmm_inject_gp(vcpu));
}
vcpu->vc_gueststate.vg_xcr0 = val;
return (0);
}
/*
* vmx_handle_misc_enable_msr
*
* Handler for writes to the MSR_MISC_ENABLE (0x1a0) MSR on Intel CPUs. We
* limit what the guest can write to this MSR (certain hardware-related
* settings like speedstep, etc).
*
* Parameters:
* vcpu: vcpu structure containing information about the wrmsr causing this
* exit
*/
void
vmx_handle_misc_enable_msr(struct vcpu *vcpu)
{
uint64_t *rax, *rdx;
struct vmx_msr_store *msr_store;
rax = &vcpu->vc_gueststate.vg_rax;
rdx = &vcpu->vc_gueststate.vg_rdx;
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
/* Filter out guest writes to TCC, EIST, and xTPR */
*rax &= ~(MISC_ENABLE_TCC | MISC_ENABLE_EIST_ENABLED |
MISC_ENABLE_xTPR_MESSAGE_DISABLE);
msr_store[VCPU_REGS_MISC_ENABLE].vms_data = *rax | (*rdx << 32);
}
/*
* vmx_handle_wrmsr
*
* Handler for wrmsr instructions. This handler logs the access, and discards
* the written data (when VMM_DEBUG is enabled). Any valid wrmsr will not end
* up here (it will be whitelisted in the MSR bitmap).
*
* Parameters:
* vcpu: vcpu structure containing instruction info causing the exit
*
* Return value:
* 0: The operation was successful
* EINVAL: An error occurred
*/
int
vmx_handle_wrmsr(struct vcpu *vcpu)
{
uint64_t insn_length, val;
uint64_t *rax, *rdx, *rcx;
int ret;
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
printf("%s: can't obtain instruction length\n", __func__);
return (EINVAL);
}
if (insn_length != 2) {
DPRINTF("%s: WRMSR with instruction length %lld not "
"supported\n", __func__, insn_length);
return (EINVAL);
}
rax = &vcpu->vc_gueststate.vg_rax;
rcx = &vcpu->vc_gueststate.vg_rcx;
rdx = &vcpu->vc_gueststate.vg_rdx;
val = (*rdx << 32) | (*rax & 0xFFFFFFFFULL);
switch (*rcx) {
case MSR_CR_PAT:
if (!vmm_pat_is_valid(val)) {
ret = vmm_inject_gp(vcpu);
return (ret);
}
vcpu->vc_shadow_pat = val;
break;
case MSR_MISC_ENABLE:
vmx_handle_misc_enable_msr(vcpu);
break;
case MSR_SMM_MONITOR_CTL:
/*
* 34.15.5 - Enabling dual monitor treatment
*
* Unsupported, so inject #GP and return without
* advancing %rip.
*/
ret = vmm_inject_gp(vcpu);
return (ret);
case KVM_MSR_SYSTEM_TIME:
vmm_init_pvclock(vcpu,
(*rax & 0xFFFFFFFFULL) | (*rdx << 32));
break;
#ifdef VMM_DEBUG
default:
/*
* Log the access, to be able to identify unknown MSRs
*/
DPRINTF("%s: wrmsr exit, msr=0x%llx, discarding data "
"written from guest=0x%llx:0x%llx\n", __func__,
*rcx, *rdx, *rax);
#endif /* VMM_DEBUG */
}
vcpu->vc_gueststate.vg_rip += insn_length;
return (0);
}
/*
* svm_handle_msr
*
* Handler for MSR instructions.
*
* Parameters:
* vcpu: vcpu structure containing instruction info causing the exit
*
* Return value:
* Always 0 (successful)
*/
int
svm_handle_msr(struct vcpu *vcpu)
{
uint64_t insn_length, val;
uint64_t *rax, *rcx, *rdx;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
int ret;
/* XXX: Validate RDMSR / WRMSR insn_length */
insn_length = 2;
rax = &vmcb->v_rax;
rcx = &vcpu->vc_gueststate.vg_rcx;
rdx = &vcpu->vc_gueststate.vg_rdx;
if (vmcb->v_exitinfo1 == 1) {
/* WRMSR */
val = (*rdx << 32) | (*rax & 0xFFFFFFFFULL);
switch (*rcx) {
case MSR_CR_PAT:
if (!vmm_pat_is_valid(val)) {
ret = vmm_inject_gp(vcpu);
return (ret);
}
vcpu->vc_shadow_pat = val;
break;
case MSR_EFER:
vmcb->v_efer = *rax | EFER_SVME;
break;
case KVM_MSR_SYSTEM_TIME:
vmm_init_pvclock(vcpu,
(*rax & 0xFFFFFFFFULL) | (*rdx << 32));
break;
default:
/* Log the access, to be able to identify unknown MSRs */
DPRINTF("%s: wrmsr exit, msr=0x%llx, discarding data "
"written from guest=0x%llx:0x%llx\n", __func__,
*rcx, *rdx, *rax);
}
} else {
/* RDMSR */
switch (*rcx) {
case MSR_BIOS_SIGN:
case MSR_INT_PEN_MSG:
case MSR_PLATFORM_ID:
/* Ignored */
*rax = 0;
*rdx = 0;
break;
case MSR_CR_PAT:
*rax = (vcpu->vc_shadow_pat & 0xFFFFFFFFULL);
*rdx = (vcpu->vc_shadow_pat >> 32);
break;
case MSR_DE_CFG:
/* LFENCE serializing bit is set by host */
*rax = DE_CFG_SERIALIZE_LFENCE;
*rdx = 0;
break;
default:
/*
* Unsupported MSRs causes #GP exception, don't advance
* %rip
*/
DPRINTF("%s: unsupported rdmsr (msr=0x%llx), "
"injecting #GP\n", __func__, *rcx);
ret = vmm_inject_gp(vcpu);
return (ret);
}
}
vcpu->vc_gueststate.vg_rip += insn_length;
return (0);
}
/*
* vmm_handle_cpuid
*
* Exit handler for CPUID instruction
*
* Parameters:
* vcpu: vcpu causing the CPUID exit
*
* Return value:
* 0: the exit was processed successfully
* EINVAL: error occurred validating the CPUID instruction arguments
*/
int
vmm_handle_cpuid(struct vcpu *vcpu)
{
uint64_t insn_length, cr4;
uint64_t *rax, *rbx, *rcx, *rdx;
struct vmcb *vmcb;
uint32_t leaf, subleaf, eax, ebx, ecx, edx;
struct vmx_msr_store *msr_store;
int vmm_cpuid_level;
/* what's the cpuid level we support/advertise? */
vmm_cpuid_level = cpuid_level;
if (vmm_cpuid_level < 0x15 && tsc_is_invariant)
vmm_cpuid_level = 0x15;
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT) {
if (vmread(VMCS_INSTRUCTION_LENGTH, &insn_length)) {
DPRINTF("%s: can't obtain instruction length\n",
__func__);
return (EINVAL);
}
if (vmread(VMCS_GUEST_IA32_CR4, &cr4)) {
DPRINTF("%s: can't obtain cr4\n", __func__);
return (EINVAL);
}
rax = &vcpu->vc_gueststate.vg_rax;
/*
* "CPUID leaves above 02H and below 80000000H are only
* visible when IA32_MISC_ENABLE MSR has bit 22 set to its
* default value 0"
*/
msr_store =
(struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
if (msr_store[VCPU_REGS_MISC_ENABLE].vms_data &
MISC_ENABLE_LIMIT_CPUID_MAXVAL)
vmm_cpuid_level = 0x02;
} else {
/* XXX: validate insn_length 2 */
insn_length = 2;
vmcb = (struct vmcb *)vcpu->vc_control_va;
rax = &vmcb->v_rax;
cr4 = vmcb->v_cr4;
}
rbx = &vcpu->vc_gueststate.vg_rbx;
rcx = &vcpu->vc_gueststate.vg_rcx;
rdx = &vcpu->vc_gueststate.vg_rdx;
vcpu->vc_gueststate.vg_rip += insn_length;
leaf = *rax;
subleaf = *rcx;
/*
* "If a value entered for CPUID.EAX is higher than the maximum input
* value for basic or extended function for that processor then the
* data for the highest basic information leaf is returned."
*
* "When CPUID returns the highest basic leaf information as a result
* of an invalid input EAX value, any dependence on input ECX value
* in the basic leaf is honored."
*
* This means if leaf is between vmm_cpuid_level and 0x40000000 (the start
* of the hypervisor info leaves), clamp to vmm_cpuid_level, but without
* altering subleaf. Also, if leaf is greater than the extended function
* info, clamp also to vmm_cpuid_level.
*/
if ((leaf > vmm_cpuid_level && leaf < 0x40000000) ||
(leaf > curcpu()->ci_pnfeatset)) {
DPRINTF("%s: invalid cpuid input leaf 0x%x, guest rip="
"0x%llx - resetting to 0x%x\n", __func__, leaf,
vcpu->vc_gueststate.vg_rip - insn_length,
vmm_cpuid_level);
leaf = vmm_cpuid_level;
}
/* we fake up values in the range (cpuid_level, vmm_cpuid_level] */
if (leaf <= cpuid_level || leaf > 0x80000000)
CPUID_LEAF(leaf, subleaf, eax, ebx, ecx, edx);
else
eax = ebx = ecx = edx = 0;
switch (leaf) {
case 0x00: /* Max level and vendor ID */
*rax = vmm_cpuid_level;
*rbx = *((uint32_t *)&cpu_vendor);
*rdx = *((uint32_t *)&cpu_vendor + 1);
*rcx = *((uint32_t *)&cpu_vendor + 2);
break;
case 0x01: /* Version, brand, feature info */
*rax = cpu_id;
/* mask off host's APIC ID, reset to vcpu id */
*rbx = cpu_ebxfeature & 0x0000FFFF;
*rbx |= (vcpu->vc_id & 0xFF) << 24;
*rcx = (cpu_ecxfeature | CPUIDECX_HV) & VMM_CPUIDECX_MASK;
/* Guest CR4.OSXSAVE determines presence of CPUIDECX_OSXSAVE */
if (cr4 & CR4_OSXSAVE)
*rcx |= CPUIDECX_OSXSAVE;
else
*rcx &= ~CPUIDECX_OSXSAVE;
*rdx = curcpu()->ci_feature_flags & VMM_CPUIDEDX_MASK;
break;
case 0x02: /* Cache and TLB information */
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
case 0x03: /* Processor serial number (not supported) */
DPRINTF("%s: function 0x03 (processor serial number) not "
"supported\n", __func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x04: /* Deterministic cache info */
*rax = eax & VMM_CPUID4_CACHE_TOPOLOGY_MASK;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
case 0x05: /* MONITOR/MWAIT (not supported) */
DPRINTF("%s: function 0x05 (monitor/mwait) not supported\n",
__func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x06: /* Thermal / Power management (not supported) */
DPRINTF("%s: function 0x06 (thermal/power mgt) not supported\n",
__func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x07: /* SEFF */
if (subleaf == 0) {
*rax = 0; /* Highest subleaf supported */
*rbx = curcpu()->ci_feature_sefflags_ebx & VMM_SEFF0EBX_MASK;
*rcx = curcpu()->ci_feature_sefflags_ecx & VMM_SEFF0ECX_MASK;
*rdx = curcpu()->ci_feature_sefflags_edx & VMM_SEFF0EDX_MASK;
} else {
/* Unsupported subleaf */
DPRINTF("%s: function 0x07 (SEFF) unsupported subleaf "
"0x%x not supported\n", __func__, subleaf);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
}
break;
case 0x09: /* Direct Cache Access (not supported) */
DPRINTF("%s: function 0x09 (direct cache access) not "
"supported\n", __func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x0a: /* Architectural perf monitoring (not supported) */
DPRINTF("%s: function 0x0a (arch. perf mon) not supported\n",
__func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x0b: /* Extended topology enumeration (not supported) */
DPRINTF("%s: function 0x0b (topology enumeration) not "
"supported\n", __func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x0d: /* Processor ext. state information */
if (subleaf == 0) {
*rax = xsave_mask;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
} else if (subleaf == 1) {
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
} else {
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
}
break;
case 0x0f: /* QoS info (not supported) */
DPRINTF("%s: function 0x0f (QoS info) not supported\n",
__func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x14: /* Processor Trace info (not supported) */
DPRINTF("%s: function 0x14 (processor trace info) not "
"supported\n", __func__);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x15:
if (cpuid_level >= 0x15) {
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
} else {
KASSERT(tsc_is_invariant);
*rax = 1;
*rbx = 100;
*rcx = tsc_frequency / 100;
*rdx = 0;
}
break;
case 0x16: /* Processor frequency info */
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
case 0x40000000: /* Hypervisor information */
*rax = 0;
*rbx = *((uint32_t *)&vmm_hv_signature[0]);
*rcx = *((uint32_t *)&vmm_hv_signature[4]);
*rdx = *((uint32_t *)&vmm_hv_signature[8]);
break;
case 0x40000001: /* KVM hypervisor features */
*rax = (1 << KVM_FEATURE_CLOCKSOURCE2) |
(1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT);
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x80000000: /* Extended function level */
*rax = 0x80000008; /* curcpu()->ci_pnfeatset */
*rbx = 0;
*rcx = 0;
*rdx = 0;
break;
case 0x80000001: /* Extended function info */
*rax = curcpu()->ci_efeature_eax;
*rbx = 0; /* Reserved */
*rcx = curcpu()->ci_efeature_ecx & VMM_ECPUIDECX_MASK;
*rdx = curcpu()->ci_feature_eflags & VMM_FEAT_EFLAGS_MASK;
break;
case 0x80000002: /* Brand string */
*rax = curcpu()->ci_brand[0];
*rbx = curcpu()->ci_brand[1];
*rcx = curcpu()->ci_brand[2];
*rdx = curcpu()->ci_brand[3];
break;
case 0x80000003: /* Brand string */
*rax = curcpu()->ci_brand[4];
*rbx = curcpu()->ci_brand[5];
*rcx = curcpu()->ci_brand[6];
*rdx = curcpu()->ci_brand[7];
break;
case 0x80000004: /* Brand string */
*rax = curcpu()->ci_brand[8];
*rbx = curcpu()->ci_brand[9];
*rcx = curcpu()->ci_brand[10];
*rdx = curcpu()->ci_brand[11];
break;
case 0x80000005: /* Reserved (Intel), cacheinfo (AMD) */
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
case 0x80000006: /* ext. cache info */
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
case 0x80000007: /* apmi */
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
case 0x80000008: /* Phys bits info and topology (AMD) */
*rax = eax;
*rbx = ebx & VMM_AMDSPEC_EBX_MASK;
/* Reset %rcx (topology) */
*rcx = 0;
*rdx = edx;
break;
case 0x8000001d: /* cache topology (AMD) */
*rax = eax;
*rbx = ebx;
*rcx = ecx;
*rdx = edx;
break;
default:
DPRINTF("%s: unsupported rax=0x%llx\n", __func__, *rax);
*rax = 0;
*rbx = 0;
*rcx = 0;
*rdx = 0;
}
if (vmm_softc->mode == VMM_MODE_SVM ||
vmm_softc->mode == VMM_MODE_RVI) {
/*
* update %rax. the rest of the registers get updated in
* svm_enter_guest
*/
vmcb->v_rax = *rax;
}
return (0);
}
/*
* vcpu_run_svm
*
* SVM main loop used to run a VCPU.
*
* Parameters:
* vcpu: The VCPU to run
* vrp: run parameters
*
* Return values:
* 0: The run loop exited and no help is needed from vmd
* EAGAIN: The run loop exited and help from vmd is needed
* EINVAL: an error occurred
*/
int
vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *vrp)
{
int ret = 0;
struct region_descriptor gdt;
struct cpu_info *ci = NULL;
uint64_t exit_reason;
struct schedstate_percpu *spc;
uint16_t irq;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
irq = vrp->vrp_irq;
/*
* If we are returning from userspace (vmd) because we exited
* last time, fix up any needed vcpu state first. Which state
* needs to be fixed up depends on what vmd populated in the
* exit data structure.
*/
if (vrp->vrp_continue) {
switch (vcpu->vc_gueststate.vg_exit_reason) {
case SVM_VMEXIT_IOIO:
if (vcpu->vc_exit.vei.vei_dir == VEI_DIR_IN) {
vcpu->vc_gueststate.vg_rax =
vcpu->vc_exit.vei.vei_data;
vmcb->v_rax = vcpu->vc_gueststate.vg_rax;
}
break;
case SVM_VMEXIT_NPF:
ret = vcpu_writeregs_svm(vcpu, VM_RWREGS_GPRS,
&vcpu->vc_exit.vrs);
if (ret) {
printf("%s: vm %d vcpu %d failed to update "
"registers\n", __func__,
vcpu->vc_parent->vm_id, vcpu->vc_id);
return (EINVAL);
}
break;
}
memset(&vcpu->vc_exit, 0, sizeof(vcpu->vc_exit));
}
while (ret == 0) {
vmm_update_pvclock(vcpu);
if (ci != curcpu()) {
/*
* We are launching for the first time, or we are
* resuming from a different pcpu, so we need to
* reset certain pcpu-specific values.
*/
ci = curcpu();
setregion(&gdt, ci->ci_gdt, GDT_SIZE - 1);
if (ci != vcpu->vc_last_pcpu) {
/*
* Flush TLB by guest ASID if feature
* available, flush entire TLB if not.
*/
if (ci->ci_vmm_cap.vcc_svm.svm_flush_by_asid)
vmcb->v_tlb_control =
SVM_TLB_CONTROL_FLUSH_ASID;
else
vmcb->v_tlb_control =
SVM_TLB_CONTROL_FLUSH_ALL;
svm_set_dirty(vcpu, SVM_CLEANBITS_ALL);
}
vcpu->vc_last_pcpu = ci;
if (gdt.rd_base == 0) {
ret = EINVAL;
break;
}
}
/* Handle vmd(8) injected interrupts */
/* Is there an interrupt pending injection? */
if (irq != 0xFFFF && vcpu->vc_irqready) {
vmcb->v_eventinj = (irq & 0xFF) | (1 << 31);
irq = 0xFFFF;
}
/* Inject event if present */
if (vcpu->vc_event != 0) {
DPRINTF("%s: inject event %d\n", __func__,
vcpu->vc_event);
vmcb->v_eventinj = 0;
/* Set the "Event Valid" flag for certain vectors */
switch (vcpu->vc_event & 0xFF) {
case VMM_EX_DF:
case VMM_EX_TS:
case VMM_EX_NP:
case VMM_EX_SS:
case VMM_EX_GP:
case VMM_EX_PF:
case VMM_EX_AC:
vmcb->v_eventinj |= (1ULL << 11);
}
vmcb->v_eventinj |= (vcpu->vc_event) | (1 << 31);
vmcb->v_eventinj |= (3ULL << 8); /* Exception */
vcpu->vc_event = 0;
}
TRACEPOINT(vmm, guest_enter, vcpu, vrp);
/* Start / resume the VCPU */
/* Disable interrupts and save the current host FPU state. */
clgi();
if ((ret = vmm_fpurestore(vcpu))) {
stgi();
break;
}
KASSERT(vmcb->v_intercept1 & SVM_INTERCEPT_INTR);
wrmsr(MSR_AMD_VM_HSAVE_PA, vcpu->vc_svm_hsa_pa);
ret = svm_enter_guest(vcpu->vc_control_pa,
&vcpu->vc_gueststate, &gdt);
/*
* On exit, interrupts are disabled, and we are running with
* the guest FPU state still possibly on the CPU. Save the FPU
* state before re-enabling interrupts.
*/
vmm_fpusave(vcpu);
/*
* Enable interrupts now. Note that if the exit was due to INTR
* (external interrupt), the interrupt will be processed now.
*/
stgi();
vcpu->vc_gueststate.vg_rip = vmcb->v_rip;
vmcb->v_tlb_control = SVM_TLB_CONTROL_FLUSH_NONE;
svm_set_clean(vcpu, SVM_CLEANBITS_ALL);
/* If we exited successfully ... */
if (ret == 0) {
exit_reason = vmcb->v_exitcode;
vcpu->vc_gueststate.vg_exit_reason = exit_reason;
TRACEPOINT(vmm, guest_exit, vcpu, vrp, exit_reason);
vcpu->vc_gueststate.vg_rflags = vmcb->v_rflags;
/*
* Handle the exit. This will alter "ret" to EAGAIN if
* the exit handler determines help from vmd is needed.
*/
ret = svm_handle_exit(vcpu);
if (vcpu->vc_gueststate.vg_rflags & PSL_I)
vcpu->vc_irqready = 1;
else
vcpu->vc_irqready = 0;
/*
* If not ready for interrupts, but interrupts pending,
* enable interrupt window exiting.
*/
if (vcpu->vc_irqready == 0 && vcpu->vc_intr) {
vmcb->v_intercept1 |= SVM_INTERCEPT_VINTR;
vmcb->v_irq = 1;
vmcb->v_intr_misc = SVM_INTR_MISC_V_IGN_TPR;
vmcb->v_intr_vector = 0;
svm_set_dirty(vcpu, SVM_CLEANBITS_TPR |
SVM_CLEANBITS_I);
}
/*
* Exit to vmd if we are terminating, failed to enter,
* or need help (device I/O)
*/
if (ret || vcpu_must_stop(vcpu))
break;
if (vcpu->vc_intr && vcpu->vc_irqready) {
ret = EAGAIN;
break;
}
/* Check if we should yield - don't hog the cpu */
spc = &ci->ci_schedstate;
if (spc->spc_schedflags & SPCF_SHOULDYIELD)
break;
}
}
/*
* We are heading back to userspace (vmd), either because we need help
* handling an exit, a guest interrupt is pending, or we failed in some
* way to enter the guest. Copy the guest registers to the exit struct
* and return to vmd.
*/
if (vcpu_readregs_svm(vcpu, VM_RWREGS_ALL, &vcpu->vc_exit.vrs))
ret = EINVAL;
return (ret);
}
/*
* vmm_alloc_vpid
*
* Sets the memory location pointed to by "vpid" to the next available VPID
* or ASID.
*
* Parameters:
* vpid: Pointer to location to receive the next VPID/ASID
*
* Return Values:
* 0: The operation completed successfully
* ENOMEM: No VPIDs/ASIDs were available. Content of 'vpid' is unchanged.
*/
int
vmm_alloc_vpid(uint16_t *vpid)
{
uint16_t i;
uint8_t idx, bit;
struct vmm_softc *sc = vmm_softc;
rw_enter_write(&vmm_softc->vpid_lock);
for (i = 1; i <= sc->max_vpid; i++) {
idx = i / 8;
bit = i - (idx * 8);
if (!(sc->vpids[idx] & (1 << bit))) {
sc->vpids[idx] |= (1 << bit);
*vpid = i;
DPRINTF("%s: allocated VPID/ASID %d\n", __func__,
i);
rw_exit_write(&vmm_softc->vpid_lock);
return 0;
}
}
printf("%s: no available %ss\n", __func__,
(sc->mode == VMM_MODE_EPT || sc->mode == VMM_MODE_VMX) ? "VPID" :
"ASID");
rw_exit_write(&vmm_softc->vpid_lock);
return ENOMEM;
}
/*
* vmm_free_vpid
*
* Frees the VPID/ASID id supplied in "vpid".
*
* Parameters:
* vpid: VPID/ASID to free.
*/
void
vmm_free_vpid(uint16_t vpid)
{
uint8_t idx, bit;
struct vmm_softc *sc = vmm_softc;
rw_enter_write(&vmm_softc->vpid_lock);
idx = vpid / 8;
bit = vpid - (idx * 8);
sc->vpids[idx] &= ~(1 << bit);
DPRINTF("%s: freed VPID/ASID %d\n", __func__, vpid);
rw_exit_write(&vmm_softc->vpid_lock);
}
/* vmm_gpa_is_valid
*
* Check if the given gpa is within guest memory space.
*
* Parameters:
* vcpu: The virtual cpu we are running on.
* gpa: The address to check.
* obj_size: The size of the object assigned to gpa
*
* Return values:
* 1: gpa is within the memory ranges allocated for the vcpu
* 0: otherwise
*/
int
vmm_gpa_is_valid(struct vcpu *vcpu, paddr_t gpa, size_t obj_size)
{
struct vm *vm = vcpu->vc_parent;
struct vm_mem_range *vmr;
size_t i;
for (i = 0; i < vm->vm_nmemranges; ++i) {
vmr = &vm->vm_memranges[i];
if (vmr->vmr_size >= obj_size &&
vmr->vmr_gpa <= gpa &&
gpa < (vmr->vmr_gpa + vmr->vmr_size - obj_size)) {
return 1;
}
}
return 0;
}
void
vmm_init_pvclock(struct vcpu *vcpu, paddr_t gpa)
{
paddr_t pvclock_gpa = gpa & 0xFFFFFFFFFFFFFFF0;
if (!vmm_gpa_is_valid(vcpu, pvclock_gpa,
sizeof(struct pvclock_time_info))) {
/* XXX: Kill guest? */
vmm_inject_gp(vcpu);
return;
}
/* XXX: handle case when this struct goes over page boundaries */
if ((pvclock_gpa & PAGE_MASK) + sizeof(struct pvclock_time_info) >
PAGE_SIZE) {
vmm_inject_gp(vcpu);
return;
}
vcpu->vc_pvclock_system_gpa = gpa;
if (tsc_frequency > 0)
vcpu->vc_pvclock_system_tsc_mul =
(int) ((1000000000L << 20) / tsc_frequency);
else
vcpu->vc_pvclock_system_tsc_mul = 0;
vmm_update_pvclock(vcpu);
}
int
vmm_update_pvclock(struct vcpu *vcpu)
{
struct pvclock_time_info *pvclock_ti;
struct timespec tv;
struct vm *vm = vcpu->vc_parent;
paddr_t pvclock_hpa, pvclock_gpa;
if (vcpu->vc_pvclock_system_gpa & PVCLOCK_SYSTEM_TIME_ENABLE) {
pvclock_gpa = vcpu->vc_pvclock_system_gpa & 0xFFFFFFFFFFFFFFF0;
if (!pmap_extract(vm->vm_map->pmap, pvclock_gpa, &pvclock_hpa))
return (EINVAL);
pvclock_ti = (void*) PMAP_DIRECT_MAP(pvclock_hpa);
/* START next cycle (must be odd) */
pvclock_ti->ti_version =
(++vcpu->vc_pvclock_version << 1) | 0x1;
pvclock_ti->ti_tsc_timestamp = rdtsc();
nanotime(&tv);
pvclock_ti->ti_system_time =
tv.tv_sec * 1000000000L + tv.tv_nsec;
pvclock_ti->ti_tsc_shift = 12;
pvclock_ti->ti_tsc_to_system_mul =
vcpu->vc_pvclock_system_tsc_mul;
pvclock_ti->ti_flags = PVCLOCK_FLAG_TSC_STABLE;
/* END (must be even) */
pvclock_ti->ti_version &= ~0x1;
}
return (0);
}
int
vmm_pat_is_valid(uint64_t pat)
{
int i;
uint8_t *byte = (uint8_t *)&pat;
/* Intel SDM Vol 3A, 11.12.2: 0x02, 0x03, and 0x08-0xFF result in #GP */
for (i = 0; i < 8; i++) {
if (byte[i] == 0x02 || byte[i] == 0x03 || byte[i] > 0x07) {
DPRINTF("%s: invalid pat %llx\n", __func__, pat);
return 0;
}
}
return 1;
}
/*
* vmx_exit_reason_decode
*
* Returns a human readable string describing exit type 'code'
*/
const char *
vmx_exit_reason_decode(uint32_t code)
{
switch (code) {
case VMX_EXIT_NMI: return "NMI";
case VMX_EXIT_EXTINT: return "External interrupt";
case VMX_EXIT_TRIPLE_FAULT: return "Triple fault";
case VMX_EXIT_INIT: return "INIT signal";
case VMX_EXIT_SIPI: return "SIPI signal";
case VMX_EXIT_IO_SMI: return "I/O SMI";
case VMX_EXIT_OTHER_SMI: return "other SMI";
case VMX_EXIT_INT_WINDOW: return "Interrupt window";
case VMX_EXIT_NMI_WINDOW: return "NMI window";
case VMX_EXIT_TASK_SWITCH: return "Task switch";
case VMX_EXIT_CPUID: return "CPUID instruction";
case VMX_EXIT_GETSEC: return "GETSEC instruction";
case VMX_EXIT_HLT: return "HLT instruction";
case VMX_EXIT_INVD: return "INVD instruction";
case VMX_EXIT_INVLPG: return "INVLPG instruction";
case VMX_EXIT_RDPMC: return "RDPMC instruction";
case VMX_EXIT_RDTSC: return "RDTSC instruction";
case VMX_EXIT_RSM: return "RSM instruction";
case VMX_EXIT_VMCALL: return "VMCALL instruction";
case VMX_EXIT_VMCLEAR: return "VMCLEAR instruction";
case VMX_EXIT_VMLAUNCH: return "VMLAUNCH instruction";
case VMX_EXIT_VMPTRLD: return "VMPTRLD instruction";
case VMX_EXIT_VMPTRST: return "VMPTRST instruction";
case VMX_EXIT_VMREAD: return "VMREAD instruction";
case VMX_EXIT_VMRESUME: return "VMRESUME instruction";
case VMX_EXIT_VMWRITE: return "VMWRITE instruction";
case VMX_EXIT_VMXOFF: return "VMXOFF instruction";
case VMX_EXIT_VMXON: return "VMXON instruction";
case VMX_EXIT_CR_ACCESS: return "CR access";
case VMX_EXIT_MOV_DR: return "MOV DR instruction";
case VMX_EXIT_IO: return "I/O instruction";
case VMX_EXIT_RDMSR: return "RDMSR instruction";
case VMX_EXIT_WRMSR: return "WRMSR instruction";
case VMX_EXIT_ENTRY_FAILED_GUEST_STATE: return "guest state invalid";
case VMX_EXIT_ENTRY_FAILED_MSR_LOAD: return "MSR load failed";
case VMX_EXIT_MWAIT: return "MWAIT instruction";
case VMX_EXIT_MTF: return "monitor trap flag";
case VMX_EXIT_MONITOR: return "MONITOR instruction";
case VMX_EXIT_PAUSE: return "PAUSE instruction";
case VMX_EXIT_ENTRY_FAILED_MCE: return "MCE during entry";
case VMX_EXIT_TPR_BELOW_THRESHOLD: return "TPR below threshold";
case VMX_EXIT_APIC_ACCESS: return "APIC access";
case VMX_EXIT_VIRTUALIZED_EOI: return "virtualized EOI";
case VMX_EXIT_GDTR_IDTR: return "GDTR/IDTR access";
case VMX_EXIT_LDTR_TR: return "LDTR/TR access";
case VMX_EXIT_EPT_VIOLATION: return "EPT violation";
case VMX_EXIT_EPT_MISCONFIGURATION: return "EPT misconfiguration";
case VMX_EXIT_INVEPT: return "INVEPT instruction";
case VMX_EXIT_RDTSCP: return "RDTSCP instruction";
case VMX_EXIT_VMX_PREEMPTION_TIMER_EXPIRED:
return "preemption timer expired";
case VMX_EXIT_INVVPID: return "INVVPID instruction";
case VMX_EXIT_WBINVD: return "WBINVD instruction";
case VMX_EXIT_XSETBV: return "XSETBV instruction";
case VMX_EXIT_APIC_WRITE: return "APIC write";
case VMX_EXIT_RDRAND: return "RDRAND instruction";
case VMX_EXIT_INVPCID: return "INVPCID instruction";
case VMX_EXIT_VMFUNC: return "VMFUNC instruction";
case VMX_EXIT_RDSEED: return "RDSEED instruction";
case VMX_EXIT_XSAVES: return "XSAVES instruction";
case VMX_EXIT_XRSTORS: return "XRSTORS instruction";
default: return "unknown";
}
}
/*
* svm_exit_reason_decode
*
* Returns a human readable string describing exit type 'code'
*/
const char *
svm_exit_reason_decode(uint32_t code)
{
switch (code) {
case SVM_VMEXIT_CR0_READ: return "CR0 read"; /* 0x00 */
case SVM_VMEXIT_CR1_READ: return "CR1 read"; /* 0x01 */
case SVM_VMEXIT_CR2_READ: return "CR2 read"; /* 0x02 */
case SVM_VMEXIT_CR3_READ: return "CR3 read"; /* 0x03 */
case SVM_VMEXIT_CR4_READ: return "CR4 read"; /* 0x04 */
case SVM_VMEXIT_CR5_READ: return "CR5 read"; /* 0x05 */
case SVM_VMEXIT_CR6_READ: return "CR6 read"; /* 0x06 */
case SVM_VMEXIT_CR7_READ: return "CR7 read"; /* 0x07 */
case SVM_VMEXIT_CR8_READ: return "CR8 read"; /* 0x08 */
case SVM_VMEXIT_CR9_READ: return "CR9 read"; /* 0x09 */
case SVM_VMEXIT_CR10_READ: return "CR10 read"; /* 0x0A */
case SVM_VMEXIT_CR11_READ: return "CR11 read"; /* 0x0B */
case SVM_VMEXIT_CR12_READ: return "CR12 read"; /* 0x0C */
case SVM_VMEXIT_CR13_READ: return "CR13 read"; /* 0x0D */
case SVM_VMEXIT_CR14_READ: return "CR14 read"; /* 0x0E */
case SVM_VMEXIT_CR15_READ: return "CR15 read"; /* 0x0F */
case SVM_VMEXIT_CR0_WRITE: return "CR0 write"; /* 0x10 */
case SVM_VMEXIT_CR1_WRITE: return "CR1 write"; /* 0x11 */
case SVM_VMEXIT_CR2_WRITE: return "CR2 write"; /* 0x12 */
case SVM_VMEXIT_CR3_WRITE: return "CR3 write"; /* 0x13 */
case SVM_VMEXIT_CR4_WRITE: return "CR4 write"; /* 0x14 */
case SVM_VMEXIT_CR5_WRITE: return "CR5 write"; /* 0x15 */
case SVM_VMEXIT_CR6_WRITE: return "CR6 write"; /* 0x16 */
case SVM_VMEXIT_CR7_WRITE: return "CR7 write"; /* 0x17 */
case SVM_VMEXIT_CR8_WRITE: return "CR8 write"; /* 0x18 */
case SVM_VMEXIT_CR9_WRITE: return "CR9 write"; /* 0x19 */
case SVM_VMEXIT_CR10_WRITE: return "CR10 write"; /* 0x1A */
case SVM_VMEXIT_CR11_WRITE: return "CR11 write"; /* 0x1B */
case SVM_VMEXIT_CR12_WRITE: return "CR12 write"; /* 0x1C */
case SVM_VMEXIT_CR13_WRITE: return "CR13 write"; /* 0x1D */
case SVM_VMEXIT_CR14_WRITE: return "CR14 write"; /* 0x1E */
case SVM_VMEXIT_CR15_WRITE: return "CR15 write"; /* 0x1F */
case SVM_VMEXIT_DR0_READ: return "DR0 read"; /* 0x20 */
case SVM_VMEXIT_DR1_READ: return "DR1 read"; /* 0x21 */
case SVM_VMEXIT_DR2_READ: return "DR2 read"; /* 0x22 */
case SVM_VMEXIT_DR3_READ: return "DR3 read"; /* 0x23 */
case SVM_VMEXIT_DR4_READ: return "DR4 read"; /* 0x24 */
case SVM_VMEXIT_DR5_READ: return "DR5 read"; /* 0x25 */
case SVM_VMEXIT_DR6_READ: return "DR6 read"; /* 0x26 */
case SVM_VMEXIT_DR7_READ: return "DR7 read"; /* 0x27 */
case SVM_VMEXIT_DR8_READ: return "DR8 read"; /* 0x28 */
case SVM_VMEXIT_DR9_READ: return "DR9 read"; /* 0x29 */
case SVM_VMEXIT_DR10_READ: return "DR10 read"; /* 0x2A */
case SVM_VMEXIT_DR11_READ: return "DR11 read"; /* 0x2B */
case SVM_VMEXIT_DR12_READ: return "DR12 read"; /* 0x2C */
case SVM_VMEXIT_DR13_READ: return "DR13 read"; /* 0x2D */
case SVM_VMEXIT_DR14_READ: return "DR14 read"; /* 0x2E */
case SVM_VMEXIT_DR15_READ: return "DR15 read"; /* 0x2F */
case SVM_VMEXIT_DR0_WRITE: return "DR0 write"; /* 0x30 */
case SVM_VMEXIT_DR1_WRITE: return "DR1 write"; /* 0x31 */
case SVM_VMEXIT_DR2_WRITE: return "DR2 write"; /* 0x32 */
case SVM_VMEXIT_DR3_WRITE: return "DR3 write"; /* 0x33 */
case SVM_VMEXIT_DR4_WRITE: return "DR4 write"; /* 0x34 */
case SVM_VMEXIT_DR5_WRITE: return "DR5 write"; /* 0x35 */
case SVM_VMEXIT_DR6_WRITE: return "DR6 write"; /* 0x36 */
case SVM_VMEXIT_DR7_WRITE: return "DR7 write"; /* 0x37 */
case SVM_VMEXIT_DR8_WRITE: return "DR8 write"; /* 0x38 */
case SVM_VMEXIT_DR9_WRITE: return "DR9 write"; /* 0x39 */
case SVM_VMEXIT_DR10_WRITE: return "DR10 write"; /* 0x3A */
case SVM_VMEXIT_DR11_WRITE: return "DR11 write"; /* 0x3B */
case SVM_VMEXIT_DR12_WRITE: return "DR12 write"; /* 0x3C */
case SVM_VMEXIT_DR13_WRITE: return "DR13 write"; /* 0x3D */
case SVM_VMEXIT_DR14_WRITE: return "DR14 write"; /* 0x3E */
case SVM_VMEXIT_DR15_WRITE: return "DR15 write"; /* 0x3F */
case SVM_VMEXIT_EXCP0: return "Exception 0x00"; /* 0x40 */
case SVM_VMEXIT_EXCP1: return "Exception 0x01"; /* 0x41 */
case SVM_VMEXIT_EXCP2: return "Exception 0x02"; /* 0x42 */
case SVM_VMEXIT_EXCP3: return "Exception 0x03"; /* 0x43 */
case SVM_VMEXIT_EXCP4: return "Exception 0x04"; /* 0x44 */
case SVM_VMEXIT_EXCP5: return "Exception 0x05"; /* 0x45 */
case SVM_VMEXIT_EXCP6: return "Exception 0x06"; /* 0x46 */
case SVM_VMEXIT_EXCP7: return "Exception 0x07"; /* 0x47 */
case SVM_VMEXIT_EXCP8: return "Exception 0x08"; /* 0x48 */
case SVM_VMEXIT_EXCP9: return "Exception 0x09"; /* 0x49 */
case SVM_VMEXIT_EXCP10: return "Exception 0x0A"; /* 0x4A */
case SVM_VMEXIT_EXCP11: return "Exception 0x0B"; /* 0x4B */
case SVM_VMEXIT_EXCP12: return "Exception 0x0C"; /* 0x4C */
case SVM_VMEXIT_EXCP13: return "Exception 0x0D"; /* 0x4D */
case SVM_VMEXIT_EXCP14: return "Exception 0x0E"; /* 0x4E */
case SVM_VMEXIT_EXCP15: return "Exception 0x0F"; /* 0x4F */
case SVM_VMEXIT_EXCP16: return "Exception 0x10"; /* 0x50 */
case SVM_VMEXIT_EXCP17: return "Exception 0x11"; /* 0x51 */
case SVM_VMEXIT_EXCP18: return "Exception 0x12"; /* 0x52 */
case SVM_VMEXIT_EXCP19: return "Exception 0x13"; /* 0x53 */
case SVM_VMEXIT_EXCP20: return "Exception 0x14"; /* 0x54 */
case SVM_VMEXIT_EXCP21: return "Exception 0x15"; /* 0x55 */
case SVM_VMEXIT_EXCP22: return "Exception 0x16"; /* 0x56 */
case SVM_VMEXIT_EXCP23: return "Exception 0x17"; /* 0x57 */
case SVM_VMEXIT_EXCP24: return "Exception 0x18"; /* 0x58 */
case SVM_VMEXIT_EXCP25: return "Exception 0x19"; /* 0x59 */
case SVM_VMEXIT_EXCP26: return "Exception 0x1A"; /* 0x5A */
case SVM_VMEXIT_EXCP27: return "Exception 0x1B"; /* 0x5B */
case SVM_VMEXIT_EXCP28: return "Exception 0x1C"; /* 0x5C */
case SVM_VMEXIT_EXCP29: return "Exception 0x1D"; /* 0x5D */
case SVM_VMEXIT_EXCP30: return "Exception 0x1E"; /* 0x5E */
case SVM_VMEXIT_EXCP31: return "Exception 0x1F"; /* 0x5F */
case SVM_VMEXIT_INTR: return "External interrupt"; /* 0x60 */
case SVM_VMEXIT_NMI: return "NMI"; /* 0x61 */
case SVM_VMEXIT_SMI: return "SMI"; /* 0x62 */
case SVM_VMEXIT_INIT: return "INIT"; /* 0x63 */
case SVM_VMEXIT_VINTR: return "Interrupt window"; /* 0x64 */
case SVM_VMEXIT_CR0_SEL_WRITE: return "Sel CR0 write"; /* 0x65 */
case SVM_VMEXIT_IDTR_READ: return "IDTR read"; /* 0x66 */
case SVM_VMEXIT_GDTR_READ: return "GDTR read"; /* 0x67 */
case SVM_VMEXIT_LDTR_READ: return "LDTR read"; /* 0x68 */
case SVM_VMEXIT_TR_READ: return "TR read"; /* 0x69 */
case SVM_VMEXIT_IDTR_WRITE: return "IDTR write"; /* 0x6A */
case SVM_VMEXIT_GDTR_WRITE: return "GDTR write"; /* 0x6B */
case SVM_VMEXIT_LDTR_WRITE: return "LDTR write"; /* 0x6C */
case SVM_VMEXIT_TR_WRITE: return "TR write"; /* 0x6D */
case SVM_VMEXIT_RDTSC: return "RDTSC instruction"; /* 0x6E */
case SVM_VMEXIT_RDPMC: return "RDPMC instruction"; /* 0x6F */
case SVM_VMEXIT_PUSHF: return "PUSHF instruction"; /* 0x70 */
case SVM_VMEXIT_POPF: return "POPF instruction"; /* 0x71 */
case SVM_VMEXIT_CPUID: return "CPUID instruction"; /* 0x72 */
case SVM_VMEXIT_RSM: return "RSM instruction"; /* 0x73 */
case SVM_VMEXIT_IRET: return "IRET instruction"; /* 0x74 */
case SVM_VMEXIT_SWINT: return "SWINT instruction"; /* 0x75 */
case SVM_VMEXIT_INVD: return "INVD instruction"; /* 0x76 */
case SVM_VMEXIT_PAUSE: return "PAUSE instruction"; /* 0x77 */
case SVM_VMEXIT_HLT: return "HLT instruction"; /* 0x78 */
case SVM_VMEXIT_INVLPG: return "INVLPG instruction"; /* 0x79 */
case SVM_VMEXIT_INVLPGA: return "INVLPGA instruction"; /* 0x7A */
case SVM_VMEXIT_IOIO: return "I/O instruction"; /* 0x7B */
case SVM_VMEXIT_MSR: return "RDMSR/WRMSR instruction"; /* 0x7C */
case SVM_VMEXIT_TASK_SWITCH: return "Task switch"; /* 0x7D */
case SVM_VMEXIT_FERR_FREEZE: return "FERR_FREEZE"; /* 0x7E */
case SVM_VMEXIT_SHUTDOWN: return "Triple fault"; /* 0x7F */
case SVM_VMEXIT_VMRUN: return "VMRUN instruction"; /* 0x80 */
case SVM_VMEXIT_VMMCALL: return "VMMCALL instruction"; /* 0x81 */
case SVM_VMEXIT_VMLOAD: return "VMLOAD instruction"; /* 0x82 */
case SVM_VMEXIT_VMSAVE: return "VMSAVE instruction"; /* 0x83 */
case SVM_VMEXIT_STGI: return "STGI instruction"; /* 0x84 */
case SVM_VMEXIT_CLGI: return "CLGI instruction"; /* 0x85 */
case SVM_VMEXIT_SKINIT: return "SKINIT instruction"; /* 0x86 */
case SVM_VMEXIT_RDTSCP: return "RDTSCP instruction"; /* 0x87 */
case SVM_VMEXIT_ICEBP: return "ICEBP instruction"; /* 0x88 */
case SVM_VMEXIT_WBINVD: return "WBINVD instruction"; /* 0x89 */
case SVM_VMEXIT_MONITOR: return "MONITOR instruction"; /* 0x8A */
case SVM_VMEXIT_MWAIT: return "MWAIT instruction"; /* 0x8B */
case SVM_VMEXIT_MWAIT_CONDITIONAL: return "Cond MWAIT"; /* 0x8C */
case SVM_VMEXIT_NPF: return "NPT violation"; /* 0x400 */
default: return "unknown";
}
}
/*
* vmx_instruction_error_decode
*
* Returns a human readable string describing the instruction error in 'code'
*/
const char *
vmx_instruction_error_decode(uint32_t code)
{
switch (code) {
case 1: return "VMCALL: unsupported in VMX root";
case 2: return "VMCLEAR: invalid paddr";
case 3: return "VMCLEAR: VMXON pointer";
case 4: return "VMLAUNCH: non-clear VMCS";
case 5: return "VMRESUME: non-launched VMCS";
case 6: return "VMRESUME: executed after VMXOFF";
case 7: return "VM entry: invalid control field(s)";
case 8: return "VM entry: invalid host state field(s)";
case 9: return "VMPTRLD: invalid paddr";
case 10: return "VMPTRLD: VMXON pointer";
case 11: return "VMPTRLD: incorrect VMCS revid";
case 12: return "VMREAD/VMWRITE: unsupported VMCS field";
case 13: return "VMWRITE: RO VMCS field";
case 15: return "VMXON: unsupported in VMX root";
case 20: return "VMCALL: invalid VM exit control fields";
case 26: return "VM entry: blocked by MOV SS";
case 28: return "Invalid operand to INVEPT/INVVPID";
case 0x80000021: return "VM entry: invalid guest state";
case 0x80000022: return "VM entry: failure due to MSR loading";
case 0x80000029: return "VM entry: machine-check event";
default: return "unknown";
}
}
/*
* vcpu_state_decode
*
* Returns a human readable string describing the vcpu state in 'state'.
*/
const char *
vcpu_state_decode(u_int state)
{
switch (state) {
case VCPU_STATE_STOPPED: return "stopped";
case VCPU_STATE_RUNNING: return "running";
case VCPU_STATE_REQTERM: return "requesting termination";
case VCPU_STATE_TERMINATED: return "terminated";
case VCPU_STATE_UNKNOWN: return "unknown";
default: return "invalid";
}
}
#ifdef VMM_DEBUG
/*
* dump_vcpu
*
* Dumps the VMX capabilities of vcpu 'vcpu'
*/
void
dump_vcpu(struct vcpu *vcpu)
{
printf("vcpu @ %p\n", vcpu);
printf(" parent vm @ %p\n", vcpu->vc_parent);
printf(" mode: ");
if (vcpu->vc_virt_mode == VMM_MODE_VMX ||
vcpu->vc_virt_mode == VMM_MODE_EPT) {
printf("VMX\n");
printf(" pinbased ctls: 0x%llx\n",
vcpu->vc_vmx_pinbased_ctls);
printf(" true pinbased ctls: 0x%llx\n",
vcpu->vc_vmx_true_pinbased_ctls);
CTRL_DUMP(vcpu, PINBASED, EXTERNAL_INT_EXITING);
CTRL_DUMP(vcpu, PINBASED, NMI_EXITING);
CTRL_DUMP(vcpu, PINBASED, VIRTUAL_NMIS);
CTRL_DUMP(vcpu, PINBASED, ACTIVATE_VMX_PREEMPTION_TIMER);
CTRL_DUMP(vcpu, PINBASED, PROCESS_POSTED_INTERRUPTS);
printf(" procbased ctls: 0x%llx\n",
vcpu->vc_vmx_procbased_ctls);
printf(" true procbased ctls: 0x%llx\n",
vcpu->vc_vmx_true_procbased_ctls);
CTRL_DUMP(vcpu, PROCBASED, INTERRUPT_WINDOW_EXITING);
CTRL_DUMP(vcpu, PROCBASED, USE_TSC_OFFSETTING);
CTRL_DUMP(vcpu, PROCBASED, HLT_EXITING);
CTRL_DUMP(vcpu, PROCBASED, INVLPG_EXITING);
CTRL_DUMP(vcpu, PROCBASED, MWAIT_EXITING);
CTRL_DUMP(vcpu, PROCBASED, RDPMC_EXITING);
CTRL_DUMP(vcpu, PROCBASED, RDTSC_EXITING);
CTRL_DUMP(vcpu, PROCBASED, CR3_LOAD_EXITING);
CTRL_DUMP(vcpu, PROCBASED, CR3_STORE_EXITING);
CTRL_DUMP(vcpu, PROCBASED, CR8_LOAD_EXITING);
CTRL_DUMP(vcpu, PROCBASED, CR8_STORE_EXITING);
CTRL_DUMP(vcpu, PROCBASED, USE_TPR_SHADOW);
CTRL_DUMP(vcpu, PROCBASED, NMI_WINDOW_EXITING);
CTRL_DUMP(vcpu, PROCBASED, MOV_DR_EXITING);
CTRL_DUMP(vcpu, PROCBASED, UNCONDITIONAL_IO_EXITING);
CTRL_DUMP(vcpu, PROCBASED, USE_IO_BITMAPS);
CTRL_DUMP(vcpu, PROCBASED, MONITOR_TRAP_FLAG);
CTRL_DUMP(vcpu, PROCBASED, USE_MSR_BITMAPS);
CTRL_DUMP(vcpu, PROCBASED, MONITOR_EXITING);
CTRL_DUMP(vcpu, PROCBASED, PAUSE_EXITING);
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1)) {
printf(" procbased2 ctls: 0x%llx\n",
vcpu->vc_vmx_procbased2_ctls);
CTRL_DUMP(vcpu, PROCBASED2, VIRTUALIZE_APIC);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_EPT);
CTRL_DUMP(vcpu, PROCBASED2, DESCRIPTOR_TABLE_EXITING);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_RDTSCP);
CTRL_DUMP(vcpu, PROCBASED2, VIRTUALIZE_X2APIC_MODE);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_VPID);
CTRL_DUMP(vcpu, PROCBASED2, WBINVD_EXITING);
CTRL_DUMP(vcpu, PROCBASED2, UNRESTRICTED_GUEST);
CTRL_DUMP(vcpu, PROCBASED2,
APIC_REGISTER_VIRTUALIZATION);
CTRL_DUMP(vcpu, PROCBASED2,
VIRTUAL_INTERRUPT_DELIVERY);
CTRL_DUMP(vcpu, PROCBASED2, PAUSE_LOOP_EXITING);
CTRL_DUMP(vcpu, PROCBASED2, RDRAND_EXITING);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_INVPCID);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_VM_FUNCTIONS);
CTRL_DUMP(vcpu, PROCBASED2, VMCS_SHADOWING);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_ENCLS_EXITING);
CTRL_DUMP(vcpu, PROCBASED2, RDSEED_EXITING);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_PML);
CTRL_DUMP(vcpu, PROCBASED2, EPT_VIOLATION_VE);
CTRL_DUMP(vcpu, PROCBASED2, CONCEAL_VMX_FROM_PT);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_XSAVES_XRSTORS);
CTRL_DUMP(vcpu, PROCBASED2, ENABLE_TSC_SCALING);
}
printf(" entry ctls: 0x%llx\n",
vcpu->vc_vmx_entry_ctls);
printf(" true entry ctls: 0x%llx\n",
vcpu->vc_vmx_true_entry_ctls);
CTRL_DUMP(vcpu, ENTRY, LOAD_DEBUG_CONTROLS);
CTRL_DUMP(vcpu, ENTRY, IA32E_MODE_GUEST);
CTRL_DUMP(vcpu, ENTRY, ENTRY_TO_SMM);
CTRL_DUMP(vcpu, ENTRY, DEACTIVATE_DUAL_MONITOR_TREATMENT);
CTRL_DUMP(vcpu, ENTRY, LOAD_IA32_PERF_GLOBAL_CTRL_ON_ENTRY);
CTRL_DUMP(vcpu, ENTRY, LOAD_IA32_PAT_ON_ENTRY);
CTRL_DUMP(vcpu, ENTRY, LOAD_IA32_EFER_ON_ENTRY);
CTRL_DUMP(vcpu, ENTRY, LOAD_IA32_BNDCFGS_ON_ENTRY);
CTRL_DUMP(vcpu, ENTRY, CONCEAL_VM_ENTRIES_FROM_PT);
printf(" exit ctls: 0x%llx\n",
vcpu->vc_vmx_exit_ctls);
printf(" true exit ctls: 0x%llx\n",
vcpu->vc_vmx_true_exit_ctls);
CTRL_DUMP(vcpu, EXIT, SAVE_DEBUG_CONTROLS);
CTRL_DUMP(vcpu, EXIT, HOST_SPACE_ADDRESS_SIZE);
CTRL_DUMP(vcpu, EXIT, LOAD_IA32_PERF_GLOBAL_CTRL_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, ACKNOWLEDGE_INTERRUPT_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, SAVE_IA32_PAT_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, LOAD_IA32_PAT_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, SAVE_IA32_EFER_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, LOAD_IA32_EFER_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, SAVE_VMX_PREEMPTION_TIMER);
CTRL_DUMP(vcpu, EXIT, CLEAR_IA32_BNDCFGS_ON_EXIT);
CTRL_DUMP(vcpu, EXIT, CONCEAL_VM_EXITS_FROM_PT);
}
}
/*
* vmx_dump_vmcs_field
*
* Debug function to dump the contents of a single VMCS field
*
* Parameters:
* fieldid: VMCS Field ID
* msg: string to display
*/
void
vmx_dump_vmcs_field(uint16_t fieldid, const char *msg)
{
uint8_t width;
uint64_t val;
DPRINTF("%s (0x%04x): ", msg, fieldid);
if (vmread(fieldid, &val))
DPRINTF("???? ");
else {
/*
* Field width encoding : bits 13:14
*
* 0: 16-bit
* 1: 64-bit
* 2: 32-bit
* 3: natural width
*/
width = (fieldid >> 13) & 0x3;
switch (width) {
case 0: DPRINTF("0x%04llx ", val); break;
case 1:
case 3: DPRINTF("0x%016llx ", val); break;
case 2: DPRINTF("0x%08llx ", val);
}
}
}
/*
* vmx_dump_vmcs
*
* Debug function to dump the contents of the current VMCS.
*/
void
vmx_dump_vmcs(struct vcpu *vcpu)
{
int has_sec, i;
uint32_t cr3_tgt_ct;
/* XXX save and load new vmcs, restore at end */
DPRINTF("--CURRENT VMCS STATE--\n");
printf("VMCS launched: %s\n",
(vcpu->vc_vmx_vmcs_state == VMCS_LAUNCHED) ? "Yes" : "No");
DPRINTF("VMXON revision : 0x%x\n",
curcpu()->ci_vmm_cap.vcc_vmx.vmx_vmxon_revision);
DPRINTF("CR0 fixed0: 0x%llx\n",
curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed0);
DPRINTF("CR0 fixed1: 0x%llx\n",
curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed1);
DPRINTF("CR4 fixed0: 0x%llx\n",
curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0);
DPRINTF("CR4 fixed1: 0x%llx\n",
curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1);
DPRINTF("MSR table size: 0x%x\n",
512 * (curcpu()->ci_vmm_cap.vcc_vmx.vmx_msr_table_size + 1));
has_sec = vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1);
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_VPID, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_VPID, "VPID");
}
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PINBASED_CTLS,
IA32_VMX_PROCESS_POSTED_INTERRUPTS, 1)) {
vmx_dump_vmcs_field(VMCS_POSTED_INT_NOTIF_VECTOR,
"Posted Int Notif Vec");
}
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_EPT_VIOLATION_VE, 1)) {
vmx_dump_vmcs_field(VMCS_EPTP_INDEX, "EPTP idx");
}
}
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_ES_SEL, "G.ES");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CS_SEL, "G.CS");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SS_SEL, "G.SS");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_DS_SEL, "G.DS");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_FS_SEL, "G.FS");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_GS_SEL, "G.GS");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_LDTR_SEL, "LDTR");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_TR_SEL, "G.TR");
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_VIRTUAL_INTERRUPT_DELIVERY, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_INTERRUPT_STATUS,
"Int sts");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_PML, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_PML_INDEX, "PML Idx");
}
}
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_ES_SEL, "H.ES");
vmx_dump_vmcs_field(VMCS_HOST_IA32_CS_SEL, "H.CS");
vmx_dump_vmcs_field(VMCS_HOST_IA32_SS_SEL, "H.SS");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_DS_SEL, "H.DS");
vmx_dump_vmcs_field(VMCS_HOST_IA32_FS_SEL, "H.FS");
vmx_dump_vmcs_field(VMCS_HOST_IA32_GS_SEL, "H.GS");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IO_BITMAP_A, "I/O Bitmap A");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IO_BITMAP_B, "I/O Bitmap B");
DPRINTF("\n");
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_USE_MSR_BITMAPS, 1)) {
vmx_dump_vmcs_field(VMCS_MSR_BITMAP_ADDRESS, "MSR Bitmap");
DPRINTF("\n");
}
vmx_dump_vmcs_field(VMCS_EXIT_STORE_MSR_ADDRESS, "Exit Store MSRs");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EXIT_LOAD_MSR_ADDRESS, "Exit Load MSRs");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_ENTRY_LOAD_MSR_ADDRESS, "Entry Load MSRs");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EXECUTIVE_VMCS_POINTER, "Exec VMCS Ptr");
DPRINTF("\n");
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_PML, 1)) {
vmx_dump_vmcs_field(VMCS_PML_ADDRESS, "PML Addr");
DPRINTF("\n");
}
}
vmx_dump_vmcs_field(VMCS_TSC_OFFSET, "TSC Offset");
DPRINTF("\n");
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_USE_TPR_SHADOW, 1)) {
vmx_dump_vmcs_field(VMCS_VIRTUAL_APIC_ADDRESS,
"Virtual APIC Addr");
DPRINTF("\n");
}
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_VIRTUALIZE_APIC, 1)) {
vmx_dump_vmcs_field(VMCS_APIC_ACCESS_ADDRESS,
"APIC Access Addr");
DPRINTF("\n");
}
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PINBASED_CTLS,
IA32_VMX_PROCESS_POSTED_INTERRUPTS, 1)) {
vmx_dump_vmcs_field(VMCS_POSTED_INTERRUPT_DESC,
"Posted Int Desc Addr");
DPRINTF("\n");
}
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_VM_FUNCTIONS, 1)) {
vmx_dump_vmcs_field(VMCS_VM_FUNCTION_CONTROLS,
"VM Function Controls");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_EPT, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_IA32_EPTP,
"EPT Pointer");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_VIRTUAL_INTERRUPT_DELIVERY, 1)) {
vmx_dump_vmcs_field(VMCS_EOI_EXIT_BITMAP_0,
"EOI Exit Bitmap 0");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EOI_EXIT_BITMAP_1,
"EOI Exit Bitmap 1");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EOI_EXIT_BITMAP_2,
"EOI Exit Bitmap 2");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EOI_EXIT_BITMAP_3,
"EOI Exit Bitmap 3");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_VM_FUNCTIONS, 1)) {
/* We assume all CPUs have the same VMFUNC caps */
if (curcpu()->ci_vmm_cap.vcc_vmx.vmx_vm_func & 0x1) {
vmx_dump_vmcs_field(VMCS_EPTP_LIST_ADDRESS,
"EPTP List Addr");
DPRINTF("\n");
}
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_VMCS_SHADOWING, 1)) {
vmx_dump_vmcs_field(VMCS_VMREAD_BITMAP_ADDRESS,
"VMREAD Bitmap Addr");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_VMWRITE_BITMAP_ADDRESS,
"VMWRITE Bitmap Addr");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_EPT_VIOLATION_VE, 1)) {
vmx_dump_vmcs_field(VMCS_VIRTUALIZATION_EXC_ADDRESS,
"#VE Addr");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_XSAVES_XRSTORS, 1)) {
vmx_dump_vmcs_field(VMCS_XSS_EXITING_BITMAP,
"XSS exiting bitmap addr");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_ENCLS_EXITING, 1)) {
vmx_dump_vmcs_field(VMCS_ENCLS_EXITING_BITMAP,
"Encls exiting bitmap addr");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_TSC_SCALING, 1)) {
vmx_dump_vmcs_field(VMCS_TSC_MULTIPLIER,
"TSC scaling factor");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_EPT, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_PHYSICAL_ADDRESS,
"Guest PA");
DPRINTF("\n");
}
}
vmx_dump_vmcs_field(VMCS_LINK_POINTER, "VMCS Link Pointer");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_DEBUGCTL, "Guest DEBUGCTL");
DPRINTF("\n");
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_ENTRY_CTLS,
IA32_VMX_LOAD_IA32_PAT_ON_ENTRY, 1) ||
vcpu_vmx_check_cap(vcpu, IA32_VMX_EXIT_CTLS,
IA32_VMX_SAVE_IA32_PAT_ON_EXIT, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_IA32_PAT,
"Guest PAT");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_ENTRY_CTLS,
IA32_VMX_LOAD_IA32_EFER_ON_ENTRY, 1) ||
vcpu_vmx_check_cap(vcpu, IA32_VMX_EXIT_CTLS,
IA32_VMX_SAVE_IA32_EFER_ON_EXIT, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_IA32_EFER,
"Guest EFER");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_ENTRY_CTLS,
IA32_VMX_LOAD_IA32_PERF_GLOBAL_CTRL_ON_ENTRY, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_IA32_PERF_GBL_CTRL,
"Guest Perf Global Ctrl");
DPRINTF("\n");
}
if (has_sec) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_ENABLE_EPT, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_PDPTE0, "Guest PDPTE0");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_PDPTE1, "Guest PDPTE1");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_PDPTE2, "Guest PDPTE2");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_PDPTE3, "Guest PDPTE3");
DPRINTF("\n");
}
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_ENTRY_CTLS,
IA32_VMX_LOAD_IA32_BNDCFGS_ON_ENTRY, 1) ||
vcpu_vmx_check_cap(vcpu, IA32_VMX_EXIT_CTLS,
IA32_VMX_CLEAR_IA32_BNDCFGS_ON_EXIT, 1)) {
vmx_dump_vmcs_field(VMCS_GUEST_IA32_BNDCFGS,
"Guest BNDCFGS");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_EXIT_CTLS,
IA32_VMX_LOAD_IA32_PAT_ON_EXIT, 1)) {
vmx_dump_vmcs_field(VMCS_HOST_IA32_PAT,
"Host PAT");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_EXIT_CTLS,
IA32_VMX_LOAD_IA32_EFER_ON_EXIT, 1)) {
vmx_dump_vmcs_field(VMCS_HOST_IA32_EFER,
"Host EFER");
DPRINTF("\n");
}
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_EXIT_CTLS,
IA32_VMX_LOAD_IA32_PERF_GLOBAL_CTRL_ON_EXIT, 1)) {
vmx_dump_vmcs_field(VMCS_HOST_IA32_PERF_GBL_CTRL,
"Host Perf Global Ctrl");
DPRINTF("\n");
}
vmx_dump_vmcs_field(VMCS_PINBASED_CTLS, "Pinbased Ctrls");
vmx_dump_vmcs_field(VMCS_PROCBASED_CTLS, "Procbased Ctrls");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EXCEPTION_BITMAP, "Exception Bitmap");
vmx_dump_vmcs_field(VMCS_PF_ERROR_CODE_MASK, "#PF Err Code Mask");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_PF_ERROR_CODE_MATCH, "#PF Err Code Match");
vmx_dump_vmcs_field(VMCS_CR3_TARGET_COUNT, "CR3 Tgt Count");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EXIT_CTLS, "Exit Ctrls");
vmx_dump_vmcs_field(VMCS_EXIT_MSR_STORE_COUNT, "Exit MSR Store Ct");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EXIT_MSR_LOAD_COUNT, "Exit MSR Load Ct");
vmx_dump_vmcs_field(VMCS_ENTRY_CTLS, "Entry Ctrls");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_ENTRY_MSR_LOAD_COUNT, "Entry MSR Load Ct");
vmx_dump_vmcs_field(VMCS_ENTRY_INTERRUPTION_INFO, "Entry Int. Info");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_ENTRY_EXCEPTION_ERROR_CODE,
"Entry Ex. Err Code");
vmx_dump_vmcs_field(VMCS_ENTRY_INSTRUCTION_LENGTH, "Entry Insn Len");
DPRINTF("\n");
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_USE_TPR_SHADOW, 1)) {
vmx_dump_vmcs_field(VMCS_TPR_THRESHOLD, "TPR Threshold");
DPRINTF("\n");
}
if (has_sec) {
vmx_dump_vmcs_field(VMCS_PROCBASED2_CTLS, "2ndary Ctrls");
DPRINTF("\n");
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
IA32_VMX_PAUSE_LOOP_EXITING, 1)) {
vmx_dump_vmcs_field(VMCS_PLE_GAP, "PLE Gap");
vmx_dump_vmcs_field(VMCS_PLE_WINDOW, "PLE Window");
}
DPRINTF("\n");
}
vmx_dump_vmcs_field(VMCS_INSTRUCTION_ERROR, "Insn Error");
vmx_dump_vmcs_field(VMCS_EXIT_REASON, "Exit Reason");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_EXIT_INTERRUPTION_INFO, "Exit Int. Info");
vmx_dump_vmcs_field(VMCS_EXIT_INTERRUPTION_ERR_CODE,
"Exit Int. Err Code");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IDT_VECTORING_INFO, "IDT vect info");
vmx_dump_vmcs_field(VMCS_IDT_VECTORING_ERROR_CODE,
"IDT vect err code");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_INSTRUCTION_LENGTH, "Insn Len");
vmx_dump_vmcs_field(VMCS_EXIT_INSTRUCTION_INFO, "Exit Insn Info");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_ES_LIMIT, "G. ES Lim");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CS_LIMIT, "G. CS Lim");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SS_LIMIT, "G. SS Lim");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_DS_LIMIT, "G. DS Lim");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_FS_LIMIT, "G. FS Lim");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_GS_LIMIT, "G. GS Lim");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_LDTR_LIMIT, "G. LDTR Lim");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_TR_LIMIT, "G. TR Lim");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_GDTR_LIMIT, "G. GDTR Lim");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_IDTR_LIMIT, "G. IDTR Lim");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_ES_AR, "G. ES AR");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CS_AR, "G. CS AR");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SS_AR, "G. SS AR");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_DS_AR, "G. DS AR");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_FS_AR, "G. FS AR");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_GS_AR, "G. GS AR");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_LDTR_AR, "G. LDTR AR");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_TR_AR, "G. TR AR");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_INTERRUPTIBILITY_ST, "G. Int St.");
vmx_dump_vmcs_field(VMCS_GUEST_ACTIVITY_STATE, "G. Act St.");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_SMBASE, "G. SMBASE");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SYSENTER_CS, "G. SYSENTER CS");
DPRINTF("\n");
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PINBASED_CTLS,
IA32_VMX_ACTIVATE_VMX_PREEMPTION_TIMER, 1)) {
vmx_dump_vmcs_field(VMCS_VMX_PREEMPTION_TIMER_VAL,
"VMX Preempt Timer");
DPRINTF("\n");
}
vmx_dump_vmcs_field(VMCS_HOST_IA32_SYSENTER_CS, "H. SYSENTER CS");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_CR0_MASK, "CR0 Mask");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_CR4_MASK, "CR4 Mask");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_CR0_READ_SHADOW, "CR0 RD Shadow");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_CR4_READ_SHADOW, "CR4 RD Shadow");
DPRINTF("\n");
/* We assume all CPUs have the same max CR3 target ct */
cr3_tgt_ct = curcpu()->ci_vmm_cap.vcc_vmx.vmx_cr3_tgt_count;
DPRINTF("Max CR3 target count: 0x%x\n", cr3_tgt_ct);
if (cr3_tgt_ct <= VMX_MAX_CR3_TARGETS) {
for (i = 0 ; i < cr3_tgt_ct; i++) {
vmx_dump_vmcs_field(VMCS_CR3_TARGET_0 + (2 * i),
"CR3 Target");
DPRINTF("\n");
}
} else {
DPRINTF("(Bogus CR3 Target Count > %d", VMX_MAX_CR3_TARGETS);
}
vmx_dump_vmcs_field(VMCS_GUEST_EXIT_QUALIFICATION, "G. Exit Qual");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IO_RCX, "I/O RCX");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IO_RSI, "I/O RSI");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IO_RDI, "I/O RDI");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_IO_RIP, "I/O RIP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_LINEAR_ADDRESS, "G. Lin Addr");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CR0, "G. CR0");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CR3, "G. CR3");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CR4, "G. CR4");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_ES_BASE, "G. ES Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_CS_BASE, "G. CS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SS_BASE, "G. SS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_DS_BASE, "G. DS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_FS_BASE, "G. FS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_GS_BASE, "G. GS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_LDTR_BASE, "G. LDTR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_TR_BASE, "G. TR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_GDTR_BASE, "G. GDTR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_IDTR_BASE, "G. IDTR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_DR7, "G. DR7");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_RSP, "G. RSP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_RIP, "G. RIP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_RFLAGS, "G. RFLAGS");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_PENDING_DBG_EXC, "G. Pend Dbg Exc");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SYSENTER_ESP, "G. SYSENTER ESP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_GUEST_IA32_SYSENTER_EIP, "G. SYSENTER EIP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_CR0, "H. CR0");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_CR3, "H. CR3");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_CR4, "H. CR4");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_FS_BASE, "H. FS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_GS_BASE, "H. GS Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_TR_BASE, "H. TR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_GDTR_BASE, "H. GDTR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_IDTR_BASE, "H. IDTR Base");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_SYSENTER_ESP, "H. SYSENTER ESP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_SYSENTER_EIP, "H. SYSENTER EIP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_RSP, "H. RSP");
DPRINTF("\n");
vmx_dump_vmcs_field(VMCS_HOST_IA32_RIP, "H. RIP");
DPRINTF("\n");
}
/*
* vmx_vcpu_dump_regs
*
* Debug function to print vcpu regs from the current vcpu
* note - vmcs for 'vcpu' must be on this pcpu.
*
* Parameters:
* vcpu - vcpu whose registers should be dumped
*/
void
vmx_vcpu_dump_regs(struct vcpu *vcpu)
{
uint64_t r;
int i;
struct vmx_msr_store *msr_store;
/* XXX reformat this for 32 bit guest as needed */
DPRINTF("vcpu @ %p in %s mode\n", vcpu, vmm_decode_cpu_mode(vcpu));
i = vmm_get_guest_cpu_cpl(vcpu);
if (i == -1)
DPRINTF(" CPL=unknown\n");
else
DPRINTF(" CPL=%d\n", i);
DPRINTF(" rax=0x%016llx rbx=0x%016llx rcx=0x%016llx\n",
vcpu->vc_gueststate.vg_rax, vcpu->vc_gueststate.vg_rbx,
vcpu->vc_gueststate.vg_rcx);
DPRINTF(" rdx=0x%016llx rbp=0x%016llx rdi=0x%016llx\n",
vcpu->vc_gueststate.vg_rdx, vcpu->vc_gueststate.vg_rbp,
vcpu->vc_gueststate.vg_rdi);
DPRINTF(" rsi=0x%016llx r8=0x%016llx r9=0x%016llx\n",
vcpu->vc_gueststate.vg_rsi, vcpu->vc_gueststate.vg_r8,
vcpu->vc_gueststate.vg_r9);
DPRINTF(" r10=0x%016llx r11=0x%016llx r12=0x%016llx\n",
vcpu->vc_gueststate.vg_r10, vcpu->vc_gueststate.vg_r11,
vcpu->vc_gueststate.vg_r12);
DPRINTF(" r13=0x%016llx r14=0x%016llx r15=0x%016llx\n",
vcpu->vc_gueststate.vg_r13, vcpu->vc_gueststate.vg_r14,
vcpu->vc_gueststate.vg_r15);
DPRINTF(" rip=0x%016llx rsp=", vcpu->vc_gueststate.vg_rip);
if (vmread(VMCS_GUEST_IA32_RSP, &r))
DPRINTF("(error reading)\n");
else
DPRINTF("0x%016llx\n", r);
DPRINTF(" rflags=");
if (vmread(VMCS_GUEST_IA32_RFLAGS, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%016llx ", r);
vmm_decode_rflags(r);
}
DPRINTF(" cr0=");
if (vmread(VMCS_GUEST_IA32_CR0, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%016llx ", r);
vmm_decode_cr0(r);
}
DPRINTF(" cr2=0x%016llx\n", vcpu->vc_gueststate.vg_cr2);
DPRINTF(" cr3=");
if (vmread(VMCS_GUEST_IA32_CR3, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%016llx ", r);
vmm_decode_cr3(r);
}
DPRINTF(" cr4=");
if (vmread(VMCS_GUEST_IA32_CR4, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%016llx ", r);
vmm_decode_cr4(r);
}
DPRINTF(" --Guest Segment Info--\n");
DPRINTF(" cs=");
if (vmread(VMCS_GUEST_IA32_CS_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx rpl=%lld", r, r & 0x3);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_CS_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_CS_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_CS_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" ds=");
if (vmread(VMCS_GUEST_IA32_DS_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx rpl=%lld", r, r & 0x3);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_DS_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_DS_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_DS_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" es=");
if (vmread(VMCS_GUEST_IA32_ES_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx rpl=%lld", r, r & 0x3);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_ES_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_ES_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_ES_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" fs=");
if (vmread(VMCS_GUEST_IA32_FS_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx rpl=%lld", r, r & 0x3);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_FS_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_FS_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_FS_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" gs=");
if (vmread(VMCS_GUEST_IA32_GS_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx rpl=%lld", r, r & 0x3);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_GS_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_GS_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_GS_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" ss=");
if (vmread(VMCS_GUEST_IA32_SS_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx rpl=%lld", r, r & 0x3);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_SS_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_SS_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_SS_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" tr=");
if (vmread(VMCS_GUEST_IA32_TR_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx", r);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_TR_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_TR_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_TR_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" gdtr base=");
if (vmread(VMCS_GUEST_IA32_GDTR_BASE, &r))
DPRINTF("(error reading) ");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_GDTR_LIMIT, &r))
DPRINTF("(error reading)\n");
else
DPRINTF("0x%016llx\n", r);
DPRINTF(" idtr base=");
if (vmread(VMCS_GUEST_IA32_IDTR_BASE, &r))
DPRINTF("(error reading) ");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_IDTR_LIMIT, &r))
DPRINTF("(error reading)\n");
else
DPRINTF("0x%016llx\n", r);
DPRINTF(" ldtr=");
if (vmread(VMCS_GUEST_IA32_LDTR_SEL, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%04llx", r);
DPRINTF(" base=");
if (vmread(VMCS_GUEST_IA32_LDTR_BASE, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" limit=");
if (vmread(VMCS_GUEST_IA32_LDTR_LIMIT, &r))
DPRINTF("(error reading)");
else
DPRINTF("0x%016llx", r);
DPRINTF(" a/r=");
if (vmread(VMCS_GUEST_IA32_LDTR_AR, &r))
DPRINTF("(error reading)\n");
else {
DPRINTF("0x%04llx\n ", r);
vmm_segment_desc_decode(r);
}
DPRINTF(" --Guest MSRs @ 0x%016llx (paddr: 0x%016llx)--\n",
(uint64_t)vcpu->vc_vmx_msr_exit_save_va,
(uint64_t)vcpu->vc_vmx_msr_exit_save_pa);
msr_store = (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
for (i = 0; i < VMX_NUM_MSR_STORE; i++) {
DPRINTF(" MSR %d @ %p : 0x%08llx (%s), "
"value=0x%016llx ",
i, &msr_store[i], msr_store[i].vms_index,
msr_name_decode(msr_store[i].vms_index),
msr_store[i].vms_data);
vmm_decode_msr_value(msr_store[i].vms_index,
msr_store[i].vms_data);
}
}
/*
* msr_name_decode
*
* Returns a human-readable name for the MSR supplied in 'msr'.
*
* Parameters:
* msr - The MSR to decode
*
* Return value:
* NULL-terminated character string containing the name of the MSR requested
*/
const char *
msr_name_decode(uint32_t msr)
{
/*
* Add as needed. Also consider adding a decode function when
* adding to this table.
*/
switch (msr) {
case MSR_TSC: return "TSC";
case MSR_APICBASE: return "APIC base";
case MSR_IA32_FEATURE_CONTROL: return "IA32 feature control";
case MSR_PERFCTR0: return "perf counter 0";
case MSR_PERFCTR1: return "perf counter 1";
case MSR_TEMPERATURE_TARGET: return "temperature target";
case MSR_MTRRcap: return "MTRR cap";
case MSR_PERF_STATUS: return "perf status";
case MSR_PERF_CTL: return "perf control";
case MSR_MTRRvarBase: return "MTRR variable base";
case MSR_MTRRfix64K_00000: return "MTRR fixed 64K";
case MSR_MTRRfix16K_80000: return "MTRR fixed 16K";
case MSR_MTRRfix4K_C0000: return "MTRR fixed 4K";
case MSR_CR_PAT: return "PAT";
case MSR_MTRRdefType: return "MTRR default type";
case MSR_EFER: return "EFER";
case MSR_STAR: return "STAR";
case MSR_LSTAR: return "LSTAR";
case MSR_CSTAR: return "CSTAR";
case MSR_SFMASK: return "SFMASK";
case MSR_FSBASE: return "FSBASE";
case MSR_GSBASE: return "GSBASE";
case MSR_KERNELGSBASE: return "KGSBASE";
case MSR_MISC_ENABLE: return "Misc Enable";
default: return "Unknown MSR";
}
}
/*
* vmm_segment_desc_decode
*
* Debug function to print segment information for supplied descriptor
*
* Parameters:
* val - The A/R bytes for the segment descriptor to decode
*/
void
vmm_segment_desc_decode(uint64_t val)
{
uint16_t ar;
uint8_t g, type, s, dpl, p, dib, l;
uint32_t unusable;
/* Exit early on unusable descriptors */
unusable = val & 0x10000;
if (unusable) {
DPRINTF("(unusable)\n");
return;
}
ar = (uint16_t)val;
g = (ar & 0x8000) >> 15;
dib = (ar & 0x4000) >> 14;
l = (ar & 0x2000) >> 13;
p = (ar & 0x80) >> 7;
dpl = (ar & 0x60) >> 5;
s = (ar & 0x10) >> 4;
type = (ar & 0xf);
DPRINTF("granularity=%d dib=%d l(64 bit)=%d present=%d sys=%d ",
g, dib, l, p, s);
DPRINTF("type=");
if (!s) {
switch (type) {
case SDT_SYSLDT: DPRINTF("ldt\n"); break;
case SDT_SYS386TSS: DPRINTF("tss (available)\n"); break;
case SDT_SYS386BSY: DPRINTF("tss (busy)\n"); break;
case SDT_SYS386CGT: DPRINTF("call gate\n"); break;
case SDT_SYS386IGT: DPRINTF("interrupt gate\n"); break;
case SDT_SYS386TGT: DPRINTF("trap gate\n"); break;
/* XXX handle 32 bit segment types by inspecting mode */
default: DPRINTF("unknown");
}
} else {
switch (type + 16) {
case SDT_MEMRO: DPRINTF("data, r/o\n"); break;
case SDT_MEMROA: DPRINTF("data, r/o, accessed\n"); break;
case SDT_MEMRW: DPRINTF("data, r/w\n"); break;
case SDT_MEMRWA: DPRINTF("data, r/w, accessed\n"); break;
case SDT_MEMROD: DPRINTF("data, r/o, expand down\n"); break;
case SDT_MEMRODA: DPRINTF("data, r/o, expand down, "
"accessed\n");
break;
case SDT_MEMRWD: DPRINTF("data, r/w, expand down\n"); break;
case SDT_MEMRWDA: DPRINTF("data, r/w, expand down, "
"accessed\n");
break;
case SDT_MEME: DPRINTF("code, x only\n"); break;
case SDT_MEMEA: DPRINTF("code, x only, accessed\n");
case SDT_MEMER: DPRINTF("code, r/x\n"); break;
case SDT_MEMERA: DPRINTF("code, r/x, accessed\n"); break;
case SDT_MEMEC: DPRINTF("code, x only, conforming\n"); break;
case SDT_MEMEAC: DPRINTF("code, x only, conforming, "
"accessed\n");
break;
case SDT_MEMERC: DPRINTF("code, r/x, conforming\n"); break;
case SDT_MEMERAC: DPRINTF("code, r/x, conforming, accessed\n");
break;
}
}
}
void
vmm_decode_cr0(uint64_t cr0)
{
struct vmm_reg_debug_info cr0_info[11] = {
{ CR0_PG, "PG ", "pg " },
{ CR0_CD, "CD ", "cd " },
{ CR0_NW, "NW ", "nw " },
{ CR0_AM, "AM ", "am " },
{ CR0_WP, "WP ", "wp " },
{ CR0_NE, "NE ", "ne " },
{ CR0_ET, "ET ", "et " },
{ CR0_TS, "TS ", "ts " },
{ CR0_EM, "EM ", "em " },
{ CR0_MP, "MP ", "mp " },
{ CR0_PE, "PE", "pe" }
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(cr0_info); i++)
if (cr0 & cr0_info[i].vrdi_bit)
DPRINTF("%s", cr0_info[i].vrdi_present);
else
DPRINTF("%s", cr0_info[i].vrdi_absent);
DPRINTF(")\n");
}
void
vmm_decode_cr3(uint64_t cr3)
{
struct vmm_reg_debug_info cr3_info[2] = {
{ CR3_PWT, "PWT ", "pwt "},
{ CR3_PCD, "PCD", "pcd"}
};
uint64_t cr4;
uint8_t i;
if (vmread(VMCS_GUEST_IA32_CR4, &cr4)) {
DPRINTF("(error)\n");
return;
}
/* If CR4.PCIDE = 0, interpret CR3.PWT and CR3.PCD */
if ((cr4 & CR4_PCIDE) == 0) {
DPRINTF("(");
for (i = 0 ; i < nitems(cr3_info) ; i++)
if (cr3 & cr3_info[i].vrdi_bit)
DPRINTF("%s", cr3_info[i].vrdi_present);
else
DPRINTF("%s", cr3_info[i].vrdi_absent);
DPRINTF(")\n");
} else {
DPRINTF("(pcid=0x%llx)\n", cr3 & 0xFFF);
}
}
void
vmm_decode_cr4(uint64_t cr4)
{
struct vmm_reg_debug_info cr4_info[19] = {
{ CR4_PKE, "PKE ", "pke "},
{ CR4_SMAP, "SMAP ", "smap "},
{ CR4_SMEP, "SMEP ", "smep "},
{ CR4_OSXSAVE, "OSXSAVE ", "osxsave "},
{ CR4_PCIDE, "PCIDE ", "pcide "},
{ CR4_FSGSBASE, "FSGSBASE ", "fsgsbase "},
{ CR4_SMXE, "SMXE ", "smxe "},
{ CR4_VMXE, "VMXE ", "vmxe "},
{ CR4_OSXMMEXCPT, "OSXMMEXCPT ", "osxmmexcpt "},
{ CR4_OSFXSR, "OSFXSR ", "osfxsr "},
{ CR4_PCE, "PCE ", "pce "},
{ CR4_PGE, "PGE ", "pge "},
{ CR4_MCE, "MCE ", "mce "},
{ CR4_PAE, "PAE ", "pae "},
{ CR4_PSE, "PSE ", "pse "},
{ CR4_DE, "DE ", "de "},
{ CR4_TSD, "TSD ", "tsd "},
{ CR4_PVI, "PVI ", "pvi "},
{ CR4_VME, "VME", "vme"}
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(cr4_info); i++)
if (cr4 & cr4_info[i].vrdi_bit)
DPRINTF("%s", cr4_info[i].vrdi_present);
else
DPRINTF("%s", cr4_info[i].vrdi_absent);
DPRINTF(")\n");
}
void
vmm_decode_apicbase_msr_value(uint64_t apicbase)
{
struct vmm_reg_debug_info apicbase_info[3] = {
{ APICBASE_BSP, "BSP ", "bsp "},
{ APICBASE_ENABLE_X2APIC, "X2APIC ", "x2apic "},
{ APICBASE_GLOBAL_ENABLE, "GLB_EN", "glb_en"}
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(apicbase_info); i++)
if (apicbase & apicbase_info[i].vrdi_bit)
DPRINTF("%s", apicbase_info[i].vrdi_present);
else
DPRINTF("%s", apicbase_info[i].vrdi_absent);
DPRINTF(")\n");
}
void
vmm_decode_ia32_fc_value(uint64_t fcr)
{
struct vmm_reg_debug_info fcr_info[4] = {
{ IA32_FEATURE_CONTROL_LOCK, "LOCK ", "lock "},
{ IA32_FEATURE_CONTROL_SMX_EN, "SMX ", "smx "},
{ IA32_FEATURE_CONTROL_VMX_EN, "VMX ", "vmx "},
{ IA32_FEATURE_CONTROL_SENTER_EN, "SENTER ", "senter "}
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(fcr_info); i++)
if (fcr & fcr_info[i].vrdi_bit)
DPRINTF("%s", fcr_info[i].vrdi_present);
else
DPRINTF("%s", fcr_info[i].vrdi_absent);
if (fcr & IA32_FEATURE_CONTROL_SENTER_EN)
DPRINTF(" [SENTER param = 0x%llx]",
(fcr & IA32_FEATURE_CONTROL_SENTER_PARAM_MASK) >> 8);
DPRINTF(")\n");
}
void
vmm_decode_mtrrcap_value(uint64_t val)
{
struct vmm_reg_debug_info mtrrcap_info[3] = {
{ MTRRcap_FIXED, "FIXED ", "fixed "},
{ MTRRcap_WC, "WC ", "wc "},
{ MTRRcap_SMRR, "SMRR ", "smrr "}
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(mtrrcap_info); i++)
if (val & mtrrcap_info[i].vrdi_bit)
DPRINTF("%s", mtrrcap_info[i].vrdi_present);
else
DPRINTF("%s", mtrrcap_info[i].vrdi_absent);
if (val & MTRRcap_FIXED)
DPRINTF(" [nr fixed ranges = 0x%llx]",
(val & 0xff));
DPRINTF(")\n");
}
void
vmm_decode_perf_status_value(uint64_t val)
{
DPRINTF("(pstate ratio = 0x%llx)\n", (val & 0xffff));
}
void vmm_decode_perf_ctl_value(uint64_t val)
{
DPRINTF("(%s ", (val & PERF_CTL_TURBO) ? "TURBO" : "turbo");
DPRINTF("pstate req = 0x%llx)\n", (val & 0xfffF));
}
void
vmm_decode_mtrrdeftype_value(uint64_t mtrrdeftype)
{
struct vmm_reg_debug_info mtrrdeftype_info[2] = {
{ MTRRdefType_FIXED_ENABLE, "FIXED ", "fixed "},
{ MTRRdefType_ENABLE, "ENABLED ", "enabled "},
};
uint8_t i;
int type;
DPRINTF("(");
for (i = 0; i < nitems(mtrrdeftype_info); i++)
if (mtrrdeftype & mtrrdeftype_info[i].vrdi_bit)
DPRINTF("%s", mtrrdeftype_info[i].vrdi_present);
else
DPRINTF("%s", mtrrdeftype_info[i].vrdi_absent);
DPRINTF("type = ");
type = mtrr2mrt(mtrrdeftype & 0xff);
switch (type) {
case MDF_UNCACHEABLE: DPRINTF("UC"); break;
case MDF_WRITECOMBINE: DPRINTF("WC"); break;
case MDF_WRITETHROUGH: DPRINTF("WT"); break;
case MDF_WRITEPROTECT: DPRINTF("RO"); break;
case MDF_WRITEBACK: DPRINTF("WB"); break;
case MDF_UNKNOWN:
default:
DPRINTF("??");
break;
}
DPRINTF(")\n");
}
void
vmm_decode_efer_value(uint64_t efer)
{
struct vmm_reg_debug_info efer_info[4] = {
{ EFER_SCE, "SCE ", "sce "},
{ EFER_LME, "LME ", "lme "},
{ EFER_LMA, "LMA ", "lma "},
{ EFER_NXE, "NXE", "nxe"},
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(efer_info); i++)
if (efer & efer_info[i].vrdi_bit)
DPRINTF("%s", efer_info[i].vrdi_present);
else
DPRINTF("%s", efer_info[i].vrdi_absent);
DPRINTF(")\n");
}
void
vmm_decode_msr_value(uint64_t msr, uint64_t val)
{
switch (msr) {
case MSR_APICBASE: vmm_decode_apicbase_msr_value(val); break;
case MSR_IA32_FEATURE_CONTROL: vmm_decode_ia32_fc_value(val); break;
case MSR_MTRRcap: vmm_decode_mtrrcap_value(val); break;
case MSR_PERF_STATUS: vmm_decode_perf_status_value(val); break;
case MSR_PERF_CTL: vmm_decode_perf_ctl_value(val); break;
case MSR_MTRRdefType: vmm_decode_mtrrdeftype_value(val); break;
case MSR_EFER: vmm_decode_efer_value(val); break;
case MSR_MISC_ENABLE: vmm_decode_misc_enable_value(val); break;
default: DPRINTF("\n");
}
}
void
vmm_decode_rflags(uint64_t rflags)
{
struct vmm_reg_debug_info rflags_info[16] = {
{ PSL_C, "CF ", "cf "},
{ PSL_PF, "PF ", "pf "},
{ PSL_AF, "AF ", "af "},
{ PSL_Z, "ZF ", "zf "},
{ PSL_N, "SF ", "sf "}, /* sign flag */
{ PSL_T, "TF ", "tf "},
{ PSL_I, "IF ", "if "},
{ PSL_D, "DF ", "df "},
{ PSL_V, "OF ", "of "}, /* overflow flag */
{ PSL_NT, "NT ", "nt "},
{ PSL_RF, "RF ", "rf "},
{ PSL_VM, "VM ", "vm "},
{ PSL_AC, "AC ", "ac "},
{ PSL_VIF, "VIF ", "vif "},
{ PSL_VIP, "VIP ", "vip "},
{ PSL_ID, "ID ", "id "},
};
uint8_t i, iopl;
DPRINTF("(");
for (i = 0; i < nitems(rflags_info); i++)
if (rflags & rflags_info[i].vrdi_bit)
DPRINTF("%s", rflags_info[i].vrdi_present);
else
DPRINTF("%s", rflags_info[i].vrdi_absent);
iopl = (rflags & PSL_IOPL) >> 12;
DPRINTF("IOPL=%d", iopl);
DPRINTF(")\n");
}
void
vmm_decode_misc_enable_value(uint64_t misc)
{
struct vmm_reg_debug_info misc_info[10] = {
{ MISC_ENABLE_FAST_STRINGS, "FSE ", "fse "},
{ MISC_ENABLE_TCC, "TCC ", "tcc "},
{ MISC_ENABLE_PERF_MON_AVAILABLE, "PERF ", "perf "},
{ MISC_ENABLE_BTS_UNAVAILABLE, "BTSU ", "btsu "},
{ MISC_ENABLE_PEBS_UNAVAILABLE, "PEBSU ", "pebsu "},
{ MISC_ENABLE_EIST_ENABLED, "EIST ", "eist "},
{ MISC_ENABLE_ENABLE_MONITOR_FSM, "MFSM ", "mfsm "},
{ MISC_ENABLE_LIMIT_CPUID_MAXVAL, "CMAX ", "cmax "},
{ MISC_ENABLE_xTPR_MESSAGE_DISABLE, "xTPRD ", "xtprd "},
{ MISC_ENABLE_XD_BIT_DISABLE, "NXD", "nxd"},
};
uint8_t i;
DPRINTF("(");
for (i = 0; i < nitems(misc_info); i++)
if (misc & misc_info[i].vrdi_bit)
DPRINTF("%s", misc_info[i].vrdi_present);
else
DPRINTF("%s", misc_info[i].vrdi_absent);
DPRINTF(")\n");
}
const char *
vmm_decode_cpu_mode(struct vcpu *vcpu)
{
int mode = vmm_get_guest_cpu_mode(vcpu);
switch (mode) {
case VMM_CPU_MODE_REAL: return "real";
case VMM_CPU_MODE_PROT: return "16 bit protected";
case VMM_CPU_MODE_PROT32: return "32 bit protected";
case VMM_CPU_MODE_COMPAT: return "compatibility";
case VMM_CPU_MODE_LONG: return "long";
default: return "unknown";
}
}
#endif /* VMM_DEBUG */
126
146
217
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
/* $OpenBSD: bus_space.c,v 1.27 2022/06/29 07:44:10 kettenis Exp $ */
/* $NetBSD: bus_space.c,v 1.2 2003/03/14 18:47:53 christos Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
* Simulation Facility, NASA Ames Research Center.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/extent.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
#include <dev/isa/isareg.h>
#include <machine/isa_machdep.h>
extern int pmap_initialized;
/*
* Extent maps to manage I/O and memory space. Allocate
* storage for 16 regions in each, initially. Later, ioport_malloc_safe
* will indicate that it's safe to use malloc() to dynamically allocate
* region descriptors.
*
* N.B. At least two regions are _always_ allocated from the iomem
* extent map; (0 -> ISA hole) and (end of ISA hole -> end of RAM).
*
* The extent maps are not static! Machine-dependent ISA and EISA
* routines need access to them for bus address space allocation.
*/
static long ioport_ex_storage[EXTENT_FIXED_STORAGE_SIZE(16) / sizeof(long)];
static long iomem_ex_storage[EXTENT_FIXED_STORAGE_SIZE(16) / sizeof(long)];
struct extent *ioport_ex;
struct extent *iomem_ex;
static int ioport_malloc_safe;
int x86_mem_add_mapping(bus_addr_t, bus_size_t,
int, bus_space_handle_t *);
u_int8_t x86_bus_space_io_read_1(bus_space_handle_t, bus_size_t);
u_int16_t x86_bus_space_io_read_2(bus_space_handle_t, bus_size_t);
u_int32_t x86_bus_space_io_read_4(bus_space_handle_t, bus_size_t);
u_int64_t x86_bus_space_io_read_8(bus_space_handle_t, bus_size_t);
void x86_bus_space_io_read_multi_1(bus_space_handle_t, bus_size_t,
u_int8_t *, bus_size_t);
void x86_bus_space_io_read_multi_2(bus_space_handle_t, bus_size_t,
u_int16_t *, bus_size_t);
void x86_bus_space_io_read_multi_4(bus_space_handle_t, bus_size_t,
u_int32_t *, bus_size_t);
void x86_bus_space_io_read_multi_8(bus_space_handle_t, bus_size_t,
u_int64_t *, bus_size_t);
void x86_bus_space_io_read_region_1(bus_space_handle_t, bus_size_t,
u_int8_t *, bus_size_t);
void x86_bus_space_io_read_region_2(bus_space_handle_t, bus_size_t,
u_int16_t *, bus_size_t);
void x86_bus_space_io_read_region_4(bus_space_handle_t, bus_size_t,
u_int32_t *, bus_size_t);
void x86_bus_space_io_read_region_8(bus_space_handle_t, bus_size_t,
u_int64_t *, bus_size_t);
void x86_bus_space_io_write_1(bus_space_handle_t, bus_size_t,
u_int8_t);
void x86_bus_space_io_write_2(bus_space_handle_t, bus_size_t,
u_int16_t);
void x86_bus_space_io_write_4(bus_space_handle_t, bus_size_t,
u_int32_t);
void x86_bus_space_io_write_8(bus_space_handle_t, bus_size_t,
u_int64_t);
void x86_bus_space_io_write_multi_1(bus_space_handle_t,
bus_size_t, const u_int8_t *, bus_size_t);
void x86_bus_space_io_write_multi_2(bus_space_handle_t,
bus_size_t, const u_int16_t *, bus_size_t);
void x86_bus_space_io_write_multi_4(bus_space_handle_t,
bus_size_t, const u_int32_t *, bus_size_t);
void x86_bus_space_io_write_multi_8(bus_space_handle_t,
bus_size_t, const u_int64_t *, bus_size_t);
void x86_bus_space_io_write_region_1(bus_space_handle_t,
bus_size_t, const u_int8_t *, bus_size_t);
void x86_bus_space_io_write_region_2(bus_space_handle_t,
bus_size_t, const u_int16_t *, bus_size_t);
void x86_bus_space_io_write_region_4(bus_space_handle_t,
bus_size_t, const u_int32_t *, bus_size_t);
void x86_bus_space_io_write_region_8(bus_space_handle_t,
bus_size_t, const u_int64_t *, bus_size_t);
void x86_bus_space_io_set_multi_1(bus_space_handle_t, bus_size_t,
u_int8_t, size_t);
void x86_bus_space_io_set_multi_2(bus_space_handle_t, bus_size_t,
u_int16_t, size_t);
void x86_bus_space_io_set_multi_4(bus_space_handle_t, bus_size_t,
u_int32_t, size_t);
void x86_bus_space_io_set_multi_8(bus_space_handle_t, bus_size_t,
u_int64_t, size_t);
void x86_bus_space_io_set_region_1(bus_space_handle_t, bus_size_t,
u_int8_t, size_t);
void x86_bus_space_io_set_region_2(bus_space_handle_t, bus_size_t,
u_int16_t, size_t);
void x86_bus_space_io_set_region_4(bus_space_handle_t, bus_size_t,
u_int32_t, size_t);
void x86_bus_space_io_set_region_8(bus_space_handle_t, bus_size_t,
u_int64_t, size_t);
void x86_bus_space_io_copy_1(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void x86_bus_space_io_copy_2(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void x86_bus_space_io_copy_4(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void x86_bus_space_io_copy_8(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void * x86_bus_space_io_vaddr(bus_space_handle_t);
paddr_t x86_bus_space_io_mmap(bus_addr_t, off_t, int, int);
const struct x86_bus_space_ops x86_bus_space_io_ops = {
x86_bus_space_io_read_1,
x86_bus_space_io_read_2,
x86_bus_space_io_read_4,
x86_bus_space_io_read_8,
x86_bus_space_io_read_multi_1,
x86_bus_space_io_read_multi_2,
x86_bus_space_io_read_multi_4,
x86_bus_space_io_read_multi_8,
x86_bus_space_io_read_region_1,
x86_bus_space_io_read_region_2,
x86_bus_space_io_read_region_4,
x86_bus_space_io_read_region_8,
x86_bus_space_io_write_1,
x86_bus_space_io_write_2,
x86_bus_space_io_write_4,
x86_bus_space_io_write_8,
x86_bus_space_io_write_multi_1,
x86_bus_space_io_write_multi_2,
x86_bus_space_io_write_multi_4,
x86_bus_space_io_write_multi_8,
x86_bus_space_io_write_region_1,
x86_bus_space_io_write_region_2,
x86_bus_space_io_write_region_4,
x86_bus_space_io_write_region_8,
x86_bus_space_io_set_multi_1,
x86_bus_space_io_set_multi_2,
x86_bus_space_io_set_multi_4,
x86_bus_space_io_set_multi_8,
x86_bus_space_io_set_region_1,
x86_bus_space_io_set_region_2,
x86_bus_space_io_set_region_4,
x86_bus_space_io_set_region_8,
x86_bus_space_io_copy_1,
x86_bus_space_io_copy_2,
x86_bus_space_io_copy_4,
x86_bus_space_io_copy_8,
x86_bus_space_io_vaddr,
x86_bus_space_io_mmap
};
u_int8_t x86_bus_space_mem_read_1(bus_space_handle_t, bus_size_t);
u_int16_t x86_bus_space_mem_read_2(bus_space_handle_t, bus_size_t);
u_int32_t x86_bus_space_mem_read_4(bus_space_handle_t, bus_size_t);
u_int64_t x86_bus_space_mem_read_8(bus_space_handle_t, bus_size_t);
void x86_bus_space_mem_read_multi_1(bus_space_handle_t, bus_size_t,
u_int8_t *, bus_size_t);
void x86_bus_space_mem_read_multi_2(bus_space_handle_t, bus_size_t,
u_int16_t *, bus_size_t);
void x86_bus_space_mem_read_multi_4(bus_space_handle_t, bus_size_t,
u_int32_t *, bus_size_t);
void x86_bus_space_mem_read_multi_8(bus_space_handle_t, bus_size_t,
u_int64_t *, bus_size_t);
void x86_bus_space_mem_read_region_1(bus_space_handle_t, bus_size_t,
u_int8_t *, bus_size_t);
void x86_bus_space_mem_read_region_2(bus_space_handle_t, bus_size_t,
u_int16_t *, bus_size_t);
void x86_bus_space_mem_read_region_4(bus_space_handle_t, bus_size_t,
u_int32_t *, bus_size_t);
void x86_bus_space_mem_read_region_8(bus_space_handle_t, bus_size_t,
u_int64_t *, bus_size_t);
void x86_bus_space_mem_write_1(bus_space_handle_t, bus_size_t,
u_int8_t);
void x86_bus_space_mem_write_2(bus_space_handle_t, bus_size_t,
u_int16_t);
void x86_bus_space_mem_write_4(bus_space_handle_t, bus_size_t,
u_int32_t);
void x86_bus_space_mem_write_8(bus_space_handle_t, bus_size_t,
u_int64_t);
void x86_bus_space_mem_write_multi_1(bus_space_handle_t,
bus_size_t, const u_int8_t *, bus_size_t);
void x86_bus_space_mem_write_multi_2(bus_space_handle_t,
bus_size_t, const u_int16_t *, bus_size_t);
void x86_bus_space_mem_write_multi_4(bus_space_handle_t,
bus_size_t, const u_int32_t *, bus_size_t);
void x86_bus_space_mem_write_multi_8(bus_space_handle_t,
bus_size_t, const u_int64_t *, bus_size_t);
void x86_bus_space_mem_write_region_1(bus_space_handle_t,
bus_size_t, const u_int8_t *, bus_size_t);
void x86_bus_space_mem_write_region_2(bus_space_handle_t,
bus_size_t, const u_int16_t *, bus_size_t);
void x86_bus_space_mem_write_region_4(bus_space_handle_t,
bus_size_t, const u_int32_t *, bus_size_t);
void x86_bus_space_mem_write_region_8(bus_space_handle_t,
bus_size_t, const u_int64_t *, bus_size_t);
void x86_bus_space_mem_set_multi_1(bus_space_handle_t, bus_size_t,
u_int8_t, size_t);
void x86_bus_space_mem_set_multi_2(bus_space_handle_t, bus_size_t,
u_int16_t, size_t);
void x86_bus_space_mem_set_multi_4(bus_space_handle_t, bus_size_t,
u_int32_t, size_t);
void x86_bus_space_mem_set_multi_8(bus_space_handle_t, bus_size_t,
u_int64_t, size_t);
void x86_bus_space_mem_set_region_1(bus_space_handle_t, bus_size_t,
u_int8_t, size_t);
void x86_bus_space_mem_set_region_2(bus_space_handle_t, bus_size_t,
u_int16_t, size_t);
void x86_bus_space_mem_set_region_4(bus_space_handle_t, bus_size_t,
u_int32_t, size_t);
void x86_bus_space_mem_set_region_8(bus_space_handle_t, bus_size_t,
u_int64_t, size_t);
void x86_bus_space_mem_copy_1(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void x86_bus_space_mem_copy_2(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void x86_bus_space_mem_copy_4(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void x86_bus_space_mem_copy_8(bus_space_handle_t, bus_size_t,
bus_space_handle_t, bus_size_t, size_t);
void * x86_bus_space_mem_vaddr(bus_space_handle_t);
paddr_t x86_bus_space_mem_mmap(bus_addr_t, off_t, int, int);
const struct x86_bus_space_ops x86_bus_space_mem_ops = {
x86_bus_space_mem_read_1,
x86_bus_space_mem_read_2,
x86_bus_space_mem_read_4,
x86_bus_space_mem_read_8,
x86_bus_space_mem_read_multi_1,
x86_bus_space_mem_read_multi_2,
x86_bus_space_mem_read_multi_4,
x86_bus_space_mem_read_multi_8,
x86_bus_space_mem_read_region_1,
x86_bus_space_mem_read_region_2,
x86_bus_space_mem_read_region_4,
x86_bus_space_mem_read_region_8,
x86_bus_space_mem_write_1,
x86_bus_space_mem_write_2,
x86_bus_space_mem_write_4,
x86_bus_space_mem_write_8,
x86_bus_space_mem_write_multi_1,
x86_bus_space_mem_write_multi_2,
x86_bus_space_mem_write_multi_4,
x86_bus_space_mem_write_multi_8,
x86_bus_space_mem_write_region_1,
x86_bus_space_mem_write_region_2,
x86_bus_space_mem_write_region_4,
x86_bus_space_mem_write_region_8,
x86_bus_space_mem_set_multi_1,
x86_bus_space_mem_set_multi_2,
x86_bus_space_mem_set_multi_4,
x86_bus_space_mem_set_multi_8,
x86_bus_space_mem_set_region_1,
x86_bus_space_mem_set_region_2,
x86_bus_space_mem_set_region_4,
x86_bus_space_mem_set_region_8,
x86_bus_space_mem_copy_1,
x86_bus_space_mem_copy_2,
x86_bus_space_mem_copy_4,
x86_bus_space_mem_copy_8,
x86_bus_space_mem_vaddr,
x86_bus_space_mem_mmap
};
void
x86_bus_space_init(void)
{
/*
* Initialize the I/O port and I/O mem extent maps.
* Note: we don't have to check the return value since
* creation of a fixed extent map will never fail (since
* descriptor storage has already been allocated).
*
* N.B. The iomem extent manages _all_ physical addresses
* on the machine. When the amount of RAM is found, the two
* extents of RAM are allocated from the map (0 -> ISA hole
* and end of ISA hole -> end of RAM).
*/
ioport_ex = extent_create("ioport", 0x0, 0xffff, M_DEVBUF,
(caddr_t)ioport_ex_storage, sizeof(ioport_ex_storage),
EX_NOCOALESCE|EX_NOWAIT);
iomem_ex = extent_create("iomem", 0x0, 0xffffffffffff, M_DEVBUF,
(caddr_t)iomem_ex_storage, sizeof(iomem_ex_storage),
EX_NOCOALESCE|EX_NOWAIT);
}
void
x86_bus_space_mallocok(void)
{
ioport_malloc_safe = 1;
}
int
bus_space_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
int error;
struct extent *ex;
/*
* Pick the appropriate extent map.
*/
if (t == X86_BUS_SPACE_IO) {
ex = ioport_ex;
if (flags & BUS_SPACE_MAP_LINEAR)
return (EINVAL);
} else if (t == X86_BUS_SPACE_MEM)
ex = iomem_ex;
else
panic("bus_space_map: bad bus space tag");
/*
* Before we go any further, let's make sure that this
* region is available.
*/
error = extent_alloc_region(ex, bpa, size,
EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0));
if (error)
return (error);
/*
* For I/O space, that's all she wrote.
*/
if (t == X86_BUS_SPACE_IO) {
*bshp = bpa;
return (0);
}
if (bpa >= IOM_BEGIN && (bpa + size) <= IOM_END) {
*bshp = (bus_space_handle_t)ISA_HOLE_VADDR(bpa);
return(0);
}
if (!pmap_initialized && bpa < 0x100000000) {
*bshp = (bus_space_handle_t)PMAP_DIRECT_MAP(bpa);
return(0);
}
/*
* For memory space, map the bus physical address to
* a kernel virtual address.
*/
error = x86_mem_add_mapping(bpa, size, flags, bshp);
if (error) {
if (extent_free(ex, bpa, size, EX_NOWAIT |
(ioport_malloc_safe ? EX_MALLOCOK : 0))) {
printf("bus_space_map: pa 0x%lx, size 0x%lx\n",
bpa, size);
printf("bus_space_map: can't free region\n");
}
}
return (error);
}
int
_bus_space_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
/*
* For I/O space, just fill in the handle.
*/
if (t == X86_BUS_SPACE_IO) {
*bshp = bpa;
return (0);
}
/*
* For memory space, map the bus physical address to
* a kernel virtual address.
*/
return (x86_mem_add_mapping(bpa, size, flags, bshp));
}
int
bus_space_alloc(bus_space_tag_t t, bus_addr_t rstart, bus_addr_t rend,
bus_size_t size, bus_size_t alignment, bus_size_t boundary, int flags,
bus_addr_t *bpap, bus_space_handle_t *bshp)
{
struct extent *ex;
u_long bpa;
int error;
/*
* Pick the appropriate extent map.
*/
if (t == X86_BUS_SPACE_IO) {
ex = ioport_ex;
} else if (t == X86_BUS_SPACE_MEM)
ex = iomem_ex;
else
panic("bus_space_alloc: bad bus space tag");
/*
* Sanity check the allocation against the extent's boundaries.
*/
if (rstart < ex->ex_start || rend > ex->ex_end)
panic("bus_space_alloc: bad region start/end");
/*
* Do the requested allocation.
*/
error = extent_alloc_subregion(ex, rstart, rend, size, alignment,
0, boundary,
EX_FAST | EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0),
&bpa);
if (error)
return (error);
/*
* For I/O space, that's all she wrote.
*/
if (t == X86_BUS_SPACE_IO) {
*bshp = *bpap = bpa;
return (0);
}
/*
* For memory space, map the bus physical address to
* a kernel virtual address.
*/
error = x86_mem_add_mapping(bpa, size, flags, bshp);
if (error) {
if (extent_free(iomem_ex, bpa, size, EX_NOWAIT |
(ioport_malloc_safe ? EX_MALLOCOK : 0))) {
printf("bus_space_alloc: pa 0x%lx, size 0x%lx\n",
bpa, size);
printf("bus_space_alloc: can't free region\n");
}
}
*bpap = bpa;
return (error);
}
int
x86_mem_add_mapping(bus_addr_t bpa, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
paddr_t pa, endpa;
vaddr_t va;
bus_size_t map_size;
int pmap_flags = PMAP_NOCACHE;
pa = trunc_page(bpa);
endpa = round_page(bpa + size);
#ifdef DIAGNOSTIC
if (endpa <= pa && endpa != 0)
panic("bus_mem_add_mapping: overflow");
#endif
map_size = endpa - pa;
va = (vaddr_t)km_alloc(map_size, &kv_any, &kp_none, &kd_nowait);
if (va == 0)
return (ENOMEM);
*bshp = (bus_space_handle_t)(va + (bpa & PGOFSET));
if (flags & BUS_SPACE_MAP_CACHEABLE)
pmap_flags = 0;
else if (flags & BUS_SPACE_MAP_PREFETCHABLE)
pmap_flags = PMAP_WC;
for (; map_size > 0;
pa += PAGE_SIZE, va += PAGE_SIZE, map_size -= PAGE_SIZE)
pmap_kenter_pa(va, pa | pmap_flags, PROT_READ | PROT_WRITE);
pmap_update(pmap_kernel());
return 0;
}
/*
* void _bus_space_unmap(bus_space_tag bst, bus_space_handle bsh,
* bus_size_t size, bus_addr_t *adrp)
*
* This function unmaps memory- or io-space mapped by the function
* _bus_space_map(). This function works nearly as same as
* bus_space_unmap(), but this function does not ask kernel
* built-in extents and returns physical address of the bus space,
* for the convenience of the extra extent manager.
*/
void
_bus_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size,
bus_addr_t *adrp)
{
u_long va, endva;
bus_addr_t bpa;
/*
* Find the correct bus physical address.
*/
if (t == X86_BUS_SPACE_IO) {
bpa = bsh;
} else if (t == X86_BUS_SPACE_MEM) {
bpa = (bus_addr_t)ISA_PHYSADDR(bsh);
if (IOM_BEGIN <= bpa && bpa <= IOM_END)
goto ok;
va = trunc_page(bsh);
endva = round_page(bsh + size);
#ifdef DIAGNOSTIC
if (endva <= va)
panic("_bus_space_unmap: overflow");
#endif
(void) pmap_extract(pmap_kernel(), va, &bpa);
bpa += (bsh & PGOFSET);
pmap_kremove(va, endva - va);
pmap_update(pmap_kernel());
/*
* Free the kernel virtual mapping.
*/
km_free((void *)va, endva - va, &kv_any, &kp_none);
} else
panic("bus_space_unmap: bad bus space tag");
ok:
if (adrp != NULL)
*adrp = bpa;
}
void
bus_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
struct extent *ex;
u_long va, endva;
bus_addr_t bpa;
/*
* Find the correct extent and bus physical address.
*/
if (t == X86_BUS_SPACE_IO) {
ex = ioport_ex;
bpa = bsh;
} else if (t == X86_BUS_SPACE_MEM) {
ex = iomem_ex;
bpa = (bus_addr_t)ISA_PHYSADDR(bsh);
if (IOM_BEGIN <= bpa && bpa <= IOM_END)
goto ok;
if (bsh >= PMAP_DIRECT_BASE && bsh < PMAP_DIRECT_END) {
bpa = PMAP_DIRECT_UNMAP(bsh);
goto ok;
}
va = trunc_page(bsh);
endva = round_page(bsh + size);
#ifdef DIAGNOSTIC
if (endva <= va)
panic("bus_space_unmap: overflow");
#endif
(void)pmap_extract(pmap_kernel(), va, &bpa);
bpa += (bsh & PGOFSET);
pmap_kremove(va, endva - va);
pmap_update(pmap_kernel());
/*
* Free the kernel virtual mapping.
*/
km_free((void *)va, endva - va, &kv_any, &kp_none);
} else
panic("bus_space_unmap: bad bus space tag");
ok:
if (extent_free(ex, bpa, size,
EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0))) {
printf("bus_space_unmap: %s 0x%lx, size 0x%lx\n",
(t == X86_BUS_SPACE_IO) ? "port" : "pa", bpa, size);
printf("bus_space_unmap: can't free region\n");
}
}
void
bus_space_free(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
/* bus_space_unmap() does all that we need to do. */
bus_space_unmap(t, bsh, size);
}
int
bus_space_subregion(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
{
*nbshp = bsh + offset;
return (0);
}
u_int8_t
x86_bus_space_io_read_1(bus_space_handle_t h, bus_size_t o)
{
return (inb(h + o));
}
u_int16_t
x86_bus_space_io_read_2(bus_space_handle_t h, bus_size_t o)
{
return (inw(h + o));
}
u_int32_t
x86_bus_space_io_read_4(bus_space_handle_t h, bus_size_t o)
{
return (inl(h + o));
}
u_int64_t
x86_bus_space_io_read_8(bus_space_handle_t h, bus_size_t o)
{
panic("bus_space_read_8: invalid bus space tag");
}
void
x86_bus_space_io_read_multi_1(bus_space_handle_t h, bus_size_t o,
u_int8_t *ptr, bus_size_t cnt)
{
insb(h + o, ptr, cnt);
}
void
x86_bus_space_io_read_multi_2(bus_space_handle_t h, bus_size_t o,
u_int16_t *ptr, bus_size_t cnt)
{
insw(h + o, ptr, cnt);
}
void
x86_bus_space_io_read_multi_4(bus_space_handle_t h, bus_size_t o,
u_int32_t *ptr, bus_size_t cnt)
{
insl(h + o, ptr, cnt);
}
void
x86_bus_space_io_read_multi_8(bus_space_handle_t h, bus_size_t o,
u_int64_t *ptr, bus_size_t cnt)
{
panic("bus_space_multi_8: invalid bus space tag");
}
void
x86_bus_space_io_read_region_1(bus_space_handle_t h,
bus_size_t o, u_int8_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
int __x;
u_int32_t port = h + o;
__asm volatile(
"1: inb %w1,%%al ;"
" stosb ;"
" incl %1 ;"
" loop 1b" :
"=&a" (__x), "=d" (dummy1), "=D" (dummy2),
"=c" (dummy3) :
"1" (port), "2" (ptr), "3" (cnt) :
"memory");
}
void
x86_bus_space_io_read_region_2(bus_space_handle_t h,
bus_size_t o, u_int16_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
int __x;
u_int32_t port = h + o;
__asm volatile(
"1: inw %w1,%%ax ;"
" stosw ;"
" addl $2,%1 ;"
" loop 1b" :
"=&a" (__x), "=d" (dummy1), "=D" (dummy2),
"=c" (dummy3) :
"1" ((port)), "2" ((ptr)), "3" ((cnt)) :
"memory");
}
void
x86_bus_space_io_read_region_4(bus_space_handle_t h,
bus_size_t o, u_int32_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
int __x;
u_int32_t port = h + o;
__asm volatile(
"1: inl %w1,%%eax ;"
" stosl ;"
" addl $4,%1 ;"
" loop 1b" :
"=&a" (__x), "=d" (dummy1), "=D" (dummy2),
"=c" (dummy3) :
"1" (port), "2" (ptr), "3" (cnt) :
"memory");
}
void
x86_bus_space_io_read_region_8(bus_space_handle_t h,
bus_size_t o, u_int64_t *ptr, bus_size_t cnt)
{
panic("bus_space_read_region_8: invalid bus space tag");
}
void
x86_bus_space_io_write_1(bus_space_handle_t h, bus_size_t o, u_int8_t v)
{
outb(h + o, v);
}
void
x86_bus_space_io_write_2(bus_space_handle_t h, bus_size_t o, u_int16_t v)
{
outw(h + o, v);
}
void
x86_bus_space_io_write_4(bus_space_handle_t h, bus_size_t o, u_int32_t v)
{
outl(h + o, v);
}
void
x86_bus_space_io_write_8(bus_space_handle_t h, bus_size_t o, u_int64_t v)
{
panic("bus_space_write_8: invalid bus space tag");
}
void
x86_bus_space_io_write_multi_1(bus_space_handle_t h,
bus_size_t o, const u_int8_t *ptr, bus_size_t cnt)
{
outsb(h + o, ptr, cnt);
}
void
x86_bus_space_io_write_multi_2(bus_space_handle_t h,
bus_size_t o, const u_int16_t *ptr, bus_size_t cnt)
{
outsw(h + o, ptr, cnt);
}
void
x86_bus_space_io_write_multi_4(bus_space_handle_t h,
bus_size_t o, const u_int32_t *ptr, bus_size_t cnt)
{
outsl(h + o, ptr, cnt);
}
void
x86_bus_space_io_write_multi_8(bus_space_handle_t h,
bus_size_t o, const u_int64_t *ptr, bus_size_t cnt)
{
panic("bus_space_write_multi_8: invalid bus space tag");
}
void
x86_bus_space_io_write_region_1(bus_space_handle_t h,
bus_size_t o, const u_int8_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
int __x;
u_int32_t port = h + o;
__asm volatile(
"1: lodsb ;"
" outb %%al,%w1 ;"
" incl %1 ;"
" loop 1b" :
"=&a" (__x), "=d" (dummy1), "=S" (dummy2),
"=c" (dummy3) :
"1" (port), "2" (ptr), "3" (cnt) :
"memory");
}
void
x86_bus_space_io_write_region_2(bus_space_handle_t h,
bus_size_t o, const u_int16_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
int __x;
u_int32_t port = h + o;
__asm volatile(
"1: lodsw ;"
" outw %%ax,%w1 ;"
" addl $2,%1 ;"
" loop 1b" :
"=&a" (__x), "=d" (dummy1), "=S" (dummy2),
"=c" (dummy3) :
"1" (port), "2" (ptr), "3" (cnt) :
"memory");
}
void
x86_bus_space_io_write_region_4(bus_space_handle_t h,
bus_size_t o, const u_int32_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
int __x;
u_int32_t port = h + o;
__asm volatile(
"1: lodsl ;"
" outl %%eax,%w1 ;"
" addl $4,%1 ;"
" loop 1b" :
"=&a" (__x), "=d" (dummy1), "=S" (dummy2),
"=c" (dummy3) :
"1" (port), "2" (ptr), "3" (cnt) :
"memory");
}
void
x86_bus_space_io_write_region_8(bus_space_handle_t h,
bus_size_t o, const u_int64_t *ptr, bus_size_t cnt)
{
panic("bus_space_write_region_8: invalid bus space tag");
}
void
x86_bus_space_io_set_multi_1(bus_space_handle_t h, bus_size_t o,
u_int8_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
outb(addr, v);
}
void
x86_bus_space_io_set_multi_2(bus_space_handle_t h, bus_size_t o,
u_int16_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
outw(addr, v);
}
void
x86_bus_space_io_set_multi_4(bus_space_handle_t h, bus_size_t o,
u_int32_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
outl(addr, v);
}
void
x86_bus_space_io_set_multi_8(bus_space_handle_t h, bus_size_t o,
u_int64_t v, size_t c)
{
panic("bus_space_set_multi_8: invalid bus space tag");
}
void
x86_bus_space_io_set_region_1(bus_space_handle_t h, bus_size_t o,
u_int8_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr++)
outb(addr, v);
}
void
x86_bus_space_io_set_region_2(bus_space_handle_t h, bus_size_t o,
u_int16_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr += sizeof(v))
outw(addr, v);
}
void
x86_bus_space_io_set_region_4(bus_space_handle_t h, bus_size_t o,
u_int32_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr += sizeof(v))
outl(addr, v);
}
void
x86_bus_space_io_set_region_8(bus_space_handle_t h, bus_size_t o,
u_int64_t v, size_t c)
{
panic("bus_space_set_region_8: invalid bus space tag");
}
void
x86_bus_space_io_copy_1(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1++, addr2++)
outb(addr2, inb(addr1));
} else {
/* dest after src: copy backwards */
for (addr1 += (c - 1), addr2 += (c - 1);
c != 0; c--, addr1--, addr2--)
outb(addr2, inb(addr1));
}
}
void
x86_bus_space_io_copy_2(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1 += 2, addr2 += 2)
outw(addr2, inw(addr1));
} else {
/* dest after src: copy backwards */
for (addr1 += 2 * (c - 1), addr2 += 2 * (c - 1);
c != 0; c--, addr1 -= 2, addr2 -= 2)
outw(addr2, inw(addr1));
}
}
void
x86_bus_space_io_copy_4(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1 += 4, addr2 += 4)
outl(addr2, inl(addr1));
} else {
/* dest after src: copy backwards */
for (addr1 += 4 * (c - 1), addr2 += 4 * (c - 1);
c != 0; c--, addr1 -= 4, addr2 -= 4)
outl(addr2, inl(addr1));
}
}
void
x86_bus_space_io_copy_8(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
panic("bus_space_set_region_8: invalid bus space tag");
}
void *
x86_bus_space_io_vaddr(bus_space_handle_t h)
{
return (NULL);
}
paddr_t
x86_bus_space_io_mmap(bus_addr_t addr, off_t off, int prot, int flags)
{
/* Can't mmap I/O space. */
return (-1);
}
void
x86_bus_space_mem_write_1(bus_space_handle_t h, bus_size_t o, u_int8_t v)
{
*(volatile u_int8_t *)(h + o) = v;
}
void
x86_bus_space_mem_write_2(bus_space_handle_t h, bus_size_t o, u_int16_t v)
{
*(volatile u_int16_t *)(h + o) = v;
}
u_int8_t
x86_bus_space_mem_read_1(bus_space_handle_t h, bus_size_t o)
{
return (*(volatile u_int8_t *)(h + o));
}
u_int16_t
x86_bus_space_mem_read_2(bus_space_handle_t h, bus_size_t o)
{
return (*(volatile u_int16_t *)(h + o));
}
u_int32_t
x86_bus_space_mem_read_4(bus_space_handle_t h, bus_size_t o)
{
return (*(volatile u_int32_t *)(h + o));
}
u_int64_t
x86_bus_space_mem_read_8(bus_space_handle_t h, bus_size_t o)
{
return (*(volatile u_int64_t *)(h + o));
}
void
x86_bus_space_mem_read_multi_1(bus_space_handle_t h, bus_size_t o,
u_int8_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: movb (%2),%%al ;"
" stosb ;"
" loop 1b" :
"=D" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" ((ptr)), "1" ((cnt)), "2" (h + o) :
"memory");
}
void
x86_bus_space_mem_read_multi_2(bus_space_handle_t h, bus_size_t o,
u_int16_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: movw (%2),%%ax ;"
" stosw ;"
" loop 1b" :
"=D" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" ((ptr)), "1" ((cnt)), "2" (h + o) :
"memory");
}
void
x86_bus_space_mem_read_multi_4(bus_space_handle_t h, bus_size_t o,
u_int32_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: movl (%2),%%eax ;"
" stosl ;"
" loop 1b" :
"=D" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" ((ptr)), "1" ((cnt)), "2" (h + o) :
"memory");
}
void
x86_bus_space_mem_read_multi_8(bus_space_handle_t h, bus_size_t o,
u_int64_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: movq (%2),%%rax ;"
" stosq ;"
" loop 1b" :
"=D" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" ((ptr)), "1" ((cnt)), "2" (h + o) :
"memory");
}
void
x86_bus_space_mem_read_region_1(bus_space_handle_t h,
bus_size_t o, u_int8_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsb" :
"=S" (dummy1), "=D" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_read_region_2(bus_space_handle_t h,
bus_size_t o, u_int16_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsw" :
"=S" (dummy1), "=D" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_read_region_4(bus_space_handle_t h,
bus_size_t o, u_int32_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsl" :
"=S" (dummy1), "=D" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_read_region_8(bus_space_handle_t h,
bus_size_t o, u_int64_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsq" :
"=S" (dummy1), "=D" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_write_4(bus_space_handle_t h, bus_size_t o, u_int32_t v)
{
*(volatile u_int32_t *)(h + o) = v;
}
void
x86_bus_space_mem_write_8(bus_space_handle_t h, bus_size_t o, u_int64_t v)
{
*(volatile u_int64_t *)(h + o) = v;
}
void
x86_bus_space_mem_write_multi_1(bus_space_handle_t h,
bus_size_t o, const u_int8_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: lodsb ;"
" movb %%al,(%2) ;"
" loop 1b" :
"=S" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" (ptr), "1" (cnt), "2" (h + o));
}
void
x86_bus_space_mem_write_multi_2(bus_space_handle_t h,
bus_size_t o, const u_int16_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: lodsw ;"
" movw %%ax,(%2) ;"
" loop 1b" :
"=S" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" (ptr), "1" (cnt), "2" (h + o));
}
void
x86_bus_space_mem_write_multi_4(bus_space_handle_t h,
bus_size_t o, const u_int32_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: lodsl ;"
" movl %%eax,(%2) ;"
" loop 1b" :
"=S" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" (ptr), "1" (cnt), "2" (h + o));
}
void
x86_bus_space_mem_write_multi_8(bus_space_handle_t h,
bus_size_t o, const u_int64_t *ptr, bus_size_t cnt)
{
void *dummy1;
int dummy2;
void *dummy3;
int __x;
__asm volatile(
"1: lodsq ;"
" movq %%rax,(%2) ;"
" loop 1b" :
"=S" (dummy1), "=c" (dummy2), "=r" (dummy3), "=&a" (__x) :
"0" (ptr), "1" (cnt), "2" (h + o));
}
void
x86_bus_space_mem_write_region_1(bus_space_handle_t h,
bus_size_t o, const u_int8_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsb" :
"=D" (dummy1), "=S" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_write_region_2(bus_space_handle_t h,
bus_size_t o, const u_int16_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsw" :
"=D" (dummy1), "=S" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_write_region_4(bus_space_handle_t h,
bus_size_t o, const u_int32_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsl" :
"=D" (dummy1), "=S" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_write_region_8(bus_space_handle_t h,
bus_size_t o, const u_int64_t *ptr, bus_size_t cnt)
{
int dummy1;
void *dummy2;
int dummy3;
__asm volatile(
" repne ;"
" movsq" :
"=D" (dummy1), "=S" (dummy2), "=c" (dummy3) :
"0" (h + o), "1" (ptr), "2" (cnt) :
"memory");
}
void
x86_bus_space_mem_set_multi_1(bus_space_handle_t h, bus_size_t o,
u_int8_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
*(volatile u_int8_t *)(addr) = v;
}
void
x86_bus_space_mem_set_multi_2(bus_space_handle_t h, bus_size_t o,
u_int16_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
*(volatile u_int16_t *)(addr) = v;
}
void
x86_bus_space_mem_set_multi_4(bus_space_handle_t h, bus_size_t o,
u_int32_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
*(volatile u_int32_t *)(addr) = v;
}
void
x86_bus_space_mem_set_multi_8(bus_space_handle_t h, bus_size_t o,
u_int64_t v, size_t c)
{
bus_addr_t addr = h + o;
while (c--)
*(volatile u_int64_t *)(addr) = v;
}
void
x86_bus_space_mem_set_region_1(bus_space_handle_t h, bus_size_t o,
u_int8_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr++)
*(volatile u_int8_t *)(addr) = v;
}
void
x86_bus_space_mem_set_region_2(bus_space_handle_t h, bus_size_t o,
u_int16_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr += sizeof(v))
*(volatile u_int16_t *)(addr) = v;
}
void
x86_bus_space_mem_set_region_4(bus_space_handle_t h, bus_size_t o,
u_int32_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr += sizeof(v))
*(volatile u_int32_t *)(addr) = v;
}
void
x86_bus_space_mem_set_region_8(bus_space_handle_t h, bus_size_t o,
u_int64_t v, size_t c)
{
bus_addr_t addr = h + o;
for (; c != 0; c--, addr += sizeof(v))
*(volatile u_int64_t *)(addr) = v;
}
void
x86_bus_space_mem_copy_1( bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1++, addr2++)
*(volatile u_int8_t *)(addr2) =
*(volatile u_int8_t *)(addr1);
} else {
/* dest after src: copy backwards */
for (addr1 += (c - 1), addr2 += (c - 1);
c != 0; c--, addr1--, addr2--)
*(volatile u_int8_t *)(addr2) =
*(volatile u_int8_t *)(addr1);
}
}
void
x86_bus_space_mem_copy_2(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1 += 2, addr2 += 2)
*(volatile u_int16_t *)(addr2) =
*(volatile u_int16_t *)(addr1);
} else {
/* dest after src: copy backwards */
for (addr1 += 2 * (c - 1), addr2 += 2 * (c - 1);
c != 0; c--, addr1 -= 2, addr2 -= 2)
*(volatile u_int16_t *)(addr2) =
*(volatile u_int16_t *)(addr1);
}
}
void
x86_bus_space_mem_copy_4(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1 += 4, addr2 += 4)
*(volatile u_int32_t *)(addr2) =
*(volatile u_int32_t *)(addr1);
} else {
/* dest after src: copy backwards */
for (addr1 += 4 * (c - 1), addr2 += 4 * (c - 1);
c != 0; c--, addr1 -= 4, addr2 -= 4)
*(volatile u_int32_t *)(addr2) =
*(volatile u_int32_t *)(addr1);
}
}
void
x86_bus_space_mem_copy_8(bus_space_handle_t h1, bus_size_t o1,
bus_space_handle_t h2, bus_size_t o2, size_t c)
{
bus_addr_t addr1 = h1 + o1;
bus_addr_t addr2 = h2 + o2;
if (addr1 >= addr2) {
/* src after dest: copy forward */
for (; c != 0; c--, addr1 += 8, addr2 += 8)
*(volatile u_int64_t *)(addr2) =
*(volatile u_int64_t *)(addr1);
} else {
/* dest after src: copy backwards */
for (addr1 += 8 * (c - 1), addr2 += 8 * (c - 1);
c != 0; c--, addr1 -= 8, addr2 -= 8)
*(volatile u_int64_t *)(addr2) =
*(volatile u_int64_t *)(addr1);
}
}
void *
x86_bus_space_mem_vaddr(bus_space_handle_t h)
{
return ((void *)h);
}
paddr_t
x86_bus_space_mem_mmap(bus_addr_t addr, off_t off, int prot, int flags)
{
/*
* "addr" is the base address of the device we're mapping.
* "off" is the offset into that device.
*
* Note we are called for each "page" in the device that
* the upper layers want to map.
*/
return (addr + off);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* $OpenBSD: libkern.h,v 1.36 2020/02/26 14:23:15 visa Exp $ */
/* $NetBSD: libkern.h,v 1.7 1996/03/14 18:52:08 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)libkern.h 8.1 (Berkeley) 6/10/93
*/
#ifndef __LIBKERN_H__
#define __LIBKERN_H__
#include <sys/types.h>
#ifndef LIBKERN_INLINE
#define LIBKERN_INLINE static __inline
#define LIBKERN_BODY
#endif
LIBKERN_INLINE int imax(int, int);
LIBKERN_INLINE int imin(int, int);
LIBKERN_INLINE u_int max(u_int, u_int);
LIBKERN_INLINE u_int min(u_int, u_int);
LIBKERN_INLINE long lmax(long, long);
LIBKERN_INLINE long lmin(long, long);
LIBKERN_INLINE u_long ulmax(u_long, u_long);
LIBKERN_INLINE u_long ulmin(u_long, u_long);
LIBKERN_INLINE int abs(int);
#ifdef LIBKERN_BODY
LIBKERN_INLINE int
imax(int a, int b)
{
return (a > b ? a : b);
}
LIBKERN_INLINE int
imin(int a, int b)
{
return (a < b ? a : b);
}
LIBKERN_INLINE long
lmax(long a, long b)
{
return (a > b ? a : b);
}
LIBKERN_INLINE long
lmin(long a, long b)
{
return (a < b ? a : b);
}
LIBKERN_INLINE u_int
max(u_int a, u_int b)
{
return (a > b ? a : b);
}
LIBKERN_INLINE u_int
min(u_int a, u_int b)
{
return (a < b ? a : b);
}
LIBKERN_INLINE u_long
ulmax(u_long a, u_long b)
{
return (a > b ? a : b);
}
LIBKERN_INLINE u_long
ulmin(u_long a, u_long b)
{
return (a < b ? a : b);
}
LIBKERN_INLINE int
abs(int j)
{
return(j < 0 ? -j : j);
}
#endif
#ifdef NDEBUG /* tradition! */
#define assert(e) ((void)0)
#else
#define assert(e) ((e) ? (void)0 : \
__assert("", __FILE__, __LINE__, #e))
#endif
#define __KASSERTSTR "kernel %sassertion \"%s\" failed: file \"%s\", line %d"
#ifndef DIAGNOSTIC
#define KASSERTMSG(e, msg, ...) ((void)0)
#define KASSERT(e) ((void)0)
#else
#define KASSERTMSG(e, msg, ...) ((e) ? (void)0 : \
panic(__KASSERTSTR " " msg, "diagnostic ", #e, \
__FILE__, __LINE__, ## __VA_ARGS__))
#define KASSERT(e) ((e) ? (void)0 : \
__assert("diagnostic ", __FILE__, __LINE__, #e))
#endif
#ifndef DEBUG
#define KDASSERTMSG(e, msg, ...) ((void)0)
#define KDASSERT(e) ((void)0)
#else
#define KDASSERTMSG(e, msg, ...) ((e) ? (void)0 : \
panic(__KASSERTSTR " " msg, "debugging ", #e, \
__FILE__, __LINE__, ## __VA_ARGS__))
#define KDASSERT(e) ((e) ? (void)0 : \
__assert("debugging ", __FILE__, __LINE__, #e))
#endif
#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \
__attribute__((__unused__))
/* Prototypes for non-quad routines. */
void __assert(const char *, const char *, int, const char *)
__attribute__ ((__noreturn__));
int bcmp(const void *, const void *, size_t);
void bzero(void *, size_t);
void explicit_bzero(void *, size_t);
int ffs(int);
int fls(int);
int flsl(long);
void *memchr(const void *, int, size_t);
int memcmp(const void *, const void *, size_t);
void *memset(void *, int c, size_t len);
u_int32_t random(void);
int scanc(u_int, const u_char *, const u_char [], int);
int skpc(int, size_t, u_char *);
size_t strlen(const char *);
char *strncpy(char *, const char *, size_t)
__attribute__ ((__bounded__(__string__,1,3)));
size_t strnlen(const char *, size_t);
size_t strlcpy(char *, const char *, size_t)
__attribute__ ((__bounded__(__string__,1,3)));
size_t strlcat(char *, const char *, size_t)
__attribute__ ((__bounded__(__string__,1,3)));
int strcmp(const char *, const char *);
int strncmp(const char *, const char *, size_t);
int strncasecmp(const char *, const char *, size_t);
size_t getsn(char *, size_t)
__attribute__ ((__bounded__(__string__,1,2)));
char *strchr(const char *, int);
char *strrchr(const char *, int);
int timingsafe_bcmp(const void *, const void *, size_t);
#endif /* __LIBKERN_H__ */
9
7
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
/* $OpenBSD: ffs_softdep.c,v 1.150 2021/04/28 09:53:53 claudio Exp $ */
/*
* Copyright 1998, 2000 Marshall Kirk McKusick. All Rights Reserved.
*
* The soft updates code is derived from the appendix of a University
* of Michigan technical report (Gregory R. Ganger and Yale N. Patt,
* "Soft Updates: A Solution to the Metadata Update Problem in File
* Systems", CSE-TR-254-95, August 1995).
*
* Further information about soft updates can be obtained from:
*
* Marshall Kirk McKusick http://www.mckusick.com/softdep/
* 1614 Oxford Street mckusick@mckusick.com
* Berkeley, CA 94709-1608 +1-510-843-9542
* USA
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY MARSHALL KIRK MCKUSICK ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL MARSHALL KIRK MCKUSICK BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)ffs_softdep.c 9.59 (McKusick) 6/21/00
* $FreeBSD: src/sys/ufs/ffs/ffs_softdep.c,v 1.86 2001/02/04 16:08:18 phk Exp $
*/
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/specdev.h>
#include <crypto/siphash.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ffs/fs.h>
#include <ufs/ffs/softdep.h>
#include <ufs/ffs/ffs_extern.h>
#include <ufs/ufs/ufs_extern.h>
#define STATIC
/*
* Mapping of dependency structure types to malloc types.
*/
#define D_PAGEDEP 0
#define D_INODEDEP 1
#define D_NEWBLK 2
#define D_BMSAFEMAP 3
#define D_ALLOCDIRECT 4
#define D_INDIRDEP 5
#define D_ALLOCINDIR 6
#define D_FREEFRAG 7
#define D_FREEBLKS 8
#define D_FREEFILE 9
#define D_DIRADD 10
#define D_MKDIR 11
#define D_DIRREM 12
#define D_NEWDIRBLK 13
#define D_LAST 13
/*
* Names of softdep types.
*/
const char *softdep_typenames[] = {
"pagedep",
"inodedep",
"newblk",
"bmsafemap",
"allocdirect",
"indirdep",
"allocindir",
"freefrag",
"freeblks",
"freefile",
"diradd",
"mkdir",
"dirrem",
"newdirblk",
};
#define TYPENAME(type) \
((unsigned)(type) <= D_LAST ? softdep_typenames[type] : "???")
/*
* Finding the current process.
*/
#define CURPROC curproc
/*
* End system adaptation definitions.
*/
/*
* Internal function prototypes.
*/
STATIC void softdep_error(char *, int);
STATIC void drain_output(struct vnode *, int);
STATIC int getdirtybuf(struct buf *, int);
STATIC void clear_remove(struct proc *);
STATIC void clear_inodedeps(struct proc *);
STATIC int flush_pagedep_deps(struct vnode *, struct mount *,
struct diraddhd *);
STATIC int flush_inodedep_deps(struct fs *, ufsino_t);
STATIC int handle_written_filepage(struct pagedep *, struct buf *);
STATIC void diradd_inode_written(struct diradd *, struct inodedep *);
STATIC int handle_written_inodeblock(struct inodedep *, struct buf *);
STATIC void handle_allocdirect_partdone(struct allocdirect *);
STATIC void handle_allocindir_partdone(struct allocindir *);
STATIC void initiate_write_filepage(struct pagedep *, struct buf *);
STATIC void handle_written_mkdir(struct mkdir *, int);
STATIC void initiate_write_inodeblock_ufs1(struct inodedep *, struct buf *);
#ifdef FFS2
STATIC void initiate_write_inodeblock_ufs2(struct inodedep *, struct buf *);
#endif
STATIC void handle_workitem_freefile(struct freefile *);
STATIC void handle_workitem_remove(struct dirrem *);
STATIC struct dirrem *newdirrem(struct buf *, struct inode *,
struct inode *, int, struct dirrem **);
STATIC void free_diradd(struct diradd *);
STATIC void free_allocindir(struct allocindir *, struct inodedep *);
STATIC void free_newdirblk(struct newdirblk *);
STATIC int indir_trunc(struct inode *, daddr_t, int, daddr_t, long *);
STATIC void deallocate_dependencies(struct buf *, struct inodedep *);
STATIC void free_allocdirect(struct allocdirectlst *,
struct allocdirect *, int);
STATIC int check_inode_unwritten(struct inodedep *);
STATIC int free_inodedep(struct inodedep *);
STATIC void handle_workitem_freeblocks(struct freeblks *);
STATIC void merge_inode_lists(struct inodedep *);
STATIC void setup_allocindir_phase2(struct buf *, struct inode *,
struct allocindir *);
STATIC struct allocindir *newallocindir(struct inode *, int, daddr_t,
daddr_t);
STATIC void handle_workitem_freefrag(struct freefrag *);
STATIC struct freefrag *newfreefrag(struct inode *, daddr_t, long);
STATIC void allocdirect_merge(struct allocdirectlst *,
struct allocdirect *, struct allocdirect *);
STATIC struct bmsafemap *bmsafemap_lookup(struct buf *);
STATIC int newblk_lookup(struct fs *, daddr_t, int,
struct newblk **);
STATIC int inodedep_lookup(struct fs *, ufsino_t, int, struct inodedep **);
STATIC int pagedep_lookup(struct inode *, daddr_t, int, struct pagedep **);
STATIC void pause_timer(void *);
STATIC int request_cleanup(int, int);
STATIC int process_worklist_item(struct mount *, int *, int);
STATIC void add_to_worklist(struct worklist *);
/*
* Exported softdep operations.
*/
void softdep_disk_io_initiation(struct buf *);
void softdep_disk_write_complete(struct buf *);
void softdep_deallocate_dependencies(struct buf *);
void softdep_move_dependencies(struct buf *, struct buf *);
int softdep_count_dependencies(struct buf *bp, int, int);
/*
* Locking primitives.
*
* For a uniprocessor, all we need to do is protect against disk
* interrupts. For a multiprocessor, this lock would have to be
* a mutex. A single mutex is used throughout this file, though
* finer grain locking could be used if contention warranted it.
*
* For a multiprocessor, the sleep call would accept a lock and
* release it after the sleep processing was complete. In a uniprocessor
* implementation there is no such interlock, so we simple mark
* the places where it needs to be done with the `interlocked' form
* of the lock calls. Since the uniprocessor sleep already interlocks
* the spl, there is nothing that really needs to be done.
*/
#ifndef /* NOT */ DEBUG
STATIC struct lockit {
int lkt_spl;
} lk = { 0 };
#define ACQUIRE_LOCK(lk) (lk)->lkt_spl = splbio()
#define FREE_LOCK(lk) splx((lk)->lkt_spl)
#define ACQUIRE_LOCK_INTERLOCKED(lk,s) (lk)->lkt_spl = (s)
#define FREE_LOCK_INTERLOCKED(lk) ((lk)->lkt_spl)
#else /* DEBUG */
STATIC struct lockit {
int lkt_spl;
pid_t lkt_held;
int lkt_line;
} lk = { 0, -1 };
STATIC int lockcnt;
STATIC void acquire_lock(struct lockit *, int);
STATIC void free_lock(struct lockit *, int);
STATIC void acquire_lock_interlocked(struct lockit *, int, int);
STATIC int free_lock_interlocked(struct lockit *, int);
#define ACQUIRE_LOCK(lk) acquire_lock(lk, __LINE__)
#define FREE_LOCK(lk) free_lock(lk, __LINE__)
#define ACQUIRE_LOCK_INTERLOCKED(lk,s) acquire_lock_interlocked(lk, (s), __LINE__)
#define FREE_LOCK_INTERLOCKED(lk) free_lock_interlocked(lk, __LINE__)
STATIC void
acquire_lock(struct lockit *lk, int line)
{
pid_t holder;
int original_line;
if (lk->lkt_held != -1) {
holder = lk->lkt_held;
original_line = lk->lkt_line;
FREE_LOCK(lk);
if (holder == CURPROC->p_tid)
panic("softdep_lock: locking against myself, acquired at line %d, relocked at line %d", original_line, line);
else
panic("softdep_lock: lock held by %d, acquired at line %d, relocked at line %d", holder, original_line, line);
}
lk->lkt_spl = splbio();
lk->lkt_held = CURPROC->p_tid;
lk->lkt_line = line;
lockcnt++;
}
STATIC void
free_lock(struct lockit *lk, int line)
{
if (lk->lkt_held == -1)
panic("softdep_unlock: lock not held at line %d", line);
lk->lkt_held = -1;
splx(lk->lkt_spl);
}
STATIC void
acquire_lock_interlocked(struct lockit *lk, int s, int line)
{
pid_t holder;
int original_line;
if (lk->lkt_held != -1) {
holder = lk->lkt_held;
original_line = lk->lkt_line;
FREE_LOCK_INTERLOCKED(lk);
if (holder == CURPROC->p_tid)
panic("softdep_lock: locking against myself, acquired at line %d, relocked at line %d", original_line, line);
else
panic("softdep_lock: lock held by %d, acquired at line %d, relocked at line %d", holder, original_line, line);
}
lk->lkt_held = CURPROC->p_tid;
lk->lkt_line = line;
lk->lkt_spl = s;
lockcnt++;
}
STATIC int
free_lock_interlocked(struct lockit *lk, int line)
{
if (lk->lkt_held == -1)
panic("softdep_unlock_interlocked: lock not held at line %d", line);
lk->lkt_held = -1;
return (lk->lkt_spl);
}
#endif /* DEBUG */
/*
* Place holder for real semaphores.
*/
struct sema {
int value;
pid_t holder;
char *name;
int prio;
};
STATIC void sema_init(struct sema *, char *, int);
STATIC int sema_get(struct sema *, struct lockit *);
STATIC void sema_release(struct sema *);
STATIC void
sema_init(struct sema *semap, char *name, int prio)
{
semap->holder = -1;
semap->value = 0;
semap->name = name;
semap->prio = prio;
}
STATIC int
sema_get(struct sema *semap, struct lockit *interlock)
{
int s;
if (semap->value++ > 0) {
if (interlock != NULL)
s = FREE_LOCK_INTERLOCKED(interlock);
tsleep_nsec(semap, semap->prio, semap->name, INFSLP);
if (interlock != NULL) {
ACQUIRE_LOCK_INTERLOCKED(interlock, s);
FREE_LOCK(interlock);
}
return (0);
}
semap->holder = CURPROC->p_tid;
if (interlock != NULL)
FREE_LOCK(interlock);
return (1);
}
STATIC void
sema_release(struct sema *semap)
{
if (semap->value <= 0 || semap->holder != CURPROC->p_tid) {
#ifdef DEBUG
if (lk.lkt_held != -1)
FREE_LOCK(&lk);
#endif
panic("sema_release: not held");
}
if (--semap->value > 0) {
semap->value = 0;
wakeup(semap);
}
semap->holder = -1;
}
/*
* Memory management.
*/
STATIC struct pool pagedep_pool;
STATIC struct pool inodedep_pool;
STATIC struct pool newblk_pool;
STATIC struct pool bmsafemap_pool;
STATIC struct pool allocdirect_pool;
STATIC struct pool indirdep_pool;
STATIC struct pool allocindir_pool;
STATIC struct pool freefrag_pool;
STATIC struct pool freeblks_pool;
STATIC struct pool freefile_pool;
STATIC struct pool diradd_pool;
STATIC struct pool mkdir_pool;
STATIC struct pool dirrem_pool;
STATIC struct pool newdirblk_pool;
static __inline void
softdep_free(struct worklist *item, int type)
{
switch (type) {
case D_PAGEDEP:
pool_put(&pagedep_pool, item);
break;
case D_INODEDEP:
pool_put(&inodedep_pool, item);
break;
case D_BMSAFEMAP:
pool_put(&bmsafemap_pool, item);
break;
case D_ALLOCDIRECT:
pool_put(&allocdirect_pool, item);
break;
case D_INDIRDEP:
pool_put(&indirdep_pool, item);
break;
case D_ALLOCINDIR:
pool_put(&allocindir_pool, item);
break;
case D_FREEFRAG:
pool_put(&freefrag_pool, item);
break;
case D_FREEBLKS:
pool_put(&freeblks_pool, item);
break;
case D_FREEFILE:
pool_put(&freefile_pool, item);
break;
case D_DIRADD:
pool_put(&diradd_pool, item);
break;
case D_MKDIR:
pool_put(&mkdir_pool, item);
break;
case D_DIRREM:
pool_put(&dirrem_pool, item);
break;
case D_NEWDIRBLK:
pool_put(&newdirblk_pool, item);
break;
default:
#ifdef DEBUG
if (lk.lkt_held != -1)
FREE_LOCK(&lk);
#endif
panic("softdep_free: unknown type %d", type);
}
}
struct workhead softdep_freequeue;
static __inline void
softdep_freequeue_add(struct worklist *item)
{
int s;
s = splbio();
LIST_INSERT_HEAD(&softdep_freequeue, item, wk_list);
splx(s);
}
static __inline void
softdep_freequeue_process(void)
{
struct worklist *wk;
splassert(IPL_BIO);
while ((wk = LIST_FIRST(&softdep_freequeue)) != NULL) {
LIST_REMOVE(wk, wk_list);
FREE_LOCK(&lk);
softdep_free(wk, wk->wk_type);
ACQUIRE_LOCK(&lk);
}
}
/*
* Worklist queue management.
* These routines require that the lock be held.
*/
#ifndef /* NOT */ DEBUG
#define WORKLIST_INSERT(head, item) do { \
(item)->wk_state |= ONWORKLIST; \
LIST_INSERT_HEAD(head, item, wk_list); \
} while (0)
#define WORKLIST_REMOVE(item) do { \
(item)->wk_state &= ~ONWORKLIST; \
LIST_REMOVE(item, wk_list); \
} while (0)
#define WORKITEM_FREE(item, type) softdep_freequeue_add((struct worklist *)item)
#else /* DEBUG */
STATIC void worklist_insert(struct workhead *, struct worklist *);
STATIC void worklist_remove(struct worklist *);
STATIC void workitem_free(struct worklist *);
#define WORKLIST_INSERT(head, item) worklist_insert(head, item)
#define WORKLIST_REMOVE(item) worklist_remove(item)
#define WORKITEM_FREE(item, type) workitem_free((struct worklist *)item)
STATIC void
worklist_insert(struct workhead *head, struct worklist *item)
{
if (lk.lkt_held == -1)
panic("worklist_insert: lock not held");
if (item->wk_state & ONWORKLIST) {
FREE_LOCK(&lk);
panic("worklist_insert: already on list");
}
item->wk_state |= ONWORKLIST;
LIST_INSERT_HEAD(head, item, wk_list);
}
STATIC void
worklist_remove(struct worklist *item)
{
if (lk.lkt_held == -1)
panic("worklist_remove: lock not held");
if ((item->wk_state & ONWORKLIST) == 0) {
FREE_LOCK(&lk);
panic("worklist_remove: not on list");
}
item->wk_state &= ~ONWORKLIST;
LIST_REMOVE(item, wk_list);
}
STATIC void
workitem_free(struct worklist *item)
{
if (item->wk_state & ONWORKLIST) {
if (lk.lkt_held != -1)
FREE_LOCK(&lk);
panic("workitem_free: still on list");
}
softdep_freequeue_add(item);
}
#endif /* DEBUG */
/*
* Workitem queue management
*/
STATIC struct workhead softdep_workitem_pending;
STATIC struct worklist *worklist_tail;
STATIC int num_on_worklist; /* number of worklist items to be processed */
STATIC int softdep_worklist_busy; /* 1 => trying to do unmount */
STATIC int softdep_worklist_req; /* serialized waiters */
STATIC int max_softdeps; /* maximum number of structs before slowdown */
STATIC int tickdelay = 2; /* number of ticks to pause during slowdown */
STATIC int proc_waiting; /* tracks whether we have a timeout posted */
STATIC int *stat_countp; /* statistic to count in proc_waiting timeout */
STATIC struct timeout proc_waiting_timeout;
STATIC struct proc *filesys_syncer; /* proc of filesystem syncer process */
STATIC int req_clear_inodedeps; /* syncer process flush some inodedeps */
#define FLUSH_INODES 1
STATIC int req_clear_remove; /* syncer process flush some freeblks */
#define FLUSH_REMOVE 2
/*
* runtime statistics
*/
STATIC int stat_worklist_push; /* number of worklist cleanups */
STATIC int stat_blk_limit_push; /* number of times block limit neared */
STATIC int stat_ino_limit_push; /* number of times inode limit neared */
STATIC int stat_blk_limit_hit; /* number of times block slowdown imposed */
STATIC int stat_ino_limit_hit; /* number of times inode slowdown imposed */
STATIC int stat_sync_limit_hit; /* number of synchronous slowdowns imposed */
STATIC int stat_indir_blk_ptrs; /* bufs redirtied as indir ptrs not written */
STATIC int stat_inode_bitmap; /* bufs redirtied as inode bitmap not written */
STATIC int stat_direct_blk_ptrs;/* bufs redirtied as direct ptrs not written */
STATIC int stat_dir_entry; /* bufs redirtied as dir entry cannot write */
/*
* Add an item to the end of the work queue.
* This routine requires that the lock be held.
* This is the only routine that adds items to the list.
* The following routine is the only one that removes items
* and does so in order from first to last.
*/
STATIC void
add_to_worklist(struct worklist *wk)
{
if (wk->wk_state & ONWORKLIST) {
#ifdef DEBUG
if (lk.lkt_held != -1)
FREE_LOCK(&lk);
#endif
panic("add_to_worklist: already on list");
}
wk->wk_state |= ONWORKLIST;
if (LIST_FIRST(&softdep_workitem_pending) == NULL)
LIST_INSERT_HEAD(&softdep_workitem_pending, wk, wk_list);
else
LIST_INSERT_AFTER(worklist_tail, wk, wk_list);
worklist_tail = wk;
num_on_worklist += 1;
}
/*
* Process that runs once per second to handle items in the background queue.
*
* Note that we ensure that everything is done in the order in which they
* appear in the queue. The code below depends on this property to ensure
* that blocks of a file are freed before the inode itself is freed. This
* ordering ensures that no new <vfsid, inum, lbn> triples will be generated
* until all the old ones have been purged from the dependency lists.
*/
int
softdep_process_worklist(struct mount *matchmnt)
{
struct proc *p = CURPROC;
int matchcnt, loopcount;
struct timeval starttime;
/*
* First process any items on the delayed-free queue.
*/
ACQUIRE_LOCK(&lk);
softdep_freequeue_process();
FREE_LOCK(&lk);
/*
* Record the process identifier of our caller so that we can give
* this process preferential treatment in request_cleanup below.
* We can't do this in softdep_initialize, because the syncer doesn't
* have to run then.
* NOTE! This function _could_ be called with a curproc != syncerproc.
*/
filesys_syncer = syncerproc;
matchcnt = 0;
/*
* There is no danger of having multiple processes run this
* code, but we have to single-thread it when softdep_flushfiles()
* is in operation to get an accurate count of the number of items
* related to its mount point that are in the list.
*/
if (matchmnt == NULL) {
if (softdep_worklist_busy < 0)
return(-1);
softdep_worklist_busy += 1;
}
/*
* If requested, try removing inode or removal dependencies.
*/
if (req_clear_inodedeps) {
clear_inodedeps(p);
req_clear_inodedeps -= 1;
wakeup_one(&proc_waiting);
}
if (req_clear_remove) {
clear_remove(p);
req_clear_remove -= 1;
wakeup_one(&proc_waiting);
}
loopcount = 1;
getmicrouptime(&starttime);
while (num_on_worklist > 0) {
if (process_worklist_item(matchmnt, &matchcnt, LK_NOWAIT) == 0)
break;
/*
* If a umount operation wants to run the worklist
* accurately, abort.
*/
if (softdep_worklist_req && matchmnt == NULL) {
matchcnt = -1;
break;
}
/*
* If requested, try removing inode or removal dependencies.
*/
if (req_clear_inodedeps) {
clear_inodedeps(p);
req_clear_inodedeps -= 1;
wakeup_one(&proc_waiting);
}
if (req_clear_remove) {
clear_remove(p);
req_clear_remove -= 1;
wakeup_one(&proc_waiting);
}
/*
* We do not generally want to stop for buffer space, but if
* we are really being a buffer hog, we will stop and wait.
*/
#if 0
if (loopcount++ % 128 == 0)
bwillwrite();
#endif
/*
* Never allow processing to run for more than one
* second. Otherwise the other syncer tasks may get
* excessively backlogged.
*/
{
struct timeval diff;
struct timeval tv;
getmicrouptime(&tv);
timersub(&tv, &starttime, &diff);
if (diff.tv_sec != 0 && matchmnt == NULL) {
matchcnt = -1;
break;
}
}
/*
* Process any new items on the delayed-free queue.
*/
ACQUIRE_LOCK(&lk);
softdep_freequeue_process();
FREE_LOCK(&lk);
}
if (matchmnt == NULL) {
softdep_worklist_busy -= 1;
if (softdep_worklist_req && softdep_worklist_busy == 0)
wakeup(&softdep_worklist_req);
}
return (matchcnt);
}
/*
* Process one item on the worklist.
*/
STATIC int
process_worklist_item(struct mount *matchmnt, int *matchcnt, int flags)
{
struct worklist *wk, *wkend;
struct dirrem *dirrem;
struct mount *mp;
struct vnode *vp;
ACQUIRE_LOCK(&lk);
/*
* Normally we just process each item on the worklist in order.
* However, if we are in a situation where we cannot lock any
* inodes, we have to skip over any dirrem requests whose
* vnodes are resident and locked.
*/
LIST_FOREACH(wk, &softdep_workitem_pending, wk_list) {
if ((flags & LK_NOWAIT) == 0 || wk->wk_type != D_DIRREM)
break;
dirrem = WK_DIRREM(wk);
vp = ufs_ihashlookup(VFSTOUFS(dirrem->dm_mnt)->um_dev,
dirrem->dm_oldinum);
if (vp == NULL || !VOP_ISLOCKED(vp))
break;
}
if (wk == NULL) {
FREE_LOCK(&lk);
return (0);
}
/*
* Remove the item to be processed. If we are removing the last
* item on the list, we need to recalculate the tail pointer.
* As this happens rarely and usually when the list is short,
* we just run down the list to find it rather than tracking it
* in the above loop.
*/
WORKLIST_REMOVE(wk);
if (wk == worklist_tail) {
LIST_FOREACH(wkend, &softdep_workitem_pending, wk_list)
if (LIST_NEXT(wkend, wk_list) == NULL)
break;
worklist_tail = wkend;
}
num_on_worklist -= 1;
FREE_LOCK(&lk);
switch (wk->wk_type) {
case D_DIRREM:
/* removal of a directory entry */
mp = WK_DIRREM(wk)->dm_mnt;
#if 0
if (vn_write_suspend_wait(NULL, mp, V_NOWAIT))
panic("%s: dirrem on suspended filesystem",
"process_worklist_item");
#endif
if (matchmnt != NULL && mp == matchmnt)
*matchcnt += 1;
handle_workitem_remove(WK_DIRREM(wk));
break;
case D_FREEBLKS:
/* releasing blocks and/or fragments from a file */
mp = WK_FREEBLKS(wk)->fb_mnt;
#if 0
if (vn_write_suspend_wait(NULL, mp, V_NOWAIT))
panic("%s: freeblks on suspended filesystem",
"process_worklist_item");
#endif
if (matchmnt != NULL && mp == matchmnt)
*matchcnt += 1;
handle_workitem_freeblocks(WK_FREEBLKS(wk));
break;
case D_FREEFRAG:
/* releasing a fragment when replaced as a file grows */
mp = WK_FREEFRAG(wk)->ff_mnt;
#if 0
if (vn_write_suspend_wait(NULL, mp, V_NOWAIT))
panic("%s: freefrag on suspended filesystem",
"process_worklist_item");
#endif
if (matchmnt != NULL && mp == matchmnt)
*matchcnt += 1;
handle_workitem_freefrag(WK_FREEFRAG(wk));
break;
case D_FREEFILE:
/* releasing an inode when its link count drops to 0 */
mp = WK_FREEFILE(wk)->fx_mnt;
#if 0
if (vn_write_suspend_wait(NULL, mp, V_NOWAIT))
panic("%s: freefile on suspended filesystem",
"process_worklist_item");
#endif
if (matchmnt != NULL && mp == matchmnt)
*matchcnt += 1;
handle_workitem_freefile(WK_FREEFILE(wk));
break;
default:
panic("%s_process_worklist: Unknown type %s",
"softdep", TYPENAME(wk->wk_type));
/* NOTREACHED */
}
return (1);
}
/*
* Move dependencies from one buffer to another.
*/
void
softdep_move_dependencies(struct buf *oldbp, struct buf *newbp)
{
struct worklist *wk, *wktail;
if (LIST_FIRST(&newbp->b_dep) != NULL)
panic("softdep_move_dependencies: need merge code");
wktail = NULL;
ACQUIRE_LOCK(&lk);
while ((wk = LIST_FIRST(&oldbp->b_dep)) != NULL) {
LIST_REMOVE(wk, wk_list);
if (wktail == NULL)
LIST_INSERT_HEAD(&newbp->b_dep, wk, wk_list);
else
LIST_INSERT_AFTER(wktail, wk, wk_list);
wktail = wk;
}
FREE_LOCK(&lk);
}
/*
* Purge the work list of all items associated with a particular mount point.
*/
int
softdep_flushworklist(struct mount *oldmnt, int *countp, struct proc *p)
{
struct vnode *devvp;
int count, error = 0;
/*
* Await our turn to clear out the queue, then serialize access.
*/
while (softdep_worklist_busy) {
softdep_worklist_req += 1;
tsleep_nsec(&softdep_worklist_req, PRIBIO, "softflush", INFSLP);
softdep_worklist_req -= 1;
}
softdep_worklist_busy = -1;
/*
* Alternately flush the block device associated with the mount
* point and process any dependencies that the flushing
* creates. We continue until no more worklist dependencies
* are found.
*/
*countp = 0;
devvp = VFSTOUFS(oldmnt)->um_devvp;
while ((count = softdep_process_worklist(oldmnt)) > 0) {
*countp += count;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_FSYNC(devvp, p->p_ucred, MNT_WAIT, p);
VOP_UNLOCK(devvp);
if (error)
break;
}
softdep_worklist_busy = 0;
if (softdep_worklist_req)
wakeup(&softdep_worklist_req);
return (error);
}
/*
* Flush all vnodes and worklist items associated with a specified mount point.
*/
int
softdep_flushfiles(struct mount *oldmnt, int flags, struct proc *p)
{
int error, count, loopcnt;
/*
* Alternately flush the vnodes associated with the mount
* point and process any dependencies that the flushing
* creates. In theory, this loop can happen at most twice,
* but we give it a few extra just to be sure.
*/
for (loopcnt = 10; loopcnt > 0; loopcnt--) {
/*
* Do another flush in case any vnodes were brought in
* as part of the cleanup operations.
*/
if ((error = ffs_flushfiles(oldmnt, flags, p)) != 0)
break;
if ((error = softdep_flushworklist(oldmnt, &count, p)) != 0 ||
count == 0)
break;
}
/*
* If the reboot process sleeps during the loop, the update
* process may call softdep_process_worklist() and create
* new dirty vnodes at the mount point. Call ffs_flushfiles()
* again after the loop has flushed all soft dependencies.
*/
if (error == 0)
error = ffs_flushfiles(oldmnt, flags, p);
/*
* If we are unmounting then it is an error to fail. If we
* are simply trying to downgrade to read-only, then filesystem
* activity can keep us busy forever, so we just fail with EBUSY.
*/
if (loopcnt == 0) {
error = EBUSY;
}
return (error);
}
/*
* Structure hashing.
*
* There are three types of structures that can be looked up:
* 1) pagedep structures identified by mount point, inode number,
* and logical block.
* 2) inodedep structures identified by mount point and inode number.
* 3) newblk structures identified by mount point and
* physical block number.
*
* The "pagedep" and "inodedep" dependency structures are hashed
* separately from the file blocks and inodes to which they correspond.
* This separation helps when the in-memory copy of an inode or
* file block must be replaced. It also obviates the need to access
* an inode or file page when simply updating (or de-allocating)
* dependency structures. Lookup of newblk structures is needed to
* find newly allocated blocks when trying to associate them with
* their allocdirect or allocindir structure.
*
* The lookup routines optionally create and hash a new instance when
* an existing entry is not found.
*/
#define DEPALLOC 0x0001 /* allocate structure if lookup fails */
#define NODELAY 0x0002 /* cannot do background work */
SIPHASH_KEY softdep_hashkey;
/*
* Structures and routines associated with pagedep caching.
*/
LIST_HEAD(pagedep_hashhead, pagedep) *pagedep_hashtbl;
u_long pagedep_hash; /* size of hash table - 1 */
STATIC struct sema pagedep_in_progress;
/*
* Look up a pagedep. Return 1 if found, 0 if not found or found
* when asked to allocate but not associated with any buffer.
* If not found, allocate if DEPALLOC flag is passed.
* Found or allocated entry is returned in pagedeppp.
* This routine must be called with splbio interrupts blocked.
*/
STATIC int
pagedep_lookup(struct inode *ip, daddr_t lbn, int flags,
struct pagedep **pagedeppp)
{
SIPHASH_CTX ctx;
struct pagedep *pagedep;
struct pagedep_hashhead *pagedephd;
struct mount *mp;
int i;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("pagedep_lookup: lock not held");
#endif
mp = ITOV(ip)->v_mount;
SipHash24_Init(&ctx, &softdep_hashkey);
SipHash24_Update(&ctx, &mp, sizeof(mp));
SipHash24_Update(&ctx, &ip->i_number, sizeof(ip->i_number));
SipHash24_Update(&ctx, &lbn, sizeof(lbn));
pagedephd = &pagedep_hashtbl[SipHash24_End(&ctx) & pagedep_hash];
top:
LIST_FOREACH(pagedep, pagedephd, pd_hash)
if (ip->i_number == pagedep->pd_ino &&
lbn == pagedep->pd_lbn &&
mp == pagedep->pd_mnt)
break;
if (pagedep) {
*pagedeppp = pagedep;
if ((flags & DEPALLOC) != 0 &&
(pagedep->pd_state & ONWORKLIST) == 0)
return (0);
return (1);
}
if ((flags & DEPALLOC) == 0) {
*pagedeppp = NULL;
return (0);
}
if (sema_get(&pagedep_in_progress, &lk) == 0) {
ACQUIRE_LOCK(&lk);
goto top;
}
pagedep = pool_get(&pagedep_pool, PR_WAITOK | PR_ZERO);
pagedep->pd_list.wk_type = D_PAGEDEP;
pagedep->pd_mnt = mp;
pagedep->pd_ino = ip->i_number;
pagedep->pd_lbn = lbn;
LIST_INIT(&pagedep->pd_dirremhd);
LIST_INIT(&pagedep->pd_pendinghd);
for (i = 0; i < DAHASHSZ; i++)
LIST_INIT(&pagedep->pd_diraddhd[i]);
ACQUIRE_LOCK(&lk);
LIST_INSERT_HEAD(pagedephd, pagedep, pd_hash);
sema_release(&pagedep_in_progress);
*pagedeppp = pagedep;
return (0);
}
/*
* Structures and routines associated with inodedep caching.
*/
LIST_HEAD(inodedep_hashhead, inodedep) *inodedep_hashtbl;
STATIC u_long inodedep_hash; /* size of hash table - 1 */
STATIC long num_inodedep; /* number of inodedep allocated */
STATIC struct sema inodedep_in_progress;
/*
* Look up a inodedep. Return 1 if found, 0 if not found.
* If not found, allocate if DEPALLOC flag is passed.
* Found or allocated entry is returned in inodedeppp.
* This routine must be called with splbio interrupts blocked.
*/
STATIC int
inodedep_lookup(struct fs *fs, ufsino_t inum, int flags,
struct inodedep **inodedeppp)
{
SIPHASH_CTX ctx;
struct inodedep *inodedep;
struct inodedep_hashhead *inodedephd;
int firsttry;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("inodedep_lookup: lock not held");
#endif
firsttry = 1;
SipHash24_Init(&ctx, &softdep_hashkey);
SipHash24_Update(&ctx, &fs, sizeof(fs));
SipHash24_Update(&ctx, &inum, sizeof(inum));
inodedephd = &inodedep_hashtbl[SipHash24_End(&ctx) & inodedep_hash];
top:
LIST_FOREACH(inodedep, inodedephd, id_hash)
if (inum == inodedep->id_ino && fs == inodedep->id_fs)
break;
if (inodedep) {
*inodedeppp = inodedep;
return (1);
}
if ((flags & DEPALLOC) == 0) {
*inodedeppp = NULL;
return (0);
}
/*
* If we are over our limit, try to improve the situation.
*/
if (num_inodedep > max_softdeps && firsttry && (flags & NODELAY) == 0 &&
request_cleanup(FLUSH_INODES, 1)) {
firsttry = 0;
goto top;
}
if (sema_get(&inodedep_in_progress, &lk) == 0) {
ACQUIRE_LOCK(&lk);
goto top;
}
num_inodedep += 1;
inodedep = pool_get(&inodedep_pool, PR_WAITOK);
inodedep->id_list.wk_type = D_INODEDEP;
inodedep->id_fs = fs;
inodedep->id_ino = inum;
inodedep->id_state = ALLCOMPLETE;
inodedep->id_nlinkdelta = 0;
inodedep->id_savedino1 = NULL;
inodedep->id_savedsize = -1;
inodedep->id_buf = NULL;
LIST_INIT(&inodedep->id_pendinghd);
LIST_INIT(&inodedep->id_inowait);
LIST_INIT(&inodedep->id_bufwait);
TAILQ_INIT(&inodedep->id_inoupdt);
TAILQ_INIT(&inodedep->id_newinoupdt);
ACQUIRE_LOCK(&lk);
LIST_INSERT_HEAD(inodedephd, inodedep, id_hash);
sema_release(&inodedep_in_progress);
*inodedeppp = inodedep;
return (0);
}
/*
* Structures and routines associated with newblk caching.
*/
LIST_HEAD(newblk_hashhead, newblk) *newblk_hashtbl;
u_long newblk_hash; /* size of hash table - 1 */
STATIC struct sema newblk_in_progress;
/*
* Look up a newblk. Return 1 if found, 0 if not found.
* If not found, allocate if DEPALLOC flag is passed.
* Found or allocated entry is returned in newblkpp.
*/
STATIC int
newblk_lookup(struct fs *fs, daddr_t newblkno, int flags,
struct newblk **newblkpp)
{
SIPHASH_CTX ctx;
struct newblk *newblk;
struct newblk_hashhead *newblkhd;
SipHash24_Init(&ctx, &softdep_hashkey);
SipHash24_Update(&ctx, &fs, sizeof(fs));
SipHash24_Update(&ctx, &newblkno, sizeof(newblkno));
newblkhd = &newblk_hashtbl[SipHash24_End(&ctx) & newblk_hash];
top:
LIST_FOREACH(newblk, newblkhd, nb_hash)
if (newblkno == newblk->nb_newblkno && fs == newblk->nb_fs)
break;
if (newblk) {
*newblkpp = newblk;
return (1);
}
if ((flags & DEPALLOC) == 0) {
*newblkpp = NULL;
return (0);
}
if (sema_get(&newblk_in_progress, NULL) == 0)
goto top;
newblk = pool_get(&newblk_pool, PR_WAITOK);
newblk->nb_state = 0;
newblk->nb_fs = fs;
newblk->nb_newblkno = newblkno;
LIST_INSERT_HEAD(newblkhd, newblk, nb_hash);
sema_release(&newblk_in_progress);
*newblkpp = newblk;
return (0);
}
/*
* Executed during filesystem system initialization before
* mounting any file systems.
*/
void
softdep_initialize(void)
{
bioops.io_start = softdep_disk_io_initiation;
bioops.io_complete = softdep_disk_write_complete;
bioops.io_deallocate = softdep_deallocate_dependencies;
bioops.io_movedeps = softdep_move_dependencies;
bioops.io_countdeps = softdep_count_dependencies;
LIST_INIT(&mkdirlisthd);
LIST_INIT(&softdep_workitem_pending);
#ifdef KMEMSTATS
max_softdeps = min (initialvnodes * 8,
kmemstats[M_INODEDEP].ks_limit / (2 * sizeof(struct inodedep)));
#else
max_softdeps = initialvnodes * 4;
#endif
arc4random_buf(&softdep_hashkey, sizeof(softdep_hashkey));
pagedep_hashtbl = hashinit(initialvnodes / 5, M_PAGEDEP, M_WAITOK,
&pagedep_hash);
sema_init(&pagedep_in_progress, "pagedep", PRIBIO);
inodedep_hashtbl = hashinit(initialvnodes, M_INODEDEP, M_WAITOK,
&inodedep_hash);
sema_init(&inodedep_in_progress, "inodedep", PRIBIO);
newblk_hashtbl = hashinit(64, M_NEWBLK, M_WAITOK, &newblk_hash);
sema_init(&newblk_in_progress, "newblk", PRIBIO);
timeout_set(&proc_waiting_timeout, pause_timer, NULL);
pool_init(&pagedep_pool, sizeof(struct pagedep), 0, IPL_NONE,
PR_WAITOK, "pagedep", NULL);
pool_init(&inodedep_pool, sizeof(struct inodedep), 0, IPL_NONE,
PR_WAITOK, "inodedep", NULL);
pool_init(&newblk_pool, sizeof(struct newblk), 0, IPL_NONE,
PR_WAITOK, "newblk", NULL);
pool_init(&bmsafemap_pool, sizeof(struct bmsafemap), 0, IPL_NONE,
PR_WAITOK, "bmsafemap", NULL);
pool_init(&allocdirect_pool, sizeof(struct allocdirect), 0, IPL_NONE,
PR_WAITOK, "allocdir", NULL);
pool_init(&indirdep_pool, sizeof(struct indirdep), 0, IPL_NONE,
PR_WAITOK, "indirdep", NULL);
pool_init(&allocindir_pool, sizeof(struct allocindir), 0, IPL_NONE,
PR_WAITOK, "allocindir", NULL);
pool_init(&freefrag_pool, sizeof(struct freefrag), 0, IPL_NONE,
PR_WAITOK, "freefrag", NULL);
pool_init(&freeblks_pool, sizeof(struct freeblks), 0, IPL_NONE,
PR_WAITOK, "freeblks", NULL);
pool_init(&freefile_pool, sizeof(struct freefile), 0, IPL_NONE,
PR_WAITOK, "freefile", NULL);
pool_init(&diradd_pool, sizeof(struct diradd), 0, IPL_NONE,
PR_WAITOK, "diradd", NULL);
pool_init(&mkdir_pool, sizeof(struct mkdir), 0, IPL_NONE,
PR_WAITOK, "mkdir", NULL);
pool_init(&dirrem_pool, sizeof(struct dirrem), 0, IPL_NONE,
PR_WAITOK, "dirrem", NULL);
pool_init(&newdirblk_pool, sizeof(struct newdirblk), 0, IPL_NONE,
PR_WAITOK, "newdirblk", NULL);
}
/*
* Called at mount time to notify the dependency code that a
* filesystem wishes to use it.
*/
int
softdep_mount(struct vnode *devvp, struct mount *mp, struct fs *fs,
struct ucred *cred)
{
struct csum_total cstotal;
struct cg *cgp;
struct buf *bp;
int error, cyl;
/*
* When doing soft updates, the counters in the
* superblock may have gotten out of sync, so we have
* to scan the cylinder groups and recalculate them.
*/
if ((fs->fs_flags & FS_UNCLEAN) == 0)
return (0);
memset(&cstotal, 0, sizeof(cstotal));
for (cyl = 0; cyl < fs->fs_ncg; cyl++) {
if ((error = bread(devvp, fsbtodb(fs, cgtod(fs, cyl)),
fs->fs_cgsize, &bp)) != 0) {
brelse(bp);
return (error);
}
cgp = (struct cg *)bp->b_data;
cstotal.cs_nffree += cgp->cg_cs.cs_nffree;
cstotal.cs_nbfree += cgp->cg_cs.cs_nbfree;
cstotal.cs_nifree += cgp->cg_cs.cs_nifree;
cstotal.cs_ndir += cgp->cg_cs.cs_ndir;
fs->fs_cs(fs, cyl) = cgp->cg_cs;
brelse(bp);
}
#ifdef DEBUG
if (memcmp(&cstotal, &fs->fs_cstotal, sizeof(cstotal)))
printf("ffs_mountfs: superblock updated for soft updates\n");
#endif
memcpy(&fs->fs_cstotal, &cstotal, sizeof(cstotal));
return (0);
}
/*
* Protecting the freemaps (or bitmaps).
*
* To eliminate the need to execute fsck before mounting a file system
* after a power failure, one must (conservatively) guarantee that the
* on-disk copy of the bitmaps never indicate that a live inode or block is
* free. So, when a block or inode is allocated, the bitmap should be
* updated (on disk) before any new pointers. When a block or inode is
* freed, the bitmap should not be updated until all pointers have been
* reset. The latter dependency is handled by the delayed de-allocation
* approach described below for block and inode de-allocation. The former
* dependency is handled by calling the following procedure when a block or
* inode is allocated. When an inode is allocated an "inodedep" is created
* with its DEPCOMPLETE flag cleared until its bitmap is written to disk.
* Each "inodedep" is also inserted into the hash indexing structure so
* that any additional link additions can be made dependent on the inode
* allocation.
*
* The ufs file system maintains a number of free block counts (e.g., per
* cylinder group, per cylinder and per <cylinder, rotational position> pair)
* in addition to the bitmaps. These counts are used to improve efficiency
* during allocation and therefore must be consistent with the bitmaps.
* There is no convenient way to guarantee post-crash consistency of these
* counts with simple update ordering, for two main reasons: (1) The counts
* and bitmaps for a single cylinder group block are not in the same disk
* sector. If a disk write is interrupted (e.g., by power failure), one may
* be written and the other not. (2) Some of the counts are located in the
* superblock rather than the cylinder group block. So, we focus our soft
* updates implementation on protecting the bitmaps. When mounting a
* filesystem, we recompute the auxiliary counts from the bitmaps.
*/
/*
* Called just after updating the cylinder group block to allocate an inode.
*/
/* buffer for cylgroup block with inode map */
/* inode related to allocation */
/* new inode number being allocated */
void
softdep_setup_inomapdep(struct buf *bp, struct inode *ip, ufsino_t newinum)
{
struct inodedep *inodedep;
struct bmsafemap *bmsafemap;
/*
* Create a dependency for the newly allocated inode.
* Panic if it already exists as something is seriously wrong.
* Otherwise add it to the dependency list for the buffer holding
* the cylinder group map from which it was allocated.
*/
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(ip->i_fs, newinum, DEPALLOC | NODELAY, &inodedep)
!= 0) {
FREE_LOCK(&lk);
panic("softdep_setup_inomapdep: found inode");
}
inodedep->id_buf = bp;
inodedep->id_state &= ~DEPCOMPLETE;
bmsafemap = bmsafemap_lookup(bp);
LIST_INSERT_HEAD(&bmsafemap->sm_inodedephd, inodedep, id_deps);
FREE_LOCK(&lk);
}
/*
* Called just after updating the cylinder group block to
* allocate block or fragment.
*/
/* buffer for cylgroup block with block map */
/* filesystem doing allocation */
/* number of newly allocated block */
void
softdep_setup_blkmapdep(struct buf *bp, struct fs *fs, daddr_t newblkno)
{
struct newblk *newblk;
struct bmsafemap *bmsafemap;
/*
* Create a dependency for the newly allocated block.
* Add it to the dependency list for the buffer holding
* the cylinder group map from which it was allocated.
*/
if (newblk_lookup(fs, newblkno, DEPALLOC, &newblk) != 0)
panic("softdep_setup_blkmapdep: found block");
ACQUIRE_LOCK(&lk);
newblk->nb_bmsafemap = bmsafemap = bmsafemap_lookup(bp);
LIST_INSERT_HEAD(&bmsafemap->sm_newblkhd, newblk, nb_deps);
FREE_LOCK(&lk);
}
/*
* Find the bmsafemap associated with a cylinder group buffer.
* If none exists, create one. The buffer must be locked when
* this routine is called and this routine must be called with
* splbio interrupts blocked.
*/
STATIC struct bmsafemap *
bmsafemap_lookup(struct buf *bp)
{
struct bmsafemap *bmsafemap;
struct worklist *wk;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("bmsafemap_lookup: lock not held");
#endif
LIST_FOREACH(wk, &bp->b_dep, wk_list)
if (wk->wk_type == D_BMSAFEMAP)
return (WK_BMSAFEMAP(wk));
FREE_LOCK(&lk);
bmsafemap = pool_get(&bmsafemap_pool, PR_WAITOK);
bmsafemap->sm_list.wk_type = D_BMSAFEMAP;
bmsafemap->sm_list.wk_state = 0;
bmsafemap->sm_buf = bp;
LIST_INIT(&bmsafemap->sm_allocdirecthd);
LIST_INIT(&bmsafemap->sm_allocindirhd);
LIST_INIT(&bmsafemap->sm_inodedephd);
LIST_INIT(&bmsafemap->sm_newblkhd);
ACQUIRE_LOCK(&lk);
WORKLIST_INSERT(&bp->b_dep, &bmsafemap->sm_list);
return (bmsafemap);
}
/*
* Direct block allocation dependencies.
*
* When a new block is allocated, the corresponding disk locations must be
* initialized (with zeros or new data) before the on-disk inode points to
* them. Also, the freemap from which the block was allocated must be
* updated (on disk) before the inode's pointer. These two dependencies are
* independent of each other and are needed for all file blocks and indirect
* blocks that are pointed to directly by the inode. Just before the
* "in-core" version of the inode is updated with a newly allocated block
* number, a procedure (below) is called to setup allocation dependency
* structures. These structures are removed when the corresponding
* dependencies are satisfied or when the block allocation becomes obsolete
* (i.e., the file is deleted, the block is de-allocated, or the block is a
* fragment that gets upgraded). All of these cases are handled in
* procedures described later.
*
* When a file extension causes a fragment to be upgraded, either to a larger
* fragment or to a full block, the on-disk location may change (if the
* previous fragment could not simply be extended). In this case, the old
* fragment must be de-allocated, but not until after the inode's pointer has
* been updated. In most cases, this is handled by later procedures, which
* will construct a "freefrag" structure to be added to the workitem queue
* when the inode update is complete (or obsolete). The main exception to
* this is when an allocation occurs while a pending allocation dependency
* (for the same block pointer) remains. This case is handled in the main
* allocation dependency setup procedure by immediately freeing the
* unreferenced fragments.
*/
/* inode to which block is being added */
/* block pointer within inode */
/* disk block number being added */
/* previous block number, 0 unless frag */
/* size of new block */
/* size of new block */
/* bp for allocated block */
void
softdep_setup_allocdirect(struct inode *ip, daddr_t lbn, daddr_t newblkno,
daddr_t oldblkno, long newsize, long oldsize, struct buf *bp)
{
struct allocdirect *adp, *oldadp;
struct allocdirectlst *adphead;
struct bmsafemap *bmsafemap;
struct inodedep *inodedep;
struct pagedep *pagedep;
struct newblk *newblk;
adp = pool_get(&allocdirect_pool, PR_WAITOK | PR_ZERO);
adp->ad_list.wk_type = D_ALLOCDIRECT;
adp->ad_lbn = lbn;
adp->ad_newblkno = newblkno;
adp->ad_oldblkno = oldblkno;
adp->ad_newsize = newsize;
adp->ad_oldsize = oldsize;
adp->ad_state = ATTACHED;
LIST_INIT(&adp->ad_newdirblk);
if (newblkno == oldblkno)
adp->ad_freefrag = NULL;
else
adp->ad_freefrag = newfreefrag(ip, oldblkno, oldsize);
if (newblk_lookup(ip->i_fs, newblkno, 0, &newblk) == 0)
panic("softdep_setup_allocdirect: lost block");
ACQUIRE_LOCK(&lk);
inodedep_lookup(ip->i_fs, ip->i_number, DEPALLOC | NODELAY, &inodedep);
adp->ad_inodedep = inodedep;
if (newblk->nb_state == DEPCOMPLETE) {
adp->ad_state |= DEPCOMPLETE;
adp->ad_buf = NULL;
} else {
bmsafemap = newblk->nb_bmsafemap;
adp->ad_buf = bmsafemap->sm_buf;
LIST_REMOVE(newblk, nb_deps);
LIST_INSERT_HEAD(&bmsafemap->sm_allocdirecthd, adp, ad_deps);
}
LIST_REMOVE(newblk, nb_hash);
pool_put(&newblk_pool, newblk);
if (bp == NULL) {
/*
* XXXUBC - Yes, I know how to fix this, but not right now.
*/
panic("softdep_setup_allocdirect: Bonk art in the head");
}
WORKLIST_INSERT(&bp->b_dep, &adp->ad_list);
if (lbn >= NDADDR) {
/* allocating an indirect block */
if (oldblkno != 0) {
FREE_LOCK(&lk);
panic("softdep_setup_allocdirect: non-zero indir");
}
} else {
/*
* Allocating a direct block.
*
* If we are allocating a directory block, then we must
* allocate an associated pagedep to track additions and
* deletions.
*/
if ((DIP(ip, mode) & IFMT) == IFDIR &&
pagedep_lookup(ip, lbn, DEPALLOC, &pagedep) == 0)
WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list);
}
/*
* The list of allocdirects must be kept in sorted and ascending
* order so that the rollback routines can quickly determine the
* first uncommitted block (the size of the file stored on disk
* ends at the end of the lowest committed fragment, or if there
* are no fragments, at the end of the highest committed block).
* Since files generally grow, the typical case is that the new
* block is to be added at the end of the list. We speed this
* special case by checking against the last allocdirect in the
* list before laboriously traversing the list looking for the
* insertion point.
*/
adphead = &inodedep->id_newinoupdt;
oldadp = TAILQ_LAST(adphead, allocdirectlst);
if (oldadp == NULL || oldadp->ad_lbn <= lbn) {
/* insert at end of list */
TAILQ_INSERT_TAIL(adphead, adp, ad_next);
if (oldadp != NULL && oldadp->ad_lbn == lbn)
allocdirect_merge(adphead, adp, oldadp);
FREE_LOCK(&lk);
return;
}
TAILQ_FOREACH(oldadp, adphead, ad_next) {
if (oldadp->ad_lbn >= lbn)
break;
}
if (oldadp == NULL) {
FREE_LOCK(&lk);
panic("softdep_setup_allocdirect: lost entry");
}
/* insert in middle of list */
TAILQ_INSERT_BEFORE(oldadp, adp, ad_next);
if (oldadp->ad_lbn == lbn)
allocdirect_merge(adphead, adp, oldadp);
FREE_LOCK(&lk);
}
/*
* Replace an old allocdirect dependency with a newer one.
* This routine must be called with splbio interrupts blocked.
*/
/* head of list holding allocdirects */
/* allocdirect being added */
/* existing allocdirect being checked */
STATIC void
allocdirect_merge(struct allocdirectlst *adphead, struct allocdirect *newadp,
struct allocdirect *oldadp)
{
struct worklist *wk;
struct freefrag *freefrag;
struct newdirblk *newdirblk;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("allocdirect_merge: lock not held");
#endif
if (newadp->ad_oldblkno != oldadp->ad_newblkno ||
newadp->ad_oldsize != oldadp->ad_newsize ||
newadp->ad_lbn >= NDADDR) {
FREE_LOCK(&lk);
panic("allocdirect_merge: old %lld != new %lld || lbn %lld >= "
"%d", (long long)newadp->ad_oldblkno,
(long long)oldadp->ad_newblkno, (long long)newadp->ad_lbn,
NDADDR);
}
newadp->ad_oldblkno = oldadp->ad_oldblkno;
newadp->ad_oldsize = oldadp->ad_oldsize;
/*
* If the old dependency had a fragment to free or had never
* previously had a block allocated, then the new dependency
* can immediately post its freefrag and adopt the old freefrag.
* This action is done by swapping the freefrag dependencies.
* The new dependency gains the old one's freefrag, and the
* old one gets the new one and then immediately puts it on
* the worklist when it is freed by free_allocdirect. It is
* not possible to do this swap when the old dependency had a
* non-zero size but no previous fragment to free. This condition
* arises when the new block is an extension of the old block.
* Here, the first part of the fragment allocated to the new
* dependency is part of the block currently claimed on disk by
* the old dependency, so cannot legitimately be freed until the
* conditions for the new dependency are fulfilled.
*/
if (oldadp->ad_freefrag != NULL || oldadp->ad_oldblkno == 0) {
freefrag = newadp->ad_freefrag;
newadp->ad_freefrag = oldadp->ad_freefrag;
oldadp->ad_freefrag = freefrag;
}
/*
* If we are tracking a new directory-block allocation,
* move it from the old allocdirect to the new allocdirect.
*/
if ((wk = LIST_FIRST(&oldadp->ad_newdirblk)) != NULL) {
newdirblk = WK_NEWDIRBLK(wk);
WORKLIST_REMOVE(&newdirblk->db_list);
if (LIST_FIRST(&oldadp->ad_newdirblk) != NULL)
panic("allocdirect_merge: extra newdirblk");
WORKLIST_INSERT(&newadp->ad_newdirblk, &newdirblk->db_list);
}
free_allocdirect(adphead, oldadp, 0);
}
/*
* Allocate a new freefrag structure if needed.
*/
STATIC struct freefrag *
newfreefrag(struct inode *ip, daddr_t blkno, long size)
{
struct freefrag *freefrag;
struct fs *fs;
if (blkno == 0)
return (NULL);
fs = ip->i_fs;
if (fragnum(fs, blkno) + numfrags(fs, size) > fs->fs_frag)
panic("newfreefrag: frag size");
freefrag = pool_get(&freefrag_pool, PR_WAITOK);
freefrag->ff_list.wk_type = D_FREEFRAG;
freefrag->ff_state = DIP(ip, uid) & ~ONWORKLIST; /* used below */
freefrag->ff_inum = ip->i_number;
freefrag->ff_mnt = ITOV(ip)->v_mount;
freefrag->ff_devvp = ip->i_devvp;
freefrag->ff_blkno = blkno;
freefrag->ff_fragsize = size;
return (freefrag);
}
/*
* This workitem de-allocates fragments that were replaced during
* file block allocation.
*/
STATIC void
handle_workitem_freefrag(struct freefrag *freefrag)
{
struct inode tip;
struct ufs1_dinode dtip1;
tip.i_vnode = NULL;
tip.i_din1 = &dtip1;
tip.i_fs = VFSTOUFS(freefrag->ff_mnt)->um_fs;
tip.i_ump = VFSTOUFS(freefrag->ff_mnt);
tip.i_dev = freefrag->ff_devvp->v_rdev;
tip.i_number = freefrag->ff_inum;
tip.i_ffs1_uid = freefrag->ff_state & ~ONWORKLIST; /* set above */
ffs_blkfree(&tip, freefrag->ff_blkno, freefrag->ff_fragsize);
pool_put(&freefrag_pool, freefrag);
}
/*
* Indirect block allocation dependencies.
*
* The same dependencies that exist for a direct block also exist when
* a new block is allocated and pointed to by an entry in a block of
* indirect pointers. The undo/redo states described above are also
* used here. Because an indirect block contains many pointers that
* may have dependencies, a second copy of the entire in-memory indirect
* block is kept. The buffer cache copy is always completely up-to-date.
* The second copy, which is used only as a source for disk writes,
* contains only the safe pointers (i.e., those that have no remaining
* update dependencies). The second copy is freed when all pointers
* are safe. The cache is not allowed to replace indirect blocks with
* pending update dependencies. If a buffer containing an indirect
* block with dependencies is written, these routines will mark it
* dirty again. It can only be successfully written once all the
* dependencies are removed. The ffs_fsync routine in conjunction with
* softdep_sync_metadata work together to get all the dependencies
* removed so that a file can be successfully written to disk. Three
* procedures are used when setting up indirect block pointer
* dependencies. The division is necessary because of the organization
* of the "balloc" routine and because of the distinction between file
* pages and file metadata blocks.
*/
/*
* Allocate a new allocindir structure.
*/
/* inode for file being extended */
/* offset of pointer in indirect block */
/* disk block number being added */
/* previous block number, 0 if none */
STATIC struct allocindir *
newallocindir(struct inode *ip, int ptrno, daddr_t newblkno,
daddr_t oldblkno)
{
struct allocindir *aip;
aip = pool_get(&allocindir_pool, PR_WAITOK | PR_ZERO);
aip->ai_list.wk_type = D_ALLOCINDIR;
aip->ai_state = ATTACHED;
aip->ai_offset = ptrno;
aip->ai_newblkno = newblkno;
aip->ai_oldblkno = oldblkno;
aip->ai_freefrag = newfreefrag(ip, oldblkno, ip->i_fs->fs_bsize);
return (aip);
}
/*
* Called just before setting an indirect block pointer
* to a newly allocated file page.
*/
/* inode for file being extended */
/* allocated block number within file */
/* buffer with indirect blk referencing page */
/* offset of pointer in indirect block */
/* disk block number being added */
/* previous block number, 0 if none */
/* buffer holding allocated page */
void
softdep_setup_allocindir_page(struct inode *ip, daddr_t lbn, struct buf *bp,
int ptrno, daddr_t newblkno, daddr_t oldblkno, struct buf *nbp)
{
struct allocindir *aip;
struct pagedep *pagedep;
aip = newallocindir(ip, ptrno, newblkno, oldblkno);
ACQUIRE_LOCK(&lk);
/*
* If we are allocating a directory page, then we must
* allocate an associated pagedep to track additions and
* deletions.
*/
if ((DIP(ip, mode) & IFMT) == IFDIR &&
pagedep_lookup(ip, lbn, DEPALLOC, &pagedep) == 0)
WORKLIST_INSERT(&nbp->b_dep, &pagedep->pd_list);
if (nbp == NULL) {
/*
* XXXUBC - Yes, I know how to fix this, but not right now.
*/
panic("softdep_setup_allocindir_page: Bonk art in the head");
}
WORKLIST_INSERT(&nbp->b_dep, &aip->ai_list);
FREE_LOCK(&lk);
setup_allocindir_phase2(bp, ip, aip);
}
/*
* Called just before setting an indirect block pointer to a
* newly allocated indirect block.
*/
/* newly allocated indirect block */
/* inode for file being extended */
/* indirect block referencing allocated block */
/* offset of pointer in indirect block */
/* disk block number being added */
void
softdep_setup_allocindir_meta(struct buf *nbp, struct inode *ip,
struct buf *bp, int ptrno, daddr_t newblkno)
{
struct allocindir *aip;
aip = newallocindir(ip, ptrno, newblkno, 0);
ACQUIRE_LOCK(&lk);
WORKLIST_INSERT(&nbp->b_dep, &aip->ai_list);
FREE_LOCK(&lk);
setup_allocindir_phase2(bp, ip, aip);
}
/*
* Called to finish the allocation of the "aip" allocated
* by one of the two routines above.
*/
/* in-memory copy of the indirect block */
/* inode for file being extended */
/* allocindir allocated by the above routines */
STATIC void
setup_allocindir_phase2(struct buf *bp, struct inode *ip,
struct allocindir *aip)
{
struct worklist *wk;
struct indirdep *indirdep, *newindirdep;
struct bmsafemap *bmsafemap;
struct allocindir *oldaip;
struct freefrag *freefrag;
struct newblk *newblk;
if (bp->b_lblkno >= 0)
panic("setup_allocindir_phase2: not indir blk");
for (indirdep = NULL, newindirdep = NULL; ; ) {
ACQUIRE_LOCK(&lk);
LIST_FOREACH(wk, &bp->b_dep, wk_list) {
if (wk->wk_type != D_INDIRDEP)
continue;
indirdep = WK_INDIRDEP(wk);
break;
}
if (indirdep == NULL && newindirdep) {
indirdep = newindirdep;
WORKLIST_INSERT(&bp->b_dep, &indirdep->ir_list);
newindirdep = NULL;
}
FREE_LOCK(&lk);
if (indirdep) {
if (newblk_lookup(ip->i_fs, aip->ai_newblkno, 0,
&newblk) == 0)
panic("setup_allocindir: lost block");
ACQUIRE_LOCK(&lk);
if (newblk->nb_state == DEPCOMPLETE) {
aip->ai_state |= DEPCOMPLETE;
aip->ai_buf = NULL;
} else {
bmsafemap = newblk->nb_bmsafemap;
aip->ai_buf = bmsafemap->sm_buf;
LIST_REMOVE(newblk, nb_deps);
LIST_INSERT_HEAD(&bmsafemap->sm_allocindirhd,
aip, ai_deps);
}
LIST_REMOVE(newblk, nb_hash);
pool_put(&newblk_pool, newblk);
aip->ai_indirdep = indirdep;
/*
* Check to see if there is an existing dependency
* for this block. If there is, merge the old
* dependency into the new one.
*/
if (aip->ai_oldblkno == 0)
oldaip = NULL;
else
LIST_FOREACH(oldaip, &indirdep->ir_deplisthd, ai_next)
if (oldaip->ai_offset == aip->ai_offset)
break;
freefrag = NULL;
if (oldaip != NULL) {
if (oldaip->ai_newblkno != aip->ai_oldblkno) {
FREE_LOCK(&lk);
panic("setup_allocindir_phase2: blkno");
}
aip->ai_oldblkno = oldaip->ai_oldblkno;
freefrag = aip->ai_freefrag;
aip->ai_freefrag = oldaip->ai_freefrag;
oldaip->ai_freefrag = NULL;
free_allocindir(oldaip, NULL);
}
LIST_INSERT_HEAD(&indirdep->ir_deplisthd, aip, ai_next);
if (ip->i_ump->um_fstype == UM_UFS1)
((int32_t *)indirdep->ir_savebp->b_data)
[aip->ai_offset] = aip->ai_oldblkno;
else
((int64_t *)indirdep->ir_savebp->b_data)
[aip->ai_offset] = aip->ai_oldblkno;
FREE_LOCK(&lk);
if (freefrag != NULL)
handle_workitem_freefrag(freefrag);
}
if (newindirdep) {
if (indirdep->ir_savebp != NULL)
brelse(newindirdep->ir_savebp);
WORKITEM_FREE(newindirdep, D_INDIRDEP);
}
if (indirdep)
break;
newindirdep = pool_get(&indirdep_pool, PR_WAITOK);
newindirdep->ir_list.wk_type = D_INDIRDEP;
newindirdep->ir_state = ATTACHED;
if (ip->i_ump->um_fstype == UM_UFS1)
newindirdep->ir_state |= UFS1FMT;
LIST_INIT(&newindirdep->ir_deplisthd);
LIST_INIT(&newindirdep->ir_donehd);
if (bp->b_blkno == bp->b_lblkno) {
VOP_BMAP(bp->b_vp, bp->b_lblkno, NULL, &bp->b_blkno,
NULL);
}
newindirdep->ir_savebp =
getblk(ip->i_devvp, bp->b_blkno, bp->b_bcount, 0, INFSLP);
#if 0
BUF_KERNPROC(newindirdep->ir_savebp);
#endif
memcpy(newindirdep->ir_savebp->b_data, bp->b_data, bp->b_bcount);
}
}
/*
* Block de-allocation dependencies.
*
* When blocks are de-allocated, the on-disk pointers must be nullified before
* the blocks are made available for use by other files. (The true
* requirement is that old pointers must be nullified before new on-disk
* pointers are set. We chose this slightly more stringent requirement to
* reduce complexity.) Our implementation handles this dependency by updating
* the inode (or indirect block) appropriately but delaying the actual block
* de-allocation (i.e., freemap and free space count manipulation) until
* after the updated versions reach stable storage. After the disk is
* updated, the blocks can be safely de-allocated whenever it is convenient.
* This implementation handles only the common case of reducing a file's
* length to zero. Other cases are handled by the conventional synchronous
* write approach.
*
* The ffs implementation with which we worked double-checks
* the state of the block pointers and file size as it reduces
* a file's length. Some of this code is replicated here in our
* soft updates implementation. The freeblks->fb_chkcnt field is
* used to transfer a part of this information to the procedure
* that eventually de-allocates the blocks.
*
* This routine should be called from the routine that shortens
* a file's length, before the inode's size or block pointers
* are modified. It will save the block pointer information for
* later release and zero the inode so that the calling routine
* can release it.
*/
/* The inode whose length is to be reduced */
/* The new length for the file */
void
softdep_setup_freeblocks(struct inode *ip, off_t length)
{
struct freeblks *freeblks;
struct inodedep *inodedep;
struct allocdirect *adp;
struct vnode *vp;
struct buf *bp;
struct fs *fs;
int i, delay, error;
fs = ip->i_fs;
if (length != 0)
panic("softdep_setup_freeblocks: non-zero length");
freeblks = pool_get(&freeblks_pool, PR_WAITOK | PR_ZERO);
freeblks->fb_list.wk_type = D_FREEBLKS;
freeblks->fb_state = ATTACHED;
freeblks->fb_uid = DIP(ip, uid);
freeblks->fb_previousinum = ip->i_number;
freeblks->fb_devvp = ip->i_devvp;
freeblks->fb_mnt = ITOV(ip)->v_mount;
freeblks->fb_oldsize = DIP(ip, size);
freeblks->fb_newsize = length;
freeblks->fb_chkcnt = DIP(ip, blocks);
for (i = 0; i < NDADDR; i++) {
freeblks->fb_dblks[i] = DIP(ip, db[i]);
DIP_ASSIGN(ip, db[i], 0);
}
for (i = 0; i < NIADDR; i++) {
freeblks->fb_iblks[i] = DIP(ip, ib[i]);
DIP_ASSIGN(ip, ib[i], 0);
}
DIP_ASSIGN(ip, blocks, 0);
DIP_ASSIGN(ip, size, 0);
/*
* Push the zero'ed inode to to its disk buffer so that we are free
* to delete its dependencies below. Once the dependencies are gone
* the buffer can be safely released.
*/
if ((error = bread(ip->i_devvp,
fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
(int)fs->fs_bsize, &bp)) != 0)
softdep_error("softdep_setup_freeblocks", error);
if (ip->i_ump->um_fstype == UM_UFS1)
*((struct ufs1_dinode *) bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1;
else
*((struct ufs2_dinode *) bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
/*
* Find and eliminate any inode dependencies.
*/
ACQUIRE_LOCK(&lk);
(void) inodedep_lookup(fs, ip->i_number, DEPALLOC, &inodedep);
if ((inodedep->id_state & IOSTARTED) != 0) {
FREE_LOCK(&lk);
panic("softdep_setup_freeblocks: inode busy");
}
/*
* Add the freeblks structure to the list of operations that
* must await the zero'ed inode being written to disk. If we
* still have a bitmap dependency (delay == 0), then the inode
* has never been written to disk, so we can process the
* freeblks below once we have deleted the dependencies.
*/
delay = (inodedep->id_state & DEPCOMPLETE);
if (delay)
WORKLIST_INSERT(&inodedep->id_bufwait, &freeblks->fb_list);
/*
* Because the file length has been truncated to zero, any
* pending block allocation dependency structures associated
* with this inode are obsolete and can simply be de-allocated.
* We must first merge the two dependency lists to get rid of
* any duplicate freefrag structures, then purge the merged list.
* If we still have a bitmap dependency, then the inode has never
* been written to disk, so we can free any fragments without delay.
*/
merge_inode_lists(inodedep);
while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != NULL)
free_allocdirect(&inodedep->id_inoupdt, adp, delay);
FREE_LOCK(&lk);
bdwrite(bp);
/*
* We must wait for any I/O in progress to finish so that
* all potential buffers on the dirty list will be visible.
* Once they are all there, walk the list and get rid of
* any dependencies.
*/
vp = ITOV(ip);
ACQUIRE_LOCK(&lk);
drain_output(vp, 1);
while ((bp = LIST_FIRST(&vp->v_dirtyblkhd))) {
if (getdirtybuf(bp, MNT_WAIT) <= 0)
break;
(void) inodedep_lookup(fs, ip->i_number, 0, &inodedep);
deallocate_dependencies(bp, inodedep);
bp->b_flags |= B_INVAL | B_NOCACHE;
FREE_LOCK(&lk);
brelse(bp);
ACQUIRE_LOCK(&lk);
}
if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) != 0)
(void) free_inodedep(inodedep);
if (delay) {
freeblks->fb_state |= DEPCOMPLETE;
/*
* If the inode with zeroed block pointers is now on disk we
* can start freeing blocks. Add freeblks to the worklist
* instead of calling handle_workitem_freeblocks() directly as
* it is more likely that additional IO is needed to complete
* the request than in the !delay case.
*/
if ((freeblks->fb_state & ALLCOMPLETE) == ALLCOMPLETE)
add_to_worklist(&freeblks->fb_list);
}
FREE_LOCK(&lk);
/*
* If the inode has never been written to disk (delay == 0),
* then we can process the freeblks now that we have deleted
* the dependencies.
*/
if (!delay)
handle_workitem_freeblocks(freeblks);
}
/*
* Reclaim any dependency structures from a buffer that is about to
* be reallocated to a new vnode. The buffer must be locked, thus,
* no I/O completion operations can occur while we are manipulating
* its associated dependencies. The mutex is held so that other I/O's
* associated with related dependencies do not occur.
*/
STATIC void
deallocate_dependencies(struct buf *bp, struct inodedep *inodedep)
{
struct worklist *wk;
struct indirdep *indirdep;
struct allocindir *aip;
struct pagedep *pagedep;
struct dirrem *dirrem;
struct diradd *dap;
int i;
while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) {
switch (wk->wk_type) {
case D_INDIRDEP:
indirdep = WK_INDIRDEP(wk);
/*
* None of the indirect pointers will ever be visible,
* so they can simply be tossed. GOINGAWAY ensures
* that allocated pointers will be saved in the buffer
* cache until they are freed. Note that they will
* only be able to be found by their physical address
* since the inode mapping the logical address will
* be gone. The save buffer used for the safe copy
* was allocated in setup_allocindir_phase2 using
* the physical address so it could be used for this
* purpose. Hence we swap the safe copy with the real
* copy, allowing the safe copy to be freed and holding
* on to the real copy for later use in indir_trunc.
*/
if (indirdep->ir_state & GOINGAWAY) {
FREE_LOCK(&lk);
panic("deallocate_dependencies: already gone");
}
indirdep->ir_state |= GOINGAWAY;
while ((aip = LIST_FIRST(&indirdep->ir_deplisthd)))
free_allocindir(aip, inodedep);
if (bp->b_lblkno >= 0 ||
bp->b_blkno != indirdep->ir_savebp->b_lblkno) {
FREE_LOCK(&lk);
panic("deallocate_dependencies: not indir");
}
memcpy(indirdep->ir_savebp->b_data, bp->b_data,
bp->b_bcount);
WORKLIST_REMOVE(wk);
WORKLIST_INSERT(&indirdep->ir_savebp->b_dep, wk);
continue;
case D_PAGEDEP:
pagedep = WK_PAGEDEP(wk);
/*
* None of the directory additions will ever be
* visible, so they can simply be tossed.
*/
for (i = 0; i < DAHASHSZ; i++)
while ((dap =
LIST_FIRST(&pagedep->pd_diraddhd[i])))
free_diradd(dap);
while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)))
free_diradd(dap);
/*
* Copy any directory remove dependencies to the list
* to be processed after the zero'ed inode is written.
* If the inode has already been written, then they
* can be dumped directly onto the work list.
*/
while ((dirrem = LIST_FIRST(&pagedep->pd_dirremhd))) {
LIST_REMOVE(dirrem, dm_next);
dirrem->dm_dirinum = pagedep->pd_ino;
if (inodedep == NULL ||
(inodedep->id_state & ALLCOMPLETE) ==
ALLCOMPLETE)
add_to_worklist(&dirrem->dm_list);
else
WORKLIST_INSERT(&inodedep->id_bufwait,
&dirrem->dm_list);
}
if ((pagedep->pd_state & NEWBLOCK) != 0) {
LIST_FOREACH(wk, &inodedep->id_bufwait, wk_list)
if (wk->wk_type == D_NEWDIRBLK &&
WK_NEWDIRBLK(wk)->db_pagedep ==
pagedep)
break;
if (wk != NULL) {
WORKLIST_REMOVE(wk);
free_newdirblk(WK_NEWDIRBLK(wk));
} else {
FREE_LOCK(&lk);
panic("deallocate_dependencies: "
"lost pagedep");
}
}
WORKLIST_REMOVE(&pagedep->pd_list);
LIST_REMOVE(pagedep, pd_hash);
WORKITEM_FREE(pagedep, D_PAGEDEP);
continue;
case D_ALLOCINDIR:
free_allocindir(WK_ALLOCINDIR(wk), inodedep);
continue;
case D_ALLOCDIRECT:
case D_INODEDEP:
FREE_LOCK(&lk);
panic("deallocate_dependencies: Unexpected type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
default:
FREE_LOCK(&lk);
panic("deallocate_dependencies: Unknown type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
}
}
}
/*
* Free an allocdirect. Generate a new freefrag work request if appropriate.
* This routine must be called with splbio interrupts blocked.
*/
STATIC void
free_allocdirect(struct allocdirectlst *adphead, struct allocdirect *adp,
int delay)
{
struct newdirblk *newdirblk;
struct worklist *wk;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("free_allocdirect: lock not held");
#endif
if ((adp->ad_state & DEPCOMPLETE) == 0)
LIST_REMOVE(adp, ad_deps);
TAILQ_REMOVE(adphead, adp, ad_next);
if ((adp->ad_state & COMPLETE) == 0)
WORKLIST_REMOVE(&adp->ad_list);
if (adp->ad_freefrag != NULL) {
if (delay)
WORKLIST_INSERT(&adp->ad_inodedep->id_bufwait,
&adp->ad_freefrag->ff_list);
else
add_to_worklist(&adp->ad_freefrag->ff_list);
}
if ((wk = LIST_FIRST(&adp->ad_newdirblk)) != NULL) {
newdirblk = WK_NEWDIRBLK(wk);
WORKLIST_REMOVE(&newdirblk->db_list);
if (LIST_FIRST(&adp->ad_newdirblk) != NULL)
panic("free_allocdirect: extra newdirblk");
if (delay)
WORKLIST_INSERT(&adp->ad_inodedep->id_bufwait,
&newdirblk->db_list);
else
free_newdirblk(newdirblk);
}
WORKITEM_FREE(adp, D_ALLOCDIRECT);
}
/*
* Free a newdirblk. Clear the NEWBLOCK flag on its associated pagedep.
* This routine must be called with splbio interrupts blocked.
*/
void
free_newdirblk(struct newdirblk *newdirblk)
{
struct pagedep *pagedep;
struct diradd *dap;
int i;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("free_newdirblk: lock not held");
#endif
/*
* If the pagedep is still linked onto the directory buffer
* dependency chain, then some of the entries on the
* pd_pendinghd list may not be committed to disk yet. In
* this case, we will simply clear the NEWBLOCK flag and
* let the pd_pendinghd list be processed when the pagedep
* is next written. If the pagedep is no longer on the buffer
* dependency chain, then all the entries on the pd_pending
* list are committed to disk and we can free them here.
*/
pagedep = newdirblk->db_pagedep;
pagedep->pd_state &= ~NEWBLOCK;
if ((pagedep->pd_state & ONWORKLIST) == 0)
while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != NULL)
free_diradd(dap);
/*
* If no dependencies remain, the pagedep will be freed.
*/
for (i = 0; i < DAHASHSZ; i++)
if (LIST_FIRST(&pagedep->pd_diraddhd[i]) != NULL)
break;
if (i == DAHASHSZ && (pagedep->pd_state & ONWORKLIST) == 0) {
LIST_REMOVE(pagedep, pd_hash);
WORKITEM_FREE(pagedep, D_PAGEDEP);
}
WORKITEM_FREE(newdirblk, D_NEWDIRBLK);
}
/*
* Prepare an inode to be freed. The actual free operation is not
* done until the zero'ed inode has been written to disk.
*/
void
softdep_freefile(struct vnode *pvp, ufsino_t ino, mode_t mode)
{
struct inode *ip = VTOI(pvp);
struct inodedep *inodedep;
struct freefile *freefile;
/*
* This sets up the inode de-allocation dependency.
*/
freefile = pool_get(&freefile_pool, PR_WAITOK);
freefile->fx_list.wk_type = D_FREEFILE;
freefile->fx_list.wk_state = 0;
freefile->fx_mode = mode;
freefile->fx_oldinum = ino;
freefile->fx_devvp = ip->i_devvp;
freefile->fx_mnt = ITOV(ip)->v_mount;
/*
* If the inodedep does not exist, then the zero'ed inode has
* been written to disk. If the allocated inode has never been
* written to disk, then the on-disk inode is zero'ed. In either
* case we can free the file immediately.
*/
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(ip->i_fs, ino, 0, &inodedep) == 0 ||
check_inode_unwritten(inodedep)) {
FREE_LOCK(&lk);
handle_workitem_freefile(freefile);
return;
}
WORKLIST_INSERT(&inodedep->id_inowait, &freefile->fx_list);
FREE_LOCK(&lk);
}
/*
* Check to see if an inode has never been written to disk. If
* so free the inodedep and return success, otherwise return failure.
* This routine must be called with splbio interrupts blocked.
*
* If we still have a bitmap dependency, then the inode has never
* been written to disk. Drop the dependency as it is no longer
* necessary since the inode is being deallocated. We set the
* ALLCOMPLETE flags since the bitmap now properly shows that the
* inode is not allocated. Even if the inode is actively being
* written, it has been rolled back to its zero'ed state, so we
* are ensured that a zero inode is what is on the disk. For short
* lived files, this change will usually result in removing all the
* dependencies from the inode so that it can be freed immediately.
*/
STATIC int
check_inode_unwritten(struct inodedep *inodedep)
{
splassert(IPL_BIO);
if ((inodedep->id_state & DEPCOMPLETE) != 0 ||
LIST_FIRST(&inodedep->id_pendinghd) != NULL ||
LIST_FIRST(&inodedep->id_bufwait) != NULL ||
LIST_FIRST(&inodedep->id_inowait) != NULL ||
TAILQ_FIRST(&inodedep->id_inoupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL ||
inodedep->id_nlinkdelta != 0)
return (0);
inodedep->id_state |= ALLCOMPLETE;
LIST_REMOVE(inodedep, id_deps);
inodedep->id_buf = NULL;
if (inodedep->id_state & ONWORKLIST)
WORKLIST_REMOVE(&inodedep->id_list);
if (inodedep->id_savedino1 != NULL) {
free(inodedep->id_savedino1, M_INODEDEP, inodedep->id_unsize);
inodedep->id_savedino1 = NULL;
}
if (free_inodedep(inodedep) == 0) {
FREE_LOCK(&lk);
panic("check_inode_unwritten: busy inode");
}
return (1);
}
/*
* Try to free an inodedep structure. Return 1 if it could be freed.
*/
STATIC int
free_inodedep(struct inodedep *inodedep)
{
if ((inodedep->id_state & ONWORKLIST) != 0 ||
(inodedep->id_state & ALLCOMPLETE) != ALLCOMPLETE ||
LIST_FIRST(&inodedep->id_pendinghd) != NULL ||
LIST_FIRST(&inodedep->id_bufwait) != NULL ||
LIST_FIRST(&inodedep->id_inowait) != NULL ||
TAILQ_FIRST(&inodedep->id_inoupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL ||
inodedep->id_nlinkdelta != 0 || inodedep->id_savedino1 != NULL)
return (0);
LIST_REMOVE(inodedep, id_hash);
WORKITEM_FREE(inodedep, D_INODEDEP);
num_inodedep -= 1;
return (1);
}
/*
* This workitem routine performs the block de-allocation.
* The workitem is added to the pending list after the updated
* inode block has been written to disk. As mentioned above,
* checks regarding the number of blocks de-allocated (compared
* to the number of blocks allocated for the file) are also
* performed in this function.
*/
STATIC void
handle_workitem_freeblocks(struct freeblks *freeblks)
{
struct inode tip;
daddr_t bn;
union {
struct ufs1_dinode di1;
struct ufs2_dinode di2;
} di;
struct fs *fs;
int i, level, bsize;
long nblocks, blocksreleased = 0;
int error, allerror = 0;
daddr_t baselbns[NIADDR], tmpval;
if (VFSTOUFS(freeblks->fb_mnt)->um_fstype == UM_UFS1)
tip.i_din1 = &di.di1;
else
tip.i_din2 = &di.di2;
tip.i_fs = fs = VFSTOUFS(freeblks->fb_mnt)->um_fs;
tip.i_number = freeblks->fb_previousinum;
tip.i_ump = VFSTOUFS(freeblks->fb_mnt);
tip.i_dev = freeblks->fb_devvp->v_rdev;
DIP_ASSIGN(&tip, size, freeblks->fb_oldsize);
DIP_ASSIGN(&tip, uid, freeblks->fb_uid);
tip.i_vnode = NULL;
tmpval = 1;
baselbns[0] = NDADDR;
for (i = 1; i < NIADDR; i++) {
tmpval *= NINDIR(fs);
baselbns[i] = baselbns[i - 1] + tmpval;
}
nblocks = btodb(fs->fs_bsize);
blocksreleased = 0;
/*
* Indirect blocks first.
*/
for (level = (NIADDR - 1); level >= 0; level--) {
if ((bn = freeblks->fb_iblks[level]) == 0)
continue;
if ((error = indir_trunc(&tip, fsbtodb(fs, bn), level,
baselbns[level], &blocksreleased)) != 0)
allerror = error;
ffs_blkfree(&tip, bn, fs->fs_bsize);
blocksreleased += nblocks;
}
/*
* All direct blocks or frags.
*/
for (i = (NDADDR - 1); i >= 0; i--) {
if ((bn = freeblks->fb_dblks[i]) == 0)
continue;
bsize = blksize(fs, &tip, i);
ffs_blkfree(&tip, bn, bsize);
blocksreleased += btodb(bsize);
}
#ifdef DIAGNOSTIC
if (freeblks->fb_chkcnt != blocksreleased)
printf("handle_workitem_freeblocks: block count\n");
if (allerror)
softdep_error("handle_workitem_freeblks", allerror);
#endif /* DIAGNOSTIC */
WORKITEM_FREE(freeblks, D_FREEBLKS);
}
/*
* Release blocks associated with the inode ip and stored in the indirect
* block dbn. If level is greater than SINGLE, the block is an indirect block
* and recursive calls to indirtrunc must be used to cleanse other indirect
* blocks.
*/
STATIC int
indir_trunc(struct inode *ip, daddr_t dbn, int level, daddr_t lbn,
long *countp)
{
struct buf *bp;
int32_t *bap1 = NULL;
int64_t nb, *bap2 = NULL;
struct fs *fs;
struct worklist *wk;
struct indirdep *indirdep;
int i, lbnadd, nblocks, ufs1fmt;
int error, allerror = 0;
fs = ip->i_fs;
lbnadd = 1;
for (i = level; i > 0; i--)
lbnadd *= NINDIR(fs);
/*
* Get buffer of block pointers to be freed. This routine is not
* called until the zero'ed inode has been written, so it is safe
* to free blocks as they are encountered. Because the inode has
* been zero'ed, calls to bmap on these blocks will fail. So, we
* have to use the on-disk address and the block device for the
* filesystem to look them up. If the file was deleted before its
* indirect blocks were all written to disk, the routine that set
* us up (deallocate_dependencies) will have arranged to leave
* a complete copy of the indirect block in memory for our use.
* Otherwise we have to read the blocks in from the disk.
*/
ACQUIRE_LOCK(&lk);
if ((bp = incore(ip->i_devvp, dbn)) != NULL &&
(wk = LIST_FIRST(&bp->b_dep)) != NULL) {
if (wk->wk_type != D_INDIRDEP ||
(indirdep = WK_INDIRDEP(wk))->ir_savebp != bp ||
(indirdep->ir_state & GOINGAWAY) == 0) {
FREE_LOCK(&lk);
panic("indir_trunc: lost indirdep");
}
WORKLIST_REMOVE(wk);
WORKITEM_FREE(indirdep, D_INDIRDEP);
if (LIST_FIRST(&bp->b_dep) != NULL) {
FREE_LOCK(&lk);
panic("indir_trunc: dangling dep");
}
FREE_LOCK(&lk);
} else {
FREE_LOCK(&lk);
error = bread(ip->i_devvp, dbn, (int)fs->fs_bsize, &bp);
if (error)
return (error);
}
/*
* Recursively free indirect blocks.
*/
if (ip->i_ump->um_fstype == UM_UFS1) {
ufs1fmt = 1;
bap1 = (int32_t *)bp->b_data;
} else {
ufs1fmt = 0;
bap2 = (int64_t *)bp->b_data;
}
nblocks = btodb(fs->fs_bsize);
for (i = NINDIR(fs) - 1; i >= 0; i--) {
if (ufs1fmt)
nb = bap1[i];
else
nb = bap2[i];
if (nb == 0)
continue;
if (level != 0) {
if ((error = indir_trunc(ip, fsbtodb(fs, nb),
level - 1, lbn + (i * lbnadd), countp)) != 0)
allerror = error;
}
ffs_blkfree(ip, nb, fs->fs_bsize);
*countp += nblocks;
}
bp->b_flags |= B_INVAL | B_NOCACHE;
brelse(bp);
return (allerror);
}
/*
* Free an allocindir.
* This routine must be called with splbio interrupts blocked.
*/
STATIC void
free_allocindir(struct allocindir *aip, struct inodedep *inodedep)
{
struct freefrag *freefrag;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("free_allocindir: lock not held");
#endif
if ((aip->ai_state & DEPCOMPLETE) == 0)
LIST_REMOVE(aip, ai_deps);
if (aip->ai_state & ONWORKLIST)
WORKLIST_REMOVE(&aip->ai_list);
LIST_REMOVE(aip, ai_next);
if ((freefrag = aip->ai_freefrag) != NULL) {
if (inodedep == NULL)
add_to_worklist(&freefrag->ff_list);
else
WORKLIST_INSERT(&inodedep->id_bufwait,
&freefrag->ff_list);
}
WORKITEM_FREE(aip, D_ALLOCINDIR);
}
/*
* Directory entry addition dependencies.
*
* When adding a new directory entry, the inode (with its incremented link
* count) must be written to disk before the directory entry's pointer to it.
* Also, if the inode is newly allocated, the corresponding freemap must be
* updated (on disk) before the directory entry's pointer. These requirements
* are met via undo/redo on the directory entry's pointer, which consists
* simply of the inode number.
*
* As directory entries are added and deleted, the free space within a
* directory block can become fragmented. The ufs file system will compact
* a fragmented directory block to make space for a new entry. When this
* occurs, the offsets of previously added entries change. Any "diradd"
* dependency structures corresponding to these entries must be updated with
* the new offsets.
*/
/*
* This routine is called after the in-memory inode's link
* count has been incremented, but before the directory entry's
* pointer to the inode has been set.
*/
/* buffer containing directory block */
/* inode for directory */
/* offset of new entry in directory */
/* inode referenced by new directory entry */
/* non-NULL => contents of new mkdir */
/* entry is in a newly allocated block */
int
softdep_setup_directory_add(struct buf *bp, struct inode *dp, off_t diroffset,
long newinum, struct buf *newdirbp, int isnewblk)
{
int offset; /* offset of new entry within directory block */
daddr_t lbn; /* block in directory containing new entry */
struct fs *fs;
struct diradd *dap;
struct allocdirect *adp;
struct pagedep *pagedep;
struct inodedep *inodedep;
struct newdirblk *newdirblk = NULL;
struct mkdir *mkdir1, *mkdir2;
fs = dp->i_fs;
lbn = lblkno(fs, diroffset);
offset = blkoff(fs, diroffset);
dap = pool_get(&diradd_pool, PR_WAITOK | PR_ZERO);
dap->da_list.wk_type = D_DIRADD;
dap->da_offset = offset;
dap->da_newinum = newinum;
dap->da_state = ATTACHED;
if (isnewblk && lbn < NDADDR && fragoff(fs, diroffset) == 0) {
newdirblk = pool_get(&newdirblk_pool, PR_WAITOK);
newdirblk->db_list.wk_type = D_NEWDIRBLK;
newdirblk->db_state = 0;
}
if (newdirbp == NULL) {
dap->da_state |= DEPCOMPLETE;
ACQUIRE_LOCK(&lk);
} else {
dap->da_state |= MKDIR_BODY | MKDIR_PARENT;
mkdir1 = pool_get(&mkdir_pool, PR_WAITOK);
mkdir1->md_list.wk_type = D_MKDIR;
mkdir1->md_state = MKDIR_BODY;
mkdir1->md_diradd = dap;
mkdir2 = pool_get(&mkdir_pool, PR_WAITOK);
mkdir2->md_list.wk_type = D_MKDIR;
mkdir2->md_state = MKDIR_PARENT;
mkdir2->md_diradd = dap;
/*
* Dependency on "." and ".." being written to disk.
*/
mkdir1->md_buf = newdirbp;
ACQUIRE_LOCK(&lk);
LIST_INSERT_HEAD(&mkdirlisthd, mkdir1, md_mkdirs);
WORKLIST_INSERT(&newdirbp->b_dep, &mkdir1->md_list);
FREE_LOCK(&lk);
bdwrite(newdirbp);
/*
* Dependency on link count increase for parent directory
*/
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(fs, dp->i_number, 0, &inodedep) == 0
|| (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) {
dap->da_state &= ~MKDIR_PARENT;
WORKITEM_FREE(mkdir2, D_MKDIR);
} else {
LIST_INSERT_HEAD(&mkdirlisthd, mkdir2, md_mkdirs);
WORKLIST_INSERT(&inodedep->id_bufwait,&mkdir2->md_list);
}
}
/*
* Link into parent directory pagedep to await its being written.
*/
if (pagedep_lookup(dp, lbn, DEPALLOC, &pagedep) == 0)
WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list);
dap->da_pagedep = pagedep;
LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)], dap,
da_pdlist);
/*
* Link into its inodedep. Put it on the id_bufwait list if the inode
* is not yet written. If it is written, do the post-inode write
* processing to put it on the id_pendinghd list.
*/
(void) inodedep_lookup(fs, newinum, DEPALLOC, &inodedep);
if ((inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE)
diradd_inode_written(dap, inodedep);
else
WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list);
if (isnewblk) {
/*
* Directories growing into indirect blocks are rare
* enough and the frequency of new block allocation
* in those cases even more rare, that we choose not
* to bother tracking them. Rather we simply force the
* new directory entry to disk.
*/
if (lbn >= NDADDR) {
FREE_LOCK(&lk);
/*
* We only have a new allocation when at the
* beginning of a new block, not when we are
* expanding into an existing block.
*/
if (blkoff(fs, diroffset) == 0)
return (1);
return (0);
}
/*
* We only have a new allocation when at the beginning
* of a new fragment, not when we are expanding into an
* existing fragment. Also, there is nothing to do if we
* are already tracking this block.
*/
if (fragoff(fs, diroffset) != 0) {
FREE_LOCK(&lk);
return (0);
}
if ((pagedep->pd_state & NEWBLOCK) != 0) {
WORKITEM_FREE(newdirblk, D_NEWDIRBLK);
FREE_LOCK(&lk);
return (0);
}
/*
* Find our associated allocdirect and have it track us.
*/
if (inodedep_lookup(fs, dp->i_number, 0, &inodedep) == 0)
panic("softdep_setup_directory_add: lost inodedep");
adp = TAILQ_LAST(&inodedep->id_newinoupdt, allocdirectlst);
if (adp == NULL || adp->ad_lbn != lbn) {
FREE_LOCK(&lk);
panic("softdep_setup_directory_add: lost entry");
}
pagedep->pd_state |= NEWBLOCK;
newdirblk->db_pagedep = pagedep;
WORKLIST_INSERT(&adp->ad_newdirblk, &newdirblk->db_list);
}
FREE_LOCK(&lk);
return (0);
}
/*
* This procedure is called to change the offset of a directory
* entry when compacting a directory block which must be owned
* exclusively by the caller. Note that the actual entry movement
* must be done in this procedure to ensure that no I/O completions
* occur while the move is in progress.
*/
/* inode for directory */
/* address of dp->i_offset */
/* address of old directory location */
/* address of new directory location */
/* size of directory entry */
void
softdep_change_directoryentry_offset(struct inode *dp, caddr_t base,
caddr_t oldloc, caddr_t newloc, int entrysize)
{
int offset, oldoffset, newoffset;
struct pagedep *pagedep;
struct diradd *dap;
daddr_t lbn;
ACQUIRE_LOCK(&lk);
lbn = lblkno(dp->i_fs, dp->i_offset);
offset = blkoff(dp->i_fs, dp->i_offset);
if (pagedep_lookup(dp, lbn, 0, &pagedep) == 0)
goto done;
oldoffset = offset + (oldloc - base);
newoffset = offset + (newloc - base);
LIST_FOREACH(dap, &pagedep->pd_diraddhd[DIRADDHASH(oldoffset)], da_pdlist) {
if (dap->da_offset != oldoffset)
continue;
dap->da_offset = newoffset;
if (DIRADDHASH(newoffset) == DIRADDHASH(oldoffset))
break;
LIST_REMOVE(dap, da_pdlist);
LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(newoffset)],
dap, da_pdlist);
break;
}
if (dap == NULL) {
LIST_FOREACH(dap, &pagedep->pd_pendinghd, da_pdlist) {
if (dap->da_offset == oldoffset) {
dap->da_offset = newoffset;
break;
}
}
}
done:
memmove(newloc, oldloc, entrysize);
FREE_LOCK(&lk);
}
/*
* Free a diradd dependency structure. This routine must be called
* with splbio interrupts blocked.
*/
STATIC void
free_diradd(struct diradd *dap)
{
struct dirrem *dirrem;
struct pagedep *pagedep;
struct inodedep *inodedep;
struct mkdir *mkdir, *nextmd;
splassert(IPL_BIO);
#ifdef DEBUG
if (lk.lkt_held == -1)
panic("free_diradd: lock not held");
#endif
WORKLIST_REMOVE(&dap->da_list);
LIST_REMOVE(dap, da_pdlist);
if ((dap->da_state & DIRCHG) == 0) {
pagedep = dap->da_pagedep;
} else {
dirrem = dap->da_previous;
pagedep = dirrem->dm_pagedep;
dirrem->dm_dirinum = pagedep->pd_ino;
add_to_worklist(&dirrem->dm_list);
}
if (inodedep_lookup(VFSTOUFS(pagedep->pd_mnt)->um_fs, dap->da_newinum,
0, &inodedep) != 0)
(void) free_inodedep(inodedep);
if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != 0) {
for (mkdir = LIST_FIRST(&mkdirlisthd); mkdir; mkdir = nextmd) {
nextmd = LIST_NEXT(mkdir, md_mkdirs);
if (mkdir->md_diradd != dap)
continue;
dap->da_state &= ~mkdir->md_state;
WORKLIST_REMOVE(&mkdir->md_list);
LIST_REMOVE(mkdir, md_mkdirs);
WORKITEM_FREE(mkdir, D_MKDIR);
}
if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != 0) {
FREE_LOCK(&lk);
panic("free_diradd: unfound ref");
}
}
WORKITEM_FREE(dap, D_DIRADD);
}
/*
* Directory entry removal dependencies.
*
* When removing a directory entry, the entry's inode pointer must be
* zero'ed on disk before the corresponding inode's link count is decremented
* (possibly freeing the inode for re-use). This dependency is handled by
* updating the directory entry but delaying the inode count reduction until
* after the directory block has been written to disk. After this point, the
* inode count can be decremented whenever it is convenient.
*/
/*
* This routine should be called immediately after removing
* a directory entry. The inode's link count should not be
* decremented by the calling procedure -- the soft updates
* code will do this task when it is safe.
*/
/* buffer containing directory block */
/* inode for the directory being modified */
/* inode for directory entry being removed */
/* indicates if doing RMDIR */
void
softdep_setup_remove(struct buf *bp, struct inode *dp, struct inode *ip,
int isrmdir)
{
struct dirrem *dirrem, *prevdirrem;
/*
* Allocate a new dirrem if appropriate and ACQUIRE_LOCK.
*/
dirrem = newdirrem(bp, dp, ip, isrmdir, &prevdirrem);
/*
* If the COMPLETE flag is clear, then there were no active
* entries and we want to roll back to a zeroed entry until
* the new inode is committed to disk. If the COMPLETE flag is
* set then we have deleted an entry that never made it to
* disk. If the entry we deleted resulted from a name change,
* then the old name still resides on disk. We cannot delete
* its inode (returned to us in prevdirrem) until the zeroed
* directory entry gets to disk. The new inode has never been
* referenced on the disk, so can be deleted immediately.
*/
if ((dirrem->dm_state & COMPLETE) == 0) {
LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd, dirrem,
dm_next);
FREE_LOCK(&lk);
} else {
if (prevdirrem != NULL)
LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd,
prevdirrem, dm_next);
dirrem->dm_dirinum = dirrem->dm_pagedep->pd_ino;
FREE_LOCK(&lk);
handle_workitem_remove(dirrem);
}
}
STATIC long num_dirrem; /* number of dirrem allocated */
/*
* Allocate a new dirrem if appropriate and return it along with
* its associated pagedep. Called without a lock, returns with lock.
*/
/* buffer containing directory block */
/* inode for the directory being modified */
/* inode for directory entry being removed */
/* indicates if doing RMDIR */
/* previously referenced inode, if any */
STATIC struct dirrem *
newdirrem(struct buf *bp, struct inode *dp, struct inode *ip, int isrmdir,
struct dirrem **prevdirremp)
{
int offset;
daddr_t lbn;
struct diradd *dap;
struct dirrem *dirrem;
struct pagedep *pagedep;
/*
* Whiteouts have no deletion dependencies.
*/
if (ip == NULL)
panic("newdirrem: whiteout");
/*
* If we are over our limit, try to improve the situation.
* Limiting the number of dirrem structures will also limit
* the number of freefile and freeblks structures.
*/
if (num_dirrem > max_softdeps / 2)
(void) request_cleanup(FLUSH_REMOVE, 0);
num_dirrem += 1;
dirrem = pool_get(&dirrem_pool, PR_WAITOK | PR_ZERO);
dirrem->dm_list.wk_type = D_DIRREM;
dirrem->dm_state = isrmdir ? RMDIR : 0;
dirrem->dm_mnt = ITOV(ip)->v_mount;
dirrem->dm_oldinum = ip->i_number;
*prevdirremp = NULL;
ACQUIRE_LOCK(&lk);
lbn = lblkno(dp->i_fs, dp->i_offset);
offset = blkoff(dp->i_fs, dp->i_offset);
if (pagedep_lookup(dp, lbn, DEPALLOC, &pagedep) == 0)
WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list);
dirrem->dm_pagedep = pagedep;
/*
* Check for a diradd dependency for the same directory entry.
* If present, then both dependencies become obsolete and can
* be de-allocated. Check for an entry on both the pd_dirraddhd
* list and the pd_pendinghd list.
*/
LIST_FOREACH(dap, &pagedep->pd_diraddhd[DIRADDHASH(offset)], da_pdlist)
if (dap->da_offset == offset)
break;
if (dap == NULL) {
LIST_FOREACH(dap, &pagedep->pd_pendinghd, da_pdlist)
if (dap->da_offset == offset)
break;
if (dap == NULL)
return (dirrem);
}
/*
* Must be ATTACHED at this point.
*/
if ((dap->da_state & ATTACHED) == 0) {
FREE_LOCK(&lk);
panic("newdirrem: not ATTACHED");
}
if (dap->da_newinum != ip->i_number) {
FREE_LOCK(&lk);
panic("newdirrem: inum %u should be %u",
ip->i_number, dap->da_newinum);
}
/*
* If we are deleting a changed name that never made it to disk,
* then return the dirrem describing the previous inode (which
* represents the inode currently referenced from this entry on disk).
*/
if ((dap->da_state & DIRCHG) != 0) {
*prevdirremp = dap->da_previous;
dap->da_state &= ~DIRCHG;
dap->da_pagedep = pagedep;
}
/*
* We are deleting an entry that never made it to disk.
* Mark it COMPLETE so we can delete its inode immediately.
*/
dirrem->dm_state |= COMPLETE;
free_diradd(dap);
return (dirrem);
}
/*
* Directory entry change dependencies.
*
* Changing an existing directory entry requires that an add operation
* be completed first followed by a deletion. The semantics for the addition
* are identical to the description of adding a new entry above except
* that the rollback is to the old inode number rather than zero. Once
* the addition dependency is completed, the removal is done as described
* in the removal routine above.
*/
/*
* This routine should be called immediately after changing
* a directory entry. The inode's link count should not be
* decremented by the calling procedure -- the soft updates
* code will perform this task when it is safe.
*/
/* buffer containing directory block */
/* inode for the directory being modified */
/* inode for directory entry being removed */
/* new inode number for changed entry */
/* indicates if doing RMDIR */
void
softdep_setup_directory_change(struct buf *bp, struct inode *dp,
struct inode *ip, long newinum, int isrmdir)
{
int offset;
struct diradd *dap;
struct dirrem *dirrem, *prevdirrem;
struct pagedep *pagedep;
struct inodedep *inodedep;
offset = blkoff(dp->i_fs, dp->i_offset);
dap = pool_get(&diradd_pool, PR_WAITOK | PR_ZERO);
dap->da_list.wk_type = D_DIRADD;
dap->da_state = DIRCHG | ATTACHED | DEPCOMPLETE;
dap->da_offset = offset;
dap->da_newinum = newinum;
/*
* Allocate a new dirrem and ACQUIRE_LOCK.
*/
dirrem = newdirrem(bp, dp, ip, isrmdir, &prevdirrem);
pagedep = dirrem->dm_pagedep;
/*
* The possible values for isrmdir:
* 0 - non-directory file rename
* 1 - directory rename within same directory
* inum - directory rename to new directory of given inode number
* When renaming to a new directory, we are both deleting and
* creating a new directory entry, so the link count on the new
* directory should not change. Thus we do not need the followup
* dirrem which is usually done in handle_workitem_remove. We set
* the DIRCHG flag to tell handle_workitem_remove to skip the
* followup dirrem.
*/
if (isrmdir > 1)
dirrem->dm_state |= DIRCHG;
/*
* If the COMPLETE flag is clear, then there were no active
* entries and we want to roll back to the previous inode until
* the new inode is committed to disk. If the COMPLETE flag is
* set, then we have deleted an entry that never made it to disk.
* If the entry we deleted resulted from a name change, then the old
* inode reference still resides on disk. Any rollback that we do
* needs to be to that old inode (returned to us in prevdirrem). If
* the entry we deleted resulted from a create, then there is
* no entry on the disk, so we want to roll back to zero rather
* than the uncommitted inode. In either of the COMPLETE cases we
* want to immediately free the unwritten and unreferenced inode.
*/
if ((dirrem->dm_state & COMPLETE) == 0) {
dap->da_previous = dirrem;
} else {
if (prevdirrem != NULL) {
dap->da_previous = prevdirrem;
} else {
dap->da_state &= ~DIRCHG;
dap->da_pagedep = pagedep;
}
dirrem->dm_dirinum = pagedep->pd_ino;
add_to_worklist(&dirrem->dm_list);
}
/*
* Link into its inodedep. Put it on the id_bufwait list if the inode
* is not yet written. If it is written, do the post-inode write
* processing to put it on the id_pendinghd list.
*/
if (inodedep_lookup(dp->i_fs, newinum, DEPALLOC, &inodedep) == 0 ||
(inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) {
dap->da_state |= COMPLETE;
LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist);
WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list);
} else {
LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)],
dap, da_pdlist);
WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list);
}
FREE_LOCK(&lk);
}
/*
* Called whenever the link count on an inode is changed.
* It creates an inode dependency so that the new reference(s)
* to the inode cannot be committed to disk until the updated
* inode has been written.
*/
/* the inode with the increased link count */
/* do background work or not */
void
softdep_change_linkcnt(struct inode *ip, int nodelay)
{
struct inodedep *inodedep;
int flags;
/*
* If requested, do not allow background work to happen.
*/
flags = DEPALLOC;
if (nodelay)
flags |= NODELAY;
ACQUIRE_LOCK(&lk);
(void) inodedep_lookup(ip->i_fs, ip->i_number, flags, &inodedep);
if (DIP(ip, nlink) < ip->i_effnlink) {
FREE_LOCK(&lk);
panic("softdep_change_linkcnt: bad delta");
}
inodedep->id_nlinkdelta = DIP(ip, nlink) - ip->i_effnlink;
FREE_LOCK(&lk);
}
/*
* This workitem decrements the inode's link count.
* If the link count reaches zero, the file is removed.
*/
STATIC void
handle_workitem_remove(struct dirrem *dirrem)
{
struct proc *p = CURPROC; /* XXX */
struct inodedep *inodedep;
struct vnode *vp;
struct inode *ip;
ufsino_t oldinum;
int error;
if ((error = VFS_VGET(dirrem->dm_mnt, dirrem->dm_oldinum, &vp)) != 0) {
softdep_error("handle_workitem_remove: vget", error);
return;
}
ip = VTOI(vp);
ACQUIRE_LOCK(&lk);
if ((inodedep_lookup(ip->i_fs, dirrem->dm_oldinum, 0, &inodedep))
== 0) {
FREE_LOCK(&lk);
panic("handle_workitem_remove: lost inodedep");
}
/*
* Normal file deletion.
*/
if ((dirrem->dm_state & RMDIR) == 0) {
DIP_ADD(ip, nlink, -1);
ip->i_flag |= IN_CHANGE;
if (DIP(ip, nlink) < ip->i_effnlink) {
FREE_LOCK(&lk);
panic("handle_workitem_remove: bad file delta");
}
inodedep->id_nlinkdelta = DIP(ip, nlink) - ip->i_effnlink;
FREE_LOCK(&lk);
vput(vp);
num_dirrem -= 1;
WORKITEM_FREE(dirrem, D_DIRREM);
return;
}
/*
* Directory deletion. Decrement reference count for both the
* just deleted parent directory entry and the reference for ".".
* Next truncate the directory to length zero. When the
* truncation completes, arrange to have the reference count on
* the parent decremented to account for the loss of "..".
*/
DIP_ADD(ip, nlink, -2);
ip->i_flag |= IN_CHANGE;
if (DIP(ip, nlink) < ip->i_effnlink)
panic("handle_workitem_remove: bad dir delta");
inodedep->id_nlinkdelta = DIP(ip, nlink) - ip->i_effnlink;
FREE_LOCK(&lk);
if ((error = UFS_TRUNCATE(ip, (off_t)0, 0, p->p_ucred)) != 0)
softdep_error("handle_workitem_remove: truncate", error);
/*
* Rename a directory to a new parent. Since, we are both deleting
* and creating a new directory entry, the link count on the new
* directory should not change. Thus we skip the followup dirrem.
*/
if (dirrem->dm_state & DIRCHG) {
vput(vp);
num_dirrem -= 1;
WORKITEM_FREE(dirrem, D_DIRREM);
return;
}
/*
* If the inodedep does not exist, then the zero'ed inode has
* been written to disk. If the allocated inode has never been
* written to disk, then the on-disk inode is zero'ed. In either
* case we can remove the file immediately.
*/
ACQUIRE_LOCK(&lk);
dirrem->dm_state = 0;
oldinum = dirrem->dm_oldinum;
dirrem->dm_oldinum = dirrem->dm_dirinum;
if (inodedep_lookup(ip->i_fs, oldinum, 0, &inodedep) == 0 ||
check_inode_unwritten(inodedep)) {
FREE_LOCK(&lk);
vput(vp);
handle_workitem_remove(dirrem);
return;
}
WORKLIST_INSERT(&inodedep->id_inowait, &dirrem->dm_list);
FREE_LOCK(&lk);
ip->i_flag |= IN_CHANGE;
UFS_UPDATE(VTOI(vp), 0);
vput(vp);
}
/*
* Inode de-allocation dependencies.
*
* When an inode's link count is reduced to zero, it can be de-allocated. We
* found it convenient to postpone de-allocation until after the inode is
* written to disk with its new link count (zero). At this point, all of the
* on-disk inode's block pointers are nullified and, with careful dependency
* list ordering, all dependencies related to the inode will be satisfied and
* the corresponding dependency structures de-allocated. So, if/when the
* inode is reused, there will be no mixing of old dependencies with new
* ones. This artificial dependency is set up by the block de-allocation
* procedure above (softdep_setup_freeblocks) and completed by the
* following procedure.
*/
STATIC void
handle_workitem_freefile(struct freefile *freefile)
{
struct fs *fs;
struct vnode vp;
struct inode tip;
#ifdef DEBUG
struct inodedep *idp;
#endif
int error;
fs = VFSTOUFS(freefile->fx_mnt)->um_fs;
#ifdef DEBUG
ACQUIRE_LOCK(&lk);
error = inodedep_lookup(fs, freefile->fx_oldinum, 0, &idp);
FREE_LOCK(&lk);
if (error)
panic("handle_workitem_freefile: inodedep survived");
#endif
tip.i_ump = VFSTOUFS(freefile->fx_mnt);
tip.i_dev = freefile->fx_devvp->v_rdev;
tip.i_fs = fs;
tip.i_vnode = &vp;
vp.v_data = &tip;
if ((error = ffs_freefile(&tip, freefile->fx_oldinum,
freefile->fx_mode)) != 0) {
softdep_error("handle_workitem_freefile", error);
}
WORKITEM_FREE(freefile, D_FREEFILE);
}
/*
* Disk writes.
*
* The dependency structures constructed above are most actively used when file
* system blocks are written to disk. No constraints are placed on when a
* block can be written, but unsatisfied update dependencies are made safe by
* modifying (or replacing) the source memory for the duration of the disk
* write. When the disk write completes, the memory block is again brought
* up-to-date.
*
* In-core inode structure reclamation.
*
* Because there are a finite number of "in-core" inode structures, they are
* reused regularly. By transferring all inode-related dependencies to the
* in-memory inode block and indexing them separately (via "inodedep"s), we
* can allow "in-core" inode structures to be reused at any time and avoid
* any increase in contention.
*
* Called just before entering the device driver to initiate a new disk I/O.
* The buffer must be locked, thus, no I/O completion operations can occur
* while we are manipulating its associated dependencies.
*/
/* structure describing disk write to occur */
void
softdep_disk_io_initiation(struct buf *bp)
{
struct worklist *wk, *nextwk;
struct indirdep *indirdep;
struct inodedep *inodedep;
struct buf *sbp;
/*
* We only care about write operations. There should never
* be dependencies for reads.
*/
if (bp->b_flags & B_READ)
panic("softdep_disk_io_initiation: read");
ACQUIRE_LOCK(&lk);
/*
* Do any necessary pre-I/O processing.
*/
for (wk = LIST_FIRST(&bp->b_dep); wk; wk = nextwk) {
nextwk = LIST_NEXT(wk, wk_list);
switch (wk->wk_type) {
case D_PAGEDEP:
initiate_write_filepage(WK_PAGEDEP(wk), bp);
continue;
case D_INODEDEP:
inodedep = WK_INODEDEP(wk);
if (inodedep->id_fs->fs_magic == FS_UFS1_MAGIC)
initiate_write_inodeblock_ufs1(inodedep, bp);
#ifdef FFS2
else
initiate_write_inodeblock_ufs2(inodedep, bp);
#endif
continue;
case D_INDIRDEP:
indirdep = WK_INDIRDEP(wk);
if (indirdep->ir_state & GOINGAWAY)
panic("disk_io_initiation: indirdep gone");
/*
* If there are no remaining dependencies, this
* will be writing the real pointers, so the
* dependency can be freed.
*/
if (LIST_FIRST(&indirdep->ir_deplisthd) == NULL) {
sbp = indirdep->ir_savebp;
sbp->b_flags |= B_INVAL | B_NOCACHE;
/* inline expand WORKLIST_REMOVE(wk); */
wk->wk_state &= ~ONWORKLIST;
LIST_REMOVE(wk, wk_list);
WORKITEM_FREE(indirdep, D_INDIRDEP);
FREE_LOCK(&lk);
brelse(sbp);
ACQUIRE_LOCK(&lk);
continue;
}
/*
* Replace up-to-date version with safe version.
*/
FREE_LOCK(&lk);
indirdep->ir_saveddata = malloc(bp->b_bcount,
M_INDIRDEP, M_WAITOK);
ACQUIRE_LOCK(&lk);
indirdep->ir_state &= ~ATTACHED;
indirdep->ir_state |= UNDONE;
memcpy(indirdep->ir_saveddata, bp->b_data, bp->b_bcount);
memcpy(bp->b_data, indirdep->ir_savebp->b_data,
bp->b_bcount);
continue;
case D_MKDIR:
case D_BMSAFEMAP:
case D_ALLOCDIRECT:
case D_ALLOCINDIR:
continue;
default:
FREE_LOCK(&lk);
panic("handle_disk_io_initiation: Unexpected type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
}
}
FREE_LOCK(&lk);
}
/*
* Called from within the procedure above to deal with unsatisfied
* allocation dependencies in a directory. The buffer must be locked,
* thus, no I/O completion operations can occur while we are
* manipulating its associated dependencies.
*/
STATIC void
initiate_write_filepage(struct pagedep *pagedep, struct buf *bp)
{
struct diradd *dap;
struct direct *ep;
int i;
if (pagedep->pd_state & IOSTARTED) {
/*
* This can only happen if there is a driver that does not
* understand chaining. Here biodone will reissue the call
* to strategy for the incomplete buffers.
*/
printf("initiate_write_filepage: already started\n");
return;
}
pagedep->pd_state |= IOSTARTED;
for (i = 0; i < DAHASHSZ; i++) {
LIST_FOREACH(dap, &pagedep->pd_diraddhd[i], da_pdlist) {
ep = (struct direct *)
((char *)bp->b_data + dap->da_offset);
if (ep->d_ino != dap->da_newinum) {
FREE_LOCK(&lk);
panic("%s: dir inum %u != new %u",
"initiate_write_filepage",
ep->d_ino, dap->da_newinum);
}
if (dap->da_state & DIRCHG)
ep->d_ino = dap->da_previous->dm_oldinum;
else
ep->d_ino = 0;
dap->da_state &= ~ATTACHED;
dap->da_state |= UNDONE;
}
}
}
/*
* Called from within the procedure above to deal with unsatisfied
* allocation dependencies in an inodeblock. The buffer must be
* locked, thus, no I/O completion operations can occur while we
* are manipulating its associated dependencies.
*/
/* The inode block */
STATIC void
initiate_write_inodeblock_ufs1(struct inodedep *inodedep, struct buf *bp)
{
struct allocdirect *adp, *lastadp;
struct ufs1_dinode *dp;
struct fs *fs;
#ifdef DIAGNOSTIC
daddr_t prevlbn = 0;
int32_t d1, d2;
#endif
int i, deplist;
if (inodedep->id_state & IOSTARTED) {
FREE_LOCK(&lk);
panic("initiate_write_inodeblock: already started");
}
inodedep->id_state |= IOSTARTED;
fs = inodedep->id_fs;
dp = (struct ufs1_dinode *)bp->b_data +
ino_to_fsbo(fs, inodedep->id_ino);
/*
* If the bitmap is not yet written, then the allocated
* inode cannot be written to disk.
*/
if ((inodedep->id_state & DEPCOMPLETE) == 0) {
if (inodedep->id_savedino1 != NULL) {
FREE_LOCK(&lk);
panic("initiate_write_inodeblock: already doing I/O");
}
FREE_LOCK(&lk);
inodedep->id_savedino1 = malloc(sizeof(struct ufs1_dinode),
M_INODEDEP, M_WAITOK);
inodedep->id_unsize = sizeof(struct ufs1_dinode);
ACQUIRE_LOCK(&lk);
*inodedep->id_savedino1 = *dp;
memset(dp, 0, sizeof(struct ufs1_dinode));
return;
}
/*
* If no dependencies, then there is nothing to roll back.
*/
inodedep->id_savedsize = dp->di_size;
if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL)
return;
/*
* Set the dependencies to busy.
*/
for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp;
adp = TAILQ_NEXT(adp, ad_next)) {
#ifdef DIAGNOSTIC
if (deplist != 0 && prevlbn >= adp->ad_lbn) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lbn order");
}
prevlbn = adp->ad_lbn;
if (adp->ad_lbn < NDADDR &&
(d1 = dp->di_db[adp->ad_lbn]) != (d2 = adp->ad_newblkno)) {
FREE_LOCK(&lk);
panic("%s: direct pointer #%lld mismatch %d != %d",
"softdep_write_inodeblock", (long long)adp->ad_lbn,
d1, d2);
}
if (adp->ad_lbn >= NDADDR &&
(d1 = dp->di_ib[adp->ad_lbn - NDADDR]) !=
(d2 = adp->ad_newblkno)) {
FREE_LOCK(&lk);
panic("%s: indirect pointer #%lld mismatch %d != %d",
"softdep_write_inodeblock", (long long)(adp->ad_lbn -
NDADDR), d1, d2);
}
deplist |= 1 << adp->ad_lbn;
if ((adp->ad_state & ATTACHED) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: Unknown state 0x%x",
adp->ad_state);
}
#endif /* DIAGNOSTIC */
adp->ad_state &= ~ATTACHED;
adp->ad_state |= UNDONE;
}
/*
* The on-disk inode cannot claim to be any larger than the last
* fragment that has been written. Otherwise, the on-disk inode
* might have fragments that were not the last block in the file
* which would corrupt the filesystem.
*/
for (lastadp = NULL, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp;
lastadp = adp, adp = TAILQ_NEXT(adp, ad_next)) {
if (adp->ad_lbn >= NDADDR)
break;
dp->di_db[adp->ad_lbn] = adp->ad_oldblkno;
/* keep going until hitting a rollback to a frag */
if (adp->ad_oldsize == 0 || adp->ad_oldsize == fs->fs_bsize)
continue;
dp->di_size = fs->fs_bsize * adp->ad_lbn + adp->ad_oldsize;
for (i = adp->ad_lbn + 1; i < NDADDR; i++) {
#ifdef DIAGNOSTIC
if (dp->di_db[i] != 0 && (deplist & (1 << i)) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lost dep1");
}
#endif /* DIAGNOSTIC */
dp->di_db[i] = 0;
}
for (i = 0; i < NIADDR; i++) {
#ifdef DIAGNOSTIC
if (dp->di_ib[i] != 0 &&
(deplist & ((1 << NDADDR) << i)) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lost dep2");
}
#endif /* DIAGNOSTIC */
dp->di_ib[i] = 0;
}
return;
}
/*
* If we have zero'ed out the last allocated block of the file,
* roll back the size to the last currently allocated block.
* We know that this last allocated block is a full-sized as
* we already checked for fragments in the loop above.
*/
if (lastadp != NULL &&
dp->di_size <= (lastadp->ad_lbn + 1) * fs->fs_bsize) {
for (i = lastadp->ad_lbn; i >= 0; i--)
if (dp->di_db[i] != 0)
break;
dp->di_size = (i + 1) * fs->fs_bsize;
}
/*
* The only dependencies are for indirect blocks.
*
* The file size for indirect block additions is not guaranteed.
* Such a guarantee would be non-trivial to achieve. The conventional
* synchronous write implementation also does not make this guarantee.
* Fsck should catch and fix discrepancies. Arguably, the file size
* can be over-estimated without destroying integrity when the file
* moves into the indirect blocks (i.e., is large). If we want to
* postpone fsck, we are stuck with this argument.
*/
for (; adp; adp = TAILQ_NEXT(adp, ad_next))
dp->di_ib[adp->ad_lbn - NDADDR] = 0;
}
#ifdef FFS2
/*
* Version of initiate_write_inodeblock that handles FFS2 dinodes.
*/
/* The inode block */
STATIC void
initiate_write_inodeblock_ufs2(struct inodedep *inodedep, struct buf *bp)
{
struct allocdirect *adp, *lastadp;
struct ufs2_dinode *dp;
struct fs *fs = inodedep->id_fs;
#ifdef DIAGNOSTIC
daddr_t prevlbn = -1, d1, d2;
#endif
int deplist, i;
if (inodedep->id_state & IOSTARTED)
panic("initiate_write_inodeblock_ufs2: already started");
inodedep->id_state |= IOSTARTED;
fs = inodedep->id_fs;
dp = (struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, inodedep->id_ino);
/*
* If the bitmap is not yet written, then the allocated
* inode cannot be written to disk.
*/
if ((inodedep->id_state & DEPCOMPLETE) == 0) {
if (inodedep->id_savedino2 != NULL)
panic("initiate_write_inodeblock_ufs2: I/O underway");
inodedep->id_savedino2 = malloc(sizeof(struct ufs2_dinode),
M_INODEDEP, M_WAITOK);
inodedep->id_unsize = sizeof(struct ufs2_dinode);
*inodedep->id_savedino2 = *dp;
memset(dp, 0, sizeof(struct ufs2_dinode));
return;
}
/*
* If no dependencies, then there is nothing to roll back.
*/
inodedep->id_savedsize = dp->di_size;
if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL)
return;
#ifdef notyet
inodedep->id_savedextsize = dp->di_extsize;
if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL &&
TAILQ_FIRST(&inodedep->id_extupdt) == NULL)
return;
/*
* Set the ext data dependencies to busy.
*/
for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_extupdt); adp;
adp = TAILQ_NEXT(adp, ad_next)) {
#ifdef DIAGNOSTIC
if (deplist != 0 && prevlbn >= adp->ad_lbn) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lbn order");
}
prevlbn = adp->ad_lbn;
if ((d1 = dp->di_extb[adp->ad_lbn]) !=
(d2 = adp->ad_newblkno)) {
FREE_LOCK(&lk);
panic("%s: direct pointer #%lld mismatch %lld != %lld",
"softdep_write_inodeblock", (long long)adp->ad_lbn,
d1, d2);
}
deplist |= 1 << adp->ad_lbn;
if ((adp->ad_state & ATTACHED) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: Unknown state 0x%x",
adp->ad_state);
}
#endif /* DIAGNOSTIC */
adp->ad_state &= ~ATTACHED;
adp->ad_state |= UNDONE;
}
/*
* The on-disk inode cannot claim to be any larger than the last
* fragment that has been written. Otherwise, the on-disk inode
* might have fragments that were not the last block in the ext
* data which would corrupt the filesystem.
*/
for (lastadp = NULL, adp = TAILQ_FIRST(&inodedep->id_extupdt); adp;
lastadp = adp, adp = TAILQ_NEXT(adp, ad_next)) {
dp->di_extb[adp->ad_lbn] = adp->ad_oldblkno;
/* keep going until hitting a rollback to a frag */
if (adp->ad_oldsize == 0 || adp->ad_oldsize == fs->fs_bsize)
continue;
dp->di_extsize = fs->fs_bsize * adp->ad_lbn + adp->ad_oldsize;
for (i = adp->ad_lbn + 1; i < NXADDR; i++) {
#ifdef DIAGNOSTIC
if (dp->di_extb[i] != 0 && (deplist & (1 << i)) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lost dep1");
}
#endif /* DIAGNOSTIC */
dp->di_extb[i] = 0;
}
lastadp = NULL;
break;
}
/*
* If we have zero'ed out the last allocated block of the ext
* data, roll back the size to the last currently allocated block.
* We know that this last allocated block is a full-sized as
* we already checked for fragments in the loop above.
*/
if (lastadp != NULL &&
dp->di_extsize <= (lastadp->ad_lbn + 1) * fs->fs_bsize) {
for (i = lastadp->ad_lbn; i >= 0; i--)
if (dp->di_extb[i] != 0)
break;
dp->di_extsize = (i + 1) * fs->fs_bsize;
}
#endif /* notyet */
/*
* Set the file data dependencies to busy.
*/
for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp;
adp = TAILQ_NEXT(adp, ad_next)) {
#ifdef DIAGNOSTIC
if (deplist != 0 && prevlbn >= adp->ad_lbn) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lbn order");
}
prevlbn = adp->ad_lbn;
if (adp->ad_lbn < NDADDR &&
(d1 = dp->di_db[adp->ad_lbn]) != (d2 = adp->ad_newblkno)) {
FREE_LOCK(&lk);
panic("%s: direct pointer #%lld mismatch %lld != %lld",
"softdep_write_inodeblock", (long long)adp->ad_lbn,
d1, d2);
}
if (adp->ad_lbn >= NDADDR &&
(d1 = dp->di_ib[adp->ad_lbn - NDADDR]) !=
(d2 = adp->ad_newblkno)) {
FREE_LOCK(&lk);
panic("%s: indirect pointer #%lld mismatch %lld != %lld",
"softdep_write_inodeblock", (long long)(adp->ad_lbn -
NDADDR), d1, d2);
}
deplist |= 1 << adp->ad_lbn;
if ((adp->ad_state & ATTACHED) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: Unknown state 0x%x",
adp->ad_state);
}
#endif /* DIAGNOSTIC */
adp->ad_state &= ~ATTACHED;
adp->ad_state |= UNDONE;
}
/*
* The on-disk inode cannot claim to be any larger than the last
* fragment that has been written. Otherwise, the on-disk inode
* might have fragments that were not the last block in the file
* which would corrupt the filesystem.
*/
for (lastadp = NULL, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp;
lastadp = adp, adp = TAILQ_NEXT(adp, ad_next)) {
if (adp->ad_lbn >= NDADDR)
break;
dp->di_db[adp->ad_lbn] = adp->ad_oldblkno;
/* keep going until hitting a rollback to a frag */
if (adp->ad_oldsize == 0 || adp->ad_oldsize == fs->fs_bsize)
continue;
dp->di_size = fs->fs_bsize * adp->ad_lbn + adp->ad_oldsize;
for (i = adp->ad_lbn + 1; i < NDADDR; i++) {
#ifdef DIAGNOSTIC
if (dp->di_db[i] != 0 && (deplist & (1 << i)) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lost dep2");
}
#endif /* DIAGNOSTIC */
dp->di_db[i] = 0;
}
for (i = 0; i < NIADDR; i++) {
#ifdef DIAGNOSTIC
if (dp->di_ib[i] != 0 &&
(deplist & ((1 << NDADDR) << i)) == 0) {
FREE_LOCK(&lk);
panic("softdep_write_inodeblock: lost dep3");
}
#endif /* DIAGNOSTIC */
dp->di_ib[i] = 0;
}
return;
}
/*
* If we have zero'ed out the last allocated block of the file,
* roll back the size to the last currently allocated block.
* We know that this last allocated block is a full-sized as
* we already checked for fragments in the loop above.
*/
if (lastadp != NULL &&
dp->di_size <= (lastadp->ad_lbn + 1) * fs->fs_bsize) {
for (i = lastadp->ad_lbn; i >= 0; i--)
if (dp->di_db[i] != 0)
break;
dp->di_size = (i + 1) * fs->fs_bsize;
}
/*
* The only dependencies are for indirect blocks.
*
* The file size for indirect block additions is not guaranteed.
* Such a guarantee would be non-trivial to achieve. The conventional
* synchronous write implementation also does not make this guarantee.
* Fsck should catch and fix discrepancies. Arguably, the file size
* can be over-estimated without destroying integrity when the file
* moves into the indirect blocks (i.e., is large). If we want to
* postpone fsck, we are stuck with this argument.
*/
for (; adp; adp = TAILQ_NEXT(adp, ad_next))
dp->di_ib[adp->ad_lbn - NDADDR] = 0;
}
#endif /* FFS2 */
/*
* This routine is called during the completion interrupt
* service routine for a disk write (from the procedure called
* by the device driver to inform the file system caches of
* a request completion). It should be called early in this
* procedure, before the block is made available to other
* processes or other routines are called.
*/
/* describes the completed disk write */
void
softdep_disk_write_complete(struct buf *bp)
{
struct worklist *wk;
struct workhead reattach;
struct newblk *newblk;
struct allocindir *aip;
struct allocdirect *adp;
struct indirdep *indirdep;
struct inodedep *inodedep;
struct bmsafemap *bmsafemap;
/*
* If an error occurred while doing the write, then the data
* has not hit the disk and the dependencies cannot be unrolled.
*/
if ((bp->b_flags & B_ERROR) && !(bp->b_flags & B_INVAL))
return;
#ifdef DEBUG
if (lk.lkt_held != -1)
panic("softdep_disk_write_complete: lock is held");
lk.lkt_held = -2;
#endif
LIST_INIT(&reattach);
while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) {
WORKLIST_REMOVE(wk);
switch (wk->wk_type) {
case D_PAGEDEP:
if (handle_written_filepage(WK_PAGEDEP(wk), bp))
WORKLIST_INSERT(&reattach, wk);
continue;
case D_INODEDEP:
if (handle_written_inodeblock(WK_INODEDEP(wk), bp))
WORKLIST_INSERT(&reattach, wk);
continue;
case D_BMSAFEMAP:
bmsafemap = WK_BMSAFEMAP(wk);
while ((newblk = LIST_FIRST(&bmsafemap->sm_newblkhd))) {
newblk->nb_state |= DEPCOMPLETE;
newblk->nb_bmsafemap = NULL;
LIST_REMOVE(newblk, nb_deps);
}
while ((adp =
LIST_FIRST(&bmsafemap->sm_allocdirecthd))) {
adp->ad_state |= DEPCOMPLETE;
adp->ad_buf = NULL;
LIST_REMOVE(adp, ad_deps);
handle_allocdirect_partdone(adp);
}
while ((aip =
LIST_FIRST(&bmsafemap->sm_allocindirhd))) {
aip->ai_state |= DEPCOMPLETE;
aip->ai_buf = NULL;
LIST_REMOVE(aip, ai_deps);
handle_allocindir_partdone(aip);
}
while ((inodedep =
LIST_FIRST(&bmsafemap->sm_inodedephd)) != NULL) {
inodedep->id_state |= DEPCOMPLETE;
LIST_REMOVE(inodedep, id_deps);
inodedep->id_buf = NULL;
}
WORKITEM_FREE(bmsafemap, D_BMSAFEMAP);
continue;
case D_MKDIR:
handle_written_mkdir(WK_MKDIR(wk), MKDIR_BODY);
continue;
case D_ALLOCDIRECT:
adp = WK_ALLOCDIRECT(wk);
adp->ad_state |= COMPLETE;
handle_allocdirect_partdone(adp);
continue;
case D_ALLOCINDIR:
aip = WK_ALLOCINDIR(wk);
aip->ai_state |= COMPLETE;
handle_allocindir_partdone(aip);
continue;
case D_INDIRDEP:
indirdep = WK_INDIRDEP(wk);
if (indirdep->ir_state & GOINGAWAY)
panic("disk_write_complete: indirdep gone");
memcpy(bp->b_data, indirdep->ir_saveddata, bp->b_bcount);
free(indirdep->ir_saveddata, M_INDIRDEP, bp->b_bcount);
indirdep->ir_saveddata = NULL;
indirdep->ir_state &= ~UNDONE;
indirdep->ir_state |= ATTACHED;
while ((aip = LIST_FIRST(&indirdep->ir_donehd))) {
handle_allocindir_partdone(aip);
if (aip == LIST_FIRST(&indirdep->ir_donehd))
panic("disk_write_complete: not gone");
}
WORKLIST_INSERT(&reattach, wk);
if ((bp->b_flags & B_DELWRI) == 0)
stat_indir_blk_ptrs++;
buf_dirty(bp);
continue;
default:
panic("handle_disk_write_complete: Unknown type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
}
}
/*
* Reattach any requests that must be redone.
*/
while ((wk = LIST_FIRST(&reattach)) != NULL) {
WORKLIST_REMOVE(wk);
WORKLIST_INSERT(&bp->b_dep, wk);
}
#ifdef DEBUG
if (lk.lkt_held != -2)
panic("softdep_disk_write_complete: lock lost");
lk.lkt_held = -1;
#endif
}
/*
* Called from within softdep_disk_write_complete above. Note that
* this routine is always called from interrupt level with further
* splbio interrupts blocked.
*/
/* the completed allocdirect */
STATIC void
handle_allocdirect_partdone(struct allocdirect *adp)
{
struct allocdirect *listadp;
struct inodedep *inodedep;
long bsize, delay;
splassert(IPL_BIO);
if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE)
return;
if (adp->ad_buf != NULL)
panic("handle_allocdirect_partdone: dangling dep");
/*
* The on-disk inode cannot claim to be any larger than the last
* fragment that has been written. Otherwise, the on-disk inode
* might have fragments that were not the last block in the file
* which would corrupt the filesystem. Thus, we cannot free any
* allocdirects after one whose ad_oldblkno claims a fragment as
* these blocks must be rolled back to zero before writing the inode.
* We check the currently active set of allocdirects in id_inoupdt.
*/
inodedep = adp->ad_inodedep;
bsize = inodedep->id_fs->fs_bsize;
TAILQ_FOREACH(listadp, &inodedep->id_inoupdt, ad_next) {
/* found our block */
if (listadp == adp)
break;
/* continue if ad_oldlbn is not a fragment */
if (listadp->ad_oldsize == 0 ||
listadp->ad_oldsize == bsize)
continue;
/* hit a fragment */
return;
}
/*
* If we have reached the end of the current list without
* finding the just finished dependency, then it must be
* on the future dependency list. Future dependencies cannot
* be freed until they are moved to the current list.
*/
if (listadp == NULL) {
#ifdef DEBUG
TAILQ_FOREACH(listadp, &inodedep->id_newinoupdt, ad_next)
/* found our block */
if (listadp == adp)
break;
if (listadp == NULL)
panic("handle_allocdirect_partdone: lost dep");
#endif /* DEBUG */
return;
}
/*
* If we have found the just finished dependency, then free
* it along with anything that follows it that is complete.
* If the inode still has a bitmap dependency, then it has
* never been written to disk, hence the on-disk inode cannot
* reference the old fragment so we can free it without delay.
*/
delay = (inodedep->id_state & DEPCOMPLETE);
for (; adp; adp = listadp) {
listadp = TAILQ_NEXT(adp, ad_next);
if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE)
return;
free_allocdirect(&inodedep->id_inoupdt, adp, delay);
}
}
/*
* Called from within softdep_disk_write_complete above. Note that
* this routine is always called from interrupt level with further
* splbio interrupts blocked.
*/
/* the completed allocindir */
STATIC void
handle_allocindir_partdone(struct allocindir *aip)
{
struct indirdep *indirdep;
splassert(IPL_BIO);
if ((aip->ai_state & ALLCOMPLETE) != ALLCOMPLETE)
return;
if (aip->ai_buf != NULL)
panic("handle_allocindir_partdone: dangling dependency");
indirdep = aip->ai_indirdep;
if (indirdep->ir_state & UNDONE) {
LIST_REMOVE(aip, ai_next);
LIST_INSERT_HEAD(&indirdep->ir_donehd, aip, ai_next);
return;
}
if (indirdep->ir_state & UFS1FMT)
((int32_t *)indirdep->ir_savebp->b_data)[aip->ai_offset] =
aip->ai_newblkno;
else
((int64_t *)indirdep->ir_savebp->b_data)[aip->ai_offset] =
aip->ai_newblkno;
LIST_REMOVE(aip, ai_next);
if (aip->ai_freefrag != NULL)
add_to_worklist(&aip->ai_freefrag->ff_list);
WORKITEM_FREE(aip, D_ALLOCINDIR);
}
/*
* Called from within softdep_disk_write_complete above to restore
* in-memory inode block contents to their most up-to-date state. Note
* that this routine is always called from interrupt level with further
* splbio interrupts blocked.
*/
/* buffer containing the inode block */
STATIC int
handle_written_inodeblock(struct inodedep *inodedep, struct buf *bp)
{
struct worklist *wk, *filefree;
struct allocdirect *adp, *nextadp;
struct ufs1_dinode *dp1 = NULL;
struct ufs2_dinode *dp2 = NULL;
int hadchanges, fstype;
splassert(IPL_BIO);
if ((inodedep->id_state & IOSTARTED) == 0)
panic("handle_written_inodeblock: not started");
inodedep->id_state &= ~IOSTARTED;
if (inodedep->id_fs->fs_magic == FS_UFS1_MAGIC) {
fstype = UM_UFS1;
dp1 = (struct ufs1_dinode *) bp->b_data +
ino_to_fsbo(inodedep->id_fs, inodedep->id_ino);
} else {
fstype = UM_UFS2;
dp2 = (struct ufs2_dinode *) bp->b_data +
ino_to_fsbo(inodedep->id_fs, inodedep->id_ino);
}
/*
* If we had to rollback the inode allocation because of
* bitmaps being incomplete, then simply restore it.
* Keep the block dirty so that it will not be reclaimed until
* all associated dependencies have been cleared and the
* corresponding updates written to disk.
*/
if (inodedep->id_savedino1 != NULL) {
if (fstype == UM_UFS1)
*dp1 = *inodedep->id_savedino1;
else
*dp2 = *inodedep->id_savedino2;
free(inodedep->id_savedino1, M_INODEDEP, inodedep->id_unsize);
inodedep->id_savedino1 = NULL;
if ((bp->b_flags & B_DELWRI) == 0)
stat_inode_bitmap++;
buf_dirty(bp);
return (1);
}
inodedep->id_state |= COMPLETE;
/*
* Roll forward anything that had to be rolled back before
* the inode could be updated.
*/
hadchanges = 0;
for (adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; adp = nextadp) {
nextadp = TAILQ_NEXT(adp, ad_next);
if (adp->ad_state & ATTACHED)
panic("handle_written_inodeblock: new entry");
if (fstype == UM_UFS1) {
if (adp->ad_lbn < NDADDR) {
if (dp1->di_db[adp->ad_lbn] != adp->ad_oldblkno)
panic("%s: %s #%lld mismatch %d != "
"%lld",
"handle_written_inodeblock",
"direct pointer",
(long long)adp->ad_lbn,
dp1->di_db[adp->ad_lbn],
(long long)adp->ad_oldblkno);
dp1->di_db[adp->ad_lbn] = adp->ad_newblkno;
} else {
if (dp1->di_ib[adp->ad_lbn - NDADDR] != 0)
panic("%s: %s #%lld allocated as %d",
"handle_written_inodeblock",
"indirect pointer",
(long long)(adp->ad_lbn - NDADDR),
dp1->di_ib[adp->ad_lbn - NDADDR]);
dp1->di_ib[adp->ad_lbn - NDADDR] =
adp->ad_newblkno;
}
} else {
if (adp->ad_lbn < NDADDR) {
if (dp2->di_db[adp->ad_lbn] != adp->ad_oldblkno)
panic("%s: %s #%lld mismatch %lld != "
"%lld", "handle_written_inodeblock",
"direct pointer",
(long long)adp->ad_lbn,
dp2->di_db[adp->ad_lbn],
(long long)adp->ad_oldblkno);
dp2->di_db[adp->ad_lbn] = adp->ad_newblkno;
} else {
if (dp2->di_ib[adp->ad_lbn - NDADDR] != 0)
panic("%s: %s #%lld allocated as %lld",
"handle_written_inodeblock",
"indirect pointer",
(long long)(adp->ad_lbn - NDADDR),
dp2->di_ib[adp->ad_lbn - NDADDR]);
dp2->di_ib[adp->ad_lbn - NDADDR] =
adp->ad_newblkno;
}
}
adp->ad_state &= ~UNDONE;
adp->ad_state |= ATTACHED;
hadchanges = 1;
}
if (hadchanges && (bp->b_flags & B_DELWRI) == 0)
stat_direct_blk_ptrs++;
/*
* Reset the file size to its most up-to-date value.
*/
if (inodedep->id_savedsize == -1)
panic("handle_written_inodeblock: bad size");
if (fstype == UM_UFS1) {
if (dp1->di_size != inodedep->id_savedsize) {
dp1->di_size = inodedep->id_savedsize;
hadchanges = 1;
}
} else {
if (dp2->di_size != inodedep->id_savedsize) {
dp2->di_size = inodedep->id_savedsize;
hadchanges = 1;
}
}
inodedep->id_savedsize = -1;
/*
* If there were any rollbacks in the inode block, then it must be
* marked dirty so that its will eventually get written back in
* its correct form.
*/
if (hadchanges)
buf_dirty(bp);
/*
* Process any allocdirects that completed during the update.
*/
if ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != NULL)
handle_allocdirect_partdone(adp);
/*
* Process deallocations that were held pending until the
* inode had been written to disk. Freeing of the inode
* is delayed until after all blocks have been freed to
* avoid creation of new <vfsid, inum, lbn> triples
* before the old ones have been deleted.
*/
filefree = NULL;
while ((wk = LIST_FIRST(&inodedep->id_bufwait)) != NULL) {
WORKLIST_REMOVE(wk);
switch (wk->wk_type) {
case D_FREEFILE:
/*
* We defer adding filefree to the worklist until
* all other additions have been made to ensure
* that it will be done after all the old blocks
* have been freed.
*/
if (filefree != NULL)
panic("handle_written_inodeblock: filefree");
filefree = wk;
continue;
case D_MKDIR:
handle_written_mkdir(WK_MKDIR(wk), MKDIR_PARENT);
continue;
case D_DIRADD:
diradd_inode_written(WK_DIRADD(wk), inodedep);
continue;
case D_FREEBLKS:
wk->wk_state |= COMPLETE;
if ((wk->wk_state & ALLCOMPLETE) != ALLCOMPLETE)
continue;
/* FALLTHROUGH */
case D_FREEFRAG:
case D_DIRREM:
add_to_worklist(wk);
continue;
case D_NEWDIRBLK:
free_newdirblk(WK_NEWDIRBLK(wk));
continue;
default:
panic("handle_written_inodeblock: Unknown type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
}
}
if (filefree != NULL) {
if (free_inodedep(inodedep) == 0)
panic("handle_written_inodeblock: live inodedep");
add_to_worklist(filefree);
return (0);
}
/*
* If no outstanding dependencies, free it.
*/
if (free_inodedep(inodedep) ||
TAILQ_FIRST(&inodedep->id_inoupdt) == NULL)
return (0);
return (hadchanges);
}
/*
* Process a diradd entry after its dependent inode has been written.
* This routine must be called with splbio interrupts blocked.
*/
STATIC void
diradd_inode_written(struct diradd *dap, struct inodedep *inodedep)
{
struct pagedep *pagedep;
splassert(IPL_BIO);
dap->da_state |= COMPLETE;
if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) {
if (dap->da_state & DIRCHG)
pagedep = dap->da_previous->dm_pagedep;
else
pagedep = dap->da_pagedep;
LIST_REMOVE(dap, da_pdlist);
LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist);
}
WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list);
}
/*
* Handle the completion of a mkdir dependency.
*/
STATIC void
handle_written_mkdir(struct mkdir *mkdir, int type)
{
struct diradd *dap;
struct pagedep *pagedep;
splassert(IPL_BIO);
if (mkdir->md_state != type)
panic("handle_written_mkdir: bad type");
dap = mkdir->md_diradd;
dap->da_state &= ~type;
if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) == 0)
dap->da_state |= DEPCOMPLETE;
if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) {
if (dap->da_state & DIRCHG)
pagedep = dap->da_previous->dm_pagedep;
else
pagedep = dap->da_pagedep;
LIST_REMOVE(dap, da_pdlist);
LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist);
}
LIST_REMOVE(mkdir, md_mkdirs);
WORKITEM_FREE(mkdir, D_MKDIR);
}
/*
* Called from within softdep_disk_write_complete above.
* A write operation was just completed. Removed inodes can
* now be freed and associated block pointers may be committed.
* Note that this routine is always called from interrupt level
* with further splbio interrupts blocked.
*/
/* buffer containing the written page */
STATIC int
handle_written_filepage(struct pagedep *pagedep, struct buf *bp)
{
struct dirrem *dirrem;
struct diradd *dap, *nextdap;
struct direct *ep;
int i, chgs;
splassert(IPL_BIO);
if ((pagedep->pd_state & IOSTARTED) == 0)
panic("handle_written_filepage: not started");
pagedep->pd_state &= ~IOSTARTED;
/*
* Process any directory removals that have been committed.
*/
while ((dirrem = LIST_FIRST(&pagedep->pd_dirremhd)) != NULL) {
LIST_REMOVE(dirrem, dm_next);
dirrem->dm_dirinum = pagedep->pd_ino;
add_to_worklist(&dirrem->dm_list);
}
/*
* Free any directory additions that have been committed.
* If it is a newly allocated block, we have to wait until
* the on-disk directory inode claims the new block.
*/
if ((pagedep->pd_state & NEWBLOCK) == 0)
while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != NULL)
free_diradd(dap);
/*
* Uncommitted directory entries must be restored.
*/
for (chgs = 0, i = 0; i < DAHASHSZ; i++) {
for (dap = LIST_FIRST(&pagedep->pd_diraddhd[i]); dap;
dap = nextdap) {
nextdap = LIST_NEXT(dap, da_pdlist);
if (dap->da_state & ATTACHED)
panic("handle_written_filepage: attached");
ep = (struct direct *)
((char *)bp->b_data + dap->da_offset);
ep->d_ino = dap->da_newinum;
dap->da_state &= ~UNDONE;
dap->da_state |= ATTACHED;
chgs = 1;
/*
* If the inode referenced by the directory has
* been written out, then the dependency can be
* moved to the pending list.
*/
if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) {
LIST_REMOVE(dap, da_pdlist);
LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap,
da_pdlist);
}
}
}
/*
* If there were any rollbacks in the directory, then it must be
* marked dirty so that its will eventually get written back in
* its correct form.
*/
if (chgs) {
if ((bp->b_flags & B_DELWRI) == 0)
stat_dir_entry++;
buf_dirty(bp);
return (1);
}
/*
* If we are not waiting for a new directory block to be
* claimed by its inode, then the pagedep will be freed.
* Otherwise it will remain to track any new entries on
* the page in case they are fsync'ed.
*/
if ((pagedep->pd_state & NEWBLOCK) == 0) {
LIST_REMOVE(pagedep, pd_hash);
WORKITEM_FREE(pagedep, D_PAGEDEP);
}
return (0);
}
/*
* Writing back in-core inode structures.
*
* The file system only accesses an inode's contents when it occupies an
* "in-core" inode structure. These "in-core" structures are separate from
* the page frames used to cache inode blocks. Only the latter are
* transferred to/from the disk. So, when the updated contents of the
* "in-core" inode structure are copied to the corresponding in-memory inode
* block, the dependencies are also transferred. The following procedure is
* called when copying a dirty "in-core" inode to a cached inode block.
*/
/*
* Called when an inode is loaded from disk. If the effective link count
* differed from the actual link count when it was last flushed, then we
* need to ensure that the correct effective link count is put back.
*/
/* the "in_core" copy of the inode */
void
softdep_load_inodeblock(struct inode *ip)
{
struct inodedep *inodedep;
/*
* Check for alternate nlink count.
*/
ip->i_effnlink = DIP(ip, nlink);
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep) == 0) {
FREE_LOCK(&lk);
return;
}
ip->i_effnlink -= inodedep->id_nlinkdelta;
FREE_LOCK(&lk);
}
/*
* This routine is called just before the "in-core" inode
* information is to be copied to the in-memory inode block.
* Recall that an inode block contains several inodes. If
* the force flag is set, then the dependencies will be
* cleared so that the update can always be made. Note that
* the buffer is locked when this routine is called, so we
* will never be in the middle of writing the inode block
* to disk.
*/
/* the "in_core" copy of the inode */
/* the buffer containing the inode block */
/* nonzero => update must be allowed */
void
softdep_update_inodeblock(struct inode *ip, struct buf *bp, int waitfor)
{
struct inodedep *inodedep;
struct worklist *wk;
int error, gotit;
/*
* If the effective link count is not equal to the actual link
* count, then we must track the difference in an inodedep while
* the inode is (potentially) tossed out of the cache. Otherwise,
* if there is no existing inodedep, then there are no dependencies
* to track.
*/
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep) == 0) {
FREE_LOCK(&lk);
if (ip->i_effnlink != DIP(ip, nlink))
panic("softdep_update_inodeblock: bad link count");
return;
}
if (inodedep->id_nlinkdelta != DIP(ip, nlink) - ip->i_effnlink) {
FREE_LOCK(&lk);
panic("softdep_update_inodeblock: bad delta");
}
/*
* Changes have been initiated. Anything depending on these
* changes cannot occur until this inode has been written.
*/
inodedep->id_state &= ~COMPLETE;
if ((inodedep->id_state & ONWORKLIST) == 0)
WORKLIST_INSERT(&bp->b_dep, &inodedep->id_list);
/*
* Any new dependencies associated with the incore inode must
* now be moved to the list associated with the buffer holding
* the in-memory copy of the inode. Once merged process any
* allocdirects that are completed by the merger.
*/
merge_inode_lists(inodedep);
if (TAILQ_FIRST(&inodedep->id_inoupdt) != NULL)
handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_inoupdt));
/*
* Now that the inode has been pushed into the buffer, the
* operations dependent on the inode being written to disk
* can be moved to the id_bufwait so that they will be
* processed when the buffer I/O completes.
*/
while ((wk = LIST_FIRST(&inodedep->id_inowait)) != NULL) {
WORKLIST_REMOVE(wk);
WORKLIST_INSERT(&inodedep->id_bufwait, wk);
}
/*
* Newly allocated inodes cannot be written until the bitmap
* that allocates them have been written (indicated by
* DEPCOMPLETE being set in id_state). If we are doing a
* forced sync (e.g., an fsync on a file), we force the bitmap
* to be written so that the update can be done.
*/
do {
if ((inodedep->id_state & DEPCOMPLETE) != 0 || waitfor == 0) {
FREE_LOCK(&lk);
return;
}
bp = inodedep->id_buf;
gotit = getdirtybuf(bp, MNT_WAIT);
} while (gotit == -1);
FREE_LOCK(&lk);
if (gotit && (error = bwrite(bp)) != 0)
softdep_error("softdep_update_inodeblock: bwrite", error);
if ((inodedep->id_state & DEPCOMPLETE) == 0)
panic("softdep_update_inodeblock: update failed");
}
/*
* Merge the new inode dependency list (id_newinoupdt) into the old
* inode dependency list (id_inoupdt). This routine must be called
* with splbio interrupts blocked.
*/
STATIC void
merge_inode_lists(struct inodedep *inodedep)
{
struct allocdirect *listadp, *newadp;
splassert(IPL_BIO);
newadp = TAILQ_FIRST(&inodedep->id_newinoupdt);
for (listadp = TAILQ_FIRST(&inodedep->id_inoupdt); listadp && newadp;) {
if (listadp->ad_lbn < newadp->ad_lbn) {
listadp = TAILQ_NEXT(listadp, ad_next);
continue;
}
TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next);
TAILQ_INSERT_BEFORE(listadp, newadp, ad_next);
if (listadp->ad_lbn == newadp->ad_lbn) {
allocdirect_merge(&inodedep->id_inoupdt, newadp,
listadp);
listadp = newadp;
}
newadp = TAILQ_FIRST(&inodedep->id_newinoupdt);
}
TAILQ_CONCAT(&inodedep->id_inoupdt, &inodedep->id_newinoupdt, ad_next);
}
/*
* If we are doing an fsync, then we must ensure that any directory
* entries for the inode have been written after the inode gets to disk.
*/
/* the "in_core" copy of the inode */
int
softdep_fsync(struct vnode *vp)
{
struct inodedep *inodedep;
struct pagedep *pagedep;
struct worklist *wk;
struct diradd *dap;
struct mount *mnt;
struct vnode *pvp;
struct inode *ip;
struct inode *pip;
struct buf *bp;
struct fs *fs;
struct proc *p = CURPROC; /* XXX */
int error, flushparent;
ufsino_t parentino;
daddr_t lbn;
ip = VTOI(vp);
fs = ip->i_fs;
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) == 0) {
FREE_LOCK(&lk);
return (0);
}
if (LIST_FIRST(&inodedep->id_inowait) != NULL ||
LIST_FIRST(&inodedep->id_bufwait) != NULL ||
TAILQ_FIRST(&inodedep->id_inoupdt) != NULL ||
TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL) {
FREE_LOCK(&lk);
panic("softdep_fsync: pending ops");
}
for (error = 0, flushparent = 0; ; ) {
if ((wk = LIST_FIRST(&inodedep->id_pendinghd)) == NULL)
break;
if (wk->wk_type != D_DIRADD) {
FREE_LOCK(&lk);
panic("softdep_fsync: Unexpected type %s",
TYPENAME(wk->wk_type));
}
dap = WK_DIRADD(wk);
/*
* Flush our parent if this directory entry has a MKDIR_PARENT
* dependency or is contained in a newly allocated block.
*/
if (dap->da_state & DIRCHG)
pagedep = dap->da_previous->dm_pagedep;
else
pagedep = dap->da_pagedep;
mnt = pagedep->pd_mnt;
parentino = pagedep->pd_ino;
lbn = pagedep->pd_lbn;
if ((dap->da_state & (MKDIR_BODY | COMPLETE)) != COMPLETE) {
FREE_LOCK(&lk);
panic("softdep_fsync: dirty");
}
if ((dap->da_state & MKDIR_PARENT) ||
(pagedep->pd_state & NEWBLOCK))
flushparent = 1;
else
flushparent = 0;
/*
* If we are being fsync'ed as part of vgone'ing this vnode,
* then we will not be able to release and recover the
* vnode below, so we just have to give up on writing its
* directory entry out. It will eventually be written, just
* not now, but then the user was not asking to have it
* written, so we are not breaking any promises.
*/
mtx_enter(&vnode_mtx);
if (vp->v_lflag & VXLOCK) {
mtx_leave(&vnode_mtx);
break;
}
mtx_leave(&vnode_mtx);
/*
* We prevent deadlock by always fetching inodes from the
* root, moving down the directory tree. Thus, when fetching
* our parent directory, we must unlock ourselves before
* requesting the lock on our parent. See the comment in
* ufs_lookup for details on possible races.
*/
FREE_LOCK(&lk);
VOP_UNLOCK(vp);
error = VFS_VGET(mnt, parentino, &pvp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error != 0)
return (error);
/*
* All MKDIR_PARENT dependencies and all the NEWBLOCK pagedeps
* that are contained in direct blocks will be resolved by
* doing a UFS_UPDATE. Pagedeps contained in indirect blocks
* may require a complete sync'ing of the directory. So, we
* try the cheap and fast UFS_UPDATE first, and if that fails,
* then we do the slower VOP_FSYNC of the directory.
*/
pip = VTOI(pvp);
if (flushparent) {
error = UFS_UPDATE(pip, 1);
if (error) {
vput(pvp);
return (error);
}
if (pagedep->pd_state & NEWBLOCK) {
error = VOP_FSYNC(pvp, p->p_ucred, MNT_WAIT, p);
if (error) {
vput(pvp);
return (error);
}
}
}
/*
* Flush directory page containing the inode's name.
*/
error = bread(pvp, lbn, fs->fs_bsize, &bp);
if (error == 0) {
bp->b_bcount = blksize(fs, pip, lbn);
error = bwrite(bp);
} else
brelse(bp);
vput(pvp);
if (error != 0)
return (error);
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) == 0)
break;
}
FREE_LOCK(&lk);
return (0);
}
/*
* Flush all the dirty bitmaps associated with the block device
* before flushing the rest of the dirty blocks so as to reduce
* the number of dependencies that will have to be rolled back.
*/
void
softdep_fsync_mountdev(struct vnode *vp, int waitfor)
{
struct buf *bp, *nbp;
struct worklist *wk;
if (!vn_isdisk(vp, NULL))
panic("softdep_fsync_mountdev: vnode not a disk");
ACQUIRE_LOCK(&lk);
LIST_FOREACH_SAFE(bp, &vp->v_dirtyblkhd, b_vnbufs, nbp) {
/*
* If it is already scheduled, skip to the next buffer.
*/
splassert(IPL_BIO);
if (bp->b_flags & B_BUSY)
continue;
if ((bp->b_flags & B_DELWRI) == 0) {
FREE_LOCK(&lk);
panic("softdep_fsync_mountdev: not dirty");
}
/*
* We are only interested in bitmaps with outstanding
* dependencies.
*/
if ((wk = LIST_FIRST(&bp->b_dep)) == NULL ||
wk->wk_type != D_BMSAFEMAP) {
continue;
}
bremfree(bp);
buf_acquire(bp);
FREE_LOCK(&lk);
(void) bawrite(bp);
ACQUIRE_LOCK(&lk);
/*
* Since we may have slept during the I/O, we need
* to start from a known point.
*/
nbp = LIST_FIRST(&vp->v_dirtyblkhd);
}
if (waitfor == MNT_WAIT)
drain_output(vp, 1);
FREE_LOCK(&lk);
}
/*
* This routine is called when we are trying to synchronously flush a
* file. This routine must eliminate any filesystem metadata dependencies
* so that the syncing routine can succeed by pushing the dirty blocks
* associated with the file. If any I/O errors occur, they are returned.
*/
int
softdep_sync_metadata(struct vop_fsync_args *ap)
{
struct vnode *vp = ap->a_vp;
struct pagedep *pagedep;
struct allocdirect *adp;
struct allocindir *aip;
struct buf *bp, *nbp;
struct worklist *wk;
int i, gotit, error, waitfor;
/*
* Check whether this vnode is involved in a filesystem
* that is doing soft dependency processing.
*/
if (!vn_isdisk(vp, NULL)) {
if (!DOINGSOFTDEP(vp))
return (0);
} else
if (vp->v_specmountpoint == NULL ||
(vp->v_specmountpoint->mnt_flag & MNT_SOFTDEP) == 0)
return (0);
/*
* Ensure that any direct block dependencies have been cleared.
*/
ACQUIRE_LOCK(&lk);
if ((error = flush_inodedep_deps(VTOI(vp)->i_fs, VTOI(vp)->i_number))) {
FREE_LOCK(&lk);
return (error);
}
/*
* For most files, the only metadata dependencies are the
* cylinder group maps that allocate their inode or blocks.
* The block allocation dependencies can be found by traversing
* the dependency lists for any buffers that remain on their
* dirty buffer list. The inode allocation dependency will
* be resolved when the inode is updated with MNT_WAIT.
* This work is done in two passes. The first pass grabs most
* of the buffers and begins asynchronously writing them. The
* only way to wait for these asynchronous writes is to sleep
* on the filesystem vnode which may stay busy for a long time
* if the filesystem is active. So, instead, we make a second
* pass over the dependencies blocking on each write. In the
* usual case we will be blocking against a write that we
* initiated, so when it is done the dependency will have been
* resolved. Thus the second pass is expected to end quickly.
*/
waitfor = MNT_NOWAIT;
top:
/*
* We must wait for any I/O in progress to finish so that
* all potential buffers on the dirty list will be visible.
*/
drain_output(vp, 1);
bp = LIST_FIRST(&vp->v_dirtyblkhd);
gotit = getdirtybuf(bp, MNT_WAIT);
if (gotit == 0) {
FREE_LOCK(&lk);
return (0);
} else if (gotit == -1)
goto top;
loop:
/*
* As we hold the buffer locked, none of its dependencies
* will disappear.
*/
LIST_FOREACH(wk, &bp->b_dep, wk_list) {
switch (wk->wk_type) {
case D_ALLOCDIRECT:
adp = WK_ALLOCDIRECT(wk);
if (adp->ad_state & DEPCOMPLETE)
break;
nbp = adp->ad_buf;
gotit = getdirtybuf(nbp, waitfor);
if (gotit == 0)
break;
else if (gotit == -1)
goto loop;
FREE_LOCK(&lk);
if (waitfor == MNT_NOWAIT) {
bawrite(nbp);
} else if ((error = VOP_BWRITE(nbp)) != 0) {
bawrite(bp);
return (error);
}
ACQUIRE_LOCK(&lk);
break;
case D_ALLOCINDIR:
aip = WK_ALLOCINDIR(wk);
if (aip->ai_state & DEPCOMPLETE)
break;
nbp = aip->ai_buf;
gotit = getdirtybuf(nbp, waitfor);
if (gotit == 0)
break;
else if (gotit == -1)
goto loop;
FREE_LOCK(&lk);
if (waitfor == MNT_NOWAIT) {
bawrite(nbp);
} else if ((error = VOP_BWRITE(nbp)) != 0) {
bawrite(bp);
return (error);
}
ACQUIRE_LOCK(&lk);
break;
case D_INDIRDEP:
restart:
LIST_FOREACH(aip, &WK_INDIRDEP(wk)->ir_deplisthd, ai_next) {
if (aip->ai_state & DEPCOMPLETE)
continue;
nbp = aip->ai_buf;
if (getdirtybuf(nbp, MNT_WAIT) <= 0)
goto restart;
FREE_LOCK(&lk);
if ((error = VOP_BWRITE(nbp)) != 0) {
bawrite(bp);
return (error);
}
ACQUIRE_LOCK(&lk);
goto restart;
}
break;
case D_INODEDEP:
if ((error = flush_inodedep_deps(WK_INODEDEP(wk)->id_fs,
WK_INODEDEP(wk)->id_ino)) != 0) {
FREE_LOCK(&lk);
bawrite(bp);
return (error);
}
break;
case D_PAGEDEP:
/*
* We are trying to sync a directory that may
* have dependencies on both its own metadata
* and/or dependencies on the inodes of any
* recently allocated files. We walk its diradd
* lists pushing out the associated inode.
*/
pagedep = WK_PAGEDEP(wk);
for (i = 0; i < DAHASHSZ; i++) {
if (LIST_FIRST(&pagedep->pd_diraddhd[i]) ==
NULL)
continue;
if ((error =
flush_pagedep_deps(vp, pagedep->pd_mnt,
&pagedep->pd_diraddhd[i]))) {
FREE_LOCK(&lk);
bawrite(bp);
return (error);
}
}
break;
case D_MKDIR:
/*
* This case should never happen if the vnode has
* been properly sync'ed. However, if this function
* is used at a place where the vnode has not yet
* been sync'ed, this dependency can show up. So,
* rather than panic, just flush it.
*/
nbp = WK_MKDIR(wk)->md_buf;
KASSERT(bp != nbp);
gotit = getdirtybuf(nbp, waitfor);
if (gotit == 0)
break;
else if (gotit == -1)
goto loop;
FREE_LOCK(&lk);
if (waitfor == MNT_NOWAIT) {
bawrite(nbp);
} else if ((error = VOP_BWRITE(nbp)) != 0) {
bawrite(bp);
return (error);
}
ACQUIRE_LOCK(&lk);
break;
case D_BMSAFEMAP:
/*
* This case should never happen if the vnode has
* been properly sync'ed. However, if this function
* is used at a place where the vnode has not yet
* been sync'ed, this dependency can show up. So,
* rather than panic, just flush it.
*/
nbp = WK_BMSAFEMAP(wk)->sm_buf;
if (bp == nbp)
break;
gotit = getdirtybuf(nbp, waitfor);
if (gotit == 0)
break;
else if (gotit == -1)
goto loop;
FREE_LOCK(&lk);
if (waitfor == MNT_NOWAIT) {
bawrite(nbp);
} else if ((error = VOP_BWRITE(nbp)) != 0) {
bawrite(bp);
return (error);
}
ACQUIRE_LOCK(&lk);
break;
default:
FREE_LOCK(&lk);
panic("softdep_sync_metadata: Unknown type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
}
}
do {
nbp = LIST_NEXT(bp, b_vnbufs);
gotit = getdirtybuf(nbp, MNT_WAIT);
} while (gotit == -1);
FREE_LOCK(&lk);
bawrite(bp);
ACQUIRE_LOCK(&lk);
if (nbp != NULL) {
bp = nbp;
goto loop;
}
/*
* The brief unlock is to allow any pent up dependency
* processing to be done. Then proceed with the second pass.
*/
if (waitfor == MNT_NOWAIT) {
waitfor = MNT_WAIT;
FREE_LOCK(&lk);
ACQUIRE_LOCK(&lk);
goto top;
}
/*
* If we have managed to get rid of all the dirty buffers,
* then we are done. For certain directories and block
* devices, we may need to do further work.
*
* We must wait for any I/O in progress to finish so that
* all potential buffers on the dirty list will be visible.
*/
drain_output(vp, 1);
if (LIST_EMPTY(&vp->v_dirtyblkhd)) {
FREE_LOCK(&lk);
return (0);
}
FREE_LOCK(&lk);
/*
* If we are trying to sync a block device, some of its buffers may
* contain metadata that cannot be written until the contents of some
* partially written files have been written to disk. The only easy
* way to accomplish this is to sync the entire filesystem (luckily
* this happens rarely).
*/
if (vn_isdisk(vp, NULL) &&
vp->v_specmountpoint && !VOP_ISLOCKED(vp) &&
(error = VFS_SYNC(vp->v_specmountpoint, MNT_WAIT, 0, ap->a_cred,
ap->a_p)) != 0)
return (error);
return (0);
}
/*
* Flush the dependencies associated with an inodedep.
* Called with splbio blocked.
*/
STATIC int
flush_inodedep_deps(struct fs *fs, ufsino_t ino)
{
struct inodedep *inodedep;
struct allocdirect *adp;
int gotit, error, waitfor;
struct buf *bp;
splassert(IPL_BIO);
/*
* This work is done in two passes. The first pass grabs most
* of the buffers and begins asynchronously writing them. The
* only way to wait for these asynchronous writes is to sleep
* on the filesystem vnode which may stay busy for a long time
* if the filesystem is active. So, instead, we make a second
* pass over the dependencies blocking on each write. In the
* usual case we will be blocking against a write that we
* initiated, so when it is done the dependency will have been
* resolved. Thus the second pass is expected to end quickly.
* We give a brief window at the top of the loop to allow
* any pending I/O to complete.
*/
for (waitfor = MNT_NOWAIT; ; ) {
retry_ino:
FREE_LOCK(&lk);
ACQUIRE_LOCK(&lk);
if (inodedep_lookup(fs, ino, 0, &inodedep) == 0)
return (0);
TAILQ_FOREACH(adp, &inodedep->id_inoupdt, ad_next) {
if (adp->ad_state & DEPCOMPLETE)
continue;
bp = adp->ad_buf;
gotit = getdirtybuf(bp, waitfor);
if (gotit == 0) {
if (waitfor == MNT_NOWAIT)
continue;
break;
} else if (gotit == -1)
goto retry_ino;
FREE_LOCK(&lk);
if (waitfor == MNT_NOWAIT) {
bawrite(bp);
} else if ((error = VOP_BWRITE(bp)) != 0) {
ACQUIRE_LOCK(&lk);
return (error);
}
ACQUIRE_LOCK(&lk);
break;
}
if (adp != NULL)
continue;
retry_newino:
TAILQ_FOREACH(adp, &inodedep->id_newinoupdt, ad_next) {
if (adp->ad_state & DEPCOMPLETE)
continue;
bp = adp->ad_buf;
gotit = getdirtybuf(bp, waitfor);
if (gotit == 0) {
if (waitfor == MNT_NOWAIT)
continue;
break;
} else if (gotit == -1)
goto retry_newino;
FREE_LOCK(&lk);
if (waitfor == MNT_NOWAIT) {
bawrite(bp);
} else if ((error = VOP_BWRITE(bp)) != 0) {
ACQUIRE_LOCK(&lk);
return (error);
}
ACQUIRE_LOCK(&lk);
break;
}
if (adp != NULL)
continue;
/*
* If pass2, we are done, otherwise do pass 2.
*/
if (waitfor == MNT_WAIT)
break;
waitfor = MNT_WAIT;
}
/*
* Try freeing inodedep in case all dependencies have been removed.
*/
if (inodedep_lookup(fs, ino, 0, &inodedep) != 0)
(void) free_inodedep(inodedep);
return (0);
}
/*
* Eliminate a pagedep dependency by flushing out all its diradd dependencies.
* Called with splbio blocked.
*/
STATIC int
flush_pagedep_deps(struct vnode *pvp, struct mount *mp,
struct diraddhd *diraddhdp)
{
struct proc *p = CURPROC; /* XXX */
struct worklist *wk;
struct inodedep *inodedep;
struct ufsmount *ump;
struct diradd *dap;
struct vnode *vp;
int gotit, error = 0;
struct buf *bp;
ufsino_t inum;
splassert(IPL_BIO);
ump = VFSTOUFS(mp);
while ((dap = LIST_FIRST(diraddhdp)) != NULL) {
/*
* Flush ourselves if this directory entry
* has a MKDIR_PARENT dependency.
*/
if (dap->da_state & MKDIR_PARENT) {
FREE_LOCK(&lk);
if ((error = UFS_UPDATE(VTOI(pvp), 1)))
break;
ACQUIRE_LOCK(&lk);
/*
* If that cleared dependencies, go on to next.
*/
if (dap != LIST_FIRST(diraddhdp))
continue;
if (dap->da_state & MKDIR_PARENT) {
FREE_LOCK(&lk);
panic("flush_pagedep_deps: MKDIR_PARENT");
}
}
/*
* A newly allocated directory must have its "." and
* ".." entries written out before its name can be
* committed in its parent. We do not want or need
* the full semantics of a synchronous VOP_FSYNC as
* that may end up here again, once for each directory
* level in the filesystem. Instead, we push the blocks
* and wait for them to clear. We have to fsync twice
* because the first call may choose to defer blocks
* that still have dependencies, but deferral will
* happen at most once.
*/
inum = dap->da_newinum;
if (dap->da_state & MKDIR_BODY) {
FREE_LOCK(&lk);
if ((error = VFS_VGET(mp, inum, &vp)) != 0)
break;
if ((error=VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p)) ||
(error=VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) {
vput(vp);
break;
}
drain_output(vp, 0);
/*
* If first block is still dirty with a D_MKDIR
* dependency then it needs to be written now.
*/
for (;;) {
error = 0;
ACQUIRE_LOCK(&lk);
bp = incore(vp, 0);
if (bp == NULL) {
FREE_LOCK(&lk);
break;
}
LIST_FOREACH(wk, &bp->b_dep, wk_list)
if (wk->wk_type == D_MKDIR)
break;
if (wk) {
gotit = getdirtybuf(bp, MNT_WAIT);
FREE_LOCK(&lk);
if (gotit == -1)
continue;
if (gotit && (error = bwrite(bp)) != 0)
break;
} else
FREE_LOCK(&lk);
break;
}
vput(vp);
/* Flushing of first block failed */
if (error)
break;
ACQUIRE_LOCK(&lk);
/*
* If that cleared dependencies, go on to next.
*/
if (dap != LIST_FIRST(diraddhdp))
continue;
if (dap->da_state & MKDIR_BODY) {
FREE_LOCK(&lk);
panic("flush_pagedep_deps: MKDIR_BODY");
}
}
/*
* Flush the inode on which the directory entry depends.
* Having accounted for MKDIR_PARENT and MKDIR_BODY above,
* the only remaining dependency is that the updated inode
* count must get pushed to disk. The inode has already
* been pushed into its inode buffer (via VOP_UPDATE) at
* the time of the reference count change. So we need only
* locate that buffer, ensure that there will be no rollback
* caused by a bitmap dependency, then write the inode buffer.
*/
if (inodedep_lookup(ump->um_fs, inum, 0, &inodedep) == 0) {
FREE_LOCK(&lk);
panic("flush_pagedep_deps: lost inode");
}
/*
* If the inode still has bitmap dependencies,
* push them to disk.
*/
retry:
if ((inodedep->id_state & DEPCOMPLETE) == 0) {
bp = inodedep->id_buf;
gotit = getdirtybuf(bp, MNT_WAIT);
if (gotit == -1)
goto retry;
FREE_LOCK(&lk);
if (gotit && (error = bwrite(bp)) != 0)
break;
ACQUIRE_LOCK(&lk);
if (dap != LIST_FIRST(diraddhdp))
continue;
}
/*
* If the inode is still sitting in a buffer waiting
* to be written, push it to disk.
*/
FREE_LOCK(&lk);
if ((error = bread(ump->um_devvp,
fsbtodb(ump->um_fs, ino_to_fsba(ump->um_fs, inum)),
(int)ump->um_fs->fs_bsize, &bp)) != 0) {
brelse(bp);
break;
}
if ((error = bwrite(bp)) != 0)
break;
ACQUIRE_LOCK(&lk);
/*
* If we have failed to get rid of all the dependencies
* then something is seriously wrong.
*/
if (dap == LIST_FIRST(diraddhdp)) {
FREE_LOCK(&lk);
panic("flush_pagedep_deps: flush failed");
}
}
if (error)
ACQUIRE_LOCK(&lk);
return (error);
}
/*
* A large burst of file addition or deletion activity can drive the
* memory load excessively high. First attempt to slow things down
* using the techniques below. If that fails, this routine requests
* the offending operations to fall back to running synchronously
* until the memory load returns to a reasonable level.
*/
int
softdep_slowdown(struct vnode *vp)
{
int max_softdeps_hard;
max_softdeps_hard = max_softdeps * 11 / 10;
if (num_dirrem < max_softdeps_hard / 2 &&
num_inodedep < max_softdeps_hard)
return (0);
stat_sync_limit_hit += 1;
return (1);
}
/*
* If memory utilization has gotten too high, deliberately slow things
* down and speed up the I/O processing.
*/
STATIC int
request_cleanup(int resource, int islocked)
{
struct proc *p = CURPROC;
int s;
/*
* We never hold up the filesystem syncer process.
*/
if (p == filesys_syncer || (p->p_flag & P_SOFTDEP))
return (0);
/*
* First check to see if the work list has gotten backlogged.
* If it has, co-opt this process to help clean up two entries.
* Because this process may hold inodes locked, we cannot
* handle any remove requests that might block on a locked
* inode as that could lead to deadlock. We set P_SOFTDEP
* to avoid recursively processing the worklist.
*/
if (num_on_worklist > max_softdeps / 10) {
atomic_setbits_int(&p->p_flag, P_SOFTDEP);
if (islocked)
FREE_LOCK(&lk);
process_worklist_item(NULL, NULL, LK_NOWAIT);
process_worklist_item(NULL, NULL, LK_NOWAIT);
atomic_clearbits_int(&p->p_flag, P_SOFTDEP);
stat_worklist_push += 2;
if (islocked)
ACQUIRE_LOCK(&lk);
return(1);
}
/*
* Next, we attempt to speed up the syncer process. If that
* is successful, then we allow the process to continue.
*/
if (speedup_syncer())
return(0);
/*
* If we are resource constrained on inode dependencies, try
* flushing some dirty inodes. Otherwise, we are constrained
* by file deletions, so try accelerating flushes of directories
* with removal dependencies. We would like to do the cleanup
* here, but we probably hold an inode locked at this point and
* that might deadlock against one that we try to clean. So,
* the best that we can do is request the syncer daemon to do
* the cleanup for us.
*/
switch (resource) {
case FLUSH_INODES:
stat_ino_limit_push += 1;
req_clear_inodedeps += 1;
stat_countp = &stat_ino_limit_hit;
break;
case FLUSH_REMOVE:
stat_blk_limit_push += 1;
req_clear_remove += 1;
stat_countp = &stat_blk_limit_hit;
break;
default:
if (islocked)
FREE_LOCK(&lk);
panic("request_cleanup: unknown type");
}
/*
* Hopefully the syncer daemon will catch up and awaken us.
* We wait at most tickdelay before proceeding in any case.
*/
if (islocked == 0)
ACQUIRE_LOCK(&lk);
proc_waiting += 1;
if (!timeout_pending(&proc_waiting_timeout))
timeout_add(&proc_waiting_timeout, tickdelay > 2 ? tickdelay : 2);
s = FREE_LOCK_INTERLOCKED(&lk);
tsleep_nsec(&proc_waiting, PPAUSE, "softupdate", INFSLP);
ACQUIRE_LOCK_INTERLOCKED(&lk, s);
proc_waiting -= 1;
if (islocked == 0)
FREE_LOCK(&lk);
return (1);
}
/*
* Awaken processes pausing in request_cleanup and clear proc_waiting
* to indicate that there is no longer a timer running.
*/
void
pause_timer(void *arg)
{
*stat_countp += 1;
wakeup_one(&proc_waiting);
if (proc_waiting > 0)
timeout_add(&proc_waiting_timeout, tickdelay > 2 ? tickdelay : 2);
}
/*
* Flush out a directory with at least one removal dependency in an effort to
* reduce the number of dirrem, freefile, and freeblks dependency structures.
*/
STATIC void
clear_remove(struct proc *p)
{
struct pagedep_hashhead *pagedephd;
struct pagedep *pagedep;
static int next = 0;
struct mount *mp;
struct vnode *vp;
int error, cnt;
ufsino_t ino;
ACQUIRE_LOCK(&lk);
for (cnt = 0; cnt <= pagedep_hash; cnt++) {
pagedephd = &pagedep_hashtbl[next++];
if (next > pagedep_hash)
next = 0;
LIST_FOREACH(pagedep, pagedephd, pd_hash) {
if (LIST_FIRST(&pagedep->pd_dirremhd) == NULL)
continue;
mp = pagedep->pd_mnt;
ino = pagedep->pd_ino;
#if 0
if (vn_start_write(NULL, &mp, V_NOWAIT) != 0)
continue;
#endif
FREE_LOCK(&lk);
if ((error = VFS_VGET(mp, ino, &vp)) != 0) {
softdep_error("clear_remove: vget", error);
#if 0
vn_finished_write(mp);
#endif
return;
}
if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p)))
softdep_error("clear_remove: fsync", error);
drain_output(vp, 0);
vput(vp);
#if 0
vn_finished_write(mp);
#endif
return;
}
}
FREE_LOCK(&lk);
}
/*
* Clear out a block of dirty inodes in an effort to reduce
* the number of inodedep dependency structures.
*/
STATIC void
clear_inodedeps(struct proc *p)
{
struct inodedep_hashhead *inodedephd;
struct inodedep *inodedep = NULL;
static int next = 0;
struct mount *mp;
struct vnode *vp;
struct fs *fs;
int error, cnt;
ufsino_t firstino, lastino, ino;
ACQUIRE_LOCK(&lk);
/*
* Pick a random inode dependency to be cleared.
* We will then gather up all the inodes in its block
* that have dependencies and flush them out.
*/
for (cnt = 0; cnt <= inodedep_hash; cnt++) {
inodedephd = &inodedep_hashtbl[next++];
if (next > inodedep_hash)
next = 0;
if ((inodedep = LIST_FIRST(inodedephd)) != NULL)
break;
}
if (inodedep == NULL) {
FREE_LOCK(&lk);
return;
}
/*
* Ugly code to find mount point given pointer to superblock.
*/
fs = inodedep->id_fs;
TAILQ_FOREACH(mp, &mountlist, mnt_list)
if ((mp->mnt_flag & MNT_SOFTDEP) && fs == VFSTOUFS(mp)->um_fs)
break;
/*
* Find the last inode in the block with dependencies.
*/
firstino = inodedep->id_ino & ~(INOPB(fs) - 1);
for (lastino = firstino + INOPB(fs) - 1; lastino > firstino; lastino--)
if (inodedep_lookup(fs, lastino, 0, &inodedep) != 0)
break;
/*
* Asynchronously push all but the last inode with dependencies.
* Synchronously push the last inode with dependencies to ensure
* that the inode block gets written to free up the inodedeps.
*/
for (ino = firstino; ino <= lastino; ino++) {
if (inodedep_lookup(fs, ino, 0, &inodedep) == 0)
continue;
FREE_LOCK(&lk);
#if 0
if (vn_start_write(NULL, &mp, V_NOWAIT) != 0)
continue;
#endif
if ((error = VFS_VGET(mp, ino, &vp)) != 0) {
softdep_error("clear_inodedeps: vget", error);
#if 0
vn_finished_write(mp);
#endif
return;
}
if (ino == lastino) {
if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p)))
softdep_error("clear_inodedeps: fsync1", error);
} else {
if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p)))
softdep_error("clear_inodedeps: fsync2", error);
drain_output(vp, 0);
}
vput(vp);
#if 0
vn_finished_write(mp);
#endif
ACQUIRE_LOCK(&lk);
}
FREE_LOCK(&lk);
}
/*
* Function to determine if the buffer has outstanding dependencies
* that will cause a roll-back if the buffer is written. If wantcount
* is set, return number of dependencies, otherwise just yes or no.
*/
int
softdep_count_dependencies(struct buf *bp, int wantcount, int islocked)
{
struct worklist *wk;
struct inodedep *inodedep;
struct indirdep *indirdep;
struct allocindir *aip;
struct pagedep *pagedep;
struct diradd *dap;
int i, retval;
retval = 0;
if (!islocked)
ACQUIRE_LOCK(&lk);
LIST_FOREACH(wk, &bp->b_dep, wk_list) {
switch (wk->wk_type) {
case D_INODEDEP:
inodedep = WK_INODEDEP(wk);
if ((inodedep->id_state & DEPCOMPLETE) == 0) {
/* bitmap allocation dependency */
retval += 1;
if (!wantcount)
goto out;
}
if (TAILQ_FIRST(&inodedep->id_inoupdt)) {
/* direct block pointer dependency */
retval += 1;
if (!wantcount)
goto out;
}
continue;
case D_INDIRDEP:
indirdep = WK_INDIRDEP(wk);
LIST_FOREACH(aip, &indirdep->ir_deplisthd, ai_next) {
/* indirect block pointer dependency */
retval += 1;
if (!wantcount)
goto out;
}
continue;
case D_PAGEDEP:
pagedep = WK_PAGEDEP(wk);
for (i = 0; i < DAHASHSZ; i++) {
LIST_FOREACH(dap, &pagedep->pd_diraddhd[i], da_pdlist) {
/* directory entry dependency */
retval += 1;
if (!wantcount)
goto out;
}
}
continue;
case D_BMSAFEMAP:
case D_ALLOCDIRECT:
case D_ALLOCINDIR:
case D_MKDIR:
/* never a dependency on these blocks */
continue;
default:
if (!islocked)
FREE_LOCK(&lk);
panic("softdep_check_for_rollback: Unexpected type %s",
TYPENAME(wk->wk_type));
/* NOTREACHED */
}
}
out:
if (!islocked)
FREE_LOCK(&lk);
return retval;
}
/*
* Acquire exclusive access to a buffer.
* Must be called with splbio blocked.
* Returns:
* 1 if the buffer was acquired and is dirty;
* 0 if the buffer was clean, or we would have slept but had MN_NOWAIT;
* -1 if we slept and may try again (but not with this bp).
*/
STATIC int
getdirtybuf(struct buf *bp, int waitfor)
{
int s;
if (bp == NULL)
return (0);
splassert(IPL_BIO);
if (bp->b_flags & B_BUSY) {
if (waitfor != MNT_WAIT)
return (0);
bp->b_flags |= B_WANTED;
s = FREE_LOCK_INTERLOCKED(&lk);
tsleep_nsec(bp, PRIBIO+1, "sdsdty", INFSLP);
ACQUIRE_LOCK_INTERLOCKED(&lk, s);
return (-1);
}
if ((bp->b_flags & B_DELWRI) == 0)
return (0);
bremfree(bp);
buf_acquire(bp);
return (1);
}
/*
* Wait for pending output on a vnode to complete.
* Must be called with vnode locked.
*/
STATIC void
drain_output(struct vnode *vp, int islocked)
{
int s;
if (!islocked)
ACQUIRE_LOCK(&lk);
splassert(IPL_BIO);
while (vp->v_numoutput) {
vp->v_bioflag |= VBIOWAIT;
s = FREE_LOCK_INTERLOCKED(&lk);
tsleep_nsec(&vp->v_numoutput, PRIBIO+1, "drain_output", INFSLP);
ACQUIRE_LOCK_INTERLOCKED(&lk, s);
}
if (!islocked)
FREE_LOCK(&lk);
}
/*
* Called whenever a buffer that is being invalidated or reallocated
* contains dependencies. This should only happen if an I/O error has
* occurred. The routine is called with the buffer locked.
*/
void
softdep_deallocate_dependencies(struct buf *bp)
{
if ((bp->b_flags & B_ERROR) == 0)
panic("softdep_deallocate_dependencies: dangling deps");
softdep_error(bp->b_vp->v_mount->mnt_stat.f_mntonname, bp->b_error);
panic("softdep_deallocate_dependencies: unrecovered I/O error");
}
/*
* Function to handle asynchronous write errors in the filesystem.
*/
void
softdep_error(char *func, int error)
{
/* XXX should do something better! */
printf("%s: got error %d while accessing filesystem\n", func, error);
}
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
#include <ddb/db_output.h>
void
softdep_print(struct buf *bp, int full,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct worklist *wk;
(*pr)(" deps:\n");
LIST_FOREACH(wk, &bp->b_dep, wk_list)
worklist_print(wk, full, pr);
}
void
worklist_print(struct worklist *wk, int full,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct pagedep *pagedep;
struct inodedep *inodedep;
struct newblk *newblk;
struct bmsafemap *bmsafemap;
struct allocdirect *adp;
struct indirdep *indirdep;
struct allocindir *aip;
struct freefrag *freefrag;
struct freeblks *freeblks;
struct freefile *freefile;
struct diradd *dap;
struct mkdir *mkdir;
struct dirrem *dirrem;
struct newdirblk *newdirblk;
char prefix[33];
int i;
for (prefix[i = 2 * MIN(16, full)] = '\0'; i--; prefix[i] = ' ')
;
(*pr)("%s%s(%p) state %b\n%s", prefix, TYPENAME(wk->wk_type), wk,
wk->wk_state, DEP_BITS, prefix);
switch (wk->wk_type) {
case D_PAGEDEP:
pagedep = WK_PAGEDEP(wk);
(*pr)("mount %p ino %u lbn %lld\n", pagedep->pd_mnt,
pagedep->pd_ino, (long long)pagedep->pd_lbn);
break;
case D_INODEDEP:
inodedep = WK_INODEDEP(wk);
(*pr)("fs %p ino %u nlinkdelta %u dino %p\n"
"%s bp %p savsz %lld\n", inodedep->id_fs,
inodedep->id_ino, inodedep->id_nlinkdelta,
inodedep->id_un.idu_savedino1,
prefix, inodedep->id_buf, inodedep->id_savedsize);
break;
case D_NEWBLK:
newblk = WK_NEWBLK(wk);
(*pr)("fs %p newblk %lld state %d bmsafemap %p\n",
newblk->nb_fs, (long long)newblk->nb_newblkno,
newblk->nb_state, newblk->nb_bmsafemap);
break;
case D_BMSAFEMAP:
bmsafemap = WK_BMSAFEMAP(wk);
(*pr)("buf %p\n", bmsafemap->sm_buf);
break;
case D_ALLOCDIRECT:
adp = WK_ALLOCDIRECT(wk);
(*pr)("lbn %lld newlbk %lld oldblk %lld newsize %ld olsize "
"%ld\n%s bp %p inodedep %p freefrag %p\n",
(long long)adp->ad_lbn, (long long)adp->ad_newblkno,
(long long)adp->ad_oldblkno, adp->ad_newsize,
adp->ad_oldsize,
prefix, adp->ad_buf, adp->ad_inodedep, adp->ad_freefrag);
break;
case D_INDIRDEP:
indirdep = WK_INDIRDEP(wk);
(*pr)("savedata %p savebp %p\n", indirdep->ir_saveddata,
indirdep->ir_savebp);
break;
case D_ALLOCINDIR:
aip = WK_ALLOCINDIR(wk);
(*pr)("off %d newblk %lld oldblk %lld freefrag %p\n"
"%s indirdep %p buf %p\n", aip->ai_offset,
(long long)aip->ai_newblkno, (long long)aip->ai_oldblkno,
aip->ai_freefrag, prefix, aip->ai_indirdep, aip->ai_buf);
break;
case D_FREEFRAG:
freefrag = WK_FREEFRAG(wk);
(*pr)("vnode %p mp %p blkno %lld fsize %ld ino %u\n",
freefrag->ff_devvp, freefrag->ff_mnt,
(long long)freefrag->ff_blkno, freefrag->ff_fragsize,
freefrag->ff_inum);
break;
case D_FREEBLKS:
freeblks = WK_FREEBLKS(wk);
(*pr)("previno %u devvp %p mp %p oldsz %lld newsz %lld\n"
"%s chkcnt %d uid %d\n", freeblks->fb_previousinum,
freeblks->fb_devvp, freeblks->fb_mnt, freeblks->fb_oldsize,
freeblks->fb_newsize,
prefix, freeblks->fb_chkcnt, freeblks->fb_uid);
break;
case D_FREEFILE:
freefile = WK_FREEFILE(wk);
(*pr)("mode %x oldino %u vnode %p mp %p\n", freefile->fx_mode,
freefile->fx_oldinum, freefile->fx_devvp, freefile->fx_mnt);
break;
case D_DIRADD:
dap = WK_DIRADD(wk);
(*pr)("off %d ino %u da_un %p\n", dap->da_offset,
dap->da_newinum, dap->da_un.dau_previous);
break;
case D_MKDIR:
mkdir = WK_MKDIR(wk);
(*pr)("diradd %p bp %p\n", mkdir->md_diradd, mkdir->md_buf);
break;
case D_DIRREM:
dirrem = WK_DIRREM(wk);
(*pr)("mp %p ino %u dm_un %p\n", dirrem->dm_mnt,
dirrem->dm_oldinum, dirrem->dm_un.dmu_pagedep);
break;
case D_NEWDIRBLK:
newdirblk = WK_NEWDIRBLK(wk);
(*pr)("pagedep %p\n", newdirblk->db_pagedep);
break;
}
}
#endif
3
3
3
3
3
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* $OpenBSD: md5.c,v 1.4 2014/12/28 10:04:35 tedu Exp $ */
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <crypto/md5.h>
#define PUT_64BIT_LE(cp, value) do { \
(cp)[7] = (value) >> 56; \
(cp)[6] = (value) >> 48; \
(cp)[5] = (value) >> 40; \
(cp)[4] = (value) >> 32; \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
#define PUT_32BIT_LE(cp, value) do { \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
static u_int8_t PADDING[MD5_BLOCK_LENGTH] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void
MD5Init(MD5_CTX *ctx)
{
ctx->count = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(MD5_CTX *ctx, const void *inputptr, size_t len)
{
const uint8_t *input = inputptr;
size_t have, need;
/* Check how many bytes we already have and how many more we need. */
have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
need = MD5_BLOCK_LENGTH - have;
/* Update bitcount */
ctx->count += (u_int64_t)len << 3;
if (len >= need) {
if (have != 0) {
memcpy(ctx->buffer + have, input, need);
MD5Transform(ctx->state, ctx->buffer);
input += need;
len -= need;
have = 0;
}
/* Process data in MD5_BLOCK_LENGTH-byte chunks. */
while (len >= MD5_BLOCK_LENGTH) {
MD5Transform(ctx->state, input);
input += MD5_BLOCK_LENGTH;
len -= MD5_BLOCK_LENGTH;
}
}
/* Handle any remaining bytes of data. */
if (len != 0)
memcpy(ctx->buffer + have, input, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)
{
u_int8_t count[8];
size_t padlen;
int i;
/* Convert count to 8 bytes in little endian order. */
PUT_64BIT_LE(count, ctx->count);
/* Pad out to 56 mod 64. */
padlen = MD5_BLOCK_LENGTH -
((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
if (padlen < 1 + 8)
padlen += MD5_BLOCK_LENGTH;
MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
MD5Update(ctx, count, 8);
for (i = 0; i < 4; i++)
PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
explicit_bzero(ctx, sizeof(*ctx)); /* in case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void
MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH])
{
u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
#if BYTE_ORDER == LITTLE_ENDIAN
memcpy(in, block, sizeof(in));
#else
for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
in[a] = (u_int32_t)(
(u_int32_t)(block[a * 4 + 0]) |
(u_int32_t)(block[a * 4 + 1]) << 8 |
(u_int32_t)(block[a * 4 + 2]) << 16 |
(u_int32_t)(block[a * 4 + 3]) << 24);
}
#endif
a = state[0];
b = state[1];
c = state[2];
d = state[3];
MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
/* $OpenBSD: tcp_timer.c,v 1.70 2022/09/03 19:22:19 bluhm Exp $ */
/* $NetBSD: tcp_timer.c,v 1.14 1996/02/13 23:44:09 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp_seq.h>
/*
* Locks used to protect struct members in this file:
* T tcp_timer_mtx global tcp timer data structures
*/
int tcp_always_keepalive;
int tcp_keepidle;
int tcp_keepintvl;
int tcp_maxpersistidle; /* max idle time in persist */
int tcp_maxidle; /* [T] max idle time for keep alive */
/*
* Time to delay the ACK. This is initialized in tcp_init(), unless
* its patched.
*/
int tcp_delack_msecs;
void tcp_timer_rexmt(void *);
void tcp_timer_persist(void *);
void tcp_timer_keep(void *);
void tcp_timer_2msl(void *);
void tcp_timer_reaper(void *);
void tcp_timer_delack(void *);
const tcp_timer_func_t tcp_timer_funcs[TCPT_NTIMERS] = {
tcp_timer_rexmt,
tcp_timer_persist,
tcp_timer_keep,
tcp_timer_2msl,
tcp_timer_reaper,
tcp_timer_delack,
};
/*
* Timer state initialization, called from tcp_init().
*/
void
tcp_timer_init(void)
{
if (tcp_keepidle == 0)
tcp_keepidle = TCPTV_KEEP_IDLE;
if (tcp_keepintvl == 0)
tcp_keepintvl = TCPTV_KEEPINTVL;
if (tcp_maxpersistidle == 0)
tcp_maxpersistidle = TCPTV_KEEP_IDLE;
if (tcp_delack_msecs == 0)
tcp_delack_msecs = TCP_DELACK_MSECS;
}
/*
* Callout to process delayed ACKs for a TCPCB.
*/
void
tcp_timer_delack(void *arg)
{
struct tcpcb *otp = NULL, *tp = arg;
short ostate;
/*
* If tcp_output() wasn't able to transmit the ACK
* for whatever reason, it will restart the delayed
* ACK callout.
*/
NET_LOCK();
/* Ignore canceled timeouts or timeouts that have been rescheduled. */
if (!ISSET((tp)->t_flags, TF_TMR_DELACK) ||
timeout_pending(&tp->t_timer[TCPT_DELACK]))
goto out;
CLR((tp)->t_flags, TF_TMR_DELACK);
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tp->t_flags |= TF_ACKNOW;
(void) tcp_output(tp);
if (otp)
tcp_trace(TA_TIMER, ostate, tp, otp, NULL, TCPT_DELACK, 0);
out:
NET_UNLOCK();
}
/*
* Tcp protocol timeout routine called every 500 ms.
* Updates the timers in all active tcb's and
* causes finite state machine actions if timers expire.
*/
void
tcp_slowtimo(void)
{
mtx_enter(&tcp_timer_mtx);
tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl;
tcp_iss += TCP_ISSINCR2/PR_SLOWHZ; /* increment iss */
tcp_now++; /* for timestamps */
mtx_leave(&tcp_timer_mtx);
}
/*
* Cancel all timers for TCP tp.
*/
void
tcp_canceltimers(struct tcpcb *tp)
{
int i;
for (i = 0; i < TCPT_NTIMERS; i++)
TCP_TIMER_DISARM(tp, i);
}
int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
{ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
int tcp_totbackoff = 511; /* sum of tcp_backoff[] */
/*
* TCP timer processing.
*/
void tcp_timer_freesack(struct tcpcb *);
void
tcp_timer_freesack(struct tcpcb *tp)
{
struct sackhole *p, *q;
/*
* Free SACK holes for 2MSL and REXMT timers.
*/
q = tp->snd_holes;
while (q != NULL) {
p = q;
q = q->next;
pool_put(&sackhl_pool, p);
}
tp->snd_holes = 0;
}
void
tcp_timer_rexmt(void *arg)
{
struct tcpcb *otp = NULL, *tp = arg;
uint32_t rto;
short ostate;
NET_LOCK();
/* Ignore canceled timeouts or timeouts that have been rescheduled. */
if (!ISSET((tp)->t_flags, TF_TMR_REXMT) ||
timeout_pending(&tp->t_timer[TCPT_REXMT]))
goto out;
CLR((tp)->t_flags, TF_TMR_REXMT);
if ((tp->t_flags & TF_PMTUD_PEND) && tp->t_inpcb &&
SEQ_GEQ(tp->t_pmtud_th_seq, tp->snd_una) &&
SEQ_LT(tp->t_pmtud_th_seq, (int)(tp->snd_una + tp->t_maxseg))) {
struct sockaddr_in sin;
struct icmp icmp;
tp->t_flags &= ~TF_PMTUD_PEND;
/* XXX create fake icmp message with relevant entries */
icmp.icmp_nextmtu = tp->t_pmtud_nextmtu;
icmp.icmp_ip.ip_len = tp->t_pmtud_ip_len;
icmp.icmp_ip.ip_hl = tp->t_pmtud_ip_hl;
icmp.icmp_ip.ip_dst = tp->t_inpcb->inp_faddr;
icmp_mtudisc(&icmp, tp->t_inpcb->inp_rtableid);
/*
* Notify all connections to the same peer about
* new mss and trigger retransmit.
*/
bzero(&sin, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = tp->t_inpcb->inp_faddr;
in_pcbnotifyall(&tcbtable, sintosa(&sin),
tp->t_inpcb->inp_rtableid, EMSGSIZE, tcp_mtudisc);
goto out;
}
tcp_timer_freesack(tp);
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
tp->t_rxtshift = TCP_MAXRXTSHIFT;
tcpstat_inc(tcps_timeoutdrop);
tp = tcp_drop(tp, tp->t_softerror ?
tp->t_softerror : ETIMEDOUT);
goto out;
}
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tcpstat_inc(tcps_rexmttimeo);
rto = TCP_REXMTVAL(tp);
if (rto < tp->t_rttmin)
rto = tp->t_rttmin;
TCPT_RANGESET(tp->t_rxtcur,
rto * tcp_backoff[tp->t_rxtshift],
tp->t_rttmin, TCPTV_REXMTMAX);
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
/*
* If we are losing and we are trying path MTU discovery,
* try turning it off. This will avoid black holes in
* the network which suppress or fail to send "packet
* too big" ICMP messages. We should ideally do
* lots more sophisticated searching to find the right
* value here...
*/
if (ip_mtudisc && tp->t_inpcb &&
TCPS_HAVEESTABLISHED(tp->t_state) &&
tp->t_rxtshift > TCP_MAXRXTSHIFT / 6) {
struct inpcb *inp = tp->t_inpcb;
struct rtentry *rt = NULL;
/* No data to send means path mtu is not a problem */
if (!inp->inp_socket->so_snd.sb_cc)
goto leave;
rt = in_pcbrtentry(inp);
/* Check if path MTU discovery is disabled already */
if (rt && (rt->rt_flags & RTF_HOST) &&
(rt->rt_locks & RTV_MTU))
goto leave;
rt = NULL;
switch(tp->pf) {
#ifdef INET6
case PF_INET6:
/*
* We can not turn off path MTU for IPv6.
* Do nothing for now, maybe lower to
* minimum MTU.
*/
break;
#endif
case PF_INET:
rt = icmp_mtudisc_clone(inp->inp_faddr,
inp->inp_rtableid, 0);
break;
}
if (rt != NULL) {
/* Disable path MTU discovery */
if ((rt->rt_locks & RTV_MTU) == 0) {
rt->rt_locks |= RTV_MTU;
in_rtchange(inp, 0);
}
rtfree(rt);
}
leave:
;
}
/*
* If losing, let the lower level know and try for
* a better route. Also, if we backed off this far,
* our srtt estimate is probably bogus. Clobber it
* so we'll take the next rtt measurement as our srtt;
* move the current srtt into rttvar to keep the current
* retransmit times until then.
*/
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
in_losing(tp->t_inpcb);
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
tp->t_srtt = 0;
}
tp->snd_nxt = tp->snd_una;
/*
* Note: We overload snd_last to function also as the
* snd_last variable described in RFC 2582
*/
tp->snd_last = tp->snd_max;
/*
* If timing a segment in this window, stop the timer.
*/
tp->t_rtttime = 0;
#ifdef TCP_ECN
/*
* if ECN is enabled, there might be a broken firewall which
* blocks ecn packets. fall back to non-ecn.
*/
if ((tp->t_state == TCPS_SYN_SENT || tp->t_state == TCPS_SYN_RECEIVED)
&& tcp_do_ecn && !(tp->t_flags & TF_DISABLE_ECN))
tp->t_flags |= TF_DISABLE_ECN;
#endif
/*
* Close the congestion window down to one segment
* (we'll open it by one segment for each ack we get).
* Since we probably have a window's worth of unacked
* data accumulated, this "slow start" keeps us from
* dumping all that data as back-to-back packets (which
* might overwhelm an intermediate gateway).
*
* There are two phases to the opening: Initially we
* open by one mss on each ack. This makes the window
* size increase exponentially with time. If the
* window is larger than the path can handle, this
* exponential growth results in dropped packet(s)
* almost immediately. To get more time between
* drops but still "push" the network to take advantage
* of improving conditions, we switch from exponential
* to linear window opening at some threshold size.
* For a threshold, we use half the current window
* size, truncated to a multiple of the mss.
*
* (the minimum cwnd that will give us exponential
* growth is 2 mss. We don't allow the threshold
* to go below this.)
*/
{
u_long win = ulmin(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
if (win < 2)
win = 2;
tp->snd_cwnd = tp->t_maxseg;
tp->snd_ssthresh = win * tp->t_maxseg;
tp->t_dupacks = 0;
#ifdef TCP_ECN
tp->snd_last = tp->snd_max;
tp->t_flags |= TF_SEND_CWR;
#endif
#if 1 /* TCP_ECN */
tcpstat_inc(tcps_cwr_timeout);
#endif
}
(void) tcp_output(tp);
if (otp)
tcp_trace(TA_TIMER, ostate, tp, otp, NULL, TCPT_REXMT, 0);
out:
NET_UNLOCK();
}
void
tcp_timer_persist(void *arg)
{
struct tcpcb *otp = NULL, *tp = arg;
uint32_t rto;
short ostate;
uint32_t now;
NET_LOCK();
/* Ignore canceled timeouts or timeouts that have been rescheduled. */
if (!ISSET((tp)->t_flags, TF_TMR_PERSIST) ||
timeout_pending(&tp->t_timer[TCPT_PERSIST]))
goto out;
CLR((tp)->t_flags, TF_TMR_PERSIST);
if (TCP_TIMER_ISARMED(tp, TCPT_REXMT))
goto out;
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tcpstat_inc(tcps_persisttimeo);
/*
* Hack: if the peer is dead/unreachable, we do not
* time out if the window is closed. After a full
* backoff, drop the connection if the idle time
* (no responses to probes) reaches the maximum
* backoff that we would use if retransmitting.
*/
rto = TCP_REXMTVAL(tp);
if (rto < tp->t_rttmin)
rto = tp->t_rttmin;
now = READ_ONCE(tcp_now);
if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
((now - tp->t_rcvtime) >= tcp_maxpersistidle ||
(now - tp->t_rcvtime) >= rto * tcp_totbackoff)) {
tcpstat_inc(tcps_persistdrop);
tp = tcp_drop(tp, ETIMEDOUT);
goto out;
}
tcp_setpersist(tp);
tp->t_force = 1;
(void) tcp_output(tp);
tp->t_force = 0;
if (otp)
tcp_trace(TA_TIMER, ostate, tp, otp, NULL, TCPT_PERSIST, 0);
out:
NET_UNLOCK();
}
void
tcp_timer_keep(void *arg)
{
struct tcpcb *otp = NULL, *tp = arg;
short ostate;
NET_LOCK();
/* Ignore canceled timeouts or timeouts that have been rescheduled. */
if (!ISSET((tp)->t_flags, TF_TMR_KEEP) ||
timeout_pending(&tp->t_timer[TCPT_KEEP]))
goto out;
CLR((tp)->t_flags, TF_TMR_KEEP);
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tcpstat_inc(tcps_keeptimeo);
if (TCPS_HAVEESTABLISHED(tp->t_state) == 0)
goto dropit;
if ((tcp_always_keepalive ||
tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) &&
tp->t_state <= TCPS_CLOSING) {
int maxidle;
uint32_t now;
maxidle = READ_ONCE(tcp_maxidle);
now = READ_ONCE(tcp_now);
if ((maxidle > 0) &&
((now - tp->t_rcvtime) >= tcp_keepidle + maxidle))
goto dropit;
/*
* Send a packet designed to force a response
* if the peer is up and reachable:
* either an ACK if the connection is still alive,
* or an RST if the peer has closed the connection
* due to timeout or reboot.
* Using sequence number tp->snd_una-1
* causes the transmitted zero-length segment
* to lie outside the receive window;
* by the protocol spec, this requires the
* correspondent TCP to respond.
*/
tcpstat_inc(tcps_keepprobe);
tcp_respond(tp, mtod(tp->t_template, caddr_t),
NULL, tp->rcv_nxt, tp->snd_una - 1, 0, 0, now);
TCP_TIMER_ARM(tp, TCPT_KEEP, tcp_keepintvl);
} else
TCP_TIMER_ARM(tp, TCPT_KEEP, tcp_keepidle);
if (otp)
tcp_trace(TA_TIMER, ostate, tp, otp, NULL, TCPT_KEEP, 0);
out:
NET_UNLOCK();
return;
dropit:
tcpstat_inc(tcps_keepdrops);
tp = tcp_drop(tp, ETIMEDOUT);
NET_UNLOCK();
}
void
tcp_timer_2msl(void *arg)
{
struct tcpcb *otp = NULL, *tp = arg;
short ostate;
int maxidle;
uint32_t now;
NET_LOCK();
/* Ignore canceled timeouts or timeouts that have been rescheduled. */
if (!ISSET((tp)->t_flags, TF_TMR_2MSL) ||
timeout_pending(&tp->t_timer[TCPT_2MSL]))
goto out;
CLR((tp)->t_flags, TF_TMR_2MSL);
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tcp_timer_freesack(tp);
maxidle = READ_ONCE(tcp_maxidle);
now = READ_ONCE(tcp_now);
if (tp->t_state != TCPS_TIME_WAIT &&
((maxidle == 0) || ((now - tp->t_rcvtime) <= maxidle)))
TCP_TIMER_ARM(tp, TCPT_2MSL, tcp_keepintvl);
else
tp = tcp_close(tp);
if (otp)
tcp_trace(TA_TIMER, ostate, tp, otp, NULL, TCPT_2MSL, 0);
out:
NET_UNLOCK();
}
void
tcp_timer_reaper(void *arg)
{
struct tcpcb *tp = arg;
/*
* This timer is necessary to delay the pool_put() after all timers
* have finished, even if they were sleeping to grab the net lock.
* Putting the pool_put() in a timer is sufficient as all timers run
* from the same timeout thread. Note that neither softnet thread nor
* user process may access the tcpcb after arming the reaper timer.
* Freeing may run in parallel as it does not grab the net lock.
*/
pool_put(&tcpcb_pool, tp);
tcpstat_inc(tcps_closed);
}
504
495
5
498
493
2
494
1
321
83
356
2
360
12
12
11
12
12
2463
96
74
2618
9
4038
4036
2
209
210
6
4
1
3
138
137
3
9
125
125
179
32
3
8
1
1
2
1
2
1
3
9
3
29
57
1
2
71
3
53
14
6
4
4
30
4
17
21
43
1
1
2
13
5
3
5
5
11
2
167
132
120
1
128
3
112
23
1
319
315
5
320
358
184
170
170
504
10
3
492
495
482
10
7
483
455
3
443
13
459
492
180
317
168
329
21
1
1
12
6
2
16
15
4
4
3
1
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
/* $OpenBSD: kern_descrip.c,v 1.206 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_descrip.c,v 1.42 1996/03/30 22:24:38 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_descrip.c 8.6 (Berkeley) 4/19/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/ucred.h>
#include <sys/unistd.h>
#include <sys/resourcevar.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/event.h>
#include <sys/pool.h>
#include <sys/ktrace.h>
#include <sys/pledge.h>
/*
* Descriptor management.
*
* We need to block interrupts as long as `fhdlk' is being taken
* with and without the KERNEL_LOCK().
*/
struct mutex fhdlk = MUTEX_INITIALIZER(IPL_MPFLOOR);
struct filelist filehead; /* head of list of open files */
int numfiles; /* actual number of open files */
static __inline void fd_used(struct filedesc *, int);
static __inline void fd_unused(struct filedesc *, int);
static __inline int find_next_zero(u_int *, int, u_int);
static __inline int fd_inuse(struct filedesc *, int);
int finishdup(struct proc *, struct file *, int, int, register_t *, int);
int find_last_set(struct filedesc *, int);
int dodup3(struct proc *, int, int, int, register_t *);
#define DUPF_CLOEXEC 0x01
#define DUPF_DUP2 0x02
struct pool file_pool;
struct pool fdesc_pool;
void
filedesc_init(void)
{
pool_init(&file_pool, sizeof(struct file), 0, IPL_MPFLOOR,
PR_WAITOK, "filepl", NULL);
pool_init(&fdesc_pool, sizeof(struct filedesc0), 0, IPL_NONE,
PR_WAITOK, "fdescpl", NULL);
LIST_INIT(&filehead);
}
static __inline int
find_next_zero (u_int *bitmap, int want, u_int bits)
{
int i, off, maxoff;
u_int sub;
if (want > bits)
return -1;
off = want >> NDENTRYSHIFT;
i = want & NDENTRYMASK;
if (i) {
sub = bitmap[off] | ((u_int)~0 >> (NDENTRIES - i));
if (sub != ~0)
goto found;
off++;
}
maxoff = NDLOSLOTS(bits);
while (off < maxoff) {
if ((sub = bitmap[off]) != ~0)
goto found;
off++;
}
return -1;
found:
return (off << NDENTRYSHIFT) + ffs(~sub) - 1;
}
int
find_last_set(struct filedesc *fd, int last)
{
int off, i;
u_int *bitmap = fd->fd_lomap;
off = (last - 1) >> NDENTRYSHIFT;
while (off >= 0 && !bitmap[off])
off--;
if (off < 0)
return 0;
i = ((off + 1) << NDENTRYSHIFT) - 1;
if (i >= last)
i = last - 1;
while (i > 0 && !fd_inuse(fd, i))
i--;
return i;
}
static __inline int
fd_inuse(struct filedesc *fdp, int fd)
{
u_int off = fd >> NDENTRYSHIFT;
if (fdp->fd_lomap[off] & (1U << (fd & NDENTRYMASK)))
return 1;
return 0;
}
static __inline void
fd_used(struct filedesc *fdp, int fd)
{
u_int off = fd >> NDENTRYSHIFT;
fdp->fd_lomap[off] |= 1U << (fd & NDENTRYMASK);
if (fdp->fd_lomap[off] == ~0)
fdp->fd_himap[off >> NDENTRYSHIFT] |= 1U << (off & NDENTRYMASK);
if (fd > fdp->fd_lastfile)
fdp->fd_lastfile = fd;
fdp->fd_openfd++;
}
static __inline void
fd_unused(struct filedesc *fdp, int fd)
{
u_int off = fd >> NDENTRYSHIFT;
if (fd < fdp->fd_freefile)
fdp->fd_freefile = fd;
if (fdp->fd_lomap[off] == ~0)
fdp->fd_himap[off >> NDENTRYSHIFT] &= ~(1U << (off & NDENTRYMASK));
fdp->fd_lomap[off] &= ~(1U << (fd & NDENTRYMASK));
#ifdef DIAGNOSTIC
if (fd > fdp->fd_lastfile)
panic("fd_unused: fd_lastfile inconsistent");
#endif
if (fd == fdp->fd_lastfile)
fdp->fd_lastfile = find_last_set(fdp, fd);
fdp->fd_openfd--;
}
struct file *
fd_iterfile(struct file *fp, struct proc *p)
{
struct file *nfp;
unsigned int count;
mtx_enter(&fhdlk);
if (fp == NULL)
nfp = LIST_FIRST(&filehead);
else
nfp = LIST_NEXT(fp, f_list);
/* don't refcount when f_count == 0 to avoid race in fdrop() */
while (nfp != NULL) {
count = nfp->f_count;
if (count == 0) {
nfp = LIST_NEXT(nfp, f_list);
continue;
}
if (atomic_cas_uint(&nfp->f_count, count, count + 1) == count)
break;
}
mtx_leave(&fhdlk);
if (fp != NULL)
FRELE(fp, p);
return nfp;
}
struct file *
fd_getfile(struct filedesc *fdp, int fd)
{
struct file *fp;
vfs_stall_barrier();
if ((u_int)fd >= fdp->fd_nfiles)
return (NULL);
mtx_enter(&fdp->fd_fplock);
fp = fdp->fd_ofiles[fd];
if (fp != NULL)
atomic_inc_int(&fp->f_count);
mtx_leave(&fdp->fd_fplock);
return (fp);
}
struct file *
fd_getfile_mode(struct filedesc *fdp, int fd, int mode)
{
struct file *fp;
KASSERT(mode != 0);
fp = fd_getfile(fdp, fd);
if (fp == NULL)
return (NULL);
if ((fp->f_flag & mode) == 0) {
FRELE(fp, curproc);
return (NULL);
}
return (fp);
}
int
fd_checkclosed(struct filedesc *fdp, int fd, struct file *fp)
{
int closed;
mtx_enter(&fdp->fd_fplock);
KASSERT(fd < fdp->fd_nfiles);
closed = (fdp->fd_ofiles[fd] != fp);
mtx_leave(&fdp->fd_fplock);
return (closed);
}
/*
* System calls on descriptors.
*/
/*
* Duplicate a file descriptor.
*/
int
sys_dup(struct proc *p, void *v, register_t *retval)
{
struct sys_dup_args /* {
syscallarg(int) fd;
} */ *uap = v;
struct filedesc *fdp = p->p_fd;
int old = SCARG(uap, fd);
struct file *fp;
int new;
int error;
restart:
if ((fp = fd_getfile(fdp, old)) == NULL)
return (EBADF);
fdplock(fdp);
if ((error = fdalloc(p, 0, &new)) != 0) {
if (error == ENOSPC) {
fdexpand(p);
fdpunlock(fdp);
FRELE(fp, p);
goto restart;
}
fdpunlock(fdp);
FRELE(fp, p);
return (error);
}
/* No need for FRELE(), finishdup() uses current ref. */
return (finishdup(p, fp, old, new, retval, 0));
}
/*
* Duplicate a file descriptor to a particular value.
*/
int
sys_dup2(struct proc *p, void *v, register_t *retval)
{
struct sys_dup2_args /* {
syscallarg(int) from;
syscallarg(int) to;
} */ *uap = v;
return (dodup3(p, SCARG(uap, from), SCARG(uap, to), 0, retval));
}
int
sys_dup3(struct proc *p, void *v, register_t *retval)
{
struct sys_dup3_args /* {
syscallarg(int) from;
syscallarg(int) to;
syscallarg(int) flags;
} */ *uap = v;
if (SCARG(uap, from) == SCARG(uap, to))
return (EINVAL);
if (SCARG(uap, flags) & ~O_CLOEXEC)
return (EINVAL);
return (dodup3(p, SCARG(uap, from), SCARG(uap, to),
SCARG(uap, flags), retval));
}
int
dodup3(struct proc *p, int old, int new, int flags, register_t *retval)
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
int dupflags, error, i;
restart:
if ((fp = fd_getfile(fdp, old)) == NULL)
return (EBADF);
if (old == new) {
/*
* NOTE! This doesn't clear the close-on-exec flag. This might
* or might not be the intended behavior from the start, but
* this is what everyone else does.
*/
*retval = new;
FRELE(fp, p);
return (0);
}
if ((u_int)new >= lim_cur(RLIMIT_NOFILE) ||
(u_int)new >= maxfiles) {
FRELE(fp, p);
return (EBADF);
}
fdplock(fdp);
if (new >= fdp->fd_nfiles) {
if ((error = fdalloc(p, new, &i)) != 0) {
if (error == ENOSPC) {
fdexpand(p);
fdpunlock(fdp);
FRELE(fp, p);
goto restart;
}
fdpunlock(fdp);
FRELE(fp, p);
return (error);
}
if (new != i)
panic("dup2: fdalloc");
fd_unused(fdp, new);
}
dupflags = DUPF_DUP2;
if (flags & O_CLOEXEC)
dupflags |= DUPF_CLOEXEC;
/* No need for FRELE(), finishdup() uses current ref. */
return (finishdup(p, fp, old, new, retval, dupflags));
}
/*
* The file control system call.
*/
int
sys_fcntl(struct proc *p, void *v, register_t *retval)
{
struct sys_fcntl_args /* {
syscallarg(int) fd;
syscallarg(int) cmd;
syscallarg(void *) arg;
} */ *uap = v;
int fd = SCARG(uap, fd);
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct vnode *vp;
int i, prev, tmp, newmin, flg = F_POSIX;
struct flock fl;
int error = 0;
error = pledge_fcntl(p, SCARG(uap, cmd));
if (error)
return (error);
restart:
if ((fp = fd_getfile(fdp, fd)) == NULL)
return (EBADF);
switch (SCARG(uap, cmd)) {
case F_DUPFD:
case F_DUPFD_CLOEXEC:
newmin = (long)SCARG(uap, arg);
if ((u_int)newmin >= lim_cur(RLIMIT_NOFILE) ||
(u_int)newmin >= maxfiles) {
error = EINVAL;
break;
}
fdplock(fdp);
if ((error = fdalloc(p, newmin, &i)) != 0) {
if (error == ENOSPC) {
fdexpand(p);
fdpunlock(fdp);
FRELE(fp, p);
goto restart;
}
fdpunlock(fdp);
FRELE(fp, p);
} else {
int dupflags = 0;
if (SCARG(uap, cmd) == F_DUPFD_CLOEXEC)
dupflags |= DUPF_CLOEXEC;
/* No need for FRELE(), finishdup() uses current ref. */
error = finishdup(p, fp, fd, i, retval, dupflags);
}
return (error);
case F_GETFD:
fdplock(fdp);
*retval = fdp->fd_ofileflags[fd] & UF_EXCLOSE ? 1 : 0;
fdpunlock(fdp);
break;
case F_SETFD:
fdplock(fdp);
if ((long)SCARG(uap, arg) & 1)
fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
else
fdp->fd_ofileflags[fd] &= ~UF_EXCLOSE;
fdpunlock(fdp);
break;
case F_GETFL:
*retval = OFLAGS(fp->f_flag);
break;
case F_ISATTY:
vp = fp->f_data;
if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
*retval = 1;
else {
*retval = 0;
error = ENOTTY;
}
break;
case F_SETFL:
do {
tmp = prev = fp->f_flag;
tmp &= ~FCNTLFLAGS;
tmp |= FFLAGS((long)SCARG(uap, arg)) & FCNTLFLAGS;
} while (atomic_cas_uint(&fp->f_flag, prev, tmp) != prev);
tmp = fp->f_flag & FNONBLOCK;
error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
if (error)
break;
tmp = fp->f_flag & FASYNC;
error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (caddr_t)&tmp, p);
if (!error)
break;
atomic_clearbits_int(&fp->f_flag, FNONBLOCK);
tmp = 0;
(void) (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
break;
case F_GETOWN:
tmp = 0;
error = (*fp->f_ops->fo_ioctl)
(fp, FIOGETOWN, (caddr_t)&tmp, p);
*retval = tmp;
break;
case F_SETOWN:
tmp = (long)SCARG(uap, arg);
error = ((*fp->f_ops->fo_ioctl)
(fp, FIOSETOWN, (caddr_t)&tmp, p));
break;
case F_SETLKW:
flg |= F_WAIT;
/* FALLTHROUGH */
case F_SETLK:
error = pledge_flock(p);
if (error != 0)
break;
if (fp->f_type != DTYPE_VNODE) {
error = EINVAL;
break;
}
vp = fp->f_data;
/* Copy in the lock structure */
error = copyin((caddr_t)SCARG(uap, arg), (caddr_t)&fl,
sizeof (fl));
if (error)
break;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrflock(p, &fl);
#endif
if (fl.l_whence == SEEK_CUR) {
off_t offset = foffset(fp);
if (fl.l_start == 0 && fl.l_len < 0) {
/* lockf(3) compliance hack */
fl.l_len = -fl.l_len;
fl.l_start = offset - fl.l_len;
} else
fl.l_start += offset;
}
switch (fl.l_type) {
case F_RDLCK:
if ((fp->f_flag & FREAD) == 0) {
error = EBADF;
goto out;
}
atomic_setbits_int(&fdp->fd_flags, FD_ADVLOCK);
error = VOP_ADVLOCK(vp, fdp, F_SETLK, &fl, flg);
break;
case F_WRLCK:
if ((fp->f_flag & FWRITE) == 0) {
error = EBADF;
goto out;
}
atomic_setbits_int(&fdp->fd_flags, FD_ADVLOCK);
error = VOP_ADVLOCK(vp, fdp, F_SETLK, &fl, flg);
break;
case F_UNLCK:
error = VOP_ADVLOCK(vp, fdp, F_UNLCK, &fl, F_POSIX);
goto out;
default:
error = EINVAL;
goto out;
}
if (fd_checkclosed(fdp, fd, fp)) {
/*
* We have lost the race with close() or dup2();
* unlock, pretend that we've won the race and that
* lock had been removed by close()
*/
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
VOP_ADVLOCK(vp, fdp, F_UNLCK, &fl, F_POSIX);
fl.l_type = F_UNLCK;
}
goto out;
case F_GETLK:
error = pledge_flock(p);
if (error != 0)
break;
if (fp->f_type != DTYPE_VNODE) {
error = EINVAL;
break;
}
vp = fp->f_data;
/* Copy in the lock structure */
error = copyin((caddr_t)SCARG(uap, arg), (caddr_t)&fl,
sizeof (fl));
if (error)
break;
if (fl.l_whence == SEEK_CUR) {
off_t offset = foffset(fp);
if (fl.l_start == 0 && fl.l_len < 0) {
/* lockf(3) compliance hack */
fl.l_len = -fl.l_len;
fl.l_start = offset - fl.l_len;
} else
fl.l_start += offset;
}
if (fl.l_type != F_RDLCK &&
fl.l_type != F_WRLCK &&
fl.l_type != F_UNLCK &&
fl.l_type != 0) {
error = EINVAL;
break;
}
error = VOP_ADVLOCK(vp, fdp, F_GETLK, &fl, F_POSIX);
if (error)
break;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrflock(p, &fl);
#endif
error = (copyout((caddr_t)&fl, (caddr_t)SCARG(uap, arg),
sizeof (fl)));
break;
default:
error = EINVAL;
break;
}
out:
FRELE(fp, p);
return (error);
}
/*
* Common code for dup, dup2, and fcntl(F_DUPFD).
*/
int
finishdup(struct proc *p, struct file *fp, int old, int new,
register_t *retval, int dupflags)
{
struct file *oldfp;
struct filedesc *fdp = p->p_fd;
int error;
fdpassertlocked(fdp);
KASSERT(fp->f_iflags & FIF_INSERTED);
if (fp->f_count >= FDUP_MAX_COUNT) {
error = EDEADLK;
goto fail;
}
oldfp = fd_getfile(fdp, new);
if ((dupflags & DUPF_DUP2) && oldfp == NULL) {
if (fd_inuse(fdp, new)) {
error = EBUSY;
goto fail;
}
fd_used(fdp, new);
}
/*
* Use `fd_fplock' to synchronize with fd_getfile() so that
* the function no longer creates a new reference to the old file.
*/
mtx_enter(&fdp->fd_fplock);
fdp->fd_ofiles[new] = fp;
mtx_leave(&fdp->fd_fplock);
fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] & ~UF_EXCLOSE;
if (dupflags & DUPF_CLOEXEC)
fdp->fd_ofileflags[new] |= UF_EXCLOSE;
*retval = new;
if (oldfp != NULL) {
knote_fdclose(p, new);
fdpunlock(fdp);
closef(oldfp, p);
} else {
fdpunlock(fdp);
}
return (0);
fail:
fdpunlock(fdp);
FRELE(fp, p);
return (error);
}
void
fdinsert(struct filedesc *fdp, int fd, int flags, struct file *fp)
{
struct file *fq;
fdpassertlocked(fdp);
mtx_enter(&fhdlk);
if ((fp->f_iflags & FIF_INSERTED) == 0) {
atomic_setbits_int(&fp->f_iflags, FIF_INSERTED);
if ((fq = fdp->fd_ofiles[0]) != NULL) {
LIST_INSERT_AFTER(fq, fp, f_list);
} else {
LIST_INSERT_HEAD(&filehead, fp, f_list);
}
}
mtx_leave(&fhdlk);
mtx_enter(&fdp->fd_fplock);
KASSERT(fdp->fd_ofiles[fd] == NULL);
fdp->fd_ofiles[fd] = fp;
mtx_leave(&fdp->fd_fplock);
fdp->fd_ofileflags[fd] |= (flags & UF_EXCLOSE);
}
void
fdremove(struct filedesc *fdp, int fd)
{
fdpassertlocked(fdp);
/*
* Use `fd_fplock' to synchronize with fd_getfile() so that
* the function no longer creates a new reference to the file.
*/
mtx_enter(&fdp->fd_fplock);
fdp->fd_ofiles[fd] = NULL;
mtx_leave(&fdp->fd_fplock);
fdp->fd_ofileflags[fd] = 0;
fd_unused(fdp, fd);
}
int
fdrelease(struct proc *p, int fd)
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
fdpassertlocked(fdp);
fp = fd_getfile(fdp, fd);
if (fp == NULL) {
fdpunlock(fdp);
return (EBADF);
}
fdremove(fdp, fd);
knote_fdclose(p, fd);
fdpunlock(fdp);
return (closef(fp, p));
}
/*
* Close a file descriptor.
*/
int
sys_close(struct proc *p, void *v, register_t *retval)
{
struct sys_close_args /* {
syscallarg(int) fd;
} */ *uap = v;
int fd = SCARG(uap, fd), error;
struct filedesc *fdp = p->p_fd;
fdplock(fdp);
/* fdrelease unlocks fdp. */
error = fdrelease(p, fd);
return (error);
}
/*
* Return status information about a file descriptor.
*/
int
sys_fstat(struct proc *p, void *v, register_t *retval)
{
struct sys_fstat_args /* {
syscallarg(int) fd;
syscallarg(struct stat *) sb;
} */ *uap = v;
int fd = SCARG(uap, fd);
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct stat ub;
int error;
if ((fp = fd_getfile(fdp, fd)) == NULL)
return (EBADF);
error = (*fp->f_ops->fo_stat)(fp, &ub, p);
FRELE(fp, p);
if (error == 0) {
/*
* Don't let non-root see generation numbers
* (for NFS security)
*/
if (suser(p))
ub.st_gen = 0;
error = copyout((caddr_t)&ub, (caddr_t)SCARG(uap, sb),
sizeof (ub));
}
#ifdef KTRACE
if (error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrstat(p, &ub);
#endif
return (error);
}
/*
* Return pathconf information about a file descriptor.
*/
int
sys_fpathconf(struct proc *p, void *v, register_t *retval)
{
struct sys_fpathconf_args /* {
syscallarg(int) fd;
syscallarg(int) name;
} */ *uap = v;
int fd = SCARG(uap, fd);
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct vnode *vp;
int error;
if ((fp = fd_getfile(fdp, fd)) == NULL)
return (EBADF);
switch (fp->f_type) {
case DTYPE_PIPE:
case DTYPE_SOCKET:
if (SCARG(uap, name) != _PC_PIPE_BUF) {
error = EINVAL;
break;
}
*retval = PIPE_BUF;
error = 0;
break;
case DTYPE_VNODE:
vp = fp->f_data;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_PATHCONF(vp, SCARG(uap, name), retval);
VOP_UNLOCK(vp);
break;
default:
error = EOPNOTSUPP;
break;
}
FRELE(fp, p);
return (error);
}
/*
* Allocate a file descriptor for the process.
*/
int
fdalloc(struct proc *p, int want, int *result)
{
struct filedesc *fdp = p->p_fd;
int lim, last, i;
u_int new, off;
fdpassertlocked(fdp);
/*
* Search for a free descriptor starting at the higher
* of want or fd_freefile. If that fails, consider
* expanding the ofile array.
*/
restart:
lim = min((int)lim_cur(RLIMIT_NOFILE), maxfiles);
last = min(fdp->fd_nfiles, lim);
if ((i = want) < fdp->fd_freefile)
i = fdp->fd_freefile;
off = i >> NDENTRYSHIFT;
new = find_next_zero(fdp->fd_himap, off,
(last + NDENTRIES - 1) >> NDENTRYSHIFT);
if (new != -1) {
i = find_next_zero(&fdp->fd_lomap[new],
new > off ? 0 : i & NDENTRYMASK,
NDENTRIES);
if (i == -1) {
/*
* Free file descriptor in this block was
* below want, try again with higher want.
*/
want = (new + 1) << NDENTRYSHIFT;
goto restart;
}
i += (new << NDENTRYSHIFT);
if (i < last) {
fd_used(fdp, i);
if (want <= fdp->fd_freefile)
fdp->fd_freefile = i;
*result = i;
fdp->fd_ofileflags[i] = 0;
if (ISSET(p->p_p->ps_flags, PS_PLEDGE))
fdp->fd_ofileflags[i] |= UF_PLEDGED;
return (0);
}
}
if (fdp->fd_nfiles >= lim)
return (EMFILE);
return (ENOSPC);
}
void
fdexpand(struct proc *p)
{
struct filedesc *fdp = p->p_fd;
int nfiles, oldnfiles;
size_t copylen;
struct file **newofile, **oldofile;
char *newofileflags;
u_int *newhimap, *newlomap;
fdpassertlocked(fdp);
oldnfiles = fdp->fd_nfiles;
oldofile = fdp->fd_ofiles;
/*
* No space in current array.
*/
if (fdp->fd_nfiles < NDEXTENT)
nfiles = NDEXTENT;
else
nfiles = 2 * fdp->fd_nfiles;
newofile = mallocarray(nfiles, OFILESIZE, M_FILEDESC, M_WAITOK);
/*
* Allocate all required chunks before calling free(9) to make
* sure that ``fd_ofiles'' stays valid if we go to sleep.
*/
if (NDHISLOTS(nfiles) > NDHISLOTS(fdp->fd_nfiles)) {
newhimap = mallocarray(NDHISLOTS(nfiles), sizeof(u_int),
M_FILEDESC, M_WAITOK);
newlomap = mallocarray(NDLOSLOTS(nfiles), sizeof(u_int),
M_FILEDESC, M_WAITOK);
}
newofileflags = (char *) &newofile[nfiles];
/*
* Copy the existing ofile and ofileflags arrays
* and zero the new portion of each array.
*/
copylen = sizeof(struct file *) * fdp->fd_nfiles;
memcpy(newofile, fdp->fd_ofiles, copylen);
memset((char *)newofile + copylen, 0,
nfiles * sizeof(struct file *) - copylen);
copylen = sizeof(char) * fdp->fd_nfiles;
memcpy(newofileflags, fdp->fd_ofileflags, copylen);
memset(newofileflags + copylen, 0, nfiles * sizeof(char) - copylen);
if (NDHISLOTS(nfiles) > NDHISLOTS(fdp->fd_nfiles)) {
copylen = NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int);
memcpy(newhimap, fdp->fd_himap, copylen);
memset((char *)newhimap + copylen, 0,
NDHISLOTS(nfiles) * sizeof(u_int) - copylen);
copylen = NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int);
memcpy(newlomap, fdp->fd_lomap, copylen);
memset((char *)newlomap + copylen, 0,
NDLOSLOTS(nfiles) * sizeof(u_int) - copylen);
if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) {
free(fdp->fd_himap, M_FILEDESC,
NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int));
free(fdp->fd_lomap, M_FILEDESC,
NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int));
}
fdp->fd_himap = newhimap;
fdp->fd_lomap = newlomap;
}
mtx_enter(&fdp->fd_fplock);
fdp->fd_ofiles = newofile;
mtx_leave(&fdp->fd_fplock);
fdp->fd_ofileflags = newofileflags;
fdp->fd_nfiles = nfiles;
if (oldnfiles > NDFILE)
free(oldofile, M_FILEDESC, oldnfiles * OFILESIZE);
}
/*
* Create a new open file structure and allocate
* a file descriptor for the process that refers to it.
*/
int
falloc(struct proc *p, struct file **resultfp, int *resultfd)
{
struct file *fp;
int error, i;
KASSERT(resultfp != NULL);
KASSERT(resultfd != NULL);
fdpassertlocked(p->p_fd);
restart:
if ((error = fdalloc(p, 0, &i)) != 0) {
if (error == ENOSPC) {
fdexpand(p);
goto restart;
}
return (error);
}
fp = fnew(p);
if (fp == NULL) {
fd_unused(p->p_fd, i);
return (ENFILE);
}
FREF(fp);
*resultfp = fp;
*resultfd = i;
return (0);
}
struct file *
fnew(struct proc *p)
{
struct file *fp;
int nfiles;
nfiles = atomic_inc_int_nv(&numfiles);
if (nfiles > maxfiles) {
atomic_dec_int(&numfiles);
tablefull("file");
return (NULL);
}
fp = pool_get(&file_pool, PR_WAITOK|PR_ZERO);
/*
* We need to block interrupts as long as `f_mtx' is being taken
* with and without the KERNEL_LOCK().
*/
mtx_init(&fp->f_mtx, IPL_MPFLOOR);
fp->f_count = 1;
fp->f_cred = p->p_ucred;
crhold(fp->f_cred);
return (fp);
}
/*
* Build a new filedesc structure.
*/
struct filedesc *
fdinit(void)
{
struct filedesc0 *newfdp;
newfdp = pool_get(&fdesc_pool, PR_WAITOK|PR_ZERO);
rw_init(&newfdp->fd_fd.fd_lock, "fdlock");
mtx_init(&newfdp->fd_fd.fd_fplock, IPL_MPFLOOR);
LIST_INIT(&newfdp->fd_fd.fd_kqlist);
/* Create the file descriptor table. */
newfdp->fd_fd.fd_refcnt = 1;
newfdp->fd_fd.fd_cmask = S_IWGRP|S_IWOTH;
newfdp->fd_fd.fd_ofiles = newfdp->fd_dfiles;
newfdp->fd_fd.fd_ofileflags = newfdp->fd_dfileflags;
newfdp->fd_fd.fd_nfiles = NDFILE;
newfdp->fd_fd.fd_himap = newfdp->fd_dhimap;
newfdp->fd_fd.fd_lomap = newfdp->fd_dlomap;
newfdp->fd_fd.fd_freefile = 0;
newfdp->fd_fd.fd_lastfile = 0;
return (&newfdp->fd_fd);
}
/*
* Share a filedesc structure.
*/
struct filedesc *
fdshare(struct process *pr)
{
pr->ps_fd->fd_refcnt++;
return (pr->ps_fd);
}
/*
* Copy a filedesc structure.
*/
struct filedesc *
fdcopy(struct process *pr)
{
struct filedesc *newfdp, *fdp = pr->ps_fd;
int i;
newfdp = fdinit();
fdplock(fdp);
if (fdp->fd_cdir) {
vref(fdp->fd_cdir);
newfdp->fd_cdir = fdp->fd_cdir;
}
if (fdp->fd_rdir) {
vref(fdp->fd_rdir);
newfdp->fd_rdir = fdp->fd_rdir;
}
/*
* If the number of open files fits in the internal arrays
* of the open file structure, use them, otherwise allocate
* additional memory for the number of descriptors currently
* in use.
*/
if (fdp->fd_lastfile >= NDFILE) {
/*
* Compute the smallest multiple of NDEXTENT needed
* for the file descriptors currently in use,
* allowing the table to shrink.
*/
i = fdp->fd_nfiles;
while (i >= 2 * NDEXTENT && i > fdp->fd_lastfile * 2)
i /= 2;
newfdp->fd_ofiles = mallocarray(i, OFILESIZE, M_FILEDESC,
M_WAITOK | M_ZERO);
newfdp->fd_ofileflags = (char *) &newfdp->fd_ofiles[i];
newfdp->fd_nfiles = i;
}
if (NDHISLOTS(newfdp->fd_nfiles) > NDHISLOTS(NDFILE)) {
newfdp->fd_himap = mallocarray(NDHISLOTS(newfdp->fd_nfiles),
sizeof(u_int), M_FILEDESC, M_WAITOK | M_ZERO);
newfdp->fd_lomap = mallocarray(NDLOSLOTS(newfdp->fd_nfiles),
sizeof(u_int), M_FILEDESC, M_WAITOK | M_ZERO);
}
newfdp->fd_freefile = fdp->fd_freefile;
newfdp->fd_flags = fdp->fd_flags;
newfdp->fd_cmask = fdp->fd_cmask;
for (i = 0; i <= fdp->fd_lastfile; i++) {
struct file *fp = fdp->fd_ofiles[i];
if (fp != NULL) {
/*
* XXX Gruesome hack. If count gets too high, fail
* to copy an fd, since fdcopy()'s callers do not
* permit it to indicate failure yet.
* Meanwhile, kqueue files have to be
* tied to the process that opened them to enforce
* their internal consistency, so close them here.
*/
if (fp->f_count >= FDUP_MAX_COUNT ||
fp->f_type == DTYPE_KQUEUE) {
if (i < newfdp->fd_freefile)
newfdp->fd_freefile = i;
continue;
}
FREF(fp);
newfdp->fd_ofiles[i] = fp;
newfdp->fd_ofileflags[i] = fdp->fd_ofileflags[i];
fd_used(newfdp, i);
}
}
fdpunlock(fdp);
return (newfdp);
}
/*
* Release a filedesc structure.
*/
void
fdfree(struct proc *p)
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
int fd;
if (--fdp->fd_refcnt > 0)
return;
for (fd = 0; fd <= fdp->fd_lastfile; fd++) {
fp = fdp->fd_ofiles[fd];
if (fp != NULL) {
fdp->fd_ofiles[fd] = NULL;
knote_fdclose(p, fd);
/* closef() expects a refcount of 2 */
FREF(fp);
(void) closef(fp, p);
}
}
p->p_fd = NULL;
if (fdp->fd_nfiles > NDFILE)
free(fdp->fd_ofiles, M_FILEDESC, fdp->fd_nfiles * OFILESIZE);
if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) {
free(fdp->fd_himap, M_FILEDESC,
NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int));
free(fdp->fd_lomap, M_FILEDESC,
NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int));
}
if (fdp->fd_cdir)
vrele(fdp->fd_cdir);
if (fdp->fd_rdir)
vrele(fdp->fd_rdir);
pool_put(&fdesc_pool, fdp);
}
/*
* Internal form of close.
* Decrement reference count on file structure.
* Note: p may be NULL when closing a file
* that was being passed in a message.
*
* The fp must have its usecount bumped and will be FRELEd here.
*/
int
closef(struct file *fp, struct proc *p)
{
struct filedesc *fdp;
if (fp == NULL)
return (0);
KASSERTMSG(fp->f_count >= 2, "count (%u) < 2", fp->f_count);
atomic_dec_int(&fp->f_count);
/*
* POSIX record locking dictates that any close releases ALL
* locks owned by this process. This is handled by setting
* a flag in the unlock to free ONLY locks obeying POSIX
* semantics, and not to free BSD-style file locks.
* If the descriptor was in a message, POSIX-style locks
* aren't passed with the descriptor.
*/
if (p && ((fdp = p->p_fd) != NULL) &&
(fdp->fd_flags & FD_ADVLOCK) &&
fp->f_type == DTYPE_VNODE) {
struct vnode *vp = fp->f_data;
struct flock lf;
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_UNLCK;
(void) VOP_ADVLOCK(vp, fdp, F_UNLCK, &lf, F_POSIX);
}
return (FRELE(fp, p));
}
int
fdrop(struct file *fp, struct proc *p)
{
int error;
KASSERTMSG(fp->f_count == 0, "count (%u) != 0", fp->f_count);
mtx_enter(&fhdlk);
if (fp->f_iflags & FIF_INSERTED)
LIST_REMOVE(fp, f_list);
mtx_leave(&fhdlk);
if (fp->f_ops)
error = (*fp->f_ops->fo_close)(fp, p);
else
error = 0;
crfree(fp->f_cred);
atomic_dec_int(&numfiles);
pool_put(&file_pool, fp);
return (error);
}
/*
* Apply an advisory lock on a file descriptor.
*
* Just attempt to get a record lock of the requested type on
* the entire file (l_whence = SEEK_SET, l_start = 0, l_len = 0).
*/
int
sys_flock(struct proc *p, void *v, register_t *retval)
{
struct sys_flock_args /* {
syscallarg(int) fd;
syscallarg(int) how;
} */ *uap = v;
int fd = SCARG(uap, fd);
int how = SCARG(uap, how);
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct vnode *vp;
struct flock lf;
int error;
if ((fp = fd_getfile(fdp, fd)) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_VNODE) {
error = EOPNOTSUPP;
goto out;
}
vp = fp->f_data;
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
if (how & LOCK_UN) {
lf.l_type = F_UNLCK;
atomic_clearbits_int(&fp->f_iflags, FIF_HASLOCK);
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK);
goto out;
}
if (how & LOCK_EX)
lf.l_type = F_WRLCK;
else if (how & LOCK_SH)
lf.l_type = F_RDLCK;
else {
error = EINVAL;
goto out;
}
atomic_setbits_int(&fp->f_iflags, FIF_HASLOCK);
if (how & LOCK_NB)
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_FLOCK);
else
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_FLOCK|F_WAIT);
out:
FRELE(fp, p);
return (error);
}
/*
* File Descriptor pseudo-device driver (/dev/fd/).
*
* Opening minor device N dup()s the file (if any) connected to file
* descriptor N belonging to the calling process. Note that this driver
* consists of only the ``open()'' routine, because all subsequent
* references to this file will be direct to the other driver.
*/
int
filedescopen(dev_t dev, int mode, int type, struct proc *p)
{
/*
* XXX Kludge: set curproc->p_dupfd to contain the value of the
* the file descriptor being sought for duplication. The error
* return ensures that the vnode for this device will be released
* by vn_open. Open will detect this special error and take the
* actions in dupfdopen below. Other callers of vn_open or VOP_OPEN
* will simply report the error.
*/
p->p_dupfd = minor(dev);
return (ENODEV);
}
/*
* Duplicate the specified descriptor to a free descriptor.
*/
int
dupfdopen(struct proc *p, int indx, int mode)
{
struct filedesc *fdp = p->p_fd;
int dupfd = p->p_dupfd;
struct file *wfp;
fdpassertlocked(fdp);
/*
* Assume that the filename was user-specified; applications do
* not tend to open /dev/fd/# when they can just call dup()
*/
if ((p->p_p->ps_flags & (PS_SUGIDEXEC | PS_SUGID))) {
if (p->p_descfd == 255)
return (EPERM);
if (p->p_descfd != dupfd)
return (EPERM);
}
/*
* If the to-be-dup'd fd number is greater than the allowed number
* of file descriptors, or the fd to be dup'd has already been
* closed, reject. Note, there is no need to check for new == old
* because fd_getfile will return NULL if the file at indx is
* newly created by falloc.
*/
if ((wfp = fd_getfile(fdp, dupfd)) == NULL)
return (EBADF);
/*
* Check that the mode the file is being opened for is a
* subset of the mode of the existing descriptor.
*/
if (((mode & (FREAD|FWRITE)) | wfp->f_flag) != wfp->f_flag) {
FRELE(wfp, p);
return (EACCES);
}
if (wfp->f_count >= FDUP_MAX_COUNT) {
FRELE(wfp, p);
return (EDEADLK);
}
KASSERT(wfp->f_iflags & FIF_INSERTED);
mtx_enter(&fdp->fd_fplock);
KASSERT(fdp->fd_ofiles[indx] == NULL);
fdp->fd_ofiles[indx] = wfp;
mtx_leave(&fdp->fd_fplock);
fdp->fd_ofileflags[indx] = (fdp->fd_ofileflags[indx] & UF_EXCLOSE) |
(fdp->fd_ofileflags[dupfd] & ~UF_EXCLOSE);
return (0);
}
/*
* Close any files on exec?
*/
void
fdcloseexec(struct proc *p)
{
struct filedesc *fdp = p->p_fd;
int fd;
fdplock(fdp);
for (fd = 0; fd <= fdp->fd_lastfile; fd++) {
fdp->fd_ofileflags[fd] &= ~UF_PLEDGED;
if (fdp->fd_ofileflags[fd] & UF_EXCLOSE) {
/* fdrelease() unlocks fdp. */
(void) fdrelease(p, fd);
fdplock(fdp);
}
}
fdpunlock(fdp);
}
int
sys_closefrom(struct proc *p, void *v, register_t *retval)
{
struct sys_closefrom_args *uap = v;
struct filedesc *fdp = p->p_fd;
u_int startfd, i;
startfd = SCARG(uap, fd);
fdplock(fdp);
if (startfd > fdp->fd_lastfile) {
fdpunlock(fdp);
return (EBADF);
}
for (i = startfd; i <= fdp->fd_lastfile; i++) {
/* fdrelease() unlocks fdp. */
fdrelease(p, i);
fdplock(fdp);
}
fdpunlock(fdp);
return (0);
}
int
sys_getdtablecount(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_fd->fd_openfd;
return (0);
}
8
8
12
3
6
5
1
8
1
4
7
6
16
7
12
3
4
4
1
6
29
20
10
1
2
6
1
3
1
2
5
2
5
1
3
12
2
2
8
1
1
8
18
18
4
12
20
2
2
6
1
1
6
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
/* $OpenBSD: sysv_shm.c,v 1.80 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: sysv_shm.c,v 1.50 1998/10/21 22:24:29 tron Exp $ */
/*
* Copyright (c) 2002 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*
* Copyright (c) 1994 Adam Glass and Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Adam Glass and Charles M.
* Hannum.
* 4. The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/shm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/pool.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
extern struct shminfo shminfo;
struct shmid_ds **shmsegs; /* linear mapping of shmid -> shmseg */
struct pool shm_pool;
unsigned short *shmseqs; /* array of shm sequence numbers */
struct shmid_ds *shm_find_segment_by_shmid(int);
/*
* Provides the following externally accessible functions:
*
* shminit(void); initialization
* shmexit(struct vmspace *) cleanup
* shmfork(struct vmspace *, struct vmspace *) fork handling
* shmsys(arg1, arg2, arg3, arg4); shm{at,ctl,dt,get}(arg2, arg3, arg4)
*
* Structures:
* shmsegs (an array of 'struct shmid_ds *')
* per proc 'struct shmmap_head' with an array of 'struct shmmap_state'
*/
#define SHMSEG_REMOVED 0x0200 /* can't overlap ACCESSPERMS */
int shm_last_free, shm_nused, shm_committed;
struct shm_handle {
struct uvm_object *shm_object;
};
struct shmmap_state {
vaddr_t va;
int shmid;
};
struct shmmap_head {
int shmseg;
struct shmmap_state state[1];
};
int shm_find_segment_by_key(key_t);
void shm_deallocate_segment(struct shmid_ds *);
int shm_delete_mapping(struct vmspace *, struct shmmap_state *);
int shmget_existing(struct proc *, struct sys_shmget_args *,
int, int, register_t *);
int shmget_allocate_segment(struct proc *, struct sys_shmget_args *,
int, register_t *);
int
shm_find_segment_by_key(key_t key)
{
struct shmid_ds *shmseg;
int i;
for (i = 0; i < shminfo.shmmni; i++) {
shmseg = shmsegs[i];
if (shmseg != NULL && shmseg->shm_perm.key == key)
return (i);
}
return (-1);
}
struct shmid_ds *
shm_find_segment_by_shmid(int shmid)
{
int segnum;
struct shmid_ds *shmseg;
segnum = IPCID_TO_IX(shmid);
if (segnum < 0 || segnum >= shminfo.shmmni ||
(shmseg = shmsegs[segnum]) == NULL ||
shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid))
return (NULL);
return (shmseg);
}
void
shm_deallocate_segment(struct shmid_ds *shmseg)
{
struct shm_handle *shm_handle;
size_t size;
shm_handle = shmseg->shm_internal;
size = round_page(shmseg->shm_segsz);
uao_detach(shm_handle->shm_object);
pool_put(&shm_pool, shmseg);
shm_committed -= atop(size);
shm_nused--;
}
int
shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s)
{
struct shmid_ds *shmseg;
int segnum;
vaddr_t end;
segnum = IPCID_TO_IX(shmmap_s->shmid);
if (segnum < 0 || segnum >= shminfo.shmmni ||
(shmseg = shmsegs[segnum]) == NULL)
return (EINVAL);
end = round_page(shmmap_s->va+shmseg->shm_segsz);
uvm_unmap(&vm->vm_map, trunc_page(shmmap_s->va), end);
shmmap_s->shmid = -1;
shmseg->shm_dtime = gettime();
if ((--shmseg->shm_nattch <= 0) &&
(shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
shm_deallocate_segment(shmseg);
shm_last_free = segnum;
shmsegs[shm_last_free] = NULL;
}
return (0);
}
int
sys_shmdt(struct proc *p, void *v, register_t *retval)
{
struct sys_shmdt_args /* {
syscallarg(const void *) shmaddr;
} */ *uap = v;
struct shmmap_head *shmmap_h;
struct shmmap_state *shmmap_s;
int i;
shmmap_h = (struct shmmap_head *)p->p_vmspace->vm_shm;
if (shmmap_h == NULL)
return (EINVAL);
for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg;
i++, shmmap_s++)
if (shmmap_s->shmid != -1 &&
shmmap_s->va == (vaddr_t)SCARG(uap, shmaddr))
break;
if (i == shmmap_h->shmseg)
return (EINVAL);
return (shm_delete_mapping(p->p_vmspace, shmmap_s));
}
int
sys_shmat(struct proc *p, void *v, register_t *retval)
{
struct sys_shmat_args /* {
syscallarg(int) shmid;
syscallarg(const void *) shmaddr;
syscallarg(int) shmflg;
} */ *uap = v;
int error, i, flags = 0;
struct ucred *cred = p->p_ucred;
struct shmid_ds *shmseg;
struct shmmap_head *shmmap_h;
struct shmmap_state *shmmap_s;
struct shm_handle *shm_handle;
vaddr_t attach_va;
vm_prot_t prot;
vsize_t size;
shmmap_h = (struct shmmap_head *)p->p_vmspace->vm_shm;
if (shmmap_h == NULL) {
size = sizeof(int) +
shminfo.shmseg * sizeof(struct shmmap_state);
shmmap_h = malloc(size, M_SHM, M_WAITOK | M_CANFAIL);
if (shmmap_h == NULL)
return (ENOMEM);
shmmap_h->shmseg = shminfo.shmseg;
for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg;
i++, shmmap_s++)
shmmap_s->shmid = -1;
p->p_vmspace->vm_shm = (caddr_t)shmmap_h;
}
shmseg = shm_find_segment_by_shmid(SCARG(uap, shmid));
if (shmseg == NULL)
return (EINVAL);
error = ipcperm(cred, &shmseg->shm_perm,
(SCARG(uap, shmflg) & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
if (error)
return (error);
for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg; i++) {
if (shmmap_s->shmid == -1)
break;
shmmap_s++;
}
if (i >= shmmap_h->shmseg)
return (EMFILE);
size = round_page(shmseg->shm_segsz);
prot = PROT_READ;
if ((SCARG(uap, shmflg) & SHM_RDONLY) == 0)
prot |= PROT_WRITE;
if (SCARG(uap, shmaddr)) {
flags |= UVM_FLAG_FIXED;
if (SCARG(uap, shmflg) & SHM_RND)
attach_va =
(vaddr_t)SCARG(uap, shmaddr) & ~(SHMLBA-1);
else if (((vaddr_t)SCARG(uap, shmaddr) & (SHMLBA-1)) == 0)
attach_va = (vaddr_t)SCARG(uap, shmaddr);
else
return (EINVAL);
} else
attach_va = 0;
/*
* Since uvm_map() could end up sleeping, grab a reference to prevent
* the segment from being deallocated while sleeping.
*/
shmseg->shm_nattch++;
shm_handle = shmseg->shm_internal;
uao_reference(shm_handle->shm_object);
error = uvm_map(&p->p_vmspace->vm_map, &attach_va, size,
shm_handle->shm_object, 0, 0, UVM_MAPFLAG(prot, prot,
MAP_INHERIT_SHARE, MADV_RANDOM, flags));
if (error) {
if ((--shmseg->shm_nattch <= 0) &&
(shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
shm_deallocate_segment(shmseg);
shm_last_free = IPCID_TO_IX(SCARG(uap, shmid));
shmsegs[shm_last_free] = NULL;
} else {
uao_detach(shm_handle->shm_object);
}
return (error);
}
shmmap_s->va = attach_va;
shmmap_s->shmid = SCARG(uap, shmid);
shmseg->shm_lpid = p->p_p->ps_pid;
shmseg->shm_atime = gettime();
*retval = attach_va;
return (0);
}
int
sys_shmctl(struct proc *p, void *v, register_t *retval)
{
struct sys_shmctl_args /* {
syscallarg(int) shmid;
syscallarg(int) cmd;
syscallarg(struct shmid_ds *) buf;
} */ *uap = v;
int shmid = SCARG(uap, shmid);
int cmd = SCARG(uap, cmd);
void *buf = SCARG(uap, buf);
struct ucred *cred = p->p_ucred;
struct shmid_ds inbuf, *shmseg;
int error;
if (cmd == IPC_SET) {
error = copyin(buf, &inbuf, sizeof(inbuf));
if (error)
return (error);
}
shmseg = shm_find_segment_by_shmid(shmid);
if (shmseg == NULL)
return (EINVAL);
switch (cmd) {
case IPC_STAT:
if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0)
return (error);
error = copyout(shmseg, buf, sizeof(inbuf));
if (error)
return (error);
break;
case IPC_SET:
if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0)
return (error);
shmseg->shm_perm.uid = inbuf.shm_perm.uid;
shmseg->shm_perm.gid = inbuf.shm_perm.gid;
shmseg->shm_perm.mode =
(shmseg->shm_perm.mode & ~ACCESSPERMS) |
(inbuf.shm_perm.mode & ACCESSPERMS);
shmseg->shm_ctime = gettime();
break;
case IPC_RMID:
if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0)
return (error);
shmseg->shm_perm.key = IPC_PRIVATE;
shmseg->shm_perm.mode |= SHMSEG_REMOVED;
if (shmseg->shm_nattch <= 0) {
shm_deallocate_segment(shmseg);
shm_last_free = IPCID_TO_IX(shmid);
shmsegs[shm_last_free] = NULL;
}
break;
case SHM_LOCK:
case SHM_UNLOCK:
default:
return (EINVAL);
}
return (0);
}
int
shmget_existing(struct proc *p,
struct sys_shmget_args /* {
syscallarg(key_t) key;
syscallarg(size_t) size;
syscallarg(int) shmflg;
} */ *uap,
int mode, int segnum, register_t *retval)
{
struct shmid_ds *shmseg;
struct ucred *cred = p->p_ucred;
int error;
shmseg = shmsegs[segnum]; /* We assume the segnum is valid */
if ((error = ipcperm(cred, &shmseg->shm_perm, mode)) != 0)
return (error);
if (SCARG(uap, size) && SCARG(uap, size) > shmseg->shm_segsz)
return (EINVAL);
if ((SCARG(uap, shmflg) & (IPC_CREAT | IPC_EXCL)) ==
(IPC_CREAT | IPC_EXCL))
return (EEXIST);
*retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
return (0);
}
int
shmget_allocate_segment(struct proc *p,
struct sys_shmget_args /* {
syscallarg(key_t) key;
syscallarg(size_t) size;
syscallarg(int) shmflg;
} */ *uap,
int mode, register_t *retval)
{
size_t size;
key_t key;
int segnum;
struct ucred *cred = p->p_ucred;
struct shmid_ds *shmseg;
struct shm_handle *shm_handle;
int error = 0;
if (SCARG(uap, size) < shminfo.shmmin ||
SCARG(uap, size) > shminfo.shmmax)
return (EINVAL);
if (shm_nused >= shminfo.shmmni) /* any shmids left? */
return (ENOSPC);
size = round_page(SCARG(uap, size));
if (shm_committed + atop(size) > shminfo.shmall)
return (ENOMEM);
shm_nused++;
shm_committed += atop(size);
/*
* If a key has been specified and we had to wait for memory
* to be freed up we need to verify that no one has allocated
* the key we want in the meantime. Yes, this is ugly.
*/
key = SCARG(uap, key);
shmseg = pool_get(&shm_pool, key == IPC_PRIVATE ? PR_WAITOK :
PR_NOWAIT);
if (shmseg == NULL) {
shmseg = pool_get(&shm_pool, PR_WAITOK);
if (shm_find_segment_by_key(key) != -1) {
pool_put(&shm_pool, shmseg);
shm_nused--;
shm_committed -= atop(size);
return (EAGAIN);
}
}
/* XXX - hash shmids instead */
if (shm_last_free < 0) {
for (segnum = 0; segnum < shminfo.shmmni && shmsegs[segnum];
segnum++)
;
if (segnum == shminfo.shmmni)
panic("shmseg free count inconsistent");
} else {
segnum = shm_last_free;
if (++shm_last_free >= shminfo.shmmni || shmsegs[shm_last_free])
shm_last_free = -1;
}
shmsegs[segnum] = shmseg;
shm_handle = (struct shm_handle *)((caddr_t)shmseg + sizeof(*shmseg));
shm_handle->shm_object = uao_create(size, 0);
shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
shmseg->shm_perm.mode = (mode & ACCESSPERMS);
shmseg->shm_perm.seq = shmseqs[segnum] = (shmseqs[segnum] + 1) & 0x7fff;
shmseg->shm_perm.key = key;
shmseg->shm_segsz = SCARG(uap, size);
shmseg->shm_cpid = p->p_p->ps_pid;
shmseg->shm_lpid = shmseg->shm_nattch = 0;
shmseg->shm_atime = shmseg->shm_dtime = 0;
shmseg->shm_ctime = gettime();
shmseg->shm_internal = shm_handle;
*retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
return (error);
}
int
sys_shmget(struct proc *p, void *v, register_t *retval)
{
struct sys_shmget_args /* {
syscallarg(key_t) key;
syscallarg(size_t) size;
syscallarg(int) shmflg;
} */ *uap = v;
int segnum, mode, error;
mode = SCARG(uap, shmflg) & ACCESSPERMS;
if (SCARG(uap, key) != IPC_PRIVATE) {
again:
segnum = shm_find_segment_by_key(SCARG(uap, key));
if (segnum >= 0)
return (shmget_existing(p, uap, mode, segnum, retval));
if ((SCARG(uap, shmflg) & IPC_CREAT) == 0)
return (ENOENT);
}
error = shmget_allocate_segment(p, uap, mode, retval);
if (error == EAGAIN)
goto again;
return (error);
}
void
shmfork(struct vmspace *vm1, struct vmspace *vm2)
{
struct shmmap_head *shmmap_h;
struct shmmap_state *shmmap_s;
struct shmid_ds *shmseg;
size_t size;
int i;
if (vm1->vm_shm == NULL) {
vm2->vm_shm = NULL;
return;
}
shmmap_h = (struct shmmap_head *)vm1->vm_shm;
size = sizeof(int) + shmmap_h->shmseg * sizeof(struct shmmap_state);
vm2->vm_shm = malloc(size, M_SHM, M_WAITOK);
memcpy(vm2->vm_shm, vm1->vm_shm, size);
for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg;
i++, shmmap_s++) {
if (shmmap_s->shmid != -1 &&
(shmseg = shmsegs[IPCID_TO_IX(shmmap_s->shmid)]) != NULL)
shmseg->shm_nattch++;
}
}
void
shmexit(struct vmspace *vm)
{
struct shmmap_head *shmmap_h;
struct shmmap_state *shmmap_s;
size_t size;
int i;
shmmap_h = (struct shmmap_head *)vm->vm_shm;
if (shmmap_h == NULL)
return;
size = sizeof(int) + shmmap_h->shmseg * sizeof(struct shmmap_state);
for (i = 0, shmmap_s = shmmap_h->state; i < shmmap_h->shmseg;
i++, shmmap_s++)
if (shmmap_s->shmid != -1)
shm_delete_mapping(vm, shmmap_s);
free(vm->vm_shm, M_SHM, size);
vm->vm_shm = NULL;
}
void
shminit(void)
{
pool_init(&shm_pool,
sizeof(struct shmid_ds) + sizeof(struct shm_handle), 0,
IPL_NONE, PR_WAITOK, "shmpl", NULL);
shmsegs = mallocarray(shminfo.shmmni, sizeof(struct shmid_ds *),
M_SHM, M_WAITOK|M_ZERO);
shmseqs = mallocarray(shminfo.shmmni, sizeof(unsigned short),
M_SHM, M_WAITOK|M_ZERO);
shminfo.shmmax *= PAGE_SIZE; /* actually in pages */
shm_last_free = 0;
shm_nused = 0;
shm_committed = 0;
}
/* Expand shmsegs and shmseqs arrays */
void
shm_reallocate(int val)
{
struct shmid_ds **newsegs;
unsigned short *newseqs;
newsegs = mallocarray(val, sizeof(struct shmid_ds *),
M_SHM, M_WAITOK | M_ZERO);
memcpy(newsegs, shmsegs,
shminfo.shmmni * sizeof(struct shmid_ds *));
free(shmsegs, M_SHM,
shminfo.shmmni * sizeof(struct shmid_ds *));
shmsegs = newsegs;
newseqs = mallocarray(val, sizeof(unsigned short), M_SHM,
M_WAITOK | M_ZERO);
memcpy(newseqs, shmseqs,
shminfo.shmmni * sizeof(unsigned short));
free(shmseqs, M_SHM, shminfo.shmmni * sizeof(unsigned short));
shmseqs = newseqs;
shminfo.shmmni = val;
}
/*
* Userland access to struct shminfo.
*/
int
sysctl_sysvshm(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error, val;
if (namelen != 1)
return (ENOTDIR); /* leaf-only */
switch (name[0]) {
case KERN_SHMINFO_SHMMAX:
if ((error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&shminfo.shmmax, 0, INT_MAX)) || newp == NULL)
return (error);
/* If new shmmax > shmall, crank shmall */
if (atop(round_page(shminfo.shmmax)) > shminfo.shmall)
shminfo.shmall = atop(round_page(shminfo.shmmax));
return (0);
case KERN_SHMINFO_SHMMIN:
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&shminfo.shmmin, 1, INT_MAX));
case KERN_SHMINFO_SHMMNI:
val = shminfo.shmmni;
/* can't decrease shmmni */
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&val, val, 0xffff);
/* returns success and skips reallocation if val is unchanged */
if (error || val == shminfo.shmmni)
return (error);
shm_reallocate(val);
return (0);
case KERN_SHMINFO_SHMSEG:
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&shminfo.shmseg, 1, INT_MAX));
case KERN_SHMINFO_SHMALL:
/* can't decrease shmall */
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&shminfo.shmall, shminfo.shmall, INT_MAX));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
4
4
4
4
4
2
1
1
1
2
2
1
1
2
2
2
2
2
1
1
224
3
1
1
220
1
1
1
1
1
1
1
15
14
1
9
3
8
7
1
215
194
189
19
8
24
1
1
1
1
17
12
2
3
1
1
227
6
221
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
/* $OpenBSD: subr_disk.c,v 1.260 2022/09/03 15:29:43 kettenis Exp $ */
/* $NetBSD: subr_disk.c,v 1.17 1996/03/16 23:17:08 christos Exp $ */
/*
* Copyright (c) 1995 Jason R. Thorpe. All rights reserved.
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_disksubr.c 8.5 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/buf.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/time.h>
#include <sys/disklabel.h>
#include <sys/conf.h>
#include <sys/disk.h>
#include <sys/reboot.h>
#include <sys/dkio.h>
#include <sys/vnode.h>
#include <sys/task.h>
#include <sys/stdint.h>
#include <sys/socket.h>
#include <net/if.h>
#include <dev/cons.h>
#include <lib/libz/zlib.h>
#include "softraid.h"
#ifdef DEBUG
#define DPRINTF(x...) printf(x)
#else
#define DPRINTF(x...)
#endif
/*
* A global list of all disks attached to the system. May grow or
* shrink over time.
*/
struct disklist_head disklist; /* TAILQ_HEAD */
int disk_count; /* number of drives in global disklist */
int disk_change; /* set if a disk has been attached/detached
* since last we looked at this variable. This
* is reset by hw_sysctl()
*/
#define DUID_SIZE 8
u_char bootduid[DUID_SIZE]; /* DUID of boot disk. */
u_char rootduid[DUID_SIZE]; /* DUID of root disk. */
struct device *rootdv;
/* softraid callback, do not use! */
void (*softraid_disk_attach)(struct disk *, int);
void sr_map_root(void);
struct disk_attach_task {
struct task task;
struct disk *dk;
};
void disk_attach_callback(void *);
int spoofgpt(struct buf *, void (*)(struct buf *), const uint8_t *,
struct disklabel *, daddr_t *);
void spoofmbr(struct buf *, void (*)(struct buf *), const uint8_t *,
struct disklabel *, daddr_t *);
void spooffat(const uint8_t *, struct disklabel *, daddr_t *);
int gpt_chk_mbr(struct dos_partition *, uint64_t);
int gpt_get_hdr(struct buf *, void (*)(struct buf *), struct disklabel *,
uint64_t, struct gpt_header *);
int gpt_get_parts(struct buf *, void (*)(struct buf *),
struct disklabel *, const struct gpt_header *, struct gpt_partition **);
int gpt_get_fstype(struct uuid *);
int duid_equal(u_char *, u_char *);
/*
* Compute checksum for disk label.
*/
u_int
dkcksum(struct disklabel *lp)
{
u_int16_t *start, *end;
u_int16_t sum = 0;
start = (u_int16_t *)lp;
end = (u_int16_t *)&lp->d_partitions[lp->d_npartitions];
while (start < end)
sum ^= *start++;
return (sum);
}
int
initdisklabel(struct disklabel *lp)
{
int i;
/* minimal requirements for archetypal disk label */
if (lp->d_secsize < DEV_BSIZE)
lp->d_secsize = DEV_BSIZE;
if (DL_GETDSIZE(lp) == 0)
DL_SETDSIZE(lp, MAXDISKSIZE);
if (lp->d_secpercyl == 0)
return (ERANGE);
lp->d_npartitions = MAXPARTITIONS;
for (i = 0; i < RAW_PART; i++) {
DL_SETPSIZE(&lp->d_partitions[i], 0);
DL_SETPOFFSET(&lp->d_partitions[i], 0);
}
if (DL_GETPSIZE(&lp->d_partitions[RAW_PART]) == 0)
DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
DL_SETPOFFSET(&lp->d_partitions[RAW_PART], 0);
DL_SETBSTART(lp, 0);
DL_SETBEND(lp, DL_GETDSIZE(lp));
lp->d_version = 1;
return (0);
}
/*
* Check an incoming block to make sure it is a disklabel, convert it to
* a newer version if needed, etc etc.
*/
int
checkdisklabel(void *rlp, struct disklabel *lp, u_int64_t boundstart,
u_int64_t boundend)
{
struct disklabel *dlp = rlp;
struct __partitionv0 *v0pp;
struct partition *pp;
u_int64_t disksize;
int error = 0;
int i;
if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC)
error = ENOENT; /* no disk label */
else if (dlp->d_npartitions > MAXPARTITIONS)
error = E2BIG; /* too many partitions */
else if (dlp->d_secpercyl == 0)
error = EINVAL; /* invalid label */
else if (dlp->d_secsize == 0)
error = ENOSPC; /* disk too small */
else if (dkcksum(dlp) != 0)
error = EINVAL; /* incorrect checksum */
if (error) {
u_int16_t *start, *end, sum = 0;
/* If it is byte-swapped, attempt to convert it */
if (swap32(dlp->d_magic) != DISKMAGIC ||
swap32(dlp->d_magic2) != DISKMAGIC ||
swap16(dlp->d_npartitions) > MAXPARTITIONS)
return (error);
/*
* Need a byte-swap aware dkcksum variant
* inlined, because dkcksum uses a sub-field
*/
start = (u_int16_t *)dlp;
end = (u_int16_t *)&dlp->d_partitions[
swap16(dlp->d_npartitions)];
while (start < end)
sum ^= *start++;
if (sum != 0)
return (error);
dlp->d_magic = swap32(dlp->d_magic);
dlp->d_type = swap16(dlp->d_type);
/* d_typename and d_packname are strings */
dlp->d_secsize = swap32(dlp->d_secsize);
dlp->d_nsectors = swap32(dlp->d_nsectors);
dlp->d_ntracks = swap32(dlp->d_ntracks);
dlp->d_ncylinders = swap32(dlp->d_ncylinders);
dlp->d_secpercyl = swap32(dlp->d_secpercyl);
dlp->d_secperunit = swap32(dlp->d_secperunit);
/* d_uid is a string */
dlp->d_acylinders = swap32(dlp->d_acylinders);
dlp->d_flags = swap32(dlp->d_flags);
for (i = 0; i < NDDATA; i++)
dlp->d_drivedata[i] = swap32(dlp->d_drivedata[i]);
dlp->d_secperunith = swap16(dlp->d_secperunith);
dlp->d_version = swap16(dlp->d_version);
for (i = 0; i < NSPARE; i++)
dlp->d_spare[i] = swap32(dlp->d_spare[i]);
dlp->d_magic2 = swap32(dlp->d_magic2);
dlp->d_npartitions = swap16(dlp->d_npartitions);
for (i = 0; i < MAXPARTITIONS; i++) {
pp = &dlp->d_partitions[i];
pp->p_size = swap32(pp->p_size);
pp->p_offset = swap32(pp->p_offset);
if (dlp->d_version == 0) {
v0pp = (struct __partitionv0 *)pp;
v0pp->p_fsize = swap32(v0pp->p_fsize);
} else {
pp->p_offseth = swap16(pp->p_offseth);
pp->p_sizeh = swap16(pp->p_sizeh);
}
pp->p_cpg = swap16(pp->p_cpg);
}
dlp->d_checksum = 0;
dlp->d_checksum = dkcksum(dlp);
error = 0;
}
/* XXX should verify lots of other fields and whine a lot */
/* Initial passed in lp contains the real disk size. */
disksize = DL_GETDSIZE(lp);
if (lp != dlp)
*lp = *dlp;
if (lp->d_version == 0) {
lp->d_version = 1;
lp->d_secperunith = 0;
v0pp = (struct __partitionv0 *)lp->d_partitions;
pp = lp->d_partitions;
for (i = 0; i < lp->d_npartitions; i++, pp++, v0pp++) {
pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(v0pp->
p_fsize, v0pp->p_frag);
pp->p_offseth = 0;
pp->p_sizeh = 0;
}
}
#ifdef DEBUG
if (DL_GETDSIZE(lp) != disksize)
printf("on-disk disklabel has incorrect disksize (%llu)\n",
DL_GETDSIZE(lp));
if (DL_GETPSIZE(&lp->d_partitions[RAW_PART]) != disksize)
printf("on-disk disklabel RAW_PART has incorrect size (%llu)\n",
DL_GETPSIZE(&lp->d_partitions[RAW_PART]));
if (DL_GETPOFFSET(&lp->d_partitions[RAW_PART]) != 0)
printf("on-disk disklabel RAW_PART offset != 0 (%llu)\n",
DL_GETPOFFSET(&lp->d_partitions[RAW_PART]));
#endif
DL_SETDSIZE(lp, disksize);
DL_SETPSIZE(&lp->d_partitions[RAW_PART], disksize);
DL_SETPOFFSET(&lp->d_partitions[RAW_PART], 0);
DL_SETBSTART(lp, boundstart);
DL_SETBEND(lp, boundend < DL_GETDSIZE(lp) ? boundend : DL_GETDSIZE(lp));
lp->d_checksum = 0;
lp->d_checksum = dkcksum(lp);
return (0);
}
/*
* Read a disk sector.
*/
int
readdisksector(struct buf *bp, void (*strat)(struct buf *),
struct disklabel *lp, u_int64_t sector)
{
bp->b_blkno = DL_SECTOBLK(lp, sector);
bp->b_bcount = lp->d_secsize;
bp->b_error = 0;
CLR(bp->b_flags, B_READ | B_WRITE | B_DONE | B_ERROR);
SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
(*strat)(bp);
return (biowait(bp));
}
int
readdoslabel(struct buf *bp, void (*strat)(struct buf *), struct disklabel *lp,
daddr_t *partoffp, int spoofonly)
{
uint8_t dosbb[DEV_BSIZE];
struct disklabel nlp;
struct disklabel *rlp;
daddr_t partoff;
int error;
#ifdef DEBUG
char devname[32];
switch (major(bp->b_dev)) {
case 13:
case 4:
snprintf(devname, sizeof(devname), "sd%d",
minor(bp->b_dev) / MAXPARTITIONS);
break;
case 41:
case 14:
snprintf(devname, sizeof(devname), "vnd%d",
minor(bp->b_dev) / MAXPARTITIONS);
break;
default:
snprintf(devname, sizeof(devname), "<%d,%d>",
major(bp->b_dev), minor(bp->b_dev));
break;
}
printf("readdoslabel(new) enter: %s, spoofonly %d, partoffp %sNULL\n",
devname, spoofonly, (partoffp == NULL) ? "" : "not ");
#endif /* DEBUG */
error = readdisksector(bp, strat, lp, DOSBBSECTOR);
if (error) {
DPRINTF("readdoslabel(new) return: %s, %d -- lp unchanged, "
"DOSBBSECTOR read error\n", devname, error);
return error;
}
memcpy(dosbb, bp->b_data, sizeof(dosbb));
nlp = *lp;
memset(nlp.d_partitions, 0, sizeof(nlp.d_partitions));
nlp.d_partitions[RAW_PART] = lp->d_partitions[RAW_PART];
nlp.d_magic = 0;
error = spoofgpt(bp, strat, dosbb, &nlp, &partoff);
if (error)
return error;
if (nlp.d_magic != DISKMAGIC)
spoofmbr(bp, strat, dosbb, &nlp, &partoff);
if (nlp.d_magic != DISKMAGIC)
spooffat(dosbb, &nlp, &partoff);
if (nlp.d_magic != DISKMAGIC) {
DPRINTF("readdoslabel(new): N/A -- label partition @ "
"daddr_t 0 (default)\n");
partoff = 0;
}
if (partoffp != NULL) {
/*
* If a non-zero value is returned writedisklabel() exits with
* EIO. If 0 is returned the label sector is read from disk and
* lp is copied into it. So leave lp alone!
*/
if (partoff == -1) {
DPRINTF("readdoslabel(new) return: %s, ENXIO, lp "
"unchanged, *partoffp unchanged\n", devname);
return ENXIO;
}
*partoffp = partoff;
DPRINTF("readdoslabel(new) return: %s, 0, lp unchanged, "
"*partoffp set to %lld\n", devname, *partoffp);
return 0;
}
nlp.d_magic = lp->d_magic;
*lp = nlp;
lp->d_checksum = 0;
lp->d_checksum = dkcksum(lp);
if (spoofonly || partoff == -1) {
DPRINTF("readdoslabel(new) return: %s, 0, lp spoofed\n",
devname);
return 0;
}
partoff += DOS_LABELSECTOR;
error = readdisksector(bp, strat, lp, DL_BLKTOSEC(lp, partoff));
if (error) {
DPRINTF("readdoslabel(new) return: %s, %d, lp read failed\n",
devname, error);
return bp->b_error;
}
rlp = (struct disklabel *)(bp->b_data + DL_BLKOFFSET(lp, partoff));
error = checkdisklabel(rlp, lp, DL_GETBSTART(rlp), DL_GETBEND(rlp));
DPRINTF("readdoslabel(new) return: %s, %d, checkdisklabel() of daddr_t "
"%lld %s\n", devname, error, partoff, error ? "failed" : "ok");
return error;
}
/*
* Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
* partition exists.
*
* Copied into sbin/fdisk/mbr.c.
*/
int
gpt_chk_mbr(struct dos_partition *dp, uint64_t dsize)
{
struct dos_partition *dp2;
int efi, eficnt, found, i;
uint32_t psize;
found = efi = eficnt = 0;
for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
if (dp2->dp_typ == DOSPTYP_UNUSED)
continue;
found++;
if (dp2->dp_typ != DOSPTYP_EFI)
continue;
if (letoh32(dp2->dp_start) != GPTSECTOR)
continue;
psize = letoh32(dp2->dp_size);
if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
efi = i;
eficnt++;
}
}
if (found == 1 && eficnt == 1)
return (efi);
return (-1);
}
int
gpt_get_hdr(struct buf *bp, void (*strat)(struct buf *), struct disklabel *lp,
uint64_t sector, struct gpt_header *gh)
{
struct gpt_header ngh;
int error;
uint64_t lbaend, lbastart;
uint32_t csum;
uint32_t size, partsize;
error = readdisksector(bp, strat, lp, sector);
if (error)
return error;
memcpy(&ngh, bp->b_data, sizeof(ngh));
size = letoh32(ngh.gh_size);
partsize = letoh32(ngh.gh_part_size);
lbaend = letoh64(ngh.gh_lba_end);
lbastart = letoh64(ngh.gh_lba_start);
csum = ngh.gh_csum;
ngh.gh_csum = 0;
ngh.gh_csum = htole32(crc32(0, (unsigned char *)&ngh, GPTMINHDRSIZE));
if (letoh64(ngh.gh_sig) == GPTSIGNATURE &&
letoh32(ngh.gh_rev) == GPTREVISION &&
size == GPTMINHDRSIZE && lbastart <= lbaend &&
partsize == GPTMINPARTSIZE && lp->d_secsize % partsize == 0 &&
csum == ngh.gh_csum)
*gh = ngh;
else
memset(gh, 0, sizeof(*gh));
return 0;
}
int
gpt_get_parts(struct buf *bp, void (*strat)(struct buf *), struct disklabel *lp,
const struct gpt_header *gh, struct gpt_partition **gp)
{
uint8_t *ngp;
int error, i;
uint64_t bytes, partlba, sectors;
uint32_t partnum, partsize, partcsum;
partlba = letoh64(gh->gh_part_lba);
partnum = letoh32(gh->gh_part_num);
partsize = letoh32(gh->gh_part_size);
sectors = ((uint64_t)partnum * partsize + lp->d_secsize - 1) /
lp->d_secsize;
ngp = mallocarray(sectors, lp->d_secsize, M_DEVBUF, M_NOWAIT | M_ZERO);
if (ngp == NULL) {
*gp = NULL;
return ENOMEM;
}
bytes = sectors * lp->d_secsize;
for (i = 0; i < sectors; i++) {
error = readdisksector(bp, strat, lp, partlba + i);
if (error) {
free(ngp, M_DEVBUF, bytes);
*gp = NULL;
return error;
}
memcpy(ngp + i * lp->d_secsize, bp->b_data, lp->d_secsize);
}
partcsum = htole32(crc32(0, ngp, partnum * partsize));
if (partcsum != gh->gh_part_csum) {
DPRINTF("invalid %s GPT partition array @ %llu\n",
(letoh64(gh->gh_lba_self) == GPTSECTOR) ? "Primary" :
"Secondary", partlba);
free(ngp, M_DEVBUF, bytes);
*gp = NULL;
} else {
*gp = (struct gpt_partition *)ngp;
}
return 0;
}
int
gpt_get_fstype(struct uuid *uuid_part)
{
static int init = 0;
static struct uuid uuid_openbsd, uuid_msdos, uuid_chromefs,
uuid_linux, uuid_hfs, uuid_unused, uuid_efi_system, uuid_bios_boot;
static const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
static const uint8_t gpt_uuid_msdos[] = GPT_UUID_MSDOS;
static const uint8_t gpt_uuid_chromerootfs[] = GPT_UUID_CHROMEROOTFS;
static const uint8_t gpt_uuid_linux[] = GPT_UUID_LINUX;
static const uint8_t gpt_uuid_hfs[] = GPT_UUID_APPLE_HFS;
static const uint8_t gpt_uuid_unused[] = GPT_UUID_UNUSED;
static const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
static const uint8_t gpt_uuid_bios_boot[] = GPT_UUID_BIOS_BOOT;
if (init == 0) {
uuid_dec_be(gpt_uuid_openbsd, &uuid_openbsd);
uuid_dec_be(gpt_uuid_msdos, &uuid_msdos);
uuid_dec_be(gpt_uuid_chromerootfs, &uuid_chromefs);
uuid_dec_be(gpt_uuid_linux, &uuid_linux);
uuid_dec_be(gpt_uuid_hfs, &uuid_hfs);
uuid_dec_be(gpt_uuid_unused, &uuid_unused);
uuid_dec_be(gpt_uuid_efi_system, &uuid_efi_system);
uuid_dec_be(gpt_uuid_bios_boot, &uuid_bios_boot);
init = 1;
}
if (!memcmp(uuid_part, &uuid_unused, sizeof(struct uuid)))
return FS_UNUSED;
else if (!memcmp(uuid_part, &uuid_openbsd, sizeof(struct uuid)))
return FS_BSDFFS;
else if (!memcmp(uuid_part, &uuid_msdos, sizeof(struct uuid)))
return FS_MSDOS;
else if (!memcmp(uuid_part, &uuid_chromefs, sizeof(struct uuid)))
return FS_EXT2FS;
else if (!memcmp(uuid_part, &uuid_linux, sizeof(struct uuid)))
return FS_EXT2FS;
else if (!memcmp(uuid_part, &uuid_hfs, sizeof(struct uuid)))
return FS_HFS;
else if (!memcmp(uuid_part, &uuid_efi_system, sizeof(struct uuid)))
return FS_MSDOS;
else if (!memcmp(uuid_part, &uuid_bios_boot, sizeof(struct uuid)))
return FS_BOOT;
else
return FS_OTHER;
}
int
spoofgpt(struct buf *bp, void (*strat)(struct buf *), const uint8_t *dosbb,
struct disklabel *lp, daddr_t *partoffp)
{
struct dos_partition dp[NDOSPART];
struct gpt_header gh;
struct uuid gptype;
struct gpt_partition *gp;
struct partition *pp;
uint64_t lbaend, lbastart, labelsec;
uint64_t gpbytes, end, start;
daddr_t partoff;
unsigned int i, n;
int error, fstype, obsdfound;
uint32_t partnum;
uint16_t sig;
gp = NULL;
gpbytes = 0;
memcpy(dp, dosbb + DOSPARTOFF, sizeof(dp));
memcpy(&sig, dosbb + DOSMBR_SIGNATURE_OFF, sizeof(sig));
if (letoh16(sig) != DOSMBR_SIGNATURE ||
gpt_chk_mbr(dp, DL_GETDSIZE(lp)) == -1)
return 0;
error = gpt_get_hdr(bp, strat, lp, GPTSECTOR, &gh);
if (error == 0 && letoh64(gh.gh_sig) == GPTSIGNATURE)
error = gpt_get_parts(bp, strat, lp, &gh, &gp);
if (error || letoh64(gh.gh_sig) != GPTSIGNATURE || gp == NULL) {
error = gpt_get_hdr(bp, strat, lp, DL_GETDSIZE(lp) - 1, &gh);
if (error == 0 && letoh64(gh.gh_sig) == GPTSIGNATURE)
error = gpt_get_parts(bp, strat, lp, &gh, &gp);
}
if (error)
return error;
if (gp == NULL)
return ENXIO;
lbastart = letoh64(gh.gh_lba_start);
lbaend = letoh64(gh.gh_lba_end);
partnum = letoh32(gh.gh_part_num);
n = 'i' - 'a'; /* Start spoofing at 'i', a.k.a. 8. */
DL_SETBSTART(lp, lbastart);
DL_SETBEND(lp, lbaend + 1);
partoff = DL_SECTOBLK(lp, lbastart);
obsdfound = 0;
for (i = 0; i < partnum; i++) {
start = letoh64(gp[i].gp_lba_start);
if (start > lbaend || start < lbastart)
continue;
end = letoh64(gp[i].gp_lba_end);
if (start > end)
continue;
uuid_dec_le(&gp[i].gp_type, &gptype);
fstype = gpt_get_fstype(&gptype);
if (obsdfound && fstype == FS_BSDFFS)
continue;
if (fstype == FS_BSDFFS) {
obsdfound = 1;
partoff = DL_SECTOBLK(lp, start);
labelsec = DL_BLKTOSEC(lp, partoff + DOS_LABELSECTOR);
if (labelsec > ((end < lbaend) ? end : lbaend))
partoff = -1;
DL_SETBSTART(lp, start);
DL_SETBEND(lp, end + 1);
continue;
}
if (partoff != -1) {
labelsec = DL_BLKTOSEC(lp, partoff + DOS_LABELSECTOR);
if (labelsec >= start && labelsec <= end)
partoff = -1;
}
if (n < MAXPARTITIONS && end <= lbaend) {
pp = &lp->d_partitions[n];
n++;
pp->p_fstype = fstype;
DL_SETPOFFSET(pp, start);
DL_SETPSIZE(pp, end - start + 1);
}
}
lp->d_magic = DISKMAGIC;
*partoffp = partoff;
free(gp, M_DEVBUF, gpbytes);
#ifdef DEBUG
printf("readdoslabel(new): GPT -- ");
if (partoff == -1)
printf("no label partition\n");
else if (obsdfound == 0)
printf("label partition @ daddr_t %lld (free space)\n", partoff);
else
printf("label partition @ daddr_t %lld (A6)\n", partoff);
#endif /* DEBUG */
return 0;
}
void
spoofmbr(struct buf *bp, void (*strat)(struct buf *), const uint8_t *dosbb,
struct disklabel *lp, daddr_t *partoffp)
{
struct dos_partition dp[NDOSPART];
struct partition *pp;
uint64_t sector = DOSBBSECTOR;
uint64_t start, end;
daddr_t labeloff, partoff;
unsigned int i, n, parts;
int wander = 1, ebr = 0;
int error, obsdfound;
uint32_t extoff = 0;
uint16_t sig;
uint8_t fstype;
memcpy(&sig, dosbb + DOSMBR_SIGNATURE_OFF, sizeof(sig));
if (letoh16(sig) != DOSMBR_SIGNATURE)
return;
memcpy(dp, dosbb + DOSPARTOFF, sizeof(dp));
obsdfound = 0;
partoff = 0;
parts = 0;
n = 'i' - 'a';
while (wander && ebr < DOS_MAXEBR) {
ebr++;
wander = 0;
if (sector < extoff)
sector = extoff;
error = 0;
if (sector != DOSBBSECTOR) {
error = readdisksector(bp, strat, lp, sector);
if (error)
break;
memcpy(&sig, bp->b_data + DOSMBR_SIGNATURE_OFF,
sizeof(sig));
if (letoh16(sig) != DOSMBR_SIGNATURE)
break;
memcpy(dp, bp->b_data + DOSPARTOFF, sizeof(dp));
}
for (i = 0; i < NDOSPART; i++) {
if (letoh32(dp[i].dp_size) == 0)
continue;
if (obsdfound && dp[i].dp_typ == DOSPTYP_OPENBSD)
continue;
if (dp[i].dp_typ != DOSPTYP_OPENBSD) {
if (letoh32(dp[i].dp_start) > DL_GETDSIZE(lp))
continue;
if (letoh32(dp[i].dp_size) > DL_GETDSIZE(lp))
continue;
}
start = sector + letoh32(dp[i].dp_start);
end = start + letoh32(dp[i].dp_size);
parts++;
if (obsdfound == 0) {
labeloff = partoff + DOS_LABELSECTOR;
if (labeloff >= DL_SECTOBLK(lp, start) &&
labeloff < DL_SECTOBLK(lp, end))
partoff = -1;
}
switch (dp[i].dp_typ) {
case DOSPTYP_OPENBSD:
obsdfound = 1;
partoff = DL_SECTOBLK(lp, start);
labeloff = partoff + DOS_LABELSECTOR;
if (labeloff >= DL_SECTOBLK(lp, end))
partoff = -1;
DL_SETBSTART(lp, start);
DL_SETBEND(lp, end);
continue;
case DOSPTYP_EFI:
continue;
case DOSPTYP_EXTEND:
case DOSPTYP_EXTENDL:
sector = start + extoff;
if (extoff == 0) {
extoff = start;
sector = 0;
}
wander = 1;
continue;
case DOSPTYP_UNUSED:
fstype = FS_UNUSED;
break;
case DOSPTYP_LINUX:
fstype = FS_EXT2FS;
break;
case DOSPTYP_NTFS:
fstype = FS_NTFS;
break;
case DOSPTYP_EFISYS:
case DOSPTYP_FAT12:
case DOSPTYP_FAT16S:
case DOSPTYP_FAT16B:
case DOSPTYP_FAT16L:
case DOSPTYP_FAT32:
case DOSPTYP_FAT32L:
fstype = FS_MSDOS;
break;
default:
fstype = FS_OTHER;
break;
}
if (n < MAXPARTITIONS) {
pp = &lp->d_partitions[n++];
pp->p_fstype = fstype;
if (start)
DL_SETPOFFSET(pp, start);
DL_SETPSIZE(pp, end - start);
}
}
}
if (parts > 0) {
lp->d_magic = DISKMAGIC;
*partoffp = partoff;
#ifdef DEBUG
printf("readdoslabel(new): MBR -- ");
if (partoff == -1)
printf("no label partition\n");
else if (obsdfound == 0)
printf("label partition @ daddr_t %lld (free space)\n", partoff);
else
printf("label partition @ daddr_t %lld (A6)\n", partoff);
#endif /* DEBUG */
}
}
void
spooffat(const uint8_t *dosbb, struct disklabel *lp, daddr_t *partoffp)
{
uint16_t secsize;
#define VALID_JMP(_p) (((_p)[0] == 0xeb && (_p)[2] == 0x90) || (_p)[0] == 0xe9)
#define VALID_FAT(_p) ((_p)[16] == 1 || (_p)[16] == 2)
#define VALID_SEC(_s) ((_s) >= DEV_BSIZE && (_s) <= 4096 && ((_s) % 512 == 0))
memcpy(&secsize, dosbb + 11, sizeof(secsize));
secsize = letoh16(secsize);
if (VALID_JMP(dosbb) && VALID_SEC(secsize) && VALID_FAT(dosbb)) {
lp->d_partitions['i' - 'a'] = lp->d_partitions[RAW_PART];
lp->d_partitions['i' - 'a'].p_fstype = FS_MSDOS;
*partoffp = -1;
lp->d_magic = DISKMAGIC;
DPRINTF("readdoslabel(new): FAT -- no label partition\n");
}
}
/*
* Check new disk label for sensibility before setting it.
*/
int
setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_int openmask)
{
struct partition *opp, *npp;
struct disk *dk;
int i;
/* sanity clause */
if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 ||
(nlp->d_secsize % DEV_BSIZE) != 0)
return (EINVAL);
/* special case to allow disklabel to be invalidated */
if (nlp->d_magic == 0xffffffff) {
*olp = *nlp;
return (0);
}
if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
dkcksum(nlp) != 0)
return (EINVAL);
/* XXX missing check if other dos partitions will be overwritten */
for (i = 0; i < MAXPARTITIONS; i++) {
opp = &olp->d_partitions[i];
npp = &nlp->d_partitions[i];
if ((openmask & (1 << i)) &&
(DL_GETPOFFSET(npp) != DL_GETPOFFSET(opp) ||
DL_GETPSIZE(npp) < DL_GETPSIZE(opp)))
return (EBUSY);
/*
* Copy internally-set partition information
* if new label doesn't include it. XXX
*/
if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
npp->p_fragblock = opp->p_fragblock;
npp->p_cpg = opp->p_cpg;
}
}
/* Generate a UID if the disklabel does not already have one. */
if (duid_iszero(nlp->d_uid)) {
do {
arc4random_buf(nlp->d_uid, sizeof(nlp->d_uid));
TAILQ_FOREACH(dk, &disklist, dk_link)
if (dk->dk_label &&
duid_equal(dk->dk_label->d_uid, nlp->d_uid))
break;
} while (dk != NULL || duid_iszero(nlp->d_uid));
}
/* Preserve the disk size and RAW_PART values. */
DL_SETDSIZE(nlp, DL_GETDSIZE(olp));
npp = &nlp->d_partitions[RAW_PART];
DL_SETPOFFSET(npp, 0);
DL_SETPSIZE(npp, DL_GETDSIZE(nlp));
nlp->d_checksum = 0;
nlp->d_checksum = dkcksum(nlp);
*olp = *nlp;
disk_change = 1;
return (0);
}
/*
* Determine the size of the transfer, and make sure it is within the
* boundaries of the partition. Adjust transfer if needed, and signal errors or
* early completion.
*/
int
bounds_check_with_label(struct buf *bp, struct disklabel *lp)
{
struct partition *p = &lp->d_partitions[DISKPART(bp->b_dev)];
daddr_t partblocks, sz;
/* Avoid division by zero, negative offsets, and negative sizes. */
if (lp->d_secpercyl == 0 || bp->b_blkno < 0 || bp->b_bcount < 0)
goto bad;
/* Ensure transfer is a whole number of aligned sectors. */
if ((bp->b_blkno % DL_BLKSPERSEC(lp)) != 0 ||
(bp->b_bcount % lp->d_secsize) != 0)
goto bad;
/* Ensure transfer starts within partition boundary. */
partblocks = DL_SECTOBLK(lp, DL_GETPSIZE(p));
if (bp->b_blkno > partblocks)
goto bad;
/* If exactly at end of partition or null transfer, return EOF. */
if (bp->b_blkno == partblocks || bp->b_bcount == 0)
goto done;
/* Truncate request if it extends past the end of the partition. */
sz = bp->b_bcount >> DEV_BSHIFT;
if (sz > partblocks - bp->b_blkno) {
sz = partblocks - bp->b_blkno;
bp->b_bcount = sz << DEV_BSHIFT;
}
return (0);
bad:
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
done:
bp->b_resid = bp->b_bcount;
return (-1);
}
/*
* Disk error is the preface to plaintive error messages
* about failing disk transfers. It prints messages of the form
hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
* if the offset of the error in the transfer and a disk label
* are both available. blkdone should be -1 if the position of the error
* is unknown; the disklabel pointer may be null from drivers that have not
* been converted to use them. The message is printed with printf
* if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
* The message should be completed (with at least a newline) with printf
* or addlog, respectively. There is no trailing space.
*/
void
diskerr(struct buf *bp, char *dname, char *what, int pri, int blkdone,
struct disklabel *lp)
{
int unit = DISKUNIT(bp->b_dev), part = DISKPART(bp->b_dev);
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2)));
char partname = 'a' + part;
daddr_t sn;
if (pri != LOG_PRINTF) {
log(pri, "%s", "");
pr = addlog;
} else
pr = printf;
(*pr)("%s%d%c: %s %sing fsbn ", dname, unit, partname, what,
bp->b_flags & B_READ ? "read" : "writ");
sn = bp->b_blkno;
if (bp->b_bcount <= DEV_BSIZE)
(*pr)("%lld", (long long)sn);
else {
if (blkdone >= 0) {
sn += blkdone;
(*pr)("%lld of ", (long long)sn);
}
(*pr)("%lld-%lld", (long long)bp->b_blkno,
(long long)(bp->b_blkno + (bp->b_bcount - 1) / DEV_BSIZE));
}
if (lp && (blkdone >= 0 || bp->b_bcount <= lp->d_secsize)) {
sn += DL_SECTOBLK(lp, DL_GETPOFFSET(&lp->d_partitions[part]));
(*pr)(" (%s%d bn %lld; cn %lld", dname, unit, (long long)sn,
(long long)(sn / DL_SECTOBLK(lp, lp->d_secpercyl)));
sn %= DL_SECTOBLK(lp, lp->d_secpercyl);
(*pr)(" tn %lld sn %lld)",
(long long)(sn / DL_SECTOBLK(lp, lp->d_nsectors)),
(long long)(sn % DL_SECTOBLK(lp, lp->d_nsectors)));
}
}
/*
* Initialize the disklist. Called by main() before autoconfiguration.
*/
void
disk_init(void)
{
TAILQ_INIT(&disklist);
disk_count = disk_change = 0;
}
int
disk_construct(struct disk *diskp)
{
rw_init_flags(&diskp->dk_lock, "dklk", RWL_IS_VNODE);
mtx_init(&diskp->dk_mtx, IPL_BIO);
diskp->dk_flags |= DKF_CONSTRUCTED;
return (0);
}
/*
* Attach a disk.
*/
void
disk_attach(struct device *dv, struct disk *diskp)
{
int majdev;
KERNEL_ASSERT_LOCKED();
if (!ISSET(diskp->dk_flags, DKF_CONSTRUCTED))
disk_construct(diskp);
/*
* Allocate and initialize the disklabel structures. Note that
* it's not safe to sleep here, since we're probably going to be
* called during autoconfiguration.
*/
diskp->dk_label = malloc(sizeof(struct disklabel), M_DEVBUF,
M_NOWAIT|M_ZERO);
if (diskp->dk_label == NULL)
panic("disk_attach: can't allocate storage for disklabel");
/*
* Set the attached timestamp.
*/
microuptime(&diskp->dk_attachtime);
/*
* Link into the disklist.
*/
TAILQ_INSERT_TAIL(&disklist, diskp, dk_link);
++disk_count;
disk_change = 1;
/*
* Store device structure and number for later use.
*/
diskp->dk_device = dv;
diskp->dk_devno = NODEV;
if (dv != NULL) {
majdev = findblkmajor(dv);
if (majdev >= 0)
diskp->dk_devno =
MAKEDISKDEV(majdev, dv->dv_unit, RAW_PART);
if (diskp->dk_devno != NODEV) {
struct disk_attach_task *dat;
dat = malloc(sizeof(*dat), M_TEMP, M_WAITOK);
/* XXX: Assumes dk is part of the device softc. */
device_ref(dv);
dat->dk = diskp;
task_set(&dat->task, disk_attach_callback, dat);
task_add(systq, &dat->task);
}
}
if (softraid_disk_attach)
softraid_disk_attach(diskp, 1);
}
void
disk_attach_callback(void *xdat)
{
struct disk_attach_task *dat = xdat;
struct disk *dk = dat->dk;
struct disklabel dl;
char errbuf[100];
free(dat, M_TEMP, sizeof(*dat));
if (dk->dk_flags & (DKF_OPENED | DKF_NOLABELREAD))
goto done;
/* Read disklabel. */
if (disk_readlabel(&dl, dk->dk_devno, errbuf, sizeof(errbuf)) == NULL) {
enqueue_randomness(dl.d_checksum);
dk->dk_flags |= DKF_LABELVALID;
}
done:
dk->dk_flags |= DKF_OPENED;
device_unref(dk->dk_device);
wakeup(dk);
}
/*
* Detach a disk.
*/
void
disk_detach(struct disk *diskp)
{
KERNEL_ASSERT_LOCKED();
if (softraid_disk_attach)
softraid_disk_attach(diskp, -1);
/*
* Free the space used by the disklabel structures.
*/
free(diskp->dk_label, M_DEVBUF, sizeof(*diskp->dk_label));
/*
* Remove from the disklist.
*/
TAILQ_REMOVE(&disklist, diskp, dk_link);
disk_change = 1;
if (--disk_count < 0)
panic("disk_detach: disk_count < 0");
}
int
disk_openpart(struct disk *dk, int part, int fmt, int haslabel)
{
KASSERT(part >= 0 && part < MAXPARTITIONS);
/* Unless opening the raw partition, check that the partition exists. */
if (part != RAW_PART && (!haslabel ||
part >= dk->dk_label->d_npartitions ||
dk->dk_label->d_partitions[part].p_fstype == FS_UNUSED))
return (ENXIO);
/* Ensure the partition doesn't get changed under our feet. */
switch (fmt) {
case S_IFCHR:
dk->dk_copenmask |= (1 << part);
break;
case S_IFBLK:
dk->dk_bopenmask |= (1 << part);
break;
}
dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask;
return (0);
}
void
disk_closepart(struct disk *dk, int part, int fmt)
{
KASSERT(part >= 0 && part < MAXPARTITIONS);
switch (fmt) {
case S_IFCHR:
dk->dk_copenmask &= ~(1 << part);
break;
case S_IFBLK:
dk->dk_bopenmask &= ~(1 << part);
break;
}
dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask;
}
void
disk_gone(int (*open)(dev_t, int, int, struct proc *), int unit)
{
int bmaj, cmaj, mn;
/* Locate the lowest minor number to be detached. */
mn = DISKMINOR(unit, 0);
for (bmaj = 0; bmaj < nblkdev; bmaj++)
if (bdevsw[bmaj].d_open == open)
vdevgone(bmaj, mn, mn + MAXPARTITIONS - 1, VBLK);
for (cmaj = 0; cmaj < nchrdev; cmaj++)
if (cdevsw[cmaj].d_open == open)
vdevgone(cmaj, mn, mn + MAXPARTITIONS - 1, VCHR);
}
/*
* Increment a disk's busy counter. If the counter is going from
* 0 to 1, set the timestamp.
*/
void
disk_busy(struct disk *diskp)
{
/*
* XXX We'd like to use something as accurate as microtime(),
* but that doesn't depend on the system TOD clock.
*/
mtx_enter(&diskp->dk_mtx);
if (diskp->dk_busy++ == 0)
microuptime(&diskp->dk_timestamp);
mtx_leave(&diskp->dk_mtx);
}
/*
* Decrement a disk's busy counter, increment the byte count, total busy
* time, and reset the timestamp.
*/
void
disk_unbusy(struct disk *diskp, long bcount, daddr_t blkno, int read)
{
struct timeval dv_time, diff_time;
mtx_enter(&diskp->dk_mtx);
if (diskp->dk_busy-- == 0)
printf("disk_unbusy: %s: dk_busy < 0\n", diskp->dk_name);
microuptime(&dv_time);
timersub(&dv_time, &diskp->dk_timestamp, &diff_time);
timeradd(&diskp->dk_time, &diff_time, &diskp->dk_time);
diskp->dk_timestamp = dv_time;
if (bcount > 0) {
if (read) {
diskp->dk_rbytes += bcount;
diskp->dk_rxfer++;
} else {
diskp->dk_wbytes += bcount;
diskp->dk_wxfer++;
}
} else
diskp->dk_seek++;
mtx_leave(&diskp->dk_mtx);
enqueue_randomness(bcount ^ diff_time.tv_usec ^
(blkno >> 32) ^ (blkno & 0xffffffff));
}
int
disk_lock(struct disk *dk)
{
return (rw_enter(&dk->dk_lock, RW_WRITE|RW_INTR));
}
void
disk_lock_nointr(struct disk *dk)
{
rw_enter_write(&dk->dk_lock);
}
void
disk_unlock(struct disk *dk)
{
rw_exit_write(&dk->dk_lock);
}
int
dk_mountroot(void)
{
char errbuf[100];
int part = DISKPART(rootdev);
int (*mountrootfn)(void);
struct disklabel dl;
char *error;
error = disk_readlabel(&dl, rootdev, errbuf, sizeof(errbuf));
if (error)
panic("%s", error);
if (DL_GETPSIZE(&dl.d_partitions[part]) == 0)
panic("root filesystem has size 0");
switch (dl.d_partitions[part].p_fstype) {
#ifdef EXT2FS
case FS_EXT2FS:
{
extern int ext2fs_mountroot(void);
mountrootfn = ext2fs_mountroot;
}
break;
#endif
#ifdef FFS
case FS_BSDFFS:
{
extern int ffs_mountroot(void);
mountrootfn = ffs_mountroot;
}
break;
#endif
#ifdef CD9660
case FS_ISO9660:
{
extern int cd9660_mountroot(void);
mountrootfn = cd9660_mountroot;
}
break;
#endif
default:
#ifdef FFS
{
extern int ffs_mountroot(void);
printf("filesystem type %d not known.. assuming ffs\n",
dl.d_partitions[part].p_fstype);
mountrootfn = ffs_mountroot;
}
#else
panic("disk 0x%x filesystem type %d not known",
rootdev, dl.d_partitions[part].p_fstype);
#endif
}
return (*mountrootfn)();
}
struct device *
getdisk(char *str, int len, int defpart, dev_t *devp)
{
struct device *dv;
if ((dv = parsedisk(str, len, defpart, devp)) == NULL) {
printf("use one of: exit");
TAILQ_FOREACH(dv, &alldevs, dv_list) {
if (dv->dv_class == DV_DISK)
printf(" %s[a-p]", dv->dv_xname);
#if defined(NFSCLIENT)
if (dv->dv_class == DV_IFNET)
printf(" %s", dv->dv_xname);
#endif
}
printf("\n");
}
return (dv);
}
struct device *
parsedisk(char *str, int len, int defpart, dev_t *devp)
{
struct device *dv;
int majdev, part = defpart;
char c;
if (len == 0)
return (NULL);
c = str[len-1];
if (c >= 'a' && (c - 'a') < MAXPARTITIONS) {
part = c - 'a';
len -= 1;
}
TAILQ_FOREACH(dv, &alldevs, dv_list) {
if (dv->dv_class == DV_DISK &&
strncmp(str, dv->dv_xname, len) == 0 &&
dv->dv_xname[len] == '\0') {
majdev = findblkmajor(dv);
if (majdev < 0)
return NULL;
*devp = MAKEDISKDEV(majdev, dv->dv_unit, part);
break;
}
#if defined(NFSCLIENT)
if (dv->dv_class == DV_IFNET &&
strncmp(str, dv->dv_xname, len) == 0 &&
dv->dv_xname[len] == '\0') {
*devp = NODEV;
break;
}
#endif
}
return (dv);
}
void
setroot(struct device *bootdv, int part, int exitflags)
{
int majdev, unit, len, s, slept = 0;
struct swdevt *swp;
struct device *dv;
dev_t nrootdev, nswapdev = NODEV, temp = NODEV;
struct ifnet *ifp = NULL;
struct disk *dk;
char buf[128];
#if defined(NFSCLIENT)
extern char *nfsbootdevname;
#endif
/* Ensure that all disk attach callbacks have completed. */
do {
TAILQ_FOREACH(dk, &disklist, dk_link) {
if (dk->dk_devno != NODEV &&
(dk->dk_flags & DKF_OPENED) == 0) {
tsleep_nsec(dk, 0, "dkopen", SEC_TO_NSEC(1));
slept++;
break;
}
}
} while (dk != NULL && slept < 5);
if (slept == 5) {
printf("disklabels not read:");
TAILQ_FOREACH(dk, &disklist, dk_link)
if (dk->dk_devno != NODEV &&
(dk->dk_flags & DKF_OPENED) == 0)
printf(" %s", dk->dk_name);
printf("\n");
}
if (duid_iszero(bootduid)) {
/* Locate DUID for boot disk since it was not provided. */
TAILQ_FOREACH(dk, &disklist, dk_link)
if (dk->dk_device == bootdv)
break;
if (dk && (dk->dk_flags & DKF_LABELVALID))
bcopy(dk->dk_label->d_uid, bootduid, sizeof(bootduid));
} else if (bootdv == NULL) {
/* Locate boot disk based on the provided DUID. */
TAILQ_FOREACH(dk, &disklist, dk_link)
if (duid_equal(dk->dk_label->d_uid, bootduid))
break;
if (dk && (dk->dk_flags & DKF_LABELVALID))
bootdv = dk->dk_device;
}
bcopy(bootduid, rootduid, sizeof(rootduid));
#if NSOFTRAID > 0
sr_map_root();
#endif
/*
* If `swap generic' and we couldn't determine boot device,
* ask the user.
*/
dk = NULL;
if (mountroot == NULL && bootdv == NULL)
boothowto |= RB_ASKNAME;
if (boothowto & RB_ASKNAME) {
while (1) {
printf("root device");
if (bootdv != NULL) {
printf(" (default %s", bootdv->dv_xname);
if (bootdv->dv_class == DV_DISK)
printf("%c", 'a' + part);
printf(")");
}
printf(": ");
s = splhigh();
cnpollc(1);
len = getsn(buf, sizeof(buf));
cnpollc(0);
splx(s);
if (strcmp(buf, "exit") == 0)
reboot(exitflags);
if (len == 0 && bootdv != NULL) {
strlcpy(buf, bootdv->dv_xname, sizeof buf);
len = strlen(buf);
}
if (len > 0 && buf[len - 1] == '*') {
buf[--len] = '\0';
dv = getdisk(buf, len, part, &nrootdev);
if (dv != NULL) {
rootdv = dv;
nswapdev = nrootdev;
goto gotswap;
}
}
dv = getdisk(buf, len, part, &nrootdev);
if (dv != NULL) {
rootdv = dv;
break;
}
}
if (rootdv->dv_class == DV_IFNET)
goto gotswap;
/* try to build swap device out of new root device */
while (1) {
printf("swap device");
if (rootdv != NULL)
printf(" (default %s%s)", rootdv->dv_xname,
rootdv->dv_class == DV_DISK ? "b" : "");
printf(": ");
s = splhigh();
cnpollc(1);
len = getsn(buf, sizeof(buf));
cnpollc(0);
splx(s);
if (strcmp(buf, "exit") == 0)
reboot(exitflags);
if (len == 0 && rootdv != NULL) {
switch (rootdv->dv_class) {
case DV_IFNET:
nswapdev = NODEV;
break;
case DV_DISK:
nswapdev = MAKEDISKDEV(major(nrootdev),
DISKUNIT(nrootdev), 1);
if (nswapdev == nrootdev)
continue;
break;
default:
break;
}
break;
}
dv = getdisk(buf, len, 1, &nswapdev);
if (dv) {
if (dv->dv_class == DV_IFNET)
nswapdev = NODEV;
if (nswapdev == nrootdev)
continue;
break;
}
}
gotswap:
rootdev = nrootdev;
dumpdev = nswapdev;
swdevt[0].sw_dev = nswapdev;
swdevt[1].sw_dev = NODEV;
#if defined(NFSCLIENT)
} else if (mountroot == nfs_mountroot) {
rootdv = bootdv;
rootdev = dumpdev = swapdev = NODEV;
#endif
} else if (mountroot == NULL && rootdev == NODEV) {
/*
* `swap generic'
*/
rootdv = bootdv;
if (bootdv->dv_class == DV_DISK) {
if (!duid_iszero(rootduid)) {
TAILQ_FOREACH(dk, &disklist, dk_link)
if ((dk->dk_flags & DKF_LABELVALID) &&
dk->dk_label && duid_equal(
dk->dk_label->d_uid, rootduid))
break;
if (dk == NULL)
panic("root device (%s) not found",
duid_format(rootduid));
rootdv = dk->dk_device;
}
}
majdev = findblkmajor(rootdv);
if (majdev >= 0) {
/*
* Root and swap are on the disk.
* Assume swap is on partition b.
*/
rootdev = MAKEDISKDEV(majdev, rootdv->dv_unit, part);
nswapdev = MAKEDISKDEV(majdev, rootdv->dv_unit, 1);
} else {
/*
* Root and swap are on a net.
*/
nswapdev = NODEV;
}
dumpdev = nswapdev;
swdevt[0].sw_dev = nswapdev;
/* swdevt[1].sw_dev = NODEV; */
} else {
/* Completely pre-configured, but we want rootdv .. */
majdev = major(rootdev);
if (findblkname(majdev) == NULL)
return;
unit = DISKUNIT(rootdev);
part = DISKPART(rootdev);
snprintf(buf, sizeof buf, "%s%d%c",
findblkname(majdev), unit, 'a' + part);
rootdv = parsedisk(buf, strlen(buf), 0, &nrootdev);
if (rootdv == NULL)
panic("root device (%s) not found", buf);
}
if (bootdv != NULL && bootdv->dv_class == DV_IFNET)
ifp = if_unit(bootdv->dv_xname);
if (ifp) {
if_addgroup(ifp, "netboot");
if_put(ifp);
}
switch (rootdv->dv_class) {
#if defined(NFSCLIENT)
case DV_IFNET:
mountroot = nfs_mountroot;
nfsbootdevname = rootdv->dv_xname;
return;
#endif
case DV_DISK:
mountroot = dk_mountroot;
part = DISKPART(rootdev);
break;
default:
printf("can't figure root, hope your kernel is right\n");
return;
}
printf("root on %s%c", rootdv->dv_xname, 'a' + part);
if (dk && dk->dk_device == rootdv)
printf(" (%s.%c)", duid_format(rootduid), 'a' + part);
/*
* Make the swap partition on the root drive the primary swap.
*/
for (swp = swdevt; swp->sw_dev != NODEV; swp++) {
if (major(rootdev) == major(swp->sw_dev) &&
DISKUNIT(rootdev) == DISKUNIT(swp->sw_dev)) {
temp = swdevt[0].sw_dev;
swdevt[0].sw_dev = swp->sw_dev;
swp->sw_dev = temp;
break;
}
}
if (swp->sw_dev != NODEV) {
/*
* If dumpdev was the same as the old primary swap device,
* move it to the new primary swap device.
*/
if (temp == dumpdev)
dumpdev = swdevt[0].sw_dev;
}
if (swdevt[0].sw_dev != NODEV)
printf(" swap on %s%d%c", findblkname(major(swdevt[0].sw_dev)),
DISKUNIT(swdevt[0].sw_dev),
'a' + DISKPART(swdevt[0].sw_dev));
if (dumpdev != NODEV)
printf(" dump on %s%d%c", findblkname(major(dumpdev)),
DISKUNIT(dumpdev), 'a' + DISKPART(dumpdev));
printf("\n");
}
extern const struct nam2blk nam2blk[];
int
findblkmajor(struct device *dv)
{
char buf[16], *p;
int i;
if (strlcpy(buf, dv->dv_xname, sizeof buf) >= sizeof buf)
return (-1);
for (p = buf; *p; p++)
if (*p >= '0' && *p <= '9')
*p = '\0';
for (i = 0; nam2blk[i].name; i++)
if (!strcmp(buf, nam2blk[i].name))
return (nam2blk[i].maj);
return (-1);
}
char *
findblkname(int maj)
{
int i;
for (i = 0; nam2blk[i].name; i++)
if (nam2blk[i].maj == maj)
return (nam2blk[i].name);
return (NULL);
}
char *
disk_readlabel(struct disklabel *dl, dev_t dev, char *errbuf, size_t errsize)
{
struct vnode *vn;
dev_t chrdev, rawdev;
int error;
chrdev = blktochr(dev);
rawdev = MAKEDISKDEV(major(chrdev), DISKUNIT(chrdev), RAW_PART);
#ifdef DEBUG
printf("dev=0x%x chrdev=0x%x rawdev=0x%x\n", dev, chrdev, rawdev);
#endif
if (cdevvp(rawdev, &vn)) {
snprintf(errbuf, errsize,
"cannot obtain vnode for 0x%x/0x%x", dev, rawdev);
return (errbuf);
}
error = VOP_OPEN(vn, FREAD, NOCRED, curproc);
if (error) {
snprintf(errbuf, errsize,
"cannot open disk, 0x%x/0x%x, error %d",
dev, rawdev, error);
goto done;
}
error = VOP_IOCTL(vn, DIOCGDINFO, (caddr_t)dl, FREAD, NOCRED, curproc);
if (error) {
snprintf(errbuf, errsize,
"cannot read disk label, 0x%x/0x%x, error %d",
dev, rawdev, error);
}
done:
VOP_CLOSE(vn, FREAD, NOCRED, curproc);
vput(vn);
if (error)
return (errbuf);
return (NULL);
}
int
disk_map(char *path, char *mappath, int size, int flags)
{
struct disk *dk, *mdk;
u_char uid[8];
char c, part;
int i;
/*
* Attempt to map a request for a disklabel UID to the correct device.
* We should be supplied with a disklabel UID which has the following
* format:
*
* [disklabel uid] . [partition]
*
* Alternatively, if the DM_OPENPART flag is set the disklabel UID can
* based passed on its own.
*/
if (strchr(path, '/') != NULL)
return -1;
/* Verify that the device name is properly formed. */
if (!((strlen(path) == 16 && (flags & DM_OPENPART)) ||
(strlen(path) == 18 && path[16] == '.')))
return -1;
/* Get partition. */
if (flags & DM_OPENPART)
part = 'a' + RAW_PART;
else
part = path[17];
if (part < 'a' || part >= 'a' + MAXPARTITIONS)
return -1;
/* Derive label UID. */
memset(uid, 0, sizeof(uid));
for (i = 0; i < 16; i++) {
c = path[i];
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'a' && c <= 'f')
c -= ('a' - 10);
else
return -1;
uid[i / 2] <<= 4;
uid[i / 2] |= c & 0xf;
}
mdk = NULL;
TAILQ_FOREACH(dk, &disklist, dk_link) {
if ((dk->dk_flags & DKF_LABELVALID) && dk->dk_label &&
memcmp(dk->dk_label->d_uid, uid,
sizeof(dk->dk_label->d_uid)) == 0) {
/* Fail if there are duplicate UIDs! */
if (mdk != NULL)
return -1;
mdk = dk;
}
}
if (mdk == NULL || mdk->dk_name == NULL)
return -1;
snprintf(mappath, size, "/dev/%s%s%c",
(flags & DM_OPENBLCK) ? "" : "r", mdk->dk_name, part);
return 0;
}
/*
* Lookup a disk device and verify that it has completed attaching.
*/
struct device *
disk_lookup(struct cfdriver *cd, int unit)
{
struct device *dv;
struct disk *dk;
dv = device_lookup(cd, unit);
if (dv == NULL)
return (NULL);
TAILQ_FOREACH(dk, &disklist, dk_link)
if (dk->dk_device == dv)
break;
if (dk == NULL) {
device_unref(dv);
return (NULL);
}
return (dv);
}
int
duid_equal(u_char *duid1, u_char *duid2)
{
return (memcmp(duid1, duid2, DUID_SIZE) == 0);
}
int
duid_iszero(u_char *duid)
{
u_char zeroduid[DUID_SIZE];
memset(zeroduid, 0, sizeof(zeroduid));
return (duid_equal(duid, zeroduid));
}
const char *
duid_format(u_char *duid)
{
static char duid_str[17];
KERNEL_ASSERT_LOCKED();
snprintf(duid_str, sizeof(duid_str),
"%02x%02x%02x%02x%02x%02x%02x%02x",
duid[0], duid[1], duid[2], duid[3],
duid[4], duid[5], duid[6], duid[7]);
return (duid_str);
}
67
56
57
55
13
66
2
63
2
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/* $OpenBSD: pcppi.c,v 1.19 2022/04/06 18:59:28 naddy Exp $ */
/* $NetBSD: pcppi.c,v 1.1 1998/04/15 20:26:18 drochner Exp $ */
/*
* Copyright (c) 1996 Carnegie-Mellon University.
* All rights reserved.
*
* Author: Chris G. Demetriou
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/timeout.h>
#include <machine/bus.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
#include <dev/isa/pcppireg.h>
#include <dev/isa/pcppivar.h>
#include <dev/ic/i8253reg.h>
#include "pckbd.h"
#include "hidkbd.h"
#if NPCKBD > 0 || NHIDKBD > 0
#include <dev/ic/pckbcvar.h>
#include <dev/pckbc/pckbdvar.h>
#include <dev/hid/hidkbdvar.h>
void pcppi_kbd_bell(void *, u_int, u_int, u_int, int);
#endif
struct pcppi_softc {
struct device sc_dv;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ppi_ioh, sc_pit1_ioh;
struct timeout sc_bell_timeout;
int sc_bellactive, sc_bellpitch;
int sc_slp;
int sc_timeout;
};
int pcppi_match(struct device *, void *, void *);
void pcppi_attach(struct device *, struct device *, void *);
const struct cfattach pcppi_ca = {
sizeof(struct pcppi_softc), pcppi_match, pcppi_attach,
};
struct cfdriver pcppi_cd = {
NULL, "pcppi", DV_DULL
};
static void pcppi_bell_stop(void *);
#define PCPPIPRI (PZERO - 1)
int
pcppi_match(struct device *parent, void *match, void *aux)
{
struct isa_attach_args *ia = aux;
bus_space_handle_t ppi_ioh, pit1_ioh;
int have_pit1, have_ppi, rv;
u_int8_t v, nv;
/* If values are hardwired to something that they can't be, punt. */
if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_PPI) ||
ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 ||
ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK)
return (0);
rv = 0;
have_pit1 = have_ppi = 0;
if (bus_space_map(ia->ia_iot, IO_TIMER1, 4, 0, &pit1_ioh))
goto lose;
have_pit1 = 1;
if (bus_space_map(ia->ia_iot, IO_PPI, 1, 0, &ppi_ioh))
goto lose;
have_ppi = 1;
/*
* Check for existence of PPI. Realistically, this is either going to
* be here or nothing is going to be here.
*
* We don't want to have any chance of changing speaker output (which
* this test might, if it crashes in the middle, or something;
* normally it's too quick to produce anything audible), but
* many "combo chip" mock-PPI's don't seem to support the top bit
* of Port B as a settable bit. The bottom bit has to be settable,
* since the speaker driver hardware still uses it.
*/
v = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */
bus_space_write_1(ia->ia_iot, ppi_ioh, 0, v ^ 0x01); /* XXX */
nv = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */
if (((nv ^ v) & 0x01) == 0x01)
rv = 1;
bus_space_write_1(ia->ia_iot, ppi_ioh, 0, v); /* XXX */
nv = bus_space_read_1(ia->ia_iot, ppi_ioh, 0); /* XXX */
if (((nv ^ v) & 0x01) != 0x00) {
rv = 0;
goto lose;
}
/*
* We assume that the programmable interval timer is there.
*/
lose:
if (have_pit1)
bus_space_unmap(ia->ia_iot, pit1_ioh, 4);
if (have_ppi)
bus_space_unmap(ia->ia_iot, ppi_ioh, 1);
if (rv) {
ia->ia_iobase = IO_PPI;
ia->ia_iosize = 0x1;
ia->ia_msize = 0x0;
}
return (rv);
}
void
pcppi_attach(struct device *parent, struct device *self, void *aux)
{
struct pcppi_softc *sc = (struct pcppi_softc *)self;
struct isa_attach_args *ia = aux;
bus_space_tag_t iot;
struct pcppi_attach_args pa;
timeout_set(&sc->sc_bell_timeout, pcppi_bell_stop, sc);
sc->sc_iot = iot = ia->ia_iot;
if (bus_space_map(iot, IO_TIMER1, 4, 0, &sc->sc_pit1_ioh) ||
bus_space_map(iot, IO_PPI, 1, 0, &sc->sc_ppi_ioh))
panic("pcppi_attach: couldn't map");
printf("\n");
sc->sc_bellactive = sc->sc_bellpitch = sc->sc_slp = 0;
/* Provide a beeper for the keyboard, if there isn't one already. */
#if NPCKBD > 0
pckbd_hookup_bell(pcppi_kbd_bell, sc);
#endif
#if NHIDKBD > 0
hidkbd_hookup_bell(pcppi_kbd_bell, sc);
#endif
pa.pa_cookie = sc;
while (config_found(self, &pa, 0))
;
}
void
pcppi_bell(pcppi_tag_t self, int pitch, int period_ms, int slp)
{
struct pcppi_softc *sc = self;
int s1, s2;
if (pitch < 0)
pitch = 0;
else if (pitch > INT_MAX - TIMER_FREQ)
pitch = INT_MAX - TIMER_FREQ;
if (period_ms < 0)
period_ms = 0;
else if (period_ms > INT_MAX / 1000)
period_ms = INT_MAX / 1000;
s1 = spltty(); /* ??? */
if (sc->sc_bellactive) {
if (sc->sc_timeout) {
sc->sc_timeout = 0;
timeout_del(&sc->sc_bell_timeout);
}
if (sc->sc_slp)
wakeup(pcppi_bell_stop);
}
if (pitch == 0 || period_ms == 0) {
pcppi_bell_stop(sc);
sc->sc_bellpitch = 0;
splx(s1);
return;
}
if (!sc->sc_bellactive || sc->sc_bellpitch != pitch) {
s2 = splhigh();
bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_MODE,
TIMER_SEL2 | TIMER_16BIT | TIMER_SQWAVE);
bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_CNTR2,
TIMER_DIV(pitch) % 256);
bus_space_write_1(sc->sc_iot, sc->sc_pit1_ioh, TIMER_CNTR2,
TIMER_DIV(pitch) / 256);
splx(s2);
/* enable speaker */
bus_space_write_1(sc->sc_iot, sc->sc_ppi_ioh, 0,
bus_space_read_1(sc->sc_iot, sc->sc_ppi_ioh, 0)
| PIT_SPKR);
}
sc->sc_bellpitch = pitch;
sc->sc_bellactive = 1;
if (slp & PCPPI_BELL_POLL) {
delay(period_ms * 1000);
pcppi_bell_stop(sc);
} else {
sc->sc_timeout = 1;
timeout_add_msec(&sc->sc_bell_timeout, period_ms);
if (slp & PCPPI_BELL_SLEEP) {
sc->sc_slp = 1;
tsleep_nsec(pcppi_bell_stop, PCPPIPRI | PCATCH, "bell",
INFSLP);
sc->sc_slp = 0;
}
}
splx(s1);
}
static void
pcppi_bell_stop(void *arg)
{
struct pcppi_softc *sc = arg;
int s;
s = spltty(); /* ??? */
sc->sc_timeout = 0;
/* disable bell */
bus_space_write_1(sc->sc_iot, sc->sc_ppi_ioh, 0,
bus_space_read_1(sc->sc_iot, sc->sc_ppi_ioh, 0)
& ~PIT_SPKR);
sc->sc_bellactive = 0;
if (sc->sc_slp)
wakeup(pcppi_bell_stop);
splx(s);
}
#if NPCKBD > 0 || NHIDKBD > 0
void
pcppi_kbd_bell(void *arg, u_int pitch, u_int period, u_int volume, int poll)
{
/*
* NB: volume ignored.
*/
pcppi_bell(arg, volume ? pitch : 0, period,
poll ? PCPPI_BELL_POLL : 0);
}
#endif /* NPCKBD > 0 || NHIDKBD > 0 */
105
1
61
76
2
21
21
3
12
35
20
16
35
4
223
3
17
210
82
4
7
48
52
5
6
24
137
58
9
43
23
34
4
8
9
4
9
7
18
2
146
1
8
137
138
139
52
3
2
1
1
3
2
2
40
10
10
4
6
10
65
51
18
32
33
65
65
65
423
365
206
344
207
142
67
67
67
2
6
8
1
2
199
178
19
2
32
7
27
14
3
1
1
19
58
12
48
7
5
72
69
5
6
63
2
5
4
3
2
60
63
18
46
64
90
214
214
246
247
247
150
98
247
51
51
51
1
13
2
4
4
4
4
51
51
4
4
2
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
/* $OpenBSD: in_pcb.c,v 1.275 2022/09/03 22:43:38 mvs Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/mount.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/pfvar.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#ifdef IPSEC
#include <netinet/ip_esp.h>
#endif /* IPSEC */
#include "stoeplitz.h"
#if NSTOEPLITZ > 0
#include <net/toeplitz.h>
#endif
const struct in_addr zeroin_addr;
union {
struct in_addr za_in;
struct in6_addr za_in6;
} zeroin46_addr;
/*
* These configure the range of local port addresses assigned to
* "unspecified" outgoing connections/packets/whatever.
*/
int ipport_firstauto = IPPORT_RESERVED;
int ipport_lastauto = IPPORT_USERRESERVED;
int ipport_hifirstauto = IPPORT_HIFIRSTAUTO;
int ipport_hilastauto = IPPORT_HILASTAUTO;
struct baddynamicports baddynamicports;
struct baddynamicports rootonlyports;
struct pool inpcb_pool;
void in_pcbhash_insert(struct inpcb *);
struct inpcb *in_pcbhash_lookup(struct inpcbtable *, u_int,
const struct in_addr *, u_short, const struct in_addr *, u_short);
int in_pcbresize(struct inpcbtable *, int);
#define INPCBHASH_LOADFACTOR(_x) (((_x) * 3) / 4)
struct inpcbhead *in_pcbhash(struct inpcbtable *, u_int,
const struct in_addr *, u_short, const struct in_addr *, u_short);
struct inpcbhead *in_pcblhash(struct inpcbtable *, u_int, u_short);
/*
* in_pcb is used for inet and inet6. in6_pcb only contains special
* IPv6 cases. So the internet initializer is used for both domains.
*/
void
in_init(void)
{
pool_init(&inpcb_pool, sizeof(struct inpcb), 0,
IPL_SOFTNET, 0, "inpcb", NULL);
}
struct inpcbhead *
in_pcbhash(struct inpcbtable *table, u_int rdomain,
const struct in_addr *faddr, u_short fport,
const struct in_addr *laddr, u_short lport)
{
SIPHASH_CTX ctx;
u_int32_t nrdom = htonl(rdomain);
SipHash24_Init(&ctx, &table->inpt_key);
SipHash24_Update(&ctx, &nrdom, sizeof(nrdom));
SipHash24_Update(&ctx, faddr, sizeof(*faddr));
SipHash24_Update(&ctx, &fport, sizeof(fport));
SipHash24_Update(&ctx, laddr, sizeof(*laddr));
SipHash24_Update(&ctx, &lport, sizeof(lport));
return (&table->inpt_hashtbl[SipHash24_End(&ctx) & table->inpt_mask]);
}
struct inpcbhead *
in_pcblhash(struct inpcbtable *table, u_int rdomain, u_short lport)
{
SIPHASH_CTX ctx;
u_int32_t nrdom = htonl(rdomain);
SipHash24_Init(&ctx, &table->inpt_lkey);
SipHash24_Update(&ctx, &nrdom, sizeof(nrdom));
SipHash24_Update(&ctx, &lport, sizeof(lport));
return (&table->inpt_lhashtbl[SipHash24_End(&ctx) & table->inpt_lmask]);
}
void
in_pcbinit(struct inpcbtable *table, int hashsize)
{
mtx_init(&table->inpt_mtx, IPL_SOFTNET);
rw_init(&table->inpt_notify, "inpnotify");
TAILQ_INIT(&table->inpt_queue);
table->inpt_hashtbl = hashinit(hashsize, M_PCB, M_WAITOK,
&table->inpt_mask);
table->inpt_lhashtbl = hashinit(hashsize, M_PCB, M_WAITOK,
&table->inpt_lmask);
table->inpt_count = 0;
table->inpt_size = hashsize;
arc4random_buf(&table->inpt_key, sizeof(table->inpt_key));
arc4random_buf(&table->inpt_lkey, sizeof(table->inpt_lkey));
}
/*
* Check if the specified port is invalid for dynamic allocation.
*/
int
in_baddynamic(u_int16_t port, u_int16_t proto)
{
switch (proto) {
case IPPROTO_TCP:
return (DP_ISSET(baddynamicports.tcp, port));
case IPPROTO_UDP:
#ifdef IPSEC
/* Cannot preset this as it is a sysctl */
if (port == udpencap_port)
return (1);
#endif
return (DP_ISSET(baddynamicports.udp, port));
default:
return (0);
}
}
int
in_rootonly(u_int16_t port, u_int16_t proto)
{
switch (proto) {
case IPPROTO_TCP:
return (port < IPPORT_RESERVED ||
DP_ISSET(rootonlyports.tcp, port));
case IPPROTO_UDP:
return (port < IPPORT_RESERVED ||
DP_ISSET(rootonlyports.udp, port));
default:
return (0);
}
}
int
in_pcballoc(struct socket *so, struct inpcbtable *table)
{
struct inpcb *inp;
inp = pool_get(&inpcb_pool, PR_NOWAIT|PR_ZERO);
if (inp == NULL)
return (ENOBUFS);
inp->inp_table = table;
inp->inp_socket = so;
refcnt_init_trace(&inp->inp_refcnt, DT_REFCNT_IDX_INPCB);
mtx_init(&inp->inp_mtx, IPL_SOFTNET);
inp->inp_seclevel[SL_AUTH] = IPSEC_AUTH_LEVEL_DEFAULT;
inp->inp_seclevel[SL_ESP_TRANS] = IPSEC_ESP_TRANS_LEVEL_DEFAULT;
inp->inp_seclevel[SL_ESP_NETWORK] = IPSEC_ESP_NETWORK_LEVEL_DEFAULT;
inp->inp_seclevel[SL_IPCOMP] = IPSEC_IPCOMP_LEVEL_DEFAULT;
inp->inp_rtableid = curproc->p_p->ps_rtableid;
inp->inp_hops = -1;
#ifdef INET6
/*
* Small change in this function to set the INP_IPV6 flag so routines
* outside pcb-specific routines don't need to use sotopf(), and all
* of its pointer chasing, later.
*/
if (sotopf(so) == PF_INET6)
inp->inp_flags = INP_IPV6;
inp->inp_cksum6 = -1;
#endif /* INET6 */
mtx_enter(&table->inpt_mtx);
if (table->inpt_count++ > INPCBHASH_LOADFACTOR(table->inpt_size))
(void)in_pcbresize(table, table->inpt_size * 2);
TAILQ_INSERT_HEAD(&table->inpt_queue, inp, inp_queue);
in_pcbhash_insert(inp);
mtx_leave(&table->inpt_mtx);
so->so_pcb = inp;
return (0);
}
int
in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
{
struct socket *so = inp->inp_socket;
u_int16_t lport = 0;
int wild = 0;
void *laddr = &zeroin46_addr;
int error;
if (inp->inp_lport)
return (EINVAL);
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
(so->so_options & SO_ACCEPTCONN) == 0))
wild = INPLOOKUP_WILDCARD;
switch (sotopf(so)) {
#ifdef INET6
case PF_INET6:
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
return (EINVAL);
wild |= INPLOOKUP_IPV6;
if (nam) {
struct sockaddr_in6 *sin6;
if ((error = in6_nam2sin6(nam, &sin6)))
return (error);
if ((error = in6_pcbaddrisavail(inp, sin6, wild, p)))
return (error);
laddr = &sin6->sin6_addr;
lport = sin6->sin6_port;
}
break;
#endif
case PF_INET:
if (inp->inp_laddr.s_addr != INADDR_ANY)
return (EINVAL);
if (nam) {
struct sockaddr_in *sin;
if ((error = in_nam2sin(nam, &sin)))
return (error);
if ((error = in_pcbaddrisavail(inp, sin, wild, p)))
return (error);
laddr = &sin->sin_addr;
lport = sin->sin_port;
}
break;
default:
return (EINVAL);
}
if (lport == 0) {
if ((error = in_pcbpickport(&lport, laddr, wild, inp, p)))
return (error);
} else {
if (in_rootonly(ntohs(lport), so->so_proto->pr_protocol) &&
suser(p) != 0)
return (EACCES);
}
if (nam) {
switch (sotopf(so)) {
#ifdef INET6
case PF_INET6:
inp->inp_laddr6 = *(struct in6_addr *)laddr;
break;
#endif
case PF_INET:
inp->inp_laddr = *(struct in_addr *)laddr;
break;
}
}
inp->inp_lport = lport;
in_pcbrehash(inp);
return (0);
}
int
in_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in *sin, int wild,
struct proc *p)
{
struct socket *so = inp->inp_socket;
struct inpcbtable *table = inp->inp_table;
u_int16_t lport = sin->sin_port;
int reuseport = (so->so_options & SO_REUSEPORT);
if (IN_MULTICAST(sin->sin_addr.s_addr)) {
/*
* Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
* allow complete duplication of binding if
* SO_REUSEPORT is set, or if SO_REUSEADDR is set
* and a multicast address is bound on both
* new and duplicated sockets.
*/
if (so->so_options & (SO_REUSEADDR|SO_REUSEPORT))
reuseport = SO_REUSEADDR|SO_REUSEPORT;
} else if (sin->sin_addr.s_addr != INADDR_ANY) {
/*
* we must check that we are binding to an address we
* own except when:
* - SO_BINDANY is set or
* - we are binding a UDP socket to 255.255.255.255 or
* - we are binding a UDP socket to one of our broadcast
* addresses
*/
if (!ISSET(so->so_options, SO_BINDANY) &&
!(so->so_type == SOCK_DGRAM &&
sin->sin_addr.s_addr == INADDR_BROADCAST) &&
!(so->so_type == SOCK_DGRAM &&
in_broadcast(sin->sin_addr, inp->inp_rtableid))) {
struct ifaddr *ia;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
ia = ifa_ifwithaddr(sintosa(sin), inp->inp_rtableid);
sin->sin_port = lport;
if (ia == NULL)
return (EADDRNOTAVAIL);
}
}
if (lport) {
struct inpcb *t;
int error = 0;
if (so->so_euid && !IN_MULTICAST(sin->sin_addr.s_addr)) {
t = in_pcblookup_local(table, &sin->sin_addr, lport,
INPLOOKUP_WILDCARD, inp->inp_rtableid);
if (t && (so->so_euid != t->inp_socket->so_euid))
error = EADDRINUSE;
in_pcbunref(t);
if (error)
return (error);
}
t = in_pcblookup_local(table, &sin->sin_addr, lport,
wild, inp->inp_rtableid);
if (t && (reuseport & t->inp_socket->so_options) == 0)
error = EADDRINUSE;
in_pcbunref(t);
if (error)
return (error);
}
return (0);
}
int
in_pcbpickport(u_int16_t *lport, void *laddr, int wild, struct inpcb *inp,
struct proc *p)
{
struct socket *so = inp->inp_socket;
struct inpcbtable *table = inp->inp_table;
struct inpcb *t;
u_int16_t first, last, lower, higher, candidate, localport;
int count;
if (inp->inp_flags & INP_HIGHPORT) {
first = ipport_hifirstauto; /* sysctl */
last = ipport_hilastauto;
} else if (inp->inp_flags & INP_LOWPORT) {
if (suser(p))
return (EACCES);
first = IPPORT_RESERVED-1; /* 1023 */
last = 600; /* not IPPORT_RESERVED/2 */
} else {
first = ipport_firstauto; /* sysctl */
last = ipport_lastauto;
}
if (first < last) {
lower = first;
higher = last;
} else {
lower = last;
higher = first;
}
/*
* Simple check to ensure all ports are not used up causing
* a deadlock here.
*/
count = higher - lower;
candidate = lower + arc4random_uniform(count);
t = NULL;
do {
in_pcbunref(t);
do {
if (count-- < 0) /* completely used? */
return (EADDRNOTAVAIL);
++candidate;
if (candidate < lower || candidate > higher)
candidate = lower;
localport = htons(candidate);
} while (in_baddynamic(candidate, so->so_proto->pr_protocol));
t = in_pcblookup_local(table, laddr, localport, wild,
inp->inp_rtableid);
} while (t != NULL);
*lport = localport;
return (0);
}
/*
* Connect from a socket to a specified address.
* Both address and port must be specified in argument sin.
* If don't have a local address for this socket yet,
* then pick one.
*/
int
in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
{
struct in_addr ina;
struct sockaddr_in *sin;
struct inpcb *t;
int error;
#ifdef INET6
if (sotopf(inp->inp_socket) == PF_INET6)
return (in6_pcbconnect(inp, nam));
KASSERT((inp->inp_flags & INP_IPV6) == 0);
#endif /* INET6 */
if ((error = in_nam2sin(nam, &sin)))
return (error);
if (sin->sin_port == 0)
return (EADDRNOTAVAIL);
error = in_pcbselsrc(&ina, sin, inp);
if (error)
return (error);
t = in_pcblookup(inp->inp_table, sin->sin_addr, sin->sin_port,
ina, inp->inp_lport, inp->inp_rtableid);
if (t != NULL) {
in_pcbunref(t);
return (EADDRINUSE);
}
KASSERT(inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_lport);
if (inp->inp_laddr.s_addr == INADDR_ANY) {
if (inp->inp_lport == 0) {
error = in_pcbbind(inp, NULL, curproc);
if (error)
return (error);
t = in_pcblookup(inp->inp_table, sin->sin_addr,
sin->sin_port, ina, inp->inp_lport,
inp->inp_rtableid);
if (t != NULL) {
inp->inp_lport = 0;
in_pcbunref(t);
return (EADDRINUSE);
}
}
inp->inp_laddr = ina;
}
inp->inp_faddr = sin->sin_addr;
inp->inp_fport = sin->sin_port;
in_pcbrehash(inp);
#if NSTOEPLITZ > 0
inp->inp_flowid = stoeplitz_ip4port(inp->inp_faddr.s_addr,
inp->inp_laddr.s_addr, inp->inp_fport, inp->inp_lport);
#endif
return (0);
}
void
in_pcbdisconnect(struct inpcb *inp)
{
#if NPF > 0
if (inp->inp_pf_sk) {
pf_remove_divert_state(inp->inp_pf_sk);
/* pf_remove_divert_state() may have detached the state */
pf_inp_unlink(inp);
}
#endif
switch (sotopf(inp->inp_socket)) {
#ifdef INET6
case PF_INET6:
inp->inp_faddr6 = in6addr_any;
break;
#endif
case PF_INET:
inp->inp_faddr.s_addr = INADDR_ANY;
break;
}
inp->inp_fport = 0;
inp->inp_flowid = 0;
in_pcbrehash(inp);
if (inp->inp_socket->so_state & SS_NOFDREF)
in_pcbdetach(inp);
}
void
in_pcbdetach(struct inpcb *inp)
{
struct socket *so = inp->inp_socket;
struct inpcbtable *table = inp->inp_table;
so->so_pcb = NULL;
/*
* As long as the NET_LOCK() is the default lock for Internet
* sockets, do not release it to not introduce new sleeping
* points.
*/
sofree(so, 1);
m_freem(inp->inp_options);
if (inp->inp_route.ro_rt) {
rtfree(inp->inp_route.ro_rt);
inp->inp_route.ro_rt = NULL;
}
#ifdef INET6
if (inp->inp_flags & INP_IPV6) {
ip6_freepcbopts(inp->inp_outputopts6);
ip6_freemoptions(inp->inp_moptions6);
} else
#endif
ip_freemoptions(inp->inp_moptions);
#if NPF > 0
if (inp->inp_pf_sk) {
pf_remove_divert_state(inp->inp_pf_sk);
/* pf_remove_divert_state() may have detached the state */
pf_inp_unlink(inp);
}
#endif
mtx_enter(&table->inpt_mtx);
LIST_REMOVE(inp, inp_lhash);
LIST_REMOVE(inp, inp_hash);
TAILQ_REMOVE(&table->inpt_queue, inp, inp_queue);
table->inpt_count--;
mtx_leave(&table->inpt_mtx);
in_pcbunref(inp);
}
struct inpcb *
in_pcbref(struct inpcb *inp)
{
if (inp == NULL)
return NULL;
refcnt_take(&inp->inp_refcnt);
return inp;
}
void
in_pcbunref(struct inpcb *inp)
{
if (inp == NULL)
return;
if (refcnt_rele(&inp->inp_refcnt) == 0)
return;
KASSERT((LIST_NEXT(inp, inp_hash) == NULL) ||
(LIST_NEXT(inp, inp_hash) == _Q_INVALID));
KASSERT((LIST_NEXT(inp, inp_lhash) == NULL) ||
(LIST_NEXT(inp, inp_lhash) == _Q_INVALID));
KASSERT((TAILQ_NEXT(inp, inp_queue) == NULL) ||
(TAILQ_NEXT(inp, inp_queue) == _Q_INVALID));
pool_put(&inpcb_pool, inp);
}
void
in_setsockaddr(struct inpcb *inp, struct mbuf *nam)
{
struct sockaddr_in *sin;
nam->m_len = sizeof(*sin);
sin = mtod(nam, struct sockaddr_in *);
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_port = inp->inp_lport;
sin->sin_addr = inp->inp_laddr;
}
void
in_setpeeraddr(struct inpcb *inp, struct mbuf *nam)
{
struct sockaddr_in *sin;
#ifdef INET6
if (sotopf(inp->inp_socket) == PF_INET6) {
in6_setpeeraddr(inp, nam);
return;
}
#endif /* INET6 */
nam->m_len = sizeof(*sin);
sin = mtod(nam, struct sockaddr_in *);
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_port = inp->inp_fport;
sin->sin_addr = inp->inp_faddr;
}
int
in_sockaddr(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp;
inp = sotoinpcb(so);
in_setsockaddr(inp, nam);
return (0);
}
int
in_peeraddr(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp;
inp = sotoinpcb(so);
in_setpeeraddr(inp, nam);
return (0);
}
/*
* Pass some notification to all connections of a protocol
* associated with address dst. The "usual action" will be
* taken, depending on the ctlinput cmd. The caller must filter any
* cmds that are uninteresting (e.g., no error in the map).
* Call the protocol specific routine (if any) to report
* any errors for each matching socket.
*/
void
in_pcbnotifyall(struct inpcbtable *table, struct sockaddr *dst, u_int rtable,
int errno, void (*notify)(struct inpcb *, int))
{
SIMPLEQ_HEAD(, inpcb) inpcblist;
struct inpcb *inp;
struct in_addr faddr;
u_int rdomain;
if (dst->sa_family != AF_INET)
return;
faddr = satosin(dst)->sin_addr;
if (faddr.s_addr == INADDR_ANY)
return;
if (notify == NULL)
return;
/*
* Use a temporary notify list protected by rwlock to run over
* selected PCB. This is necessary as the list of all PCB is
* protected by a mutex. Notify may call ip_output() eventually
* which may sleep as pf lock is a rwlock. Also the SRP
* implementation of the routing table might sleep.
* The same inp_notify list entry and inpt_notify rwlock are
* used for UDP multicast and raw IP delivery.
*/
SIMPLEQ_INIT(&inpcblist);
rdomain = rtable_l2(rtable);
rw_enter_write(&table->inpt_notify);
mtx_enter(&table->inpt_mtx);
TAILQ_FOREACH(inp, &table->inpt_queue, inp_queue) {
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
continue;
#endif
if (inp->inp_faddr.s_addr != faddr.s_addr ||
rtable_l2(inp->inp_rtableid) != rdomain ||
inp->inp_socket == NULL) {
continue;
}
in_pcbref(inp);
SIMPLEQ_INSERT_TAIL(&inpcblist, inp, inp_notify);
}
mtx_leave(&table->inpt_mtx);
while ((inp = SIMPLEQ_FIRST(&inpcblist)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&inpcblist, inp_notify);
(*notify)(inp, errno);
in_pcbunref(inp);
}
rw_exit_write(&table->inpt_notify);
}
/*
* Check for alternatives when higher level complains
* about service problems. For now, invalidate cached
* routing information. If the route was created dynamically
* (by a redirect), time to try a default gateway again.
*/
void
in_losing(struct inpcb *inp)
{
struct rtentry *rt = inp->inp_route.ro_rt;
if (rt) {
inp->inp_route.ro_rt = NULL;
if (rt->rt_flags & RTF_DYNAMIC) {
struct ifnet *ifp;
ifp = if_get(rt->rt_ifidx);
/*
* If the interface is gone, all its attached
* route entries have been removed from the table,
* so we're dealing with a stale cache and have
* nothing to do.
*/
if (ifp != NULL)
rtdeletemsg(rt, ifp, inp->inp_rtableid);
if_put(ifp);
}
/*
* A new route can be allocated
* the next time output is attempted.
* rtfree() needs to be called in anycase because the inp
* is still holding a reference to rt.
*/
rtfree(rt);
}
}
/*
* After a routing change, flush old routing
* and allocate a (hopefully) better one.
*/
void
in_rtchange(struct inpcb *inp, int errno)
{
if (inp->inp_route.ro_rt) {
rtfree(inp->inp_route.ro_rt);
inp->inp_route.ro_rt = NULL;
/*
* A new route can be allocated the next time
* output is attempted.
*/
}
}
struct inpcb *
in_pcblookup_local(struct inpcbtable *table, void *laddrp, u_int lport_arg,
int flags, u_int rtable)
{
struct inpcb *inp, *match = NULL;
int matchwild = 3, wildcard;
u_int16_t lport = lport_arg;
struct in_addr laddr = *(struct in_addr *)laddrp;
#ifdef INET6
struct in6_addr *laddr6 = (struct in6_addr *)laddrp;
#endif
struct inpcbhead *head;
u_int rdomain;
rdomain = rtable_l2(rtable);
mtx_enter(&table->inpt_mtx);
head = in_pcblhash(table, rdomain, lport);
LIST_FOREACH(inp, head, inp_lhash) {
if (rtable_l2(inp->inp_rtableid) != rdomain)
continue;
if (inp->inp_lport != lport)
continue;
wildcard = 0;
#ifdef INET6
if (ISSET(flags, INPLOOKUP_IPV6)) {
if (!ISSET(inp->inp_flags, INP_IPV6))
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
wildcard++;
if (!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr6)) {
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) ||
IN6_IS_ADDR_UNSPECIFIED(laddr6))
wildcard++;
else
continue;
}
} else
#endif /* INET6 */
{
#ifdef INET6
if (ISSET(inp->inp_flags, INP_IPV6))
continue;
#endif /* INET6 */
if (inp->inp_faddr.s_addr != INADDR_ANY)
wildcard++;
if (inp->inp_laddr.s_addr != laddr.s_addr) {
if (inp->inp_laddr.s_addr == INADDR_ANY ||
laddr.s_addr == INADDR_ANY)
wildcard++;
else
continue;
}
}
if ((!wildcard || (flags & INPLOOKUP_WILDCARD)) &&
wildcard < matchwild) {
match = inp;
if ((matchwild = wildcard) == 0)
break;
}
}
in_pcbref(match);
mtx_leave(&table->inpt_mtx);
return (match);
}
struct rtentry *
in_pcbrtentry(struct inpcb *inp)
{
struct route *ro;
ro = &inp->inp_route;
/* check if route is still valid */
if (!rtisvalid(ro->ro_rt)) {
rtfree(ro->ro_rt);
ro->ro_rt = NULL;
}
/*
* No route yet, so try to acquire one.
*/
if (ro->ro_rt == NULL) {
#ifdef INET6
memset(ro, 0, sizeof(struct route_in6));
#else
memset(ro, 0, sizeof(struct route));
#endif
switch(sotopf(inp->inp_socket)) {
#ifdef INET6
case PF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
break;
ro->ro_dst.sa_family = AF_INET6;
ro->ro_dst.sa_len = sizeof(struct sockaddr_in6);
satosin6(&ro->ro_dst)->sin6_addr = inp->inp_faddr6;
ro->ro_tableid = inp->inp_rtableid;
ro->ro_rt = rtalloc_mpath(&ro->ro_dst,
&inp->inp_laddr6.s6_addr32[0], ro->ro_tableid);
break;
#endif /* INET6 */
case PF_INET:
if (inp->inp_faddr.s_addr == INADDR_ANY)
break;
ro->ro_dst.sa_family = AF_INET;
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
satosin(&ro->ro_dst)->sin_addr = inp->inp_faddr;
ro->ro_tableid = inp->inp_rtableid;
ro->ro_rt = rtalloc_mpath(&ro->ro_dst,
&inp->inp_laddr.s_addr, ro->ro_tableid);
break;
}
}
return (ro->ro_rt);
}
/*
* Return an IPv4 address, which is the most appropriate for a given
* destination.
* If necessary, this function lookups the routing table and returns
* an entry to the caller for later use.
*/
int
in_pcbselsrc(struct in_addr *insrc, struct sockaddr_in *sin,
struct inpcb *inp)
{
struct ip_moptions *mopts = inp->inp_moptions;
struct route *ro = &inp->inp_route;
struct in_addr *laddr = &inp->inp_laddr;
u_int rtableid = inp->inp_rtableid;
struct sockaddr *ip4_source = NULL;
struct sockaddr_in *sin2;
struct in_ifaddr *ia = NULL;
/*
* If the socket(if any) is already bound, use that bound address
* unless it is INADDR_ANY or INADDR_BROADCAST.
*/
if (laddr->s_addr != INADDR_ANY &&
laddr->s_addr != INADDR_BROADCAST) {
*insrc = *laddr;
return (0);
}
/*
* If the destination address is multicast or limited
* broadcast (255.255.255.255) and an outgoing interface has
* been set as a multicast option, use the address of that
* interface as our source address.
*/
if ((IN_MULTICAST(sin->sin_addr.s_addr) ||
sin->sin_addr.s_addr == INADDR_BROADCAST) && mopts != NULL) {
struct ifnet *ifp;
ifp = if_get(mopts->imo_ifidx);
if (ifp != NULL) {
if (ifp->if_rdomain == rtable_l2(rtableid))
IFP_TO_IA(ifp, ia);
if (ia == NULL) {
if_put(ifp);
return (EADDRNOTAVAIL);
}
*insrc = ia->ia_addr.sin_addr;
if_put(ifp);
return (0);
}
}
/*
* If route is known or can be allocated now,
* our src addr is taken from the i/f, else punt.
*/
if (!rtisvalid(ro->ro_rt) || (ro->ro_tableid != rtableid) ||
(satosin(&ro->ro_dst)->sin_addr.s_addr != sin->sin_addr.s_addr)) {
rtfree(ro->ro_rt);
ro->ro_rt = NULL;
}
if (ro->ro_rt == NULL) {
/* No route yet, so try to acquire one */
ro->ro_dst.sa_family = AF_INET;
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
satosin(&ro->ro_dst)->sin_addr = sin->sin_addr;
ro->ro_tableid = rtableid;
ro->ro_rt = rtalloc_mpath(&ro->ro_dst, NULL, ro->ro_tableid);
/*
* It is important to zero out the rest of the
* struct sockaddr_in when mixing v6 & v4!
*/
sin2 = satosin(&ro->ro_dst);
memset(sin2->sin_zero, 0, sizeof(sin2->sin_zero));
}
/*
* If we found a route, use the address
* corresponding to the outgoing interface.
*/
if (ro->ro_rt != NULL)
ia = ifatoia(ro->ro_rt->rt_ifa);
/*
* Use preferred source address if :
* - destination is not onlink
* - preferred source address is set
* - output interface is UP
*/
if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) &&
!(ro->ro_rt->rt_flags & RTF_HOST)) {
ip4_source = rtable_getsource(rtableid, AF_INET);
if (ip4_source != NULL) {
struct ifaddr *ifa;
if ((ifa = ifa_ifwithaddr(ip4_source, rtableid)) !=
NULL && ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) {
*insrc = satosin(ip4_source)->sin_addr;
return (0);
}
}
}
if (ia == NULL)
return (EADDRNOTAVAIL);
*insrc = ia->ia_addr.sin_addr;
return (0);
}
void
in_pcbrehash(struct inpcb *inp)
{
struct inpcbtable *table = inp->inp_table;
mtx_enter(&table->inpt_mtx);
LIST_REMOVE(inp, inp_lhash);
LIST_REMOVE(inp, inp_hash);
in_pcbhash_insert(inp);
mtx_leave(&table->inpt_mtx);
}
void
in_pcbhash_insert(struct inpcb *inp)
{
struct inpcbtable *table = inp->inp_table;
struct inpcbhead *head;
NET_ASSERT_LOCKED();
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
head = in_pcblhash(table, inp->inp_rtableid, inp->inp_lport);
LIST_INSERT_HEAD(head, inp, inp_lhash);
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
head = in6_pcbhash(table, rtable_l2(inp->inp_rtableid),
&inp->inp_faddr6, inp->inp_fport,
&inp->inp_laddr6, inp->inp_lport);
else
#endif /* INET6 */
head = in_pcbhash(table, rtable_l2(inp->inp_rtableid),
&inp->inp_faddr, inp->inp_fport,
&inp->inp_laddr, inp->inp_lport);
LIST_INSERT_HEAD(head, inp, inp_hash);
}
struct inpcb *
in_pcbhash_lookup(struct inpcbtable *table, u_int rdomain,
const struct in_addr *faddr, u_short fport,
const struct in_addr *laddr, u_short lport)
{
struct inpcbhead *head;
struct inpcb *inp;
NET_ASSERT_LOCKED();
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
head = in_pcbhash(table, rdomain, faddr, fport, laddr, lport);
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
if (ISSET(inp->inp_flags, INP_IPV6))
continue;
#endif
if (inp->inp_fport == fport && inp->inp_lport == lport &&
inp->inp_faddr.s_addr == faddr->s_addr &&
inp->inp_laddr.s_addr == laddr->s_addr &&
rtable_l2(inp->inp_rtableid) == rdomain) {
break;
}
}
if (inp != NULL) {
/*
* Move this PCB to the head of hash chain so that
* repeated accesses are quicker. This is analogous to
* the historic single-entry PCB cache.
*/
if (inp != LIST_FIRST(head)) {
LIST_REMOVE(inp, inp_hash);
LIST_INSERT_HEAD(head, inp, inp_hash);
}
}
return (inp);
}
int
in_pcbresize(struct inpcbtable *table, int hashsize)
{
u_long nmask, nlmask;
int osize;
void *nhashtbl, *nlhashtbl, *ohashtbl, *olhashtbl;
struct inpcb *inp;
MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
ohashtbl = table->inpt_hashtbl;
olhashtbl = table->inpt_lhashtbl;
osize = table->inpt_size;
nhashtbl = hashinit(hashsize, M_PCB, M_NOWAIT, &nmask);
if (nhashtbl == NULL)
return ENOBUFS;
nlhashtbl = hashinit(hashsize, M_PCB, M_NOWAIT, &nlmask);
if (nlhashtbl == NULL) {
hashfree(nhashtbl, hashsize, M_PCB);
return ENOBUFS;
}
table->inpt_hashtbl = nhashtbl;
table->inpt_lhashtbl = nlhashtbl;
table->inpt_mask = nmask;
table->inpt_lmask = nlmask;
table->inpt_size = hashsize;
arc4random_buf(&table->inpt_key, sizeof(table->inpt_key));
arc4random_buf(&table->inpt_lkey, sizeof(table->inpt_lkey));
TAILQ_FOREACH(inp, &table->inpt_queue, inp_queue) {
LIST_REMOVE(inp, inp_lhash);
LIST_REMOVE(inp, inp_hash);
in_pcbhash_insert(inp);
}
hashfree(ohashtbl, osize, M_PCB);
hashfree(olhashtbl, osize, M_PCB);
return (0);
}
#ifdef DIAGNOSTIC
int in_pcbnotifymiss = 0;
#endif
/*
* The in(6)_pcblookup functions are used to locate connected sockets
* quickly:
* faddr.fport <-> laddr.lport
* No wildcard matching is done so that listening sockets are not found.
* If the functions return NULL in(6)_pcblookup_listen can be used to
* find a listening/bound socket that may accept the connection.
* After those two lookups no other are necessary.
*/
struct inpcb *
in_pcblookup(struct inpcbtable *table, struct in_addr faddr,
u_int fport, struct in_addr laddr, u_int lport, u_int rtable)
{
struct inpcb *inp;
u_int rdomain;
rdomain = rtable_l2(rtable);
mtx_enter(&table->inpt_mtx);
inp = in_pcbhash_lookup(table, rdomain, &faddr, fport, &laddr, lport);
in_pcbref(inp);
mtx_leave(&table->inpt_mtx);
#ifdef DIAGNOSTIC
if (inp == NULL && in_pcbnotifymiss) {
printf("%s: faddr=%08x fport=%d laddr=%08x lport=%d rdom=%u\n",
__func__, ntohl(faddr.s_addr), ntohs(fport),
ntohl(laddr.s_addr), ntohs(lport), rdomain);
}
#endif
return (inp);
}
/*
* The in(6)_pcblookup_listen functions are used to locate listening
* sockets quickly. This are sockets with unspecified foreign address
* and port:
* *.* <-> laddr.lport
* *.* <-> *.lport
*/
struct inpcb *
in_pcblookup_listen(struct inpcbtable *table, struct in_addr laddr,
u_int lport_arg, struct mbuf *m, u_int rtable)
{
const struct in_addr *key1, *key2;
struct inpcb *inp;
u_int16_t lport = lport_arg;
u_int rdomain;
key1 = &laddr;
key2 = &zeroin_addr;
#if NPF > 0
if (m && m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
struct pf_divert *divert;
divert = pf_find_divert(m);
KASSERT(divert != NULL);
switch (divert->type) {
case PF_DIVERT_TO:
key1 = key2 = &divert->addr.v4;
lport = divert->port;
break;
case PF_DIVERT_REPLY:
return (NULL);
default:
panic("%s: unknown divert type %d, mbuf %p, divert %p",
__func__, divert->type, m, divert);
}
} else if (m && m->m_pkthdr.pf.flags & PF_TAG_TRANSLATE_LOCALHOST) {
/*
* Redirected connections should not be treated the same
* as connections directed to 127.0.0.0/8 since localhost
* can only be accessed from the host itself.
* For example portmap(8) grants more permissions for
* connections to the socket bound to 127.0.0.1 than
* to the * socket.
*/
key1 = &zeroin_addr;
key2 = &laddr;
}
#endif
rdomain = rtable_l2(rtable);
mtx_enter(&table->inpt_mtx);
inp = in_pcbhash_lookup(table, rdomain, &zeroin_addr, 0, key1, lport);
if (inp == NULL && key1->s_addr != key2->s_addr) {
inp = in_pcbhash_lookup(table, rdomain,
&zeroin_addr, 0, key2, lport);
}
in_pcbref(inp);
mtx_leave(&table->inpt_mtx);
#ifdef DIAGNOSTIC
if (inp == NULL && in_pcbnotifymiss) {
printf("%s: laddr=%08x lport=%d rdom=%u\n",
__func__, ntohl(laddr.s_addr), ntohs(lport), rdomain);
}
#endif
return (inp);
}
9016
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
/* $OpenBSD: proc.h,v 1.334 2022/07/23 22:10:59 cheloha Exp $ */
/* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */
/*-
* Copyright (c) 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)proc.h 8.8 (Berkeley) 1/21/94
*/
#ifndef _SYS_PROC_H_
#define _SYS_PROC_H_
#include <machine/proc.h> /* Machine-dependent proc substruct. */
#include <sys/selinfo.h> /* For struct selinfo */
#include <sys/syslimits.h> /* For LOGIN_NAME_MAX */
#include <sys/queue.h>
#include <sys/timeout.h> /* For struct timeout */
#include <sys/event.h> /* For struct klist */
#include <sys/mutex.h> /* For struct mutex */
#include <sys/resource.h> /* For struct rusage */
#include <sys/rwlock.h> /* For struct rwlock */
#include <sys/sigio.h> /* For struct sigio */
#ifdef _KERNEL
#include <sys/atomic.h>
#define __need_process
#endif
/*
* One structure allocated per session.
*/
struct process;
struct session {
int s_count; /* Ref cnt; pgrps in session. */
struct process *s_leader; /* Session leader. */
struct vnode *s_ttyvp; /* Vnode of controlling terminal. */
struct tty *s_ttyp; /* Controlling terminal. */
char s_login[LOGIN_NAME_MAX]; /* Setlogin() name. */
pid_t s_verauthppid;
uid_t s_verauthuid;
struct timeout s_verauthto;
};
void zapverauth(/* struct session */ void *);
/*
* One structure allocated per process group.
*/
struct pgrp {
LIST_ENTRY(pgrp) pg_hash; /* Hash chain. */
LIST_HEAD(, process) pg_members;/* Pointer to pgrp members. */
struct session *pg_session; /* Pointer to session. */
struct sigiolst pg_sigiolst; /* List of sigio structures. */
pid_t pg_id; /* Pgrp id. */
int pg_jobc; /* # procs qualifying pgrp for job control */
};
/*
* time usage: accumulated times in ticks
* Once a second, each thread's immediate counts (p_[usi]ticks) are
* accumulated into these.
*/
struct tusage {
struct timespec tu_runtime; /* Realtime. */
uint64_t tu_uticks; /* Statclock hits in user mode. */
uint64_t tu_sticks; /* Statclock hits in system mode. */
uint64_t tu_iticks; /* Statclock hits processing intr. */
};
/*
* Description of a process.
*
* These structures contain the information needed to manage a thread of
* control, known in UN*X as a process; it has references to substructures
* containing descriptions of things that the process uses, but may share
* with related processes.
*
* struct process is the higher level process containing information
* shared by all threads in a process, while struct proc contains the
* run-time information needed by threads.
*/
#ifdef __need_process
struct futex;
LIST_HEAD(futex_list, futex);
struct proc;
struct tslpentry;
TAILQ_HEAD(tslpqueue, tslpentry);
struct unveil;
/*
* Locks used to protect struct members in this file:
* I immutable after creation
* a atomic operations
* K kernel lock
* m this process' `ps_mtx'
* p this process' `ps_lock'
* R rlimit_lock
* S scheduler lock
* T itimer_mtx
*/
struct process {
/*
* ps_mainproc is the original thread in the process.
* It's only still special for the handling of
* some signal and ptrace behaviors that need to be fixed.
*/
struct proc *ps_mainproc;
struct ucred *ps_ucred; /* Process owner's identity. */
LIST_ENTRY(process) ps_list; /* List of all processes. */
TAILQ_HEAD(,proc) ps_threads; /* [K|S] Threads in this process. */
LIST_ENTRY(process) ps_pglist; /* List of processes in pgrp. */
struct process *ps_pptr; /* Pointer to parent process. */
LIST_ENTRY(process) ps_sibling; /* List of sibling processes. */
LIST_HEAD(, process) ps_children;/* Pointer to list of children. */
LIST_ENTRY(process) ps_hash; /* Hash chain. */
/*
* An orphan is the child that has been re-parented to the
* debugger as a result of attaching to it. Need to keep
* track of them for parent to be able to collect the exit
* status of what used to be children.
*/
LIST_ENTRY(process) ps_orphan; /* List of orphan processes. */
LIST_HEAD(, process) ps_orphans;/* Pointer to list of orphans. */
struct sigiolst ps_sigiolst; /* List of sigio structures. */
struct sigacts *ps_sigacts; /* [I] Signal actions, state */
struct vnode *ps_textvp; /* Vnode of executable. */
struct filedesc *ps_fd; /* Ptr to open files structure */
struct vmspace *ps_vmspace; /* Address space */
pid_t ps_pid; /* Process identifier. */
struct futex_list ps_ftlist; /* futexes attached to this process */
struct tslpqueue ps_tslpqueue; /* [p] queue of threads in thrsleep */
struct rwlock ps_lock; /* per-process rwlock */
struct mutex ps_mtx; /* per-process mutex */
/* The following fields are all zeroed upon creation in process_new. */
#define ps_startzero ps_klist
struct klist ps_klist; /* knotes attached to this process */
u_int ps_flags; /* [a] PS_* flags. */
int ps_siglist; /* Signals pending for the process. */
struct proc *ps_single; /* [S] Thread for single-threading. */
u_int ps_singlecount; /* [a] Not yet suspended threads. */
int ps_traceflag; /* Kernel trace points. */
struct vnode *ps_tracevp; /* Trace to vnode. */
struct ucred *ps_tracecred; /* Creds for writing trace */
u_int ps_xexit; /* Exit status for wait */
int ps_xsig; /* Stopping or killing signal */
pid_t ps_ppid; /* [a] Cached parent pid */
pid_t ps_oppid; /* [a] Save parent pid during ptrace. */
int ps_ptmask; /* Ptrace event mask */
struct ptrace_state *ps_ptstat;/* Ptrace state */
struct rusage *ps_ru; /* sum of stats for dead threads. */
struct tusage ps_tu; /* accumulated times. */
struct rusage ps_cru; /* sum of stats for reaped children */
struct itimerspec ps_timer[3]; /* [m] ITIMER_REAL timer */
/* [T] ITIMER_{VIRTUAL,PROF} timers */
struct timeout ps_rucheck_to; /* [] resource limit check timer */
time_t ps_nextxcpu; /* when to send next SIGXCPU, */
/* in seconds of process runtime */
u_int64_t ps_wxcounter;
struct unveil *ps_uvpaths; /* unveil vnodes and names */
ssize_t ps_uvvcount; /* count of unveil vnodes held */
size_t ps_uvncount; /* count of unveil names allocated */
int ps_uvdone; /* no more unveil is permitted */
/* End area that is zeroed on creation. */
#define ps_endzero ps_startcopy
/* The following fields are all copied upon creation in process_new. */
#define ps_startcopy ps_limit
struct plimit *ps_limit; /* [m,R] Process limits. */
struct pgrp *ps_pgrp; /* Pointer to process group. */
char ps_comm[_MAXCOMLEN]; /* command name, incl NUL */
vaddr_t ps_strings; /* User pointers to argv/env */
vaddr_t ps_timekeep; /* User pointer to timekeep */
vaddr_t ps_sigcode; /* [I] User pointer to signal code */
vaddr_t ps_sigcoderet; /* [I] User ptr to sigreturn retPC */
u_long ps_sigcookie; /* [I] */
u_int ps_rtableid; /* [a] Process routing table/domain. */
char ps_nice; /* Process "nice" value. */
struct uprof { /* profile arguments */
caddr_t pr_base; /* buffer base */
size_t pr_size; /* buffer size */
u_long pr_off; /* pc offset */
u_int pr_scale; /* pc scaling */
} ps_prof;
u_int32_t ps_acflag; /* Accounting flags. */
uint64_t ps_pledge; /* [m] pledge promises */
uint64_t ps_execpledge; /* [m] execpledge promises */
int64_t ps_kbind_cookie; /* [m] */
u_long ps_kbind_addr; /* [m] */
/* End area that is copied on creation. */
#define ps_endcopy ps_refcnt
int ps_refcnt; /* Number of references. */
struct timespec ps_start; /* starting uptime. */
struct timeout ps_realit_to; /* [m] ITIMER_REAL timeout */
};
#define ps_session ps_pgrp->pg_session
#define ps_pgid ps_pgrp->pg_id
#endif /* __need_process */
/*
* These flags are kept in ps_flags.
*/
#define PS_CONTROLT 0x00000001 /* Has a controlling terminal. */
#define PS_EXEC 0x00000002 /* Process called exec. */
#define PS_INEXEC 0x00000004 /* Process is doing an exec right now */
#define PS_EXITING 0x00000008 /* Process is exiting. */
#define PS_SUGID 0x00000010 /* Had set id privs since last exec. */
#define PS_SUGIDEXEC 0x00000020 /* last execve() was set[ug]id */
#define PS_PPWAIT 0x00000040 /* Parent waits for exec/exit. */
#define PS_ISPWAIT 0x00000080 /* Is parent of PPWAIT child. */
#define PS_PROFIL 0x00000100 /* Has started profiling. */
#define PS_TRACED 0x00000200 /* Being ptraced. */
#define PS_WAITED 0x00000400 /* Stopped proc was waited for. */
#define PS_COREDUMP 0x00000800 /* Busy coredumping */
#define PS_SINGLEEXIT 0x00001000 /* Other threads must die. */
#define PS_SINGLEUNWIND 0x00002000 /* Other threads must unwind. */
#define PS_NOZOMBIE 0x00004000 /* No signal or zombie at exit. */
#define PS_STOPPED 0x00008000 /* Just stopped, need sig to parent. */
#define PS_SYSTEM 0x00010000 /* No sigs, stats or swapping. */
#define PS_EMBRYO 0x00020000 /* New process, not yet fledged */
#define PS_ZOMBIE 0x00040000 /* Dead and ready to be waited for */
#define PS_NOBROADCASTKILL 0x00080000 /* Process excluded from kill -1. */
#define PS_PLEDGE 0x00100000 /* Has called pledge(2) */
#define PS_WXNEEDED 0x00200000 /* Process allowed to violate W^X */
#define PS_EXECPLEDGE 0x00400000 /* Has exec pledges */
#define PS_ORPHAN 0x00800000 /* Process is on an orphan list */
#define PS_CHROOT 0x01000000 /* Process is chrooted */
#define PS_BITS \
("\20" "\01CONTROLT" "\02EXEC" "\03INEXEC" "\04EXITING" "\05SUGID" \
"\06SUGIDEXEC" "\07PPWAIT" "\010ISPWAIT" "\011PROFIL" "\012TRACED" \
"\013WAITED" "\014COREDUMP" "\015SINGLEEXIT" "\016SINGLEUNWIND" \
"\017NOZOMBIE" "\020STOPPED" "\021SYSTEM" "\022EMBRYO" "\023ZOMBIE" \
"\024NOBROADCASTKILL" "\025PLEDGE" "\026WXNEEDED" "\027EXECPLEDGE" \
"\030ORPHAN" "\031CHROOT")
struct kcov_dev;
struct lock_list_entry;
struct kqueue;
struct p_inentry {
u_long ie_serial;
vaddr_t ie_start;
vaddr_t ie_end;
};
/*
* Locks used to protect struct members in this file:
* I immutable after creation
* S scheduler lock
* l read only reference, see lim_read_enter()
* o owned (read/modified only) by this thread
*/
struct proc {
TAILQ_ENTRY(proc) p_runq; /* [S] current run/sleep queue */
LIST_ENTRY(proc) p_list; /* List of all threads. */
struct process *p_p; /* [I] The process of this thread. */
TAILQ_ENTRY(proc) p_thr_link; /* Threads in a process linkage. */
TAILQ_ENTRY(proc) p_fut_link; /* Threads in a futex linkage. */
struct futex *p_futex; /* Current sleeping futex. */
/* substructures: */
struct filedesc *p_fd; /* copy of p_p->ps_fd */
struct vmspace *p_vmspace; /* [I] copy of p_p->ps_vmspace */
struct p_inentry p_spinentry; /* [o] cache for SP check */
struct p_inentry p_pcinentry; /* [o] cache for PC check */
int p_flag; /* P_* flags. */
u_char p_spare; /* unused */
char p_stat; /* [S] S* process status. */
u_char p_runpri; /* [S] Runqueue priority */
u_char p_descfd; /* if not 255, fdesc permits this fd */
pid_t p_tid; /* Thread identifier. */
LIST_ENTRY(proc) p_hash; /* Hash chain. */
/* The following fields are all zeroed upon creation in fork. */
#define p_startzero p_dupfd
int p_dupfd; /* Sideways return value from filedescopen. XXX */
/* scheduling */
int p_cpticks; /* Ticks of cpu time. */
const volatile void *p_wchan; /* [S] Sleep address. */
struct timeout p_sleep_to;/* timeout for tsleep() */
const char *p_wmesg; /* [S] Reason for sleep. */
fixpt_t p_pctcpu; /* [S] %cpu for this thread */
u_int p_slptime; /* [S] Time since last blocked. */
u_int p_uticks; /* Statclock hits in user mode. */
u_int p_sticks; /* Statclock hits in system mode. */
u_int p_iticks; /* Statclock hits processing intr. */
struct cpu_info * volatile p_cpu; /* [S] CPU we're running on. */
struct rusage p_ru; /* Statistics */
struct tusage p_tu; /* accumulated times. */
struct timespec p_rtime; /* Real time. */
struct plimit *p_limit; /* [l] read ref. of p_p->ps_limit */
struct kcov_dev *p_kd; /* kcov device handle */
struct lock_list_entry *p_sleeplocks; /* WITNESS lock tracking */
struct kqueue *p_kq; /* [o] select/poll queue of evts */
unsigned long p_kq_serial; /* [o] to check against enqueued evts */
int p_siglist; /* [a] Signals arrived & not delivered*/
/* End area that is zeroed on creation. */
#define p_endzero p_startcopy
/* The following fields are all copied upon creation in fork. */
#define p_startcopy p_sigmask
sigset_t p_sigmask; /* [a] Current signal mask */
u_char p_slppri; /* [S] Sleeping priority */
u_char p_usrpri; /* [S] Priority based on p_estcpu & ps_nice */
u_int p_estcpu; /* [S] Time averaged val of p_cpticks */
int p_pledge_syscall; /* Cache of current syscall */
struct ucred *p_ucred; /* [o] cached credentials */
struct sigaltstack p_sigstk; /* sp & on stack state variable */
u_long p_prof_addr; /* tmp storage for profiling addr until AST */
u_long p_prof_ticks; /* tmp storage for profiling ticks until AST */
/* End area that is copied on creation. */
#define p_endcopy p_addr
struct user *p_addr; /* Kernel virtual addr of u-area */
struct mdproc p_md; /* Any machine-dependent fields. */
sigset_t p_oldmask; /* Saved mask from before sigpause */
int p_sisig; /* For core dump/debugger XXX */
union sigval p_sigval; /* For core dump/debugger XXX */
long p_sitrapno; /* For core dump/debugger XXX */
int p_sicode; /* For core dump/debugger XXX */
};
/* Status values. */
#define SIDL 1 /* Thread being created by fork. */
#define SRUN 2 /* Currently runnable. */
#define SSLEEP 3 /* Sleeping on an address. */
#define SSTOP 4 /* Debugging or suspension. */
#define SZOMB 5 /* unused */
#define SDEAD 6 /* Thread is almost gone */
#define SONPROC 7 /* Thread is currently on a CPU. */
#define P_ZOMBIE(p) ((p)->p_stat == SDEAD)
#define P_HASSIBLING(p) (TAILQ_FIRST(&(p)->p_p->ps_threads) != (p) || \
TAILQ_NEXT((p), p_thr_link) != NULL)
/*
* These flags are per-thread and kept in p_flag
*/
#define P_INKTR 0x00000001 /* In a ktrace op, don't recurse */
#define P_PROFPEND 0x00000002 /* SIGPROF needs to be posted */
#define P_ALRMPEND 0x00000004 /* SIGVTALRM needs to be posted */
#define P_SIGSUSPEND 0x00000008 /* Need to restore before-suspend mask*/
#define P_CANTSLEEP 0x00000010 /* insomniac thread */
#define P_SINTR 0x00000080 /* Sleep is interruptible. */
#define P_SYSTEM 0x00000200 /* No sigs, stats or swapping. */
#define P_TIMEOUT 0x00000400 /* Timing out during sleep. */
#define P_WEXIT 0x00002000 /* Working on exiting. */
#define P_OWEUPC 0x00008000 /* Owe proc an addupc() at next ast. */
#define P_SUSPSINGLE 0x00080000 /* Need to stop for single threading. */
#define P_CONTINUED 0x00800000 /* Proc has continued from a stopped state. */
#define P_THREAD 0x04000000 /* Only a thread, not a real process */
#define P_SUSPSIG 0x08000000 /* Stopped from signal. */
#define P_SOFTDEP 0x10000000 /* Stuck processing softdep worklist */
#define P_CPUPEG 0x40000000 /* Do not move to another cpu. */
#define P_BITS \
("\20" "\01INKTR" "\02PROFPEND" "\03ALRMPEND" "\04SIGSUSPEND" \
"\05CANTSLEEP" "\010SINTR" "\012SYSTEM" "\013TIMEOUT" \
"\016WEXIT" "\020OWEUPC" "\024SUSPSINGLE" "\027XX" \
"\030CONTINUED" "\033THREAD" "\034SUSPSIG" "\035SOFTDEP" "\037CPUPEG")
#define THREAD_PID_OFFSET 100000
#ifdef _KERNEL
struct uidinfo {
LIST_ENTRY(uidinfo) ui_hash;
uid_t ui_uid;
long ui_proccnt; /* proc structs */
long ui_lockcnt; /* lockf structs */
};
struct uidinfo *uid_find(uid_t);
void uid_release(struct uidinfo *);
/*
* We use process IDs <= PID_MAX; PID_MAX + 1 must also fit in a pid_t,
* as it is used to represent "no process group".
* We set PID_MAX to 99999 to keep it in 5 columns in ps
* When exposed to userspace, thread IDs have THREAD_PID_OFFSET
* added to keep them from overlapping the PID range. For them,
* we use a * a (0 .. 2^n] range for cheapness, picking 'n' such
* that 2^n + THREAD_PID_OFFSET and THREAD_PID_OFFSET have
* the same number of columns when printed.
*/
#define PID_MAX 99999
#define TID_MASK 0x7ffff
#define NO_PID (PID_MAX+1)
#define SESS_LEADER(pr) ((pr)->ps_session->s_leader == (pr))
#define SESSHOLD(s) ((s)->s_count++)
#define SESSRELE(s) do { \
if (--(s)->s_count == 0) { \
timeout_del(&(s)->s_verauthto); \
pool_put(&session_pool, (s)); \
} \
} while (/* CONSTCOND */ 0)
/*
* Flags to fork1().
*/
#define FORK_FORK 0x00000001
#define FORK_VFORK 0x00000002
#define FORK_IDLE 0x00000004
#define FORK_PPWAIT 0x00000008
#define FORK_SHAREFILES 0x00000010
#define FORK_SYSTEM 0x00000020
#define FORK_NOZOMBIE 0x00000040
#define FORK_SHAREVM 0x00000080
#define FORK_PTRACE 0x00000400
#define EXIT_NORMAL 0x00000001
#define EXIT_THREAD 0x00000002
#define EXIT_THREAD_NOCHECK 0x00000003
#define TIDHASH(tid) (&tidhashtbl[(tid) & tidhash])
extern LIST_HEAD(tidhashhead, proc) *tidhashtbl;
extern u_long tidhash;
#define PIDHASH(pid) (&pidhashtbl[(pid) & pidhash])
extern LIST_HEAD(pidhashhead, process) *pidhashtbl;
extern u_long pidhash;
#define PGRPHASH(pgid) (&pgrphashtbl[(pgid) & pgrphash])
extern LIST_HEAD(pgrphashhead, pgrp) *pgrphashtbl;
extern u_long pgrphash;
extern struct proc proc0; /* Process slot for swapper. */
extern struct process process0; /* Process slot for kernel threads. */
extern int nprocesses, maxprocess; /* Cur and max number of processes. */
extern int nthreads, maxthread; /* Cur and max number of threads. */
LIST_HEAD(proclist, proc);
LIST_HEAD(processlist, process);
extern struct processlist allprocess; /* List of all processes. */
extern struct processlist zombprocess; /* List of zombie processes. */
extern struct proclist allproc; /* List of all threads. */
extern struct process *initprocess; /* Process slot for init. */
extern struct proc *reaperproc; /* Thread slot for reaper. */
extern struct proc *syncerproc; /* filesystem syncer daemon */
extern struct pool process_pool; /* memory pool for processes */
extern struct pool proc_pool; /* memory pool for procs */
extern struct pool rusage_pool; /* memory pool for zombies */
extern struct pool ucred_pool; /* memory pool for ucreds */
extern struct pool session_pool; /* memory pool for sessions */
extern struct pool pgrp_pool; /* memory pool for pgrps */
void freepid(pid_t);
struct process *prfind(pid_t); /* Find process by id. */
struct process *zombiefind(pid_t); /* Find zombie process by id. */
struct proc *tfind(pid_t); /* Find thread by id. */
struct pgrp *pgfind(pid_t); /* Find process group by id. */
void proc_printit(struct proc *p, const char *modif,
int (*pr)(const char *, ...));
int chgproccnt(uid_t uid, int diff);
void enternewpgrp(struct process *, struct pgrp *, struct session *);
void enterthispgrp(struct process *, struct pgrp *);
int inferior(struct process *, struct process *);
void leavepgrp(struct process *);
void killjobc(struct process *);
void preempt(void);
void procinit(void);
void setpriority(struct proc *, uint32_t, uint8_t);
void setrunnable(struct proc *);
void endtsleep(void *);
int wakeup_proc(struct proc *, const volatile void *);
void unsleep(struct proc *);
void reaper(void *);
__dead void exit1(struct proc *, int, int, int);
void exit2(struct proc *);
int dowait4(struct proc *, pid_t, int *, int, struct rusage *,
register_t *);
void cpu_fork(struct proc *_curp, struct proc *_child, void *_stack,
void *_tcb, void (*_func)(void *), void *_arg);
void cpu_exit(struct proc *);
void process_initialize(struct process *, struct proc *);
int fork1(struct proc *_curp, int _flags, void (*_func)(void *),
void *_arg, register_t *_retval, struct proc **_newprocp);
int thread_fork(struct proc *_curp, void *_stack, void *_tcb,
pid_t *_tidptr, register_t *_retval);
int groupmember(gid_t, struct ucred *);
void dorefreshcreds(struct process *, struct proc *);
void dosigsuspend(struct proc *, sigset_t);
static inline void
refreshcreds(struct proc *p)
{
struct process *pr = p->p_p;
/* this is an unlocked access to ps_ucred, but the result is benign */
if (pr->ps_ucred != p->p_ucred)
dorefreshcreds(pr, p);
}
enum single_thread_mode {
SINGLE_SUSPEND, /* other threads to stop wherever they are */
SINGLE_UNWIND, /* other threads to unwind and stop */
SINGLE_EXIT /* other threads to unwind and then exit */
};
int single_thread_set(struct proc *, enum single_thread_mode, int);
int single_thread_wait(struct process *, int);
void single_thread_clear(struct proc *, int);
int single_thread_check(struct proc *, int);
void child_return(void *);
int proc_cansugid(struct proc *);
struct sleep_state {
int sls_s;
int sls_catch;
int sls_timeout;
};
struct cond {
unsigned int c_wait; /* [a] initialized and waiting */
};
#define COND_INITIALIZER() { .c_wait = 1 }
#if defined(MULTIPROCESSOR)
void proc_trampoline_mp(void); /* XXX */
#endif
/*
* functions to handle sets of cpus.
*
* For now we keep the cpus in ints so that we can use the generic
* atomic ops.
*/
#define CPUSET_ASIZE(x) (((x) - 1)/32 + 1)
#define CPUSET_SSIZE CPUSET_ASIZE(MAXCPUS)
struct cpuset {
int cs_set[CPUSET_SSIZE];
};
void cpuset_init_cpu(struct cpu_info *);
void cpuset_clear(struct cpuset *);
void cpuset_add(struct cpuset *, struct cpu_info *);
void cpuset_del(struct cpuset *, struct cpu_info *);
int cpuset_isset(struct cpuset *, struct cpu_info *);
void cpuset_add_all(struct cpuset *);
void cpuset_copy(struct cpuset *, struct cpuset *);
void cpuset_union(struct cpuset *, struct cpuset *, struct cpuset *);
void cpuset_intersection(struct cpuset *t, struct cpuset *, struct cpuset *);
void cpuset_complement(struct cpuset *, struct cpuset *, struct cpuset *);
int cpuset_cardinality(struct cpuset *);
struct cpu_info *cpuset_first(struct cpuset *);
#endif /* _KERNEL */
#endif /* !_SYS_PROC_H_ */
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
/* $OpenBSD: ntfs_vfsops.c,v 1.65 2022/01/11 03:13:59 jsg Exp $ */
/* $NetBSD: ntfs_vfsops.c,v 1.7 2003/04/24 07:50:19 christos Exp $ */
/*-
* Copyright (c) 1998, 1999 Semen Ustimenko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Id: ntfs_vfsops.c,v 1.7 1999/05/31 11:28:30 phk Exp
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/disk.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/specdev.h>
/*#define NTFS_DEBUG 1*/
#include <ntfs/ntfs.h>
#include <ntfs/ntfs_inode.h>
#include <ntfs/ntfs_subr.h>
#include <ntfs/ntfs_vfsops.h>
#include <ntfs/ntfs_ihash.h>
int ntfs_mount(struct mount *, const char *, void *,
struct nameidata *, struct proc *);
int ntfs_quotactl(struct mount *, int, uid_t, caddr_t,
struct proc *);
int ntfs_root(struct mount *, struct vnode **);
int ntfs_start(struct mount *, int, struct proc *);
int ntfs_statfs(struct mount *, struct statfs *,
struct proc *);
int ntfs_sync(struct mount *, int, int, struct ucred *,
struct proc *);
int ntfs_unmount(struct mount *, int, struct proc *);
int ntfs_vget(struct mount *mp, ino_t ino,
struct vnode **vpp);
int ntfs_mountfs(struct vnode *, struct mount *,
struct ntfs_args *, struct proc *);
int ntfs_vptofh(struct vnode *, struct fid *);
int ntfs_init(struct vfsconf *);
int ntfs_fhtovp(struct mount *, struct fid *,
struct vnode **);
int ntfs_checkexp(struct mount *, struct mbuf *,
int *, struct ucred **);
int ntfs_sysctl(int *, u_int, void *, size_t *, void *,
size_t, struct proc *);
/*
* Verify a remote client has export rights and return these rights via.
* exflagsp and credanonp.
*/
int
ntfs_checkexp(struct mount *mp, struct mbuf *nam, int *exflagsp,
struct ucred **credanonp)
{
struct netcred *np;
struct ntfsmount *ntm = VFSTONTFS(mp);
/*
* Get the export permission structure for this <mp, client> tuple.
*/
np = vfs_export_lookup(mp, &ntm->ntm_export, nam);
if (np == NULL)
return (EACCES);
*exflagsp = np->netc_exflags;
*credanonp = &np->netc_anon;
return (0);
}
int
ntfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
return (EINVAL);
}
int
ntfs_init(struct vfsconf *vcp)
{
return 0;
}
int
ntfs_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
int err = 0;
struct vnode *devvp;
struct ntfs_args *args = data;
char fname[MNAMELEN];
char fspec[MNAMELEN];
ntfs_nthashinit();
/*
***
* Mounting non-root file system or updating a file system
***
*/
/*
* If updating, check whether changing from read-only to
* read/write; if there is no device name, that's all we do.
*/
if (mp->mnt_flag & MNT_UPDATE) {
/* if not updating name...*/
if (args && args->fspec == NULL) {
/*
* Process export requests. Jumping to "success"
* will return the vfs_export() error code.
*/
struct ntfsmount *ntm = VFSTONTFS(mp);
err = vfs_export(mp, &ntm->ntm_export, &args->export_info);
goto success;
}
printf("ntfs_mount(): MNT_UPDATE not supported\n");
err = EINVAL;
goto error_1;
}
/*
* Not an update, or updating the name: look up the name
* and verify that it refers to a sensible block device.
*/
err = copyinstr(args->fspec, fspec, sizeof(fspec), NULL);
if (err)
goto error_1;
if (disk_map(fspec, fname, sizeof(fname), DM_OPENBLCK) == -1)
bcopy(fspec, fname, sizeof(fname));
NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fname, p);
err = namei(ndp);
if (err) {
/* can't get devvp!*/
goto error_1;
}
devvp = ndp->ni_vp;
if (devvp->v_type != VBLK) {
err = ENOTBLK;
goto error_2;
}
if (major(devvp->v_rdev) >= nblkdev) {
err = ENXIO;
goto error_2;
}
if (mp->mnt_flag & MNT_UPDATE) {
#if 0
/*
********************
* UPDATE
********************
*/
if (devvp != ntmp->um_devvp)
err = EINVAL; /* needs translation */
else
vrele(devvp);
/*
* Update device name only on success
*/
if( !err) {
err = set_statfs_info(NULL, UIO_USERSPACE, args->fspec,
UIO_USERSPACE, mp, p);
}
#endif
} else {
/*
********************
* NEW MOUNT
********************
*/
/*
* Since this is a new mount, we want the names for
* the device and the mount point copied in. If an
* error occurs, the mountpoint is discarded by the
* upper level code.
*/
/* Save "last mounted on" info for mount point (NULL pad)*/
bzero(mp->mnt_stat.f_mntonname, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, fname, MNAMELEN);
bzero(mp->mnt_stat.f_mntfromspec, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
bcopy(args, &mp->mnt_stat.mount_info.ntfs_args, sizeof(*args));
if ( !err) {
err = ntfs_mountfs(devvp, mp, args, p);
}
}
if (err) {
goto error_2;
}
/*
* Initialize FS stat information in mount struct; uses both
* mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname
*
* This code is common to root and non-root mounts
*/
(void)VFS_STATFS(mp, &mp->mnt_stat, p);
goto success;
error_2: /* error with devvp held*/
/* release devvp before failing*/
vrele(devvp);
error_1: /* no state to back out*/
success:
return(err);
}
/*
* Common code for mount and mountroot
*/
int
ntfs_mountfs(struct vnode *devvp, struct mount *mp, struct ntfs_args *argsp,
struct proc *p)
{
struct buf *bp;
struct ntfsmount *ntmp = NULL;
dev_t dev = devvp->v_rdev;
int error, ncount, i;
struct vnode *vp;
/*
* Disallow multiple mounts of the same device.
* Disallow mounting of a device that is currently in use
* (except for root, which might share swap device for miniroot).
* Flush out any old buffers remaining from a previous use.
*/
error = vfs_mountedon(devvp);
if (error)
return (error);
ncount = vcount(devvp);
if (ncount > 1 && devvp != rootvp)
return (EBUSY);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error)
return (error);
error = VOP_OPEN(devvp, FREAD, FSCRED, p);
if (error)
return (error);
bp = NULL;
error = bread(devvp, BBLOCK, BBSIZE, &bp);
if (error)
goto out;
ntmp = malloc(sizeof *ntmp, M_NTFSMNT, M_WAITOK | M_ZERO);
bcopy(bp->b_data, &ntmp->ntm_bootfile, sizeof(struct bootfile));
brelse(bp);
bp = NULL;
if (strncmp(ntmp->ntm_bootfile.bf_sysid, NTFS_BBID, NTFS_BBIDLEN)) {
error = EINVAL;
DPRINTF("ntfs_mountfs: invalid boot block\n");
goto out;
}
{
int8_t cpr = ntmp->ntm_mftrecsz;
if( cpr > 0 )
ntmp->ntm_bpmftrec = ntmp->ntm_spc * cpr;
else
ntmp->ntm_bpmftrec = (1 << (-cpr)) / ntmp->ntm_bps;
}
DPRINTF("ntfs_mountfs(): bps: %u, spc: %u, media: %x, "
"mftrecsz: %u (%u sects)\n", ntmp->ntm_bps, ntmp->ntm_spc,
ntmp->ntm_bootfile.bf_media, ntmp->ntm_mftrecsz,
ntmp->ntm_bpmftrec);
DPRINTF("ntfs_mountfs(): mftcn: 0x%llx|0x%llx\n",
ntmp->ntm_mftcn, ntmp->ntm_mftmirrcn);
ntmp->ntm_mountp = mp;
ntmp->ntm_dev = dev;
ntmp->ntm_devvp = devvp;
ntmp->ntm_uid = argsp->uid;
ntmp->ntm_gid = argsp->gid;
ntmp->ntm_mode = argsp->mode;
ntmp->ntm_flag = argsp->flag;
mp->mnt_data = ntmp;
TAILQ_INIT(&ntmp->ntm_ntnodeq);
/* set file name encode/decode hooks XXX utf-8 only for now */
ntmp->ntm_wget = ntfs_utf8_wget;
ntmp->ntm_wput = ntfs_utf8_wput;
ntmp->ntm_wcmp = ntfs_utf8_wcmp;
DPRINTF("ntfs_mountfs(): case-%s,%s uid: %d, gid: %d, mode: %o\n",
(ntmp->ntm_flag & NTFS_MFLAG_CASEINS) ? "insens." : "sens.",
(ntmp->ntm_flag & NTFS_MFLAG_ALLNAMES) ? " allnames," : "",
ntmp->ntm_uid, ntmp->ntm_gid, ntmp->ntm_mode);
/*
* We read in some system nodes to do not allow
* reclaim them and to have everytime access to them.
*/
{
int pi[3] = { NTFS_MFTINO, NTFS_ROOTINO, NTFS_BITMAPINO };
for (i=0; i<3; i++) {
error = VFS_VGET(mp, pi[i], &(ntmp->ntm_sysvn[pi[i]]));
if(error)
goto out1;
ntmp->ntm_sysvn[pi[i]]->v_flag |= VSYSTEM;
vref(ntmp->ntm_sysvn[pi[i]]);
vput(ntmp->ntm_sysvn[pi[i]]);
}
}
/* read the Unicode lowercase --> uppercase translation table,
* if necessary */
if ((error = ntfs_toupper_use(mp, ntmp, p)))
goto out1;
/*
* Scan $BitMap and count free clusters
*/
error = ntfs_calccfree(ntmp, &ntmp->ntm_cfree);
if(error)
goto out1;
/*
* Read and translate to internal format attribute
* definition file.
*/
{
int num,j;
struct attrdef ad;
/* Open $AttrDef */
error = VFS_VGET(mp, NTFS_ATTRDEFINO, &vp );
if(error)
goto out1;
/* Count valid entries */
for(num = 0; ; num++) {
error = ntfs_readattr(ntmp, VTONT(vp),
NTFS_A_DATA, NULL, num * sizeof(ad), sizeof(ad),
&ad, NULL);
if (error)
goto out1;
if (ad.ad_name[0] == 0)
break;
}
/* Alloc memory for attribute definitions */
ntmp->ntm_ad = mallocarray(num, sizeof(struct ntvattrdef),
M_NTFSMNT, M_WAITOK);
ntmp->ntm_adnum = num;
/* Read them and translate */
for(i = 0; i < num; i++){
error = ntfs_readattr(ntmp, VTONT(vp),
NTFS_A_DATA, NULL, i * sizeof(ad), sizeof(ad),
&ad, NULL);
if (error)
goto out1;
j = 0;
do {
ntmp->ntm_ad[i].ad_name[j] = ad.ad_name[j];
} while(ad.ad_name[j++]);
ntmp->ntm_ad[i].ad_namelen = j - 1;
ntmp->ntm_ad[i].ad_type = ad.ad_type;
}
vput(vp);
}
mp->mnt_stat.f_fsid.val[0] = dev;
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
mp->mnt_stat.f_namemax = NTFS_MAXFILENAME;
mp->mnt_flag |= MNT_LOCAL;
devvp->v_specmountpoint = mp;
return (0);
out1:
for (i = 0; i < NTFS_SYSNODESNUM; i++)
if (ntmp->ntm_sysvn[i])
vrele(ntmp->ntm_sysvn[i]);
if (vflush(mp,NULLVP,0))
DPRINTF("ntfs_mountfs: vflush failed\n");
out:
if (devvp->v_specinfo)
devvp->v_specmountpoint = NULL;
if (bp)
brelse(bp);
if (ntmp != NULL) {
if (ntmp->ntm_ad != NULL)
free(ntmp->ntm_ad, M_NTFSMNT, 0);
free(ntmp, M_NTFSMNT, 0);
mp->mnt_data = NULL;
}
/* lock the device vnode before calling VOP_CLOSE() */
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_CLOSE(devvp, FREAD, NOCRED, p);
VOP_UNLOCK(devvp);
return (error);
}
int
ntfs_start(struct mount *mp, int flags, struct proc *p)
{
return (0);
}
int
ntfs_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct ntfsmount *ntmp;
int error, flags, i;
DPRINTF("ntfs_unmount: unmounting...\n");
ntmp = VFSTONTFS(mp);
flags = 0;
if(mntflags & MNT_FORCE)
flags |= FORCECLOSE;
DPRINTF("ntfs_unmount: vflushing...\n");
error = vflush(mp,NULLVP,flags | SKIPSYSTEM);
if (error) {
DPRINTF("ntfs_unmount: vflush failed: %d\n", error);
return (error);
}
/* Check if system vnodes are still referenced */
for(i=0;i<NTFS_SYSNODESNUM;i++) {
if(((mntflags & MNT_FORCE) == 0) && (ntmp->ntm_sysvn[i] &&
ntmp->ntm_sysvn[i]->v_usecount > 1))
return (EBUSY);
}
/* Dereference all system vnodes */
for(i=0;i<NTFS_SYSNODESNUM;i++)
if(ntmp->ntm_sysvn[i]) vrele(ntmp->ntm_sysvn[i]);
/* vflush system vnodes */
error = vflush(mp,NULLVP,flags);
if (error) {
/* XXX should this be panic() ? */
printf("ntfs_unmount: vflush failed(sysnodes): %d\n",error);
}
/* Check if the type of device node isn't VBAD before
* touching v_specinfo. If the device vnode is revoked, the
* field is NULL and touching it causes null pointer dereference.
*/
if (ntmp->ntm_devvp->v_type != VBAD)
ntmp->ntm_devvp->v_specmountpoint = NULL;
/* lock the device vnode before calling VOP_CLOSE() */
vn_lock(ntmp->ntm_devvp, LK_EXCLUSIVE | LK_RETRY);
vinvalbuf(ntmp->ntm_devvp, V_SAVE, NOCRED, p, 0, INFSLP);
(void)VOP_CLOSE(ntmp->ntm_devvp, FREAD, NOCRED, p);
vput(ntmp->ntm_devvp);
/* free the toupper table, if this has been last mounted ntfs volume */
ntfs_toupper_unuse(p);
DPRINTF("ntfs_unmount: freeing memory...\n");
free(ntmp->ntm_ad, M_NTFSMNT, 0);
free(ntmp, M_NTFSMNT, 0);
mp->mnt_data = NULL;
mp->mnt_flag &= ~MNT_LOCAL;
return (0);
}
int
ntfs_root(struct mount *mp, struct vnode **vpp)
{
struct vnode *nvp;
int error = 0;
DPRINTF("ntfs_root(): sysvn: %p\n",
VFSTONTFS(mp)->ntm_sysvn[NTFS_ROOTINO]);
error = VFS_VGET(mp, (ino_t)NTFS_ROOTINO, &nvp);
if(error) {
printf("ntfs_root: VFS_VGET failed: %d\n",error);
return (error);
}
*vpp = nvp;
return (0);
}
/*
* Do operations associated with quotas, not supported
*/
int
ntfs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
struct proc *p)
{
return EOPNOTSUPP;
}
int
ntfs_calccfree(struct ntfsmount *ntmp, cn_t *cfreep)
{
struct vnode *vp;
u_int8_t *tmp;
int j, error;
cn_t cfree = 0;
uint64_t bmsize, offset;
size_t chunksize, i;
vp = ntmp->ntm_sysvn[NTFS_BITMAPINO];
bmsize = VTOF(vp)->f_size;
if (bmsize > 1024 * 1024)
chunksize = 1024 * 1024;
else
chunksize = bmsize;
tmp = malloc(chunksize, M_TEMP, M_WAITOK);
for (offset = 0; offset < bmsize; offset += chunksize) {
if (chunksize > bmsize - offset)
chunksize = bmsize - offset;
error = ntfs_readattr(ntmp, VTONT(vp), NTFS_A_DATA, NULL,
offset, chunksize, tmp, NULL);
if (error)
goto out;
for (i = 0; i < chunksize; i++)
for (j = 0; j < 8; j++)
if (~tmp[i] & (1 << j))
cfree++;
}
*cfreep = cfree;
out:
free(tmp, M_TEMP, 0);
return(error);
}
int
ntfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct ntfsmount *ntmp = VFSTONTFS(mp);
u_int64_t mftallocated;
DPRINTF("ntfs_statfs():\n");
mftallocated = VTOF(ntmp->ntm_sysvn[NTFS_MFTINO])->f_allocated;
sbp->f_bsize = ntmp->ntm_bps;
sbp->f_iosize = ntmp->ntm_bps * ntmp->ntm_spc;
sbp->f_blocks = ntmp->ntm_bootfile.bf_spv;
sbp->f_bfree = sbp->f_bavail = ntfs_cntobn(ntmp->ntm_cfree);
sbp->f_ffree = sbp->f_favail = sbp->f_bfree / ntmp->ntm_bpmftrec;
sbp->f_files = mftallocated / ntfs_bntob(ntmp->ntm_bpmftrec) +
sbp->f_ffree;
copy_statfs_info(sbp, mp);
return (0);
}
int
ntfs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, struct proc *p)
{
/*DPRINTF("ntfs_sync():\n");*/
return (0);
}
int
ntfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
struct ntfid *ntfhp = (struct ntfid *)fhp;
int error;
DDPRINTF("ntfs_fhtovp(): %s: %u\n",
mp->mnt_stat.f_mntonname, ntfhp->ntfid_ino);
error = ntfs_vgetex(mp, ntfhp->ntfid_ino, ntfhp->ntfid_attr, NULL,
LK_EXCLUSIVE | LK_RETRY, 0, vpp); /* XXX */
if (error != 0) {
*vpp = NULLVP;
return (error);
}
/* XXX as unlink/rmdir/mkdir/creat are not currently possible
* with NTFS, we don't need to check anything else for now */
return (0);
}
int
ntfs_vptofh(struct vnode *vp, struct fid *fhp)
{
struct ntnode *ntp;
struct ntfid *ntfhp;
struct fnode *fn;
DDPRINTF("ntfs_fhtovp(): %s: %p\n",
vp->v_mount->mnt_stat.f_mntonname, vp);
fn = VTOF(vp);
ntp = VTONT(vp);
ntfhp = (struct ntfid *)fhp;
ntfhp->ntfid_len = sizeof(struct ntfid);
ntfhp->ntfid_ino = ntp->i_number;
ntfhp->ntfid_attr = fn->f_attrtype;
#ifdef notyet
ntfhp->ntfid_gen = ntp->i_gen;
#endif
return (0);
}
int
ntfs_vgetex(struct mount *mp, ntfsino_t ino, u_int32_t attrtype, char *attrname,
u_long lkflags, u_long flags, struct vnode **vpp)
{
int error;
struct ntfsmount *ntmp;
struct ntnode *ip;
struct fnode *fp;
struct vnode *vp;
enum vtype f_type;
DPRINTF("ntfs_vgetex: ino: %u, attr: 0x%x:%s, lkf: 0x%lx, f: 0x%lx\n",
ino, attrtype, attrname ? attrname : "", lkflags, flags);
ntmp = VFSTONTFS(mp);
*vpp = NULL;
/* Get ntnode */
error = ntfs_ntlookup(ntmp, ino, &ip);
if (error) {
printf("ntfs_vget: ntfs_ntget failed\n");
return (error);
}
/* It may be not initialized fully, so force load it */
if (!(flags & VG_DONTLOADIN) && !(ip->i_flag & IN_LOADED)) {
error = ntfs_loadntnode(ntmp, ip);
if(error) {
printf("ntfs_vget: CAN'T LOAD ATTRIBUTES FOR INO: %d\n",
ip->i_number);
ntfs_ntput(ip);
return (error);
}
}
error = ntfs_fget(ntmp, ip, attrtype, attrname, &fp);
if (error) {
printf("ntfs_vget: ntfs_fget failed\n");
ntfs_ntput(ip);
return (error);
}
if (!(flags & VG_DONTVALIDFN) && !(fp->f_flag & FN_VALID)) {
if ((ip->i_frflag & NTFS_FRFLAG_DIR) &&
(fp->f_attrtype == NTFS_A_DATA && fp->f_attrname == NULL)) {
f_type = VDIR;
} else if (flags & VG_EXT) {
f_type = VNON;
fp->f_size = fp->f_allocated = 0;
} else {
f_type = VREG;
error = ntfs_filesize(ntmp, fp,
&fp->f_size, &fp->f_allocated);
if (error) {
ntfs_ntput(ip);
return (error);
}
}
fp->f_flag |= FN_VALID;
}
/*
* We may be calling vget() now. To avoid potential deadlock, we need
* to release ntnode lock, since due to locking order vnode
* lock has to be acquired first.
* ntfs_fget() bumped ntnode usecount, so ntnode won't be recycled
* prematurely.
*/
ntfs_ntput(ip);
if (FTOV(fp)) {
/* vget() returns error if the vnode has been recycled */
if (vget(FTOV(fp), lkflags) == 0) {
*vpp = FTOV(fp);
return (0);
}
}
error = getnewvnode(VT_NTFS, ntmp->ntm_mountp, &ntfs_vops, &vp);
if(error) {
ntfs_frele(fp);
ntfs_ntput(ip);
return (error);
}
DPRINTF("ntfs_vget: vnode: %p for ntnode: %u\n", vp, ino);
fp->f_vp = vp;
vp->v_data = fp;
vp->v_type = f_type;
if (ino == NTFS_ROOTINO)
vp->v_flag |= VROOT;
if (lkflags & LK_TYPE_MASK) {
error = vn_lock(vp, lkflags);
if (error) {
vput(vp);
return (error);
}
}
*vpp = vp;
return (0);
}
int
ntfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
if (ino > (ntfsino_t)-1)
panic("ntfs_vget: alien ino_t %llu", (unsigned long long)ino);
return ntfs_vgetex(mp, ino, NTFS_A_DATA, NULL,
LK_EXCLUSIVE | LK_RETRY, 0, vpp); /* XXX */
}
const struct vfsops ntfs_vfsops = {
.vfs_mount = ntfs_mount,
.vfs_start = ntfs_start,
.vfs_unmount = ntfs_unmount,
.vfs_root = ntfs_root,
.vfs_quotactl = ntfs_quotactl,
.vfs_statfs = ntfs_statfs,
.vfs_sync = ntfs_sync,
.vfs_vget = ntfs_vget,
.vfs_fhtovp = ntfs_fhtovp,
.vfs_vptofh = ntfs_vptofh,
.vfs_init = ntfs_init,
.vfs_sysctl = ntfs_sysctl,
.vfs_checkexp = ntfs_checkexp,
};
45
6
40
43
28
13
1
5
21
13
1
12
14
20
10
23
40
6
98
2
51
46
6
7
32
5
11
1
1
9
9
8
4
4
8
3
3
2
37
3
13
22
2
2
3
48
1
36
3
10
3
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
/* $OpenBSD: raw_ip6.c,v 1.168 2022/09/03 22:43:38 mvs Exp $ */
/* $KAME: raw_ip6.c,v 1.69 2001/03/04 15:55:44 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)raw_ip.c 8.2 (Berkeley) 1/4/94
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#ifdef MROUTING
#include <netinet6/ip6_mroute.h>
#endif
#include <netinet/icmp6.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet6/nd6.h>
#include <netinet6/ip6protosw.h>
#include <netinet6/raw_ip6.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <sys/stdarg.h>
/*
* Raw interface to IP6 protocol.
*/
struct inpcbtable rawin6pcbtable;
struct cpumem *rip6counters;
const struct pr_usrreqs rip6_usrreqs = {
.pru_attach = rip6_attach,
.pru_detach = rip6_detach,
.pru_bind = rip6_bind,
.pru_connect = rip6_connect,
.pru_disconnect = rip6_disconnect,
.pru_shutdown = rip6_shutdown,
.pru_send = rip6_send,
.pru_abort = rip6_abort,
.pru_control = in6_control,
.pru_sockaddr = in6_sockaddr,
.pru_peeraddr = in6_peeraddr,
};
/*
* Initialize raw connection block queue.
*/
void
rip6_init(void)
{
in_pcbinit(&rawin6pcbtable, 1);
rip6counters = counters_alloc(rip6s_ncounters);
}
int
rip6_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct inpcb *in6p;
SIMPLEQ_HEAD(, inpcb) inpcblist;
struct in6_addr *key;
struct sockaddr_in6 rip6src;
uint8_t type;
KASSERT(af == AF_INET6);
if (proto == IPPROTO_ICMPV6) {
struct icmp6_hdr *icmp6;
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, *offp,
sizeof(*icmp6));
if (icmp6 == NULL)
return IPPROTO_DONE;
type = icmp6->icmp6_type;
} else
rip6stat_inc(rip6s_ipackets);
bzero(&rip6src, sizeof(rip6src));
rip6src.sin6_len = sizeof(struct sockaddr_in6);
rip6src.sin6_family = AF_INET6;
/* KAME hack: recover scopeid */
in6_recoverscope(&rip6src, &ip6->ip6_src);
key = &ip6->ip6_dst;
#if NPF > 0
if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
struct pf_divert *divert;
divert = pf_find_divert(m);
KASSERT(divert != NULL);
switch (divert->type) {
case PF_DIVERT_TO:
key = &divert->addr.v6;
break;
case PF_DIVERT_REPLY:
break;
default:
panic("%s: unknown divert type %d, mbuf %p, divert %p",
__func__, divert->type, m, divert);
}
}
#endif
SIMPLEQ_INIT(&inpcblist);
rw_enter_write(&rawin6pcbtable.inpt_notify);
mtx_enter(&rawin6pcbtable.inpt_mtx);
TAILQ_FOREACH(in6p, &rawin6pcbtable.inpt_queue, inp_queue) {
if (in6p->inp_socket->so_state & SS_CANTRCVMORE)
continue;
if (rtable_l2(in6p->inp_rtableid) !=
rtable_l2(m->m_pkthdr.ph_rtableid))
continue;
if (!(in6p->inp_flags & INP_IPV6))
continue;
if ((in6p->inp_ipv6.ip6_nxt || proto == IPPROTO_ICMPV6) &&
in6p->inp_ipv6.ip6_nxt != proto)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_laddr6) &&
!IN6_ARE_ADDR_EQUAL(&in6p->inp_laddr6, key))
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_faddr6) &&
!IN6_ARE_ADDR_EQUAL(&in6p->inp_faddr6, &ip6->ip6_src))
continue;
if (proto == IPPROTO_ICMPV6 && in6p->inp_icmp6filt) {
if (ICMP6_FILTER_WILLBLOCK(type, in6p->inp_icmp6filt))
continue;
}
if (proto != IPPROTO_ICMPV6 && in6p->inp_cksum6 != -1) {
rip6stat_inc(rip6s_isum);
/*
* Although in6_cksum() does not need the position of
* the checksum field for verification, enforce that it
* is located within the packet. Userland has given
* a checksum offset, a packet too short for that is
* invalid. Avoid overflow with user supplied offset.
*/
if (m->m_pkthdr.len < *offp + 2 ||
m->m_pkthdr.len - *offp - 2 < in6p->inp_cksum6 ||
in6_cksum(m, proto, *offp,
m->m_pkthdr.len - *offp)) {
rip6stat_inc(rip6s_badsum);
continue;
}
}
in_pcbref(in6p);
SIMPLEQ_INSERT_TAIL(&inpcblist, in6p, inp_notify);
}
mtx_leave(&rawin6pcbtable.inpt_mtx);
if (SIMPLEQ_EMPTY(&inpcblist)) {
struct counters_ref ref;
uint64_t *counters;
rw_exit_write(&rawin6pcbtable.inpt_notify);
if (proto != IPPROTO_ICMPV6) {
rip6stat_inc(rip6s_nosock);
if (m->m_flags & M_MCAST)
rip6stat_inc(rip6s_nosockmcast);
}
if (proto == IPPROTO_NONE || proto == IPPROTO_ICMPV6) {
m_freem(m);
} else {
int prvnxt = ip6_get_prevhdr(m, *offp);
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_NEXTHEADER, prvnxt);
}
counters = counters_enter(&ref, ip6counters);
counters[ip6s_delivered]--;
counters_leave(&ref, ip6counters);
return IPPROTO_DONE;
}
while ((in6p = SIMPLEQ_FIRST(&inpcblist)) != NULL) {
struct mbuf *n, *opts = NULL;
SIMPLEQ_REMOVE_HEAD(&inpcblist, inp_notify);
if (SIMPLEQ_EMPTY(&inpcblist))
n = m;
else
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n != NULL) {
if (in6p->inp_flags & IN6P_CONTROLOPTS)
ip6_savecontrol(in6p, n, &opts);
/* strip intermediate headers */
m_adj(n, *offp);
if (sbappendaddr(in6p->inp_socket,
&in6p->inp_socket->so_rcv,
sin6tosa(&rip6src), n, opts) == 0) {
/* should notify about lost packet */
m_freem(n);
m_freem(opts);
rip6stat_inc(rip6s_fullsock);
} else
sorwakeup(in6p->inp_socket);
}
in_pcbunref(in6p);
}
rw_exit_write(&rawin6pcbtable.inpt_notify);
return IPPROTO_DONE;
}
void
rip6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *d)
{
struct ip6_hdr *ip6;
struct ip6ctlparam *ip6cp = NULL;
struct sockaddr_in6 *sa6 = satosin6(sa);
const struct sockaddr_in6 *sa6_src = NULL;
void *cmdarg;
void (*notify)(struct inpcb *, int) = in_rtchange;
int nxt;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
if (PRC_IS_REDIRECT(cmd))
notify = in_rtchange, d = NULL;
else if (cmd == PRC_HOSTDEAD)
d = NULL;
else if (cmd == PRC_MSGSIZE)
; /* special code is present, see below */
else if (inet6ctlerrmap[cmd] == 0)
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
ip6 = ip6cp->ip6c_ip6;
cmdarg = ip6cp->ip6c_cmdarg;
sa6_src = ip6cp->ip6c_src;
nxt = ip6cp->ip6c_nxt;
} else {
ip6 = NULL;
cmdarg = NULL;
sa6_src = &sa6_any;
nxt = -1;
}
if (ip6 && cmd == PRC_MSGSIZE) {
int valid = 0;
struct inpcb *in6p;
/*
* Check to see if we have a valid raw IPv6 socket
* corresponding to the address in the ICMPv6 message
* payload, and the protocol (ip6_nxt) meets the socket.
* XXX chase extension headers, or pass final nxt value
* from icmp6_notify_error()
*/
in6p = in6_pcblookup(&rawin6pcbtable, &sa6->sin6_addr, 0,
&sa6_src->sin6_addr, 0, rdomain);
if (in6p && in6p->inp_ipv6.ip6_nxt &&
in6p->inp_ipv6.ip6_nxt == nxt)
valid = 1;
/*
* Depending on the value of "valid" and routing table
* size (mtudisc_{hi,lo}wat), we will:
* - recalculate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
in_pcbunref(in6p);
/*
* regardless of if we called icmp6_mtudisc_update(),
* we need to call in6_pcbnotify(), to notify path
* MTU change to the userland (2292bis-02), because
* some unconnected sockets may share the same
* destination and want to know the path MTU.
*/
}
in6_pcbnotify(&rawin6pcbtable, sa6, 0,
sa6_src, 0, rdomain, cmd, cmdarg, notify);
}
/*
* Generate IPv6 header and pass packet to ip6_output.
* Tack on options user may have setup with control call.
*/
int
rip6_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
struct mbuf *control)
{
struct in6_addr *dst;
struct ip6_hdr *ip6;
struct inpcb *in6p;
u_int plen = m->m_pkthdr.len;
int error = 0;
struct ip6_pktopts opt, *optp = NULL, *origoptp;
int type; /* for ICMPv6 output statistics only */
int priv = 0;
int flags;
in6p = sotoinpcb(so);
priv = 0;
if ((so->so_state & SS_PRIV) != 0)
priv = 1;
if (control) {
if ((error = ip6_setpktopts(control, &opt,
in6p->inp_outputopts6,
priv, so->so_proto->pr_protocol)) != 0)
goto bad;
optp = &opt;
} else
optp = in6p->inp_outputopts6;
if (dstaddr->sa_family != AF_INET6) {
error = EAFNOSUPPORT;
goto bad;
}
dst = &satosin6(dstaddr)->sin6_addr;
if (IN6_IS_ADDR_V4MAPPED(dst)) {
error = EADDRNOTAVAIL;
goto bad;
}
/*
* For an ICMPv6 packet, we should know its type and code
* to update statistics.
*/
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
struct icmp6_hdr *icmp6;
if (m->m_len < sizeof(struct icmp6_hdr) &&
(m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
error = ENOBUFS;
goto bad;
}
icmp6 = mtod(m, struct icmp6_hdr *);
type = icmp6->icmp6_type;
}
M_PREPEND(m, sizeof(*ip6), M_DONTWAIT);
if (!m) {
error = ENOBUFS;
goto bad;
}
ip6 = mtod(m, struct ip6_hdr *);
/*
* Next header might not be ICMP6 but use its pseudo header anyway.
*/
ip6->ip6_dst = *dst;
/* KAME hack: embed scopeid */
origoptp = in6p->inp_outputopts6;
in6p->inp_outputopts6 = optp;
if (in6_embedscope(&ip6->ip6_dst, satosin6(dstaddr), in6p) != 0) {
error = EINVAL;
goto bad;
}
in6p->inp_outputopts6 = origoptp;
/*
* Source address selection.
*/
{
struct in6_addr *in6a;
error = in6_pcbselsrc(&in6a, satosin6(dstaddr), in6p, optp);
if (error)
goto bad;
ip6->ip6_src = *in6a;
}
ip6->ip6_flow = in6p->inp_flowinfo & IPV6_FLOWINFO_MASK;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
#if 0 /* ip6_plen will be filled in ip6_output. */
ip6->ip6_plen = htons((u_short)plen);
#endif
ip6->ip6_nxt = in6p->inp_ipv6.ip6_nxt;
ip6->ip6_hlim = in6_selecthlim(in6p);
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
in6p->inp_cksum6 != -1) {
struct mbuf *n;
int off;
u_int16_t *sump;
int sumoff;
/* compute checksum */
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
off = offsetof(struct icmp6_hdr, icmp6_cksum);
else
off = in6p->inp_cksum6;
if (plen < 2 || plen - 2 < off) {
error = EINVAL;
goto bad;
}
off += sizeof(struct ip6_hdr);
n = m_pulldown(m, off, sizeof(*sump), &sumoff);
if (n == NULL) {
m = NULL;
error = ENOBUFS;
goto bad;
}
sump = (u_int16_t *)(mtod(n, caddr_t) + sumoff);
*sump = 0;
*sump = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
}
flags = 0;
if (in6p->inp_flags & IN6P_MINMTU)
flags |= IPV6_MINMTU;
/* force routing table */
m->m_pkthdr.ph_rtableid = in6p->inp_rtableid;
#if NPF > 0
if (in6p->inp_socket->so_state & SS_ISCONNECTED &&
so->so_proto->pr_protocol != IPPROTO_ICMPV6)
pf_mbuf_link_inpcb(m, in6p);
#endif
error = ip6_output(m, optp, &in6p->inp_route6, flags,
in6p->inp_moptions6, in6p);
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
icmp6stat_inc(icp6s_outhist + type);
} else
rip6stat_inc(rip6s_opackets);
goto freectl;
bad:
m_freem(m);
freectl:
if (control) {
ip6_clearpktopts(&opt, -1);
m_freem(control);
}
return (error);
}
/*
* Raw IPv6 socket option processing.
*/
int
rip6_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
#ifdef MROUTING
int error;
#endif
switch (level) {
case IPPROTO_IPV6:
switch (optname) {
#ifdef MROUTING
case MRT6_INIT:
case MRT6_DONE:
case MRT6_ADD_MIF:
case MRT6_DEL_MIF:
case MRT6_ADD_MFC:
case MRT6_DEL_MFC:
if (op == PRCO_SETOPT) {
error = ip6_mrouter_set(optname, so, m);
} else if (op == PRCO_GETOPT)
error = ip6_mrouter_get(optname, so, m);
else
error = EINVAL;
return (error);
#endif
case IPV6_CHECKSUM:
return (ip6_raw_ctloutput(op, so, level, optname, m));
default:
return (ip6_ctloutput(op, so, level, optname, m));
}
case IPPROTO_ICMPV6:
/*
* XXX: is it better to call icmp6_ctloutput() directly
* from protosw?
*/
return (icmp6_ctloutput(op, so, level, optname, m));
default:
return EINVAL;
}
}
extern u_long rip6_sendspace;
extern u_long rip6_recvspace;
int
rip6_attach(struct socket *so, int proto)
{
struct inpcb *in6p;
int error;
if (so->so_pcb)
panic("%s", __func__);
if ((so->so_state & SS_PRIV) == 0)
return (EACCES);
if (proto < 0 || proto >= IPPROTO_MAX)
return EPROTONOSUPPORT;
if ((error = soreserve(so, rip6_sendspace, rip6_recvspace)))
return error;
NET_ASSERT_LOCKED();
if ((error = in_pcballoc(so, &rawin6pcbtable)))
return error;
in6p = sotoinpcb(so);
in6p->inp_ipv6.ip6_nxt = proto;
in6p->inp_cksum6 = -1;
in6p->inp_icmp6filt = malloc(sizeof(struct icmp6_filter),
M_PCB, M_NOWAIT);
if (in6p->inp_icmp6filt == NULL) {
in_pcbdetach(in6p);
return ENOMEM;
}
ICMP6_FILTER_SETPASSALL(in6p->inp_icmp6filt);
return 0;
}
int
rip6_detach(struct socket *so)
{
struct inpcb *in6p = sotoinpcb(so);
soassertlocked(so);
if (in6p == NULL)
panic("%s", __func__);
#ifdef MROUTING
if (so == ip6_mrouter[in6p->inp_rtableid])
ip6_mrouter_done(so);
#endif
free(in6p->inp_icmp6filt, M_PCB, sizeof(struct icmp6_filter));
in6p->inp_icmp6filt = NULL;
in_pcbdetach(in6p);
return (0);
}
int
rip6_bind(struct socket *so, struct mbuf *nam, struct proc *p)
{
struct inpcb *in6p = sotoinpcb(so);
struct sockaddr_in6 *addr;
int error;
soassertlocked(so);
if ((error = in6_nam2sin6(nam, &addr)))
return (error);
/*
* Make sure to not enter in_pcblookup_local(), local ports
* are non-sensical for raw sockets.
*/
addr->sin6_port = 0;
if ((error = in6_pcbaddrisavail(in6p, addr, 0, p)))
return (error);
in6p->inp_laddr6 = addr->sin6_addr;
return (0);
}
int
rip6_connect(struct socket *so, struct mbuf *nam)
{
struct inpcb *in6p = sotoinpcb(so);
struct sockaddr_in6 *addr;
struct in6_addr *in6a = NULL;
int error;
soassertlocked(so);
if ((error = in6_nam2sin6(nam, &addr)))
return (error);
/* Source address selection. XXX: need pcblookup? */
error = in6_pcbselsrc(&in6a, addr, in6p, in6p->inp_outputopts6);
if (error)
return (error);
in6p->inp_laddr6 = *in6a;
in6p->inp_faddr6 = addr->sin6_addr;
soisconnected(so);
return (0);
}
int
rip6_disconnect(struct socket *so)
{
struct inpcb *in6p = sotoinpcb(so);
soassertlocked(so);
if ((so->so_state & SS_ISCONNECTED) == 0)
return (ENOTCONN);
in6p->inp_faddr6 = in6addr_any;
so->so_state &= ~SS_ISCONNECTED; /* XXX */
return (0);
}
int
rip6_shutdown(struct socket *so)
{
/*
* Mark the connection as being incapable of further input.
*/
soassertlocked(so);
socantsendmore(so);
return (0);
}
int
rip6_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct inpcb *in6p = sotoinpcb(so);
struct sockaddr_in6 dst;
int error;
soassertlocked(so);
/*
* Ship a packet out. The appropriate raw output
* routine handles any messaging necessary.
*/
/* always copy sockaddr to avoid overwrites */
memset(&dst, 0, sizeof(dst));
dst.sin6_family = AF_INET6;
dst.sin6_len = sizeof(dst);
if (so->so_state & SS_ISCONNECTED) {
if (nam) {
error = EISCONN;
goto out;
}
dst.sin6_addr = in6p->inp_faddr6;
} else {
struct sockaddr_in6 *addr6;
if (nam == NULL) {
error = ENOTCONN;
goto out;
}
if ((error = in6_nam2sin6(nam, &addr6)))
goto out;
dst.sin6_addr = addr6->sin6_addr;
dst.sin6_scope_id = addr6->sin6_scope_id;
}
error = rip6_output(m, so, sin6tosa(&dst), control);
control = NULL;
m = NULL;
out:
m_freem(control);
m_freem(m);
return (error);
}
int
rip6_abort(struct socket *so)
{
struct inpcb *in6p = sotoinpcb(so);
soassertlocked(so);
soisdisconnected(so);
#ifdef MROUTING
if (so == ip6_mrouter[in6p->inp_rtableid])
ip6_mrouter_done(so);
#endif
free(in6p->inp_icmp6filt, M_PCB, sizeof(struct icmp6_filter));
in6p->inp_icmp6filt = NULL;
in_pcbdetach(in6p);
return (0);
}
int
rip6_sysctl_rip6stat(void *oldp, size_t *oldplen, void *newp)
{
struct rip6stat rip6stat;
CTASSERT(sizeof(rip6stat) == rip6s_ncounters * sizeof(uint64_t));
counters_read(rip6counters, (uint64_t *)&rip6stat, rip6s_ncounters);
return (sysctl_rdstruct(oldp, oldplen, newp,
&rip6stat, sizeof(rip6stat)));
}
int
rip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return ENOTDIR;
switch (name[0]) {
case RIPV6CTL_STATS:
return (rip6_sysctl_rip6stat(oldp, oldlenp, newp));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
7
1
11
33
1
32
6
1
7
7
4
3
7
7
7
30
30
1
1
1
28
1
5
5
5
2
5
2
3
11
5
5
62
46
3
1
1
3
19
2
4
2
2
8
9
12
7
7
1
2
3
15
26
8
2
22
16
7
18
8
8
1
20
21
2
21
21
21
12
32
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
/* $OpenBSD: com.c,v 1.175 2022/01/11 11:51:14 uaa Exp $ */
/* $NetBSD: com.c,v 1.82.4.1 1996/06/02 09:08:00 mrg Exp $ */
/*
* Copyright (c) 1997 - 1999, Jason Downs. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*-
* Copyright (c) 1993, 1994, 1995, 1996
* Charles M. Hannum. All rights reserved.
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)com.c 7.5 (Berkeley) 5/16/91
*/
/*
* COM driver, based on HP dca driver
* uses National Semiconductor NS16450/NS16550AF UART
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/selinfo.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/vnode.h>
#ifdef DDB
#include <ddb/db_var.h>
#endif
#include <machine/bus.h>
#include <machine/intr.h>
#define COM_CONSOLE
#include <dev/cons.h>
#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>
#include <dev/ic/ns16550reg.h>
#define com_lcr com_cfcr
cdev_decl(com);
static u_char tiocm_xxx2mcr(int);
void compwroff(struct com_softc *);
void cominit(bus_space_tag_t, bus_space_handle_t, int, int);
int com_is_console(bus_space_tag_t, bus_addr_t);
struct cfdriver com_cd = {
NULL, "com", DV_TTY
};
int comdefaultrate = TTYDEF_SPEED;
#ifdef COM_CONSOLE
int comconsfreq;
int comconsrate = TTYDEF_SPEED;
bus_addr_t comconsaddr = 0;
int comconsattached;
bus_space_tag_t comconsiot;
bus_space_handle_t comconsioh;
int comconsunit;
tcflag_t comconscflag = TTYDEF_CFLAG;
#endif
int commajor;
#define DEVUNIT(x) (minor(x) & 0x7f)
#define DEVCUA(x) (minor(x) & 0x80)
int
comspeed(long freq, long speed)
{
#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */
int x, err;
if (speed == 0)
return 0;
if (speed < 0)
return -1;
x = divrnd((freq / 16), speed);
if (x <= 0)
return -1;
err = divrnd((quad_t)freq * 1000 / 16, speed * x) - 1000;
if (err < 0)
err = -err;
if (err > COM_TOLERANCE)
return -1;
return x;
#undef divrnd
}
#ifdef COM_CONSOLE
int
comprobe1(bus_space_tag_t iot, bus_space_handle_t ioh)
{
int i, k;
/* force access to id reg */
bus_space_write_1(iot, ioh, com_lcr, 0);
bus_space_write_1(iot, ioh, com_iir, 0);
for (i = 0; i < 32; i++) {
k = bus_space_read_1(iot, ioh, com_iir);
if (k & 0x38) {
bus_space_read_1(iot, ioh, com_data); /* cleanup */
} else
break;
}
if (i >= 32)
return 0;
return 1;
}
#endif
int
com_detach(struct device *self, int flags)
{
struct com_softc *sc = (struct com_softc *)self;
int maj, mn;
sc->sc_swflags |= COM_SW_DEAD;
/* Locate the major number. */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == comopen)
break;
/* Nuke the vnodes for any open instances. */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
/* XXX a symbolic constant for the cua bit would be nicer. */
mn |= 0x80;
vdevgone(maj, mn, mn, VCHR);
timeout_del(&sc->sc_dtr_tmo);
timeout_del(&sc->sc_diag_tmo);
softintr_disestablish(sc->sc_si);
/* Detach and free the tty. */
if (sc->sc_tty) {
ttyfree(sc->sc_tty);
}
return (0);
}
int
com_activate(struct device *self, int act)
{
struct com_softc *sc = (struct com_softc *)self;
int s, rv = 0;
switch (act) {
case DVACT_SUSPEND:
if (timeout_del(&sc->sc_dtr_tmo)) {
/* Make sure DTR gets raised upon resume. */
SET(sc->sc_mcr, MCR_DTR | MCR_RTS);
}
timeout_del(&sc->sc_diag_tmo);
break;
case DVACT_RESUME:
com_resume(sc);
break;
case DVACT_DEACTIVATE:
if (sc->sc_hwflags & COM_HW_CONSOLE) {
rv = EBUSY;
break;
}
s = spltty();
if (sc->disable != NULL && sc->enabled != 0) {
(*sc->disable)(sc);
sc->enabled = 0;
}
splx(s);
break;
}
return (rv);
}
int
comopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = DEVUNIT(dev);
struct com_softc *sc;
struct tty *tp;
int s;
int error = 0;
if (unit >= com_cd.cd_ndevs)
return ENXIO;
sc = com_cd.cd_devs[unit];
if (!sc)
return ENXIO;
s = spltty();
if (!sc->sc_tty) {
tp = sc->sc_tty = ttymalloc(1000000);
} else
tp = sc->sc_tty;
splx(s);
tp->t_oproc = comstart;
tp->t_param = comparam;
tp->t_dev = dev;
if (!ISSET(tp->t_state, TS_ISOPEN)) {
SET(tp->t_state, TS_WOPEN);
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
#ifdef COM_CONSOLE
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
tp->t_cflag = comconscflag;
tp->t_ispeed = tp->t_ospeed = comconsrate;
} else
#endif
{
tp->t_cflag = TTYDEF_CFLAG;
tp->t_ispeed = tp->t_ospeed = comdefaultrate;
}
if (ISSET(sc->sc_swflags, COM_SW_CLOCAL))
SET(tp->t_cflag, CLOCAL);
if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS))
SET(tp->t_cflag, CRTSCTS);
if (ISSET(sc->sc_swflags, COM_SW_MDMBUF))
SET(tp->t_cflag, MDMBUF);
tp->t_lflag = TTYDEF_LFLAG;
s = spltty();
sc->sc_initialize = 1;
comparam(tp, &tp->t_termios);
ttsetwater(tp);
sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER;
sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE;
/*
* Wake up the sleepy heads.
*/
if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
switch (sc->sc_uarttype) {
case COM_UART_ST16650:
case COM_UART_ST16650V2:
com_write_reg(sc, com_lcr, LCR_EFR);
com_write_reg(sc, com_efr, EFR_ECB);
com_write_reg(sc, com_ier, 0);
com_write_reg(sc, com_efr, 0);
com_write_reg(sc, com_lcr, 0);
break;
case COM_UART_TI16750:
com_write_reg(sc, com_ier, 0);
break;
case COM_UART_XR17V35X:
com_write_reg(sc, UART_EXAR_SLEEP, 0);
break;
}
}
if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) {
u_int8_t fifo = FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST;
u_int8_t lcr;
if (tp->t_ispeed <= 1200)
fifo |= FIFO_TRIGGER_1;
else if (tp->t_ispeed <= 38400)
fifo |= FIFO_TRIGGER_4;
else
fifo |= FIFO_TRIGGER_8;
if (sc->sc_uarttype == COM_UART_TI16750) {
fifo |= FIFO_ENABLE_64BYTE;
lcr = com_read_reg(sc, com_lcr);
com_write_reg(sc, com_lcr,
lcr | LCR_DLAB);
}
/*
* (Re)enable and drain FIFOs.
*
* Certain SMC chips cause problems if the FIFOs are
* enabled while input is ready. Turn off the FIFO
* if necessary to clear the input. Test the input
* ready bit after enabling the FIFOs to handle races
* between enabling and fresh input.
*
* Set the FIFO threshold based on the receive speed.
*/
for (;;) {
com_write_reg(sc, com_fifo, 0);
delay(100);
(void) com_read_reg(sc, com_data);
com_write_reg(sc, com_fifo, fifo |
FIFO_RCV_RST | FIFO_XMT_RST);
delay(100);
if(!ISSET(com_read_reg(sc,
com_lsr), LSR_RXRDY))
break;
}
if (sc->sc_uarttype == COM_UART_TI16750)
com_write_reg(sc, com_lcr, lcr);
}
/* Flush any pending I/O. */
while (ISSET(com_read_reg(sc, com_lsr), LSR_RXRDY))
(void) com_read_reg(sc, com_data);
/* You turn me on, baby! */
sc->sc_mcr = MCR_DTR | MCR_RTS;
if (!ISSET(sc->sc_hwflags, COM_HW_NOIEN))
SET(sc->sc_mcr, MCR_IENABLE);
com_write_reg(sc, com_mcr, sc->sc_mcr);
sc->sc_ier = IER_ERXRDY | IER_ERLS | IER_EMSC;
com_write_reg(sc, com_ier, sc->sc_ier);
sc->sc_msr = com_read_reg(sc, com_msr);
if (ISSET(sc->sc_swflags, COM_SW_SOFTCAR) || DEVCUA(dev) ||
ISSET(sc->sc_msr, MSR_DCD) || ISSET(tp->t_cflag, MDMBUF))
SET(tp->t_state, TS_CARR_ON);
else
CLR(tp->t_state, TS_CARR_ON);
} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
return EBUSY;
else
s = spltty();
if (DEVCUA(dev)) {
if (ISSET(tp->t_state, TS_ISOPEN)) {
/* Ah, but someone already is dialed in... */
splx(s);
return EBUSY;
}
sc->sc_cua = 1; /* We go into CUA mode. */
} else {
/* tty (not cua) device; wait for carrier if necessary. */
if (ISSET(flag, O_NONBLOCK)) {
if (sc->sc_cua) {
/* Opening TTY non-blocking... but the CUA is busy. */
splx(s);
return EBUSY;
}
} else {
while (sc->sc_cua ||
(!ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(tp->t_state, TS_CARR_ON))) {
SET(tp->t_state, TS_WOPEN);
error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH, ttopen);
/*
* If TS_WOPEN has been reset, that means the cua device
* has been closed. We don't want to fail in that case,
* so just go around again.
*/
if (error && ISSET(tp->t_state, TS_WOPEN)) {
CLR(tp->t_state, TS_WOPEN);
if (!sc->sc_cua && !ISSET(tp->t_state, TS_ISOPEN))
compwroff(sc);
splx(s);
return error;
}
}
}
}
splx(s);
return (*linesw[tp->t_line].l_open)(dev, tp, p);
}
int
comclose(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = DEVUNIT(dev);
struct com_softc *sc = com_cd.cd_devs[unit];
struct tty *tp = sc->sc_tty;
int s;
#ifdef COM_CONSOLE
/* XXX This is for cons.c. */
if (!ISSET(tp->t_state, TS_ISOPEN))
return 0;
#endif
if(sc->sc_swflags & COM_SW_DEAD)
return 0;
(*linesw[tp->t_line].l_close)(tp, flag, p);
s = spltty();
if (ISSET(tp->t_state, TS_WOPEN)) {
/* tty device is waiting for carrier; drop dtr then re-raise */
CLR(sc->sc_mcr, MCR_DTR | MCR_RTS);
com_write_reg(sc, com_mcr, sc->sc_mcr);
timeout_add_sec(&sc->sc_dtr_tmo, 2);
} else {
/* no one else waiting; turn off the uart */
compwroff(sc);
}
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
sc->sc_cua = 0;
splx(s);
ttyclose(tp);
#ifdef COM_CONSOLE
#ifdef notyet /* XXXX */
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
ttyfree(tp);
sc->sc_tty = 0;
}
#endif
#endif
return 0;
}
void
compwroff(struct com_softc *sc)
{
struct tty *tp = sc->sc_tty;
CLR(sc->sc_lcr, LCR_SBREAK);
com_write_reg(sc, com_lcr, sc->sc_lcr);
com_write_reg(sc, com_ier, 0);
if (ISSET(tp->t_cflag, HUPCL) &&
!ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) {
/* XXX perhaps only clear DTR */
sc->sc_mcr = 0;
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
/*
* Turn FIFO off; enter sleep mode if possible.
*/
com_write_reg(sc, com_fifo, 0);
delay(100);
if (ISSET(com_read_reg(sc, com_lsr), LSR_RXRDY))
(void) com_read_reg(sc, com_data);
delay(100);
com_write_reg(sc, com_fifo,
FIFO_RCV_RST | FIFO_XMT_RST);
if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
switch (sc->sc_uarttype) {
case COM_UART_ST16650:
case COM_UART_ST16650V2:
com_write_reg(sc, com_lcr, LCR_EFR);
com_write_reg(sc, com_efr, EFR_ECB);
com_write_reg(sc, com_ier, IER_SLEEP);
com_write_reg(sc, com_lcr, 0);
break;
case COM_UART_TI16750:
com_write_reg(sc, com_ier, IER_SLEEP);
break;
case COM_UART_XR17V35X:
com_write_reg(sc, UART_EXAR_SLEEP, 0xff);
break;
}
}
}
void
com_resume(struct com_softc *sc)
{
struct tty *tp = sc->sc_tty;
int ospeed;
if (!tp || !ISSET(tp->t_state, TS_ISOPEN)) {
#ifdef COM_CONSOLE
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
cominit(comconsiot, comconsioh, comconsrate,
comconsfreq);
#endif
return;
}
/*
* Wake up the sleepy heads.
*/
if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
switch (sc->sc_uarttype) {
case COM_UART_ST16650:
case COM_UART_ST16650V2:
com_write_reg(sc, com_lcr, LCR_EFR);
com_write_reg(sc, com_efr, EFR_ECB);
com_write_reg(sc, com_ier, 0);
com_write_reg(sc, com_efr, 0);
com_write_reg(sc, com_lcr, 0);
break;
case COM_UART_TI16750:
com_write_reg(sc, com_ier, 0);
break;
case COM_UART_XR17V35X:
com_write_reg(sc, UART_EXAR_SLEEP, 0);
break;
}
}
ospeed = comspeed(sc->sc_frequency, tp->t_ospeed);
if (ospeed != 0) {
com_write_reg(sc, com_lcr, sc->sc_lcr | LCR_DLAB);
com_write_reg(sc, com_dlbl, ospeed);
com_write_reg(sc, com_dlbh, ospeed >> 8);
com_write_reg(sc, com_lcr, sc->sc_lcr);
} else {
com_write_reg(sc, com_lcr, sc->sc_lcr);
}
if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) {
u_int8_t fifo = FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST;
u_int8_t lcr;
if (tp->t_ispeed <= 1200)
fifo |= FIFO_TRIGGER_1;
else if (tp->t_ispeed <= 38400)
fifo |= FIFO_TRIGGER_4;
else
fifo |= FIFO_TRIGGER_8;
if (sc->sc_uarttype == COM_UART_TI16750) {
fifo |= FIFO_ENABLE_64BYTE;
lcr = com_read_reg(sc, com_lcr);
com_write_reg(sc, com_lcr,
lcr | LCR_DLAB);
}
/*
* (Re)enable and drain FIFOs.
*
* Certain SMC chips cause problems if the FIFOs are
* enabled while input is ready. Turn off the FIFO
* if necessary to clear the input. Test the input
* ready bit after enabling the FIFOs to handle races
* between enabling and fresh input.
*
* Set the FIFO threshold based on the receive speed.
*/
for (;;) {
com_write_reg(sc, com_fifo, 0);
delay(100);
(void) com_read_reg(sc, com_data);
com_write_reg(sc, com_fifo, fifo |
FIFO_RCV_RST | FIFO_XMT_RST);
delay(100);
if(!ISSET(com_read_reg(sc,
com_lsr), LSR_RXRDY))
break;
}
if (sc->sc_uarttype == COM_UART_TI16750)
com_write_reg(sc, com_lcr, lcr);
}
/* You turn me on, baby! */
com_write_reg(sc, com_mcr, sc->sc_mcr);
com_write_reg(sc, com_ier, sc->sc_ier);
}
void
com_raisedtr(void *arg)
{
struct com_softc *sc = arg;
SET(sc->sc_mcr, MCR_DTR | MCR_RTS);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
int
comread(dev_t dev, struct uio *uio, int flag)
{
struct com_softc *sc = com_cd.cd_devs[DEVUNIT(dev)];
struct tty *tp = sc->sc_tty;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
int
comwrite(dev_t dev, struct uio *uio, int flag)
{
struct com_softc *sc = com_cd.cd_devs[DEVUNIT(dev)];
struct tty *tp = sc->sc_tty;
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
struct tty *
comtty(dev_t dev)
{
struct com_softc *sc = com_cd.cd_devs[DEVUNIT(dev)];
struct tty *tp = sc->sc_tty;
return (tp);
}
static u_char
tiocm_xxx2mcr(int data)
{
u_char m = 0;
if (ISSET(data, TIOCM_DTR))
SET(m, MCR_DTR);
if (ISSET(data, TIOCM_RTS))
SET(m, MCR_RTS);
return m;
}
int
comioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
int unit = DEVUNIT(dev);
struct com_softc *sc = com_cd.cd_devs[unit];
struct tty *tp = sc->sc_tty;
int error;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return error;
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return error;
switch (cmd) {
case TIOCSBRK:
SET(sc->sc_lcr, LCR_SBREAK);
com_write_reg(sc, com_lcr, sc->sc_lcr);
break;
case TIOCCBRK:
CLR(sc->sc_lcr, LCR_SBREAK);
com_write_reg(sc, com_lcr, sc->sc_lcr);
break;
case TIOCSDTR:
SET(sc->sc_mcr, sc->sc_dtr);
com_write_reg(sc, com_mcr, sc->sc_mcr);
break;
case TIOCCDTR:
CLR(sc->sc_mcr, sc->sc_dtr);
com_write_reg(sc, com_mcr, sc->sc_mcr);
break;
case TIOCMSET:
CLR(sc->sc_mcr, MCR_DTR | MCR_RTS);
case TIOCMBIS:
SET(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data));
com_write_reg(sc, com_mcr, sc->sc_mcr);
break;
case TIOCMBIC:
CLR(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data));
com_write_reg(sc, com_mcr, sc->sc_mcr);
break;
case TIOCMGET: {
u_char m;
int bits = 0;
m = sc->sc_mcr;
if (ISSET(m, MCR_DTR))
SET(bits, TIOCM_DTR);
if (ISSET(m, MCR_RTS))
SET(bits, TIOCM_RTS);
m = sc->sc_msr;
if (ISSET(m, MSR_DCD))
SET(bits, TIOCM_CD);
if (ISSET(m, MSR_CTS))
SET(bits, TIOCM_CTS);
if (ISSET(m, MSR_DSR))
SET(bits, TIOCM_DSR);
if (ISSET(m, MSR_RI | MSR_TERI))
SET(bits, TIOCM_RI);
if (com_read_reg(sc, com_ier))
SET(bits, TIOCM_LE);
*(int *)data = bits;
break;
}
case TIOCGFLAGS: {
int driverbits, userbits = 0;
driverbits = sc->sc_swflags;
if (ISSET(driverbits, COM_SW_SOFTCAR))
SET(userbits, TIOCFLAG_SOFTCAR);
if (ISSET(driverbits, COM_SW_CLOCAL))
SET(userbits, TIOCFLAG_CLOCAL);
if (ISSET(driverbits, COM_SW_CRTSCTS))
SET(userbits, TIOCFLAG_CRTSCTS);
if (ISSET(driverbits, COM_SW_MDMBUF))
SET(userbits, TIOCFLAG_MDMBUF);
if (ISSET(driverbits, COM_SW_PPS))
SET(userbits, TIOCFLAG_PPS);
*(int *)data = userbits;
break;
}
case TIOCSFLAGS: {
int userbits, driverbits = 0;
error = suser(p);
if (error != 0)
return(EPERM);
userbits = *(int *)data;
if (ISSET(userbits, TIOCFLAG_SOFTCAR) ||
ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
SET(driverbits, COM_SW_SOFTCAR);
if (ISSET(userbits, TIOCFLAG_CLOCAL))
SET(driverbits, COM_SW_CLOCAL);
if (ISSET(userbits, TIOCFLAG_CRTSCTS))
SET(driverbits, COM_SW_CRTSCTS);
if (ISSET(userbits, TIOCFLAG_MDMBUF))
SET(driverbits, COM_SW_MDMBUF);
if (ISSET(userbits, TIOCFLAG_PPS))
SET(driverbits, COM_SW_PPS);
sc->sc_swflags = driverbits;
break;
}
default:
return ENOTTY;
}
return 0;
}
/* already called at spltty */
int
comparam(struct tty *tp, struct termios *t)
{
struct com_softc *sc = com_cd.cd_devs[DEVUNIT(tp->t_dev)];
int ospeed = comspeed(sc->sc_frequency, t->c_ospeed);
u_char lcr;
tcflag_t oldcflag;
/* Check requested parameters. */
if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
return EINVAL;
lcr = ISSET(sc->sc_lcr, LCR_SBREAK);
switch (ISSET(t->c_cflag, CSIZE)) {
case CS5:
SET(lcr, LCR_5BITS);
break;
case CS6:
SET(lcr, LCR_6BITS);
break;
case CS7:
SET(lcr, LCR_7BITS);
break;
case CS8:
SET(lcr, LCR_8BITS);
break;
}
if (ISSET(t->c_cflag, PARENB)) {
SET(lcr, LCR_PENAB);
if (!ISSET(t->c_cflag, PARODD))
SET(lcr, LCR_PEVEN);
}
if (ISSET(t->c_cflag, CSTOPB))
SET(lcr, LCR_STOPB);
sc->sc_lcr = lcr;
if (ospeed == 0) {
CLR(sc->sc_mcr, MCR_DTR);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
/*
* Set the FIFO threshold based on the receive speed, if we are
* changing it.
*/
if (sc->sc_initialize || (tp->t_ispeed != t->c_ispeed)) {
sc->sc_initialize = 0;
if (ospeed != 0) {
/*
* Make sure the transmit FIFO is empty before
* proceeding. If we don't do this, some revisions
* of the UART will hang. Interestingly enough,
* even if we do this while the last character is
* still being pushed out, they don't hang. This
* seems good enough.
*/
while (ISSET(tp->t_state, TS_BUSY)) {
int error;
++sc->sc_halt;
error = ttysleep(tp, &tp->t_outq,
TTOPRI | PCATCH, "comprm");
--sc->sc_halt;
if (error) {
comstart(tp);
return (error);
}
}
com_write_reg(sc, com_lcr, lcr | LCR_DLAB);
com_write_reg(sc, com_dlbl, ospeed);
com_write_reg(sc, com_dlbh, ospeed >> 8);
com_write_reg(sc, com_lcr, lcr);
SET(sc->sc_mcr, MCR_DTR);
com_write_reg(sc, com_mcr, sc->sc_mcr);
} else
com_write_reg(sc, com_lcr, lcr);
if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) {
if (sc->sc_uarttype == COM_UART_TI16750) {
com_write_reg(sc, com_lcr,
lcr | LCR_DLAB);
com_write_reg(sc, com_fifo,
FIFO_ENABLE | FIFO_ENABLE_64BYTE |
(t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8));
com_write_reg(sc, com_lcr, lcr);
} else
com_write_reg(sc, com_fifo,
FIFO_ENABLE |
(t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8));
}
} else
com_write_reg(sc, com_lcr, lcr);
/* When not using CRTSCTS, RTS follows DTR. */
if (!ISSET(t->c_cflag, CRTSCTS)) {
if (ISSET(sc->sc_mcr, MCR_DTR)) {
if (!ISSET(sc->sc_mcr, MCR_RTS)) {
SET(sc->sc_mcr, MCR_RTS);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
} else {
if (ISSET(sc->sc_mcr, MCR_RTS)) {
CLR(sc->sc_mcr, MCR_RTS);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
}
sc->sc_dtr = MCR_DTR | MCR_RTS;
} else
sc->sc_dtr = MCR_DTR;
/* and copy to tty */
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
oldcflag = tp->t_cflag;
tp->t_cflag = t->c_cflag;
/*
* If DCD is off and MDMBUF is changed, ask the tty layer if we should
* stop the device.
*/
if (!ISSET(sc->sc_msr, MSR_DCD) &&
!ISSET(sc->sc_swflags, COM_SW_SOFTCAR) &&
ISSET(oldcflag, MDMBUF) != ISSET(tp->t_cflag, MDMBUF) &&
(*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
CLR(sc->sc_mcr, sc->sc_dtr);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
/* Just to be sure... */
comstart(tp);
return 0;
}
void
comstart(struct tty *tp)
{
struct com_softc *sc = com_cd.cd_devs[DEVUNIT(tp->t_dev)];
int s;
s = spltty();
if (ISSET(tp->t_state, TS_BUSY))
goto out;
if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
goto stopped;
if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, MSR_CTS))
goto stopped;
ttwakeupwr(tp);
if (tp->t_outq.c_cc == 0)
goto stopped;
SET(tp->t_state, TS_BUSY);
/* Enable transmit completion interrupts. */
if (!ISSET(sc->sc_ier, IER_ETXRDY)) {
SET(sc->sc_ier, IER_ETXRDY);
com_write_reg(sc, com_ier, sc->sc_ier);
}
if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) {
u_char buffer[256]; /* largest fifo */
int i, n;
n = q_to_b(&tp->t_outq, buffer,
min(sc->sc_fifolen, sizeof buffer));
for (i = 0; i < n; i++) {
com_write_reg(sc, com_data, buffer[i]);
}
bzero(buffer, n);
} else if (tp->t_outq.c_cc != 0)
com_write_reg(sc, com_data, getc(&tp->t_outq));
out:
splx(s);
return;
stopped:
if (ISSET(sc->sc_ier, IER_ETXRDY)) {
CLR(sc->sc_ier, IER_ETXRDY);
com_write_reg(sc, com_ier, sc->sc_ier);
}
splx(s);
}
/*
* Stop output on a line.
*/
int
comstop(struct tty *tp, int flag)
{
int s;
s = spltty();
if (ISSET(tp->t_state, TS_BUSY))
if (!ISSET(tp->t_state, TS_TTSTOP))
SET(tp->t_state, TS_FLUSH);
splx(s);
return 0;
}
void
comdiag(void *arg)
{
struct com_softc *sc = arg;
int overflows, floods;
int s;
s = spltty();
sc->sc_errors = 0;
overflows = sc->sc_overflows;
sc->sc_overflows = 0;
floods = sc->sc_floods;
sc->sc_floods = 0;
splx(s);
log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n",
sc->sc_dev.dv_xname,
overflows, overflows == 1 ? "" : "s",
floods, floods == 1 ? "" : "s");
}
void
comsoft(void *arg)
{
struct com_softc *sc = (struct com_softc *)arg;
struct tty *tp;
u_char *ibufp;
u_char *ibufend;
int c;
int s;
static int lsrmap[8] = {
0, TTY_PE,
TTY_FE, TTY_PE|TTY_FE,
TTY_FE, TTY_PE|TTY_FE,
TTY_FE, TTY_PE|TTY_FE
};
if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf)
return;
tp = sc->sc_tty;
s = spltty();
ibufp = sc->sc_ibuf;
ibufend = sc->sc_ibufp;
if (ibufp == ibufend) {
splx(s);
return;
}
sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
sc->sc_ibufs[1] : sc->sc_ibufs[0];
sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER;
sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE;
if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
splx(s);
return;
}
if (ISSET(tp->t_cflag, CRTSCTS) &&
!ISSET(sc->sc_mcr, MCR_RTS)) {
/* XXX */
SET(sc->sc_mcr, MCR_RTS);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
splx(s);
while (ibufp < ibufend) {
c = *ibufp++;
if (ISSET(*ibufp, LSR_OE)) {
sc->sc_overflows++;
if (sc->sc_errors++ == 0)
timeout_add_sec(&sc->sc_diag_tmo, 60);
}
/* This is ugly, but fast. */
c |= lsrmap[(*ibufp++ & (LSR_BI|LSR_FE|LSR_PE)) >> 2];
(*linesw[tp->t_line].l_rint)(c, tp);
}
}
int
comintr(void *arg)
{
struct com_softc *sc = arg;
struct tty *tp;
u_char lsr, data, msr, delta;
if (!sc->sc_tty)
return (0); /* Can't do squat. */
if (ISSET(com_read_reg(sc, com_iir), IIR_NOPEND))
return (0);
tp = sc->sc_tty;
for (;;) {
lsr = com_read_reg(sc, com_lsr);
if (ISSET(lsr, LSR_RXRDY)) {
u_char *p = sc->sc_ibufp;
softintr_schedule(sc->sc_si);
do {
data = com_read_reg(sc, com_data);
if (ISSET(lsr, LSR_BI)) {
#if defined(COM_CONSOLE) && defined(DDB)
if (ISSET(sc->sc_hwflags,
COM_HW_CONSOLE)) {
if (db_console)
db_enter();
goto next;
}
#endif
data = 0;
}
if (p >= sc->sc_ibufend) {
sc->sc_floods++;
if (sc->sc_errors++ == 0)
timeout_add_sec(&sc->sc_diag_tmo, 60);
} else {
*p++ = data;
*p++ = lsr;
if (p == sc->sc_ibufhigh &&
ISSET(tp->t_cflag, CRTSCTS)) {
/* XXX */
CLR(sc->sc_mcr, MCR_RTS);
com_write_reg(sc, com_mcr,
sc->sc_mcr);
}
}
#if defined(COM_CONSOLE) && defined(DDB)
next:
#endif
lsr = com_read_reg(sc, com_lsr);
} while (ISSET(lsr, LSR_RXRDY));
sc->sc_ibufp = p;
}
msr = com_read_reg(sc, com_msr);
if (msr != sc->sc_msr) {
delta = msr ^ sc->sc_msr;
ttytstamp(tp, sc->sc_msr & MSR_CTS, msr & MSR_CTS,
sc->sc_msr & MSR_DCD, msr & MSR_DCD);
sc->sc_msr = msr;
if (ISSET(delta, MSR_DCD)) {
if (!ISSET(sc->sc_swflags, COM_SW_SOFTCAR) &&
(*linesw[tp->t_line].l_modem)(tp, ISSET(msr, MSR_DCD)) == 0) {
CLR(sc->sc_mcr, sc->sc_dtr);
com_write_reg(sc, com_mcr, sc->sc_mcr);
}
}
if (ISSET(delta & msr, MSR_CTS) &&
ISSET(tp->t_cflag, CRTSCTS)) {
/* the line is up and we want to do rts/cts flow control */
(*linesw[tp->t_line].l_start)(tp);
}
}
if (ISSET(lsr, LSR_TXRDY) && ISSET(tp->t_state, TS_BUSY)) {
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
if (sc->sc_halt > 0)
wakeup(&tp->t_outq);
(*linesw[tp->t_line].l_start)(tp);
}
if (ISSET(com_read_reg(sc, com_iir), IIR_NOPEND))
return (1);
}
}
void
cominit(bus_space_tag_t iot, bus_space_handle_t ioh, int rate, int frequency)
{
int s = splhigh();
u_char stat;
bus_space_write_1(iot, ioh, com_lcr, LCR_DLAB);
rate = comspeed(frequency, rate); /* XXX not comdefaultrate? */
bus_space_write_1(iot, ioh, com_dlbl, rate);
bus_space_write_1(iot, ioh, com_dlbh, rate >> 8);
bus_space_write_1(iot, ioh, com_lcr, LCR_8BITS);
bus_space_write_1(iot, ioh, com_mcr, MCR_DTR | MCR_RTS);
bus_space_write_1(iot, ioh, com_ier, 0); /* Make sure they are off */
bus_space_write_1(iot, ioh, com_fifo,
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1);
stat = bus_space_read_1(iot, ioh, com_iir);
splx(s);
}
#ifdef COM_CONSOLE
void
comcnprobe(struct consdev *cp)
{
bus_space_handle_t ioh;
int found;
if (comconsaddr == 0)
return;
if (bus_space_map(comconsiot, comconsaddr, COM_NPORTS, 0, &ioh))
return;
found = comprobe1(comconsiot, ioh);
bus_space_unmap(comconsiot, ioh, COM_NPORTS);
if (!found)
return;
/* Locate the major number. */
for (commajor = 0; commajor < nchrdev; commajor++)
if (cdevsw[commajor].d_open == comopen)
break;
/* Initialize required fields. */
cp->cn_dev = makedev(commajor, comconsunit);
cp->cn_pri = CN_HIGHPRI;
}
void
comcninit(struct consdev *cp)
{
if (bus_space_map(comconsiot, comconsaddr, COM_NPORTS, 0, &comconsioh))
panic("comcninit: mapping failed");
if (comconsfreq == 0)
comconsfreq = COM_FREQ;
cominit(comconsiot, comconsioh, comconsrate, comconsfreq);
}
int
comcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate,
int frequency, tcflag_t cflag)
{
static struct consdev comcons = {
NULL, NULL, comcngetc, comcnputc, comcnpollc, NULL,
NODEV, CN_LOWPRI
};
#ifndef __sparc64__
if (bus_space_map(iot, iobase, COM_NPORTS, 0, &comconsioh))
return ENOMEM;
#endif
cominit(iot, comconsioh, rate, frequency);
cn_tab = &comcons;
comconsiot = iot;
comconsaddr = iobase;
comconscflag = cflag;
comconsfreq = frequency;
comconsrate = rate;
return (0);
}
int
comcngetc(dev_t dev)
{
int s = splhigh();
u_char stat, c;
/* Block until a character becomes available. */
while (!ISSET(stat = comcn_read_reg(com_lsr), LSR_RXRDY))
continue;
c = comcn_read_reg(com_data);
/* Clear any interrupts generated by this transmission. */
stat = comcn_read_reg(com_iir);
splx(s);
return (c);
}
/*
* Console kernel output character routine.
*/
void
comcnputc(dev_t dev, int c)
{
int s = spltty();
int timo;
/* Wait for any pending transmission to finish. */
timo = 2000;
while (!ISSET(comcn_read_reg(com_lsr), LSR_TXRDY) && --timo)
delay(1);
comcn_write_reg(com_data, (u_int8_t)(c & 0xff));
bus_space_barrier(comconsiot, comconsioh, 0,
COM_NPORTS << comcons_reg_shift,
(BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE));
/* Wait for this transmission to complete. */
timo = 2000;
while (!ISSET(comcn_read_reg(com_lsr), LSR_TXRDY) && --timo)
delay(1);
splx(s);
}
void
comcnpollc(dev_t dev, int on)
{
}
#endif /* COM_CONSOLE */
void com_enable_debugport(struct com_softc *);
void com_fifo_probe(struct com_softc *);
#ifdef COM_CONSOLE
void
com_enable_debugport(struct com_softc *sc)
{
int s;
/* Turn on line break interrupt, set carrier. */
s = splhigh();
SET(sc->sc_mcr, MCR_DTR | MCR_RTS | MCR_IENABLE);
com_write_reg(sc, com_mcr, sc->sc_mcr);
splx(s);
}
#endif /* COM_CONSOLE */
void
com_attach_subr(struct com_softc *sc)
{
int probe = 0;
u_int8_t lcr, fifo;
u_int32_t cpr;
sc->sc_ier = 0;
/* disable interrupts */
com_write_reg(sc, com_ier, sc->sc_ier);
#ifdef COM_CONSOLE
if (sc->sc_iot == comconsiot && sc->sc_iobase == comconsaddr) {
comconsattached = 1;
delay(10000); /* wait for output to finish */
SET(sc->sc_hwflags, COM_HW_CONSOLE);
SET(sc->sc_swflags, COM_SW_SOFTCAR);
}
#endif
/*
* Probe for all known forms of UART.
*/
lcr = com_read_reg(sc, com_lcr);
com_write_reg(sc, com_lcr, LCR_EFR);
com_write_reg(sc, com_efr, 0);
com_write_reg(sc, com_lcr, 0);
com_write_reg(sc, com_fifo, FIFO_ENABLE);
delay(100);
/*
* Skip specific probes if attachment code knows it already.
*/
if (sc->sc_uarttype == COM_UART_UNKNOWN) {
switch (com_read_reg(sc, com_iir) >> 6) {
case 0:
sc->sc_uarttype = COM_UART_16450;
break;
case 2:
sc->sc_uarttype = COM_UART_16550;
break;
case 3:
sc->sc_uarttype = COM_UART_16550A;
break;
default:
sc->sc_uarttype = COM_UART_UNKNOWN;
break;
}
probe = 1;
}
/* Probe for ST16650s */
if (probe && sc->sc_uarttype == COM_UART_16550A) {
com_write_reg(sc, com_lcr, lcr | LCR_DLAB);
if (com_read_reg(sc, com_efr) == 0) {
com_write_reg(sc, com_efr, EFR_CTS);
if (com_read_reg(sc, com_efr) != 0)
sc->sc_uarttype = COM_UART_ST16650;
com_write_reg(sc, com_efr, 0);
} else {
com_write_reg(sc, com_lcr, LCR_EFR);
if (com_read_reg(sc, com_efr) == 0)
sc->sc_uarttype = COM_UART_ST16650V2;
}
}
#if 0 /* until com works with large FIFOs */
/* Probe for XR16850s */
if (probe && sc->sc_uarttype == COM_UART_ST16650V2) {
u_int8_t dlbl, dlbh;
/* Enable latch access and get the current values. */
com_write_reg(sc, com_lcr, lcr | LCR_DLAB);
dlbl = com_read_reg(sc, com_dlbl);
dlbh = com_read_reg(sc, com_dlbh);
/* Zero out the latch divisors */
com_write_reg(sc, com_dlbl, 0);
com_write_reg(sc, com_dlbh, 0);
if (com_read_reg(sc, com_dlbh) == 0x10) {
sc->sc_uarttype = COM_UART_XR16850;
sc->sc_uartrev = com_read_reg(sc, com_dlbl);
}
/* Reset to original. */
com_write_reg(sc, com_dlbl, dlbl);
com_write_reg(sc, com_dlbh, dlbh);
}
#endif
/* Probe for TI16750s */
if (probe && sc->sc_uarttype == COM_UART_16550A) {
com_write_reg(sc, com_lcr, lcr | LCR_DLAB);
com_write_reg(sc, com_fifo,
FIFO_ENABLE | FIFO_ENABLE_64BYTE);
if ((com_read_reg(sc, com_iir) >> 5) == 7) {
#if 0
com_write_reg(sc, com_lcr, 0);
if ((com_read_reg(sc, com_iir) >> 5) == 6)
#endif
sc->sc_uarttype = COM_UART_TI16750;
}
com_write_reg(sc, com_fifo, FIFO_ENABLE);
}
/* Reset the LCR (latch access is probably enabled). */
com_write_reg(sc, com_lcr, lcr);
/* Probe for 8250 */
if (probe && sc->sc_uarttype == COM_UART_16450) {
u_int8_t scr0, scr1, scr2;
scr0 = com_read_reg(sc, com_scratch);
com_write_reg(sc, com_scratch, 0xa5);
scr1 = com_read_reg(sc, com_scratch);
com_write_reg(sc, com_scratch, 0x5a);
scr2 = com_read_reg(sc, com_scratch);
com_write_reg(sc, com_scratch, scr0);
if ((scr1 != 0xa5) || (scr2 != 0x5a))
sc->sc_uarttype = COM_UART_8250;
}
/*
* Print UART type and initialize ourself.
*/
switch (sc->sc_uarttype) {
case COM_UART_UNKNOWN:
printf(": unknown uart\n");
break;
case COM_UART_8250:
printf(": ns8250, no fifo\n");
break;
case COM_UART_16450:
printf(": ns16450, no fifo\n");
break;
case COM_UART_16550:
printf(": ns16550, no working fifo\n");
break;
case COM_UART_16550A:
if (sc->sc_fifolen == 0)
sc->sc_fifolen = 16;
printf(": ns16550a, %d byte fifo\n", sc->sc_fifolen);
SET(sc->sc_hwflags, COM_HW_FIFO);
break;
case COM_UART_ST16650:
printf(": st16650, no working fifo\n");
break;
case COM_UART_ST16650V2:
if (sc->sc_fifolen == 0)
sc->sc_fifolen = 32;
printf(": st16650, %d byte fifo\n", sc->sc_fifolen);
SET(sc->sc_hwflags, COM_HW_FIFO);
break;
case COM_UART_ST16C654:
printf(": st16c654, 64 byte fifo\n");
SET(sc->sc_hwflags, COM_HW_FIFO);
sc->sc_fifolen = 64;
break;
case COM_UART_TI16750:
printf(": ti16750, 64 byte fifo\n");
SET(sc->sc_hwflags, COM_HW_FIFO);
sc->sc_fifolen = 64;
break;
#if 0
case COM_UART_XR16850:
printf(": xr16850 (rev %d), 128 byte fifo\n", sc->sc_uartrev);
SET(sc->sc_hwflags, COM_HW_FIFO);
sc->sc_fifolen = 128;
break;
#ifdef COM_UART_OX16C950
case COM_UART_OX16C950:
printf(": ox16c950 (rev %d), 128 byte fifo\n", sc->sc_uartrev);
SET(sc->sc_hwflags, COM_HW_FIFO);
sc->sc_fifolen = 128;
break;
#endif
#endif
case COM_UART_XR17V35X:
printf(": xr17v35x, 256 byte fifo\n");
SET(sc->sc_hwflags, COM_HW_FIFO);
sc->sc_fifolen = 256;
break;
case COM_UART_DW_APB:
printf(": dw16550");
SET(sc->sc_hwflags, COM_HW_FIFO);
cpr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, com_cpr << 2);
sc->sc_fifolen = CPR_FIFO_MODE(cpr) * 16;
if (sc->sc_fifolen) {
printf(", %d byte fifo\n", sc->sc_fifolen);
} else {
printf("\n");
/*
* The DW-APB configuration on the Allwinner H6 SoC
* does not provide the CPR register and will be
* detected as having no FIFO. But it does have a
* 256-byte FIFO and with the FIFO disabled the
* LSR_RXRDY bit remains set even if the input
* buffer is empty. As a workaround, treat as a
* 1-byte FIFO.
*/
sc->sc_fifolen = 1;
}
break;
default:
panic("comattach: bad fifo type");
}
#ifdef COM_CONSOLE
if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
#endif
if (sc->sc_fifolen < 256)
com_fifo_probe(sc);
if (sc->sc_fifolen == 0) {
CLR(sc->sc_hwflags, COM_HW_FIFO);
sc->sc_fifolen = 1;
}
/* clear and disable fifo */
/* DW-APB UART cannot turn off FIFO here (ddb will not work) */
fifo = (sc->sc_uarttype == COM_UART_DW_APB) ?
(FIFO_ENABLE | FIFO_TRIGGER_1) : 0;
com_write_reg(sc, com_fifo, fifo | FIFO_RCV_RST | FIFO_XMT_RST);
if (ISSET(com_read_reg(sc, com_lsr), LSR_RXRDY))
(void)com_read_reg(sc, com_data);
com_write_reg(sc, com_fifo, fifo);
sc->sc_mcr = 0;
com_write_reg(sc, com_mcr, sc->sc_mcr);
#ifdef COM_CONSOLE
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
int maj;
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == comopen)
break;
KASSERT(maj < nchrdev);
cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
printf("%s: console\n", sc->sc_dev.dv_xname);
}
#endif
timeout_set(&sc->sc_diag_tmo, comdiag, sc);
timeout_set(&sc->sc_dtr_tmo, com_raisedtr, sc);
sc->sc_si = softintr_establish(IPL_TTY, comsoft, sc);
if (sc->sc_si == NULL)
panic("%s: can't establish soft interrupt",
sc->sc_dev.dv_xname);
/*
* If there are no enable/disable functions, assume the device
* is always enabled.
*/
if (!sc->enable)
sc->enabled = 1;
#ifdef COM_CONSOLE
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
com_enable_debugport(sc);
#endif
}
void
com_fifo_probe(struct com_softc *sc)
{
u_int8_t fifo, ier;
int timo, len;
if (!ISSET(sc->sc_hwflags, COM_HW_FIFO))
return;
ier = 0;
com_write_reg(sc, com_ier, ier);
com_write_reg(sc, com_lcr, LCR_DLAB);
com_write_reg(sc, com_dlbl, 3);
com_write_reg(sc, com_dlbh, 0);
com_write_reg(sc, com_lcr, LCR_PNONE | LCR_8BITS);
com_write_reg(sc, com_mcr, MCR_LOOPBACK);
fifo = FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST;
if (sc->sc_uarttype == COM_UART_TI16750)
fifo |= FIFO_ENABLE_64BYTE;
com_write_reg(sc, com_fifo, fifo);
for (len = 0; len < 256; len++) {
com_write_reg(sc, com_data, (len + 1));
timo = 2000;
while (!ISSET(com_read_reg(sc, com_lsr),
LSR_TXRDY) && --timo)
delay(1);
if (!timo)
break;
}
delay(100);
for (len = 0; len < 256; len++) {
timo = 2000;
while (!ISSET(com_read_reg(sc, com_lsr),
LSR_RXRDY) && --timo)
delay(1);
if (!timo || com_read_reg(sc, com_data) != (len + 1))
break;
}
/* For safety, always use the smaller value. */
if (sc->sc_fifolen > len) {
printf("%s: probed fifo depth: %d bytes\n",
sc->sc_dev.dv_xname, len);
sc->sc_fifolen = len;
}
}
uint8_t
com_read_reg(struct com_softc *sc, bus_size_t reg)
{
reg <<= sc->sc_reg_shift;
if (sc->sc_reg_width == 4)
return bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
else
return bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg);
}
void
com_write_reg(struct com_softc *sc, bus_size_t reg, uint8_t value)
{
reg <<= sc->sc_reg_shift;
if (sc->sc_reg_width == 4)
bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, value);
else
bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, value);
}
#ifdef COM_CONSOLE
u_char comcons_reg_width;
u_char comcons_reg_shift;
uint8_t
comcn_read_reg(bus_size_t reg)
{
reg <<= comcons_reg_shift;
if (comcons_reg_width == 4)
return bus_space_read_4(comconsiot, comconsioh, reg);
else
return bus_space_read_1(comconsiot, comconsioh, reg);
}
void
comcn_write_reg(bus_size_t reg, uint8_t value)
{
reg <<= comcons_reg_shift;
if (comcons_reg_width == 4)
bus_space_write_4(comconsiot, comconsioh, reg, value);
else
bus_space_write_1(comconsiot, comconsioh, reg, value);
}
#endif
4
51
49
2
47
1
38
9
8
40
40
5
3
30
43
6
1
3
5
4
4
1
36
36
2
35
247
25
215
44
121
34
100
76
68
42
1
1
40
23
21
3
17
1
2
1
15
1
2
6
2
6
6
5
7
4
7
235
3
5
19
1
13
4
2
209
209
208
3
208
208
2
3
10
10
219
2
18
9
3
10
12
8
11
10
2
15
3
2
2
9
2
8
7
14
5
10
7
5
9
33
1
18
13
11
36
149
1
1
2
117
22
1
1
1
1
1
1
1
1
1
1
1
130
3
5
2
132
3
60
77
44
75
23
2
38
31
38
38
2
40
40
41
1
40
1
1
38
38
2
36
36
36
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
/* $OpenBSD: tty_pty.c,v 1.114 2022/09/02 07:37:57 deraadt Exp $ */
/* $NetBSD: tty_pty.c,v 1.33.4.1 1996/06/02 09:08:11 mrg Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tty_pty.c 8.4 (Berkeley) 2/20/95
*/
/*
* Pseudo-teletype Driver
* (Actually two drivers, requiring two entries in 'cdevsw')
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/signalvar.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/pledge.h>
#include <sys/rwlock.h>
#define BUFSIZ 100 /* Chunk size iomoved to/from user */
/*
* pts == /dev/tty[p-zP-T][0-9a-zA-Z]
* ptc == /dev/pty[p-zP-T][0-9a-zA-Z]
*/
/* XXX this needs to come from somewhere sane, and work with MAKEDEV */
#define TTY_LETTERS "pqrstuvwxyzPQRST"
#define TTY_SUFFIX "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
static int pts_major;
struct pt_softc {
struct tty *pt_tty;
int pt_flags;
struct selinfo pt_selr, pt_selw;
u_char pt_send;
u_char pt_ucntl;
char pty_pn[11];
char pty_sn[11];
};
#define NPTY_MIN 8 /* number of initial ptys */
#define NPTY_MAX 992 /* maximum number of ptys supported */
static struct pt_softc **pt_softc = NULL; /* pty array */
static int npty = 0; /* size of pty array */
static int maxptys = NPTY_MAX; /* maximum number of ptys */
/* for pty array */
struct rwlock pt_softc_lock = RWLOCK_INITIALIZER("ptarrlk");
#define PF_PKT 0x08 /* packet mode */
#define PF_STOPPED 0x10 /* user told stopped */
#define PF_REMOTE 0x20 /* remote and flow controlled input */
#define PF_NOSTOP 0x40
#define PF_UCNTL 0x80 /* user control mode */
void ptyattach(int);
void ptcwakeup(struct tty *, int);
struct tty *ptytty(dev_t);
void ptsstart(struct tty *);
int sysctl_pty(int *, u_int, void *, size_t *, void *, size_t);
void filt_ptcrdetach(struct knote *);
int filt_ptcread(struct knote *, long);
void filt_ptcwdetach(struct knote *);
int filt_ptcwrite(struct knote *, long);
int filt_ptcexcept(struct knote *, long);
static struct pt_softc **ptyarralloc(int);
static int check_pty(int);
static gid_t tty_gid = TTY_GID;
void ptydevname(int, struct pt_softc *);
dev_t pty_getfree(void);
void ptmattach(int);
int ptmopen(dev_t, int, int, struct proc *);
int ptmclose(dev_t, int, int, struct proc *);
int ptmioctl(dev_t, u_long, caddr_t, int, struct proc *p);
static int ptm_vn_open(struct nameidata *);
void
ptydevname(int minor, struct pt_softc *pti)
{
char buf[11] = "/dev/XtyXX";
int i, j;
i = minor / (sizeof(TTY_SUFFIX) - 1);
j = minor % (sizeof(TTY_SUFFIX) - 1);
if (i >= sizeof(TTY_LETTERS) - 1) {
pti->pty_pn[0] = '\0';
pti->pty_sn[0] = '\0';
return;
}
buf[5] = 'p';
buf[8] = TTY_LETTERS[i];
buf[9] = TTY_SUFFIX[j];
memcpy(pti->pty_pn, buf, sizeof(buf));
buf[5] = 't';
memcpy(pti->pty_sn, buf, sizeof(buf));
}
/*
* Allocate and zero array of nelem elements.
*/
struct pt_softc **
ptyarralloc(int nelem)
{
struct pt_softc **pt;
pt = mallocarray(nelem, sizeof(struct pt_softc *), M_DEVBUF,
M_WAITOK|M_ZERO);
return pt;
}
/*
* Check if the minor is correct and ensure necessary structures
* are properly allocated.
*/
int
check_pty(int dev)
{
struct pt_softc *pti;
int minor = minor(dev);
rw_enter_write(&pt_softc_lock);
if (minor >= npty) {
struct pt_softc **newpt;
int newnpty;
/* check if the requested pty can be granted */
if (minor >= maxptys)
goto limit_reached;
/* grow pty array by powers of two, up to maxptys */
for (newnpty = npty; newnpty <= minor; newnpty *= 2)
;
if (newnpty > maxptys)
newnpty = maxptys;
newpt = ptyarralloc(newnpty);
memcpy(newpt, pt_softc, npty * sizeof(struct pt_softc *));
free(pt_softc, M_DEVBUF, npty * sizeof(struct pt_softc *));
pt_softc = newpt;
npty = newnpty;
}
/*
* If the entry is not yet allocated, allocate one.
*/
if (!pt_softc[minor]) {
pti = malloc(sizeof(struct pt_softc), M_DEVBUF,
M_WAITOK|M_ZERO);
pti->pt_tty = ttymalloc(1000000);
pti->pt_tty->t_dev = dev;
ptydevname(minor, pti);
pt_softc[minor] = pti;
}
rw_exit_write(&pt_softc_lock);
return (0);
limit_reached:
rw_exit_write(&pt_softc_lock);
tablefull("pty");
return (ENXIO);
}
/*
* Establish n (or default if n is 1) ptys in the system.
*/
void
ptyattach(int n)
{
/* maybe should allow 0 => none? */
if (n <= 1)
n = NPTY_MIN;
pt_softc = ptyarralloc(n);
npty = n;
/*
* If we have pty, we need ptm too.
*/
ptmattach(1);
}
int
ptsopen(dev_t dev, int flag, int devtype, struct proc *p)
{
struct pt_softc *pti;
struct tty *tp;
int error;
if ((error = check_pty(dev)))
return (error);
pti = pt_softc[minor(dev)];
tp = pti->pt_tty;
if ((tp->t_state & TS_ISOPEN) == 0) {
tp->t_state |= TS_WOPEN;
ttychars(tp); /* Set up default chars */
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_cflag = TTYDEF_CFLAG;
tp->t_ispeed = tp->t_ospeed = B115200;
ttsetwater(tp); /* would be done in xxparam() */
} else if (tp->t_state & TS_XCLUDE && suser(p) != 0)
return (EBUSY);
if (tp->t_oproc) /* Ctrlr still around. */
tp->t_state |= TS_CARR_ON;
while ((tp->t_state & TS_CARR_ON) == 0) {
tp->t_state |= TS_WOPEN;
if (flag & FNONBLOCK)
break;
error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH, ttopen);
if (error)
return (error);
}
error = (*linesw[tp->t_line].l_open)(dev, tp, p);
ptcwakeup(tp, FREAD|FWRITE);
return (error);
}
int
ptsclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
int error;
error = (*linesw[tp->t_line].l_close)(tp, flag, p);
error |= ttyclose(tp);
ptcwakeup(tp, FREAD|FWRITE);
return (error);
}
int
ptsread(dev_t dev, struct uio *uio, int flag)
{
struct proc *p = curproc;
struct process *pr = p->p_p;
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
int error = 0;
again:
if (pti->pt_flags & PF_REMOTE) {
while (isbackground(pr, tp)) {
if (sigismasked(p, SIGTTIN) ||
pr->ps_pgrp->pg_jobc == 0 ||
pr->ps_flags & PS_PPWAIT)
return (EIO);
pgsignal(pr->ps_pgrp, SIGTTIN, 1);
error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg);
if (error)
return (error);
}
if (tp->t_canq.c_cc == 0) {
if (flag & IO_NDELAY)
return (EWOULDBLOCK);
error = ttysleep(tp, &tp->t_canq,
TTIPRI | PCATCH, ttyin);
if (error)
return (error);
goto again;
}
while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0)
if (ureadc(getc(&tp->t_canq), uio) < 0) {
error = EFAULT;
break;
}
if (tp->t_canq.c_cc == 1)
(void) getc(&tp->t_canq);
if (tp->t_canq.c_cc)
return (error);
} else
if (tp->t_oproc)
error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
ptcwakeup(tp, FWRITE);
return (error);
}
/*
* Write to pseudo-tty.
* Wakeups of controlling tty will happen
* indirectly, when tty driver calls ptsstart.
*/
int
ptswrite(dev_t dev, struct uio *uio, int flag)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
if (tp->t_oproc == NULL)
return (EIO);
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
/*
* Start output on pseudo-tty.
* Wake up process polling or sleeping for input from controlling tty.
*/
void
ptsstart(struct tty *tp)
{
struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
if (tp->t_state & TS_TTSTOP)
return;
if (pti->pt_flags & PF_STOPPED) {
pti->pt_flags &= ~PF_STOPPED;
pti->pt_send = TIOCPKT_START;
}
ptcwakeup(tp, FREAD);
}
int
ptsstop(struct tty *tp, int flush)
{
struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
int flag;
/* note: FLUSHREAD and FLUSHWRITE already ok */
if (flush == 0) {
flush = TIOCPKT_STOP;
pti->pt_flags |= PF_STOPPED;
} else
pti->pt_flags &= ~PF_STOPPED;
pti->pt_send |= flush;
/* change of perspective */
flag = 0;
if (flush & FREAD)
flag |= FWRITE;
if (flush & FWRITE)
flag |= FREAD;
ptcwakeup(tp, flag);
return 0;
}
void
ptcwakeup(struct tty *tp, int flag)
{
struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
if (flag & FREAD) {
selwakeup(&pti->pt_selr);
wakeup(&tp->t_outq.c_cf);
}
if (flag & FWRITE) {
selwakeup(&pti->pt_selw);
wakeup(&tp->t_rawq.c_cf);
}
}
int ptcopen(dev_t, int, int, struct proc *);
int
ptcopen(dev_t dev, int flag, int devtype, struct proc *p)
{
struct pt_softc *pti;
struct tty *tp;
int error;
if ((error = check_pty(dev)))
return (error);
pti = pt_softc[minor(dev)];
tp = pti->pt_tty;
if (tp->t_oproc)
return (EIO);
tp->t_oproc = ptsstart;
(void)(*linesw[tp->t_line].l_modem)(tp, 1);
tp->t_lflag &= ~EXTPROC;
pti->pt_flags = 0;
pti->pt_send = 0;
pti->pt_ucntl = 0;
return (0);
}
int
ptcclose(dev_t dev, int flag, int devtype, struct proc *p)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
(void)(*linesw[tp->t_line].l_modem)(tp, 0);
tp->t_state &= ~TS_CARR_ON;
tp->t_oproc = NULL; /* mark closed */
return (0);
}
int
ptcread(dev_t dev, struct uio *uio, int flag)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
char buf[BUFSIZ];
int error = 0, cc, bufcc = 0;
/*
* We want to block until the slave
* is open, and there's something to read;
* but if we lost the slave or we're NBIO,
* then return the appropriate error instead.
*/
for (;;) {
if (tp->t_state & TS_ISOPEN) {
if (pti->pt_flags & PF_PKT && pti->pt_send) {
error = ureadc((int)pti->pt_send, uio);
if (error)
return (error);
if (pti->pt_send & TIOCPKT_IOCTL) {
cc = MIN(uio->uio_resid,
sizeof(tp->t_termios));
error = uiomove(&tp->t_termios, cc, uio);
if (error)
return (error);
}
pti->pt_send = 0;
return (0);
}
if (pti->pt_flags & PF_UCNTL && pti->pt_ucntl) {
error = ureadc((int)pti->pt_ucntl, uio);
if (error)
return (error);
pti->pt_ucntl = 0;
return (0);
}
if (tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0)
break;
}
if ((tp->t_state & TS_CARR_ON) == 0)
return (0); /* EOF */
if (flag & IO_NDELAY)
return (EWOULDBLOCK);
error = tsleep_nsec(&tp->t_outq.c_cf, TTIPRI | PCATCH, ttyin,
INFSLP);
if (error)
return (error);
}
if (pti->pt_flags & (PF_PKT|PF_UCNTL))
error = ureadc(0, uio);
while (uio->uio_resid > 0 && error == 0) {
cc = MIN(uio->uio_resid, BUFSIZ);
cc = q_to_b(&tp->t_outq, buf, cc);
if (cc > bufcc)
bufcc = cc;
if (cc <= 0)
break;
error = uiomove(buf, cc, uio);
}
ttwakeupwr(tp);
if (bufcc)
explicit_bzero(buf, bufcc);
return (error);
}
int
ptcwrite(dev_t dev, struct uio *uio, int flag)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
u_char *cp = NULL;
int cc = 0, bufcc = 0;
u_char buf[BUFSIZ];
size_t cnt = 0;
int error = 0;
again:
if ((tp->t_state & TS_ISOPEN) == 0)
goto block;
if (pti->pt_flags & PF_REMOTE) {
if (tp->t_canq.c_cc)
goto block;
while (uio->uio_resid > 0 && tp->t_canq.c_cc < TTYHOG(tp) - 1) {
if (cc == 0) {
cc = MIN(uio->uio_resid, BUFSIZ);
cc = min(cc, TTYHOG(tp) - 1 - tp->t_canq.c_cc);
if (cc > bufcc)
bufcc = cc;
cp = buf;
error = uiomove(cp, cc, uio);
if (error)
goto done;
/* check again for safety */
if ((tp->t_state & TS_ISOPEN) == 0) {
error = EIO;
goto done;
}
}
if (cc)
(void) b_to_q((char *)cp, cc, &tp->t_canq);
cc = 0;
}
(void) putc(0, &tp->t_canq);
ttwakeup(tp);
wakeup(&tp->t_canq);
goto done;
}
do {
if (cc == 0) {
cc = MIN(uio->uio_resid, BUFSIZ);
if (cc > bufcc)
bufcc = cc;
cp = buf;
error = uiomove(cp, cc, uio);
if (error)
goto done;
/* check again for safety */
if ((tp->t_state & TS_ISOPEN) == 0) {
error = EIO;
goto done;
}
}
bufcc = cc;
while (cc > 0) {
if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG(tp) - 2 &&
(tp->t_canq.c_cc > 0 || !ISSET(tp->t_lflag, ICANON))) {
wakeup(&tp->t_rawq);
goto block;
}
if ((*linesw[tp->t_line].l_rint)(*cp++, tp) == 1 &&
tsleep(tp, TTIPRI | PCATCH, "ttyretype", 1) == EINTR)
goto interrupt;
cnt++;
cc--;
}
cc = 0;
} while (uio->uio_resid > 0);
goto done;
block:
/*
* Come here to wait for slave to open, for space
* in outq, or space in rawq.
*/
if ((tp->t_state & TS_CARR_ON) == 0) {
error = EIO;
goto done;
}
if (flag & IO_NDELAY) {
/* adjust for data copied in but not written */
uio->uio_resid += cc;
if (cnt == 0)
error = EWOULDBLOCK;
goto done;
}
error = tsleep_nsec(&tp->t_rawq.c_cf, TTOPRI | PCATCH, ttyout, INFSLP);
if (error == 0)
goto again;
interrupt:
/* adjust for data copied in but not written */
uio->uio_resid += cc;
done:
if (bufcc)
explicit_bzero(buf, bufcc);
return (error);
}
void
filt_ptcrdetach(struct knote *kn)
{
struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
int s;
s = spltty();
klist_remove_locked(&pti->pt_selr.si_note, kn);
splx(s);
}
int
filt_ptcread(struct knote *kn, long hint)
{
struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
struct tty *tp;
int active;
tp = pti->pt_tty;
kn->kn_data = 0;
if (ISSET(tp->t_state, TS_ISOPEN)) {
if (!ISSET(tp->t_state, TS_TTSTOP))
kn->kn_data = tp->t_outq.c_cc;
if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
kn->kn_data++;
}
active = (kn->kn_data > 0);
if (!ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL)
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
kn->kn_flags &= ~(EV_EOF | __EV_HUP);
}
return (active);
}
void
filt_ptcwdetach(struct knote *kn)
{
struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
int s;
s = spltty();
klist_remove_locked(&pti->pt_selw.si_note, kn);
splx(s);
}
int
filt_ptcwrite(struct knote *kn, long hint)
{
struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
struct tty *tp;
int active;
tp = pti->pt_tty;
kn->kn_data = 0;
if (ISSET(tp->t_state, TS_ISOPEN)) {
if (ISSET(pti->pt_flags, PF_REMOTE)) {
if (tp->t_canq.c_cc == 0)
kn->kn_data = tp->t_canq.c_cn;
} else if ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG(tp)-2) ||
(tp->t_canq.c_cc == 0 && ISSET(tp->t_lflag, ICANON)))
kn->kn_data = tp->t_canq.c_cn -
(tp->t_rawq.c_cc + tp->t_canq.c_cc);
}
active = (kn->kn_data > 0);
/* Write-side HUP condition is only for poll(2) and select(2). */
if (kn->kn_flags & (__EV_POLL | __EV_SELECT)) {
if (!ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
kn->kn_flags &= ~__EV_HUP;
}
}
return (active);
}
int
filt_ptcexcept(struct knote *kn, long hint)
{
struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
struct tty *tp;
int active = 0;
tp = pti->pt_tty;
if (kn->kn_sfflags & NOTE_OOB) {
/* If in packet or user control mode, check for data. */
if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl)) {
kn->kn_fflags |= NOTE_OOB;
kn->kn_data = 1;
active = 1;
}
}
if (kn->kn_flags & __EV_POLL) {
if (!ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
kn->kn_flags &= ~__EV_HUP;
}
}
return (active);
}
const struct filterops ptcread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ptcrdetach,
.f_event = filt_ptcread,
};
const struct filterops ptcwrite_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ptcwdetach,
.f_event = filt_ptcwrite,
};
const struct filterops ptcexcept_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ptcrdetach,
.f_event = filt_ptcexcept,
};
int
ptckqfilter(dev_t dev, struct knote *kn)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &pti->pt_selr.si_note;
kn->kn_fop = &ptcread_filtops;
break;
case EVFILT_WRITE:
klist = &pti->pt_selw.si_note;
kn->kn_fop = &ptcwrite_filtops;
break;
case EVFILT_EXCEPT:
klist = &pti->pt_selr.si_note;
kn->kn_fop = &ptcexcept_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = (caddr_t)pti;
s = spltty();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
struct tty *
ptytty(dev_t dev)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
return (tp);
}
int
ptyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct pt_softc *pti = pt_softc[minor(dev)];
struct tty *tp = pti->pt_tty;
u_char *cc = tp->t_cc;
int stop, error;
/*
* IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
* ttywflush(tp) will hang if there are characters in the outq.
*/
if (cmd == TIOCEXT) {
/*
* When the EXTPROC bit is being toggled, we need
* to send an TIOCPKT_IOCTL if the packet driver
* is turned on.
*/
if (*(int *)data) {
if (pti->pt_flags & PF_PKT) {
pti->pt_send |= TIOCPKT_IOCTL;
ptcwakeup(tp, FREAD);
}
tp->t_lflag |= EXTPROC;
} else {
if ((tp->t_lflag & EXTPROC) &&
(pti->pt_flags & PF_PKT)) {
pti->pt_send |= TIOCPKT_IOCTL;
ptcwakeup(tp, FREAD);
}
tp->t_lflag &= ~EXTPROC;
}
return(0);
} else if (cdevsw[major(dev)].d_open == ptcopen)
switch (cmd) {
case TIOCGPGRP:
/*
* We avoid calling ttioctl on the controller since,
* in that case, tp must be the controlling terminal.
*/
*(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
return (0);
case TIOCPKT:
if (*(int *)data) {
if (pti->pt_flags & PF_UCNTL)
return (EINVAL);
pti->pt_flags |= PF_PKT;
} else
pti->pt_flags &= ~PF_PKT;
return (0);
case TIOCUCNTL:
if (*(int *)data) {
if (pti->pt_flags & PF_PKT)
return (EINVAL);
pti->pt_flags |= PF_UCNTL;
} else
pti->pt_flags &= ~PF_UCNTL;
return (0);
case TIOCREMOTE:
if (*(int *)data)
pti->pt_flags |= PF_REMOTE;
else
pti->pt_flags &= ~PF_REMOTE;
ttyflush(tp, FREAD|FWRITE);
return (0);
case TIOCSETD:
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
ndflush(&tp->t_outq, tp->t_outq.c_cc);
break;
case TIOCSIG:
if (*(unsigned int *)data >= NSIG ||
*(unsigned int *)data == 0)
return(EINVAL);
if ((tp->t_lflag & NOFLSH) == 0)
ttyflush(tp, FREAD|FWRITE);
pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
if ((*(unsigned int *)data == SIGINFO) &&
((tp->t_lflag & NOKERNINFO) == 0))
ttyinfo(tp);
return (0);
case FIONREAD:
/*
* FIONREAD on the master side must return the amount
* in the output queue rather than the input.
*/
*(int *)data = tp->t_outq.c_cc;
return (0);
}
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error < 0)
error = ttioctl(tp, cmd, data, flag, p);
if (error < 0) {
/*
* Translate TIOCSBRK/TIOCCBRK to user mode ioctls to
* let the master interpret BREAK conditions.
*/
switch (cmd) {
case TIOCSBRK:
cmd = UIOCCMD(TIOCUCNTL_SBRK);
break;
case TIOCCBRK:
cmd = UIOCCMD(TIOCUCNTL_CBRK);
break;
default:
break;
}
if (pti->pt_flags & PF_UCNTL &&
(cmd & ~0xff) == UIOCCMD(0)) {
if (cmd & 0xff) {
pti->pt_ucntl = (u_char)cmd;
ptcwakeup(tp, FREAD);
}
return (0);
}
error = ENOTTY;
}
/*
* If external processing and packet mode send ioctl packet.
*/
if ((tp->t_lflag & EXTPROC) && (pti->pt_flags & PF_PKT)) {
switch (cmd) {
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
pti->pt_send |= TIOCPKT_IOCTL;
ptcwakeup(tp, FREAD);
default:
break;
}
}
stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) &&
CCEQ(cc[VSTART], CTRL('q'));
if (pti->pt_flags & PF_NOSTOP) {
if (stop) {
pti->pt_send &= ~TIOCPKT_NOSTOP;
pti->pt_send |= TIOCPKT_DOSTOP;
pti->pt_flags &= ~PF_NOSTOP;
ptcwakeup(tp, FREAD);
}
} else {
if (!stop) {
pti->pt_send &= ~TIOCPKT_DOSTOP;
pti->pt_send |= TIOCPKT_NOSTOP;
pti->pt_flags |= PF_NOSTOP;
ptcwakeup(tp, FREAD);
}
}
return (error);
}
/*
* Return pty-related information.
*/
int
sysctl_pty(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
/*
* Check if a pty is free to use.
*/
static int
pty_isfree_locked(int minor)
{
struct pt_softc *pt = pt_softc[minor];
return (pt == NULL || pt->pt_tty == NULL ||
pt->pt_tty->t_oproc == NULL);
}
static int
pty_isfree(int minor)
{
int isfree;
rw_enter_read(&pt_softc_lock);
isfree = pty_isfree_locked(minor);
rw_exit_read(&pt_softc_lock);
return(isfree);
}
dev_t
pty_getfree(void)
{
int i;
rw_enter_read(&pt_softc_lock);
for (i = 0; i < npty; i++) {
if (pty_isfree_locked(i))
break;
}
rw_exit_read(&pt_softc_lock);
return (makedev(pts_major, i));
}
/*
* Hacked up version of vn_open. We _only_ handle ptys and only open
* them with FREAD|FWRITE and never deal with creat or stuff like that.
*
* We need it because we have to fake up root credentials to open the pty.
*/
static int
ptm_vn_open(struct nameidata *ndp)
{
struct proc *p = ndp->ni_cnd.cn_proc;
struct ucred *cred;
struct vattr vattr;
struct vnode *vp;
int error;
if ((error = namei(ndp)) != 0)
return (error);
vp = ndp->ni_vp;
if (vp->v_type != VCHR) {
error = EINVAL;
goto bad;
}
/*
* Get us a fresh cred with root privileges.
*/
cred = crget();
error = VOP_OPEN(vp, FREAD|FWRITE, cred, p);
if (!error) {
/* update atime/mtime */
VATTR_NULL(&vattr);
getnanotime(&vattr.va_atime);
vattr.va_mtime = vattr.va_atime;
vattr.va_vaflags |= VA_UTIMES_NULL;
(void)VOP_SETATTR(vp, &vattr, p->p_ucred, p);
}
crfree(cred);
if (error)
goto bad;
vp->v_writecount++;
return (0);
bad:
vput(vp);
return (error);
}
void
ptmattach(int n)
{
/* find the major and minor of the pty devices */
int i;
for (i = 0; i < nchrdev; i++)
if (cdevsw[i].d_open == ptsopen)
break;
if (i == nchrdev)
panic("ptmattach: Can't find pty slave in cdevsw");
pts_major = i;
}
int
ptmopen(dev_t dev, int flag, int mode, struct proc *p)
{
return(0);
}
int
ptmclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
int
ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
dev_t newdev;
struct pt_softc * pti;
struct nameidata cnd, snd;
struct filedesc *fdp = p->p_fd;
struct file *cfp = NULL, *sfp = NULL;
int cindx, sindx, error;
uid_t uid;
gid_t gid;
struct vattr vattr;
struct ucred *cred;
struct ptmget *ptm = (struct ptmget *)data;
switch (cmd) {
case PTMGET:
fdplock(fdp);
/* Grab two filedescriptors. */
if ((error = falloc(p, &cfp, &cindx)) != 0) {
fdpunlock(fdp);
break;
}
if ((error = falloc(p, &sfp, &sindx)) != 0) {
fdremove(fdp, cindx);
fdpunlock(fdp);
closef(cfp, p);
break;
}
fdpunlock(fdp);
retry:
/* Find and open a free master pty. */
newdev = pty_getfree();
if ((error = check_pty(newdev)))
goto bad;
pti = pt_softc[minor(newdev)];
NDINIT(&cnd, LOOKUP, NOFOLLOW|LOCKLEAF|KERNELPATH, UIO_SYSSPACE,
pti->pty_pn, p);
cnd.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
if ((error = ptm_vn_open(&cnd)) != 0) {
/*
* Check if the master open failed because we lost
* the race to grab it.
*/
if (error == EIO && !pty_isfree(minor(newdev)))
goto retry;
goto bad;
}
cfp->f_flag = FREAD|FWRITE;
cfp->f_type = DTYPE_VNODE;
cfp->f_ops = &vnops;
cfp->f_data = (caddr_t) cnd.ni_vp;
VOP_UNLOCK(cnd.ni_vp);
/*
* Open the slave.
* namei -> setattr -> unlock -> revoke -> vrele ->
* namei -> open -> unlock
* Three stage rocket:
* 1. Change the owner and permissions on the slave.
* 2. Revoke all the users of the slave.
* 3. open the slave.
*/
NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF|KERNELPATH, UIO_SYSSPACE,
pti->pty_sn, p);
snd.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
if ((error = namei(&snd)) != 0)
goto bad;
if ((snd.ni_vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
gid = tty_gid;
/* get real uid */
uid = p->p_ucred->cr_ruid;
VATTR_NULL(&vattr);
vattr.va_uid = uid;
vattr.va_gid = gid;
vattr.va_mode = (S_IRUSR|S_IWUSR|S_IWGRP) & ALLPERMS;
/* Get a fake cred to pretend we're root. */
cred = crget();
error = VOP_SETATTR(snd.ni_vp, &vattr, cred, p);
crfree(cred);
if (error) {
vput(snd.ni_vp);
goto bad;
}
}
VOP_UNLOCK(snd.ni_vp);
if (snd.ni_vp->v_usecount > 1 ||
(snd.ni_vp->v_flag & (VALIASED)))
VOP_REVOKE(snd.ni_vp, REVOKEALL);
/*
* The vnode is useless after the revoke, we need to
* namei again.
*/
vrele(snd.ni_vp);
NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF|KERNELPATH, UIO_SYSSPACE,
pti->pty_sn, p);
snd.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
/* now open it */
if ((error = ptm_vn_open(&snd)) != 0)
goto bad;
sfp->f_flag = FREAD|FWRITE;
sfp->f_type = DTYPE_VNODE;
sfp->f_ops = &vnops;
sfp->f_data = (caddr_t) snd.ni_vp;
VOP_UNLOCK(snd.ni_vp);
/* now, put the indexen and names into struct ptmget */
ptm->cfd = cindx;
ptm->sfd = sindx;
memcpy(ptm->cn, pti->pty_pn, sizeof(pti->pty_pn));
memcpy(ptm->sn, pti->pty_sn, sizeof(pti->pty_sn));
/* insert files now that we've passed all errors */
fdplock(fdp);
fdinsert(fdp, cindx, 0, cfp);
fdinsert(fdp, sindx, 0, sfp);
fdpunlock(fdp);
FRELE(cfp, p);
FRELE(sfp, p);
break;
default:
error = EINVAL;
break;
}
return (error);
bad:
fdplock(fdp);
fdremove(fdp, cindx);
fdremove(fdp, sindx);
fdpunlock(fdp);
closef(cfp, p);
closef(sfp, p);
return (error);
}
487
487
2
17
30
2
32
2
37
3
35
40
375
2
3
31
123
293
197
2
217
421
6
112
293
28
130
200
22
233
233
445
2
426
20
117
121
180
85
28
57
83
2
28
54
60
23
155
121
35
2
32
115
1969
1971
1194
782
15
1318
11
11
1472
1472
2
5
1447
3379
4
3377
4
187
175
12
156
5
1
4
2
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
/* $OpenBSD: vfs_vnops.c,v 1.121 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_vnops.c,v 1.20 1996/02/04 02:18:41 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_vnops.c 8.5 (Berkeley) 12/8/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/lock.h>
#include <sys/vnode.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/specdev.h>
#include <sys/unistd.h>
int vn_read(struct file *, struct uio *, int);
int vn_write(struct file *, struct uio *, int);
int vn_kqfilter(struct file *, struct knote *);
int vn_closefile(struct file *, struct proc *);
int vn_seek(struct file *, off_t *, int, struct proc *);
const struct fileops vnops = {
.fo_read = vn_read,
.fo_write = vn_write,
.fo_ioctl = vn_ioctl,
.fo_kqfilter = vn_kqfilter,
.fo_stat = vn_statfile,
.fo_close = vn_closefile,
.fo_seek = vn_seek,
};
/*
* Common code for vnode open operations.
* Check permissions, and call the VOP_OPEN or VOP_CREATE routine.
*/
int
vn_open(struct nameidata *ndp, int fmode, int cmode)
{
struct vnode *vp;
struct proc *p = ndp->ni_cnd.cn_proc;
struct ucred *cred = p->p_ucred;
struct vattr va;
struct cloneinfo *cip;
int error;
/*
* The only valid flag to pass in here from NDINIT is
* KERNELPATH, This function will override the nameiop based
* on the fmode and cmode flags, So validate that our caller
* has not set other flags or operations in the nameidata
* structure.
*/
KASSERT(ndp->ni_cnd.cn_flags == 0 || ndp->ni_cnd.cn_flags == KERNELPATH);
KASSERT(ndp->ni_cnd.cn_nameiop == 0);
if ((fmode & (FREAD|FWRITE)) == 0)
return (EINVAL);
if ((fmode & (O_TRUNC | FWRITE)) == O_TRUNC)
return (EINVAL);
if (fmode & O_CREAT) {
ndp->ni_cnd.cn_nameiop = CREATE;
ndp->ni_cnd.cn_flags |= LOCKPARENT | LOCKLEAF;
if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0)
ndp->ni_cnd.cn_flags |= FOLLOW;
if ((error = namei(ndp)) != 0)
return (error);
if (ndp->ni_vp == NULL) {
VATTR_NULL(&va);
va.va_type = VREG;
va.va_mode = cmode;
if (fmode & O_EXCL)
va.va_vaflags |= VA_EXCLUSIVE;
error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp,
&ndp->ni_cnd, &va);
vput(ndp->ni_dvp);
if (error)
return (error);
fmode &= ~O_TRUNC;
vp = ndp->ni_vp;
} else {
VOP_ABORTOP(ndp->ni_dvp, &ndp->ni_cnd);
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
ndp->ni_dvp = NULL;
vp = ndp->ni_vp;
if (fmode & O_EXCL) {
error = EEXIST;
goto bad;
}
fmode &= ~O_CREAT;
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
ndp->ni_cnd.cn_flags |= ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF;
if ((error = namei(ndp)) != 0)
return (error);
vp = ndp->ni_vp;
}
if (vp->v_type == VSOCK) {
error = EOPNOTSUPP;
goto bad;
}
if (vp->v_type == VLNK) {
error = ELOOP;
goto bad;
}
if ((fmode & O_DIRECTORY) && vp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
if ((fmode & O_CREAT) == 0) {
if (fmode & FREAD) {
if ((error = VOP_ACCESS(vp, VREAD, cred, p)) != 0)
goto bad;
}
if (fmode & FWRITE) {
if (vp->v_type == VDIR) {
error = EISDIR;
goto bad;
}
if ((error = vn_writechk(vp)) != 0 ||
(error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0)
goto bad;
}
}
if ((fmode & O_TRUNC) && vp->v_type == VREG) {
VATTR_NULL(&va);
va.va_size = 0;
if ((error = VOP_SETATTR(vp, &va, cred, p)) != 0)
goto bad;
}
if ((error = VOP_OPEN(vp, fmode, cred, p)) != 0)
goto bad;
if (vp->v_flag & VCLONED) {
cip = (struct cloneinfo *)vp->v_data;
vp->v_flag &= ~VCLONED;
ndp->ni_vp = cip->ci_vp; /* return cloned vnode */
vp->v_data = cip->ci_data; /* restore v_data */
VOP_UNLOCK(vp); /* keep a reference */
vp = ndp->ni_vp; /* for the increment below */
free(cip, M_TEMP, sizeof(*cip));
}
if (fmode & FWRITE)
vp->v_writecount++;
return (0);
bad:
vput(vp);
return (error);
}
/*
* Check for write permissions on the specified vnode.
* Prototype text segments cannot be written.
*/
int
vn_writechk(struct vnode *vp)
{
/*
* Disallow write attempts on read-only file systems;
* unless the file is a socket or a block or character
* device resident on the file system.
*/
if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
return (EROFS);
case VNON:
case VCHR:
case VSOCK:
case VFIFO:
case VBAD:
case VBLK:
break;
}
}
/*
* If there's shared text associated with
* the vnode, try to free it up once. If
* we fail, we can't allow writing.
*/
if ((vp->v_flag & VTEXT) && !uvm_vnp_uncache(vp))
return (ETXTBSY);
return (0);
}
/*
* Check whether a write operation would exceed the file size rlimit
* for the process, if one should be applied for this operation.
* If a partial write should take place, the uio is adjusted and the
* amount by which the request would have exceeded the limit is returned
* via the 'overrun' argument.
*/
int
vn_fsizechk(struct vnode *vp, struct uio *uio, int ioflag, ssize_t *overrun)
{
struct proc *p = uio->uio_procp;
*overrun = 0;
if (vp->v_type == VREG && p != NULL && !(ioflag & IO_NOLIMIT)) {
rlim_t limit = lim_cur_proc(p, RLIMIT_FSIZE);
/* if already at or over the limit, send the signal and fail */
if (uio->uio_offset >= limit) {
psignal(p, SIGXFSZ);
return (EFBIG);
}
/* otherwise, clamp the write to stay under the limit */
if (uio->uio_resid > limit - uio->uio_offset) {
*overrun = uio->uio_resid - (limit - uio->uio_offset);
uio->uio_resid = limit - uio->uio_offset;
}
}
return (0);
}
/*
* Mark a vnode as being the text image of a running process.
*/
void
vn_marktext(struct vnode *vp)
{
vp->v_flag |= VTEXT;
}
/*
* Vnode close call
*/
int
vn_close(struct vnode *vp, int flags, struct ucred *cred, struct proc *p)
{
int error;
if (flags & FWRITE)
vp->v_writecount--;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_CLOSE(vp, flags, cred, p);
vput(vp);
return (error);
}
/*
* Package up an I/O request on a vnode into a uio and do it.
*/
int
vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, int len, off_t offset,
enum uio_seg segflg, int ioflg, struct ucred *cred, size_t *aresid,
struct proc *p)
{
struct uio auio;
struct iovec aiov;
int error;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = base;
aiov.iov_len = len;
auio.uio_resid = len;
auio.uio_offset = offset;
auio.uio_segflg = segflg;
auio.uio_rw = rw;
auio.uio_procp = p;
if ((ioflg & IO_NODELOCKED) == 0)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (rw == UIO_READ) {
error = VOP_READ(vp, &auio, ioflg, cred);
} else {
error = VOP_WRITE(vp, &auio, ioflg, cred);
}
if ((ioflg & IO_NODELOCKED) == 0)
VOP_UNLOCK(vp);
if (aresid)
*aresid = auio.uio_resid;
else
if (auio.uio_resid && error == 0)
error = EIO;
return (error);
}
/*
* File table vnode read routine.
*/
int
vn_read(struct file *fp, struct uio *uio, int fflags)
{
struct vnode *vp = fp->f_data;
struct ucred *cred = fp->f_cred;
size_t count = uio->uio_resid;
off_t offset;
int error;
KERNEL_LOCK();
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if ((fflags & FO_POSITION) == 0)
offset = uio->uio_offset = fp->f_offset;
else
offset = uio->uio_offset;
/* no wrap around of offsets except on character devices */
if (vp->v_type != VCHR && count > LLONG_MAX - offset) {
error = EINVAL;
goto done;
}
if (vp->v_type == VDIR) {
error = EISDIR;
goto done;
}
error = VOP_READ(vp, uio, (fp->f_flag & FNONBLOCK) ? IO_NDELAY : 0,
cred);
if ((fflags & FO_POSITION) == 0) {
mtx_enter(&fp->f_mtx);
fp->f_offset += count - uio->uio_resid;
mtx_leave(&fp->f_mtx);
}
done:
VOP_UNLOCK(vp);
KERNEL_UNLOCK();
return (error);
}
/*
* File table vnode write routine.
*/
int
vn_write(struct file *fp, struct uio *uio, int fflags)
{
struct vnode *vp = fp->f_data;
struct ucred *cred = fp->f_cred;
int error, ioflag = IO_UNIT;
size_t count;
KERNEL_LOCK();
/* note: pwrite/pwritev are unaffected by O_APPEND */
if (vp->v_type == VREG && (fp->f_flag & O_APPEND) &&
(fflags & FO_POSITION) == 0)
ioflag |= IO_APPEND;
if (fp->f_flag & FNONBLOCK)
ioflag |= IO_NDELAY;
if ((fp->f_flag & FFSYNC) ||
(vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS)))
ioflag |= IO_SYNC;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if ((fflags & FO_POSITION) == 0)
uio->uio_offset = fp->f_offset;
count = uio->uio_resid;
error = VOP_WRITE(vp, uio, ioflag, cred);
if ((fflags & FO_POSITION) == 0) {
mtx_enter(&fp->f_mtx);
if (ioflag & IO_APPEND)
fp->f_offset = uio->uio_offset;
else
fp->f_offset += count - uio->uio_resid;
mtx_leave(&fp->f_mtx);
}
VOP_UNLOCK(vp);
KERNEL_UNLOCK();
return (error);
}
/*
* File table wrapper for vn_stat
*/
int
vn_statfile(struct file *fp, struct stat *sb, struct proc *p)
{
struct vnode *vp = fp->f_data;
return vn_stat(vp, sb, p);
}
/*
* vnode stat routine.
*/
int
vn_stat(struct vnode *vp, struct stat *sb, struct proc *p)
{
struct vattr va;
int error;
mode_t mode;
error = VOP_GETATTR(vp, &va, p->p_ucred, p);
if (error)
return (error);
/*
* Copy from vattr table
*/
memset(sb, 0, sizeof(*sb));
sb->st_dev = va.va_fsid;
sb->st_ino = va.va_fileid;
mode = va.va_mode;
switch (vp->v_type) {
case VREG:
mode |= S_IFREG;
break;
case VDIR:
mode |= S_IFDIR;
break;
case VBLK:
mode |= S_IFBLK;
break;
case VCHR:
mode |= S_IFCHR;
break;
case VLNK:
mode |= S_IFLNK;
break;
case VSOCK:
mode |= S_IFSOCK;
break;
case VFIFO:
mode |= S_IFIFO;
break;
default:
return (EBADF);
}
sb->st_mode = mode;
sb->st_nlink = va.va_nlink;
sb->st_uid = va.va_uid;
sb->st_gid = va.va_gid;
sb->st_rdev = va.va_rdev;
sb->st_size = va.va_size;
sb->st_atim.tv_sec = va.va_atime.tv_sec;
sb->st_atim.tv_nsec = va.va_atime.tv_nsec;
sb->st_mtim.tv_sec = va.va_mtime.tv_sec;
sb->st_mtim.tv_nsec = va.va_mtime.tv_nsec;
sb->st_ctim.tv_sec = va.va_ctime.tv_sec;
sb->st_ctim.tv_nsec = va.va_ctime.tv_nsec;
sb->st_blksize = va.va_blocksize;
sb->st_flags = va.va_flags;
sb->st_gen = va.va_gen;
sb->st_blocks = va.va_bytes / S_BLKSIZE;
return (0);
}
/*
* File table vnode ioctl routine.
*/
int
vn_ioctl(struct file *fp, u_long com, caddr_t data, struct proc *p)
{
struct vnode *vp = fp->f_data;
struct vattr vattr;
int error = ENOTTY;
KERNEL_LOCK();
switch (vp->v_type) {
case VREG:
case VDIR:
if (com == FIONREAD) {
error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
if (error)
break;
*(int *)data = vattr.va_size - foffset(fp);
} else if (com == FIONBIO || com == FIOASYNC) /* XXX */
error = 0; /* XXX */
break;
case VFIFO:
case VCHR:
case VBLK:
error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p);
if (error == 0 && com == TIOCSCTTY) {
struct session *s = p->p_p->ps_session;
struct vnode *ovp = s->s_ttyvp;
s->s_ttyvp = vp;
vref(vp);
if (ovp)
vrele(ovp);
}
break;
default:
break;
}
KERNEL_UNLOCK();
return (error);
}
/*
* Check that the vnode is still valid, and if so
* acquire requested lock.
*/
int
vn_lock(struct vnode *vp, int flags)
{
int error, xlocked, do_wakeup;
do {
mtx_enter(&vnode_mtx);
if (vp->v_lflag & VXLOCK) {
vp->v_lflag |= VXWANT;
msleep_nsec(vp, &vnode_mtx, PINOD, "vn_lock", INFSLP);
mtx_leave(&vnode_mtx);
error = ENOENT;
} else {
vp->v_lockcount++;
mtx_leave(&vnode_mtx);
error = VOP_LOCK(vp, flags);
mtx_enter(&vnode_mtx);
vp->v_lockcount--;
do_wakeup = (vp->v_lockcount == 0);
xlocked = vp->v_lflag & VXLOCK;
mtx_leave(&vnode_mtx);
if (error == 0) {
if (!xlocked)
return (0);
/*
* The vnode was exclusively locked while
* acquiring the requested lock. Release it and
* try again.
*/
error = ENOENT;
VOP_UNLOCK(vp);
if (do_wakeup)
wakeup_one(&vp->v_lockcount);
}
}
} while (flags & LK_RETRY);
return (error);
}
/*
* File table vnode close routine.
*/
int
vn_closefile(struct file *fp, struct proc *p)
{
struct vnode *vp = fp->f_data;
struct flock lf;
int error;
KERNEL_LOCK();
if ((fp->f_iflags & FIF_HASLOCK)) {
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_UNLCK;
(void) VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK);
}
error = vn_close(vp, fp->f_flag, fp->f_cred, p);
KERNEL_UNLOCK();
return (error);
}
int
vn_kqfilter(struct file *fp, struct knote *kn)
{
int error;
KERNEL_LOCK();
error = VOP_KQFILTER(fp->f_data, fp->f_flag, kn);
KERNEL_UNLOCK();
return (error);
}
int
vn_seek(struct file *fp, off_t *offset, int whence, struct proc *p)
{
struct ucred *cred = p->p_ucred;
struct vnode *vp = fp->f_data;
struct vattr vattr;
off_t newoff;
int error = 0;
int special;
if (vp->v_type == VFIFO)
return (ESPIPE);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_type == VCHR)
special = 1;
else
special = 0;
switch (whence) {
case SEEK_CUR:
newoff = fp->f_offset + *offset;
break;
case SEEK_END:
KERNEL_LOCK();
error = VOP_GETATTR(vp, &vattr, cred, p);
KERNEL_UNLOCK();
if (error)
goto out;
newoff = *offset + (off_t)vattr.va_size;
break;
case SEEK_SET:
newoff = *offset;
break;
default:
error = EINVAL;
goto out;
}
if (!special && newoff < 0) {
error = EINVAL;
goto out;
}
mtx_enter(&fp->f_mtx);
fp->f_offset = newoff;
mtx_leave(&fp->f_mtx);
*offset = newoff;
out:
VOP_UNLOCK(vp);
return (error);
}
/*
* Common code for vnode access operations.
*/
/* Check if a directory can be found inside another in the hierarchy */
int
vn_isunder(struct vnode *lvp, struct vnode *rvp, struct proc *p)
{
int error;
error = vfs_getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN/2, 0, p);
if (!error)
return (1);
return (0);
}
58
5
23
1
35
1
4
12
3
53
58
58
31
31
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
/* $OpenBSD: tcp_input.c,v 1.380 2022/09/03 19:22:19 bluhm Exp $ */
/* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
struct tcpiphdr tcp_saveti;
int tcp_mss_adv(struct mbuf *, int);
int tcp_flush_queue(struct tcpcb *);
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
struct tcpipv6hdr tcp_saveti6;
/* for the packet header length in the mbuf */
#define M_PH_LEN(m) (((struct mbuf *)(m))->m_pkthdr.len)
#define M_V6_LEN(m) (M_PH_LEN(m) - sizeof(struct ip6_hdr))
#define M_V4_LEN(m) (M_PH_LEN(m) - sizeof(struct ip))
#endif /* INET6 */
int tcprexmtthresh = 3;
int tcptv_keep_init = TCPTV_KEEP_INIT;
int tcp_rst_ppslim = 100; /* 100pps */
int tcp_rst_ppslim_count = 0;
struct timeval tcp_rst_ppslim_last;
int tcp_ackdrop_ppslim = 100; /* 100pps */
int tcp_ackdrop_ppslim_count = 0;
struct timeval tcp_ackdrop_ppslim_last;
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
/* for modulo comparisons of timestamps */
#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
/* for TCP SACK comparisons */
#define SEQ_MIN(a,b) (SEQ_LT(a,b) ? (a) : (b))
#define SEQ_MAX(a,b) (SEQ_GT(a,b) ? (a) : (b))
/*
* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint.
*/
#ifdef INET6
#define ND6_HINT(tp) \
do { \
if (tp && tp->t_inpcb && (tp->t_inpcb->inp_flags & INP_IPV6) && \
rtisvalid(tp->t_inpcb->inp_route6.ro_rt)) { \
nd6_nud_hint(tp->t_inpcb->inp_route6.ro_rt); \
} \
} while (0)
#else
#define ND6_HINT(tp)
#endif
#ifdef TCP_ECN
/*
* ECN (Explicit Congestion Notification) support based on RFC3168
* implementation note:
* snd_last is used to track a recovery phase.
* when cwnd is reduced, snd_last is set to snd_max.
* while snd_last > snd_una, the sender is in a recovery phase and
* its cwnd should not be reduced again.
* snd_last follows snd_una when not in a recovery phase.
*/
#endif
/*
* Macro to compute ACK transmission behavior. Delay the ACK unless
* we have already delayed an ACK (must send an ACK every two segments).
* We also ACK immediately if we received a PUSH and the ACK-on-PUSH
* option is enabled or when the packet is coming from a loopback
* interface.
*/
#define TCP_SETUP_ACK(tp, tiflags, m) \
do { \
struct ifnet *ifp = NULL; \
if (m && (m->m_flags & M_PKTHDR)) \
ifp = if_get(m->m_pkthdr.ph_ifidx); \
if (TCP_TIMER_ISARMED(tp, TCPT_DELACK) || \
(tcp_ack_on_push && (tiflags) & TH_PUSH) || \
(ifp && (ifp->if_flags & IFF_LOOPBACK))) \
tp->t_flags |= TF_ACKNOW; \
else \
TCP_TIMER_ARM_MSEC(tp, TCPT_DELACK, tcp_delack_msecs); \
if_put(ifp); \
} while (0)
void tcp_sack_partialack(struct tcpcb *, struct tcphdr *);
void tcp_newreno_partialack(struct tcpcb *, struct tcphdr *);
void syn_cache_put(struct syn_cache *);
void syn_cache_rm(struct syn_cache *);
int syn_cache_respond(struct syn_cache *, struct mbuf *, uint32_t);
void syn_cache_timer(void *);
void syn_cache_reaper(void *);
void syn_cache_insert(struct syn_cache *, struct tcpcb *);
void syn_cache_reset(struct sockaddr *, struct sockaddr *,
struct tcphdr *, u_int);
int syn_cache_add(struct sockaddr *, struct sockaddr *, struct tcphdr *,
unsigned int, struct socket *, struct mbuf *, u_char *, int,
struct tcp_opt_info *, tcp_seq *, uint32_t);
struct socket *syn_cache_get(struct sockaddr *, struct sockaddr *,
struct tcphdr *, unsigned int, unsigned int, struct socket *,
struct mbuf *, uint32_t);
struct syn_cache *syn_cache_lookup(struct sockaddr *, struct sockaddr *,
struct syn_cache_head **, u_int);
/*
* Insert segment ti into reassembly queue of tcp with
* control block tp. Return TH_FIN if reassembly now includes
* a segment with FIN. The macro form does the common case inline
* (segment is the next to be received on an established connection,
* and the queue is empty), avoiding linkage into and removal
* from the queue and repetition of various conversions.
* Set DELACK for segments received in order, but ack immediately
* when segments are out of order (so fast retransmit can work).
*/
int
tcp_reass(struct tcpcb *tp, struct tcphdr *th, struct mbuf *m, int *tlen)
{
struct tcpqent *p, *q, *nq, *tiqe;
/*
* Allocate a new queue entry, before we throw away any data.
* If we can't, just drop the packet. XXX
*/
tiqe = pool_get(&tcpqe_pool, PR_NOWAIT);
if (tiqe == NULL) {
tiqe = TAILQ_LAST(&tp->t_segq, tcpqehead);
if (tiqe != NULL && th->th_seq == tp->rcv_nxt) {
/* Reuse last entry since new segment fills a hole */
m_freem(tiqe->tcpqe_m);
TAILQ_REMOVE(&tp->t_segq, tiqe, tcpqe_q);
}
if (tiqe == NULL || th->th_seq != tp->rcv_nxt) {
/* Flush segment queue for this connection */
tcp_freeq(tp);
tcpstat_inc(tcps_rcvmemdrop);
m_freem(m);
return (0);
}
}
/*
* Find a segment which begins after this one does.
*/
for (p = NULL, q = TAILQ_FIRST(&tp->t_segq); q != NULL;
p = q, q = TAILQ_NEXT(q, tcpqe_q))
if (SEQ_GT(q->tcpqe_tcp->th_seq, th->th_seq))
break;
/*
* If there is a preceding segment, it may provide some of
* our data already. If so, drop the data from the incoming
* segment. If it provides all of our data, drop us.
*/
if (p != NULL) {
struct tcphdr *phdr = p->tcpqe_tcp;
int i;
/* conversion to int (in i) handles seq wraparound */
i = phdr->th_seq + phdr->th_reseqlen - th->th_seq;
if (i > 0) {
if (i >= *tlen) {
tcpstat_pkt(tcps_rcvduppack, tcps_rcvdupbyte,
*tlen);
m_freem(m);
pool_put(&tcpqe_pool, tiqe);
return (0);
}
m_adj(m, i);
*tlen -= i;
th->th_seq += i;
}
}
tcpstat_pkt(tcps_rcvoopack, tcps_rcvoobyte, *tlen);
tp->t_rcvoopack++;
/*
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
*/
for (; q != NULL; q = nq) {
struct tcphdr *qhdr = q->tcpqe_tcp;
int i = (th->th_seq + *tlen) - qhdr->th_seq;
if (i <= 0)
break;
if (i < qhdr->th_reseqlen) {
qhdr->th_seq += i;
qhdr->th_reseqlen -= i;
m_adj(q->tcpqe_m, i);
break;
}
nq = TAILQ_NEXT(q, tcpqe_q);
m_freem(q->tcpqe_m);
TAILQ_REMOVE(&tp->t_segq, q, tcpqe_q);
pool_put(&tcpqe_pool, q);
}
/* Insert the new segment queue entry into place. */
tiqe->tcpqe_m = m;
th->th_reseqlen = *tlen;
tiqe->tcpqe_tcp = th;
if (p == NULL) {
TAILQ_INSERT_HEAD(&tp->t_segq, tiqe, tcpqe_q);
} else {
TAILQ_INSERT_AFTER(&tp->t_segq, p, tiqe, tcpqe_q);
}
if (th->th_seq != tp->rcv_nxt)
return (0);
return (tcp_flush_queue(tp));
}
int
tcp_flush_queue(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
struct tcpqent *q, *nq;
int flags;
/*
* Present data to user, advancing rcv_nxt through
* completed sequence space.
*/
if (TCPS_HAVEESTABLISHED(tp->t_state) == 0)
return (0);
q = TAILQ_FIRST(&tp->t_segq);
if (q == NULL || q->tcpqe_tcp->th_seq != tp->rcv_nxt)
return (0);
if (tp->t_state == TCPS_SYN_RECEIVED && q->tcpqe_tcp->th_reseqlen)
return (0);
do {
tp->rcv_nxt += q->tcpqe_tcp->th_reseqlen;
flags = q->tcpqe_tcp->th_flags & TH_FIN;
nq = TAILQ_NEXT(q, tcpqe_q);
TAILQ_REMOVE(&tp->t_segq, q, tcpqe_q);
ND6_HINT(tp);
if (so->so_state & SS_CANTRCVMORE)
m_freem(q->tcpqe_m);
else
sbappendstream(so, &so->so_rcv, q->tcpqe_m);
pool_put(&tcpqe_pool, q);
q = nq;
} while (q != NULL && q->tcpqe_tcp->th_seq == tp->rcv_nxt);
tp->t_flags |= TF_BLOCKOUTPUT;
sorwakeup(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
return (flags);
}
/*
* TCP input routine, follows pages 65-76 of the
* protocol specification dated September, 1981 very closely.
*/
int
tcp_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
int iphlen = *offp;
struct ip *ip = NULL;
struct inpcb *inp = NULL;
u_int8_t *optp = NULL;
int optlen = 0;
int tlen, off;
struct tcpcb *otp = NULL, *tp = NULL;
int tiflags;
struct socket *so = NULL;
int todrop, acked, ourfinisacked;
int hdroptlen = 0;
short ostate;
caddr_t saveti;
tcp_seq iss, *reuse = NULL;
uint32_t now;
u_long tiwin;
struct tcp_opt_info opti;
struct tcphdr *th;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
#endif /* INET6 */
#ifdef TCP_ECN
u_char iptos;
#endif
tcpstat_inc(tcps_rcvtotal);
opti.ts_present = 0;
opti.maxseg = 0;
now = READ_ONCE(tcp_now);
/*
* RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
*/
if (m->m_flags & (M_BCAST|M_MCAST))
goto drop;
/*
* Get IP and TCP header together in first mbuf.
* Note: IP leaves IP header in first mbuf.
*/
IP6_EXTHDR_GET(th, struct tcphdr *, m, iphlen, sizeof(*th));
if (!th) {
tcpstat_inc(tcps_rcvshort);
return IPPROTO_DONE;
}
tlen = m->m_pkthdr.len - iphlen;
switch (af) {
case AF_INET:
ip = mtod(m, struct ip *);
#ifdef TCP_ECN
/* save ip_tos before clearing it for checksum */
iptos = ip->ip_tos;
#endif
break;
#ifdef INET6
case AF_INET6:
ip6 = mtod(m, struct ip6_hdr *);
#ifdef TCP_ECN
iptos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
#endif
/*
* Be proactive about unspecified IPv6 address in source.
* As we use all-zero to indicate unbounded/unconnected pcb,
* unspecified IPv6 address can be used to confuse us.
*
* Note that packets with unspecified IPv6 destination is
* already dropped in ip6_input.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
/* XXX stat */
goto drop;
}
/* Discard packets to multicast */
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
/* XXX stat */
goto drop;
}
break;
#endif
default:
unhandled_af(af);
}
/*
* Checksum extended TCP header and data.
*/
if ((m->m_pkthdr.csum_flags & M_TCP_CSUM_IN_OK) == 0) {
int sum;
if (m->m_pkthdr.csum_flags & M_TCP_CSUM_IN_BAD) {
tcpstat_inc(tcps_rcvbadsum);
goto drop;
}
tcpstat_inc(tcps_inswcsum);
switch (af) {
case AF_INET:
sum = in4_cksum(m, IPPROTO_TCP, iphlen, tlen);
break;
#ifdef INET6
case AF_INET6:
sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
tlen);
break;
#endif
}
if (sum != 0) {
tcpstat_inc(tcps_rcvbadsum);
goto drop;
}
}
/*
* Check that TCP offset makes sense,
* pull out TCP options and adjust length. XXX
*/
off = th->th_off << 2;
if (off < sizeof(struct tcphdr) || off > tlen) {
tcpstat_inc(tcps_rcvbadoff);
goto drop;
}
tlen -= off;
if (off > sizeof(struct tcphdr)) {
IP6_EXTHDR_GET(th, struct tcphdr *, m, iphlen, off);
if (!th) {
tcpstat_inc(tcps_rcvshort);
return IPPROTO_DONE;
}
optlen = off - sizeof(struct tcphdr);
optp = (u_int8_t *)(th + 1);
/*
* Do quick retrieval of timestamp options ("options
* prediction?"). If timestamp is the only option and it's
* formatted as recommended in RFC 1323 appendix A, we
* quickly get the values now and not bother calling
* tcp_dooptions(), etc.
*/
if ((optlen == TCPOLEN_TSTAMP_APPA ||
(optlen > TCPOLEN_TSTAMP_APPA &&
optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
*(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
(th->th_flags & TH_SYN) == 0) {
opti.ts_present = 1;
opti.ts_val = ntohl(*(u_int32_t *)(optp + 4));
opti.ts_ecr = ntohl(*(u_int32_t *)(optp + 8));
optp = NULL; /* we've parsed the options */
}
}
tiflags = th->th_flags;
/*
* Convert TCP protocol specific fields to host format.
*/
th->th_seq = ntohl(th->th_seq);
th->th_ack = ntohl(th->th_ack);
th->th_win = ntohs(th->th_win);
th->th_urp = ntohs(th->th_urp);
/*
* Locate pcb for segment.
*/
#if NPF > 0
inp = pf_inp_lookup(m);
#endif
findpcb:
if (inp == NULL) {
switch (af) {
#ifdef INET6
case AF_INET6:
inp = in6_pcblookup(&tcbtable, &ip6->ip6_src,
th->th_sport, &ip6->ip6_dst, th->th_dport,
m->m_pkthdr.ph_rtableid);
break;
#endif
case AF_INET:
inp = in_pcblookup(&tcbtable, ip->ip_src,
th->th_sport, ip->ip_dst, th->th_dport,
m->m_pkthdr.ph_rtableid);
break;
}
}
if (inp == NULL) {
tcpstat_inc(tcps_pcbhashmiss);
switch (af) {
#ifdef INET6
case AF_INET6:
inp = in6_pcblookup_listen(&tcbtable, &ip6->ip6_dst,
th->th_dport, m, m->m_pkthdr.ph_rtableid);
break;
#endif /* INET6 */
case AF_INET:
inp = in_pcblookup_listen(&tcbtable, ip->ip_dst,
th->th_dport, m, m->m_pkthdr.ph_rtableid);
break;
}
/*
* If the state is CLOSED (i.e., TCB does not exist) then
* all data in the incoming segment is discarded.
* If the TCB exists but is in CLOSED state, it is embryonic,
* but should either do a listen or a connect soon.
*/
}
#ifdef IPSEC
if (ipsec_in_use) {
struct m_tag *mtag;
struct tdb *tdb = NULL;
int error;
/* Find most recent IPsec tag */
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
if (mtag != NULL) {
struct tdb_ident *tdbi;
tdbi = (struct tdb_ident *)(mtag + 1);
tdb = gettdb(tdbi->rdomain, tdbi->spi,
&tdbi->dst, tdbi->proto);
}
error = ipsp_spd_lookup(m, af, iphlen, IPSP_DIRECTION_IN,
tdb, inp, NULL, NULL);
tdb_unref(tdb);
if (error) {
tcpstat_inc(tcps_rcvnosec);
goto drop;
}
}
#endif /* IPSEC */
if (inp == NULL) {
tcpstat_inc(tcps_noport);
goto dropwithreset_ratelim;
}
KASSERT(sotoinpcb(inp->inp_socket) == inp);
KASSERT(intotcpcb(inp) == NULL || intotcpcb(inp)->t_inpcb == inp);
soassertlocked(inp->inp_socket);
/* Check the minimum TTL for socket. */
switch (af) {
case AF_INET:
if (inp->inp_ip_minttl && inp->inp_ip_minttl > ip->ip_ttl)
goto drop;
break;
#ifdef INET6
case AF_INET6:
if (inp->inp_ip6_minhlim &&
inp->inp_ip6_minhlim > ip6->ip6_hlim)
goto drop;
break;
#endif
}
tp = intotcpcb(inp);
if (tp == NULL)
goto dropwithreset_ratelim;
if (tp->t_state == TCPS_CLOSED)
goto drop;
/* Unscale the window into a 32-bit value. */
if ((tiflags & TH_SYN) == 0)
tiwin = th->th_win << tp->snd_scale;
else
tiwin = th->th_win;
so = inp->inp_socket;
if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {
union syn_cache_sa src;
union syn_cache_sa dst;
bzero(&src, sizeof(src));
bzero(&dst, sizeof(dst));
switch (af) {
case AF_INET:
src.sin.sin_len = sizeof(struct sockaddr_in);
src.sin.sin_family = AF_INET;
src.sin.sin_addr = ip->ip_src;
src.sin.sin_port = th->th_sport;
dst.sin.sin_len = sizeof(struct sockaddr_in);
dst.sin.sin_family = AF_INET;
dst.sin.sin_addr = ip->ip_dst;
dst.sin.sin_port = th->th_dport;
break;
#ifdef INET6
case AF_INET6:
src.sin6.sin6_len = sizeof(struct sockaddr_in6);
src.sin6.sin6_family = AF_INET6;
src.sin6.sin6_addr = ip6->ip6_src;
src.sin6.sin6_port = th->th_sport;
dst.sin6.sin6_len = sizeof(struct sockaddr_in6);
dst.sin6.sin6_family = AF_INET6;
dst.sin6.sin6_addr = ip6->ip6_dst;
dst.sin6.sin6_port = th->th_dport;
break;
#endif /* INET6 */
}
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
switch (af) {
#ifdef INET6
case AF_INET6:
saveti = (caddr_t) &tcp_saveti6;
memcpy(&tcp_saveti6.ti6_i, ip6, sizeof(*ip6));
memcpy(&tcp_saveti6.ti6_t, th, sizeof(*th));
break;
#endif
case AF_INET:
saveti = (caddr_t) &tcp_saveti;
memcpy(&tcp_saveti.ti_i, ip, sizeof(*ip));
memcpy(&tcp_saveti.ti_t, th, sizeof(*th));
break;
}
}
if (so->so_options & SO_ACCEPTCONN) {
switch (tiflags & (TH_RST|TH_SYN|TH_ACK)) {
case TH_SYN|TH_ACK|TH_RST:
case TH_SYN|TH_RST:
case TH_ACK|TH_RST:
case TH_RST:
syn_cache_reset(&src.sa, &dst.sa, th,
inp->inp_rtableid);
goto drop;
case TH_SYN|TH_ACK:
/*
* Received a SYN,ACK. This should
* never happen while we are in
* LISTEN. Send an RST.
*/
goto badsyn;
case TH_ACK:
so = syn_cache_get(&src.sa, &dst.sa,
th, iphlen, tlen, so, m, now);
if (so == NULL) {
/*
* We don't have a SYN for
* this ACK; send an RST.
*/
goto badsyn;
} else if (so == (struct socket *)(-1)) {
/*
* We were unable to create
* the connection. If the
* 3-way handshake was
* completed, and RST has
* been sent to the peer.
* Since the mbuf might be
* in use for the reply,
* do not free it.
*/
m = *mp = NULL;
goto drop;
} else {
/*
* We have created a
* full-blown connection.
*/
tp = NULL;
in_pcbunref(inp);
inp = in_pcbref(sotoinpcb(so));
tp = intotcpcb(inp);
if (tp == NULL)
goto badsyn; /*XXX*/
}
break;
default:
/*
* None of RST, SYN or ACK was set.
* This is an invalid packet for a
* TCB in LISTEN state. Send a RST.
*/
goto badsyn;
case TH_SYN:
/*
* Received a SYN.
*/
#ifdef INET6
/*
* If deprecated address is forbidden, we do
* not accept SYN to deprecated interface
* address to prevent any new inbound
* connection from getting established.
* When we do not accept SYN, we send a TCP
* RST, with deprecated source address (instead
* of dropping it). We compromise it as it is
* much better for peer to send a RST, and
* RST will be the final packet for the
* exchange.
*
* If we do not forbid deprecated addresses, we
* accept the SYN packet. RFC2462 does not
* suggest dropping SYN in this case.
* If we decipher RFC2462 5.5.4, it says like
* this:
* 1. use of deprecated addr with existing
* communication is okay - "SHOULD continue
* to be used"
* 2. use of it with new communication:
* (2a) "SHOULD NOT be used if alternate
* address with sufficient scope is
* available"
* (2b) nothing mentioned otherwise.
* Here we fall into (2b) case as we have no
* choice in our source address selection - we
* must obey the peer.
*
* The wording in RFC2462 is confusing, and
* there are multiple description text for
* deprecated address handling - worse, they
* are not exactly the same. I believe 5.5.4
* is the best one, so we follow 5.5.4.
*/
if (ip6 && !ip6_use_deprecated) {
struct in6_ifaddr *ia6;
struct ifnet *ifp =
if_get(m->m_pkthdr.ph_ifidx);
if (ifp &&
(ia6 = in6ifa_ifpwithaddr(ifp,
&ip6->ip6_dst)) &&
(ia6->ia6_flags &
IN6_IFF_DEPRECATED)) {
tp = NULL;
if_put(ifp);
goto dropwithreset;
}
if_put(ifp);
}
#endif
/*
* LISTEN socket received a SYN
* from itself? This can't possibly
* be valid; drop the packet.
*/
if (th->th_dport == th->th_sport) {
switch (af) {
#ifdef INET6
case AF_INET6:
if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src,
&ip6->ip6_dst)) {
tcpstat_inc(tcps_badsyn);
goto drop;
}
break;
#endif /* INET6 */
case AF_INET:
if (ip->ip_dst.s_addr == ip->ip_src.s_addr) {
tcpstat_inc(tcps_badsyn);
goto drop;
}
break;
}
}
/*
* SYN looks ok; create compressed TCP
* state for it.
*/
if (so->so_qlen > so->so_qlimit ||
syn_cache_add(&src.sa, &dst.sa, th, iphlen,
so, m, optp, optlen, &opti, reuse, now)
== -1) {
tcpstat_inc(tcps_dropsyn);
goto drop;
}
in_pcbunref(inp);
return IPPROTO_DONE;
}
}
}
#ifdef DIAGNOSTIC
/*
* Should not happen now that all embryonic connections
* are handled with compressed state.
*/
if (tp->t_state == TCPS_LISTEN)
panic("tcp_input: TCPS_LISTEN");
#endif
#if NPF > 0
pf_inp_link(m, inp);
#endif
/*
* Segment received on connection.
* Reset idle time and keep-alive timer.
*/
tp->t_rcvtime = now;
if (TCPS_HAVEESTABLISHED(tp->t_state))
TCP_TIMER_ARM(tp, TCPT_KEEP, tcp_keepidle);
if (tp->sack_enable)
tcp_del_sackholes(tp, th); /* Delete stale SACK holes */
/*
* Process options.
*/
#ifdef TCP_SIGNATURE
if (optp || (tp->t_flags & TF_SIGNATURE))
#else
if (optp)
#endif
if (tcp_dooptions(tp, optp, optlen, th, m, iphlen, &opti,
m->m_pkthdr.ph_rtableid, now))
goto drop;
if (opti.ts_present && opti.ts_ecr) {
int rtt_test;
/* subtract out the tcp timestamp modulator */
opti.ts_ecr -= tp->ts_modulate;
/* make sure ts_ecr is sensible */
rtt_test = now - opti.ts_ecr;
if (rtt_test < 0 || rtt_test > TCP_RTT_MAX)
opti.ts_ecr = 0;
}
#ifdef TCP_ECN
/* if congestion experienced, set ECE bit in subsequent packets. */
if ((iptos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
tp->t_flags |= TF_RCVD_CE;
tcpstat_inc(tcps_ecn_rcvce);
}
#endif
/*
* Header prediction: check for the two common cases
* of a uni-directional data xfer. If the packet has
* no control flags, is in-sequence, the window didn't
* change and we're not retransmitting, it's a
* candidate. If the length is zero and the ack moved
* forward, we're the sender side of the xfer. Just
* free the data acked & wake any higher level process
* that was blocked waiting for space. If the length
* is non-zero and the ack didn't move, we're the
* receiver side. If we're getting packets in-order
* (the reassembly queue is empty), add the data to
* the socket buffer and note that we need a delayed ack.
*/
if (tp->t_state == TCPS_ESTABLISHED &&
#ifdef TCP_ECN
(tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ECE|TH_CWR|TH_ACK)) == TH_ACK &&
#else
(tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
#endif
(!opti.ts_present || TSTMP_GEQ(opti.ts_val, tp->ts_recent)) &&
th->th_seq == tp->rcv_nxt &&
tiwin && tiwin == tp->snd_wnd &&
tp->snd_nxt == tp->snd_max) {
/*
* If last ACK falls within this segment's sequence numbers,
* record the timestamp.
* Fix from Braden, see Stevens p. 870
*/
if (opti.ts_present && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
tp->ts_recent_age = now;
tp->ts_recent = opti.ts_val;
}
if (tlen == 0) {
if (SEQ_GT(th->th_ack, tp->snd_una) &&
SEQ_LEQ(th->th_ack, tp->snd_max) &&
tp->snd_cwnd >= tp->snd_wnd &&
tp->t_dupacks == 0) {
/*
* this is a pure ack for outstanding data.
*/
tcpstat_inc(tcps_predack);
if (opti.ts_present && opti.ts_ecr)
tcp_xmit_timer(tp, now - opti.ts_ecr);
else if (tp->t_rtttime &&
SEQ_GT(th->th_ack, tp->t_rtseq))
tcp_xmit_timer(tp, now - tp->t_rtttime);
acked = th->th_ack - tp->snd_una;
tcpstat_pkt(tcps_rcvackpack, tcps_rcvackbyte,
acked);
tp->t_rcvacktime = now;
ND6_HINT(tp);
sbdrop(so, &so->so_snd, acked);
/*
* If we had a pending ICMP message that
* refers to data that have just been
* acknowledged, disregard the recorded ICMP
* message.
*/
if ((tp->t_flags & TF_PMTUD_PEND) &&
SEQ_GT(th->th_ack, tp->t_pmtud_th_seq))
tp->t_flags &= ~TF_PMTUD_PEND;
/*
* Keep track of the largest chunk of data
* acknowledged since last PMTU update
*/
if (tp->t_pmtud_mss_acked < acked)
tp->t_pmtud_mss_acked = acked;
tp->snd_una = th->th_ack;
/* Pull snd_wl2 up to prevent seq wrap. */
tp->snd_wl2 = th->th_ack;
/*
* We want snd_last to track snd_una so
* as to avoid sequence wraparound problems
* for very large transfers.
*/
#ifdef TCP_ECN
if (SEQ_GT(tp->snd_una, tp->snd_last))
#endif
tp->snd_last = tp->snd_una;
m_freem(m);
/*
* If all outstanding data are acked, stop
* retransmit timer, otherwise restart timer
* using current (possibly backed-off) value.
* If process is waiting for space,
* wakeup/selwakeup/signal. If data
* are ready to send, let tcp_output
* decide between more output or persist.
*/
if (tp->snd_una == tp->snd_max)
TCP_TIMER_DISARM(tp, TCPT_REXMT);
else if (TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0)
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
tcp_update_sndspace(tp);
if (sb_notify(so, &so->so_snd)) {
tp->t_flags |= TF_BLOCKOUTPUT;
sowwakeup(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
}
if (so->so_snd.sb_cc ||
tp->t_flags & TF_NEEDOUTPUT)
(void) tcp_output(tp);
in_pcbunref(inp);
return IPPROTO_DONE;
}
} else if (th->th_ack == tp->snd_una &&
TAILQ_EMPTY(&tp->t_segq) &&
tlen <= sbspace(so, &so->so_rcv)) {
/*
* This is a pure, in-sequence data packet
* with nothing on the reassembly queue and
* we have enough buffer space to take it.
*/
/* Clean receiver SACK report if present */
if (tp->sack_enable && tp->rcv_numsacks)
tcp_clean_sackreport(tp);
tcpstat_inc(tcps_preddat);
tp->rcv_nxt += tlen;
/* Pull snd_wl1 and rcv_up up to prevent seq wrap. */
tp->snd_wl1 = th->th_seq;
/* Packet has most recent segment, no urgent exists. */
tp->rcv_up = tp->rcv_nxt;
tcpstat_pkt(tcps_rcvpack, tcps_rcvbyte, tlen);
ND6_HINT(tp);
TCP_SETUP_ACK(tp, tiflags, m);
/*
* Drop TCP, IP headers and TCP options then add data
* to socket buffer.
*/
if (so->so_state & SS_CANTRCVMORE)
m_freem(m);
else {
if (opti.ts_present && opti.ts_ecr) {
if (tp->rfbuf_ts < opti.ts_ecr &&
opti.ts_ecr - tp->rfbuf_ts < hz) {
tcp_update_rcvspace(tp);
/* Start over with next RTT. */
tp->rfbuf_cnt = 0;
tp->rfbuf_ts = 0;
} else
tp->rfbuf_cnt += tlen;
}
m_adj(m, iphlen + off);
sbappendstream(so, &so->so_rcv, m);
}
tp->t_flags |= TF_BLOCKOUTPUT;
sorwakeup(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
if (tp->t_flags & (TF_ACKNOW|TF_NEEDOUTPUT))
(void) tcp_output(tp);
in_pcbunref(inp);
return IPPROTO_DONE;
}
}
/*
* Compute mbuf offset to TCP data segment.
*/
hdroptlen = iphlen + off;
/*
* Calculate amount of space in receive window,
* and then do TCP input processing.
* Receive window is amount of space in rcv queue,
* but not less than advertised window.
*/
{ int win;
win = sbspace(so, &so->so_rcv);
if (win < 0)
win = 0;
tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt));
}
/* Reset receive buffer auto scaling when not in bulk receive mode. */
tp->rfbuf_cnt = 0;
tp->rfbuf_ts = 0;
switch (tp->t_state) {
/*
* If the state is SYN_RECEIVED:
* if seg contains SYN/ACK, send an RST.
* if seg contains an ACK, but not for our SYN/ACK, send an RST
*/
case TCPS_SYN_RECEIVED:
if (tiflags & TH_ACK) {
if (tiflags & TH_SYN) {
tcpstat_inc(tcps_badsyn);
goto dropwithreset;
}
if (SEQ_LEQ(th->th_ack, tp->snd_una) ||
SEQ_GT(th->th_ack, tp->snd_max))
goto dropwithreset;
}
break;
/*
* If the state is SYN_SENT:
* if seg contains an ACK, but not for our SYN, drop the input.
* if seg contains a RST, then drop the connection.
* if seg does not contain SYN, then drop it.
* Otherwise this is an acceptable SYN segment
* initialize tp->rcv_nxt and tp->irs
* if seg contains ack then advance tp->snd_una
* if SYN has been acked change to ESTABLISHED else SYN_RCVD state
* arrange for segment to be acked (eventually)
* continue processing rest of data/controls, beginning with URG
*/
case TCPS_SYN_SENT:
if ((tiflags & TH_ACK) &&
(SEQ_LEQ(th->th_ack, tp->iss) ||
SEQ_GT(th->th_ack, tp->snd_max)))
goto dropwithreset;
if (tiflags & TH_RST) {
#ifdef TCP_ECN
/* if ECN is enabled, fall back to non-ecn at rexmit */
if (tcp_do_ecn && !(tp->t_flags & TF_DISABLE_ECN))
goto drop;
#endif
if (tiflags & TH_ACK)
tp = tcp_drop(tp, ECONNREFUSED);
goto drop;
}
if ((tiflags & TH_SYN) == 0)
goto drop;
if (tiflags & TH_ACK) {
tp->snd_una = th->th_ack;
if (SEQ_LT(tp->snd_nxt, tp->snd_una))
tp->snd_nxt = tp->snd_una;
}
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->irs = th->th_seq;
tcp_mss(tp, opti.maxseg);
/* Reset initial window to 1 segment for retransmit */
if (tp->t_rxtshift > 0)
tp->snd_cwnd = tp->t_maxseg;
tcp_rcvseqinit(tp);
tp->t_flags |= TF_ACKNOW;
/*
* If we've sent a SACK_PERMITTED option, and the peer
* also replied with one, then TF_SACK_PERMIT should have
* been set in tcp_dooptions(). If it was not, disable SACKs.
*/
if (tp->sack_enable)
tp->sack_enable = tp->t_flags & TF_SACK_PERMIT;
#ifdef TCP_ECN
/*
* if ECE is set but CWR is not set for SYN-ACK, or
* both ECE and CWR are set for simultaneous open,
* peer is ECN capable.
*/
if (tcp_do_ecn) {
switch (tiflags & (TH_ACK|TH_ECE|TH_CWR)) {
case TH_ACK|TH_ECE:
case TH_ECE|TH_CWR:
tp->t_flags |= TF_ECN_PERMIT;
tiflags &= ~(TH_ECE|TH_CWR);
tcpstat_inc(tcps_ecn_accepts);
}
}
#endif
if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
tcpstat_inc(tcps_connects);
tp->t_flags |= TF_BLOCKOUTPUT;
soisconnected(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
tp->t_state = TCPS_ESTABLISHED;
TCP_TIMER_ARM(tp, TCPT_KEEP, tcp_keepidle);
/* Do window scaling on this connection? */
if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
(TF_RCVD_SCALE|TF_REQ_SCALE)) {
tp->snd_scale = tp->requested_s_scale;
tp->rcv_scale = tp->request_r_scale;
}
tcp_flush_queue(tp);
/*
* if we didn't have to retransmit the SYN,
* use its rtt as our initial srtt & rtt var.
*/
if (tp->t_rtttime)
tcp_xmit_timer(tp, now - tp->t_rtttime);
/*
* Since new data was acked (the SYN), open the
* congestion window by one MSS. We do this
* here, because we won't go through the normal
* ACK processing below. And since this is the
* start of the connection, we know we are in
* the exponential phase of slow-start.
*/
tp->snd_cwnd += tp->t_maxseg;
} else
tp->t_state = TCPS_SYN_RECEIVED;
#if 0
trimthenstep6:
#endif
/*
* Advance th->th_seq to correspond to first data byte.
* If data, trim to stay within window,
* dropping FIN if necessary.
*/
th->th_seq++;
if (tlen > tp->rcv_wnd) {
todrop = tlen - tp->rcv_wnd;
m_adj(m, -todrop);
tlen = tp->rcv_wnd;
tiflags &= ~TH_FIN;
tcpstat_pkt(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
todrop);
}
tp->snd_wl1 = th->th_seq - 1;
tp->rcv_up = th->th_seq;
goto step6;
/*
* If a new connection request is received while in TIME_WAIT,
* drop the old connection and start over if the if the
* timestamp or the sequence numbers are above the previous
* ones.
*/
case TCPS_TIME_WAIT:
if (((tiflags & (TH_SYN|TH_ACK)) == TH_SYN) &&
((opti.ts_present &&
TSTMP_LT(tp->ts_recent, opti.ts_val)) ||
SEQ_GT(th->th_seq, tp->rcv_nxt))) {
#if NPF > 0
/*
* The socket will be recreated but the new state
* has already been linked to the socket. Remove the
* link between old socket and new state.
*/
pf_inp_unlink(inp);
#endif
/*
* Advance the iss by at least 32768, but
* clear the msb in order to make sure
* that SEG_LT(snd_nxt, iss).
*/
iss = tp->snd_nxt +
((arc4random() & 0x7fffffff) | 0x8000);
reuse = &iss;
tp = tcp_close(tp);
in_pcbunref(inp);
inp = NULL;
goto findpcb;
}
}
/*
* States other than LISTEN or SYN_SENT.
* First check timestamp, if present.
* Then check that at least some bytes of segment are within
* receive window. If segment begins before rcv_nxt,
* drop leading data (and SYN); if nothing left, just ack.
*
* RFC 1323 PAWS: If we have a timestamp reply on this segment
* and it's less than opti.ts_recent, drop it.
*/
if (opti.ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
TSTMP_LT(opti.ts_val, tp->ts_recent)) {
/* Check to see if ts_recent is over 24 days old. */
if ((int)(now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
/*
* Invalidate ts_recent. If this segment updates
* ts_recent, the age will be reset later and ts_recent
* will get a valid value. If it does not, setting
* ts_recent to zero will at least satisfy the
* requirement that zero be placed in the timestamp
* echo reply when ts_recent isn't valid. The
* age isn't reset until we get a valid ts_recent
* because we don't want out-of-order segments to be
* dropped when ts_recent is old.
*/
tp->ts_recent = 0;
} else {
tcpstat_pkt(tcps_rcvduppack, tcps_rcvdupbyte, tlen);
tcpstat_inc(tcps_pawsdrop);
if (tlen)
goto dropafterack;
goto drop;
}
}
todrop = tp->rcv_nxt - th->th_seq;
if (todrop > 0) {
if (tiflags & TH_SYN) {
tiflags &= ~TH_SYN;
th->th_seq++;
if (th->th_urp > 1)
th->th_urp--;
else
tiflags &= ~TH_URG;
todrop--;
}
if (todrop > tlen ||
(todrop == tlen && (tiflags & TH_FIN) == 0)) {
/*
* Any valid FIN must be to the left of the
* window. At this point, FIN must be a
* duplicate or out-of-sequence, so drop it.
*/
tiflags &= ~TH_FIN;
/*
* Send ACK to resynchronize, and drop any data,
* but keep on processing for RST or ACK.
*/
tp->t_flags |= TF_ACKNOW;
todrop = tlen;
tcpstat_pkt(tcps_rcvduppack, tcps_rcvdupbyte, todrop);
} else {
tcpstat_pkt(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
todrop);
}
hdroptlen += todrop; /* drop from head afterwards */
th->th_seq += todrop;
tlen -= todrop;
if (th->th_urp > todrop)
th->th_urp -= todrop;
else {
tiflags &= ~TH_URG;
th->th_urp = 0;
}
}
/*
* If new data are received on a connection after the
* user processes are gone, then RST the other end.
*/
if ((so->so_state & SS_NOFDREF) &&
tp->t_state > TCPS_CLOSE_WAIT && tlen) {
tp = tcp_close(tp);
tcpstat_inc(tcps_rcvafterclose);
goto dropwithreset;
}
/*
* If segment ends after window, drop trailing data
* (and PUSH and FIN); if nothing left, just ACK.
*/
todrop = (th->th_seq + tlen) - (tp->rcv_nxt+tp->rcv_wnd);
if (todrop > 0) {
tcpstat_inc(tcps_rcvpackafterwin);
if (todrop >= tlen) {
tcpstat_add(tcps_rcvbyteafterwin, tlen);
/*
* If window is closed can only take segments at
* window edge, and have to drop data and PUSH from
* incoming segments. Continue processing, but
* remember to ack. Otherwise, drop segment
* and ack.
*/
if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) {
tp->t_flags |= TF_ACKNOW;
tcpstat_inc(tcps_rcvwinprobe);
} else
goto dropafterack;
} else
tcpstat_add(tcps_rcvbyteafterwin, todrop);
m_adj(m, -todrop);
tlen -= todrop;
tiflags &= ~(TH_PUSH|TH_FIN);
}
/*
* If last ACK falls within this segment's sequence numbers,
* record its timestamp if it's more recent.
* NOTE that the test is modified according to the latest
* proposal of the tcplw@cray.com list (Braden 1993/04/26).
*/
if (opti.ts_present && TSTMP_GEQ(opti.ts_val, tp->ts_recent) &&
SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
tp->ts_recent_age = now;
tp->ts_recent = opti.ts_val;
}
/*
* If the RST bit is set examine the state:
* SYN_RECEIVED STATE:
* If passive open, return to LISTEN state.
* If active open, inform user that connection was refused.
* ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
* Inform user that connection was reset, and close tcb.
* CLOSING, LAST_ACK, TIME_WAIT STATES
* Close the tcb.
*/
if (tiflags & TH_RST) {
if (th->th_seq != tp->last_ack_sent &&
th->th_seq != tp->rcv_nxt &&
th->th_seq != (tp->rcv_nxt + 1))
goto drop;
switch (tp->t_state) {
case TCPS_SYN_RECEIVED:
#ifdef TCP_ECN
/* if ECN is enabled, fall back to non-ecn at rexmit */
if (tcp_do_ecn && !(tp->t_flags & TF_DISABLE_ECN))
goto drop;
#endif
so->so_error = ECONNREFUSED;
goto close;
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
so->so_error = ECONNRESET;
close:
tp->t_state = TCPS_CLOSED;
tcpstat_inc(tcps_drops);
tp = tcp_close(tp);
goto drop;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
tp = tcp_close(tp);
goto drop;
}
}
/*
* If a SYN is in the window, then this is an
* error and we ACK and drop the packet.
*/
if (tiflags & TH_SYN)
goto dropafterack_ratelim;
/*
* If the ACK bit is off we drop the segment and return.
*/
if ((tiflags & TH_ACK) == 0) {
if (tp->t_flags & TF_ACKNOW)
goto dropafterack;
else
goto drop;
}
/*
* Ack processing.
*/
switch (tp->t_state) {
/*
* In SYN_RECEIVED state, the ack ACKs our SYN, so enter
* ESTABLISHED state and continue processing.
* The ACK was checked above.
*/
case TCPS_SYN_RECEIVED:
tcpstat_inc(tcps_connects);
tp->t_flags |= TF_BLOCKOUTPUT;
soisconnected(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
tp->t_state = TCPS_ESTABLISHED;
TCP_TIMER_ARM(tp, TCPT_KEEP, tcp_keepidle);
/* Do window scaling? */
if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
(TF_RCVD_SCALE|TF_REQ_SCALE)) {
tp->snd_scale = tp->requested_s_scale;
tp->rcv_scale = tp->request_r_scale;
tiwin = th->th_win << tp->snd_scale;
}
tcp_flush_queue(tp);
tp->snd_wl1 = th->th_seq - 1;
/* fall into ... */
/*
* In ESTABLISHED state: drop duplicate ACKs; ACK out of range
* ACKs. If the ack is in the range
* tp->snd_una < th->th_ack <= tp->snd_max
* then advance tp->snd_una to th->th_ack and drop
* data from the retransmission queue. If this ACK reflects
* more up to date window information we update our window information.
*/
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
#ifdef TCP_ECN
/*
* if we receive ECE and are not already in recovery phase,
* reduce cwnd by half but don't slow-start.
* advance snd_last to snd_max not to reduce cwnd again
* until all outstanding packets are acked.
*/
if (tcp_do_ecn && (tiflags & TH_ECE)) {
if ((tp->t_flags & TF_ECN_PERMIT) &&
SEQ_GEQ(tp->snd_una, tp->snd_last)) {
u_int win;
win = min(tp->snd_wnd, tp->snd_cwnd) / tp->t_maxseg;
if (win > 1) {
tp->snd_ssthresh = win / 2 * tp->t_maxseg;
tp->snd_cwnd = tp->snd_ssthresh;
tp->snd_last = tp->snd_max;
tp->t_flags |= TF_SEND_CWR;
tcpstat_inc(tcps_cwr_ecn);
}
}
tcpstat_inc(tcps_ecn_rcvece);
}
/*
* if we receive CWR, we know that the peer has reduced
* its congestion window. stop sending ecn-echo.
*/
if ((tiflags & TH_CWR)) {
tp->t_flags &= ~TF_RCVD_CE;
tcpstat_inc(tcps_ecn_rcvcwr);
}
#endif /* TCP_ECN */
if (SEQ_LEQ(th->th_ack, tp->snd_una)) {
/*
* Duplicate/old ACK processing.
* Increments t_dupacks:
* Pure duplicate (same seq/ack/window, no data)
* Doesn't affect t_dupacks:
* Data packets.
* Normal window updates (window opens)
* Resets t_dupacks:
* New data ACKed.
* Window shrinks
* Old ACK
*/
if (tlen) {
/* Drop very old ACKs unless th_seq matches */
if (th->th_seq != tp->rcv_nxt &&
SEQ_LT(th->th_ack,
tp->snd_una - tp->max_sndwnd)) {
tcpstat_inc(tcps_rcvacktooold);
goto drop;
}
break;
}
/*
* If we get an old ACK, there is probably packet
* reordering going on. Be conservative and reset
* t_dupacks so that we are less aggressive in
* doing a fast retransmit.
*/
if (th->th_ack != tp->snd_una) {
tp->t_dupacks = 0;
break;
}
if (tiwin == tp->snd_wnd) {
tcpstat_inc(tcps_rcvdupack);
/*
* If we have outstanding data (other than
* a window probe), this is a completely
* duplicate ack (ie, window info didn't
* change), the ack is the biggest we've
* seen and we've seen exactly our rexmt
* threshold of them, assume a packet
* has been dropped and retransmit it.
* Kludge snd_nxt & the congestion
* window so we send only this one
* packet.
*
* We know we're losing at the current
* window size so do congestion avoidance
* (set ssthresh to half the current window
* and pull our congestion window back to
* the new ssthresh).
*
* Dup acks mean that packets have left the
* network (they're now cached at the receiver)
* so bump cwnd by the amount in the receiver
* to keep a constant cwnd packets in the
* network.
*/
if (TCP_TIMER_ISARMED(tp, TCPT_REXMT) == 0)
tp->t_dupacks = 0;
else if (++tp->t_dupacks == tcprexmtthresh) {
tcp_seq onxt = tp->snd_nxt;
u_long win =
ulmin(tp->snd_wnd, tp->snd_cwnd) /
2 / tp->t_maxseg;
if (SEQ_LT(th->th_ack, tp->snd_last)){
/*
* False fast retx after
* timeout. Do not cut window.
*/
tp->t_dupacks = 0;
goto drop;
}
if (win < 2)
win = 2;
tp->snd_ssthresh = win * tp->t_maxseg;
tp->snd_last = tp->snd_max;
if (tp->sack_enable) {
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->t_rtttime = 0;
#ifdef TCP_ECN
tp->t_flags |= TF_SEND_CWR;
#endif
tcpstat_inc(tcps_cwr_frecovery);
tcpstat_inc(tcps_sack_recovery_episode);
/*
* tcp_output() will send
* oldest SACK-eligible rtx.
*/
(void) tcp_output(tp);
tp->snd_cwnd = tp->snd_ssthresh+
tp->t_maxseg * tp->t_dupacks;
goto drop;
}
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->t_rtttime = 0;
tp->snd_nxt = th->th_ack;
tp->snd_cwnd = tp->t_maxseg;
#ifdef TCP_ECN
tp->t_flags |= TF_SEND_CWR;
#endif
tcpstat_inc(tcps_cwr_frecovery);
tcpstat_inc(tcps_sndrexmitfast);
(void) tcp_output(tp);
tp->snd_cwnd = tp->snd_ssthresh +
tp->t_maxseg * tp->t_dupacks;
if (SEQ_GT(onxt, tp->snd_nxt))
tp->snd_nxt = onxt;
goto drop;
} else if (tp->t_dupacks > tcprexmtthresh) {
tp->snd_cwnd += tp->t_maxseg;
(void) tcp_output(tp);
goto drop;
}
} else if (tiwin < tp->snd_wnd) {
/*
* The window was retracted! Previous dup
* ACKs may have been due to packets arriving
* after the shrunken window, not a missing
* packet, so play it safe and reset t_dupacks
*/
tp->t_dupacks = 0;
}
break;
}
/*
* If the congestion window was inflated to account
* for the other side's cached packets, retract it.
*/
if (tp->t_dupacks >= tcprexmtthresh) {
/* Check for a partial ACK */
if (SEQ_LT(th->th_ack, tp->snd_last)) {
if (tp->sack_enable)
tcp_sack_partialack(tp, th);
else
tcp_newreno_partialack(tp, th);
} else {
/* Out of fast recovery */
tp->snd_cwnd = tp->snd_ssthresh;
if (tcp_seq_subtract(tp->snd_max, th->th_ack) <
tp->snd_ssthresh)
tp->snd_cwnd =
tcp_seq_subtract(tp->snd_max,
th->th_ack);
tp->t_dupacks = 0;
}
} else {
/*
* Reset the duplicate ACK counter if we
* were not in fast recovery.
*/
tp->t_dupacks = 0;
}
if (SEQ_GT(th->th_ack, tp->snd_max)) {
tcpstat_inc(tcps_rcvacktoomuch);
goto dropafterack_ratelim;
}
acked = th->th_ack - tp->snd_una;
tcpstat_pkt(tcps_rcvackpack, tcps_rcvackbyte, acked);
tp->t_rcvacktime = now;
/*
* If we have a timestamp reply, update smoothed
* round trip time. If no timestamp is present but
* transmit timer is running and timed sequence
* number was acked, update smoothed round trip time.
* Since we now have an rtt measurement, cancel the
* timer backoff (cf., Phil Karn's retransmit alg.).
* Recompute the initial retransmit timer.
*/
if (opti.ts_present && opti.ts_ecr)
tcp_xmit_timer(tp, now - opti.ts_ecr);
else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq))
tcp_xmit_timer(tp, now - tp->t_rtttime);
/*
* If all outstanding data is acked, stop retransmit
* timer and remember to restart (more output or persist).
* If there is more data to be acked, restart retransmit
* timer, using current (possibly backed-off) value.
*/
if (th->th_ack == tp->snd_max) {
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->t_flags |= TF_NEEDOUTPUT;
} else if (TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0)
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
/*
* When new data is acked, open the congestion window.
* If the window gives us less than ssthresh packets
* in flight, open exponentially (maxseg per packet).
* Otherwise open linearly: maxseg per window
* (maxseg^2 / cwnd per packet).
*/
{
u_int cw = tp->snd_cwnd;
u_int incr = tp->t_maxseg;
if (cw > tp->snd_ssthresh)
incr = max(incr * incr / cw, 1);
if (tp->t_dupacks < tcprexmtthresh)
tp->snd_cwnd = ulmin(cw + incr,
TCP_MAXWIN << tp->snd_scale);
}
ND6_HINT(tp);
if (acked > so->so_snd.sb_cc) {
if (tp->snd_wnd > so->so_snd.sb_cc)
tp->snd_wnd -= so->so_snd.sb_cc;
else
tp->snd_wnd = 0;
sbdrop(so, &so->so_snd, (int)so->so_snd.sb_cc);
ourfinisacked = 1;
} else {
sbdrop(so, &so->so_snd, acked);
if (tp->snd_wnd > acked)
tp->snd_wnd -= acked;
else
tp->snd_wnd = 0;
ourfinisacked = 0;
}
tcp_update_sndspace(tp);
if (sb_notify(so, &so->so_snd)) {
tp->t_flags |= TF_BLOCKOUTPUT;
sowwakeup(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
}
/*
* If we had a pending ICMP message that referred to data
* that have just been acknowledged, disregard the recorded
* ICMP message.
*/
if ((tp->t_flags & TF_PMTUD_PEND) &&
SEQ_GT(th->th_ack, tp->t_pmtud_th_seq))
tp->t_flags &= ~TF_PMTUD_PEND;
/*
* Keep track of the largest chunk of data acknowledged
* since last PMTU update
*/
if (tp->t_pmtud_mss_acked < acked)
tp->t_pmtud_mss_acked = acked;
tp->snd_una = th->th_ack;
#ifdef TCP_ECN
/* sync snd_last with snd_una */
if (SEQ_GT(tp->snd_una, tp->snd_last))
tp->snd_last = tp->snd_una;
#endif
if (SEQ_LT(tp->snd_nxt, tp->snd_una))
tp->snd_nxt = tp->snd_una;
switch (tp->t_state) {
/*
* In FIN_WAIT_1 STATE in addition to the processing
* for the ESTABLISHED state if our FIN is now acknowledged
* then enter FIN_WAIT_2.
*/
case TCPS_FIN_WAIT_1:
if (ourfinisacked) {
/*
* If we can't receive any more
* data, then closing user can proceed.
* Starting the timer is contrary to the
* specification, but if we don't get a FIN
* we'll hang forever.
*/
if (so->so_state & SS_CANTRCVMORE) {
tp->t_flags |= TF_BLOCKOUTPUT;
soisdisconnected(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
TCP_TIMER_ARM(tp, TCPT_2MSL, tcp_maxidle);
}
tp->t_state = TCPS_FIN_WAIT_2;
}
break;
/*
* In CLOSING STATE in addition to the processing for
* the ESTABLISHED state if the ACK acknowledges our FIN
* then enter the TIME-WAIT state, otherwise ignore
* the segment.
*/
case TCPS_CLOSING:
if (ourfinisacked) {
tp->t_state = TCPS_TIME_WAIT;
tcp_canceltimers(tp);
TCP_TIMER_ARM(tp, TCPT_2MSL, 2 * TCPTV_MSL);
tp->t_flags |= TF_BLOCKOUTPUT;
soisdisconnected(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
}
break;
/*
* In LAST_ACK, we may still be waiting for data to drain
* and/or to be acked, as well as for the ack of our FIN.
* If our FIN is now acknowledged, delete the TCB,
* enter the closed state and return.
*/
case TCPS_LAST_ACK:
if (ourfinisacked) {
tp = tcp_close(tp);
goto drop;
}
break;
/*
* In TIME_WAIT state the only thing that should arrive
* is a retransmission of the remote FIN. Acknowledge
* it and restart the finack timer.
*/
case TCPS_TIME_WAIT:
TCP_TIMER_ARM(tp, TCPT_2MSL, 2 * TCPTV_MSL);
goto dropafterack;
}
}
step6:
/*
* Update window information.
* Don't look at window if no ACK: TAC's send garbage on first SYN.
*/
if ((tiflags & TH_ACK) &&
(SEQ_LT(tp->snd_wl1, th->th_seq) || (tp->snd_wl1 == th->th_seq &&
(SEQ_LT(tp->snd_wl2, th->th_ack) ||
(tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd))))) {
/* keep track of pure window updates */
if (tlen == 0 &&
tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd)
tcpstat_inc(tcps_rcvwinupd);
tp->snd_wnd = tiwin;
tp->snd_wl1 = th->th_seq;
tp->snd_wl2 = th->th_ack;
if (tp->snd_wnd > tp->max_sndwnd)
tp->max_sndwnd = tp->snd_wnd;
tp->t_flags |= TF_NEEDOUTPUT;
}
/*
* Process segments with URG.
*/
if ((tiflags & TH_URG) && th->th_urp &&
TCPS_HAVERCVDFIN(tp->t_state) == 0) {
/*
* This is a kludge, but if we receive and accept
* random urgent pointers, we'll crash in
* soreceive. It's hard to imagine someone
* actually wanting to send this much urgent data.
*/
if (th->th_urp + so->so_rcv.sb_cc > sb_max) {
th->th_urp = 0; /* XXX */
tiflags &= ~TH_URG; /* XXX */
goto dodata; /* XXX */
}
/*
* If this segment advances the known urgent pointer,
* then mark the data stream. This should not happen
* in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
* a FIN has been received from the remote side.
* In these states we ignore the URG.
*
* According to RFC961 (Assigned Protocols),
* the urgent pointer points to the last octet
* of urgent data. We continue, however,
* to consider it to indicate the first octet
* of data past the urgent section as the original
* spec states (in one of two places).
*/
if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) {
tp->rcv_up = th->th_seq + th->th_urp;
so->so_oobmark = so->so_rcv.sb_cc +
(tp->rcv_up - tp->rcv_nxt) - 1;
if (so->so_oobmark == 0)
so->so_state |= SS_RCVATMARK;
sohasoutofband(so);
tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
}
/*
* Remove out of band data so doesn't get presented to user.
* This can happen independent of advancing the URG pointer,
* but if two URG's are pending at once, some out-of-band
* data may creep in... ick.
*/
if (th->th_urp <= (u_int16_t) tlen &&
(so->so_options & SO_OOBINLINE) == 0)
tcp_pulloutofband(so, th->th_urp, m, hdroptlen);
} else
/*
* If no out of band data is expected,
* pull receive urgent pointer along
* with the receive window.
*/
if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
tp->rcv_up = tp->rcv_nxt;
dodata: /* XXX */
/*
* Process the segment text, merging it into the TCP sequencing queue,
* and arranging for acknowledgment of receipt if necessary.
* This process logically involves adjusting tp->rcv_wnd as data
* is presented to the user (this happens in tcp_usrreq.c,
* case PRU_RCVD). If a FIN has already been received on this
* connection then we just ignore the text.
*/
if ((tlen || (tiflags & TH_FIN)) &&
TCPS_HAVERCVDFIN(tp->t_state) == 0) {
tcp_seq laststart = th->th_seq;
tcp_seq lastend = th->th_seq + tlen;
if (th->th_seq == tp->rcv_nxt && TAILQ_EMPTY(&tp->t_segq) &&
tp->t_state == TCPS_ESTABLISHED) {
TCP_SETUP_ACK(tp, tiflags, m);
tp->rcv_nxt += tlen;
tiflags = th->th_flags & TH_FIN;
tcpstat_pkt(tcps_rcvpack, tcps_rcvbyte, tlen);
ND6_HINT(tp);
if (so->so_state & SS_CANTRCVMORE)
m_freem(m);
else {
m_adj(m, hdroptlen);
sbappendstream(so, &so->so_rcv, m);
}
tp->t_flags |= TF_BLOCKOUTPUT;
sorwakeup(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
} else {
m_adj(m, hdroptlen);
tiflags = tcp_reass(tp, th, m, &tlen);
tp->t_flags |= TF_ACKNOW;
}
if (tp->sack_enable)
tcp_update_sack_list(tp, laststart, lastend);
/*
* variable len never referenced again in modern BSD,
* so why bother computing it ??
*/
#if 0
/*
* Note the amount of data that peer has sent into
* our window, in order to estimate the sender's
* buffer size.
*/
len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);
#endif /* 0 */
} else {
m_freem(m);
tiflags &= ~TH_FIN;
}
/*
* If FIN is received ACK the FIN and let the user know
* that the connection is closing. Ignore a FIN received before
* the connection is fully established.
*/
if ((tiflags & TH_FIN) && TCPS_HAVEESTABLISHED(tp->t_state)) {
if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
tp->t_flags |= TF_BLOCKOUTPUT;
socantrcvmore(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
tp->t_flags |= TF_ACKNOW;
tp->rcv_nxt++;
}
switch (tp->t_state) {
/*
* In ESTABLISHED STATE enter the CLOSE_WAIT state.
*/
case TCPS_ESTABLISHED:
tp->t_state = TCPS_CLOSE_WAIT;
break;
/*
* If still in FIN_WAIT_1 STATE FIN has not been acked so
* enter the CLOSING state.
*/
case TCPS_FIN_WAIT_1:
tp->t_state = TCPS_CLOSING;
break;
/*
* In FIN_WAIT_2 state enter the TIME_WAIT state,
* starting the time-wait timer, turning off the other
* standard timers.
*/
case TCPS_FIN_WAIT_2:
tp->t_state = TCPS_TIME_WAIT;
tcp_canceltimers(tp);
TCP_TIMER_ARM(tp, TCPT_2MSL, 2 * TCPTV_MSL);
tp->t_flags |= TF_BLOCKOUTPUT;
soisdisconnected(so);
tp->t_flags &= ~TF_BLOCKOUTPUT;
break;
/*
* In TIME_WAIT state restart the 2 MSL time_wait timer.
*/
case TCPS_TIME_WAIT:
TCP_TIMER_ARM(tp, TCPT_2MSL, 2 * TCPTV_MSL);
break;
}
}
if (otp)
tcp_trace(TA_INPUT, ostate, tp, otp, saveti, 0, tlen);
/*
* Return any desired output.
*/
if (tp->t_flags & (TF_ACKNOW|TF_NEEDOUTPUT))
(void) tcp_output(tp);
in_pcbunref(inp);
return IPPROTO_DONE;
badsyn:
/*
* Received a bad SYN. Increment counters and dropwithreset.
*/
tcpstat_inc(tcps_badsyn);
tp = NULL;
goto dropwithreset;
dropafterack_ratelim:
if (ppsratecheck(&tcp_ackdrop_ppslim_last, &tcp_ackdrop_ppslim_count,
tcp_ackdrop_ppslim) == 0) {
/* XXX stat */
goto drop;
}
/* ...fall into dropafterack... */
dropafterack:
/*
* Generate an ACK dropping incoming segment if it occupies
* sequence space, where the ACK reflects our state.
*/
if (tiflags & TH_RST)
goto drop;
m_freem(m);
tp->t_flags |= TF_ACKNOW;
(void) tcp_output(tp);
in_pcbunref(inp);
return IPPROTO_DONE;
dropwithreset_ratelim:
/*
* We may want to rate-limit RSTs in certain situations,
* particularly if we are sending an RST in response to
* an attempt to connect to or otherwise communicate with
* a port for which we have no socket.
*/
if (ppsratecheck(&tcp_rst_ppslim_last, &tcp_rst_ppslim_count,
tcp_rst_ppslim) == 0) {
/* XXX stat */
goto drop;
}
/* ...fall into dropwithreset... */
dropwithreset:
/*
* Generate a RST, dropping incoming segment.
* Make ACK acceptable to originator of segment.
* Don't bother to respond to RST.
*/
if (tiflags & TH_RST)
goto drop;
if (tiflags & TH_ACK) {
tcp_respond(tp, mtod(m, caddr_t), th, (tcp_seq)0, th->th_ack,
TH_RST, m->m_pkthdr.ph_rtableid, now);
} else {
if (tiflags & TH_SYN)
tlen++;
tcp_respond(tp, mtod(m, caddr_t), th, th->th_seq + tlen,
(tcp_seq)0, TH_RST|TH_ACK, m->m_pkthdr.ph_rtableid, now);
}
m_freem(m);
in_pcbunref(inp);
return IPPROTO_DONE;
drop:
/*
* Drop space held by incoming segment and return.
*/
if (otp)
tcp_trace(TA_DROP, ostate, tp, otp, saveti, 0, tlen);
m_freem(m);
in_pcbunref(inp);
return IPPROTO_DONE;
}
int
tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcphdr *th,
struct mbuf *m, int iphlen, struct tcp_opt_info *oi,
u_int rtableid, uint32_t now)
{
u_int16_t mss = 0;
int opt, optlen;
#ifdef TCP_SIGNATURE
caddr_t sigp = NULL;
struct tdb *tdb = NULL;
#endif /* TCP_SIGNATURE */
for (; cp && cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[0];
if (opt == TCPOPT_EOL)
break;
if (opt == TCPOPT_NOP)
optlen = 1;
else {
if (cnt < 2)
break;
optlen = cp[1];
if (optlen < 2 || optlen > cnt)
break;
}
switch (opt) {
default:
continue;
case TCPOPT_MAXSEG:
if (optlen != TCPOLEN_MAXSEG)
continue;
if (!(th->th_flags & TH_SYN))
continue;
if (TCPS_HAVERCVDSYN(tp->t_state))
continue;
memcpy(&mss, cp + 2, sizeof(mss));
mss = ntohs(mss);
oi->maxseg = mss;
break;
case TCPOPT_WINDOW:
if (optlen != TCPOLEN_WINDOW)
continue;
if (!(th->th_flags & TH_SYN))
continue;
if (TCPS_HAVERCVDSYN(tp->t_state))
continue;
tp->t_flags |= TF_RCVD_SCALE;
tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
break;
case TCPOPT_TIMESTAMP:
if (optlen != TCPOLEN_TIMESTAMP)
continue;
oi->ts_present = 1;
memcpy(&oi->ts_val, cp + 2, sizeof(oi->ts_val));
oi->ts_val = ntohl(oi->ts_val);
memcpy(&oi->ts_ecr, cp + 6, sizeof(oi->ts_ecr));
oi->ts_ecr = ntohl(oi->ts_ecr);
if (!(th->th_flags & TH_SYN))
continue;
if (TCPS_HAVERCVDSYN(tp->t_state))
continue;
/*
* A timestamp received in a SYN makes
* it ok to send timestamp requests and replies.
*/
tp->t_flags |= TF_RCVD_TSTMP;
tp->ts_recent = oi->ts_val;
tp->ts_recent_age = now;
break;
case TCPOPT_SACK_PERMITTED:
if (!tp->sack_enable || optlen!=TCPOLEN_SACK_PERMITTED)
continue;
if (!(th->th_flags & TH_SYN))
continue;
if (TCPS_HAVERCVDSYN(tp->t_state))
continue;
/* MUST only be set on SYN */
tp->t_flags |= TF_SACK_PERMIT;
break;
case TCPOPT_SACK:
tcp_sack_option(tp, th, cp, optlen);
break;
#ifdef TCP_SIGNATURE
case TCPOPT_SIGNATURE:
if (optlen != TCPOLEN_SIGNATURE)
continue;
if (sigp && timingsafe_bcmp(sigp, cp + 2, 16))
goto bad;
sigp = cp + 2;
break;
#endif /* TCP_SIGNATURE */
}
}
#ifdef TCP_SIGNATURE
if (tp->t_flags & TF_SIGNATURE) {
union sockaddr_union src, dst;
memset(&src, 0, sizeof(union sockaddr_union));
memset(&dst, 0, sizeof(union sockaddr_union));
switch (tp->pf) {
case 0:
case AF_INET:
src.sa.sa_len = sizeof(struct sockaddr_in);
src.sa.sa_family = AF_INET;
src.sin.sin_addr = mtod(m, struct ip *)->ip_src;
dst.sa.sa_len = sizeof(struct sockaddr_in);
dst.sa.sa_family = AF_INET;
dst.sin.sin_addr = mtod(m, struct ip *)->ip_dst;
break;
#ifdef INET6
case AF_INET6:
src.sa.sa_len = sizeof(struct sockaddr_in6);
src.sa.sa_family = AF_INET6;
src.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_src;
dst.sa.sa_len = sizeof(struct sockaddr_in6);
dst.sa.sa_family = AF_INET6;
dst.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_dst;
break;
#endif /* INET6 */
}
tdb = gettdbbysrcdst(rtable_l2(rtableid),
0, &src, &dst, IPPROTO_TCP);
/*
* We don't have an SA for this peer, so we turn off
* TF_SIGNATURE on the listen socket
*/
if (tdb == NULL && tp->t_state == TCPS_LISTEN)
tp->t_flags &= ~TF_SIGNATURE;
}
if ((sigp ? TF_SIGNATURE : 0) ^ (tp->t_flags & TF_SIGNATURE)) {
tcpstat_inc(tcps_rcvbadsig);
goto bad;
}
if (sigp) {
char sig[16];
if (tdb == NULL) {
tcpstat_inc(tcps_rcvbadsig);
goto bad;
}
if (tcp_signature(tdb, tp->pf, m, th, iphlen, 1, sig) < 0)
goto bad;
if (timingsafe_bcmp(sig, sigp, 16)) {
tcpstat_inc(tcps_rcvbadsig);
goto bad;
}
tcpstat_inc(tcps_rcvgoodsig);
}
tdb_unref(tdb);
#endif /* TCP_SIGNATURE */
return (0);
#ifdef TCP_SIGNATURE
bad:
tdb_unref(tdb);
#endif /* TCP_SIGNATURE */
return (-1);
}
u_long
tcp_seq_subtract(u_long a, u_long b)
{
return ((long)(a - b));
}
/*
* This function is called upon receipt of new valid data (while not in header
* prediction mode), and it updates the ordered list of sacks.
*/
void
tcp_update_sack_list(struct tcpcb *tp, tcp_seq rcv_laststart,
tcp_seq rcv_lastend)
{
/*
* First reported block MUST be the most recent one. Subsequent
* blocks SHOULD be in the order in which they arrived at the
* receiver. These two conditions make the implementation fully
* compliant with RFC 2018.
*/
int i, j = 0, count = 0, lastpos = -1;
struct sackblk sack, firstsack, temp[MAX_SACK_BLKS];
/* First clean up current list of sacks */
for (i = 0; i < tp->rcv_numsacks; i++) {
sack = tp->sackblks[i];
if (sack.start == 0 && sack.end == 0) {
count++; /* count = number of blocks to be discarded */
continue;
}
if (SEQ_LEQ(sack.end, tp->rcv_nxt)) {
tp->sackblks[i].start = tp->sackblks[i].end = 0;
count++;
} else {
temp[j].start = tp->sackblks[i].start;
temp[j++].end = tp->sackblks[i].end;
}
}
tp->rcv_numsacks -= count;
if (tp->rcv_numsacks == 0) { /* no sack blocks currently (fast path) */
tcp_clean_sackreport(tp);
if (SEQ_LT(tp->rcv_nxt, rcv_laststart)) {
/* ==> need first sack block */
tp->sackblks[0].start = rcv_laststart;
tp->sackblks[0].end = rcv_lastend;
tp->rcv_numsacks = 1;
}
return;
}
/* Otherwise, sack blocks are already present. */
for (i = 0; i < tp->rcv_numsacks; i++)
tp->sackblks[i] = temp[i]; /* first copy back sack list */
if (SEQ_GEQ(tp->rcv_nxt, rcv_lastend))
return; /* sack list remains unchanged */
/*
* From here, segment just received should be (part of) the 1st sack.
* Go through list, possibly coalescing sack block entries.
*/
firstsack.start = rcv_laststart;
firstsack.end = rcv_lastend;
for (i = 0; i < tp->rcv_numsacks; i++) {
sack = tp->sackblks[i];
if (SEQ_LT(sack.end, firstsack.start) ||
SEQ_GT(sack.start, firstsack.end))
continue; /* no overlap */
if (sack.start == firstsack.start && sack.end == firstsack.end){
/*
* identical block; delete it here since we will
* move it to the front of the list.
*/
tp->sackblks[i].start = tp->sackblks[i].end = 0;
lastpos = i; /* last posn with a zero entry */
continue;
}
if (SEQ_LEQ(sack.start, firstsack.start))
firstsack.start = sack.start; /* merge blocks */
if (SEQ_GEQ(sack.end, firstsack.end))
firstsack.end = sack.end; /* merge blocks */
tp->sackblks[i].start = tp->sackblks[i].end = 0;
lastpos = i; /* last posn with a zero entry */
}
if (lastpos != -1) { /* at least one merge */
for (i = 0, j = 1; i < tp->rcv_numsacks; i++) {
sack = tp->sackblks[i];
if (sack.start == 0 && sack.end == 0)
continue;
temp[j++] = sack;
}
tp->rcv_numsacks = j; /* including first blk (added later) */
for (i = 1; i < tp->rcv_numsacks; i++) /* now copy back */
tp->sackblks[i] = temp[i];
} else { /* no merges -- shift sacks by 1 */
if (tp->rcv_numsacks < MAX_SACK_BLKS)
tp->rcv_numsacks++;
for (i = tp->rcv_numsacks-1; i > 0; i--)
tp->sackblks[i] = tp->sackblks[i-1];
}
tp->sackblks[0] = firstsack;
return;
}
/*
* Process the TCP SACK option. tp->snd_holes is an ordered list
* of holes (oldest to newest, in terms of the sequence space).
*/
void
tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
{
int tmp_olen;
u_char *tmp_cp;
struct sackhole *cur, *p, *temp;
if (!tp->sack_enable)
return;
/* SACK without ACK doesn't make sense. */
if ((th->th_flags & TH_ACK) == 0)
return;
/* Make sure the ACK on this segment is in [snd_una, snd_max]. */
if (SEQ_LT(th->th_ack, tp->snd_una) ||
SEQ_GT(th->th_ack, tp->snd_max))
return;
/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
return;
/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
tmp_cp = cp + 2;
tmp_olen = optlen - 2;
tcpstat_inc(tcps_sack_rcv_opts);
if (tp->snd_numholes < 0)
tp->snd_numholes = 0;
if (tp->t_maxseg == 0)
panic("tcp_sack_option"); /* Should never happen */
while (tmp_olen > 0) {
struct sackblk sack;
memcpy(&sack.start, tmp_cp, sizeof(tcp_seq));
sack.start = ntohl(sack.start);
memcpy(&sack.end, tmp_cp + sizeof(tcp_seq), sizeof(tcp_seq));
sack.end = ntohl(sack.end);
tmp_olen -= TCPOLEN_SACK;
tmp_cp += TCPOLEN_SACK;
if (SEQ_LEQ(sack.end, sack.start))
continue; /* bad SACK fields */
if (SEQ_LEQ(sack.end, tp->snd_una))
continue; /* old block */
if (SEQ_GT(th->th_ack, tp->snd_una)) {
if (SEQ_LT(sack.start, th->th_ack))
continue;
}
if (SEQ_GT(sack.end, tp->snd_max))
continue;
if (tp->snd_holes == NULL) { /* first hole */
tp->snd_holes = (struct sackhole *)
pool_get(&sackhl_pool, PR_NOWAIT);
if (tp->snd_holes == NULL) {
/* ENOBUFS, so ignore SACKed block for now */
goto dropped;
}
cur = tp->snd_holes;
cur->start = th->th_ack;
cur->end = sack.start;
cur->rxmit = cur->start;
cur->next = NULL;
tp->snd_numholes = 1;
tp->rcv_lastsack = sack.end;
/*
* dups is at least one. If more data has been
* SACKed, it can be greater than one.
*/
cur->dups = min(tcprexmtthresh,
((sack.end - cur->end)/tp->t_maxseg));
if (cur->dups < 1)
cur->dups = 1;
continue; /* with next sack block */
}
/* Go thru list of holes: p = previous, cur = current */
p = cur = tp->snd_holes;
while (cur) {
if (SEQ_LEQ(sack.end, cur->start))
/* SACKs data before the current hole */
break; /* no use going through more holes */
if (SEQ_GEQ(sack.start, cur->end)) {
/* SACKs data beyond the current hole */
cur->dups++;
if (((sack.end - cur->end)/tp->t_maxseg) >=
tcprexmtthresh)
cur->dups = tcprexmtthresh;
p = cur;
cur = cur->next;
continue;
}
if (SEQ_LEQ(sack.start, cur->start)) {
/* Data acks at least the beginning of hole */
if (SEQ_GEQ(sack.end, cur->end)) {
/* Acks entire hole, so delete hole */
if (p != cur) {
p->next = cur->next;
pool_put(&sackhl_pool, cur);
cur = p->next;
} else {
cur = cur->next;
pool_put(&sackhl_pool, p);
p = cur;
tp->snd_holes = p;
}
tp->snd_numholes--;
continue;
}
/* otherwise, move start of hole forward */
cur->start = sack.end;
cur->rxmit = SEQ_MAX(cur->rxmit, cur->start);
p = cur;
cur = cur->next;
continue;
}
/* move end of hole backward */
if (SEQ_GEQ(sack.end, cur->end)) {
cur->end = sack.start;
cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
cur->dups++;
if (((sack.end - cur->end)/tp->t_maxseg) >=
tcprexmtthresh)
cur->dups = tcprexmtthresh;
p = cur;
cur = cur->next;
continue;
}
if (SEQ_LT(cur->start, sack.start) &&
SEQ_GT(cur->end, sack.end)) {
/*
* ACKs some data in middle of a hole; need to
* split current hole
*/
if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
goto dropped;
temp = (struct sackhole *)
pool_get(&sackhl_pool, PR_NOWAIT);
if (temp == NULL)
goto dropped; /* ENOBUFS */
temp->next = cur->next;
temp->start = sack.end;
temp->end = cur->end;
temp->dups = cur->dups;
temp->rxmit = SEQ_MAX(cur->rxmit, temp->start);
cur->end = sack.start;
cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
cur->dups++;
if (((sack.end - cur->end)/tp->t_maxseg) >=
tcprexmtthresh)
cur->dups = tcprexmtthresh;
cur->next = temp;
p = temp;
cur = p->next;
tp->snd_numholes++;
}
}
/* At this point, p points to the last hole on the list */
if (SEQ_LT(tp->rcv_lastsack, sack.start)) {
/*
* Need to append new hole at end.
* Last hole is p (and it's not NULL).
*/
if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
goto dropped;
temp = (struct sackhole *)
pool_get(&sackhl_pool, PR_NOWAIT);
if (temp == NULL)
goto dropped; /* ENOBUFS */
temp->start = tp->rcv_lastsack;
temp->end = sack.start;
temp->dups = min(tcprexmtthresh,
((sack.end - sack.start)/tp->t_maxseg));
if (temp->dups < 1)
temp->dups = 1;
temp->rxmit = temp->start;
temp->next = 0;
p->next = temp;
tp->rcv_lastsack = sack.end;
tp->snd_numholes++;
}
}
return;
dropped:
tcpstat_inc(tcps_sack_drop_opts);
}
/*
* Delete stale (i.e, cumulatively ack'd) holes. Hole is deleted only if
* it is completely acked; otherwise, tcp_sack_option(), called from
* tcp_dooptions(), will fix up the hole.
*/
void
tcp_del_sackholes(struct tcpcb *tp, struct tcphdr *th)
{
if (tp->sack_enable && tp->t_state != TCPS_LISTEN) {
/* max because this could be an older ack just arrived */
tcp_seq lastack = SEQ_GT(th->th_ack, tp->snd_una) ?
th->th_ack : tp->snd_una;
struct sackhole *cur = tp->snd_holes;
struct sackhole *prev;
while (cur)
if (SEQ_LEQ(cur->end, lastack)) {
prev = cur;
cur = cur->next;
pool_put(&sackhl_pool, prev);
tp->snd_numholes--;
} else if (SEQ_LT(cur->start, lastack)) {
cur->start = lastack;
if (SEQ_LT(cur->rxmit, cur->start))
cur->rxmit = cur->start;
break;
} else
break;
tp->snd_holes = cur;
}
}
/*
* Delete all receiver-side SACK information.
*/
void
tcp_clean_sackreport(struct tcpcb *tp)
{
int i;
tp->rcv_numsacks = 0;
for (i = 0; i < MAX_SACK_BLKS; i++)
tp->sackblks[i].start = tp->sackblks[i].end=0;
}
/*
* Partial ack handling within a sack recovery episode. When a partial ack
* arrives, turn off retransmission timer, deflate the window, do not clear
* tp->t_dupacks.
*/
void
tcp_sack_partialack(struct tcpcb *tp, struct tcphdr *th)
{
/* Turn off retx. timer (will start again next segment) */
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->t_rtttime = 0;
/*
* Partial window deflation. This statement relies on the
* fact that tp->snd_una has not been updated yet.
*/
if (tp->snd_cwnd > (th->th_ack - tp->snd_una)) {
tp->snd_cwnd -= th->th_ack - tp->snd_una;
tp->snd_cwnd += tp->t_maxseg;
} else
tp->snd_cwnd = tp->t_maxseg;
tp->snd_cwnd += tp->t_maxseg;
tp->t_flags |= TF_NEEDOUTPUT;
}
/*
* Pull out of band byte out of a segment so
* it doesn't appear in the user's data queue.
* It is still reflected in the segment length for
* sequencing purposes.
*/
void
tcp_pulloutofband(struct socket *so, u_int urgent, struct mbuf *m, int off)
{
int cnt = off + urgent - 1;
while (cnt >= 0) {
if (m->m_len > cnt) {
char *cp = mtod(m, caddr_t) + cnt;
struct tcpcb *tp = sototcpcb(so);
tp->t_iobc = *cp;
tp->t_oobflags |= TCPOOB_HAVEDATA;
memmove(cp, cp + 1, m->m_len - cnt - 1);
m->m_len--;
return;
}
cnt -= m->m_len;
m = m->m_next;
if (m == NULL)
break;
}
panic("tcp_pulloutofband");
}
/*
* Collect new round-trip time estimate
* and update averages and current timeout.
*/
void
tcp_xmit_timer(struct tcpcb *tp, int rtt)
{
short delta;
short rttmin;
if (rtt < 0)
rtt = 0;
else if (rtt > TCP_RTT_MAX)
rtt = TCP_RTT_MAX;
tcpstat_inc(tcps_rttupdated);
if (tp->t_srtt != 0) {
/*
* delta is fixed point with 2 (TCP_RTT_BASE_SHIFT) bits
* after the binary point (scaled by 4), whereas
* srtt is stored as fixed point with 5 bits after the
* binary point (i.e., scaled by 32). The following magic
* is equivalent to the smoothing algorithm in rfc793 with
* an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
* point).
*/
delta = (rtt << TCP_RTT_BASE_SHIFT) -
(tp->t_srtt >> TCP_RTT_SHIFT);
if ((tp->t_srtt += delta) <= 0)
tp->t_srtt = 1 << TCP_RTT_BASE_SHIFT;
/*
* We accumulate a smoothed rtt variance (actually, a
* smoothed mean difference), then set the retransmit
* timer to smoothed rtt + 4 times the smoothed variance.
* rttvar is stored as fixed point with 4 bits after the
* binary point (scaled by 16). The following is
* equivalent to rfc793 smoothing with an alpha of .75
* (rttvar = rttvar*3/4 + |delta| / 4). This replaces
* rfc793's wired-in beta.
*/
if (delta < 0)
delta = -delta;
delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
if ((tp->t_rttvar += delta) <= 0)
tp->t_rttvar = 1 << TCP_RTT_BASE_SHIFT;
} else {
/*
* No rtt measurement yet - use the unsmoothed rtt.
* Set the variance to half the rtt (so our first
* retransmit happens at 3*rtt).
*/
tp->t_srtt = (rtt + 1) << (TCP_RTT_SHIFT + TCP_RTT_BASE_SHIFT);
tp->t_rttvar = (rtt + 1) <<
(TCP_RTTVAR_SHIFT + TCP_RTT_BASE_SHIFT - 1);
}
tp->t_rtttime = 0;
tp->t_rxtshift = 0;
/*
* the retransmit should happen at rtt + 4 * rttvar.
* Because of the way we do the smoothing, srtt and rttvar
* will each average +1/2 tick of bias. When we compute
* the retransmit timer, we want 1/2 tick of rounding and
* 1 extra tick because of +-1/2 tick uncertainty in the
* firing of the timer. The bias will give us exactly the
* 1.5 tick we need. But, because the bias is
* statistical, we have to test that we don't drop below
* the minimum feasible timer (which is 2 ticks).
*/
rttmin = min(max(rtt + 2, tp->t_rttmin), TCPTV_REXMTMAX);
TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), rttmin, TCPTV_REXMTMAX);
/*
* We received an ack for a packet that wasn't retransmitted;
* it is probably safe to discard any error indications we've
* received recently. This isn't quite right, but close enough
* for now (a route might have failed after we sent a segment,
* and the return path might not be symmetrical).
*/
tp->t_softerror = 0;
}
/*
* Determine a reasonable value for maxseg size.
* If the route is known, check route for mtu.
* If none, use an mss that can be handled on the outgoing
* interface without forcing IP to fragment; if bigger than
* an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
* to utilize large mbufs. If no route is found, route has no mtu,
* or the destination isn't local, use a default, hopefully conservative
* size (usually 512 or the default IP max size, but no more than the mtu
* of the interface), as we can't discover anything about intervening
* gateways or networks. We also initialize the congestion/slow start
* window to be a single segment if the destination isn't local.
* While looking at the routing entry, we also initialize other path-dependent
* parameters from pre-set or cached values in the routing entry.
*
* Also take into account the space needed for options that we
* send regularly. Make maxseg shorter by that amount to assure
* that we can send maxseg amount of data even when the options
* are present. Store the upper limit of the length of options plus
* data in maxopd.
*
* NOTE: offer == -1 indicates that the maxseg size changed due to
* Path MTU discovery.
*/
int
tcp_mss(struct tcpcb *tp, int offer)
{
struct rtentry *rt;
struct ifnet *ifp = NULL;
int mss, mssopt;
int iphlen;
struct inpcb *inp;
inp = tp->t_inpcb;
mssopt = mss = tcp_mssdflt;
rt = in_pcbrtentry(inp);
if (rt == NULL)
goto out;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
goto out;
switch (tp->pf) {
#ifdef INET6
case AF_INET6:
iphlen = sizeof(struct ip6_hdr);
break;
#endif
case AF_INET:
iphlen = sizeof(struct ip);
break;
default:
/* the family does not support path MTU discovery */
goto out;
}
/*
* if there's an mtu associated with the route and we support
* path MTU discovery for the underlying protocol family, use it.
*/
if (rt->rt_mtu) {
/*
* One may wish to lower MSS to take into account options,
* especially security-related options.
*/
if (tp->pf == AF_INET6 && rt->rt_mtu < IPV6_MMTU) {
/*
* RFC2460 section 5, last paragraph: if path MTU is
* smaller than 1280, use 1280 as packet size and
* attach fragment header.
*/
mss = IPV6_MMTU - iphlen - sizeof(struct ip6_frag) -
sizeof(struct tcphdr);
} else {
mss = rt->rt_mtu - iphlen -
sizeof(struct tcphdr);
}
} else if (ifp->if_flags & IFF_LOOPBACK) {
mss = ifp->if_mtu - iphlen - sizeof(struct tcphdr);
} else if (tp->pf == AF_INET) {
if (ip_mtudisc)
mss = ifp->if_mtu - iphlen - sizeof(struct tcphdr);
}
#ifdef INET6
else if (tp->pf == AF_INET6) {
/*
* for IPv6, path MTU discovery is always turned on,
* or the node must use packet size <= 1280.
*/
mss = ifp->if_mtu - iphlen - sizeof(struct tcphdr);
}
#endif /* INET6 */
/* Calculate the value that we offer in TCPOPT_MAXSEG */
if (offer != -1) {
mssopt = ifp->if_mtu - iphlen - sizeof(struct tcphdr);
mssopt = max(tcp_mssdflt, mssopt);
}
out:
if_put(ifp);
/*
* The current mss, t_maxseg, is initialized to the default value.
* If we compute a smaller value, reduce the current mss.
* If we compute a larger value, return it for use in sending
* a max seg size option, but don't store it for use
* unless we received an offer at least that large from peer.
*
* However, do not accept offers lower than the minimum of
* the interface MTU and 216.
*/
if (offer > 0)
tp->t_peermss = offer;
if (tp->t_peermss)
mss = min(mss, max(tp->t_peermss, 216));
/* sanity - at least max opt. space */
mss = max(mss, 64);
/*
* maxopd stores the maximum length of data AND options
* in a segment; maxseg is the amount of data in a normal
* segment. We need to store this value (maxopd) apart
* from maxseg, because now every segment carries options
* and thus we normally have somewhat less data in segments.
*/
tp->t_maxopd = mss;
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
(tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)
mss -= TCPOLEN_TSTAMP_APPA;
#ifdef TCP_SIGNATURE
if (tp->t_flags & TF_SIGNATURE)
mss -= TCPOLEN_SIGLEN;
#endif
if (offer == -1) {
/* mss changed due to Path MTU discovery */
tp->t_flags &= ~TF_PMTUD_PEND;
tp->t_pmtud_mtu_sent = 0;
tp->t_pmtud_mss_acked = 0;
if (mss < tp->t_maxseg) {
/*
* Follow suggestion in RFC 2414 to reduce the
* congestion window by the ratio of the old
* segment size to the new segment size.
*/
tp->snd_cwnd = ulmax((tp->snd_cwnd / tp->t_maxseg) *
mss, mss);
}
} else if (tcp_do_rfc3390 == 2) {
/* increase initial window */
tp->snd_cwnd = ulmin(10 * mss, ulmax(2 * mss, 14600));
} else if (tcp_do_rfc3390) {
/* increase initial window */
tp->snd_cwnd = ulmin(4 * mss, ulmax(2 * mss, 4380));
} else
tp->snd_cwnd = mss;
tp->t_maxseg = mss;
return (offer != -1 ? mssopt : mss);
}
u_int
tcp_hdrsz(struct tcpcb *tp)
{
u_int hlen;
switch (tp->pf) {
#ifdef INET6
case AF_INET6:
hlen = sizeof(struct ip6_hdr);
break;
#endif
case AF_INET:
hlen = sizeof(struct ip);
break;
default:
hlen = 0;
break;
}
hlen += sizeof(struct tcphdr);
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
(tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)
hlen += TCPOLEN_TSTAMP_APPA;
#ifdef TCP_SIGNATURE
if (tp->t_flags & TF_SIGNATURE)
hlen += TCPOLEN_SIGLEN;
#endif
return (hlen);
}
/*
* Set connection variables based on the effective MSS.
* We are passed the TCPCB for the actual connection. If we
* are the server, we are called by the compressed state engine
* when the 3-way handshake is complete. If we are the client,
* we are called when we receive the SYN,ACK from the server.
*
* NOTE: The t_maxseg value must be initialized in the TCPCB
* before this routine is called!
*/
void
tcp_mss_update(struct tcpcb *tp)
{
int mss;
u_long bufsize;
struct rtentry *rt;
struct socket *so;
so = tp->t_inpcb->inp_socket;
mss = tp->t_maxseg;
rt = in_pcbrtentry(tp->t_inpcb);
if (rt == NULL)
return;
bufsize = so->so_snd.sb_hiwat;
if (bufsize < mss) {
mss = bufsize;
/* Update t_maxseg and t_maxopd */
tcp_mss(tp, mss);
} else {
bufsize = roundup(bufsize, mss);
if (bufsize > sb_max)
bufsize = sb_max;
(void)sbreserve(so, &so->so_snd, bufsize);
}
bufsize = so->so_rcv.sb_hiwat;
if (bufsize > mss) {
bufsize = roundup(bufsize, mss);
if (bufsize > sb_max)
bufsize = sb_max;
(void)sbreserve(so, &so->so_rcv, bufsize);
}
}
/*
* When a partial ack arrives, force the retransmission of the
* next unacknowledged segment. Do not clear tp->t_dupacks.
* By setting snd_nxt to ti_ack, this forces retransmission timer
* to be started again.
*/
void
tcp_newreno_partialack(struct tcpcb *tp, struct tcphdr *th)
{
/*
* snd_una has not been updated and the socket send buffer
* not yet drained of the acked data, so we have to leave
* snd_una as it was to get the correct data offset in
* tcp_output().
*/
tcp_seq onxt = tp->snd_nxt;
u_long ocwnd = tp->snd_cwnd;
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->t_rtttime = 0;
tp->snd_nxt = th->th_ack;
/*
* Set snd_cwnd to one segment beyond acknowledged offset
* (tp->snd_una not yet updated when this function is called)
*/
tp->snd_cwnd = tp->t_maxseg + (th->th_ack - tp->snd_una);
(void)tcp_output(tp);
tp->snd_cwnd = ocwnd;
if (SEQ_GT(onxt, tp->snd_nxt))
tp->snd_nxt = onxt;
/*
* Partial window deflation. Relies on fact that tp->snd_una
* not updated yet.
*/
if (tp->snd_cwnd > th->th_ack - tp->snd_una)
tp->snd_cwnd -= th->th_ack - tp->snd_una;
else
tp->snd_cwnd = 0;
tp->snd_cwnd += tp->t_maxseg;
}
int
tcp_mss_adv(struct mbuf *m, int af)
{
int mss = 0;
int iphlen;
struct ifnet *ifp = NULL;
if (m && (m->m_flags & M_PKTHDR))
ifp = if_get(m->m_pkthdr.ph_ifidx);
switch (af) {
case AF_INET:
if (ifp != NULL)
mss = ifp->if_mtu;
iphlen = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
if (ifp != NULL)
mss = ifp->if_mtu;
iphlen = sizeof(struct ip6_hdr);
break;
#endif
default:
unhandled_af(af);
}
if_put(ifp);
mss = mss - iphlen - sizeof(struct tcphdr);
return (max(mss, tcp_mssdflt));
}
/*
* TCP compressed state engine. Currently used to hold compressed
* state for SYN_RECEIVED.
*/
/* syn hash parameters */
int tcp_syn_hash_size = TCP_SYN_HASH_SIZE;
int tcp_syn_cache_limit = TCP_SYN_HASH_SIZE*TCP_SYN_BUCKET_SIZE;
int tcp_syn_bucket_limit = 3*TCP_SYN_BUCKET_SIZE;
int tcp_syn_use_limit = 100000;
struct syn_cache_set tcp_syn_cache[2];
int tcp_syn_cache_active;
#define SYN_HASH(sa, sp, dp, rand) \
(((sa)->s_addr ^ (rand)[0]) * \
(((((u_int32_t)(dp))<<16) + ((u_int32_t)(sp))) ^ (rand)[4]))
#ifndef INET6
#define SYN_HASHALL(hash, src, dst, rand) \
do { \
hash = SYN_HASH(&satosin(src)->sin_addr, \
satosin(src)->sin_port, \
satosin(dst)->sin_port, (rand)); \
} while (/*CONSTCOND*/ 0)
#else
#define SYN_HASH6(sa, sp, dp, rand) \
(((sa)->s6_addr32[0] ^ (rand)[0]) * \
((sa)->s6_addr32[1] ^ (rand)[1]) * \
((sa)->s6_addr32[2] ^ (rand)[2]) * \
((sa)->s6_addr32[3] ^ (rand)[3]) * \
(((((u_int32_t)(dp))<<16) + ((u_int32_t)(sp))) ^ (rand)[4]))
#define SYN_HASHALL(hash, src, dst, rand) \
do { \
switch ((src)->sa_family) { \
case AF_INET: \
hash = SYN_HASH(&satosin(src)->sin_addr, \
satosin(src)->sin_port, \
satosin(dst)->sin_port, (rand)); \
break; \
case AF_INET6: \
hash = SYN_HASH6(&satosin6(src)->sin6_addr, \
satosin6(src)->sin6_port, \
satosin6(dst)->sin6_port, (rand)); \
break; \
default: \
hash = 0; \
} \
} while (/*CONSTCOND*/0)
#endif /* INET6 */
void
syn_cache_rm(struct syn_cache *sc)
{
sc->sc_flags |= SCF_DEAD;
TAILQ_REMOVE(&sc->sc_buckethead->sch_bucket, sc, sc_bucketq);
sc->sc_tp = NULL;
LIST_REMOVE(sc, sc_tpq);
sc->sc_buckethead->sch_length--;
timeout_del(&sc->sc_timer);
sc->sc_set->scs_count--;
}
void
syn_cache_put(struct syn_cache *sc)
{
m_free(sc->sc_ipopts);
if (sc->sc_route4.ro_rt != NULL) {
rtfree(sc->sc_route4.ro_rt);
sc->sc_route4.ro_rt = NULL;
}
timeout_set(&sc->sc_timer, syn_cache_reaper, sc);
timeout_add(&sc->sc_timer, 0);
}
struct pool syn_cache_pool;
/*
* We don't estimate RTT with SYNs, so each packet starts with the default
* RTT and each timer step has a fixed timeout value.
*/
#define SYN_CACHE_TIMER_ARM(sc) \
do { \
TCPT_RANGESET((sc)->sc_rxtcur, \
TCPTV_SRTTDFLT * tcp_backoff[(sc)->sc_rxtshift], TCPTV_MIN, \
TCPTV_REXMTMAX); \
if (!timeout_initialized(&(sc)->sc_timer)) \
timeout_set_proc(&(sc)->sc_timer, syn_cache_timer, (sc)); \
timeout_add(&(sc)->sc_timer, (sc)->sc_rxtcur * (hz / PR_SLOWHZ)); \
} while (/*CONSTCOND*/0)
void
syn_cache_init(void)
{
int i;
/* Initialize the hash buckets. */
tcp_syn_cache[0].scs_buckethead = mallocarray(tcp_syn_hash_size,
sizeof(struct syn_cache_head), M_SYNCACHE, M_WAITOK|M_ZERO);
tcp_syn_cache[1].scs_buckethead = mallocarray(tcp_syn_hash_size,
sizeof(struct syn_cache_head), M_SYNCACHE, M_WAITOK|M_ZERO);
tcp_syn_cache[0].scs_size = tcp_syn_hash_size;
tcp_syn_cache[1].scs_size = tcp_syn_hash_size;
for (i = 0; i < tcp_syn_hash_size; i++) {
TAILQ_INIT(&tcp_syn_cache[0].scs_buckethead[i].sch_bucket);
TAILQ_INIT(&tcp_syn_cache[1].scs_buckethead[i].sch_bucket);
}
/* Initialize the syn cache pool. */
pool_init(&syn_cache_pool, sizeof(struct syn_cache), 0, IPL_SOFTNET,
0, "syncache", NULL);
}
void
syn_cache_insert(struct syn_cache *sc, struct tcpcb *tp)
{
struct syn_cache_set *set = &tcp_syn_cache[tcp_syn_cache_active];
struct syn_cache_head *scp;
struct syn_cache *sc2;
int i;
NET_ASSERT_LOCKED();
/*
* If there are no entries in the hash table, reinitialize
* the hash secrets. To avoid useless cache swaps and
* reinitialization, use it until the limit is reached.
* An empty cache is also the opportunity to resize the hash.
*/
if (set->scs_count == 0 && set->scs_use <= 0) {
set->scs_use = tcp_syn_use_limit;
if (set->scs_size != tcp_syn_hash_size) {
scp = mallocarray(tcp_syn_hash_size, sizeof(struct
syn_cache_head), M_SYNCACHE, M_NOWAIT|M_ZERO);
if (scp == NULL) {
/* Try again next time. */
set->scs_use = 0;
} else {
free(set->scs_buckethead, M_SYNCACHE,
set->scs_size *
sizeof(struct syn_cache_head));
set->scs_buckethead = scp;
set->scs_size = tcp_syn_hash_size;
for (i = 0; i < tcp_syn_hash_size; i++)
TAILQ_INIT(&scp[i].sch_bucket);
}
}
arc4random_buf(set->scs_random, sizeof(set->scs_random));
tcpstat_inc(tcps_sc_seedrandom);
}
SYN_HASHALL(sc->sc_hash, &sc->sc_src.sa, &sc->sc_dst.sa,
set->scs_random);
scp = &set->scs_buckethead[sc->sc_hash % set->scs_size];
sc->sc_buckethead = scp;
/*
* Make sure that we don't overflow the per-bucket
* limit or the total cache size limit.
*/
if (scp->sch_length >= tcp_syn_bucket_limit) {
tcpstat_inc(tcps_sc_bucketoverflow);
/*
* Someone might attack our bucket hash function. Reseed
* with random as soon as the passive syn cache gets empty.
*/
set->scs_use = 0;
/*
* The bucket is full. Toss the oldest element in the
* bucket. This will be the first entry in the bucket.
*/
sc2 = TAILQ_FIRST(&scp->sch_bucket);
#ifdef DIAGNOSTIC
/*
* This should never happen; we should always find an
* entry in our bucket.
*/
if (sc2 == NULL)
panic("%s: bucketoverflow: impossible", __func__);
#endif
syn_cache_rm(sc2);
syn_cache_put(sc2);
} else if (set->scs_count >= tcp_syn_cache_limit) {
struct syn_cache_head *scp2, *sce;
tcpstat_inc(tcps_sc_overflowed);
/*
* The cache is full. Toss the oldest entry in the
* first non-empty bucket we can find.
*
* XXX We would really like to toss the oldest
* entry in the cache, but we hope that this
* condition doesn't happen very often.
*/
scp2 = scp;
if (TAILQ_EMPTY(&scp2->sch_bucket)) {
sce = &set->scs_buckethead[set->scs_size];
for (++scp2; scp2 != scp; scp2++) {
if (scp2 >= sce)
scp2 = &set->scs_buckethead[0];
if (! TAILQ_EMPTY(&scp2->sch_bucket))
break;
}
#ifdef DIAGNOSTIC
/*
* This should never happen; we should always find a
* non-empty bucket.
*/
if (scp2 == scp)
panic("%s: cacheoverflow: impossible",
__func__);
#endif
}
sc2 = TAILQ_FIRST(&scp2->sch_bucket);
syn_cache_rm(sc2);
syn_cache_put(sc2);
}
/*
* Initialize the entry's timer.
*/
sc->sc_rxttot = 0;
sc->sc_rxtshift = 0;
SYN_CACHE_TIMER_ARM(sc);
/* Link it from tcpcb entry */
LIST_INSERT_HEAD(&tp->t_sc, sc, sc_tpq);
/* Put it into the bucket. */
TAILQ_INSERT_TAIL(&scp->sch_bucket, sc, sc_bucketq);
scp->sch_length++;
sc->sc_set = set;
set->scs_count++;
set->scs_use--;
tcpstat_inc(tcps_sc_added);
/*
* If the active cache has exceeded its use limit and
* the passive syn cache is empty, exchange their roles.
*/
if (set->scs_use <= 0 &&
tcp_syn_cache[!tcp_syn_cache_active].scs_count == 0)
tcp_syn_cache_active = !tcp_syn_cache_active;
}
/*
* Walk the timer queues, looking for SYN,ACKs that need to be retransmitted.
* If we have retransmitted an entry the maximum number of times, expire
* that entry.
*/
void
syn_cache_timer(void *arg)
{
struct syn_cache *sc = arg;
uint32_t now;
NET_LOCK();
if (sc->sc_flags & SCF_DEAD)
goto out;
now = READ_ONCE(tcp_now);
if (__predict_false(sc->sc_rxtshift == TCP_MAXRXTSHIFT)) {
/* Drop it -- too many retransmissions. */
goto dropit;
}
/*
* Compute the total amount of time this entry has
* been on a queue. If this entry has been on longer
* than the keep alive timer would allow, expire it.
*/
sc->sc_rxttot += sc->sc_rxtcur;
if (sc->sc_rxttot >= tcptv_keep_init)
goto dropit;
tcpstat_inc(tcps_sc_retransmitted);
(void) syn_cache_respond(sc, NULL, now);
/* Advance the timer back-off. */
sc->sc_rxtshift++;
SYN_CACHE_TIMER_ARM(sc);
out:
NET_UNLOCK();
return;
dropit:
tcpstat_inc(tcps_sc_timed_out);
syn_cache_rm(sc);
syn_cache_put(sc);
NET_UNLOCK();
}
void
syn_cache_reaper(void *arg)
{
struct syn_cache *sc = arg;
pool_put(&syn_cache_pool, (sc));
return;
}
/*
* Remove syn cache created by the specified tcb entry,
* because this does not make sense to keep them
* (if there's no tcb entry, syn cache entry will never be used)
*/
void
syn_cache_cleanup(struct tcpcb *tp)
{
struct syn_cache *sc, *nsc;
NET_ASSERT_LOCKED();
LIST_FOREACH_SAFE(sc, &tp->t_sc, sc_tpq, nsc) {
#ifdef DIAGNOSTIC
if (sc->sc_tp != tp)
panic("invalid sc_tp in syn_cache_cleanup");
#endif
syn_cache_rm(sc);
syn_cache_put(sc);
}
/* just for safety */
LIST_INIT(&tp->t_sc);
}
/*
* Find an entry in the syn cache.
*/
struct syn_cache *
syn_cache_lookup(struct sockaddr *src, struct sockaddr *dst,
struct syn_cache_head **headp, u_int rtableid)
{
struct syn_cache_set *sets[2];
struct syn_cache *sc;
struct syn_cache_head *scp;
u_int32_t hash;
int i;
NET_ASSERT_LOCKED();
/* Check the active cache first, the passive cache is likely empty. */
sets[0] = &tcp_syn_cache[tcp_syn_cache_active];
sets[1] = &tcp_syn_cache[!tcp_syn_cache_active];
for (i = 0; i < 2; i++) {
if (sets[i]->scs_count == 0)
continue;
SYN_HASHALL(hash, src, dst, sets[i]->scs_random);
scp = &sets[i]->scs_buckethead[hash % sets[i]->scs_size];
*headp = scp;
TAILQ_FOREACH(sc, &scp->sch_bucket, sc_bucketq) {
if (sc->sc_hash != hash)
continue;
if (!bcmp(&sc->sc_src, src, src->sa_len) &&
!bcmp(&sc->sc_dst, dst, dst->sa_len) &&
rtable_l2(rtableid) == rtable_l2(sc->sc_rtableid))
return (sc);
}
}
return (NULL);
}
/*
* This function gets called when we receive an ACK for a
* socket in the LISTEN state. We look up the connection
* in the syn cache, and if its there, we pull it out of
* the cache and turn it into a full-blown connection in
* the SYN-RECEIVED state.
*
* The return values may not be immediately obvious, and their effects
* can be subtle, so here they are:
*
* NULL SYN was not found in cache; caller should drop the
* packet and send an RST.
*
* -1 We were unable to create the new connection, and are
* aborting it. An ACK,RST is being sent to the peer
* (unless we got screwy sequence numbers; see below),
* because the 3-way handshake has been completed. Caller
* should not free the mbuf, since we may be using it. If
* we are not, we will free it.
*
* Otherwise, the return value is a pointer to the new socket
* associated with the connection.
*/
struct socket *
syn_cache_get(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th,
u_int hlen, u_int tlen, struct socket *so, struct mbuf *m, uint32_t now)
{
struct syn_cache *sc;
struct syn_cache_head *scp;
struct inpcb *inp, *oldinp;
struct tcpcb *tp = NULL;
struct mbuf *am;
struct socket *oso;
NET_ASSERT_LOCKED();
sc = syn_cache_lookup(src, dst, &scp, sotoinpcb(so)->inp_rtableid);
if (sc == NULL)
return (NULL);
/*
* Verify the sequence and ack numbers. Try getting the correct
* response again.
*/
if ((th->th_ack != sc->sc_iss + 1) ||
SEQ_LEQ(th->th_seq, sc->sc_irs) ||
SEQ_GT(th->th_seq, sc->sc_irs + 1 + sc->sc_win)) {
(void) syn_cache_respond(sc, m, now);
return ((struct socket *)(-1));
}
/* Remove this cache entry */
syn_cache_rm(sc);
/*
* Ok, create the full blown connection, and set things up
* as they would have been set up if we had created the
* connection when the SYN arrived. If we can't create
* the connection, abort it.
*/
oso = so;
so = sonewconn(so, SS_ISCONNECTED);
if (so == NULL)
goto resetandabort;
oldinp = sotoinpcb(oso);
inp = sotoinpcb(so);
#ifdef IPSEC
/*
* We need to copy the required security levels
* from the old pcb. Ditto for any other
* IPsec-related information.
*/
memcpy(inp->inp_seclevel, oldinp->inp_seclevel,
sizeof(oldinp->inp_seclevel));
#endif /* IPSEC */
#ifdef INET6
/*
* inp still has the OLD in_pcb stuff, set the
* v6-related flags on the new guy, too.
*/
inp->inp_flags |= (oldinp->inp_flags & INP_IPV6);
if (inp->inp_flags & INP_IPV6) {
inp->inp_ipv6.ip6_hlim = oldinp->inp_ipv6.ip6_hlim;
inp->inp_hops = oldinp->inp_hops;
} else
#endif /* INET6 */
{
inp->inp_ip.ip_ttl = oldinp->inp_ip.ip_ttl;
}
#if NPF > 0
if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
struct pf_divert *divert;
divert = pf_find_divert(m);
KASSERT(divert != NULL);
inp->inp_rtableid = divert->rdomain;
} else
#endif
/* inherit rtable from listening socket */
inp->inp_rtableid = sc->sc_rtableid;
inp->inp_lport = th->th_dport;
switch (src->sa_family) {
#ifdef INET6
case AF_INET6:
inp->inp_laddr6 = satosin6(dst)->sin6_addr;
break;
#endif /* INET6 */
case AF_INET:
inp->inp_laddr = satosin(dst)->sin_addr;
inp->inp_options = ip_srcroute(m);
if (inp->inp_options == NULL) {
inp->inp_options = sc->sc_ipopts;
sc->sc_ipopts = NULL;
}
break;
}
in_pcbrehash(inp);
/*
* Give the new socket our cached route reference.
*/
if (src->sa_family == AF_INET)
inp->inp_route = sc->sc_route4; /* struct assignment */
#ifdef INET6
else
inp->inp_route6 = sc->sc_route6;
#endif
sc->sc_route4.ro_rt = NULL;
am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */
if (am == NULL)
goto resetandabort;
am->m_len = src->sa_len;
memcpy(mtod(am, caddr_t), src, src->sa_len);
if (in_pcbconnect(inp, am)) {
(void) m_free(am);
goto resetandabort;
}
(void) m_free(am);
tp = intotcpcb(inp);
tp->t_flags = sototcpcb(oso)->t_flags & (TF_NOPUSH|TF_NODELAY);
if (sc->sc_request_r_scale != 15) {
tp->requested_s_scale = sc->sc_requested_s_scale;
tp->request_r_scale = sc->sc_request_r_scale;
tp->t_flags |= TF_REQ_SCALE|TF_RCVD_SCALE;
}
if (sc->sc_flags & SCF_TIMESTAMP)
tp->t_flags |= TF_REQ_TSTMP|TF_RCVD_TSTMP;
tp->t_template = tcp_template(tp);
if (tp->t_template == 0) {
tp = tcp_drop(tp, ENOBUFS); /* destroys socket */
so = NULL;
goto abort;
}
tp->sack_enable = sc->sc_flags & SCF_SACK_PERMIT;
tp->ts_modulate = sc->sc_modulate;
tp->ts_recent = sc->sc_timestamp;
tp->iss = sc->sc_iss;
tp->irs = sc->sc_irs;
tcp_sendseqinit(tp);
tp->snd_last = tp->snd_una;
#ifdef TCP_ECN
if (sc->sc_flags & SCF_ECN_PERMIT) {
tp->t_flags |= TF_ECN_PERMIT;
tcpstat_inc(tcps_ecn_accepts);
}
#endif
if (sc->sc_flags & SCF_SACK_PERMIT)
tp->t_flags |= TF_SACK_PERMIT;
#ifdef TCP_SIGNATURE
if (sc->sc_flags & SCF_SIGNATURE)
tp->t_flags |= TF_SIGNATURE;
#endif
tcp_rcvseqinit(tp);
tp->t_state = TCPS_SYN_RECEIVED;
tp->t_rcvtime = now;
tp->t_sndtime = now;
tp->t_rcvacktime = now;
tp->t_sndacktime = now;
TCP_TIMER_ARM(tp, TCPT_KEEP, tcptv_keep_init);
tcpstat_inc(tcps_accepts);
tcp_mss(tp, sc->sc_peermaxseg); /* sets t_maxseg */
if (sc->sc_peermaxseg)
tcp_mss_update(tp);
/* Reset initial window to 1 segment for retransmit */
if (sc->sc_rxtshift > 0)
tp->snd_cwnd = tp->t_maxseg;
tp->snd_wl1 = sc->sc_irs;
tp->rcv_up = sc->sc_irs + 1;
/*
* This is what would have happened in tcp_output() when
* the SYN,ACK was sent.
*/
tp->snd_up = tp->snd_una;
tp->snd_max = tp->snd_nxt = tp->iss+1;
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
if (sc->sc_win > 0 && SEQ_GT(tp->rcv_nxt + sc->sc_win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + sc->sc_win;
tp->last_ack_sent = tp->rcv_nxt;
tcpstat_inc(tcps_sc_completed);
syn_cache_put(sc);
return (so);
resetandabort:
tcp_respond(NULL, mtod(m, caddr_t), th, (tcp_seq)0, th->th_ack, TH_RST,
m->m_pkthdr.ph_rtableid, now);
abort:
m_freem(m);
if (so != NULL)
soabort(so);
syn_cache_put(sc);
tcpstat_inc(tcps_sc_aborted);
return ((struct socket *)(-1));
}
/*
* This function is called when we get a RST for a
* non-existent connection, so that we can see if the
* connection is in the syn cache. If it is, zap it.
*/
void
syn_cache_reset(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th,
u_int rtableid)
{
struct syn_cache *sc;
struct syn_cache_head *scp;
NET_ASSERT_LOCKED();
if ((sc = syn_cache_lookup(src, dst, &scp, rtableid)) == NULL)
return;
if (SEQ_LT(th->th_seq, sc->sc_irs) ||
SEQ_GT(th->th_seq, sc->sc_irs + 1))
return;
syn_cache_rm(sc);
tcpstat_inc(tcps_sc_reset);
syn_cache_put(sc);
}
void
syn_cache_unreach(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th,
u_int rtableid)
{
struct syn_cache *sc;
struct syn_cache_head *scp;
NET_ASSERT_LOCKED();
if ((sc = syn_cache_lookup(src, dst, &scp, rtableid)) == NULL)
return;
/* If the sequence number != sc_iss, then it's a bogus ICMP msg */
if (ntohl (th->th_seq) != sc->sc_iss) {
return;
}
/*
* If we've retransmitted 3 times and this is our second error,
* we remove the entry. Otherwise, we allow it to continue on.
* This prevents us from incorrectly nuking an entry during a
* spurious network outage.
*
* See tcp_notify().
*/
if ((sc->sc_flags & SCF_UNREACH) == 0 || sc->sc_rxtshift < 3) {
sc->sc_flags |= SCF_UNREACH;
return;
}
syn_cache_rm(sc);
tcpstat_inc(tcps_sc_unreach);
syn_cache_put(sc);
}
/*
* Given a LISTEN socket and an inbound SYN request, add
* this to the syn cache, and send back a segment:
* <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
* to the source.
*
* IMPORTANT NOTE: We do _NOT_ ACK data that might accompany the SYN.
* Doing so would require that we hold onto the data and deliver it
* to the application. However, if we are the target of a SYN-flood
* DoS attack, an attacker could send data which would eventually
* consume all available buffer space if it were ACKed. By not ACKing
* the data, we avoid this DoS scenario.
*/
int
syn_cache_add(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th,
u_int iphlen, struct socket *so, struct mbuf *m, u_char *optp, int optlen,
struct tcp_opt_info *oi, tcp_seq *issp, uint32_t now)
{
struct tcpcb tb, *tp;
long win;
struct syn_cache *sc;
struct syn_cache_head *scp;
struct mbuf *ipopts;
tp = sototcpcb(so);
/*
* RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
*
* Note this check is performed in tcp_input() very early on.
*/
/*
* Initialize some local state.
*/
win = sbspace(so, &so->so_rcv);
if (win > TCP_MAXWIN)
win = TCP_MAXWIN;
bzero(&tb, sizeof(tb));
#ifdef TCP_SIGNATURE
if (optp || (tp->t_flags & TF_SIGNATURE)) {
#else
if (optp) {
#endif
tb.pf = tp->pf;
tb.sack_enable = tp->sack_enable;
tb.t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
#ifdef TCP_SIGNATURE
if (tp->t_flags & TF_SIGNATURE)
tb.t_flags |= TF_SIGNATURE;
#endif
tb.t_state = TCPS_LISTEN;
if (tcp_dooptions(&tb, optp, optlen, th, m, iphlen, oi,
sotoinpcb(so)->inp_rtableid, now))
return (-1);
}
switch (src->sa_family) {
case AF_INET:
/*
* Remember the IP options, if any.
*/
ipopts = ip_srcroute(m);
break;
default:
ipopts = NULL;
}
/*
* See if we already have an entry for this connection.
* If we do, resend the SYN,ACK. We do not count this
* as a retransmission (XXX though maybe we should).
*/
sc = syn_cache_lookup(src, dst, &scp, sotoinpcb(so)->inp_rtableid);
if (sc != NULL) {
tcpstat_inc(tcps_sc_dupesyn);
if (ipopts) {
/*
* If we were remembering a previous source route,
* forget it and use the new one we've been given.
*/
m_free(sc->sc_ipopts);
sc->sc_ipopts = ipopts;
}
sc->sc_timestamp = tb.ts_recent;
if (syn_cache_respond(sc, m, now) == 0) {
tcpstat_inc(tcps_sndacks);
tcpstat_inc(tcps_sndtotal);
}
return (0);
}
sc = pool_get(&syn_cache_pool, PR_NOWAIT|PR_ZERO);
if (sc == NULL) {
m_free(ipopts);
return (-1);
}
/*
* Fill in the cache, and put the necessary IP and TCP
* options into the reply.
*/
memcpy(&sc->sc_src, src, src->sa_len);
memcpy(&sc->sc_dst, dst, dst->sa_len);
sc->sc_rtableid = sotoinpcb(so)->inp_rtableid;
sc->sc_flags = 0;
sc->sc_ipopts = ipopts;
sc->sc_irs = th->th_seq;
sc->sc_iss = issp ? *issp : arc4random();
sc->sc_peermaxseg = oi->maxseg;
sc->sc_ourmaxseg = tcp_mss_adv(m, sc->sc_src.sa.sa_family);
sc->sc_win = win;
sc->sc_timestamp = tb.ts_recent;
if ((tb.t_flags & (TF_REQ_TSTMP|TF_RCVD_TSTMP)) ==
(TF_REQ_TSTMP|TF_RCVD_TSTMP)) {
sc->sc_flags |= SCF_TIMESTAMP;
sc->sc_modulate = arc4random();
}
if ((tb.t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
(TF_RCVD_SCALE|TF_REQ_SCALE)) {
sc->sc_requested_s_scale = tb.requested_s_scale;
sc->sc_request_r_scale = 0;
/*
* Pick the smallest possible scaling factor that
* will still allow us to scale up to sb_max.
*
* We do this because there are broken firewalls that
* will corrupt the window scale option, leading to
* the other endpoint believing that our advertised
* window is unscaled. At scale factors larger than
* 5 the unscaled window will drop below 1500 bytes,
* leading to serious problems when traversing these
* broken firewalls.
*
* With the default sbmax of 256K, a scale factor
* of 3 will be chosen by this algorithm. Those who
* choose a larger sbmax should watch out
* for the compatibility problems mentioned above.
*
* RFC1323: The Window field in a SYN (i.e., a <SYN>
* or <SYN,ACK>) segment itself is never scaled.
*/
while (sc->sc_request_r_scale < TCP_MAX_WINSHIFT &&
(TCP_MAXWIN << sc->sc_request_r_scale) < sb_max)
sc->sc_request_r_scale++;
} else {
sc->sc_requested_s_scale = 15;
sc->sc_request_r_scale = 15;
}
#ifdef TCP_ECN
/*
* if both ECE and CWR flag bits are set, peer is ECN capable.
*/
if (tcp_do_ecn &&
(th->th_flags & (TH_ECE|TH_CWR)) == (TH_ECE|TH_CWR))
sc->sc_flags |= SCF_ECN_PERMIT;
#endif
/*
* Set SCF_SACK_PERMIT if peer did send a SACK_PERMITTED option
* (i.e., if tcp_dooptions() did set TF_SACK_PERMIT).
*/
if (tb.sack_enable && (tb.t_flags & TF_SACK_PERMIT))
sc->sc_flags |= SCF_SACK_PERMIT;
#ifdef TCP_SIGNATURE
if (tb.t_flags & TF_SIGNATURE)
sc->sc_flags |= SCF_SIGNATURE;
#endif
sc->sc_tp = tp;
if (syn_cache_respond(sc, m, now) == 0) {
syn_cache_insert(sc, tp);
tcpstat_inc(tcps_sndacks);
tcpstat_inc(tcps_sndtotal);
} else {
syn_cache_put(sc);
tcpstat_inc(tcps_sc_dropped);
}
return (0);
}
int
syn_cache_respond(struct syn_cache *sc, struct mbuf *m, uint32_t now)
{
u_int8_t *optp;
int optlen, error;
u_int16_t tlen;
struct ip *ip = NULL;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
#endif
struct tcphdr *th;
u_int hlen;
struct inpcb *inp;
switch (sc->sc_src.sa.sa_family) {
case AF_INET:
hlen = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
hlen = sizeof(struct ip6_hdr);
break;
#endif
default:
m_freem(m);
return (EAFNOSUPPORT);
}
/* Compute the size of the TCP options. */
optlen = 4 + (sc->sc_request_r_scale != 15 ? 4 : 0) +
((sc->sc_flags & SCF_SACK_PERMIT) ? 4 : 0) +
#ifdef TCP_SIGNATURE
((sc->sc_flags & SCF_SIGNATURE) ? TCPOLEN_SIGLEN : 0) +
#endif
((sc->sc_flags & SCF_TIMESTAMP) ? TCPOLEN_TSTAMP_APPA : 0);
tlen = hlen + sizeof(struct tcphdr) + optlen;
/*
* Create the IP+TCP header from scratch.
*/
m_freem(m);
#ifdef DIAGNOSTIC
if (max_linkhdr + tlen > MCLBYTES)
return (ENOBUFS);
#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && max_linkhdr + tlen > MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
m = NULL;
}
}
if (m == NULL)
return (ENOBUFS);
/* Fixup the mbuf. */
m->m_data += max_linkhdr;
m->m_len = m->m_pkthdr.len = tlen;
m->m_pkthdr.ph_ifidx = 0;
m->m_pkthdr.ph_rtableid = sc->sc_rtableid;
memset(mtod(m, u_char *), 0, tlen);
switch (sc->sc_src.sa.sa_family) {
case AF_INET:
ip = mtod(m, struct ip *);
ip->ip_dst = sc->sc_src.sin.sin_addr;
ip->ip_src = sc->sc_dst.sin.sin_addr;
ip->ip_p = IPPROTO_TCP;
th = (struct tcphdr *)(ip + 1);
th->th_dport = sc->sc_src.sin.sin_port;
th->th_sport = sc->sc_dst.sin.sin_port;
break;
#ifdef INET6
case AF_INET6:
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_dst = sc->sc_src.sin6.sin6_addr;
ip6->ip6_src = sc->sc_dst.sin6.sin6_addr;
ip6->ip6_nxt = IPPROTO_TCP;
/* ip6_plen will be updated in ip6_output() */
th = (struct tcphdr *)(ip6 + 1);
th->th_dport = sc->sc_src.sin6.sin6_port;
th->th_sport = sc->sc_dst.sin6.sin6_port;
break;
#endif
default:
unhandled_af(sc->sc_src.sa.sa_family);
}
th->th_seq = htonl(sc->sc_iss);
th->th_ack = htonl(sc->sc_irs + 1);
th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
th->th_flags = TH_SYN|TH_ACK;
#ifdef TCP_ECN
/* Set ECE for SYN-ACK if peer supports ECN. */
if (tcp_do_ecn && (sc->sc_flags & SCF_ECN_PERMIT))
th->th_flags |= TH_ECE;
#endif
th->th_win = htons(sc->sc_win);
/* th_sum already 0 */
/* th_urp already 0 */
/* Tack on the TCP options. */
optp = (u_int8_t *)(th + 1);
*optp++ = TCPOPT_MAXSEG;
*optp++ = 4;
*optp++ = (sc->sc_ourmaxseg >> 8) & 0xff;
*optp++ = sc->sc_ourmaxseg & 0xff;
/* Include SACK_PERMIT_HDR option if peer has already done so. */
if (sc->sc_flags & SCF_SACK_PERMIT) {
*((u_int32_t *)optp) = htonl(TCPOPT_SACK_PERMIT_HDR);
optp += 4;
}
if (sc->sc_request_r_scale != 15) {
*((u_int32_t *)optp) = htonl(TCPOPT_NOP << 24 |
TCPOPT_WINDOW << 16 | TCPOLEN_WINDOW << 8 |
sc->sc_request_r_scale);
optp += 4;
}
if (sc->sc_flags & SCF_TIMESTAMP) {
u_int32_t *lp = (u_int32_t *)(optp);
/* Form timestamp option as shown in appendix A of RFC 1323. */
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
*lp++ = htonl(now + sc->sc_modulate);
*lp = htonl(sc->sc_timestamp);
optp += TCPOLEN_TSTAMP_APPA;
}
#ifdef TCP_SIGNATURE
if (sc->sc_flags & SCF_SIGNATURE) {
union sockaddr_union src, dst;
struct tdb *tdb;
bzero(&src, sizeof(union sockaddr_union));
bzero(&dst, sizeof(union sockaddr_union));
src.sa.sa_len = sc->sc_src.sa.sa_len;
src.sa.sa_family = sc->sc_src.sa.sa_family;
dst.sa.sa_len = sc->sc_dst.sa.sa_len;
dst.sa.sa_family = sc->sc_dst.sa.sa_family;
switch (sc->sc_src.sa.sa_family) {
case 0: /*default to PF_INET*/
case AF_INET:
src.sin.sin_addr = mtod(m, struct ip *)->ip_src;
dst.sin.sin_addr = mtod(m, struct ip *)->ip_dst;
break;
#ifdef INET6
case AF_INET6:
src.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_src;
dst.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_dst;
break;
#endif /* INET6 */
}
tdb = gettdbbysrcdst(rtable_l2(sc->sc_rtableid),
0, &src, &dst, IPPROTO_TCP);
if (tdb == NULL) {
m_freem(m);
return (EPERM);
}
/* Send signature option */
*(optp++) = TCPOPT_SIGNATURE;
*(optp++) = TCPOLEN_SIGNATURE;
if (tcp_signature(tdb, sc->sc_src.sa.sa_family, m, th,
hlen, 0, optp) < 0) {
m_freem(m);
tdb_unref(tdb);
return (EINVAL);
}
tdb_unref(tdb);
optp += 16;
/* Pad options list to the next 32 bit boundary and
* terminate it.
*/
*optp++ = TCPOPT_NOP;
*optp++ = TCPOPT_EOL;
}
#endif /* TCP_SIGNATURE */
/* Compute the packet's checksum. */
switch (sc->sc_src.sa.sa_family) {
case AF_INET:
ip->ip_len = htons(tlen - hlen);
th->th_sum = 0;
th->th_sum = in_cksum(m, tlen);
break;
#ifdef INET6
case AF_INET6:
ip6->ip6_plen = htons(tlen - hlen);
th->th_sum = 0;
th->th_sum = in6_cksum(m, IPPROTO_TCP, hlen, tlen - hlen);
break;
#endif
}
/* use IPsec policy and ttl from listening socket, on SYN ACK */
inp = sc->sc_tp ? sc->sc_tp->t_inpcb : NULL;
/*
* Fill in some straggling IP bits. Note the stack expects
* ip_len to be in host order, for convenience.
*/
switch (sc->sc_src.sa.sa_family) {
case AF_INET:
ip->ip_len = htons(tlen);
ip->ip_ttl = inp ? inp->inp_ip.ip_ttl : ip_defttl;
if (inp != NULL)
ip->ip_tos = inp->inp_ip.ip_tos;
break;
#ifdef INET6
case AF_INET6:
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_plen = htons(tlen - hlen);
/* ip6_hlim will be initialized afterwards */
/* leave flowlabel = 0, it is legal and require no state mgmt */
break;
#endif
}
switch (sc->sc_src.sa.sa_family) {
case AF_INET:
error = ip_output(m, sc->sc_ipopts, &sc->sc_route4,
(ip_mtudisc ? IP_MTUDISC : 0), NULL, inp, 0);
break;
#ifdef INET6
case AF_INET6:
ip6->ip6_hlim = in6_selecthlim(inp);
error = ip6_output(m, NULL /*XXX*/, &sc->sc_route6, 0,
NULL, NULL);
break;
#endif
default:
error = EAFNOSUPPORT;
break;
}
return (error);
}
89
89
89
88
89
89
87
89
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* $OpenBSD: ip6_id.c,v 1.16 2021/03/10 10:21:49 jsg Exp $ */
/* $NetBSD: ip6_id.c,v 1.7 2003/09/13 21:32:59 itojun Exp $ */
/* $KAME: ip6_id.c,v 1.8 2003/09/06 13:41:06 itojun Exp $ */
/*
* Copyright (C) 2003 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright 1998 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Theo de Raadt <deraadt@openbsd.org> came up with the idea of using
* such a mathematical system to generate more random (yet non-repeating)
* ids to solve the resolver/named problem. But Niels designed the
* actual system based on the constraints.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* seed = random (bits - 1) bit
* n = prime, g0 = generator to n,
* j = random so that gcd(j,n-1) == 1
* g = g0^j mod n will be a generator again.
*
* X[0] = random seed.
* X[n] = a*X[n-1]+b mod m is a Linear Congruential Generator
* with a = 7^(even random) mod m,
* b = random with gcd(b,m) == 1
* m = constant and a maximal period of m-1.
*
* The transaction id is determined by:
* id[n] = seed xor (g^X[n] mod n)
*
* Effectivly the id is restricted to the lower (bits - 1) bits, thus
* yielding two different cycles by toggling the msb on and off.
* This avoids reuse issues caused by reseeding.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
struct randomtab {
const int ru_bits; /* resulting bits */
const long ru_out; /* Time after which will be reseeded */
const u_int32_t ru_max; /* Uniq cycle, avoid blackjack prediction */
const u_int32_t ru_gen; /* Starting generator */
const u_int32_t ru_n; /* ru_n: prime, ru_n - 1: product of pfacts[] */
const u_int32_t ru_agen; /* determine ru_a as ru_agen^(2*rand) */
const u_int32_t ru_m; /* ru_m = 2^x*3^y */
const u_int32_t pfacts[4]; /* factors of ru_n */
u_int32_t ru_counter;
u_int32_t ru_msb;
u_int32_t ru_x;
u_int32_t ru_seed, ru_seed2;
u_int32_t ru_a, ru_b;
u_int32_t ru_g;
long ru_reseed;
};
static struct randomtab randomtab_20 = {
20, /* resulting bits */
180, /* Time after which will be reseeded */
200000, /* Uniq cycle, avoid blackjack prediction */
2, /* Starting generator */
524269, /* RU_N-1 = 2^2*3^2*14563 */
7, /* determine ru_a as RU_AGEN^(2*rand) */
279936, /* RU_M = 2^7*3^7 - don't change */
{ 2, 3, 14563, 0 }, /* factors of ru_n */
};
u_int32_t ip6id_pmod(u_int32_t, u_int32_t, u_int32_t);
void ip6id_initid(struct randomtab *);
u_int32_t ip6id_randomid(struct randomtab *);
/*
* Do a fast modular exponation, returned value will be in the range
* of 0 - (mod-1)
*/
u_int32_t
ip6id_pmod(u_int32_t gen, u_int32_t expo, u_int32_t mod)
{
u_int64_t s, t, u;
s = 1;
t = gen;
u = expo;
while (u) {
if (u & 1)
s = (s * t) % mod;
u >>= 1;
t = (t * t) % mod;
}
return (s);
}
/*
* Initializes the seed and chooses a suitable generator. Also toggles
* the msb flag. The msb flag is used to generate two distinct
* cycles of random numbers and thus avoiding reuse of ids.
*
* This function is called from id_randomid() when needed, an
* application does not have to worry about it.
*/
void
ip6id_initid(struct randomtab *p)
{
u_int32_t j, i;
int noprime = 1;
p->ru_x = arc4random_uniform(p->ru_m);
/* (bits - 1) bits of random seed */
p->ru_seed = arc4random() & (~0U >> (32 - p->ru_bits + 1));
p->ru_seed2 = arc4random() & (~0U >> (32 - p->ru_bits + 1));
/* Determine the LCG we use */
p->ru_b = (arc4random() & (~0U >> (32 - p->ru_bits))) | 1;
p->ru_a = ip6id_pmod(p->ru_agen,
(arc4random() & (~0U >> (32 - p->ru_bits))) & (~1U), p->ru_m);
while (p->ru_b % 3 == 0)
p->ru_b += 2;
j = arc4random_uniform(p->ru_n);
/*
* Do a fast gcd(j, RU_N - 1), so we can find a j with
* gcd(j, RU_N - 1) == 1, giving a new generator for
* RU_GEN^j mod RU_N
*/
while (noprime) {
for (i = 0; p->pfacts[i] > 0; i++)
if (j % p->pfacts[i] == 0)
break;
if (p->pfacts[i] == 0)
noprime = 0;
else
j = (j + 1) % p->ru_n;
}
p->ru_g = ip6id_pmod(p->ru_gen, j, p->ru_n);
p->ru_counter = 0;
p->ru_reseed = getuptime() + p->ru_out;
p->ru_msb = p->ru_msb ? 0 : (1U << (p->ru_bits - 1));
}
u_int32_t
ip6id_randomid(struct randomtab *p)
{
int i, n;
if (p->ru_counter >= p->ru_max || getuptime() > p->ru_reseed)
ip6id_initid(p);
/* Skip a random number of ids */
n = arc4random() & 0x3;
if (p->ru_counter + n >= p->ru_max)
ip6id_initid(p);
for (i = 0; i <= n; i++) {
/* Linear Congruential Generator */
p->ru_x = (u_int32_t)((u_int64_t)p->ru_a * p->ru_x + p->ru_b) % p->ru_m;
}
p->ru_counter += i;
return (p->ru_seed ^ ip6id_pmod(p->ru_g, p->ru_seed2 + p->ru_x, p->ru_n)) |
p->ru_msb;
}
u_int32_t
ip6_randomflowlabel(void)
{
return ip6id_randomid(&randomtab_20) & 0xfffff;
}
7
3
1
5
5
4
1
1
1
1
4
3
3
3
2
4
4
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
/* $OpenBSD: tty_endrun.c,v 1.8 2018/02/19 08:59:52 mpi Exp $ */
/*
* Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
* Copyright (c) 2009 Kevin Steves <stevesk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* A tty line discipline to decode the EndRun Technologies native
* time-of-day message.
* http://www.endruntechnologies.com/
*/
/*
* EndRun Format:
*
* T YYYY DDD HH:MM:SS zZZ m<CR><LF>
*
* T is the Time Figure of Merit (TFOM) character (described below).
* This is the on-time character, transmitted during the first
* millisecond of each second.
*
* YYYY is the year
* DDD is the day-of-year
* : is the colon character (0x3A)
* HH is the hour of the day
* MM is the minute of the hour
* SS is the second of the minute
* z is the sign of the offset to UTC, + implies time is ahead of UTC.
* ZZ is the magnitude of the offset to UTC in units of half-hours.
* Non-zero only when the Timemode is Local.
* m is the Timemode character and is one of:
* G = GPS
* L = Local
* U = UTC
* <CR> is the ASCII carriage return character (0x0D)
* <LF> is the ASCII line feed character (0x0A)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sensors.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/time.h>
#ifdef ENDRUN_DEBUG
#define DPRINTFN(n, x) do { if (endrundebug > (n)) printf x; } while (0)
int endrundebug = 0;
#else
#define DPRINTFN(n, x)
#endif
#define DPRINTF(x) DPRINTFN(0, x)
void endrunattach(int);
#define ENDRUNLEN 27 /* strlen("6 2009 018 20:41:17 +00 U\r\n") */
#define NUMFLDS 6
#ifdef ENDRUN_DEBUG
#define TRUSTTIME 30
#else
#define TRUSTTIME (10 * 60) /* 10 minutes */
#endif
int endrun_count, endrun_nxid;
struct endrun {
char cbuf[ENDRUNLEN]; /* receive buffer */
struct ksensor time; /* the timedelta sensor */
struct ksensor signal; /* signal status */
struct ksensordev timedev;
struct timespec ts; /* current timestamp */
struct timespec lts; /* timestamp of last TFOM */
struct timeout endrun_tout; /* invalidate sensor */
int64_t gap; /* gap between two sentences */
int64_t last; /* last time rcvd */
#define SYNC_SCAN 1 /* scanning for '\n' */
#define SYNC_EOL 2 /* '\n' seen, next char TFOM */
int sync;
int pos; /* position in rcv buffer */
int no_pps; /* no PPS although requested */
#ifdef ENDRUN_DEBUG
char tfom;
#endif
};
/* EndRun decoding */
void endrun_scan(struct endrun *, struct tty *);
void endrun_decode(struct endrun *, struct tty *, char *fld[], int fldcnt);
/* date and time conversion */
int endrun_atoi(char *s, int len);
int endrun_date_to_nano(char *s1, char *s2, int64_t *nano);
int endrun_time_to_nano(char *s, int64_t *nano);
int endrun_offset_to_nano(char *s, int64_t *nano);
/* degrade the timedelta sensor */
void endrun_timeout(void *);
void
endrunattach(int dummy)
{
}
int
endrunopen(dev_t dev, struct tty *tp, struct proc *p)
{
struct endrun *np;
int error;
DPRINTF(("endrunopen\n"));
if (tp->t_line == ENDRUNDISC)
return ENODEV;
if ((error = suser(p)) != 0)
return error;
np = malloc(sizeof(struct endrun), M_DEVBUF, M_WAITOK|M_ZERO);
snprintf(np->timedev.xname, sizeof(np->timedev.xname), "endrun%d",
endrun_nxid++);
endrun_count++;
np->time.status = SENSOR_S_UNKNOWN;
np->time.type = SENSOR_TIMEDELTA;
#ifndef ENDRUN_DEBUG
np->time.flags = SENSOR_FINVALID;
#endif
sensor_attach(&np->timedev, &np->time);
np->signal.type = SENSOR_PERCENT;
np->signal.status = SENSOR_S_UNKNOWN;
np->signal.value = 100000LL;
strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
sensor_attach(&np->timedev, &np->signal);
np->sync = SYNC_SCAN;
#ifdef ENDRUN_DEBUG
np->tfom = '0';
#endif
tp->t_sc = (caddr_t)np;
error = linesw[TTYDISC].l_open(dev, tp, p);
if (error) {
free(np, M_DEVBUF, sizeof(*np));
tp->t_sc = NULL;
} else {
sensordev_install(&np->timedev);
timeout_set(&np->endrun_tout, endrun_timeout, np);
}
return error;
}
int
endrunclose(struct tty *tp, int flags, struct proc *p)
{
struct endrun *np = (struct endrun *)tp->t_sc;
DPRINTF(("endrunclose\n"));
tp->t_line = TTYDISC; /* switch back to termios */
timeout_del(&np->endrun_tout);
sensordev_deinstall(&np->timedev);
free(np, M_DEVBUF, sizeof(*np));
tp->t_sc = NULL;
endrun_count--;
if (endrun_count == 0)
endrun_nxid = 0;
return linesw[TTYDISC].l_close(tp, flags, p);
}
/* collect EndRun sentence from tty */
int
endruninput(int c, struct tty *tp)
{
struct endrun *np = (struct endrun *)tp->t_sc;
struct timespec ts;
int64_t gap;
long tmin, tmax;
if (np->sync == SYNC_EOL) {
nanotime(&ts);
np->pos = 0;
np->sync = SYNC_SCAN;
np->cbuf[np->pos++] = c; /* TFOM char */
gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
(np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
np->lts.tv_sec = ts.tv_sec;
np->lts.tv_nsec = ts.tv_nsec;
if (gap <= np->gap)
goto nogap;
np->ts.tv_sec = ts.tv_sec;
np->ts.tv_nsec = ts.tv_nsec;
np->gap = gap;
/*
* If a tty timestamp is available, make sure its value is
* reasonable by comparing against the timestamp just taken.
* If they differ by more than 2 seconds, assume no PPS signal
* is present, note the fact, and keep using the timestamp
* value. When this happens, the sensor state is set to
* CRITICAL later when the EndRun sentence is decoded.
*/
if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
if (tmax - tmin > 1)
np->no_pps = 1;
else {
np->ts.tv_sec = tp->t_tv.tv_sec;
np->ts.tv_nsec = tp->t_tv.tv_usec *
1000L;
np->no_pps = 0;
}
}
} else if (c == '\n') {
if (np->pos == ENDRUNLEN - 1) {
/* don't copy '\n' into cbuf */
np->cbuf[np->pos] = '\0';
endrun_scan(np, tp);
}
np->sync = SYNC_EOL;
} else {
if (np->pos < ENDRUNLEN - 1)
np->cbuf[np->pos++] = c;
}
nogap:
/* pass data to termios */
return linesw[TTYDISC].l_rint(c, tp);
}
/* Scan the EndRun sentence just received */
void
endrun_scan(struct endrun *np, struct tty *tp)
{
int fldcnt = 0, n;
char *fld[NUMFLDS], *cs;
DPRINTFN(1, ("%s\n", np->cbuf));
/* split into fields */
fld[fldcnt++] = &np->cbuf[0];
for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
switch (np->cbuf[n]) {
case '\r':
np->cbuf[n] = '\0';
cs = &np->cbuf[n + 1];
break;
case ' ':
if (fldcnt < NUMFLDS) {
np->cbuf[n] = '\0';
fld[fldcnt++] = &np->cbuf[n + 1];
} else {
DPRINTF(("endrun: nr of fields in sentence "
"exceeds expected: %d\n", NUMFLDS));
return;
}
break;
}
}
endrun_decode(np, tp, fld, fldcnt);
}
/* Decode the time string */
void
endrun_decode(struct endrun *np, struct tty *tp, char *fld[], int fldcnt)
{
int64_t date_nano, time_nano, offset_nano, endrun_now;
char tfom;
int jumped = 0;
if (fldcnt != NUMFLDS) {
DPRINTF(("endrun: field count mismatch, %d\n", fldcnt));
return;
}
if (endrun_time_to_nano(fld[3], &time_nano) == -1) {
DPRINTF(("endrun: illegal time, %s\n", fld[3]));
return;
}
if (endrun_date_to_nano(fld[1], fld[2], &date_nano) == -1) {
DPRINTF(("endrun: illegal date, %s %s\n", fld[1], fld[2]));
return;
}
offset_nano = 0;
/* only parse offset when timemode is local */
if (fld[5][0] == 'L' &&
endrun_offset_to_nano(fld[4], &offset_nano) == -1) {
DPRINTF(("endrun: illegal offset, %s\n", fld[4]));
return;
}
endrun_now = date_nano + time_nano + offset_nano;
if (endrun_now <= np->last) {
DPRINTF(("endrun: time not monotonically increasing "
"last %lld now %lld\n",
(long long)np->last, (long long)endrun_now));
jumped = 1;
}
np->last = endrun_now;
np->gap = 0LL;
#ifdef ENDRUN_DEBUG
if (np->time.status == SENSOR_S_UNKNOWN) {
np->time.status = SENSOR_S_OK;
timeout_add_sec(&np->endrun_tout, TRUSTTIME);
}
#endif
np->time.value = np->ts.tv_sec * 1000000000LL +
np->ts.tv_nsec - endrun_now;
np->time.tv.tv_sec = np->ts.tv_sec;
np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
if (np->time.status == SENSOR_S_UNKNOWN) {
np->time.status = SENSOR_S_OK;
np->time.flags &= ~SENSOR_FINVALID;
strlcpy(np->time.desc, "EndRun", sizeof(np->time.desc));
}
/*
* Only update the timeout if the clock reports the time as valid.
*
* Time Figure Of Merit (TFOM) values:
*
* 6 - time error is < 100 us
* 7 - time error is < 1 ms
* 8 - time error is < 10 ms
* 9 - time error is > 10 ms,
* unsynchronized state if never locked to CDMA
*/
switch (tfom = fld[0][0]) {
case '6':
case '7':
case '8':
np->time.status = SENSOR_S_OK;
np->signal.status = SENSOR_S_OK;
break;
case '9':
np->signal.status = SENSOR_S_WARN;
break;
default:
DPRINTF(("endrun: invalid TFOM: '%c'\n", tfom));
np->signal.status = SENSOR_S_CRIT;
break;
}
#ifdef ENDRUN_DEBUG
if (np->tfom != tfom) {
DPRINTF(("endrun: TFOM changed from %c to %c\n",
np->tfom, tfom));
np->tfom = tfom;
}
#endif
if (jumped)
np->time.status = SENSOR_S_WARN;
if (np->time.status == SENSOR_S_OK)
timeout_add_sec(&np->endrun_tout, TRUSTTIME);
/*
* If tty timestamping is requested, but no PPS signal is present, set
* the sensor state to CRITICAL.
*/
if (np->no_pps)
np->time.status = SENSOR_S_CRIT;
}
int
endrun_atoi(char *s, int len)
{
int n;
char *p;
/* make sure the input contains only numbers */
for (n = 0, p = s; n < len && *p && *p >= '0' && *p <= '9'; n++, p++)
;
if (n != len || *p != '\0')
return -1;
for (n = 0; *s; s++)
n = n * 10 + *s - '0';
return n;
}
/*
* Convert date fields from EndRun to nanoseconds since the epoch.
* The year string must be of the form YYYY .
* The day of year string must be of the form DDD .
* Return 0 on success, -1 if illegal characters are encountered.
*/
int
endrun_date_to_nano(char *y, char *doy, int64_t *nano)
{
struct clock_ymdhms clock;
time_t secs;
int n, i;
int year_days = 365;
int month_days[] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
#define FEBRUARY 2
#define LEAPYEAR(x) \
((x) % 4 == 0 && \
(x) % 100 != 0) || \
(x) % 400 == 0
if ((n = endrun_atoi(y, 4)) == -1)
return -1;
clock.dt_year = n;
if (LEAPYEAR(n)) {
month_days[FEBRUARY]++;
year_days++;
}
if ((n = endrun_atoi(doy, 3)) == -1 || n == 0 || n > year_days)
return -1;
/* convert day of year to month, day */
for (i = 1; n > month_days[i]; i++) {
n -= month_days[i];
}
clock.dt_mon = i;
clock.dt_day = n;
DPRINTFN(1, ("mm/dd %d/%d\n", i, n));
clock.dt_hour = clock.dt_min = clock.dt_sec = 0;
secs = clock_ymdhms_to_secs(&clock);
*nano = secs * 1000000000LL;
return 0;
}
/*
* Convert time field from EndRun to nanoseconds since midnight.
* The string must be of the form HH:MM:SS .
* Return 0 on success, -1 if illegal characters are encountered.
*/
int
endrun_time_to_nano(char *s, int64_t *nano)
{
struct clock_ymdhms clock;
time_t secs;
int n;
if (s[2] != ':' || s[5] != ':')
return -1;
s[2] = '\0';
s[5] = '\0';
if ((n = endrun_atoi(&s[0], 2)) == -1 || n > 23)
return -1;
clock.dt_hour = n;
if ((n = endrun_atoi(&s[3], 2)) == -1 || n > 59)
return -1;
clock.dt_min = n;
if ((n = endrun_atoi(&s[6], 2)) == -1 || n > 60)
return -1;
clock.dt_sec = n;
DPRINTFN(1, ("hh:mm:ss %d:%d:%d\n", (int)clock.dt_hour,
(int)clock.dt_min,
(int)clock.dt_sec));
secs = clock.dt_hour * 3600
+ clock.dt_min * 60
+ clock.dt_sec;
DPRINTFN(1, ("secs %lu\n", (unsigned long)secs));
*nano = secs * 1000000000LL;
return 0;
}
int
endrun_offset_to_nano(char *s, int64_t *nano)
{
time_t secs;
int n;
if (!(s[0] == '+' || s[0] == '-'))
return -1;
if ((n = endrun_atoi(&s[1], 2)) == -1)
return -1;
secs = n * 30 * 60;
*nano = secs * 1000000000LL;
if (s[0] == '+')
*nano = -*nano;
DPRINTFN(1, ("offset secs %lu nanosecs %lld\n",
(unsigned long)secs, (long long)*nano));
return 0;
}
/*
* Degrade the sensor state if we received no EndRun string for more than
* TRUSTTIME seconds.
*/
void
endrun_timeout(void *xnp)
{
struct endrun *np = xnp;
if (np->time.status == SENSOR_S_OK) {
np->time.status = SENSOR_S_WARN;
/*
* further degrade in TRUSTTIME seconds if no new valid EndRun
* strings are received.
*/
timeout_add_sec(&np->endrun_tout, TRUSTTIME);
} else
np->time.status = SENSOR_S_CRIT;
}
1
1
1
3
1
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
/* $OpenBSD: uhid.c,v 1.89 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: uhid.c,v 1.57 2003/03/11 16:44:00 augustss Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* HID spec: https://www.usb.org/sites/default/files/hid1_11.pdf
*/
#include "fido.h"
#include "ujoy.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/selinfo.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/uhidev.h>
#include <dev/usb/uhid.h>
#ifdef UHID_DEBUG
#define DPRINTF(x) do { if (uhiddebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0)
int uhiddebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
int uhid_match(struct device *, void *, void *);
struct cfdriver uhid_cd = {
NULL, "uhid", DV_DULL
};
const struct cfattach uhid_ca = {
sizeof(struct uhid_softc),
uhid_match,
uhid_attach,
uhid_detach,
};
struct uhid_softc *
uhid_lookup(dev_t dev)
{
struct uhid_softc *sc = NULL;
struct cdevsw *cdev;
struct cfdriver *cd;
cdev = &cdevsw[major(dev)];
if (cdev->d_open == uhidopen)
cd = &uhid_cd;
#if NFIDO > 0
else if (cdev->d_open == fidoopen)
cd = &fido_cd;
#endif
#if NUJOY > 0
else if (cdev->d_open == ujoyopen)
cd = &ujoy_cd;
#endif
else
return (NULL);
if (UHIDUNIT(dev) < cd->cd_ndevs)
sc = cd->cd_devs[UHIDUNIT(dev)];
return (sc);
}
int
uhid_match(struct device *parent, void *match, void *aux)
{
struct uhidev_attach_arg *uha = aux;
if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
return (UMATCH_NONE);
return (UMATCH_IFACECLASS_GENERIC);
}
void
uhid_attach(struct device *parent, struct device *self, void *aux)
{
struct uhid_softc *sc = (struct uhid_softc *)self;
struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
int size, repid;
void *desc;
sc->sc_hdev.sc_intr = uhid_intr;
sc->sc_hdev.sc_parent = uha->parent;
sc->sc_hdev.sc_udev = uha->uaa->device;
sc->sc_hdev.sc_report_id = uha->reportid;
uhidev_get_report_desc(uha->parent, &desc, &size);
repid = uha->reportid;
sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
printf(": input=%d, output=%d, feature=%d\n",
sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize, sc->sc_hdev.sc_fsize);
}
int
uhid_detach(struct device *self, int flags)
{
struct uhid_softc *sc = (struct uhid_softc *)self;
int s;
int maj, mn;
DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
if (sc->sc_hdev.sc_state & UHIDEV_OPEN) {
s = splusb();
if (--sc->sc_refcnt >= 0) {
/* Wake everyone */
wakeup(&sc->sc_q);
/* Wait for processes to go away. */
usb_detach_wait(&sc->sc_hdev.sc_dev);
}
splx(s);
}
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == uhidopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
s = splusb();
klist_invalidate(&sc->sc_rsel.si_note);
splx(s);
return (0);
}
void
uhid_intr(struct uhidev *addr, void *data, u_int len)
{
struct uhid_softc *sc = (struct uhid_softc *)addr;
#ifdef UHID_DEBUG
if (uhiddebug > 5) {
u_int32_t i;
DPRINTF(("uhid_intr: data ="));
for (i = 0; i < len; i++)
DPRINTF((" %02x", ((u_char *)data)[i]));
DPRINTF(("\n"));
}
#endif
(void)b_to_q(data, len, &sc->sc_q);
if (sc->sc_state & UHID_ASLP) {
sc->sc_state &= ~UHID_ASLP;
DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
wakeup(&sc->sc_q);
}
selwakeup(&sc->sc_rsel);
}
int
uhidopen(dev_t dev, int flag, int mode, struct proc *p)
{
return (uhid_do_open(dev, flag, mode, p));
}
int
uhid_do_open(dev_t dev, int flag, int mode, struct proc *p)
{
struct uhid_softc *sc;
int error;
if ((sc = uhid_lookup(dev)) == NULL)
return (ENXIO);
DPRINTF(("uhidopen: sc=%p\n", sc));
if (usbd_is_dying(sc->sc_hdev.sc_udev))
return (ENXIO);
error = uhidev_open(&sc->sc_hdev);
if (error)
return (error);
clalloc(&sc->sc_q, UHID_BSIZE, 0);
sc->sc_obuf = malloc(sc->sc_hdev.sc_osize, M_USBDEV, M_WAITOK);
return (0);
}
int
uhidclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct uhid_softc *sc;
if ((sc = uhid_lookup(dev)) == NULL)
return (ENXIO);
DPRINTF(("uhidclose: sc=%p\n", sc));
clfree(&sc->sc_q);
free(sc->sc_obuf, M_USBDEV, sc->sc_hdev.sc_osize);
uhidev_close(&sc->sc_hdev);
return (0);
}
int
uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
{
int s;
int error = 0;
size_t length;
u_char buffer[UHID_CHUNK];
DPRINTFN(1, ("uhidread\n"));
s = splusb();
while (sc->sc_q.c_cc == 0) {
if (flag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
sc->sc_state |= UHID_ASLP;
DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
error = tsleep_nsec(&sc->sc_q, PZERO|PCATCH, "uhidrea", INFSLP);
DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
if (usbd_is_dying(sc->sc_hdev.sc_udev))
error = EIO;
if (error) {
sc->sc_state &= ~UHID_ASLP;
break;
}
}
splx(s);
/* Transfer as many chunks as possible. */
while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
length = ulmin(sc->sc_q.c_cc, uio->uio_resid);
if (length > sizeof(buffer))
length = sizeof(buffer);
/* Remove a small chunk from the input queue. */
(void) q_to_b(&sc->sc_q, buffer, length);
DPRINTFN(5, ("uhidread: got %zu chars\n", length));
/* Copy the data to the user process. */
if ((error = uiomove(buffer, length, uio)) != 0)
break;
}
return (error);
}
int
uhidread(dev_t dev, struct uio *uio, int flag)
{
struct uhid_softc *sc;
int error;
if ((sc = uhid_lookup(dev)) == NULL)
return (ENXIO);
sc->sc_refcnt++;
error = uhid_do_read(sc, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_hdev.sc_dev);
return (error);
}
int
uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
{
int error;
int size;
DPRINTFN(1, ("uhidwrite\n"));
if (usbd_is_dying(sc->sc_hdev.sc_udev))
return (EIO);
size = sc->sc_hdev.sc_osize;
error = 0;
if (uio->uio_resid > size)
return (EMSGSIZE);
else if (uio->uio_resid < size) {
/* don't leak kernel memory to the USB device */
memset(sc->sc_obuf + uio->uio_resid, 0, size - uio->uio_resid);
}
error = uiomove(sc->sc_obuf, uio->uio_resid, uio);
if (!error) {
if (uhidev_set_report(sc->sc_hdev.sc_parent,
UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, sc->sc_obuf,
size) != size)
error = EIO;
}
return (error);
}
int
uhidwrite(dev_t dev, struct uio *uio, int flag)
{
struct uhid_softc *sc;
int error;
if ((sc = uhid_lookup(dev)) == NULL)
return (ENXIO);
sc->sc_refcnt++;
error = uhid_do_write(sc, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_hdev.sc_dev);
return (error);
}
int
uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr,
int flag, struct proc *p)
{
int rc;
DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
if (usbd_is_dying(sc->sc_hdev.sc_udev))
return (EIO);
switch (cmd) {
case FIONBIO:
case FIOASYNC:
/* All handled in the upper FS layer. */
break;
case USB_GET_DEVICEINFO:
usbd_fill_deviceinfo(sc->sc_hdev.sc_udev,
(struct usb_device_info *)addr);
break;
case USB_GET_REPORT_DESC:
case USB_GET_REPORT:
case USB_SET_REPORT:
case USB_GET_REPORT_ID:
default:
rc = uhidev_ioctl(&sc->sc_hdev, cmd, addr, flag, p);
if (rc == -1)
rc = ENOTTY;
return rc;
}
return (0);
}
int
uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct uhid_softc *sc;
int error;
if ((sc = uhid_lookup(dev)) == NULL)
return (ENXIO);
sc->sc_refcnt++;
error = uhid_do_ioctl(sc, cmd, addr, flag, p);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_hdev.sc_dev);
return (error);
}
void filt_uhidrdetach(struct knote *);
int filt_uhidread(struct knote *, long);
int uhidkqfilter(dev_t, struct knote *);
void
filt_uhidrdetach(struct knote *kn)
{
struct uhid_softc *sc = (void *)kn->kn_hook;
int s;
s = splusb();
klist_remove_locked(&sc->sc_rsel.si_note, kn);
splx(s);
}
int
filt_uhidread(struct knote *kn, long hint)
{
struct uhid_softc *sc = (void *)kn->kn_hook;
kn->kn_data = sc->sc_q.c_cc;
return (kn->kn_data > 0);
}
const struct filterops uhidread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_uhidrdetach,
.f_event = filt_uhidread,
};
int
uhidkqfilter(dev_t dev, struct knote *kn)
{
struct uhid_softc *sc;
struct klist *klist;
int s;
if ((sc = uhid_lookup(dev)) == NULL)
return (ENXIO);
if (usbd_is_dying(sc->sc_hdev.sc_udev))
return (ENXIO);
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sc->sc_rsel.si_note;
kn->kn_fop = &uhidread_filtops;
break;
case EVFILT_WRITE:
return (seltrue_kqfilter(dev, kn));
default:
return (EINVAL);
}
kn->kn_hook = (void *)sc;
s = splusb();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
26
19
6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/* $OpenBSD: socketvar.h,v 1.110 2022/09/05 14:56:09 bluhm Exp $ */
/* $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $ */
/*-
* Copyright (c) 1982, 1986, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)socketvar.h 8.1 (Berkeley) 6/2/93
*/
#ifndef _SYS_SOCKETVAR_H_
#define _SYS_SOCKETVAR_H_
#include <sys/selinfo.h> /* for struct selinfo */
#include <sys/queue.h>
#include <sys/sigio.h> /* for struct sigio_ref */
#include <sys/task.h>
#include <sys/timeout.h>
#include <sys/rwlock.h>
#include <sys/refcnt.h>
#ifndef _SOCKLEN_T_DEFINED_
#define _SOCKLEN_T_DEFINED_
typedef __socklen_t socklen_t; /* length type for network syscalls */
#endif
TAILQ_HEAD(soqhead, socket);
/*
* Kernel structure per socket.
* Contains send and receive buffer queues,
* handle on protocol and pointer to protocol
* private data and error information.
*/
struct socket {
const struct protosw *so_proto; /* protocol handle */
struct rwlock so_lock; /* this socket lock */
struct refcnt so_refcnt; /* references to this socket */
void *so_pcb; /* protocol control block */
u_int so_state; /* internal state flags SS_*, below */
short so_type; /* generic type, see socket.h */
short so_options; /* from socket call, see socket.h */
short so_linger; /* time to linger while closing */
/*
* Variables for connection queueing.
* Socket where accepts occur is so_head in all subsidiary sockets.
* If so_head is 0, socket is not related to an accept.
* For head socket so_q0 queues partially completed connections,
* while so_q is a queue of connections ready to be accepted.
* If a connection is aborted and it has so_head set, then
* it has to be pulled out of either so_q0 or so_q.
* We allow connections to queue up based on current queue lengths
* and limit on number of queued connections for this socket.
*/
struct socket *so_head; /* back pointer to accept socket */
struct soqhead *so_onq; /* queue (q or q0) that we're on */
struct soqhead so_q0; /* queue of partial connections */
struct soqhead so_q; /* queue of incoming connections */
struct sigio_ref so_sigio; /* async I/O registration */
TAILQ_ENTRY(socket) so_qe; /* our queue entry (q or q0) */
short so_q0len; /* partials on so_q0 */
short so_qlen; /* number of connections on so_q */
short so_qlimit; /* max number queued connections */
u_long so_newconn; /* # of pending sonewconn() threads */
short so_timeo; /* connection timeout */
u_long so_oobmark; /* chars to oob mark */
u_int so_error; /* error affecting connection */
/*
* Variables for socket splicing, allocated only when needed.
*/
struct sosplice {
struct socket *ssp_socket; /* send data to drain socket */
struct socket *ssp_soback; /* back ref to source socket */
off_t ssp_len; /* number of bytes spliced */
off_t ssp_max; /* maximum number of bytes */
struct timeval ssp_idletv; /* idle timeout */
struct timeout ssp_idleto;
struct task ssp_task; /* task for somove */
} *so_sp;
/*
* Variables for socket buffering.
*/
struct sockbuf {
/* The following fields are all zeroed on flush. */
#define sb_startzero sb_cc
u_long sb_cc; /* actual chars in buffer */
u_long sb_datacc; /* data only chars in buffer */
u_long sb_hiwat; /* max actual char count */
u_long sb_wat; /* default watermark */
u_long sb_mbcnt; /* chars of mbufs used */
u_long sb_mbmax; /* max chars of mbufs to use */
long sb_lowat; /* low water mark */
struct mbuf *sb_mb; /* the mbuf chain */
struct mbuf *sb_mbtail; /* the last mbuf in the chain */
struct mbuf *sb_lastrecord;/* first mbuf of last record in
socket buffer */
short sb_flags; /* flags, see below */
/* End area that is zeroed on flush. */
#define sb_endzero sb_flags
uint64_t sb_timeo_nsecs;/* timeout for read/write */
struct selinfo sb_sel; /* process selecting read/write */
} so_rcv, so_snd;
#define SB_MAX (2*1024*1024) /* default for max chars in sockbuf */
#define SB_LOCK 0x01 /* lock on data queue */
#define SB_WANT 0x02 /* someone is waiting to lock */
#define SB_WAIT 0x04 /* someone is waiting for data/space */
#define SB_ASYNC 0x10 /* ASYNC I/O, need signals */
#define SB_SPLICE 0x20 /* buffer is splice source or drain */
#define SB_NOINTR 0x40 /* operations not interruptible */
void (*so_upcall)(struct socket *so, caddr_t arg, int waitf);
caddr_t so_upcallarg; /* Arg for above */
uid_t so_euid, so_ruid; /* who opened the socket */
gid_t so_egid, so_rgid;
pid_t so_cpid; /* pid of process that opened socket */
};
/*
* Socket state bits.
*/
#define SS_NOFDREF 0x001 /* no file table ref any more */
#define SS_ISCONNECTED 0x002 /* socket connected to a peer */
#define SS_ISCONNECTING 0x004 /* in process of connecting to peer */
#define SS_ISDISCONNECTING 0x008 /* in process of disconnecting */
#define SS_CANTSENDMORE 0x010 /* can't send more data to peer */
#define SS_CANTRCVMORE 0x020 /* can't receive more data from peer */
#define SS_RCVATMARK 0x040 /* at mark on input */
#define SS_ISDISCONNECTED 0x800 /* socket disconnected from peer */
#define SS_PRIV 0x080 /* privileged for broadcast, raw... */
#define SS_CONNECTOUT 0x1000 /* connect, not accept, at this end */
#define SS_ISSENDING 0x2000 /* hint for lower layer */
#define SS_DNS 0x4000 /* created using SOCK_DNS socket(2) */
#define SS_NEWCONN_WAIT 0x8000 /* waiting sonewconn() relock */
#define SS_YP 0x10000 /* created using ypconnect(2) */
#ifdef _KERNEL
#include <lib/libkern/libkern.h>
void soassertlocked(struct socket *);
static inline void
soref(struct socket *so)
{
refcnt_take(&so->so_refcnt);
}
static inline void
sorele(struct socket *so)
{
refcnt_rele_wake(&so->so_refcnt);
}
/*
* Macros for sockets and socket buffering.
*/
#define isspliced(so) ((so)->so_sp && (so)->so_sp->ssp_socket)
#define issplicedback(so) ((so)->so_sp && (so)->so_sp->ssp_soback)
/*
* Do we need to notify the other side when I/O is possible?
*/
static inline int
sb_notify(struct socket *so, struct sockbuf *sb)
{
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
soassertlocked(so);
return ((sb->sb_flags & (SB_WAIT|SB_ASYNC|SB_SPLICE)) != 0 ||
!klist_empty(&sb->sb_sel.si_note));
}
/*
* How much space is there in a socket buffer (so->so_snd or so->so_rcv)?
* This is problematical if the fields are unsigned, as the space might
* still be negative (cc > hiwat or mbcnt > mbmax). Should detect
* overflow and return 0.
*/
static inline long
sbspace(struct socket *so, struct sockbuf *sb)
{
KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
soassertlocked(so);
return lmin(sb->sb_hiwat - sb->sb_cc, sb->sb_mbmax - sb->sb_mbcnt);
}
/* do we have to send all at once on a socket? */
#define sosendallatonce(so) \
((so)->so_proto->pr_flags & PR_ATOMIC)
/* are we sending on this socket? */
#define soissending(so) \
((so)->so_state & SS_ISSENDING)
/* can we read something from so? */
static inline int
soreadable(struct socket *so)
{
soassertlocked(so);
if (isspliced(so))
return 0;
return (so->so_state & SS_CANTRCVMORE) || so->so_qlen || so->so_error ||
so->so_rcv.sb_cc >= so->so_rcv.sb_lowat;
}
/* can we write something to so? */
#define sowriteable(so) \
((sbspace((so), &(so)->so_snd) >= (so)->so_snd.sb_lowat && \
(((so)->so_state & SS_ISCONNECTED) || \
((so)->so_proto->pr_flags & PR_CONNREQUIRED)==0)) || \
((so)->so_state & SS_CANTSENDMORE) || (so)->so_error)
/* adjust counters in sb reflecting allocation of m */
#define sballoc(so, sb, m) do { \
(sb)->sb_cc += (m)->m_len; \
if ((m)->m_type != MT_CONTROL && (m)->m_type != MT_SONAME) \
(sb)->sb_datacc += (m)->m_len; \
(sb)->sb_mbcnt += MSIZE; \
if ((m)->m_flags & M_EXT) \
(sb)->sb_mbcnt += (m)->m_ext.ext_size; \
} while (/* CONSTCOND */ 0)
/* adjust counters in sb reflecting freeing of m */
#define sbfree(so, sb, m) do { \
(sb)->sb_cc -= (m)->m_len; \
if ((m)->m_type != MT_CONTROL && (m)->m_type != MT_SONAME) \
(sb)->sb_datacc -= (m)->m_len; \
(sb)->sb_mbcnt -= MSIZE; \
if ((m)->m_flags & M_EXT) \
(sb)->sb_mbcnt -= (m)->m_ext.ext_size; \
} while (/* CONSTCOND */ 0)
/*
* Set lock on sockbuf sb; sleep if lock is already held.
* Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
* Returns error without lock if sleep is interrupted.
*/
int sblock(struct socket *, struct sockbuf *, int);
/* release lock on sockbuf sb */
void sbunlock(struct socket *, struct sockbuf *);
#define SB_EMPTY_FIXUP(sb) do { \
if ((sb)->sb_mb == NULL) { \
(sb)->sb_mbtail = NULL; \
(sb)->sb_lastrecord = NULL; \
} \
} while (/*CONSTCOND*/0)
extern u_long sb_max;
extern struct pool socket_pool;
struct mbuf;
struct sockaddr;
struct proc;
struct msghdr;
struct stat;
struct knote;
/*
* File operations on sockets.
*/
int soo_read(struct file *, struct uio *, int);
int soo_write(struct file *, struct uio *, int);
int soo_ioctl(struct file *, u_long, caddr_t, struct proc *);
int soo_kqfilter(struct file *, struct knote *);
int soo_close(struct file *, struct proc *);
int soo_stat(struct file *, struct stat *, struct proc *);
void sbappend(struct socket *, struct sockbuf *, struct mbuf *);
void sbappendstream(struct socket *, struct sockbuf *, struct mbuf *);
int sbappendaddr(struct socket *, struct sockbuf *,
const struct sockaddr *, struct mbuf *, struct mbuf *);
int sbappendcontrol(struct socket *, struct sockbuf *, struct mbuf *,
struct mbuf *);
void sbappendrecord(struct socket *, struct sockbuf *, struct mbuf *);
void sbcompress(struct socket *, struct sockbuf *, struct mbuf *,
struct mbuf *);
struct mbuf *
sbcreatecontrol(const void *, size_t, int, int);
void sbdrop(struct socket *, struct sockbuf *, int);
void sbdroprecord(struct socket *, struct sockbuf *);
void sbflush(struct socket *, struct sockbuf *);
void sbrelease(struct socket *, struct sockbuf *);
int sbcheckreserve(u_long, u_long);
int sbchecklowmem(void);
int sbreserve(struct socket *, struct sockbuf *, u_long);
int sbwait(struct socket *, struct sockbuf *);
void soinit(void);
void soabort(struct socket *);
int soaccept(struct socket *, struct mbuf *);
int sobind(struct socket *, struct mbuf *, struct proc *);
void socantrcvmore(struct socket *);
void socantsendmore(struct socket *);
int soclose(struct socket *, int);
int soconnect(struct socket *, struct mbuf *);
int soconnect2(struct socket *, struct socket *);
int socreate(int, struct socket **, int, int);
int sodisconnect(struct socket *);
struct socket *soalloc(int);
void sofree(struct socket *, int);
int sogetopt(struct socket *, int, int, struct mbuf *);
void sohasoutofband(struct socket *);
void soisconnected(struct socket *);
void soisconnecting(struct socket *);
void soisdisconnected(struct socket *);
void soisdisconnecting(struct socket *);
int solisten(struct socket *, int);
struct socket *sonewconn(struct socket *, int);
void soqinsque(struct socket *, struct socket *, int);
int soqremque(struct socket *, int);
int soreceive(struct socket *, struct mbuf **, struct uio *,
struct mbuf **, struct mbuf **, int *, socklen_t);
int soreserve(struct socket *, u_long, u_long);
int sosend(struct socket *, struct mbuf *, struct uio *,
struct mbuf *, struct mbuf *, int);
int sosetopt(struct socket *, int, int, struct mbuf *);
int soshutdown(struct socket *, int);
void sowakeup(struct socket *, struct sockbuf *);
void sorwakeup(struct socket *);
void sowwakeup(struct socket *);
int sockargs(struct mbuf **, const void *, size_t, int);
int sosleep_nsec(struct socket *, void *, int, const char *, uint64_t);
void solock(struct socket *);
void solock_shared(struct socket *);
int solock_persocket(struct socket *);
void solock_pair(struct socket *, struct socket *);
void sounlock(struct socket *);
void sounlock_shared(struct socket *);
int sendit(struct proc *, int, struct msghdr *, int, register_t *);
int recvit(struct proc *, int, struct msghdr *, caddr_t, register_t *);
int doaccept(struct proc *, int, struct sockaddr *, socklen_t *, int,
register_t *);
#ifdef SOCKBUF_DEBUG
void sblastrecordchk(struct sockbuf *, const char *);
#define SBLASTRECORDCHK(sb, where) sblastrecordchk((sb), (where))
void sblastmbufchk(struct sockbuf *, const char *);
#define SBLASTMBUFCHK(sb, where) sblastmbufchk((sb), (where))
void sbcheck(struct socket *, struct sockbuf *);
#define SBCHECK(so, sb) sbcheck((so), (sb))
#else
#define SBLASTRECORDCHK(sb, where) /* nothing */
#define SBLASTMBUFCHK(sb, where) /* nothing */
#define SBCHECK(so, sb) /* nothing */
#endif /* SOCKBUF_DEBUG */
#endif /* _KERNEL */
#endif /* _SYS_SOCKETVAR_H_ */
42
42
42
7
1
7
1
3
1
33
3
30
4
34
11
27
14
7
13
6
7
13
157
156
1
1
1
41
24
25
21
10
12
23
27
12
5
8
6
11
5
2
7
3
6
6
6
12
12
12
8
3
3
9
9
35
12
12
12
12
217
198
30
216
32
217
166
1
1
44
166
208
208
40
179
177
2
179
165
12
176
177
2
4
50
134
178
176
1
3
490
440
65
6
493
451
64
11
3
3
3
3
3
3
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
/* $OpenBSD: uvm_vnode.c,v 1.127 2022/08/31 09:07:35 gnezdo Exp $ */
/* $NetBSD: uvm_vnode.c,v 1.36 2000/11/24 20:34:01 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993
* The Regents of the University of California.
* Copyright (c) 1990 University of Utah.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vnode_pager.c 8.8 (Berkeley) 2/13/94
* from: Id: uvm_vnode.c,v 1.1.2.26 1998/02/02 20:38:07 chuck Exp
*/
/*
* uvm_vnode.c: the vnode pager.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/disklabel.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/rwlock.h>
#include <sys/dkio.h>
#include <sys/specdev.h>
#include <uvm/uvm.h>
#include <uvm/uvm_vnode.h>
/*
* private global data structure
*
* we keep a list of writeable active vnode-backed VM objects for sync op.
* we keep a simpleq of vnodes that are currently being sync'd.
*/
LIST_HEAD(uvn_list_struct, uvm_vnode);
struct uvn_list_struct uvn_wlist; /* writeable uvns */
SIMPLEQ_HEAD(uvn_sq_struct, uvm_vnode);
struct uvn_sq_struct uvn_sync_q; /* sync'ing uvns */
struct rwlock uvn_sync_lock; /* locks sync operation */
extern int rebooting;
/*
* functions
*/
void uvn_cluster(struct uvm_object *, voff_t, voff_t *, voff_t *);
void uvn_detach(struct uvm_object *);
boolean_t uvn_flush(struct uvm_object *, voff_t, voff_t, int);
int uvn_get(struct uvm_object *, voff_t, vm_page_t *, int *, int,
vm_prot_t, int, int);
void uvn_init(void);
int uvn_io(struct uvm_vnode *, vm_page_t *, int, int, int);
int uvn_put(struct uvm_object *, vm_page_t *, int, boolean_t);
void uvn_reference(struct uvm_object *);
/*
* master pager structure
*/
const struct uvm_pagerops uvm_vnodeops = {
.pgo_init = uvn_init,
.pgo_reference = uvn_reference,
.pgo_detach = uvn_detach,
.pgo_flush = uvn_flush,
.pgo_get = uvn_get,
.pgo_put = uvn_put,
.pgo_cluster = uvn_cluster,
/* use generic version of this: see uvm_pager.c */
.pgo_mk_pcluster = uvm_mk_pcluster,
};
/*
* the ops!
*/
/*
* uvn_init
*
* init pager private data structures.
*/
void
uvn_init(void)
{
LIST_INIT(&uvn_wlist);
/* note: uvn_sync_q init'd in uvm_vnp_sync() */
rw_init_flags(&uvn_sync_lock, "uvnsync", RWL_IS_VNODE);
}
/*
* uvn_attach
*
* attach a vnode structure to a VM object. if the vnode is already
* attached, then just bump the reference count by one and return the
* VM object. if not already attached, attach and return the new VM obj.
* the "accessprot" tells the max access the attaching thread wants to
* our pages.
*
* => in fact, nothing should be locked so that we can sleep here.
* => note that uvm_object is first thing in vnode structure, so their
* pointers are equiv.
*/
struct uvm_object *
uvn_attach(struct vnode *vp, vm_prot_t accessprot)
{
struct uvm_vnode *uvn = vp->v_uvm;
struct vattr vattr;
int oldflags, result;
struct partinfo pi;
u_quad_t used_vnode_size = 0;
/* first get a lock on the uvn. */
while (uvn->u_flags & UVM_VNODE_BLOCKED) {
uvn->u_flags |= UVM_VNODE_WANTED;
tsleep_nsec(uvn, PVM, "uvn_attach", INFSLP);
}
/* if we're mapping a BLK device, make sure it is a disk. */
if (vp->v_type == VBLK && bdevsw[major(vp->v_rdev)].d_type != D_DISK) {
return NULL;
}
/*
* now uvn must not be in a blocked state.
* first check to see if it is already active, in which case
* we can bump the reference count, check to see if we need to
* add it to the writeable list, and then return.
*/
rw_enter(uvn->u_obj.vmobjlock, RW_WRITE);
if (uvn->u_flags & UVM_VNODE_VALID) { /* already active? */
/* regain vref if we were persisting */
if (uvn->u_obj.uo_refs == 0) {
vref(vp);
}
uvn->u_obj.uo_refs++; /* bump uvn ref! */
rw_exit(uvn->u_obj.vmobjlock);
/* check for new writeable uvn */
if ((accessprot & PROT_WRITE) != 0 &&
(uvn->u_flags & UVM_VNODE_WRITEABLE) == 0) {
LIST_INSERT_HEAD(&uvn_wlist, uvn, u_wlist);
/* we are now on wlist! */
uvn->u_flags |= UVM_VNODE_WRITEABLE;
}
return (&uvn->u_obj);
}
rw_exit(uvn->u_obj.vmobjlock);
/*
* need to call VOP_GETATTR() to get the attributes, but that could
* block (due to I/O), so we want to unlock the object before calling.
* however, we want to keep anyone else from playing with the object
* while it is unlocked. to do this we set UVM_VNODE_ALOCK which
* prevents anyone from attaching to the vnode until we are done with
* it.
*/
uvn->u_flags = UVM_VNODE_ALOCK;
if (vp->v_type == VBLK) {
/*
* We could implement this as a specfs getattr call, but:
*
* (1) VOP_GETATTR() would get the file system
* vnode operation, not the specfs operation.
*
* (2) All we want is the size, anyhow.
*/
result = (*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev,
DIOCGPART, (caddr_t)&pi, FREAD, curproc);
if (result == 0) {
/* XXX should remember blocksize */
used_vnode_size = (u_quad_t)pi.disklab->d_secsize *
(u_quad_t)DL_GETPSIZE(pi.part);
}
} else {
result = VOP_GETATTR(vp, &vattr, curproc->p_ucred, curproc);
if (result == 0)
used_vnode_size = vattr.va_size;
}
if (result != 0) {
if (uvn->u_flags & UVM_VNODE_WANTED)
wakeup(uvn);
uvn->u_flags = 0;
return NULL;
}
/*
* make sure that the newsize fits within a vaddr_t
* XXX: need to revise addressing data types
*/
#ifdef DEBUG
if (vp->v_type == VBLK)
printf("used_vnode_size = %llu\n", (long long)used_vnode_size);
#endif
/* now set up the uvn. */
KASSERT(uvn->u_obj.uo_refs == 0);
uvn->u_obj.uo_refs++;
oldflags = uvn->u_flags;
uvn->u_flags = UVM_VNODE_VALID|UVM_VNODE_CANPERSIST;
uvn->u_nio = 0;
uvn->u_size = used_vnode_size;
/* if write access, we need to add it to the wlist */
if (accessprot & PROT_WRITE) {
LIST_INSERT_HEAD(&uvn_wlist, uvn, u_wlist);
uvn->u_flags |= UVM_VNODE_WRITEABLE; /* we are on wlist! */
}
/*
* add a reference to the vnode. this reference will stay as long
* as there is a valid mapping of the vnode. dropped when the
* reference count goes to zero [and we either free or persist].
*/
vref(vp);
if (oldflags & UVM_VNODE_WANTED)
wakeup(uvn);
return &uvn->u_obj;
}
/*
* uvn_reference
*
* duplicate a reference to a VM object. Note that the reference
* count must already be at least one (the passed in reference) so
* there is no chance of the uvn being killed out here.
*
* => caller must be using the same accessprot as was used at attach time
*/
void
uvn_reference(struct uvm_object *uobj)
{
#ifdef DEBUG
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj;
#endif
#ifdef DEBUG
if ((uvn->u_flags & UVM_VNODE_VALID) == 0) {
printf("uvn_reference: ref=%d, flags=0x%x\n",
uobj->uo_refs, uvn->u_flags);
panic("uvn_reference: invalid state");
}
#endif
rw_enter(uobj->vmobjlock, RW_WRITE);
uobj->uo_refs++;
rw_exit(uobj->vmobjlock);
}
/*
* uvn_detach
*
* remove a reference to a VM object.
*
* => caller must call with map locked.
* => this starts the detach process, but doesn't have to finish it
* (async i/o could still be pending).
*/
void
uvn_detach(struct uvm_object *uobj)
{
struct uvm_vnode *uvn;
struct vnode *vp;
int oldflags;
rw_enter(uobj->vmobjlock, RW_WRITE);
uobj->uo_refs--; /* drop ref! */
if (uobj->uo_refs) { /* still more refs */
rw_exit(uobj->vmobjlock);
return;
}
/* get other pointers ... */
uvn = (struct uvm_vnode *) uobj;
vp = uvn->u_vnode;
/*
* clear VTEXT flag now that there are no mappings left (VTEXT is used
* to keep an active text file from being overwritten).
*/
vp->v_flag &= ~VTEXT;
/*
* we just dropped the last reference to the uvn. see if we can
* let it "stick around".
*/
if (uvn->u_flags & UVM_VNODE_CANPERSIST) {
/* won't block */
uvn_flush(uobj, 0, 0, PGO_DEACTIVATE|PGO_ALLPAGES);
goto out;
}
/* its a goner! */
uvn->u_flags |= UVM_VNODE_DYING;
/*
* even though we may unlock in flush, no one can gain a reference
* to us until we clear the "dying" flag [because it blocks
* attaches]. we will not do that until after we've disposed of all
* the pages with uvn_flush(). note that before the flush the only
* pages that could be marked PG_BUSY are ones that are in async
* pageout by the daemon. (there can't be any pending "get"'s
* because there are no references to the object).
*/
(void) uvn_flush(uobj, 0, 0, PGO_CLEANIT|PGO_FREE|PGO_ALLPAGES);
/*
* given the structure of this pager, the above flush request will
* create the following state: all the pages that were in the object
* have either been free'd or they are marked PG_BUSY and in the
* middle of an async io. If we still have pages we set the "relkill"
* state, so that in the case the vnode gets terminated we know
* to leave it alone. Otherwise we'll kill the vnode when it's empty.
*/
uvn->u_flags |= UVM_VNODE_RELKILL;
/* wait on any outstanding io */
while (uobj->uo_npages && uvn->u_flags & UVM_VNODE_RELKILL) {
uvn->u_flags |= UVM_VNODE_IOSYNC;
rwsleep_nsec(&uvn->u_nio, uobj->vmobjlock, PVM, "uvn_term",
INFSLP);
}
if ((uvn->u_flags & UVM_VNODE_RELKILL) == 0) {
rw_exit(uobj->vmobjlock);
return;
}
/*
* kill object now. note that we can't be on the sync q because
* all references are gone.
*/
if (uvn->u_flags & UVM_VNODE_WRITEABLE) {
LIST_REMOVE(uvn, u_wlist);
}
KASSERT(RBT_EMPTY(uvm_objtree, &uobj->memt));
oldflags = uvn->u_flags;
uvn->u_flags = 0;
/* wake up any sleepers */
if (oldflags & UVM_VNODE_WANTED)
wakeup(uvn);
out:
rw_exit(uobj->vmobjlock);
/* drop our reference to the vnode. */
vrele(vp);
return;
}
/*
* uvm_vnp_terminate: external hook to clear out a vnode's VM
*
* called in two cases:
* [1] when a persisting vnode vm object (i.e. one with a zero reference
* count) needs to be freed so that a vnode can be reused. this
* happens under "getnewvnode" in vfs_subr.c. if the vnode from
* the free list is still attached (i.e. not VBAD) then vgone is
* called. as part of the vgone trace this should get called to
* free the vm object. this is the common case.
* [2] when a filesystem is being unmounted by force (MNT_FORCE,
* "umount -f") the vgone() function is called on active vnodes
* on the mounted file systems to kill their data (the vnodes become
* "dead" ones [see src/sys/miscfs/deadfs/...]). that results in a
* call here (even if the uvn is still in use -- i.e. has a non-zero
* reference count). this case happens at "umount -f" and during a
* "reboot/halt" operation.
*
* => the caller must XLOCK and VOP_LOCK the vnode before calling us
* [protects us from getting a vnode that is already in the DYING
* state...]
* => in case [2] the uvn is still alive after this call, but all I/O
* ops will fail (due to the backing vnode now being "dead"). this
* will prob. kill any process using the uvn due to pgo_get failing.
*/
void
uvm_vnp_terminate(struct vnode *vp)
{
struct uvm_vnode *uvn = vp->v_uvm;
struct uvm_object *uobj = &uvn->u_obj;
int oldflags;
/* check if it is valid */
rw_enter(uobj->vmobjlock, RW_WRITE);
if ((uvn->u_flags & UVM_VNODE_VALID) == 0) {
rw_exit(uobj->vmobjlock);
return;
}
/*
* must be a valid uvn that is not already dying (because XLOCK
* protects us from that). the uvn can't in the ALOCK state
* because it is valid, and uvn's that are in the ALOCK state haven't
* been marked valid yet.
*/
#ifdef DEBUG
/*
* debug check: are we yanking the vnode out from under our uvn?
*/
if (uvn->u_obj.uo_refs) {
printf("uvm_vnp_terminate(%p): terminating active vnode "
"(refs=%d)\n", uvn, uvn->u_obj.uo_refs);
}
#endif
/*
* it is possible that the uvn was detached and is in the relkill
* state [i.e. waiting for async i/o to finish].
* we take over the vnode now and cancel the relkill.
* we want to know when the i/o is done so we can recycle right
* away. note that a uvn can only be in the RELKILL state if it
* has a zero reference count.
*/
if (uvn->u_flags & UVM_VNODE_RELKILL)
uvn->u_flags &= ~UVM_VNODE_RELKILL; /* cancel RELKILL */
/*
* block the uvn by setting the dying flag, and then flush the
* pages.
*
* also, note that we tell I/O that we are already VOP_LOCK'd so
* that uvn_io doesn't attempt to VOP_LOCK again.
*
* XXXCDC: setting VNISLOCKED on an active uvn which is being terminated
* due to a forceful unmount might not be a good idea. maybe we
* need a way to pass in this info to uvn_flush through a
* pager-defined PGO_ constant [currently there are none].
*/
uvn->u_flags |= UVM_VNODE_DYING|UVM_VNODE_VNISLOCKED;
(void) uvn_flush(&uvn->u_obj, 0, 0, PGO_CLEANIT|PGO_FREE|PGO_ALLPAGES);
/*
* as we just did a flush we expect all the pages to be gone or in
* the process of going. sleep to wait for the rest to go [via iosync].
*/
while (uvn->u_obj.uo_npages) {
#ifdef DEBUG
struct vm_page *pp;
RBT_FOREACH(pp, uvm_objtree, &uvn->u_obj.memt) {
if ((pp->pg_flags & PG_BUSY) == 0)
panic("uvm_vnp_terminate: detected unbusy pg");
}
if (uvn->u_nio == 0)
panic("uvm_vnp_terminate: no I/O to wait for?");
printf("uvm_vnp_terminate: waiting for I/O to fin.\n");
/*
* XXXCDC: this is unlikely to happen without async i/o so we
* put a printf in just to keep an eye on it.
*/
#endif
uvn->u_flags |= UVM_VNODE_IOSYNC;
rwsleep_nsec(&uvn->u_nio, uobj->vmobjlock, PVM, "uvn_term",
INFSLP);
}
/*
* done. now we free the uvn if its reference count is zero
* (true if we are zapping a persisting uvn). however, if we are
* terminating a uvn with active mappings we let it live ... future
* calls down to the vnode layer will fail.
*/
oldflags = uvn->u_flags;
if (uvn->u_obj.uo_refs) {
/*
* uvn must live on it is dead-vnode state until all references
* are gone. restore flags. clear CANPERSIST state.
*/
uvn->u_flags &= ~(UVM_VNODE_DYING|UVM_VNODE_VNISLOCKED|
UVM_VNODE_WANTED|UVM_VNODE_CANPERSIST);
} else {
/*
* free the uvn now. note that the vref reference is already
* gone [it is dropped when we enter the persist state].
*/
if (uvn->u_flags & UVM_VNODE_IOSYNCWANTED)
panic("uvm_vnp_terminate: io sync wanted bit set");
if (uvn->u_flags & UVM_VNODE_WRITEABLE) {
LIST_REMOVE(uvn, u_wlist);
}
uvn->u_flags = 0; /* uvn is history, clear all bits */
}
if (oldflags & UVM_VNODE_WANTED)
wakeup(uvn);
rw_exit(uobj->vmobjlock);
}
/*
* NOTE: currently we have to use VOP_READ/VOP_WRITE because they go
* through the buffer cache and allow I/O in any size. These VOPs use
* synchronous i/o. [vs. VOP_STRATEGY which can be async, but doesn't
* go through the buffer cache or allow I/O sizes larger than a
* block]. we will eventually want to change this.
*
* issues to consider:
* uvm provides the uvm_aiodesc structure for async i/o management.
* there are two tailq's in the uvm. structure... one for pending async
* i/o and one for "done" async i/o. to do an async i/o one puts
* an aiodesc on the "pending" list (protected by splbio()), starts the
* i/o and returns VM_PAGER_PEND. when the i/o is done, we expect
* some sort of "i/o done" function to be called (at splbio(), interrupt
* time). this function should remove the aiodesc from the pending list
* and place it on the "done" list and wakeup the daemon. the daemon
* will run at normal spl() and will remove all items from the "done"
* list and call the "aiodone" hook for each done request (see uvm_pager.c).
* [in the old vm code, this was done by calling the "put" routine with
* null arguments which made the code harder to read and understand because
* you had one function ("put") doing two things.]
*
* so the current pager needs:
* int uvn_aiodone(struct uvm_aiodesc *)
*
* => return 0 (aio finished, free it). otherwise requeue for later collection.
* => called with pageq's locked by the daemon.
*
* general outline:
* - drop "u_nio" (this req is done!)
* - if (object->iosync && u_naio == 0) { wakeup &uvn->u_naio }
* - get "page" structures (atop?).
* - handle "wanted" pages
* dont forget to look at "object" wanted flag in all cases.
*/
/*
* uvn_flush: flush pages out of a uvm object.
*
* => if PGO_CLEANIT is set, we may block (due to I/O). thus, a caller
* might want to unlock higher level resources (e.g. vm_map)
* before calling flush.
* => if PGO_CLEANIT is not set, then we will not block
* => if PGO_ALLPAGE is set, then all pages in the object are valid targets
* for flushing.
* => NOTE: we are allowed to lock the page queues, so the caller
* must not be holding the lock on them [e.g. pagedaemon had
* better not call us with the queues locked]
* => we return TRUE unless we encountered some sort of I/O error
*
* comment on "cleaning" object and PG_BUSY pages:
* this routine is holding the lock on the object. the only time
* that it can run into a PG_BUSY page that it does not own is if
* some other process has started I/O on the page (e.g. either
* a pagein, or a pageout). if the PG_BUSY page is being paged
* in, then it can not be dirty (!PG_CLEAN) because no one has
* had a chance to modify it yet. if the PG_BUSY page is being
* paged out then it means that someone else has already started
* cleaning the page for us (how nice!). in this case, if we
* have syncio specified, then after we make our pass through the
* object we need to wait for the other PG_BUSY pages to clear
* off (i.e. we need to do an iosync). also note that once a
* page is PG_BUSY it must stay in its object until it is un-busyed.
*/
boolean_t
uvn_flush(struct uvm_object *uobj, voff_t start, voff_t stop, int flags)
{
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj;
struct vm_page *pp, *ptmp;
struct vm_page *pps[MAXBSIZE >> PAGE_SHIFT], **ppsp;
struct pglist dead;
int npages, result, lcv;
boolean_t retval, need_iosync, needs_clean;
voff_t curoff;
KASSERT(rw_write_held(uobj->vmobjlock));
TAILQ_INIT(&dead);
/* get init vals and determine how we are going to traverse object */
need_iosync = FALSE;
retval = TRUE; /* return value */
if (flags & PGO_ALLPAGES) {
start = 0;
stop = round_page(uvn->u_size);
} else {
start = trunc_page(start);
stop = MIN(round_page(stop), round_page(uvn->u_size));
}
/*
* PG_CLEANCHK: this bit is used by the pgo_mk_pcluster function as
* a _hint_ as to how up to date the PG_CLEAN bit is. if the hint
* is wrong it will only prevent us from clustering... it won't break
* anything. we clear all PG_CLEANCHK bits here, and pgo_mk_pcluster
* will set them as it syncs PG_CLEAN. This is only an issue if we
* are looking at non-inactive pages (because inactive page's PG_CLEAN
* bit is always up to date since there are no mappings).
* [borrowed PG_CLEANCHK idea from FreeBSD VM]
*/
if ((flags & PGO_CLEANIT) != 0) {
KASSERT(uobj->pgops->pgo_mk_pcluster != 0);
for (curoff = start ; curoff < stop; curoff += PAGE_SIZE) {
if ((pp = uvm_pagelookup(uobj, curoff)) != NULL)
atomic_clearbits_int(&pp->pg_flags,
PG_CLEANCHK);
}
}
ppsp = NULL; /* XXX: shut up gcc */
uvm_lock_pageq();
/* locked: both page queues */
for (curoff = start; curoff < stop; curoff += PAGE_SIZE) {
if ((pp = uvm_pagelookup(uobj, curoff)) == NULL)
continue;
/*
* handle case where we do not need to clean page (either
* because we are not clean or because page is not dirty or
* is busy):
*
* NOTE: we are allowed to deactivate a non-wired active
* PG_BUSY page, but once a PG_BUSY page is on the inactive
* queue it must stay put until it is !PG_BUSY (so as not to
* confuse pagedaemon).
*/
if ((flags & PGO_CLEANIT) == 0 || (pp->pg_flags & PG_BUSY) != 0) {
needs_clean = FALSE;
if ((pp->pg_flags & PG_BUSY) != 0 &&
(flags & (PGO_CLEANIT|PGO_SYNCIO)) ==
(PGO_CLEANIT|PGO_SYNCIO))
need_iosync = TRUE;
} else {
/*
* freeing: nuke all mappings so we can sync
* PG_CLEAN bit with no race
*/
if ((pp->pg_flags & PG_CLEAN) != 0 &&
(flags & PGO_FREE) != 0 &&
(pp->pg_flags & PQ_ACTIVE) != 0)
pmap_page_protect(pp, PROT_NONE);
if ((pp->pg_flags & PG_CLEAN) != 0 &&
pmap_is_modified(pp))
atomic_clearbits_int(&pp->pg_flags, PG_CLEAN);
atomic_setbits_int(&pp->pg_flags, PG_CLEANCHK);
needs_clean = ((pp->pg_flags & PG_CLEAN) == 0);
}
/* if we don't need a clean, deactivate/free pages then cont. */
if (!needs_clean) {
if (flags & PGO_DEACTIVATE) {
if (pp->wire_count == 0) {
pmap_page_protect(pp, PROT_NONE);
uvm_pagedeactivate(pp);
}
} else if (flags & PGO_FREE) {
if (pp->pg_flags & PG_BUSY) {
uvm_unlock_pageq();
uvm_pagewait(pp, uobj->vmobjlock,
"uvn_flsh");
rw_enter(uobj->vmobjlock, RW_WRITE);
uvm_lock_pageq();
curoff -= PAGE_SIZE;
continue;
} else {
pmap_page_protect(pp, PROT_NONE);
/* removed page from object */
uvm_pageclean(pp);
TAILQ_INSERT_HEAD(&dead, pp, pageq);
}
}
continue;
}
/*
* pp points to a page in the object that we are
* working on. if it is !PG_CLEAN,!PG_BUSY and we asked
* for cleaning (PGO_CLEANIT). we clean it now.
*
* let uvm_pager_put attempted a clustered page out.
* note: locked: page queues.
*/
atomic_setbits_int(&pp->pg_flags, PG_BUSY);
UVM_PAGE_OWN(pp, "uvn_flush");
pmap_page_protect(pp, PROT_READ);
/* if we're async, free the page in aiodoned */
if ((flags & (PGO_FREE|PGO_SYNCIO)) == PGO_FREE)
atomic_setbits_int(&pp->pg_flags, PG_RELEASED);
ReTry:
ppsp = pps;
npages = sizeof(pps) / sizeof(struct vm_page *);
result = uvm_pager_put(uobj, pp, &ppsp, &npages,
flags | PGO_DOACTCLUST, start, stop);
/*
* if we did an async I/O it is remotely possible for the
* async i/o to complete and the page "pp" be freed or what
* not before we get a chance to relock the object. Therefore,
* we only touch it when it won't be freed, RELEASED took care
* of the rest.
*/
uvm_lock_pageq();
/*
* VM_PAGER_AGAIN: given the structure of this pager, this
* can only happen when we are doing async I/O and can't
* map the pages into kernel memory (pager_map) due to lack
* of vm space. if this happens we drop back to sync I/O.
*/
if (result == VM_PAGER_AGAIN) {
/*
* it is unlikely, but page could have been released
* we ignore this now and retry the I/O.
* we will detect and
* handle the released page after the syncio I/O
* completes.
*/
#ifdef DIAGNOSTIC
if (flags & PGO_SYNCIO)
panic("%s: PGO_SYNCIO return 'try again' error (impossible)", __func__);
#endif
flags |= PGO_SYNCIO;
if (flags & PGO_FREE)
atomic_clearbits_int(&pp->pg_flags,
PG_RELEASED);
goto ReTry;
}
/*
* the cleaning operation is now done. finish up. note that
* on error (!OK, !PEND) uvm_pager_put drops the cluster for us.
* if success (OK, PEND) then uvm_pager_put returns the cluster
* to us in ppsp/npages.
*/
/*
* for pending async i/o if we are not deactivating
* we can move on to the next page. aiodoned deals with
* the freeing case for us.
*/
if (result == VM_PAGER_PEND && (flags & PGO_DEACTIVATE) == 0)
continue;
/*
* need to look at each page of the I/O operation, and do what
* we gotta do.
*/
for (lcv = 0 ; lcv < npages; lcv++) {
ptmp = ppsp[lcv];
/*
* verify the page didn't get moved
*/
if (result == VM_PAGER_PEND && ptmp->uobject != uobj)
continue;
/*
* unbusy the page if I/O is done. note that for
* pending I/O it is possible that the I/O op
* finished
* (in which case the page is no longer busy).
*/
if (result != VM_PAGER_PEND) {
if (ptmp->pg_flags & PG_WANTED)
wakeup(ptmp);
atomic_clearbits_int(&ptmp->pg_flags,
PG_WANTED|PG_BUSY);
UVM_PAGE_OWN(ptmp, NULL);
atomic_setbits_int(&ptmp->pg_flags,
PG_CLEAN|PG_CLEANCHK);
if ((flags & PGO_FREE) == 0)
pmap_clear_modify(ptmp);
}
/* dispose of page */
if (flags & PGO_DEACTIVATE) {
if (ptmp->wire_count == 0) {
pmap_page_protect(ptmp, PROT_NONE);
uvm_pagedeactivate(ptmp);
}
} else if (flags & PGO_FREE &&
result != VM_PAGER_PEND) {
if (result != VM_PAGER_OK) {
static struct timeval lasttime;
static const struct timeval interval =
{ 5, 0 };
if (ratecheck(&lasttime, &interval)) {
printf("%s: obj=%p, "
"offset=0x%llx. error "
"during pageout.\n",
__func__, pp->uobject,
(long long)pp->offset);
printf("%s: WARNING: "
"changes to page may be "
"lost!\n", __func__);
}
retval = FALSE;
}
pmap_page_protect(ptmp, PROT_NONE);
uvm_pageclean(ptmp);
TAILQ_INSERT_TAIL(&dead, ptmp, pageq);
}
} /* end of "lcv" for loop */
} /* end of "pp" for loop */
/* done with pagequeues: unlock */
uvm_unlock_pageq();
/* now wait for all I/O if required. */
if (need_iosync) {
while (uvn->u_nio != 0) {
uvn->u_flags |= UVM_VNODE_IOSYNC;
rwsleep_nsec(&uvn->u_nio, uobj->vmobjlock, PVM,
"uvn_flush", INFSLP);
}
if (uvn->u_flags & UVM_VNODE_IOSYNCWANTED)
wakeup(&uvn->u_flags);
uvn->u_flags &= ~(UVM_VNODE_IOSYNC|UVM_VNODE_IOSYNCWANTED);
}
uvm_pglistfree(&dead);
return retval;
}
/*
* uvn_cluster
*
* we are about to do I/O in an object at offset. this function is called
* to establish a range of offsets around "offset" in which we can cluster
* I/O.
*/
void
uvn_cluster(struct uvm_object *uobj, voff_t offset, voff_t *loffset,
voff_t *hoffset)
{
struct uvm_vnode *uvn = (struct uvm_vnode *) uobj;
*loffset = offset;
if (*loffset >= uvn->u_size)
panic("uvn_cluster: offset out of range");
/*
* XXX: old pager claims we could use VOP_BMAP to get maxcontig value.
*/
*hoffset = *loffset + MAXBSIZE;
if (*hoffset > round_page(uvn->u_size)) /* past end? */
*hoffset = round_page(uvn->u_size);
return;
}
/*
* uvn_put: flush page data to backing store.
*
* => prefer map unlocked (not required)
* => flags: PGO_SYNCIO -- use sync. I/O
* => note: caller must set PG_CLEAN and pmap_clear_modify (if needed)
* => XXX: currently we use VOP_READ/VOP_WRITE which are only sync.
* [thus we never do async i/o! see iodone comment]
*/
int
uvn_put(struct uvm_object *uobj, struct vm_page **pps, int npages, int flags)
{
int retval;
KASSERT(rw_write_held(uobj->vmobjlock));
retval = uvn_io((struct uvm_vnode*)uobj, pps, npages, flags, UIO_WRITE);
return retval;
}
/*
* uvn_get: get pages (synchronously) from backing store
*
* => prefer map unlocked (not required)
* => flags: PGO_ALLPAGES: get all of the pages
* PGO_LOCKED: fault data structures are locked
* => NOTE: offset is the offset of pps[0], _NOT_ pps[centeridx]
* => NOTE: caller must check for released pages!!
*/
int
uvn_get(struct uvm_object *uobj, voff_t offset, struct vm_page **pps,
int *npagesp, int centeridx, vm_prot_t access_type, int advice, int flags)
{
voff_t current_offset;
struct vm_page *ptmp;
int lcv, result, gotpages;
boolean_t done;
KASSERT(((flags & PGO_LOCKED) != 0 && rw_lock_held(uobj->vmobjlock)) ||
(flags & PGO_LOCKED) == 0);
/* step 1: handled the case where fault data structures are locked. */
if (flags & PGO_LOCKED) {
/*
* gotpages is the current number of pages we've gotten (which
* we pass back up to caller via *npagesp.
*/
gotpages = 0;
/*
* step 1a: get pages that are already resident. only do this
* if the data structures are locked (i.e. the first time
* through).
*/
done = TRUE; /* be optimistic */
for (lcv = 0, current_offset = offset ; lcv < *npagesp ;
lcv++, current_offset += PAGE_SIZE) {
/* do we care about this page? if not, skip it */
if (pps[lcv] == PGO_DONTCARE)
continue;
/* lookup page */
ptmp = uvm_pagelookup(uobj, current_offset);
/* to be useful must get a non-busy, non-released pg */
if (ptmp == NULL ||
(ptmp->pg_flags & PG_BUSY) != 0) {
if (lcv == centeridx || (flags & PGO_ALLPAGES)
!= 0)
done = FALSE; /* need to do a wait or I/O! */
continue;
}
/*
* useful page: busy it and plug it in our
* result array
*/
atomic_setbits_int(&ptmp->pg_flags, PG_BUSY);
UVM_PAGE_OWN(ptmp, "uvn_get1");
pps[lcv] = ptmp;
gotpages++;
}
/*
* XXX: given the "advice", should we consider async read-ahead?
* XXX: fault current does deactivate of pages behind us. is
* this good (other callers might now).
*/
/*
* XXX: read-ahead currently handled by buffer cache (bread)
* level.
* XXX: no async i/o available.
* XXX: so we don't do anything now.
*/
/*
* step 1c: now we've either done everything needed or we to
* unlock and do some waiting or I/O.
*/
*npagesp = gotpages; /* let caller know */
if (done)
return VM_PAGER_OK; /* bingo! */
else
return VM_PAGER_UNLOCK;
}
/*
* step 2: get non-resident or busy pages.
* data structures are unlocked.
*
* XXX: because we can't do async I/O at this level we get things
* page at a time (otherwise we'd chunk). the VOP_READ() will do
* async-read-ahead for us at a lower level.
*/
for (lcv = 0, current_offset = offset;
lcv < *npagesp ; lcv++, current_offset += PAGE_SIZE) {
/* skip over pages we've already gotten or don't want */
/* skip over pages we don't _have_ to get */
if (pps[lcv] != NULL || (lcv != centeridx &&
(flags & PGO_ALLPAGES) == 0))
continue;
/*
* we have yet to locate the current page (pps[lcv]). we first
* look for a page that is already at the current offset. if
* we fine a page, we check to see if it is busy or released.
* if that is the case, then we sleep on the page until it is
* no longer busy or released and repeat the lookup. if the
* page we found is neither busy nor released, then we busy it
* (so we own it) and plug it into pps[lcv]. this breaks the
* following while loop and indicates we are ready to move on
* to the next page in the "lcv" loop above.
*
* if we exit the while loop with pps[lcv] still set to NULL,
* then it means that we allocated a new busy/fake/clean page
* ptmp in the object and we need to do I/O to fill in the data.
*/
while (pps[lcv] == NULL) { /* top of "pps" while loop */
/* look for a current page */
ptmp = uvm_pagelookup(uobj, current_offset);
/* nope? allocate one now (if we can) */
if (ptmp == NULL) {
ptmp = uvm_pagealloc(uobj, current_offset,
NULL, 0);
/* out of RAM? */
if (ptmp == NULL) {
uvm_wait("uvn_getpage");
/* goto top of pps while loop */
continue;
}
/*
* got new page ready for I/O. break pps
* while loop. pps[lcv] is still NULL.
*/
break;
}
/* page is there, see if we need to wait on it */
if ((ptmp->pg_flags & PG_BUSY) != 0) {
uvm_pagewait(ptmp, uobj->vmobjlock, "uvn_get");
rw_enter(uobj->vmobjlock, RW_WRITE);
continue; /* goto top of pps while loop */
}
/*
* if we get here then the page has become resident
* and unbusy between steps 1 and 2. we busy it
* now (so we own it) and set pps[lcv] (so that we
* exit the while loop).
*/
atomic_setbits_int(&ptmp->pg_flags, PG_BUSY);
UVM_PAGE_OWN(ptmp, "uvn_get2");
pps[lcv] = ptmp;
}
/*
* if we own the a valid page at the correct offset, pps[lcv]
* will point to it. nothing more to do except go to the
* next page.
*/
if (pps[lcv])
continue; /* next lcv */
/*
* we have a "fake/busy/clean" page that we just allocated. do
* I/O to fill it with valid data.
*/
result = uvn_io((struct uvm_vnode *) uobj, &ptmp, 1,
PGO_SYNCIO|PGO_NOWAIT, UIO_READ);
/*
* I/O done. because we used syncio the result can not be
* PEND or AGAIN.
*/
if (result != VM_PAGER_OK) {
if (ptmp->pg_flags & PG_WANTED)
wakeup(ptmp);
atomic_clearbits_int(&ptmp->pg_flags,
PG_WANTED|PG_BUSY);
UVM_PAGE_OWN(ptmp, NULL);
uvm_lock_pageq();
uvm_pagefree(ptmp);
uvm_unlock_pageq();
rw_exit(uobj->vmobjlock);
return result;
}
/*
* we got the page! clear the fake flag (indicates valid
* data now in page) and plug into our result array. note
* that page is still busy.
*
* it is the callers job to:
* => check if the page is released
* => unbusy the page
* => activate the page
*/
/* data is valid ... */
atomic_clearbits_int(&ptmp->pg_flags, PG_FAKE);
pmap_clear_modify(ptmp); /* ... and clean */
pps[lcv] = ptmp;
}
rw_exit(uobj->vmobjlock);
return (VM_PAGER_OK);
}
/*
* uvn_io: do I/O to a vnode
*
* => prefer map unlocked (not required)
* => flags: PGO_SYNCIO -- use sync. I/O
* => XXX: currently we use VOP_READ/VOP_WRITE which are only sync.
* [thus we never do async i/o! see iodone comment]
*/
int
uvn_io(struct uvm_vnode *uvn, vm_page_t *pps, int npages, int flags, int rw)
{
struct uvm_object *uobj = &uvn->u_obj;
struct vnode *vn;
struct uio uio;
struct iovec iov;
vaddr_t kva;
off_t file_offset;
int waitf, result, mapinflags;
size_t got, wanted;
int netunlocked = 0;
int lkflags = (flags & PGO_NOWAIT) ? LK_NOWAIT : 0;
KASSERT(rw_write_held(uobj->vmobjlock));
/* init values */
waitf = (flags & PGO_SYNCIO) ? M_WAITOK : M_NOWAIT;
vn = uvn->u_vnode;
file_offset = pps[0]->offset;
/* check for sync'ing I/O. */
while (uvn->u_flags & UVM_VNODE_IOSYNC) {
if (waitf == M_NOWAIT) {
return VM_PAGER_AGAIN;
}
uvn->u_flags |= UVM_VNODE_IOSYNCWANTED;
rwsleep_nsec(&uvn->u_flags, uobj->vmobjlock, PVM, "uvn_iosync",
INFSLP);
}
/* check size */
if (file_offset >= uvn->u_size) {
return VM_PAGER_BAD;
}
/* first try and map the pages in (without waiting) */
mapinflags = (rw == UIO_READ) ?
UVMPAGER_MAPIN_READ : UVMPAGER_MAPIN_WRITE;
kva = uvm_pagermapin(pps, npages, mapinflags);
if (kva == 0 && waitf == M_NOWAIT) {
return VM_PAGER_AGAIN;
}
/*
* ok, now bump u_nio up. at this point we are done with uvn
* and can unlock it. if we still don't have a kva, try again
* (this time with sleep ok).
*/
uvn->u_nio++; /* we have an I/O in progress! */
rw_exit(uobj->vmobjlock);
if (kva == 0)
kva = uvm_pagermapin(pps, npages,
mapinflags | UVMPAGER_MAPIN_WAITOK);
/*
* ok, mapped in. our pages are PG_BUSY so they are not going to
* get touched (so we can look at "offset" without having to lock
* the object). set up for I/O.
*/
/* fill out uio/iov */
iov.iov_base = (caddr_t) kva;
wanted = (size_t)npages << PAGE_SHIFT;
if (file_offset + wanted > uvn->u_size)
wanted = uvn->u_size - file_offset; /* XXX: needed? */
iov.iov_len = wanted;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = file_offset;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = rw;
uio.uio_resid = wanted;
uio.uio_procp = curproc;
/*
* This process may already have the NET_LOCK(), if we
* faulted in copyin() or copyout() in the network stack.
*/
if (rw_status(&netlock) == RW_WRITE) {
NET_UNLOCK();
netunlocked = 1;
}
/* do the I/O! (XXX: curproc?) */
/*
* This process may already have this vnode locked, if we faulted in
* copyin() or copyout() on a region backed by this vnode
* while doing I/O to the vnode. If this is the case, don't
* panic.. instead, return the error to the user.
*
* XXX this is a stopgap to prevent a panic.
* Ideally, this kind of operation *should* work.
*/
result = 0;
KERNEL_LOCK();
if ((uvn->u_flags & UVM_VNODE_VNISLOCKED) == 0)
result = vn_lock(vn, LK_EXCLUSIVE | LK_RECURSEFAIL | lkflags);
if (result == 0) {
/* NOTE: vnode now locked! */
if (rw == UIO_READ)
result = VOP_READ(vn, &uio, 0, curproc->p_ucred);
else
result = VOP_WRITE(vn, &uio,
(flags & PGO_PDFREECLUST) ? IO_NOCACHE : 0,
curproc->p_ucred);
if ((uvn->u_flags & UVM_VNODE_VNISLOCKED) == 0)
VOP_UNLOCK(vn);
}
KERNEL_UNLOCK();
if (netunlocked)
NET_LOCK();
/* NOTE: vnode now unlocked (unless vnislocked) */
/*
* result == unix style errno (0 == OK!)
*
* zero out rest of buffer (if needed)
*/
if (result == 0) {
got = wanted - uio.uio_resid;
if (wanted && got == 0) {
result = EIO; /* XXX: error? */
} else if (got < PAGE_SIZE * npages && rw == UIO_READ) {
memset((void *) (kva + got), 0,
((size_t)npages << PAGE_SHIFT) - got);
}
}
/* now remove pager mapping */
uvm_pagermapout(kva, npages);
/* now clean up the object (i.e. drop I/O count) */
rw_enter(uobj->vmobjlock, RW_WRITE);
uvn->u_nio--; /* I/O DONE! */
if ((uvn->u_flags & UVM_VNODE_IOSYNC) != 0 && uvn->u_nio == 0) {
wakeup(&uvn->u_nio);
}
if (result == 0) {
return VM_PAGER_OK;
} else if (result == EBUSY) {
KASSERT(flags & PGO_NOWAIT);
return VM_PAGER_AGAIN;
} else {
if (rebooting) {
KERNEL_LOCK();
while (rebooting)
tsleep_nsec(&rebooting, PVM, "uvndead", INFSLP);
KERNEL_UNLOCK();
}
return VM_PAGER_ERROR;
}
}
/*
* uvm_vnp_uncache: disable "persisting" in a vnode... when last reference
* is gone we will kill the object (flushing dirty pages back to the vnode
* if needed).
*
* => returns TRUE if there was no uvm_object attached or if there was
* one and we killed it [i.e. if there is no active uvn]
* => called with the vnode VOP_LOCK'd [we will unlock it for I/O, if
* needed]
*
* => XXX: given that we now kill uvn's when a vnode is recycled (without
* having to hold a reference on the vnode) and given a working
* uvm_vnp_sync(), how does that effect the need for this function?
* [XXXCDC: seems like it can die?]
*
* => XXX: this function should DIE once we merge the VM and buffer
* cache.
*
* research shows that this is called in the following places:
* ext2fs_truncate, ffs_truncate, detrunc[msdosfs]: called when vnode
* changes sizes
* ext2fs_write, WRITE [ufs_readwrite], msdosfs_write: called when we
* are written to
* ex2fs_chmod, ufs_chmod: called if VTEXT vnode and the sticky bit
* is off
* ffs_realloccg: when we can't extend the current block and have
* to allocate a new one we call this [XXX: why?]
* nfsrv_rename, rename_files: called when the target filename is there
* and we want to remove it
* nfsrv_remove, sys_unlink: called on file we are removing
* nfsrv_access: if VTEXT and we want WRITE access and we don't uncache
* then return "text busy"
* nfs_open: seems to uncache any file opened with nfs
* vn_writechk: if VTEXT vnode and can't uncache return "text busy"
* fusefs_open: uncaches any file that is opened
* fusefs_write: uncaches on every write
*/
int
uvm_vnp_uncache(struct vnode *vp)
{
struct uvm_vnode *uvn = vp->v_uvm;
struct uvm_object *uobj = &uvn->u_obj;
/* lock uvn part of the vnode and check if we need to do anything */
rw_enter(uobj->vmobjlock, RW_WRITE);
if ((uvn->u_flags & UVM_VNODE_VALID) == 0 ||
(uvn->u_flags & UVM_VNODE_BLOCKED) != 0) {
rw_exit(uobj->vmobjlock);
return TRUE;
}
/*
* we have a valid, non-blocked uvn. clear persist flag.
* if uvn is currently active we can return now.
*/
uvn->u_flags &= ~UVM_VNODE_CANPERSIST;
if (uvn->u_obj.uo_refs) {
rw_exit(uobj->vmobjlock);
return FALSE;
}
/*
* uvn is currently persisting! we have to gain a reference to
* it so that we can call uvn_detach to kill the uvn.
*/
vref(vp); /* seems ok, even with VOP_LOCK */
uvn->u_obj.uo_refs++; /* value is now 1 */
rw_exit(uobj->vmobjlock);
#ifdef VFSLCKDEBUG
/*
* carry over sanity check from old vnode pager: the vnode should
* be VOP_LOCK'd, and we confirm it here.
*/
if ((vp->v_flag & VLOCKSWORK) && !VOP_ISLOCKED(vp))
panic("uvm_vnp_uncache: vnode not locked!");
#endif
/*
* now drop our reference to the vnode. if we have the sole
* reference to the vnode then this will cause it to die [as we
* just cleared the persist flag]. we have to unlock the vnode
* while we are doing this as it may trigger I/O.
*
* XXX: it might be possible for uvn to get reclaimed while we are
* unlocked causing us to return TRUE when we should not. we ignore
* this as a false-positive return value doesn't hurt us.
*/
VOP_UNLOCK(vp);
uvn_detach(&uvn->u_obj);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
return TRUE;
}
/*
* uvm_vnp_setsize: grow or shrink a vnode uvn
*
* grow => just update size value
* shrink => toss un-needed pages
*
* => we assume that the caller has a reference of some sort to the
* vnode in question so that it will not be yanked out from under
* us.
*
* called from:
* => truncate fns (ext2fs_truncate, ffs_truncate, detrunc[msdos],
* fusefs_setattr)
* => "write" fns (ext2fs_write, WRITE [ufs/ufs], msdosfs_write, nfs_write
* fusefs_write)
* => ffs_balloc [XXX: why? doesn't WRITE handle?]
* => NFS: nfs_loadattrcache, nfs_getattrcache, nfs_setattr
* => union fs: union_newsize
*/
void
uvm_vnp_setsize(struct vnode *vp, off_t newsize)
{
struct uvm_vnode *uvn = vp->v_uvm;
struct uvm_object *uobj = &uvn->u_obj;
KERNEL_ASSERT_LOCKED();
rw_enter(uobj->vmobjlock, RW_WRITE);
/* lock uvn and check for valid object, and if valid: do it! */
if (uvn->u_flags & UVM_VNODE_VALID) {
/*
* now check if the size has changed: if we shrink we had better
* toss some pages...
*/
if (uvn->u_size > newsize) {
(void)uvn_flush(&uvn->u_obj, newsize,
uvn->u_size, PGO_FREE);
}
uvn->u_size = newsize;
}
rw_exit(uobj->vmobjlock);
}
/*
* uvm_vnp_sync: flush all dirty VM pages back to their backing vnodes.
*
* => called from sys_sync with no VM structures locked
* => only one process can do a sync at a time (because the uvn
* structure only has one queue for sync'ing). we ensure this
* by holding the uvn_sync_lock while the sync is in progress.
* other processes attempting a sync will sleep on this lock
* until we are done.
*/
void
uvm_vnp_sync(struct mount *mp)
{
struct uvm_vnode *uvn;
struct vnode *vp;
/*
* step 1: ensure we are only ones using the uvn_sync_q by locking
* our lock...
*/
rw_enter_write(&uvn_sync_lock);
/*
* step 2: build up a simpleq of uvns of interest based on the
* write list. we gain a reference to uvns of interest.
*/
SIMPLEQ_INIT(&uvn_sync_q);
LIST_FOREACH(uvn, &uvn_wlist, u_wlist) {
vp = uvn->u_vnode;
if (mp && vp->v_mount != mp)
continue;
/*
* If the vnode is "blocked" it means it must be dying, which
* in turn means its in the process of being flushed out so
* we can safely skip it.
*
* note that uvn must already be valid because we found it on
* the wlist (this also means it can't be ALOCK'd).
*/
if ((uvn->u_flags & UVM_VNODE_BLOCKED) != 0)
continue;
/*
* gain reference. watch out for persisting uvns (need to
* regain vnode REF).
*/
if (uvn->u_obj.uo_refs == 0)
vref(vp);
uvn->u_obj.uo_refs++;
SIMPLEQ_INSERT_HEAD(&uvn_sync_q, uvn, u_syncq);
}
/* step 3: we now have a list of uvn's that may need cleaning. */
SIMPLEQ_FOREACH(uvn, &uvn_sync_q, u_syncq) {
rw_enter(uvn->u_obj.vmobjlock, RW_WRITE);
#ifdef DEBUG
if (uvn->u_flags & UVM_VNODE_DYING) {
printf("uvm_vnp_sync: dying vnode on sync list\n");
}
#endif
uvn_flush(&uvn->u_obj, 0, 0, PGO_CLEANIT|PGO_ALLPAGES|PGO_DOACTCLUST);
/*
* if we have the only reference and we just cleaned the uvn,
* then we can pull it out of the UVM_VNODE_WRITEABLE state
* thus allowing us to avoid thinking about flushing it again
* on later sync ops.
*/
if (uvn->u_obj.uo_refs == 1 &&
(uvn->u_flags & UVM_VNODE_WRITEABLE)) {
LIST_REMOVE(uvn, u_wlist);
uvn->u_flags &= ~UVM_VNODE_WRITEABLE;
}
rw_exit(uvn->u_obj.vmobjlock);
/* now drop our reference to the uvn */
uvn_detach(&uvn->u_obj);
}
rw_exit_write(&uvn_sync_lock);
}
202
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/* $OpenBSD: uvm_map.h,v 1.75 2022/03/12 08:11:07 mpi Exp $ */
/* $NetBSD: uvm_map.h,v 1.24 2001/02/18 21:19:08 chs Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993, The Regents of the University of California.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* The Mach Operating System project at Carnegie-Mellon University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_map.h 8.3 (Berkeley) 3/15/94
* from: Id: uvm_map.h,v 1.1.2.3 1998/02/07 01:16:55 chs Exp
*
*
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* All rights reserved.
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
#ifndef _UVM_UVM_MAP_H_
#define _UVM_UVM_MAP_H_
#include <sys/mutex.h>
#include <sys/rwlock.h>
#ifdef _KERNEL
/*
* UVM_MAP_CLIP_START: ensure that the entry begins at or after
* the starting address, if it doesn't we split the entry.
*
* => map must be locked by caller
*/
#define UVM_MAP_CLIP_START(_map, _entry, _addr) \
do { \
KASSERT((_entry)->end + (_entry)->fspace > (_addr)); \
if ((_entry)->start < (_addr)) \
uvm_map_clip_start((_map), (_entry), (_addr)); \
} while (0)
/*
* UVM_MAP_CLIP_END: ensure that the entry ends at or before
* the ending address, if it doesn't we split the entry.
*
* => map must be locked by caller
*/
#define UVM_MAP_CLIP_END(_map, _entry, _addr) \
do { \
KASSERT((_entry)->start < (_addr)); \
if ((_entry)->end > (_addr)) \
uvm_map_clip_end((_map), (_entry), (_addr)); \
} while (0)
/*
* extract flags
*/
#define UVM_EXTRACT_FIXPROT 0x8 /* set prot to maxprot as we go */
#endif /* _KERNEL */
#include <uvm/uvm_anon.h>
/*
* Address map entries consist of start and end addresses,
* a VM object (or sharing map) and offset into that object,
* and user-exported inheritance and protection information.
* Also included is control information for virtual copy operations.
*/
struct vm_map_entry {
union {
RBT_ENTRY(vm_map_entry) addr_entry; /* address tree */
SLIST_ENTRY(vm_map_entry) addr_kentry;
} daddrs;
union {
RBT_ENTRY(vm_map_entry) rbtree; /* Link freespace tree. */
TAILQ_ENTRY(vm_map_entry) tailq;/* Link freespace queue. */
TAILQ_ENTRY(vm_map_entry) deadq;/* dead entry queue */
} dfree;
#define uvm_map_entry_start_copy start
vaddr_t start; /* start address */
vaddr_t end; /* end address */
vsize_t guard; /* bytes in guard */
vsize_t fspace; /* free space */
union {
struct uvm_object *uvm_obj; /* uvm object */
struct vm_map *sub_map; /* belongs to another map */
} object; /* object I point to */
voff_t offset; /* offset into object */
struct vm_aref aref; /* anonymous overlay */
int etype; /* entry type */
vm_prot_t protection; /* protection code */
vm_prot_t max_protection; /* maximum protection */
vm_inherit_t inheritance; /* inheritance */
int wired_count; /* can be paged if == 0 */
int advice; /* madvise advice */
#define uvm_map_entry_stop_copy flags
u_int8_t flags; /* flags */
#define UVM_MAP_STATIC 0x01 /* static map entry */
#define UVM_MAP_KMEM 0x02 /* from kmem entry pool */
vsize_t fspace_augment; /* max(fspace) in subtree */
};
#define VM_MAPENT_ISWIRED(entry) ((entry)->wired_count != 0)
TAILQ_HEAD(uvm_map_deadq, vm_map_entry); /* dead entry queue */
RBT_HEAD(uvm_map_addr, vm_map_entry);
#ifdef _KERNEL
RBT_PROTOTYPE(uvm_map_addr, vm_map_entry, daddrs.addr_entry,
uvm_mapentry_addrcmp);
#endif
/*
* A Map is a rbtree of map entries, kept sorted by address.
* In addition, free space entries are also kept in a rbtree,
* indexed by free size.
*
*
*
* LOCKING PROTOCOL NOTES:
* -----------------------
*
* VM map locking is a little complicated. There are both shared
* and exclusive locks on maps. However, it is sometimes required
* to downgrade an exclusive lock to a shared lock, and upgrade to
* an exclusive lock again (to perform error recovery). However,
* another thread *must not* queue itself to receive an exclusive
* lock while before we upgrade back to exclusive, otherwise the
* error recovery becomes extremely difficult, if not impossible.
*
* In order to prevent this scenario, we introduce the notion of
* a `busy' map. A `busy' map is read-locked, but other threads
* attempting to write-lock wait for this flag to clear before
* entering the lock manager. A map may only be marked busy
* when the map is write-locked (and then the map must be downgraded
* to read-locked), and may only be marked unbusy by the thread
* which marked it busy (holding *either* a read-lock or a
* write-lock, the latter being gained by an upgrade).
*
* Access to the map `flags' member is controlled by the `flags_lock'
* simple lock. Note that some flags are static (set once at map
* creation time, and never changed), and thus require no locking
* to check those flags. All flags which are r/w must be set or
* cleared while the `flags_lock' is asserted. Additional locking
* requirements are:
*
* VM_MAP_PAGEABLE r/o static flag; no locking required
*
* VM_MAP_INTRSAFE r/o static flag; no locking required
*
* VM_MAP_WIREFUTURE r/w; may only be set or cleared when
* map is write-locked. may be tested
* without asserting `flags_lock'.
*
* VM_MAP_BUSY r/w; may only be set when map is
* write-locked, may only be cleared by
* thread which set it, map read-locked
* or write-locked. must be tested
* while `flags_lock' is asserted.
*
* VM_MAP_WANTLOCK r/w; may only be set when the map
* is busy, and thread is attempting
* to write-lock. must be tested
* while `flags_lock' is asserted.
*
* VM_MAP_GUARDPAGES r/o; must be specified at map
* initialization time.
* If set, guards will appear between
* automatic allocations.
* No locking required.
*
* VM_MAP_ISVMSPACE r/o; set by uvmspace_alloc.
* Signifies that this map is a vmspace.
* (The implementation treats all maps
* without this bit as kernel maps.)
* No locking required.
*
*
* All automatic allocations (uvm_map without MAP_FIXED) will allocate
* from vm_map.free.
* If that allocation fails:
* - vmspace maps will spill over into vm_map.bfree,
* - all other maps will call uvm_map_kmem_grow() to increase the arena.
*
* vmspace maps have their data, brk() and stack arenas automatically
* updated when uvm_map() is invoked without MAP_FIXED.
* The spill over arena (vm_map.bfree) will contain the space in the brk()
* and stack ranges.
* Kernel maps never have a bfree arena and this tree will always be empty.
*
*
* read_locks and write_locks are used in lock debugging code.
*
* Locks used to protect struct members in this file:
* a atomic operations
* I immutable after creation or exec(2)
* v `vm_map_lock' (this map `lock' or `mtx')
*/
struct vm_map {
struct pmap *pmap; /* [I] Physical map */
struct rwlock lock; /* Non-intrsafe lock */
struct mutex mtx; /* Intrsafe lock */
u_long sserial; /* [v] # stack changes */
u_long wserial; /* [v] # PROT_WRITE increases */
struct uvm_map_addr addr; /* [v] Entry tree, by addr */
vsize_t size; /* virtual size */
int ref_count; /* [a] Reference count */
int flags; /* flags */
struct mutex flags_lock; /* flags lock */
unsigned int timestamp; /* Version number */
vaddr_t min_offset; /* [I] First address in map. */
vaddr_t max_offset; /* [I] Last address in map. */
/*
* Allocation overflow regions.
*/
vaddr_t b_start; /* [v] Start for brk() alloc. */
vaddr_t b_end; /* [v] End for brk() alloc. */
vaddr_t s_start; /* [v] Start for stack alloc. */
vaddr_t s_end; /* [v] End for stack alloc. */
/*
* Special address selectors.
*
* The uaddr_exe mapping is used if:
* - protX is selected
* - the pointer is not NULL
*
* If uaddr_exe is not used, the other mappings are checked in
* order of appearance.
* If a hint is given, the selection will only be used if the hint
* falls in the range described by the mapping.
*
* The states are pointers because:
* - they may not all be in use
* - the struct size for different schemes is variable
*
* The uaddr_brk_stack selector will select addresses that are in
* the brk/stack area of the map.
*/
struct uvm_addr_state *uaddr_exe; /* Executable selector. */
struct uvm_addr_state *uaddr_any[4]; /* More selectors. */
struct uvm_addr_state *uaddr_brk_stack; /* Brk/stack selector. */
};
/* vm_map flags */
#define VM_MAP_PAGEABLE 0x01 /* ro: entries are pageable */
#define VM_MAP_INTRSAFE 0x02 /* ro: interrupt safe map */
#define VM_MAP_WIREFUTURE 0x04 /* rw: wire future mappings */
#define VM_MAP_BUSY 0x08 /* rw: map is busy */
#define VM_MAP_WANTLOCK 0x10 /* rw: want to write-lock */
#define VM_MAP_GUARDPAGES 0x20 /* rw: add guard pgs to map */
#define VM_MAP_ISVMSPACE 0x40 /* ro: map is a vmspace */
#define VM_MAP_SYSCALL_ONCE 0x80 /* rw: libc syscall registered */
/* Number of kernel maps and entries to statically allocate */
#define MAX_KMAPENT 1024 /* Sufficient to make it to the scheduler. */
#ifdef _KERNEL
/*
* globals:
*/
extern vaddr_t uvm_maxkaddr;
/*
* protos: the following prototypes define the interface to vm_map
*/
void uvm_map_deallocate(struct vm_map *);
int uvm_map_clean(struct vm_map *, vaddr_t, vaddr_t, int);
void uvm_map_clip_start(struct vm_map *, struct vm_map_entry *,
vaddr_t);
void uvm_map_clip_end(struct vm_map *, struct vm_map_entry *,
vaddr_t);
int uvm_map_extract(struct vm_map *, vaddr_t, vsize_t,
vaddr_t *, int);
struct vm_map * uvm_map_create(pmap_t, vaddr_t, vaddr_t, int);
vaddr_t uvm_map_pie(vaddr_t);
vaddr_t uvm_map_hint(struct vmspace *, vm_prot_t, vaddr_t, vaddr_t);
int uvm_map_syscall(struct vm_map *, vaddr_t, vaddr_t);
int uvm_map_inherit(struct vm_map *, vaddr_t, vaddr_t, vm_inherit_t);
int uvm_map_advice(struct vm_map *, vaddr_t, vaddr_t, int);
void uvm_map_init(void);
boolean_t uvm_map_lookup_entry(struct vm_map *, vaddr_t, vm_map_entry_t *);
boolean_t uvm_map_is_stack_remappable(struct vm_map *, vaddr_t, vsize_t);
int uvm_map_remap_as_stack(struct proc *, vaddr_t, vsize_t);
int uvm_map_replace(struct vm_map *, vaddr_t, vaddr_t,
vm_map_entry_t, int);
int uvm_map_reserve(struct vm_map *, vsize_t, vaddr_t, vsize_t,
vaddr_t *);
void uvm_map_setup(struct vm_map *, pmap_t, vaddr_t, vaddr_t, int);
int uvm_map_submap(struct vm_map *, vaddr_t, vaddr_t,
struct vm_map *);
void uvm_unmap(struct vm_map *, vaddr_t, vaddr_t);
void uvm_unmap_detach(struct uvm_map_deadq *, int);
void uvm_unmap_remove(struct vm_map*, vaddr_t, vaddr_t,
struct uvm_map_deadq *, boolean_t, boolean_t);
void uvm_map_set_uaddr(struct vm_map*, struct uvm_addr_state**,
struct uvm_addr_state*);
int uvm_map_mquery(struct vm_map*, vaddr_t*, vsize_t, voff_t, int);
struct p_inentry;
int uvm_map_inentry_sp(vm_map_entry_t);
int uvm_map_inentry_pc(vm_map_entry_t);
boolean_t uvm_map_inentry(struct proc *, struct p_inentry *, vaddr_t addr,
const char *fmt, int (*fn)(vm_map_entry_t), u_long serial);
struct kinfo_vmentry;
int uvm_map_fill_vmmap(struct vm_map *, struct kinfo_vmentry *,
size_t *);
/*
* VM map locking operations:
*
* These operations perform locking on the data portion of the
* map.
*
* vm_map_lock_try: try to lock a map, failing if it is already locked.
*
* vm_map_lock: acquire an exclusive (write) lock on a map.
*
* vm_map_lock_read: acquire a shared (read) lock on a map.
*
* vm_map_unlock: release an exclusive lock on a map.
*
* vm_map_unlock_read: release a shared lock on a map.
*
* vm_map_downgrade: downgrade an exclusive lock to a shared lock.
*
* vm_map_upgrade: upgrade a shared lock to an exclusive lock.
*
* vm_map_busy: mark a map as busy.
*
* vm_map_unbusy: clear busy status on a map.
*
*/
boolean_t vm_map_lock_try_ln(struct vm_map*, char*, int);
void vm_map_lock_ln(struct vm_map*, char*, int);
void vm_map_lock_read_ln(struct vm_map*, char*, int);
void vm_map_unlock_ln(struct vm_map*, char*, int);
void vm_map_unlock_read_ln(struct vm_map*, char*, int);
void vm_map_downgrade_ln(struct vm_map*, char*, int);
void vm_map_upgrade_ln(struct vm_map*, char*, int);
void vm_map_busy_ln(struct vm_map*, char*, int);
void vm_map_unbusy_ln(struct vm_map*, char*, int);
#ifdef DIAGNOSTIC
#define vm_map_lock_try(map) vm_map_lock_try_ln(map, __FILE__, __LINE__)
#define vm_map_lock(map) vm_map_lock_ln(map, __FILE__, __LINE__)
#define vm_map_lock_read(map) vm_map_lock_read_ln(map, __FILE__, __LINE__)
#define vm_map_unlock(map) vm_map_unlock_ln(map, __FILE__, __LINE__)
#define vm_map_unlock_read(map) vm_map_unlock_read_ln(map, __FILE__, __LINE__)
#define vm_map_downgrade(map) vm_map_downgrade_ln(map, __FILE__, __LINE__)
#define vm_map_upgrade(map) vm_map_upgrade_ln(map, __FILE__, __LINE__)
#define vm_map_busy(map) vm_map_busy_ln(map, __FILE__, __LINE__)
#define vm_map_unbusy(map) vm_map_unbusy_ln(map, __FILE__, __LINE__)
#else
#define vm_map_lock_try(map) vm_map_lock_try_ln(map, NULL, 0)
#define vm_map_lock(map) vm_map_lock_ln(map, NULL, 0)
#define vm_map_lock_read(map) vm_map_lock_read_ln(map, NULL, 0)
#define vm_map_unlock(map) vm_map_unlock_ln(map, NULL, 0)
#define vm_map_unlock_read(map) vm_map_unlock_read_ln(map, NULL, 0)
#define vm_map_downgrade(map) vm_map_downgrade_ln(map, NULL, 0)
#define vm_map_upgrade(map) vm_map_upgrade_ln(map, NULL, 0)
#define vm_map_busy(map) vm_map_busy_ln(map, NULL, 0)
#define vm_map_unbusy(map) vm_map_unbusy_ln(map, NULL, 0)
#endif
void uvm_map_lock_entry(struct vm_map_entry *);
void uvm_map_unlock_entry(struct vm_map_entry *);
#endif /* _KERNEL */
/*
* Functions implemented as macros
*/
#define vm_map_min(map) ((map)->min_offset)
#define vm_map_max(map) ((map)->max_offset)
#define vm_map_pmap(map) ((map)->pmap)
#endif /* _UVM_UVM_MAP_H_ */
10
3
1
4
1
3
5
5
3
5
1
2
5
5
4
5
5
2
4
4
5
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
/* $OpenBSD: tty_nmea.c,v 1.51 2022/04/02 22:45:18 mlarkin Exp $ */
/*
* Copyright (c) 2006, 2007, 2008 Marc Balmer <mbalmer@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* A tty line discipline to decode NMEA 0183 data to get the time
* and GPS position data
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sensors.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/time.h>
#ifdef NMEA_DEBUG
#define DPRINTFN(n, x) do { if (nmeadebug > (n)) printf x; } while (0)
int nmeadebug = 0;
#else
#define DPRINTFN(n, x)
#endif
#define DPRINTF(x) DPRINTFN(0, x)
void nmeaattach(int);
#define NMEAMAX 82
#define MAXFLDS 32
#define KNOTTOMS (51444 / 100)
#ifdef NMEA_DEBUG
#define TRUSTTIME 30
#else
#define TRUSTTIME (10 * 60) /* 10 minutes */
#endif
int nmea_count, nmea_nxid;
struct nmea {
char cbuf[NMEAMAX]; /* receive buffer */
struct ksensor time; /* the timedelta sensor */
struct ksensor signal; /* signal status */
struct ksensor latitude;
struct ksensor longitude;
struct ksensor altitude;
struct ksensor speed;
struct ksensordev timedev;
struct timespec ts; /* current timestamp */
struct timespec lts; /* timestamp of last '$' seen */
struct timeout nmea_tout; /* invalidate sensor */
int64_t gap; /* gap between two sentences */
#ifdef NMEA_DEBUG
int gapno;
#endif
int64_t last; /* last time rcvd */
int sync; /* if 1, waiting for '$' */
int pos; /* position in rcv buffer */
int no_pps; /* no PPS although requested */
char mode; /* GPS mode */
};
/* NMEA decoding */
void nmea_scan(struct nmea *, struct tty *);
void nmea_gprmc(struct nmea *, struct tty *, char *fld[], int fldcnt);
void nmea_decode_gga(struct nmea *, struct tty *, char *fld[], int fldcnt);
/* date and time conversion */
int nmea_date_to_nano(char *s, int64_t *nano);
int nmea_time_to_nano(char *s, int64_t *nano);
/* longitude and latitude conversion */
int nmea_degrees(int64_t *dst, char *src, int neg);
int nmea_atoi(int64_t *dst, char *src);
/* degrade the timedelta sensor */
void nmea_timeout(void *);
void
nmeaattach(int dummy)
{
/* noop */
}
int
nmeaopen(dev_t dev, struct tty *tp, struct proc *p)
{
struct nmea *np;
int error;
if (tp->t_line == NMEADISC)
return (ENODEV);
if ((error = suser(p)) != 0)
return (error);
np = malloc(sizeof(struct nmea), M_DEVBUF, M_WAITOK | M_ZERO);
snprintf(np->timedev.xname, sizeof(np->timedev.xname), "nmea%d",
nmea_nxid++);
nmea_count++;
np->time.status = SENSOR_S_UNKNOWN;
np->time.type = SENSOR_TIMEDELTA;
np->time.flags = SENSOR_FINVALID;
sensor_attach(&np->timedev, &np->time);
np->signal.type = SENSOR_INDICATOR;
np->signal.status = SENSOR_S_UNKNOWN;
np->signal.value = 0;
strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
sensor_attach(&np->timedev, &np->signal);
np->latitude.type = SENSOR_ANGLE;
np->latitude.status = SENSOR_S_UNKNOWN;
np->latitude.flags = SENSOR_FINVALID;
np->latitude.value = 0;
strlcpy(np->latitude.desc, "Latitude", sizeof(np->latitude.desc));
sensor_attach(&np->timedev, &np->latitude);
np->longitude.type = SENSOR_ANGLE;
np->longitude.status = SENSOR_S_UNKNOWN;
np->longitude.flags = SENSOR_FINVALID;
np->longitude.value = 0;
strlcpy(np->longitude.desc, "Longitude", sizeof(np->longitude.desc));
sensor_attach(&np->timedev, &np->longitude);
np->altitude.type = SENSOR_DISTANCE;
np->altitude.status = SENSOR_S_UNKNOWN;
np->altitude.flags = SENSOR_FINVALID;
np->altitude.value = 0;
strlcpy(np->altitude.desc, "Altitude", sizeof(np->altitude.desc));
sensor_attach(&np->timedev, &np->altitude);
np->speed.type = SENSOR_VELOCITY;
np->speed.status = SENSOR_S_UNKNOWN;
np->speed.flags = SENSOR_FINVALID;
np->speed.value = 0;
strlcpy(np->speed.desc, "Ground speed", sizeof(np->speed.desc));
sensor_attach(&np->timedev, &np->speed);
np->sync = 1;
tp->t_sc = (caddr_t)np;
error = linesw[TTYDISC].l_open(dev, tp, p);
if (error) {
free(np, M_DEVBUF, sizeof(*np));
tp->t_sc = NULL;
} else {
sensordev_install(&np->timedev);
timeout_set(&np->nmea_tout, nmea_timeout, np);
}
return (error);
}
int
nmeaclose(struct tty *tp, int flags, struct proc *p)
{
struct nmea *np = (struct nmea *)tp->t_sc;
tp->t_line = TTYDISC; /* switch back to termios */
timeout_del(&np->nmea_tout);
sensordev_deinstall(&np->timedev);
free(np, M_DEVBUF, sizeof(*np));
tp->t_sc = NULL;
nmea_count--;
if (nmea_count == 0)
nmea_nxid = 0;
return (linesw[TTYDISC].l_close(tp, flags, p));
}
/* Collect NMEA sentences from the tty. */
int
nmeainput(int c, struct tty *tp)
{
struct nmea *np = (struct nmea *)tp->t_sc;
struct timespec ts;
int64_t gap;
long tmin, tmax;
switch (c) {
case '$':
nanotime(&ts);
np->pos = np->sync = 0;
gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
(np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
np->lts.tv_sec = ts.tv_sec;
np->lts.tv_nsec = ts.tv_nsec;
if (gap <= np->gap)
break;
np->ts.tv_sec = ts.tv_sec;
np->ts.tv_nsec = ts.tv_nsec;
#ifdef NMEA_DEBUG
if (nmeadebug > 0) {
linesw[TTYDISC].l_rint('[', tp);
linesw[TTYDISC].l_rint('0' + np->gapno++, tp);
linesw[TTYDISC].l_rint(']', tp);
}
#endif
np->gap = gap;
/*
* If a tty timestamp is available, make sure its value is
* reasonable by comparing against the timestamp just taken.
* If they differ by more than 2 seconds, assume no PPS signal
* is present, note the fact, and keep using the timestamp
* value. When this happens, the sensor state is set to
* CRITICAL later when the GPRMC sentence is decoded.
*/
if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
if (tmax - tmin > 1)
np->no_pps = 1;
else {
np->ts.tv_sec = tp->t_tv.tv_sec;
np->ts.tv_nsec = tp->t_tv.tv_usec *
1000L;
np->no_pps = 0;
}
}
break;
case '\r':
case '\n':
if (!np->sync) {
np->cbuf[np->pos] = '\0';
nmea_scan(np, tp);
np->sync = 1;
}
break;
default:
if (!np->sync && np->pos < (NMEAMAX - 1))
np->cbuf[np->pos++] = c;
break;
}
/* pass data to termios */
return (linesw[TTYDISC].l_rint(c, tp));
}
/* Scan the NMEA sentence just received. */
void
nmea_scan(struct nmea *np, struct tty *tp)
{
int fldcnt = 0, cksum = 0, msgcksum, n;
char *fld[MAXFLDS], *cs;
/* split into fields and calculate the checksum */
fld[fldcnt++] = &np->cbuf[0]; /* message type */
for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
switch (np->cbuf[n]) {
case '*':
np->cbuf[n] = '\0';
cs = &np->cbuf[n + 1];
break;
case ',':
if (fldcnt < MAXFLDS) {
cksum ^= np->cbuf[n];
np->cbuf[n] = '\0';
fld[fldcnt++] = &np->cbuf[n + 1];
} else {
DPRINTF(("nr of fields in %s sentence exceeds "
"maximum of %d\n", fld[0], MAXFLDS));
return;
}
break;
default:
cksum ^= np->cbuf[n];
}
}
/*
* we only look at the messages coming from well-known sources or 'talkers',
* distinguished by the two-chars prefix, the most common being:
* GPS (GP)
* Glonass (GL)
* BeiDou (BD)
* Galileo (GA)
* 'Any kind/a mix of GNSS systems' (GN)
*/
if (strncmp(fld[0], "BD", 2) &&
strncmp(fld[0], "GA", 2) &&
strncmp(fld[0], "GL", 2) &&
strncmp(fld[0], "GN", 2) &&
strncmp(fld[0], "GP", 2))
return;
/* we look for the RMC & GGA messages */
if (strncmp(fld[0] + 2, "RMC", 3) &&
strncmp(fld[0] + 2, "GGA", 3))
return;
/* if we have a checksum, verify it */
if (cs != NULL) {
msgcksum = 0;
while (*cs) {
if ((*cs >= '0' && *cs <= '9') ||
(*cs >= 'A' && *cs <= 'F')) {
if (msgcksum)
msgcksum <<= 4;
if (*cs >= '0' && *cs<= '9')
msgcksum += *cs - '0';
else if (*cs >= 'A' && *cs <= 'F')
msgcksum += 10 + *cs - 'A';
cs++;
} else {
DPRINTF(("bad char %c in checksum\n", *cs));
return;
}
}
if (msgcksum != cksum) {
DPRINTF(("checksum mismatch\n"));
return;
}
}
if (strncmp(fld[0] + 2, "RMC", 3) == 0)
nmea_gprmc(np, tp, fld, fldcnt);
if (strncmp(fld[0] + 2, "GGA", 3) == 0)
nmea_decode_gga(np, tp, fld, fldcnt);
}
/* Decode the recommended minimum specific GPS/TRANSIT data. */
void
nmea_gprmc(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
{
int64_t date_nano, time_nano, nmea_now;
int jumped = 0;
if (fldcnt < 12 || fldcnt > 14) {
DPRINTF(("gprmc: field count mismatch, %d\n", fldcnt));
return;
}
if (nmea_time_to_nano(fld[1], &time_nano)) {
DPRINTF(("gprmc: illegal time, %s\n", fld[1]));
return;
}
if (nmea_date_to_nano(fld[9], &date_nano)) {
DPRINTF(("gprmc: illegal date, %s\n", fld[9]));
return;
}
nmea_now = date_nano + time_nano;
if (nmea_now <= np->last) {
DPRINTF(("gprmc: time not monotonically increasing\n"));
jumped = 1;
}
np->last = nmea_now;
np->gap = 0LL;
#ifdef NMEA_DEBUG
if (np->time.status == SENSOR_S_UNKNOWN) {
np->time.status = SENSOR_S_OK;
timeout_add_sec(&np->nmea_tout, TRUSTTIME);
}
np->gapno = 0;
if (nmeadebug > 0) {
linesw[TTYDISC].l_rint('[', tp);
linesw[TTYDISC].l_rint('C', tp);
linesw[TTYDISC].l_rint(']', tp);
}
#endif
np->time.value = np->ts.tv_sec * 1000000000LL +
np->ts.tv_nsec - nmea_now;
np->time.tv.tv_sec = np->ts.tv_sec;
np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
if (fldcnt < 13)
strlcpy(np->time.desc, "GPS", sizeof(np->time.desc));
else if (*fld[12] != np->mode) {
np->mode = *fld[12];
switch (np->mode) {
case 'S':
strlcpy(np->time.desc, "GPS simulated",
sizeof(np->time.desc));
break;
case 'E':
strlcpy(np->time.desc, "GPS estimated",
sizeof(np->time.desc));
break;
case 'A':
strlcpy(np->time.desc, "GPS autonomous",
sizeof(np->time.desc));
break;
case 'D':
strlcpy(np->time.desc, "GPS differential",
sizeof(np->time.desc));
break;
case 'N':
strlcpy(np->time.desc, "GPS invalid",
sizeof(np->time.desc));
break;
default:
strlcpy(np->time.desc, "GPS unknown",
sizeof(np->time.desc));
DPRINTF(("gprmc: unknown mode '%c'\n", np->mode));
}
}
switch (*fld[2]) {
case 'A': /* The GPS has a fix, (re)arm the timeout. */
/* XXX is 'D' also a valid state? */
np->time.status = SENSOR_S_OK;
np->signal.value = 1;
np->signal.status = SENSOR_S_OK;
np->latitude.status = SENSOR_S_OK;
np->longitude.status = SENSOR_S_OK;
np->speed.status = SENSOR_S_OK;
np->time.flags &= ~SENSOR_FINVALID;
np->latitude.flags &= ~SENSOR_FINVALID;
np->longitude.flags &= ~SENSOR_FINVALID;
np->speed.flags &= ~SENSOR_FINVALID;
break;
case 'V': /*
* The GPS indicates a warning status, do not add to
* the timeout, if the condition persist, the sensor
* will be degraded. Signal the condition through
* the signal sensor.
*/
np->signal.value = 0;
np->signal.status = SENSOR_S_CRIT;
np->latitude.status = SENSOR_S_WARN;
np->longitude.status = SENSOR_S_WARN;
np->speed.status = SENSOR_S_WARN;
break;
}
if (nmea_degrees(&np->latitude.value, fld[3], *fld[4] == 'S' ? 1 : 0))
np->latitude.status = SENSOR_S_WARN;
if (nmea_degrees(&np->longitude.value,fld[5], *fld[6] == 'W' ? 1 : 0))
np->longitude.status = SENSOR_S_WARN;
if (nmea_atoi(&np->speed.value, fld[7]))
np->speed.status = SENSOR_S_WARN;
/* convert from knot to um/s */
np->speed.value *= KNOTTOMS;
if (jumped)
np->time.status = SENSOR_S_WARN;
if (np->time.status == SENSOR_S_OK)
timeout_add_sec(&np->nmea_tout, TRUSTTIME);
/*
* If tty timestamping is requested, but no PPS signal is present, set
* the sensor state to CRITICAL.
*/
if (np->no_pps)
np->time.status = SENSOR_S_CRIT;
}
/* Decode the GPS fix data for altitude.
* - field 9 is the altitude in meters
* $GNGGA,085901.00,1234.5678,N,00987.12345,E,1,12,0.84,1040.9,M,47.4,M,,*4B
*/
void
nmea_decode_gga(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
{
if (fldcnt != 15) {
DPRINTF(("GGA: field count mismatch, %d\n", fldcnt));
return;
}
#ifdef NMEA_DEBUG
if (nmeadebug > 0) {
linesw[TTYDISC].l_rint('[', tp);
linesw[TTYDISC].l_rint('C', tp);
linesw[TTYDISC].l_rint(']', tp);
}
#endif
np->altitude.status = SENSOR_S_OK;
if (nmea_atoi(&np->altitude.value, fld[9]))
np->altitude.status = SENSOR_S_WARN;
/* convert to uMeter */
np->altitude.value *= 1000;
np->altitude.flags &= ~SENSOR_FINVALID;
}
/*
* Convert nmea integer/decimal values in the form of XXXX.Y to an integer value
* if it's a meter/altitude value, will be returned as mm
*/
int
nmea_atoi(int64_t *dst, char *src)
{
char *p;
int i = 3; /* take 3 digits */
*dst = 0;
for (p = src; *p && *p != '.' && *p >= '0' && *p <= '9' ; )
*dst = *dst * 10 + (*p++ - '0');
/* *p should be '.' at that point */
if (*p != '.')
return -1; /* no decimal point, or bogus value ? */
p++;
/* read digits after decimal point, stop at first non-digit */
for (; *p && i > 0 && *p >= '0' && *p <= '9' ; i--)
*dst = *dst * 10 + (*p++ - '0');
for (; i > 0 ; i--)
*dst *= 10;
DPRINTFN(2,("%s -> %lld\n", src, *dst));
return 0;
}
/*
* Convert a nmea position in the form DDDMM.MMMM to an
* angle sensor value (degrees*1000000)
*/
int
nmea_degrees(int64_t *dst, char *src, int neg)
{
size_t ppos;
int i, n;
int64_t deg = 0, min = 0;
char *p;
while (*src == '0')
++src; /* skip leading zeroes */
for (p = src, ppos = 0; *p; ppos++)
if (*p++ == '.')
break;
if (*p == '\0')
return (-1); /* no decimal point */
for (n = 0; *src && n + 2 < ppos; n++)
deg = deg * 10 + (*src++ - '0');
for (; *src && n < ppos; n++)
min = min * 10 + (*src++ - '0');
src++; /* skip decimal point */
for (; *src && n < (ppos + 4); n++)
min = min * 10 + (*src++ - '0');
for (i=0; i < 6 + ppos - n; i++)
min *= 10;
deg = deg * 1000000 + (min/60);
*dst = neg ? -deg : deg;
return (0);
}
/*
* Convert a NMEA 0183 formatted date string to seconds since the epoch.
* The string must be of the form DDMMYY.
* Return 0 on success, -1 if illegal characters are encountered.
*/
int
nmea_date_to_nano(char *s, int64_t *nano)
{
struct clock_ymdhms ymd;
time_t secs;
char *p;
int n;
/* make sure the input contains only numbers and is six digits long */
for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
;
if (n != 6 || (*p != '\0'))
return (-1);
ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
secs = clock_ymdhms_to_secs(&ymd);
*nano = secs * 1000000000LL;
return (0);
}
/*
* Convert NMEA 0183 formatted time string to nanoseconds since midnight.
* The string must be of the form HHMMSS[.[sss]] (e.g. 143724 or 143723.615).
* Return 0 on success, -1 if illegal characters are encountered.
*/
int
nmea_time_to_nano(char *s, int64_t *nano)
{
long fac = 36000L, div = 6L, secs = 0L, frac = 0L;
char ul = '2';
int n;
for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
secs += (*s - '0') * fac;
div = 16 - div;
fac /= div;
switch (n) {
case 0:
if (*s <= '1')
ul = '9';
else
ul = '3';
break;
case 1:
case 3:
ul = '5';
break;
case 2:
case 4:
ul = '9';
break;
}
}
if (fac)
return (-1);
/* Handle the fractions of a second, up to a maximum of 6 digits. */
div = 1L;
if (*s == '.') {
for (++s; div < 1000000 && *s && *s >= '0' && *s <= '9'; s++) {
frac *= 10;
frac += (*s - '0');
div *= 10;
}
}
if (*s != '\0')
return (-1);
*nano = secs * 1000000000LL + (int64_t)frac * (1000000000 / div);
return (0);
}
/*
* Degrade the sensor state if we received no NMEA sentences for more than
* TRUSTTIME seconds.
*/
void
nmea_timeout(void *xnp)
{
struct nmea *np = xnp;
np->signal.value = 0;
np->signal.status = SENSOR_S_CRIT;
if (np->time.status == SENSOR_S_OK) {
np->time.status = SENSOR_S_WARN;
np->latitude.status = SENSOR_S_WARN;
np->longitude.status = SENSOR_S_WARN;
np->altitude.status = SENSOR_S_WARN;
np->speed.status = SENSOR_S_WARN;
/*
* further degrade in TRUSTTIME seconds if no new valid NMEA
* sentences are received.
*/
timeout_add_sec(&np->nmea_tout, TRUSTTIME);
} else {
np->time.status = SENSOR_S_CRIT;
np->latitude.status = SENSOR_S_CRIT;
np->longitude.status = SENSOR_S_CRIT;
np->altitude.status = SENSOR_S_CRIT;
np->speed.status = SENSOR_S_CRIT;
}
}
14
6
8
1
9
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/* $OpenBSD: kern_clock.c,v 1.105 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */
/*-
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_clock.c 8.5 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/resourcevar.h>
#include <sys/sysctl.h>
#include <sys/sched.h>
#include <sys/timetc.h>
#if defined(GPROF) || defined(DDBPROF)
#include <sys/gmon.h>
#endif
#include "dt.h"
#if NDT > 0
#include <dev/dt/dtvar.h>
#endif
/*
* Clock handling routines.
*
* This code is written to operate with two timers that run independently of
* each other. The main clock, running hz times per second, is used to keep
* track of real time. The second timer handles kernel and user profiling,
* and does resource use estimation. If the second timer is programmable,
* it is randomized to avoid aliasing between the two clocks. For example,
* the randomization prevents an adversary from always giving up the cpu
* just before its quantum expires. Otherwise, it would never accumulate
* cpu ticks. The mean frequency of the second timer is stathz.
*
* If no second timer exists, stathz will be zero; in this case we drive
* profiling and statistics off the main clock. This WILL NOT be accurate;
* do not do it unless absolutely necessary.
*
* The statistics clock may (or may not) be run at a higher rate while
* profiling. This profile clock runs at profhz. We require that profhz
* be an integral multiple of stathz.
*
* If the statistics clock is running fast, it must be divided by the ratio
* profhz/stathz for statistics. (For profiling, every tick counts.)
*/
int stathz;
int schedhz;
int profhz;
int profprocs;
int ticks;
static int psdiv, pscnt; /* prof => stat divider */
int psratio; /* ratio: prof / stat */
volatile unsigned long jiffies; /* XXX Linux API for drm(4) */
/*
* Initialize clock frequencies and start both clocks running.
*/
void
initclocks(void)
{
int i;
ticks = INT_MAX - (15 * 60 * hz);
jiffies = ULONG_MAX - (10 * 60 * hz);
/*
* Set divisors to 1 (normal case) and let the machine-specific
* code do its bit.
*/
psdiv = pscnt = 1;
cpu_initclocks();
/*
* Compute profhz/stathz, and fix profhz if needed.
*/
i = stathz ? stathz : hz;
if (profhz == 0)
profhz = i;
psratio = profhz / i;
inittimecounter();
}
/*
* hardclock does the accounting needed for ITIMER_PROF and ITIMER_VIRTUAL.
* We don't want to send signals with psignal from hardclock because it makes
* MULTIPROCESSOR locking very complicated. Instead, to use an idea from
* FreeBSD, we set a flag on the thread and when it goes to return to
* userspace it signals itself.
*/
/*
* The real-time timer, interrupting hz times per second.
*/
void
hardclock(struct clockframe *frame)
{
struct proc *p;
struct cpu_info *ci = curcpu();
p = curproc;
if (p && ((p->p_flag & (P_SYSTEM | P_WEXIT)) == 0)) {
struct process *pr = p->p_p;
/*
* Run current process's virtual and profile time, as needed.
*/
if (CLKF_USERMODE(frame) &&
timespecisset(&pr->ps_timer[ITIMER_VIRTUAL].it_value) &&
itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], tick_nsec) == 0) {
atomic_setbits_int(&p->p_flag, P_ALRMPEND);
need_proftick(p);
}
if (timespecisset(&pr->ps_timer[ITIMER_PROF].it_value) &&
itimerdecr(&pr->ps_timer[ITIMER_PROF], tick_nsec) == 0) {
atomic_setbits_int(&p->p_flag, P_PROFPEND);
need_proftick(p);
}
}
/*
* If no separate statistics clock is available, run it from here.
*/
if (stathz == 0)
statclock(frame);
if (--ci->ci_schedstate.spc_rrticks <= 0)
roundrobin(ci);
#if NDT > 0
DT_ENTER(profile, NULL);
if (CPU_IS_PRIMARY(ci))
DT_ENTER(interval, NULL);
#endif
/*
* If we are not the primary CPU, we're not allowed to do
* any more work.
*/
if (CPU_IS_PRIMARY(ci) == 0)
return;
tc_ticktock();
ticks++;
jiffies++;
/*
* Update the timeout wheel.
*/
timeout_hardclock_update();
}
/*
* Compute number of hz in the specified amount of time.
*/
int
tvtohz(const struct timeval *tv)
{
unsigned long nticks;
time_t sec;
long usec;
/*
* If the number of usecs in the whole seconds part of the time
* fits in a long, then the total number of usecs will
* fit in an unsigned long. Compute the total and convert it to
* ticks, rounding up and adding 1 to allow for the current tick
* to expire. Rounding also depends on unsigned long arithmetic
* to avoid overflow.
*
* Otherwise, if the number of ticks in the whole seconds part of
* the time fits in a long, then convert the parts to
* ticks separately and add, using similar rounding methods and
* overflow avoidance. This method would work in the previous
* case but it is slightly slower and assumes that hz is integral.
*
* Otherwise, round the time down to the maximum
* representable value.
*
* If ints have 32 bits, then the maximum value for any timeout in
* 10ms ticks is 248 days.
*/
sec = tv->tv_sec;
usec = tv->tv_usec;
if (sec < 0 || (sec == 0 && usec <= 0))
nticks = 0;
else if (sec <= LONG_MAX / 1000000)
nticks = (sec * 1000000 + (unsigned long)usec + (tick - 1))
/ tick + 1;
else if (sec <= LONG_MAX / hz)
nticks = sec * hz
+ ((unsigned long)usec + (tick - 1)) / tick + 1;
else
nticks = LONG_MAX;
if (nticks > INT_MAX)
nticks = INT_MAX;
return ((int)nticks);
}
int
tstohz(const struct timespec *ts)
{
struct timeval tv;
TIMESPEC_TO_TIMEVAL(&tv, ts);
/* Round up. */
if ((ts->tv_nsec % 1000) != 0) {
tv.tv_usec += 1;
if (tv.tv_usec >= 1000000) {
tv.tv_usec -= 1000000;
tv.tv_sec += 1;
}
}
return (tvtohz(&tv));
}
/*
* Start profiling on a process.
*
* Kernel profiling passes proc0 which never exits and hence
* keeps the profile clock running constantly.
*/
void
startprofclock(struct process *pr)
{
int s;
if ((pr->ps_flags & PS_PROFIL) == 0) {
atomic_setbits_int(&pr->ps_flags, PS_PROFIL);
if (++profprocs == 1 && stathz != 0) {
s = splstatclock();
psdiv = pscnt = psratio;
setstatclockrate(profhz);
splx(s);
}
}
}
/*
* Stop profiling on a process.
*/
void
stopprofclock(struct process *pr)
{
int s;
if (pr->ps_flags & PS_PROFIL) {
atomic_clearbits_int(&pr->ps_flags, PS_PROFIL);
if (--profprocs == 0 && stathz != 0) {
s = splstatclock();
psdiv = pscnt = 1;
setstatclockrate(stathz);
splx(s);
}
}
}
/*
* Statistics clock. Grab profile sample, and if divider reaches 0,
* do process and kernel statistics.
*/
void
statclock(struct clockframe *frame)
{
#if defined(GPROF) || defined(DDBPROF)
struct gmonparam *g;
u_long i;
#endif
struct cpu_info *ci = curcpu();
struct schedstate_percpu *spc = &ci->ci_schedstate;
struct proc *p = curproc;
struct process *pr;
/*
* Notice changes in divisor frequency, and adjust clock
* frequency accordingly.
*/
if (spc->spc_psdiv != psdiv) {
spc->spc_psdiv = psdiv;
spc->spc_pscnt = psdiv;
if (psdiv == 1) {
setstatclockrate(stathz);
} else {
setstatclockrate(profhz);
}
}
if (CLKF_USERMODE(frame)) {
pr = p->p_p;
if (pr->ps_flags & PS_PROFIL)
addupc_intr(p, CLKF_PC(frame));
if (--spc->spc_pscnt > 0)
return;
/*
* Came from user mode; CPU was in user state.
* If this process is being profiled record the tick.
*/
p->p_uticks++;
if (pr->ps_nice > NZERO)
spc->spc_cp_time[CP_NICE]++;
else
spc->spc_cp_time[CP_USER]++;
} else {
#if defined(GPROF) || defined(DDBPROF)
/*
* Kernel statistics are just like addupc_intr, only easier.
*/
g = ci->ci_gmon;
if (g != NULL && g->state == GMON_PROF_ON) {
i = CLKF_PC(frame) - g->lowpc;
if (i < g->textsize) {
i /= HISTFRACTION * sizeof(*g->kcount);
g->kcount[i]++;
}
}
#endif
if (p != NULL && p->p_p->ps_flags & PS_PROFIL)
addupc_intr(p, PROC_PC(p));
if (--spc->spc_pscnt > 0)
return;
/*
* Came from kernel mode, so we were:
* - spinning on a lock
* - handling an interrupt,
* - doing syscall or trap work on behalf of the current
* user process, or
* - spinning in the idle loop.
* Whichever it is, charge the time as appropriate.
* Note that we charge interrupts to the current process,
* regardless of whether they are ``for'' that process,
* so that we know how much of its real time was spent
* in ``non-process'' (i.e., interrupt) work.
*/
if (CLKF_INTR(frame)) {
if (p != NULL)
p->p_iticks++;
spc->spc_cp_time[spc->spc_spinning ?
CP_SPIN : CP_INTR]++;
} else if (p != NULL && p != spc->spc_idleproc) {
p->p_sticks++;
spc->spc_cp_time[spc->spc_spinning ?
CP_SPIN : CP_SYS]++;
} else
spc->spc_cp_time[spc->spc_spinning ?
CP_SPIN : CP_IDLE]++;
}
spc->spc_pscnt = psdiv;
if (p != NULL) {
p->p_cpticks++;
/*
* If no schedclock is provided, call it here at ~~12-25 Hz;
* ~~16 Hz is best
*/
if (schedhz == 0) {
if ((++spc->spc_schedticks & 3) == 0)
schedclock(p);
}
}
}
/*
* Return information about system clocks.
*/
int
sysctl_clockrate(char *where, size_t *sizep, void *newp)
{
struct clockinfo clkinfo;
/*
* Construct clockinfo structure.
*/
memset(&clkinfo, 0, sizeof clkinfo);
clkinfo.tick = tick;
clkinfo.hz = hz;
clkinfo.profhz = profhz;
clkinfo.stathz = stathz ? stathz : hz;
return (sysctl_rdstruct(where, sizep, newp, &clkinfo, sizeof(clkinfo)));
}
60
61
48
18
61
19
61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
/* $OpenBSD: lapic.c,v 1.62 2022/08/25 20:43:17 cheloha Exp $ */
/* $NetBSD: lapic.c,v 1.2 2003/05/08 01:04:35 fvdl Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by RedBack Networks Inc.
*
* Author: Bill Sommerfeld
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <uvm/uvm_extern.h>
#include <machine/codepatch.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/pmap.h>
#include <machine/mpbiosvar.h>
#include <machine/specialreg.h>
#include <machine/segments.h>
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#include <dev/ic/i8253reg.h>
#include "ioapic.h"
#include "xen.h"
#include "hyperv.h"
#if NIOAPIC > 0
#include <machine/i82093var.h>
#endif
/* #define LAPIC_DEBUG */
#ifdef LAPIC_DEBUG
#define DPRINTF(x...) do { printf(x); } while(0)
#else
#define DPRINTF(x...)
#endif /* LAPIC_DEBUG */
struct evcount clk_count;
#ifdef MULTIPROCESSOR
struct evcount ipi_count;
#endif
void lapic_delay(int);
static u_int32_t lapic_gettick(void);
void lapic_clockintr(void *, struct intrframe);
void lapic_initclocks(void);
void lapic_map(paddr_t);
void lapic_hwmask(struct pic *, int);
void lapic_hwunmask(struct pic *, int);
void lapic_setup(struct pic *, struct cpu_info *, int, int, int);
extern char idt_allocmap[];
struct pic local_pic = {
{0, {NULL}, NULL, 0, "lapic", NULL, 0, 0},
PIC_LAPIC,
#ifdef MULTIPROCESSOR
{},
#endif
lapic_hwmask,
lapic_hwunmask,
lapic_setup,
lapic_setup,
};
extern int x2apic_eoi;
int x2apic_enabled = 0;
u_int32_t x2apic_readreg(int reg);
u_int32_t x2apic_cpu_number(void);
void x2apic_writereg(int reg, u_int32_t val);
void x2apic_ipi(int vec, int target, int dl);
u_int32_t i82489_readreg(int reg);
u_int32_t i82489_cpu_number(void);
void i82489_writereg(int reg, u_int32_t val);
void i82489_ipi(int vec, int target, int dl);
u_int32_t (*lapic_readreg)(int) = i82489_readreg;
void (*lapic_writereg)(int, u_int32_t) = i82489_writereg;
#ifdef MULTIPROCESSOR
void (*x86_ipi)(int vec, int target, int dl) = i82489_ipi;
#endif
u_int32_t
i82489_readreg(int reg)
{
return *((volatile u_int32_t *)(((volatile u_int8_t *)local_apic)
+ reg));
}
u_int32_t
i82489_cpu_number(void)
{
return i82489_readreg(LAPIC_ID) >> LAPIC_ID_SHIFT;
}
void
i82489_writereg(int reg, u_int32_t val)
{
*((volatile u_int32_t *)(((volatile u_int8_t *)local_apic) + reg)) =
val;
}
u_int32_t
x2apic_readreg(int reg)
{
return rdmsr(MSR_X2APIC_BASE + (reg >> 4));
}
u_int32_t
x2apic_cpu_number(void)
{
return x2apic_readreg(LAPIC_ID) & X2APIC_ID_MASK;
}
void
x2apic_writereg(int reg, u_int32_t val)
{
wrmsr(MSR_X2APIC_BASE + (reg >> 4), val);
}
#ifdef MULTIPROCESSOR
static inline void
x2apic_writeicr(u_int32_t hi, u_int32_t lo)
{
u_int32_t msr = MSR_X2APIC_BASE + (LAPIC_ICRLO >> 4);
__asm volatile("wrmsr" : : "a" (lo), "d" (hi), "c" (msr));
}
#endif
u_int32_t
lapic_cpu_number(void)
{
if (x2apic_enabled)
return x2apic_cpu_number();
return i82489_cpu_number();
}
void
lapic_map(paddr_t lapic_base)
{
pt_entry_t *pte;
vaddr_t va;
u_int64_t msr;
u_long s;
int tpr;
s = intr_disable();
tpr = lapic_tpr;
msr = rdmsr(MSR_APICBASE);
if (ISSET(msr, APICBASE_ENABLE_X2APIC) ||
(ISSET(cpu_ecxfeature, CPUIDECX_HV) &&
ISSET(cpu_ecxfeature, CPUIDECX_X2APIC))) {
/*
* On real hardware, x2apic must only be enabled if interrupt
* remapping is also enabled. See 10.12.7 of the SDM vol 3.
* On hypervisors, this is not necessary. Hypervisors can
* implement x2apic support even if the host CPU does not
* support it. Until we support interrupt remapping, use
* x2apic only if the hypervisor flag is also set or it is
* enabled by BIOS.
*/
if (!ISSET(msr, APICBASE_ENABLE_X2APIC)) {
msr |= APICBASE_ENABLE_X2APIC;
wrmsr(MSR_APICBASE, msr);
}
lapic_readreg = x2apic_readreg;
lapic_writereg = x2apic_writereg;
#ifdef MULTIPROCESSOR
x86_ipi = x2apic_ipi;
#endif
x2apic_enabled = 1;
codepatch_call(CPTAG_EOI, &x2apic_eoi);
lapic_writereg(LAPIC_TPRI, tpr);
va = (vaddr_t)&local_apic;
} else {
/*
* Map local apic.
*
* Whap the PTE "by hand" rather than calling pmap_kenter_pa
* because the latter will attempt to invoke TLB shootdown
* code just as we might have changed the value of
* cpu_number()..
*/
va = (vaddr_t)&local_apic;
pte = kvtopte(va);
*pte = lapic_base | PG_RW | PG_V | PG_N | PG_G | pg_nx;
invlpg(va);
lapic_tpr = tpr;
}
/*
* Enter the LAPIC MMIO page in the U-K page table for handling
* Meltdown (needed in the interrupt stub to acknowledge the
* incoming interrupt). On CPUs unaffected by Meltdown,
* pmap_enter_special is a no-op.
*/
pmap_enter_special(va, lapic_base, PROT_READ | PROT_WRITE);
DPRINTF("%s: entered lapic page va 0x%llx pa 0x%llx\n", __func__,
(uint64_t)va, (uint64_t)lapic_base);
intr_restore(s);
}
/*
* enable local apic
*/
void
lapic_enable(void)
{
lapic_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
}
void
lapic_disable(void)
{
lapic_writereg(LAPIC_SVR, 0);
}
void
lapic_set_lvt(void)
{
struct cpu_info *ci = curcpu();
int i;
struct mp_intr_map *mpi;
uint32_t lint0;
#ifdef MULTIPROCESSOR
if (mp_verbose) {
apic_format_redir(ci->ci_dev->dv_xname, "prelint", 0, 0,
lapic_readreg(LAPIC_LVINT0));
apic_format_redir(ci->ci_dev->dv_xname, "prelint", 1, 0,
lapic_readreg(LAPIC_LVINT1));
}
#endif
#if NIOAPIC > 0
/*
* Disable ExtINT by default when using I/O APICs.
*/
if (nioapics > 0) {
lint0 = lapic_readreg(LAPIC_LVINT0);
lint0 |= LAPIC_LVT_MASKED;
lapic_writereg(LAPIC_LVINT0, lint0);
}
#endif
if (strcmp(cpu_vendor, "AuthenticAMD") == 0) {
/*
* Detect the presence of C1E capability mostly on latest
* dual-cores (or future) k8 family. This mis-feature renders
* the local APIC timer dead, so we disable it by reading
* the Interrupt Pending Message register and clearing both
* C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
*
* Reference:
* "BIOS and Kernel Developer's Guide for AMD NPT
* Family 0Fh Processors"
* #32559 revision 3.00
*/
if (ci->ci_family == 0xf || ci->ci_family == 0x10) {
uint64_t msr;
msr = rdmsr(MSR_INT_PEN_MSG);
if (msr & (IPM_C1E_CMP_HLT|IPM_SMI_CMP_HLT)) {
msr &= ~(IPM_C1E_CMP_HLT|IPM_SMI_CMP_HLT);
wrmsr(MSR_INT_PEN_MSG, msr);
}
}
}
for (i = 0; i < mp_nintrs; i++) {
mpi = &mp_intrs[i];
if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
|| mpi->cpu_id == ci->ci_apicid)) {
#ifdef DIAGNOSTIC
if (mpi->ioapic_pin > 1)
panic("lapic_set_lvt: bad pin value %d",
mpi->ioapic_pin);
#endif
if (mpi->ioapic_pin == 0)
lapic_writereg(LAPIC_LVINT0, mpi->redir);
else
lapic_writereg(LAPIC_LVINT1, mpi->redir);
}
}
#ifdef MULTIPROCESSOR
if (mp_verbose) {
apic_format_redir(ci->ci_dev->dv_xname, "timer", 0, 0,
lapic_readreg(LAPIC_LVTT));
apic_format_redir(ci->ci_dev->dv_xname, "pcint", 0, 0,
lapic_readreg(LAPIC_PCINT));
apic_format_redir(ci->ci_dev->dv_xname, "lint", 0, 0,
lapic_readreg(LAPIC_LVINT0));
apic_format_redir(ci->ci_dev->dv_xname, "lint", 1, 0,
lapic_readreg(LAPIC_LVINT1));
apic_format_redir(ci->ci_dev->dv_xname, "err", 0, 0,
lapic_readreg(LAPIC_LVERR));
}
#endif
}
/*
* Initialize fixed idt vectors for use by local apic.
*/
void
lapic_boot_init(paddr_t lapic_base)
{
static u_int64_t clk_irq = 0;
#ifdef MULTIPROCESSOR
static u_int64_t ipi_irq = 0;
#endif
lapic_map(lapic_base);
#ifdef MULTIPROCESSOR
idt_allocmap[LAPIC_IPI_VECTOR] = 1;
idt_vec_set(LAPIC_IPI_VECTOR, Xintr_lapic_ipi);
idt_allocmap[LAPIC_IPI_INVLTLB] = 1;
idt_allocmap[LAPIC_IPI_INVLPG] = 1;
idt_allocmap[LAPIC_IPI_INVLRANGE] = 1;
if (!pmap_use_pcid) {
idt_vec_set(LAPIC_IPI_INVLTLB, Xipi_invltlb);
idt_vec_set(LAPIC_IPI_INVLPG, Xipi_invlpg);
idt_vec_set(LAPIC_IPI_INVLRANGE, Xipi_invlrange);
} else {
idt_vec_set(LAPIC_IPI_INVLTLB, Xipi_invltlb_pcid);
idt_vec_set(LAPIC_IPI_INVLPG, Xipi_invlpg_pcid);
idt_vec_set(LAPIC_IPI_INVLRANGE, Xipi_invlrange_pcid);
}
#endif
idt_allocmap[LAPIC_SPURIOUS_VECTOR] = 1;
idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
idt_allocmap[LAPIC_TIMER_VECTOR] = 1;
idt_vec_set(LAPIC_TIMER_VECTOR, Xintr_lapic_ltimer);
#if NXEN > 0
/* Xen HVM Event Channel Interrupt Vector */
idt_allocmap[LAPIC_XEN_VECTOR] = 1;
idt_vec_set(LAPIC_XEN_VECTOR, Xintr_xen_upcall);
#endif
#if NHYPERV > 0
/* Hyper-V Interrupt Vector */
idt_allocmap[LAPIC_HYPERV_VECTOR] = 1;
idt_vec_set(LAPIC_HYPERV_VECTOR, Xintr_hyperv_upcall);
#endif
evcount_attach(&clk_count, "clock", &clk_irq);
#ifdef MULTIPROCESSOR
evcount_attach(&ipi_count, "ipi", &ipi_irq);
#endif
}
static __inline u_int32_t
lapic_gettick(void)
{
return lapic_readreg(LAPIC_CCR_TIMER);
}
#include <sys/kernel.h> /* for hz */
u_int32_t lapic_tval;
/*
* this gets us up to a 4GHz busclock....
*/
u_int32_t lapic_per_second = 0;
u_int32_t lapic_frac_usec_per_cycle;
u_int64_t lapic_frac_cycle_per_usec;
u_int32_t lapic_delaytab[26];
void lapic_timer_oneshot(uint32_t, uint32_t);
void lapic_timer_periodic(uint32_t, uint32_t);
/*
* Start the local apic countdown timer.
*
* First set the mode, mask, and vector. Then set the
* divisor. Last, set the cycle count: this restarts
* the countdown.
*/
static inline void
lapic_timer_start(uint32_t mode, uint32_t mask, uint32_t cycles)
{
lapic_writereg(LAPIC_LVTT, mode | mask | LAPIC_TIMER_VECTOR);
lapic_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
lapic_writereg(LAPIC_ICR_TIMER, cycles);
}
void
lapic_timer_oneshot(uint32_t mask, uint32_t cycles)
{
lapic_timer_start(LAPIC_LVTT_TM_ONESHOT, mask, cycles);
}
void
lapic_timer_periodic(uint32_t mask, uint32_t cycles)
{
lapic_timer_start(LAPIC_LVTT_TM_PERIODIC, mask, cycles);
}
void
lapic_clockintr(void *arg, struct intrframe frame)
{
struct cpu_info *ci = curcpu();
int floor;
floor = ci->ci_handled_intr_level;
ci->ci_handled_intr_level = ci->ci_ilevel;
hardclock((struct clockframe *)&frame);
ci->ci_handled_intr_level = floor;
clk_count.ec_count++;
}
void
lapic_startclock(void)
{
lapic_timer_periodic(0, lapic_tval);
}
void
lapic_initclocks(void)
{
lapic_startclock();
i8254_inittimecounter_simple();
}
extern int gettick(void); /* XXX put in header file */
extern u_long rtclock_tval; /* XXX put in header file */
static __inline void
wait_next_cycle(void)
{
unsigned int tick, tlast;
tlast = (1 << 16); /* i8254 counter has 16 bits at most */
for (;;) {
tick = gettick();
if (tick > tlast)
return;
tlast = tick;
}
}
/*
* Calibrate the local apic count-down timer (which is running at
* bus-clock speed) vs. the i8254 counter/timer (which is running at
* a fixed rate).
*
* The Intel MP spec says: "An MP operating system may use the IRQ8
* real-time clock as a reference to determine the actual APIC timer clock
* speed."
*
* We're actually using the IRQ0 timer. Hmm.
*/
void
lapic_calibrate_timer(struct cpu_info *ci)
{
unsigned int startapic, endapic;
u_int64_t dtick, dapic, tmp;
u_long s;
int i;
if (lapic_per_second)
goto skip_calibration;
if (mp_verbose)
printf("%s: calibrating local timer\n", ci->ci_dev->dv_xname);
/*
* Configure timer to one-shot, interrupt masked,
* large positive number.
*/
lapic_timer_oneshot(LAPIC_LVTT_M, 0x80000000);
if (delay_func == i8254_delay) {
s = intr_disable();
/* wait for current cycle to finish */
wait_next_cycle();
startapic = lapic_gettick();
/* wait the next hz cycles */
for (i = 0; i < hz; i++)
wait_next_cycle();
endapic = lapic_gettick();
intr_restore(s);
dtick = hz * rtclock_tval;
dapic = startapic-endapic;
/*
* there are TIMER_FREQ ticks per second.
* in dtick ticks, there are dapic bus clocks.
*/
tmp = (TIMER_FREQ * dapic) / dtick;
lapic_per_second = tmp;
} else {
s = intr_disable();
startapic = lapic_gettick();
delay(1 * 1000 * 1000);
endapic = lapic_gettick();
intr_restore(s);
lapic_per_second = startapic - endapic;
}
skip_calibration:
printf("%s: apic clock running at %dMHz\n",
ci->ci_dev->dv_xname, lapic_per_second / (1000 * 1000));
if (lapic_per_second != 0) {
/*
* reprogram the apic timer to run in periodic mode.
* XXX need to program timer on other cpu's, too.
*/
lapic_tval = (lapic_per_second * 2) / hz;
lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
lapic_timer_periodic(LAPIC_LVTT_M, lapic_tval);
/*
* Compute fixed-point ratios between cycles and
* microseconds to avoid having to do any division
* in lapic_delay.
*/
tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
lapic_frac_usec_per_cycle = tmp;
tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;
lapic_frac_cycle_per_usec = tmp;
/*
* Compute delay in cycles for likely short delays in usec.
*/
for (i = 0; i < 26; i++)
lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
32;
/*
* Now that the timer's calibrated, use the apic timer routines
* for all our timing needs..
*/
delay_init(lapic_delay, 3000);
initclock_func = lapic_initclocks;
}
}
/*
* delay for N usec.
*/
void
lapic_delay(int usec)
{
int32_t tick, otick;
int64_t deltat; /* XXX may want to be 64bit */
otick = lapic_gettick();
if (usec <= 0)
return;
if (usec <= 25)
deltat = lapic_delaytab[usec];
else
deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
while (deltat > 0) {
tick = lapic_gettick();
if (tick > otick)
deltat -= lapic_tval - (tick - otick);
else
deltat -= otick - tick;
otick = tick;
CPU_BUSY_CYCLE();
}
}
/*
* XXX the following belong mostly or partly elsewhere..
*/
#ifdef MULTIPROCESSOR
static __inline void i82489_icr_wait(void);
static __inline void
i82489_icr_wait(void)
{
#ifdef DIAGNOSTIC
unsigned j = 100000;
#endif /* DIAGNOSTIC */
while ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
__asm volatile("pause": : :"memory");
#ifdef DIAGNOSTIC
j--;
if (j == 0)
panic("i82489_icr_wait: busy");
#endif /* DIAGNOSTIC */
}
}
void
i82489_ipi_init(int target)
{
if ((target & LAPIC_DEST_MASK) == 0)
i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );
i82489_icr_wait();
delay(10000);
i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
i82489_icr_wait();
}
void
i82489_ipi(int vec, int target, int dl)
{
int s;
s = splhigh();
i82489_icr_wait();
if ((target & LAPIC_DEST_MASK) == 0)
i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
i82489_writereg(LAPIC_ICRLO,
(target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);
i82489_icr_wait();
splx(s);
}
void
x2apic_ipi_init(int target)
{
u_int64_t hi = 0;
if ((target & LAPIC_DEST_MASK) == 0)
hi = target & 0xff;
x2apic_writeicr(hi, (target & LAPIC_DEST_MASK) | LAPIC_DLMODE_INIT |
LAPIC_LVL_ASSERT );
delay(10000);
x2apic_writeicr(0, (target & LAPIC_DEST_MASK) | LAPIC_DLMODE_INIT |
LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
}
void
x2apic_ipi(int vec, int target, int dl)
{
u_int64_t hi = 0, lo;
if ((target & LAPIC_DEST_MASK) == 0)
hi = target & 0xff;
lo = (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT;
x2apic_writeicr(hi, lo);
}
void
x86_ipi_init(int target)
{
if (x2apic_enabled)
x2apic_ipi_init(target);
else
i82489_ipi_init(target);
}
#endif /* MULTIPROCESSOR */
/*
* Using 'pin numbers' as:
* 0 - timer
* 1 - unused
* 2 - PCINT
* 3 - LVINT0
* 4 - LVINT1
* 5 - LVERR
*/
void
lapic_hwmask(struct pic *pic, int pin)
{
int reg;
u_int32_t val;
reg = LAPIC_LVTT + (pin << 4);
val = lapic_readreg(reg);
val |= LAPIC_LVT_MASKED;
lapic_writereg(reg, val);
}
void
lapic_hwunmask(struct pic *pic, int pin)
{
int reg;
u_int32_t val;
reg = LAPIC_LVTT + (pin << 4);
val = lapic_readreg(reg);
val &= ~LAPIC_LVT_MASKED;
lapic_writereg(reg, val);
}
void
lapic_setup(struct pic *pic, struct cpu_info *ci, int pin, int idtvec, int type)
{
}
475
244
286
236
236
183
310
309
77
336
335
40
40
9
9
9
154
155
150
6
106
3
104
886
886
490
315
490
888
150
150
151
150
151
149
151
150
151
151
150
151
150
151
3
37
120
151
123
116
8
5
5
3
2
2
7
122
121
60
97
23
23
19
23
120
38
54
2
1
49
2
3
47
2
48
49
3
47
48
51
102
55
118
56
56
47
2
6
51
51
51
60
57
3
53
6
14
52
58
58
58
49
13
58
57
1
14
14
12
12
12
1
13
9
1
8
8
8
8
8
8
8
38
16
1
9
4
6
25
25
38
38
13
11
17
4
3
18
1
1
15
28
12
19
28
28
24
4
2
2
2
2
8
2
2
2
10
19
3
18
2
9
7
16
7
1
8
9
1
8
9
9
16
16
1
21
2
23
23
23
1
4
5
5
40
40
40
40
40
40
40
40
40
40
40
39
40
40
15
4
11
11
11
11
11
11
11
15
9
9
7
7
2
12
1
10
2
1
8
6
11
36
36
31
5
2547
2549
2530
184
181
12
4
181
4
139
1048
163
133
48
6
4
21
7
16
19
19
58
58
58
58
58
58
55
4
1
58
11
7
2
2
9
19
19
17
3
10
7
20
20
3
3
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
/* $OpenBSD: ufs_vnops.c,v 1.156 2022/06/26 05:20:43 visa Exp $ */
/* $NetBSD: ufs_vnops.c,v 1.18 1996/05/11 18:28:04 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/dirent.h>
#include <sys/lockf.h>
#include <sys/event.h>
#include <sys/specdev.h>
#include <sys/unistd.h>
#include <miscfs/fifofs/fifo.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#ifdef UFS_DIRHASH
#include <ufs/ufs/dirhash.h>
#endif
#include <ufs/ext2fs/ext2fs_extern.h>
#include <uvm/uvm_extern.h>
int ufs_chmod(struct vnode *, int, struct ucred *);
int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *);
int filt_ufsread(struct knote *, long);
int filt_ufswrite(struct knote *, long);
int filt_ufsvnode(struct knote *, long);
void filt_ufsdetach(struct knote *);
/*
* A virgin directory (no blushing please).
*/
static struct dirtemplate mastertemplate = {
0, 12, DT_DIR, 1, ".",
0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
};
static struct odirtemplate omastertemplate = {
0, 12, 1, ".",
0, DIRBLKSIZ - 12, 2, ".."
};
/*
* Update the times in the inode
*/
void
ufs_itimes(struct vnode *vp)
{
struct inode *ip;
struct timespec ts;
ip = VTOI(vp);
if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
return;
if (vp->v_mount->mnt_flag & MNT_RDONLY)
goto out;
#ifdef EXT2FS
if (IS_EXT2_VNODE(ip->i_vnode)) {
EXT2FS_ITIMES(ip);
goto out;
}
#endif
if ((vp->v_type == VBLK || vp->v_type == VCHR) && !DOINGSOFTDEP(vp))
ip->i_flag |= IN_LAZYMOD;
else
ip->i_flag |= IN_MODIFIED;
getnanotime(&ts);
if (ip->i_flag & IN_ACCESS) {
DIP_ASSIGN(ip, atime, ts.tv_sec);
DIP_ASSIGN(ip, atimensec, ts.tv_nsec);
}
if (ip->i_flag & IN_UPDATE) {
DIP_ASSIGN(ip, mtime, ts.tv_sec);
DIP_ASSIGN(ip, mtimensec, ts.tv_nsec);
}
if (ip->i_flag & IN_CHANGE) {
DIP_ASSIGN(ip, ctime, ts.tv_sec);
DIP_ASSIGN(ip, ctimensec, ts.tv_nsec);
ip->i_modrev++;
}
out:
ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
}
/*
* Create a regular file
*/
int
ufs_create(void *v)
{
struct vop_create_args *ap = v;
int error;
error =
ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
ap->a_dvp, ap->a_vpp, ap->a_cnp);
if (error == 0)
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
return (error);
}
/*
* Mknod vnode call
*/
int
ufs_mknod(void *v)
{
struct vop_mknod_args *ap = v;
struct vattr *vap = ap->a_vap;
struct vnode **vpp = ap->a_vpp;
struct inode *ip;
int error;
if ((error =
ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
ap->a_dvp, vpp, ap->a_cnp)) != 0)
return (error);
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
ip = VTOI(*vpp);
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
if (vap->va_rdev != VNOVAL) {
/*
* Want to be able to use this to make badblock
* inodes, so don't truncate the dev number.
*/
DIP_ASSIGN(ip, rdev, vap->va_rdev);
}
/*
* Remove inode so that it will be reloaded by VFS_VGET and
* checked to see if it is an alias of an existing entry in
* the inode cache.
*/
vput(*vpp);
(*vpp)->v_type = VNON;
vgone(*vpp);
*vpp = NULL;
return (0);
}
/*
* Open called.
*
* Nothing to do.
*/
int
ufs_open(void *v)
{
struct vop_open_args *ap = v;
struct inode *ip = VTOI(ap->a_vp);
/*
* Files marked append-only must be opened for appending.
*/
if ((DIP(ip, flags) & APPEND) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
if (ap->a_mode & O_TRUNC)
ip->i_flag |= IN_CHANGE | IN_UPDATE;
return (0);
}
/*
* Close called.
*
* Update the times on the inode.
*/
int
ufs_close(void *v)
{
struct vop_close_args *ap = v;
struct vnode *vp = ap->a_vp;
if (vp->v_usecount > 1)
ufs_itimes(vp);
return (0);
}
int
ufs_access(void *v)
{
struct vop_access_args *ap = v;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
mode_t mode = ap->a_mode;
/*
* Disallow write attempts on read-only file systems;
* unless the file is a socket, fifo, or a block or
* character device resident on the file system.
*/
if (mode & VWRITE) {
switch (vp->v_type) {
int error;
case VDIR:
case VLNK:
case VREG:
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
if ((error = getinoquota(ip)) != 0)
return (error);
break;
case VBAD:
case VBLK:
case VCHR:
case VSOCK:
case VFIFO:
case VNON:
break;
}
}
/* If immutable bit set, nobody gets to write it. */
if ((mode & VWRITE) && (DIP(ip, flags) & IMMUTABLE))
return (EPERM);
if (vnoperm(vp)) {
/* For VEXEC, at least one of the execute bits must be set. */
if ((mode & VEXEC) && vp->v_type != VDIR &&
(DIP(ip, mode) & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
return EACCES;
return 0;
}
return (vaccess(vp->v_type, DIP(ip, mode), DIP(ip, uid), DIP(ip, gid),
mode, ap->a_cred));
}
int
ufs_getattr(void *v)
{
struct vop_getattr_args *ap = v;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
struct vattr *vap = ap->a_vap;
ufs_itimes(vp);
/*
* Copy from inode table
*/
vap->va_fsid = ip->i_dev;
vap->va_fileid = ip->i_number;
vap->va_mode = DIP(ip, mode) & ~IFMT;
vap->va_nlink = ip->i_effnlink;
vap->va_uid = DIP(ip, uid);
vap->va_gid = DIP(ip, gid);
vap->va_rdev = (dev_t) DIP(ip, rdev);
vap->va_size = DIP(ip, size);
vap->va_atime.tv_sec = DIP(ip, atime);
vap->va_atime.tv_nsec = DIP(ip, atimensec);
vap->va_mtime.tv_sec = DIP(ip, mtime);
vap->va_mtime.tv_nsec = DIP(ip, mtimensec);
vap->va_ctime.tv_sec = DIP(ip, ctime);
vap->va_ctime.tv_nsec = DIP(ip, ctimensec);
vap->va_flags = DIP(ip, flags);
vap->va_gen = DIP(ip, gen);
/* this doesn't belong here */
if (vp->v_type == VBLK)
vap->va_blocksize = BLKDEV_IOSIZE;
else if (vp->v_type == VCHR)
vap->va_blocksize = MAXBSIZE;
else
vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
vap->va_bytes = dbtob((u_quad_t) DIP(ip, blocks));
vap->va_type = vp->v_type;
vap->va_filerev = ip->i_modrev;
return (0);
}
/*
* Set attribute vnode op. called from several syscalls
*/
int
ufs_setattr(void *v)
{
struct vop_setattr_args *ap = v;
struct vattr *vap = ap->a_vap;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
struct ucred *cred = ap->a_cred;
int error;
long hint = NOTE_ATTRIB;
u_quad_t oldsize;
/*
* Check for unsettable attributes.
*/
if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
return (EINVAL);
}
if (vap->va_flags != VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
if (cred->cr_uid != DIP(ip, uid) &&
!vnoperm(vp) &&
(error = suser_ucred(cred)))
return (error);
if (cred->cr_uid == 0 || vnoperm(vp)) {
if ((DIP(ip, flags) & (SF_IMMUTABLE | SF_APPEND)) &&
securelevel > 0)
return (EPERM);
DIP_ASSIGN(ip, flags, vap->va_flags);
} else {
if (DIP(ip, flags) & (SF_IMMUTABLE | SF_APPEND) ||
(vap->va_flags & UF_SETTABLE) != vap->va_flags)
return (EPERM);
DIP_AND(ip, flags, SF_SETTABLE);
DIP_OR(ip, flags, vap->va_flags & UF_SETTABLE);
}
ip->i_flag |= IN_CHANGE;
if (vap->va_flags & (IMMUTABLE | APPEND))
return (0);
}
if (DIP(ip, flags) & (IMMUTABLE | APPEND))
return (EPERM);
/*
* Go through the fields and update if not VNOVAL.
*/
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred);
if (error)
return (error);
}
if (vap->va_size != VNOVAL) {
oldsize = DIP(ip, size);
/*
* Disallow write attempts on read-only file systems;
* unless the file is a socket, fifo, or a block or
* character device resident on the file system.
*/
switch (vp->v_type) {
case VDIR:
return (EISDIR);
case VLNK:
case VREG:
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
break;
default:
break;
}
if ((error = UFS_TRUNCATE(ip, vap->va_size, 0, cred)) != 0)
return (error);
if (vap->va_size < oldsize)
hint |= NOTE_TRUNCATE;
}
if ((vap->va_vaflags & VA_UTIMES_CHANGE) ||
vap->va_atime.tv_nsec != VNOVAL ||
vap->va_mtime.tv_nsec != VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
if (cred->cr_uid != DIP(ip, uid) &&
!vnoperm(vp) &&
(error = suser_ucred(cred)) &&
((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
(error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p))))
return (error);
if (vap->va_mtime.tv_nsec != VNOVAL)
ip->i_flag |= IN_CHANGE | IN_UPDATE;
else if (vap->va_vaflags & VA_UTIMES_CHANGE)
ip->i_flag |= IN_CHANGE;
if (vap->va_atime.tv_nsec != VNOVAL) {
if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
(ip->i_flag & (IN_CHANGE | IN_UPDATE)))
ip->i_flag |= IN_ACCESS;
}
ufs_itimes(vp);
if (vap->va_mtime.tv_nsec != VNOVAL) {
DIP_ASSIGN(ip, mtime, vap->va_mtime.tv_sec);
DIP_ASSIGN(ip, mtimensec, vap->va_mtime.tv_nsec);
}
if (vap->va_atime.tv_nsec != VNOVAL) {
DIP_ASSIGN(ip, atime, vap->va_atime.tv_sec);
DIP_ASSIGN(ip, atimensec, vap->va_atime.tv_nsec);
}
error = UFS_UPDATE(ip, 0);
if (error)
return (error);
}
error = 0;
if (vap->va_mode != (mode_t)VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
error = ufs_chmod(vp, (int)vap->va_mode, cred);
}
VN_KNOTE(vp, hint);
return (error);
}
/*
* Change the mode on a file.
* Inode must be locked before calling.
*/
int
ufs_chmod(struct vnode *vp, int mode, struct ucred *cred)
{
struct inode *ip = VTOI(vp);
int error;
if (cred->cr_uid != DIP(ip, uid) &&
!vnoperm(vp) &&
(error = suser_ucred(cred)))
return (error);
if (cred->cr_uid && !vnoperm(vp)) {
if (vp->v_type != VDIR && (mode & S_ISTXT))
return (EFTYPE);
if (!groupmember(DIP(ip, gid), cred) && (mode & ISGID))
return (EPERM);
}
DIP_AND(ip, mode, ~ALLPERMS);
DIP_OR(ip, mode, mode & ALLPERMS);
ip->i_flag |= IN_CHANGE;
if ((vp->v_flag & VTEXT) && (DIP(ip, mode) & S_ISTXT) == 0)
(void) uvm_vnp_uncache(vp);
return (0);
}
/*
* Perform chown operation on inode ip;
* inode must be locked prior to call.
*/
int
ufs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred)
{
struct inode *ip = VTOI(vp);
uid_t ouid;
gid_t ogid;
int error = 0;
daddr_t change;
enum ufs_quota_flags quota_flags = 0;
if (uid == (uid_t)VNOVAL)
uid = DIP(ip, uid);
if (gid == (gid_t)VNOVAL)
gid = DIP(ip, gid);
/*
* If we don't own the file, are trying to change the owner
* of the file, or are not a member of the target group,
* the caller must be superuser or the call fails.
*/
if ((cred->cr_uid != DIP(ip, uid) || uid != DIP(ip, uid) ||
(gid != DIP(ip, gid) && !groupmember(gid, cred))) &&
!vnoperm(vp) &&
(error = suser_ucred(cred)))
return (error);
ogid = DIP(ip, gid);
ouid = DIP(ip, uid);
change = DIP(ip, blocks);
if (ouid == uid)
quota_flags |= UFS_QUOTA_NOUID;
if (ogid == gid)
quota_flags |= UFS_QUOTA_NOGID;
if ((error = getinoquota(ip)) != 0)
return (error);
(void) ufs_quota_free_blocks2(ip, change, cred, quota_flags);
(void) ufs_quota_free_inode2(ip, cred, quota_flags);
(void) ufs_quota_delete(ip);
DIP_ASSIGN(ip, gid, gid);
DIP_ASSIGN(ip, uid, uid);
if ((error = getinoquota(ip)) != 0)
goto error;
if ((error = ufs_quota_alloc_blocks2(ip, change, cred,
quota_flags)) != 0)
goto error;
if ((error = ufs_quota_alloc_inode2(ip, cred ,
quota_flags)) != 0) {
(void)ufs_quota_free_blocks2(ip, change, cred,
quota_flags);
goto error;
}
if (getinoquota(ip))
panic("chown: lost quota");
if (ouid != uid || ogid != gid)
ip->i_flag |= IN_CHANGE;
if (!vnoperm(vp)) {
if (ouid != uid && cred->cr_uid != 0)
DIP_AND(ip, mode, ~ISUID);
if (ogid != gid && cred->cr_uid != 0)
DIP_AND(ip, mode, ~ISGID);
}
return (0);
error:
(void) ufs_quota_delete(ip);
DIP_ASSIGN(ip, gid, ogid);
DIP_ASSIGN(ip, uid, ouid);
if (getinoquota(ip) == 0) {
(void) ufs_quota_alloc_blocks2(ip, change, cred,
quota_flags | UFS_QUOTA_FORCE);
(void) ufs_quota_alloc_inode2(ip, cred,
quota_flags | UFS_QUOTA_FORCE);
(void) getinoquota(ip);
}
return (error);
}
/* ARGSUSED */
int
ufs_ioctl(void *v)
{
#if 0
struct vop_ioctl_args *ap = v;
#endif
return (ENOTTY);
}
int
ufs_remove(void *v)
{
struct vop_remove_args *ap = v;
struct inode *ip;
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
int error;
ip = VTOI(vp);
if (vp->v_type == VDIR || (DIP(ip, flags) & (IMMUTABLE | APPEND)) ||
(DIP(VTOI(dvp), flags) & APPEND)) {
error = EPERM;
goto out;
}
error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0);
VN_KNOTE(vp, NOTE_DELETE);
VN_KNOTE(dvp, NOTE_WRITE);
out:
if (dvp == vp)
vrele(vp);
else
vput(vp);
vput(dvp);
return (error);
}
/*
* link vnode call
*/
int
ufs_link(void *v)
{
struct vop_link_args *ap = v;
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
struct componentname *cnp = ap->a_cnp;
struct inode *ip;
struct direct newdir;
int error;
#ifdef DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("ufs_link: no name");
#endif
if (vp->v_type == VDIR) {
VOP_ABORTOP(dvp, cnp);
error = EPERM;
goto out2;
}
if (dvp->v_mount != vp->v_mount) {
VOP_ABORTOP(dvp, cnp);
error = EXDEV;
goto out2;
}
if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) {
VOP_ABORTOP(dvp, cnp);
goto out2;
}
ip = VTOI(vp);
if ((nlink_t) DIP(ip, nlink) >= LINK_MAX) {
VOP_ABORTOP(dvp, cnp);
error = EMLINK;
goto out1;
}
if (DIP(ip, flags) & (IMMUTABLE | APPEND)) {
VOP_ABORTOP(dvp, cnp);
error = EPERM;
goto out1;
}
ip->i_effnlink++;
DIP_ADD(ip, nlink, 1);
ip->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(vp))
softdep_change_linkcnt(ip, 0);
if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(vp))) == 0) {
ufs_makedirentry(ip, cnp, &newdir);
error = ufs_direnter(dvp, vp, &newdir, cnp, NULL);
}
if (error) {
ip->i_effnlink--;
DIP_ADD(ip, nlink, -1);
ip->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(vp))
softdep_change_linkcnt(ip, 0);
}
pool_put(&namei_pool, cnp->cn_pnbuf);
VN_KNOTE(vp, NOTE_LINK);
VN_KNOTE(dvp, NOTE_WRITE);
out1:
if (dvp != vp)
VOP_UNLOCK(vp);
out2:
vput(dvp);
return (error);
}
/*
* Rename system call.
* rename("foo", "bar");
* is essentially
* unlink("bar");
* link("foo", "bar");
* unlink("foo");
* but ``atomically''. Can't do full commit without saving state in the
* inode on disk which isn't feasible at this time. Best we can do is
* always guarantee the target exists.
*
* Basic algorithm is:
*
* 1) Bump link count on source while we're linking it to the
* target. This also ensure the inode won't be deleted out
* from underneath us while we work (it may be truncated by
* a concurrent `trunc' or `open' for creation).
* 2) Link source to destination. If destination already exists,
* delete it first.
* 3) Unlink source reference to inode if still around. If a
* directory was moved and the parent of the destination
* is different from the source, patch the ".." entry in the
* directory.
*/
int
ufs_rename(void *v)
{
struct vop_rename_args *ap = v;
struct vnode *tvp = ap->a_tvp;
struct vnode *tdvp = ap->a_tdvp;
struct vnode *fvp = ap->a_fvp;
struct vnode *fdvp = ap->a_fdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
struct inode *ip, *xp, *dp;
struct direct newdir;
int doingdirectory = 0, oldparent = 0, newparent = 0;
int error = 0;
#ifdef DIAGNOSTIC
if ((tcnp->cn_flags & HASBUF) == 0 ||
(fcnp->cn_flags & HASBUF) == 0)
panic("ufs_rename: no name");
#endif
/*
* Check for cross-device rename.
*/
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
abortit:
VOP_ABORTOP(tdvp, tcnp);
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
VOP_ABORTOP(fdvp, fcnp);
vrele(fdvp);
vrele(fvp);
return (error);
}
if (tvp && ((DIP(VTOI(tvp), flags) & (IMMUTABLE | APPEND)) ||
(DIP(VTOI(tdvp), flags) & APPEND))) {
error = EPERM;
goto abortit;
}
/*
* Check if just deleting a link name or if we've lost a race.
* If another process completes the same rename after we've looked
* up the source and have blocked looking up the target, then the
* source and target inodes may be identical now although the
* names were never linked.
*/
if (fvp == tvp) {
if (fvp->v_type == VDIR) {
/*
* Linked directories are impossible, so we must
* have lost the race. Pretend that the rename
* completed before the lookup.
*/
error = ENOENT;
goto abortit;
}
/* Release destination completely. */
VOP_ABORTOP(tdvp, tcnp);
vput(tdvp);
vput(tvp);
/*
* Delete source. There is another race now that everything
* is unlocked, but this doesn't cause any new complications.
* relookup() may find a file that is unrelated to the
* original one, or it may fail. Too bad.
*/
vrele(fvp);
fcnp->cn_flags &= ~MODMASK;
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
if ((fcnp->cn_flags & SAVESTART) == 0)
panic("ufs_rename: lost from startdir");
fcnp->cn_nameiop = DELETE;
if ((error = vfs_relookup(fdvp, &fvp, fcnp)) != 0)
return (error); /* relookup did vrele() */
vrele(fdvp);
return (VOP_REMOVE(fdvp, fvp, fcnp));
}
if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
goto abortit;
/* fvp, tdvp, tvp now locked */
dp = VTOI(fdvp);
ip = VTOI(fvp);
if ((nlink_t) DIP(ip, nlink) >= LINK_MAX) {
VOP_UNLOCK(fvp);
error = EMLINK;
goto abortit;
}
if ((DIP(ip, flags) & (IMMUTABLE | APPEND)) ||
(DIP(dp, flags) & APPEND)) {
VOP_UNLOCK(fvp);
error = EPERM;
goto abortit;
}
if ((DIP(ip, mode) & IFMT) == IFDIR) {
error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
if (!error && tvp)
error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
if (error) {
VOP_UNLOCK(fvp);
error = EACCES;
goto abortit;
}
/*
* Avoid ".", "..", and aliases of "." for obvious reasons.
*/
if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
dp == ip ||
(fcnp->cn_flags & ISDOTDOT) ||
(tcnp->cn_flags & ISDOTDOT) ||
(ip->i_flag & IN_RENAME)) {
VOP_UNLOCK(fvp);
error = EINVAL;
goto abortit;
}
ip->i_flag |= IN_RENAME;
oldparent = dp->i_number;
doingdirectory = 1;
}
VN_KNOTE(fdvp, NOTE_WRITE); /* XXX right place? */
/*
* When the target exists, both the directory
* and target vnodes are returned locked.
*/
dp = VTOI(tdvp);
xp = NULL;
if (tvp)
xp = VTOI(tvp);
/*
* 1) Bump link count while we're moving stuff
* around. If we crash somewhere before
* completing our work, the link count
* may be wrong, but correctable.
*/
ip->i_effnlink++;
DIP_ADD(ip, nlink, 1);
ip->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(fvp))
softdep_change_linkcnt(ip, 0);
if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(fvp))) != 0) {
VOP_UNLOCK(fvp);
goto bad;
}
/*
* If ".." must be changed (ie the directory gets a new
* parent) then the source directory must not be in the
* directory hierarchy above the target, as this would
* orphan everything below the source directory. Also
* the user must have write permission in the source so
* as to be able to change "..". We must repeat the call
* to namei, as the parent directory is unlocked by the
* call to checkpath().
*/
error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
VOP_UNLOCK(fvp);
/* tdvp and tvp locked */
if (oldparent != dp->i_number)
newparent = dp->i_number;
if (doingdirectory && newparent) {
if (error) /* write access check above */
goto bad;
if (xp != NULL)
vput(tvp);
/*
* Compensate for the reference ufs_checkpath() loses.
*/
vref(tdvp);
/* Only tdvp is locked */
if ((error = ufs_checkpath(ip, dp, tcnp->cn_cred)) != 0) {
vrele(tdvp);
goto out;
}
if ((tcnp->cn_flags & SAVESTART) == 0)
panic("ufs_rename: lost to startdir");
if ((error = vfs_relookup(tdvp, &tvp, tcnp)) != 0)
goto out;
vrele(tdvp); /* relookup() acquired a reference */
dp = VTOI(tdvp);
xp = NULL;
if (tvp)
xp = VTOI(tvp);
}
/*
* 2) If target doesn't exist, link the target
* to the source and unlink the source.
* Otherwise, rewrite the target directory
* entry to reference the source inode and
* expunge the original entry's existence.
*/
if (xp == NULL) {
if (dp->i_dev != ip->i_dev)
panic("rename: EXDEV");
/*
* Account for ".." in new directory.
* When source and destination have the same
* parent we don't fool with the link count.
*/
if (doingdirectory && newparent) {
if ((nlink_t) DIP(dp, nlink) >= LINK_MAX) {
error = EMLINK;
goto bad;
}
dp->i_effnlink++;
DIP_ADD(dp, nlink, 1);
dp->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(tdvp))
softdep_change_linkcnt(dp, 0);
if ((error = UFS_UPDATE(dp, !DOINGSOFTDEP(tdvp)))
!= 0) {
dp->i_effnlink--;
DIP_ADD(dp, nlink, -1);
dp->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(tdvp))
softdep_change_linkcnt(dp, 0);
goto bad;
}
}
ufs_makedirentry(ip, tcnp, &newdir);
if ((error = ufs_direnter(tdvp, NULL, &newdir, tcnp, NULL)) != 0) {
if (doingdirectory && newparent) {
dp->i_effnlink--;
DIP_ADD(dp, nlink, -1);
dp->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(tdvp))
softdep_change_linkcnt(dp, 0);
(void)UFS_UPDATE(dp, 1);
}
goto bad;
}
VN_KNOTE(tdvp, NOTE_WRITE);
vput(tdvp);
} else {
if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
panic("rename: EXDEV");
/*
* Short circuit rename(foo, foo).
*/
if (xp->i_number == ip->i_number)
panic("ufs_rename: same file");
/*
* If the parent directory is "sticky", then the user must
* own the parent directory, or the destination of the rename,
* otherwise the destination may not be changed (except by
* root). This implements append-only directories.
*/
if ((DIP(dp, mode) & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
tcnp->cn_cred->cr_uid != DIP(dp, uid) &&
DIP(xp, uid )!= tcnp->cn_cred->cr_uid &&
!vnoperm(tdvp)) {
error = EPERM;
goto bad;
}
/*
* Target must be empty if a directory and have no links
* to it. Also, ensure source and target are compatible
* (both directories, or both not directories).
*/
if ((DIP(xp, mode) & IFMT) == IFDIR) {
if (xp->i_effnlink > 2 ||
!ufs_dirempty(xp, dp->i_number, tcnp->cn_cred)) {
error = ENOTEMPTY;
goto bad;
}
if (!doingdirectory) {
error = ENOTDIR;
goto bad;
}
cache_purge(tdvp);
} else if (doingdirectory) {
error = EISDIR;
goto bad;
}
if ((error = ufs_dirrewrite(dp, xp, ip->i_number,
IFTODT(DIP(ip, mode)), (doingdirectory && newparent) ?
newparent : doingdirectory)) != 0)
goto bad;
if (doingdirectory) {
if (!newparent) {
dp->i_effnlink--;
if (DOINGSOFTDEP(tdvp))
softdep_change_linkcnt(dp, 0);
}
xp->i_effnlink--;
if (DOINGSOFTDEP(tvp))
softdep_change_linkcnt(xp, 0);
}
if (doingdirectory && !DOINGSOFTDEP(tvp)) {
/*
* Truncate inode. The only stuff left in the directory
* is "." and "..". The "." reference is inconsequential
* since we are quashing it. We have removed the "."
* reference and the reference in the parent directory,
* but there may be other hard links. The soft
* dependency code will arrange to do these operations
* after the parent directory entry has been deleted on
* disk, so when running with that code we avoid doing
* them now.
*/
if (!newparent) {
DIP_ADD(dp, nlink, -1);
dp->i_flag |= IN_CHANGE;
}
DIP_ADD(xp, nlink, -1);
xp->i_flag |= IN_CHANGE;
if ((error = UFS_TRUNCATE(VTOI(tvp), (off_t)0, IO_SYNC,
tcnp->cn_cred)) != 0)
goto bad;
}
VN_KNOTE(tdvp, NOTE_WRITE);
vput(tdvp);
VN_KNOTE(tvp, NOTE_DELETE);
vput(tvp);
xp = NULL;
}
/*
* 3) Unlink the source.
*/
fcnp->cn_flags &= ~MODMASK;
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
if ((fcnp->cn_flags & SAVESTART) == 0)
panic("ufs_rename: lost from startdir");
if ((error = vfs_relookup(fdvp, &fvp, fcnp)) != 0) {
vrele(ap->a_fvp);
return (error);
}
vrele(fdvp);
if (fvp == NULL) {
/*
* From name has disappeared.
*/
if (doingdirectory)
panic("ufs_rename: lost dir entry");
vrele(ap->a_fvp);
return (0);
}
xp = VTOI(fvp);
dp = VTOI(fdvp);
/*
* Ensure that the directory entry still exists and has not
* changed while the new name has been entered. If the source is
* a file then the entry may have been unlinked or renamed. In
* either case there is no further work to be done. If the source
* is a directory then it cannot have been rmdir'ed; the IN_RENAME
* flag ensures that it cannot be moved by another rename or removed
* by a rmdir.
*/
if (xp != ip) {
if (doingdirectory)
panic("ufs_rename: lost dir entry");
} else {
/*
* If the source is a directory with a
* new parent, the link count of the old
* parent directory must be decremented
* and ".." set to point to the new parent.
*/
if (doingdirectory && newparent) {
xp->i_offset = mastertemplate.dot_reclen;
ufs_dirrewrite(xp, dp, newparent, DT_DIR, 0);
cache_purge(fdvp);
}
error = ufs_dirremove(fdvp, xp, fcnp->cn_flags, 0);
xp->i_flag &= ~IN_RENAME;
}
VN_KNOTE(fvp, NOTE_RENAME);
if (dp)
vput(fdvp);
if (xp)
vput(fvp);
vrele(ap->a_fvp);
return (error);
bad:
if (xp)
vput(ITOV(xp));
vput(ITOV(dp));
out:
vrele(fdvp);
if (doingdirectory)
ip->i_flag &= ~IN_RENAME;
if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
ip->i_effnlink--;
DIP_ADD(ip, nlink, -1);
ip->i_flag |= IN_CHANGE;
ip->i_flag &= ~IN_RENAME;
if (DOINGSOFTDEP(fvp))
softdep_change_linkcnt(ip, 0);
vput(fvp);
} else
vrele(fvp);
return (error);
}
/*
* Mkdir system call
*/
int
ufs_mkdir(void *v)
{
struct vop_mkdir_args *ap = v;
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct componentname *cnp = ap->a_cnp;
struct inode *ip, *dp;
struct vnode *tvp;
struct buf *bp;
struct direct newdir;
struct dirtemplate dirtemplate, *dtp;
int error, dmode, blkoff;
#ifdef DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("ufs_mkdir: no name");
#endif
dp = VTOI(dvp);
if ((nlink_t) DIP(dp, nlink) >= LINK_MAX) {
error = EMLINK;
goto out;
}
dmode = vap->va_mode & 0777;
dmode |= IFDIR;
/*
* Must simulate part of ufs_makeinode here to acquire the inode,
* but not have it entered in the parent directory. The entry is
* made later after writing "." and ".." entries.
*/
if ((error = UFS_INODE_ALLOC(dp, dmode, cnp->cn_cred, &tvp)) != 0)
goto out;
ip = VTOI(tvp);
DIP_ASSIGN(ip, uid, cnp->cn_cred->cr_uid);
DIP_ASSIGN(ip, gid, DIP(dp, gid));
if ((error = getinoquota(ip)) ||
(error = ufs_quota_alloc_inode(ip, cnp->cn_cred))) {
pool_put(&namei_pool, cnp->cn_pnbuf);
UFS_INODE_FREE(ip, ip->i_number, dmode);
vput(tvp);
vput(dvp);
return (error);
}
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
DIP_ASSIGN(ip, mode, dmode);
tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
ip->i_effnlink = 2;
DIP_ASSIGN(ip, nlink, 2);
if (DOINGSOFTDEP(tvp))
softdep_change_linkcnt(ip, 0);
/*
* Bump link count in parent directory to reflect work done below.
* Should be done before reference is create so cleanup is
* possible if we crash.
*/
dp->i_effnlink++;
DIP_ADD(dp, nlink, 1);
dp->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(dvp))
softdep_change_linkcnt(dp, 0);
if ((error = UFS_UPDATE(dp, !DOINGSOFTDEP(dvp))) != 0)
goto bad;
/*
* Initialize directory with "." and ".." from static template.
*/
if (dp->i_ump->um_maxsymlinklen > 0)
dtp = &mastertemplate;
else
dtp = (struct dirtemplate *)&omastertemplate;
dirtemplate = *dtp;
dirtemplate.dot_ino = ip->i_number;
dirtemplate.dotdot_ino = dp->i_number;
if ((error = UFS_BUF_ALLOC(ip, (off_t)0, DIRBLKSIZ, cnp->cn_cred,
B_CLRBUF, &bp)) != 0)
goto bad;
DIP_ASSIGN(ip, size, DIRBLKSIZ);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
uvm_vnp_setsize(tvp, DIP(ip, size));
memcpy(bp->b_data, &dirtemplate, sizeof(dirtemplate));
if (DOINGSOFTDEP(tvp)) {
/*
* Ensure that the entire newly allocated block is a
* valid directory so that future growth within the
* block does not have to ensure that the block is
* written before the inode
*/
blkoff = DIRBLKSIZ;
while (blkoff < bp->b_bcount) {
((struct direct *)
(bp->b_data + blkoff))->d_reclen = DIRBLKSIZ;
blkoff += DIRBLKSIZ;
}
}
if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(tvp))) != 0) {
(void)VOP_BWRITE(bp);
goto bad;
}
/*
* Directory set up, now install its entry in the parent directory.
*
* If we are not doing soft dependencies, then we must write out the
* buffer containing the new directory body before entering the new
* name in the parent. If we are doing soft dependencies, then the
* buffer containing the new directory body will be passed to and
* released in the soft dependency code after the code has attached
* an appropriate ordering dependency to the buffer which ensures that
* the buffer is written before the new name is written in the parent.
*/
if (!DOINGSOFTDEP(dvp) && ((error = VOP_BWRITE(bp)) != 0))
goto bad;
ufs_makedirentry(ip, cnp, &newdir);
error = ufs_direnter(dvp, tvp, &newdir, cnp, bp);
bad:
if (error == 0) {
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
*ap->a_vpp = tvp;
} else {
dp->i_effnlink--;
DIP_ADD(dp, nlink, -1);
dp->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(dvp))
softdep_change_linkcnt(dp, 0);
/*
* No need to do an explicit VOP_TRUNCATE here, vrele will
* do this for us because we set the link count to 0.
*/
ip->i_effnlink = 0;
DIP_ASSIGN(ip, nlink, 0);
ip->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(tvp))
softdep_change_linkcnt(ip, 0);
vput(tvp);
}
out:
pool_put(&namei_pool, cnp->cn_pnbuf);
vput(dvp);
return (error);
}
/*
* Rmdir system call.
*/
int
ufs_rmdir(void *v)
{
struct vop_rmdir_args *ap = v;
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct inode *ip, *dp;
int error;
ip = VTOI(vp);
dp = VTOI(dvp);
/*
* Do not remove a directory that is in the process of being renamed.
* Verify the directory is empty (and valid). Rmdir ".." will not be
* valid since ".." will contain a reference to the current directory
* and thus be non-empty.
*/
error = 0;
if (ip->i_flag & IN_RENAME) {
error = EINVAL;
goto out;
}
if (ip->i_effnlink != 2 ||
!ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
error = ENOTEMPTY;
goto out;
}
if ((DIP(dp, flags) & APPEND) ||
(DIP(ip, flags) & (IMMUTABLE | APPEND))) {
error = EPERM;
goto out;
}
/*
* Delete reference to directory before purging
* inode. If we crash in between, the directory
* will be reattached to lost+found,
*/
dp->i_effnlink--;
ip->i_effnlink--;
if (DOINGSOFTDEP(vp)) {
softdep_change_linkcnt(dp, 0);
softdep_change_linkcnt(ip, 0);
}
if ((error = ufs_dirremove(dvp, ip, cnp->cn_flags, 1)) != 0) {
dp->i_effnlink++;
ip->i_effnlink++;
if (DOINGSOFTDEP(vp)) {
softdep_change_linkcnt(dp, 0);
softdep_change_linkcnt(ip, 0);
}
goto out;
}
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
cache_purge(dvp);
/*
* Truncate inode. The only stuff left in the directory is "." and
* "..". The "." reference is inconsequential since we are quashing
* it. The soft dependency code will arrange to do these operations
* after the parent directory entry has been deleted on disk, so
* when running with that code we avoid doing them now.
*/
if (!DOINGSOFTDEP(vp)) {
int ioflag;
DIP_ADD(dp, nlink, -1);
dp->i_flag |= IN_CHANGE;
DIP_ADD(ip, nlink, -1);
ip->i_flag |= IN_CHANGE;
ioflag = DOINGASYNC(vp) ? 0 : IO_SYNC;
error = UFS_TRUNCATE(ip, (off_t)0, ioflag, cnp->cn_cred);
}
cache_purge(vp);
#ifdef UFS_DIRHASH
/* Kill any active hash; i_effnlink == 0, so it will not come back. */
if (ip->i_dirhash != NULL)
ufsdirhash_free(ip);
#endif
out:
VN_KNOTE(vp, NOTE_DELETE);
vput(dvp);
vput(vp);
return (error);
}
/*
* symlink -- make a symbolic link
*/
int
ufs_symlink(void *v)
{
struct vop_symlink_args *ap = v;
struct vnode *vp, **vpp = ap->a_vpp;
struct inode *ip;
int len, error;
error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
vpp, ap->a_cnp);
if (error) {
vput(ap->a_dvp);
return (error);
}
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
vput(ap->a_dvp);
vp = *vpp;
ip = VTOI(vp);
len = strlen(ap->a_target);
if (len < ip->i_ump->um_maxsymlinklen) {
memcpy(SHORTLINK(ip), ap->a_target, len);
DIP_ASSIGN(ip, size, len);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
} else
error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL,
curproc);
vput(vp);
return (error);
}
/*
* Vnode op for reading directories.
*
* This routine converts the on-disk struct direct entries to the
* struct dirent entries expected by userland and the rest of the kernel.
*/
int
ufs_readdir(void *v)
{
struct vop_readdir_args *ap = v;
struct uio auio, *uio = ap->a_uio;
struct iovec aiov;
union {
struct dirent dn;
char __pad[roundup(sizeof(struct dirent), 8)];
} u;
off_t off = uio->uio_offset;
struct direct *dp;
char *edp;
caddr_t diskbuf;
size_t count, entries;
int bufsize, readcnt, error;
#if (BYTE_ORDER == LITTLE_ENDIAN)
int ofmt = VTOI(ap->a_vp)->i_ump->um_maxsymlinklen == 0;
#endif
if (uio->uio_rw != UIO_READ)
return (EINVAL);
count = uio->uio_resid;
entries = (uio->uio_offset + count) & (DIRBLKSIZ - 1);
/* Make sure we don't return partial entries. */
if (count <= entries)
return (EINVAL);
/*
* Convert and copy back the on-disk struct direct format to
* the user-space struct dirent format, one entry at a time
*/
/* read from disk, stopping on a block boundary, max 64kB */
readcnt = min(count, 64*1024) - entries;
auio = *uio;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = readcnt;
auio.uio_segflg = UIO_SYSSPACE;
aiov.iov_len = readcnt;
bufsize = readcnt;
diskbuf = malloc(bufsize, M_TEMP, M_WAITOK);
aiov.iov_base = diskbuf;
error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
readcnt -= auio.uio_resid;
dp = (struct direct *)diskbuf;
edp = &diskbuf[readcnt];
memset(&u, 0, sizeof(u));
/*
* While
* - we haven't failed to VOP_READ or uiomove()
* - there's space in the read buf for the head of an entry
* - that entry has a valid d_reclen, and
* - there's space for the *entire* entry
* then we're good to process this one.
*/
while (error == 0 &&
(char *)dp + offsetof(struct direct, d_name) < edp &&
dp->d_reclen > offsetof(struct direct, d_name) &&
(char *)dp + dp->d_reclen <= edp) {
u.dn.d_reclen = roundup(dp->d_namlen+1 +
offsetof(struct dirent, d_name), 8);
if (u.dn.d_reclen > uio->uio_resid)
break;
off += dp->d_reclen;
u.dn.d_off = off;
u.dn.d_fileno = dp->d_ino;
#if (BYTE_ORDER == LITTLE_ENDIAN)
if (ofmt) {
u.dn.d_type = dp->d_namlen;
u.dn.d_namlen = dp->d_type;
} else
#endif
{
u.dn.d_type = dp->d_type;
u.dn.d_namlen = dp->d_namlen;
}
memcpy(u.dn.d_name, dp->d_name, u.dn.d_namlen);
memset(u.dn.d_name + u.dn.d_namlen, 0, u.dn.d_reclen
- u.dn.d_namlen - offsetof(struct dirent, d_name));
error = uiomove(&u.dn, u.dn.d_reclen, uio);
dp = (struct direct *)((char *)dp + dp->d_reclen);
}
/*
* If there was room for an entry in what we read but its
* d_reclen is bogus, fail
*/
if ((char *)dp + offsetof(struct direct, d_name) < edp &&
dp->d_reclen <= offsetof(struct direct, d_name))
error = EIO;
free(diskbuf, M_TEMP, bufsize);
uio->uio_offset = off;
*ap->a_eofflag = DIP(VTOI(ap->a_vp), size) <= off;
return (error);
}
/*
* Return target name of a symbolic link
*/
int
ufs_readlink(void *v)
{
struct vop_readlink_args *ap = v;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
u_int64_t isize;
isize = DIP(ip, size);
if (isize < ip->i_ump->um_maxsymlinklen ||
(ip->i_ump->um_maxsymlinklen == 0 && DIP(ip, blocks) == 0)) {
return (uiomove((char *)SHORTLINK(ip), isize, ap->a_uio));
}
return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
}
/*
* Lock an inode. If its already locked, set the WANT bit and sleep.
*/
int
ufs_lock(void *v)
{
struct vop_lock_args *ap = v;
struct vnode *vp = ap->a_vp;
return rrw_enter(&VTOI(vp)->i_lock, ap->a_flags & LK_RWFLAGS);
}
/*
* Unlock an inode. If WANT bit is on, wakeup.
*/
int
ufs_unlock(void *v)
{
struct vop_unlock_args *ap = v;
struct vnode *vp = ap->a_vp;
rrw_exit(&VTOI(vp)->i_lock);
return 0;
}
/*
* Check for a locked inode.
*/
int
ufs_islocked(void *v)
{
struct vop_islocked_args *ap = v;
return rrw_status(&VTOI(ap->a_vp)->i_lock);
}
/*
* Calculate the logical to physical mapping if not done already,
* then call the device strategy routine.
*/
int
ufs_strategy(void *v)
{
struct vop_strategy_args *ap = v;
struct buf *bp = ap->a_bp;
struct vnode *vp = bp->b_vp;
struct inode *ip;
int error;
int s;
ip = VTOI(vp);
if (vp->v_type == VBLK || vp->v_type == VCHR)
panic("ufs_strategy: spec");
if (bp->b_blkno == bp->b_lblkno) {
error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno,
NULL);
if (error) {
bp->b_error = error;
bp->b_flags |= B_ERROR;
s = splbio();
biodone(bp);
splx(s);
return (error);
}
if (bp->b_blkno == -1)
clrbuf(bp);
}
if (bp->b_blkno == -1) {
s = splbio();
biodone(bp);
splx(s);
return (0);
}
vp = ip->i_devvp;
bp->b_dev = vp->v_rdev;
VOP_STRATEGY(vp, bp);
return (0);
}
/*
* Print out the contents of an inode.
*/
int
ufs_print(void *v)
{
#ifdef DIAGNOSTIC
struct vop_print_args *ap = v;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
printf("tag VT_UFS, ino %u, on dev %d, %d", ip->i_number,
major(ip->i_dev), minor(ip->i_dev));
printf(" flags 0x%x, effnlink %d, nlink %d\n",
ip->i_flag, ip->i_effnlink, DIP(ip, nlink));
printf("\tmode 0%o, owner %d, group %d, size %lld",
DIP(ip, mode), DIP(ip, uid), DIP(ip, gid), DIP(ip, size));
#ifdef FIFO
if (vp->v_type == VFIFO)
fifo_printinfo(vp);
#endif /* FIFO */
printf("\n");
#endif /* DIAGNOSTIC */
return (0);
}
/*
* Read wrapper for special devices.
*/
int
ufsspec_read(void *v)
{
struct vop_read_args *ap = v;
/*
* Set access flag.
*/
VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
return (spec_read(ap));
}
/*
* Write wrapper for special devices.
*/
int
ufsspec_write(void *v)
{
struct vop_write_args *ap = v;
/*
* Set update and change flags.
*/
VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
return (spec_write(ap));
}
/*
* Close wrapper for special devices.
*
* Update the times on the inode then do device close.
*/
int
ufsspec_close(void *v)
{
struct vop_close_args *ap = v;
struct vnode *vp = ap->a_vp;
if (vp->v_usecount > 1)
ufs_itimes(vp);
return (spec_close(ap));
}
#ifdef FIFO
/*
* Read wrapper for fifo's
*/
int
ufsfifo_read(void *v)
{
struct vop_read_args *ap = v;
/*
* Set access flag.
*/
VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
return (fifo_read(ap));
}
/*
* Write wrapper for fifo's.
*/
int
ufsfifo_write(void *v)
{
struct vop_write_args *ap = v;
/*
* Set update and change flags.
*/
VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
return (fifo_write(ap));
}
/*
* Close wrapper for fifo's.
*
* Update the times on the inode then do device close.
*/
int
ufsfifo_close(void *v)
{
struct vop_close_args *ap = v;
struct vnode *vp = ap->a_vp;
if (vp->v_usecount > 1)
ufs_itimes(vp);
return (fifo_close(ap));
}
#endif /* FIFO */
/*
* Return POSIX pathconf information applicable to ufs filesystems.
*/
int
ufs_pathconf(void *v)
{
struct vop_pathconf_args *ap = v;
int error = 0;
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
break;
case _PC_NAME_MAX:
*ap->a_retval = NAME_MAX;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
break;
case _PC_NO_TRUNC:
*ap->a_retval = 1;
break;
case _PC_ALLOC_SIZE_MIN:
*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_bsize;
break;
case _PC_FILESIZEBITS:
*ap->a_retval = 64;
break;
case _PC_REC_INCR_XFER_SIZE:
*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
break;
case _PC_REC_MAX_XFER_SIZE:
*ap->a_retval = -1; /* means ``unlimited'' */
break;
case _PC_REC_MIN_XFER_SIZE:
*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
break;
case _PC_REC_XFER_ALIGN:
*ap->a_retval = PAGE_SIZE;
break;
case _PC_SYMLINK_MAX:
*ap->a_retval = MAXPATHLEN;
break;
case _PC_2_SYMLINKS:
*ap->a_retval = 1;
break;
case _PC_TIMESTAMP_RESOLUTION:
*ap->a_retval = 1;
break;
default:
error = EINVAL;
break;
}
return (error);
}
/*
* Advisory record locking support
*/
int
ufs_advlock(void *v)
{
struct vop_advlock_args *ap = v;
struct inode *ip = VTOI(ap->a_vp);
return (lf_advlock(&ip->i_lockf, DIP(ip, size), ap->a_id, ap->a_op,
ap->a_fl, ap->a_flags));
}
/*
* Allocate a new inode.
*/
int
ufs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp)
{
struct inode *ip, *pdir;
struct direct newdir;
struct vnode *tvp;
int error;
pdir = VTOI(dvp);
#ifdef DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("ufs_makeinode: no name");
#endif
*vpp = NULL;
if ((mode & IFMT) == 0)
mode |= IFREG;
if ((error = UFS_INODE_ALLOC(pdir, mode, cnp->cn_cred, &tvp)) != 0) {
pool_put(&namei_pool, cnp->cn_pnbuf);
return (error);
}
ip = VTOI(tvp);
DIP_ASSIGN(ip, gid, DIP(pdir, gid));
DIP_ASSIGN(ip, uid, cnp->cn_cred->cr_uid);
if ((error = getinoquota(ip)) ||
(error = ufs_quota_alloc_inode(ip, cnp->cn_cred))) {
pool_put(&namei_pool, cnp->cn_pnbuf);
UFS_INODE_FREE(ip, ip->i_number, mode);
vput(tvp);
return (error);
}
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
DIP_ASSIGN(ip, mode, mode);
tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
ip->i_effnlink = 1;
DIP_ASSIGN(ip, nlink, 1);
if (DOINGSOFTDEP(tvp))
softdep_change_linkcnt(ip, 0);
if ((DIP(ip, mode) & ISGID) &&
!groupmember(DIP(ip, gid), cnp->cn_cred) &&
!vnoperm(dvp) &&
suser_ucred(cnp->cn_cred))
DIP_AND(ip, mode, ~ISGID);
/*
* Make sure inode goes to disk before directory entry.
*/
if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(tvp))) != 0)
goto bad;
ufs_makedirentry(ip, cnp, &newdir);
if ((error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL)) != 0)
goto bad;
if ((cnp->cn_flags & SAVESTART) == 0)
pool_put(&namei_pool, cnp->cn_pnbuf);
*vpp = tvp;
return (0);
bad:
/*
* Write error occurred trying to update the inode
* or the directory so must deallocate the inode.
*/
pool_put(&namei_pool, cnp->cn_pnbuf);
ip->i_effnlink = 0;
DIP_ASSIGN(ip, nlink, 0);
ip->i_flag |= IN_CHANGE;
if (DOINGSOFTDEP(tvp))
softdep_change_linkcnt(ip, 0);
tvp->v_type = VNON;
vput(tvp);
return (error);
}
const struct filterops ufsread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ufsdetach,
.f_event = filt_ufsread,
};
const struct filterops ufswrite_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ufsdetach,
.f_event = filt_ufswrite,
};
const struct filterops ufsvnode_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ufsdetach,
.f_event = filt_ufsvnode,
};
int
ufs_kqfilter(void *v)
{
struct vop_kqfilter_args *ap = v;
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &ufsread_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &ufswrite_filtops;
break;
case EVFILT_VNODE:
kn->kn_fop = &ufsvnode_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = (caddr_t)vp;
klist_insert_locked(&vp->v_selectinfo.si_note, kn);
return (0);
}
void
filt_ufsdetach(struct knote *kn)
{
struct vnode *vp = (struct vnode *)kn->kn_hook;
klist_remove_locked(&vp->v_selectinfo.si_note, kn);
}
int
filt_ufsread(struct knote *kn, long hint)
{
struct vnode *vp = (struct vnode *)kn->kn_hook;
struct inode *ip = VTOI(vp);
/*
* filesystem is gone, so set the EOF flag and schedule
* the knote for deletion.
*/
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
#ifdef EXT2FS
if (IS_EXT2_VNODE(ip->i_vnode))
kn->kn_data = ext2fs_size(ip) - foffset(kn->kn_fp);
else
#endif
kn->kn_data = DIP(ip, size) - foffset(kn->kn_fp);
if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) {
kn->kn_fflags |= NOTE_EOF;
return (1);
}
if (kn->kn_flags & (__EV_POLL | __EV_SELECT))
return (1);
return (kn->kn_data != 0);
}
int
filt_ufswrite(struct knote *kn, long hint)
{
/*
* filesystem is gone, so set the EOF flag and schedule
* the knote for deletion.
*/
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
kn->kn_data = 0;
return (1);
}
int
filt_ufsvnode(struct knote *kn, long hint)
{
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= EV_EOF;
return (1);
}
return (kn->kn_fflags != 0);
}
100
90
11
11
25
85
31
100
101
100
72
35
28
9
101
12
2
12
12
12
5
11
2
8
4
101
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/* $OpenBSD: in6_cksum.c,v 1.18 2019/04/22 22:47:49 bluhm Exp $ */
/* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1988, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
/*
* Checksum routine for Internet Protocol family headers (Portable Version).
*
* This routine is very heavily used in the network
* code and should be modified for each CPU to be as fast as possible.
*/
#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
/*
* m MUST contain a continuous IP6 header.
* off is a offset where TCP/UDP/ICMP6 header starts.
* len is a total length of a transport segment.
* (e.g. TCP header + TCP payload)
*/
int
in6_cksum(struct mbuf *m, uint8_t nxt, uint32_t off, uint32_t len)
{
uint16_t *w;
int sum = 0;
int mlen = 0;
int byte_swapped = 0;
struct ip6_hdr *ip6;
union {
uint16_t phs[4];
struct {
uint32_t ph_len;
uint8_t ph_zero[3];
uint8_t ph_nxt;
} ph __packed;
} uph;
union {
uint8_t c[2];
uint16_t s;
} s_util;
union {
uint16_t s[2];
uint32_t l;
} l_util;
/* sanity check */
if (m->m_pkthdr.len < off + len) {
panic("%s: mbuf len (%d) < off+len (%d+%d)",
__func__, m->m_pkthdr.len, off, len);
}
/* Skip pseudo-header if nxt == 0. */
if (nxt == 0)
goto skip_phdr;
bzero(&uph, sizeof(uph));
/*
* First create IP6 pseudo header and calculate a summary.
*/
ip6 = mtod(m, struct ip6_hdr *);
w = (uint16_t *)&ip6->ip6_src;
uph.ph.ph_len = htonl(len);
uph.ph.ph_nxt = nxt;
/* IPv6 source address */
sum += w[0];
if (!IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
sum += w[1];
sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
sum += w[6]; sum += w[7];
/* IPv6 destination address */
sum += w[8];
if (!IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
sum += w[9];
sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13];
sum += w[14]; sum += w[15];
/* Payload length and upper layer identifier */
sum += uph.phs[0]; sum += uph.phs[1];
sum += uph.phs[2]; sum += uph.phs[3];
skip_phdr:
/*
* Secondly calculate a summary of the first mbuf excluding offset.
*/
while (m != NULL && off > 0) {
if (m->m_len <= off)
off -= m->m_len;
else
break;
m = m->m_next;
}
if (m == NULL) {
if (off)
panic("%s: out of header, off %u", __func__, off);
goto end;
}
w = (uint16_t *)(mtod(m, uint8_t *) + off);
mlen = m->m_len - off;
if (len < mlen)
mlen = len;
len -= mlen;
/*
* Force to even boundary.
*/
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(uint8_t *)w;
w = (uint16_t *)((uint8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
goto next;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(uint8_t *)w;
next:
m = m->m_next;
/*
* Lastly calculate a summary of the rest of mbufs.
*/
for (;m && len; m = m->m_next) {
if (m->m_len == 0)
continue;
w = mtod(m, uint16_t *);
if (mlen == -1) {
/*
* The first byte of this mbuf is the continuation
* of a word spanning between this mbuf and the
* last mbuf.
*
* s_util.c[0] is already saved when scanning previous
* mbuf.
*/
s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
w = (uint16_t *)((uint8_t *)w + 1);
mlen = m->m_len - 1;
len--;
} else
mlen = m->m_len;
if (len < mlen)
mlen = len;
len -= mlen;
/*
* Force to even boundary.
*/
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(uint8_t *)w;
w = (uint16_t *)((uint8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(uint8_t *)w;
}
end:
if (len)
panic("%s: out of data, len %u", __func__, len);
if (mlen == -1) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}
6415
8467
8470
6407
6412
6
8460
8460
6410
10
39
39
189
3665
1
38
3669
3100
3095
34
4396
5667
1116
1837
7401
8463
274
275
280
2548
3
21
1
2546
2549
2549
2527
489
3
3
44
1
43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
/* $OpenBSD: kern_rwlock.c,v 1.48 2022/05/10 16:56:16 bluhm Exp $ */
/*
* Copyright (c) 2002, 2003 Artur Grabowski <art@openbsd.org>
* Copyright (c) 2011 Thordur Bjornsson <thib@secnorth.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/limits.h>
#include <sys/atomic.h>
#include <sys/witness.h>
void rw_do_exit(struct rwlock *, unsigned long);
/* XXX - temporary measure until proc0 is properly aligned */
#define RW_PROC(p) (((long)p) & ~RWLOCK_MASK)
/*
* Other OSes implement more sophisticated mechanism to determine how long the
* process attempting to acquire the lock should be spinning. We start with
* the most simple approach: we do RW_SPINS attempts at most before eventually
* giving up and putting the process to sleep queue.
*/
#define RW_SPINS 1000
#ifdef MULTIPROCESSOR
#define rw_cas(p, o, n) (atomic_cas_ulong(p, o, n) != o)
#else
static inline int
rw_cas(volatile unsigned long *p, unsigned long o, unsigned long n)
{
if (*p != o)
return (1);
*p = n;
return (0);
}
#endif
/*
* Magic wand for lock operations. Every operation checks if certain
* flags are set and if they aren't, it increments the lock with some
* value (that might need some computing in a few cases). If the operation
* fails, we need to set certain flags while waiting for the lock.
*
* RW_WRITE The lock must be completely empty. We increment it with
* RWLOCK_WRLOCK and the proc pointer of the holder.
* Sets RWLOCK_WAIT|RWLOCK_WRWANT while waiting.
* RW_READ RWLOCK_WRLOCK|RWLOCK_WRWANT may not be set. We increment
* with RWLOCK_READ_INCR. RWLOCK_WAIT while waiting.
*/
static const struct rwlock_op {
unsigned long inc;
unsigned long check;
unsigned long wait_set;
long proc_mult;
int wait_prio;
} rw_ops[] = {
{ /* RW_WRITE */
RWLOCK_WRLOCK,
ULONG_MAX,
RWLOCK_WAIT | RWLOCK_WRWANT,
1,
PLOCK - 4
},
{ /* RW_READ */
RWLOCK_READ_INCR,
RWLOCK_WRLOCK | RWLOCK_WRWANT,
RWLOCK_WAIT,
0,
PLOCK
},
{ /* Sparse Entry. */
0,
},
{ /* RW_DOWNGRADE */
RWLOCK_READ_INCR - RWLOCK_WRLOCK,
0,
0,
-1,
PLOCK
},
};
void
rw_enter_read(struct rwlock *rwl)
{
unsigned long owner = rwl->rwl_owner;
if (__predict_false((owner & (RWLOCK_WRLOCK | RWLOCK_WRWANT)) ||
rw_cas(&rwl->rwl_owner, owner, owner + RWLOCK_READ_INCR)))
rw_enter(rwl, RW_READ);
else {
membar_enter_after_atomic();
WITNESS_CHECKORDER(&rwl->rwl_lock_obj, LOP_NEWORDER, NULL);
WITNESS_LOCK(&rwl->rwl_lock_obj, 0);
}
}
void
rw_enter_write(struct rwlock *rwl)
{
struct proc *p = curproc;
if (__predict_false(rw_cas(&rwl->rwl_owner, 0,
RW_PROC(p) | RWLOCK_WRLOCK)))
rw_enter(rwl, RW_WRITE);
else {
membar_enter_after_atomic();
WITNESS_CHECKORDER(&rwl->rwl_lock_obj,
LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
WITNESS_LOCK(&rwl->rwl_lock_obj, LOP_EXCLUSIVE);
}
}
void
rw_exit_read(struct rwlock *rwl)
{
unsigned long owner;
rw_assert_rdlock(rwl);
WITNESS_UNLOCK(&rwl->rwl_lock_obj, 0);
membar_exit_before_atomic();
owner = rwl->rwl_owner;
if (__predict_false((owner & RWLOCK_WAIT) ||
rw_cas(&rwl->rwl_owner, owner, owner - RWLOCK_READ_INCR)))
rw_do_exit(rwl, 0);
}
void
rw_exit_write(struct rwlock *rwl)
{
unsigned long owner;
rw_assert_wrlock(rwl);
WITNESS_UNLOCK(&rwl->rwl_lock_obj, LOP_EXCLUSIVE);
membar_exit_before_atomic();
owner = rwl->rwl_owner;
if (__predict_false((owner & RWLOCK_WAIT) ||
rw_cas(&rwl->rwl_owner, owner, 0)))
rw_do_exit(rwl, RWLOCK_WRLOCK);
}
#ifdef DIAGNOSTIC
/*
* Put the diagnostic functions here to keep the main code free
* from ifdef clutter.
*/
static void
rw_enter_diag(struct rwlock *rwl, int flags)
{
switch (flags & RW_OPMASK) {
case RW_WRITE:
case RW_READ:
if (RW_PROC(curproc) == RW_PROC(rwl->rwl_owner))
panic("rw_enter: %s locking against myself",
rwl->rwl_name);
break;
case RW_DOWNGRADE:
/*
* If we're downgrading, we must hold the write lock.
*/
if ((rwl->rwl_owner & RWLOCK_WRLOCK) == 0)
panic("rw_enter: %s downgrade of non-write lock",
rwl->rwl_name);
if (RW_PROC(curproc) != RW_PROC(rwl->rwl_owner))
panic("rw_enter: %s downgrade, not holder",
rwl->rwl_name);
break;
default:
panic("rw_enter: unknown op 0x%x", flags);
}
}
#else
#define rw_enter_diag(r, f)
#endif
static void
_rw_init_flags_witness(struct rwlock *rwl, const char *name, int lo_flags,
const struct lock_type *type)
{
rwl->rwl_owner = 0;
rwl->rwl_name = name;
#ifdef WITNESS
rwl->rwl_lock_obj.lo_flags = lo_flags;
rwl->rwl_lock_obj.lo_name = name;
rwl->rwl_lock_obj.lo_type = type;
WITNESS_INIT(&rwl->rwl_lock_obj, type);
#else
(void)type;
(void)lo_flags;
#endif
}
void
_rw_init_flags(struct rwlock *rwl, const char *name, int flags,
const struct lock_type *type)
{
_rw_init_flags_witness(rwl, name, RWLOCK_LO_FLAGS(flags), type);
}
int
rw_enter(struct rwlock *rwl, int flags)
{
const struct rwlock_op *op;
struct sleep_state sls;
unsigned long inc, o;
#ifdef MULTIPROCESSOR
/*
* If process holds the kernel lock, then we want to give up on CPU
* as soon as possible so other processes waiting for the kernel lock
* can progress. Hence no spinning if we hold the kernel lock.
*/
unsigned int spin = (_kernel_lock_held()) ? 0 : RW_SPINS;
#endif
int error, prio;
#ifdef WITNESS
int lop_flags;
lop_flags = LOP_NEWORDER;
if (flags & RW_WRITE)
lop_flags |= LOP_EXCLUSIVE;
if (flags & RW_DUPOK)
lop_flags |= LOP_DUPOK;
if ((flags & RW_NOSLEEP) == 0 && (flags & RW_DOWNGRADE) == 0)
WITNESS_CHECKORDER(&rwl->rwl_lock_obj, lop_flags, NULL);
#endif
op = &rw_ops[(flags & RW_OPMASK) - 1];
inc = op->inc + RW_PROC(curproc) * op->proc_mult;
retry:
while (__predict_false(((o = rwl->rwl_owner) & op->check) != 0)) {
unsigned long set = o | op->wait_set;
int do_sleep;
/* Avoid deadlocks after panic or in DDB */
if (panicstr || db_active)
return (0);
#ifdef MULTIPROCESSOR
/*
* It makes sense to try to spin just in case the lock
* is acquired by writer.
*/
if ((o & RWLOCK_WRLOCK) && (spin != 0)) {
spin--;
CPU_BUSY_CYCLE();
continue;
}
#endif
rw_enter_diag(rwl, flags);
if (flags & RW_NOSLEEP)
return (EBUSY);
prio = op->wait_prio;
if (flags & RW_INTR)
prio |= PCATCH;
sleep_setup(&sls, rwl, prio, rwl->rwl_name, 0);
do_sleep = !rw_cas(&rwl->rwl_owner, o, set);
error = sleep_finish(&sls, do_sleep);
if ((flags & RW_INTR) &&
(error != 0))
return (error);
if (flags & RW_SLEEPFAIL)
return (EAGAIN);
}
if (__predict_false(rw_cas(&rwl->rwl_owner, o, o + inc)))
goto retry;
membar_enter_after_atomic();
/*
* If old lock had RWLOCK_WAIT and RWLOCK_WRLOCK set, it means we
* downgraded a write lock and had possible read waiter, wake them
* to let them retry the lock.
*/
if (__predict_false((o & (RWLOCK_WRLOCK|RWLOCK_WAIT)) ==
(RWLOCK_WRLOCK|RWLOCK_WAIT)))
wakeup(rwl);
if (flags & RW_DOWNGRADE)
WITNESS_DOWNGRADE(&rwl->rwl_lock_obj, lop_flags);
else
WITNESS_LOCK(&rwl->rwl_lock_obj, lop_flags);
return (0);
}
void
rw_exit(struct rwlock *rwl)
{
unsigned long wrlock;
/* Avoid deadlocks after panic or in DDB */
if (panicstr || db_active)
return;
wrlock = rwl->rwl_owner & RWLOCK_WRLOCK;
if (wrlock)
rw_assert_wrlock(rwl);
else
rw_assert_rdlock(rwl);
WITNESS_UNLOCK(&rwl->rwl_lock_obj, wrlock ? LOP_EXCLUSIVE : 0);
membar_exit_before_atomic();
rw_do_exit(rwl, wrlock);
}
/* membar_exit_before_atomic() has to precede call of this function. */
void
rw_do_exit(struct rwlock *rwl, unsigned long wrlock)
{
unsigned long owner, set;
do {
owner = rwl->rwl_owner;
if (wrlock)
set = 0;
else
set = (owner - RWLOCK_READ_INCR) &
~(RWLOCK_WAIT|RWLOCK_WRWANT);
/*
* Potential MP race here. If the owner had WRWANT set, we
* cleared it and a reader can sneak in before a writer.
*/
} while (__predict_false(rw_cas(&rwl->rwl_owner, owner, set)));
if (owner & RWLOCK_WAIT)
wakeup(rwl);
}
int
rw_status(struct rwlock *rwl)
{
unsigned long owner = rwl->rwl_owner;
if (owner & RWLOCK_WRLOCK) {
if (RW_PROC(curproc) == RW_PROC(owner))
return RW_WRITE;
else
return RW_WRITE_OTHER;
}
if (owner)
return RW_READ;
return (0);
}
#ifdef DIAGNOSTIC
void
rw_assert_wrlock(struct rwlock *rwl)
{
if (panicstr || db_active)
return;
#ifdef WITNESS
witness_assert(&rwl->rwl_lock_obj, LA_XLOCKED);
#else
if (!(rwl->rwl_owner & RWLOCK_WRLOCK))
panic("%s: lock not held", rwl->rwl_name);
if (RW_PROC(curproc) != RW_PROC(rwl->rwl_owner))
panic("%s: lock not held by this process", rwl->rwl_name);
#endif
}
void
rw_assert_rdlock(struct rwlock *rwl)
{
if (panicstr || db_active)
return;
#ifdef WITNESS
witness_assert(&rwl->rwl_lock_obj, LA_SLOCKED);
#else
if (!RW_PROC(rwl->rwl_owner) || (rwl->rwl_owner & RWLOCK_WRLOCK))
panic("%s: lock not shared", rwl->rwl_name);
#endif
}
void
rw_assert_anylock(struct rwlock *rwl)
{
if (panicstr || db_active)
return;
#ifdef WITNESS
witness_assert(&rwl->rwl_lock_obj, LA_LOCKED);
#else
switch (rw_status(rwl)) {
case RW_WRITE_OTHER:
panic("%s: lock held by different process", rwl->rwl_name);
case 0:
panic("%s: lock not held", rwl->rwl_name);
}
#endif
}
void
rw_assert_unlocked(struct rwlock *rwl)
{
if (panicstr || db_active)
return;
#ifdef WITNESS
witness_assert(&rwl->rwl_lock_obj, LA_UNLOCKED);
#else
if (RW_PROC(curproc) == RW_PROC(rwl->rwl_owner))
panic("%s: lock held", rwl->rwl_name);
#endif
}
#endif
/* recursive rwlocks; */
void
_rrw_init_flags(struct rrwlock *rrwl, const char *name, int flags,
const struct lock_type *type)
{
memset(rrwl, 0, sizeof(struct rrwlock));
_rw_init_flags_witness(&rrwl->rrwl_lock, name, RRWLOCK_LO_FLAGS(flags),
type);
}
int
rrw_enter(struct rrwlock *rrwl, int flags)
{
int rv;
if (RW_PROC(rrwl->rrwl_lock.rwl_owner) == RW_PROC(curproc)) {
if (flags & RW_RECURSEFAIL)
return (EDEADLK);
else {
rrwl->rrwl_wcnt++;
WITNESS_LOCK(&rrwl->rrwl_lock.rwl_lock_obj,
LOP_EXCLUSIVE);
return (0);
}
}
rv = rw_enter(&rrwl->rrwl_lock, flags);
if (rv == 0)
rrwl->rrwl_wcnt = 1;
return (rv);
}
void
rrw_exit(struct rrwlock *rrwl)
{
if (RW_PROC(rrwl->rrwl_lock.rwl_owner) == RW_PROC(curproc)) {
KASSERT(rrwl->rrwl_wcnt > 0);
rrwl->rrwl_wcnt--;
if (rrwl->rrwl_wcnt != 0) {
WITNESS_UNLOCK(&rrwl->rrwl_lock.rwl_lock_obj,
LOP_EXCLUSIVE);
return;
}
}
rw_exit(&rrwl->rrwl_lock);
}
int
rrw_status(struct rrwlock *rrwl)
{
return (rw_status(&rrwl->rrwl_lock));
}
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andrew Doran.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define RWLOCK_OBJ_MAGIC 0x5aa3c85d
struct rwlock_obj {
struct rwlock ro_lock;
u_int ro_magic;
u_int ro_refcnt;
};
struct pool rwlock_obj_pool;
/*
* rw_obj_init:
*
* Initialize the mutex object store.
*/
void
rw_obj_init(void)
{
pool_init(&rwlock_obj_pool, sizeof(struct rwlock_obj), 0, IPL_MPFLOOR,
PR_WAITOK, "rwobjpl", NULL);
}
/*
* rw_obj_alloc:
*
* Allocate a single lock object.
*/
void
_rw_obj_alloc_flags(struct rwlock **lock, const char *name, int flags,
struct lock_type *type)
{
struct rwlock_obj *mo;
mo = pool_get(&rwlock_obj_pool, PR_WAITOK);
mo->ro_magic = RWLOCK_OBJ_MAGIC;
_rw_init_flags(&mo->ro_lock, name, flags, type);
mo->ro_refcnt = 1;
*lock = &mo->ro_lock;
}
/*
* rw_obj_hold:
*
* Add a single reference to a lock object. A reference to the object
* must already be held, and must be held across this call.
*/
void
rw_obj_hold(struct rwlock *lock)
{
struct rwlock_obj *mo = (struct rwlock_obj *)lock;
KASSERTMSG(mo->ro_magic == RWLOCK_OBJ_MAGIC,
"%s: lock %p: mo->ro_magic (%#x) != RWLOCK_OBJ_MAGIC (%#x)",
__func__, mo, mo->ro_magic, RWLOCK_OBJ_MAGIC);
KASSERTMSG(mo->ro_refcnt > 0,
"%s: lock %p: mo->ro_refcnt (%#x) == 0",
__func__, mo, mo->ro_refcnt);
atomic_inc_int(&mo->ro_refcnt);
}
/*
* rw_obj_free:
*
* Drop a reference from a lock object. If the last reference is being
* dropped, free the object and return true. Otherwise, return false.
*/
int
rw_obj_free(struct rwlock *lock)
{
struct rwlock_obj *mo = (struct rwlock_obj *)lock;
KASSERTMSG(mo->ro_magic == RWLOCK_OBJ_MAGIC,
"%s: lock %p: mo->ro_magic (%#x) != RWLOCK_OBJ_MAGIC (%#x)",
__func__, mo, mo->ro_magic, RWLOCK_OBJ_MAGIC);
KASSERTMSG(mo->ro_refcnt > 0,
"%s: lock %p: mo->ro_refcnt (%#x) == 0",
__func__, mo, mo->ro_refcnt);
if (atomic_dec_int_nv(&mo->ro_refcnt) > 0) {
return false;
}
#if notyet
WITNESS_DESTROY(&mo->ro_lock);
#endif
pool_put(&rwlock_obj_pool, mo);
return true;
}
5
5
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* $OpenBSD: uvm_io.c,v 1.29 2022/03/12 08:11:07 mpi Exp $ */
/* $NetBSD: uvm_io.c,v 1.12 2000/06/27 17:29:23 mrg Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* from: Id: uvm_io.c,v 1.1.2.2 1997/12/30 12:02:00 mrg Exp
*/
/*
* uvm_io.c: uvm i/o ops
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <uvm/uvm.h>
/*
* functions
*/
/*
* uvm_io: perform I/O on a map
*
* => caller must have a reference to "map" so that it doesn't go away
* while we are working.
*/
int
uvm_io(vm_map_t map, struct uio *uio, int flags)
{
vaddr_t baseva, endva, pageoffset, kva;
vsize_t chunksz, togo, sz;
struct uvm_map_deadq dead_entries;
int error, extractflags;
/*
* step 0: sanity checks and set up for copy loop. start with a
* large chunk size. if we have trouble finding vm space we will
* reduce it.
*/
if (uio->uio_resid == 0)
return(0);
togo = uio->uio_resid;
baseva = (vaddr_t) uio->uio_offset;
endva = baseva + (togo - 1);
if (endva < baseva) /* wrap around? */
return(EIO);
if (baseva >= VM_MAXUSER_ADDRESS)
return(0);
if (endva >= VM_MAXUSER_ADDRESS)
/* EOF truncate */
togo = togo - (endva - VM_MAXUSER_ADDRESS + 1);
pageoffset = baseva & PAGE_MASK;
baseva = trunc_page(baseva);
chunksz = min(round_page(togo + pageoffset), MAXBSIZE);
error = 0;
extractflags = 0;
if (flags & UVM_IO_FIXPROT)
extractflags |= UVM_EXTRACT_FIXPROT;
/*
* step 1: main loop... while we've got data to move
*/
for (/*null*/; togo > 0 ; pageoffset = 0) {
/*
* step 2: extract mappings from the map into kernel_map
*/
error = uvm_map_extract(map, baseva, chunksz, &kva,
extractflags);
if (error) {
/* retry with a smaller chunk... */
if (error == ENOMEM && chunksz > PAGE_SIZE) {
chunksz = trunc_page(chunksz / 2);
if (chunksz < PAGE_SIZE)
chunksz = PAGE_SIZE;
continue;
}
break;
}
/*
* step 3: move a chunk of data
*/
sz = chunksz - pageoffset;
if (sz > togo)
sz = togo;
error = uiomove((caddr_t) (kva + pageoffset), sz, uio);
togo -= sz;
baseva += chunksz;
/*
* step 4: unmap the area of kernel memory
*/
vm_map_lock(kernel_map);
TAILQ_INIT(&dead_entries);
uvm_unmap_remove(kernel_map, kva, kva+chunksz,
&dead_entries, FALSE, TRUE);
vm_map_unlock(kernel_map);
uvm_unmap_detach(&dead_entries, AMAP_REFALL);
if (error)
break;
}
return (error);
}
4
6
5
5
2
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* $OpenBSD: clock_subr.c,v 1.6 2016/08/26 07:09:56 guenther Exp $ */
/* $NetBSD: clock_subr.c,v 1.3 1997/03/15 18:11:16 is Exp $ */
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: clock.c 1.18 91/01/21$
*
* @(#)clock.c 8.2 (Berkeley) 1/12/94
*/
/*
* Generic routines to convert between a POSIX date
* (seconds since 1/1/1970) and yr/mo/day/hr/min/sec
* Derived from arch/hp300/hp300/clock.c
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/systm.h>
static inline int leapyear(int year);
#define FEBRUARY 2
#define days_in_year(a) (leapyear(a) ? 366 : 365)
#define days_in_month(a) (month_days[(a) - 1])
static const int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
/*
* This inline avoids some unnecessary modulo operations
* as compared with the usual macro:
* ( ((year % 4) == 0 &&
* (year % 100) != 0) ||
* ((year % 400) == 0) )
* It is otherwise equivalent.
*/
static inline int
leapyear(int year)
{
int rv = 0;
if ((year & 3) == 0) {
rv = 1;
if ((year % 100) == 0) {
rv = 0;
if ((year % 400) == 0)
rv = 1;
}
}
return (rv);
}
time_t
clock_ymdhms_to_secs(struct clock_ymdhms *dt)
{
time_t secs;
int i, year, days;
year = dt->dt_year;
/*
* Compute days since start of time.
* First from years, then from months.
*/
days = 0;
for (i = POSIX_BASE_YEAR; i < year; i++)
days += days_in_year(i);
if (leapyear(year) && dt->dt_mon > FEBRUARY)
days++;
/* Months */
for (i = 1; i < dt->dt_mon; i++)
days += days_in_month(i);
days += (dt->dt_day - 1);
/* Add hours, minutes, seconds. */
secs = (time_t)((days
* 24 + dt->dt_hour)
* 60 + dt->dt_min)
* 60 + dt->dt_sec;
return (secs);
}
/* This function uses a copy of month_days[] */
#undef days_in_month
#define days_in_month(a) (mthdays[(a) - 1])
void
clock_secs_to_ymdhms(time_t secs, struct clock_ymdhms *dt)
{
int mthdays[12];
int i, days;
int rsec; /* remainder seconds */
memcpy(mthdays, month_days, sizeof(mthdays));
days = secs / SECDAY;
rsec = secs % SECDAY;
/* Day of week (Note: 1/1/1970 was a Thursday) */
dt->dt_wday = (days + 4) % 7;
/* Subtract out whole years, counting them in i. */
for (i = POSIX_BASE_YEAR; days >= days_in_year(i); i++)
days -= days_in_year(i);
dt->dt_year = i;
/* Subtract out whole months, counting them in i. */
if (leapyear(i))
days_in_month(FEBRUARY) = 29;
for (i = 1; days >= days_in_month(i); i++)
days -= days_in_month(i);
dt->dt_mon = i;
/* Days are what is left over (+1) from all that. */
dt->dt_day = days + 1;
/* Hours, minutes, seconds are easy */
dt->dt_hour = rsec / 3600;
rsec = rsec % 3600;
dt->dt_min = rsec / 60;
rsec = rsec % 60;
dt->dt_sec = rsec;
}
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
/* $OpenBSD: cd.c,v 1.266 2022/09/01 13:45:27 krw Exp $ */
/* $NetBSD: cd.c,v 1.100 1997/04/02 02:29:30 mycroft Exp $ */
/*
* Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Originally written by Julian Elischer (julian@tfs.com)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
*
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
*
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/cdio.h>
#include <sys/conf.h>
#include <sys/scsiio.h>
#include <sys/dkio.h>
#include <sys/vnode.h>
#include <scsi/scsi_all.h>
#include <scsi/cd.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsi_disk.h> /* rw_10 and start_stop come from there */
#include <scsi/scsiconf.h>
#include <ufs/ffs/fs.h> /* for BBSIZE and SBSIZE */
#define CDOUTSTANDING 4
#define MAXTRACK 99
#define CD_FRAMES 75
#define CD_SECS 60
struct cd_toc {
struct ioc_toc_header header;
struct cd_toc_entry entries[MAXTRACK+1]; /* One extra for the */
/* leadout */
};
int cdmatch(struct device *, void *, void *);
void cdattach(struct device *, struct device *, void *);
int cdactivate(struct device *, int);
int cddetach(struct device *, int);
struct cd_softc {
struct device sc_dev;
struct disk sc_dk;
int sc_flags;
#define CDF_DYING 0x40 /* dying, when deactivated */
struct scsi_link *sc_link; /* contains targ, lun, etc. */
struct cd_parms {
u_int32_t secsize;
u_int64_t disksize; /* total number sectors */
} params;
struct bufq sc_bufq;
struct scsi_xshandler sc_xsh;
};
void cdstart(struct scsi_xfer *);
void cd_buf_done(struct scsi_xfer *);
int cd_cmd_rw6(struct scsi_generic *, int, u_int64_t, u_int32_t);
int cd_cmd_rw10(struct scsi_generic *, int, u_int64_t, u_int32_t);
int cd_cmd_rw12(struct scsi_generic *, int, u_int64_t, u_int32_t);
void cdminphys(struct buf *);
int cdgetdisklabel(dev_t, struct cd_softc *, struct disklabel *, int);
int cd_setchan(struct cd_softc *, int, int, int, int, int);
int cd_getvol(struct cd_softc *cd, struct ioc_vol *, int);
int cd_setvol(struct cd_softc *, const struct ioc_vol *, int);
int cd_load_unload(struct cd_softc *, int, int);
int cd_set_pa_immed(struct cd_softc *, int);
int cd_play(struct cd_softc *, int, int);
int cd_play_tracks(struct cd_softc *, int, int, int, int);
int cd_play_msf(struct cd_softc *, int, int, int, int, int, int);
int cd_pause(struct cd_softc *, int);
int cd_reset(struct cd_softc *);
int cd_read_subchannel(struct cd_softc *, int, int, int,
struct cd_sub_channel_info *, int );
int cd_read_toc(struct cd_softc *, int, int, void *, int, int);
int cd_get_parms(struct cd_softc *, int);
int cd_load_toc(struct cd_softc *, struct cd_toc *, int);
int cd_interpret_sense(struct scsi_xfer *);
u_int64_t cd_size(struct scsi_link *, int, u_int32_t *);
int dvd_auth(struct cd_softc *, union dvd_authinfo *);
int dvd_read_physical(struct cd_softc *, union dvd_struct *);
int dvd_read_copyright(struct cd_softc *, union dvd_struct *);
int dvd_read_disckey(struct cd_softc *, union dvd_struct *);
int dvd_read_bca(struct cd_softc *, union dvd_struct *);
int dvd_read_manufact(struct cd_softc *, union dvd_struct *);
int dvd_read_struct(struct cd_softc *, union dvd_struct *);
#if defined(__macppc__)
int cd_eject(void);
#endif /* __macppc__ */
const struct cfattach cd_ca = {
sizeof(struct cd_softc), cdmatch, cdattach,
cddetach, cdactivate
};
struct cfdriver cd_cd = {
NULL, "cd", DV_DISK
};
const struct scsi_inquiry_pattern cd_patterns[] = {
{T_CDROM, T_REMOV,
"", "", ""},
{T_CDROM, T_FIXED,
"", "", ""},
{T_WORM, T_REMOV,
"", "", ""},
{T_WORM, T_FIXED,
"", "", ""},
{T_DIRECT, T_REMOV,
"NEC CD-ROM DRIVE:260", "", ""},
#if 0
{T_CDROM, T_REMOV, /* more luns */
"PIONEER ", "CD-ROM DRM-600 ", ""},
#endif /* 0 */
};
#define cdlookup(unit) (struct cd_softc *)disk_lookup(&cd_cd, (unit))
int
cdmatch(struct device *parent, void *match, void *aux)
{
struct scsi_attach_args *sa = aux;
struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
int priority;
scsi_inqmatch(inq, cd_patterns, nitems(cd_patterns),
sizeof(cd_patterns[0]), &priority);
return priority;
}
/*
* The routine called by the low level scsi routine when it discovers
* A device suitable for this driver
*/
void
cdattach(struct device *parent, struct device *self, void *aux)
{
struct cd_softc *sc = (struct cd_softc *)self;
struct scsi_attach_args *sa = aux;
struct scsi_link *link = sa->sa_sc_link;
SC_DEBUG(link, SDEV_DB2, ("cdattach:\n"));
/*
* Store information needed to contact our base driver
*/
sc->sc_link = link;
link->interpret_sense = cd_interpret_sense;
link->device_softc = sc;
if (link->openings > CDOUTSTANDING)
link->openings = CDOUTSTANDING;
/*
* Initialize disk structures.
*/
sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
bufq_init(&sc->sc_bufq, BUFQ_DEFAULT);
printf("\n");
scsi_xsh_set(&sc->sc_xsh, link, cdstart);
/* Attach disk. */
sc->sc_dk.dk_flags = DKF_NOLABELREAD;
disk_attach(&sc->sc_dev, &sc->sc_dk);
}
int
cdactivate(struct device *self, int act)
{
struct cd_softc *sc = (struct cd_softc *)self;
switch (act) {
case DVACT_RESUME:
/*
* When resuming, hardware may have forgotten we locked it. So
* if there are any open partitions, lock the CD.
*/
if (sc->sc_dk.dk_openmask != 0)
scsi_prevent(sc->sc_link, PR_PREVENT,
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE |
SCSI_SILENT | SCSI_AUTOCONF);
break;
case DVACT_DEACTIVATE:
SET(sc->sc_flags, CDF_DYING);
scsi_xsh_del(&sc->sc_xsh);
break;
}
return 0;
}
int
cddetach(struct device *self, int flags)
{
struct cd_softc *sc = (struct cd_softc *)self;
bufq_drain(&sc->sc_bufq);
disk_gone(cdopen, self->dv_unit);
/* Detach disk. */
bufq_destroy(&sc->sc_bufq);
disk_detach(&sc->sc_dk);
return 0;
}
/*
* Open the device. Make sure the partition info is as up-to-date as can be.
*/
int
cdopen(dev_t dev, int flag, int fmt, struct proc *p)
{
struct scsi_link *link;
struct cd_softc *sc;
int error = 0, part, rawopen, unit;
unit = DISKUNIT(dev);
part = DISKPART(dev);
rawopen = (part == RAW_PART) && (fmt == S_IFCHR);
sc = cdlookup(unit);
if (sc == NULL)
return ENXIO;
if (ISSET(sc->sc_flags, CDF_DYING)) {
device_unref(&sc->sc_dev);
return ENXIO;
}
link = sc->sc_link;
SC_DEBUG(link, SDEV_DB1,
("cdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
cd_cd.cd_ndevs, part));
if ((error = disk_lock(&sc->sc_dk)) != 0) {
device_unref(&sc->sc_dev);
return error;
}
if (sc->sc_dk.dk_openmask != 0) {
/*
* If any partition is open, but the disk has been invalidated,
* disallow further opens.
*/
if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
if (rawopen)
goto out;
error = EIO;
goto bad;
}
} else {
/*
* Check that it is still responding and ok. Drive can be in
* progress of loading media so use increased retries number
* and don't ignore NOT_READY.
*/
/* Use cd_interpret_sense() now. */
SET(link->flags, SDEV_OPEN);
error = scsi_test_unit_ready(link, TEST_READY_RETRIES,
(rawopen ? SCSI_SILENT : 0) | SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE);
/* Start the cd spinning if necessary. */
if (error == EIO)
error = scsi_start(link, SSS_START,
SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT);
if (error) {
if (rawopen) {
error = 0;
goto out;
} else
goto bad;
}
/* Lock the cd in. */
error = scsi_prevent(link, PR_PREVENT,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE |
SCSI_SILENT);
if (error)
goto bad;
/* Load the physical device parameters. */
SET(link->flags, SDEV_MEDIA_LOADED);
if (cd_get_parms(sc, (rawopen ? SCSI_SILENT : 0) |
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE)) {
CLR(link->flags, SDEV_MEDIA_LOADED);
error = ENXIO;
goto bad;
}
SC_DEBUG(link, SDEV_DB3, ("Params loaded\n"));
/* Fabricate a disk label. */
cdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0);
SC_DEBUG(link, SDEV_DB3, ("Disklabel fabricated\n"));
}
out:
if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
goto bad;
SET(link->flags, SDEV_OPEN);
SC_DEBUG(link, SDEV_DB3, ("open complete\n"));
/* It's OK to fall through because dk_openmask is now non-zero. */
bad:
if (sc->sc_dk.dk_openmask == 0) {
scsi_prevent(link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE |
SCSI_SILENT);
CLR(link->flags, SDEV_OPEN | SDEV_MEDIA_LOADED);
}
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return error;
}
/*
* Close the device. Only called if we are the last occurrence of an open
* device.
*/
int
cdclose(dev_t dev, int flag, int fmt, struct proc *p)
{
struct cd_softc *sc;
int part = DISKPART(dev);
sc = cdlookup(DISKUNIT(dev));
if (sc == NULL)
return ENXIO;
if (ISSET(sc->sc_flags, CDF_DYING)) {
device_unref(&sc->sc_dev);
return ENXIO;
}
disk_lock_nointr(&sc->sc_dk);
disk_closepart(&sc->sc_dk, part, fmt);
if (sc->sc_dk.dk_openmask == 0) {
/* XXXX Must wait for I/O to complete! */
scsi_prevent(sc->sc_link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY |
SCSI_SILENT);
CLR(sc->sc_link->flags, SDEV_OPEN | SDEV_MEDIA_LOADED);
if (ISSET(sc->sc_link->flags, SDEV_EJECTING)) {
scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
CLR(sc->sc_link->flags, SDEV_EJECTING);
}
scsi_xsh_del(&sc->sc_xsh);
}
disk_unlock(&sc->sc_dk);
device_unref(&sc->sc_dev);
return 0;
}
/*
* Actually translate the requested transfer into one the physical driver can
* understand. The transfer is described by a buf and will include only one
* physical transfer.
*/
void
cdstrategy(struct buf *bp)
{
struct cd_softc *sc;
int s;
sc = cdlookup(DISKUNIT(bp->b_dev));
if (sc == NULL) {
bp->b_error = ENXIO;
goto bad;
}
if (ISSET(sc->sc_flags, CDF_DYING)) {
bp->b_error = ENXIO;
goto bad;
}
SC_DEBUG(sc->sc_link, SDEV_DB2, ("cdstrategy: %ld bytes @ blk %lld\n",
bp->b_bcount, (long long)bp->b_blkno));
/*
* If the device has been made invalid, error out
* maybe the media changed, or no media loaded
*/
if (!ISSET(sc->sc_link->flags, SDEV_MEDIA_LOADED)) {
bp->b_error = EIO;
goto bad;
}
/* Validate the request. */
if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
goto done;
/* Place it in the queue of disk activities for this disk. */
bufq_queue(&sc->sc_bufq, bp);
/*
* Tell the device to get going on the transfer if it's
* not doing anything, otherwise just wait for completion
*/
scsi_xsh_add(&sc->sc_xsh);
device_unref(&sc->sc_dev);
return;
bad:
SET(bp->b_flags, B_ERROR);
bp->b_resid = bp->b_bcount;
done:
s = splbio();
biodone(bp);
splx(s);
if (sc != NULL)
device_unref(&sc->sc_dev);
}
int
cd_cmd_rw6(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw *cmd = (struct scsi_rw *)generic;
cmd->opcode = read ? READ_COMMAND : WRITE_COMMAND;
_lto3b(secno, cmd->addr);
cmd->length = nsecs & 0xff;
return sizeof(*cmd);
}
int
cd_cmd_rw10(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw_10 *cmd = (struct scsi_rw_10 *)generic;
cmd->opcode = read ? READ_10 : WRITE_10;
_lto4b(secno, cmd->addr);
_lto2b(nsecs, cmd->length);
return sizeof(*cmd);
}
int
cd_cmd_rw12(struct scsi_generic *generic, int read, u_int64_t secno,
u_int32_t nsecs)
{
struct scsi_rw_12 *cmd = (struct scsi_rw_12 *)generic;
cmd->opcode = read ? READ_12 : WRITE_12;
_lto4b(secno, cmd->addr);
_lto4b(nsecs, cmd->length);
return sizeof(*cmd);
}
/*
* cdstart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
* It dequeues the buf and creates a scsi command to perform the
* transfer in the buf. The transfer request will call scsi_done
* on completion, which will in turn call this routine again
* so that the next queued transfer is performed.
* The bufs are queued by the strategy routine (cdstrategy)
*
* This routine is also called after other non-queued requests
* have been made of the scsi driver, to ensure that the queue
* continues to be drained.
*
* must be called at the correct (highish) spl level
* cdstart() is called at splbio from cdstrategy and scsi_done
*/
void
cdstart(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct cd_softc *sc = link->device_softc;
struct buf *bp;
struct partition *p;
u_int64_t secno;
u_int32_t nsecs;
int read;
SC_DEBUG(link, SDEV_DB2, ("cdstart\n"));
if (ISSET(sc->sc_flags, CDF_DYING)) {
scsi_xs_put(xs);
return;
}
if (!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
bufq_drain(&sc->sc_bufq);
scsi_xs_put(xs);
return;
}
bp = bufq_dequeue(&sc->sc_bufq);
if (bp == NULL) {
scsi_xs_put(xs);
return;
}
read = ISSET(bp->b_flags, B_READ);
SET(xs->flags, (read ? SCSI_DATA_IN : SCSI_DATA_OUT));
xs->timeout = 30000;
xs->data = bp->b_data;
xs->datalen = bp->b_bcount;
xs->done = cd_buf_done;
xs->cookie = bp;
xs->bp = bp;
p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
secno = DL_GETPOFFSET(p) + DL_BLKTOSEC(sc->sc_dk.dk_label, bp->b_blkno);
nsecs = howmany(bp->b_bcount, sc->sc_dk.dk_label->d_secsize);
if (!ISSET(link->flags, SDEV_ATAPI | SDEV_UMASS) &&
(SID_ANSII_REV(&link->inqdata) < SCSI_REV_2) &&
((secno & 0x1fffff) == secno) &&
((nsecs & 0xff) == nsecs))
xs->cmdlen = cd_cmd_rw6(&xs->cmd, read, secno, nsecs);
else if (((secno & 0xffffffff) == secno) &&
((nsecs & 0xffff) == nsecs))
xs->cmdlen = cd_cmd_rw10(&xs->cmd, read, secno, nsecs);
else
xs->cmdlen = cd_cmd_rw12(&xs->cmd, read, secno, nsecs);
disk_busy(&sc->sc_dk);
scsi_xs_exec(xs);
/* Move onto the next io. */
if (bufq_peek(&sc->sc_bufq))
scsi_xsh_add(&sc->sc_xsh);
}
void
cd_buf_done(struct scsi_xfer *xs)
{
struct cd_softc *sc = xs->sc_link->device_softc;
struct buf *bp = xs->cookie;
int error, s;
switch (xs->error) {
case XS_NOERROR:
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
bp->b_resid = xs->resid;
break;
case XS_SENSE:
case XS_SHORTSENSE:
SC_DEBUG_SENSE(xs);
error = cd_interpret_sense(xs);
if (error == 0) {
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
bp->b_resid = xs->resid;
break;
}
if (error != ERESTART)
xs->retries = 0;
goto retry;
case XS_BUSY:
if (xs->retries) {
if (scsi_delay(xs, 1) != ERESTART)
xs->retries = 0;
}
goto retry;
case XS_TIMEOUT:
retry:
if (xs->retries--) {
scsi_xs_exec(xs);
return;
}
/* FALLTHROUGH */
default:
bp->b_error = EIO;
SET(bp->b_flags, B_ERROR);
bp->b_resid = bp->b_bcount;
break;
}
disk_unbusy(&sc->sc_dk, bp->b_bcount - xs->resid, bp->b_blkno,
bp->b_flags & B_READ);
s = splbio();
biodone(bp);
splx(s);
scsi_xs_put(xs);
}
void
cdminphys(struct buf *bp)
{
struct scsi_link *link;
struct cd_softc *sc;
long max;
sc = cdlookup(DISKUNIT(bp->b_dev));
if (sc == NULL)
return;
link = sc->sc_link;
/*
* If the device is ancient, we want to make sure that
* the transfer fits into a 6-byte cdb.
*
* XXX Note that the SCSI-I spec says that 256-block transfers
* are allowed in a 6-byte read/write, and are specified
* by setting the "length" to 0. However, we're conservative
* here, allowing only 255-block transfers in case an
* ancient device gets confused by length == 0. A length of 0
* in a 10-byte read/write actually means 0 blocks.
*/
if (!ISSET(link->flags, SDEV_ATAPI | SDEV_UMASS) &&
SID_ANSII_REV(&link->inqdata) < SCSI_REV_2) {
max = sc->sc_dk.dk_label->d_secsize * 0xff;
if (bp->b_bcount > max)
bp->b_bcount = max;
}
if (link->bus->sb_adapter->dev_minphys != NULL)
(*link->bus->sb_adapter->dev_minphys)(bp, link);
else
minphys(bp);
device_unref(&sc->sc_dev);
}
int
cdread(dev_t dev, struct uio *uio, int ioflag)
{
return physio(cdstrategy, dev, B_READ, cdminphys, uio);
}
int
cdwrite(dev_t dev, struct uio *uio, int ioflag)
{
return physio(cdstrategy, dev, B_WRITE, cdminphys, uio);
}
/*
* Perform special action on behalf of the user.
* Knows about the internals of this device
*/
int
cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct cd_softc *sc;
struct disklabel *lp;
int part = DISKPART(dev);
int error = 0;
sc = cdlookup(DISKUNIT(dev));
if (sc == NULL)
return ENXIO;
if (ISSET(sc->sc_flags, CDF_DYING)) {
device_unref(&sc->sc_dev);
return ENXIO;
}
SC_DEBUG(sc->sc_link, SDEV_DB2, ("cdioctl 0x%lx\n", cmd));
/*
* If the device is not valid.. abandon ship
*/
if (!ISSET(sc->sc_link->flags, SDEV_MEDIA_LOADED)) {
switch (cmd) {
case DIOCLOCK:
case DIOCEJECT:
case SCIOCIDENTIFY:
case SCIOCCOMMAND:
case SCIOCDEBUG:
case CDIOCLOADUNLOAD:
case SCIOCRESET:
case CDIOCGETVOL:
case CDIOCSETVOL:
case CDIOCSETMONO:
case CDIOCSETSTEREO:
case CDIOCSETMUTE:
case CDIOCSETLEFT:
case CDIOCSETRIGHT:
case CDIOCCLOSE:
case CDIOCEJECT:
case CDIOCALLOW:
case CDIOCPREVENT:
case CDIOCSETDEBUG:
case CDIOCCLRDEBUG:
case CDIOCRESET:
case DVD_AUTH:
case DVD_READ_STRUCT:
case MTIOCTOP:
if (part == RAW_PART)
break;
/* FALLTHROUGH */
default:
if (!ISSET(sc->sc_link->flags, SDEV_OPEN))
error = ENODEV;
else
error = EIO;
goto exit;
}
}
switch (cmd) {
case DIOCRLDINFO:
lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
cdgetdisklabel(dev, sc, lp, 0);
memcpy(sc->sc_dk.dk_label, lp, sizeof(*lp));
free(lp, M_TEMP, sizeof(*lp));
break;
case DIOCGPDINFO:
cdgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
break;
case DIOCGDINFO:
*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
break;
case DIOCGPART:
((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
break;
case DIOCWDINFO:
case DIOCSDINFO:
if (!ISSET(flag, FWRITE)) {
error = EBADF;
break;
}
if ((error = disk_lock(&sc->sc_dk)) != 0)
break;
error = setdisklabel(sc->sc_dk.dk_label,
(struct disklabel *)addr, sc->sc_dk.dk_openmask);
if (error == 0) {
}
disk_unlock(&sc->sc_dk);
break;
case CDIOCPLAYTRACKS: {
struct ioc_play_track *args = (struct ioc_play_track *)addr;
if ((error = cd_set_pa_immed(sc, 0)) != 0)
break;
error = cd_play_tracks(sc, args->start_track,
args->start_index, args->end_track, args->end_index);
break;
}
case CDIOCPLAYMSF: {
struct ioc_play_msf *args = (struct ioc_play_msf *)addr;
if ((error = cd_set_pa_immed(sc, 0)) != 0)
break;
error = cd_play_msf(sc, args->start_m, args->start_s,
args->start_f, args->end_m, args->end_s, args->end_f);
break;
}
case CDIOCPLAYBLOCKS: {
struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr;
if ((error = cd_set_pa_immed(sc, 0)) != 0)
break;
error = cd_play(sc, args->blk, args->len);
break;
}
case CDIOCREADSUBCHANNEL: {
struct ioc_read_subchannel *args =
(struct ioc_read_subchannel *)addr;
struct cd_sub_channel_info *data;
int len = args->data_len;
if (len > sizeof(*data) ||
len < sizeof(struct cd_sub_channel_header)) {
error = EINVAL;
break;
}
data = dma_alloc(sizeof(*data), PR_WAITOK);
error = cd_read_subchannel(sc, args->address_format,
args->data_format, args->track, data, len);
if (error) {
dma_free(data, sizeof(*data));
break;
}
len = min(len, _2btol(data->header.data_len) +
sizeof(struct cd_sub_channel_header));
error = copyout(data, args->data, len);
dma_free(data, sizeof(*data));
break;
}
case CDIOREADTOCHEADER: {
struct ioc_toc_header *th;
th = dma_alloc(sizeof(*th), PR_WAITOK);
if ((error = cd_read_toc(sc, 0, 0, th, sizeof(*th), 0)) != 0) {
dma_free(th, sizeof(*th));
break;
}
if (ISSET(sc->sc_link->quirks, ADEV_LITTLETOC))
th->len = letoh16(th->len);
else
th->len = betoh16(th->len);
if (th->len > 0)
memcpy(addr, th, sizeof(*th));
else
error = EIO;
dma_free(th, sizeof(*th));
break;
}
case CDIOREADTOCENTRYS: {
struct cd_toc *toc;
struct ioc_read_toc_entry *te =
(struct ioc_read_toc_entry *)addr;
struct ioc_toc_header *th;
struct cd_toc_entry *cte;
int len = te->data_len;
int ntracks;
toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
th = &toc->header;
if (len > sizeof(toc->entries) ||
len < sizeof(struct cd_toc_entry)) {
dma_free(toc, sizeof(*toc));
error = EINVAL;
break;
}
error = cd_read_toc(sc, te->address_format, te->starting_track,
toc, len + sizeof(struct ioc_toc_header), 0);
if (error) {
dma_free(toc, sizeof(*toc));
break;
}
if (te->address_format == CD_LBA_FORMAT)
for (ntracks =
th->ending_track - th->starting_track + 1;
ntracks >= 0; ntracks--) {
cte = &toc->entries[ntracks];
cte->addr_type = CD_LBA_FORMAT;
if (ISSET(sc->sc_link->quirks,
ADEV_LITTLETOC)) {
#if BYTE_ORDER == BIG_ENDIAN
swap16_multi((u_int16_t *)&cte->addr,
sizeof(cte->addr) / 2);
#endif /* BYTE_ORDER == BIG_ENDIAN */
} else
cte->addr.lba = betoh32(cte->addr.lba);
}
if (ISSET(sc->sc_link->quirks, ADEV_LITTLETOC)) {
th->len = letoh16(th->len);
} else
th->len = betoh16(th->len);
len = min(len, th->len - (sizeof(th->starting_track) +
sizeof(th->ending_track)));
error = copyout(toc->entries, te->data, len);
dma_free(toc, sizeof(*toc));
break;
}
case CDIOREADMSADDR: {
struct cd_toc *toc;
int sessno = *(int *)addr;
struct cd_toc_entry *cte;
if (sessno != 0) {
error = EINVAL;
break;
}
toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
error = cd_read_toc(sc, 0, 0, toc,
sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry),
0x40 /* control word for "get MS info" */);
if (error) {
dma_free(toc, sizeof(*toc));
break;
}
cte = &toc->entries[0];
if (ISSET(sc->sc_link->quirks, ADEV_LITTLETOC)) {
#if BYTE_ORDER == BIG_ENDIAN
swap16_multi((u_int16_t *)&cte->addr,
sizeof(cte->addr) / 2);
#endif /* BYTE_ORDER == BIG_ENDIAN */
} else
cte->addr.lba = betoh32(cte->addr.lba);
if (ISSET(sc->sc_link->quirks, ADEV_LITTLETOC))
toc->header.len = letoh16(toc->header.len);
else
toc->header.len = betoh16(toc->header.len);
*(int *)addr = (toc->header.len >= 10 && cte->track > 1) ?
cte->addr.lba : 0;
dma_free(toc, sizeof(*toc));
break;
}
case CDIOCSETPATCH: {
struct ioc_patch *arg = (struct ioc_patch *)addr;
error = cd_setchan(sc, arg->patch[0], arg->patch[1],
arg->patch[2], arg->patch[3], 0);
break;
}
case CDIOCGETVOL: {
struct ioc_vol *arg = (struct ioc_vol *)addr;
error = cd_getvol(sc, arg, 0);
break;
}
case CDIOCSETVOL: {
struct ioc_vol *arg = (struct ioc_vol *)addr;
error = cd_setvol(sc, arg, 0);
break;
}
case CDIOCSETMONO:
error = cd_setchan(sc, BOTH_CHANNEL, BOTH_CHANNEL, MUTE_CHANNEL,
MUTE_CHANNEL, 0);
break;
case CDIOCSETSTEREO:
error = cd_setchan(sc, LEFT_CHANNEL, RIGHT_CHANNEL,
MUTE_CHANNEL, MUTE_CHANNEL, 0);
break;
case CDIOCSETMUTE:
error = cd_setchan(sc, MUTE_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL,
MUTE_CHANNEL, 0);
break;
case CDIOCSETLEFT:
error = cd_setchan(sc, LEFT_CHANNEL, LEFT_CHANNEL, MUTE_CHANNEL,
MUTE_CHANNEL, 0);
break;
case CDIOCSETRIGHT:
error = cd_setchan(sc, RIGHT_CHANNEL, RIGHT_CHANNEL,
MUTE_CHANNEL, MUTE_CHANNEL, 0);
break;
case CDIOCRESUME:
error = cd_pause(sc, 1);
break;
case CDIOCPAUSE:
error = cd_pause(sc, 0);
break;
case CDIOCSTART:
error = scsi_start(sc->sc_link, SSS_START, 0);
break;
case CDIOCSTOP:
error = scsi_start(sc->sc_link, SSS_STOP, 0);
break;
close_tray:
case CDIOCCLOSE:
error = scsi_start(sc->sc_link, SSS_START|SSS_LOEJ,
SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE);
break;
case MTIOCTOP:
if (((struct mtop *)addr)->mt_op == MTRETEN)
goto close_tray;
if (((struct mtop *)addr)->mt_op != MTOFFL) {
error = EIO;
break;
}
/* FALLTHROUGH */
case CDIOCEJECT: /* FALLTHROUGH */
case DIOCEJECT:
SET(sc->sc_link->flags, SDEV_EJECTING);
break;
case CDIOCALLOW:
error = scsi_prevent(sc->sc_link, PR_ALLOW, 0);
break;
case CDIOCPREVENT:
error = scsi_prevent(sc->sc_link, PR_PREVENT, 0);
break;
case DIOCLOCK:
error = scsi_prevent(sc->sc_link,
(*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0);
break;
case CDIOCSETDEBUG:
SET(sc->sc_link->flags, SDEV_DB1 | SDEV_DB2);
break;
case CDIOCCLRDEBUG:
CLR(sc->sc_link->flags, SDEV_DB1 | SDEV_DB2);
break;
case CDIOCRESET:
case SCIOCRESET:
error = cd_reset(sc);
break;
case CDIOCLOADUNLOAD: {
struct ioc_load_unload *args = (struct ioc_load_unload *)addr;
error = cd_load_unload(sc, args->options, args->slot);
break;
}
case DVD_AUTH:
error = dvd_auth(sc, (union dvd_authinfo *)addr);
break;
case DVD_READ_STRUCT:
error = dvd_read_struct(sc, (union dvd_struct *)addr);
break;
default:
if (DISKPART(dev) != RAW_PART) {
error = ENOTTY;
break;
}
error = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
break;
}
exit:
device_unref(&sc->sc_dev);
return error;
}
/*
* Load the label information on the named device
* Actually fabricate a disklabel
*
* EVENTUALLY take information about different
* data tracks from the TOC and put it in the disklabel
*/
int
cdgetdisklabel(dev_t dev, struct cd_softc *sc, struct disklabel *lp,
int spoofonly)
{
struct cd_toc *toc;
int tocidx, n, audioonly = 1;
bzero(lp, sizeof(struct disklabel));
lp->d_secsize = sc->params.secsize;
lp->d_ntracks = 1;
lp->d_nsectors = 100;
lp->d_secpercyl = 100;
lp->d_ncylinders = (sc->params.disksize / 100) + 1;
if (ISSET(sc->sc_link->flags, SDEV_ATAPI)) {
strncpy(lp->d_typename, "ATAPI CD-ROM", sizeof(lp->d_typename));
lp->d_type = DTYPE_ATAPI;
} else {
strncpy(lp->d_typename, "SCSI CD-ROM", sizeof(lp->d_typename));
lp->d_type = DTYPE_SCSI;
}
strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
DL_SETDSIZE(lp, sc->params.disksize);
lp->d_version = 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
if (cd_load_toc(sc, toc, CD_LBA_FORMAT)) {
audioonly = 0; /* No valid TOC found == not an audio CD. */
goto done;
}
n = toc->header.ending_track - toc->header.starting_track + 1;
for (tocidx = 0; tocidx < n; tocidx++)
if (toc->entries[tocidx].control & 4) {
audioonly = 0; /* Found a non-audio track. */
goto done;
}
done:
dma_free(toc, sizeof(*toc));
if (audioonly)
return 0;
return readdisklabel(DISKLABELDEV(dev), cdstrategy, lp, spoofonly);
}
int
cd_setchan(struct cd_softc *sc, int p0, int p1, int p2, int p3, int flags)
{
union scsi_mode_sense_buf *data;
struct cd_audio_page *audio = NULL;
int error, big;
data = dma_alloc(sizeof(*data), PR_NOWAIT);
if (data == NULL)
return ENOMEM;
error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
(void **)&audio, sizeof(*audio), flags, &big);
if (error == 0 && audio == NULL)
error = EIO;
if (error == 0) {
audio->port[LEFT_PORT].channels = p0;
audio->port[RIGHT_PORT].channels = p1;
audio->port[2].channels = p2;
audio->port[3].channels = p3;
if (big)
error = scsi_mode_select_big(sc->sc_link, SMS_PF,
&data->hdr_big, flags, 20000);
else
error = scsi_mode_select(sc->sc_link, SMS_PF,
&data->hdr, flags, 20000);
}
dma_free(data, sizeof(*data));
return error;
}
int
cd_getvol(struct cd_softc *sc, struct ioc_vol *arg, int flags)
{
union scsi_mode_sense_buf *data;
struct cd_audio_page *audio = NULL;
int big, error;
data = dma_alloc(sizeof(*data), PR_NOWAIT);
if (data == NULL)
return ENOMEM;
error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
(void **)&audio, sizeof(*audio), flags, &big);
if (error == 0 && audio == NULL)
error = EIO;
if (error == 0) {
arg->vol[0] = audio->port[0].volume;
arg->vol[1] = audio->port[1].volume;
arg->vol[2] = audio->port[2].volume;
arg->vol[3] = audio->port[3].volume;
}
dma_free(data, sizeof(*data));
return 0;
}
int
cd_setvol(struct cd_softc *sc, const struct ioc_vol *arg, int flags)
{
union scsi_mode_sense_buf *data;
struct cd_audio_page *audio = NULL;
u_int8_t mask_volume[4];
int error, big;
data = dma_alloc(sizeof(*data), PR_NOWAIT);
if (data == NULL)
return ENOMEM;
error = scsi_do_mode_sense(sc->sc_link,
AUDIO_PAGE | SMS_PAGE_CTRL_CHANGEABLE, data, (void **)&audio,
sizeof(*audio), flags, &big);
if (error == 0 && audio == NULL)
error = EIO;
if (error != 0) {
dma_free(data, sizeof(*data));
return error;
}
mask_volume[0] = audio->port[0].volume;
mask_volume[1] = audio->port[1].volume;
mask_volume[2] = audio->port[2].volume;
mask_volume[3] = audio->port[3].volume;
error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
(void **)&audio, sizeof(*audio), flags, &big);
if (error == 0 && audio == NULL)
error = EIO;
if (error != 0) {
dma_free(data, sizeof(*data));
return error;
}
audio->port[0].volume = arg->vol[0] & mask_volume[0];
audio->port[1].volume = arg->vol[1] & mask_volume[1];
audio->port[2].volume = arg->vol[2] & mask_volume[2];
audio->port[3].volume = arg->vol[3] & mask_volume[3];
if (big)
error = scsi_mode_select_big(sc->sc_link, SMS_PF,
&data->hdr_big, flags, 20000);
else
error = scsi_mode_select(sc->sc_link, SMS_PF,
&data->hdr, flags, 20000);
dma_free(data, sizeof(*data));
return error;
}
int
cd_load_unload(struct cd_softc *sc, int options, int slot)
{
struct scsi_load_unload *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = 200000;
cmd = (struct scsi_load_unload *)&xs->cmd;
cmd->opcode = LOAD_UNLOAD;
cmd->options = options; /* ioctl uses ATAPI values */
cmd->slot = slot;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
int
cd_set_pa_immed(struct cd_softc *sc, int flags)
{
union scsi_mode_sense_buf *data;
struct cd_audio_page *audio = NULL;
int error, oflags, big;
if (ISSET(sc->sc_link->flags, SDEV_ATAPI))
/* XXX Noop? */
return 0;
data = dma_alloc(sizeof(*data), PR_NOWAIT);
if (data == NULL)
return ENOMEM;
error = scsi_do_mode_sense(sc->sc_link, AUDIO_PAGE, data,
(void **)&audio, sizeof(*audio), flags, &big);
if (error == 0 && audio == NULL)
error = EIO;
if (error == 0) {
oflags = audio->flags;
CLR(audio->flags, CD_PA_SOTC);
SET(audio->flags, CD_PA_IMMED);
if (audio->flags != oflags) {
if (big)
error = scsi_mode_select_big(sc->sc_link,
SMS_PF, &data->hdr_big, flags, 20000);
else
error = scsi_mode_select(sc->sc_link, SMS_PF,
&data->hdr, flags, 20000);
}
}
dma_free(data, sizeof(*data));
return error;
}
/*
* Get scsi driver to send a "start playing" command
*/
int
cd_play(struct cd_softc *sc, int secno, int nsecs)
{
struct scsi_play *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = 200000;
cmd = (struct scsi_play *)&xs->cmd;
cmd->opcode = PLAY;
_lto4b(secno, cmd->blk_addr);
_lto2b(nsecs, cmd->xfer_len);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Get scsi driver to send a "start playing" command
*/
int
cd_play_tracks(struct cd_softc *sc, int strack, int sindex, int etrack,
int eindex)
{
struct cd_toc *toc;
int error;
u_char endf, ends, endm;
if (!etrack)
return EIO;
if (strack > etrack)
return EINVAL;
toc = dma_alloc(sizeof(*toc), PR_WAITOK | PR_ZERO);
if ((error = cd_load_toc(sc, toc, CD_MSF_FORMAT)) != 0)
goto done;
if (++etrack > (toc->header.ending_track+1))
etrack = toc->header.ending_track+1;
strack -= toc->header.starting_track;
etrack -= toc->header.starting_track;
if (strack < 0) {
error = EINVAL;
goto done;
}
/*
* The track ends one frame before the next begins. The last track
* is taken care of by the leadoff track.
*/
endm = toc->entries[etrack].addr.msf.minute;
ends = toc->entries[etrack].addr.msf.second;
endf = toc->entries[etrack].addr.msf.frame;
if (endf-- == 0) {
endf = CD_FRAMES - 1;
if (ends-- == 0) {
ends = CD_SECS - 1;
if (endm-- == 0) {
error = EINVAL;
goto done;
}
}
}
error = cd_play_msf(sc, toc->entries[strack].addr.msf.minute,
toc->entries[strack].addr.msf.second,
toc->entries[strack].addr.msf.frame,
endm, ends, endf);
done:
dma_free(toc, sizeof(*toc));
return error;
}
/*
* Get scsi driver to send a "play msf" command
*/
int
cd_play_msf(struct cd_softc *sc, int startm, int starts, int startf, int endm,
int ends, int endf)
{
struct scsi_play_msf *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = 20000;
cmd = (struct scsi_play_msf *)&xs->cmd;
cmd->opcode = PLAY_MSF;
cmd->start_m = startm;
cmd->start_s = starts;
cmd->start_f = startf;
cmd->end_m = endm;
cmd->end_s = ends;
cmd->end_f = endf;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Get scsi driver to send a "start up" command
*/
int
cd_pause(struct cd_softc *sc, int go)
{
struct scsi_pause *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = 2000;
cmd = (struct scsi_pause *)&xs->cmd;
cmd->opcode = PAUSE;
cmd->resume = go;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Get scsi driver to send a "RESET" command
*/
int
cd_reset(struct cd_softc *sc)
{
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, SCSI_RESET);
if (xs == NULL)
return ENOMEM;
xs->timeout = 2000;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Read subchannel
*/
int
cd_read_subchannel(struct cd_softc *sc, int mode, int format, int track,
struct cd_sub_channel_info *data, int len)
{
struct scsi_read_subchannel *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)data;
xs->datalen = len;
xs->timeout = 5000;
cmd = (struct scsi_read_subchannel *)&xs->cmd;
cmd->opcode = READ_SUBCHANNEL;
if (mode == CD_MSF_FORMAT)
SET(cmd->byte2, CD_MSF);
cmd->byte3 = SRS_SUBQ;
cmd->subchan_format = format;
cmd->track = track;
_lto2b(len, cmd->data_len);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Read table of contents
*/
int
cd_read_toc(struct cd_softc *sc, int mode, int start, void *data, int len,
int control)
{
struct scsi_read_toc *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN |
SCSI_IGNORE_ILLEGAL_REQUEST);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = data;
xs->datalen = len;
xs->timeout = 5000;
bzero(data, len);
cmd = (struct scsi_read_toc *)&xs->cmd;
cmd->opcode = READ_TOC;
if (mode == CD_MSF_FORMAT)
SET(cmd->byte2, CD_MSF);
cmd->from_track = start;
_lto2b(len, cmd->data_len);
cmd->control = control;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
int
cd_load_toc(struct cd_softc *sc, struct cd_toc *toc, int fmt)
{
int n, len, error;
error = cd_read_toc(sc, 0, 0, toc, sizeof(toc->header), 0);
if (error == 0) {
if (toc->header.ending_track < toc->header.starting_track)
return EIO;
/* +2 to account for leading out track. */
n = toc->header.ending_track - toc->header.starting_track + 2;
len = n * sizeof(struct cd_toc_entry) + sizeof(toc->header);
error = cd_read_toc(sc, fmt, 0, toc, len, 0);
}
return error;
}
/*
* Get the scsi driver to send a full inquiry to the device and use the
* results to fill out the disk parameter structure.
*/
int
cd_get_parms(struct cd_softc *sc, int flags)
{
/* Reasonable defaults for drives that don't support READ_CAPACITY */
sc->params.secsize = 2048;
sc->params.disksize = 400000;
if (ISSET(sc->sc_link->quirks, ADEV_NOCAPACITY))
return 0;
sc->params.disksize = cd_size(sc->sc_link, flags, &sc->params.secsize);
if ((sc->params.secsize < 512) ||
((sc->params.secsize & 511) != 0))
sc->params.secsize = 2048; /* some drives lie ! */
if (sc->params.disksize < 100)
sc->params.disksize = 400000;
return 0;
}
daddr_t
cdsize(dev_t dev)
{
/* CD-ROMs are read-only. */
return -1;
}
int
cddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
/* Not implemented. */
return ENXIO;
}
#define dvd_copy_key(dst, src) memcpy((dst), (src), DVD_KEY_SIZE)
#define dvd_copy_challenge(dst, src) memcpy((dst), (src), DVD_CHALLENGE_SIZE)
#define DVD_AUTH_BUFSIZE 20
int
dvd_auth(struct cd_softc *sc, union dvd_authinfo *a)
{
struct scsi_generic *cmd;
struct scsi_xfer *xs;
u_int8_t *buf;
int error;
buf = dma_alloc(DVD_AUTH_BUFSIZE, PR_WAITOK | PR_ZERO);
if (buf == NULL)
return ENOMEM;
xs = scsi_xs_get(sc->sc_link, 0);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->timeout = 30000;
xs->data = buf;
cmd = &xs->cmd;
switch (a->type) {
case DVD_LU_SEND_AGID:
cmd->opcode = GPCMD_REPORT_KEY;
cmd->bytes[8] = 8;
cmd->bytes[9] = 0 | (0 << 6);
xs->datalen = 8;
SET(xs->flags, SCSI_DATA_IN);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
a->lsa.agid = buf[7] >> 6;
break;
case DVD_LU_SEND_CHALLENGE:
cmd->opcode = GPCMD_REPORT_KEY;
cmd->bytes[8] = 16;
cmd->bytes[9] = 1 | (a->lsc.agid << 6);
xs->datalen = 16;
SET(xs->flags, SCSI_DATA_IN);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
dvd_copy_challenge(a->lsc.chal, &buf[4]);
break;
case DVD_LU_SEND_KEY1:
cmd->opcode = GPCMD_REPORT_KEY;
cmd->bytes[8] = 12;
cmd->bytes[9] = 2 | (a->lsk.agid << 6);
xs->datalen = 12;
SET(xs->flags, SCSI_DATA_IN);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
dvd_copy_key(a->lsk.key, &buf[4]);
break;
case DVD_LU_SEND_TITLE_KEY:
cmd->opcode = GPCMD_REPORT_KEY;
_lto4b(a->lstk.lba, &cmd->bytes[1]);
cmd->bytes[8] = 12;
cmd->bytes[9] = 4 | (a->lstk.agid << 6);
xs->datalen = 12;
SET(xs->flags, SCSI_DATA_IN);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
a->lstk.cpm = (buf[4] >> 7) & 1;
a->lstk.cp_sec = (buf[4] >> 6) & 1;
a->lstk.cgms = (buf[4] >> 4) & 3;
dvd_copy_key(a->lstk.title_key, &buf[5]);
}
break;
case DVD_LU_SEND_ASF:
cmd->opcode = GPCMD_REPORT_KEY;
cmd->bytes[8] = 8;
cmd->bytes[9] = 5 | (a->lsasf.agid << 6);
xs->datalen = 8;
SET(xs->flags, SCSI_DATA_IN);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
a->lsasf.asf = buf[7] & 1;
break;
case DVD_HOST_SEND_CHALLENGE:
cmd->opcode = GPCMD_SEND_KEY;
cmd->bytes[8] = 16;
cmd->bytes[9] = 1 | (a->hsc.agid << 6);
buf[1] = 14;
dvd_copy_challenge(&buf[4], a->hsc.chal);
xs->datalen = 16;
SET(xs->flags, SCSI_DATA_OUT);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
a->type = DVD_LU_SEND_KEY1;
break;
case DVD_HOST_SEND_KEY2:
cmd->opcode = GPCMD_SEND_KEY;
cmd->bytes[8] = 12;
cmd->bytes[9] = 3 | (a->hsk.agid << 6);
buf[1] = 10;
dvd_copy_key(&buf[4], a->hsk.key);
xs->datalen = 12;
SET(xs->flags, SCSI_DATA_OUT);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
a->type = DVD_AUTH_ESTABLISHED;
else
a->type = DVD_AUTH_FAILURE;
break;
case DVD_INVALIDATE_AGID:
cmd->opcode = GPCMD_REPORT_KEY;
cmd->bytes[9] = 0x3f | (a->lsa.agid << 6);
xs->data = NULL;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
break;
case DVD_LU_SEND_RPC_STATE:
cmd->opcode = GPCMD_REPORT_KEY;
cmd->bytes[8] = 8;
cmd->bytes[9] = 8 | (0 << 6);
xs->datalen = 8;
SET(xs->flags, SCSI_DATA_IN);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
a->lrpcs.type = (buf[4] >> 6) & 3;
a->lrpcs.vra = (buf[4] >> 3) & 7;
a->lrpcs.ucca = (buf[4]) & 7;
a->lrpcs.region_mask = buf[5];
a->lrpcs.rpc_scheme = buf[6];
}
break;
case DVD_HOST_SEND_RPC_STATE:
cmd->opcode = GPCMD_SEND_KEY;
cmd->bytes[8] = 8;
cmd->bytes[9] = 6 | (0 << 6);
buf[1] = 6;
buf[4] = a->hrpcs.pdrc;
xs->datalen = 8;
SET(xs->flags, SCSI_DATA_OUT);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
break;
default:
scsi_xs_put(xs);
error = ENOTTY;
break;
}
done:
dma_free(buf, DVD_AUTH_BUFSIZE);
return error;
}
#define DVD_READ_PHYSICAL_BUFSIZE (4 + 4 * 20)
int
dvd_read_physical(struct cd_softc *sc, union dvd_struct *s)
{
struct scsi_generic *cmd;
struct dvd_layer *layer;
struct scsi_xfer *xs;
u_int8_t *buf, *bufp;
int error, i;
buf = dma_alloc(DVD_READ_PHYSICAL_BUFSIZE, PR_WAITOK | PR_ZERO);
if (buf == NULL)
return ENOMEM;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->data = buf;
xs->datalen = DVD_READ_PHYSICAL_BUFSIZE;
xs->timeout = 30000;
cmd = &xs->cmd;
cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
cmd->bytes[6] = s->type;
_lto2b(xs->datalen, &cmd->bytes[7]);
cmd->bytes[5] = s->physical.layer_num;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
for (i = 0, bufp = &buf[4], layer = &s->physical.layer[0];
i < 4; i++, bufp += 20, layer++) {
bzero(layer, sizeof(*layer));
layer->book_version = bufp[0] & 0xf;
layer->book_type = bufp[0] >> 4;
layer->min_rate = bufp[1] & 0xf;
layer->disc_size = bufp[1] >> 4;
layer->layer_type = bufp[2] & 0xf;
layer->track_path = (bufp[2] >> 4) & 1;
layer->nlayers = (bufp[2] >> 5) & 3;
layer->track_density = bufp[3] & 0xf;
layer->linear_density = bufp[3] >> 4;
layer->start_sector = _4btol(&bufp[4]);
layer->end_sector = _4btol(&bufp[8]);
layer->end_sector_l0 = _4btol(&bufp[12]);
layer->bca = bufp[16] >> 7;
}
}
done:
dma_free(buf, DVD_READ_PHYSICAL_BUFSIZE);
return error;
}
#define DVD_READ_COPYRIGHT_BUFSIZE 8
int
dvd_read_copyright(struct cd_softc *sc, union dvd_struct *s)
{
struct scsi_generic *cmd;
struct scsi_xfer *xs;
u_int8_t *buf;
int error;
buf = dma_alloc(DVD_READ_COPYRIGHT_BUFSIZE, PR_WAITOK | PR_ZERO);
if (buf == NULL)
return ENOMEM;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->data = buf;
xs->datalen = DVD_READ_COPYRIGHT_BUFSIZE;
xs->timeout = 30000;
cmd = &xs->cmd;
cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
cmd->bytes[6] = s->type;
_lto2b(xs->datalen, &cmd->bytes[7]);
cmd->bytes[5] = s->copyright.layer_num;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
s->copyright.cpst = buf[4];
s->copyright.rmi = buf[5];
}
done:
dma_free(buf, DVD_READ_COPYRIGHT_BUFSIZE);
return error;
}
int
dvd_read_disckey(struct cd_softc *sc, union dvd_struct *s)
{
struct scsi_read_dvd_structure_data *buf;
struct scsi_read_dvd_structure *cmd;
struct scsi_xfer *xs;
int error;
buf = dma_alloc(sizeof(*buf), PR_WAITOK | PR_ZERO);
if (buf == NULL)
return ENOMEM;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)buf;
xs->datalen = sizeof(*buf);
xs->timeout = 30000;
cmd = (struct scsi_read_dvd_structure *)&xs->cmd;
cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
cmd->format = s->type;
cmd->agid = s->disckey.agid << 6;
_lto2b(xs->datalen, cmd->length);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0)
memcpy(s->disckey.value, buf->data, sizeof(s->disckey.value));
done:
dma_free(buf, sizeof(*buf));
return error;
}
#define DVD_READ_BCA_BUFLEN (4 + 188)
int
dvd_read_bca(struct cd_softc *sc, union dvd_struct *s)
{
struct scsi_generic *cmd;
struct scsi_xfer *xs;
u_int8_t *buf;
int error;
buf = dma_alloc(DVD_READ_BCA_BUFLEN, PR_WAITOK | PR_ZERO);
if (buf == NULL)
return ENOMEM;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->data = buf;
xs->datalen = DVD_READ_BCA_BUFLEN;
xs->timeout = 30000;
cmd = &xs->cmd;
cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
cmd->bytes[6] = s->type;
_lto2b(xs->datalen, &cmd->bytes[7]);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
s->bca.len = _2btol(&buf[0]);
if (s->bca.len < 12 || s->bca.len > 188)
return EIO;
memcpy(s->bca.value, &buf[4], s->bca.len);
}
done:
dma_free(buf, DVD_READ_BCA_BUFLEN);
return error;
}
int
dvd_read_manufact(struct cd_softc *sc, union dvd_struct *s)
{
struct scsi_read_dvd_structure_data *buf;
struct scsi_read_dvd_structure *cmd;
struct scsi_xfer *xs;
int error;
buf = dma_alloc(sizeof(*buf), PR_WAITOK | PR_ZERO);
if (buf == NULL)
return ENOMEM;
xs = scsi_xs_get(sc->sc_link, SCSI_DATA_IN);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)buf;
xs->datalen = sizeof(*buf);
xs->timeout = 30000;
cmd = (struct scsi_read_dvd_structure *)&xs->cmd;
cmd->opcode = GPCMD_READ_DVD_STRUCTURE;
cmd->format = s->type;
_lto2b(xs->datalen, cmd->length);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
s->manufact.len = _2btol(buf->len);
if (s->manufact.len >= 0 && s->manufact.len <= 2048)
memcpy(s->manufact.value, buf->data, s->manufact.len);
else
error = EIO;
}
done:
dma_free(buf, sizeof(*buf));
return error;
}
int
dvd_read_struct(struct cd_softc *sc, union dvd_struct *s)
{
switch (s->type) {
case DVD_STRUCT_PHYSICAL:
return dvd_read_physical(sc, s);
case DVD_STRUCT_COPYRIGHT:
return dvd_read_copyright(sc, s);
case DVD_STRUCT_DISCKEY:
return dvd_read_disckey(sc, s);
case DVD_STRUCT_BCA:
return dvd_read_bca(sc, s);
case DVD_STRUCT_MANUFACT:
return dvd_read_manufact(sc, s);
default:
return EINVAL;
}
}
int
cd_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
struct scsi_link *link = xs->sc_link;
u_int8_t skey = sense->flags & SSD_KEY;
u_int8_t serr = sense->error_code & SSD_ERRCODE;
if (!ISSET(link->flags, SDEV_OPEN) ||
(serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
return scsi_interpret_sense(xs);
/*
* We do custom processing in cd for the unit becoming ready
* case. We do not allow xs->retries to be decremented on the
* "Unit Becoming Ready" case. This is because CD drives
* report "Unit Becoming Ready" when loading media and can
* take a long time. Rather than having a massive timeout for
* all operations (which would cause other problems), we allow
* operations to wait (but be interruptible with Ctrl-C)
* forever as long as the drive is reporting that it is
* becoming ready. All other cases of not being ready are
* handled by the default handler.
*/
switch(skey) {
case SKEY_NOT_READY:
if (ISSET(xs->flags, SCSI_IGNORE_NOT_READY))
return 0;
if (ASC_ASCQ(sense) == SENSE_NOT_READY_BECOMING_READY) {
SC_DEBUG(link, SDEV_DB1, ("not ready: busy (%#x)\n",
sense->add_sense_code_qual));
/* don't count this as a retry */
xs->retries++;
return scsi_delay(xs, 1);
}
break;
/* XXX more to come here for a few other cases */
default:
break;
}
return scsi_interpret_sense(xs);
}
/*
* Find out from the device what its capacity is.
*/
u_int64_t
cd_size(struct scsi_link *link, int flags, u_int32_t *blksize)
{
struct scsi_read_cap_data_16 *rdcap16;
struct scsi_read_cap_data *rdcap;
u_int64_t max_addr;
int error;
if (blksize != NULL)
*blksize = 0;
CLR(flags, SCSI_IGNORE_ILLEGAL_REQUEST);
/*
* Start with a READ CAPACITY(10).
*/
rdcap = dma_alloc(sizeof(*rdcap), ((flags & SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (rdcap == NULL)
return 0;
error = scsi_read_cap_10(link, rdcap, flags);
if (error) {
dma_free(rdcap, sizeof(*rdcap));
return 0;
}
max_addr = _4btol(rdcap->addr);
if (blksize != NULL)
*blksize = _4btol(rdcap->length);
dma_free(rdcap, sizeof(*rdcap));
/*
* pre-SPC (i.e. pre-SCSI-3) devices reporting less than 2^32-1 sectors
* can stop here.
*/
if (SID_ANSII_REV(&link->inqdata) < SCSI_REV_SPC &&
max_addr != 0xffffffff)
goto exit;
rdcap16 = dma_alloc(sizeof(*rdcap16), ((flags & SCSI_NOSLEEP) ?
PR_NOWAIT : PR_WAITOK) | PR_ZERO);
if (rdcap16 == NULL)
goto exit;
error = scsi_read_cap_16(link, rdcap16, flags);
if (error) {
dma_free(rdcap16, sizeof(*rdcap16));
goto exit;
}
max_addr = _8btol(rdcap16->addr);
if (blksize != NULL)
*blksize = _4btol(rdcap16->length);
/* XXX The other READ CAPACITY(16) info could be stored away. */
dma_free(rdcap16, sizeof(*rdcap16));
return max_addr + 1;
exit:
/* Return READ CAPACITY 10 values. */
if (max_addr != 0xffffffff)
return max_addr + 1;
else if (blksize != NULL)
*blksize = 0;
return 0;
}
#if defined(__macppc__)
int
cd_eject(void)
{
struct cd_softc *sc;
int error = 0;
if (cd_cd.cd_ndevs == 0 || (sc = cd_cd.cd_devs[0]) == NULL)
return ENXIO;
if ((error = disk_lock(&sc->sc_dk)) != 0)
return error;
if (sc->sc_dk.dk_openmask == 0) {
SET(sc->sc_link->flags, SDEV_EJECTING);
scsi_prevent(sc->sc_link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY |
SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE);
CLR(sc->sc_link->flags, SDEV_MEDIA_LOADED);
scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0);
CLR(sc->sc_link->flags, SDEV_EJECTING);
}
disk_unlock(&sc->sc_dk);
return error;
}
#endif /* __macppc__ */
57
60
12
6
211
211
212
210
1
1
27
183
203
194
7
173
120
175
149
159
155
211
60
147
6
6
18
1
6
6
132
134
123
16
10
43
10
34
53
27
4
26
53
53
60
13
38
2
8
8
3
16
2
129
117
1
3
12
5
45
70
48
40
37
10
27
59
47
25
1
24
1
2
2
2
1
3
1
19
7
25
7
9
2
2
114
116
1
116
1
1
1
2
7
51
115
115
116
2
8
34
3
1
4
2
1
2
2
2
5
1
1
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
/* $OpenBSD: ip6_input.c,v 1.254 2022/08/21 14:15:55 bluhm Exp $ */
/* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94
*/
#include "pf.h"
#include "carp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/sysctl.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/task.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet6/in6_var.h>
#include <netinet6/in6_ifattach.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#include "gif.h"
#include "bpfilter.h"
#ifdef MROUTING
#include <netinet6/ip6_mroute.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
struct niqueue ip6intrq = NIQUEUE_INITIALIZER(IPQ_MAXLEN, NETISR_IPV6);
struct cpumem *ip6counters;
uint8_t ip6_soiikey[IP6_SOIIKEY_LEN];
int ip6_ours(struct mbuf **, int *, int, int);
int ip6_check_rh0hdr(struct mbuf *, int *);
int ip6_hbhchcheck(struct mbuf **, int *, int *);
int ip6_hopopts_input(struct mbuf **, int *, u_int32_t *, u_int32_t *);
struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int);
int ip6_sysctl_soiikey(void *, size_t *, void *, size_t);
static struct mbuf_queue ip6send_mq;
static void ip6_send_dispatch(void *);
static struct task ip6send_task =
TASK_INITIALIZER(ip6_send_dispatch, &ip6send_mq);
/*
* IP6 initialization: fill in IP6 protocol switch table.
* All protocols not implemented in kernel go to raw IP6 protocol handler.
*/
void
ip6_init(void)
{
const struct protosw *pr;
int i;
pr = pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW);
if (pr == NULL)
panic("%s", __func__);
for (i = 0; i < IPPROTO_MAX; i++)
ip6_protox[i] = pr - inet6sw;
for (pr = inet6domain.dom_protosw;
pr < inet6domain.dom_protoswNPROTOSW; pr++)
if (pr->pr_domain->dom_family == PF_INET6 &&
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW &&
pr->pr_protocol < IPPROTO_MAX)
ip6_protox[pr->pr_protocol] = pr - inet6sw;
ip6_randomid_init();
nd6_init();
frag6_init();
mq_init(&ip6send_mq, 64, IPL_SOFTNET);
ip6counters = counters_alloc(ip6s_ncounters);
#ifdef MROUTING
rt_timer_queue_init(&ip6_mrouterq, MCAST_EXPIRE_TIMEOUT,
&mf6c_expire_route);
#endif
}
struct ip6_offnxt {
int ion_off;
int ion_nxt;
};
/*
* Enqueue packet for local delivery. Queuing is used as a boundary
* between the network layer (input/forward path) running with
* NET_LOCK_SHARED() and the transport layer needing it exclusively.
*/
int
ip6_ours(struct mbuf **mp, int *offp, int nxt, int af)
{
/* ip6_hbhchcheck() may be run before, then off and nxt are set */
if (*offp == 0) {
nxt = ip6_hbhchcheck(mp, offp, NULL);
if (nxt == IPPROTO_DONE)
return IPPROTO_DONE;
}
/* We are already in a IPv4/IPv6 local deliver loop. */
if (af != AF_UNSPEC)
return nxt;
/* save values for later, use after dequeue */
if (*offp != sizeof(struct ip6_hdr)) {
struct m_tag *mtag;
struct ip6_offnxt *ion;
/* mbuf tags are expensive, but only used for header options */
mtag = m_tag_get(PACKET_TAG_IP6_OFFNXT, sizeof(*ion),
M_NOWAIT);
if (mtag == NULL) {
ip6stat_inc(ip6s_idropped);
m_freemp(mp);
return IPPROTO_DONE;
}
ion = (struct ip6_offnxt *)(mtag + 1);
ion->ion_off = *offp;
ion->ion_nxt = nxt;
m_tag_prepend(*mp, mtag);
}
niq_enqueue(&ip6intrq, *mp);
*mp = NULL;
return IPPROTO_DONE;
}
/*
* Dequeue and process locally delivered packets.
* This is called with exclusive NET_LOCK().
*/
void
ip6intr(void)
{
struct mbuf *m;
while ((m = niq_dequeue(&ip6intrq)) != NULL) {
struct m_tag *mtag;
int off, nxt;
#ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0)
panic("ip6intr no HDR");
#endif
mtag = m_tag_find(m, PACKET_TAG_IP6_OFFNXT, NULL);
if (mtag != NULL) {
struct ip6_offnxt *ion;
ion = (struct ip6_offnxt *)(mtag + 1);
off = ion->ion_off;
nxt = ion->ion_nxt;
m_tag_delete(m, mtag);
} else {
struct ip6_hdr *ip6;
ip6 = mtod(m, struct ip6_hdr *);
off = sizeof(struct ip6_hdr);
nxt = ip6->ip6_nxt;
}
nxt = ip_deliver(&m, &off, nxt, AF_INET6);
KASSERT(nxt == IPPROTO_DONE);
}
}
void
ipv6_input(struct ifnet *ifp, struct mbuf *m)
{
int off, nxt;
off = 0;
nxt = ip6_input_if(&m, &off, IPPROTO_IPV6, AF_UNSPEC, ifp);
KASSERT(nxt == IPPROTO_DONE);
}
struct mbuf *
ipv6_check(struct ifnet *ifp, struct mbuf *m)
{
struct ip6_hdr *ip6;
if (m->m_len < sizeof(*ip6)) {
m = m_pullup(m, sizeof(*ip6));
if (m == NULL) {
ip6stat_inc(ip6s_toosmall);
return (NULL);
}
}
ip6 = mtod(m, struct ip6_hdr *);
if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
ip6stat_inc(ip6s_badvers);
goto bad;
}
/*
* Check against address spoofing/corruption.
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) ||
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) {
/*
* XXX: "badscope" is not very suitable for a multicast source.
*/
ip6stat_inc(ip6s_badscope);
goto bad;
}
if ((IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) ||
IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) &&
(ifp->if_flags & IFF_LOOPBACK) == 0) {
ip6stat_inc(ip6s_badscope);
goto bad;
}
/* Drop packets if interface ID portion is already filled. */
if (((IN6_IS_SCOPE_EMBED(&ip6->ip6_src) && ip6->ip6_src.s6_addr16[1]) ||
(IN6_IS_SCOPE_EMBED(&ip6->ip6_dst) && ip6->ip6_dst.s6_addr16[1])) &&
(ifp->if_flags & IFF_LOOPBACK) == 0) {
ip6stat_inc(ip6s_badscope);
goto bad;
}
if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) &&
!(m->m_flags & M_LOOP)) {
/*
* In this case, the packet should come from the loopback
* interface. However, we cannot just check the if_flags,
* because ip6_mloopback() passes the "actual" interface
* as the outgoing/incoming interface.
*/
ip6stat_inc(ip6s_badscope);
goto bad;
}
/*
* The following check is not documented in specs. A malicious
* party may be able to use IPv4 mapped addr to confuse tcp/udp stack
* and bypass security checks (act as if it was from 127.0.0.1 by using
* IPv6 src ::ffff:127.0.0.1). Be cautious.
*
* This check chokes if we are in an SIIT cloud. As none of BSDs
* support IPv4-less kernel compilation, we cannot support SIIT
* environment at all. So, it makes more sense for us to reject any
* malicious packets for non-SIIT environment, than try to do a
* partial support for SIIT environment.
*/
if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) ||
IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) {
ip6stat_inc(ip6s_badscope);
goto bad;
}
/*
* Reject packets with IPv4 compatible addresses (auto tunnel).
*
* The code forbids automatic tunneling as per RFC4213.
*/
if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) ||
IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) {
ip6stat_inc(ip6s_badscope);
goto bad;
}
return (m);
bad:
m_freem(m);
return (NULL);
}
int
ip6_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp)
{
struct mbuf *m;
struct ip6_hdr *ip6;
struct sockaddr_in6 sin6;
struct rtentry *rt = NULL;
int ours = 0;
u_int16_t src_scope, dst_scope;
#if NPF > 0
struct in6_addr odst;
#endif
int srcrt = 0;
KASSERT(*offp == 0);
ip6stat_inc(ip6s_total);
m = *mp = ipv6_check(ifp, *mp);
if (m == NULL)
goto bad;
ip6 = mtod(m, struct ip6_hdr *);
#if NCARP > 0
if (carp_lsdrop(ifp, m, AF_INET6, ip6->ip6_src.s6_addr32,
ip6->ip6_dst.s6_addr32, (ip6->ip6_nxt == IPPROTO_ICMPV6 ? 0 : 1)))
goto bad;
#endif
ip6stat_inc(ip6s_nxthist + ip6->ip6_nxt);
/*
* If the packet has been received on a loopback interface it
* can be destined to any local address, not necessarily to
* an address configured on `ifp'.
*/
if (ifp->if_flags & IFF_LOOPBACK) {
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) {
src_scope = ip6->ip6_src.s6_addr16[1];
ip6->ip6_src.s6_addr16[1] = 0;
}
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) {
dst_scope = ip6->ip6_dst.s6_addr16[1];
ip6->ip6_dst.s6_addr16[1] = 0;
}
}
#if NPF > 0
/*
* Packet filter
*/
odst = ip6->ip6_dst;
if (pf_test(AF_INET6, PF_IN, ifp, mp) != PF_PASS)
goto bad;
m = *mp;
if (m == NULL)
goto bad;
ip6 = mtod(m, struct ip6_hdr *);
srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst);
#endif
/*
* Without embedded scope ID we cannot find link-local
* addresses in the routing table.
*/
if (ifp->if_flags & IFF_LOOPBACK) {
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = src_scope;
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = dst_scope;
} else {
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = htons(ifp->if_index);
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
}
/*
* Be more secure than RFC5095 and scan for type 0 routing headers.
* If pf has already scanned the header chain, do not do it twice.
*/
if (!(m->m_pkthdr.pf.flags & PF_TAG_PROCESSED) &&
ip6_check_rh0hdr(m, offp)) {
ip6stat_inc(ip6s_badoptions);
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, *offp);
m = *mp = NULL;
goto bad;
}
#if NPF > 0
if (pf_ouraddr(m) == 1) {
nxt = ip6_ours(mp, offp, nxt, af);
goto out;
}
#endif
/*
* Multicast check
*/
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
/*
* Make sure M_MCAST is set. It should theoretically
* already be there, but let's play safe because upper
* layers check for this flag.
*/
m->m_flags |= M_MCAST;
/*
* See if we belong to the destination multicast group on the
* arrival interface.
*/
if (in6_hasmulti(&ip6->ip6_dst, ifp))
ours = 1;
#ifdef MROUTING
if (ip6_mforwarding && ip6_mrouter[ifp->if_rdomain]) {
int error;
nxt = ip6_hbhchcheck(&m, offp, &ours);
if (nxt == IPPROTO_DONE)
goto out;
ip6 = mtod(m, struct ip6_hdr *);
/*
* If we are acting as a multicast router, all
* incoming multicast packets are passed to the
* kernel-level multicast forwarding function.
* The packet is returned (relatively) intact; if
* ip6_mforward() returns a non-zero value, the packet
* must be discarded, else it may be accepted below.
*/
KERNEL_LOCK();
error = ip6_mforward(ip6, ifp, m);
KERNEL_UNLOCK();
if (error) {
ip6stat_inc(ip6s_cantforward);
goto bad;
}
if (ours) {
if (af == AF_UNSPEC)
nxt = ip6_ours(mp, offp, nxt, af);
goto out;
}
goto bad;
}
#endif
if (!ours) {
ip6stat_inc(ip6s_notmember);
if (!IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst))
ip6stat_inc(ip6s_cantforward);
goto bad;
}
nxt = ip6_ours(mp, offp, nxt, af);
goto out;
}
/*
* Unicast check
*/
memset(&sin6, 0, sizeof(struct sockaddr_in6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = ip6->ip6_dst;
rt = rtalloc_mpath(sin6tosa(&sin6), &ip6->ip6_src.s6_addr32[0],
m->m_pkthdr.ph_rtableid);
/*
* Accept the packet if the route to the destination is marked
* as local.
*/
if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL)) {
struct in6_ifaddr *ia6 = ifatoia6(rt->rt_ifa);
if (ip6_forwarding == 0 && rt->rt_ifidx != ifp->if_index &&
!((ifp->if_flags & IFF_LOOPBACK) ||
(ifp->if_type == IFT_ENC) ||
(m->m_pkthdr.pf.flags & PF_TAG_TRANSLATE_LOCALHOST))) {
/* received on wrong interface */
#if NCARP > 0
struct ifnet *out_if;
/*
* Virtual IPs on carp interfaces need to be checked
* also against the parent interface and other carp
* interfaces sharing the same parent.
*/
out_if = if_get(rt->rt_ifidx);
if (!(out_if && carp_strict_addr_chk(out_if, ifp))) {
ip6stat_inc(ip6s_wrongif);
if_put(out_if);
goto bad;
}
if_put(out_if);
#else
ip6stat_inc(ip6s_wrongif);
goto bad;
#endif
}
/*
* packets to a tentative, duplicated, or somehow invalid
* address must not be accepted.
*/
if ((ia6->ia6_flags & (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED))) {
char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src));
inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst));
/* address is not ready, so discard the packet. */
nd6log((LOG_INFO,
"%s: packet to an unready address %s->%s\n",
__func__, src, dst));
goto bad;
} else {
nxt = ip6_ours(mp, offp, nxt, af);
goto out;
}
}
#if NCARP > 0
if (ip6->ip6_nxt == IPPROTO_ICMPV6 &&
carp_lsdrop(ifp, m, AF_INET6, ip6->ip6_src.s6_addr32,
ip6->ip6_dst.s6_addr32, 1))
goto bad;
#endif
/*
* Now there is no reason to process the packet if it's not our own
* and we're not a router.
*/
if (!ip6_forwarding) {
ip6stat_inc(ip6s_cantforward);
goto bad;
}
nxt = ip6_hbhchcheck(&m, offp, &ours);
if (nxt == IPPROTO_DONE)
goto out;
if (ours) {
if (af == AF_UNSPEC)
nxt = ip6_ours(mp, offp, nxt, af);
goto out;
}
#ifdef IPSEC
if (ipsec_in_use) {
int rv;
rv = ipsec_forward_check(m, *offp, AF_INET6);
if (rv != 0) {
ip6stat_inc(ip6s_cantforward);
goto bad;
}
/*
* Fall through, forward packet. Outbound IPsec policy
* checking will occur in ip6_forward().
*/
}
#endif /* IPSEC */
ip6_forward(m, rt, srcrt);
*mp = NULL;
return IPPROTO_DONE;
bad:
nxt = IPPROTO_DONE;
m_freemp(mp);
out:
rtfree(rt);
return nxt;
}
/* On error free mbuf and return IPPROTO_DONE. */
int
ip6_hbhchcheck(struct mbuf **mp, int *offp, int *oursp)
{
struct ip6_hdr *ip6;
u_int32_t plen, rtalert = ~0;
int nxt;
ip6 = mtod(*mp, struct ip6_hdr *);
/*
* Process Hop-by-Hop options header if it's contained.
* m may be modified in ip6_hopopts_input().
* If a JumboPayload option is included, plen will also be modified.
*/
plen = (u_int32_t)ntohs(ip6->ip6_plen);
*offp = sizeof(struct ip6_hdr);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
struct ip6_hbh *hbh;
if (ip6_hopopts_input(mp, offp, &plen, &rtalert))
goto bad; /* m have already been freed */
/* adjust pointer */
ip6 = mtod(*mp, struct ip6_hdr *);
/*
* if the payload length field is 0 and the next header field
* indicates Hop-by-Hop Options header, then a Jumbo Payload
* option MUST be included.
*/
if (ip6->ip6_plen == 0 && plen == 0) {
/*
* Note that if a valid jumbo payload option is
* contained, ip6_hopopts_input() must set a valid
* (non-zero) payload length to the variable plen.
*/
ip6stat_inc(ip6s_badoptions);
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
(caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
goto bad;
}
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, *mp,
sizeof(struct ip6_hdr), sizeof(struct ip6_hbh));
if (hbh == NULL) {
ip6stat_inc(ip6s_tooshort);
goto bad;
}
nxt = hbh->ip6h_nxt;
/*
* accept the packet if a router alert option is included
* and we act as an IPv6 router.
*/
if (rtalert != ~0 && ip6_forwarding && oursp != NULL)
*oursp = 1;
} else
nxt = ip6->ip6_nxt;
/*
* Check that the amount of data in the buffers
* is as at least much as the IPv6 header would have us expect.
* Trim mbufs if longer than we expect.
* Drop packet if shorter than we expect.
*/
if ((*mp)->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) {
ip6stat_inc(ip6s_tooshort);
m_freemp(mp);
goto bad;
}
if ((*mp)->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) {
if ((*mp)->m_len == (*mp)->m_pkthdr.len) {
(*mp)->m_len = sizeof(struct ip6_hdr) + plen;
(*mp)->m_pkthdr.len = sizeof(struct ip6_hdr) + plen;
} else {
m_adj((*mp), sizeof(struct ip6_hdr) + plen -
(*mp)->m_pkthdr.len);
}
}
return nxt;
bad:
return IPPROTO_DONE;
}
/* scan packet for RH0 routing header. Mostly stolen from pf.c:pf_test() */
int
ip6_check_rh0hdr(struct mbuf *m, int *offp)
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct ip6_rthdr rthdr;
struct ip6_ext opt6;
u_int8_t proto = ip6->ip6_nxt;
int done = 0, lim, off, rh_cnt = 0;
off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr);
lim = min(m->m_pkthdr.len, ntohs(ip6->ip6_plen) + sizeof(*ip6));
do {
switch (proto) {
case IPPROTO_ROUTING:
if (rh_cnt++) {
/* more than one rh header present */
*offp = off;
return (1);
}
if (off + sizeof(rthdr) > lim) {
/* packet to short to make sense */
*offp = off;
return (1);
}
m_copydata(m, off, sizeof(rthdr), &rthdr);
if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) {
*offp = off +
offsetof(struct ip6_rthdr, ip6r_type);
return (1);
}
off += (rthdr.ip6r_len + 1) * 8;
proto = rthdr.ip6r_nxt;
break;
case IPPROTO_AH:
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
/* get next header and header length */
if (off + sizeof(opt6) > lim) {
/*
* Packet to short to make sense, we could
* reject the packet but as a router we
* should not do that so forward it.
*/
return (0);
}
m_copydata(m, off, sizeof(opt6), &opt6);
if (proto == IPPROTO_AH)
off += (opt6.ip6e_len + 2) * 4;
else
off += (opt6.ip6e_len + 1) * 8;
proto = opt6.ip6e_nxt;
break;
case IPPROTO_FRAGMENT:
default:
/* end of header stack */
done = 1;
break;
}
} while (!done);
return (0);
}
/*
* Hop-by-Hop options header processing. If a valid jumbo payload option is
* included, the real payload length will be stored in plenp.
* On error free mbuf and return -1.
*
* rtalertp - XXX: should be stored in a more smart way
*/
int
ip6_hopopts_input(struct mbuf **mp, int *offp, u_int32_t *plenp,
u_int32_t *rtalertp)
{
int off = *offp, hbhlen;
struct ip6_hbh *hbh;
/* validation of the length of the header */
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, *mp,
sizeof(struct ip6_hdr), sizeof(struct ip6_hbh));
if (hbh == NULL) {
ip6stat_inc(ip6s_tooshort);
return -1;
}
hbhlen = (hbh->ip6h_len + 1) << 3;
IP6_EXTHDR_GET(hbh, struct ip6_hbh *, *mp, sizeof(struct ip6_hdr),
hbhlen);
if (hbh == NULL) {
ip6stat_inc(ip6s_tooshort);
return -1;
}
off += hbhlen;
hbhlen -= sizeof(struct ip6_hbh);
if (ip6_process_hopopts(mp, (u_int8_t *)hbh + sizeof(struct ip6_hbh),
hbhlen, rtalertp, plenp) < 0)
return (-1);
*offp = off;
return (0);
}
/*
* Search header for all Hop-by-hop options and process each option.
* This function is separate from ip6_hopopts_input() in order to
* handle a case where the sending node itself process its hop-by-hop
* options header. In such a case, the function is called from ip6_output().
* On error free mbuf and return -1.
*
* The function assumes that hbh header is located right after the IPv6 header
* (RFC2460 p7), opthead is pointer into data content in m, and opthead to
* opthead + hbhlen is located in continuous memory region.
*/
int
ip6_process_hopopts(struct mbuf **mp, u_int8_t *opthead, int hbhlen,
u_int32_t *rtalertp, u_int32_t *plenp)
{
struct ip6_hdr *ip6;
int optlen = 0;
u_int8_t *opt = opthead;
u_int16_t rtalert_val;
u_int32_t jumboplen;
const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh);
for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) {
switch (*opt) {
case IP6OPT_PAD1:
optlen = 1;
break;
case IP6OPT_PADN:
if (hbhlen < IP6OPT_MINLEN) {
ip6stat_inc(ip6s_toosmall);
goto bad;
}
optlen = *(opt + 1) + 2;
break;
case IP6OPT_ROUTER_ALERT:
/* XXX may need check for alignment */
if (hbhlen < IP6OPT_RTALERT_LEN) {
ip6stat_inc(ip6s_toosmall);
goto bad;
}
if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) {
/* XXX stat */
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 1 - opthead);
return (-1);
}
optlen = IP6OPT_RTALERT_LEN;
memcpy((caddr_t)&rtalert_val, (caddr_t)(opt + 2), 2);
*rtalertp = ntohs(rtalert_val);
break;
case IP6OPT_JUMBO:
/* XXX may need check for alignment */
if (hbhlen < IP6OPT_JUMBO_LEN) {
ip6stat_inc(ip6s_toosmall);
goto bad;
}
if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) {
/* XXX stat */
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 1 - opthead);
return (-1);
}
optlen = IP6OPT_JUMBO_LEN;
/*
* IPv6 packets that have non 0 payload length
* must not contain a jumbo payload option.
*/
ip6 = mtod(*mp, struct ip6_hdr *);
if (ip6->ip6_plen) {
ip6stat_inc(ip6s_badoptions);
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt - opthead);
return (-1);
}
/*
* We may see jumbolen in unaligned location, so
* we'd need to perform memcpy().
*/
memcpy(&jumboplen, opt + 2, sizeof(jumboplen));
jumboplen = (u_int32_t)htonl(jumboplen);
#if 1
/*
* if there are multiple jumbo payload options,
* *plenp will be non-zero and the packet will be
* rejected.
* the behavior may need some debate in ipngwg -
* multiple options does not make sense, however,
* there's no explicit mention in specification.
*/
if (*plenp != 0) {
ip6stat_inc(ip6s_badoptions);
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 2 - opthead);
return (-1);
}
#endif
/*
* jumbo payload length must be larger than 65535.
*/
if (jumboplen <= IPV6_MAXPACKET) {
ip6stat_inc(ip6s_badoptions);
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
erroff + opt + 2 - opthead);
return (-1);
}
*plenp = jumboplen;
break;
default: /* unknown option */
if (hbhlen < IP6OPT_MINLEN) {
ip6stat_inc(ip6s_toosmall);
goto bad;
}
optlen = ip6_unknown_opt(mp, opt,
erroff + opt - opthead);
if (optlen == -1)
return (-1);
optlen += 2;
break;
}
}
return (0);
bad:
m_freemp(mp);
return (-1);
}
/*
* Unknown option processing.
* The third argument `off' is the offset from the IPv6 header to the option,
* which allows returning an ICMPv6 error even if the IPv6 header and the
* option header are not continuous.
* On error free mbuf and return -1.
*/
int
ip6_unknown_opt(struct mbuf **mp, u_int8_t *optp, int off)
{
struct ip6_hdr *ip6;
switch (IP6OPT_TYPE(*optp)) {
case IP6OPT_TYPE_SKIP: /* ignore the option */
return ((int)*(optp + 1));
case IP6OPT_TYPE_DISCARD: /* silently discard */
m_freemp(mp);
return (-1);
case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */
ip6stat_inc(ip6s_badoptions);
icmp6_error(*mp, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off);
return (-1);
case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */
ip6stat_inc(ip6s_badoptions);
ip6 = mtod(*mp, struct ip6_hdr *);
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
((*mp)->m_flags & (M_BCAST|M_MCAST)))
m_freemp(mp);
else
icmp6_error(*mp, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_OPTION, off);
return (-1);
}
m_freemp(mp); /* XXX: NOTREACHED */
return (-1);
}
/*
* Create the "control" list for this pcb.
*
* The routine will be called from upper layer handlers like udp_input().
* Thus the routine assumes that the caller (udp_input) have already
* called IP6_EXTHDR_CHECK() and all the extension headers are located in the
* very first mbuf on the mbuf chain.
* We may want to add some infinite loop prevention or sanity checks for safety.
* (This applies only when you are using KAME mbuf chain restriction, i.e.
* you are using IP6_EXTHDR_CHECK() not m_pulldown())
*/
void
ip6_savecontrol(struct inpcb *in6p, struct mbuf *m, struct mbuf **mp)
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (in6p->inp_socket->so_options & SO_TIMESTAMP) {
struct timeval tv;
m_microtime(m, &tv);
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
SCM_TIMESTAMP, SOL_SOCKET);
if (*mp)
mp = &(*mp)->m_next;
}
/* RFC 2292 sec. 5 */
if ((in6p->inp_flags & IN6P_PKTINFO) != 0) {
struct in6_pktinfo pi6;
memcpy(&pi6.ipi6_addr, &ip6->ip6_dst, sizeof(struct in6_addr));
if (IN6_IS_SCOPE_EMBED(&pi6.ipi6_addr))
pi6.ipi6_addr.s6_addr16[1] = 0;
pi6.ipi6_ifindex = m ? m->m_pkthdr.ph_ifidx : 0;
*mp = sbcreatecontrol((caddr_t) &pi6,
sizeof(struct in6_pktinfo),
IPV6_PKTINFO, IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
}
if ((in6p->inp_flags & IN6P_HOPLIMIT) != 0) {
int hlim = ip6->ip6_hlim & 0xff;
*mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int),
IPV6_HOPLIMIT, IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
}
if ((in6p->inp_flags & IN6P_TCLASS) != 0) {
u_int32_t flowinfo;
int tclass;
flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK);
flowinfo >>= 20;
tclass = flowinfo & 0xff;
*mp = sbcreatecontrol((caddr_t)&tclass, sizeof(tclass),
IPV6_TCLASS, IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
}
/*
* IPV6_HOPOPTS socket option. Recall that we required super-user
* privilege for the option (see ip6_ctloutput), but it might be too
* strict, since there might be some hop-by-hop options which can be
* returned to normal user.
* See also RFC 2292 section 6 (or RFC 3542 section 8).
*/
if ((in6p->inp_flags & IN6P_HOPOPTS) != 0) {
/*
* Check if a hop-by-hop options header is contained in the
* received packet, and if so, store the options as ancillary
* data. Note that a hop-by-hop options header must be
* just after the IPv6 header, which is assured through the
* IPv6 input processing.
*/
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
struct ip6_hbh *hbh;
int hbhlen = 0;
struct mbuf *ext;
ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr),
ip6->ip6_nxt);
if (ext == NULL) {
ip6stat_inc(ip6s_tooshort);
return;
}
hbh = mtod(ext, struct ip6_hbh *);
hbhlen = (hbh->ip6h_len + 1) << 3;
if (hbhlen != ext->m_len) {
m_freem(ext);
ip6stat_inc(ip6s_tooshort);
return;
}
/*
* XXX: We copy the whole header even if a
* jumbo payload option is included, the option which
* is to be removed before returning according to
* RFC2292.
* Note: this constraint is removed in RFC3542.
*/
*mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
IPV6_HOPOPTS,
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
m_freem(ext);
}
}
/* IPV6_DSTOPTS and IPV6_RTHDR socket options */
if ((in6p->inp_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) {
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);
/*
* Search for destination options headers or routing
* header(s) through the header chain, and stores each
* header as ancillary data.
* Note that the order of the headers remains in
* the chain of ancillary data.
*/
while (1) { /* is explicit loop prevention necessary? */
struct ip6_ext *ip6e = NULL;
int elen;
struct mbuf *ext = NULL;
/*
* if it is not an extension header, don't try to
* pull it from the chain.
*/
switch (nxt) {
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
case IPPROTO_HOPOPTS:
case IPPROTO_AH: /* is it possible? */
break;
default:
goto loopend;
}
ext = ip6_pullexthdr(m, off, nxt);
if (ext == NULL) {
ip6stat_inc(ip6s_tooshort);
return;
}
ip6e = mtod(ext, struct ip6_ext *);
if (nxt == IPPROTO_AH)
elen = (ip6e->ip6e_len + 2) << 2;
else
elen = (ip6e->ip6e_len + 1) << 3;
if (elen != ext->m_len) {
m_freem(ext);
ip6stat_inc(ip6s_tooshort);
return;
}
switch (nxt) {
case IPPROTO_DSTOPTS:
if (!(in6p->inp_flags & IN6P_DSTOPTS))
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
IPV6_DSTOPTS,
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
break;
case IPPROTO_ROUTING:
if (!(in6p->inp_flags & IN6P_RTHDR))
break;
*mp = sbcreatecontrol((caddr_t)ip6e, elen,
IPV6_RTHDR,
IPPROTO_IPV6);
if (*mp)
mp = &(*mp)->m_next;
break;
case IPPROTO_HOPOPTS:
case IPPROTO_AH: /* is it possible? */
break;
default:
/*
* other cases have been filtered in the above.
* none will visit this case. here we supply
* the code just in case (nxt overwritten or
* other cases).
*/
m_freem(ext);
goto loopend;
}
/* proceed with the next header. */
off += elen;
nxt = ip6e->ip6e_nxt;
ip6e = NULL;
m_freem(ext);
ext = NULL;
}
loopend:
;
}
}
/*
* pull single extension header from mbuf chain. returns single mbuf that
* contains the result, or NULL on error.
*/
struct mbuf *
ip6_pullexthdr(struct mbuf *m, size_t off, int nxt)
{
struct ip6_ext ip6e;
size_t elen;
struct mbuf *n;
#ifdef DIAGNOSTIC
switch (nxt) {
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
case IPPROTO_HOPOPTS:
case IPPROTO_AH: /* is it possible? */
break;
default:
printf("ip6_pullexthdr: invalid nxt=%d\n", nxt);
}
#endif
if (off + sizeof(ip6e) > m->m_pkthdr.len)
return NULL;
m_copydata(m, off, sizeof(ip6e), &ip6e);
if (nxt == IPPROTO_AH)
elen = (ip6e.ip6e_len + 2) << 2;
else
elen = (ip6e.ip6e_len + 1) << 3;
if (off + elen > m->m_pkthdr.len)
return NULL;
MGET(n, M_DONTWAIT, MT_DATA);
if (n && elen >= MLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
ip6stat_inc(ip6s_idropped);
return NULL;
}
n->m_len = 0;
if (elen >= m_trailingspace(n)) {
m_free(n);
return NULL;
}
m_copydata(m, off, elen, mtod(n, caddr_t));
n->m_len = elen;
return n;
}
/*
* Get offset to the previous header followed by the header
* currently processed.
*/
int
ip6_get_prevhdr(struct mbuf *m, int off)
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (off == sizeof(struct ip6_hdr)) {
return offsetof(struct ip6_hdr, ip6_nxt);
} else if (off < sizeof(struct ip6_hdr)) {
panic("%s: off < sizeof(struct ip6_hdr)", __func__);
} else {
int len, nlen, nxt;
struct ip6_ext ip6e;
nxt = ip6->ip6_nxt;
len = sizeof(struct ip6_hdr);
nlen = 0;
while (len < off) {
m_copydata(m, len, sizeof(ip6e), &ip6e);
switch (nxt) {
case IPPROTO_FRAGMENT:
nlen = sizeof(struct ip6_frag);
break;
case IPPROTO_AH:
nlen = (ip6e.ip6e_len + 2) << 2;
break;
default:
nlen = (ip6e.ip6e_len + 1) << 3;
break;
}
len += nlen;
nxt = ip6e.ip6e_nxt;
}
return (len - nlen);
}
}
/*
* get next header offset. m will be retained.
*/
int
ip6_nexthdr(struct mbuf *m, int off, int proto, int *nxtp)
{
struct ip6_hdr ip6;
struct ip6_ext ip6e;
struct ip6_frag fh;
/* just in case */
if (m == NULL)
panic("%s: m == NULL", __func__);
if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off)
return -1;
switch (proto) {
case IPPROTO_IPV6:
if (m->m_pkthdr.len < off + sizeof(ip6))
return -1;
m_copydata(m, off, sizeof(ip6), &ip6);
if (nxtp)
*nxtp = ip6.ip6_nxt;
off += sizeof(ip6);
return off;
case IPPROTO_FRAGMENT:
/*
* terminate parsing if it is not the first fragment,
* it does not make sense to parse through it.
*/
if (m->m_pkthdr.len < off + sizeof(fh))
return -1;
m_copydata(m, off, sizeof(fh), &fh);
if ((fh.ip6f_offlg & IP6F_OFF_MASK) != 0)
return -1;
if (nxtp)
*nxtp = fh.ip6f_nxt;
off += sizeof(struct ip6_frag);
return off;
case IPPROTO_AH:
if (m->m_pkthdr.len < off + sizeof(ip6e))
return -1;
m_copydata(m, off, sizeof(ip6e), &ip6e);
if (nxtp)
*nxtp = ip6e.ip6e_nxt;
off += (ip6e.ip6e_len + 2) << 2;
if (m->m_pkthdr.len < off)
return -1;
return off;
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
if (m->m_pkthdr.len < off + sizeof(ip6e))
return -1;
m_copydata(m, off, sizeof(ip6e), &ip6e);
if (nxtp)
*nxtp = ip6e.ip6e_nxt;
off += (ip6e.ip6e_len + 1) << 3;
if (m->m_pkthdr.len < off)
return -1;
return off;
case IPPROTO_NONE:
case IPPROTO_ESP:
case IPPROTO_IPCOMP:
/* give up */
return -1;
default:
return -1;
}
return -1;
}
/*
* get offset for the last header in the chain. m will be kept untainted.
*/
int
ip6_lasthdr(struct mbuf *m, int off, int proto, int *nxtp)
{
int newoff;
int nxt;
if (!nxtp) {
nxt = -1;
nxtp = &nxt;
}
while (1) {
newoff = ip6_nexthdr(m, off, proto, nxtp);
if (newoff < 0)
return off;
else if (newoff < off)
return -1; /* invalid */
else if (newoff == off)
return newoff;
off = newoff;
proto = *nxtp;
}
}
/*
* System control for IP6
*/
const u_char inet6ctlerrmap[PRC_NCMDS] = {
0, 0, 0, 0,
0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
EMSGSIZE, EHOSTUNREACH, 0, 0,
0, 0, 0, 0,
ENOPROTOOPT
};
#ifdef MROUTING
extern int ip6_mrtproto;
#endif
const struct sysctl_bounded_args ipv6ctl_vars[] = {
{ IPV6CTL_DAD_PENDING, &ip6_dad_pending, SYSCTL_INT_READONLY },
#ifdef MROUTING
{ IPV6CTL_MRTPROTO, &ip6_mrtproto, SYSCTL_INT_READONLY },
#endif
{ IPV6CTL_FORWARDING, &ip6_forwarding, 0, 1 },
{ IPV6CTL_SENDREDIRECTS, &ip6_sendredirects, 0, 1 },
{ IPV6CTL_DEFHLIM, &ip6_defhlim, 0, 255 },
{ IPV6CTL_MAXFRAGPACKETS, &ip6_maxfragpackets, 0, 1000 },
{ IPV6CTL_LOG_INTERVAL, &ip6_log_interval, 0, INT_MAX },
{ IPV6CTL_HDRNESTLIMIT, &ip6_hdrnestlimit, 0, 100 },
{ IPV6CTL_DAD_COUNT, &ip6_dad_count, 0, 10 },
{ IPV6CTL_AUTO_FLOWLABEL, &ip6_auto_flowlabel, 0, 1 },
{ IPV6CTL_DEFMCASTHLIM, &ip6_defmcasthlim, 0, 255 },
{ IPV6CTL_USE_DEPRECATED, &ip6_use_deprecated, 0, 1 },
{ IPV6CTL_MAXFRAGS, &ip6_maxfrags, 0, 1000 },
{ IPV6CTL_MFORWARDING, &ip6_mforwarding, 0, 1 },
{ IPV6CTL_MULTIPATH, &ip6_multipath, 0, 1 },
{ IPV6CTL_MCAST_PMTU, &ip6_mcast_pmtu, 0, 1 },
{ IPV6CTL_NEIGHBORGCTHRESH, &ip6_neighborgcthresh, -1, 5 * 2048 },
{ IPV6CTL_MAXDYNROUTES, &ip6_maxdynroutes, -1, 5 * 4096 },
};
int
ip6_sysctl_ip6stat(void *oldp, size_t *oldlenp, void *newp)
{
struct ip6stat *ip6stat;
int ret;
CTASSERT(sizeof(*ip6stat) == (ip6s_ncounters * sizeof(uint64_t)));
ip6stat = malloc(sizeof(*ip6stat), M_TEMP, M_WAITOK);
counters_read(ip6counters, (uint64_t *)ip6stat, ip6s_ncounters);
ret = sysctl_rdstruct(oldp, oldlenp, newp,
ip6stat, sizeof(*ip6stat));
free(ip6stat, M_TEMP, sizeof(*ip6stat));
return (ret);
}
int
ip6_sysctl_soiikey(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
uint8_t oldkey[IP6_SOIIKEY_LEN];
int error;
error = suser(curproc);
if (error != 0)
return (error);
memcpy(oldkey, ip6_soiikey, sizeof(oldkey));
error = sysctl_struct(oldp, oldlenp, newp, newlen, ip6_soiikey,
sizeof(ip6_soiikey));
return (error);
}
int
ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
#ifdef MROUTING
extern struct mrt6stat mrt6stat;
#endif
int error;
/* Almost all sysctl names at this level are terminal. */
if (namelen != 1 && name[0] != IPV6CTL_IFQUEUE)
return (ENOTDIR);
switch (name[0]) {
case IPV6CTL_STATS:
return (ip6_sysctl_ip6stat(oldp, oldlenp, newp));
#ifdef MROUTING
case IPV6CTL_MRTSTATS:
if (newp != NULL)
return (EPERM);
NET_LOCK();
error = sysctl_struct(oldp, oldlenp, newp, newlen,
&mrt6stat, sizeof(mrt6stat));
NET_UNLOCK();
return (error);
case IPV6CTL_MRTMIF:
if (newp)
return (EPERM);
NET_LOCK();
error = mrt6_sysctl_mif(oldp, oldlenp);
NET_UNLOCK();
return (error);
case IPV6CTL_MRTMFC:
if (newp)
return (EPERM);
NET_LOCK();
error = mrt6_sysctl_mfc(oldp, oldlenp);
NET_UNLOCK();
return (error);
#else
case IPV6CTL_MRTSTATS:
case IPV6CTL_MRTPROTO:
case IPV6CTL_MRTMIF:
case IPV6CTL_MRTMFC:
return (EOPNOTSUPP);
#endif
case IPV6CTL_MTUDISCTIMEOUT:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&ip6_mtudisc_timeout, 0, INT_MAX);
rt_timer_queue_change(&icmp6_mtudisc_timeout_q,
ip6_mtudisc_timeout);
NET_UNLOCK();
return (error);
case IPV6CTL_IFQUEUE:
return (sysctl_niq(name + 1, namelen - 1,
oldp, oldlenp, newp, newlen, &ip6intrq));
case IPV6CTL_SOIIKEY:
return (ip6_sysctl_soiikey(oldp, oldlenp, newp, newlen));
default:
NET_LOCK();
error = sysctl_bounded_arr(ipv6ctl_vars, nitems(ipv6ctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
/* NOTREACHED */
}
void
ip6_send_dispatch(void *xmq)
{
struct mbuf_queue *mq = xmq;
struct mbuf *m;
struct mbuf_list ml;
mq_delist(mq, &ml);
if (ml_empty(&ml))
return;
NET_LOCK();
while ((m = ml_dequeue(&ml)) != NULL) {
ip6_output(m, NULL, NULL, 0, NULL, NULL);
}
NET_UNLOCK();
}
void
ip6_send(struct mbuf *m)
{
mq_enqueue(&ip6send_mq, m);
task_add(net_tq(0), &ip6send_task);
}
21
2
18
1
19
2
9
9
5
2
5
4
2
9
7
13
4
12
1
2
3
8
1
6
2
1815
1813
15
10
10
6
14
17
1
13
4
17
1
5
5
6
5
5
5
2
5
15
7
16
8
16
859
847
158
857
445
446
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
/* $OpenBSD: kern_resource.c,v 1.74 2022/05/28 03:47:43 deraadt Exp $ */
/* $NetBSD: kern_resource.c,v 1.38 1996/10/23 07:19:38 matthias Exp $ */
/*-
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_resource.c 8.5 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/resourcevar.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/ktrace.h>
#include <sys/sched.h>
#include <sys/signalvar.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
/* Resource usage check interval in msec */
#define RUCHECK_INTERVAL 1000
/* SIGXCPU interval in seconds of process runtime */
#define SIGXCPU_INTERVAL 5
struct plimit *lim_copy(struct plimit *);
struct plimit *lim_write_begin(void);
void lim_write_commit(struct plimit *);
void tuagg_sub(struct tusage *, struct proc *);
/*
* Patchable maximum data and stack limits.
*/
rlim_t maxdmap = MAXDSIZ;
rlim_t maxsmap = MAXSSIZ;
/*
* Serializes resource limit updates.
* This lock has to be held together with ps_mtx when updating
* the process' ps_limit.
*/
struct rwlock rlimit_lock = RWLOCK_INITIALIZER("rlimitlk");
/*
* Resource controls and accounting.
*/
int
sys_getpriority(struct proc *curp, void *v, register_t *retval)
{
struct sys_getpriority_args /* {
syscallarg(int) which;
syscallarg(id_t) who;
} */ *uap = v;
struct process *pr;
int low = NZERO + PRIO_MAX + 1;
switch (SCARG(uap, which)) {
case PRIO_PROCESS:
if (SCARG(uap, who) == 0)
pr = curp->p_p;
else
pr = prfind(SCARG(uap, who));
if (pr == NULL)
break;
if (pr->ps_nice < low)
low = pr->ps_nice;
break;
case PRIO_PGRP: {
struct pgrp *pg;
if (SCARG(uap, who) == 0)
pg = curp->p_p->ps_pgrp;
else if ((pg = pgfind(SCARG(uap, who))) == NULL)
break;
LIST_FOREACH(pr, &pg->pg_members, ps_pglist)
if (pr->ps_nice < low)
low = pr->ps_nice;
break;
}
case PRIO_USER:
if (SCARG(uap, who) == 0)
SCARG(uap, who) = curp->p_ucred->cr_uid;
LIST_FOREACH(pr, &allprocess, ps_list)
if (pr->ps_ucred->cr_uid == SCARG(uap, who) &&
pr->ps_nice < low)
low = pr->ps_nice;
break;
default:
return (EINVAL);
}
if (low == NZERO + PRIO_MAX + 1)
return (ESRCH);
*retval = low - NZERO;
return (0);
}
int
sys_setpriority(struct proc *curp, void *v, register_t *retval)
{
struct sys_setpriority_args /* {
syscallarg(int) which;
syscallarg(id_t) who;
syscallarg(int) prio;
} */ *uap = v;
struct process *pr;
int found = 0, error = 0;
switch (SCARG(uap, which)) {
case PRIO_PROCESS:
if (SCARG(uap, who) == 0)
pr = curp->p_p;
else
pr = prfind(SCARG(uap, who));
if (pr == NULL)
break;
error = donice(curp, pr, SCARG(uap, prio));
found = 1;
break;
case PRIO_PGRP: {
struct pgrp *pg;
if (SCARG(uap, who) == 0)
pg = curp->p_p->ps_pgrp;
else if ((pg = pgfind(SCARG(uap, who))) == NULL)
break;
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
error = donice(curp, pr, SCARG(uap, prio));
found = 1;
}
break;
}
case PRIO_USER:
if (SCARG(uap, who) == 0)
SCARG(uap, who) = curp->p_ucred->cr_uid;
LIST_FOREACH(pr, &allprocess, ps_list)
if (pr->ps_ucred->cr_uid == SCARG(uap, who)) {
error = donice(curp, pr, SCARG(uap, prio));
found = 1;
}
break;
default:
return (EINVAL);
}
if (!found)
return (ESRCH);
return (error);
}
int
donice(struct proc *curp, struct process *chgpr, int n)
{
struct ucred *ucred = curp->p_ucred;
struct proc *p;
int s;
if (ucred->cr_uid != 0 && ucred->cr_ruid != 0 &&
ucred->cr_uid != chgpr->ps_ucred->cr_uid &&
ucred->cr_ruid != chgpr->ps_ucred->cr_uid)
return (EPERM);
if (n > PRIO_MAX)
n = PRIO_MAX;
if (n < PRIO_MIN)
n = PRIO_MIN;
n += NZERO;
if (n < chgpr->ps_nice && suser(curp))
return (EACCES);
chgpr->ps_nice = n;
SCHED_LOCK(s);
TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link) {
setpriority(p, p->p_estcpu, n);
}
SCHED_UNLOCK(s);
return (0);
}
int
sys_setrlimit(struct proc *p, void *v, register_t *retval)
{
struct sys_setrlimit_args /* {
syscallarg(int) which;
syscallarg(const struct rlimit *) rlp;
} */ *uap = v;
struct rlimit alim;
int error;
error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim,
sizeof (struct rlimit));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrrlimit(p, &alim);
#endif
return (dosetrlimit(p, SCARG(uap, which), &alim));
}
int
dosetrlimit(struct proc *p, u_int which, struct rlimit *limp)
{
struct rlimit *alimp;
struct plimit *limit;
rlim_t maxlim;
int error;
if (which >= RLIM_NLIMITS || limp->rlim_cur > limp->rlim_max)
return (EINVAL);
rw_enter_write(&rlimit_lock);
alimp = &p->p_p->ps_limit->pl_rlimit[which];
if (limp->rlim_max > alimp->rlim_max) {
if ((error = suser(p)) != 0) {
rw_exit_write(&rlimit_lock);
return (error);
}
}
/* Get exclusive write access to the limit structure. */
limit = lim_write_begin();
alimp = &limit->pl_rlimit[which];
switch (which) {
case RLIMIT_DATA:
maxlim = maxdmap;
break;
case RLIMIT_STACK:
maxlim = maxsmap;
break;
case RLIMIT_NOFILE:
maxlim = maxfiles;
break;
case RLIMIT_NPROC:
maxlim = maxprocess;
break;
default:
maxlim = RLIM_INFINITY;
break;
}
if (limp->rlim_max > maxlim)
limp->rlim_max = maxlim;
if (limp->rlim_cur > limp->rlim_max)
limp->rlim_cur = limp->rlim_max;
if (which == RLIMIT_CPU && limp->rlim_cur != RLIM_INFINITY &&
alimp->rlim_cur == RLIM_INFINITY)
timeout_add_msec(&p->p_p->ps_rucheck_to, RUCHECK_INTERVAL);
if (which == RLIMIT_STACK) {
/*
* Stack is allocated to the max at exec time with only
* "rlim_cur" bytes accessible. If stack limit is going
* up make more accessible, if going down make inaccessible.
*/
if (limp->rlim_cur != alimp->rlim_cur) {
vaddr_t addr;
vsize_t size;
vm_prot_t prot;
struct vmspace *vm = p->p_vmspace;
if (limp->rlim_cur > alimp->rlim_cur) {
prot = PROT_READ | PROT_WRITE;
size = limp->rlim_cur - alimp->rlim_cur;
#ifdef MACHINE_STACK_GROWS_UP
addr = (vaddr_t)vm->vm_maxsaddr +
alimp->rlim_cur;
#else
addr = (vaddr_t)vm->vm_minsaddr -
limp->rlim_cur;
#endif
} else {
prot = PROT_NONE;
size = alimp->rlim_cur - limp->rlim_cur;
#ifdef MACHINE_STACK_GROWS_UP
addr = (vaddr_t)vm->vm_maxsaddr +
limp->rlim_cur;
#else
addr = (vaddr_t)vm->vm_minsaddr -
alimp->rlim_cur;
#endif
}
addr = trunc_page(addr);
size = round_page(size);
KERNEL_LOCK();
(void) uvm_map_protect(&vm->vm_map,
addr, addr+size, prot, FALSE);
KERNEL_UNLOCK();
}
}
*alimp = *limp;
lim_write_commit(limit);
rw_exit_write(&rlimit_lock);
return (0);
}
int
sys_getrlimit(struct proc *p, void *v, register_t *retval)
{
struct sys_getrlimit_args /* {
syscallarg(int) which;
syscallarg(struct rlimit *) rlp;
} */ *uap = v;
struct plimit *limit;
struct rlimit alimp;
int error;
if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS)
return (EINVAL);
limit = lim_read_enter();
alimp = limit->pl_rlimit[SCARG(uap, which)];
lim_read_leave(limit);
error = copyout(&alimp, SCARG(uap, rlp), sizeof(struct rlimit));
#ifdef KTRACE
if (error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrrlimit(p, &alimp);
#endif
return (error);
}
void
tuagg_sub(struct tusage *tup, struct proc *p)
{
timespecadd(&tup->tu_runtime, &p->p_rtime, &tup->tu_runtime);
tup->tu_uticks += p->p_uticks;
tup->tu_sticks += p->p_sticks;
tup->tu_iticks += p->p_iticks;
}
/*
* Aggregate a single thread's immediate time counts into the running
* totals for the thread and process
*/
void
tuagg_unlocked(struct process *pr, struct proc *p)
{
tuagg_sub(&pr->ps_tu, p);
tuagg_sub(&p->p_tu, p);
timespecclear(&p->p_rtime);
p->p_uticks = 0;
p->p_sticks = 0;
p->p_iticks = 0;
}
void
tuagg(struct process *pr, struct proc *p)
{
int s;
SCHED_LOCK(s);
tuagg_unlocked(pr, p);
SCHED_UNLOCK(s);
}
/*
* Transform the running time and tick information in a struct tusage
* into user, system, and interrupt time usage.
*/
void
calctsru(struct tusage *tup, struct timespec *up, struct timespec *sp,
struct timespec *ip)
{
u_quad_t st, ut, it;
int freq;
st = tup->tu_sticks;
ut = tup->tu_uticks;
it = tup->tu_iticks;
if (st + ut + it == 0) {
timespecclear(up);
timespecclear(sp);
if (ip != NULL)
timespecclear(ip);
return;
}
freq = stathz ? stathz : hz;
st = st * 1000000000 / freq;
sp->tv_sec = st / 1000000000;
sp->tv_nsec = st % 1000000000;
ut = ut * 1000000000 / freq;
up->tv_sec = ut / 1000000000;
up->tv_nsec = ut % 1000000000;
if (ip != NULL) {
it = it * 1000000000 / freq;
ip->tv_sec = it / 1000000000;
ip->tv_nsec = it % 1000000000;
}
}
void
calcru(struct tusage *tup, struct timeval *up, struct timeval *sp,
struct timeval *ip)
{
struct timespec u, s, i;
calctsru(tup, &u, &s, ip != NULL ? &i : NULL);
TIMESPEC_TO_TIMEVAL(up, &u);
TIMESPEC_TO_TIMEVAL(sp, &s);
if (ip != NULL)
TIMESPEC_TO_TIMEVAL(ip, &i);
}
int
sys_getrusage(struct proc *p, void *v, register_t *retval)
{
struct sys_getrusage_args /* {
syscallarg(int) who;
syscallarg(struct rusage *) rusage;
} */ *uap = v;
struct rusage ru;
int error;
error = dogetrusage(p, SCARG(uap, who), &ru);
if (error == 0) {
error = copyout(&ru, SCARG(uap, rusage), sizeof(ru));
#ifdef KTRACE
if (error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrrusage(p, &ru);
#endif
}
return (error);
}
int
dogetrusage(struct proc *p, int who, struct rusage *rup)
{
struct process *pr = p->p_p;
struct proc *q;
switch (who) {
case RUSAGE_SELF:
/* start with the sum of dead threads, if any */
if (pr->ps_ru != NULL)
*rup = *pr->ps_ru;
else
memset(rup, 0, sizeof(*rup));
/* add on all living threads */
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
ruadd(rup, &q->p_ru);
tuagg(pr, q);
}
calcru(&pr->ps_tu, &rup->ru_utime, &rup->ru_stime, NULL);
break;
case RUSAGE_THREAD:
*rup = p->p_ru;
calcru(&p->p_tu, &rup->ru_utime, &rup->ru_stime, NULL);
break;
case RUSAGE_CHILDREN:
*rup = pr->ps_cru;
break;
default:
return (EINVAL);
}
return (0);
}
void
ruadd(struct rusage *ru, struct rusage *ru2)
{
long *ip, *ip2;
int i;
timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
if (ru->ru_maxrss < ru2->ru_maxrss)
ru->ru_maxrss = ru2->ru_maxrss;
ip = &ru->ru_first; ip2 = &ru2->ru_first;
for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
*ip++ += *ip2++;
}
/*
* Check if the process exceeds its cpu resource allocation.
* If over max, kill it.
*/
void
rucheck(void *arg)
{
struct rlimit rlim;
struct process *pr = arg;
time_t runtime;
int s;
KERNEL_ASSERT_LOCKED();
SCHED_LOCK(s);
runtime = pr->ps_tu.tu_runtime.tv_sec;
SCHED_UNLOCK(s);
mtx_enter(&pr->ps_mtx);
rlim = pr->ps_limit->pl_rlimit[RLIMIT_CPU];
mtx_leave(&pr->ps_mtx);
if ((rlim_t)runtime >= rlim.rlim_cur) {
if ((rlim_t)runtime >= rlim.rlim_max) {
prsignal(pr, SIGKILL);
} else if (runtime >= pr->ps_nextxcpu) {
prsignal(pr, SIGXCPU);
pr->ps_nextxcpu = runtime + SIGXCPU_INTERVAL;
}
}
timeout_add_msec(&pr->ps_rucheck_to, RUCHECK_INTERVAL);
}
struct pool plimit_pool;
void
lim_startup(struct plimit *limit0)
{
rlim_t lim;
int i;
pool_init(&plimit_pool, sizeof(struct plimit), 0, IPL_MPFLOOR,
PR_WAITOK, "plimitpl", NULL);
for (i = 0; i < nitems(limit0->pl_rlimit); i++)
limit0->pl_rlimit[i].rlim_cur =
limit0->pl_rlimit[i].rlim_max = RLIM_INFINITY;
limit0->pl_rlimit[RLIMIT_NOFILE].rlim_cur = NOFILE;
limit0->pl_rlimit[RLIMIT_NOFILE].rlim_max = MIN(NOFILE_MAX,
(maxfiles - NOFILE > NOFILE) ? maxfiles - NOFILE : NOFILE);
limit0->pl_rlimit[RLIMIT_NPROC].rlim_cur = MAXUPRC;
lim = ptoa(uvmexp.free);
limit0->pl_rlimit[RLIMIT_RSS].rlim_max = lim;
lim = ptoa(64*1024); /* Default to very low */
limit0->pl_rlimit[RLIMIT_MEMLOCK].rlim_max = lim;
limit0->pl_rlimit[RLIMIT_MEMLOCK].rlim_cur = lim / 3;
refcnt_init(&limit0->pl_refcnt);
}
/*
* Make a copy of the plimit structure.
* We share these structures copy-on-write after fork,
* and copy when a limit is changed.
*/
struct plimit *
lim_copy(struct plimit *lim)
{
struct plimit *newlim;
newlim = pool_get(&plimit_pool, PR_WAITOK);
memcpy(newlim->pl_rlimit, lim->pl_rlimit,
sizeof(struct rlimit) * RLIM_NLIMITS);
refcnt_init(&newlim->pl_refcnt);
return (newlim);
}
void
lim_free(struct plimit *lim)
{
if (refcnt_rele(&lim->pl_refcnt) == 0)
return;
pool_put(&plimit_pool, lim);
}
void
lim_fork(struct process *parent, struct process *child)
{
struct plimit *limit;
mtx_enter(&parent->ps_mtx);
limit = parent->ps_limit;
refcnt_take(&limit->pl_refcnt);
mtx_leave(&parent->ps_mtx);
child->ps_limit = limit;
if (limit->pl_rlimit[RLIMIT_CPU].rlim_cur != RLIM_INFINITY)
timeout_add_msec(&child->ps_rucheck_to, RUCHECK_INTERVAL);
}
/*
* Return an exclusive write reference to the process' resource limit structure.
* The caller has to release the structure by calling lim_write_commit().
*
* This invalidates any plimit read reference held by the calling thread.
*/
struct plimit *
lim_write_begin(void)
{
struct plimit *limit;
struct proc *p = curproc;
rw_assert_wrlock(&rlimit_lock);
if (p->p_limit != NULL)
lim_free(p->p_limit);
p->p_limit = NULL;
/*
* It is safe to access ps_limit here without holding ps_mtx
* because rlimit_lock excludes other writers.
*/
limit = p->p_p->ps_limit;
if (P_HASSIBLING(p) || refcnt_shared(&limit->pl_refcnt))
limit = lim_copy(limit);
return (limit);
}
/*
* Finish exclusive write access to the plimit structure.
* This makes the structure visible to other threads in the process.
*/
void
lim_write_commit(struct plimit *limit)
{
struct plimit *olimit;
struct proc *p = curproc;
rw_assert_wrlock(&rlimit_lock);
if (limit != p->p_p->ps_limit) {
mtx_enter(&p->p_p->ps_mtx);
olimit = p->p_p->ps_limit;
p->p_p->ps_limit = limit;
mtx_leave(&p->p_p->ps_mtx);
lim_free(olimit);
}
}
/*
* Begin read access to the process' resource limit structure.
* The access has to be finished by calling lim_read_leave().
*
* Sections denoted by lim_read_enter() and lim_read_leave() cannot nest.
*/
struct plimit *
lim_read_enter(void)
{
struct plimit *limit;
struct proc *p = curproc;
struct process *pr = p->p_p;
/*
* This thread might not observe the latest value of ps_limit
* if another thread updated the limits very recently on another CPU.
* However, the anomaly should disappear quickly, especially if
* there is any synchronization activity between the threads (or
* the CPUs).
*/
limit = p->p_limit;
if (limit != pr->ps_limit) {
mtx_enter(&pr->ps_mtx);
limit = pr->ps_limit;
refcnt_take(&limit->pl_refcnt);
mtx_leave(&pr->ps_mtx);
if (p->p_limit != NULL)
lim_free(p->p_limit);
p->p_limit = limit;
}
KASSERT(limit != NULL);
return (limit);
}
/*
* Get the value of the resource limit in given process.
*/
rlim_t
lim_cur_proc(struct proc *p, int which)
{
struct process *pr = p->p_p;
rlim_t val;
KASSERT(which >= 0 && which < RLIM_NLIMITS);
mtx_enter(&pr->ps_mtx);
val = pr->ps_limit->pl_rlimit[which].rlim_cur;
mtx_leave(&pr->ps_mtx);
return (val);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
/* $OpenBSD: cd9660_vfsops.c,v 1.96 2022/09/01 13:45:26 krw Exp $ */
/* $NetBSD: cd9660_vfsops.c,v 1.26 1997/06/13 15:38:58 pk Exp $ */
/*-
* Copyright (c) 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley
* by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
* Support code is derived from software contributed to Berkeley
* by Atsushi Murai (amurai@spec.co.jp).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)cd9660_vfsops.c 8.9 (Berkeley) 12/5/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/specdev.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/fcntl.h>
#include <sys/disklabel.h>
#include <sys/ioctl.h>
#include <sys/cdio.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <isofs/cd9660/iso.h>
#include <isofs/cd9660/cd9660_extern.h>
#include <isofs/cd9660/iso_rrip.h>
#include <isofs/cd9660/cd9660_node.h>
const struct vfsops cd9660_vfsops = {
.vfs_mount = cd9660_mount,
.vfs_start = cd9660_start,
.vfs_unmount = cd9660_unmount,
.vfs_root = cd9660_root,
.vfs_quotactl = cd9660_quotactl,
.vfs_statfs = cd9660_statfs,
.vfs_sync = cd9660_sync,
.vfs_vget = cd9660_vget,
.vfs_fhtovp = cd9660_fhtovp,
.vfs_vptofh = cd9660_vptofh,
.vfs_init = cd9660_init,
.vfs_sysctl = cd9660_sysctl,
.vfs_checkexp = cd9660_check_export,
};
/*
* Called by vfs_mountroot when iso is going to be mounted as root.
*/
static int iso_mountfs(struct vnode *devvp, struct mount *mp,
struct proc *p, struct iso_args *argp);
int iso_disklabelspoof(dev_t dev, void (*strat)(struct buf *),
struct disklabel *lp);
int
cd9660_mountroot(void)
{
struct mount *mp;
extern struct vnode *rootvp;
struct proc *p = curproc; /* XXX */
int error;
struct iso_args args;
/*
* Get vnodes for swapdev and rootdev.
*/
if ((error = bdevvp(swapdev, &swapdev_vp)) ||
(error = bdevvp(rootdev, &rootvp))) {
printf("cd9660_mountroot: can't setup bdevvp's");
return (error);
}
if ((error = vfs_rootmountalloc("cd9660", "root_device", &mp)) != 0)
return (error);
args.flags = ISOFSMNT_ROOT;
if ((error = iso_mountfs(rootvp, mp, p, &args)) != 0) {
vfs_unbusy(mp);
vfs_mount_free(mp);
return (error);
}
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
(void)cd9660_statfs(mp, &mp->mnt_stat, p);
vfs_unbusy(mp);
inittodr(0);
return (0);
}
/*
* VFS Operations.
*
* mount system call
*/
int
cd9660_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
struct iso_mnt *imp = NULL;
struct iso_args *args = data;
struct vnode *devvp;
char fspec[MNAMELEN];
int error;
if ((mp->mnt_flag & MNT_RDONLY) == 0)
return (EROFS);
/*
* If updating, check whether changing from read-only to
* read/write; if there is no device name, that's all we do.
*/
if (mp->mnt_flag & MNT_UPDATE) {
imp = VFSTOISOFS(mp);
if (args && args->fspec == NULL)
return (vfs_export(mp, &imp->im_export,
&args->export_info));
return (0);
}
/*
* Not an update, or updating the name: look up the name
* and verify that it refers to a sensible block device.
*/
error = copyinstr(args->fspec, fspec, sizeof(fspec), NULL);
if (error)
return (error);
NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec, p);
if ((error = namei(ndp)) != 0)
return (error);
devvp = ndp->ni_vp;
if (devvp->v_type != VBLK) {
vrele(devvp);
return (ENOTBLK);
}
if (major(devvp->v_rdev) >= nblkdev) {
vrele(devvp);
return (ENXIO);
}
if ((mp->mnt_flag & MNT_UPDATE) == 0)
error = iso_mountfs(devvp, mp, p, args);
else {
if (devvp != imp->im_devvp)
error = EINVAL; /* needs translation */
else
vrele(devvp);
}
if (error) {
vrele(devvp);
return (error);
}
bzero(mp->mnt_stat.f_mntonname, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, fspec, MNAMELEN);
bzero(mp->mnt_stat.f_mntfromspec, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
bcopy(args, &mp->mnt_stat.mount_info.iso_args, sizeof(*args));
cd9660_statfs(mp, &mp->mnt_stat, p);
return (0);
}
/*
* Common code for mount and mountroot
*/
static int
iso_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p,
struct iso_args *argp)
{
struct iso_mnt *isomp = NULL;
struct buf *bp = NULL;
struct buf *pribp = NULL, *supbp = NULL;
dev_t dev = devvp->v_rdev;
int error = EINVAL;
int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
extern struct vnode *rootvp;
int iso_bsize;
int iso_blknum;
int joliet_level;
struct iso_volume_descriptor *vdp;
struct iso_primary_descriptor *pri = NULL;
struct iso_supplementary_descriptor *sup = NULL;
struct iso_directory_record *rootp;
int logical_block_size;
int sess;
if (!ronly)
return (EROFS);
/*
* Disallow multiple mounts of the same device.
* Disallow mounting of a device that is currently in use
* (except for root, which might share swap device for miniroot).
* Flush out any old buffers remaining from a previous use.
*/
if ((error = vfs_mountedon(devvp)) != 0)
return (error);
if (vcount(devvp) > 1 && devvp != rootvp)
return (EBUSY);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error)
return (error);
error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
if (error)
return (error);
/*
* This is the "logical sector size". The standard says this
* should be 2048 or the physical sector size on the device,
* whichever is greater. For now, we'll just use a constant.
*/
iso_bsize = ISO_DEFAULT_BLOCK_SIZE;
if (argp->flags & ISOFSMNT_SESS) {
sess = argp->sess;
if (sess < 0)
sess = 0;
} else {
sess = 0;
error = VOP_IOCTL(devvp, CDIOREADMSADDR, (caddr_t)&sess, 0,
FSCRED, p);
if (error)
sess = 0;
}
joliet_level = 0;
for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
if ((error = bread(devvp,
(iso_blknum + sess) * btodb(iso_bsize),
iso_bsize, &bp)) != 0)
goto out;
vdp = (struct iso_volume_descriptor *)bp->b_data;
if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) {
error = EINVAL;
goto out;
}
switch (isonum_711 (vdp->type)){
case ISO_VD_PRIMARY:
if (pribp == NULL) {
pribp = bp;
bp = NULL;
pri = (struct iso_primary_descriptor *)vdp;
}
break;
case ISO_VD_SUPPLEMENTARY:
if (supbp == NULL) {
supbp = bp;
bp = NULL;
sup = (struct iso_supplementary_descriptor *)vdp;
if (!(argp->flags & ISOFSMNT_NOJOLIET)) {
if (bcmp(sup->escape, "%/@", 3) == 0)
joliet_level = 1;
if (bcmp(sup->escape, "%/C", 3) == 0)
joliet_level = 2;
if (bcmp(sup->escape, "%/E", 3) == 0)
joliet_level = 3;
if (isonum_711 (sup->flags) & 1)
joliet_level = 0;
}
}
break;
case ISO_VD_END:
goto vd_end;
default:
break;
}
if (bp) {
brelse(bp);
bp = NULL;
}
}
vd_end:
if (bp) {
brelse(bp);
bp = NULL;
}
if (pri == NULL) {
error = EINVAL;
goto out;
}
logical_block_size = isonum_723 (pri->logical_block_size);
if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE
|| (logical_block_size & (logical_block_size - 1)) != 0) {
error = EINVAL;
goto out;
}
rootp = (struct iso_directory_record *)pri->root_directory_record;
isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK);
bzero((caddr_t)isomp, sizeof *isomp);
isomp->logical_block_size = logical_block_size;
isomp->volume_space_size = isonum_733 (pri->volume_space_size);
bcopy (rootp, isomp->root, sizeof isomp->root);
isomp->root_extent = isonum_733 (rootp->extent);
isomp->root_size = isonum_733 (rootp->size);
isomp->joliet_level = 0;
/*
* Since an ISO9660 multi-session CD can also access previous sessions,
* we have to include them into the space considerations.
*/
isomp->volume_space_size += sess;
isomp->im_bmask = logical_block_size - 1;
isomp->im_bshift = ffs(logical_block_size) - 1;
brelse(pribp);
pribp = NULL;
mp->mnt_data = isomp;
mp->mnt_stat.f_fsid.val[0] = (long)dev;
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
mp->mnt_stat.f_namemax = NAME_MAX;
mp->mnt_flag |= MNT_LOCAL;
isomp->im_mountp = mp;
isomp->im_dev = dev;
isomp->im_devvp = devvp;
/* Check the Rock Ridge Extension support */
if (!(argp->flags & ISOFSMNT_NORRIP)) {
if ((error = bread(isomp->im_devvp, (isomp->root_extent +
isonum_711(rootp->ext_attr_length)) <<
(isomp->im_bshift - DEV_BSHIFT),
isomp->logical_block_size, &bp)) != 0)
goto out;
rootp = (struct iso_directory_record *)bp->b_data;
if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) {
argp->flags |= ISOFSMNT_NORRIP;
} else {
argp->flags &= ~ISOFSMNT_GENS;
}
/*
* The contents are valid,
* but they will get reread as part of another vnode, so...
*/
brelse(bp);
bp = NULL;
}
isomp->im_flags = argp->flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS |
ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET);
switch (isomp->im_flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS)) {
default:
isomp->iso_ftype = ISO_FTYPE_DEFAULT;
break;
case ISOFSMNT_GENS|ISOFSMNT_NORRIP:
isomp->iso_ftype = ISO_FTYPE_9660;
break;
case 0:
isomp->iso_ftype = ISO_FTYPE_RRIP;
break;
}
/* Decide whether to use the Joliet descriptor */
if (isomp->iso_ftype != ISO_FTYPE_RRIP && joliet_level) {
rootp = (struct iso_directory_record *)
sup->root_directory_record;
bcopy(rootp, isomp->root, sizeof isomp->root);
isomp->root_extent = isonum_733(rootp->extent);
isomp->root_size = isonum_733(rootp->size);
isomp->joliet_level = joliet_level;
}
if (supbp) {
brelse(supbp);
supbp = NULL;
}
devvp->v_specmountpoint = mp;
return (0);
out:
if (devvp->v_specinfo)
devvp->v_specmountpoint = NULL;
if (bp)
brelse(bp);
if (supbp)
brelse(supbp);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
VOP_UNLOCK(devvp);
if (isomp) {
free((caddr_t)isomp, M_ISOFSMNT, 0);
mp->mnt_data = NULL;
}
return (error);
}
/*
* Test to see if the device is an ISOFS filesystem.
*/
int
iso_disklabelspoof(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp)
{
struct buf *bp = NULL;
struct iso_volume_descriptor *vdp;
struct iso_primary_descriptor *pri;
int logical_block_size;
int error = EINVAL;
int iso_blknum;
int i;
bp = geteblk(ISO_DEFAULT_BLOCK_SIZE);
bp->b_dev = dev;
for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
bp->b_blkno = iso_blknum * btodb(ISO_DEFAULT_BLOCK_SIZE);
bp->b_bcount = ISO_DEFAULT_BLOCK_SIZE;
CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
/*printf("d_secsize %d iso_blknum %d b_blkno %d bcount %d\n",
lp->d_secsize, iso_blknum, bp->b_blkno, bp->b_bcount);*/
(*strat)(bp);
if (biowait(bp))
goto out;
vdp = (struct iso_volume_descriptor *)bp->b_data;
/*printf("%2x%2x%2x type %2x\n", vdp->id[0], vdp->id[1],
vdp->id[2], isonum_711(vdp->type));*/
if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0 ||
isonum_711 (vdp->type) == ISO_VD_END)
goto out;
if (isonum_711 (vdp->type) == ISO_VD_PRIMARY)
break;
}
if (isonum_711 (vdp->type) != ISO_VD_PRIMARY)
goto out;
pri = (struct iso_primary_descriptor *)vdp;
logical_block_size = isonum_723 (pri->logical_block_size);
if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE ||
(logical_block_size & (logical_block_size - 1)) != 0)
goto out;
/*
* build a disklabel for the CD
*/
strncpy(lp->d_typename, pri->volume_id, sizeof lp->d_typename);
strncpy(lp->d_packname, pri->volume_id+16, sizeof lp->d_packname);
for (i = 0; i < MAXPARTITIONS; i++) {
DL_SETPSIZE(&lp->d_partitions[i], 0);
DL_SETPOFFSET(&lp->d_partitions[i], 0);
}
DL_SETPOFFSET(&lp->d_partitions[0], 0);
DL_SETPSIZE(&lp->d_partitions[0], DL_GETDSIZE(lp));
lp->d_partitions[0].p_fstype = FS_ISO9660;
DL_SETPOFFSET(&lp->d_partitions[RAW_PART], 0);
DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
lp->d_partitions[RAW_PART].p_fstype = FS_ISO9660;
lp->d_npartitions = MAXPARTITIONS;
lp->d_version = 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
error = 0;
out:
bp->b_flags |= B_INVAL;
brelse(bp);
return (error);
}
/*
* Make a filesystem operational.
* Nothing to do at the moment.
*/
/* ARGSUSED */
int
cd9660_start(struct mount *mp, int flags, struct proc *p)
{
return (0);
}
/*
* unmount system call
*/
int
cd9660_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct iso_mnt *isomp;
int error, flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
#if 0
mntflushbuf(mp, 0);
if (mntinvalbuf(mp))
return (EBUSY);
#endif
if ((error = vflush(mp, NULLVP, flags)) != 0)
return (error);
isomp = VFSTOISOFS(mp);
isomp->im_devvp->v_specmountpoint = NULL;
vn_lock(isomp->im_devvp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p);
vput(isomp->im_devvp);
free((caddr_t)isomp, M_ISOFSMNT, 0);
mp->mnt_data = NULL;
mp->mnt_flag &= ~MNT_LOCAL;
return (0);
}
/*
* Return root of a filesystem
*/
int
cd9660_root(struct mount *mp, struct vnode **vpp)
{
struct iso_mnt *imp = VFSTOISOFS(mp);
struct iso_directory_record *dp =
(struct iso_directory_record *)imp->root;
cdino_t ino = isodirino(dp, imp);
/*
* With RRIP we must use the `.' entry of the root directory.
* Simply tell vget, that it's a relocated directory.
*/
return (cd9660_vget_internal(mp, ino, vpp,
imp->iso_ftype == ISO_FTYPE_RRIP, dp));
}
/*
* Do operations associated with quotas, not supported
*/
/* ARGSUSED */
int
cd9660_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg,
struct proc *p)
{
return (EOPNOTSUPP);
}
/*
* Get file system statistics.
*/
int
cd9660_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct iso_mnt *isomp;
isomp = VFSTOISOFS(mp);
sbp->f_bsize = isomp->logical_block_size;
sbp->f_iosize = sbp->f_bsize; /* XXX */
sbp->f_blocks = isomp->volume_space_size;
sbp->f_bfree = 0; /* total free blocks */
sbp->f_bavail = 0; /* blocks free for non superuser */
sbp->f_files = 0; /* total files */
sbp->f_ffree = 0; /* free file nodes */
sbp->f_favail = 0; /* file nodes free for non superuser */
copy_statfs_info(sbp, mp);
return (0);
}
/* ARGSUSED */
int
cd9660_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
struct proc *p)
{
return (0);
}
/*
* File handle to vnode
*
* Have to be really careful about stale file handles:
* - check that the inode number is in range
* - call iget() to get the locked inode
* - check for an unallocated inode (i_mode == 0)
* - check that the generation number matches
*/
struct ifid {
ushort ifid_len;
ushort ifid_pad;
int ifid_ino;
long ifid_start;
};
/* ARGSUSED */
int
cd9660_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
struct ifid *ifhp = (struct ifid *)fhp;
struct iso_node *ip;
struct vnode *nvp;
int error;
#ifdef ISOFS_DBG
printf("fhtovp: ino %d, start %ld\n", ifhp->ifid_ino,
ifhp->ifid_start);
#endif
if ((error = VFS_VGET(mp, ifhp->ifid_ino, &nvp)) != 0) {
*vpp = NULLVP;
return (error);
}
ip = VTOI(nvp);
if (ip->inode.iso_mode == 0) {
vput(nvp);
*vpp = NULLVP;
return (ESTALE);
}
*vpp = nvp;
return (0);
}
int
cd9660_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
if (ino > (cdino_t)-1)
panic("cd9660_vget: alien ino_t %llu",
(unsigned long long)ino);
/*
* XXXX
* It would be nice if we didn't always set the `relocated' flag
* and force the extra read, but I don't want to think about fixing
* that right now.
*/
return (cd9660_vget_internal(mp, ino, vpp,
#if 0
VFSTOISOFS(mp)->iso_ftype == ISO_FTYPE_RRIP,
#else
0,
#endif
NULL));
}
int
cd9660_vget_internal(struct mount *mp, cdino_t ino, struct vnode **vpp,
int relocated, struct iso_directory_record *isodir)
{
struct iso_mnt *imp;
struct iso_node *ip;
struct buf *bp;
struct vnode *vp, *nvp;
dev_t dev;
int error;
retry:
imp = VFSTOISOFS(mp);
dev = imp->im_dev;
if ((*vpp = cd9660_ihashget(dev, ino)) != NULLVP)
return (0);
/* Allocate a new vnode/iso_node. */
if ((error = getnewvnode(VT_ISOFS, mp, &cd9660_vops, &vp)) != 0) {
*vpp = NULLVP;
return (error);
}
ip = malloc(sizeof(*ip), M_ISOFSNODE, M_WAITOK | M_ZERO);
rrw_init_flags(&ip->i_lock, "isoinode", RWL_DUPOK | RWL_IS_VNODE);
vp->v_data = ip;
ip->i_vnode = vp;
ip->i_dev = dev;
ip->i_number = ino;
/*
* Put it onto its hash chain and lock it so that other requests for
* this inode will block if they arrive while we are sleeping waiting
* for old data structures to be purged or for the contents of the
* disk portion of this inode to be read.
*/
error = cd9660_ihashins(ip);
if (error) {
vrele(vp);
if (error == EEXIST)
goto retry;
return (error);
}
if (isodir == 0) {
int lbn, off;
lbn = lblkno(imp, ino);
if (lbn >= imp->volume_space_size) {
vput(vp);
printf("fhtovp: lbn exceed volume space %d\n", lbn);
return (ESTALE);
}
off = blkoff(imp, ino);
if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size)
{
vput(vp);
printf("fhtovp: crosses block boundary %d\n",
off + ISO_DIRECTORY_RECORD_SIZE);
return (ESTALE);
}
error = bread(imp->im_devvp,
lbn << (imp->im_bshift - DEV_BSHIFT),
imp->logical_block_size, &bp);
if (error) {
vput(vp);
brelse(bp);
printf("fhtovp: bread error %d\n",error);
return (error);
}
isodir = (struct iso_directory_record *)(bp->b_data + off);
if (off + isonum_711(isodir->length) >
imp->logical_block_size) {
vput(vp);
if (bp != 0)
brelse(bp);
printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
off +isonum_711(isodir->length), off,
isonum_711(isodir->length));
return (ESTALE);
}
#if 0
if (isonum_733(isodir->extent) +
isonum_711(isodir->ext_attr_length) != ifhp->ifid_start) {
if (bp != 0)
brelse(bp);
printf("fhtovp: file start miss %d vs %d\n",
isonum_733(isodir->extent) +
isonum_711(isodir->ext_attr_length),
ifhp->ifid_start);
return (ESTALE);
}
#endif
} else
bp = 0;
ip->i_mnt = imp;
ip->i_devvp = imp->im_devvp;
vref(ip->i_devvp);
if (relocated) {
/*
* On relocated directories we must
* read the `.' entry out of a dir.
*/
ip->iso_start = ino >> imp->im_bshift;
if (bp != 0)
brelse(bp);
if ((error = cd9660_bufatoff(ip, (off_t)0, NULL, &bp)) != 0) {
vput(vp);
return (error);
}
isodir = (struct iso_directory_record *)bp->b_data;
}
ip->iso_extent = isonum_733(isodir->extent);
ip->i_size = (u_int32_t) isonum_733(isodir->size);
ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
/*
* Setup time stamp, attribute
*/
vp->v_type = VNON;
switch (imp->iso_ftype) {
default: /* ISO_FTYPE_9660 */
{
struct buf *bp2;
int off;
if ((imp->im_flags & ISOFSMNT_EXTATT) &&
(off = isonum_711(isodir->ext_attr_length)))
cd9660_bufatoff(ip, (off_t)-(off << imp->im_bshift),
NULL, &bp2);
else
bp2 = NULL;
cd9660_defattr(isodir, ip, bp2);
cd9660_deftstamp(isodir, ip, bp2);
if (bp2)
brelse(bp2);
break;
}
case ISO_FTYPE_RRIP:
cd9660_rrip_analyze(isodir, ip, imp);
break;
}
if (bp != 0)
brelse(bp);
/*
* Initialize the associated vnode
*/
switch (vp->v_type = IFTOVT(ip->inode.iso_mode)) {
case VFIFO:
#ifdef FIFO
vp->v_op = &cd9660_fifovops;
break;
#else
vput(vp);
return (EOPNOTSUPP);
#endif /* FIFO */
case VCHR:
case VBLK:
/*
* if device, look at device number table for translation
*/
vp->v_op = &cd9660_specvops;
if ((nvp = checkalias(vp, ip->inode.iso_rdev, mp)) != NULL) {
/*
* Discard unneeded vnode, but save its iso_node.
* Note that the lock is carried over in the iso_node
*/
nvp->v_data = vp->v_data;
vp->v_data = NULL;
vp->v_op = &spec_vops;
vrele(vp);
vgone(vp);
/*
* Reinitialize aliased inode.
*/
vp = nvp;
ip->i_vnode = vp;
}
break;
case VLNK:
case VNON:
case VSOCK:
case VDIR:
case VBAD:
break;
case VREG:
uvm_vnp_setsize(vp, ip->i_size);
break;
}
if (ip->iso_extent == imp->root_extent)
vp->v_flag |= VROOT;
/*
* XXX need generation number?
*/
*vpp = vp;
return (0);
}
/*
* Vnode pointer to File handle
*/
/* ARGSUSED */
int
cd9660_vptofh(struct vnode *vp, struct fid *fhp)
{
struct iso_node *ip = VTOI(vp);
struct ifid *ifhp;
ifhp = (struct ifid *)fhp;
ifhp->ifid_len = sizeof(struct ifid);
ifhp->ifid_ino = ip->i_number;
ifhp->ifid_start = ip->iso_start;
#ifdef ISOFS_DBG
printf("vptofh: ino %d, start %ld\n",
ifhp->ifid_ino,ifhp->ifid_start);
#endif
return (0);
}
/*
* Verify a remote client has export rights and return these rights via
* exflagsp and credanonp.
*/
int
cd9660_check_export(struct mount *mp, struct mbuf *nam, int *exflagsp,
struct ucred **credanonp)
{
struct netcred *np;
struct iso_mnt *imp = VFSTOISOFS(mp);
/*
* Get the export permission structure for this <mp, client> tuple.
*/
np = vfs_export_lookup(mp, &imp->im_export, nam);
if (np == NULL)
return (EACCES);
*exflagsp = np->netc_exflags;
*credanonp = &np->netc_anon;
return (0);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
/* $OpenBSD: usb.c,v 1.130 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: usb.c,v 1.77 2003/01/01 00:10:26 thorpej Exp $ */
/*
* Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* USB specifications and other documentation can be found at
* https://www.usb.org/documents
*/
#include "ohci.h"
#include "uhci.h"
#include "ehci.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/timeout.h>
#include <sys/kthread.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/signalvar.h>
#include <sys/time.h>
#include <sys/rwlock.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <machine/bus.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>
#include <dev/usb/usbpcap.h>
#ifdef USB_DEBUG
#define DPRINTF(x) do { if (usbdebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (usbdebug>(n)) printf x; } while (0)
int usbdebug = 0;
#if defined(UHCI_DEBUG) && NUHCI > 0
extern int uhcidebug;
#endif
#if defined(OHCI_DEBUG) && NOHCI > 0
extern int ohcidebug;
#endif
#if defined(EHCI_DEBUG) && NEHCI > 0
extern int ehcidebug;
#endif
/*
* 0 - do usual exploration
* !0 - do no exploration
*/
int usb_noexplore = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
struct usb_softc {
struct device sc_dev; /* base device */
struct usbd_bus *sc_bus; /* USB controller */
struct usbd_port sc_port; /* dummy port for root hub */
int sc_speed;
struct usb_task sc_explore_task;
struct timeval sc_ptime;
};
struct rwlock usbpalock;
TAILQ_HEAD(, usb_task) usb_abort_tasks;
TAILQ_HEAD(, usb_task) usb_explore_tasks;
TAILQ_HEAD(, usb_task) usb_generic_tasks;
static int usb_nbuses = 0;
static int usb_run_tasks, usb_run_abort_tasks;
int explore_pending;
const char *usbrev_str[] = USBREV_STR;
void usb_explore(void *);
void usb_create_task_threads(void *);
void usb_task_thread(void *);
struct proc *usb_task_thread_proc = NULL;
void usb_abort_task_thread(void *);
struct proc *usb_abort_task_thread_proc = NULL;
void usb_fill_udc_task(void *);
void usb_fill_udf_task(void *);
int usb_match(struct device *, void *, void *);
void usb_attach(struct device *, struct device *, void *);
int usb_detach(struct device *, int);
int usb_activate(struct device *, int);
int usb_attach_roothub(struct usb_softc *);
void usb_detach_roothub(struct usb_softc *);
struct cfdriver usb_cd = {
NULL, "usb", DV_DULL
};
const struct cfattach usb_ca = {
sizeof(struct usb_softc), usb_match, usb_attach, usb_detach,
usb_activate,
};
int
usb_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
usb_attach(struct device *parent, struct device *self, void *aux)
{
struct usb_softc *sc = (struct usb_softc *)self;
int usbrev;
if (usb_nbuses == 0) {
rw_init(&usbpalock, "usbpalock");
TAILQ_INIT(&usb_abort_tasks);
TAILQ_INIT(&usb_explore_tasks);
TAILQ_INIT(&usb_generic_tasks);
usb_run_tasks = usb_run_abort_tasks = 1;
kthread_create_deferred(usb_create_task_threads, NULL);
}
usb_nbuses++;
sc->sc_bus = aux;
sc->sc_bus->usbctl = self;
sc->sc_port.power = USB_MAX_POWER;
usbrev = sc->sc_bus->usbrev;
printf(": USB revision %s", usbrev_str[usbrev]);
switch (usbrev) {
case USBREV_1_0:
case USBREV_1_1:
sc->sc_speed = USB_SPEED_FULL;
break;
case USBREV_2_0:
sc->sc_speed = USB_SPEED_HIGH;
break;
case USBREV_3_0:
sc->sc_speed = USB_SPEED_SUPER;
break;
default:
printf(", not supported\n");
sc->sc_bus->dying = 1;
return;
}
printf("\n");
#if NBPFILTER > 0
sc->sc_bus->bpfif = bpfsattach(&sc->sc_bus->bpf, sc->sc_dev.dv_xname,
DLT_USBPCAP, sizeof(struct usbpcap_pkt_hdr));
#endif
/* Make sure not to use tsleep() if we are cold booting. */
if (cold)
sc->sc_bus->use_polling++;
/* Don't let hub interrupts cause explore until ready. */
sc->sc_bus->flags |= USB_BUS_CONFIG_PENDING;
/* explore task */
usb_init_task(&sc->sc_explore_task, usb_explore, sc,
USB_TASK_TYPE_EXPLORE);
sc->sc_bus->soft = softintr_establish(IPL_SOFTUSB,
sc->sc_bus->methods->soft_intr, sc->sc_bus);
if (sc->sc_bus->soft == NULL) {
printf("%s: can't register softintr\n", sc->sc_dev.dv_xname);
sc->sc_bus->dying = 1;
return;
}
if (!usb_attach_roothub(sc)) {
struct usbd_device *dev = sc->sc_bus->root_hub;
#if 1
/*
* Turning this code off will delay attachment of USB devices
* until the USB task thread is running, which means that
* the keyboard will not work until after cold boot.
*/
if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1))
dev->hub->explore(sc->sc_bus->root_hub);
#endif
}
if (cold)
sc->sc_bus->use_polling--;
if (!sc->sc_bus->dying) {
getmicrouptime(&sc->sc_ptime);
if (sc->sc_bus->usbrev == USBREV_2_0)
explore_pending++;
config_pending_incr();
usb_needs_explore(sc->sc_bus->root_hub, 1);
}
}
int
usb_attach_roothub(struct usb_softc *sc)
{
struct usbd_device *dev;
if (usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, sc->sc_speed, 0,
&sc->sc_port)) {
printf("%s: root hub problem\n", sc->sc_dev.dv_xname);
sc->sc_bus->dying = 1;
return (1);
}
dev = sc->sc_port.device;
if (dev->hub == NULL) {
printf("%s: root device is not a hub\n", sc->sc_dev.dv_xname);
sc->sc_bus->dying = 1;
return (1);
}
sc->sc_bus->root_hub = dev;
return (0);
}
void
usb_detach_roothub(struct usb_softc *sc)
{
/*
* To avoid races with the usb task thread, mark the root hub
* as disconnecting and schedule an exploration task to detach
* it.
*/
sc->sc_bus->flags |= USB_BUS_DISCONNECTING;
/*
* Reset the dying flag in case it has been set by the interrupt
* handler when unplugging an HC card otherwise the task wont be
* scheduled. This is safe since a dead HC should not trigger
* new interrupt.
*/
sc->sc_bus->dying = 0;
usb_needs_explore(sc->sc_bus->root_hub, 0);
usb_wait_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
sc->sc_bus->root_hub = NULL;
}
void
usb_create_task_threads(void *arg)
{
if (kthread_create(usb_abort_task_thread, NULL,
&usb_abort_task_thread_proc, "usbatsk"))
panic("unable to create usb abort task thread");
if (kthread_create(usb_task_thread, NULL,
&usb_task_thread_proc, "usbtask"))
panic("unable to create usb task thread");
}
/*
* Add a task to be performed by the task thread. This function can be
* called from any context and the task will be executed in a process
* context ASAP.
*/
void
usb_add_task(struct usbd_device *dev, struct usb_task *task)
{
int s;
/*
* If the thread detaching ``dev'' is sleeping, waiting
* for all submitted transfers to finish, we must be able
* to enqueue abort tasks. Otherwise timeouts can't give
* back submitted transfers to the stack.
*/
if (usbd_is_dying(dev) && (task->type != USB_TASK_TYPE_ABORT))
return;
DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
task->state, task->type));
s = splusb();
if (!(task->state & USB_TASK_STATE_ONQ)) {
switch (task->type) {
case USB_TASK_TYPE_ABORT:
TAILQ_INSERT_TAIL(&usb_abort_tasks, task, next);
break;
case USB_TASK_TYPE_EXPLORE:
TAILQ_INSERT_TAIL(&usb_explore_tasks, task, next);
break;
case USB_TASK_TYPE_GENERIC:
TAILQ_INSERT_TAIL(&usb_generic_tasks, task, next);
break;
}
task->state |= USB_TASK_STATE_ONQ;
task->dev = dev;
}
if (task->type == USB_TASK_TYPE_ABORT)
wakeup(&usb_run_abort_tasks);
else
wakeup(&usb_run_tasks);
splx(s);
}
void
usb_rem_task(struct usbd_device *dev, struct usb_task *task)
{
int s;
if (!(task->state & USB_TASK_STATE_ONQ))
return;
DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
task->state, task->type));
s = splusb();
switch (task->type) {
case USB_TASK_TYPE_ABORT:
TAILQ_REMOVE(&usb_abort_tasks, task, next);
break;
case USB_TASK_TYPE_EXPLORE:
TAILQ_REMOVE(&usb_explore_tasks, task, next);
break;
case USB_TASK_TYPE_GENERIC:
TAILQ_REMOVE(&usb_generic_tasks, task, next);
break;
}
task->state &= ~USB_TASK_STATE_ONQ;
if (task->state == USB_TASK_STATE_NONE)
wakeup(task);
splx(s);
}
void
usb_wait_task(struct usbd_device *dev, struct usb_task *task)
{
int s;
DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
task->state, task->type));
if (task->state == USB_TASK_STATE_NONE)
return;
s = splusb();
while (task->state != USB_TASK_STATE_NONE) {
DPRINTF(("%s: waiting for task to complete\n", __func__));
tsleep_nsec(task, PWAIT, "endtask", INFSLP);
}
splx(s);
}
void
usb_rem_wait_task(struct usbd_device *dev, struct usb_task *task)
{
usb_rem_task(dev, task);
usb_wait_task(dev, task);
}
void
usb_task_thread(void *arg)
{
struct usb_task *task;
int s;
DPRINTF(("usb_task_thread: start\n"));
s = splusb();
while (usb_run_tasks) {
if ((task = TAILQ_FIRST(&usb_explore_tasks)) != NULL)
TAILQ_REMOVE(&usb_explore_tasks, task, next);
else if ((task = TAILQ_FIRST(&usb_generic_tasks)) != NULL)
TAILQ_REMOVE(&usb_generic_tasks, task, next);
else {
tsleep_nsec(&usb_run_tasks, PWAIT, "usbtsk", INFSLP);
continue;
}
/*
* Set the state run bit before clearing the onq bit.
* This avoids state == none between dequeue and
* execution, which could cause usb_wait_task() to do
* the wrong thing.
*/
task->state |= USB_TASK_STATE_RUN;
task->state &= ~USB_TASK_STATE_ONQ;
/* Don't actually execute the task if dying. */
if (!usbd_is_dying(task->dev)) {
splx(s);
task->fun(task->arg);
s = splusb();
}
task->state &= ~USB_TASK_STATE_RUN;
if (task->state == USB_TASK_STATE_NONE)
wakeup(task);
}
splx(s);
kthread_exit(0);
}
/*
* This thread is ONLY for the HCI drivers to be able to abort xfers.
* Synchronous xfers sleep the task thread, so the aborts need to happen
* in a different thread.
*/
void
usb_abort_task_thread(void *arg)
{
struct usb_task *task;
int s;
DPRINTF(("usb_xfer_abort_thread: start\n"));
s = splusb();
while (usb_run_abort_tasks) {
if ((task = TAILQ_FIRST(&usb_abort_tasks)) != NULL)
TAILQ_REMOVE(&usb_abort_tasks, task, next);
else {
tsleep_nsec(&usb_run_abort_tasks, PWAIT, "usbatsk",
INFSLP);
continue;
}
/*
* Set the state run bit before clearing the onq bit.
* This avoids state == none between dequeue and
* execution, which could cause usb_wait_task() to do
* the wrong thing.
*/
task->state |= USB_TASK_STATE_RUN;
task->state &= ~USB_TASK_STATE_ONQ;
splx(s);
task->fun(task->arg);
s = splusb();
task->state &= ~USB_TASK_STATE_RUN;
if (task->state == USB_TASK_STATE_NONE)
wakeup(task);
}
splx(s);
kthread_exit(0);
}
int
usbctlprint(void *aux, const char *pnp)
{
/* only "usb"es can attach to host controllers */
if (pnp)
printf("usb at %s", pnp);
return (UNCONF);
}
int
usbopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = minor(dev);
struct usb_softc *sc;
if (unit >= usb_cd.cd_ndevs)
return (ENXIO);
sc = usb_cd.cd_devs[unit];
if (sc == NULL)
return (ENXIO);
if (sc->sc_bus->dying)
return (EIO);
return (0);
}
int
usbclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
void
usb_fill_udc_task(void *arg)
{
struct usb_device_cdesc *udc = (struct usb_device_cdesc *)arg;
struct usb_softc *sc;
struct usbd_device *dev;
int addr = udc->udc_addr, cdesc_len;
usb_config_descriptor_t *cdesc;
/* check that the bus and device are still present */
if (udc->udc_bus >= usb_cd.cd_ndevs)
return;
sc = usb_cd.cd_devs[udc->udc_bus];
if (sc == NULL)
return;
dev = sc->sc_bus->devices[udc->udc_addr];
if (dev == NULL)
return;
cdesc = usbd_get_cdesc(sc->sc_bus->devices[addr],
udc->udc_config_index, &cdesc_len);
if (cdesc == NULL)
return;
udc->udc_desc = *cdesc;
free(cdesc, M_TEMP, cdesc_len);
}
void
usb_fill_udf_task(void *arg)
{
struct usb_device_fdesc *udf = (struct usb_device_fdesc *)arg;
struct usb_softc *sc;
struct usbd_device *dev;
int addr = udf->udf_addr;
usb_config_descriptor_t *cdesc;
/* check that the bus and device are still present */
if (udf->udf_bus >= usb_cd.cd_ndevs)
return;
sc = usb_cd.cd_devs[udf->udf_bus];
if (sc == NULL)
return;
dev = sc->sc_bus->devices[udf->udf_addr];
if (dev == NULL)
return;
cdesc = usbd_get_cdesc(sc->sc_bus->devices[addr],
udf->udf_config_index, &udf->udf_size);
udf->udf_data = (char *)cdesc;
}
int
usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct usb_softc *sc;
int unit = minor(devt);
int error;
sc = usb_cd.cd_devs[unit];
if (sc->sc_bus->dying)
return (EIO);
error = 0;
switch (cmd) {
#ifdef USB_DEBUG
case USB_SETDEBUG:
/* only root can access to these debug flags */
if ((error = suser(curproc)) != 0)
return (error);
if (!(flag & FWRITE))
return (EBADF);
usbdebug = ((*(unsigned int *)data) & 0x000000ff);
#if defined(UHCI_DEBUG) && NUHCI > 0
uhcidebug = ((*(unsigned int *)data) & 0x0000ff00) >> 8;
#endif
#if defined(OHCI_DEBUG) && NOHCI > 0
ohcidebug = ((*(unsigned int *)data) & 0x00ff0000) >> 16;
#endif
#if defined(EHCI_DEBUG) && NEHCI > 0
ehcidebug = ((*(unsigned int *)data) & 0xff000000) >> 24;
#endif
break;
#endif /* USB_DEBUG */
case USB_REQUEST:
{
struct usb_ctl_request *ur = (void *)data;
size_t len = UGETW(ur->ucr_request.wLength), mlen;
struct iovec iov;
struct uio uio;
void *ptr = NULL;
int addr = ur->ucr_addr;
usbd_status err;
if (!(flag & FWRITE))
return (EBADF);
DPRINTF(("%s: USB_REQUEST addr=%d len=%zu\n", __func__, addr, len));
/* Avoid requests that would damage the bus integrity. */
if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
ur->ucr_request.bRequest == UR_SET_ADDRESS) ||
(ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
ur->ucr_request.bRequest == UR_SET_CONFIG) ||
(ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE &&
ur->ucr_request.bRequest == UR_SET_INTERFACE))
return (EINVAL);
if (len > 32767)
return (EINVAL);
if (addr < 0 || addr >= USB_MAX_DEVICES)
return (EINVAL);
if (sc->sc_bus->devices[addr] == NULL)
return (ENXIO);
if (len != 0) {
iov.iov_base = (caddr_t)ur->ucr_data;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_resid = len;
uio.uio_offset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw =
ur->ucr_request.bmRequestType & UT_READ ?
UIO_READ : UIO_WRITE;
uio.uio_procp = p;
if ((ptr = malloc(len, M_TEMP, M_NOWAIT)) == NULL) {
error = ENOMEM;
goto ret;
}
if (uio.uio_rw == UIO_WRITE) {
error = uiomove(ptr, len, &uio);
if (error)
goto ret;
}
}
err = usbd_do_request_flags(sc->sc_bus->devices[addr],
&ur->ucr_request, ptr, ur->ucr_flags,
&ur->ucr_actlen, USBD_DEFAULT_TIMEOUT);
if (err) {
error = EIO;
goto ret;
}
/* Only if USBD_SHORT_XFER_OK is set. */
mlen = len;
if (mlen > ur->ucr_actlen)
mlen = ur->ucr_actlen;
if (mlen != 0) {
if (uio.uio_rw == UIO_READ) {
error = uiomove(ptr, mlen, &uio);
if (error)
goto ret;
}
}
ret:
free(ptr, M_TEMP, len);
return (error);
}
case USB_DEVICEINFO:
{
struct usb_device_info *di = (void *)data;
int addr = di->udi_addr;
struct usbd_device *dev;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
dev = sc->sc_bus->devices[addr];
if (dev == NULL)
return (ENXIO);
usbd_fill_deviceinfo(dev, di);
break;
}
case USB_DEVICESTATS:
*(struct usb_device_stats *)data = sc->sc_bus->stats;
break;
case USB_DEVICE_GET_DDESC:
{
struct usb_device_ddesc *udd = (struct usb_device_ddesc *)data;
int addr = udd->udd_addr;
struct usbd_device *dev;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
dev = sc->sc_bus->devices[addr];
if (dev == NULL)
return (ENXIO);
udd->udd_bus = unit;
udd->udd_desc = *usbd_get_device_descriptor(dev);
break;
}
case USB_DEVICE_GET_CDESC:
{
struct usb_device_cdesc *udc = (struct usb_device_cdesc *)data;
int addr = udc->udc_addr;
struct usb_task udc_task;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
if (sc->sc_bus->devices[addr] == NULL)
return (ENXIO);
udc->udc_bus = unit;
udc->udc_desc.bLength = 0;
usb_init_task(&udc_task, usb_fill_udc_task, udc,
USB_TASK_TYPE_GENERIC);
usb_add_task(sc->sc_bus->root_hub, &udc_task);
usb_wait_task(sc->sc_bus->root_hub, &udc_task);
if (udc->udc_desc.bLength == 0)
return (EINVAL);
break;
}
case USB_DEVICE_GET_FDESC:
{
struct usb_device_fdesc *udf = (struct usb_device_fdesc *)data;
int addr = udf->udf_addr;
struct usb_task udf_task;
struct usb_device_fdesc save_udf;
usb_config_descriptor_t *cdesc;
struct iovec iov;
struct uio uio;
size_t len, cdesc_len;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
if (sc->sc_bus->devices[addr] == NULL)
return (ENXIO);
udf->udf_bus = unit;
save_udf = *udf;
udf->udf_data = NULL;
usb_init_task(&udf_task, usb_fill_udf_task, udf,
USB_TASK_TYPE_GENERIC);
usb_add_task(sc->sc_bus->root_hub, &udf_task);
usb_wait_task(sc->sc_bus->root_hub, &udf_task);
len = cdesc_len = udf->udf_size;
cdesc = (usb_config_descriptor_t *)udf->udf_data;
*udf = save_udf;
if (cdesc == NULL)
return (EINVAL);
if (len > udf->udf_size)
len = udf->udf_size;
iov.iov_base = (caddr_t)udf->udf_data;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_resid = len;
uio.uio_offset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = p;
error = uiomove((void *)cdesc, len, &uio);
free(cdesc, M_TEMP, cdesc_len);
return (error);
}
default:
return (EINVAL);
}
return (0);
}
/*
* Explore device tree from the root. We need mutual exclusion to this
* hub while traversing the device tree, but this is guaranteed since this
* function is only called from the task thread, with one exception:
* usb_attach() calls this function, but there shouldn't be anything else
* trying to explore this hub at that time.
*/
void
usb_explore(void *v)
{
struct usb_softc *sc = v;
struct timeval now, waited;
int pwrdly, waited_ms;
DPRINTFN(2,("%s: %s\n", __func__, sc->sc_dev.dv_xname));
#ifdef USB_DEBUG
if (usb_noexplore)
return;
#endif
if (sc->sc_bus->dying)
return;
if (sc->sc_bus->flags & USB_BUS_CONFIG_PENDING) {
/*
* If this is a low/full speed hub and there is a high
* speed hub that hasn't explored yet, reschedule this
* task, allowing the high speed explore task to run.
*/
if (sc->sc_bus->usbrev < USBREV_2_0 && explore_pending > 0) {
usb_add_task(sc->sc_bus->root_hub,
&sc->sc_explore_task);
return;
}
/*
* Wait for power to stabilize.
*/
getmicrouptime(&now);
timersub(&now, &sc->sc_ptime, &waited);
waited_ms = waited.tv_sec * 1000 + waited.tv_usec / 1000;
pwrdly = sc->sc_bus->root_hub->hub->powerdelay +
USB_EXTRA_POWER_UP_TIME;
if (pwrdly > waited_ms)
usb_delay_ms(sc->sc_bus, pwrdly - waited_ms);
}
if (sc->sc_bus->flags & USB_BUS_DISCONNECTING) {
/* Prevent new tasks from being scheduled. */
sc->sc_bus->dying = 1;
/* Make all devices disconnect. */
if (sc->sc_port.device != NULL) {
usbd_detach(sc->sc_port.device, (struct device *)sc);
sc->sc_port.device = NULL;
}
sc->sc_bus->flags &= ~USB_BUS_DISCONNECTING;
} else {
sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
}
if (sc->sc_bus->flags & USB_BUS_CONFIG_PENDING) {
DPRINTF(("%s: %s: first explore done\n", __func__,
sc->sc_dev.dv_xname));
if (sc->sc_bus->usbrev == USBREV_2_0 && explore_pending)
explore_pending--;
config_pending_decr();
sc->sc_bus->flags &= ~(USB_BUS_CONFIG_PENDING);
}
}
void
usb_needs_explore(struct usbd_device *dev, int first_explore)
{
struct usb_softc *usbctl = (struct usb_softc *)dev->bus->usbctl;
DPRINTFN(3,("%s: %s\n", usbctl->sc_dev.dv_xname, __func__));
if (!first_explore && (dev->bus->flags & USB_BUS_CONFIG_PENDING)) {
DPRINTF(("%s: %s: not exploring before first explore\n",
__func__, usbctl->sc_dev.dv_xname));
return;
}
usb_add_task(dev, &usbctl->sc_explore_task);
}
void
usb_needs_reattach(struct usbd_device *dev)
{
DPRINTFN(2,("usb_needs_reattach\n"));
dev->powersrc->reattach = 1;
usb_needs_explore(dev, 0);
}
void
usb_schedsoftintr(struct usbd_bus *bus)
{
DPRINTFN(10,("%s: polling=%d\n", __func__, bus->use_polling));
/* In case usb(4) is disabled */
if (bus->soft == NULL)
return;
if (bus->use_polling) {
bus->methods->soft_intr(bus);
} else {
softintr_schedule(bus->soft);
}
}
int
usb_activate(struct device *self, int act)
{
struct usb_softc *sc = (struct usb_softc *)self;
int rv = 0;
switch (act) {
case DVACT_QUIESCE:
if (sc->sc_bus->root_hub != NULL)
usb_detach_roothub(sc);
break;
case DVACT_RESUME:
sc->sc_bus->dying = 0;
/*
* Make sure the root hub is present before interrupts
* get enabled. As long as the bus is in polling mode
* it is safe to call usbd_new_device() now since root
* hub transfers do not need to sleep.
*/
sc->sc_bus->use_polling++;
if (!usb_attach_roothub(sc))
usb_needs_explore(sc->sc_bus->root_hub, 0);
sc->sc_bus->use_polling--;
break;
default:
rv = config_activate_children(self, act);
break;
}
return (rv);
}
int
usb_detach(struct device *self, int flags)
{
struct usb_softc *sc = (struct usb_softc *)self;
if (sc->sc_bus->root_hub != NULL) {
usb_detach_roothub(sc);
if (--usb_nbuses == 0) {
usb_run_tasks = usb_run_abort_tasks = 0;
wakeup(&usb_run_abort_tasks);
wakeup(&usb_run_tasks);
}
}
if (sc->sc_bus->soft != NULL) {
softintr_disestablish(sc->sc_bus->soft);
sc->sc_bus->soft = NULL;
}
#if NBPFILTER > 0
bpfsdetach(sc->sc_bus->bpfif);
#endif
return (0);
}
void
usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir)
{
#if NBPFILTER > 0
struct usb_softc *sc = (struct usb_softc *)bus->usbctl;
usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
union {
struct usbpcap_ctl_hdr uch;
struct usbpcap_iso_hdr_full uih;
} h;
struct usbpcap_pkt_hdr *uph = &h.uch.uch_hdr;
uint32_t nframes, offset;
unsigned int bpfdir;
void *data = NULL;
size_t flen;
caddr_t bpf;
int i;
bpf = bus->bpf;
if (bpf == NULL)
return;
switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
case UE_CONTROL:
/* Control transfer headers include an extra byte */
uph->uph_hlen = htole16(sizeof(struct usbpcap_ctl_hdr));
uph->uph_xfertype = USBPCAP_TRANSFER_CONTROL;
break;
case UE_ISOCHRONOUS:
offset = 0;
nframes = xfer->nframes;
#ifdef DIAGNOSTIC
if (nframes > _USBPCAP_MAX_ISOFRAMES) {
printf("%s: too many frames: %d > %d\n", __func__,
xfer->nframes, _USBPCAP_MAX_ISOFRAMES);
nframes = _USBPCAP_MAX_ISOFRAMES;
}
#endif
/* Isochronous transfer headers include space for one frame */
flen = (nframes - 1) * sizeof(struct usbpcap_iso_pkt);
uph->uph_hlen = htole16(sizeof(struct usbpcap_iso_hdr) + flen);
uph->uph_xfertype = USBPCAP_TRANSFER_ISOCHRONOUS;
h.uih.uih_startframe = 0; /* not yet used */
h.uih.uih_nframes = nframes;
h.uih.uih_errors = 0; /* we don't have per-frame error */
for (i = 0; i < nframes; i++) {
h.uih.uih_frames[i].uip_offset = offset;
h.uih.uih_frames[i].uip_length = xfer->frlengths[i];
/* See above, we don't have per-frame error */
h.uih.uih_frames[i].uip_status = 0;
offset += xfer->frlengths[i];
}
break;
case UE_BULK:
uph->uph_hlen = htole16(sizeof(*uph));
uph->uph_xfertype = USBPCAP_TRANSFER_BULK;
break;
case UE_INTERRUPT:
uph->uph_hlen = htole16(sizeof(*uph));
uph->uph_xfertype = USBPCAP_TRANSFER_INTERRUPT;
break;
default:
return;
}
uph->uph_id = 0; /* not yet used */
uph->uph_status = htole32(xfer->status);
uph->uph_function = 0; /* not yet used */
uph->uph_bus = htole32(sc->sc_dev.dv_unit);
uph->uph_devaddr = htole16(xfer->device->address);
uph->uph_epaddr = ed->bEndpointAddress;
uph->uph_info = 0;
/* Outgoing control requests start with a STAGE dump. */
if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_OUT)) {
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_SETUP;
uph->uph_dlen = sizeof(usb_device_request_t);
bpf_tap_hdr(bpf, uph, uph->uph_hlen, &xfer->request,
uph->uph_dlen, BPF_DIRECTION_OUT);
}
if (dir == USBTAP_DIR_OUT) {
bpfdir = BPF_DIRECTION_OUT;
if (!usbd_xfer_isread(xfer)) {
data = KERNADDR(&xfer->dmabuf, 0);
uph->uph_dlen = xfer->length;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA;
} else {
data = NULL;
uph->uph_dlen = 0;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
}
} else { /* USBTAP_DIR_IN */
bpfdir = BPF_DIRECTION_IN;
uph->uph_info = USBPCAP_INFO_DIRECTION_IN;
if (usbd_xfer_isread(xfer)) {
data = KERNADDR(&xfer->dmabuf, 0);
uph->uph_dlen = xfer->actlen;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA;
} else {
data = NULL;
uph->uph_dlen = 0;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
}
}
/* Dump bulk/intr/iso data, ctrl DATA or STATUS stage. */
bpf_tap_hdr(bpf, uph, uph->uph_hlen, data, uph->uph_dlen, bpfdir);
/* Incoming control requests with DATA need a STATUS stage. */
if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_IN) &&
(h.uch.uch_stage == USBPCAP_CONTROL_STAGE_DATA)) {
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
uph->uph_dlen = 0;
bpf_tap_hdr(bpf, uph, uph->uph_hlen, NULL, 0, BPF_DIRECTION_IN);
}
#endif
}
37
27
17
17
34
28
24
905
2
901
904
1
52
9
3
30
2
807
2
899
904
4
651
653
17
651
8
209
437
176
262
208
48
8
2
2
1
3
1
1
1
1
1
14
14
13
7
11
17
17
1
17
13
4
17
7
2
17
10
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
/* $OpenBSD: if_ethersubr.c,v 1.284 2022/06/29 09:08:07 mvs Exp $ */
/* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93
*/
/*
%%% portions-copyright-nrl-95
Portions of this software are Copyright 1995-1998 by Randall Atkinson,
Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
Reserved. All rights under this copyright have been assigned to the US
Naval Research Laboratory (NRL). The NRL Copyright Notice and License
Agreement Version 1.1 (January 17, 1995) applies to these portions of the
software.
You should have received a copy of the license with this software. If you
didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
*/
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/timeout.h>
#include <sys/smr.h>
#include <net/if.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/if_llc.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip_ipsp.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include "vlan.h"
#if NVLAN > 0
#include <net/if_vlan_var.h>
#endif
#include "carp.h"
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
#include "pppoe.h"
#if NPPPOE > 0
#include <net/if_pppoe.h>
#endif
#include "bpe.h"
#if NBPE > 0
#include <net/if_bpe.h>
#endif
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#endif
#ifdef PIPEX
#include <net/pipex.h>
#endif
#ifdef MPLS
#include <netmpls/mpls.h>
#endif /* MPLS */
u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
u_int8_t etheranyaddr[ETHER_ADDR_LEN] =
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#define senderr(e) { error = (e); goto bad;}
int
ether_ioctl(struct ifnet *ifp, struct arpcom *arp, u_long cmd, caddr_t data)
{
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ifp->if_hardmtu)
error = EINVAL;
else
ifp->if_mtu = ifr->ifr_mtu;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
if (ifp->if_flags & IFF_MULTICAST) {
error = (cmd == SIOCADDMULTI) ?
ether_addmulti(ifr, arp) :
ether_delmulti(ifr, arp);
} else
error = ENOTTY;
break;
default:
error = ENOTTY;
}
return (error);
}
void
ether_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
{
if (rt == NULL)
return;
switch (rt_key(rt)->sa_family) {
case AF_INET:
arp_rtrequest(ifp, req, rt);
break;
#ifdef INET6
case AF_INET6:
nd6_rtrequest(ifp, req, rt);
break;
#endif
default:
break;
}
}
int
ether_resolve(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt, struct ether_header *eh)
{
struct arpcom *ac = (struct arpcom *)ifp;
sa_family_t af = dst->sa_family;
int error = 0;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
senderr(ENETDOWN);
KASSERT(rt != NULL || ISSET(m->m_flags, M_MCAST|M_BCAST) ||
af == AF_UNSPEC || af == pseudo_AF_HDRCMPLT);
#ifdef DIAGNOSTIC
if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) {
printf("%s: trying to send packet on wrong domain. "
"if %d vs. mbuf %d\n", ifp->if_xname,
ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid));
}
#endif
switch (af) {
case AF_INET:
error = arpresolve(ifp, rt, m, dst, eh->ether_dhost);
if (error)
return (error);
eh->ether_type = htons(ETHERTYPE_IP);
/*
* If broadcasting on a simplex interface, loopback a copy.
* The checksum must be calculated in software. Keep the
* condition in sync with in_ifcap_cksum().
*/
if (ISSET(m->m_flags, M_BCAST) &&
ISSET(ifp->if_flags, IFF_SIMPLEX) &&
!m->m_pkthdr.pf.routed) {
struct mbuf *mcopy;
/* XXX Should we input an unencrypted IPsec packet? */
mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (mcopy != NULL)
if_input_local(ifp, mcopy, af);
}
break;
#ifdef INET6
case AF_INET6:
KERNEL_LOCK();
/* XXXSMP there is a MP race in nd6_resolve() */
error = nd6_resolve(ifp, rt, m, dst, eh->ether_dhost);
KERNEL_UNLOCK();
if (error)
return (error);
eh->ether_type = htons(ETHERTYPE_IPV6);
break;
#endif
#ifdef MPLS
case AF_MPLS:
if (rt == NULL)
senderr(EHOSTUNREACH);
if (!ISSET(ifp->if_xflags, IFXF_MPLS))
senderr(ENETUNREACH);
dst = ISSET(rt->rt_flags, RTF_GATEWAY) ?
rt->rt_gateway : rt_key(rt);
switch (dst->sa_family) {
case AF_LINK:
if (satosdl(dst)->sdl_alen < sizeof(eh->ether_dhost))
senderr(EHOSTUNREACH);
memcpy(eh->ether_dhost, LLADDR(satosdl(dst)),
sizeof(eh->ether_dhost));
break;
#ifdef INET6
case AF_INET6:
KERNEL_LOCK();
/* XXXSMP there is a MP race in nd6_resolve() */
error = nd6_resolve(ifp, rt, m, dst, eh->ether_dhost);
KERNEL_UNLOCK();
if (error)
return (error);
break;
#endif
case AF_INET:
error = arpresolve(ifp, rt, m, dst, eh->ether_dhost);
if (error)
return (error);
break;
default:
senderr(EHOSTUNREACH);
}
/* XXX handling for simplex devices in case of M/BCAST ?? */
if (m->m_flags & (M_BCAST | M_MCAST))
eh->ether_type = htons(ETHERTYPE_MPLS_MCAST);
else
eh->ether_type = htons(ETHERTYPE_MPLS);
break;
#endif /* MPLS */
case pseudo_AF_HDRCMPLT:
/* take the whole header from the sa */
memcpy(eh, dst->sa_data, sizeof(*eh));
return (0);
case AF_UNSPEC:
/* take the dst and type from the sa, but get src below */
memcpy(eh, dst->sa_data, sizeof(*eh));
break;
default:
printf("%s: can't handle af%d\n", ifp->if_xname, af);
senderr(EAFNOSUPPORT);
}
memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(eh->ether_shost));
return (0);
bad:
m_freem(m);
return (error);
}
struct mbuf*
ether_encap(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt, int *errorp)
{
struct ether_header eh;
int error;
error = ether_resolve(ifp, m, dst, rt, &eh);
switch (error) {
case 0:
break;
case EAGAIN:
error = 0;
default:
*errorp = error;
return (NULL);
}
m = m_prepend(m, ETHER_ALIGN + sizeof(eh), M_DONTWAIT);
if (m == NULL) {
*errorp = ENOBUFS;
return (NULL);
}
m_adj(m, ETHER_ALIGN);
memcpy(mtod(m, struct ether_header *), &eh, sizeof(eh));
return (m);
}
int
ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
int error;
m = ether_encap(ifp, m, dst, rt, &error);
if (m == NULL)
return (error);
return (if_enqueue(ifp, m));
}
/*
* Process a received Ethernet packet.
*
* Ethernet input has several "phases" of filtering packets to
* support virtual/pseudo interfaces before actual layer 3 protocol
* handling.
*
* First phase:
*
* The first phase supports drivers that aggregate multiple Ethernet
* ports into a single logical interface, ie, aggr(4) and trunk(4).
* These drivers intercept packets by swapping out the if_input handler
* on the "port" interfaces to steal the packets before they get here
* to ether_input().
*/
void
ether_input(struct ifnet *ifp, struct mbuf *m)
{
struct ether_header *eh;
void (*input)(struct ifnet *, struct mbuf *);
u_int16_t etype;
struct arpcom *ac;
const struct ether_brport *eb;
unsigned int sdelim = 0;
uint64_t dst, self;
/* Drop short frames */
if (m->m_len < ETHER_HDR_LEN)
goto dropanyway;
/*
* Second phase: service delimited packet filtering.
*
* Let vlan(4) and svlan(4) look at "service delimited"
* packets. If a virtual interface does not exist to take
* those packets, they're returned to ether_input() so a
* bridge can have a go at forwarding them.
*/
eh = mtod(m, struct ether_header *);
dst = ether_addr_to_e64((struct ether_addr *)eh->ether_dhost);
etype = ntohs(eh->ether_type);
if (ISSET(m->m_flags, M_VLANTAG) ||
etype == ETHERTYPE_VLAN || etype == ETHERTYPE_QINQ) {
#if NVLAN > 0
m = vlan_input(ifp, m, &sdelim);
if (m == NULL)
return;
#else
sdelim = 1;
#endif
}
/*
* Third phase: bridge processing.
*
* Give the packet to a bridge interface, ie, bridge(4),
* veb(4), or tpmr(4), if it is configured. A bridge
* may take the packet and forward it to another port, or it
* may return it here to ether_input() to support local
* delivery to this port.
*/
ac = (struct arpcom *)ifp;
smr_read_enter();
eb = SMR_PTR_GET(&ac->ac_brport);
if (eb != NULL)
eb->eb_port_take(eb->eb_port);
smr_read_leave();
if (eb != NULL) {
m = (*eb->eb_input)(ifp, m, dst, eb->eb_port);
eb->eb_port_rele(eb->eb_port);
if (m == NULL) {
return;
}
}
/*
* Fourth phase: drop service delimited packets.
*
* If the packet has a tag, and a bridge didn't want it,
* it's not for this port.
*/
if (sdelim)
goto dropanyway;
/*
* Fifth phase: destination address check.
*
* Is the packet specifically addressed to this port?
*/
eh = mtod(m, struct ether_header *);
self = ether_addr_to_e64((struct ether_addr *)ac->ac_enaddr);
if (dst != self) {
#if NCARP > 0
/*
* If it's not for this port, it could be for carp(4).
*/
if (ifp->if_type != IFT_CARP &&
!SRPL_EMPTY_LOCKED(&ifp->if_carp)) {
m = carp_input(ifp, m, dst);
if (m == NULL)
return;
eh = mtod(m, struct ether_header *);
}
#endif
/*
* If not, it must be multicast or broadcast to go further.
*/
if (!ETH64_IS_MULTICAST(dst))
goto dropanyway;
/*
* If this is not a simplex interface, drop the packet
* if it came from us.
*/
if ((ifp->if_flags & IFF_SIMPLEX) == 0) {
uint64_t src = ether_addr_to_e64(
(struct ether_addr *)eh->ether_shost);
if (self == src)
goto dropanyway;
}
SET(m->m_flags, ETH64_IS_BROADCAST(dst) ? M_BCAST : M_MCAST);
ifp->if_imcasts++;
}
/*
* Sixth phase: protocol demux.
*
* At this point it is known that the packet is destined
* for layer 3 protocol handling on the local port.
*/
etype = ntohs(eh->ether_type);
switch (etype) {
case ETHERTYPE_IP:
input = ipv4_input;
break;
case ETHERTYPE_ARP:
if (ifp->if_flags & IFF_NOARP)
goto dropanyway;
input = arpinput;
break;
case ETHERTYPE_REVARP:
if (ifp->if_flags & IFF_NOARP)
goto dropanyway;
input = revarpinput;
break;
#ifdef INET6
/*
* Schedule IPv6 software interrupt for incoming IPv6 packet.
*/
case ETHERTYPE_IPV6:
input = ipv6_input;
break;
#endif /* INET6 */
#if NPPPOE > 0 || defined(PIPEX)
case ETHERTYPE_PPPOEDISC:
case ETHERTYPE_PPPOE:
if (m->m_flags & (M_MCAST | M_BCAST))
goto dropanyway;
#ifdef PIPEX
if (pipex_enable) {
struct pipex_session *session;
if ((session = pipex_pppoe_lookup_session(m)) != NULL) {
pipex_pppoe_input(m, session);
pipex_rele_session(session);
return;
}
}
#endif
if (etype == ETHERTYPE_PPPOEDISC) {
if (mq_enqueue(&pppoediscinq, m) == 0)
schednetisr(NETISR_PPPOE);
} else {
if (mq_enqueue(&pppoeinq, m) == 0)
schednetisr(NETISR_PPPOE);
}
return;
#endif
#ifdef MPLS
case ETHERTYPE_MPLS:
case ETHERTYPE_MPLS_MCAST:
input = mpls_input;
break;
#endif
#if NBPE > 0
case ETHERTYPE_PBB:
bpe_input(ifp, m);
return;
#endif
default:
goto dropanyway;
}
m_adj(m, sizeof(*eh));
(*input)(ifp, m);
return;
dropanyway:
m_freem(m);
return;
}
int
ether_brport_isset(struct ifnet *ifp)
{
struct arpcom *ac = (struct arpcom *)ifp;
KERNEL_ASSERT_LOCKED();
if (SMR_PTR_GET_LOCKED(&ac->ac_brport) != NULL)
return (EBUSY);
return (0);
}
void
ether_brport_set(struct ifnet *ifp, const struct ether_brport *eb)
{
struct arpcom *ac = (struct arpcom *)ifp;
KERNEL_ASSERT_LOCKED();
KASSERTMSG(SMR_PTR_GET_LOCKED(&ac->ac_brport) == NULL,
"%s setting an already set brport", ifp->if_xname);
SMR_PTR_SET_LOCKED(&ac->ac_brport, eb);
}
void
ether_brport_clr(struct ifnet *ifp)
{
struct arpcom *ac = (struct arpcom *)ifp;
KERNEL_ASSERT_LOCKED();
KASSERTMSG(SMR_PTR_GET_LOCKED(&ac->ac_brport) != NULL,
"%s clearing an already clear brport", ifp->if_xname);
SMR_PTR_SET_LOCKED(&ac->ac_brport, NULL);
}
const struct ether_brport *
ether_brport_get(struct ifnet *ifp)
{
struct arpcom *ac = (struct arpcom *)ifp;
SMR_ASSERT_CRITICAL();
return (SMR_PTR_GET(&ac->ac_brport));
}
const struct ether_brport *
ether_brport_get_locked(struct ifnet *ifp)
{
struct arpcom *ac = (struct arpcom *)ifp;
KERNEL_ASSERT_LOCKED();
return (SMR_PTR_GET_LOCKED(&ac->ac_brport));
}
/*
* Convert Ethernet address to printable (loggable) representation.
*/
static char digits[] = "0123456789abcdef";
char *
ether_sprintf(u_char *ap)
{
int i;
static char etherbuf[ETHER_ADDR_LEN * 3];
char *cp = etherbuf;
for (i = 0; i < ETHER_ADDR_LEN; i++) {
*cp++ = digits[*ap >> 4];
*cp++ = digits[*ap++ & 0xf];
*cp++ = ':';
}
*--cp = 0;
return (etherbuf);
}
/*
* Generate a (hopefully) acceptable MAC address, if asked.
*/
void
ether_fakeaddr(struct ifnet *ifp)
{
static int unit;
int rng = arc4random();
/* Non-multicast; locally administered address */
((struct arpcom *)ifp)->ac_enaddr[0] = 0xfe;
((struct arpcom *)ifp)->ac_enaddr[1] = 0xe1;
((struct arpcom *)ifp)->ac_enaddr[2] = 0xba;
((struct arpcom *)ifp)->ac_enaddr[3] = 0xd0 | (unit++ & 0xf);
((struct arpcom *)ifp)->ac_enaddr[4] = rng;
((struct arpcom *)ifp)->ac_enaddr[5] = rng >> 8;
}
/*
* Perform common duties while attaching to interface list
*/
void
ether_ifattach(struct ifnet *ifp)
{
struct arpcom *ac = (struct arpcom *)ifp;
/*
* Any interface which provides a MAC address which is obviously
* invalid gets whacked, so that users will notice.
*/
if (ETHER_IS_MULTICAST(((struct arpcom *)ifp)->ac_enaddr))
ether_fakeaddr(ifp);
ifp->if_type = IFT_ETHER;
ifp->if_addrlen = ETHER_ADDR_LEN;
ifp->if_hdrlen = ETHER_HDR_LEN;
ifp->if_mtu = ETHERMTU;
ifp->if_input = ether_input;
if (ifp->if_output == NULL)
ifp->if_output = ether_output;
ifp->if_rtrequest = ether_rtrequest;
if (ifp->if_hardmtu == 0)
ifp->if_hardmtu = ETHERMTU;
if_alloc_sadl(ifp);
memcpy(LLADDR(ifp->if_sadl), ac->ac_enaddr, ifp->if_addrlen);
LIST_INIT(&ac->ac_multiaddrs);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN);
#endif
}
void
ether_ifdetach(struct ifnet *ifp)
{
struct arpcom *ac = (struct arpcom *)ifp;
struct ether_multi *enm;
/* Undo pseudo-driver changes. */
if_deactivate(ifp);
for (enm = LIST_FIRST(&ac->ac_multiaddrs);
enm != NULL;
enm = LIST_FIRST(&ac->ac_multiaddrs)) {
LIST_REMOVE(enm, enm_list);
free(enm, M_IFMADDR, sizeof *enm);
}
}
#if 0
/*
* This is for reference. We have table-driven versions of the
* crc32 generators, which are faster than the double-loop.
*/
u_int32_t __pure
ether_crc32_le_update(u_int_32_t crc, const u_int8_t *buf, size_t len)
{
u_int32_t c, carry;
size_t i, j;
for (i = 0; i < len; i++) {
c = buf[i];
for (j = 0; j < 8; j++) {
carry = ((crc & 0x01) ? 1 : 0) ^ (c & 0x01);
crc >>= 1;
c >>= 1;
if (carry)
crc = (crc ^ ETHER_CRC_POLY_LE);
}
}
return (crc);
}
u_int32_t __pure
ether_crc32_be_update(u_int_32_t crc, const u_int8_t *buf, size_t len)
{
u_int32_t c, carry;
size_t i, j;
for (i = 0; i < len; i++) {
c = buf[i];
for (j = 0; j < 8; j++) {
carry = ((crc & 0x80000000U) ? 1 : 0) ^ (c & 0x01);
crc <<= 1;
c >>= 1;
if (carry)
crc = (crc ^ ETHER_CRC_POLY_BE) | carry;
}
}
return (crc);
}
#else
u_int32_t __pure
ether_crc32_le_update(u_int32_t crc, const u_int8_t *buf, size_t len)
{
static const u_int32_t crctab[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
size_t i;
for (i = 0; i < len; i++) {
crc ^= buf[i];
crc = (crc >> 4) ^ crctab[crc & 0xf];
crc = (crc >> 4) ^ crctab[crc & 0xf];
}
return (crc);
}
u_int32_t __pure
ether_crc32_be_update(u_int32_t crc, const u_int8_t *buf, size_t len)
{
static const u_int8_t rev[] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
};
static const u_int32_t crctab[] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd
};
size_t i;
u_int8_t data;
for (i = 0; i < len; i++) {
data = buf[i];
crc = (crc << 4) ^ crctab[(crc >> 28) ^ rev[data & 0xf]];
crc = (crc << 4) ^ crctab[(crc >> 28) ^ rev[data >> 4]];
}
return (crc);
}
#endif
u_int32_t
ether_crc32_le(const u_int8_t *buf, size_t len)
{
return ether_crc32_le_update(0xffffffff, buf, len);
}
u_int32_t
ether_crc32_be(const u_int8_t *buf, size_t len)
{
return ether_crc32_be_update(0xffffffff, buf, len);
}
u_char ether_ipmulticast_min[ETHER_ADDR_LEN] =
{ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
u_char ether_ipmulticast_max[ETHER_ADDR_LEN] =
{ 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
#ifdef INET6
u_char ether_ip6multicast_min[ETHER_ADDR_LEN] =
{ 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 };
u_char ether_ip6multicast_max[ETHER_ADDR_LEN] =
{ 0x33, 0x33, 0xff, 0xff, 0xff, 0xff };
#endif
/*
* Convert a sockaddr into an Ethernet address or range of Ethernet
* addresses.
*/
int
ether_multiaddr(struct sockaddr *sa, u_int8_t addrlo[ETHER_ADDR_LEN],
u_int8_t addrhi[ETHER_ADDR_LEN])
{
struct sockaddr_in *sin;
#ifdef INET6
struct sockaddr_in6 *sin6;
#endif /* INET6 */
switch (sa->sa_family) {
case AF_UNSPEC:
memcpy(addrlo, sa->sa_data, ETHER_ADDR_LEN);
memcpy(addrhi, addrlo, ETHER_ADDR_LEN);
break;
case AF_INET:
sin = satosin(sa);
if (sin->sin_addr.s_addr == INADDR_ANY) {
/*
* An IP address of INADDR_ANY means listen to
* or stop listening to all of the Ethernet
* multicast addresses used for IP.
* (This is for the sake of IP multicast routers.)
*/
memcpy(addrlo, ether_ipmulticast_min, ETHER_ADDR_LEN);
memcpy(addrhi, ether_ipmulticast_max, ETHER_ADDR_LEN);
} else {
ETHER_MAP_IP_MULTICAST(&sin->sin_addr, addrlo);
memcpy(addrhi, addrlo, ETHER_ADDR_LEN);
}
break;
#ifdef INET6
case AF_INET6:
sin6 = satosin6(sa);
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
/*
* An IP6 address of 0 means listen to or stop
* listening to all of the Ethernet multicast
* address used for IP6.
*
* (This might not be healthy, given IPv6's reliance on
* multicast for things like neighbor discovery.
* Perhaps initializing all-nodes, solicited nodes, and
* possibly all-routers for this interface afterwards
* is not a bad idea.)
*/
memcpy(addrlo, ether_ip6multicast_min, ETHER_ADDR_LEN);
memcpy(addrhi, ether_ip6multicast_max, ETHER_ADDR_LEN);
} else {
ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, addrlo);
memcpy(addrhi, addrlo, ETHER_ADDR_LEN);
}
break;
#endif
default:
return (EAFNOSUPPORT);
}
return (0);
}
/*
* Add an Ethernet multicast address or range of addresses to the list for a
* given interface.
*/
int
ether_addmulti(struct ifreq *ifr, struct arpcom *ac)
{
struct ether_multi *enm;
u_char addrlo[ETHER_ADDR_LEN];
u_char addrhi[ETHER_ADDR_LEN];
int s = splnet(), error;
error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
if (error != 0) {
splx(s);
return (error);
}
/*
* Verify that we have valid Ethernet multicast addresses.
*/
if ((addrlo[0] & 0x01) != 1 || (addrhi[0] & 0x01) != 1) {
splx(s);
return (EINVAL);
}
/*
* See if the address range is already in the list.
*/
ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
if (enm != NULL) {
/*
* Found it; just increment the reference count.
*/
++enm->enm_refcount;
splx(s);
return (0);
}
/*
* New address or range; malloc a new multicast record
* and link it into the interface's multicast list.
*/
enm = malloc(sizeof(*enm), M_IFMADDR, M_NOWAIT);
if (enm == NULL) {
splx(s);
return (ENOBUFS);
}
memcpy(enm->enm_addrlo, addrlo, ETHER_ADDR_LEN);
memcpy(enm->enm_addrhi, addrhi, ETHER_ADDR_LEN);
enm->enm_refcount = 1;
LIST_INSERT_HEAD(&ac->ac_multiaddrs, enm, enm_list);
ac->ac_multicnt++;
if (memcmp(addrlo, addrhi, ETHER_ADDR_LEN) != 0)
ac->ac_multirangecnt++;
splx(s);
/*
* Return ENETRESET to inform the driver that the list has changed
* and its reception filter should be adjusted accordingly.
*/
return (ENETRESET);
}
/*
* Delete a multicast address record.
*/
int
ether_delmulti(struct ifreq *ifr, struct arpcom *ac)
{
struct ether_multi *enm;
u_char addrlo[ETHER_ADDR_LEN];
u_char addrhi[ETHER_ADDR_LEN];
int s = splnet(), error;
error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
if (error != 0) {
splx(s);
return (error);
}
/*
* Look up the address in our list.
*/
ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm);
if (enm == NULL) {
splx(s);
return (ENXIO);
}
if (--enm->enm_refcount != 0) {
/*
* Still some claims to this record.
*/
splx(s);
return (0);
}
/*
* No remaining claims to this record; unlink and free it.
*/
LIST_REMOVE(enm, enm_list);
free(enm, M_IFMADDR, sizeof *enm);
ac->ac_multicnt--;
if (memcmp(addrlo, addrhi, ETHER_ADDR_LEN) != 0)
ac->ac_multirangecnt--;
splx(s);
/*
* Return ENETRESET to inform the driver that the list has changed
* and its reception filter should be adjusted accordingly.
*/
return (ENETRESET);
}
uint64_t
ether_addr_to_e64(const struct ether_addr *ea)
{
uint64_t e64 = 0;
size_t i;
for (i = 0; i < nitems(ea->ether_addr_octet); i++) {
e64 <<= 8;
e64 |= ea->ether_addr_octet[i];
}
return (e64);
}
void
ether_e64_to_addr(struct ether_addr *ea, uint64_t e64)
{
size_t i = nitems(ea->ether_addr_octet);
do {
ea->ether_addr_octet[--i] = e64;
e64 >>= 8;
} while (i > 0);
}
18
39
39
23
28
205
198
10
206
204
2
200
201
48
79
78
60
2
58
136
94
1
4
9
9
6
37
5
44
2
27
10
2
21
114
23
74
23
74
77
60
39
11
2
36
47
20
231
136
2
394
44
383
393
38
3
50
49
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
/* $OpenBSD: kern_ktrace.c,v 1.108 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_ktrace.c,v 1.23 1996/02/09 18:59:36 christos Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_ktrace.c 8.2 (Berkeley) 9/23/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/fcntl.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/ktrace.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <sys/pledge.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
void ktrinitheaderraw(struct ktr_header *, uint, pid_t, pid_t);
void ktrinitheader(struct ktr_header *, struct proc *, int);
int ktrstart(struct proc *, struct vnode *, struct ucred *);
int ktrops(struct proc *, struct process *, int, int, struct vnode *,
struct ucred *);
int ktrsetchildren(struct proc *, struct process *, int, int,
struct vnode *, struct ucred *);
int ktrwrite(struct proc *, struct ktr_header *, const void *, size_t);
int ktrwrite2(struct proc *, struct ktr_header *, const void *, size_t,
const void *, size_t);
int ktrwriteraw(struct proc *, struct vnode *, struct ucred *,
struct ktr_header *, struct iovec *);
int ktrcanset(struct proc *, struct process *);
/*
* Clear the trace settings in a correct way (to avoid races).
*/
void
ktrcleartrace(struct process *pr)
{
struct vnode *vp;
struct ucred *cred;
if (pr->ps_tracevp != NULL) {
vp = pr->ps_tracevp;
cred = pr->ps_tracecred;
pr->ps_traceflag = 0;
pr->ps_tracevp = NULL;
pr->ps_tracecred = NULL;
vp->v_writecount--;
vrele(vp);
crfree(cred);
}
}
/*
* Change the trace setting in a correct way (to avoid races).
*/
void
ktrsettrace(struct process *pr, int facs, struct vnode *newvp,
struct ucred *newcred)
{
struct vnode *oldvp;
struct ucred *oldcred;
KASSERT(newvp != NULL);
KASSERT(newcred != NULL);
pr->ps_traceflag |= facs;
/* nothing to change about where the trace goes? */
if (pr->ps_tracevp == newvp && pr->ps_tracecred == newcred)
return;
vref(newvp);
crhold(newcred);
newvp->v_writecount++;
oldvp = pr->ps_tracevp;
oldcred = pr->ps_tracecred;
pr->ps_tracevp = newvp;
pr->ps_tracecred = newcred;
if (oldvp != NULL) {
oldvp->v_writecount--;
vrele(oldvp);
crfree(oldcred);
}
}
void
ktrinitheaderraw(struct ktr_header *kth, uint type, pid_t pid, pid_t tid)
{
memset(kth, 0, sizeof(struct ktr_header));
kth->ktr_type = type;
nanotime(&kth->ktr_time);
kth->ktr_pid = pid;
kth->ktr_tid = tid;
}
void
ktrinitheader(struct ktr_header *kth, struct proc *p, int type)
{
struct process *pr = p->p_p;
ktrinitheaderraw(kth, type, pr->ps_pid, p->p_tid + THREAD_PID_OFFSET);
memcpy(kth->ktr_comm, pr->ps_comm, sizeof(kth->ktr_comm));
}
int
ktrstart(struct proc *p, struct vnode *vp, struct ucred *cred)
{
struct ktr_header kth;
ktrinitheaderraw(&kth, htobe32(KTR_START), -1, -1);
return (ktrwriteraw(p, vp, cred, &kth, NULL));
}
void
ktrsyscall(struct proc *p, register_t code, size_t argsize, register_t args[])
{
struct ktr_header kth;
struct ktr_syscall *ktp;
size_t len = sizeof(struct ktr_syscall) + argsize;
register_t *argp;
u_int nargs = 0;
int i;
if (code == SYS_sysctl) {
/*
* The sysctl encoding stores the mib[]
* array because it is interesting.
*/
if (args[1] > 0)
nargs = lmin(args[1], CTL_MAXNAME);
len += nargs * sizeof(int);
}
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_SYSCALL);
ktp = malloc(len, M_TEMP, M_WAITOK);
ktp->ktr_code = code;
ktp->ktr_argsize = argsize;
argp = (register_t *)((char *)ktp + sizeof(struct ktr_syscall));
for (i = 0; i < (argsize / sizeof *argp); i++)
*argp++ = args[i];
if (nargs && copyin((void *)args[0], argp, nargs * sizeof(int)))
memset(argp, 0, nargs * sizeof(int));
ktrwrite(p, &kth, ktp, len);
free(ktp, M_TEMP, len);
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
ktrsysret(struct proc *p, register_t code, int error,
const register_t retval[2])
{
struct ktr_header kth;
struct ktr_sysret ktp;
int len;
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_SYSRET);
ktp.ktr_code = code;
ktp.ktr_error = error;
if (error)
len = 0;
else if (code == SYS_lseek)
/* the one exception: lseek on ILP32 needs more */
len = sizeof(long long);
else
len = sizeof(register_t);
ktrwrite2(p, &kth, &ktp, sizeof(ktp), retval, len);
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
ktrnamei(struct proc *p, char *path)
{
struct ktr_header kth;
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_NAMEI);
ktrwrite(p, &kth, path, strlen(path));
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
ktrgenio(struct proc *p, int fd, enum uio_rw rw, struct iovec *iov,
ssize_t len)
{
struct ktr_header kth;
struct ktr_genio ktp;
caddr_t cp;
int count, error;
int buflen;
atomic_setbits_int(&p->p_flag, P_INKTR);
/* beware overflow */
if (len > PAGE_SIZE)
buflen = PAGE_SIZE;
else
buflen = len + sizeof(struct ktr_genio);
ktrinitheader(&kth, p, KTR_GENIO);
ktp.ktr_fd = fd;
ktp.ktr_rw = rw;
cp = malloc(buflen, M_TEMP, M_WAITOK);
while (len > 0) {
/*
* Don't allow this process to hog the cpu when doing
* huge I/O.
*/
sched_pause(preempt);
count = lmin(iov->iov_len, buflen);
if (count > len)
count = len;
if (copyin(iov->iov_base, cp, count))
break;
KERNEL_LOCK();
error = ktrwrite2(p, &kth, &ktp, sizeof(ktp), cp, count);
KERNEL_UNLOCK();
if (error != 0)
break;
iov->iov_len -= count;
iov->iov_base = (caddr_t)iov->iov_base + count;
if (iov->iov_len == 0)
iov++;
len -= count;
}
free(cp, M_TEMP, buflen);
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
ktrpsig(struct proc *p, int sig, sig_t action, int mask, int code,
siginfo_t *si)
{
struct ktr_header kth;
struct ktr_psig kp;
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_PSIG);
kp.signo = (char)sig;
kp.action = action;
kp.mask = mask;
kp.code = code;
kp.si = *si;
KERNEL_LOCK();
ktrwrite(p, &kth, &kp, sizeof(kp));
KERNEL_UNLOCK();
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
ktrstruct(struct proc *p, const char *name, const void *data, size_t datalen)
{
struct ktr_header kth;
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_STRUCT);
if (data == NULL)
datalen = 0;
KERNEL_LOCK();
ktrwrite2(p, &kth, name, strlen(name) + 1, data, datalen);
KERNEL_UNLOCK();
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
int
ktruser(struct proc *p, const char *id, const void *addr, size_t len)
{
struct ktr_header kth;
struct ktr_user ktp;
int error;
void *memp;
#define STK_PARAMS 128
long long stkbuf[STK_PARAMS / sizeof(long long)];
if (!KTRPOINT(p, KTR_USER))
return (0);
if (len > KTR_USER_MAXLEN)
return (EINVAL);
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_USER);
memset(ktp.ktr_id, 0, KTR_USER_MAXIDLEN);
error = copyinstr(id, ktp.ktr_id, KTR_USER_MAXIDLEN, NULL);
if (error == 0) {
if (len > sizeof(stkbuf))
memp = malloc(len, M_TEMP, M_WAITOK);
else
memp = stkbuf;
error = copyin(addr, memp, len);
if (error == 0)
ktrwrite2(p, &kth, &ktp, sizeof(ktp), memp, len);
if (memp != stkbuf)
free(memp, M_TEMP, len);
}
atomic_clearbits_int(&p->p_flag, P_INKTR);
return (error);
}
void
ktrexec(struct proc *p, int type, const char *data, ssize_t len)
{
struct ktr_header kth;
int count;
int buflen;
assert(type == KTR_EXECARGS || type == KTR_EXECENV);
atomic_setbits_int(&p->p_flag, P_INKTR);
/* beware overflow */
if (len > PAGE_SIZE)
buflen = PAGE_SIZE;
else
buflen = len;
ktrinitheader(&kth, p, type);
while (len > 0) {
/*
* Don't allow this process to hog the cpu when doing
* huge I/O.
*/
sched_pause(preempt);
count = lmin(len, buflen);
if (ktrwrite(p, &kth, data, count) != 0)
break;
len -= count;
data += count;
}
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
ktrpledge(struct proc *p, int error, uint64_t code, int syscall)
{
struct ktr_header kth;
struct ktr_pledge kp;
atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_PLEDGE);
kp.error = error;
kp.code = code;
kp.syscall = syscall;
KERNEL_LOCK();
ktrwrite(p, &kth, &kp, sizeof(kp));
KERNEL_UNLOCK();
atomic_clearbits_int(&p->p_flag, P_INKTR);
}
/* Interface and common routines */
int
doktrace(struct vnode *vp, int ops, int facs, pid_t pid, struct proc *p)
{
struct process *pr = NULL;
struct ucred *cred = NULL;
struct pgrp *pg;
int descend = ops & KTRFLAG_DESCEND;
int ret = 0;
int error = 0;
facs = facs & ~((unsigned)KTRFAC_ROOT);
ops = KTROP(ops);
if (ops != KTROP_CLEAR) {
/*
* an operation which requires a file argument.
*/
cred = p->p_ucred;
if (!vp) {
error = EINVAL;
goto done;
}
if (vp->v_type != VREG) {
error = EACCES;
goto done;
}
}
/*
* Clear all uses of the tracefile
*/
if (ops == KTROP_CLEARFILE) {
LIST_FOREACH(pr, &allprocess, ps_list) {
if (pr->ps_tracevp == vp) {
if (ktrcanset(p, pr))
ktrcleartrace(pr);
else
error = EPERM;
}
}
goto done;
}
/*
* need something to (un)trace (XXX - why is this here?)
*/
if (!facs) {
error = EINVAL;
goto done;
}
if (ops == KTROP_SET) {
if (suser(p) == 0)
facs |= KTRFAC_ROOT;
error = ktrstart(p, vp, cred);
if (error != 0)
goto done;
}
/*
* do it
*/
if (pid < 0) {
/*
* by process group
*/
pg = pgfind(-pid);
if (pg == NULL) {
error = ESRCH;
goto done;
}
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
if (descend)
ret |= ktrsetchildren(p, pr, ops, facs, vp,
cred);
else
ret |= ktrops(p, pr, ops, facs, vp, cred);
}
} else {
/*
* by pid
*/
pr = prfind(pid);
if (pr == NULL) {
error = ESRCH;
goto done;
}
if (descend)
ret |= ktrsetchildren(p, pr, ops, facs, vp, cred);
else
ret |= ktrops(p, pr, ops, facs, vp, cred);
}
if (!ret)
error = EPERM;
done:
return (error);
}
/*
* ktrace system call
*/
int
sys_ktrace(struct proc *p, void *v, register_t *retval)
{
struct sys_ktrace_args /* {
syscallarg(const char *) fname;
syscallarg(int) ops;
syscallarg(int) facs;
syscallarg(pid_t) pid;
} */ *uap = v;
struct vnode *vp = NULL;
const char *fname = SCARG(uap, fname);
struct ucred *cred = NULL;
int error;
if (fname) {
struct nameidata nd;
cred = p->p_ucred;
NDINIT(&nd, 0, 0, UIO_USERSPACE, fname, p);
nd.ni_pledge = PLEDGE_CPATH | PLEDGE_WPATH;
nd.ni_unveil = UNVEIL_CREATE | UNVEIL_WRITE;
if ((error = vn_open(&nd, FWRITE|O_NOFOLLOW, 0)) != 0)
return error;
vp = nd.ni_vp;
VOP_UNLOCK(vp);
}
error = doktrace(vp, SCARG(uap, ops), SCARG(uap, facs),
SCARG(uap, pid), p);
if (vp != NULL)
(void)vn_close(vp, FWRITE, cred, p);
return error;
}
int
ktrops(struct proc *curp, struct process *pr, int ops, int facs,
struct vnode *vp, struct ucred *cred)
{
if (!ktrcanset(curp, pr))
return (0);
if (ops == KTROP_SET)
ktrsettrace(pr, facs, vp, cred);
else {
/* KTROP_CLEAR */
pr->ps_traceflag &= ~facs;
if ((pr->ps_traceflag & KTRFAC_MASK) == 0) {
/* cleared all the facility bits, so stop completely */
ktrcleartrace(pr);
}
}
return (1);
}
int
ktrsetchildren(struct proc *curp, struct process *top, int ops, int facs,
struct vnode *vp, struct ucred *cred)
{
struct process *pr;
int ret = 0;
pr = top;
for (;;) {
ret |= ktrops(curp, pr, ops, facs, vp, cred);
/*
* If this process has children, descend to them next,
* otherwise do any siblings, and if done with this level,
* follow back up the tree (but not past top).
*/
if (!LIST_EMPTY(&pr->ps_children))
pr = LIST_FIRST(&pr->ps_children);
else for (;;) {
if (pr == top)
return (ret);
if (LIST_NEXT(pr, ps_sibling) != NULL) {
pr = LIST_NEXT(pr, ps_sibling);
break;
}
pr = pr->ps_pptr;
}
}
/*NOTREACHED*/
}
int
ktrwrite(struct proc *p, struct ktr_header *kth, const void *aux, size_t len)
{
struct vnode *vp = p->p_p->ps_tracevp;
struct ucred *cred = p->p_p->ps_tracecred;
struct iovec data[2];
int error;
if (vp == NULL)
return 0;
crhold(cred);
data[0].iov_base = (void *)aux;
data[0].iov_len = len;
data[1].iov_len = 0;
kth->ktr_len = len;
error = ktrwriteraw(p, vp, cred, kth, data);
crfree(cred);
return (error);
}
int
ktrwrite2(struct proc *p, struct ktr_header *kth, const void *aux1,
size_t len1, const void *aux2, size_t len2)
{
struct vnode *vp = p->p_p->ps_tracevp;
struct ucred *cred = p->p_p->ps_tracecred;
struct iovec data[2];
int error;
if (vp == NULL)
return 0;
crhold(cred);
data[0].iov_base = (void *)aux1;
data[0].iov_len = len1;
data[1].iov_base = (void *)aux2;
data[1].iov_len = len2;
kth->ktr_len = len1 + len2;
error = ktrwriteraw(p, vp, cred, kth, data);
crfree(cred);
return (error);
}
int
ktrwriteraw(struct proc *curp, struct vnode *vp, struct ucred *cred,
struct ktr_header *kth, struct iovec *data)
{
struct uio auio;
struct iovec aiov[3];
struct process *pr;
int error;
KERNEL_ASSERT_LOCKED();
auio.uio_iov = &aiov[0];
auio.uio_offset = 0;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE;
aiov[0].iov_base = (caddr_t)kth;
aiov[0].iov_len = sizeof(struct ktr_header);
auio.uio_resid = sizeof(struct ktr_header);
auio.uio_iovcnt = 1;
auio.uio_procp = curp;
if (kth->ktr_len > 0) {
aiov[1] = data[0];
aiov[2] = data[1];
auio.uio_iovcnt++;
if (aiov[2].iov_len > 0)
auio.uio_iovcnt++;
auio.uio_resid += kth->ktr_len;
}
error = vget(vp, LK_EXCLUSIVE | LK_RETRY);
if (error)
goto bad;
error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, cred);
vput(vp);
if (error)
goto bad;
return (0);
bad:
/*
* If error encountered, give up tracing on this vnode.
*/
log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n",
error);
LIST_FOREACH(pr, &allprocess, ps_list) {
if (pr == curp->p_p)
continue;
if (pr->ps_tracevp == vp && pr->ps_tracecred == cred)
ktrcleartrace(pr);
}
ktrcleartrace(curp->p_p);
return (error);
}
/*
* Return true if caller has permission to set the ktracing state
* of target. Essentially, the target can't possess any
* more permissions than the caller. KTRFAC_ROOT signifies that
* root previously set the tracing status on the target process, and
* so, only root may further change it.
*
* TODO: check groups. use caller effective gid.
*/
int
ktrcanset(struct proc *callp, struct process *targetpr)
{
struct ucred *caller = callp->p_ucred;
struct ucred *target = targetpr->ps_ucred;
if ((caller->cr_uid == target->cr_ruid &&
target->cr_ruid == target->cr_svuid &&
caller->cr_rgid == target->cr_rgid && /* XXX */
target->cr_rgid == target->cr_svgid &&
(targetpr->ps_traceflag & KTRFAC_ROOT) == 0 &&
!ISSET(targetpr->ps_flags, PS_SUGID)) ||
caller->cr_uid == 0)
return (1);
return (0);
}
12
9
12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
/* $OpenBSD: nfs_syscalls.c,v 1.118 2022/06/06 14:45:41 claudio Exp $ */
/* $NetBSD: nfs_syscalls.c,v 1.19 1996/02/18 11:53:52 fvdl Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)nfs_syscalls.c 8.5 (Berkeley) 3/30/95
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/namei.h>
#include <sys/syslog.h>
#include <sys/filedesc.h>
#include <sys/signalvar.h>
#include <sys/kthread.h>
#include <sys/queue.h>
#include <sys/syscallargs.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <nfs/xdr_subs.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsrvcache.h>
#include <nfs/nfsmount.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs_var.h>
/* Global defs. */
extern int nfs_numasync;
extern struct nfsstats nfsstats;
struct nfssvc_sock *nfs_udpsock;
int nfsd_waiting = 0;
#ifdef NFSSERVER
struct pool nfsrv_descript_pl;
int nfsrv_getslp(struct nfsd *nfsd);
static int nfs_numnfsd = 0;
int (*nfsrv3_procs[NFS_NPROCS])(struct nfsrv_descript *,
struct nfssvc_sock *, struct proc *, struct mbuf **) = {
nfsrv_null,
nfsrv_getattr,
nfsrv_setattr,
nfsrv_lookup,
nfsrv3_access,
nfsrv_readlink,
nfsrv_read,
nfsrv_write,
nfsrv_create,
nfsrv_mkdir,
nfsrv_symlink,
nfsrv_mknod,
nfsrv_remove,
nfsrv_rmdir,
nfsrv_rename,
nfsrv_link,
nfsrv_readdir,
nfsrv_readdirplus,
nfsrv_statfs,
nfsrv_fsinfo,
nfsrv_pathconf,
nfsrv_commit,
nfsrv_noop
};
#endif
TAILQ_HEAD(, nfssvc_sock) nfssvc_sockhead;
struct nfsdhead nfsd_head;
int nfssvc_sockhead_flag;
#define SLP_INIT 0x01 /* NFS data undergoing initialization */
#define SLP_WANTINIT 0x02 /* thread waiting on NFS initialization */
int nfsd_head_flag;
#ifdef NFSCLIENT
struct proc *nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
int nfs_niothreads = -1;
#endif
int nfssvc_addsock(struct file *, struct mbuf *);
int nfssvc_nfsd(struct nfsd *);
void nfsrv_slpderef(struct nfssvc_sock *);
void nfsrv_zapsock(struct nfssvc_sock *);
void nfssvc_iod(void *);
/*
* NFS server pseudo system call for the nfsd's
* Based on the flag value it either:
* - adds a socket to the selection list
* - remains in the kernel as an nfsd
*/
int
sys_nfssvc(struct proc *p, void *v, register_t *retval)
{
int error = 0;
#ifdef NFSSERVER
struct sys_nfssvc_args /* {
syscallarg(int) flag;
syscallarg(caddr_t) argp;
} */ *uap = v;
int flags = SCARG(uap, flag);
struct file *fp;
struct mbuf *nam;
struct nfsd_args nfsdarg;
struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
struct nfsd *nfsd;
#endif
/* Must be super user */
error = suser(p);
if (error)
return (error);
#ifndef NFSSERVER
error = ENOSYS;
#else
while (nfssvc_sockhead_flag & SLP_INIT) {
nfssvc_sockhead_flag |= SLP_WANTINIT;
tsleep_nsec(&nfssvc_sockhead, PSOCK, "nfsd init", INFSLP);
}
switch (flags) {
case NFSSVC_ADDSOCK:
error = copyin(SCARG(uap, argp), &nfsdarg, sizeof(nfsdarg));
if (error)
return (error);
error = getsock(p, nfsdarg.sock, &fp);
if (error)
return (error);
/*
* Get the client address for connected sockets.
*/
if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
nam = NULL;
else {
error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
MT_SONAME);
if (error) {
FRELE(fp, p);
return (error);
}
}
error = nfssvc_addsock(fp, nam);
FRELE(fp, p);
break;
case NFSSVC_NFSD:
error = copyin(SCARG(uap, argp), nsd, sizeof(*nsd));
if (error)
return (error);
nfsd = malloc(sizeof(*nfsd), M_NFSD, M_WAITOK|M_ZERO);
nfsd->nfsd_procp = p;
nfsd->nfsd_slp = NULL;
error = nfssvc_nfsd(nfsd);
break;
default:
error = EINVAL;
break;
}
if (error == EINTR || error == ERESTART)
error = 0;
#endif /* !NFSSERVER */
return (error);
}
#ifdef NFSSERVER
/*
* Adds a socket to the list for servicing by nfsds.
*/
int
nfssvc_addsock(struct file *fp, struct mbuf *mynam)
{
struct mbuf *m;
int siz;
struct nfssvc_sock *slp;
struct socket *so;
struct nfssvc_sock *tslp;
int error;
so = (struct socket *)fp->f_data;
tslp = NULL;
/*
* Add it to the list, as required.
*/
if (so->so_proto->pr_protocol == IPPROTO_UDP) {
tslp = nfs_udpsock;
if (tslp->ns_flag & SLP_VALID) {
m_freem(mynam);
return (EPERM);
}
}
if (so->so_type == SOCK_STREAM)
siz = NFS_MAXPACKET + sizeof (u_long);
else
siz = NFS_MAXPACKET;
solock(so);
error = soreserve(so, siz, siz);
if (error) {
sounlock(so);
m_freem(mynam);
return (error);
}
/*
* Set protocol specific options { for now TCP only } and
* reserve some space. For datagram sockets, this can get called
* repeatedly for the same socket, but that isn't harmful.
*/
if (so->so_type == SOCK_STREAM) {
MGET(m, M_WAIT, MT_SOOPTS);
*mtod(m, int32_t *) = 1;
m->m_len = sizeof(int32_t);
sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
m_freem(m);
}
if (so->so_proto->pr_domain->dom_family == AF_INET &&
so->so_proto->pr_protocol == IPPROTO_TCP) {
MGET(m, M_WAIT, MT_SOOPTS);
*mtod(m, int32_t *) = 1;
m->m_len = sizeof(int32_t);
sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
m_freem(m);
}
so->so_rcv.sb_flags &= ~SB_NOINTR;
so->so_rcv.sb_timeo_nsecs = INFSLP;
so->so_snd.sb_flags &= ~SB_NOINTR;
so->so_snd.sb_timeo_nsecs = INFSLP;
sounlock(so);
if (tslp)
slp = tslp;
else {
slp = malloc(sizeof(*slp), M_NFSSVC, M_WAITOK|M_ZERO);
TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain);
}
slp->ns_so = so;
slp->ns_nam = mynam;
FREF(fp);
slp->ns_fp = fp;
so->so_upcallarg = (caddr_t)slp;
so->so_upcall = nfsrv_rcv;
slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
nfsrv_wakenfsd(slp);
return (0);
}
/*
* Called by nfssvc() for nfsds. Just loops around servicing rpc requests
* until it is killed by a signal.
*/
int
nfssvc_nfsd(struct nfsd *nfsd)
{
struct mbuf *m;
int siz;
struct nfssvc_sock *slp;
struct socket *so;
int *solockp;
struct nfsrv_descript *nd = NULL;
struct mbuf *mreq;
int error = 0, cacherep, sotype;
cacherep = RC_DOIT;
TAILQ_INSERT_TAIL(&nfsd_head, nfsd, nfsd_chain);
nfs_numnfsd++;
/* Loop getting rpc requests until SIGKILL. */
loop:
if (!ISSET(nfsd->nfsd_flag, NFSD_REQINPROG)) {
/* attach an nfssvc_sock to nfsd */
error = nfsrv_getslp(nfsd);
if (error)
goto done;
slp = nfsd->nfsd_slp;
if (ISSET(slp->ns_flag, SLP_VALID)) {
if (ISSET(slp->ns_flag, SLP_DISCONN)) {
nfsrv_zapsock(slp);
} else if (ISSET(slp->ns_flag, SLP_NEEDQ)) {
CLR(slp->ns_flag, SLP_NEEDQ);
nfs_sndlock(&slp->ns_solock, NULL);
nfsrv_rcv(slp->ns_so, (caddr_t)slp, M_WAIT);
nfs_sndunlock(&slp->ns_solock);
}
error = nfsrv_dorec(slp, nfsd, &nd);
SET(nfsd->nfsd_flag, NFSD_REQINPROG);
}
} else {
error = 0;
slp = nfsd->nfsd_slp;
}
if (error || !ISSET(slp->ns_flag, SLP_VALID)) {
if (nd != NULL) {
pool_put(&nfsrv_descript_pl, nd);
nd = NULL;
}
nfsd->nfsd_slp = NULL;
CLR(nfsd->nfsd_flag, NFSD_REQINPROG);
nfsrv_slpderef(slp);
goto loop;
}
so = slp->ns_so;
sotype = so->so_type;
if (ISSET(so->so_proto->pr_flags, PR_CONNREQUIRED))
solockp = &slp->ns_solock;
else
solockp = NULL;
if (nd) {
if (nd->nd_nam2)
nd->nd_nam = nd->nd_nam2;
else
nd->nd_nam = slp->ns_nam;
}
cacherep = nfsrv_getcache(nd, slp, &mreq);
switch (cacherep) {
case RC_DOIT:
error = (*(nfsrv3_procs[nd->nd_procnum]))(nd, slp, nfsd->nfsd_procp, &mreq);
if (mreq == NULL) {
if (nd != NULL) {
m_freem(nd->nd_nam2);
m_freem(nd->nd_mrep);
}
break;
}
if (error) {
nfsstats.srv_errs++;
nfsrv_updatecache(nd, 0, mreq);
m_freem(nd->nd_nam2);
break;
}
nfsstats.srvrpccnt[nd->nd_procnum]++;
nfsrv_updatecache(nd, 1, mreq);
nd->nd_mrep = NULL;
/* FALLTHROUGH */
case RC_REPLY:
m = mreq;
siz = 0;
while (m) {
siz += m->m_len;
m = m->m_next;
}
if (siz <= 0 || siz > NFS_MAXPACKET)
panic("bad nfs svc reply, siz = %i", siz);
m = mreq;
m->m_pkthdr.len = siz;
m->m_pkthdr.ph_ifidx = 0;
/* For stream protocols, prepend a Sun RPC Record Mark. */
if (sotype == SOCK_STREAM) {
M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
*mtod(m, u_int32_t *) = htonl(0x80000000 | siz);
}
if (solockp)
nfs_sndlock(solockp, NULL);
if (ISSET(slp->ns_flag, SLP_VALID))
error = nfs_send(so, nd->nd_nam2, m, NULL);
else {
error = EPIPE;
m_freem(m);
}
m_freem(nd->nd_nam2);
m_freem(nd->nd_mrep);
if (error == EPIPE)
nfsrv_zapsock(slp);
if (solockp)
nfs_sndunlock(solockp);
if (error == EINTR || error == ERESTART) {
pool_put(&nfsrv_descript_pl, nd);
nfsrv_slpderef(slp);
goto done;
}
break;
case RC_DROPIT:
m_freem(nd->nd_mrep);
m_freem(nd->nd_nam2);
break;
};
if (nd) {
pool_put(&nfsrv_descript_pl, nd);
nd = NULL;
}
if (nfsrv_dorec(slp, nfsd, &nd)) {
nfsd->nfsd_flag &= ~NFSD_REQINPROG;
nfsd->nfsd_slp = NULL;
nfsrv_slpderef(slp);
}
goto loop;
done:
TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain);
free(nfsd, M_NFSD, sizeof(*nfsd));
if (--nfs_numnfsd == 0)
nfsrv_init(1); /* Reinitialize everything */
return (error);
}
/*
* Shut down a socket associated with an nfssvc_sock structure.
* Should be called with the send lock set, if required.
* The trick here is to increment the sref at the start, so that the nfsds
* will stop using it and clear ns_flag at the end so that it will not be
* reassigned during cleanup.
*/
void
nfsrv_zapsock(struct nfssvc_sock *slp)
{
struct socket *so;
struct file *fp;
struct mbuf *m, *n;
slp->ns_flag &= ~SLP_ALLFLAGS;
fp = slp->ns_fp;
if (fp) {
FREF(fp);
slp->ns_fp = NULL;
so = slp->ns_so;
so->so_upcall = NULL;
soshutdown(so, SHUT_RDWR);
closef(fp, NULL);
if (slp->ns_nam)
m = m_free(slp->ns_nam);
m_freem(slp->ns_raw);
m = slp->ns_rec;
while (m) {
n = m->m_nextpkt;
m_freem(m);
m = n;
}
}
}
/*
* Dereference a server socket structure. If it has no more references and
* is no longer valid, you can throw it away.
*/
void
nfsrv_slpderef(struct nfssvc_sock *slp)
{
if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
free(slp, M_NFSSVC, sizeof(*slp));
}
}
/*
* Initialize the data structures for the server.
* Handshake with any new nfsds starting up to avoid any chance of
* corruption.
*/
void
nfsrv_init(int terminating)
{
struct nfssvc_sock *slp, *nslp;
if (nfssvc_sockhead_flag & SLP_INIT)
panic("nfsd init");
nfssvc_sockhead_flag |= SLP_INIT;
if (terminating) {
for (slp = TAILQ_FIRST(&nfssvc_sockhead); slp != NULL;
slp = nslp) {
nslp = TAILQ_NEXT(slp, ns_chain);
if (slp->ns_flag & SLP_VALID)
nfsrv_zapsock(slp);
TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
free(slp, M_NFSSVC, sizeof(*slp));
}
nfsrv_cleancache(); /* And clear out server cache */
}
TAILQ_INIT(&nfssvc_sockhead);
nfssvc_sockhead_flag &= ~SLP_INIT;
if (nfssvc_sockhead_flag & SLP_WANTINIT) {
nfssvc_sockhead_flag &= ~SLP_WANTINIT;
wakeup((caddr_t)&nfssvc_sockhead);
}
TAILQ_INIT(&nfsd_head);
nfsd_head_flag &= ~NFSD_CHECKSLP;
nfs_udpsock = malloc(sizeof(*nfs_udpsock), M_NFSSVC,
M_WAITOK|M_ZERO);
TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain);
if (!terminating) {
pool_init(&nfsrv_descript_pl, sizeof(struct nfsrv_descript),
0, IPL_NONE, PR_WAITOK, "ndscpl", NULL);
}
}
#endif /* NFSSERVER */
#ifdef NFSCLIENT
/*
* Asynchronous I/O threads for client nfs.
* They do read-ahead and write-behind operations on the block I/O cache.
* Never returns unless it fails or gets killed.
*/
void
nfssvc_iod(void *arg)
{
struct proc *p = curproc;
struct buf *bp, *nbp;
int i, myiod;
struct vnode *vp;
int error = 0, s, bufcount;
bufcount = MIN(256, bcstats.kvaslots / 8);
bufcount = MIN(bufcount, bcstats.numbufs / 8);
/* Assign my position or return error if too many already running. */
myiod = -1;
for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
if (nfs_asyncdaemon[i] == NULL) {
myiod = i;
break;
}
}
if (myiod == -1)
kthread_exit(EBUSY);
nfs_asyncdaemon[myiod] = p;
nfs_numasync++;
/* Upper limit on how many bufs we'll queue up for this iod. */
if (nfs_bufqmax > bcstats.kvaslots / 4) {
nfs_bufqmax = bcstats.kvaslots / 4;
bufcount = 0;
}
if (nfs_bufqmax > bcstats.numbufs / 4) {
nfs_bufqmax = bcstats.numbufs / 4;
bufcount = 0;
}
nfs_bufqmax += bufcount;
wakeup(&nfs_bufqlen); /* wake up anyone waiting for room to enqueue IO */
/* Just loop around doin our stuff until SIGKILL. */
for (;;) {
while (TAILQ_FIRST(&nfs_bufq) == NULL && error == 0) {
error = tsleep_nsec(&nfs_bufq,
PWAIT | PCATCH, "nfsidl", INFSLP);
}
while ((bp = TAILQ_FIRST(&nfs_bufq)) != NULL) {
/* Take one off the front of the list */
TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
nfs_bufqlen--;
wakeup_one(&nfs_bufqlen);
if (bp->b_flags & B_READ)
(void) nfs_doio(bp, NULL);
else do {
/*
* Look for a delayed write for the same vnode, so I can do
* it now. We must grab it before calling nfs_doio() to
* avoid any risk of the vnode getting vclean()'d while
* we are doing the write rpc.
*/
vp = bp->b_vp;
s = splbio();
LIST_FOREACH(nbp, &vp->v_dirtyblkhd, b_vnbufs) {
if ((nbp->b_flags &
(B_BUSY|B_DELWRI|B_NEEDCOMMIT|B_NOCACHE))!=B_DELWRI)
continue;
nbp->b_flags |= B_ASYNC;
bremfree(nbp);
buf_acquire(nbp);
break;
}
/*
* For the delayed write, do the first part of nfs_bwrite()
* up to, but not including nfs_strategy().
*/
if (nbp) {
nbp->b_flags &= ~(B_READ|B_DONE|B_ERROR);
buf_undirty(nbp);
nbp->b_vp->v_numoutput++;
}
splx(s);
(void) nfs_doio(bp, NULL);
} while ((bp = nbp) != NULL);
}
if (error) {
nfs_asyncdaemon[myiod] = NULL;
nfs_numasync--;
nfs_bufqmax -= bufcount;
kthread_exit(error);
}
}
}
void
nfs_getset_niothreads(int set)
{
int i, have, start;
for (have = 0, i = 0; i < NFS_MAXASYNCDAEMON; i++)
if (nfs_asyncdaemon[i] != NULL)
have++;
if (set) {
/* clamp to sane range */
nfs_niothreads = max(0, min(nfs_niothreads, NFS_MAXASYNCDAEMON));
start = nfs_niothreads - have;
while (start > 0) {
kthread_create(nfssvc_iod, NULL, NULL, "nfsio");
start--;
}
for (i = 0; (start < 0) && (i < NFS_MAXASYNCDAEMON); i++)
if (nfs_asyncdaemon[i] != NULL) {
psignal(nfs_asyncdaemon[i], SIGKILL);
start++;
}
} else {
if (nfs_niothreads >= 0)
nfs_niothreads = have;
}
}
#endif /* NFSCLIENT */
#ifdef NFSSERVER
/*
* Find an nfssrv_sock for nfsd, sleeping if needed.
*/
int
nfsrv_getslp(struct nfsd *nfsd)
{
struct nfssvc_sock *slp;
int error;
again:
while (nfsd->nfsd_slp == NULL &&
(nfsd_head_flag & NFSD_CHECKSLP) == 0) {
nfsd->nfsd_flag |= NFSD_WAITING;
nfsd_waiting++;
error = tsleep_nsec(nfsd, PSOCK | PCATCH, "nfsd", INFSLP);
nfsd_waiting--;
if (error)
return (error);
}
if (nfsd->nfsd_slp == NULL &&
(nfsd_head_flag & NFSD_CHECKSLP) != 0) {
TAILQ_FOREACH(slp, &nfssvc_sockhead, ns_chain) {
if ((slp->ns_flag & (SLP_VALID | SLP_DOREC)) ==
(SLP_VALID | SLP_DOREC)) {
slp->ns_flag &= ~SLP_DOREC;
slp->ns_sref++;
nfsd->nfsd_slp = slp;
break;
}
}
if (slp == NULL)
nfsd_head_flag &= ~NFSD_CHECKSLP;
}
if (nfsd->nfsd_slp == NULL)
goto again;
return (0);
}
#endif /* NFSSERVER */
10
7
1148
1807
1804
1810
1807
1803
1808
1788
4
1792
1793
663
665
58
621
662
10
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
/* $OpenBSD: sched_bsd.c,v 1.72 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */
/*-
* Copyright (c) 1982, 1986, 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_synch.c 8.6 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/resourcevar.h>
#include <uvm/uvm_extern.h>
#include <sys/sched.h>
#include <sys/timeout.h>
#include <sys/smr.h>
#include <sys/tracepoint.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
int lbolt; /* once a second sleep address */
int rrticks_init; /* # of hardclock ticks per roundrobin() */
#ifdef MULTIPROCESSOR
struct __mp_lock sched_lock;
#endif
void schedcpu(void *);
uint32_t decay_aftersleep(uint32_t, uint32_t);
/*
* Force switch among equal priority processes every 100ms.
*/
void
roundrobin(struct cpu_info *ci)
{
struct schedstate_percpu *spc = &ci->ci_schedstate;
spc->spc_rrticks = rrticks_init;
if (ci->ci_curproc != NULL) {
if (spc->spc_schedflags & SPCF_SEENRR) {
/*
* The process has already been through a roundrobin
* without switching and may be hogging the CPU.
* Indicate that the process should yield.
*/
atomic_setbits_int(&spc->spc_schedflags,
SPCF_SHOULDYIELD);
} else {
atomic_setbits_int(&spc->spc_schedflags,
SPCF_SEENRR);
}
}
if (spc->spc_nrun)
need_resched(ci);
}
/*
* Constants for digital decay and forget:
* 90% of (p_estcpu) usage in 5 * loadav time
* 95% of (p_pctcpu) usage in 60 seconds (load insensitive)
* Note that, as ps(1) mentions, this can let percentages
* total over 100% (I've seen 137.9% for 3 processes).
*
* Note that hardclock updates p_estcpu and p_cpticks independently.
*
* We wish to decay away 90% of p_estcpu in (5 * loadavg) seconds.
* That is, the system wants to compute a value of decay such
* that the following for loop:
* for (i = 0; i < (5 * loadavg); i++)
* p_estcpu *= decay;
* will compute
* p_estcpu *= 0.1;
* for all values of loadavg:
*
* Mathematically this loop can be expressed by saying:
* decay ** (5 * loadavg) ~= .1
*
* The system computes decay as:
* decay = (2 * loadavg) / (2 * loadavg + 1)
*
* We wish to prove that the system's computation of decay
* will always fulfill the equation:
* decay ** (5 * loadavg) ~= .1
*
* If we compute b as:
* b = 2 * loadavg
* then
* decay = b / (b + 1)
*
* We now need to prove two things:
* 1) Given factor ** (5 * loadavg) ~= .1, prove factor == b/(b+1)
* 2) Given b/(b+1) ** power ~= .1, prove power == (5 * loadavg)
*
* Facts:
* For x close to zero, exp(x) =~ 1 + x, since
* exp(x) = 0! + x**1/1! + x**2/2! + ... .
* therefore exp(-1/b) =~ 1 - (1/b) = (b-1)/b.
* For x close to zero, ln(1+x) =~ x, since
* ln(1+x) = x - x**2/2 + x**3/3 - ... -1 < x < 1
* therefore ln(b/(b+1)) = ln(1 - 1/(b+1)) =~ -1/(b+1).
* ln(.1) =~ -2.30
*
* Proof of (1):
* Solve (factor)**(power) =~ .1 given power (5*loadav):
* solving for factor,
* ln(factor) =~ (-2.30/5*loadav), or
* factor =~ exp(-1/((5/2.30)*loadav)) =~ exp(-1/(2*loadav)) =
* exp(-1/b) =~ (b-1)/b =~ b/(b+1). QED
*
* Proof of (2):
* Solve (factor)**(power) =~ .1 given factor == (b/(b+1)):
* solving for power,
* power*ln(b/(b+1)) =~ -2.30, or
* power =~ 2.3 * (b + 1) = 4.6*loadav + 2.3 =~ 5*loadav. QED
*
* Actual power values for the implemented algorithm are as follows:
* loadav: 1 2 3 4
* power: 5.68 10.32 14.94 19.55
*/
/* calculations for digital decay to forget 90% of usage in 5*loadav sec */
#define loadfactor(loadav) (2 * (loadav))
#define decay_cpu(loadfac, cpu) (((loadfac) * (cpu)) / ((loadfac) + FSCALE))
/* decay 95% of `p_pctcpu' in 60 seconds; see CCPU_SHIFT before changing */
fixpt_t ccpu = 0.95122942450071400909 * FSCALE; /* exp(-1/20) */
/*
* If `ccpu' is not equal to `exp(-1/20)' and you still want to use the
* faster/more-accurate formula, you'll have to estimate CCPU_SHIFT below
* and possibly adjust FSHIFT in "param.h" so that (FSHIFT >= CCPU_SHIFT).
*
* To estimate CCPU_SHIFT for exp(-1/20), the following formula was used:
* 1 - exp(-1/20) ~= 0.0487 ~= 0.0488 == 1 (fixed pt, *11* bits).
*
* If you don't want to bother with the faster/more-accurate formula, you
* can set CCPU_SHIFT to (FSHIFT + 1) which will use a slower/less-accurate
* (more general) method of calculating the %age of CPU used by a process.
*/
#define CCPU_SHIFT 11
/*
* Recompute process priorities, every second.
*/
void
schedcpu(void *arg)
{
struct timeout *to = (struct timeout *)arg;
fixpt_t loadfac = loadfactor(averunnable.ldavg[0]);
struct proc *p;
int s;
unsigned int newcpu;
int phz;
/*
* If we have a statistics clock, use that to calculate CPU
* time, otherwise revert to using the profiling clock (which,
* in turn, defaults to hz if there is no separate profiling
* clock available)
*/
phz = stathz ? stathz : profhz;
KASSERT(phz);
LIST_FOREACH(p, &allproc, p_list) {
/*
* Idle threads are never placed on the runqueue,
* therefore computing their priority is pointless.
*/
if (p->p_cpu != NULL &&
p->p_cpu->ci_schedstate.spc_idleproc == p)
continue;
/*
* Increment sleep time (if sleeping). We ignore overflow.
*/
if (p->p_stat == SSLEEP || p->p_stat == SSTOP)
p->p_slptime++;
p->p_pctcpu = (p->p_pctcpu * ccpu) >> FSHIFT;
/*
* If the process has slept the entire second,
* stop recalculating its priority until it wakes up.
*/
if (p->p_slptime > 1)
continue;
SCHED_LOCK(s);
/*
* p_pctcpu is only for diagnostic tools such as ps.
*/
#if (FSHIFT >= CCPU_SHIFT)
p->p_pctcpu += (phz == 100)?
((fixpt_t) p->p_cpticks) << (FSHIFT - CCPU_SHIFT):
100 * (((fixpt_t) p->p_cpticks)
<< (FSHIFT - CCPU_SHIFT)) / phz;
#else
p->p_pctcpu += ((FSCALE - ccpu) *
(p->p_cpticks * FSCALE / phz)) >> FSHIFT;
#endif
p->p_cpticks = 0;
newcpu = (u_int) decay_cpu(loadfac, p->p_estcpu);
setpriority(p, newcpu, p->p_p->ps_nice);
if (p->p_stat == SRUN &&
(p->p_runpri / SCHED_PPQ) != (p->p_usrpri / SCHED_PPQ)) {
remrunqueue(p);
setrunqueue(p->p_cpu, p, p->p_usrpri);
}
SCHED_UNLOCK(s);
}
uvm_meter();
wakeup(&lbolt);
timeout_add_sec(to, 1);
}
/*
* Recalculate the priority of a process after it has slept for a while.
* For all load averages >= 1 and max p_estcpu of 255, sleeping for at
* least six times the loadfactor will decay p_estcpu to zero.
*/
uint32_t
decay_aftersleep(uint32_t estcpu, uint32_t slptime)
{
fixpt_t loadfac = loadfactor(averunnable.ldavg[0]);
uint32_t newcpu;
if (slptime > 5 * loadfac)
newcpu = 0;
else {
newcpu = estcpu;
slptime--; /* the first time was done in schedcpu */
while (newcpu && --slptime)
newcpu = decay_cpu(loadfac, newcpu);
}
return (newcpu);
}
/*
* General yield call. Puts the current process back on its run queue and
* performs a voluntary context switch.
*/
void
yield(void)
{
struct proc *p = curproc;
int s;
SCHED_LOCK(s);
setrunqueue(p->p_cpu, p, p->p_usrpri);
p->p_ru.ru_nvcsw++;
mi_switch();
SCHED_UNLOCK(s);
}
/*
* General preemption call. Puts the current process back on its run queue
* and performs an involuntary context switch. If a process is supplied,
* we switch to that process. Otherwise, we use the normal process selection
* criteria.
*/
void
preempt(void)
{
struct proc *p = curproc;
int s;
SCHED_LOCK(s);
setrunqueue(p->p_cpu, p, p->p_usrpri);
p->p_ru.ru_nivcsw++;
mi_switch();
SCHED_UNLOCK(s);
}
void
mi_switch(void)
{
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
struct proc *p = curproc;
struct proc *nextproc;
struct process *pr = p->p_p;
struct timespec ts;
#ifdef MULTIPROCESSOR
int hold_count;
int sched_count;
#endif
assertwaitok();
KASSERT(p->p_stat != SONPROC);
SCHED_ASSERT_LOCKED();
#ifdef MULTIPROCESSOR
/*
* Release the kernel_lock, as we are about to yield the CPU.
*/
sched_count = __mp_release_all_but_one(&sched_lock);
if (_kernel_lock_held())
hold_count = __mp_release_all(&kernel_lock);
else
hold_count = 0;
#endif
/*
* Compute the amount of time during which the current
* process was running, and add that to its total so far.
*/
nanouptime(&ts);
if (timespeccmp(&ts, &spc->spc_runtime, <)) {
#if 0
printf("uptime is not monotonic! "
"ts=%lld.%09lu, runtime=%lld.%09lu\n",
(long long)tv.tv_sec, tv.tv_nsec,
(long long)spc->spc_runtime.tv_sec,
spc->spc_runtime.tv_nsec);
#endif
} else {
timespecsub(&ts, &spc->spc_runtime, &ts);
timespecadd(&p->p_rtime, &ts, &p->p_rtime);
}
/* add the time counts for this thread to the process's total */
tuagg_unlocked(pr, p);
/*
* Process is about to yield the CPU; clear the appropriate
* scheduling flags.
*/
atomic_clearbits_int(&spc->spc_schedflags, SPCF_SWITCHCLEAR);
nextproc = sched_chooseproc();
if (p != nextproc) {
uvmexp.swtch++;
TRACEPOINT(sched, off__cpu, nextproc->p_tid + THREAD_PID_OFFSET,
nextproc->p_p->ps_pid);
cpu_switchto(p, nextproc);
TRACEPOINT(sched, on__cpu, NULL);
} else {
TRACEPOINT(sched, remain__cpu, NULL);
p->p_stat = SONPROC;
}
clear_resched(curcpu());
SCHED_ASSERT_LOCKED();
/*
* To preserve lock ordering, we need to release the sched lock
* and grab it after we grab the big lock.
* In the future, when the sched lock isn't recursive, we'll
* just release it here.
*/
#ifdef MULTIPROCESSOR
__mp_unlock(&sched_lock);
#endif
SCHED_ASSERT_UNLOCKED();
smr_idle();
/*
* We're running again; record our new start time. We might
* be running on a new CPU now, so don't use the cache'd
* schedstate_percpu pointer.
*/
KASSERT(p->p_cpu == curcpu());
nanouptime(&p->p_cpu->ci_schedstate.spc_runtime);
#ifdef MULTIPROCESSOR
/*
* Reacquire the kernel_lock now. We do this after we've
* released the scheduler lock to avoid deadlock, and before
* we reacquire the interlock and the scheduler lock.
*/
if (hold_count)
__mp_acquire_count(&kernel_lock, hold_count);
__mp_acquire_count(&sched_lock, sched_count + 1);
#endif
}
/*
* Change process state to be runnable,
* placing it on the run queue.
*/
void
setrunnable(struct proc *p)
{
struct process *pr = p->p_p;
u_char prio;
SCHED_ASSERT_LOCKED();
switch (p->p_stat) {
case 0:
case SRUN:
case SONPROC:
case SDEAD:
case SIDL:
default:
panic("setrunnable");
case SSTOP:
/*
* If we're being traced (possibly because someone attached us
* while we were stopped), check for a signal from the debugger.
*/
if ((pr->ps_flags & PS_TRACED) != 0 && pr->ps_xsig != 0)
atomic_setbits_int(&p->p_siglist, sigmask(pr->ps_xsig));
prio = p->p_usrpri;
unsleep(p);
break;
case SSLEEP:
prio = p->p_slppri;
unsleep(p); /* e.g. when sending signals */
break;
}
setrunqueue(NULL, p, prio);
if (p->p_slptime > 1) {
uint32_t newcpu;
newcpu = decay_aftersleep(p->p_estcpu, p->p_slptime);
setpriority(p, newcpu, pr->ps_nice);
}
p->p_slptime = 0;
}
/*
* Compute the priority of a process.
*/
void
setpriority(struct proc *p, uint32_t newcpu, uint8_t nice)
{
unsigned int newprio;
newprio = min((PUSER + newcpu + NICE_WEIGHT * (nice - NZERO)), MAXPRI);
SCHED_ASSERT_LOCKED();
p->p_estcpu = newcpu;
p->p_usrpri = newprio;
}
/*
* We adjust the priority of the current process. The priority of a process
* gets worse as it accumulates CPU time. The cpu usage estimator (p_estcpu)
* is increased here. The formula for computing priorities (in kern_synch.c)
* will compute a different value each time p_estcpu increases. This can
* cause a switch, but unless the priority crosses a PPQ boundary the actual
* queue will not change. The cpu usage estimator ramps up quite quickly
* when the process is running (linearly), and decays away exponentially, at
* a rate which is proportionally slower when the system is busy. The basic
* principle is that the system will 90% forget that the process used a lot
* of CPU time in 5 * loadav seconds. This causes the system to favor
* processes which haven't run much recently, and to round-robin among other
* processes.
*/
void
schedclock(struct proc *p)
{
struct cpu_info *ci = curcpu();
struct schedstate_percpu *spc = &ci->ci_schedstate;
uint32_t newcpu;
int s;
if (p == spc->spc_idleproc || spc->spc_spinning)
return;
SCHED_LOCK(s);
newcpu = ESTCPULIM(p->p_estcpu + 1);
setpriority(p, newcpu, p->p_p->ps_nice);
SCHED_UNLOCK(s);
}
void (*cpu_setperf)(int);
#define PERFPOL_MANUAL 0
#define PERFPOL_AUTO 1
#define PERFPOL_HIGH 2
int perflevel = 100;
int perfpolicy = PERFPOL_AUTO;
#ifndef SMALL_KERNEL
/*
* The code below handles CPU throttling.
*/
#include <sys/sysctl.h>
void setperf_auto(void *);
struct timeout setperf_to = TIMEOUT_INITIALIZER(setperf_auto, NULL);
extern int hw_power;
void
setperf_auto(void *v)
{
static uint64_t *idleticks, *totalticks;
static int downbeats;
int i, j = 0;
int speedup = 0;
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
uint64_t idle, total, allidle = 0, alltotal = 0;
if (perfpolicy != PERFPOL_AUTO)
return;
if (cpu_setperf == NULL)
return;
if (hw_power) {
speedup = 1;
goto faster;
}
if (!idleticks)
if (!(idleticks = mallocarray(ncpusfound, sizeof(*idleticks),
M_DEVBUF, M_NOWAIT | M_ZERO)))
return;
if (!totalticks)
if (!(totalticks = mallocarray(ncpusfound, sizeof(*totalticks),
M_DEVBUF, M_NOWAIT | M_ZERO))) {
free(idleticks, M_DEVBUF,
sizeof(*idleticks) * ncpusfound);
return;
}
CPU_INFO_FOREACH(cii, ci) {
if (!cpu_is_online(ci))
continue;
total = 0;
for (i = 0; i < CPUSTATES; i++) {
total += ci->ci_schedstate.spc_cp_time[i];
}
total -= totalticks[j];
idle = ci->ci_schedstate.spc_cp_time[CP_IDLE] - idleticks[j];
if (idle < total / 3)
speedup = 1;
alltotal += total;
allidle += idle;
idleticks[j] += idle;
totalticks[j] += total;
j++;
}
if (allidle < alltotal / 2)
speedup = 1;
if (speedup && downbeats < 5)
downbeats++;
if (speedup && perflevel != 100) {
faster:
perflevel = 100;
cpu_setperf(perflevel);
} else if (!speedup && perflevel != 0 && --downbeats <= 0) {
perflevel = 0;
cpu_setperf(perflevel);
}
timeout_add_msec(&setperf_to, 100);
}
int
sysctl_hwsetperf(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
int err;
if (!cpu_setperf)
return EOPNOTSUPP;
if (perfpolicy != PERFPOL_MANUAL)
return sysctl_rdint(oldp, oldlenp, newp, perflevel);
err = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&perflevel, 0, 100);
if (err)
return err;
if (newp != NULL)
cpu_setperf(perflevel);
return 0;
}
int
sysctl_hwperfpolicy(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
char policy[32];
int err;
if (!cpu_setperf)
return EOPNOTSUPP;
switch (perfpolicy) {
case PERFPOL_MANUAL:
strlcpy(policy, "manual", sizeof(policy));
break;
case PERFPOL_AUTO:
strlcpy(policy, "auto", sizeof(policy));
break;
case PERFPOL_HIGH:
strlcpy(policy, "high", sizeof(policy));
break;
default:
strlcpy(policy, "unknown", sizeof(policy));
break;
}
if (newp == NULL)
return sysctl_rdstring(oldp, oldlenp, newp, policy);
err = sysctl_string(oldp, oldlenp, newp, newlen, policy, sizeof(policy));
if (err)
return err;
if (strcmp(policy, "manual") == 0)
perfpolicy = PERFPOL_MANUAL;
else if (strcmp(policy, "auto") == 0)
perfpolicy = PERFPOL_AUTO;
else if (strcmp(policy, "high") == 0)
perfpolicy = PERFPOL_HIGH;
else
return EINVAL;
if (perfpolicy == PERFPOL_AUTO) {
timeout_add_msec(&setperf_to, 200);
} else if (perfpolicy == PERFPOL_HIGH) {
perflevel = 100;
cpu_setperf(perflevel);
}
return 0;
}
#endif
void
scheduler_start(void)
{
static struct timeout schedcpu_to;
/*
* We avoid polluting the global namespace by keeping the scheduler
* timeouts static in this function.
* We setup the timeout here and kick schedcpu once to make it do
* its job.
*/
timeout_set(&schedcpu_to, schedcpu, &schedcpu_to);
rrticks_init = hz / 10;
schedcpu(&schedcpu_to);
#ifndef SMALL_KERNEL
if (perfpolicy == PERFPOL_AUTO)
timeout_add_msec(&setperf_to, 200);
#endif
}
26
19
22
23
2
24
24
23
3
3
7
7
4
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
/* $OpenBSD: pci_machdep.c,v 1.77 2021/03/11 11:16:55 jsg Exp $ */
/* $NetBSD: pci_machdep.c,v 1.3 2003/05/07 21:33:58 fvdl Exp $ */
/*-
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
* NASA Ames Research Center.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1996 Christopher G. Demetriou. All rights reserved.
* Copyright (c) 1994 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Machine-specific functions for PCI autoconfiguration.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/extent.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <machine/pio.h>
#include <machine/intr.h>
#include <machine/biosvar.h>
#include <dev/isa/isareg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/ppbreg.h>
#include "ioapic.h"
#if NIOAPIC > 0
#include <machine/i82093var.h>
#include <machine/mpbiosvar.h>
#endif
#include "acpi.h"
#include "acpidmar.h"
#if NACPIDMAR > 0
#include <dev/acpi/acpidmar.h>
#endif
/*
* Memory Mapped Configuration space access.
*
* Since mapping the whole configuration space will cost us up to
* 256MB of kernel virtual memory, we use separate mappings per bus.
* The mappings are created on-demand, such that we only use kernel
* virtual memory for busses that are actually present.
*/
bus_addr_t pci_mcfg_addr;
int pci_mcfg_min_bus, pci_mcfg_max_bus;
bus_space_tag_t pci_mcfgt;
bus_space_handle_t pci_mcfgh[256];
struct mutex pci_conf_lock = MUTEX_INITIALIZER(IPL_HIGH);
#define PCI_CONF_LOCK() \
do { \
mtx_enter(&pci_conf_lock); \
} while (0)
#define PCI_CONF_UNLOCK() \
do { \
mtx_leave(&pci_conf_lock); \
} while (0)
#define PCI_MODE1_ENABLE 0x80000000UL
#define PCI_MODE1_ADDRESS_REG 0x0cf8
#define PCI_MODE1_DATA_REG 0x0cfc
/*
* PCI doesn't have any special needs; just use the generic versions
* of these functions.
*/
struct bus_dma_tag pci_bus_dma_tag = {
NULL, /* _may_bounce */
_bus_dmamap_create,
_bus_dmamap_destroy,
_bus_dmamap_load,
_bus_dmamap_load_mbuf,
_bus_dmamap_load_uio,
_bus_dmamap_load_raw,
_bus_dmamap_unload,
_bus_dmamap_sync,
_bus_dmamem_alloc,
_bus_dmamem_alloc_range,
_bus_dmamem_free,
_bus_dmamem_map,
_bus_dmamem_unmap,
_bus_dmamem_mmap,
};
void
pci_mcfg_init(bus_space_tag_t iot, bus_addr_t addr, int segment,
int min_bus, int max_bus)
{
if (segment == 0) {
pci_mcfgt = iot;
pci_mcfg_addr = addr;
pci_mcfg_min_bus = min_bus;
pci_mcfg_max_bus = max_bus;
}
}
pci_chipset_tag_t
pci_lookup_segment(int segment)
{
KASSERT(segment == 0);
return NULL;
}
void
pci_attach_hook(struct device *parent, struct device *self,
struct pcibus_attach_args *pba)
{
}
int
pci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
{
return (32);
}
pcitag_t
pci_make_tag(pci_chipset_tag_t pc, int bus, int device, int function)
{
if (bus >= 256 || device >= 32 || function >= 8)
panic("pci_make_tag: bad request");
return (PCI_MODE1_ENABLE |
(bus << 16) | (device << 11) | (function << 8));
}
void
pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *bp, int *dp, int *fp)
{
if (bp != NULL)
*bp = (tag >> 16) & 0xff;
if (dp != NULL)
*dp = (tag >> 11) & 0x1f;
if (fp != NULL)
*fp = (tag >> 8) & 0x7;
}
int
pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
{
int bus;
if (pci_mcfg_addr) {
pci_decompose_tag(pc, tag, &bus, NULL, NULL);
if (bus >= pci_mcfg_min_bus && bus <= pci_mcfg_max_bus)
return PCIE_CONFIG_SPACE_SIZE;
}
return PCI_CONFIG_SPACE_SIZE;
}
void
pci_mcfg_map_bus(int bus)
{
if (pci_mcfgh[bus])
return;
if (bus_space_map(pci_mcfgt, pci_mcfg_addr + (bus << 20), 1 << 20,
0, &pci_mcfgh[bus]))
panic("pci_conf_read: cannot map mcfg space");
}
pcireg_t
pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
pcireg_t data;
int bus;
KASSERT((reg & 0x3) == 0);
if (pci_mcfg_addr && reg >= PCI_CONFIG_SPACE_SIZE) {
pci_decompose_tag(pc, tag, &bus, NULL, NULL);
if (bus >= pci_mcfg_min_bus && bus <= pci_mcfg_max_bus) {
pci_mcfg_map_bus(bus);
data = bus_space_read_4(pci_mcfgt, pci_mcfgh[bus],
(tag & 0x000ff00) << 4 | reg);
return data;
}
}
PCI_CONF_LOCK();
outl(PCI_MODE1_ADDRESS_REG, tag | reg);
data = inl(PCI_MODE1_DATA_REG);
outl(PCI_MODE1_ADDRESS_REG, 0);
PCI_CONF_UNLOCK();
return data;
}
void
pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
{
int bus;
KASSERT((reg & 0x3) == 0);
if (pci_mcfg_addr && reg >= PCI_CONFIG_SPACE_SIZE) {
pci_decompose_tag(pc, tag, &bus, NULL, NULL);
if (bus >= pci_mcfg_min_bus && bus <= pci_mcfg_max_bus) {
pci_mcfg_map_bus(bus);
bus_space_write_4(pci_mcfgt, pci_mcfgh[bus],
(tag & 0x000ff00) << 4 | reg, data);
return;
}
}
PCI_CONF_LOCK();
outl(PCI_MODE1_ADDRESS_REG, tag | reg);
outl(PCI_MODE1_DATA_REG, data);
outl(PCI_MODE1_ADDRESS_REG, 0);
PCI_CONF_UNLOCK();
}
int
pci_msix_table_map(pci_chipset_tag_t pc, pcitag_t tag,
bus_space_tag_t memt, bus_space_handle_t *memh)
{
bus_addr_t base;
pcireg_t reg, table, type;
int bir, offset;
int off, tblsz;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0)
panic("%s: no msix capability", __func__);
table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
bir = (table & PCI_MSIX_TABLE_BIR);
offset = (table & PCI_MSIX_TABLE_OFF);
tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
bir = PCI_MAPREG_START + bir * 4;
type = pci_mapreg_type(pc, tag, bir);
if (pci_mapreg_info(pc, tag, bir, type, &base, NULL, NULL) ||
_bus_space_map(memt, base + offset, tblsz * 16, 0, memh))
return -1;
return 0;
}
void
pci_msix_table_unmap(pci_chipset_tag_t pc, pcitag_t tag,
bus_space_tag_t memt, bus_space_handle_t memh)
{
pcireg_t reg;
int tblsz;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
panic("%s: no msix capability", __func__);
tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
_bus_space_unmap(memt, memh, tblsz * 16, NULL);
}
void msi_hwmask(struct pic *, int);
void msi_hwunmask(struct pic *, int);
void msi_addroute(struct pic *, struct cpu_info *, int, int, int);
void msi_delroute(struct pic *, struct cpu_info *, int, int, int);
struct pic msi_pic = {
{0, {NULL}, NULL, 0, "msi", NULL, 0, 0},
PIC_MSI,
#ifdef MULTIPROCESSOR
{},
#endif
msi_hwmask,
msi_hwunmask,
msi_addroute,
msi_delroute,
NULL,
ioapic_edge_stubs
};
void
msi_hwmask(struct pic *pic, int pin)
{
}
void
msi_hwunmask(struct pic *pic, int pin)
{
}
void
msi_addroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type)
{
pci_chipset_tag_t pc = NULL; /* XXX */
pcitag_t tag = pin;
pcireg_t reg, addr;
int off;
if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, ®) == 0)
panic("%s: no msi capability", __func__);
addr = 0xfee00000UL | (ci->ci_apicid << 12);
if (reg & PCI_MSI_MC_C64) {
pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
pci_conf_write(pc, tag, off + PCI_MSI_MAU32, 0);
pci_conf_write(pc, tag, off + PCI_MSI_MD64, vec);
} else {
pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
pci_conf_write(pc, tag, off + PCI_MSI_MD32, vec);
}
pci_conf_write(pc, tag, off, reg | PCI_MSI_MC_MSIE);
}
void
msi_delroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type)
{
pci_chipset_tag_t pc = NULL; /* XXX */
pcitag_t tag = pin;
pcireg_t reg;
int off;
if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, ®))
pci_conf_write(pc, tag, off, reg & ~PCI_MSI_MC_MSIE);
}
int
pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t tag = pa->pa_tag;
if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 || mp_busses == NULL ||
pci_get_capability(pc, tag, PCI_CAP_MSI, NULL, NULL) == 0)
return 1;
ihp->tag = tag;
ihp->line = APIC_INT_VIA_MSG;
ihp->pin = 0;
return 0;
}
void msix_hwmask(struct pic *, int);
void msix_hwunmask(struct pic *, int);
void msix_addroute(struct pic *, struct cpu_info *, int, int, int);
void msix_delroute(struct pic *, struct cpu_info *, int, int, int);
struct pic msix_pic = {
{0, {NULL}, NULL, 0, "msix", NULL, 0, 0},
PIC_MSI,
#ifdef MULTIPROCESSOR
{},
#endif
msix_hwmask,
msix_hwunmask,
msix_addroute,
msix_delroute,
NULL,
ioapic_edge_stubs
};
/*
* We pack the MSI-X vector number into the lower 8 bits of the PCI
* tag and use that as the MSI-X "PIC" pin number. This allows us to
* address 256 MSI-X vectors which ought to be enough for anybody.
*/
#define PCI_MSIX_VEC_MASK 0xff
#define PCI_MSIX_VEC(pin) ((pin) & PCI_MSIX_VEC_MASK)
#define PCI_MSIX_TAG(pin) ((pin) & ~PCI_MSIX_VEC_MASK)
#define PCI_MSIX_PIN(tag, vec) ((tag) | (vec))
void
msix_hwmask(struct pic *pic, int pin)
{
}
void
msix_hwunmask(struct pic *pic, int pin)
{
}
void
msix_addroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type)
{
pci_chipset_tag_t pc = NULL; /* XXX */
bus_space_tag_t memt = X86_BUS_SPACE_MEM; /* XXX */
bus_space_handle_t memh;
pcitag_t tag = PCI_MSIX_TAG(pin);
int entry = PCI_MSIX_VEC(pin);
pcireg_t reg, addr;
uint32_t ctrl;
int off;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0)
panic("%s: no msix capability", __func__);
KASSERT(entry <= PCI_MSIX_MC_TBLSZ(reg));
if (pci_msix_table_map(pc, tag, memt, &memh))
panic("%s: cannot map registers", __func__);
addr = 0xfee00000UL | (ci->ci_apicid << 12);
bus_space_write_4(memt, memh, PCI_MSIX_MA(entry), addr);
bus_space_write_4(memt, memh, PCI_MSIX_MAU32(entry), 0);
bus_space_write_4(memt, memh, PCI_MSIX_MD(entry), vec);
bus_space_barrier(memt, memh, PCI_MSIX_MA(entry), 16,
BUS_SPACE_BARRIER_WRITE);
ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(entry));
bus_space_write_4(memt, memh, PCI_MSIX_VC(entry),
ctrl & ~PCI_MSIX_VC_MASK);
pci_msix_table_unmap(pc, tag, memt, memh);
pci_conf_write(pc, tag, off, reg | PCI_MSIX_MC_MSIXE);
}
void
msix_delroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type)
{
pci_chipset_tag_t pc = NULL; /* XXX */
bus_space_tag_t memt = X86_BUS_SPACE_MEM; /* XXX */
bus_space_handle_t memh;
pcitag_t tag = PCI_MSIX_TAG(pin);
int entry = PCI_MSIX_VEC(pin);
pcireg_t reg;
uint32_t ctrl;
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
return;
KASSERT(entry <= PCI_MSIX_MC_TBLSZ(reg));
if (pci_msix_table_map(pc, tag, memt, &memh))
return;
ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(entry));
bus_space_write_4(memt, memh, PCI_MSIX_VC(entry),
ctrl | PCI_MSIX_VC_MASK);
pci_msix_table_unmap(pc, tag, memt, memh);
}
int
pci_intr_map_msix(struct pci_attach_args *pa, int vec, pci_intr_handle_t *ihp)
{
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t tag = pa->pa_tag;
pcireg_t reg;
KASSERT(PCI_MSIX_VEC(vec) == vec);
if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 || mp_busses == NULL ||
pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
return 1;
if (vec > PCI_MSIX_MC_TBLSZ(reg))
return 1;
ihp->tag = PCI_MSIX_PIN(tag, vec);
ihp->line = APIC_INT_VIA_MSGX;
ihp->pin = 0;
return 0;
}
int
pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
int pin = pa->pa_rawintrpin;
int line = pa->pa_intrline;
#if NIOAPIC > 0
struct mp_intr_map *mip;
int bus, dev, func;
#endif
if (pin == 0) {
/* No IRQ used. */
goto bad;
}
if (pin > PCI_INTERRUPT_PIN_MAX) {
printf("pci_intr_map: bad interrupt pin %d\n", pin);
goto bad;
}
ihp->tag = pa->pa_tag;
ihp->line = line;
ihp->pin = pin;
#if NIOAPIC > 0
pci_decompose_tag(pa->pa_pc, pa->pa_tag, &bus, &dev, &func);
if (mp_busses != NULL) {
int mpspec_pin = (dev << 2) | (pin - 1);
if (bus < mp_nbusses) {
for (mip = mp_busses[bus].mb_intrs;
mip != NULL; mip = mip->next) {
if (&mp_busses[bus] == mp_isa_bus ||
&mp_busses[bus] == mp_eisa_bus)
continue;
if (mip->bus_pin == mpspec_pin) {
ihp->line = mip->ioapic_ih | line;
return 0;
}
}
}
if (pa->pa_bridgetag) {
int swizpin = PPB_INTERRUPT_SWIZZLE(pin, dev);
if (pa->pa_bridgeih[swizpin - 1].line != -1) {
ihp->line = pa->pa_bridgeih[swizpin - 1].line;
ihp->line |= line;
return 0;
}
}
/*
* No explicit PCI mapping found. This is not fatal,
* we'll try the ISA (or possibly EISA) mappings next.
*/
}
#endif
/*
* Section 6.2.4, `Miscellaneous Functions', says that 255 means
* `unknown' or `no connection' on a PC. We assume that a device with
* `no connection' either doesn't have an interrupt (in which case the
* pin number should be 0, and would have been noticed above), or
* wasn't configured by the BIOS (in which case we punt, since there's
* no real way we can know how the interrupt lines are mapped in the
* hardware).
*
* XXX
* Since IRQ 0 is only used by the clock, and we can't actually be sure
* that the BIOS did its job, we also recognize that as meaning that
* the BIOS has not configured the device.
*/
if (line == 0 || line == X86_PCI_INTERRUPT_LINE_NO_CONNECTION)
goto bad;
if (line >= NUM_LEGACY_IRQS) {
printf("pci_intr_map: bad interrupt line %d\n", line);
goto bad;
}
if (line == 2) {
printf("pci_intr_map: changed line 2 to line 9\n");
line = 9;
}
#if NIOAPIC > 0
if (mp_busses != NULL) {
if (mip == NULL && mp_isa_bus) {
for (mip = mp_isa_bus->mb_intrs; mip != NULL;
mip = mip->next) {
if (mip->bus_pin == line) {
ihp->line = mip->ioapic_ih | line;
return 0;
}
}
}
#if NEISA > 0
if (mip == NULL && mp_eisa_bus) {
for (mip = mp_eisa_bus->mb_intrs; mip != NULL;
mip = mip->next) {
if (mip->bus_pin == line) {
ihp->line = mip->ioapic_ih | line;
return 0;
}
}
}
#endif
if (mip == NULL) {
printf("pci_intr_map: "
"bus %d dev %d func %d pin %d; line %d\n",
bus, dev, func, pin, line);
printf("pci_intr_map: no MP mapping found\n");
}
}
#endif
return 0;
bad:
ihp->line = -1;
return 1;
}
const char *
pci_intr_string(pci_chipset_tag_t pc, pci_intr_handle_t ih)
{
static char irqstr[64];
if (ih.line == 0)
panic("pci_intr_string: bogus handle 0x%x", ih.line);
if (ih.line & APIC_INT_VIA_MSG)
return ("msi");
if (ih.line & APIC_INT_VIA_MSGX)
return ("msix");
#if NIOAPIC > 0
if (ih.line & APIC_INT_VIA_APIC)
snprintf(irqstr, sizeof(irqstr), "apic %d int %d",
APIC_IRQ_APIC(ih.line), APIC_IRQ_PIN(ih.line));
else
snprintf(irqstr, sizeof(irqstr), "irq %d",
pci_intr_line(pc, ih));
#else
snprintf(irqstr, sizeof(irqstr), "irq %d", pci_intr_line(pc, ih));
#endif
return (irqstr);
}
#include "acpiprt.h"
#if NACPIPRT > 0
void acpiprt_route_interrupt(int bus, int dev, int pin);
#endif
void *
pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, int level,
int (*func)(void *), void *arg, const char *what)
{
return pci_intr_establish_cpu(pc, ih, level, NULL, func, arg, what);
}
void *
pci_intr_establish_cpu(pci_chipset_tag_t pc, pci_intr_handle_t ih,
int level, struct cpu_info *ci,
int (*func)(void *), void *arg, const char *what)
{
int pin, irq;
int bus, dev;
pcitag_t tag = ih.tag;
struct pic *pic;
if (ih.line & APIC_INT_VIA_MSG) {
return intr_establish(-1, &msi_pic, tag, IST_PULSE, level,
ci, func, arg, what);
}
if (ih.line & APIC_INT_VIA_MSGX) {
return intr_establish(-1, &msix_pic, tag, IST_PULSE, level,
ci, func, arg, what);
}
pci_decompose_tag(pc, ih.tag, &bus, &dev, NULL);
#if NACPIPRT > 0
acpiprt_route_interrupt(bus, dev, ih.pin);
#endif
pic = &i8259_pic;
pin = irq = ih.line;
#if NIOAPIC > 0
if (ih.line & APIC_INT_VIA_APIC) {
pic = (struct pic *)ioapic_find(APIC_IRQ_APIC(ih.line));
if (pic == NULL) {
printf("pci_intr_establish: bad ioapic %d\n",
APIC_IRQ_APIC(ih.line));
return NULL;
}
pin = APIC_IRQ_PIN(ih.line);
irq = APIC_IRQ_LEGACY_IRQ(ih.line);
if (irq < 0 || irq >= NUM_LEGACY_IRQS)
irq = -1;
}
#endif
return intr_establish(irq, pic, pin, IST_LEVEL, level, ci,
func, arg, what);
}
void
pci_intr_disestablish(pci_chipset_tag_t pc, void *cookie)
{
intr_disestablish(cookie);
}
struct extent *pciio_ex;
struct extent *pcimem_ex;
struct extent *pcibus_ex;
void
pci_init_extents(void)
{
bios_memmap_t *bmp;
u_int64_t size;
if (pciio_ex == NULL) {
/*
* We only have 64K of addressable I/O space.
* However, since BARs may contain garbage, we cover
* the full 32-bit address space defined by PCI of
* which we only make the first 64K available.
*/
pciio_ex = extent_create("pciio", 0, 0xffffffff, M_DEVBUF,
NULL, 0, EX_NOWAIT | EX_FILLED);
if (pciio_ex == NULL)
return;
extent_free(pciio_ex, 0, 0x10000, EX_NOWAIT);
}
if (pcimem_ex == NULL) {
/*
* Cover the 36-bit address space addressable by PAE
* here. As long as vendors continue to support
* 32-bit operating systems, we should never see BARs
* outside that region.
*
* Dell 13G servers have important devices outside the
* 36-bit address space. Until we can extract the address
* ranges from ACPI, expand the allowed range to suit.
*/
pcimem_ex = extent_create("pcimem", 0, 0xffffffffffffffffUL,
M_DEVBUF, NULL, 0, EX_NOWAIT);
if (pcimem_ex == NULL)
return;
extent_alloc_region(pcimem_ex, 0x40000000000UL,
0xfffffc0000000000UL, EX_NOWAIT);
for (bmp = bios_memmap; bmp->type != BIOS_MAP_END; bmp++) {
/*
* Ignore address space beyond 4G.
*/
if (bmp->addr >= 0x100000000ULL)
continue;
size = bmp->size;
if (bmp->addr + size >= 0x100000000ULL)
size = 0x100000000ULL - bmp->addr;
/* Ignore zero-sized regions. */
if (size == 0)
continue;
if (extent_alloc_region(pcimem_ex, bmp->addr, size,
EX_NOWAIT))
printf("memory map conflict 0x%llx/0x%llx\n",
bmp->addr, bmp->size);
}
/* Take out the video buffer area and BIOS areas. */
extent_alloc_region(pcimem_ex, IOM_BEGIN, IOM_SIZE,
EX_CONFLICTOK | EX_NOWAIT);
}
if (pcibus_ex == NULL) {
pcibus_ex = extent_create("pcibus", 0, 0xff, M_DEVBUF,
NULL, 0, EX_NOWAIT);
}
}
int
pci_probe_device_hook(pci_chipset_tag_t pc, struct pci_attach_args *pa)
{
#if NACPIDMAR > 0
acpidmar_pci_hook(pc, pa);
#endif
return 0;
}
#if NACPI > 0
void acpi_pci_match(struct device *, struct pci_attach_args *);
pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
void acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int);
#endif
void
pci_dev_postattach(struct device *dev, struct pci_attach_args *pa)
{
#if NACPI > 0
acpi_pci_match(dev, pa);
#endif
}
pcireg_t
pci_min_powerstate(pci_chipset_tag_t pc, pcitag_t tag)
{
#if NACPI > 0
return acpi_pci_min_powerstate(pc, tag);
#else
return pci_get_powerstate(pc, tag);
#endif
}
void
pci_set_powerstate_md(pci_chipset_tag_t pc, pcitag_t tag, int state, int pre)
{
#if NACPI > 0
acpi_pci_set_powerstate(pc, tag, state, pre);
#endif
}
2
2
2
78
2
78
2
2
24
24
14
14
14
17
17
17
83
897
1
858
42
900
1
13
2
2
2
2
10
3
7
1
4
2
23
23
14
14
17
17
17
82
100
2
2
2
17
900
898
1
13
13
9
9
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
/* $OpenBSD: ifq.c,v 1.46 2022/04/30 21:13:57 bluhm Exp $ */
/*
* Copyright (c) 2015 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "bpfilter.h"
#include "kstat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#if NKSTAT > 0
#include <sys/kstat.h>
#endif
/*
* priq glue
*/
unsigned int priq_idx(unsigned int, const struct mbuf *);
struct mbuf *priq_enq(struct ifqueue *, struct mbuf *);
struct mbuf *priq_deq_begin(struct ifqueue *, void **);
void priq_deq_commit(struct ifqueue *, struct mbuf *, void *);
void priq_purge(struct ifqueue *, struct mbuf_list *);
void *priq_alloc(unsigned int, void *);
void priq_free(unsigned int, void *);
const struct ifq_ops priq_ops = {
priq_idx,
priq_enq,
priq_deq_begin,
priq_deq_commit,
priq_purge,
priq_alloc,
priq_free,
};
const struct ifq_ops * const ifq_priq_ops = &priq_ops;
/*
* priq internal structures
*/
struct priq {
struct mbuf_list pq_lists[IFQ_NQUEUES];
};
/*
* ifqueue serialiser
*/
void ifq_start_task(void *);
void ifq_restart_task(void *);
void ifq_barrier_task(void *);
void ifq_bundle_task(void *);
static inline void
ifq_run_start(struct ifqueue *ifq)
{
ifq_serialize(ifq, &ifq->ifq_start);
}
void
ifq_serialize(struct ifqueue *ifq, struct task *t)
{
struct task work;
if (ISSET(t->t_flags, TASK_ONQUEUE))
return;
mtx_enter(&ifq->ifq_task_mtx);
if (!ISSET(t->t_flags, TASK_ONQUEUE)) {
SET(t->t_flags, TASK_ONQUEUE);
TAILQ_INSERT_TAIL(&ifq->ifq_task_list, t, t_entry);
}
if (ifq->ifq_serializer == NULL) {
ifq->ifq_serializer = curcpu();
while ((t = TAILQ_FIRST(&ifq->ifq_task_list)) != NULL) {
TAILQ_REMOVE(&ifq->ifq_task_list, t, t_entry);
CLR(t->t_flags, TASK_ONQUEUE);
work = *t; /* copy to caller to avoid races */
mtx_leave(&ifq->ifq_task_mtx);
(*work.t_func)(work.t_arg);
mtx_enter(&ifq->ifq_task_mtx);
}
ifq->ifq_serializer = NULL;
}
mtx_leave(&ifq->ifq_task_mtx);
}
int
ifq_is_serialized(struct ifqueue *ifq)
{
return (ifq->ifq_serializer == curcpu());
}
void
ifq_start(struct ifqueue *ifq)
{
if (ifq_len(ifq) >= min(ifq->ifq_if->if_txmit, ifq->ifq_maxlen)) {
task_del(ifq->ifq_softnet, &ifq->ifq_bundle);
ifq_run_start(ifq);
} else
task_add(ifq->ifq_softnet, &ifq->ifq_bundle);
}
void
ifq_start_task(void *p)
{
struct ifqueue *ifq = p;
struct ifnet *ifp = ifq->ifq_if;
if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
ifq_empty(ifq) || ifq_is_oactive(ifq))
return;
ifp->if_qstart(ifq);
}
void
ifq_restart_task(void *p)
{
struct ifqueue *ifq = p;
struct ifnet *ifp = ifq->ifq_if;
ifq_clr_oactive(ifq);
ifp->if_qstart(ifq);
}
void
ifq_bundle_task(void *p)
{
struct ifqueue *ifq = p;
ifq_run_start(ifq);
}
void
ifq_barrier(struct ifqueue *ifq)
{
struct cond c = COND_INITIALIZER();
struct task t = TASK_INITIALIZER(ifq_barrier_task, &c);
task_del(ifq->ifq_softnet, &ifq->ifq_bundle);
if (ifq->ifq_serializer == NULL)
return;
ifq_serialize(ifq, &t);
cond_wait(&c, "ifqbar");
}
void
ifq_barrier_task(void *p)
{
struct cond *c = p;
cond_signal(c);
}
/*
* ifqueue mbuf queue API
*/
#if NKSTAT > 0
struct ifq_kstat_data {
struct kstat_kv kd_packets;
struct kstat_kv kd_bytes;
struct kstat_kv kd_qdrops;
struct kstat_kv kd_errors;
struct kstat_kv kd_qlen;
struct kstat_kv kd_maxqlen;
struct kstat_kv kd_oactive;
};
static const struct ifq_kstat_data ifq_kstat_tpl = {
KSTAT_KV_UNIT_INITIALIZER("packets",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("bytes",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES),
KSTAT_KV_UNIT_INITIALIZER("qdrops",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("errors",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("qlen",
KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("maxqlen",
KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS),
KSTAT_KV_INITIALIZER("oactive", KSTAT_KV_T_BOOL),
};
int
ifq_kstat_copy(struct kstat *ks, void *dst)
{
struct ifqueue *ifq = ks->ks_softc;
struct ifq_kstat_data *kd = dst;
*kd = ifq_kstat_tpl;
kstat_kv_u64(&kd->kd_packets) = ifq->ifq_packets;
kstat_kv_u64(&kd->kd_bytes) = ifq->ifq_bytes;
kstat_kv_u64(&kd->kd_qdrops) = ifq->ifq_qdrops;
kstat_kv_u64(&kd->kd_errors) = ifq->ifq_errors;
kstat_kv_u32(&kd->kd_qlen) = ifq->ifq_len;
kstat_kv_u32(&kd->kd_maxqlen) = ifq->ifq_maxlen;
kstat_kv_bool(&kd->kd_oactive) = ifq->ifq_oactive;
return (0);
}
#endif
void
ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx)
{
ifq->ifq_if = ifp;
ifq->ifq_softnet = net_tq(ifp->if_index + idx);
ifq->ifq_softc = NULL;
mtx_init(&ifq->ifq_mtx, IPL_NET);
/* default to priq */
ifq->ifq_ops = &priq_ops;
ifq->ifq_q = priq_ops.ifqop_alloc(idx, NULL);
ml_init(&ifq->ifq_free);
ifq->ifq_len = 0;
ifq->ifq_packets = 0;
ifq->ifq_bytes = 0;
ifq->ifq_qdrops = 0;
ifq->ifq_errors = 0;
ifq->ifq_mcasts = 0;
mtx_init(&ifq->ifq_task_mtx, IPL_NET);
TAILQ_INIT(&ifq->ifq_task_list);
ifq->ifq_serializer = NULL;
task_set(&ifq->ifq_bundle, ifq_bundle_task, ifq);
task_set(&ifq->ifq_start, ifq_start_task, ifq);
task_set(&ifq->ifq_restart, ifq_restart_task, ifq);
if (ifq->ifq_maxlen == 0)
ifq_set_maxlen(ifq, IFQ_MAXLEN);
ifq->ifq_idx = idx;
#if NKSTAT > 0
/* XXX xname vs driver name and unit */
ifq->ifq_kstat = kstat_create(ifp->if_xname, 0,
"txq", ifq->ifq_idx, KSTAT_T_KV, 0);
KASSERT(ifq->ifq_kstat != NULL);
kstat_set_mutex(ifq->ifq_kstat, &ifq->ifq_mtx);
ifq->ifq_kstat->ks_softc = ifq;
ifq->ifq_kstat->ks_datalen = sizeof(ifq_kstat_tpl);
ifq->ifq_kstat->ks_copy = ifq_kstat_copy;
kstat_install(ifq->ifq_kstat);
#endif
}
void
ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf_list free_ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
const struct ifq_ops *oldops;
void *newq, *oldq;
newq = newops->ifqop_alloc(ifq->ifq_idx, opsarg);
mtx_enter(&ifq->ifq_mtx);
ifq->ifq_ops->ifqop_purge(ifq, &ml);
ifq->ifq_len = 0;
oldops = ifq->ifq_ops;
oldq = ifq->ifq_q;
ifq->ifq_ops = newops;
ifq->ifq_q = newq;
while ((m = ml_dequeue(&ml)) != NULL) {
m = ifq->ifq_ops->ifqop_enq(ifq, m);
if (m != NULL) {
ifq->ifq_qdrops++;
ml_enqueue(&free_ml, m);
} else
ifq->ifq_len++;
}
mtx_leave(&ifq->ifq_mtx);
oldops->ifqop_free(ifq->ifq_idx, oldq);
ml_purge(&free_ml);
}
void
ifq_destroy(struct ifqueue *ifq)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
#if NKSTAT > 0
kstat_destroy(ifq->ifq_kstat);
#endif
NET_ASSERT_UNLOCKED();
if (!task_del(ifq->ifq_softnet, &ifq->ifq_bundle))
taskq_barrier(ifq->ifq_softnet);
/* don't need to lock because this is the last use of the ifq */
ifq->ifq_ops->ifqop_purge(ifq, &ml);
ifq->ifq_ops->ifqop_free(ifq->ifq_idx, ifq->ifq_q);
ml_purge(&ml);
}
void
ifq_add_data(struct ifqueue *ifq, struct if_data *data)
{
mtx_enter(&ifq->ifq_mtx);
data->ifi_opackets += ifq->ifq_packets;
data->ifi_obytes += ifq->ifq_bytes;
data->ifi_oqdrops += ifq->ifq_qdrops;
data->ifi_omcasts += ifq->ifq_mcasts;
/* ifp->if_data.ifi_oerrors */
mtx_leave(&ifq->ifq_mtx);
}
int
ifq_enqueue(struct ifqueue *ifq, struct mbuf *m)
{
struct mbuf *dm;
mtx_enter(&ifq->ifq_mtx);
dm = ifq->ifq_ops->ifqop_enq(ifq, m);
if (dm != m) {
ifq->ifq_packets++;
ifq->ifq_bytes += m->m_pkthdr.len;
if (ISSET(m->m_flags, M_MCAST))
ifq->ifq_mcasts++;
}
if (dm == NULL)
ifq->ifq_len++;
else
ifq->ifq_qdrops++;
mtx_leave(&ifq->ifq_mtx);
if (dm != NULL)
m_freem(dm);
return (dm == m ? ENOBUFS : 0);
}
static inline void
ifq_deq_enter(struct ifqueue *ifq)
{
mtx_enter(&ifq->ifq_mtx);
}
static inline void
ifq_deq_leave(struct ifqueue *ifq)
{
struct mbuf_list ml;
ml = ifq->ifq_free;
ml_init(&ifq->ifq_free);
mtx_leave(&ifq->ifq_mtx);
if (!ml_empty(&ml))
ml_purge(&ml);
}
struct mbuf *
ifq_deq_begin(struct ifqueue *ifq)
{
struct mbuf *m = NULL;
void *cookie;
ifq_deq_enter(ifq);
if (ifq->ifq_len == 0 ||
(m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) {
ifq_deq_leave(ifq);
return (NULL);
}
m->m_pkthdr.ph_cookie = cookie;
return (m);
}
void
ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m)
{
void *cookie;
KASSERT(m != NULL);
cookie = m->m_pkthdr.ph_cookie;
ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
ifq->ifq_len--;
ifq_deq_leave(ifq);
}
void
ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m)
{
KASSERT(m != NULL);
ifq_deq_leave(ifq);
}
struct mbuf *
ifq_dequeue(struct ifqueue *ifq)
{
struct mbuf *m;
m = ifq_deq_begin(ifq);
if (m == NULL)
return (NULL);
ifq_deq_commit(ifq, m);
return (m);
}
int
ifq_deq_sleep(struct ifqueue *ifq, struct mbuf **mp, int nbio, int priority,
const char *wmesg, volatile unsigned int *sleeping,
volatile unsigned int *alive)
{
struct mbuf *m;
void *cookie;
int error = 0;
ifq_deq_enter(ifq);
if (ifq->ifq_len == 0 && nbio)
error = EWOULDBLOCK;
else {
for (;;) {
m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie);
if (m != NULL) {
ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
ifq->ifq_len--;
*mp = m;
break;
}
(*sleeping)++;
error = msleep_nsec(ifq, &ifq->ifq_mtx,
priority, wmesg, INFSLP);
(*sleeping)--;
if (error != 0)
break;
if (!(*alive)) {
error = EIO;
break;
}
}
}
ifq_deq_leave(ifq);
return (error);
}
int
ifq_hdatalen(struct ifqueue *ifq)
{
struct mbuf *m;
int len = 0;
if (ifq_empty(ifq))
return (0);
m = ifq_deq_begin(ifq);
if (m != NULL) {
len = m->m_pkthdr.len;
ifq_deq_rollback(ifq, m);
}
return (len);
}
unsigned int
ifq_purge(struct ifqueue *ifq)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
unsigned int rv;
mtx_enter(&ifq->ifq_mtx);
ifq->ifq_ops->ifqop_purge(ifq, &ml);
rv = ifq->ifq_len;
ifq->ifq_len = 0;
ifq->ifq_qdrops += rv;
mtx_leave(&ifq->ifq_mtx);
KASSERT(rv == ml_len(&ml));
ml_purge(&ml);
return (rv);
}
void *
ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops)
{
mtx_enter(&ifq->ifq_mtx);
if (ifq->ifq_ops == ops)
return (ifq->ifq_q);
mtx_leave(&ifq->ifq_mtx);
return (NULL);
}
void
ifq_q_leave(struct ifqueue *ifq, void *q)
{
KASSERT(q == ifq->ifq_q);
mtx_leave(&ifq->ifq_mtx);
}
void
ifq_mfreem(struct ifqueue *ifq, struct mbuf *m)
{
MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx);
ifq->ifq_len--;
ifq->ifq_qdrops++;
ml_enqueue(&ifq->ifq_free, m);
}
void
ifq_mfreeml(struct ifqueue *ifq, struct mbuf_list *ml)
{
MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx);
ifq->ifq_len -= ml_len(ml);
ifq->ifq_qdrops += ml_len(ml);
ml_enlist(&ifq->ifq_free, ml);
}
/*
* ifiq
*/
#if NKSTAT > 0
struct ifiq_kstat_data {
struct kstat_kv kd_packets;
struct kstat_kv kd_bytes;
struct kstat_kv kd_qdrops;
struct kstat_kv kd_errors;
struct kstat_kv kd_qlen;
};
static const struct ifiq_kstat_data ifiq_kstat_tpl = {
KSTAT_KV_UNIT_INITIALIZER("packets",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("bytes",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES),
KSTAT_KV_UNIT_INITIALIZER("qdrops",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("errors",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("qlen",
KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS),
};
int
ifiq_kstat_copy(struct kstat *ks, void *dst)
{
struct ifiqueue *ifiq = ks->ks_softc;
struct ifiq_kstat_data *kd = dst;
*kd = ifiq_kstat_tpl;
kstat_kv_u64(&kd->kd_packets) = ifiq->ifiq_packets;
kstat_kv_u64(&kd->kd_bytes) = ifiq->ifiq_bytes;
kstat_kv_u64(&kd->kd_qdrops) = ifiq->ifiq_qdrops;
kstat_kv_u64(&kd->kd_errors) = ifiq->ifiq_errors;
kstat_kv_u32(&kd->kd_qlen) = ml_len(&ifiq->ifiq_ml);
return (0);
}
#endif
static void ifiq_process(void *);
void
ifiq_init(struct ifiqueue *ifiq, struct ifnet *ifp, unsigned int idx)
{
ifiq->ifiq_if = ifp;
ifiq->ifiq_softnet = net_tq(ifp->if_index + idx);
ifiq->ifiq_softc = NULL;
mtx_init(&ifiq->ifiq_mtx, IPL_NET);
ml_init(&ifiq->ifiq_ml);
task_set(&ifiq->ifiq_task, ifiq_process, ifiq);
ifiq->ifiq_pressure = 0;
ifiq->ifiq_packets = 0;
ifiq->ifiq_bytes = 0;
ifiq->ifiq_qdrops = 0;
ifiq->ifiq_errors = 0;
ifiq->ifiq_idx = idx;
#if NKSTAT > 0
/* XXX xname vs driver name and unit */
ifiq->ifiq_kstat = kstat_create(ifp->if_xname, 0,
"rxq", ifiq->ifiq_idx, KSTAT_T_KV, 0);
KASSERT(ifiq->ifiq_kstat != NULL);
kstat_set_mutex(ifiq->ifiq_kstat, &ifiq->ifiq_mtx);
ifiq->ifiq_kstat->ks_softc = ifiq;
ifiq->ifiq_kstat->ks_datalen = sizeof(ifiq_kstat_tpl);
ifiq->ifiq_kstat->ks_copy = ifiq_kstat_copy;
kstat_install(ifiq->ifiq_kstat);
#endif
}
void
ifiq_destroy(struct ifiqueue *ifiq)
{
#if NKSTAT > 0
kstat_destroy(ifiq->ifiq_kstat);
#endif
NET_ASSERT_UNLOCKED();
if (!task_del(ifiq->ifiq_softnet, &ifiq->ifiq_task))
taskq_barrier(ifiq->ifiq_softnet);
/* don't need to lock because this is the last use of the ifiq */
ml_purge(&ifiq->ifiq_ml);
}
unsigned int ifiq_maxlen_drop = 2048 * 5;
unsigned int ifiq_maxlen_return = 2048 * 3;
int
ifiq_input(struct ifiqueue *ifiq, struct mbuf_list *ml)
{
struct ifnet *ifp = ifiq->ifiq_if;
struct mbuf *m;
uint64_t packets;
uint64_t bytes = 0;
unsigned int len;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
if (ml_empty(ml))
return (0);
MBUF_LIST_FOREACH(ml, m) {
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
bytes += m->m_pkthdr.len;
}
packets = ml_len(ml);
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf) {
struct mbuf_list ml0 = *ml;
ml_init(ml);
while ((m = ml_dequeue(&ml0)) != NULL) {
if ((*ifp->if_bpf_mtap)(if_bpf, m, BPF_DIRECTION_IN))
m_freem(m);
else
ml_enqueue(ml, m);
}
if (ml_empty(ml)) {
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_packets += packets;
ifiq->ifiq_bytes += bytes;
mtx_leave(&ifiq->ifiq_mtx);
return (0);
}
}
#endif
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_packets += packets;
ifiq->ifiq_bytes += bytes;
len = ml_len(&ifiq->ifiq_ml);
if (__predict_true(!ISSET(ifp->if_xflags, IFXF_MONITOR))) {
if (len > ifiq_maxlen_drop)
ifiq->ifiq_qdrops += ml_len(ml);
else
ml_enlist(&ifiq->ifiq_ml, ml);
}
mtx_leave(&ifiq->ifiq_mtx);
if (ml_empty(ml))
task_add(ifiq->ifiq_softnet, &ifiq->ifiq_task);
else
ml_purge(ml);
return (len > ifiq_maxlen_return);
}
void
ifiq_add_data(struct ifiqueue *ifiq, struct if_data *data)
{
mtx_enter(&ifiq->ifiq_mtx);
data->ifi_ipackets += ifiq->ifiq_packets;
data->ifi_ibytes += ifiq->ifiq_bytes;
data->ifi_iqdrops += ifiq->ifiq_qdrops;
mtx_leave(&ifiq->ifiq_mtx);
}
int
ifiq_enqueue(struct ifiqueue *ifiq, struct mbuf *m)
{
mtx_enter(&ifiq->ifiq_mtx);
ml_enqueue(&ifiq->ifiq_ml, m);
mtx_leave(&ifiq->ifiq_mtx);
task_add(ifiq->ifiq_softnet, &ifiq->ifiq_task);
return (0);
}
static void
ifiq_process(void *arg)
{
struct ifiqueue *ifiq = arg;
struct mbuf_list ml;
if (ifiq_empty(ifiq))
return;
mtx_enter(&ifiq->ifiq_mtx);
ml = ifiq->ifiq_ml;
ml_init(&ifiq->ifiq_ml);
mtx_leave(&ifiq->ifiq_mtx);
if_input_process(ifiq->ifiq_if, &ml);
}
int
net_ifiq_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error = EOPNOTSUPP;
/* pressure is disabled for 6.6-release */
#if 0
int val;
if (namelen != 1)
return (EISDIR);
switch (name[0]) {
case NET_LINK_IFRXQ_PRESSURE_RETURN:
val = ifiq_pressure_return;
error = sysctl_int(oldp, oldlenp, newp, newlen, &val);
if (error != 0)
return (error);
if (val < 1 || val > ifiq_pressure_drop)
return (EINVAL);
ifiq_pressure_return = val;
break;
case NET_LINK_IFRXQ_PRESSURE_DROP:
val = ifiq_pressure_drop;
error = sysctl_int(oldp, oldlenp, newp, newlen, &val);
if (error != 0)
return (error);
if (ifiq_pressure_return > val)
return (EINVAL);
ifiq_pressure_drop = val;
break;
default:
error = EOPNOTSUPP;
break;
}
#endif
return (error);
}
/*
* priq implementation
*/
unsigned int
priq_idx(unsigned int nqueues, const struct mbuf *m)
{
unsigned int flow = 0;
if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
flow = m->m_pkthdr.ph_flowid;
return (flow % nqueues);
}
void *
priq_alloc(unsigned int idx, void *null)
{
struct priq *pq;
int i;
pq = malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK);
for (i = 0; i < IFQ_NQUEUES; i++)
ml_init(&pq->pq_lists[i]);
return (pq);
}
void
priq_free(unsigned int idx, void *pq)
{
free(pq, M_DEVBUF, sizeof(struct priq));
}
struct mbuf *
priq_enq(struct ifqueue *ifq, struct mbuf *m)
{
struct priq *pq;
struct mbuf_list *pl;
struct mbuf *n = NULL;
unsigned int prio;
pq = ifq->ifq_q;
KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO);
/* Find a lower priority queue to drop from */
if (ifq_len(ifq) >= ifq->ifq_maxlen) {
for (prio = 0; prio < m->m_pkthdr.pf.prio; prio++) {
pl = &pq->pq_lists[prio];
if (ml_len(pl) > 0) {
n = ml_dequeue(pl);
goto enqueue;
}
}
/*
* There's no lower priority queue that we can
* drop from so don't enqueue this one.
*/
return (m);
}
enqueue:
pl = &pq->pq_lists[m->m_pkthdr.pf.prio];
ml_enqueue(pl, m);
return (n);
}
struct mbuf *
priq_deq_begin(struct ifqueue *ifq, void **cookiep)
{
struct priq *pq = ifq->ifq_q;
struct mbuf_list *pl;
unsigned int prio = nitems(pq->pq_lists);
struct mbuf *m;
do {
pl = &pq->pq_lists[--prio];
m = MBUF_LIST_FIRST(pl);
if (m != NULL) {
*cookiep = pl;
return (m);
}
} while (prio > 0);
return (NULL);
}
void
priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie)
{
struct mbuf_list *pl = cookie;
KASSERT(MBUF_LIST_FIRST(pl) == m);
ml_dequeue(pl);
}
void
priq_purge(struct ifqueue *ifq, struct mbuf_list *ml)
{
struct priq *pq = ifq->ifq_q;
struct mbuf_list *pl;
unsigned int prio = nitems(pq->pq_lists);
do {
pl = &pq->pq_lists[--prio];
ml_enlist(ml, pl);
} while (prio > 0);
}
2
6
6
1
12
12
2
2
1
40
2
1
2
3
12
4
1
2
1
1
6
4
1
623
620
7247
7242
7255
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
/* $OpenBSD: machdep.c,v 1.280 2022/08/25 17:25:25 cheloha Exp $ */
/* $NetBSD: machdep.c,v 1.3 2003/05/07 22:58:18 fvdl Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
* Simulation Facility, NASA Ames Research Center.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1982, 1987, 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)machdep.c 7.4 (Berkeley) 6/3/91
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/exec.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#include <sys/msgbuf.h>
#include <sys/mount.h>
#include <sys/extent.h>
#include <sys/core.h>
#include <sys/kcore.h>
#include <sys/syscallargs.h>
#include <dev/cons.h>
#include <stand/boot/bootarg.h>
#include <net/if.h>
#include <uvm/uvm_extern.h>
#include <sys/sysctl.h>
#include <machine/cpu_full.h>
#include <machine/cpufunc.h>
#include <machine/pio.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/fpu.h>
#include <machine/biosvar.h>
#include <machine/mpbiosvar.h>
#include <machine/kcore.h>
#include <machine/tss.h>
#include <dev/isa/isareg.h>
#include <dev/ic/i8042reg.h>
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_extern.h>
extern int db_console;
#endif
#include "isa.h"
#include "isadma.h"
#include "ksyms.h"
#include "acpi.h"
#if NACPI > 0
#include <dev/acpi/acpivar.h>
#endif
#include "com.h"
#if NCOM > 0
#include <sys/tty.h>
#include <dev/ic/comvar.h>
#include <dev/ic/comreg.h>
#endif
#include "softraid.h"
#if NSOFTRAID > 0
#include <dev/softraidvar.h>
#endif
#ifdef HIBERNATE
#include <machine/hibernate_var.h>
#endif /* HIBERNATE */
#include "ukbd.h"
#include "pckbc.h"
#if NPCKBC > 0 && NUKBD > 0
#include <dev/ic/pckbcvar.h>
#endif
/* #define MACHDEP_DEBUG */
#ifdef MACHDEP_DEBUG
#define DPRINTF(x...) do { printf(x); } while(0)
#else
#define DPRINTF(x...)
#endif /* MACHDEP_DEBUG */
/* the following is used externally (sysctl_hw) */
char machine[] = MACHINE;
/*
* switchto vectors
*/
void (*cpu_idle_cycle_fcn)(void) = NULL;
/* the following is used externally for concurrent handlers */
int setperf_prio = 0;
#ifdef CPURESET_DELAY
int cpureset_delay = CPURESET_DELAY;
#else
int cpureset_delay = 0;
#endif
int physmem;
u_int64_t dumpmem_low;
u_int64_t dumpmem_high;
extern int boothowto;
int cpu_class;
paddr_t dumpmem_paddr;
vaddr_t dumpmem_vaddr;
psize_t dumpmem_sz;
vaddr_t kern_end;
vaddr_t msgbuf_vaddr;
paddr_t msgbuf_paddr;
vaddr_t idt_vaddr;
paddr_t idt_paddr;
vaddr_t lo32_vaddr;
paddr_t lo32_paddr;
paddr_t tramp_pdirpa;
int kbd_reset;
int lid_action = 1;
int pwr_action = 1;
int forceukbd;
/*
* safepri is a safe priority for sleep to set for a spin-wait
* during autoconfiguration or after a panic.
*/
int safepri = 0;
struct vm_map *exec_map = NULL;
struct vm_map *phys_map = NULL;
/* UVM constraint ranges. */
struct uvm_constraint_range isa_constraint = { 0x0, 0x00ffffffUL };
struct uvm_constraint_range dma_constraint = { 0x0, 0xffffffffUL };
struct uvm_constraint_range *uvm_md_constraints[] = {
&isa_constraint,
&dma_constraint,
NULL,
};
paddr_t avail_start;
paddr_t avail_end;
void (*delay_func)(int) = i8254_delay;
void (*initclock_func)(void) = i8254_initclocks;
/*
* Format of boot information passed to us by 32-bit /boot
*/
typedef struct _boot_args32 {
int ba_type;
int ba_size;
int ba_nextX; /* a ptr in 32-bit world, but not here */
char ba_arg[1];
} bootarg32_t;
#define BOOTARGC_MAX NBPG /* one page */
bios_bootmac_t *bios_bootmac;
/* locore copies the arguments from /boot to here for us */
char bootinfo[BOOTARGC_MAX];
int bootinfo_size = BOOTARGC_MAX;
void getbootinfo(char *, int);
/* Data passed to us by /boot, filled in by getbootinfo() */
bios_diskinfo_t *bios_diskinfo;
bios_memmap_t *bios_memmap;
u_int32_t bios_cksumlen;
bios_efiinfo_t *bios_efiinfo;
bios_ucode_t *bios_ucode;
/*
* Size of memory segments, before any memory is stolen.
*/
phys_ram_seg_t mem_clusters[VM_PHYSSEG_MAX];
int mem_cluster_cnt;
int cpu_dump(void);
int cpu_dumpsize(void);
u_long cpu_dump_mempagecnt(void);
void dumpsys(void);
void cpu_init_extents(void);
void map_tramps(void);
void init_x86_64(paddr_t);
void (*cpuresetfn)(void);
void enter_shared_special_pages(void);
#ifdef APERTURE
int allowaperture = 0;
#endif
/*
* Machine-dependent startup code
*/
void
cpu_startup(void)
{
vaddr_t minaddr, maxaddr;
msgbuf_vaddr = PMAP_DIRECT_MAP(msgbuf_paddr);
initmsgbuf((caddr_t)msgbuf_vaddr, round_page(MSGBUFSIZE));
printf("%s", version);
startclocks();
rtcinit();
printf("real mem = %lu (%luMB)\n", ptoa((psize_t)physmem),
ptoa((psize_t)physmem)/1024/1024);
/*
* Allocate a submap for exec arguments. This map effectively
* limits the number of processes exec'ing at any time.
*/
minaddr = vm_map_min(kernel_map);
exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
16*NCARGS, VM_MAP_PAGEABLE, FALSE, NULL);
/*
* Allocate a submap for physio
*/
minaddr = vm_map_min(kernel_map);
phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
VM_PHYS_SIZE, 0, FALSE, NULL);
printf("avail mem = %lu (%luMB)\n", ptoa((psize_t)uvmexp.free),
ptoa((psize_t)uvmexp.free)/1024/1024);
bufinit();
if (boothowto & RB_CONFIG) {
#ifdef BOOT_CONFIG
user_config();
#else
printf("kernel does not support -c; continuing..\n");
#endif
}
/* Safe for i/o port / memory space allocation to use malloc now. */
x86_bus_space_mallocok();
#ifndef SMALL_KERNEL
cpu_ucode_setup();
cpu_ucode_apply(&cpu_info_primary);
#endif
cpu_tsx_disable(&cpu_info_primary);
/* enter the IDT and trampoline code in the u-k maps */
enter_shared_special_pages();
/* initialize CPU0's TSS and GDT and put them in the u-k maps */
cpu_enter_pages(&cpu_info_full_primary);
}
/*
* enter_shared_special_pages
*
* Requests mapping of various special pages required in the Intel Meltdown
* case (to be entered into the U-K page table):
*
* 1 IDT page
* Various number of pages covering the U-K ".kutext" section. This section
* contains code needed during trampoline operation
* Various number of pages covering the U-K ".kudata" section. This section
* contains data accessed by the trampoline, before switching to U+K
* (for example, various shared global variables used by IPIs, etc)
*
* The linker script places the required symbols in the sections above.
*
* On CPUs not affected by Meltdown, the calls to pmap_enter_special below
* become no-ops.
*/
void
enter_shared_special_pages(void)
{
extern char __kutext_start[], __kutext_end[], __kernel_kutext_phys[];
extern char __text_page_start[], __text_page_end[];
extern char __kernel_kutext_page_phys[];
extern char __kudata_start[], __kudata_end[], __kernel_kudata_phys[];
vaddr_t va;
paddr_t pa;
/* idt */
pmap_enter_special(idt_vaddr, idt_paddr, PROT_READ);
DPRINTF("%s: entered idt page va 0x%llx pa 0x%llx\n", __func__,
(uint64_t)idt_vaddr, (uint64_t)idt_paddr);
/* .kutext section */
va = (vaddr_t)__kutext_start;
pa = (paddr_t)__kernel_kutext_phys;
while (va < (vaddr_t)__kutext_end) {
pmap_enter_special(va, pa, PROT_READ | PROT_EXEC);
DPRINTF("%s: entered kutext page va 0x%llx pa 0x%llx\n",
__func__, (uint64_t)va, (uint64_t)pa);
va += PAGE_SIZE;
pa += PAGE_SIZE;
}
/* .kutext.page section */
va = (vaddr_t)__text_page_start;
pa = (paddr_t)__kernel_kutext_page_phys;
while (va < (vaddr_t)__text_page_end) {
pmap_enter_special(va, pa, PROT_READ | PROT_EXEC);
DPRINTF("%s: entered kutext.page va 0x%llx pa 0x%llx\n",
__func__, (uint64_t)va, (uint64_t)pa);
va += PAGE_SIZE;
pa += PAGE_SIZE;
}
/* .kudata section */
va = (vaddr_t)__kudata_start;
pa = (paddr_t)__kernel_kudata_phys;
while (va < (vaddr_t)__kudata_end) {
pmap_enter_special(va, pa, PROT_READ | PROT_WRITE);
DPRINTF("%s: entered kudata page va 0x%llx pa 0x%llx\n",
__func__, (uint64_t)va, (uint64_t)pa);
va += PAGE_SIZE;
pa += PAGE_SIZE;
}
}
/*
* Set up proc0's PCB and the cpu's TSS.
*/
void
x86_64_proc0_tss_ldt_init(void)
{
struct pcb *pcb;
cpu_info_primary.ci_curpcb = pcb = &proc0.p_addr->u_pcb;
pcb->pcb_fsbase = 0;
pcb->pcb_kstack = (u_int64_t)proc0.p_addr + USPACE - 16;
proc0.p_md.md_regs = (struct trapframe *)pcb->pcb_kstack - 1;
ltr(GSYSSEL(GPROC0_SEL, SEL_KPL));
lldt(0);
}
bios_diskinfo_t *
bios_getdiskinfo(dev_t dev)
{
bios_diskinfo_t *pdi;
if (bios_diskinfo == NULL)
return NULL;
for (pdi = bios_diskinfo; pdi->bios_number != -1; pdi++) {
if ((dev & B_MAGICMASK) == B_DEVMAGIC) { /* search by bootdev */
if (pdi->bsd_dev == dev)
break;
} else {
if (pdi->bios_number == dev)
break;
}
}
if (pdi->bios_number == -1)
return NULL;
else
return pdi;
}
int
bios_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
bios_diskinfo_t *pdi;
extern dev_t bootdev;
int biosdev;
/* all sysctl names at this level except diskinfo are terminal */
if (namelen != 1 && name[0] != BIOS_DISKINFO)
return (ENOTDIR); /* overloaded */
if (!(bootapiver & BAPIV_VECTOR))
return EOPNOTSUPP;
switch (name[0]) {
case BIOS_DEV:
if ((pdi = bios_getdiskinfo(bootdev)) == NULL)
return ENXIO;
biosdev = pdi->bios_number;
return sysctl_rdint(oldp, oldlenp, newp, biosdev);
case BIOS_DISKINFO:
if (namelen != 2)
return ENOTDIR;
if ((pdi = bios_getdiskinfo(name[1])) == NULL)
return ENXIO;
return sysctl_rdstruct(oldp, oldlenp, newp, pdi, sizeof(*pdi));
case BIOS_CKSUMLEN:
return sysctl_rdint(oldp, oldlenp, newp, bios_cksumlen);
default:
return EOPNOTSUPP;
}
/* NOTREACHED */
}
extern int tsc_is_invariant;
extern int amd64_has_xcrypt;
const struct sysctl_bounded_args cpuctl_vars[] = {
{ CPU_LIDACTION, &lid_action, 0, 2 },
{ CPU_PWRACTION, &pwr_action, 0, 2 },
{ CPU_CPUID, &cpu_id, SYSCTL_INT_READONLY },
{ CPU_CPUFEATURE, &cpu_feature, SYSCTL_INT_READONLY },
{ CPU_XCRYPT, &amd64_has_xcrypt, SYSCTL_INT_READONLY },
{ CPU_INVARIANTTSC, &tsc_is_invariant, SYSCTL_INT_READONLY },
};
/*
* machine dependent system variables.
*/
int
cpu_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
extern uint64_t tsc_frequency;
dev_t consdev;
dev_t dev;
switch (name[0]) {
case CPU_CONSDEV:
if (namelen != 1)
return (ENOTDIR); /* overloaded */
if (cn_tab != NULL)
consdev = cn_tab->cn_dev;
else
consdev = NODEV;
return (sysctl_rdstruct(oldp, oldlenp, newp, &consdev,
sizeof consdev));
case CPU_CHR2BLK:
if (namelen != 2)
return (ENOTDIR); /* overloaded */
dev = chrtoblk((dev_t)name[1]);
return sysctl_rdstruct(oldp, oldlenp, newp, &dev, sizeof(dev));
case CPU_BIOS:
return bios_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen, p);
case CPU_CPUVENDOR:
return (sysctl_rdstring(oldp, oldlenp, newp, cpu_vendor));
case CPU_KBDRESET:
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&kbd_reset));
case CPU_ALLOWAPERTURE:
if (namelen != 1)
return (ENOTDIR); /* overloaded */
#ifdef APERTURE
if (securelevel > 0)
return (sysctl_int_lower(oldp, oldlenp, newp, newlen,
&allowaperture));
else
return (sysctl_int(oldp, oldlenp, newp, newlen,
&allowaperture));
#else
return (sysctl_rdint(oldp, oldlenp, newp, 0));
#endif
#if NPCKBC > 0 && NUKBD > 0
case CPU_FORCEUKBD:
{
int error;
if (forceukbd)
return (sysctl_rdint(oldp, oldlenp, newp, forceukbd));
error = sysctl_int(oldp, oldlenp, newp, newlen, &forceukbd);
if (forceukbd)
pckbc_release_console();
return (error);
}
#endif
case CPU_TSCFREQ:
return (sysctl_rdquad(oldp, oldlenp, newp, tsc_frequency));
default:
return (sysctl_bounded_arr(cpuctl_vars, nitems(cpuctl_vars),
name, namelen, oldp, oldlenp, newp, newlen));
}
/* NOTREACHED */
}
/*
* Send an interrupt to process.
*
* Stack is set up to allow sigcode to call routine, followed by
* syscall to sigreturn routine below. After sigreturn resets the
* signal mask, the stack, and the frame pointer, it returns to the
* user specified pc.
*/
int
sendsig(sig_t catcher, int sig, sigset_t mask, const siginfo_t *ksip,
int info, int onstack)
{
struct proc *p = curproc;
struct trapframe *tf = p->p_md.md_regs;
struct sigcontext ksc;
struct savefpu *sfp = &p->p_addr->u_pcb.pcb_savefpu;
register_t sp, scp, sip;
u_long sss;
memset(&ksc, 0, sizeof ksc);
ksc.sc_rdi = tf->tf_rdi;
ksc.sc_rsi = tf->tf_rsi;
ksc.sc_rdx = tf->tf_rdx;
ksc.sc_rcx = tf->tf_rcx;
ksc.sc_r8 = tf->tf_r8;
ksc.sc_r9 = tf->tf_r9;
ksc.sc_r10 = tf->tf_r10;
ksc.sc_r11 = tf->tf_r11;
ksc.sc_r12 = tf->tf_r12;
ksc.sc_r13 = tf->tf_r13;
ksc.sc_r14 = tf->tf_r14;
ksc.sc_r15 = tf->tf_r15;
ksc.sc_rbx = tf->tf_rbx;
ksc.sc_rax = tf->tf_rax;
ksc.sc_rbp = tf->tf_rbp;
ksc.sc_rip = tf->tf_rip;
ksc.sc_cs = tf->tf_cs;
ksc.sc_rflags = tf->tf_rflags;
ksc.sc_rsp = tf->tf_rsp;
ksc.sc_ss = tf->tf_ss;
ksc.sc_mask = mask;
/* Allocate space for the signal handler context. */
if ((p->p_sigstk.ss_flags & SS_DISABLE) == 0 &&
!sigonstack(tf->tf_rsp) && onstack)
sp = trunc_page((vaddr_t)p->p_sigstk.ss_sp + p->p_sigstk.ss_size);
else
sp = tf->tf_rsp - 128;
sp &= ~15ULL; /* just in case */
sss = (sizeof(ksc) + 15) & ~15;
/* Save FPU state to PCB if necessary, then copy it out */
if (curcpu()->ci_pflags & CPUPF_USERXSTATE) {
curcpu()->ci_pflags &= ~CPUPF_USERXSTATE;
fpusavereset(&p->p_addr->u_pcb.pcb_savefpu);
}
sp -= fpu_save_len;
ksc.sc_fpstate = (struct fxsave64 *)sp;
if (copyout(sfp, (void *)sp, fpu_save_len))
return 1;
/* Now reset the FPU state in PCB */
memcpy(&p->p_addr->u_pcb.pcb_savefpu,
&proc0.p_addr->u_pcb.pcb_savefpu, fpu_save_len);
sip = 0;
if (info) {
sip = sp - ((sizeof(*ksip) + 15) & ~15);
sss += (sizeof(*ksip) + 15) & ~15;
if (copyout(ksip, (void *)sip, sizeof(*ksip)))
return 1;
}
scp = sp - sss;
ksc.sc_cookie = (long)scp ^ p->p_p->ps_sigcookie;
if (copyout(&ksc, (void *)scp, sizeof(ksc)))
return 1;
/*
* Build context to run handler in.
*/
tf->tf_rax = (u_int64_t)catcher;
tf->tf_rdi = sig;
tf->tf_rsi = sip;
tf->tf_rdx = scp;
tf->tf_rip = (u_int64_t)p->p_p->ps_sigcode;
tf->tf_cs = GSEL(GUCODE_SEL, SEL_UPL);
tf->tf_rflags &= ~(PSL_T|PSL_D|PSL_VM|PSL_AC);
tf->tf_rsp = scp;
tf->tf_ss = GSEL(GUDATA_SEL, SEL_UPL);
/* The reset state _is_ the userspace state for this thread now */
curcpu()->ci_pflags |= CPUPF_USERXSTATE;
return 0;
}
/*
* System call to cleanup state after a signal
* has been taken. Reset signal mask and
* stack state from context left by sendsig (above).
* Return to previous pc and psl as specified by
* context left by sendsig. Check carefully to
* make sure that the user has not modified the
* psl to gain improper privileges or to cause
* a machine fault.
*/
int
sys_sigreturn(struct proc *p, void *v, register_t *retval)
{
struct sys_sigreturn_args /* {
syscallarg(struct sigcontext *) sigcntxp;
} */ *uap = v;
struct sigcontext ksc, *scp = SCARG(uap, sigcntxp);
struct trapframe *tf = p->p_md.md_regs;
int error;
if (PROC_PC(p) != p->p_p->ps_sigcoderet) {
sigexit(p, SIGILL);
return (EPERM);
}
if ((error = copyin((caddr_t)scp, &ksc, sizeof ksc)))
return (error);
if (ksc.sc_cookie != ((long)scp ^ p->p_p->ps_sigcookie)) {
sigexit(p, SIGILL);
return (EFAULT);
}
/* Prevent reuse of the sigcontext cookie */
ksc.sc_cookie = 0;
(void)copyout(&ksc.sc_cookie, (caddr_t)scp +
offsetof(struct sigcontext, sc_cookie), sizeof (ksc.sc_cookie));
if (((ksc.sc_rflags ^ tf->tf_rflags) & PSL_USERSTATIC) != 0 ||
!USERMODE(ksc.sc_cs, ksc.sc_eflags))
return (EINVAL);
/* Current state is obsolete; toss it and force a reload */
if (curcpu()->ci_pflags & CPUPF_USERXSTATE) {
curcpu()->ci_pflags &= ~CPUPF_USERXSTATE;
fpureset();
}
/* Copy in the FPU state to restore */
if (__predict_true(ksc.sc_fpstate != NULL)) {
struct fxsave64 *fx = &p->p_addr->u_pcb.pcb_savefpu.fp_fxsave;
if ((error = copyin(ksc.sc_fpstate, fx, fpu_save_len)))
return (error);
fx->fx_mxcsr &= fpu_mxcsr_mask;
} else {
/* shouldn't happen, but handle it */
memcpy(&p->p_addr->u_pcb.pcb_savefpu,
&proc0.p_addr->u_pcb.pcb_savefpu, fpu_save_len);
}
tf->tf_rdi = ksc.sc_rdi;
tf->tf_rsi = ksc.sc_rsi;
tf->tf_rdx = ksc.sc_rdx;
tf->tf_rcx = ksc.sc_rcx;
tf->tf_r8 = ksc.sc_r8;
tf->tf_r9 = ksc.sc_r9;
tf->tf_r10 = ksc.sc_r10;
tf->tf_r11 = ksc.sc_r11;
tf->tf_r12 = ksc.sc_r12;
tf->tf_r13 = ksc.sc_r13;
tf->tf_r14 = ksc.sc_r14;
tf->tf_r15 = ksc.sc_r15;
tf->tf_rbx = ksc.sc_rbx;
tf->tf_rax = ksc.sc_rax;
tf->tf_rbp = ksc.sc_rbp;
tf->tf_rip = ksc.sc_rip;
tf->tf_cs = ksc.sc_cs;
tf->tf_rflags = ksc.sc_rflags;
tf->tf_rsp = ksc.sc_rsp;
tf->tf_ss = ksc.sc_ss;
/* Restore signal mask. */
p->p_sigmask = ksc.sc_mask & ~sigcantmask;
/*
* sigreturn() needs to return to userspace via the 'iretq'
* method, so that if the process was interrupted (by tick,
* an IPI, whatever) as opposed to already being in the kernel
* when a signal was being delivered, the process will be
* completely restored, including the userland %rcx and %r11
* registers which the 'sysretq' instruction cannot restore.
* Also need to make sure we can handle faulting on xrstor.
*/
p->p_md.md_flags |= MDP_IRET;
return (EJUSTRETURN);
}
#ifdef MULTIPROCESSOR
/* force a CPU into the kernel, whether or not it's idle */
void
cpu_kick(struct cpu_info *ci)
{
/* only need to kick other CPUs */
if (ci != curcpu()) {
if (cpu_mwait_size > 0) {
/*
* If not idling, then send an IPI, else
* just clear the "keep idling" bit.
*/
if ((ci->ci_mwait & MWAIT_IN_IDLE) == 0)
x86_send_ipi(ci, X86_IPI_NOP);
else
atomic_clearbits_int(&ci->ci_mwait,
MWAIT_KEEP_IDLING);
} else {
/* no mwait, so need an IPI */
x86_send_ipi(ci, X86_IPI_NOP);
}
}
}
#endif
/*
* Notify the current process (p) that it has a signal pending,
* process as soon as possible.
*/
void
signotify(struct proc *p)
{
aston(p);
cpu_kick(p->p_cpu);
}
#ifdef MULTIPROCESSOR
void
cpu_unidle(struct cpu_info *ci)
{
if (cpu_mwait_size > 0 && (ci->ci_mwait & MWAIT_ONLY)) {
/*
* Just clear the "keep idling" bit; if it wasn't
* idling then we didn't need to do anything anyway.
*/
atomic_clearbits_int(&ci->ci_mwait, MWAIT_KEEP_IDLING);
return;
}
if (ci != curcpu())
x86_send_ipi(ci, X86_IPI_NOP);
}
#endif
int waittime = -1;
struct pcb dumppcb;
__dead void
boot(int howto)
{
if ((howto & RB_POWERDOWN) != 0)
lid_action = 0;
if ((howto & RB_RESET) != 0)
goto doreset;
if (cold) {
if ((howto & RB_USERREQ) == 0)
howto |= RB_HALT;
goto haltsys;
}
boothowto = howto;
if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
waittime = 0;
vfs_shutdown(curproc);
if ((howto & RB_TIMEBAD) == 0) {
resettodr();
} else {
printf("WARNING: not updating battery clock\n");
}
}
if_downall();
uvm_shutdown();
splhigh();
cold = 1;
if ((howto & RB_DUMP) != 0)
dumpsys();
haltsys:
config_suspend_all(DVACT_POWERDOWN);
#ifdef MULTIPROCESSOR
x86_broadcast_ipi(X86_IPI_HALT);
#endif
if ((howto & RB_HALT) != 0) {
#if NACPI > 0 && !defined(SMALL_KERNEL)
extern int acpi_enabled;
if (acpi_enabled) {
delay(500000);
if ((howto & RB_POWERDOWN) != 0)
acpi_powerdown();
}
#endif
printf("\n");
printf("The operating system has halted.\n");
printf("Please press any key to reboot.\n\n");
cnpollc(1); /* for proper keyboard command handling */
cngetc();
cnpollc(0);
}
doreset:
printf("rebooting...\n");
if (cpureset_delay > 0)
delay(cpureset_delay * 1000);
cpu_reset();
for (;;)
continue;
/* NOTREACHED */
}
/*
* These variables are needed by /sbin/savecore
*/
u_long dumpmag = 0x8fca0101; /* magic number */
int dumpsize = 0; /* pages */
long dumplo = 0; /* blocks */
/*
* cpu_dump: dump the machine-dependent kernel core dump headers.
*/
int
cpu_dump(void)
{
int (*dump)(dev_t, daddr_t, caddr_t, size_t);
char buf[dbtob(1)];
kcore_seg_t *segp;
cpu_kcore_hdr_t *cpuhdrp;
phys_ram_seg_t *memsegp;
caddr_t va;
int i;
dump = bdevsw[major(dumpdev)].d_dump;
memset(buf, 0, sizeof buf);
segp = (kcore_seg_t *)buf;
cpuhdrp = (cpu_kcore_hdr_t *)&buf[ALIGN(sizeof(*segp))];
memsegp = (phys_ram_seg_t *)&buf[ALIGN(sizeof(*segp)) +
ALIGN(sizeof(*cpuhdrp))];
/*
* Generate a segment header.
*/
CORE_SETMAGIC(*segp, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
segp->c_size = dbtob(1) - ALIGN(sizeof(*segp));
/*
* Add the machine-dependent header info.
*/
cpuhdrp->ptdpaddr = proc0.p_addr->u_pcb.pcb_cr3;
cpuhdrp->nmemsegs = mem_cluster_cnt;
/*
* Fill in the memory segment descriptors.
*/
for (i = 0; i < mem_cluster_cnt; i++) {
memsegp[i].start = mem_clusters[i].start;
memsegp[i].size = mem_clusters[i].size & ~PAGE_MASK;
}
/*
* If we have dump memory then assume the kernel stack is in high
* memory and bounce
*/
if (dumpmem_vaddr != 0) {
memcpy((char *)dumpmem_vaddr, buf, sizeof(buf));
va = (caddr_t)dumpmem_vaddr;
} else {
va = (caddr_t)buf;
}
return (dump(dumpdev, dumplo, va, dbtob(1)));
}
/*
* This is called by main to set dumplo and dumpsize.
* Dumps always skip the first PAGE_SIZE of disk space
* in case there might be a disk label stored there.
* If there is extra space, put dump at the end to
* reduce the chance that swapping trashes it.
*/
void
dumpconf(void)
{
int nblks, dumpblks; /* size of dump area */
if (dumpdev == NODEV ||
(nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0)
return;
if (nblks <= ctod(1))
return;
dumpblks = cpu_dumpsize();
if (dumpblks < 0)
return;
dumpblks += ctod(cpu_dump_mempagecnt());
/* If dump won't fit (incl. room for possible label), punt. */
if (dumpblks > (nblks - ctod(1)))
return;
/* Put dump at end of partition */
dumplo = nblks - dumpblks;
/* dumpsize is in page units, and doesn't include headers. */
dumpsize = cpu_dump_mempagecnt();
}
/*
* Doadump comes here after turning off memory management and
* getting on the dump stack, either when called above, or by
* the auto-restart code.
*/
#define BYTES_PER_DUMP MAXPHYS /* must be a multiple of pagesize */
void
dumpsys(void)
{
u_long totalbytesleft, bytes, i, n, memseg;
u_long maddr;
daddr_t blkno;
void *va;
int (*dump)(dev_t, daddr_t, caddr_t, size_t);
int error;
/* Save registers. */
savectx(&dumppcb);
if (dumpdev == NODEV)
return;
/*
* For dumps during autoconfiguration,
* if dump device has already configured...
*/
if (dumpsize == 0)
dumpconf();
if (dumplo <= 0 || dumpsize == 0) {
printf("\ndump to dev %u,%u not possible\n", major(dumpdev),
minor(dumpdev));
return;
}
printf("\ndumping to dev %u,%u offset %ld\n", major(dumpdev),
minor(dumpdev), dumplo);
error = (*bdevsw[major(dumpdev)].d_psize)(dumpdev);
printf("dump ");
if (error == -1) {
printf("area unavailable\n");
return;
}
if ((error = cpu_dump()) != 0)
goto err;
totalbytesleft = ptoa(cpu_dump_mempagecnt());
blkno = dumplo + cpu_dumpsize();
dump = bdevsw[major(dumpdev)].d_dump;
error = 0;
for (memseg = 0; memseg < mem_cluster_cnt; memseg++) {
maddr = mem_clusters[memseg].start;
bytes = mem_clusters[memseg].size;
for (i = 0; i < bytes; i += n, totalbytesleft -= n) {
/* Print out how many MBs we have left to go. */
if ((totalbytesleft % (1024*1024)) < BYTES_PER_DUMP)
printf("%ld ", totalbytesleft / (1024 * 1024));
/* Limit size for next transfer. */
n = bytes - i;
if (n > BYTES_PER_DUMP)
n = BYTES_PER_DUMP;
if (maddr > 0xffffffff) {
va = (void *)dumpmem_vaddr;
if (n > dumpmem_sz)
n = dumpmem_sz;
memcpy(va, (void *)PMAP_DIRECT_MAP(maddr), n);
} else {
va = (void *)PMAP_DIRECT_MAP(maddr);
}
error = (*dump)(dumpdev, blkno, va, n);
if (error)
goto err;
maddr += n;
blkno += btodb(n); /* XXX? */
#if 0 /* XXX this doesn't work. grr. */
/* operator aborting dump? */
if (sget() != NULL) {
error = EINTR;
break;
}
#endif
}
}
err:
switch (error) {
case ENXIO:
printf("device bad\n");
break;
case EFAULT:
printf("device not ready\n");
break;
case EINVAL:
printf("area improper\n");
break;
case EIO:
printf("i/o error\n");
break;
case EINTR:
printf("aborted from console\n");
break;
case 0:
printf("succeeded\n");
break;
default:
printf("error %d\n", error);
break;
}
printf("\n\n");
delay(5000000); /* 5 seconds */
}
/*
* Force the userspace FS.base to be reloaded from the PCB on return from
* the kernel, and reset the segment registers (%ds, %es, %fs, and %gs)
* to their expected userspace value.
*/
void
reset_segs(void)
{
/*
* This operates like the cpu_switchto() sequence: if we
* haven't reset %[defg]s already, do so now.
*/
if (curcpu()->ci_pflags & CPUPF_USERSEGS) {
curcpu()->ci_pflags &= ~CPUPF_USERSEGS;
__asm volatile(
"movw %%ax,%%ds\n\t"
"movw %%ax,%%es\n\t"
"movw %%ax,%%fs\n\t"
"cli\n\t" /* block intr when on user GS.base */
"swapgs\n\t" /* swap from kernel to user GS.base */
"movw %%ax,%%gs\n\t"/* set %gs to UDATA and GS.base to 0 */
"swapgs\n\t" /* back to kernel GS.base */
"sti" : : "a"(GSEL(GUDATA_SEL, SEL_UPL)));
}
}
/*
* Clear registers on exec
*/
void
setregs(struct proc *p, struct exec_package *pack, u_long stack,
register_t *retval)
{
struct trapframe *tf;
/* Reset FPU state in PCB */
memcpy(&p->p_addr->u_pcb.pcb_savefpu,
&proc0.p_addr->u_pcb.pcb_savefpu, fpu_save_len);
if (curcpu()->ci_pflags & CPUPF_USERXSTATE) {
/* state in CPU is obsolete; reset it */
fpureset();
} else {
/* the reset state _is_ the userspace state now */
curcpu()->ci_pflags |= CPUPF_USERXSTATE;
}
/* To reset all registers we have to return via iretq */
p->p_md.md_flags |= MDP_IRET;
reset_segs();
p->p_addr->u_pcb.pcb_fsbase = 0;
tf = p->p_md.md_regs;
tf->tf_rdi = 0;
tf->tf_rsi = 0;
tf->tf_rbp = 0;
tf->tf_rbx = 0;
tf->tf_rdx = 0;
tf->tf_rcx = 0;
tf->tf_rax = 0;
tf->tf_r8 = 0;
tf->tf_r9 = 0;
tf->tf_r10 = 0;
tf->tf_r11 = 0;
tf->tf_r12 = 0;
tf->tf_r13 = 0;
tf->tf_r14 = 0;
tf->tf_r15 = 0;
tf->tf_rip = pack->ep_entry;
tf->tf_cs = GSEL(GUCODE_SEL, SEL_UPL);
tf->tf_rflags = PSL_USERSET;
tf->tf_rsp = stack;
tf->tf_ss = GSEL(GUDATA_SEL, SEL_UPL);
retval[1] = 0;
}
/*
* Initialize segments and descriptor tables
*/
struct gate_descriptor *idt;
char idt_allocmap[NIDT];
extern struct user *proc0paddr;
void
setgate(struct gate_descriptor *gd, void *func, int ist, int type, int dpl,
int sel)
{
gd->gd_looffset = (u_int64_t)func & 0xffff;
gd->gd_selector = sel;
gd->gd_ist = ist;
gd->gd_type = type;
gd->gd_dpl = dpl;
gd->gd_p = 1;
gd->gd_hioffset = (u_int64_t)func >> 16;
gd->gd_zero = 0;
gd->gd_xx1 = 0;
gd->gd_xx2 = 0;
gd->gd_xx3 = 0;
}
void
unsetgate(struct gate_descriptor *gd)
{
memset(gd, 0, sizeof (*gd));
}
void
setregion(struct region_descriptor *rd, void *base, u_int16_t limit)
{
rd->rd_limit = limit;
rd->rd_base = (u_int64_t)base;
}
/*
* Note that the base and limit fields are ignored in long mode.
*/
void
set_mem_segment(struct mem_segment_descriptor *sd, void *base, size_t limit,
int type, int dpl, int gran, int def32, int is64)
{
sd->sd_lolimit = (unsigned)limit;
sd->sd_lobase = (unsigned long)base;
sd->sd_type = type;
sd->sd_dpl = dpl;
sd->sd_p = 1;
sd->sd_hilimit = (unsigned)limit >> 16;
sd->sd_avl = 0;
sd->sd_long = is64;
sd->sd_def32 = def32;
sd->sd_gran = gran;
sd->sd_hibase = (unsigned long)base >> 24;
}
void
set_sys_segment(struct sys_segment_descriptor *sd, void *base, size_t limit,
int type, int dpl, int gran)
{
memset(sd, 0, sizeof *sd);
sd->sd_lolimit = (unsigned)limit;
sd->sd_lobase = (u_int64_t)base;
sd->sd_type = type;
sd->sd_dpl = dpl;
sd->sd_p = 1;
sd->sd_hilimit = (unsigned)limit >> 16;
sd->sd_gran = gran;
sd->sd_hibase = (u_int64_t)base >> 24;
}
void cpu_init_idt(void)
{
struct region_descriptor region;
setregion(®ion, idt, NIDT * sizeof(idt[0]) - 1);
lidt(®ion);
}
void
cpu_init_extents(void)
{
extern struct extent *iomem_ex;
static int already_done;
int i;
/* We get called for each CPU, only first should do this */
if (already_done)
return;
/*
* Allocate the physical addresses used by RAM from the iomem
* extent map.
*/
for (i = 0; i < mem_cluster_cnt; i++) {
if (extent_alloc_region(iomem_ex, mem_clusters[i].start,
mem_clusters[i].size, EX_NOWAIT)) {
/* XXX What should we do? */
printf("WARNING: CAN'T ALLOCATE RAM (%llx-%llx)"
" FROM IOMEM EXTENT MAP!\n", mem_clusters[i].start,
mem_clusters[i].start + mem_clusters[i].size - 1);
}
}
already_done = 1;
}
void
map_tramps(void)
{
#if defined(MULTIPROCESSOR) || \
(NACPI > 0 && !defined(SMALL_KERNEL))
struct pmap *kmp = pmap_kernel();
extern paddr_t tramp_pdirpa;
#ifdef MULTIPROCESSOR
extern u_char cpu_spinup_trampoline[];
extern u_char cpu_spinup_trampoline_end[];
extern u_char mp_tramp_data_start[];
extern u_char mp_tramp_data_end[];
extern u_int32_t mp_pdirpa;
#endif
/*
* The initial PML4 pointer must be below 4G, so if the
* current one isn't, use a "bounce buffer" and save it
* for tramps to use.
*/
if (kmp->pm_pdirpa > 0xffffffff) {
pmap_kenter_pa(lo32_vaddr, lo32_paddr, PROT_READ | PROT_WRITE);
memcpy((void *)lo32_vaddr, kmp->pm_pdir, PAGE_SIZE);
tramp_pdirpa = lo32_paddr;
pmap_kremove(lo32_vaddr, PAGE_SIZE);
} else
tramp_pdirpa = kmp->pm_pdirpa;
#ifdef MULTIPROCESSOR
/* Map MP tramp code and data pages RW for copy */
pmap_kenter_pa(MP_TRAMPOLINE, MP_TRAMPOLINE,
PROT_READ | PROT_WRITE);
pmap_kenter_pa(MP_TRAMP_DATA, MP_TRAMP_DATA,
PROT_READ | PROT_WRITE);
memset((caddr_t)MP_TRAMPOLINE, 0xcc, PAGE_SIZE);
memset((caddr_t)MP_TRAMP_DATA, 0xcc, PAGE_SIZE);
memcpy((caddr_t)MP_TRAMPOLINE,
cpu_spinup_trampoline,
cpu_spinup_trampoline_end-cpu_spinup_trampoline);
memcpy((caddr_t)MP_TRAMP_DATA,
mp_tramp_data_start,
mp_tramp_data_end - mp_tramp_data_start);
/*
* We need to patch this after we copy the tramp data,
* the symbol points into the copied tramp data page.
*/
mp_pdirpa = tramp_pdirpa;
/* Unmap, will be remapped in cpu_start_secondary */
pmap_kremove(MP_TRAMPOLINE, PAGE_SIZE);
pmap_kremove(MP_TRAMP_DATA, PAGE_SIZE);
#endif /* MULTIPROCESSOR */
#endif
}
#define IDTVEC(name) __CONCAT(X, name)
typedef void (vector)(void);
extern vector *IDTVEC(exceptions)[];
paddr_t early_pte_pages;
void
init_x86_64(paddr_t first_avail)
{
struct region_descriptor region;
bios_memmap_t *bmp;
int x, ist;
uint64_t max_dm_size = ((uint64_t)512 * NUM_L4_SLOT_DIRECT) << 30;
/*
* locore0 mapped 3 pages for use before the pmap is initialized
* starting at first_avail. These pages are currently used by
* efifb to create early-use VAs for the framebuffer before efifb
* is attached.
*/
early_pte_pages = first_avail;
first_avail += 3 * NBPG;
cpu_init_msrs(&cpu_info_primary);
proc0.p_addr = proc0paddr;
cpu_info_primary.ci_curpcb = &proc0.p_addr->u_pcb;
x86_bus_space_init();
i8254_startclock();
/*
* Initialize PAGE_SIZE-dependent variables.
*/
uvm_setpagesize();
/*
* Boot arguments are in a single page specified by /boot.
*
* We require the "new" vector form, as well as memory ranges
* to be given in bytes rather than KB.
*
* locore copies the data into bootinfo[] for us.
*/
if ((bootapiver & (BAPIV_VECTOR | BAPIV_BMEMMAP)) ==
(BAPIV_VECTOR | BAPIV_BMEMMAP)) {
if (bootinfo_size >= sizeof(bootinfo))
panic("boot args too big");
getbootinfo(bootinfo, bootinfo_size);
} else
panic("invalid /boot");
cninit();
/*
* Memory on the AMD64 port is described by three different things.
*
* 1. biosbasemem - This is outdated, and should really only be used to
* sanitize the other values. This is what we get back from the BIOS
* using the legacy routines, describing memory below 640KB.
*
* 2. bios_memmap[] - This is the memory map as the bios has returned
* it to us. It includes memory the kernel occupies, etc.
*
* 3. mem_cluster[] - This is the massaged free memory segments after
* taking into account the contents of bios_memmap, biosbasemem,
* and locore/machdep/pmap kernel allocations of physical
* pages.
*
* The other thing is that the physical page *RANGE* is described by
* three more variables:
*
* avail_start - This is a physical address of the start of available
* pages, until IOM_BEGIN. This is basically the start
* of the UVM managed range of memory, with some holes...
*
* avail_end - This is the end of physical pages. All physical pages
* that UVM manages are between avail_start and avail_end.
* There are holes...
*
* first_avail - This is the first available physical page after the
* kernel, page tables, etc.
*
* We skip the first few pages for trampolines, hibernate, and to avoid
* buggy SMI implementations that could corrupt the first 64KB.
*/
avail_start = 16*PAGE_SIZE;
#ifdef MULTIPROCESSOR
if (avail_start < MP_TRAMPOLINE + PAGE_SIZE)
avail_start = MP_TRAMPOLINE + PAGE_SIZE;
if (avail_start < MP_TRAMP_DATA + PAGE_SIZE)
avail_start = MP_TRAMP_DATA + PAGE_SIZE;
#endif
#if (NACPI > 0 && !defined(SMALL_KERNEL))
if (avail_start < ACPI_TRAMPOLINE + PAGE_SIZE)
avail_start = ACPI_TRAMPOLINE + PAGE_SIZE;
if (avail_start < ACPI_TRAMP_DATA + PAGE_SIZE)
avail_start = ACPI_TRAMP_DATA + PAGE_SIZE;
#endif
#ifdef HIBERNATE
if (avail_start < HIBERNATE_HIBALLOC_PAGE + PAGE_SIZE)
avail_start = HIBERNATE_HIBALLOC_PAGE + PAGE_SIZE;
#endif /* HIBERNATE */
/*
* We need to go through the BIOS memory map given, and
* fill out mem_clusters and mem_cluster_cnt stuff, taking
* into account all the points listed above.
*/
avail_end = mem_cluster_cnt = 0;
for (bmp = bios_memmap; bmp->type != BIOS_MAP_END; bmp++) {
paddr_t s1, s2, e1, e2;
/* Ignore non-free memory */
if (bmp->type != BIOS_MAP_FREE)
continue;
if (bmp->size < PAGE_SIZE)
continue;
/* Init our segment(s), round/trunc to pages */
s1 = round_page(bmp->addr);
e1 = trunc_page(bmp->addr + bmp->size);
s2 = e2 = 0;
/*
* XXX Some buggy ACPI BIOSes use memory that they
* declare as free. Current worst offender is
* Supermicro 5019D-FTN4. Typically the affected memory
* areas are small blocks between areas reserved for
* ACPI and other BIOS goo. So skip areas smaller
* than 32 MB above the 16 MB boundary (to avoid
* affecting legacy stuff).
*/
if (s1 > 16*1024*1024 && (e1 - s1) < 32*1024*1024)
continue;
/* Check and adjust our segment(s) */
/* Nuke low pages */
if (s1 < avail_start) {
s1 = avail_start;
if (s1 > e1)
continue;
}
/*
* The direct map is limited to 512GB * NUM_L4_SLOT_DIRECT of
* memory, so discard anything above that.
*/
if (e1 >= max_dm_size) {
e1 = max_dm_size;
if (s1 > e1)
continue;
}
/* Crop stuff into "640K hole" */
if (s1 < IOM_BEGIN && e1 > IOM_BEGIN)
e1 = IOM_BEGIN;
if (s1 < biosbasemem && e1 > biosbasemem)
e1 = biosbasemem;
/* Split any segments straddling the 16MB boundary */
if (s1 < 16*1024*1024 && e1 > 16*1024*1024) {
e2 = e1;
s2 = e1 = 16*1024*1024;
}
/* Store segment(s) */
if (e1 - s1 >= PAGE_SIZE) {
mem_clusters[mem_cluster_cnt].start = s1;
mem_clusters[mem_cluster_cnt].size = e1 - s1;
mem_cluster_cnt++;
}
if (e2 - s2 >= PAGE_SIZE) {
mem_clusters[mem_cluster_cnt].start = s2;
mem_clusters[mem_cluster_cnt].size = e2 - s2;
mem_cluster_cnt++;
}
if (avail_end < e1) avail_end = e1;
if (avail_end < e2) avail_end = e2;
}
/*
* Call pmap initialization to make new kernel address space.
* We must do this before loading pages into the VM system.
*/
first_avail = pmap_bootstrap(first_avail, trunc_page(avail_end));
/* Allocate these out of the 640KB base memory */
if (avail_start != PAGE_SIZE)
avail_start = pmap_prealloc_lowmem_ptps(avail_start);
cpu_init_extents();
/* Make sure the end of the space used by the kernel is rounded. */
first_avail = round_page(first_avail);
kern_end = KERNBASE + first_avail;
/*
* Now, load the memory clusters (which have already been
* flensed) into the VM system.
*/
for (x = 0; x < mem_cluster_cnt; x++) {
paddr_t seg_start = mem_clusters[x].start;
paddr_t seg_end = seg_start + mem_clusters[x].size;
if (seg_start < first_avail) seg_start = first_avail;
if (seg_start > seg_end) continue;
if (seg_end - seg_start < PAGE_SIZE) continue;
physmem += atop(mem_clusters[x].size);
#if DEBUG_MEMLOAD
printf("loading 0x%lx-0x%lx (0x%lx-0x%lx)\n",
seg_start, seg_end, atop(seg_start), atop(seg_end));
#endif
uvm_page_physload(atop(seg_start), atop(seg_end),
atop(seg_start), atop(seg_end), 0);
}
/*
* Now, load the memory between the end of I/O memory "hole"
* and the kernel.
*/
{
paddr_t seg_start = round_page(IOM_END);
paddr_t seg_end = trunc_page(KERNTEXTOFF - KERNBASE);
if (seg_start < seg_end) {
#if DEBUG_MEMLOAD
printf("loading 0x%lx-0x%lx\n", seg_start, seg_end);
#endif
uvm_page_physload(atop(seg_start), atop(seg_end),
atop(seg_start), atop(seg_end), 0);
}
}
#if DEBUG_MEMLOAD
printf("avail_start = 0x%lx\n", avail_start);
printf("avail_end = 0x%lx\n", avail_end);
printf("first_avail = 0x%lx\n", first_avail);
#endif
/*
* Steal memory for the message buffer (at end of core).
*/
{
struct vm_physseg *vps = NULL;
psize_t sz = round_page(MSGBUFSIZE);
psize_t reqsz = sz;
for (x = 0; x < vm_nphysseg; x++) {
vps = &vm_physmem[x];
if (ptoa(vps->avail_end) == avail_end)
break;
}
if (x == vm_nphysseg)
panic("init_x86_64: can't find end of memory");
/* Shrink so it'll fit in the last segment. */
if ((vps->avail_end - vps->avail_start) < atop(sz))
sz = ptoa(vps->avail_end - vps->avail_start);
vps->avail_end -= atop(sz);
vps->end -= atop(sz);
msgbuf_paddr = ptoa(vps->avail_end);
/* Remove the last segment if it now has no pages. */
if (vps->start == vps->end) {
for (vm_nphysseg--; x < vm_nphysseg; x++)
vm_physmem[x] = vm_physmem[x + 1];
}
/* Now find where the new avail_end is. */
for (avail_end = 0, x = 0; x < vm_nphysseg; x++)
if (vm_physmem[x].avail_end > avail_end)
avail_end = vm_physmem[x].avail_end;
avail_end = ptoa(avail_end);
/* Warn if the message buffer had to be shrunk. */
if (sz != reqsz)
printf("WARNING: %ld bytes not available for msgbuf "
"in last cluster (%ld used)\n", reqsz, sz);
}
/*
* Steal some memory for a dump bouncebuffer if we have memory over
* the 32-bit barrier.
*/
if (avail_end > 0xffffffff) {
struct vm_physseg *vps = NULL;
psize_t sz = round_page(MAX(BYTES_PER_DUMP, dbtob(1)));
/* XXX assumes segments are ordered */
for (x = 0; x < vm_nphysseg; x++) {
vps = &vm_physmem[x];
/* Find something between 16meg and 4gig */
if (ptoa(vps->avail_end) <= 0xffffffff &&
ptoa(vps->avail_start) >= 0xffffff)
break;
}
if (x == vm_nphysseg)
panic("init_x86_64: no memory between "
"0xffffff-0xffffffff");
/* Shrink so it'll fit in the segment. */
if ((vps->avail_end - vps->avail_start) < atop(sz))
sz = ptoa(vps->avail_end - vps->avail_start);
vps->avail_end -= atop(sz);
vps->end -= atop(sz);
dumpmem_paddr = ptoa(vps->avail_end);
dumpmem_vaddr = PMAP_DIRECT_MAP(dumpmem_paddr);
dumpmem_sz = sz;
/* Remove the last segment if it now has no pages. */
if (vps->start == vps->end) {
for (vm_nphysseg--; x < vm_nphysseg; x++)
vm_physmem[x] = vm_physmem[x + 1];
}
}
pmap_growkernel(VM_MIN_KERNEL_ADDRESS + 32 * 1024 * 1024);
pmap_kenter_pa(idt_vaddr, idt_paddr, PROT_READ | PROT_WRITE);
idt = (struct gate_descriptor *)idt_vaddr;
cpu_info_primary.ci_tss = &cpu_info_full_primary.cif_tss;
cpu_info_primary.ci_gdt = &cpu_info_full_primary.cif_gdt;
/* make gdt gates and memory segments */
set_mem_segment(GDT_ADDR_MEM(cpu_info_primary.ci_gdt, GCODE_SEL), 0,
0xfffff, SDT_MEMERA, SEL_KPL, 1, 0, 1);
set_mem_segment(GDT_ADDR_MEM(cpu_info_primary.ci_gdt, GDATA_SEL), 0,
0xfffff, SDT_MEMRWA, SEL_KPL, 1, 0, 1);
set_mem_segment(GDT_ADDR_MEM(cpu_info_primary.ci_gdt, GUCODE32_SEL), 0,
atop(VM_MAXUSER_ADDRESS32) - 1, SDT_MEMERA, SEL_UPL, 1, 1, 0);
set_mem_segment(GDT_ADDR_MEM(cpu_info_primary.ci_gdt, GUDATA_SEL), 0,
atop(VM_MAXUSER_ADDRESS) - 1, SDT_MEMRWA, SEL_UPL, 1, 0, 1);
set_mem_segment(GDT_ADDR_MEM(cpu_info_primary.ci_gdt, GUCODE_SEL), 0,
atop(VM_MAXUSER_ADDRESS) - 1, SDT_MEMERA, SEL_UPL, 1, 0, 1);
set_sys_segment(GDT_ADDR_SYS(cpu_info_primary.ci_gdt, GPROC0_SEL),
cpu_info_primary.ci_tss, sizeof (struct x86_64_tss)-1,
SDT_SYS386TSS, SEL_KPL, 0);
/* exceptions */
for (x = 0; x < 32; x++) {
/* trap2 == NMI, trap8 == double fault */
ist = (x == 2) ? 2 : (x == 8) ? 1 : 0;
setgate(&idt[x], IDTVEC(exceptions)[x], ist, SDT_SYS386IGT,
(x == 3) ? SEL_UPL : SEL_KPL,
GSEL(GCODE_SEL, SEL_KPL));
idt_allocmap[x] = 1;
}
setregion(®ion, cpu_info_primary.ci_gdt, GDT_SIZE - 1);
lgdt(®ion);
cpu_init_idt();
intr_default_setup();
fpuinit(&cpu_info_primary);
softintr_init();
splraise(IPL_IPI);
intr_enable();
#ifdef DDB
db_machine_init();
ddb_init();
if (boothowto & RB_KDB)
db_enter();
#endif
}
void
cpu_reset(void)
{
intr_disable();
if (cpuresetfn)
(*cpuresetfn)();
/*
* The keyboard controller has 4 random output pins, one of which is
* connected to the RESET pin on the CPU in many PCs. We tell the
* keyboard controller to pulse this line a couple of times.
*/
outb(IO_KBD + KBCMDP, KBC_PULSE0);
delay(100000);
outb(IO_KBD + KBCMDP, KBC_PULSE0);
delay(100000);
/*
* Try to cause a triple fault and watchdog reset by making the IDT
* invalid and causing a fault.
*/
memset((caddr_t)idt, 0, NIDT * sizeof(idt[0]));
__asm volatile("divl %0,%1" : : "q" (0), "a" (0));
for (;;)
continue;
/* NOTREACHED */
}
/*
* cpu_dumpsize: calculate size of machine-dependent kernel core dump headers.
*/
int
cpu_dumpsize(void)
{
int size;
size = ALIGN(sizeof(kcore_seg_t)) +
ALIGN(mem_cluster_cnt * sizeof(phys_ram_seg_t));
if (roundup(size, dbtob(1)) != dbtob(1))
return (-1);
return (1);
}
/*
* cpu_dump_mempagecnt: calculate the size of RAM (in pages) to be dumped.
*/
u_long
cpu_dump_mempagecnt(void)
{
u_long i, n;
n = 0;
for (i = 0; i < mem_cluster_cnt; i++)
n += atop(mem_clusters[i].size);
return (n);
}
/*
* Figure out which portions of memory are used by the kernel/system.
*/
int
amd64_pa_used(paddr_t addr)
{
struct vm_page *pg;
/* Kernel manages these */
if ((pg = PHYS_TO_VM_PAGE(addr)) && (pg->pg_flags & PG_DEV) == 0)
return 1;
/* Kernel is loaded here */
if (addr > IOM_END && addr < (kern_end - KERNBASE))
return 1;
/* Low memory used for various bootstrap things */
if (addr < avail_start)
return 1;
/*
* The only regions I can think of that are left are the things
* we steal away from UVM. The message buffer?
* XXX - ignore these for now.
*/
return 0;
}
void
cpu_initclocks(void)
{
(*initclock_func)();
}
void
need_resched(struct cpu_info *ci)
{
ci->ci_want_resched = 1;
/* There's a risk we'll be called before the idle threads start */
if (ci->ci_curproc) {
aston(ci->ci_curproc);
cpu_kick(ci);
}
}
/*
* Allocate an IDT vector slot within the given range.
* XXX needs locking to avoid MP allocation races.
*/
int
idt_vec_alloc(int low, int high)
{
int vec;
for (vec = low; vec <= high; vec++) {
if (idt_allocmap[vec] == 0) {
idt_allocmap[vec] = 1;
return vec;
}
}
return 0;
}
void
idt_vec_set(int vec, void (*function)(void))
{
/*
* Vector should be allocated, so no locking needed.
*/
KASSERT(idt_allocmap[vec] == 1);
setgate(&idt[vec], function, 0, SDT_SYS386IGT, SEL_KPL,
GSEL(GCODE_SEL, SEL_KPL));
}
void
idt_vec_free(int vec)
{
unsetgate(&idt[vec]);
idt_allocmap[vec] = 0;
}
#ifdef DIAGNOSTIC
void
splassert_check(int wantipl, const char *func)
{
int cpl = curcpu()->ci_ilevel;
int floor = curcpu()->ci_handled_intr_level;
if (cpl < wantipl) {
splassert_fail(wantipl, cpl, func);
}
if (floor > wantipl) {
splassert_fail(wantipl, floor, func);
}
}
#endif
int
copyin32(const uint32_t *uaddr, uint32_t *kaddr)
{
if ((vaddr_t)uaddr & 0x3)
return EFAULT;
/* copyin(9) is atomic */
return copyin(uaddr, kaddr, sizeof(uint32_t));
}
void
getbootinfo(char *bootinfo, int bootinfo_size)
{
bootarg32_t *q;
bios_ddb_t *bios_ddb;
bios_bootduid_t *bios_bootduid;
bios_bootsr_t *bios_bootsr;
#undef BOOTINFO_DEBUG
#ifdef BOOTINFO_DEBUG
printf("bootargv:");
#endif
for (q = (bootarg32_t *)bootinfo;
(q->ba_type != BOOTARG_END) &&
((((char *)q) - bootinfo) < bootinfo_size);
q = (bootarg32_t *)(((char *)q) + q->ba_size)) {
switch (q->ba_type) {
case BOOTARG_MEMMAP:
bios_memmap = (bios_memmap_t *)q->ba_arg;
#ifdef BOOTINFO_DEBUG
printf(" memmap %p", bios_memmap);
#endif
break;
case BOOTARG_DISKINFO:
bios_diskinfo = (bios_diskinfo_t *)q->ba_arg;
#ifdef BOOTINFO_DEBUG
printf(" diskinfo %p", bios_diskinfo);
#endif
break;
case BOOTARG_APMINFO:
/* generated by i386 boot loader */
break;
case BOOTARG_CKSUMLEN:
bios_cksumlen = *(u_int32_t *)q->ba_arg;
#ifdef BOOTINFO_DEBUG
printf(" cksumlen %d", bios_cksumlen);
#endif
break;
case BOOTARG_PCIINFO:
/* generated by i386 boot loader */
break;
case BOOTARG_CONSDEV:
if (q->ba_size > sizeof(bios_oconsdev_t) +
offsetof(struct _boot_args32, ba_arg)) {
#if NCOM > 0
bios_consdev_t *cdp =
(bios_consdev_t*)q->ba_arg;
static const int ports[] =
{ 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
int unit = minor(cdp->consdev);
uint64_t consaddr = cdp->consaddr;
if (consaddr == -1 && unit >= 0 &&
unit < nitems(ports))
consaddr = ports[unit];
if (major(cdp->consdev) == 8 &&
consaddr != -1) {
comconsunit = unit;
comconsaddr = consaddr;
comconsrate = cdp->conspeed;
comconsfreq = cdp->consfreq;
comcons_reg_width = cdp->reg_width;
comcons_reg_shift = cdp->reg_shift;
if (cdp->flags & BCD_MMIO)
comconsiot = X86_BUS_SPACE_MEM;
else
comconsiot = X86_BUS_SPACE_IO;
}
#endif
#ifdef BOOTINFO_DEBUG
printf(" console 0x%x:%d",
cdp->consdev, cdp->conspeed);
#endif
} else {
#if NCOM > 0
bios_oconsdev_t *cdp =
(bios_oconsdev_t*)q->ba_arg;
static const int ports[] =
{ 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
int unit = minor(cdp->consdev);
int consaddr = cdp->consaddr;
if (consaddr == -1 && unit >= 0 &&
unit < nitems(ports))
consaddr = ports[unit];
if (major(cdp->consdev) == 8 &&
consaddr != -1) {
comconsunit = unit;
comconsaddr = consaddr;
comconsrate = cdp->conspeed;
comconsiot = X86_BUS_SPACE_IO;
}
#endif
#ifdef BOOTINFO_DEBUG
printf(" console 0x%x:%d",
cdp->consdev, cdp->conspeed);
#endif
}
break;
case BOOTARG_BOOTMAC:
bios_bootmac = (bios_bootmac_t *)q->ba_arg;
break;
case BOOTARG_DDB:
bios_ddb = (bios_ddb_t *)q->ba_arg;
#ifdef DDB
db_console = bios_ddb->db_console;
#endif
break;
case BOOTARG_BOOTDUID:
bios_bootduid = (bios_bootduid_t *)q->ba_arg;
memcpy(bootduid, bios_bootduid, sizeof(bootduid));
break;
case BOOTARG_BOOTSR:
bios_bootsr = (bios_bootsr_t *)q->ba_arg;
#if NSOFTRAID > 0
memcpy(&sr_bootuuid, &bios_bootsr->uuid,
sizeof(sr_bootuuid));
memcpy(&sr_bootkey, &bios_bootsr->maskkey,
sizeof(sr_bootkey));
#endif
explicit_bzero(bios_bootsr, sizeof(bios_bootsr_t));
break;
case BOOTARG_EFIINFO:
bios_efiinfo = (bios_efiinfo_t *)q->ba_arg;
break;
case BOOTARG_UCODE:
bios_ucode = (bios_ucode_t *)q->ba_arg;
break;
default:
#ifdef BOOTINFO_DEBUG
printf(" unsupported arg (%d) %p", q->ba_type,
q->ba_arg);
#endif
break;
}
}
#ifdef BOOTINFO_DEBUG
printf("\n");
#endif
}
int
check_context(const struct reg *regs, struct trapframe *tf)
{
uint16_t sel;
if (((regs->r_rflags ^ tf->tf_rflags) & PSL_USERSTATIC) != 0)
return EINVAL;
sel = regs->r_ss & 0xffff;
if (!VALID_USER_DSEL(sel))
return EINVAL;
sel = regs->r_cs & 0xffff;
if (!VALID_USER_CSEL(sel))
return EINVAL;
if (regs->r_rip >= VM_MAXUSER_ADDRESS)
return EINVAL;
return 0;
}
void
delay_init(void(*fn)(int), int fn_quality)
{
static int cur_quality = 0;
if (fn_quality > cur_quality) {
delay_func = fn;
cur_quality = fn_quality;
}
}
1126
1130
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */
/*
* Public domain.
* Written by Matthew Dempsky.
*/
#include <lib/libkern/libkern.h>
__attribute__((weak)) void __explicit_bzero_hook(void *, size_t);
__attribute__((weak)) void
__explicit_bzero_hook(void *buf, size_t len)
{
}
void
explicit_bzero(void *buf, size_t len)
{
memset(buf, 0, len);
__explicit_bzero_hook(buf, len);
}
289
38
1080
1305
766
12
6
623
4
4
3
2
11
32
22
80
36
104
289
275
17
17
32
266
266
17
16
288
38
38
31
9
33
5
38
38
36
33
271
49
16
215
215
272
3
1
1
3
3
3
3
3
3
1
3
1
3
1
84
41
81
59
79
40
23
60
60
43
62
50
10
1
3
7
7
5
2
3
67
1555
1556
1078
567
572
7
6
85
75
11
80
23
23
90
30
38
41
10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
/* $OpenBSD: uvm_amap.c,v 1.91 2022/08/01 14:15:46 mpi Exp $ */
/* $NetBSD: uvm_amap.c,v 1.27 2000/11/25 06:27:59 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* uvm_amap.c: amap operations
*
* this file contains functions that perform operations on amaps. see
* uvm_amap.h for a brief explanation of the role of amaps in uvm.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/atomic.h>
#include <uvm/uvm.h>
#include <uvm/uvm_swap.h>
/*
* pools for allocation of vm_amap structures. note that in order to
* avoid an endless loop, the amap pool's allocator cannot allocate
* memory from an amap (it currently goes through the kernel uobj, so
* we are ok).
*/
struct pool uvm_amap_pool;
struct pool uvm_small_amap_pool[UVM_AMAP_CHUNK];
struct pool uvm_amap_chunk_pool;
LIST_HEAD(, vm_amap) amap_list;
struct rwlock amap_list_lock = RWLOCK_INITIALIZER("amaplstlk");
#define amap_lock_list() rw_enter_write(&amap_list_lock)
#define amap_unlock_list() rw_exit_write(&amap_list_lock)
static char amap_small_pool_names[UVM_AMAP_CHUNK][9];
/*
* local functions
*/
static struct vm_amap *amap_alloc1(int, int, int);
static inline void amap_list_insert(struct vm_amap *);
static inline void amap_list_remove(struct vm_amap *);
struct vm_amap_chunk *amap_chunk_get(struct vm_amap *, int, int, int);
void amap_chunk_free(struct vm_amap *, struct vm_amap_chunk *);
/*
* if we enable PPREF, then we have a couple of extra functions that
* we need to prototype here...
*/
#ifdef UVM_AMAP_PPREF
#define PPREF_NONE ((int *) -1) /* not using ppref */
void amap_pp_adjref(struct vm_amap *, int, vsize_t, int);
void amap_pp_establish(struct vm_amap *);
void amap_wiperange_chunk(struct vm_amap *, struct vm_amap_chunk *, int,
int);
void amap_wiperange(struct vm_amap *, int, int);
#endif /* UVM_AMAP_PPREF */
static inline void
amap_list_insert(struct vm_amap *amap)
{
amap_lock_list();
LIST_INSERT_HEAD(&amap_list, amap, am_list);
amap_unlock_list();
}
static inline void
amap_list_remove(struct vm_amap *amap)
{
amap_lock_list();
LIST_REMOVE(amap, am_list);
amap_unlock_list();
}
/*
* amap_chunk_get: lookup a chunk for slot. if create is non-zero,
* the chunk is created if it does not yet exist.
*
* => returns the chunk on success or NULL on error
*/
struct vm_amap_chunk *
amap_chunk_get(struct vm_amap *amap, int slot, int create, int waitf)
{
int bucket = UVM_AMAP_BUCKET(amap, slot);
int baseslot = AMAP_BASE_SLOT(slot);
int n;
struct vm_amap_chunk *chunk, *newchunk, *pchunk = NULL;
if (UVM_AMAP_SMALL(amap))
return &amap->am_small;
for (chunk = amap->am_buckets[bucket]; chunk != NULL;
chunk = TAILQ_NEXT(chunk, ac_list)) {
if (UVM_AMAP_BUCKET(amap, chunk->ac_baseslot) != bucket)
break;
if (chunk->ac_baseslot == baseslot)
return chunk;
pchunk = chunk;
}
if (!create)
return NULL;
if (amap->am_nslot - baseslot >= UVM_AMAP_CHUNK)
n = UVM_AMAP_CHUNK;
else
n = amap->am_nslot - baseslot;
newchunk = pool_get(&uvm_amap_chunk_pool, waitf | PR_ZERO);
if (newchunk == NULL)
return NULL;
if (pchunk == NULL) {
TAILQ_INSERT_TAIL(&amap->am_chunks, newchunk, ac_list);
KASSERT(amap->am_buckets[bucket] == NULL);
amap->am_buckets[bucket] = newchunk;
} else
TAILQ_INSERT_AFTER(&amap->am_chunks, pchunk, newchunk,
ac_list);
amap->am_ncused++;
newchunk->ac_baseslot = baseslot;
newchunk->ac_nslot = n;
return newchunk;
}
void
amap_chunk_free(struct vm_amap *amap, struct vm_amap_chunk *chunk)
{
int bucket = UVM_AMAP_BUCKET(amap, chunk->ac_baseslot);
struct vm_amap_chunk *nchunk;
if (UVM_AMAP_SMALL(amap))
return;
nchunk = TAILQ_NEXT(chunk, ac_list);
TAILQ_REMOVE(&amap->am_chunks, chunk, ac_list);
if (amap->am_buckets[bucket] == chunk) {
if (nchunk != NULL &&
UVM_AMAP_BUCKET(amap, nchunk->ac_baseslot) == bucket)
amap->am_buckets[bucket] = nchunk;
else
amap->am_buckets[bucket] = NULL;
}
pool_put(&uvm_amap_chunk_pool, chunk);
amap->am_ncused--;
}
#ifdef UVM_AMAP_PPREF
/*
* what is ppref? ppref is an _optional_ amap feature which is used
* to keep track of reference counts on a per-page basis. it is enabled
* when UVM_AMAP_PPREF is defined.
*
* when enabled, an array of ints is allocated for the pprefs. this
* array is allocated only when a partial reference is added to the
* map (either by unmapping part of the amap, or gaining a reference
* to only a part of an amap). if the allocation of the array fails
* (M_NOWAIT), then we set the array pointer to PPREF_NONE to indicate
* that we tried to do ppref's but couldn't alloc the array so just
* give up (after all, this is an optional feature!).
*
* the array is divided into page sized "chunks." for chunks of length 1,
* the chunk reference count plus one is stored in that chunk's slot.
* for chunks of length > 1 the first slot contains (the reference count
* plus one) * -1. [the negative value indicates that the length is
* greater than one.] the second slot of the chunk contains the length
* of the chunk. here is an example:
*
* actual REFS: 2 2 2 2 3 1 1 0 0 0 4 4 0 1 1 1
* ppref: -3 4 x x 4 -2 2 -1 3 x -5 2 1 -2 3 x
* <----------><-><----><-------><----><-><------->
* (x = don't care)
*
* this allows us to allow one int to contain the ref count for the whole
* chunk. note that the "plus one" part is needed because a reference
* count of zero is neither positive or negative (need a way to tell
* if we've got one zero or a bunch of them).
*
* here are some in-line functions to help us.
*/
/*
* pp_getreflen: get the reference and length for a specific offset
*
* => ppref's amap must be locked
*/
static inline void
pp_getreflen(int *ppref, int offset, int *refp, int *lenp)
{
if (ppref[offset] > 0) { /* chunk size must be 1 */
*refp = ppref[offset] - 1; /* don't forget to adjust */
*lenp = 1;
} else {
*refp = (ppref[offset] * -1) - 1;
*lenp = ppref[offset+1];
}
}
/*
* pp_setreflen: set the reference and length for a specific offset
*
* => ppref's amap must be locked
*/
static inline void
pp_setreflen(int *ppref, int offset, int ref, int len)
{
if (len == 1) {
ppref[offset] = ref + 1;
} else {
ppref[offset] = (ref + 1) * -1;
ppref[offset+1] = len;
}
}
#endif /* UVM_AMAP_PPREF */
/*
* amap_init: called at boot time to init global amap data structures
*/
void
amap_init(void)
{
int i;
size_t size;
/* Initialize the vm_amap pool. */
pool_init(&uvm_amap_pool, sizeof(struct vm_amap),
0, IPL_MPFLOOR, PR_WAITOK, "amappl", NULL);
pool_sethiwat(&uvm_amap_pool, 4096);
/* initialize small amap pools */
for (i = 0; i < nitems(uvm_small_amap_pool); i++) {
snprintf(amap_small_pool_names[i],
sizeof(amap_small_pool_names[0]), "amappl%d", i + 1);
size = offsetof(struct vm_amap, am_small.ac_anon) +
(i + 1) * sizeof(struct vm_anon *);
pool_init(&uvm_small_amap_pool[i], size, 0, IPL_MPFLOOR,
PR_WAITOK, amap_small_pool_names[i], NULL);
}
pool_init(&uvm_amap_chunk_pool, sizeof(struct vm_amap_chunk) +
UVM_AMAP_CHUNK * sizeof(struct vm_anon *),
0, IPL_MPFLOOR, PR_WAITOK, "amapchunkpl", NULL);
pool_sethiwat(&uvm_amap_chunk_pool, 4096);
}
/*
* amap_alloc1: allocate an amap, but do not initialise the overlay.
*
* => Note: lock is not set.
*/
static inline struct vm_amap *
amap_alloc1(int slots, int waitf, int lazyalloc)
{
struct vm_amap *amap;
struct vm_amap_chunk *chunk, *tmp;
int chunks, log_chunks, chunkperbucket = 1, hashshift = 0;
int buckets, i, n;
int pwaitf = (waitf & M_WAITOK) ? PR_WAITOK : PR_NOWAIT;
KASSERT(slots > 0);
/*
* Cast to unsigned so that rounding up cannot cause integer overflow
* if slots is large.
*/
chunks = roundup((unsigned int)slots, UVM_AMAP_CHUNK) / UVM_AMAP_CHUNK;
if (lazyalloc) {
/*
* Basically, the amap is a hash map where the number of
* buckets is fixed. We select the number of buckets using the
* following strategy:
*
* 1. The maximal number of entries to search in a bucket upon
* a collision should be less than or equal to
* log2(slots / UVM_AMAP_CHUNK). This is the worst-case number
* of lookups we would have if we could chunk the amap. The
* log2(n) comes from the fact that amaps are chunked by
* splitting up their vm_map_entries and organizing those
* in a binary search tree.
*
* 2. The maximal number of entries in a bucket must be a
* power of two.
*
* The maximal number of entries per bucket is used to hash
* a slot to a bucket.
*
* In the future, this strategy could be refined to make it
* even harder/impossible that the total amount of KVA needed
* for the hash buckets of all amaps to exceed the maximal
* amount of KVA memory reserved for amaps.
*/
for (log_chunks = 1; (chunks >> log_chunks) > 0; log_chunks++)
continue;
chunkperbucket = 1 << hashshift;
while (chunkperbucket + 1 < log_chunks) {
hashshift++;
chunkperbucket = 1 << hashshift;
}
}
if (slots > UVM_AMAP_CHUNK)
amap = pool_get(&uvm_amap_pool, pwaitf);
else
amap = pool_get(&uvm_small_amap_pool[slots - 1],
pwaitf | PR_ZERO);
if (amap == NULL)
return NULL;
amap->am_lock = NULL;
amap->am_ref = 1;
amap->am_flags = 0;
#ifdef UVM_AMAP_PPREF
amap->am_ppref = NULL;
#endif
amap->am_nslot = slots;
amap->am_nused = 0;
if (UVM_AMAP_SMALL(amap)) {
amap->am_small.ac_nslot = slots;
return amap;
}
amap->am_ncused = 0;
TAILQ_INIT(&amap->am_chunks);
amap->am_hashshift = hashshift;
amap->am_buckets = NULL;
buckets = howmany(chunks, chunkperbucket);
amap->am_buckets = mallocarray(buckets, sizeof(*amap->am_buckets),
M_UVMAMAP, waitf | (lazyalloc ? M_ZERO : 0));
if (amap->am_buckets == NULL)
goto fail1;
amap->am_nbuckets = buckets;
if (!lazyalloc) {
for (i = 0; i < buckets; i++) {
if (i == buckets - 1) {
n = slots % UVM_AMAP_CHUNK;
if (n == 0)
n = UVM_AMAP_CHUNK;
} else
n = UVM_AMAP_CHUNK;
chunk = pool_get(&uvm_amap_chunk_pool,
PR_ZERO | pwaitf);
if (chunk == NULL)
goto fail1;
amap->am_buckets[i] = chunk;
amap->am_ncused++;
chunk->ac_baseslot = i * UVM_AMAP_CHUNK;
chunk->ac_nslot = n;
TAILQ_INSERT_TAIL(&amap->am_chunks, chunk, ac_list);
}
}
return amap;
fail1:
free(amap->am_buckets, M_UVMAMAP, buckets * sizeof(*amap->am_buckets));
TAILQ_FOREACH_SAFE(chunk, &amap->am_chunks, ac_list, tmp)
pool_put(&uvm_amap_chunk_pool, chunk);
pool_put(&uvm_amap_pool, amap);
return NULL;
}
static void
amap_lock_alloc(struct vm_amap *amap)
{
rw_obj_alloc(&amap->am_lock, "amaplk");
}
/*
* amap_alloc: allocate an amap to manage "sz" bytes of anonymous VM
*
* => caller should ensure sz is a multiple of PAGE_SIZE
* => reference count to new amap is set to one
* => new amap is returned unlocked
*/
struct vm_amap *
amap_alloc(vaddr_t sz, int waitf, int lazyalloc)
{
struct vm_amap *amap;
size_t slots;
AMAP_B2SLOT(slots, sz); /* load slots */
if (slots > INT_MAX)
return NULL;
amap = amap_alloc1(slots, waitf, lazyalloc);
if (amap != NULL) {
amap_lock_alloc(amap);
amap_list_insert(amap);
}
return amap;
}
/*
* amap_free: free an amap
*
* => the amap must be unlocked
* => the amap should have a zero reference count and be empty
*/
void
amap_free(struct vm_amap *amap)
{
struct vm_amap_chunk *chunk, *tmp;
KASSERT(amap->am_ref == 0 && amap->am_nused == 0);
KASSERT((amap->am_flags & AMAP_SWAPOFF) == 0);
if (amap->am_lock != NULL) {
KASSERT(amap->am_lock == NULL || !rw_write_held(amap->am_lock));
rw_obj_free(amap->am_lock);
}
#ifdef UVM_AMAP_PPREF
if (amap->am_ppref && amap->am_ppref != PPREF_NONE)
free(amap->am_ppref, M_UVMAMAP, amap->am_nslot * sizeof(int));
#endif
if (UVM_AMAP_SMALL(amap))
pool_put(&uvm_small_amap_pool[amap->am_nslot - 1], amap);
else {
TAILQ_FOREACH_SAFE(chunk, &amap->am_chunks, ac_list, tmp)
pool_put(&uvm_amap_chunk_pool, chunk);
free(amap->am_buckets, M_UVMAMAP,
amap->am_nbuckets * sizeof(*amap->am_buckets));
pool_put(&uvm_amap_pool, amap);
}
}
/*
* amap_wipeout: wipeout all anon's in an amap; then free the amap!
*
* => Called from amap_unref(), when reference count drops to zero.
* => amap must be locked.
*/
void
amap_wipeout(struct vm_amap *amap)
{
int slot;
struct vm_anon *anon;
struct vm_amap_chunk *chunk;
struct pglist pgl;
KASSERT(rw_write_held(amap->am_lock));
KASSERT(amap->am_ref == 0);
if (__predict_false((amap->am_flags & AMAP_SWAPOFF) != 0)) {
/*
* Note: amap_swap_off() will call us again.
*/
amap_unlock(amap);
return;
}
TAILQ_INIT(&pgl);
amap_list_remove(amap);
AMAP_CHUNK_FOREACH(chunk, amap) {
int i, refs, map = chunk->ac_usedmap;
for (i = ffs(map); i != 0; i = ffs(map)) {
slot = i - 1;
map ^= 1 << slot;
anon = chunk->ac_anon[slot];
if (anon == NULL || anon->an_ref == 0)
panic("amap_wipeout: corrupt amap");
KASSERT(anon->an_lock == amap->am_lock);
/*
* Drop the reference.
*/
refs = --anon->an_ref;
if (refs == 0) {
uvm_anfree_list(anon, &pgl);
}
}
}
/* free the pages */
uvm_pglistfree(&pgl);
/*
* Finally, destroy the amap.
*/
amap->am_ref = 0; /* ... was one */
amap->am_nused = 0;
amap_unlock(amap);
amap_free(amap);
}
/*
* amap_copy: ensure that a map entry's "needs_copy" flag is false
* by copying the amap if necessary.
*
* => an entry with a null amap pointer will get a new (blank) one.
* => the map that the map entry belongs to must be locked by caller.
* => the amap currently attached to "entry" (if any) must be unlocked.
* => if canchunk is true, then we may clip the entry into a chunk
* => "startva" and "endva" are used only if canchunk is true. they are
* used to limit chunking (e.g. if you have a large space that you
* know you are going to need to allocate amaps for, there is no point
* in allowing that to be chunked)
*/
void
amap_copy(struct vm_map *map, struct vm_map_entry *entry, int waitf,
boolean_t canchunk, vaddr_t startva, vaddr_t endva)
{
struct vm_amap *amap, *srcamap;
int slots, lcv, lazyalloc = 0;
vaddr_t chunksize;
int i, j, k, n, srcslot;
struct vm_amap_chunk *chunk = NULL, *srcchunk = NULL;
struct vm_anon *anon;
KASSERT(map != kernel_map); /* we use sleeping locks */
/*
* Is there an amap to copy? If not, create one.
*/
if (entry->aref.ar_amap == NULL) {
/*
* Check to see if we have a large amap that we can
* chunk. We align startva/endva to chunk-sized
* boundaries and then clip to them.
*
* If we cannot chunk the amap, allocate it in a way
* that makes it grow or shrink dynamically with
* the number of slots.
*/
if (atop(entry->end - entry->start) >= UVM_AMAP_LARGE) {
if (canchunk) {
/* convert slots to bytes */
chunksize = UVM_AMAP_CHUNK << PAGE_SHIFT;
startva = (startva / chunksize) * chunksize;
endva = roundup(endva, chunksize);
UVM_MAP_CLIP_START(map, entry, startva);
/* watch out for endva wrap-around! */
if (endva >= startva)
UVM_MAP_CLIP_END(map, entry, endva);
} else
lazyalloc = 1;
}
entry->aref.ar_pageoff = 0;
entry->aref.ar_amap = amap_alloc(entry->end - entry->start,
waitf, lazyalloc);
if (entry->aref.ar_amap != NULL)
entry->etype &= ~UVM_ET_NEEDSCOPY;
return;
}
/*
* First check and see if we are the only map entry referencing
* he amap we currently have. If so, then just take it over instead
* of copying it. Note that we are reading am_ref without lock held
* as the value value can only be one if we have the only reference
* to the amap (via our locked map). If the value is greater than
* one, then allocate amap and re-check the value.
*/
if (entry->aref.ar_amap->am_ref == 1) {
entry->etype &= ~UVM_ET_NEEDSCOPY;
return;
}
/*
* Allocate a new amap (note: not initialised, etc).
*/
AMAP_B2SLOT(slots, entry->end - entry->start);
if (!UVM_AMAP_SMALL(entry->aref.ar_amap) &&
entry->aref.ar_amap->am_hashshift != 0)
lazyalloc = 1;
amap = amap_alloc1(slots, waitf, lazyalloc);
if (amap == NULL)
return;
srcamap = entry->aref.ar_amap;
/*
* Make the new amap share the source amap's lock, and then lock
* both.
*/
amap->am_lock = srcamap->am_lock;
rw_obj_hold(amap->am_lock);
amap_lock(srcamap);
/*
* Re-check the reference count with the lock held. If it has
* dropped to one - we can take over the existing map.
*/
if (srcamap->am_ref == 1) {
/* Just take over the existing amap. */
entry->etype &= ~UVM_ET_NEEDSCOPY;
amap_unlock(srcamap);
/* Destroy the new (unused) amap. */
amap->am_ref--;
amap_free(amap);
return;
}
/*
* Copy the slots.
*/
for (lcv = 0; lcv < slots; lcv += n) {
srcslot = entry->aref.ar_pageoff + lcv;
i = UVM_AMAP_SLOTIDX(lcv);
j = UVM_AMAP_SLOTIDX(srcslot);
n = UVM_AMAP_CHUNK;
if (i > j)
n -= i;
else
n -= j;
if (lcv + n > slots)
n = slots - lcv;
srcchunk = amap_chunk_get(srcamap, srcslot, 0, PR_NOWAIT);
if (srcchunk == NULL)
continue;
chunk = amap_chunk_get(amap, lcv, 1, PR_NOWAIT);
if (chunk == NULL) {
/* amap_wipeout() releases the lock. */
amap->am_ref = 0;
amap_wipeout(amap);
return;
}
for (k = 0; k < n; i++, j++, k++) {
chunk->ac_anon[i] = anon = srcchunk->ac_anon[j];
if (anon == NULL)
continue;
KASSERT(anon->an_lock == srcamap->am_lock);
KASSERT(anon->an_ref > 0);
chunk->ac_usedmap |= (1 << i);
anon->an_ref++;
amap->am_nused++;
}
}
/*
* Drop our reference to the old amap (srcamap) and unlock.
* Since the reference count on srcamap is greater than one,
* (we checked above), it cannot drop to zero while it is locked.
*/
srcamap->am_ref--;
KASSERT(srcamap->am_ref > 0);
if (srcamap->am_ref == 1 && (srcamap->am_flags & AMAP_SHARED) != 0)
srcamap->am_flags &= ~AMAP_SHARED; /* clear shared flag */
#ifdef UVM_AMAP_PPREF
if (srcamap->am_ppref && srcamap->am_ppref != PPREF_NONE) {
amap_pp_adjref(srcamap, entry->aref.ar_pageoff,
(entry->end - entry->start) >> PAGE_SHIFT, -1);
}
#endif
/*
* If we referenced any anons, then share the source amap's lock.
* Otherwise, we have nothing in common, so allocate a new one.
*/
KASSERT(amap->am_lock == srcamap->am_lock);
if (amap->am_nused == 0) {
rw_obj_free(amap->am_lock);
amap->am_lock = NULL;
}
amap_unlock(srcamap);
if (amap->am_lock == NULL)
amap_lock_alloc(amap);
/*
* Install new amap.
*/
entry->aref.ar_pageoff = 0;
entry->aref.ar_amap = amap;
entry->etype &= ~UVM_ET_NEEDSCOPY;
amap_list_insert(amap);
}
/*
* amap_cow_now: resolve all copy-on-write faults in an amap now for fork(2)
*
* called during fork(2) when the parent process has a wired map
* entry. in that case we want to avoid write-protecting pages
* in the parent's map (e.g. like what you'd do for a COW page)
* so we resolve the COW here.
*
* => assume parent's entry was wired, thus all pages are resident.
* => the parent and child vm_map must both be locked.
* => caller passes child's map/entry in to us
* => XXXCDC: out of memory should cause fork to fail, but there is
* currently no easy way to do this (needs fix)
*/
void
amap_cow_now(struct vm_map *map, struct vm_map_entry *entry)
{
struct vm_amap *amap = entry->aref.ar_amap;
int slot;
struct vm_anon *anon, *nanon;
struct vm_page *pg, *npg;
struct vm_amap_chunk *chunk;
/*
* note that if we unlock the amap then we must ReStart the "lcv" for
* loop because some other process could reorder the anon's in the
* am_anon[] array on us while the lock is dropped.
*/
ReStart:
amap_lock(amap);
AMAP_CHUNK_FOREACH(chunk, amap) {
int i, map = chunk->ac_usedmap;
for (i = ffs(map); i != 0; i = ffs(map)) {
slot = i - 1;
map ^= 1 << slot;
anon = chunk->ac_anon[slot];
pg = anon->an_page;
KASSERT(anon->an_lock == amap->am_lock);
/*
* The old page must be resident since the parent is
* wired.
*/
KASSERT(pg != NULL);
/*
* if the anon ref count is one, we are safe (the child
* has exclusive access to the page).
*/
if (anon->an_ref <= 1)
continue;
/*
* If the page is busy, then we have to unlock, wait for
* it and then restart.
*/
if (pg->pg_flags & PG_BUSY) {
uvm_pagewait(pg, amap->am_lock, "cownow");
goto ReStart;
}
/*
* Perform a copy-on-write.
* First - get a new anon and a page.
*/
nanon = uvm_analloc();
if (nanon != NULL) {
/* the new anon will share the amap's lock */
nanon->an_lock = amap->am_lock;
npg = uvm_pagealloc(NULL, 0, nanon, 0);
} else
npg = NULL; /* XXX: quiet gcc warning */
if (nanon == NULL || npg == NULL) {
/* out of memory */
amap_unlock(amap);
if (nanon != NULL) {
nanon->an_lock = NULL;
nanon->an_ref--;
KASSERT(nanon->an_ref == 0);
uvm_anfree(nanon);
}
uvm_wait("cownowpage");
goto ReStart;
}
/*
* Copy the data and replace anon with the new one.
* Also, setup its lock (share the with amap's lock).
*/
uvm_pagecopy(pg, npg);
anon->an_ref--;
KASSERT(anon->an_ref > 0);
chunk->ac_anon[slot] = nanon;
/*
* Drop PG_BUSY on new page. Since its owner was write
* locked all this time - it cannot be PG_RELEASED or
* PG_WANTED.
*/
atomic_clearbits_int(&npg->pg_flags, PG_BUSY|PG_FAKE);
UVM_PAGE_OWN(npg, NULL);
uvm_lock_pageq();
uvm_pageactivate(npg);
uvm_unlock_pageq();
}
}
amap_unlock(amap);
}
/*
* amap_splitref: split a single reference into two separate references
*
* => called from uvm_map's clip routines
* => origref's map should be locked
* => origref->ar_amap should be unlocked (we will lock)
*/
void
amap_splitref(struct vm_aref *origref, struct vm_aref *splitref, vaddr_t offset)
{
struct vm_amap *amap = origref->ar_amap;
int leftslots;
KASSERT(splitref->ar_amap == amap);
AMAP_B2SLOT(leftslots, offset);
if (leftslots == 0)
panic("amap_splitref: split at zero offset");
amap_lock(amap);
if (amap->am_nslot - origref->ar_pageoff - leftslots <= 0)
panic("amap_splitref: map size check failed");
#ifdef UVM_AMAP_PPREF
/* Establish ppref before we add a duplicate reference to the amap. */
if (amap->am_ppref == NULL)
amap_pp_establish(amap);
#endif
/* Note: not a share reference. */
amap->am_ref++;
splitref->ar_amap = amap;
splitref->ar_pageoff = origref->ar_pageoff + leftslots;
amap_unlock(amap);
}
#ifdef UVM_AMAP_PPREF
/*
* amap_pp_establish: add a ppref array to an amap, if possible.
*
* => amap should be locked by caller* => amap should be locked by caller
*/
void
amap_pp_establish(struct vm_amap *amap)
{
KASSERT(rw_write_held(amap->am_lock));
amap->am_ppref = mallocarray(amap->am_nslot, sizeof(int),
M_UVMAMAP, M_NOWAIT|M_ZERO);
if (amap->am_ppref == NULL) {
/* Failure - just do not use ppref. */
amap->am_ppref = PPREF_NONE;
return;
}
pp_setreflen(amap->am_ppref, 0, amap->am_ref, amap->am_nslot);
}
/*
* amap_pp_adjref: adjust reference count to a part of an amap using the
* per-page reference count array.
*
* => caller must check that ppref != PPREF_NONE before calling.
* => map and amap must be locked.
*/
void
amap_pp_adjref(struct vm_amap *amap, int curslot, vsize_t slotlen, int adjval)
{
int stopslot, *ppref, lcv, prevlcv;
int ref, len, prevref, prevlen;
KASSERT(rw_write_held(amap->am_lock));
stopslot = curslot + slotlen;
ppref = amap->am_ppref;
prevlcv = 0;
/*
* Advance to the correct place in the array, fragment if needed.
*/
for (lcv = 0 ; lcv < curslot ; lcv += len) {
pp_getreflen(ppref, lcv, &ref, &len);
if (lcv + len > curslot) { /* goes past start? */
pp_setreflen(ppref, lcv, ref, curslot - lcv);
pp_setreflen(ppref, curslot, ref, len - (curslot -lcv));
len = curslot - lcv; /* new length of entry @ lcv */
}
prevlcv = lcv;
}
if (lcv != 0)
pp_getreflen(ppref, prevlcv, &prevref, &prevlen);
else {
/*
* Ensure that the "prevref == ref" test below always
* fails, since we are starting from the beginning of
* the ppref array; that is, there is no previous chunk.
*/
prevref = -1;
prevlen = 0;
}
/*
* Now adjust reference counts in range. Merge the first
* changed entry with the last unchanged entry if possible.
*/
if (lcv != curslot)
panic("amap_pp_adjref: overshot target");
for (/* lcv already set */; lcv < stopslot ; lcv += len) {
pp_getreflen(ppref, lcv, &ref, &len);
if (lcv + len > stopslot) { /* goes past end? */
pp_setreflen(ppref, lcv, ref, stopslot - lcv);
pp_setreflen(ppref, stopslot, ref,
len - (stopslot - lcv));
len = stopslot - lcv;
}
ref += adjval;
if (ref < 0)
panic("amap_pp_adjref: negative reference count");
if (lcv == prevlcv + prevlen && ref == prevref) {
pp_setreflen(ppref, prevlcv, ref, prevlen + len);
} else {
pp_setreflen(ppref, lcv, ref, len);
}
if (ref == 0)
amap_wiperange(amap, lcv, len);
}
}
void
amap_wiperange_chunk(struct vm_amap *amap, struct vm_amap_chunk *chunk,
int slotoff, int slots)
{
int curslot, i, map;
int startbase, endbase;
struct vm_anon *anon;
startbase = AMAP_BASE_SLOT(slotoff);
endbase = AMAP_BASE_SLOT(slotoff + slots - 1);
map = chunk->ac_usedmap;
if (startbase == chunk->ac_baseslot)
map &= ~((1 << (slotoff - startbase)) - 1);
if (endbase == chunk->ac_baseslot)
map &= (1 << (slotoff + slots - endbase)) - 1;
for (i = ffs(map); i != 0; i = ffs(map)) {
int refs;
curslot = i - 1;
map ^= 1 << curslot;
chunk->ac_usedmap ^= 1 << curslot;
anon = chunk->ac_anon[curslot];
KASSERT(anon->an_lock == amap->am_lock);
/* remove it from the amap */
chunk->ac_anon[curslot] = NULL;
amap->am_nused--;
/* drop anon reference count */
refs = --anon->an_ref;
if (refs == 0) {
uvm_anfree(anon);
}
/*
* done with this anon, next ...!
*/
} /* end of 'for' loop */
}
/*
* amap_wiperange: wipe out a range of an amap.
* Note: different from amap_wipeout because the amap is kept intact.
*
* => Both map and amap must be locked by caller.
*/
void
amap_wiperange(struct vm_amap *amap, int slotoff, int slots)
{
int bucket, startbucket, endbucket;
struct vm_amap_chunk *chunk, *nchunk;
KASSERT(rw_write_held(amap->am_lock));
startbucket = UVM_AMAP_BUCKET(amap, slotoff);
endbucket = UVM_AMAP_BUCKET(amap, slotoff + slots - 1);
/*
* We can either traverse the amap by am_chunks or by am_buckets.
* Determine which way is less expensive.
*/
if (UVM_AMAP_SMALL(amap))
amap_wiperange_chunk(amap, &amap->am_small, slotoff, slots);
else if (endbucket + 1 - startbucket >= amap->am_ncused) {
TAILQ_FOREACH_SAFE(chunk, &amap->am_chunks, ac_list, nchunk) {
if (chunk->ac_baseslot + chunk->ac_nslot <= slotoff)
continue;
if (chunk->ac_baseslot >= slotoff + slots)
continue;
amap_wiperange_chunk(amap, chunk, slotoff, slots);
if (chunk->ac_usedmap == 0)
amap_chunk_free(amap, chunk);
}
} else {
for (bucket = startbucket; bucket <= endbucket; bucket++) {
for (chunk = amap->am_buckets[bucket]; chunk != NULL;
chunk = nchunk) {
nchunk = TAILQ_NEXT(chunk, ac_list);
if (UVM_AMAP_BUCKET(amap, chunk->ac_baseslot) !=
bucket)
break;
if (chunk->ac_baseslot + chunk->ac_nslot <=
slotoff)
continue;
if (chunk->ac_baseslot >= slotoff + slots)
continue;
amap_wiperange_chunk(amap, chunk, slotoff,
slots);
if (chunk->ac_usedmap == 0)
amap_chunk_free(amap, chunk);
}
}
}
}
#endif
/*
* amap_swap_off: pagein anonymous pages in amaps and drop swap slots.
*
* => note that we don't always traverse all anons.
* eg. amaps being wiped out, released anons.
* => return TRUE if failed.
*/
boolean_t
amap_swap_off(int startslot, int endslot)
{
struct vm_amap *am;
struct vm_amap *am_next;
struct vm_amap marker;
boolean_t rv = FALSE;
amap_lock_list();
for (am = LIST_FIRST(&amap_list); am != NULL && !rv; am = am_next) {
int i, map;
struct vm_amap_chunk *chunk;
amap_lock(am);
if (am->am_nused == 0) {
amap_unlock(am);
am_next = LIST_NEXT(am, am_list);
continue;
}
LIST_INSERT_AFTER(am, &marker, am_list);
amap_unlock_list();
again:
AMAP_CHUNK_FOREACH(chunk, am) {
map = chunk->ac_usedmap;
for (i = ffs(map); i != 0; i = ffs(map)) {
int swslot;
int slot = i - 1;
struct vm_anon *anon;
map ^= 1 << slot;
anon = chunk->ac_anon[slot];
swslot = anon->an_swslot;
if (swslot < startslot || endslot <= swslot) {
continue;
}
am->am_flags |= AMAP_SWAPOFF;
rv = uvm_anon_pagein(am, anon);
amap_lock(am);
am->am_flags &= ~AMAP_SWAPOFF;
if (amap_refs(am) == 0) {
amap_wipeout(am);
am = NULL;
goto nextamap;
}
if (rv)
goto nextamap;
goto again;
}
}
nextamap:
if (am != NULL)
amap_unlock(am);
amap_lock_list();
am_next = LIST_NEXT(&marker, am_list);
LIST_REMOVE(&marker, am_list);
}
amap_unlock_list();
return rv;
}
/*
* amap_lookup: look up a page in an amap.
*
* => amap should be locked by caller.
*/
struct vm_anon *
amap_lookup(struct vm_aref *aref, vaddr_t offset)
{
int slot;
struct vm_amap *amap = aref->ar_amap;
struct vm_amap_chunk *chunk;
AMAP_B2SLOT(slot, offset);
slot += aref->ar_pageoff;
KASSERT(slot < amap->am_nslot);
chunk = amap_chunk_get(amap, slot, 0, PR_NOWAIT);
if (chunk == NULL)
return NULL;
return chunk->ac_anon[UVM_AMAP_SLOTIDX(slot)];
}
/*
* amap_lookups: look up a range of pages in an amap.
*
* => amap should be locked by caller.
* => XXXCDC: this interface is biased toward array-based amaps. fix.
*/
void
amap_lookups(struct vm_aref *aref, vaddr_t offset,
struct vm_anon **anons, int npages)
{
int i, lcv, n, slot;
struct vm_amap *amap = aref->ar_amap;
struct vm_amap_chunk *chunk = NULL;
AMAP_B2SLOT(slot, offset);
slot += aref->ar_pageoff;
KASSERT((slot + (npages - 1)) < amap->am_nslot);
for (i = 0, lcv = slot; lcv < slot + npages; i += n, lcv += n) {
n = UVM_AMAP_CHUNK - UVM_AMAP_SLOTIDX(lcv);
if (lcv + n > slot + npages)
n = slot + npages - lcv;
chunk = amap_chunk_get(amap, lcv, 0, PR_NOWAIT);
if (chunk == NULL)
memset(&anons[i], 0, n * sizeof(*anons));
else
memcpy(&anons[i],
&chunk->ac_anon[UVM_AMAP_SLOTIDX(lcv)],
n * sizeof(*anons));
}
}
/*
* amap_populate: ensure that the amap can store an anon for the page at
* offset. This function can sleep until memory to store the anon is
* available.
*/
void
amap_populate(struct vm_aref *aref, vaddr_t offset)
{
int slot;
struct vm_amap *amap = aref->ar_amap;
struct vm_amap_chunk *chunk;
AMAP_B2SLOT(slot, offset);
slot += aref->ar_pageoff;
KASSERT(slot < amap->am_nslot);
chunk = amap_chunk_get(amap, slot, 1, PR_WAITOK);
KASSERT(chunk != NULL);
}
/*
* amap_add: add (or replace) a page to an amap.
*
* => amap should be locked by caller.
* => anon must have the lock associated with this amap.
*/
int
amap_add(struct vm_aref *aref, vaddr_t offset, struct vm_anon *anon,
boolean_t replace)
{
int slot;
struct vm_amap *amap = aref->ar_amap;
struct vm_amap_chunk *chunk;
AMAP_B2SLOT(slot, offset);
slot += aref->ar_pageoff;
KASSERT(slot < amap->am_nslot);
chunk = amap_chunk_get(amap, slot, 1, PR_NOWAIT);
if (chunk == NULL)
return 1;
slot = UVM_AMAP_SLOTIDX(slot);
if (replace) {
struct vm_anon *oanon = chunk->ac_anon[slot];
KASSERT(oanon != NULL);
if (oanon->an_page && (amap->am_flags & AMAP_SHARED) != 0) {
pmap_page_protect(oanon->an_page, PROT_NONE);
/*
* XXX: suppose page is supposed to be wired somewhere?
*/
}
} else { /* !replace */
if (chunk->ac_anon[slot] != NULL)
panic("amap_add: slot in use");
chunk->ac_usedmap |= 1 << slot;
amap->am_nused++;
}
chunk->ac_anon[slot] = anon;
return 0;
}
/*
* amap_unadd: remove a page from an amap.
*
* => amap should be locked by caller.
*/
void
amap_unadd(struct vm_aref *aref, vaddr_t offset)
{
struct vm_amap *amap = aref->ar_amap;
struct vm_amap_chunk *chunk;
int slot;
KASSERT(rw_write_held(amap->am_lock));
AMAP_B2SLOT(slot, offset);
slot += aref->ar_pageoff;
KASSERT(slot < amap->am_nslot);
chunk = amap_chunk_get(amap, slot, 0, PR_NOWAIT);
KASSERT(chunk != NULL);
slot = UVM_AMAP_SLOTIDX(slot);
KASSERT(chunk->ac_anon[slot] != NULL);
chunk->ac_anon[slot] = NULL;
chunk->ac_usedmap &= ~(1 << slot);
amap->am_nused--;
if (chunk->ac_usedmap == 0)
amap_chunk_free(amap, chunk);
}
/*
* amap_adjref_anons: adjust the reference count(s) on amap and its anons.
*/
static void
amap_adjref_anons(struct vm_amap *amap, vaddr_t offset, vsize_t len,
int refv, boolean_t all)
{
#ifdef UVM_AMAP_PPREF
KASSERT(rw_write_held(amap->am_lock));
/*
* We must establish the ppref array before changing am_ref
* so that the ppref values match the current amap refcount.
*/
if (amap->am_ppref == NULL && !all && len != amap->am_nslot) {
amap_pp_establish(amap);
}
#endif
amap->am_ref += refv;
#ifdef UVM_AMAP_PPREF
if (amap->am_ppref && amap->am_ppref != PPREF_NONE) {
if (all) {
amap_pp_adjref(amap, 0, amap->am_nslot, refv);
} else {
amap_pp_adjref(amap, offset, len, refv);
}
}
#endif
amap_unlock(amap);
}
/*
* amap_ref: gain a reference to an amap.
*
* => amap must not be locked (we will lock).
* => "offset" and "len" are in units of pages.
* => Called at fork time to gain the child's reference.
*/
void
amap_ref(struct vm_amap *amap, vaddr_t offset, vsize_t len, int flags)
{
amap_lock(amap);
if (flags & AMAP_SHARED)
amap->am_flags |= AMAP_SHARED;
amap_adjref_anons(amap, offset, len, 1, (flags & AMAP_REFALL) != 0);
}
/*
* amap_unref: remove a reference to an amap.
*
* => All pmap-level references to this amap must be already removed.
* => Called from uvm_unmap_detach(); entry is already removed from the map.
* => We will lock amap, so it must be unlocked.
*/
void
amap_unref(struct vm_amap *amap, vaddr_t offset, vsize_t len, boolean_t all)
{
amap_lock(amap);
KASSERT(amap->am_ref > 0);
if (amap->am_ref == 1) {
/*
* If the last reference - wipeout and destroy the amap.
*/
amap->am_ref--;
amap_wipeout(amap);
return;
}
/*
* Otherwise, drop the reference count(s) on anons.
*/
if (amap->am_ref == 2 && (amap->am_flags & AMAP_SHARED) != 0) {
amap->am_flags &= ~AMAP_SHARED;
}
amap_adjref_anons(amap, offset, len, -1, all);
}
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/* $OpenBSD: ip_carp.h,v 1.51 2021/03/07 06:02:32 dlg Exp $ */
/*
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
* Copyright (c) 2003 Ryan McBride. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NETINET_IP_CARP_H_
#define _NETINET_IP_CARP_H_
/*
* The CARP header layout is as follows:
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |Version| Type | VirtualHostID | AdvSkew | Auth Len |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Demotion | AdvBase | Checksum |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Counter (1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Counter (2) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SHA-1 HMAC (1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SHA-1 HMAC (2) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SHA-1 HMAC (3) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SHA-1 HMAC (4) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SHA-1 HMAC (5) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
struct carp_header {
#if _BYTE_ORDER == _LITTLE_ENDIAN
u_int carp_type:4,
carp_version:4;
#endif
#if _BYTE_ORDER == _BIG_ENDIAN
u_int carp_version:4,
carp_type:4;
#endif
u_int8_t carp_vhid; /* virtual host id */
u_int8_t carp_advskew; /* advertisement skew */
u_int8_t carp_authlen; /* size of counter+md, 32bit chunks */
u_int8_t carp_demote; /* demotion indicator */
u_int8_t carp_advbase; /* advertisement interval */
u_int16_t carp_cksum;
u_int32_t carp_counter[2];
unsigned char carp_md[20]; /* SHA1 HMAC */
} __packed;
#define CARP_DFLTTL 255
/* carp_version */
#define CARP_VERSION 2
/* carp_type */
#define CARP_ADVERTISEMENT 0x01
#define CARP_KEY_LEN 20 /* a sha1 hash of a passphrase */
/* carp_advbase */
#define CARP_DFLTINTV 1
/*
* Statistics.
*/
struct carpstats {
u_int64_t carps_ipackets; /* total input packets, IPv4 */
u_int64_t carps_ipackets6; /* total input packets, IPv6 */
u_int64_t carps_badif; /* wrong interface */
u_int64_t carps_badttl; /* TTL is not CARP_DFLTTL */
u_int64_t carps_hdrops; /* packets shorter than hdr */
u_int64_t carps_badsum; /* bad checksum */
u_int64_t carps_badver; /* bad (incl unsupp) version */
u_int64_t carps_badlen; /* data length does not match */
u_int64_t carps_badauth; /* bad authentication */
u_int64_t carps_badvhid; /* bad VHID */
u_int64_t carps_badaddrs; /* bad address list */
u_int64_t carps_opackets; /* total output packets, IPv4 */
u_int64_t carps_opackets6; /* total output packets, IPv6 */
u_int64_t carps_onomem; /* no memory for an mbuf */
u_int64_t carps_ostates; /* total state updates sent */
u_int64_t carps_preempt; /* transitions to master */
};
#define CARPDEVNAMSIZ 16
#ifdef IFNAMSIZ
#if CARPDEVNAMSIZ != IFNAMSIZ
#error namsiz mismatch
#endif
#endif
/*
* Configuration structure for SIOCSVH SIOCGVH
*/
struct carpreq {
int carpr_state;
#define CARP_STATES "INIT", "BACKUP", "MASTER"
#define CARP_MAXSTATE 2
#define CARP_MAXNODES 32
char carpr_carpdev[CARPDEVNAMSIZ];
u_int8_t carpr_vhids[CARP_MAXNODES];
u_int8_t carpr_advskews[CARP_MAXNODES];
u_int8_t carpr_states[CARP_MAXNODES];
#define CARP_BAL_MODES "none", "ip", "ip-stealth", "ip-unicast"
#define CARP_BAL_NONE 0
#define CARP_BAL_IP 1
#define CARP_BAL_IPSTEALTH 2
#define CARP_BAL_IPUNICAST 3
#define CARP_BAL_MAXID 3
u_int8_t carpr_balancing;
int carpr_advbase;
unsigned char carpr_key[CARP_KEY_LEN];
struct in_addr carpr_peer;
};
/*
* Names for CARP sysctl objects
*/
#define CARPCTL_ALLOW 1 /* accept incoming CARP packets */
#define CARPCTL_PREEMPT 2 /* high-pri backup preemption mode */
#define CARPCTL_LOG 3 /* log bad packets */
#define CARPCTL_STATS 4 /* CARP stats */
#define CARPCTL_MAXID 5
#define CARPCTL_NAMES { \
{ 0, 0 }, \
{ "allow", CTLTYPE_INT }, \
{ "preempt", CTLTYPE_INT }, \
{ "log", CTLTYPE_INT }, \
{ "stats", CTLTYPE_STRUCT }, \
}
#ifdef _KERNEL
#include <net/if_types.h>
#include <sys/percpu.h>
enum carpstat_counters {
carps_ipackets,
carps_ipackets6,
carps_badif,
carps_badttl,
carps_hdrops,
carps_badsum,
carps_badver,
carps_badlen,
carps_badauth,
carps_badvhid,
carps_badaddrs,
carps_opackets,
carps_opackets6,
carps_onomem,
carps_ostates,
carps_preempt,
carps_ncounters,
};
extern struct cpumem *carpcounters;
static inline void
carpstat_inc(enum carpstat_counters c)
{
counters_inc(carpcounters, c);
}
/*
* If two carp interfaces share same physical interface, then we pretend all IP
* addresses belong to single interface.
*/
static inline int
carp_strict_addr_chk(struct ifnet *ifp_a, struct ifnet *ifp_b)
{
return ((ifp_a->if_type == IFT_CARP &&
ifp_b->if_index == ifp_a->if_carpdevidx) ||
(ifp_b->if_type == IFT_CARP &&
ifp_a->if_index == ifp_b->if_carpdevidx) ||
(ifp_a->if_type == IFT_CARP && ifp_b->if_type == IFT_CARP &&
ifp_a->if_carpdevidx == ifp_b->if_carpdevidx));
}
struct mbuf *carp_input(struct ifnet *, struct mbuf *, uint64_t);
int carp_proto_input(struct mbuf **, int *, int, int);
void carp_carpdev_state(void *);
void carp_group_demote_adj(struct ifnet *, int, char *);
int carp6_proto_input(struct mbuf **, int *, int, int);
int carp_iamatch(struct ifnet *);
int carp_ourether(struct ifnet *, u_int8_t *);
int carp_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int carp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
int carp_lsdrop(struct ifnet *, struct mbuf *, sa_family_t,
u_int32_t *, u_int32_t *, int);
#endif /* _KERNEL */
#endif /* _NETINET_IP_CARP_H_ */
25
7
7
8
22
16
7
1
7
7
7
19
19
19
19
19
19
12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
/* $OpenBSD: pckbc.c,v 1.53 2019/11/30 18:18:34 cheloha Exp $ */
/* $NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp $ */
/*
* Copyright (c) 1998
* Matthias Drochner. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <sys/queue.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/ic/i8042reg.h>
#include <dev/ic/pckbcvar.h>
#include "pckbd.h"
#if NPCKBD > 0
#include <dev/pckbc/pckbdvar.h>
#endif
#ifdef PCKBCDEBUG
#define DPRINTF(x...) do { printf(x); } while (0);
#else
#define DPRINTF(x...)
#endif
/* descriptor for one device command */
struct pckbc_devcmd {
TAILQ_ENTRY(pckbc_devcmd) next;
int flags;
#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
#define KBC_CMDFLAG_SLOW 2
#define KBC_CMDFLAG_QUEUED 4 /* descriptor on cmdqueue */
u_char cmd[4];
int cmdlen, cmdidx, retries;
u_char response[4];
int status, responselen, responseidx;
};
/* data per slave device */
struct pckbc_slotdata {
int polling; /* don't read data port in interrupt handler */
TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
#define NCMD 5
struct pckbc_devcmd cmds[NCMD];
};
#define CMD_IN_QUEUE(q) (!TAILQ_EMPTY(&(q)->cmdqueue))
void pckbc_init_slotdata(struct pckbc_slotdata *);
int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t, int);
int pckbc_submatch_locators(struct device *, void *, void *);
int pckbc_submatch(struct device *, void *, void *);
int pckbcprint(void *, const char *);
struct pckbc_internal pckbc_consdata;
int pckbc_console_attached;
int pckbc_console;
static struct pckbc_slotdata pckbc_cons_slotdata;
static int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
static int pckbc_get8042cmd(struct pckbc_internal *);
static int pckbc_put8042cmd(struct pckbc_internal *);
static int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t,
u_char);
static void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t,
struct pckbc_devcmd *);
void pckbc_cleanqueues(struct pckbc_internal *);
void pckbc_cleanqueue(struct pckbc_slotdata *);
void pckbc_cleanup(void *);
void pckbc_poll(void *);
int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
int pckbcintr_internal(struct pckbc_internal *, struct pckbc_softc *);
const char *pckbc_slot_names[] = { "kbd", "aux" };
#define KBC_DEVCMD_ACK 0xfa
#define KBC_DEVCMD_RESEND 0xfe
#define KBC_DEVCMD_BAT_DONE 0xaa
#define KBC_DEVCMD_BAT_FAIL 0xfc
#define KBD_DELAY DELAY(8)
static inline int
pckbc_wait_output(bus_space_tag_t iot, bus_space_handle_t ioh_c)
{
u_int i;
for (i = 100000; i; i--)
if (!(bus_space_read_1(iot, ioh_c, 0) & KBS_IBF)) {
KBD_DELAY;
return (1);
}
return (0);
}
int
pckbc_send_cmd(bus_space_tag_t iot, bus_space_handle_t ioh_c, u_char val)
{
if (!pckbc_wait_output(iot, ioh_c))
return (0);
bus_space_write_1(iot, ioh_c, 0, val);
return (1);
}
int
pckbc_poll_data1(bus_space_tag_t iot, bus_space_handle_t ioh_d,
bus_space_handle_t ioh_c, pckbc_slot_t slot, int checkaux)
{
int i;
u_char stat;
/* polls for ~100ms */
for (i = 100; i; i--, delay(1000)) {
stat = bus_space_read_1(iot, ioh_c, 0);
if (stat & KBS_DIB) {
register u_char c;
KBD_DELAY;
CPU_BUSY_CYCLE();
c = bus_space_read_1(iot, ioh_d, 0);
if (checkaux && (stat & KBS_AUXDATA)) {
if (slot != PCKBC_AUX_SLOT) {
DPRINTF("lost aux 0x%x\n", c);
continue;
}
} else {
if (slot == PCKBC_AUX_SLOT) {
DPRINTF("lost kbd 0x%x\n", c);
continue;
} else if (stat & KBS_AUXDATA) {
DPRINTF("discard aux data 0x%x\n", c);
continue;
}
}
return (c);
}
}
return (-1);
}
/*
* Get the current command byte.
*/
static int
pckbc_get8042cmd(struct pckbc_internal *t)
{
bus_space_tag_t iot = t->t_iot;
bus_space_handle_t ioh_d = t->t_ioh_d;
bus_space_handle_t ioh_c = t->t_ioh_c;
int data;
if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE))
return (0);
data = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT,
t->t_haveaux);
if (data == -1)
return (0);
t->t_cmdbyte = data;
return (1);
}
/*
* Pass command byte to keyboard controller (8042).
*/
static int
pckbc_put8042cmd(struct pckbc_internal *t)
{
bus_space_tag_t iot = t->t_iot;
bus_space_handle_t ioh_d = t->t_ioh_d;
bus_space_handle_t ioh_c = t->t_ioh_c;
if (!pckbc_send_cmd(iot, ioh_c, K_LDCMDBYTE))
return (0);
if (!pckbc_wait_output(iot, ioh_c))
return (0);
bus_space_write_1(iot, ioh_d, 0, t->t_cmdbyte);
return (1);
}
static int
pckbc_send_devcmd(struct pckbc_internal *t, pckbc_slot_t slot, u_char val)
{
bus_space_tag_t iot = t->t_iot;
bus_space_handle_t ioh_d = t->t_ioh_d;
bus_space_handle_t ioh_c = t->t_ioh_c;
if (slot == PCKBC_AUX_SLOT) {
if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
return (0);
}
if (!pckbc_wait_output(iot, ioh_c))
return (0);
bus_space_write_1(iot, ioh_d, 0, val);
return (1);
}
int
pckbc_is_console(bus_space_tag_t iot, bus_addr_t addr)
{
if (pckbc_console && !pckbc_console_attached &&
pckbc_consdata.t_iot == iot &&
pckbc_consdata.t_addr == addr)
return (1);
return (0);
}
int
pckbc_submatch_locators(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct pckbc_attach_args *pa = aux;
if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
return (0);
return (1);
}
int
pckbc_submatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
if (pckbc_submatch_locators(parent, match, aux) == 0)
return (0);
return ((*cf->cf_attach->ca_match)(parent, cf, aux));
}
int
pckbc_attach_slot(struct pckbc_softc *sc, pckbc_slot_t slot, int force)
{
struct pckbc_internal *t = sc->id;
struct pckbc_attach_args pa;
int found;
pa.pa_tag = t;
pa.pa_slot = slot;
found = (config_found_sm((struct device *)sc, &pa, pckbcprint,
force ? pckbc_submatch_locators : pckbc_submatch) != NULL);
if ((found || slot == PCKBC_AUX_SLOT) && !t->t_slotdata[slot]) {
t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
M_DEVBUF, M_NOWAIT);
if (t->t_slotdata[slot] == NULL)
return 0;
pckbc_init_slotdata(t->t_slotdata[slot]);
if (!found && slot == PCKBC_AUX_SLOT) {
/*
* Some machines don't handle disabling the aux slot
* completely and still generate data when the mouse is
* moved, so setup a dummy interrupt handler to discard
* this slot's data.
*/
pckbc_set_inputhandler(t, PCKBC_AUX_SLOT, NULL, sc,
NULL);
found = 1;
}
}
return (found);
}
void
pckbc_attach(struct pckbc_softc *sc, int flags)
{
struct pckbc_internal *t;
bus_space_tag_t iot;
bus_space_handle_t ioh_d, ioh_c;
int haskbd = 0, res;
u_char cmdbits = 0;
t = sc->id;
iot = t->t_iot;
ioh_d = t->t_ioh_d;
ioh_c = t->t_ioh_c;
if (pckbc_console == 0) {
timeout_set(&t->t_cleanup, pckbc_cleanup, t);
timeout_set(&t->t_poll, pckbc_poll, t);
}
/* flush */
(void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
/* set initial cmd byte */
if (!pckbc_put8042cmd(t)) {
#if defined(__i386__) || defined(__amd64__)
if (!ISSET(flags, PCKBCF_FORCE_KEYBOARD_PRESENT)) {
pckbc_release_console();
return;
}
#endif
printf("kbc: cmd word write error\n");
return;
}
/*
* XXX Don't check the keyboard port. There are broken keyboard controllers
* which don't pass the test but work normally otherwise.
*/
#if 0
/*
* check kbd port ok
*/
if (!pckbc_send_cmd(iot, ioh_c, KBC_KBDTEST))
return;
res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
/*
* Normally, we should get a "0" here.
* But there are keyboard controllers behaving differently.
*/
if (res == 0 || res == 0xfa || res == 0x01 || res == 0xab) {
#ifdef PCKBCDEBUG
if (res != 0)
printf("kbc: returned %x on kbd slot test\n", res);
#endif
if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT, 0)) {
cmdbits |= KC8_KENABLE;
haskbd = 1;
}
} else {
printf("kbc: kbd port test: %x\n", res);
return;
}
#else
if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT, 0)) {
cmdbits |= KC8_KENABLE;
haskbd = 1;
}
#endif /* 0 */
/*
* Check aux port ok.
* Avoid KBC_AUXTEST because it hangs some older controllers
* (eg UMC880?).
*/
if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXECHO)) {
printf("kbc: aux echo error 1\n");
goto nomouse;
}
if (!pckbc_wait_output(iot, ioh_c)) {
printf("kbc: aux echo error 2\n");
goto nomouse;
}
bus_space_write_1(iot, ioh_d, 0, 0x5a); /* a random value */
res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_AUX_SLOT, 1);
if (ISSET(t->t_flags, PCKBC_NEED_AUXWRITE)) {
/*
* The following code is necessary to find the aux port on the
* oqo-1 machine, among others. However if confuses old
* (non-ps/2) keyboard controllers (at least UMC880x again).
*/
if (res == -1) {
/* Read of aux echo timed out, try again */
if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
goto nomouse;
if (!pckbc_wait_output(iot, ioh_c))
goto nomouse;
bus_space_write_1(iot, ioh_d, 0, 0x5a);
res = pckbc_poll_data1(iot, ioh_d, ioh_c,
PCKBC_AUX_SLOT, 1);
DPRINTF("kbc: aux echo: %x\n", res);
}
}
if (res != -1) {
/*
* In most cases, the 0x5a gets echoed.
* Some old controllers (Gateway 2000 circa 1993)
* return 0xfe here.
* We are satisfied if there is anything in the
* aux output buffer.
*/
DPRINTF("kbc: aux echo: %x\n", res);
t->t_haveaux = 1;
if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT, 0))
cmdbits |= KC8_MENABLE;
}
#ifdef PCKBCDEBUG
else
printf("kbc: aux echo test failed\n");
#endif
#if defined(__i386__) || defined(__amd64__)
if (haskbd == 0 && !ISSET(flags, PCKBCF_FORCE_KEYBOARD_PRESENT)) {
if (t->t_haveaux) {
if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT, 1))
cmdbits |= KC8_KENABLE;
} else {
pckbc_release_console();
}
}
#endif
nomouse:
/* enable needed interrupts */
t->t_cmdbyte |= cmdbits;
if (!pckbc_put8042cmd(t))
printf("kbc: cmd word write error\n");
}
int
pckbcprint(void *aux, const char *pnp)
{
struct pckbc_attach_args *pa = aux;
if (!pnp)
printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
return (QUIET);
}
void
pckbc_release_console(void)
{
#if defined(__i386__) || defined(__amd64__)
/*
* If there is no keyboard present, yet we are the console,
* we might be on a legacy-free PC where the PS/2 emulated
* keyboard was elected as console, but went away as soon
* as the USB controller drivers attached.
*
* In that case, we want to release ourselves from console
* duties, unless we have been able to attach a mouse,
* which would mean this is a real PS/2 controller
* afterwards.
*/
if (pckbc_console != 0) {
extern void wscn_input_init(int);
pckbc_console = 0;
wscn_input_init(1);
}
#endif
}
void
pckbc_init_slotdata(struct pckbc_slotdata *q)
{
int i;
TAILQ_INIT(&q->cmdqueue);
TAILQ_INIT(&q->freequeue);
for (i = 0; i < NCMD; i++) {
TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
}
q->polling = 0;
}
void
pckbc_flush(pckbc_tag_t self, pckbc_slot_t slot)
{
struct pckbc_internal *t = self;
(void) pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
slot, t->t_haveaux);
}
int
pckbc_poll_data(pckbc_tag_t self, pckbc_slot_t slot)
{
struct pckbc_internal *t = self;
struct pckbc_slotdata *q = t->t_slotdata[slot];
int c;
c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
slot, t->t_haveaux);
if (c != -1 && q && CMD_IN_QUEUE(q)) {
/* we jumped into a running command - try to
deliver the response */
if (pckbc_cmdresponse(t, slot, c))
return (-1);
}
return (c);
}
/*
* set scancode translation on
*/
int
pckbc_xt_translation(pckbc_tag_t self)
{
struct pckbc_internal *t = self;
if (ISSET(t->t_flags, PCKBC_CANT_TRANSLATE))
return (-1);
if (t->t_cmdbyte & KC8_TRANS)
return (0);
t->t_cmdbyte |= KC8_TRANS;
if (!pckbc_put8042cmd(t))
return (-1);
/* read back to be sure */
if (!pckbc_get8042cmd(t))
return (-1);
return (t->t_cmdbyte & KC8_TRANS) ? (0) : (-1);
}
static struct pckbc_portcmd {
u_char cmd_en, cmd_dis;
} pckbc_portcmd[2] = {
{
KBC_KBDENABLE, KBC_KBDDISABLE,
}, {
KBC_AUXENABLE, KBC_AUXDISABLE,
}
};
void
pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on)
{
struct pckbc_internal *t = (struct pckbc_internal *)self;
struct pckbc_portcmd *cmd;
cmd = &pckbc_portcmd[slot];
if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c,
on ? cmd->cmd_en : cmd->cmd_dis))
printf("pckbc_slot_enable(%d) failed\n", on);
if (slot == PCKBC_KBD_SLOT) {
if (on)
timeout_add_sec(&t->t_poll, 1);
else
timeout_del(&t->t_poll);
}
}
void
pckbc_set_poll(pckbc_tag_t self, pckbc_slot_t slot, int on)
{
struct pckbc_internal *t = (struct pckbc_internal *)self;
t->t_slotdata[slot]->polling = on;
if (!on) {
int s;
/*
* If disabling polling on a device that's been configured,
* make sure there are no bytes left in the FIFO, holding up
* the interrupt line. Otherwise we won't get any further
* interrupts.
*/
if (t->t_sc) {
s = spltty();
pckbcintr_internal(t, t->t_sc);
splx(s);
}
}
}
/*
* Pass command to device, poll for ACK and data.
* to be called at spltty()
*/
static void
pckbc_poll_cmd1(struct pckbc_internal *t, pckbc_slot_t slot,
struct pckbc_devcmd *cmd)
{
bus_space_tag_t iot = t->t_iot;
bus_space_handle_t ioh_d = t->t_ioh_d;
bus_space_handle_t ioh_c = t->t_ioh_c;
int i, c = 0;
while (cmd->cmdidx < cmd->cmdlen) {
if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
printf("pckbc_cmd: send error\n");
cmd->status = EIO;
return;
}
for (i = 10; i; i--) { /* 1s ??? */
c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
t->t_haveaux);
if (c != -1)
break;
}
switch (c) {
case KBC_DEVCMD_ACK:
cmd->cmdidx++;
continue;
/*
* Some legacy free PCs keep returning Basic Assurance Test
* (BAT) instead of something usable, so fail gracefully.
*/
case KBC_DEVCMD_RESEND:
case KBC_DEVCMD_BAT_DONE:
case KBC_DEVCMD_BAT_FAIL:
DPRINTF("pckbc_cmd: %s\n",
c == KBC_DEVCMD_RESEND ? "RESEND": "BAT");
if (cmd->retries++ < 5)
continue;
DPRINTF("pckbc_cmd: cmd failed\n");
cmd->status = ENXIO;
return;
case -1:
DPRINTF("pckbc_cmd: timeout\n");
cmd->status = EIO;
return;
default:
DPRINTF("pckbc_cmd: lost 0x%x\n", c);
}
}
while (cmd->responseidx < cmd->responselen) {
if (cmd->flags & KBC_CMDFLAG_SLOW)
i = 100; /* 10s ??? */
else
i = 10; /* 1s ??? */
while (i--) {
c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
t->t_haveaux);
if (c != -1)
break;
}
if (c == -1) {
DPRINTF("pckbc_cmd: no data\n");
cmd->status = ETIMEDOUT;
return;
} else
cmd->response[cmd->responseidx++] = c;
}
}
/* for use in autoconfiguration */
int
pckbc_poll_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
int responselen, u_char *respbuf, int slow)
{
struct pckbc_devcmd nc;
if ((len > 4) || (responselen > 4))
return (EINVAL);
bzero(&nc, sizeof(nc));
memcpy(nc.cmd, cmd, len);
nc.cmdlen = len;
nc.responselen = responselen;
nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
pckbc_poll_cmd1(self, slot, &nc);
if (nc.status == 0 && respbuf)
memcpy(respbuf, nc.response, responselen);
return (nc.status);
}
/*
* Clean up a command queue, throw away everything.
*/
void
pckbc_cleanqueue(struct pckbc_slotdata *q)
{
struct pckbc_devcmd *cmd;
#ifdef PCKBCDEBUG
int i;
#endif
while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
TAILQ_REMOVE(&q->cmdqueue, cmd, next);
cmd->flags &= ~KBC_CMDFLAG_QUEUED;
#ifdef PCKBCDEBUG
printf("pckbc_cleanqueue: removing");
for (i = 0; i < cmd->cmdlen; i++)
printf(" %02x", cmd->cmd[i]);
printf("\n");
#endif
/*
* A synchronous command on the cmdqueue is currently owned by a
* sleeping proc. The same proc is responsible for putting it
* back on the freequeue once awake.
*/
if (cmd->flags & KBC_CMDFLAG_SYNC)
continue;
TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
}
}
void
pckbc_cleanqueues(struct pckbc_internal *t)
{
if (t->t_slotdata[PCKBC_KBD_SLOT])
pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
if (t->t_slotdata[PCKBC_AUX_SLOT])
pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
}
/*
* Timeout error handler: clean queues and data port.
* XXX could be less invasive.
*/
void
pckbc_cleanup(void *self)
{
struct pckbc_internal *t = self;
int s;
printf("pckbc: command timeout\n");
s = spltty();
pckbc_cleanqueues(t);
while (bus_space_read_1(t->t_iot, t->t_ioh_c, 0) & KBS_DIB) {
KBD_DELAY;
(void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
}
/* reset KBC? */
splx(s);
}
/*
* Stop the keyboard controller when we are going to suspend
*/
void
pckbc_stop(struct pckbc_softc *sc)
{
struct pckbc_internal *t = sc->id;
timeout_del(&t->t_poll);
pckbc_cleanqueues(t);
timeout_del(&t->t_cleanup);
}
/*
* Reset the keyboard controller in a violent fashion; normally done
* after suspend/resume when we do not trust the machine.
*/
void
pckbc_reset(struct pckbc_softc *sc)
{
struct pckbc_internal *t = sc->id;
bus_space_tag_t iot = t->t_iot;
bus_space_handle_t ioh_d = t->t_ioh_d, ioh_c = t->t_ioh_c;
pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
/* KBC selftest */
if (pckbc_send_cmd(iot, ioh_c, KBC_SELFTEST) == 0)
return;
pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
(void)pckbc_put8042cmd(t);
pckbcintr_internal(t->t_sc->id, t->t_sc);
}
/*
* Pass command to device during normal operation.
* to be called at spltty()
*/
void
pckbc_start(struct pckbc_internal *t, pckbc_slot_t slot)
{
struct pckbc_slotdata *q = t->t_slotdata[slot];
struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
if (q->polling) {
do {
pckbc_poll_cmd1(t, slot, cmd);
if (cmd->status)
printf("pckbc_start: command error\n");
TAILQ_REMOVE(&q->cmdqueue, cmd, next);
cmd->flags &= ~KBC_CMDFLAG_QUEUED;
if (cmd->flags & KBC_CMDFLAG_SYNC) {
wakeup(cmd);
} else {
timeout_del(&t->t_cleanup);
TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
}
cmd = TAILQ_FIRST(&q->cmdqueue);
} while (cmd);
return;
}
if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
printf("pckbc_start: send error\n");
/* XXX what now? */
return;
}
}
/*
* Handle command responses coming in asynchronously,
* return nonzero if valid response.
* to be called at spltty()
*/
int
pckbc_cmdresponse(struct pckbc_internal *t, pckbc_slot_t slot, u_char data)
{
struct pckbc_slotdata *q = t->t_slotdata[slot];
struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
#ifdef DIAGNOSTIC
if (!cmd)
panic("pckbc_cmdresponse: no active command");
#endif
if (cmd->cmdidx < cmd->cmdlen) {
if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
return (0);
if (data == KBC_DEVCMD_RESEND) {
if (cmd->retries++ < 5) {
/* try again last command */
goto restart;
} else {
DPRINTF("pckbc: cmd failed\n");
cmd->status = ENXIO;
/* dequeue */
}
} else {
if (++cmd->cmdidx < cmd->cmdlen)
goto restart;
if (cmd->responselen)
return (1);
/* else dequeue */
}
} else if (cmd->responseidx < cmd->responselen) {
cmd->response[cmd->responseidx++] = data;
if (cmd->responseidx < cmd->responselen)
return (1);
/* else dequeue */
} else
return (0);
/* dequeue: */
TAILQ_REMOVE(&q->cmdqueue, cmd, next);
cmd->flags &= ~KBC_CMDFLAG_QUEUED;
if (cmd->flags & KBC_CMDFLAG_SYNC) {
wakeup(cmd);
} else {
timeout_del(&t->t_cleanup);
TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
}
cmd = TAILQ_FIRST(&q->cmdqueue);
if (cmd == NULL)
return (1);
restart:
pckbc_start(t, slot);
return (1);
}
/*
* Put command into the device's command queue, return zero or errno.
*/
int
pckbc_enqueue_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
int responselen, int sync, u_char *respbuf)
{
struct pckbc_internal *t = self;
struct pckbc_slotdata *q = t->t_slotdata[slot];
struct pckbc_devcmd *nc;
int s, isactive, res = 0;
if ((len > 4) || (responselen > 4))
return (EINVAL);
s = spltty();
nc = TAILQ_FIRST(&q->freequeue);
if (nc) {
TAILQ_REMOVE(&q->freequeue, nc, next);
}
splx(s);
if (!nc)
return (ENOMEM);
bzero(nc, sizeof(*nc));
memcpy(nc->cmd, cmd, len);
nc->cmdlen = len;
nc->responselen = responselen;
nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
s = spltty();
if (q->polling && sync) {
/*
* XXX We should poll until the queue is empty.
* But we don't come here normally, so make
* it simple and throw away everything.
*/
pckbc_cleanqueue(q);
}
isactive = CMD_IN_QUEUE(q);
nc->flags |= KBC_CMDFLAG_QUEUED;
TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
if (!isactive)
pckbc_start(t, slot);
if (q->polling)
res = (sync ? nc->status : 0);
else if (sync) {
if ((res = tsleep_nsec(nc, 0, "kbccmd", SEC_TO_NSEC(1)))) {
pckbc_cleanup(t);
} else {
/*
* Under certain circumstances, such as during suspend,
* tsleep() becomes a no-op and the command is left on
* the cmdqueue.
*/
if (nc->flags & KBC_CMDFLAG_QUEUED) {
TAILQ_REMOVE(&q->cmdqueue, nc, next);
nc->flags &= ~KBC_CMDFLAG_QUEUED;
}
res = nc->status;
}
} else
timeout_add_sec(&t->t_cleanup, 1);
if (sync) {
if (respbuf)
memcpy(respbuf, nc->response, responselen);
TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
}
splx(s);
return (res);
}
void
pckbc_set_inputhandler(pckbc_tag_t self, pckbc_slot_t slot, pckbc_inputfcn func,
void *arg, char *name)
{
struct pckbc_internal *t = (struct pckbc_internal *)self;
struct pckbc_softc *sc = t->t_sc;
if (slot >= PCKBC_NSLOTS)
panic("pckbc_set_inputhandler: bad slot %d", slot);
sc->inputhandler[slot] = func;
sc->inputarg[slot] = arg;
sc->subname[slot] = name;
if (pckbc_console && slot == PCKBC_KBD_SLOT)
timeout_add_sec(&t->t_poll, 1);
}
void
pckbc_poll(void *v)
{
struct pckbc_internal *t = v;
int s;
s = spltty();
(void)pckbcintr_internal(t, t->t_sc);
timeout_add_sec(&t->t_poll, 1);
splx(s);
}
int
pckbcintr(void *vsc)
{
struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
return (pckbcintr_internal(sc->id, sc));
}
int
pckbcintr_internal(struct pckbc_internal *t, struct pckbc_softc *sc)
{
u_char stat;
pckbc_slot_t slot;
struct pckbc_slotdata *q;
int served = 0, data;
/* reschedule timeout further into the idle times */
if (timeout_pending(&t->t_poll))
timeout_add_sec(&t->t_poll, 1);
for(;;) {
stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
if (!(stat & KBS_DIB))
break;
served = 1;
slot = (t->t_haveaux && (stat & KBS_AUXDATA)) ?
PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
q = t->t_slotdata[slot];
if (!q) {
/* XXX do something for live insertion? */
#ifdef PCKBCDEBUG
printf("pckbcintr: no dev for slot %d\n", slot);
#endif
KBD_DELAY;
(void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
continue;
}
if (q->polling)
break; /* pckbc_poll_data() will get it */
KBD_DELAY;
data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
continue;
if (sc != NULL) {
if (sc->inputhandler[slot])
(*sc->inputhandler[slot])(sc->inputarg[slot],
data);
#ifdef PCKBCDEBUG
else
printf("pckbcintr: slot %d lost %d\n",
slot, data);
#endif
}
}
return (served);
}
int
pckbc_cnattach(bus_space_tag_t iot, bus_addr_t addr, bus_size_t cmd_offset,
int flags)
{
bus_space_handle_t ioh_d, ioh_c;
int res = 0;
if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d))
return (ENXIO);
if (bus_space_map(iot, addr + cmd_offset, 1, 0, &ioh_c)) {
bus_space_unmap(iot, ioh_d, 1);
return (ENXIO);
}
pckbc_consdata.t_iot = iot;
pckbc_consdata.t_ioh_d = ioh_d;
pckbc_consdata.t_ioh_c = ioh_c;
pckbc_consdata.t_addr = addr;
pckbc_consdata.t_flags = flags;
timeout_set(&pckbc_consdata.t_cleanup, pckbc_cleanup, &pckbc_consdata);
timeout_set(&pckbc_consdata.t_poll, pckbc_poll, &pckbc_consdata);
/* flush */
(void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
/* selftest? */
/* init cmd byte, enable ports */
pckbc_consdata.t_cmdbyte = KC8_CPU;
if (!pckbc_put8042cmd(&pckbc_consdata)) {
printf("kbc: cmd word write error\n");
res = EIO;
}
if (!res) {
#if (NPCKBD > 0)
res = pckbd_cnattach(&pckbc_consdata);
#else
res = ENXIO;
#endif /* NPCKBD > 0 */
}
if (res) {
bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1);
bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1);
} else {
pckbc_consdata.t_slotdata[PCKBC_KBD_SLOT] = &pckbc_cons_slotdata;
pckbc_init_slotdata(&pckbc_cons_slotdata);
pckbc_console = 1;
}
return (res);
}
struct cfdriver pckbc_cd = {
NULL, "pckbc", DV_DULL
};
234
235
5
7
7
7
7
7
7
7
7
7
694
7
309
1
310
605
7
607
308
7
176
175
1
3
10
2
8
8
8
2
10
3
2
8
8
1
5
7
6
2
5
5
1
6
4
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
/* $OpenBSD: rnd.c,v 1.223 2022/02/06 17:24:58 rob Exp $ */
/*
* Copyright (c) 2011,2020 Theo de Raadt.
* Copyright (c) 2008 Damien Miller.
* Copyright (c) 1996, 1997, 2000-2002 Michael Shalayeff.
* Copyright (c) 2013 Markus Friedl.
* Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* ALTERNATIVELY, this product may be distributed under the terms of
* the GNU Public License, in which case the provisions of the GPL are
* required INSTEAD OF the above restrictions. (This clause is
* necessary due to a potential bad interaction between the GPL and
* the restrictions contained in a BSD-style copyright.)
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* The bootblocks pre-fill the kernel .openbsd.randomdata section with seed
* material (on-disk from previous boot, hopefully mixed with a hardware rng).
* The first arc4random(9) call initializes this seed material as a chacha
* state. Calls can be done early in kernel bootstrap code -- early use is
* encouraged.
*
* After the kernel timeout subsystem is initialized, random_start() prepares
* the entropy collection mechanism enqueue_randomness() and timeout-driven
* mixing into the chacha state. The first submissions come from device
* probes, later on interrupt-time submissions are more common. Entropy
* data (and timing information) get mixed over the entropy input ring
* rnd_event_space[] -- the goal is to collect damage.
*
* Based upon timeouts, a selection of the entropy ring rnd_event_space[]
* CRC bit-distributed and XOR mixed into entropy_pool[].
*
* From time to time, entropy_pool[] is SHA512-whitened, mixed with time
* information again, XOR'd with the inner and outer states of the existing
* chacha state, to create a new chacha state.
*
* During early boot (until cold=0), enqueue operations are immediately
* dequeued, and mixed into the chacha.
*/
#include <sys/param.h>
#include <sys/event.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/timeout.h>
#include <sys/atomic.h>
#include <sys/task.h>
#include <sys/msgbuf.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <crypto/sha2.h>
#define KEYSTREAM_ONLY
#include <crypto/chacha_private.h>
#include <uvm/uvm_extern.h>
/*
* For the purposes of better mixing, we use the CRC-32 polynomial as
* well to make a twisted Generalized Feedback Shift Register
*
* (See M. Matsumoto & Y. Kurita, 1992. Twisted GFSR generators. ACM
* Transactions on Modeling and Computer Simulation 2(3):179-194.
* Also see M. Matsumoto & Y. Kurita, 1994. Twisted GFSR generators
* II. ACM Transactions on Modeling and Computer Simulation 4:254-266)
*/
/*
* Stirring polynomials over GF(2) for various pool sizes. Used in
* add_entropy_words() below.
*
* The polynomial terms are chosen to be evenly spaced (minimum RMS
* distance from evenly spaced; except for the last tap, which is 1 to
* get the twisting happening as fast as possible.
*
* The resultant polynomial is:
* 2^POOLWORDS + 2^POOL_TAP1 + 2^POOL_TAP2 + 2^POOL_TAP3 + 2^POOL_TAP4 + 1
*/
#define POOLWORDS 2048
#define POOLBYTES (POOLWORDS*4)
#define POOLMASK (POOLWORDS - 1)
#define POOL_TAP1 1638
#define POOL_TAP2 1231
#define POOL_TAP3 819
#define POOL_TAP4 411
/*
* Raw entropy collection from device drivers; at interrupt context or not.
* enqueue_randomness() is used to submit data into the entropy input ring.
*/
#define QEVLEN 128 /* must be a power of 2 */
#define QEVCONSUME 8 /* how many events to consume a time */
#define KEYSZ 32
#define IVSZ 8
#define BLOCKSZ 64
#define RSBUFSZ (16*BLOCKSZ)
#define EBUFSIZE KEYSZ + IVSZ
struct rand_event {
u_int re_time;
u_int re_val;
} rnd_event_space[QEVLEN];
u_int rnd_event_cons;
u_int rnd_event_prod;
int rnd_cold = 1;
int rnd_slowextract = 1;
void rnd_reinit(void *v); /* timeout to start reinit */
void rnd_init(void *); /* actually do the reinit */
static u_int32_t entropy_pool[POOLWORDS];
u_int32_t entropy_pool0[POOLWORDS] __attribute__((section(".openbsd.randomdata")));
void dequeue_randomness(void *);
void add_entropy_words(const u_int32_t *, u_int);
void extract_entropy(u_int8_t *)
__attribute__((__bounded__(__minbytes__,1,EBUFSIZE)));
struct timeout rnd_timeout = TIMEOUT_INITIALIZER(dequeue_randomness, NULL);
int filt_randomread(struct knote *, long);
void filt_randomdetach(struct knote *);
int filt_randomwrite(struct knote *, long);
static void _rs_seed(u_char *, size_t);
static void _rs_clearseed(const void *p, size_t s);
const struct filterops randomread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_randomdetach,
.f_event = filt_randomread,
};
const struct filterops randomwrite_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_randomdetach,
.f_event = filt_randomwrite,
};
/*
* This function mixes entropy and timing into the entropy input ring.
*/
void
enqueue_randomness(u_int val)
{
struct rand_event *rep;
int e;
e = (atomic_inc_int_nv(&rnd_event_prod) - 1) & (QEVLEN-1);
rep = &rnd_event_space[e];
rep->re_time += cpu_rnd_messybits();
rep->re_val += val;
if (rnd_cold) {
dequeue_randomness(NULL);
rnd_init(NULL);
if (!cold)
rnd_cold = 0;
} else if (!timeout_pending(&rnd_timeout) &&
(rnd_event_prod - rnd_event_cons) > QEVCONSUME) {
rnd_slowextract = min(rnd_slowextract * 2, 5000);
timeout_add_msec(&rnd_timeout, rnd_slowextract * 10);
}
}
/*
* This function merges entropy ring information into the buffer using
* a polynomial to spread the bits.
*/
void
add_entropy_words(const u_int32_t *buf, u_int n)
{
/* derived from IEEE 802.3 CRC-32 */
static const u_int32_t twist_table[8] = {
0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
};
static u_int entropy_add_ptr;
static u_char entropy_input_rotate;
for (; n--; buf++) {
u_int32_t w = (*buf << entropy_input_rotate) |
(*buf >> ((32 - entropy_input_rotate) & 31));
u_int i = entropy_add_ptr =
(entropy_add_ptr - 1) & POOLMASK;
/*
* Normally, we add 7 bits of rotation to the pool.
* At the beginning of the pool, add an extra 7 bits
* rotation, so that successive passes spread the
* input bits across the pool evenly.
*/
entropy_input_rotate =
(entropy_input_rotate + (i ? 7 : 14)) & 31;
/* XOR pool contents corresponding to polynomial terms */
w ^= entropy_pool[(i + POOL_TAP1) & POOLMASK] ^
entropy_pool[(i + POOL_TAP2) & POOLMASK] ^
entropy_pool[(i + POOL_TAP3) & POOLMASK] ^
entropy_pool[(i + POOL_TAP4) & POOLMASK] ^
entropy_pool[(i + 1) & POOLMASK] ^
entropy_pool[i]; /* + 2^POOLWORDS */
entropy_pool[i] = (w >> 3) ^ twist_table[w & 7];
}
}
/*
* Pulls entropy out of the queue and merges it into the pool with the
* CRC. This takes a mix of fresh entries from the producer end of the
* queue and entries from the consumer end of the queue which are
* likely to have collected more damage.
*/
/* ARGSUSED */
void
dequeue_randomness(void *v)
{
u_int32_t buf[2];
u_int startp, startc, i;
if (!rnd_cold)
timeout_del(&rnd_timeout);
/* Some very new damage */
startp = rnd_event_prod - QEVCONSUME;
for (i = 0; i < QEVCONSUME; i++) {
u_int e = (startp + i) & (QEVLEN-1);
buf[0] = rnd_event_space[e].re_time;
buf[1] = rnd_event_space[e].re_val;
add_entropy_words(buf, 2);
}
/* and some probably more damaged */
startc = rnd_event_cons;
for (i = 0; i < QEVCONSUME; i++) {
u_int e = (startc + i) & (QEVLEN-1);
buf[0] = rnd_event_space[e].re_time;
buf[1] = rnd_event_space[e].re_val;
add_entropy_words(buf, 2);
}
rnd_event_cons = startp + QEVCONSUME;
}
/*
* Grabs a chunk from the entropy_pool[] and slams it through SHA512 when
* requested.
*/
void
extract_entropy(u_int8_t *buf)
{
static u_int32_t extract_pool[POOLWORDS];
u_char digest[SHA512_DIGEST_LENGTH];
SHA2_CTX shactx;
#if SHA512_DIGEST_LENGTH < EBUFSIZE
#error "need more bigger hash output"
#endif
/*
* INTENTIONALLY not protected by any lock. Races during
* memcpy() result in acceptable input data; races during
* SHA512Update() would create nasty data dependencies. We
* do not rely on this as a benefit, but if it happens, cool.
*/
memcpy(extract_pool, entropy_pool, sizeof(extract_pool));
/* Hash the pool to get the output */
SHA512Init(&shactx);
SHA512Update(&shactx, (u_int8_t *)extract_pool, sizeof(extract_pool));
SHA512Final(digest, &shactx);
/* Copy data to destination buffer */
memcpy(buf, digest, EBUFSIZE);
/*
* Modify pool so next hash will produce different results.
* During boot-time enqueue/dequeue stage, avoid recursion.
*/
if (!rnd_cold)
enqueue_randomness(extract_pool[0]);
dequeue_randomness(NULL);
/* Wipe data from memory */
explicit_bzero(extract_pool, sizeof(extract_pool));
explicit_bzero(digest, sizeof(digest));
}
/* random keystream by ChaCha */
struct mutex rndlock = MUTEX_INITIALIZER(IPL_HIGH);
struct timeout rndreinit_timeout = TIMEOUT_INITIALIZER(rnd_reinit, NULL);
struct task rnd_task = TASK_INITIALIZER(rnd_init, NULL);
static chacha_ctx rs; /* chacha context for random keystream */
/* keystream blocks (also chacha seed from boot) */
static u_char rs_buf[RSBUFSZ];
u_char rs_buf0[RSBUFSZ] __attribute__((section(".openbsd.randomdata")));
static size_t rs_have; /* valid bytes at end of rs_buf */
static size_t rs_count; /* bytes till reseed */
void
suspend_randomness(void)
{
struct timespec ts;
getnanotime(&ts);
enqueue_randomness(ts.tv_sec);
enqueue_randomness(ts.tv_nsec);
dequeue_randomness(NULL);
rs_count = 0;
arc4random_buf(entropy_pool, sizeof(entropy_pool));
}
void
resume_randomness(char *buf, size_t buflen)
{
struct timespec ts;
if (buf && buflen)
_rs_seed(buf, buflen);
getnanotime(&ts);
enqueue_randomness(ts.tv_sec);
enqueue_randomness(ts.tv_nsec);
dequeue_randomness(NULL);
rs_count = 0;
}
static inline void _rs_rekey(u_char *dat, size_t datlen);
static inline void
_rs_init(u_char *buf, size_t n)
{
KASSERT(n >= KEYSZ + IVSZ);
chacha_keysetup(&rs, buf, KEYSZ * 8);
chacha_ivsetup(&rs, buf + KEYSZ, NULL);
}
static void
_rs_seed(u_char *buf, size_t n)
{
_rs_rekey(buf, n);
/* invalidate rs_buf */
rs_have = 0;
memset(rs_buf, 0, sizeof(rs_buf));
rs_count = 1600000;
}
static void
_rs_stir(int do_lock)
{
struct timespec ts;
u_int8_t buf[EBUFSIZE], *p;
int i;
/*
* Use SHA512 PRNG data and a system timespec; early in the boot
* process this is the best we can do -- some architectures do
* not collect entropy very well during this time, but may have
* clock information which is better than nothing.
*/
extract_entropy(buf);
nanotime(&ts);
for (p = (u_int8_t *)&ts, i = 0; i < sizeof(ts); i++)
buf[i] ^= p[i];
if (do_lock)
mtx_enter(&rndlock);
_rs_seed(buf, sizeof(buf));
if (do_lock)
mtx_leave(&rndlock);
explicit_bzero(buf, sizeof(buf));
/* encourage fast-dequeue again */
rnd_slowextract = 1;
}
static inline void
_rs_stir_if_needed(size_t len)
{
static int rs_initialized;
if (!rs_initialized) {
memcpy(entropy_pool, entropy_pool0, sizeof(entropy_pool));
memcpy(rs_buf, rs_buf0, sizeof(rs_buf));
/* seeds cannot be cleaned yet, random_start() will do so */
_rs_init(rs_buf, KEYSZ + IVSZ);
rs_count = 1024 * 1024 * 1024; /* until main() runs */
rs_initialized = 1;
} else if (rs_count <= len)
_rs_stir(0);
else
rs_count -= len;
}
static void
_rs_clearseed(const void *p, size_t s)
{
struct kmem_dyn_mode kd_avoidalias;
vaddr_t va = trunc_page((vaddr_t)p);
vsize_t off = (vaddr_t)p - va;
vsize_t len;
vaddr_t rwva;
paddr_t pa;
while (s > 0) {
pmap_extract(pmap_kernel(), va, &pa);
memset(&kd_avoidalias, 0, sizeof(kd_avoidalias));
kd_avoidalias.kd_prefer = pa;
kd_avoidalias.kd_waitok = 1;
rwva = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any, &kp_none,
&kd_avoidalias);
if (!rwva)
panic("_rs_clearseed");
pmap_kenter_pa(rwva, pa, PROT_READ | PROT_WRITE);
pmap_update(pmap_kernel());
len = MIN(s, PAGE_SIZE - off);
explicit_bzero((void *)(rwva + off), len);
pmap_kremove(rwva, PAGE_SIZE);
km_free((void *)rwva, PAGE_SIZE, &kv_any, &kp_none);
va += PAGE_SIZE;
s -= len;
off = 0;
}
}
static inline void
_rs_rekey(u_char *dat, size_t datlen)
{
#ifndef KEYSTREAM_ONLY
memset(rs_buf, 0, sizeof(rs_buf));
#endif
/* fill rs_buf with the keystream */
chacha_encrypt_bytes(&rs, rs_buf, rs_buf, sizeof(rs_buf));
/* mix in optional user provided data */
if (dat) {
size_t i, m;
m = MIN(datlen, KEYSZ + IVSZ);
for (i = 0; i < m; i++)
rs_buf[i] ^= dat[i];
}
/* immediately reinit for backtracking resistance */
_rs_init(rs_buf, KEYSZ + IVSZ);
memset(rs_buf, 0, KEYSZ + IVSZ);
rs_have = sizeof(rs_buf) - KEYSZ - IVSZ;
}
static inline void
_rs_random_buf(void *_buf, size_t n)
{
u_char *buf = (u_char *)_buf;
size_t m;
_rs_stir_if_needed(n);
while (n > 0) {
if (rs_have > 0) {
m = MIN(n, rs_have);
memcpy(buf, rs_buf + sizeof(rs_buf) - rs_have, m);
memset(rs_buf + sizeof(rs_buf) - rs_have, 0, m);
buf += m;
n -= m;
rs_have -= m;
}
if (rs_have == 0)
_rs_rekey(NULL, 0);
}
}
static inline void
_rs_random_u32(u_int32_t *val)
{
_rs_stir_if_needed(sizeof(*val));
if (rs_have < sizeof(*val))
_rs_rekey(NULL, 0);
memcpy(val, rs_buf + sizeof(rs_buf) - rs_have, sizeof(*val));
memset(rs_buf + sizeof(rs_buf) - rs_have, 0, sizeof(*val));
rs_have -= sizeof(*val);
}
/* Return one word of randomness from a ChaCha20 generator */
u_int32_t
arc4random(void)
{
u_int32_t ret;
mtx_enter(&rndlock);
_rs_random_u32(&ret);
mtx_leave(&rndlock);
return ret;
}
/*
* Fill a buffer of arbitrary length with ChaCha20-derived randomness.
*/
void
arc4random_buf(void *buf, size_t n)
{
mtx_enter(&rndlock);
_rs_random_buf(buf, n);
mtx_leave(&rndlock);
}
/*
* Allocate a new ChaCha20 context for the caller to use.
*/
struct arc4random_ctx *
arc4random_ctx_new(void)
{
char keybuf[KEYSZ + IVSZ];
chacha_ctx *ctx = malloc(sizeof(chacha_ctx), M_TEMP, M_WAITOK);
arc4random_buf(keybuf, KEYSZ + IVSZ);
chacha_keysetup(ctx, keybuf, KEYSZ * 8);
chacha_ivsetup(ctx, keybuf + KEYSZ, NULL);
explicit_bzero(keybuf, sizeof(keybuf));
return (struct arc4random_ctx *)ctx;
}
/*
* Free a ChaCha20 context created by arc4random_ctx_new()
*/
void
arc4random_ctx_free(struct arc4random_ctx *ctx)
{
explicit_bzero(ctx, sizeof(chacha_ctx));
free(ctx, M_TEMP, sizeof(chacha_ctx));
}
/*
* Use a given ChaCha20 context to fill a buffer
*/
void
arc4random_ctx_buf(struct arc4random_ctx *ctx, void *buf, size_t n)
{
#ifndef KEYSTREAM_ONLY
memset(buf, 0, n);
#endif
chacha_encrypt_bytes((chacha_ctx *)ctx, buf, buf, n);
}
/*
* Calculate a uniformly distributed random number less than upper_bound
* avoiding "modulo bias".
*
* Uniformity is achieved by generating new random numbers until the one
* returned is outside the range [0, 2**32 % upper_bound). This
* guarantees the selected random number will be inside
* [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
* after reduction modulo upper_bound.
*/
u_int32_t
arc4random_uniform(u_int32_t upper_bound)
{
u_int32_t r, min;
if (upper_bound < 2)
return 0;
/* 2**32 % x == (2**32 - x) % x */
min = -upper_bound % upper_bound;
/*
* This could theoretically loop forever but each retry has
* p > 0.5 (worst case, usually far better) of selecting a
* number inside the range we need, so it should rarely need
* to re-roll.
*/
for (;;) {
r = arc4random();
if (r >= min)
break;
}
return r % upper_bound;
}
/* ARGSUSED */
void
rnd_init(void *null)
{
_rs_stir(1);
}
/*
* Called by timeout to mark arc4 for stirring,
*/
void
rnd_reinit(void *v)
{
task_add(systq, &rnd_task);
/* 10 minutes, per dm@'s suggestion */
timeout_add_sec(&rndreinit_timeout, 10 * 60);
}
/*
* Start periodic services inside the random subsystem, which pull
* entropy forward, hash it, and re-seed the random stream as needed.
*/
void
random_start(int goodseed)
{
extern char etext[];
#if !defined(NO_PROPOLICE)
extern long __guard_local;
if (__guard_local == 0)
printf("warning: no entropy supplied by boot loader\n");
#endif
_rs_clearseed(entropy_pool0, sizeof(entropy_pool0));
_rs_clearseed(rs_buf0, sizeof(rs_buf0));
/* Message buffer may contain data from previous boot */
if (msgbufp->msg_magic == MSG_MAGIC)
add_entropy_words((u_int32_t *)msgbufp->msg_bufc,
msgbufp->msg_bufs / sizeof(u_int32_t));
add_entropy_words((u_int32_t *)etext - 32*1024,
8192/sizeof(u_int32_t));
dequeue_randomness(NULL);
rnd_init(NULL);
rnd_reinit(NULL);
if (goodseed)
printf("random: good seed from bootblocks\n");
else {
/* XXX kernel should work harder here */
printf("random: boothowto does not indicate good seed\n");
}
}
int
randomopen(dev_t dev, int flag, int mode, struct proc *p)
{
return 0;
}
int
randomclose(dev_t dev, int flag, int mode, struct proc *p)
{
return 0;
}
/*
* Maximum number of bytes to serve directly from the main ChaCha
* pool. Larger requests are served from a discrete ChaCha instance keyed
* from the main pool.
*/
#define RND_MAIN_MAX_BYTES 2048
int
randomread(dev_t dev, struct uio *uio, int ioflag)
{
struct arc4random_ctx *lctx = NULL;
size_t total = uio->uio_resid;
u_char *buf;
int ret = 0;
if (uio->uio_resid == 0)
return 0;
buf = malloc(POOLBYTES, M_TEMP, M_WAITOK);
if (total > RND_MAIN_MAX_BYTES)
lctx = arc4random_ctx_new();
while (ret == 0 && uio->uio_resid > 0) {
size_t n = ulmin(POOLBYTES, uio->uio_resid);
if (lctx != NULL)
arc4random_ctx_buf(lctx, buf, n);
else
arc4random_buf(buf, n);
ret = uiomove(buf, n, uio);
if (ret == 0 && uio->uio_resid > 0)
yield();
}
if (lctx != NULL)
arc4random_ctx_free(lctx);
explicit_bzero(buf, POOLBYTES);
free(buf, M_TEMP, POOLBYTES);
return ret;
}
int
randomwrite(dev_t dev, struct uio *uio, int flags)
{
int ret = 0, newdata = 0;
u_int32_t *buf;
if (uio->uio_resid == 0)
return 0;
buf = malloc(POOLBYTES, M_TEMP, M_WAITOK);
while (ret == 0 && uio->uio_resid > 0) {
size_t n = ulmin(POOLBYTES, uio->uio_resid);
ret = uiomove(buf, n, uio);
if (ret != 0)
break;
while (n % sizeof(u_int32_t))
((u_int8_t *)buf)[n++] = 0;
add_entropy_words(buf, n / 4);
if (uio->uio_resid > 0)
yield();
newdata = 1;
}
if (newdata)
rnd_init(NULL);
explicit_bzero(buf, POOLBYTES);
free(buf, M_TEMP, POOLBYTES);
return ret;
}
int
randomkqfilter(dev_t dev, struct knote *kn)
{
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &randomread_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &randomwrite_filtops;
break;
default:
return (EINVAL);
}
return (0);
}
void
filt_randomdetach(struct knote *kn)
{
}
int
filt_randomread(struct knote *kn, long hint)
{
kn->kn_data = RND_MAIN_MAX_BYTES;
return (1);
}
int
filt_randomwrite(struct knote *kn, long hint)
{
kn->kn_data = POOLBYTES;
return (1);
}
int
randomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
switch (cmd) {
case FIOASYNC:
/* No async flag in softc so this is a no-op. */
break;
case FIONBIO:
/* Handled in the upper FS layer. */
break;
default:
return ENOTTY;
}
return 0;
}
int
sys_getentropy(struct proc *p, void *v, register_t *retval)
{
struct sys_getentropy_args /* {
syscallarg(void *) buf;
syscallarg(size_t) nbyte;
} */ *uap = v;
char buf[256];
int error;
if (SCARG(uap, nbyte) > sizeof(buf))
return (EIO);
arc4random_buf(buf, SCARG(uap, nbyte));
if ((error = copyout(buf, SCARG(uap, buf), SCARG(uap, nbyte))) != 0)
return (error);
explicit_bzero(buf, sizeof(buf));
retval[0] = 0;
return (0);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
/* $OpenBSD: audio.c,v 1.200 2022/07/31 03:31:36 visa Exp $ */
/*
* Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/event.h>
#include <sys/mutex.h>
#include <sys/task.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <dev/mulaw.h>
#include "audio.h"
#include "wskbd.h"
#ifdef AUDIO_DEBUG
#define DPRINTF(...) \
do { \
if (audio_debug) \
printf(__VA_ARGS__); \
} while(0)
#define DPRINTFN(n, ...) \
do { \
if (audio_debug > (n)) \
printf(__VA_ARGS__); \
} while(0)
#else
#define DPRINTF(...) do {} while(0)
#define DPRINTFN(n, ...) do {} while(0)
#endif
#define IPL_SOFTAUDIO IPL_SOFTNET
#define DEVNAME(sc) ((sc)->dev.dv_xname)
#define AUDIO_UNIT(n) (minor(n) & 0x0f)
#define AUDIO_DEV(n) (minor(n) & 0xf0)
#define AUDIO_DEV_AUDIO 0 /* minor of /dev/audio0 */
#define AUDIO_DEV_AUDIOCTL 0xc0 /* minor of /dev/audioctl */
#define AUDIO_BUFSZ 65536 /* buffer size in bytes */
/*
* mixer entries added by the audio(4) layer
*/
#define MIXER_RECORD 0 /* record class */
#define MIXER_RECORD_ENABLE 1 /* record.enable control */
#define MIXER_RECORD_ENABLE_OFF 0 /* record.enable=off value */
#define MIXER_RECORD_ENABLE_ON 1 /* record.enable=on value */
#define MIXER_RECORD_ENABLE_SYSCTL 2 /* record.enable=sysctl val */
/*
* dma buffer
*/
struct audio_buf {
unsigned char *data; /* DMA memory block */
size_t datalen; /* size of DMA memory block */
size_t len; /* size of DMA FIFO */
size_t start; /* first byte used in the FIFO */
size_t used; /* bytes used in the FIFO */
size_t blksz; /* DMA block size */
unsigned int nblks; /* number of blocks */
struct klist klist; /* list of knotes */
unsigned int pos; /* bytes transferred */
unsigned int xrun; /* bytes lost by xruns */
int blocking; /* read/write blocking */
};
#if NWSKBD > 0
struct wskbd_vol
{
int val; /* index of the value control */
int mute; /* index of the mute control */
int step; /* increment/decrement step */
int nch; /* channels in the value control */
int val_pending; /* pending change of val */
int mute_pending; /* pending change of mute */
#define WSKBD_MUTE_TOGGLE 1
#define WSKBD_MUTE_DISABLE 2
#define WSKBD_MUTE_ENABLE 3
};
int wskbd_set_mixervolume_unit(int, long, long);
#endif
/*
* event indicating that a control was changed
*/
struct mixer_ev {
struct mixer_ev *next;
int pending;
};
/*
* device structure
*/
struct audio_softc {
struct device dev;
const struct audio_hw_if *ops; /* driver funcs */
void *cookie; /* wskbd cookie */
void *arg; /* first arg to driver funcs */
int mode; /* bitmask of AUMODE_* */
int quiesce; /* device suspended */
struct audio_buf play, rec;
unsigned int sw_enc; /* user exposed AUDIO_ENCODING_* */
unsigned int hw_enc; /* hardware AUDIO_ENCODING_* */
unsigned int bits; /* bits per sample */
unsigned int bps; /* bytes-per-sample */
unsigned int msb; /* sample are MSB aligned */
unsigned int rate; /* rate in Hz */
unsigned int round; /* block size in frames */
unsigned int pchan, rchan; /* number of channels */
unsigned char silence[4]; /* a sample of silence */
int pause; /* not trying to start DMA */
int active; /* DMA in process */
int offs; /* offset between play & rec dir */
void (*conv_enc)(unsigned char *, int); /* encode to native */
void (*conv_dec)(unsigned char *, int); /* decode to user */
struct mixer_ctrl *mix_ents; /* mixer state for suspend/resume */
int mix_nent; /* size of mixer state */
int mix_isopen; /* mixer open for reading */
int mix_blocking; /* read() blocking */
struct klist mix_klist; /* list of knotes */
struct mixer_ev *mix_evbuf; /* per mixer-control event */
struct mixer_ev *mix_pending; /* list of changed controls */
#if NWSKBD > 0
struct wskbd_vol spkr, mic;
struct task wskbd_task;
#endif
int record_enable; /* mixer record.enable value */
};
int audio_match(struct device *, void *, void *);
void audio_attach(struct device *, struct device *, void *);
int audio_activate(struct device *, int);
int audio_detach(struct device *, int);
void audio_pintr(void *);
void audio_rintr(void *);
void audio_buf_wakeup(struct audio_buf *);
void audio_mixer_wakeup(struct audio_softc *);
#if NWSKBD > 0
void wskbd_mixer_init(struct audio_softc *);
void wskbd_mixer_cb(void *);
#endif
const struct cfattach audio_ca = {
sizeof(struct audio_softc), audio_match, audio_attach,
audio_detach, audio_activate
};
struct cfdriver audio_cd = {
NULL, "audio", DV_DULL
};
void filt_audioctlrdetach(struct knote *);
int filt_audioctlread(struct knote *, long);
int filt_audiomodify(struct kevent *, struct knote *);
int filt_audioprocess(struct knote *, struct kevent *);
const struct filterops audioctlread_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_audioctlrdetach,
.f_event = filt_audioctlread,
.f_modify = filt_audiomodify,
.f_process = filt_audioprocess,
};
void filt_audiowdetach(struct knote *);
int filt_audiowrite(struct knote *, long);
const struct filterops audiowrite_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_audiowdetach,
.f_event = filt_audiowrite,
.f_modify = filt_audiomodify,
.f_process = filt_audioprocess,
};
void filt_audiordetach(struct knote *);
int filt_audioread(struct knote *, long);
const struct filterops audioread_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_audiordetach,
.f_event = filt_audioread,
.f_modify = filt_audiomodify,
.f_process = filt_audioprocess,
};
/*
* This mutex protects data structures (including registers on the
* sound-card) that are manipulated by both the interrupt handler and
* syscall code-paths.
*
* Note that driver methods may sleep (e.g. in malloc); consequently the
* audio layer calls them with the mutex unlocked. Driver methods are
* responsible for locking the mutex when they manipulate data used by
* the interrupt handler and interrupts may occur.
*
* Similarly, the driver is responsible for locking the mutex in its
* interrupt handler and to call the audio layer call-backs (i.e.
* audio_{p,r}int()) with the mutex locked.
*/
struct mutex audio_lock = MUTEX_INITIALIZER(IPL_AUDIO);
/*
* Global flag to control if audio recording is enabled when the
* mixerctl setting is record.enable=sysctl
*/
int audio_record_enable = 0;
#ifdef AUDIO_DEBUG
/*
* 0 - nothing, as if AUDIO_DEBUG isn't defined
* 1 - initialisations & setup
* 2 - blocks & interrupts
*/
int audio_debug = 1;
#endif
unsigned int
audio_gcd(unsigned int a, unsigned int b)
{
unsigned int r;
while (b > 0) {
r = a % b;
a = b;
b = r;
}
return a;
}
/*
* Calculate the least block size (in frames) such that both the
* corresponding play and/or record block sizes (in bytes) are multiple
* of the given number of bytes.
*/
int
audio_blksz_bytes(int mode,
struct audio_params *p, struct audio_params *r, int bytes)
{
unsigned int np, nr;
if (mode & AUMODE_PLAY) {
np = bytes / audio_gcd(p->bps * p->channels, bytes);
if (!(mode & AUMODE_RECORD))
nr = np;
}
if (mode & AUMODE_RECORD) {
nr = bytes / audio_gcd(r->bps * r->channels, bytes);
if (!(mode & AUMODE_PLAY))
np = nr;
}
return nr * np / audio_gcd(nr, np);
}
void
audio_mixer_wakeup(struct audio_softc *sc)
{
MUTEX_ASSERT_LOCKED(&audio_lock);
if (sc->mix_blocking) {
wakeup(&sc->mix_blocking);
sc->mix_blocking = 0;
}
KNOTE(&sc->mix_klist, 0);
}
void
audio_buf_wakeup(struct audio_buf *buf)
{
MUTEX_ASSERT_LOCKED(&audio_lock);
if (buf->blocking) {
wakeup(&buf->blocking);
buf->blocking = 0;
}
KNOTE(&buf->klist, 0);
}
int
audio_buf_init(struct audio_softc *sc, struct audio_buf *buf, int dir)
{
klist_init_mutex(&buf->klist, &audio_lock);
if (sc->ops->round_buffersize) {
buf->datalen = sc->ops->round_buffersize(sc->arg,
dir, AUDIO_BUFSZ);
} else
buf->datalen = AUDIO_BUFSZ;
if (sc->ops->allocm) {
buf->data = sc->ops->allocm(sc->arg, dir, buf->datalen,
M_DEVBUF, M_WAITOK);
} else
buf->data = malloc(buf->datalen, M_DEVBUF, M_WAITOK);
if (buf->data == NULL) {
klist_free(&buf->klist);
return ENOMEM;
}
return 0;
}
void
audio_buf_done(struct audio_softc *sc, struct audio_buf *buf)
{
if (sc->ops->freem)
sc->ops->freem(sc->arg, buf->data, M_DEVBUF);
else
free(buf->data, M_DEVBUF, buf->datalen);
klist_free(&buf->klist);
}
/*
* return the reader pointer and the number of bytes available
*/
unsigned char *
audio_buf_rgetblk(struct audio_buf *buf, size_t *rsize)
{
size_t count;
count = buf->len - buf->start;
if (count > buf->used)
count = buf->used;
*rsize = count;
return buf->data + buf->start;
}
/*
* discard "count" bytes at the start position.
*/
void
audio_buf_rdiscard(struct audio_buf *buf, size_t count)
{
#ifdef AUDIO_DEBUG
if (count > buf->used) {
panic("audio_buf_rdiscard: bad count = %zu, "
"start = %zu, used = %zu", count, buf->start, buf->used);
}
#endif
buf->used -= count;
buf->start += count;
if (buf->start >= buf->len)
buf->start -= buf->len;
}
/*
* advance the writer pointer by "count" bytes
*/
void
audio_buf_wcommit(struct audio_buf *buf, size_t count)
{
#ifdef AUDIO_DEBUG
if (count > (buf->len - buf->used)) {
panic("audio_buf_wcommit: bad count = %zu, "
"start = %zu, used = %zu", count, buf->start, buf->used);
}
#endif
buf->used += count;
}
/*
* get writer pointer and the number of bytes writable
*/
unsigned char *
audio_buf_wgetblk(struct audio_buf *buf, size_t *rsize)
{
size_t end, avail, count;
end = buf->start + buf->used;
if (end >= buf->len)
end -= buf->len;
avail = buf->len - buf->used;
count = buf->len - end;
if (count > avail)
count = avail;
*rsize = count;
return buf->data + end;
}
void
audio_calc_sil(struct audio_softc *sc)
{
unsigned char *q;
unsigned int s, i;
int d, e;
e = sc->sw_enc;
#ifdef AUDIO_DEBUG
switch (e) {
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_SLINEAR_BE:
case AUDIO_ENCODING_ULINEAR_BE:
break;
default:
printf("%s: unhandled play encoding %d\n", DEVNAME(sc), e);
memset(sc->silence, 0, sc->bps);
return;
}
#endif
if (e == AUDIO_ENCODING_SLINEAR_BE || e == AUDIO_ENCODING_ULINEAR_BE) {
d = -1;
q = sc->silence + sc->bps - 1;
} else {
d = 1;
q = sc->silence;
}
if (e == AUDIO_ENCODING_SLINEAR_LE || e == AUDIO_ENCODING_SLINEAR_BE) {
s = 0;
} else {
s = 0x80000000;
if (sc->msb)
s >>= 32 - 8 * sc->bps;
else
s >>= 32 - sc->bits;
}
for (i = 0; i < sc->bps; i++) {
*q = s;
q += d;
s >>= 8;
}
if (sc->conv_enc)
sc->conv_enc(sc->silence, sc->bps);
}
void
audio_fill_sil(struct audio_softc *sc, unsigned char *ptr, size_t count)
{
unsigned char *q, *p;
size_t i, j;
q = ptr;
for (j = count / sc->bps; j > 0; j--) {
p = sc->silence;
for (i = sc->bps; i > 0; i--)
*q++ = *p++;
}
}
void
audio_clear(struct audio_softc *sc)
{
if (sc->mode & AUMODE_PLAY) {
sc->play.used = sc->play.start = 0;
sc->play.pos = sc->play.xrun = 0;
audio_fill_sil(sc, sc->play.data, sc->play.len);
}
if (sc->mode & AUMODE_RECORD) {
sc->rec.used = sc->rec.start = 0;
sc->rec.pos = sc->rec.xrun = 0;
audio_fill_sil(sc, sc->rec.data, sc->rec.len);
}
}
/*
* called whenever a block is consumed by the driver
*/
void
audio_pintr(void *addr)
{
struct audio_softc *sc = addr;
unsigned char *ptr;
size_t count;
int error, nblk, todo;
MUTEX_ASSERT_LOCKED(&audio_lock);
if (!(sc->mode & AUMODE_PLAY) || !sc->active) {
printf("%s: play interrupt but not playing\n", DEVNAME(sc));
return;
}
if (sc->quiesce) {
DPRINTF("%s: quiesced, skipping play intr\n", DEVNAME(sc));
return;
}
/*
* check if record pointer wrapped, see explanation
* in audio_rintr()
*/
if ((sc->mode & AUMODE_RECORD) && sc->ops->underrun == NULL) {
sc->offs--;
nblk = sc->rec.len / sc->rec.blksz;
todo = -sc->offs;
if (todo >= nblk) {
todo -= todo % nblk;
DPRINTFN(1, "%s: rec ptr wrapped, moving %d blocks\n",
DEVNAME(sc), todo);
while (todo-- > 0)
audio_rintr(sc);
}
}
sc->play.pos += sc->play.blksz;
if (!sc->ops->underrun) {
audio_fill_sil(sc, sc->play.data + sc->play.start,
sc->play.blksz);
}
audio_buf_rdiscard(&sc->play, sc->play.blksz);
if (sc->play.used < sc->play.blksz) {
DPRINTFN(1, "%s: play underrun\n", DEVNAME(sc));
sc->play.xrun += sc->play.blksz;
audio_buf_wcommit(&sc->play, sc->play.blksz);
if (sc->ops->underrun)
sc->ops->underrun(sc->arg);
}
DPRINTFN(1, "%s: play intr, used -> %zu, start -> %zu\n",
DEVNAME(sc), sc->play.used, sc->play.start);
if (!sc->ops->trigger_output) {
ptr = audio_buf_rgetblk(&sc->play, &count);
error = sc->ops->start_output(sc->arg,
ptr, sc->play.blksz, audio_pintr, sc);
if (error) {
printf("%s: play restart failed: %d\n",
DEVNAME(sc), error);
}
}
if (sc->play.used < sc->play.len) {
DPRINTFN(1, "%s: play wakeup, chan = %d\n",
DEVNAME(sc), sc->play.blocking);
audio_buf_wakeup(&sc->play);
}
}
/*
* called whenever a block is produced by the driver
*/
void
audio_rintr(void *addr)
{
struct audio_softc *sc = addr;
unsigned char *ptr;
size_t count;
int error, nblk, todo;
MUTEX_ASSERT_LOCKED(&audio_lock);
if (!(sc->mode & AUMODE_RECORD) || !sc->active) {
printf("%s: rec interrupt but not recording\n", DEVNAME(sc));
return;
}
if (sc->quiesce) {
DPRINTF("%s: quiesced, skipping rec intr\n", DEVNAME(sc));
return;
}
/*
* Interrupts may be masked by other sub-systems during 320ms
* and more. During such a delay the hardware doesn't stop
* playing and the play buffer pointers may wrap, this can't be
* detected and corrected by low level drivers. This makes the
* record stream ahead of the play stream; this is detected as a
* hardware anomaly by userland and cause programs to misbehave.
*
* We fix this by advancing play position by an integer count of
* full buffers, so it reaches the record position.
*/
if ((sc->mode & AUMODE_PLAY) && sc->ops->underrun == NULL) {
sc->offs++;
nblk = sc->play.len / sc->play.blksz;
todo = sc->offs;
if (todo >= nblk) {
todo -= todo % nblk;
DPRINTFN(1, "%s: play ptr wrapped, moving %d blocks\n",
DEVNAME(sc), todo);
while (todo-- > 0)
audio_pintr(sc);
}
}
sc->rec.pos += sc->rec.blksz;
if ((sc->record_enable == MIXER_RECORD_ENABLE_SYSCTL &&
!audio_record_enable) ||
sc->record_enable == MIXER_RECORD_ENABLE_OFF) {
ptr = audio_buf_wgetblk(&sc->rec, &count);
audio_fill_sil(sc, ptr, sc->rec.blksz);
}
audio_buf_wcommit(&sc->rec, sc->rec.blksz);
if (sc->rec.used > sc->rec.len - sc->rec.blksz) {
DPRINTFN(1, "%s: rec overrun\n", DEVNAME(sc));
sc->rec.xrun += sc->rec.blksz;
audio_buf_rdiscard(&sc->rec, sc->rec.blksz);
}
DPRINTFN(1, "%s: rec intr, used -> %zu\n", DEVNAME(sc), sc->rec.used);
if (!sc->ops->trigger_input) {
ptr = audio_buf_wgetblk(&sc->rec, &count);
error = sc->ops->start_input(sc->arg,
ptr, sc->rec.blksz, audio_rintr, sc);
if (error) {
printf("%s: rec restart failed: %d\n",
DEVNAME(sc), error);
}
}
if (sc->rec.used > 0) {
DPRINTFN(1, "%s: rec wakeup, chan = %d\n",
DEVNAME(sc), sc->rec.blocking);
audio_buf_wakeup(&sc->rec);
}
}
int
audio_start_do(struct audio_softc *sc)
{
int error;
struct audio_params p;
unsigned char *ptr;
size_t count;
DPRINTF("%s: starting\n", DEVNAME(sc));
error = 0;
sc->offs = 0;
if (sc->mode & AUMODE_PLAY) {
if (sc->ops->trigger_output) {
p.encoding = sc->hw_enc;
p.precision = sc->bits;
p.bps = sc->bps;
p.msb = sc->msb;
p.sample_rate = sc->rate;
p.channels = sc->pchan;
error = sc->ops->trigger_output(sc->arg,
sc->play.data,
sc->play.data + sc->play.len,
sc->play.blksz,
audio_pintr, sc, &p);
} else {
mtx_enter(&audio_lock);
ptr = audio_buf_rgetblk(&sc->play, &count);
error = sc->ops->start_output(sc->arg,
ptr, sc->play.blksz, audio_pintr, sc);
mtx_leave(&audio_lock);
}
if (error)
printf("%s: failed to start playback\n", DEVNAME(sc));
}
if (sc->mode & AUMODE_RECORD) {
if (sc->ops->trigger_input) {
p.encoding = sc->hw_enc;
p.precision = sc->bits;
p.bps = sc->bps;
p.msb = sc->msb;
p.sample_rate = sc->rate;
p.channels = sc->rchan;
error = sc->ops->trigger_input(sc->arg,
sc->rec.data,
sc->rec.data + sc->rec.len,
sc->rec.blksz,
audio_rintr, sc, &p);
} else {
mtx_enter(&audio_lock);
ptr = audio_buf_wgetblk(&sc->rec, &count);
error = sc->ops->start_input(sc->arg,
ptr, sc->rec.blksz, audio_rintr, sc);
mtx_leave(&audio_lock);
}
if (error)
printf("%s: failed to start recording\n", DEVNAME(sc));
}
return error;
}
int
audio_stop_do(struct audio_softc *sc)
{
if (sc->mode & AUMODE_PLAY)
sc->ops->halt_output(sc->arg);
if (sc->mode & AUMODE_RECORD)
sc->ops->halt_input(sc->arg);
DPRINTF("%s: stopped\n", DEVNAME(sc));
return 0;
}
int
audio_start(struct audio_softc *sc)
{
sc->active = 1;
sc->play.xrun = sc->play.pos = sc->rec.xrun = sc->rec.pos = 0;
return audio_start_do(sc);
}
int
audio_stop(struct audio_softc *sc)
{
int error;
error = audio_stop_do(sc);
if (error)
return error;
audio_clear(sc);
sc->active = 0;
return 0;
}
int
audio_canstart(struct audio_softc *sc)
{
if (sc->active || sc->pause)
return 0;
if ((sc->mode & AUMODE_RECORD) && sc->rec.used != 0)
return 0;
if ((sc->mode & AUMODE_PLAY) && sc->play.used != sc->play.len)
return 0;
return 1;
}
int
audio_setpar_blksz(struct audio_softc *sc,
struct audio_params *p, struct audio_params *r)
{
unsigned int nr, np, max, min, mult;
unsigned int blk_mult, blk_max;
if (sc->ops->set_blksz) {
/*
* Don't allow block size of exceed half the buffer size
*/
if (sc->mode & AUMODE_PLAY) {
max = sc->play.datalen / 2 / (sc->pchan * sc->bps);
if (sc->round > max)
sc->round = max;
}
if (sc->mode & AUMODE_RECORD) {
max = sc->rec.datalen / 2 / (sc->rchan * sc->bps);
if (sc->round > max)
sc->round = max;
}
sc->round = sc->ops->set_blksz(sc->arg, sc->mode,
p, r, sc->round);
DPRINTF("%s: block size set to: %u\n", DEVNAME(sc), sc->round);
return 0;
}
/*
* get least multiplier of the number of frames per block
*/
if (sc->ops->round_blocksize) {
blk_mult = sc->ops->round_blocksize(sc->arg, 1);
if (blk_mult == 0) {
printf("%s: 0x%x: bad block size multiplier\n",
DEVNAME(sc), blk_mult);
return ENODEV;
}
} else
blk_mult = 1;
DPRINTF("%s: hw block size multiplier: %u\n", DEVNAME(sc), blk_mult);
if (sc->mode & AUMODE_PLAY) {
np = blk_mult / audio_gcd(sc->pchan * sc->bps, blk_mult);
if (!(sc->mode & AUMODE_RECORD))
nr = np;
DPRINTF("%s: play number of frames multiplier: %u\n",
DEVNAME(sc), np);
}
if (sc->mode & AUMODE_RECORD) {
nr = blk_mult / audio_gcd(sc->rchan * sc->bps, blk_mult);
if (!(sc->mode & AUMODE_PLAY))
np = nr;
DPRINTF("%s: record number of frames multiplier: %u\n",
DEVNAME(sc), nr);
}
mult = nr * np / audio_gcd(nr, np);
DPRINTF("%s: least common number of frames multiplier: %u\n",
DEVNAME(sc), mult);
/*
* get minimum and maximum frames per block
*/
if (sc->ops->round_blocksize)
blk_max = sc->ops->round_blocksize(sc->arg, AUDIO_BUFSZ);
else
blk_max = AUDIO_BUFSZ;
if ((sc->mode & AUMODE_PLAY) && blk_max > sc->play.datalen / 2)
blk_max = sc->play.datalen / 2;
if ((sc->mode & AUMODE_RECORD) && blk_max > sc->rec.datalen / 2)
blk_max = sc->rec.datalen / 2;
if (sc->mode & AUMODE_PLAY) {
np = blk_max / (sc->pchan * sc->bps);
if (!(sc->mode & AUMODE_RECORD))
nr = np;
}
if (sc->mode & AUMODE_RECORD) {
nr = blk_max / (sc->rchan * sc->bps);
if (!(sc->mode & AUMODE_PLAY))
np = nr;
}
max = np < nr ? np : nr;
max -= max % mult;
min = sc->rate / 1000 + mult - 1;
min -= min % mult;
DPRINTF("%s: frame number range: %u..%u\n", DEVNAME(sc), min, max);
if (max < min) {
printf("%s: %u: bad max frame number\n", DEVNAME(sc), max);
return EIO;
}
/*
* adjust the frame per block to match our constraints
*/
sc->round += mult / 2;
sc->round -= sc->round % mult;
if (sc->round > max)
sc->round = max;
else if (sc->round < min)
sc->round = min;
return 0;
}
int
audio_setpar_nblks(struct audio_softc *sc,
struct audio_params *p, struct audio_params *r)
{
unsigned int max;
/*
* set buffer size (number of blocks)
*/
if (sc->mode & AUMODE_PLAY) {
max = sc->play.datalen / (sc->round * sc->pchan * sc->bps);
if (sc->play.nblks > max)
sc->play.nblks = max;
else if (sc->play.nblks < 2)
sc->play.nblks = 2;
if (sc->ops->set_nblks) {
sc->play.nblks = sc->ops->set_nblks(sc->arg, sc->mode,
p, sc->round, sc->play.nblks);
DPRINTF("%s: play nblks -> %u\n", DEVNAME(sc),
sc->play.nblks);
}
}
if (sc->mode & AUMODE_RECORD) {
/*
* for recording, buffer size is not the latency (it's
* exactly one block), so let's get the maximum buffer
* size of maximum reliability during xruns
*/
max = sc->rec.datalen / (sc->round * sc->rchan * sc->bps);
if (sc->ops->set_nblks) {
max = sc->ops->set_nblks(sc->arg, sc->mode,
r, sc->round, max);
DPRINTF("%s: rec nblks -> %u\n", DEVNAME(sc), max);
}
sc->rec.nblks = max;
}
return 0;
}
int
audio_setpar(struct audio_softc *sc)
{
struct audio_params p, r;
int error;
DPRINTF("%s: setpar: req enc=%d bits=%d, bps=%d, msb=%d "
"rate=%d, pchan=%d, rchan=%d, round=%u, nblks=%d\n",
DEVNAME(sc), sc->sw_enc, sc->bits, sc->bps, sc->msb,
sc->rate, sc->pchan, sc->rchan, sc->round, sc->play.nblks);
/*
* check if requested parameters are in the allowed ranges
*/
if (sc->mode & AUMODE_PLAY) {
if (sc->pchan < 1)
sc->pchan = 1;
else if (sc->pchan > 64)
sc->pchan = 64;
}
if (sc->mode & AUMODE_RECORD) {
if (sc->rchan < 1)
sc->rchan = 1;
else if (sc->rchan > 64)
sc->rchan = 64;
}
switch (sc->sw_enc) {
case AUDIO_ENCODING_ULAW:
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_SLINEAR_BE:
case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_ULINEAR_BE:
break;
default:
sc->sw_enc = AUDIO_ENCODING_SLINEAR_LE;
}
if (sc->bits < 8)
sc->bits = 8;
else if (sc->bits > 32)
sc->bits = 32;
if (sc->bps < 1)
sc->bps = 1;
else if (sc->bps > 4)
sc->bps = 4;
if (sc->rate < 4000)
sc->rate = 4000;
else if (sc->rate > 192000)
sc->rate = 192000;
/*
* copy into struct audio_params, required by drivers
*/
p.encoding = r.encoding = sc->sw_enc;
p.precision = r.precision = sc->bits;
p.bps = r.bps = sc->bps;
p.msb = r.msb = sc->msb;
p.sample_rate = r.sample_rate = sc->rate;
p.channels = sc->pchan;
r.channels = sc->rchan;
/*
* set parameters
*/
error = sc->ops->set_params(sc->arg, sc->mode, sc->mode, &p, &r);
if (error)
return error;
if (sc->mode == (AUMODE_PLAY | AUMODE_RECORD)) {
if (p.encoding != r.encoding ||
p.precision != r.precision ||
p.bps != r.bps ||
p.msb != r.msb ||
p.sample_rate != r.sample_rate) {
printf("%s: different play and record parameters "
"returned by hardware\n", DEVNAME(sc));
return ENODEV;
}
}
if (sc->mode & AUMODE_PLAY) {
sc->hw_enc = p.encoding;
sc->bits = p.precision;
sc->bps = p.bps;
sc->msb = p.msb;
sc->rate = p.sample_rate;
sc->pchan = p.channels;
}
if (sc->mode & AUMODE_RECORD) {
sc->hw_enc = r.encoding;
sc->bits = r.precision;
sc->bps = r.bps;
sc->msb = r.msb;
sc->rate = r.sample_rate;
sc->rchan = r.channels;
}
if (sc->rate == 0 || sc->bps == 0 || sc->bits == 0) {
printf("%s: invalid parameters returned by hardware\n",
DEVNAME(sc));
return ENODEV;
}
if (sc->ops->commit_settings) {
error = sc->ops->commit_settings(sc->arg);
if (error)
return error;
}
/*
* conversion from/to exotic/dead encoding, for drivers not supporting
* linear
*/
switch (sc->hw_enc) {
case AUDIO_ENCODING_SLINEAR_LE:
case AUDIO_ENCODING_SLINEAR_BE:
case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_ULINEAR_BE:
sc->sw_enc = sc->hw_enc;
sc->conv_dec = sc->conv_enc = NULL;
break;
case AUDIO_ENCODING_ULAW:
#if BYTE_ORDER == LITTLE_ENDIAN
sc->sw_enc = AUDIO_ENCODING_SLINEAR_LE;
#else
sc->sw_enc = AUDIO_ENCODING_SLINEAR_BE;
#endif
if (sc->bits == 8) {
sc->conv_enc = slinear8_to_mulaw;
sc->conv_dec = mulaw_to_slinear8;
} else if (sc->bits == 24) {
sc->conv_enc = slinear24_to_mulaw24;
sc->conv_dec = mulaw24_to_slinear24;
} else {
sc->sw_enc = sc->hw_enc;
sc->conv_dec = sc->conv_enc = NULL;
}
break;
default:
printf("%s: setpar: enc = %d, bits = %d: emulation skipped\n",
DEVNAME(sc), sc->hw_enc, sc->bits);
sc->sw_enc = sc->hw_enc;
sc->conv_dec = sc->conv_enc = NULL;
}
audio_calc_sil(sc);
error = audio_setpar_blksz(sc, &p, &r);
if (error)
return error;
error = audio_setpar_nblks(sc, &p, &r);
if (error)
return error;
/*
* set buffer
*/
if (sc->mode & AUMODE_PLAY) {
sc->play.blksz = sc->round * sc->pchan * sc->bps;
sc->play.len = sc->play.nblks * sc->play.blksz;
}
if (sc->mode & AUMODE_RECORD) {
sc->rec.blksz = sc->round * sc->rchan * sc->bps;
sc->rec.len = sc->rec.nblks * sc->rec.blksz;
}
DPRINTF("%s: setpar: new enc=%d bits=%d, bps=%d, msb=%d "
"rate=%d, pchan=%d, rchan=%d, round=%u, nblks=%d\n",
DEVNAME(sc), sc->sw_enc, sc->bits, sc->bps, sc->msb,
sc->rate, sc->pchan, sc->rchan, sc->round, sc->play.nblks);
return 0;
}
int
audio_ioc_start(struct audio_softc *sc)
{
if (!sc->pause) {
DPRINTF("%s: can't start: already started\n", DEVNAME(sc));
return EBUSY;
}
if ((sc->mode & AUMODE_PLAY) && sc->play.used != sc->play.len) {
DPRINTF("%s: play buffer not ready\n", DEVNAME(sc));
return EBUSY;
}
if ((sc->mode & AUMODE_RECORD) && sc->rec.used != 0) {
DPRINTF("%s: record buffer not ready\n", DEVNAME(sc));
return EBUSY;
}
sc->pause = 0;
return audio_start(sc);
}
int
audio_ioc_stop(struct audio_softc *sc)
{
if (sc->pause) {
DPRINTF("%s: can't stop: not started\n", DEVNAME(sc));
return EBUSY;
}
sc->pause = 1;
if (sc->active)
return audio_stop(sc);
return 0;
}
int
audio_ioc_getpar(struct audio_softc *sc, struct audio_swpar *p)
{
p->rate = sc->rate;
p->sig = sc->sw_enc == AUDIO_ENCODING_SLINEAR_LE ||
sc->sw_enc == AUDIO_ENCODING_SLINEAR_BE;
p->le = sc->sw_enc == AUDIO_ENCODING_SLINEAR_LE ||
sc->sw_enc == AUDIO_ENCODING_ULINEAR_LE;
p->bits = sc->bits;
p->bps = sc->bps;
p->msb = sc->msb;
p->pchan = sc->pchan;
p->rchan = sc->rchan;
p->nblks = sc->play.nblks;
p->round = sc->round;
return 0;
}
int
audio_ioc_setpar(struct audio_softc *sc, struct audio_swpar *p)
{
int error, le, sig;
if (sc->active) {
DPRINTF("%s: can't change params during dma\n",
DEVNAME(sc));
return EBUSY;
}
/*
* copy desired parameters into the softc structure
*/
if (p->sig != ~0U || p->le != ~0U || p->bits != ~0U) {
sig = 1;
le = (BYTE_ORDER == LITTLE_ENDIAN);
sc->bits = 16;
sc->bps = 2;
sc->msb = 1;
if (p->sig != ~0U)
sig = p->sig;
if (p->le != ~0U)
le = p->le;
if (p->bits != ~0U) {
sc->bits = p->bits;
sc->bps = sc->bits <= 8 ?
1 : (sc->bits <= 16 ? 2 : 4);
if (p->bps != ~0U)
sc->bps = p->bps;
if (p->msb != ~0U)
sc->msb = p->msb ? 1 : 0;
}
sc->sw_enc = (sig) ?
(le ? AUDIO_ENCODING_SLINEAR_LE :
AUDIO_ENCODING_SLINEAR_BE) :
(le ? AUDIO_ENCODING_ULINEAR_LE :
AUDIO_ENCODING_ULINEAR_BE);
}
if (p->rate != ~0)
sc->rate = p->rate;
if (p->pchan != ~0)
sc->pchan = p->pchan;
if (p->rchan != ~0)
sc->rchan = p->rchan;
if (p->round != ~0)
sc->round = p->round;
if (p->nblks != ~0)
sc->play.nblks = p->nblks;
/*
* if the device is not opened for playback or recording don't
* touch the hardware yet (ex. if this is /dev/audioctlN)
*/
if (sc->mode == 0)
return 0;
/*
* negotiate parameters with the hardware
*/
error = audio_setpar(sc);
if (error)
return error;
audio_clear(sc);
if ((sc->mode & AUMODE_PLAY) && sc->ops->init_output) {
error = sc->ops->init_output(sc->arg,
sc->play.data, sc->play.len);
if (error)
return error;
}
if ((sc->mode & AUMODE_RECORD) && sc->ops->init_input) {
error = sc->ops->init_input(sc->arg,
sc->rec.data, sc->rec.len);
if (error)
return error;
}
return 0;
}
int
audio_ioc_getstatus(struct audio_softc *sc, struct audio_status *p)
{
p->mode = sc->mode;
p->pause = sc->pause;
p->active = sc->active;
return 0;
}
int
audio_match(struct device *parent, void *match, void *aux)
{
struct audio_attach_args *sa = aux;
return (sa->type == AUDIODEV_TYPE_AUDIO) ? 1 : 0;
}
void
audio_attach(struct device *parent, struct device *self, void *aux)
{
struct audio_softc *sc = (void *)self;
struct audio_attach_args *sa = aux;
const struct audio_hw_if *ops = sa->hwif;
struct mixer_devinfo *mi;
struct mixer_ctrl *ent;
void *arg = sa->hdl;
int error;
printf("\n");
#ifdef DIAGNOSTIC
if (ops == 0 ||
ops->open == 0 ||
ops->close == 0 ||
ops->set_params == 0 ||
(ops->start_output == 0 && ops->trigger_output == 0) ||
(ops->start_input == 0 && ops->trigger_input == 0) ||
ops->halt_output == 0 ||
ops->halt_input == 0 ||
ops->set_port == 0 ||
ops->get_port == 0 ||
ops->query_devinfo == 0 ||
ops->get_props == 0) {
printf("%s: missing method\n", DEVNAME(sc));
sc->ops = 0;
return;
}
#endif
sc->ops = ops;
sc->cookie = sa->cookie;
sc->arg = arg;
#if NWSKBD > 0
wskbd_mixer_init(sc);
#endif /* NWSKBD > 0 */
error = audio_buf_init(sc, &sc->play, AUMODE_PLAY);
if (error) {
sc->ops = 0;
printf("%s: could not allocate play buffer\n", DEVNAME(sc));
return;
}
error = audio_buf_init(sc, &sc->rec, AUMODE_RECORD);
if (error) {
audio_buf_done(sc, &sc->play);
sc->ops = 0;
printf("%s: could not allocate record buffer\n", DEVNAME(sc));
return;
}
klist_init_mutex(&sc->mix_klist, &audio_lock);
/* set defaults */
#if BYTE_ORDER == LITTLE_ENDIAN
sc->sw_enc = AUDIO_ENCODING_SLINEAR_LE;
#else
sc->sw_enc = AUDIO_ENCODING_SLINEAR_BE;
#endif
sc->bits = 16;
sc->bps = 2;
sc->msb = 1;
sc->rate = 48000;
sc->pchan = 2;
sc->rchan = 2;
sc->round = 960;
sc->play.nblks = 2;
sc->play.pos = sc->play.xrun = sc->rec.pos = sc->rec.xrun = 0;
sc->record_enable = MIXER_RECORD_ENABLE_SYSCTL;
/*
* allocate an array of mixer_ctrl structures to save the
* mixer state and prefill them.
*/
mi = malloc(sizeof(struct mixer_devinfo), M_TEMP, M_WAITOK);
mi->index = 0;
while (1) {
if (sc->ops->query_devinfo(sc->arg, mi) != 0)
break;
mi->index++;
}
sc->mix_nent = mi->index;
sc->mix_ents = mallocarray(sc->mix_nent,
sizeof(struct mixer_ctrl), M_DEVBUF, M_WAITOK);
sc->mix_evbuf = mallocarray(sc->mix_nent,
sizeof(struct mixer_ev), M_DEVBUF, M_WAITOK | M_ZERO);
ent = sc->mix_ents;
mi->index = 0;
while (1) {
if (sc->ops->query_devinfo(sc->arg, mi) != 0)
break;
switch (mi->type) {
case AUDIO_MIXER_VALUE:
ent->un.value.num_channels = mi->un.v.num_channels;
/* FALLTHROUGH */
case AUDIO_MIXER_SET:
case AUDIO_MIXER_ENUM:
ent->dev = mi->index;
ent->type = mi->type;
}
mi->index++;
ent++;
}
free(mi, M_TEMP, sizeof(struct mixer_devinfo));
}
int
audio_activate(struct device *self, int act)
{
struct audio_softc *sc = (struct audio_softc *)self;
int i;
switch (act) {
case DVACT_QUIESCE:
/*
* good drivers run play and rec handlers in a single
* interrupt. Grab the lock to ensure we expose the same
* sc->quiesce value to both play and rec handlers
*/
mtx_enter(&audio_lock);
sc->quiesce = 1;
mtx_leave(&audio_lock);
/*
* once sc->quiesce is set, interrupts may occur, but
* counters are not advanced and consequently processes
* keep sleeping.
*
* XXX: ensure read/write/ioctl don't start/stop
* DMA at the same time, this needs a "ready" condvar
*/
if (sc->mode != 0 && sc->active)
audio_stop_do(sc);
/*
* save mixer state
*/
for (i = 0; i != sc->mix_nent; i++)
sc->ops->get_port(sc->arg, sc->mix_ents + i);
DPRINTF("%s: quiesce: active = %d\n", DEVNAME(sc), sc->active);
break;
case DVACT_WAKEUP:
DPRINTF("%s: wakeup: active = %d\n", DEVNAME(sc), sc->active);
/*
* restore mixer state
*/
for (i = 0; i != sc->mix_nent; i++)
sc->ops->set_port(sc->arg, sc->mix_ents + i);
/*
* keep buffer usage the same, but set start pointer to
* the beginning of the buffer.
*
* No need to grab the audio_lock as DMA is stopped and
* this is the only thread running (caller ensures this)
*/
sc->quiesce = 0;
wakeup(&sc->quiesce);
if (sc->mode != 0) {
if (audio_setpar(sc) != 0)
break;
if (sc->mode & AUMODE_PLAY) {
sc->play.start = 0;
audio_fill_sil(sc, sc->play.data, sc->play.len);
}
if (sc->mode & AUMODE_RECORD) {
sc->rec.start = sc->rec.len - sc->rec.used;
audio_fill_sil(sc, sc->rec.data, sc->rec.len);
}
if (sc->active)
audio_start_do(sc);
}
break;
}
return 0;
}
int
audio_detach(struct device *self, int flags)
{
struct audio_softc *sc = (struct audio_softc *)self;
int maj, mn;
DPRINTF("%s: audio_detach: flags = %d\n", DEVNAME(sc), flags);
wakeup(&sc->quiesce);
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == audioopen)
break;
/*
* Nuke the vnodes for any open instances, calls close but as
* close uses device_lookup, it returns EXIO and does nothing
*/
mn = self->dv_unit;
vdevgone(maj, mn | AUDIO_DEV_AUDIO, mn | AUDIO_DEV_AUDIO, VCHR);
vdevgone(maj, mn | AUDIO_DEV_AUDIOCTL, mn | AUDIO_DEV_AUDIOCTL, VCHR);
/*
* The close() method did nothing, quickly halt DMA (normally
* parent is already gone, and code below is no-op), and wake-up
* user-land blocked in read/write/ioctl, which return EIO.
*/
if (sc->mode != 0) {
if (sc->active) {
wakeup(&sc->play.blocking);
wakeup(&sc->rec.blocking);
audio_stop(sc);
}
sc->ops->close(sc->arg);
sc->mode = 0;
}
if (sc->mix_isopen)
wakeup(&sc->mix_blocking);
klist_invalidate(&sc->play.klist);
klist_invalidate(&sc->rec.klist);
klist_invalidate(&sc->mix_klist);
/* free resources */
klist_free(&sc->mix_klist);
free(sc->mix_evbuf, M_DEVBUF, sc->mix_nent * sizeof(struct mixer_ev));
free(sc->mix_ents, M_DEVBUF, sc->mix_nent * sizeof(struct mixer_ctrl));
audio_buf_done(sc, &sc->play);
audio_buf_done(sc, &sc->rec);
return 0;
}
int
audio_submatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
return (cf->cf_driver == &audio_cd);
}
struct device *
audio_attach_mi(const struct audio_hw_if *ops, void *arg, void *cookie,
struct device *dev)
{
struct audio_attach_args aa;
aa.type = AUDIODEV_TYPE_AUDIO;
aa.hwif = ops;
aa.hdl = arg;
aa.cookie = cookie;
/*
* attach this driver to the caller (hardware driver), this
* checks the kernel config and possibly calls audio_attach()
*/
return config_found_sm(dev, &aa, audioprint, audio_submatch);
}
int
audioprint(void *aux, const char *pnp)
{
struct audio_attach_args *arg = aux;
const char *type;
if (pnp != NULL) {
switch (arg->type) {
case AUDIODEV_TYPE_AUDIO:
type = "audio";
break;
case AUDIODEV_TYPE_OPL:
type = "opl";
break;
case AUDIODEV_TYPE_MPU:
type = "mpu";
break;
default:
panic("audioprint: unknown type %d", arg->type);
}
printf("%s at %s", type, pnp);
}
return UNCONF;
}
int
audio_open(struct audio_softc *sc, int flags)
{
int error;
int props;
if (sc->mode)
return EBUSY;
error = sc->ops->open(sc->arg, flags);
if (error)
return error;
sc->active = 0;
sc->pause = 1;
sc->rec.blocking = 0;
sc->play.blocking = 0;
sc->mode = 0;
if (flags & FWRITE)
sc->mode |= AUMODE_PLAY;
if (flags & FREAD)
sc->mode |= AUMODE_RECORD;
props = sc->ops->get_props(sc->arg);
if (sc->mode == (AUMODE_PLAY | AUMODE_RECORD)) {
if (!(props & AUDIO_PROP_FULLDUPLEX)) {
error = ENOTTY;
goto bad;
}
if (sc->ops->setfd) {
error = sc->ops->setfd(sc->arg, 1);
if (error)
goto bad;
}
}
if (sc->ops->speaker_ctl) {
/*
* XXX: what is this used for?
*/
sc->ops->speaker_ctl(sc->arg,
(sc->mode & AUMODE_PLAY) ? SPKR_ON : SPKR_OFF);
}
error = audio_setpar(sc);
if (error)
goto bad;
audio_clear(sc);
/*
* allow read(2)/write(2) to automatically start DMA, without
* the need for ioctl(), to make /dev/audio usable in scripts
*/
sc->pause = 0;
return 0;
bad:
sc->ops->close(sc->arg);
sc->mode = 0;
return error;
}
int
audio_drain(struct audio_softc *sc)
{
int error, xrun;
unsigned char *ptr;
size_t count, bpf;
DPRINTF("%s: drain: mode = %d, pause = %d, active = %d, used = %zu\n",
DEVNAME(sc), sc->mode, sc->pause, sc->active, sc->play.used);
if (!(sc->mode & AUMODE_PLAY) || sc->pause)
return 0;
/* discard partial samples, required by audio_fill_sil() */
mtx_enter(&audio_lock);
bpf = sc->pchan * sc->bps;
sc->play.used -= sc->play.used % bpf;
if (sc->play.used == 0) {
mtx_leave(&audio_lock);
return 0;
}
if (!sc->active) {
/*
* dma not started yet because buffer was not full
* enough to start automatically. Pad it and start now.
*/
for (;;) {
ptr = audio_buf_wgetblk(&sc->play, &count);
if (count == 0)
break;
audio_fill_sil(sc, ptr, count);
audio_buf_wcommit(&sc->play, count);
}
mtx_leave(&audio_lock);
error = audio_start(sc);
if (error)
return error;
mtx_enter(&audio_lock);
}
xrun = sc->play.xrun;
while (sc->play.xrun == xrun) {
DPRINTF("%s: drain: used = %zu, xrun = %d\n",
DEVNAME(sc), sc->play.used, sc->play.xrun);
/*
* set a 5 second timeout, in case interrupts don't
* work, useful only for debugging drivers
*/
sc->play.blocking = 1;
error = msleep_nsec(&sc->play.blocking, &audio_lock,
PWAIT | PCATCH, "au_dr", SEC_TO_NSEC(5));
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error) {
DPRINTF("%s: drain, err = %d\n", DEVNAME(sc), error);
break;
}
}
mtx_leave(&audio_lock);
return error;
}
int
audio_close(struct audio_softc *sc)
{
audio_drain(sc);
if (sc->active)
audio_stop(sc);
sc->ops->close(sc->arg);
sc->mode = 0;
DPRINTF("%s: close: done\n", DEVNAME(sc));
return 0;
}
int
audio_read(struct audio_softc *sc, struct uio *uio, int ioflag)
{
unsigned char *ptr;
size_t count;
int error;
DPRINTFN(1, "%s: read: resid = %zd\n", DEVNAME(sc), uio->uio_resid);
/* block if quiesced */
while (sc->quiesce)
tsleep_nsec(&sc->quiesce, 0, "au_qrd", INFSLP);
/* start automatically if audio_ioc_start() was never called */
if (audio_canstart(sc)) {
error = audio_start(sc);
if (error)
return error;
}
mtx_enter(&audio_lock);
/* if there is no data then sleep */
while (sc->rec.used == 0) {
if (ioflag & IO_NDELAY) {
mtx_leave(&audio_lock);
return EWOULDBLOCK;
}
DPRINTFN(1, "%s: read sleep\n", DEVNAME(sc));
sc->rec.blocking = 1;
error = msleep_nsec(&sc->rec.blocking,
&audio_lock, PWAIT | PCATCH, "au_rd", INFSLP);
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error) {
DPRINTF("%s: read woke up error = %d\n",
DEVNAME(sc), error);
mtx_leave(&audio_lock);
return error;
}
}
/* at this stage, there is data to transfer */
while (uio->uio_resid > 0 && sc->rec.used > 0) {
ptr = audio_buf_rgetblk(&sc->rec, &count);
if (count > uio->uio_resid)
count = uio->uio_resid;
mtx_leave(&audio_lock);
DPRINTFN(1, "%s: read: start = %zu, count = %zu\n",
DEVNAME(sc), ptr - sc->rec.data, count);
if (sc->conv_dec)
sc->conv_dec(ptr, count);
error = uiomove(ptr, count, uio);
if (error)
return error;
mtx_enter(&audio_lock);
audio_buf_rdiscard(&sc->rec, count);
}
mtx_leave(&audio_lock);
return 0;
}
int
audio_write(struct audio_softc *sc, struct uio *uio, int ioflag)
{
unsigned char *ptr;
size_t count;
int error;
DPRINTFN(1, "%s: write: resid = %zd\n", DEVNAME(sc), uio->uio_resid);
/* block if quiesced */
while (sc->quiesce)
tsleep_nsec(&sc->quiesce, 0, "au_qwr", INFSLP);
/*
* if IO_NDELAY flag is set then check if there is enough room
* in the buffer to store at least one byte. If not then don't
* start the write process.
*/
mtx_enter(&audio_lock);
if (uio->uio_resid > 0 && (ioflag & IO_NDELAY)) {
if (sc->play.used == sc->play.len) {
mtx_leave(&audio_lock);
return EWOULDBLOCK;
}
}
while (uio->uio_resid > 0) {
while (1) {
ptr = audio_buf_wgetblk(&sc->play, &count);
if (count > 0)
break;
if (ioflag & IO_NDELAY) {
/*
* At this stage at least one byte is already
* moved so we do not return EWOULDBLOCK
*/
mtx_leave(&audio_lock);
return 0;
}
DPRINTFN(1, "%s: write sleep\n", DEVNAME(sc));
sc->play.blocking = 1;
error = msleep_nsec(&sc->play.blocking,
&audio_lock, PWAIT | PCATCH, "au_wr", INFSLP);
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error) {
DPRINTF("%s: write woke up error = %d\n",
DEVNAME(sc), error);
mtx_leave(&audio_lock);
return error;
}
}
if (count > uio->uio_resid)
count = uio->uio_resid;
mtx_leave(&audio_lock);
error = uiomove(ptr, count, uio);
if (error)
return 0;
if (sc->conv_enc) {
sc->conv_enc(ptr, count);
DPRINTFN(1, "audio_write: converted count = %zu\n",
count);
}
if (sc->ops->copy_output)
sc->ops->copy_output(sc->arg, count);
mtx_enter(&audio_lock);
audio_buf_wcommit(&sc->play, count);
/* start automatically if audio_ioc_start() was never called */
if (audio_canstart(sc)) {
mtx_leave(&audio_lock);
error = audio_start(sc);
if (error)
return error;
mtx_enter(&audio_lock);
}
}
mtx_leave(&audio_lock);
return 0;
}
int
audio_getdev(struct audio_softc *sc, struct audio_device *adev)
{
memset(adev, 0, sizeof(struct audio_device));
if (sc->dev.dv_parent == NULL)
return EIO;
strlcpy(adev->name, sc->dev.dv_parent->dv_xname, MAX_AUDIO_DEV_LEN);
return 0;
}
int
audio_ioctl(struct audio_softc *sc, unsigned long cmd, void *addr)
{
struct audio_pos *ap;
int error = 0;
/* block if quiesced */
while (sc->quiesce)
tsleep_nsec(&sc->quiesce, 0, "au_qio", INFSLP);
switch (cmd) {
case FIONBIO:
/* All handled in the upper FS layer. */
break;
case AUDIO_GETPOS:
mtx_enter(&audio_lock);
ap = (struct audio_pos *)addr;
ap->play_pos = sc->play.pos;
ap->play_xrun = sc->play.xrun;
ap->rec_pos = sc->rec.pos;
ap->rec_xrun = sc->rec.xrun;
mtx_leave(&audio_lock);
break;
case AUDIO_START:
return audio_ioc_start(sc);
case AUDIO_STOP:
return audio_ioc_stop(sc);
case AUDIO_SETPAR:
error = audio_ioc_setpar(sc, (struct audio_swpar *)addr);
break;
case AUDIO_GETPAR:
error = audio_ioc_getpar(sc, (struct audio_swpar *)addr);
break;
case AUDIO_GETSTATUS:
error = audio_ioc_getstatus(sc, (struct audio_status *)addr);
break;
case AUDIO_GETDEV:
error = audio_getdev(sc, (struct audio_device *)addr);
break;
default:
DPRINTF("%s: unknown ioctl 0x%lx\n", DEVNAME(sc), cmd);
error = ENOTTY;
break;
}
return error;
}
void
audio_event(struct audio_softc *sc, int addr)
{
struct mixer_ev *e;
mtx_enter(&audio_lock);
if (sc->mix_isopen) {
e = sc->mix_evbuf + addr;
if (!e->pending) {
e->pending = 1;
e->next = sc->mix_pending;
sc->mix_pending = e;
}
audio_mixer_wakeup(sc);
}
mtx_leave(&audio_lock);
}
int
audio_mixer_devinfo(struct audio_softc *sc, struct mixer_devinfo *devinfo)
{
if (devinfo->index < sc->mix_nent)
return sc->ops->query_devinfo(sc->arg, devinfo);
devinfo->next = -1;
devinfo->prev = -1;
switch (devinfo->index - sc->mix_nent) {
case MIXER_RECORD:
strlcpy(devinfo->label.name, AudioCrecord, MAX_AUDIO_DEV_LEN);
devinfo->type = AUDIO_MIXER_CLASS;
devinfo->mixer_class = -1;
break;
case MIXER_RECORD_ENABLE:
strlcpy(devinfo->label.name, "enable", MAX_AUDIO_DEV_LEN);
devinfo->type = AUDIO_MIXER_ENUM;
devinfo->mixer_class = MIXER_RECORD + sc->mix_nent;
devinfo->un.e.num_mem = 3;
devinfo->un.e.member[0].ord = MIXER_RECORD_ENABLE_OFF;
strlcpy(devinfo->un.e.member[0].label.name, "off",
MAX_AUDIO_DEV_LEN);
devinfo->un.e.member[1].ord = MIXER_RECORD_ENABLE_ON;
strlcpy(devinfo->un.e.member[1].label.name, "on",
MAX_AUDIO_DEV_LEN);
devinfo->un.e.member[2].ord = MIXER_RECORD_ENABLE_SYSCTL;
strlcpy(devinfo->un.e.member[2].label.name, "sysctl",
MAX_AUDIO_DEV_LEN);
break;
default:
return EINVAL;
}
return 0;
}
int
audio_mixer_get(struct audio_softc *sc, struct mixer_ctrl *c)
{
if (c->dev < sc->mix_nent)
return sc->ops->get_port(sc->arg, c);
switch (c->dev - sc->mix_nent) {
case MIXER_RECORD:
return EBADF;
case MIXER_RECORD_ENABLE:
c->un.ord = sc->record_enable;
break;
default:
return EINVAL;
}
return 0;
}
int
audio_mixer_set(struct audio_softc *sc, struct mixer_ctrl *c, struct proc *p)
{
int error;
if (c->dev < sc->mix_nent) {
error = sc->ops->set_port(sc->arg, c);
if (error)
return error;
if (sc->ops->commit_settings)
return sc->ops->commit_settings(sc->arg);
audio_event(sc, c->dev);
return 0;
}
switch (c->dev - sc->mix_nent) {
case MIXER_RECORD:
return EBADF;
case MIXER_RECORD_ENABLE:
switch (c->un.ord) {
case MIXER_RECORD_ENABLE_OFF:
case MIXER_RECORD_ENABLE_ON:
case MIXER_RECORD_ENABLE_SYSCTL:
break;
default:
return EINVAL;
}
if (suser(p) == 0)
sc->record_enable = c->un.ord;
break;
default:
return EINVAL;
}
return 0;
}
int
audio_ioctl_mixer(struct audio_softc *sc, unsigned long cmd, void *addr,
struct proc *p)
{
/* block if quiesced */
while (sc->quiesce)
tsleep_nsec(&sc->quiesce, 0, "mix_qio", INFSLP);
switch (cmd) {
case FIONBIO:
/* All handled in the upper FS layer. */
break;
case AUDIO_MIXER_DEVINFO:
return audio_mixer_devinfo(sc, addr);
case AUDIO_MIXER_READ:
return audio_mixer_get(sc, addr);
case AUDIO_MIXER_WRITE:
return audio_mixer_set(sc, addr, p);
default:
return ENOTTY;
}
return 0;
}
int
audio_mixer_read(struct audio_softc *sc, struct uio *uio, int ioflag)
{
struct mixer_ev *e;
int data;
int error;
DPRINTF("%s: mixer read: resid = %zd\n", DEVNAME(sc), uio->uio_resid);
/* block if quiesced */
while (sc->quiesce)
tsleep_nsec(&sc->quiesce, 0, "mix_qrd", INFSLP);
mtx_enter(&audio_lock);
/* if there are no events then sleep */
while (!sc->mix_pending) {
if (ioflag & IO_NDELAY) {
mtx_leave(&audio_lock);
return EWOULDBLOCK;
}
DPRINTF("%s: mixer read sleep\n", DEVNAME(sc));
sc->mix_blocking = 1;
error = msleep_nsec(&sc->mix_blocking,
&audio_lock, PWAIT | PCATCH, "mix_rd", INFSLP);
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error) {
DPRINTF("%s: mixer read woke up error = %d\n",
DEVNAME(sc), error);
mtx_leave(&audio_lock);
return error;
}
}
/* at this stage, there is an event to transfer */
while (uio->uio_resid >= sizeof(int) && sc->mix_pending) {
e = sc->mix_pending;
sc->mix_pending = e->next;
e->pending = 0;
data = e - sc->mix_evbuf;
mtx_leave(&audio_lock);
DPRINTF("%s: mixer read: %u\n", DEVNAME(sc), data);
error = uiomove(&data, sizeof(int), uio);
if (error)
return error;
mtx_enter(&audio_lock);
}
mtx_leave(&audio_lock);
return 0;
}
int
audio_mixer_open(struct audio_softc *sc, int flags)
{
DPRINTF("%s: flags = 0x%x\n", __func__, flags);
if (flags & FREAD) {
if (sc->mix_isopen)
return EBUSY;
sc->mix_isopen = 1;
}
return 0;
}
int
audio_mixer_close(struct audio_softc *sc, int flags)
{
int i;
DPRINTF("%s: flags = 0x%x\n", __func__, flags);
if (flags & FREAD) {
sc->mix_isopen = 0;
mtx_enter(&audio_lock);
sc->mix_pending = NULL;
for (i = 0; i < sc->mix_nent; i++)
sc->mix_evbuf[i].pending = 0;
mtx_leave(&audio_lock);
}
return 0;
}
int
audioopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct audio_softc *sc;
int error;
sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev));
if (sc == NULL)
return ENXIO;
if (sc->ops == NULL)
error = ENXIO;
else {
switch (AUDIO_DEV(dev)) {
case AUDIO_DEV_AUDIO:
error = audio_open(sc, flags);
break;
case AUDIO_DEV_AUDIOCTL:
error = audio_mixer_open(sc, flags);
break;
default:
error = ENXIO;
}
}
device_unref(&sc->dev);
return error;
}
int
audioclose(dev_t dev, int flags, int ifmt, struct proc *p)
{
struct audio_softc *sc;
int error;
sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev));
if (sc == NULL)
return ENXIO;
switch (AUDIO_DEV(dev)) {
case AUDIO_DEV_AUDIO:
error = audio_close(sc);
break;
case AUDIO_DEV_AUDIOCTL:
error = audio_mixer_close(sc, flags);
break;
default:
error = ENXIO;
}
device_unref(&sc->dev);
return error;
}
int
audioread(dev_t dev, struct uio *uio, int ioflag)
{
struct audio_softc *sc;
int error;
sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev));
if (sc == NULL)
return ENXIO;
switch (AUDIO_DEV(dev)) {
case AUDIO_DEV_AUDIO:
error = audio_read(sc, uio, ioflag);
break;
case AUDIO_DEV_AUDIOCTL:
error = audio_mixer_read(sc, uio, ioflag);
break;
default:
error = ENXIO;
}
device_unref(&sc->dev);
return error;
}
int
audiowrite(dev_t dev, struct uio *uio, int ioflag)
{
struct audio_softc *sc;
int error;
sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev));
if (sc == NULL)
return ENXIO;
switch (AUDIO_DEV(dev)) {
case AUDIO_DEV_AUDIO:
error = audio_write(sc, uio, ioflag);
break;
case AUDIO_DEV_AUDIOCTL:
error = ENODEV;
break;
default:
error = ENXIO;
}
device_unref(&sc->dev);
return error;
}
int
audioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct audio_softc *sc;
int error;
sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev));
if (sc == NULL)
return ENXIO;
switch (AUDIO_DEV(dev)) {
case AUDIO_DEV_AUDIO:
error = audio_ioctl(sc, cmd, addr);
break;
case AUDIO_DEV_AUDIOCTL:
if (cmd == AUDIO_SETPAR && sc->mode != 0) {
error = EBUSY;
break;
}
if (cmd == AUDIO_START || cmd == AUDIO_STOP) {
error = ENXIO;
break;
}
if (cmd == AUDIO_MIXER_DEVINFO ||
cmd == AUDIO_MIXER_READ ||
cmd == AUDIO_MIXER_WRITE)
error = audio_ioctl_mixer(sc, cmd, addr, p);
else
error = audio_ioctl(sc, cmd, addr);
break;
default:
error = ENXIO;
}
device_unref(&sc->dev);
return error;
}
int
audiokqfilter(dev_t dev, struct knote *kn)
{
struct audio_softc *sc;
struct klist *klist;
int error;
sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev));
if (sc == NULL)
return ENXIO;
error = 0;
switch (AUDIO_DEV(dev)) {
case AUDIO_DEV_AUDIO:
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sc->rec.klist;
kn->kn_fop = &audioread_filtops;
break;
case EVFILT_WRITE:
klist = &sc->play.klist;
kn->kn_fop = &audiowrite_filtops;
break;
default:
error = EINVAL;
goto done;
}
break;
case AUDIO_DEV_AUDIOCTL:
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sc->mix_klist;
kn->kn_fop = &audioctlread_filtops;
break;
default:
error = EINVAL;
goto done;
}
break;
}
kn->kn_hook = sc;
klist_insert(klist, kn);
done:
device_unref(&sc->dev);
return error;
}
void
filt_audiordetach(struct knote *kn)
{
struct audio_softc *sc = kn->kn_hook;
klist_remove(&sc->rec.klist, kn);
}
int
filt_audioread(struct knote *kn, long hint)
{
struct audio_softc *sc = kn->kn_hook;
MUTEX_ASSERT_LOCKED(&audio_lock);
return (sc->mode & AUMODE_RECORD) && (sc->rec.used > 0);
}
void
filt_audiowdetach(struct knote *kn)
{
struct audio_softc *sc = kn->kn_hook;
klist_remove(&sc->play.klist, kn);
}
int
filt_audiowrite(struct knote *kn, long hint)
{
struct audio_softc *sc = kn->kn_hook;
MUTEX_ASSERT_LOCKED(&audio_lock);
return (sc->mode & AUMODE_PLAY) && (sc->play.used < sc->play.len);
}
void
filt_audioctlrdetach(struct knote *kn)
{
struct audio_softc *sc = kn->kn_hook;
klist_remove(&sc->mix_klist, kn);
}
int
filt_audioctlread(struct knote *kn, long hint)
{
struct audio_softc *sc = kn->kn_hook;
MUTEX_ASSERT_LOCKED(&audio_lock);
return (sc->mix_isopen && sc->mix_pending);
}
int
filt_audiomodify(struct kevent *kev, struct knote *kn)
{
int active;
mtx_enter(&audio_lock);
active = knote_modify(kev, kn);
mtx_leave(&audio_lock);
return active;
}
int
filt_audioprocess(struct knote *kn, struct kevent *kev)
{
int active;
mtx_enter(&audio_lock);
active = knote_process(kn, kev);
mtx_leave(&audio_lock);
return active;
}
#if NWSKBD > 0
int
wskbd_initmute(struct audio_softc *sc, struct mixer_devinfo *vol)
{
struct mixer_devinfo *mi;
int index = -1;
mi = malloc(sizeof(struct mixer_devinfo), M_TEMP, M_WAITOK);
for (mi->index = vol->next; mi->index != -1; mi->index = mi->next) {
if (sc->ops->query_devinfo(sc->arg, mi) != 0)
break;
if (strcmp(mi->label.name, AudioNmute) == 0) {
index = mi->index;
break;
}
}
free(mi, M_TEMP, sizeof(struct mixer_devinfo));
return index;
}
int
wskbd_initvol(struct audio_softc *sc, struct wskbd_vol *vol, char *cn, char *dn)
{
struct mixer_devinfo *dev, *cls;
vol->val = vol->mute = -1;
dev = malloc(sizeof(struct mixer_devinfo), M_TEMP, M_WAITOK);
cls = malloc(sizeof(struct mixer_devinfo), M_TEMP, M_WAITOK);
for (dev->index = 0; ; dev->index++) {
if (sc->ops->query_devinfo(sc->arg, dev) != 0)
break;
if (dev->type != AUDIO_MIXER_VALUE)
continue;
cls->index = dev->mixer_class;
if (sc->ops->query_devinfo(sc->arg, cls) != 0)
continue;
if (strcmp(cls->label.name, cn) == 0 &&
strcmp(dev->label.name, dn) == 0) {
vol->val = dev->index;
vol->nch = dev->un.v.num_channels;
vol->step = dev->un.v.delta > 8 ? dev->un.v.delta : 8;
vol->mute = wskbd_initmute(sc, dev);
vol->val_pending = vol->mute_pending = 0;
DPRINTF("%s: wskbd using %s.%s%s\n", DEVNAME(sc),
cn, dn, vol->mute >= 0 ? ", mute control" : "");
break;
}
}
free(cls, M_TEMP, sizeof(struct mixer_devinfo));
free(dev, M_TEMP, sizeof(struct mixer_devinfo));
return (vol->val != -1);
}
void
wskbd_mixer_init(struct audio_softc *sc)
{
static struct {
char *cn, *dn;
} spkr_names[] = {
{AudioCoutputs, AudioNmaster},
{AudioCinputs, AudioNdac},
{AudioCoutputs, AudioNdac},
{AudioCoutputs, AudioNoutput}
}, mic_names[] = {
{AudioCrecord, AudioNrecord},
{AudioCrecord, AudioNvolume},
{AudioCinputs, AudioNrecord},
{AudioCinputs, AudioNvolume},
{AudioCinputs, AudioNinput}
};
int i;
for (i = 0; i < sizeof(spkr_names) / sizeof(spkr_names[0]); i++) {
if (wskbd_initvol(sc, &sc->spkr,
spkr_names[i].cn, spkr_names[i].dn))
break;
}
for (i = 0; i < sizeof(mic_names) / sizeof(mic_names[0]); i++) {
if (wskbd_initvol(sc, &sc->mic,
mic_names[i].cn, mic_names[i].dn))
break;
}
task_set(&sc->wskbd_task, wskbd_mixer_cb, sc);
}
void
wskbd_mixer_update(struct audio_softc *sc, struct wskbd_vol *vol)
{
struct mixer_ctrl ctrl;
int val_pending, mute_pending, i, gain, error, s;
s = spltty();
val_pending = vol->val_pending;
vol->val_pending = 0;
mute_pending = vol->mute_pending;
vol->mute_pending = 0;
splx(s);
if (sc->ops == NULL)
return;
if (vol->mute >= 0 && mute_pending) {
ctrl.dev = vol->mute;
ctrl.type = AUDIO_MIXER_ENUM;
error = sc->ops->get_port(sc->arg, &ctrl);
if (error) {
DPRINTF("%s: get mute err = %d\n", DEVNAME(sc), error);
return;
}
switch (mute_pending) {
case WSKBD_MUTE_TOGGLE:
ctrl.un.ord = !ctrl.un.ord;
break;
case WSKBD_MUTE_DISABLE:
ctrl.un.ord = 0;
break;
case WSKBD_MUTE_ENABLE:
ctrl.un.ord = 1;
break;
}
DPRINTFN(1, "%s: wskbd mute setting to %d\n",
DEVNAME(sc), ctrl.un.ord);
error = sc->ops->set_port(sc->arg, &ctrl);
if (error) {
DPRINTF("%s: set mute err = %d\n", DEVNAME(sc), error);
return;
}
audio_event(sc, vol->mute);
}
if (vol->val >= 0 && val_pending) {
ctrl.dev = vol->val;
ctrl.type = AUDIO_MIXER_VALUE;
ctrl.un.value.num_channels = vol->nch;
error = sc->ops->get_port(sc->arg, &ctrl);
if (error) {
DPRINTF("%s: get mute err = %d\n", DEVNAME(sc), error);
return;
}
for (i = 0; i < vol->nch; i++) {
gain = ctrl.un.value.level[i] + vol->step * val_pending;
if (gain > AUDIO_MAX_GAIN)
gain = AUDIO_MAX_GAIN;
else if (gain < AUDIO_MIN_GAIN)
gain = AUDIO_MIN_GAIN;
ctrl.un.value.level[i] = gain;
DPRINTFN(1, "%s: wskbd level %d set to %d\n",
DEVNAME(sc), i, gain);
}
error = sc->ops->set_port(sc->arg, &ctrl);
if (error) {
DPRINTF("%s: set vol err = %d\n", DEVNAME(sc), error);
return;
}
audio_event(sc, vol->val);
}
}
void
wskbd_mixer_cb(void *arg)
{
struct audio_softc *sc = arg;
wskbd_mixer_update(sc, &sc->spkr);
wskbd_mixer_update(sc, &sc->mic);
device_unref(&sc->dev);
}
int
wskbd_set_mixermute(long mute, long out)
{
struct audio_softc *sc;
struct wskbd_vol *vol;
sc = (struct audio_softc *)device_lookup(&audio_cd, 0);
if (sc == NULL)
return ENODEV;
vol = out ? &sc->spkr : &sc->mic;
vol->mute_pending = mute ? WSKBD_MUTE_ENABLE : WSKBD_MUTE_DISABLE;
if (!task_add(systq, &sc->wskbd_task))
device_unref(&sc->dev);
return 0;
}
/*
* Adjust the volume of the audio device associated with the given cookie.
* Otherwise, fallback to audio0.
*/
int
wskbd_set_mixervolume_dev(void *cookie, long dir, long out)
{
int unit = 0;
int i;
for (i = 0; i < audio_cd.cd_ndevs; i++) {
struct audio_softc *sc;
sc = (struct audio_softc *)device_lookup(&audio_cd, i);
if (sc == NULL)
continue;
if (sc->cookie != cookie) {
device_unref(&sc->dev);
continue;
}
device_unref(&sc->dev);
unit = i;
break;
}
return wskbd_set_mixervolume_unit(unit, dir, out);
}
int
wskbd_set_mixervolume(long dir, long out)
{
return wskbd_set_mixervolume_unit(0, dir, out);
}
int
wskbd_set_mixervolume_unit(int unit, long dir, long out)
{
struct audio_softc *sc;
struct wskbd_vol *vol;
sc = (struct audio_softc *)device_lookup(&audio_cd, unit);
if (sc == NULL)
return ENODEV;
vol = out ? &sc->spkr : &sc->mic;
if (dir == 0)
vol->mute_pending ^= WSKBD_MUTE_TOGGLE;
else
vol->val_pending += dir;
if (!task_add(systq, &sc->wskbd_task))
device_unref(&sc->dev);
return 0;
}
#endif /* NWSKBD > 0 */
8
96
42
10
22
33
5
4
5
5
6
4
2
12
39
50
36
109
11
75
41
15
1
4
27
17
29
3
35
19
19
1
7
7
7
7
33
33
12
3
15
3
12
18
5
49
48
9
5
2
4
9
35
34
2
6
2
3
25
31
14
15
16
12
61
61
27
5
1
1
1
1
1
34
1
1
1
1
10
6
5
10
10
10
10
10
14
14
14
10
9
1
10
3
4
3
3
79
78
14
34
34
5
11
11
3
3
55
54
41
16
41
41
20
41
10
41
41
25
41
25
41
15
41
11
41
41
24
41
20
41
8
6
8
22
3
2
2
2
12
1
1
1
2
4
2
3
1
3
63
25
25
64
63
28
36
64
63
64
64
64
64
64
14
42
41
42
42
42
42
26
16
22
22
22
22
6
6
6
42
42
42
42
42
42
42
11
42
36
36
36
25
64
63
52
4
1
7
64
1
62
63
63
63
56
8
53
3
56
54
56
56
9
9
8
1
9
12
12
9
1
2
2
2
2
51
21
2
11
49
11
10
4
5
2
7
50
40
9
2
47
1
1
1
2
12
38
14
11
3
6
3
3
3
4
4
47
12
37
5
6
8
34
1
1
1
3
1
1
1
9
1
1
11
4
7
3
8
8
2
2
2
3
21
96
1
49
1
15
34
33
1
8
1
6
9
3
1
3
8
47
32
46
32
97
97
55
55
504
427
98
20
1
14
65
67
29
1
5
1
7
21
3
1
23
21
51
3
1
50
52
29
23
52
63
44
13
13
48
56
98
62
37
98
75
23
98
58
42
40
46
56
54
54
186
185
1556
210
15
14
15
35
210
1263
2200
368
367
1264
2089
126
15
15
14
72
7
107
107
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302
6303
6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315
6316
6317
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
6614
6615
6616
6617
6618
6619
6620
6621
6622
6623
6624
6625
6626
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
6676
6677
6678
6679
6680
6681
6682
6683
6684
6685
6686
6687
6688
6689
6690
6691
6692
6693
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723
6724
6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
6779
6780
6781
6782
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809
6810
6811
6812
6813
6814
6815
6816
6817
6818
6819
6820
6821
6822
6823
6824
6825
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
6847
6848
6849
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
6918
6919
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
6993
6994
6995
6996
6997
6998
6999
7000
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
7092
7093
7094
7095
7096
7097
7098
7099
7100
7101
7102
7103
7104
7105
7106
7107
7108
7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
7180
7181
7182
7183
7184
7185
7186
7187
7188
7189
7190
7191
7192
7193
7194
7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297
7298
7299
7300
7301
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
7499
7500
7501
7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
/* $OpenBSD: pf.c,v 1.1140 2022/09/03 19:22:19 bluhm Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Effort sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
*
*/
#include "bpfilter.h"
#include "carp.h"
#include "pflog.h"
#include "pfsync.h"
#include "pflow.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/syslog.h>
#include <crypto/sha2.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_fsm.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/ip_divert.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#include <netinet6/ip6_divert.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#if NPFLOG > 0
#include <net/if_pflog.h>
#endif /* NPFLOG > 0 */
#if NPFLOW > 0
#include <net/if_pflow.h>
#endif /* NPFLOW > 0 */
#if NPFSYNC > 0
#include <net/if_pfsync.h>
#else
struct pfsync_deferral;
#endif /* NPFSYNC > 0 */
/*
* Global variables
*/
struct pf_state_tree pf_statetbl;
struct pf_queuehead pf_queues[2];
struct pf_queuehead *pf_queues_active;
struct pf_queuehead *pf_queues_inactive;
struct pf_status pf_status;
int pf_hdr_limit = 20; /* arbitrary limit, tune in ddb */
SHA2_CTX pf_tcp_secret_ctx;
u_char pf_tcp_secret[16];
int pf_tcp_secret_init;
int pf_tcp_iss_off;
int pf_npurge;
struct task pf_purge_task = TASK_INITIALIZER(pf_purge, &pf_npurge);
struct timeout pf_purge_to = TIMEOUT_INITIALIZER(pf_purge_timeout, NULL);
enum pf_test_status {
PF_TEST_FAIL = -1,
PF_TEST_OK,
PF_TEST_QUICK
};
struct pf_test_ctx {
enum pf_test_status test_status;
struct pf_pdesc *pd;
struct pf_rule_actions act;
u_int8_t icmpcode;
u_int8_t icmptype;
int icmp_dir;
int state_icmp;
int tag;
u_short reason;
struct pf_rule_item *ri;
struct pf_src_node *sns[PF_SN_MAX];
struct pf_rule_slist rules;
struct pf_rule *nr;
struct pf_rule **rm;
struct pf_rule *a;
struct pf_rule **am;
struct pf_ruleset **rsm;
struct pf_ruleset *arsm;
struct pf_ruleset *aruleset;
struct tcphdr *th;
int depth;
};
#define PF_ANCHOR_STACK_MAX 64
struct pool pf_src_tree_pl, pf_rule_pl, pf_queue_pl;
struct pool pf_state_pl, pf_state_key_pl, pf_state_item_pl;
struct pool pf_rule_item_pl, pf_sn_item_pl, pf_pktdelay_pl;
void pf_add_threshold(struct pf_threshold *);
int pf_check_threshold(struct pf_threshold *);
int pf_check_tcp_cksum(struct mbuf *, int, int,
sa_family_t);
static __inline void pf_cksum_fixup(u_int16_t *, u_int16_t, u_int16_t,
u_int8_t);
void pf_cksum_fixup_a(u_int16_t *, const struct pf_addr *,
const struct pf_addr *, sa_family_t, u_int8_t);
int pf_modulate_sack(struct pf_pdesc *,
struct pf_state_peer *);
int pf_icmp_mapping(struct pf_pdesc *, u_int8_t, int *,
u_int16_t *, u_int16_t *);
int pf_change_icmp_af(struct mbuf *, int,
struct pf_pdesc *, struct pf_pdesc *,
struct pf_addr *, struct pf_addr *, sa_family_t,
sa_family_t);
int pf_translate_a(struct pf_pdesc *, struct pf_addr *,
struct pf_addr *);
void pf_translate_icmp(struct pf_pdesc *, struct pf_addr *,
u_int16_t *, struct pf_addr *, struct pf_addr *,
u_int16_t);
int pf_translate_icmp_af(struct pf_pdesc*, int, void *);
void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, int,
sa_family_t, struct pf_rule *, u_int);
void pf_detach_state(struct pf_state *);
void pf_state_key_detach(struct pf_state *, int);
u_int32_t pf_tcp_iss(struct pf_pdesc *);
void pf_rule_to_actions(struct pf_rule *,
struct pf_rule_actions *);
int pf_test_rule(struct pf_pdesc *, struct pf_rule **,
struct pf_state **, struct pf_rule **,
struct pf_ruleset **, u_short *,
struct pfsync_deferral **);
static __inline int pf_create_state(struct pf_pdesc *, struct pf_rule *,
struct pf_rule *, struct pf_rule *,
struct pf_state_key **, struct pf_state_key **,
int *, struct pf_state **, int,
struct pf_rule_slist *, struct pf_rule_actions *,
struct pf_src_node *[]);
static __inline int pf_state_key_addr_setup(struct pf_pdesc *, void *,
int, struct pf_addr *, int, struct pf_addr *,
int, int);
int pf_state_key_setup(struct pf_pdesc *, struct
pf_state_key **, struct pf_state_key **, int);
int pf_tcp_track_full(struct pf_pdesc *,
struct pf_state **, u_short *, int *, int);
int pf_tcp_track_sloppy(struct pf_pdesc *,
struct pf_state **, u_short *);
static __inline int pf_synproxy(struct pf_pdesc *, struct pf_state **,
u_short *);
int pf_test_state(struct pf_pdesc *, struct pf_state **,
u_short *);
int pf_icmp_state_lookup(struct pf_pdesc *,
struct pf_state_key_cmp *, struct pf_state **,
u_int16_t, u_int16_t, int, int *, int, int);
int pf_test_state_icmp(struct pf_pdesc *,
struct pf_state **, u_short *);
u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, int,
u_int16_t);
static __inline int pf_set_rt_ifp(struct pf_state *, struct pf_addr *,
sa_family_t, struct pf_src_node **);
struct pf_divert *pf_get_divert(struct mbuf *);
int pf_walk_option(struct pf_pdesc *, struct ip *,
int, int, u_short *);
int pf_walk_header(struct pf_pdesc *, struct ip *,
u_short *);
int pf_walk_option6(struct pf_pdesc *, struct ip6_hdr *,
int, int, u_short *);
int pf_walk_header6(struct pf_pdesc *, struct ip6_hdr *,
u_short *);
void pf_print_state_parts(struct pf_state *,
struct pf_state_key *, struct pf_state_key *);
int pf_addr_wrap_neq(struct pf_addr_wrap *,
struct pf_addr_wrap *);
int pf_compare_state_keys(struct pf_state_key *,
struct pf_state_key *, struct pfi_kif *, u_int);
int pf_find_state(struct pf_pdesc *,
struct pf_state_key_cmp *, struct pf_state **);
int pf_src_connlimit(struct pf_state **);
int pf_match_rcvif(struct mbuf *, struct pf_rule *);
int pf_step_into_anchor(struct pf_test_ctx *,
struct pf_rule *);
int pf_match_rule(struct pf_test_ctx *,
struct pf_ruleset *);
void pf_counters_inc(int, struct pf_pdesc *,
struct pf_state *, struct pf_rule *,
struct pf_rule *);
int pf_state_key_isvalid(struct pf_state_key *);
struct pf_state_key *pf_state_key_ref(struct pf_state_key *);
void pf_state_key_unref(struct pf_state_key *);
void pf_state_key_link_reverse(struct pf_state_key *,
struct pf_state_key *);
void pf_state_key_unlink_reverse(struct pf_state_key *);
void pf_state_key_link_inpcb(struct pf_state_key *,
struct inpcb *);
void pf_state_key_unlink_inpcb(struct pf_state_key *);
void pf_inpcb_unlink_state_key(struct inpcb *);
void pf_pktenqueue_delayed(void *);
int32_t pf_state_expires(const struct pf_state *, uint8_t);
#if NPFLOG > 0
void pf_log_matches(struct pf_pdesc *, struct pf_rule *,
struct pf_rule *, struct pf_ruleset *,
struct pf_rule_slist *);
#endif /* NPFLOG > 0 */
extern struct pool pfr_ktable_pl;
extern struct pool pfr_kentry_pl;
struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = {
{ &pf_state_pl, PFSTATE_HIWAT, PFSTATE_HIWAT },
{ &pf_src_tree_pl, PFSNODE_HIWAT, PFSNODE_HIWAT },
{ &pf_frent_pl, PFFRAG_FRENT_HIWAT, PFFRAG_FRENT_HIWAT },
{ &pfr_ktable_pl, PFR_KTABLE_HIWAT, PFR_KTABLE_HIWAT },
{ &pfr_kentry_pl, PFR_KENTRY_HIWAT, PFR_KENTRY_HIWAT },
{ &pf_pktdelay_pl, PF_PKTDELAY_MAXPKTS, PF_PKTDELAY_MAXPKTS },
{ &pf_anchor_pl, PF_ANCHOR_HIWAT, PF_ANCHOR_HIWAT }
};
#define BOUND_IFACE(r, k) \
((r)->rule_flag & PFRULE_IFBOUND) ? (k) : pfi_all
#define STATE_INC_COUNTERS(s) \
do { \
struct pf_rule_item *mrm; \
s->rule.ptr->states_cur++; \
s->rule.ptr->states_tot++; \
if (s->anchor.ptr != NULL) { \
s->anchor.ptr->states_cur++; \
s->anchor.ptr->states_tot++; \
} \
SLIST_FOREACH(mrm, &s->match_rules, entry) \
mrm->r->states_cur++; \
} while (0)
static __inline int pf_src_compare(struct pf_src_node *, struct pf_src_node *);
static __inline int pf_state_compare_key(struct pf_state_key *,
struct pf_state_key *);
static __inline int pf_state_compare_id(struct pf_state *,
struct pf_state *);
#ifdef INET6
static __inline void pf_cksum_uncover(u_int16_t *, u_int16_t, u_int8_t);
static __inline void pf_cksum_cover(u_int16_t *, u_int16_t, u_int8_t);
#endif /* INET6 */
static __inline void pf_set_protostate(struct pf_state *, int, u_int8_t);
struct pf_src_tree tree_src_tracking;
struct pf_state_tree_id tree_id;
struct pf_state_list pf_state_list = PF_STATE_LIST_INITIALIZER(pf_state_list);
RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare);
RB_GENERATE(pf_state_tree, pf_state_key, entry, pf_state_compare_key);
RB_GENERATE(pf_state_tree_id, pf_state,
entry_id, pf_state_compare_id);
SLIST_HEAD(pf_rule_gcl, pf_rule) pf_rule_gcl =
SLIST_HEAD_INITIALIZER(pf_rule_gcl);
__inline int
pf_addr_compare(struct pf_addr *a, struct pf_addr *b, sa_family_t af)
{
switch (af) {
case AF_INET:
if (a->addr32[0] > b->addr32[0])
return (1);
if (a->addr32[0] < b->addr32[0])
return (-1);
break;
#ifdef INET6
case AF_INET6:
if (a->addr32[3] > b->addr32[3])
return (1);
if (a->addr32[3] < b->addr32[3])
return (-1);
if (a->addr32[2] > b->addr32[2])
return (1);
if (a->addr32[2] < b->addr32[2])
return (-1);
if (a->addr32[1] > b->addr32[1])
return (1);
if (a->addr32[1] < b->addr32[1])
return (-1);
if (a->addr32[0] > b->addr32[0])
return (1);
if (a->addr32[0] < b->addr32[0])
return (-1);
break;
#endif /* INET6 */
}
return (0);
}
static __inline int
pf_src_compare(struct pf_src_node *a, struct pf_src_node *b)
{
int diff;
if (a->rule.ptr > b->rule.ptr)
return (1);
if (a->rule.ptr < b->rule.ptr)
return (-1);
if ((diff = a->type - b->type) != 0)
return (diff);
if ((diff = a->af - b->af) != 0)
return (diff);
if ((diff = pf_addr_compare(&a->addr, &b->addr, a->af)) != 0)
return (diff);
return (0);
}
static __inline void
pf_set_protostate(struct pf_state *s, int which, u_int8_t newstate)
{
if (which == PF_PEER_DST || which == PF_PEER_BOTH)
s->dst.state = newstate;
if (which == PF_PEER_DST)
return;
if (s->src.state == newstate)
return;
if (s->creatorid == pf_status.hostid && s->key[PF_SK_STACK] != NULL &&
s->key[PF_SK_STACK]->proto == IPPROTO_TCP &&
!(TCPS_HAVEESTABLISHED(s->src.state) ||
s->src.state == TCPS_CLOSED) &&
(TCPS_HAVEESTABLISHED(newstate) || newstate == TCPS_CLOSED))
pf_status.states_halfopen--;
s->src.state = newstate;
}
void
pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af)
{
switch (af) {
case AF_INET:
dst->addr32[0] = src->addr32[0];
break;
#ifdef INET6
case AF_INET6:
dst->addr32[0] = src->addr32[0];
dst->addr32[1] = src->addr32[1];
dst->addr32[2] = src->addr32[2];
dst->addr32[3] = src->addr32[3];
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
}
void
pf_init_threshold(struct pf_threshold *threshold,
u_int32_t limit, u_int32_t seconds)
{
threshold->limit = limit * PF_THRESHOLD_MULT;
threshold->seconds = seconds;
threshold->count = 0;
threshold->last = getuptime();
}
void
pf_add_threshold(struct pf_threshold *threshold)
{
u_int32_t t = getuptime(), diff = t - threshold->last;
if (diff >= threshold->seconds)
threshold->count = 0;
else
threshold->count -= threshold->count * diff /
threshold->seconds;
threshold->count += PF_THRESHOLD_MULT;
threshold->last = t;
}
int
pf_check_threshold(struct pf_threshold *threshold)
{
return (threshold->count > threshold->limit);
}
void
pf_state_list_insert(struct pf_state_list *pfs, struct pf_state *st)
{
/*
* we can always put states on the end of the list.
*
* things reading the list should take a read lock, then
* the mutex, get the head and tail pointers, release the
* mutex, and then they can iterate between the head and tail.
*/
pf_state_ref(st); /* get a ref for the list */
mtx_enter(&pfs->pfs_mtx);
TAILQ_INSERT_TAIL(&pfs->pfs_list, st, entry_list);
mtx_leave(&pfs->pfs_mtx);
}
void
pf_state_list_remove(struct pf_state_list *pfs, struct pf_state *st)
{
/* states can only be removed when the write lock is held */
rw_assert_wrlock(&pfs->pfs_rwl);
mtx_enter(&pfs->pfs_mtx);
TAILQ_REMOVE(&pfs->pfs_list, st, entry_list);
mtx_leave(&pfs->pfs_mtx);
pf_state_unref(st); /* list no longer references the state */
}
int
pf_src_connlimit(struct pf_state **state)
{
int bad = 0;
struct pf_src_node *sn;
if ((sn = pf_get_src_node((*state), PF_SN_NONE)) == NULL)
return (0);
sn->conn++;
(*state)->src.tcp_est = 1;
pf_add_threshold(&sn->conn_rate);
if ((*state)->rule.ptr->max_src_conn &&
(*state)->rule.ptr->max_src_conn < sn->conn) {
pf_status.lcounters[LCNT_SRCCONN]++;
bad++;
}
if ((*state)->rule.ptr->max_src_conn_rate.limit &&
pf_check_threshold(&sn->conn_rate)) {
pf_status.lcounters[LCNT_SRCCONNRATE]++;
bad++;
}
if (!bad)
return (0);
if ((*state)->rule.ptr->overload_tbl) {
struct pfr_addr p;
u_int32_t killed = 0;
pf_status.lcounters[LCNT_OVERLOAD_TABLE]++;
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: pf_src_connlimit: blocking address ");
pf_print_host(&sn->addr, 0,
(*state)->key[PF_SK_WIRE]->af);
}
memset(&p, 0, sizeof(p));
p.pfra_af = (*state)->key[PF_SK_WIRE]->af;
switch ((*state)->key[PF_SK_WIRE]->af) {
case AF_INET:
p.pfra_net = 32;
p.pfra_ip4addr = sn->addr.v4;
break;
#ifdef INET6
case AF_INET6:
p.pfra_net = 128;
p.pfra_ip6addr = sn->addr.v6;
break;
#endif /* INET6 */
}
pfr_insert_kentry((*state)->rule.ptr->overload_tbl,
&p, gettime());
/* kill existing states if that's required. */
if ((*state)->rule.ptr->flush) {
struct pf_state_key *sk;
struct pf_state *st;
pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++;
RB_FOREACH(st, pf_state_tree_id, &tree_id) {
sk = st->key[PF_SK_WIRE];
/*
* Kill states from this source. (Only those
* from the same rule if PF_FLUSH_GLOBAL is not
* set)
*/
if (sk->af ==
(*state)->key[PF_SK_WIRE]->af &&
(((*state)->direction == PF_OUT &&
PF_AEQ(&sn->addr, &sk->addr[1], sk->af)) ||
((*state)->direction == PF_IN &&
PF_AEQ(&sn->addr, &sk->addr[0], sk->af))) &&
((*state)->rule.ptr->flush &
PF_FLUSH_GLOBAL ||
(*state)->rule.ptr == st->rule.ptr)) {
st->timeout = PFTM_PURGE;
pf_set_protostate(st, PF_PEER_BOTH,
TCPS_CLOSED);
killed++;
}
}
if (pf_status.debug >= LOG_NOTICE)
addlog(", %u states killed", killed);
}
if (pf_status.debug >= LOG_NOTICE)
addlog("\n");
}
/* kill this state */
(*state)->timeout = PFTM_PURGE;
pf_set_protostate(*state, PF_PEER_BOTH, TCPS_CLOSED);
return (1);
}
int
pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule,
enum pf_sn_types type, sa_family_t af, struct pf_addr *src,
struct pf_addr *raddr, struct pfi_kif *kif)
{
struct pf_src_node k;
if (*sn == NULL) {
k.af = af;
k.type = type;
pf_addrcpy(&k.addr, src, af);
k.rule.ptr = rule;
pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
*sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
}
if (*sn == NULL) {
if (!rule->max_src_nodes ||
rule->src_nodes < rule->max_src_nodes)
(*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT | PR_ZERO);
else
pf_status.lcounters[LCNT_SRCNODES]++;
if ((*sn) == NULL)
return (-1);
pf_init_threshold(&(*sn)->conn_rate,
rule->max_src_conn_rate.limit,
rule->max_src_conn_rate.seconds);
(*sn)->type = type;
(*sn)->af = af;
(*sn)->rule.ptr = rule;
pf_addrcpy(&(*sn)->addr, src, af);
if (raddr)
pf_addrcpy(&(*sn)->raddr, raddr, af);
if (RB_INSERT(pf_src_tree,
&tree_src_tracking, *sn) != NULL) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: src_tree insert failed: ");
pf_print_host(&(*sn)->addr, 0, af);
addlog("\n");
}
pool_put(&pf_src_tree_pl, *sn);
return (-1);
}
(*sn)->creation = getuptime();
(*sn)->rule.ptr->src_nodes++;
if (kif != NULL) {
(*sn)->kif = kif;
pfi_kif_ref(kif, PFI_KIF_REF_SRCNODE);
}
pf_status.scounters[SCNT_SRC_NODE_INSERT]++;
pf_status.src_nodes++;
} else {
if (rule->max_src_states &&
(*sn)->states >= rule->max_src_states) {
pf_status.lcounters[LCNT_SRCSTATES]++;
return (-1);
}
}
return (0);
}
void
pf_remove_src_node(struct pf_src_node *sn)
{
if (sn->states > 0 || sn->expire > getuptime())
return;
sn->rule.ptr->src_nodes--;
if (sn->rule.ptr->states_cur == 0 &&
sn->rule.ptr->src_nodes == 0)
pf_rm_rule(NULL, sn->rule.ptr);
RB_REMOVE(pf_src_tree, &tree_src_tracking, sn);
pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
pf_status.src_nodes--;
pfi_kif_unref(sn->kif, PFI_KIF_REF_SRCNODE);
pool_put(&pf_src_tree_pl, sn);
}
struct pf_src_node *
pf_get_src_node(struct pf_state *s, enum pf_sn_types type)
{
struct pf_sn_item *sni;
SLIST_FOREACH(sni, &s->src_nodes, next)
if (sni->sn->type == type)
return (sni->sn);
return (NULL);
}
void
pf_state_rm_src_node(struct pf_state *s, struct pf_src_node *sn)
{
struct pf_sn_item *sni, *snin, *snip = NULL;
for (sni = SLIST_FIRST(&s->src_nodes); sni; sni = snin) {
snin = SLIST_NEXT(sni, next);
if (sni->sn == sn) {
if (snip)
SLIST_REMOVE_AFTER(snip, next);
else
SLIST_REMOVE_HEAD(&s->src_nodes, next);
pool_put(&pf_sn_item_pl, sni);
sni = NULL;
sn->states--;
}
if (sni != NULL)
snip = sni;
}
}
/* state table stuff */
static __inline int
pf_state_compare_key(struct pf_state_key *a, struct pf_state_key *b)
{
int diff;
if ((diff = a->proto - b->proto) != 0)
return (diff);
if ((diff = a->af - b->af) != 0)
return (diff);
if ((diff = pf_addr_compare(&a->addr[0], &b->addr[0], a->af)) != 0)
return (diff);
if ((diff = pf_addr_compare(&a->addr[1], &b->addr[1], a->af)) != 0)
return (diff);
if ((diff = a->port[0] - b->port[0]) != 0)
return (diff);
if ((diff = a->port[1] - b->port[1]) != 0)
return (diff);
if ((diff = a->rdomain - b->rdomain) != 0)
return (diff);
return (0);
}
static __inline int
pf_state_compare_id(struct pf_state *a, struct pf_state *b)
{
if (a->id > b->id)
return (1);
if (a->id < b->id)
return (-1);
if (a->creatorid > b->creatorid)
return (1);
if (a->creatorid < b->creatorid)
return (-1);
return (0);
}
int
pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx)
{
struct pf_state_item *si;
struct pf_state_key *cur;
struct pf_state *olds = NULL;
KASSERT(s->key[idx] == NULL);
if ((cur = RB_INSERT(pf_state_tree, &pf_statetbl, sk)) != NULL) {
/* key exists. check for same kif, if none, add to key */
TAILQ_FOREACH(si, &cur->states, entry)
if (si->s->kif == s->kif &&
((si->s->key[PF_SK_WIRE]->af == sk->af &&
si->s->direction == s->direction) ||
(si->s->key[PF_SK_WIRE]->af !=
si->s->key[PF_SK_STACK]->af &&
sk->af == si->s->key[PF_SK_STACK]->af &&
si->s->direction != s->direction))) {
int reuse = 0;
if (sk->proto == IPPROTO_TCP &&
si->s->src.state >= TCPS_FIN_WAIT_2 &&
si->s->dst.state >= TCPS_FIN_WAIT_2)
reuse = 1;
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: %s key attach %s on %s: ",
(idx == PF_SK_WIRE) ?
"wire" : "stack",
reuse ? "reuse" : "failed",
s->kif->pfik_name);
pf_print_state_parts(s,
(idx == PF_SK_WIRE) ? sk : NULL,
(idx == PF_SK_STACK) ? sk : NULL);
addlog(", existing: ");
pf_print_state_parts(si->s,
(idx == PF_SK_WIRE) ? sk : NULL,
(idx == PF_SK_STACK) ? sk : NULL);
addlog("\n");
}
if (reuse) {
pf_set_protostate(si->s, PF_PEER_BOTH,
TCPS_CLOSED);
/* remove late or sks can go away */
olds = si->s;
} else {
pool_put(&pf_state_key_pl, sk);
return (-1); /* collision! */
}
}
pool_put(&pf_state_key_pl, sk);
s->key[idx] = cur;
} else
s->key[idx] = sk;
if ((si = pool_get(&pf_state_item_pl, PR_NOWAIT)) == NULL) {
pf_state_key_detach(s, idx);
return (-1);
}
si->s = s;
/* list is sorted, if-bound states before floating */
if (s->kif == pfi_all)
TAILQ_INSERT_TAIL(&s->key[idx]->states, si, entry);
else
TAILQ_INSERT_HEAD(&s->key[idx]->states, si, entry);
if (olds)
pf_remove_state(olds);
return (0);
}
void
pf_detach_state(struct pf_state *s)
{
if (s->key[PF_SK_WIRE] == s->key[PF_SK_STACK])
s->key[PF_SK_WIRE] = NULL;
if (s->key[PF_SK_STACK] != NULL)
pf_state_key_detach(s, PF_SK_STACK);
if (s->key[PF_SK_WIRE] != NULL)
pf_state_key_detach(s, PF_SK_WIRE);
}
void
pf_state_key_detach(struct pf_state *s, int idx)
{
struct pf_state_item *si;
struct pf_state_key *sk;
if (s->key[idx] == NULL)
return;
si = TAILQ_FIRST(&s->key[idx]->states);
while (si && si->s != s)
si = TAILQ_NEXT(si, entry);
if (si) {
TAILQ_REMOVE(&s->key[idx]->states, si, entry);
pool_put(&pf_state_item_pl, si);
}
sk = s->key[idx];
s->key[idx] = NULL;
if (TAILQ_EMPTY(&sk->states)) {
RB_REMOVE(pf_state_tree, &pf_statetbl, sk);
sk->removed = 1;
pf_state_key_unlink_reverse(sk);
pf_state_key_unlink_inpcb(sk);
pf_state_key_unref(sk);
}
}
struct pf_state_key *
pf_alloc_state_key(int pool_flags)
{
struct pf_state_key *sk;
if ((sk = pool_get(&pf_state_key_pl, pool_flags)) == NULL)
return (NULL);
TAILQ_INIT(&sk->states);
return (sk);
}
static __inline int
pf_state_key_addr_setup(struct pf_pdesc *pd, void *arg, int sidx,
struct pf_addr *saddr, int didx, struct pf_addr *daddr, int af, int multi)
{
struct pf_state_key_cmp *key = arg;
#ifdef INET6
struct pf_addr *target;
if (af == AF_INET || pd->proto != IPPROTO_ICMPV6)
goto copy;
switch (pd->hdr.icmp6.icmp6_type) {
case ND_NEIGHBOR_SOLICIT:
if (multi)
return (-1);
target = (struct pf_addr *)&pd->hdr.nd_ns.nd_ns_target;
daddr = target;
break;
case ND_NEIGHBOR_ADVERT:
if (multi)
return (-1);
target = (struct pf_addr *)&pd->hdr.nd_ns.nd_ns_target;
saddr = target;
if (IN6_IS_ADDR_MULTICAST(&pd->dst->v6)) {
key->addr[didx].addr32[0] = 0;
key->addr[didx].addr32[1] = 0;
key->addr[didx].addr32[2] = 0;
key->addr[didx].addr32[3] = 0;
daddr = NULL; /* overwritten */
}
break;
default:
if (multi) {
key->addr[sidx].addr32[0] = __IPV6_ADDR_INT32_MLL;
key->addr[sidx].addr32[1] = 0;
key->addr[sidx].addr32[2] = 0;
key->addr[sidx].addr32[3] = __IPV6_ADDR_INT32_ONE;
saddr = NULL; /* overwritten */
}
}
copy:
#endif /* INET6 */
if (saddr)
pf_addrcpy(&key->addr[sidx], saddr, af);
if (daddr)
pf_addrcpy(&key->addr[didx], daddr, af);
return (0);
}
int
pf_state_key_setup(struct pf_pdesc *pd, struct pf_state_key **skw,
struct pf_state_key **sks, int rtableid)
{
/* if returning error we MUST pool_put state keys ourselves */
struct pf_state_key *sk1, *sk2;
u_int wrdom = pd->rdomain;
int afto = pd->af != pd->naf;
if ((sk1 = pf_alloc_state_key(PR_NOWAIT | PR_ZERO)) == NULL)
return (ENOMEM);
pf_state_key_addr_setup(pd, sk1, pd->sidx, pd->src, pd->didx, pd->dst,
pd->af, 0);
sk1->port[pd->sidx] = pd->osport;
sk1->port[pd->didx] = pd->odport;
sk1->proto = pd->proto;
sk1->af = pd->af;
sk1->rdomain = pd->rdomain;
PF_REF_INIT(sk1->refcnt);
sk1->removed = 0;
if (rtableid >= 0)
wrdom = rtable_l2(rtableid);
if (PF_ANEQ(&pd->nsaddr, pd->src, pd->af) ||
PF_ANEQ(&pd->ndaddr, pd->dst, pd->af) ||
pd->nsport != pd->osport || pd->ndport != pd->odport ||
wrdom != pd->rdomain || afto) { /* NAT/NAT64 */
if ((sk2 = pf_alloc_state_key(PR_NOWAIT | PR_ZERO)) == NULL) {
pool_put(&pf_state_key_pl, sk1);
return (ENOMEM);
}
pf_state_key_addr_setup(pd, sk2, afto ? pd->didx : pd->sidx,
&pd->nsaddr, afto ? pd->sidx : pd->didx, &pd->ndaddr,
pd->naf, 0);
sk2->port[afto ? pd->didx : pd->sidx] = pd->nsport;
sk2->port[afto ? pd->sidx : pd->didx] = pd->ndport;
if (afto) {
switch (pd->proto) {
case IPPROTO_ICMP:
sk2->proto = IPPROTO_ICMPV6;
break;
case IPPROTO_ICMPV6:
sk2->proto = IPPROTO_ICMP;
break;
default:
sk2->proto = pd->proto;
}
} else
sk2->proto = pd->proto;
sk2->af = pd->naf;
sk2->rdomain = wrdom;
PF_REF_INIT(sk2->refcnt);
sk2->removed = 0;
} else
sk2 = sk1;
if (pd->dir == PF_IN) {
*skw = sk1;
*sks = sk2;
} else {
*sks = sk1;
*skw = sk2;
}
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: key setup: ");
pf_print_state_parts(NULL, *skw, *sks);
addlog("\n");
}
return (0);
}
int
pf_state_insert(struct pfi_kif *kif, struct pf_state_key **skw,
struct pf_state_key **sks, struct pf_state *s)
{
PF_ASSERT_LOCKED();
s->kif = kif;
PF_STATE_ENTER_WRITE();
if (*skw == *sks) {
if (pf_state_key_attach(*skw, s, PF_SK_WIRE)) {
PF_STATE_EXIT_WRITE();
return (-1);
}
*skw = *sks = s->key[PF_SK_WIRE];
s->key[PF_SK_STACK] = s->key[PF_SK_WIRE];
} else {
if (pf_state_key_attach(*skw, s, PF_SK_WIRE)) {
pool_put(&pf_state_key_pl, *sks);
PF_STATE_EXIT_WRITE();
return (-1);
}
*skw = s->key[PF_SK_WIRE];
if (pf_state_key_attach(*sks, s, PF_SK_STACK)) {
pf_state_key_detach(s, PF_SK_WIRE);
PF_STATE_EXIT_WRITE();
return (-1);
}
*sks = s->key[PF_SK_STACK];
}
if (s->id == 0 && s->creatorid == 0) {
s->id = htobe64(pf_status.stateid++);
s->creatorid = pf_status.hostid;
}
if (RB_INSERT(pf_state_tree_id, &tree_id, s) != NULL) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: state insert failed: "
"id: %016llx creatorid: %08x",
betoh64(s->id), ntohl(s->creatorid));
addlog("\n");
}
pf_detach_state(s);
PF_STATE_EXIT_WRITE();
return (-1);
}
pf_state_list_insert(&pf_state_list, s);
pf_status.fcounters[FCNT_STATE_INSERT]++;
pf_status.states++;
pfi_kif_ref(kif, PFI_KIF_REF_STATE);
PF_STATE_EXIT_WRITE();
#if NPFSYNC > 0
pfsync_insert_state(s);
#endif /* NPFSYNC > 0 */
return (0);
}
struct pf_state *
pf_find_state_byid(struct pf_state_cmp *key)
{
pf_status.fcounters[FCNT_STATE_SEARCH]++;
return (RB_FIND(pf_state_tree_id, &tree_id, (struct pf_state *)key));
}
int
pf_compare_state_keys(struct pf_state_key *a, struct pf_state_key *b,
struct pfi_kif *kif, u_int dir)
{
/* a (from hdr) and b (new) must be exact opposites of each other */
if (a->af == b->af && a->proto == b->proto &&
PF_AEQ(&a->addr[0], &b->addr[1], a->af) &&
PF_AEQ(&a->addr[1], &b->addr[0], a->af) &&
a->port[0] == b->port[1] &&
a->port[1] == b->port[0] && a->rdomain == b->rdomain)
return (0);
else {
/* mismatch. must not happen. */
if (pf_status.debug >= LOG_ERR) {
log(LOG_ERR,
"pf: state key linking mismatch! dir=%s, "
"if=%s, stored af=%u, a0: ",
dir == PF_OUT ? "OUT" : "IN",
kif->pfik_name, a->af);
pf_print_host(&a->addr[0], a->port[0], a->af);
addlog(", a1: ");
pf_print_host(&a->addr[1], a->port[1], a->af);
addlog(", proto=%u", a->proto);
addlog(", found af=%u, a0: ", b->af);
pf_print_host(&b->addr[0], b->port[0], b->af);
addlog(", a1: ");
pf_print_host(&b->addr[1], b->port[1], b->af);
addlog(", proto=%u", b->proto);
addlog("\n");
}
return (-1);
}
}
int
pf_find_state(struct pf_pdesc *pd, struct pf_state_key_cmp *key,
struct pf_state **state)
{
struct pf_state_key *sk, *pkt_sk, *inp_sk;
struct pf_state_item *si;
struct pf_state *s = NULL;
pf_status.fcounters[FCNT_STATE_SEARCH]++;
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: key search, %s on %s: ",
pd->dir == PF_OUT ? "out" : "in", pd->kif->pfik_name);
pf_print_state_parts(NULL, (struct pf_state_key *)key, NULL);
addlog("\n");
}
inp_sk = NULL;
pkt_sk = NULL;
sk = NULL;
if (pd->dir == PF_OUT) {
/* first if block deals with outbound forwarded packet */
pkt_sk = pd->m->m_pkthdr.pf.statekey;
if (!pf_state_key_isvalid(pkt_sk)) {
pf_mbuf_unlink_state_key(pd->m);
pkt_sk = NULL;
}
if (pkt_sk && pf_state_key_isvalid(pkt_sk->reverse))
sk = pkt_sk->reverse;
if (pkt_sk == NULL) {
/* here we deal with local outbound packet */
if (pd->m->m_pkthdr.pf.inp != NULL) {
inp_sk = pd->m->m_pkthdr.pf.inp->inp_pf_sk;
if (pf_state_key_isvalid(inp_sk))
sk = inp_sk;
else
pf_inpcb_unlink_state_key(
pd->m->m_pkthdr.pf.inp);
}
}
}
if (sk == NULL) {
if ((sk = RB_FIND(pf_state_tree, &pf_statetbl,
(struct pf_state_key *)key)) == NULL)
return (PF_DROP);
if (pd->dir == PF_OUT && pkt_sk &&
pf_compare_state_keys(pkt_sk, sk, pd->kif, pd->dir) == 0)
pf_state_key_link_reverse(sk, pkt_sk);
else if (pd->dir == PF_OUT && pd->m->m_pkthdr.pf.inp &&
!pd->m->m_pkthdr.pf.inp->inp_pf_sk && !sk->inp)
pf_state_key_link_inpcb(sk, pd->m->m_pkthdr.pf.inp);
}
/* remove firewall data from outbound packet */
if (pd->dir == PF_OUT)
pf_pkt_addr_changed(pd->m);
/* list is sorted, if-bound states before floating ones */
TAILQ_FOREACH(si, &sk->states, entry)
if (si->s->timeout != PFTM_PURGE &&
(si->s->kif == pfi_all || si->s->kif == pd->kif) &&
((si->s->key[PF_SK_WIRE]->af == si->s->key[PF_SK_STACK]->af
&& sk == (pd->dir == PF_IN ? si->s->key[PF_SK_WIRE] :
si->s->key[PF_SK_STACK])) ||
(si->s->key[PF_SK_WIRE]->af != si->s->key[PF_SK_STACK]->af
&& pd->dir == PF_IN && (sk == si->s->key[PF_SK_STACK] ||
sk == si->s->key[PF_SK_WIRE])))) {
s = si->s;
break;
}
if (s == NULL)
return (PF_DROP);
if (ISSET(s->state_flags, PFSTATE_INP_UNLINKED))
return (PF_DROP);
if (s->rule.ptr->pktrate.limit && pd->dir == s->direction) {
pf_add_threshold(&s->rule.ptr->pktrate);
if (pf_check_threshold(&s->rule.ptr->pktrate))
return (PF_DROP);
}
*state = s;
return (PF_MATCH);
}
struct pf_state *
pf_find_state_all(struct pf_state_key_cmp *key, u_int dir, int *more)
{
struct pf_state_key *sk;
struct pf_state_item *si, *ret = NULL;
pf_status.fcounters[FCNT_STATE_SEARCH]++;
sk = RB_FIND(pf_state_tree, &pf_statetbl, (struct pf_state_key *)key);
if (sk != NULL) {
TAILQ_FOREACH(si, &sk->states, entry)
if (dir == PF_INOUT ||
(sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] :
si->s->key[PF_SK_STACK]))) {
if (more == NULL)
return (si->s);
if (ret)
(*more)++;
else
ret = si;
}
}
return (ret ? ret->s : NULL);
}
void
pf_state_export(struct pfsync_state *sp, struct pf_state *st)
{
int32_t expire;
memset(sp, 0, sizeof(struct pfsync_state));
/* copy from state key */
sp->key[PF_SK_WIRE].addr[0] = st->key[PF_SK_WIRE]->addr[0];
sp->key[PF_SK_WIRE].addr[1] = st->key[PF_SK_WIRE]->addr[1];
sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0];
sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1];
sp->key[PF_SK_WIRE].rdomain = htons(st->key[PF_SK_WIRE]->rdomain);
sp->key[PF_SK_WIRE].af = st->key[PF_SK_WIRE]->af;
sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0];
sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1];
sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0];
sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1];
sp->key[PF_SK_STACK].rdomain = htons(st->key[PF_SK_STACK]->rdomain);
sp->key[PF_SK_STACK].af = st->key[PF_SK_STACK]->af;
sp->rtableid[PF_SK_WIRE] = htonl(st->rtableid[PF_SK_WIRE]);
sp->rtableid[PF_SK_STACK] = htonl(st->rtableid[PF_SK_STACK]);
sp->proto = st->key[PF_SK_WIRE]->proto;
sp->af = st->key[PF_SK_WIRE]->af;
/* copy from state */
strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname));
sp->rt = st->rt;
sp->rt_addr = st->rt_addr;
sp->creation = htonl(getuptime() - st->creation);
expire = pf_state_expires(st, st->timeout);
if (expire <= getuptime())
sp->expire = htonl(0);
else
sp->expire = htonl(expire - getuptime());
sp->direction = st->direction;
#if NPFLOG > 0
sp->log = st->log;
#endif /* NPFLOG > 0 */
sp->timeout = st->timeout;
sp->state_flags = htons(st->state_flags);
if (!SLIST_EMPTY(&st->src_nodes))
sp->sync_flags |= PFSYNC_FLAG_SRCNODE;
sp->id = st->id;
sp->creatorid = st->creatorid;
pf_state_peer_hton(&st->src, &sp->src);
pf_state_peer_hton(&st->dst, &sp->dst);
if (st->rule.ptr == NULL)
sp->rule = htonl(-1);
else
sp->rule = htonl(st->rule.ptr->nr);
if (st->anchor.ptr == NULL)
sp->anchor = htonl(-1);
else
sp->anchor = htonl(st->anchor.ptr->nr);
sp->nat_rule = htonl(-1); /* left for compat, nat_rule is gone */
pf_state_counter_hton(st->packets[0], sp->packets[0]);
pf_state_counter_hton(st->packets[1], sp->packets[1]);
pf_state_counter_hton(st->bytes[0], sp->bytes[0]);
pf_state_counter_hton(st->bytes[1], sp->bytes[1]);
sp->max_mss = htons(st->max_mss);
sp->min_ttl = st->min_ttl;
sp->set_tos = st->set_tos;
sp->set_prio[0] = st->set_prio[0];
sp->set_prio[1] = st->set_prio[1];
}
/* END state table stuff */
void
pf_purge_expired_rules(void)
{
struct pf_rule *r;
PF_ASSERT_LOCKED();
if (SLIST_EMPTY(&pf_rule_gcl))
return;
while ((r = SLIST_FIRST(&pf_rule_gcl)) != NULL) {
SLIST_REMOVE(&pf_rule_gcl, r, pf_rule, gcle);
KASSERT(r->rule_flag & PFRULE_EXPIRED);
pf_purge_rule(r);
}
}
void
pf_purge_timeout(void *unused)
{
/* XXX move to systqmp to avoid KERNEL_LOCK */
task_add(systq, &pf_purge_task);
}
void
pf_purge(void *xnloops)
{
int *nloops = xnloops;
/*
* process a fraction of the state table every second
* Note:
* we no longer need PF_LOCK() here, because
* pf_purge_expired_states() uses pf_state_lock to maintain
* consistency.
*/
if (pf_default_rule.timeout[PFTM_INTERVAL] > 0)
pf_purge_expired_states(1 + (pf_status.states
/ pf_default_rule.timeout[PFTM_INTERVAL]));
NET_LOCK();
PF_LOCK();
/* purge other expired types every PFTM_INTERVAL seconds */
if (++(*nloops) >= pf_default_rule.timeout[PFTM_INTERVAL]) {
pf_purge_expired_src_nodes();
pf_purge_expired_rules();
}
PF_UNLOCK();
/*
* Fragments don't require PF_LOCK(), they use their own lock.
*/
if ((*nloops) >= pf_default_rule.timeout[PFTM_INTERVAL]) {
pf_purge_expired_fragments();
*nloops = 0;
}
NET_UNLOCK();
timeout_add_sec(&pf_purge_to, 1);
}
int32_t
pf_state_expires(const struct pf_state *state, uint8_t stimeout)
{
u_int32_t timeout;
u_int32_t start;
u_int32_t end;
u_int32_t states;
/*
* pf_state_expires is used by the state purge task to
* decide if a state is a candidate for cleanup, and by the
* pfsync state export code to populate an expiry time.
*
* this function may be called by the state purge task while
* the state is being modified. avoid inconsistent reads of
* state->timeout by having the caller do the read (and any
* checks it needs to do on the same variable) and then pass
* their view of the timeout in here for this function to use.
* the only consequence of using a stale timeout value is
* that the state won't be a candidate for purging until the
* next pass of the purge task.
*/
/* handle all PFTM_* > PFTM_MAX here */
if (stimeout == PFTM_PURGE)
return (0);
KASSERT(stimeout != PFTM_UNLINKED);
KASSERT(stimeout < PFTM_MAX);
timeout = state->rule.ptr->timeout[stimeout];
if (!timeout)
timeout = pf_default_rule.timeout[stimeout];
start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START];
if (start) {
end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END];
states = state->rule.ptr->states_cur;
} else {
start = pf_default_rule.timeout[PFTM_ADAPTIVE_START];
end = pf_default_rule.timeout[PFTM_ADAPTIVE_END];
states = pf_status.states;
}
if (end && states > start && start < end) {
if (states >= end)
return (0);
timeout = (u_int64_t)timeout * (end - states) / (end - start);
}
return (state->expire + timeout);
}
void
pf_purge_expired_src_nodes(void)
{
struct pf_src_node *cur, *next;
PF_ASSERT_LOCKED();
for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) {
next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur);
if (cur->states == 0 && cur->expire <= getuptime()) {
next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur);
pf_remove_src_node(cur);
}
}
}
void
pf_src_tree_remove_state(struct pf_state *s)
{
u_int32_t timeout;
struct pf_sn_item *sni;
while ((sni = SLIST_FIRST(&s->src_nodes)) != NULL) {
SLIST_REMOVE_HEAD(&s->src_nodes, next);
if (s->src.tcp_est)
--sni->sn->conn;
if (--sni->sn->states == 0) {
timeout = s->rule.ptr->timeout[PFTM_SRC_NODE];
if (!timeout)
timeout =
pf_default_rule.timeout[PFTM_SRC_NODE];
sni->sn->expire = getuptime() + timeout;
}
pool_put(&pf_sn_item_pl, sni);
}
}
void
pf_remove_state(struct pf_state *cur)
{
PF_ASSERT_LOCKED();
/* handle load balancing related tasks */
pf_postprocess_addr(cur);
if (cur->src.state == PF_TCPS_PROXY_DST) {
pf_send_tcp(cur->rule.ptr, cur->key[PF_SK_WIRE]->af,
&cur->key[PF_SK_WIRE]->addr[1],
&cur->key[PF_SK_WIRE]->addr[0],
cur->key[PF_SK_WIRE]->port[1],
cur->key[PF_SK_WIRE]->port[0],
cur->src.seqhi, cur->src.seqlo + 1,
TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag,
cur->key[PF_SK_WIRE]->rdomain);
}
if (cur->key[PF_SK_STACK]->proto == IPPROTO_TCP)
pf_set_protostate(cur, PF_PEER_BOTH, TCPS_CLOSED);
RB_REMOVE(pf_state_tree_id, &tree_id, cur);
#if NPFLOW > 0
if (cur->state_flags & PFSTATE_PFLOW)
export_pflow(cur);
#endif /* NPFLOW > 0 */
#if NPFSYNC > 0
pfsync_delete_state(cur);
#endif /* NPFSYNC > 0 */
cur->timeout = PFTM_UNLINKED;
pf_src_tree_remove_state(cur);
pf_detach_state(cur);
}
void
pf_remove_divert_state(struct pf_state_key *sk)
{
struct pf_state_item *si;
PF_ASSERT_UNLOCKED();
PF_LOCK();
PF_STATE_ENTER_WRITE();
TAILQ_FOREACH(si, &sk->states, entry) {
if (sk == si->s->key[PF_SK_STACK] && si->s->rule.ptr &&
(si->s->rule.ptr->divert.type == PF_DIVERT_TO ||
si->s->rule.ptr->divert.type == PF_DIVERT_REPLY)) {
if (si->s->key[PF_SK_STACK]->proto == IPPROTO_TCP &&
si->s->key[PF_SK_WIRE] != si->s->key[PF_SK_STACK]) {
/*
* If the local address is translated, keep
* the state for "tcp.closed" seconds to
* prevent its source port from being reused.
*/
if (si->s->src.state < TCPS_FIN_WAIT_2 ||
si->s->dst.state < TCPS_FIN_WAIT_2) {
pf_set_protostate(si->s, PF_PEER_BOTH,
TCPS_TIME_WAIT);
si->s->timeout = PFTM_TCP_CLOSED;
si->s->expire = getuptime();
}
si->s->state_flags |= PFSTATE_INP_UNLINKED;
} else
pf_remove_state(si->s);
break;
}
}
PF_STATE_EXIT_WRITE();
PF_UNLOCK();
}
void
pf_free_state(struct pf_state *cur)
{
struct pf_rule_item *ri;
PF_ASSERT_LOCKED();
#if NPFSYNC > 0
if (pfsync_state_in_use(cur))
return;
#endif /* NPFSYNC > 0 */
KASSERT(cur->timeout == PFTM_UNLINKED);
if (--cur->rule.ptr->states_cur == 0 &&
cur->rule.ptr->src_nodes == 0)
pf_rm_rule(NULL, cur->rule.ptr);
if (cur->anchor.ptr != NULL)
if (--cur->anchor.ptr->states_cur == 0)
pf_rm_rule(NULL, cur->anchor.ptr);
while ((ri = SLIST_FIRST(&cur->match_rules))) {
SLIST_REMOVE_HEAD(&cur->match_rules, entry);
if (--ri->r->states_cur == 0 &&
ri->r->src_nodes == 0)
pf_rm_rule(NULL, ri->r);
pool_put(&pf_rule_item_pl, ri);
}
pf_normalize_tcp_cleanup(cur);
pfi_kif_unref(cur->kif, PFI_KIF_REF_STATE);
pf_state_list_remove(&pf_state_list, cur);
if (cur->tag)
pf_tag_unref(cur->tag);
pf_state_unref(cur);
pf_status.fcounters[FCNT_STATE_REMOVALS]++;
pf_status.states--;
}
void
pf_purge_expired_states(u_int32_t maxcheck)
{
/*
* this task/thread/context/whatever is the only thing that
* removes states from the pf_state_list, so the cur reference
* it holds between calls is guaranteed to still be in the
* list.
*/
static struct pf_state *cur = NULL;
struct pf_state *head, *tail;
struct pf_state *st;
SLIST_HEAD(pf_state_gcl, pf_state) gcl = SLIST_HEAD_INITIALIZER(gcl);
time_t now;
PF_ASSERT_UNLOCKED();
rw_enter_read(&pf_state_list.pfs_rwl);
mtx_enter(&pf_state_list.pfs_mtx);
head = TAILQ_FIRST(&pf_state_list.pfs_list);
tail = TAILQ_LAST(&pf_state_list.pfs_list, pf_state_queue);
mtx_leave(&pf_state_list.pfs_mtx);
if (head == NULL) {
/* the list is empty */
rw_exit_read(&pf_state_list.pfs_rwl);
return;
}
/* (re)start at the front of the list */
if (cur == NULL)
cur = head;
now = getuptime();
do {
uint8_t stimeout = cur->timeout;
if ((stimeout == PFTM_UNLINKED) ||
(pf_state_expires(cur, stimeout) <= now)) {
st = pf_state_ref(cur);
SLIST_INSERT_HEAD(&gcl, st, gc_list);
}
/* don't iterate past the end of our view of the list */
if (cur == tail) {
cur = NULL;
break;
}
cur = TAILQ_NEXT(cur, entry_list);
} while (maxcheck--);
rw_exit_read(&pf_state_list.pfs_rwl);
if (SLIST_EMPTY(&gcl))
return;
NET_LOCK();
rw_enter_write(&pf_state_list.pfs_rwl);
PF_LOCK();
PF_STATE_ENTER_WRITE();
SLIST_FOREACH(st, &gcl, gc_list) {
if (st->timeout != PFTM_UNLINKED)
pf_remove_state(st);
pf_free_state(st);
}
PF_STATE_EXIT_WRITE();
PF_UNLOCK();
rw_exit_write(&pf_state_list.pfs_rwl);
NET_UNLOCK();
while ((st = SLIST_FIRST(&gcl)) != NULL) {
SLIST_REMOVE_HEAD(&gcl, gc_list);
pf_state_unref(st);
}
}
int
pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw, int wait)
{
if (aw->type != PF_ADDR_TABLE)
return (0);
if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname, wait)) == NULL)
return (1);
return (0);
}
void
pf_tbladdr_remove(struct pf_addr_wrap *aw)
{
if (aw->type != PF_ADDR_TABLE || aw->p.tbl == NULL)
return;
pfr_detach_table(aw->p.tbl);
aw->p.tbl = NULL;
}
void
pf_tbladdr_copyout(struct pf_addr_wrap *aw)
{
struct pfr_ktable *kt = aw->p.tbl;
if (aw->type != PF_ADDR_TABLE || kt == NULL)
return;
if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL)
kt = kt->pfrkt_root;
aw->p.tbl = NULL;
aw->p.tblcnt = (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) ?
kt->pfrkt_cnt : -1;
}
void
pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af)
{
switch (af) {
case AF_INET: {
u_int32_t a = ntohl(addr->addr32[0]);
addlog("%u.%u.%u.%u", (a>>24)&255, (a>>16)&255,
(a>>8)&255, a&255);
if (p) {
p = ntohs(p);
addlog(":%u", p);
}
break;
}
#ifdef INET6
case AF_INET6: {
u_int16_t b;
u_int8_t i, curstart, curend, maxstart, maxend;
curstart = curend = maxstart = maxend = 255;
for (i = 0; i < 8; i++) {
if (!addr->addr16[i]) {
if (curstart == 255)
curstart = i;
curend = i;
} else {
if ((curend - curstart) >
(maxend - maxstart)) {
maxstart = curstart;
maxend = curend;
}
curstart = curend = 255;
}
}
if ((curend - curstart) >
(maxend - maxstart)) {
maxstart = curstart;
maxend = curend;
}
for (i = 0; i < 8; i++) {
if (i >= maxstart && i <= maxend) {
if (i == 0)
addlog(":");
if (i == maxend)
addlog(":");
} else {
b = ntohs(addr->addr16[i]);
addlog("%x", b);
if (i < 7)
addlog(":");
}
}
if (p) {
p = ntohs(p);
addlog("[%u]", p);
}
break;
}
#endif /* INET6 */
}
}
void
pf_print_state(struct pf_state *s)
{
pf_print_state_parts(s, NULL, NULL);
}
void
pf_print_state_parts(struct pf_state *s,
struct pf_state_key *skwp, struct pf_state_key *sksp)
{
struct pf_state_key *skw, *sks;
u_int8_t proto, dir;
/* Do our best to fill these, but they're skipped if NULL */
skw = skwp ? skwp : (s ? s->key[PF_SK_WIRE] : NULL);
sks = sksp ? sksp : (s ? s->key[PF_SK_STACK] : NULL);
proto = skw ? skw->proto : (sks ? sks->proto : 0);
dir = s ? s->direction : 0;
switch (proto) {
case IPPROTO_IPV4:
addlog("IPv4");
break;
case IPPROTO_IPV6:
addlog("IPv6");
break;
case IPPROTO_TCP:
addlog("TCP");
break;
case IPPROTO_UDP:
addlog("UDP");
break;
case IPPROTO_ICMP:
addlog("ICMP");
break;
case IPPROTO_ICMPV6:
addlog("ICMPv6");
break;
default:
addlog("%u", proto);
break;
}
switch (dir) {
case PF_IN:
addlog(" in");
break;
case PF_OUT:
addlog(" out");
break;
}
if (skw) {
addlog(" wire: (%d) ", skw->rdomain);
pf_print_host(&skw->addr[0], skw->port[0], skw->af);
addlog(" ");
pf_print_host(&skw->addr[1], skw->port[1], skw->af);
}
if (sks) {
addlog(" stack: (%d) ", sks->rdomain);
if (sks != skw) {
pf_print_host(&sks->addr[0], sks->port[0], sks->af);
addlog(" ");
pf_print_host(&sks->addr[1], sks->port[1], sks->af);
} else
addlog("-");
}
if (s) {
if (proto == IPPROTO_TCP) {
addlog(" [lo=%u high=%u win=%u modulator=%u",
s->src.seqlo, s->src.seqhi,
s->src.max_win, s->src.seqdiff);
if (s->src.wscale && s->dst.wscale)
addlog(" wscale=%u",
s->src.wscale & PF_WSCALE_MASK);
addlog("]");
addlog(" [lo=%u high=%u win=%u modulator=%u",
s->dst.seqlo, s->dst.seqhi,
s->dst.max_win, s->dst.seqdiff);
if (s->src.wscale && s->dst.wscale)
addlog(" wscale=%u",
s->dst.wscale & PF_WSCALE_MASK);
addlog("]");
}
addlog(" %u:%u", s->src.state, s->dst.state);
if (s->rule.ptr)
addlog(" @%d", s->rule.ptr->nr);
}
}
void
pf_print_flags(u_int8_t f)
{
if (f)
addlog(" ");
if (f & TH_FIN)
addlog("F");
if (f & TH_SYN)
addlog("S");
if (f & TH_RST)
addlog("R");
if (f & TH_PUSH)
addlog("P");
if (f & TH_ACK)
addlog("A");
if (f & TH_URG)
addlog("U");
if (f & TH_ECE)
addlog("E");
if (f & TH_CWR)
addlog("W");
}
#define PF_SET_SKIP_STEPS(i) \
do { \
while (head[i] != cur) { \
head[i]->skip[i].ptr = cur; \
head[i] = TAILQ_NEXT(head[i], entries); \
} \
} while (0)
void
pf_calc_skip_steps(struct pf_rulequeue *rules)
{
struct pf_rule *cur, *prev, *head[PF_SKIP_COUNT];
int i;
cur = TAILQ_FIRST(rules);
prev = cur;
for (i = 0; i < PF_SKIP_COUNT; ++i)
head[i] = cur;
while (cur != NULL) {
if (cur->kif != prev->kif || cur->ifnot != prev->ifnot)
PF_SET_SKIP_STEPS(PF_SKIP_IFP);
if (cur->direction != prev->direction)
PF_SET_SKIP_STEPS(PF_SKIP_DIR);
if (cur->onrdomain != prev->onrdomain ||
cur->ifnot != prev->ifnot)
PF_SET_SKIP_STEPS(PF_SKIP_RDOM);
if (cur->af != prev->af)
PF_SET_SKIP_STEPS(PF_SKIP_AF);
if (cur->proto != prev->proto)
PF_SET_SKIP_STEPS(PF_SKIP_PROTO);
if (cur->src.neg != prev->src.neg ||
pf_addr_wrap_neq(&cur->src.addr, &prev->src.addr))
PF_SET_SKIP_STEPS(PF_SKIP_SRC_ADDR);
if (cur->dst.neg != prev->dst.neg ||
pf_addr_wrap_neq(&cur->dst.addr, &prev->dst.addr))
PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR);
if (cur->src.port[0] != prev->src.port[0] ||
cur->src.port[1] != prev->src.port[1] ||
cur->src.port_op != prev->src.port_op)
PF_SET_SKIP_STEPS(PF_SKIP_SRC_PORT);
if (cur->dst.port[0] != prev->dst.port[0] ||
cur->dst.port[1] != prev->dst.port[1] ||
cur->dst.port_op != prev->dst.port_op)
PF_SET_SKIP_STEPS(PF_SKIP_DST_PORT);
prev = cur;
cur = TAILQ_NEXT(cur, entries);
}
for (i = 0; i < PF_SKIP_COUNT; ++i)
PF_SET_SKIP_STEPS(i);
}
int
pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2)
{
if (aw1->type != aw2->type)
return (1);
switch (aw1->type) {
case PF_ADDR_ADDRMASK:
case PF_ADDR_RANGE:
if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, AF_INET6))
return (1);
if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, AF_INET6))
return (1);
return (0);
case PF_ADDR_DYNIFTL:
return (aw1->p.dyn->pfid_kt != aw2->p.dyn->pfid_kt);
case PF_ADDR_NONE:
case PF_ADDR_NOROUTE:
case PF_ADDR_URPFFAILED:
return (0);
case PF_ADDR_TABLE:
return (aw1->p.tbl != aw2->p.tbl);
case PF_ADDR_RTLABEL:
return (aw1->v.rtlabel != aw2->v.rtlabel);
default:
addlog("invalid address type: %d\n", aw1->type);
return (1);
}
}
/* This algorithm computes 'a + b - c' in ones-complement using a trick to
* emulate at most one ones-complement subtraction. This thereby limits net
* carries/borrows to at most one, eliminating a reduction step and saving one
* each of +, >>, & and ~.
*
* def. x mod y = x - (x//y)*y for integer x,y
* def. sum = x mod 2^16
* def. accumulator = (x >> 16) mod 2^16
*
* The trick works as follows: subtracting exactly one u_int16_t from the
* u_int32_t x incurs at most one underflow, wrapping its upper 16-bits, the
* accumulator, to 2^16 - 1. Adding this to the 16-bit sum preserves the
* ones-complement borrow:
*
* (sum + accumulator) mod 2^16
* = { assume underflow: accumulator := 2^16 - 1 }
* (sum + 2^16 - 1) mod 2^16
* = { mod }
* (sum - 1) mod 2^16
*
* Although this breaks for sum = 0, giving 0xffff, which is ones-complement's
* other zero, not -1, that cannot occur: the 16-bit sum cannot be underflown
* to zero as that requires subtraction of at least 2^16, which exceeds a
* single u_int16_t's range.
*
* We use the following theorem to derive the implementation:
*
* th. (x + (y mod z)) mod z = (x + y) mod z (0)
* proof.
* (x + (y mod z)) mod z
* = { def mod }
* (x + y - (y//z)*z) mod z
* = { (a + b*c) mod c = a mod c }
* (x + y) mod z [end of proof]
*
* ... and thereby obtain:
*
* (sum + accumulator) mod 2^16
* = { def. accumulator, def. sum }
* (x mod 2^16 + (x >> 16) mod 2^16) mod 2^16
* = { (0), twice }
* (x + (x >> 16)) mod 2^16
* = { x mod 2^n = x & (2^n - 1) }
* (x + (x >> 16)) & 0xffff
*
* Note: this serves also as a reduction step for at most one add (as the
* trailing mod 2^16 prevents further reductions by destroying carries).
*/
static __inline void
pf_cksum_fixup(u_int16_t *cksum, u_int16_t was, u_int16_t now,
u_int8_t proto)
{
u_int32_t x;
const int udp = proto == IPPROTO_UDP;
x = *cksum + was - now;
x = (x + (x >> 16)) & 0xffff;
/* optimise: eliminate a branch when not udp */
if (udp && *cksum == 0x0000)
return;
if (udp && x == 0x0000)
x = 0xffff;
*cksum = (u_int16_t)(x);
}
#ifdef INET6
/* pre: coverage(cksum) is superset of coverage(covered_cksum) */
static __inline void
pf_cksum_uncover(u_int16_t *cksum, u_int16_t covered_cksum, u_int8_t proto)
{
pf_cksum_fixup(cksum, ~covered_cksum, 0x0, proto);
}
/* pre: disjoint(coverage(cksum), coverage(uncovered_cksum)) */
static __inline void
pf_cksum_cover(u_int16_t *cksum, u_int16_t uncovered_cksum, u_int8_t proto)
{
pf_cksum_fixup(cksum, 0x0, ~uncovered_cksum, proto);
}
#endif /* INET6 */
/* pre: *a is 16-bit aligned within its packet
*
* This algorithm emulates 16-bit ones-complement sums on a twos-complement
* machine by conserving ones-complement's otherwise discarded carries in the
* upper bits of x. These accumulated carries when added to the lower 16-bits
* over at least zero 'reduction' steps then complete the ones-complement sum.
*
* def. sum = x mod 2^16
* def. accumulator = (x >> 16)
*
* At most two reduction steps
*
* x := sum + accumulator
* = { def sum, def accumulator }
* x := x mod 2^16 + (x >> 16)
* = { x mod 2^n = x & (2^n - 1) }
* x := (x & 0xffff) + (x >> 16)
*
* are necessary to incorporate the accumulated carries (at most one per add)
* i.e. to reduce x < 2^16 from at most 16 carries in the upper 16 bits.
*
* The function is also invariant over the endian of the host. Why?
*
* Define the unary transpose operator ~ on a bitstring in python slice
* notation as lambda m: m[P:] + m[:P] , for some constant pivot P.
*
* th. ~ distributes over ones-complement addition, denoted by +_1, i.e.
*
* ~m +_1 ~n = ~(m +_1 n) (for all bitstrings m,n of equal length)
*
* proof. Regard the bitstrings in m +_1 n as split at P, forming at most two
* 'half-adds'. Under ones-complement addition, each half-add carries to the
* other, so the sum of each half-add is unaffected by their relative
* order. Therefore:
*
* ~m +_1 ~n
* = { half-adds invariant under transposition }
* ~s
* = { substitute }
* ~(m +_1 n) [end of proof]
*
* th. Summing two in-memory ones-complement 16-bit variables m,n on a machine
* with the converse endian does not alter the result.
*
* proof.
* { converse machine endian: load/store transposes, P := 8 }
* ~(~m +_1 ~n)
* = { ~ over +_1 }
* ~~m +_1 ~~n
* = { ~ is an involution }
* m +_1 n [end of proof]
*
*/
#define NEG(x) ((u_int16_t)~(x))
void
pf_cksum_fixup_a(u_int16_t *cksum, const struct pf_addr *a,
const struct pf_addr *an, sa_family_t af, u_int8_t proto)
{
u_int32_t x;
const u_int16_t *n = an->addr16;
const u_int16_t *o = a->addr16;
const int udp = proto == IPPROTO_UDP;
switch (af) {
case AF_INET:
x = *cksum + o[0] + NEG(n[0]) + o[1] + NEG(n[1]);
break;
#ifdef INET6
case AF_INET6:
x = *cksum + o[0] + NEG(n[0]) + o[1] + NEG(n[1]) +\
o[2] + NEG(n[2]) + o[3] + NEG(n[3]) +\
o[4] + NEG(n[4]) + o[5] + NEG(n[5]) +\
o[6] + NEG(n[6]) + o[7] + NEG(n[7]);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
x = (x & 0xffff) + (x >> 16);
x = (x & 0xffff) + (x >> 16);
/* optimise: eliminate a branch when not udp */
if (udp && *cksum == 0x0000)
return;
if (udp && x == 0x0000)
x = 0xffff;
*cksum = (u_int16_t)(x);
}
int
pf_patch_8(struct pf_pdesc *pd, u_int8_t *f, u_int8_t v, bool hi)
{
int rewrite = 0;
if (*f != v) {
u_int16_t old = htons(hi ? (*f << 8) : *f);
u_int16_t new = htons(hi ? ( v << 8) : v);
pf_cksum_fixup(pd->pcksum, old, new, pd->proto);
*f = v;
rewrite = 1;
}
return (rewrite);
}
/* pre: *f is 16-bit aligned within its packet */
int
pf_patch_16(struct pf_pdesc *pd, u_int16_t *f, u_int16_t v)
{
int rewrite = 0;
if (*f != v) {
pf_cksum_fixup(pd->pcksum, *f, v, pd->proto);
*f = v;
rewrite = 1;
}
return (rewrite);
}
int
pf_patch_16_unaligned(struct pf_pdesc *pd, void *f, u_int16_t v, bool hi)
{
int rewrite = 0;
u_int8_t *fb = (u_int8_t*)f;
u_int8_t *vb = (u_int8_t*)&v;
if (hi && ALIGNED_POINTER(f, u_int16_t)) {
return (pf_patch_16(pd, f, v)); /* optimise */
}
rewrite += pf_patch_8(pd, fb++, *vb++, hi);
rewrite += pf_patch_8(pd, fb++, *vb++,!hi);
return (rewrite);
}
/* pre: *f is 16-bit aligned within its packet */
/* pre: pd->proto != IPPROTO_UDP */
int
pf_patch_32(struct pf_pdesc *pd, u_int32_t *f, u_int32_t v)
{
int rewrite = 0;
u_int16_t *pc = pd->pcksum;
u_int8_t proto = pd->proto;
/* optimise: inline udp fixup code is unused; let compiler scrub it */
if (proto == IPPROTO_UDP)
panic("%s: udp", __func__);
/* optimise: skip *f != v guard; true for all use-cases */
pf_cksum_fixup(pc, *f / (1 << 16), v / (1 << 16), proto);
pf_cksum_fixup(pc, *f % (1 << 16), v % (1 << 16), proto);
*f = v;
rewrite = 1;
return (rewrite);
}
int
pf_patch_32_unaligned(struct pf_pdesc *pd, void *f, u_int32_t v, bool hi)
{
int rewrite = 0;
u_int8_t *fb = (u_int8_t*)f;
u_int8_t *vb = (u_int8_t*)&v;
if (hi && ALIGNED_POINTER(f, u_int32_t)) {
return (pf_patch_32(pd, f, v)); /* optimise */
}
rewrite += pf_patch_8(pd, fb++, *vb++, hi);
rewrite += pf_patch_8(pd, fb++, *vb++,!hi);
rewrite += pf_patch_8(pd, fb++, *vb++, hi);
rewrite += pf_patch_8(pd, fb++, *vb++,!hi);
return (rewrite);
}
int
pf_icmp_mapping(struct pf_pdesc *pd, u_int8_t type, int *icmp_dir,
u_int16_t *virtual_id, u_int16_t *virtual_type)
{
/*
* ICMP types marked with PF_OUT are typically responses to
* PF_IN, and will match states in the opposite direction.
* PF_IN ICMP types need to match a state with that type.
*/
*icmp_dir = PF_OUT;
/* Queries (and responses) */
switch (pd->af) {
case AF_INET:
switch (type) {
case ICMP_ECHO:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_ECHOREPLY:
*virtual_type = ICMP_ECHO;
*virtual_id = pd->hdr.icmp.icmp_id;
break;
case ICMP_TSTAMP:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_TSTAMPREPLY:
*virtual_type = ICMP_TSTAMP;
*virtual_id = pd->hdr.icmp.icmp_id;
break;
case ICMP_IREQ:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_IREQREPLY:
*virtual_type = ICMP_IREQ;
*virtual_id = pd->hdr.icmp.icmp_id;
break;
case ICMP_MASKREQ:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_MASKREPLY:
*virtual_type = ICMP_MASKREQ;
*virtual_id = pd->hdr.icmp.icmp_id;
break;
case ICMP_IPV6_WHEREAREYOU:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_IPV6_IAMHERE:
*virtual_type = ICMP_IPV6_WHEREAREYOU;
*virtual_id = 0; /* Nothing sane to match on! */
break;
case ICMP_MOBILE_REGREQUEST:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_MOBILE_REGREPLY:
*virtual_type = ICMP_MOBILE_REGREQUEST;
*virtual_id = 0; /* Nothing sane to match on! */
break;
case ICMP_ROUTERSOLICIT:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP_ROUTERADVERT:
*virtual_type = ICMP_ROUTERSOLICIT;
*virtual_id = 0; /* Nothing sane to match on! */
break;
/* These ICMP types map to other connections */
case ICMP_UNREACH:
case ICMP_SOURCEQUENCH:
case ICMP_REDIRECT:
case ICMP_TIMXCEED:
case ICMP_PARAMPROB:
/* These will not be used, but set them anyway */
*icmp_dir = PF_IN;
*virtual_type = htons(type);
*virtual_id = 0;
return (1); /* These types match to another state */
/*
* All remaining ICMP types get their own states,
* and will only match in one direction.
*/
default:
*icmp_dir = PF_IN;
*virtual_type = type;
*virtual_id = 0;
break;
}
break;
#ifdef INET6
case AF_INET6:
switch (type) {
case ICMP6_ECHO_REQUEST:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP6_ECHO_REPLY:
*virtual_type = ICMP6_ECHO_REQUEST;
*virtual_id = pd->hdr.icmp6.icmp6_id;
break;
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT: {
struct mld_hdr *mld = &pd->hdr.mld;
u_int32_t h;
/*
* Listener Report can be sent by clients
* without an associated Listener Query.
* In addition to that, when Report is sent as a
* reply to a Query its source and destination
* address are different.
*/
*icmp_dir = PF_IN;
*virtual_type = MLD_LISTENER_QUERY;
/* generate fake id for these messages */
h = mld->mld_addr.s6_addr32[0] ^
mld->mld_addr.s6_addr32[1] ^
mld->mld_addr.s6_addr32[2] ^
mld->mld_addr.s6_addr32[3];
*virtual_id = (h >> 16) ^ (h & 0xffff);
break;
}
/*
* ICMP6_FQDN and ICMP6_NI query/reply are the same type as
* ICMP6_WRU
*/
case ICMP6_WRUREQUEST:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ICMP6_WRUREPLY:
*virtual_type = ICMP6_WRUREQUEST;
*virtual_id = 0; /* Nothing sane to match on! */
break;
case MLD_MTRACE:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case MLD_MTRACE_RESP:
*virtual_type = MLD_MTRACE;
*virtual_id = 0; /* Nothing sane to match on! */
break;
case ND_NEIGHBOR_SOLICIT:
*icmp_dir = PF_IN;
/* FALLTHROUGH */
case ND_NEIGHBOR_ADVERT: {
struct nd_neighbor_solicit *nd = &pd->hdr.nd_ns;
u_int32_t h;
*virtual_type = ND_NEIGHBOR_SOLICIT;
/* generate fake id for these messages */
h = nd->nd_ns_target.s6_addr32[0] ^
nd->nd_ns_target.s6_addr32[1] ^
nd->nd_ns_target.s6_addr32[2] ^
nd->nd_ns_target.s6_addr32[3];
*virtual_id = (h >> 16) ^ (h & 0xffff);
break;
}
/*
* These ICMP types map to other connections.
* ND_REDIRECT can't be in this list because the triggering
* packet header is optional.
*/
case ICMP6_DST_UNREACH:
case ICMP6_PACKET_TOO_BIG:
case ICMP6_TIME_EXCEEDED:
case ICMP6_PARAM_PROB:
/* These will not be used, but set them anyway */
*icmp_dir = PF_IN;
*virtual_type = htons(type);
*virtual_id = 0;
return (1); /* These types match to another state */
/*
* All remaining ICMP6 types get their own states,
* and will only match in one direction.
*/
default:
*icmp_dir = PF_IN;
*virtual_type = type;
*virtual_id = 0;
break;
}
break;
#endif /* INET6 */
}
*virtual_type = htons(*virtual_type);
return (0); /* These types match to their own state */
}
void
pf_translate_icmp(struct pf_pdesc *pd, struct pf_addr *qa, u_int16_t *qp,
struct pf_addr *oa, struct pf_addr *na, u_int16_t np)
{
/* note: doesn't trouble to fixup quoted checksums, if any */
/* change quoted protocol port */
if (qp != NULL)
pf_patch_16(pd, qp, np);
/* change quoted ip address */
pf_cksum_fixup_a(pd->pcksum, qa, na, pd->af, pd->proto);
pf_addrcpy(qa, na, pd->af);
/* change network-header's ip address */
if (oa)
pf_translate_a(pd, oa, na);
}
/* pre: *a is 16-bit aligned within its packet */
/* *a is a network header src/dst address */
int
pf_translate_a(struct pf_pdesc *pd, struct pf_addr *a, struct pf_addr *an)
{
int rewrite = 0;
/* warning: !PF_ANEQ != PF_AEQ */
if (!PF_ANEQ(a, an, pd->af))
return (0);
/* fixup transport pseudo-header, if any */
switch (pd->proto) {
case IPPROTO_TCP: /* FALLTHROUGH */
case IPPROTO_UDP: /* FALLTHROUGH */
case IPPROTO_ICMPV6:
pf_cksum_fixup_a(pd->pcksum, a, an, pd->af, pd->proto);
break;
default:
break; /* assume no pseudo-header */
}
pf_addrcpy(a, an, pd->af);
rewrite = 1;
return (rewrite);
}
#ifdef INET6
/* pf_translate_af() may change pd->m, adjust local copies after calling */
int
pf_translate_af(struct pf_pdesc *pd)
{
static const struct pf_addr zero;
struct ip *ip4;
struct ip6_hdr *ip6;
int copyback = 0;
u_int hlen, ohlen, dlen;
u_int16_t *pc;
u_int8_t af_proto, naf_proto;
hlen = (pd->naf == AF_INET) ? sizeof(*ip4) : sizeof(*ip6);
ohlen = pd->off;
dlen = pd->tot_len - pd->off;
pc = pd->pcksum;
af_proto = naf_proto = pd->proto;
if (naf_proto == IPPROTO_ICMP)
af_proto = IPPROTO_ICMPV6;
if (naf_proto == IPPROTO_ICMPV6)
af_proto = IPPROTO_ICMP;
/* uncover stale pseudo-header */
switch (af_proto) {
case IPPROTO_ICMPV6:
/* optimise: unchanged for TCP/UDP */
pf_cksum_fixup(pc, htons(af_proto), 0x0, af_proto);
pf_cksum_fixup(pc, htons(dlen), 0x0, af_proto);
/* FALLTHROUGH */
case IPPROTO_UDP: /* FALLTHROUGH */
case IPPROTO_TCP:
pf_cksum_fixup_a(pc, pd->src, &zero, pd->af, af_proto);
pf_cksum_fixup_a(pc, pd->dst, &zero, pd->af, af_proto);
copyback = 1;
break;
default:
break; /* assume no pseudo-header */
}
/* replace the network header */
m_adj(pd->m, pd->off);
pd->src = NULL;
pd->dst = NULL;
if ((M_PREPEND(pd->m, hlen, M_DONTWAIT)) == NULL) {
pd->m = NULL;
return (-1);
}
pd->off = hlen;
pd->tot_len += hlen - ohlen;
switch (pd->naf) {
case AF_INET:
ip4 = mtod(pd->m, struct ip *);
memset(ip4, 0, hlen);
ip4->ip_v = IPVERSION;
ip4->ip_hl = hlen >> 2;
ip4->ip_tos = pd->tos;
ip4->ip_len = htons(hlen + dlen);
ip4->ip_id = htons(ip_randomid());
ip4->ip_off = htons(IP_DF);
ip4->ip_ttl = pd->ttl;
ip4->ip_p = pd->proto;
ip4->ip_src = pd->nsaddr.v4;
ip4->ip_dst = pd->ndaddr.v4;
break;
case AF_INET6:
ip6 = mtod(pd->m, struct ip6_hdr *);
memset(ip6, 0, hlen);
ip6->ip6_vfc = IPV6_VERSION;
ip6->ip6_flow |= htonl((u_int32_t)pd->tos << 20);
ip6->ip6_plen = htons(dlen);
ip6->ip6_nxt = pd->proto;
if (!pd->ttl || pd->ttl > IPV6_DEFHLIM)
ip6->ip6_hlim = IPV6_DEFHLIM;
else
ip6->ip6_hlim = pd->ttl;
ip6->ip6_src = pd->nsaddr.v6;
ip6->ip6_dst = pd->ndaddr.v6;
break;
default:
unhandled_af(pd->naf);
}
/* UDP over IPv6 must be checksummed per rfc2460 p27 */
if (naf_proto == IPPROTO_UDP && *pc == 0x0000 &&
pd->naf == AF_INET6) {
pd->m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
}
/* cover fresh pseudo-header */
switch (naf_proto) {
case IPPROTO_ICMPV6:
/* optimise: unchanged for TCP/UDP */
pf_cksum_fixup(pc, 0x0, htons(naf_proto), naf_proto);
pf_cksum_fixup(pc, 0x0, htons(dlen), naf_proto);
/* FALLTHROUGH */
case IPPROTO_UDP: /* FALLTHROUGH */
case IPPROTO_TCP:
pf_cksum_fixup_a(pc, &zero, &pd->nsaddr, pd->naf, naf_proto);
pf_cksum_fixup_a(pc, &zero, &pd->ndaddr, pd->naf, naf_proto);
copyback = 1;
break;
default:
break; /* assume no pseudo-header */
}
/* flush pd->pcksum */
if (copyback)
m_copyback(pd->m, pd->off, pd->hdrlen, &pd->hdr, M_NOWAIT);
return (0);
}
int
pf_change_icmp_af(struct mbuf *m, int ipoff2, struct pf_pdesc *pd,
struct pf_pdesc *pd2, struct pf_addr *src, struct pf_addr *dst,
sa_family_t af, sa_family_t naf)
{
struct mbuf *n = NULL;
struct ip *ip4;
struct ip6_hdr *ip6;
u_int hlen, ohlen, dlen;
int d;
if (af == naf || (af != AF_INET && af != AF_INET6) ||
(naf != AF_INET && naf != AF_INET6))
return (-1);
/* split the mbuf chain on the quoted ip/ip6 header boundary */
if ((n = m_split(m, ipoff2, M_DONTWAIT)) == NULL)
return (-1);
/* new quoted header */
hlen = naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6);
/* old quoted header */
ohlen = pd2->off - ipoff2;
/* trim old quoted header */
pf_cksum_uncover(pd->pcksum, in_cksum(n, ohlen), pd->proto);
m_adj(n, ohlen);
/* prepend a new, translated, quoted header */
if ((M_PREPEND(n, hlen, M_DONTWAIT)) == NULL)
return (-1);
switch (naf) {
case AF_INET:
ip4 = mtod(n, struct ip *);
memset(ip4, 0, sizeof(*ip4));
ip4->ip_v = IPVERSION;
ip4->ip_hl = sizeof(*ip4) >> 2;
ip4->ip_len = htons(sizeof(*ip4) + pd2->tot_len - ohlen);
ip4->ip_id = htons(ip_randomid());
ip4->ip_off = htons(IP_DF);
ip4->ip_ttl = pd2->ttl;
if (pd2->proto == IPPROTO_ICMPV6)
ip4->ip_p = IPPROTO_ICMP;
else
ip4->ip_p = pd2->proto;
ip4->ip_src = src->v4;
ip4->ip_dst = dst->v4;
ip4->ip_sum = in_cksum(n, ip4->ip_hl << 2);
break;
case AF_INET6:
ip6 = mtod(n, struct ip6_hdr *);
memset(ip6, 0, sizeof(*ip6));
ip6->ip6_vfc = IPV6_VERSION;
ip6->ip6_plen = htons(pd2->tot_len - ohlen);
if (pd2->proto == IPPROTO_ICMP)
ip6->ip6_nxt = IPPROTO_ICMPV6;
else
ip6->ip6_nxt = pd2->proto;
if (!pd2->ttl || pd2->ttl > IPV6_DEFHLIM)
ip6->ip6_hlim = IPV6_DEFHLIM;
else
ip6->ip6_hlim = pd2->ttl;
ip6->ip6_src = src->v6;
ip6->ip6_dst = dst->v6;
break;
}
/* cover new quoted header */
/* optimise: any new AF_INET header of ours sums to zero */
if (naf != AF_INET) {
pf_cksum_cover(pd->pcksum, in_cksum(n, hlen), pd->proto);
}
/* reattach modified quoted packet to outer header */
{
int nlen = n->m_pkthdr.len;
m_cat(m, n);
m->m_pkthdr.len += nlen;
}
/* account for altered length */
d = hlen - ohlen;
if (pd->proto == IPPROTO_ICMPV6) {
/* fixup pseudo-header */
dlen = pd->tot_len - pd->off;
pf_cksum_fixup(pd->pcksum,
htons(dlen), htons(dlen + d), pd->proto);
}
pd->tot_len += d;
pd2->tot_len += d;
pd2->off += d;
/* note: not bothering to update network headers as
these due for rewrite by pf_translate_af() */
return (0);
}
#define PTR_IP(field) (offsetof(struct ip, field))
#define PTR_IP6(field) (offsetof(struct ip6_hdr, field))
int
pf_translate_icmp_af(struct pf_pdesc *pd, int af, void *arg)
{
struct icmp *icmp4;
struct icmp6_hdr *icmp6;
u_int32_t mtu;
int32_t ptr = -1;
u_int8_t type;
u_int8_t code;
switch (af) {
case AF_INET:
icmp6 = arg;
type = icmp6->icmp6_type;
code = icmp6->icmp6_code;
mtu = ntohl(icmp6->icmp6_mtu);
switch (type) {
case ICMP6_ECHO_REQUEST:
type = ICMP_ECHO;
break;
case ICMP6_ECHO_REPLY:
type = ICMP_ECHOREPLY;
break;
case ICMP6_DST_UNREACH:
type = ICMP_UNREACH;
switch (code) {
case ICMP6_DST_UNREACH_NOROUTE:
case ICMP6_DST_UNREACH_BEYONDSCOPE:
case ICMP6_DST_UNREACH_ADDR:
code = ICMP_UNREACH_HOST;
break;
case ICMP6_DST_UNREACH_ADMIN:
code = ICMP_UNREACH_HOST_PROHIB;
break;
case ICMP6_DST_UNREACH_NOPORT:
code = ICMP_UNREACH_PORT;
break;
default:
return (-1);
}
break;
case ICMP6_PACKET_TOO_BIG:
type = ICMP_UNREACH;
code = ICMP_UNREACH_NEEDFRAG;
mtu -= 20;
break;
case ICMP6_TIME_EXCEEDED:
type = ICMP_TIMXCEED;
break;
case ICMP6_PARAM_PROB:
switch (code) {
case ICMP6_PARAMPROB_HEADER:
type = ICMP_PARAMPROB;
code = ICMP_PARAMPROB_ERRATPTR;
ptr = ntohl(icmp6->icmp6_pptr);
if (ptr == PTR_IP6(ip6_vfc))
; /* preserve */
else if (ptr == PTR_IP6(ip6_vfc) + 1)
ptr = PTR_IP(ip_tos);
else if (ptr == PTR_IP6(ip6_plen) ||
ptr == PTR_IP6(ip6_plen) + 1)
ptr = PTR_IP(ip_len);
else if (ptr == PTR_IP6(ip6_nxt))
ptr = PTR_IP(ip_p);
else if (ptr == PTR_IP6(ip6_hlim))
ptr = PTR_IP(ip_ttl);
else if (ptr >= PTR_IP6(ip6_src) &&
ptr < PTR_IP6(ip6_dst))
ptr = PTR_IP(ip_src);
else if (ptr >= PTR_IP6(ip6_dst) &&
ptr < sizeof(struct ip6_hdr))
ptr = PTR_IP(ip_dst);
else {
return (-1);
}
break;
case ICMP6_PARAMPROB_NEXTHEADER:
type = ICMP_UNREACH;
code = ICMP_UNREACH_PROTOCOL;
break;
default:
return (-1);
}
break;
default:
return (-1);
}
pf_patch_8(pd, &icmp6->icmp6_type, type, PF_HI);
pf_patch_8(pd, &icmp6->icmp6_code, code, PF_LO);
/* aligns well with a icmpv4 nextmtu */
pf_patch_32(pd, &icmp6->icmp6_mtu, htonl(mtu));
/* icmpv4 pptr is a one most significant byte */
if (ptr >= 0)
pf_patch_32(pd, &icmp6->icmp6_pptr, htonl(ptr << 24));
break;
case AF_INET6:
icmp4 = arg;
type = icmp4->icmp_type;
code = icmp4->icmp_code;
mtu = ntohs(icmp4->icmp_nextmtu);
switch (type) {
case ICMP_ECHO:
type = ICMP6_ECHO_REQUEST;
break;
case ICMP_ECHOREPLY:
type = ICMP6_ECHO_REPLY;
break;
case ICMP_UNREACH:
type = ICMP6_DST_UNREACH;
switch (code) {
case ICMP_UNREACH_NET:
case ICMP_UNREACH_HOST:
case ICMP_UNREACH_NET_UNKNOWN:
case ICMP_UNREACH_HOST_UNKNOWN:
case ICMP_UNREACH_ISOLATED:
case ICMP_UNREACH_TOSNET:
case ICMP_UNREACH_TOSHOST:
code = ICMP6_DST_UNREACH_NOROUTE;
break;
case ICMP_UNREACH_PORT:
code = ICMP6_DST_UNREACH_NOPORT;
break;
case ICMP_UNREACH_NET_PROHIB:
case ICMP_UNREACH_HOST_PROHIB:
case ICMP_UNREACH_FILTER_PROHIB:
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
code = ICMP6_DST_UNREACH_ADMIN;
break;
case ICMP_UNREACH_PROTOCOL:
type = ICMP6_PARAM_PROB;
code = ICMP6_PARAMPROB_NEXTHEADER;
ptr = offsetof(struct ip6_hdr, ip6_nxt);
break;
case ICMP_UNREACH_NEEDFRAG:
type = ICMP6_PACKET_TOO_BIG;
code = 0;
mtu += 20;
break;
default:
return (-1);
}
break;
case ICMP_TIMXCEED:
type = ICMP6_TIME_EXCEEDED;
break;
case ICMP_PARAMPROB:
type = ICMP6_PARAM_PROB;
switch (code) {
case ICMP_PARAMPROB_ERRATPTR:
code = ICMP6_PARAMPROB_HEADER;
break;
case ICMP_PARAMPROB_LENGTH:
code = ICMP6_PARAMPROB_HEADER;
break;
default:
return (-1);
}
ptr = icmp4->icmp_pptr;
if (ptr == 0 || ptr == PTR_IP(ip_tos))
; /* preserve */
else if (ptr == PTR_IP(ip_len) ||
ptr == PTR_IP(ip_len) + 1)
ptr = PTR_IP6(ip6_plen);
else if (ptr == PTR_IP(ip_ttl))
ptr = PTR_IP6(ip6_hlim);
else if (ptr == PTR_IP(ip_p))
ptr = PTR_IP6(ip6_nxt);
else if (ptr >= PTR_IP(ip_src) &&
ptr < PTR_IP(ip_dst))
ptr = PTR_IP6(ip6_src);
else if (ptr >= PTR_IP(ip_dst) &&
ptr < sizeof(struct ip))
ptr = PTR_IP6(ip6_dst);
else {
return (-1);
}
break;
default:
return (-1);
}
pf_patch_8(pd, &icmp4->icmp_type, type, PF_HI);
pf_patch_8(pd, &icmp4->icmp_code, code, PF_LO);
pf_patch_16(pd, &icmp4->icmp_nextmtu, htons(mtu));
if (ptr >= 0)
pf_patch_32(pd, &icmp4->icmp_void, htonl(ptr));
break;
}
return (0);
}
#endif /* INET6 */
/*
* Need to modulate the sequence numbers in the TCP SACK option
* (credits to Krzysztof Pfaff for report and patch)
*/
int
pf_modulate_sack(struct pf_pdesc *pd, struct pf_state_peer *dst)
{
struct sackblk sack;
int copyback = 0, i;
int olen, optsoff;
u_int8_t opts[MAX_TCPOPTLEN], *opt, *eoh;
olen = (pd->hdr.tcp.th_off << 2) - sizeof(struct tcphdr);
optsoff = pd->off + sizeof(struct tcphdr);
#define TCPOLEN_MINSACK (TCPOLEN_SACK + 2)
if (olen < TCPOLEN_MINSACK ||
!pf_pull_hdr(pd->m, optsoff, opts, olen, NULL, NULL, pd->af))
return (0);
eoh = opts + olen;
opt = opts;
while ((opt = pf_find_tcpopt(opt, opts, olen,
TCPOPT_SACK, TCPOLEN_MINSACK)) != NULL)
{
size_t safelen = MIN(opt[1], (eoh - opt));
for (i = 2; i + TCPOLEN_SACK <= safelen; i += TCPOLEN_SACK) {
size_t startoff = (opt + i) - opts;
memcpy(&sack, &opt[i], sizeof(sack));
pf_patch_32_unaligned(pd, &sack.start,
htonl(ntohl(sack.start) - dst->seqdiff),
PF_ALGNMNT(startoff));
pf_patch_32_unaligned(pd, &sack.end,
htonl(ntohl(sack.end) - dst->seqdiff),
PF_ALGNMNT(startoff + sizeof(sack.start)));
memcpy(&opt[i], &sack, sizeof(sack));
}
copyback = 1;
opt += opt[1];
}
if (copyback)
m_copyback(pd->m, optsoff, olen, opts, M_NOWAIT);
return (copyback);
}
struct mbuf *
pf_build_tcp(const struct pf_rule *r, sa_family_t af,
const struct pf_addr *saddr, const struct pf_addr *daddr,
u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack,
u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag,
u_int16_t rtag, u_int sack, u_int rdom)
{
struct mbuf *m;
int len, tlen;
struct ip *h;
#ifdef INET6
struct ip6_hdr *h6;
#endif /* INET6 */
struct tcphdr *th;
char *opt;
/* maximum segment size tcp option */
tlen = sizeof(struct tcphdr);
if (mss)
tlen += 4;
if (sack)
tlen += 2;
switch (af) {
case AF_INET:
len = sizeof(struct ip) + tlen;
break;
#ifdef INET6
case AF_INET6:
len = sizeof(struct ip6_hdr) + tlen;
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
/* create outgoing mbuf */
m = m_gethdr(M_DONTWAIT, MT_HEADER);
if (m == NULL)
return (NULL);
if (tag)
m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
m->m_pkthdr.pf.tag = rtag;
m->m_pkthdr.ph_rtableid = rdom;
if (r && (r->scrub_flags & PFSTATE_SETPRIO))
m->m_pkthdr.pf.prio = r->set_prio[0];
if (r && r->qid)
m->m_pkthdr.pf.qid = r->qid;
m->m_data += max_linkhdr;
m->m_pkthdr.len = m->m_len = len;
m->m_pkthdr.ph_ifidx = 0;
m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
memset(m->m_data, 0, len);
switch (af) {
case AF_INET:
h = mtod(m, struct ip *);
h->ip_p = IPPROTO_TCP;
h->ip_len = htons(tlen);
h->ip_v = 4;
h->ip_hl = sizeof(*h) >> 2;
h->ip_tos = IPTOS_LOWDELAY;
h->ip_len = htons(len);
h->ip_off = htons(ip_mtudisc ? IP_DF : 0);
h->ip_ttl = ttl ? ttl : ip_defttl;
h->ip_sum = 0;
h->ip_src.s_addr = saddr->v4.s_addr;
h->ip_dst.s_addr = daddr->v4.s_addr;
th = (struct tcphdr *)((caddr_t)h + sizeof(struct ip));
break;
#ifdef INET6
case AF_INET6:
h6 = mtod(m, struct ip6_hdr *);
h6->ip6_nxt = IPPROTO_TCP;
h6->ip6_plen = htons(tlen);
h6->ip6_vfc |= IPV6_VERSION;
h6->ip6_hlim = IPV6_DEFHLIM;
memcpy(&h6->ip6_src, &saddr->v6, sizeof(struct in6_addr));
memcpy(&h6->ip6_dst, &daddr->v6, sizeof(struct in6_addr));
th = (struct tcphdr *)((caddr_t)h6 + sizeof(struct ip6_hdr));
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
/* TCP header */
th->th_sport = sport;
th->th_dport = dport;
th->th_seq = htonl(seq);
th->th_ack = htonl(ack);
th->th_off = tlen >> 2;
th->th_flags = flags;
th->th_win = htons(win);
opt = (char *)(th + 1);
if (mss) {
opt[0] = TCPOPT_MAXSEG;
opt[1] = 4;
mss = htons(mss);
memcpy((opt + 2), &mss, 2);
opt += 4;
}
if (sack) {
opt[0] = TCPOPT_SACK_PERMITTED;
opt[1] = 2;
opt += 2;
}
return (m);
}
void
pf_send_tcp(const struct pf_rule *r, sa_family_t af,
const struct pf_addr *saddr, const struct pf_addr *daddr,
u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack,
u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag,
u_int16_t rtag, u_int rdom)
{
struct mbuf *m;
if ((m = pf_build_tcp(r, af, saddr, daddr, sport, dport, seq, ack,
flags, win, mss, ttl, tag, rtag, 0, rdom)) == NULL)
return;
switch (af) {
case AF_INET:
ip_send(m);
break;
#ifdef INET6
case AF_INET6:
ip6_send(m);
break;
#endif /* INET6 */
}
}
static void
pf_send_challenge_ack(struct pf_pdesc *pd, struct pf_state *s,
struct pf_state_peer *src, struct pf_state_peer *dst)
{
/*
* We are sending challenge ACK as a response to SYN packet, which
* matches existing state (modulo TCP window check). Therefore packet
* must be sent on behalf of destination.
*
* We expect sender to remain either silent, or send RST packet
* so both, firewall and remote peer, can purge dead state from
* memory.
*/
pf_send_tcp(s->rule.ptr, pd->af, pd->dst, pd->src,
pd->hdr.tcp.th_dport, pd->hdr.tcp.th_sport, dst->seqlo,
src->seqlo, TH_ACK, 0, 0, s->rule.ptr->return_ttl, 1, 0,
pd->rdomain);
}
void
pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, int param,
sa_family_t af, struct pf_rule *r, u_int rdomain)
{
struct mbuf *m0;
if ((m0 = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL)
return;
m0->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
m0->m_pkthdr.ph_rtableid = rdomain;
if (r && (r->scrub_flags & PFSTATE_SETPRIO))
m0->m_pkthdr.pf.prio = r->set_prio[0];
if (r && r->qid)
m0->m_pkthdr.pf.qid = r->qid;
switch (af) {
case AF_INET:
icmp_error(m0, type, code, 0, param);
break;
#ifdef INET6
case AF_INET6:
icmp6_error(m0, type, code, param);
break;
#endif /* INET6 */
}
}
/*
* Return ((n = 0) == (a = b [with mask m]))
* Note: n != 0 => returns (a != b [with mask m])
*/
int
pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m,
struct pf_addr *b, sa_family_t af)
{
switch (af) {
case AF_INET:
if ((a->addr32[0] & m->addr32[0]) ==
(b->addr32[0] & m->addr32[0]))
return (n == 0);
break;
#ifdef INET6
case AF_INET6:
if (((a->addr32[0] & m->addr32[0]) ==
(b->addr32[0] & m->addr32[0])) &&
((a->addr32[1] & m->addr32[1]) ==
(b->addr32[1] & m->addr32[1])) &&
((a->addr32[2] & m->addr32[2]) ==
(b->addr32[2] & m->addr32[2])) &&
((a->addr32[3] & m->addr32[3]) ==
(b->addr32[3] & m->addr32[3])))
return (n == 0);
break;
#endif /* INET6 */
}
return (n != 0);
}
/*
* Return 1 if b <= a <= e, otherwise return 0.
*/
int
pf_match_addr_range(struct pf_addr *b, struct pf_addr *e,
struct pf_addr *a, sa_family_t af)
{
switch (af) {
case AF_INET:
if ((ntohl(a->addr32[0]) < ntohl(b->addr32[0])) ||
(ntohl(a->addr32[0]) > ntohl(e->addr32[0])))
return (0);
break;
#ifdef INET6
case AF_INET6: {
int i;
/* check a >= b */
for (i = 0; i < 4; ++i)
if (ntohl(a->addr32[i]) > ntohl(b->addr32[i]))
break;
else if (ntohl(a->addr32[i]) < ntohl(b->addr32[i]))
return (0);
/* check a <= e */
for (i = 0; i < 4; ++i)
if (ntohl(a->addr32[i]) < ntohl(e->addr32[i]))
break;
else if (ntohl(a->addr32[i]) > ntohl(e->addr32[i]))
return (0);
break;
}
#endif /* INET6 */
}
return (1);
}
int
pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p)
{
switch (op) {
case PF_OP_IRG:
return ((p > a1) && (p < a2));
case PF_OP_XRG:
return ((p < a1) || (p > a2));
case PF_OP_RRG:
return ((p >= a1) && (p <= a2));
case PF_OP_EQ:
return (p == a1);
case PF_OP_NE:
return (p != a1);
case PF_OP_LT:
return (p < a1);
case PF_OP_LE:
return (p <= a1);
case PF_OP_GT:
return (p > a1);
case PF_OP_GE:
return (p >= a1);
}
return (0); /* never reached */
}
int
pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p)
{
return (pf_match(op, ntohs(a1), ntohs(a2), ntohs(p)));
}
int
pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u)
{
if (u == -1 && op != PF_OP_EQ && op != PF_OP_NE)
return (0);
return (pf_match(op, a1, a2, u));
}
int
pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g)
{
if (g == -1 && op != PF_OP_EQ && op != PF_OP_NE)
return (0);
return (pf_match(op, a1, a2, g));
}
int
pf_match_tag(struct mbuf *m, struct pf_rule *r, int *tag)
{
if (*tag == -1)
*tag = m->m_pkthdr.pf.tag;
return ((!r->match_tag_not && r->match_tag == *tag) ||
(r->match_tag_not && r->match_tag != *tag));
}
int
pf_match_rcvif(struct mbuf *m, struct pf_rule *r)
{
struct ifnet *ifp;
#if NCARP > 0
struct ifnet *ifp0;
#endif
struct pfi_kif *kif;
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL)
return (0);
#if NCARP > 0
if (ifp->if_type == IFT_CARP &&
(ifp0 = if_get(ifp->if_carpdevidx)) != NULL) {
kif = (struct pfi_kif *)ifp0->if_pf_kif;
if_put(ifp0);
} else
#endif /* NCARP */
kif = (struct pfi_kif *)ifp->if_pf_kif;
if_put(ifp);
if (kif == NULL) {
DPFPRINTF(LOG_ERR,
"%s: kif == NULL, @%d via %s", __func__,
r->nr, r->rcv_ifname);
return (0);
}
return (pfi_kif_match(r->rcv_kif, kif));
}
void
pf_tag_packet(struct mbuf *m, int tag, int rtableid)
{
if (tag > 0)
m->m_pkthdr.pf.tag = tag;
if (rtableid >= 0)
m->m_pkthdr.ph_rtableid = (u_int)rtableid;
}
enum pf_test_status
pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_rule *r)
{
int rv;
if (ctx->depth >= PF_ANCHOR_STACK_MAX) {
log(LOG_ERR, "pf_step_into_anchor: stack overflow\n");
return (PF_TEST_FAIL);
}
ctx->depth++;
if (r->anchor_wildcard) {
struct pf_anchor *child;
rv = PF_TEST_OK;
RB_FOREACH(child, pf_anchor_node, &r->anchor->children) {
rv = pf_match_rule(ctx, &child->ruleset);
if ((rv == PF_TEST_QUICK) || (rv == PF_TEST_FAIL)) {
/*
* we either hit a rule with quick action
* (more likely), or hit some runtime
* error (e.g. pool_get() failure).
*/
break;
}
}
} else {
rv = pf_match_rule(ctx, &r->anchor->ruleset);
/*
* Unless errors occurred, stop iff any rule matched
* within quick anchors.
*/
if (rv != PF_TEST_FAIL && r->quick == PF_TEST_QUICK &&
*ctx->am == r)
rv = PF_TEST_QUICK;
}
ctx->depth--;
return (rv);
}
void
pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr,
struct pf_addr *rmask, struct pf_addr *saddr, sa_family_t af)
{
switch (af) {
case AF_INET:
naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) |
((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]);
break;
#ifdef INET6
case AF_INET6:
naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) |
((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]);
naddr->addr32[1] = (raddr->addr32[1] & rmask->addr32[1]) |
((rmask->addr32[1] ^ 0xffffffff ) & saddr->addr32[1]);
naddr->addr32[2] = (raddr->addr32[2] & rmask->addr32[2]) |
((rmask->addr32[2] ^ 0xffffffff ) & saddr->addr32[2]);
naddr->addr32[3] = (raddr->addr32[3] & rmask->addr32[3]) |
((rmask->addr32[3] ^ 0xffffffff ) & saddr->addr32[3]);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
}
void
pf_addr_inc(struct pf_addr *addr, sa_family_t af)
{
switch (af) {
case AF_INET:
addr->addr32[0] = htonl(ntohl(addr->addr32[0]) + 1);
break;
#ifdef INET6
case AF_INET6:
if (addr->addr32[3] == 0xffffffff) {
addr->addr32[3] = 0;
if (addr->addr32[2] == 0xffffffff) {
addr->addr32[2] = 0;
if (addr->addr32[1] == 0xffffffff) {
addr->addr32[1] = 0;
addr->addr32[0] =
htonl(ntohl(addr->addr32[0]) + 1);
} else
addr->addr32[1] =
htonl(ntohl(addr->addr32[1]) + 1);
} else
addr->addr32[2] =
htonl(ntohl(addr->addr32[2]) + 1);
} else
addr->addr32[3] =
htonl(ntohl(addr->addr32[3]) + 1);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
}
int
pf_socket_lookup(struct pf_pdesc *pd)
{
struct pf_addr *saddr, *daddr;
u_int16_t sport, dport;
struct inpcbtable *tb;
struct inpcb *inp;
pd->lookup.uid = -1;
pd->lookup.gid = -1;
pd->lookup.pid = NO_PID;
switch (pd->virtual_proto) {
case IPPROTO_TCP:
sport = pd->hdr.tcp.th_sport;
dport = pd->hdr.tcp.th_dport;
PF_ASSERT_LOCKED();
NET_ASSERT_LOCKED();
tb = &tcbtable;
break;
case IPPROTO_UDP:
sport = pd->hdr.udp.uh_sport;
dport = pd->hdr.udp.uh_dport;
PF_ASSERT_LOCKED();
NET_ASSERT_LOCKED();
tb = &udbtable;
break;
default:
return (-1);
}
if (pd->dir == PF_IN) {
saddr = pd->src;
daddr = pd->dst;
} else {
u_int16_t p;
p = sport;
sport = dport;
dport = p;
saddr = pd->dst;
daddr = pd->src;
}
switch (pd->af) {
case AF_INET:
/*
* Fails when rtable is changed while evaluating the ruleset
* The socket looked up will not match the one hit in the end.
*/
inp = in_pcblookup(tb, saddr->v4, sport, daddr->v4, dport,
pd->rdomain);
if (inp == NULL) {
inp = in_pcblookup_listen(tb, daddr->v4, dport,
NULL, pd->rdomain);
if (inp == NULL)
return (-1);
}
break;
#ifdef INET6
case AF_INET6:
inp = in6_pcblookup(tb, &saddr->v6, sport, &daddr->v6,
dport, pd->rdomain);
if (inp == NULL) {
inp = in6_pcblookup_listen(tb, &daddr->v6, dport,
NULL, pd->rdomain);
if (inp == NULL)
return (-1);
}
break;
#endif /* INET6 */
default:
unhandled_af(pd->af);
}
pd->lookup.uid = inp->inp_socket->so_euid;
pd->lookup.gid = inp->inp_socket->so_egid;
pd->lookup.pid = inp->inp_socket->so_cpid;
in_pcbunref(inp);
return (1);
}
/* post: r => (r[0] == type /\ r[1] >= min_typelen >= 2 "validity"
* /\ (eoh - r) >= min_typelen >= 2 "safety" )
*
* warning: r + r[1] may exceed opts bounds for r[1] > min_typelen
*/
u_int8_t*
pf_find_tcpopt(u_int8_t *opt, u_int8_t *opts, size_t hlen, u_int8_t type,
u_int8_t min_typelen)
{
u_int8_t *eoh = opts + hlen;
if (min_typelen < 2)
return (NULL);
while ((eoh - opt) >= min_typelen) {
switch (*opt) {
case TCPOPT_EOL:
/* FALLTHROUGH - Workaround the failure of some
systems to NOP-pad their bzero'd option buffers,
producing spurious EOLs */
case TCPOPT_NOP:
opt++;
continue;
default:
if (opt[0] == type &&
opt[1] >= min_typelen)
return (opt);
}
opt += MAX(opt[1], 2); /* evade infinite loops */
}
return (NULL);
}
u_int8_t
pf_get_wscale(struct pf_pdesc *pd)
{
int olen;
u_int8_t opts[MAX_TCPOPTLEN], *opt;
u_int8_t wscale = 0;
olen = (pd->hdr.tcp.th_off << 2) - sizeof(struct tcphdr);
if (olen < TCPOLEN_WINDOW || !pf_pull_hdr(pd->m,
pd->off + sizeof(struct tcphdr), opts, olen, NULL, NULL, pd->af))
return (0);
opt = opts;
while ((opt = pf_find_tcpopt(opt, opts, olen,
TCPOPT_WINDOW, TCPOLEN_WINDOW)) != NULL) {
wscale = opt[2];
wscale = MIN(wscale, TCP_MAX_WINSHIFT);
wscale |= PF_WSCALE_FLAG;
opt += opt[1];
}
return (wscale);
}
u_int16_t
pf_get_mss(struct pf_pdesc *pd)
{
int olen;
u_int8_t opts[MAX_TCPOPTLEN], *opt;
u_int16_t mss = tcp_mssdflt;
olen = (pd->hdr.tcp.th_off << 2) - sizeof(struct tcphdr);
if (olen < TCPOLEN_MAXSEG || !pf_pull_hdr(pd->m,
pd->off + sizeof(struct tcphdr), opts, olen, NULL, NULL, pd->af))
return (0);
opt = opts;
while ((opt = pf_find_tcpopt(opt, opts, olen,
TCPOPT_MAXSEG, TCPOLEN_MAXSEG)) != NULL) {
memcpy(&mss, (opt + 2), 2);
mss = ntohs(mss);
opt += opt[1];
}
return (mss);
}
u_int16_t
pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer)
{
struct ifnet *ifp;
struct sockaddr_in *dst;
#ifdef INET6
struct sockaddr_in6 *dst6;
#endif /* INET6 */
struct rtentry *rt = NULL;
struct sockaddr_storage ss;
int hlen;
u_int16_t mss = tcp_mssdflt;
memset(&ss, 0, sizeof(ss));
switch (af) {
case AF_INET:
hlen = sizeof(struct ip);
dst = (struct sockaddr_in *)&ss;
dst->sin_family = AF_INET;
dst->sin_len = sizeof(*dst);
dst->sin_addr = addr->v4;
rt = rtalloc(sintosa(dst), 0, rtableid);
break;
#ifdef INET6
case AF_INET6:
hlen = sizeof(struct ip6_hdr);
dst6 = (struct sockaddr_in6 *)&ss;
dst6->sin6_family = AF_INET6;
dst6->sin6_len = sizeof(*dst6);
dst6->sin6_addr = addr->v6;
rt = rtalloc(sin6tosa(dst6), 0, rtableid);
break;
#endif /* INET6 */
}
if (rt != NULL && (ifp = if_get(rt->rt_ifidx)) != NULL) {
mss = ifp->if_mtu - hlen - sizeof(struct tcphdr);
mss = max(tcp_mssdflt, mss);
if_put(ifp);
}
rtfree(rt);
mss = min(mss, offer);
mss = max(mss, 64); /* sanity - at least max opt space */
return (mss);
}
static __inline int
pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr, sa_family_t af,
struct pf_src_node **sns)
{
struct pf_rule *r = s->rule.ptr;
int rv;
if (!r->rt)
return (0);
rv = pf_map_addr(af, r, saddr, &s->rt_addr, NULL, sns,
&r->route, PF_SN_ROUTE);
if (rv == 0)
s->rt = r->rt;
return (rv);
}
u_int32_t
pf_tcp_iss(struct pf_pdesc *pd)
{
SHA2_CTX ctx;
union {
uint8_t bytes[SHA512_DIGEST_LENGTH];
uint32_t words[1];
} digest;
if (pf_tcp_secret_init == 0) {
arc4random_buf(pf_tcp_secret, sizeof(pf_tcp_secret));
SHA512Init(&pf_tcp_secret_ctx);
SHA512Update(&pf_tcp_secret_ctx, pf_tcp_secret,
sizeof(pf_tcp_secret));
pf_tcp_secret_init = 1;
}
ctx = pf_tcp_secret_ctx;
SHA512Update(&ctx, &pd->rdomain, sizeof(pd->rdomain));
SHA512Update(&ctx, &pd->hdr.tcp.th_sport, sizeof(u_short));
SHA512Update(&ctx, &pd->hdr.tcp.th_dport, sizeof(u_short));
switch (pd->af) {
case AF_INET:
SHA512Update(&ctx, &pd->src->v4, sizeof(struct in_addr));
SHA512Update(&ctx, &pd->dst->v4, sizeof(struct in_addr));
break;
#ifdef INET6
case AF_INET6:
SHA512Update(&ctx, &pd->src->v6, sizeof(struct in6_addr));
SHA512Update(&ctx, &pd->dst->v6, sizeof(struct in6_addr));
break;
#endif /* INET6 */
}
SHA512Final(digest.bytes, &ctx);
pf_tcp_iss_off += 4096;
return (digest.words[0] + READ_ONCE(tcp_iss) + pf_tcp_iss_off);
}
void
pf_rule_to_actions(struct pf_rule *r, struct pf_rule_actions *a)
{
if (r->qid)
a->qid = r->qid;
if (r->pqid)
a->pqid = r->pqid;
if (r->rtableid >= 0)
a->rtableid = r->rtableid;
#if NPFLOG > 0
a->log |= r->log;
#endif /* NPFLOG > 0 */
if (r->scrub_flags & PFSTATE_SETTOS)
a->set_tos = r->set_tos;
if (r->min_ttl)
a->min_ttl = r->min_ttl;
if (r->max_mss)
a->max_mss = r->max_mss;
a->flags |= (r->scrub_flags & (PFSTATE_NODF|PFSTATE_RANDOMID|
PFSTATE_SETTOS|PFSTATE_SCRUB_TCP|PFSTATE_SETPRIO));
if (r->scrub_flags & PFSTATE_SETPRIO) {
a->set_prio[0] = r->set_prio[0];
a->set_prio[1] = r->set_prio[1];
}
if (r->rule_flag & PFRULE_SETDELAY)
a->delay = r->delay;
}
#define PF_TEST_ATTRIB(t, a) \
if (t) { \
r = a; \
continue; \
} else do { \
} while (0)
enum pf_test_status
pf_match_rule(struct pf_test_ctx *ctx, struct pf_ruleset *ruleset)
{
struct pf_rule *r;
struct pf_rule *save_a;
struct pf_ruleset *save_aruleset;
r = TAILQ_FIRST(ruleset->rules.active.ptr);
while (r != NULL) {
r->evaluations++;
PF_TEST_ATTRIB(
(pfi_kif_match(r->kif, ctx->pd->kif) == r->ifnot),
r->skip[PF_SKIP_IFP].ptr);
PF_TEST_ATTRIB((r->direction && r->direction != ctx->pd->dir),
r->skip[PF_SKIP_DIR].ptr);
PF_TEST_ATTRIB((r->onrdomain >= 0 &&
(r->onrdomain == ctx->pd->rdomain) == r->ifnot),
r->skip[PF_SKIP_RDOM].ptr);
PF_TEST_ATTRIB((r->af && r->af != ctx->pd->af),
r->skip[PF_SKIP_AF].ptr);
PF_TEST_ATTRIB((r->proto && r->proto != ctx->pd->proto),
r->skip[PF_SKIP_PROTO].ptr);
PF_TEST_ATTRIB((PF_MISMATCHAW(&r->src.addr, &ctx->pd->nsaddr,
ctx->pd->naf, r->src.neg, ctx->pd->kif,
ctx->act.rtableid)),
r->skip[PF_SKIP_SRC_ADDR].ptr);
PF_TEST_ATTRIB((PF_MISMATCHAW(&r->dst.addr, &ctx->pd->ndaddr,
ctx->pd->af, r->dst.neg, NULL, ctx->act.rtableid)),
r->skip[PF_SKIP_DST_ADDR].ptr);
switch (ctx->pd->virtual_proto) {
case PF_VPROTO_FRAGMENT:
/* tcp/udp only. port_op always 0 in other cases */
PF_TEST_ATTRIB((r->src.port_op || r->dst.port_op),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((ctx->pd->proto == IPPROTO_TCP &&
r->flagset),
TAILQ_NEXT(r, entries));
/* icmp only. type/code always 0 in other cases */
PF_TEST_ATTRIB((r->type || r->code),
TAILQ_NEXT(r, entries));
/* tcp/udp only. {uid|gid}.op always 0 in other cases */
PF_TEST_ATTRIB((r->gid.op || r->uid.op),
TAILQ_NEXT(r, entries));
break;
case IPPROTO_TCP:
PF_TEST_ATTRIB(((r->flagset & ctx->th->th_flags) !=
r->flags),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((r->os_fingerprint != PF_OSFP_ANY &&
!pf_osfp_match(pf_osfp_fingerprint(ctx->pd),
r->os_fingerprint)),
TAILQ_NEXT(r, entries));
/* FALLTHROUGH */
case IPPROTO_UDP:
/* tcp/udp only. port_op always 0 in other cases */
PF_TEST_ATTRIB((r->src.port_op &&
!pf_match_port(r->src.port_op, r->src.port[0],
r->src.port[1], ctx->pd->nsport)),
r->skip[PF_SKIP_SRC_PORT].ptr);
PF_TEST_ATTRIB((r->dst.port_op &&
!pf_match_port(r->dst.port_op, r->dst.port[0],
r->dst.port[1], ctx->pd->ndport)),
r->skip[PF_SKIP_DST_PORT].ptr);
/* tcp/udp only. uid.op always 0 in other cases */
PF_TEST_ATTRIB((r->uid.op && (ctx->pd->lookup.done ||
(ctx->pd->lookup.done =
pf_socket_lookup(ctx->pd), 1)) &&
!pf_match_uid(r->uid.op, r->uid.uid[0],
r->uid.uid[1], ctx->pd->lookup.uid)),
TAILQ_NEXT(r, entries));
/* tcp/udp only. gid.op always 0 in other cases */
PF_TEST_ATTRIB((r->gid.op && (ctx->pd->lookup.done ||
(ctx->pd->lookup.done =
pf_socket_lookup(ctx->pd), 1)) &&
!pf_match_gid(r->gid.op, r->gid.gid[0],
r->gid.gid[1], ctx->pd->lookup.gid)),
TAILQ_NEXT(r, entries));
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
/* icmp only. type always 0 in other cases */
PF_TEST_ATTRIB((r->type &&
r->type != ctx->icmptype + 1),
TAILQ_NEXT(r, entries));
/* icmp only. type always 0 in other cases */
PF_TEST_ATTRIB((r->code &&
r->code != ctx->icmpcode + 1),
TAILQ_NEXT(r, entries));
/* icmp only. don't create states on replies */
PF_TEST_ATTRIB((r->keep_state && !ctx->state_icmp &&
(r->rule_flag & PFRULE_STATESLOPPY) == 0 &&
ctx->icmp_dir != PF_IN),
TAILQ_NEXT(r, entries));
break;
default:
break;
}
PF_TEST_ATTRIB((r->rule_flag & PFRULE_FRAGMENT &&
ctx->pd->virtual_proto != PF_VPROTO_FRAGMENT),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((r->tos && !(r->tos == ctx->pd->tos)),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((r->prob &&
r->prob <= arc4random_uniform(UINT_MAX - 1) + 1),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((r->match_tag &&
!pf_match_tag(ctx->pd->m, r, &ctx->tag)),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((r->rcv_kif && pf_match_rcvif(ctx->pd->m, r) ==
r->rcvifnot),
TAILQ_NEXT(r, entries));
PF_TEST_ATTRIB((r->prio &&
(r->prio == PF_PRIO_ZERO ? 0 : r->prio) !=
ctx->pd->m->m_pkthdr.pf.prio),
TAILQ_NEXT(r, entries));
/* must be last! */
if (r->pktrate.limit) {
pf_add_threshold(&r->pktrate);
PF_TEST_ATTRIB((pf_check_threshold(&r->pktrate)),
TAILQ_NEXT(r, entries));
}
/* FALLTHROUGH */
if (r->tag)
ctx->tag = r->tag;
if (r->anchor == NULL) {
if (r->action == PF_MATCH) {
if ((ctx->ri = pool_get(&pf_rule_item_pl,
PR_NOWAIT)) == NULL) {
REASON_SET(&ctx->reason, PFRES_MEMORY);
ctx->test_status = PF_TEST_FAIL;
break;
}
ctx->ri->r = r;
/* order is irrelevant */
SLIST_INSERT_HEAD(&ctx->rules, ctx->ri, entry);
ctx->ri = NULL;
pf_rule_to_actions(r, &ctx->act);
if (r->rule_flag & PFRULE_AFTO)
ctx->pd->naf = r->naf;
if (pf_get_transaddr(r, ctx->pd, ctx->sns,
&ctx->nr) == -1) {
REASON_SET(&ctx->reason,
PFRES_TRANSLATE);
ctx->test_status = PF_TEST_FAIL;
break;
}
#if NPFLOG > 0
if (r->log) {
REASON_SET(&ctx->reason, PFRES_MATCH);
pflog_packet(ctx->pd, ctx->reason, r,
ctx->a, ruleset, NULL);
}
#endif /* NPFLOG > 0 */
} else {
/*
* found matching r
*/
*ctx->rm = r;
/*
* anchor, with ruleset, where r belongs to
*/
*ctx->am = ctx->a;
/*
* ruleset where r belongs to
*/
*ctx->rsm = ruleset;
/*
* ruleset, where anchor belongs to.
*/
ctx->arsm = ctx->aruleset;
}
#if NPFLOG > 0
if (ctx->act.log & PF_LOG_MATCHES)
pf_log_matches(ctx->pd, r, ctx->a, ruleset,
&ctx->rules);
#endif /* NPFLOG > 0 */
if (r->quick) {
ctx->test_status = PF_TEST_QUICK;
break;
}
} else {
save_a = ctx->a;
save_aruleset = ctx->aruleset;
ctx->a = r; /* remember anchor */
ctx->aruleset = ruleset; /* and its ruleset */
/*
* Note: we don't need to restore if we are not going
* to continue with ruleset evaluation.
*/
if (pf_step_into_anchor(ctx, r) != PF_TEST_OK)
break;
ctx->a = save_a;
ctx->aruleset = save_aruleset;
}
r = TAILQ_NEXT(r, entries);
}
return (ctx->test_status);
}
int
pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm,
struct pf_rule **am, struct pf_ruleset **rsm, u_short *reason,
struct pfsync_deferral **pdeferral)
{
struct pf_rule *r = NULL;
struct pf_rule *a = NULL;
struct pf_ruleset *ruleset = NULL;
struct pf_state_key *skw = NULL, *sks = NULL;
int rewrite = 0;
u_int16_t virtual_type, virtual_id;
int action = PF_DROP;
struct pf_test_ctx ctx;
int rv;
memset(&ctx, 0, sizeof(ctx));
ctx.pd = pd;
ctx.rm = rm;
ctx.am = am;
ctx.rsm = rsm;
ctx.th = &pd->hdr.tcp;
ctx.act.rtableid = pd->rdomain;
ctx.tag = -1;
SLIST_INIT(&ctx.rules);
if (pd->dir == PF_IN && if_congested()) {
REASON_SET(&ctx.reason, PFRES_CONGEST);
return (PF_DROP);
}
switch (pd->virtual_proto) {
case IPPROTO_ICMP:
ctx.icmptype = pd->hdr.icmp.icmp_type;
ctx.icmpcode = pd->hdr.icmp.icmp_code;
ctx.state_icmp = pf_icmp_mapping(pd, ctx.icmptype,
&ctx.icmp_dir, &virtual_id, &virtual_type);
if (ctx.icmp_dir == PF_IN) {
pd->osport = pd->nsport = virtual_id;
pd->odport = pd->ndport = virtual_type;
} else {
pd->osport = pd->nsport = virtual_type;
pd->odport = pd->ndport = virtual_id;
}
break;
#ifdef INET6
case IPPROTO_ICMPV6:
ctx.icmptype = pd->hdr.icmp6.icmp6_type;
ctx.icmpcode = pd->hdr.icmp6.icmp6_code;
ctx.state_icmp = pf_icmp_mapping(pd, ctx.icmptype,
&ctx.icmp_dir, &virtual_id, &virtual_type);
if (ctx.icmp_dir == PF_IN) {
pd->osport = pd->nsport = virtual_id;
pd->odport = pd->ndport = virtual_type;
} else {
pd->osport = pd->nsport = virtual_type;
pd->odport = pd->ndport = virtual_id;
}
break;
#endif /* INET6 */
}
ruleset = &pf_main_ruleset;
rv = pf_match_rule(&ctx, ruleset);
if (rv == PF_TEST_FAIL) {
/*
* Reason has been set in pf_match_rule() already.
*/
goto cleanup;
}
r = *ctx.rm; /* matching rule */
a = *ctx.am; /* rule that defines an anchor containing 'r' */
ruleset = *ctx.rsm;/* ruleset of the anchor defined by the rule 'a' */
ctx.aruleset = ctx.arsm;/* ruleset of the 'a' rule itself */
/* apply actions for last matching pass/block rule */
pf_rule_to_actions(r, &ctx.act);
if (r->rule_flag & PFRULE_AFTO)
pd->naf = r->naf;
if (pf_get_transaddr(r, pd, ctx.sns, &ctx.nr) == -1) {
REASON_SET(&ctx.reason, PFRES_TRANSLATE);
goto cleanup;
}
REASON_SET(&ctx.reason, PFRES_MATCH);
#if NPFLOG > 0
if (r->log)
pflog_packet(pd, ctx.reason, r, a, ruleset, NULL);
if (ctx.act.log & PF_LOG_MATCHES)
pf_log_matches(pd, r, a, ruleset, &ctx.rules);
#endif /* NPFLOG > 0 */
if (pd->virtual_proto != PF_VPROTO_FRAGMENT &&
(r->action == PF_DROP) &&
((r->rule_flag & PFRULE_RETURNRST) ||
(r->rule_flag & PFRULE_RETURNICMP) ||
(r->rule_flag & PFRULE_RETURN))) {
if (pd->proto == IPPROTO_TCP &&
((r->rule_flag & PFRULE_RETURNRST) ||
(r->rule_flag & PFRULE_RETURN)) &&
!(ctx.th->th_flags & TH_RST)) {
u_int32_t ack =
ntohl(ctx.th->th_seq) + pd->p_len;
if (pf_check_tcp_cksum(pd->m, pd->off,
pd->tot_len - pd->off, pd->af))
REASON_SET(&ctx.reason, PFRES_PROTCKSUM);
else {
if (ctx.th->th_flags & TH_SYN)
ack++;
if (ctx.th->th_flags & TH_FIN)
ack++;
pf_send_tcp(r, pd->af, pd->dst,
pd->src, ctx.th->th_dport,
ctx.th->th_sport, ntohl(ctx.th->th_ack),
ack, TH_RST|TH_ACK, 0, 0, r->return_ttl,
1, 0, pd->rdomain);
}
} else if ((pd->proto != IPPROTO_ICMP ||
ICMP_INFOTYPE(ctx.icmptype)) && pd->af == AF_INET &&
r->return_icmp)
pf_send_icmp(pd->m, r->return_icmp >> 8,
r->return_icmp & 255, 0, pd->af, r, pd->rdomain);
else if ((pd->proto != IPPROTO_ICMPV6 ||
(ctx.icmptype >= ICMP6_ECHO_REQUEST &&
ctx.icmptype != ND_REDIRECT)) && pd->af == AF_INET6 &&
r->return_icmp6)
pf_send_icmp(pd->m, r->return_icmp6 >> 8,
r->return_icmp6 & 255, 0, pd->af, r, pd->rdomain);
}
if (r->action == PF_DROP)
goto cleanup;
/*
* If an expired "once" rule has not been purged, drop any new matching
* packets.
*/
if (r->rule_flag & PFRULE_EXPIRED)
goto cleanup;
pf_tag_packet(pd->m, ctx.tag, ctx.act.rtableid);
if (ctx.act.rtableid >= 0 &&
rtable_l2(ctx.act.rtableid) != pd->rdomain)
pd->destchg = 1;
if (r->action == PF_PASS && pd->badopts != 0 && ! r->allow_opts) {
REASON_SET(&ctx.reason, PFRES_IPOPTIONS);
#if NPFLOG > 0
pd->pflog |= PF_LOG_FORCE;
#endif /* NPFLOG > 0 */
DPFPRINTF(LOG_NOTICE, "dropping packet with "
"ip/ipv6 options in pf_test_rule()");
goto cleanup;
}
action = PF_PASS;
if (pd->virtual_proto != PF_VPROTO_FRAGMENT
&& !ctx.state_icmp && r->keep_state) {
if (r->rule_flag & PFRULE_SRCTRACK &&
pf_insert_src_node(&ctx.sns[PF_SN_NONE], r, PF_SN_NONE,
pd->af, pd->src, NULL, NULL) != 0) {
REASON_SET(&ctx.reason, PFRES_SRCLIMIT);
goto cleanup;
}
if (r->max_states && (r->states_cur >= r->max_states)) {
pf_status.lcounters[LCNT_STATES]++;
REASON_SET(&ctx.reason, PFRES_MAXSTATES);
goto cleanup;
}
action = pf_create_state(pd, r, a, ctx.nr, &skw, &sks,
&rewrite, sm, ctx.tag, &ctx.rules, &ctx.act, ctx.sns);
if (action != PF_PASS)
goto cleanup;
if (sks != skw) {
struct pf_state_key *sk;
if (pd->dir == PF_IN)
sk = sks;
else
sk = skw;
rewrite += pf_translate(pd,
&sk->addr[pd->af == pd->naf ? pd->sidx : pd->didx],
sk->port[pd->af == pd->naf ? pd->sidx : pd->didx],
&sk->addr[pd->af == pd->naf ? pd->didx : pd->sidx],
sk->port[pd->af == pd->naf ? pd->didx : pd->sidx],
virtual_type, ctx.icmp_dir);
}
#ifdef INET6
if (rewrite && skw->af != sks->af)
action = PF_AFRT;
#endif /* INET6 */
} else {
while ((ctx.ri = SLIST_FIRST(&ctx.rules))) {
SLIST_REMOVE_HEAD(&ctx.rules, entry);
pool_put(&pf_rule_item_pl, ctx.ri);
}
}
/* copy back packet headers if needed */
if (rewrite && pd->hdrlen) {
m_copyback(pd->m, pd->off, pd->hdrlen, &pd->hdr, M_NOWAIT);
}
if (r->rule_flag & PFRULE_ONCE) {
u_int32_t rule_flag;
/*
* Use atomic_cas() to determine a clear winner, which will
* insert an expired rule to gcl.
*/
rule_flag = r->rule_flag;
if (((rule_flag & PFRULE_EXPIRED) == 0) &&
atomic_cas_uint(&r->rule_flag, rule_flag,
rule_flag | PFRULE_EXPIRED) == rule_flag) {
r->exptime = gettime();
SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);
}
}
#if NPFSYNC > 0
if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) &&
pd->dir == PF_OUT && pfsync_up()) {
/*
* We want the state created, but we dont
* want to send this in case a partner
* firewall has to know about it to allow
* replies through it.
*/
if (pfsync_defer(*sm, pd->m, pdeferral))
return (PF_DEFER);
}
#endif /* NPFSYNC > 0 */
return (action);
cleanup:
while ((ctx.ri = SLIST_FIRST(&ctx.rules))) {
SLIST_REMOVE_HEAD(&ctx.rules, entry);
pool_put(&pf_rule_item_pl, ctx.ri);
}
return (action);
}
static __inline int
pf_create_state(struct pf_pdesc *pd, struct pf_rule *r, struct pf_rule *a,
struct pf_rule *nr, struct pf_state_key **skw, struct pf_state_key **sks,
int *rewrite, struct pf_state **sm, int tag, struct pf_rule_slist *rules,
struct pf_rule_actions *act, struct pf_src_node *sns[PF_SN_MAX])
{
struct pf_state *s = NULL;
struct tcphdr *th = &pd->hdr.tcp;
u_int16_t mss = tcp_mssdflt;
u_short reason;
u_int i;
s = pool_get(&pf_state_pl, PR_NOWAIT | PR_ZERO);
if (s == NULL) {
REASON_SET(&reason, PFRES_MEMORY);
goto csfailed;
}
s->rule.ptr = r;
s->anchor.ptr = a;
s->natrule.ptr = nr;
if (r->allow_opts)
s->state_flags |= PFSTATE_ALLOWOPTS;
if (r->rule_flag & PFRULE_STATESLOPPY)
s->state_flags |= PFSTATE_SLOPPY;
if (r->rule_flag & PFRULE_PFLOW)
s->state_flags |= PFSTATE_PFLOW;
#if NPFLOG > 0
s->log = act->log & PF_LOG_ALL;
#endif /* NPFLOG > 0 */
s->qid = act->qid;
s->pqid = act->pqid;
s->rtableid[pd->didx] = act->rtableid;
s->rtableid[pd->sidx] = -1; /* return traffic is routed normally */
s->min_ttl = act->min_ttl;
s->set_tos = act->set_tos;
s->max_mss = act->max_mss;
s->state_flags |= act->flags;
#if NPFSYNC > 0
s->sync_state = PFSYNC_S_NONE;
#endif /* NPFSYNC > 0 */
s->set_prio[0] = act->set_prio[0];
s->set_prio[1] = act->set_prio[1];
s->delay = act->delay;
SLIST_INIT(&s->src_nodes);
/*
* must initialize refcnt, before pf_state_insert() gets called.
* pf_state_inserts() grabs reference for pfsync!
*/
refcnt_init(&s->refcnt);
switch (pd->proto) {
case IPPROTO_TCP:
s->src.seqlo = ntohl(th->th_seq);
s->src.seqhi = s->src.seqlo + pd->p_len + 1;
if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN &&
r->keep_state == PF_STATE_MODULATE) {
/* Generate sequence number modulator */
if ((s->src.seqdiff = pf_tcp_iss(pd) - s->src.seqlo) ==
0)
s->src.seqdiff = 1;
pf_patch_32(pd,
&th->th_seq, htonl(s->src.seqlo + s->src.seqdiff));
*rewrite = 1;
} else
s->src.seqdiff = 0;
if (th->th_flags & TH_SYN) {
s->src.seqhi++;
s->src.wscale = pf_get_wscale(pd);
}
s->src.max_win = MAX(ntohs(th->th_win), 1);
if (s->src.wscale & PF_WSCALE_MASK) {
/* Remove scale factor from initial window */
int win = s->src.max_win;
win += 1 << (s->src.wscale & PF_WSCALE_MASK);
s->src.max_win = (win - 1) >>
(s->src.wscale & PF_WSCALE_MASK);
}
if (th->th_flags & TH_FIN)
s->src.seqhi++;
s->dst.seqhi = 1;
s->dst.max_win = 1;
pf_set_protostate(s, PF_PEER_SRC, TCPS_SYN_SENT);
pf_set_protostate(s, PF_PEER_DST, TCPS_CLOSED);
s->timeout = PFTM_TCP_FIRST_PACKET;
pf_status.states_halfopen++;
break;
case IPPROTO_UDP:
pf_set_protostate(s, PF_PEER_SRC, PFUDPS_SINGLE);
pf_set_protostate(s, PF_PEER_DST, PFUDPS_NO_TRAFFIC);
s->timeout = PFTM_UDP_FIRST_PACKET;
break;
case IPPROTO_ICMP:
#ifdef INET6
case IPPROTO_ICMPV6:
#endif /* INET6 */
s->timeout = PFTM_ICMP_FIRST_PACKET;
break;
default:
pf_set_protostate(s, PF_PEER_SRC, PFOTHERS_SINGLE);
pf_set_protostate(s, PF_PEER_DST, PFOTHERS_NO_TRAFFIC);
s->timeout = PFTM_OTHER_FIRST_PACKET;
}
s->creation = getuptime();
s->expire = getuptime();
if (pd->proto == IPPROTO_TCP) {
if (s->state_flags & PFSTATE_SCRUB_TCP &&
pf_normalize_tcp_init(pd, &s->src)) {
REASON_SET(&reason, PFRES_MEMORY);
goto csfailed;
}
if (s->state_flags & PFSTATE_SCRUB_TCP && s->src.scrub &&
pf_normalize_tcp_stateful(pd, &reason, s, &s->src, &s->dst,
rewrite)) {
/* This really shouldn't happen!!! */
DPFPRINTF(LOG_ERR,
"%s: tcp normalize failed on first pkt", __func__);
goto csfailed;
}
}
s->direction = pd->dir;
if (pf_state_key_setup(pd, skw, sks, act->rtableid)) {
REASON_SET(&reason, PFRES_MEMORY);
goto csfailed;
}
if (pf_set_rt_ifp(s, pd->src, (*skw)->af, sns) != 0) {
REASON_SET(&reason, PFRES_NOROUTE);
goto csfailed;
}
for (i = 0; i < PF_SN_MAX; i++)
if (sns[i] != NULL) {
struct pf_sn_item *sni;
sni = pool_get(&pf_sn_item_pl, PR_NOWAIT);
if (sni == NULL) {
REASON_SET(&reason, PFRES_MEMORY);
goto csfailed;
}
sni->sn = sns[i];
SLIST_INSERT_HEAD(&s->src_nodes, sni, next);
sni->sn->states++;
}
if (pf_state_insert(BOUND_IFACE(r, pd->kif), skw, sks, s)) {
pf_detach_state(s);
*sks = *skw = NULL;
REASON_SET(&reason, PFRES_STATEINS);
goto csfailed;
} else
*sm = s;
/*
* Make state responsible for rules it binds here.
*/
memcpy(&s->match_rules, rules, sizeof(s->match_rules));
memset(rules, 0, sizeof(*rules));
STATE_INC_COUNTERS(s);
if (tag > 0) {
pf_tag_ref(tag);
s->tag = tag;
}
if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) ==
TH_SYN && r->keep_state == PF_STATE_SYNPROXY && pd->dir == PF_IN) {
int rtid = pd->rdomain;
if (act->rtableid >= 0)
rtid = act->rtableid;
pf_set_protostate(s, PF_PEER_SRC, PF_TCPS_PROXY_SRC);
s->src.seqhi = arc4random();
/* Find mss option */
mss = pf_get_mss(pd);
mss = pf_calc_mss(pd->src, pd->af, rtid, mss);
mss = pf_calc_mss(pd->dst, pd->af, rtid, mss);
s->src.mss = mss;
pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport,
th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1,
TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, pd->rdomain);
REASON_SET(&reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
}
return (PF_PASS);
csfailed:
if (s) {
pf_normalize_tcp_cleanup(s); /* safe even w/o init */
pf_src_tree_remove_state(s);
pool_put(&pf_state_pl, s);
}
for (i = 0; i < PF_SN_MAX; i++)
if (sns[i] != NULL)
pf_remove_src_node(sns[i]);
return (PF_DROP);
}
int
pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
struct pf_addr *daddr, u_int16_t dport, u_int16_t virtual_type,
int icmp_dir)
{
int rewrite = 0;
int afto = pd->af != pd->naf;
if (afto || PF_ANEQ(daddr, pd->dst, pd->af))
pd->destchg = 1;
switch (pd->proto) {
case IPPROTO_TCP: /* FALLTHROUGH */
case IPPROTO_UDP:
rewrite += pf_patch_16(pd, pd->sport, sport);
rewrite += pf_patch_16(pd, pd->dport, dport);
break;
case IPPROTO_ICMP:
if (pd->af != AF_INET)
return (0);
#ifdef INET6
if (afto) {
if (pf_translate_icmp_af(pd, AF_INET6, &pd->hdr.icmp))
return (0);
pd->proto = IPPROTO_ICMPV6;
rewrite = 1;
}
#endif /* INET6 */
if (virtual_type == htons(ICMP_ECHO)) {
u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport;
rewrite += pf_patch_16(pd,
&pd->hdr.icmp.icmp_id, icmpid);
}
break;
#ifdef INET6
case IPPROTO_ICMPV6:
if (pd->af != AF_INET6)
return (0);
if (afto) {
if (pf_translate_icmp_af(pd, AF_INET, &pd->hdr.icmp6))
return (0);
pd->proto = IPPROTO_ICMP;
rewrite = 1;
}
if (virtual_type == htons(ICMP6_ECHO_REQUEST)) {
u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport;
rewrite += pf_patch_16(pd,
&pd->hdr.icmp6.icmp6_id, icmpid);
}
break;
#endif /* INET6 */
}
if (!afto) {
rewrite += pf_translate_a(pd, pd->src, saddr);
rewrite += pf_translate_a(pd, pd->dst, daddr);
}
return (rewrite);
}
int
pf_tcp_track_full(struct pf_pdesc *pd, struct pf_state **state, u_short *reason,
int *copyback, int reverse)
{
struct tcphdr *th = &pd->hdr.tcp;
struct pf_state_peer *src, *dst;
u_int16_t win = ntohs(th->th_win);
u_int32_t ack, end, data_end, seq, orig_seq;
u_int8_t sws, dws, psrc, pdst;
int ackskew;
if ((pd->dir == (*state)->direction && !reverse) ||
(pd->dir != (*state)->direction && reverse)) {
src = &(*state)->src;
dst = &(*state)->dst;
psrc = PF_PEER_SRC;
pdst = PF_PEER_DST;
} else {
src = &(*state)->dst;
dst = &(*state)->src;
psrc = PF_PEER_DST;
pdst = PF_PEER_SRC;
}
if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) {
sws = src->wscale & PF_WSCALE_MASK;
dws = dst->wscale & PF_WSCALE_MASK;
} else
sws = dws = 0;
/*
* Sequence tracking algorithm from Guido van Rooij's paper:
* http://www.madison-gurkha.com/publications/tcp_filtering/
* tcp_filtering.ps
*/
orig_seq = seq = ntohl(th->th_seq);
if (src->seqlo == 0) {
/* First packet from this end. Set its state */
if (((*state)->state_flags & PFSTATE_SCRUB_TCP || dst->scrub) &&
src->scrub == NULL) {
if (pf_normalize_tcp_init(pd, src)) {
REASON_SET(reason, PFRES_MEMORY);
return (PF_DROP);
}
}
/* Deferred generation of sequence number modulator */
if (dst->seqdiff && !src->seqdiff) {
/* use random iss for the TCP server */
while ((src->seqdiff = arc4random() - seq) == 0)
continue;
ack = ntohl(th->th_ack) - dst->seqdiff;
pf_patch_32(pd, &th->th_seq, htonl(seq + src->seqdiff));
pf_patch_32(pd, &th->th_ack, htonl(ack));
*copyback = 1;
} else {
ack = ntohl(th->th_ack);
}
end = seq + pd->p_len;
if (th->th_flags & TH_SYN) {
end++;
if (dst->wscale & PF_WSCALE_FLAG) {
src->wscale = pf_get_wscale(pd);
if (src->wscale & PF_WSCALE_FLAG) {
/* Remove scale factor from initial
* window */
sws = src->wscale & PF_WSCALE_MASK;
win = ((u_int32_t)win + (1 << sws) - 1)
>> sws;
dws = dst->wscale & PF_WSCALE_MASK;
} else {
/* fixup other window */
dst->max_win = MIN(TCP_MAXWIN,
(u_int32_t)dst->max_win <<
(dst->wscale & PF_WSCALE_MASK));
/* in case of a retrans SYN|ACK */
dst->wscale = 0;
}
}
}
data_end = end;
if (th->th_flags & TH_FIN)
end++;
src->seqlo = seq;
if (src->state < TCPS_SYN_SENT)
pf_set_protostate(*state, psrc, TCPS_SYN_SENT);
/*
* May need to slide the window (seqhi may have been set by
* the crappy stack check or if we picked up the connection
* after establishment)
*/
if (src->seqhi == 1 ||
SEQ_GEQ(end + MAX(1, dst->max_win << dws), src->seqhi))
src->seqhi = end + MAX(1, dst->max_win << dws);
if (win > src->max_win)
src->max_win = win;
} else {
ack = ntohl(th->th_ack) - dst->seqdiff;
if (src->seqdiff) {
/* Modulate sequence numbers */
pf_patch_32(pd, &th->th_seq, htonl(seq + src->seqdiff));
pf_patch_32(pd, &th->th_ack, htonl(ack));
*copyback = 1;
}
end = seq + pd->p_len;
if (th->th_flags & TH_SYN)
end++;
data_end = end;
if (th->th_flags & TH_FIN)
end++;
}
if ((th->th_flags & TH_ACK) == 0) {
/* Let it pass through the ack skew check */
ack = dst->seqlo;
} else if ((ack == 0 &&
(th->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) ||
/* broken tcp stacks do not set ack */
(dst->state < TCPS_SYN_SENT)) {
/*
* Many stacks (ours included) will set the ACK number in an
* FIN|ACK if the SYN times out -- no sequence to ACK.
*/
ack = dst->seqlo;
}
if (seq == end) {
/* Ease sequencing restrictions on no data packets */
seq = src->seqlo;
data_end = end = seq;
}
ackskew = dst->seqlo - ack;
/*
* Need to demodulate the sequence numbers in any TCP SACK options
* (Selective ACK). We could optionally validate the SACK values
* against the current ACK window, either forwards or backwards, but
* I'm not confident that SACK has been implemented properly
* everywhere. It wouldn't surprise me if several stacks accidently
* SACK too far backwards of previously ACKed data. There really aren't
* any security implications of bad SACKing unless the target stack
* doesn't validate the option length correctly. Someone trying to
* spoof into a TCP connection won't bother blindly sending SACK
* options anyway.
*/
if (dst->seqdiff && (th->th_off << 2) > sizeof(struct tcphdr)) {
if (pf_modulate_sack(pd, dst))
*copyback = 1;
}
#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */
if (SEQ_GEQ(src->seqhi, data_end) &&
/* Last octet inside other's window space */
SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) &&
/* Retrans: not more than one window back */
(ackskew >= -MAXACKWINDOW) &&
/* Acking not more than one reassembled fragment backwards */
(ackskew <= (MAXACKWINDOW << sws)) &&
/* Acking not more than one window forward */
((th->th_flags & TH_RST) == 0 || orig_seq == src->seqlo ||
(orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo))) {
/* Require an exact/+1 sequence match on resets when possible */
if (dst->scrub || src->scrub) {
if (pf_normalize_tcp_stateful(pd, reason, *state, src,
dst, copyback))
return (PF_DROP);
}
/* update max window */
if (src->max_win < win)
src->max_win = win;
/* synchronize sequencing */
if (SEQ_GT(end, src->seqlo))
src->seqlo = end;
/* slide the window of what the other end can send */
if (SEQ_GEQ(ack + (win << sws), dst->seqhi))
dst->seqhi = ack + MAX((win << sws), 1);
/* update states */
if (th->th_flags & TH_SYN)
if (src->state < TCPS_SYN_SENT)
pf_set_protostate(*state, psrc, TCPS_SYN_SENT);
if (th->th_flags & TH_FIN)
if (src->state < TCPS_CLOSING)
pf_set_protostate(*state, psrc, TCPS_CLOSING);
if (th->th_flags & TH_ACK) {
if (dst->state == TCPS_SYN_SENT) {
pf_set_protostate(*state, pdst,
TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
!SLIST_EMPTY(&(*state)->src_nodes) &&
pf_src_connlimit(state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
} else if (dst->state == TCPS_CLOSING)
pf_set_protostate(*state, pdst,
TCPS_FIN_WAIT_2);
}
if (th->th_flags & TH_RST)
pf_set_protostate(*state, PF_PEER_BOTH, TCPS_TIME_WAIT);
/* update expire time */
(*state)->expire = getuptime();
if (src->state >= TCPS_FIN_WAIT_2 &&
dst->state >= TCPS_FIN_WAIT_2)
(*state)->timeout = PFTM_TCP_CLOSED;
else if (src->state >= TCPS_CLOSING &&
dst->state >= TCPS_CLOSING)
(*state)->timeout = PFTM_TCP_FIN_WAIT;
else if (src->state < TCPS_ESTABLISHED ||
dst->state < TCPS_ESTABLISHED)
(*state)->timeout = PFTM_TCP_OPENING;
else if (src->state >= TCPS_CLOSING ||
dst->state >= TCPS_CLOSING)
(*state)->timeout = PFTM_TCP_CLOSING;
else
(*state)->timeout = PFTM_TCP_ESTABLISHED;
/* Fall through to PASS packet */
} else if ((dst->state < TCPS_SYN_SENT ||
dst->state >= TCPS_FIN_WAIT_2 ||
src->state >= TCPS_FIN_WAIT_2) &&
SEQ_GEQ(src->seqhi + MAXACKWINDOW, data_end) &&
/* Within a window forward of the originating packet */
SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW)) {
/* Within a window backward of the originating packet */
/*
* This currently handles three situations:
* 1) Stupid stacks will shotgun SYNs before their peer
* replies.
* 2) When PF catches an already established stream (the
* firewall rebooted, the state table was flushed, routes
* changed...)
* 3) Packets get funky immediately after the connection
* closes (this should catch Solaris spurious ACK|FINs
* that web servers like to spew after a close)
*
* This must be a little more careful than the above code
* since packet floods will also be caught here. We don't
* update the TTL here to mitigate the damage of a packet
* flood and so the same code can handle awkward establishment
* and a loosened connection close.
* In the establishment case, a correct peer response will
* validate the connection, go through the normal state code
* and keep updating the state TTL.
*/
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: loose state match: ");
pf_print_state(*state);
pf_print_flags(th->th_flags);
addlog(" seq=%u (%u) ack=%u len=%u ackskew=%d "
"pkts=%llu:%llu dir=%s,%s\n", seq, orig_seq, ack,
pd->p_len, ackskew, (*state)->packets[0],
(*state)->packets[1],
pd->dir == PF_IN ? "in" : "out",
pd->dir == (*state)->direction ? "fwd" : "rev");
}
if (dst->scrub || src->scrub) {
if (pf_normalize_tcp_stateful(pd, reason, *state, src,
dst, copyback))
return (PF_DROP);
}
/* update max window */
if (src->max_win < win)
src->max_win = win;
/* synchronize sequencing */
if (SEQ_GT(end, src->seqlo))
src->seqlo = end;
/* slide the window of what the other end can send */
if (SEQ_GEQ(ack + (win << sws), dst->seqhi))
dst->seqhi = ack + MAX((win << sws), 1);
/*
* Cannot set dst->seqhi here since this could be a shotgunned
* SYN and not an already established connection.
*/
if (th->th_flags & TH_FIN)
if (src->state < TCPS_CLOSING)
pf_set_protostate(*state, psrc, TCPS_CLOSING);
if (th->th_flags & TH_RST)
pf_set_protostate(*state, PF_PEER_BOTH, TCPS_TIME_WAIT);
/* Fall through to PASS packet */
} else {
if ((*state)->dst.state == TCPS_SYN_SENT &&
(*state)->src.state == TCPS_SYN_SENT) {
/* Send RST for state mismatches during handshake */
if (!(th->th_flags & TH_RST))
pf_send_tcp((*state)->rule.ptr, pd->af,
pd->dst, pd->src, th->th_dport,
th->th_sport, ntohl(th->th_ack), 0,
TH_RST, 0, 0,
(*state)->rule.ptr->return_ttl, 1, 0,
pd->rdomain);
src->seqlo = 0;
src->seqhi = 1;
src->max_win = 1;
} else if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: BAD state: ");
pf_print_state(*state);
pf_print_flags(th->th_flags);
addlog(" seq=%u (%u) ack=%u len=%u ackskew=%d "
"pkts=%llu:%llu dir=%s,%s\n",
seq, orig_seq, ack, pd->p_len, ackskew,
(*state)->packets[0], (*state)->packets[1],
pd->dir == PF_IN ? "in" : "out",
pd->dir == (*state)->direction ? "fwd" : "rev");
addlog("pf: State failure on: %c %c %c %c | %c %c\n",
SEQ_GEQ(src->seqhi, data_end) ? ' ' : '1',
SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ?
' ': '2',
(ackskew >= -MAXACKWINDOW) ? ' ' : '3',
(ackskew <= (MAXACKWINDOW << sws)) ? ' ' : '4',
SEQ_GEQ(src->seqhi + MAXACKWINDOW, data_end) ?
' ' :'5',
SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW) ?' ' :'6');
}
REASON_SET(reason, PFRES_BADSTATE);
return (PF_DROP);
}
return (PF_PASS);
}
int
pf_tcp_track_sloppy(struct pf_pdesc *pd, struct pf_state **state,
u_short *reason)
{
struct tcphdr *th = &pd->hdr.tcp;
struct pf_state_peer *src, *dst;
u_int8_t psrc, pdst;
if (pd->dir == (*state)->direction) {
src = &(*state)->src;
dst = &(*state)->dst;
psrc = PF_PEER_SRC;
pdst = PF_PEER_DST;
} else {
src = &(*state)->dst;
dst = &(*state)->src;
psrc = PF_PEER_DST;
pdst = PF_PEER_SRC;
}
if (th->th_flags & TH_SYN)
if (src->state < TCPS_SYN_SENT)
pf_set_protostate(*state, psrc, TCPS_SYN_SENT);
if (th->th_flags & TH_FIN)
if (src->state < TCPS_CLOSING)
pf_set_protostate(*state, psrc, TCPS_CLOSING);
if (th->th_flags & TH_ACK) {
if (dst->state == TCPS_SYN_SENT) {
pf_set_protostate(*state, pdst, TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
!SLIST_EMPTY(&(*state)->src_nodes) &&
pf_src_connlimit(state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
} else if (dst->state == TCPS_CLOSING) {
pf_set_protostate(*state, pdst, TCPS_FIN_WAIT_2);
} else if (src->state == TCPS_SYN_SENT &&
dst->state < TCPS_SYN_SENT) {
/*
* Handle a special sloppy case where we only see one
* half of the connection. If there is a ACK after
* the initial SYN without ever seeing a packet from
* the destination, set the connection to established.
*/
pf_set_protostate(*state, PF_PEER_BOTH,
TCPS_ESTABLISHED);
if (!SLIST_EMPTY(&(*state)->src_nodes) &&
pf_src_connlimit(state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
} else if (src->state == TCPS_CLOSING &&
dst->state == TCPS_ESTABLISHED &&
dst->seqlo == 0) {
/*
* Handle the closing of half connections where we
* don't see the full bidirectional FIN/ACK+ACK
* handshake.
*/
pf_set_protostate(*state, pdst, TCPS_CLOSING);
}
}
if (th->th_flags & TH_RST)
pf_set_protostate(*state, PF_PEER_BOTH, TCPS_TIME_WAIT);
/* update expire time */
(*state)->expire = getuptime();
if (src->state >= TCPS_FIN_WAIT_2 &&
dst->state >= TCPS_FIN_WAIT_2)
(*state)->timeout = PFTM_TCP_CLOSED;
else if (src->state >= TCPS_CLOSING &&
dst->state >= TCPS_CLOSING)
(*state)->timeout = PFTM_TCP_FIN_WAIT;
else if (src->state < TCPS_ESTABLISHED ||
dst->state < TCPS_ESTABLISHED)
(*state)->timeout = PFTM_TCP_OPENING;
else if (src->state >= TCPS_CLOSING ||
dst->state >= TCPS_CLOSING)
(*state)->timeout = PFTM_TCP_CLOSING;
else
(*state)->timeout = PFTM_TCP_ESTABLISHED;
return (PF_PASS);
}
static __inline int
pf_synproxy(struct pf_pdesc *pd, struct pf_state **state, u_short *reason)
{
struct pf_state_key *sk = (*state)->key[pd->didx];
if ((*state)->src.state == PF_TCPS_PROXY_SRC) {
struct tcphdr *th = &pd->hdr.tcp;
if (pd->dir != (*state)->direction) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
}
if (th->th_flags & TH_SYN) {
if (ntohl(th->th_seq) != (*state)->src.seqlo) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_DROP);
}
pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst,
pd->src, th->th_dport, th->th_sport,
(*state)->src.seqhi, ntohl(th->th_seq) + 1,
TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1,
0, pd->rdomain);
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
} else if ((th->th_flags & (TH_ACK|TH_RST|TH_FIN)) != TH_ACK ||
(ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
(ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_DROP);
} else if (!SLIST_EMPTY(&(*state)->src_nodes) &&
pf_src_connlimit(state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
} else
pf_set_protostate(*state, PF_PEER_SRC,
PF_TCPS_PROXY_DST);
}
if ((*state)->src.state == PF_TCPS_PROXY_DST) {
struct tcphdr *th = &pd->hdr.tcp;
if (pd->dir == (*state)->direction) {
if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) ||
(ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
(ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_DROP);
}
(*state)->src.max_win = MAX(ntohs(th->th_win), 1);
if ((*state)->dst.seqhi == 1)
(*state)->dst.seqhi = arc4random();
pf_send_tcp((*state)->rule.ptr, pd->af,
&sk->addr[pd->sidx], &sk->addr[pd->didx],
sk->port[pd->sidx], sk->port[pd->didx],
(*state)->dst.seqhi, 0, TH_SYN, 0,
(*state)->src.mss, 0, 0, (*state)->tag,
sk->rdomain);
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
} else if (((th->th_flags & (TH_SYN|TH_ACK)) !=
(TH_SYN|TH_ACK)) ||
(ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_DROP);
} else {
(*state)->dst.max_win = MAX(ntohs(th->th_win), 1);
(*state)->dst.seqlo = ntohl(th->th_seq);
pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst,
pd->src, th->th_dport, th->th_sport,
ntohl(th->th_ack), ntohl(th->th_seq) + 1,
TH_ACK, (*state)->src.max_win, 0, 0, 0,
(*state)->tag, pd->rdomain);
pf_send_tcp((*state)->rule.ptr, pd->af,
&sk->addr[pd->sidx], &sk->addr[pd->didx],
sk->port[pd->sidx], sk->port[pd->didx],
(*state)->src.seqhi + 1, (*state)->src.seqlo + 1,
TH_ACK, (*state)->dst.max_win, 0, 0, 1,
0, sk->rdomain);
(*state)->src.seqdiff = (*state)->dst.seqhi -
(*state)->src.seqlo;
(*state)->dst.seqdiff = (*state)->src.seqhi -
(*state)->dst.seqlo;
(*state)->src.seqhi = (*state)->src.seqlo +
(*state)->dst.max_win;
(*state)->dst.seqhi = (*state)->dst.seqlo +
(*state)->src.max_win;
(*state)->src.wscale = (*state)->dst.wscale = 0;
pf_set_protostate(*state, PF_PEER_BOTH,
TCPS_ESTABLISHED);
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
}
}
return (PF_PASS);
}
int
pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason)
{
int copyback = 0;
struct pf_state_peer *src, *dst;
int action;
struct inpcb *inp = pd->m->m_pkthdr.pf.inp;
u_int8_t psrc, pdst;
action = PF_PASS;
if (pd->dir == (*state)->direction) {
src = &(*state)->src;
dst = &(*state)->dst;
psrc = PF_PEER_SRC;
pdst = PF_PEER_DST;
} else {
src = &(*state)->dst;
dst = &(*state)->src;
psrc = PF_PEER_DST;
pdst = PF_PEER_SRC;
}
switch (pd->virtual_proto) {
case IPPROTO_TCP:
if ((action = pf_synproxy(pd, state, reason)) != PF_PASS)
return (action);
if ((pd->hdr.tcp.th_flags & (TH_SYN|TH_ACK)) == TH_SYN) {
if (dst->state >= TCPS_FIN_WAIT_2 &&
src->state >= TCPS_FIN_WAIT_2) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE, "pf: state reuse ");
pf_print_state(*state);
pf_print_flags(pd->hdr.tcp.th_flags);
addlog("\n");
}
/* XXX make sure it's the same direction ?? */
(*state)->timeout = PFTM_PURGE;
pf_state_unref(*state);
*state = NULL;
pf_mbuf_link_inpcb(pd->m, inp);
return (PF_DROP);
} else if (dst->state >= TCPS_ESTABLISHED &&
src->state >= TCPS_ESTABLISHED) {
/*
* SYN matches existing state???
* Typically happens when sender boots up after
* sudden panic. Certain protocols (NFSv3) are
* always using same port numbers. Challenge
* ACK enables all parties (firewall and peers)
* to get in sync again.
*/
pf_send_challenge_ack(pd, *state, src, dst);
return (PF_DROP);
}
}
if ((*state)->state_flags & PFSTATE_SLOPPY) {
if (pf_tcp_track_sloppy(pd, state, reason) == PF_DROP)
return (PF_DROP);
} else {
if (pf_tcp_track_full(pd, state, reason, ©back,
PF_REVERSED_KEY((*state)->key, pd->af)) == PF_DROP)
return (PF_DROP);
}
break;
case IPPROTO_UDP:
/* update states */
if (src->state < PFUDPS_SINGLE)
pf_set_protostate(*state, psrc, PFUDPS_SINGLE);
if (dst->state == PFUDPS_SINGLE)
pf_set_protostate(*state, pdst, PFUDPS_MULTIPLE);
/* update expire time */
(*state)->expire = getuptime();
if (src->state == PFUDPS_MULTIPLE &&
dst->state == PFUDPS_MULTIPLE)
(*state)->timeout = PFTM_UDP_MULTIPLE;
else
(*state)->timeout = PFTM_UDP_SINGLE;
break;
default:
/* update states */
if (src->state < PFOTHERS_SINGLE)
pf_set_protostate(*state, psrc, PFOTHERS_SINGLE);
if (dst->state == PFOTHERS_SINGLE)
pf_set_protostate(*state, pdst, PFOTHERS_MULTIPLE);
/* update expire time */
(*state)->expire = getuptime();
if (src->state == PFOTHERS_MULTIPLE &&
dst->state == PFOTHERS_MULTIPLE)
(*state)->timeout = PFTM_OTHER_MULTIPLE;
else
(*state)->timeout = PFTM_OTHER_SINGLE;
break;
}
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
int afto, sidx, didx;
if (PF_REVERSED_KEY((*state)->key, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
sidx = afto ? pd->didx : pd->sidx;
didx = afto ? pd->sidx : pd->didx;
#ifdef INET6
if (afto) {
pf_addrcpy(&pd->nsaddr, &nk->addr[sidx], nk->af);
pf_addrcpy(&pd->ndaddr, &nk->addr[didx], nk->af);
pd->naf = nk->af;
action = PF_AFRT;
}
#endif /* INET6 */
if (!afto)
pf_translate_a(pd, pd->src, &nk->addr[sidx]);
if (pd->sport != NULL)
pf_patch_16(pd, pd->sport, nk->port[sidx]);
if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
pd->rdomain != nk->rdomain)
pd->destchg = 1;
if (!afto)
pf_translate_a(pd, pd->dst, &nk->addr[didx]);
if (pd->dport != NULL)
pf_patch_16(pd, pd->dport, nk->port[didx]);
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
copyback = 1;
}
if (copyback && pd->hdrlen > 0) {
m_copyback(pd->m, pd->off, pd->hdrlen, &pd->hdr, M_NOWAIT);
}
return (action);
}
int
pf_icmp_state_lookup(struct pf_pdesc *pd, struct pf_state_key_cmp *key,
struct pf_state **state, u_int16_t icmpid, u_int16_t type,
int icmp_dir, int *iidx, int multi, int inner)
{
int direction, action;
key->af = pd->af;
key->proto = pd->proto;
key->rdomain = pd->rdomain;
if (icmp_dir == PF_IN) {
*iidx = pd->sidx;
key->port[pd->sidx] = icmpid;
key->port[pd->didx] = type;
} else {
*iidx = pd->didx;
key->port[pd->sidx] = type;
key->port[pd->didx] = icmpid;
}
if (pf_state_key_addr_setup(pd, key, pd->sidx, pd->src, pd->didx,
pd->dst, pd->af, multi))
return (PF_DROP);
action = pf_find_state(pd, key, state);
if (action != PF_MATCH)
return (action);
if ((*state)->state_flags & PFSTATE_SLOPPY)
return (-1);
/* Is this ICMP message flowing in right direction? */
if ((*state)->key[PF_SK_WIRE]->af != (*state)->key[PF_SK_STACK]->af)
direction = (pd->af == (*state)->key[PF_SK_WIRE]->af) ?
PF_IN : PF_OUT;
else
direction = (*state)->direction;
if ((((!inner && direction == pd->dir) ||
(inner && direction != pd->dir)) ?
PF_IN : PF_OUT) != icmp_dir) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: icmp type %d in wrong direction (%d): ",
ntohs(type), icmp_dir);
pf_print_state(*state);
addlog("\n");
}
return (PF_DROP);
}
return (-1);
}
int
pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
u_short *reason)
{
u_int16_t virtual_id, virtual_type;
u_int8_t icmptype, icmpcode;
int icmp_dir, iidx, ret, copyback = 0;
struct pf_state_key_cmp key;
switch (pd->proto) {
case IPPROTO_ICMP:
icmptype = pd->hdr.icmp.icmp_type;
icmpcode = pd->hdr.icmp.icmp_code;
break;
#ifdef INET6
case IPPROTO_ICMPV6:
icmptype = pd->hdr.icmp6.icmp6_type;
icmpcode = pd->hdr.icmp6.icmp6_code;
break;
#endif /* INET6 */
default:
panic("unhandled proto %d", pd->proto);
}
if (pf_icmp_mapping(pd, icmptype, &icmp_dir, &virtual_id,
&virtual_type) == 0) {
/*
* ICMP query/reply message not related to a TCP/UDP packet.
* Search for an ICMP state.
*/
ret = pf_icmp_state_lookup(pd, &key, state,
virtual_id, virtual_type, icmp_dir, &iidx,
0, 0);
/* IPv6? try matching a multicast address */
if (ret == PF_DROP && pd->af == AF_INET6 && icmp_dir == PF_OUT)
ret = pf_icmp_state_lookup(pd, &key, state, virtual_id,
virtual_type, icmp_dir, &iidx, 1, 0);
if (ret >= 0)
return (ret);
(*state)->expire = getuptime();
(*state)->timeout = PFTM_ICMP_ERROR_REPLY;
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
int afto, sidx, didx;
if (PF_REVERSED_KEY((*state)->key, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
sidx = afto ? pd->didx : pd->sidx;
didx = afto ? pd->sidx : pd->didx;
iidx = afto ? !iidx : iidx;
#ifdef INET6
if (afto) {
pf_addrcpy(&pd->nsaddr, &nk->addr[sidx],
nk->af);
pf_addrcpy(&pd->ndaddr, &nk->addr[didx],
nk->af);
pd->naf = nk->af;
}
#endif /* INET6 */
if (!afto) {
pf_translate_a(pd, pd->src, &nk->addr[sidx]);
pf_translate_a(pd, pd->dst, &nk->addr[didx]);
}
if (pd->rdomain != nk->rdomain)
pd->destchg = 1;
if (!afto && PF_ANEQ(pd->dst,
&nk->addr[didx], pd->af))
pd->destchg = 1;
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
switch (pd->af) {
case AF_INET:
#ifdef INET6
if (afto) {
if (pf_translate_icmp_af(pd, AF_INET6,
&pd->hdr.icmp))
return (PF_DROP);
pd->proto = IPPROTO_ICMPV6;
}
#endif /* INET6 */
pf_patch_16(pd,
&pd->hdr.icmp.icmp_id, nk->port[iidx]);
m_copyback(pd->m, pd->off, ICMP_MINLEN,
&pd->hdr.icmp, M_NOWAIT);
copyback = 1;
break;
#ifdef INET6
case AF_INET6:
if (afto) {
if (pf_translate_icmp_af(pd, AF_INET,
&pd->hdr.icmp6))
return (PF_DROP);
pd->proto = IPPROTO_ICMP;
}
pf_patch_16(pd,
&pd->hdr.icmp6.icmp6_id, nk->port[iidx]);
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr), &pd->hdr.icmp6,
M_NOWAIT);
copyback = 1;
break;
#endif /* INET6 */
}
#ifdef INET6
if (afto)
return (PF_AFRT);
#endif /* INET6 */
}
} else {
/*
* ICMP error message in response to a TCP/UDP packet.
* Extract the inner TCP/UDP header and search for that state.
*/
struct pf_pdesc pd2;
struct ip h2;
#ifdef INET6
struct ip6_hdr h2_6;
#endif /* INET6 */
int ipoff2;
/* Initialize pd2 fields valid for both packets with pd. */
memset(&pd2, 0, sizeof(pd2));
pd2.af = pd->af;
pd2.dir = pd->dir;
pd2.kif = pd->kif;
pd2.m = pd->m;
pd2.rdomain = pd->rdomain;
/* Payload packet is from the opposite direction. */
pd2.sidx = (pd2.dir == PF_IN) ? 1 : 0;
pd2.didx = (pd2.dir == PF_IN) ? 0 : 1;
switch (pd->af) {
case AF_INET:
/* offset of h2 in mbuf chain */
ipoff2 = pd->off + ICMP_MINLEN;
if (!pf_pull_hdr(pd2.m, ipoff2, &h2, sizeof(h2),
NULL, reason, pd2.af)) {
DPFPRINTF(LOG_NOTICE,
"ICMP error message too short (ip)");
return (PF_DROP);
}
/*
* ICMP error messages don't refer to non-first
* fragments
*/
if (h2.ip_off & htons(IP_OFFMASK)) {
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
/* offset of protocol header that follows h2 */
pd2.off = ipoff2;
if (pf_walk_header(&pd2, &h2, reason) != PF_PASS)
return (PF_DROP);
pd2.tot_len = ntohs(h2.ip_len);
pd2.src = (struct pf_addr *)&h2.ip_src;
pd2.dst = (struct pf_addr *)&h2.ip_dst;
break;
#ifdef INET6
case AF_INET6:
ipoff2 = pd->off + sizeof(struct icmp6_hdr);
if (!pf_pull_hdr(pd2.m, ipoff2, &h2_6, sizeof(h2_6),
NULL, reason, pd2.af)) {
DPFPRINTF(LOG_NOTICE,
"ICMP error message too short (ip6)");
return (PF_DROP);
}
pd2.off = ipoff2;
if (pf_walk_header6(&pd2, &h2_6, reason) != PF_PASS)
return (PF_DROP);
pd2.tot_len = ntohs(h2_6.ip6_plen) +
sizeof(struct ip6_hdr);
pd2.src = (struct pf_addr *)&h2_6.ip6_src;
pd2.dst = (struct pf_addr *)&h2_6.ip6_dst;
break;
#endif /* INET6 */
default:
unhandled_af(pd->af);
}
if (PF_ANEQ(pd->dst, pd2.src, pd->af)) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: BAD ICMP %d:%d outer dst: ",
icmptype, icmpcode);
pf_print_host(pd->src, 0, pd->af);
addlog(" -> ");
pf_print_host(pd->dst, 0, pd->af);
addlog(" inner src: ");
pf_print_host(pd2.src, 0, pd2.af);
addlog(" -> ");
pf_print_host(pd2.dst, 0, pd2.af);
addlog("\n");
}
REASON_SET(reason, PFRES_BADSTATE);
return (PF_DROP);
}
switch (pd2.proto) {
case IPPROTO_TCP: {
struct tcphdr *th = &pd2.hdr.tcp;
u_int32_t seq;
struct pf_state_peer *src, *dst;
u_int8_t dws;
int action;
/*
* Only the first 8 bytes of the TCP header can be
* expected. Don't access any TCP header fields after
* th_seq, an ackskew test is not possible.
*/
if (!pf_pull_hdr(pd2.m, pd2.off, th, 8, NULL, reason,
pd2.af)) {
DPFPRINTF(LOG_NOTICE,
"ICMP error message too short (tcp)");
return (PF_DROP);
}
key.af = pd2.af;
key.proto = IPPROTO_TCP;
key.rdomain = pd2.rdomain;
pf_addrcpy(&key.addr[pd2.sidx], pd2.src, key.af);
pf_addrcpy(&key.addr[pd2.didx], pd2.dst, key.af);
key.port[pd2.sidx] = th->th_sport;
key.port[pd2.didx] = th->th_dport;
action = pf_find_state(&pd2, &key, state);
if (action != PF_MATCH)
return (action);
if (pd2.dir == (*state)->direction) {
if (PF_REVERSED_KEY((*state)->key, pd->af)) {
src = &(*state)->src;
dst = &(*state)->dst;
} else {
src = &(*state)->dst;
dst = &(*state)->src;
}
} else {
if (PF_REVERSED_KEY((*state)->key, pd->af)) {
src = &(*state)->dst;
dst = &(*state)->src;
} else {
src = &(*state)->src;
dst = &(*state)->dst;
}
}
if (src->wscale && dst->wscale)
dws = dst->wscale & PF_WSCALE_MASK;
else
dws = 0;
/* Demodulate sequence number */
seq = ntohl(th->th_seq) - src->seqdiff;
if (src->seqdiff) {
pf_patch_32(pd, &th->th_seq, htonl(seq));
copyback = 1;
}
if (!((*state)->state_flags & PFSTATE_SLOPPY) &&
(!SEQ_GEQ(src->seqhi, seq) || !SEQ_GEQ(seq,
src->seqlo - (dst->max_win << dws)))) {
if (pf_status.debug >= LOG_NOTICE) {
log(LOG_NOTICE,
"pf: BAD ICMP %d:%d ",
icmptype, icmpcode);
pf_print_host(pd->src, 0, pd->af);
addlog(" -> ");
pf_print_host(pd->dst, 0, pd->af);
addlog(" state: ");
pf_print_state(*state);
addlog(" seq=%u\n", seq);
}
REASON_SET(reason, PFRES_BADSTATE);
return (PF_DROP);
} else {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG,
"pf: OK ICMP %d:%d ",
icmptype, icmpcode);
pf_print_host(pd->src, 0, pd->af);
addlog(" -> ");
pf_print_host(pd->dst, 0, pd->af);
addlog(" state: ");
pf_print_state(*state);
addlog(" seq=%u\n", seq);
}
}
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
int afto, sidx, didx;
if (PF_REVERSED_KEY((*state)->key, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
sidx = afto ? pd2.didx : pd2.sidx;
didx = afto ? pd2.sidx : pd2.didx;
#ifdef INET6
if (afto) {
if (pf_translate_icmp_af(pd, nk->af,
&pd->hdr.icmp))
return (PF_DROP);
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
if (pf_change_icmp_af(pd->m, ipoff2,
pd, &pd2, &nk->addr[sidx],
&nk->addr[didx], pd->af, nk->af))
return (PF_DROP);
if (nk->af == AF_INET)
pd->proto = IPPROTO_ICMP;
else
pd->proto = IPPROTO_ICMPV6;
pd->m->m_pkthdr.ph_rtableid =
nk->rdomain;
pd->destchg = 1;
pf_addrcpy(&pd->nsaddr,
&nk->addr[pd2.sidx], nk->af);
pf_addrcpy(&pd->ndaddr,
&nk->addr[pd2.didx], nk->af);
pd->naf = nk->af;
pf_patch_16(pd,
&th->th_sport, nk->port[sidx]);
pf_patch_16(pd,
&th->th_dport, nk->port[didx]);
m_copyback(pd2.m, pd2.off, 8, th,
M_NOWAIT);
return (PF_AFRT);
}
#endif /* INET6 */
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af) ||
nk->port[pd2.sidx] != th->th_sport)
pf_translate_icmp(pd, pd2.src,
&th->th_sport, pd->dst,
&nk->addr[pd2.sidx],
nk->port[pd2.sidx]);
if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
pd2.af) || pd2.rdomain != nk->rdomain)
pd->destchg = 1;
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af) ||
nk->port[pd2.didx] != th->th_dport)
pf_translate_icmp(pd, pd2.dst,
&th->th_dport, pd->src,
&nk->addr[pd2.didx],
nk->port[pd2.didx]);
copyback = 1;
}
if (copyback) {
switch (pd2.af) {
case AF_INET:
m_copyback(pd->m, pd->off, ICMP_MINLEN,
&pd->hdr.icmp, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2),
&h2, M_NOWAIT);
break;
#ifdef INET6
case AF_INET6:
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2_6),
&h2_6, M_NOWAIT);
break;
#endif /* INET6 */
}
m_copyback(pd2.m, pd2.off, 8, th, M_NOWAIT);
}
break;
}
case IPPROTO_UDP: {
struct udphdr *uh = &pd2.hdr.udp;
int action;
if (!pf_pull_hdr(pd2.m, pd2.off, uh, sizeof(*uh),
NULL, reason, pd2.af)) {
DPFPRINTF(LOG_NOTICE,
"ICMP error message too short (udp)");
return (PF_DROP);
}
key.af = pd2.af;
key.proto = IPPROTO_UDP;
key.rdomain = pd2.rdomain;
pf_addrcpy(&key.addr[pd2.sidx], pd2.src, key.af);
pf_addrcpy(&key.addr[pd2.didx], pd2.dst, key.af);
key.port[pd2.sidx] = uh->uh_sport;
key.port[pd2.didx] = uh->uh_dport;
action = pf_find_state(&pd2, &key, state);
if (action != PF_MATCH)
return (action);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
int afto, sidx, didx;
if (PF_REVERSED_KEY((*state)->key, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
sidx = afto ? pd2.didx : pd2.sidx;
didx = afto ? pd2.sidx : pd2.didx;
#ifdef INET6
if (afto) {
if (pf_translate_icmp_af(pd, nk->af,
&pd->hdr.icmp))
return (PF_DROP);
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
if (pf_change_icmp_af(pd->m, ipoff2,
pd, &pd2, &nk->addr[sidx],
&nk->addr[didx], pd->af, nk->af))
return (PF_DROP);
if (nk->af == AF_INET)
pd->proto = IPPROTO_ICMP;
else
pd->proto = IPPROTO_ICMPV6;
pd->m->m_pkthdr.ph_rtableid =
nk->rdomain;
pd->destchg = 1;
pf_addrcpy(&pd->nsaddr,
&nk->addr[pd2.sidx], nk->af);
pf_addrcpy(&pd->ndaddr,
&nk->addr[pd2.didx], nk->af);
pd->naf = nk->af;
pf_patch_16(pd,
&uh->uh_sport, nk->port[sidx]);
pf_patch_16(pd,
&uh->uh_dport, nk->port[didx]);
m_copyback(pd2.m, pd2.off, sizeof(*uh),
uh, M_NOWAIT);
return (PF_AFRT);
}
#endif /* INET6 */
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af) ||
nk->port[pd2.sidx] != uh->uh_sport)
pf_translate_icmp(pd, pd2.src,
&uh->uh_sport, pd->dst,
&nk->addr[pd2.sidx],
nk->port[pd2.sidx]);
if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
pd2.af) || pd2.rdomain != nk->rdomain)
pd->destchg = 1;
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af) ||
nk->port[pd2.didx] != uh->uh_dport)
pf_translate_icmp(pd, pd2.dst,
&uh->uh_dport, pd->src,
&nk->addr[pd2.didx],
nk->port[pd2.didx]);
switch (pd2.af) {
case AF_INET:
m_copyback(pd->m, pd->off, ICMP_MINLEN,
&pd->hdr.icmp, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2),
&h2, M_NOWAIT);
break;
#ifdef INET6
case AF_INET6:
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2_6),
&h2_6, M_NOWAIT);
break;
#endif /* INET6 */
}
/* Avoid recomputing quoted UDP checksum.
* note: udp6 0 csum invalid per rfc2460 p27.
* but presumed nothing cares in this context */
pf_patch_16(pd, &uh->uh_sum, 0);
m_copyback(pd2.m, pd2.off, sizeof(*uh), uh,
M_NOWAIT);
copyback = 1;
}
break;
}
case IPPROTO_ICMP: {
struct icmp *iih = &pd2.hdr.icmp;
if (pd2.af != AF_INET) {
REASON_SET(reason, PFRES_NORM);
return (PF_DROP);
}
if (!pf_pull_hdr(pd2.m, pd2.off, iih, ICMP_MINLEN,
NULL, reason, pd2.af)) {
DPFPRINTF(LOG_NOTICE,
"ICMP error message too short (icmp)");
return (PF_DROP);
}
pf_icmp_mapping(&pd2, iih->icmp_type,
&icmp_dir, &virtual_id, &virtual_type);
ret = pf_icmp_state_lookup(&pd2, &key, state,
virtual_id, virtual_type, icmp_dir, &iidx, 0, 1);
if (ret >= 0)
return (ret);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
int afto, sidx, didx;
if (PF_REVERSED_KEY((*state)->key, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
sidx = afto ? pd2.didx : pd2.sidx;
didx = afto ? pd2.sidx : pd2.didx;
iidx = afto ? !iidx : iidx;
#ifdef INET6
if (afto) {
if (nk->af != AF_INET6)
return (PF_DROP);
if (pf_translate_icmp_af(pd, nk->af,
&pd->hdr.icmp))
return (PF_DROP);
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
if (pf_change_icmp_af(pd->m, ipoff2,
pd, &pd2, &nk->addr[sidx],
&nk->addr[didx], pd->af, nk->af))
return (PF_DROP);
pd->proto = IPPROTO_ICMPV6;
if (pf_translate_icmp_af(pd,
nk->af, iih))
return (PF_DROP);
if (virtual_type == htons(ICMP_ECHO))
pf_patch_16(pd, &iih->icmp_id,
nk->port[iidx]);
m_copyback(pd2.m, pd2.off, ICMP_MINLEN,
iih, M_NOWAIT);
pd->m->m_pkthdr.ph_rtableid =
nk->rdomain;
pd->destchg = 1;
pf_addrcpy(&pd->nsaddr,
&nk->addr[pd2.sidx], nk->af);
pf_addrcpy(&pd->ndaddr,
&nk->addr[pd2.didx], nk->af);
pd->naf = nk->af;
return (PF_AFRT);
}
#endif /* INET6 */
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af) ||
(virtual_type == htons(ICMP_ECHO) &&
nk->port[iidx] != iih->icmp_id))
pf_translate_icmp(pd, pd2.src,
(virtual_type == htons(ICMP_ECHO)) ?
&iih->icmp_id : NULL,
pd->dst, &nk->addr[pd2.sidx],
(virtual_type == htons(ICMP_ECHO)) ?
nk->port[iidx] : 0);
if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
pd2.af) || pd2.rdomain != nk->rdomain)
pd->destchg = 1;
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af))
pf_translate_icmp(pd, pd2.dst, NULL,
pd->src, &nk->addr[pd2.didx], 0);
m_copyback(pd->m, pd->off, ICMP_MINLEN,
&pd->hdr.icmp, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2), &h2,
M_NOWAIT);
m_copyback(pd2.m, pd2.off, ICMP_MINLEN, iih,
M_NOWAIT);
copyback = 1;
}
break;
}
#ifdef INET6
case IPPROTO_ICMPV6: {
struct icmp6_hdr *iih = &pd2.hdr.icmp6;
if (pd2.af != AF_INET6) {
REASON_SET(reason, PFRES_NORM);
return (PF_DROP);
}
if (!pf_pull_hdr(pd2.m, pd2.off, iih,
sizeof(struct icmp6_hdr), NULL, reason, pd2.af)) {
DPFPRINTF(LOG_NOTICE,
"ICMP error message too short (icmp6)");
return (PF_DROP);
}
pf_icmp_mapping(&pd2, iih->icmp6_type,
&icmp_dir, &virtual_id, &virtual_type);
ret = pf_icmp_state_lookup(&pd2, &key, state,
virtual_id, virtual_type, icmp_dir, &iidx, 0, 1);
/* IPv6? try matching a multicast address */
if (ret == PF_DROP && pd2.af == AF_INET6 &&
icmp_dir == PF_OUT)
ret = pf_icmp_state_lookup(&pd2, &key, state,
virtual_id, virtual_type, icmp_dir, &iidx,
1, 1);
if (ret >= 0)
return (ret);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
int afto, sidx, didx;
if (PF_REVERSED_KEY((*state)->key, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
sidx = afto ? pd2.didx : pd2.sidx;
didx = afto ? pd2.sidx : pd2.didx;
iidx = afto ? !iidx : iidx;
if (afto) {
if (nk->af != AF_INET)
return (PF_DROP);
if (pf_translate_icmp_af(pd, nk->af,
&pd->hdr.icmp))
return (PF_DROP);
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
if (pf_change_icmp_af(pd->m, ipoff2,
pd, &pd2, &nk->addr[sidx],
&nk->addr[didx], pd->af, nk->af))
return (PF_DROP);
pd->proto = IPPROTO_ICMP;
if (pf_translate_icmp_af(pd,
nk->af, iih))
return (PF_DROP);
if (virtual_type ==
htons(ICMP6_ECHO_REQUEST))
pf_patch_16(pd, &iih->icmp6_id,
nk->port[iidx]);
m_copyback(pd2.m, pd2.off,
sizeof(struct icmp6_hdr), iih,
M_NOWAIT);
pd->m->m_pkthdr.ph_rtableid =
nk->rdomain;
pd->destchg = 1;
pf_addrcpy(&pd->nsaddr,
&nk->addr[pd2.sidx], nk->af);
pf_addrcpy(&pd->ndaddr,
&nk->addr[pd2.didx], nk->af);
pd->naf = nk->af;
return (PF_AFRT);
}
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af) ||
((virtual_type ==
htons(ICMP6_ECHO_REQUEST)) &&
nk->port[pd2.sidx] != iih->icmp6_id))
pf_translate_icmp(pd, pd2.src,
(virtual_type ==
htons(ICMP6_ECHO_REQUEST))
? &iih->icmp6_id : NULL,
pd->dst, &nk->addr[pd2.sidx],
(virtual_type ==
htons(ICMP6_ECHO_REQUEST))
? nk->port[iidx] : 0);
if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
pd2.af) || pd2.rdomain != nk->rdomain)
pd->destchg = 1;
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af))
pf_translate_icmp(pd, pd2.dst, NULL,
pd->src, &nk->addr[pd2.didx], 0);
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr), &pd->hdr.icmp6,
M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2_6), &h2_6,
M_NOWAIT);
m_copyback(pd2.m, pd2.off,
sizeof(struct icmp6_hdr), iih, M_NOWAIT);
copyback = 1;
}
break;
}
#endif /* INET6 */
default: {
int action;
key.af = pd2.af;
key.proto = pd2.proto;
key.rdomain = pd2.rdomain;
pf_addrcpy(&key.addr[pd2.sidx], pd2.src, key.af);
pf_addrcpy(&key.addr[pd2.didx], pd2.dst, key.af);
key.port[0] = key.port[1] = 0;
action = pf_find_state(&pd2, &key, state);
if (action != PF_MATCH)
return (action);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk =
(*state)->key[pd->didx];
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af))
pf_translate_icmp(pd, pd2.src, NULL,
pd->dst, &nk->addr[pd2.sidx], 0);
if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
pd2.af) || pd2.rdomain != nk->rdomain)
pd->destchg = 1;
pd->m->m_pkthdr.ph_rtableid = nk->rdomain;
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af))
pf_translate_icmp(pd, pd2.dst, NULL,
pd->src, &nk->addr[pd2.didx], 0);
switch (pd2.af) {
case AF_INET:
m_copyback(pd->m, pd->off, ICMP_MINLEN,
&pd->hdr.icmp, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2),
&h2, M_NOWAIT);
break;
#ifdef INET6
case AF_INET6:
m_copyback(pd->m, pd->off,
sizeof(struct icmp6_hdr),
&pd->hdr.icmp6, M_NOWAIT);
m_copyback(pd2.m, ipoff2, sizeof(h2_6),
&h2_6, M_NOWAIT);
break;
#endif /* INET6 */
}
copyback = 1;
}
break;
}
}
}
if (copyback) {
m_copyback(pd->m, pd->off, pd->hdrlen, &pd->hdr, M_NOWAIT);
}
return (PF_PASS);
}
/*
* ipoff and off are measured from the start of the mbuf chain.
* h must be at "ipoff" on the mbuf chain.
*/
void *
pf_pull_hdr(struct mbuf *m, int off, void *p, int len,
u_short *actionp, u_short *reasonp, sa_family_t af)
{
int iplen = 0;
switch (af) {
case AF_INET: {
struct ip *h = mtod(m, struct ip *);
u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3;
if (fragoff) {
if (fragoff >= len)
ACTION_SET(actionp, PF_PASS);
else {
ACTION_SET(actionp, PF_DROP);
REASON_SET(reasonp, PFRES_FRAG);
}
return (NULL);
}
iplen = ntohs(h->ip_len);
break;
}
#ifdef INET6
case AF_INET6: {
struct ip6_hdr *h = mtod(m, struct ip6_hdr *);
iplen = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
break;
}
#endif /* INET6 */
}
if (m->m_pkthdr.len < off + len || iplen < off + len) {
ACTION_SET(actionp, PF_DROP);
REASON_SET(reasonp, PFRES_SHORT);
return (NULL);
}
m_copydata(m, off, len, p);
return (p);
}
int
pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif,
int rtableid)
{
struct sockaddr_storage ss;
struct sockaddr_in *dst;
int ret = 1;
int check_mpath;
#ifdef INET6
struct sockaddr_in6 *dst6;
#endif /* INET6 */
struct rtentry *rt = NULL;
check_mpath = 0;
memset(&ss, 0, sizeof(ss));
switch (af) {
case AF_INET:
dst = (struct sockaddr_in *)&ss;
dst->sin_family = AF_INET;
dst->sin_len = sizeof(*dst);
dst->sin_addr = addr->v4;
if (ipmultipath)
check_mpath = 1;
break;
#ifdef INET6
case AF_INET6:
/*
* Skip check for addresses with embedded interface scope,
* as they would always match anyway.
*/
if (IN6_IS_SCOPE_EMBED(&addr->v6))
goto out;
dst6 = (struct sockaddr_in6 *)&ss;
dst6->sin6_family = AF_INET6;
dst6->sin6_len = sizeof(*dst6);
dst6->sin6_addr = addr->v6;
if (ip6_multipath)
check_mpath = 1;
break;
#endif /* INET6 */
}
/* Skip checks for ipsec interfaces */
if (kif != NULL && kif->pfik_ifp->if_type == IFT_ENC)
goto out;
rt = rtalloc(sstosa(&ss), 0, rtableid);
if (rt != NULL) {
/* No interface given, this is a no-route check */
if (kif == NULL)
goto out;
if (kif->pfik_ifp == NULL) {
ret = 0;
goto out;
}
/* Perform uRPF check if passed input interface */
ret = 0;
do {
if (rt->rt_ifidx == kif->pfik_ifp->if_index) {
ret = 1;
#if NCARP > 0
} else {
struct ifnet *ifp;
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL && ifp->if_type == IFT_CARP &&
ifp->if_carpdevidx ==
kif->pfik_ifp->if_index)
ret = 1;
if_put(ifp);
#endif /* NCARP */
}
rt = rtable_iterate(rt);
} while (check_mpath == 1 && rt != NULL && ret == 0);
} else
ret = 0;
out:
rtfree(rt);
return (ret);
}
int
pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw,
int rtableid)
{
struct sockaddr_storage ss;
struct sockaddr_in *dst;
#ifdef INET6
struct sockaddr_in6 *dst6;
#endif /* INET6 */
struct rtentry *rt;
int ret = 0;
memset(&ss, 0, sizeof(ss));
switch (af) {
case AF_INET:
dst = (struct sockaddr_in *)&ss;
dst->sin_family = AF_INET;
dst->sin_len = sizeof(*dst);
dst->sin_addr = addr->v4;
break;
#ifdef INET6
case AF_INET6:
dst6 = (struct sockaddr_in6 *)&ss;
dst6->sin6_family = AF_INET6;
dst6->sin6_len = sizeof(*dst6);
dst6->sin6_addr = addr->v6;
break;
#endif /* INET6 */
}
rt = rtalloc(sstosa(&ss), RT_RESOLVE, rtableid);
if (rt != NULL) {
if (rt->rt_labelid == aw->v.rtlabel)
ret = 1;
rtfree(rt);
}
return (ret);
}
/* pf_route() may change pd->m, adjust local copies after calling */
void
pf_route(struct pf_pdesc *pd, struct pf_state *s)
{
struct mbuf *m0;
struct mbuf_list fml;
struct sockaddr_in *dst, sin;
struct rtentry *rt = NULL;
struct ip *ip;
struct ifnet *ifp = NULL;
int error = 0;
unsigned int rtableid;
if (pd->m->m_pkthdr.pf.routed++ > 3) {
m_freem(pd->m);
pd->m = NULL;
return;
}
if (s->rt == PF_DUPTO) {
if ((m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT)) == NULL)
return;
} else {
if ((s->rt == PF_REPLYTO) == (s->direction == pd->dir))
return;
m0 = pd->m;
pd->m = NULL;
}
if (m0->m_len < sizeof(struct ip)) {
DPFPRINTF(LOG_ERR,
"%s: m0->m_len < sizeof(struct ip)", __func__);
goto bad;
}
ip = mtod(m0, struct ip *);
if (pd->dir == PF_IN) {
if (ip->ip_ttl <= IPTTLDEC) {
if (s->rt != PF_DUPTO) {
pf_send_icmp(m0, ICMP_TIMXCEED,
ICMP_TIMXCEED_INTRANS, 0,
pd->af, s->rule.ptr, pd->rdomain);
}
goto bad;
}
ip->ip_ttl -= IPTTLDEC;
}
memset(&sin, 0, sizeof(sin));
dst = &sin;
dst->sin_family = AF_INET;
dst->sin_len = sizeof(*dst);
dst->sin_addr = s->rt_addr.v4;
rtableid = m0->m_pkthdr.ph_rtableid;
rt = rtalloc_mpath(sintosa(dst), &ip->ip_src.s_addr, rtableid);
if (!rtisvalid(rt)) {
if (s->rt != PF_DUPTO) {
pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_HOST,
0, pd->af, s->rule.ptr, pd->rdomain);
}
ipstat_inc(ips_noroute);
goto bad;
}
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
goto bad;
/* A locally generated packet may have invalid source address. */
if ((ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET &&
(ifp->if_flags & IFF_LOOPBACK) == 0)
ip->ip_src = ifatoia(rt->rt_ifa)->ia_addr.sin_addr;
if (s->rt != PF_DUPTO && pd->dir == PF_IN) {
if (pf_test(AF_INET, PF_OUT, ifp, &m0) != PF_PASS)
goto bad;
else if (m0 == NULL)
goto done;
if (m0->m_len < sizeof(struct ip)) {
DPFPRINTF(LOG_ERR,
"%s: m0->m_len < sizeof(struct ip)", __func__);
goto bad;
}
ip = mtod(m0, struct ip *);
}
in_proto_cksum_out(m0, ifp);
if (ntohs(ip->ip_len) <= ifp->if_mtu) {
ip->ip_sum = 0;
if (ifp->if_capabilities & IFCAP_CSUM_IPv4)
m0->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
else {
ipstat_inc(ips_outswcsum);
ip->ip_sum = in_cksum(m0, ip->ip_hl << 2);
}
error = ifp->if_output(ifp, m0, sintosa(dst), rt);
goto done;
}
/*
* Too large for interface; fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
if (ip->ip_off & htons(IP_DF)) {
ipstat_inc(ips_cantfrag);
if (s->rt != PF_DUPTO)
pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
ifp->if_mtu, pd->af, s->rule.ptr, pd->rdomain);
goto bad;
}
error = ip_fragment(m0, &fml, ifp, ifp->if_mtu);
if (error)
goto done;
while ((m0 = ml_dequeue(&fml)) != NULL) {
error = ifp->if_output(ifp, m0, sintosa(dst), rt);
if (error)
break;
}
if (error)
ml_purge(&fml);
else
ipstat_inc(ips_fragmented);
done:
if_put(ifp);
rtfree(rt);
return;
bad:
m_freem(m0);
goto done;
}
#ifdef INET6
/* pf_route6() may change pd->m, adjust local copies after calling */
void
pf_route6(struct pf_pdesc *pd, struct pf_state *s)
{
struct mbuf *m0;
struct sockaddr_in6 *dst, sin6;
struct rtentry *rt = NULL;
struct ip6_hdr *ip6;
struct ifnet *ifp = NULL;
struct m_tag *mtag;
unsigned int rtableid;
if (pd->m->m_pkthdr.pf.routed++ > 3) {
m_freem(pd->m);
pd->m = NULL;
return;
}
if (s->rt == PF_DUPTO) {
if ((m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT)) == NULL)
return;
} else {
if ((s->rt == PF_REPLYTO) == (s->direction == pd->dir))
return;
m0 = pd->m;
pd->m = NULL;
}
if (m0->m_len < sizeof(struct ip6_hdr)) {
DPFPRINTF(LOG_ERR,
"%s: m0->m_len < sizeof(struct ip6_hdr)", __func__);
goto bad;
}
ip6 = mtod(m0, struct ip6_hdr *);
if (pd->dir == PF_IN) {
if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
if (s->rt != PF_DUPTO) {
pf_send_icmp(m0, ICMP6_TIME_EXCEEDED,
ICMP6_TIME_EXCEED_TRANSIT, 0,
pd->af, s->rule.ptr, pd->rdomain);
}
goto bad;
}
ip6->ip6_hlim -= IPV6_HLIMDEC;
}
memset(&sin6, 0, sizeof(sin6));
dst = &sin6;
dst->sin6_family = AF_INET6;
dst->sin6_len = sizeof(*dst);
dst->sin6_addr = s->rt_addr.v6;
rtableid = m0->m_pkthdr.ph_rtableid;
rt = rtalloc_mpath(sin6tosa(dst), &ip6->ip6_src.s6_addr32[0],
rtableid);
if (!rtisvalid(rt)) {
if (s->rt != PF_DUPTO) {
pf_send_icmp(m0, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_NOROUTE, 0,
pd->af, s->rule.ptr, pd->rdomain);
}
ip6stat_inc(ip6s_noroute);
goto bad;
}
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
goto bad;
/* A locally generated packet may have invalid source address. */
if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) &&
(ifp->if_flags & IFF_LOOPBACK) == 0)
ip6->ip6_src = ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr;
if (s->rt != PF_DUPTO && pd->dir == PF_IN) {
if (pf_test(AF_INET6, PF_OUT, ifp, &m0) != PF_PASS)
goto bad;
else if (m0 == NULL)
goto done;
if (m0->m_len < sizeof(struct ip6_hdr)) {
DPFPRINTF(LOG_ERR,
"%s: m0->m_len < sizeof(struct ip6_hdr)", __func__);
goto bad;
}
}
in6_proto_cksum_out(m0, ifp);
/*
* If packet has been reassembled by PF earlier, we have to
* use pf_refragment6() here to turn it back to fragments.
*/
if ((mtag = m_tag_find(m0, PACKET_TAG_PF_REASSEMBLED, NULL))) {
(void) pf_refragment6(&m0, mtag, dst, ifp, rt);
} else if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) {
ifp->if_output(ifp, m0, sin6tosa(dst), rt);
} else {
ip6stat_inc(ip6s_cantfrag);
if (s->rt != PF_DUPTO)
pf_send_icmp(m0, ICMP6_PACKET_TOO_BIG, 0,
ifp->if_mtu, pd->af, s->rule.ptr, pd->rdomain);
goto bad;
}
done:
if_put(ifp);
rtfree(rt);
return;
bad:
m_freem(m0);
goto done;
}
#endif /* INET6 */
/*
* check TCP checksum and set mbuf flag
* off is the offset where the protocol header starts
* len is the total length of protocol header plus payload
* returns 0 when the checksum is valid, otherwise returns 1.
* if the _OUT flag is set the checksum isn't done yet, consider these ok
*/
int
pf_check_tcp_cksum(struct mbuf *m, int off, int len, sa_family_t af)
{
u_int16_t sum;
if (m->m_pkthdr.csum_flags &
(M_TCP_CSUM_IN_OK | M_TCP_CSUM_OUT)) {
return (0);
}
if (m->m_pkthdr.csum_flags & M_TCP_CSUM_IN_BAD ||
off < sizeof(struct ip) ||
m->m_pkthdr.len < off + len) {
return (1);
}
/* need to do it in software */
tcpstat_inc(tcps_inswcsum);
switch (af) {
case AF_INET:
if (m->m_len < sizeof(struct ip))
return (1);
sum = in4_cksum(m, IPPROTO_TCP, off, len);
break;
#ifdef INET6
case AF_INET6:
if (m->m_len < sizeof(struct ip6_hdr))
return (1);
sum = in6_cksum(m, IPPROTO_TCP, off, len);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
if (sum) {
tcpstat_inc(tcps_rcvbadsum);
m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_BAD;
return (1);
}
m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK;
return (0);
}
struct pf_divert *
pf_find_divert(struct mbuf *m)
{
struct m_tag *mtag;
if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL)
return (NULL);
return ((struct pf_divert *)(mtag + 1));
}
struct pf_divert *
pf_get_divert(struct mbuf *m)
{
struct m_tag *mtag;
if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) {
mtag = m_tag_get(PACKET_TAG_PF_DIVERT, sizeof(struct pf_divert),
M_NOWAIT);
if (mtag == NULL)
return (NULL);
memset(mtag + 1, 0, sizeof(struct pf_divert));
m_tag_prepend(m, mtag);
}
return ((struct pf_divert *)(mtag + 1));
}
int
pf_walk_option(struct pf_pdesc *pd, struct ip *h, int off, int end,
u_short *reason)
{
uint8_t type, length, opts[15 * 4 - sizeof(struct ip)];
/* IP header in payload of ICMP packet may be too short */
if (pd->m->m_pkthdr.len < end) {
DPFPRINTF(LOG_NOTICE, "IP option too short");
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
KASSERT(end - off <= sizeof(opts));
m_copydata(pd->m, off, end - off, opts);
end -= off;
off = 0;
while (off < end) {
type = opts[off];
if (type == IPOPT_EOL)
break;
if (type == IPOPT_NOP) {
off++;
continue;
}
if (off + 2 > end) {
DPFPRINTF(LOG_NOTICE, "IP length opt");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
length = opts[off + 1];
if (length < 2) {
DPFPRINTF(LOG_NOTICE, "IP short opt");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (off + length > end) {
DPFPRINTF(LOG_NOTICE, "IP long opt");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
switch (type) {
case IPOPT_RA:
SET(pd->badopts, PF_OPT_ROUTER_ALERT);
break;
default:
SET(pd->badopts, PF_OPT_OTHER);
break;
}
off += length;
}
return (PF_PASS);
}
int
pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason)
{
struct ip6_ext ext;
u_int32_t hlen, end;
int hdr_cnt;
hlen = h->ip_hl << 2;
if (hlen < sizeof(struct ip) || hlen > ntohs(h->ip_len)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
if (hlen != sizeof(struct ip)) {
if (pf_walk_option(pd, h, pd->off + sizeof(struct ip),
pd->off + hlen, reason) != PF_PASS)
return (PF_DROP);
/* header options which contain only padding is fishy */
if (pd->badopts == 0)
SET(pd->badopts, PF_OPT_OTHER);
}
end = pd->off + ntohs(h->ip_len);
pd->off += hlen;
pd->proto = h->ip_p;
/* IGMP packets have router alert options, allow them */
if (pd->proto == IPPROTO_IGMP) {
/* According to RFC 1112 ttl must be set to 1. */
if ((h->ip_ttl != 1) || !IN_MULTICAST(h->ip_dst.s_addr)) {
DPFPRINTF(LOG_NOTICE, "Invalid IGMP");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
CLR(pd->badopts, PF_OPT_ROUTER_ALERT);
}
/* stop walking over non initial fragments */
if ((h->ip_off & htons(IP_OFFMASK)) != 0)
return (PF_PASS);
for (hdr_cnt = 0; hdr_cnt < pf_hdr_limit; hdr_cnt++) {
switch (pd->proto) {
case IPPROTO_AH:
/* fragments may be short */
if ((h->ip_off & htons(IP_MF | IP_OFFMASK)) != 0 &&
end < pd->off + sizeof(ext))
return (PF_PASS);
if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
NULL, reason, AF_INET)) {
DPFPRINTF(LOG_NOTICE, "IP short exthdr");
return (PF_DROP);
}
pd->off += (ext.ip6e_len + 2) * 4;
pd->proto = ext.ip6e_nxt;
break;
default:
return (PF_PASS);
}
}
DPFPRINTF(LOG_NOTICE, "IPv4 nested authentication header limit");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
#ifdef INET6
int
pf_walk_option6(struct pf_pdesc *pd, struct ip6_hdr *h, int off, int end,
u_short *reason)
{
struct ip6_opt opt;
struct ip6_opt_jumbo jumbo;
while (off < end) {
if (!pf_pull_hdr(pd->m, off, &opt.ip6o_type,
sizeof(opt.ip6o_type), NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short opt type");
return (PF_DROP);
}
if (opt.ip6o_type == IP6OPT_PAD1) {
off++;
continue;
}
if (!pf_pull_hdr(pd->m, off, &opt, sizeof(opt),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short opt");
return (PF_DROP);
}
if (off + sizeof(opt) + opt.ip6o_len > end) {
DPFPRINTF(LOG_NOTICE, "IPv6 long opt");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
switch (opt.ip6o_type) {
case IP6OPT_PADN:
break;
case IP6OPT_JUMBO:
SET(pd->badopts, PF_OPT_JUMBO);
if (pd->jumbolen != 0) {
DPFPRINTF(LOG_NOTICE, "IPv6 multiple jumbo");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (ntohs(h->ip6_plen) != 0) {
DPFPRINTF(LOG_NOTICE, "IPv6 bad jumbo plen");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (!pf_pull_hdr(pd->m, off, &jumbo, sizeof(jumbo),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short jumbo");
return (PF_DROP);
}
memcpy(&pd->jumbolen, jumbo.ip6oj_jumbo_len,
sizeof(pd->jumbolen));
pd->jumbolen = ntohl(pd->jumbolen);
if (pd->jumbolen < IPV6_MAXPACKET) {
DPFPRINTF(LOG_NOTICE, "IPv6 short jumbolen");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
break;
case IP6OPT_ROUTER_ALERT:
SET(pd->badopts, PF_OPT_ROUTER_ALERT);
break;
default:
SET(pd->badopts, PF_OPT_OTHER);
break;
}
off += sizeof(opt) + opt.ip6o_len;
}
return (PF_PASS);
}
int
pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
{
struct ip6_frag frag;
struct ip6_ext ext;
struct icmp6_hdr icmp6;
struct ip6_rthdr rthdr;
u_int32_t end;
int hdr_cnt, fraghdr_cnt = 0, rthdr_cnt = 0;
pd->off += sizeof(struct ip6_hdr);
end = pd->off + ntohs(h->ip6_plen);
pd->fragoff = pd->extoff = pd->jumbolen = 0;
pd->proto = h->ip6_nxt;
for (hdr_cnt = 0; hdr_cnt < pf_hdr_limit; hdr_cnt++) {
switch (pd->proto) {
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
SET(pd->badopts, PF_OPT_OTHER);
break;
case IPPROTO_HOPOPTS:
if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short exthdr");
return (PF_DROP);
}
if (pf_walk_option6(pd, h, pd->off + sizeof(ext),
pd->off + (ext.ip6e_len + 1) * 8, reason)
!= PF_PASS)
return (PF_DROP);
/* option header which contains only padding is fishy */
if (pd->badopts == 0)
SET(pd->badopts, PF_OPT_OTHER);
break;
}
switch (pd->proto) {
case IPPROTO_FRAGMENT:
if (fraghdr_cnt++) {
DPFPRINTF(LOG_NOTICE, "IPv6 multiple fragment");
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
/* jumbo payload packets cannot be fragmented */
if (pd->jumbolen != 0) {
DPFPRINTF(LOG_NOTICE, "IPv6 fragmented jumbo");
REASON_SET(reason, PFRES_FRAG);
return (PF_DROP);
}
if (!pf_pull_hdr(pd->m, pd->off, &frag, sizeof(frag),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short fragment");
return (PF_DROP);
}
/* stop walking over non initial fragments */
if (ntohs((frag.ip6f_offlg & IP6F_OFF_MASK)) != 0) {
pd->fragoff = pd->off;
return (PF_PASS);
}
/* RFC6946: reassemble only non atomic fragments */
if (frag.ip6f_offlg & IP6F_MORE_FRAG)
pd->fragoff = pd->off;
pd->off += sizeof(frag);
pd->proto = frag.ip6f_nxt;
break;
case IPPROTO_ROUTING:
if (rthdr_cnt++) {
DPFPRINTF(LOG_NOTICE, "IPv6 multiple rthdr");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
/* fragments may be short */
if (pd->fragoff != 0 && end < pd->off + sizeof(rthdr)) {
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
return (PF_PASS);
}
if (!pf_pull_hdr(pd->m, pd->off, &rthdr, sizeof(rthdr),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short rthdr");
return (PF_DROP);
}
if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) {
DPFPRINTF(LOG_NOTICE, "IPv6 rthdr0");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
/* FALLTHROUGH */
case IPPROTO_HOPOPTS:
/* RFC2460 4.1: Hop-by-Hop only after IPv6 header */
if (pd->proto == IPPROTO_HOPOPTS && hdr_cnt > 0) {
DPFPRINTF(LOG_NOTICE, "IPv6 hopopts not first");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
/* FALLTHROUGH */
case IPPROTO_AH:
case IPPROTO_DSTOPTS:
/* fragments may be short */
if (pd->fragoff != 0 && end < pd->off + sizeof(ext)) {
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
return (PF_PASS);
}
if (!pf_pull_hdr(pd->m, pd->off, &ext, sizeof(ext),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short exthdr");
return (PF_DROP);
}
/* reassembly needs the ext header before the frag */
if (pd->fragoff == 0)
pd->extoff = pd->off;
if (pd->proto == IPPROTO_HOPOPTS && pd->fragoff == 0 &&
ntohs(h->ip6_plen) == 0 && pd->jumbolen != 0) {
DPFPRINTF(LOG_NOTICE, "IPv6 missing jumbo");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
if (pd->proto == IPPROTO_AH)
pd->off += (ext.ip6e_len + 2) * 4;
else
pd->off += (ext.ip6e_len + 1) * 8;
pd->proto = ext.ip6e_nxt;
break;
case IPPROTO_ICMPV6:
/* fragments may be short, ignore inner header then */
if (pd->fragoff != 0 && end < pd->off + sizeof(icmp6)) {
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
return (PF_PASS);
}
if (!pf_pull_hdr(pd->m, pd->off, &icmp6, sizeof(icmp6),
NULL, reason, AF_INET6)) {
DPFPRINTF(LOG_NOTICE, "IPv6 short icmp6hdr");
return (PF_DROP);
}
/* ICMP multicast packets have router alert options */
switch (icmp6.icmp6_type) {
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
case MLD_LISTENER_DONE:
case MLDV2_LISTENER_REPORT:
/*
* According to RFC 2710 all MLD messages are
* sent with hop-limit (ttl) set to 1, and link
* local source address. If either one is
* missing then MLD message is invalid and
* should be discarded.
*/
if ((h->ip6_hlim != 1) ||
!IN6_IS_ADDR_LINKLOCAL(&h->ip6_src)) {
DPFPRINTF(LOG_NOTICE, "Invalid MLD");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
CLR(pd->badopts, PF_OPT_ROUTER_ALERT);
break;
}
return (PF_PASS);
case IPPROTO_TCP:
case IPPROTO_UDP:
/* fragments may be short, ignore inner header then */
if (pd->fragoff != 0 && end < pd->off +
(pd->proto == IPPROTO_TCP ? sizeof(struct tcphdr) :
pd->proto == IPPROTO_UDP ? sizeof(struct udphdr) :
sizeof(struct icmp6_hdr))) {
pd->off = pd->fragoff;
pd->proto = IPPROTO_FRAGMENT;
}
/* FALLTHROUGH */
default:
return (PF_PASS);
}
}
DPFPRINTF(LOG_NOTICE, "IPv6 nested extension header limit");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
}
#endif /* INET6 */
int
pf_setup_pdesc(struct pf_pdesc *pd, sa_family_t af, int dir,
struct pfi_kif *kif, struct mbuf *m, u_short *reason)
{
memset(pd, 0, sizeof(*pd));
pd->dir = dir;
pd->kif = kif; /* kif is NULL when called by pflog */
pd->m = m;
pd->sidx = (dir == PF_IN) ? 0 : 1;
pd->didx = (dir == PF_IN) ? 1 : 0;
pd->af = pd->naf = af;
pd->rdomain = rtable_l2(pd->m->m_pkthdr.ph_rtableid);
switch (pd->af) {
case AF_INET: {
struct ip *h;
/* Check for illegal packets */
if (pd->m->m_pkthdr.len < (int)sizeof(struct ip)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
h = mtod(pd->m, struct ip *);
if (pd->m->m_pkthdr.len < ntohs(h->ip_len)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
if (pf_walk_header(pd, h, reason) != PF_PASS)
return (PF_DROP);
pd->src = (struct pf_addr *)&h->ip_src;
pd->dst = (struct pf_addr *)&h->ip_dst;
pd->tot_len = ntohs(h->ip_len);
pd->tos = h->ip_tos & ~IPTOS_ECN_MASK;
pd->ttl = h->ip_ttl;
pd->virtual_proto = (h->ip_off & htons(IP_MF | IP_OFFMASK)) ?
PF_VPROTO_FRAGMENT : pd->proto;
break;
}
#ifdef INET6
case AF_INET6: {
struct ip6_hdr *h;
/* Check for illegal packets */
if (pd->m->m_pkthdr.len < (int)sizeof(struct ip6_hdr)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
h = mtod(pd->m, struct ip6_hdr *);
if (pd->m->m_pkthdr.len <
sizeof(struct ip6_hdr) + ntohs(h->ip6_plen)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
if (pf_walk_header6(pd, h, reason) != PF_PASS)
return (PF_DROP);
#if 1
/*
* we do not support jumbogram yet. if we keep going, zero
* ip6_plen will do something bad, so drop the packet for now.
*/
if (pd->jumbolen != 0) {
REASON_SET(reason, PFRES_NORM);
return (PF_DROP);
}
#endif /* 1 */
pd->src = (struct pf_addr *)&h->ip6_src;
pd->dst = (struct pf_addr *)&h->ip6_dst;
pd->tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
pd->tos = (ntohl(h->ip6_flow) & 0x0fc00000) >> 20;
pd->ttl = h->ip6_hlim;
pd->virtual_proto = (pd->fragoff != 0) ?
PF_VPROTO_FRAGMENT : pd->proto;
break;
}
#endif /* INET6 */
default:
panic("pf_setup_pdesc called with illegal af %u", pd->af);
}
pf_addrcpy(&pd->nsaddr, pd->src, pd->af);
pf_addrcpy(&pd->ndaddr, pd->dst, pd->af);
switch (pd->virtual_proto) {
case IPPROTO_TCP: {
struct tcphdr *th = &pd->hdr.tcp;
if (!pf_pull_hdr(pd->m, pd->off, th, sizeof(*th),
NULL, reason, pd->af))
return (PF_DROP);
pd->hdrlen = sizeof(*th);
if (pd->off + (th->th_off << 2) > pd->tot_len ||
(th->th_off << 2) < sizeof(struct tcphdr)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
pd->p_len = pd->tot_len - pd->off - (th->th_off << 2);
pd->sport = &th->th_sport;
pd->dport = &th->th_dport;
pd->pcksum = &th->th_sum;
break;
}
case IPPROTO_UDP: {
struct udphdr *uh = &pd->hdr.udp;
if (!pf_pull_hdr(pd->m, pd->off, uh, sizeof(*uh),
NULL, reason, pd->af))
return (PF_DROP);
pd->hdrlen = sizeof(*uh);
if (uh->uh_dport == 0 ||
pd->off + ntohs(uh->uh_ulen) > pd->tot_len ||
ntohs(uh->uh_ulen) < sizeof(struct udphdr)) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
pd->sport = &uh->uh_sport;
pd->dport = &uh->uh_dport;
pd->pcksum = &uh->uh_sum;
break;
}
case IPPROTO_ICMP: {
if (!pf_pull_hdr(pd->m, pd->off, &pd->hdr.icmp, ICMP_MINLEN,
NULL, reason, pd->af))
return (PF_DROP);
pd->hdrlen = ICMP_MINLEN;
if (pd->off + pd->hdrlen > pd->tot_len) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
pd->pcksum = &pd->hdr.icmp.icmp_cksum;
break;
}
#ifdef INET6
case IPPROTO_ICMPV6: {
size_t icmp_hlen = sizeof(struct icmp6_hdr);
if (!pf_pull_hdr(pd->m, pd->off, &pd->hdr.icmp6, icmp_hlen,
NULL, reason, pd->af))
return (PF_DROP);
/* ICMP headers we look further into to match state */
switch (pd->hdr.icmp6.icmp6_type) {
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
icmp_hlen = sizeof(struct mld_hdr);
break;
case ND_NEIGHBOR_SOLICIT:
case ND_NEIGHBOR_ADVERT:
icmp_hlen = sizeof(struct nd_neighbor_solicit);
/* FALLTHROUGH */
case ND_ROUTER_SOLICIT:
case ND_ROUTER_ADVERT:
case ND_REDIRECT:
if (pd->ttl != 255) {
REASON_SET(reason, PFRES_NORM);
return (PF_DROP);
}
break;
}
if (icmp_hlen > sizeof(struct icmp6_hdr) &&
!pf_pull_hdr(pd->m, pd->off, &pd->hdr.icmp6, icmp_hlen,
NULL, reason, pd->af))
return (PF_DROP);
pd->hdrlen = icmp_hlen;
if (pd->off + pd->hdrlen > pd->tot_len) {
REASON_SET(reason, PFRES_SHORT);
return (PF_DROP);
}
pd->pcksum = &pd->hdr.icmp6.icmp6_cksum;
break;
}
#endif /* INET6 */
}
if (pd->sport)
pd->osport = pd->nsport = *pd->sport;
if (pd->dport)
pd->odport = pd->ndport = *pd->dport;
return (PF_PASS);
}
void
pf_counters_inc(int action, struct pf_pdesc *pd, struct pf_state *s,
struct pf_rule *r, struct pf_rule *a)
{
int dirndx;
pd->kif->pfik_bytes[pd->af == AF_INET6][pd->dir == PF_OUT]
[action != PF_PASS] += pd->tot_len;
pd->kif->pfik_packets[pd->af == AF_INET6][pd->dir == PF_OUT]
[action != PF_PASS]++;
if (action == PF_PASS || action == PF_AFRT || r->action == PF_DROP) {
dirndx = (pd->dir == PF_OUT);
r->packets[dirndx]++;
r->bytes[dirndx] += pd->tot_len;
if (a != NULL) {
a->packets[dirndx]++;
a->bytes[dirndx] += pd->tot_len;
}
if (s != NULL) {
struct pf_rule_item *ri;
struct pf_sn_item *sni;
SLIST_FOREACH(sni, &s->src_nodes, next) {
sni->sn->packets[dirndx]++;
sni->sn->bytes[dirndx] += pd->tot_len;
}
dirndx = (pd->dir == s->direction) ? 0 : 1;
s->packets[dirndx]++;
s->bytes[dirndx] += pd->tot_len;
SLIST_FOREACH(ri, &s->match_rules, entry) {
ri->r->packets[dirndx]++;
ri->r->bytes[dirndx] += pd->tot_len;
if (ri->r->src.addr.type == PF_ADDR_TABLE)
pfr_update_stats(ri->r->src.addr.p.tbl,
&s->key[(s->direction == PF_IN)]->
addr[(s->direction == PF_OUT)],
pd, ri->r->action, ri->r->src.neg);
if (ri->r->dst.addr.type == PF_ADDR_TABLE)
pfr_update_stats(ri->r->dst.addr.p.tbl,
&s->key[(s->direction == PF_IN)]->
addr[(s->direction == PF_IN)],
pd, ri->r->action, ri->r->dst.neg);
}
}
if (r->src.addr.type == PF_ADDR_TABLE)
pfr_update_stats(r->src.addr.p.tbl,
(s == NULL) ? pd->src :
&s->key[(s->direction == PF_IN)]->
addr[(s->direction == PF_OUT)],
pd, r->action, r->src.neg);
if (r->dst.addr.type == PF_ADDR_TABLE)
pfr_update_stats(r->dst.addr.p.tbl,
(s == NULL) ? pd->dst :
&s->key[(s->direction == PF_IN)]->
addr[(s->direction == PF_IN)],
pd, r->action, r->dst.neg);
}
}
int
pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0)
{
#if NCARP > 0
struct ifnet *ifp0;
#endif
struct pfi_kif *kif;
u_short action, reason = 0;
struct pf_rule *a = NULL, *r = &pf_default_rule;
struct pf_state *s = NULL;
struct pf_state_key_cmp key;
struct pf_ruleset *ruleset = NULL;
struct pf_pdesc pd;
int dir = (fwdir == PF_FWD) ? PF_OUT : fwdir;
u_int32_t qid, pqid = 0;
int have_pf_lock = 0;
struct pfsync_deferral *deferral = NULL;
if (!pf_status.running)
return (PF_PASS);
#if NCARP > 0
if (ifp->if_type == IFT_CARP &&
(ifp0 = if_get(ifp->if_carpdevidx)) != NULL) {
kif = (struct pfi_kif *)ifp0->if_pf_kif;
if_put(ifp0);
} else
#endif /* NCARP */
kif = (struct pfi_kif *)ifp->if_pf_kif;
if (kif == NULL) {
DPFPRINTF(LOG_ERR,
"%s: kif == NULL, if_xname %s", __func__, ifp->if_xname);
return (PF_DROP);
}
if (kif->pfik_flags & PFI_IFLAG_SKIP)
return (PF_PASS);
#ifdef DIAGNOSTIC
if (((*m0)->m_flags & M_PKTHDR) == 0)
panic("non-M_PKTHDR is passed to pf_test");
#endif /* DIAGNOSTIC */
if ((*m0)->m_pkthdr.pf.flags & PF_TAG_GENERATED)
return (PF_PASS);
if ((*m0)->m_pkthdr.pf.flags & PF_TAG_DIVERTED_PACKET) {
(*m0)->m_pkthdr.pf.flags &= ~PF_TAG_DIVERTED_PACKET;
return (PF_PASS);
}
if ((*m0)->m_pkthdr.pf.flags & PF_TAG_REFRAGMENTED) {
(*m0)->m_pkthdr.pf.flags &= ~PF_TAG_REFRAGMENTED;
return (PF_PASS);
}
action = pf_setup_pdesc(&pd, af, dir, kif, *m0, &reason);
if (action != PF_PASS) {
#if NPFLOG > 0
pd.pflog |= PF_LOG_FORCE;
#endif /* NPFLOG > 0 */
goto done;
}
/* packet normalization and reassembly */
switch (pd.af) {
case AF_INET:
action = pf_normalize_ip(&pd, &reason);
break;
#ifdef INET6
case AF_INET6:
action = pf_normalize_ip6(&pd, &reason);
break;
#endif /* INET6 */
}
*m0 = pd.m;
/* if packet sits in reassembly queue, return without error */
if (pd.m == NULL)
return PF_PASS;
if (action != PF_PASS) {
#if NPFLOG > 0
pd.pflog |= PF_LOG_FORCE;
#endif /* NPFLOG > 0 */
goto done;
}
/* if packet has been reassembled, update packet description */
if (pf_status.reass && pd.virtual_proto == PF_VPROTO_FRAGMENT) {
action = pf_setup_pdesc(&pd, af, dir, kif, pd.m, &reason);
if (action != PF_PASS) {
#if NPFLOG > 0
pd.pflog |= PF_LOG_FORCE;
#endif /* NPFLOG > 0 */
goto done;
}
}
pd.m->m_pkthdr.pf.flags |= PF_TAG_PROCESSED;
/*
* Avoid pcb-lookups from the forwarding path. They should never
* match and would cause MP locking problems.
*/
if (fwdir == PF_FWD) {
pd.lookup.done = -1;
pd.lookup.uid = -1;
pd.lookup.gid = -1;
pd.lookup.pid = NO_PID;
}
switch (pd.virtual_proto) {
case PF_VPROTO_FRAGMENT: {
/*
* handle fragments that aren't reassembled by
* normalization
*/
PF_LOCK();
have_pf_lock = 1;
action = pf_test_rule(&pd, &r, &s, &a, &ruleset, &reason,
&deferral);
s = pf_state_ref(s);
if (action != PF_PASS)
REASON_SET(&reason, PFRES_FRAG);
break;
}
case IPPROTO_ICMP: {
if (pd.af != AF_INET) {
action = PF_DROP;
REASON_SET(&reason, PFRES_NORM);
DPFPRINTF(LOG_NOTICE,
"dropping IPv6 packet with ICMPv4 payload");
break;
}
PF_STATE_ENTER_READ();
action = pf_test_state_icmp(&pd, &s, &reason);
s = pf_state_ref(s);
PF_STATE_EXIT_READ();
if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC > 0 */
r = s->rule.ptr;
a = s->anchor.ptr;
#if NPFLOG > 0
pd.pflog |= s->log;
#endif /* NPFLOG > 0 */
} else if (s == NULL) {
PF_LOCK();
have_pf_lock = 1;
action = pf_test_rule(&pd, &r, &s, &a, &ruleset,
&reason, &deferral);
s = pf_state_ref(s);
}
break;
}
#ifdef INET6
case IPPROTO_ICMPV6: {
if (pd.af != AF_INET6) {
action = PF_DROP;
REASON_SET(&reason, PFRES_NORM);
DPFPRINTF(LOG_NOTICE,
"dropping IPv4 packet with ICMPv6 payload");
break;
}
PF_STATE_ENTER_READ();
action = pf_test_state_icmp(&pd, &s, &reason);
s = pf_state_ref(s);
PF_STATE_EXIT_READ();
if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC > 0 */
r = s->rule.ptr;
a = s->anchor.ptr;
#if NPFLOG > 0
pd.pflog |= s->log;
#endif /* NPFLOG > 0 */
} else if (s == NULL) {
PF_LOCK();
have_pf_lock = 1;
action = pf_test_rule(&pd, &r, &s, &a, &ruleset,
&reason, &deferral);
s = pf_state_ref(s);
}
break;
}
#endif /* INET6 */
default:
if (pd.virtual_proto == IPPROTO_TCP) {
if (pd.dir == PF_IN && (pd.hdr.tcp.th_flags &
(TH_SYN|TH_ACK)) == TH_SYN &&
pf_synflood_check(&pd)) {
PF_LOCK();
have_pf_lock = 1;
pf_syncookie_send(&pd);
action = PF_DROP;
break;
}
if ((pd.hdr.tcp.th_flags & TH_ACK) && pd.p_len == 0)
pqid = 1;
action = pf_normalize_tcp(&pd);
if (action == PF_DROP)
break;
}
key.af = pd.af;
key.proto = pd.virtual_proto;
key.rdomain = pd.rdomain;
pf_addrcpy(&key.addr[pd.sidx], pd.src, key.af);
pf_addrcpy(&key.addr[pd.didx], pd.dst, key.af);
key.port[pd.sidx] = pd.osport;
key.port[pd.didx] = pd.odport;
PF_STATE_ENTER_READ();
action = pf_find_state(&pd, &key, &s);
s = pf_state_ref(s);
PF_STATE_EXIT_READ();
/* check for syncookies if tcp ack and no active state */
if (pd.dir == PF_IN && pd.virtual_proto == IPPROTO_TCP &&
(s == NULL || (s->src.state >= TCPS_FIN_WAIT_2 &&
s->dst.state >= TCPS_FIN_WAIT_2)) &&
(pd.hdr.tcp.th_flags & (TH_SYN|TH_ACK|TH_RST)) == TH_ACK &&
pf_syncookie_validate(&pd)) {
struct mbuf *msyn = pf_syncookie_recreate_syn(&pd);
if (msyn) {
action = pf_test(af, fwdir, ifp, &msyn);
m_freem(msyn);
if (action == PF_PASS || action == PF_AFRT) {
PF_STATE_ENTER_READ();
pf_state_unref(s);
action = pf_find_state(&pd, &key, &s);
s = pf_state_ref(s);
PF_STATE_EXIT_READ();
if (s == NULL)
return (PF_DROP);
s->src.seqhi = s->dst.seqhi =
ntohl(pd.hdr.tcp.th_ack) - 1;
s->src.seqlo =
ntohl(pd.hdr.tcp.th_seq) - 1;
pf_set_protostate(s, PF_PEER_SRC,
PF_TCPS_PROXY_DST);
}
} else
action = PF_DROP;
}
if (action == PF_MATCH)
action = pf_test_state(&pd, &s, &reason);
if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC > 0 */
r = s->rule.ptr;
a = s->anchor.ptr;
#if NPFLOG > 0
pd.pflog |= s->log;
#endif /* NPFLOG > 0 */
} else if (s == NULL) {
PF_LOCK();
have_pf_lock = 1;
action = pf_test_rule(&pd, &r, &s, &a, &ruleset,
&reason, &deferral);
s = pf_state_ref(s);
}
if (pd.virtual_proto == IPPROTO_TCP) {
if (s) {
if (s->max_mss)
pf_normalize_mss(&pd, s->max_mss);
} else if (r->max_mss)
pf_normalize_mss(&pd, r->max_mss);
}
break;
}
if (have_pf_lock != 0)
PF_UNLOCK();
/*
* At the moment, we rely on NET_LOCK() to prevent removal of items
* we've collected above ('r', 'anchor' and 'ruleset'). They'll have
* to be refcounted when NET_LOCK() is gone.
*/
done:
if (action != PF_DROP) {
if (s) {
/* The non-state case is handled in pf_test_rule() */
if (action == PF_PASS && pd.badopts != 0 &&
!(s->state_flags & PFSTATE_ALLOWOPTS)) {
action = PF_DROP;
REASON_SET(&reason, PFRES_IPOPTIONS);
#if NPFLOG > 0
pd.pflog |= PF_LOG_FORCE;
#endif /* NPFLOG > 0 */
DPFPRINTF(LOG_NOTICE, "dropping packet with "
"ip/ipv6 options in pf_test()");
}
pf_scrub(pd.m, s->state_flags, pd.af, s->min_ttl,
s->set_tos);
pf_tag_packet(pd.m, s->tag, s->rtableid[pd.didx]);
if (pqid || (pd.tos & IPTOS_LOWDELAY)) {
qid = s->pqid;
if (s->state_flags & PFSTATE_SETPRIO)
pd.m->m_pkthdr.pf.prio = s->set_prio[1];
} else {
qid = s->qid;
if (s->state_flags & PFSTATE_SETPRIO)
pd.m->m_pkthdr.pf.prio = s->set_prio[0];
}
pd.m->m_pkthdr.pf.delay = s->delay;
} else {
pf_scrub(pd.m, r->scrub_flags, pd.af, r->min_ttl,
r->set_tos);
if (pqid || (pd.tos & IPTOS_LOWDELAY)) {
qid = r->pqid;
if (r->scrub_flags & PFSTATE_SETPRIO)
pd.m->m_pkthdr.pf.prio = r->set_prio[1];
} else {
qid = r->qid;
if (r->scrub_flags & PFSTATE_SETPRIO)
pd.m->m_pkthdr.pf.prio = r->set_prio[0];
}
pd.m->m_pkthdr.pf.delay = r->delay;
}
}
if (action == PF_PASS && qid)
pd.m->m_pkthdr.pf.qid = qid;
if (pd.dir == PF_IN && s && s->key[PF_SK_STACK])
pf_mbuf_link_state_key(pd.m, s->key[PF_SK_STACK]);
if (pd.dir == PF_OUT &&
pd.m->m_pkthdr.pf.inp && !pd.m->m_pkthdr.pf.inp->inp_pf_sk &&
s && s->key[PF_SK_STACK] && !s->key[PF_SK_STACK]->inp)
pf_state_key_link_inpcb(s->key[PF_SK_STACK],
pd.m->m_pkthdr.pf.inp);
if (s != NULL && !ISSET(pd.m->m_pkthdr.csum_flags, M_FLOWID)) {
pd.m->m_pkthdr.ph_flowid = bemtoh64(&s->id);
SET(pd.m->m_pkthdr.csum_flags, M_FLOWID);
}
/*
* connections redirected to loopback should not match sockets
* bound specifically to loopback due to security implications,
* see in_pcblookup_listen().
*/
if (pd.destchg)
if ((pd.af == AF_INET && (ntohl(pd.dst->v4.s_addr) >>
IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) ||
(pd.af == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&pd.dst->v6)))
pd.m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST;
/* We need to redo the route lookup on outgoing routes. */
if (pd.destchg && pd.dir == PF_OUT)
pd.m->m_pkthdr.pf.flags |= PF_TAG_REROUTE;
if (pd.dir == PF_IN && action == PF_PASS &&
(r->divert.type == PF_DIVERT_TO ||
r->divert.type == PF_DIVERT_REPLY)) {
struct pf_divert *divert;
if ((divert = pf_get_divert(pd.m))) {
pd.m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED;
divert->addr = r->divert.addr;
divert->port = r->divert.port;
divert->rdomain = pd.rdomain;
divert->type = r->divert.type;
}
}
if (action == PF_PASS && r->divert.type == PF_DIVERT_PACKET)
action = PF_DIVERT;
#if NPFLOG > 0
if (pd.pflog) {
struct pf_rule_item *ri;
if (pd.pflog & PF_LOG_FORCE || r->log & PF_LOG_ALL)
pflog_packet(&pd, reason, r, a, ruleset, NULL);
if (s) {
SLIST_FOREACH(ri, &s->match_rules, entry)
if (ri->r->log & PF_LOG_ALL)
pflog_packet(&pd, reason, ri->r, a,
ruleset, NULL);
}
}
#endif /* NPFLOG > 0 */
pf_counters_inc(action, &pd, s, r, a);
switch (action) {
case PF_SYNPROXY_DROP:
m_freem(pd.m);
/* FALLTHROUGH */
case PF_DEFER:
#if NPFSYNC > 0
/*
* We no longer hold PF_LOCK() here, so we can dispatch
* deferral if we are asked to do so.
*/
if (deferral != NULL)
pfsync_undefer(deferral, 0);
#endif /* NPFSYNC > 0 */
pd.m = NULL;
action = PF_PASS;
break;
case PF_DIVERT:
switch (pd.af) {
case AF_INET:
divert_packet(pd.m, pd.dir, r->divert.port);
pd.m = NULL;
break;
#ifdef INET6
case AF_INET6:
divert6_packet(pd.m, pd.dir, r->divert.port);
pd.m = NULL;
break;
#endif /* INET6 */
}
action = PF_PASS;
break;
#ifdef INET6
case PF_AFRT:
if (pf_translate_af(&pd)) {
action = PF_DROP;
break;
}
pd.m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
switch (pd.naf) {
case AF_INET:
if (pd.dir == PF_IN) {
if (ipforwarding == 0) {
ipstat_inc(ips_cantforward);
action = PF_DROP;
break;
}
ip_forward(pd.m, ifp, NULL, 1);
} else
ip_output(pd.m, NULL, NULL, 0, NULL, NULL, 0);
break;
case AF_INET6:
if (pd.dir == PF_IN) {
if (ip6_forwarding == 0) {
ip6stat_inc(ip6s_cantforward);
action = PF_DROP;
break;
}
ip6_forward(pd.m, NULL, 1);
} else
ip6_output(pd.m, NULL, NULL, 0, NULL, NULL);
break;
}
if (action != PF_DROP) {
pd.m = NULL;
action = PF_PASS;
}
break;
#endif /* INET6 */
case PF_DROP:
m_freem(pd.m);
pd.m = NULL;
break;
default:
if (s && s->rt) {
switch (pd.af) {
case AF_INET:
pf_route(&pd, s);
break;
#ifdef INET6
case AF_INET6:
pf_route6(&pd, s);
break;
#endif /* INET6 */
}
}
break;
}
#ifdef INET6
/* if reassembled packet passed, create new fragments */
if (pf_status.reass && action == PF_PASS && pd.m && fwdir == PF_FWD &&
pd.af == AF_INET6) {
struct m_tag *mtag;
if ((mtag = m_tag_find(pd.m, PACKET_TAG_PF_REASSEMBLED, NULL)))
action = pf_refragment6(&pd.m, mtag, NULL, NULL, NULL);
}
#endif /* INET6 */
if (s && action != PF_DROP) {
if (!s->if_index_in && dir == PF_IN)
s->if_index_in = ifp->if_index;
else if (!s->if_index_out && dir == PF_OUT)
s->if_index_out = ifp->if_index;
}
*m0 = pd.m;
pf_state_unref(s);
return (action);
}
int
pf_ouraddr(struct mbuf *m)
{
struct pf_state_key *sk;
if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED)
return (1);
sk = m->m_pkthdr.pf.statekey;
if (sk != NULL) {
if (sk->inp != NULL)
return (1);
}
return (-1);
}
/*
* must be called whenever any addressing information such as
* address, port, protocol has changed
*/
void
pf_pkt_addr_changed(struct mbuf *m)
{
pf_mbuf_unlink_state_key(m);
pf_mbuf_unlink_inpcb(m);
}
struct inpcb *
pf_inp_lookup(struct mbuf *m)
{
struct inpcb *inp = NULL;
struct pf_state_key *sk = m->m_pkthdr.pf.statekey;
if (!pf_state_key_isvalid(sk))
pf_mbuf_unlink_state_key(m);
else
inp = m->m_pkthdr.pf.statekey->inp;
if (inp && inp->inp_pf_sk)
KASSERT(m->m_pkthdr.pf.statekey == inp->inp_pf_sk);
in_pcbref(inp);
return (inp);
}
void
pf_inp_link(struct mbuf *m, struct inpcb *inp)
{
struct pf_state_key *sk = m->m_pkthdr.pf.statekey;
if (!pf_state_key_isvalid(sk)) {
pf_mbuf_unlink_state_key(m);
return;
}
/*
* we don't need to grab PF-lock here. At worst case we link inp to
* state, which might be just being marked as deleted by another
* thread.
*/
if (inp && !sk->inp && !inp->inp_pf_sk)
pf_state_key_link_inpcb(sk, inp);
/* The statekey has finished finding the inp, it is no longer needed. */
pf_mbuf_unlink_state_key(m);
}
void
pf_inp_unlink(struct inpcb *inp)
{
pf_inpcb_unlink_state_key(inp);
}
void
pf_state_key_link_reverse(struct pf_state_key *sk, struct pf_state_key *skrev)
{
struct pf_state_key *old_reverse;
old_reverse = atomic_cas_ptr(&sk->reverse, NULL, skrev);
if (old_reverse != NULL)
KASSERT(old_reverse == skrev);
else {
pf_state_key_ref(skrev);
/*
* NOTE: if sk == skrev, then KASSERT() below holds true, we
* still want to grab a reference in such case, because
* pf_state_key_unlink_reverse() does not check whether keys
* are identical or not.
*/
old_reverse = atomic_cas_ptr(&skrev->reverse, NULL, sk);
if (old_reverse != NULL)
KASSERT(old_reverse == sk);
pf_state_key_ref(sk);
}
}
#if NPFLOG > 0
void
pf_log_matches(struct pf_pdesc *pd, struct pf_rule *rm, struct pf_rule *am,
struct pf_ruleset *ruleset, struct pf_rule_slist *matchrules)
{
struct pf_rule_item *ri;
/* if this is the log(matches) rule, packet has been logged already */
if (rm->log & PF_LOG_MATCHES)
return;
SLIST_FOREACH(ri, matchrules, entry)
if (ri->r->log & PF_LOG_MATCHES)
pflog_packet(pd, PFRES_MATCH, rm, am, ruleset, ri->r);
}
#endif /* NPFLOG > 0 */
struct pf_state_key *
pf_state_key_ref(struct pf_state_key *sk)
{
if (sk != NULL)
PF_REF_TAKE(sk->refcnt);
return (sk);
}
void
pf_state_key_unref(struct pf_state_key *sk)
{
if (PF_REF_RELE(sk->refcnt)) {
/* state key must be removed from tree */
KASSERT(!pf_state_key_isvalid(sk));
/* state key must be unlinked from reverse key */
KASSERT(sk->reverse == NULL);
/* state key must be unlinked from socket */
KASSERT(sk->inp == NULL);
pool_put(&pf_state_key_pl, sk);
}
}
int
pf_state_key_isvalid(struct pf_state_key *sk)
{
return ((sk != NULL) && (sk->removed == 0));
}
void
pf_mbuf_link_state_key(struct mbuf *m, struct pf_state_key *sk)
{
KASSERT(m->m_pkthdr.pf.statekey == NULL);
m->m_pkthdr.pf.statekey = pf_state_key_ref(sk);
}
void
pf_mbuf_unlink_state_key(struct mbuf *m)
{
struct pf_state_key *sk = m->m_pkthdr.pf.statekey;
if (sk != NULL) {
m->m_pkthdr.pf.statekey = NULL;
pf_state_key_unref(sk);
}
}
void
pf_mbuf_link_inpcb(struct mbuf *m, struct inpcb *inp)
{
KASSERT(m->m_pkthdr.pf.inp == NULL);
m->m_pkthdr.pf.inp = in_pcbref(inp);
}
void
pf_mbuf_unlink_inpcb(struct mbuf *m)
{
struct inpcb *inp = m->m_pkthdr.pf.inp;
if (inp != NULL) {
m->m_pkthdr.pf.inp = NULL;
in_pcbunref(inp);
}
}
void
pf_state_key_link_inpcb(struct pf_state_key *sk, struct inpcb *inp)
{
KASSERT(sk->inp == NULL);
sk->inp = in_pcbref(inp);
KASSERT(inp->inp_pf_sk == NULL);
inp->inp_pf_sk = pf_state_key_ref(sk);
}
void
pf_inpcb_unlink_state_key(struct inpcb *inp)
{
struct pf_state_key *sk = inp->inp_pf_sk;
if (sk != NULL) {
KASSERT(sk->inp == inp);
sk->inp = NULL;
inp->inp_pf_sk = NULL;
pf_state_key_unref(sk);
in_pcbunref(inp);
}
}
void
pf_state_key_unlink_inpcb(struct pf_state_key *sk)
{
struct inpcb *inp = sk->inp;
if (inp != NULL) {
KASSERT(inp->inp_pf_sk == sk);
sk->inp = NULL;
inp->inp_pf_sk = NULL;
pf_state_key_unref(sk);
in_pcbunref(inp);
}
}
void
pf_state_key_unlink_reverse(struct pf_state_key *sk)
{
struct pf_state_key *skrev = sk->reverse;
/* Note that sk and skrev may be equal, then we unref twice. */
if (skrev != NULL) {
KASSERT(skrev->reverse == sk);
sk->reverse = NULL;
skrev->reverse = NULL;
pf_state_key_unref(skrev);
pf_state_key_unref(sk);
}
}
struct pf_state *
pf_state_ref(struct pf_state *s)
{
if (s != NULL)
PF_REF_TAKE(s->refcnt);
return (s);
}
void
pf_state_unref(struct pf_state *s)
{
if ((s != NULL) && PF_REF_RELE(s->refcnt)) {
/* never inserted or removed */
#if NPFSYNC > 0
KASSERT((TAILQ_NEXT(s, sync_list) == NULL) ||
((TAILQ_NEXT(s, sync_list) == _Q_INVALID) &&
(s->sync_state == PFSYNC_S_NONE)));
#endif /* NPFSYNC */
KASSERT((TAILQ_NEXT(s, entry_list) == NULL) ||
(TAILQ_NEXT(s, entry_list) == _Q_INVALID));
KASSERT((s->key[PF_SK_WIRE] == NULL) &&
(s->key[PF_SK_STACK] == NULL));
pool_put(&pf_state_pl, s);
}
}
int
pf_delay_pkt(struct mbuf *m, u_int ifidx)
{
struct pf_pktdelay *pdy;
if ((pdy = pool_get(&pf_pktdelay_pl, PR_NOWAIT)) == NULL) {
m_freem(m);
return (ENOBUFS);
}
pdy->ifidx = ifidx;
pdy->m = m;
timeout_set(&pdy->to, pf_pktenqueue_delayed, pdy);
timeout_add_msec(&pdy->to, m->m_pkthdr.pf.delay);
m->m_pkthdr.pf.delay = 0;
return (0);
}
void
pf_pktenqueue_delayed(void *arg)
{
struct pf_pktdelay *pdy = arg;
struct ifnet *ifp;
ifp = if_get(pdy->ifidx);
if (ifp != NULL) {
if_enqueue(ifp, pdy->m);
if_put(ifp);
} else
m_freem(pdy->m);
pool_put(&pf_pktdelay_pl, pdy);
}
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/* $OpenBSD: fuse_vfsops.c,v 1.45 2021/05/01 16:18:29 gnezdo Exp $ */
/*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/specdev.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/fusebuf.h>
#include "fusefs_node.h"
#include "fusefs.h"
int fusefs_mount(struct mount *, const char *, void *, struct nameidata *,
struct proc *);
int fusefs_start(struct mount *, int, struct proc *);
int fusefs_unmount(struct mount *, int, struct proc *);
int fusefs_root(struct mount *, struct vnode **);
int fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
int fusefs_statfs(struct mount *, struct statfs *, struct proc *);
int fusefs_sync(struct mount *, int, int, struct ucred *, struct proc *);
int fusefs_vget(struct mount *, ino_t, struct vnode **);
int fusefs_fhtovp(struct mount *, struct fid *, struct vnode **);
int fusefs_vptofh(struct vnode *, struct fid *);
int fusefs_init(struct vfsconf *);
int fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
struct proc *);
int fusefs_checkexp(struct mount *, struct mbuf *, int *,
struct ucred **);
const struct vfsops fusefs_vfsops = {
.vfs_mount = fusefs_mount,
.vfs_start = fusefs_start,
.vfs_unmount = fusefs_unmount,
.vfs_root = fusefs_root,
.vfs_quotactl = fusefs_quotactl,
.vfs_statfs = fusefs_statfs,
.vfs_sync = fusefs_sync,
.vfs_vget = fusefs_vget,
.vfs_fhtovp = fusefs_fhtovp,
.vfs_vptofh = fusefs_vptofh,
.vfs_init = fusefs_init,
.vfs_sysctl = fusefs_sysctl,
.vfs_checkexp = fusefs_checkexp,
};
struct pool fusefs_fbuf_pool;
#define PENDING 2 /* FBT_INIT reply not yet received */
int
fusefs_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
struct fusefs_mnt *fmp;
struct fusebuf *fbuf;
struct fusefs_args *args = data;
struct vnode *vp;
struct file *fp;
int error = 0;
if (mp->mnt_flag & MNT_UPDATE)
return (EOPNOTSUPP);
if ((fp = fd_getfile(p->p_fd, args->fd)) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_VNODE) {
error = EINVAL;
goto bad;
}
vp = fp->f_data;
if (vp->v_type != VCHR) {
error = EBADF;
goto bad;
}
/* Only root may specify allow_other. */
if (args->allow_other && (error = suser_ucred(p->p_ucred)))
goto bad;
fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO);
fmp->mp = mp;
fmp->sess_init = PENDING;
fmp->dev = vp->v_rdev;
if (args->max_read > 0)
fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE);
else
fmp->max_read = FUSEBUFMAXSIZE;
fmp->allow_other = args->allow_other;
mp->mnt_data = fmp;
mp->mnt_flag |= MNT_LOCAL;
vfs_getnewfsid(mp);
memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN);
memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
fuse_device_set_fmp(fmp, 1);
fbuf = fb_setup(0, 0, FBT_INIT, p);
/* cannot tsleep on mount */
fuse_device_queue_fbuf(fmp->dev, fbuf);
bad:
FRELE(fp, p);
return (error);
}
int
fusefs_start(struct mount *mp, int flags, struct proc *p)
{
return (0);
}
int
fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct fusefs_mnt *fmp;
struct fusebuf *fbuf;
int flags = 0;
int error;
fmp = VFSTOFUSEFS(mp);
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
if ((error = vflush(mp, NULLVP, flags)))
return (error);
if (fmp->sess_init && fmp->sess_init != PENDING) {
fbuf = fb_setup(0, 0, FBT_DESTROY, p);
error = fb_queue(fmp->dev, fbuf);
if (error)
printf("fusefs: error %d on destroy\n", error);
fb_delete(fbuf);
}
fmp->sess_init = 0;
fuse_device_cleanup(fmp->dev);
fuse_device_set_fmp(fmp, 0);
free(fmp, M_FUSEFS, sizeof(*fmp));
mp->mnt_data = NULL;
return (0);
}
int
fusefs_root(struct mount *mp, struct vnode **vpp)
{
struct vnode *nvp;
int error;
if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0)
return (error);
nvp->v_type = VDIR;
*vpp = nvp;
return (0);
}
int
fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
struct proc *p)
{
return (EOPNOTSUPP);
}
int
fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct fusefs_mnt *fmp;
struct fusebuf *fbuf;
int error;
fmp = VFSTOFUSEFS(mp);
/* Deny other users unless allow_other mount option was specified. */
if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner)
return (EPERM);
copy_statfs_info(sbp, mp);
/*
* Both FBT_INIT and FBT_STATFS are sent to the FUSE file system
* daemon when it is mounted. However, the daemon is the process
* that called mount(2) so to prevent a deadlock return dummy
* values until the response to FBT_INIT init is received. All
* other VFS syscalls are queued.
*/
if (!fmp->sess_init || fmp->sess_init == PENDING) {
sbp->f_bavail = 0;
sbp->f_bfree = 0;
sbp->f_blocks = 0;
sbp->f_ffree = 0;
sbp->f_favail = 0;
sbp->f_files = 0;
sbp->f_bsize = 0;
sbp->f_iosize = 0;
sbp->f_namemax = 0;
} else {
fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p);
error = fb_queue(fmp->dev, fbuf);
if (error) {
fb_delete(fbuf);
return (error);
}
sbp->f_bavail = fbuf->fb_stat.f_bavail;
sbp->f_bfree = fbuf->fb_stat.f_bfree;
sbp->f_blocks = fbuf->fb_stat.f_blocks;
sbp->f_files = fbuf->fb_stat.f_files;
sbp->f_ffree = fbuf->fb_stat.f_ffree;
sbp->f_favail = fbuf->fb_stat.f_favail;
sbp->f_bsize = fbuf->fb_stat.f_frsize;
sbp->f_iosize = fbuf->fb_stat.f_bsize;
sbp->f_namemax = fbuf->fb_stat.f_namemax;
fb_delete(fbuf);
}
return (0);
}
int
fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
struct proc *p)
{
return (0);
}
int
fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
struct vattr vattr;
struct fusefs_mnt *fmp;
struct fusefs_node *ip;
struct vnode *nvp;
int i;
int error;
retry:
fmp = VFSTOFUSEFS(mp);
/*
* check if vnode is in hash.
*/
if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP)
return (0);
/*
* if not create it
*/
if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
printf("fusefs: getnewvnode error\n");
*vpp = NULLVP;
return (error);
}
ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
rrw_init_flags(&ip->ufs_ino.i_lock, "fuseinode",
RWL_DUPOK | RWL_IS_VNODE);
nvp->v_data = ip;
ip->ufs_ino.i_vnode = nvp;
ip->ufs_ino.i_dev = fmp->dev;
ip->ufs_ino.i_number = ino;
for (i = 0; i < FUFH_MAXTYPE; i++)
ip->fufh[i].fh_type = FUFH_INVALID;
error = ufs_ihashins(&ip->ufs_ino);
if (error) {
vrele(nvp);
if (error == EEXIST)
goto retry;
return (error);
}
ip->ufs_ino.i_ump = (struct ufsmount *)fmp;
if (ino == FUSE_ROOTINO)
nvp->v_flag |= VROOT;
else {
/*
* Initialise the file size so that file size changes can be
* detected during file operations.
*/
error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc);
if (error) {
vrele(nvp);
return (error);
}
ip->filesize = vattr.va_size;
}
*vpp = nvp;
return (0);
}
int
fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
return (EINVAL);
}
int
fusefs_vptofh(struct vnode *vp, struct fid *fhp)
{
return (EINVAL);
}
int
fusefs_init(struct vfsconf *vfc)
{
pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
"fmsg", NULL);
return (0);
}
extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
const struct sysctl_bounded_args fusefs_vars[] = {
{ FUSEFS_OPENDEVS, &stat_opened_fusedev, SYSCTL_INT_READONLY },
{ FUSEFS_INFBUFS, &stat_fbufs_in, SYSCTL_INT_READONLY },
{ FUSEFS_WAITFBUFS, &stat_fbufs_wait, SYSCTL_INT_READONLY },
{ FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, SYSCTL_INT_READONLY },
};
int
fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen, struct proc *p)
{
return sysctl_bounded_arr(fusefs_vars, nitems(fusefs_vars), name,
namelen, oldp, oldlenp, newp, newlen);
}
int
fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
struct ucred **credanonp)
{
return (EOPNOTSUPP);
}
2
27
5
1
1
6
3
3
3
10
6
1
1
4
1
1
1
1
2
2
2
1
1
3
3
3
4
1
1
2
3
3
1
2
1
1
1
2
2
2
2
2
1
2
2
2
2
2
1
1
6
6
2
1
1
1
1
3
3
2
30
32
3
3
2
3
3
2
5
4
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
/* $OpenBSD: ip_mroute.c,v 1.136 2022/08/06 15:57:59 bluhm Exp $ */
/* $NetBSD: ip_mroute.c,v 1.85 2004/04/26 01:31:57 matt Exp $ */
/*
* Copyright (c) 1989 Stephen Deering
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Stephen Deering of Stanford University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93
*/
/*
* IP multicast forwarding procedures
*
* Written by David Waitzman, BBN Labs, August 1988.
* Modified by Steve Deering, Stanford, February 1989.
* Modified by Mark J. Steiglitz, Stanford, May, 1991
* Modified by Van Jacobson, LBL, January 1993
* Modified by Ajit Thyagarajan, PARC, August 1993
* Modified by Bill Fenner, PARC, April 1994
* Modified by Charles M. Hannum, NetBSD, May 1995.
* Modified by Ahmed Helmy, SGI, June 1996
* Modified by George Edmond Eddy (Rusty), ISI, February 1998
* Modified by Pavlin Radoslavov, USC/ISI, May 1998, August 1999, October 2000
* Modified by Hitoshi Asaeda, WIDE, August 2000
* Modified by Pavlin Radoslavov, ICSI, October 2002
*
* MROUTING Revision: 1.2
* advanced API support, bandwidth metering and signaling
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/igmp.h>
#include <netinet/ip_mroute.h>
/* #define MCAST_DEBUG */
#ifdef MCAST_DEBUG
int mcast_debug = 1;
#define DPRINTF(fmt, args...) \
do { \
if (mcast_debug) \
printf("%s:%d " fmt "\n", \
__func__, __LINE__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
/*
* Globals. All but ip_mrouter and ip_mrtproto could be static,
* except for netstat or debugging purposes.
*/
struct socket *ip_mrouter[RT_TABLEID_MAX + 1];
struct rttimer_queue ip_mrouterq;
uint64_t mrt_count[RT_TABLEID_MAX + 1];
int ip_mrtproto = IGMP_DVMRP; /* for netstat only */
struct mrtstat mrtstat;
struct rtentry *mfc_find(struct ifnet *, struct in_addr *,
struct in_addr *, unsigned int);
int get_sg_cnt(unsigned int, struct sioc_sg_req *);
int get_vif_cnt(unsigned int, struct sioc_vif_req *);
int mrt_rtwalk_mfcsysctl(struct rtentry *, void *, unsigned int);
int ip_mrouter_init(struct socket *, struct mbuf *);
int mrouter_rtwalk_delete(struct rtentry *, void *, unsigned int);
int get_version(struct mbuf *);
int add_vif(struct socket *, struct mbuf *);
int del_vif(struct socket *, struct mbuf *);
void update_mfc_params(struct mfcctl2 *, int, unsigned int);
int mfc_add(struct mfcctl2 *, struct in_addr *, struct in_addr *,
int, unsigned int, int);
int add_mfc(struct socket *, struct mbuf *);
int del_mfc(struct socket *, struct mbuf *);
int set_api_config(struct socket *, struct mbuf *); /* chose API capabilities */
int get_api_support(struct mbuf *);
int get_api_config(struct mbuf *);
int socket_send(struct socket *, struct mbuf *,
struct sockaddr_in *);
int ip_mdq(struct mbuf *, struct ifnet *, struct rtentry *);
struct ifnet *if_lookupbyvif(vifi_t, unsigned int);
struct rtentry *rt_mcast_add(struct ifnet *, struct sockaddr *,
struct sockaddr *);
void mrt_mcast_del(struct rtentry *, unsigned int);
/*
* Kernel multicast routing API capabilities and setup.
* If more API capabilities are added to the kernel, they should be
* recorded in `mrt_api_support'.
*/
static const u_int32_t mrt_api_support = (MRT_MFC_FLAGS_DISABLE_WRONGVIF |
MRT_MFC_RP);
static u_int32_t mrt_api_config = 0;
/*
* Find a route for a given origin IP address and Multicast group address
* Type of service parameter to be added in the future!!!
* Statistics are updated by the caller if needed
* (mrtstat.mrts_mfc_lookups and mrtstat.mrts_mfc_misses)
*/
struct rtentry *
mfc_find(struct ifnet *ifp, struct in_addr *origin, struct in_addr *group,
unsigned int rtableid)
{
struct rtentry *rt;
struct sockaddr_in msin;
memset(&msin, 0, sizeof(msin));
msin.sin_len = sizeof(msin);
msin.sin_family = AF_INET;
msin.sin_addr = *group;
rt = rtalloc(sintosa(&msin), 0, rtableid);
do {
if (!rtisvalid(rt)) {
rtfree(rt);
return NULL;
}
/* Don't consider non multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
continue;
/* Return first occurrence if interface is not specified. */
if (ifp == NULL)
return (rt);
if (rt->rt_ifidx == ifp->if_index)
return (rt);
} while ((rt = rtable_iterate(rt)) != NULL);
return (NULL);
}
/*
* Handle MRT setsockopt commands to modify the multicast routing tables.
*/
int
ip_mrouter_set(struct socket *so, int optname, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
int error;
if (optname != MRT_INIT &&
so != ip_mrouter[inp->inp_rtableid])
error = ENOPROTOOPT;
else
switch (optname) {
case MRT_INIT:
error = ip_mrouter_init(so, m);
break;
case MRT_DONE:
error = ip_mrouter_done(so);
break;
case MRT_ADD_VIF:
error = add_vif(so, m);
break;
case MRT_DEL_VIF:
error = del_vif(so, m);
break;
case MRT_ADD_MFC:
error = add_mfc(so, m);
break;
case MRT_DEL_MFC:
error = del_mfc(so, m);
break;
case MRT_API_CONFIG:
error = set_api_config(so, m);
break;
default:
error = ENOPROTOOPT;
break;
}
return (error);
}
/*
* Handle MRT getsockopt commands
*/
int
ip_mrouter_get(struct socket *so, int optname, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
int error;
if (so != ip_mrouter[inp->inp_rtableid])
error = ENOPROTOOPT;
else {
switch (optname) {
case MRT_VERSION:
error = get_version(m);
break;
case MRT_API_SUPPORT:
error = get_api_support(m);
break;
case MRT_API_CONFIG:
error = get_api_config(m);
break;
default:
error = ENOPROTOOPT;
break;
}
}
return (error);
}
/*
* Handle ioctl commands to obtain information from the cache
*/
int
mrt_ioctl(struct socket *so, u_long cmd, caddr_t data)
{
struct inpcb *inp = sotoinpcb(so);
int error;
if (inp == NULL)
return (ENOTCONN);
if (so != ip_mrouter[inp->inp_rtableid])
error = EINVAL;
else
switch (cmd) {
case SIOCGETVIFCNT:
NET_LOCK_SHARED();
error = get_vif_cnt(inp->inp_rtableid,
(struct sioc_vif_req *)data);
NET_UNLOCK_SHARED();
break;
case SIOCGETSGCNT:
NET_LOCK_SHARED();
error = get_sg_cnt(inp->inp_rtableid,
(struct sioc_sg_req *)data);
NET_UNLOCK_SHARED();
break;
default:
error = ENOTTY;
break;
}
return (error);
}
/*
* returns the packet, byte, rpf-failure count for the source group provided
*/
int
get_sg_cnt(unsigned int rtableid, struct sioc_sg_req *req)
{
struct rtentry *rt;
struct mfc *mfc;
rt = mfc_find(NULL, &req->src, &req->grp, rtableid);
if (rt == NULL) {
req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff;
return (EADDRNOTAVAIL);
}
req->pktcnt = req->bytecnt = req->wrong_if = 0;
do {
/* Don't consider non multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
continue;
mfc = (struct mfc *)rt->rt_llinfo;
if (mfc == NULL)
continue;
req->pktcnt += mfc->mfc_pkt_cnt;
req->bytecnt += mfc->mfc_byte_cnt;
req->wrong_if += mfc->mfc_wrong_if;
} while ((rt = rtable_iterate(rt)) != NULL);
return (0);
}
/*
* returns the input and output packet and byte counts on the vif provided
*/
int
get_vif_cnt(unsigned int rtableid, struct sioc_vif_req *req)
{
struct ifnet *ifp;
struct vif *v;
vifi_t vifi = req->vifi;
if ((ifp = if_lookupbyvif(vifi, rtableid)) == NULL)
return (EINVAL);
v = (struct vif *)ifp->if_mcast;
req->icount = v->v_pkt_in;
req->ocount = v->v_pkt_out;
req->ibytes = v->v_bytes_in;
req->obytes = v->v_bytes_out;
return (0);
}
int
mrt_sysctl_vif(void *oldp, size_t *oldlenp)
{
caddr_t where = oldp;
size_t needed, given;
struct ifnet *ifp;
struct vif *vifp;
struct vifinfo vinfo;
given = *oldlenp;
needed = 0;
memset(&vinfo, 0, sizeof vinfo);
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if ((vifp = (struct vif *)ifp->if_mcast) == NULL)
continue;
vinfo.v_vifi = vifp->v_id;
vinfo.v_flags = vifp->v_flags;
vinfo.v_threshold = vifp->v_threshold;
vinfo.v_lcl_addr = vifp->v_lcl_addr;
vinfo.v_rmt_addr = vifp->v_rmt_addr;
vinfo.v_pkt_in = vifp->v_pkt_in;
vinfo.v_pkt_out = vifp->v_pkt_out;
vinfo.v_bytes_in = vifp->v_bytes_in;
vinfo.v_bytes_out = vifp->v_bytes_out;
needed += sizeof(vinfo);
if (where && needed <= given) {
int error;
error = copyout(&vinfo, where, sizeof(vinfo));
if (error)
return (error);
where += sizeof(vinfo);
}
}
if (where) {
*oldlenp = needed;
if (given < needed)
return (ENOMEM);
} else
*oldlenp = (11 * needed) / 10;
return (0);
}
struct mfcsysctlarg {
struct mfcinfo *msa_minfos;
size_t msa_len;
size_t msa_needed;
};
int
mrt_rtwalk_mfcsysctl(struct rtentry *rt, void *arg, unsigned int rtableid)
{
struct mfc *mfc = (struct mfc *)rt->rt_llinfo;
struct mfcsysctlarg *msa = (struct mfcsysctlarg *)arg;
struct ifnet *ifp;
struct vif *v;
struct mfcinfo *minfo;
int new = 0;
/* Skip entries being removed. */
if (mfc == NULL)
return (0);
/* Skip non-multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
return (0);
/* User just asked for the output size. */
if (msa->msa_minfos == NULL) {
msa->msa_needed += sizeof(*minfo);
return (0);
}
/* Skip route with invalid interfaces. */
if ((ifp = if_get(rt->rt_ifidx)) == NULL)
return (0);
if ((v = (struct vif *)ifp->if_mcast) == NULL) {
if_put(ifp);
return (0);
}
for (minfo = msa->msa_minfos;
(uint8_t *)minfo < ((uint8_t *)msa->msa_minfos + msa->msa_len);
minfo++) {
/* Find a new entry or update old entry. */
if (minfo->mfc_origin.s_addr !=
satosin(rt->rt_gateway)->sin_addr.s_addr ||
minfo->mfc_mcastgrp.s_addr !=
satosin(rt_key(rt))->sin_addr.s_addr) {
if (minfo->mfc_origin.s_addr != 0 ||
minfo->mfc_mcastgrp.s_addr != 0)
continue;
new = 1;
}
minfo->mfc_origin = satosin(rt->rt_gateway)->sin_addr;
minfo->mfc_mcastgrp = satosin(rt_key(rt))->sin_addr;
minfo->mfc_parent = mfc->mfc_parent;
minfo->mfc_pkt_cnt += mfc->mfc_pkt_cnt;
minfo->mfc_byte_cnt += mfc->mfc_byte_cnt;
minfo->mfc_ttls[v->v_id] = mfc->mfc_ttl;
break;
}
if (new != 0)
msa->msa_needed += sizeof(*minfo);
if_put(ifp);
return (0);
}
int
mrt_sysctl_mfc(void *oldp, size_t *oldlenp)
{
unsigned int rtableid;
int error;
struct mfcsysctlarg msa;
if (oldp != NULL && *oldlenp > MAXPHYS)
return (EINVAL);
if (oldp != NULL)
msa.msa_minfos = malloc(*oldlenp, M_TEMP, M_WAITOK | M_ZERO);
else
msa.msa_minfos = NULL;
msa.msa_len = *oldlenp;
msa.msa_needed = 0;
for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) {
rtable_walk(rtableid, AF_INET, NULL, mrt_rtwalk_mfcsysctl,
&msa);
}
if (msa.msa_minfos != NULL && msa.msa_needed > 0 &&
(error = copyout(msa.msa_minfos, oldp, msa.msa_needed)) != 0) {
free(msa.msa_minfos, M_TEMP, *oldlenp);
return (error);
}
free(msa.msa_minfos, M_TEMP, *oldlenp);
*oldlenp = msa.msa_needed;
return (0);
}
/*
* Enable multicast routing
*/
int
ip_mrouter_init(struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
unsigned int rtableid = inp->inp_rtableid;
int *v;
if (so->so_type != SOCK_RAW ||
so->so_proto->pr_protocol != IPPROTO_IGMP)
return (EOPNOTSUPP);
if (m == NULL || m->m_len < sizeof(int))
return (EINVAL);
v = mtod(m, int *);
if (*v != 1)
return (EINVAL);
if (ip_mrouter[rtableid] != NULL)
return (EADDRINUSE);
ip_mrouter[rtableid] = so;
return (0);
}
int
mrouter_rtwalk_delete(struct rtentry *rt, void *arg, unsigned int rtableid)
{
/* Skip non-multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
return (0);
return EEXIST;
}
/*
* Disable multicast routing
*/
int
ip_mrouter_done(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
struct ifnet *ifp;
unsigned int rtableid = inp->inp_rtableid;
int error;
NET_ASSERT_LOCKED();
/* Delete all remaining installed multicast routes. */
do {
struct rtentry *rt = NULL;
error = rtable_walk(rtableid, AF_INET, &rt,
mrouter_rtwalk_delete, NULL);
if (rt != NULL && error == EEXIST) {
mrt_mcast_del(rt, rtableid);
error = EAGAIN;
}
rtfree(rt);
} while (error == EAGAIN);
/* Unregister all interfaces in the domain. */
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rtableid)
continue;
vif_delete(ifp);
}
mrt_api_config = 0;
ip_mrouter[rtableid] = NULL;
mrt_count[rtableid] = 0;
return (0);
}
int
get_version(struct mbuf *m)
{
int *v = mtod(m, int *);
*v = 0x0305; /* XXX !!!! */
m->m_len = sizeof(int);
return (0);
}
/*
* Configure API capabilities
*/
int
set_api_config(struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
struct ifnet *ifp;
u_int32_t *apival;
unsigned int rtableid = inp->inp_rtableid;
if (m == NULL || m->m_len < sizeof(u_int32_t))
return (EINVAL);
apival = mtod(m, u_int32_t *);
/*
* We can set the API capabilities only if it is the first operation
* after MRT_INIT. I.e.:
* - there are no vifs installed
* - the MFC table is empty
*/
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rtableid)
continue;
if (ifp->if_mcast == NULL)
continue;
*apival = 0;
return (EPERM);
}
if (mrt_count[rtableid] > 0) {
*apival = 0;
return (EPERM);
}
mrt_api_config = *apival & mrt_api_support;
*apival = mrt_api_config;
return (0);
}
/*
* Get API capabilities
*/
int
get_api_support(struct mbuf *m)
{
u_int32_t *apival;
if (m == NULL || m->m_len < sizeof(u_int32_t))
return (EINVAL);
apival = mtod(m, u_int32_t *);
*apival = mrt_api_support;
return (0);
}
/*
* Get API configured capabilities
*/
int
get_api_config(struct mbuf *m)
{
u_int32_t *apival;
if (m == NULL || m->m_len < sizeof(u_int32_t))
return (EINVAL);
apival = mtod(m, u_int32_t *);
*apival = mrt_api_config;
return (0);
}
static struct sockaddr_in sin = { sizeof(sin), AF_INET };
int
add_vif(struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
struct vifctl *vifcp;
struct vif *vifp;
struct ifaddr *ifa;
struct ifnet *ifp;
struct ifreq ifr;
int error;
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
if (m == NULL || m->m_len < sizeof(struct vifctl))
return (EINVAL);
vifcp = mtod(m, struct vifctl *);
if (vifcp->vifc_vifi >= MAXVIFS)
return (EINVAL);
if (in_nullhost(vifcp->vifc_lcl_addr))
return (EADDRNOTAVAIL);
if (if_lookupbyvif(vifcp->vifc_vifi, rtableid) != NULL)
return (EADDRINUSE);
/* Tunnels are no longer supported use gif(4) instead. */
if (vifcp->vifc_flags & VIFF_TUNNEL)
return (EOPNOTSUPP);
{
sin.sin_addr = vifcp->vifc_lcl_addr;
ifa = ifa_ifwithaddr(sintosa(&sin), rtableid);
if (ifa == NULL)
return (EADDRNOTAVAIL);
}
/* Use the physical interface associated with the address. */
ifp = ifa->ifa_ifp;
if (ifp->if_mcast != NULL)
return (EADDRINUSE);
{
/* Make sure the interface supports multicast. */
if ((ifp->if_flags & IFF_MULTICAST) == 0)
return (EOPNOTSUPP);
/* Enable promiscuous reception of all IP multicasts. */
memset(&ifr, 0, sizeof(ifr));
satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in);
satosin(&ifr.ifr_addr)->sin_family = AF_INET;
satosin(&ifr.ifr_addr)->sin_addr = zeroin_addr;
error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
if (error)
return (error);
}
vifp = malloc(sizeof(*vifp), M_MRTABLE, M_WAITOK | M_ZERO);
ifp->if_mcast = (caddr_t)vifp;
vifp->v_id = vifcp->vifc_vifi;
vifp->v_flags = vifcp->vifc_flags;
vifp->v_threshold = vifcp->vifc_threshold;
vifp->v_lcl_addr = vifcp->vifc_lcl_addr;
vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
return (0);
}
int
del_vif(struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
struct ifnet *ifp;
vifi_t *vifip;
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
if (m == NULL || m->m_len < sizeof(vifi_t))
return (EINVAL);
vifip = mtod(m, vifi_t *);
if ((ifp = if_lookupbyvif(*vifip, rtableid)) == NULL)
return (EADDRNOTAVAIL);
vif_delete(ifp);
return (0);
}
void
vif_delete(struct ifnet *ifp)
{
struct vif *v;
struct ifreq ifr;
if ((v = (struct vif *)ifp->if_mcast) == NULL)
return;
ifp->if_mcast = NULL;
memset(&ifr, 0, sizeof(ifr));
satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in);
satosin(&ifr.ifr_addr)->sin_family = AF_INET;
satosin(&ifr.ifr_addr)->sin_addr = zeroin_addr;
KERNEL_LOCK();
(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
KERNEL_UNLOCK();
free(v, M_MRTABLE, sizeof(*v));
}
void
mfc_expire_route(struct rtentry *rt, u_int rtableid)
{
struct mfc *mfc = (struct mfc *)rt->rt_llinfo;
/* Skip entry being deleted. */
if (mfc == NULL)
return;
DPRINTF("Route domain %d origin %#08X group %#08x interface %d "
"expire %s", rtableid, satosin(rt->rt_gateway)->sin_addr.s_addr,
satosin(rt_key(rt))->sin_addr.s_addr,
rt->rt_ifidx, mfc->mfc_expire ? "yes" : "no");
/* Not expired, add it back to the queue. */
if (mfc->mfc_expire == 0) {
mfc->mfc_expire = 1;
rt_timer_add(rt, &ip_mrouterq, rtableid);
return;
}
mrt_mcast_del(rt, rtableid);
}
int
mfc_add_route(struct ifnet *ifp, struct sockaddr *origin,
struct sockaddr *group, struct mfcctl2 *mfccp, int wait)
{
struct vif *v = (struct vif *)ifp->if_mcast;
struct rtentry *rt;
struct mfc *mfc;
unsigned int rtableid = ifp->if_rdomain;
rt = rt_mcast_add(ifp, origin, group);
if (rt == NULL)
return (EHOSTUNREACH);
mfc = malloc(sizeof(*mfc), M_MRTABLE, wait | M_ZERO);
if (mfc == NULL) {
DPRINTF("origin %#08X group %#08X parent %d (%s) "
"malloc failed",
satosin(origin)->sin_addr.s_addr,
satosin(group)->sin_addr.s_addr,
mfccp->mfcc_parent, ifp->if_xname);
mrt_mcast_del(rt, rtableid);
rtfree(rt);
return (ENOMEM);
}
rt->rt_llinfo = (caddr_t)mfc;
rt_timer_add(rt, &ip_mrouterq, rtableid);
mfc->mfc_parent = mfccp->mfcc_parent;
mfc->mfc_pkt_cnt = 0;
mfc->mfc_byte_cnt = 0;
mfc->mfc_wrong_if = 0;
mfc->mfc_ttl = mfccp->mfcc_ttls[v->v_id];
mfc->mfc_flags = mfccp->mfcc_flags[v->v_id] & mrt_api_config &
MRT_MFC_FLAGS_ALL;
mfc->mfc_expire = 0;
/* set the RP address */
if (mrt_api_config & MRT_MFC_RP)
mfc->mfc_rp = mfccp->mfcc_rp;
else
mfc->mfc_rp = zeroin_addr;
rtfree(rt);
return (0);
}
void
update_mfc_params(struct mfcctl2 *mfccp, int wait, unsigned int rtableid)
{
struct rtentry *rt;
struct mfc *mfc;
struct ifnet *ifp;
int i;
struct sockaddr_in osin, msin;
memset(&osin, 0, sizeof(osin));
osin.sin_len = sizeof(osin);
osin.sin_family = AF_INET;
osin.sin_addr = mfccp->mfcc_origin;
memset(&msin, 0, sizeof(msin));
msin.sin_len = sizeof(msin);
msin.sin_family = AF_INET;
msin.sin_addr = mfccp->mfcc_mcastgrp;
for (i = 0; i < MAXVIFS; i++) {
/* Don't add/del upstream routes here. */
if (i == mfccp->mfcc_parent)
continue;
/* Test for vif existence and then update the entry. */
if ((ifp = if_lookupbyvif(i, rtableid)) == NULL)
continue;
rt = mfc_find(ifp, &mfccp->mfcc_origin,
&mfccp->mfcc_mcastgrp, rtableid);
/* vif not configured or removed. */
if (mfccp->mfcc_ttls[i] == 0) {
/* Route doesn't exist, nothing to do. */
if (rt == NULL)
continue;
DPRINTF("del route (group %#08X) for vif %d (%s)",
mfccp->mfcc_mcastgrp.s_addr, i, ifp->if_xname);
mrt_mcast_del(rt, rtableid);
rtfree(rt);
continue;
}
/* Route exists, look for changes. */
if (rt != NULL) {
mfc = (struct mfc *)rt->rt_llinfo;
/* Skip route being deleted. */
if (mfc == NULL) {
rtfree(rt);
continue;
}
/* No new changes to apply. */
if (mfccp->mfcc_ttls[i] == mfc->mfc_ttl &&
mfccp->mfcc_parent == mfc->mfc_parent) {
rtfree(rt);
continue;
}
DPRINTF("update route (group %#08X) for vif %d (%s)",
mfccp->mfcc_mcastgrp.s_addr, i, ifp->if_xname);
mfc->mfc_ttl = mfccp->mfcc_ttls[i];
mfc->mfc_parent = mfccp->mfcc_parent;
rtfree(rt);
continue;
}
DPRINTF("add route (group %#08X) for vif %d (%s)",
mfccp->mfcc_mcastgrp.s_addr, i, ifp->if_xname);
mfc_add_route(ifp, sintosa(&osin), sintosa(&msin),
mfccp, wait);
}
/* Create route for the parent interface. */
if ((ifp = if_lookupbyvif(mfccp->mfcc_parent, rtableid)) == NULL) {
DPRINTF("failed to find upstream interface %d",
mfccp->mfcc_parent);
return;
}
/* We already have a route, nothing to do here. */
if ((rt = mfc_find(ifp, &mfccp->mfcc_origin,
&mfccp->mfcc_mcastgrp, rtableid)) != NULL) {
rtfree(rt);
return;
}
DPRINTF("add upstream route (group %#08X) for if %s",
mfccp->mfcc_mcastgrp.s_addr, ifp->if_xname);
mfc_add_route(ifp, sintosa(&osin), sintosa(&msin), mfccp, wait);
}
int
mfc_add(struct mfcctl2 *mfcctl2, struct in_addr *origin,
struct in_addr *group, int vidx, unsigned int rtableid, int wait)
{
struct ifnet *ifp;
struct vif *v;
struct mfcctl2 mfcctl;
ifp = if_lookupbyvif(vidx, rtableid);
if (ifp == NULL ||
(v = (struct vif *)ifp->if_mcast) == NULL)
return (EHOSTUNREACH);
memset(&mfcctl, 0, sizeof(mfcctl));
if (mfcctl2 == NULL) {
mfcctl.mfcc_origin = *origin;
mfcctl.mfcc_mcastgrp = *group;
mfcctl.mfcc_parent = vidx;
} else
memcpy(&mfcctl, mfcctl2, sizeof(mfcctl));
update_mfc_params(&mfcctl, wait, rtableid);
return (0);
}
int
add_mfc(struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
struct mfcctl2 mfcctl2;
int mfcctl_size = sizeof(struct mfcctl);
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
if (mrt_api_config & MRT_API_FLAGS_ALL)
mfcctl_size = sizeof(struct mfcctl2);
if (m == NULL || m->m_len < mfcctl_size)
return (EINVAL);
/*
* select data size depending on API version.
*/
if (mrt_api_config & MRT_API_FLAGS_ALL) {
struct mfcctl2 *mp2 = mtod(m, struct mfcctl2 *);
memcpy((caddr_t)&mfcctl2, mp2, sizeof(*mp2));
} else {
struct mfcctl *mp = mtod(m, struct mfcctl *);
memcpy((caddr_t)&mfcctl2, mp, sizeof(*mp));
memset((caddr_t)&mfcctl2 + sizeof(struct mfcctl), 0,
sizeof(mfcctl2) - sizeof(struct mfcctl));
}
if (mfc_add(&mfcctl2, &mfcctl2.mfcc_origin, &mfcctl2.mfcc_mcastgrp,
mfcctl2.mfcc_parent, rtableid, M_WAITOK) == -1)
return (EINVAL);
return (0);
}
int
del_mfc(struct socket *so, struct mbuf *m)
{
struct inpcb *inp = sotoinpcb(so);
struct rtentry *rt;
struct mfcctl2 mfcctl2;
int mfcctl_size = sizeof(struct mfcctl);
struct mfcctl *mp;
unsigned int rtableid = inp->inp_rtableid;
NET_ASSERT_LOCKED();
/*
* XXX: for deleting MFC entries the information in entries
* of size "struct mfcctl" is sufficient.
*/
if (m == NULL || m->m_len < mfcctl_size)
return (EINVAL);
mp = mtod(m, struct mfcctl *);
memcpy((caddr_t)&mfcctl2, mp, sizeof(*mp));
memset((caddr_t)&mfcctl2 + sizeof(struct mfcctl), 0,
sizeof(mfcctl2) - sizeof(struct mfcctl));
DPRINTF("origin %#08X group %#08X rtableid %d",
mfcctl2.mfcc_origin.s_addr, mfcctl2.mfcc_mcastgrp.s_addr, rtableid);
while ((rt = mfc_find(NULL, &mfcctl2.mfcc_origin,
&mfcctl2.mfcc_mcastgrp, rtableid)) != NULL) {
mrt_mcast_del(rt, rtableid);
rtfree(rt);
}
return (0);
}
int
socket_send(struct socket *s, struct mbuf *mm, struct sockaddr_in *src)
{
if (s != NULL) {
if (sbappendaddr(s, &s->so_rcv, sintosa(src), mm, NULL) != 0) {
sorwakeup(s);
return (0);
}
}
m_freem(mm);
return (-1);
}
/*
* IP multicast forwarding function. This function assumes that the packet
* pointed to by "ip" has arrived on (or is about to be sent to) the interface
* pointed to by "ifp", and the packet is to be relayed to other networks
* that have members of the packet's destination IP multicast group.
*
* The packet is returned unscathed to the caller, unless it is
* erroneous, in which case a non-zero return value tells the caller to
* discard it.
*/
#define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */
#define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */
int
ip_mforward(struct mbuf *m, struct ifnet *ifp)
{
struct ip *ip = mtod(m, struct ip *);
struct vif *v;
struct rtentry *rt;
static int srctun = 0;
struct mbuf *mm;
unsigned int rtableid = ifp->if_rdomain;
if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
((u_char *)(ip + 1))[1] != IPOPT_LSRR) {
/*
* Packet arrived via a physical interface or
* an encapsulated tunnel or a register_vif.
*/
} else {
/*
* Packet arrived through a source-route tunnel.
* Source-route tunnels are no longer supported.
*/
if ((srctun++ % 1000) == 0)
log(LOG_ERR, "ip_mforward: received source-routed "
"packet from %x\n", ntohl(ip->ip_src.s_addr));
return (EOPNOTSUPP);
}
/*
* Don't forward a packet with time-to-live of zero or one,
* or a packet destined to a local-only group.
*/
if (ip->ip_ttl <= 1 || IN_LOCAL_GROUP(ip->ip_dst.s_addr))
return (0);
/*
* Determine forwarding vifs from the forwarding cache table
*/
++mrtstat.mrts_mfc_lookups;
rt = mfc_find(NULL, &ip->ip_src, &ip->ip_dst, rtableid);
/* Entry exists, so forward if necessary */
if (rt != NULL) {
return (ip_mdq(m, ifp, rt));
} else {
/*
* If we don't have a route for packet's origin,
* Make a copy of the packet & send message to routing daemon
*/
int hlen = ip->ip_hl << 2;
++mrtstat.mrts_mfc_misses;
mrtstat.mrts_no_route++;
{
struct igmpmsg *im;
/*
* Locate the vifi for the incoming interface for
* this packet.
* If none found, drop packet.
*/
if ((v = (struct vif *)ifp->if_mcast) == NULL)
return (EHOSTUNREACH);
/*
* Make a copy of the header to send to the user level
* process
*/
mm = m_copym(m, 0, hlen, M_NOWAIT);
if (mm == NULL ||
(mm = m_pullup(mm, hlen)) == NULL)
return (ENOBUFS);
/*
* Send message to routing daemon to install
* a route into the kernel table
*/
im = mtod(mm, struct igmpmsg *);
im->im_msgtype = IGMPMSG_NOCACHE;
im->im_mbz = 0;
im->im_vif = v->v_id;
mrtstat.mrts_upcalls++;
sin.sin_addr = ip->ip_src;
if (socket_send(ip_mrouter[rtableid], mm, &sin) < 0) {
log(LOG_WARNING, "ip_mforward: ip_mrouter "
"socket queue full\n");
++mrtstat.mrts_upq_sockfull;
return (ENOBUFS);
}
mfc_add(NULL, &ip->ip_src, &ip->ip_dst, v->v_id,
rtableid, M_NOWAIT);
}
return (0);
}
}
/*
* Packet forwarding routine once entry in the cache is made
*/
int
ip_mdq(struct mbuf *m, struct ifnet *ifp0, struct rtentry *rt)
{
struct ip *ip = mtod(m, struct ip *);
struct mfc *mfc = (struct mfc *)rt->rt_llinfo;
struct vif *v = (struct vif *)ifp0->if_mcast;
struct ifnet *ifp;
struct mbuf *mc;
struct ip_moptions imo;
/* Sanity check: we have all promised pointers. */
if (v == NULL || mfc == NULL) {
rtfree(rt);
return (EHOSTUNREACH);
}
/*
* Don't forward if it didn't arrive from the parent vif for its origin.
*/
if (mfc->mfc_parent != v->v_id) {
/* came in the wrong interface */
++mrtstat.mrts_wrong_if;
mfc->mfc_wrong_if++;
rtfree(rt);
return (0);
}
/* If I sourced this packet, it counts as output, else it was input. */
if (in_hosteq(ip->ip_src, v->v_lcl_addr)) {
v->v_pkt_out++;
v->v_bytes_out += m->m_pkthdr.len;
} else {
v->v_pkt_in++;
v->v_bytes_in += m->m_pkthdr.len;
}
/*
* For each vif, decide if a copy of the packet should be forwarded.
* Forward if:
* - the ttl exceeds the vif's threshold
* - there are group members downstream on interface
*/
do {
/* Don't consider non multicast routes. */
if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
(RTF_HOST | RTF_MULTICAST))
continue;
mfc = (struct mfc *)rt->rt_llinfo;
if (mfc == NULL)
continue;
mfc->mfc_pkt_cnt++;
mfc->mfc_byte_cnt += m->m_pkthdr.len;
/* Don't let this route expire. */
mfc->mfc_expire = 0;
if (ip->ip_ttl <= mfc->mfc_ttl)
continue;
if ((ifp = if_get(rt->rt_ifidx)) == NULL)
continue;
/* Sanity check: did we configure this? */
if ((v = (struct vif *)ifp->if_mcast) == NULL) {
if_put(ifp);
continue;
}
/* Don't send in the upstream interface. */
if (mfc->mfc_parent == v->v_id) {
if_put(ifp);
continue;
}
v->v_pkt_out++;
v->v_bytes_out += m->m_pkthdr.len;
/*
* Make a new reference to the packet; make sure
* that the IP header is actually copied, not
* just referenced, so that ip_output() only
* scribbles on the copy.
*/
mc = m_dup_pkt(m, max_linkhdr, M_NOWAIT);
if (mc == NULL) {
if_put(ifp);
rtfree(rt);
return (ENOBUFS);
}
/*
* if physical interface option, extract the options
* and then send
*/
imo.imo_ifidx = rt->rt_ifidx;
imo.imo_ttl = ip->ip_ttl - IPTTLDEC;
imo.imo_loop = 1;
ip_output(mc, NULL, NULL, IP_FORWARDING, &imo, NULL, 0);
if_put(ifp);
} while ((rt = rtable_iterate(rt)) != NULL);
return (0);
}
struct ifnet *
if_lookupbyvif(vifi_t vifi, unsigned int rtableid)
{
struct vif *v;
struct ifnet *ifp;
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rtableid)
continue;
if ((v = (struct vif *)ifp->if_mcast) == NULL)
continue;
if (v->v_id != vifi)
continue;
return (ifp);
}
return (NULL);
}
struct rtentry *
rt_mcast_add(struct ifnet *ifp, struct sockaddr *origin, struct sockaddr *group)
{
struct ifaddr *ifa;
int rv;
unsigned int rtableid = ifp->if_rdomain;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family == AF_INET)
break;
}
if (ifa == NULL) {
DPRINTF("ifa == NULL");
return (NULL);
}
rv = rt_ifa_add(ifa, RTF_HOST | RTF_MULTICAST | RTF_MPATH,
group, ifp->if_rdomain);
if (rv != 0) {
DPRINTF("rt_ifa_add failed (%d)", rv);
return (NULL);
}
mrt_count[rtableid]++;
return (mfc_find(ifp, NULL, &satosin(group)->sin_addr, rtableid));
}
void
mrt_mcast_del(struct rtentry *rt, unsigned int rtableid)
{
struct ifnet *ifp;
int error;
/* Remove all timers related to this route. */
rt_timer_remove_all(rt);
free(rt->rt_llinfo, M_MRTABLE, sizeof(struct mfc));
rt->rt_llinfo = NULL;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return;
error = rtdeletemsg(rt, ifp, rtableid);
if_put(ifp);
if (error)
DPRINTF("delete route error %d\n", error);
mrt_count[rtableid]--;
}
4
4
4
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
/* $OpenBSD: uvm_swap.c,v 1.164 2022/08/29 11:09:31 mpi Exp $ */
/* $NetBSD: uvm_swap.c,v 1.40 2000/11/17 11:39:39 mrg Exp $ */
/*
* Copyright (c) 1995, 1996, 1997 Matthew R. Green
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: NetBSD: vm_swap.c,v 1.52 1997/12/02 13:47:37 pk Exp
* from: Id: uvm_swap.c,v 1.1.2.42 1998/02/02 20:38:06 chuck Exp
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/namei.h>
#include <sys/disklabel.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/extent.h>
#include <sys/blist.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/pool.h>
#include <sys/syscallargs.h>
#include <sys/swap.h>
#include <sys/disk.h>
#include <sys/task.h>
#include <sys/pledge.h>
#if defined(NFSCLIENT)
#include <sys/socket.h>
#include <netinet/in.h>
#include <nfs/nfsproto.h>
#include <nfs/nfsdiskless.h>
#endif
#include <uvm/uvm.h>
#ifdef UVM_SWAP_ENCRYPT
#include <uvm/uvm_swap_encrypt.h>
#endif
#include <sys/specdev.h>
#include "vnd.h"
/*
* uvm_swap.c: manage configuration and i/o to swap space.
*/
/*
* swap space is managed in the following way:
*
* each swap partition or file is described by a "swapdev" structure.
* each "swapdev" structure contains a "swapent" structure which contains
* information that is passed up to the user (via system calls).
*
* each swap partition is assigned a "priority" (int) which controls
* swap partition usage.
*
* the system maintains a global data structure describing all swap
* partitions/files. there is a sorted LIST of "swappri" structures
* which describe "swapdev"'s at that priority. this LIST is headed
* by the "swap_priority" global var. each "swappri" contains a
* TAILQ of "swapdev" structures at that priority.
*
* locking:
* - swap_syscall_lock (sleep lock): this lock serializes the swapctl
* system call and prevents the swap priority list from changing
* while we are in the middle of a system call (e.g. SWAP_STATS).
* - uvm_swap_data_lock (mutex): this lock protects all swap data
* structures including the priority list, the swapdev structures,
* and the swapmap arena.
*
* each swap device has the following info:
* - swap device in use (could be disabled, preventing future use)
* - swap enabled (allows new allocations on swap)
* - map info in /dev/drum
* - vnode pointer
* for swap files only:
* - block size
* - max byte count in buffer
* - buffer
* - credentials to use when doing i/o to file
*
* userland controls and configures swap with the swapctl(2) system call.
* the sys_swapctl performs the following operations:
* [1] SWAP_NSWAP: returns the number of swap devices currently configured
* [2] SWAP_STATS: given a pointer to an array of swapent structures
* (passed in via "arg") of a size passed in via "misc" ... we load
* the current swap config into the array.
* [3] SWAP_ON: given a pathname in arg (could be device or file) and a
* priority in "misc", start swapping on it.
* [4] SWAP_OFF: as SWAP_ON, but stops swapping to a device
* [5] SWAP_CTL: changes the priority of a swap device (new priority in
* "misc")
*/
/*
* swapdev: describes a single swap partition/file
*
* note the following should be true:
* swd_inuse <= swd_nblks [number of blocks in use is <= total blocks]
* swd_nblks <= swd_mapsize [because mapsize includes disklabel]
*/
struct swapdev {
struct swapent swd_se;
#define swd_dev swd_se.se_dev /* device id */
#define swd_flags swd_se.se_flags /* flags:inuse/enable/fake */
#define swd_priority swd_se.se_priority /* our priority */
#define swd_inuse swd_se.se_inuse /* blocks used */
#define swd_nblks swd_se.se_nblks /* total blocks */
char *swd_path; /* saved pathname of device */
int swd_pathlen; /* length of pathname */
int swd_npages; /* #pages we can use */
int swd_npginuse; /* #pages in use */
int swd_npgbad; /* #pages bad */
int swd_drumoffset; /* page0 offset in drum */
int swd_drumsize; /* #pages in drum */
blist_t swd_blist; /* blist for this swapdev */
struct vnode *swd_vp; /* backing vnode */
TAILQ_ENTRY(swapdev) swd_next; /* priority tailq */
int swd_bsize; /* blocksize (bytes) */
int swd_maxactive; /* max active i/o reqs */
int swd_active; /* # of active i/o reqs */
struct bufq swd_bufq;
struct ucred *swd_cred; /* cred for file access */
#ifdef UVM_SWAP_ENCRYPT
#define SWD_KEY_SHIFT 7 /* One key per 0.5 MByte */
#define SWD_KEY(x,y) &((x)->swd_keys[((y) - (x)->swd_drumoffset) >> SWD_KEY_SHIFT])
#define SWD_KEY_SIZE(x) (((x) + (1 << SWD_KEY_SHIFT) - 1) >> SWD_KEY_SHIFT)
#define SWD_DCRYPT_SHIFT 5
#define SWD_DCRYPT_BITS 32
#define SWD_DCRYPT_MASK (SWD_DCRYPT_BITS - 1)
#define SWD_DCRYPT_OFF(x) ((x) >> SWD_DCRYPT_SHIFT)
#define SWD_DCRYPT_BIT(x) ((x) & SWD_DCRYPT_MASK)
#define SWD_DCRYPT_SIZE(x) (SWD_DCRYPT_OFF((x) + SWD_DCRYPT_MASK) * sizeof(u_int32_t))
u_int32_t *swd_decrypt; /* bitmap for decryption */
struct swap_key *swd_keys; /* keys for different parts */
#endif
};
/*
* swap device priority entry; the list is kept sorted on `spi_priority'.
*/
struct swappri {
int spi_priority; /* priority */
TAILQ_HEAD(spi_swapdev, swapdev) spi_swapdev;
/* tailq of swapdevs at this priority */
LIST_ENTRY(swappri) spi_swappri; /* global list of pri's */
};
/*
* The following two structures are used to keep track of data transfers
* on swap devices associated with regular files.
* NOTE: this code is more or less a copy of vnd.c; we use the same
* structure names here to ease porting..
*/
struct vndxfer {
struct buf *vx_bp; /* Pointer to parent buffer */
struct swapdev *vx_sdp;
int vx_error;
int vx_pending; /* # of pending aux buffers */
int vx_flags;
#define VX_BUSY 1
#define VX_DEAD 2
};
struct vndbuf {
struct buf vb_buf;
struct vndxfer *vb_vnx;
struct task vb_task;
};
/*
* We keep a of pool vndbuf's and vndxfer structures.
*/
struct pool vndxfer_pool;
struct pool vndbuf_pool;
/*
* local variables
*/
struct extent *swapmap; /* controls the mapping of /dev/drum */
/* list of all active swap devices [by priority] */
LIST_HEAD(swap_priority, swappri);
struct swap_priority swap_priority; /* [S] */
/* locks */
struct mutex uvm_swap_data_lock = MUTEX_INITIALIZER(IPL_NONE);
struct rwlock swap_syscall_lock = RWLOCK_INITIALIZER("swplk");
struct mutex oommtx = MUTEX_INITIALIZER(IPL_VM);
struct vm_page *oompps[SWCLUSTPAGES];
int oom = 0;
/*
* prototypes
*/
void swapdrum_add(struct swapdev *, int);
struct swapdev *swapdrum_getsdp(int);
struct swapdev *swaplist_find(struct vnode *, int);
void swaplist_insert(struct swapdev *,
struct swappri *, int);
void swaplist_trim(void);
int swap_on(struct proc *, struct swapdev *);
int swap_off(struct proc *, struct swapdev *);
void sw_reg_strategy(struct swapdev *, struct buf *, int);
void sw_reg_iodone(struct buf *);
void sw_reg_iodone_internal(void *);
void sw_reg_start(struct swapdev *);
int uvm_swap_io(struct vm_page **, int, int, int);
void swapmount(void);
int uvm_swap_allocpages(struct vm_page **, int, int);
#ifdef UVM_SWAP_ENCRYPT
/* for swap encrypt */
void uvm_swap_markdecrypt(struct swapdev *, int, int, int);
boolean_t uvm_swap_needdecrypt(struct swapdev *, int);
void uvm_swap_initcrypt(struct swapdev *, int);
#endif
/*
* uvm_swap_init: init the swap system data structures and locks
*
* => called at boot time from init_main.c after the filesystems
* are brought up (which happens after uvm_init())
*/
void
uvm_swap_init(void)
{
int error;
/*
* first, init the swap list, its counter, and its lock.
* then get a handle on the vnode for /dev/drum by using
* the its dev_t number ("swapdev", from MD conf.c).
*/
LIST_INIT(&swap_priority);
uvmexp.nswapdev = 0;
if (!swapdev_vp && bdevvp(swapdev, &swapdev_vp))
panic("uvm_swap_init: can't get vnode for swap device");
/*
* create swap block extent to map /dev/drum. The extent spans
* 1 to INT_MAX allows 2 gigablocks of swap space. Note that
* block 0 is reserved (used to indicate an allocation failure,
* or no allocation).
*/
swapmap = extent_create("swapmap", 1, INT_MAX,
M_VMSWAP, 0, 0, EX_NOWAIT);
if (swapmap == 0)
panic("uvm_swap_init: extent_create failed");
/* allocate pools for structures used for swapping to files. */
pool_init(&vndxfer_pool, sizeof(struct vndxfer), 0, IPL_BIO, 0,
"swp vnx", NULL);
pool_init(&vndbuf_pool, sizeof(struct vndbuf), 0, IPL_BIO, 0,
"swp vnd", NULL);
/* allocate pages for OOM situations. */
error = uvm_swap_allocpages(oompps, SWCLUSTPAGES, UVM_PLA_NOWAIT);
KASSERT(error == 0);
/* Setup the initial swap partition */
swapmount();
}
#ifdef UVM_SWAP_ENCRYPT
void
uvm_swap_initcrypt_all(void)
{
struct swapdev *sdp;
struct swappri *spp;
int npages;
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
if (sdp->swd_decrypt == NULL) {
npages = dbtob((uint64_t)sdp->swd_nblks) >>
PAGE_SHIFT;
uvm_swap_initcrypt(sdp, npages);
}
}
}
}
void
uvm_swap_initcrypt(struct swapdev *sdp, int npages)
{
/*
* keep information if a page needs to be decrypted when we get it
* from the swap device.
* We cannot chance a malloc later, if we are doing ASYNC puts,
* we may not call malloc with M_WAITOK. This consumes only
* 8KB memory for a 256MB swap partition.
*/
sdp->swd_decrypt = malloc(SWD_DCRYPT_SIZE(npages), M_VMSWAP,
M_WAITOK|M_ZERO);
sdp->swd_keys = mallocarray(SWD_KEY_SIZE(npages),
sizeof(struct swap_key), M_VMSWAP, M_WAITOK|M_ZERO);
}
#endif /* UVM_SWAP_ENCRYPT */
int
uvm_swap_allocpages(struct vm_page **pps, int npages, int flags)
{
struct pglist pgl;
int error, i;
KASSERT(npages <= SWCLUSTPAGES);
TAILQ_INIT(&pgl);
again:
error = uvm_pglistalloc(npages * PAGE_SIZE, dma_constraint.ucr_low,
dma_constraint.ucr_high, 0, 0, &pgl, npages, flags);
if (error && (curproc == uvm.pagedaemon_proc)) {
mtx_enter(&oommtx);
if (oom) {
msleep_nsec(&oom, &oommtx, PVM | PNORELOCK,
"oom", INFSLP);
goto again;
}
oom = 1;
for (i = 0; i < npages; i++) {
pps[i] = oompps[i];
atomic_setbits_int(&pps[i]->pg_flags, PG_BUSY);
}
mtx_leave(&oommtx);
return 0;
}
if (error)
return error;
for (i = 0; i < npages; i++) {
pps[i] = TAILQ_FIRST(&pgl);
/* *sigh* */
atomic_setbits_int(&pps[i]->pg_flags, PG_BUSY);
TAILQ_REMOVE(&pgl, pps[i], pageq);
}
return 0;
}
void
uvm_swap_freepages(struct vm_page **pps, int npages)
{
int i;
if (pps[0] == oompps[0]) {
for (i = 0; i < npages; i++)
uvm_pageclean(pps[i]);
mtx_enter(&oommtx);
KASSERT(oom == 1);
oom = 0;
mtx_leave(&oommtx);
wakeup(&oom);
return;
}
uvm_lock_pageq();
for (i = 0; i < npages; i++)
uvm_pagefree(pps[i]);
uvm_unlock_pageq();
}
#ifdef UVM_SWAP_ENCRYPT
/*
* Mark pages on the swap device for later decryption
*/
void
uvm_swap_markdecrypt(struct swapdev *sdp, int startslot, int npages,
int decrypt)
{
int pagestart, i;
int off, bit;
if (!sdp)
return;
pagestart = startslot - sdp->swd_drumoffset;
for (i = 0; i < npages; i++, pagestart++) {
off = SWD_DCRYPT_OFF(pagestart);
bit = SWD_DCRYPT_BIT(pagestart);
if (decrypt)
/* pages read need decryption */
sdp->swd_decrypt[off] |= 1 << bit;
else
/* pages read do not need decryption */
sdp->swd_decrypt[off] &= ~(1 << bit);
}
}
/*
* Check if the page that we got from disk needs to be decrypted
*/
boolean_t
uvm_swap_needdecrypt(struct swapdev *sdp, int off)
{
if (!sdp)
return FALSE;
off -= sdp->swd_drumoffset;
return sdp->swd_decrypt[SWD_DCRYPT_OFF(off)] & (1 << SWD_DCRYPT_BIT(off)) ?
TRUE : FALSE;
}
void
uvm_swap_finicrypt_all(void)
{
struct swapdev *sdp;
struct swappri *spp;
struct swap_key *key;
unsigned int nkeys;
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
if (sdp->swd_decrypt == NULL)
continue;
nkeys = dbtob((uint64_t)sdp->swd_nblks) >> PAGE_SHIFT;
key = sdp->swd_keys + (SWD_KEY_SIZE(nkeys) - 1);
do {
if (key->refcount != 0)
swap_key_delete(key);
} while (key-- != sdp->swd_keys);
}
}
}
#endif /* UVM_SWAP_ENCRYPT */
/*
* swaplist functions: functions that operate on the list of swap
* devices on the system.
*/
/*
* swaplist_insert: insert swap device "sdp" into the global list
*
* => caller must hold both swap_syscall_lock and uvm_swap_data_lock
* => caller must provide a newly allocated swappri structure (we will
* FREE it if we don't need it... this it to prevent allocation
* blocking here while adding swap)
*/
void
swaplist_insert(struct swapdev *sdp, struct swappri *newspp, int priority)
{
struct swappri *spp, *pspp;
KASSERT(rw_write_held(&swap_syscall_lock));
MUTEX_ASSERT_LOCKED(&uvm_swap_data_lock);
/*
* find entry at or after which to insert the new device.
*/
pspp = NULL;
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
if (priority <= spp->spi_priority)
break;
pspp = spp;
}
/*
* new priority?
*/
if (spp == NULL || spp->spi_priority != priority) {
spp = newspp; /* use newspp! */
spp->spi_priority = priority;
TAILQ_INIT(&spp->spi_swapdev);
if (pspp)
LIST_INSERT_AFTER(pspp, spp, spi_swappri);
else
LIST_INSERT_HEAD(&swap_priority, spp, spi_swappri);
} else {
/* we don't need a new priority structure, free it */
free(newspp, M_VMSWAP, sizeof(*newspp));
}
/*
* priority found (or created). now insert on the priority's
* tailq list and bump the total number of swapdevs.
*/
sdp->swd_priority = priority;
TAILQ_INSERT_TAIL(&spp->spi_swapdev, sdp, swd_next);
uvmexp.nswapdev++;
}
/*
* swaplist_find: find and optionally remove a swap device from the
* global list.
*
* => caller must hold both swap_syscall_lock and uvm_swap_data_lock
* => we return the swapdev we found (and removed)
*/
struct swapdev *
swaplist_find(struct vnode *vp, boolean_t remove)
{
struct swapdev *sdp;
struct swappri *spp;
KASSERT(rw_write_held(&swap_syscall_lock));
MUTEX_ASSERT_LOCKED(&uvm_swap_data_lock);
/*
* search the lists for the requested vp
*/
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
if (sdp->swd_vp != vp)
continue;
if (remove) {
TAILQ_REMOVE(&spp->spi_swapdev, sdp, swd_next);
uvmexp.nswapdev--;
}
return (sdp);
}
}
return (NULL);
}
/*
* swaplist_trim: scan priority list for empty priority entries and kill
* them.
*
* => caller must hold both swap_syscall_lock and uvm_swap_data_lock
*/
void
swaplist_trim(void)
{
struct swappri *spp, *nextspp;
KASSERT(rw_write_held(&swap_syscall_lock));
MUTEX_ASSERT_LOCKED(&uvm_swap_data_lock);
LIST_FOREACH_SAFE(spp, &swap_priority, spi_swappri, nextspp) {
if (!TAILQ_EMPTY(&spp->spi_swapdev))
continue;
LIST_REMOVE(spp, spi_swappri);
free(spp, M_VMSWAP, sizeof(*spp));
}
}
/*
* swapdrum_add: add a "swapdev"'s blocks into /dev/drum's area.
*
* => caller must hold swap_syscall_lock
* => uvm_swap_data_lock should be unlocked (we may sleep)
*/
void
swapdrum_add(struct swapdev *sdp, int npages)
{
u_long result;
if (extent_alloc(swapmap, npages, EX_NOALIGN, 0, EX_NOBOUNDARY,
EX_WAITOK, &result))
panic("swapdrum_add");
sdp->swd_drumoffset = result;
sdp->swd_drumsize = npages;
}
/*
* swapdrum_getsdp: given a page offset in /dev/drum, convert it back
* to the "swapdev" that maps that section of the drum.
*
* => each swapdev takes one big contig chunk of the drum
* => caller must hold uvm_swap_data_lock
*/
struct swapdev *
swapdrum_getsdp(int pgno)
{
struct swapdev *sdp;
struct swappri *spp;
MUTEX_ASSERT_LOCKED(&uvm_swap_data_lock);
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
if (pgno >= sdp->swd_drumoffset &&
pgno < (sdp->swd_drumoffset + sdp->swd_drumsize)) {
return sdp;
}
}
}
return NULL;
}
/*
* sys_swapctl: main entry point for swapctl(2) system call
* [with two helper functions: swap_on and swap_off]
*/
int
sys_swapctl(struct proc *p, void *v, register_t *retval)
{
struct sys_swapctl_args /* {
syscallarg(int) cmd;
syscallarg(void *) arg;
syscallarg(int) misc;
} */ *uap = (struct sys_swapctl_args *)v;
struct vnode *vp;
struct nameidata nd;
struct swappri *spp;
struct swapdev *sdp;
struct swapent *sep;
char userpath[MAXPATHLEN];
size_t len;
int count, error, misc;
int priority;
misc = SCARG(uap, misc);
if ((error = pledge_swapctl(p, SCARG(uap, cmd))))
return error;
/*
* ensure serialized syscall access by grabbing the swap_syscall_lock
*/
rw_enter_write(&swap_syscall_lock);
/*
* we handle the non-priv NSWAP and STATS request first.
*
* SWAP_NSWAP: return number of config'd swap devices
* [can also be obtained with uvmexp sysctl]
*/
if (SCARG(uap, cmd) == SWAP_NSWAP) {
*retval = uvmexp.nswapdev;
error = 0;
goto out;
}
/*
* SWAP_STATS: get stats on current # of configured swap devs
*
* note that the swap_priority list can't change as long
* as we are holding the swap_syscall_lock. we don't want
* to grab the uvm_swap_data_lock because we may fault&sleep during
* copyout() and we don't want to be holding that lock then!
*/
if (SCARG(uap, cmd) == SWAP_STATS) {
sep = (struct swapent *)SCARG(uap, arg);
count = 0;
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
if (count >= misc)
continue;
sdp->swd_inuse =
btodb((u_int64_t)sdp->swd_npginuse <<
PAGE_SHIFT);
error = copyout(&sdp->swd_se, sep,
sizeof(struct swapent));
if (error)
goto out;
/* now copy out the path if necessary */
error = copyoutstr(sdp->swd_path,
sep->se_path, sizeof(sep->se_path), NULL);
if (error)
goto out;
count++;
sep++;
}
}
*retval = count;
error = 0;
goto out;
}
/* all other requests require superuser privs. verify. */
if ((error = suser(p)))
goto out;
/*
* at this point we expect a path name in arg. we will
* use namei() to gain a vnode reference (vref), and lock
* the vnode (VOP_LOCK).
*/
error = copyinstr(SCARG(uap, arg), userpath, sizeof(userpath), &len);
if (error)
goto out;
disk_map(userpath, userpath, sizeof(userpath), DM_OPENBLCK);
NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, userpath, p);
if ((error = namei(&nd)))
goto out;
vp = nd.ni_vp;
/* note: "vp" is referenced and locked */
error = 0; /* assume no error */
switch(SCARG(uap, cmd)) {
case SWAP_DUMPDEV:
if (vp->v_type != VBLK) {
error = ENOTBLK;
break;
}
dumpdev = vp->v_rdev;
break;
case SWAP_CTL:
/*
* get new priority, remove old entry (if any) and then
* reinsert it in the correct place. finally, prune out
* any empty priority structures.
*/
priority = SCARG(uap, misc);
spp = malloc(sizeof *spp, M_VMSWAP, M_WAITOK);
mtx_enter(&uvm_swap_data_lock);
if ((sdp = swaplist_find(vp, 1)) == NULL) {
error = ENOENT;
} else {
swaplist_insert(sdp, spp, priority);
swaplist_trim();
}
mtx_leave(&uvm_swap_data_lock);
if (error)
free(spp, M_VMSWAP, sizeof(*spp));
break;
case SWAP_ON:
/*
* If the device is a regular file, make sure the filesystem
* can be used for swapping.
*/
if (vp->v_type == VREG &&
(vp->v_mount->mnt_flag & MNT_SWAPPABLE) == 0) {
error = ENOTSUP;
break;
}
/*
* check for duplicates. if none found, then insert a
* dummy entry on the list to prevent someone else from
* trying to enable this device while we are working on
* it.
*/
priority = SCARG(uap, misc);
sdp = malloc(sizeof *sdp, M_VMSWAP, M_WAITOK|M_ZERO);
spp = malloc(sizeof *spp, M_VMSWAP, M_WAITOK);
sdp->swd_flags = SWF_FAKE; /* placeholder only */
sdp->swd_vp = vp;
sdp->swd_dev = (vp->v_type == VBLK) ? vp->v_rdev : NODEV;
/*
* XXX Is NFS elaboration necessary?
*/
if (vp->v_type == VREG) {
sdp->swd_cred = crdup(p->p_ucred);
}
mtx_enter(&uvm_swap_data_lock);
if (swaplist_find(vp, 0) != NULL) {
error = EBUSY;
mtx_leave(&uvm_swap_data_lock);
if (vp->v_type == VREG) {
crfree(sdp->swd_cred);
}
free(sdp, M_VMSWAP, sizeof *sdp);
free(spp, M_VMSWAP, sizeof *spp);
break;
}
swaplist_insert(sdp, spp, priority);
mtx_leave(&uvm_swap_data_lock);
sdp->swd_pathlen = len;
sdp->swd_path = malloc(sdp->swd_pathlen, M_VMSWAP, M_WAITOK);
strlcpy(sdp->swd_path, userpath, len);
/*
* we've now got a FAKE placeholder in the swap list.
* now attempt to enable swap on it. if we fail, undo
* what we've done and kill the fake entry we just inserted.
* if swap_on is a success, it will clear the SWF_FAKE flag
*/
if ((error = swap_on(p, sdp)) != 0) {
mtx_enter(&uvm_swap_data_lock);
(void) swaplist_find(vp, 1); /* kill fake entry */
swaplist_trim();
mtx_leave(&uvm_swap_data_lock);
if (vp->v_type == VREG) {
crfree(sdp->swd_cred);
}
free(sdp->swd_path, M_VMSWAP, sdp->swd_pathlen);
free(sdp, M_VMSWAP, sizeof(*sdp));
break;
}
break;
case SWAP_OFF:
mtx_enter(&uvm_swap_data_lock);
if ((sdp = swaplist_find(vp, 0)) == NULL) {
mtx_leave(&uvm_swap_data_lock);
error = ENXIO;
break;
}
/*
* If a device isn't in use or enabled, we
* can't stop swapping from it (again).
*/
if ((sdp->swd_flags & (SWF_INUSE|SWF_ENABLE)) == 0) {
mtx_leave(&uvm_swap_data_lock);
error = EBUSY;
break;
}
/*
* do the real work.
*/
error = swap_off(p, sdp);
break;
default:
error = EINVAL;
}
/* done! release the ref gained by namei() and unlock. */
vput(vp);
out:
rw_exit_write(&swap_syscall_lock);
return (error);
}
/*
* swap_on: attempt to enable a swapdev for swapping. note that the
* swapdev is already on the global list, but disabled (marked
* SWF_FAKE).
*
* => we avoid the start of the disk (to protect disk labels)
* => caller should leave uvm_swap_data_lock unlocked, we may lock it
* if needed.
*/
int
swap_on(struct proc *p, struct swapdev *sdp)
{
struct vnode *vp;
int error, npages, nblocks, size;
long addr;
struct vattr va;
#if defined(NFSCLIENT)
extern const struct vops nfs_vops;
#endif /* defined(NFSCLIENT) */
dev_t dev;
/*
* we want to enable swapping on sdp. the swd_vp contains
* the vnode we want (locked and ref'd), and the swd_dev
* contains the dev_t of the file, if it a block device.
*/
vp = sdp->swd_vp;
dev = sdp->swd_dev;
#if NVND > 0
/* no swapping to vnds. */
if (bdevsw[major(dev)].d_strategy == vndstrategy)
return (EOPNOTSUPP);
#endif
/*
* open the swap file (mostly useful for block device files to
* let device driver know what is up).
*
* we skip the open/close for root on swap because the root
* has already been opened when root was mounted (mountroot).
*/
if (vp != rootvp) {
if ((error = VOP_OPEN(vp, FREAD|FWRITE, p->p_ucred, p)))
return (error);
}
/* XXX this only works for block devices */
/*
* we now need to determine the size of the swap area. for
* block specials we can call the d_psize function.
* for normal files, we must stat [get attrs].
*
* we put the result in nblks.
* for normal files, we also want the filesystem block size
* (which we get with statfs).
*/
switch (vp->v_type) {
case VBLK:
if (bdevsw[major(dev)].d_psize == 0 ||
(nblocks = (*bdevsw[major(dev)].d_psize)(dev)) == -1) {
error = ENXIO;
goto bad;
}
break;
case VREG:
if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)))
goto bad;
nblocks = (int)btodb(va.va_size);
if ((error =
VFS_STATFS(vp->v_mount, &vp->v_mount->mnt_stat, p)) != 0)
goto bad;
sdp->swd_bsize = vp->v_mount->mnt_stat.f_iosize;
/*
* limit the max # of outstanding I/O requests we issue
* at any one time. take it easy on NFS servers.
*/
#if defined(NFSCLIENT)
if (vp->v_op == &nfs_vops)
sdp->swd_maxactive = 2; /* XXX */
else
#endif /* defined(NFSCLIENT) */
sdp->swd_maxactive = 8; /* XXX */
bufq_init(&sdp->swd_bufq, BUFQ_FIFO);
break;
default:
error = ENXIO;
goto bad;
}
/*
* save nblocks in a safe place and convert to pages.
*/
sdp->swd_nblks = nblocks;
npages = dbtob((u_int64_t)nblocks) >> PAGE_SHIFT;
/*
* for block special files, we want to make sure that leave
* the disklabel and bootblocks alone, so we arrange to skip
* over them (arbitrarily choosing to skip PAGE_SIZE bytes).
* note that because of this the "size" can be less than the
* actual number of blocks on the device.
*/
if (vp->v_type == VBLK) {
/* we use pages 1 to (size - 1) [inclusive] */
size = npages - 1;
addr = 1;
} else {
/* we use pages 0 to (size - 1) [inclusive] */
size = npages;
addr = 0;
}
/*
* make sure we have enough blocks for a reasonable sized swap
* area. we want at least one page.
*/
if (size < 1) {
error = EINVAL;
goto bad;
}
/*
* now we need to allocate a blist to manage this swap device
*/
sdp->swd_blist = blist_create(npages);
/* mark all expect the `saved' region free. */
blist_free(sdp->swd_blist, addr, size);
#ifdef HIBERNATE
/*
* Lock down the last region of primary disk swap, in case
* hibernate needs to place a signature there.
*/
if (dev == swdevt[0].sw_dev && vp->v_type == VBLK && size > 3 ) {
if (blist_fill(sdp->swd_blist, npages - 1, 1) != 1)
panic("hibernate reserve");
}
#endif
/* add a ref to vp to reflect usage as a swap device. */
vref(vp);
#ifdef UVM_SWAP_ENCRYPT
if (uvm_doswapencrypt)
uvm_swap_initcrypt(sdp, npages);
#endif
/* now add the new swapdev to the drum and enable. */
swapdrum_add(sdp, npages);
sdp->swd_npages = size;
mtx_enter(&uvm_swap_data_lock);
sdp->swd_flags &= ~SWF_FAKE; /* going live */
sdp->swd_flags |= (SWF_INUSE|SWF_ENABLE);
uvmexp.swpages += size;
mtx_leave(&uvm_swap_data_lock);
return (0);
/*
* failure: clean up and return error.
*/
bad:
if (vp != rootvp)
(void)VOP_CLOSE(vp, FREAD|FWRITE, p->p_ucred, p);
return (error);
}
/*
* swap_off: stop swapping on swapdev
*
* => swap data should be locked, we will unlock.
*/
int
swap_off(struct proc *p, struct swapdev *sdp)
{
int npages = sdp->swd_npages;
int error = 0;
KASSERT(rw_write_held(&swap_syscall_lock));
MUTEX_ASSERT_LOCKED(&uvm_swap_data_lock);
/* disable the swap area being removed */
sdp->swd_flags &= ~SWF_ENABLE;
mtx_leave(&uvm_swap_data_lock);
/*
* the idea is to find all the pages that are paged out to this
* device, and page them all in. in uvm, swap-backed pageable
* memory can take two forms: aobjs and anons. call the
* swapoff hook for each subsystem to bring in pages.
*/
if (uao_swap_off(sdp->swd_drumoffset,
sdp->swd_drumoffset + sdp->swd_drumsize) ||
amap_swap_off(sdp->swd_drumoffset,
sdp->swd_drumoffset + sdp->swd_drumsize)) {
error = ENOMEM;
} else if (sdp->swd_npginuse > sdp->swd_npgbad) {
error = EBUSY;
}
if (error) {
mtx_enter(&uvm_swap_data_lock);
sdp->swd_flags |= SWF_ENABLE;
mtx_leave(&uvm_swap_data_lock);
return error;
}
/*
* done with the vnode and saved creds.
* drop our ref on the vnode before calling VOP_CLOSE()
* so that spec_close() can tell if this is the last close.
*/
if (sdp->swd_vp->v_type == VREG) {
crfree(sdp->swd_cred);
}
vrele(sdp->swd_vp);
if (sdp->swd_vp != rootvp) {
(void) VOP_CLOSE(sdp->swd_vp, FREAD|FWRITE, p->p_ucred, p);
}
mtx_enter(&uvm_swap_data_lock);
uvmexp.swpages -= npages;
if (swaplist_find(sdp->swd_vp, 1) == NULL)
panic("swap_off: swapdev not in list");
swaplist_trim();
mtx_leave(&uvm_swap_data_lock);
/*
* free all resources!
*/
extent_free(swapmap, sdp->swd_drumoffset, sdp->swd_drumsize,
EX_WAITOK);
blist_destroy(sdp->swd_blist);
/* free sdp->swd_path ? */
free(sdp, M_VMSWAP, sizeof(*sdp));
return (0);
}
/*
* /dev/drum interface and i/o functions
*/
/*
* swstrategy: perform I/O on the drum
*
* => we must map the i/o request from the drum to the correct swapdev.
*/
void
swstrategy(struct buf *bp)
{
struct swapdev *sdp;
int s, pageno, bn;
/*
* convert block number to swapdev. note that swapdev can't
* be yanked out from under us because we are holding resources
* in it (i.e. the blocks we are doing I/O on).
*/
pageno = dbtob((u_int64_t)bp->b_blkno) >> PAGE_SHIFT;
mtx_enter(&uvm_swap_data_lock);
sdp = swapdrum_getsdp(pageno);
mtx_leave(&uvm_swap_data_lock);
if (sdp == NULL) {
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
s = splbio();
biodone(bp);
splx(s);
return;
}
/* convert drum page number to block number on this swapdev. */
pageno -= sdp->swd_drumoffset; /* page # on swapdev */
bn = btodb((u_int64_t)pageno << PAGE_SHIFT); /* convert to diskblock */
/*
* for block devices we finish up here.
* for regular files we have to do more work which we delegate
* to sw_reg_strategy().
*/
switch (sdp->swd_vp->v_type) {
default:
panic("swstrategy: vnode type 0x%x", sdp->swd_vp->v_type);
case VBLK:
/*
* must convert "bp" from an I/O on /dev/drum to an I/O
* on the swapdev (sdp).
*/
s = splbio();
buf_replacevnode(bp, sdp->swd_vp);
bp->b_blkno = bn;
splx(s);
VOP_STRATEGY(bp->b_vp, bp);
return;
case VREG:
/* delegate to sw_reg_strategy function. */
sw_reg_strategy(sdp, bp, bn);
return;
}
/* NOTREACHED */
}
/*
* sw_reg_strategy: handle swap i/o to regular files
*/
void
sw_reg_strategy(struct swapdev *sdp, struct buf *bp, int bn)
{
struct vnode *vp;
struct vndxfer *vnx;
daddr_t nbn;
caddr_t addr;
off_t byteoff;
int s, off, nra, error, sz, resid;
/*
* allocate a vndxfer head for this transfer and point it to
* our buffer.
*/
vnx = pool_get(&vndxfer_pool, PR_WAITOK);
vnx->vx_flags = VX_BUSY;
vnx->vx_error = 0;
vnx->vx_pending = 0;
vnx->vx_bp = bp;
vnx->vx_sdp = sdp;
/*
* setup for main loop where we read filesystem blocks into
* our buffer.
*/
error = 0;
bp->b_resid = bp->b_bcount; /* nothing transferred yet! */
addr = bp->b_data; /* current position in buffer */
byteoff = dbtob((u_int64_t)bn);
for (resid = bp->b_resid; resid; resid -= sz) {
struct vndbuf *nbp;
/*
* translate byteoffset into block number. return values:
* vp = vnode of underlying device
* nbn = new block number (on underlying vnode dev)
* nra = num blocks we can read-ahead (excludes requested
* block)
*/
nra = 0;
error = VOP_BMAP(sdp->swd_vp, byteoff / sdp->swd_bsize,
&vp, &nbn, &nra);
if (error == 0 && nbn == -1) {
/*
* this used to just set error, but that doesn't
* do the right thing. Instead, it causes random
* memory errors. The panic() should remain until
* this condition doesn't destabilize the system.
*/
#if 1
panic("sw_reg_strategy: swap to sparse file");
#else
error = EIO; /* failure */
#endif
}
/*
* punt if there was an error or a hole in the file.
* we must wait for any i/o ops we have already started
* to finish before returning.
*
* XXX we could deal with holes here but it would be
* a hassle (in the write case).
*/
if (error) {
s = splbio();
vnx->vx_error = error; /* pass error up */
goto out;
}
/*
* compute the size ("sz") of this transfer (in bytes).
*/
off = byteoff % sdp->swd_bsize;
sz = (1 + nra) * sdp->swd_bsize - off;
if (sz > resid)
sz = resid;
/*
* now get a buf structure. note that the vb_buf is
* at the front of the nbp structure so that you can
* cast pointers between the two structure easily.
*/
nbp = pool_get(&vndbuf_pool, PR_WAITOK);
nbp->vb_buf.b_flags = bp->b_flags | B_CALL;
nbp->vb_buf.b_bcount = sz;
nbp->vb_buf.b_bufsize = sz;
nbp->vb_buf.b_error = 0;
nbp->vb_buf.b_data = addr;
nbp->vb_buf.b_bq = NULL;
nbp->vb_buf.b_blkno = nbn + btodb(off);
nbp->vb_buf.b_proc = bp->b_proc;
nbp->vb_buf.b_iodone = sw_reg_iodone;
nbp->vb_buf.b_vp = NULLVP;
nbp->vb_buf.b_vnbufs.le_next = NOLIST;
LIST_INIT(&nbp->vb_buf.b_dep);
/*
* set b_dirtyoff/end and b_validoff/end. this is
* required by the NFS client code (otherwise it will
* just discard our I/O request).
*/
if (bp->b_dirtyend == 0) {
nbp->vb_buf.b_dirtyoff = 0;
nbp->vb_buf.b_dirtyend = sz;
} else {
nbp->vb_buf.b_dirtyoff =
max(0, bp->b_dirtyoff - (bp->b_bcount-resid));
nbp->vb_buf.b_dirtyend =
min(sz,
max(0, bp->b_dirtyend - (bp->b_bcount-resid)));
}
if (bp->b_validend == 0) {
nbp->vb_buf.b_validoff = 0;
nbp->vb_buf.b_validend = sz;
} else {
nbp->vb_buf.b_validoff =
max(0, bp->b_validoff - (bp->b_bcount-resid));
nbp->vb_buf.b_validend =
min(sz,
max(0, bp->b_validend - (bp->b_bcount-resid)));
}
/* patch it back to the vnx */
nbp->vb_vnx = vnx;
task_set(&nbp->vb_task, sw_reg_iodone_internal, nbp);
s = splbio();
if (vnx->vx_error != 0) {
pool_put(&vndbuf_pool, nbp);
goto out;
}
vnx->vx_pending++;
/* assoc new buffer with underlying vnode */
bgetvp(vp, &nbp->vb_buf);
/* start I/O if we are not over our limit */
bufq_queue(&sdp->swd_bufq, &nbp->vb_buf);
sw_reg_start(sdp);
splx(s);
/*
* advance to the next I/O
*/
byteoff += sz;
addr += sz;
}
s = splbio();
out: /* Arrive here at splbio */
vnx->vx_flags &= ~VX_BUSY;
if (vnx->vx_pending == 0) {
if (vnx->vx_error != 0) {
bp->b_error = vnx->vx_error;
bp->b_flags |= B_ERROR;
}
pool_put(&vndxfer_pool, vnx);
biodone(bp);
}
splx(s);
}
/* sw_reg_start: start an I/O request on the requested swapdev. */
void
sw_reg_start(struct swapdev *sdp)
{
struct buf *bp;
/* XXX: recursion control */
if ((sdp->swd_flags & SWF_BUSY) != 0)
return;
sdp->swd_flags |= SWF_BUSY;
while (sdp->swd_active < sdp->swd_maxactive) {
bp = bufq_dequeue(&sdp->swd_bufq);
if (bp == NULL)
break;
sdp->swd_active++;
if ((bp->b_flags & B_READ) == 0)
bp->b_vp->v_numoutput++;
VOP_STRATEGY(bp->b_vp, bp);
}
sdp->swd_flags &= ~SWF_BUSY;
}
/*
* sw_reg_iodone: one of our i/o's has completed and needs post-i/o cleanup
*
* => note that we can recover the vndbuf struct by casting the buf ptr
*
* XXX:
* We only put this onto a taskq here, because of the maxactive game since
* it basically requires us to call back into VOP_STRATEGY() (where we must
* be able to sleep) via sw_reg_start().
*/
void
sw_reg_iodone(struct buf *bp)
{
struct vndbuf *vbp = (struct vndbuf *)bp;
task_add(systq, &vbp->vb_task);
}
void
sw_reg_iodone_internal(void *xvbp)
{
struct vndbuf *vbp = xvbp;
struct vndxfer *vnx = vbp->vb_vnx;
struct buf *pbp = vnx->vx_bp; /* parent buffer */
struct swapdev *sdp = vnx->vx_sdp;
int resid, s;
s = splbio();
resid = vbp->vb_buf.b_bcount - vbp->vb_buf.b_resid;
pbp->b_resid -= resid;
vnx->vx_pending--;
/* pass error upward */
if (vbp->vb_buf.b_error)
vnx->vx_error = vbp->vb_buf.b_error;
/* disassociate this buffer from the vnode (if any). */
if (vbp->vb_buf.b_vp != NULL) {
brelvp(&vbp->vb_buf);
}
/* kill vbp structure */
pool_put(&vndbuf_pool, vbp);
/*
* wrap up this transaction if it has run to completion or, in
* case of an error, when all auxiliary buffers have returned.
*/
if (vnx->vx_error != 0) {
/* pass error upward */
pbp->b_flags |= B_ERROR;
pbp->b_error = vnx->vx_error;
if ((vnx->vx_flags & VX_BUSY) == 0 && vnx->vx_pending == 0) {
pool_put(&vndxfer_pool, vnx);
biodone(pbp);
}
} else if (pbp->b_resid == 0) {
KASSERT(vnx->vx_pending == 0);
if ((vnx->vx_flags & VX_BUSY) == 0) {
pool_put(&vndxfer_pool, vnx);
biodone(pbp);
}
}
/*
* done! start next swapdev I/O if one is pending
*/
sdp->swd_active--;
sw_reg_start(sdp);
splx(s);
}
/*
* uvm_swap_alloc: allocate space on swap
*
* => allocation is done "round robin" down the priority list, as we
* allocate in a priority we "rotate" the tail queue.
* => space can be freed with uvm_swap_free
* => we return the page slot number in /dev/drum (0 == invalid slot)
* => we lock uvm_swap_data_lock
* => XXXMRG: "LESSOK" INTERFACE NEEDED TO EXTENT SYSTEM
*/
int
uvm_swap_alloc(int *nslots, boolean_t lessok)
{
struct swapdev *sdp;
struct swappri *spp;
/*
* no swap devices configured yet? definite failure.
*/
if (uvmexp.nswapdev < 1)
return 0;
/*
* lock data lock, convert slots into blocks, and enter loop
*/
KERNEL_ASSERT_LOCKED();
mtx_enter(&uvm_swap_data_lock);
ReTry: /* XXXMRG */
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
swblk_t result;
/* if it's not enabled, then we can't swap from it */
if ((sdp->swd_flags & SWF_ENABLE) == 0)
continue;
if (sdp->swd_npginuse + *nslots > sdp->swd_npages)
continue;
result = blist_alloc(sdp->swd_blist, *nslots);
if (result == SWAPBLK_NONE) {
continue;
}
KASSERT(result < sdp->swd_drumsize);
/*
* successful allocation! now rotate the tailq.
*/
TAILQ_REMOVE(&spp->spi_swapdev, sdp, swd_next);
TAILQ_INSERT_TAIL(&spp->spi_swapdev, sdp, swd_next);
sdp->swd_npginuse += *nslots;
uvmexp.swpginuse += *nslots;
mtx_leave(&uvm_swap_data_lock);
/* done! return drum slot number */
return result + sdp->swd_drumoffset;
}
}
/* XXXMRG: BEGIN HACK */
if (*nslots > 1 && lessok) {
*nslots = 1;
/* XXXMRG: ugh! blist should support this for us */
goto ReTry;
}
/* XXXMRG: END HACK */
mtx_leave(&uvm_swap_data_lock);
return 0; /* failed */
}
/*
* uvm_swapisfull: return true if all of available swap is allocated
* and in use.
*/
int
uvm_swapisfull(void)
{
int result;
mtx_enter(&uvm_swap_data_lock);
KASSERT(uvmexp.swpgonly <= uvmexp.swpages);
result = (uvmexp.swpgonly == uvmexp.swpages);
mtx_leave(&uvm_swap_data_lock);
return result;
}
/*
* uvm_swap_markbad: keep track of swap ranges where we've had i/o errors
*
* => we lock uvm_swap_data_lock
*/
void
uvm_swap_markbad(int startslot, int nslots)
{
struct swapdev *sdp;
mtx_enter(&uvm_swap_data_lock);
sdp = swapdrum_getsdp(startslot);
if (sdp != NULL) {
/*
* we just keep track of how many pages have been marked bad
* in this device, to make everything add up in swap_off().
* we assume here that the range of slots will all be within
* one swap device.
*/
sdp->swd_npgbad += nslots;
}
mtx_leave(&uvm_swap_data_lock);
}
/*
* uvm_swap_free: free swap slots
*
* => this can be all or part of an allocation made by uvm_swap_alloc
* => we lock uvm_swap_data_lock
*/
void
uvm_swap_free(int startslot, int nslots)
{
struct swapdev *sdp;
/*
* ignore attempts to free the "bad" slot.
*/
if (startslot == SWSLOT_BAD) {
return;
}
/*
* convert drum slot offset back to sdp, free the blocks
* in the extent, and return. must hold pri lock to do
* lookup and access the extent.
*/
KERNEL_LOCK();
mtx_enter(&uvm_swap_data_lock);
sdp = swapdrum_getsdp(startslot);
KASSERT(uvmexp.nswapdev >= 1);
KASSERT(sdp != NULL);
KASSERT(sdp->swd_npginuse >= nslots);
blist_free(sdp->swd_blist, startslot - sdp->swd_drumoffset, nslots);
sdp->swd_npginuse -= nslots;
uvmexp.swpginuse -= nslots;
mtx_leave(&uvm_swap_data_lock);
#ifdef UVM_SWAP_ENCRYPT
{
int i;
if (swap_encrypt_initialized) {
/* Dereference keys */
for (i = 0; i < nslots; i++)
if (uvm_swap_needdecrypt(sdp, startslot + i)) {
struct swap_key *key;
key = SWD_KEY(sdp, startslot + i);
if (key->refcount != 0)
SWAP_KEY_PUT(sdp, key);
}
/* Mark range as not decrypt */
uvm_swap_markdecrypt(sdp, startslot, nslots, 0);
}
}
#endif /* UVM_SWAP_ENCRYPT */
KERNEL_UNLOCK();
}
/*
* uvm_swap_put: put any number of pages into a contig place on swap
*
* => can be sync or async
*/
int
uvm_swap_put(int swslot, struct vm_page **ppsp, int npages, int flags)
{
int result;
result = uvm_swap_io(ppsp, swslot, npages, B_WRITE |
((flags & PGO_SYNCIO) ? 0 : B_ASYNC));
return (result);
}
/*
* uvm_swap_get: get a single page from swap
*
* => usually a sync op (from fault)
*/
int
uvm_swap_get(struct vm_page *page, int swslot, int flags)
{
int result;
atomic_inc_int(&uvmexp.nswget);
KASSERT(flags & PGO_SYNCIO);
if (swslot == SWSLOT_BAD) {
return VM_PAGER_ERROR;
}
KERNEL_LOCK();
result = uvm_swap_io(&page, swslot, 1, B_READ);
KERNEL_UNLOCK();
if (result == VM_PAGER_OK || result == VM_PAGER_PEND) {
/*
* this page is no longer only in swap.
*/
atomic_dec_int(&uvmexp.swpgonly);
}
return (result);
}
/*
* uvm_swap_io: do an i/o operation to swap
*/
int
uvm_swap_io(struct vm_page **pps, int startslot, int npages, int flags)
{
daddr_t startblk;
struct buf *bp;
vaddr_t kva;
int result, s, mapinflags, pflag, bounce = 0, i;
boolean_t write, async;
vaddr_t bouncekva;
struct vm_page *tpps[SWCLUSTPAGES];
int pdaemon = (curproc == uvm.pagedaemon_proc);
#ifdef UVM_SWAP_ENCRYPT
struct swapdev *sdp;
int encrypt = 0;
#endif
KERNEL_ASSERT_LOCKED();
write = (flags & B_READ) == 0;
async = (flags & B_ASYNC) != 0;
/* convert starting drum slot to block number */
startblk = btodb((u_int64_t)startslot << PAGE_SHIFT);
pflag = (async || pdaemon) ? PR_NOWAIT : PR_WAITOK;
bp = pool_get(&bufpool, pflag | PR_ZERO);
if (bp == NULL)
return (VM_PAGER_AGAIN);
/*
* map the pages into the kernel (XXX: currently required
* by buffer system).
*/
mapinflags = !write ? UVMPAGER_MAPIN_READ : UVMPAGER_MAPIN_WRITE;
if (!async)
mapinflags |= UVMPAGER_MAPIN_WAITOK;
kva = uvm_pagermapin(pps, npages, mapinflags);
if (kva == 0) {
pool_put(&bufpool, bp);
return (VM_PAGER_AGAIN);
}
#ifdef UVM_SWAP_ENCRYPT
if (write) {
/*
* Check if we need to do swap encryption on old pages.
* Later we need a different scheme, that swap encrypts
* all pages of a process that had at least one page swap
* encrypted. Then we might not need to copy all pages
* in the cluster, and avoid the memory overheard in
* swapping.
*/
if (uvm_doswapencrypt)
encrypt = 1;
}
if (swap_encrypt_initialized || encrypt) {
/*
* we need to know the swap device that we are swapping to/from
* to see if the pages need to be marked for decryption or
* actually need to be decrypted.
* XXX - does this information stay the same over the whole
* execution of this function?
*/
mtx_enter(&uvm_swap_data_lock);
sdp = swapdrum_getsdp(startslot);
mtx_leave(&uvm_swap_data_lock);
}
/*
* Check that we are dma capable for read (write always bounces
* through the swapencrypt anyway...
*/
if (write && encrypt) {
bounce = 1; /* bounce through swapencrypt always */
} else {
#else
{
#endif
for (i = 0; i < npages; i++) {
if (VM_PAGE_TO_PHYS(pps[i]) < dma_constraint.ucr_low ||
VM_PAGE_TO_PHYS(pps[i]) > dma_constraint.ucr_high) {
bounce = 1;
break;
}
}
}
if (bounce) {
int swmapflags, plaflags;
/* We always need write access. */
swmapflags = UVMPAGER_MAPIN_READ;
plaflags = UVM_PLA_NOWAIT;
if (!async) {
swmapflags |= UVMPAGER_MAPIN_WAITOK;
plaflags = UVM_PLA_WAITOK;
}
if (uvm_swap_allocpages(tpps, npages, plaflags)) {
pool_put(&bufpool, bp);
uvm_pagermapout(kva, npages);
return (VM_PAGER_AGAIN);
}
bouncekva = uvm_pagermapin(tpps, npages, swmapflags);
if (bouncekva == 0) {
pool_put(&bufpool, bp);
uvm_pagermapout(kva, npages);
uvm_swap_freepages(tpps, npages);
return (VM_PAGER_AGAIN);
}
}
/* encrypt to swap */
if (write && bounce) {
int i, opages;
caddr_t src, dst;
u_int64_t block;
src = (caddr_t) kva;
dst = (caddr_t) bouncekva;
block = startblk;
for (i = 0; i < npages; i++) {
#ifdef UVM_SWAP_ENCRYPT
struct swap_key *key;
if (encrypt) {
key = SWD_KEY(sdp, startslot + i);
SWAP_KEY_GET(sdp, key); /* add reference */
swap_encrypt(key, src, dst, block, PAGE_SIZE);
block += btodb(PAGE_SIZE);
} else {
#else
{
#endif /* UVM_SWAP_ENCRYPT */
memcpy(dst, src, PAGE_SIZE);
}
/* this just tells async callbacks to free */
atomic_setbits_int(&tpps[i]->pg_flags, PQ_ENCRYPT);
src += PAGE_SIZE;
dst += PAGE_SIZE;
}
uvm_pagermapout(kva, npages);
/* dispose of pages we dont use anymore */
opages = npages;
uvm_pager_dropcluster(NULL, NULL, pps, &opages,
PGO_PDFREECLUST);
kva = bouncekva;
}
/*
* prevent ASYNC reads.
* uvm_swap_io is only called from uvm_swap_get, uvm_swap_get
* assumes that all gets are SYNCIO. Just make sure here.
* XXXARTUBC - might not be true anymore.
*/
if (!write) {
flags &= ~B_ASYNC;
async = 0;
}
/*
* fill in the bp. we currently route our i/o through
* /dev/drum's vnode [swapdev_vp].
*/
bp->b_flags = B_BUSY | B_NOCACHE | B_RAW | (flags & (B_READ|B_ASYNC));
bp->b_proc = &proc0; /* XXX */
bp->b_vnbufs.le_next = NOLIST;
if (bounce)
bp->b_data = (caddr_t)bouncekva;
else
bp->b_data = (caddr_t)kva;
bp->b_bq = NULL;
bp->b_blkno = startblk;
LIST_INIT(&bp->b_dep);
s = splbio();
bp->b_vp = NULL;
buf_replacevnode(bp, swapdev_vp);
splx(s);
bp->b_bufsize = bp->b_bcount = (long)npages << PAGE_SHIFT;
/*
* for pageouts we must set "dirtyoff" [NFS client code needs it].
* and we bump v_numoutput (counter of number of active outputs).
*/
if (write) {
bp->b_dirtyoff = 0;
bp->b_dirtyend = npages << PAGE_SHIFT;
#ifdef UVM_SWAP_ENCRYPT
/* mark the pages in the drum for decryption */
if (swap_encrypt_initialized)
uvm_swap_markdecrypt(sdp, startslot, npages, encrypt);
#endif
s = splbio();
swapdev_vp->v_numoutput++;
splx(s);
}
/* for async ops we must set up the iodone handler. */
if (async) {
bp->b_flags |= B_CALL | (pdaemon ? B_PDAEMON : 0);
bp->b_iodone = uvm_aio_biodone;
}
/* now we start the I/O, and if async, return. */
VOP_STRATEGY(bp->b_vp, bp);
if (async)
return (VM_PAGER_PEND);
/* must be sync i/o. wait for it to finish */
(void) biowait(bp);
result = (bp->b_flags & B_ERROR) ? VM_PAGER_ERROR : VM_PAGER_OK;
/* decrypt swap */
if (!write && !(bp->b_flags & B_ERROR)) {
int i;
caddr_t data = (caddr_t)kva;
caddr_t dst = (caddr_t)kva;
u_int64_t block = startblk;
if (bounce)
data = (caddr_t)bouncekva;
for (i = 0; i < npages; i++) {
#ifdef UVM_SWAP_ENCRYPT
struct swap_key *key;
/* Check if we need to decrypt */
if (swap_encrypt_initialized &&
uvm_swap_needdecrypt(sdp, startslot + i)) {
key = SWD_KEY(sdp, startslot + i);
if (key->refcount == 0) {
result = VM_PAGER_ERROR;
break;
}
swap_decrypt(key, data, dst, block, PAGE_SIZE);
} else if (bounce) {
#else
if (bounce) {
#endif
memcpy(dst, data, PAGE_SIZE);
}
data += PAGE_SIZE;
dst += PAGE_SIZE;
block += btodb(PAGE_SIZE);
}
if (bounce)
uvm_pagermapout(bouncekva, npages);
}
/* kill the pager mapping */
uvm_pagermapout(kva, npages);
/* Not anymore needed, free after encryption/bouncing */
if (!write && bounce)
uvm_swap_freepages(tpps, npages);
/* now dispose of the buf */
s = splbio();
if (bp->b_vp)
brelvp(bp);
if (write && bp->b_vp)
vwakeup(bp->b_vp);
pool_put(&bufpool, bp);
splx(s);
/* finally return. */
return (result);
}
void
swapmount(void)
{
struct swapdev *sdp;
struct swappri *spp;
struct vnode *vp;
dev_t swap_dev = swdevt[0].sw_dev;
char *nam;
char path[MNAMELEN + 1];
if (swap_dev == NODEV)
return;
rw_enter_write(&swap_syscall_lock);
#if defined(NFSCLIENT)
if (swap_dev == NETDEV) {
extern struct nfs_diskless nfs_diskless;
snprintf(path, sizeof(path), "%s",
nfs_diskless.nd_swap.ndm_host);
vp = nfs_diskless.sw_vp;
goto gotit;
} else
#endif
if (bdevvp(swap_dev, &vp))
return;
/* Construct a potential path to swap */
if ((nam = findblkname(major(swap_dev))))
snprintf(path, sizeof(path), "/dev/%s%d%c", nam,
DISKUNIT(swap_dev), 'a' + DISKPART(swap_dev));
else
snprintf(path, sizeof(path), "blkdev0x%x",
swap_dev);
#if defined(NFSCLIENT)
gotit:
#endif
sdp = malloc(sizeof(*sdp), M_VMSWAP, M_WAITOK|M_ZERO);
spp = malloc(sizeof(*spp), M_VMSWAP, M_WAITOK);
sdp->swd_flags = SWF_FAKE;
sdp->swd_dev = swap_dev;
sdp->swd_pathlen = strlen(path) + 1;
sdp->swd_path = malloc(sdp->swd_pathlen, M_VMSWAP, M_WAITOK | M_ZERO);
strlcpy(sdp->swd_path, path, sdp->swd_pathlen);
sdp->swd_vp = vp;
mtx_enter(&uvm_swap_data_lock);
swaplist_insert(sdp, spp, 0);
mtx_leave(&uvm_swap_data_lock);
if (swap_on(curproc, sdp)) {
mtx_enter(&uvm_swap_data_lock);
swaplist_find(vp, 1);
swaplist_trim();
vput(sdp->swd_vp);
mtx_leave(&uvm_swap_data_lock);
rw_exit_write(&swap_syscall_lock);
free(sdp->swd_path, M_VMSWAP, sdp->swd_pathlen);
free(sdp, M_VMSWAP, sizeof(*sdp));
return;
}
rw_exit_write(&swap_syscall_lock);
}
#ifdef HIBERNATE
int
uvm_hibswap(dev_t dev, u_long *sp, u_long *ep)
{
struct swapdev *sdp, *swd = NULL;
struct swappri *spp;
/* no swap devices configured yet? */
if (uvmexp.nswapdev < 1 || dev != swdevt[0].sw_dev)
return (1);
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
if (sdp->swd_dev == dev)
swd = sdp;
}
}
if (swd == NULL || (swd->swd_flags & SWF_ENABLE) == 0)
return (1);
blist_gapfind(swd->swd_blist, sp, ep);
if (*ep - *sp == 0)
/* no gap found */
return (1);
/*
* blist_gapfind returns the gap as [sp,ep[ ,
* whereas [sp,ep] is expected from uvm_hibswap().
*/
*ep -= 1;
return (0);
}
#endif /* HIBERNATE */
#ifdef DDB
void
swap_print_all(int (*pr)(const char *, ...))
{
struct swappri *spp;
struct swapdev *sdp;
LIST_FOREACH(spp, &swap_priority, spi_swappri) {
TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
#ifdef HIBERNATE
u_long bgap = 0, egap = 0;
#endif
pr("swap %p path \"%s\" flags 0x%x\n", sdp,
sdp->swd_path, sdp->swd_flags);
blist_print(sdp->swd_blist);
#ifdef HIBERNATE
if (!uvm_hibswap(sdp->swd_dev, &bgap, &egap))
pr("hibernate gap: [0x%lx, 0x%lx] size=%lu\n",
bgap, egap, (egap - bgap + 1));
else
pr("hibernate gap: not found\n");
#endif
}
}
}
#endif /* DDB */
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* $OpenBSD: tty_tty.c,v 1.32 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: tty_tty.c,v 1.13 1996/03/30 22:24:46 christos Exp $ */
/*-
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tty_tty.c 8.2 (Berkeley) 9/23/93
*/
/*
* Indirect driver for controlling tty.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/fcntl.h>
#define cttyvp(p) \
((p)->p_p->ps_flags & PS_CONTROLT ? \
(p)->p_p->ps_session->s_ttyvp : NULL)
int
cttyopen(dev_t dev, int flag, int mode, struct proc *p)
{
struct vnode *ttyvp = cttyvp(p);
int error;
if (ttyvp == NULL)
return (ENXIO);
vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_OPEN(ttyvp, flag, NOCRED, p);
VOP_UNLOCK(ttyvp);
return (error);
}
int
cttyread(dev_t dev, struct uio *uio, int flag)
{
struct vnode *ttyvp = cttyvp(uio->uio_procp);
int error;
if (ttyvp == NULL)
return (EIO);
vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_READ(ttyvp, uio, flag, NOCRED);
VOP_UNLOCK(ttyvp);
return (error);
}
int
cttywrite(dev_t dev, struct uio *uio, int flag)
{
struct vnode *ttyvp = cttyvp(uio->uio_procp);
int error;
if (ttyvp == NULL)
return (EIO);
vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_WRITE(ttyvp, uio, flag, NOCRED);
VOP_UNLOCK(ttyvp);
return (error);
}
int
cttyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct vnode *ttyvp = cttyvp(p);
struct session *sess;
int error, secs;
if (ttyvp == NULL)
return (EIO);
if (cmd == TIOCSCTTY) /* XXX */
return (EINVAL);
if (cmd == TIOCNOTTY) {
if (!SESS_LEADER(p->p_p)) {
atomic_clearbits_int(&p->p_p->ps_flags, PS_CONTROLT);
return (0);
} else
return (EINVAL);
}
switch (cmd) {
case TIOCSETVERAUTH:
if ((error = suser(p)))
return error;
secs = *(int *)addr;
if (secs < 1 || secs > 3600)
return EINVAL;
sess = p->p_p->ps_pgrp->pg_session;
sess->s_verauthuid = p->p_ucred->cr_ruid;
sess->s_verauthppid = p->p_p->ps_pptr->ps_pid;
timeout_add_sec(&sess->s_verauthto, secs);
return 0;
case TIOCCLRVERAUTH:
sess = p->p_p->ps_pgrp->pg_session;
timeout_del(&sess->s_verauthto);
zapverauth(sess);
return 0;
case TIOCCHKVERAUTH:
/*
* It's not clear when or what these checks are for.
* How can we reach this code with a different ruid?
* The ppid check is also more porous than desired.
* Nevertheless, the checks reflect the original intention;
* namely, that it be the same user using the same shell.
*/
sess = p->p_p->ps_pgrp->pg_session;
if (sess->s_verauthuid == p->p_ucred->cr_ruid &&
sess->s_verauthppid == p->p_p->ps_pptr->ps_pid)
return 0;
return EPERM;
}
return (VOP_IOCTL(ttyvp, cmd, addr, flag, NOCRED, p));
}
int
cttykqfilter(dev_t dev, struct knote *kn)
{
struct vnode *ttyvp = cttyvp(curproc);
if (ttyvp == NULL) {
if (kn->kn_flags & (__EV_POLL | __EV_SELECT))
return (seltrue_kqfilter(dev, kn));
return (ENXIO);
}
return (VOP_KQFILTER(ttyvp, FREAD|FWRITE, kn));
}
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
/*
* Copyright © 2008 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/module.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <linux/shmem_fs.h>
#include <linux/dma-buf.h>
#include <linux/dma-buf-map.h>
#include <linux/mem_encrypt.h>
#include <linux/pagevec.h>
#include <drm/drm.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
#include "drm_internal.h"
#include <sys/conf.h>
#include <uvm/uvm.h>
void drm_unref(struct uvm_object *);
void drm_ref(struct uvm_object *);
boolean_t drm_flush(struct uvm_object *, voff_t, voff_t, int);
int drm_fault(struct uvm_faultinfo *, vaddr_t, vm_page_t *, int, int,
vm_fault_t, vm_prot_t, int);
const struct uvm_pagerops drm_pgops = {
.pgo_reference = drm_ref,
.pgo_detach = drm_unref,
.pgo_fault = drm_fault,
.pgo_flush = drm_flush,
};
void
drm_ref(struct uvm_object *uobj)
{
struct drm_gem_object *obj =
container_of(uobj, struct drm_gem_object, uobj);
drm_gem_object_get(obj);
}
void
drm_unref(struct uvm_object *uobj)
{
struct drm_gem_object *obj =
container_of(uobj, struct drm_gem_object, uobj);
drm_gem_object_put(obj);
}
int
drm_fault(struct uvm_faultinfo *ufi, vaddr_t vaddr, vm_page_t *pps,
int npages, int centeridx, vm_fault_t fault_type,
vm_prot_t access_type, int flags)
{
struct vm_map_entry *entry = ufi->entry;
struct uvm_object *uobj = entry->object.uvm_obj;
struct drm_gem_object *obj =
container_of(uobj, struct drm_gem_object, uobj);
struct drm_device *dev = obj->dev;
int ret;
/*
* we do not allow device mappings to be mapped copy-on-write
* so we kill any attempt to do so here.
*/
if (UVM_ET_ISCOPYONWRITE(entry)) {
uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj);
return(VM_PAGER_ERROR);
}
/*
* We could end up here as the result of a copyin(9) or
* copyout(9) while handling an ioctl. So we must be careful
* not to deadlock. Therefore we only block if the quiesce
* count is zero, which guarantees we didn't enter from within
* an ioctl code path.
*/
mtx_enter(&dev->quiesce_mtx);
if (dev->quiesce && dev->quiesce_count == 0) {
mtx_leave(&dev->quiesce_mtx);
uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj);
mtx_enter(&dev->quiesce_mtx);
while (dev->quiesce) {
msleep_nsec(&dev->quiesce, &dev->quiesce_mtx,
PZERO, "drmflt", INFSLP);
}
mtx_leave(&dev->quiesce_mtx);
return(VM_PAGER_REFAULT);
}
dev->quiesce_count++;
mtx_leave(&dev->quiesce_mtx);
/* Call down into driver to do the magic */
ret = dev->driver->gem_fault(obj, ufi, entry->offset + (vaddr -
entry->start), vaddr, pps, npages, centeridx,
access_type, flags);
mtx_enter(&dev->quiesce_mtx);
dev->quiesce_count--;
if (dev->quiesce)
wakeup(&dev->quiesce_count);
mtx_leave(&dev->quiesce_mtx);
return (ret);
}
boolean_t
drm_flush(struct uvm_object *uobj, voff_t start, voff_t stop, int flags)
{
return (TRUE);
}
struct uvm_object *
udv_attach_drm(dev_t device, vm_prot_t accessprot, voff_t off, vsize_t size)
{
struct drm_device *dev = drm_get_device_from_kdev(device);
struct drm_gem_object *obj = NULL;
struct drm_vma_offset_node *node;
struct drm_file *priv;
struct file *filp;
if (cdevsw[major(device)].d_mmap != drmmmap)
return NULL;
if (dev == NULL)
return NULL;
mutex_lock(&dev->filelist_mutex);
priv = drm_find_file_by_minor(dev, minor(device));
if (priv == NULL) {
mutex_unlock(&dev->filelist_mutex);
return NULL;
}
filp = priv->filp;
mutex_unlock(&dev->filelist_mutex);
if (dev->driver->mmap)
return dev->driver->mmap(filp, accessprot, off, size);
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
off >> PAGE_SHIFT,
atop(round_page(size)));
if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
/*
* When the object is being freed, after it hits 0-refcnt it
* proceeds to tear down the object. In the process it will
* attempt to remove the VMA offset and so acquire this
* mgr->vm_lock. Therefore if we find an object with a 0-refcnt
* that matches our range, we know it is in the process of being
* destroyed and will be freed as soon as we release the lock -
* so we have to check for the 0-refcnted object and treat it as
* invalid.
*/
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
if (!obj)
return NULL;
if (!drm_vma_node_is_allowed(node, priv)) {
drm_gem_object_put(obj);
return NULL;
}
return &obj->uobj;
}
/** @file drm_gem.c
*
* This file provides some of the base ioctls and library routines for
* the graphics memory manager implemented by each device driver.
*
* Because various devices have different requirements in terms of
* synchronization and migration strategies, implementing that is left up to
* the driver, and all that the general API provides should be generic --
* allocating objects, reading/writing data with the cpu, freeing objects.
* Even there, platform-dependent optimizations for reading/writing data with
* the CPU mean we'll likely hook those out to driver-specific calls. However,
* the DRI2 implementation wants to have at least allocate/mmap be generic.
*
* The goal was to have swap-backed object allocation managed through
* struct file. However, file descriptors as handles to a struct file have
* two major failings:
* - Process limits prevent more than 1024 or so being used at a time by
* default.
* - Inability to allocate high fds will aggravate the X Server's select()
* handling, and likely that of many GL client applications as well.
*
* This led to a plan of using our own integer IDs (called handles, following
* DRM terminology) to mimic fds, and implement the fd syscalls we need as
* ioctls. The objects themselves will still include the struct file so
* that we can transition to fds if the required kernel infrastructure shows
* up at a later date, and as our interface with shmfs for memory allocation.
*/
static void
drm_gem_init_release(struct drm_device *dev, void *ptr)
{
drm_vma_offset_manager_destroy(dev->vma_offset_manager);
}
/**
* drm_gem_init - Initialize the GEM device fields
* @dev: drm_devic structure to initialize
*/
int
drm_gem_init(struct drm_device *dev)
{
struct drm_vma_offset_manager *vma_offset_manager;
rw_init(&dev->object_name_lock, "drmonl");
idr_init_base(&dev->object_name_idr, 1);
vma_offset_manager = drmm_kzalloc(dev, sizeof(*vma_offset_manager),
GFP_KERNEL);
if (!vma_offset_manager) {
DRM_ERROR("out of memory\n");
return -ENOMEM;
}
dev->vma_offset_manager = vma_offset_manager;
drm_vma_offset_manager_init(vma_offset_manager,
DRM_FILE_PAGE_OFFSET_START,
DRM_FILE_PAGE_OFFSET_SIZE);
return drmm_add_action(dev, drm_gem_init_release, NULL);
}
#ifdef __linux__
/**
* drm_gem_object_init - initialize an allocated shmem-backed GEM object
* @dev: drm_device the object should be initialized for
* @obj: drm_gem_object to initialize
* @size: object size
*
* Initialize an already allocated GEM object of the specified size with
* shmfs backing store.
*/
int drm_gem_object_init(struct drm_device *dev,
struct drm_gem_object *obj, size_t size)
{
struct file *filp;
drm_gem_private_object_init(dev, obj, size);
filp = shmem_file_setup("drm mm object", size, VM_NORESERVE);
if (IS_ERR(filp))
return PTR_ERR(filp);
obj->filp = filp;
return 0;
}
EXPORT_SYMBOL(drm_gem_object_init);
#else
int drm_gem_object_init(struct drm_device *dev,
struct drm_gem_object *obj, size_t size)
{
drm_gem_private_object_init(dev, obj, size);
if (size > (512 * 1024 * 1024)) {
printf("%s size too big %lu\n", __func__, size);
return -ENOMEM;
}
obj->uao = uao_create(size, 0);
uvm_obj_init(&obj->uobj, &drm_pgops, 1);
return 0;
}
#endif
/**
* drm_gem_private_object_init - initialize an allocated private GEM object
* @dev: drm_device the object should be initialized for
* @obj: drm_gem_object to initialize
* @size: object size
*
* Initialize an already allocated GEM object of the specified size with
* no GEM provided backing store. Instead the caller is responsible for
* backing the object and handling it.
*/
void drm_gem_private_object_init(struct drm_device *dev,
struct drm_gem_object *obj, size_t size)
{
BUG_ON((size & (PAGE_SIZE - 1)) != 0);
obj->dev = dev;
#ifdef __linux__
obj->filp = NULL;
#else
obj->uao = NULL;
obj->uobj.pgops = NULL;
#endif
kref_init(&obj->refcount);
obj->handle_count = 0;
obj->size = size;
dma_resv_init(&obj->_resv);
if (!obj->resv)
obj->resv = &obj->_resv;
drm_vma_node_reset(&obj->vma_node);
}
EXPORT_SYMBOL(drm_gem_private_object_init);
static void
drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
{
/*
* Note: obj->dma_buf can't disappear as long as we still hold a
* handle reference in obj->handle_count.
*/
mutex_lock(&filp->prime.lock);
if (obj->dma_buf) {
drm_prime_remove_buf_handle_locked(&filp->prime,
obj->dma_buf);
}
mutex_unlock(&filp->prime.lock);
}
/**
* drm_gem_object_handle_free - release resources bound to userspace handles
* @obj: GEM object to clean up.
*
* Called after the last handle to the object has been closed
*
* Removes any name for the object. Note that this must be
* called before drm_gem_object_free or we'll be touching
* freed memory
*/
static void drm_gem_object_handle_free(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
/* Remove any name for this object */
if (obj->name) {
idr_remove(&dev->object_name_idr, obj->name);
obj->name = 0;
}
}
static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj)
{
/* Unbreak the reference cycle if we have an exported dma_buf. */
if (obj->dma_buf) {
dma_buf_put(obj->dma_buf);
obj->dma_buf = NULL;
}
}
static void
drm_gem_object_handle_put_unlocked(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
bool final = false;
if (WARN_ON(READ_ONCE(obj->handle_count) == 0))
return;
/*
* Must bump handle count first as this may be the last
* ref, in which case the object would disappear before we
* checked for a name
*/
mutex_lock(&dev->object_name_lock);
if (--obj->handle_count == 0) {
drm_gem_object_handle_free(obj);
drm_gem_object_exported_dma_buf_free(obj);
final = true;
}
mutex_unlock(&dev->object_name_lock);
if (final)
drm_gem_object_put(obj);
}
/*
* Called at device or object close to release the file's
* handle references on objects.
*/
static int
drm_gem_object_release_handle(int id, void *ptr, void *data)
{
struct drm_file *file_priv = data;
struct drm_gem_object *obj = ptr;
if (obj->funcs->close)
obj->funcs->close(obj, file_priv);
drm_gem_remove_prime_handles(obj, file_priv);
drm_vma_node_revoke(&obj->vma_node, file_priv);
drm_gem_object_handle_put_unlocked(obj);
return 0;
}
/**
* drm_gem_handle_delete - deletes the given file-private handle
* @filp: drm file-private structure to use for the handle look up
* @handle: userspace handle to delete
*
* Removes the GEM handle from the @filp lookup table which has been added with
* drm_gem_handle_create(). If this is the last handle also cleans up linked
* resources like GEM names.
*/
int
drm_gem_handle_delete(struct drm_file *filp, u32 handle)
{
struct drm_gem_object *obj;
spin_lock(&filp->table_lock);
/* Check if we currently have a reference on the object */
obj = idr_replace(&filp->object_idr, NULL, handle);
spin_unlock(&filp->table_lock);
if (IS_ERR_OR_NULL(obj))
return -EINVAL;
/* Release driver's reference and decrement refcount. */
drm_gem_object_release_handle(handle, obj, filp);
/* And finally make the handle available for future allocations. */
spin_lock(&filp->table_lock);
idr_remove(&filp->object_idr, handle);
spin_unlock(&filp->table_lock);
return 0;
}
EXPORT_SYMBOL(drm_gem_handle_delete);
/**
* drm_gem_dumb_map_offset - return the fake mmap offset for a gem object
* @file: drm file-private structure containing the gem object
* @dev: corresponding drm_device
* @handle: gem object handle
* @offset: return location for the fake mmap offset
*
* This implements the &drm_driver.dumb_map_offset kms driver callback for
* drivers which use gem to manage their backing storage.
*
* Returns:
* 0 on success or a negative error code on failure.
*/
int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
u32 handle, u64 *offset)
{
struct drm_gem_object *obj;
int ret;
obj = drm_gem_object_lookup(file, handle);
if (!obj)
return -ENOENT;
/* Don't allow imported objects to be mapped */
if (obj->import_attach) {
ret = -EINVAL;
goto out;
}
ret = drm_gem_create_mmap_offset(obj);
if (ret)
goto out;
*offset = drm_vma_node_offset_addr(&obj->vma_node);
out:
drm_gem_object_put(obj);
return ret;
}
EXPORT_SYMBOL_GPL(drm_gem_dumb_map_offset);
int drm_gem_dumb_destroy(struct drm_file *file,
struct drm_device *dev,
u32 handle)
{
return drm_gem_handle_delete(file, handle);
}
/**
* drm_gem_handle_create_tail - internal functions to create a handle
* @file_priv: drm file-private structure to register the handle for
* @obj: object to register
* @handlep: pointer to return the created handle to the caller
*
* This expects the &drm_device.object_name_lock to be held already and will
* drop it before returning. Used to avoid races in establishing new handles
* when importing an object from either an flink name or a dma-buf.
*
* Handles must be release again through drm_gem_handle_delete(). This is done
* when userspace closes @file_priv for all attached handles, or through the
* GEM_CLOSE ioctl for individual handles.
*/
int
drm_gem_handle_create_tail(struct drm_file *file_priv,
struct drm_gem_object *obj,
u32 *handlep)
{
struct drm_device *dev = obj->dev;
u32 handle;
int ret;
WARN_ON(!mutex_is_locked(&dev->object_name_lock));
if (obj->handle_count++ == 0)
drm_gem_object_get(obj);
/*
* Get the user-visible handle using idr. Preload and perform
* allocation under our spinlock.
*/
idr_preload(GFP_KERNEL);
spin_lock(&file_priv->table_lock);
ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT);
spin_unlock(&file_priv->table_lock);
idr_preload_end();
mutex_unlock(&dev->object_name_lock);
if (ret < 0)
goto err_unref;
handle = ret;
ret = drm_vma_node_allow(&obj->vma_node, file_priv);
if (ret)
goto err_remove;
if (obj->funcs->open) {
ret = obj->funcs->open(obj, file_priv);
if (ret)
goto err_revoke;
}
*handlep = handle;
return 0;
err_revoke:
drm_vma_node_revoke(&obj->vma_node, file_priv);
err_remove:
spin_lock(&file_priv->table_lock);
idr_remove(&file_priv->object_idr, handle);
spin_unlock(&file_priv->table_lock);
err_unref:
drm_gem_object_handle_put_unlocked(obj);
return ret;
}
/**
* drm_gem_handle_create - create a gem handle for an object
* @file_priv: drm file-private structure to register the handle for
* @obj: object to register
* @handlep: pointer to return the created handle to the caller
*
* Create a handle for this object. This adds a handle reference to the object,
* which includes a regular reference count. Callers will likely want to
* dereference the object afterwards.
*
* Since this publishes @obj to userspace it must be fully set up by this point,
* drivers must call this last in their buffer object creation callbacks.
*/
int drm_gem_handle_create(struct drm_file *file_priv,
struct drm_gem_object *obj,
u32 *handlep)
{
mutex_lock(&obj->dev->object_name_lock);
return drm_gem_handle_create_tail(file_priv, obj, handlep);
}
EXPORT_SYMBOL(drm_gem_handle_create);
/**
* drm_gem_free_mmap_offset - release a fake mmap offset for an object
* @obj: obj in question
*
* This routine frees fake offsets allocated by drm_gem_create_mmap_offset().
*
* Note that drm_gem_object_release() already calls this function, so drivers
* don't have to take care of releasing the mmap offset themselves when freeing
* the GEM object.
*/
void
drm_gem_free_mmap_offset(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
drm_vma_offset_remove(dev->vma_offset_manager, &obj->vma_node);
}
EXPORT_SYMBOL(drm_gem_free_mmap_offset);
/**
* drm_gem_create_mmap_offset_size - create a fake mmap offset for an object
* @obj: obj in question
* @size: the virtual size
*
* GEM memory mapping works by handing back to userspace a fake mmap offset
* it can use in a subsequent mmap(2) call. The DRM core code then looks
* up the object based on the offset and sets up the various memory mapping
* structures.
*
* This routine allocates and attaches a fake offset for @obj, in cases where
* the virtual size differs from the physical size (ie. &drm_gem_object.size).
* Otherwise just use drm_gem_create_mmap_offset().
*
* This function is idempotent and handles an already allocated mmap offset
* transparently. Drivers do not need to check for this case.
*/
int
drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)
{
struct drm_device *dev = obj->dev;
return drm_vma_offset_add(dev->vma_offset_manager, &obj->vma_node,
size / PAGE_SIZE);
}
EXPORT_SYMBOL(drm_gem_create_mmap_offset_size);
/**
* drm_gem_create_mmap_offset - create a fake mmap offset for an object
* @obj: obj in question
*
* GEM memory mapping works by handing back to userspace a fake mmap offset
* it can use in a subsequent mmap(2) call. The DRM core code then looks
* up the object based on the offset and sets up the various memory mapping
* structures.
*
* This routine allocates and attaches a fake offset for @obj.
*
* Drivers can call drm_gem_free_mmap_offset() before freeing @obj to release
* the fake offset again.
*/
int drm_gem_create_mmap_offset(struct drm_gem_object *obj)
{
return drm_gem_create_mmap_offset_size(obj, obj->size);
}
EXPORT_SYMBOL(drm_gem_create_mmap_offset);
#ifdef notyet
/*
* Move pages to appropriate lru and release the pagevec, decrementing the
* ref count of those pages.
*/
static void drm_gem_check_release_pagevec(struct pagevec *pvec)
{
check_move_unevictable_pages(pvec);
__pagevec_release(pvec);
cond_resched();
}
#endif
/**
* drm_gem_get_pages - helper to allocate backing pages for a GEM object
* from shmem
* @obj: obj in question
*
* This reads the page-array of the shmem-backing storage of the given gem
* object. An array of pages is returned. If a page is not allocated or
* swapped-out, this will allocate/swap-in the required pages. Note that the
* whole object is covered by the page-array and pinned in memory.
*
* Use drm_gem_put_pages() to release the array and unpin all pages.
*
* This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()).
* If you require other GFP-masks, you have to do those allocations yourself.
*
* Note that you are not allowed to change gfp-zones during runtime. That is,
* shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as
* set during initialization. If you have special zone constraints, set them
* after drm_gem_object_init() via mapping_set_gfp_mask(). shmem-core takes care
* to keep pages in the required zone during swap-in.
*
* This function is only valid on objects initialized with
* drm_gem_object_init(), but not for those initialized with
* drm_gem_private_object_init() only.
*/
struct vm_page **drm_gem_get_pages(struct drm_gem_object *obj)
{
STUB();
return ERR_PTR(-ENOSYS);
#ifdef notyet
struct address_space *mapping;
struct vm_page *p, **pages;
struct pagevec pvec;
int i, npages;
if (WARN_ON(!obj->filp))
return ERR_PTR(-EINVAL);
/* This is the shared memory object that backs the GEM resource */
mapping = obj->filp->f_mapping;
/* We already BUG_ON() for non-page-aligned sizes in
* drm_gem_object_init(), so we should never hit this unless
* driver author is doing something really wrong:
*/
WARN_ON((obj->size & (PAGE_SIZE - 1)) != 0);
npages = obj->size >> PAGE_SHIFT;
pages = kvmalloc_array(npages, sizeof(struct vm_page *), GFP_KERNEL);
if (pages == NULL)
return ERR_PTR(-ENOMEM);
mapping_set_unevictable(mapping);
for (i = 0; i < npages; i++) {
p = shmem_read_mapping_page(mapping, i);
if (IS_ERR(p))
goto fail;
pages[i] = p;
/* Make sure shmem keeps __GFP_DMA32 allocated pages in the
* correct region during swapin. Note that this requires
* __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping)
* so shmem can relocate pages during swapin if required.
*/
BUG_ON(mapping_gfp_constraint(mapping, __GFP_DMA32) &&
(page_to_pfn(p) >= 0x00100000UL));
}
return pages;
fail:
mapping_clear_unevictable(mapping);
pagevec_init(&pvec);
while (i--) {
if (!pagevec_add(&pvec, pages[i]))
drm_gem_check_release_pagevec(&pvec);
}
if (pagevec_count(&pvec))
drm_gem_check_release_pagevec(&pvec);
kvfree(pages);
return ERR_CAST(p);
#endif
}
EXPORT_SYMBOL(drm_gem_get_pages);
/**
* drm_gem_put_pages - helper to free backing pages for a GEM object
* @obj: obj in question
* @pages: pages to free
* @dirty: if true, pages will be marked as dirty
* @accessed: if true, the pages will be marked as accessed
*/
void drm_gem_put_pages(struct drm_gem_object *obj, struct vm_page **pages,
bool dirty, bool accessed)
{
STUB();
#ifdef notyet
int i, npages;
struct address_space *mapping;
struct pagevec pvec;
mapping = file_inode(obj->filp)->i_mapping;
mapping_clear_unevictable(mapping);
/* We already BUG_ON() for non-page-aligned sizes in
* drm_gem_object_init(), so we should never hit this unless
* driver author is doing something really wrong:
*/
WARN_ON((obj->size & (PAGE_SIZE - 1)) != 0);
npages = obj->size >> PAGE_SHIFT;
pagevec_init(&pvec);
for (i = 0; i < npages; i++) {
if (!pages[i])
continue;
if (dirty)
set_page_dirty(pages[i]);
if (accessed)
mark_page_accessed(pages[i]);
/* Undo the reference we took when populating the table */
if (!pagevec_add(&pvec, pages[i]))
drm_gem_check_release_pagevec(&pvec);
}
if (pagevec_count(&pvec))
drm_gem_check_release_pagevec(&pvec);
kvfree(pages);
#endif
}
EXPORT_SYMBOL(drm_gem_put_pages);
static int objects_lookup(struct drm_file *filp, u32 *handle, int count,
struct drm_gem_object **objs)
{
int i, ret = 0;
struct drm_gem_object *obj;
spin_lock(&filp->table_lock);
for (i = 0; i < count; i++) {
/* Check if we currently have a reference on the object */
obj = idr_find(&filp->object_idr, handle[i]);
if (!obj) {
ret = -ENOENT;
break;
}
drm_gem_object_get(obj);
objs[i] = obj;
}
spin_unlock(&filp->table_lock);
return ret;
}
/**
* drm_gem_objects_lookup - look up GEM objects from an array of handles
* @filp: DRM file private date
* @bo_handles: user pointer to array of userspace handle
* @count: size of handle array
* @objs_out: returned pointer to array of drm_gem_object pointers
*
* Takes an array of userspace handles and returns a newly allocated array of
* GEM objects.
*
* For a single handle lookup, use drm_gem_object_lookup().
*
* Returns:
*
* @objs filled in with GEM object pointers. Returned GEM objects need to be
* released with drm_gem_object_put(). -ENOENT is returned on a lookup
* failure. 0 is returned on success.
*
*/
int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles,
int count, struct drm_gem_object ***objs_out)
{
int ret;
u32 *handles;
struct drm_gem_object **objs;
if (!count)
return 0;
objs = kvmalloc_array(count, sizeof(struct drm_gem_object *),
GFP_KERNEL | __GFP_ZERO);
if (!objs)
return -ENOMEM;
*objs_out = objs;
handles = kvmalloc_array(count, sizeof(u32), GFP_KERNEL);
if (!handles) {
ret = -ENOMEM;
goto out;
}
if (copy_from_user(handles, bo_handles, count * sizeof(u32))) {
ret = -EFAULT;
DRM_DEBUG("Failed to copy in GEM handles\n");
goto out;
}
ret = objects_lookup(filp, handles, count, objs);
out:
kvfree(handles);
return ret;
}
EXPORT_SYMBOL(drm_gem_objects_lookup);
/**
* drm_gem_object_lookup - look up a GEM object from its handle
* @filp: DRM file private date
* @handle: userspace handle
*
* Returns:
*
* A reference to the object named by the handle if such exists on @filp, NULL
* otherwise.
*
* If looking up an array of handles, use drm_gem_objects_lookup().
*/
struct drm_gem_object *
drm_gem_object_lookup(struct drm_file *filp, u32 handle)
{
struct drm_gem_object *obj = NULL;
objects_lookup(filp, &handle, 1, &obj);
return obj;
}
EXPORT_SYMBOL(drm_gem_object_lookup);
/**
* drm_gem_dma_resv_wait - Wait on GEM object's reservation's objects
* shared and/or exclusive fences.
* @filep: DRM file private date
* @handle: userspace handle
* @wait_all: if true, wait on all fences, else wait on just exclusive fence
* @timeout: timeout value in jiffies or zero to return immediately
*
* Returns:
*
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or
* greater than 0 on success.
*/
long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,
bool wait_all, unsigned long timeout)
{
long ret;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(filep, handle);
if (!obj) {
DRM_DEBUG("Failed to look up GEM BO %d\n", handle);
return -EINVAL;
}
ret = dma_resv_wait_timeout(obj->resv, wait_all, true, timeout);
if (ret == 0)
ret = -ETIME;
else if (ret > 0)
ret = 0;
drm_gem_object_put(obj);
return ret;
}
EXPORT_SYMBOL(drm_gem_dma_resv_wait);
/**
* drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl
* @dev: drm_device
* @data: ioctl data
* @file_priv: drm file-private structure
*
* Releases the handle to an mm object.
*/
int
drm_gem_close_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_gem_close *args = data;
int ret;
if (!drm_core_check_feature(dev, DRIVER_GEM))
return -EOPNOTSUPP;
ret = drm_gem_handle_delete(file_priv, args->handle);
return ret;
}
/**
* drm_gem_flink_ioctl - implementation of the GEM_FLINK ioctl
* @dev: drm_device
* @data: ioctl data
* @file_priv: drm file-private structure
*
* Create a global name for an object, returning the name.
*
* Note that the name does not hold a reference; when the object
* is freed, the name goes away.
*/
int
drm_gem_flink_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_gem_flink *args = data;
struct drm_gem_object *obj;
int ret;
if (!drm_core_check_feature(dev, DRIVER_GEM))
return -EOPNOTSUPP;
obj = drm_gem_object_lookup(file_priv, args->handle);
if (obj == NULL)
return -ENOENT;
mutex_lock(&dev->object_name_lock);
/* prevent races with concurrent gem_close. */
if (obj->handle_count == 0) {
ret = -ENOENT;
goto err;
}
if (!obj->name) {
ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_KERNEL);
if (ret < 0)
goto err;
obj->name = ret;
}
args->name = (uint64_t) obj->name;
ret = 0;
err:
mutex_unlock(&dev->object_name_lock);
drm_gem_object_put(obj);
return ret;
}
/**
* drm_gem_open_ioctl - implementation of the GEM_OPEN ioctl
* @dev: drm_device
* @data: ioctl data
* @file_priv: drm file-private structure
*
* Open an object using the global name, returning a handle and the size.
*
* This handle (of course) holds a reference to the object, so the object
* will not go away until the handle is deleted.
*/
int
drm_gem_open_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_gem_open *args = data;
struct drm_gem_object *obj;
int ret;
u32 handle;
if (!drm_core_check_feature(dev, DRIVER_GEM))
return -EOPNOTSUPP;
mutex_lock(&dev->object_name_lock);
obj = idr_find(&dev->object_name_idr, (int) args->name);
if (obj) {
drm_gem_object_get(obj);
} else {
mutex_unlock(&dev->object_name_lock);
return -ENOENT;
}
/* drm_gem_handle_create_tail unlocks dev->object_name_lock. */
ret = drm_gem_handle_create_tail(file_priv, obj, &handle);
if (ret)
goto err;
args->handle = handle;
args->size = obj->size;
err:
drm_gem_object_put(obj);
return ret;
}
/**
* drm_gem_open - initializes GEM file-private structures at devnode open time
* @dev: drm_device which is being opened by userspace
* @file_private: drm file-private structure to set up
*
* Called at device open time, sets up the structure for handling refcounting
* of mm objects.
*/
void
drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
{
idr_init_base(&file_private->object_idr, 1);
mtx_init(&file_private->table_lock, IPL_NONE);
}
/**
* drm_gem_release - release file-private GEM resources
* @dev: drm_device which is being closed by userspace
* @file_private: drm file-private structure to clean up
*
* Called at close time when the filp is going away.
*
* Releases any remaining references on objects by this filp.
*/
void
drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
{
idr_for_each(&file_private->object_idr,
&drm_gem_object_release_handle, file_private);
idr_destroy(&file_private->object_idr);
}
/**
* drm_gem_object_release - release GEM buffer object resources
* @obj: GEM buffer object
*
* This releases any structures and resources used by @obj and is the inverse of
* drm_gem_object_init().
*/
void
drm_gem_object_release(struct drm_gem_object *obj)
{
WARN_ON(obj->dma_buf);
#ifdef __linux__
if (obj->filp)
fput(obj->filp);
#else
if (obj->uao)
uao_detach(obj->uao);
if (obj->uobj.pgops)
uvm_obj_destroy(&obj->uobj);
#endif
dma_resv_fini(&obj->_resv);
drm_gem_free_mmap_offset(obj);
}
EXPORT_SYMBOL(drm_gem_object_release);
/**
* drm_gem_object_free - free a GEM object
* @kref: kref of the object to free
*
* Called after the last reference to the object has been lost.
*
* Frees the object
*/
void
drm_gem_object_free(struct kref *kref)
{
struct drm_gem_object *obj =
container_of(kref, struct drm_gem_object, refcount);
if (WARN_ON(!obj->funcs->free))
return;
obj->funcs->free(obj);
}
EXPORT_SYMBOL(drm_gem_object_free);
#ifdef __linux__
/**
* drm_gem_vm_open - vma->ops->open implementation for GEM
* @vma: VM area structure
*
* This function implements the #vm_operations_struct open() callback for GEM
* drivers. This must be used together with drm_gem_vm_close().
*/
void drm_gem_vm_open(struct vm_area_struct *vma)
{
struct drm_gem_object *obj = vma->vm_private_data;
drm_gem_object_get(obj);
}
EXPORT_SYMBOL(drm_gem_vm_open);
/**
* drm_gem_vm_close - vma->ops->close implementation for GEM
* @vma: VM area structure
*
* This function implements the #vm_operations_struct close() callback for GEM
* drivers. This must be used together with drm_gem_vm_open().
*/
void drm_gem_vm_close(struct vm_area_struct *vma)
{
struct drm_gem_object *obj = vma->vm_private_data;
drm_gem_object_put(obj);
}
EXPORT_SYMBOL(drm_gem_vm_close);
/**
* drm_gem_mmap_obj - memory map a GEM object
* @obj: the GEM object to map
* @obj_size: the object size to be mapped, in bytes
* @vma: VMA for the area to be mapped
*
* Set up the VMA to prepare mapping of the GEM object using the GEM object's
* vm_ops. Depending on their requirements, GEM objects can either
* provide a fault handler in their vm_ops (in which case any accesses to
* the object will be trapped, to perform migration, GTT binding, surface
* register allocation, or performance monitoring), or mmap the buffer memory
* synchronously after calling drm_gem_mmap_obj.
*
* This function is mainly intended to implement the DMABUF mmap operation, when
* the GEM object is not looked up based on its fake offset. To implement the
* DRM mmap operation, drivers should use the drm_gem_mmap() function.
*
* drm_gem_mmap_obj() assumes the user is granted access to the buffer while
* drm_gem_mmap() prevents unprivileged users from mapping random objects. So
* callers must verify access restrictions before calling this helper.
*
* Return 0 or success or -EINVAL if the object size is smaller than the VMA
* size, or if no vm_ops are provided.
*/
int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
struct vm_area_struct *vma)
{
int ret;
/* Check for valid size. */
if (obj_size < vma->vm_end - vma->vm_start)
return -EINVAL;
/* Take a ref for this mapping of the object, so that the fault
* handler can dereference the mmap offset's pointer to the object.
* This reference is cleaned up by the corresponding vm_close
* (which should happen whether the vma was created by this call, or
* by a vm_open due to mremap or partial unmap or whatever).
*/
drm_gem_object_get(obj);
vma->vm_private_data = obj;
vma->vm_ops = obj->funcs->vm_ops;
if (obj->funcs->mmap) {
ret = obj->funcs->mmap(obj, vma);
if (ret)
goto err_drm_gem_object_put;
WARN_ON(!(vma->vm_flags & VM_DONTEXPAND));
} else {
if (!vma->vm_ops) {
ret = -EINVAL;
goto err_drm_gem_object_put;
}
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
}
return 0;
err_drm_gem_object_put:
drm_gem_object_put(obj);
return ret;
}
EXPORT_SYMBOL(drm_gem_mmap_obj);
/**
* drm_gem_mmap - memory map routine for GEM objects
* @filp: DRM file pointer
* @vma: VMA for the area to be mapped
*
* If a driver supports GEM object mapping, mmap calls on the DRM file
* descriptor will end up here.
*
* Look up the GEM object based on the offset passed in (vma->vm_pgoff will
* contain the fake offset we created when the GTT map ioctl was called on
* the object) and map it with a call to drm_gem_mmap_obj().
*
* If the caller is not granted access to the buffer object, the mmap will fail
* with EACCES. Please see the vma manager for more information.
*/
int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
struct drm_gem_object *obj = NULL;
struct drm_vma_offset_node *node;
int ret;
if (drm_dev_is_unplugged(dev))
return -ENODEV;
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
/*
* When the object is being freed, after it hits 0-refcnt it
* proceeds to tear down the object. In the process it will
* attempt to remove the VMA offset and so acquire this
* mgr->vm_lock. Therefore if we find an object with a 0-refcnt
* that matches our range, we know it is in the process of being
* destroyed and will be freed as soon as we release the lock -
* so we have to check for the 0-refcnted object and treat it as
* invalid.
*/
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
if (!obj)
return -EINVAL;
if (!drm_vma_node_is_allowed(node, priv)) {
drm_gem_object_put(obj);
return -EACCES;
}
ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node) << PAGE_SHIFT,
vma);
drm_gem_object_put(obj);
return ret;
}
EXPORT_SYMBOL(drm_gem_mmap);
#else /* ! __linux__ */
int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
vm_prot_t accessprot, voff_t off, vsize_t size)
{
int ret;
/* Check for valid size. */
if (obj_size < size)
return -EINVAL;
/* Take a ref for this mapping of the object, so that the fault
* handler can dereference the mmap offset's pointer to the object.
* This reference is cleaned up by the corresponding vm_close
* (which should happen whether the vma was created by this call, or
* by a vm_open due to mremap or partial unmap or whatever).
*/
drm_gem_object_get(obj);
#ifdef __linux__
vma->vm_private_data = obj;
vma->vm_ops = obj->funcs->vm_ops;
#else
if (obj->uobj.pgops == NULL)
uvm_obj_init(&obj->uobj, obj->funcs->vm_ops, 1);
#endif
if (obj->funcs->mmap) {
ret = obj->funcs->mmap(obj, accessprot, off, size);
if (ret)
goto err_drm_gem_object_put;
#ifdef notyet
WARN_ON(!(vma->vm_flags & VM_DONTEXPAND));
#endif
} else {
#ifdef notyet
if (!vma->vm_ops) {
ret = -EINVAL;
goto err_drm_gem_object_put;
}
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
#else
ret = -EINVAL;
goto err_drm_gem_object_put;
#endif
}
return 0;
err_drm_gem_object_put:
drm_gem_object_put(obj);
return ret;
}
struct uvm_object *
drm_gem_mmap(struct file *filp, vm_prot_t accessprot, voff_t off,
vsize_t size)
{
struct drm_file *priv = (void *)filp;
struct drm_device *dev = priv->minor->dev;
struct drm_gem_object *obj = NULL;
struct drm_vma_offset_node *node;
int ret;
if (drm_dev_is_unplugged(dev))
return NULL;
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
off >> PAGE_SHIFT,
atop(round_page(size)));
if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
/*
* When the object is being freed, after it hits 0-refcnt it
* proceeds to tear down the object. In the process it will
* attempt to remove the VMA offset and so acquire this
* mgr->vm_lock. Therefore if we find an object with a 0-refcnt
* that matches our range, we know it is in the process of being
* destroyed and will be freed as soon as we release the lock -
* so we have to check for the 0-refcnted object and treat it as
* invalid.
*/
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
if (!obj)
return NULL;
if (!drm_vma_node_is_allowed(node, priv)) {
drm_gem_object_put(obj);
return NULL;
}
ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node) << PAGE_SHIFT,
accessprot, off, size);
drm_gem_object_put(obj);
return &obj->uobj;
}
#endif /* __linux__ */
void drm_gem_print_info(struct drm_printer *p, unsigned int indent,
const struct drm_gem_object *obj)
{
drm_printf_indent(p, indent, "name=%d\n", obj->name);
drm_printf_indent(p, indent, "refcount=%u\n",
kref_read(&obj->refcount));
drm_printf_indent(p, indent, "start=%08lx\n",
drm_vma_node_start(&obj->vma_node));
drm_printf_indent(p, indent, "size=%zu\n", obj->size);
drm_printf_indent(p, indent, "imported=%s\n",
obj->import_attach ? "yes" : "no");
if (obj->funcs->print_info)
obj->funcs->print_info(p, indent, obj);
}
int drm_gem_pin(struct drm_gem_object *obj)
{
if (obj->funcs->pin)
return obj->funcs->pin(obj);
else
return 0;
}
void drm_gem_unpin(struct drm_gem_object *obj)
{
if (obj->funcs->unpin)
obj->funcs->unpin(obj);
}
int drm_gem_vmap(struct drm_gem_object *obj, struct dma_buf_map *map)
{
int ret;
if (!obj->funcs->vmap)
return -EOPNOTSUPP;
ret = obj->funcs->vmap(obj, map);
if (ret)
return ret;
else if (dma_buf_map_is_null(map))
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL(drm_gem_vmap);
void drm_gem_vunmap(struct drm_gem_object *obj, struct dma_buf_map *map)
{
if (dma_buf_map_is_null(map))
return;
if (obj->funcs->vunmap)
obj->funcs->vunmap(obj, map);
/* Always set the mapping to NULL. Callers may rely on this. */
dma_buf_map_clear(map);
}
EXPORT_SYMBOL(drm_gem_vunmap);
/**
* drm_gem_lock_reservations - Sets up the ww context and acquires
* the lock on an array of GEM objects.
*
* Once you've locked your reservations, you'll want to set up space
* for your shared fences (if applicable), submit your job, then
* drm_gem_unlock_reservations().
*
* @objs: drm_gem_objects to lock
* @count: Number of objects in @objs
* @acquire_ctx: struct ww_acquire_ctx that will be initialized as
* part of tracking this set of locked reservations.
*/
int
drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
struct ww_acquire_ctx *acquire_ctx)
{
int contended = -1;
int i, ret;
ww_acquire_init(acquire_ctx, &reservation_ww_class);
retry:
if (contended != -1) {
struct drm_gem_object *obj = objs[contended];
ret = dma_resv_lock_slow_interruptible(obj->resv,
acquire_ctx);
if (ret) {
ww_acquire_fini(acquire_ctx);
return ret;
}
}
for (i = 0; i < count; i++) {
if (i == contended)
continue;
ret = dma_resv_lock_interruptible(objs[i]->resv,
acquire_ctx);
if (ret) {
int j;
for (j = 0; j < i; j++)
dma_resv_unlock(objs[j]->resv);
if (contended != -1 && contended >= i)
dma_resv_unlock(objs[contended]->resv);
if (ret == -EDEADLK) {
contended = i;
goto retry;
}
ww_acquire_fini(acquire_ctx);
return ret;
}
}
ww_acquire_done(acquire_ctx);
return 0;
}
EXPORT_SYMBOL(drm_gem_lock_reservations);
void
drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
struct ww_acquire_ctx *acquire_ctx)
{
int i;
for (i = 0; i < count; i++)
dma_resv_unlock(objs[i]->resv);
ww_acquire_fini(acquire_ctx);
}
EXPORT_SYMBOL(drm_gem_unlock_reservations);
#ifdef notyet
/**
* drm_gem_fence_array_add - Adds the fence to an array of fences to be
* waited on, deduplicating fences from the same context.
*
* @fence_array: array of dma_fence * for the job to block on.
* @fence: the dma_fence to add to the list of dependencies.
*
* This functions consumes the reference for @fence both on success and error
* cases.
*
* Returns:
* 0 on success, or an error on failing to expand the array.
*/
int drm_gem_fence_array_add(struct xarray *fence_array,
struct dma_fence *fence)
{
struct dma_fence *entry;
unsigned long index;
u32 id = 0;
int ret;
if (!fence)
return 0;
/* Deduplicate if we already depend on a fence from the same context.
* This lets the size of the array of deps scale with the number of
* engines involved, rather than the number of BOs.
*/
xa_for_each(fence_array, index, entry) {
if (entry->context != fence->context)
continue;
if (dma_fence_is_later(fence, entry)) {
dma_fence_put(entry);
xa_store(fence_array, index, fence, GFP_KERNEL);
} else {
dma_fence_put(fence);
}
return 0;
}
ret = xa_alloc(fence_array, &id, fence, xa_limit_32b, GFP_KERNEL);
if (ret != 0)
dma_fence_put(fence);
return ret;
}
EXPORT_SYMBOL(drm_gem_fence_array_add);
/**
* drm_gem_fence_array_add_implicit - Adds the implicit dependencies tracked
* in the GEM object's reservation object to an array of dma_fences for use in
* scheduling a rendering job.
*
* This should be called after drm_gem_lock_reservations() on your array of
* GEM objects used in the job but before updating the reservations with your
* own fences.
*
* @fence_array: array of dma_fence * for the job to block on.
* @obj: the gem object to add new dependencies from.
* @write: whether the job might write the object (so we need to depend on
* shared fences in the reservation object).
*/
int drm_gem_fence_array_add_implicit(struct xarray *fence_array,
struct drm_gem_object *obj,
bool write)
{
int ret;
struct dma_fence **fences;
unsigned int i, fence_count;
if (!write) {
struct dma_fence *fence =
dma_resv_get_excl_unlocked(obj->resv);
return drm_gem_fence_array_add(fence_array, fence);
}
ret = dma_resv_get_fences(obj->resv, NULL,
&fence_count, &fences);
if (ret || !fence_count)
return ret;
for (i = 0; i < fence_count; i++) {
ret = drm_gem_fence_array_add(fence_array, fences[i]);
if (ret)
break;
}
for (; i < fence_count; i++)
dma_fence_put(fences[i]);
kfree(fences);
return ret;
}
EXPORT_SYMBOL(drm_gem_fence_array_add_implicit);
#endif /* notyet */
567
567
565
566
508
509
72
72
73
26
44
73
72
180
181
1
172
181
180
880
879
732
219
880
396
517
78
879
714
103
122
455
176
352
1107
767
610
29
29
663
713
57
719
1959
1961
1088
1656
519
56
56
3611
3608
2519
3596
3477
315
1643
88
87
88
88
5
5
38
38
5
22
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
/* $OpenBSD: kern_synch.c,v 1.190 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_synch.c 8.6 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/signalvar.h>
#include <sys/sched.h>
#include <sys/timeout.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/refcnt.h>
#include <sys/atomic.h>
#include <sys/tracepoint.h>
#include <ddb/db_output.h>
#include <machine/spinlock.h>
#ifdef DIAGNOSTIC
#include <sys/syslog.h>
#endif
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
int sleep_signal_check(void);
int thrsleep(struct proc *, struct sys___thrsleep_args *);
int thrsleep_unlock(void *);
/*
* We're only looking at 7 bits of the address; everything is
* aligned to 4, lots of things are aligned to greater powers
* of 2. Shift right by 8, i.e. drop the bottom 256 worth.
*/
#define TABLESIZE 128
#define LOOKUP(x) (((long)(x) >> 8) & (TABLESIZE - 1))
TAILQ_HEAD(slpque,proc) slpque[TABLESIZE];
void
sleep_queue_init(void)
{
int i;
for (i = 0; i < TABLESIZE; i++)
TAILQ_INIT(&slpque[i]);
}
/*
* Global sleep channel for threads that do not want to
* receive wakeup(9) broadcasts.
*/
int nowake;
/*
* During autoconfiguration or after a panic, a sleep will simply
* lower the priority briefly to allow interrupts, then return.
* The priority to be used (safepri) is machine-dependent, thus this
* value is initialized and maintained in the machine-dependent layers.
* This priority will typically be 0, or the lowest priority
* that is safe for use on the interrupt stack; it can be made
* higher to block network software interrupts after panics.
*/
extern int safepri;
/*
* General sleep call. Suspends the current process until a wakeup is
* performed on the specified identifier. The process will then be made
* runnable with the specified priority. Sleeps at most timo/hz seconds
* (0 means no timeout). If pri includes PCATCH flag, signals are checked
* before and after sleeping, else signals are not checked. Returns 0 if
* awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a
* signal needs to be delivered, ERESTART is returned if the current system
* call should be restarted if possible, and EINTR is returned if the system
* call should be interrupted by the signal (return EINTR).
*/
int
tsleep(const volatile void *ident, int priority, const char *wmesg, int timo)
{
struct sleep_state sls;
#ifdef MULTIPROCESSOR
int hold_count;
#endif
KASSERT((priority & ~(PRIMASK | PCATCH)) == 0);
KASSERT(ident != &nowake || ISSET(priority, PCATCH) || timo != 0);
#ifdef MULTIPROCESSOR
KASSERT(timo || _kernel_lock_held());
#endif
#ifdef DDB
if (cold == 2)
db_stack_dump();
#endif
if (cold || panicstr) {
int s;
/*
* After a panic, or during autoconfiguration,
* just give interrupts a chance, then just return;
* don't run any other procs or panic below,
* in case this is the idle process and already asleep.
*/
s = splhigh();
splx(safepri);
#ifdef MULTIPROCESSOR
if (_kernel_lock_held()) {
hold_count = __mp_release_all(&kernel_lock);
__mp_acquire_count(&kernel_lock, hold_count);
}
#endif
splx(s);
return (0);
}
sleep_setup(&sls, ident, priority, wmesg, timo);
return sleep_finish(&sls, 1);
}
int
tsleep_nsec(const volatile void *ident, int priority, const char *wmesg,
uint64_t nsecs)
{
uint64_t to_ticks;
if (nsecs == INFSLP)
return tsleep(ident, priority, wmesg, 0);
#ifdef DIAGNOSTIC
if (nsecs == 0) {
log(LOG_WARNING,
"%s: %s[%d]: %s: trying to sleep zero nanoseconds\n",
__func__, curproc->p_p->ps_comm, curproc->p_p->ps_pid,
wmesg);
}
#endif
/*
* We want to sleep at least nsecs nanoseconds worth of ticks.
*
* - Clamp nsecs to prevent arithmetic overflow.
*
* - Round nsecs up to account for any nanoseconds that do not
* divide evenly into tick_nsec, otherwise we'll lose them to
* integer division in the next step. We add (tick_nsec - 1)
* to keep from introducing a spurious tick if there are no
* such nanoseconds, i.e. nsecs % tick_nsec == 0.
*
* - Divide the rounded value to a count of ticks. We divide
* by (tick_nsec + 1) to discard the extra tick introduced if,
* before rounding, nsecs % tick_nsec == 1.
*
* - Finally, add a tick to the result. We need to wait out
* the current tick before we can begin counting our interval,
* as we do not know how much time has elapsed since the
* current tick began.
*/
nsecs = MIN(nsecs, UINT64_MAX - tick_nsec);
to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1;
if (to_ticks > INT_MAX)
to_ticks = INT_MAX;
return tsleep(ident, priority, wmesg, (int)to_ticks);
}
/*
* Same as tsleep, but if we have a mutex provided, then once we've
* entered the sleep queue we drop the mutex. After sleeping we re-lock.
*/
int
msleep(const volatile void *ident, struct mutex *mtx, int priority,
const char *wmesg, int timo)
{
struct sleep_state sls;
int error, spl;
#ifdef MULTIPROCESSOR
int hold_count;
#endif
KASSERT((priority & ~(PRIMASK | PCATCH | PNORELOCK)) == 0);
KASSERT(ident != &nowake || ISSET(priority, PCATCH) || timo != 0);
KASSERT(mtx != NULL);
#ifdef DDB
if (cold == 2)
db_stack_dump();
#endif
if (cold || panicstr) {
/*
* After a panic, or during autoconfiguration,
* just give interrupts a chance, then just return;
* don't run any other procs or panic below,
* in case this is the idle process and already asleep.
*/
spl = MUTEX_OLDIPL(mtx);
MUTEX_OLDIPL(mtx) = safepri;
mtx_leave(mtx);
#ifdef MULTIPROCESSOR
if (_kernel_lock_held()) {
hold_count = __mp_release_all(&kernel_lock);
__mp_acquire_count(&kernel_lock, hold_count);
}
#endif
if ((priority & PNORELOCK) == 0) {
mtx_enter(mtx);
MUTEX_OLDIPL(mtx) = spl;
} else
splx(spl);
return (0);
}
sleep_setup(&sls, ident, priority, wmesg, timo);
/* XXX - We need to make sure that the mutex doesn't
* unblock splsched. This can be made a bit more
* correct when the sched_lock is a mutex.
*/
spl = MUTEX_OLDIPL(mtx);
MUTEX_OLDIPL(mtx) = splsched();
mtx_leave(mtx);
/* signal may stop the process, release mutex before that */
error = sleep_finish(&sls, 1);
if ((priority & PNORELOCK) == 0) {
mtx_enter(mtx);
MUTEX_OLDIPL(mtx) = spl; /* put the ipl back */
} else
splx(spl);
return error;
}
int
msleep_nsec(const volatile void *ident, struct mutex *mtx, int priority,
const char *wmesg, uint64_t nsecs)
{
uint64_t to_ticks;
if (nsecs == INFSLP)
return msleep(ident, mtx, priority, wmesg, 0);
#ifdef DIAGNOSTIC
if (nsecs == 0) {
log(LOG_WARNING,
"%s: %s[%d]: %s: trying to sleep zero nanoseconds\n",
__func__, curproc->p_p->ps_comm, curproc->p_p->ps_pid,
wmesg);
}
#endif
nsecs = MIN(nsecs, UINT64_MAX - tick_nsec);
to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1;
if (to_ticks > INT_MAX)
to_ticks = INT_MAX;
return msleep(ident, mtx, priority, wmesg, (int)to_ticks);
}
/*
* Same as tsleep, but if we have a rwlock provided, then once we've
* entered the sleep queue we drop the it. After sleeping we re-lock.
*/
int
rwsleep(const volatile void *ident, struct rwlock *rwl, int priority,
const char *wmesg, int timo)
{
struct sleep_state sls;
int error, status;
KASSERT((priority & ~(PRIMASK | PCATCH | PNORELOCK)) == 0);
KASSERT(ident != &nowake || ISSET(priority, PCATCH) || timo != 0);
rw_assert_anylock(rwl);
status = rw_status(rwl);
sleep_setup(&sls, ident, priority, wmesg, timo);
rw_exit(rwl);
/* signal may stop the process, release rwlock before that */
error = sleep_finish(&sls, 1);
if ((priority & PNORELOCK) == 0)
rw_enter(rwl, status);
return error;
}
int
rwsleep_nsec(const volatile void *ident, struct rwlock *rwl, int priority,
const char *wmesg, uint64_t nsecs)
{
uint64_t to_ticks;
if (nsecs == INFSLP)
return rwsleep(ident, rwl, priority, wmesg, 0);
#ifdef DIAGNOSTIC
if (nsecs == 0) {
log(LOG_WARNING,
"%s: %s[%d]: %s: trying to sleep zero nanoseconds\n",
__func__, curproc->p_p->ps_comm, curproc->p_p->ps_pid,
wmesg);
}
#endif
nsecs = MIN(nsecs, UINT64_MAX - tick_nsec);
to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1;
if (to_ticks > INT_MAX)
to_ticks = INT_MAX;
return rwsleep(ident, rwl, priority, wmesg, (int)to_ticks);
}
void
sleep_setup(struct sleep_state *sls, const volatile void *ident, int prio,
const char *wmesg, int timo)
{
struct proc *p = curproc;
#ifdef DIAGNOSTIC
if (p->p_flag & P_CANTSLEEP)
panic("sleep: %s failed insomnia", p->p_p->ps_comm);
if (ident == NULL)
panic("tsleep: no ident");
if (p->p_stat != SONPROC)
panic("tsleep: not SONPROC");
#endif
sls->sls_catch = prio & PCATCH;
sls->sls_timeout = 0;
SCHED_LOCK(sls->sls_s);
TRACEPOINT(sched, sleep, NULL);
p->p_wchan = ident;
p->p_wmesg = wmesg;
p->p_slptime = 0;
p->p_slppri = prio & PRIMASK;
TAILQ_INSERT_TAIL(&slpque[LOOKUP(ident)], p, p_runq);
if (timo) {
KASSERT((p->p_flag & P_TIMEOUT) == 0);
sls->sls_timeout = 1;
timeout_add(&p->p_sleep_to, timo);
}
}
int
sleep_finish(struct sleep_state *sls, int do_sleep)
{
struct proc *p = curproc;
int error = 0, error1 = 0;
if (sls->sls_catch != 0) {
/*
* We put ourselves on the sleep queue and start our
* timeout before calling sleep_signal_check(), as we could
* stop there, and a wakeup or a SIGCONT (or both) could
* occur while we were stopped. A SIGCONT would cause
* us to be marked as SSLEEP without resuming us, thus
* we must be ready for sleep when sleep_signal_check() is
* called.
* If the wakeup happens while we're stopped, p->p_wchan
* will be NULL upon return from sleep_signal_check(). In
* that case we need to unwind immediately.
*/
atomic_setbits_int(&p->p_flag, P_SINTR);
if ((error = sleep_signal_check()) != 0) {
p->p_stat = SONPROC;
sls->sls_catch = 0;
do_sleep = 0;
} else if (p->p_wchan == NULL) {
sls->sls_catch = 0;
do_sleep = 0;
}
}
if (do_sleep) {
p->p_stat = SSLEEP;
p->p_ru.ru_nvcsw++;
SCHED_ASSERT_LOCKED();
mi_switch();
} else {
unsleep(p);
}
#ifdef DIAGNOSTIC
if (p->p_stat != SONPROC)
panic("sleep_finish !SONPROC");
#endif
p->p_cpu->ci_schedstate.spc_curpriority = p->p_usrpri;
SCHED_UNLOCK(sls->sls_s);
/*
* Even though this belongs to the signal handling part of sleep,
* we need to clear it before the ktrace.
*/
atomic_clearbits_int(&p->p_flag, P_SINTR);
if (sls->sls_timeout) {
if (p->p_flag & P_TIMEOUT) {
error1 = EWOULDBLOCK;
} else {
/* This can sleep. It must not use timeouts. */
timeout_del_barrier(&p->p_sleep_to);
}
atomic_clearbits_int(&p->p_flag, P_TIMEOUT);
}
/* Check if thread was woken up because of a unwind or signal */
if (sls->sls_catch != 0)
error = sleep_signal_check();
/* Signal errors are higher priority than timeouts. */
if (error == 0 && error1 != 0)
error = error1;
return error;
}
/*
* Check and handle signals and suspensions around a sleep cycle.
*/
int
sleep_signal_check(void)
{
struct proc *p = curproc;
struct sigctx ctx;
int err, sig;
if ((err = single_thread_check(p, 1)) != 0)
return err;
if ((sig = cursig(p, &ctx)) != 0) {
if (ctx.sig_intr)
return EINTR;
else
return ERESTART;
}
return 0;
}
int
wakeup_proc(struct proc *p, const volatile void *chan)
{
int s, awakened = 0;
SCHED_LOCK(s);
if (p->p_wchan != NULL &&
((chan == NULL) || (p->p_wchan == chan))) {
awakened = 1;
if (p->p_stat == SSLEEP)
setrunnable(p);
else
unsleep(p);
}
SCHED_UNLOCK(s);
return awakened;
}
/*
* Implement timeout for tsleep.
* If process hasn't been awakened (wchan non-zero),
* set timeout flag and undo the sleep. If proc
* is stopped, just unsleep so it will remain stopped.
*/
void
endtsleep(void *arg)
{
struct proc *p = arg;
int s;
SCHED_LOCK(s);
if (wakeup_proc(p, NULL))
atomic_setbits_int(&p->p_flag, P_TIMEOUT);
SCHED_UNLOCK(s);
}
/*
* Remove a process from its wait queue
*/
void
unsleep(struct proc *p)
{
SCHED_ASSERT_LOCKED();
if (p->p_wchan != NULL) {
TAILQ_REMOVE(&slpque[LOOKUP(p->p_wchan)], p, p_runq);
p->p_wchan = NULL;
TRACEPOINT(sched, wakeup, p->p_tid + THREAD_PID_OFFSET,
p->p_p->ps_pid);
}
}
/*
* Make a number of processes sleeping on the specified identifier runnable.
*/
void
wakeup_n(const volatile void *ident, int n)
{
struct slpque *qp;
struct proc *p;
struct proc *pnext;
int s;
SCHED_LOCK(s);
qp = &slpque[LOOKUP(ident)];
for (p = TAILQ_FIRST(qp); p != NULL && n != 0; p = pnext) {
pnext = TAILQ_NEXT(p, p_runq);
/*
* This happens if wakeup(9) is called after enqueuing
* itself on the sleep queue and both `ident' collide.
*/
if (p == curproc)
continue;
#ifdef DIAGNOSTIC
if (p->p_stat != SSLEEP && p->p_stat != SSTOP)
panic("wakeup: p_stat is %d", (int)p->p_stat);
#endif
if (wakeup_proc(p, ident))
--n;
}
SCHED_UNLOCK(s);
}
/*
* Make all processes sleeping on the specified identifier runnable.
*/
void
wakeup(const volatile void *chan)
{
wakeup_n(chan, -1);
}
int
sys_sched_yield(struct proc *p, void *v, register_t *retval)
{
struct proc *q;
uint8_t newprio;
int s;
SCHED_LOCK(s);
/*
* If one of the threads of a multi-threaded process called
* sched_yield(2), drop its priority to ensure its siblings
* can make some progress.
*/
newprio = p->p_usrpri;
TAILQ_FOREACH(q, &p->p_p->ps_threads, p_thr_link)
newprio = max(newprio, q->p_runpri);
setrunqueue(p->p_cpu, p, newprio);
p->p_ru.ru_nvcsw++;
mi_switch();
SCHED_UNLOCK(s);
return (0);
}
int
thrsleep_unlock(void *lock)
{
static _atomic_lock_t unlocked = _ATOMIC_LOCK_UNLOCKED;
_atomic_lock_t *atomiclock = lock;
if (!lock)
return 0;
return copyout(&unlocked, atomiclock, sizeof(unlocked));
}
struct tslpentry {
TAILQ_ENTRY(tslpentry) tslp_link;
long tslp_ident;
};
/* thrsleep queue shared between processes */
static struct tslpqueue thrsleep_queue = TAILQ_HEAD_INITIALIZER(thrsleep_queue);
static struct rwlock thrsleep_lock = RWLOCK_INITIALIZER("thrsleeplk");
int
thrsleep(struct proc *p, struct sys___thrsleep_args *v)
{
struct sys___thrsleep_args /* {
syscallarg(const volatile void *) ident;
syscallarg(clockid_t) clock_id;
syscallarg(const struct timespec *) tp;
syscallarg(void *) lock;
syscallarg(const int *) abort;
} */ *uap = v;
long ident = (long)SCARG(uap, ident);
struct tslpentry entry;
struct tslpqueue *queue;
struct rwlock *qlock;
struct timespec *tsp = (struct timespec *)SCARG(uap, tp);
void *lock = SCARG(uap, lock);
uint64_t nsecs = INFSLP;
int abort = 0, error;
clockid_t clock_id = SCARG(uap, clock_id);
if (ident == 0)
return (EINVAL);
if (tsp != NULL) {
struct timespec now;
if ((error = clock_gettime(p, clock_id, &now)))
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrabstimespec(p, tsp);
#endif
if (timespeccmp(tsp, &now, <=)) {
/* already passed: still do the unlock */
if ((error = thrsleep_unlock(lock)))
return (error);
return (EWOULDBLOCK);
}
timespecsub(tsp, &now, tsp);
nsecs = MIN(TIMESPEC_TO_NSEC(tsp), MAXTSLP);
}
if (ident == -1) {
queue = &thrsleep_queue;
qlock = &thrsleep_lock;
} else {
queue = &p->p_p->ps_tslpqueue;
qlock = &p->p_p->ps_lock;
}
/* Interlock with wakeup. */
entry.tslp_ident = ident;
rw_enter_write(qlock);
TAILQ_INSERT_TAIL(queue, &entry, tslp_link);
rw_exit_write(qlock);
error = thrsleep_unlock(lock);
if (error == 0 && SCARG(uap, abort) != NULL)
error = copyin(SCARG(uap, abort), &abort, sizeof(abort));
rw_enter_write(qlock);
if (error != 0)
goto out;
if (abort != 0) {
error = EINTR;
goto out;
}
if (entry.tslp_ident != 0) {
error = rwsleep_nsec(&entry, qlock, PWAIT|PCATCH, "thrsleep",
nsecs);
}
out:
if (entry.tslp_ident != 0)
TAILQ_REMOVE(queue, &entry, tslp_link);
rw_exit_write(qlock);
if (error == ERESTART)
error = ECANCELED;
return (error);
}
int
sys___thrsleep(struct proc *p, void *v, register_t *retval)
{
struct sys___thrsleep_args /* {
syscallarg(const volatile void *) ident;
syscallarg(clockid_t) clock_id;
syscallarg(struct timespec *) tp;
syscallarg(void *) lock;
syscallarg(const int *) abort;
} */ *uap = v;
struct timespec ts;
int error;
if (SCARG(uap, tp) != NULL) {
if ((error = copyin(SCARG(uap, tp), &ts, sizeof(ts)))) {
*retval = error;
return 0;
}
if (!timespecisvalid(&ts)) {
*retval = EINVAL;
return 0;
}
SCARG(uap, tp) = &ts;
}
*retval = thrsleep(p, uap);
return 0;
}
int
sys___thrwakeup(struct proc *p, void *v, register_t *retval)
{
struct sys___thrwakeup_args /* {
syscallarg(const volatile void *) ident;
syscallarg(int) n;
} */ *uap = v;
struct tslpentry *entry, *tmp;
struct tslpqueue *queue;
struct rwlock *qlock;
long ident = (long)SCARG(uap, ident);
int n = SCARG(uap, n);
int found = 0;
if (ident == 0)
*retval = EINVAL;
else {
if (ident == -1) {
queue = &thrsleep_queue;
qlock = &thrsleep_lock;
/*
* Wake up all waiters with ident -1. This is needed
* because ident -1 can be shared by multiple userspace
* lock state machines concurrently. The implementation
* has no way to direct the wakeup to a particular
* state machine.
*/
n = 0;
} else {
queue = &p->p_p->ps_tslpqueue;
qlock = &p->p_p->ps_lock;
}
rw_enter_write(qlock);
TAILQ_FOREACH_SAFE(entry, queue, tslp_link, tmp) {
if (entry->tslp_ident == ident) {
TAILQ_REMOVE(queue, entry, tslp_link);
entry->tslp_ident = 0;
wakeup_one(entry);
if (++found == n)
break;
}
}
rw_exit_write(qlock);
if (ident == -1)
*retval = 0;
else
*retval = found ? 0 : ESRCH;
}
return (0);
}
void
refcnt_init(struct refcnt *r)
{
refcnt_init_trace(r, 0);
}
void
refcnt_init_trace(struct refcnt *r, int idx)
{
r->r_traceidx = idx;
atomic_store_int(&r->r_refs, 1);
TRACEINDEX(refcnt, r->r_traceidx, r, 0, +1);
}
void
refcnt_take(struct refcnt *r)
{
u_int refs;
refs = atomic_inc_int_nv(&r->r_refs);
KASSERT(refs != 0);
TRACEINDEX(refcnt, r->r_traceidx, r, refs - 1, +1);
(void)refs;
}
int
refcnt_rele(struct refcnt *r)
{
u_int refs;
membar_exit_before_atomic();
refs = atomic_dec_int_nv(&r->r_refs);
KASSERT(refs != ~0);
TRACEINDEX(refcnt, r->r_traceidx, r, refs + 1, -1);
if (refs == 0) {
membar_enter_after_atomic();
return (1);
}
return (0);
}
void
refcnt_rele_wake(struct refcnt *r)
{
if (refcnt_rele(r))
wakeup_one(r);
}
void
refcnt_finalize(struct refcnt *r, const char *wmesg)
{
struct sleep_state sls;
u_int refs;
membar_exit_before_atomic();
refs = atomic_dec_int_nv(&r->r_refs);
KASSERT(refs != ~0);
TRACEINDEX(refcnt, r->r_traceidx, r, refs + 1, -1);
while (refs) {
sleep_setup(&sls, r, PWAIT, wmesg, 0);
refs = atomic_load_int(&r->r_refs);
sleep_finish(&sls, refs);
}
TRACEINDEX(refcnt, r->r_traceidx, r, refs, 0);
/* Order subsequent loads and stores after refs == 0 load. */
membar_sync();
}
int
refcnt_shared(struct refcnt *r)
{
u_int refs;
refs = atomic_load_int(&r->r_refs);
TRACEINDEX(refcnt, r->r_traceidx, r, refs, 0);
return (refs > 1);
}
unsigned int
refcnt_read(struct refcnt *r)
{
u_int refs;
refs = atomic_load_int(&r->r_refs);
TRACEINDEX(refcnt, r->r_traceidx, r, refs, 0);
return (refs);
}
void
cond_init(struct cond *c)
{
atomic_store_int(&c->c_wait, 1);
}
void
cond_signal(struct cond *c)
{
atomic_store_int(&c->c_wait, 0);
wakeup_one(c);
}
void
cond_wait(struct cond *c, const char *wmesg)
{
struct sleep_state sls;
unsigned int wait;
wait = atomic_load_int(&c->c_wait);
while (wait) {
sleep_setup(&sls, c, PWAIT, wmesg, 0);
wait = atomic_load_int(&c->c_wait);
sleep_finish(&sls, wait);
}
}
416
61
39
55
36
2
28
31
25
20
2
2
2
372
2
2
15
6
7
6
3
4
168
169
316
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/* $OpenBSD: uipc_domain.c,v 1.60 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: uipc_domain.c,v 1.14 1996/02/09 19:00:44 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_domain.c 8.2 (Berkeley) 10/18/93
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/timeout.h>
#include "bpfilter.h"
#include "pflow.h"
const struct domain *const domains[] = {
#ifdef MPLS
&mplsdomain,
#endif
#if defined (IPSEC) || defined (TCP_SIGNATURE)
&pfkeydomain,
#endif
#ifdef INET6
&inet6domain,
#endif /* INET6 */
&inetdomain,
&unixdomain,
&routedomain,
NULL
};
void pffasttimo(void *);
void pfslowtimo(void *);
const struct domain * pffinddomain(int);
void
domaininit(void)
{
const struct domain *dp;
const struct protosw *pr;
static struct timeout pffast_timeout;
static struct timeout pfslow_timeout;
int i;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_init)
(*dp->dom_init)();
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_init)
(*pr->pr_init)();
}
/*
* max_linkhdr of 64 was chosen to encompass tunnelling
* traffic in IP payloads, eg, by etherip(4) or gif(4),
* without needing to prepend an mbuf to fit those
* headers.
*/
if (max_linkhdr < 64)
max_linkhdr = 64;
max_hdr = max_linkhdr + max_protohdr;
timeout_set_proc(&pffast_timeout, pffasttimo, &pffast_timeout);
timeout_set_proc(&pfslow_timeout, pfslowtimo, &pfslow_timeout);
timeout_add(&pffast_timeout, 1);
timeout_add(&pfslow_timeout, 1);
}
const struct domain *
pffinddomain(int family)
{
const struct domain *dp;
int i;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_family == family)
return (dp);
}
return (NULL);
}
const struct protosw *
pffindtype(int family, int type)
{
const struct domain *dp;
const struct protosw *pr;
dp = pffinddomain(family);
if (dp == NULL)
return (NULL);
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_type && pr->pr_type == type)
return (pr);
return (NULL);
}
const struct protosw *
pffindproto(int family, int protocol, int type)
{
const struct domain *dp;
const struct protosw *pr;
const struct protosw *maybe = NULL;
if (family == PF_UNSPEC)
return (NULL);
dp = pffinddomain(family);
if (dp == NULL)
return (NULL);
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
if ((pr->pr_protocol == protocol) && (pr->pr_type == type))
return (pr);
if (type == SOCK_RAW && pr->pr_type == SOCK_RAW &&
pr->pr_protocol == 0 && maybe == NULL)
maybe = pr;
}
return (maybe);
}
static int
net_link_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int node;
int error;
/*
* All sysctl names at this level are nonterminal.
*/
if (namelen < 2)
return (EISDIR); /* overloaded */
node = name[0];
namelen--;
name++;
switch (node) {
case NET_LINK_IFRXQ:
error = net_ifiq_sysctl(name, namelen, oldp, oldlenp,
newp, newlen);
break;
default:
error = ENOPROTOOPT;
break;
}
return (error);
}
int
net_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
const struct domain *dp;
const struct protosw *pr;
int error, family, protocol;
/*
* All sysctl names at this level are nonterminal.
* Usually: next two components are protocol family and protocol
* number, then at least one addition component.
*/
if (namelen < 2)
return (EISDIR); /* overloaded */
family = name[0];
if (family == PF_UNSPEC)
return (0);
if (family == PF_LINK)
return (net_link_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
if (family == PF_UNIX)
return (uipc_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
#if NBPFILTER > 0
if (family == PF_BPF)
return (bpf_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
#endif
#if NPFLOW > 0
if (family == PF_PFLOW)
return (pflow_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
#endif
#ifdef PIPEX
if (family == PF_PIPEX)
return (pipex_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
#endif
#ifdef MPLS
if (family == PF_MPLS)
return (mpls_sysctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen));
#endif
dp = pffinddomain(family);
if (dp == NULL)
return (ENOPROTOOPT);
if (namelen < 3)
return (EISDIR); /* overloaded */
protocol = name[1];
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_protocol == protocol && pr->pr_sysctl) {
error = (*pr->pr_sysctl)(name + 2, namelen - 2,
oldp, oldlenp, newp, newlen);
return (error);
}
return (ENOPROTOOPT);
}
void
pfctlinput(int cmd, struct sockaddr *sa)
{
const struct domain *dp;
const struct protosw *pr;
int i;
NET_ASSERT_LOCKED();
for (i = 0; (dp = domains[i]) != NULL; i++) {
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_ctlinput)
(*pr->pr_ctlinput)(cmd, sa, 0, NULL);
}
}
void
pfslowtimo(void *arg)
{
struct timeout *to = (struct timeout *)arg;
const struct domain *dp;
const struct protosw *pr;
int i;
for (i = 0; (dp = domains[i]) != NULL; i++) {
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_slowtimo)
(*pr->pr_slowtimo)();
}
timeout_add_msec(to, 500);
}
void
pffasttimo(void *arg)
{
struct timeout *to = (struct timeout *)arg;
const struct domain *dp;
const struct protosw *pr;
int i;
for (i = 0; (dp = domains[i]) != NULL; i++) {
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_fasttimo)
(*pr->pr_fasttimo)();
}
timeout_add_msec(to, 200);
}
19
3
2
6
2
4
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/* $OpenBSD: uvm_swap_encrypt.c,v 1.24 2021/03/12 14:15:49 jsg Exp $ */
/*
* Copyright 1999 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <crypto/rijndael.h>
#include <uvm/uvm.h>
#include <uvm/uvm_swap_encrypt.h>
struct swap_key *kcur = NULL;
rijndael_ctx swap_ctxt;
int uvm_doswapencrypt = 1;
u_int uvm_swpkeyscreated = 0;
u_int uvm_swpkeysdeleted = 0;
int swap_encrypt_initialized = 0;
int
swap_encrypt_ctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen, struct proc *p)
{
/* all sysctl names at this level are terminal */
if (namelen != 1)
return (ENOTDIR); /* overloaded */
switch (name[0]) {
case SWPENC_ENABLE: {
int doencrypt = uvm_doswapencrypt;
int result;
result = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&doencrypt, 0, 1);
if (result)
return result;
/*
* Swap Encryption has been turned on, we need to
* initialize state for swap devices that have been
* added.
*/
if (doencrypt)
uvm_swap_initcrypt_all();
uvm_doswapencrypt = doencrypt;
return (0);
}
case SWPENC_CREATED:
return (sysctl_rdint(oldp, oldlenp, newp, uvm_swpkeyscreated));
case SWPENC_DELETED:
return (sysctl_rdint(oldp, oldlenp, newp, uvm_swpkeysdeleted));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
void
swap_key_create(struct swap_key *key)
{
arc4random_buf(key->key, sizeof(key->key));
uvm_swpkeyscreated++;
}
void
swap_key_delete(struct swap_key *key)
{
/* Make sure that this key gets removed if we just used it */
swap_key_cleanup(key);
explicit_bzero(key, sizeof(*key));
uvm_swpkeysdeleted++;
}
/*
* Encrypt the data before it goes to swap, the size should be 64-bit
* aligned.
*/
void
swap_encrypt(struct swap_key *key, caddr_t src, caddr_t dst, u_int64_t block,
size_t count)
{
u_int32_t *dsrc = (u_int32_t *)src;
u_int32_t *ddst = (u_int32_t *)dst;
u_int32_t iv[4];
u_int32_t iv1, iv2, iv3, iv4;
if (!swap_encrypt_initialized)
swap_encrypt_initialized = 1;
swap_key_prepare(key, 1);
count /= sizeof(u_int32_t);
iv[0] = block >> 32; iv[1] = block; iv[2] = ~iv[0]; iv[3] = ~iv[1];
rijndael_encrypt(&swap_ctxt, (u_char *)iv, (u_char *)iv);
iv1 = iv[0]; iv2 = iv[1]; iv3 = iv[2]; iv4 = iv[3];
for (; count > 0; count -= 4) {
ddst[0] = dsrc[0] ^ iv1;
ddst[1] = dsrc[1] ^ iv2;
ddst[2] = dsrc[2] ^ iv3;
ddst[3] = dsrc[3] ^ iv4;
/*
* Do not worry about endianness, it only needs to decrypt
* on this machine.
*/
rijndael_encrypt(&swap_ctxt, (u_char *)ddst, (u_char *)ddst);
iv1 = ddst[0];
iv2 = ddst[1];
iv3 = ddst[2];
iv4 = ddst[3];
dsrc += 4;
ddst += 4;
}
}
/*
* Decrypt the data after we retrieved it from swap, the size should be 64-bit
* aligned.
*/
void
swap_decrypt(struct swap_key *key, caddr_t src, caddr_t dst, u_int64_t block,
size_t count)
{
u_int32_t *dsrc = (u_int32_t *)src;
u_int32_t *ddst = (u_int32_t *)dst;
u_int32_t iv[4];
u_int32_t iv1, iv2, iv3, iv4, niv1, niv2, niv3, niv4;
if (!swap_encrypt_initialized)
panic("swap_decrypt: key not initialized");
swap_key_prepare(key, 0);
count /= sizeof(u_int32_t);
iv[0] = block >> 32; iv[1] = block; iv[2] = ~iv[0]; iv[3] = ~iv[1];
rijndael_encrypt(&swap_ctxt, (u_char *)iv, (u_char *)iv);
iv1 = iv[0]; iv2 = iv[1]; iv3 = iv[2]; iv4 = iv[3];
for (; count > 0; count -= 4) {
ddst[0] = niv1 = dsrc[0];
ddst[1] = niv2 = dsrc[1];
ddst[2] = niv3 = dsrc[2];
ddst[3] = niv4 = dsrc[3];
rijndael_decrypt(&swap_ctxt, (u_char *)ddst, (u_char *)ddst);
ddst[0] ^= iv1;
ddst[1] ^= iv2;
ddst[2] ^= iv3;
ddst[3] ^= iv4;
iv1 = niv1;
iv2 = niv2;
iv3 = niv3;
iv4 = niv4;
dsrc += 4;
ddst += 4;
}
}
void
swap_key_prepare(struct swap_key *key, int encrypt)
{
/*
* Check if we have prepared for this key already,
* if we only have the encryption schedule, we have
* to recompute and get the decryption schedule also.
*/
if (kcur == key && (encrypt || !swap_ctxt.enc_only))
return;
if (encrypt)
rijndael_set_key_enc_only(&swap_ctxt, (u_char *)key->key,
sizeof(key->key) * 8);
else
rijndael_set_key(&swap_ctxt, (u_char *)key->key,
sizeof(key->key) * 8);
kcur = key;
}
/*
* Make sure that a specific key is no longer available.
*/
void
swap_key_cleanup(struct swap_key *key)
{
/* Check if we have a key */
if (kcur == NULL || kcur != key)
return;
/* Zero out the subkeys */
explicit_bzero(&swap_ctxt, sizeof(swap_ctxt));
kcur = NULL;
}
8
2
2
2
2
2
1
1
334
3
236
67
1
2
1
1
2
1
2
2
1
2
1
1
1
2
1
1
1
1
1
1
1
1
2
1
11
11
11
11
11
11
27
65
56
56
14
10
3
11
10
81
67
3
38
3
5
2
11
57
67
4
3
4
3
14
23
19
2
19
10
7
7
22
22
18
18
3
8
3
4
1
2
1
5
17
17
14
2
16
14
15
12
2
8
3
1
2
2
28
14
12
1
32
4
27
1
29
29
2
1
1
6
1
1
1
16
2
1
1
5
11
2
1
9
4
13
1
1
1
75
2
2
2
1
19
10
7
9
1
8
2
2
9
5
89
10
81
20
81
3
43
69
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
/* $OpenBSD: tcp_usrreq.c,v 1.207 2022/09/03 22:43:38 mvs Exp $ */
/* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/domain.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#endif
#ifndef TCP_SENDSPACE
#define TCP_SENDSPACE 1024*16
#endif
u_int tcp_sendspace = TCP_SENDSPACE;
#ifndef TCP_RECVSPACE
#define TCP_RECVSPACE 1024*16
#endif
u_int tcp_recvspace = TCP_RECVSPACE;
u_int tcp_autorcvbuf_inc = 16 * 1024;
const struct pr_usrreqs tcp_usrreqs = {
.pru_attach = tcp_attach,
.pru_detach = tcp_detach,
.pru_bind = tcp_bind,
.pru_listen = tcp_listen,
.pru_connect = tcp_connect,
.pru_accept = tcp_accept,
.pru_disconnect = tcp_disconnect,
.pru_shutdown = tcp_shutdown,
.pru_rcvd = tcp_rcvd,
.pru_send = tcp_send,
.pru_abort = tcp_abort,
.pru_sense = tcp_sense,
.pru_rcvoob = tcp_rcvoob,
.pru_sendoob = tcp_sendoob,
.pru_control = in_control,
.pru_sockaddr = tcp_sockaddr,
.pru_peeraddr = tcp_peeraddr,
};
#ifdef INET6
const struct pr_usrreqs tcp6_usrreqs = {
.pru_attach = tcp_attach,
.pru_detach = tcp_detach,
.pru_bind = tcp_bind,
.pru_listen = tcp_listen,
.pru_connect = tcp_connect,
.pru_accept = tcp_accept,
.pru_disconnect = tcp_disconnect,
.pru_shutdown = tcp_shutdown,
.pru_rcvd = tcp_rcvd,
.pru_send = tcp_send,
.pru_abort = tcp_abort,
.pru_sense = tcp_sense,
.pru_rcvoob = tcp_rcvoob,
.pru_sendoob = tcp_sendoob,
.pru_control = in6_control,
.pru_sockaddr = tcp_sockaddr,
.pru_peeraddr = tcp_peeraddr,
};
#endif
static int pr_slowhz = PR_SLOWHZ;
const struct sysctl_bounded_args tcpctl_vars[] = {
{ TCPCTL_SLOWHZ, &pr_slowhz, SYSCTL_INT_READONLY },
{ TCPCTL_RFC1323, &tcp_do_rfc1323, 0, 1 },
{ TCPCTL_KEEPINITTIME, &tcptv_keep_init, 1, 3 * TCPTV_KEEP_INIT },
{ TCPCTL_KEEPIDLE, &tcp_keepidle, 1, 5 * TCPTV_KEEP_IDLE },
{ TCPCTL_KEEPINTVL, &tcp_keepintvl, 1, 3 * TCPTV_KEEPINTVL },
{ TCPCTL_SACK, &tcp_do_sack, 0, 1 },
{ TCPCTL_MSSDFLT, &tcp_mssdflt, TCP_MSS, 65535 },
{ TCPCTL_RSTPPSLIMIT, &tcp_rst_ppslim, 1, 1000 * 1000 },
{ TCPCTL_ACK_ON_PUSH, &tcp_ack_on_push, 0, 1 },
#ifdef TCP_ECN
{ TCPCTL_ECN, &tcp_do_ecn, 0, 1 },
#endif
{ TCPCTL_SYN_CACHE_LIMIT, &tcp_syn_cache_limit, 1, 1000 * 1000 },
{ TCPCTL_SYN_BUCKET_LIMIT, &tcp_syn_bucket_limit, 1, INT_MAX },
{ TCPCTL_RFC3390, &tcp_do_rfc3390, 0, 2 },
{ TCPCTL_ALWAYS_KEEPALIVE, &tcp_always_keepalive, 0, 1 },
};
struct inpcbtable tcbtable;
int tcp_fill_info(struct tcpcb *, struct socket *, struct mbuf *);
int tcp_ident(void *, size_t *, void *, size_t, int);
static inline int tcp_sogetpcb(struct socket *, struct inpcb **,
struct tcpcb **);
static inline int
tcp_sogetpcb(struct socket *so, struct inpcb **rinp, struct tcpcb **rtp)
{
struct inpcb *inp;
struct tcpcb *tp;
/*
* When a TCP is attached to a socket, then there will be
* a (struct inpcb) pointed at by the socket, and this
* structure will point at a subsidiary (struct tcpcb).
*/
if ((inp = sotoinpcb(so)) == NULL || (tp = intotcpcb(inp)) == NULL) {
if (so->so_error)
return so->so_error;
return EINVAL;
}
*rinp = inp;
*rtp = tp;
return 0;
}
/*
* Export internal TCP state information via a struct tcp_info without
* leaking any sensitive information. Sequence numbers are reported
* relative to the initial sequence number.
*/
int
tcp_fill_info(struct tcpcb *tp, struct socket *so, struct mbuf *m)
{
struct proc *p = curproc;
struct tcp_info *ti;
u_int t = 1000000 / PR_SLOWHZ;
uint32_t now;
if (sizeof(*ti) > MLEN) {
MCLGETL(m, M_WAITOK, sizeof(*ti));
if (!ISSET(m->m_flags, M_EXT))
return ENOMEM;
}
ti = mtod(m, struct tcp_info *);
m->m_len = sizeof(*ti);
memset(ti, 0, sizeof(*ti));
now = READ_ONCE(tcp_now);
ti->tcpi_state = tp->t_state;
if ((tp->t_flags & TF_REQ_TSTMP) && (tp->t_flags & TF_RCVD_TSTMP))
ti->tcpi_options |= TCPI_OPT_TIMESTAMPS;
if (tp->t_flags & TF_SACK_PERMIT)
ti->tcpi_options |= TCPI_OPT_SACK;
if ((tp->t_flags & TF_REQ_SCALE) && (tp->t_flags & TF_RCVD_SCALE)) {
ti->tcpi_options |= TCPI_OPT_WSCALE;
ti->tcpi_snd_wscale = tp->snd_scale;
ti->tcpi_rcv_wscale = tp->rcv_scale;
}
#ifdef TCP_ECN
if (tp->t_flags & TF_ECN_PERMIT)
ti->tcpi_options |= TCPI_OPT_ECN;
#endif
ti->tcpi_rto = tp->t_rxtcur * t;
ti->tcpi_snd_mss = tp->t_maxseg;
ti->tcpi_rcv_mss = tp->t_peermss;
ti->tcpi_last_data_sent = (now - tp->t_sndtime) * t;
ti->tcpi_last_ack_sent = (now - tp->t_sndacktime) * t;
ti->tcpi_last_data_recv = (now - tp->t_rcvtime) * t;
ti->tcpi_last_ack_recv = (now - tp->t_rcvacktime) * t;
ti->tcpi_rtt = ((uint64_t)tp->t_srtt * t) >>
(TCP_RTT_SHIFT + TCP_RTT_BASE_SHIFT);
ti->tcpi_rttvar = ((uint64_t)tp->t_rttvar * t) >>
(TCP_RTTVAR_SHIFT + TCP_RTT_BASE_SHIFT);
ti->tcpi_snd_ssthresh = tp->snd_ssthresh;
ti->tcpi_snd_cwnd = tp->snd_cwnd;
ti->tcpi_rcv_space = tp->rcv_wnd;
/*
* Provide only minimal information for unprivileged processes.
*/
if (suser(p) != 0)
return 0;
/* FreeBSD-specific extension fields for tcp_info. */
ti->tcpi_snd_wnd = tp->snd_wnd;
ti->tcpi_snd_nxt = tp->snd_nxt - tp->iss;
ti->tcpi_rcv_nxt = tp->rcv_nxt - tp->irs;
/* missing tcpi_toe_tid */
ti->tcpi_snd_rexmitpack = tp->t_sndrexmitpack;
ti->tcpi_rcv_ooopack = tp->t_rcvoopack;
ti->tcpi_snd_zerowin = tp->t_sndzerowin;
/* OpenBSD extensions */
ti->tcpi_rttmin = tp->t_rttmin * t;
ti->tcpi_max_sndwnd = tp->max_sndwnd;
ti->tcpi_rcv_adv = tp->rcv_adv - tp->irs;
ti->tcpi_rcv_up = tp->rcv_up - tp->irs;
ti->tcpi_snd_una = tp->snd_una - tp->iss;
ti->tcpi_snd_up = tp->snd_up - tp->iss;
ti->tcpi_snd_wl1 = tp->snd_wl1 - tp->iss;
ti->tcpi_snd_wl2 = tp->snd_wl2 - tp->iss;
ti->tcpi_snd_max = tp->snd_max - tp->iss;
ti->tcpi_ts_recent = tp->ts_recent; /* XXX value from the wire */
ti->tcpi_ts_recent_age = (now - tp->ts_recent_age) * t;
ti->tcpi_rfbuf_cnt = tp->rfbuf_cnt;
ti->tcpi_rfbuf_ts = (now - tp->rfbuf_ts) * t;
ti->tcpi_so_rcv_sb_cc = so->so_rcv.sb_cc;
ti->tcpi_so_rcv_sb_hiwat = so->so_rcv.sb_hiwat;
ti->tcpi_so_rcv_sb_lowat = so->so_rcv.sb_lowat;
ti->tcpi_so_rcv_sb_wat = so->so_rcv.sb_wat;
ti->tcpi_so_snd_sb_cc = so->so_snd.sb_cc;
ti->tcpi_so_snd_sb_hiwat = so->so_snd.sb_hiwat;
ti->tcpi_so_snd_sb_lowat = so->so_snd.sb_lowat;
ti->tcpi_so_snd_sb_wat = so->so_snd.sb_wat;
return 0;
}
int
tcp_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
int error = 0;
struct inpcb *inp;
struct tcpcb *tp;
int i;
inp = sotoinpcb(so);
if (inp == NULL)
return (ECONNRESET);
if (level != IPPROTO_TCP) {
switch (so->so_proto->pr_domain->dom_family) {
#ifdef INET6
case PF_INET6:
error = ip6_ctloutput(op, so, level, optname, m);
break;
#endif /* INET6 */
case PF_INET:
error = ip_ctloutput(op, so, level, optname, m);
break;
default:
error = EAFNOSUPPORT; /*?*/
break;
}
return (error);
}
tp = intotcpcb(inp);
switch (op) {
case PRCO_SETOPT:
switch (optname) {
case TCP_NODELAY:
if (m == NULL || m->m_len < sizeof (int))
error = EINVAL;
else if (*mtod(m, int *))
tp->t_flags |= TF_NODELAY;
else
tp->t_flags &= ~TF_NODELAY;
break;
case TCP_NOPUSH:
if (m == NULL || m->m_len < sizeof (int))
error = EINVAL;
else if (*mtod(m, int *))
tp->t_flags |= TF_NOPUSH;
else if (tp->t_flags & TF_NOPUSH) {
tp->t_flags &= ~TF_NOPUSH;
if (TCPS_HAVEESTABLISHED(tp->t_state))
error = tcp_output(tp);
}
break;
case TCP_MAXSEG:
if (m == NULL || m->m_len < sizeof (int)) {
error = EINVAL;
break;
}
i = *mtod(m, int *);
if (i > 0 && i <= tp->t_maxseg)
tp->t_maxseg = i;
else
error = EINVAL;
break;
case TCP_SACK_ENABLE:
if (m == NULL || m->m_len < sizeof (int)) {
error = EINVAL;
break;
}
if (TCPS_HAVEESTABLISHED(tp->t_state)) {
error = EPERM;
break;
}
if (tp->t_flags & TF_SIGNATURE) {
error = EPERM;
break;
}
if (*mtod(m, int *))
tp->sack_enable = 1;
else
tp->sack_enable = 0;
break;
#ifdef TCP_SIGNATURE
case TCP_MD5SIG:
if (m == NULL || m->m_len < sizeof (int)) {
error = EINVAL;
break;
}
if (TCPS_HAVEESTABLISHED(tp->t_state)) {
error = EPERM;
break;
}
if (*mtod(m, int *)) {
tp->t_flags |= TF_SIGNATURE;
tp->sack_enable = 0;
} else
tp->t_flags &= ~TF_SIGNATURE;
break;
#endif /* TCP_SIGNATURE */
default:
error = ENOPROTOOPT;
break;
}
break;
case PRCO_GETOPT:
switch (optname) {
case TCP_NODELAY:
m->m_len = sizeof(int);
*mtod(m, int *) = tp->t_flags & TF_NODELAY;
break;
case TCP_NOPUSH:
m->m_len = sizeof(int);
*mtod(m, int *) = tp->t_flags & TF_NOPUSH;
break;
case TCP_MAXSEG:
m->m_len = sizeof(int);
*mtod(m, int *) = tp->t_maxseg;
break;
case TCP_SACK_ENABLE:
m->m_len = sizeof(int);
*mtod(m, int *) = tp->sack_enable;
break;
case TCP_INFO:
error = tcp_fill_info(tp, so, m);
break;
#ifdef TCP_SIGNATURE
case TCP_MD5SIG:
m->m_len = sizeof(int);
*mtod(m, int *) = tp->t_flags & TF_SIGNATURE;
break;
#endif
default:
error = ENOPROTOOPT;
break;
}
break;
}
return (error);
}
/*
* Attach TCP protocol to socket, allocating
* internet protocol control block, tcp control block,
* buffer space, and entering LISTEN state to accept connections.
*/
int
tcp_attach(struct socket *so, int proto)
{
struct tcpcb *tp;
struct inpcb *inp;
int error;
if (so->so_pcb)
return EISCONN;
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0 ||
sbcheckreserve(so->so_snd.sb_wat, tcp_sendspace) ||
sbcheckreserve(so->so_rcv.sb_wat, tcp_recvspace)) {
error = soreserve(so, tcp_sendspace, tcp_recvspace);
if (error)
return (error);
}
NET_ASSERT_LOCKED();
error = in_pcballoc(so, &tcbtable);
if (error)
return (error);
inp = sotoinpcb(so);
tp = tcp_newtcpcb(inp);
if (tp == NULL) {
unsigned int nofd = so->so_state & SS_NOFDREF; /* XXX */
so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */
in_pcbdetach(inp);
so->so_state |= nofd;
return (ENOBUFS);
}
tp->t_state = TCPS_CLOSED;
#ifdef INET6
/* we disallow IPv4 mapped address completely. */
if (inp->inp_flags & INP_IPV6)
tp->pf = PF_INET6;
else
tp->pf = PF_INET;
#else
tp->pf = PF_INET;
#endif
if ((so->so_options & SO_LINGER) && so->so_linger == 0)
so->so_linger = TCP_LINGERTIME;
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, TCPS_CLOSED, tp, tp, NULL, PRU_ATTACH, 0);
return (0);
}
int
tcp_detach(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *otp = NULL, *tp;
int error = 0;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
/*
* Detach the TCP protocol from the socket.
* If the protocol state is non-embryonic, then can't
* do this directly: have to initiate a PRU_DISCONNECT,
* which may finish later; embryonic TCB's can just
* be discarded here.
*/
tp = tcp_dodisconnect(tp);
if (otp)
tcp_trace(TA_USER, ostate, tp, otp, NULL, PRU_DETACH, 0);
return (error);
}
/*
* Give the socket an address.
*/
int
tcp_bind(struct socket *so, struct mbuf *nam, struct proc *p)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG)
ostate = tp->t_state;
error = in_pcbbind(inp, nam, p);
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, tp, NULL, PRU_BIND, 0);
return (error);
}
/*
* Prepare to accept connections.
*/
int
tcp_listen(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp, *otp = NULL;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
if (inp->inp_lport == 0)
if ((error = in_pcbbind(inp, NULL, curproc)))
goto out;
/*
* If the in_pcbbind() above is called, the tp->pf
* should still be whatever it was before.
*/
tp->t_state = TCPS_LISTEN;
out:
if (otp)
tcp_trace(TA_USER, ostate, tp, otp, NULL, PRU_LISTEN, 0);
return (error);
}
/*
* Initiate connection to peer.
* Create a template for use in transmissions on this connection.
* Enter SYN_SENT state, and mark socket as connecting.
* Start keep-alive timer, and seed output sequence space.
* Send initial segment on connection.
*/
int
tcp_connect(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp;
struct tcpcb *tp, *otp = NULL;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
#ifdef INET6
if (inp->inp_flags & INP_IPV6) {
struct sockaddr_in6 *sin6;
if ((error = in6_nam2sin6(nam, &sin6)))
goto out;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
error = EINVAL;
goto out;
}
error = in6_pcbconnect(inp, nam);
} else
#endif /* INET6 */
{
struct sockaddr_in *sin;
if ((error = in_nam2sin(nam, &sin)))
goto out;
if ((sin->sin_addr.s_addr == INADDR_ANY) ||
(sin->sin_addr.s_addr == INADDR_BROADCAST) ||
IN_MULTICAST(sin->sin_addr.s_addr) ||
in_broadcast(sin->sin_addr, inp->inp_rtableid)) {
error = EINVAL;
goto out;
}
error = in_pcbconnect(inp, nam);
}
if (error)
goto out;
tp->t_template = tcp_template(tp);
if (tp->t_template == 0) {
in_pcbdisconnect(inp);
error = ENOBUFS;
goto out;
}
so->so_state |= SS_CONNECTOUT;
/* Compute window scaling to request. */
tcp_rscale(tp, sb_max);
soisconnecting(so);
tcpstat_inc(tcps_connattempt);
tp->t_state = TCPS_SYN_SENT;
TCP_TIMER_ARM(tp, TCPT_KEEP, tcptv_keep_init);
tcp_set_iss_tsm(tp);
tcp_sendseqinit(tp);
tp->snd_last = tp->snd_una;
error = tcp_output(tp);
out:
if (otp)
tcp_trace(TA_USER, ostate, tp, otp, NULL, PRU_CONNECT, 0);
return (error);
}
/*
* Accept a connection. Essentially all the work is done at higher
* levels; just return the address of the peer, storing through addr.
*/
int
tcp_accept(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG)
ostate = tp->t_state;
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
in6_setpeeraddr(inp, nam);
else
#endif
in_setpeeraddr(inp, nam);
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, tp, NULL, PRU_ACCEPT, 0);
return (error);
}
/*
* Initiate disconnect from peer.
* If connection never passed embryonic stage, just drop;
* else if don't need to let data drain, then can just drop anyways,
* else have to begin TCP shutdown process: mark socket disconnecting,
* drain unread data, state switch to reflect user close, and
* send segment (e.g. FIN) to peer. Socket will be really disconnected
* when peer sends FIN and acks ours.
*
* SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
*/
int
tcp_disconnect(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp, *otp = NULL;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tp = tcp_dodisconnect(tp);
if (otp)
tcp_trace(TA_USER, ostate, tp, otp, NULL, PRU_DISCONNECT, 0);
return (0);
}
/*
* Mark the connection as being incapable of further output.
*/
int
tcp_shutdown(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp, *otp = NULL;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
if (so->so_state & SS_CANTSENDMORE)
goto out;
socantsendmore(so);
tp = tcp_usrclosed(tp);
if (tp)
error = tcp_output(tp);
out:
if (otp)
tcp_trace(TA_USER, ostate, tp, otp, NULL, PRU_SHUTDOWN, 0);
return (error);
}
/*
* After a receive, possibly send window update to peer.
*/
int
tcp_rcvd(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG)
ostate = tp->t_state;
/*
* soreceive() calls this function when a user receives
* ancillary data on a listening socket. We don't call
* tcp_output in such a case, since there is no header
* template for a listening socket and hence the kernel
* will panic.
*/
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) != 0)
(void) tcp_output(tp);
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, tp, NULL, PRU_RCVD, 0);
return (0);
}
/*
* Do a send by putting data in output queue and updating urgent
* marker if URG set. Possibly send more data.
*/
int
tcp_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
short ostate;
soassertlocked(so);
if (control && control->m_len) {
error = EINVAL;
goto out;
}
if ((error = tcp_sogetpcb(so, &inp, &tp)))
goto out;
if (so->so_options & SO_DEBUG)
ostate = tp->t_state;
sbappendstream(so, &so->so_snd, m);
m = NULL;
error = tcp_output(tp);
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, tp, NULL, PRU_SEND, 0);
out:
m_freem(control);
m_freem(m);
return (error);
}
/*
* Abort the TCP.
*/
int
tcp_abort(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp, *otp = NULL;
int error;
short ostate;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if (so->so_options & SO_DEBUG) {
otp = tp;
ostate = tp->t_state;
}
tp = tcp_drop(tp, ECONNABORTED);
if (otp)
tcp_trace(TA_USER, ostate, tp, otp, NULL, PRU_ABORT, 0);
return (0);
}
int
tcp_sense(struct socket *so, struct stat *ub)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
ub->st_blksize = so->so_snd.sb_hiwat;
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, tp->t_state, tp, tp, NULL, PRU_SENSE, 0);
return (0);
}
int
tcp_rcvoob(struct socket *so, struct mbuf *m, int flags)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
if ((so->so_oobmark == 0 &&
(so->so_state & SS_RCVATMARK) == 0) ||
so->so_options & SO_OOBINLINE ||
tp->t_oobflags & TCPOOB_HADDATA) {
error = EINVAL;
goto out;
}
if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
error = EWOULDBLOCK;
goto out;
}
m->m_len = 1;
*mtod(m, caddr_t) = tp->t_iobc;
if ((flags & MSG_PEEK) == 0)
tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
out:
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, tp->t_state, tp, tp, NULL, PRU_RCVOOB, 0);
return (error);
}
int
tcp_sendoob(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
short ostate;
soassertlocked(so);
if (control && control->m_len) {
error = EINVAL;
goto release;
}
if ((error = tcp_sogetpcb(so, &inp, &tp)))
goto release;
if (so->so_options & SO_DEBUG)
ostate = tp->t_state;
if (sbspace(so, &so->so_snd) < -512) {
error = ENOBUFS;
goto out;
}
/*
* According to RFC961 (Assigned Protocols),
* the urgent pointer points to the last octet
* of urgent data. We continue, however,
* to consider it to indicate the first octet
* of data past the urgent section.
* Otherwise, snd_up should be one lower.
*/
sbappendstream(so, &so->so_snd, m);
m = NULL;
tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
tp->t_force = 1;
error = tcp_output(tp);
tp->t_force = 0;
out:
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, tp, NULL, PRU_SENDOOB, 0);
release:
m_freem(control);
m_freem(m);
return (error);
}
int
tcp_sockaddr(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
in6_setsockaddr(inp, nam);
else
#endif
in_setsockaddr(inp, nam);
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, tp->t_state, tp, tp, NULL,
PRU_SOCKADDR, 0);
return (0);
}
int
tcp_peeraddr(struct socket *so, struct mbuf *nam)
{
struct inpcb *inp;
struct tcpcb *tp;
int error;
soassertlocked(so);
if ((error = tcp_sogetpcb(so, &inp, &tp)))
return (error);
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
in6_setpeeraddr(inp, nam);
else
#endif
in_setpeeraddr(inp, nam);
if (so->so_options & SO_DEBUG)
tcp_trace(TA_USER, tp->t_state, tp, tp, NULL,
PRU_PEERADDR, 0);
return (0);
}
/*
* Initiate (or continue) disconnect.
* If embryonic state, just send reset (once).
* If in ``let data drain'' option and linger null, just drop.
* Otherwise (hard), mark socket disconnecting and drop
* current input data; switch states based on user close, and
* send segment to peer (with FIN).
*/
struct tcpcb *
tcp_dodisconnect(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
if (TCPS_HAVEESTABLISHED(tp->t_state) == 0)
tp = tcp_close(tp);
else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
tp = tcp_drop(tp, 0);
else {
soisdisconnecting(so);
sbflush(so, &so->so_rcv);
tp = tcp_usrclosed(tp);
if (tp)
(void) tcp_output(tp);
}
return (tp);
}
/*
* User issued close, and wish to trail through shutdown states:
* if never received SYN, just forget it. If got a SYN from peer,
* but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
* If already got a FIN from peer, then almost done; go to LAST_ACK
* state. In all other cases, have already sent FIN to peer (e.g.
* after PRU_SHUTDOWN), and just have to play tedious game waiting
* for peer to send FIN or not respond to keep-alives, etc.
* We can let the user exit from the close as soon as the FIN is acked.
*/
struct tcpcb *
tcp_usrclosed(struct tcpcb *tp)
{
switch (tp->t_state) {
case TCPS_CLOSED:
case TCPS_LISTEN:
case TCPS_SYN_SENT:
tp->t_state = TCPS_CLOSED;
tp = tcp_close(tp);
break;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
tp->t_state = TCPS_FIN_WAIT_1;
break;
case TCPS_CLOSE_WAIT:
tp->t_state = TCPS_LAST_ACK;
break;
}
if (tp && tp->t_state >= TCPS_FIN_WAIT_2) {
soisdisconnected(tp->t_inpcb->inp_socket);
/*
* If we are in FIN_WAIT_2, we arrived here because the
* application did a shutdown of the send side. Like the
* case of a transition from FIN_WAIT_1 to FIN_WAIT_2 after
* a full close, we start a timer to make sure sockets are
* not left in FIN_WAIT_2 forever.
*/
if (tp->t_state == TCPS_FIN_WAIT_2)
TCP_TIMER_ARM(tp, TCPT_2MSL, tcp_maxidle);
}
return (tp);
}
/*
* Look up a socket for ident or tcpdrop, ...
*/
int
tcp_ident(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int dodrop)
{
int error = 0;
struct tcp_ident_mapping tir;
struct inpcb *inp;
struct tcpcb *tp = NULL;
struct sockaddr_in *fin, *lin;
#ifdef INET6
struct sockaddr_in6 *fin6, *lin6;
struct in6_addr f6, l6;
#endif
NET_ASSERT_LOCKED();
if (dodrop) {
if (oldp != NULL || *oldlenp != 0)
return (EINVAL);
if (newp == NULL)
return (EPERM);
if (newlen < sizeof(tir))
return (ENOMEM);
if ((error = copyin(newp, &tir, sizeof (tir))) != 0 )
return (error);
} else {
if (oldp == NULL)
return (EINVAL);
if (*oldlenp < sizeof(tir))
return (ENOMEM);
if (newp != NULL || newlen != 0)
return (EINVAL);
if ((error = copyin(oldp, &tir, sizeof (tir))) != 0 )
return (error);
}
switch (tir.faddr.ss_family) {
#ifdef INET6
case AF_INET6:
fin6 = (struct sockaddr_in6 *)&tir.faddr;
error = in6_embedscope(&f6, fin6, NULL);
if (error)
return EINVAL; /*?*/
lin6 = (struct sockaddr_in6 *)&tir.laddr;
error = in6_embedscope(&l6, lin6, NULL);
if (error)
return EINVAL; /*?*/
break;
#endif
case AF_INET:
fin = (struct sockaddr_in *)&tir.faddr;
lin = (struct sockaddr_in *)&tir.laddr;
break;
default:
return (EINVAL);
}
switch (tir.faddr.ss_family) {
#ifdef INET6
case AF_INET6:
inp = in6_pcblookup(&tcbtable, &f6,
fin6->sin6_port, &l6, lin6->sin6_port, tir.rdomain);
break;
#endif
case AF_INET:
inp = in_pcblookup(&tcbtable, fin->sin_addr,
fin->sin_port, lin->sin_addr, lin->sin_port, tir.rdomain);
break;
default:
unhandled_af(tir.faddr.ss_family);
}
if (dodrop) {
if (inp && (tp = intotcpcb(inp)) &&
((inp->inp_socket->so_options & SO_ACCEPTCONN) == 0))
tp = tcp_drop(tp, ECONNABORTED);
else
error = ESRCH;
in_pcbunref(inp);
return (error);
}
if (inp == NULL) {
tcpstat_inc(tcps_pcbhashmiss);
switch (tir.faddr.ss_family) {
#ifdef INET6
case AF_INET6:
inp = in6_pcblookup_listen(&tcbtable,
&l6, lin6->sin6_port, NULL, tir.rdomain);
break;
#endif
case AF_INET:
inp = in_pcblookup_listen(&tcbtable,
lin->sin_addr, lin->sin_port, NULL, tir.rdomain);
break;
}
}
if (inp != NULL && (inp->inp_socket->so_state & SS_CONNECTOUT)) {
tir.ruid = inp->inp_socket->so_ruid;
tir.euid = inp->inp_socket->so_euid;
} else {
tir.ruid = -1;
tir.euid = -1;
}
*oldlenp = sizeof (tir);
error = copyout((void *)&tir, oldp, sizeof (tir));
in_pcbunref(inp);
return (error);
}
int
tcp_sysctl_tcpstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[tcps_ncounters];
struct tcpstat tcpstat;
struct syn_cache_set *set;
int i = 0;
#define ASSIGN(field) do { tcpstat.field = counters[i++]; } while (0)
memset(&tcpstat, 0, sizeof tcpstat);
counters_read(tcpcounters, counters, nitems(counters));
ASSIGN(tcps_connattempt);
ASSIGN(tcps_accepts);
ASSIGN(tcps_connects);
ASSIGN(tcps_drops);
ASSIGN(tcps_conndrops);
ASSIGN(tcps_closed);
ASSIGN(tcps_segstimed);
ASSIGN(tcps_rttupdated);
ASSIGN(tcps_delack);
ASSIGN(tcps_timeoutdrop);
ASSIGN(tcps_rexmttimeo);
ASSIGN(tcps_persisttimeo);
ASSIGN(tcps_persistdrop);
ASSIGN(tcps_keeptimeo);
ASSIGN(tcps_keepprobe);
ASSIGN(tcps_keepdrops);
ASSIGN(tcps_sndtotal);
ASSIGN(tcps_sndpack);
ASSIGN(tcps_sndbyte);
ASSIGN(tcps_sndrexmitpack);
ASSIGN(tcps_sndrexmitbyte);
ASSIGN(tcps_sndrexmitfast);
ASSIGN(tcps_sndacks);
ASSIGN(tcps_sndprobe);
ASSIGN(tcps_sndurg);
ASSIGN(tcps_sndwinup);
ASSIGN(tcps_sndctrl);
ASSIGN(tcps_rcvtotal);
ASSIGN(tcps_rcvpack);
ASSIGN(tcps_rcvbyte);
ASSIGN(tcps_rcvbadsum);
ASSIGN(tcps_rcvbadoff);
ASSIGN(tcps_rcvmemdrop);
ASSIGN(tcps_rcvnosec);
ASSIGN(tcps_rcvshort);
ASSIGN(tcps_rcvduppack);
ASSIGN(tcps_rcvdupbyte);
ASSIGN(tcps_rcvpartduppack);
ASSIGN(tcps_rcvpartdupbyte);
ASSIGN(tcps_rcvoopack);
ASSIGN(tcps_rcvoobyte);
ASSIGN(tcps_rcvpackafterwin);
ASSIGN(tcps_rcvbyteafterwin);
ASSIGN(tcps_rcvafterclose);
ASSIGN(tcps_rcvwinprobe);
ASSIGN(tcps_rcvdupack);
ASSIGN(tcps_rcvacktoomuch);
ASSIGN(tcps_rcvacktooold);
ASSIGN(tcps_rcvackpack);
ASSIGN(tcps_rcvackbyte);
ASSIGN(tcps_rcvwinupd);
ASSIGN(tcps_pawsdrop);
ASSIGN(tcps_predack);
ASSIGN(tcps_preddat);
ASSIGN(tcps_pcbhashmiss);
ASSIGN(tcps_noport);
ASSIGN(tcps_badsyn);
ASSIGN(tcps_dropsyn);
ASSIGN(tcps_rcvbadsig);
ASSIGN(tcps_rcvgoodsig);
ASSIGN(tcps_inswcsum);
ASSIGN(tcps_outswcsum);
ASSIGN(tcps_ecn_accepts);
ASSIGN(tcps_ecn_rcvece);
ASSIGN(tcps_ecn_rcvcwr);
ASSIGN(tcps_ecn_rcvce);
ASSIGN(tcps_ecn_sndect);
ASSIGN(tcps_ecn_sndece);
ASSIGN(tcps_ecn_sndcwr);
ASSIGN(tcps_cwr_ecn);
ASSIGN(tcps_cwr_frecovery);
ASSIGN(tcps_cwr_timeout);
ASSIGN(tcps_sc_added);
ASSIGN(tcps_sc_completed);
ASSIGN(tcps_sc_timed_out);
ASSIGN(tcps_sc_overflowed);
ASSIGN(tcps_sc_reset);
ASSIGN(tcps_sc_unreach);
ASSIGN(tcps_sc_bucketoverflow);
ASSIGN(tcps_sc_aborted);
ASSIGN(tcps_sc_dupesyn);
ASSIGN(tcps_sc_dropped);
ASSIGN(tcps_sc_collisions);
ASSIGN(tcps_sc_retransmitted);
ASSIGN(tcps_sc_seedrandom);
ASSIGN(tcps_sc_hash_size);
ASSIGN(tcps_sc_entry_count);
ASSIGN(tcps_sc_entry_limit);
ASSIGN(tcps_sc_bucket_maxlen);
ASSIGN(tcps_sc_bucket_limit);
ASSIGN(tcps_sc_uses_left);
ASSIGN(tcps_conndrained);
ASSIGN(tcps_sack_recovery_episode);
ASSIGN(tcps_sack_rexmits);
ASSIGN(tcps_sack_rexmit_bytes);
ASSIGN(tcps_sack_rcv_opts);
ASSIGN(tcps_sack_snd_opts);
ASSIGN(tcps_sack_drop_opts);
#undef ASSIGN
set = &tcp_syn_cache[tcp_syn_cache_active];
tcpstat.tcps_sc_hash_size = set->scs_size;
tcpstat.tcps_sc_entry_count = set->scs_count;
tcpstat.tcps_sc_entry_limit = tcp_syn_cache_limit;
tcpstat.tcps_sc_bucket_maxlen = 0;
for (i = 0; i < set->scs_size; i++) {
if (tcpstat.tcps_sc_bucket_maxlen <
set->scs_buckethead[i].sch_length)
tcpstat.tcps_sc_bucket_maxlen =
set->scs_buckethead[i].sch_length;
}
tcpstat.tcps_sc_bucket_limit = tcp_syn_bucket_limit;
tcpstat.tcps_sc_uses_left = set->scs_use;
return (sysctl_rdstruct(oldp, oldlenp, newp,
&tcpstat, sizeof(tcpstat)));
}
/*
* Sysctl for tcp variables.
*/
int
tcp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error, nval;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case TCPCTL_BADDYNAMIC:
NET_LOCK();
error = sysctl_struct(oldp, oldlenp, newp, newlen,
baddynamicports.tcp, sizeof(baddynamicports.tcp));
NET_UNLOCK();
return (error);
case TCPCTL_ROOTONLY:
if (newp && securelevel > 0)
return (EPERM);
NET_LOCK();
error = sysctl_struct(oldp, oldlenp, newp, newlen,
rootonlyports.tcp, sizeof(rootonlyports.tcp));
NET_UNLOCK();
return (error);
case TCPCTL_IDENT:
NET_LOCK();
error = tcp_ident(oldp, oldlenp, newp, newlen, 0);
NET_UNLOCK();
return (error);
case TCPCTL_DROP:
NET_LOCK();
error = tcp_ident(oldp, oldlenp, newp, newlen, 1);
NET_UNLOCK();
return (error);
case TCPCTL_REASS_LIMIT:
NET_LOCK();
nval = tcp_reass_limit;
error = sysctl_int(oldp, oldlenp, newp, newlen, &nval);
if (!error && nval != tcp_reass_limit) {
error = pool_sethardlimit(&tcpqe_pool, nval, NULL, 0);
if (!error)
tcp_reass_limit = nval;
}
NET_UNLOCK();
return (error);
case TCPCTL_SACKHOLE_LIMIT:
NET_LOCK();
nval = tcp_sackhole_limit;
error = sysctl_int(oldp, oldlenp, newp, newlen, &nval);
if (!error && nval != tcp_sackhole_limit) {
error = pool_sethardlimit(&sackhl_pool, nval, NULL, 0);
if (!error)
tcp_sackhole_limit = nval;
}
NET_UNLOCK();
return (error);
case TCPCTL_STATS:
return (tcp_sysctl_tcpstat(oldp, oldlenp, newp));
case TCPCTL_SYN_USE_LIMIT:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&tcp_syn_use_limit, 0, INT_MAX);
if (!error && newp != NULL) {
/*
* Global tcp_syn_use_limit is used when reseeding a
* new cache. Also update the value in active cache.
*/
if (tcp_syn_cache[0].scs_use > tcp_syn_use_limit)
tcp_syn_cache[0].scs_use = tcp_syn_use_limit;
if (tcp_syn_cache[1].scs_use > tcp_syn_use_limit)
tcp_syn_cache[1].scs_use = tcp_syn_use_limit;
}
NET_UNLOCK();
return (error);
case TCPCTL_SYN_HASH_SIZE:
NET_LOCK();
nval = tcp_syn_hash_size;
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&nval, 1, 100000);
if (!error && nval != tcp_syn_hash_size) {
/*
* If global hash size has been changed,
* switch sets as soon as possible. Then
* the actual hash array will be reallocated.
*/
if (tcp_syn_cache[0].scs_size != nval)
tcp_syn_cache[0].scs_use = 0;
if (tcp_syn_cache[1].scs_size != nval)
tcp_syn_cache[1].scs_use = 0;
tcp_syn_hash_size = nval;
}
NET_UNLOCK();
return (error);
default:
NET_LOCK();
error = sysctl_bounded_arr(tcpctl_vars, nitems(tcpctl_vars), name,
namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
/* NOTREACHED */
}
/*
* Scale the send buffer so that inflight data is not accounted against
* the limit. The buffer will scale with the congestion window, if the
* the receiver stops acking data the window will shrink and therefore
* the buffer size will shrink as well.
* In low memory situation try to shrink the buffer to the initial size
* disabling the send buffer scaling as long as the situation persists.
*/
void
tcp_update_sndspace(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
u_long nmax = so->so_snd.sb_hiwat;
if (sbchecklowmem()) {
/* low on memory try to get rid of some */
if (tcp_sendspace < nmax)
nmax = tcp_sendspace;
} else if (so->so_snd.sb_wat != tcp_sendspace)
/* user requested buffer size, auto-scaling disabled */
nmax = so->so_snd.sb_wat;
else
/* automatic buffer scaling */
nmax = MIN(sb_max, so->so_snd.sb_wat + tp->snd_max -
tp->snd_una);
/* a writable socket must be preserved because of poll(2) semantics */
if (sbspace(so, &so->so_snd) >= so->so_snd.sb_lowat) {
if (nmax < so->so_snd.sb_cc + so->so_snd.sb_lowat)
nmax = so->so_snd.sb_cc + so->so_snd.sb_lowat;
/* keep in sync with sbreserve() calculation */
if (nmax * 8 < so->so_snd.sb_mbcnt + so->so_snd.sb_lowat)
nmax = (so->so_snd.sb_mbcnt+so->so_snd.sb_lowat+7) / 8;
}
/* round to MSS boundary */
nmax = roundup(nmax, tp->t_maxseg);
if (nmax != so->so_snd.sb_hiwat)
sbreserve(so, &so->so_snd, nmax);
}
/*
* Scale the recv buffer by looking at how much data was transferred in
* on approximated RTT. If more than a big part of the recv buffer was
* transferred during that time we increase the buffer by a constant.
* In low memory situation try to shrink the buffer to the initial size.
*/
void
tcp_update_rcvspace(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
u_long nmax = so->so_rcv.sb_hiwat;
if (sbchecklowmem()) {
/* low on memory try to get rid of some */
if (tcp_recvspace < nmax)
nmax = tcp_recvspace;
} else if (so->so_rcv.sb_wat != tcp_recvspace)
/* user requested buffer size, auto-scaling disabled */
nmax = so->so_rcv.sb_wat;
else {
/* automatic buffer scaling */
if (tp->rfbuf_cnt > so->so_rcv.sb_hiwat / 8 * 7)
nmax = MIN(sb_max, so->so_rcv.sb_hiwat +
tcp_autorcvbuf_inc);
}
/* a readable socket must be preserved because of poll(2) semantics */
if (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat &&
nmax < so->so_snd.sb_lowat)
nmax = so->so_snd.sb_lowat;
if (nmax == so->so_rcv.sb_hiwat)
return;
/* round to MSS boundary */
nmax = roundup(nmax, tp->t_maxseg);
sbreserve(so, &so->so_rcv, nmax);
}
199
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/* $OpenBSD: tty_conf.c,v 1.23 2015/12/22 20:31:51 sf Exp $ */
/* $NetBSD: tty_conf.c,v 1.18 1996/05/19 17:17:55 jonathan Exp $ */
/*-
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tty_conf.c 8.4 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include "ppp.h"
#include "nmea.h"
#include "msts.h"
#include "endrun.h"
#define ttynodisc ((int (*)(dev_t, struct tty *, struct proc *))enodev)
#define ttyerrclose ((int (*)(struct tty *, int flags, struct proc *))enodev)
#define ttyerrio ((int (*)(struct tty *, struct uio *, int))enodev)
#define ttyerrinput ((int (*)(int c, struct tty *))enodev)
#define ttyerrstart ((int (*)(struct tty *))enodev)
struct linesw linesw[] =
{
{ ttyopen, ttylclose, ttread, ttwrite, nullioctl,
ttyinput, ttstart, ttymodem }, /* 0- termios */
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem }, /* 1- defunct */
/* 2- old NTTYDISC (defunct) */
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
/* 3- TABLDISC (defunct) */
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
/* 4- SLIPDISC (defunct) */
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#if NPPP > 0
{ pppopen, pppclose, pppread, pppwrite, ppptioctl,
pppinput, pppstart, ttymodem }, /* 5- PPPDISC */
#else
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#endif
/* 6- STRIPDISC (defunct) */
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#if NNMEA > 0
{ nmeaopen, nmeaclose, ttread, ttwrite, nullioctl,
nmeainput, ttstart, ttymodem }, /* 7- NMEADISC */
#else
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#endif
#if NMSTS > 0
{ mstsopen, mstsclose, ttread, ttwrite, nullioctl,
mstsinput, ttstart, ttymodem }, /* 8- MSTSDISC */
#else
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#endif
#if NENDRUN > 0
{ endrunopen, endrunclose, ttread, ttwrite, nullioctl,
endruninput, ttstart, ttymodem }, /* 9- ENDRUNDISC */
#else
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#endif
};
int nlinesw = sizeof (linesw) / sizeof (linesw[0]);
/*
* Do nothing specific version of line
* discipline specific ioctl command.
*/
int
nullioctl(struct tty *tp, u_long cmd, char *data, int flags, struct proc *p)
{
return (-1);
}
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/* $OpenBSD: ujoy.c,v 1.4 2022/07/02 08:50:42 visa Exp $ */
/*
* Copyright (c) 2021 Thomas Frohwein <thfr@openbsd.org>
* Copyright (c) 2021 Bryan Steele <brynet@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/uhidev.h>
#include <dev/usb/uhid.h>
int ujoy_match(struct device *, void *, void *);
struct cfdriver ujoy_cd = {
NULL, "ujoy", DV_DULL
};
const struct cfattach ujoy_ca = {
sizeof(struct uhid_softc),
ujoy_match,
uhid_attach,
uhid_detach,
};
/*
* XXX workaround:
*
* This is a copy of sys/dev/hid/hid.c:hid_is_collection(), synced up to the
* NetBSD version. Our current hid_is_collection() is not playing nice with
* all HID devices like the PS4 controller. But applying this version
* globally breaks other HID devices like ims(4) and imt(4). Until our global
* hid_is_collection() can't be fixed to play nice with all HID devices, we
* go for this dedicated ujoy(4) version.
*/
int
ujoy_hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage)
{
struct hid_data *hd;
struct hid_item hi;
uint32_t coll_usage = ~0;
hd = hid_start_parse(desc, size, hid_input);
if (hd == NULL)
return (0);
while (hid_get_item(hd, &hi)) {
if (hi.kind == hid_collection &&
hi.collection == HCOLL_APPLICATION)
coll_usage = hi.usage;
if (hi.kind == hid_endcollection)
coll_usage = ~0;
if (hi.kind == hid_input &&
coll_usage == usage &&
hi.report_ID == id) {
hid_end_parse(hd);
return (1);
}
}
hid_end_parse(hd);
return (0);
}
int
ujoy_match(struct device *parent, void *match, void *aux)
{
struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
int size;
void *desc;
int ret = UMATCH_NONE;
if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
return (ret);
/* Find the general usage page and gamecontroller collections */
uhidev_get_report_desc(uha->parent, &desc, &size);
if (ujoy_hid_is_collection(desc, size, uha->reportid,
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_JOYSTICK)))
ret = UMATCH_IFACECLASS;
if (ujoy_hid_is_collection(desc, size, uha->reportid,
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_GAME_PAD)))
ret = UMATCH_IFACECLASS;
return (ret);
}
int
ujoyopen(dev_t dev, int flag, int mode, struct proc *p)
{
/* Restrict ujoy devices to read operations */
if ((flag & FWRITE))
return (EPERM);
return (uhid_do_open(dev, flag, mode, p));
}
int
ujoyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
switch (cmd) {
case FIONBIO:
case FIOASYNC:
case USB_GET_DEVICEINFO:
case USB_GET_REPORT:
case USB_GET_REPORT_DESC:
case USB_GET_REPORT_ID:
break;
default:
return (EPERM);
}
return (uhidioctl(dev, cmd, addr, flag, p));
}
12
10
2
9
4
9
10
8
4
4
7
7
7
6
7
9
5
1
8
1
5
5
11
11
11
4
10
1
4
8
2
1
9
3
5
1
1
1
1
1
8
5
5
8
8
7
9
3
7
8
3
7
1
8
7
3
1
1
3
4
7
3
5
1
1
1
2
5
6
6
36
36
36
36
36
36
36
21
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
/* $OpenBSD: radix.c,v 1.61 2022/01/02 22:36:04 jsg Exp $ */
/* $NetBSD: radix.c,v 1.20 2003/08/07 16:32:56 agc Exp $ */
/*
* Copyright (c) 1988, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)radix.c 8.6 (Berkeley) 10/17/95
*/
/*
* Routines to build and maintain radix trees for routing lookups.
*/
#ifndef _KERNEL
#include "kern_compat.h"
#else
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
#include <sys/pool.h>
#endif
#include <net/radix.h>
#define SALEN(sa) (*(u_char *)(sa))
/*
* Read-only variables, allocated & filled during rn_init().
*/
static char *rn_zeros; /* array of 0s */
static char *rn_ones; /* array of 1s */
static unsigned int max_keylen; /* size of the above arrays */
#define KEYLEN_LIMIT 64 /* maximum allowed keylen */
struct radix_node_head *mask_rnhead; /* head of shared mask tree */
struct pool rtmask_pool; /* pool for radix_mask structures */
static inline int rn_satisfies_leaf(char *, struct radix_node *, int);
static inline int rn_lexobetter(void *, void *);
static inline struct radix_mask *rn_new_radix_mask(struct radix_node *,
struct radix_mask *);
int rn_refines(void *, void *);
int rn_inithead0(struct radix_node_head *, int);
struct radix_node *rn_addmask(void *, int, int);
struct radix_node *rn_insert(void *, struct radix_node_head *, int *,
struct radix_node [2]);
struct radix_node *rn_newpair(void *, int, struct radix_node[2]);
void rn_link_dupedkey(struct radix_node *, struct radix_node *, int);
static inline struct radix_node *rn_search(void *, struct radix_node *);
struct radix_node *rn_search_m(void *, struct radix_node *, void *);
int rn_add_dupedkey(struct radix_node *, struct radix_node_head *,
struct radix_node [2], u_int8_t);
void rn_fixup_nodes(struct radix_node *);
static inline struct radix_node *rn_lift_node(struct radix_node *);
void rn_add_radix_mask(struct radix_node *, int);
int rn_del_radix_mask(struct radix_node *);
static inline void rn_swap_nodes(struct radix_node *, struct radix_node *);
/*
* The data structure for the keys is a radix tree with one way
* branching removed. The index rn_b at an internal node n represents a bit
* position to be tested. The tree is arranged so that all descendants
* of a node n have keys whose bits all agree up to position rn_b - 1.
* (We say the index of n is rn_b.)
*
* There is at least one descendant which has a one bit at position rn_b,
* and at least one with a zero there.
*
* A route is determined by a pair of key and mask. We require that the
* bit-wise logical and of the key and mask to be the key.
* We define the index of a route to associated with the mask to be
* the first bit number in the mask where 0 occurs (with bit number 0
* representing the highest order bit).
*
* We say a mask is normal if every bit is 0, past the index of the mask.
* If a node n has a descendant (k, m) with index(m) == index(n) == rn_b,
* and m is a normal mask, then the route applies to every descendant of n.
* If the index(m) < rn_b, this implies the trailing last few bits of k
* before bit b are all 0, (and hence consequently true of every descendant
* of n), so the route applies to all descendants of the node as well.
*
* Similar logic shows that a non-normal mask m such that
* index(m) <= index(n) could potentially apply to many children of n.
* Thus, for each non-host route, we attach its mask to a list at an internal
* node as high in the tree as we can go.
*
* The present version of the code makes use of normal routes in short-
* circuiting an explicit mask and compare operation when testing whether
* a key satisfies a normal route, and also in remembering the unique leaf
* that governs a subtree.
*/
static inline struct radix_node *
rn_search(void *v_arg, struct radix_node *head)
{
struct radix_node *x = head;
caddr_t v = v_arg;
while (x->rn_b >= 0) {
if (x->rn_bmask & v[x->rn_off])
x = x->rn_r;
else
x = x->rn_l;
}
return (x);
}
struct radix_node *
rn_search_m(void *v_arg, struct radix_node *head, void *m_arg)
{
struct radix_node *x = head;
caddr_t v = v_arg;
caddr_t m = m_arg;
while (x->rn_b >= 0) {
if ((x->rn_bmask & m[x->rn_off]) &&
(x->rn_bmask & v[x->rn_off]))
x = x->rn_r;
else
x = x->rn_l;
}
return x;
}
int
rn_refines(void *m_arg, void *n_arg)
{
caddr_t m = m_arg;
caddr_t n = n_arg;
caddr_t lim, lim2;
int longer;
int masks_are_equal = 1;
lim2 = lim = n + *(u_char *)n;
longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
if (longer > 0)
lim -= longer;
while (n < lim) {
if (*n & ~(*m))
return 0;
if (*n++ != *m++)
masks_are_equal = 0;
}
while (n < lim2)
if (*n++)
return 0;
if (masks_are_equal && (longer < 0))
for (lim2 = m - longer; m < lim2; )
if (*m++)
return 1;
return (!masks_are_equal);
}
/* return a perfect match if m_arg is set, else do a regular rn_match */
struct radix_node *
rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head)
{
struct radix_node *x, *tm;
caddr_t netmask = 0;
if (m_arg) {
tm = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off);
if (tm == NULL)
return (NULL);
netmask = tm->rn_key;
}
x = rn_match(v_arg, head);
if (x && netmask) {
while (x && x->rn_mask != netmask)
x = x->rn_dupedkey;
}
/* Never return internal nodes to the upper layer. */
if (x && (x->rn_flags & RNF_ROOT))
return (NULL);
return x;
}
static inline int
rn_satisfies_leaf(char *trial, struct radix_node *leaf, int skip)
{
char *cp = trial;
char *cp2 = leaf->rn_key;
char *cp3 = leaf->rn_mask;
char *cplim;
int length;
length = min(SALEN(cp), SALEN(cp2));
if (cp3 == NULL)
cp3 = rn_ones;
else
length = min(length, SALEN(cp3));
cplim = cp + length;
cp += skip;
cp2 += skip;
cp3 += skip;
while (cp < cplim) {
if ((*cp ^ *cp2) & *cp3)
return 0;
cp++, cp2++, cp3++;
}
return 1;
}
struct radix_node *
rn_match(void *v_arg, struct radix_node_head *head)
{
caddr_t v = v_arg;
caddr_t cp, cp2, cplim;
struct radix_node *top = head->rnh_treetop;
struct radix_node *saved_t, *t;
int off = top->rn_off;
int vlen, matched_off;
int test, b, rn_b;
t = rn_search(v, top);
/*
* See if we match exactly as a host destination
* or at least learn how many bits match, for normal mask finesse.
*
* It doesn't hurt us to limit how many bytes to check
* to the length of the mask, since if it matches we had a genuine
* match and the leaf we have is the most specific one anyway;
* if it didn't match with a shorter length it would fail
* with a long one. This wins big for class B&C netmasks which
* are probably the most common case...
*/
if (t->rn_mask)
vlen = SALEN(t->rn_mask);
else
vlen = SALEN(v);
cp = v + off;
cp2 = t->rn_key + off;
cplim = v + vlen;
for (; cp < cplim; cp++, cp2++)
if (*cp != *cp2)
goto on1;
/*
* This extra grot is in case we are explicitly asked
* to look up the default. Ugh!
*/
if (t->rn_flags & RNF_ROOT)
t = t->rn_dupedkey;
KASSERT(t == NULL || (t->rn_flags & RNF_ROOT) == 0);
return t;
on1:
test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */
for (b = 7; (test >>= 1) > 0;)
b--;
matched_off = cp - v;
b += matched_off << 3;
rn_b = -1 - b;
/*
* If there is a host route in a duped-key chain, it will be first.
*/
saved_t = t;
if (t->rn_mask == NULL)
t = t->rn_dupedkey;
for (; t; t = t->rn_dupedkey)
/*
* Even if we don't match exactly as a host,
* we may match if the leaf we wound up at is
* a route to a net.
*/
if (t->rn_flags & RNF_NORMAL) {
if (rn_b <= t->rn_b) {
KASSERT((t->rn_flags & RNF_ROOT) == 0);
return t;
}
} else if (rn_satisfies_leaf(v, t, matched_off)) {
KASSERT((t->rn_flags & RNF_ROOT) == 0);
return t;
}
t = saved_t;
/* start searching up the tree */
do {
struct radix_mask *m;
t = t->rn_p;
m = t->rn_mklist;
while (m) {
/*
* If non-contiguous masks ever become important
* we can restore the masking and open coding of
* the search and satisfaction test and put the
* calculation of "off" back before the "do".
*/
if (m->rm_flags & RNF_NORMAL) {
if (rn_b <= m->rm_b) {
KASSERT((m->rm_leaf->rn_flags &
RNF_ROOT) == 0);
return (m->rm_leaf);
}
} else {
struct radix_node *x;
off = min(t->rn_off, matched_off);
x = rn_search_m(v, t, m->rm_mask);
while (x && x->rn_mask != m->rm_mask)
x = x->rn_dupedkey;
if (x && rn_satisfies_leaf(v, x, off)) {
KASSERT((x->rn_flags & RNF_ROOT) == 0);
return x;
}
}
m = m->rm_mklist;
}
} while (t != top);
return NULL;
}
struct radix_node *
rn_newpair(void *v, int b, struct radix_node nodes[2])
{
struct radix_node *tt = nodes, *t = nodes + 1;
t->rn_b = b;
t->rn_bmask = 0x80 >> (b & 7);
t->rn_l = tt;
t->rn_off = b >> 3;
tt->rn_b = -1;
tt->rn_key = v;
tt->rn_p = t;
tt->rn_flags = t->rn_flags = RNF_ACTIVE;
return t;
}
struct radix_node *
rn_insert(void *v_arg, struct radix_node_head *head,
int *dupentry, struct radix_node nodes[2])
{
caddr_t v = v_arg;
struct radix_node *top = head->rnh_treetop;
struct radix_node *t, *tt;
int off = top->rn_off;
int b;
t = rn_search(v_arg, top);
/*
* Find first bit at which v and t->rn_key differ
*/
{
caddr_t cp, cp2, cplim;
int vlen, cmp_res;
vlen = SALEN(v);
cp = v + off;
cp2 = t->rn_key + off;
cplim = v + vlen;
while (cp < cplim)
if (*cp2++ != *cp++)
goto on1;
*dupentry = 1;
return t;
on1:
*dupentry = 0;
cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
for (b = (cp - v) << 3; cmp_res; b--)
cmp_res >>= 1;
}
{
struct radix_node *p, *x = top;
caddr_t cp = v;
do {
p = x;
if (cp[x->rn_off] & x->rn_bmask)
x = x->rn_r;
else
x = x->rn_l;
} while (b > (unsigned int) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */
t = rn_newpair(v_arg, b, nodes);
tt = t->rn_l;
if ((cp[p->rn_off] & p->rn_bmask) == 0)
p->rn_l = t;
else
p->rn_r = t;
x->rn_p = t;
t->rn_p = p; /* frees x, p as temp vars below */
if ((cp[t->rn_off] & t->rn_bmask) == 0) {
t->rn_r = x;
} else {
t->rn_r = tt;
t->rn_l = x;
}
}
return (tt);
}
struct radix_node *
rn_addmask(void *n_arg, int search, int skip)
{
caddr_t netmask = n_arg;
struct radix_node *tm, *saved_tm;
caddr_t cp, cplim;
int b = 0, mlen, j;
int maskduplicated, m0, isnormal;
char addmask_key[KEYLEN_LIMIT];
if ((mlen = SALEN(netmask)) > max_keylen)
mlen = max_keylen;
if (skip == 0)
skip = 1;
if (mlen <= skip)
return (mask_rnhead->rnh_nodes); /* rn_zero root node */
if (skip > 1)
memcpy(addmask_key + 1, rn_ones + 1, skip - 1);
if ((m0 = mlen) > skip)
memcpy(addmask_key + skip, netmask + skip, mlen - skip);
/*
* Trim trailing zeroes.
*/
for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;)
cp--;
mlen = cp - addmask_key;
if (mlen <= skip)
return (mask_rnhead->rnh_nodes);
memset(addmask_key + m0, 0, max_keylen - m0);
SALEN(addmask_key) = mlen;
tm = rn_search(addmask_key, mask_rnhead->rnh_treetop);
if (memcmp(addmask_key, tm->rn_key, mlen) != 0)
tm = NULL;
if (tm || search)
return (tm);
tm = malloc(max_keylen + 2 * sizeof(*tm), M_RTABLE, M_NOWAIT | M_ZERO);
if (tm == NULL)
return (0);
saved_tm = tm;
netmask = cp = (caddr_t)(tm + 2);
memcpy(cp, addmask_key, mlen);
tm = rn_insert(cp, mask_rnhead, &maskduplicated, tm);
if (maskduplicated) {
log(LOG_ERR, "%s: mask impossibly already in tree\n", __func__);
free(saved_tm, M_RTABLE, max_keylen + 2 * sizeof(*saved_tm));
return (tm);
}
/*
* Calculate index of mask, and check for normalcy.
*/
cplim = netmask + mlen;
isnormal = 1;
for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;)
cp++;
if (cp != cplim) {
static const char normal_chars[] = {
0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, -1
};
for (j = 0x80; (j & *cp) != 0; j >>= 1)
b++;
if (*cp != normal_chars[b] || cp != (cplim - 1))
isnormal = 0;
}
b += (cp - netmask) << 3;
tm->rn_b = -1 - b;
if (isnormal)
tm->rn_flags |= RNF_NORMAL;
return (tm);
}
/* rn_lexobetter: return a arbitrary ordering for non-contiguous masks */
static inline int
rn_lexobetter(void *m_arg, void *n_arg)
{
u_char *mp = m_arg, *np = n_arg;
/*
* Longer masks might not really be lexicographically better,
* but longer masks always have precedence since they must be checked
* first. The netmasks were normalized before calling this function and
* don't have unneeded trailing zeros.
*/
if (SALEN(mp) > SALEN(np))
return 1;
if (SALEN(mp) < SALEN(np))
return 0;
/*
* Must return the first difference between the masks
* to ensure deterministic sorting.
*/
return (memcmp(mp, np, *mp) > 0);
}
static inline struct radix_mask *
rn_new_radix_mask(struct radix_node *tt, struct radix_mask *next)
{
struct radix_mask *m;
m = pool_get(&rtmask_pool, PR_NOWAIT | PR_ZERO);
if (m == NULL) {
log(LOG_ERR, "Mask for route not entered\n");
return (0);
}
m->rm_b = tt->rn_b;
m->rm_flags = tt->rn_flags;
if (tt->rn_flags & RNF_NORMAL)
m->rm_leaf = tt;
else
m->rm_mask = tt->rn_mask;
m->rm_mklist = next;
tt->rn_mklist = m;
return m;
}
/*
* Find the point where the rn_mklist needs to be changed.
*/
static inline struct radix_node *
rn_lift_node(struct radix_node *t)
{
struct radix_node *x = t;
int b = -1 - t->rn_b;
/* rewind possible dupedkey list to head */
while (t->rn_b < 0)
t = t->rn_p;
/* can't lift node above head of dupedkey list, give up */
if (b > t->rn_b)
return (NULL);
do {
x = t;
t = t->rn_p;
} while (b <= t->rn_b && x != t);
return (x);
}
void
rn_add_radix_mask(struct radix_node *tt, int keyduplicated)
{
caddr_t netmask, mmask;
struct radix_node *x;
struct radix_mask *m, **mp;
int b_leaf = tt->rn_b;
/* Add new route to highest possible ancestor's list */
if (tt->rn_mask == NULL)
return; /* can't lift at all */
x = rn_lift_node(tt);
if (x == NULL)
return; /* didn't lift either */
/*
* Search through routes associated with node to
* insert new route according to index.
* Need same criteria as when sorting dupedkeys to avoid
* double loop on deletion.
*/
netmask = tt->rn_mask;
for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) {
if (m->rm_b < b_leaf)
continue;
if (m->rm_b > b_leaf)
break;
if (m->rm_flags & RNF_NORMAL) {
if (keyduplicated) {
if (m->rm_leaf->rn_p == tt)
/* new route is better */
m->rm_leaf = tt;
#ifdef DIAGNOSTIC
else {
struct radix_node *t;
for (t = m->rm_leaf;
t && t->rn_mklist == m;
t = t->rn_dupedkey)
if (t == tt)
break;
if (t == NULL) {
log(LOG_ERR, "Non-unique "
"normal route on dupedkey, "
"mask not entered\n");
return;
}
}
#endif
m->rm_refs++;
tt->rn_mklist = m;
return;
} else if (tt->rn_flags & RNF_NORMAL) {
log(LOG_ERR, "Non-unique normal route,"
" mask not entered\n");
return;
}
mmask = m->rm_leaf->rn_mask;
} else
mmask = m->rm_mask;
if (mmask == netmask) {
m->rm_refs++;
tt->rn_mklist = m;
return;
}
if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask))
break;
}
*mp = rn_new_radix_mask(tt, *mp);
}
int
rn_add_dupedkey(struct radix_node *saved_tt, struct radix_node_head *head,
struct radix_node *tt, u_int8_t prio)
{
caddr_t netmask = tt->rn_mask;
struct radix_node *x = saved_tt, *xp;
int before = -1;
int b_leaf = 0;
if (netmask)
b_leaf = tt->rn_b;
for (xp = x; x; xp = x, x = x->rn_dupedkey) {
if (x->rn_mask == netmask)
return (-1);
if (netmask == NULL ||
(x->rn_mask &&
((b_leaf < x->rn_b) || /* index(netmask) > node */
rn_refines(netmask, x->rn_mask) ||
rn_lexobetter(netmask, x->rn_mask))))
break;
}
/*
* If the mask is not duplicated, we wouldn't
* find it among possible duplicate key entries
* anyway, so the above test doesn't hurt.
*
* We sort the masks for a duplicated key the same way as
* in a masklist -- most specific to least specific.
* This may require the unfortunate nuisance of relocating
* the head of the list.
*
* We also reverse, or doubly link the list through the
* parent pointer.
*/
if ((x == saved_tt && before) || before == 1)
before = 1;
else
before = 0;
rn_link_dupedkey(tt, xp, before);
return (0);
}
/*
* Insert tt after x or in place of x if before is true.
*/
void
rn_link_dupedkey(struct radix_node *tt, struct radix_node *x, int before)
{
if (before) {
if (x->rn_p->rn_b > 0) {
/* link in at head of list */
tt->rn_dupedkey = x;
tt->rn_flags = x->rn_flags;
tt->rn_p = x->rn_p;
x->rn_p = tt;
if (tt->rn_p->rn_l == x)
tt->rn_p->rn_l = tt;
else
tt->rn_p->rn_r = tt;
} else {
tt->rn_dupedkey = x;
x->rn_p->rn_dupedkey = tt;
tt->rn_p = x->rn_p;
x->rn_p = tt;
}
} else {
tt->rn_dupedkey = x->rn_dupedkey;
x->rn_dupedkey = tt;
tt->rn_p = x;
if (tt->rn_dupedkey)
tt->rn_dupedkey->rn_p = tt;
}
}
/*
* This function ensures that routes are properly promoted upwards.
* It adjusts the rn_mklist of the parent node to make sure overlapping
* routes can be found.
*
* There are two cases:
* - leaf nodes with possible rn_dupedkey list
* - internal nodes with maybe their own mklist
* If the mask of the route is bigger than the current branch bit then
* a rn_mklist entry needs to be made.
*/
void
rn_fixup_nodes(struct radix_node *tt)
{
struct radix_node *tp, *x;
struct radix_mask *m, **mp;
int b_leaf;
tp = tt->rn_p;
if (tp->rn_r == tt)
x = tp->rn_l;
else
x = tp->rn_r;
b_leaf = -1 - tp->rn_b;
if (x->rn_b < 0) { /* x is a leaf node */
struct radix_node *xx = NULL;
for (mp = &tp->rn_mklist; x; xx = x, x = x->rn_dupedkey) {
if (xx && xx->rn_mklist && xx->rn_mask == x->rn_mask &&
x->rn_mklist == 0) {
/* multipath route */
x->rn_mklist = xx->rn_mklist;
x->rn_mklist->rm_refs++;
}
if (x->rn_mask && (x->rn_b >= b_leaf) &&
x->rn_mklist == 0) {
*mp = m = rn_new_radix_mask(x, 0);
if (m)
mp = &m->rm_mklist;
}
}
} else if (x->rn_mklist) { /* x is an internal node */
/*
* Skip over masks whose index is > that of new node
*/
for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
if (m->rm_b >= b_leaf)
break;
tp->rn_mklist = m;
*mp = 0;
}
}
struct radix_node *
rn_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
struct radix_node treenodes[2], u_int8_t prio)
{
caddr_t v = v_arg;
struct radix_node *top = head->rnh_treetop;
struct radix_node *tt, *saved_tt, *tm = NULL;
int keyduplicated;
/*
* In dealing with non-contiguous masks, there may be
* many different routes which have the same mask.
* We will find it useful to have a unique pointer to
* the mask to speed avoiding duplicate references at
* nodes and possibly save time in calculating indices.
*/
if (n_arg) {
if ((tm = rn_addmask(n_arg, 0, top->rn_off)) == 0)
return (0);
}
tt = rn_insert(v, head, &keyduplicated, treenodes);
if (keyduplicated) {
saved_tt = tt;
tt = treenodes;
tt->rn_key = v_arg;
tt->rn_b = -1;
tt->rn_flags = RNF_ACTIVE;
}
/* Put mask into the node. */
if (tm) {
tt->rn_mask = tm->rn_key;
tt->rn_b = tm->rn_b;
tt->rn_flags |= tm->rn_flags & RNF_NORMAL;
}
/* Either insert into dupedkey list or as a leaf node. */
if (keyduplicated) {
if (rn_add_dupedkey(saved_tt, head, tt, prio))
return (NULL);
} else {
rn_fixup_nodes(tt);
}
/* finally insert a radix_mask element if needed */
rn_add_radix_mask(tt, keyduplicated);
return (tt);
}
/*
* Cleanup mask list, tt points to route that needs to be cleaned
*/
int
rn_del_radix_mask(struct radix_node *tt)
{
struct radix_node *x;
struct radix_mask *m, *saved_m, **mp;
/*
* Cleanup mask list from possible references to this route.
*/
saved_m = m = tt->rn_mklist;
if (tt->rn_mask == NULL || m == NULL)
return (0);
if (tt->rn_flags & RNF_NORMAL) {
if (m->rm_leaf != tt && m->rm_refs == 0) {
log(LOG_ERR, "rn_delete: inconsistent normal "
"annotation\n");
return (-1);
}
if (m->rm_leaf != tt) {
if (--m->rm_refs >= 0)
return (0);
else
log(LOG_ERR, "rn_delete: "
"inconsistent mklist refcount\n");
}
/*
* If we end up here tt should be m->rm_leaf and therefore
* tt should be the head of a multipath chain.
* If this is not the case the table is no longer consistent.
*/
if (m->rm_refs > 0) {
if (tt->rn_dupedkey == NULL ||
tt->rn_dupedkey->rn_mklist != m) {
log(LOG_ERR, "rn_delete: inconsistent "
"dupedkey list\n");
return (-1);
}
m->rm_leaf = tt->rn_dupedkey;
--m->rm_refs;
return (0);
}
/* else tt is last and only route */
} else {
if (m->rm_mask != tt->rn_mask) {
log(LOG_ERR, "rn_delete: inconsistent annotation\n");
return (0);
}
if (--m->rm_refs >= 0)
return (0);
}
/*
* No other references hold to the radix_mask remove it from
* the tree.
*/
x = rn_lift_node(tt);
if (x == NULL)
return (0); /* Wasn't lifted at all */
/* Finally eliminate the radix_mask from the tree */
for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
if (m == saved_m) {
*mp = m->rm_mklist;
pool_put(&rtmask_pool, m);
break;
}
if (m == NULL) {
log(LOG_ERR, "rn_delete: couldn't find our annotation\n");
if (tt->rn_flags & RNF_NORMAL)
return (-1); /* Dangling ref to us */
}
return (0);
}
/* swap two internal nodes and fixup the parent and child pointers */
static inline void
rn_swap_nodes(struct radix_node *from, struct radix_node *to)
{
*to = *from;
if (from->rn_p->rn_l == from)
from->rn_p->rn_l = to;
else
from->rn_p->rn_r = to;
to->rn_l->rn_p = to;
to->rn_r->rn_p = to;
}
struct radix_node *
rn_delete(void *v_arg, void *n_arg, struct radix_node_head *head,
struct radix_node *rn)
{
caddr_t v = v_arg;
caddr_t netmask = n_arg;
struct radix_node *top = head->rnh_treetop;
struct radix_node *tt, *tp, *pp, *x;
struct radix_node *dupedkey_tt, *saved_tt;
int off = top->rn_off;
int vlen;
vlen = SALEN(v);
/*
* Implement a lookup similar to rn_lookup but we need to save
* the radix leaf node (where th rn_dupedkey list starts) so
* it is not possible to use rn_lookup.
*/
tt = rn_search(v, top);
/* make sure the key is a perfect match */
if (memcmp(v + off, tt->rn_key + off, vlen - off))
return (NULL);
/*
* Here, tt is the deletion target, and
* saved_tt is the head of the dupedkey chain.
* dupedkey_tt will point to the start of the multipath chain.
*/
saved_tt = tt;
/*
* make tt point to the start of the rn_dupedkey list of multipath
* routes.
*/
if (netmask) {
struct radix_node *tm;
if ((tm = rn_addmask(netmask, 1, off)) == NULL)
return (NULL);
netmask = tm->rn_key;
while (tt->rn_mask != netmask)
if ((tt = tt->rn_dupedkey) == NULL)
return (NULL);
}
/* save start of multi path chain for later use */
dupedkey_tt = tt;
KASSERT((tt->rn_flags & RNF_ROOT) == 0);
/* remove possible radix_mask */
if (rn_del_radix_mask(tt))
return (NULL);
/*
* Finally eliminate us from tree
*/
tp = tt->rn_p;
if (saved_tt->rn_dupedkey) {
if (tt == saved_tt) {
x = saved_tt->rn_dupedkey;
x->rn_p = tp;
if (tp->rn_l == tt)
tp->rn_l = x;
else
tp->rn_r = x;
/* head changed adjust dupedkey pointer */
dupedkey_tt = x;
} else {
x = saved_tt;
/* dupedkey will change so adjust pointer */
if (dupedkey_tt == tt)
dupedkey_tt = tt->rn_dupedkey;
tp->rn_dupedkey = tt->rn_dupedkey;
if (tt->rn_dupedkey)
tt->rn_dupedkey->rn_p = tp;
}
/*
* We may be holding an active internal node in the tree.
*/
if (tt[1].rn_flags & RNF_ACTIVE)
rn_swap_nodes(&tt[1], &x[1]);
/* over and out */
goto out;
}
/* non-rn_dupedkey case, remove tt and tp node from the tree */
if (tp->rn_l == tt)
x = tp->rn_r;
else
x = tp->rn_l;
pp = tp->rn_p;
if (pp->rn_r == tp)
pp->rn_r = x;
else
pp->rn_l = x;
x->rn_p = pp;
/*
* Demote routes attached to us (actually on the internal parent node).
*/
if (tp->rn_mklist) {
struct radix_mask *m, **mp;
if (x->rn_b >= 0) {
for (mp = &x->rn_mklist; (m = *mp);)
mp = &m->rm_mklist;
*mp = tp->rn_mklist;
} else {
/* If there are any key,mask pairs in a sibling
duped-key chain, some subset will appear sorted
in the same order attached to our mklist */
for (m = tp->rn_mklist; m && x; x = x->rn_dupedkey)
if (m == x->rn_mklist) {
struct radix_mask *mm = m->rm_mklist;
x->rn_mklist = 0;
if (--(m->rm_refs) < 0)
pool_put(&rtmask_pool, m);
else if (m->rm_flags & RNF_NORMAL)
/*
* don't progress because this
* a multipath route. Next
* route will use the same m.
*/
mm = m;
m = mm;
}
if (m)
log(LOG_ERR, "%s %p at %p\n",
"rn_delete: Orphaned Mask", m, x);
}
}
/*
* We may be holding an active internal node in the tree.
* If so swap our internal node (t) with the parent node (tp)
* since that one was just removed from the tree.
*/
if (tp != &tt[1])
rn_swap_nodes(&tt[1], tp);
/* no rn_dupedkey list so no need to fixup multipath chains */
out:
tt[0].rn_flags &= ~RNF_ACTIVE;
tt[1].rn_flags &= ~RNF_ACTIVE;
return (tt);
}
int
rn_walktree(struct radix_node_head *h, int (*f)(struct radix_node *, void *,
u_int), void *w)
{
int error;
struct radix_node *base, *next;
struct radix_node *rn = h->rnh_treetop;
/*
* This gets complicated because we may delete the node
* while applying the function f to it, so we need to calculate
* the successor node in advance.
*/
/* First time through node, go left */
while (rn->rn_b >= 0)
rn = rn->rn_l;
for (;;) {
base = rn;
/* If at right child go back up, otherwise, go right */
while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0)
rn = rn->rn_p;
/* Find the next *leaf* since next node might vanish, too */
for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;)
rn = rn->rn_l;
next = rn;
/* Process leaves */
while ((rn = base) != NULL) {
base = rn->rn_dupedkey;
if (!(rn->rn_flags & RNF_ROOT) &&
(error = (*f)(rn, w, h->rnh_rtableid)))
return (error);
}
rn = next;
if (rn->rn_flags & RNF_ROOT)
return (0);
}
/* NOTREACHED */
}
int
rn_initmask(void)
{
if (mask_rnhead != NULL)
return (0);
KASSERT(max_keylen > 0);
mask_rnhead = malloc(sizeof(*mask_rnhead), M_RTABLE, M_NOWAIT);
if (mask_rnhead == NULL)
return (1);
rn_inithead0(mask_rnhead, 0);
return (0);
}
int
rn_inithead(void **head, int off)
{
struct radix_node_head *rnh;
if (*head != NULL)
return (1);
if (rn_initmask())
panic("failed to initialize the mask tree");
rnh = malloc(sizeof(*rnh), M_RTABLE, M_NOWAIT);
if (rnh == NULL)
return (0);
*head = rnh;
rn_inithead0(rnh, off);
return (1);
}
int
rn_inithead0(struct radix_node_head *rnh, int offset)
{
struct radix_node *t, *tt, *ttt;
int off = offset * NBBY;
memset(rnh, 0, sizeof(*rnh));
t = rn_newpair(rn_zeros, off, rnh->rnh_nodes);
ttt = rnh->rnh_nodes + 2;
t->rn_r = ttt;
t->rn_p = t;
tt = t->rn_l;
tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE;
tt->rn_b = -1 - off;
*ttt = *tt;
ttt->rn_key = rn_ones;
rnh->rnh_treetop = t;
return (1);
}
/*
* rn_init() can be called multiple time with a different key length
* as long as no radix tree head has been allocated.
*/
void
rn_init(unsigned int keylen)
{
char *cp, *cplim;
KASSERT(keylen <= KEYLEN_LIMIT);
if (max_keylen == 0) {
pool_init(&rtmask_pool, sizeof(struct radix_mask), 0,
IPL_SOFTNET, 0, "rtmask", NULL);
}
if (keylen <= max_keylen)
return;
KASSERT(mask_rnhead == NULL);
free(rn_zeros, M_RTABLE, 2 * max_keylen);
rn_zeros = mallocarray(2, keylen, M_RTABLE, M_NOWAIT | M_ZERO);
if (rn_zeros == NULL)
panic("cannot initialize a radix tree without memory");
max_keylen = keylen;
cp = rn_ones = rn_zeros + max_keylen;
cplim = rn_ones + max_keylen;
while (cp < cplim)
*cp++ = -1;
}
1783
5741
5754
6524
6549
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/* $OpenBSD: subr_poison.c,v 1.15 2022/08/14 01:58:28 jsg Exp $ */
/*
* Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
/*
* The POISON is used as known text to copy into free objects so
* that modifications after frees can be detected.
*/
#ifdef DEADBEEF0
#define POISON0 ((unsigned) DEADBEEF0)
#else
#define POISON0 ((unsigned) 0xdeadbeef)
#endif
#ifdef DEADBEEF1
#define POISON1 ((unsigned) DEADBEEF1)
#else
#define POISON1 ((unsigned) 0xdeafbead)
#endif
#define POISON_SIZE 64
uint32_t
poison_value(void *v)
{
ulong l = (u_long)v;
l = l >> PAGE_SHIFT;
switch (l & 3) {
case 0:
return POISON0;
case 1:
return POISON1;
case 2:
return (POISON0 & 0xffff0000) | (~POISON0 & 0x0000ffff);
case 3:
return (POISON1 & 0xffff0000) | (~POISON1 & 0x0000ffff);
}
return 0;
}
void
poison_mem(void *v, size_t len)
{
uint32_t *ip = v;
size_t i;
uint32_t poison;
poison = poison_value(v);
if (len > POISON_SIZE)
len = POISON_SIZE;
len = len / sizeof(*ip);
for (i = 0; i < len; i++)
ip[i] = poison;
}
int
poison_check(void *v, size_t len, size_t *pidx, uint32_t *pval)
{
uint32_t *ip = v;
size_t i;
uint32_t poison;
poison = poison_value(v);
if (len > POISON_SIZE)
len = POISON_SIZE;
len = len / sizeof(*ip);
for (i = 0; i < len; i++) {
if (ip[i] != poison) {
*pidx = i;
*pval = poison;
return 1;
}
}
return 0;
}
3
217
217
217
215
215
217
215
217
217
215
214
2
2
2
217
216
2
215
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
/* $OpenBSD: scsi_base.c,v 1.280 2022/02/28 14:48:11 krw Exp $ */
/* $NetBSD: scsi_base.c,v 1.43 1997/04/02 02:29:36 mycroft Exp $ */
/*
* Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Originally written by Julian Elischer (julian@dialix.oz.au)
* Detailed SCSI error printing Copyright 1997 by Matthew Jacob.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/uio.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/pool.h>
#include <sys/task.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
static __inline void asc2ascii(u_int8_t, u_int8_t ascq, char *result,
size_t len);
int scsi_xs_error(struct scsi_xfer *);
char *scsi_decode_sense(struct scsi_sense_data *, int);
void scsi_xs_sync_done(struct scsi_xfer *);
/* Values for flag parameter to scsi_decode_sense. */
#define DECODE_SENSE_KEY 1
#define DECODE_ASC_ASCQ 2
#define DECODE_SKSV 3
struct pool scsi_xfer_pool;
struct pool scsi_plug_pool;
struct scsi_plug {
struct task task;
struct scsibus_softc *sb;
int target;
int lun;
int how;
};
void scsi_plug_probe(void *);
void scsi_plug_detach(void *);
struct scsi_xfer * scsi_xs_io(struct scsi_link *, void *, int);
int scsi_ioh_pending(struct scsi_iopool *);
struct scsi_iohandler * scsi_ioh_deq(struct scsi_iopool *);
void scsi_xsh_runqueue(struct scsi_link *);
void scsi_xsh_ioh(void *, void *);
int scsi_link_open(struct scsi_link *);
void scsi_link_close(struct scsi_link *);
void * scsi_iopool_get(struct scsi_iopool *);
void scsi_iopool_put(struct scsi_iopool *, void *);
/* Various helper functions for scsi_do_mode_sense() */
int scsi_mode_sense(struct scsi_link *, int,
union scsi_mode_sense_buf *, int);
int scsi_mode_sense_big(struct scsi_link *, int,
union scsi_mode_sense_buf *, int);
void * scsi_mode_sense_page(struct scsi_mode_header *, int,
int);
void * scsi_mode_sense_big_page(struct scsi_mode_header_big *,
int, int);
/* ioh/xsh queue state */
#define RUNQ_IDLE 0
#define RUNQ_LINKQ 1
#define RUNQ_POOLQ 2
/* synchronous api for allocating an io. */
struct scsi_io_mover {
struct mutex mtx;
void *io;
u_int done;
};
#define SCSI_IO_MOVER_INITIALIZER { MUTEX_INITIALIZER(IPL_BIO), NULL, 0 }
void scsi_move(struct scsi_io_mover *);
void scsi_move_done(void *, void *);
void scsi_io_get_done(void *, void *);
void scsi_xs_get_done(void *, void *);
/*
* Called when a scsibus is attached to initialize global data.
*/
void
scsi_init(void)
{
static int scsi_init_done;
if (scsi_init_done)
return;
scsi_init_done = 1;
#if defined(SCSI_DELAY) && SCSI_DELAY > 0
/* Historical. Older buses may need a moment to stabilize. */
delay(1000000 * SCSI_DELAY);
#endif /* SCSI_DELAY && SCSI_DELAY > 0 */
/* Initialize the scsi_xfer pool. */
pool_init(&scsi_xfer_pool, sizeof(struct scsi_xfer), 0, IPL_BIO, 0,
"scxspl", NULL);
pool_init(&scsi_plug_pool, sizeof(struct scsi_plug), 0, IPL_BIO, 0,
"scsiplug", NULL);
}
int
scsi_req_probe(struct scsibus_softc *sb, int target, int lun)
{
struct scsi_plug *p;
p = pool_get(&scsi_plug_pool, PR_NOWAIT);
if (p == NULL)
return ENOMEM;
task_set(&p->task, scsi_plug_probe, p);
p->sb = sb;
p->target = target;
p->lun = lun;
task_add(systq, &p->task);
return 0;
}
int
scsi_req_detach(struct scsibus_softc *sb, int target, int lun, int how)
{
struct scsi_plug *p;
p = pool_get(&scsi_plug_pool, PR_NOWAIT);
if (p == NULL)
return ENOMEM;
task_set(&p->task, scsi_plug_detach, p);
p->sb = sb;
p->target = target;
p->lun = lun;
p->how = how;
task_add(systq, &p->task);
return 0;
}
void
scsi_plug_probe(void *xp)
{
struct scsi_plug *p = xp;
struct scsibus_softc *sb = p->sb;
int target = p->target, lun = p->lun;
pool_put(&scsi_plug_pool, p);
scsi_probe(sb, target, lun);
}
void
scsi_plug_detach(void *xp)
{
struct scsi_plug *p = xp;
struct scsibus_softc *sb = p->sb;
int target = p->target, lun = p->lun;
int how = p->how;
pool_put(&scsi_plug_pool, p);
scsi_detach(sb, target, lun, how);
}
int
scsi_pending_start(struct mutex *mtx, u_int *running)
{
int rv = 1;
mtx_enter(mtx);
(*running)++;
if ((*running) > 1)
rv = 0;
mtx_leave(mtx);
return rv;
}
int
scsi_pending_finish(struct mutex *mtx, u_int *running)
{
int rv = 1;
mtx_enter(mtx);
(*running)--;
if ((*running) > 0) {
(*running) = 1;
rv = 0;
}
mtx_leave(mtx);
return rv;
}
void
scsi_iopool_init(struct scsi_iopool *iopl, void *iocookie,
void *(*io_get)(void *), void (*io_put)(void *, void *))
{
iopl->iocookie = iocookie;
iopl->io_get = io_get;
iopl->io_put = io_put;
TAILQ_INIT(&iopl->queue);
iopl->running = 0;
mtx_init(&iopl->mtx, IPL_BIO);
}
void *
scsi_iopool_get(struct scsi_iopool *iopl)
{
void *io;
KERNEL_LOCK();
io = iopl->io_get(iopl->iocookie);
KERNEL_UNLOCK();
return io;
}
void
scsi_iopool_put(struct scsi_iopool *iopl, void *io)
{
KERNEL_LOCK();
iopl->io_put(iopl->iocookie, io);
KERNEL_UNLOCK();
}
void
scsi_iopool_destroy(struct scsi_iopool *iopl)
{
struct scsi_runq sleepers = TAILQ_HEAD_INITIALIZER(sleepers);
struct scsi_iohandler *ioh = NULL;
mtx_enter(&iopl->mtx);
while ((ioh = TAILQ_FIRST(&iopl->queue)) != NULL) {
TAILQ_REMOVE(&iopl->queue, ioh, q_entry);
ioh->q_state = RUNQ_IDLE;
if (ioh->handler == scsi_io_get_done)
TAILQ_INSERT_TAIL(&sleepers, ioh, q_entry);
#ifdef DIAGNOSTIC
else
panic("scsi_iopool_destroy: scsi_iohandler on pool");
#endif /* DIAGNOSTIC */
}
mtx_leave(&iopl->mtx);
while ((ioh = TAILQ_FIRST(&sleepers)) != NULL) {
TAILQ_REMOVE(&sleepers, ioh, q_entry);
ioh->handler(ioh->cookie, NULL);
}
}
void *
scsi_default_get(void *iocookie)
{
return SCSI_IOPOOL_POISON;
}
void
scsi_default_put(void *iocookie, void *io)
{
#ifdef DIAGNOSTIC
if (io != SCSI_IOPOOL_POISON)
panic("unexpected opening returned");
#endif /* DIAGNOSTIC */
}
/*
* public interface to the ioh api.
*/
void
scsi_ioh_set(struct scsi_iohandler *ioh, struct scsi_iopool *iopl,
void (*handler)(void *, void *), void *cookie)
{
ioh->q_state = RUNQ_IDLE;
ioh->pool = iopl;
ioh->handler = handler;
ioh->cookie = cookie;
}
int
scsi_ioh_add(struct scsi_iohandler *ioh)
{
struct scsi_iopool *iopl = ioh->pool;
int rv = 0;
mtx_enter(&iopl->mtx);
switch (ioh->q_state) {
case RUNQ_IDLE:
TAILQ_INSERT_TAIL(&iopl->queue, ioh, q_entry);
ioh->q_state = RUNQ_POOLQ;
rv = 1;
break;
#ifdef DIAGNOSTIC
case RUNQ_POOLQ:
break;
default:
panic("scsi_ioh_add: unexpected state %u", ioh->q_state);
#endif /* DIAGNOSTIC */
}
mtx_leave(&iopl->mtx);
/* lets get some io up in the air */
scsi_iopool_run(iopl);
return rv;
}
int
scsi_ioh_del(struct scsi_iohandler *ioh)
{
struct scsi_iopool *iopl = ioh->pool;
int rv = 0;
mtx_enter(&iopl->mtx);
switch (ioh->q_state) {
case RUNQ_POOLQ:
TAILQ_REMOVE(&iopl->queue, ioh, q_entry);
ioh->q_state = RUNQ_IDLE;
rv = 1;
break;
#ifdef DIAGNOSTIC
case RUNQ_IDLE:
break;
default:
panic("scsi_ioh_del: unexpected state %u", ioh->q_state);
#endif /* DIAGNOSTIC */
}
mtx_leave(&iopl->mtx);
return rv;
}
/*
* internal iopool runqueue handling.
*/
struct scsi_iohandler *
scsi_ioh_deq(struct scsi_iopool *iopl)
{
struct scsi_iohandler *ioh = NULL;
mtx_enter(&iopl->mtx);
ioh = TAILQ_FIRST(&iopl->queue);
if (ioh != NULL) {
TAILQ_REMOVE(&iopl->queue, ioh, q_entry);
ioh->q_state = RUNQ_IDLE;
}
mtx_leave(&iopl->mtx);
return ioh;
}
int
scsi_ioh_pending(struct scsi_iopool *iopl)
{
int rv;
mtx_enter(&iopl->mtx);
rv = !TAILQ_EMPTY(&iopl->queue);
mtx_leave(&iopl->mtx);
return rv;
}
void
scsi_iopool_run(struct scsi_iopool *iopl)
{
struct scsi_iohandler *ioh;
void *io;
if (!scsi_pending_start(&iopl->mtx, &iopl->running))
return;
do {
while (scsi_ioh_pending(iopl)) {
io = scsi_iopool_get(iopl);
if (io == NULL)
break;
ioh = scsi_ioh_deq(iopl);
if (ioh == NULL) {
scsi_iopool_put(iopl, io);
break;
}
ioh->handler(ioh->cookie, io);
}
} while (!scsi_pending_finish(&iopl->mtx, &iopl->running));
}
/*
* move an io from a runq to a proc thats waiting for an io.
*/
void
scsi_move(struct scsi_io_mover *m)
{
mtx_enter(&m->mtx);
while (!m->done)
msleep_nsec(m, &m->mtx, PRIBIO, "scsiiomv", INFSLP);
mtx_leave(&m->mtx);
}
void
scsi_move_done(void *cookie, void *io)
{
struct scsi_io_mover *m = cookie;
mtx_enter(&m->mtx);
m->io = io;
m->done = 1;
wakeup_one(m);
mtx_leave(&m->mtx);
}
/*
* synchronous api for allocating an io.
*/
void *
scsi_io_get(struct scsi_iopool *iopl, int flags)
{
struct scsi_io_mover m = SCSI_IO_MOVER_INITIALIZER;
struct scsi_iohandler ioh;
void *io;
/* try and sneak an io off the backend immediately */
io = scsi_iopool_get(iopl);
if (io != NULL)
return io;
else if (ISSET(flags, SCSI_NOSLEEP))
return NULL;
/* otherwise sleep until we get one */
scsi_ioh_set(&ioh, iopl, scsi_io_get_done, &m);
scsi_ioh_add(&ioh);
scsi_move(&m);
return m.io;
}
void
scsi_io_get_done(void *cookie, void *io)
{
scsi_move_done(cookie, io);
}
void
scsi_io_put(struct scsi_iopool *iopl, void *io)
{
scsi_iopool_put(iopl, io);
scsi_iopool_run(iopl);
}
/*
* public interface to the xsh api.
*/
void
scsi_xsh_set(struct scsi_xshandler *xsh, struct scsi_link *link,
void (*handler)(struct scsi_xfer *))
{
scsi_ioh_set(&xsh->ioh, link->pool, scsi_xsh_ioh, xsh);
xsh->link = link;
xsh->handler = handler;
}
int
scsi_xsh_add(struct scsi_xshandler *xsh)
{
struct scsi_link *link = xsh->link;
int rv = 0;
if (ISSET(link->state, SDEV_S_DYING))
return 0;
mtx_enter(&link->pool->mtx);
if (xsh->ioh.q_state == RUNQ_IDLE) {
TAILQ_INSERT_TAIL(&link->queue, &xsh->ioh, q_entry);
xsh->ioh.q_state = RUNQ_LINKQ;
rv = 1;
}
mtx_leave(&link->pool->mtx);
/* lets get some io up in the air */
scsi_xsh_runqueue(link);
return rv;
}
int
scsi_xsh_del(struct scsi_xshandler *xsh)
{
struct scsi_link *link = xsh->link;
int rv = 1;
mtx_enter(&link->pool->mtx);
switch (xsh->ioh.q_state) {
case RUNQ_IDLE:
rv = 0;
break;
case RUNQ_LINKQ:
TAILQ_REMOVE(&link->queue, &xsh->ioh, q_entry);
break;
case RUNQ_POOLQ:
TAILQ_REMOVE(&link->pool->queue, &xsh->ioh, q_entry);
link->pending--;
if (ISSET(link->state, SDEV_S_DYING) && link->pending == 0)
wakeup_one(&link->pending);
break;
default:
panic("unexpected xsh state %u", xsh->ioh.q_state);
}
xsh->ioh.q_state = RUNQ_IDLE;
mtx_leave(&link->pool->mtx);
return rv;
}
/*
* internal xs runqueue handling.
*/
void
scsi_xsh_runqueue(struct scsi_link *link)
{
struct scsi_iohandler *ioh;
int runq;
if (!scsi_pending_start(&link->pool->mtx, &link->running))
return;
do {
runq = 0;
mtx_enter(&link->pool->mtx);
while (!ISSET(link->state, SDEV_S_DYING) &&
link->pending < link->openings &&
((ioh = TAILQ_FIRST(&link->queue)) != NULL)) {
link->pending++;
TAILQ_REMOVE(&link->queue, ioh, q_entry);
TAILQ_INSERT_TAIL(&link->pool->queue, ioh, q_entry);
ioh->q_state = RUNQ_POOLQ;
runq = 1;
}
mtx_leave(&link->pool->mtx);
if (runq)
scsi_iopool_run(link->pool);
} while (!scsi_pending_finish(&link->pool->mtx, &link->running));
}
void
scsi_xsh_ioh(void *cookie, void *io)
{
struct scsi_xshandler *xsh = cookie;
struct scsi_xfer *xs;
xs = scsi_xs_io(xsh->link, io, SCSI_NOSLEEP);
if (xs == NULL) {
/*
* in this situation we should queue things waiting for an
* xs and then give them xses when they were supposed be to
* returned to the pool.
*/
printf("scsi_xfer pool exhausted!\n");
scsi_xsh_add(xsh);
return;
}
xsh->handler(xs);
}
/*
* Get a scsi transfer structure for the caller.
* Go to the iopool backend for an "opening" and then attach an xs to it.
*/
struct scsi_xfer *
scsi_xs_get(struct scsi_link *link, int flags)
{
struct scsi_xshandler xsh;
struct scsi_io_mover m = SCSI_IO_MOVER_INITIALIZER;
struct scsi_iopool *iopl = link->pool;
void *io;
if (ISSET(link->state, SDEV_S_DYING))
return NULL;
/* really custom xs handler to avoid scsi_xsh_ioh */
scsi_ioh_set(&xsh.ioh, iopl, scsi_xs_get_done, &m);
xsh.link = link;
if (!scsi_link_open(link)) {
if (ISSET(flags, SCSI_NOSLEEP))
return NULL;
scsi_xsh_add(&xsh);
scsi_move(&m);
if (m.io == NULL)
return NULL;
io = m.io;
} else if ((io = scsi_iopool_get(iopl)) == NULL) {
if (ISSET(flags, SCSI_NOSLEEP)) {
scsi_link_close(link);
return NULL;
}
scsi_ioh_add(&xsh.ioh);
scsi_move(&m);
if (m.io == NULL)
return NULL;
io = m.io;
}
return scsi_xs_io(link, io, flags);
}
void
scsi_xs_get_done(void *cookie, void *io)
{
scsi_move_done(cookie, io);
}
void
scsi_link_shutdown(struct scsi_link *link)
{
struct scsi_runq sleepers = TAILQ_HEAD_INITIALIZER(sleepers);
struct scsi_iopool *iopl = link->pool;
struct scsi_iohandler *ioh;
struct scsi_xshandler *xsh;
mtx_enter(&iopl->mtx);
while ((ioh = TAILQ_FIRST(&link->queue)) != NULL) {
TAILQ_REMOVE(&link->queue, ioh, q_entry);
ioh->q_state = RUNQ_IDLE;
if (ioh->handler == scsi_xs_get_done)
TAILQ_INSERT_TAIL(&sleepers, ioh, q_entry);
#ifdef DIAGNOSTIC
else
panic("scsi_link_shutdown: scsi_xshandler on link");
#endif /* DIAGNOSTIC */
}
ioh = TAILQ_FIRST(&iopl->queue);
while (ioh != NULL) {
xsh = (struct scsi_xshandler *)ioh;
ioh = TAILQ_NEXT(ioh, q_entry);
#ifdef DIAGNOSTIC
if (xsh->ioh.handler == scsi_xsh_ioh &&
xsh->link == link)
panic("scsi_link_shutdown: scsi_xshandler on pool");
#endif /* DIAGNOSTIC */
if (xsh->ioh.handler == scsi_xs_get_done &&
xsh->link == link) {
TAILQ_REMOVE(&iopl->queue, &xsh->ioh, q_entry);
xsh->ioh.q_state = RUNQ_IDLE;
link->pending--;
TAILQ_INSERT_TAIL(&sleepers, &xsh->ioh, q_entry);
}
}
while (link->pending > 0)
msleep_nsec(&link->pending, &iopl->mtx, PRIBIO, "pendxs",
INFSLP);
mtx_leave(&iopl->mtx);
while ((ioh = TAILQ_FIRST(&sleepers)) != NULL) {
TAILQ_REMOVE(&sleepers, ioh, q_entry);
ioh->handler(ioh->cookie, NULL);
}
}
int
scsi_link_open(struct scsi_link *link)
{
int open = 0;
mtx_enter(&link->pool->mtx);
if (link->pending < link->openings) {
link->pending++;
open = 1;
}
mtx_leave(&link->pool->mtx);
return open;
}
void
scsi_link_close(struct scsi_link *link)
{
mtx_enter(&link->pool->mtx);
link->pending--;
if (ISSET(link->state, SDEV_S_DYING) && link->pending == 0)
wakeup_one(&link->pending);
mtx_leave(&link->pool->mtx);
scsi_xsh_runqueue(link);
}
struct scsi_xfer *
scsi_xs_io(struct scsi_link *link, void *io, int flags)
{
struct scsi_xfer *xs;
xs = pool_get(&scsi_xfer_pool, PR_ZERO |
(ISSET(flags, SCSI_NOSLEEP) ? PR_NOWAIT : PR_WAITOK));
if (xs == NULL) {
scsi_io_put(link->pool, io);
scsi_link_close(link);
} else {
xs->flags = flags;
xs->sc_link = link;
xs->retries = SCSI_RETRIES;
xs->timeout = 10000;
xs->io = io;
}
return xs;
}
void
scsi_xs_put(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
void *io = xs->io;
pool_put(&scsi_xfer_pool, xs);
scsi_io_put(link->pool, io);
scsi_link_close(link);
}
/*
* Get scsi driver to send a "are you ready?" command
*/
int
scsi_test_unit_ready(struct scsi_link *link, int retries, int flags)
{
struct scsi_test_unit_ready *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->retries = retries;
xs->timeout = 10000;
cmd = (struct scsi_test_unit_ready *)&xs->cmd;
cmd->opcode = TEST_UNIT_READY;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
void
scsi_init_inquiry(struct scsi_xfer *xs, u_int8_t flags, u_int8_t pagecode,
void *data, size_t len)
{
struct scsi_inquiry *cmd;
cmd = (struct scsi_inquiry *)&xs->cmd;
cmd->opcode = INQUIRY;
cmd->flags = flags;
cmd->pagecode = pagecode;
_lto2b(len, cmd->length);
xs->cmdlen = sizeof(*cmd);
SET(xs->flags, SCSI_DATA_IN);
xs->data = data;
xs->datalen = len;
}
/*
* Do a scsi operation asking a device what it is.
* Use the scsi_cmd routine in the switch table.
*/
int
scsi_inquire(struct scsi_link *link, struct scsi_inquiry_data *inqbuf,
int flags)
{
struct scsi_xfer *xs;
size_t bytes;
int avail, retries, error, received;
/*
* Start by asking for only the basic 36 bytes of SCSI2 inquiry
* information. This avoids problems with devices that choke trying to
* supply more.
*/
bytes = SID_SCSI2_HDRLEN + SID_SCSI2_ALEN;
retries = 0;
again:
xs = scsi_xs_get(link, flags);
if (xs == NULL)
return EBUSY;
if (bytes > sizeof(*inqbuf))
bytes = sizeof(*inqbuf);
scsi_init_inquiry(xs, 0, 0, inqbuf, bytes);
error = scsi_xs_sync(xs);
received = xs->datalen - xs->resid;
scsi_xs_put(xs);
if (error != 0) {
SC_DEBUG(link, SDEV_DB2, ("INQUIRE error %d\n", error));
return error;
}
if (received < SID_SCSI2_HDRLEN) {
SC_DEBUG(link, SDEV_DB2, ("INQUIRE data < SID_SCSI2_HDRLEN\n"));
return EINVAL;
}
avail = SID_SCSI2_HDRLEN + inqbuf->additional_length;
if (received < avail && retries == 0) {
retries++;
bytes = avail;
goto again;
}
#ifdef SCSIDEBUG
sc_print_addr(link);
printf("got %d of %d bytes of inquiry data:\n", received,
avail);
scsi_show_mem((u_char *)inqbuf, received);
sc_print_addr(link);
scsi_show_inquiry_header(inqbuf);
sc_print_addr(link);
scsi_show_inquiry_match(inqbuf);
#endif /* SCSIDEBUG */
if (avail > received)
inqbuf->additional_length = received - SID_SCSI2_HDRLEN;
return 0;
}
/*
* Query a VPD inquiry page
*/
int
scsi_inquire_vpd(struct scsi_link *link, void *buf, u_int buflen,
u_int8_t page, int flags)
{
struct scsi_xfer *xs;
int error;
#ifdef SCSIDEBUG
u_int32_t bytes;
#endif /* SCSIDEBUG */
if (ISSET(link->flags, SDEV_UMASS))
return EJUSTRETURN;
xs = scsi_xs_get(link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
return ENOMEM;
xs->retries = 2;
xs->timeout = 10000;
scsi_init_inquiry(xs, SI_EVPD, page, buf, buflen);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
#ifdef SCSIDEBUG
sc_print_addr(link);
if (error == 0) {
bytes = sizeof(struct scsi_vpd_hdr) +
_2btol(((struct scsi_vpd_hdr *)buf)->page_length);
if (bytes < buflen)
buflen = bytes;
printf("got %u of %u bytes of VPD inquiry page %u data:\n",
buflen, bytes, page);
scsi_show_mem(buf, buflen);
} else {
printf("VPD inquiry page %u not available\n", page);
}
#endif /* SCSIDEBUG */
return error;
}
int
scsi_read_cap_10(struct scsi_link *link, struct scsi_read_cap_data *rdcap,
int flags)
{
struct scsi_read_capacity cdb;
struct scsi_xfer *xs;
int rv;
xs = scsi_xs_get(link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
return ENOMEM;
memset(&cdb, 0, sizeof(cdb));
cdb.opcode = READ_CAPACITY;
memcpy(&xs->cmd, &cdb, sizeof(cdb));
xs->cmdlen = sizeof(cdb);
xs->data = (void *)rdcap;
xs->datalen = sizeof(*rdcap);
xs->timeout = 20000;
rv = scsi_xs_sync(xs);
scsi_xs_put(xs);
#ifdef SCSIDEBUG
if (rv == 0) {
sc_print_addr(link);
printf("read capacity 10 data:\n");
scsi_show_mem((u_char *)rdcap, sizeof(*rdcap));
}
#endif /* SCSIDEBUG */
return rv;
}
int
scsi_read_cap_16(struct scsi_link *link, struct scsi_read_cap_data_16 *rdcap,
int flags)
{
struct scsi_read_capacity_16 cdb;
struct scsi_xfer *xs;
int rv;
xs = scsi_xs_get(link, flags | SCSI_DATA_IN | SCSI_SILENT);
if (xs == NULL)
return ENOMEM;
memset(&cdb, 0, sizeof(cdb));
cdb.opcode = READ_CAPACITY_16;
cdb.byte2 = SRC16_SERVICE_ACTION;
_lto4b(sizeof(*rdcap), cdb.length);
memcpy(&xs->cmd, &cdb, sizeof(cdb));
xs->cmdlen = sizeof(cdb);
xs->data = (void *)rdcap;
xs->datalen = sizeof(*rdcap);
xs->timeout = 20000;
rv = scsi_xs_sync(xs);
scsi_xs_put(xs);
#ifdef SCSIDEBUG
if (rv == 0) {
sc_print_addr(link);
printf("read capacity 16 data:\n");
scsi_show_mem((u_char *)rdcap, sizeof(*rdcap));
}
#endif /* SCSIDEBUG */
return rv;
}
/*
* Prevent or allow the user to remove the media
*/
int
scsi_prevent(struct scsi_link *link, int type, int flags)
{
struct scsi_prevent *cmd;
struct scsi_xfer *xs;
int error;
if (ISSET(link->quirks, ADEV_NODOORLOCK))
return 0;
xs = scsi_xs_get(link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->retries = 2;
xs->timeout = 5000;
cmd = (struct scsi_prevent *)&xs->cmd;
cmd->opcode = PREVENT_ALLOW;
cmd->how = type;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Get scsi driver to send a "start up" command
*/
int
scsi_start(struct scsi_link *link, int type, int flags)
{
struct scsi_start_stop *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->retries = 2;
xs->timeout = (type == SSS_START) ? 30000 : 10000;
cmd = (struct scsi_start_stop *)&xs->cmd;
cmd->opcode = START_STOP;
cmd->how = type;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
int
scsi_mode_sense(struct scsi_link *link, int pg_code,
union scsi_mode_sense_buf *data, int flags)
{
struct scsi_mode_sense *cmd;
struct scsi_xfer *xs;
size_t len;
int error;
#ifdef SCSIDEBUG
size_t bytes;
#endif /* SCSIDEBUG */
len = sizeof(*data);
xs = scsi_xs_get(link, flags | SCSI_DATA_IN);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)data;
xs->datalen = len;
xs->timeout = 20000;
/*
* Make sure the sense buffer is clean before we do the mode sense, so
* that checks for bogus values of 0 will work in case the mode sense
* fails.
*/
memset(data, 0, len);
cmd = (struct scsi_mode_sense *)&xs->cmd;
cmd->opcode = MODE_SENSE;
cmd->page = pg_code;
if (len > 0xff)
len = 0xff;
cmd->length = len;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0 && !VALID_MODE_HDR(&data->hdr))
error = EIO;
#ifdef SCSIDEBUG
sc_print_addr(link);
if (error == 0) {
bytes = sizeof(data->hdr.data_length) + data->hdr.data_length;
if (bytes < len)
len = bytes;
printf("got %zu of %zu bytes of mode sense (6) page %d data:\n",
len, bytes, pg_code);
scsi_show_mem((u_char *)data, len);
} else
printf("mode sense (6) page %d not available\n", pg_code);
#endif /* SCSIDEBUG */
return error;
}
int
scsi_mode_sense_big(struct scsi_link *link, int pg_code,
union scsi_mode_sense_buf *data, int flags)
{
struct scsi_mode_sense_big *cmd;
struct scsi_xfer *xs;
size_t len;
int error;
#ifdef SCSIDEBUG
size_t bytes;
#endif /* SCSIDEBUG */
len = sizeof(*data);
xs = scsi_xs_get(link, flags | SCSI_DATA_IN);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)data;
xs->datalen = len;
xs->timeout = 20000;
/*
* Make sure the sense buffer is clean before we do the mode sense, so
* that checks for bogus values of 0 will work in case the mode sense
* fails.
*/
memset(data, 0, len);
cmd = (struct scsi_mode_sense_big *)&xs->cmd;
cmd->opcode = MODE_SENSE_BIG;
cmd->page = pg_code;
if (len > 0xffff)
len = 0xffff;
_lto2b(len, cmd->length);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0 && !VALID_MODE_HDR_BIG(&data->hdr_big))
error = EIO;
#ifdef SCSIDEBUG
sc_print_addr(link);
if (error == 0) {
bytes = sizeof(data->hdr_big.data_length) +
_2btol(data->hdr_big.data_length);
if (bytes < len)
len = bytes;
printf("got %zu bytes of %zu bytes of mode sense (10) page %d "
"data:\n", len, bytes, pg_code);
scsi_show_mem((u_char *)data, len);
} else
printf("mode sense (10) page %d not available\n", pg_code);
#endif /* SCSIDEBUG */
return error;
}
void *
scsi_mode_sense_page(struct scsi_mode_header *hdr, int pg_code, int pg_length)
{
u_int8_t *page;
int total_length, header_length;
total_length = hdr->data_length + sizeof(hdr->data_length);
header_length = sizeof(*hdr) + hdr->blk_desc_len;
page = (u_int8_t *)hdr + header_length;
if ((total_length - header_length) < pg_length)
return NULL;
if ((*page & SMS_PAGE_CODE) != pg_code)
return NULL;
return page;
}
void *
scsi_mode_sense_big_page(struct scsi_mode_header_big *hdr, int pg_code,
int pg_length)
{
u_int8_t *page;
int total_length, header_length;
total_length = _2btol(hdr->data_length) + sizeof(hdr->data_length);
header_length = sizeof(*hdr) + _2btol(hdr->blk_desc_len);
page = (u_int8_t *)hdr + header_length;
if ((total_length - header_length) < pg_length)
return NULL;
if ((*page & SMS_PAGE_CODE) != pg_code)
return NULL;
return page;
}
void
scsi_parse_blkdesc(struct scsi_link *link, union scsi_mode_sense_buf *buf,
int big, u_int32_t *density, u_int64_t *block_count, u_int32_t *block_size)
{
struct scsi_direct_blk_desc *direct;
struct scsi_blk_desc *general;
size_t offset;
unsigned int blk_desc_len;
if (big == 0) {
offset = sizeof(struct scsi_mode_header);
blk_desc_len = buf->hdr.blk_desc_len;
} else {
offset = sizeof(struct scsi_mode_header_big);
blk_desc_len = _2btol(buf->hdr_big.blk_desc_len);
}
/* Both scsi_blk_desc and scsi_direct_blk_desc are 8 bytes. */
if (blk_desc_len == 0 || (blk_desc_len % 8 != 0))
return;
switch (link->inqdata.device & SID_TYPE) {
case T_SEQUENTIAL:
/*
* XXX What other device types return general block descriptors?
*/
general = (struct scsi_blk_desc *)&buf->buf[offset];
if (density != NULL)
*density = general->density;
if (block_size != NULL)
*block_size = _3btol(general->blklen);
if (block_count != NULL)
*block_count = (u_int64_t)_3btol(general->nblocks);
break;
default:
direct = (struct scsi_direct_blk_desc *)&buf->buf[offset];
if (density != NULL)
*density = direct->density;
if (block_size != NULL)
*block_size = _3btol(direct->blklen);
if (block_count != NULL)
*block_count = (u_int64_t)_4btol(direct->nblocks);
break;
}
}
int
scsi_do_mode_sense(struct scsi_link *link, int pg_code,
union scsi_mode_sense_buf *buf, void **page_data,
int pg_length, int flags, int *big)
{
int error = 0;
*page_data = NULL;
*big = 0;
if (!ISSET(link->flags, SDEV_ATAPI) ||
(link->inqdata.device & SID_TYPE) == T_SEQUENTIAL) {
/*
* Try 6 byte mode sense request first. Some devices don't
* distinguish between 6 and 10 byte MODE SENSE commands,
* returning 6 byte data for 10 byte requests. ATAPI tape
* drives use MODE SENSE (6) even though ATAPI uses 10 byte
* everything else. Don't bother with SMS_DBD. Check returned
* data length to ensure that at least a header (3 additional
* bytes) is returned.
*/
error = scsi_mode_sense(link, pg_code, buf, flags);
if (error == 0) {
/*
* Page data may be invalid (e.g. all zeros) but we
* accept the device's word that this is the best it can
* do. Some devices will freak out if their word is not
* accepted and MODE_SENSE_BIG is attempted.
*/
*page_data = scsi_mode_sense_page(&buf->hdr, pg_code,
pg_length);
return 0;
}
}
/*
* non-ATAPI, non-USB devices that don't support SCSI-2 commands
* (i.e. MODE SENSE (10)) are done.
*/
if (!ISSET(link->flags, (SDEV_ATAPI | SDEV_UMASS)) &&
SID_ANSII_REV(&link->inqdata) < SCSI_REV_2)
return error;
/*
* Try 10 byte mode sense request.
*/
error = scsi_mode_sense_big(link, pg_code, buf, flags);
if (error != 0)
return error;
*big = 1;
*page_data = scsi_mode_sense_big_page(&buf->hdr_big, pg_code,
pg_length);
return 0;
}
int
scsi_mode_select(struct scsi_link *link, int byte2,
struct scsi_mode_header *data, int flags, int timeout)
{
struct scsi_mode_select *cmd;
struct scsi_xfer *xs;
int error;
u_int32_t len;
len = data->data_length + 1; /* 1 == sizeof(data_length) */
xs = scsi_xs_get(link, flags | SCSI_DATA_OUT);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)data;
xs->datalen = len;
xs->timeout = timeout;
cmd = (struct scsi_mode_select *)&xs->cmd;
cmd->opcode = MODE_SELECT;
cmd->byte2 = byte2;
cmd->length = len;
/* Length is reserved when doing mode select so zero it. */
data->data_length = 0;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
SC_DEBUG(link, SDEV_DB2, ("scsi_mode_select: error = %d\n", error));
return error;
}
int
scsi_mode_select_big(struct scsi_link *link, int byte2,
struct scsi_mode_header_big *data, int flags, int timeout)
{
struct scsi_mode_select_big *cmd;
struct scsi_xfer *xs;
int error;
u_int32_t len;
len = _2btol(data->data_length) + 2; /* 2 == sizeof data_length */
xs = scsi_xs_get(link, flags | SCSI_DATA_OUT);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)data;
xs->datalen = len;
xs->timeout = timeout;
cmd = (struct scsi_mode_select_big *)&xs->cmd;
cmd->opcode = MODE_SELECT_BIG;
cmd->byte2 = byte2;
_lto2b(len, cmd->length);
/* Length is reserved when doing mode select so zero it. */
_lto2b(0, data->data_length);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
SC_DEBUG(link, SDEV_DB2, ("scsi_mode_select_big: error = %d\n",
error));
return error;
}
int
scsi_report_luns(struct scsi_link *link, int selectreport,
struct scsi_report_luns_data *data, u_int32_t datalen, int flags,
int timeout)
{
struct scsi_report_luns *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(link, flags | SCSI_DATA_IN);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)data;
xs->datalen = datalen;
xs->timeout = timeout;
bzero(data, datalen);
cmd = (struct scsi_report_luns *)&xs->cmd;
cmd->opcode = REPORT_LUNS;
cmd->selectreport = selectreport;
_lto4b(datalen, cmd->length);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
SC_DEBUG(link, SDEV_DB2, ("scsi_report_luns: error = %d\n", error));
return error;
}
void
scsi_xs_exec(struct scsi_xfer *xs)
{
xs->error = XS_NOERROR;
xs->resid = xs->datalen;
xs->status = 0;
CLR(xs->flags, ITSDONE);
#ifdef SCSIDEBUG
scsi_show_xs(xs);
#endif /* SCSIDEBUG */
/* The adapter's scsi_cmd() is responsible for calling scsi_done(). */
KERNEL_LOCK();
xs->sc_link->bus->sb_adapter->scsi_cmd(xs);
KERNEL_UNLOCK();
}
/*
* Used by device drivers that fake various scsi commands.
*/
void
scsi_copy_internal_data(struct scsi_xfer *xs, void *data, size_t datalen)
{
size_t copy_cnt;
SC_DEBUG(xs->sc_link, SDEV_DB3, ("scsi_copy_internal_data\n"));
if (xs->datalen == 0) {
sc_print_addr(xs->sc_link);
printf("uio internal data copy not supported\n");
} else {
copy_cnt = MIN(datalen, xs->datalen);
memcpy(xs->data, data, copy_cnt);
xs->resid = xs->datalen - copy_cnt;
}
}
/*
* This routine is called by the adapter when its xs handling is done.
*/
void
scsi_done(struct scsi_xfer *xs)
{
#ifdef SCSIDEBUG
if (ISSET(xs->sc_link->flags, SDEV_DB1)) {
if (xs->datalen && ISSET(xs->flags, SCSI_DATA_IN))
scsi_show_mem(xs->data, min(64, xs->datalen));
}
#endif /* SCSIDEBUG */
SET(xs->flags, ITSDONE);
KERNEL_LOCK();
xs->done(xs);
KERNEL_UNLOCK();
}
int
scsi_xs_sync(struct scsi_xfer *xs)
{
struct mutex cookie = MUTEX_INITIALIZER(IPL_BIO);
int error;
#ifdef DIAGNOSTIC
if (xs->cookie != NULL)
panic("xs->cookie != NULL in scsi_xs_sync");
if (xs->done != NULL)
panic("xs->done != NULL in scsi_xs_sync");
#endif /* DIAGNOSTIC */
/*
* If we cant sleep while waiting for completion, get the adapter to
* complete it for us.
*/
if (ISSET(xs->flags, SCSI_NOSLEEP))
SET(xs->flags, SCSI_POLL);
xs->done = scsi_xs_sync_done;
do {
xs->cookie = &cookie;
scsi_xs_exec(xs);
mtx_enter(&cookie);
while (xs->cookie != NULL)
msleep_nsec(xs, &cookie, PRIBIO, "syncxs", INFSLP);
mtx_leave(&cookie);
error = scsi_xs_error(xs);
} while (error == ERESTART);
return error;
}
void
scsi_xs_sync_done(struct scsi_xfer *xs)
{
struct mutex *cookie = xs->cookie;
if (cookie == NULL)
panic("scsi_done called twice on xs(%p)", xs);
mtx_enter(cookie);
xs->cookie = NULL;
if (!ISSET(xs->flags, SCSI_NOSLEEP))
wakeup_one(xs);
mtx_leave(cookie);
}
int
scsi_xs_error(struct scsi_xfer *xs)
{
int error = EIO;
SC_DEBUG(xs->sc_link, SDEV_DB3, ("scsi_xs_error,err = 0x%x\n",
xs->error));
if (ISSET(xs->sc_link->state, SDEV_S_DYING))
return ENXIO;
switch (xs->error) {
case XS_NOERROR: /* nearly always hit this one */
error = 0;
break;
case XS_SENSE:
case XS_SHORTSENSE:
SC_DEBUG_SENSE(xs);
error = xs->sc_link->interpret_sense(xs);
SC_DEBUG(xs->sc_link, SDEV_DB3,
("scsi_interpret_sense returned %#x\n", error));
break;
case XS_BUSY:
error = scsi_delay(xs, 1);
break;
case XS_TIMEOUT:
case XS_RESET:
error = ERESTART;
break;
case XS_DRIVER_STUFFUP:
case XS_SELTIMEOUT:
break;
default:
sc_print_addr(xs->sc_link);
printf("unknown error category (0x%x) from scsi driver\n",
xs->error);
break;
}
if (error == ERESTART && xs->retries-- < 1)
return EIO;
else
return error;
}
int
scsi_delay(struct scsi_xfer *xs, int seconds)
{
int ret;
switch (xs->flags & (SCSI_POLL | SCSI_NOSLEEP)) {
case SCSI_POLL:
delay(1000000 * seconds);
return ERESTART;
case SCSI_NOSLEEP:
/* Retry the command immediately since we can't delay. */
return ERESTART;
case (SCSI_POLL | SCSI_NOSLEEP):
/* Invalid combination! */
return EIO;
}
ret = tsleep_nsec(&ret, PRIBIO|PCATCH, "scbusy", SEC_TO_NSEC(seconds));
/* Signal == abort xs. */
if (ret == ERESTART || ret == EINTR)
return EIO;
return ERESTART;
}
/*
* Look at the returned sense and act on the error, determining
* the unix error number to pass back. (0 = report no error)
*
* THIS IS THE DEFAULT ERROR HANDLER
*/
int
scsi_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
struct scsi_link *link = xs->sc_link;
u_int8_t serr, skey;
int error;
/* Default sense interpretation. */
serr = sense->error_code & SSD_ERRCODE;
if (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED)
skey = 0xff; /* Invalid value, since key is 4 bit value. */
else
skey = sense->flags & SSD_KEY;
/*
* Interpret the key/asc/ascq information where appropriate.
*/
error = 0;
switch (skey) {
case SKEY_NO_SENSE:
case SKEY_RECOVERED_ERROR:
if (xs->resid == xs->datalen)
xs->resid = 0; /* not short read */
break;
case SKEY_BLANK_CHECK:
case SKEY_EQUAL:
break;
case SKEY_NOT_READY:
if (ISSET(xs->flags, SCSI_IGNORE_NOT_READY))
return 0;
error = EIO;
if (xs->retries) {
switch (ASC_ASCQ(sense)) {
case SENSE_NOT_READY_BECOMING_READY:
case SENSE_NOT_READY_FORMAT:
case SENSE_NOT_READY_REBUILD:
case SENSE_NOT_READY_RECALC:
case SENSE_NOT_READY_INPROGRESS:
case SENSE_NOT_READY_LONGWRITE:
case SENSE_NOT_READY_SELFTEST:
case SENSE_NOT_READY_INIT_REQUIRED:
SC_DEBUG(link, SDEV_DB1,
("not ready (ASC_ASCQ == %#x)\n",
ASC_ASCQ(sense)));
return scsi_delay(xs, 1);
case SENSE_NOMEDIUM:
case SENSE_NOMEDIUM_TCLOSED:
case SENSE_NOMEDIUM_TOPEN:
case SENSE_NOMEDIUM_LOADABLE:
case SENSE_NOMEDIUM_AUXMEM:
CLR(link->flags, SDEV_MEDIA_LOADED);
error = ENOMEDIUM;
break;
default:
break;
}
}
break;
case SKEY_MEDIUM_ERROR:
switch (ASC_ASCQ(sense)) {
case SENSE_NOMEDIUM:
case SENSE_NOMEDIUM_TCLOSED:
case SENSE_NOMEDIUM_TOPEN:
case SENSE_NOMEDIUM_LOADABLE:
case SENSE_NOMEDIUM_AUXMEM:
CLR(link->flags, SDEV_MEDIA_LOADED);
error = ENOMEDIUM;
break;
case SENSE_BAD_MEDIUM:
case SENSE_NR_MEDIUM_UNKNOWN_FORMAT:
case SENSE_NR_MEDIUM_INCOMPATIBLE_FORMAT:
case SENSE_NW_MEDIUM_UNKNOWN_FORMAT:
case SENSE_NW_MEDIUM_INCOMPATIBLE_FORMAT:
case SENSE_NF_MEDIUM_INCOMPATIBLE_FORMAT:
case SENSE_NW_MEDIUM_AC_MISMATCH:
error = EMEDIUMTYPE;
break;
default:
error = EIO;
break;
}
break;
case SKEY_ILLEGAL_REQUEST:
if (ISSET(xs->flags, SCSI_IGNORE_ILLEGAL_REQUEST))
return 0;
if (ASC_ASCQ(sense) == SENSE_MEDIUM_REMOVAL_PREVENTED)
return EBUSY;
error = EINVAL;
break;
case SKEY_UNIT_ATTENTION:
switch (ASC_ASCQ(sense)) {
case SENSE_POWER_RESET_OR_BUS:
case SENSE_POWER_ON:
case SENSE_BUS_RESET:
case SENSE_BUS_DEVICE_RESET:
case SENSE_DEVICE_INTERNAL_RESET:
case SENSE_TSC_CHANGE_SE:
case SENSE_TSC_CHANGE_LVD:
case SENSE_IT_NEXUS_LOSS:
return scsi_delay(xs, 1);
default:
break;
}
if (ISSET(link->flags, SDEV_REMOVABLE))
CLR(link->flags, SDEV_MEDIA_LOADED);
if (ISSET(xs->flags, SCSI_IGNORE_MEDIA_CHANGE) ||
/* XXX Should reupload any transient state. */
!ISSET(link->flags, SDEV_REMOVABLE)) {
return scsi_delay(xs, 1);
}
error = EIO;
break;
case SKEY_WRITE_PROTECT:
error = EROFS;
break;
case SKEY_ABORTED_COMMAND:
error = ERESTART;
break;
case SKEY_VOLUME_OVERFLOW:
error = ENOSPC;
break;
case SKEY_HARDWARE_ERROR:
if (ASC_ASCQ(sense) == SENSE_CARTRIDGE_FAULT)
return EMEDIUMTYPE;
error = EIO;
break;
default:
error = EIO;
break;
}
#ifndef SCSIDEBUG
/* SCSIDEBUG would mean it has already been printed. */
if (skey && !ISSET(xs->flags, SCSI_SILENT))
scsi_print_sense(xs);
#endif /* ~SCSIDEBUG */
return error;
}
/*
* Utility routines often used in SCSI stuff
*/
/*
* Print out the scsi_link structure's address info.
*/
void
sc_print_addr(struct scsi_link *link)
{
struct device *adapter_device = link->bus->sc_dev.dv_parent;
printf("%s(%s:%d:%d): ",
link->device_softc ?
((struct device *)link->device_softc)->dv_xname : "probe",
adapter_device->dv_xname,
link->target, link->lun);
}
static const char *sense_keys[16] = {
"No Additional Sense",
"Soft Error",
"Not Ready",
"Media Error",
"Hardware Error",
"Illegal Request",
"Unit Attention",
"Write Protected",
"Blank Check",
"Vendor Unique",
"Copy Aborted",
"Aborted Command",
"Equal Error",
"Volume Overflow",
"Miscompare Error",
"Reserved"
};
#ifdef SCSITERSE
static __inline void
asc2ascii(u_int8_t asc, u_int8_t ascq, char *result, size_t len)
{
snprintf(result, len, "ASC 0x%02x ASCQ 0x%02x", asc, ascq);
}
#else
static const struct {
u_int8_t asc, ascq;
char *description;
} adesc[] = {
/* www.t10.org/lists/asc-num.txt as of 11/15/10. */
{ 0x00, 0x00, "No Additional Sense Information" },
{ 0x00, 0x01, "Filemark Detected" },
{ 0x00, 0x02, "End-Of-Partition/Medium Detected" },
{ 0x00, 0x03, "Setmark Detected" },
{ 0x00, 0x04, "Beginning-Of-Partition/Medium Detected" },
{ 0x00, 0x05, "End-Of-Data Detected" },
{ 0x00, 0x06, "I/O Process Terminated" },
{ 0x00, 0x11, "Audio Play Operation In Progress" },
{ 0x00, 0x12, "Audio Play Operation Paused" },
{ 0x00, 0x13, "Audio Play Operation Successfully Completed" },
{ 0x00, 0x14, "Audio Play Operation Stopped Due to Error" },
{ 0x00, 0x15, "No Current Audio Status To Return" },
{ 0x00, 0x16, "Operation In Progress" },
{ 0x00, 0x17, "Cleaning Requested" },
{ 0x00, 0x18, "Erase Operation In Progress" },
{ 0x00, 0x19, "Locate Operation In Progress" },
{ 0x00, 0x1A, "Rewind Operation In Progress" },
{ 0x00, 0x1B, "Set Capacity Operation In Progress" },
{ 0x00, 0x1C, "Verify Operation In Progress" },
{ 0x01, 0x00, "No Index/Sector Signal" },
{ 0x02, 0x00, "No Seek Complete" },
{ 0x03, 0x00, "Peripheral Device Write Fault" },
{ 0x03, 0x01, "No Write Current" },
{ 0x03, 0x02, "Excessive Write Errors" },
{ 0x04, 0x00, "Logical Unit Not Ready, Cause Not Reportable" },
{ 0x04, 0x01, "Logical Unit Is in Process Of Becoming Ready" },
{ 0x04, 0x02, "Logical Unit Not Ready, Initialization Command Required" },
{ 0x04, 0x03, "Logical Unit Not Ready, Manual Intervention Required" },
{ 0x04, 0x04, "Logical Unit Not Ready, Format In Progress" },
{ 0x04, 0x05, "Logical Unit Not Ready, Rebuild In Progress" },
{ 0x04, 0x06, "Logical Unit Not Ready, Recalculation In Progress" },
{ 0x04, 0x07, "Logical Unit Not Ready, Operation In Progress" },
{ 0x04, 0x08, "Logical Unit Not Ready, Long Write In Progress" },
{ 0x04, 0x09, "Logical Unit Not Ready, Self-Test In Progress" },
{ 0x04, 0x0A, "Logical Unit Not Accessible, Asymmetric Access State Transition" },
{ 0x04, 0x0B, "Logical Unit Not Accessible, Target Port In Standby State" },
{ 0x04, 0x0C, "Logical Unit Not Accessible, Target Port In Unavailable State" },
{ 0x04, 0x0D, "Logical Unit Not Ready, Structure Check Required" },
{ 0x04, 0x10, "Logical Unit Not Ready, Auxiliary Memory Not Accessible" },
{ 0x04, 0x11, "Logical Unit Not Ready, Notify (Enable Spinup) Required" },
{ 0x04, 0x12, "Logical Unit Not Ready, Offline" },
{ 0x04, 0x13, "Logical Unit Not Ready, SA Creation In Progress" },
{ 0x04, 0x14, "Logical Unit Not Ready, Space Allocation In Progress" },
{ 0x04, 0x15, "Logical Unit Not Ready, Robotics Disabled" },
{ 0x04, 0x16, "Logical Unit Not Ready, Configuration Required" },
{ 0x04, 0x17, "Logical Unit Not Ready, Calibration Required" },
{ 0x04, 0x18, "Logical Unit Not Ready, A Door Is Open" },
{ 0x04, 0x19, "Logical Unit Not Ready, Operating In Sequential Mode" },
{ 0x04, 0x1A, "Logical Unit Not Ready, Start Stop Unit Command In Progress" },
{ 0x05, 0x00, "Logical Unit Does Not Respond To Selection" },
{ 0x06, 0x00, "No Reference Position Found" },
{ 0x07, 0x00, "Multiple Peripheral Devices Selected" },
{ 0x08, 0x00, "Logical Unit Communication Failure" },
{ 0x08, 0x01, "Logical Unit Communication Timeout" },
{ 0x08, 0x02, "Logical Unit Communication Parity Error" },
{ 0x08, 0x03, "Logical Unit Communication CRC Error (ULTRA-DMA/32)" },
{ 0x08, 0x04, "Unreachable Copy Target" },
{ 0x09, 0x00, "Track Following Error" },
{ 0x09, 0x01, "Tracking Servo Failure" },
{ 0x09, 0x02, "Focus Servo Failure" },
{ 0x09, 0x03, "Spindle Servo Failure" },
{ 0x09, 0x04, "Head Select Fault" },
{ 0x0A, 0x00, "Error Log Overflow" },
{ 0x0B, 0x00, "Warning" },
{ 0x0B, 0x01, "Warning - Specified Temperature Exceeded" },
{ 0x0B, 0x02, "Warning - Enclosure Degraded" },
{ 0x0B, 0x03, "Warning - Background Self-Test Failed" },
{ 0x0B, 0x04, "Warning - Background Pre-Scan Detected Medium Error" },
{ 0x0B, 0x05, "Warning - Background Medium Scan Detected Medium Error" },
{ 0x0B, 0x06, "Warning - Non-Volatile Cache Now Volatile" },
{ 0x0B, 0x07, "Warning - Degraded Power To Non-Volatile Cache" },
{ 0x0B, 0x08, "Warning - Power Loss Expected" },
{ 0x0C, 0x00, "Write Error" },
{ 0x0C, 0x01, "Write Error Recovered with Auto Reallocation" },
{ 0x0C, 0x02, "Write Error - Auto Reallocate Failed" },
{ 0x0C, 0x03, "Write Error - Recommend Reassignment" },
{ 0x0C, 0x04, "Compression Check Miscompare Error" },
{ 0x0C, 0x05, "Data Expansion Occurred During Compression" },
{ 0x0C, 0x06, "Block Not Compressible" },
{ 0x0C, 0x07, "Write Error - Recovery Needed" },
{ 0x0C, 0x08, "Write Error - Recovery Failed" },
{ 0x0C, 0x09, "Write Error - Loss Of Streaming" },
{ 0x0C, 0x0A, "Write Error - Padding Blocks Added" },
{ 0x0C, 0x0B, "Auxiliary Memory Write Error" },
{ 0x0C, 0x0C, "Write Error - Unexpected Unsolicited Data" },
{ 0x0C, 0x0D, "Write Error - Not Enough Unsolicited Data" },
{ 0x0C, 0x0F, "Defects In Error Window" },
{ 0x0D, 0x00, "Error Detected By Third Party Temporary Initiator" },
{ 0x0D, 0x01, "Third Party Device Failure" },
{ 0x0D, 0x02, "Copy Target Device Not Reachable" },
{ 0x0D, 0x03, "Incorrect Copy Target Device Type" },
{ 0x0D, 0x04, "Copy Target Device Data Underrun" },
{ 0x0D, 0x05, "Copy Target Device Data Overrun" },
{ 0x0E, 0x00, "Invalid Information Unit" },
{ 0x0E, 0x01, "Information Unit Too Short" },
{ 0x0E, 0x02, "Information Unit Too Long" },
{ 0x10, 0x00, "ID CRC Or ECC Error" },
{ 0x10, 0x01, "Logical Block Guard Check Failed" },
{ 0x10, 0x02, "Logical Block Application Tag Check Failed" },
{ 0x10, 0x03, "Logical Block Reference Tag Check Failed" },
{ 0x10, 0x04, "Logical Block Protection Error On Recover Buffered Data" },
{ 0x10, 0x05, "Logical Block Protection Method Error" },
{ 0x11, 0x00, "Unrecovered Read Error" },
{ 0x11, 0x01, "Read Retries Exhausted" },
{ 0x11, 0x02, "Error Too Long To Correct" },
{ 0x11, 0x03, "Multiple Read Errors" },
{ 0x11, 0x04, "Unrecovered Read Error - Auto Reallocate Failed" },
{ 0x11, 0x05, "L-EC Uncorrectable Error" },
{ 0x11, 0x06, "CIRC Unrecovered Error" },
{ 0x11, 0x07, "Data Resynchronization Error" },
{ 0x11, 0x08, "Incomplete Block Read" },
{ 0x11, 0x09, "No Gap Found" },
{ 0x11, 0x0A, "Miscorrected Error" },
{ 0x11, 0x0B, "Uncorrected Read Error - Recommend Reassignment" },
{ 0x11, 0x0C, "Uncorrected Read Error - Recommend Rewrite The Data" },
{ 0x11, 0x0D, "De-Compression CRC Error" },
{ 0x11, 0x0E, "Cannot Decompress Using Declared Algorithm" },
{ 0x11, 0x0F, "Error Reading UPC/EAN Number" },
{ 0x11, 0x10, "Error Reading ISRC Number" },
{ 0x11, 0x11, "Read Error - Loss Of Streaming" },
{ 0x11, 0x12, "Auxiliary Memory Read Error" },
{ 0x11, 0x13, "Read Error - Failed Retransmission Request" },
{ 0x11, 0x14, "Read Error - LBA Marked Bad By Application Client" },
{ 0x12, 0x00, "Address Mark Not Found for ID Field" },
{ 0x13, 0x00, "Address Mark Not Found for Data Field" },
{ 0x14, 0x00, "Recorded Entity Not Found" },
{ 0x14, 0x01, "Record Not Found" },
{ 0x14, 0x02, "Filemark or Setmark Not Found" },
{ 0x14, 0x03, "End-Of-Data Not Found" },
{ 0x14, 0x04, "Block Sequence Error" },
{ 0x14, 0x05, "Record Not Found - Recommend Reassignment" },
{ 0x14, 0x06, "Record Not Found - Data Auto-Reallocated" },
{ 0x14, 0x07, "Locate Operation Failure" },
{ 0x15, 0x00, "Random Positioning Error" },
{ 0x15, 0x01, "Mechanical Positioning Error" },
{ 0x15, 0x02, "Positioning Error Detected By Read of Medium" },
{ 0x16, 0x00, "Data Synchronization Mark Error" },
{ 0x16, 0x01, "Data Sync Error - Data Rewritten" },
{ 0x16, 0x02, "Data Sync Error - Recommend Rewrite" },
{ 0x16, 0x03, "Data Sync Error - Data Auto-Reallocated" },
{ 0x16, 0x04, "Data Sync Error - Recommend Reassignment" },
{ 0x17, 0x00, "Recovered Data With No Error Correction Applied" },
{ 0x17, 0x01, "Recovered Data With Retries" },
{ 0x17, 0x02, "Recovered Data With Positive Head Offset" },
{ 0x17, 0x03, "Recovered Data With Negative Head Offset" },
{ 0x17, 0x04, "Recovered Data With Retries and/or CIRC Applied" },
{ 0x17, 0x05, "Recovered Data Using Previous Sector ID" },
{ 0x17, 0x06, "Recovered Data Without ECC - Data Auto-Reallocated" },
{ 0x17, 0x07, "Recovered Data Without ECC - Recommend Reassignment" },
{ 0x17, 0x08, "Recovered Data Without ECC - Recommend Rewrite" },
{ 0x17, 0x09, "Recovered Data Without ECC - Data Rewritten" },
{ 0x18, 0x00, "Recovered Data With Error Correction Applied" },
{ 0x18, 0x01, "Recovered Data With Error Correction & Retries Applied" },
{ 0x18, 0x02, "Recovered Data - Data Auto-Reallocated" },
{ 0x18, 0x03, "Recovered Data With CIRC" },
{ 0x18, 0x04, "Recovered Data With L-EC" },
{ 0x18, 0x05, "Recovered Data - Recommend Reassignment" },
{ 0x18, 0x06, "Recovered Data - Recommend Rewrite" },
{ 0x18, 0x07, "Recovered Data With ECC - Data Rewritten" },
{ 0x18, 0x08, "Recovered Data With Linking" },
{ 0x19, 0x00, "Defect List Error" },
{ 0x19, 0x01, "Defect List Not Available" },
{ 0x19, 0x02, "Defect List Error in Primary List" },
{ 0x19, 0x03, "Defect List Error in Grown List" },
{ 0x1A, 0x00, "Parameter List Length Error" },
{ 0x1B, 0x00, "Synchronous Data Transfer Error" },
{ 0x1C, 0x00, "Defect List Not Found" },
{ 0x1C, 0x01, "Primary Defect List Not Found" },
{ 0x1C, 0x02, "Grown Defect List Not Found" },
{ 0x1D, 0x00, "Miscompare During Verify Operation" },
{ 0x1D, 0x01, "Miscompare Verify Of Unmapped Lba" },
{ 0x1E, 0x00, "Recovered ID with ECC" },
{ 0x1F, 0x00, "Partial Defect List Transfer" },
{ 0x20, 0x00, "Invalid Command Operation Code" },
{ 0x20, 0x01, "Access Denied - Initiator Pending-Enrolled" },
{ 0x20, 0x02, "Access Denied - No Access rights" },
{ 0x20, 0x03, "Access Denied - Invalid Mgmt ID Key" },
{ 0x20, 0x04, "Illegal Command While In Write Capable State" },
{ 0x20, 0x05, "Obsolete" },
{ 0x20, 0x06, "Illegal Command While In Explicit Address Mode" },
{ 0x20, 0x07, "Illegal Command While In Implicit Address Mode" },
{ 0x20, 0x08, "Access Denied - Enrollment Conflict" },
{ 0x20, 0x09, "Access Denied - Invalid LU Identifier" },
{ 0x20, 0x0A, "Access Denied - Invalid Proxy Token" },
{ 0x20, 0x0B, "Access Denied - ACL LUN Conflict" },
{ 0x20, 0x0C, "Illegal Command When Not In Append-Only Mode" },
{ 0x21, 0x00, "Logical Block Address Out of Range" },
{ 0x21, 0x01, "Invalid Element Address" },
{ 0x21, 0x02, "Invalid Address For Write" },
{ 0x21, 0x03, "Invalid Write Crossing Layer Jump" },
{ 0x22, 0x00, "Illegal Function (Should 20 00, 24 00, or 26 00)" },
{ 0x24, 0x00, "Illegal Field in CDB" },
{ 0x24, 0x01, "CDB Decryption Error" },
{ 0x24, 0x02, "Obsolete" },
{ 0x24, 0x03, "Obsolete" },
{ 0x24, 0x04, "Security Audit Value Frozen" },
{ 0x24, 0x05, "Security Working Key Frozen" },
{ 0x24, 0x06, "Nonce Not Unique" },
{ 0x24, 0x07, "Nonce Timestamp Out Of Range" },
{ 0x24, 0x08, "Invalid XCDB" },
{ 0x25, 0x00, "Logical Unit Not Supported" },
{ 0x26, 0x00, "Invalid Field In Parameter List" },
{ 0x26, 0x01, "Parameter Not Supported" },
{ 0x26, 0x02, "Parameter Value Invalid" },
{ 0x26, 0x03, "Threshold Parameters Not Supported" },
{ 0x26, 0x04, "Invalid Release Of Persistent Reservation" },
{ 0x26, 0x05, "Data Decryption Error" },
{ 0x26, 0x06, "Too Many Target Descriptors" },
{ 0x26, 0x07, "Unsupported Target Descriptor Type Code" },
{ 0x26, 0x08, "Too Many Segment Descriptors" },
{ 0x26, 0x09, "Unsupported Segment Descriptor Type Code" },
{ 0x26, 0x0A, "Unexpected Inexact Segment" },
{ 0x26, 0x0B, "Inline Data Length Exceeded" },
{ 0x26, 0x0C, "Invalid Operation For Copy Source Or Destination" },
{ 0x26, 0x0D, "Copy Segment Granularity Violation" },
{ 0x26, 0x0E, "Invalid Parameter While Port Is Enabled" },
{ 0x26, 0x0F, "Invalid Data-Out Buffer Integrity Check Value" },
{ 0x26, 0x10, "Data Decryption Key Fail Limit Reached" },
{ 0x26, 0x11, "Incomplete Key-Associated Data Set" },
{ 0x26, 0x12, "Vendor Specific Key Reference Not Found" },
{ 0x27, 0x00, "Write Protected" },
{ 0x27, 0x01, "Hardware Write Protected" },
{ 0x27, 0x02, "Logical Unit Software Write Protected" },
{ 0x27, 0x03, "Associated Write Protect" },
{ 0x27, 0x04, "Persistent Write Protect" },
{ 0x27, 0x05, "Permanent Write Protect" },
{ 0x27, 0x06, "Conditional Write Protect" },
{ 0x27, 0x07, "Space Allocation Failed Write Protect" },
{ 0x28, 0x00, "Not Ready To Ready Transition (Medium May Have Changed)" },
{ 0x28, 0x01, "Import Or Export Element Accessed" },
{ 0x28, 0x02, "Format-Layer May Have Changed" },
{ 0x28, 0x03, "Import/Export Element Accessed, Medium Changed" },
{ 0x29, 0x00, "Power On, Reset, or Bus Device Reset Occurred" },
{ 0x29, 0x01, "Power On Occurred" },
{ 0x29, 0x02, "SCSI Bus Reset Occurred" },
{ 0x29, 0x03, "Bus Device Reset Function Occurred" },
{ 0x29, 0x04, "Device Internal Reset" },
{ 0x29, 0x05, "Transceiver Mode Changed to Single Ended" },
{ 0x29, 0x06, "Transceiver Mode Changed to LVD" },
{ 0x29, 0x07, "I_T Nexus Loss Occurred" },
{ 0x2A, 0x00, "Parameters Changed" },
{ 0x2A, 0x01, "Mode Parameters Changed" },
{ 0x2A, 0x02, "Log Parameters Changed" },
{ 0x2A, 0x03, "Reservations Preempted" },
{ 0x2A, 0x04, "Reservations Released" },
{ 0x2A, 0x05, "Registrations Preempted" },
{ 0x2A, 0x06, "Asymmetric Access State Changed" },
{ 0x2A, 0x07, "Implicit Asymmetric Access State Transition Failed" },
{ 0x2A, 0x08, "Priority Changed" },
{ 0x2A, 0x09, "Capacity Data Has Changed" },
{ 0x2A, 0x0A, "Error History I_T Nexus Cleared" },
{ 0x2A, 0x0B, "Error History Snapshot Released" },
{ 0x2A, 0x0C, "Error Recovery Attributes Have Changed" },
{ 0x2A, 0x0D, "Data Encryption Capabilities Changed" },
{ 0x2A, 0x10, "Timestamp Changed" },
{ 0x2A, 0x11, "Data Encryption Parameters Changed By Another I_T Nexus" },
{ 0x2A, 0x12, "Data Encryption Parameters Changed By Vendor Specific Event" },
{ 0x2A, 0x13, "Data Encryption Key Instance Counter Has Changed" },
{ 0x2A, 0x14, "SA Creation Capabilities Data Has Changed" },
{ 0x2B, 0x00, "Copy Cannot Execute Since Host Cannot Disconnect" },
{ 0x2C, 0x00, "Command Sequence Error" },
{ 0x2C, 0x01, "Too Many Windows Specified" },
{ 0x2C, 0x02, "Invalid Combination of Windows Specified" },
{ 0x2C, 0x03, "Current Program Area Is Not Empty" },
{ 0x2C, 0x04, "Current Program Area Is Empty" },
{ 0x2C, 0x05, "Illegal Power Condition Request" },
{ 0x2C, 0x06, "Persistent Prevent Conflict" },
{ 0x2C, 0x07, "Previous Busy Status" },
{ 0x2C, 0x08, "Previous Task Set Full Status" },
{ 0x2C, 0x09, "Previous Reservation Conflict Status" },
{ 0x2C, 0x0A, "Partition Or Collection Contains User Objects" },
{ 0x2C, 0x0B, "Not Reserved" },
{ 0x2C, 0x0C, "ORWrite Generation Does Not Match" },
{ 0x2D, 0x00, "Overwrite Error On Update In Place" },
{ 0x2E, 0x00, "Insufficient Time For Operation" },
{ 0x2F, 0x00, "Commands Cleared By Another Initiator" },
{ 0x2F, 0x01, "Commands Cleared By Power Loss Notification" },
{ 0x2F, 0x02, "Commands Cleared By Device Server" },
{ 0x30, 0x00, "Incompatible Medium Installed" },
{ 0x30, 0x01, "Cannot Read Medium - Unknown Format" },
{ 0x30, 0x02, "Cannot Read Medium - Incompatible Format" },
{ 0x30, 0x03, "Cleaning Cartridge Installed" },
{ 0x30, 0x04, "Cannot Write Medium - Unknown Format" },
{ 0x30, 0x05, "Cannot Write Medium - Incompatible Format" },
{ 0x30, 0x06, "Cannot Format Medium - Incompatible Medium" },
{ 0x30, 0x07, "Cleaning Failure" },
{ 0x30, 0x08, "Cannot Write - Application Code Mismatch" },
{ 0x30, 0x09, "Current Session Not Fixated For Append" },
{ 0x30, 0x0A, "Cleaning Request Rejected" },
{ 0x30, 0x10, "Medium Not Formatted" },
{ 0x30, 0x11, "Incompatible Volume Type" },
{ 0x30, 0x12, "Incompatible Volume Qualifier" },
{ 0x30, 0x13, "Cleaning Volume Expired" },
{ 0x31, 0x00, "Medium Format Corrupted" },
{ 0x31, 0x01, "Format Command Failed" },
{ 0x31, 0x02, "Zoned Formatting Failed Due To Spare Linking" },
{ 0x32, 0x00, "No Defect Spare Location Available" },
{ 0x32, 0x01, "Defect List Update Failure" },
{ 0x33, 0x00, "Tape Length Error" },
{ 0x34, 0x00, "Enclosure Failure" },
{ 0x35, 0x00, "Enclosure Services Failure" },
{ 0x35, 0x01, "Unsupported Enclosure Function" },
{ 0x35, 0x02, "Enclosure Services Unavailable" },
{ 0x35, 0x03, "Enclosure Services Transfer Failure" },
{ 0x35, 0x04, "Enclosure Services Transfer Refused" },
{ 0x36, 0x00, "Ribbon, Ink, or Toner Failure" },
{ 0x37, 0x00, "Rounded Parameter" },
{ 0x38, 0x00, "Event Status Notification" },
{ 0x38, 0x02, "ESN - Power Management Class Event" },
{ 0x38, 0x04, "ESN - Media Class Event" },
{ 0x38, 0x06, "ESN - Device Busy Class Event" },
{ 0x39, 0x00, "Saving Parameters Not Supported" },
{ 0x3A, 0x00, "Medium Not Present" },
{ 0x3A, 0x01, "Medium Not Present - Tray Closed" },
{ 0x3A, 0x02, "Medium Not Present - Tray Open" },
{ 0x3A, 0x03, "Medium Not Present - Loadable" },
{ 0x3A, 0x04, "Medium Not Present - Medium Auxiliary Memory Accessible" },
{ 0x3B, 0x00, "Sequential Positioning Error" },
{ 0x3B, 0x01, "Tape Position Error At Beginning-of-Medium" },
{ 0x3B, 0x02, "Tape Position Error At End-of-Medium" },
{ 0x3B, 0x03, "Tape or Electronic Vertical Forms Unit Not Ready" },
{ 0x3B, 0x04, "Slew Failure" },
{ 0x3B, 0x05, "Paper Jam" },
{ 0x3B, 0x06, "Failed To Sense Top-Of-Form" },
{ 0x3B, 0x07, "Failed To Sense Bottom-Of-Form" },
{ 0x3B, 0x08, "Reposition Error" },
{ 0x3B, 0x09, "Read Past End Of Medium" },
{ 0x3B, 0x0A, "Read Past Beginning Of Medium" },
{ 0x3B, 0x0B, "Position Past End Of Medium" },
{ 0x3B, 0x0C, "Position Past Beginning Of Medium" },
{ 0x3B, 0x0D, "Medium Destination Element Full" },
{ 0x3B, 0x0E, "Medium Source Element Empty" },
{ 0x3B, 0x0F, "End Of Medium Reached" },
{ 0x3B, 0x11, "Medium Magazine Not Accessible" },
{ 0x3B, 0x12, "Medium Magazine Removed" },
{ 0x3B, 0x13, "Medium Magazine Inserted" },
{ 0x3B, 0x14, "Medium Magazine Locked" },
{ 0x3B, 0x15, "Medium Magazine Unlocked" },
{ 0x3B, 0x16, "Mechanical Positioning Or Changer Error" },
{ 0x3B, 0x17, "Read Past End Of User Object" },
{ 0x3B, 0x18, "Element Disabled" },
{ 0x3B, 0x19, "Element Enabled" },
{ 0x3B, 0x1A, "Data Transfer Device Removed" },
{ 0x3B, 0x1B, "Data Transfer Device Inserted" },
{ 0x3D, 0x00, "Invalid Bits In IDENTIFY Message" },
{ 0x3E, 0x00, "Logical Unit Has Not Self-Configured Yet" },
{ 0x3E, 0x01, "Logical Unit Failure" },
{ 0x3E, 0x02, "Timeout On Logical Unit" },
{ 0x3E, 0x03, "Logical Unit Failed Self-Test" },
{ 0x3E, 0x04, "Logical Unit Unable To Update Self-Test Log" },
{ 0x3F, 0x00, "Target Operating Conditions Have Changed" },
{ 0x3F, 0x01, "Microcode Has Changed" },
{ 0x3F, 0x02, "Changed Operating Definition" },
{ 0x3F, 0x03, "INQUIRY Data Has Changed" },
{ 0x3F, 0x04, "component Device Attached" },
{ 0x3F, 0x05, "Device Identifier Changed" },
{ 0x3F, 0x06, "Redundancy Group Created Or Modified" },
{ 0x3F, 0x07, "Redundancy Group Deleted" },
{ 0x3F, 0x08, "Spare Created Or Modified" },
{ 0x3F, 0x09, "Spare Deleted" },
{ 0x3F, 0x0A, "Volume Set Created Or Modified" },
{ 0x3F, 0x0B, "Volume Set Deleted" },
{ 0x3F, 0x0C, "Volume Set Deassigned" },
{ 0x3F, 0x0D, "Volume Set Reassigned" },
{ 0x3F, 0x0E, "Reported LUNs Data Has Changed" },
{ 0x3F, 0x0F, "Echo Buffer Overwritten" },
{ 0x3F, 0x10, "Medium Loadable" },
{ 0x3F, 0x11, "Medium Auxiliary Memory Accessible" },
{ 0x3F, 0x12, "iSCSI IP Address Added" },
{ 0x3F, 0x13, "iSCSI IP Address Removed" },
{ 0x3F, 0x14, "iSCSI IP Address Changed" },
{ 0x40, 0x00, "RAM FAILURE (Should Use 40 NN)" },
/*
* ASC 0x40 also has an ASCQ range from 0x80 to 0xFF.
* 0x40 0xNN DIAGNOSTIC FAILURE ON COMPONENT NN
*/
{ 0x41, 0x00, "Data Path FAILURE (Should Use 40 NN)" },
{ 0x42, 0x00, "Power-On or Self-Test FAILURE (Should Use 40 NN)" },
{ 0x43, 0x00, "Message Error" },
{ 0x44, 0x00, "Internal Target Failure" },
{ 0x44, 0x71, "ATA Device Failed Set Features" },
{ 0x45, 0x00, "Select Or Reselect Failure" },
{ 0x46, 0x00, "Unsuccessful Soft Reset" },
{ 0x47, 0x00, "SCSI Parity Error" },
{ 0x47, 0x01, "Data Phase CRC Error Detected" },
{ 0x47, 0x02, "SCSI Parity Error Detected During ST Data Phase" },
{ 0x47, 0x03, "Information Unit iuCRC Error Detected" },
{ 0x47, 0x04, "Asynchronous Information Protection Error Detected" },
{ 0x47, 0x05, "Protocol Service CRC Error" },
{ 0x47, 0x06, "PHY Test Function In Progress" },
{ 0x47, 0x7F, "Some Commands Cleared By iSCSI Protocol Event" },
{ 0x48, 0x00, "Initiator Detected Error Message Received" },
{ 0x49, 0x00, "Invalid Message Error" },
{ 0x4A, 0x00, "Command Phase Error" },
{ 0x4B, 0x00, "Data Phase Error" },
{ 0x4B, 0x01, "Invalid Target Port Transfer Tag Received" },
{ 0x4B, 0x02, "Too Much Write Data" },
{ 0x4B, 0x03, "ACK/NAK Timeout" },
{ 0x4B, 0x04, "NAK Received" },
{ 0x4B, 0x05, "Data Offset Error" },
{ 0x4B, 0x06, "Initiator Response Timeout" },
{ 0x4B, 0x07, "Connection Lost" },
{ 0x4C, 0x00, "Logical Unit Failed Self-Configuration" },
/*
* ASC 0x4D has an ASCQ range from 0x00 to 0xFF.
* 0x4D 0xNN TAGGED OVERLAPPED COMMANDS (NN = TASK TAG)
*/
{ 0x4E, 0x00, "Overlapped Commands Attempted" },
{ 0x50, 0x00, "Write Append Error" },
{ 0x50, 0x01, "Write Append Position Error" },
{ 0x50, 0x02, "Position Error Related To Timing" },
{ 0x51, 0x00, "Erase Failure" },
{ 0x51, 0x01, "Erase Failure - Incomplete Erase Operation Detected" },
{ 0x52, 0x00, "Cartridge Fault" },
{ 0x53, 0x00, "Media Load or Eject Failed" },
{ 0x53, 0x01, "Unload Tape Failure" },
{ 0x53, 0x02, "Medium Removal Prevented" },
{ 0x53, 0x03, "Medium Removal Prevented By Data Transfer Element" },
{ 0x53, 0x04, "Medium Thread Or Unthread Failure" },
{ 0x53, 0x05, "Volume Identifier Invalid" },
{ 0x53, 0x06, "Volume Identifier Missing" },
{ 0x53, 0x07, "Duplicate Volume Identifier" },
{ 0x53, 0x08, "Element Status Unknown" },
{ 0x54, 0x00, "SCSI To Host System Interface Failure" },
{ 0x55, 0x00, "System Resource Failure" },
{ 0x55, 0x01, "System Buffer Full" },
{ 0x55, 0x02, "Insufficient Reservation Resources" },
{ 0x55, 0x03, "Insufficient Resources" },
{ 0x55, 0x04, "Insufficient Registration Resources" },
{ 0x55, 0x05, "Insufficient Access Control Resources" },
{ 0x55, 0x06, "Auxiliary Memory Out Of Space" },
{ 0x55, 0x07, "Quota Error" },
{ 0x55, 0x08, "Maximum Number Of Supplemental Decryption Keys Exceeded" },
{ 0x55, 0x09, "Medium Auxiliary Memory Not Accessible" },
{ 0x55, 0x0A, "Data Currently Unavailable" },
{ 0x55, 0x0B, "Insufficient Power For Operation" },
{ 0x57, 0x00, "Unable To Recover Table-Of-Contents" },
{ 0x58, 0x00, "Generation Does Not Exist" },
{ 0x59, 0x00, "Updated Block Read" },
{ 0x5A, 0x00, "Operator Request or State Change Input" },
{ 0x5A, 0x01, "Operator Medium Removal Requested" },
{ 0x5A, 0x02, "Operator Selected Write Protect" },
{ 0x5A, 0x03, "Operator Selected Write Permit" },
{ 0x5B, 0x00, "Log Exception" },
{ 0x5B, 0x01, "Threshold Condition Met" },
{ 0x5B, 0x02, "Log Counter At Maximum" },
{ 0x5B, 0x03, "Log List Codes Exhausted" },
{ 0x5C, 0x00, "RPL Status Change" },
{ 0x5C, 0x01, "Spindles Synchronized" },
{ 0x5C, 0x02, "Spindles Not Synchronized" },
{ 0x5D, 0x00, "Failure Prediction Threshold Exceeded" },
{ 0x5D, 0x01, "Media Failure Prediction Threshold Exceeded" },
{ 0x5D, 0x02, "Logical Unit Failure Prediction Threshold Exceeded" },
{ 0x5D, 0x03, "Spare Area Exhaustion Prediction Threshold Exceeded" },
{ 0x5D, 0x10, "Hardware Impending Failure General Hard Drive Failure" },
{ 0x5D, 0x11, "Hardware Impending Failure Drive Error Rate Too High" },
{ 0x5D, 0x12, "Hardware Impending Failure Data Error Rate Too High" },
{ 0x5D, 0x13, "Hardware Impending Failure Seek Error Rate Too High" },
{ 0x5D, 0x14, "Hardware Impending Failure Too Many Block Reassigns" },
{ 0x5D, 0x15, "Hardware Impending Failure Access Times Too High" },
{ 0x5D, 0x16, "Hardware Impending Failure Start Unit Times Too High" },
{ 0x5D, 0x17, "Hardware Impending Failure Channel Parametrics" },
{ 0x5D, 0x18, "Hardware Impending Failure Controller Detected" },
{ 0x5D, 0x19, "Hardware Impending Failure Throughput Performance" },
{ 0x5D, 0x1A, "Hardware Impending Failure Seek Time Performance" },
{ 0x5D, 0x1B, "Hardware Impending Failure Spin-Up Retry Count" },
{ 0x5D, 0x1C, "Hardware Impending Failure Drive Calibration Retry Count" },
{ 0x5D, 0x20, "Controller Impending Failure General Hard Drive Failure" },
{ 0x5D, 0x21, "Controller Impending Failure Drive Error Rate Too High" },
{ 0x5D, 0x22, "Controller Impending Failure Data Error Rate Too High" },
{ 0x5D, 0x23, "Controller Impending Failure Seek Error Rate Too High" },
{ 0x5D, 0x24, "Controller Impending Failure Too Many Block Reassigns" },
{ 0x5D, 0x25, "Controller Impending Failure Access Times Too High" },
{ 0x5D, 0x26, "Controller Impending Failure Start Unit Times Too High" },
{ 0x5D, 0x27, "Controller Impending Failure Channel Parametrics" },
{ 0x5D, 0x28, "Controller Impending Failure Controller Detected" },
{ 0x5D, 0x29, "Controller Impending Failure Throughput Performance" },
{ 0x5D, 0x2A, "Controller Impending Failure Seek Time Performance" },
{ 0x5D, 0x2B, "Controller Impending Failure Spin-Up Retry Count" },
{ 0x5D, 0x2C, "Controller Impending Failure Drive Calibration Retry Count" },
{ 0x5D, 0x30, "Data Channel Impending Failure General Hard Drive Failure" },
{ 0x5D, 0x31, "Data Channel Impending Failure Drive Error Rate Too High" },
{ 0x5D, 0x32, "Data Channel Impending Failure Data Error Rate Too High" },
{ 0x5D, 0x33, "Data Channel Impending Failure Seek Error Rate Too High" },
{ 0x5D, 0x34, "Data Channel Impending Failure Too Many Block Reassigns" },
{ 0x5D, 0x35, "Data Channel Impending Failure Access Times Too High" },
{ 0x5D, 0x36, "Data Channel Impending Failure Start Unit Times Too High" },
{ 0x5D, 0x37, "Data Channel Impending Failure Channel Parametrics" },
{ 0x5D, 0x38, "Data Channel Impending Failure Controller Detected" },
{ 0x5D, 0x39, "Data Channel Impending Failure Throughput Performance" },
{ 0x5D, 0x3A, "Data Channel Impending Failure Seek Time Performance" },
{ 0x5D, 0x3B, "Data Channel Impending Failure Spin-Up Retry Count" },
{ 0x5D, 0x3C, "Data Channel Impending Failure Drive Calibration Retry Count" },
{ 0x5D, 0x40, "Servo Impending Failure General Hard Drive Failure" },
{ 0x5D, 0x41, "Servo Impending Failure Drive Error Rate Too High" },
{ 0x5D, 0x42, "Servo Impending Failure Data Error Rate Too High" },
{ 0x5D, 0x43, "Servo Impending Failure Seek Error Rate Too High" },
{ 0x5D, 0x44, "Servo Impending Failure Too Many Block Reassigns" },
{ 0x5D, 0x45, "Servo Impending Failure Access Times Too High" },
{ 0x5D, 0x46, "Servo Impending Failure Start Unit Times Too High" },
{ 0x5D, 0x47, "Servo Impending Failure Channel Parametrics" },
{ 0x5D, 0x48, "Servo Impending Failure Controller Detected" },
{ 0x5D, 0x49, "Servo Impending Failure Throughput Performance" },
{ 0x5D, 0x4A, "Servo Impending Failure Seek Time Performance" },
{ 0x5D, 0x4B, "Servo Impending Failure Spin-Up Retry Count" },
{ 0x5D, 0x4C, "Servo Impending Failure Drive Calibration Retry Count" },
{ 0x5D, 0x50, "Spindle Impending Failure General Hard Drive Failure" },
{ 0x5D, 0x51, "Spindle Impending Failure Drive Error Rate Too High" },
{ 0x5D, 0x52, "Spindle Impending Failure Data Error Rate Too High" },
{ 0x5D, 0x53, "Spindle Impending Failure Seek Error Rate Too High" },
{ 0x5D, 0x54, "Spindle Impending Failure Too Many Block Reassigns" },
{ 0x5D, 0x55, "Spindle Impending Failure Access Times Too High" },
{ 0x5D, 0x56, "Spindle Impending Failure Start Unit Times Too High" },
{ 0x5D, 0x57, "Spindle Impending Failure Channel Parametrics" },
{ 0x5D, 0x58, "Spindle Impending Failure Controller Detected" },
{ 0x5D, 0x59, "Spindle Impending Failure Throughput Performance" },
{ 0x5D, 0x5A, "Spindle Impending Failure Seek Time Performance" },
{ 0x5D, 0x5B, "Spindle Impending Failure Spin-Up Retry Count" },
{ 0x5D, 0x5C, "Spindle Impending Failure Drive Calibration Retry Count" },
{ 0x5D, 0x60, "Firmware Impending Failure General Hard Drive Failure" },
{ 0x5D, 0x61, "Firmware Impending Failure Drive Error Rate Too High" },
{ 0x5D, 0x62, "Firmware Impending Failure Data Error Rate Too High" },
{ 0x5D, 0x63, "Firmware Impending Failure Seek Error Rate Too High" },
{ 0x5D, 0x64, "Firmware Impending Failure Too Many Block Reassigns" },
{ 0x5D, 0x65, "Firmware Impending Failure Access Times Too High" },
{ 0x5D, 0x66, "Firmware Impending Failure Start Unit Times Too High" },
{ 0x5D, 0x67, "Firmware Impending Failure Channel Parametrics" },
{ 0x5D, 0x68, "Firmware Impending Failure Controller Detected" },
{ 0x5D, 0x69, "Firmware Impending Failure Throughput Performance" },
{ 0x5D, 0x6A, "Firmware Impending Failure Seek Time Performance" },
{ 0x5D, 0x6B, "Firmware Impending Failure Spin-Up Retry Count" },
{ 0x5D, 0x6C, "Firmware Impending Failure Drive Calibration Retry Count" },
{ 0x5D, 0xFF, "Failure Prediction Threshold Exceeded (false)" },
{ 0x5E, 0x00, "Low Power Condition On" },
{ 0x5E, 0x01, "Idle Condition Activated By Timer" },
{ 0x5E, 0x02, "Standby Condition Activated By Timer" },
{ 0x5E, 0x03, "Idle Condition Activated By Command" },
{ 0x5E, 0x04, "Standby Condition Activated By Command" },
{ 0x5E, 0x05, "IDLE_B Condition Activated By Timer" },
{ 0x5E, 0x06, "IDLE_B Condition Activated By Command" },
{ 0x5E, 0x07, "IDLE_C Condition Activated By Timer" },
{ 0x5E, 0x08, "IDLE_C Condition Activated By Command" },
{ 0x5E, 0x09, "STANDBY_Y Condition Activated By Timer" },
{ 0x5E, 0x0A, "STANDBY_Y Condition Activated By Command" },
{ 0x5E, 0x41, "Power State Change To Active" },
{ 0x5E, 0x42, "Power State Change To Idle" },
{ 0x5E, 0x43, "Power State Change To Standby" },
{ 0x5E, 0x45, "Power State Change To Sleep" },
{ 0x5E, 0x47, "Power State Change To Device Control" },
{ 0x60, 0x00, "Lamp Failure" },
{ 0x61, 0x00, "Video Acquisition Error" },
{ 0x61, 0x01, "Unable To Acquire Video" },
{ 0x61, 0x02, "Out Of Focus" },
{ 0x62, 0x00, "Scan Head Positioning Error" },
{ 0x63, 0x00, "End Of User Area Encountered On This Track" },
{ 0x63, 0x01, "Packet Does Not Fit In Available Space" },
{ 0x64, 0x00, "Illegal Mode For This Track" },
{ 0x64, 0x01, "Invalid Packet Size" },
{ 0x65, 0x00, "Voltage Fault" },
{ 0x66, 0x00, "Automatic Document Feeder Cover Up" },
{ 0x66, 0x01, "Automatic Document Feeder Lift Up" },
{ 0x66, 0x02, "Document Jam In Automatic Document Feeder" },
{ 0x66, 0x03, "Document Miss Feed Automatic In Document Feeder" },
{ 0x67, 0x00, "Configuration Failure" },
{ 0x67, 0x01, "Configuration Of Incapable Logical Units Failed" },
{ 0x67, 0x02, "Add Logical Unit Failed" },
{ 0x67, 0x03, "Modification Of Logical Unit Failed" },
{ 0x67, 0x04, "Exchange Of Logical Unit Failed" },
{ 0x67, 0x05, "Remove Of Logical Unit Failed" },
{ 0x67, 0x06, "Attachment Of Logical Unit Failed" },
{ 0x67, 0x07, "Creation Of Logical Unit Failed" },
{ 0x67, 0x08, "Assign Failure Occurred" },
{ 0x67, 0x09, "Multiply Assigned Logical Unit" },
{ 0x67, 0x0A, "Set Target Port Groups Command Failed" },
{ 0x67, 0x0B, "ATA Device Feature Not Enabled" },
{ 0x68, 0x00, "Logical Unit Not Configured" },
{ 0x69, 0x00, "Data Loss On Logical Unit" },
{ 0x69, 0x01, "Multiple Logical Unit Failures" },
{ 0x69, 0x02, "Parity/Data Mismatch" },
{ 0x6A, 0x00, "Informational, Refer To Log" },
{ 0x6B, 0x00, "State Change Has Occurred" },
{ 0x6B, 0x01, "Redundancy Level Got Better" },
{ 0x6B, 0x02, "Redundancy Level Got Worse" },
{ 0x6C, 0x00, "Rebuild Failure Occurred" },
{ 0x6D, 0x00, "Recalculate Failure Occurred" },
{ 0x6E, 0x00, "Command To Logical Unit Failed" },
{ 0x6F, 0x00, "Copy Protection Key Exchange Failure - Authentication Failure" },
{ 0x6F, 0x01, "Copy Protection Key Exchange Failure - Key Not Present" },
{ 0x6F, 0x02, "Copy Protection Key Exchange Failure - Key Not Established" },
{ 0x6F, 0x03, "Read Of Scrambled Sector Without Authentication" },
{ 0x6F, 0x04, "Media Region Code Is Mismatched To Logical Unit Region" },
{ 0x6F, 0x05, "Drive Region Must Be Permanent/Region Reset Count Error" },
/*
* ASC 0x70 has an ASCQ range from 0x00 to 0xFF.
* 0x70 0xNN DECOMPRESSION EXCEPTION SHORT ALGORITHM ID Of NN
*/
{ 0x71, 0x00, "Decompression Exception Long Algorithm ID" },
{ 0x72, 0x00, "Session Fixation Error" },
{ 0x72, 0x01, "Session Fixation Error Writing Lead-In" },
{ 0x72, 0x02, "Session Fixation Error Writing Lead-Out" },
{ 0x72, 0x03, "Session Fixation Error - Incomplete Track In Session" },
{ 0x72, 0x04, "Empty Or Partially Written Reserved Track" },
{ 0x72, 0x05, "No More Track Reservations Allowed" },
{ 0x72, 0x06, "RMZ Extension Is Not Allowed" },
{ 0x72, 0x07, "No More Test Zone Extensions Are Allowed" },
{ 0x73, 0x00, "CD Control Error" },
{ 0x73, 0x01, "Power Calibration Area Almost Full" },
{ 0x73, 0x02, "Power Calibration Area Is Full" },
{ 0x73, 0x03, "Power Calibration Area Error" },
{ 0x73, 0x04, "Program Memory Area Update Failure" },
{ 0x73, 0x05, "Program Memory Area Is Full" },
{ 0x73, 0x06, "RMA/PMA Is Almost Full" },
{ 0x73, 0x10, "Current Power Calibration Area Almost Full" },
{ 0x73, 0x11, "Current Power Calibration Area Is Full" },
{ 0x73, 0x17, "RDZ Is Full" },
{ 0x74, 0x00, "Security Error" },
{ 0x74, 0x01, "Unable To Decrypt Data" },
{ 0x74, 0x02, "Unencrypted Data Encountered While Decrypting" },
{ 0x74, 0x03, "Incorrect Data Encryption Key" },
{ 0x74, 0x04, "Cryptographic Integrity Validation Failed" },
{ 0x74, 0x05, "Error Decrypting Data" },
{ 0x74, 0x06, "Unknown Signature Verification Key" },
{ 0x74, 0x07, "Encryption Parameters Not Useable" },
{ 0x74, 0x08, "Digital Signature Validation Failure" },
{ 0x74, 0x09, "Encryption Mode Mismatch On Read" },
{ 0x74, 0x0A, "Encrypted Block Not Raw Read Enabled" },
{ 0x74, 0x0B, "Incorrect Encryption Parameters" },
{ 0x74, 0x0C, "Unable To Decrypt Parameter List" },
{ 0x74, 0x0D, "Encryption Algorithm Disabled" },
{ 0x74, 0x10, "SA Creation Parameter Value Invalid" },
{ 0x74, 0x11, "SA Creation Parameter Value Rejected" },
{ 0x74, 0x12, "Invalid SA Usage" },
{ 0x74, 0x21, "Data Encryption Configuration Prevented" },
{ 0x74, 0x30, "SA Creation Parameter Not Supported" },
{ 0x74, 0x40, "Authentication Failed" },
{ 0x74, 0x61, "External Data Encryption Key Manager Access Error" },
{ 0x74, 0x62, "External Data Encryption Key Manager Error" },
{ 0x74, 0x63, "External Data Encryption Key Not Found" },
{ 0x74, 0x64, "External Data Encryption Request Not Authorized" },
{ 0x74, 0x6E, "External Data Encryption Control Timeout" },
{ 0x74, 0x6F, "External Data Encryption Control Error" },
{ 0x74, 0x71, "Logical Unit Access Not Authorized" },
{ 0x74, 0x79, "Security Conflict In Translated Device" },
{ 0x00, 0x00, NULL }
};
static __inline void
asc2ascii(u_int8_t asc, u_int8_t ascq, char *result, size_t len)
{
int i;
/* Check for a dynamically built description. */
switch (asc) {
case 0x40:
if (ascq >= 0x80) {
snprintf(result, len,
"Diagnostic Failure on Component 0x%02x", ascq);
return;
}
break;
case 0x4d:
snprintf(result, len,
"Tagged Overlapped Commands (0x%02x = TASK TAG)", ascq);
return;
case 0x70:
snprintf(result, len,
"Decompression Exception Short Algorithm ID OF 0x%02x",
ascq);
return;
default:
break;
}
/* Check for a fixed description. */
for (i = 0; adesc[i].description != NULL; i++) {
if (adesc[i].asc == asc && adesc[i].ascq == ascq) {
strlcpy(result, adesc[i].description, len);
return;
}
}
/* Just print out the ASC and ASCQ values as a description. */
snprintf(result, len, "ASC 0x%02x ASCQ 0x%02x", asc, ascq);
}
#endif /* SCSITERSE */
void
scsi_print_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
char *sbs;
int32_t info;
u_int8_t serr = sense->error_code & SSD_ERRCODE;
sc_print_addr(xs->sc_link);
/* XXX For error 0x71, current opcode is not the relevant one. */
printf("%sCheck Condition (error %#x) on opcode 0x%x\n",
(serr == SSD_ERRCODE_DEFERRED) ? "DEFERRED " : "", serr,
xs->cmd.opcode);
if (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED) {
if (ISSET(sense->error_code, SSD_ERRCODE_VALID)) {
struct scsi_sense_data_unextended *usense =
(struct scsi_sense_data_unextended *)sense;
printf(" AT BLOCK #: %d (decimal)",
_3btol(usense->block));
}
return;
}
printf(" SENSE KEY: %s\n", scsi_decode_sense(sense,
DECODE_SENSE_KEY));
if (sense->flags & (SSD_FILEMARK | SSD_EOM | SSD_ILI)) {
char pad = ' ';
printf(" ");
if (ISSET(sense->flags, SSD_FILEMARK)) {
printf("%c Filemark Detected", pad);
pad = ',';
}
if (ISSET(sense->flags, SSD_EOM)) {
printf("%c EOM Detected", pad);
pad = ',';
}
if (ISSET(sense->flags, SSD_ILI))
printf("%c Incorrect Length Indicator Set", pad);
printf("\n");
}
/*
* It is inconvenient to use device type to figure out how to
* format the info fields. So print them as 32 bit integers.
*/
info = _4btol(&sense->info[0]);
if (info)
printf(" INFO: 0x%x (VALID flag %s)\n", info,
ISSET(sense->error_code, SSD_ERRCODE_VALID) ? "on" : "off");
if (sense->extra_len < 4)
return;
info = _4btol(&sense->cmd_spec_info[0]);
if (info)
printf(" COMMAND INFO: 0x%x\n", info);
sbs = scsi_decode_sense(sense, DECODE_ASC_ASCQ);
if (strlen(sbs) > 0)
printf(" ASC/ASCQ: %s\n", sbs);
if (sense->fru != 0)
printf(" FRU CODE: 0x%x\n", sense->fru);
sbs = scsi_decode_sense(sense, DECODE_SKSV);
if (strlen(sbs) > 0)
printf(" SKSV: %s\n", sbs);
}
char *
scsi_decode_sense(struct scsi_sense_data *sense, int flag)
{
static char rqsbuf[132];
u_int16_t count;
u_int8_t skey, spec_1;
int len;
bzero(rqsbuf, sizeof(rqsbuf));
skey = sense->flags & SSD_KEY;
spec_1 = sense->sense_key_spec_1;
count = _2btol(&sense->sense_key_spec_2);
switch (flag) {
case DECODE_SENSE_KEY:
strlcpy(rqsbuf, sense_keys[skey], sizeof(rqsbuf));
break;
case DECODE_ASC_ASCQ:
asc2ascii(sense->add_sense_code, sense->add_sense_code_qual,
rqsbuf, sizeof(rqsbuf));
break;
case DECODE_SKSV:
if (sense->extra_len < 9 || !ISSET(spec_1, SSD_SCS_VALID))
break;
switch (skey) {
case SKEY_ILLEGAL_REQUEST:
len = snprintf(rqsbuf, sizeof rqsbuf,
"Error in %s, Offset %d",
ISSET(spec_1, SSD_SCS_CDB_ERROR) ? "CDB" :
"Parameters", count);
if ((len != -1 && len < sizeof rqsbuf) &&
ISSET(spec_1, SSD_SCS_VALID_BIT_INDEX))
snprintf(rqsbuf+len, sizeof rqsbuf - len,
", bit %d", spec_1 & SSD_SCS_BIT_INDEX);
break;
case SKEY_RECOVERED_ERROR:
case SKEY_MEDIUM_ERROR:
case SKEY_HARDWARE_ERROR:
snprintf(rqsbuf, sizeof rqsbuf,
"Actual Retry Count: %d", count);
break;
case SKEY_NOT_READY:
snprintf(rqsbuf, sizeof rqsbuf,
"Progress Indicator: %d", count);
break;
default:
break;
}
break;
default:
break;
}
return rqsbuf;
}
void
scsi_cmd_rw_decode(struct scsi_generic *cmd, u_int64_t *blkno,
u_int32_t *nblks)
{
switch (cmd->opcode) {
case READ_COMMAND:
case WRITE_COMMAND: {
struct scsi_rw *rw = (struct scsi_rw *)cmd;
*blkno = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff);
*nblks = rw->length ? rw->length : 0x100;
break;
}
case READ_10:
case WRITE_10: {
struct scsi_rw_10 *rw10 = (struct scsi_rw_10 *)cmd;
*blkno = _4btol(rw10->addr);
*nblks = _2btol(rw10->length);
break;
}
case READ_12:
case WRITE_12: {
struct scsi_rw_12 *rw12 = (struct scsi_rw_12 *)cmd;
*blkno = _4btol(rw12->addr);
*nblks = _4btol(rw12->length);
break;
}
case READ_16:
case WRITE_16: {
struct scsi_rw_16 *rw16 = (struct scsi_rw_16 *)cmd;
*blkno = _8btol(rw16->addr);
*nblks = _4btol(rw16->length);
break;
}
default:
panic("scsi_cmd_rw_decode: bad opcode 0x%02x", cmd->opcode);
}
}
#ifdef SCSIDEBUG
u_int32_t scsidebug_buses = SCSIDEBUG_BUSES;
u_int32_t scsidebug_targets = SCSIDEBUG_TARGETS;
u_int32_t scsidebug_luns = SCSIDEBUG_LUNS;
int scsidebug_level = SCSIDEBUG_LEVEL;
const char *flagnames[] = {
"REMOVABLE",
"MEDIA LOADED",
"READONLY",
"OPEN",
"DB1",
"DB2",
"DB3",
"DB4",
"EJECTING",
"ATAPI",
"UMASS",
"VIRTUAL",
"OWN_IOPL",
NULL
};
const char *quirknames[] = {
"AUTOSAVE",
"NOSYNC",
"NOWIDE",
"NOTAGS",
"NOSYNCCACHE",
"NOSENSE",
"LITTLETOC",
"NOCAPACITY",
"NODOORLOCK",
NULL
};
const char *devicetypenames[32] = {
"T_DIRECT",
"T_SEQUENTIAL",
"T_PRINTER",
"T_PROCESSOR",
"T_WORM",
"T_CDROM",
"T_SCANNER",
"T_OPTICAL",
"T_CHANGER",
"T_COMM",
"T_ASC0",
"T_ASC1",
"T_STROARRAY",
"T_ENCLOSURE",
"T_RDIRECT",
"T_OCRW",
"T_BCC",
"T_OSD",
"T_ADC",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_RESERVED",
"T_WELL_KNOWN_LU",
"T_NODEVICE"
};
/*
* Print out sense data details.
*/
void
scsi_show_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
struct scsi_link *link = xs->sc_link;
SC_DEBUG(link, SDEV_DB1,
("code:%#x valid:%d key:%#x ili:%d eom:%d fmark:%d extra:%d\n",
sense->error_code & SSD_ERRCODE,
sense->error_code & SSD_ERRCODE_VALID ? 1 : 0,
sense->flags & SSD_KEY,
sense->flags & SSD_ILI ? 1 : 0,
sense->flags & SSD_EOM ? 1 : 0,
sense->flags & SSD_FILEMARK ? 1 : 0,
sense->extra_len));
if (ISSET(xs->sc_link->flags, SDEV_DB1))
scsi_show_mem((u_char *)&xs->sense, sizeof(xs->sense));
scsi_print_sense(xs);
}
/*
* Given a scsi_xfer, dump the request, in all its glory
*/
void
scsi_show_xs(struct scsi_xfer *xs)
{
u_char *b = (u_char *)&xs->cmd;
int i = 0;
if (!ISSET(xs->sc_link->flags, SDEV_DB1))
return;
sc_print_addr(xs->sc_link);
printf("xs (%p): ", xs);
printf("flg(0x%x)", xs->flags);
printf("link(%p)", xs->sc_link);
printf("retr(0x%x)", xs->retries);
printf("timo(0x%x)", xs->timeout);
printf("data(%p)", xs->data);
printf("res(0x%zx)", xs->resid);
printf("err(0x%x)", xs->error);
printf("bp(%p)\n", xs->bp);
sc_print_addr(xs->sc_link);
printf("cmd (%p): ", &xs->cmd);
if (!ISSET(xs->flags, SCSI_RESET)) {
while (i < xs->cmdlen) {
if (i)
printf(",");
printf("%x", b[i++]);
}
printf("-[%d bytes]\n", xs->datalen);
} else
printf("-RESET-\n");
if (xs->datalen && ISSET(xs->flags, SCSI_DATA_OUT))
scsi_show_mem(xs->data, min(64, xs->datalen));
}
void
scsi_show_mem(u_char *address, int num)
{
int x;
printf("------------------------------");
for (x = 0; x < num; x++) {
if ((x % 16) == 0)
printf("\n%03d: ", x);
printf("%02x ", *address++);
}
printf("\n------------------------------\n");
}
void
scsi_show_flags(u_int32_t flags, const char **names)
{
int i, first, exhausted;
u_int32_t unnamed;
printf("<");
for (first = 1, exhausted = 0, unnamed = 0, i = 0; i < 32; i++) {
if (!ISSET(flags, 1 << i))
continue;
if (exhausted == 0 && names[i] == NULL)
exhausted = 1;
if (exhausted || strlen(names[i]) == 0) {
SET(unnamed, 1 << i);
continue;
}
if (first == 0)
printf(", ");
else
first = 0;
printf("%s", names[i]);
}
if (unnamed != 0)
printf("%s0x%08x", first ? "" : ", ", unnamed);
printf(">");
}
void
scsi_show_inquiry_header(struct scsi_inquiry_data *inqbuf)
{
switch (inqbuf->device & SID_QUAL) {
case SID_QUAL_RSVD:
printf("SID_QUAL_RSVD, ");
break;
case SID_QUAL_BAD_LU:
printf("SID_QUAL_BAD_LU, ");
break;
case SID_QUAL_LU_OFFLINE:
printf("SID_QUAL_LU_OFFLINE, ");
break;
case SID_QUAL_LU_OK:
printf("SID_QUAL_LU_OK, ");
break;
default:
printf("SID_QUAL = 0x%02x, ", inqbuf->device & SID_QUAL);
break;
}
printf("%s, ", devicetypenames[inqbuf->device & SID_TYPE]);
if (ISSET(inqbuf->dev_qual2, SID_REMOVABLE))
printf("T_REMOV, ");
else
printf("T_FIXED, ");
printf("SID_ANSII_REV %u, SID_RESPONSE_DATA_FMT %u\n",
SID_ANSII_REV(inqbuf),
inqbuf->response_format & SID_RESPONSE_DATA_FMT);
}
void
scsi_show_inquiry_match(struct scsi_inquiry_data *inqbuf)
{
char visbuf[65];
unsigned int inqbytes;
inqbytes = SID_SCSI2_HDRLEN + inqbuf->additional_length;
printf("<");
if (inqbytes >= offsetof(struct scsi_inquiry_data, product))
scsi_strvis(visbuf, inqbuf->vendor, sizeof(inqbuf->vendor));
else
visbuf[0] = '\0';
printf("\"%s\", ", visbuf);
if (inqbytes >= offsetof(struct scsi_inquiry_data, revision))
scsi_strvis(visbuf, inqbuf->product, sizeof(inqbuf->product));
else
visbuf[0] = '\0';
printf("\"%s\", ", visbuf);
if (inqbytes >= offsetof(struct scsi_inquiry_data, extra))
scsi_strvis(visbuf, inqbuf->revision, sizeof(inqbuf->revision));
else
visbuf[0] = '\0';
printf("\"%s\">\n", visbuf);
}
#endif /* SCSIDEBUG */
3
2
1
1
1
3
2
1
5
2
3
3
3
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
/* $OpenBSD: vscsi.c,v 1.61 2022/07/02 08:50:41 visa Exp $ */
/*
* Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/queue.h>
#include <sys/rwlock.h>
#include <sys/pool.h>
#include <sys/task.h>
#include <sys/ioctl.h>
#include <sys/selinfo.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <dev/vscsivar.h>
int vscsi_match(struct device *, void *, void *);
void vscsi_attach(struct device *, struct device *, void *);
void vscsi_shutdown(void *);
struct vscsi_ccb {
TAILQ_ENTRY(vscsi_ccb) ccb_entry;
int ccb_tag;
struct scsi_xfer *ccb_xs;
size_t ccb_datalen;
};
TAILQ_HEAD(vscsi_ccb_list, vscsi_ccb);
enum vscsi_state {
VSCSI_S_CLOSED,
VSCSI_S_CONFIG,
VSCSI_S_RUNNING
};
struct vscsi_softc {
struct device sc_dev;
struct scsibus_softc *sc_scsibus;
struct mutex sc_state_mtx;
enum vscsi_state sc_state;
u_int sc_ref_count;
struct pool sc_ccb_pool;
struct scsi_iopool sc_iopool;
struct vscsi_ccb_list sc_ccb_i2t;
struct vscsi_ccb_list sc_ccb_t2i;
int sc_ccb_tag;
struct mutex sc_poll_mtx;
struct rwlock sc_ioc_lock;
struct selinfo sc_sel;
struct mutex sc_sel_mtx;
};
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
#define DEV2SC(_d) ((struct vscsi_softc *)device_lookup(&vscsi_cd, minor(_d)))
const struct cfattach vscsi_ca = {
sizeof(struct vscsi_softc),
vscsi_match,
vscsi_attach
};
struct cfdriver vscsi_cd = {
NULL,
"vscsi",
DV_DULL
};
void vscsi_cmd(struct scsi_xfer *);
int vscsi_probe(struct scsi_link *);
void vscsi_free(struct scsi_link *);
const struct scsi_adapter vscsi_switch = {
vscsi_cmd, NULL, vscsi_probe, vscsi_free, NULL
};
int vscsi_i2t(struct vscsi_softc *, struct vscsi_ioc_i2t *);
int vscsi_data(struct vscsi_softc *, struct vscsi_ioc_data *, int);
int vscsi_t2i(struct vscsi_softc *, struct vscsi_ioc_t2i *);
int vscsi_devevent(struct vscsi_softc *, u_long,
struct vscsi_ioc_devevent *);
void vscsi_devevent_task(void *);
void vscsi_done(struct vscsi_softc *, struct vscsi_ccb *);
void * vscsi_ccb_get(void *);
void vscsi_ccb_put(void *, void *);
void filt_vscsidetach(struct knote *);
int filt_vscsiread(struct knote *, long);
const struct filterops vscsi_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_vscsidetach,
.f_event = filt_vscsiread,
};
int
vscsi_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
vscsi_attach(struct device *parent, struct device *self, void *aux)
{
struct vscsi_softc *sc = (struct vscsi_softc *)self;
struct scsibus_attach_args saa;
printf("\n");
mtx_init(&sc->sc_state_mtx, IPL_BIO);
sc->sc_state = VSCSI_S_CLOSED;
TAILQ_INIT(&sc->sc_ccb_i2t);
TAILQ_INIT(&sc->sc_ccb_t2i);
mtx_init(&sc->sc_poll_mtx, IPL_BIO);
mtx_init(&sc->sc_sel_mtx, IPL_BIO);
rw_init(&sc->sc_ioc_lock, "vscsiioc");
scsi_iopool_init(&sc->sc_iopool, sc, vscsi_ccb_get, vscsi_ccb_put);
saa.saa_adapter = &vscsi_switch;
saa.saa_adapter_softc = sc;
saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
saa.saa_adapter_buswidth = 256;
saa.saa_luns = 8;
saa.saa_openings = 16;
saa.saa_pool = &sc->sc_iopool;
saa.saa_quirks = saa.saa_flags = 0;
saa.saa_wwpn = saa.saa_wwnn = 0;
sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
&saa, scsiprint);
}
void
vscsi_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct vscsi_softc *sc = link->bus->sb_adapter_softc;
struct vscsi_ccb *ccb = xs->io;
int polled = ISSET(xs->flags, SCSI_POLL);
int running = 0;
if (ISSET(xs->flags, SCSI_POLL) && ISSET(xs->flags, SCSI_NOSLEEP)) {
printf("%s: POLL && NOSLEEP for 0x%02x\n", DEVNAME(sc),
xs->cmd.opcode);
xs->error = XS_DRIVER_STUFFUP;
scsi_done(xs);
return;
}
ccb->ccb_xs = xs;
mtx_enter(&sc->sc_state_mtx);
if (sc->sc_state == VSCSI_S_RUNNING) {
running = 1;
TAILQ_INSERT_TAIL(&sc->sc_ccb_i2t, ccb, ccb_entry);
}
mtx_leave(&sc->sc_state_mtx);
if (!running) {
xs->error = XS_DRIVER_STUFFUP;
scsi_done(xs);
return;
}
selwakeup(&sc->sc_sel);
if (polled) {
mtx_enter(&sc->sc_poll_mtx);
while (ccb->ccb_xs != NULL)
msleep_nsec(ccb, &sc->sc_poll_mtx, PRIBIO, "vscsipoll",
INFSLP);
mtx_leave(&sc->sc_poll_mtx);
scsi_done(xs);
}
}
void
vscsi_done(struct vscsi_softc *sc, struct vscsi_ccb *ccb)
{
struct scsi_xfer *xs = ccb->ccb_xs;
if (ISSET(xs->flags, SCSI_POLL)) {
mtx_enter(&sc->sc_poll_mtx);
ccb->ccb_xs = NULL;
wakeup(ccb);
mtx_leave(&sc->sc_poll_mtx);
} else
scsi_done(xs);
}
int
vscsi_probe(struct scsi_link *link)
{
struct vscsi_softc *sc = link->bus->sb_adapter_softc;
int rv = 0;
mtx_enter(&sc->sc_state_mtx);
if (sc->sc_state == VSCSI_S_RUNNING)
sc->sc_ref_count++;
else
rv = ENXIO;
mtx_leave(&sc->sc_state_mtx);
return (rv);
}
void
vscsi_free(struct scsi_link *link)
{
struct vscsi_softc *sc = link->bus->sb_adapter_softc;
mtx_enter(&sc->sc_state_mtx);
sc->sc_ref_count--;
if (sc->sc_state != VSCSI_S_RUNNING && sc->sc_ref_count == 0)
wakeup(&sc->sc_ref_count);
mtx_leave(&sc->sc_state_mtx);
}
int
vscsiopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct vscsi_softc *sc = DEV2SC(dev);
enum vscsi_state state = VSCSI_S_RUNNING;
int rv = 0;
if (sc == NULL)
return (ENXIO);
mtx_enter(&sc->sc_state_mtx);
if (sc->sc_state != VSCSI_S_CLOSED)
rv = EBUSY;
else
sc->sc_state = VSCSI_S_CONFIG;
mtx_leave(&sc->sc_state_mtx);
if (rv != 0) {
device_unref(&sc->sc_dev);
return (rv);
}
pool_init(&sc->sc_ccb_pool, sizeof(struct vscsi_ccb), 0, IPL_BIO, 0,
"vscsiccb", NULL);
/* we need to guarantee some ccbs will be available for the iopool */
rv = pool_prime(&sc->sc_ccb_pool, 8);
if (rv != 0) {
pool_destroy(&sc->sc_ccb_pool);
state = VSCSI_S_CLOSED;
}
/* commit changes */
mtx_enter(&sc->sc_state_mtx);
sc->sc_state = state;
mtx_leave(&sc->sc_state_mtx);
device_unref(&sc->sc_dev);
return (rv);
}
int
vscsiioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
{
struct vscsi_softc *sc = DEV2SC(dev);
int read = 0;
int err = 0;
if (sc == NULL)
return (ENXIO);
rw_enter_write(&sc->sc_ioc_lock);
switch (cmd) {
case VSCSI_I2T:
err = vscsi_i2t(sc, (struct vscsi_ioc_i2t *)addr);
break;
case VSCSI_DATA_READ:
read = 1;
case VSCSI_DATA_WRITE:
err = vscsi_data(sc, (struct vscsi_ioc_data *)addr, read);
break;
case VSCSI_T2I:
err = vscsi_t2i(sc, (struct vscsi_ioc_t2i *)addr);
break;
case VSCSI_REQPROBE:
case VSCSI_REQDETACH:
err = vscsi_devevent(sc, cmd,
(struct vscsi_ioc_devevent *)addr);
break;
default:
err = ENOTTY;
break;
}
rw_exit_write(&sc->sc_ioc_lock);
device_unref(&sc->sc_dev);
return (err);
}
int
vscsi_i2t(struct vscsi_softc *sc, struct vscsi_ioc_i2t *i2t)
{
struct vscsi_ccb *ccb;
struct scsi_xfer *xs;
struct scsi_link *link;
mtx_enter(&sc->sc_state_mtx);
ccb = TAILQ_FIRST(&sc->sc_ccb_i2t);
if (ccb != NULL)
TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
mtx_leave(&sc->sc_state_mtx);
if (ccb == NULL)
return (EAGAIN);
xs = ccb->ccb_xs;
link = xs->sc_link;
i2t->tag = ccb->ccb_tag;
i2t->target = link->target;
i2t->lun = link->lun;
memcpy(&i2t->cmd, &xs->cmd, xs->cmdlen);
i2t->cmdlen = xs->cmdlen;
i2t->datalen = xs->datalen;
switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
case SCSI_DATA_IN:
i2t->direction = VSCSI_DIR_READ;
break;
case SCSI_DATA_OUT:
i2t->direction = VSCSI_DIR_WRITE;
break;
default:
i2t->direction = VSCSI_DIR_NONE;
break;
}
TAILQ_INSERT_TAIL(&sc->sc_ccb_t2i, ccb, ccb_entry);
return (0);
}
int
vscsi_data(struct vscsi_softc *sc, struct vscsi_ioc_data *data, int read)
{
struct vscsi_ccb *ccb;
struct scsi_xfer *xs;
int xsread;
u_int8_t *buf;
int rv = EINVAL;
TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
if (ccb->ccb_tag == data->tag)
break;
}
if (ccb == NULL)
return (EFAULT);
xs = ccb->ccb_xs;
if (data->datalen > xs->datalen - ccb->ccb_datalen)
return (ENOMEM);
switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
case SCSI_DATA_IN:
xsread = 1;
break;
case SCSI_DATA_OUT:
xsread = 0;
break;
default:
return (EINVAL);
}
if (read != xsread)
return (EINVAL);
buf = xs->data;
buf += ccb->ccb_datalen;
if (read)
rv = copyin(data->data, buf, data->datalen);
else
rv = copyout(buf, data->data, data->datalen);
if (rv == 0)
ccb->ccb_datalen += data->datalen;
return (rv);
}
int
vscsi_t2i(struct vscsi_softc *sc, struct vscsi_ioc_t2i *t2i)
{
struct vscsi_ccb *ccb;
struct scsi_xfer *xs;
int rv = 0;
TAILQ_FOREACH(ccb, &sc->sc_ccb_t2i, ccb_entry) {
if (ccb->ccb_tag == t2i->tag)
break;
}
if (ccb == NULL)
return (EFAULT);
TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
xs = ccb->ccb_xs;
xs->resid = xs->datalen - ccb->ccb_datalen;
xs->status = SCSI_OK;
switch (t2i->status) {
case VSCSI_STAT_DONE:
xs->error = XS_NOERROR;
break;
case VSCSI_STAT_SENSE:
xs->error = XS_SENSE;
memcpy(&xs->sense, &t2i->sense, sizeof(xs->sense));
break;
case VSCSI_STAT_RESET:
xs->error = XS_RESET;
break;
case VSCSI_STAT_ERR:
default:
xs->error = XS_DRIVER_STUFFUP;
break;
}
vscsi_done(sc, ccb);
return (rv);
}
struct vscsi_devevent_task {
struct vscsi_softc *sc;
struct task t;
struct vscsi_ioc_devevent de;
u_long cmd;
};
int
vscsi_devevent(struct vscsi_softc *sc, u_long cmd,
struct vscsi_ioc_devevent *de)
{
struct vscsi_devevent_task *dt;
dt = malloc(sizeof(*dt), M_TEMP, M_WAITOK | M_CANFAIL);
if (dt == NULL)
return (ENOMEM);
task_set(&dt->t, vscsi_devevent_task, dt);
dt->sc = sc;
dt->de = *de;
dt->cmd = cmd;
device_ref(&sc->sc_dev);
task_add(systq, &dt->t);
return (0);
}
void
vscsi_devevent_task(void *xdt)
{
struct vscsi_devevent_task *dt = xdt;
struct vscsi_softc *sc = dt->sc;
int state;
mtx_enter(&sc->sc_state_mtx);
state = sc->sc_state;
mtx_leave(&sc->sc_state_mtx);
if (state != VSCSI_S_RUNNING)
goto gone;
switch (dt->cmd) {
case VSCSI_REQPROBE:
scsi_probe(sc->sc_scsibus, dt->de.target, dt->de.lun);
break;
case VSCSI_REQDETACH:
scsi_detach(sc->sc_scsibus, dt->de.target, dt->de.lun,
DETACH_FORCE);
break;
#ifdef DIAGNOSTIC
default:
panic("unexpected vscsi_devevent cmd");
/* NOTREACHED */
#endif
}
gone:
device_unref(&sc->sc_dev);
free(dt, M_TEMP, sizeof(*dt));
}
int
vscsikqfilter(dev_t dev, struct knote *kn)
{
struct vscsi_softc *sc = DEV2SC(dev);
struct klist *klist;
if (sc == NULL)
return (ENXIO);
klist = &sc->sc_sel.si_note;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &vscsi_filtops;
break;
default:
device_unref(&sc->sc_dev);
return (EINVAL);
}
kn->kn_hook = sc;
mtx_enter(&sc->sc_sel_mtx);
klist_insert_locked(klist, kn);
mtx_leave(&sc->sc_sel_mtx);
/* device ref is given to the knote in the klist */
return (0);
}
void
filt_vscsidetach(struct knote *kn)
{
struct vscsi_softc *sc = kn->kn_hook;
struct klist *klist = &sc->sc_sel.si_note;
mtx_enter(&sc->sc_sel_mtx);
klist_remove_locked(klist, kn);
mtx_leave(&sc->sc_sel_mtx);
device_unref(&sc->sc_dev);
}
int
filt_vscsiread(struct knote *kn, long hint)
{
struct vscsi_softc *sc = kn->kn_hook;
int event = 0;
mtx_enter(&sc->sc_state_mtx);
if (!TAILQ_EMPTY(&sc->sc_ccb_i2t))
event = 1;
mtx_leave(&sc->sc_state_mtx);
return (event);
}
int
vscsiclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct vscsi_softc *sc = DEV2SC(dev);
struct vscsi_ccb *ccb;
if (sc == NULL)
return (ENXIO);
mtx_enter(&sc->sc_state_mtx);
KASSERT(sc->sc_state == VSCSI_S_RUNNING);
sc->sc_state = VSCSI_S_CONFIG;
mtx_leave(&sc->sc_state_mtx);
scsi_activate(sc->sc_scsibus, -1, -1, DVACT_DEACTIVATE);
while ((ccb = TAILQ_FIRST(&sc->sc_ccb_t2i)) != NULL) {
TAILQ_REMOVE(&sc->sc_ccb_t2i, ccb, ccb_entry);
ccb->ccb_xs->error = XS_RESET;
vscsi_done(sc, ccb);
}
while ((ccb = TAILQ_FIRST(&sc->sc_ccb_i2t)) != NULL) {
TAILQ_REMOVE(&sc->sc_ccb_i2t, ccb, ccb_entry);
ccb->ccb_xs->error = XS_RESET;
vscsi_done(sc, ccb);
}
scsi_req_detach(sc->sc_scsibus, -1, -1, DETACH_FORCE);
mtx_enter(&sc->sc_state_mtx);
while (sc->sc_ref_count > 0) {
msleep_nsec(&sc->sc_ref_count, &sc->sc_state_mtx,
PRIBIO, "vscsiref", INFSLP);
}
mtx_leave(&sc->sc_state_mtx);
pool_destroy(&sc->sc_ccb_pool);
mtx_enter(&sc->sc_state_mtx);
sc->sc_state = VSCSI_S_CLOSED;
mtx_leave(&sc->sc_state_mtx);
device_unref(&sc->sc_dev);
return (0);
}
void *
vscsi_ccb_get(void *cookie)
{
struct vscsi_softc *sc = cookie;
struct vscsi_ccb *ccb = NULL;
ccb = pool_get(&sc->sc_ccb_pool, PR_NOWAIT);
if (ccb != NULL) {
ccb->ccb_tag = sc->sc_ccb_tag++;
ccb->ccb_datalen = 0;
}
return (ccb);
}
void
vscsi_ccb_put(void *cookie, void *io)
{
struct vscsi_softc *sc = cookie;
struct vscsi_ccb *ccb = io;
pool_put(&sc->sc_ccb_pool, ccb);
}
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* $OpenBSD: fido.c,v 1.5 2022/07/02 08:50:42 visa Exp $ */
/*
* Copyright (c) 2019 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "fido.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/uhidev.h>
#include <dev/usb/uhid.h>
int fido_match(struct device *, void *, void *);
struct cfdriver fido_cd = {
NULL, "fido", DV_DULL
};
const struct cfattach fido_ca = {
sizeof(struct uhid_softc),
fido_match,
uhid_attach,
uhid_detach,
};
int
fido_match(struct device *parent, void *match, void *aux)
{
struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
int size;
void *desc;
int ret = UMATCH_NONE;
if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
return (ret);
/* Find the FIDO usage page and U2F collection */
uhidev_get_report_desc(uha->parent, &desc, &size);
if (hid_is_collection(desc, size, uha->reportid,
HID_USAGE2(HUP_FIDO, HUF_U2FHID)))
ret = UMATCH_IFACECLASS;
return (ret);
}
int
fidoopen(dev_t dev, int flag, int mode, struct proc *p)
{
return (uhid_do_open(dev, flag, mode, p));
}
int
fidoioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
int error;
switch (cmd) {
case FIONBIO:
case FIOASYNC:
case USB_GET_DEVICEINFO:
break;
default:
/*
* Users don't need USB/HID ioctl access to fido(4) devices
* but it can still be useful for debugging by root.
*/
if ((error = suser(p)) != 0)
return (error);
break;
}
return (uhidioctl(dev, cmd, addr, flag, p));
}
284
284
1
148
17
111
122
1
3
124
215
90
32
232
1
12
3
1
14
156
156
7
151
3
2
2
3
1
40
1918
1920
1913
2
2
2
1418
125
1
1
5
119
1
1
218
217
183
180
26
160
3
2
1
129
45
143
26
14
6
93
32
1
11
31
3
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
/* $OpenBSD: spec_vnops.c,v 1.109 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: spec_vnops.c,v 1.29 1996/04/22 01:42:38 christos Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)spec_vnops.c 8.8 (Berkeley) 11/21/94
*/
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/disklabel.h>
#include <sys/lockf.h>
#include <sys/dkio.h>
#include <sys/malloc.h>
#include <sys/specdev.h>
#include <sys/unistd.h>
#define v_lastr v_specinfo->si_lastr
int spec_open_clone(struct vop_open_args *);
struct vnodechain speclisth[SPECHSZ];
const struct vops spec_vops = {
.vop_lookup = vop_generic_lookup,
.vop_create = vop_generic_badop,
.vop_mknod = vop_generic_badop,
.vop_open = spec_open,
.vop_close = spec_close,
.vop_access = spec_access,
.vop_getattr = spec_getattr,
.vop_setattr = spec_setattr,
.vop_read = spec_read,
.vop_write = spec_write,
.vop_ioctl = spec_ioctl,
.vop_kqfilter = spec_kqfilter,
.vop_revoke = vop_generic_revoke,
.vop_fsync = spec_fsync,
.vop_remove = vop_generic_badop,
.vop_link = vop_generic_badop,
.vop_rename = vop_generic_badop,
.vop_mkdir = vop_generic_badop,
.vop_rmdir = vop_generic_badop,
.vop_symlink = vop_generic_badop,
.vop_readdir = vop_generic_badop,
.vop_readlink = vop_generic_badop,
.vop_abortop = vop_generic_badop,
.vop_inactive = spec_inactive,
.vop_reclaim = nullop,
.vop_lock = nullop,
.vop_unlock = nullop,
.vop_islocked = nullop,
.vop_bmap = vop_generic_bmap,
.vop_strategy = spec_strategy,
.vop_print = spec_print,
.vop_pathconf = spec_pathconf,
.vop_advlock = spec_advlock,
.vop_bwrite = vop_generic_bwrite,
};
/*
* Open a special file.
*/
int
spec_open(void *v)
{
struct vop_open_args *ap = v;
struct proc *p = ap->a_p;
struct vnode *vp = ap->a_vp;
struct vnode *bvp;
dev_t bdev;
dev_t dev = (dev_t)vp->v_rdev;
int maj = major(dev);
int error;
/*
* Don't allow open if fs is mounted -nodev.
*/
if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
return (ENXIO);
switch (vp->v_type) {
case VCHR:
if ((u_int)maj >= nchrdev)
return (ENXIO);
if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) {
/*
* When running in very secure mode, do not allow
* opens for writing of any disk character devices.
*/
if (securelevel >= 2 && cdevsw[maj].d_type == D_DISK)
return (EPERM);
/*
* When running in secure mode, do not allow opens
* for writing of /dev/mem, /dev/kmem, or character
* devices whose corresponding block devices are
* currently mounted.
*/
if (securelevel >= 1) {
if ((bdev = chrtoblk(dev)) != NODEV &&
vfinddev(bdev, VBLK, &bvp) &&
bvp->v_usecount > 0 &&
(error = vfs_mountedon(bvp)))
return (error);
if (iskmemdev(dev))
return (EPERM);
}
}
if (cdevsw[maj].d_type == D_TTY)
vp->v_flag |= VISTTY;
if (cdevsw[maj].d_flags & D_CLONE)
return (spec_open_clone(ap));
VOP_UNLOCK(vp);
error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, p);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
return (error);
case VBLK:
if ((u_int)maj >= nblkdev)
return (ENXIO);
/*
* When running in very secure mode, do not allow
* opens for writing of any disk block devices.
*/
if (securelevel >= 2 && ap->a_cred != FSCRED &&
(ap->a_mode & FWRITE) && bdevsw[maj].d_type == D_DISK)
return (EPERM);
/*
* Do not allow opens of block devices that are
* currently mounted.
*/
if ((error = vfs_mountedon(vp)) != 0)
return (error);
return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, p));
case VNON:
case VLNK:
case VDIR:
case VREG:
case VBAD:
case VFIFO:
case VSOCK:
break;
}
return (0);
}
/*
* Vnode op for read
*/
int
spec_read(void *v)
{
struct vop_read_args *ap = v;
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct proc *p = uio->uio_procp;
struct buf *bp;
daddr_t bn, nextbn, bscale;
int bsize;
struct partinfo dpart;
size_t n;
int on, majordev;
int (*ioctl)(dev_t, u_long, caddr_t, int, struct proc *);
int error = 0;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("spec_read mode");
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
panic("spec_read proc");
#endif
if (uio->uio_resid == 0)
return (0);
switch (vp->v_type) {
case VCHR:
VOP_UNLOCK(vp);
error = (*cdevsw[major(vp->v_rdev)].d_read)
(vp->v_rdev, uio, ap->a_ioflag);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
return (error);
case VBLK:
if (uio->uio_offset < 0)
return (EINVAL);
bsize = BLKDEV_IOSIZE;
if ((majordev = major(vp->v_rdev)) < nblkdev &&
(ioctl = bdevsw[majordev].d_ioctl) != NULL &&
(*ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) {
u_int32_t frag =
DISKLABELV1_FFS_FRAG(dpart.part->p_fragblock);
u_int32_t fsize =
DISKLABELV1_FFS_FSIZE(dpart.part->p_fragblock);
if (dpart.part->p_fstype == FS_BSDFFS && frag != 0 &&
fsize != 0)
bsize = frag * fsize;
}
bscale = btodb(bsize);
do {
bn = btodb(uio->uio_offset) & ~(bscale - 1);
on = uio->uio_offset % bsize;
n = ulmin((bsize - on), uio->uio_resid);
if (vp->v_lastr + bscale == bn) {
nextbn = bn + bscale;
error = breadn(vp, bn, bsize, &nextbn, &bsize,
1, &bp);
} else
error = bread(vp, bn, bsize, &bp);
vp->v_lastr = bn;
n = ulmin(n, bsize - bp->b_resid);
if (error) {
brelse(bp);
return (error);
}
error = uiomove((char *)bp->b_data + on, n, uio);
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return (error);
default:
panic("spec_read type");
}
/* NOTREACHED */
}
int
spec_inactive(void *v)
{
struct vop_inactive_args *ap = v;
VOP_UNLOCK(ap->a_vp);
return (0);
}
/*
* Vnode op for write
*/
int
spec_write(void *v)
{
struct vop_write_args *ap = v;
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct proc *p = uio->uio_procp;
struct buf *bp;
daddr_t bn, bscale;
int bsize;
struct partinfo dpart;
size_t n;
int on, majordev;
int (*ioctl)(dev_t, u_long, caddr_t, int, struct proc *);
int error = 0;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_WRITE)
panic("spec_write mode");
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
panic("spec_write proc");
#endif
switch (vp->v_type) {
case VCHR:
VOP_UNLOCK(vp);
error = (*cdevsw[major(vp->v_rdev)].d_write)
(vp->v_rdev, uio, ap->a_ioflag);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
return (error);
case VBLK:
if (uio->uio_resid == 0)
return (0);
if (uio->uio_offset < 0)
return (EINVAL);
bsize = BLKDEV_IOSIZE;
if ((majordev = major(vp->v_rdev)) < nblkdev &&
(ioctl = bdevsw[majordev].d_ioctl) != NULL &&
(*ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) {
u_int32_t frag =
DISKLABELV1_FFS_FRAG(dpart.part->p_fragblock);
u_int32_t fsize =
DISKLABELV1_FFS_FSIZE(dpart.part->p_fragblock);
if (dpart.part->p_fstype == FS_BSDFFS && frag != 0 &&
fsize != 0)
bsize = frag * fsize;
}
bscale = btodb(bsize);
do {
bn = btodb(uio->uio_offset) & ~(bscale - 1);
on = uio->uio_offset % bsize;
n = ulmin((bsize - on), uio->uio_resid);
error = bread(vp, bn, bsize, &bp);
n = ulmin(n, bsize - bp->b_resid);
if (error) {
brelse(bp);
return (error);
}
error = uiomove((char *)bp->b_data + on, n, uio);
if (n + on == bsize)
bawrite(bp);
else
bdwrite(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return (error);
default:
panic("spec_write type");
}
/* NOTREACHED */
}
/*
* Device ioctl operation.
*/
int
spec_ioctl(void *v)
{
struct vop_ioctl_args *ap = v;
dev_t dev = ap->a_vp->v_rdev;
int maj = major(dev);
switch (ap->a_vp->v_type) {
case VCHR:
return ((*cdevsw[maj].d_ioctl)(dev, ap->a_command, ap->a_data,
ap->a_fflag, ap->a_p));
case VBLK:
return ((*bdevsw[maj].d_ioctl)(dev, ap->a_command, ap->a_data,
ap->a_fflag, ap->a_p));
default:
panic("spec_ioctl");
/* NOTREACHED */
}
}
int
spec_kqfilter(void *v)
{
struct vop_kqfilter_args *ap = v;
dev_t dev;
dev = ap->a_vp->v_rdev;
switch (ap->a_vp->v_type) {
default:
if (ap->a_kn->kn_flags & (__EV_POLL | __EV_SELECT))
return seltrue_kqfilter(dev, ap->a_kn);
break;
case VCHR:
if (cdevsw[major(dev)].d_kqfilter)
return (*cdevsw[major(dev)].d_kqfilter)(dev, ap->a_kn);
}
return (EOPNOTSUPP);
}
/*
* Synch buffers associated with a block device
*/
int
spec_fsync(void *v)
{
struct vop_fsync_args *ap = v;
struct vnode *vp = ap->a_vp;
struct buf *bp;
struct buf *nbp;
int s;
if (vp->v_type == VCHR)
return (0);
/*
* Flush all dirty buffers associated with a block device.
*/
loop:
s = splbio();
LIST_FOREACH_SAFE(bp, &vp->v_dirtyblkhd, b_vnbufs, nbp) {
if ((bp->b_flags & B_BUSY))
continue;
if ((bp->b_flags & B_DELWRI) == 0)
panic("spec_fsync: not dirty");
bremfree(bp);
buf_acquire(bp);
splx(s);
bawrite(bp);
goto loop;
}
if (ap->a_waitfor == MNT_WAIT) {
vwaitforio (vp, 0, "spec_fsync", INFSLP);
#ifdef DIAGNOSTIC
if (!LIST_EMPTY(&vp->v_dirtyblkhd)) {
splx(s);
vprint("spec_fsync: dirty", vp);
goto loop;
}
#endif
}
splx(s);
return (0);
}
int
spec_strategy(void *v)
{
struct vop_strategy_args *ap = v;
struct buf *bp = ap->a_bp;
int maj = major(bp->b_dev);
if (LIST_FIRST(&bp->b_dep) != NULL)
buf_start(bp);
(*bdevsw[maj].d_strategy)(bp);
return (0);
}
/*
* Device close routine
*/
int
spec_close(void *v)
{
struct vop_close_args *ap = v;
struct proc *p = ap->a_p;
struct vnode *vp = ap->a_vp;
dev_t dev = vp->v_rdev;
int (*devclose)(dev_t, int, int, struct proc *);
int mode, relock, xlocked, error;
int clone = 0;
switch (vp->v_type) {
case VCHR:
/*
* Hack: a tty device that is a controlling terminal
* has a reference from the session structure.
* We cannot easily tell that a character device is
* a controlling terminal, unless it is the closing
* process' controlling terminal. In that case,
* if the reference count is 2 (this last descriptor
* plus the session), release the reference from the session.
*/
if (vcount(vp) == 2 && p != NULL && p->p_p->ps_pgrp &&
vp == p->p_p->ps_pgrp->pg_session->s_ttyvp) {
vrele(vp);
p->p_p->ps_pgrp->pg_session->s_ttyvp = NULL;
}
if (cdevsw[major(dev)].d_flags & D_CLONE) {
clone = 1;
} else {
/*
* If the vnode is locked, then we are in the midst
* of forcibly closing the device, otherwise we only
* close on last reference.
*/
mtx_enter(&vnode_mtx);
xlocked = (vp->v_lflag & VXLOCK);
mtx_leave(&vnode_mtx);
if (vcount(vp) > 1 && !xlocked)
return (0);
}
devclose = cdevsw[major(dev)].d_close;
mode = S_IFCHR;
break;
case VBLK:
/*
* On last close of a block device (that isn't mounted)
* we must invalidate any in core blocks, so that
* we can, for instance, change floppy disks. In order to do
* that, we must lock the vnode. If we are coming from
* vclean(), the vnode is already locked.
*/
mtx_enter(&vnode_mtx);
xlocked = (vp->v_lflag & VXLOCK);
mtx_leave(&vnode_mtx);
if (!xlocked)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(vp, V_SAVE, ap->a_cred, p, 0, INFSLP);
if (!xlocked)
VOP_UNLOCK(vp);
if (error)
return (error);
/*
* We do not want to really close the device if it
* is still in use unless we are trying to close it
* forcibly. Since every use (buffer, vnode, swap, cmap)
* holds a reference to the vnode, and because we mark
* any other vnodes that alias this device, when the
* sum of the reference counts on all the aliased
* vnodes descends to one, we are on last close.
*/
mtx_enter(&vnode_mtx);
xlocked = (vp->v_lflag & VXLOCK);
mtx_leave(&vnode_mtx);
if (vcount(vp) > 1 && !xlocked)
return (0);
devclose = bdevsw[major(dev)].d_close;
mode = S_IFBLK;
break;
default:
panic("spec_close: not special");
}
/* release lock if held and this isn't coming from vclean() */
mtx_enter(&vnode_mtx);
xlocked = (vp->v_lflag & VXLOCK);
mtx_leave(&vnode_mtx);
relock = VOP_ISLOCKED(vp) && !xlocked;
if (relock)
VOP_UNLOCK(vp);
error = (*devclose)(dev, ap->a_fflag, mode, p);
if (relock)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error == 0 && clone) {
struct vnode *pvp;
pvp = vp->v_specparent; /* get parent device */
clrbit(pvp->v_specbitmap, minor(dev) >> CLONE_SHIFT);
vrele(pvp);
}
return (error);
}
int
spec_getattr(void *v)
{
struct vop_getattr_args *ap = v;
struct vnode *vp = ap->a_vp;
int error;
if (!(vp->v_flag & VCLONE))
return (EBADF);
vn_lock(vp->v_specparent, LK_EXCLUSIVE|LK_RETRY);
error = VOP_GETATTR(vp->v_specparent, ap->a_vap, ap->a_cred, ap->a_p);
VOP_UNLOCK(vp->v_specparent);
return (error);
}
int
spec_setattr(void *v)
{
struct vop_getattr_args *ap = v;
struct proc *p = ap->a_p;
struct vnode *vp = ap->a_vp;
int error;
if (!(vp->v_flag & VCLONE))
return (EBADF);
vn_lock(vp->v_specparent, LK_EXCLUSIVE|LK_RETRY);
error = VOP_SETATTR(vp->v_specparent, ap->a_vap, ap->a_cred, p);
VOP_UNLOCK(vp->v_specparent);
return (error);
}
int
spec_access(void *v)
{
struct vop_access_args *ap = v;
struct vnode *vp = ap->a_vp;
int error;
if (!(vp->v_flag & VCLONE))
return (EBADF);
vn_lock(vp->v_specparent, LK_EXCLUSIVE|LK_RETRY);
error = VOP_ACCESS(vp->v_specparent, ap->a_mode, ap->a_cred, ap->a_p);
VOP_UNLOCK(vp->v_specparent);
return (error);
}
/*
* Print out the contents of a special device vnode.
*/
int
spec_print(void *v)
{
struct vop_print_args *ap = v;
printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev),
minor(ap->a_vp->v_rdev));
return 0;
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
int
spec_pathconf(void *v)
{
struct vop_pathconf_args *ap = v;
int error = 0;
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
break;
case _PC_MAX_CANON:
*ap->a_retval = MAX_CANON;
break;
case _PC_MAX_INPUT:
*ap->a_retval = MAX_INPUT;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
break;
case _PC_VDISABLE:
*ap->a_retval = _POSIX_VDISABLE;
break;
case _PC_TIMESTAMP_RESOLUTION:
*ap->a_retval = 1;
break;
default:
error = EINVAL;
break;
}
return (error);
}
/*
* Special device advisory byte-level locks.
*/
int
spec_advlock(void *v)
{
struct vop_advlock_args *ap = v;
struct vnode *vp = ap->a_vp;
return (lf_advlock(&vp->v_speclockf, (off_t)0, ap->a_id,
ap->a_op, ap->a_fl, ap->a_flags));
}
/*
* Copyright (c) 2006 Pedro Martelletto <pedro@ambientworks.net>
* Copyright (c) 2006 Thordur Bjornsson <thib@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef CLONE_DEBUG
#define DNPRINTF(m...) do { printf(m); } while (0)
#else
#define DNPRINTF(m...) /* nothing */
#endif
int
spec_open_clone(struct vop_open_args *ap)
{
struct vnode *cvp, *vp = ap->a_vp;
struct cloneinfo *cip;
int error, i;
DNPRINTF("cloning vnode\n");
if (minor(vp->v_rdev) >= (1 << CLONE_SHIFT))
return (ENXIO);
for (i = 1; i < CLONE_MAPSZ * NBBY; i++)
if (isclr(vp->v_specbitmap, i)) {
setbit(vp->v_specbitmap, i);
break;
}
if (i == CLONE_MAPSZ * NBBY)
return (EBUSY); /* too many open instances */
error = cdevvp(makedev(major(vp->v_rdev),
(i << CLONE_SHIFT) | minor(vp->v_rdev)), &cvp);
if (error) {
clrbit(vp->v_specbitmap, i);
return (error); /* out of vnodes */
}
VOP_UNLOCK(vp);
error = cdevsw[major(vp->v_rdev)].d_open(cvp->v_rdev, ap->a_mode,
S_IFCHR, ap->a_p);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error) {
vput(cvp);
clrbit(vp->v_specbitmap, i);
return (error); /* device open failed */
}
cvp->v_flag |= VCLONE;
cip = malloc(sizeof(struct cloneinfo), M_TEMP, M_WAITOK);
cip->ci_data = vp->v_data;
cip->ci_vp = cvp;
cvp->v_specparent = vp;
vp->v_flag |= VCLONED;
vp->v_data = cip;
DNPRINTF("clone of vnode %p is vnode %p\n", vp, cvp);
return (0); /* device cloned */
}
9
2
92
22
314
16
16
302
7
7
7
7
1422
10
170
1
123
41
40
99
59
23
39
13
71
59
44
441
134
353
34
1
6
5
31
30
1
1
1
38
10
18
36
18
37
108
108
109
107
70
18
17
1
101
19
17
19
17
18
18
18
2
42
44
38
95
86
87
87
52
10
14
56
14
33
2
5
2
3
2
2
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
/* $OpenBSD: rtable.c,v 1.80 2022/06/29 22:20:47 bluhm Exp $ */
/*
* Copyright (c) 2014-2016 Martin Pieuchot
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _KERNEL
#include "kern_compat.h"
#else
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/domain.h>
#include <sys/srp.h>
#endif
#include <net/rtable.h>
#include <net/route.h>
/*
* Structures used by rtable_get() to retrieve the corresponding
* routing table for a given pair of ``af'' and ``rtableid''.
*
* Note that once allocated routing table heads are never freed.
* This way we do not need to reference count them.
*
* afmap rtmap/dommp
* ----------- --------- -----
* | 0 |--------> | 0 | 0 | ... | 0 | Array mapping rtableid (=index)
* ----------- --------- ----- to rdomain/loopback (=value).
* | AF_INET |.
* ----------- `. .---------. .---------.
* ... `----> | rtable0 | ... | rtableN | Array of pointers for
* ----------- '---------' '---------' IPv4 routing tables
* | AF_MPLS | indexed by ``rtableid''.
* -----------
*/
struct srp *afmap;
uint8_t af2idx[AF_MAX+1]; /* To only allocate supported AF */
uint8_t af2idx_max;
/* Array of routing table pointers. */
struct rtmap {
unsigned int limit;
void **tbl;
};
/*
* Array of rtableid -> rdomain mapping.
*
* Only used for the first index as described above.
*/
struct dommp {
unsigned int limit;
/*
* Array to get the routing domain and loopback interface related to
* a routing table. Format:
*
* 8 unused bits | 16 bits for loopback index | 8 bits for rdomain
*/
unsigned int *value;
};
unsigned int rtmap_limit = 0;
void rtmap_init(void);
void rtmap_grow(unsigned int, sa_family_t);
void rtmap_dtor(void *, void *);
struct srp_gc rtmap_gc = SRP_GC_INITIALIZER(rtmap_dtor, NULL);
void rtable_init_backend(void);
void *rtable_alloc(unsigned int, unsigned int, unsigned int);
void *rtable_get(unsigned int, sa_family_t);
void
rtmap_init(void)
{
const struct domain *dp;
int i;
/* Start with a single table for every domain that requires it. */
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
rtmap_grow(1, dp->dom_family);
}
/* Initialize the rtableid->rdomain mapping table. */
rtmap_grow(1, 0);
rtmap_limit = 1;
}
/*
* Grow the size of the array of routing table for AF ``af'' to ``nlimit''.
*/
void
rtmap_grow(unsigned int nlimit, sa_family_t af)
{
struct rtmap *map, *nmap;
int i;
KERNEL_ASSERT_LOCKED();
KASSERT(nlimit > rtmap_limit);
nmap = malloc(sizeof(*nmap), M_RTABLE, M_WAITOK);
nmap->limit = nlimit;
nmap->tbl = mallocarray(nlimit, sizeof(*nmap[0].tbl), M_RTABLE,
M_WAITOK|M_ZERO);
map = srp_get_locked(&afmap[af2idx[af]]);
if (map != NULL) {
KASSERT(map->limit == rtmap_limit);
for (i = 0; i < map->limit; i++)
nmap->tbl[i] = map->tbl[i];
}
srp_update_locked(&rtmap_gc, &afmap[af2idx[af]], nmap);
}
void
rtmap_dtor(void *null, void *xmap)
{
struct rtmap *map = xmap;
/*
* doesn't need to be serialized since this is the last reference
* to this map. there's nothing to race against.
*/
free(map->tbl, M_RTABLE, map->limit * sizeof(*map[0].tbl));
free(map, M_RTABLE, sizeof(*map));
}
void
rtable_init(void)
{
const struct domain *dp;
int i;
KASSERT(sizeof(struct rtmap) == sizeof(struct dommp));
/* We use index 0 for the rtable/rdomain map. */
af2idx_max = 1;
memset(af2idx, 0, sizeof(af2idx));
/*
* Compute the maximum supported key length in case the routing
* table backend needs it.
*/
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
af2idx[dp->dom_family] = af2idx_max++;
}
rtable_init_backend();
/*
* Allocate AF-to-id table now that we now how many AFs this
* kernel supports.
*/
afmap = mallocarray(af2idx_max + 1, sizeof(*afmap), M_RTABLE,
M_WAITOK|M_ZERO);
rtmap_init();
if (rtable_add(0) != 0)
panic("unable to create default routing table");
rt_timer_init();
}
int
rtable_add(unsigned int id)
{
const struct domain *dp;
void *tbl;
struct rtmap *map;
struct dommp *dmm;
sa_family_t af;
unsigned int off, alen;
int i, error = 0;
if (id > RT_TABLEID_MAX)
return (EINVAL);
KERNEL_LOCK();
if (rtable_exists(id))
goto out;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
af = dp->dom_family;
off = dp->dom_rtoffset;
alen = dp->dom_maxplen;
if (id >= rtmap_limit)
rtmap_grow(id + 1, af);
tbl = rtable_alloc(id, alen, off);
if (tbl == NULL) {
error = ENOMEM;
goto out;
}
map = srp_get_locked(&afmap[af2idx[af]]);
map->tbl[id] = tbl;
}
/* Reflect possible growth. */
if (id >= rtmap_limit) {
rtmap_grow(id + 1, 0);
rtmap_limit = id + 1;
}
/* Use main rtable/rdomain by default. */
dmm = srp_get_locked(&afmap[0]);
dmm->value[id] = 0;
out:
KERNEL_UNLOCK();
return (error);
}
void *
rtable_get(unsigned int rtableid, sa_family_t af)
{
struct rtmap *map;
void *tbl = NULL;
struct srp_ref sr;
if (af >= nitems(af2idx) || af2idx[af] == 0)
return (NULL);
map = srp_enter(&sr, &afmap[af2idx[af]]);
if (rtableid < map->limit)
tbl = map->tbl[rtableid];
srp_leave(&sr);
return (tbl);
}
int
rtable_exists(unsigned int rtableid)
{
const struct domain *dp;
void *tbl;
int i;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
tbl = rtable_get(rtableid, dp->dom_family);
if (tbl != NULL)
return (1);
}
return (0);
}
int
rtable_empty(unsigned int rtableid)
{
const struct domain *dp;
int i;
struct art_root *tbl;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
tbl = rtable_get(rtableid, dp->dom_family);
if (tbl == NULL)
continue;
if (tbl->ar_root.ref != NULL)
return (0);
}
return (1);
}
unsigned int
rtable_l2(unsigned int rtableid)
{
struct dommp *dmm;
unsigned int rdomain = 0;
struct srp_ref sr;
dmm = srp_enter(&sr, &afmap[0]);
if (rtableid < dmm->limit)
rdomain = (dmm->value[rtableid] & RT_TABLEID_MASK);
srp_leave(&sr);
return (rdomain);
}
unsigned int
rtable_loindex(unsigned int rtableid)
{
struct dommp *dmm;
unsigned int loifidx = 0;
struct srp_ref sr;
dmm = srp_enter(&sr, &afmap[0]);
if (rtableid < dmm->limit)
loifidx = (dmm->value[rtableid] >> RT_TABLEID_BITS);
srp_leave(&sr);
return (loifidx);
}
void
rtable_l2set(unsigned int rtableid, unsigned int rdomain, unsigned int loifidx)
{
struct dommp *dmm;
unsigned int value;
KERNEL_ASSERT_LOCKED();
if (!rtable_exists(rtableid) || !rtable_exists(rdomain))
return;
value = (rdomain & RT_TABLEID_MASK) | (loifidx << RT_TABLEID_BITS);
dmm = srp_get_locked(&afmap[0]);
dmm->value[rtableid] = value;
}
static inline uint8_t *satoaddr(struct art_root *, struct sockaddr *);
int an_match(struct art_node *, struct sockaddr *, int);
void rtentry_ref(void *, void *);
void rtentry_unref(void *, void *);
void rtable_mpath_insert(struct art_node *, struct rtentry *);
struct srpl_rc rt_rc = SRPL_RC_INITIALIZER(rtentry_ref, rtentry_unref, NULL);
void
rtable_init_backend(void)
{
art_init();
}
void *
rtable_alloc(unsigned int rtableid, unsigned int alen, unsigned int off)
{
return (art_alloc(rtableid, alen, off));
}
int
rtable_setsource(unsigned int rtableid, int af, struct sockaddr *src)
{
struct art_root *ar;
if ((ar = rtable_get(rtableid, af)) == NULL)
return (EAFNOSUPPORT);
ar->source = src;
return (0);
}
struct sockaddr *
rtable_getsource(unsigned int rtableid, int af)
{
struct art_root *ar;
ar = rtable_get(rtableid, af);
if (ar == NULL)
return (NULL);
return (ar->source);
}
void
rtable_clearsource(unsigned int rtableid, struct sockaddr *src)
{
struct sockaddr *addr;
addr = rtable_getsource(rtableid, src->sa_family);
if (addr && (addr->sa_len == src->sa_len)) {
if (memcmp(src, addr, addr->sa_len) == 0) {
rtable_setsource(rtableid, src->sa_family, NULL);
}
}
}
struct rtentry *
rtable_lookup(unsigned int rtableid, struct sockaddr *dst,
struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio)
{
struct art_root *ar;
struct art_node *an;
struct rtentry *rt = NULL;
struct srp_ref sr, nsr;
uint8_t *addr;
int plen;
ar = rtable_get(rtableid, dst->sa_family);
if (ar == NULL)
return (NULL);
addr = satoaddr(ar, dst);
/* No need for a perfect match. */
if (mask == NULL) {
an = art_match(ar, addr, &nsr);
if (an == NULL)
goto out;
} else {
plen = rtable_satoplen(dst->sa_family, mask);
if (plen == -1)
return (NULL);
an = art_lookup(ar, addr, plen, &nsr);
/* Make sure we've got a perfect match. */
if (!an_match(an, dst, plen))
goto out;
}
SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) {
if (prio != RTP_ANY &&
(rt->rt_priority & RTP_MASK) != (prio & RTP_MASK))
continue;
if (gateway == NULL)
break;
if (rt->rt_gateway->sa_len == gateway->sa_len &&
memcmp(rt->rt_gateway, gateway, gateway->sa_len) == 0)
break;
}
if (rt != NULL)
rtref(rt);
SRPL_LEAVE(&sr);
out:
srp_leave(&nsr);
return (rt);
}
struct rtentry *
rtable_match(unsigned int rtableid, struct sockaddr *dst, uint32_t *src)
{
struct art_root *ar;
struct art_node *an;
struct rtentry *rt = NULL;
struct srp_ref sr, nsr;
uint8_t *addr;
int hash;
ar = rtable_get(rtableid, dst->sa_family);
if (ar == NULL)
return (NULL);
addr = satoaddr(ar, dst);
an = art_match(ar, addr, &nsr);
if (an == NULL)
goto out;
rt = SRPL_FIRST(&sr, &an->an_rtlist);
if (rt == NULL) {
SRPL_LEAVE(&sr);
goto out;
}
rtref(rt);
SRPL_LEAVE(&sr);
/* Gateway selection by Hash-Threshold (RFC 2992) */
if ((hash = rt_hash(rt, dst, src)) != -1) {
struct rtentry *mrt;
int threshold, npaths = 0;
KASSERT(hash <= 0xffff);
SRPL_FOREACH(mrt, &sr, &an->an_rtlist, rt_next) {
/* Only count nexthops with the same priority. */
if (mrt->rt_priority == rt->rt_priority)
npaths++;
}
SRPL_LEAVE(&sr);
threshold = (0xffff / npaths) + 1;
/*
* we have no protection against concurrent modification of the
* route list attached to the node, so we won't necessarily
* have the same number of routes. for most modifications,
* we'll pick a route that we wouldn't have if we only saw the
* list before or after the change. if we were going to use
* the last available route, but it got removed, we'll hit
* the end of the list and then pick the first route.
*/
mrt = SRPL_FIRST(&sr, &an->an_rtlist);
while (hash > threshold && mrt != NULL) {
if (mrt->rt_priority == rt->rt_priority)
hash -= threshold;
mrt = SRPL_FOLLOW(&sr, mrt, rt_next);
}
if (mrt != NULL) {
rtref(mrt);
rtfree(rt);
rt = mrt;
}
SRPL_LEAVE(&sr);
}
out:
srp_leave(&nsr);
return (rt);
}
int
rtable_insert(unsigned int rtableid, struct sockaddr *dst,
struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio,
struct rtentry *rt)
{
struct rtentry *mrt;
struct srp_ref sr;
struct art_root *ar;
struct art_node *an, *prev;
uint8_t *addr;
int plen;
unsigned int rt_flags;
int error = 0;
ar = rtable_get(rtableid, dst->sa_family);
if (ar == NULL)
return (EAFNOSUPPORT);
addr = satoaddr(ar, dst);
plen = rtable_satoplen(dst->sa_family, mask);
if (plen == -1)
return (EINVAL);
rtref(rt); /* guarantee rtfree won't do anything during insert */
rw_enter_write(&ar->ar_lock);
/* Do not permit exactly the same dst/mask/gw pair. */
an = art_lookup(ar, addr, plen, &sr);
srp_leave(&sr); /* an can't go away while we have the lock */
if (an_match(an, dst, plen)) {
struct rtentry *mrt;
int mpathok = ISSET(rt->rt_flags, RTF_MPATH);
SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next) {
if (prio != RTP_ANY &&
(mrt->rt_priority & RTP_MASK) != (prio & RTP_MASK))
continue;
if (!mpathok ||
(mrt->rt_gateway->sa_len == gateway->sa_len &&
memcmp(mrt->rt_gateway, gateway,
gateway->sa_len) == 0)) {
error = EEXIST;
goto leave;
}
}
}
an = art_get(dst, plen);
if (an == NULL) {
error = ENOBUFS;
goto leave;
}
/* prepare for immediate operation if insert succeeds */
rt_flags = rt->rt_flags;
rt->rt_flags &= ~RTF_MPATH;
rt->rt_dest = dst;
rt->rt_plen = plen;
SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
prev = art_insert(ar, an, addr, plen);
if (prev != an) {
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,
rt_next);
rt->rt_flags = rt_flags;
art_put(an);
if (prev == NULL) {
error = ESRCH;
goto leave;
}
an = prev;
mrt = SRPL_FIRST_LOCKED(&an->an_rtlist);
KASSERT(mrt != NULL);
KASSERT((rt->rt_flags & RTF_MPATH) || mrt->rt_priority != prio);
/*
* An ART node with the same destination/netmask already
* exists, MPATH conflict must have been already checked.
*/
if (rt->rt_flags & RTF_MPATH) {
/*
* Only keep the RTF_MPATH flag if two routes have
* the same gateway.
*/
rt->rt_flags &= ~RTF_MPATH;
SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next) {
if (mrt->rt_priority == prio) {
mrt->rt_flags |= RTF_MPATH;
rt->rt_flags |= RTF_MPATH;
}
}
}
/* Put newly inserted entry at the right place. */
rtable_mpath_insert(an, rt);
}
leave:
rw_exit_write(&ar->ar_lock);
rtfree(rt);
return (error);
}
int
rtable_delete(unsigned int rtableid, struct sockaddr *dst,
struct sockaddr *mask, struct rtentry *rt)
{
struct art_root *ar;
struct art_node *an;
struct srp_ref sr;
uint8_t *addr;
int plen;
struct rtentry *mrt;
int npaths = 0;
int error = 0;
ar = rtable_get(rtableid, dst->sa_family);
if (ar == NULL)
return (EAFNOSUPPORT);
addr = satoaddr(ar, dst);
plen = rtable_satoplen(dst->sa_family, mask);
if (plen == -1)
return (EINVAL);
rtref(rt); /* guarantee rtfree won't do anything under ar_lock */
rw_enter_write(&ar->ar_lock);
an = art_lookup(ar, addr, plen, &sr);
srp_leave(&sr); /* an can't go away while we have the lock */
/* Make sure we've got a perfect match. */
if (!an_match(an, dst, plen)) {
error = ESRCH;
goto leave;
}
/*
* If other multipath route entries are still attached to
* this ART node we only have to unlink it.
*/
SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next)
npaths++;
if (npaths > 1) {
KASSERT(refcnt_read(&rt->rt_refcnt) >= 1);
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,
rt_next);
mrt = SRPL_FIRST_LOCKED(&an->an_rtlist);
if (npaths == 2)
mrt->rt_flags &= ~RTF_MPATH;
goto leave;
}
if (art_delete(ar, an, addr, plen) == NULL)
panic("art_delete failed to find node %p", an);
KASSERT(refcnt_read(&rt->rt_refcnt) >= 1);
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next);
art_put(an);
leave:
rw_exit_write(&ar->ar_lock);
rtfree(rt);
return (error);
}
struct rtable_walk_cookie {
int (*rwc_func)(struct rtentry *, void *, unsigned int);
void *rwc_arg;
struct rtentry **rwc_prt;
unsigned int rwc_rid;
};
/*
* Helper for rtable_walk to keep the ART code free from any "struct rtentry".
*/
int
rtable_walk_helper(struct art_node *an, void *xrwc)
{
struct srp_ref sr;
struct rtable_walk_cookie *rwc = xrwc;
struct rtentry *rt;
int error = 0;
SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) {
error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid);
if (error != 0)
break;
}
if (rwc->rwc_prt != NULL && rt != NULL) {
rtref(rt);
*rwc->rwc_prt = rt;
}
SRPL_LEAVE(&sr);
return (error);
}
int
rtable_walk(unsigned int rtableid, sa_family_t af, struct rtentry **prt,
int (*func)(struct rtentry *, void *, unsigned int), void *arg)
{
struct art_root *ar;
struct rtable_walk_cookie rwc;
int error;
ar = rtable_get(rtableid, af);
if (ar == NULL)
return (EAFNOSUPPORT);
rwc.rwc_func = func;
rwc.rwc_arg = arg;
rwc.rwc_prt = prt;
rwc.rwc_rid = rtableid;
error = art_walk(ar, rtable_walk_helper, &rwc);
return (error);
}
struct rtentry *
rtable_iterate(struct rtentry *rt0)
{
struct rtentry *rt = NULL;
struct srp_ref sr;
rt = SRPL_NEXT(&sr, rt0, rt_next);
if (rt != NULL)
rtref(rt);
SRPL_LEAVE(&sr);
rtfree(rt0);
return (rt);
}
int
rtable_mpath_capable(unsigned int rtableid, sa_family_t af)
{
return (1);
}
int
rtable_mpath_reprio(unsigned int rtableid, struct sockaddr *dst,
int plen, uint8_t prio, struct rtentry *rt)
{
struct art_root *ar;
struct art_node *an;
struct srp_ref sr;
uint8_t *addr;
int error = 0;
ar = rtable_get(rtableid, dst->sa_family);
if (ar == NULL)
return (EAFNOSUPPORT);
addr = satoaddr(ar, dst);
rw_enter_write(&ar->ar_lock);
an = art_lookup(ar, addr, plen, &sr);
srp_leave(&sr); /* an can't go away while we have the lock */
/* Make sure we've got a perfect match. */
if (!an_match(an, dst, plen)) {
error = ESRCH;
} else if (SRPL_FIRST_LOCKED(&an->an_rtlist) == rt &&
SRPL_NEXT_LOCKED(rt, rt_next) == NULL) {
/*
* If there's only one entry on the list do not go
* through an insert/remove cycle. This is done to
* guarantee that ``an->an_rtlist'' is never empty
* when a node is in the tree.
*/
rt->rt_priority = prio;
} else {
rtref(rt); /* keep rt alive in between remove and insert */
SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist,
rt, rtentry, rt_next);
rt->rt_priority = prio;
rtable_mpath_insert(an, rt);
rtfree(rt);
error = EAGAIN;
}
rw_exit_write(&ar->ar_lock);
return (error);
}
void
rtable_mpath_insert(struct art_node *an, struct rtentry *rt)
{
struct rtentry *mrt, *prt = NULL;
uint8_t prio = rt->rt_priority;
if ((mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)) == NULL) {
SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
return;
}
/* Iterate until we find the route to be placed after ``rt''. */
while (mrt->rt_priority <= prio && SRPL_NEXT_LOCKED(mrt, rt_next)) {
prt = mrt;
mrt = SRPL_NEXT_LOCKED(mrt, rt_next);
}
if (mrt->rt_priority <= prio) {
SRPL_INSERT_AFTER_LOCKED(&rt_rc, mrt, rt, rt_next);
} else if (prt != NULL) {
SRPL_INSERT_AFTER_LOCKED(&rt_rc, prt, rt, rt_next);
} else {
SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
}
}
/*
* Returns 1 if ``an'' perfectly matches (``dst'', ``plen''), 0 otherwise.
*/
int
an_match(struct art_node *an, struct sockaddr *dst, int plen)
{
struct rtentry *rt;
struct srp_ref sr;
int match;
if (an == NULL || an->an_plen != plen)
return (0);
rt = SRPL_FIRST(&sr, &an->an_rtlist);
match = (memcmp(rt->rt_dest, dst, dst->sa_len) == 0);
SRPL_LEAVE(&sr);
return (match);
}
void
rtentry_ref(void *null, void *xrt)
{
struct rtentry *rt = xrt;
rtref(rt);
}
void
rtentry_unref(void *null, void *xrt)
{
struct rtentry *rt = xrt;
rtfree(rt);
}
/*
* Return a pointer to the address (key). This is an heritage from the
* BSD radix tree needed to skip the non-address fields from the flavor
* of "struct sockaddr" used by this routing table.
*/
static inline uint8_t *
satoaddr(struct art_root *at, struct sockaddr *sa)
{
return (((uint8_t *)sa) + at->ar_off);
}
/*
* Return the prefix length of a mask.
*/
int
rtable_satoplen(sa_family_t af, struct sockaddr *mask)
{
const struct domain *dp;
uint8_t *ap, *ep;
int mlen, plen = 0;
int i;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
if (af == dp->dom_family)
break;
}
if (dp == NULL)
return (-1);
/* Host route */
if (mask == NULL)
return (dp->dom_maxplen);
mlen = mask->sa_len;
/* Default route */
if (mlen == 0)
return (0);
ap = (uint8_t *)((uint8_t *)mask) + dp->dom_rtoffset;
ep = (uint8_t *)((uint8_t *)mask) + mlen;
if (ap > ep)
return (-1);
/* Trim trailing zeroes. */
while (ap < ep && ep[-1] == 0)
ep--;
if (ap == ep)
return (0);
/* "Beauty" adapted from sbin/route/show.c ... */
while (ap < ep) {
switch (*ap++) {
case 0xff:
plen += 8;
break;
case 0xfe:
plen += 7;
goto out;
case 0xfc:
plen += 6;
goto out;
case 0xf8:
plen += 5;
goto out;
case 0xf0:
plen += 4;
goto out;
case 0xe0:
plen += 3;
goto out;
case 0xc0:
plen += 2;
goto out;
case 0x80:
plen += 1;
goto out;
default:
/* Non contiguous mask. */
return (-1);
}
}
out:
if (plen > dp->dom_maxplen || ap != ep)
return -1;
return (plen);
}
3
2
1
2
1
1
1
1
1
1
1
1
2
2
3
4
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/* $OpenBSD: ip6_divert.c,v 1.86 2022/09/05 14:56:09 bluhm Exp $ */
/*
* Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/if_var.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip6.h>
#include <netinet6/in6_var.h>
#include <netinet6/ip6_divert.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/icmp6.h>
#include <net/pfvar.h>
struct inpcbtable divb6table;
struct cpumem *div6counters;
#ifndef DIVERT_SENDSPACE
#define DIVERT_SENDSPACE (65536 + 100)
#endif
u_int divert6_sendspace = DIVERT_SENDSPACE;
#ifndef DIVERT_RECVSPACE
#define DIVERT_RECVSPACE (65536 + 100)
#endif
u_int divert6_recvspace = DIVERT_RECVSPACE;
#ifndef DIVERTHASHSIZE
#define DIVERTHASHSIZE 128
#endif
const struct sysctl_bounded_args divert6ctl_vars[] = {
{ DIVERT6CTL_RECVSPACE, &divert6_recvspace, 0, INT_MAX },
{ DIVERT6CTL_SENDSPACE, &divert6_sendspace, 0, INT_MAX },
};
const struct pr_usrreqs divert6_usrreqs = {
.pru_attach = divert6_attach,
.pru_detach = divert6_detach,
.pru_lock = divert6_lock,
.pru_unlock = divert6_unlock,
.pru_bind = divert6_bind,
.pru_shutdown = divert6_shutdown,
.pru_send = divert6_send,
.pru_abort = divert6_abort,
.pru_control = in6_control,
.pru_sockaddr = in6_sockaddr,
.pru_peeraddr = in6_peeraddr,
};
int divb6hashsize = DIVERTHASHSIZE;
int divert6_output(struct inpcb *, struct mbuf *, struct mbuf *,
struct mbuf *);
void
divert6_init(void)
{
in_pcbinit(&divb6table, divb6hashsize);
div6counters = counters_alloc(div6s_ncounters);
}
int
divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct sockaddr_in6 *sin6;
int error, min_hdrlen, nxt, off, dir;
struct ip6_hdr *ip6;
m_freem(control);
if ((error = in6_nam2sin6(nam, &sin6)))
goto fail;
/* Do basic sanity checks. */
if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
goto fail;
if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
/* m_pullup() has freed the mbuf, so just return. */
div6stat_inc(div6s_errors);
return (ENOBUFS);
}
ip6 = mtod(m, struct ip6_hdr *);
if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
goto fail;
if (m->m_pkthdr.len < sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen))
goto fail;
/*
* Recalculate the protocol checksum since the userspace application
* may have modified the packet prior to reinjection.
*/
off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
if (off < sizeof(struct ip6_hdr))
goto fail;
dir = (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ? PF_OUT : PF_IN);
switch (nxt) {
case IPPROTO_TCP:
min_hdrlen = sizeof(struct tcphdr);
m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
break;
case IPPROTO_UDP:
min_hdrlen = sizeof(struct udphdr);
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
break;
case IPPROTO_ICMPV6:
min_hdrlen = sizeof(struct icmp6_hdr);
m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
break;
default:
min_hdrlen = 0;
break;
}
if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
goto fail;
m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
if (dir == PF_IN) {
struct rtentry *rt;
struct ifnet *ifp;
rt = rtalloc(sin6tosa(sin6), 0, inp->inp_rtableid);
if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
rtfree(rt);
error = EADDRNOTAVAIL;
goto fail;
}
m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
rtfree(rt);
/*
* Recalculate the protocol checksum for the inbound packet
* since the userspace application may have modified the packet
* prior to reinjection.
*/
in6_proto_cksum_out(m, NULL);
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
error = ENETDOWN;
goto fail;
}
ipv6_input(ifp, m);
if_put(ifp);
} else {
m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
error = ip6_output(m, NULL, &inp->inp_route6,
IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL);
}
div6stat_inc(div6s_opackets);
return (error);
fail:
div6stat_inc(div6s_errors);
m_freem(m);
return (error ? error : EINVAL);
}
void
divert6_packet(struct mbuf *m, int dir, u_int16_t divert_port)
{
struct inpcb *inp = NULL;
struct socket *so;
struct sockaddr_in6 sin6;
div6stat_inc(div6s_ipackets);
if (m->m_len < sizeof(struct ip6_hdr) &&
(m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
div6stat_inc(div6s_errors);
goto bad;
}
mtx_enter(&divb6table.inpt_mtx);
TAILQ_FOREACH(inp, &divb6table.inpt_queue, inp_queue) {
if (inp->inp_lport != divert_port)
continue;
in_pcbref(inp);
break;
}
mtx_leave(&divb6table.inpt_mtx);
if (inp == NULL) {
div6stat_inc(div6s_noport);
goto bad;
}
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(sin6);
if (dir == PF_IN) {
struct ifaddr *ifa;
struct ifnet *ifp;
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
div6stat_inc(div6s_errors);
goto bad;
}
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
sin6.sin6_addr = satosin6(ifa->ifa_addr)->sin6_addr;
break;
}
if_put(ifp);
}
mtx_enter(&inp->inp_mtx);
so = inp->inp_socket;
if (sbappendaddr(so, &so->so_rcv, sin6tosa(&sin6), m, NULL) == 0) {
mtx_leave(&inp->inp_mtx);
div6stat_inc(div6s_fullsock);
goto bad;
}
mtx_leave(&inp->inp_mtx);
sorwakeup(so);
in_pcbunref(inp);
return;
bad:
if (inp != NULL)
in_pcbunref(inp);
m_freem(m);
}
int
divert6_attach(struct socket *so, int proto)
{
int error;
if (so->so_pcb != NULL)
return EINVAL;
if ((so->so_state & SS_PRIV) == 0)
return EACCES;
error = in_pcballoc(so, &divb6table);
if (error)
return (error);
error = soreserve(so, divert6_sendspace, divert6_recvspace);
if (error)
return (error);
sotoinpcb(so)->inp_flags |= INP_HDRINCL;
return (0);
}
int
divert6_detach(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
if (inp == NULL)
return (EINVAL);
in_pcbdetach(inp);
return (0);
}
void
divert6_lock(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
NET_ASSERT_LOCKED();
mtx_enter(&inp->inp_mtx);
}
void
divert6_unlock(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
NET_ASSERT_LOCKED();
mtx_leave(&inp->inp_mtx);
}
int
divert6_bind(struct socket *so, struct mbuf *addr, struct proc *p)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return in_pcbbind(inp, addr, p);
}
int
divert6_shutdown(struct socket *so)
{
soassertlocked(so);
socantsendmore(so);
return (0);
}
int
divert6_send(struct socket *so, struct mbuf *m, struct mbuf *addr,
struct mbuf *control)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return (divert6_output(inp, m, addr, control));
}
int
divert6_abort(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
soisdisconnected(so);
in_pcbdetach(inp);
return (0);
}
int
divert6_sysctl_div6stat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[div6s_ncounters];
struct div6stat div6stat;
u_long *words = (u_long *)&div6stat;
int i;
CTASSERT(sizeof(div6stat) == (nitems(counters) * sizeof(u_long)));
counters_read(div6counters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&div6stat, sizeof(div6stat)));
}
/*
* Sysctl for divert variables.
*/
int
divert6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case DIVERT6CTL_STATS:
return (divert6_sysctl_div6stat(oldp, oldlenp, newp));
default:
NET_LOCK();
error = sysctl_bounded_arr(divert6ctl_vars,
nitems(divert6ctl_vars), name, namelen, oldp, oldlenp,
newp, newlen);
NET_UNLOCK();
return (error);
}
/* NOTREACHED */
}
4
15
11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
/* $OpenBSD: exec_elf.c,v 1.168 2022/08/29 16:53:46 deraadt Exp $ */
/*
* Copyright (c) 1996 Per Fogelstrom
* All rights reserved.
*
* Copyright (c) 1994 Christos Zoulas
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* Copyright (c) 2001 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/core.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>
#include <sys/fcntl.h>
#include <sys/ptrace.h>
#include <sys/signalvar.h>
#include <sys/pledge.h>
#include <sys/mman.h>
#include <uvm/uvm_extern.h>
#include <machine/reg.h>
#include <machine/exec.h>
int elf_load_file(struct proc *, char *, struct exec_package *,
struct elf_args *);
int elf_check_header(Elf_Ehdr *);
int elf_read_from(struct proc *, struct vnode *, u_long, void *, int);
void elf_load_psection(struct exec_vmcmd_set *, struct vnode *,
Elf_Phdr *, Elf_Addr *, Elf_Addr *, int *, int);
int elf_os_pt_note_name(Elf_Note *);
int elf_os_pt_note(struct proc *, struct exec_package *, Elf_Ehdr *, int *);
/* round up and down to page boundaries. */
#define ELF_ROUND(a, b) (((a) + (b) - 1) & ~((b) - 1))
#define ELF_TRUNC(a, b) ((a) & ~((b) - 1))
/*
* We limit the number of program headers to 32, this should
* be a reasonable limit for ELF, the most we have seen so far is 12
*/
#define ELF_MAX_VALID_PHDR 32
#define ELF_NOTE_NAME_OPENBSD 0x01
struct elf_note_name {
char *name;
int id;
} elf_note_names[] = {
{ "OpenBSD", ELF_NOTE_NAME_OPENBSD },
};
#define ELFROUNDSIZE sizeof(Elf_Word)
#define elfround(x) roundup((x), ELFROUNDSIZE)
/*
* Check header for validity; return 0 for ok, ENOEXEC if error
*/
int
elf_check_header(Elf_Ehdr *ehdr)
{
/*
* We need to check magic, class size, endianness, and version before
* we look at the rest of the Elf_Ehdr structure. These few elements
* are represented in a machine independent fashion.
*/
if (!IS_ELF(*ehdr) ||
ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
ehdr->e_ident[EI_VERSION] != ELF_TARG_VER)
return (ENOEXEC);
/* Now check the machine dependent header */
if (ehdr->e_machine != ELF_TARG_MACH ||
ehdr->e_version != ELF_TARG_VER)
return (ENOEXEC);
/* Don't allow an insane amount of sections. */
if (ehdr->e_phnum > ELF_MAX_VALID_PHDR)
return (ENOEXEC);
return (0);
}
/*
* Load a psection at the appropriate address
*/
void
elf_load_psection(struct exec_vmcmd_set *vcset, struct vnode *vp,
Elf_Phdr *ph, Elf_Addr *addr, Elf_Addr *size, int *prot, int flags)
{
u_long msize, lsize, psize, rm, rf;
long diff, offset, bdiff;
Elf_Addr base;
/*
* If the user specified an address, then we load there.
*/
if (*addr != ELF_NO_ADDR) {
if (ph->p_align > 1) {
*addr = ELF_TRUNC(*addr, ph->p_align);
diff = ph->p_vaddr - ELF_TRUNC(ph->p_vaddr, ph->p_align);
/* page align vaddr */
base = *addr + trunc_page(ph->p_vaddr)
- ELF_TRUNC(ph->p_vaddr, ph->p_align);
} else {
diff = 0;
base = *addr + trunc_page(ph->p_vaddr) - ph->p_vaddr;
}
} else {
*addr = ph->p_vaddr;
if (ph->p_align > 1)
*addr = ELF_TRUNC(*addr, ph->p_align);
base = trunc_page(ph->p_vaddr);
diff = ph->p_vaddr - *addr;
}
bdiff = ph->p_vaddr - trunc_page(ph->p_vaddr);
/*
* Enforce W^X and map W|X segments without X permission
* initially. The dynamic linker will make these read-only
* and add back X permission after relocation processing.
* Static executables with W|X segments will probably crash.
*/
*prot |= (ph->p_flags & PF_R) ? PROT_READ : 0;
*prot |= (ph->p_flags & PF_W) ? PROT_WRITE : 0;
if ((ph->p_flags & PF_W) == 0)
*prot |= (ph->p_flags & PF_X) ? PROT_EXEC : 0;
msize = ph->p_memsz + diff;
offset = ph->p_offset - bdiff;
lsize = ph->p_filesz + bdiff;
psize = round_page(lsize);
/*
* Because the pagedvn pager can't handle zero fill of the last
* data page if it's not page aligned we map the last page readvn.
*/
if (ph->p_flags & PF_W) {
psize = trunc_page(lsize);
if (psize > 0)
NEW_VMCMD2(vcset, vmcmd_map_pagedvn, psize, base, vp,
offset, *prot, flags);
if (psize != lsize) {
NEW_VMCMD2(vcset, vmcmd_map_readvn, lsize - psize,
base + psize, vp, offset + psize, *prot, flags);
}
} else {
NEW_VMCMD2(vcset, vmcmd_map_pagedvn, psize, base, vp, offset,
*prot, flags);
}
/*
* Check if we need to extend the size of the segment
*/
rm = round_page(*addr + ph->p_memsz + diff);
rf = round_page(*addr + ph->p_filesz + diff);
if (rm != rf) {
NEW_VMCMD2(vcset, vmcmd_map_zero, rm - rf, rf, NULLVP, 0,
*prot, flags);
}
*size = msize;
}
/*
* Read from vnode into buffer at offset.
*/
int
elf_read_from(struct proc *p, struct vnode *vp, u_long off, void *buf,
int size)
{
int error;
size_t resid;
if ((error = vn_rdwr(UIO_READ, vp, buf, size, off, UIO_SYSSPACE,
0, p->p_ucred, &resid, p)) != 0)
return error;
/*
* See if we got all of it
*/
if (resid != 0)
return (ENOEXEC);
return (0);
}
/*
* Load a file (interpreter/library) pointed to by path [stolen from
* coff_load_shlib()]. Made slightly generic so it might be used externally.
*/
int
elf_load_file(struct proc *p, char *path, struct exec_package *epp,
struct elf_args *ap)
{
int error, i;
struct nameidata nd;
Elf_Ehdr eh;
Elf_Phdr *ph = NULL;
u_long phsize = 0;
Elf_Addr addr;
struct vnode *vp;
Elf_Phdr *base_ph = NULL;
struct interp_ld_sec {
Elf_Addr vaddr;
u_long memsz;
} loadmap[ELF_MAX_VALID_PHDR];
int nload, idx = 0;
Elf_Addr pos;
int file_align;
int loop;
size_t randomizequota = ELF_RANDOMIZE_LIMIT;
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, path, p);
nd.ni_pledge = PLEDGE_RPATH;
nd.ni_unveil = UNVEIL_READ;
if ((error = namei(&nd)) != 0) {
return (error);
}
vp = nd.ni_vp;
if (vp->v_type != VREG) {
error = EACCES;
goto bad;
}
if ((error = VOP_GETATTR(vp, epp->ep_vap, p->p_ucred, p)) != 0)
goto bad;
if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
error = EACCES;
goto bad;
}
if ((error = VOP_ACCESS(vp, VREAD, p->p_ucred, p)) != 0)
goto bad1;
if ((error = elf_read_from(p, nd.ni_vp, 0, &eh, sizeof(eh))) != 0)
goto bad1;
if (elf_check_header(&eh) || eh.e_type != ET_DYN) {
error = ENOEXEC;
goto bad1;
}
ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_WAITOK);
phsize = eh.e_phnum * sizeof(Elf_Phdr);
if ((error = elf_read_from(p, nd.ni_vp, eh.e_phoff, ph, phsize)) != 0)
goto bad1;
for (i = 0; i < eh.e_phnum; i++) {
if (ph[i].p_type == PT_LOAD) {
if (ph[i].p_filesz > ph[i].p_memsz ||
ph[i].p_memsz == 0) {
error = EINVAL;
goto bad1;
}
loadmap[idx].vaddr = trunc_page(ph[i].p_vaddr);
loadmap[idx].memsz = round_page (ph[i].p_vaddr +
ph[i].p_memsz - loadmap[idx].vaddr);
file_align = ph[i].p_align;
idx++;
}
}
nload = idx;
/*
* Load the interpreter where a non-fixed mmap(NULL, ...)
* would (i.e. something safely out of the way).
*/
pos = uvm_map_hint(p->p_vmspace, PROT_EXEC, VM_MIN_ADDRESS,
VM_MAXUSER_ADDRESS);
pos = ELF_ROUND(pos, file_align);
loop = 0;
for (i = 0; i < nload;/**/) {
vaddr_t addr;
struct uvm_object *uobj;
off_t uoff;
size_t size;
#ifdef this_needs_fixing
if (i == 0) {
uobj = &vp->v_uvm.u_obj;
/* need to fix uoff */
} else {
#endif
uobj = NULL;
uoff = 0;
#ifdef this_needs_fixing
}
#endif
addr = trunc_page(pos + loadmap[i].vaddr);
size = round_page(addr + loadmap[i].memsz) - addr;
/* CRAP - map_findspace does not avoid daddr+BRKSIZ */
if ((addr + size > (vaddr_t)p->p_vmspace->vm_daddr) &&
(addr < (vaddr_t)p->p_vmspace->vm_daddr + BRKSIZ))
addr = round_page((vaddr_t)p->p_vmspace->vm_daddr +
BRKSIZ);
if (uvm_map_mquery(&p->p_vmspace->vm_map, &addr, size,
(i == 0 ? uoff : UVM_UNKNOWN_OFFSET), 0) != 0) {
if (loop == 0) {
loop = 1;
i = 0;
pos = 0;
continue;
}
error = ENOMEM;
goto bad1;
}
if (addr != pos + loadmap[i].vaddr) {
/* base changed. */
pos = addr - trunc_page(loadmap[i].vaddr);
pos = ELF_ROUND(pos,file_align);
i = 0;
continue;
}
i++;
}
/*
* Load all the necessary sections
*/
for (i = 0; i < eh.e_phnum; i++) {
Elf_Addr size = 0;
int prot = 0;
int flags;
switch (ph[i].p_type) {
case PT_LOAD:
if (base_ph == NULL) {
flags = VMCMD_BASE;
addr = pos;
base_ph = &ph[i];
} else {
flags = VMCMD_RELATIVE;
addr = ph[i].p_vaddr - base_ph->p_vaddr;
}
elf_load_psection(&epp->ep_vmcmds, nd.ni_vp,
&ph[i], &addr, &size, &prot, flags | VMCMD_SYSCALL);
/* If entry is within this section it must be text */
if (eh.e_entry >= ph[i].p_vaddr &&
eh.e_entry < (ph[i].p_vaddr + size)) {
epp->ep_entry = addr + eh.e_entry -
ELF_TRUNC(ph[i].p_vaddr,ph[i].p_align);
if (flags == VMCMD_RELATIVE)
epp->ep_entry += pos;
ap->arg_interp = pos;
}
addr += size;
break;
case PT_DYNAMIC:
case PT_PHDR:
case PT_NOTE:
break;
case PT_OPENBSD_RANDOMIZE:
if (ph[i].p_memsz > randomizequota) {
error = ENOMEM;
goto bad1;
}
randomizequota -= ph[i].p_memsz;
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_randomize,
ph[i].p_memsz, ph[i].p_vaddr + pos, NULLVP, 0, 0);
break;
default:
break;
}
}
vn_marktext(nd.ni_vp);
bad1:
VOP_CLOSE(nd.ni_vp, FREAD, p->p_ucred, p);
bad:
free(ph, M_TEMP, phsize);
vput(nd.ni_vp);
return (error);
}
/*
* Prepare an Elf binary's exec package
*
* First, set of the various offsets/lengths in the exec package.
*
* Then, mark the text image busy (so it can be demand paged) or error out if
* this is not possible. Finally, set up vmcmds for the text, data, bss, and
* stack segments.
*/
int
exec_elf_makecmds(struct proc *p, struct exec_package *epp)
{
Elf_Ehdr *eh = epp->ep_hdr;
Elf_Phdr *ph, *pp, *base_ph = NULL;
Elf_Addr phdr = 0, exe_base = 0;
int error, i, has_phdr = 0, names = 0;
char *interp = NULL;
u_long phsize;
size_t randomizequota = ELF_RANDOMIZE_LIMIT;
if (epp->ep_hdrvalid < sizeof(Elf_Ehdr))
return (ENOEXEC);
if (elf_check_header(eh) ||
(eh->e_type != ET_EXEC && eh->e_type != ET_DYN))
return (ENOEXEC);
/*
* check if vnode is in open for writing, because we want to demand-
* page out of it. if it is, don't do it, for various reasons.
*/
if (epp->ep_vp->v_writecount != 0) {
#ifdef DIAGNOSTIC
if (epp->ep_vp->v_flag & VTEXT)
panic("exec: a VTEXT vnode has writecount != 0");
#endif
return (ETXTBSY);
}
/*
* Allocate space to hold all the program headers, and read them
* from the file
*/
ph = mallocarray(eh->e_phnum, sizeof(Elf_Phdr), M_TEMP, M_WAITOK);
phsize = eh->e_phnum * sizeof(Elf_Phdr);
if ((error = elf_read_from(p, epp->ep_vp, eh->e_phoff, ph,
phsize)) != 0)
goto bad;
epp->ep_tsize = ELF_NO_ADDR;
epp->ep_dsize = ELF_NO_ADDR;
for (i = 0, pp = ph; i < eh->e_phnum; i++, pp++) {
if (pp->p_type == PT_INTERP && !interp) {
if (pp->p_filesz < 2 || pp->p_filesz > MAXPATHLEN)
goto bad;
interp = pool_get(&namei_pool, PR_WAITOK);
if ((error = elf_read_from(p, epp->ep_vp,
pp->p_offset, interp, pp->p_filesz)) != 0) {
goto bad;
}
if (interp[pp->p_filesz - 1] != '\0')
goto bad;
} else if (pp->p_type == PT_LOAD) {
if (pp->p_filesz > pp->p_memsz ||
pp->p_memsz == 0) {
error = EINVAL;
goto bad;
}
if (base_ph == NULL)
base_ph = pp;
} else if (pp->p_type == PT_PHDR) {
has_phdr = 1;
}
}
if (eh->e_type == ET_DYN) {
/* need phdr and load sections for PIE */
if (!has_phdr || base_ph == NULL) {
error = EINVAL;
goto bad;
}
/* randomize exe_base for PIE */
exe_base = uvm_map_pie(base_ph->p_align);
}
/*
* Verify this is an OpenBSD executable. If it's marked that way
* via a PT_NOTE then also check for a PT_OPENBSD_WXNEEDED segment.
*/
if ((error = elf_os_pt_note(p, epp, epp->ep_hdr, &names)) != 0)
goto bad;
if (eh->e_ident[EI_OSABI] == ELFOSABI_OPENBSD)
names |= ELF_NOTE_NAME_OPENBSD;
/*
* Load all the necessary sections
*/
for (i = 0, pp = ph; i < eh->e_phnum; i++, pp++) {
Elf_Addr addr, size = 0;
int prot = 0;
int flags = 0;
switch (pp->p_type) {
case PT_LOAD:
if (exe_base != 0) {
if (pp == base_ph) {
flags = VMCMD_BASE;
addr = exe_base;
} else {
flags = VMCMD_RELATIVE;
addr = pp->p_vaddr - base_ph->p_vaddr;
}
} else
addr = ELF_NO_ADDR;
/* Permit system calls in specific main-programs */
if (interp == NULL) {
/* statics. Also block the ld.so syscall-grant */
flags |= VMCMD_SYSCALL;
p->p_vmspace->vm_map.flags |= VM_MAP_SYSCALL_ONCE;
}
/*
* Calculates size of text and data segments
* by starting at first and going to end of last.
* 'rwx' sections are treated as data.
* this is correct for BSS_PLT, but may not be
* for DATA_PLT, is fine for TEXT_PLT.
*/
elf_load_psection(&epp->ep_vmcmds, epp->ep_vp,
pp, &addr, &size, &prot, flags);
/*
* Update exe_base in case alignment was off.
* For PIE, addr is relative to exe_base so
* adjust it (non PIE exe_base is 0 so no change).
*/
if (flags == VMCMD_BASE)
exe_base = addr;
else
addr += exe_base;
/*
* Decide whether it's text or data by looking
* at the protection of the section
*/
if (prot & PROT_WRITE) {
/* data section */
if (epp->ep_dsize == ELF_NO_ADDR) {
epp->ep_daddr = addr;
epp->ep_dsize = size;
} else {
if (addr < epp->ep_daddr) {
epp->ep_dsize =
epp->ep_dsize +
epp->ep_daddr -
addr;
epp->ep_daddr = addr;
} else
epp->ep_dsize = addr+size -
epp->ep_daddr;
}
} else if (prot & PROT_EXEC) {
/* text section */
if (epp->ep_tsize == ELF_NO_ADDR) {
epp->ep_taddr = addr;
epp->ep_tsize = size;
} else {
if (addr < epp->ep_taddr) {
epp->ep_tsize =
epp->ep_tsize +
epp->ep_taddr -
addr;
epp->ep_taddr = addr;
} else
epp->ep_tsize = addr+size -
epp->ep_taddr;
}
}
break;
case PT_SHLIB:
error = ENOEXEC;
goto bad;
case PT_INTERP:
/* Already did this one */
case PT_DYNAMIC:
case PT_NOTE:
break;
case PT_PHDR:
/* Note address of program headers (in text segment) */
phdr = pp->p_vaddr;
break;
case PT_OPENBSD_RANDOMIZE:
if (ph[i].p_memsz > randomizequota) {
error = ENOMEM;
goto bad;
}
randomizequota -= ph[i].p_memsz;
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_randomize,
ph[i].p_memsz, ph[i].p_vaddr + exe_base, NULLVP, 0, 0);
break;
default:
/*
* Not fatal, we don't need to understand everything
* :-)
*/
break;
}
}
phdr += exe_base;
/*
* Strangely some linux programs may have all load sections marked
* writeable, in this case, textsize is not -1, but rather 0;
*/
if (epp->ep_tsize == ELF_NO_ADDR)
epp->ep_tsize = 0;
/*
* Another possibility is that it has all load sections marked
* read-only. Fake a zero-sized data segment right after the
* text segment.
*/
if (epp->ep_dsize == ELF_NO_ADDR) {
epp->ep_daddr = round_page(epp->ep_taddr + epp->ep_tsize);
epp->ep_dsize = 0;
}
epp->ep_interp = interp;
epp->ep_entry = eh->e_entry + exe_base;
/*
* Check if we found a dynamically linked binary and arrange to load
* its interpreter when the exec file is released.
*/
if (interp || eh->e_type == ET_DYN) {
struct elf_args *ap;
ap = malloc(sizeof(*ap), M_TEMP, M_WAITOK);
ap->arg_phaddr = phdr;
ap->arg_phentsize = eh->e_phentsize;
ap->arg_phnum = eh->e_phnum;
ap->arg_entry = eh->e_entry + exe_base;
ap->arg_interp = exe_base;
epp->ep_args = ap;
}
free(ph, M_TEMP, phsize);
vn_marktext(epp->ep_vp);
return (exec_setup_stack(p, epp));
bad:
if (interp)
pool_put(&namei_pool, interp);
free(ph, M_TEMP, phsize);
kill_vmcmds(&epp->ep_vmcmds);
if (error == 0)
return (ENOEXEC);
return (error);
}
/*
* Phase II of load. It is now safe to load the interpreter. Info collected
* when loading the program is available for setup of the interpreter.
*/
int
exec_elf_fixup(struct proc *p, struct exec_package *epp)
{
char *interp;
int error = 0;
struct elf_args *ap;
AuxInfo ai[ELF_AUX_ENTRIES], *a;
ap = epp->ep_args;
if (ap == NULL) {
return (0);
}
interp = epp->ep_interp;
if (interp &&
(error = elf_load_file(p, interp, epp, ap)) != 0) {
uprintf("execve: cannot load %s\n", interp);
free(ap, M_TEMP, sizeof *ap);
pool_put(&namei_pool, interp);
kill_vmcmds(&epp->ep_vmcmds);
return (error);
}
/*
* We have to do this ourselves...
*/
error = exec_process_vmcmds(p, epp);
/*
* Push extra arguments on the stack needed by dynamically
* linked binaries
*/
if (error == 0) {
memset(&ai, 0, sizeof ai);
a = ai;
a->au_id = AUX_phdr;
a->au_v = ap->arg_phaddr;
a++;
a->au_id = AUX_phent;
a->au_v = ap->arg_phentsize;
a++;
a->au_id = AUX_phnum;
a->au_v = ap->arg_phnum;
a++;
a->au_id = AUX_pagesz;
a->au_v = PAGE_SIZE;
a++;
a->au_id = AUX_base;
a->au_v = ap->arg_interp;
a++;
a->au_id = AUX_flags;
a->au_v = 0;
a++;
a->au_id = AUX_entry;
a->au_v = ap->arg_entry;
a++;
a->au_id = AUX_openbsd_timekeep;
a->au_v = p->p_p->ps_timekeep;
a++;
a->au_id = AUX_null;
a->au_v = 0;
a++;
error = copyout(ai, epp->ep_auxinfo, sizeof ai);
}
free(ap, M_TEMP, sizeof *ap);
if (interp)
pool_put(&namei_pool, interp);
return (error);
}
int
elf_os_pt_note_name(Elf_Note *np)
{
int i, j;
for (i = 0; i < nitems(elf_note_names); i++) {
size_t namlen = strlen(elf_note_names[i].name);
if (np->namesz < namlen)
continue;
/* verify name padding (after the NUL) is NUL */
for (j = namlen + 1; j < elfround(np->namesz); j++)
if (((char *)(np + 1))[j] != '\0')
continue;
/* verify desc padding is NUL */
for (j = np->descsz; j < elfround(np->descsz); j++)
if (((char *)(np + 1))[j] != '\0')
continue;
if (strcmp((char *)(np + 1), elf_note_names[i].name) == 0)
return elf_note_names[i].id;
}
return (0);
}
int
elf_os_pt_note(struct proc *p, struct exec_package *epp, Elf_Ehdr *eh, int *namesp)
{
Elf_Phdr *hph, *ph;
Elf_Note *np = NULL;
size_t phsize, offset, pfilesz = 0, total;
int error, names = 0;
hph = mallocarray(eh->e_phnum, sizeof(Elf_Phdr), M_TEMP, M_WAITOK);
phsize = eh->e_phnum * sizeof(Elf_Phdr);
if ((error = elf_read_from(p, epp->ep_vp, eh->e_phoff,
hph, phsize)) != 0)
goto out1;
for (ph = hph; ph < &hph[eh->e_phnum]; ph++) {
if (ph->p_type == PT_OPENBSD_WXNEEDED) {
epp->ep_flags |= EXEC_WXNEEDED;
continue;
}
if (ph->p_type != PT_NOTE || ph->p_filesz > 1024)
continue;
if (np && ph->p_filesz != pfilesz) {
free(np, M_TEMP, pfilesz);
np = NULL;
}
if (!np)
np = malloc(ph->p_filesz, M_TEMP, M_WAITOK);
pfilesz = ph->p_filesz;
if ((error = elf_read_from(p, epp->ep_vp, ph->p_offset,
np, ph->p_filesz)) != 0)
goto out2;
for (offset = 0; offset < ph->p_filesz; offset += total) {
Elf_Note *np2 = (Elf_Note *)((char *)np + offset);
if (offset + sizeof(Elf_Note) > ph->p_filesz)
break;
total = sizeof(Elf_Note) + elfround(np2->namesz) +
elfround(np2->descsz);
if (offset + total > ph->p_filesz)
break;
names |= elf_os_pt_note_name(np2);
}
}
out2:
free(np, M_TEMP, pfilesz);
out1:
free(hph, M_TEMP, phsize);
*namesp = names;
return ((names & ELF_NOTE_NAME_OPENBSD) ? 0 : ENOEXEC);
}
/*
* Start of routines related to dumping core
*/
#ifdef SMALL_KERNEL
int
coredump_elf(struct proc *p, void *cookie)
{
return EPERM;
}
#else /* !SMALL_KERNEL */
struct writesegs_state {
off_t notestart;
off_t secstart;
off_t secoff;
struct proc *p;
void *iocookie;
Elf_Phdr *psections;
size_t psectionslen;
size_t notesize;
int npsections;
};
uvm_coredump_setup_cb coredump_setup_elf;
uvm_coredump_walk_cb coredump_walk_elf;
int coredump_notes_elf(struct proc *, void *, size_t *);
int coredump_note_elf(struct proc *, void *, size_t *);
int coredump_writenote_elf(struct proc *, void *, Elf_Note *,
const char *, void *);
int
coredump_elf(struct proc *p, void *cookie)
{
#ifdef DIAGNOSTIC
off_t offset;
#endif
struct writesegs_state ws;
size_t notesize;
int error, i;
ws.p = p;
ws.iocookie = cookie;
ws.psections = NULL;
/*
* Walk the map to get all the segment offsets and lengths,
* write out the ELF header.
*/
error = uvm_coredump_walkmap(p, coredump_setup_elf,
coredump_walk_elf, &ws);
if (error)
goto out;
error = coredump_write(cookie, UIO_SYSSPACE, ws.psections,
ws.psectionslen);
if (error)
goto out;
/* Write out the notes. */
error = coredump_notes_elf(p, cookie, ¬esize);
if (error)
goto out;
#ifdef DIAGNOSTIC
if (notesize != ws.notesize)
panic("coredump: notesize changed: %zu != %zu",
ws.notesize, notesize);
offset = ws.notestart + notesize;
if (offset != ws.secstart)
panic("coredump: offset %lld != secstart %lld",
(long long) offset, (long long) ws.secstart);
#endif
/* Pass 3: finally, write the sections themselves. */
for (i = 0; i < ws.npsections - 1; i++) {
Elf_Phdr *pent = &ws.psections[i];
if (pent->p_filesz == 0)
continue;
#ifdef DIAGNOSTIC
if (offset != pent->p_offset)
panic("coredump: offset %lld != p_offset[%d] %lld",
(long long) offset, i,
(long long) pent->p_filesz);
#endif
error = coredump_write(cookie, UIO_USERSPACE,
(void *)(vaddr_t)pent->p_vaddr, pent->p_filesz);
if (error)
goto out;
coredump_unmap(cookie, (vaddr_t)pent->p_vaddr,
(vaddr_t)pent->p_vaddr + pent->p_filesz);
#ifdef DIAGNOSTIC
offset += ws.psections[i].p_filesz;
#endif
}
out:
free(ws.psections, M_TEMP, ws.psectionslen);
return (error);
}
/*
* Normally we lay out core files like this:
* [ELF Header] [Program headers] [Notes] [data for PT_LOAD segments]
*
* However, if there's >= 65535 segments then it overflows the field
* in the ELF header, so the standard specifies putting a magic
* number there and saving the real count in the .sh_info field of
* the first *section* header...which requires generating a section
* header. To avoid confusing tools, we include an .shstrtab section
* as well so all the indexes look valid. So in this case we lay
* out the core file like this:
* [ELF Header] [Section Headers] [.shstrtab] [Program headers] \
* [Notes] [data for PT_LOAD segments]
*
* The 'shstrtab' structure below is data for the second of the two
* section headers, plus the .shstrtab itself, in one const buffer.
*/
static const struct {
Elf_Shdr shdr;
char shstrtab[sizeof(ELF_SHSTRTAB) + 1];
} shstrtab = {
.shdr = {
.sh_name = 1, /* offset in .shstrtab below */
.sh_type = SHT_STRTAB,
.sh_offset = sizeof(Elf_Ehdr) + 2*sizeof(Elf_Shdr),
.sh_size = sizeof(ELF_SHSTRTAB) + 1,
.sh_addralign = 1,
},
.shstrtab = "\0" ELF_SHSTRTAB,
};
int
coredump_setup_elf(int segment_count, void *cookie)
{
Elf_Ehdr ehdr;
struct writesegs_state *ws = cookie;
Elf_Phdr *note;
int error;
/* Get the count of segments, plus one for the PT_NOTE */
ws->npsections = segment_count + 1;
/* Get the size of the notes. */
error = coredump_notes_elf(ws->p, NULL, &ws->notesize);
if (error)
return error;
/* Setup the ELF header */
memset(&ehdr, 0, sizeof(ehdr));
memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
ehdr.e_ident[EI_CLASS] = ELF_TARG_CLASS;
ehdr.e_ident[EI_DATA] = ELF_TARG_DATA;
ehdr.e_ident[EI_VERSION] = EV_CURRENT;
/* XXX Should be the OSABI/ABI version of the executable. */
ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV;
ehdr.e_ident[EI_ABIVERSION] = 0;
ehdr.e_type = ET_CORE;
/* XXX This should be the e_machine of the executable. */
ehdr.e_machine = ELF_TARG_MACH;
ehdr.e_version = EV_CURRENT;
ehdr.e_entry = 0;
ehdr.e_flags = 0;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf_Phdr);
if (ws->npsections < PN_XNUM) {
ehdr.e_phoff = sizeof(ehdr);
ehdr.e_shoff = 0;
ehdr.e_phnum = ws->npsections;
ehdr.e_shentsize = 0;
ehdr.e_shnum = 0;
ehdr.e_shstrndx = 0;
} else {
/* too many segments, use extension setup */
ehdr.e_shoff = sizeof(ehdr);
ehdr.e_phnum = PN_XNUM;
ehdr.e_shentsize = sizeof(Elf_Shdr);
ehdr.e_shnum = 2;
ehdr.e_shstrndx = 1;
ehdr.e_phoff = shstrtab.shdr.sh_offset + shstrtab.shdr.sh_size;
}
/* Write out the ELF header. */
error = coredump_write(ws->iocookie, UIO_SYSSPACE, &ehdr, sizeof(ehdr));
if (error)
return error;
/*
* If an section header is needed to store extension info, write
* it out after the ELF header and before the program header.
*/
if (ehdr.e_shnum != 0) {
Elf_Shdr shdr = { .sh_info = ws->npsections };
error = coredump_write(ws->iocookie, UIO_SYSSPACE, &shdr,
sizeof shdr);
if (error)
return error;
error = coredump_write(ws->iocookie, UIO_SYSSPACE, &shstrtab,
sizeof(shstrtab.shdr) + sizeof(shstrtab.shstrtab));
if (error)
return error;
}
/*
* Allocate the segment header array and setup to collect
* the section sizes and offsets
*/
ws->psections = mallocarray(ws->npsections, sizeof(Elf_Phdr),
M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO);
if (ws->psections == NULL)
return ENOMEM;
ws->psectionslen = ws->npsections * sizeof(Elf_Phdr);
ws->notestart = ehdr.e_phoff + ws->psectionslen;
ws->secstart = ws->notestart + ws->notesize;
ws->secoff = ws->secstart;
/* Fill in the PT_NOTE segment header in the last slot */
note = &ws->psections[ws->npsections - 1];
note->p_type = PT_NOTE;
note->p_offset = ws->notestart;
note->p_vaddr = 0;
note->p_paddr = 0;
note->p_filesz = ws->notesize;
note->p_memsz = 0;
note->p_flags = PF_R;
note->p_align = ELFROUNDSIZE;
return (0);
}
int
coredump_walk_elf(vaddr_t start, vaddr_t realend, vaddr_t end, vm_prot_t prot,
int nsegment, void *cookie)
{
struct writesegs_state *ws = cookie;
Elf_Phdr phdr;
vsize_t size, realsize;
size = end - start;
realsize = realend - start;
phdr.p_type = PT_LOAD;
phdr.p_offset = ws->secoff;
phdr.p_vaddr = start;
phdr.p_paddr = 0;
phdr.p_filesz = realsize;
phdr.p_memsz = size;
phdr.p_flags = 0;
if (prot & PROT_READ)
phdr.p_flags |= PF_R;
if (prot & PROT_WRITE)
phdr.p_flags |= PF_W;
if (prot & PROT_EXEC)
phdr.p_flags |= PF_X;
phdr.p_align = PAGE_SIZE;
ws->secoff += phdr.p_filesz;
ws->psections[nsegment] = phdr;
return (0);
}
int
coredump_notes_elf(struct proc *p, void *iocookie, size_t *sizep)
{
struct ps_strings pss;
struct iovec iov;
struct uio uio;
struct elfcore_procinfo cpi;
Elf_Note nhdr;
struct process *pr = p->p_p;
struct proc *q;
size_t size, notesize;
int error;
KASSERT(!P_HASSIBLING(p) || pr->ps_single != NULL);
size = 0;
/* First, write an elfcore_procinfo. */
notesize = sizeof(nhdr) + elfround(sizeof("OpenBSD")) +
elfround(sizeof(cpi));
if (iocookie) {
memset(&cpi, 0, sizeof(cpi));
cpi.cpi_version = ELFCORE_PROCINFO_VERSION;
cpi.cpi_cpisize = sizeof(cpi);
cpi.cpi_signo = p->p_sisig;
cpi.cpi_sigcode = p->p_sicode;
cpi.cpi_sigpend = p->p_siglist | pr->ps_siglist;
cpi.cpi_sigmask = p->p_sigmask;
cpi.cpi_sigignore = pr->ps_sigacts->ps_sigignore;
cpi.cpi_sigcatch = pr->ps_sigacts->ps_sigcatch;
cpi.cpi_pid = pr->ps_pid;
cpi.cpi_ppid = pr->ps_ppid;
cpi.cpi_pgrp = pr->ps_pgid;
if (pr->ps_session->s_leader)
cpi.cpi_sid = pr->ps_session->s_leader->ps_pid;
else
cpi.cpi_sid = 0;
cpi.cpi_ruid = p->p_ucred->cr_ruid;
cpi.cpi_euid = p->p_ucred->cr_uid;
cpi.cpi_svuid = p->p_ucred->cr_svuid;
cpi.cpi_rgid = p->p_ucred->cr_rgid;
cpi.cpi_egid = p->p_ucred->cr_gid;
cpi.cpi_svgid = p->p_ucred->cr_svgid;
(void)strlcpy(cpi.cpi_name, pr->ps_comm, sizeof(cpi.cpi_name));
nhdr.namesz = sizeof("OpenBSD");
nhdr.descsz = sizeof(cpi);
nhdr.type = NT_OPENBSD_PROCINFO;
error = coredump_writenote_elf(p, iocookie, &nhdr,
"OpenBSD", &cpi);
if (error)
return (error);
}
size += notesize;
/* Second, write an NT_OPENBSD_AUXV note. */
notesize = sizeof(nhdr) + elfround(sizeof("OpenBSD")) +
elfround(ELF_AUX_WORDS * sizeof(char *));
if (iocookie) {
iov.iov_base = &pss;
iov.iov_len = sizeof(pss);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)pr->ps_strings;
uio.uio_resid = sizeof(pss);
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = NULL;
error = uvm_io(&p->p_vmspace->vm_map, &uio, 0);
if (error)
return (error);
if (pss.ps_envstr == NULL)
return (EIO);
nhdr.namesz = sizeof("OpenBSD");
nhdr.descsz = ELF_AUX_WORDS * sizeof(char *);
nhdr.type = NT_OPENBSD_AUXV;
error = coredump_write(iocookie, UIO_SYSSPACE,
&nhdr, sizeof(nhdr));
if (error)
return (error);
error = coredump_write(iocookie, UIO_SYSSPACE,
"OpenBSD", elfround(nhdr.namesz));
if (error)
return (error);
error = coredump_write(iocookie, UIO_USERSPACE,
pss.ps_envstr + pss.ps_nenvstr + 1, nhdr.descsz);
if (error)
return (error);
}
size += notesize;
#ifdef PT_WCOOKIE
notesize = sizeof(nhdr) + elfround(sizeof("OpenBSD")) +
elfround(sizeof(register_t));
if (iocookie) {
register_t wcookie;
nhdr.namesz = sizeof("OpenBSD");
nhdr.descsz = sizeof(register_t);
nhdr.type = NT_OPENBSD_WCOOKIE;
wcookie = process_get_wcookie(p);
error = coredump_writenote_elf(p, iocookie, &nhdr,
"OpenBSD", &wcookie);
if (error)
return (error);
}
size += notesize;
#endif
/*
* Now write the register info for the thread that caused the
* coredump.
*/
error = coredump_note_elf(p, iocookie, ¬esize);
if (error)
return (error);
size += notesize;
/*
* Now, for each thread, write the register info and any other
* per-thread notes. Since we're dumping core, all the other
* threads in the process have been stopped and the list can't
* change.
*/
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
if (q == p) /* we've taken care of this thread */
continue;
error = coredump_note_elf(q, iocookie, ¬esize);
if (error)
return (error);
size += notesize;
}
*sizep = size;
return (0);
}
int
coredump_note_elf(struct proc *p, void *iocookie, size_t *sizep)
{
Elf_Note nhdr;
int size, notesize, error;
int namesize;
char name[64+ELFROUNDSIZE];
struct reg intreg;
#ifdef PT_GETFPREGS
struct fpreg freg;
#endif
size = 0;
snprintf(name, sizeof(name)-ELFROUNDSIZE, "%s@%d",
"OpenBSD", p->p_tid + THREAD_PID_OFFSET);
namesize = strlen(name) + 1;
memset(name + namesize, 0, elfround(namesize) - namesize);
notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(intreg));
if (iocookie) {
error = process_read_regs(p, &intreg);
if (error)
return (error);
nhdr.namesz = namesize;
nhdr.descsz = sizeof(intreg);
nhdr.type = NT_OPENBSD_REGS;
error = coredump_writenote_elf(p, iocookie, &nhdr,
name, &intreg);
if (error)
return (error);
}
size += notesize;
#ifdef PT_GETFPREGS
notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(freg));
if (iocookie) {
error = process_read_fpregs(p, &freg);
if (error)
return (error);
nhdr.namesz = namesize;
nhdr.descsz = sizeof(freg);
nhdr.type = NT_OPENBSD_FPREGS;
error = coredump_writenote_elf(p, iocookie, &nhdr, name, &freg);
if (error)
return (error);
}
size += notesize;
#endif
*sizep = size;
/* XXX Add hook for machdep per-LWP notes. */
return (0);
}
int
coredump_writenote_elf(struct proc *p, void *cookie, Elf_Note *nhdr,
const char *name, void *data)
{
int error;
error = coredump_write(cookie, UIO_SYSSPACE, nhdr, sizeof(*nhdr));
if (error)
return error;
error = coredump_write(cookie, UIO_SYSSPACE, name,
elfround(nhdr->namesz));
if (error)
return error;
return coredump_write(cookie, UIO_SYSSPACE, data, nhdr->descsz);
}
#endif /* !SMALL_KERNEL */
695
8478
1083
8455
8466
1084
8464
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/* $OpenBSD: kern_lock.c,v 1.72 2022/04/26 15:31:14 dv Exp $ */
/*
* Copyright (c) 2017 Visa Hankala
* Copyright (c) 2014 David Gwynne <dlg@openbsd.org>
* Copyright (c) 2004 Artur Grabowski <art@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sched.h>
#include <sys/atomic.h>
#include <sys/witness.h>
#include <sys/mutex.h>
#include <ddb/db_output.h>
#ifdef MP_LOCKDEBUG
#ifndef DDB
#error "MP_LOCKDEBUG requires DDB"
#endif
/* CPU-dependent timing, this needs to be settable from ddb. */
int __mp_lock_spinout = INT_MAX;
#endif /* MP_LOCKDEBUG */
#ifdef MULTIPROCESSOR
#include <sys/mplock.h>
struct __mp_lock kernel_lock;
/*
* Functions for manipulating the kernel_lock. We put them here
* so that they show up in profiles.
*/
void
_kernel_lock_init(void)
{
__mp_lock_init(&kernel_lock);
}
/*
* Acquire/release the kernel lock. Intended for use in the scheduler
* and the lower half of the kernel.
*/
void
_kernel_lock(void)
{
SCHED_ASSERT_UNLOCKED();
__mp_lock(&kernel_lock);
}
void
_kernel_unlock(void)
{
__mp_unlock(&kernel_lock);
}
int
_kernel_lock_held(void)
{
if (panicstr || db_active)
return 1;
return (__mp_lock_held(&kernel_lock, curcpu()));
}
#ifdef __USE_MI_MPLOCK
/* Ticket lock implementation */
#include <machine/cpu.h>
void
___mp_lock_init(struct __mp_lock *mpl, const struct lock_type *type)
{
memset(mpl->mpl_cpus, 0, sizeof(mpl->mpl_cpus));
mpl->mpl_users = 0;
mpl->mpl_ticket = 1;
#ifdef WITNESS
mpl->mpl_lock_obj.lo_name = type->lt_name;
mpl->mpl_lock_obj.lo_type = type;
if (mpl == &kernel_lock)
mpl->mpl_lock_obj.lo_flags = LO_WITNESS | LO_INITIALIZED |
LO_SLEEPABLE | (LO_CLASS_KERNEL_LOCK << LO_CLASSSHIFT);
else if (mpl == &sched_lock)
mpl->mpl_lock_obj.lo_flags = LO_WITNESS | LO_INITIALIZED |
LO_RECURSABLE | (LO_CLASS_SCHED_LOCK << LO_CLASSSHIFT);
WITNESS_INIT(&mpl->mpl_lock_obj, type);
#endif
}
static __inline void
__mp_lock_spin(struct __mp_lock *mpl, u_int me)
{
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
#ifdef MP_LOCKDEBUG
int nticks = __mp_lock_spinout;
#endif
spc->spc_spinning++;
while (mpl->mpl_ticket != me) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: %p lock spun out\n", __func__, mpl);
db_enter();
nticks = __mp_lock_spinout;
}
#endif
}
spc->spc_spinning--;
}
void
__mp_lock(struct __mp_lock *mpl)
{
struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
unsigned long s;
#ifdef WITNESS
if (!__mp_lock_held(mpl, curcpu()))
WITNESS_CHECKORDER(&mpl->mpl_lock_obj,
LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
#endif
s = intr_disable();
if (cpu->mplc_depth++ == 0)
cpu->mplc_ticket = atomic_inc_int_nv(&mpl->mpl_users);
intr_restore(s);
__mp_lock_spin(mpl, cpu->mplc_ticket);
membar_enter_after_atomic();
WITNESS_LOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
}
void
__mp_unlock(struct __mp_lock *mpl)
{
struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
unsigned long s;
#ifdef MP_LOCKDEBUG
if (!__mp_lock_held(mpl, curcpu())) {
db_printf("__mp_unlock(%p): not held lock\n", mpl);
db_enter();
}
#endif
WITNESS_UNLOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
s = intr_disable();
if (--cpu->mplc_depth == 0) {
membar_exit();
mpl->mpl_ticket++;
}
intr_restore(s);
}
int
__mp_release_all(struct __mp_lock *mpl)
{
struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
unsigned long s;
int rv;
#ifdef WITNESS
int i;
#endif
s = intr_disable();
rv = cpu->mplc_depth;
#ifdef WITNESS
for (i = 0; i < rv; i++)
WITNESS_UNLOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
#endif
cpu->mplc_depth = 0;
membar_exit();
mpl->mpl_ticket++;
intr_restore(s);
return (rv);
}
int
__mp_release_all_but_one(struct __mp_lock *mpl)
{
struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[cpu_number()];
int rv = cpu->mplc_depth - 1;
#ifdef WITNESS
int i;
for (i = 0; i < rv; i++)
WITNESS_UNLOCK(&mpl->mpl_lock_obj, LOP_EXCLUSIVE);
#endif
#ifdef MP_LOCKDEBUG
if (!__mp_lock_held(mpl, curcpu())) {
db_printf("__mp_release_all_but_one(%p): not held lock\n", mpl);
db_enter();
}
#endif
cpu->mplc_depth = 1;
return (rv);
}
void
__mp_acquire_count(struct __mp_lock *mpl, int count)
{
while (count--)
__mp_lock(mpl);
}
int
__mp_lock_held(struct __mp_lock *mpl, struct cpu_info *ci)
{
struct __mp_lock_cpu *cpu = &mpl->mpl_cpus[CPU_INFO_UNIT(ci)];
return (cpu->mplc_ticket == mpl->mpl_ticket && cpu->mplc_depth > 0);
}
#endif /* __USE_MI_MPLOCK */
#endif /* MULTIPROCESSOR */
#ifdef __USE_MI_MUTEX
void
__mtx_init(struct mutex *mtx, int wantipl)
{
mtx->mtx_owner = NULL;
mtx->mtx_wantipl = wantipl;
mtx->mtx_oldipl = IPL_NONE;
}
#ifdef MULTIPROCESSOR
void
mtx_enter(struct mutex *mtx)
{
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
#ifdef MP_LOCKDEBUG
int nticks = __mp_lock_spinout;
#endif
WITNESS_CHECKORDER(MUTEX_LOCK_OBJECT(mtx),
LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
spc->spc_spinning++;
while (mtx_enter_try(mtx) == 0) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks == 0) {
db_printf("%s: %p lock spun out\n", __func__, mtx);
db_enter();
nticks = __mp_lock_spinout;
}
#endif
}
spc->spc_spinning--;
}
int
mtx_enter_try(struct mutex *mtx)
{
struct cpu_info *owner, *ci = curcpu();
int s;
/* Avoid deadlocks after panic or in DDB */
if (panicstr || db_active)
return (1);
if (mtx->mtx_wantipl != IPL_NONE)
s = splraise(mtx->mtx_wantipl);
owner = atomic_cas_ptr(&mtx->mtx_owner, NULL, ci);
#ifdef DIAGNOSTIC
if (__predict_false(owner == ci))
panic("mtx %p: locking against myself", mtx);
#endif
if (owner == NULL) {
membar_enter_after_atomic();
if (mtx->mtx_wantipl != IPL_NONE)
mtx->mtx_oldipl = s;
#ifdef DIAGNOSTIC
ci->ci_mutex_level++;
#endif
WITNESS_LOCK(MUTEX_LOCK_OBJECT(mtx), LOP_EXCLUSIVE);
return (1);
}
if (mtx->mtx_wantipl != IPL_NONE)
splx(s);
return (0);
}
#else
void
mtx_enter(struct mutex *mtx)
{
struct cpu_info *ci = curcpu();
/* Avoid deadlocks after panic or in DDB */
if (panicstr || db_active)
return;
WITNESS_CHECKORDER(MUTEX_LOCK_OBJECT(mtx),
LOP_EXCLUSIVE | LOP_NEWORDER, NULL);
#ifdef DIAGNOSTIC
if (__predict_false(mtx->mtx_owner == ci))
panic("mtx %p: locking against myself", mtx);
#endif
if (mtx->mtx_wantipl != IPL_NONE)
mtx->mtx_oldipl = splraise(mtx->mtx_wantipl);
mtx->mtx_owner = ci;
#ifdef DIAGNOSTIC
ci->ci_mutex_level++;
#endif
WITNESS_LOCK(MUTEX_LOCK_OBJECT(mtx), LOP_EXCLUSIVE);
}
int
mtx_enter_try(struct mutex *mtx)
{
mtx_enter(mtx);
return (1);
}
#endif
void
mtx_leave(struct mutex *mtx)
{
int s;
/* Avoid deadlocks after panic or in DDB */
if (panicstr || db_active)
return;
MUTEX_ASSERT_LOCKED(mtx);
WITNESS_UNLOCK(MUTEX_LOCK_OBJECT(mtx), LOP_EXCLUSIVE);
#ifdef DIAGNOSTIC
curcpu()->ci_mutex_level--;
#endif
s = mtx->mtx_oldipl;
#ifdef MULTIPROCESSOR
membar_exit();
#endif
mtx->mtx_owner = NULL;
if (mtx->mtx_wantipl != IPL_NONE)
splx(s);
}
#ifdef DDB
void
db_mtx_enter(struct db_mutex *mtx)
{
struct cpu_info *ci = curcpu(), *owner;
unsigned long s;
#ifdef DIAGNOSTIC
if (__predict_false(mtx->mtx_owner == ci))
panic("%s: mtx %p: locking against myself", __func__, mtx);
#endif
s = intr_disable();
for (;;) {
owner = atomic_cas_ptr(&mtx->mtx_owner, NULL, ci);
if (owner == NULL)
break;
CPU_BUSY_CYCLE();
}
membar_enter_after_atomic();
mtx->mtx_intr_state = s;
#ifdef DIAGNOSTIC
ci->ci_mutex_level++;
#endif
}
void
db_mtx_leave(struct db_mutex *mtx)
{
#ifdef DIAGNOSTIC
struct cpu_info *ci = curcpu();
#endif
unsigned long s;
#ifdef DIAGNOSTIC
if (__predict_false(mtx->mtx_owner != ci))
panic("%s: mtx %p: not owned by this CPU", __func__, mtx);
ci->ci_mutex_level--;
#endif
s = mtx->mtx_intr_state;
#ifdef MULTIPROCESSOR
membar_exit();
#endif
mtx->mtx_owner = NULL;
intr_restore(s);
}
#endif /* DDB */
#endif /* __USE_MI_MUTEX */
#ifdef WITNESS
void
_mtx_init_flags(struct mutex *m, int ipl, const char *name, int flags,
const struct lock_type *type)
{
struct lock_object *lo = MUTEX_LOCK_OBJECT(m);
lo->lo_flags = MTX_LO_FLAGS(flags);
if (name != NULL)
lo->lo_name = name;
else
lo->lo_name = type->lt_name;
WITNESS_INIT(lo, type);
_mtx_init(m, ipl);
}
#endif /* WITNESS */
64
3
3
63
64
3
4
90
71
60
8
56
13
11
43
54
12
35
35
3
21
9
12
10
43
43
42
13
44
46
41
45
5
1
2
91
1
2
2
26
26
2
2
1
10
6
8
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
/* $OpenBSD: spkr.c,v 1.27 2022/04/06 18:59:29 naddy Exp $ */
/* $NetBSD: spkr.c,v 1.1 1998/04/15 20:26:18 drochner Exp $ */
/*
* Copyright (c) 1990 Eric S. Raymond (esr@snark.thyrsus.com)
* Copyright (c) 1990 Andrew A. Chernov (ache@astral.msk.su)
* Copyright (c) 1990 Lennart Augustsson (lennart@augustsson.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Eric S. Raymond
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* spkr.c -- device driver for console speaker on 80386
*
* v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
* modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
* 386bsd only clean version, all SYSV stuff removed
* use hz value from param.c
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <dev/isa/pcppivar.h>
#include <dev/isa/spkrio.h>
cdev_decl(spkr);
int spkrprobe(struct device *, void *, void *);
void spkrattach(struct device *, struct device *, void *);
const struct cfattach spkr_ca = {
sizeof(struct device), spkrprobe, spkrattach
};
struct cfdriver spkr_cd = {
NULL, "spkr", DV_DULL
};
static pcppi_tag_t ppicookie;
#define SPKRPRI (PZERO - 1)
static void tone(u_int, u_int);
static void rest(int);
static void playinit(void);
static void playtone(int, int, int);
static void playstring(char *, size_t);
/* emit tone of frequency freq for given number of milliseconds */
static void
tone(u_int freq, u_int ms)
{
pcppi_bell(ppicookie, freq, ms, PCPPI_BELL_SLEEP);
}
/* rest for given number of milliseconds */
static void
rest(int ms)
{
/*
* Set timeout to endrest function, then give up the timeslice.
* This is so other processes can execute while the rest is being
* waited out.
*/
#ifdef SPKRDEBUG
printf("rest: %dms\n", ms);
#endif /* SPKRDEBUG */
if (ms > 0)
tsleep_nsec(rest, SPKRPRI | PCATCH, "rest", MSEC_TO_NSEC(ms));
}
/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
*
* Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
* M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
* Requires tone(), rest(), and endtone(). String play is not interruptible
* except possibly at physical block boundaries.
*/
#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
#define dtoi(c) ((c) - '0')
static int octave; /* currently selected octave */
static int whole; /* whole-note time at current tempo, in milliseconds */
static int value; /* whole divisor for note time, quarter note = 1 */
static int fill; /* controls spacing of notes */
static int octtrack; /* octave-tracking on? */
static int octprefix; /* override current octave-tracking state? */
/*
* Magic number avoidance...
*/
#define SECS_PER_MIN 60 /* seconds per minute */
#define WHOLE_NOTE 4 /* quarter notes per whole note */
#define MIN_VALUE 64 /* the most we can divide a note by */
#define DFLT_VALUE 4 /* default value (quarter-note) */
#define FILLTIME 8 /* for articulation, break note in parts */
#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
#define NORMAL 7 /* 7/8ths of note interval is filled */
#define LEGATO 8 /* all of note interval is filled */
#define DFLT_OCTAVE 4 /* default octave */
#define MIN_TEMPO 32 /* minimum tempo */
#define DFLT_TEMPO 120 /* default tempo */
#define MAX_TEMPO 255 /* max tempo */
#define NUM_MULT 3 /* numerator of dot multiplier */
#define DENOM_MULT 2 /* denominator of dot multiplier */
/* letter to half-tone: A B C D E F G */
static int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 };
/*
* This is the American Standard A440 Equal-Tempered scale with frequencies
* rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
* our octave 0 is standard octave 2.
*/
#define OCTAVE_NOTES 12 /* semitones per octave */
static int pitchtab[] =
{
/* C C# D D# E F F# G G# A A# B*/
/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
};
#define NOCTAVES (sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
static void
playinit(void)
{
octave = DFLT_OCTAVE;
whole = (1000 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
fill = NORMAL;
value = DFLT_VALUE;
octtrack = 0;
octprefix = 1; /* act as though there was an initial O(n) */
}
/* play tone of proper duration for current rhythm signature */
static void
playtone(int pitch, int value, int sustain)
{
int sound, silence, snum = 1, sdenom = 1;
/* this weirdness avoids floating-point arithmetic */
for (; sustain; sustain--) {
snum *= NUM_MULT;
sdenom *= DENOM_MULT;
}
if (pitch == -1)
rest(whole * snum / (value * sdenom));
else if (pitch >= 0 &&
pitch < (sizeof(pitchtab) / sizeof(pitchtab[0]))) {
sound = (whole * snum) / (value * sdenom) -
(whole * (FILLTIME - fill)) / (value * FILLTIME);
silence = whole * (FILLTIME-fill) * snum /
(FILLTIME * value * sdenom);
#ifdef SPKRDEBUG
printf("playtone: pitch %d for %dms, rest for %dms\n",
pitch, sound, silence);
#endif /* SPKRDEBUG */
tone(pitchtab[pitch], sound);
if (fill != LEGATO)
rest(silence);
}
}
/* interpret and play an item from a notation string */
static void
playstring(char *cp, size_t slen)
{
int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
#define GETNUM(cp, v) \
do { \
for (v = 0; slen > 0 && isdigit(cp[1]); ) { \
v = v * 10 + (*++cp - '0'); \
slen--; \
} \
} while (0)
for (; slen--; cp++) {
int sustain, timeval, tempo;
char c = toupper(*cp);
#ifdef SPKRDEBUG
printf("playstring: %c (%x)\n", c, c);
#endif /* SPKRDEBUG */
switch (c) {
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
/* compute pitch */
pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
/* this may be followed by an accidental sign */
if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
++pitch;
++cp;
slen--;
} else if (slen > 0 && cp[1] == '-') {
--pitch;
++cp;
slen--;
}
/*
* If octave-tracking mode is on, and there has been
* no octave-setting prefix, find the version of the
* current letter note closest to the last regardless
* of octave.
*/
if (octtrack && !octprefix) {
if (abs(pitch - lastpitch) >
abs(pitch + OCTAVE_NOTES - lastpitch)) {
++octave;
pitch += OCTAVE_NOTES;
}
if (abs(pitch - lastpitch) >
abs(pitch - OCTAVE_NOTES - lastpitch)) {
--octave;
pitch -= OCTAVE_NOTES;
}
}
octprefix = 0;
lastpitch = pitch;
/*
* ...which may in turn be followed by an override
* time value
*/
GETNUM(cp, timeval);
if (timeval <= 0 || timeval > MIN_VALUE)
timeval = value;
/* ...and/or sustain dots */
for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
slen--;
sustain++;
}
/* time to emit the actual tone */
playtone(pitch, timeval, sustain);
break;
case 'O':
if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
octprefix = octtrack = 0;
++cp;
slen--;
} else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
octtrack = 1;
++cp;
slen--;
} else {
GETNUM(cp, octave);
if (octave >= NOCTAVES)
octave = DFLT_OCTAVE;
octprefix = 1;
}
break;
case '>':
if (octave < NOCTAVES - 1)
octave++;
octprefix = 1;
break;
case '<':
if (octave > 0)
octave--;
octprefix = 1;
break;
case 'N':
GETNUM(cp, pitch);
for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
slen--;
sustain++;
}
playtone(pitch - 1, value, sustain);
break;
case 'L':
GETNUM(cp, value);
if (value <= 0 || value > MIN_VALUE)
value = DFLT_VALUE;
break;
case 'P':
case '~':
/* this may be followed by an override time value */
GETNUM(cp, timeval);
if (timeval <= 0 || timeval > MIN_VALUE)
timeval = value;
for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
slen--;
sustain++;
}
playtone(-1, timeval, sustain);
break;
case 'T':
GETNUM(cp, tempo);
if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
tempo = DFLT_TEMPO;
whole = (1000 * SECS_PER_MIN * WHOLE_NOTE) / tempo;
break;
case 'M':
if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
fill = NORMAL;
++cp;
slen--;
} else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
fill = LEGATO;
++cp;
slen--;
} else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
fill = STACCATO;
++cp;
slen--;
}
break;
}
}
}
/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
*
* This section implements driver hooks to run playstring() and the tone(),
* endtone(), and rest() functions defined above.
*/
static int spkr_active; /* exclusion flag */
static void *spkr_inbuf;
static int spkr_attached = 0;
int
spkrprobe(struct device *parent, void *match, void *aux)
{
return (!spkr_attached);
}
void
spkrattach(struct device *parent, struct device *self, void *aux)
{
printf("\n");
ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
spkr_attached = 1;
}
int
spkropen(dev_t dev, int flags, int mode, struct proc *p)
{
#ifdef SPKRDEBUG
printf("spkropen: entering with dev = %x\n", dev);
#endif /* SPKRDEBUG */
if (minor(dev) != 0 || !spkr_attached)
return (ENXIO);
else if (spkr_active)
return (EBUSY);
else {
playinit();
spkr_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
spkr_active = 1;
}
return (0);
}
int
spkrwrite(dev_t dev, struct uio *uio, int flags)
{
size_t n;
int error;
#ifdef SPKRDEBUG
printf("spkrwrite: entering with dev = %x, count = %zu\n",
dev, uio->uio_resid);
#endif /* SPKRDEBUG */
if (minor(dev) != 0)
return (ENXIO);
else {
n = ulmin(DEV_BSIZE, uio->uio_resid);
error = uiomove(spkr_inbuf, n, uio);
if (!error)
playstring((char *)spkr_inbuf, n);
return (error);
}
}
int
spkrclose(dev_t dev, int flags, int mode, struct proc *p)
{
#ifdef SPKRDEBUG
printf("spkrclose: entering with dev = %x\n", dev);
#endif /* SPKRDEBUG */
if (minor(dev) != 0)
return (ENXIO);
else {
tone(0, 0);
free(spkr_inbuf, M_DEVBUF, DEV_BSIZE);
spkr_active = 0;
}
return (0);
}
int
spkrioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
tone_t *tp, ttp;
int error;
#ifdef SPKRDEBUG
printf("spkrioctl: entering with dev = %x, cmd = %lx\n", dev, cmd);
#endif /* SPKRDEBUG */
if (minor(dev) != 0)
return (ENXIO);
switch (cmd) {
case SPKRTONE:
case SPKRTUNE:
if ((flag & FWRITE) == 0)
return (EACCES);
default:
break;
}
switch (cmd) {
case SPKRTONE:
tp = (tone_t *)data;
if (tp->duration < 0 || tp->frequency < 0)
return (EINVAL);
if (tp->frequency == 0)
rest(tp->duration);
else
tone(tp->frequency, tp->duration);
break;
case SPKRTUNE:
tp = (tone_t *)(*(caddr_t *)data);
for (; ; tp++) {
error = copyin(tp, &ttp, sizeof(tone_t));
if (error)
return (error);
if (ttp.duration < 0 || ttp.frequency < 0)
return (EINVAL);
if (ttp.duration == 0)
break;
if (ttp.frequency == 0)
rest(ttp.duration);
else
tone(ttp.frequency, ttp.duration);
}
break;
default:
return (ENOTTY);
}
return (0);
}
74
4
80
44
91
41
92
96
265
19
8
238
156
144
10
143
1
118
2
92
28
92
92
2
1
1
1
77
14
1
33
51
16
54
1
13
13
2
2
2
2
3
1
3
58
59
56
93
93
93
1
1
5
63
76
75
4
1
26
26
39
1
19
17
50
64
11
55
7
59
62
3
54
19
6
13
19
6
13
19
15
33
33
33
33
32
32
1
32
64
65
63
58
56
65
54
19
31
31
30
29
42
42
30
12
29
73
73
12
12
9
41
113
72
31
51
108
86
88
82
107
13
107
23
24
38
99
20
108
108
91
26
27
27
2
118
118
73
116
60
116
112
12
108
71
28
19
71
7
4
66
14
7
42
9
48
53
13
105
5
6
6
1
6
14
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
/* $OpenBSD: in6.c,v 1.248 2022/08/29 07:51:45 bluhm Exp $ */
/* $KAME: in6.c,v 1.372 2004/06/14 08:14:21 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in.c 8.2 (Berkeley) 11/15/93
*/
#include "carp.h"
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/mld6_var.h>
#ifdef MROUTING
#include <netinet6/ip6_mroute.h>
#endif
#include <netinet6/in6_ifattach.h>
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
/*
* Definitions of some constant IP6 addresses.
*/
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
const struct in6_addr in6addr_intfacelocal_allnodes =
IN6ADDR_INTFACELOCAL_ALLNODES_INIT;
const struct in6_addr in6addr_linklocal_allnodes =
IN6ADDR_LINKLOCAL_ALLNODES_INIT;
const struct in6_addr in6addr_linklocal_allrouters =
IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
const struct in6_addr in6mask0 = IN6MASK0;
const struct in6_addr in6mask32 = IN6MASK32;
const struct in6_addr in6mask64 = IN6MASK64;
const struct in6_addr in6mask96 = IN6MASK96;
const struct in6_addr in6mask128 = IN6MASK128;
int in6_ioctl(u_long, caddr_t, struct ifnet *, int);
int in6_ioctl_change_ifaddr(u_long, caddr_t, struct ifnet *);
int in6_ioctl_get(u_long, caddr_t, struct ifnet *);
int in6_check_embed_scope(struct sockaddr_in6 *, unsigned int);
int in6_clear_scope_id(struct sockaddr_in6 *, unsigned int);
int in6_ifinit(struct ifnet *, struct in6_ifaddr *, int);
void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
const struct sockaddr_in6 sa6_any = {
sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0
};
int
in6_mask2len(struct in6_addr *mask, u_char *lim0)
{
int x = 0, y;
u_char *lim = lim0, *p;
/* ignore the scope_id part */
if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask))
lim = (u_char *)mask + sizeof(*mask);
for (p = (u_char *)mask; p < lim; x++, p++) {
if (*p != 0xff)
break;
}
y = 0;
if (p < lim) {
for (y = 0; y < 8; y++) {
if ((*p & (0x80 >> y)) == 0)
break;
}
}
/*
* when the limit pointer is given, do a stricter check on the
* remaining bits.
*/
if (p < lim) {
if (y != 0 && (*p & (0x00ff >> y)) != 0)
return (-1);
for (p = p + 1; p < lim; p++)
if (*p != 0)
return (-1);
}
return x * 8 + y;
}
int
in6_nam2sin6(const struct mbuf *nam, struct sockaddr_in6 **sin6)
{
struct sockaddr *sa = mtod(nam, struct sockaddr *);
if (nam->m_len < offsetof(struct sockaddr, sa_data))
return EINVAL;
if (sa->sa_family != AF_INET6)
return EAFNOSUPPORT;
if (sa->sa_len != nam->m_len)
return EINVAL;
if (sa->sa_len != sizeof(struct sockaddr_in6))
return EINVAL;
*sin6 = satosin6(sa);
return 0;
}
int
in6_sa2sin6(struct sockaddr *sa, struct sockaddr_in6 **sin6)
{
if (sa->sa_family != AF_INET6)
return EAFNOSUPPORT;
if (sa->sa_len != sizeof(struct sockaddr_in6))
return EINVAL;
*sin6 = satosin6(sa);
return 0;
}
int
in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp)
{
int privileged;
int error;
privileged = 0;
if ((so->so_state & SS_PRIV) != 0)
privileged++;
switch (cmd) {
#ifdef MROUTING
case SIOCGETSGCNT_IN6:
case SIOCGETMIFCNT_IN6:
error = mrt6_ioctl(so, cmd, data);
break;
#endif /* MROUTING */
default:
error = in6_ioctl(cmd, data, ifp, privileged);
break;
}
return error;
}
int
in6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged)
{
if (ifp == NULL)
return (ENXIO);
switch (cmd) {
case SIOCGIFINFO_IN6:
case SIOCGNBRINFO_IN6:
return (nd6_ioctl(cmd, data, ifp));
case SIOCGIFDSTADDR_IN6:
case SIOCGIFNETMASK_IN6:
case SIOCGIFAFLAG_IN6:
case SIOCGIFALIFETIME_IN6:
return (in6_ioctl_get(cmd, data, ifp));
case SIOCAIFADDR_IN6:
case SIOCDIFADDR_IN6:
if (!privileged)
return (EPERM);
return (in6_ioctl_change_ifaddr(cmd, data, ifp));
case SIOCSIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFBRDADDR:
case SIOCSIFNETMASK:
/*
* Do not pass those ioctl to driver handler since they are not
* properly set up. Instead just error out.
*/
return (EINVAL);
default:
return (EOPNOTSUPP);
}
}
int
in6_ioctl_change_ifaddr(u_long cmd, caddr_t data, struct ifnet *ifp)
{
struct in6_ifaddr *ia6 = NULL;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
struct sockaddr *sa;
struct sockaddr_in6 *sa6 = NULL;
int error = 0, newifaddr = 0, plen;
/*
* Find address for this interface, if it exists.
*
* In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation
* only, and used the first interface address as the target of other
* operations (without checking ifra_addr). This was because netinet
* code/API assumed at most 1 interface address per interface.
* Since IPv6 allows a node to assign multiple addresses
* on a single interface, we almost always look and check the
* presence of ifra_addr, and reject invalid ones here.
* It also decreases duplicated code among SIOC*_IN6 operations.
*
* We always require users to specify a valid IPv6 address for
* the corresponding operation.
*/
switch (cmd) {
case SIOCAIFADDR_IN6:
sa = sin6tosa(&ifra->ifra_addr);
break;
case SIOCDIFADDR_IN6:
sa = sin6tosa(&((struct in6_ifreq *)data)->ifr_addr);
break;
default:
panic("%s: invalid ioctl %lu", __func__, cmd);
}
if (sa->sa_family == AF_INET6) {
error = in6_sa2sin6(sa, &sa6);
if (error)
return (error);
}
NET_LOCK();
if (sa6 != NULL) {
error = in6_check_embed_scope(sa6, ifp->if_index);
if (error)
goto err;
error = in6_clear_scope_id(sa6, ifp->if_index);
if (error)
goto err;
ia6 = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
}
switch (cmd) {
case SIOCDIFADDR_IN6:
/*
* for IPv4, we look for existing in_ifaddr here to allow
* "ifconfig if0 delete" to remove the first IPv4 address on
* the interface. For IPv6, as the spec allows multiple
* interface address from the day one, we consider "remove the
* first one" semantics to be not preferable.
*/
if (ia6 == NULL) {
error = EADDRNOTAVAIL;
break;
}
in6_purgeaddr(&ia6->ia_ifa);
if_addrhooks_run(ifp);
break;
case SIOCAIFADDR_IN6:
if (ifra->ifra_addr.sin6_family != AF_INET6 ||
ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) {
error = EAFNOSUPPORT;
break;
}
/* reject read-only flags */
if ((ifra->ifra_flags & IN6_IFF_DUPLICATED) != 0 ||
(ifra->ifra_flags & IN6_IFF_DETACHED) != 0 ||
(ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) {
error = EINVAL;
break;
}
if (ia6 == NULL)
newifaddr = 1;
/*
* Make the address tentative before joining multicast
* addresses, so that corresponding MLD responses would
* not have a tentative source address.
*/
if (newifaddr && in6if_do_dad(ifp))
ifra->ifra_flags |= IN6_IFF_TENTATIVE;
/*
* first, make or update the interface address structure,
* and link it to the list. try to enable inet6 if there
* is no link-local yet.
*/
error = in6_ifattach(ifp);
if (error)
break;
error = in6_update_ifa(ifp, ifra, ia6);
if (error)
break;
ia6 = NULL;
if (sa6 != NULL)
ia6 = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
if (ia6 == NULL) {
/*
* this can happen when the user specify the 0 valid
* lifetime.
*/
break;
}
/* Perform DAD, if needed. */
if (ia6->ia6_flags & IN6_IFF_TENTATIVE)
nd6_dad_start(&ia6->ia_ifa);
if (!newifaddr) {
if_addrhooks_run(ifp);
break;
}
plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL);
if ((ifp->if_flags & IFF_LOOPBACK) || plen == 128) {
if_addrhooks_run(ifp);
break; /* No need to install a connected route. */
}
error = rt_ifa_add(&ia6->ia_ifa,
RTF_CLONING | RTF_CONNECTED | RTF_MPATH,
ia6->ia_ifa.ifa_addr, ifp->if_rdomain);
if (error) {
in6_purgeaddr(&ia6->ia_ifa);
break;
}
if_addrhooks_run(ifp);
break;
}
err:
NET_UNLOCK();
return (error);
}
int
in6_ioctl_get(u_long cmd, caddr_t data, struct ifnet *ifp)
{
struct in6_ifreq *ifr = (struct in6_ifreq *)data;
struct in6_ifaddr *ia6 = NULL;
struct sockaddr *sa;
struct sockaddr_in6 *sa6 = NULL;
int error = 0;
sa = sin6tosa(&ifr->ifr_addr);
if (sa->sa_family == AF_INET6) {
sa->sa_len = sizeof(struct sockaddr_in6);
error = in6_sa2sin6(sa, &sa6);
if (error)
return (error);
}
NET_LOCK_SHARED();
if (sa6 != NULL) {
error = in6_check_embed_scope(sa6, ifp->if_index);
if (error)
goto err;
error = in6_clear_scope_id(sa6, ifp->if_index);
if (error)
goto err;
ia6 = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
}
/* must think again about its semantics */
if (ia6 == NULL) {
error = EADDRNOTAVAIL;
goto err;
}
switch (cmd) {
case SIOCGIFDSTADDR_IN6:
if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
error = EINVAL;
break;
}
/*
* XXX: should we check if ifa_dstaddr is NULL and return
* an error?
*/
ifr->ifr_dstaddr = ia6->ia_dstaddr;
break;
case SIOCGIFNETMASK_IN6:
ifr->ifr_addr = ia6->ia_prefixmask;
break;
case SIOCGIFAFLAG_IN6:
ifr->ifr_ifru.ifru_flags6 = ia6->ia6_flags;
break;
case SIOCGIFALIFETIME_IN6:
ifr->ifr_ifru.ifru_lifetime = ia6->ia6_lifetime;
if (ia6->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
time_t expire, maxexpire;
struct in6_addrlifetime *retlt =
&ifr->ifr_ifru.ifru_lifetime;
/*
* XXX: adjust expiration time assuming time_t is
* signed.
*/
maxexpire =
(time_t)~(1ULL << ((sizeof(maxexpire) * 8) - 1));
if (ia6->ia6_lifetime.ia6t_vltime <
maxexpire - ia6->ia6_updatetime) {
expire = ia6->ia6_updatetime +
ia6->ia6_lifetime.ia6t_vltime;
if (expire != 0) {
expire -= getuptime();
expire += gettime();
}
retlt->ia6t_expire = expire;
} else
retlt->ia6t_expire = maxexpire;
}
if (ia6->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
time_t expire, maxexpire;
struct in6_addrlifetime *retlt =
&ifr->ifr_ifru.ifru_lifetime;
/*
* XXX: adjust expiration time assuming time_t is
* signed.
*/
maxexpire =
(time_t)~(1ULL << ((sizeof(maxexpire) * 8) - 1));
if (ia6->ia6_lifetime.ia6t_pltime <
maxexpire - ia6->ia6_updatetime) {
expire = ia6->ia6_updatetime +
ia6->ia6_lifetime.ia6t_pltime;
if (expire != 0) {
expire -= getuptime();
expire += gettime();
}
retlt->ia6t_preferred = expire;
} else
retlt->ia6t_preferred = maxexpire;
}
break;
default:
panic("%s: invalid ioctl %lu", __func__, cmd);
}
err:
NET_UNLOCK_SHARED();
return (error);
}
int
in6_check_embed_scope(struct sockaddr_in6 *sa6, unsigned int ifidx)
{
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
if (sa6->sin6_addr.s6_addr16[1] == 0) {
/* link ID is not embedded by the user */
sa6->sin6_addr.s6_addr16[1] = htons(ifidx);
} else if (sa6->sin6_addr.s6_addr16[1] != htons(ifidx))
return EINVAL; /* link ID contradicts */
}
return 0;
}
int
in6_clear_scope_id(struct sockaddr_in6 *sa6, unsigned int ifidx)
{
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
if (sa6->sin6_scope_id) {
if (sa6->sin6_scope_id != (u_int32_t)ifidx)
return EINVAL;
sa6->sin6_scope_id = 0; /* XXX: good way? */
}
}
return 0;
}
/*
* Update parameters of an IPv6 interface address.
* If necessary, a new entry is created and linked into address chains.
* This function is separated from in6_control().
*/
int
in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
struct in6_ifaddr *ia6)
{
int error = 0, hostIsNew = 0, plen = -1;
struct sockaddr_in6 dst6;
struct in6_addrlifetime *lt;
struct in6_multi_mship *imm;
struct rtentry *rt;
char addr[INET6_ADDRSTRLEN];
NET_ASSERT_LOCKED();
/* Validate parameters */
if (ifp == NULL || ifra == NULL) /* this maybe redundant */
return (EINVAL);
/*
* The destination address for a p2p link must have a family
* of AF_UNSPEC or AF_INET6.
*/
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
return (EAFNOSUPPORT);
/*
* validate ifra_prefixmask. don't check sin6_family, netmask
* does not carry fields other than sin6_len.
*/
if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6))
return (EINVAL);
/*
* Because the IPv6 address architecture is classless, we require
* users to specify a (non 0) prefix length (mask) for a new address.
* We also require the prefix (when specified) mask is valid, and thus
* reject a non-consecutive mask.
*/
if (ia6 == NULL && ifra->ifra_prefixmask.sin6_len == 0)
return (EINVAL);
if (ifra->ifra_prefixmask.sin6_len != 0) {
plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
(u_char *)&ifra->ifra_prefixmask +
ifra->ifra_prefixmask.sin6_len);
if (plen <= 0)
return (EINVAL);
} else {
/*
* In this case, ia6 must not be NULL. We just use its prefix
* length.
*/
plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL);
}
/*
* If the destination address on a p2p interface is specified,
* and the address is a scoped one, validate/set the scope
* zone identifier.
*/
dst6 = ifra->ifra_dstaddr;
if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 &&
(dst6.sin6_family == AF_INET6)) {
error = in6_check_embed_scope(&dst6, ifp->if_index);
if (error)
return error;
}
/*
* The destination address can be specified only for a p2p or a
* loopback interface. If specified, the corresponding prefix length
* must be 128.
*/
if (ifra->ifra_dstaddr.sin6_family == AF_INET6) {
if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0)
return (EINVAL);
if (plen != 128)
return (EINVAL);
}
/* lifetime consistency check */
lt = &ifra->ifra_lifetime;
if (lt->ia6t_pltime > lt->ia6t_vltime)
return (EINVAL);
if (lt->ia6t_vltime == 0) {
/*
* the following log might be noisy, but this is a typical
* configuration mistake or a tool's bug.
*/
nd6log((LOG_INFO, "%s: valid lifetime is 0 for %s\n", __func__,
inet_ntop(AF_INET6, &ifra->ifra_addr.sin6_addr,
addr, sizeof(addr))));
if (ia6 == NULL)
return (0); /* there's nothing to do */
}
/*
* If this is a new address, allocate a new ifaddr and link it
* into chains.
*/
if (ia6 == NULL) {
hostIsNew = 1;
ia6 = malloc(sizeof(*ia6), M_IFADDR, M_WAITOK | M_ZERO);
refcnt_init_trace(&ia6->ia_ifa.ifa_refcnt,
DT_REFCNT_IDX_IFADDR);
LIST_INIT(&ia6->ia6_memberships);
/* Initialize the address and masks, and put time stamp */
ia6->ia_ifa.ifa_addr = sin6tosa(&ia6->ia_addr);
ia6->ia_addr.sin6_family = AF_INET6;
ia6->ia_addr.sin6_len = sizeof(ia6->ia_addr);
ia6->ia6_updatetime = getuptime();
if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) {
/*
* XXX: some functions expect that ifa_dstaddr is not
* NULL for p2p interfaces.
*/
ia6->ia_ifa.ifa_dstaddr = sin6tosa(&ia6->ia_dstaddr);
} else {
ia6->ia_ifa.ifa_dstaddr = NULL;
}
ia6->ia_ifa.ifa_netmask = sin6tosa(&ia6->ia_prefixmask);
ia6->ia_ifp = ifp;
ia6->ia_addr = ifra->ifra_addr;
ifa_add(ifp, &ia6->ia_ifa);
}
/* set prefix mask */
if (ifra->ifra_prefixmask.sin6_len) {
/*
* We prohibit changing the prefix length of an existing
* address, because
* + such an operation should be rare in IPv6, and
* + the operation would confuse prefix management.
*/
if (ia6->ia_prefixmask.sin6_len &&
in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL) != plen) {
error = EINVAL;
goto unlink;
}
ia6->ia_prefixmask = ifra->ifra_prefixmask;
}
/*
* If a new destination address is specified, scrub the old one and
* install the new destination. Note that the interface must be
* p2p or loopback (see the check above.)
*/
if ((ifp->if_flags & IFF_POINTOPOINT) && dst6.sin6_family == AF_INET6 &&
!IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia6->ia_dstaddr.sin6_addr)) {
struct ifaddr *ifa = &ia6->ia_ifa;
if ((ia6->ia_flags & IFA_ROUTE) != 0 &&
rt_ifa_del(ifa, RTF_HOST, ifa->ifa_dstaddr,
ifp->if_rdomain) != 0) {
nd6log((LOG_ERR, "%s: failed to remove a route "
"to the old destination: %s\n", __func__,
inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr,
addr, sizeof(addr))));
/* proceed anyway... */
} else
ia6->ia_flags &= ~IFA_ROUTE;
ia6->ia_dstaddr = dst6;
}
/*
* Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred
* to see if the address is deprecated or invalidated, but initialize
* these members for applications.
*/
ia6->ia6_updatetime = getuptime();
ia6->ia6_lifetime = ifra->ifra_lifetime;
if (ia6->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
ia6->ia6_lifetime.ia6t_expire =
getuptime() + ia6->ia6_lifetime.ia6t_vltime;
} else
ia6->ia6_lifetime.ia6t_expire = 0;
if (ia6->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
ia6->ia6_lifetime.ia6t_preferred =
getuptime() + ia6->ia6_lifetime.ia6t_pltime;
} else
ia6->ia6_lifetime.ia6t_preferred = 0;
/* reset the interface and routing table appropriately. */
if ((error = in6_ifinit(ifp, ia6, hostIsNew)) != 0)
goto unlink;
/* re-run DAD */
if (ia6->ia6_flags & (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED))
ifra->ifra_flags |= IN6_IFF_TENTATIVE;
/*
* configure address flags.
*/
ia6->ia6_flags = ifra->ifra_flags;
nd6_expire_timer_update(ia6);
/*
* We are done if we have simply modified an existing address.
*/
if (!hostIsNew)
return (error);
/*
* Beyond this point, we should call in6_purgeaddr upon an error,
* not just go to unlink.
*/
/* join necessary multiast groups */
if ((ifp->if_flags & IFF_MULTICAST) != 0) {
struct sockaddr_in6 mltaddr, mltmask;
/* join solicited multicast addr for new host id */
struct sockaddr_in6 llsol;
bzero(&llsol, sizeof(llsol));
llsol.sin6_family = AF_INET6;
llsol.sin6_len = sizeof(llsol);
llsol.sin6_addr.s6_addr16[0] = htons(0xff02);
llsol.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
llsol.sin6_addr.s6_addr32[1] = 0;
llsol.sin6_addr.s6_addr32[2] = htonl(1);
llsol.sin6_addr.s6_addr32[3] =
ifra->ifra_addr.sin6_addr.s6_addr32[3];
llsol.sin6_addr.s6_addr8[12] = 0xff;
imm = in6_joingroup(ifp, &llsol.sin6_addr, &error);
if (!imm)
goto cleanup;
LIST_INSERT_HEAD(&ia6->ia6_memberships, imm, i6mm_chain);
bzero(&mltmask, sizeof(mltmask));
mltmask.sin6_len = sizeof(struct sockaddr_in6);
mltmask.sin6_family = AF_INET6;
mltmask.sin6_addr = in6mask32;
/*
* join link-local all-nodes address
*/
bzero(&mltaddr, sizeof(mltaddr));
mltaddr.sin6_len = sizeof(struct sockaddr_in6);
mltaddr.sin6_family = AF_INET6;
mltaddr.sin6_addr = in6addr_linklocal_allnodes;
mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
mltaddr.sin6_scope_id = 0;
/*
* XXX: do we really need this automatic routes?
* We should probably reconsider this stuff. Most applications
* actually do not need the routes, since they usually specify
* the outgoing interface.
*/
rt = rtalloc(sin6tosa(&mltaddr), 0, ifp->if_rdomain);
if (rt) {
/* 32bit came from "mltmask" */
if (memcmp(&mltaddr.sin6_addr,
&satosin6(rt_key(rt))->sin6_addr,
32 / 8)) {
rtfree(rt);
rt = NULL;
}
}
if (!rt) {
struct rt_addrinfo info;
bzero(&info, sizeof(info));
info.rti_ifa = &ia6->ia_ifa;
info.rti_info[RTAX_DST] = sin6tosa(&mltaddr);
info.rti_info[RTAX_GATEWAY] = sin6tosa(&ia6->ia_addr);
info.rti_info[RTAX_NETMASK] = sin6tosa(&mltmask);
info.rti_info[RTAX_IFA] = sin6tosa(&ia6->ia_addr);
info.rti_flags = RTF_MULTICAST;
error = rtrequest(RTM_ADD, &info, RTP_CONNECTED, NULL,
ifp->if_rdomain);
if (error)
goto cleanup;
} else {
rtfree(rt);
}
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error);
if (!imm)
goto cleanup;
LIST_INSERT_HEAD(&ia6->ia6_memberships, imm, i6mm_chain);
/*
* join interface-local all-nodes address.
* (ff01::1%ifN, and ff01::%ifN/32)
*/
bzero(&mltaddr, sizeof(mltaddr));
mltaddr.sin6_len = sizeof(struct sockaddr_in6);
mltaddr.sin6_family = AF_INET6;
mltaddr.sin6_addr = in6addr_intfacelocal_allnodes;
mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
mltaddr.sin6_scope_id = 0;
/* XXX: again, do we really need the route? */
rt = rtalloc(sin6tosa(&mltaddr), 0, ifp->if_rdomain);
if (rt) {
/* 32bit came from "mltmask" */
if (memcmp(&mltaddr.sin6_addr,
&satosin6(rt_key(rt))->sin6_addr,
32 / 8)) {
rtfree(rt);
rt = NULL;
}
}
if (!rt) {
struct rt_addrinfo info;
bzero(&info, sizeof(info));
info.rti_ifa = &ia6->ia_ifa;
info.rti_info[RTAX_DST] = sin6tosa(&mltaddr);
info.rti_info[RTAX_GATEWAY] = sin6tosa(&ia6->ia_addr);
info.rti_info[RTAX_NETMASK] = sin6tosa(&mltmask);
info.rti_info[RTAX_IFA] = sin6tosa(&ia6->ia_addr);
info.rti_flags = RTF_MULTICAST;
error = rtrequest(RTM_ADD, &info, RTP_CONNECTED, NULL,
ifp->if_rdomain);
if (error)
goto cleanup;
} else {
rtfree(rt);
}
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error);
if (!imm)
goto cleanup;
LIST_INSERT_HEAD(&ia6->ia6_memberships, imm, i6mm_chain);
}
return (error);
unlink:
/*
* XXX: if a change of an existing address failed, keep the entry
* anyway.
*/
if (hostIsNew)
in6_unlink_ifa(ia6, ifp);
return (error);
cleanup:
in6_purgeaddr(&ia6->ia_ifa);
return error;
}
void
in6_purgeaddr(struct ifaddr *ifa)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct in6_ifaddr *ia6 = ifatoia6(ifa);
struct in6_multi_mship *imm;
/* stop DAD processing */
nd6_dad_stop(ifa);
/*
* delete route to the destination of the address being purged.
* The interface must be p2p or loopback in this case.
*/
if ((ifp->if_flags & IFF_POINTOPOINT) && (ia6->ia_flags & IFA_ROUTE) &&
ia6->ia_dstaddr.sin6_len != 0) {
int e;
e = rt_ifa_del(ifa, RTF_HOST, ifa->ifa_dstaddr,
ifp->if_rdomain);
if (e != 0) {
char addr[INET6_ADDRSTRLEN];
log(LOG_ERR, "in6_purgeaddr: failed to remove "
"a route to the p2p destination: %s on %s, "
"errno=%d\n",
inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr,
addr, sizeof(addr)),
ifp->if_xname, e);
/* proceed anyway... */
} else
ia6->ia_flags &= ~IFA_ROUTE;
}
/* Remove ownaddr's loopback rtentry, if it exists. */
rt_ifa_dellocal(&(ia6->ia_ifa));
/*
* leave from multicast groups we have joined for the interface
*/
while (!LIST_EMPTY(&ia6->ia6_memberships)) {
imm = LIST_FIRST(&ia6->ia6_memberships);
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
}
in6_unlink_ifa(ia6, ifp);
}
void
in6_unlink_ifa(struct in6_ifaddr *ia6, struct ifnet *ifp)
{
struct ifaddr *ifa = &ia6->ia_ifa;
int plen;
NET_ASSERT_LOCKED();
/* Release the reference to the base prefix. */
plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL);
if ((ifp->if_flags & IFF_LOOPBACK) == 0 && plen != 128) {
rt_ifa_del(ifa, RTF_CLONING | RTF_CONNECTED,
ifa->ifa_addr, ifp->if_rdomain);
}
rt_ifa_purge(ifa);
ifa_del(ifp, ifa);
ia6->ia_ifp = NULL;
ifafree(&ia6->ia_ifa);
}
/*
* Initialize an interface's inet6 address
* and routing table entry.
*/
int
in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia6, int newhost)
{
int error = 0, plen, ifacount = 0;
struct ifaddr *ifa;
NET_ASSERT_LOCKED();
/*
* Give the interface a chance to initialize
* if this is its first address (or it is a CARP interface)
* and to validate the address if necessary.
*/
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ifacount++;
}
if ((ifacount <= 1 || ifp->if_type == IFT_CARP ||
(ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))) &&
(error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia6))) {
return (error);
}
ia6->ia_ifa.ifa_metric = ifp->if_metric;
/* we could do in(6)_socktrim here, but just omit it at this moment. */
/*
* Special case:
* If the destination address is specified for a point-to-point
* interface, install a route to the destination as an interface
* direct route.
*/
plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); /* XXX */
if ((ifp->if_flags & IFF_POINTOPOINT) && plen == 128 &&
ia6->ia_dstaddr.sin6_family == AF_INET6) {
ifa = &ia6->ia_ifa;
error = rt_ifa_add(ifa, RTF_HOST | RTF_MPATH,
ifa->ifa_dstaddr, ifp->if_rdomain);
if (error != 0)
return (error);
ia6->ia_flags |= IFA_ROUTE;
}
if (newhost)
error = rt_ifa_addlocal(&(ia6->ia_ifa));
return (error);
}
/*
* Add an address to the list of IP6 multicast addresses for a
* given interface.
*/
struct in6_multi *
in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, int *errorp)
{
struct in6_ifreq ifr;
struct in6_multi *in6m;
NET_ASSERT_LOCKED();
*errorp = 0;
/*
* See if address already in list.
*/
IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
if (in6m != NULL) {
/*
* Found it; just increment the reference count.
*/
in6m->in6m_refcnt++;
} else {
/*
* New address; allocate a new multicast record
* and link it into the interface's multicast list.
*/
in6m = malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT | M_ZERO);
if (in6m == NULL) {
*errorp = ENOBUFS;
return (NULL);
}
in6m->in6m_sin.sin6_len = sizeof(struct sockaddr_in6);
in6m->in6m_sin.sin6_family = AF_INET6;
in6m->in6m_sin.sin6_addr = *maddr6;
in6m->in6m_refcnt = 1;
in6m->in6m_ifidx = ifp->if_index;
in6m->in6m_ifma.ifma_addr = sin6tosa(&in6m->in6m_sin);
/*
* Ask the network driver to update its multicast reception
* filter appropriately for the new address.
*/
memcpy(&ifr.ifr_addr, &in6m->in6m_sin, sizeof(in6m->in6m_sin));
*errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
if (*errorp) {
free(in6m, M_IPMADDR, sizeof(*in6m));
return (NULL);
}
TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &in6m->in6m_ifma,
ifma_list);
/*
* Let MLD6 know that we have joined a new IP6 multicast
* group.
*/
mld6_start_listening(in6m);
}
return (in6m);
}
/*
* Delete a multicast address record.
*/
void
in6_delmulti(struct in6_multi *in6m)
{
struct in6_ifreq ifr;
struct ifnet *ifp;
NET_ASSERT_LOCKED();
if (--in6m->in6m_refcnt == 0) {
/*
* No remaining claims to this record; let MLD6 know
* that we are leaving the multicast group.
*/
mld6_stop_listening(in6m);
ifp = if_get(in6m->in6m_ifidx);
/*
* Notify the network driver to update its multicast
* reception filter.
*/
if (ifp != NULL) {
bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
ifr.ifr_addr.sin6_family = AF_INET6;
ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
KERNEL_LOCK();
(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
KERNEL_UNLOCK();
TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma,
ifma_list);
}
if_put(ifp);
free(in6m, M_IPMADDR, sizeof(*in6m));
}
}
/*
* Return 1 if the multicast group represented by ``maddr6'' has been
* joined by interface ``ifp'', 0 otherwise.
*/
int
in6_hasmulti(struct in6_addr *maddr6, struct ifnet *ifp)
{
struct in6_multi *in6m;
int joined;
IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
joined = (in6m != NULL);
return (joined);
}
struct in6_multi_mship *
in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, int *errorp)
{
struct in6_multi_mship *imm;
imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT);
if (!imm) {
*errorp = ENOBUFS;
return NULL;
}
imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp);
if (!imm->i6mm_maddr) {
/* *errorp is already set */
free(imm, M_IPMADDR, sizeof(*imm));
return NULL;
}
return imm;
}
void
in6_leavegroup(struct in6_multi_mship *imm)
{
if (imm->i6mm_maddr)
in6_delmulti(imm->i6mm_maddr);
free(imm, M_IPMADDR, sizeof(*imm));
}
/*
* Find an IPv6 interface link-local address specific to an interface.
*/
struct in6_ifaddr *
in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
{
struct ifaddr *ifa;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) {
if ((ifatoia6(ifa)->ia6_flags & ignoreflags) != 0)
continue;
break;
}
}
return (ifatoia6(ifa));
}
/*
* find the internet address corresponding to a given interface and address.
*/
struct in6_ifaddr *
in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
{
struct ifaddr *ifa;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
break;
}
return (ifatoia6(ifa));
}
/*
* Get a scope of the address. Node-local, link-local, site-local or global.
*/
int
in6_addrscope(struct in6_addr *addr)
{
int scope;
if (addr->s6_addr8[0] == 0xfe) {
scope = addr->s6_addr8[1] & 0xc0;
switch (scope) {
case 0x80:
return __IPV6_ADDR_SCOPE_LINKLOCAL;
break;
case 0xc0:
return __IPV6_ADDR_SCOPE_SITELOCAL;
break;
default:
return __IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
break;
}
}
if (addr->s6_addr8[0] == 0xff) {
scope = addr->s6_addr8[1] & 0x0f;
/*
* due to other scope such as reserved,
* return scope doesn't work.
*/
switch (scope) {
case __IPV6_ADDR_SCOPE_INTFACELOCAL:
return __IPV6_ADDR_SCOPE_INTFACELOCAL;
break;
case __IPV6_ADDR_SCOPE_LINKLOCAL:
return __IPV6_ADDR_SCOPE_LINKLOCAL;
break;
case __IPV6_ADDR_SCOPE_SITELOCAL:
return __IPV6_ADDR_SCOPE_SITELOCAL;
break;
default:
return __IPV6_ADDR_SCOPE_GLOBAL;
break;
}
}
if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
if (addr->s6_addr8[15] == 1) /* loopback */
return __IPV6_ADDR_SCOPE_INTFACELOCAL;
if (addr->s6_addr8[15] == 0) /* unspecified */
return __IPV6_ADDR_SCOPE_LINKLOCAL;
}
return __IPV6_ADDR_SCOPE_GLOBAL;
}
int
in6_addr2scopeid(unsigned int ifidx, struct in6_addr *addr)
{
int scope = in6_addrscope(addr);
switch(scope) {
case __IPV6_ADDR_SCOPE_INTFACELOCAL:
case __IPV6_ADDR_SCOPE_LINKLOCAL:
/* XXX: we do not distinguish between a link and an I/F. */
return (ifidx);
case __IPV6_ADDR_SCOPE_SITELOCAL:
return (0); /* XXX: invalid. */
default:
return (0); /* XXX: treat as global. */
}
}
/*
* return length of part which dst and src are equal
* hard coding...
*/
int
in6_matchlen(struct in6_addr *src, struct in6_addr *dst)
{
int match = 0;
u_char *s = (u_char *)src, *d = (u_char *)dst;
u_char *lim = s + 16, r;
while (s < lim)
if ((r = (*d++ ^ *s++)) != 0) {
while (r < 128) {
match++;
r <<= 1;
}
break;
} else
match += 8;
return match;
}
void
in6_prefixlen2mask(struct in6_addr *maskp, int len)
{
u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
int bytelen, bitlen, i;
/* sanity check */
if (0 > len || len > 128) {
log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n",
len);
return;
}
bzero(maskp, sizeof(*maskp));
bytelen = len / 8;
bitlen = len % 8;
for (i = 0; i < bytelen; i++)
maskp->s6_addr[i] = 0xff;
/* len == 128 is ok because bitlen == 0 then */
if (bitlen)
maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
}
/*
* return the best address out of the same scope
*/
struct in6_ifaddr *
in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst, u_int rdomain)
{
int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0;
int blen = -1;
struct ifaddr *ifa;
struct ifnet *ifp;
struct in6_ifaddr *ia6_best = NULL;
if (oifp == NULL) {
printf("%s: output interface is not specified\n", __func__);
return (NULL);
}
/* We search for all addresses on all interfaces from the beginning. */
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp->if_rdomain != rdomain)
continue;
#if NCARP > 0
/*
* Never use a carp address of an interface which is not
* the master.
*/
if (ifp->if_type == IFT_CARP && !carp_iamatch(ifp))
continue;
#endif
/*
* We can never take an address that breaks the scope zone
* of the destination.
*/
if (in6_addr2scopeid(ifp->if_index, dst) !=
in6_addr2scopeid(oifp->if_index, dst))
continue;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
int tlen = -1;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
src_scope = in6_addrscope(IFA_IN6(ifa));
/*
* Don't use an address before completing DAD
* nor a duplicated address.
*/
if (ifatoia6(ifa)->ia6_flags &
(IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED))
continue;
/*
* RFC 6724 allows anycast addresses as source address
* because the restriction was removed in RFC 4291.
* However RFC 4443 states that ICMPv6 responses
* MUST use a unicast source address.
*
* XXX Skip anycast addresses for now since
* icmp6_reflect() uses this function for source
* address selection.
*/
if (ifatoia6(ifa)->ia6_flags & IN6_IFF_ANYCAST)
continue;
if (ifatoia6(ifa)->ia6_flags & IN6_IFF_DETACHED)
continue;
/*
* If this is the first address we find,
* keep it anyway.
*/
if (ia6_best == NULL)
goto replace;
/*
* ia6_best is never NULL beyond this line except
* within the block labeled "replace".
*/
/*
* Rule 2: Prefer appropriate scope.
* Find the address with the smallest scope that is
* bigger (or equal) to the scope of the destination
* address.
* Accept an address with smaller scope than the
* destination if non exists with bigger scope.
*/
if (best_scope < src_scope) {
if (best_scope < dst_scope)
goto replace;
else
continue;
} else if (src_scope < best_scope) {
if (src_scope < dst_scope)
continue;
else
goto replace;
}
/* Rule 3: Avoid deprecated addresses. */
if (ifatoia6(ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
/*
* Ignore any deprecated addresses if
* specified by configuration.
*/
if (!ip6_use_deprecated)
continue;
/*
* If we have already found a non-deprecated
* candidate, just ignore deprecated addresses.
*/
if ((ia6_best->ia6_flags & IN6_IFF_DEPRECATED)
== 0)
continue;
} else if ((ia6_best->ia6_flags & IN6_IFF_DEPRECATED))
goto replace;
/*
* Rule 4: Prefer home addresses.
* We do not support home addresses.
*/
/* Rule 5: Prefer outgoing interface */
if (ia6_best->ia_ifp == oifp && ifp != oifp)
continue;
if (ia6_best->ia_ifp != oifp && ifp == oifp)
goto replace;
/*
* Rule 5.5: Prefer addresses in a prefix advertised
* by the next-hop.
* We do not track this information.
*/
/*
* Rule 6: Prefer matching label.
* We do not implement policy tables.
*/
/* Rule 7: Prefer temporary addresses. */
if ((ia6_best->ia6_flags & IN6_IFF_TEMPORARY) &&
!(ifatoia6(ifa)->ia6_flags & IN6_IFF_TEMPORARY))
continue;
if (!(ia6_best->ia6_flags & IN6_IFF_TEMPORARY) &&
(ifatoia6(ifa)->ia6_flags & IN6_IFF_TEMPORARY))
goto replace;
/* Rule 8: Use longest matching prefix. */
tlen = in6_matchlen(IFA_IN6(ifa), dst);
if (tlen > blen) {
#if NCARP > 0
/*
* Don't let carp interfaces win a tie against
* the output interface based on matchlen.
* We should only use a carp address if no
* other interface has a usable address.
* Otherwise, when communicating from a carp
* master to a carp backup, the backup system
* won't respond since the carp address is also
* configured as a local address on the backup.
* Note that carp interfaces in backup state
* were already skipped above.
*/
if (ifp->if_type == IFT_CARP &&
oifp->if_type != IFT_CARP)
continue;
#endif
goto replace;
} else if (tlen < blen)
continue;
/*
* If the eight rules fail to choose a single address,
* the tiebreaker is implementation-specific.
*/
/* Prefer address with highest pltime. */
if (ia6_best->ia6_updatetime +
ia6_best->ia6_lifetime.ia6t_pltime <
ifatoia6(ifa)->ia6_updatetime +
ifatoia6(ifa)->ia6_lifetime.ia6t_pltime)
goto replace;
else if (ia6_best->ia6_updatetime +
ia6_best->ia6_lifetime.ia6t_pltime >
ifatoia6(ifa)->ia6_updatetime +
ifatoia6(ifa)->ia6_lifetime.ia6t_pltime)
continue;
/* Prefer address with highest vltime. */
if (ia6_best->ia6_updatetime +
ia6_best->ia6_lifetime.ia6t_vltime <
ifatoia6(ifa)->ia6_updatetime +
ifatoia6(ifa)->ia6_lifetime.ia6t_vltime)
goto replace;
else if (ia6_best->ia6_updatetime +
ia6_best->ia6_lifetime.ia6t_vltime >
ifatoia6(ifa)->ia6_updatetime +
ifatoia6(ifa)->ia6_lifetime.ia6t_vltime)
continue;
continue;
replace:
ia6_best = ifatoia6(ifa);
blen = tlen >= 0 ? tlen :
in6_matchlen(IFA_IN6(ifa), dst);
best_scope =
in6_addrscope(&ia6_best->ia_addr.sin6_addr);
}
}
/* count statistics for future improvements */
if (ia6_best == NULL)
ip6stat_inc(ip6s_sources_none);
else {
if (oifp == ia6_best->ia_ifp)
ip6stat_inc(ip6s_sources_sameif + best_scope);
else
ip6stat_inc(ip6s_sources_otherif + best_scope);
if (best_scope == dst_scope)
ip6stat_inc(ip6s_sources_samescope + best_scope);
else
ip6stat_inc(ip6s_sources_otherscope + best_scope);
if ((ia6_best->ia6_flags & IN6_IFF_DEPRECATED) != 0)
ip6stat_inc(ip6s_sources_deprecated + best_scope);
}
return (ia6_best);
}
int
in6if_do_dad(struct ifnet *ifp)
{
if ((ifp->if_flags & IFF_LOOPBACK) != 0)
return (0);
switch (ifp->if_type) {
#if NCARP > 0
case IFT_CARP:
/*
* XXX: DAD does not work currently on carp(4)
* so disable it for now.
*/
return (0);
#endif
default:
/*
* Our DAD routine requires the interface up and running.
* However, some interfaces can be up before the RUNNING
* status. Additionally, users may try to assign addresses
* before the interface becomes up (or running).
* We simply skip DAD in such a case as a work around.
* XXX: we should rather mark "tentative" on such addresses,
* and do DAD after the interface becomes ready.
*/
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) !=
(IFF_UP|IFF_RUNNING))
return (0);
return (1);
}
}
void *
in6_domifattach(struct ifnet *ifp)
{
struct in6_ifextra *ext;
ext = malloc(sizeof(*ext), M_IFADDR, M_WAITOK | M_ZERO);
ext->nd_ifinfo = nd6_ifattach(ifp);
ext->nprefixes = 0;
ext->ndefrouters = 0;
return ext;
}
void
in6_domifdetach(struct ifnet *ifp, void *aux)
{
struct in6_ifextra *ext = (struct in6_ifextra *)aux;
nd6_ifdetach(ext->nd_ifinfo);
free(ext, M_IFADDR, sizeof(*ext));
}
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
/* $OpenBSD: if_gre.c,v 1.172 2022/06/26 15:50:21 mvs Exp $ */
/* $NetBSD: if_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Heiko W.Rupp <hwr@pilhuhn.de>
*
* IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Encapsulate L3 protocols into IP, per RFC 1701 and 1702.
* See gre(4) for more details.
* Also supported: IP in IP encapsulation (proto 55) per RFC 2004.
*/
#include "bpfilter.h"
#include "pf.h"
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/timeout.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/pool.h>
#include <sys/rwlock.h>
#include <crypto/siphash.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_ecn.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_var.h>
#endif
#ifdef PIPEX
#include <net/pipex.h>
#endif
#ifdef MPLS
#include <netmpls/mpls.h>
#endif /* MPLS */
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <net/if_gre.h>
#include <netinet/ip_gre.h>
#include <sys/sysctl.h>
/* for nvgre bridge shizz */
#include <sys/socket.h>
#include <net/if_bridge.h>
#include <net/if_etherbridge.h>
/*
* packet formats
*/
struct gre_header {
uint16_t gre_flags;
#define GRE_CP 0x8000 /* Checksum Present */
#define GRE_KP 0x2000 /* Key Present */
#define GRE_SP 0x1000 /* Sequence Present */
#define GRE_VERS_MASK 0x0007
#define GRE_VERS_0 0x0000
#define GRE_VERS_1 0x0001
uint16_t gre_proto;
} __packed __aligned(4);
struct gre_h_cksum {
uint16_t gre_cksum;
uint16_t gre_reserved1;
} __packed __aligned(4);
struct gre_h_key {
uint32_t gre_key;
} __packed __aligned(4);
#define GRE_EOIP 0x6400
struct gre_h_key_eoip {
uint16_t eoip_len; /* network order */
uint16_t eoip_tunnel_id; /* little endian */
} __packed __aligned(4);
#define NVGRE_VSID_RES_MIN 0x000000 /* reserved for future use */
#define NVGRE_VSID_RES_MAX 0x000fff
#define NVGRE_VSID_NVE2NVE 0xffffff /* vendor specific NVE-to-NVE comms */
struct gre_h_seq {
uint32_t gre_seq;
} __packed __aligned(4);
struct gre_h_wccp {
uint8_t wccp_flags;
uint8_t service_id;
uint8_t alt_bucket;
uint8_t pri_bucket;
} __packed __aligned(4);
#define GRE_WCCP 0x883e
#define GRE_HDRLEN (sizeof(struct ip) + sizeof(struct gre_header))
/*
* GRE tunnel metadata
*/
#define GRE_KA_NONE 0
#define GRE_KA_DOWN 1
#define GRE_KA_HOLD 2
#define GRE_KA_UP 3
union gre_addr {
struct in_addr in4;
struct in6_addr in6;
};
static inline int
gre_ip_cmp(int, const union gre_addr *,
const union gre_addr *);
#define GRE_KEY_MIN 0x00000000U
#define GRE_KEY_MAX 0xffffffffU
#define GRE_KEY_SHIFT 0
#define GRE_KEY_ENTROPY_MIN 0x00000000U
#define GRE_KEY_ENTROPY_MAX 0x00ffffffU
#define GRE_KEY_ENTROPY_SHIFT 8
struct gre_tunnel {
uint32_t t_key_mask;
#define GRE_KEY_NONE htonl(0x00000000U)
#define GRE_KEY_ENTROPY htonl(0xffffff00U)
#define GRE_KEY_MASK htonl(0xffffffffU)
uint32_t t_key;
u_int t_rtableid;
union gre_addr t_src;
#define t_src4 t_src.in4
#define t_src6 t_src.in6
union gre_addr t_dst;
#define t_dst4 t_dst.in4
#define t_dst6 t_dst.in6
int t_ttl;
int t_txhprio;
int t_rxhprio;
int t_ecn;
uint16_t t_df;
sa_family_t t_af;
};
static int
gre_cmp_src(const struct gre_tunnel *,
const struct gre_tunnel *);
static int
gre_cmp(const struct gre_tunnel *, const struct gre_tunnel *);
static int gre_set_tunnel(struct gre_tunnel *, struct if_laddrreq *, int);
static int gre_get_tunnel(struct gre_tunnel *, struct if_laddrreq *);
static int gre_del_tunnel(struct gre_tunnel *);
static int gre_set_vnetid(struct gre_tunnel *, struct ifreq *);
static int gre_get_vnetid(struct gre_tunnel *, struct ifreq *);
static int gre_del_vnetid(struct gre_tunnel *);
static int gre_set_vnetflowid(struct gre_tunnel *, struct ifreq *);
static int gre_get_vnetflowid(struct gre_tunnel *, struct ifreq *);
static struct mbuf *
gre_encap_dst(const struct gre_tunnel *, const union gre_addr *,
struct mbuf *, uint16_t, uint8_t, uint8_t);
#define gre_encap(_t, _m, _p, _ttl, _tos) \
gre_encap_dst((_t), &(_t)->t_dst, (_m), (_p), (_ttl), (_tos))
static struct mbuf *
gre_encap_dst_ip(const struct gre_tunnel *,
const union gre_addr *, struct mbuf *, uint8_t, uint8_t);
#define gre_encap_ip(_t, _m, _ttl, _tos) \
gre_encap_dst_ip((_t), &(_t)->t_dst, (_m), (_ttl), (_tos))
static int
gre_ip_output(const struct gre_tunnel *, struct mbuf *);
static int gre_tunnel_ioctl(struct ifnet *, struct gre_tunnel *,
u_long, void *);
static uint8_t gre_l2_tos(const struct gre_tunnel *, const struct mbuf *);
static uint8_t gre_l3_tos(const struct gre_tunnel *,
const struct mbuf *, uint8_t);
/*
* layer 3 GRE tunnels
*/
struct gre_softc {
struct gre_tunnel sc_tunnel; /* must be first */
TAILQ_ENTRY(gre_softc) sc_entry;
struct ifnet sc_if;
struct timeout sc_ka_send;
struct timeout sc_ka_hold;
unsigned int sc_ka_state;
unsigned int sc_ka_timeo;
unsigned int sc_ka_count;
unsigned int sc_ka_holdmax;
unsigned int sc_ka_holdcnt;
SIPHASH_KEY sc_ka_key;
uint32_t sc_ka_bias;
int sc_ka_recvtm;
};
TAILQ_HEAD(gre_list, gre_softc);
struct gre_keepalive {
uint32_t gk_uptime;
uint32_t gk_random;
uint8_t gk_digest[SIPHASH_DIGEST_LENGTH];
} __packed __aligned(4);
static int gre_clone_create(struct if_clone *, int);
static int gre_clone_destroy(struct ifnet *);
struct if_clone gre_cloner =
IF_CLONE_INITIALIZER("gre", gre_clone_create, gre_clone_destroy);
/* protected by NET_LOCK */
struct gre_list gre_list = TAILQ_HEAD_INITIALIZER(gre_list);
static int gre_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
static void gre_start(struct ifnet *);
static int gre_ioctl(struct ifnet *, u_long, caddr_t);
static int gre_up(struct gre_softc *);
static int gre_down(struct gre_softc *);
static void gre_link_state(struct ifnet *, unsigned int);
static int gre_input_key(struct mbuf **, int *, int, int, uint8_t,
struct gre_tunnel *);
static struct mbuf *
gre_ipv4_patch(const struct gre_tunnel *, struct mbuf *,
uint8_t *, uint8_t);
#ifdef INET6
static struct mbuf *
gre_ipv6_patch(const struct gre_tunnel *, struct mbuf *,
uint8_t *, uint8_t);
#endif
#ifdef MPLS
static struct mbuf *
gre_mpls_patch(const struct gre_tunnel *, struct mbuf *,
uint8_t *, uint8_t);
#endif
static void gre_keepalive_send(void *);
static void gre_keepalive_recv(struct ifnet *ifp, struct mbuf *);
static void gre_keepalive_hold(void *);
static struct mbuf *
gre_l3_encap_dst(const struct gre_tunnel *, const void *,
struct mbuf *m, sa_family_t);
#define gre_l3_encap(_t, _m, _af) \
gre_l3_encap_dst((_t), &(_t)->t_dst, (_m), (_af))
struct mgre_softc {
struct gre_tunnel sc_tunnel; /* must be first */
RBT_ENTRY(mgre_softc) sc_entry;
struct ifnet sc_if;
};
RBT_HEAD(mgre_tree, mgre_softc);
static inline int
mgre_cmp(const struct mgre_softc *, const struct mgre_softc *);
RBT_PROTOTYPE(mgre_tree, mgre_softc, sc_entry, mgre_cmp);
static int mgre_clone_create(struct if_clone *, int);
static int mgre_clone_destroy(struct ifnet *);
struct if_clone mgre_cloner =
IF_CLONE_INITIALIZER("mgre", mgre_clone_create, mgre_clone_destroy);
static void mgre_rtrequest(struct ifnet *, int, struct rtentry *);
static int mgre_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
static void mgre_start(struct ifnet *);
static int mgre_ioctl(struct ifnet *, u_long, caddr_t);
static int mgre_set_tunnel(struct mgre_softc *, struct if_laddrreq *);
static int mgre_get_tunnel(struct mgre_softc *, struct if_laddrreq *);
static int mgre_up(struct mgre_softc *);
static int mgre_down(struct mgre_softc *);
/* protected by NET_LOCK */
struct mgre_tree mgre_tree = RBT_INITIALIZER();
/*
* Ethernet GRE tunnels
*/
static struct mbuf *
gre_ether_align(struct mbuf *, int);
struct egre_softc {
struct gre_tunnel sc_tunnel; /* must be first */
RBT_ENTRY(egre_softc) sc_entry;
struct arpcom sc_ac;
struct ifmedia sc_media;
};
RBT_HEAD(egre_tree, egre_softc);
static inline int
egre_cmp(const struct egre_softc *, const struct egre_softc *);
RBT_PROTOTYPE(egre_tree, egre_softc, sc_entry, egre_cmp);
static int egre_clone_create(struct if_clone *, int);
static int egre_clone_destroy(struct ifnet *);
static void egre_start(struct ifnet *);
static int egre_ioctl(struct ifnet *, u_long, caddr_t);
static int egre_media_change(struct ifnet *);
static void egre_media_status(struct ifnet *, struct ifmediareq *);
static int egre_up(struct egre_softc *);
static int egre_down(struct egre_softc *);
static int egre_input(const struct gre_tunnel *, struct mbuf *, int,
uint8_t);
struct if_clone egre_cloner =
IF_CLONE_INITIALIZER("egre", egre_clone_create, egre_clone_destroy);
/* protected by NET_LOCK */
struct egre_tree egre_tree = RBT_INITIALIZER();
/*
* Network Virtualisation Using Generic Routing Encapsulation (NVGRE)
*/
struct nvgre_softc {
struct gre_tunnel sc_tunnel; /* must be first */
unsigned int sc_ifp0;
RBT_ENTRY(nvgre_softc) sc_uentry;
RBT_ENTRY(nvgre_softc) sc_mentry;
struct arpcom sc_ac;
struct ifmedia sc_media;
struct mbuf_queue sc_send_list;
struct task sc_send_task;
void *sc_inm;
struct task sc_ltask;
struct task sc_dtask;
struct etherbridge sc_eb;
};
RBT_HEAD(nvgre_ucast_tree, nvgre_softc);
RBT_HEAD(nvgre_mcast_tree, nvgre_softc);
static inline int
nvgre_cmp_ucast(const struct nvgre_softc *,
const struct nvgre_softc *);
static int
nvgre_cmp_mcast(const struct gre_tunnel *,
const union gre_addr *, unsigned int,
const struct gre_tunnel *, const union gre_addr *,
unsigned int);
static inline int
nvgre_cmp_mcast_sc(const struct nvgre_softc *,
const struct nvgre_softc *);
RBT_PROTOTYPE(nvgre_ucast_tree, nvgre_softc, sc_uentry, nvgre_cmp_ucast);
RBT_PROTOTYPE(nvgre_mcast_tree, nvgre_softc, sc_mentry, nvgre_cmp_mcast_sc);
static int nvgre_clone_create(struct if_clone *, int);
static int nvgre_clone_destroy(struct ifnet *);
static void nvgre_start(struct ifnet *);
static int nvgre_ioctl(struct ifnet *, u_long, caddr_t);
static int nvgre_up(struct nvgre_softc *);
static int nvgre_down(struct nvgre_softc *);
static int nvgre_set_parent(struct nvgre_softc *, const char *);
static void nvgre_link_change(void *);
static void nvgre_detach(void *);
static int nvgre_input(const struct gre_tunnel *, struct mbuf *, int,
uint8_t);
static void nvgre_send(void *);
static int nvgre_add_addr(struct nvgre_softc *, const struct ifbareq *);
static int nvgre_del_addr(struct nvgre_softc *, const struct ifbareq *);
static int nvgre_eb_port_eq(void *, void *, void *);
static void *nvgre_eb_port_take(void *, void *);
static void nvgre_eb_port_rele(void *, void *);
static size_t nvgre_eb_port_ifname(void *, char *, size_t, void *);
static void nvgre_eb_port_sa(void *, struct sockaddr_storage *, void *);
static const struct etherbridge_ops nvgre_etherbridge_ops = {
nvgre_eb_port_eq,
nvgre_eb_port_take,
nvgre_eb_port_rele,
nvgre_eb_port_ifname,
nvgre_eb_port_sa,
};
struct if_clone nvgre_cloner =
IF_CLONE_INITIALIZER("nvgre", nvgre_clone_create, nvgre_clone_destroy);
struct pool nvgre_endpoint_pool;
/* protected by NET_LOCK */
struct nvgre_ucast_tree nvgre_ucast_tree = RBT_INITIALIZER();
struct nvgre_mcast_tree nvgre_mcast_tree = RBT_INITIALIZER();
/*
* MikroTik Ethernet over IP protocol (eoip)
*/
struct eoip_softc {
struct gre_tunnel sc_tunnel; /* must be first */
uint16_t sc_tunnel_id;
RBT_ENTRY(eoip_softc) sc_entry;
struct arpcom sc_ac;
struct ifmedia sc_media;
struct timeout sc_ka_send;
struct timeout sc_ka_hold;
unsigned int sc_ka_state;
unsigned int sc_ka_timeo;
unsigned int sc_ka_count;
unsigned int sc_ka_holdmax;
unsigned int sc_ka_holdcnt;
};
RBT_HEAD(eoip_tree, eoip_softc);
static inline int
eoip_cmp(const struct eoip_softc *, const struct eoip_softc *);
RBT_PROTOTYPE(eoip_tree, eoip_softc, sc_entry, eoip_cmp);
static int eoip_clone_create(struct if_clone *, int);
static int eoip_clone_destroy(struct ifnet *);
static void eoip_start(struct ifnet *);
static int eoip_ioctl(struct ifnet *, u_long, caddr_t);
static void eoip_keepalive_send(void *);
static void eoip_keepalive_recv(struct eoip_softc *);
static void eoip_keepalive_hold(void *);
static int eoip_up(struct eoip_softc *);
static int eoip_down(struct eoip_softc *);
static struct mbuf *
eoip_encap(struct eoip_softc *, struct mbuf *, uint8_t);
static struct mbuf *
eoip_input(struct gre_tunnel *, struct mbuf *,
const struct gre_header *, uint8_t, int);
struct if_clone eoip_cloner =
IF_CLONE_INITIALIZER("eoip", eoip_clone_create, eoip_clone_destroy);
/* protected by NET_LOCK */
struct eoip_tree eoip_tree = RBT_INITIALIZER();
/*
* It is not easy to calculate the right value for a GRE MTU.
* We leave this task to the admin and use the same default that
* other vendors use.
*/
#define GREMTU 1476
/*
* We can control the acceptance of GRE and MobileIP packets by
* altering the sysctl net.inet.gre.allow values
* respectively. Zero means drop them, all else is acceptance. We can also
* control acceptance of WCCPv1-style GRE packets through the
* net.inet.gre.wccp value, but be aware it depends upon normal GRE being
* allowed as well.
*
*/
int gre_allow = 0;
int gre_wccp = 0;
void
greattach(int n)
{
if_clone_attach(&gre_cloner);
if_clone_attach(&mgre_cloner);
if_clone_attach(&egre_cloner);
if_clone_attach(&nvgre_cloner);
if_clone_attach(&eoip_cloner);
}
static int
gre_clone_create(struct if_clone *ifc, int unit)
{
struct gre_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
snprintf(sc->sc_if.if_xname, sizeof sc->sc_if.if_xname, "%s%d",
ifc->ifc_name, unit);
ifp = &sc->sc_if;
ifp->if_softc = sc;
ifp->if_type = IFT_TUNNEL;
ifp->if_hdrlen = GRE_HDRLEN;
ifp->if_mtu = GREMTU;
ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
ifp->if_xflags = IFXF_CLONED;
ifp->if_bpf_mtap = p2p_bpf_mtap;
ifp->if_input = p2p_input;
ifp->if_output = gre_output;
ifp->if_start = gre_start;
ifp->if_ioctl = gre_ioctl;
ifp->if_rtrequest = p2p_rtrequest;
sc->sc_tunnel.t_ttl = ip_defttl;
sc->sc_tunnel.t_txhprio = IF_HDRPRIO_PAYLOAD;
sc->sc_tunnel.t_rxhprio = IF_HDRPRIO_PACKET;
sc->sc_tunnel.t_df = htons(0);
sc->sc_tunnel.t_ecn = ECN_ALLOWED;
timeout_set(&sc->sc_ka_send, gre_keepalive_send, sc);
timeout_set_proc(&sc->sc_ka_hold, gre_keepalive_hold, sc);
sc->sc_ka_state = GRE_KA_NONE;
if_counters_alloc(ifp);
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
#endif
ifp->if_llprio = IFQ_TOS2PRIO(IPTOS_PREC_INTERNETCONTROL);
NET_LOCK();
TAILQ_INSERT_TAIL(&gre_list, sc, sc_entry);
NET_UNLOCK();
return (0);
}
static int
gre_clone_destroy(struct ifnet *ifp)
{
struct gre_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
gre_down(sc);
TAILQ_REMOVE(&gre_list, sc, sc_entry);
NET_UNLOCK();
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
static int
mgre_clone_create(struct if_clone *ifc, int unit)
{
struct mgre_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = &sc->sc_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname),
"%s%d", ifc->ifc_name, unit);
ifp->if_softc = sc;
ifp->if_type = IFT_L3IPVLAN;
ifp->if_hdrlen = GRE_HDRLEN;
ifp->if_mtu = GREMTU;
ifp->if_flags = IFF_MULTICAST|IFF_SIMPLEX;
ifp->if_xflags = IFXF_CLONED;
ifp->if_bpf_mtap = p2p_bpf_mtap;
ifp->if_input = p2p_input;
ifp->if_rtrequest = mgre_rtrequest;
ifp->if_output = mgre_output;
ifp->if_start = mgre_start;
ifp->if_ioctl = mgre_ioctl;
sc->sc_tunnel.t_ttl = ip_defttl;
sc->sc_tunnel.t_txhprio = IF_HDRPRIO_PAYLOAD;
sc->sc_tunnel.t_rxhprio = IF_HDRPRIO_PACKET;
sc->sc_tunnel.t_df = htons(0);
sc->sc_tunnel.t_ecn = ECN_ALLOWED;
if_counters_alloc(ifp);
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
#endif
return (0);
}
static int
mgre_clone_destroy(struct ifnet *ifp)
{
struct mgre_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
mgre_down(sc);
NET_UNLOCK();
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
static int
egre_clone_create(struct if_clone *ifc, int unit)
{
struct egre_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = &sc->sc_ac.ac_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
ifp->if_softc = sc;
ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
ifp->if_ioctl = egre_ioctl;
ifp->if_start = egre_start;
ifp->if_xflags = IFXF_CLONED;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ether_fakeaddr(ifp);
sc->sc_tunnel.t_ttl = ip_defttl;
sc->sc_tunnel.t_txhprio = 0;
sc->sc_tunnel.t_rxhprio = IF_HDRPRIO_PACKET;
sc->sc_tunnel.t_df = htons(0);
ifmedia_init(&sc->sc_media, 0, egre_media_change, egre_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
return (0);
}
static int
egre_clone_destroy(struct ifnet *ifp)
{
struct egre_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
egre_down(sc);
NET_UNLOCK();
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
ether_ifdetach(ifp);
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
static int
nvgre_clone_create(struct if_clone *ifc, int unit)
{
struct nvgre_softc *sc;
struct ifnet *ifp;
struct gre_tunnel *tunnel;
int error;
if (nvgre_endpoint_pool.pr_size == 0) {
pool_init(&nvgre_endpoint_pool, sizeof(union gre_addr),
0, IPL_SOFTNET, 0, "nvgreep", NULL);
}
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = &sc->sc_ac.ac_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
error = etherbridge_init(&sc->sc_eb, ifp->if_xname,
&nvgre_etherbridge_ops, sc);
if (error != 0) {
free(sc, M_DEVBUF, sizeof(*sc));
return (error);
}
ifp->if_softc = sc;
ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
ifp->if_ioctl = nvgre_ioctl;
ifp->if_start = nvgre_start;
ifp->if_xflags = IFXF_CLONED;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ether_fakeaddr(ifp);
tunnel = &sc->sc_tunnel;
tunnel->t_ttl = IP_DEFAULT_MULTICAST_TTL;
tunnel->t_txhprio = 0;
sc->sc_tunnel.t_rxhprio = IF_HDRPRIO_PACKET;
tunnel->t_df = htons(IP_DF);
tunnel->t_key_mask = GRE_KEY_ENTROPY;
tunnel->t_key = htonl((NVGRE_VSID_RES_MAX + 1) <<
GRE_KEY_ENTROPY_SHIFT);
mq_init(&sc->sc_send_list, IFQ_MAXLEN * 2, IPL_SOFTNET);
task_set(&sc->sc_send_task, nvgre_send, sc);
task_set(&sc->sc_ltask, nvgre_link_change, sc);
task_set(&sc->sc_dtask, nvgre_detach, sc);
ifmedia_init(&sc->sc_media, 0, egre_media_change, egre_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
return (0);
}
static int
nvgre_clone_destroy(struct ifnet *ifp)
{
struct nvgre_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
nvgre_down(sc);
NET_UNLOCK();
etherbridge_destroy(&sc->sc_eb);
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
ether_ifdetach(ifp);
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
static int
eoip_clone_create(struct if_clone *ifc, int unit)
{
struct eoip_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = &sc->sc_ac.ac_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
ifp->if_softc = sc;
ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
ifp->if_ioctl = eoip_ioctl;
ifp->if_start = eoip_start;
ifp->if_xflags = IFXF_CLONED;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ether_fakeaddr(ifp);
sc->sc_tunnel.t_ttl = ip_defttl;
sc->sc_tunnel.t_txhprio = 0;
sc->sc_tunnel.t_rxhprio = IF_HDRPRIO_PACKET;
sc->sc_tunnel.t_df = htons(0);
sc->sc_ka_timeo = 10;
sc->sc_ka_count = 10;
timeout_set(&sc->sc_ka_send, eoip_keepalive_send, sc);
timeout_set_proc(&sc->sc_ka_hold, eoip_keepalive_hold, sc);
sc->sc_ka_state = GRE_KA_DOWN;
ifmedia_init(&sc->sc_media, 0, egre_media_change, egre_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
return (0);
}
static int
eoip_clone_destroy(struct ifnet *ifp)
{
struct eoip_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
eoip_down(sc);
NET_UNLOCK();
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
ether_ifdetach(ifp);
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
int
gre_input(struct mbuf **mp, int *offp, int type, int af)
{
struct mbuf *m = *mp;
struct gre_tunnel key;
struct ip *ip;
ip = mtod(m, struct ip *);
/* XXX check if ip_src is sane for nvgre? */
key.t_af = AF_INET;
key.t_src4 = ip->ip_dst;
key.t_dst4 = ip->ip_src;
if (gre_input_key(mp, offp, type, af, ip->ip_tos, &key) == -1)
return (rip_input(mp, offp, type, af));
return (IPPROTO_DONE);
}
#ifdef INET6
int
gre_input6(struct mbuf **mp, int *offp, int type, int af)
{
struct mbuf *m = *mp;
struct gre_tunnel key;
struct ip6_hdr *ip6;
uint32_t flow;
ip6 = mtod(m, struct ip6_hdr *);
/* XXX check if ip6_src is sane for nvgre? */
key.t_af = AF_INET6;
key.t_src6 = ip6->ip6_dst;
key.t_dst6 = ip6->ip6_src;
flow = bemtoh32(&ip6->ip6_flow);
if (gre_input_key(mp, offp, type, af, flow >> 20, &key) == -1)
return (rip6_input(mp, offp, type, af));
return (IPPROTO_DONE);
}
#endif /* INET6 */
static inline struct ifnet *
gre_find(const struct gre_tunnel *key)
{
struct gre_softc *sc;
TAILQ_FOREACH(sc, &gre_list, sc_entry) {
if (gre_cmp(key, &sc->sc_tunnel) != 0)
continue;
if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING))
continue;
return (&sc->sc_if);
}
return (NULL);
}
static inline struct ifnet *
mgre_find(const struct gre_tunnel *key)
{
struct mgre_softc *sc;
NET_ASSERT_LOCKED();
sc = RBT_FIND(mgre_tree, &mgre_tree, (const struct mgre_softc *)key);
if (sc != NULL)
return (&sc->sc_if);
return (NULL);
}
static struct mbuf *
gre_input_1(struct gre_tunnel *key, struct mbuf *m,
const struct gre_header *gh, uint8_t otos, int iphlen)
{
switch (gh->gre_proto) {
case htons(ETHERTYPE_PPP):
#ifdef PIPEX
if (pipex_enable) {
struct pipex_session *session;
session = pipex_pptp_lookup_session(m);
if (session != NULL) {
struct mbuf *m0;
m0 = pipex_pptp_input(m, session);
pipex_rele_session(session);
if (m0 == NULL)
return (NULL);
}
}
#endif
break;
case htons(GRE_EOIP):
return (eoip_input(key, m, gh, otos, iphlen));
break;
}
return (m);
}
static int
gre_input_key(struct mbuf **mp, int *offp, int type, int af, uint8_t otos,
struct gre_tunnel *key)
{
struct mbuf *m = *mp;
int iphlen = *offp, hlen, rxprio;
struct ifnet *ifp;
const struct gre_tunnel *tunnel;
caddr_t buf;
struct gre_header *gh;
struct gre_h_key *gkh;
struct mbuf *(*patch)(const struct gre_tunnel *, struct mbuf *,
uint8_t *, uint8_t);
int mcast = 0;
uint8_t itos;
if (!gre_allow)
goto decline;
key->t_rtableid = m->m_pkthdr.ph_rtableid;
hlen = iphlen + sizeof(*gh);
if (m->m_pkthdr.len < hlen)
goto decline;
m = m_pullup(m, hlen);
if (m == NULL)
return (IPPROTO_DONE);
buf = mtod(m, caddr_t);
gh = (struct gre_header *)(buf + iphlen);
/* check the version */
switch (gh->gre_flags & htons(GRE_VERS_MASK)) {
case htons(GRE_VERS_0):
break;
case htons(GRE_VERS_1):
m = gre_input_1(key, m, gh, otos, iphlen);
if (m == NULL)
return (IPPROTO_DONE);
/* FALLTHROUGH */
default:
goto decline;
}
/* the only optional bit in the header is K flag */
if ((gh->gre_flags & htons(~(GRE_KP|GRE_VERS_MASK))) != htons(0))
goto decline;
if (gh->gre_flags & htons(GRE_KP)) {
hlen += sizeof(*gkh);
if (m->m_pkthdr.len < hlen)
goto decline;
m = m_pullup(m, hlen);
if (m == NULL)
return (IPPROTO_DONE);
buf = mtod(m, caddr_t);
gh = (struct gre_header *)(buf + iphlen);
gkh = (struct gre_h_key *)(gh + 1);
key->t_key_mask = GRE_KEY_MASK;
key->t_key = gkh->gre_key;
} else
key->t_key_mask = GRE_KEY_NONE;
if (gh->gre_proto == htons(ETHERTYPE_TRANSETHER)) {
if (egre_input(key, m, hlen, otos) == -1 &&
nvgre_input(key, m, hlen, otos) == -1)
goto decline;
return (IPPROTO_DONE);
}
ifp = gre_find(key);
if (ifp == NULL) {
ifp = mgre_find(key);
if (ifp == NULL)
goto decline;
}
switch (gh->gre_proto) {
case htons(GRE_WCCP): {
struct mbuf *n;
int off;
/* WCCP/GRE:
* So far as I can see (and test) it seems that Cisco's WCCP
* GRE tunnel is precisely a IP-in-GRE tunnel that differs
* only in its protocol number. At least, it works for me.
*
* The Internet Drafts can be found if you look for
* the following:
* draft-forster-wrec-wccp-v1-00.txt
* draft-wilson-wrec-wccp-v2-01.txt
*/
if (!gre_wccp && !ISSET(ifp->if_flags, IFF_LINK0))
goto decline;
/*
* If the first nibble of the payload does not look like
* IPv4, assume it is WCCP v2.
*/
n = m_getptr(m, hlen, &off);
if (n == NULL)
goto decline;
if (n->m_data[off] >> 4 != IPVERSION)
hlen += 4; /* four-octet Redirect header */
/* FALLTHROUGH */
}
case htons(ETHERTYPE_IP):
m->m_pkthdr.ph_family = AF_INET;
patch = gre_ipv4_patch;
break;
#ifdef INET6
case htons(ETHERTYPE_IPV6):
m->m_pkthdr.ph_family = AF_INET6;
patch = gre_ipv6_patch;
break;
#endif
#ifdef MPLS
case htons(ETHERTYPE_MPLS_MCAST):
mcast = M_MCAST|M_BCAST;
/* fallthrough */
case htons(ETHERTYPE_MPLS):
m->m_pkthdr.ph_family = AF_MPLS;
patch = gre_mpls_patch;
break;
#endif
case htons(0):
if (ifp->if_type != IFT_TUNNEL) {
/* keepalives dont make sense for mgre */
goto decline;
}
m_adj(m, hlen);
gre_keepalive_recv(ifp, m);
return (IPPROTO_DONE);
default:
goto decline;
}
/* it's ours now */
m_adj(m, hlen);
tunnel = ifp->if_softc; /* gre and mgre tunnel info is at the front */
m = (*patch)(tunnel, m, &itos, otos);
if (m == NULL)
return (IPPROTO_DONE);
if (tunnel->t_key_mask == GRE_KEY_ENTROPY) {
SET(m->m_pkthdr.csum_flags, M_FLOWID);
m->m_pkthdr.ph_flowid =
bemtoh32(&key->t_key) & ~GRE_KEY_ENTROPY;
}
rxprio = tunnel->t_rxhprio;
switch (rxprio) {
case IF_HDRPRIO_PACKET:
/* nop */
break;
case IF_HDRPRIO_OUTER:
m->m_pkthdr.pf.prio = IFQ_TOS2PRIO(otos);
break;
case IF_HDRPRIO_PAYLOAD:
m->m_pkthdr.pf.prio = IFQ_TOS2PRIO(itos);
break;
default:
m->m_pkthdr.pf.prio = rxprio;
break;
}
m->m_flags &= ~(M_MCAST|M_BCAST);
m->m_flags |= mcast;
if_vinput(ifp, m);
return (IPPROTO_DONE);
decline:
*mp = m;
return (-1);
}
static struct mbuf *
gre_ipv4_patch(const struct gre_tunnel *tunnel, struct mbuf *m,
uint8_t *itosp, uint8_t otos)
{
struct ip *ip;
uint8_t itos;
m = m_pullup(m, sizeof(*ip));
if (m == NULL)
return (NULL);
ip = mtod(m, struct ip *);
itos = ip->ip_tos;
if (ip_ecn_egress(tunnel->t_ecn, &otos, &itos) == 0) {
m_freem(m);
return (NULL);
}
if (itos != ip->ip_tos)
ip_tos_patch(ip, itos);
*itosp = itos;
return (m);
}
#ifdef INET6
static struct mbuf *
gre_ipv6_patch(const struct gre_tunnel *tunnel, struct mbuf *m,
uint8_t *itosp, uint8_t otos)
{
struct ip6_hdr *ip6;
uint32_t flow;
uint8_t itos;
m = m_pullup(m, sizeof(*ip6));
if (m == NULL)
return (NULL);
ip6 = mtod(m, struct ip6_hdr *);
flow = bemtoh32(&ip6->ip6_flow);
itos = flow >> 20;
if (ip_ecn_egress(tunnel->t_ecn, &otos, &itos) == 0) {
m_freem(m);
return (NULL);
}
CLR(flow, 0xff << 20);
SET(flow, itos << 20);
htobem32(&ip6->ip6_flow, flow);
*itosp = itos;
return (m);
}
#endif
#ifdef MPLS
static struct mbuf *
gre_mpls_patch(const struct gre_tunnel *tunnel, struct mbuf *m,
uint8_t *itosp, uint8_t otos)
{
uint8_t itos;
uint32_t shim;
m = m_pullup(m, sizeof(shim));
if (m == NULL)
return (NULL);
shim = *mtod(m, uint32_t *);
itos = (ntohl(shim & MPLS_EXP_MASK) >> MPLS_EXP_OFFSET) << 5;
if (ip_ecn_egress(tunnel->t_ecn, &otos, &itos) == 0) {
m_freem(m);
return (NULL);
}
*itosp = itos;
return (m);
}
#endif
#define gre_l2_prio(_t, _m, _otos) do { \
int rxprio = (_t)->t_rxhprio; \
switch (rxprio) { \
case IF_HDRPRIO_PACKET: \
/* nop */ \
break; \
case IF_HDRPRIO_OUTER: \
(_m)->m_pkthdr.pf.prio = IFQ_TOS2PRIO((_otos)); \
break; \
default: \
(_m)->m_pkthdr.pf.prio = rxprio; \
break; \
} \
} while (0)
static int
egre_input(const struct gre_tunnel *key, struct mbuf *m, int hlen, uint8_t otos)
{
struct egre_softc *sc;
NET_ASSERT_LOCKED();
sc = RBT_FIND(egre_tree, &egre_tree, (const struct egre_softc *)key);
if (sc == NULL)
return (-1);
/* it's ours now */
m = gre_ether_align(m, hlen);
if (m == NULL)
return (0);
if (sc->sc_tunnel.t_key_mask == GRE_KEY_ENTROPY) {
SET(m->m_pkthdr.csum_flags, M_FLOWID);
m->m_pkthdr.ph_flowid =
bemtoh32(&key->t_key) & ~GRE_KEY_ENTROPY;
}
m->m_flags &= ~(M_MCAST|M_BCAST);
gre_l2_prio(&sc->sc_tunnel, m, otos);
if_vinput(&sc->sc_ac.ac_if, m);
return (0);
}
static inline struct nvgre_softc *
nvgre_mcast_find(const struct gre_tunnel *key, unsigned int if0idx)
{
struct nvgre_softc *sc;
int rv;
/*
* building an nvgre_softc to use with RBT_FIND is expensive, and
* would need to swap the src and dst addresses in the key. so do the
* find by hand.
*/
NET_ASSERT_LOCKED();
sc = RBT_ROOT(nvgre_mcast_tree, &nvgre_mcast_tree);
while (sc != NULL) {
rv = nvgre_cmp_mcast(key, &key->t_src, if0idx,
&sc->sc_tunnel, &sc->sc_tunnel.t_dst, sc->sc_ifp0);
if (rv == 0)
return (sc);
if (rv < 0)
sc = RBT_LEFT(nvgre_mcast_tree, sc);
else
sc = RBT_RIGHT(nvgre_mcast_tree, sc);
}
return (NULL);
}
static inline struct nvgre_softc *
nvgre_ucast_find(const struct gre_tunnel *key)
{
NET_ASSERT_LOCKED();
return (RBT_FIND(nvgre_ucast_tree, &nvgre_ucast_tree,
(struct nvgre_softc *)key));
}
static int
nvgre_input(const struct gre_tunnel *key, struct mbuf *m, int hlen,
uint8_t otos)
{
struct nvgre_softc *sc;
struct ether_header *eh;
if (ISSET(m->m_flags, M_MCAST|M_BCAST))
sc = nvgre_mcast_find(key, m->m_pkthdr.ph_ifidx);
else
sc = nvgre_ucast_find(key);
if (sc == NULL)
return (-1);
/* it's ours now */
m = gre_ether_align(m, hlen);
if (m == NULL)
return (0);
eh = mtod(m, struct ether_header *);
etherbridge_map_ea(&sc->sc_eb, (void *)&key->t_dst,
(struct ether_addr *)eh->ether_shost);
SET(m->m_pkthdr.csum_flags, M_FLOWID);
m->m_pkthdr.ph_flowid = bemtoh32(&key->t_key) & ~GRE_KEY_ENTROPY;
m->m_flags &= ~(M_MCAST|M_BCAST);
gre_l2_prio(&sc->sc_tunnel, m, otos);
if_vinput(&sc->sc_ac.ac_if, m);
return (0);
}
static struct mbuf *
gre_ether_align(struct mbuf *m, int hlen)
{
struct mbuf *n;
int off;
m_adj(m, hlen);
if (m->m_pkthdr.len < sizeof(struct ether_header)) {
m_freem(m);
return (NULL);
}
m = m_pullup(m, sizeof(struct ether_header));
if (m == NULL)
return (NULL);
n = m_getptr(m, sizeof(struct ether_header), &off);
if (n == NULL) {
m_freem(m);
return (NULL);
}
if (!ALIGNED_POINTER(mtod(n, caddr_t) + off, uint32_t)) {
n = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
m_freem(m);
if (n == NULL)
return (NULL);
m = n;
}
return (m);
}
static void
gre_keepalive_recv(struct ifnet *ifp, struct mbuf *m)
{
struct gre_softc *sc = ifp->if_softc;
struct gre_keepalive *gk;
SIPHASH_CTX ctx;
uint8_t digest[SIPHASH_DIGEST_LENGTH];
int uptime, delta;
int tick = ticks;
if (sc->sc_ka_state == GRE_KA_NONE ||
sc->sc_tunnel.t_rtableid != sc->sc_if.if_rdomain)
goto drop;
if (m->m_pkthdr.len < sizeof(*gk))
goto drop;
m = m_pullup(m, sizeof(*gk));
if (m == NULL)
return;
gk = mtod(m, struct gre_keepalive *);
uptime = bemtoh32(&gk->gk_uptime) - sc->sc_ka_bias;
delta = tick - uptime;
if (delta < 0)
goto drop;
if (delta > hz * 10) /* magic */
goto drop;
/* avoid too much siphash work */
delta = tick - sc->sc_ka_recvtm;
if (delta > 0 && delta < (hz / 10))
goto drop;
SipHash24_Init(&ctx, &sc->sc_ka_key);
SipHash24_Update(&ctx, &gk->gk_uptime, sizeof(gk->gk_uptime));
SipHash24_Update(&ctx, &gk->gk_random, sizeof(gk->gk_random));
SipHash24_Final(digest, &ctx);
if (memcmp(digest, gk->gk_digest, sizeof(digest)) != 0)
goto drop;
sc->sc_ka_recvtm = tick;
switch (sc->sc_ka_state) {
case GRE_KA_DOWN:
sc->sc_ka_state = GRE_KA_HOLD;
sc->sc_ka_holdcnt = sc->sc_ka_holdmax;
sc->sc_ka_holdmax = MIN(sc->sc_ka_holdmax * 2,
16 * sc->sc_ka_count);
break;
case GRE_KA_HOLD:
if (--sc->sc_ka_holdcnt > 0)
break;
sc->sc_ka_state = GRE_KA_UP;
gre_link_state(&sc->sc_if, sc->sc_ka_state);
break;
case GRE_KA_UP:
sc->sc_ka_holdmax--;
sc->sc_ka_holdmax = MAX(sc->sc_ka_holdmax, sc->sc_ka_count);
break;
}
timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timeo * sc->sc_ka_count);
drop:
m_freem(m);
}
static int
gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
struct m_tag *mtag;
int error = 0;
if (!gre_allow) {
error = EACCES;
goto drop;
}
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
error = ENETDOWN;
goto drop;
}
switch (dst->sa_family) {
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
#ifdef MPLS
case AF_MPLS:
#endif
break;
default:
error = EAFNOSUPPORT;
goto drop;
}
/* Try to limit infinite recursion through misconfiguration. */
for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag;
mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) {
if (memcmp((caddr_t)(mtag + 1), &ifp->if_index,
sizeof(ifp->if_index)) == 0) {
m_freem(m);
error = EIO;
goto end;
}
}
mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
if (mtag == NULL) {
m_freem(m);
error = ENOBUFS;
goto end;
}
memcpy((caddr_t)(mtag + 1), &ifp->if_index, sizeof(ifp->if_index));
m_tag_prepend(m, mtag);
m->m_pkthdr.ph_family = dst->sa_family;
error = if_enqueue(ifp, m);
end:
if (error)
ifp->if_oerrors++;
return (error);
drop:
m_freem(m);
return (error);
}
void
gre_start(struct ifnet *ifp)
{
struct gre_softc *sc = ifp->if_softc;
struct mbuf *m;
int af;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
af = m->m_pkthdr.ph_family;
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_af(if_bpf, af, m, BPF_DIRECTION_OUT);
#endif
m = gre_l3_encap(&sc->sc_tunnel, m, af);
if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
ifp->if_oerrors++;
continue;
}
}
}
void
mgre_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
{
struct ifnet *lo0ifp;
struct ifaddr *ifa, *lo0ifa;
switch (req) {
case RTM_ADD:
if (!ISSET(rt->rt_flags, RTF_LOCAL))
break;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (memcmp(rt_key(rt), ifa->ifa_addr,
rt_key(rt)->sa_len) == 0)
break;
}
if (ifa == NULL)
break;
KASSERT(ifa == rt->rt_ifa);
lo0ifp = if_get(rtable_loindex(ifp->if_rdomain));
KASSERT(lo0ifp != NULL);
TAILQ_FOREACH(lo0ifa, &lo0ifp->if_addrlist, ifa_list) {
if (lo0ifa->ifa_addr->sa_family ==
ifa->ifa_addr->sa_family)
break;
}
if_put(lo0ifp);
if (lo0ifa == NULL)
break;
rt->rt_flags &= ~RTF_LLINFO;
break;
case RTM_DELETE:
case RTM_RESOLVE:
default:
break;
}
}
static int
mgre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dest,
struct rtentry *rt0)
{
struct mgre_softc *sc = ifp->if_softc;
struct sockaddr *gate;
struct rtentry *rt;
struct m_tag *mtag;
int error = 0;
sa_family_t af;
const void *addr;
if (!gre_allow) {
error = EACCES;
goto drop;
}
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
error = ENETDOWN;
goto drop;
}
switch (dest->sa_family) {
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
#ifdef MPLS
case AF_MPLS:
#endif
break;
default:
error = EAFNOSUPPORT;
goto drop;
}
if (ISSET(m->m_flags, M_MCAST|M_BCAST)) {
error = ENETUNREACH;
goto drop;
}
rt = rt_getll(rt0);
/* check rt_expire? */
if (ISSET(rt->rt_flags, RTF_REJECT)) {
error = (rt == rt0) ? EHOSTDOWN : EHOSTUNREACH;
goto drop;
}
if (!ISSET(rt->rt_flags, RTF_HOST)) {
error = EHOSTUNREACH;
goto drop;
}
if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
error = EINVAL;
goto drop;
}
gate = rt->rt_gateway;
af = gate->sa_family;
if (af != sc->sc_tunnel.t_af) {
error = EAGAIN;
goto drop;
}
/* Try to limit infinite recursion through misconfiguration. */
for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag;
mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) {
if (memcmp((caddr_t)(mtag + 1), &ifp->if_index,
sizeof(ifp->if_index)) == 0) {
error = EIO;
goto drop;
}
}
mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
if (mtag == NULL) {
error = ENOBUFS;
goto drop;
}
memcpy((caddr_t)(mtag + 1), &ifp->if_index, sizeof(ifp->if_index));
m_tag_prepend(m, mtag);
switch (af) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)gate;
addr = &sin->sin_addr;
break;
}
#ifdef INET6
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)gate;
addr = &sin6->sin6_addr;
break;
}
#endif
default:
unhandled_af(af);
/* NOTREACHED */
}
m = gre_l3_encap_dst(&sc->sc_tunnel, addr, m, dest->sa_family);
if (m == NULL) {
ifp->if_oerrors++;
return (ENOBUFS);
}
m->m_pkthdr.ph_family = dest->sa_family;
error = if_enqueue(ifp, m);
if (error)
ifp->if_oerrors++;
return (error);
drop:
m_freem(m);
return (error);
}
static void
mgre_start(struct ifnet *ifp)
{
struct mgre_softc *sc = ifp->if_softc;
struct mbuf *m;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf) {
struct m_hdr mh;
struct mbuf *n;
int off;
n = m_getptr(m, ifp->if_hdrlen, &off);
KASSERT(n != NULL);
mh.mh_flags = 0;
mh.mh_next = n->m_next;
mh.mh_len = n->m_len - off;
mh.mh_data = n->m_data + off;
bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family,
(struct mbuf *)&mh, BPF_DIRECTION_OUT);
}
#endif
if (gre_ip_output(&sc->sc_tunnel, m) != 0) {
ifp->if_oerrors++;
continue;
}
}
}
static void
egre_start(struct ifnet *ifp)
{
struct egre_softc *sc = ifp->if_softc;
struct mbuf *m0, *m;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
if (!gre_allow) {
ifq_purge(&ifp->if_snd);
return;
}
while ((m0 = ifq_dequeue(&ifp->if_snd)) != NULL) {
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_ether(if_bpf, m0, BPF_DIRECTION_OUT);
#endif
/* force prepend mbuf because of alignment problems */
m = m_get(M_DONTWAIT, m0->m_type);
if (m == NULL) {
m_freem(m0);
continue;
}
M_MOVE_PKTHDR(m, m0);
m->m_next = m0;
m_align(m, 0);
m->m_len = 0;
m = gre_encap(&sc->sc_tunnel, m, htons(ETHERTYPE_TRANSETHER),
sc->sc_tunnel.t_ttl, gre_l2_tos(&sc->sc_tunnel, m));
if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
ifp->if_oerrors++;
continue;
}
}
}
static struct mbuf *
gre_l3_encap_dst(const struct gre_tunnel *tunnel, const void *dst,
struct mbuf *m, sa_family_t af)
{
uint16_t proto;
uint8_t ttl, itos, otos;
int tttl = tunnel->t_ttl;
int ttloff;
switch (af) {
case AF_INET: {
struct ip *ip;
m = m_pullup(m, sizeof(*ip));
if (m == NULL)
return (NULL);
ip = mtod(m, struct ip *);
itos = ip->ip_tos;
ttloff = offsetof(struct ip, ip_ttl);
proto = htons(ETHERTYPE_IP);
break;
}
#ifdef INET6
case AF_INET6: {
struct ip6_hdr *ip6;
m = m_pullup(m, sizeof(*ip6));
if (m == NULL)
return (NULL);
ip6 = mtod(m, struct ip6_hdr *);
itos = (ntohl(ip6->ip6_flow) & 0x0ff00000) >> 20;
ttloff = offsetof(struct ip6_hdr, ip6_hlim);
proto = htons(ETHERTYPE_IPV6);
break;
}
#endif
#ifdef MPLS
case AF_MPLS: {
uint32_t shim;
m = m_pullup(m, sizeof(shim));
if (m == NULL)
return (NULL);
shim = bemtoh32(mtod(m, uint32_t *)) & MPLS_EXP_MASK;
itos = (shim >> MPLS_EXP_OFFSET) << 5;
ttloff = 3;
if (m->m_flags & (M_BCAST | M_MCAST))
proto = htons(ETHERTYPE_MPLS_MCAST);
else
proto = htons(ETHERTYPE_MPLS);
break;
}
#endif
default:
unhandled_af(af);
}
if (tttl == -1) {
KASSERT(m->m_len > ttloff); /* m_pullup has happened */
ttl = *(m->m_data + ttloff);
} else
ttl = tttl;
itos = gre_l3_tos(tunnel, m, itos);
ip_ecn_ingress(tunnel->t_ecn, &otos, &itos);
return (gre_encap_dst(tunnel, dst, m, proto, ttl, otos));
}
static struct mbuf *
gre_encap_dst(const struct gre_tunnel *tunnel, const union gre_addr *dst,
struct mbuf *m, uint16_t proto, uint8_t ttl, uint8_t tos)
{
struct gre_header *gh;
struct gre_h_key *gkh;
int hlen;
hlen = sizeof(*gh);
if (tunnel->t_key_mask != GRE_KEY_NONE)
hlen += sizeof(*gkh);
m = m_prepend(m, hlen, M_DONTWAIT);
if (m == NULL)
return (NULL);
gh = mtod(m, struct gre_header *);
gh->gre_flags = GRE_VERS_0;
gh->gre_proto = proto;
if (tunnel->t_key_mask != GRE_KEY_NONE) {
gh->gre_flags |= htons(GRE_KP);
gkh = (struct gre_h_key *)(gh + 1);
gkh->gre_key = tunnel->t_key;
if (tunnel->t_key_mask == GRE_KEY_ENTROPY &&
ISSET(m->m_pkthdr.csum_flags, M_FLOWID)) {
gkh->gre_key |= htonl(~GRE_KEY_ENTROPY &
m->m_pkthdr.ph_flowid);
}
}
return (gre_encap_dst_ip(tunnel, dst, m, ttl, tos));
}
static struct mbuf *
gre_encap_dst_ip(const struct gre_tunnel *tunnel, const union gre_addr *dst,
struct mbuf *m, uint8_t ttl, uint8_t tos)
{
switch (tunnel->t_af) {
case AF_UNSPEC:
/* packets may arrive before tunnel is set up */
m_freem(m);
return (NULL);
case AF_INET: {
struct ip *ip;
m = m_prepend(m, sizeof(*ip), M_DONTWAIT);
if (m == NULL)
return (NULL);
ip = mtod(m, struct ip *);
ip->ip_v = IPVERSION;
ip->ip_hl = sizeof(*ip) >> 2;
ip->ip_off = tunnel->t_df;
ip->ip_tos = tos;
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_ttl = ttl;
ip->ip_p = IPPROTO_GRE;
ip->ip_src = tunnel->t_src4;
ip->ip_dst = dst->in4;
break;
}
#ifdef INET6
case AF_INET6: {
struct ip6_hdr *ip6;
int len = m->m_pkthdr.len;
m = m_prepend(m, sizeof(*ip6), M_DONTWAIT);
if (m == NULL)
return (NULL);
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = ISSET(m->m_pkthdr.csum_flags, M_FLOWID) ?
htonl(m->m_pkthdr.ph_flowid) : 0;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_flow |= htonl((uint32_t)tos << 20);
ip6->ip6_plen = htons(len);
ip6->ip6_nxt = IPPROTO_GRE;
ip6->ip6_hlim = ttl;
ip6->ip6_src = tunnel->t_src6;
ip6->ip6_dst = dst->in6;
if (tunnel->t_df)
SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
break;
}
#endif /* INET6 */
default:
unhandled_af(tunnel->t_af);
}
return (m);
}
static int
gre_ip_output(const struct gre_tunnel *tunnel, struct mbuf *m)
{
m->m_flags &= ~(M_BCAST|M_MCAST);
m->m_pkthdr.ph_rtableid = tunnel->t_rtableid;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
switch (tunnel->t_af) {
case AF_INET:
ip_send(m);
break;
#ifdef INET6
case AF_INET6:
ip6_send(m);
break;
#endif
default:
unhandled_af(tunnel->t_af);
}
return (0);
}
static int
gre_tunnel_ioctl(struct ifnet *ifp, struct gre_tunnel *tunnel,
u_long cmd, void *data)
{
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch(cmd) {
case SIOCSIFMTU:
if (ifr->ifr_mtu < 576) {
error = EINVAL;
break;
}
ifp->if_mtu = ifr->ifr_mtu;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
case SIOCSVNETID:
error = gre_set_vnetid(tunnel, ifr);
break;
case SIOCGVNETID:
error = gre_get_vnetid(tunnel, ifr);
break;
case SIOCDVNETID:
error = gre_del_vnetid(tunnel);
break;
case SIOCSVNETFLOWID:
error = gre_set_vnetflowid(tunnel, ifr);
break;
case SIOCGVNETFLOWID:
error = gre_get_vnetflowid(tunnel, ifr);
break;
case SIOCSLIFPHYADDR:
error = gre_set_tunnel(tunnel, (struct if_laddrreq *)data, 1);
break;
case SIOCGLIFPHYADDR:
error = gre_get_tunnel(tunnel, (struct if_laddrreq *)data);
break;
case SIOCDIFPHYADDR:
error = gre_del_tunnel(tunnel);
break;
case SIOCSLIFPHYRTABLE:
if (ifr->ifr_rdomainid < 0 ||
ifr->ifr_rdomainid > RT_TABLEID_MAX ||
!rtable_exists(ifr->ifr_rdomainid)) {
error = EINVAL;
break;
}
tunnel->t_rtableid = ifr->ifr_rdomainid;
break;
case SIOCGLIFPHYRTABLE:
ifr->ifr_rdomainid = tunnel->t_rtableid;
break;
case SIOCSLIFPHYDF:
/* commit */
tunnel->t_df = ifr->ifr_df ? htons(IP_DF) : htons(0);
break;
case SIOCGLIFPHYDF:
ifr->ifr_df = tunnel->t_df ? 1 : 0;
break;
default:
error = ENOTTY;
break;
}
return (error);
}
static uint8_t
gre_l2_tos(const struct gre_tunnel *t, const struct mbuf *m)
{
uint8_t prio;
switch (t->t_txhprio) {
case IF_HDRPRIO_PACKET:
prio = m->m_pkthdr.pf.prio;
break;
default:
prio = t->t_txhprio;
break;
}
return (IFQ_PRIO2TOS(prio));
}
static uint8_t
gre_l3_tos(const struct gre_tunnel *t, const struct mbuf *m, uint8_t tos)
{
uint8_t prio;
switch (t->t_txhprio) {
case IF_HDRPRIO_PAYLOAD:
return (tos);
case IF_HDRPRIO_PACKET:
prio = m->m_pkthdr.pf.prio;
break;
default:
prio = t->t_txhprio;
break;
}
return (IFQ_PRIO2TOS(prio) | (tos & IPTOS_ECN_MASK));
}
static int
gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct gre_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct ifkalivereq *ikar = (struct ifkalivereq *)data;
int error = 0;
switch(cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
/* FALLTHROUGH */
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = gre_up(sc);
else
error = 0;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = gre_down(sc);
}
break;
case SIOCSIFRDOMAIN:
/* let if_rdomain do its thing */
error = ENOTTY;
break;
case SIOCSETKALIVE:
if (ikar->ikar_timeo < 0 || ikar->ikar_timeo > 86400 ||
ikar->ikar_cnt < 0 || ikar->ikar_cnt > 256 ||
(ikar->ikar_timeo == 0) != (ikar->ikar_cnt == 0))
return (EINVAL);
if (ikar->ikar_timeo == 0 || ikar->ikar_cnt == 0) {
sc->sc_ka_count = 0;
sc->sc_ka_timeo = 0;
sc->sc_ka_state = GRE_KA_NONE;
} else {
sc->sc_ka_count = ikar->ikar_cnt;
sc->sc_ka_timeo = ikar->ikar_timeo;
sc->sc_ka_state = GRE_KA_DOWN;
arc4random_buf(&sc->sc_ka_key, sizeof(sc->sc_ka_key));
sc->sc_ka_bias = arc4random();
sc->sc_ka_holdmax = sc->sc_ka_count;
sc->sc_ka_recvtm = ticks - hz;
timeout_add(&sc->sc_ka_send, 1);
timeout_add_sec(&sc->sc_ka_hold,
sc->sc_ka_timeo * sc->sc_ka_count);
}
break;
case SIOCGETKALIVE:
ikar->ikar_cnt = sc->sc_ka_count;
ikar->ikar_timeo = sc->sc_ka_timeo;
break;
case SIOCSLIFPHYTTL:
if (ifr->ifr_ttl != -1 &&
(ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff)) {
error = EINVAL;
break;
}
/* commit */
sc->sc_tunnel.t_ttl = ifr->ifr_ttl;
break;
case SIOCGLIFPHYTTL:
ifr->ifr_ttl = sc->sc_tunnel.t_ttl;
break;
case SIOCSLIFPHYECN:
sc->sc_tunnel.t_ecn =
ifr->ifr_metric ? ECN_ALLOWED : ECN_FORBIDDEN;
break;
case SIOCGLIFPHYECN:
ifr->ifr_metric = (sc->sc_tunnel.t_ecn == ECN_ALLOWED);
break;
case SIOCSTXHPRIO:
error = if_txhprio_l3_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l3_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_rxhprio;
break;
default:
error = gre_tunnel_ioctl(ifp, &sc->sc_tunnel, cmd, data);
break;
}
return (error);
}
static int
mgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct mgre_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch(cmd) {
case SIOCSIFADDR:
break;
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = mgre_up(sc);
else
error = 0;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = mgre_down(sc);
}
break;
case SIOCSLIFPHYTTL:
if (ifr->ifr_ttl != -1 &&
(ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff)) {
error = EINVAL;
break;
}
/* commit */
sc->sc_tunnel.t_ttl = ifr->ifr_ttl;
break;
case SIOCGLIFPHYTTL:
ifr->ifr_ttl = sc->sc_tunnel.t_ttl;
break;
case SIOCSLIFPHYECN:
sc->sc_tunnel.t_ecn =
ifr->ifr_metric ? ECN_ALLOWED : ECN_FORBIDDEN;
break;
case SIOCGLIFPHYECN:
ifr->ifr_metric = (sc->sc_tunnel.t_ecn == ECN_ALLOWED);
break;
case SIOCSLIFPHYADDR:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
error = mgre_set_tunnel(sc, (struct if_laddrreq *)data);
break;
case SIOCGLIFPHYADDR:
error = mgre_get_tunnel(sc, (struct if_laddrreq *)data);
break;
case SIOCSTXHPRIO:
error = if_txhprio_l3_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l3_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_rxhprio;
break;
case SIOCSVNETID:
case SIOCDVNETID:
case SIOCDIFPHYADDR:
case SIOCSLIFPHYRTABLE:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
/* FALLTHROUGH */
default:
error = gre_tunnel_ioctl(ifp, &sc->sc_tunnel, cmd, data);
break;
}
return (error);
}
static int
mgre_set_tunnel(struct mgre_softc *sc, struct if_laddrreq *req)
{
struct gre_tunnel *tunnel = &sc->sc_tunnel;
struct sockaddr *addr = (struct sockaddr *)&req->addr;
struct sockaddr *dstaddr = (struct sockaddr *)&req->dstaddr;
struct sockaddr_in *addr4;
#ifdef INET6
struct sockaddr_in6 *addr6;
int error;
#endif
if (dstaddr->sa_family != AF_UNSPEC)
return (EINVAL);
/* validate */
switch (addr->sa_family) {
case AF_INET:
if (addr->sa_len != sizeof(*addr4))
return (EINVAL);
addr4 = (struct sockaddr_in *)addr;
if (in_nullhost(addr4->sin_addr) ||
IN_MULTICAST(addr4->sin_addr.s_addr))
return (EINVAL);
tunnel->t_src4 = addr4->sin_addr;
tunnel->t_dst4.s_addr = INADDR_ANY;
break;
#ifdef INET6
case AF_INET6:
if (addr->sa_len != sizeof(*addr6))
return (EINVAL);
addr6 = (struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_UNSPECIFIED(&addr6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr))
return (EINVAL);
error = in6_embedscope(&tunnel->t_src6, addr6, NULL);
if (error != 0)
return (error);
memset(&tunnel->t_dst6, 0, sizeof(tunnel->t_dst6));
break;
#endif
default:
return (EAFNOSUPPORT);
}
/* commit */
tunnel->t_af = addr->sa_family;
return (0);
}
static int
mgre_get_tunnel(struct mgre_softc *sc, struct if_laddrreq *req)
{
struct gre_tunnel *tunnel = &sc->sc_tunnel;
struct sockaddr *dstaddr = (struct sockaddr *)&req->dstaddr;
struct sockaddr_in *sin;
#ifdef INET6
struct sockaddr_in6 *sin6;
#endif
switch (tunnel->t_af) {
case AF_UNSPEC:
return (EADDRNOTAVAIL);
case AF_INET:
sin = (struct sockaddr_in *)&req->addr;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = tunnel->t_src4;
break;
#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&req->addr;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
in6_recoverscope(sin6, &tunnel->t_src6);
break;
#endif
default:
unhandled_af(tunnel->t_af);
}
dstaddr->sa_len = 2;
dstaddr->sa_family = AF_UNSPEC;
return (0);
}
static int
egre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct egre_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch(cmd) {
case SIOCSIFADDR:
break;
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = egre_up(sc);
else
error = 0;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = egre_down(sc);
}
break;
case SIOCSLIFPHYTTL:
if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) {
error = EINVAL;
break;
}
/* commit */
sc->sc_tunnel.t_ttl = (uint8_t)ifr->ifr_ttl;
break;
case SIOCGLIFPHYTTL:
ifr->ifr_ttl = (int)sc->sc_tunnel.t_ttl;
break;
case SIOCSTXHPRIO:
error = if_txhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_rxhprio;
break;
case SIOCSVNETID:
case SIOCDVNETID:
case SIOCSVNETFLOWID:
case SIOCSLIFPHYADDR:
case SIOCDIFPHYADDR:
case SIOCSLIFPHYRTABLE:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
/* FALLTHROUGH */
default:
error = gre_tunnel_ioctl(ifp, &sc->sc_tunnel, cmd, data);
if (error == ENOTTY)
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
break;
}
if (error == ENETRESET) {
/* no hardware to program */
error = 0;
}
return (error);
}
static int
nvgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct nvgre_softc *sc = ifp->if_softc;
struct gre_tunnel *tunnel = &sc->sc_tunnel;
struct ifreq *ifr = (struct ifreq *)data;
struct if_parent *parent = (struct if_parent *)data;
struct ifbrparam *bparam = (struct ifbrparam *)data;
struct ifnet *ifp0;
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
break;
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = nvgre_up(sc);
else
error = ENETRESET;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = nvgre_down(sc);
}
break;
case SIOCSLIFPHYADDR:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
error = gre_set_tunnel(tunnel, (struct if_laddrreq *)data, 0);
if (error == 0)
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGLIFPHYADDR:
error = gre_get_tunnel(tunnel, (struct if_laddrreq *)data);
break;
case SIOCDIFPHYADDR:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
error = gre_del_tunnel(tunnel);
if (error == 0)
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCSIFPARENT:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
error = nvgre_set_parent(sc, parent->ifp_parent);
if (error == 0)
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGIFPARENT:
ifp0 = if_get(sc->sc_ifp0);
if (ifp0 == NULL)
error = EADDRNOTAVAIL;
else {
memcpy(parent->ifp_parent, ifp0->if_xname,
sizeof(parent->ifp_parent));
}
if_put(ifp0);
break;
case SIOCDIFPARENT:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
/* commit */
sc->sc_ifp0 = 0;
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCSVNETID:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
if (ifr->ifr_vnetid < GRE_KEY_ENTROPY_MIN ||
ifr->ifr_vnetid > GRE_KEY_ENTROPY_MAX) {
error = EINVAL;
break;
}
/* commit */
tunnel->t_key = htonl(ifr->ifr_vnetid << GRE_KEY_ENTROPY_SHIFT);
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGVNETID:
error = gre_get_vnetid(tunnel, ifr);
break;
case SIOCSLIFPHYRTABLE:
if (ifr->ifr_rdomainid < 0 ||
ifr->ifr_rdomainid > RT_TABLEID_MAX ||
!rtable_exists(ifr->ifr_rdomainid)) {
error = EINVAL;
break;
}
tunnel->t_rtableid = ifr->ifr_rdomainid;
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
break;
case SIOCGLIFPHYRTABLE:
ifr->ifr_rdomainid = tunnel->t_rtableid;
break;
case SIOCSLIFPHYDF:
/* commit */
tunnel->t_df = ifr->ifr_df ? htons(IP_DF) : htons(0);
break;
case SIOCGLIFPHYDF:
ifr->ifr_df = tunnel->t_df ? 1 : 0;
break;
case SIOCSLIFPHYTTL:
if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) {
error = EINVAL;
break;
}
/* commit */
tunnel->t_ttl = ifr->ifr_ttl;
break;
case SIOCGLIFPHYTTL:
ifr->ifr_ttl = tunnel->t_ttl;
break;
case SIOCSTXHPRIO:
error = if_txhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_rxhprio;
break;
case SIOCBRDGSCACHE:
error = etherbridge_set_max(&sc->sc_eb, bparam);
break;
case SIOCBRDGGCACHE:
error = etherbridge_get_max(&sc->sc_eb, bparam);
break;
case SIOCBRDGSTO:
error = etherbridge_set_tmo(&sc->sc_eb, bparam);
break;
case SIOCBRDGGTO:
error = etherbridge_get_tmo(&sc->sc_eb, bparam);
break;
case SIOCBRDGRTS:
error = etherbridge_rtfind(&sc->sc_eb,
(struct ifbaconf *)data);
break;
case SIOCBRDGFLUSH:
etherbridge_flush(&sc->sc_eb,
((struct ifbreq *)data)->ifbr_ifsflags);
break;
case SIOCBRDGSADDR:
error = nvgre_add_addr(sc, (struct ifbareq *)data);
break;
case SIOCBRDGDADDR:
error = nvgre_del_addr(sc, (struct ifbareq *)data);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
break;
}
if (error == ENETRESET) {
/* no hardware to program */
error = 0;
}
return (error);
}
static int
eoip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct eoip_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct ifkalivereq *ikar = (struct ifkalivereq *)data;
int error = 0;
switch(cmd) {
case SIOCSIFADDR:
break;
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = eoip_up(sc);
else
error = 0;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = eoip_down(sc);
}
break;
case SIOCSETKALIVE:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
if (ikar->ikar_timeo < 0 || ikar->ikar_timeo > 86400 ||
ikar->ikar_cnt < 0 || ikar->ikar_cnt > 256)
return (EINVAL);
if (ikar->ikar_timeo == 0 || ikar->ikar_cnt == 0) {
sc->sc_ka_count = 0;
sc->sc_ka_timeo = 0;
sc->sc_ka_state = GRE_KA_NONE;
} else {
sc->sc_ka_count = ikar->ikar_cnt;
sc->sc_ka_timeo = ikar->ikar_timeo;
sc->sc_ka_state = GRE_KA_DOWN;
}
break;
case SIOCGETKALIVE:
ikar->ikar_cnt = sc->sc_ka_count;
ikar->ikar_timeo = sc->sc_ka_timeo;
break;
case SIOCSVNETID:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
if (ifr->ifr_vnetid < 0 || ifr->ifr_vnetid > 0xffff)
return (EINVAL);
sc->sc_tunnel.t_key = htole16(ifr->ifr_vnetid); /* for cmp */
sc->sc_tunnel_id = htole16(ifr->ifr_vnetid);
break;
case SIOCGVNETID:
ifr->ifr_vnetid = letoh16(sc->sc_tunnel_id);
break;
case SIOCSLIFPHYADDR:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
error = gre_set_tunnel(&sc->sc_tunnel,
(struct if_laddrreq *)data, 1);
break;
case SIOCGLIFPHYADDR:
error = gre_get_tunnel(&sc->sc_tunnel,
(struct if_laddrreq *)data);
break;
case SIOCDIFPHYADDR:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
error = gre_del_tunnel(&sc->sc_tunnel);
break;
case SIOCSLIFPHYRTABLE:
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
break;
}
if (ifr->ifr_rdomainid < 0 ||
ifr->ifr_rdomainid > RT_TABLEID_MAX ||
!rtable_exists(ifr->ifr_rdomainid)) {
error = EINVAL;
break;
}
sc->sc_tunnel.t_rtableid = ifr->ifr_rdomainid;
break;
case SIOCGLIFPHYRTABLE:
ifr->ifr_rdomainid = sc->sc_tunnel.t_rtableid;
break;
case SIOCSLIFPHYTTL:
if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) {
error = EINVAL;
break;
}
/* commit */
sc->sc_tunnel.t_ttl = (uint8_t)ifr->ifr_ttl;
break;
case SIOCGLIFPHYTTL:
ifr->ifr_ttl = (int)sc->sc_tunnel.t_ttl;
break;
case SIOCSLIFPHYDF:
/* commit */
sc->sc_tunnel.t_df = ifr->ifr_df ? htons(IP_DF) : htons(0);
break;
case SIOCGLIFPHYDF:
ifr->ifr_df = sc->sc_tunnel.t_df ? 1 : 0;
break;
case SIOCSTXHPRIO:
error = if_txhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_tunnel.t_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_tunnel.t_rxhprio;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
break;
}
if (error == ENETRESET) {
/* no hardware to program */
error = 0;
}
return (error);
}
static int
gre_up(struct gre_softc *sc)
{
NET_ASSERT_LOCKED();
SET(sc->sc_if.if_flags, IFF_RUNNING);
if (sc->sc_ka_state != GRE_KA_NONE)
gre_keepalive_send(sc);
return (0);
}
static int
gre_down(struct gre_softc *sc)
{
NET_ASSERT_LOCKED();
CLR(sc->sc_if.if_flags, IFF_RUNNING);
if (sc->sc_ka_state != GRE_KA_NONE) {
timeout_del_barrier(&sc->sc_ka_hold);
timeout_del_barrier(&sc->sc_ka_send);
sc->sc_ka_state = GRE_KA_DOWN;
gre_link_state(&sc->sc_if, sc->sc_ka_state);
}
return (0);
}
static void
gre_link_state(struct ifnet *ifp, unsigned int state)
{
int link_state = LINK_STATE_UNKNOWN;
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
switch (state) {
case GRE_KA_NONE:
/* maybe up? or down? it's unknown, really */
break;
case GRE_KA_UP:
link_state = LINK_STATE_UP;
break;
default:
link_state = LINK_STATE_KALIVE_DOWN;
break;
}
}
if (ifp->if_link_state != link_state) {
ifp->if_link_state = link_state;
if_link_state_change(ifp);
}
}
static void
gre_keepalive_send(void *arg)
{
struct gre_tunnel t;
struct gre_softc *sc = arg;
struct mbuf *m;
struct gre_keepalive *gk;
SIPHASH_CTX ctx;
int linkhdr, len;
uint16_t proto;
uint8_t ttl;
uint8_t tos;
/*
* re-schedule immediately, so we deal with incomplete configuration
* or temporary errors.
*/
if (sc->sc_ka_timeo)
timeout_add_sec(&sc->sc_ka_send, sc->sc_ka_timeo);
if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
sc->sc_ka_state == GRE_KA_NONE ||
sc->sc_tunnel.t_af == AF_UNSPEC ||
sc->sc_tunnel.t_rtableid != sc->sc_if.if_rdomain)
return;
/* this is really conservative */
#ifdef INET6
linkhdr = max_linkhdr + MAX(sizeof(struct ip), sizeof(struct ip6_hdr)) +
sizeof(struct gre_header) + sizeof(struct gre_h_key);
#else
linkhdr = max_linkhdr + sizeof(struct ip) +
sizeof(struct gre_header) + sizeof(struct gre_h_key);
#endif
len = linkhdr + sizeof(*gk);
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return;
if (len > MHLEN) {
MCLGETL(m, M_DONTWAIT, len);
if (!ISSET(m->m_flags, M_EXT)) {
m_freem(m);
return;
}
}
m->m_pkthdr.len = m->m_len = len;
m_adj(m, linkhdr);
/*
* build the inside packet
*/
gk = mtod(m, struct gre_keepalive *);
htobem32(&gk->gk_uptime, sc->sc_ka_bias + ticks);
htobem32(&gk->gk_random, arc4random());
SipHash24_Init(&ctx, &sc->sc_ka_key);
SipHash24_Update(&ctx, &gk->gk_uptime, sizeof(gk->gk_uptime));
SipHash24_Update(&ctx, &gk->gk_random, sizeof(gk->gk_random));
SipHash24_Final(gk->gk_digest, &ctx);
ttl = sc->sc_tunnel.t_ttl == -1 ? ip_defttl : sc->sc_tunnel.t_ttl;
m->m_pkthdr.pf.prio = sc->sc_if.if_llprio;
tos = gre_l3_tos(&sc->sc_tunnel, m, IFQ_PRIO2TOS(m->m_pkthdr.pf.prio));
t.t_af = sc->sc_tunnel.t_af;
t.t_df = sc->sc_tunnel.t_df;
t.t_src = sc->sc_tunnel.t_dst;
t.t_dst = sc->sc_tunnel.t_src;
t.t_key = sc->sc_tunnel.t_key;
t.t_key_mask = sc->sc_tunnel.t_key_mask;
m = gre_encap(&t, m, htons(0), ttl, tos);
if (m == NULL)
return;
switch (sc->sc_tunnel.t_af) {
case AF_INET: {
struct ip *ip;
ip = mtod(m, struct ip *);
ip->ip_id = htons(ip_randomid());
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, sizeof(*ip));
proto = htons(ETHERTYPE_IP);
break;
}
#ifdef INET6
case AF_INET6:
proto = htons(ETHERTYPE_IPV6);
break;
#endif
default:
m_freem(m);
return;
}
/*
* put it in the tunnel
*/
m = gre_encap(&sc->sc_tunnel, m, proto, ttl, tos);
if (m == NULL)
return;
gre_ip_output(&sc->sc_tunnel, m);
}
static void
gre_keepalive_hold(void *arg)
{
struct gre_softc *sc = arg;
struct ifnet *ifp = &sc->sc_if;
if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
sc->sc_ka_state == GRE_KA_NONE)
return;
NET_LOCK();
sc->sc_ka_state = GRE_KA_DOWN;
gre_link_state(ifp, sc->sc_ka_state);
NET_UNLOCK();
}
static int
gre_set_tunnel(struct gre_tunnel *tunnel, struct if_laddrreq *req, int ucast)
{
struct sockaddr *src = (struct sockaddr *)&req->addr;
struct sockaddr *dst = (struct sockaddr *)&req->dstaddr;
struct sockaddr_in *src4, *dst4;
#ifdef INET6
struct sockaddr_in6 *src6, *dst6;
int error;
#endif
/* sa_family and sa_len must be equal */
if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len)
return (EINVAL);
/* validate */
switch (dst->sa_family) {
case AF_INET:
if (dst->sa_len != sizeof(*dst4))
return (EINVAL);
src4 = (struct sockaddr_in *)src;
if (in_nullhost(src4->sin_addr) ||
IN_MULTICAST(src4->sin_addr.s_addr))
return (EINVAL);
dst4 = (struct sockaddr_in *)dst;
if (in_nullhost(dst4->sin_addr) ||
(IN_MULTICAST(dst4->sin_addr.s_addr) != !ucast))
return (EINVAL);
tunnel->t_src4 = src4->sin_addr;
tunnel->t_dst4 = dst4->sin_addr;
break;
#ifdef INET6
case AF_INET6:
if (dst->sa_len != sizeof(*dst6))
return (EINVAL);
src6 = (struct sockaddr_in6 *)src;
if (IN6_IS_ADDR_UNSPECIFIED(&src6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&src6->sin6_addr))
return (EINVAL);
dst6 = (struct sockaddr_in6 *)dst;
if (IN6_IS_ADDR_UNSPECIFIED(&dst6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&dst6->sin6_addr) != !ucast)
return (EINVAL);
if (src6->sin6_scope_id != dst6->sin6_scope_id)
return (EINVAL);
error = in6_embedscope(&tunnel->t_src6, src6, NULL);
if (error != 0)
return (error);
error = in6_embedscope(&tunnel->t_dst6, dst6, NULL);
if (error != 0)
return (error);
break;
#endif
default:
return (EAFNOSUPPORT);
}
/* commit */
tunnel->t_af = dst->sa_family;
return (0);
}
static int
gre_get_tunnel(struct gre_tunnel *tunnel, struct if_laddrreq *req)
{
struct sockaddr *src = (struct sockaddr *)&req->addr;
struct sockaddr *dst = (struct sockaddr *)&req->dstaddr;
struct sockaddr_in *sin;
#ifdef INET6 /* ifconfig already embeds the scopeid */
struct sockaddr_in6 *sin6;
#endif
switch (tunnel->t_af) {
case AF_UNSPEC:
return (EADDRNOTAVAIL);
case AF_INET:
sin = (struct sockaddr_in *)src;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = tunnel->t_src4;
sin = (struct sockaddr_in *)dst;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = tunnel->t_dst4;
break;
#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6 *)src;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
in6_recoverscope(sin6, &tunnel->t_src6);
sin6 = (struct sockaddr_in6 *)dst;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
in6_recoverscope(sin6, &tunnel->t_dst6);
break;
#endif
default:
return (EAFNOSUPPORT);
}
return (0);
}
static int
gre_del_tunnel(struct gre_tunnel *tunnel)
{
/* commit */
tunnel->t_af = AF_UNSPEC;
return (0);
}
static int
gre_set_vnetid(struct gre_tunnel *tunnel, struct ifreq *ifr)
{
uint32_t key;
uint32_t min = GRE_KEY_MIN;
uint32_t max = GRE_KEY_MAX;
unsigned int shift = GRE_KEY_SHIFT;
uint32_t mask = GRE_KEY_MASK;
if (tunnel->t_key_mask == GRE_KEY_ENTROPY) {
min = GRE_KEY_ENTROPY_MIN;
max = GRE_KEY_ENTROPY_MAX;
shift = GRE_KEY_ENTROPY_SHIFT;
mask = GRE_KEY_ENTROPY;
}
if (ifr->ifr_vnetid < min || ifr->ifr_vnetid > max)
return (EINVAL);
key = htonl(ifr->ifr_vnetid << shift);
/* commit */
tunnel->t_key_mask = mask;
tunnel->t_key = key;
return (0);
}
static int
gre_get_vnetid(struct gre_tunnel *tunnel, struct ifreq *ifr)
{
int shift;
switch (tunnel->t_key_mask) {
case GRE_KEY_NONE:
return (EADDRNOTAVAIL);
case GRE_KEY_ENTROPY:
shift = GRE_KEY_ENTROPY_SHIFT;
break;
case GRE_KEY_MASK:
shift = GRE_KEY_SHIFT;
break;
}
ifr->ifr_vnetid = ntohl(tunnel->t_key) >> shift;
return (0);
}
static int
gre_del_vnetid(struct gre_tunnel *tunnel)
{
tunnel->t_key_mask = GRE_KEY_NONE;
return (0);
}
static int
gre_set_vnetflowid(struct gre_tunnel *tunnel, struct ifreq *ifr)
{
uint32_t mask, key;
if (tunnel->t_key_mask == GRE_KEY_NONE)
return (EADDRNOTAVAIL);
mask = ifr->ifr_vnetid ? GRE_KEY_ENTROPY : GRE_KEY_MASK;
if (tunnel->t_key_mask == mask) {
/* nop */
return (0);
}
key = ntohl(tunnel->t_key);
if (mask == GRE_KEY_ENTROPY) {
if (key > GRE_KEY_ENTROPY_MAX)
return (ERANGE);
key = htonl(key << GRE_KEY_ENTROPY_SHIFT);
} else
key = htonl(key >> GRE_KEY_ENTROPY_SHIFT);
/* commit */
tunnel->t_key_mask = mask;
tunnel->t_key = key;
return (0);
}
static int
gre_get_vnetflowid(struct gre_tunnel *tunnel, struct ifreq *ifr)
{
if (tunnel->t_key_mask == GRE_KEY_NONE)
return (EADDRNOTAVAIL);
ifr->ifr_vnetid = tunnel->t_key_mask == GRE_KEY_ENTROPY;
return (0);
}
static int
mgre_up(struct mgre_softc *sc)
{
unsigned int hlen;
switch (sc->sc_tunnel.t_af) {
case AF_UNSPEC:
return (EDESTADDRREQ);
case AF_INET:
hlen = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
hlen = sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
default:
unhandled_af(sc->sc_tunnel.t_af);
}
hlen += sizeof(struct gre_header);
if (sc->sc_tunnel.t_key_mask != GRE_KEY_NONE)
hlen += sizeof(struct gre_h_key);
NET_ASSERT_LOCKED();
if (RBT_INSERT(mgre_tree, &mgre_tree, sc) != NULL)
return (EADDRINUSE);
sc->sc_if.if_hdrlen = hlen;
SET(sc->sc_if.if_flags, IFF_RUNNING);
return (0);
}
static int
mgre_down(struct mgre_softc *sc)
{
NET_ASSERT_LOCKED();
CLR(sc->sc_if.if_flags, IFF_RUNNING);
sc->sc_if.if_hdrlen = GRE_HDRLEN; /* symmetry */
RBT_REMOVE(mgre_tree, &mgre_tree, sc);
/* barrier? */
return (0);
}
static int
egre_up(struct egre_softc *sc)
{
if (sc->sc_tunnel.t_af == AF_UNSPEC)
return (EDESTADDRREQ);
NET_ASSERT_LOCKED();
if (RBT_INSERT(egre_tree, &egre_tree, sc) != NULL)
return (EADDRINUSE);
SET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
return (0);
}
static int
egre_down(struct egre_softc *sc)
{
NET_ASSERT_LOCKED();
CLR(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
RBT_REMOVE(egre_tree, &egre_tree, sc);
/* barrier? */
return (0);
}
static int
egre_media_change(struct ifnet *ifp)
{
return (ENOTTY);
}
static void
egre_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
imr->ifm_active = IFM_ETHER | IFM_AUTO;
imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
}
static int
nvgre_up(struct nvgre_softc *sc)
{
struct gre_tunnel *tunnel = &sc->sc_tunnel;
struct ifnet *ifp0;
void *inm;
int error;
if (tunnel->t_af == AF_UNSPEC)
return (EDESTADDRREQ);
ifp0 = if_get(sc->sc_ifp0);
if (ifp0 == NULL)
return (ENXIO);
if (!ISSET(ifp0->if_flags, IFF_MULTICAST)) {
error = ENODEV;
goto put;
}
NET_ASSERT_LOCKED();
if (RBT_INSERT(nvgre_mcast_tree, &nvgre_mcast_tree, sc) != NULL) {
error = EADDRINUSE;
goto put;
}
if (RBT_INSERT(nvgre_ucast_tree, &nvgre_ucast_tree, sc) != NULL) {
error = EADDRINUSE;
goto remove_mcast;
}
switch (tunnel->t_af) {
case AF_INET:
inm = in_addmulti(&tunnel->t_dst4, ifp0);
if (inm == NULL) {
error = ECONNABORTED;
goto remove_ucast;
}
break;
#ifdef INET6
case AF_INET6:
inm = in6_addmulti(&tunnel->t_dst6, ifp0, &error);
if (inm == NULL) {
/* error is already set */
goto remove_ucast;
}
break;
#endif /* INET6 */
default:
unhandled_af(tunnel->t_af);
}
if_linkstatehook_add(ifp0, &sc->sc_ltask);
if_detachhook_add(ifp0, &sc->sc_dtask);
if_put(ifp0);
sc->sc_inm = inm;
SET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
return (0);
remove_ucast:
RBT_REMOVE(nvgre_ucast_tree, &nvgre_ucast_tree, sc);
remove_mcast:
RBT_REMOVE(nvgre_mcast_tree, &nvgre_mcast_tree, sc);
put:
if_put(ifp0);
return (error);
}
static int
nvgre_down(struct nvgre_softc *sc)
{
struct gre_tunnel *tunnel = &sc->sc_tunnel;
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct taskq *softnet = net_tq(ifp->if_index);
struct ifnet *ifp0;
NET_ASSERT_LOCKED();
CLR(ifp->if_flags, IFF_RUNNING);
NET_UNLOCK();
ifq_barrier(&ifp->if_snd);
if (!task_del(softnet, &sc->sc_send_task))
taskq_barrier(softnet);
NET_LOCK();
mq_purge(&sc->sc_send_list);
ifp0 = if_get(sc->sc_ifp0);
if (ifp0 != NULL) {
if_detachhook_del(ifp0, &sc->sc_dtask);
if_linkstatehook_del(ifp0, &sc->sc_ltask);
}
if_put(ifp0);
switch (tunnel->t_af) {
case AF_INET:
in_delmulti(sc->sc_inm);
break;
#ifdef INET6
case AF_INET6:
in6_delmulti(sc->sc_inm);
break;
#endif
default:
unhandled_af(tunnel->t_af);
}
RBT_REMOVE(nvgre_ucast_tree, &nvgre_ucast_tree, sc);
RBT_REMOVE(nvgre_mcast_tree, &nvgre_mcast_tree, sc);
return (0);
}
static void
nvgre_link_change(void *arg)
{
/* nop */
}
static void
nvgre_detach(void *arg)
{
struct nvgre_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ac.ac_if;
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
nvgre_down(sc);
if_down(ifp);
}
sc->sc_ifp0 = 0;
}
static int
nvgre_set_parent(struct nvgre_softc *sc, const char *parent)
{
struct ifnet *ifp0;
ifp0 = if_unit(parent);
if (ifp0 == NULL)
return (EINVAL);
if (!ISSET(ifp0->if_flags, IFF_MULTICAST)) {
if_put(ifp0);
return (EPROTONOSUPPORT);
}
/* commit */
sc->sc_ifp0 = ifp0->if_index;
if_put(ifp0);
return (0);
}
static int
nvgre_add_addr(struct nvgre_softc *sc, const struct ifbareq *ifba)
{
struct sockaddr_in *sin;
#ifdef INET6
struct sockaddr_in6 *sin6;
struct sockaddr_in6 src6 = {
.sin6_len = sizeof(src6),
.sin6_family = AF_UNSPEC,
};
int error;
#endif
union gre_addr endpoint;
unsigned int type;
/* ignore ifba_ifsname */
if (ISSET(ifba->ifba_flags, ~IFBAF_TYPEMASK))
return (EINVAL);
switch (ifba->ifba_flags & IFBAF_TYPEMASK) {
case IFBAF_DYNAMIC:
type = EBE_DYNAMIC;
break;
case IFBAF_STATIC:
type = EBE_STATIC;
break;
default:
return (EINVAL);
}
memset(&endpoint, 0, sizeof(endpoint));
if (ifba->ifba_dstsa.ss_family != sc->sc_tunnel.t_af)
return (EAFNOSUPPORT);
switch (ifba->ifba_dstsa.ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)&ifba->ifba_dstsa;
if (in_nullhost(sin->sin_addr) ||
IN_MULTICAST(sin->sin_addr.s_addr))
return (EADDRNOTAVAIL);
endpoint.in4 = sin->sin_addr;
break;
#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&ifba->ifba_dstsa;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
return (EADDRNOTAVAIL);
in6_recoverscope(&src6, &sc->sc_tunnel.t_src6);
if (src6.sin6_scope_id != sin6->sin6_scope_id)
return (EADDRNOTAVAIL);
error = in6_embedscope(&endpoint.in6, sin6, NULL);
if (error != 0)
return (error);
break;
#endif
default: /* AF_UNSPEC */
return (EADDRNOTAVAIL);
}
return (etherbridge_add_addr(&sc->sc_eb, &endpoint,
&ifba->ifba_dst, type));
}
static int
nvgre_del_addr(struct nvgre_softc *sc, const struct ifbareq *ifba)
{
return (etherbridge_del_addr(&sc->sc_eb, &ifba->ifba_dst));
}
static void
nvgre_start(struct ifnet *ifp)
{
struct nvgre_softc *sc = ifp->if_softc;
const struct gre_tunnel *tunnel = &sc->sc_tunnel;
union gre_addr gateway;
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct ether_header *eh;
struct mbuf *m, *m0;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
if (!gre_allow) {
ifq_purge(&ifp->if_snd);
return;
}
while ((m0 = ifq_dequeue(&ifp->if_snd)) != NULL) {
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_ether(if_bpf, m0, BPF_DIRECTION_OUT);
#endif
eh = mtod(m0, struct ether_header *);
if (ETHER_IS_BROADCAST(eh->ether_dhost))
gateway = tunnel->t_dst;
else {
const union gre_addr *endpoint;
smr_read_enter();
endpoint = etherbridge_resolve_ea(&sc->sc_eb,
(struct ether_addr *)eh->ether_dhost);
if (endpoint == NULL) {
/* "flood" to unknown hosts */
endpoint = &tunnel->t_dst;
}
gateway = *endpoint;
smr_read_leave();
}
/* force prepend mbuf because of alignment problems */
m = m_get(M_DONTWAIT, m0->m_type);
if (m == NULL) {
m_freem(m0);
continue;
}
M_MOVE_PKTHDR(m, m0);
m->m_next = m0;
m_align(m, 0);
m->m_len = 0;
m = gre_encap_dst(tunnel, &gateway, m,
htons(ETHERTYPE_TRANSETHER),
tunnel->t_ttl, gre_l2_tos(tunnel, m));
if (m == NULL)
continue;
m->m_flags &= ~(M_BCAST|M_MCAST);
m->m_pkthdr.ph_rtableid = tunnel->t_rtableid;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
ml_enqueue(&ml, m);
}
if (!ml_empty(&ml)) {
if (mq_enlist(&sc->sc_send_list, &ml) == 0)
task_add(net_tq(ifp->if_index), &sc->sc_send_task);
/* else set OACTIVE? */
}
}
static uint64_t
nvgre_send4(struct nvgre_softc *sc, struct mbuf_list *ml)
{
struct ip_moptions imo;
struct mbuf *m;
uint64_t oerrors = 0;
imo.imo_ifidx = sc->sc_ifp0;
imo.imo_ttl = sc->sc_tunnel.t_ttl;
imo.imo_loop = 0;
NET_LOCK();
while ((m = ml_dequeue(ml)) != NULL) {
if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &imo, NULL, 0) != 0)
oerrors++;
}
NET_UNLOCK();
return (oerrors);
}
#ifdef INET6
static uint64_t
nvgre_send6(struct nvgre_softc *sc, struct mbuf_list *ml)
{
struct ip6_moptions im6o;
struct mbuf *m;
uint64_t oerrors = 0;
im6o.im6o_ifidx = sc->sc_ifp0;
im6o.im6o_hlim = sc->sc_tunnel.t_ttl;
im6o.im6o_loop = 0;
NET_LOCK();
while ((m = ml_dequeue(ml)) != NULL) {
if (ip6_output(m, NULL, NULL, 0, &im6o, NULL) != 0)
oerrors++;
}
NET_UNLOCK();
return (oerrors);
}
#endif /* INET6 */
static void
nvgre_send(void *arg)
{
struct nvgre_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ac.ac_if;
sa_family_t af = sc->sc_tunnel.t_af;
struct mbuf_list ml;
uint64_t oerrors;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return;
mq_delist(&sc->sc_send_list, &ml);
if (ml_empty(&ml))
return;
switch (af) {
case AF_INET:
oerrors = nvgre_send4(sc, &ml);
break;
#ifdef INET6
case AF_INET6:
oerrors = nvgre_send6(sc, &ml);
break;
#endif
default:
unhandled_af(af);
/* NOTREACHED */
}
ifp->if_oerrors += oerrors; /* XXX should be ifq_oerrors */
}
static int
eoip_up(struct eoip_softc *sc)
{
if (sc->sc_tunnel.t_af == AF_UNSPEC)
return (EDESTADDRREQ);
NET_ASSERT_LOCKED();
if (RBT_INSERT(eoip_tree, &eoip_tree, sc) != NULL)
return (EADDRINUSE);
SET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
if (sc->sc_ka_state != GRE_KA_NONE) {
sc->sc_ka_holdmax = sc->sc_ka_count;
eoip_keepalive_send(sc);
}
return (0);
}
static int
eoip_down(struct eoip_softc *sc)
{
NET_ASSERT_LOCKED();
CLR(sc->sc_ac.ac_if.if_flags, IFF_RUNNING);
if (sc->sc_ka_state != GRE_KA_NONE) {
timeout_del_barrier(&sc->sc_ka_hold);
timeout_del_barrier(&sc->sc_ka_send);
sc->sc_ka_state = GRE_KA_DOWN;
gre_link_state(&sc->sc_ac.ac_if, sc->sc_ka_state);
}
RBT_REMOVE(eoip_tree, &eoip_tree, sc);
return (0);
}
static void
eoip_start(struct ifnet *ifp)
{
struct eoip_softc *sc = ifp->if_softc;
struct mbuf *m0, *m;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
if (!gre_allow) {
ifq_purge(&ifp->if_snd);
return;
}
while ((m0 = ifq_dequeue(&ifp->if_snd)) != NULL) {
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_ether(if_bpf, m0, BPF_DIRECTION_OUT);
#endif
/* force prepend mbuf because of alignment problems */
m = m_get(M_DONTWAIT, m0->m_type);
if (m == NULL) {
m_freem(m0);
continue;
}
M_MOVE_PKTHDR(m, m0);
m->m_next = m0;
m_align(m, 0);
m->m_len = 0;
m = eoip_encap(sc, m, gre_l2_tos(&sc->sc_tunnel, m));
if (m == NULL || gre_ip_output(&sc->sc_tunnel, m) != 0) {
ifp->if_oerrors++;
continue;
}
}
}
static struct mbuf *
eoip_encap(struct eoip_softc *sc, struct mbuf *m, uint8_t tos)
{
struct gre_header *gh;
struct gre_h_key_eoip *eoiph;
int len = m->m_pkthdr.len;
m = m_prepend(m, sizeof(*gh) + sizeof(*eoiph), M_DONTWAIT);
if (m == NULL)
return (NULL);
gh = mtod(m, struct gre_header *);
gh->gre_flags = htons(GRE_VERS_1 | GRE_KP);
gh->gre_proto = htons(GRE_EOIP);
eoiph = (struct gre_h_key_eoip *)(gh + 1);
htobem16(&eoiph->eoip_len, len);
eoiph->eoip_tunnel_id = sc->sc_tunnel_id;
return (gre_encap_ip(&sc->sc_tunnel, m, sc->sc_tunnel.t_ttl, tos));
}
static void
eoip_keepalive_send(void *arg)
{
struct eoip_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct mbuf *m;
int linkhdr;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return;
/* this is really conservative */
#ifdef INET6
linkhdr = max_linkhdr + MAX(sizeof(struct ip), sizeof(struct ip6_hdr)) +
sizeof(struct gre_header) + sizeof(struct gre_h_key_eoip);
#else
linkhdr = max_linkhdr + sizeof(struct ip) +
sizeof(struct gre_header) + sizeof(struct gre_h_key_eoip);
#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return;
if (linkhdr > MHLEN) {
MCLGETL(m, M_DONTWAIT, linkhdr);
if (!ISSET(m->m_flags, M_EXT)) {
m_freem(m);
return;
}
}
m->m_pkthdr.pf.prio = ifp->if_llprio;
m->m_pkthdr.len = m->m_len = linkhdr;
m_adj(m, linkhdr);
m = eoip_encap(sc, m, gre_l2_tos(&sc->sc_tunnel, m));
if (m == NULL)
return;
gre_ip_output(&sc->sc_tunnel, m);
timeout_add_sec(&sc->sc_ka_send, sc->sc_ka_timeo);
}
static void
eoip_keepalive_hold(void *arg)
{
struct eoip_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ac.ac_if;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return;
NET_LOCK();
sc->sc_ka_state = GRE_KA_DOWN;
gre_link_state(ifp, sc->sc_ka_state);
NET_UNLOCK();
}
static void
eoip_keepalive_recv(struct eoip_softc *sc)
{
switch (sc->sc_ka_state) {
case GRE_KA_NONE:
return;
case GRE_KA_DOWN:
sc->sc_ka_state = GRE_KA_HOLD;
sc->sc_ka_holdcnt = sc->sc_ka_holdmax;
sc->sc_ka_holdmax = MIN(sc->sc_ka_holdmax * 2,
16 * sc->sc_ka_count);
break;
case GRE_KA_HOLD:
if (--sc->sc_ka_holdcnt > 0)
break;
sc->sc_ka_state = GRE_KA_UP;
gre_link_state(&sc->sc_ac.ac_if, sc->sc_ka_state);
break;
case GRE_KA_UP:
sc->sc_ka_holdmax--;
sc->sc_ka_holdmax = MAX(sc->sc_ka_holdmax, sc->sc_ka_count);
break;
}
timeout_add_sec(&sc->sc_ka_hold, sc->sc_ka_timeo * sc->sc_ka_count);
}
static struct mbuf *
eoip_input(struct gre_tunnel *key, struct mbuf *m,
const struct gre_header *gh, uint8_t otos, int iphlen)
{
struct eoip_softc *sc;
struct gre_h_key_eoip *eoiph;
int hlen, len;
caddr_t buf;
if (gh->gre_flags != htons(GRE_KP | GRE_VERS_1))
goto decline;
hlen = iphlen + sizeof(*gh) + sizeof(*eoiph);
if (m->m_pkthdr.len < hlen)
goto decline;
m = m_pullup(m, hlen);
if (m == NULL)
return (NULL);
buf = mtod(m, caddr_t);
gh = (struct gre_header *)(buf + iphlen);
eoiph = (struct gre_h_key_eoip *)(gh + 1);
key->t_key = eoiph->eoip_tunnel_id;
NET_ASSERT_LOCKED();
sc = RBT_FIND(eoip_tree, &eoip_tree, (const struct eoip_softc *)key);
if (sc == NULL)
goto decline;
/* it's ours now */
len = bemtoh16(&eoiph->eoip_len);
if (len == 0) {
eoip_keepalive_recv(sc);
goto drop;
}
m = gre_ether_align(m, hlen);
if (m == NULL)
return (NULL);
if (m->m_pkthdr.len < len)
goto drop;
if (m->m_pkthdr.len != len)
m_adj(m, len - m->m_pkthdr.len);
m->m_flags &= ~(M_MCAST|M_BCAST);
gre_l2_prio(&sc->sc_tunnel, m, otos);
if_vinput(&sc->sc_ac.ac_if, m);
return (NULL);
decline:
return (m);
drop:
m_freem(m);
return (NULL);
}
const struct sysctl_bounded_args gre_vars[] = {
{ GRECTL_ALLOW, &gre_allow, 0, 1 },
{ GRECTL_WCCP, &gre_wccp, 0, 1 },
};
int
gre_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
NET_LOCK();
error = sysctl_bounded_arr(gre_vars, nitems(gre_vars), name,
namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return error;
}
static inline int
gre_ip_cmp(int af, const union gre_addr *a, const union gre_addr *b)
{
switch (af) {
#ifdef INET6
case AF_INET6:
return (memcmp(&a->in6, &b->in6, sizeof(a->in6)));
#endif /* INET6 */
case AF_INET:
return (memcmp(&a->in4, &b->in4, sizeof(a->in4)));
default:
unhandled_af(af);
}
return (0);
}
static int
gre_cmp_src(const struct gre_tunnel *a, const struct gre_tunnel *b)
{
uint32_t ka, kb;
uint32_t mask;
int rv;
/* is K set at all? */
ka = a->t_key_mask & GRE_KEY_ENTROPY;
kb = b->t_key_mask & GRE_KEY_ENTROPY;
/* sort by whether K is set */
if (ka > kb)
return (1);
if (ka < kb)
return (-1);
/* is K set on both? */
if (ka != GRE_KEY_NONE) {
/* get common prefix */
mask = a->t_key_mask & b->t_key_mask;
ka = a->t_key & mask;
kb = b->t_key & mask;
/* sort by common prefix */
if (ka > kb)
return (1);
if (ka < kb)
return (-1);
}
/* sort by routing table */
if (a->t_rtableid > b->t_rtableid)
return (1);
if (a->t_rtableid < b->t_rtableid)
return (-1);
/* sort by address */
if (a->t_af > b->t_af)
return (1);
if (a->t_af < b->t_af)
return (-1);
rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src);
if (rv != 0)
return (rv);
return (0);
}
static int
gre_cmp(const struct gre_tunnel *a, const struct gre_tunnel *b)
{
int rv;
rv = gre_cmp_src(a, b);
if (rv != 0)
return (rv);
return (gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst));
}
static inline int
mgre_cmp(const struct mgre_softc *a, const struct mgre_softc *b)
{
return (gre_cmp_src(&a->sc_tunnel, &b->sc_tunnel));
}
RBT_GENERATE(mgre_tree, mgre_softc, sc_entry, mgre_cmp);
static inline int
egre_cmp(const struct egre_softc *a, const struct egre_softc *b)
{
return (gre_cmp(&a->sc_tunnel, &b->sc_tunnel));
}
RBT_GENERATE(egre_tree, egre_softc, sc_entry, egre_cmp);
static int
nvgre_cmp_tunnel(const struct gre_tunnel *a, const struct gre_tunnel *b)
{
uint32_t ka, kb;
ka = a->t_key & GRE_KEY_ENTROPY;
kb = b->t_key & GRE_KEY_ENTROPY;
/* sort by common prefix */
if (ka > kb)
return (1);
if (ka < kb)
return (-1);
/* sort by routing table */
if (a->t_rtableid > b->t_rtableid)
return (1);
if (a->t_rtableid < b->t_rtableid)
return (-1);
/* sort by address */
if (a->t_af > b->t_af)
return (1);
if (a->t_af < b->t_af)
return (-1);
return (0);
}
static inline int
nvgre_cmp_ucast(const struct nvgre_softc *na, const struct nvgre_softc *nb)
{
const struct gre_tunnel *a = &na->sc_tunnel;
const struct gre_tunnel *b = &nb->sc_tunnel;
int rv;
rv = nvgre_cmp_tunnel(a, b);
if (rv != 0)
return (rv);
rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src);
if (rv != 0)
return (rv);
return (0);
}
static int
nvgre_cmp_mcast(const struct gre_tunnel *a, const union gre_addr *aa,
unsigned int if0idxa, const struct gre_tunnel *b,
const union gre_addr *ab,unsigned int if0idxb)
{
int rv;
rv = nvgre_cmp_tunnel(a, b);
if (rv != 0)
return (rv);
rv = gre_ip_cmp(a->t_af, aa, ab);
if (rv != 0)
return (rv);
if (if0idxa > if0idxb)
return (1);
if (if0idxa < if0idxb)
return (-1);
return (0);
}
static inline int
nvgre_cmp_mcast_sc(const struct nvgre_softc *na, const struct nvgre_softc *nb)
{
const struct gre_tunnel *a = &na->sc_tunnel;
const struct gre_tunnel *b = &nb->sc_tunnel;
return (nvgre_cmp_mcast(a, &a->t_dst, na->sc_ifp0,
b, &b->t_dst, nb->sc_ifp0));
}
RBT_GENERATE(nvgre_ucast_tree, nvgre_softc, sc_uentry, nvgre_cmp_ucast);
RBT_GENERATE(nvgre_mcast_tree, nvgre_softc, sc_mentry, nvgre_cmp_mcast_sc);
static inline int
eoip_cmp(const struct eoip_softc *ea, const struct eoip_softc *eb)
{
const struct gre_tunnel *a = &ea->sc_tunnel;
const struct gre_tunnel *b = &eb->sc_tunnel;
int rv;
if (a->t_key > b->t_key)
return (1);
if (a->t_key < b->t_key)
return (-1);
/* sort by routing table */
if (a->t_rtableid > b->t_rtableid)
return (1);
if (a->t_rtableid < b->t_rtableid)
return (-1);
/* sort by address */
if (a->t_af > b->t_af)
return (1);
if (a->t_af < b->t_af)
return (-1);
rv = gre_ip_cmp(a->t_af, &a->t_src, &b->t_src);
if (rv != 0)
return (rv);
rv = gre_ip_cmp(a->t_af, &a->t_dst, &b->t_dst);
if (rv != 0)
return (rv);
return (0);
}
RBT_GENERATE(eoip_tree, eoip_softc, sc_entry, eoip_cmp);
static int
nvgre_eb_port_eq(void *arg, void *a, void *b)
{
struct nvgre_softc *sc = arg;
return (gre_ip_cmp(sc->sc_tunnel.t_af, a, b) == 0);
}
static void *
nvgre_eb_port_take(void *arg, void *port)
{
union gre_addr *ea = port;
union gre_addr *endpoint;
endpoint = pool_get(&nvgre_endpoint_pool, PR_NOWAIT);
if (endpoint == NULL)
return (NULL);
*endpoint = *ea;
return (endpoint);
}
static void
nvgre_eb_port_rele(void *arg, void *port)
{
union gre_addr *endpoint = port;
pool_put(&nvgre_endpoint_pool, endpoint);
}
static size_t
nvgre_eb_port_ifname(void *arg, char *dst, size_t len, void *port)
{
struct nvgre_softc *sc = arg;
return (strlcpy(dst, sc->sc_ac.ac_if.if_xname, len));
}
static void
nvgre_eb_port_sa(void *arg, struct sockaddr_storage *ss, void *port)
{
struct nvgre_softc *sc = arg;
union gre_addr *endpoint = port;
switch (sc->sc_tunnel.t_af) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)ss;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_addr = endpoint->in4;
break;
}
#ifdef INET6
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
in6_recoverscope(sin6, &endpoint->in6);
break;
}
#endif /* INET6 */
default:
unhandled_af(sc->sc_tunnel.t_af);
}
}
97
18
17
159
128
6
12
26
31
8
30
17
19
7
14
9
14
65
64
4
40
9
29
30
76
75
2
3
11
8
21
79
63
2
12
2
15
13
2
17
8
9
11
11
1
2
2
2
3
33
30
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/* $OpenBSD: pf_ruleset.c,v 1.19 2022/07/20 09:33:11 mbuhl Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002,2003 Henning Brauer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Effort sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
*
*/
#include <sys/param.h>
#include <sys/socket.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/pool.h>
#endif /* _KERNEL */
#include <sys/syslog.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <net/pfvar.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif /* INET6 */
#ifdef _KERNEL
#define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO)
#define rs_free(x, siz) free(x, M_TEMP, siz)
#define rs_pool_get_anchor() pool_get(&pf_anchor_pl, \
PR_WAITOK|PR_LIMITFAIL|PR_ZERO)
#define rs_pool_put_anchor(x) pool_put(&pf_anchor_pl, x)
struct pool pf_anchor_pl;
#else /* !_KERNEL */
/* Userland equivalents so we can lend code to pfctl et al. */
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define rs_malloc(x) calloc(1, x)
#define rs_free(x, siz) freezero(x, siz)
#define rs_pool_get_anchor() calloc(1, sizeof(struct pf_anchor))
#define rs_pool_put_anchor(x) freezero(x, sizeof(struct pf_anchor))
#ifdef PFDEBUG
#include <sys/stdarg.h> /* for DPFPRINTF() */
#endif /* PFDEBUG */
#endif /* _KERNEL */
struct pf_anchor_global pf_anchors;
struct pf_anchor pf_main_anchor;
static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
static __inline int
pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
{
int c = strcmp(a->path, b->path);
return (c ? (c < 0 ? -1 : 1) : 0);
}
void
pf_init_ruleset(struct pf_ruleset *ruleset)
{
memset(ruleset, 0, sizeof(struct pf_ruleset));
TAILQ_INIT(&ruleset->rules.queues[0]);
TAILQ_INIT(&ruleset->rules.queues[1]);
ruleset->rules.active.ptr = &ruleset->rules.queues[0];
ruleset->rules.inactive.ptr = &ruleset->rules.queues[1];
}
struct pf_anchor *
pf_find_anchor(const char *path)
{
struct pf_anchor *key, *found;
key = rs_malloc(sizeof(*key));
if (key == NULL)
return (NULL);
strlcpy(key->path, path, sizeof(key->path));
found = RB_FIND(pf_anchor_global, &pf_anchors, key);
rs_free(key, sizeof(*key));
return (found);
}
struct pf_ruleset *
pf_find_ruleset(const char *path)
{
struct pf_anchor *anchor;
while (*path == '/')
path++;
if (!*path)
return (&pf_main_ruleset);
anchor = pf_find_anchor(path);
if (anchor == NULL)
return (NULL);
else
return (&anchor->ruleset);
}
struct pf_ruleset *
pf_get_leaf_ruleset(char *path, char **path_remainder)
{
struct pf_ruleset *ruleset;
char *leaf, *p;
int i = 0;
p = path;
while (*p == '/')
p++;
ruleset = pf_find_ruleset(p);
leaf = p;
while (ruleset == NULL) {
leaf = strrchr(p, '/');
if (leaf != NULL) {
*leaf = '\0';
i++;
ruleset = pf_find_ruleset(p);
} else {
leaf = path;
/*
* if no path component exists, then main ruleset is
* our parent.
*/
ruleset = &pf_main_ruleset;
}
}
if (path_remainder != NULL)
*path_remainder = leaf;
/* restore slashes in path. */
while (i != 0) {
while (*leaf != '\0')
leaf++;
*leaf = '/';
i--;
}
return (ruleset);
}
struct pf_anchor *
pf_create_anchor(struct pf_anchor *parent, const char *aname)
{
struct pf_anchor *anchor, *dup;
if (!*aname || (strlen(aname) >= PF_ANCHOR_NAME_SIZE) ||
((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH)))
return (NULL);
anchor = rs_pool_get_anchor();
if (anchor == NULL)
return (NULL);
RB_INIT(&anchor->children);
strlcpy(anchor->name, aname, sizeof(anchor->name));
if (parent != NULL) {
/*
* Make sure path for levels 2, 3, ... is terminated by '/':
* 1/2/3/...
*/
strlcpy(anchor->path, parent->path, sizeof(anchor->path));
strlcat(anchor->path, "/", sizeof(anchor->path));
}
strlcat(anchor->path, anchor->name, sizeof(anchor->path));
if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != NULL) {
DPFPRINTF(LOG_NOTICE,
"%s: RB_INSERT to global '%s' '%s' collides with '%s' '%s'",
__func__, anchor->path, anchor->name, dup->path, dup->name);
rs_pool_put_anchor(anchor);
return (NULL);
}
if (parent != NULL) {
anchor->parent = parent;
dup = RB_INSERT(pf_anchor_node, &parent->children, anchor);
if (dup != NULL) {
DPFPRINTF(LOG_NOTICE,
"%s: RB_INSERT to parent '%s' '%s' collides with "
"'%s' '%s'", __func__, anchor->path, anchor->name,
dup->path, dup->name);
RB_REMOVE(pf_anchor_global, &pf_anchors,
anchor);
rs_pool_put_anchor(anchor);
return (NULL);
}
}
pf_init_ruleset(&anchor->ruleset);
anchor->ruleset.anchor = anchor;
return (anchor);
}
struct pf_ruleset *
pf_find_or_create_ruleset(const char *path)
{
char *p, *aname, *r;
struct pf_ruleset *ruleset;
struct pf_anchor *anchor;
if (path[0] == 0)
return (&pf_main_ruleset);
while (*path == '/')
path++;
ruleset = pf_find_ruleset(path);
if (ruleset != NULL)
return (ruleset);
p = rs_malloc(MAXPATHLEN);
if (p == NULL)
return (NULL);
strlcpy(p, path, MAXPATHLEN);
ruleset = pf_get_leaf_ruleset(p, &aname);
anchor = ruleset->anchor;
while (*aname == '/')
aname++;
/*
* aname is a path remainder, which contains nodes we must create. We
* process the aname path from left to right, effectively descending
* from parents to children.
*/
while ((r = strchr(aname, '/')) != NULL || *aname) {
if (r != NULL)
*r = 0;
anchor = pf_create_anchor(anchor, aname);
if (anchor == NULL) {
rs_free(p, MAXPATHLEN);
return (NULL);
}
if (r == NULL)
break;
else
aname = r + 1;
}
rs_free(p, MAXPATHLEN);
return (&anchor->ruleset);
}
void
pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
{
struct pf_anchor *parent;
while (ruleset != NULL) {
if (ruleset == &pf_main_ruleset ||
!RB_EMPTY(&ruleset->anchor->children) ||
ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
ruleset->topen)
return;
if (!TAILQ_EMPTY(ruleset->rules.active.ptr) ||
!TAILQ_EMPTY(ruleset->rules.inactive.ptr) ||
ruleset->rules.inactive.open)
return;
RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
if ((parent = ruleset->anchor->parent) != NULL)
RB_REMOVE(pf_anchor_node, &parent->children,
ruleset->anchor);
rs_pool_put_anchor(ruleset->anchor);
if (parent == NULL)
return;
ruleset = &parent->ruleset;
}
}
int
pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
const char *name)
{
char *p, *path;
struct pf_ruleset *ruleset;
r->anchor = NULL;
r->anchor_relative = 0;
r->anchor_wildcard = 0;
if (!name[0])
return (0);
path = rs_malloc(MAXPATHLEN);
if (path == NULL)
return (1);
if (name[0] == '/')
strlcpy(path, name + 1, MAXPATHLEN);
else {
/* relative path */
r->anchor_relative = 1;
if (s->anchor == NULL || !s->anchor->path[0])
path[0] = 0;
else
strlcpy(path, s->anchor->path, MAXPATHLEN);
while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
if (!path[0]) {
DPFPRINTF(LOG_NOTICE,
"pf_anchor_setup: .. beyond root");
rs_free(path, MAXPATHLEN);
return (1);
}
if ((p = strrchr(path, '/')) != NULL)
*p = 0;
else
path[0] = 0;
r->anchor_relative++;
name += 3;
}
if (path[0])
strlcat(path, "/", MAXPATHLEN);
strlcat(path, name, MAXPATHLEN);
}
if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
r->anchor_wildcard = 1;
*p = 0;
}
ruleset = pf_find_or_create_ruleset(path);
rs_free(path, MAXPATHLEN);
if (ruleset == NULL || ruleset == &pf_main_ruleset) {
DPFPRINTF(LOG_NOTICE,
"pf_anchor_setup: ruleset");
return (1);
}
r->anchor = ruleset->anchor;
r->anchor->refcnt++;
return (0);
}
int
pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
struct pfioc_rule *pr)
{
pr->anchor_call[0] = 0;
if (r->anchor == NULL)
return (0);
if (!r->anchor_relative) {
strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
strlcat(pr->anchor_call, r->anchor->path,
sizeof(pr->anchor_call));
} else {
char *a, *p;
int i;
a = rs_malloc(MAXPATHLEN);
if (a == NULL)
return (1);
if (rs == &pf_main_ruleset)
a[0] = 0;
else
strlcpy(a, rs->anchor->path, MAXPATHLEN);
for (i = 1; i < r->anchor_relative; ++i) {
if ((p = strrchr(a, '/')) == NULL)
p = a;
*p = 0;
strlcat(pr->anchor_call, "../",
sizeof(pr->anchor_call));
}
if (strncmp(a, r->anchor->path, strlen(a))) {
DPFPRINTF(LOG_NOTICE,
"pf_anchor_copyout: '%s' '%s'", a,
r->anchor->path);
rs_free(a, MAXPATHLEN);
return (1);
}
if (strlen(r->anchor->path) > strlen(a))
strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
strlen(a) + 1 : 0), sizeof(pr->anchor_call));
rs_free(a, MAXPATHLEN);
}
if (r->anchor_wildcard)
strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
sizeof(pr->anchor_call));
return (0);
}
void
pf_remove_anchor(struct pf_rule *r)
{
if (r->anchor == NULL)
return;
if (r->anchor->refcnt <= 0)
DPFPRINTF(LOG_NOTICE, "pf_remove_anchor: broken refcount");
else if (!--r->anchor->refcnt)
pf_remove_if_empty_ruleset(&r->anchor->ruleset);
r->anchor = NULL;
}
11
2
1
8
1
4
1
3
12
2
2
86
8
94
71
2
2
35
2
29
94
94
3
5
1
1
2
2
2
3
2
1
1
4
6
4
2
2
32
31
2
17
2
2
2
2
2
2
2
3
3
3
2
3
2
2
2
2
2
35
25
3
2
4
2
3
2
4
16
2
2
2
3
4
4
2
5
4
3
5
2
3
2
2
33
12
12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
/* $OpenBSD: wsmouse.c,v 1.69 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */
/*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou
* for the NetBSD Project.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ms.c 8.1 (Berkeley) 6/11/93
*/
/*
* Copyright (c) 2015, 2016 Ulf Brosziewski
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Mouse driver.
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/device.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <dev/wscons/wscons_features.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#include <dev/wscons/wseventvar.h>
#include <dev/wscons/wsmouseinput.h>
#include "wsmux.h"
#include "wsdisplay.h"
#include "wskbd.h"
#include <dev/wscons/wsmuxvar.h>
#if defined(WSMUX_DEBUG) && NWSMUX > 0
#define DPRINTF(x) if (wsmuxdebug) printf x
extern int wsmuxdebug;
#else
#define DPRINTF(x)
#endif
struct wsmouse_softc {
struct wsevsrc sc_base;
const struct wsmouse_accessops *sc_accessops;
void *sc_accesscookie;
struct wsmouseinput sc_input;
int sc_refcnt;
u_char sc_dying; /* device is being detached */
};
int wsmouse_match(struct device *, void *, void *);
void wsmouse_attach(struct device *, struct device *, void *);
int wsmouse_detach(struct device *, int);
int wsmouse_activate(struct device *, int);
int wsmouse_do_ioctl(struct wsmouse_softc *, u_long, caddr_t,
int, struct proc *);
#if NWSMUX > 0
int wsmouse_mux_open(struct wsevsrc *, struct wseventvar *);
int wsmouse_mux_close(struct wsevsrc *);
#endif
int wsmousedoioctl(struct device *, u_long, caddr_t, int,
struct proc *);
int wsmousedoopen(struct wsmouse_softc *, struct wseventvar *);
struct cfdriver wsmouse_cd = {
NULL, "wsmouse", DV_TTY
};
const struct cfattach wsmouse_ca = {
sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach,
wsmouse_detach, wsmouse_activate
};
#if NWSMUX > 0
struct wssrcops wsmouse_srcops = {
.type = WSMUX_MOUSE,
.dopen = wsmouse_mux_open,
.dclose = wsmouse_mux_close,
.dioctl = wsmousedoioctl,
.ddispioctl = NULL,
.dsetdisplay = NULL,
};
#endif
/*
* Print function (for parent devices).
*/
int
wsmousedevprint(void *aux, const char *pnp)
{
if (pnp)
printf("wsmouse at %s", pnp);
return (UNCONF);
}
int
wsmouse_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
wsmouse_attach(struct device *parent, struct device *self, void *aux)
{
struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
struct wsmousedev_attach_args *ap = aux;
#if NWSMUX > 0
int mux, error;
#endif
sc->sc_accessops = ap->accessops;
sc->sc_accesscookie = ap->accesscookie;
sc->sc_input.evar = &sc->sc_base.me_evp;
#if NWSMUX > 0
sc->sc_base.me_ops = &wsmouse_srcops;
mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
if (mux >= 0) {
error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
if (error)
printf(" attach error=%d", error);
else
printf(" mux %d", mux);
}
#else
#if 0 /* not worth keeping, especially since the default value is not -1... */
if (sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux >= 0)
printf(" (mux ignored)");
#endif
#endif /* NWSMUX > 0 */
printf("\n");
}
int
wsmouse_activate(struct device *self, int act)
{
struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
if (act == DVACT_DEACTIVATE)
sc->sc_dying = 1;
return (0);
}
/*
* Detach a mouse. To keep track of users of the softc we keep
* a reference count that's incremented while inside, e.g., read.
* If the mouse is active and the reference count is > 0 (0 is the
* normal state) we post an event and then wait for the process
* that had the reference to wake us up again. Then we blow away the
* vnode and return (which will deallocate the softc).
*/
int
wsmouse_detach(struct device *self, int flags)
{
struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
struct wseventvar *evar;
int maj, mn;
int s;
#if NWSMUX > 0
/* Tell parent mux we're leaving. */
if (sc->sc_base.me_parent != NULL) {
DPRINTF(("%s\n", __func__));
wsmux_detach_sc(&sc->sc_base);
}
#endif
/* If we're open ... */
evar = sc->sc_base.me_evp;
if (evar != NULL) {
s = spltty();
if (--sc->sc_refcnt >= 0) {
/* Wake everyone by generating a dummy event. */
if (++evar->put >= WSEVENT_QSIZE)
evar->put = 0;
WSEVENT_WAKEUP(evar);
/* Wait for processes to go away. */
if (tsleep_nsec(sc, PZERO, "wsmdet", SEC_TO_NSEC(60)))
printf("wsmouse_detach: %s didn't detach\n",
sc->sc_base.me_dv.dv_xname);
}
splx(s);
}
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == wsmouseopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
wsmouse_input_cleanup(&sc->sc_input);
return (0);
}
int
wsmouseopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct wsmouse_softc *sc;
struct wseventvar *evar;
int error, unit;
unit = minor(dev);
if (unit >= wsmouse_cd.cd_ndevs || /* make sure it was attached */
(sc = wsmouse_cd.cd_devs[unit]) == NULL)
return (ENXIO);
#if NWSMUX > 0
DPRINTF(("%s: %s mux=%p\n", __func__, sc->sc_base.me_dv.dv_xname,
sc->sc_base.me_parent));
#endif
if (sc->sc_dying)
return (EIO);
if ((flags & (FREAD | FWRITE)) == FWRITE)
return (0); /* always allow open for write
so ioctl() is possible. */
#if NWSMUX > 0
if (sc->sc_base.me_parent != NULL) {
/* Grab the mouse out of the greedy hands of the mux. */
DPRINTF(("%s: detach\n", __func__));
wsmux_detach_sc(&sc->sc_base);
}
#endif
if (sc->sc_base.me_evp != NULL)
return (EBUSY);
evar = &sc->sc_base.me_evar;
if (wsevent_init(evar))
return (EBUSY);
error = wsmousedoopen(sc, evar);
if (error)
wsevent_fini(evar);
return (error);
}
int
wsmouseclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct wsmouse_softc *sc =
(struct wsmouse_softc *)wsmouse_cd.cd_devs[minor(dev)];
struct wseventvar *evar = sc->sc_base.me_evp;
if ((flags & (FREAD | FWRITE)) == FWRITE)
/* Not open for read */
return (0);
sc->sc_base.me_evp = NULL;
(*sc->sc_accessops->disable)(sc->sc_accesscookie);
wsevent_fini(evar);
#if NWSMUX > 0
if (sc->sc_base.me_parent == NULL) {
int mux, error;
DPRINTF(("%s: attach\n", __func__));
mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
if (mux >= 0) {
error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
if (error)
printf("%s: can't attach mux (error=%d)\n",
sc->sc_base.me_dv.dv_xname, error);
}
}
#endif
return (0);
}
int
wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
{
int error;
/* The device could already be attached to a mux. */
if (sc->sc_base.me_evp != NULL)
return (EBUSY);
sc->sc_base.me_evp = evp;
wsmouse_input_reset(&sc->sc_input);
/* enable the device, and punt if that's not possible */
error = (*sc->sc_accessops->enable)(sc->sc_accesscookie);
if (error)
sc->sc_base.me_evp = NULL;
return (error);
}
int
wsmouseread(dev_t dev, struct uio *uio, int flags)
{
struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
int error;
if (sc->sc_dying)
return (EIO);
#ifdef DIAGNOSTIC
if (sc->sc_base.me_evp == NULL) {
printf("wsmouseread: evp == NULL\n");
return (EINVAL);
}
#endif
sc->sc_refcnt++;
error = wsevent_read(sc->sc_base.me_evp, uio, flags);
if (--sc->sc_refcnt < 0) {
wakeup(sc);
error = EIO;
}
return (error);
}
int
wsmouseioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
return (wsmousedoioctl(wsmouse_cd.cd_devs[minor(dev)],
cmd, data, flag, p));
}
/* A wrapper around the ioctl() workhorse to make reference counting easy. */
int
wsmousedoioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
int error;
sc->sc_refcnt++;
error = wsmouse_do_ioctl(sc, cmd, data, flag, p);
if (--sc->sc_refcnt < 0)
wakeup(sc);
return (error);
}
int
wsmouse_param_ioctl(struct wsmouse_softc *sc,
u_long cmd, struct wsmouse_param *params, u_int nparams)
{
struct wsmouse_param *buf;
int error, s, size;
if (params == NULL || nparams > WSMOUSECFG_MAX)
return (EINVAL);
size = nparams * sizeof(struct wsmouse_param);
buf = malloc(size, M_DEVBUF, M_WAITOK);
if (buf == NULL)
return (ENOMEM);
if ((error = copyin(params, buf, size))) {
free(buf, M_DEVBUF, size);
return (error);
}
s = spltty();
if (cmd == WSMOUSEIO_SETPARAMS) {
if (wsmouse_set_params((struct device *) sc, buf, nparams))
error = EINVAL;
} else {
if (wsmouse_get_params((struct device *) sc, buf, nparams))
error = EINVAL;
else
error = copyout(buf, params, size);
}
splx(s);
free(buf, M_DEVBUF, size);
return (error);
}
int
wsmouse_do_ioctl(struct wsmouse_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wseventvar *evar;
int error;
if (sc->sc_dying)
return (EIO);
/*
* Try the generic ioctls that the wsmouse interface supports.
*/
switch (cmd) {
case FIOASYNC:
case FIOSETOWN:
case TIOCSPGRP:
if ((flag & FWRITE) == 0)
return (EACCES);
}
switch (cmd) {
case FIONBIO: /* we will remove this someday (soon???) */
return (0);
case FIOASYNC:
if (sc->sc_base.me_evp == NULL)
return (EINVAL);
sc->sc_base.me_evp->async = *(int *)data != 0;
return (0);
case FIOGETOWN:
case TIOCGPGRP:
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
sigio_getown(&evar->sigio, cmd, data);
return (0);
case FIOSETOWN:
case TIOCSPGRP:
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
return (sigio_setown(&evar->sigio, cmd, data));
case WSMOUSEIO_GETPARAMS:
case WSMOUSEIO_SETPARAMS:
return (wsmouse_param_ioctl(sc, cmd,
((struct wsmouse_parameters *) data)->params,
((struct wsmouse_parameters *) data)->nparams));
}
/*
* Try the mouse driver for WSMOUSEIO ioctls. It returns -1
* if it didn't recognize the request.
*/
error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
data, flag, p);
return (error != -1 ? error : ENOTTY);
}
int
wsmousekqfilter(dev_t dev, struct knote *kn)
{
struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
if (sc->sc_base.me_evp == NULL)
return (ENXIO);
return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
}
#if NWSMUX > 0
int
wsmouse_mux_open(struct wsevsrc *me, struct wseventvar *evp)
{
struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
return (wsmousedoopen(sc, evp));
}
int
wsmouse_mux_close(struct wsevsrc *me)
{
struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
(*sc->sc_accessops->disable)(sc->sc_accesscookie);
sc->sc_base.me_evp = NULL;
return (0);
}
int
wsmouse_add_mux(int unit, struct wsmux_softc *muxsc)
{
struct wsmouse_softc *sc;
if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
(sc = wsmouse_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
return (EBUSY);
return (wsmux_attach_sc(muxsc, &sc->sc_base));
}
#endif /* NWSMUX > 0 */
void
wsmouse_buttons(struct device *sc, u_int buttons)
{
struct btn_state *btn = &((struct wsmouse_softc *) sc)->sc_input.btn;
if (btn->sync)
/* Restore the old state. */
btn->buttons ^= btn->sync;
btn->sync = btn->buttons ^ buttons;
btn->buttons = buttons;
}
void
wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
{
struct motion_state *motion =
&((struct wsmouse_softc *) sc)->sc_input.motion;
motion->dx = dx;
motion->dy = dy;
motion->dz = dz;
motion->dw = dw;
if (dx || dy || dz || dw)
motion->sync |= SYNC_DELTAS;
}
static inline void
set_x(struct position *pos, int x, u_int *sync, u_int mask)
{
if (*sync & mask) {
if (x == pos->x)
return;
pos->x -= pos->dx;
pos->acc_dx -= pos->dx;
}
if ((pos->dx = x - pos->x)) {
pos->x = x;
if ((pos->dx > 0) == (pos->acc_dx > 0))
pos->acc_dx += pos->dx;
else
pos->acc_dx = pos->dx;
*sync |= mask;
}
}
static inline void
set_y(struct position *pos, int y, u_int *sync, u_int mask)
{
if (*sync & mask) {
if (y == pos->y)
return;
pos->y -= pos->dy;
pos->acc_dy -= pos->dy;
}
if ((pos->dy = y - pos->y)) {
pos->y = y;
if ((pos->dy > 0) == (pos->acc_dy > 0))
pos->acc_dy += pos->dy;
else
pos->acc_dy = pos->dy;
*sync |= mask;
}
}
static inline void
cleardeltas(struct position *pos)
{
pos->dx = pos->acc_dx = 0;
pos->dy = pos->acc_dy = 0;
}
void
wsmouse_position(struct device *sc, int x, int y)
{
struct motion_state *motion =
&((struct wsmouse_softc *) sc)->sc_input.motion;
set_x(&motion->pos, x, &motion->sync, SYNC_X);
set_y(&motion->pos, y, &motion->sync, SYNC_Y);
}
static inline int
normalized_pressure(struct wsmouseinput *input, int pressure)
{
int limit = imax(input->touch.min_pressure, 1);
if (pressure >= limit)
return pressure;
else
return (pressure < 0 ? limit : 0);
}
void
wsmouse_touch(struct device *sc, int pressure, int contacts)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct touch_state *touch = &input->touch;
pressure = normalized_pressure(input, pressure);
contacts = (pressure ? imax(contacts, 1) : 0);
if (pressure == 0 || pressure != touch->pressure) {
/*
* pressure == 0: Drivers may report possibly arbitrary
* coordinates in this case; touch_update will correct them.
*/
touch->pressure = pressure;
touch->sync |= SYNC_PRESSURE;
}
if (contacts != touch->contacts) {
touch->contacts = contacts;
touch->sync |= SYNC_CONTACTS;
}
}
void
wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
struct mt_slot *mts;
u_int bit;
if (slot < 0 || slot >= mt->num_slots)
return;
bit = (1 << slot);
mt->frame |= bit;
mts = &mt->slots[slot];
set_x(&mts->pos, x, mt->sync + MTS_X, bit);
set_y(&mts->pos, y, mt->sync + MTS_Y, bit);
/* Is this a new touch? */
if ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit))
cleardeltas(&mts->pos);
pressure = normalized_pressure(input, pressure);
if (pressure != mts->pressure) {
mts->pressure = pressure;
mt->sync[MTS_PRESSURE] |= bit;
if (pressure) {
if ((mt->touches & bit) == 0) {
mt->num_touches++;
mt->touches |= bit;
mt->sync[MTS_TOUCH] |= bit;
mt->sync[MTS_X] |= bit;
mt->sync[MTS_Y] |= bit;
}
} else if (mt->touches & bit) {
mt->num_touches--;
mt->touches ^= bit;
mt->sync[MTS_TOUCH] |= bit;
mt->ptr_mask &= mt->touches;
}
}
}
void
wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_slot *mts;
if (WSMOUSE_IS_MT_CODE(type)) {
if (aux < 0 || aux >= input->mt.num_slots)
return;
mts = &input->mt.slots[aux];
}
switch (type) {
case WSMOUSE_REL_X:
value += input->motion.pos.x; /* fall through */
case WSMOUSE_ABS_X:
wsmouse_position(sc, value, input->motion.pos.y);
return;
case WSMOUSE_REL_Y:
value += input->motion.pos.y; /* fall through */
case WSMOUSE_ABS_Y:
wsmouse_position(sc, input->motion.pos.x, value);
return;
case WSMOUSE_PRESSURE:
wsmouse_touch(sc, value, input->touch.contacts);
return;
case WSMOUSE_CONTACTS:
/* Contact counts can be overridden by wsmouse_touch. */
if (value != input->touch.contacts) {
input->touch.contacts = value;
input->touch.sync |= SYNC_CONTACTS;
}
return;
case WSMOUSE_TOUCH_WIDTH:
if (value != input->touch.width) {
input->touch.width = value;
input->touch.sync |= SYNC_TOUCH_WIDTH;
}
return;
case WSMOUSE_MT_REL_X:
value += mts->pos.x; /* fall through */
case WSMOUSE_MT_ABS_X:
wsmouse_mtstate(sc, aux, value, mts->pos.y, mts->pressure);
return;
case WSMOUSE_MT_REL_Y:
value += mts->pos.y; /* fall through */
case WSMOUSE_MT_ABS_Y:
wsmouse_mtstate(sc, aux, mts->pos.x, value, mts->pressure);
return;
case WSMOUSE_MT_PRESSURE:
wsmouse_mtstate(sc, aux, mts->pos.x, mts->pos.y, value);
return;
}
}
/* Make touch and motion state consistent. */
void
wsmouse_touch_update(struct wsmouseinput *input)
{
struct motion_state *motion = &input->motion;
struct touch_state *touch = &input->touch;
if (touch->pressure == 0) {
/*
* There may be zero coordinates, or coordinates of
* touches with pressure values below min_pressure.
*/
if (motion->sync & SYNC_POSITION) {
/* Restore valid coordinates. */
motion->pos.x -= motion->pos.dx;
motion->pos.y -= motion->pos.dy;
motion->sync &= ~SYNC_POSITION;
}
if (touch->prev_contacts == 0)
touch->sync &= ~SYNC_PRESSURE;
}
if (touch->sync & SYNC_CONTACTS)
/* Suppress pointer movement. */
cleardeltas(&motion->pos);
if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
if (touch->pressure >= input->filter.pressure_hi)
touch->min_pressure = input->filter.pressure_lo;
else if (touch->pressure < input->filter.pressure_lo)
touch->min_pressure = input->filter.pressure_hi;
}
}
/* Normalize multitouch state. */
void
wsmouse_mt_update(struct wsmouseinput *input)
{
int i;
/*
* The same as above: There may be arbitrary coordinates if
* (pressure == 0). Clear the sync flags for touches that have
* been released.
*/
if (input->mt.frame & ~input->mt.touches) {
for (i = MTS_X; i < MTS_SIZE; i++)
input->mt.sync[i] &= input->mt.touches;
}
}
/* Return TRUE if a coordinate update may be noise. */
int
wsmouse_hysteresis(struct wsmouseinput *input, struct position *pos)
{
return (abs(pos->acc_dx) < input->filter.h.hysteresis
&& abs(pos->acc_dy) < input->filter.v.hysteresis);
}
/*
* Select the pointer-controlling MT slot.
*
* Pointer-control is assigned to slots with non-zero motion deltas if
* at least one such slot exists. This function doesn't impose any
* restrictions on the way drivers use wsmouse_mtstate(), it covers
* partial, unordered, and "delta-filtered" input.
*
* The "cycle" is the set of slots with X/Y updates in previous sync
* operations; it will be cleared and rebuilt whenever a slot that is
* being updated is already a member. If a cycle ends that doesn't
* contain the pointer-controlling slot, a new slot will be selected.
*/
void
wsmouse_ptr_ctrl(struct wsmouseinput *input)
{
struct mt_state *mt = &input->mt;
u_int updates;
int select, slot;
mt->prev_ptr = mt->ptr;
if (mt->num_touches <= 1) {
mt->ptr = mt->touches;
mt->ptr_cycle = mt->ptr;
return;
}
updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
FOREACHBIT(updates, slot) {
/*
* Touches that just produce noise are no problem if the
* frequency of zero deltas is high enough, but there might
* be no guarantee for that.
*/
if (wsmouse_hysteresis(input, &mt->slots[slot].pos))
updates ^= (1 << slot);
}
/*
* If there is no pointer-controlling slot, or if it should be
* masked, select a new one.
*/
select = ((mt->ptr & mt->touches & ~mt->ptr_mask) == 0);
/* Remove slots without coordinate deltas from the cycle. */
mt->ptr_cycle &= ~(mt->frame ^ updates);
if (mt->ptr_cycle & updates) {
select |= ((mt->ptr_cycle & mt->ptr) == 0);
mt->ptr_cycle = updates;
} else {
mt->ptr_cycle |= updates;
}
if (select) {
if (mt->ptr_cycle & ~mt->ptr_mask)
slot = ffs(mt->ptr_cycle & ~mt->ptr_mask) - 1;
else if (mt->touches & ~mt->ptr_mask)
slot = ffs(mt->touches & ~mt->ptr_mask) - 1;
else
slot = ffs(mt->touches) - 1;
mt->ptr = (1 << slot);
}
}
/* Derive touch and motion state from MT state. */
void
wsmouse_mt_convert(struct device *sc)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
struct mt_slot *mts;
int slot, pressure;
wsmouse_ptr_ctrl(input);
if (mt->ptr) {
slot = ffs(mt->ptr) - 1;
mts = &mt->slots[slot];
if (mts->pos.x != input->motion.pos.x)
input->motion.sync |= SYNC_X;
if (mts->pos.y != input->motion.pos.y)
input->motion.sync |= SYNC_Y;
if (mt->ptr != mt->prev_ptr)
/* Suppress pointer movement. */
mts->pos.dx = mts->pos.dy = 0;
memcpy(&input->motion.pos, &mts->pos, sizeof(struct position));
pressure = mts->pressure;
} else {
pressure = 0;
}
wsmouse_touch(sc, pressure, mt->num_touches);
}
void
wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
{
struct wscons_event *ev;
int space;
space = evq->evar->get - evq->put;
if (space != 1 && space != 1 - WSEVENT_QSIZE) {
ev = &evq->evar->q[evq->put++];
evq->put %= WSEVENT_QSIZE;
ev->type = ev_type;
ev->value = ev_value;
memcpy(&ev->time, &evq->ts, sizeof(struct timespec));
evq->result |= EVQ_RESULT_SUCCESS;
} else {
evq->result = EVQ_RESULT_OVERFLOW;
}
}
void
wsmouse_btn_sync(struct btn_state *btn, struct evq_access *evq)
{
int button, ev_type;
u_int bit, sync;
for (sync = btn->sync; sync; sync ^= bit) {
button = ffs(sync) - 1;
bit = (1 << button);
ev_type = (btn->buttons & bit) ? BTN_DOWN_EV : BTN_UP_EV;
wsmouse_evq_put(evq, ev_type, button);
}
}
/*
* Scale with a [*.12] fixed-point factor and a remainder:
*/
static inline int
scale(int val, int factor, int *rmdr)
{
val = val * factor + *rmdr;
if (val >= 0) {
*rmdr = val & 0xfff;
return (val >> 12);
} else {
*rmdr = -(-val & 0xfff);
return -(-val >> 12);
}
}
void
wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
{
struct motion_state *motion = &input->motion;
struct axis_filter *h = &input->filter.h;
struct axis_filter *v = &input->filter.v;
int x, y, dx, dy, dz, dw;
if (motion->sync & SYNC_DELTAS) {
dx = h->inv ? -motion->dx : motion->dx;
dy = v->inv ? -motion->dy : motion->dy;
if (h->scale)
dx = scale(dx, h->scale, &h->rmdr);
if (v->scale)
dy = scale(dy, v->scale, &v->rmdr);
if (dx)
wsmouse_evq_put(evq, DELTA_X_EV(input), dx);
if (dy)
wsmouse_evq_put(evq, DELTA_Y_EV(input), dy);
if (motion->dz) {
dz = (input->flags & REVERSE_SCROLLING)
? -motion->dz : motion->dz;
if (IS_TOUCHPAD(input))
wsmouse_evq_put(evq, VSCROLL_EV, dz);
else
wsmouse_evq_put(evq, DELTA_Z_EV, dz);
}
if (motion->dw) {
dw = (input->flags & REVERSE_SCROLLING)
? -motion->dw : motion->dw;
if (IS_TOUCHPAD(input))
wsmouse_evq_put(evq, HSCROLL_EV, dw);
else
wsmouse_evq_put(evq, DELTA_W_EV, dw);
}
}
if (motion->sync & SYNC_POSITION) {
if (motion->sync & SYNC_X) {
x = (h->inv ? h->inv - motion->pos.x : motion->pos.x);
wsmouse_evq_put(evq, ABS_X_EV(input), x);
}
if (motion->sync & SYNC_Y) {
y = (v->inv ? v->inv - motion->pos.y : motion->pos.y);
wsmouse_evq_put(evq, ABS_Y_EV(input), y);
}
if (motion->pos.dx == 0 && motion->pos.dy == 0
&& (input->flags & TPAD_NATIVE_MODE ))
/* Suppress pointer motion. */
wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
}
}
void
wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
{
struct touch_state *touch = &input->touch;
if (touch->sync & SYNC_PRESSURE)
wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure);
if (touch->sync & SYNC_CONTACTS)
wsmouse_evq_put(evq, ABS_W_EV, touch->contacts);
if ((touch->sync & SYNC_TOUCH_WIDTH)
&& (input->flags & TPAD_NATIVE_MODE))
wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, touch->width);
}
void
wsmouse_log_input(struct wsmouseinput *input, struct timespec *ts)
{
struct motion_state *motion = &input->motion;
int t_sync, mt_sync;
t_sync = (input->touch.sync & SYNC_CONTACTS);
mt_sync = (input->mt.frame && (input->mt.sync[MTS_TOUCH]
|| input->mt.ptr != input->mt.prev_ptr));
if (motion->sync || mt_sync || t_sync || input->btn.sync)
printf("[%s-in][%04d]", DEVNAME(input), LOGTIME(ts));
else
return;
if (motion->sync & SYNC_POSITION)
printf(" abs:%d,%d", motion->pos.x, motion->pos.y);
if (motion->sync & SYNC_DELTAS)
printf(" rel:%d,%d,%d,%d", motion->dx, motion->dy,
motion->dz, motion->dw);
if (mt_sync)
printf(" mt:0x%02x:%d", input->mt.touches,
ffs(input->mt.ptr) - 1);
else if (t_sync)
printf(" t:%d", input->touch.contacts);
if (input->btn.sync)
printf(" btn:0x%02x", input->btn.buttons);
printf("\n");
}
void
wsmouse_log_events(struct wsmouseinput *input, struct evq_access *evq)
{
struct wscons_event *ev;
int n = evq->evar->put;
if (n != evq->put) {
printf("[%s-ev][%04d]", DEVNAME(input), LOGTIME(&evq->ts));
while (n != evq->put) {
ev = &evq->evar->q[n++];
n %= WSEVENT_QSIZE;
printf(" %d:%d", ev->type, ev->value);
}
printf("\n");
}
}
static inline void
clear_sync_flags(struct wsmouseinput *input)
{
int i;
input->btn.sync = 0;
input->sbtn.sync = 0;
input->motion.sync = 0;
input->touch.sync = 0;
input->touch.prev_contacts = input->touch.contacts;
if (input->mt.frame) {
input->mt.frame = 0;
for (i = 0; i < MTS_SIZE; i++)
input->mt.sync[i] = 0;
}
}
void
wsmouse_input_sync(struct device *sc)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct evq_access evq;
evq.evar = *input->evar;
if (evq.evar == NULL)
return;
evq.put = evq.evar->put;
evq.result = EVQ_RESULT_NONE;
getnanotime(&evq.ts);
enqueue_randomness(input->btn.buttons
^ input->motion.dx ^ input->motion.dy
^ input->motion.pos.x ^ input->motion.pos.y
^ input->motion.dz ^ input->motion.dw);
if (input->mt.frame) {
wsmouse_mt_update(input);
wsmouse_mt_convert(sc);
}
if (input->touch.sync)
wsmouse_touch_update(input);
if (input->flags & LOG_INPUT)
wsmouse_log_input(input, &evq.ts);
if (input->flags & TPAD_COMPAT_MODE)
wstpad_compat_convert(input, &evq);
if (input->flags & RESYNC) {
input->flags &= ~RESYNC;
input->motion.sync &= SYNC_POSITION;
}
if (input->btn.sync)
wsmouse_btn_sync(&input->btn, &evq);
if (input->sbtn.sync)
wsmouse_btn_sync(&input->sbtn, &evq);
if (input->motion.sync)
wsmouse_motion_sync(input, &evq);
if (input->touch.sync)
wsmouse_touch_sync(input, &evq);
/* No MT events are generated yet. */
if (evq.result == EVQ_RESULT_SUCCESS) {
wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0);
if (evq.result == EVQ_RESULT_SUCCESS) {
if (input->flags & LOG_EVENTS) {
wsmouse_log_events(input, &evq);
}
evq.evar->put = evq.put;
WSEVENT_WAKEUP(evq.evar);
}
}
if (evq.result != EVQ_RESULT_OVERFLOW)
clear_sync_flags(input);
else
input->flags |= RESYNC;
}
int
wsmouse_id_to_slot(struct device *sc, int id)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
int slot;
if (mt->num_slots == 0)
return (-1);
FOREACHBIT(mt->touches, slot) {
if (mt->slots[slot].id == id)
return slot;
}
slot = ffs(~(mt->touches | mt->frame)) - 1;
if (slot >= 0 && slot < mt->num_slots) {
mt->frame |= 1 << slot;
mt->slots[slot].id = id;
return (slot);
} else {
return (-1);
}
}
/*
* Find a minimum-weight matching for an m-by-n matrix.
*
* m must be greater than or equal to n. The size of the buffer must be
* at least 3m + 3n.
*
* On return, the first m elements of the buffer contain the row-to-
* column mappings, i.e., buffer[i] is the column index for row i, or -1
* if there is no assignment for that row (which may happen if n < m).
*
* Wrong results because of overflows will not occur with input values
* in the range of 0 to INT_MAX / 2 inclusive.
*
* The function applies the Dinic-Kronrod algorithm. It is not modern or
* popular, but it seems to be a good choice for small matrices at least.
* The original form of the algorithm is modified as follows: There is no
* initial search for row minima, the initial assignments are in a
* "virtual" column with the index -1 and zero values. This permits inputs
* with n < m, and it simplifies the reassignments.
*/
void
wsmouse_matching(int *matrix, int m, int n, int *buffer)
{
int i, j, k, d, e, row, col, delta;
int *p;
int *r2c = buffer; /* row-to-column assignments */
int *red = r2c + m; /* reduced values of the assignments */
int *mc = red + m; /* row-wise minimal elements of cs */
int *cs = mc + m; /* the column set */
int *c2r = cs + n; /* column-to-row assignments in cs */
int *cd = c2r + n; /* column deltas (reduction) */
for (p = r2c; p < red; *p++ = -1) {}
for (; p < mc; *p++ = 0) {}
for (col = 0; col < n; col++) {
delta = INT_MAX;
for (i = 0, p = matrix + col; i < m; i++, p += n) {
d = *p - red[i];
if (d < delta || (d == delta && r2c[i] < 0)) {
delta = d;
row = i;
}
}
cd[col] = delta;
if (r2c[row] < 0) {
r2c[row] = col;
continue;
}
for (p = mc; p < cs; *p++ = col) {}
for (k = 0; (j = r2c[row]) >= 0;) {
cs[k++] = j;
c2r[j] = row;
mc[row] -= n;
delta = INT_MAX;
for (i = 0, p = matrix; i < m; i++, p += n)
if (mc[i] >= 0) {
d = p[mc[i]] - cd[mc[i]];
e = p[j] - cd[j];
if (e < d) {
d = e;
mc[i] = j;
}
d -= red[i];
if (d < delta || (d == delta
&& r2c[i] < 0)) {
delta = d;
row = i;
}
}
cd[col] += delta;
for (i = 0; i < k; i++) {
cd[cs[i]] += delta;
red[c2r[cs[i]]] -= delta;
}
}
for (j = mc[row]; (r2c[row] = j) != col;) {
row = c2r[j];
j = mc[row] + n;
}
}
}
/*
* Assign slot numbers to the points in the pt array, and update all slots by
* calling wsmouse_mtstate internally. The slot numbers are passed to the
* caller in the pt->slot fields.
*
* The slot assignment pairs the points with points of the previous frame in
* such a way that the sum of the squared distances is minimal. Using
* squares instead of simple distances favours assignments with more uniform
* distances, and it is faster.
*/
void
wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
struct mt_state *mt = &input->mt;
int i, j, m, n, dx, dy, slot, maxdist;
int *p, *r2c, *c2r;
u_int touches;
if (mt->num_slots == 0 || mt->matrix == NULL)
return;
size = imax(0, imin(size, mt->num_slots));
p = mt->matrix;
touches = mt->touches;
if (mt->num_touches >= size) {
FOREACHBIT(touches, slot)
for (i = 0; i < size; i++) {
dx = pt[i].x - mt->slots[slot].pos.x;
dy = pt[i].y - mt->slots[slot].pos.y;
*p++ = dx * dx + dy * dy;
}
m = mt->num_touches;
n = size;
} else {
for (i = 0; i < size; i++)
FOREACHBIT(touches, slot) {
dx = pt[i].x - mt->slots[slot].pos.x;
dy = pt[i].y - mt->slots[slot].pos.y;
*p++ = dx * dx + dy * dy;
}
m = size;
n = mt->num_touches;
}
wsmouse_matching(mt->matrix, m, n, p);
r2c = p;
c2r = p + m;
maxdist = input->filter.tracking_maxdist;
maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
for (i = 0, p = mt->matrix; i < m; i++, p += n)
if ((j = r2c[i]) >= 0) {
if (p[j] <= maxdist)
c2r[j] = i;
else
c2r[j] = r2c[i] = -1;
}
p = (n == size ? c2r : r2c);
for (i = 0; i < size; i++)
if (*p++ < 0) {
slot = ffs(~(mt->touches | mt->frame)) - 1;
if (slot < 0 || slot >= mt->num_slots)
break;
wsmouse_mtstate(sc, slot,
pt[i].x, pt[i].y, pt[i].pressure);
pt[i].slot = slot;
}
p = (n == size ? r2c : c2r);
FOREACHBIT(touches, slot)
if ((i = *p++) >= 0) {
wsmouse_mtstate(sc, slot,
pt[i].x, pt[i].y, pt[i].pressure);
pt[i].slot = slot;
} else {
wsmouse_mtstate(sc, slot, 0, 0, 0);
}
}
static inline void
free_mt_slots(struct wsmouseinput *input)
{
int n, size;
if ((n = input->mt.num_slots)) {
size = n * sizeof(struct mt_slot);
if (input->flags & MT_TRACKING)
size += MATRIX_SIZE(n);
input->mt.num_slots = 0;
free(input->mt.slots, M_DEVBUF, size);
input->mt.slots = NULL;
input->mt.matrix = NULL;
}
}
/* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
int
wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int n, size;
if (num_slots == input->mt.num_slots
&& (!tracking == ((input->flags & MT_TRACKING) == 0)))
return (0);
free_mt_slots(input);
if (tracking)
input->flags |= MT_TRACKING;
else
input->flags &= ~MT_TRACKING;
n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX);
if (n) {
size = n * sizeof(struct mt_slot);
if (input->flags & MT_TRACKING)
size += MATRIX_SIZE(n);
input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
if (input->mt.slots != NULL) {
if (input->flags & MT_TRACKING)
input->mt.matrix = (int *)
(input->mt.slots + n);
input->mt.num_slots = n;
return (0);
}
}
return (-1);
}
int
wsmouse_get_params(struct device *sc,
struct wsmouse_param *params, u_int nparams)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int i, key, error = 0;
for (i = 0; i < nparams; i++) {
key = params[i].key;
switch (key) {
case WSMOUSECFG_DX_SCALE:
params[i].value = input->filter.h.scale;
break;
case WSMOUSECFG_DY_SCALE:
params[i].value = input->filter.v.scale;
break;
case WSMOUSECFG_PRESSURE_LO:
params[i].value = input->filter.pressure_lo;
break;
case WSMOUSECFG_PRESSURE_HI:
params[i].value = input->filter.pressure_hi;
break;
case WSMOUSECFG_TRKMAXDIST:
params[i].value = input->filter.tracking_maxdist;
break;
case WSMOUSECFG_SWAPXY:
params[i].value = input->filter.swapxy;
break;
case WSMOUSECFG_X_INV:
params[i].value = input->filter.h.inv;
break;
case WSMOUSECFG_Y_INV:
params[i].value = input->filter.v.inv;
break;
case WSMOUSECFG_REVERSE_SCROLLING:
params[i].value = !!(input->flags & REVERSE_SCROLLING);
break;
case WSMOUSECFG_DX_MAX:
params[i].value = input->filter.h.dmax;
break;
case WSMOUSECFG_DY_MAX:
params[i].value = input->filter.v.dmax;
break;
case WSMOUSECFG_X_HYSTERESIS:
params[i].value = input->filter.h.hysteresis;
break;
case WSMOUSECFG_Y_HYSTERESIS:
params[i].value = input->filter.v.hysteresis;
break;
case WSMOUSECFG_DECELERATION:
params[i].value = input->filter.dclr;
break;
case WSMOUSECFG_STRONG_HYSTERESIS:
params[i].value = 0; /* The feature has been removed. */
break;
case WSMOUSECFG_SMOOTHING:
params[i].value =
input->filter.mode & SMOOTHING_MASK;
break;
case WSMOUSECFG_LOG_INPUT:
params[i].value = !!(input->flags & LOG_INPUT);
break;
case WSMOUSECFG_LOG_EVENTS:
params[i].value = !!(input->flags & LOG_EVENTS);
break;
default:
error = wstpad_get_param(input, key, ¶ms[i].value);
if (error != 0)
return (error);
break;
}
}
return (0);
}
int
wsmouse_set_params(struct device *sc,
const struct wsmouse_param *params, u_int nparams)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int i, val, key, needreset = 0, error = 0;
for (i = 0; i < nparams; i++) {
key = params[i].key;
val = params[i].value;
switch (params[i].key) {
case WSMOUSECFG_PRESSURE_LO:
input->filter.pressure_lo = val;
if (val > input->filter.pressure_hi)
input->filter.pressure_hi = val;
input->touch.min_pressure = input->filter.pressure_hi;
break;
case WSMOUSECFG_PRESSURE_HI:
input->filter.pressure_hi = val;
if (val < input->filter.pressure_lo)
input->filter.pressure_lo = val;
input->touch.min_pressure = val;
break;
case WSMOUSECFG_X_HYSTERESIS:
input->filter.h.hysteresis = val;
break;
case WSMOUSECFG_Y_HYSTERESIS:
input->filter.v.hysteresis = val;
break;
case WSMOUSECFG_DECELERATION:
input->filter.dclr = val;
wstpad_init_deceleration(input);
break;
case WSMOUSECFG_DX_SCALE:
input->filter.h.scale = val;
break;
case WSMOUSECFG_DY_SCALE:
input->filter.v.scale = val;
break;
case WSMOUSECFG_TRKMAXDIST:
input->filter.tracking_maxdist = val;
break;
case WSMOUSECFG_SWAPXY:
input->filter.swapxy = val;
break;
case WSMOUSECFG_X_INV:
input->filter.h.inv = val;
break;
case WSMOUSECFG_Y_INV:
input->filter.v.inv = val;
break;
case WSMOUSECFG_REVERSE_SCROLLING:
if (val)
input->flags |= REVERSE_SCROLLING;
else
input->flags &= ~REVERSE_SCROLLING;
break;
case WSMOUSECFG_DX_MAX:
input->filter.h.dmax = val;
break;
case WSMOUSECFG_DY_MAX:
input->filter.v.dmax = val;
break;
case WSMOUSECFG_SMOOTHING:
input->filter.mode &= ~SMOOTHING_MASK;
input->filter.mode |= (val & SMOOTHING_MASK);
break;
case WSMOUSECFG_LOG_INPUT:
if (val)
input->flags |= LOG_INPUT;
else
input->flags &= ~LOG_INPUT;
break;
case WSMOUSECFG_LOG_EVENTS:
if (val)
input->flags |= LOG_EVENTS;
else
input->flags &= ~LOG_EVENTS;
break;
default:
needreset = 1;
error = wstpad_set_param(input, key, val);
if (error != 0)
return (error);
break;
}
}
/* Reset soft-states if touchpad parameters changed */
if (needreset) {
wstpad_reset(input);
return (wstpad_configure(input));
}
return (0);
}
int
wsmouse_set_mode(struct device *sc, int mode)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
if (mode == WSMOUSE_COMPAT) {
input->flags &= ~TPAD_NATIVE_MODE;
input->flags |= TPAD_COMPAT_MODE;
return (0);
} else if (mode == WSMOUSE_NATIVE) {
input->flags &= ~TPAD_COMPAT_MODE;
input->flags |= TPAD_NATIVE_MODE;
return (0);
}
return (-1);
}
struct wsmousehw *wsmouse_get_hw(struct device *sc)
{
return &((struct wsmouse_softc *) sc)->sc_input.hw;
}
/*
* Create a default configuration based on the hardware infos in the 'hw'
* fields. The 'params' argument is optional, hardware drivers can use it
* to modify the generic defaults. Up to now this function is only useful
* for touchpads.
*/
int
wsmouse_configure(struct device *sc,
struct wsmouse_param *params, u_int nparams)
{
struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
int error;
if (!(input->flags & CONFIGURED)) {
if (input->hw.x_max && input->hw.y_max) {
if (input->hw.flags & WSMOUSEHW_LR_DOWN) {
input->filter.v.inv =
input->hw.y_max + input->hw.y_min;
}
}
input->filter.ratio = 1 << 12;
if (input->hw.h_res > 0 && input->hw.v_res > 0) {
input->filter.ratio *= input->hw.h_res;
input->filter.ratio /= input->hw.v_res;
}
if (wsmouse_mt_init(sc, input->hw.mt_slots,
(input->hw.flags & WSMOUSEHW_MT_TRACKING))) {
printf("wsmouse_configure: "
"MT initialization failed.\n");
return (-1);
}
if (IS_TOUCHPAD(input) && wstpad_configure(input)) {
printf("wstpad_configure: "
"Initialization failed.\n");
return (-1);
}
if (params != NULL) {
if ((error = wsmouse_set_params(sc, params, nparams)))
return (error);
}
input->flags |= CONFIGURED;
}
if (IS_TOUCHPAD(input))
wsmouse_set_mode(sc, WSMOUSE_COMPAT);
return (0);
}
void
wsmouse_input_reset(struct wsmouseinput *input)
{
int num_slots, *matrix;
struct mt_slot *slots;
memset(&input->btn, 0, sizeof(struct btn_state));
memset(&input->motion, 0, sizeof(struct motion_state));
memset(&input->touch, 0, sizeof(struct touch_state));
input->touch.min_pressure = input->filter.pressure_hi;
if ((num_slots = input->mt.num_slots)) {
slots = input->mt.slots;
matrix = input->mt.matrix;
memset(&input->mt, 0, sizeof(struct mt_state));
memset(slots, 0, num_slots * sizeof(struct mt_slot));
input->mt.num_slots = num_slots;
input->mt.slots = slots;
input->mt.matrix = matrix;
}
if (input->tp != NULL)
wstpad_reset(input);
}
void
wsmouse_input_cleanup(struct wsmouseinput *input)
{
if (input->tp != NULL)
wstpad_cleanup(input);
free_mt_slots(input);
}
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/* $OpenBSD: pctr.c,v 1.9 2019/03/25 18:48:12 guenther Exp $ */
/*
* Copyright (c) 2007 Mike Belopuhov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Pentium performance counter driver for OpenBSD.
* Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
*
* Modification and redistribution in source and binary forms is
* permitted provided that due credit is given to the author and the
* OpenBSD project by leaving this copyright notice intact.
*/
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/ioccom.h>
#include <sys/mutex.h>
#include <sys/systm.h>
#include <machine/intr.h>
#include <machine/pctr.h>
#include <machine/cpu.h>
#include <machine/specialreg.h>
#define PCTR_AMD_NUM PCTR_NUM
#define PCTR_INTEL_NUM 2 /* Intel supports only 2 counters */
#define PCTR_INTEL_VERSION_MASK 0xff
#define usetsc (cpu_feature & CPUID_TSC)
#define usepctr ((pctr_isamd && ((cpu_id >> 8) & 15) >= 6) || \
(pctr_isintel && \
(pctr_intel_cap & PCTR_INTEL_VERSION_MASK) >= 1))
int pctr_isamd;
int pctr_isintel;
uint32_t pctr_intel_cap;
struct mutex pctr_conf_lock = MUTEX_INITIALIZER(IPL_HIGH);
uint32_t pctr_fn[PCTR_NUM];
static void pctrrd(struct pctrst *);
static int pctrsel(int, uint32_t, uint32_t);
static void pctr_enable(struct cpu_info *);
static void
pctrrd(struct pctrst *st)
{
int i, num, reg;
num = pctr_isamd ? PCTR_AMD_NUM : PCTR_INTEL_NUM;
reg = pctr_isamd ? MSR_K7_EVNTSEL0 : MSR_EVNTSEL0;
for (i = 0; i < num; i++)
st->pctr_fn[i] = rdmsr(reg + i);
__asm volatile("cli");
st->pctr_tsc = rdtsc();
for (i = 0; i < num; i++)
st->pctr_hwc[i] = rdpmc(i);
__asm volatile("sti");
}
void
pctrattach(int num)
{
uint32_t dummy;
if (num > 1)
return;
pctr_isamd = (strcmp(cpu_vendor, "AuthenticAMD") == 0);
if (!pctr_isamd) {
pctr_isintel = (strcmp(cpu_vendor, "GenuineIntel") == 0);
CPUID(0xa, pctr_intel_cap, dummy, dummy, dummy);
}
}
void
pctr_enable(struct cpu_info *ci)
{
if (usepctr) {
/* Enable RDTSC and RDPMC instructions from user-level. */
__asm volatile("movq %%cr4,%%rax\n"
"\tandq %0,%%rax\n"
"\torq %1,%%rax\n"
"\tmovq %%rax,%%cr4"
:: "i" (~CR4_TSD), "i" (CR4_PCE) : "rax");
} else if (usetsc) {
/* Enable RDTSC instruction from user-level. */
__asm volatile("movq %%cr4,%%rax\n"
"\tandq %0,%%rax\n"
"\tmovq %%rax,%%cr4"
:: "i" (~CR4_TSD) : "rax");
}
}
int
pctropen(dev_t dev, int oflags, int devtype, struct proc *p)
{
if (minor(dev))
return (ENXIO);
return (0);
}
int
pctrclose(dev_t dev, int oflags, int devtype, struct proc *p)
{
return (0);
}
static int
pctrsel(int fflag, uint32_t cmd, uint32_t fn)
{
int msrsel, msrval, changed;
cmd -= PCIOCS0;
if (pctr_isamd) {
if (cmd > PCTR_AMD_NUM-1)
return (EINVAL);
msrsel = MSR_K7_EVNTSEL0 + cmd;
msrval = MSR_K7_PERFCTR0 + cmd;
} else {
if (cmd > PCTR_INTEL_NUM-1)
return (EINVAL);
msrsel = MSR_EVNTSEL0 + cmd;
msrval = MSR_PERFCTR0 + cmd;
}
if (!(fflag & FWRITE))
return (EPERM);
if (fn & 0x380000)
return (EINVAL);
if (fn != 0)
pctr_enable(curcpu());
mtx_enter(&pctr_conf_lock);
changed = fn != pctr_fn[cmd];
if (changed) {
pctr_fn[cmd] = fn;
wrmsr(msrval, 0);
wrmsr(msrsel, fn);
wrmsr(msrval, 0);
}
mtx_leave(&pctr_conf_lock);
#ifdef MULTIPROCESSOR
if (changed)
x86_broadcast_ipi(X86_IPI_PCTR);
#endif
return (0);
}
int
pctrioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
{
switch (cmd) {
case PCIOCRD:
{
struct pctrst *st = (struct pctrst *)data;
if (usepctr)
pctrrd(st);
else if (usetsc)
st->pctr_tsc = rdtsc();
return (0);
}
case PCIOCS0:
case PCIOCS1:
case PCIOCS2:
case PCIOCS3:
if (usepctr)
return (pctrsel(fflag, cmd, *(u_int *)data));
return (ENODEV);
default:
return (EINVAL);
}
}
void
pctr_reload(struct cpu_info *ci)
{
int num, i, msrsel, msrval, anyset;
uint32_t fn;
if (pctr_isamd) {
num = PCTR_AMD_NUM;
msrsel = MSR_K7_EVNTSEL0;
msrval = MSR_K7_PERFCTR0;
} else {
num = PCTR_INTEL_NUM;
msrsel = MSR_EVNTSEL0;
msrval = MSR_PERFCTR0;
}
anyset = 0;
mtx_enter(&pctr_conf_lock);
for (i = 0; i < num; i++) {
/* only update the ones that don't match */
/* XXX generation numbers for zeroing? */
fn = rdmsr(msrsel + i);
if (fn != pctr_fn[i]) {
wrmsr(msrval + i, 0);
wrmsr(msrsel + i, pctr_fn[i]);
wrmsr(msrval + i, 0);
}
if (fn)
anyset = 1;
}
mtx_leave(&pctr_conf_lock);
if (! anyset)
pctr_enable(curcpu());
}
void
pctr_resume(struct cpu_info *ci)
{
if (usepctr)
pctr_reload(ci);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
/* $OpenBSD: softraid.c,v 1.426 2022/08/29 19:01:52 stsp Exp $ */
/*
* Copyright (c) 2007, 2008, 2009 Marco Peereboom <marco@peereboom.us>
* Copyright (c) 2008 Chris Kuethe <ckuethe@openbsd.org>
* Copyright (c) 2009 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "bio.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/kernel.h>
#include <sys/disk.h>
#include <sys/rwlock.h>
#include <sys/queue.h>
#include <sys/fcntl.h>
#include <sys/disklabel.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/mount.h>
#include <sys/sensors.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/task.h>
#include <sys/kthread.h>
#include <sys/dkio.h>
#include <sys/stdint.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_disk.h>
#include <dev/softraidvar.h>
#ifdef HIBERNATE
#include <lib/libsa/aes_xts.h>
#include <sys/hibernate.h>
#include <scsi/sdvar.h>
#endif /* HIBERNATE */
/* #define SR_FANCY_STATS */
#ifdef SR_DEBUG
#define SR_FANCY_STATS
uint32_t sr_debug = 0
/* | SR_D_CMD */
/* | SR_D_MISC */
/* | SR_D_INTR */
/* | SR_D_IOCTL */
/* | SR_D_CCB */
/* | SR_D_WU */
/* | SR_D_META */
/* | SR_D_DIS */
/* | SR_D_STATE */
/* | SR_D_REBUILD */
;
#endif
struct sr_softc *softraid0;
struct sr_uuid sr_bootuuid;
u_int8_t sr_bootkey[SR_CRYPTO_MAXKEYBYTES];
int sr_match(struct device *, void *, void *);
void sr_attach(struct device *, struct device *, void *);
int sr_detach(struct device *, int);
void sr_map_root(void);
const struct cfattach softraid_ca = {
sizeof(struct sr_softc), sr_match, sr_attach, sr_detach,
};
struct cfdriver softraid_cd = {
NULL, "softraid", DV_DULL
};
/* scsi & discipline */
void sr_scsi_cmd(struct scsi_xfer *);
int sr_scsi_probe(struct scsi_link *);
int sr_scsi_ioctl(struct scsi_link *, u_long,
caddr_t, int);
int sr_bio_ioctl(struct device *, u_long, caddr_t);
int sr_bio_handler(struct sr_softc *,
struct sr_discipline *, u_long, struct bio *);
int sr_ioctl_inq(struct sr_softc *, struct bioc_inq *);
int sr_ioctl_vol(struct sr_softc *, struct bioc_vol *);
int sr_ioctl_disk(struct sr_softc *, struct bioc_disk *);
int sr_ioctl_setstate(struct sr_softc *,
struct bioc_setstate *);
int sr_ioctl_createraid(struct sr_softc *,
struct bioc_createraid *, int, void *);
int sr_ioctl_deleteraid(struct sr_softc *,
struct sr_discipline *, struct bioc_deleteraid *);
int sr_ioctl_discipline(struct sr_softc *,
struct sr_discipline *, struct bioc_discipline *);
int sr_ioctl_installboot(struct sr_softc *,
struct sr_discipline *, struct bioc_installboot *);
void sr_chunks_unwind(struct sr_softc *,
struct sr_chunk_head *);
void sr_discipline_free(struct sr_discipline *);
void sr_discipline_shutdown(struct sr_discipline *, int, int);
int sr_discipline_init(struct sr_discipline *, int);
int sr_alloc_resources(struct sr_discipline *);
void sr_free_resources(struct sr_discipline *);
void sr_set_chunk_state(struct sr_discipline *, int, int);
void sr_set_vol_state(struct sr_discipline *);
/* utility functions */
void sr_shutdown(int);
void sr_uuid_generate(struct sr_uuid *);
char *sr_uuid_format(struct sr_uuid *);
void sr_uuid_print(struct sr_uuid *, int);
void sr_checksum_print(u_int8_t *);
int sr_boot_assembly(struct sr_softc *);
int sr_already_assembled(struct sr_discipline *);
int sr_hotspare(struct sr_softc *, dev_t);
void sr_hotspare_rebuild(struct sr_discipline *);
int sr_rebuild_init(struct sr_discipline *, dev_t, int);
void sr_rebuild_start(void *);
void sr_rebuild_thread(void *);
void sr_rebuild(struct sr_discipline *);
void sr_roam_chunks(struct sr_discipline *);
int sr_chunk_in_use(struct sr_softc *, dev_t);
int sr_rw(struct sr_softc *, dev_t, char *, size_t,
daddr_t, long);
void sr_wu_done_callback(void *);
/* don't include these on RAMDISK */
#ifndef SMALL_KERNEL
void sr_sensors_refresh(void *);
int sr_sensors_create(struct sr_discipline *);
void sr_sensors_delete(struct sr_discipline *);
#endif
/* metadata */
int sr_meta_probe(struct sr_discipline *, dev_t *, int);
int sr_meta_attach(struct sr_discipline *, int, int);
int sr_meta_rw(struct sr_discipline *, dev_t, void *, long);
int sr_meta_clear(struct sr_discipline *);
void sr_meta_init(struct sr_discipline *, int, int);
void sr_meta_init_complete(struct sr_discipline *);
void sr_meta_opt_handler(struct sr_discipline *,
struct sr_meta_opt_hdr *);
/* hotplug magic */
void sr_disk_attach(struct disk *, int);
struct sr_hotplug_list {
void (*sh_hotplug)(struct sr_discipline *,
struct disk *, int);
struct sr_discipline *sh_sd;
SLIST_ENTRY(sr_hotplug_list) shl_link;
};
SLIST_HEAD(sr_hotplug_list_head, sr_hotplug_list);
struct sr_hotplug_list_head sr_hotplug_callbacks;
extern void (*softraid_disk_attach)(struct disk *, int);
/* scsi glue */
const struct scsi_adapter sr_switch = {
sr_scsi_cmd, NULL, sr_scsi_probe, NULL, sr_scsi_ioctl
};
/* native metadata format */
int sr_meta_native_bootprobe(struct sr_softc *, dev_t,
struct sr_boot_chunk_head *);
#define SR_META_NOTCLAIMED (0)
#define SR_META_CLAIMED (1)
int sr_meta_native_probe(struct sr_softc *,
struct sr_chunk *);
int sr_meta_native_attach(struct sr_discipline *, int);
int sr_meta_native_write(struct sr_discipline *, dev_t,
struct sr_metadata *,void *);
#ifdef SR_DEBUG
void sr_meta_print(struct sr_metadata *);
#else
#define sr_meta_print(m)
#endif
/* the metadata driver should remain stateless */
struct sr_meta_driver {
daddr_t smd_offset; /* metadata location */
u_int32_t smd_size; /* size of metadata */
int (*smd_probe)(struct sr_softc *,
struct sr_chunk *);
int (*smd_attach)(struct sr_discipline *, int);
int (*smd_detach)(struct sr_discipline *);
int (*smd_read)(struct sr_discipline *, dev_t,
struct sr_metadata *, void *);
int (*smd_write)(struct sr_discipline *, dev_t,
struct sr_metadata *, void *);
int (*smd_validate)(struct sr_discipline *,
struct sr_metadata *, void *);
} smd[] = {
{ SR_META_OFFSET, SR_META_SIZE * DEV_BSIZE,
sr_meta_native_probe, sr_meta_native_attach, NULL,
sr_meta_native_read, sr_meta_native_write, NULL },
{ 0, 0, NULL, NULL, NULL, NULL }
};
int
sr_meta_attach(struct sr_discipline *sd, int chunk_no, int force)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk_head *cl;
struct sr_chunk *ch_entry, *chunk1, *chunk2;
int rv = 1, i = 0;
DNPRINTF(SR_D_META, "%s: sr_meta_attach(%d)\n", DEVNAME(sc), chunk_no);
/* in memory copy of metadata */
sd->sd_meta = malloc(SR_META_SIZE * DEV_BSIZE, M_DEVBUF,
M_ZERO | M_NOWAIT);
if (!sd->sd_meta) {
sr_error(sc, "could not allocate memory for metadata");
goto bad;
}
if (sd->sd_meta_type != SR_META_F_NATIVE) {
/* in memory copy of foreign metadata */
sd->sd_meta_foreign = malloc(smd[sd->sd_meta_type].smd_size,
M_DEVBUF, M_ZERO | M_NOWAIT);
if (!sd->sd_meta_foreign) {
/* unwind frees sd_meta */
sr_error(sc, "could not allocate memory for foreign "
"metadata");
goto bad;
}
}
/* we have a valid list now create an array index */
cl = &sd->sd_vol.sv_chunk_list;
sd->sd_vol.sv_chunks = mallocarray(chunk_no, sizeof(struct sr_chunk *),
M_DEVBUF, M_WAITOK | M_ZERO);
/* fill out chunk array */
i = 0;
SLIST_FOREACH(ch_entry, cl, src_link)
sd->sd_vol.sv_chunks[i++] = ch_entry;
/* attach metadata */
if (smd[sd->sd_meta_type].smd_attach(sd, force))
goto bad;
/* Force chunks into correct order now that metadata is attached. */
SLIST_INIT(cl);
for (i = 0; i < chunk_no; i++) {
ch_entry = sd->sd_vol.sv_chunks[i];
chunk2 = NULL;
SLIST_FOREACH(chunk1, cl, src_link) {
if (chunk1->src_meta.scmi.scm_chunk_id >
ch_entry->src_meta.scmi.scm_chunk_id)
break;
chunk2 = chunk1;
}
if (chunk2 == NULL)
SLIST_INSERT_HEAD(cl, ch_entry, src_link);
else
SLIST_INSERT_AFTER(chunk2, ch_entry, src_link);
}
i = 0;
SLIST_FOREACH(ch_entry, cl, src_link)
sd->sd_vol.sv_chunks[i++] = ch_entry;
rv = 0;
bad:
return (rv);
}
int
sr_meta_probe(struct sr_discipline *sd, dev_t *dt, int no_chunk)
{
struct sr_softc *sc = sd->sd_sc;
struct vnode *vn;
struct sr_chunk *ch_entry, *ch_prev = NULL;
struct sr_chunk_head *cl;
char devname[32];
int i, d, type, found, prevf, error;
dev_t dev;
DNPRINTF(SR_D_META, "%s: sr_meta_probe(%d)\n", DEVNAME(sc), no_chunk);
if (no_chunk == 0)
goto unwind;
cl = &sd->sd_vol.sv_chunk_list;
for (d = 0, prevf = SR_META_F_INVALID; d < no_chunk; d++) {
ch_entry = malloc(sizeof(struct sr_chunk), M_DEVBUF,
M_WAITOK | M_ZERO);
/* keep disks in user supplied order */
if (ch_prev)
SLIST_INSERT_AFTER(ch_prev, ch_entry, src_link);
else
SLIST_INSERT_HEAD(cl, ch_entry, src_link);
ch_prev = ch_entry;
dev = dt[d];
ch_entry->src_dev_mm = dev;
if (dev == NODEV) {
ch_entry->src_meta.scm_status = BIOC_SDOFFLINE;
continue;
} else {
sr_meta_getdevname(sc, dev, devname, sizeof(devname));
if (bdevvp(dev, &vn)) {
sr_error(sc, "sr_meta_probe: cannot allocate "
"vnode");
goto unwind;
}
/*
* XXX leaving dev open for now; move this to attach
* and figure out the open/close dance for unwind.
*/
error = VOP_OPEN(vn, FREAD | FWRITE, NOCRED, curproc);
if (error) {
DNPRINTF(SR_D_META,"%s: sr_meta_probe can't "
"open %s\n", DEVNAME(sc), devname);
vput(vn);
goto unwind;
}
strlcpy(ch_entry->src_devname, devname,
sizeof(ch_entry->src_devname));
ch_entry->src_vn = vn;
}
/* determine if this is a device we understand */
for (i = 0, found = SR_META_F_INVALID; smd[i].smd_probe; i++) {
type = smd[i].smd_probe(sc, ch_entry);
if (type == SR_META_F_INVALID)
continue;
else {
found = type;
break;
}
}
if (found == SR_META_F_INVALID)
goto unwind;
if (prevf == SR_META_F_INVALID)
prevf = found;
if (prevf != found) {
DNPRINTF(SR_D_META, "%s: prevf != found\n",
DEVNAME(sc));
goto unwind;
}
}
return (prevf);
unwind:
return (SR_META_F_INVALID);
}
void
sr_meta_getdevname(struct sr_softc *sc, dev_t dev, char *buf, int size)
{
int maj, unit, part;
char *name;
DNPRINTF(SR_D_META, "%s: sr_meta_getdevname(%p, %d)\n",
DEVNAME(sc), buf, size);
if (!buf)
return;
maj = major(dev);
part = DISKPART(dev);
unit = DISKUNIT(dev);
name = findblkname(maj);
if (name == NULL)
return;
snprintf(buf, size, "%s%d%c", name, unit, part + 'a');
}
int
sr_rw(struct sr_softc *sc, dev_t dev, char *buf, size_t size, daddr_t blkno,
long flags)
{
struct vnode *vp;
struct buf b;
size_t bufsize, dma_bufsize;
int rv = 1;
char *dma_buf;
int s;
DNPRINTF(SR_D_MISC, "%s: sr_rw(0x%x, %p, %zu, %lld 0x%lx)\n",
DEVNAME(sc), dev, buf, size, (long long)blkno, flags);
dma_bufsize = (size > MAXPHYS) ? MAXPHYS : size;
dma_buf = dma_alloc(dma_bufsize, PR_WAITOK);
if (bdevvp(dev, &vp)) {
printf("%s: sr_rw: failed to allocate vnode\n", DEVNAME(sc));
goto done;
}
while (size > 0) {
DNPRINTF(SR_D_MISC, "%s: dma_buf %p, size %zu, blkno %lld)\n",
DEVNAME(sc), dma_buf, size, (long long)blkno);
bufsize = (size > MAXPHYS) ? MAXPHYS : size;
if (flags == B_WRITE)
memcpy(dma_buf, buf, bufsize);
bzero(&b, sizeof(b));
b.b_flags = flags | B_PHYS;
b.b_proc = curproc;
b.b_dev = dev;
b.b_iodone = NULL;
b.b_error = 0;
b.b_blkno = blkno;
b.b_data = dma_buf;
b.b_bcount = bufsize;
b.b_bufsize = bufsize;
b.b_resid = bufsize;
b.b_vp = vp;
if ((b.b_flags & B_READ) == 0) {
s = splbio();
vp->v_numoutput++;
splx(s);
}
LIST_INIT(&b.b_dep);
VOP_STRATEGY(vp, &b);
biowait(&b);
if (b.b_flags & B_ERROR) {
printf("%s: I/O error %d on dev 0x%x at block %llu\n",
DEVNAME(sc), b.b_error, dev, b.b_blkno);
goto done;
}
if (flags == B_READ)
memcpy(buf, dma_buf, bufsize);
size -= bufsize;
buf += bufsize;
blkno += howmany(bufsize, DEV_BSIZE);
}
rv = 0;
done:
if (vp)
vput(vp);
dma_free(dma_buf, dma_bufsize);
return (rv);
}
int
sr_meta_rw(struct sr_discipline *sd, dev_t dev, void *md, long flags)
{
int rv = 1;
DNPRINTF(SR_D_META, "%s: sr_meta_rw(0x%x, %p, 0x%lx)\n",
DEVNAME(sd->sd_sc), dev, md, flags);
if (md == NULL) {
printf("%s: sr_meta_rw: invalid metadata pointer\n",
DEVNAME(sd->sd_sc));
goto done;
}
rv = sr_rw(sd->sd_sc, dev, md, SR_META_SIZE * DEV_BSIZE,
SR_META_OFFSET, flags);
done:
return (rv);
}
int
sr_meta_clear(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk_head *cl = &sd->sd_vol.sv_chunk_list;
struct sr_chunk *ch_entry;
void *m;
int rv = 1;
DNPRINTF(SR_D_META, "%s: sr_meta_clear\n", DEVNAME(sc));
if (sd->sd_meta_type != SR_META_F_NATIVE) {
sr_error(sc, "cannot clear foreign metadata");
goto done;
}
m = malloc(SR_META_SIZE * DEV_BSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
SLIST_FOREACH(ch_entry, cl, src_link) {
if (sr_meta_native_write(sd, ch_entry->src_dev_mm, m, NULL)) {
/* XXX mark disk offline */
DNPRINTF(SR_D_META, "%s: sr_meta_clear failed to "
"clear %s\n", DEVNAME(sc), ch_entry->src_devname);
rv++;
continue;
}
bzero(&ch_entry->src_meta, sizeof(ch_entry->src_meta));
}
bzero(sd->sd_meta, SR_META_SIZE * DEV_BSIZE);
free(m, M_DEVBUF, SR_META_SIZE * DEV_BSIZE);
rv = 0;
done:
return (rv);
}
void
sr_meta_init(struct sr_discipline *sd, int level, int no_chunk)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_metadata *sm = sd->sd_meta;
struct sr_chunk_head *cl = &sd->sd_vol.sv_chunk_list;
struct sr_meta_chunk *scm;
struct sr_chunk *chunk;
int cid = 0;
u_int64_t max_chunk_sz = 0, min_chunk_sz = 0;
u_int32_t secsize = DEV_BSIZE;
DNPRINTF(SR_D_META, "%s: sr_meta_init\n", DEVNAME(sc));
if (!sm)
return;
/* Initialise volume metadata. */
sm->ssdi.ssd_magic = SR_MAGIC;
sm->ssdi.ssd_version = SR_META_VERSION;
sm->ssdi.ssd_vol_flags = sd->sd_meta_flags;
sm->ssdi.ssd_volid = 0;
sm->ssdi.ssd_chunk_no = no_chunk;
sm->ssdi.ssd_level = level;
sm->ssd_data_blkno = SR_DATA_OFFSET;
sm->ssd_ondisk = 0;
sr_uuid_generate(&sm->ssdi.ssd_uuid);
/* Initialise chunk metadata and get min/max chunk sizes & secsize. */
SLIST_FOREACH(chunk, cl, src_link) {
scm = &chunk->src_meta;
scm->scmi.scm_size = chunk->src_size;
scm->scmi.scm_chunk_id = cid++;
scm->scm_status = BIOC_SDONLINE;
scm->scmi.scm_volid = 0;
strlcpy(scm->scmi.scm_devname, chunk->src_devname,
sizeof(scm->scmi.scm_devname));
memcpy(&scm->scmi.scm_uuid, &sm->ssdi.ssd_uuid,
sizeof(scm->scmi.scm_uuid));
sr_checksum(sc, scm, &scm->scm_checksum,
sizeof(scm->scm_checksum));
if (min_chunk_sz == 0)
min_chunk_sz = scm->scmi.scm_size;
if (chunk->src_secsize > secsize)
secsize = chunk->src_secsize;
min_chunk_sz = MIN(min_chunk_sz, scm->scmi.scm_size);
max_chunk_sz = MAX(max_chunk_sz, scm->scmi.scm_size);
}
sm->ssdi.ssd_secsize = secsize;
/* Equalize chunk sizes. */
SLIST_FOREACH(chunk, cl, src_link)
chunk->src_meta.scmi.scm_coerced_size = min_chunk_sz;
sd->sd_vol.sv_chunk_minsz = min_chunk_sz;
sd->sd_vol.sv_chunk_maxsz = max_chunk_sz;
}
void
sr_meta_init_complete(struct sr_discipline *sd)
{
#ifdef SR_DEBUG
struct sr_softc *sc = sd->sd_sc;
#endif
struct sr_metadata *sm = sd->sd_meta;
DNPRINTF(SR_D_META, "%s: sr_meta_complete\n", DEVNAME(sc));
/* Complete initialisation of volume metadata. */
strlcpy(sm->ssdi.ssd_vendor, "OPENBSD", sizeof(sm->ssdi.ssd_vendor));
snprintf(sm->ssdi.ssd_product, sizeof(sm->ssdi.ssd_product),
"SR %s", sd->sd_name);
snprintf(sm->ssdi.ssd_revision, sizeof(sm->ssdi.ssd_revision),
"%03d", sm->ssdi.ssd_version);
}
void
sr_meta_opt_handler(struct sr_discipline *sd, struct sr_meta_opt_hdr *om)
{
if (om->som_type != SR_OPT_BOOT)
panic("unknown optional metadata type");
}
void
sr_meta_save_callback(void *xsd)
{
struct sr_discipline *sd = xsd;
int s;
s = splbio();
if (sr_meta_save(sd, SR_META_DIRTY))
printf("%s: save metadata failed\n", DEVNAME(sd->sd_sc));
sd->sd_must_flush = 0;
splx(s);
}
int
sr_meta_save(struct sr_discipline *sd, u_int32_t flags)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_metadata *sm = sd->sd_meta, *m;
struct sr_meta_driver *s;
struct sr_chunk *src;
struct sr_meta_chunk *cm;
struct sr_workunit wu;
struct sr_meta_opt_hdr *omh;
struct sr_meta_opt_item *omi;
int i;
DNPRINTF(SR_D_META, "%s: sr_meta_save %s\n",
DEVNAME(sc), sd->sd_meta->ssd_devname);
if (!sm) {
printf("%s: no in memory copy of metadata\n", DEVNAME(sc));
goto bad;
}
/* meta scratchpad */
s = &smd[sd->sd_meta_type];
m = malloc(SR_META_SIZE * DEV_BSIZE, M_DEVBUF, M_ZERO | M_NOWAIT);
if (!m) {
printf("%s: could not allocate metadata scratch area\n",
DEVNAME(sc));
goto bad;
}
/* from here on out metadata is updated */
restart:
sm->ssd_ondisk++;
sm->ssd_meta_flags = flags;
memcpy(m, sm, sizeof(*m));
/* Chunk metadata. */
cm = (struct sr_meta_chunk *)(m + 1);
for (i = 0; i < sm->ssdi.ssd_chunk_no; i++) {
src = sd->sd_vol.sv_chunks[i];
memcpy(cm, &src->src_meta, sizeof(*cm));
cm++;
}
/* Optional metadata. */
omh = (struct sr_meta_opt_hdr *)(cm);
SLIST_FOREACH(omi, &sd->sd_meta_opt, omi_link) {
DNPRINTF(SR_D_META, "%s: saving optional metadata type %u with "
"length %u\n", DEVNAME(sc), omi->omi_som->som_type,
omi->omi_som->som_length);
bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH);
sr_checksum(sc, omi->omi_som, &omi->omi_som->som_checksum,
omi->omi_som->som_length);
memcpy(omh, omi->omi_som, omi->omi_som->som_length);
omh = (struct sr_meta_opt_hdr *)((u_int8_t *)omh +
omi->omi_som->som_length);
}
for (i = 0; i < sm->ssdi.ssd_chunk_no; i++) {
src = sd->sd_vol.sv_chunks[i];
/* skip disks that are offline */
if (src->src_meta.scm_status == BIOC_SDOFFLINE)
continue;
/* calculate metadata checksum for correct chunk */
m->ssdi.ssd_chunk_id = i;
sr_checksum(sc, m, &m->ssd_checksum,
sizeof(struct sr_meta_invariant));
#ifdef SR_DEBUG
DNPRINTF(SR_D_META, "%s: sr_meta_save %s: volid: %d "
"chunkid: %d checksum: ",
DEVNAME(sc), src->src_meta.scmi.scm_devname,
m->ssdi.ssd_volid, m->ssdi.ssd_chunk_id);
if (sr_debug & SR_D_META)
sr_checksum_print((u_int8_t *)&m->ssd_checksum);
DNPRINTF(SR_D_META, "\n");
sr_meta_print(m);
#endif
/* translate and write to disk */
if (s->smd_write(sd, src->src_dev_mm, m, NULL /* XXX */)) {
printf("%s: could not write metadata to %s\n",
DEVNAME(sc), src->src_devname);
/* restart the meta write */
src->src_meta.scm_status = BIOC_SDOFFLINE;
/* XXX recalculate volume status */
goto restart;
}
}
/* not all disciplines have sync */
if (sd->sd_scsi_sync) {
bzero(&wu, sizeof(wu));
wu.swu_flags |= SR_WUF_FAKE;
wu.swu_dis = sd;
sd->sd_scsi_sync(&wu);
}
free(m, M_DEVBUF, SR_META_SIZE * DEV_BSIZE);
return (0);
bad:
return (1);
}
int
sr_meta_read(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk_head *cl = &sd->sd_vol.sv_chunk_list;
struct sr_metadata *sm;
struct sr_chunk *ch_entry;
struct sr_meta_chunk *cp;
struct sr_meta_driver *s;
void *fm = NULL;
int no_disk = 0, got_meta = 0;
DNPRINTF(SR_D_META, "%s: sr_meta_read\n", DEVNAME(sc));
sm = malloc(SR_META_SIZE * DEV_BSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
s = &smd[sd->sd_meta_type];
if (sd->sd_meta_type != SR_META_F_NATIVE)
fm = malloc(s->smd_size, M_DEVBUF, M_WAITOK | M_ZERO);
cp = (struct sr_meta_chunk *)(sm + 1);
SLIST_FOREACH(ch_entry, cl, src_link) {
/* skip disks that are offline */
if (ch_entry->src_meta.scm_status == BIOC_SDOFFLINE) {
DNPRINTF(SR_D_META,
"%s: %s chunk marked offline, spoofing status\n",
DEVNAME(sc), ch_entry->src_devname);
cp++; /* adjust chunk pointer to match failure */
continue;
} else if (s->smd_read(sd, ch_entry->src_dev_mm, sm, fm)) {
/* read and translate */
/* XXX mark chunk offline, elsewhere!! */
ch_entry->src_meta.scm_status = BIOC_SDOFFLINE;
cp++; /* adjust chunk pointer to match failure */
DNPRINTF(SR_D_META, "%s: sr_meta_read failed\n",
DEVNAME(sc));
continue;
}
if (sm->ssdi.ssd_magic != SR_MAGIC) {
DNPRINTF(SR_D_META, "%s: sr_meta_read !SR_MAGIC\n",
DEVNAME(sc));
continue;
}
/* validate metadata */
if (sr_meta_validate(sd, ch_entry->src_dev_mm, sm, fm)) {
DNPRINTF(SR_D_META, "%s: invalid metadata\n",
DEVNAME(sc));
no_disk = -1;
goto done;
}
/* assume first chunk contains metadata */
if (got_meta == 0) {
sr_meta_opt_load(sc, sm, &sd->sd_meta_opt);
memcpy(sd->sd_meta, sm, sizeof(*sd->sd_meta));
got_meta = 1;
}
memcpy(&ch_entry->src_meta, cp, sizeof(ch_entry->src_meta));
no_disk++;
cp++;
}
free(sm, M_DEVBUF, SR_META_SIZE * DEV_BSIZE);
free(fm, M_DEVBUF, s->smd_size);
done:
DNPRINTF(SR_D_META, "%s: sr_meta_read found %d parts\n", DEVNAME(sc),
no_disk);
return (no_disk);
}
void
sr_meta_opt_load(struct sr_softc *sc, struct sr_metadata *sm,
struct sr_meta_opt_head *som)
{
struct sr_meta_opt_hdr *omh;
struct sr_meta_opt_item *omi;
u_int8_t checksum[MD5_DIGEST_LENGTH];
int i;
/* Process optional metadata. */
omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
omi = malloc(sizeof(struct sr_meta_opt_item), M_DEVBUF,
M_WAITOK | M_ZERO);
SLIST_INSERT_HEAD(som, omi, omi_link);
if (omh->som_length == 0) {
/* Load old fixed length optional metadata. */
DNPRINTF(SR_D_META, "%s: old optional metadata of type "
"%u\n", DEVNAME(sc), omh->som_type);
/* Validate checksum. */
sr_checksum(sc, (void *)omh, &checksum,
SR_OLD_META_OPT_SIZE - MD5_DIGEST_LENGTH);
if (bcmp(&checksum, (void *)omh + SR_OLD_META_OPT_MD5,
sizeof(checksum)))
panic("%s: invalid optional metadata checksum",
DEVNAME(sc));
/* Determine correct length. */
switch (omh->som_type) {
case SR_OPT_CRYPTO:
omh->som_length = sizeof(struct sr_meta_crypto);
break;
case SR_OPT_BOOT:
omh->som_length = sizeof(struct sr_meta_boot);
break;
case SR_OPT_KEYDISK:
omh->som_length =
sizeof(struct sr_meta_keydisk);
break;
default:
panic("unknown old optional metadata type %u",
omh->som_type);
}
omi->omi_som = malloc(omh->som_length, M_DEVBUF,
M_WAITOK | M_ZERO);
memcpy((u_int8_t *)omi->omi_som + sizeof(*omi->omi_som),
(u_int8_t *)omh + SR_OLD_META_OPT_OFFSET,
omh->som_length - sizeof(*omi->omi_som));
omi->omi_som->som_type = omh->som_type;
omi->omi_som->som_length = omh->som_length;
omh = (struct sr_meta_opt_hdr *)((void *)omh +
SR_OLD_META_OPT_SIZE);
} else {
/* Load variable length optional metadata. */
DNPRINTF(SR_D_META, "%s: optional metadata of type %u, "
"length %u\n", DEVNAME(sc), omh->som_type,
omh->som_length);
omi->omi_som = malloc(omh->som_length, M_DEVBUF,
M_WAITOK | M_ZERO);
memcpy(omi->omi_som, omh, omh->som_length);
/* Validate checksum. */
memcpy(&checksum, &omi->omi_som->som_checksum,
MD5_DIGEST_LENGTH);
bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH);
sr_checksum(sc, omi->omi_som,
&omi->omi_som->som_checksum, omh->som_length);
if (bcmp(&checksum, &omi->omi_som->som_checksum,
sizeof(checksum)))
panic("%s: invalid optional metadata checksum",
DEVNAME(sc));
omh = (struct sr_meta_opt_hdr *)((void *)omh +
omh->som_length);
}
}
}
int
sr_meta_validate(struct sr_discipline *sd, dev_t dev, struct sr_metadata *sm,
void *fm)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_meta_driver *s;
#ifdef SR_DEBUG
struct sr_meta_chunk *mc;
#endif
u_int8_t checksum[MD5_DIGEST_LENGTH];
char devname[32];
int rv = 1;
DNPRINTF(SR_D_META, "%s: sr_meta_validate(%p)\n", DEVNAME(sc), sm);
sr_meta_getdevname(sc, dev, devname, sizeof(devname));
s = &smd[sd->sd_meta_type];
if (sd->sd_meta_type != SR_META_F_NATIVE)
if (s->smd_validate(sd, sm, fm)) {
sr_error(sc, "invalid foreign metadata");
goto done;
}
/*
* at this point all foreign metadata has been translated to the native
* format and will be treated just like the native format
*/
if (sm->ssdi.ssd_magic != SR_MAGIC) {
sr_error(sc, "not valid softraid metadata");
goto done;
}
/* Verify metadata checksum. */
sr_checksum(sc, sm, &checksum, sizeof(struct sr_meta_invariant));
if (bcmp(&checksum, &sm->ssd_checksum, sizeof(checksum))) {
sr_error(sc, "invalid metadata checksum");
goto done;
}
/* Handle changes between versions. */
if (sm->ssdi.ssd_version == 3) {
/*
* Version 3 - update metadata version and fix up data blkno
* value since this did not exist in version 3.
*/
if (sm->ssd_data_blkno == 0)
sm->ssd_data_blkno = SR_META_V3_DATA_OFFSET;
sm->ssdi.ssd_secsize = DEV_BSIZE;
} else if (sm->ssdi.ssd_version == 4) {
/*
* Version 4 - original metadata format did not store
* data blkno so fix this up if necessary.
*/
if (sm->ssd_data_blkno == 0)
sm->ssd_data_blkno = SR_DATA_OFFSET;
sm->ssdi.ssd_secsize = DEV_BSIZE;
} else if (sm->ssdi.ssd_version == 5) {
/*
* Version 5 - variable length optional metadata. Migration
* from earlier fixed length optional metadata is handled
* in sr_meta_read().
*/
sm->ssdi.ssd_secsize = DEV_BSIZE;
} else if (sm->ssdi.ssd_version == SR_META_VERSION) {
/*
* Version 6 - store & report a sector size.
*/
} else {
sr_error(sc, "cannot read metadata version %u on %s, "
"expected version %u or earlier",
sm->ssdi.ssd_version, devname, SR_META_VERSION);
goto done;
}
/* Update version number and revision string. */
sm->ssdi.ssd_version = SR_META_VERSION;
snprintf(sm->ssdi.ssd_revision, sizeof(sm->ssdi.ssd_revision),
"%03d", SR_META_VERSION);
#ifdef SR_DEBUG
/* warn if disk changed order */
mc = (struct sr_meta_chunk *)(sm + 1);
if (strncmp(mc[sm->ssdi.ssd_chunk_id].scmi.scm_devname, devname,
sizeof(mc[sm->ssdi.ssd_chunk_id].scmi.scm_devname)))
DNPRINTF(SR_D_META, "%s: roaming device %s -> %s\n",
DEVNAME(sc), mc[sm->ssdi.ssd_chunk_id].scmi.scm_devname,
devname);
#endif
/* we have meta data on disk */
DNPRINTF(SR_D_META, "%s: sr_meta_validate valid metadata %s\n",
DEVNAME(sc), devname);
rv = 0;
done:
return (rv);
}
int
sr_meta_native_bootprobe(struct sr_softc *sc, dev_t devno,
struct sr_boot_chunk_head *bch)
{
struct vnode *vn;
struct disklabel label;
struct sr_metadata *md = NULL;
struct sr_discipline *fake_sd = NULL;
struct sr_boot_chunk *bc;
char devname[32];
dev_t chrdev, rawdev;
int error, i;
int rv = SR_META_NOTCLAIMED;
DNPRINTF(SR_D_META, "%s: sr_meta_native_bootprobe\n", DEVNAME(sc));
/*
* Use character raw device to avoid SCSI complaints about missing
* media on removable media devices.
*/
chrdev = blktochr(devno);
rawdev = MAKEDISKDEV(major(chrdev), DISKUNIT(devno), RAW_PART);
if (cdevvp(rawdev, &vn)) {
sr_error(sc, "sr_meta_native_bootprobe: cannot allocate vnode");
goto done;
}
/* open device */
error = VOP_OPEN(vn, FREAD, NOCRED, curproc);
if (error) {
DNPRINTF(SR_D_META, "%s: sr_meta_native_bootprobe open "
"failed\n", DEVNAME(sc));
vput(vn);
goto done;
}
/* get disklabel */
error = VOP_IOCTL(vn, DIOCGDINFO, (caddr_t)&label, FREAD, NOCRED,
curproc);
if (error) {
DNPRINTF(SR_D_META, "%s: sr_meta_native_bootprobe ioctl "
"failed\n", DEVNAME(sc));
VOP_CLOSE(vn, FREAD, NOCRED, curproc);
vput(vn);
goto done;
}
/* we are done, close device */
error = VOP_CLOSE(vn, FREAD, NOCRED, curproc);
if (error) {
DNPRINTF(SR_D_META, "%s: sr_meta_native_bootprobe close "
"failed\n", DEVNAME(sc));
vput(vn);
goto done;
}
vput(vn);
md = malloc(SR_META_SIZE * DEV_BSIZE, M_DEVBUF, M_ZERO | M_NOWAIT);
if (md == NULL) {
sr_error(sc, "not enough memory for metadata buffer");
goto done;
}
/* create fake sd to use utility functions */
fake_sd = malloc(sizeof(struct sr_discipline), M_DEVBUF,
M_ZERO | M_NOWAIT);
if (fake_sd == NULL) {
sr_error(sc, "not enough memory for fake discipline");
goto done;
}
fake_sd->sd_sc = sc;
fake_sd->sd_meta_type = SR_META_F_NATIVE;
for (i = 0; i < MAXPARTITIONS; i++) {
if (label.d_partitions[i].p_fstype != FS_RAID)
continue;
/* open partition */
rawdev = MAKEDISKDEV(major(devno), DISKUNIT(devno), i);
if (bdevvp(rawdev, &vn)) {
sr_error(sc, "sr_meta_native_bootprobe: cannot "
"allocate vnode for partition");
goto done;
}
error = VOP_OPEN(vn, FREAD, NOCRED, curproc);
if (error) {
DNPRINTF(SR_D_META, "%s: sr_meta_native_bootprobe "
"open failed, partition %d\n",
DEVNAME(sc), i);
vput(vn);
continue;
}
if (sr_meta_native_read(fake_sd, rawdev, md, NULL)) {
sr_error(sc, "native bootprobe could not read native "
"metadata");
VOP_CLOSE(vn, FREAD, NOCRED, curproc);
vput(vn);
continue;
}
/* are we a softraid partition? */
if (md->ssdi.ssd_magic != SR_MAGIC) {
VOP_CLOSE(vn, FREAD, NOCRED, curproc);
vput(vn);
continue;
}
sr_meta_getdevname(sc, rawdev, devname, sizeof(devname));
if (sr_meta_validate(fake_sd, rawdev, md, NULL) == 0) {
/* XXX fix M_WAITOK, this is boot time */
bc = malloc(sizeof(struct sr_boot_chunk),
M_DEVBUF, M_WAITOK | M_ZERO);
bc->sbc_metadata = malloc(sizeof(struct sr_metadata),
M_DEVBUF, M_WAITOK | M_ZERO);
memcpy(bc->sbc_metadata, md, sizeof(struct sr_metadata));
bc->sbc_mm = rawdev;
SLIST_INSERT_HEAD(bch, bc, sbc_link);
rv = SR_META_CLAIMED;
}
/* we are done, close partition */
VOP_CLOSE(vn, FREAD, NOCRED, curproc);
vput(vn);
}
done:
free(fake_sd, M_DEVBUF, sizeof(struct sr_discipline));
free(md, M_DEVBUF, SR_META_SIZE * DEV_BSIZE);
return (rv);
}
int
sr_boot_assembly(struct sr_softc *sc)
{
struct sr_boot_volume_head bvh;
struct sr_boot_chunk_head bch, kdh;
struct sr_boot_volume *bv, *bv1, *bv2;
struct sr_boot_chunk *bc, *bcnext, *bc1, *bc2;
struct sr_disk_head sdklist;
struct sr_disk *sdk;
struct disk *dk;
struct bioc_createraid bcr;
struct sr_meta_chunk *hm;
struct sr_chunk_head *cl;
struct sr_chunk *hotspare, *chunk, *last;
u_int64_t *ondisk = NULL;
dev_t *devs = NULL;
void *data;
char devname[32];
int rv = 0, i;
DNPRINTF(SR_D_META, "%s: sr_boot_assembly\n", DEVNAME(sc));
SLIST_INIT(&sdklist);
SLIST_INIT(&bvh);
SLIST_INIT(&bch);
SLIST_INIT(&kdh);
dk = TAILQ_FIRST(&disklist);
while (dk != NULL) {
/* See if this disk has been checked. */
SLIST_FOREACH(sdk, &sdklist, sdk_link)
if (sdk->sdk_devno == dk->dk_devno)
break;
if (sdk != NULL || dk->dk_devno == NODEV) {
dk = TAILQ_NEXT(dk, dk_link);
continue;
}
/* Add this disk to the list that we've checked. */
sdk = malloc(sizeof(struct sr_disk), M_DEVBUF,
M_NOWAIT | M_ZERO);
if (sdk == NULL)
goto unwind;
sdk->sdk_devno = dk->dk_devno;
SLIST_INSERT_HEAD(&sdklist, sdk, sdk_link);
/* Only check sd(4) and wd(4) devices. */
if (strncmp(dk->dk_name, "sd", 2) &&
strncmp(dk->dk_name, "wd", 2)) {
dk = TAILQ_NEXT(dk, dk_link);
continue;
}
/* native softraid uses partitions */
rw_enter_write(&sc->sc_lock);
bio_status_init(&sc->sc_status, &sc->sc_dev);
sr_meta_native_bootprobe(sc, dk->dk_devno, &bch);
rw_exit_write(&sc->sc_lock);
/* probe non-native disks if native failed. */
/* Restart scan since we may have slept. */
dk = TAILQ_FIRST(&disklist);
}
/*
* Create a list of volumes and associate chunks with each volume.
*/
for (bc = SLIST_FIRST(&bch); bc != NULL; bc = bcnext) {
bcnext = SLIST_NEXT(bc, sbc_link);
SLIST_REMOVE(&bch, bc, sr_boot_chunk, sbc_link);
bc->sbc_chunk_id = bc->sbc_metadata->ssdi.ssd_chunk_id;
/* Handle key disks separately. */
if (bc->sbc_metadata->ssdi.ssd_level == SR_KEYDISK_LEVEL) {
SLIST_INSERT_HEAD(&kdh, bc, sbc_link);
continue;
}
SLIST_FOREACH(bv, &bvh, sbv_link) {
if (bcmp(&bc->sbc_metadata->ssdi.ssd_uuid,
&bv->sbv_uuid,
sizeof(bc->sbc_metadata->ssdi.ssd_uuid)) == 0)
break;
}
if (bv == NULL) {
bv = malloc(sizeof(struct sr_boot_volume),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (bv == NULL) {
printf("%s: failed to allocate boot volume\n",
DEVNAME(sc));
goto unwind;
}
bv->sbv_level = bc->sbc_metadata->ssdi.ssd_level;
bv->sbv_volid = bc->sbc_metadata->ssdi.ssd_volid;
bv->sbv_chunk_no = bc->sbc_metadata->ssdi.ssd_chunk_no;
bv->sbv_flags = bc->sbc_metadata->ssdi.ssd_vol_flags;
memcpy(&bv->sbv_uuid, &bc->sbc_metadata->ssdi.ssd_uuid,
sizeof(bc->sbc_metadata->ssdi.ssd_uuid));
SLIST_INIT(&bv->sbv_chunks);
/* Maintain volume order. */
bv2 = NULL;
SLIST_FOREACH(bv1, &bvh, sbv_link) {
if (bv1->sbv_volid > bv->sbv_volid)
break;
bv2 = bv1;
}
if (bv2 == NULL) {
DNPRINTF(SR_D_META, "%s: insert volume %u "
"at head\n", DEVNAME(sc), bv->sbv_volid);
SLIST_INSERT_HEAD(&bvh, bv, sbv_link);
} else {
DNPRINTF(SR_D_META, "%s: insert volume %u "
"after %u\n", DEVNAME(sc), bv->sbv_volid,
bv2->sbv_volid);
SLIST_INSERT_AFTER(bv2, bv, sbv_link);
}
}
/* Maintain chunk order. */
bc2 = NULL;
SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) {
if (bc1->sbc_chunk_id > bc->sbc_chunk_id)
break;
bc2 = bc1;
}
if (bc2 == NULL) {
DNPRINTF(SR_D_META, "%s: volume %u insert chunk %u "
"at head\n", DEVNAME(sc), bv->sbv_volid,
bc->sbc_chunk_id);
SLIST_INSERT_HEAD(&bv->sbv_chunks, bc, sbc_link);
} else {
DNPRINTF(SR_D_META, "%s: volume %u insert chunk %u "
"after %u\n", DEVNAME(sc), bv->sbv_volid,
bc->sbc_chunk_id, bc2->sbc_chunk_id);
SLIST_INSERT_AFTER(bc2, bc, sbc_link);
}
bv->sbv_chunks_found++;
}
/* Allocate memory for device and ondisk version arrays. */
devs = mallocarray(BIOC_CRMAXLEN, sizeof(dev_t), M_DEVBUF,
M_NOWAIT);
if (devs == NULL) {
printf("%s: failed to allocate device array\n", DEVNAME(sc));
goto unwind;
}
ondisk = mallocarray(BIOC_CRMAXLEN, sizeof(u_int64_t), M_DEVBUF,
M_NOWAIT);
if (ondisk == NULL) {
printf("%s: failed to allocate ondisk array\n", DEVNAME(sc));
goto unwind;
}
/*
* Assemble hotspare "volumes".
*/
SLIST_FOREACH(bv, &bvh, sbv_link) {
/* Check if this is a hotspare "volume". */
if (bv->sbv_level != SR_HOTSPARE_LEVEL ||
bv->sbv_chunk_no != 1)
continue;
#ifdef SR_DEBUG
DNPRINTF(SR_D_META, "%s: assembling hotspare volume ",
DEVNAME(sc));
if (sr_debug & SR_D_META)
sr_uuid_print(&bv->sbv_uuid, 0);
DNPRINTF(SR_D_META, " volid %u with %u chunks\n",
bv->sbv_volid, bv->sbv_chunk_no);
#endif
/* Create hotspare chunk metadata. */
hotspare = malloc(sizeof(struct sr_chunk), M_DEVBUF,
M_NOWAIT | M_ZERO);
if (hotspare == NULL) {
printf("%s: failed to allocate hotspare\n",
DEVNAME(sc));
goto unwind;
}
bc = SLIST_FIRST(&bv->sbv_chunks);
sr_meta_getdevname(sc, bc->sbc_mm, devname, sizeof(devname));
hotspare->src_dev_mm = bc->sbc_mm;
strlcpy(hotspare->src_devname, devname,
sizeof(hotspare->src_devname));
hotspare->src_size = bc->sbc_metadata->ssdi.ssd_size;
hm = &hotspare->src_meta;
hm->scmi.scm_volid = SR_HOTSPARE_VOLID;
hm->scmi.scm_chunk_id = 0;
hm->scmi.scm_size = bc->sbc_metadata->ssdi.ssd_size;
hm->scmi.scm_coerced_size = bc->sbc_metadata->ssdi.ssd_size;
strlcpy(hm->scmi.scm_devname, devname,
sizeof(hm->scmi.scm_devname));
memcpy(&hm->scmi.scm_uuid, &bc->sbc_metadata->ssdi.ssd_uuid,
sizeof(struct sr_uuid));
sr_checksum(sc, hm, &hm->scm_checksum,
sizeof(struct sr_meta_chunk_invariant));
hm->scm_status = BIOC_SDHOTSPARE;
/* Add chunk to hotspare list. */
rw_enter_write(&sc->sc_hs_lock);
cl = &sc->sc_hotspare_list;
if (SLIST_EMPTY(cl))
SLIST_INSERT_HEAD(cl, hotspare, src_link);
else {
SLIST_FOREACH(chunk, cl, src_link)
last = chunk;
SLIST_INSERT_AFTER(last, hotspare, src_link);
}
sc->sc_hotspare_no++;
rw_exit_write(&sc->sc_hs_lock);
}
/*
* Assemble RAID volumes.
*/
SLIST_FOREACH(bv, &bvh, sbv_link) {
bzero(&bcr, sizeof(bcr));
data = NULL;
/* Check if this is a hotspare "volume". */
if (bv->sbv_level == SR_HOTSPARE_LEVEL &&
bv->sbv_chunk_no == 1)
continue;
/*
* Skip volumes that are marked as no auto assemble, unless
* this was the volume which we actually booted from.
*/
if (bcmp(&sr_bootuuid, &bv->sbv_uuid, sizeof(sr_bootuuid)) != 0)
if (bv->sbv_flags & BIOC_SCNOAUTOASSEMBLE)
continue;
#ifdef SR_DEBUG
DNPRINTF(SR_D_META, "%s: assembling volume ", DEVNAME(sc));
if (sr_debug & SR_D_META)
sr_uuid_print(&bv->sbv_uuid, 0);
DNPRINTF(SR_D_META, " volid %u with %u chunks\n",
bv->sbv_volid, bv->sbv_chunk_no);
#endif
/*
* If this is a crypto volume, try to find a matching
* key disk...
*/
bcr.bc_key_disk = NODEV;
if (bv->sbv_level == 'C' || bv->sbv_level == 0x1C) {
SLIST_FOREACH(bc, &kdh, sbc_link) {
if (bcmp(&bc->sbc_metadata->ssdi.ssd_uuid,
&bv->sbv_uuid,
sizeof(bc->sbc_metadata->ssdi.ssd_uuid))
== 0)
bcr.bc_key_disk = bc->sbc_mm;
}
}
for (i = 0; i < BIOC_CRMAXLEN; i++) {
devs[i] = NODEV; /* mark device as illegal */
ondisk[i] = 0;
}
SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
if (devs[bc->sbc_chunk_id] != NODEV) {
bv->sbv_chunks_found--;
sr_meta_getdevname(sc, bc->sbc_mm, devname,
sizeof(devname));
printf("%s: found duplicate chunk %u for "
"volume %u on device %s\n", DEVNAME(sc),
bc->sbc_chunk_id, bv->sbv_volid, devname);
}
if (devs[bc->sbc_chunk_id] == NODEV ||
bc->sbc_metadata->ssd_ondisk >
ondisk[bc->sbc_chunk_id]) {
devs[bc->sbc_chunk_id] = bc->sbc_mm;
ondisk[bc->sbc_chunk_id] =
bc->sbc_metadata->ssd_ondisk;
DNPRINTF(SR_D_META, "%s: using ondisk "
"metadata version %llu for chunk %u\n",
DEVNAME(sc), ondisk[bc->sbc_chunk_id],
bc->sbc_chunk_id);
}
}
if (bv->sbv_chunk_no != bv->sbv_chunks_found) {
printf("%s: not all chunks were provided; "
"attempting to bring volume %d online\n",
DEVNAME(sc), bv->sbv_volid);
}
bcr.bc_level = bv->sbv_level;
bcr.bc_dev_list_len = bv->sbv_chunk_no * sizeof(dev_t);
bcr.bc_dev_list = devs;
bcr.bc_flags = BIOC_SCDEVT |
(bv->sbv_flags & BIOC_SCNOAUTOASSEMBLE);
if ((bv->sbv_level == 'C' || bv->sbv_level == 0x1C) &&
bcmp(&sr_bootuuid, &bv->sbv_uuid, sizeof(sr_bootuuid)) == 0)
data = sr_bootkey;
rw_enter_write(&sc->sc_lock);
bio_status_init(&sc->sc_status, &sc->sc_dev);
sr_ioctl_createraid(sc, &bcr, 0, data);
rw_exit_write(&sc->sc_lock);
rv++;
}
/* done with metadata */
unwind:
/* Free boot volumes and associated chunks. */
for (bv1 = SLIST_FIRST(&bvh); bv1 != NULL; bv1 = bv2) {
bv2 = SLIST_NEXT(bv1, sbv_link);
for (bc1 = SLIST_FIRST(&bv1->sbv_chunks); bc1 != NULL;
bc1 = bc2) {
bc2 = SLIST_NEXT(bc1, sbc_link);
free(bc1->sbc_metadata, M_DEVBUF,
sizeof(*bc1->sbc_metadata));
free(bc1, M_DEVBUF, sizeof(*bc1));
}
free(bv1, M_DEVBUF, sizeof(*bv1));
}
/* Free keydisks chunks. */
for (bc1 = SLIST_FIRST(&kdh); bc1 != NULL; bc1 = bc2) {
bc2 = SLIST_NEXT(bc1, sbc_link);
free(bc1->sbc_metadata, M_DEVBUF, sizeof(*bc1->sbc_metadata));
free(bc1, M_DEVBUF, sizeof(*bc1));
}
/* Free unallocated chunks. */
for (bc1 = SLIST_FIRST(&bch); bc1 != NULL; bc1 = bc2) {
bc2 = SLIST_NEXT(bc1, sbc_link);
free(bc1->sbc_metadata, M_DEVBUF, sizeof(*bc1->sbc_metadata));
free(bc1, M_DEVBUF, sizeof(*bc1));
}
while (!SLIST_EMPTY(&sdklist)) {
sdk = SLIST_FIRST(&sdklist);
SLIST_REMOVE_HEAD(&sdklist, sdk_link);
free(sdk, M_DEVBUF, sizeof(*sdk));
}
free(devs, M_DEVBUF, BIOC_CRMAXLEN * sizeof(dev_t));
free(ondisk, M_DEVBUF, BIOC_CRMAXLEN * sizeof(u_int64_t));
return (rv);
}
void
sr_map_root(void)
{
struct sr_softc *sc = softraid0;
struct sr_discipline *sd;
struct sr_meta_opt_item *omi;
struct sr_meta_boot *sbm;
u_char duid[8];
int i;
if (sc == NULL)
return;
DNPRINTF(SR_D_MISC, "%s: sr_map_root\n", DEVNAME(sc));
bzero(duid, sizeof(duid));
if (bcmp(rootduid, duid, sizeof(duid)) == 0) {
DNPRINTF(SR_D_MISC, "%s: root duid is zero\n", DEVNAME(sc));
return;
}
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
SLIST_FOREACH(omi, &sd->sd_meta_opt, omi_link) {
if (omi->omi_som->som_type != SR_OPT_BOOT)
continue;
sbm = (struct sr_meta_boot *)omi->omi_som;
for (i = 0; i < SR_MAX_BOOT_DISKS; i++) {
if (bcmp(rootduid, sbm->sbm_boot_duid[i],
sizeof(rootduid)) == 0) {
memcpy(rootduid, sbm->sbm_root_duid,
sizeof(rootduid));
DNPRINTF(SR_D_MISC, "%s: root duid "
"mapped to %s\n", DEVNAME(sc),
duid_format(rootduid));
return;
}
}
}
}
}
int
sr_meta_native_probe(struct sr_softc *sc, struct sr_chunk *ch_entry)
{
struct disklabel label;
char *devname;
int error, part;
u_int64_t size;
DNPRINTF(SR_D_META, "%s: sr_meta_native_probe(%s)\n",
DEVNAME(sc), ch_entry->src_devname);
devname = ch_entry->src_devname;
part = DISKPART(ch_entry->src_dev_mm);
/* get disklabel */
error = VOP_IOCTL(ch_entry->src_vn, DIOCGDINFO, (caddr_t)&label, FREAD,
NOCRED, curproc);
if (error) {
DNPRINTF(SR_D_META, "%s: %s can't obtain disklabel\n",
DEVNAME(sc), devname);
goto unwind;
}
memcpy(ch_entry->src_duid, label.d_uid, sizeof(ch_entry->src_duid));
/* make sure the partition is of the right type */
if (label.d_partitions[part].p_fstype != FS_RAID) {
DNPRINTF(SR_D_META,
"%s: %s partition not of type RAID (%d)\n", DEVNAME(sc),
devname,
label.d_partitions[part].p_fstype);
goto unwind;
}
size = DL_SECTOBLK(&label, DL_GETPSIZE(&label.d_partitions[part]));
if (size <= SR_DATA_OFFSET) {
DNPRINTF(SR_D_META, "%s: %s partition too small\n", DEVNAME(sc),
devname);
goto unwind;
}
size -= SR_DATA_OFFSET;
if (size > INT64_MAX) {
DNPRINTF(SR_D_META, "%s: %s partition too large\n", DEVNAME(sc),
devname);
goto unwind;
}
ch_entry->src_size = size;
ch_entry->src_secsize = label.d_secsize;
DNPRINTF(SR_D_META, "%s: probe found %s size %lld\n", DEVNAME(sc),
devname, (long long)size);
return (SR_META_F_NATIVE);
unwind:
DNPRINTF(SR_D_META, "%s: invalid device: %s\n", DEVNAME(sc),
devname ? devname : "nodev");
return (SR_META_F_INVALID);
}
int
sr_meta_native_attach(struct sr_discipline *sd, int force)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk_head *cl = &sd->sd_vol.sv_chunk_list;
struct sr_metadata *md = NULL;
struct sr_chunk *ch_entry, *ch_next;
struct sr_uuid uuid;
u_int64_t version = 0;
int sr, not_sr, rv = 1, d, expected = -1, old_meta = 0;
DNPRINTF(SR_D_META, "%s: sr_meta_native_attach\n", DEVNAME(sc));
md = malloc(SR_META_SIZE * DEV_BSIZE, M_DEVBUF, M_ZERO | M_NOWAIT);
if (md == NULL) {
sr_error(sc, "not enough memory for metadata buffer");
goto bad;
}
bzero(&uuid, sizeof uuid);
sr = not_sr = d = 0;
SLIST_FOREACH(ch_entry, cl, src_link) {
if (ch_entry->src_dev_mm == NODEV)
continue;
if (sr_meta_native_read(sd, ch_entry->src_dev_mm, md, NULL)) {
sr_error(sc, "could not read native metadata");
goto bad;
}
if (md->ssdi.ssd_magic == SR_MAGIC) {
sr++;
ch_entry->src_meta.scmi.scm_chunk_id =
md->ssdi.ssd_chunk_id;
if (d == 0) {
memcpy(&uuid, &md->ssdi.ssd_uuid, sizeof uuid);
expected = md->ssdi.ssd_chunk_no;
version = md->ssd_ondisk;
d++;
continue;
} else if (bcmp(&md->ssdi.ssd_uuid, &uuid,
sizeof uuid)) {
sr_error(sc, "not part of the same volume");
goto bad;
}
if (md->ssd_ondisk != version) {
old_meta++;
version = MAX(md->ssd_ondisk, version);
}
} else
not_sr++;
}
if (sr && not_sr && !force) {
sr_error(sc, "not all chunks are of the native metadata "
"format");
goto bad;
}
/* mixed metadata versions; mark bad disks offline */
if (old_meta) {
d = 0;
for (ch_entry = SLIST_FIRST(cl); ch_entry != NULL;
ch_entry = ch_next, d++) {
ch_next = SLIST_NEXT(ch_entry, src_link);
/* XXX do we want to read this again? */
if (ch_entry->src_dev_mm == NODEV)
panic("src_dev_mm == NODEV");
if (sr_meta_native_read(sd, ch_entry->src_dev_mm, md,
NULL))
sr_warn(sc, "could not read native metadata");
if (md->ssd_ondisk != version)
sd->sd_vol.sv_chunks[d]->src_meta.scm_status =
BIOC_SDOFFLINE;
}
}
if (expected != sr && !force && expected != -1) {
DNPRINTF(SR_D_META, "%s: not all chunks were provided, trying "
"anyway\n", DEVNAME(sc));
}
rv = 0;
bad:
free(md, M_DEVBUF, SR_META_SIZE * DEV_BSIZE);
return (rv);
}
int
sr_meta_native_read(struct sr_discipline *sd, dev_t dev,
struct sr_metadata *md, void *fm)
{
#ifdef SR_DEBUG
struct sr_softc *sc = sd->sd_sc;
#endif
DNPRINTF(SR_D_META, "%s: sr_meta_native_read(0x%x, %p)\n",
DEVNAME(sc), dev, md);
return (sr_meta_rw(sd, dev, md, B_READ));
}
int
sr_meta_native_write(struct sr_discipline *sd, dev_t dev,
struct sr_metadata *md, void *fm)
{
#ifdef SR_DEBUG
struct sr_softc *sc = sd->sd_sc;
#endif
DNPRINTF(SR_D_META, "%s: sr_meta_native_write(0x%x, %p)\n",
DEVNAME(sc), dev, md);
return (sr_meta_rw(sd, dev, md, B_WRITE));
}
void
sr_hotplug_register(struct sr_discipline *sd, void *func)
{
struct sr_hotplug_list *mhe;
DNPRINTF(SR_D_MISC, "%s: sr_hotplug_register: %p\n",
DEVNAME(sd->sd_sc), func);
/* make sure we aren't on the list yet */
SLIST_FOREACH(mhe, &sr_hotplug_callbacks, shl_link)
if (mhe->sh_hotplug == func)
return;
mhe = malloc(sizeof(struct sr_hotplug_list), M_DEVBUF,
M_WAITOK | M_ZERO);
mhe->sh_hotplug = func;
mhe->sh_sd = sd;
SLIST_INSERT_HEAD(&sr_hotplug_callbacks, mhe, shl_link);
}
void
sr_hotplug_unregister(struct sr_discipline *sd, void *func)
{
struct sr_hotplug_list *mhe;
DNPRINTF(SR_D_MISC, "%s: sr_hotplug_unregister: %s %p\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, func);
/* make sure we are on the list yet */
SLIST_FOREACH(mhe, &sr_hotplug_callbacks, shl_link) {
if (mhe->sh_hotplug == func)
break;
}
if (mhe != NULL) {
SLIST_REMOVE(&sr_hotplug_callbacks, mhe,
sr_hotplug_list, shl_link);
free(mhe, M_DEVBUF, sizeof(*mhe));
}
}
void
sr_disk_attach(struct disk *diskp, int action)
{
struct sr_hotplug_list *mhe;
SLIST_FOREACH(mhe, &sr_hotplug_callbacks, shl_link)
if (mhe->sh_sd->sd_ready)
mhe->sh_hotplug(mhe->sh_sd, diskp, action);
}
int
sr_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
sr_attach(struct device *parent, struct device *self, void *aux)
{
struct sr_softc *sc = (void *)self;
struct scsibus_attach_args saa;
DNPRINTF(SR_D_MISC, "\n%s: sr_attach", DEVNAME(sc));
if (softraid0 == NULL)
softraid0 = sc;
rw_init(&sc->sc_lock, "sr_lock");
rw_init(&sc->sc_hs_lock, "sr_hs_lock");
SLIST_INIT(&sr_hotplug_callbacks);
TAILQ_INIT(&sc->sc_dis_list);
SLIST_INIT(&sc->sc_hotspare_list);
#if NBIO > 0
if (bio_register(&sc->sc_dev, sr_bio_ioctl) != 0)
printf("%s: controller registration failed", DEVNAME(sc));
#endif /* NBIO > 0 */
#ifndef SMALL_KERNEL
strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
sizeof(sc->sc_sensordev.xname));
sensordev_install(&sc->sc_sensordev);
#endif /* SMALL_KERNEL */
printf("\n");
saa.saa_adapter_softc = sc;
saa.saa_adapter = &sr_switch;
saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
saa.saa_adapter_buswidth = SR_MAX_LD;
saa.saa_luns = 1;
saa.saa_openings = 0;
saa.saa_pool = NULL;
saa.saa_quirks = saa.saa_flags = 0;
saa.saa_wwpn = saa.saa_wwnn = 0;
sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev, &saa,
scsiprint);
softraid_disk_attach = sr_disk_attach;
sr_boot_assembly(sc);
explicit_bzero(sr_bootkey, sizeof(sr_bootkey));
}
int
sr_detach(struct device *self, int flags)
{
struct sr_softc *sc = (void *)self;
int rv;
DNPRINTF(SR_D_MISC, "%s: sr_detach\n", DEVNAME(sc));
softraid_disk_attach = NULL;
sr_shutdown(0);
#ifndef SMALL_KERNEL
if (sc->sc_sensor_task != NULL)
sensor_task_unregister(sc->sc_sensor_task);
sensordev_deinstall(&sc->sc_sensordev);
#endif /* SMALL_KERNEL */
if (sc->sc_scsibus != NULL) {
rv = config_detach((struct device *)sc->sc_scsibus, flags);
if (rv != 0)
return (rv);
sc->sc_scsibus = NULL;
}
return (0);
}
void
sr_info(struct sr_softc *sc, const char *fmt, ...)
{
va_list ap;
rw_assert_wrlock(&sc->sc_lock);
va_start(ap, fmt);
bio_status(&sc->sc_status, 0, BIO_MSG_INFO, fmt, &ap);
va_end(ap);
}
void
sr_warn(struct sr_softc *sc, const char *fmt, ...)
{
va_list ap;
rw_assert_wrlock(&sc->sc_lock);
va_start(ap, fmt);
bio_status(&sc->sc_status, 1, BIO_MSG_WARN, fmt, &ap);
va_end(ap);
}
void
sr_error(struct sr_softc *sc, const char *fmt, ...)
{
va_list ap;
rw_assert_wrlock(&sc->sc_lock);
va_start(ap, fmt);
bio_status(&sc->sc_status, 1, BIO_MSG_ERROR, fmt, &ap);
va_end(ap);
}
int
sr_ccb_alloc(struct sr_discipline *sd)
{
struct sr_ccb *ccb;
int i;
if (!sd)
return (1);
DNPRINTF(SR_D_CCB, "%s: sr_ccb_alloc\n", DEVNAME(sd->sd_sc));
if (sd->sd_ccb)
return (1);
sd->sd_ccb = mallocarray(sd->sd_max_wu,
sd->sd_max_ccb_per_wu * sizeof(struct sr_ccb),
M_DEVBUF, M_WAITOK | M_ZERO);
TAILQ_INIT(&sd->sd_ccb_freeq);
for (i = 0; i < sd->sd_max_wu * sd->sd_max_ccb_per_wu; i++) {
ccb = &sd->sd_ccb[i];
ccb->ccb_dis = sd;
sr_ccb_put(ccb);
}
DNPRINTF(SR_D_CCB, "%s: sr_ccb_alloc ccb: %d\n",
DEVNAME(sd->sd_sc), sd->sd_max_wu * sd->sd_max_ccb_per_wu);
return (0);
}
void
sr_ccb_free(struct sr_discipline *sd)
{
struct sr_ccb *ccb;
if (!sd)
return;
DNPRINTF(SR_D_CCB, "%s: sr_ccb_free %p\n", DEVNAME(sd->sd_sc), sd);
while ((ccb = TAILQ_FIRST(&sd->sd_ccb_freeq)) != NULL)
TAILQ_REMOVE(&sd->sd_ccb_freeq, ccb, ccb_link);
free(sd->sd_ccb, M_DEVBUF, sd->sd_max_wu * sd->sd_max_ccb_per_wu *
sizeof(struct sr_ccb));
}
struct sr_ccb *
sr_ccb_get(struct sr_discipline *sd)
{
struct sr_ccb *ccb;
int s;
s = splbio();
ccb = TAILQ_FIRST(&sd->sd_ccb_freeq);
if (ccb) {
TAILQ_REMOVE(&sd->sd_ccb_freeq, ccb, ccb_link);
ccb->ccb_state = SR_CCB_INPROGRESS;
}
splx(s);
DNPRINTF(SR_D_CCB, "%s: sr_ccb_get: %p\n", DEVNAME(sd->sd_sc),
ccb);
return (ccb);
}
void
sr_ccb_put(struct sr_ccb *ccb)
{
struct sr_discipline *sd = ccb->ccb_dis;
int s;
DNPRINTF(SR_D_CCB, "%s: sr_ccb_put: %p\n", DEVNAME(sd->sd_sc),
ccb);
s = splbio();
ccb->ccb_wu = NULL;
ccb->ccb_state = SR_CCB_FREE;
ccb->ccb_target = -1;
ccb->ccb_opaque = NULL;
TAILQ_INSERT_TAIL(&sd->sd_ccb_freeq, ccb, ccb_link);
splx(s);
}
struct sr_ccb *
sr_ccb_rw(struct sr_discipline *sd, int chunk, daddr_t blkno,
long len, u_int8_t *data, int xsflags, int ccbflags)
{
struct sr_chunk *sc = sd->sd_vol.sv_chunks[chunk];
struct sr_ccb *ccb = NULL;
int s;
ccb = sr_ccb_get(sd);
if (ccb == NULL)
goto out;
ccb->ccb_flags = ccbflags;
ccb->ccb_target = chunk;
ccb->ccb_buf.b_flags = B_PHYS | B_CALL;
if (ISSET(xsflags, SCSI_DATA_IN))
ccb->ccb_buf.b_flags |= B_READ;
else
ccb->ccb_buf.b_flags |= B_WRITE;
ccb->ccb_buf.b_blkno = blkno + sd->sd_meta->ssd_data_blkno;
ccb->ccb_buf.b_bcount = len;
ccb->ccb_buf.b_bufsize = len;
ccb->ccb_buf.b_resid = len;
ccb->ccb_buf.b_data = data;
ccb->ccb_buf.b_error = 0;
ccb->ccb_buf.b_iodone = sd->sd_scsi_intr;
ccb->ccb_buf.b_proc = curproc;
ccb->ccb_buf.b_dev = sc->src_dev_mm;
ccb->ccb_buf.b_vp = sc->src_vn;
ccb->ccb_buf.b_bq = NULL;
if (!ISSET(ccb->ccb_buf.b_flags, B_READ)) {
s = splbio();
ccb->ccb_buf.b_vp->v_numoutput++;
splx(s);
}
LIST_INIT(&ccb->ccb_buf.b_dep);
DNPRINTF(SR_D_DIS, "%s: %s %s ccb "
"b_bcount %ld b_blkno %lld b_flags 0x%0lx b_data %p\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, sd->sd_name,
ccb->ccb_buf.b_bcount, (long long)ccb->ccb_buf.b_blkno,
ccb->ccb_buf.b_flags, ccb->ccb_buf.b_data);
out:
return ccb;
}
void
sr_ccb_done(struct sr_ccb *ccb)
{
struct sr_workunit *wu = ccb->ccb_wu;
struct sr_discipline *sd = wu->swu_dis;
struct sr_softc *sc = sd->sd_sc;
DNPRINTF(SR_D_INTR, "%s: %s %s ccb done b_bcount %ld b_resid %zu"
" b_flags 0x%0lx block %lld target %d\n",
DEVNAME(sc), sd->sd_meta->ssd_devname, sd->sd_name,
ccb->ccb_buf.b_bcount, ccb->ccb_buf.b_resid, ccb->ccb_buf.b_flags,
(long long)ccb->ccb_buf.b_blkno, ccb->ccb_target);
splassert(IPL_BIO);
if (ccb->ccb_target == -1)
panic("%s: invalid target on wu: %p", DEVNAME(sc), wu);
if (ccb->ccb_buf.b_flags & B_ERROR) {
DNPRINTF(SR_D_INTR, "%s: i/o error on block %lld target %d\n",
DEVNAME(sc), (long long)ccb->ccb_buf.b_blkno,
ccb->ccb_target);
if (ISSET(sd->sd_capabilities, SR_CAP_REDUNDANT))
sd->sd_set_chunk_state(sd, ccb->ccb_target,
BIOC_SDOFFLINE);
else
printf("%s: %s: i/o error %d @ %s block %lld\n",
DEVNAME(sc), sd->sd_meta->ssd_devname,
ccb->ccb_buf.b_error, sd->sd_name,
(long long)ccb->ccb_buf.b_blkno);
ccb->ccb_state = SR_CCB_FAILED;
wu->swu_ios_failed++;
} else {
ccb->ccb_state = SR_CCB_OK;
wu->swu_ios_succeeded++;
}
wu->swu_ios_complete++;
}
int
sr_wu_alloc(struct sr_discipline *sd)
{
struct sr_workunit *wu;
int i, no_wu;
DNPRINTF(SR_D_WU, "%s: sr_wu_alloc %p %d\n", DEVNAME(sd->sd_sc),
sd, sd->sd_max_wu);
no_wu = sd->sd_max_wu;
sd->sd_wu_pending = no_wu;
mtx_init(&sd->sd_wu_mtx, IPL_BIO);
TAILQ_INIT(&sd->sd_wu);
TAILQ_INIT(&sd->sd_wu_freeq);
TAILQ_INIT(&sd->sd_wu_pendq);
TAILQ_INIT(&sd->sd_wu_defq);
for (i = 0; i < no_wu; i++) {
wu = malloc(sd->sd_wu_size, M_DEVBUF, M_WAITOK | M_ZERO);
TAILQ_INSERT_TAIL(&sd->sd_wu, wu, swu_next);
TAILQ_INIT(&wu->swu_ccb);
wu->swu_dis = sd;
task_set(&wu->swu_task, sr_wu_done_callback, wu);
sr_wu_put(sd, wu);
}
return (0);
}
void
sr_wu_free(struct sr_discipline *sd)
{
struct sr_workunit *wu;
DNPRINTF(SR_D_WU, "%s: sr_wu_free %p\n", DEVNAME(sd->sd_sc), sd);
while ((wu = TAILQ_FIRST(&sd->sd_wu_freeq)) != NULL)
TAILQ_REMOVE(&sd->sd_wu_freeq, wu, swu_link);
while ((wu = TAILQ_FIRST(&sd->sd_wu_pendq)) != NULL)
TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link);
while ((wu = TAILQ_FIRST(&sd->sd_wu_defq)) != NULL)
TAILQ_REMOVE(&sd->sd_wu_defq, wu, swu_link);
while ((wu = TAILQ_FIRST(&sd->sd_wu)) != NULL) {
TAILQ_REMOVE(&sd->sd_wu, wu, swu_next);
free(wu, M_DEVBUF, sd->sd_wu_size);
}
}
void *
sr_wu_get(void *xsd)
{
struct sr_discipline *sd = (struct sr_discipline *)xsd;
struct sr_workunit *wu;
mtx_enter(&sd->sd_wu_mtx);
wu = TAILQ_FIRST(&sd->sd_wu_freeq);
if (wu) {
TAILQ_REMOVE(&sd->sd_wu_freeq, wu, swu_link);
sd->sd_wu_pending++;
}
mtx_leave(&sd->sd_wu_mtx);
DNPRINTF(SR_D_WU, "%s: sr_wu_get: %p\n", DEVNAME(sd->sd_sc), wu);
return (wu);
}
void
sr_wu_put(void *xsd, void *xwu)
{
struct sr_discipline *sd = (struct sr_discipline *)xsd;
struct sr_workunit *wu = (struct sr_workunit *)xwu;
DNPRINTF(SR_D_WU, "%s: sr_wu_put: %p\n", DEVNAME(sd->sd_sc), wu);
sr_wu_release_ccbs(wu);
sr_wu_init(sd, wu);
mtx_enter(&sd->sd_wu_mtx);
TAILQ_INSERT_TAIL(&sd->sd_wu_freeq, wu, swu_link);
sd->sd_wu_pending--;
mtx_leave(&sd->sd_wu_mtx);
}
void
sr_wu_init(struct sr_discipline *sd, struct sr_workunit *wu)
{
int s;
s = splbio();
if (wu->swu_cb_active == 1)
panic("%s: sr_wu_init got active wu", DEVNAME(sd->sd_sc));
splx(s);
wu->swu_xs = NULL;
wu->swu_state = SR_WU_FREE;
wu->swu_flags = 0;
wu->swu_blk_start = 0;
wu->swu_blk_end = 0;
wu->swu_collider = NULL;
}
void
sr_wu_enqueue_ccb(struct sr_workunit *wu, struct sr_ccb *ccb)
{
struct sr_discipline *sd = wu->swu_dis;
int s;
s = splbio();
if (wu->swu_cb_active == 1)
panic("%s: sr_wu_enqueue_ccb got active wu",
DEVNAME(sd->sd_sc));
ccb->ccb_wu = wu;
wu->swu_io_count++;
TAILQ_INSERT_TAIL(&wu->swu_ccb, ccb, ccb_link);
splx(s);
}
void
sr_wu_release_ccbs(struct sr_workunit *wu)
{
struct sr_ccb *ccb;
/* Return all ccbs that are associated with this workunit. */
while ((ccb = TAILQ_FIRST(&wu->swu_ccb)) != NULL) {
TAILQ_REMOVE(&wu->swu_ccb, ccb, ccb_link);
sr_ccb_put(ccb);
}
wu->swu_io_count = 0;
wu->swu_ios_complete = 0;
wu->swu_ios_failed = 0;
wu->swu_ios_succeeded = 0;
}
void
sr_wu_done(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
DNPRINTF(SR_D_INTR, "%s: sr_wu_done count %d completed %d failed %d\n",
DEVNAME(sd->sd_sc), wu->swu_io_count, wu->swu_ios_complete,
wu->swu_ios_failed);
if (wu->swu_ios_complete < wu->swu_io_count)
return;
task_add(sd->sd_taskq, &wu->swu_task);
}
void
sr_wu_done_callback(void *xwu)
{
struct sr_workunit *wu = xwu;
struct sr_discipline *sd = wu->swu_dis;
struct scsi_xfer *xs = wu->swu_xs;
struct sr_workunit *wup;
int s;
/*
* The SR_WUF_DISCIPLINE or SR_WUF_REBUILD flag must be set if
* the work unit is not associated with a scsi_xfer.
*/
KASSERT(xs != NULL ||
(wu->swu_flags & (SR_WUF_DISCIPLINE|SR_WUF_REBUILD)));
s = splbio();
if (xs != NULL) {
if (wu->swu_ios_failed)
xs->error = XS_DRIVER_STUFFUP;
else
xs->error = XS_NOERROR;
}
if (sd->sd_scsi_wu_done) {
if (sd->sd_scsi_wu_done(wu) == SR_WU_RESTART)
goto done;
}
/* Remove work unit from pending queue. */
TAILQ_FOREACH(wup, &sd->sd_wu_pendq, swu_link)
if (wup == wu)
break;
if (wup == NULL)
panic("%s: wu %p not on pending queue",
DEVNAME(sd->sd_sc), wu);
TAILQ_REMOVE(&sd->sd_wu_pendq, wu, swu_link);
if (wu->swu_collider) {
if (wu->swu_ios_failed)
sr_raid_recreate_wu(wu->swu_collider);
/* XXX Should the collider be failed if this xs failed? */
sr_raid_startwu(wu->swu_collider);
}
/*
* If a discipline provides its own sd_scsi_done function, then it
* is responsible for calling sr_scsi_done() once I/O is complete.
*/
if (wu->swu_flags & SR_WUF_REBUILD)
wu->swu_flags |= SR_WUF_REBUILDIOCOMP;
if (wu->swu_flags & SR_WUF_WAKEUP)
wakeup(wu);
if (sd->sd_scsi_done)
sd->sd_scsi_done(wu);
else if (wu->swu_flags & SR_WUF_DISCIPLINE)
sr_scsi_wu_put(sd, wu);
else if (!(wu->swu_flags & SR_WUF_REBUILD))
sr_scsi_done(sd, xs);
done:
splx(s);
}
struct sr_workunit *
sr_scsi_wu_get(struct sr_discipline *sd, int flags)
{
return scsi_io_get(&sd->sd_iopool, flags);
}
void
sr_scsi_wu_put(struct sr_discipline *sd, struct sr_workunit *wu)
{
scsi_io_put(&sd->sd_iopool, wu);
if (sd->sd_sync && sd->sd_wu_pending == 0)
wakeup(sd);
}
void
sr_scsi_done(struct sr_discipline *sd, struct scsi_xfer *xs)
{
DNPRINTF(SR_D_DIS, "%s: sr_scsi_done: xs %p\n", DEVNAME(sd->sd_sc), xs);
if (xs->error == XS_NOERROR)
xs->resid = 0;
scsi_done(xs);
if (sd->sd_sync && sd->sd_wu_pending == 0)
wakeup(sd);
}
void
sr_scsi_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct sr_softc *sc = link->bus->sb_adapter_softc;
struct sr_workunit *wu = xs->io;
struct sr_discipline *sd;
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd target %d xs %p flags %#x\n",
DEVNAME(sc), link->target, xs, xs->flags);
sd = sc->sc_targets[link->target];
if (sd == NULL)
panic("%s: sr_scsi_cmd NULL discipline", DEVNAME(sc));
if (sd->sd_deleted) {
printf("%s: %s device is being deleted, failing io\n",
DEVNAME(sc), sd->sd_meta->ssd_devname);
goto stuffup;
}
/* scsi layer *can* re-send wu without calling sr_wu_put(). */
sr_wu_release_ccbs(wu);
sr_wu_init(sd, wu);
wu->swu_state = SR_WU_INPROGRESS;
wu->swu_xs = xs;
switch (xs->cmd.opcode) {
case READ_COMMAND:
case READ_10:
case READ_16:
case WRITE_COMMAND:
case WRITE_10:
case WRITE_16:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd: READ/WRITE %02x\n",
DEVNAME(sc), xs->cmd.opcode);
if (sd->sd_scsi_rw(wu))
goto stuffup;
break;
case SYNCHRONIZE_CACHE:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd: SYNCHRONIZE_CACHE\n",
DEVNAME(sc));
if (sd->sd_scsi_sync(wu))
goto stuffup;
goto complete;
case TEST_UNIT_READY:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd: TEST_UNIT_READY\n",
DEVNAME(sc));
if (sd->sd_scsi_tur(wu))
goto stuffup;
goto complete;
case START_STOP:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd: START_STOP\n",
DEVNAME(sc));
if (sd->sd_scsi_start_stop(wu))
goto stuffup;
goto complete;
case INQUIRY:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd: INQUIRY\n",
DEVNAME(sc));
if (sd->sd_scsi_inquiry(wu))
goto stuffup;
goto complete;
case READ_CAPACITY:
case READ_CAPACITY_16:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd READ CAPACITY 0x%02x\n",
DEVNAME(sc), xs->cmd.opcode);
if (sd->sd_scsi_read_cap(wu))
goto stuffup;
goto complete;
case REQUEST_SENSE:
DNPRINTF(SR_D_CMD, "%s: sr_scsi_cmd REQUEST SENSE\n",
DEVNAME(sc));
if (sd->sd_scsi_req_sense(wu))
goto stuffup;
goto complete;
default:
DNPRINTF(SR_D_CMD, "%s: unsupported scsi command %x\n",
DEVNAME(sc), xs->cmd.opcode);
/* XXX might need to add generic function to handle others */
goto stuffup;
}
return;
stuffup:
if (sd->sd_scsi_sense.error_code) {
xs->error = XS_SENSE;
memcpy(&xs->sense, &sd->sd_scsi_sense, sizeof(xs->sense));
bzero(&sd->sd_scsi_sense, sizeof(sd->sd_scsi_sense));
} else {
xs->error = XS_DRIVER_STUFFUP;
}
complete:
sr_scsi_done(sd, xs);
}
int
sr_scsi_probe(struct scsi_link *link)
{
struct sr_softc *sc = link->bus->sb_adapter_softc;
struct sr_discipline *sd;
KASSERT(link->target < SR_MAX_LD && link->lun == 0);
sd = sc->sc_targets[link->target];
if (sd == NULL)
return (ENODEV);
link->pool = &sd->sd_iopool;
if (sd->sd_openings)
link->openings = sd->sd_openings(sd);
else
link->openings = sd->sd_max_wu;
return (0);
}
int
sr_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
{
struct sr_softc *sc = link->bus->sb_adapter_softc;
struct sr_discipline *sd;
sd = sc->sc_targets[link->target];
if (sd == NULL)
return (ENODEV);
DNPRINTF(SR_D_IOCTL, "%s: %s sr_scsi_ioctl cmd: %#lx\n",
DEVNAME(sc), sd->sd_meta->ssd_devname, cmd);
/* Pass bio ioctls through to the bio handler. */
if (IOCGROUP(cmd) == 'B')
return (sr_bio_handler(sc, sd, cmd, (struct bio *)addr));
switch (cmd) {
case DIOCGCACHE:
case DIOCSCACHE:
return (EOPNOTSUPP);
default:
return (ENOTTY);
}
}
int
sr_bio_ioctl(struct device *dev, u_long cmd, caddr_t addr)
{
struct sr_softc *sc = (struct sr_softc *) dev;
DNPRINTF(SR_D_IOCTL, "%s: sr_bio_ioctl\n", DEVNAME(sc));
return sr_bio_handler(sc, NULL, cmd, (struct bio *)addr);
}
int
sr_bio_handler(struct sr_softc *sc, struct sr_discipline *sd, u_long cmd,
struct bio *bio)
{
int rv = 0;
DNPRINTF(SR_D_IOCTL, "%s: sr_bio_handler ", DEVNAME(sc));
rw_enter_write(&sc->sc_lock);
bio_status_init(&sc->sc_status, &sc->sc_dev);
switch (cmd) {
case BIOCINQ:
DNPRINTF(SR_D_IOCTL, "inq\n");
rv = sr_ioctl_inq(sc, (struct bioc_inq *)bio);
break;
case BIOCVOL:
DNPRINTF(SR_D_IOCTL, "vol\n");
rv = sr_ioctl_vol(sc, (struct bioc_vol *)bio);
break;
case BIOCDISK:
DNPRINTF(SR_D_IOCTL, "disk\n");
rv = sr_ioctl_disk(sc, (struct bioc_disk *)bio);
break;
case BIOCALARM:
DNPRINTF(SR_D_IOCTL, "alarm\n");
/*rv = sr_ioctl_alarm(sc, (struct bioc_alarm *)bio); */
break;
case BIOCBLINK:
DNPRINTF(SR_D_IOCTL, "blink\n");
/*rv = sr_ioctl_blink(sc, (struct bioc_blink *)bio); */
break;
case BIOCSETSTATE:
DNPRINTF(SR_D_IOCTL, "setstate\n");
rv = sr_ioctl_setstate(sc, (struct bioc_setstate *)bio);
break;
case BIOCCREATERAID:
DNPRINTF(SR_D_IOCTL, "createraid\n");
rv = sr_ioctl_createraid(sc, (struct bioc_createraid *)bio,
1, NULL);
break;
case BIOCDELETERAID:
DNPRINTF(SR_D_IOCTL, "deleteraid\n");
rv = sr_ioctl_deleteraid(sc, sd, (struct bioc_deleteraid *)bio);
break;
case BIOCDISCIPLINE:
DNPRINTF(SR_D_IOCTL, "discipline\n");
rv = sr_ioctl_discipline(sc, sd, (struct bioc_discipline *)bio);
break;
case BIOCINSTALLBOOT:
DNPRINTF(SR_D_IOCTL, "installboot\n");
rv = sr_ioctl_installboot(sc, sd,
(struct bioc_installboot *)bio);
break;
default:
DNPRINTF(SR_D_IOCTL, "invalid ioctl\n");
rv = ENOTTY;
}
sc->sc_status.bs_status = (rv ? BIO_STATUS_ERROR : BIO_STATUS_SUCCESS);
if (sc->sc_status.bs_msg_count > 0)
rv = 0;
memcpy(&bio->bio_status, &sc->sc_status, sizeof(struct bio_status));
rw_exit_write(&sc->sc_lock);
return (rv);
}
int
sr_ioctl_inq(struct sr_softc *sc, struct bioc_inq *bi)
{
struct sr_discipline *sd;
int vol = 0, disk = 0;
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
vol++;
disk += sd->sd_meta->ssdi.ssd_chunk_no;
}
strlcpy(bi->bi_dev, sc->sc_dev.dv_xname, sizeof(bi->bi_dev));
bi->bi_novol = vol + sc->sc_hotspare_no;
bi->bi_nodisk = disk + sc->sc_hotspare_no;
return (0);
}
int
sr_ioctl_vol(struct sr_softc *sc, struct bioc_vol *bv)
{
int vol = -1, rv = EINVAL;
struct sr_discipline *sd;
struct sr_chunk *hotspare;
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
vol++;
if (vol != bv->bv_volid)
continue;
bv->bv_status = sd->sd_vol_status;
bv->bv_size = sd->sd_meta->ssdi.ssd_size << DEV_BSHIFT;
bv->bv_level = sd->sd_meta->ssdi.ssd_level;
bv->bv_nodisk = sd->sd_meta->ssdi.ssd_chunk_no;
#ifdef CRYPTO
if (sd->sd_meta->ssdi.ssd_level == 'C' &&
sd->mds.mdd_crypto.key_disk != NULL)
bv->bv_nodisk++;
else if (sd->sd_meta->ssdi.ssd_level == 0x1C &&
sd->mds.mdd_raid1c.sr1c_crypto.key_disk != NULL)
bv->bv_nodisk++;
#endif
if (bv->bv_status == BIOC_SVREBUILD)
bv->bv_percent = sr_rebuild_percent(sd);
strlcpy(bv->bv_dev, sd->sd_meta->ssd_devname,
sizeof(bv->bv_dev));
strlcpy(bv->bv_vendor, sd->sd_meta->ssdi.ssd_vendor,
sizeof(bv->bv_vendor));
rv = 0;
goto done;
}
/* Check hotspares list. */
SLIST_FOREACH(hotspare, &sc->sc_hotspare_list, src_link) {
vol++;
if (vol != bv->bv_volid)
continue;
bv->bv_status = BIOC_SVONLINE;
bv->bv_size = hotspare->src_meta.scmi.scm_size << DEV_BSHIFT;
bv->bv_level = -1; /* Hotspare. */
bv->bv_nodisk = 1;
strlcpy(bv->bv_dev, hotspare->src_meta.scmi.scm_devname,
sizeof(bv->bv_dev));
strlcpy(bv->bv_vendor, hotspare->src_meta.scmi.scm_devname,
sizeof(bv->bv_vendor));
rv = 0;
goto done;
}
done:
return (rv);
}
int
sr_ioctl_disk(struct sr_softc *sc, struct bioc_disk *bd)
{
struct sr_discipline *sd;
struct sr_chunk *src, *hotspare;
int vol = -1, rv = EINVAL;
if (bd->bd_diskid < 0)
goto done;
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
vol++;
if (vol != bd->bd_volid)
continue;
if (bd->bd_diskid < sd->sd_meta->ssdi.ssd_chunk_no)
src = sd->sd_vol.sv_chunks[bd->bd_diskid];
#ifdef CRYPTO
else if (bd->bd_diskid == sd->sd_meta->ssdi.ssd_chunk_no &&
sd->sd_meta->ssdi.ssd_level == 'C' &&
sd->mds.mdd_crypto.key_disk != NULL)
src = sd->mds.mdd_crypto.key_disk;
else if (bd->bd_diskid == sd->sd_meta->ssdi.ssd_chunk_no &&
sd->sd_meta->ssdi.ssd_level == 0x1C &&
sd->mds.mdd_raid1c.sr1c_crypto.key_disk != NULL)
src = sd->mds.mdd_crypto.key_disk;
#endif
else
break;
bd->bd_status = src->src_meta.scm_status;
bd->bd_size = src->src_meta.scmi.scm_size << DEV_BSHIFT;
bd->bd_channel = vol;
bd->bd_target = bd->bd_diskid;
strlcpy(bd->bd_vendor, src->src_meta.scmi.scm_devname,
sizeof(bd->bd_vendor));
rv = 0;
goto done;
}
/* Check hotspares list. */
SLIST_FOREACH(hotspare, &sc->sc_hotspare_list, src_link) {
vol++;
if (vol != bd->bd_volid)
continue;
if (bd->bd_diskid != 0)
break;
bd->bd_status = hotspare->src_meta.scm_status;
bd->bd_size = hotspare->src_meta.scmi.scm_size << DEV_BSHIFT;
bd->bd_channel = vol;
bd->bd_target = bd->bd_diskid;
strlcpy(bd->bd_vendor, hotspare->src_meta.scmi.scm_devname,
sizeof(bd->bd_vendor));
rv = 0;
goto done;
}
done:
return (rv);
}
int
sr_ioctl_setstate(struct sr_softc *sc, struct bioc_setstate *bs)
{
int rv = EINVAL;
int vol = -1, found, c;
struct sr_discipline *sd;
struct sr_chunk *ch_entry;
struct sr_chunk_head *cl;
if (bs->bs_other_id_type == BIOC_SSOTHER_UNUSED)
goto done;
if (bs->bs_status == BIOC_SSHOTSPARE) {
rv = sr_hotspare(sc, (dev_t)bs->bs_other_id);
goto done;
}
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
vol++;
if (vol == bs->bs_volid)
break;
}
if (sd == NULL)
goto done;
switch (bs->bs_status) {
case BIOC_SSOFFLINE:
/* Take chunk offline */
found = c = 0;
cl = &sd->sd_vol.sv_chunk_list;
SLIST_FOREACH(ch_entry, cl, src_link) {
if (ch_entry->src_dev_mm == bs->bs_other_id) {
found = 1;
break;
}
c++;
}
if (found == 0) {
sr_error(sc, "chunk not part of array");
goto done;
}
/* XXX: check current state first */
sd->sd_set_chunk_state(sd, c, BIOC_SDOFFLINE);
if (sr_meta_save(sd, SR_META_DIRTY)) {
sr_error(sc, "could not save metadata for %s",
sd->sd_meta->ssd_devname);
goto done;
}
rv = 0;
break;
case BIOC_SDSCRUB:
break;
case BIOC_SSREBUILD:
rv = sr_rebuild_init(sd, (dev_t)bs->bs_other_id, 0);
break;
default:
sr_error(sc, "unsupported state request %d", bs->bs_status);
}
done:
return (rv);
}
int
sr_chunk_in_use(struct sr_softc *sc, dev_t dev)
{
struct sr_discipline *sd;
struct sr_chunk *chunk;
int i;
DNPRINTF(SR_D_MISC, "%s: sr_chunk_in_use(%d)\n", DEVNAME(sc), dev);
if (dev == NODEV)
return BIOC_SDINVALID;
/* See if chunk is already in use. */
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
for (i = 0; i < sd->sd_meta->ssdi.ssd_chunk_no; i++) {
chunk = sd->sd_vol.sv_chunks[i];
if (chunk->src_dev_mm == dev)
return chunk->src_meta.scm_status;
}
}
/* Check hotspares list. */
SLIST_FOREACH(chunk, &sc->sc_hotspare_list, src_link)
if (chunk->src_dev_mm == dev)
return chunk->src_meta.scm_status;
return BIOC_SDINVALID;
}
int
sr_hotspare(struct sr_softc *sc, dev_t dev)
{
struct sr_discipline *sd = NULL;
struct sr_metadata *sm = NULL;
struct sr_meta_chunk *hm;
struct sr_chunk_head *cl;
struct sr_chunk *chunk, *last, *hotspare = NULL;
struct sr_uuid uuid;
struct disklabel label;
struct vnode *vn;
u_int64_t size;
char devname[32];
int rv = EINVAL;
int c, part, open = 0;
/*
* Add device to global hotspares list.
*/
sr_meta_getdevname(sc, dev, devname, sizeof(devname));
/* Make sure chunk is not already in use. */
c = sr_chunk_in_use(sc, dev);
if (c != BIOC_SDINVALID && c != BIOC_SDOFFLINE) {
if (c == BIOC_SDHOTSPARE)
sr_error(sc, "%s is already a hotspare", devname);
else
sr_error(sc, "%s is already in use", devname);
goto done;
}
/* XXX - See if there is an existing degraded volume... */
/* Open device. */
if (bdevvp(dev, &vn)) {
sr_error(sc, "sr_hotspare: cannot allocate vnode");
goto done;
}
if (VOP_OPEN(vn, FREAD | FWRITE, NOCRED, curproc)) {
DNPRINTF(SR_D_META,"%s: sr_hotspare cannot open %s\n",
DEVNAME(sc), devname);
vput(vn);
goto fail;
}
open = 1; /* close dev on error */
/* Get partition details. */
part = DISKPART(dev);
if (VOP_IOCTL(vn, DIOCGDINFO, (caddr_t)&label, FREAD,
NOCRED, curproc)) {
DNPRINTF(SR_D_META, "%s: sr_hotspare ioctl failed\n",
DEVNAME(sc));
goto fail;
}
if (label.d_partitions[part].p_fstype != FS_RAID) {
sr_error(sc, "%s partition not of type RAID (%d)",
devname, label.d_partitions[part].p_fstype);
goto fail;
}
/* Calculate partition size. */
size = DL_SECTOBLK(&label, DL_GETPSIZE(&label.d_partitions[part]));
if (size <= SR_DATA_OFFSET) {
DNPRINTF(SR_D_META, "%s: %s partition too small\n", DEVNAME(sc),
devname);
goto fail;
}
size -= SR_DATA_OFFSET;
if (size > INT64_MAX) {
DNPRINTF(SR_D_META, "%s: %s partition too large\n", DEVNAME(sc),
devname);
goto fail;
}
/*
* Create and populate chunk metadata.
*/
sr_uuid_generate(&uuid);
hotspare = malloc(sizeof(struct sr_chunk), M_DEVBUF, M_WAITOK | M_ZERO);
hotspare->src_dev_mm = dev;
hotspare->src_vn = vn;
strlcpy(hotspare->src_devname, devname, sizeof(hm->scmi.scm_devname));
hotspare->src_size = size;
hm = &hotspare->src_meta;
hm->scmi.scm_volid = SR_HOTSPARE_VOLID;
hm->scmi.scm_chunk_id = 0;
hm->scmi.scm_size = size;
hm->scmi.scm_coerced_size = size;
strlcpy(hm->scmi.scm_devname, devname, sizeof(hm->scmi.scm_devname));
memcpy(&hm->scmi.scm_uuid, &uuid, sizeof(struct sr_uuid));
sr_checksum(sc, hm, &hm->scm_checksum,
sizeof(struct sr_meta_chunk_invariant));
hm->scm_status = BIOC_SDHOTSPARE;
/*
* Create and populate our own discipline and metadata.
*/
sm = malloc(sizeof(struct sr_metadata), M_DEVBUF, M_WAITOK | M_ZERO);
sm->ssdi.ssd_magic = SR_MAGIC;
sm->ssdi.ssd_version = SR_META_VERSION;
sm->ssd_ondisk = 0;
sm->ssdi.ssd_vol_flags = 0;
memcpy(&sm->ssdi.ssd_uuid, &uuid, sizeof(struct sr_uuid));
sm->ssdi.ssd_chunk_no = 1;
sm->ssdi.ssd_volid = SR_HOTSPARE_VOLID;
sm->ssdi.ssd_level = SR_HOTSPARE_LEVEL;
sm->ssdi.ssd_size = size;
sm->ssdi.ssd_secsize = label.d_secsize;
strlcpy(sm->ssdi.ssd_vendor, "OPENBSD", sizeof(sm->ssdi.ssd_vendor));
snprintf(sm->ssdi.ssd_product, sizeof(sm->ssdi.ssd_product),
"SR %s", "HOTSPARE");
snprintf(sm->ssdi.ssd_revision, sizeof(sm->ssdi.ssd_revision),
"%03d", SR_META_VERSION);
sd = malloc(sizeof(struct sr_discipline), M_DEVBUF, M_WAITOK | M_ZERO);
sd->sd_sc = sc;
sd->sd_meta = sm;
sd->sd_meta_type = SR_META_F_NATIVE;
sd->sd_vol_status = BIOC_SVONLINE;
strlcpy(sd->sd_name, "HOTSPARE", sizeof(sd->sd_name));
SLIST_INIT(&sd->sd_meta_opt);
/* Add chunk to volume. */
sd->sd_vol.sv_chunks = malloc(sizeof(struct sr_chunk *), M_DEVBUF,
M_WAITOK | M_ZERO);
sd->sd_vol.sv_chunks[0] = hotspare;
SLIST_INIT(&sd->sd_vol.sv_chunk_list);
SLIST_INSERT_HEAD(&sd->sd_vol.sv_chunk_list, hotspare, src_link);
/* Save metadata. */
if (sr_meta_save(sd, SR_META_DIRTY)) {
sr_error(sc, "could not save metadata to %s", devname);
goto fail;
}
/*
* Add chunk to hotspare list.
*/
rw_enter_write(&sc->sc_hs_lock);
cl = &sc->sc_hotspare_list;
if (SLIST_EMPTY(cl))
SLIST_INSERT_HEAD(cl, hotspare, src_link);
else {
SLIST_FOREACH(chunk, cl, src_link)
last = chunk;
SLIST_INSERT_AFTER(last, hotspare, src_link);
}
sc->sc_hotspare_no++;
rw_exit_write(&sc->sc_hs_lock);
rv = 0;
goto done;
fail:
free(hotspare, M_DEVBUF, sizeof(*hotspare));
done:
if (sd)
free(sd->sd_vol.sv_chunks, M_DEVBUF,
sizeof(sd->sd_vol.sv_chunks));
free(sd, M_DEVBUF, sizeof(*sd));
free(sm, M_DEVBUF, sizeof(*sm));
if (open) {
VOP_CLOSE(vn, FREAD | FWRITE, NOCRED, curproc);
vput(vn);
}
return (rv);
}
void
sr_hotspare_rebuild_callback(void *xsd)
{
struct sr_discipline *sd = xsd;
sr_hotspare_rebuild(sd);
}
void
sr_hotspare_rebuild(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk_head *cl;
struct sr_chunk *hotspare, *chunk = NULL;
struct sr_workunit *wu;
struct sr_ccb *ccb;
int i, s, cid, busy;
/*
* Attempt to locate a hotspare and initiate rebuild.
*/
/* Find first offline chunk. */
for (cid = 0; cid < sd->sd_meta->ssdi.ssd_chunk_no; cid++) {
if (sd->sd_vol.sv_chunks[cid]->src_meta.scm_status ==
BIOC_SDOFFLINE) {
chunk = sd->sd_vol.sv_chunks[cid];
break;
}
}
if (chunk == NULL) {
printf("%s: no offline chunk found on %s!\n",
DEVNAME(sc), sd->sd_meta->ssd_devname);
return;
}
/* See if we have a suitable hotspare... */
rw_enter_write(&sc->sc_hs_lock);
cl = &sc->sc_hotspare_list;
SLIST_FOREACH(hotspare, cl, src_link)
if (hotspare->src_size >= chunk->src_size &&
hotspare->src_secsize <= sd->sd_meta->ssdi.ssd_secsize)
break;
if (hotspare != NULL) {
printf("%s: %s volume degraded, will attempt to "
"rebuild on hotspare %s\n", DEVNAME(sc),
sd->sd_meta->ssd_devname, hotspare->src_devname);
/*
* Ensure that all pending I/O completes on the failed chunk
* before trying to initiate a rebuild.
*/
i = 0;
do {
busy = 0;
s = splbio();
TAILQ_FOREACH(wu, &sd->sd_wu_pendq, swu_link) {
TAILQ_FOREACH(ccb, &wu->swu_ccb, ccb_link) {
if (ccb->ccb_target == cid)
busy = 1;
}
}
TAILQ_FOREACH(wu, &sd->sd_wu_defq, swu_link) {
TAILQ_FOREACH(ccb, &wu->swu_ccb, ccb_link) {
if (ccb->ccb_target == cid)
busy = 1;
}
}
splx(s);
if (busy) {
tsleep_nsec(sd, PRIBIO, "sr_hotspare",
SEC_TO_NSEC(1));
i++;
}
} while (busy && i < 120);
DNPRINTF(SR_D_META, "%s: waited %i seconds for I/O to "
"complete on failed chunk %s\n", DEVNAME(sc),
i, chunk->src_devname);
if (busy) {
printf("%s: pending I/O failed to complete on "
"failed chunk %s, hotspare rebuild aborted...\n",
DEVNAME(sc), chunk->src_devname);
goto done;
}
s = splbio();
rw_enter_write(&sc->sc_lock);
bio_status_init(&sc->sc_status, &sc->sc_dev);
if (sr_rebuild_init(sd, hotspare->src_dev_mm, 1) == 0) {
/* Remove hotspare from available list. */
sc->sc_hotspare_no--;
SLIST_REMOVE(cl, hotspare, sr_chunk, src_link);
free(hotspare, M_DEVBUF, sizeof(*hotspare));
}
rw_exit_write(&sc->sc_lock);
splx(s);
}
done:
rw_exit_write(&sc->sc_hs_lock);
}
int
sr_rebuild_init(struct sr_discipline *sd, dev_t dev, int hotspare)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk *chunk = NULL;
struct sr_meta_chunk *meta;
struct disklabel label;
struct vnode *vn;
u_int64_t size;
int64_t csize;
char devname[32];
int rv = EINVAL, open = 0;
int cid, i, part, status;
/*
* Attempt to initiate a rebuild onto the specified device.
*/
if (!(sd->sd_capabilities & SR_CAP_REBUILD)) {
sr_error(sc, "discipline does not support rebuild");
goto done;
}
/* make sure volume is in the right state */
if (sd->sd_vol_status == BIOC_SVREBUILD) {
sr_error(sc, "rebuild already in progress");
goto done;
}
if (sd->sd_vol_status != BIOC_SVDEGRADED) {
sr_error(sc, "volume not degraded");
goto done;
}
/* Find first offline chunk. */
for (cid = 0; cid < sd->sd_meta->ssdi.ssd_chunk_no; cid++) {
if (sd->sd_vol.sv_chunks[cid]->src_meta.scm_status ==
BIOC_SDOFFLINE) {
chunk = sd->sd_vol.sv_chunks[cid];
break;
}
}
if (chunk == NULL) {
sr_error(sc, "no offline chunks available to rebuild");
goto done;
}
/* Get coerced size from another online chunk. */
csize = 0;
for (i = 0; i < sd->sd_meta->ssdi.ssd_chunk_no; i++) {
if (sd->sd_vol.sv_chunks[i]->src_meta.scm_status ==
BIOC_SDONLINE) {
meta = &sd->sd_vol.sv_chunks[i]->src_meta;
csize = meta->scmi.scm_coerced_size;
break;
}
}
if (csize == 0) {
sr_error(sc, "no online chunks available for rebuild");
goto done;
}
sr_meta_getdevname(sc, dev, devname, sizeof(devname));
if (bdevvp(dev, &vn)) {
printf("%s: sr_rebuild_init: can't allocate vnode\n",
DEVNAME(sc));
goto done;
}
if (VOP_OPEN(vn, FREAD | FWRITE, NOCRED, curproc)) {
DNPRINTF(SR_D_META,"%s: sr_ioctl_setstate can't "
"open %s\n", DEVNAME(sc), devname);
vput(vn);
goto done;
}
open = 1; /* close dev on error */
/* Get disklabel and check partition. */
part = DISKPART(dev);
if (VOP_IOCTL(vn, DIOCGDINFO, (caddr_t)&label, FREAD,
NOCRED, curproc)) {
DNPRINTF(SR_D_META, "%s: sr_ioctl_setstate ioctl failed\n",
DEVNAME(sc));
goto done;
}
if (label.d_partitions[part].p_fstype != FS_RAID) {
sr_error(sc, "%s partition not of type RAID (%d)",
devname, label.d_partitions[part].p_fstype);
goto done;
}
/* Is the partition large enough? */
size = DL_SECTOBLK(&label, DL_GETPSIZE(&label.d_partitions[part]));
if (size <= sd->sd_meta->ssd_data_blkno) {
sr_error(sc, "%s: %s partition too small", DEVNAME(sc),
devname);
goto done;
}
size -= sd->sd_meta->ssd_data_blkno;
if (size > INT64_MAX) {
sr_error(sc, "%s: %s partition too large", DEVNAME(sc),
devname);
goto done;
}
if (size < csize) {
sr_error(sc, "%s partition too small, at least %lld bytes "
"required", devname, (long long)(csize << DEV_BSHIFT));
goto done;
} else if (size > csize)
sr_warn(sc, "%s partition too large, wasting %lld bytes",
devname, (long long)((size - csize) << DEV_BSHIFT));
if (label.d_secsize > sd->sd_meta->ssdi.ssd_secsize) {
sr_error(sc, "%s sector size too large, <= %u bytes "
"required", devname, sd->sd_meta->ssdi.ssd_secsize);
goto done;
}
/* Ensure that this chunk is not already in use. */
status = sr_chunk_in_use(sc, dev);
if (status != BIOC_SDINVALID && status != BIOC_SDOFFLINE &&
!(hotspare && status == BIOC_SDHOTSPARE)) {
sr_error(sc, "%s is already in use", devname);
goto done;
}
/* Reset rebuild counter since we rebuilding onto a new chunk. */
sd->sd_meta->ssd_rebuild = 0;
open = 0; /* leave dev open from here on out */
/* Fix up chunk. */
memcpy(chunk->src_duid, label.d_uid, sizeof(chunk->src_duid));
chunk->src_dev_mm = dev;
chunk->src_vn = vn;
/* Reconstruct metadata. */
meta = &chunk->src_meta;
meta->scmi.scm_volid = sd->sd_meta->ssdi.ssd_volid;
meta->scmi.scm_chunk_id = cid;
strlcpy(meta->scmi.scm_devname, devname,
sizeof(meta->scmi.scm_devname));
meta->scmi.scm_size = size;
meta->scmi.scm_coerced_size = csize;
memcpy(&meta->scmi.scm_uuid, &sd->sd_meta->ssdi.ssd_uuid,
sizeof(meta->scmi.scm_uuid));
sr_checksum(sc, meta, &meta->scm_checksum,
sizeof(struct sr_meta_chunk_invariant));
sd->sd_set_chunk_state(sd, cid, BIOC_SDREBUILD);
if (sr_meta_save(sd, SR_META_DIRTY)) {
sr_error(sc, "could not save metadata to %s", devname);
open = 1;
goto done;
}
sr_warn(sc, "rebuild of %s started on %s",
sd->sd_meta->ssd_devname, devname);
sd->sd_reb_abort = 0;
kthread_create_deferred(sr_rebuild_start, sd);
rv = 0;
done:
if (open) {
VOP_CLOSE(vn, FREAD | FWRITE, NOCRED, curproc);
vput(vn);
}
return (rv);
}
int
sr_rebuild_percent(struct sr_discipline *sd)
{
daddr_t rb, sz;
sz = sd->sd_meta->ssdi.ssd_size;
rb = sd->sd_meta->ssd_rebuild;
if (rb > 0)
return (100 - ((sz * 100 - rb * 100) / sz) - 1);
return (0);
}
void
sr_roam_chunks(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_chunk *chunk;
struct sr_meta_chunk *meta;
int roamed = 0;
/* Have any chunks roamed? */
SLIST_FOREACH(chunk, &sd->sd_vol.sv_chunk_list, src_link) {
meta = &chunk->src_meta;
if (strncmp(meta->scmi.scm_devname, chunk->src_devname,
sizeof(meta->scmi.scm_devname))) {
printf("%s: roaming device %s -> %s\n", DEVNAME(sc),
meta->scmi.scm_devname, chunk->src_devname);
strlcpy(meta->scmi.scm_devname, chunk->src_devname,
sizeof(meta->scmi.scm_devname));
roamed++;
}
}
if (roamed)
sr_meta_save(sd, SR_META_DIRTY);
}
int
sr_ioctl_createraid(struct sr_softc *sc, struct bioc_createraid *bc,
int user, void *data)
{
struct sr_meta_opt_item *omi;
struct sr_chunk_head *cl;
struct sr_discipline *sd = NULL;
struct sr_chunk *ch_entry;
struct scsi_link *link;
struct device *dev;
char *uuid, devname[32];
dev_t *dt = NULL;
int i, no_chunk, rv = EINVAL, target, vol;
int no_meta;
DNPRINTF(SR_D_IOCTL, "%s: sr_ioctl_createraid(%d)\n",
DEVNAME(sc), user);
/* user input */
if (bc->bc_dev_list_len > BIOC_CRMAXLEN)
goto unwind;
dt = malloc(bc->bc_dev_list_len, M_DEVBUF, M_WAITOK | M_ZERO);
if (user) {
if (copyin(bc->bc_dev_list, dt, bc->bc_dev_list_len) != 0)
goto unwind;
} else
memcpy(dt, bc->bc_dev_list, bc->bc_dev_list_len);
/* Initialise discipline. */
sd = malloc(sizeof(struct sr_discipline), M_DEVBUF, M_WAITOK | M_ZERO);
sd->sd_sc = sc;
SLIST_INIT(&sd->sd_meta_opt);
sd->sd_taskq = taskq_create("srdis", 1, IPL_BIO, 0);
if (sd->sd_taskq == NULL) {
sr_error(sc, "could not create discipline taskq");
goto unwind;
}
if (sr_discipline_init(sd, bc->bc_level)) {
sr_error(sc, "could not initialize discipline");
goto unwind;
}
no_chunk = bc->bc_dev_list_len / sizeof(dev_t);
cl = &sd->sd_vol.sv_chunk_list;
SLIST_INIT(cl);
/* Ensure that chunks are not already in use. */
for (i = 0; i < no_chunk; i++) {
if (sr_chunk_in_use(sc, dt[i]) != BIOC_SDINVALID) {
sr_meta_getdevname(sc, dt[i], devname, sizeof(devname));
sr_error(sc, "chunk %s already in use", devname);
goto unwind;
}
}
sd->sd_meta_type = sr_meta_probe(sd, dt, no_chunk);
if (sd->sd_meta_type == SR_META_F_INVALID) {
sr_error(sc, "invalid metadata format");
goto unwind;
}
if (sr_meta_attach(sd, no_chunk, bc->bc_flags & BIOC_SCFORCE))
goto unwind;
/* force the raid volume by clearing metadata region */
if (bc->bc_flags & BIOC_SCFORCE) {
/* make sure disk isn't up and running */
if (sr_meta_read(sd))
if (sr_already_assembled(sd)) {
uuid = sr_uuid_format(
&sd->sd_meta->ssdi.ssd_uuid);
sr_error(sc, "disk %s is currently in use; "
"cannot force create", uuid);
free(uuid, M_DEVBUF, 37);
goto unwind;
}
if (sr_meta_clear(sd)) {
sr_error(sc, "failed to clear metadata");
goto unwind;
}
}
no_meta = sr_meta_read(sd);
if (no_meta == -1) {
/* Corrupt metadata on one or more chunks. */
sr_error(sc, "one of the chunks has corrupt metadata; "
"aborting assembly");
goto unwind;
} else if (no_meta == 0) {
/* Initialise volume and chunk metadata. */
sr_meta_init(sd, bc->bc_level, no_chunk);
sd->sd_vol_status = BIOC_SVONLINE;
sd->sd_meta_flags = bc->bc_flags & BIOC_SCNOAUTOASSEMBLE;
if (sd->sd_create) {
if ((i = sd->sd_create(sd, bc, no_chunk,
sd->sd_vol.sv_chunk_minsz))) {
rv = i;
goto unwind;
}
}
sr_meta_init_complete(sd);
DNPRINTF(SR_D_IOCTL,
"%s: sr_ioctl_createraid: vol_size: %lld\n",
DEVNAME(sc), sd->sd_meta->ssdi.ssd_size);
/* Warn if we've wasted chunk space due to coercing. */
if ((sd->sd_capabilities & SR_CAP_NON_COERCED) == 0 &&
sd->sd_vol.sv_chunk_minsz != sd->sd_vol.sv_chunk_maxsz)
sr_warn(sc, "chunk sizes are not equal; up to %llu "
"blocks wasted per chunk",
sd->sd_vol.sv_chunk_maxsz -
sd->sd_vol.sv_chunk_minsz);
} else {
/* Ensure we are assembling the correct # of chunks. */
if (bc->bc_level == 0x1C &&
sd->sd_meta->ssdi.ssd_chunk_no > no_chunk) {
sr_warn(sc, "trying to bring up %s degraded",
sd->sd_meta->ssd_devname);
} else if (sd->sd_meta->ssdi.ssd_chunk_no != no_chunk) {
sr_error(sc, "volume chunk count does not match metadata "
"chunk count");
goto unwind;
}
/* Ensure metadata level matches requested assembly level. */
if (sd->sd_meta->ssdi.ssd_level != bc->bc_level) {
sr_error(sc, "volume level does not match metadata "
"level");
goto unwind;
}
if (sr_already_assembled(sd)) {
uuid = sr_uuid_format(&sd->sd_meta->ssdi.ssd_uuid);
sr_error(sc, "disk %s already assembled", uuid);
free(uuid, M_DEVBUF, 37);
goto unwind;
}
if (user == 0 && sd->sd_meta_flags & BIOC_SCNOAUTOASSEMBLE) {
DNPRINTF(SR_D_META, "%s: disk not auto assembled from "
"metadata\n", DEVNAME(sc));
goto unwind;
}
if (no_meta != no_chunk)
sr_warn(sc, "trying to bring up %s degraded",
sd->sd_meta->ssd_devname);
if (sd->sd_meta->ssd_meta_flags & SR_META_DIRTY)
sr_warn(sc, "%s was not shutdown properly",
sd->sd_meta->ssd_devname);
SLIST_FOREACH(omi, &sd->sd_meta_opt, omi_link)
if (sd->sd_meta_opt_handler == NULL ||
sd->sd_meta_opt_handler(sd, omi->omi_som) != 0)
sr_meta_opt_handler(sd, omi->omi_som);
if (sd->sd_assemble) {
if ((i = sd->sd_assemble(sd, bc, no_chunk, data))) {
rv = i;
goto unwind;
}
}
DNPRINTF(SR_D_META, "%s: disk assembled from metadata\n",
DEVNAME(sc));
}
/* Metadata MUST be fully populated by this point. */
TAILQ_INSERT_TAIL(&sc->sc_dis_list, sd, sd_link);
/* Allocate all resources. */
if ((rv = sd->sd_alloc_resources(sd)))
goto unwind;
/* Adjust flags if necessary. */
if ((sd->sd_capabilities & SR_CAP_AUTO_ASSEMBLE) &&
(bc->bc_flags & BIOC_SCNOAUTOASSEMBLE) !=
(sd->sd_meta->ssdi.ssd_vol_flags & BIOC_SCNOAUTOASSEMBLE)) {
sd->sd_meta->ssdi.ssd_vol_flags &= ~BIOC_SCNOAUTOASSEMBLE;
sd->sd_meta->ssdi.ssd_vol_flags |=
bc->bc_flags & BIOC_SCNOAUTOASSEMBLE;
}
if (sd->sd_capabilities & SR_CAP_SYSTEM_DISK) {
/* Initialise volume state. */
sd->sd_set_vol_state(sd);
if (sd->sd_vol_status == BIOC_SVOFFLINE) {
sr_error(sc, "%s is offline, will not be brought "
"online", sd->sd_meta->ssd_devname);
goto unwind;
}
/* Setup SCSI iopool. */
scsi_iopool_init(&sd->sd_iopool, sd, sr_wu_get, sr_wu_put);
/*
* All checks passed - return ENXIO if volume cannot be created.
*/
rv = ENXIO;
/*
* Find a free target.
*
* XXX: We reserve sd_target == 0 to indicate the
* discipline is not linked into sc->sc_targets, so begin
* the search with target = 1.
*/
for (target = 1; target < SR_MAX_LD; target++)
if (sc->sc_targets[target] == NULL)
break;
if (target == SR_MAX_LD) {
sr_error(sc, "no free target for %s",
sd->sd_meta->ssd_devname);
goto unwind;
}
/* Clear sense data. */
bzero(&sd->sd_scsi_sense, sizeof(sd->sd_scsi_sense));
/* Attach discipline and get midlayer to probe it. */
sd->sd_target = target;
sc->sc_targets[target] = sd;
if (scsi_probe_lun(sc->sc_scsibus, target, 0) != 0) {
sr_error(sc, "scsi_probe_lun failed");
sc->sc_targets[target] = NULL;
sd->sd_target = 0;
goto unwind;
}
link = scsi_get_link(sc->sc_scsibus, target, 0);
if (link == NULL)
goto unwind;
dev = link->device_softc;
DNPRINTF(SR_D_IOCTL, "%s: sr device added: %s at target %d\n",
DEVNAME(sc), dev->dv_xname, sd->sd_target);
/* XXX - Count volumes, not targets. */
for (i = 0, vol = -1; i <= sd->sd_target; i++)
if (sc->sc_targets[i])
vol++;
rv = 0;
if (sd->sd_meta->ssd_devname[0] != '\0' &&
strncmp(sd->sd_meta->ssd_devname, dev->dv_xname,
sizeof(dev->dv_xname)))
sr_warn(sc, "volume %s is roaming, it used to be %s, "
"updating metadata", dev->dv_xname,
sd->sd_meta->ssd_devname);
/* Populate remaining volume metadata. */
sd->sd_meta->ssdi.ssd_volid = vol;
strlcpy(sd->sd_meta->ssd_devname, dev->dv_xname,
sizeof(sd->sd_meta->ssd_devname));
sr_info(sc, "%s volume attached as %s",
sd->sd_name, sd->sd_meta->ssd_devname);
/* Update device name on any roaming chunks. */
sr_roam_chunks(sd);
#ifndef SMALL_KERNEL
if (sr_sensors_create(sd))
sr_warn(sc, "unable to create sensor for %s",
dev->dv_xname);
#endif /* SMALL_KERNEL */
} else {
/* This volume does not attach as a system disk. */
ch_entry = SLIST_FIRST(cl); /* XXX */
strlcpy(sd->sd_meta->ssd_devname, ch_entry->src_devname,
sizeof(sd->sd_meta->ssd_devname));
if (sd->sd_start_discipline(sd))
goto unwind;
}
/* Save current metadata to disk. */
rv = sr_meta_save(sd, SR_META_DIRTY);
if (sd->sd_vol_status == BIOC_SVREBUILD)
kthread_create_deferred(sr_rebuild_start, sd);
sd->sd_ready = 1;
free(dt, M_DEVBUF, bc->bc_dev_list_len);
return (rv);
unwind:
free(dt, M_DEVBUF, bc->bc_dev_list_len);
sr_discipline_shutdown(sd, 0, 0);
if (rv == EAGAIN)
rv = 0;
return (rv);
}
int
sr_ioctl_deleteraid(struct sr_softc *sc, struct sr_discipline *sd,
struct bioc_deleteraid *bd)
{
int rv = 1;
DNPRINTF(SR_D_IOCTL, "%s: sr_ioctl_deleteraid %s\n",
DEVNAME(sc), bd->bd_dev);
if (sd == NULL) {
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
if (!strncmp(sd->sd_meta->ssd_devname, bd->bd_dev,
sizeof(sd->sd_meta->ssd_devname)))
break;
}
if (sd == NULL) {
sr_error(sc, "volume %s not found", bd->bd_dev);
goto bad;
}
}
sd->sd_deleted = 1;
sd->sd_meta->ssdi.ssd_vol_flags = BIOC_SCNOAUTOASSEMBLE;
sr_discipline_shutdown(sd, 1, 0);
rv = 0;
bad:
return (rv);
}
int
sr_ioctl_discipline(struct sr_softc *sc, struct sr_discipline *sd,
struct bioc_discipline *bd)
{
int rv = 1;
/* Dispatch a discipline specific ioctl. */
DNPRINTF(SR_D_IOCTL, "%s: sr_ioctl_discipline %s\n", DEVNAME(sc),
bd->bd_dev);
if (sd == NULL) {
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
if (!strncmp(sd->sd_meta->ssd_devname, bd->bd_dev,
sizeof(sd->sd_meta->ssd_devname)))
break;
}
if (sd == NULL) {
sr_error(sc, "volume %s not found", bd->bd_dev);
goto bad;
}
}
if (sd->sd_ioctl_handler)
rv = sd->sd_ioctl_handler(sd, bd);
bad:
return (rv);
}
int
sr_ioctl_installboot(struct sr_softc *sc, struct sr_discipline *sd,
struct bioc_installboot *bb)
{
void *bootblk = NULL, *bootldr = NULL;
struct sr_chunk *chunk;
struct sr_meta_opt_item *omi;
struct sr_meta_boot *sbm;
struct disk *dk;
u_int32_t bbs = 0, bls = 0, secsize;
u_char duid[8];
int rv = EINVAL;
int i;
DNPRINTF(SR_D_IOCTL, "%s: sr_ioctl_installboot %s\n", DEVNAME(sc),
bb->bb_dev);
if (sd == NULL) {
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
if (!strncmp(sd->sd_meta->ssd_devname, bb->bb_dev,
sizeof(sd->sd_meta->ssd_devname)))
break;
}
if (sd == NULL) {
sr_error(sc, "volume %s not found", bb->bb_dev);
goto done;
}
}
bzero(duid, sizeof(duid));
TAILQ_FOREACH(dk, &disklist, dk_link)
if (!strncmp(dk->dk_name, bb->bb_dev, sizeof(bb->bb_dev)))
break;
if (dk == NULL || dk->dk_label == NULL ||
(dk->dk_flags & DKF_LABELVALID) == 0 ||
bcmp(dk->dk_label->d_uid, &duid, sizeof(duid)) == 0) {
sr_error(sc, "failed to get DUID for softraid volume");
goto done;
}
memcpy(duid, dk->dk_label->d_uid, sizeof(duid));
/* Ensure that boot storage area is large enough. */
if (sd->sd_meta->ssd_data_blkno < (SR_BOOT_OFFSET + SR_BOOT_SIZE)) {
sr_error(sc, "insufficient boot storage");
goto done;
}
if (bb->bb_bootblk_size > SR_BOOT_BLOCKS_SIZE * DEV_BSIZE) {
sr_error(sc, "boot block too large (%d > %d)",
bb->bb_bootblk_size, SR_BOOT_BLOCKS_SIZE * DEV_BSIZE);
goto done;
}
if (bb->bb_bootldr_size > SR_BOOT_LOADER_SIZE * DEV_BSIZE) {
sr_error(sc, "boot loader too large (%d > %d)",
bb->bb_bootldr_size, SR_BOOT_LOADER_SIZE * DEV_BSIZE);
goto done;
}
secsize = sd->sd_meta->ssdi.ssd_secsize;
/* Copy in boot block. */
bbs = howmany(bb->bb_bootblk_size, secsize) * secsize;
bootblk = malloc(bbs, M_DEVBUF, M_WAITOK | M_ZERO);
if (copyin(bb->bb_bootblk, bootblk, bb->bb_bootblk_size) != 0)
goto done;
/* Copy in boot loader. */
bls = howmany(bb->bb_bootldr_size, secsize) * secsize;
bootldr = malloc(bls, M_DEVBUF, M_WAITOK | M_ZERO);
if (copyin(bb->bb_bootldr, bootldr, bb->bb_bootldr_size) != 0)
goto done;
/* Create or update optional meta for bootable volumes. */
SLIST_FOREACH(omi, &sd->sd_meta_opt, omi_link)
if (omi->omi_som->som_type == SR_OPT_BOOT)
break;
if (omi == NULL) {
omi = malloc(sizeof(struct sr_meta_opt_item), M_DEVBUF,
M_WAITOK | M_ZERO);
omi->omi_som = malloc(sizeof(struct sr_meta_boot), M_DEVBUF,
M_WAITOK | M_ZERO);
omi->omi_som->som_type = SR_OPT_BOOT;
omi->omi_som->som_length = sizeof(struct sr_meta_boot);
SLIST_INSERT_HEAD(&sd->sd_meta_opt, omi, omi_link);
sd->sd_meta->ssdi.ssd_opt_no++;
}
sbm = (struct sr_meta_boot *)omi->omi_som;
memcpy(sbm->sbm_root_duid, duid, sizeof(sbm->sbm_root_duid));
bzero(&sbm->sbm_boot_duid, sizeof(sbm->sbm_boot_duid));
sbm->sbm_bootblk_size = bbs;
sbm->sbm_bootldr_size = bls;
DNPRINTF(SR_D_IOCTL, "sr_ioctl_installboot: root duid is %s\n",
duid_format(sbm->sbm_root_duid));
/* Save boot block and boot loader to each chunk. */
for (i = 0; i < sd->sd_meta->ssdi.ssd_chunk_no; i++) {
chunk = sd->sd_vol.sv_chunks[i];
if (chunk->src_meta.scm_status != BIOC_SDONLINE &&
chunk->src_meta.scm_status != BIOC_SDREBUILD)
continue;
if (i < SR_MAX_BOOT_DISKS)
memcpy(&sbm->sbm_boot_duid[i], chunk->src_duid,
sizeof(sbm->sbm_boot_duid[i]));
/* Save boot blocks. */
DNPRINTF(SR_D_IOCTL,
"sr_ioctl_installboot: saving boot block to %s "
"(%u bytes)\n", chunk->src_devname, bbs);
if (sr_rw(sc, chunk->src_dev_mm, bootblk, bbs,
SR_BOOT_BLOCKS_OFFSET, B_WRITE)) {
sr_error(sc, "failed to write boot block");
goto done;
}
/* Save boot loader.*/
DNPRINTF(SR_D_IOCTL,
"sr_ioctl_installboot: saving boot loader to %s "
"(%u bytes)\n", chunk->src_devname, bls);
if (sr_rw(sc, chunk->src_dev_mm, bootldr, bls,
SR_BOOT_LOADER_OFFSET, B_WRITE)) {
sr_error(sc, "failed to write boot loader");
goto done;
}
}
/* XXX - Install boot block on disk - MD code. */
/* Mark volume as bootable and save metadata. */
sd->sd_meta->ssdi.ssd_vol_flags |= BIOC_SCBOOTABLE;
if (sr_meta_save(sd, SR_META_DIRTY)) {
sr_error(sc, "could not save metadata to %s", DEVNAME(sc));
goto done;
}
rv = 0;
done:
free(bootblk, M_DEVBUF, bbs);
free(bootldr, M_DEVBUF, bls);
return (rv);
}
void
sr_chunks_unwind(struct sr_softc *sc, struct sr_chunk_head *cl)
{
struct sr_chunk *ch_entry, *ch_next;
DNPRINTF(SR_D_IOCTL, "%s: sr_chunks_unwind\n", DEVNAME(sc));
if (!cl)
return;
for (ch_entry = SLIST_FIRST(cl); ch_entry != NULL; ch_entry = ch_next) {
ch_next = SLIST_NEXT(ch_entry, src_link);
DNPRINTF(SR_D_IOCTL, "%s: sr_chunks_unwind closing: %s\n",
DEVNAME(sc), ch_entry->src_devname);
if (ch_entry->src_vn) {
/*
* XXX - explicitly lock the vnode until we can resolve
* the problem introduced by vnode aliasing... specfs
* has no locking, whereas ufs/ffs does!
*/
vn_lock(ch_entry->src_vn, LK_EXCLUSIVE | LK_RETRY);
VOP_CLOSE(ch_entry->src_vn, FREAD | FWRITE, NOCRED,
curproc);
vput(ch_entry->src_vn);
}
free(ch_entry, M_DEVBUF, sizeof(*ch_entry));
}
SLIST_INIT(cl);
}
void
sr_discipline_free(struct sr_discipline *sd)
{
struct sr_softc *sc;
struct sr_discipline *sdtmp1;
struct sr_meta_opt_head *som;
struct sr_meta_opt_item *omi, *omi_next;
if (!sd)
return;
sc = sd->sd_sc;
DNPRINTF(SR_D_DIS, "%s: sr_discipline_free %s\n",
DEVNAME(sc),
sd->sd_meta ? sd->sd_meta->ssd_devname : "nodev");
if (sd->sd_free_resources)
sd->sd_free_resources(sd);
free(sd->sd_vol.sv_chunks, M_DEVBUF, 0);
free(sd->sd_meta, M_DEVBUF, SR_META_SIZE * DEV_BSIZE);
free(sd->sd_meta_foreign, M_DEVBUF, smd[sd->sd_meta_type].smd_size);
som = &sd->sd_meta_opt;
for (omi = SLIST_FIRST(som); omi != NULL; omi = omi_next) {
omi_next = SLIST_NEXT(omi, omi_link);
free(omi->omi_som, M_DEVBUF, 0);
free(omi, M_DEVBUF, sizeof(*omi));
}
if (sd->sd_target != 0) {
KASSERT(sc->sc_targets[sd->sd_target] == sd);
sc->sc_targets[sd->sd_target] = NULL;
}
TAILQ_FOREACH(sdtmp1, &sc->sc_dis_list, sd_link) {
if (sdtmp1 == sd)
break;
}
if (sdtmp1 != NULL)
TAILQ_REMOVE(&sc->sc_dis_list, sd, sd_link);
explicit_bzero(sd, sizeof *sd);
free(sd, M_DEVBUF, sizeof(*sd));
}
void
sr_discipline_shutdown(struct sr_discipline *sd, int meta_save, int dying)
{
struct sr_softc *sc;
int ret, s;
if (!sd)
return;
sc = sd->sd_sc;
DNPRINTF(SR_D_DIS, "%s: sr_discipline_shutdown %s\n", DEVNAME(sc),
sd->sd_meta ? sd->sd_meta->ssd_devname : "nodev");
/* If rebuilding, abort rebuild and drain I/O. */
if (sd->sd_reb_active) {
sd->sd_reb_abort = 1;
while (sd->sd_reb_active)
tsleep_nsec(sd, PWAIT, "sr_shutdown", MSEC_TO_NSEC(1));
}
if (meta_save)
sr_meta_save(sd, 0);
s = splbio();
sd->sd_ready = 0;
/* make sure there isn't a sync pending and yield */
wakeup(sd);
while (sd->sd_sync || sd->sd_must_flush) {
ret = tsleep_nsec(&sd->sd_sync, MAXPRI, "sr_down",
SEC_TO_NSEC(60));
if (ret == EWOULDBLOCK)
break;
}
if (dying == -1) {
sd->sd_ready = 1;
splx(s);
return;
}
#ifndef SMALL_KERNEL
sr_sensors_delete(sd);
#endif /* SMALL_KERNEL */
if (sd->sd_target != 0)
scsi_detach_lun(sc->sc_scsibus, sd->sd_target, 0,
dying ? 0 : DETACH_FORCE);
sr_chunks_unwind(sc, &sd->sd_vol.sv_chunk_list);
if (sd->sd_taskq)
taskq_destroy(sd->sd_taskq);
sr_discipline_free(sd);
splx(s);
}
int
sr_discipline_init(struct sr_discipline *sd, int level)
{
int rv = 1;
/* Initialise discipline function pointers with defaults. */
sd->sd_alloc_resources = sr_alloc_resources;
sd->sd_assemble = NULL;
sd->sd_create = NULL;
sd->sd_free_resources = sr_free_resources;
sd->sd_ioctl_handler = NULL;
sd->sd_openings = NULL;
sd->sd_meta_opt_handler = NULL;
sd->sd_rebuild = sr_rebuild;
sd->sd_scsi_inquiry = sr_raid_inquiry;
sd->sd_scsi_read_cap = sr_raid_read_cap;
sd->sd_scsi_tur = sr_raid_tur;
sd->sd_scsi_req_sense = sr_raid_request_sense;
sd->sd_scsi_start_stop = sr_raid_start_stop;
sd->sd_scsi_sync = sr_raid_sync;
sd->sd_scsi_rw = NULL;
sd->sd_scsi_intr = sr_raid_intr;
sd->sd_scsi_wu_done = NULL;
sd->sd_scsi_done = NULL;
sd->sd_set_chunk_state = sr_set_chunk_state;
sd->sd_set_vol_state = sr_set_vol_state;
sd->sd_start_discipline = NULL;
task_set(&sd->sd_meta_save_task, sr_meta_save_callback, sd);
task_set(&sd->sd_hotspare_rebuild_task, sr_hotspare_rebuild_callback,
sd);
sd->sd_wu_size = sizeof(struct sr_workunit);
switch (level) {
case 0:
sr_raid0_discipline_init(sd);
break;
case 1:
sr_raid1_discipline_init(sd);
break;
case 5:
sr_raid5_discipline_init(sd);
break;
case 6:
sr_raid6_discipline_init(sd);
break;
#ifdef CRYPTO
case 'C':
sr_crypto_discipline_init(sd);
break;
case 0x1C:
sr_raid1c_discipline_init(sd);
break;
#endif
case 'c':
sr_concat_discipline_init(sd);
break;
default:
goto bad;
}
rv = 0;
bad:
return (rv);
}
int
sr_raid_inquiry(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
struct scsi_xfer *xs = wu->swu_xs;
struct scsi_inquiry *cdb = (struct scsi_inquiry *)&xs->cmd;
struct scsi_inquiry_data inq;
DNPRINTF(SR_D_DIS, "%s: sr_raid_inquiry\n", DEVNAME(sd->sd_sc));
if (xs->cmdlen != sizeof(*cdb))
return (EINVAL);
if (ISSET(cdb->flags, SI_EVPD))
return (EOPNOTSUPP);
bzero(&inq, sizeof(inq));
inq.device = T_DIRECT;
inq.dev_qual2 = 0;
inq.version = SCSI_REV_2;
inq.response_format = SID_SCSI2_RESPONSE;
inq.additional_length = SID_SCSI2_ALEN;
inq.flags |= SID_CmdQue;
strlcpy(inq.vendor, sd->sd_meta->ssdi.ssd_vendor,
sizeof(inq.vendor));
strlcpy(inq.product, sd->sd_meta->ssdi.ssd_product,
sizeof(inq.product));
strlcpy(inq.revision, sd->sd_meta->ssdi.ssd_revision,
sizeof(inq.revision));
scsi_copy_internal_data(xs, &inq, sizeof(inq));
return (0);
}
int
sr_raid_read_cap(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
struct scsi_xfer *xs = wu->swu_xs;
struct scsi_read_cap_data rcd;
struct scsi_read_cap_data_16 rcd16;
u_int64_t addr;
int rv = 1;
u_int32_t secsize;
DNPRINTF(SR_D_DIS, "%s: sr_raid_read_cap\n", DEVNAME(sd->sd_sc));
secsize = sd->sd_meta->ssdi.ssd_secsize;
addr = ((sd->sd_meta->ssdi.ssd_size * DEV_BSIZE) / secsize) - 1;
if (xs->cmd.opcode == READ_CAPACITY) {
bzero(&rcd, sizeof(rcd));
if (addr > 0xffffffffllu)
_lto4b(0xffffffff, rcd.addr);
else
_lto4b(addr, rcd.addr);
_lto4b(secsize, rcd.length);
scsi_copy_internal_data(xs, &rcd, sizeof(rcd));
rv = 0;
} else if (xs->cmd.opcode == READ_CAPACITY_16) {
bzero(&rcd16, sizeof(rcd16));
_lto8b(addr, rcd16.addr);
_lto4b(secsize, rcd16.length);
scsi_copy_internal_data(xs, &rcd16, sizeof(rcd16));
rv = 0;
}
return (rv);
}
int
sr_raid_tur(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
DNPRINTF(SR_D_DIS, "%s: sr_raid_tur\n", DEVNAME(sd->sd_sc));
if (sd->sd_vol_status == BIOC_SVOFFLINE) {
sd->sd_scsi_sense.error_code = SSD_ERRCODE_CURRENT;
sd->sd_scsi_sense.flags = SKEY_NOT_READY;
sd->sd_scsi_sense.add_sense_code = 0x04;
sd->sd_scsi_sense.add_sense_code_qual = 0x11;
sd->sd_scsi_sense.extra_len = 4;
return (1);
} else if (sd->sd_vol_status == BIOC_SVINVALID) {
sd->sd_scsi_sense.error_code = SSD_ERRCODE_CURRENT;
sd->sd_scsi_sense.flags = SKEY_HARDWARE_ERROR;
sd->sd_scsi_sense.add_sense_code = 0x05;
sd->sd_scsi_sense.add_sense_code_qual = 0x00;
sd->sd_scsi_sense.extra_len = 4;
return (1);
}
return (0);
}
int
sr_raid_request_sense(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
struct scsi_xfer *xs = wu->swu_xs;
DNPRINTF(SR_D_DIS, "%s: sr_raid_request_sense\n",
DEVNAME(sd->sd_sc));
/* use latest sense data */
memcpy(&xs->sense, &sd->sd_scsi_sense, sizeof(xs->sense));
/* clear sense data */
bzero(&sd->sd_scsi_sense, sizeof(sd->sd_scsi_sense));
return (0);
}
int
sr_raid_start_stop(struct sr_workunit *wu)
{
struct scsi_xfer *xs = wu->swu_xs;
struct scsi_start_stop *ss = (struct scsi_start_stop *)&xs->cmd;
DNPRINTF(SR_D_DIS, "%s: sr_raid_start_stop\n",
DEVNAME(wu->swu_dis->sd_sc));
if (!ss)
return (1);
/*
* do nothing!
* a softraid discipline should always reflect correct status
*/
return (0);
}
int
sr_raid_sync(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
int s, ret, rv = 0, ios;
DNPRINTF(SR_D_DIS, "%s: sr_raid_sync\n", DEVNAME(sd->sd_sc));
/* when doing a fake sync don't count the wu */
ios = (wu->swu_flags & SR_WUF_FAKE) ? 0 : 1;
s = splbio();
sd->sd_sync = 1;
while (sd->sd_wu_pending > ios) {
ret = tsleep_nsec(sd, PRIBIO, "sr_sync", SEC_TO_NSEC(15));
if (ret == EWOULDBLOCK) {
DNPRINTF(SR_D_DIS, "%s: sr_raid_sync timeout\n",
DEVNAME(sd->sd_sc));
rv = 1;
break;
}
}
sd->sd_sync = 0;
splx(s);
wakeup(&sd->sd_sync);
return (rv);
}
void
sr_raid_intr(struct buf *bp)
{
struct sr_ccb *ccb = (struct sr_ccb *)bp;
struct sr_workunit *wu = ccb->ccb_wu;
#ifdef SR_DEBUG
struct sr_discipline *sd = wu->swu_dis;
struct scsi_xfer *xs = wu->swu_xs;
#endif
int s;
DNPRINTF(SR_D_INTR, "%s: %s %s intr bp %p xs %p\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, sd->sd_name, bp, xs);
s = splbio();
sr_ccb_done(ccb);
sr_wu_done(wu);
splx(s);
}
void
sr_schedule_wu(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
struct sr_workunit *wup;
int s;
DNPRINTF(SR_D_WU, "sr_schedule_wu: schedule wu %p state %i "
"flags 0x%x\n", wu, wu->swu_state, wu->swu_flags);
KASSERT(wu->swu_io_count > 0);
s = splbio();
/* Construct the work unit, do not schedule it. */
if (wu->swu_state == SR_WU_CONSTRUCT)
goto queued;
/* Deferred work unit being reconstructed, do not start. */
if (wu->swu_state == SR_WU_REQUEUE)
goto queued;
/* Current work unit failed, restart. */
if (wu->swu_state == SR_WU_RESTART)
goto start;
if (wu->swu_state != SR_WU_INPROGRESS)
panic("sr_schedule_wu: work unit not in progress (state %i)",
wu->swu_state);
/* Walk queue backwards and fill in collider if we have one. */
TAILQ_FOREACH_REVERSE(wup, &sd->sd_wu_pendq, sr_wu_list, swu_link) {
if (wu->swu_blk_end < wup->swu_blk_start ||
wup->swu_blk_end < wu->swu_blk_start)
continue;
/* Defer work unit due to LBA collision. */
DNPRINTF(SR_D_WU, "sr_schedule_wu: deferring work unit %p\n",
wu);
wu->swu_state = SR_WU_DEFERRED;
while (wup->swu_collider)
wup = wup->swu_collider;
wup->swu_collider = wu;
TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu, swu_link);
sd->sd_wu_collisions++;
goto queued;
}
start:
sr_raid_startwu(wu);
queued:
splx(s);
}
void
sr_raid_startwu(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
struct sr_ccb *ccb;
DNPRINTF(SR_D_WU, "sr_raid_startwu: start wu %p\n", wu);
splassert(IPL_BIO);
if (wu->swu_state == SR_WU_DEFERRED) {
TAILQ_REMOVE(&sd->sd_wu_defq, wu, swu_link);
wu->swu_state = SR_WU_INPROGRESS;
}
if (wu->swu_state != SR_WU_RESTART)
TAILQ_INSERT_TAIL(&sd->sd_wu_pendq, wu, swu_link);
/* Start all of the individual I/Os. */
if (wu->swu_cb_active == 1)
panic("%s: sr_startwu_callback", DEVNAME(sd->sd_sc));
wu->swu_cb_active = 1;
TAILQ_FOREACH(ccb, &wu->swu_ccb, ccb_link)
VOP_STRATEGY(ccb->ccb_buf.b_vp, &ccb->ccb_buf);
wu->swu_cb_active = 0;
}
void
sr_raid_recreate_wu(struct sr_workunit *wu)
{
struct sr_discipline *sd = wu->swu_dis;
struct sr_workunit *wup = wu;
/*
* Recreate a work unit by releasing the associated CCBs and reissuing
* the SCSI I/O request. This process is then repeated for all of the
* colliding work units.
*/
do {
sr_wu_release_ccbs(wup);
wup->swu_state = SR_WU_REQUEUE;
if (sd->sd_scsi_rw(wup))
panic("could not requeue I/O");
wup = wup->swu_collider;
} while (wup);
}
int
sr_alloc_resources(struct sr_discipline *sd)
{
if (sr_wu_alloc(sd)) {
sr_error(sd->sd_sc, "unable to allocate work units");
return (ENOMEM);
}
if (sr_ccb_alloc(sd)) {
sr_error(sd->sd_sc, "unable to allocate ccbs");
return (ENOMEM);
}
return (0);
}
void
sr_free_resources(struct sr_discipline *sd)
{
sr_wu_free(sd);
sr_ccb_free(sd);
}
void
sr_set_chunk_state(struct sr_discipline *sd, int c, int new_state)
{
int old_state, s;
DNPRINTF(SR_D_STATE, "%s: %s: %s: sr_set_chunk_state %d -> %d\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname, c, new_state);
/* ok to go to splbio since this only happens in error path */
s = splbio();
old_state = sd->sd_vol.sv_chunks[c]->src_meta.scm_status;
/* multiple IOs to the same chunk that fail will come through here */
if (old_state == new_state)
goto done;
switch (old_state) {
case BIOC_SDONLINE:
if (new_state == BIOC_SDOFFLINE)
break;
else
goto die;
break;
case BIOC_SDOFFLINE:
goto die;
default:
die:
splx(s); /* XXX */
panic("%s: %s: %s: invalid chunk state transition %d -> %d",
DEVNAME(sd->sd_sc),
sd->sd_meta->ssd_devname,
sd->sd_vol.sv_chunks[c]->src_meta.scmi.scm_devname,
old_state, new_state);
/* NOTREACHED */
}
sd->sd_vol.sv_chunks[c]->src_meta.scm_status = new_state;
sd->sd_set_vol_state(sd);
sd->sd_must_flush = 1;
task_add(systq, &sd->sd_meta_save_task);
done:
splx(s);
}
void
sr_set_vol_state(struct sr_discipline *sd)
{
int states[SR_MAX_STATES];
int new_state, i, nd;
int old_state = sd->sd_vol_status;
u_int32_t s;
DNPRINTF(SR_D_STATE, "%s: %s: sr_set_vol_state\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname);
nd = sd->sd_meta->ssdi.ssd_chunk_no;
for (i = 0; i < SR_MAX_STATES; i++)
states[i] = 0;
for (i = 0; i < nd; i++) {
s = sd->sd_vol.sv_chunks[i]->src_meta.scm_status;
if (s >= SR_MAX_STATES)
panic("%s: %s: %s: invalid chunk state",
DEVNAME(sd->sd_sc),
sd->sd_meta->ssd_devname,
sd->sd_vol.sv_chunks[i]->src_meta.scmi.scm_devname);
states[s]++;
}
if (states[BIOC_SDONLINE] == nd)
new_state = BIOC_SVONLINE;
else
new_state = BIOC_SVOFFLINE;
DNPRINTF(SR_D_STATE, "%s: %s: sr_set_vol_state %d -> %d\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname,
old_state, new_state);
switch (old_state) {
case BIOC_SVONLINE:
if (new_state == BIOC_SVOFFLINE || new_state == BIOC_SVONLINE)
break;
else
goto die;
break;
case BIOC_SVOFFLINE:
/* XXX this might be a little too much */
goto die;
default:
die:
panic("%s: %s: invalid volume state transition %d -> %d",
DEVNAME(sd->sd_sc),
sd->sd_meta->ssd_devname,
old_state, new_state);
/* NOTREACHED */
}
sd->sd_vol_status = new_state;
}
void *
sr_block_get(struct sr_discipline *sd, long length)
{
return dma_alloc(length, PR_NOWAIT | PR_ZERO);
}
void
sr_block_put(struct sr_discipline *sd, void *ptr, int length)
{
dma_free(ptr, length);
}
void
sr_checksum_print(u_int8_t *md5)
{
int i;
for (i = 0; i < MD5_DIGEST_LENGTH; i++)
printf("%02x", md5[i]);
}
void
sr_checksum(struct sr_softc *sc, void *src, void *md5, u_int32_t len)
{
MD5_CTX ctx;
DNPRINTF(SR_D_MISC, "%s: sr_checksum(%p %p %d)\n", DEVNAME(sc), src,
md5, len);
MD5Init(&ctx);
MD5Update(&ctx, src, len);
MD5Final(md5, &ctx);
}
void
sr_uuid_generate(struct sr_uuid *uuid)
{
arc4random_buf(uuid->sui_id, sizeof(uuid->sui_id));
/* UUID version 4: random */
uuid->sui_id[6] &= 0x0f;
uuid->sui_id[6] |= 0x40;
/* RFC4122 variant */
uuid->sui_id[8] &= 0x3f;
uuid->sui_id[8] |= 0x80;
}
char *
sr_uuid_format(struct sr_uuid *uuid)
{
char *uuidstr;
uuidstr = malloc(37, M_DEVBUF, M_WAITOK);
snprintf(uuidstr, 37,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x",
uuid->sui_id[0], uuid->sui_id[1],
uuid->sui_id[2], uuid->sui_id[3],
uuid->sui_id[4], uuid->sui_id[5],
uuid->sui_id[6], uuid->sui_id[7],
uuid->sui_id[8], uuid->sui_id[9],
uuid->sui_id[10], uuid->sui_id[11],
uuid->sui_id[12], uuid->sui_id[13],
uuid->sui_id[14], uuid->sui_id[15]);
return uuidstr;
}
void
sr_uuid_print(struct sr_uuid *uuid, int cr)
{
char *uuidstr;
uuidstr = sr_uuid_format(uuid);
printf("%s%s", uuidstr, (cr ? "\n" : ""));
free(uuidstr, M_DEVBUF, 37);
}
int
sr_already_assembled(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
struct sr_discipline *sdtmp;
TAILQ_FOREACH(sdtmp, &sc->sc_dis_list, sd_link) {
if (!bcmp(&sd->sd_meta->ssdi.ssd_uuid,
&sdtmp->sd_meta->ssdi.ssd_uuid,
sizeof(sd->sd_meta->ssdi.ssd_uuid)))
return (1);
}
return (0);
}
int32_t
sr_validate_stripsize(u_int32_t b)
{
int s = 0;
if (b % DEV_BSIZE)
return (-1);
while ((b & 1) == 0) {
b >>= 1;
s++;
}
/* only multiple of twos */
b >>= 1;
if (b)
return(-1);
return (s);
}
void
sr_quiesce(void)
{
struct sr_softc *sc = softraid0;
struct sr_discipline *sd, *nsd;
if (sc == NULL)
return;
/* Shutdown disciplines in reverse attach order. */
TAILQ_FOREACH_REVERSE_SAFE(sd, &sc->sc_dis_list,
sr_discipline_list, sd_link, nsd)
sr_discipline_shutdown(sd, 1, -1);
}
void
sr_shutdown(int dying)
{
struct sr_softc *sc = softraid0;
struct sr_discipline *sd;
if (sc == NULL)
return;
DNPRINTF(SR_D_MISC, "%s: sr_shutdown\n", DEVNAME(sc));
/*
* Since softraid is not under mainbus, we have to explicitly
* notify its children that the power is going down, so they
* can execute their shutdown hooks.
*/
config_suspend((struct device *)sc, DVACT_POWERDOWN);
/* Shutdown disciplines in reverse attach order. */
while ((sd = TAILQ_LAST(&sc->sc_dis_list, sr_discipline_list)) != NULL)
sr_discipline_shutdown(sd, 1, dying);
}
int
sr_validate_io(struct sr_workunit *wu, daddr_t *blkno, char *func)
{
struct sr_discipline *sd = wu->swu_dis;
struct scsi_xfer *xs = wu->swu_xs;
int rv = 1;
DNPRINTF(SR_D_DIS, "%s: %s 0x%02x\n", DEVNAME(sd->sd_sc), func,
xs->cmd.opcode);
if (sd->sd_meta->ssd_data_blkno == 0)
panic("invalid data blkno");
if (sd->sd_vol_status == BIOC_SVOFFLINE) {
DNPRINTF(SR_D_DIS, "%s: %s device offline\n",
DEVNAME(sd->sd_sc), func);
goto bad;
}
if (xs->datalen == 0) {
printf("%s: %s: illegal block count for %s\n",
DEVNAME(sd->sd_sc), func, sd->sd_meta->ssd_devname);
goto bad;
}
if (xs->cmdlen == 10)
*blkno = _4btol(((struct scsi_rw_10 *)&xs->cmd)->addr);
else if (xs->cmdlen == 16)
*blkno = _8btol(((struct scsi_rw_16 *)&xs->cmd)->addr);
else if (xs->cmdlen == 6)
*blkno = _3btol(((struct scsi_rw *)&xs->cmd)->addr);
else {
printf("%s: %s: illegal cmdlen for %s\n",
DEVNAME(sd->sd_sc), func, sd->sd_meta->ssd_devname);
goto bad;
}
*blkno *= (sd->sd_meta->ssdi.ssd_secsize / DEV_BSIZE);
wu->swu_blk_start = *blkno;
wu->swu_blk_end = *blkno + (xs->datalen >> DEV_BSHIFT) - 1;
if (wu->swu_blk_end > sd->sd_meta->ssdi.ssd_size) {
DNPRINTF(SR_D_DIS, "%s: %s out of bounds start: %lld "
"end: %lld length: %d\n",
DEVNAME(sd->sd_sc), func, (long long)wu->swu_blk_start,
(long long)wu->swu_blk_end, xs->datalen);
sd->sd_scsi_sense.error_code = SSD_ERRCODE_CURRENT |
SSD_ERRCODE_VALID;
sd->sd_scsi_sense.flags = SKEY_ILLEGAL_REQUEST;
sd->sd_scsi_sense.add_sense_code = 0x21;
sd->sd_scsi_sense.add_sense_code_qual = 0x00;
sd->sd_scsi_sense.extra_len = 4;
goto bad;
}
rv = 0;
bad:
return (rv);
}
void
sr_rebuild_start(void *arg)
{
struct sr_discipline *sd = arg;
struct sr_softc *sc = sd->sd_sc;
DNPRINTF(SR_D_REBUILD, "%s: %s starting rebuild thread\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname);
if (kthread_create(sr_rebuild_thread, sd, &sd->sd_background_proc,
DEVNAME(sc)) != 0)
printf("%s: unable to start background operation\n",
DEVNAME(sc));
}
void
sr_rebuild_thread(void *arg)
{
struct sr_discipline *sd = arg;
DNPRINTF(SR_D_REBUILD, "%s: %s rebuild thread started\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname);
sd->sd_reb_active = 1;
sd->sd_rebuild(sd);
sd->sd_reb_active = 0;
kthread_exit(0);
}
void
sr_rebuild(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
u_int64_t sz, whole_blk, partial_blk, blk, restart;
daddr_t lba;
struct sr_workunit *wu_r, *wu_w;
struct scsi_xfer xs_r, xs_w;
struct scsi_rw_16 *cr, *cw;
int c, s, slept, percent = 0, old_percent = -1;
u_int8_t *buf;
whole_blk = sd->sd_meta->ssdi.ssd_size / SR_REBUILD_IO_SIZE;
partial_blk = sd->sd_meta->ssdi.ssd_size % SR_REBUILD_IO_SIZE;
restart = sd->sd_meta->ssd_rebuild / SR_REBUILD_IO_SIZE;
if (restart > whole_blk) {
printf("%s: bogus rebuild restart offset, starting from 0\n",
DEVNAME(sc));
restart = 0;
}
if (restart) {
/*
* XXX there is a hole here; there is a possibility that we
* had a restart however the chunk that was supposed to
* be rebuilt is no longer valid; we can reach this situation
* when a rebuild is in progress and the box crashes and
* on reboot the rebuild chunk is different (like zero'd or
* replaced). We need to check the uuid of the chunk that is
* being rebuilt to assert this.
*/
percent = sr_rebuild_percent(sd);
printf("%s: resuming rebuild on %s at %d%%\n",
DEVNAME(sc), sd->sd_meta->ssd_devname, percent);
}
/* currently this is 64k therefore we can use dma_alloc */
buf = dma_alloc(SR_REBUILD_IO_SIZE << DEV_BSHIFT, PR_WAITOK);
for (blk = restart; blk <= whole_blk; blk++) {
lba = blk * SR_REBUILD_IO_SIZE;
sz = SR_REBUILD_IO_SIZE;
if (blk == whole_blk) {
if (partial_blk == 0)
break;
sz = partial_blk;
}
/* get some wu */
wu_r = sr_scsi_wu_get(sd, 0);
wu_w = sr_scsi_wu_get(sd, 0);
DNPRINTF(SR_D_REBUILD, "%s: %s rebuild wu_r %p, wu_w %p\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, wu_r, wu_w);
/* setup read io */
bzero(&xs_r, sizeof xs_r);
xs_r.error = XS_NOERROR;
xs_r.flags = SCSI_DATA_IN;
xs_r.datalen = sz << DEV_BSHIFT;
xs_r.data = buf;
xs_r.cmdlen = sizeof(*cr);
cr = (struct scsi_rw_16 *)&xs_r.cmd;
cr->opcode = READ_16;
_lto4b(sz, cr->length);
_lto8b(lba, cr->addr);
wu_r->swu_state = SR_WU_CONSTRUCT;
wu_r->swu_flags |= SR_WUF_REBUILD;
wu_r->swu_xs = &xs_r;
if (sd->sd_scsi_rw(wu_r)) {
printf("%s: could not create read io\n",
DEVNAME(sc));
goto fail;
}
/* setup write io */
bzero(&xs_w, sizeof xs_w);
xs_w.error = XS_NOERROR;
xs_w.flags = SCSI_DATA_OUT;
xs_w.datalen = sz << DEV_BSHIFT;
xs_w.data = buf;
xs_w.cmdlen = sizeof(*cw);
cw = (struct scsi_rw_16 *)&xs_w.cmd;
cw->opcode = WRITE_16;
_lto4b(sz, cw->length);
_lto8b(lba, cw->addr);
wu_w->swu_state = SR_WU_CONSTRUCT;
wu_w->swu_flags |= SR_WUF_REBUILD | SR_WUF_WAKEUP;
wu_w->swu_xs = &xs_w;
if (sd->sd_scsi_rw(wu_w)) {
printf("%s: could not create write io\n",
DEVNAME(sc));
goto fail;
}
/*
* collide with the read io so that we get automatically
* started when the read is done
*/
wu_w->swu_state = SR_WU_DEFERRED;
wu_r->swu_collider = wu_w;
s = splbio();
TAILQ_INSERT_TAIL(&sd->sd_wu_defq, wu_w, swu_link);
splx(s);
DNPRINTF(SR_D_REBUILD, "%s: %s rebuild scheduling wu_r %p\n",
DEVNAME(sd->sd_sc), sd->sd_meta->ssd_devname, wu_r);
wu_r->swu_state = SR_WU_INPROGRESS;
sr_schedule_wu(wu_r);
/* wait for write completion */
slept = 0;
while ((wu_w->swu_flags & SR_WUF_REBUILDIOCOMP) == 0) {
tsleep_nsec(wu_w, PRIBIO, "sr_rebuild", INFSLP);
slept = 1;
}
/* yield if we didn't sleep */
if (slept == 0)
tsleep_nsec(sc, PWAIT, "sr_yield", MSEC_TO_NSEC(1));
sr_scsi_wu_put(sd, wu_r);
sr_scsi_wu_put(sd, wu_w);
sd->sd_meta->ssd_rebuild = lba;
/* XXX - this should be based on size, not percentage. */
/* save metadata every percent */
percent = sr_rebuild_percent(sd);
if (percent != old_percent && blk != whole_blk) {
if (sr_meta_save(sd, SR_META_DIRTY))
printf("%s: could not save metadata to %s\n",
DEVNAME(sc), sd->sd_meta->ssd_devname);
old_percent = percent;
}
if (sd->sd_reb_abort)
goto abort;
}
/* all done */
sd->sd_meta->ssd_rebuild = 0;
for (c = 0; c < sd->sd_meta->ssdi.ssd_chunk_no; c++) {
if (sd->sd_vol.sv_chunks[c]->src_meta.scm_status ==
BIOC_SDREBUILD) {
sd->sd_set_chunk_state(sd, c, BIOC_SDONLINE);
break;
}
}
abort:
if (sr_meta_save(sd, SR_META_DIRTY))
printf("%s: could not save metadata to %s\n",
DEVNAME(sc), sd->sd_meta->ssd_devname);
fail:
dma_free(buf, SR_REBUILD_IO_SIZE << DEV_BSHIFT);
}
#ifndef SMALL_KERNEL
int
sr_sensors_create(struct sr_discipline *sd)
{
struct sr_softc *sc = sd->sd_sc;
int rv = 1;
DNPRINTF(SR_D_STATE, "%s: %s: sr_sensors_create\n",
DEVNAME(sc), sd->sd_meta->ssd_devname);
sd->sd_vol.sv_sensor.type = SENSOR_DRIVE;
sd->sd_vol.sv_sensor.status = SENSOR_S_UNKNOWN;
strlcpy(sd->sd_vol.sv_sensor.desc, sd->sd_meta->ssd_devname,
sizeof(sd->sd_vol.sv_sensor.desc));
sensor_attach(&sc->sc_sensordev, &sd->sd_vol.sv_sensor);
sd->sd_vol.sv_sensor_attached = 1;
if (sc->sc_sensor_task == NULL) {
sc->sc_sensor_task = sensor_task_register(sc,
sr_sensors_refresh, 10);
if (sc->sc_sensor_task == NULL)
goto bad;
}
rv = 0;
bad:
return (rv);
}
void
sr_sensors_delete(struct sr_discipline *sd)
{
DNPRINTF(SR_D_STATE, "%s: sr_sensors_delete\n", DEVNAME(sd->sd_sc));
if (sd->sd_vol.sv_sensor_attached)
sensor_detach(&sd->sd_sc->sc_sensordev, &sd->sd_vol.sv_sensor);
}
void
sr_sensors_refresh(void *arg)
{
struct sr_softc *sc = arg;
struct sr_volume *sv;
struct sr_discipline *sd;
DNPRINTF(SR_D_STATE, "%s: sr_sensors_refresh\n", DEVNAME(sc));
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
sv = &sd->sd_vol;
switch(sd->sd_vol_status) {
case BIOC_SVOFFLINE:
sv->sv_sensor.value = SENSOR_DRIVE_FAIL;
sv->sv_sensor.status = SENSOR_S_CRIT;
break;
case BIOC_SVDEGRADED:
sv->sv_sensor.value = SENSOR_DRIVE_PFAIL;
sv->sv_sensor.status = SENSOR_S_WARN;
break;
case BIOC_SVREBUILD:
sv->sv_sensor.value = SENSOR_DRIVE_REBUILD;
sv->sv_sensor.status = SENSOR_S_WARN;
break;
case BIOC_SVSCRUB:
case BIOC_SVONLINE:
sv->sv_sensor.value = SENSOR_DRIVE_ONLINE;
sv->sv_sensor.status = SENSOR_S_OK;
break;
default:
sv->sv_sensor.value = 0; /* unknown */
sv->sv_sensor.status = SENSOR_S_UNKNOWN;
}
}
}
#endif /* SMALL_KERNEL */
#ifdef SR_FANCY_STATS
void sr_print_stats(void);
void
sr_print_stats(void)
{
struct sr_softc *sc = softraid0;
struct sr_discipline *sd;
if (sc == NULL) {
printf("no softraid softc found\n");
return;
}
TAILQ_FOREACH(sd, &sc->sc_dis_list, sd_link) {
printf("%s: ios pending %d, collisions %llu\n",
sd->sd_meta->ssd_devname,
sd->sd_wu_pending,
sd->sd_wu_collisions);
}
}
#endif /* SR_FANCY_STATS */
#ifdef SR_DEBUG
void
sr_meta_print(struct sr_metadata *m)
{
int i;
struct sr_meta_chunk *mc;
struct sr_meta_opt_hdr *omh;
if (!(sr_debug & SR_D_META))
return;
printf("\tssd_magic 0x%llx\n", m->ssdi.ssd_magic);
printf("\tssd_version %d\n", m->ssdi.ssd_version);
printf("\tssd_vol_flags 0x%x\n", m->ssdi.ssd_vol_flags);
printf("\tssd_uuid ");
sr_uuid_print(&m->ssdi.ssd_uuid, 1);
printf("\tssd_chunk_no %d\n", m->ssdi.ssd_chunk_no);
printf("\tssd_chunk_id %d\n", m->ssdi.ssd_chunk_id);
printf("\tssd_opt_no %d\n", m->ssdi.ssd_opt_no);
printf("\tssd_volid %d\n", m->ssdi.ssd_volid);
printf("\tssd_level %d\n", m->ssdi.ssd_level);
printf("\tssd_size %lld\n", m->ssdi.ssd_size);
printf("\tssd_devname %s\n", m->ssd_devname);
printf("\tssd_vendor %s\n", m->ssdi.ssd_vendor);
printf("\tssd_product %s\n", m->ssdi.ssd_product);
printf("\tssd_revision %s\n", m->ssdi.ssd_revision);
printf("\tssd_strip_size %d\n", m->ssdi.ssd_strip_size);
printf("\tssd_checksum ");
sr_checksum_print(m->ssd_checksum);
printf("\n");
printf("\tssd_meta_flags 0x%x\n", m->ssd_meta_flags);
printf("\tssd_ondisk %llu\n", m->ssd_ondisk);
mc = (struct sr_meta_chunk *)(m + 1);
for (i = 0; i < m->ssdi.ssd_chunk_no; i++, mc++) {
printf("\t\tscm_volid %d\n", mc->scmi.scm_volid);
printf("\t\tscm_chunk_id %d\n", mc->scmi.scm_chunk_id);
printf("\t\tscm_devname %s\n", mc->scmi.scm_devname);
printf("\t\tscm_size %lld\n", mc->scmi.scm_size);
printf("\t\tscm_coerced_size %lld\n",mc->scmi.scm_coerced_size);
printf("\t\tscm_uuid ");
sr_uuid_print(&mc->scmi.scm_uuid, 1);
printf("\t\tscm_checksum ");
sr_checksum_print(mc->scm_checksum);
printf("\n");
printf("\t\tscm_status %d\n", mc->scm_status);
}
omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(m + 1) +
sizeof(struct sr_meta_chunk) * m->ssdi.ssd_chunk_no);
for (i = 0; i < m->ssdi.ssd_opt_no; i++) {
printf("\t\t\tsom_type %d\n", omh->som_type);
printf("\t\t\tsom_checksum ");
sr_checksum_print(omh->som_checksum);
printf("\n");
omh = (struct sr_meta_opt_hdr *)((void *)omh +
omh->som_length);
}
}
void
sr_dump_block(void *blk, int len)
{
uint8_t *b = blk;
int i, j, c;
for (i = 0; i < len; i += 16) {
for (j = 0; j < 16; j++)
printf("%.2x ", b[i + j]);
printf(" ");
for (j = 0; j < 16; j++) {
c = b[i + j];
if (c < ' ' || c > 'z' || i + j > len)
c = '.';
printf("%c", c);
}
printf("\n");
}
}
void
sr_dump_mem(u_int8_t *p, int len)
{
int i;
for (i = 0; i < len; i++)
printf("%02x ", *p++);
printf("\n");
}
#endif /* SR_DEBUG */
#ifdef HIBERNATE
/*
* Side-effect free (no malloc, printf, pool, splx) softraid crypto writer.
*
* This function must perform the following:
* 1. Determine the underlying device's own side-effect free I/O function
* (eg, ahci_hibernate_io, wd_hibernate_io, etc).
* 2. Store enough information in the provided page argument for subsequent
* I/O calls (such as the crypto discipline structure for the keys, the
* offset of the softraid partition on the underlying disk, as well as
* the offset of the swap partition within the crypto volume.
* 3. Encrypt the incoming data using the sr_discipline keys, then pass
* the request to the underlying device's own I/O function.
*/
int
sr_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page)
{
/* Struct for stashing data obtained on HIB_INIT.
* XXX
* We share the page with the underlying device's own
* side-effect free I/O function, so we pad our data to
* the end of the page. Presently this does not overlap
* with either of the two other side-effect free i/o
* functions (ahci/wd).
*/
struct {
char pad[3072];
struct sr_discipline *srd;
hibio_fn subfn; /* underlying device i/o fn */
dev_t subdev; /* underlying device dev_t */
daddr_t sr_swapoff; /* ofs of swap part in sr volume */
char buf[DEV_BSIZE]; /* encryption performed into this buf */
} *my = page;
extern struct cfdriver sd_cd;
char errstr[128], *dl_ret;
struct sr_chunk *schunk;
struct sd_softc *sd;
struct aes_xts_ctx ctx;
struct sr_softc *sc;
struct device *dv;
daddr_t key_blkno;
uint32_t sub_raidoff; /* ofs of sr part in underlying dev */
struct disklabel dl;
struct partition *pp;
size_t i, j;
u_char iv[8];
/*
* In HIB_INIT, we are passed the swap partition size and offset
* in 'size' and 'blkno' respectively. These are relative to the
* start of the softraid partition, and we need to save these
* for later translation to the underlying device's layout.
*/
if (op == HIB_INIT) {
dv = disk_lookup(&sd_cd, DISKUNIT(dev));
sd = (struct sd_softc *)dv;
sc = (struct sr_softc *)dv->dv_parent->dv_parent;
/*
* Look up the sr discipline. This is used to determine
* if we are SR crypto and what the underlying device is.
*/
my->srd = sc->sc_targets[sd->sc_link->target];
DNPRINTF(SR_D_MISC, "sr_hibernate_io: discipline is %s\n",
my->srd->sd_name);
if (strncmp(my->srd->sd_name, "CRYPTO",
sizeof(my->srd->sd_name)))
return (ENOTSUP);
/* Find the underlying device */
schunk = my->srd->sd_vol.sv_chunks[0];
my->subdev = schunk->src_dev_mm;
/*
* Find the appropriate underlying device side effect free
* I/O function, based on the type of device it is.
*/
my->subfn = get_hibernate_io_function(my->subdev);
if (!my->subfn)
return (ENODEV);
/*
* Find blkno where this raid partition starts on
* the underlying disk.
*/
dl_ret = disk_readlabel(&dl, my->subdev, errstr,
sizeof(errstr));
if (dl_ret) {
printf("Hibernate error reading disklabel: %s\n", dl_ret);
return (ENOTSUP);
}
pp = &dl.d_partitions[DISKPART(my->subdev)];
if (pp->p_fstype != FS_RAID || DL_GETPSIZE(pp) == 0)
return (ENOTSUP);
/* Find the blkno of the SR part in the underlying device */
sub_raidoff = my->srd->sd_meta->ssd_data_blkno +
DL_SECTOBLK(&dl, DL_GETPOFFSET(pp));
DNPRINTF(SR_D_MISC,"sr_hibernate_io: blk trans ofs: %d blks\n",
sub_raidoff);
/* Save the blkno of the swap partition in the SR disk */
my->sr_swapoff = blkno;
/* Initialize the sub-device */
return my->subfn(my->subdev, sub_raidoff + blkno,
addr, size, op, page);
}
/* Hibernate only uses (and we only support) writes */
if (op != HIB_W)
return (ENOTSUP);
/*
* Blocks act as the IV for the encryption. These block numbers
* are relative to the start of the sr partition, but the 'blkno'
* passed above is relative to the start of the swap partition
* inside the sr partition, so bias appropriately.
*/
key_blkno = my->sr_swapoff + blkno;
/* Process each disk block one at a time. */
for (i = 0; i < size; i += DEV_BSIZE) {
int res;
bzero(&ctx, sizeof(ctx));
/*
* Set encryption key (from the sr discipline stashed
* during HIB_INIT. This code is based on the softraid
* bootblock code.
*/
aes_xts_setkey(&ctx, my->srd->mds.mdd_crypto.scr_key[0], 64);
/* We encrypt DEV_BSIZE bytes at a time in my->buf */
memcpy(my->buf, ((char *)addr) + i, DEV_BSIZE);
/* Block number is the IV */
memcpy(&iv, &key_blkno, sizeof(key_blkno));
aes_xts_reinit(&ctx, iv);
/* Encrypt DEV_BSIZE bytes, AES_XTS_BLOCKSIZE bytes at a time */
for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE)
aes_xts_encrypt(&ctx, my->buf + j);
/*
* Write one block out from my->buf to the underlying device
* using its own side-effect free I/O function.
*/
res = my->subfn(my->subdev, blkno + (i / DEV_BSIZE),
(vaddr_t)(my->buf), DEV_BSIZE, op, page);
if (res != 0)
return (res);
key_blkno++;
}
return (0);
}
#endif /* HIBERNATE */
327
212
157
5
5
70
70
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/* $OpenBSD: uvm_object.c,v 1.25 2022/02/21 16:08:36 kn Exp $ */
/*
* Copyright (c) 2006, 2010, 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Mindaugas Rasiukevicius.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* uvm_object.c: operate with memory objects
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mman.h>
#include <sys/atomic.h>
#include <sys/rwlock.h>
#include <uvm/uvm.h>
/* Dummy object used by some pmaps for sanity checks. */
const struct uvm_pagerops pmap_pager = {
/* nothing */
};
/* Dummy object used by the buffer cache for sanity checks. */
const struct uvm_pagerops bufcache_pager = {
/* nothing */
};
/* Page count to fetch per single step. */
#define FETCH_PAGECOUNT 16
/*
* uvm_obj_init: initialize UVM memory object.
*/
void
uvm_obj_init(struct uvm_object *uobj, const struct uvm_pagerops *pgops, int refs)
{
int alock;
alock = ((pgops != NULL) && (pgops != &pmap_pager) &&
(pgops != &bufcache_pager) && (refs != UVM_OBJ_KERN));
if (alock) {
/* Allocate and assign a lock. */
rw_obj_alloc(&uobj->vmobjlock, "uobjlk");
} else {
/* The lock will need to be set via uvm_obj_setlock(). */
uobj->vmobjlock = NULL;
}
uobj->pgops = pgops;
RBT_INIT(uvm_objtree, &uobj->memt);
uobj->uo_npages = 0;
uobj->uo_refs = refs;
}
/*
* uvm_obj_destroy: destroy UVM memory object.
*/
void
uvm_obj_destroy(struct uvm_object *uo)
{
KASSERT(RBT_EMPTY(uvm_objtree, &uo->memt));
rw_obj_free(uo->vmobjlock);
}
/*
* uvm_obj_setlock: assign a vmobjlock to the UVM object.
*
* => Caller is responsible to ensure that UVM objects is not use.
* => Only dynamic lock may be previously set. We drop the reference then.
*/
void
uvm_obj_setlock(struct uvm_object *uo, struct rwlock *lockptr)
{
struct rwlock *olockptr = uo->vmobjlock;
if (olockptr) {
/* Drop the reference on the old lock. */
rw_obj_free(olockptr);
}
if (lockptr == NULL) {
/* If new lock is not passed - allocate default one. */
rw_obj_alloc(&lockptr, "uobjlk");
}
uo->vmobjlock = lockptr;
}
#ifndef SMALL_KERNEL
/*
* uvm_obj_wire: wire the pages of entire UVM object.
*
* => NOTE: this function should only be used for types of objects
* where PG_RELEASED flag is never set (aobj objects)
* => caller must pass page-aligned start and end values
* => if the caller passes in a pageq pointer, we'll return a list of
* wired pages.
*/
int
uvm_obj_wire(struct uvm_object *uobj, voff_t start, voff_t end,
struct pglist *pageq)
{
int i, npages, left, error;
struct vm_page *pgs[FETCH_PAGECOUNT];
voff_t offset = start;
left = (end - start) >> PAGE_SHIFT;
rw_enter(uobj->vmobjlock, RW_WRITE | RW_DUPOK);
while (left) {
npages = MIN(FETCH_PAGECOUNT, left);
/* Get the pages */
memset(pgs, 0, sizeof(pgs));
error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, 0,
PROT_READ | PROT_WRITE, MADV_SEQUENTIAL,
PGO_ALLPAGES | PGO_SYNCIO);
if (error)
goto error;
rw_enter(uobj->vmobjlock, RW_WRITE | RW_DUPOK);
for (i = 0; i < npages; i++) {
KASSERT(pgs[i] != NULL);
KASSERT(!(pgs[i]->pg_flags & PG_RELEASED));
if (pgs[i]->pg_flags & PQ_AOBJ) {
atomic_clearbits_int(&pgs[i]->pg_flags,
PG_CLEAN);
uao_dropswap(uobj, i);
}
}
/* Wire the pages */
uvm_lock_pageq();
for (i = 0; i < npages; i++) {
uvm_pagewire(pgs[i]);
if (pageq != NULL)
TAILQ_INSERT_TAIL(pageq, pgs[i], pageq);
}
uvm_unlock_pageq();
/* Unbusy the pages */
uvm_page_unbusy(pgs, npages);
left -= npages;
offset += (voff_t)npages << PAGE_SHIFT;
}
rw_exit(uobj->vmobjlock);
return 0;
error:
/* Unwire the pages which have been wired */
uvm_obj_unwire(uobj, start, offset);
return error;
}
/*
* uvm_obj_unwire: unwire the pages of entire UVM object.
*
* => caller must pass page-aligned start and end values
*/
void
uvm_obj_unwire(struct uvm_object *uobj, voff_t start, voff_t end)
{
struct vm_page *pg;
off_t offset;
rw_enter(uobj->vmobjlock, RW_WRITE | RW_DUPOK);
uvm_lock_pageq();
for (offset = start; offset < end; offset += PAGE_SIZE) {
pg = uvm_pagelookup(uobj, offset);
KASSERT(pg != NULL);
KASSERT(!(pg->pg_flags & PG_RELEASED));
uvm_pageunwire(pg);
}
uvm_unlock_pageq();
rw_exit(uobj->vmobjlock);
}
#endif /* !SMALL_KERNEL */
/*
* uvm_obj_free: free all pages in a uvm object, used by the buffer
* cache to free all pages attached to a buffer.
*/
void
uvm_obj_free(struct uvm_object *uobj)
{
struct vm_page *pg;
struct pglist pgl;
KASSERT(UVM_OBJ_IS_BUFCACHE(uobj));
KERNEL_ASSERT_LOCKED();
TAILQ_INIT(&pgl);
/*
* Extract from rb tree in offset order. The phys addresses
* usually increase in that order, which is better for
* uvm_pglistfree().
*/
RBT_FOREACH(pg, uvm_objtree, &uobj->memt) {
/*
* clear PG_TABLED so we don't do work to remove
* this pg from the uobj we are throwing away
*/
atomic_clearbits_int(&pg->pg_flags, PG_TABLED);
uvm_lock_pageq();
uvm_pageclean(pg);
uvm_unlock_pageq();
TAILQ_INSERT_TAIL(&pgl, pg, pageq);
}
uvm_pglistfree(&pgl);
}
8
1
2
3
1
2
1
2
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/* $OpenBSD: kern_acct.c,v 1.47 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_acct.c,v 1.42 1996/02/04 02:15:12 christos Exp $ */
/*-
* Copyright (c) 1994 Christopher G. Demetriou
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_acct.c 8.1 (Berkeley) 6/14/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/namei.h>
#include <sys/errno.h>
#include <sys/acct.h>
#include <sys/resourcevar.h>
#include <sys/tty.h>
#include <sys/kthread.h>
#include <sys/rwlock.h>
#include <sys/syscallargs.h>
/*
* The routines implemented in this file are described in:
* Leffler, et al.: The Design and Implementation of the 4.3BSD
* UNIX Operating System (Addison Welley, 1989)
* on pages 62-63.
*
* Arguably, to simplify accounting operations, this mechanism should
* be replaced by one in which an accounting log file (similar to /dev/klog)
* is read by a user process, etc. However, that has its own problems.
*/
/*
* Internal accounting functions.
*/
comp_t encode_comp_t(u_long, u_long);
int acct_start(void);
void acct_thread(void *);
void acct_shutdown(void);
/*
* Accounting vnode pointer, and saved vnode pointer.
*/
struct vnode *acctp;
struct vnode *savacctp;
/*
* Lock protecting acctp and savacctp.
*/
struct rwlock acct_lock = RWLOCK_INITIALIZER("acctlk");
/*
* Values associated with enabling and disabling accounting
*/
int acctsuspend = 2; /* stop accounting when < 2% free space left */
int acctresume = 4; /* resume when free space risen to > 4% */
int acctrate = 15; /* delay (in seconds) between space checks */
struct proc *acct_proc;
/*
* Accounting system call. Written based on the specification and
* previous implementation done by Mark Tinguely.
*/
int
sys_acct(struct proc *p, void *v, register_t *retval)
{
struct sys_acct_args /* {
syscallarg(const char *) path;
} */ *uap = v;
struct nameidata nd;
int error;
/* Make sure that the caller is root. */
if ((error = suser(p)) != 0)
return (error);
/*
* If accounting is to be started to a file, open that file for
* writing and make sure it's 'normal'.
*/
if (SCARG(uap, path) != NULL) {
NDINIT(&nd, 0, 0, UIO_USERSPACE, SCARG(uap, path), p);
if ((error = vn_open(&nd, FWRITE|O_APPEND, 0)) != 0)
return (error);
VOP_UNLOCK(nd.ni_vp);
if (nd.ni_vp->v_type != VREG) {
vn_close(nd.ni_vp, FWRITE, p->p_ucred, p);
return (EACCES);
}
}
rw_enter_write(&acct_lock);
/*
* If accounting was previously enabled, kill the old space-watcher,
* close the file, and (if no new file was specified, leave).
*/
if (acctp != NULL || savacctp != NULL) {
wakeup(&acct_proc);
(void)vn_close((acctp != NULL ? acctp : savacctp), FWRITE,
p->p_ucred, p);
acctp = savacctp = NULL;
}
if (SCARG(uap, path) == NULL)
goto out;
/*
* Save the new accounting file vnode, and schedule the new
* free space watcher.
*/
acctp = nd.ni_vp;
if ((error = acct_start()) != 0) {
acctp = NULL;
(void)vn_close(nd.ni_vp, FWRITE, p->p_ucred, p);
}
out:
rw_exit_write(&acct_lock);
return (error);
}
/*
* Write out process accounting information, on process exit.
* Data to be written out is specified in Leffler, et al.
* and are enumerated below. (They're also noted in the system
* "acct.h" header file.)
*/
int
acct_process(struct proc *p)
{
struct acct acct;
struct process *pr = p->p_p;
struct rusage *r;
struct timespec booted, elapsed, realstart, st, tmp, uptime, ut;
int t;
struct vnode *vp;
int error = 0;
/* If accounting isn't enabled, don't bother */
if (acctp == NULL)
return (0);
rw_enter_read(&acct_lock);
/*
* Check the vnode again in case accounting got disabled while waiting
* for the lock.
*/
vp = acctp;
if (vp == NULL)
goto out;
/*
* Get process accounting information.
*/
/* (1) The name of the command that ran */
memcpy(acct.ac_comm, pr->ps_comm, sizeof acct.ac_comm);
/* (2) The amount of user and system time that was used */
calctsru(&pr->ps_tu, &ut, &st, NULL);
acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_nsec);
acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_nsec);
/* (3) The elapsed time the command ran (and its starting time) */
nanouptime(&uptime);
nanoboottime(&booted);
timespecadd(&booted, &pr->ps_start, &realstart);
acct.ac_btime = realstart.tv_sec;
timespecsub(&uptime, &pr->ps_start, &elapsed);
acct.ac_etime = encode_comp_t(elapsed.tv_sec, elapsed.tv_nsec);
/* (4) The average amount of memory used */
r = &p->p_ru;
timespecadd(&ut, &st, &tmp);
t = tmp.tv_sec * hz + tmp.tv_nsec / (1000 * tick);
if (t)
acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t;
else
acct.ac_mem = 0;
/* (5) The number of disk I/O operations done */
acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0);
/* (6) The UID and GID of the process */
acct.ac_uid = pr->ps_ucred->cr_ruid;
acct.ac_gid = pr->ps_ucred->cr_rgid;
/* (7) The terminal from which the process was started */
if ((pr->ps_flags & PS_CONTROLT) &&
pr->ps_pgrp->pg_session->s_ttyp)
acct.ac_tty = pr->ps_pgrp->pg_session->s_ttyp->t_dev;
else
acct.ac_tty = -1;
/* (8) The boolean flags that tell how process terminated or misbehaved. */
acct.ac_flag = pr->ps_acflag;
/* Extensions */
acct.ac_pid = pr->ps_pid;
/*
* Now, just write the accounting information to the file.
*/
error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&acct, sizeof (acct),
(off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT|IO_NOLIMIT,
p->p_ucred, NULL, p);
out:
rw_exit_read(&acct_lock);
return (error);
}
/*
* Encode_comp_t converts from ticks in seconds and microseconds
* to ticks in 1/AHZ seconds. The encoding is described in
* Leffler, et al., on page 63.
*/
#define MANTSIZE 13 /* 13 bit mantissa. */
#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */
#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */
comp_t
encode_comp_t(u_long s, u_long ns)
{
int exp, rnd;
exp = 0;
rnd = 0;
s *= AHZ;
s += ns / (1000000000 / AHZ); /* Maximize precision. */
while (s > MAXFRACT) {
rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */
s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */
exp++;
}
/* If we need to round up, do it (and handle overflow correctly). */
if (rnd && (++s > MAXFRACT)) {
s >>= EXPSIZE;
exp++;
}
/* Clean it up and polish it off. */
exp <<= MANTSIZE; /* Shift the exponent into place */
exp += s; /* and add on the mantissa. */
return (exp);
}
int
acct_start(void)
{
/* Already running. */
if (acct_proc != NULL)
return (0);
return (kthread_create(acct_thread, NULL, &acct_proc, "acct"));
}
/*
* Periodically check the file system to see if accounting
* should be turned on or off. Beware the case where the vnode
* has been vgone()'d out from underneath us, e.g. when the file
* system containing the accounting file has been forcibly unmounted.
*/
void
acct_thread(void *arg)
{
struct statfs sb;
struct proc *p = curproc;
rw_enter_write(&acct_lock);
for (;;) {
if (savacctp != NULL) {
if (savacctp->v_type == VBAD) {
(void) vn_close(savacctp, FWRITE, NOCRED, p);
savacctp = NULL;
break;
}
(void)VFS_STATFS(savacctp->v_mount, &sb, NULL);
if (sb.f_bavail > acctresume * sb.f_blocks / 100) {
acctp = savacctp;
savacctp = NULL;
log(LOG_NOTICE, "Accounting resumed\n");
}
} else if (acctp != NULL) {
if (acctp->v_type == VBAD) {
(void) vn_close(acctp, FWRITE, NOCRED, p);
acctp = NULL;
break;
}
(void)VFS_STATFS(acctp->v_mount, &sb, NULL);
if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) {
savacctp = acctp;
acctp = NULL;
log(LOG_NOTICE, "Accounting suspended\n");
}
} else {
break;
}
rwsleep_nsec(&acct_proc, &acct_lock, PPAUSE, "acct",
SEC_TO_NSEC(acctrate));
}
acct_proc = NULL;
rw_exit_write(&acct_lock);
kthread_exit(0);
}
void
acct_shutdown(void)
{
struct proc *p = curproc;
rw_enter_write(&acct_lock);
if (acctp != NULL || savacctp != NULL) {
vn_close((acctp != NULL ? acctp : savacctp), FWRITE,
NOCRED, p);
acctp = savacctp = NULL;
}
rw_exit_write(&acct_lock);
}
8
1
7
22
4
5
5
4
4
4
4
4
4
1
1
22
2
16
5
15
1
3
2
2
8
20
18
1
1
3
11
4
20
2
12
6
4
14
5
9
5
9
5
6
3
31
15
15
24
7
9
22
16
3
9
3
15
6
6
8
15
1
36
1
2
18
9
9
3
27
4
4
6
5
4
2
2
2
50
50
50
50
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
/* $OpenBSD: kern_time.c,v 1.157 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_time.c,v 1.20 1996/02/18 11:57:06 fvdl Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_time.c 8.4 (Berkeley) 5/26/95
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/proc.h>
#include <sys/ktrace.h>
#include <sys/signalvar.h>
#include <sys/stdint.h>
#include <sys/pledge.h>
#include <sys/task.h>
#include <sys/timeout.h>
#include <sys/timetc.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <dev/clock_subr.h>
int itimerfix(struct itimerval *);
/*
* Time of day and interval timer support.
*
* These routines provide the kernel entry points to get and set
* the time-of-day and per-process interval timers. Subroutines
* here provide support for adding and subtracting timeval structures
* and decrementing interval timers, optionally reloading the interval
* timers when they expire.
*/
/* This function is used by clock_settime and settimeofday */
int
settime(const struct timespec *ts)
{
struct timespec now;
/*
* Don't allow the time to be set forward so far it will wrap
* and become negative, thus allowing an attacker to bypass
* the next check below. The cutoff is 1 year before rollover
* occurs, so even if the attacker uses adjtime(2) to move
* the time past the cutoff, it will take a very long time
* to get to the wrap point.
*
* XXX: we check against UINT_MAX until we can figure out
* how to deal with the hardware RTCs.
*/
if (ts->tv_sec > UINT_MAX - 365*24*60*60) {
printf("denied attempt to set clock forward to %lld\n",
(long long)ts->tv_sec);
return (EPERM);
}
/*
* If the system is secure, we do not allow the time to be
* set to an earlier value (it may be slowed using adjtime,
* but not set back). This feature prevent interlopers from
* setting arbitrary time stamps on files.
*/
nanotime(&now);
if (securelevel > 1 && timespeccmp(ts, &now, <=)) {
printf("denied attempt to set clock back %lld seconds\n",
(long long)now.tv_sec - ts->tv_sec);
return (EPERM);
}
tc_setrealtimeclock(ts);
KERNEL_LOCK();
resettodr();
KERNEL_UNLOCK();
return (0);
}
int
clock_gettime(struct proc *p, clockid_t clock_id, struct timespec *tp)
{
struct proc *q;
int error = 0;
switch (clock_id) {
case CLOCK_REALTIME:
nanotime(tp);
break;
case CLOCK_UPTIME:
nanoruntime(tp);
break;
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
nanouptime(tp);
break;
case CLOCK_PROCESS_CPUTIME_ID:
nanouptime(tp);
timespecsub(tp, &curcpu()->ci_schedstate.spc_runtime, tp);
timespecadd(tp, &p->p_p->ps_tu.tu_runtime, tp);
timespecadd(tp, &p->p_rtime, tp);
break;
case CLOCK_THREAD_CPUTIME_ID:
nanouptime(tp);
timespecsub(tp, &curcpu()->ci_schedstate.spc_runtime, tp);
timespecadd(tp, &p->p_tu.tu_runtime, tp);
timespecadd(tp, &p->p_rtime, tp);
break;
default:
/* check for clock from pthread_getcpuclockid() */
if (__CLOCK_TYPE(clock_id) == CLOCK_THREAD_CPUTIME_ID) {
KERNEL_LOCK();
q = tfind(__CLOCK_PTID(clock_id) - THREAD_PID_OFFSET);
if (q == NULL || q->p_p != p->p_p)
error = ESRCH;
else
*tp = q->p_tu.tu_runtime;
KERNEL_UNLOCK();
} else
error = EINVAL;
break;
}
return (error);
}
int
sys_clock_gettime(struct proc *p, void *v, register_t *retval)
{
struct sys_clock_gettime_args /* {
syscallarg(clockid_t) clock_id;
syscallarg(struct timespec *) tp;
} */ *uap = v;
struct timespec ats;
int error;
memset(&ats, 0, sizeof(ats));
if ((error = clock_gettime(p, SCARG(uap, clock_id), &ats)) != 0)
return (error);
error = copyout(&ats, SCARG(uap, tp), sizeof(ats));
#ifdef KTRACE
if (error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrabstimespec(p, &ats);
#endif
return (error);
}
int
sys_clock_settime(struct proc *p, void *v, register_t *retval)
{
struct sys_clock_settime_args /* {
syscallarg(clockid_t) clock_id;
syscallarg(const struct timespec *) tp;
} */ *uap = v;
struct timespec ats;
clockid_t clock_id;
int error;
if ((error = suser(p)) != 0)
return (error);
if ((error = copyin(SCARG(uap, tp), &ats, sizeof(ats))) != 0)
return (error);
clock_id = SCARG(uap, clock_id);
switch (clock_id) {
case CLOCK_REALTIME:
if (!timespecisvalid(&ats))
return (EINVAL);
if ((error = settime(&ats)) != 0)
return (error);
break;
default: /* Other clocks are read-only */
return (EINVAL);
}
return (0);
}
int
sys_clock_getres(struct proc *p, void *v, register_t *retval)
{
struct sys_clock_getres_args /* {
syscallarg(clockid_t) clock_id;
syscallarg(struct timespec *) tp;
} */ *uap = v;
clockid_t clock_id;
struct bintime bt;
struct timespec ts;
struct proc *q;
u_int64_t scale;
int error = 0, realstathz;
memset(&ts, 0, sizeof(ts));
realstathz = (stathz == 0) ? hz : stathz;
clock_id = SCARG(uap, clock_id);
switch (clock_id) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
case CLOCK_UPTIME:
memset(&bt, 0, sizeof(bt));
rw_enter_read(&tc_lock);
scale = ((1ULL << 63) / tc_getfrequency()) * 2;
bt.frac = tc_getprecision() * scale;
rw_exit_read(&tc_lock);
BINTIME_TO_TIMESPEC(&bt, &ts);
break;
case CLOCK_PROCESS_CPUTIME_ID:
case CLOCK_THREAD_CPUTIME_ID:
ts.tv_nsec = 1000000000 / realstathz;
break;
default:
/* check for clock from pthread_getcpuclockid() */
if (__CLOCK_TYPE(clock_id) == CLOCK_THREAD_CPUTIME_ID) {
KERNEL_LOCK();
q = tfind(__CLOCK_PTID(clock_id) - THREAD_PID_OFFSET);
if (q == NULL || q->p_p != p->p_p)
error = ESRCH;
else
ts.tv_nsec = 1000000000 / realstathz;
KERNEL_UNLOCK();
} else
error = EINVAL;
break;
}
if (error == 0 && SCARG(uap, tp)) {
ts.tv_nsec = MAX(ts.tv_nsec, 1);
error = copyout(&ts, SCARG(uap, tp), sizeof(ts));
#ifdef KTRACE
if (error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
}
return error;
}
int
sys_nanosleep(struct proc *p, void *v, register_t *retval)
{
static int chan;
struct sys_nanosleep_args/* {
syscallarg(const struct timespec *) rqtp;
syscallarg(struct timespec *) rmtp;
} */ *uap = v;
struct timespec elapsed, remainder, request, start, stop;
uint64_t nsecs;
struct timespec *rmtp;
int copyout_error, error;
rmtp = SCARG(uap, rmtp);
error = copyin(SCARG(uap, rqtp), &request, sizeof(request));
if (error)
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &request);
#endif
if (request.tv_sec < 0 || !timespecisvalid(&request))
return (EINVAL);
do {
getnanouptime(&start);
nsecs = MAX(1, MIN(TIMESPEC_TO_NSEC(&request), MAXTSLP));
error = tsleep_nsec(&chan, PWAIT | PCATCH, "nanoslp", nsecs);
getnanouptime(&stop);
timespecsub(&stop, &start, &elapsed);
timespecsub(&request, &elapsed, &request);
if (request.tv_sec < 0)
timespecclear(&request);
if (error != EWOULDBLOCK)
break;
} while (timespecisset(&request));
if (error == ERESTART)
error = EINTR;
if (error == EWOULDBLOCK)
error = 0;
if (rmtp) {
memset(&remainder, 0, sizeof(remainder));
remainder = request;
copyout_error = copyout(&remainder, rmtp, sizeof(remainder));
if (copyout_error)
error = copyout_error;
#ifdef KTRACE
if (copyout_error == 0 && KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &remainder);
#endif
}
return error;
}
int
sys_gettimeofday(struct proc *p, void *v, register_t *retval)
{
struct sys_gettimeofday_args /* {
syscallarg(struct timeval *) tp;
syscallarg(struct timezone *) tzp;
} */ *uap = v;
struct timeval atv;
static const struct timezone zerotz = { 0, 0 };
struct timeval *tp;
struct timezone *tzp;
int error = 0;
tp = SCARG(uap, tp);
tzp = SCARG(uap, tzp);
if (tp) {
memset(&atv, 0, sizeof(atv));
microtime(&atv);
if ((error = copyout(&atv, tp, sizeof (atv))))
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrabstimeval(p, &atv);
#endif
}
if (tzp)
error = copyout(&zerotz, tzp, sizeof(zerotz));
return (error);
}
int
sys_settimeofday(struct proc *p, void *v, register_t *retval)
{
struct sys_settimeofday_args /* {
syscallarg(const struct timeval *) tv;
syscallarg(const struct timezone *) tzp;
} */ *uap = v;
struct timezone atz;
struct timeval atv;
const struct timeval *tv;
const struct timezone *tzp;
int error;
tv = SCARG(uap, tv);
tzp = SCARG(uap, tzp);
if ((error = suser(p)))
return (error);
/* Verify all parameters before changing time. */
if (tv && (error = copyin(tv, &atv, sizeof(atv))))
return (error);
if (tzp && (error = copyin(tzp, &atz, sizeof(atz))))
return (error);
if (tv) {
struct timespec ts;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrabstimeval(p, &atv);
#endif
if (!timerisvalid(&atv))
return (EINVAL);
TIMEVAL_TO_TIMESPEC(&atv, &ts);
if ((error = settime(&ts)) != 0)
return (error);
}
return (0);
}
#define ADJFREQ_MAX (500000000LL << 32)
#define ADJFREQ_MIN (-ADJFREQ_MAX)
int
sys_adjfreq(struct proc *p, void *v, register_t *retval)
{
struct sys_adjfreq_args /* {
syscallarg(const int64_t *) freq;
syscallarg(int64_t *) oldfreq;
} */ *uap = v;
int error = 0;
int64_t f, oldf;
const int64_t *freq = SCARG(uap, freq);
int64_t *oldfreq = SCARG(uap, oldfreq);
if (freq) {
if ((error = suser(p)))
return (error);
if ((error = copyin(freq, &f, sizeof(f))))
return (error);
if (f < ADJFREQ_MIN || f > ADJFREQ_MAX)
return (EINVAL);
}
rw_enter(&tc_lock, (freq == NULL) ? RW_READ : RW_WRITE);
if (oldfreq) {
tc_adjfreq(&oldf, NULL);
if ((error = copyout(&oldf, oldfreq, sizeof(oldf))))
goto out;
}
if (freq)
tc_adjfreq(NULL, &f);
out:
rw_exit(&tc_lock);
return (error);
}
int
sys_adjtime(struct proc *p, void *v, register_t *retval)
{
struct sys_adjtime_args /* {
syscallarg(const struct timeval *) delta;
syscallarg(struct timeval *) olddelta;
} */ *uap = v;
struct timeval atv;
const struct timeval *delta = SCARG(uap, delta);
struct timeval *olddelta = SCARG(uap, olddelta);
int64_t adjustment, remaining;
int error;
error = pledge_adjtime(p, delta);
if (error)
return error;
if (delta) {
if ((error = suser(p)))
return (error);
if ((error = copyin(delta, &atv, sizeof(struct timeval))))
return (error);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimeval(p, &atv);
#endif
if (!timerisvalid(&atv))
return (EINVAL);
if (atv.tv_sec > INT64_MAX / 1000000)
return EINVAL;
if (atv.tv_sec < INT64_MIN / 1000000)
return EINVAL;
adjustment = atv.tv_sec * 1000000;
if (adjustment > INT64_MAX - atv.tv_usec)
return EINVAL;
adjustment += atv.tv_usec;
rw_enter_write(&tc_lock);
}
if (olddelta) {
tc_adjtime(&remaining, NULL);
memset(&atv, 0, sizeof(atv));
atv.tv_sec = remaining / 1000000;
atv.tv_usec = remaining % 1000000;
if (atv.tv_usec < 0) {
atv.tv_usec += 1000000;
atv.tv_sec--;
}
if ((error = copyout(&atv, olddelta, sizeof(struct timeval))))
goto out;
}
if (delta)
tc_adjtime(NULL, &adjustment);
out:
if (delta)
rw_exit_write(&tc_lock);
return (error);
}
struct mutex itimer_mtx = MUTEX_INITIALIZER(IPL_CLOCK);
/*
* Get or set value of an interval timer. The process virtual and
* profiling virtual time timers are kept internally in the
* way they are specified externally: in time until they expire.
*
* The real time interval timer's it_value, in contrast, is kept as an
* absolute time rather than as a delta, so that it is easy to keep
* periodic real-time signals from drifting.
*
* Virtual time timers are processed in the hardclock() routine of
* kern_clock.c. The real time timer is processed by a timeout
* routine, called from the softclock() routine. Since a callout
* may be delayed in real time due to interrupt processing in the system,
* it is possible for the real time timeout routine (realitexpire, given below),
* to be delayed in real time past when it is supposed to occur. It
* does not suffice, therefore, to reload the real timer .it_value from the
* real time timers .it_interval. Rather, we compute the next time in
* absolute time the timer should go off.
*/
void
setitimer(int which, const struct itimerval *itv, struct itimerval *olditv)
{
struct itimerspec its, oldits;
struct timespec now;
struct itimerspec *itimer;
struct process *pr;
KASSERT(which >= ITIMER_REAL && which <= ITIMER_PROF);
pr = curproc->p_p;
itimer = &pr->ps_timer[which];
if (itv != NULL) {
TIMEVAL_TO_TIMESPEC(&itv->it_value, &its.it_value);
TIMEVAL_TO_TIMESPEC(&itv->it_interval, &its.it_interval);
}
if (which == ITIMER_REAL) {
mtx_enter(&pr->ps_mtx);
nanouptime(&now);
} else
mtx_enter(&itimer_mtx);
if (olditv != NULL)
oldits = *itimer;
if (itv != NULL) {
if (which == ITIMER_REAL) {
if (timespecisset(&its.it_value)) {
timespecadd(&its.it_value, &now, &its.it_value);
timeout_at_ts(&pr->ps_realit_to, &its.it_value);
} else
timeout_del(&pr->ps_realit_to);
}
*itimer = its;
}
if (which == ITIMER_REAL)
mtx_leave(&pr->ps_mtx);
else
mtx_leave(&itimer_mtx);
if (olditv != NULL) {
if (which == ITIMER_REAL && timespecisset(&oldits.it_value)) {
if (timespeccmp(&oldits.it_value, &now, <))
timespecclear(&oldits.it_value);
else {
timespecsub(&oldits.it_value, &now,
&oldits.it_value);
}
}
TIMESPEC_TO_TIMEVAL(&olditv->it_value, &oldits.it_value);
TIMESPEC_TO_TIMEVAL(&olditv->it_interval, &oldits.it_interval);
}
}
void
cancel_all_itimers(void)
{
struct itimerval itv;
int i;
timerclear(&itv.it_value);
timerclear(&itv.it_interval);
for (i = 0; i < nitems(curproc->p_p->ps_timer); i++)
setitimer(i, &itv, NULL);
}
int
sys_getitimer(struct proc *p, void *v, register_t *retval)
{
struct sys_getitimer_args /* {
syscallarg(int) which;
syscallarg(struct itimerval *) itv;
} */ *uap = v;
struct itimerval aitv;
int which;
which = SCARG(uap, which);
if (which < ITIMER_REAL || which > ITIMER_PROF)
return EINVAL;
memset(&aitv, 0, sizeof(aitv));
setitimer(which, NULL, &aitv);
return copyout(&aitv, SCARG(uap, itv), sizeof(aitv));
}
int
sys_setitimer(struct proc *p, void *v, register_t *retval)
{
struct sys_setitimer_args /* {
syscallarg(int) which;
syscallarg(const struct itimerval *) itv;
syscallarg(struct itimerval *) oitv;
} */ *uap = v;
struct itimerval aitv, olditv;
struct itimerval *newitvp, *olditvp;
int error, which;
which = SCARG(uap, which);
if (which < ITIMER_REAL || which > ITIMER_PROF)
return EINVAL;
newitvp = olditvp = NULL;
if (SCARG(uap, itv) != NULL) {
error = copyin(SCARG(uap, itv), &aitv, sizeof(aitv));
if (error)
return error;
error = itimerfix(&aitv);
if (error)
return error;
newitvp = &aitv;
}
if (SCARG(uap, oitv) != NULL) {
memset(&olditv, 0, sizeof(olditv));
olditvp = &olditv;
}
if (newitvp == NULL && olditvp == NULL)
return 0;
setitimer(which, newitvp, olditvp);
if (SCARG(uap, oitv) != NULL)
return copyout(&olditv, SCARG(uap, oitv), sizeof(olditv));
return 0;
}
/*
* Real interval timer expired:
* send process whose timer expired an alarm signal.
* If time is not set up to reload, then just return.
* Else compute next time timer should go off which is > current time.
* This is where delay in processing this timeout causes multiple
* SIGALRM calls to be compressed into one.
*/
void
realitexpire(void *arg)
{
struct timespec cts;
struct process *pr = arg;
struct itimerspec *tp = &pr->ps_timer[ITIMER_REAL];
int need_signal = 0;
mtx_enter(&pr->ps_mtx);
/*
* Do nothing if the timer was cancelled or rescheduled while we
* were entering the mutex.
*/
if (!timespecisset(&tp->it_value) || timeout_pending(&pr->ps_realit_to))
goto out;
/* The timer expired. We need to send the signal. */
need_signal = 1;
/* One-shot timers are not reloaded. */
if (!timespecisset(&tp->it_interval)) {
timespecclear(&tp->it_value);
goto out;
}
/*
* Find the nearest future expiration point and restart
* the timeout.
*/
nanouptime(&cts);
while (timespeccmp(&tp->it_value, &cts, <=))
timespecadd(&tp->it_value, &tp->it_interval, &tp->it_value);
if ((pr->ps_flags & PS_EXITING) == 0)
timeout_at_ts(&pr->ps_realit_to, &tp->it_value);
out:
mtx_leave(&pr->ps_mtx);
if (need_signal)
prsignal(pr, SIGALRM);
}
/*
* Check if the given setitimer(2) input is valid. Clear it_interval
* if it_value is unset. Round it_interval up to the minimum interval
* if necessary.
*/
int
itimerfix(struct itimerval *itv)
{
static const struct timeval max = { .tv_sec = UINT_MAX, .tv_usec = 0 };
struct timeval min_interval = { .tv_sec = 0, .tv_usec = tick };
if (itv->it_value.tv_sec < 0 || !timerisvalid(&itv->it_value))
return EINVAL;
if (timercmp(&itv->it_value, &max, >))
return EINVAL;
if (itv->it_interval.tv_sec < 0 || !timerisvalid(&itv->it_interval))
return EINVAL;
if (timercmp(&itv->it_interval, &max, >))
return EINVAL;
if (!timerisset(&itv->it_value))
timerclear(&itv->it_interval);
if (timerisset(&itv->it_interval)) {
if (timercmp(&itv->it_interval, &min_interval, <))
itv->it_interval = min_interval;
}
return 0;
}
/*
* Decrement an interval timer by the given number of nanoseconds.
* If the timer expires and it is periodic then reload it. When reloading
* the timer we subtract any overrun from the next period so that the timer
* does not drift.
*/
int
itimerdecr(struct itimerspec *itp, long nsec)
{
struct timespec decrement;
NSEC_TO_TIMESPEC(nsec, &decrement);
mtx_enter(&itimer_mtx);
/*
* Double-check that the timer is enabled. A different thread
* in setitimer(2) may have disabled it while we were entering
* the mutex.
*/
if (!timespecisset(&itp->it_value)) {
mtx_leave(&itimer_mtx);
return (1);
}
/*
* The timer is enabled. Update and reload it as needed.
*/
timespecsub(&itp->it_value, &decrement, &itp->it_value);
if (itp->it_value.tv_sec >= 0 && timespecisset(&itp->it_value)) {
mtx_leave(&itimer_mtx);
return (1);
}
if (!timespecisset(&itp->it_interval)) {
timespecclear(&itp->it_value);
mtx_leave(&itimer_mtx);
return (0);
}
while (itp->it_value.tv_sec < 0 || !timespecisset(&itp->it_value))
timespecadd(&itp->it_value, &itp->it_interval, &itp->it_value);
mtx_leave(&itimer_mtx);
return (0);
}
struct mutex ratecheck_mtx = MUTEX_INITIALIZER(IPL_HIGH);
/*
* ratecheck(): simple time-based rate-limit checking. see ratecheck(9)
* for usage and rationale.
*/
int
ratecheck(struct timeval *lasttime, const struct timeval *mininterval)
{
struct timeval tv, delta;
int rv = 0;
getmicrouptime(&tv);
mtx_enter(&ratecheck_mtx);
timersub(&tv, lasttime, &delta);
/*
* check for 0,0 is so that the message will be seen at least once,
* even if interval is huge.
*/
if (timercmp(&delta, mininterval, >=) ||
(lasttime->tv_sec == 0 && lasttime->tv_usec == 0)) {
*lasttime = tv;
rv = 1;
}
mtx_leave(&ratecheck_mtx);
return (rv);
}
struct mutex ppsratecheck_mtx = MUTEX_INITIALIZER(IPL_HIGH);
/*
* ppsratecheck(): packets (or events) per second limitation.
*/
int
ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps)
{
struct timeval tv, delta;
int rv;
microuptime(&tv);
mtx_enter(&ppsratecheck_mtx);
timersub(&tv, lasttime, &delta);
/*
* check for 0,0 is so that the message will be seen at least once.
* if more than one second have passed since the last update of
* lasttime, reset the counter.
*
* we do increment *curpps even in *curpps < maxpps case, as some may
* try to use *curpps for stat purposes as well.
*/
if (maxpps == 0)
rv = 0;
else if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) ||
delta.tv_sec >= 1) {
*lasttime = tv;
*curpps = 0;
rv = 1;
} else if (maxpps < 0)
rv = 1;
else if (*curpps < maxpps)
rv = 1;
else
rv = 0;
/* be careful about wrap-around */
if (*curpps + 1 > *curpps)
*curpps = *curpps + 1;
mtx_leave(&ppsratecheck_mtx);
return (rv);
}
todr_chip_handle_t todr_handle;
int inittodr_done;
#define MINYEAR ((OpenBSD / 100) - 1) /* minimum plausible year */
/*
* inittodr:
*
* Initialize time from the time-of-day register.
*/
void
inittodr(time_t base)
{
time_t deltat;
struct timeval rtctime;
struct timespec ts;
int badbase;
inittodr_done = 1;
if (base < (MINYEAR - 1970) * SECYR) {
printf("WARNING: preposterous time in file system\n");
/* read the system clock anyway */
base = (MINYEAR - 1970) * SECYR;
badbase = 1;
} else
badbase = 0;
rtctime.tv_sec = base;
rtctime.tv_usec = 0;
if (todr_handle == NULL ||
todr_gettime(todr_handle, &rtctime) != 0 ||
rtctime.tv_sec < (MINYEAR - 1970) * SECYR) {
/*
* Believe the time in the file system for lack of
* anything better, resetting the TODR.
*/
rtctime.tv_sec = base;
rtctime.tv_usec = 0;
if (todr_handle != NULL && !badbase)
printf("WARNING: bad clock chip time\n");
ts.tv_sec = rtctime.tv_sec;
ts.tv_nsec = rtctime.tv_usec * 1000;
tc_setclock(&ts);
goto bad;
} else {
ts.tv_sec = rtctime.tv_sec;
ts.tv_nsec = rtctime.tv_usec * 1000;
tc_setclock(&ts);
}
if (!badbase) {
/*
* See if we gained/lost two or more days; if
* so, assume something is amiss.
*/
deltat = rtctime.tv_sec - base;
if (deltat < 0)
deltat = -deltat;
if (deltat < 2 * SECDAY)
return; /* all is well */
#ifndef SMALL_KERNEL
printf("WARNING: clock %s %lld days\n",
rtctime.tv_sec < base ? "lost" : "gained",
(long long)(deltat / SECDAY));
#endif
}
bad:
printf("WARNING: CHECK AND RESET THE DATE!\n");
}
/*
* resettodr:
*
* Reset the time-of-day register with the current time.
*/
void
resettodr(void)
{
struct timeval rtctime;
/*
* Skip writing the RTC if inittodr(9) never ran. We don't
* want to overwrite a reasonable value with a nonsense value.
*/
if (!inittodr_done)
return;
microtime(&rtctime);
if (todr_handle != NULL &&
todr_settime(todr_handle, &rtctime) != 0)
printf("WARNING: can't update clock chip time\n");
}
void
todr_attach(struct todr_chip_handle *todr)
{
todr_handle = todr;
}
#define RESETTODR_PERIOD 1800
void periodic_resettodr(void *);
void perform_resettodr(void *);
struct timeout resettodr_to = TIMEOUT_INITIALIZER(periodic_resettodr, NULL);
struct task resettodr_task = TASK_INITIALIZER(perform_resettodr, NULL);
void
periodic_resettodr(void *arg __unused)
{
task_add(systq, &resettodr_task);
}
void
perform_resettodr(void *arg __unused)
{
resettodr();
timeout_add_sec(&resettodr_to, RESETTODR_PERIOD);
}
void
start_periodic_resettodr(void)
{
timeout_add_sec(&resettodr_to, RESETTODR_PERIOD);
}
void
stop_periodic_resettodr(void)
{
timeout_del(&resettodr_to);
task_del(systq, &resettodr_task);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
/* $OpenBSD: ugen.c,v 1.116 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: ugen.c,v 1.63 2002/11/26 18:49:48 christos Exp $ */
/* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.26 1999/11/17 22:33:41 n_hibma Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/fcntl.h>
#include <sys/selinfo.h>
#include <sys/vnode.h>
#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdivar.h>
#ifdef UGEN_DEBUG
#define DPRINTF(x) do { if (ugendebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (ugendebug>(n)) printf x; } while (0)
int ugendebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define UGEN_CHUNK 128 /* chunk size for read */
#define UGEN_IBSIZE 1020 /* buffer size */
#define UGEN_BBSIZE 1024
#define UGEN_NISOFRAMES 500 /* 0.5 seconds worth */
#define UGEN_NISOREQS 6 /* number of outstanding xfer requests */
#define UGEN_NISORFRMS 4 /* number of frames (milliseconds) per req */
struct ugen_endpoint {
struct ugen_softc *sc;
usb_endpoint_descriptor_t *edesc;
struct usbd_interface *iface;
int state;
#define UGEN_ASLP 0x02 /* waiting for data */
#define UGEN_SHORT_OK 0x04 /* short xfers are OK */
struct usbd_pipe *pipeh;
struct clist q;
struct selinfo rsel;
u_char *ibuf; /* start of buffer (circular for isoc) */
size_t ibuflen;
u_char *fill; /* location for input (isoc) */
u_char *limit; /* end of circular buffer (isoc) */
u_char *cur; /* current read location (isoc) */
u_int32_t timeout;
struct isoreq {
struct ugen_endpoint *sce;
struct usbd_xfer *xfer;
void *dmabuf;
u_int16_t sizes[UGEN_NISORFRMS];
} isoreqs[UGEN_NISOREQS];
};
struct ugen_softc {
struct device sc_dev; /* base device */
struct usbd_device *sc_udev;
char sc_is_open[USB_MAX_ENDPOINTS];
struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];
#define OUT 0
#define IN 1
int sc_refcnt;
u_char sc_secondary;
};
void ugenintr(struct usbd_xfer *, void *, usbd_status);
void ugen_isoc_rintr(struct usbd_xfer *, void *, usbd_status);
int ugen_do_read(struct ugen_softc *, int, struct uio *, int);
int ugen_do_write(struct ugen_softc *, int, struct uio *, int);
int ugen_do_ioctl(struct ugen_softc *, int, u_long, caddr_t, int,
struct proc *);
int ugen_do_close(struct ugen_softc *, int, int);
int ugen_set_config(struct ugen_softc *, int);
int ugen_set_interface(struct ugen_softc *, int, int);
int ugen_get_alt_index(struct ugen_softc *, int);
void ugen_clear_iface_eps(struct ugen_softc *, struct usbd_interface *);
#define UGENUNIT(n) ((minor(n) >> 4) & 0xf)
#define UGENENDPOINT(n) (minor(n) & 0xf)
#define UGENDEV(u, e) (makedev(0, ((u) << 4) | (e)))
int ugen_match(struct device *, void *, void *);
void ugen_attach(struct device *, struct device *, void *);
int ugen_detach(struct device *, int);
struct cfdriver ugen_cd = {
NULL, "ugen", DV_DULL
};
const struct cfattach ugen_ca = {
sizeof(struct ugen_softc), ugen_match, ugen_attach, ugen_detach
};
int
ugen_match(struct device *parent, void *match, void *aux)
{
struct usb_attach_arg *uaa = aux;
if (uaa->usegeneric) {
return (UMATCH_GENERIC);
} else
return (UMATCH_NONE);
}
void
ugen_attach(struct device *parent, struct device *self, void *aux)
{
struct ugen_softc *sc = (struct ugen_softc *)self;
struct usb_attach_arg *uaa = aux;
struct usbd_device *udev;
usbd_status err;
int conf;
sc->sc_udev = udev = uaa->device;
if (usbd_get_devcnt(udev) > 0)
sc->sc_secondary = 1;
if (!sc->sc_secondary) {
/* First set configuration index 0, the default one for ugen. */
err = usbd_set_config_index(udev, 0, 0);
if (err) {
printf("%s: setting configuration index 0 failed\n",
sc->sc_dev.dv_xname);
usbd_deactivate(sc->sc_udev);
return;
}
}
conf = usbd_get_config_descriptor(udev)->bConfigurationValue;
/* Set up all the local state for this configuration. */
err = ugen_set_config(sc, conf);
if (err) {
printf("%s: setting configuration %d failed\n",
sc->sc_dev.dv_xname, conf);
usbd_deactivate(sc->sc_udev);
return;
}
}
int
ugen_set_config(struct ugen_softc *sc, int configno)
{
struct usbd_device *dev = sc->sc_udev;
usb_config_descriptor_t *cdesc;
usb_interface_descriptor_t *id;
struct usbd_interface *iface;
usb_endpoint_descriptor_t *ed;
struct ugen_endpoint *sce;
int ifaceno, endptno, endpt;
int err, dir;
DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
sc->sc_dev.dv_xname, configno, sc));
/*
* We start at 1, not 0, because we don't care whether the
* control endpoint is open or not. It is always present.
*/
for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++)
if (sc->sc_is_open[endptno]) {
DPRINTFN(1,
("ugen_set_config: %s - endpoint %d is open\n",
sc->sc_dev.dv_xname, endptno));
return (USBD_IN_USE);
}
/* Avoid setting the current value. */
cdesc = usbd_get_config_descriptor(dev);
if (cdesc == NULL || cdesc->bConfigurationValue != configno) {
if (sc->sc_secondary) {
printf("%s: secondary, not changing config to %d\n",
__func__, configno);
return (USBD_IN_USE);
} else {
err = usbd_set_config_no(dev, configno, 1);
if (err)
return (err);
cdesc = usbd_get_config_descriptor(dev);
if (cdesc == NULL ||
cdesc->bConfigurationValue != configno)
return (USBD_INVAL);
}
}
memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
for (ifaceno = 0; ifaceno < cdesc->bNumInterfaces; ifaceno++) {
DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
if (usbd_iface_claimed(sc->sc_udev, ifaceno)) {
DPRINTF(("%s: iface %d not available\n", __func__,
ifaceno));
continue;
}
err = usbd_device2interface_handle(dev, ifaceno, &iface);
if (err)
return (err);
id = usbd_get_interface_descriptor(iface);
for (endptno = 0; endptno < id->bNumEndpoints; endptno++) {
ed = usbd_interface2endpoint_descriptor(iface,endptno);
endpt = ed->bEndpointAddress;
dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"
"(%d,%d), sce=%p\n",
endptno, endpt, UE_GET_ADDR(endpt),
UE_GET_DIR(endpt), sce));
sce->sc = sc;
sce->edesc = ed;
sce->iface = iface;
}
}
return (0);
}
int
ugenopen(dev_t dev, int flag, int mode, struct proc *p)
{
struct ugen_softc *sc;
int unit = UGENUNIT(dev);
int endpt = UGENENDPOINT(dev);
usb_endpoint_descriptor_t *edesc;
struct ugen_endpoint *sce;
int dir, isize;
usbd_status err;
struct usbd_xfer *xfer;
void *buf;
int i, j;
if (unit >= ugen_cd.cd_ndevs)
return (ENXIO);
sc = ugen_cd.cd_devs[unit];
if (sc == NULL)
return (ENXIO);
DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
flag, mode, unit, endpt));
if (sc == NULL || usbd_is_dying(sc->sc_udev))
return (ENXIO);
if (sc->sc_is_open[endpt])
return (EBUSY);
if (endpt == USB_CONTROL_ENDPOINT) {
sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
return (0);
}
/* Make sure there are pipes for all directions. */
for (dir = OUT; dir <= IN; dir++) {
if (flag & (dir == OUT ? FWRITE : FREAD)) {
sce = &sc->sc_endpoints[endpt][dir];
if (sce == 0 || sce->edesc == 0)
return (ENXIO);
}
}
/* Actually open the pipes. */
/* XXX Should back out properly if it fails. */
for (dir = OUT; dir <= IN; dir++) {
if (!(flag & (dir == OUT ? FWRITE : FREAD)))
continue;
sce = &sc->sc_endpoints[endpt][dir];
sce->state = 0;
sce->timeout = USBD_NO_TIMEOUT;
DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n",
sc, endpt, dir, sce));
edesc = sce->edesc;
/* Clear device endpoint toggle. */
ugen_clear_iface_eps(sc, sce->iface);
switch (UE_GET_XFERTYPE(edesc->bmAttributes)) {
case UE_INTERRUPT:
if (dir == OUT) {
err = usbd_open_pipe(sce->iface,
edesc->bEndpointAddress, 0, &sce->pipeh);
if (err)
return (EIO);
break;
}
isize = UGETW(edesc->wMaxPacketSize);
if (isize == 0) /* shouldn't happen */
return (EINVAL);
sce->ibuflen = isize;
sce->ibuf = malloc(sce->ibuflen, M_USBDEV, M_WAITOK);
DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
endpt, isize));
clalloc(&sce->q, UGEN_IBSIZE, 0);
err = usbd_open_pipe_intr(sce->iface,
edesc->bEndpointAddress,
USBD_SHORT_XFER_OK, &sce->pipeh, sce,
sce->ibuf, isize, ugenintr,
USBD_DEFAULT_INTERVAL);
if (err) {
free(sce->ibuf, M_USBDEV, sce->ibuflen);
clfree(&sce->q);
return (EIO);
}
/* Clear HC endpoint toggle. */
usbd_clear_endpoint_toggle(sce->pipeh);
DPRINTFN(5, ("ugenopen: interrupt open done\n"));
break;
case UE_BULK:
err = usbd_open_pipe(sce->iface,
edesc->bEndpointAddress, 0, &sce->pipeh);
if (err)
return (EIO);
/* Clear HC endpoint toggle. */
usbd_clear_endpoint_toggle(sce->pipeh);
break;
case UE_ISOCHRONOUS:
if (dir == OUT)
return (EINVAL);
isize = UGETW(edesc->wMaxPacketSize);
if (isize == 0) /* shouldn't happen */
return (EINVAL);
sce->ibuflen = isize * UGEN_NISOFRAMES;
sce->ibuf = mallocarray(isize, UGEN_NISOFRAMES,
M_USBDEV, M_WAITOK);
sce->cur = sce->fill = sce->ibuf;
sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES;
DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n",
endpt, isize));
err = usbd_open_pipe(sce->iface,
edesc->bEndpointAddress, 0, &sce->pipeh);
if (err) {
free(sce->ibuf, M_USBDEV, sce->ibuflen);
return (EIO);
}
for(i = 0; i < UGEN_NISOREQS; ++i) {
sce->isoreqs[i].sce = sce;
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == 0)
goto bad;
sce->isoreqs[i].xfer = xfer;
buf = usbd_alloc_buffer
(xfer, isize * UGEN_NISORFRMS);
if (buf == 0) {
i++;
goto bad;
}
sce->isoreqs[i].dmabuf = buf;
for(j = 0; j < UGEN_NISORFRMS; ++j)
sce->isoreqs[i].sizes[j] = isize;
usbd_setup_isoc_xfer(xfer, sce->pipeh,
&sce->isoreqs[i], sce->isoreqs[i].sizes,
UGEN_NISORFRMS, USBD_NO_COPY |
USBD_SHORT_XFER_OK, ugen_isoc_rintr);
(void)usbd_transfer(xfer);
}
DPRINTFN(5, ("ugenopen: isoc open done\n"));
break;
bad:
while (--i >= 0) /* implicit buffer free */
usbd_free_xfer(sce->isoreqs[i].xfer);
return (ENOMEM);
case UE_CONTROL:
sce->timeout = USBD_DEFAULT_TIMEOUT;
return (EINVAL);
}
}
sc->sc_is_open[endpt] = 1;
return (0);
}
int
ugenclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct ugen_softc *sc = ugen_cd.cd_devs[UGENUNIT(dev)];
int endpt = UGENENDPOINT(dev);
int error;
if (sc == NULL || usbd_is_dying(sc->sc_udev))
return (EIO);
DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
flag, mode, UGENUNIT(dev), endpt));
sc->sc_refcnt++;
error = ugen_do_close(sc, endpt, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ugen_do_close(struct ugen_softc *sc, int endpt, int flag)
{
struct ugen_endpoint *sce;
int dir, i;
#ifdef DIAGNOSTIC
if (!sc->sc_is_open[endpt]) {
printf("ugenclose: not open\n");
return (EINVAL);
}
#endif
if (endpt == USB_CONTROL_ENDPOINT) {
DPRINTFN(5, ("ugenclose: close control\n"));
sc->sc_is_open[endpt] = 0;
return (0);
}
for (dir = OUT; dir <= IN; dir++) {
if (!(flag & (dir == OUT ? FWRITE : FREAD)))
continue;
sce = &sc->sc_endpoints[endpt][dir];
if (sce == NULL || sce->pipeh == NULL)
continue;
DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
endpt, dir, sce));
usbd_close_pipe(sce->pipeh);
sce->pipeh = NULL;
switch (UE_GET_XFERTYPE(sce->edesc->bmAttributes)) {
case UE_INTERRUPT:
ndflush(&sce->q, sce->q.c_cc);
clfree(&sce->q);
break;
case UE_ISOCHRONOUS:
for (i = 0; i < UGEN_NISOREQS; ++i)
usbd_free_xfer(sce->isoreqs[i].xfer);
default:
break;
}
if (sce->ibuf != NULL) {
free(sce->ibuf, M_USBDEV, sce->ibuflen);
sce->ibuf = NULL;
}
}
sc->sc_is_open[endpt] = 0;
return (0);
}
int
ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
{
struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN];
u_int32_t tn;
size_t n;
char buf[UGEN_BBSIZE];
struct usbd_xfer *xfer;
usbd_status err;
int s;
int flags, error = 0;
u_char buffer[UGEN_CHUNK];
DPRINTFN(5, ("%s: ugenread: %d\n", sc->sc_dev.dv_xname, endpt));
if (usbd_is_dying(sc->sc_udev))
return (EIO);
if (endpt == USB_CONTROL_ENDPOINT)
return (ENODEV);
#ifdef DIAGNOSTIC
if (sce->edesc == NULL) {
printf("ugenread: no edesc\n");
return (EIO);
}
if (sce->pipeh == NULL) {
printf("ugenread: no pipe\n");
return (EIO);
}
#endif
switch (UE_GET_XFERTYPE(sce->edesc->bmAttributes)) {
case UE_INTERRUPT:
/* Block until activity occurred. */
s = splusb();
while (sce->q.c_cc == 0) {
if (flag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
sce->state |= UGEN_ASLP;
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
error = tsleep_nsec(sce, PZERO | PCATCH, "ugenrintr",
MSEC_TO_NSEC(sce->timeout));
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
if (usbd_is_dying(sc->sc_udev))
error = EIO;
if (error == EWOULDBLOCK) { /* timeout, return 0 */
error = 0;
break;
}
if (error)
break;
}
splx(s);
/* Transfer as many chunks as possible. */
while (sce->q.c_cc > 0 && uio->uio_resid > 0 && !error) {
n = ulmin(sce->q.c_cc, uio->uio_resid);
if (n > sizeof(buffer))
n = sizeof(buffer);
/* Remove a small chunk from the input queue. */
q_to_b(&sce->q, buffer, n);
DPRINTFN(5, ("ugenread: got %zu chars\n", n));
/* Copy the data to the user process. */
error = uiomove(buffer, n, uio);
if (error)
break;
}
break;
case UE_BULK:
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == 0)
return (ENOMEM);
flags = USBD_SYNCHRONOUS;
if (sce->state & UGEN_SHORT_OK)
flags |= USBD_SHORT_XFER_OK;
if (sce->timeout == 0)
flags |= USBD_CATCH;
while ((n = ulmin(UGEN_BBSIZE, uio->uio_resid)) != 0) {
DPRINTFN(1, ("ugenread: start transfer %zu bytes\n",n));
usbd_setup_xfer(xfer, sce->pipeh, 0, buf, n,
flags, sce->timeout, NULL);
err = usbd_transfer(xfer);
if (err) {
usbd_clear_endpoint_stall(sce->pipeh);
if (err == USBD_INTERRUPTED)
error = EINTR;
else if (err == USBD_TIMEOUT)
error = ETIMEDOUT;
else
error = EIO;
break;
}
usbd_get_xfer_status(xfer, NULL, NULL, &tn, NULL);
DPRINTFN(1, ("ugenread: got %u bytes\n", tn));
error = uiomove(buf, tn, uio);
if (error || tn < n)
break;
}
usbd_free_xfer(xfer);
break;
case UE_ISOCHRONOUS:
s = splusb();
while (sce->cur == sce->fill) {
if (flag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
sce->state |= UGEN_ASLP;
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
error = tsleep_nsec(sce, PZERO | PCATCH, "ugenriso",
MSEC_TO_NSEC(sce->timeout));
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
if (usbd_is_dying(sc->sc_udev))
error = EIO;
if (error == EWOULDBLOCK) { /* timeout, return 0 */
error = 0;
break;
}
if (error)
break;
}
while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
if(sce->fill > sce->cur)
n = ulmin(sce->fill - sce->cur, uio->uio_resid);
else
n = ulmin(sce->limit - sce->cur, uio->uio_resid);
DPRINTFN(5, ("ugenread: isoc got %zu chars\n", n));
/* Copy the data to the user process. */
error = uiomove(sce->cur, n, uio);
if (error)
break;
sce->cur += n;
if(sce->cur >= sce->limit)
sce->cur = sce->ibuf;
}
splx(s);
break;
default:
return (ENXIO);
}
return (error);
}
int
ugenread(dev_t dev, struct uio *uio, int flag)
{
int endpt = UGENENDPOINT(dev);
struct ugen_softc *sc;
int error;
sc = ugen_cd.cd_devs[UGENUNIT(dev)];
sc->sc_refcnt++;
error = ugen_do_read(sc, endpt, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
{
struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT];
size_t n;
int flags, error = 0;
char buf[UGEN_BBSIZE];
struct usbd_xfer *xfer;
usbd_status err;
DPRINTFN(5, ("%s: ugenwrite: %d\n", sc->sc_dev.dv_xname, endpt));
if (usbd_is_dying(sc->sc_udev))
return (EIO);
if (endpt == USB_CONTROL_ENDPOINT)
return (ENODEV);
#ifdef DIAGNOSTIC
if (sce->edesc == NULL) {
printf("ugenwrite: no edesc\n");
return (EIO);
}
if (sce->pipeh == NULL) {
printf("ugenwrite: no pipe\n");
return (EIO);
}
#endif
flags = USBD_SYNCHRONOUS;
if (sce->timeout == 0)
flags |= USBD_CATCH;
switch (UE_GET_XFERTYPE(sce->edesc->bmAttributes)) {
case UE_BULK:
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == 0)
return (EIO);
while ((n = ulmin(UGEN_BBSIZE, uio->uio_resid)) != 0) {
error = uiomove(buf, n, uio);
if (error)
break;
DPRINTFN(1, ("ugenwrite: transfer %zu bytes\n", n));
usbd_setup_xfer(xfer, sce->pipeh, 0, buf, n,
flags, sce->timeout, NULL);
err = usbd_transfer(xfer);
if (err) {
usbd_clear_endpoint_stall(sce->pipeh);
if (err == USBD_INTERRUPTED)
error = EINTR;
else if (err == USBD_TIMEOUT)
error = ETIMEDOUT;
else
error = EIO;
break;
}
}
usbd_free_xfer(xfer);
break;
case UE_INTERRUPT:
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == 0)
return (EIO);
while ((n = ulmin(UGETW(sce->edesc->wMaxPacketSize),
uio->uio_resid)) != 0) {
error = uiomove(buf, n, uio);
if (error)
break;
DPRINTFN(1, ("ugenwrite: transfer %zu bytes\n", n));
usbd_setup_xfer(xfer, sce->pipeh, 0, buf, n,
flags, sce->timeout, NULL);
err = usbd_transfer(xfer);
if (err) {
usbd_clear_endpoint_stall(sce->pipeh);
if (err == USBD_INTERRUPTED)
error = EINTR;
else if (err == USBD_TIMEOUT)
error = ETIMEDOUT;
else
error = EIO;
break;
}
}
usbd_free_xfer(xfer);
break;
default:
return (ENXIO);
}
return (error);
}
int
ugenwrite(dev_t dev, struct uio *uio, int flag)
{
int endpt = UGENENDPOINT(dev);
struct ugen_softc *sc;
int error;
sc = ugen_cd.cd_devs[UGENUNIT(dev)];
sc->sc_refcnt++;
error = ugen_do_write(sc, endpt, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ugen_detach(struct device *self, int flags)
{
struct ugen_softc *sc = (struct ugen_softc *)self;
struct ugen_endpoint *sce;
int i, dir, endptno;
int s, maj, mn;
DPRINTF(("ugen_detach: sc=%p flags=%d\n", sc, flags));
/* Abort all pipes. Causes processes waiting for transfer to wake. */
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
for (dir = OUT; dir <= IN; dir++) {
sce = &sc->sc_endpoints[i][dir];
if (sce && sce->pipeh)
usbd_abort_pipe(sce->pipeh);
}
}
s = splusb();
if (--sc->sc_refcnt >= 0) {
/* Wake everyone */
for (i = 0; i < USB_MAX_ENDPOINTS; i++)
wakeup(&sc->sc_endpoints[i][IN]);
/* Wait for processes to go away. */
usb_detach_wait(&sc->sc_dev);
}
splx(s);
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == ugenopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit * USB_MAX_ENDPOINTS;
vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
for (endptno = 0; endptno < USB_MAX_ENDPOINTS; endptno++) {
if (sc->sc_is_open[endptno])
ugen_do_close(sc, endptno, FREAD|FWRITE);
}
return (0);
}
void
ugenintr(struct usbd_xfer *xfer, void *addr, usbd_status status)
{
struct ugen_endpoint *sce = addr;
/*struct ugen_softc *sc = sce->sc;*/
u_int32_t count;
u_char *ibuf;
if (status == USBD_CANCELLED)
return;
if (status != USBD_NORMAL_COMPLETION) {
DPRINTF(("ugenintr: status=%d\n", status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sce->pipeh);
return;
}
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
ibuf = sce->ibuf;
DPRINTFN(5, ("ugenintr: xfer=%p status=%d count=%d\n",
xfer, status, count));
DPRINTFN(5, (" data = %02x %02x %02x\n",
ibuf[0], ibuf[1], ibuf[2]));
(void)b_to_q(ibuf, count, &sce->q);
if (sce->state & UGEN_ASLP) {
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugen_intr: waking %p\n", sce));
wakeup(sce);
}
selwakeup(&sce->rsel);
}
void
ugen_isoc_rintr(struct usbd_xfer *xfer, void *addr, usbd_status status)
{
struct isoreq *req = addr;
struct ugen_endpoint *sce = req->sce;
u_int32_t count, n;
int i, isize;
/* Return if we are aborting. */
if (status == USBD_CANCELLED)
return;
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
DPRINTFN(5,("%s: xfer %ld, count=%d\n", __func__, req - sce->isoreqs,
count));
/* throw away oldest input if the buffer is full */
if(sce->fill < sce->cur && sce->cur <= sce->fill + count) {
sce->cur += count;
if(sce->cur >= sce->limit)
sce->cur = sce->ibuf + (sce->limit - sce->cur);
DPRINTFN(5, ("%s: throwing away %d bytes\n", __func__, count));
}
isize = UGETW(sce->edesc->wMaxPacketSize);
for (i = 0; i < UGEN_NISORFRMS; i++) {
u_int32_t actlen = req->sizes[i];
char const *buf = (char const *)req->dmabuf + isize * i;
/* copy data to buffer */
while (actlen > 0) {
n = min(actlen, sce->limit - sce->fill);
memcpy(sce->fill, buf, n);
buf += n;
actlen -= n;
sce->fill += n;
if(sce->fill == sce->limit)
sce->fill = sce->ibuf;
}
/* setup size for next transfer */
req->sizes[i] = isize;
}
usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS,
USBD_NO_COPY | USBD_SHORT_XFER_OK, ugen_isoc_rintr);
(void)usbd_transfer(xfer);
if (sce->state & UGEN_ASLP) {
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce));
wakeup(sce);
}
selwakeup(&sce->rsel);
}
int
ugen_set_interface(struct ugen_softc *sc, int ifaceno, int altno)
{
struct usbd_interface *iface;
usb_config_descriptor_t *cdesc;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
struct ugen_endpoint *sce;
uint8_t endptno, endpt;
int dir, err;
DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceno, altno));
cdesc = usbd_get_config_descriptor(sc->sc_udev);
if (ifaceno < 0 || ifaceno >= cdesc->bNumInterfaces ||
usbd_iface_claimed(sc->sc_udev, ifaceno))
return (USBD_INVAL);
err = usbd_device2interface_handle(sc->sc_udev, ifaceno, &iface);
if (err)
return (err);
id = usbd_get_interface_descriptor(iface);
for (endptno = 0; endptno < id->bNumEndpoints; endptno++) {
ed = usbd_interface2endpoint_descriptor(iface,endptno);
endpt = ed->bEndpointAddress;
dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
sce->sc = 0;
sce->edesc = 0;
sce->iface = 0;
}
/* Try to change setting, if this fails put back the descriptors. */
err = usbd_set_interface(iface, altno);
id = usbd_get_interface_descriptor(iface);
for (endptno = 0; endptno < id->bNumEndpoints; endptno++) {
ed = usbd_interface2endpoint_descriptor(iface,endptno);
endpt = ed->bEndpointAddress;
dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
sce->sc = sc;
sce->edesc = ed;
sce->iface = iface;
}
return (err);
}
int
ugen_get_alt_index(struct ugen_softc *sc, int ifaceno)
{
struct usbd_interface *iface;
usbd_status err;
err = usbd_device2interface_handle(sc->sc_udev, ifaceno, &iface);
if (err)
return (-1);
return (usbd_get_interface_altindex(iface));
}
int
ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, caddr_t addr,
int flag, struct proc *p)
{
struct ugen_endpoint *sce;
int err, cdesc_len;
struct usbd_interface *iface;
struct usb_config_desc *cd;
usb_config_descriptor_t *cdesc;
struct usb_interface_desc *id;
usb_interface_descriptor_t *idesc;
struct usb_endpoint_desc *ed;
usb_endpoint_descriptor_t *edesc;
struct usb_alt_interface *ai;
u_int8_t conf, alt;
DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd));
if (usbd_is_dying(sc->sc_udev))
return (EIO);
switch (cmd) {
case FIONBIO:
/* All handled in the upper FS layer. */
return (0);
case USB_SET_SHORT_XFER:
if (endpt == USB_CONTROL_ENDPOINT)
return (EINVAL);
/* This flag only affects read */
sce = &sc->sc_endpoints[endpt][IN];
if (sce == NULL || sce->pipeh == NULL)
return (EINVAL);
if (*(int *)addr)
sce->state |= UGEN_SHORT_OK;
else
sce->state &= ~UGEN_SHORT_OK;
return (0);
case USB_SET_TIMEOUT:
sce = &sc->sc_endpoints[endpt][IN];
if (sce == NULL)
return (EINVAL);
sce->timeout = *(int *)addr;
sce = &sc->sc_endpoints[endpt][OUT];
if (sce == NULL)
return (EINVAL);
sce->timeout = *(int *)addr;
return (0);
default:
break;
}
if (endpt != USB_CONTROL_ENDPOINT)
return (EINVAL);
switch (cmd) {
#ifdef UGEN_DEBUG
case USB_SETDEBUG:
ugendebug = *(int *)addr;
break;
#endif
case USB_GET_CONFIG:
err = usbd_get_config(sc->sc_udev, &conf);
if (err)
return (EIO);
*(int *)addr = conf;
break;
case USB_SET_CONFIG:
if (!(flag & FWRITE))
return (EPERM);
err = ugen_set_config(sc, *(int *)addr);
switch (err) {
case USBD_NORMAL_COMPLETION:
break;
case USBD_IN_USE:
return (EBUSY);
default:
return (EIO);
}
break;
case USB_GET_ALTINTERFACE:
ai = (struct usb_alt_interface *)addr;
err = usbd_device2interface_handle(sc->sc_udev,
ai->uai_interface_index, &iface);
if (err)
return (EINVAL);
idesc = usbd_get_interface_descriptor(iface);
if (idesc == NULL)
return (EIO);
ai->uai_alt_no = idesc->bAlternateSetting;
break;
case USB_SET_ALTINTERFACE:
if (!(flag & FWRITE))
return (EPERM);
ai = (struct usb_alt_interface *)addr;
err = usbd_device2interface_handle(sc->sc_udev,
ai->uai_interface_index, &iface);
if (err)
return (EINVAL);
err = ugen_set_interface(sc, ai->uai_interface_index,
ai->uai_alt_no);
if (err)
return (EINVAL);
break;
case USB_GET_NO_ALT:
ai = (struct usb_alt_interface *)addr;
cdesc = usbd_get_cdesc(sc->sc_udev, ai->uai_config_index,
&cdesc_len);
if (cdesc == NULL)
return (EINVAL);
idesc = usbd_find_idesc(cdesc, ai->uai_interface_index, 0);
if (idesc == NULL) {
free(cdesc, M_TEMP, UGETW(cdesc->wTotalLength));
return (EINVAL);
}
ai->uai_alt_no = usbd_get_no_alts(cdesc,
idesc->bInterfaceNumber);
free(cdesc, M_TEMP, cdesc_len);
break;
case USB_GET_DEVICE_DESC:
*(usb_device_descriptor_t *)addr =
*usbd_get_device_descriptor(sc->sc_udev);
break;
case USB_GET_CONFIG_DESC:
cd = (struct usb_config_desc *)addr;
cdesc = usbd_get_cdesc(sc->sc_udev, cd->ucd_config_index,
&cdesc_len);
if (cdesc == NULL)
return (EINVAL);
cd->ucd_desc = *cdesc;
free(cdesc, M_TEMP, cdesc_len);
break;
case USB_GET_INTERFACE_DESC:
id = (struct usb_interface_desc *)addr;
cdesc = usbd_get_cdesc(sc->sc_udev, id->uid_config_index,
&cdesc_len);
if (cdesc == NULL)
return (EINVAL);
if (id->uid_config_index == USB_CURRENT_CONFIG_INDEX &&
id->uid_alt_index == USB_CURRENT_ALT_INDEX)
alt = ugen_get_alt_index(sc, id->uid_interface_index);
else
alt = id->uid_alt_index;
idesc = usbd_find_idesc(cdesc, id->uid_interface_index, alt);
if (idesc == NULL) {
free(cdesc, M_TEMP, UGETW(cdesc->wTotalLength));
return (EINVAL);
}
id->uid_desc = *idesc;
free(cdesc, M_TEMP, cdesc_len);
break;
case USB_GET_ENDPOINT_DESC:
ed = (struct usb_endpoint_desc *)addr;
cdesc = usbd_get_cdesc(sc->sc_udev, ed->ued_config_index,
&cdesc_len);
if (cdesc == NULL)
return (EINVAL);
if (ed->ued_config_index == USB_CURRENT_CONFIG_INDEX &&
ed->ued_alt_index == USB_CURRENT_ALT_INDEX)
alt = ugen_get_alt_index(sc, ed->ued_interface_index);
else
alt = ed->ued_alt_index;
edesc = usbd_find_edesc(cdesc, ed->ued_interface_index,
alt, ed->ued_endpoint_index);
if (edesc == NULL) {
free(cdesc, M_TEMP, UGETW(cdesc->wTotalLength));
return (EINVAL);
}
ed->ued_desc = *edesc;
free(cdesc, M_TEMP, cdesc_len);
break;
case USB_GET_FULL_DESC:
{
u_int len;
struct iovec iov;
struct uio uio;
struct usb_full_desc *fd = (struct usb_full_desc *)addr;
int error;
cdesc = usbd_get_cdesc(sc->sc_udev, fd->ufd_config_index,
&cdesc_len);
if (cdesc == NULL)
return (EINVAL);
len = cdesc_len;
if (len > fd->ufd_size)
len = fd->ufd_size;
iov.iov_base = (caddr_t)fd->ufd_data;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_resid = len;
uio.uio_offset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = p;
error = uiomove((void *)cdesc, len, &uio);
free(cdesc, M_TEMP, cdesc_len);
return (error);
}
case USB_DO_REQUEST:
{
struct usb_ctl_request *ur = (void *)addr;
size_t len = UGETW(ur->ucr_request.wLength), mlen;
struct iovec iov;
struct uio uio;
void *ptr = NULL;
int error = 0;
if (!(flag & FWRITE))
return (EPERM);
/* Avoid requests that would damage the bus integrity. */
if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
ur->ucr_request.bRequest == UR_SET_ADDRESS) ||
(ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
ur->ucr_request.bRequest == UR_SET_CONFIG) ||
(ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE &&
ur->ucr_request.bRequest == UR_SET_INTERFACE))
return (EINVAL);
if (len > 32767)
return (EINVAL);
if (len != 0) {
iov.iov_base = (caddr_t)ur->ucr_data;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_resid = len;
uio.uio_offset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw =
ur->ucr_request.bmRequestType & UT_READ ?
UIO_READ : UIO_WRITE;
uio.uio_procp = p;
if ((ptr = malloc(len, M_TEMP, M_NOWAIT)) == NULL) {
error = ENOMEM;
goto ret;
}
if (uio.uio_rw == UIO_WRITE) {
error = uiomove(ptr, len, &uio);
if (error)
goto ret;
}
}
sce = &sc->sc_endpoints[endpt][IN];
err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request,
ptr, ur->ucr_flags, &ur->ucr_actlen, sce->timeout);
if (err) {
error = EIO;
goto ret;
}
/* Only if USBD_SHORT_XFER_OK is set. */
mlen = len;
if (mlen > ur->ucr_actlen)
mlen = ur->ucr_actlen;
if (mlen != 0) {
if (uio.uio_rw == UIO_READ) {
error = uiomove(ptr, mlen, &uio);
if (error)
goto ret;
}
}
ret:
free(ptr, M_TEMP, len);
return (error);
}
case USB_GET_DEVICEINFO:
usbd_fill_deviceinfo(sc->sc_udev,
(struct usb_device_info *)addr);
break;
default:
return (EINVAL);
}
return (0);
}
int
ugenioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
int endpt = UGENENDPOINT(dev);
struct ugen_softc *sc;
int error;
sc = ugen_cd.cd_devs[UGENUNIT(dev)];
sc->sc_refcnt++;
error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
void filt_ugenrdetach(struct knote *);
int filt_ugenread_intr(struct knote *, long);
int filt_ugenread_isoc(struct knote *, long);
int ugenkqfilter(dev_t, struct knote *);
void
filt_ugenrdetach(struct knote *kn)
{
struct ugen_endpoint *sce = (void *)kn->kn_hook;
int s;
s = splusb();
klist_remove_locked(&sce->rsel.si_note, kn);
splx(s);
}
int
filt_ugenread_intr(struct knote *kn, long hint)
{
struct ugen_endpoint *sce = (void *)kn->kn_hook;
kn->kn_data = sce->q.c_cc;
return (kn->kn_data > 0);
}
int
filt_ugenread_isoc(struct knote *kn, long hint)
{
struct ugen_endpoint *sce = (void *)kn->kn_hook;
if (sce->cur == sce->fill)
return (0);
if (sce->cur < sce->fill)
kn->kn_data = sce->fill - sce->cur;
else
kn->kn_data = (sce->limit - sce->cur) +
(sce->fill - sce->ibuf);
return (1);
}
const struct filterops ugenread_intr_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ugenrdetach,
.f_event = filt_ugenread_intr,
};
const struct filterops ugenread_isoc_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ugenrdetach,
.f_event = filt_ugenread_isoc,
};
int
ugenkqfilter(dev_t dev, struct knote *kn)
{
struct ugen_softc *sc;
struct ugen_endpoint *sce;
struct klist *klist;
int s;
sc = ugen_cd.cd_devs[UGENUNIT(dev)];
if (usbd_is_dying(sc->sc_udev))
return (ENXIO);
/* XXX always IN */
sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
if (sce == NULL)
return (ENXIO);
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sce->rsel.si_note;
switch (UE_GET_XFERTYPE(sce->edesc->bmAttributes)) {
case UE_INTERRUPT:
kn->kn_fop = &ugenread_intr_filtops;
break;
case UE_ISOCHRONOUS:
kn->kn_fop = &ugenread_isoc_filtops;
break;
case UE_BULK:
/*
* We have no easy way of determining if a read will
* yield any data or a write will happen.
*/
return (seltrue_kqfilter(dev, kn));
default:
return (EINVAL);
}
break;
case EVFILT_WRITE:
klist = &sce->rsel.si_note;
switch (UE_GET_XFERTYPE(sce->edesc->bmAttributes)) {
case UE_INTERRUPT:
case UE_ISOCHRONOUS:
/* XXX poll doesn't support this */
return (EINVAL);
case UE_BULK:
/*
* We have no easy way of determining if a read will
* yield any data or a write will happen.
*/
return (seltrue_kqfilter(dev, kn));
default:
return (EINVAL);
}
break;
default:
return (EINVAL);
}
kn->kn_hook = (void *)sce;
s = splusb();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
void
ugen_clear_iface_eps(struct ugen_softc *sc, struct usbd_interface *iface)
{
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
uint8_t xfertype;
int i;
/* Only clear interface endpoints when none are in use. */
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
if (i == USB_CONTROL_ENDPOINT)
continue;
if (sc->sc_is_open[i] != 0)
return;
}
DPRINTFN(1,("%s: clear interface eps\n", __func__));
id = usbd_get_interface_descriptor(iface);
if (id == NULL)
goto bad;
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(iface, i);
if (ed == NULL)
goto bad;
xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
if (xfertype == UE_BULK || xfertype == UE_INTERRUPT) {
if (usbd_clear_endpoint_feature(sc->sc_udev,
ed->bEndpointAddress, UF_ENDPOINT_HALT))
goto bad;
}
}
return;
bad:
printf("%s: clear endpoints failed!\n", __func__);
}
58
34
4
38
37
6
15
21
15
58
58
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
/* $OpenBSD: kern_exec.c,v 1.231 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $ */
/*-
* Copyright (C) 1993, 1994 Christopher G. Demetriou
* Copyright (C) 1992 Wolfgang Solfrank.
* Copyright (C) 1992 TooLs GmbH.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/acct.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>
#include <sys/ktrace.h>
#include <sys/resourcevar.h>
#include <sys/mman.h>
#include <sys/signalvar.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/pledge.h>
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
#include <machine/tcb.h>
#include <sys/timetc.h>
struct uvm_object *sigobject; /* shared sigcode object */
struct uvm_object *timekeep_object;
struct timekeep *timekeep;
void unveil_destroy(struct process *ps);
const struct kmem_va_mode kv_exec = {
.kv_wait = 1,
.kv_map = &exec_map
};
/*
* Map the shared signal code.
*/
int exec_sigcode_map(struct process *);
/*
* Map the shared timekeep page.
*/
int exec_timekeep_map(struct process *);
/*
* If non-zero, stackgap_random specifies the upper limit of the random gap size
* added to the fixed stack position. Must be n^2.
*/
int stackgap_random = STACKGAP_RANDOM;
/*
* check exec:
* given an "executable" described in the exec package's namei info,
* see what we can do with it.
*
* ON ENTRY:
* exec package with appropriate namei info
* proc pointer of exec'ing proc
* NO SELF-LOCKED VNODES
*
* ON EXIT:
* error: nothing held, etc. exec header still allocated.
* ok: filled exec package, one locked vnode.
*
* EXEC SWITCH ENTRY:
* Locked vnode to check, exec package, proc.
*
* EXEC SWITCH EXIT:
* ok: return 0, filled exec package, one locked vnode.
* error: destructive:
* everything deallocated except exec header.
* non-destructive:
* error code, locked vnode, exec header unmodified
*/
int
check_exec(struct proc *p, struct exec_package *epp)
{
int error, i;
struct vnode *vp;
struct nameidata *ndp;
size_t resid;
ndp = epp->ep_ndp;
ndp->ni_cnd.cn_nameiop = LOOKUP;
ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF | SAVENAME;
if (epp->ep_flags & EXEC_INDIR)
ndp->ni_cnd.cn_flags |= BYPASSUNVEIL;
/* first get the vnode */
if ((error = namei(ndp)) != 0)
return (error);
epp->ep_vp = vp = ndp->ni_vp;
/* check for regular file */
if (vp->v_type != VREG) {
error = EACCES;
goto bad1;
}
/* get attributes */
if ((error = VOP_GETATTR(vp, epp->ep_vap, p->p_ucred, p)) != 0)
goto bad1;
/* Check mount point */
if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
error = EACCES;
goto bad1;
}
/* SUID programs may not be started with execpromises */
if ((epp->ep_vap->va_mode & (VSUID | VSGID)) &&
(p->p_p->ps_flags & PS_EXECPLEDGE)) {
error = EACCES;
goto bad1;
}
if ((vp->v_mount->mnt_flag & MNT_NOSUID))
epp->ep_vap->va_mode &= ~(VSUID | VSGID);
/* check access. for root we have to see if any exec bit on */
if ((error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p)) != 0)
goto bad1;
if ((epp->ep_vap->va_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
error = EACCES;
goto bad1;
}
/* try to open it */
if ((error = VOP_OPEN(vp, FREAD, p->p_ucred, p)) != 0)
goto bad1;
/* unlock vp, we need it unlocked from here */
VOP_UNLOCK(vp);
/* now we have the file, get the exec header */
error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0,
UIO_SYSSPACE, 0, p->p_ucred, &resid, p);
if (error)
goto bad2;
epp->ep_hdrvalid = epp->ep_hdrlen - resid;
/*
* set up the vmcmds for creation of the process
* address space
*/
error = ENOEXEC;
for (i = 0; i < nexecs && error != 0; i++) {
int newerror;
if (execsw[i].es_check == NULL)
continue;
newerror = (*execsw[i].es_check)(p, epp);
/* make sure the first "interesting" error code is saved. */
if (!newerror || error == ENOEXEC)
error = newerror;
if (epp->ep_flags & EXEC_DESTR && error != 0)
return (error);
}
if (!error) {
/* check that entry point is sane */
if (epp->ep_entry > VM_MAXUSER_ADDRESS) {
error = ENOEXEC;
}
/* check limits */
if ((epp->ep_tsize > MAXTSIZ) ||
(epp->ep_dsize > lim_cur(RLIMIT_DATA)))
error = ENOMEM;
if (!error)
return (0);
}
/*
* free any vmspace-creation commands,
* and release their references
*/
kill_vmcmds(&epp->ep_vmcmds);
bad2:
/*
* close the vnode, free the pathname buf, and punt.
*/
vn_close(vp, FREAD, p->p_ucred, p);
pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
return (error);
bad1:
/*
* free the namei pathname buffer, and put the vnode
* (which we don't yet have open).
*/
pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
vput(vp);
return (error);
}
/*
* exec system call
*/
int
sys_execve(struct proc *p, void *v, register_t *retval)
{
struct sys_execve_args /* {
syscallarg(const char *) path;
syscallarg(char *const *) argp;
syscallarg(char *const *) envp;
} */ *uap = v;
int error;
struct exec_package pack;
struct nameidata nid;
struct vattr attr;
struct ucred *cred = p->p_ucred;
char *argp;
char * const *cpp, *dp, *sp;
#ifdef KTRACE
char *env_start;
#endif
struct process *pr = p->p_p;
long argc, envc;
size_t len, sgap, dstsize;
#ifdef MACHINE_STACK_GROWS_UP
size_t slen;
#endif
char *stack;
struct ps_strings arginfo;
struct vmspace *vm;
struct vnode *otvp;
/* get other threads to stop */
if ((error = single_thread_set(p, SINGLE_UNWIND, 1)))
return (error);
/*
* Cheap solution to complicated problems.
* Mark this process as "leave me alone, I'm execing".
*/
atomic_setbits_int(&pr->ps_flags, PS_INEXEC);
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
nid.ni_pledge = PLEDGE_EXEC;
nid.ni_unveil = UNVEIL_EXEC;
/*
* initialize the fields of the exec package.
*/
pack.ep_name = (char *)SCARG(uap, path);
pack.ep_hdr = malloc(exec_maxhdrsz, M_EXEC, M_WAITOK);
pack.ep_hdrlen = exec_maxhdrsz;
pack.ep_hdrvalid = 0;
pack.ep_ndp = &nid;
pack.ep_interp = NULL;
pack.ep_args = NULL;
pack.ep_auxinfo = NULL;
VMCMDSET_INIT(&pack.ep_vmcmds);
pack.ep_vap = &attr;
pack.ep_flags = 0;
/* see if we can run it. */
if ((error = check_exec(p, &pack)) != 0) {
goto freehdr;
}
/* XXX -- THE FOLLOWING SECTION NEEDS MAJOR CLEANUP */
/* allocate an argument buffer */
argp = km_alloc(NCARGS, &kv_exec, &kp_pageable, &kd_waitok);
#ifdef DIAGNOSTIC
if (argp == NULL)
panic("execve: argp == NULL");
#endif
dp = argp;
argc = 0;
/*
* Copy the fake args list, if there's one, freeing it as we go.
* exec_script_makecmds() allocates either 2 or 3 fake args bounded
* by MAXINTERP + MAXPATHLEN < NCARGS so no overflow can happen.
*/
if (pack.ep_flags & EXEC_HASARGL) {
dstsize = NCARGS;
for(; pack.ep_fa[argc] != NULL; argc++) {
len = strlcpy(dp, pack.ep_fa[argc], dstsize);
len++;
dp += len; dstsize -= len;
if (pack.ep_fa[argc+1] != NULL)
free(pack.ep_fa[argc], M_EXEC, len);
else
free(pack.ep_fa[argc], M_EXEC, MAXPATHLEN);
}
free(pack.ep_fa, M_EXEC, 4 * sizeof(char *));
pack.ep_flags &= ~EXEC_HASARGL;
}
/* Now get argv & environment */
if (!(cpp = SCARG(uap, argp))) {
error = EFAULT;
goto bad;
}
if (pack.ep_flags & EXEC_SKIPARG)
cpp++;
while (1) {
len = argp + ARG_MAX - dp;
if ((error = copyin(cpp, &sp, sizeof(sp))) != 0)
goto bad;
if (!sp)
break;
if ((error = copyinstr(sp, dp, len, &len)) != 0) {
if (error == ENAMETOOLONG)
error = E2BIG;
goto bad;
}
dp += len;
cpp++;
argc++;
}
/* must have at least one argument */
if (argc == 0) {
error = EINVAL;
goto bad;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_EXECARGS))
ktrexec(p, KTR_EXECARGS, argp, dp - argp);
#endif
envc = 0;
/* environment does not need to be there */
if ((cpp = SCARG(uap, envp)) != NULL ) {
#ifdef KTRACE
env_start = dp;
#endif
while (1) {
len = argp + ARG_MAX - dp;
if ((error = copyin(cpp, &sp, sizeof(sp))) != 0)
goto bad;
if (!sp)
break;
if ((error = copyinstr(sp, dp, len, &len)) != 0) {
if (error == ENAMETOOLONG)
error = E2BIG;
goto bad;
}
dp += len;
cpp++;
envc++;
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_EXECENV))
ktrexec(p, KTR_EXECENV, env_start, dp - env_start);
#endif
}
dp = (char *)(((long)dp + _STACKALIGNBYTES) & ~_STACKALIGNBYTES);
/*
* If we have enabled random stackgap, the stack itself has already
* been moved from a random location, but is still aligned to a page
* boundary. Provide the lower bits of random placement now.
*/
if (stackgap_random == 0) {
sgap = 0;
} else {
sgap = arc4random() & PAGE_MASK;
sgap = (sgap + _STACKALIGNBYTES) & ~_STACKALIGNBYTES;
}
/* Now check if args & environ fit into new stack */
len = ((argc + envc + 2 + ELF_AUX_WORDS) * sizeof(char *) +
sizeof(long) + dp + sgap + sizeof(struct ps_strings)) - argp;
len = (len + _STACKALIGNBYTES) &~ _STACKALIGNBYTES;
if (len > pack.ep_ssize) { /* in effect, compare to initial limit */
error = ENOMEM;
goto bad;
}
/* adjust "active stack depth" for process VSZ */
pack.ep_ssize = len; /* maybe should go elsewhere, but... */
/*
* we're committed: any further errors will kill the process, so
* kill the other threads now.
*/
single_thread_set(p, SINGLE_EXIT, 1);
/*
* Prepare vmspace for remapping. Note that uvmspace_exec can replace
* ps_vmspace!
*/
uvmspace_exec(p, VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS);
vm = pr->ps_vmspace;
/* Now map address space */
vm->vm_taddr = (char *)trunc_page(pack.ep_taddr);
vm->vm_tsize = atop(round_page(pack.ep_taddr + pack.ep_tsize) -
trunc_page(pack.ep_taddr));
vm->vm_daddr = (char *)trunc_page(pack.ep_daddr);
vm->vm_dsize = atop(round_page(pack.ep_daddr + pack.ep_dsize) -
trunc_page(pack.ep_daddr));
vm->vm_dused = 0;
vm->vm_ssize = atop(round_page(pack.ep_ssize));
vm->vm_maxsaddr = (char *)pack.ep_maxsaddr;
vm->vm_minsaddr = (char *)pack.ep_minsaddr;
/* create the new process's VM space by running the vmcmds */
#ifdef DIAGNOSTIC
if (pack.ep_vmcmds.evs_used == 0)
panic("execve: no vmcmds");
#endif
error = exec_process_vmcmds(p, &pack);
/* if an error happened, deallocate and punt */
if (error)
goto exec_abort;
#ifdef MACHINE_STACK_GROWS_UP
pr->ps_strings = (vaddr_t)vm->vm_maxsaddr + sgap;
if (uvm_map_protect(&vm->vm_map, (vaddr_t)vm->vm_maxsaddr,
trunc_page(pr->ps_strings), PROT_NONE, TRUE))
goto exec_abort;
#else
pr->ps_strings = (vaddr_t)vm->vm_minsaddr - sizeof(arginfo) - sgap;
if (uvm_map_protect(&vm->vm_map,
round_page(pr->ps_strings + sizeof(arginfo)),
(vaddr_t)vm->vm_minsaddr, PROT_NONE, TRUE))
goto exec_abort;
#endif
memset(&arginfo, 0, sizeof(arginfo));
/* remember information about the process */
arginfo.ps_nargvstr = argc;
arginfo.ps_nenvstr = envc;
#ifdef MACHINE_STACK_GROWS_UP
stack = (char *)vm->vm_maxsaddr + sizeof(arginfo) + sgap;
slen = len - sizeof(arginfo) - sgap;
#else
stack = (char *)(vm->vm_minsaddr - len);
#endif
/* Now copy argc, args & environ to new stack */
if (!copyargs(&pack, &arginfo, stack, argp))
goto exec_abort;
/* copy out the process's ps_strings structure */
if (copyout(&arginfo, (char *)pr->ps_strings, sizeof(arginfo)))
goto exec_abort;
stopprofclock(pr); /* stop profiling */
fdcloseexec(p); /* handle close on exec */
execsigs(p); /* reset caught signals */
TCB_SET(p, NULL); /* reset the TCB address */
pr->ps_kbind_addr = 0; /* reset the kbind bits */
pr->ps_kbind_cookie = 0;
arc4random_buf(&pr->ps_sigcookie, sizeof pr->ps_sigcookie);
/* set command name & other accounting info */
memset(pr->ps_comm, 0, sizeof(pr->ps_comm));
strlcpy(pr->ps_comm, nid.ni_cnd.cn_nameptr, sizeof(pr->ps_comm));
pr->ps_acflag &= ~AFORK;
/* record proc's vnode, for use by sysctl */
otvp = pr->ps_textvp;
vref(pack.ep_vp);
pr->ps_textvp = pack.ep_vp;
if (otvp)
vrele(otvp);
atomic_setbits_int(&pr->ps_flags, PS_EXEC);
if (pr->ps_flags & PS_PPWAIT) {
atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
atomic_clearbits_int(&pr->ps_pptr->ps_flags, PS_ISPWAIT);
wakeup(pr->ps_pptr);
}
/*
* If process does execve() while it has a mismatched real,
* effective, or saved uid/gid, we set PS_SUGIDEXEC.
*/
if (cred->cr_uid != cred->cr_ruid ||
cred->cr_uid != cred->cr_svuid ||
cred->cr_gid != cred->cr_rgid ||
cred->cr_gid != cred->cr_svgid)
atomic_setbits_int(&pr->ps_flags, PS_SUGIDEXEC);
else
atomic_clearbits_int(&pr->ps_flags, PS_SUGIDEXEC);
if (pr->ps_flags & PS_EXECPLEDGE) {
pr->ps_pledge = pr->ps_execpledge;
atomic_setbits_int(&pr->ps_flags, PS_PLEDGE);
} else {
atomic_clearbits_int(&pr->ps_flags, PS_PLEDGE);
pr->ps_pledge = 0;
/* XXX XXX XXX XXX */
/* Clear our unveil paths out so the child
* starts afresh
*/
unveil_destroy(pr);
pr->ps_uvdone = 0;
}
/*
* deal with set[ug]id.
* MNT_NOEXEC has already been used to disable s[ug]id.
*/
if ((attr.va_mode & (VSUID | VSGID)) && proc_cansugid(p)) {
int i;
atomic_setbits_int(&pr->ps_flags, PS_SUGID|PS_SUGIDEXEC);
#ifdef KTRACE
/*
* If process is being ktraced, turn off - unless
* root set it.
*/
if (pr->ps_tracevp && !(pr->ps_traceflag & KTRFAC_ROOT))
ktrcleartrace(pr);
#endif
p->p_ucred = cred = crcopy(cred);
if (attr.va_mode & VSUID)
cred->cr_uid = attr.va_uid;
if (attr.va_mode & VSGID)
cred->cr_gid = attr.va_gid;
/*
* For set[ug]id processes, a few caveats apply to
* stdin, stdout, and stderr.
*/
error = 0;
fdplock(p->p_fd);
for (i = 0; i < 3; i++) {
struct file *fp = NULL;
/*
* NOTE - This will never return NULL because of
* immature fds. The file descriptor table is not
* shared because we're suid.
*/
fp = fd_getfile(p->p_fd, i);
/*
* Ensure that stdin, stdout, and stderr are already
* allocated. We do not want userland to accidentally
* allocate descriptors in this range which has implied
* meaning to libc.
*/
if (fp == NULL) {
short flags = FREAD | (i == 0 ? 0 : FWRITE);
struct vnode *vp;
int indx;
if ((error = falloc(p, &fp, &indx)) != 0)
break;
#ifdef DIAGNOSTIC
if (indx != i)
panic("sys_execve: falloc indx != i");
#endif
if ((error = cdevvp(getnulldev(), &vp)) != 0) {
fdremove(p->p_fd, indx);
closef(fp, p);
break;
}
if ((error = VOP_OPEN(vp, flags, cred, p)) != 0) {
fdremove(p->p_fd, indx);
closef(fp, p);
vrele(vp);
break;
}
if (flags & FWRITE)
vp->v_writecount++;
fp->f_flag = flags;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = (caddr_t)vp;
fdinsert(p->p_fd, indx, 0, fp);
}
FRELE(fp, p);
}
fdpunlock(p->p_fd);
if (error)
goto exec_abort;
} else
atomic_clearbits_int(&pr->ps_flags, PS_SUGID);
/*
* Reset the saved ugids and update the process's copy of the
* creds if the creds have been changed
*/
if (cred->cr_uid != cred->cr_svuid ||
cred->cr_gid != cred->cr_svgid) {
/* make sure we have unshared ucreds */
p->p_ucred = cred = crcopy(cred);
cred->cr_svuid = cred->cr_uid;
cred->cr_svgid = cred->cr_gid;
}
if (pr->ps_ucred != cred) {
struct ucred *ocred;
ocred = pr->ps_ucred;
crhold(cred);
pr->ps_ucred = cred;
crfree(ocred);
}
if (pr->ps_flags & PS_SUGIDEXEC) {
cancel_all_itimers();
}
/* reset CPU time usage for the thread, but not the process */
timespecclear(&p->p_tu.tu_runtime);
p->p_tu.tu_uticks = p->p_tu.tu_sticks = p->p_tu.tu_iticks = 0;
km_free(argp, NCARGS, &kv_exec, &kp_pageable);
pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf);
vn_close(pack.ep_vp, FREAD, cred, p);
/*
* notify others that we exec'd
*/
KNOTE(&pr->ps_klist, NOTE_EXEC);
/* map the process's timekeep page, needs to be before exec_elf_fixup */
if (exec_timekeep_map(pr))
goto free_pack_abort;
/* setup new registers and do misc. setup. */
if (exec_elf_fixup(p, &pack) != 0)
goto free_pack_abort;
#ifdef MACHINE_STACK_GROWS_UP
setregs(p, &pack, (u_long)stack + slen, retval);
#else
setregs(p, &pack, (u_long)stack, retval);
#endif
/* map the process's signal trampoline code */
if (exec_sigcode_map(pr))
goto free_pack_abort;
#ifdef __HAVE_EXEC_MD_MAP
/* perform md specific mappings that process might need */
if (exec_md_map(p, &pack))
goto free_pack_abort;
#endif
if (pr->ps_flags & PS_TRACED)
psignal(p, SIGTRAP);
free(pack.ep_hdr, M_EXEC, pack.ep_hdrlen);
p->p_descfd = 255;
if ((pack.ep_flags & EXEC_HASFD) && pack.ep_fd < 255)
p->p_descfd = pack.ep_fd;
if (pack.ep_flags & EXEC_WXNEEDED)
atomic_setbits_int(&p->p_p->ps_flags, PS_WXNEEDED);
else
atomic_clearbits_int(&p->p_p->ps_flags, PS_WXNEEDED);
atomic_clearbits_int(&pr->ps_flags, PS_INEXEC);
single_thread_clear(p, P_SUSPSIG);
return (0);
bad:
/* free the vmspace-creation commands, and release their references */
kill_vmcmds(&pack.ep_vmcmds);
/* kill any opened file descriptor, if necessary */
if (pack.ep_flags & EXEC_HASFD) {
pack.ep_flags &= ~EXEC_HASFD;
fdplock(p->p_fd);
/* fdrelease unlocks p->p_fd. */
(void) fdrelease(p, pack.ep_fd);
}
if (pack.ep_interp != NULL)
pool_put(&namei_pool, pack.ep_interp);
free(pack.ep_args, M_TEMP, sizeof *pack.ep_args);
/* close and put the exec'd file */
vn_close(pack.ep_vp, FREAD, cred, p);
pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf);
km_free(argp, NCARGS, &kv_exec, &kp_pageable);
freehdr:
free(pack.ep_hdr, M_EXEC, pack.ep_hdrlen);
atomic_clearbits_int(&pr->ps_flags, PS_INEXEC);
single_thread_clear(p, P_SUSPSIG);
return (error);
exec_abort:
/*
* the old process doesn't exist anymore. exit gracefully.
* get rid of the (new) address space we have created, if any, get rid
* of our namei data and vnode, and exit noting failure
*/
uvm_unmap(&vm->vm_map, VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS);
if (pack.ep_interp != NULL)
pool_put(&namei_pool, pack.ep_interp);
free(pack.ep_args, M_TEMP, sizeof *pack.ep_args);
pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf);
vn_close(pack.ep_vp, FREAD, cred, p);
km_free(argp, NCARGS, &kv_exec, &kp_pageable);
free_pack_abort:
free(pack.ep_hdr, M_EXEC, pack.ep_hdrlen);
exit1(p, 0, SIGABRT, EXIT_NORMAL);
/* NOTREACHED */
atomic_clearbits_int(&pr->ps_flags, PS_INEXEC);
return (0);
}
int
copyargs(struct exec_package *pack, struct ps_strings *arginfo, void *stack,
void *argp)
{
char **cpp = stack;
char *dp, *sp;
size_t len;
void *nullp = NULL;
long argc = arginfo->ps_nargvstr;
int envc = arginfo->ps_nenvstr;
if (copyout(&argc, cpp++, sizeof(argc)))
return (0);
dp = (char *) (cpp + argc + envc + 2 + ELF_AUX_WORDS);
sp = argp;
/* XXX don't copy them out, remap them! */
arginfo->ps_argvstr = cpp; /* remember location of argv for later */
for (; --argc >= 0; sp += len, dp += len)
if (copyout(&dp, cpp++, sizeof(dp)) ||
copyoutstr(sp, dp, ARG_MAX, &len))
return (0);
if (copyout(&nullp, cpp++, sizeof(nullp)))
return (0);
arginfo->ps_envstr = cpp; /* remember location of envp for later */
for (; --envc >= 0; sp += len, dp += len)
if (copyout(&dp, cpp++, sizeof(dp)) ||
copyoutstr(sp, dp, ARG_MAX, &len))
return (0);
if (copyout(&nullp, cpp++, sizeof(nullp)))
return (0);
/* if this process needs auxinfo, note where to place it */
if (pack->ep_args != NULL)
pack->ep_auxinfo = cpp;
return (1);
}
int
exec_sigcode_map(struct process *pr)
{
extern char sigcode[], esigcode[], sigcoderet[];
vsize_t sz;
sz = (vaddr_t)esigcode - (vaddr_t)sigcode;
/*
* If we don't have a sigobject yet, create one.
*
* sigobject is an anonymous memory object (just like SYSV shared
* memory) that we keep a permanent reference to and that we map
* in all processes that need this sigcode. The creation is simple,
* we create an object, add a permanent reference to it, map it in
* kernel space, copy out the sigcode to it and unmap it.
* Then we map it with PROT_READ|PROT_EXEC into the process just
* the way sys_mmap would map it.
*/
if (sigobject == NULL) {
extern int sigfillsiz;
extern u_char sigfill[];
size_t off, left;
vaddr_t va;
int r;
sigobject = uao_create(sz, 0);
uao_reference(sigobject); /* permanent reference */
if ((r = uvm_map(kernel_map, &va, round_page(sz), sigobject,
0, 0, UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_INHERIT_SHARE, MADV_RANDOM, 0)))) {
uao_detach(sigobject);
return (ENOMEM);
}
for (off = 0, left = round_page(sz); left != 0;
off += sigfillsiz) {
size_t chunk = ulmin(left, sigfillsiz);
memcpy((caddr_t)va + off, sigfill, chunk);
left -= chunk;
}
memcpy((caddr_t)va, sigcode, sz);
uvm_unmap(kernel_map, va, va + round_page(sz));
}
pr->ps_sigcode = 0; /* no hint */
uao_reference(sigobject);
if (uvm_map(&pr->ps_vmspace->vm_map, &pr->ps_sigcode, round_page(sz),
sigobject, 0, 0, UVM_MAPFLAG(PROT_READ | PROT_EXEC,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_INHERIT_COPY,
MADV_RANDOM, UVM_FLAG_COPYONW | UVM_FLAG_SYSCALL))) {
uao_detach(sigobject);
return (ENOMEM);
}
/* Calculate PC at point of sigreturn entry */
pr->ps_sigcoderet = pr->ps_sigcode + (sigcoderet - sigcode);
return (0);
}
int
exec_timekeep_map(struct process *pr)
{
size_t timekeep_sz = round_page(sizeof(struct timekeep));
/*
* Similar to the sigcode object
*/
if (timekeep_object == NULL) {
vaddr_t va = 0;
timekeep_object = uao_create(timekeep_sz, 0);
uao_reference(timekeep_object);
if (uvm_map(kernel_map, &va, timekeep_sz, timekeep_object,
0, 0, UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_INHERIT_SHARE, MADV_RANDOM, 0))) {
uao_detach(timekeep_object);
timekeep_object = NULL;
return (ENOMEM);
}
if (uvm_fault_wire(kernel_map, va, va + timekeep_sz,
PROT_READ | PROT_WRITE)) {
uvm_unmap(kernel_map, va, va + timekeep_sz);
uao_detach(timekeep_object);
timekeep_object = NULL;
return (ENOMEM);
}
timekeep = (struct timekeep *)va;
timekeep->tk_version = TK_VERSION;
}
pr->ps_timekeep = 0; /* no hint */
uao_reference(timekeep_object);
if (uvm_map(&pr->ps_vmspace->vm_map, &pr->ps_timekeep, timekeep_sz,
timekeep_object, 0, 0, UVM_MAPFLAG(PROT_READ, PROT_READ,
MAP_INHERIT_COPY, MADV_RANDOM, 0))) {
uao_detach(timekeep_object);
return (ENOMEM);
}
return (0);
}
6
2
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
/* $OpenBSD: if_etherip.c,v 1.50 2022/02/28 00:12:11 dlg Exp $ */
/*
* Copyright (c) 2015 Kazuya GODA <goda@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "bpfilter.h"
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/sysctl.h>
#include <sys/tree.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/rtable.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip_ether.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <net/if_etherip.h>
union etherip_addr {
struct in_addr in4;
struct in6_addr in6;
};
struct etherip_tunnel {
union etherip_addr
_t_src;
#define t_src4 _t_src.in4
#define t_src6 _t_src.in6
union etherip_addr
_t_dst;
#define t_dst4 _t_dst.in4
#define t_dst6 _t_dst.in6
unsigned int t_rtableid;
sa_family_t t_af;
uint8_t t_tos;
TAILQ_ENTRY(etherip_tunnel)
t_entry;
};
TAILQ_HEAD(etherip_list, etherip_tunnel);
static inline int etherip_cmp(const struct etherip_tunnel *,
const struct etherip_tunnel *);
struct etherip_softc {
struct etherip_tunnel sc_tunnel; /* must be first */
struct arpcom sc_ac;
struct ifmedia sc_media;
int sc_txhprio;
int sc_rxhprio;
uint16_t sc_df;
uint8_t sc_ttl;
};
/*
* We can control the acceptance of EtherIP packets by altering the sysctl
* net.inet.etherip.allow value. Zero means drop them, all else is acceptance.
*/
int etherip_allow = 0;
struct cpumem *etheripcounters;
void etheripattach(int);
int etherip_clone_create(struct if_clone *, int);
int etherip_clone_destroy(struct ifnet *);
int etherip_ioctl(struct ifnet *, u_long, caddr_t);
int etherip_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
void etherip_start(struct ifnet *);
int etherip_media_change(struct ifnet *);
void etherip_media_status(struct ifnet *, struct ifmediareq *);
int etherip_set_tunnel(struct etherip_softc *, struct if_laddrreq *);
int etherip_get_tunnel(struct etherip_softc *, struct if_laddrreq *);
int etherip_del_tunnel(struct etherip_softc *);
int etherip_up(struct etherip_softc *);
int etherip_down(struct etherip_softc *);
struct etherip_softc *etherip_find(const struct etherip_tunnel *);
int etherip_input(struct etherip_tunnel *, struct mbuf *, uint8_t, int);
struct if_clone etherip_cloner = IF_CLONE_INITIALIZER("etherip",
etherip_clone_create, etherip_clone_destroy);
struct etherip_list etherip_list = TAILQ_HEAD_INITIALIZER(etherip_list);
void
etheripattach(int count)
{
if_clone_attach(ðerip_cloner);
etheripcounters = counters_alloc(etherips_ncounters);
}
int
etherip_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
struct etherip_softc *sc;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = &sc->sc_ac.ac_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
sc->sc_ttl = ip_defttl;
sc->sc_txhprio = IFQ_TOS2PRIO(IPTOS_PREC_ROUTINE); /* 0 */
sc->sc_rxhprio = IF_HDRPRIO_PACKET;
sc->sc_df = htons(0);
ifp->if_softc = sc;
ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
ifp->if_ioctl = etherip_ioctl;
ifp->if_output = etherip_output;
ifp->if_start = etherip_start;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_xflags = IFXF_CLONED;
ifp->if_capabilities = IFCAP_VLAN_MTU;
ether_fakeaddr(ifp);
ifmedia_init(&sc->sc_media, 0, etherip_media_change,
etherip_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
NET_LOCK();
TAILQ_INSERT_TAIL(ðerip_list, &sc->sc_tunnel, t_entry);
NET_UNLOCK();
return (0);
}
int
etherip_clone_destroy(struct ifnet *ifp)
{
struct etherip_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
etherip_down(sc);
TAILQ_REMOVE(ðerip_list, &sc->sc_tunnel, t_entry);
NET_UNLOCK();
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
ether_ifdetach(ifp);
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
int
etherip_media_change(struct ifnet *ifp)
{
return 0;
}
void
etherip_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
imr->ifm_active = IFM_ETHER | IFM_AUTO;
imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
}
int
etherip_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
struct m_tag *mtag;
mtag = NULL;
while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) {
if (*(int *)(mtag + 1) == ifp->if_index) {
m_freem(m);
return (EIO);
}
}
return (ether_output(ifp, m, dst, rt));
}
void
etherip_start(struct ifnet *ifp)
{
struct etherip_softc *sc = ifp->if_softc;
struct mbuf *m;
int error;
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT);
#endif
switch (sc->sc_tunnel.t_af) {
case AF_INET:
error = ip_etherip_output(ifp, m);
break;
#ifdef INET6
case AF_INET6:
error = ip6_etherip_output(ifp, m);
break;
#endif
default:
/* unhandled_af(sc->sc_tunnel.t_af); */
m_freem(m);
continue;
}
if (error)
ifp->if_oerrors++;
}
}
int
etherip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct etherip_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
/* FALLTHROUGH */
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = etherip_up(sc);
else
error = 0;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = etherip_down(sc);
}
break;
case SIOCSLIFPHYRTABLE:
if (ifr->ifr_rdomainid < 0 ||
ifr->ifr_rdomainid > RT_TABLEID_MAX ||
!rtable_exists(ifr->ifr_rdomainid)) {
error = EINVAL;
break;
}
sc->sc_tunnel.t_rtableid = ifr->ifr_rdomainid;
break;
case SIOCGLIFPHYRTABLE:
ifr->ifr_rdomainid = sc->sc_tunnel.t_rtableid;
break;
case SIOCSLIFPHYADDR:
error = etherip_set_tunnel(sc, (struct if_laddrreq *)data);
break;
case SIOCGLIFPHYADDR:
error = etherip_get_tunnel(sc, (struct if_laddrreq *)data);
break;
case SIOCDIFPHYADDR:
error = etherip_del_tunnel(sc);
break;
case SIOCSTXHPRIO:
error = if_txhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_rxhprio;
break;
case SIOCSLIFPHYTTL:
if (ifr->ifr_ttl < 1 || ifr->ifr_ttl > 0xff) {
error = EINVAL;
break;
}
/* commit */
sc->sc_ttl = (uint8_t)ifr->ifr_ttl;
break;
case SIOCGLIFPHYTTL:
ifr->ifr_ttl = (int)sc->sc_ttl;
break;
case SIOCSLIFPHYDF:
/* commit */
sc->sc_df = ifr->ifr_df ? htons(IP_DF) : htons(0);
break;
case SIOCGLIFPHYDF:
ifr->ifr_df = sc->sc_df ? 1 : 0;
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
break;
}
if (error == ENETRESET) {
/* no hardware to program */
error = 0;
}
return (error);
}
int
etherip_set_tunnel(struct etherip_softc *sc, struct if_laddrreq *req)
{
struct sockaddr *src = (struct sockaddr *)&req->addr;
struct sockaddr *dst = (struct sockaddr *)&req->dstaddr;
struct sockaddr_in *src4, *dst4;
#ifdef INET6
struct sockaddr_in6 *src6, *dst6;
int error;
#endif
/* sa_family and sa_len must be equal */
if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len)
return (EINVAL);
/* validate */
switch (dst->sa_family) {
case AF_INET:
if (dst->sa_len != sizeof(*dst4))
return (EINVAL);
src4 = (struct sockaddr_in *)src;
if (in_nullhost(src4->sin_addr) ||
IN_MULTICAST(src4->sin_addr.s_addr))
return (EINVAL);
dst4 = (struct sockaddr_in *)dst;
if (in_nullhost(dst4->sin_addr) ||
IN_MULTICAST(dst4->sin_addr.s_addr))
return (EINVAL);
sc->sc_tunnel.t_src4 = src4->sin_addr;
sc->sc_tunnel.t_dst4 = dst4->sin_addr;
break;
#ifdef INET6
case AF_INET6:
if (dst->sa_len != sizeof(*dst6))
return (EINVAL);
src6 = (struct sockaddr_in6 *)src;
if (IN6_IS_ADDR_UNSPECIFIED(&src6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&src6->sin6_addr))
return (EINVAL);
dst6 = (struct sockaddr_in6 *)dst;
if (IN6_IS_ADDR_UNSPECIFIED(&dst6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&dst6->sin6_addr))
return (EINVAL);
error = in6_embedscope(&sc->sc_tunnel.t_src6, src6, NULL);
if (error != 0)
return (error);
error = in6_embedscope(&sc->sc_tunnel.t_dst6, dst6, NULL);
if (error != 0)
return (error);
break;
#endif
default:
return (EAFNOSUPPORT);
}
/* commit */
sc->sc_tunnel.t_af = dst->sa_family;
return (0);
}
int
etherip_get_tunnel(struct etherip_softc *sc, struct if_laddrreq *req)
{
struct sockaddr *src = (struct sockaddr *)&req->addr;
struct sockaddr *dst = (struct sockaddr *)&req->dstaddr;
struct sockaddr_in *sin;
#ifdef INET6 /* ifconfig already embeds the scopeid */
struct sockaddr_in6 *sin6;
#endif
switch (sc->sc_tunnel.t_af) {
case AF_UNSPEC:
return (EADDRNOTAVAIL);
case AF_INET:
sin = (struct sockaddr_in *)src;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = sc->sc_tunnel.t_src4;
sin = (struct sockaddr_in *)dst;
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = sc->sc_tunnel.t_dst4;
break;
#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6 *)src;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
in6_recoverscope(sin6, &sc->sc_tunnel.t_src6);
sin6 = (struct sockaddr_in6 *)dst;
memset(sin6, 0, sizeof(*sin6));
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
in6_recoverscope(sin6, &sc->sc_tunnel.t_dst6);
break;
#endif
default:
return (EAFNOSUPPORT);
}
return (0);
}
int
etherip_del_tunnel(struct etherip_softc *sc)
{
/* commit */
sc->sc_tunnel.t_af = AF_UNSPEC;
return (0);
}
int
etherip_up(struct etherip_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
NET_ASSERT_LOCKED();
SET(ifp->if_flags, IFF_RUNNING);
return (0);
}
int
etherip_down(struct etherip_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
NET_ASSERT_LOCKED();
CLR(ifp->if_flags, IFF_RUNNING);
return (0);
}
int
ip_etherip_output(struct ifnet *ifp, struct mbuf *m)
{
struct etherip_softc *sc = (struct etherip_softc *)ifp->if_softc;
struct m_tag *mtag;
struct etherip_header *eip;
struct ip *ip;
M_PREPEND(m, sizeof(*ip) + sizeof(*eip), M_DONTWAIT);
if (m == NULL) {
etheripstat_inc(etherips_adrops);
return ENOBUFS;
}
ip = mtod(m, struct ip *);
memset(ip, 0, sizeof(struct ip));
ip->ip_v = IPVERSION;
ip->ip_hl = sizeof(*ip) >> 2;
ip->ip_tos = IFQ_PRIO2TOS(sc->sc_txhprio == IF_HDRPRIO_PACKET ?
m->m_pkthdr.pf.prio : sc->sc_txhprio);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_id = htons(ip_randomid());
ip->ip_off = sc->sc_df;
ip->ip_ttl = sc->sc_ttl;
ip->ip_p = IPPROTO_ETHERIP;
ip->ip_src = sc->sc_tunnel.t_src4;
ip->ip_dst = sc->sc_tunnel.t_dst4;
eip = (struct etherip_header *)(ip + 1);
eip->eip_ver = ETHERIP_VERSION;
eip->eip_res = 0;
eip->eip_pad = 0;
mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
if (mtag == NULL) {
m_freem(m);
return (ENOMEM);
}
*(int *)(mtag + 1) = ifp->if_index;
m_tag_prepend(m, mtag);
m->m_flags &= ~(M_BCAST|M_MCAST);
m->m_pkthdr.ph_rtableid = sc->sc_tunnel.t_rtableid;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
etheripstat_pkt(etherips_opackets, etherips_obytes, m->m_pkthdr.len -
(sizeof(struct ip) + sizeof(struct etherip_header)));
ip_send(m);
return (0);
}
int
ip_etherip_input(struct mbuf **mp, int *offp, int type, int af)
{
struct mbuf *m = *mp;
struct etherip_tunnel key;
struct ip *ip;
ip = mtod(m, struct ip *);
key.t_af = AF_INET;
key.t_src4 = ip->ip_dst;
key.t_dst4 = ip->ip_src;
return (etherip_input(&key, m, ip->ip_tos, *offp));
}
struct etherip_softc *
etherip_find(const struct etherip_tunnel *key)
{
struct etherip_tunnel *t;
struct etherip_softc *sc;
TAILQ_FOREACH(t, ðerip_list, t_entry) {
if (etherip_cmp(key, t) != 0)
continue;
sc = (struct etherip_softc *)t;
if (!ISSET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING))
continue;
return (sc);
}
return (NULL);
}
int
etherip_input(struct etherip_tunnel *key, struct mbuf *m, uint8_t tos,
int hlen)
{
struct etherip_softc *sc;
struct ifnet *ifp;
struct etherip_header *eip;
int rxprio;
if (!etherip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) {
etheripstat_inc(etherips_pdrops);
goto drop;
}
key->t_rtableid = m->m_pkthdr.ph_rtableid;
NET_ASSERT_LOCKED();
sc = etherip_find(key);
if (sc == NULL) {
etheripstat_inc(etherips_noifdrops);
goto drop;
}
m_adj(m, hlen);
m = m_pullup(m, sizeof(*eip));
if (m == NULL) {
etheripstat_inc(etherips_adrops);
return IPPROTO_DONE;
}
eip = mtod(m, struct etherip_header *);
if (eip->eip_ver != ETHERIP_VERSION || eip->eip_pad) {
etheripstat_inc(etherips_adrops);
goto drop;
}
m_adj(m, sizeof(struct etherip_header));
etheripstat_pkt(etherips_ipackets, etherips_ibytes, m->m_pkthdr.len);
m = m_pullup(m, sizeof(struct ether_header));
if (m == NULL) {
etheripstat_inc(etherips_adrops);
return IPPROTO_DONE;
}
rxprio = sc->sc_rxhprio;
switch (rxprio) {
case IF_HDRPRIO_PACKET:
break;
case IF_HDRPRIO_OUTER:
m->m_pkthdr.pf.prio = IFQ_TOS2PRIO(tos);
break;
default:
m->m_pkthdr.pf.prio = rxprio;
break;
}
ifp = &sc->sc_ac.ac_if;
m->m_flags &= ~(M_BCAST|M_MCAST);
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
if_vinput(ifp, m);
return IPPROTO_DONE;
drop:
m_freem(m);
return (IPPROTO_DONE);
}
#ifdef INET6
int
ip6_etherip_output(struct ifnet *ifp, struct mbuf *m)
{
struct etherip_softc *sc = ifp->if_softc;
struct m_tag *mtag;
struct ip6_hdr *ip6;
struct etherip_header *eip;
uint16_t len;
uint32_t flow;
if (IN6_IS_ADDR_UNSPECIFIED(&sc->sc_tunnel.t_dst6)) {
m_freem(m);
return (ENETUNREACH);
}
len = m->m_pkthdr.len;
M_PREPEND(m, sizeof(*ip6) + sizeof(*eip), M_DONTWAIT);
if (m == NULL) {
etheripstat_inc(etherips_adrops);
return ENOBUFS;
}
flow = IPV6_VERSION << 24;
flow |= IFQ_PRIO2TOS(sc->sc_txhprio == IF_HDRPRIO_PACKET ?
m->m_pkthdr.pf.prio : sc->sc_txhprio) << 20;
ip6 = mtod(m, struct ip6_hdr *);
htobem32(&ip6->ip6_flow, flow);
ip6->ip6_nxt = IPPROTO_ETHERIP;
ip6->ip6_hlim = sc->sc_ttl;
ip6->ip6_plen = htons(len);
memcpy(&ip6->ip6_src, &sc->sc_tunnel.t_src6, sizeof(ip6->ip6_src));
memcpy(&ip6->ip6_dst, &sc->sc_tunnel.t_dst6, sizeof(ip6->ip6_dst));
eip = (struct etherip_header *)(ip6 + 1);
eip->eip_ver = ETHERIP_VERSION;
eip->eip_res = 0;
eip->eip_pad = 0;
mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
if (mtag == NULL) {
m_freem(m);
return (ENOMEM);
}
*(int *)(mtag + 1) = ifp->if_index;
m_tag_prepend(m, mtag);
if (sc->sc_df)
SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
m->m_flags &= ~(M_BCAST|M_MCAST);
m->m_pkthdr.ph_rtableid = sc->sc_tunnel.t_rtableid;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
etheripstat_pkt(etherips_opackets, etherips_obytes, len);
ip6_send(m);
return (0);
}
int
ip6_etherip_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
struct etherip_tunnel key;
const struct ip6_hdr *ip6;
uint32_t flow;
ip6 = mtod(m, const struct ip6_hdr *);
key.t_af = AF_INET6;
key.t_src6 = ip6->ip6_dst;
key.t_dst6 = ip6->ip6_src;
flow = bemtoh32(&ip6->ip6_flow);
return (etherip_input(&key, m, flow >> 20, *offp));
}
#endif /* INET6 */
int
etherip_sysctl_etheripstat(void *oldp, size_t *oldlenp, void *newp)
{
struct etheripstat etheripstat;
CTASSERT(sizeof(etheripstat) == (etherips_ncounters *
sizeof(uint64_t)));
memset(ðeripstat, 0, sizeof etheripstat);
counters_read(etheripcounters, (uint64_t *)ðeripstat,
etherips_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp, ðeripstat,
sizeof(etheripstat)));
}
int
etherip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return ENOTDIR;
switch (name[0]) {
case ETHERIPCTL_ALLOW:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
ðerip_allow, 0, 1);
NET_UNLOCK();
return (error);
case ETHERIPCTL_STATS:
return (etherip_sysctl_etheripstat(oldp, oldlenp, newp));
default:
break;
}
return ENOPROTOOPT;
}
static inline int
etherip_ip_cmp(int af, const union etherip_addr *a, const union etherip_addr *b)
{
switch (af) {
#ifdef INET6
case AF_INET6:
return (memcmp(&a->in6, &b->in6, sizeof(a->in6)));
/* FALLTHROUGH */
#endif /* INET6 */
case AF_INET:
return (memcmp(&a->in4, &b->in4, sizeof(a->in4)));
break;
default:
panic("%s: unsupported af %d", __func__, af);
}
return (0);
}
static inline int
etherip_cmp(const struct etherip_tunnel *a, const struct etherip_tunnel *b)
{
int rv;
if (a->t_rtableid > b->t_rtableid)
return (1);
if (a->t_rtableid < b->t_rtableid)
return (-1);
/* sort by address */
if (a->t_af > b->t_af)
return (1);
if (a->t_af < b->t_af)
return (-1);
rv = etherip_ip_cmp(a->t_af, &a->_t_dst, &b->_t_dst);
if (rv != 0)
return (rv);
rv = etherip_ip_cmp(a->t_af, &a->_t_src, &b->_t_src);
if (rv != 0)
return (rv);
return (0);
}
6
1
5
6
6
1
6
6
6
5
1
1
1
6
6
6
6
6
6
79
77
1
104
3
32
96
6
32
23
2
18
21
21
20
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
/* $OpenBSD: in6_ifattach.c,v 1.118 2021/03/15 17:28:45 florian Exp $ */
/* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <crypto/sha2.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_ifattach.h>
#include <netinet6/nd6.h>
#ifdef MROUTING
#include <netinet6/ip6_mroute.h>
#endif
void in6_get_rand_ifid(struct ifnet *, struct in6_addr *);
int in6_get_hw_ifid(struct ifnet *, struct in6_addr *);
void in6_get_ifid(struct ifnet *, struct in6_addr *);
int in6_ifattach_loopback(struct ifnet *);
#define EUI64_GBIT 0x01
#define EUI64_UBIT 0x02
#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT)
#define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6))
#define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT)
#define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6))
#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6))
#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6))
/*
* Generate a random interface identifier.
*
* in6 - upper 64bits are preserved
*/
void
in6_get_rand_ifid(struct ifnet *ifp, struct in6_addr *in6)
{
arc4random_buf(&in6->s6_addr32[2], 8);
/* make sure to set "u" bit to local, and "g" bit to individual. */
in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */
/* convert EUI64 into IPv6 interface identifier */
EUI64_TO_IFID(in6);
}
/*
* Get interface identifier for the specified interface.
*
* in6 - upper 64bits are preserved
*/
int
in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
{
struct sockaddr_dl *sdl;
char *addr;
size_t addrlen;
static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static u_int8_t allone[8] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
sdl = ifp->if_sadl;
if (sdl == NULL || sdl->sdl_alen == 0)
return -1;
addr = LLADDR(sdl);
addrlen = sdl->sdl_alen;
switch (ifp->if_type) {
case IFT_IEEE1394:
case IFT_IEEE80211:
/* IEEE1394 uses 16byte length address starting with EUI64 */
if (addrlen > 8)
addrlen = 8;
break;
default:
break;
}
/* get EUI64 */
switch (ifp->if_type) {
/* IEEE802/EUI64 cases - what others? */
case IFT_ETHER:
case IFT_CARP:
case IFT_IEEE1394:
case IFT_IEEE80211:
/* look at IEEE802/EUI64 only */
if (addrlen != 8 && addrlen != 6)
return -1;
/*
* check for invalid MAC address - on bsdi, we see it a lot
* since wildboar configures all-zero MAC on pccard before
* card insertion.
*/
if (bcmp(addr, allzero, addrlen) == 0)
return -1;
if (bcmp(addr, allone, addrlen) == 0)
return -1;
/* make EUI64 address */
if (addrlen == 8)
memcpy(&in6->s6_addr[8], addr, 8);
else if (addrlen == 6) {
in6->s6_addr[8] = addr[0];
in6->s6_addr[9] = addr[1];
in6->s6_addr[10] = addr[2];
in6->s6_addr[11] = 0xff;
in6->s6_addr[12] = 0xfe;
in6->s6_addr[13] = addr[3];
in6->s6_addr[14] = addr[4];
in6->s6_addr[15] = addr[5];
}
break;
case IFT_GIF:
/*
* RFC2893 says: "SHOULD use IPv4 address as ifid source".
* however, IPv4 address is not very suitable as unique
* identifier source (can be renumbered).
* we don't do this.
*/
return -1;
default:
return -1;
}
/* sanity check: g bit must not indicate "group" */
if (EUI64_GROUP(in6))
return -1;
/* convert EUI64 into IPv6 interface identifier */
EUI64_TO_IFID(in6);
/*
* sanity check: ifid must not be all zero, avoid conflict with
* subnet router anycast
*/
if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
bcmp(&in6->s6_addr[9], allzero, 7) == 0) {
return -1;
}
return 0;
}
/*
* Get interface identifier for the specified interface. If it is not
* available on ifp0, borrow interface identifier from other information
* sources.
*/
void
in6_get_ifid(struct ifnet *ifp0, struct in6_addr *in6)
{
struct ifnet *ifp;
/* first, try to get it from the interface itself */
if (in6_get_hw_ifid(ifp0, in6) == 0) {
nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n",
ifp0->if_xname));
goto success;
}
/* next, try to get it from some other hardware interface */
TAILQ_FOREACH(ifp, &ifnet, if_list) {
if (ifp == ifp0)
continue;
if (in6_get_hw_ifid(ifp, in6) == 0)
goto success;
}
/* last resort: get from random number source */
in6_get_rand_ifid(ifp, in6);
nd6log((LOG_DEBUG,
"%s: interface identifier generated by random number\n",
ifp0->if_xname));
success:
nd6log((LOG_INFO, "%s: ifid: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
ifp0->if_xname, in6->s6_addr[8], in6->s6_addr[9], in6->s6_addr[10],
in6->s6_addr[11], in6->s6_addr[12], in6->s6_addr[13],
in6->s6_addr[14], in6->s6_addr[15]));
}
/*
* ifid - used as EUI64 if not NULL, overrides other EUI64 sources
*/
int
in6_ifattach_linklocal(struct ifnet *ifp, struct in6_addr *ifid)
{
struct in6_aliasreq ifra;
struct in6_ifaddr *ia6;
int error, flags;
NET_ASSERT_LOCKED();
/*
* configure link-local address.
*/
bzero(&ifra, sizeof(ifra));
strlcpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name));
ifra.ifra_addr.sin6_family = AF_INET6;
ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1);
} else if (ifid) {
ifra.ifra_addr.sin6_addr = *ifid;
ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
ifra.ifra_addr.sin6_addr.s6_addr[8] &= ~EUI64_GBIT;
ifra.ifra_addr.sin6_addr.s6_addr[8] |= EUI64_UBIT;
} else
in6_get_ifid(ifp, &ifra.ifra_addr.sin6_addr);
ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
ifra.ifra_prefixmask.sin6_family = AF_INET6;
ifra.ifra_prefixmask.sin6_addr = in6mask64;
/* link-local addresses should NEVER expire. */
ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
/*
* XXX: Some P2P interfaces seem not to send packets just after
* becoming up, so we skip p2p interfaces for safety.
*/
if (in6if_do_dad(ifp) && ((ifp->if_flags & IFF_POINTOPOINT) == 0))
ifra.ifra_flags |= IN6_IFF_TENTATIVE;
error = in6_update_ifa(ifp, &ifra, in6ifa_ifpforlinklocal(ifp, 0));
if (error != 0)
return (error);
ia6 = in6ifa_ifpforlinklocal(ifp, 0);
/* Perform DAD, if needed. */
if (ia6->ia6_flags & IN6_IFF_TENTATIVE)
nd6_dad_start(&ia6->ia_ifa);
if (ifp->if_flags & IFF_LOOPBACK) {
if_addrhooks_run(ifp);
return (0); /* No need to install a connected route. */
}
flags = RTF_CONNECTED | RTF_MPATH;
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
flags |= RTF_CLONING;
error = rt_ifa_add(&ia6->ia_ifa, flags, ia6->ia_ifa.ifa_addr,
ifp->if_rdomain);
if (error) {
in6_purgeaddr(&ia6->ia_ifa);
return (error);
}
if_addrhooks_run(ifp);
return (0);
}
int
in6_ifattach_loopback(struct ifnet *ifp)
{
struct in6_addr in6 = in6addr_loopback;
struct in6_aliasreq ifra;
KASSERT(ifp->if_flags & IFF_LOOPBACK);
if (in6ifa_ifpwithaddr(ifp, &in6) != NULL)
return (0);
bzero(&ifra, sizeof(ifra));
strlcpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name));
ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
ifra.ifra_prefixmask.sin6_family = AF_INET6;
ifra.ifra_prefixmask.sin6_addr = in6mask128;
/*
* Always initialize ia_dstaddr (= broadcast address) to loopback
* address. Follows IPv4 practice - see in_ifinit().
*/
ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
ifra.ifra_dstaddr.sin6_family = AF_INET6;
ifra.ifra_dstaddr.sin6_addr = in6addr_loopback;
ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
ifra.ifra_addr.sin6_family = AF_INET6;
ifra.ifra_addr.sin6_addr = in6addr_loopback;
/* the loopback address should NEVER expire. */
ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
/*
* We are sure that this is a newly assigned address, so we can set
* NULL to the 3rd arg.
*/
return (in6_update_ifa(ifp, &ifra, NULL));
}
/*
* XXX multiple loopback interface needs more care. for instance,
* nodelocal address needs to be configured onto only one of them.
* XXX multiple link-local address case
*/
int
in6_ifattach(struct ifnet *ifp)
{
/* some of the interfaces are inherently not IPv6 capable */
switch (ifp->if_type) {
case IFT_BRIDGE:
case IFT_ENC:
case IFT_PFLOG:
case IFT_PFSYNC:
return (0);
}
/*
* if link mtu is too small, don't try to configure IPv6.
* remember there could be some link-layer that has special
* fragmentation logic.
*/
if (ifp->if_mtu < IPV6_MMTU)
return (EINVAL);
if ((ifp->if_flags & IFF_MULTICAST) == 0)
return (EINVAL);
/*
* Assign loopback address if this lo(4) interface is the
* default for its rdomain.
*/
if ((ifp->if_flags & IFF_LOOPBACK) &&
(ifp->if_index == rtable_loindex(ifp->if_rdomain))) {
int error;
error = in6_ifattach_loopback(ifp);
if (error)
return (error);
}
/* Interfaces that rely on strong a priori cryptographic binding of
* IP addresses are incompatible with automatically assigned llv6. */
switch (ifp->if_type) {
case IFT_WIREGUARD:
return (0);
}
/* Assign a link-local address, if there's none. */
if (in6ifa_ifpforlinklocal(ifp, 0) == NULL) {
if (in6_ifattach_linklocal(ifp, NULL) != 0) {
/* failed to assign linklocal address. bark? */
}
}
return (0);
}
/*
* NOTE: in6_ifdetach() does not support loopback if at this moment.
*/
void
in6_ifdetach(struct ifnet *ifp)
{
struct ifaddr *ifa, *next;
struct rtentry *rt;
struct sockaddr_in6 sin6;
#ifdef MROUTING
/* remove ip6_mrouter stuff */
ip6_mrouter_detach(ifp);
#endif
/* nuke any of IPv6 addresses we have */
TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, ifa_list, next) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
in6_purgeaddr(ifa);
if_addrhooks_run(ifp);
}
/*
* Remove neighbor management table. Must be called after
* purging addresses.
*/
nd6_purge(ifp);
/* remove route to interface local allnodes multicast (ff01::1) */
bzero(&sin6, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = in6addr_intfacelocal_allnodes;
sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
rt = rtalloc(sin6tosa(&sin6), 0, ifp->if_rdomain);
if (rt && rt->rt_ifidx == ifp->if_index) {
rtdeletemsg(rt, ifp, ifp->if_rdomain);
rtfree(rt);
}
/* remove route to link-local allnodes multicast (ff02::1) */
bzero(&sin6, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = in6addr_linklocal_allnodes;
sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
rt = rtalloc(sin6tosa(&sin6), 0, ifp->if_rdomain);
if (rt && rt->rt_ifidx == ifp->if_index) {
rtdeletemsg(rt, ifp, ifp->if_rdomain);
rtfree(rt);
}
if (ifp->if_xflags & (IFXF_AUTOCONF6 | IFXF_AUTOCONF6TEMP))
ifp->if_xflags &= ~(IFXF_AUTOCONF6 | IFXF_AUTOCONF6TEMP);
}
7
5
5
2
1
2
1
1
1
54
53
2
2
56
5
1
2
2
50
50
36
1
1
2
1
2
1
2
3
2
2
1
2
1
8
1
7
3
9
5
4
6
3
1
1
7
1
1
4
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
/* $OpenBSD: wskbd.c,v 1.113 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: wskbd.c,v 1.80 2005/05/04 01:52:16 augustss Exp $ */
/*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
*
* Keysym translator:
* Contributed to The NetBSD Foundation by Juergen Hannken-Illjes.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou
* for the NetBSD Project.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kbd.c 8.2 (Berkeley) 10/30/93
*/
/*
* Keyboard driver (/dev/wskbd*). Translates incoming bytes to ASCII or
* to `wscons_events' and passes them up to the appropriate reader.
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <ddb/db_var.h>
#include <dev/wscons/wscons_features.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wseventvar.h>
#include <dev/wscons/wscons_callbacks.h>
#include "audio.h" /* NAUDIO (mixer tuning) */
#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmux.h"
#ifdef WSKBD_DEBUG
#define DPRINTF(x) if (wskbddebug) printf x
int wskbddebug = 0;
#else
#define DPRINTF(x)
#endif
#include <dev/wscons/wsmuxvar.h>
struct wskbd_internal {
const struct wskbd_consops *t_consops;
void *t_consaccesscookie;
int t_modifiers;
int t_composelen; /* remaining entries in t_composebuf */
keysym_t t_composebuf[2];
int t_flags;
#define WSKFL_METAESC 1
#define MAXKEYSYMSPERKEY 2 /* ESC <key> at max */
keysym_t t_symbols[MAXKEYSYMSPERKEY];
struct wskbd_softc *t_sc; /* back pointer */
struct wskbd_mapdata t_keymap; /* translation map table and
current layout */
};
struct wskbd_softc {
struct wsevsrc sc_base;
struct wskbd_internal *id;
const struct wskbd_accessops *sc_accessops;
void *sc_accesscookie;
int sc_ledstate;
int sc_isconsole;
struct wskbd_bell_data sc_bell_data;
struct wskbd_keyrepeat_data sc_keyrepeat_data;
int sc_repeating; /* we've called timeout() */
int sc_repkey;
struct timeout sc_repeat_ch;
u_int sc_repeat_type;
int sc_repeat_value;
int sc_translating; /* xlate to chars for emulation */
int sc_maplen; /* number of entries in sc_map */
struct wscons_keymap *sc_map; /* current translation map */
int sc_refcnt;
u_char sc_dying; /* device is being detached */
#if NAUDIO > 0
void *sc_audiocookie;
#endif
};
#define MOD_SHIFT_L (1 << 0)
#define MOD_SHIFT_R (1 << 1)
#define MOD_SHIFTLOCK (1 << 2)
#define MOD_CAPSLOCK (1 << 3)
#define MOD_CONTROL_L (1 << 4)
#define MOD_CONTROL_R (1 << 5)
#define MOD_META_L (1 << 6)
#define MOD_META_R (1 << 7)
#define MOD_MODESHIFT (1 << 8)
#define MOD_NUMLOCK (1 << 9)
#define MOD_COMPOSE (1 << 10)
#define MOD_HOLDSCREEN (1 << 11)
#define MOD_COMMAND (1 << 12)
#define MOD_COMMAND1 (1 << 13)
#define MOD_COMMAND2 (1 << 14)
#define MOD_MODELOCK (1 << 15)
#define MOD_ANYSHIFT (MOD_SHIFT_L | MOD_SHIFT_R | MOD_SHIFTLOCK)
#define MOD_ANYCONTROL (MOD_CONTROL_L | MOD_CONTROL_R)
#define MOD_ANYMETA (MOD_META_L | MOD_META_R)
#define MOD_ANYLED (MOD_SHIFTLOCK | MOD_CAPSLOCK | MOD_NUMLOCK | \
MOD_COMPOSE | MOD_HOLDSCREEN)
#define MOD_ONESET(id, mask) (((id)->t_modifiers & (mask)) != 0)
#define MOD_ALLSET(id, mask) (((id)->t_modifiers & (mask)) == (mask))
keysym_t ksym_upcase(keysym_t);
int wskbd_match(struct device *, void *, void *);
void wskbd_attach(struct device *, struct device *, void *);
int wskbd_detach(struct device *, int);
int wskbd_activate(struct device *, int);
int wskbd_displayioctl(struct device *, u_long, caddr_t, int, struct proc *);
int wskbd_displayioctl_sc(struct wskbd_softc *, u_long, caddr_t, int,
struct proc *, int);
void update_leds(struct wskbd_internal *);
void update_modifier(struct wskbd_internal *, u_int, int, int);
int internal_command(struct wskbd_softc *, u_int *, keysym_t, keysym_t);
int wskbd_translate(struct wskbd_internal *, u_int, int);
int wskbd_enable(struct wskbd_softc *, int);
void wskbd_debugger(struct wskbd_softc *);
#if NWSDISPLAY > 0
void change_displayparam(struct wskbd_softc *, int, int, int);
#endif
int wskbd_do_ioctl_sc(struct wskbd_softc *, u_long, caddr_t, int,
struct proc *, int);
void wskbd_deliver_event(struct wskbd_softc *sc, u_int type, int value);
#if NWSMUX > 0
int wskbd_mux_open(struct wsevsrc *, struct wseventvar *);
int wskbd_mux_close(struct wsevsrc *);
#else
#define wskbd_mux_open NULL
#define wskbd_mux_close NULL
#endif
int wskbd_do_open(struct wskbd_softc *, struct wseventvar *);
int wskbd_do_ioctl(struct device *, u_long, caddr_t, int, struct proc *);
void wskbd_set_keymap(struct wskbd_softc *, struct wscons_keymap *, int);
int (*wskbd_get_backlight)(struct wskbd_backlight *);
int (*wskbd_set_backlight)(struct wskbd_backlight *);
struct cfdriver wskbd_cd = {
NULL, "wskbd", DV_TTY
};
const struct cfattach wskbd_ca = {
sizeof (struct wskbd_softc), wskbd_match, wskbd_attach,
wskbd_detach, wskbd_activate
};
#if defined(__i386__) || defined(__amd64__)
extern int kbd_reset;
#endif
#ifndef WSKBD_DEFAULT_BELL_PITCH
#define WSKBD_DEFAULT_BELL_PITCH 400 /* 400Hz */
#endif
#ifndef WSKBD_DEFAULT_BELL_PERIOD
#define WSKBD_DEFAULT_BELL_PERIOD 100 /* 100ms */
#endif
#ifndef WSKBD_DEFAULT_BELL_VOLUME
#define WSKBD_DEFAULT_BELL_VOLUME 50 /* 50% volume */
#endif
struct wskbd_bell_data wskbd_default_bell_data = {
WSKBD_BELL_DOALL,
WSKBD_DEFAULT_BELL_PITCH,
WSKBD_DEFAULT_BELL_PERIOD,
WSKBD_DEFAULT_BELL_VOLUME,
};
#ifndef WSKBD_DEFAULT_KEYREPEAT_DEL1
#define WSKBD_DEFAULT_KEYREPEAT_DEL1 400 /* 400ms to start repeating */
#endif
#ifndef WSKBD_DEFAULT_KEYREPEAT_DELN
#define WSKBD_DEFAULT_KEYREPEAT_DELN 100 /* 100ms to between repeats */
#endif
struct wskbd_keyrepeat_data wskbd_default_keyrepeat_data = {
WSKBD_KEYREPEAT_DOALL,
WSKBD_DEFAULT_KEYREPEAT_DEL1,
WSKBD_DEFAULT_KEYREPEAT_DELN,
};
#if NWSMUX > 0 || NWSDISPLAY > 0
struct wssrcops wskbd_srcops = {
.type = WSMUX_KBD,
.dopen = wskbd_mux_open,
.dclose = wskbd_mux_close,
.dioctl = wskbd_do_ioctl,
.ddispioctl = wskbd_displayioctl,
#if NWSDISPLAY > 0
.dsetdisplay = wskbd_set_display,
#else
.dsetdisplay = NULL,
#endif
};
#endif
#if NWSDISPLAY > 0
void wskbd_repeat(void *v);
#endif
static int wskbd_console_initted;
static struct wskbd_softc *wskbd_console_device;
static struct wskbd_internal wskbd_console_data;
void wskbd_update_layout(struct wskbd_internal *, kbd_t);
#if NAUDIO > 0
extern int wskbd_set_mixervolume_dev(void *, long, long);
#endif
void
wskbd_update_layout(struct wskbd_internal *id, kbd_t enc)
{
if (enc & KB_METAESC)
id->t_flags |= WSKFL_METAESC;
else
id->t_flags &= ~WSKFL_METAESC;
id->t_keymap.layout = enc;
}
/*
* Print function (for parent devices).
*/
int
wskbddevprint(void *aux, const char *pnp)
{
#if 0
struct wskbddev_attach_args *ap = aux;
#endif
if (pnp)
printf("wskbd at %s", pnp);
#if 0
printf(" console %d", ap->console);
#endif
return (UNCONF);
}
int
wskbd_match(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct wskbddev_attach_args *ap = aux;
if (cf->wskbddevcf_console != WSKBDDEVCF_CONSOLE_UNK) {
/*
* If console-ness of device specified, either match
* exactly (at high priority), or fail.
*/
if (cf->wskbddevcf_console != 0 && ap->console != 0)
return (10);
else
return (0);
}
/* If console-ness unspecified, it wins. */
return (1);
}
void
wskbd_attach(struct device *parent, struct device *self, void *aux)
{
struct wskbd_softc *sc = (struct wskbd_softc *)self;
struct wskbddev_attach_args *ap = aux;
kbd_t layout;
#if NWSMUX > 0
struct wsmux_softc *wsmux_sc = NULL;
int mux, error;
#endif
sc->sc_isconsole = ap->console;
#if NWSMUX > 0 || NWSDISPLAY > 0
sc->sc_base.me_ops = &wskbd_srcops;
#endif
#if NWSMUX > 0
mux = sc->sc_base.me_dv.dv_cfdata->wskbddevcf_mux;
if (mux >= 0)
wsmux_sc = wsmux_getmux(mux);
#endif /* NWSMUX > 0 */
if (ap->console) {
sc->id = &wskbd_console_data;
} else {
sc->id = malloc(sizeof(struct wskbd_internal),
M_DEVBUF, M_WAITOK | M_ZERO);
bcopy(ap->keymap, &sc->id->t_keymap, sizeof(sc->id->t_keymap));
}
#if NWSDISPLAY > 0
timeout_set(&sc->sc_repeat_ch, wskbd_repeat, sc);
#endif
#if NAUDIO > 0
sc->sc_audiocookie = ap->audiocookie;
#endif
sc->id->t_sc = sc;
sc->sc_accessops = ap->accessops;
sc->sc_accesscookie = ap->accesscookie;
sc->sc_repeating = 0;
sc->sc_translating = 1;
sc->sc_ledstate = -1; /* force update */
/*
* If this layout is the default choice of the driver (i.e. the
* driver doesn't know better), pick the existing layout of the
* current mux, if any.
*/
layout = sc->id->t_keymap.layout;
#if NWSMUX > 0
if (layout & KB_DEFAULT) {
if (wsmux_sc != NULL && wsmux_get_layout(wsmux_sc) != KB_NONE)
layout = wsmux_get_layout(wsmux_sc);
}
#endif
for (;;) {
struct wscons_keymap *map;
int maplen;
if (wskbd_load_keymap(&sc->id->t_keymap, layout, &map,
&maplen) == 0) {
wskbd_set_keymap(sc, map, maplen);
break;
}
#if NWSMUX > 0
if (layout == sc->id->t_keymap.layout)
panic("cannot load keymap");
if (wsmux_sc != NULL && wsmux_get_layout(wsmux_sc) != KB_NONE) {
printf("\n%s: cannot load keymap, "
"falling back to default\n%s",
sc->sc_base.me_dv.dv_xname,
sc->sc_base.me_dv.dv_xname);
layout = wsmux_get_layout(wsmux_sc);
} else
#endif
panic("cannot load keymap");
}
wskbd_update_layout(sc->id, layout);
/* set default bell and key repeat data */
sc->sc_bell_data = wskbd_default_bell_data;
sc->sc_keyrepeat_data = wskbd_default_keyrepeat_data;
if (ap->console) {
KASSERT(wskbd_console_initted);
KASSERT(wskbd_console_device == NULL);
wskbd_console_device = sc;
printf(": console keyboard");
#if NWSDISPLAY > 0
wsdisplay_set_console_kbd(&sc->sc_base); /* sets sc_displaydv */
if (sc->sc_displaydv != NULL)
printf(", using %s", sc->sc_displaydv->dv_xname);
#endif
}
#if NWSMUX > 0
/* Ignore mux for console; it always goes to the console mux. */
if (wsmux_sc != NULL && ap->console == 0) {
printf(" mux %d\n", mux);
error = wsmux_attach_sc(wsmux_sc, &sc->sc_base);
if (error)
printf("%s: attach error=%d\n",
sc->sc_base.me_dv.dv_xname, error);
/*
* Try and set this encoding as the mux default if it
* hasn't any yet, and if this is not a driver default
* layout (i.e. parent driver pretends to know better).
* Note that wsmux_set_layout() rejects layouts with
* KB_DEFAULT set.
*/
if (wsmux_get_layout(wsmux_sc) == KB_NONE)
wsmux_set_layout(wsmux_sc, layout);
} else
#endif
printf("\n");
#if NWSDISPLAY > 0 && NWSMUX == 0
if (ap->console == 0) {
/*
* In the non-wsmux world, always connect wskbd0 and wsdisplay0
* together.
*/
extern struct cfdriver wsdisplay_cd;
if (wsdisplay_cd.cd_ndevs != 0 && self->dv_unit == 0) {
if (wskbd_set_display(self,
wsdisplay_cd.cd_devs[0]) == 0)
wsdisplay_set_kbd(wsdisplay_cd.cd_devs[0],
(struct wsevsrc *)sc);
}
}
#endif
}
void
wskbd_cnattach(const struct wskbd_consops *consops, void *conscookie,
const struct wskbd_mapdata *mapdata)
{
KASSERT(!wskbd_console_initted);
bcopy(mapdata, &wskbd_console_data.t_keymap, sizeof(*mapdata));
wskbd_update_layout(&wskbd_console_data, mapdata->layout);
wskbd_console_data.t_consops = consops;
wskbd_console_data.t_consaccesscookie = conscookie;
#if NWSDISPLAY > 0
wsdisplay_set_cons_kbd(wskbd_cngetc, wskbd_cnpollc, wskbd_cnbell);
#endif
wskbd_console_initted = 1;
}
void
wskbd_cndetach(void)
{
KASSERT(wskbd_console_initted);
wskbd_console_data.t_keymap.keydesc = NULL;
wskbd_console_data.t_keymap.layout = KB_NONE;
wskbd_console_data.t_consops = NULL;
wskbd_console_data.t_consaccesscookie = NULL;
#if NWSDISPLAY > 0
wsdisplay_unset_cons_kbd();
#endif
wskbd_console_device = NULL;
wskbd_console_initted = 0;
}
#if NWSDISPLAY > 0
void
wskbd_repeat(void *v)
{
struct wskbd_softc *sc = (struct wskbd_softc *)v;
int s = spltty();
if (sc->sc_repeating == 0) {
/*
* race condition: a "key up" event came in when wskbd_repeat()
* was already called but not yet spltty()'d
*/
splx(s);
return;
}
if (sc->sc_translating) {
/* deliver keys */
if (sc->sc_displaydv != NULL)
wsdisplay_kbdinput(sc->sc_displaydv,
sc->id->t_keymap.layout,
sc->id->t_symbols, sc->sc_repeating);
} else {
/* queue event */
wskbd_deliver_event(sc, sc->sc_repeat_type,
sc->sc_repeat_value);
}
if (sc->sc_keyrepeat_data.delN != 0)
timeout_add_msec(&sc->sc_repeat_ch, sc->sc_keyrepeat_data.delN);
splx(s);
}
#endif
int
wskbd_activate(struct device *self, int act)
{
struct wskbd_softc *sc = (struct wskbd_softc *)self;
if (act == DVACT_DEACTIVATE)
sc->sc_dying = 1;
return (0);
}
/*
* Detach a keyboard. To keep track of users of the softc we keep
* a reference count that's incremented while inside, e.g., read.
* If the keyboard is active and the reference count is > 0 (0 is the
* normal state) we post an event and then wait for the process
* that had the reference to wake us up again. Then we blow away the
* vnode and return (which will deallocate the softc).
*/
int
wskbd_detach(struct device *self, int flags)
{
struct wskbd_softc *sc = (struct wskbd_softc *)self;
struct wseventvar *evar;
int maj, mn;
int s;
#if NWSMUX > 0
/* Tell parent mux we're leaving. */
if (sc->sc_base.me_parent != NULL)
wsmux_detach_sc(&sc->sc_base);
#endif
#if NWSDISPLAY > 0
if (sc->sc_repeating) {
sc->sc_repeating = 0;
timeout_del(&sc->sc_repeat_ch);
}
#endif
if (sc->sc_isconsole) {
KASSERT(wskbd_console_device == sc);
wskbd_cndetach();
}
evar = sc->sc_base.me_evp;
if (evar != NULL) {
s = spltty();
if (--sc->sc_refcnt >= 0) {
/* Wake everyone by generating a dummy event. */
if (++evar->put >= WSEVENT_QSIZE)
evar->put = 0;
WSEVENT_WAKEUP(evar);
/* Wait for processes to go away. */
if (tsleep_nsec(sc, PZERO, "wskdet", SEC_TO_NSEC(60)))
printf("wskbd_detach: %s didn't detach\n",
sc->sc_base.me_dv.dv_xname);
}
splx(s);
}
free(sc->sc_map, M_DEVBUF,
sc->sc_maplen * sizeof(struct wscons_keymap));
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == wskbdopen)
break;
/* Nuke the vnodes for any open instances. */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
return (0);
}
void
wskbd_input(struct device *dev, u_int type, int value)
{
struct wskbd_softc *sc = (struct wskbd_softc *)dev;
#if NWSDISPLAY > 0
int num;
#endif
#if NWSDISPLAY > 0
if (sc->sc_repeating) {
sc->sc_repeating = 0;
timeout_del(&sc->sc_repeat_ch);
}
/*
* If /dev/wskbdN is not connected in event mode translate and
* send upstream.
*/
if (sc->sc_translating) {
#ifdef HAVE_BURNER_SUPPORT
if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_displaydv != NULL)
wsdisplay_burn(sc->sc_displaydv, WSDISPLAY_BURN_KBD);
#endif
num = wskbd_translate(sc->id, type, value);
if (num > 0) {
if (sc->sc_displaydv != NULL) {
#ifdef HAVE_SCROLLBACK_SUPPORT
/* XXX - Shift_R+PGUP(release) emits PrtSc */
if (sc->id->t_symbols[0] != KS_Print_Screen) {
wsscrollback(sc->sc_displaydv,
WSDISPLAY_SCROLL_RESET);
}
#endif
wsdisplay_kbdinput(sc->sc_displaydv,
sc->id->t_keymap.layout,
sc->id->t_symbols, num);
}
if (sc->sc_keyrepeat_data.del1 != 0) {
sc->sc_repeating = num;
timeout_add_msec(&sc->sc_repeat_ch,
sc->sc_keyrepeat_data.del1);
}
}
return;
}
#endif
wskbd_deliver_event(sc, type, value);
#if NWSDISPLAY > 0
/* Repeat key presses if enabled. */
if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_keyrepeat_data.del1 != 0) {
sc->sc_repeat_type = type;
sc->sc_repeat_value = value;
sc->sc_repeating = 1;
timeout_add_msec(&sc->sc_repeat_ch, sc->sc_keyrepeat_data.del1);
}
#endif
}
/*
* Keyboard is generating events. Turn this keystroke into an
* event and put it in the queue. If the queue is full, the
* keystroke is lost (sorry!).
*/
void
wskbd_deliver_event(struct wskbd_softc *sc, u_int type, int value)
{
struct wseventvar *evar;
struct wscons_event *ev;
int put;
evar = sc->sc_base.me_evp;
if (evar == NULL) {
DPRINTF(("%s: not open\n", __func__));
return;
}
#ifdef DIAGNOSTIC
if (evar->q == NULL) {
printf("wskbd_input: evar->q=NULL\n");
return;
}
#endif
put = evar->put;
ev = &evar->q[put];
put = (put + 1) % WSEVENT_QSIZE;
if (put == evar->get) {
log(LOG_WARNING, "%s: event queue overflow\n",
sc->sc_base.me_dv.dv_xname);
return;
}
ev->type = type;
ev->value = value;
nanotime(&ev->time);
evar->put = put;
WSEVENT_WAKEUP(evar);
}
#ifdef WSDISPLAY_COMPAT_RAWKBD
void
wskbd_rawinput(struct device *dev, u_char *buf, int len)
{
#if NWSDISPLAY > 0
struct wskbd_softc *sc = (struct wskbd_softc *)dev;
if (sc->sc_displaydv != NULL)
wsdisplay_rawkbdinput(sc->sc_displaydv, buf, len);
#endif
}
#endif /* WSDISPLAY_COMPAT_RAWKBD */
int
wskbd_enable(struct wskbd_softc *sc, int on)
{
int error;
#if NWSDISPLAY > 0
if (sc->sc_displaydv != NULL)
return (0);
/* Always cancel auto repeat when fiddling with the kbd. */
if (sc->sc_repeating) {
sc->sc_repeating = 0;
timeout_del(&sc->sc_repeat_ch);
}
#endif
error = (*sc->sc_accessops->enable)(sc->sc_accesscookie, on);
DPRINTF(("%s: sc=%p on=%d res=%d\n", __func__, sc, on, error));
return (error);
}
#if NWSMUX > 0
int
wskbd_mux_open(struct wsevsrc *me, struct wseventvar *evp)
{
struct wskbd_softc *sc = (struct wskbd_softc *)me;
if (sc->sc_dying)
return (EIO);
return (wskbd_do_open(sc, evp));
}
#endif
int
wskbdopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct wskbd_softc *sc;
struct wseventvar *evar;
int unit, error;
unit = minor(dev);
if (unit >= wskbd_cd.cd_ndevs || /* make sure it was attached */
(sc = wskbd_cd.cd_devs[unit]) == NULL)
return (ENXIO);
#if NWSMUX > 0
DPRINTF(("%s: %s mux=%p\n", __func__, sc->sc_base.me_dv.dv_xname,
sc->sc_base.me_parent));
#endif
if (sc->sc_dying)
return (EIO);
if ((flags & (FREAD | FWRITE)) == FWRITE) {
/* Not opening for read, only ioctl is available. */
return (0);
}
#if NWSMUX > 0
if (sc->sc_base.me_parent != NULL) {
/* Grab the keyboard out of the greedy hands of the mux. */
DPRINTF(("%s: detach\n", __func__));
wsmux_detach_sc(&sc->sc_base);
}
#endif
if (sc->sc_base.me_evp != NULL)
return (EBUSY);
evar = &sc->sc_base.me_evar;
if (wsevent_init(evar))
return (EBUSY);
error = wskbd_do_open(sc, evar);
if (error)
wsevent_fini(evar);
return (error);
}
int
wskbd_do_open(struct wskbd_softc *sc, struct wseventvar *evp)
{
int error;
/* The device could already be attached to a mux. */
if (sc->sc_base.me_evp != NULL)
return (EBUSY);
sc->sc_base.me_evp = evp;
sc->sc_translating = 0;
error = wskbd_enable(sc, 1);
if (error)
sc->sc_base.me_evp = NULL;
return (error);
}
int
wskbdclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct wskbd_softc *sc =
(struct wskbd_softc *)wskbd_cd.cd_devs[minor(dev)];
struct wseventvar *evar = sc->sc_base.me_evp;
if ((flags & (FREAD | FWRITE)) == FWRITE) {
/* not open for read */
return (0);
}
sc->sc_base.me_evp = NULL;
sc->sc_translating = 1;
(void)wskbd_enable(sc, 0);
wsevent_fini(evar);
#if NWSMUX > 0
if (sc->sc_base.me_parent == NULL) {
int mux, error;
DPRINTF(("%s: attach\n", __func__));
mux = sc->sc_base.me_dv.dv_cfdata->wskbddevcf_mux;
if (mux >= 0) {
error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
if (error)
printf("%s: can't attach mux (error=%d)\n",
sc->sc_base.me_dv.dv_xname, error);
}
}
#endif
return (0);
}
#if NWSMUX > 0
int
wskbd_mux_close(struct wsevsrc *me)
{
struct wskbd_softc *sc = (struct wskbd_softc *)me;
(void)wskbd_enable(sc, 0);
sc->sc_translating = 1;
sc->sc_base.me_evp = NULL;
return (0);
}
#endif
int
wskbdread(dev_t dev, struct uio *uio, int flags)
{
struct wskbd_softc *sc = wskbd_cd.cd_devs[minor(dev)];
int error;
if (sc->sc_dying)
return (EIO);
#ifdef DIAGNOSTIC
if (sc->sc_base.me_evp == NULL) {
printf("wskbdread: evp == NULL\n");
return (EINVAL);
}
#endif
sc->sc_refcnt++;
error = wsevent_read(&sc->sc_base.me_evar, uio, flags);
if (--sc->sc_refcnt < 0) {
wakeup(sc);
error = EIO;
}
return (error);
}
int
wskbdioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct wskbd_softc *sc = wskbd_cd.cd_devs[minor(dev)];
int error;
sc->sc_refcnt++;
error = wskbd_do_ioctl_sc(sc, cmd, data, flag, p, 0);
if (--sc->sc_refcnt < 0)
wakeup(sc);
return (error);
}
/* A wrapper around the ioctl() workhorse to make reference counting easy. */
int
wskbd_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wskbd_softc *sc = (struct wskbd_softc *)dv;
int error;
sc->sc_refcnt++;
error = wskbd_do_ioctl_sc(sc, cmd, data, flag, p, 1);
if (--sc->sc_refcnt < 0)
wakeup(sc);
return (error);
}
int
wskbd_do_ioctl_sc(struct wskbd_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p, int evsrc)
{
struct wseventvar *evar;
int error;
/*
* Try the generic ioctls that the wskbd interface supports.
*/
switch (cmd) {
case FIONBIO: /* we will remove this someday (soon???) */
return (0);
case FIOASYNC:
if (sc->sc_base.me_evp == NULL)
return (EINVAL);
sc->sc_base.me_evp->async = *(int *)data != 0;
return (0);
case FIOGETOWN:
case TIOCGPGRP:
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
sigio_getown(&evar->sigio, cmd, data);
return (0);
case FIOSETOWN:
case TIOCSPGRP:
evar = sc->sc_base.me_evp;
if (evar == NULL)
return (EINVAL);
return (sigio_setown(&evar->sigio, cmd, data));
}
/*
* Try the keyboard driver for WSKBDIO ioctls. It returns -1
* if it didn't recognize the request.
*/
error = wskbd_displayioctl_sc(sc, cmd, data, flag, p, evsrc);
return (error != -1 ? error : ENOTTY);
}
/*
* WSKBDIO ioctls, handled in both emulation mode and in ``raw'' mode.
* Some of these have no real effect in raw mode, however.
*/
int
wskbd_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wskbd_softc *sc = (struct wskbd_softc *)dv;
return (wskbd_displayioctl_sc(sc, cmd, data, flag, p, 1));
}
int
wskbd_displayioctl_sc(struct wskbd_softc *sc, u_long cmd, caddr_t data,
int flag, struct proc *p, int evsrc)
{
struct wskbd_bell_data *ubdp, *kbdp;
struct wskbd_keyrepeat_data *ukdp, *kkdp;
struct wskbd_map_data *umdp;
struct wskbd_encoding_data *uedp;
kbd_t enc;
void *buf;
int len, error;
int count, i;
switch (cmd) {
case WSKBDIO_BELL:
case WSKBDIO_COMPLEXBELL:
case WSKBDIO_SETBELL:
case WSKBDIO_SETKEYREPEAT:
case WSKBDIO_SETDEFAULTKEYREPEAT:
case WSKBDIO_SETMAP:
case WSKBDIO_SETENCODING:
case WSKBDIO_SETBACKLIGHT:
if ((flag & FWRITE) == 0)
return (EACCES);
}
switch (cmd) {
#define SETBELL(dstp, srcp, dfltp) \
do { \
(dstp)->pitch = ((srcp)->which & WSKBD_BELL_DOPITCH) ? \
(srcp)->pitch : (dfltp)->pitch; \
(dstp)->period = ((srcp)->which & WSKBD_BELL_DOPERIOD) ? \
(srcp)->period : (dfltp)->period; \
(dstp)->volume = ((srcp)->which & WSKBD_BELL_DOVOLUME) ? \
(srcp)->volume : (dfltp)->volume; \
(dstp)->which = WSKBD_BELL_DOALL; \
} while (0)
case WSKBDIO_BELL:
return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
WSKBDIO_COMPLEXBELL, (caddr_t)&sc->sc_bell_data, flag, p));
case WSKBDIO_COMPLEXBELL:
ubdp = (struct wskbd_bell_data *)data;
SETBELL(ubdp, ubdp, &sc->sc_bell_data);
return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
WSKBDIO_COMPLEXBELL, (caddr_t)ubdp, flag, p));
case WSKBDIO_SETBELL:
kbdp = &sc->sc_bell_data;
setbell:
ubdp = (struct wskbd_bell_data *)data;
SETBELL(kbdp, ubdp, kbdp);
return (0);
case WSKBDIO_GETBELL:
kbdp = &sc->sc_bell_data;
getbell:
ubdp = (struct wskbd_bell_data *)data;
SETBELL(ubdp, kbdp, kbdp);
return (0);
case WSKBDIO_SETDEFAULTBELL:
if ((error = suser(p)) != 0)
return (error);
kbdp = &wskbd_default_bell_data;
goto setbell;
case WSKBDIO_GETDEFAULTBELL:
kbdp = &wskbd_default_bell_data;
goto getbell;
#undef SETBELL
#define SETKEYREPEAT(dstp, srcp, dfltp) \
do { \
(dstp)->del1 = ((srcp)->which & WSKBD_KEYREPEAT_DODEL1) ? \
(srcp)->del1 : (dfltp)->del1; \
(dstp)->delN = ((srcp)->which & WSKBD_KEYREPEAT_DODELN) ? \
(srcp)->delN : (dfltp)->delN; \
(dstp)->which = WSKBD_KEYREPEAT_DOALL; \
} while (0)
case WSKBDIO_SETKEYREPEAT:
kkdp = &sc->sc_keyrepeat_data;
setkeyrepeat:
ukdp = (struct wskbd_keyrepeat_data *)data;
SETKEYREPEAT(kkdp, ukdp, kkdp);
return (0);
case WSKBDIO_GETKEYREPEAT:
kkdp = &sc->sc_keyrepeat_data;
getkeyrepeat:
ukdp = (struct wskbd_keyrepeat_data *)data;
SETKEYREPEAT(ukdp, kkdp, kkdp);
return (0);
case WSKBDIO_SETDEFAULTKEYREPEAT:
if ((error = suser(p)) != 0)
return (error);
kkdp = &wskbd_default_keyrepeat_data;
goto setkeyrepeat;
case WSKBDIO_GETDEFAULTKEYREPEAT:
kkdp = &wskbd_default_keyrepeat_data;
goto getkeyrepeat;
#undef SETKEYREPEAT
case WSKBDIO_SETMAP:
umdp = (struct wskbd_map_data *)data;
if (umdp->maplen > WSKBDIO_MAXMAPLEN)
return (EINVAL);
buf = mallocarray(umdp->maplen, sizeof(struct wscons_keymap),
M_TEMP, M_WAITOK);
len = umdp->maplen * sizeof(struct wscons_keymap);
error = copyin(umdp->map, buf, len);
if (error == 0) {
struct wscons_keymap *map;
map = wskbd_init_keymap(umdp->maplen);
memcpy(map, buf, len);
wskbd_set_keymap(sc, map, umdp->maplen);
/* drop the variant bits handled by the map */
enc = KB_USER | (KB_VARIANT(sc->id->t_keymap.layout) &
KB_HANDLEDBYWSKBD);
wskbd_update_layout(sc->id, enc);
}
free(buf, M_TEMP, len);
return(error);
case WSKBDIO_GETMAP:
umdp = (struct wskbd_map_data *)data;
if (umdp->maplen > sc->sc_maplen)
umdp->maplen = sc->sc_maplen;
error = copyout(sc->sc_map, umdp->map,
umdp->maplen*sizeof(struct wscons_keymap));
return(error);
case WSKBDIO_GETENCODING:
/* Do not advertise encoding to the parent mux. */
if (evsrc && (sc->id->t_keymap.layout & KB_NOENCODING))
return (ENOTTY);
*((kbd_t *)data) = sc->id->t_keymap.layout & ~KB_DEFAULT;
return(0);
case WSKBDIO_SETENCODING:
enc = *((kbd_t *)data);
if (KB_ENCODING(enc) == KB_USER) {
/* user map must already be loaded */
if (KB_ENCODING(sc->id->t_keymap.layout) != KB_USER)
return (EINVAL);
/* map variants make no sense */
if (KB_VARIANT(enc) & ~KB_HANDLEDBYWSKBD)
return (EINVAL);
} else if (sc->id->t_keymap.layout & KB_NOENCODING) {
return (0);
} else {
struct wscons_keymap *map;
int maplen;
error = wskbd_load_keymap(&sc->id->t_keymap, enc,
&map, &maplen);
if (error)
return (error);
wskbd_set_keymap(sc, map, maplen);
}
wskbd_update_layout(sc->id, enc);
#if NWSMUX > 0
/* Update mux default layout */
if (sc->sc_base.me_parent != NULL)
wsmux_set_layout(sc->sc_base.me_parent, enc);
#endif
return (0);
case WSKBDIO_GETENCODINGS:
uedp = (struct wskbd_encoding_data *)data;
for (count = 0; sc->id->t_keymap.keydesc[count].name; count++)
;
if (uedp->nencodings > count)
uedp->nencodings = count;
for (i = 0; i < uedp->nencodings; i++) {
error = copyout(&sc->id->t_keymap.keydesc[i].name,
&uedp->encodings[i], sizeof(kbd_t));
if (error)
return (error);
}
return (0);
case WSKBDIO_GETBACKLIGHT:
if (wskbd_get_backlight != NULL)
return (*wskbd_get_backlight)((struct wskbd_backlight *)data);
break;
case WSKBDIO_SETBACKLIGHT:
if (wskbd_set_backlight != NULL)
return (*wskbd_set_backlight)((struct wskbd_backlight *)data);
break;
}
/*
* Try the keyboard driver for WSKBDIO ioctls. It returns -1
* if it didn't recognize the request, and in turn we return
* -1 if we didn't recognize the request.
*/
/* printf("kbdaccess\n"); */
error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
flag, p);
#ifdef WSDISPLAY_COMPAT_RAWKBD
if (!error && cmd == WSKBDIO_SETMODE && *(int *)data == WSKBD_RAW) {
int s = spltty();
sc->id->t_modifiers &= ~(MOD_SHIFT_L | MOD_SHIFT_R
| MOD_CONTROL_L | MOD_CONTROL_R
| MOD_META_L | MOD_META_R
| MOD_COMMAND
| MOD_COMMAND1 | MOD_COMMAND2);
#if NWSDISPLAY > 0
if (sc->sc_repeating) {
sc->sc_repeating = 0;
timeout_del(&sc->sc_repeat_ch);
}
#endif
splx(s);
}
#endif
return (error);
}
int
wskbdkqfilter(dev_t dev, struct knote *kn)
{
struct wskbd_softc *sc = wskbd_cd.cd_devs[minor(dev)];
if (sc->sc_base.me_evp == NULL)
return (ENXIO);
return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
}
#if NWSDISPLAY > 0
int
wskbd_pickfree(void)
{
int i;
struct wskbd_softc *sc;
for (i = 0; i < wskbd_cd.cd_ndevs; i++) {
if ((sc = wskbd_cd.cd_devs[i]) == NULL)
continue;
if (sc->sc_displaydv == NULL)
return (i);
}
return (-1);
}
struct wsevsrc *
wskbd_set_console_display(struct device *displaydv, struct wsevsrc *me)
{
struct wskbd_softc *sc = wskbd_console_device;
if (sc == NULL)
return (NULL);
sc->sc_displaydv = displaydv;
#if NWSMUX > 0
(void)wsmux_attach_sc((struct wsmux_softc *)me, &sc->sc_base);
#endif
return (&sc->sc_base);
}
int
wskbd_set_display(struct device *dv, struct device *displaydv)
{
struct wskbd_softc *sc = (struct wskbd_softc *)dv;
struct device *odisplaydv;
int error;
DPRINTF(("%s: %s odisp=%p disp=%p cons=%d\n", __func__,
dv->dv_xname, sc->sc_displaydv, displaydv,
sc->sc_isconsole));
if (sc->sc_isconsole)
return (EBUSY);
if (displaydv != NULL) {
if (sc->sc_displaydv != NULL)
return (EBUSY);
} else {
if (sc->sc_displaydv == NULL)
return (ENXIO);
}
odisplaydv = sc->sc_displaydv;
sc->sc_displaydv = NULL;
error = wskbd_enable(sc, displaydv != NULL);
sc->sc_displaydv = displaydv;
if (error) {
sc->sc_displaydv = odisplaydv;
return (error);
}
if (displaydv)
printf("%s: connecting to %s\n",
sc->sc_base.me_dv.dv_xname, displaydv->dv_xname);
else
printf("%s: disconnecting from %s\n",
sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname);
return (0);
}
#endif /* NWSDISPLAY > 0 */
#if NWSMUX > 0
int
wskbd_add_mux(int unit, struct wsmux_softc *muxsc)
{
struct wskbd_softc *sc;
if (unit < 0 || unit >= wskbd_cd.cd_ndevs ||
(sc = wskbd_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
return (EBUSY);
return (wsmux_attach_sc(muxsc, &sc->sc_base));
}
#endif
/*
* Console interface.
*/
int
wskbd_cngetc(dev_t dev)
{
static int num = 0;
static int pos;
u_int type;
int data;
keysym_t ks;
if (!wskbd_console_initted)
return 0;
if (wskbd_console_device != NULL &&
!wskbd_console_device->sc_translating)
return 0;
for(;;) {
if (num-- > 0) {
ks = wskbd_console_data.t_symbols[pos++];
if (KS_GROUP(ks) == KS_GROUP_Ascii)
return (KS_VALUE(ks));
} else {
(*wskbd_console_data.t_consops->getc)
(wskbd_console_data.t_consaccesscookie,
&type, &data);
num = wskbd_translate(&wskbd_console_data, type, data);
pos = 0;
}
}
}
void
wskbd_cnpollc(dev_t dev, int poll)
{
if (!wskbd_console_initted)
return;
if (wskbd_console_device != NULL &&
!wskbd_console_device->sc_translating)
return;
(*wskbd_console_data.t_consops->pollc)
(wskbd_console_data.t_consaccesscookie, poll);
}
void
wskbd_cnbell(dev_t dev, u_int pitch, u_int period, u_int volume)
{
if (!wskbd_console_initted)
return;
if (wskbd_console_data.t_consops->bell != NULL)
(*wskbd_console_data.t_consops->bell)
(wskbd_console_data.t_consaccesscookie, pitch, period,
volume);
}
void
update_leds(struct wskbd_internal *id)
{
int new_state;
new_state = 0;
if (id->t_modifiers & (MOD_SHIFTLOCK | MOD_CAPSLOCK))
new_state |= WSKBD_LED_CAPS;
if (id->t_modifiers & MOD_NUMLOCK)
new_state |= WSKBD_LED_NUM;
if (id->t_modifiers & MOD_COMPOSE)
new_state |= WSKBD_LED_COMPOSE;
if (id->t_modifiers & MOD_HOLDSCREEN)
new_state |= WSKBD_LED_SCROLL;
if (id->t_sc && new_state != id->t_sc->sc_ledstate) {
(*id->t_sc->sc_accessops->set_leds)
(id->t_sc->sc_accesscookie, new_state);
id->t_sc->sc_ledstate = new_state;
}
}
void
update_modifier(struct wskbd_internal *id, u_int type, int toggle, int mask)
{
if (toggle) {
if (type == WSCONS_EVENT_KEY_DOWN)
id->t_modifiers ^= mask;
} else {
if (type == WSCONS_EVENT_KEY_DOWN)
id->t_modifiers |= mask;
else
id->t_modifiers &= ~mask;
}
if (mask & MOD_ANYLED)
update_leds(id);
}
#if NWSDISPLAY > 0
void
change_displayparam(struct wskbd_softc *sc, int param, int updown,
int wraparound)
{
int res;
struct wsdisplay_param dp;
dp.param = param;
res = wsdisplay_param(sc->sc_displaydv, WSDISPLAYIO_GETPARAM, &dp);
if (res == EINVAL)
return; /* no such parameter */
dp.curval += updown;
if (dp.max < dp.curval)
dp.curval = wraparound ? dp.min : dp.max;
else
if (dp.curval < dp.min)
dp.curval = wraparound ? dp.max : dp.min;
wsdisplay_param(sc->sc_displaydv, WSDISPLAYIO_SETPARAM, &dp);
}
#endif
int
internal_command(struct wskbd_softc *sc, u_int *type, keysym_t ksym,
keysym_t ksym2)
{
switch (ksym) {
case KS_Cmd:
update_modifier(sc->id, *type, 0, MOD_COMMAND);
ksym = ksym2;
break;
case KS_Cmd1:
update_modifier(sc->id, *type, 0, MOD_COMMAND1);
break;
case KS_Cmd2:
update_modifier(sc->id, *type, 0, MOD_COMMAND2);
break;
}
if (*type != WSCONS_EVENT_KEY_DOWN)
return (0);
#ifdef HAVE_SCROLLBACK_SUPPORT
#if NWSDISPLAY > 0
switch (ksym) {
case KS_Cmd_ScrollBack:
if (MOD_ONESET(sc->id, MOD_ANYSHIFT)) {
if (sc->sc_displaydv != NULL)
wsscrollback(sc->sc_displaydv,
WSDISPLAY_SCROLL_BACKWARD);
return (1);
}
break;
case KS_Cmd_ScrollFwd:
if (MOD_ONESET(sc->id, MOD_ANYSHIFT)) {
if (sc->sc_displaydv != NULL)
wsscrollback(sc->sc_displaydv,
WSDISPLAY_SCROLL_FORWARD);
return (1);
}
break;
}
#endif
#endif
#if NWSDISPLAY > 0
switch(ksym) {
case KS_Cmd_BrightnessUp:
wsdisplay_brightness_step(sc->sc_displaydv, 1);
return (1);
case KS_Cmd_BrightnessDown:
wsdisplay_brightness_step(sc->sc_displaydv, -1);
return (1);
case KS_Cmd_BrightnessRotate:
wsdisplay_brightness_cycle(sc->sc_displaydv);
return (1);
}
#endif
if (!MOD_ONESET(sc->id, MOD_COMMAND) &&
!MOD_ALLSET(sc->id, MOD_COMMAND1 | MOD_COMMAND2))
return (0);
#ifdef DDB
if (ksym == KS_Cmd_Debugger) {
wskbd_debugger(sc);
/* discard this key (ddb discarded command modifiers) */
*type = WSCONS_EVENT_KEY_UP;
return (1);
}
#endif
#if NWSDISPLAY > 0
if (sc->sc_displaydv == NULL)
return (0);
switch (ksym) {
case KS_Cmd_Screen0:
case KS_Cmd_Screen1:
case KS_Cmd_Screen2:
case KS_Cmd_Screen3:
case KS_Cmd_Screen4:
case KS_Cmd_Screen5:
case KS_Cmd_Screen6:
case KS_Cmd_Screen7:
case KS_Cmd_Screen8:
case KS_Cmd_Screen9:
case KS_Cmd_Screen10:
case KS_Cmd_Screen11:
wsdisplay_switch(sc->sc_displaydv, ksym - KS_Cmd_Screen0, 0);
return (1);
case KS_Cmd_ResetEmul:
wsdisplay_reset(sc->sc_displaydv, WSDISPLAY_RESETEMUL);
return (1);
case KS_Cmd_ResetClose:
wsdisplay_reset(sc->sc_displaydv, WSDISPLAY_RESETCLOSE);
return (1);
#if defined(__i386__) || defined(__amd64__)
case KS_Cmd_KbdReset:
switch (kbd_reset) {
#ifdef DDB
case 2:
wskbd_debugger(sc);
/* discard this key (ddb discarded command modifiers) */
*type = WSCONS_EVENT_KEY_UP;
break;
#endif
case 1:
kbd_reset = 0;
prsignal(initprocess, SIGUSR1);
break;
default:
break;
}
return (1);
#endif
case KS_Cmd_BacklightOn:
case KS_Cmd_BacklightOff:
case KS_Cmd_BacklightToggle:
change_displayparam(sc, WSDISPLAYIO_PARAM_BACKLIGHT,
ksym == KS_Cmd_BacklightOff ? -1 : 1,
ksym == KS_Cmd_BacklightToggle ? 1 : 0);
return (1);
case KS_Cmd_ContrastUp:
case KS_Cmd_ContrastDown:
case KS_Cmd_ContrastRotate:
change_displayparam(sc, WSDISPLAYIO_PARAM_CONTRAST,
ksym == KS_Cmd_ContrastDown ? -1 : 1,
ksym == KS_Cmd_ContrastRotate ? 1 : 0);
return (1);
}
#endif
return (0);
}
int
wskbd_translate(struct wskbd_internal *id, u_int type, int value)
{
struct wskbd_softc *sc = id->t_sc;
keysym_t ksym, res, *group;
struct wscons_keymap kpbuf, *kp;
int gindex, iscommand = 0;
if (type == WSCONS_EVENT_ALL_KEYS_UP) {
#if NWSDISPLAY > 0
if (sc != NULL && sc->sc_repeating) {
sc->sc_repeating = 0;
timeout_del(&sc->sc_repeat_ch);
}
#endif
id->t_modifiers &= ~(MOD_SHIFT_L | MOD_SHIFT_R |
MOD_CONTROL_L | MOD_CONTROL_R |
MOD_META_L | MOD_META_R |
MOD_MODESHIFT | MOD_MODELOCK |
MOD_COMMAND | MOD_COMMAND1 | MOD_COMMAND2);
return (0);
}
if (sc != NULL) {
if (value < 0 || value >= sc->sc_maplen) {
#ifdef DEBUG
printf("wskbd_translate: keycode %d out of range\n",
value);
#endif
return (0);
}
kp = sc->sc_map + value;
} else {
kp = &kpbuf;
wskbd_get_mapentry(&id->t_keymap, value, kp);
}
/* if this key has a command, process it first */
if (sc != NULL && kp->command != KS_voidSymbol)
iscommand = internal_command(sc, &type, kp->command,
kp->group1[0]);
/* Now update modifiers */
switch (kp->group1[0]) {
case KS_Shift_L:
update_modifier(id, type, 0, MOD_SHIFT_L);
break;
case KS_Shift_R:
update_modifier(id, type, 0, MOD_SHIFT_R);
break;
case KS_Shift_Lock:
update_modifier(id, type, 1, MOD_SHIFTLOCK);
break;
case KS_Caps_Lock:
update_modifier(id, type, 1, MOD_CAPSLOCK);
break;
case KS_Control_L:
update_modifier(id, type, 0, MOD_CONTROL_L);
break;
case KS_Control_R:
update_modifier(id, type, 0, MOD_CONTROL_R);
break;
case KS_Alt_L:
update_modifier(id, type, 0, MOD_META_L);
break;
case KS_Alt_R:
update_modifier(id, type, 0, MOD_META_R);
break;
case KS_Mode_switch:
update_modifier(id, type, 0, MOD_MODESHIFT);
break;
case KS_Mode_Lock:
update_modifier(id, type, 1, MOD_MODELOCK);
break;
case KS_Num_Lock:
update_modifier(id, type, 1, MOD_NUMLOCK);
break;
#if NWSDISPLAY > 0
case KS_Hold_Screen:
if (sc != NULL) {
update_modifier(id, type, 1, MOD_HOLDSCREEN);
if (sc->sc_displaydv != NULL)
wsdisplay_kbdholdscreen(sc->sc_displaydv,
id->t_modifiers & MOD_HOLDSCREEN);
}
break;
default:
if (sc != NULL && sc->sc_repeating &&
((type == WSCONS_EVENT_KEY_UP && value != sc->sc_repkey) ||
(type == WSCONS_EVENT_KEY_DOWN && value == sc->sc_repkey)))
return (0);
break;
#endif
}
#if NWSDISPLAY > 0
if (sc != NULL) {
if (sc->sc_repeating) {
sc->sc_repeating = 0;
timeout_del(&sc->sc_repeat_ch);
}
sc->sc_repkey = value;
}
#endif
/* If this is a key release or we are in command mode, we are done */
if (type != WSCONS_EVENT_KEY_DOWN || iscommand)
return (0);
/* Get the keysym */
if (id->t_modifiers & (MOD_MODESHIFT|MOD_MODELOCK) &&
!MOD_ONESET(id, MOD_ANYCONTROL))
group = & kp->group2[0];
else
group = & kp->group1[0];
if ((id->t_modifiers & MOD_NUMLOCK) &&
KS_GROUP(group[1]) == KS_GROUP_Keypad) {
gindex = !MOD_ONESET(id, MOD_ANYSHIFT);
ksym = group[gindex];
} else {
/* CAPS alone should only affect letter keys */
if ((id->t_modifiers & (MOD_CAPSLOCK | MOD_ANYSHIFT)) ==
MOD_CAPSLOCK) {
gindex = 0;
ksym = ksym_upcase(group[0]);
} else {
gindex = MOD_ONESET(id, MOD_ANYSHIFT);
ksym = group[gindex];
}
}
/* Submit Audio keys for hotkey processing */
if (KS_GROUP(ksym) == KS_GROUP_Function) {
switch (ksym) {
#if NAUDIO > 0
case KS_AudioMute:
wskbd_set_mixervolume_dev(sc->sc_audiocookie, 0, 1);
return (0);
case KS_AudioLower:
wskbd_set_mixervolume_dev(sc->sc_audiocookie, -1, 1);
return (0);
case KS_AudioRaise:
wskbd_set_mixervolume_dev(sc->sc_audiocookie, 1, 1);
return (0);
#endif
default:
break;
}
}
/* Process compose sequence and dead accents */
res = KS_voidSymbol;
switch (KS_GROUP(ksym)) {
case KS_GROUP_Ascii:
case KS_GROUP_Keypad:
case KS_GROUP_Function:
res = ksym;
break;
case KS_GROUP_Mod:
if (ksym == KS_Multi_key) {
update_modifier(id, 1, 0, MOD_COMPOSE);
id->t_composelen = 2;
}
break;
case KS_GROUP_Dead:
if (id->t_composelen == 0) {
update_modifier(id, 1, 0, MOD_COMPOSE);
id->t_composelen = 1;
id->t_composebuf[0] = ksym;
} else
res = ksym;
break;
}
if (res == KS_voidSymbol)
return (0);
if (id->t_composelen > 0) {
/*
* If the compose key also serves as AltGr (i.e. set to both
* KS_Multi_key and KS_Mode_switch), and would provide a valid,
* distinct combination as AltGr, leave compose mode.
*/
if (id->t_composelen == 2 && group == &kp->group2[0]) {
if (kp->group1[gindex] != kp->group2[gindex])
id->t_composelen = 0;
}
if (id->t_composelen != 0) {
id->t_composebuf[2 - id->t_composelen] = res;
if (--id->t_composelen == 0) {
res = wskbd_compose_value(id->t_composebuf);
update_modifier(id, 0, 0, MOD_COMPOSE);
} else {
return (0);
}
}
}
/* We are done, return the symbol */
if (KS_GROUP(res) == KS_GROUP_Ascii) {
if (MOD_ONESET(id, MOD_ANYCONTROL)) {
if ((res >= KS_at && res <= KS_z) || res == KS_space)
res = res & 0x1f;
else if (res == KS_2)
res = 0x00;
else if (res >= KS_3 && res <= KS_7)
res = KS_Escape + (res - KS_3);
else if (res == KS_8)
res = KS_Delete;
}
if (MOD_ONESET(id, MOD_ANYMETA)) {
if (id->t_flags & WSKFL_METAESC) {
id->t_symbols[0] = KS_Escape;
id->t_symbols[1] = res;
return (2);
} else
res |= 0x80;
}
}
id->t_symbols[0] = res;
return (1);
}
void
wskbd_debugger(struct wskbd_softc *sc)
{
#ifdef DDB
if (sc->sc_isconsole && db_console) {
if (sc->id->t_consops->debugger != NULL) {
(*sc->id->t_consops->debugger)
(sc->id->t_consaccesscookie);
} else
db_enter();
}
#endif
}
void
wskbd_set_keymap(struct wskbd_softc *sc, struct wscons_keymap *map, int maplen)
{
free(sc->sc_map, M_DEVBUF, sc->sc_maplen * sizeof(*sc->sc_map));
sc->sc_map = map;
sc->sc_maplen = maplen;
}
146
145
74
63
84
88
87
23
114
135
136
136
136
123
25
2
1
135
4
135
52
72
72
106
75
1
77
1
65
26
36
67
31
31
36
24
6
11
47
17
64
114
5
58
58
1
61
9
54
61
102
36
8
102
24
3
2
1
98
93
21
99
51
50
50
99
2
1
93
1
2
2
145
146
2
2
2
27
2
2
2
2
6
3
52
2
42
377
6
308
8
24
1
1
1
1
2
2
2
2
2
1
2
1
1
2
2
2
6
10
37
2
1
1
1
8
4
3
3
3
3
2
1
2
1
1
2
1
45
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
4
1
7
2
1
1
1
1
1
23
39
17
2
1
2
2
1
2
2
2
1
2
1
68
68
56
7
63
63
5
62
62
3
66
65
1
65
63
63
4
9
5
4
9
9
9
9
32
26
6
168
134
44
10
4
2
2
10
4
5
10
5
9
15
13
18
13
5
16
13
5
4
12
10
15
15
16
2
9
4
10
6
3
6
73
32
30
37
2
2
1
1
1
1
32
28
4
3
57
48
2
19
19
20
17
34
94
19
2
11
8
6
2
17
9
7
3
4
7
4
1
1
1
1
2
2
2
2
2
3
3
3
6
3
1
2
3
2
2
2
1
1
1
9
1
3
2
9
9
1
9
9
29
19
9
89
88
35
35
103
19
33
35
21
33
33
35
19
21
124
124
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
/* $OpenBSD: ip6_output.c,v 1.271 2022/08/12 17:04:17 bluhm Exp $ */
/* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/errno.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_enc.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip_var.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <crypto/idgen.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#ifdef IPSEC
#include <netinet/ip_ipsp.h>
#include <netinet/ip_ah.h>
#include <netinet/ip_esp.h>
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
#endif /* IPSEC */
struct ip6_exthdrs {
struct mbuf *ip6e_ip6;
struct mbuf *ip6e_hbh;
struct mbuf *ip6e_dest1;
struct mbuf *ip6e_rthdr;
struct mbuf *ip6e_dest2;
};
int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **, int, int);
int ip6_getpcbopt(struct ip6_pktopts *, int, struct mbuf *);
int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, int, int, int);
int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *, unsigned int);
int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf *);
int ip6_copyexthdr(struct mbuf **, caddr_t, int);
int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
struct ip6_frag **);
int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
int ip6_getpmtu(struct rtentry *, struct ifnet *, u_long *);
int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *);
static __inline u_int16_t __attribute__((__unused__))
in6_cksum_phdr(const struct in6_addr *, const struct in6_addr *,
u_int32_t, u_int32_t);
void in6_delayed_cksum(struct mbuf *, u_int8_t);
int ip6_output_ipsec_pmtu_update(struct tdb *, struct route_in6 *,
struct in6_addr *, int, int, int);
/* Context for non-repeating IDs */
struct idgen32_ctx ip6_id_ctx;
/*
* IP6 output. The packet in mbuf chain m contains a skeletal IP6
* header (with pri, len, nxt, hlim, src, dst).
* This function may modify ver and hlim only.
* The mbuf chain containing the packet will be freed.
* The mbuf opt, if present, will not be freed.
*
* type of "mtu": rt_mtu is u_long, ifnet.ifr_mtu is int, and
* nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one,
* which is rt_mtu.
*/
int
ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route_in6 *ro,
int flags, struct ip6_moptions *im6o, struct inpcb *inp)
{
struct ip6_hdr *ip6;
struct ifnet *ifp = NULL;
struct mbuf_list fml;
int hlen, tlen;
struct route_in6 ip6route;
struct rtentry *rt = NULL;
struct sockaddr_in6 *dst, dstsock;
int error = 0;
u_long mtu;
int dontfrag;
u_int16_t src_scope, dst_scope;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs;
struct in6_addr finaldst;
struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0;
u_int8_t sproto = 0;
u_char nextproto;
#ifdef IPSEC
struct tdb *tdb = NULL;
#endif /* IPSEC */
#ifdef IPSEC
if (inp && (inp->inp_flags & INP_IPV6) == 0)
panic("%s: IPv4 pcb is passed", __func__);
#endif /* IPSEC */
ip6 = mtod(m, struct ip6_hdr *);
finaldst = ip6->ip6_dst;
#define MAKE_EXTHDR(hp, mp) \
do { \
if (hp) { \
struct ip6_ext *eh = (struct ip6_ext *)(hp); \
error = ip6_copyexthdr((mp), (caddr_t)(hp), \
((eh)->ip6e_len + 1) << 3); \
if (error) \
goto freehdrs; \
} \
} while (0)
bzero(&exthdrs, sizeof(exthdrs));
if (opt) {
/* Hop-by-Hop options header */
MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
/* Destination options header(1st part) */
MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
/* Routing header */
MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
/* Destination options header(2nd part) */
MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
}
#ifdef IPSEC
if (ipsec_in_use || inp != NULL) {
error = ip6_output_ipsec_lookup(m, inp, &tdb);
if (error) {
/*
* -EINVAL is used to indicate that the packet should
* be silently dropped, typically because we've asked
* key management for an SA.
*/
if (error == -EINVAL) /* Should silently drop packet */
error = 0;
goto freehdrs;
}
}
#endif /* IPSEC */
/*
* Calculate the total length of the extension header chain.
* Keep the length of the unfragmentable part for fragmentation.
*/
optlen = 0;
if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
unfragpartlen = optlen + sizeof(struct ip6_hdr);
/* NOTE: we don't add AH/ESP length here. do that later. */
if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
/*
* If we need IPsec, or there is at least one extension header,
* separate IP6 header from the payload.
*/
if ((sproto || optlen) && !hdrsplit) {
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
m = NULL;
goto freehdrs;
}
m = exthdrs.ip6e_ip6;
hdrsplit++;
}
/* adjust pointer */
ip6 = mtod(m, struct ip6_hdr *);
/* adjust mbuf packet header length */
m->m_pkthdr.len += optlen;
plen = m->m_pkthdr.len - sizeof(*ip6);
/* If this is a jumbo payload, insert a jumbo payload option. */
if (plen > IPV6_MAXPACKET) {
if (!hdrsplit) {
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
m = NULL;
goto freehdrs;
}
m = exthdrs.ip6e_ip6;
hdrsplit++;
}
/* adjust pointer */
ip6 = mtod(m, struct ip6_hdr *);
if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
goto freehdrs;
ip6->ip6_plen = 0;
} else
ip6->ip6_plen = htons(plen);
/*
* Concatenate headers and fill in next header fields.
* Here we have, on "m"
* IPv6 payload
* and we insert headers accordingly. Finally, we should be getting:
* IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
*
* during the header composing process, "m" points to IPv6 header.
* "mprev" points to an extension header prior to esp.
*/
{
u_char *nexthdrp = &ip6->ip6_nxt;
struct mbuf *mprev = m;
/*
* we treat dest2 specially. this makes IPsec processing
* much easier. the goal here is to make mprev point the
* mbuf prior to dest2.
*
* result: IPv6 dest2 payload
* m and mprev will point to IPv6 header.
*/
if (exthdrs.ip6e_dest2) {
if (!hdrsplit)
panic("%s: assumption failed: hdr not split",
__func__);
exthdrs.ip6e_dest2->m_next = m->m_next;
m->m_next = exthdrs.ip6e_dest2;
*mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
ip6->ip6_nxt = IPPROTO_DSTOPTS;
}
#define MAKE_CHAIN(m, mp, p, i)\
do {\
if (m) {\
if (!hdrsplit) \
panic("assumption failed: hdr not split"); \
*mtod((m), u_char *) = *(p);\
*(p) = (i);\
p = mtod((m), u_char *);\
(m)->m_next = (mp)->m_next;\
(mp)->m_next = (m);\
(mp) = (m);\
}\
} while (0)
/*
* result: IPv6 hbh dest1 rthdr dest2 payload
* m will point to IPv6 header. mprev will point to the
* extension header prior to dest2 (rthdr in the above case).
*/
MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS);
MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp,
IPPROTO_DSTOPTS);
MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp,
IPPROTO_ROUTING);
}
/*
* If there is a routing header, replace the destination address field
* with the first hop of the routing header.
*/
if (exthdrs.ip6e_rthdr) {
struct ip6_rthdr *rh;
struct ip6_rthdr0 *rh0;
struct in6_addr *addr;
rh = (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
struct ip6_rthdr *));
switch (rh->ip6r_type) {
case IPV6_RTHDR_TYPE_0:
rh0 = (struct ip6_rthdr0 *)rh;
addr = (struct in6_addr *)(rh0 + 1);
ip6->ip6_dst = addr[0];
bcopy(&addr[1], &addr[0],
sizeof(struct in6_addr) * (rh0->ip6r0_segleft - 1));
addr[rh0->ip6r0_segleft - 1] = finaldst;
break;
default: /* is it possible? */
error = EINVAL;
goto bad;
}
}
/* Source address validation */
if (!(flags & IPV6_UNSPECSRC) &&
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
/*
* XXX: we can probably assume validation in the caller, but
* we explicitly check the address here for safety.
*/
error = EOPNOTSUPP;
ip6stat_inc(ip6s_badscope);
goto bad;
}
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
error = EOPNOTSUPP;
ip6stat_inc(ip6s_badscope);
goto bad;
}
ip6stat_inc(ip6s_localout);
/*
* Route packet.
*/
#if NPF > 0
reroute:
#endif
/* initialize cached route */
if (ro == NULL) {
ro = &ip6route;
bzero((caddr_t)ro, sizeof(*ro));
}
ro_pmtu = ro;
if (opt && opt->ip6po_rthdr)
ro = &opt->ip6po_route;
dst = &ro->ro_dst;
/*
* if specified, try to fill in the traffic class field.
* do not override if a non-zero value is already set.
* we check the diffserv field and the ecn field separately.
*/
if (opt && opt->ip6po_tclass >= 0) {
int mask = 0;
if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0)
mask |= 0xfc;
if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0)
mask |= 0x03;
if (mask != 0)
ip6->ip6_flow |=
htonl((opt->ip6po_tclass & mask) << 20);
}
/* fill in or override the hop limit field, if necessary. */
if (opt && opt->ip6po_hlim != -1)
ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
if (im6o != NULL)
ip6->ip6_hlim = im6o->im6o_hlim;
else
ip6->ip6_hlim = ip6_defmcasthlim;
}
#ifdef IPSEC
if (tdb != NULL) {
/*
* XXX what should we do if ip6_hlim == 0 and the
* packet gets tunneled?
*/
/*
* if we are source-routing, do not attempt to tunnel the
* packet just because ip6_dst is different from what tdb has.
* XXX
*/
error = ip6_output_ipsec_send(tdb, m, ro,
exthdrs.ip6e_rthdr ? 1 : 0, 0);
goto done;
}
#endif /* IPSEC */
bzero(&dstsock, sizeof(dstsock));
dstsock.sin6_family = AF_INET6;
dstsock.sin6_addr = ip6->ip6_dst;
dstsock.sin6_len = sizeof(dstsock);
ro->ro_tableid = m->m_pkthdr.ph_rtableid;
if (IN6_IS_ADDR_MULTICAST(&dstsock.sin6_addr)) {
struct in6_pktinfo *pi = NULL;
/*
* If the caller specify the outgoing interface
* explicitly, use it.
*/
if (opt != NULL && (pi = opt->ip6po_pktinfo) != NULL)
ifp = if_get(pi->ipi6_ifindex);
if (ifp == NULL && im6o != NULL)
ifp = if_get(im6o->im6o_ifidx);
}
if (ifp == NULL) {
rt = in6_selectroute(&dstsock, opt, ro, ro->ro_tableid);
if (rt == NULL) {
ip6stat_inc(ip6s_noroute);
error = EHOSTUNREACH;
goto bad;
}
if (ISSET(rt->rt_flags, RTF_LOCAL))
ifp = if_get(rtable_loindex(m->m_pkthdr.ph_rtableid));
else
ifp = if_get(rt->rt_ifidx);
/*
* We aren't using rtisvalid() here because the UP/DOWN state
* machine is broken with some Ethernet drivers like em(4).
* As a result we might try to use an invalid cached route
* entry while an interface is being detached.
*/
if (ifp == NULL) {
ip6stat_inc(ip6s_noroute);
error = EHOSTUNREACH;
goto bad;
}
} else {
*dst = dstsock;
}
if (rt && (rt->rt_flags & RTF_GATEWAY) &&
!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
dst = satosin6(rt->rt_gateway);
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
/* Unicast */
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
} else {
/* Multicast */
m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
/*
* Confirm that the outgoing interface supports multicast.
*/
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
ip6stat_inc(ip6s_noroute);
error = ENETUNREACH;
goto bad;
}
if ((im6o == NULL || im6o->im6o_loop) &&
in6_hasmulti(&ip6->ip6_dst, ifp)) {
/*
* If we belong to the destination multicast group
* on the outgoing interface, and the caller did not
* forbid loopback, loop back a copy.
* Can't defer TCP/UDP checksumming, do the
* computation now.
*/
in6_proto_cksum_out(m, NULL);
ip6_mloopback(ifp, m, dst);
}
#ifdef MROUTING
else {
/*
* If we are acting as a multicast router, perform
* multicast forwarding as if the packet had just
* arrived on the interface to which we are about
* to send. The multicast forwarding function
* recursively calls this function, using the
* IPV6_FORWARDING flag to prevent infinite recursion.
*
* Multicasts that are looped back by ip6_mloopback(),
* above, will be forwarded by the ip6_input() routine,
* if necessary.
*/
if (ip6_mforwarding && ip6_mrouter[ifp->if_rdomain] &&
(flags & IPV6_FORWARDING) == 0) {
if (ip6_mforward(ip6, ifp, m) != 0) {
m_freem(m);
goto done;
}
}
}
#endif
/*
* Multicasts with a hoplimit of zero may be looped back,
* above, but must not be transmitted on a network.
* Also, multicasts addressed to the loopback interface
* are not sent -- the above call to ip6_mloopback() will
* loop back a copy if this host actually belongs to the
* destination group on the loopback interface.
*/
if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
m_freem(m);
goto done;
}
}
/*
* If this packet is going through a loopback interface we won't
* be able to restore its scope ID using the interface index.
*/
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) {
if (ifp->if_flags & IFF_LOOPBACK)
src_scope = ip6->ip6_src.s6_addr16[1];
ip6->ip6_src.s6_addr16[1] = 0;
}
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) {
if (ifp->if_flags & IFF_LOOPBACK)
dst_scope = ip6->ip6_dst.s6_addr16[1];
ip6->ip6_dst.s6_addr16[1] = 0;
}
/* Determine path MTU. */
if ((error = ip6_getpmtu(ro_pmtu->ro_rt, ifp, &mtu)) != 0)
goto bad;
/*
* The caller of this function may specify to use the minimum MTU
* in some cases.
* An advanced API option (IPV6_USE_MIN_MTU) can also override MTU
* setting. The logic is a bit complicated; by default, unicast
* packets will follow path MTU while multicast packets will be sent at
* the minimum MTU. If IP6PO_MINMTU_ALL is specified, all packets
* including unicast ones will be sent at the minimum MTU. Multicast
* packets will always be sent at the minimum MTU unless
* IP6PO_MINMTU_DISABLE is explicitly specified.
* See RFC 3542 for more details.
*/
if (mtu > IPV6_MMTU) {
if ((flags & IPV6_MINMTU))
mtu = IPV6_MMTU;
else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL)
mtu = IPV6_MMTU;
else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && (opt == NULL ||
opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) {
mtu = IPV6_MMTU;
}
}
/*
* If the outgoing packet contains a hop-by-hop options header,
* it must be examined and processed even by the source node.
* (RFC 2460, section 4.)
*/
if (exthdrs.ip6e_hbh) {
struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
u_int32_t rtalert; /* returned value is ignored */
u_int32_t plen = 0; /* no more than 1 jumbo payload option! */
m->m_pkthdr.ph_ifidx = ifp->if_index;
if (ip6_process_hopopts(&m, (u_int8_t *)(hbh + 1),
((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh),
&rtalert, &plen) < 0) {
/* m was already freed at this point */
error = EINVAL;/* better error? */
goto done;
}
m->m_pkthdr.ph_ifidx = 0;
}
#if NPF > 0
if (pf_test(AF_INET6, PF_OUT, ifp, &m) != PF_PASS) {
error = EACCES;
m_freem(m);
goto done;
}
if (m == NULL)
goto done;
ip6 = mtod(m, struct ip6_hdr *);
if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
(PF_TAG_REROUTE | PF_TAG_GENERATED)) {
/* already rerun the route lookup, go on */
m->m_pkthdr.pf.flags &= ~(PF_TAG_GENERATED | PF_TAG_REROUTE);
} else if (m->m_pkthdr.pf.flags & PF_TAG_REROUTE) {
/* tag as generated to skip over pf_test on rerun */
m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
finaldst = ip6->ip6_dst;
ro = NULL;
if_put(ifp); /* drop reference since destination changed */
ifp = NULL;
goto reroute;
}
#endif
/*
* If the packet is not going on the wire it can be destined
* to any local address. In this case do not clear its scopes
* to let ip6_input() find a matching local route.
*/
if (ifp->if_flags & IFF_LOOPBACK) {
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = src_scope;
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = dst_scope;
}
in6_proto_cksum_out(m, ifp);
/*
* Send the packet to the outgoing interface.
* If necessary, do IPv6 fragmentation before sending.
*
* the logic here is rather complex:
* 1: normal case (dontfrag == 0)
* 1-a: send as is if tlen <= path mtu
* 1-b: fragment if tlen > path mtu
*
* 2: if user asks us not to fragment (dontfrag == 1)
* 2-a: send as is if tlen <= interface mtu
* 2-b: error if tlen > interface mtu
*/
tlen = m->m_pkthdr.len;
if (ISSET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT)) {
CLR(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
dontfrag = 1;
} else if (opt && ISSET(opt->ip6po_flags, IP6PO_DONTFRAG))
dontfrag = 1;
else
dontfrag = 0;
if (dontfrag && tlen > ifp->if_mtu) { /* case 2-b */
#ifdef IPSEC
if (ip_mtudisc)
ipsec_adjust_mtu(m, mtu);
#endif
error = EMSGSIZE;
goto bad;
}
/*
* transmit packet without fragmentation
*/
if (dontfrag || (tlen <= mtu)) { /* case 1-a and 2-a */
error = ifp->if_output(ifp, m, sin6tosa(dst), ro->ro_rt);
goto done;
}
/*
* try to fragment the packet. case 1-b
*/
if (mtu < IPV6_MMTU) {
/* path MTU cannot be less than IPV6_MMTU */
error = EMSGSIZE;
goto bad;
} else if (ip6->ip6_plen == 0) {
/* jumbo payload cannot be fragmented */
error = EMSGSIZE;
goto bad;
}
/*
* Too large for the destination or interface;
* fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
hlen = unfragpartlen;
if (mtu > IPV6_MAXPACKET)
mtu = IPV6_MAXPACKET;
/*
* If we are doing fragmentation, we can't defer TCP/UDP
* checksumming; compute the checksum and clear the flag.
*/
in6_proto_cksum_out(m, NULL);
/*
* Change the next header field of the last header in the
* unfragmentable part.
*/
if (exthdrs.ip6e_rthdr) {
nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
*mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
} else if (exthdrs.ip6e_dest1) {
nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
*mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
} else if (exthdrs.ip6e_hbh) {
nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
*mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
} else {
nextproto = ip6->ip6_nxt;
ip6->ip6_nxt = IPPROTO_FRAGMENT;
}
error = ip6_fragment(m, &fml, hlen, nextproto, mtu);
if (error)
goto done;
while ((m = ml_dequeue(&fml)) != NULL) {
error = ifp->if_output(ifp, m, sin6tosa(dst), ro->ro_rt);
if (error)
break;
}
if (error)
ml_purge(&fml);
else
ip6stat_inc(ip6s_fragmented);
done:
if (ro == &ip6route && ro->ro_rt) {
rtfree(ro->ro_rt);
} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
rtfree(ro_pmtu->ro_rt);
}
if_put(ifp);
#ifdef IPSEC
tdb_unref(tdb);
#endif /* IPSEC */
return (error);
freehdrs:
m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */
m_freem(exthdrs.ip6e_dest1);
m_freem(exthdrs.ip6e_rthdr);
m_freem(exthdrs.ip6e_dest2);
/* FALLTHROUGH */
bad:
m_freem(m);
goto done;
}
int
ip6_fragment(struct mbuf *m0, struct mbuf_list *fml, int hlen,
u_char nextproto, u_long mtu)
{
struct mbuf *m;
struct ip6_hdr *ip6;
u_int32_t id;
int tlen, len, off;
int error;
ml_init(fml);
ip6 = mtod(m0, struct ip6_hdr *);
tlen = m0->m_pkthdr.len;
len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
if (len < 8) {
error = EMSGSIZE;
goto bad;
}
id = htonl(ip6_randomid());
/*
* Loop through length of segment,
* make new header and copy data of each part and link onto chain.
*/
for (off = hlen; off < tlen; off += len) {
struct mbuf *mlast;
struct ip6_hdr *mhip6;
struct ip6_frag *ip6f;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
ml_enqueue(fml, m);
if ((error = m_dup_pkthdr(m, m0, M_DONTWAIT)) != 0)
goto bad;
m->m_data += max_linkhdr;
mhip6 = mtod(m, struct ip6_hdr *);
*mhip6 = *ip6;
m->m_len = sizeof(struct ip6_hdr);
if ((error = ip6_insertfraghdr(m0, m, hlen, &ip6f)) != 0)
goto bad;
ip6f->ip6f_offlg = htons((off - hlen) & ~7);
if (off + len >= tlen)
len = tlen - off;
else
ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
m->m_pkthdr.len = hlen + sizeof(struct ip6_frag) + len;
mhip6->ip6_plen = htons(m->m_pkthdr.len -
sizeof(struct ip6_hdr));
for (mlast = m; mlast->m_next; mlast = mlast->m_next)
;
mlast->m_next = m_copym(m0, off, len, M_DONTWAIT);
if (mlast->m_next == NULL) {
error = ENOBUFS;
goto bad;
}
ip6f->ip6f_reserved = 0;
ip6f->ip6f_ident = id;
ip6f->ip6f_nxt = nextproto;
}
ip6stat_add(ip6s_ofragments, ml_len(fml));
m_freem(m0);
return (0);
bad:
ip6stat_inc(ip6s_odropped);
ml_purge(fml);
m_freem(m0);
return (error);
}
int
ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen)
{
struct mbuf *m;
if (hlen > MCLBYTES)
return (ENOBUFS); /* XXX */
MGET(m, M_DONTWAIT, MT_DATA);
if (!m)
return (ENOBUFS);
if (hlen > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return (ENOBUFS);
}
}
m->m_len = hlen;
if (hdr)
memcpy(mtod(m, caddr_t), hdr, hlen);
*mp = m;
return (0);
}
/*
* Insert jumbo payload option.
*/
int
ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen)
{
struct mbuf *mopt;
u_int8_t *optbuf;
u_int32_t v;
#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
/*
* If there is no hop-by-hop options header, allocate new one.
* If there is one but it doesn't have enough space to store the
* jumbo payload option, allocate a cluster to store the whole options.
* Otherwise, use it to store the options.
*/
if (exthdrs->ip6e_hbh == 0) {
MGET(mopt, M_DONTWAIT, MT_DATA);
if (mopt == NULL)
return (ENOBUFS);
mopt->m_len = JUMBOOPTLEN;
optbuf = mtod(mopt, u_int8_t *);
optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */
exthdrs->ip6e_hbh = mopt;
} else {
struct ip6_hbh *hbh;
mopt = exthdrs->ip6e_hbh;
if (m_trailingspace(mopt) < JUMBOOPTLEN) {
/*
* XXX assumption:
* - exthdrs->ip6e_hbh is not referenced from places
* other than exthdrs.
* - exthdrs->ip6e_hbh is not an mbuf chain.
*/
int oldoptlen = mopt->m_len;
struct mbuf *n;
/*
* XXX: give up if the whole (new) hbh header does
* not fit even in an mbuf cluster.
*/
if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
return (ENOBUFS);
/*
* As a consequence, we must always prepare a cluster
* at this point.
*/
MGET(n, M_DONTWAIT, MT_DATA);
if (n) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_freem(n);
n = NULL;
}
}
if (!n)
return (ENOBUFS);
n->m_len = oldoptlen + JUMBOOPTLEN;
memcpy(mtod(n, caddr_t), mtod(mopt, caddr_t),
oldoptlen);
optbuf = mtod(n, u_int8_t *) + oldoptlen;
m_freem(mopt);
mopt = exthdrs->ip6e_hbh = n;
} else {
optbuf = mtod(mopt, u_int8_t *) + mopt->m_len;
mopt->m_len += JUMBOOPTLEN;
}
optbuf[0] = IP6OPT_PADN;
optbuf[1] = 0;
/*
* Adjust the header length according to the pad and
* the jumbo payload option.
*/
hbh = mtod(mopt, struct ip6_hbh *);
hbh->ip6h_len += (JUMBOOPTLEN >> 3);
}
/* fill in the option. */
optbuf[2] = IP6OPT_JUMBO;
optbuf[3] = 4;
v = (u_int32_t)htonl(plen + JUMBOOPTLEN);
memcpy(&optbuf[4], &v, sizeof(u_int32_t));
/* finally, adjust the packet header length */
exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
return (0);
#undef JUMBOOPTLEN
}
/*
* Insert fragment header and copy unfragmentable header portions.
*/
int
ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen,
struct ip6_frag **frghdrp)
{
struct mbuf *n, *mlast;
if (hlen > sizeof(struct ip6_hdr)) {
n = m_copym(m0, sizeof(struct ip6_hdr),
hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
if (n == NULL)
return (ENOBUFS);
m->m_next = n;
} else
n = m;
/* Search for the last mbuf of unfragmentable part. */
for (mlast = n; mlast->m_next; mlast = mlast->m_next)
;
if ((mlast->m_flags & M_EXT) == 0 &&
m_trailingspace(mlast) >= sizeof(struct ip6_frag)) {
/* use the trailing space of the last mbuf for fragment hdr */
*frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) +
mlast->m_len);
mlast->m_len += sizeof(struct ip6_frag);
m->m_pkthdr.len += sizeof(struct ip6_frag);
} else {
/* allocate a new mbuf for the fragment header */
struct mbuf *mfrg;
MGET(mfrg, M_DONTWAIT, MT_DATA);
if (mfrg == NULL)
return (ENOBUFS);
mfrg->m_len = sizeof(struct ip6_frag);
*frghdrp = mtod(mfrg, struct ip6_frag *);
mlast->m_next = mfrg;
}
return (0);
}
int
ip6_getpmtu(struct rtentry *rt, struct ifnet *ifp, u_long *mtup)
{
u_int32_t mtu = 0;
int error = 0;
if (rt != NULL) {
mtu = rt->rt_mtu;
if (mtu == 0)
mtu = ifp->if_mtu;
else if (mtu < IPV6_MMTU) {
/* RFC8021 IPv6 Atomic Fragments Considered Harmful */
mtu = IPV6_MMTU;
} else if (mtu > ifp->if_mtu) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
* MTU of the interface has been changed after the
* interface was brought up. Change the MTU in the
* route to match the interface MTU (as long as the
* field isn't locked).
*/
mtu = ifp->if_mtu;
if (!(rt->rt_locks & RTV_MTU))
rt->rt_mtu = mtu;
}
} else {
mtu = ifp->if_mtu;
}
*mtup = mtu;
return (error);
}
/*
* IP6 socket option processing.
*/
int
ip6_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
int privileged, optdatalen, uproto;
void *optdata;
struct inpcb *inp = sotoinpcb(so);
int error, optval;
struct proc *p = curproc; /* For IPsec and rdomain */
u_int rtableid, rtid = 0;
error = optval = 0;
privileged = (inp->inp_socket->so_state & SS_PRIV);
uproto = (int)so->so_proto->pr_protocol;
if (level != IPPROTO_IPV6)
return (EINVAL);
rtableid = p->p_p->ps_rtableid;
switch (op) {
case PRCO_SETOPT:
switch (optname) {
/*
* Use of some Hop-by-Hop options or some
* Destination options, might require special
* privilege. That is, normal applications
* (without special privilege) might be forbidden
* from setting certain options in outgoing packets,
* and might never see certain options in received
* packets. [RFC 2292 Section 6]
* KAME specific note:
* KAME prevents non-privileged users from sending or
* receiving ANY hbh/dst options in order to avoid
* overhead of parsing options in the kernel.
*/
case IPV6_RECVHOPOPTS:
case IPV6_RECVDSTOPTS:
if (!privileged) {
error = EPERM;
break;
}
/* FALLTHROUGH */
case IPV6_UNICAST_HOPS:
case IPV6_MINHOPCOUNT:
case IPV6_HOPLIMIT:
case IPV6_RECVPKTINFO:
case IPV6_RECVHOPLIMIT:
case IPV6_RECVRTHDR:
case IPV6_RECVPATHMTU:
case IPV6_RECVTCLASS:
case IPV6_V6ONLY:
case IPV6_AUTOFLOWLABEL:
case IPV6_RECVDSTPORT:
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
optval = *mtod(m, int *);
switch (optname) {
case IPV6_UNICAST_HOPS:
if (optval < -1 || optval >= 256)
error = EINVAL;
else {
/* -1 = kernel default */
inp->inp_hops = optval;
}
break;
case IPV6_MINHOPCOUNT:
if (optval < 0 || optval > 255)
error = EINVAL;
else
inp->inp_ip6_minhlim = optval;
break;
#define OPTSET(bit) \
do { \
if (optval) \
inp->inp_flags |= (bit); \
else \
inp->inp_flags &= ~(bit); \
} while (/*CONSTCOND*/ 0)
#define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0)
case IPV6_RECVPKTINFO:
OPTSET(IN6P_PKTINFO);
break;
case IPV6_HOPLIMIT:
{
struct ip6_pktopts **optp;
optp = &inp->inp_outputopts6;
error = ip6_pcbopt(IPV6_HOPLIMIT,
(u_char *)&optval, sizeof(optval), optp,
privileged, uproto);
break;
}
case IPV6_RECVHOPLIMIT:
OPTSET(IN6P_HOPLIMIT);
break;
case IPV6_RECVHOPOPTS:
OPTSET(IN6P_HOPOPTS);
break;
case IPV6_RECVDSTOPTS:
OPTSET(IN6P_DSTOPTS);
break;
case IPV6_RECVRTHDR:
OPTSET(IN6P_RTHDR);
break;
case IPV6_RECVPATHMTU:
/*
* We ignore this option for TCP
* sockets.
* (RFC3542 leaves this case
* unspecified.)
*/
if (uproto != IPPROTO_TCP)
OPTSET(IN6P_MTU);
break;
case IPV6_V6ONLY:
/*
* make setsockopt(IPV6_V6ONLY)
* available only prior to bind(2).
* see ipng mailing list, Jun 22 2001.
*/
if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(
&inp->inp_laddr6)) {
error = EINVAL;
break;
}
/* No support for IPv4-mapped addresses. */
if (!optval)
error = EINVAL;
else
error = 0;
break;
case IPV6_RECVTCLASS:
OPTSET(IN6P_TCLASS);
break;
case IPV6_AUTOFLOWLABEL:
OPTSET(IN6P_AUTOFLOWLABEL);
break;
case IPV6_RECVDSTPORT:
OPTSET(IN6P_RECVDSTPORT);
break;
}
break;
case IPV6_TCLASS:
case IPV6_DONTFRAG:
case IPV6_USE_MIN_MTU:
if (m == NULL || m->m_len != sizeof(optval)) {
error = EINVAL;
break;
}
optval = *mtod(m, int *);
{
struct ip6_pktopts **optp;
optp = &inp->inp_outputopts6;
error = ip6_pcbopt(optname, (u_char *)&optval,
sizeof(optval), optp, privileged, uproto);
break;
}
case IPV6_PKTINFO:
case IPV6_HOPOPTS:
case IPV6_RTHDR:
case IPV6_DSTOPTS:
case IPV6_RTHDRDSTOPTS:
{
/* new advanced API (RFC3542) */
u_char *optbuf;
int optbuflen;
struct ip6_pktopts **optp;
if (m && m->m_next) {
error = EINVAL; /* XXX */
break;
}
if (m) {
optbuf = mtod(m, u_char *);
optbuflen = m->m_len;
} else {
optbuf = NULL;
optbuflen = 0;
}
optp = &inp->inp_outputopts6;
error = ip6_pcbopt(optname, optbuf, optbuflen, optp,
privileged, uproto);
break;
}
#undef OPTSET
case IPV6_MULTICAST_IF:
case IPV6_MULTICAST_HOPS:
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
error = ip6_setmoptions(optname,
&inp->inp_moptions6,
m, inp->inp_rtableid);
break;
case IPV6_PORTRANGE:
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
optval = *mtod(m, int *);
switch (optval) {
case IPV6_PORTRANGE_DEFAULT:
inp->inp_flags &= ~(IN6P_LOWPORT);
inp->inp_flags &= ~(IN6P_HIGHPORT);
break;
case IPV6_PORTRANGE_HIGH:
inp->inp_flags &= ~(IN6P_LOWPORT);
inp->inp_flags |= IN6P_HIGHPORT;
break;
case IPV6_PORTRANGE_LOW:
inp->inp_flags &= ~(IN6P_HIGHPORT);
inp->inp_flags |= IN6P_LOWPORT;
break;
default:
error = EINVAL;
break;
}
break;
case IPSEC6_OUTSA:
error = EINVAL;
break;
case IPV6_AUTH_LEVEL:
case IPV6_ESP_TRANS_LEVEL:
case IPV6_ESP_NETWORK_LEVEL:
case IPV6_IPCOMP_LEVEL:
#ifndef IPSEC
error = EINVAL;
#else
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
optval = *mtod(m, int *);
if (optval < IPSEC_LEVEL_BYPASS ||
optval > IPSEC_LEVEL_UNIQUE) {
error = EINVAL;
break;
}
switch (optname) {
case IPV6_AUTH_LEVEL:
if (optval < IPSEC_AUTH_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_AUTH] = optval;
break;
case IPV6_ESP_TRANS_LEVEL:
if (optval < IPSEC_ESP_TRANS_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_ESP_TRANS] = optval;
break;
case IPV6_ESP_NETWORK_LEVEL:
if (optval < IPSEC_ESP_NETWORK_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_ESP_NETWORK] = optval;
break;
case IPV6_IPCOMP_LEVEL:
if (optval < IPSEC_IPCOMP_LEVEL_DEFAULT &&
suser(p)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_IPCOMP] = optval;
break;
}
#endif
break;
case SO_RTABLE:
if (m == NULL || m->m_len < sizeof(u_int)) {
error = EINVAL;
break;
}
rtid = *mtod(m, u_int *);
if (inp->inp_rtableid == rtid)
break;
/* needs privileges to switch when already set */
if (rtableid != rtid && rtableid != 0 &&
(error = suser(p)) != 0)
break;
/* table must exist */
if (!rtable_exists(rtid)) {
error = EINVAL;
break;
}
if (inp->inp_lport) {
error = EBUSY;
break;
}
inp->inp_rtableid = rtid;
in_pcbrehash(inp);
break;
case IPV6_PIPEX:
if (m != NULL && m->m_len == sizeof(int))
inp->inp_pipex = *mtod(m, int *);
else
error = EINVAL;
break;
default:
error = ENOPROTOOPT;
break;
}
break;
case PRCO_GETOPT:
switch (optname) {
case IPV6_RECVHOPOPTS:
case IPV6_RECVDSTOPTS:
case IPV6_UNICAST_HOPS:
case IPV6_MINHOPCOUNT:
case IPV6_RECVPKTINFO:
case IPV6_RECVHOPLIMIT:
case IPV6_RECVRTHDR:
case IPV6_RECVPATHMTU:
case IPV6_V6ONLY:
case IPV6_PORTRANGE:
case IPV6_RECVTCLASS:
case IPV6_AUTOFLOWLABEL:
case IPV6_RECVDSTPORT:
switch (optname) {
case IPV6_RECVHOPOPTS:
optval = OPTBIT(IN6P_HOPOPTS);
break;
case IPV6_RECVDSTOPTS:
optval = OPTBIT(IN6P_DSTOPTS);
break;
case IPV6_UNICAST_HOPS:
optval = inp->inp_hops;
break;
case IPV6_MINHOPCOUNT:
optval = inp->inp_ip6_minhlim;
break;
case IPV6_RECVPKTINFO:
optval = OPTBIT(IN6P_PKTINFO);
break;
case IPV6_RECVHOPLIMIT:
optval = OPTBIT(IN6P_HOPLIMIT);
break;
case IPV6_RECVRTHDR:
optval = OPTBIT(IN6P_RTHDR);
break;
case IPV6_RECVPATHMTU:
optval = OPTBIT(IN6P_MTU);
break;
case IPV6_V6ONLY:
optval = 1;
break;
case IPV6_PORTRANGE:
{
int flags;
flags = inp->inp_flags;
if (flags & IN6P_HIGHPORT)
optval = IPV6_PORTRANGE_HIGH;
else if (flags & IN6P_LOWPORT)
optval = IPV6_PORTRANGE_LOW;
else
optval = 0;
break;
}
case IPV6_RECVTCLASS:
optval = OPTBIT(IN6P_TCLASS);
break;
case IPV6_AUTOFLOWLABEL:
optval = OPTBIT(IN6P_AUTOFLOWLABEL);
break;
case IPV6_RECVDSTPORT:
optval = OPTBIT(IN6P_RECVDSTPORT);
break;
}
if (error)
break;
m->m_len = sizeof(int);
*mtod(m, int *) = optval;
break;
case IPV6_PATHMTU:
{
u_long pmtu = 0;
struct ip6_mtuinfo mtuinfo;
struct ifnet *ifp;
struct rtentry *rt;
if (!(so->so_state & SS_ISCONNECTED))
return (ENOTCONN);
rt = in_pcbrtentry(inp);
if (!rtisvalid(rt))
return (EHOSTUNREACH);
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return (EHOSTUNREACH);
/*
* XXX: we dot not consider the case of source
* routing, or optional information to specify
* the outgoing interface.
*/
error = ip6_getpmtu(rt, ifp, &pmtu);
if_put(ifp);
if (error)
break;
if (pmtu > IPV6_MAXPACKET)
pmtu = IPV6_MAXPACKET;
bzero(&mtuinfo, sizeof(mtuinfo));
mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
optdata = (void *)&mtuinfo;
optdatalen = sizeof(mtuinfo);
if (optdatalen > MCLBYTES)
return (EMSGSIZE); /* XXX */
if (optdatalen > MLEN)
MCLGET(m, M_WAIT);
m->m_len = optdatalen;
bcopy(optdata, mtod(m, void *), optdatalen);
break;
}
case IPV6_PKTINFO:
case IPV6_HOPOPTS:
case IPV6_RTHDR:
case IPV6_DSTOPTS:
case IPV6_RTHDRDSTOPTS:
case IPV6_TCLASS:
case IPV6_DONTFRAG:
case IPV6_USE_MIN_MTU:
error = ip6_getpcbopt(inp->inp_outputopts6,
optname, m);
break;
case IPV6_MULTICAST_IF:
case IPV6_MULTICAST_HOPS:
case IPV6_MULTICAST_LOOP:
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP:
error = ip6_getmoptions(optname,
inp->inp_moptions6, m);
break;
case IPSEC6_OUTSA:
error = EINVAL;
break;
case IPV6_AUTH_LEVEL:
case IPV6_ESP_TRANS_LEVEL:
case IPV6_ESP_NETWORK_LEVEL:
case IPV6_IPCOMP_LEVEL:
#ifndef IPSEC
m->m_len = sizeof(int);
*mtod(m, int *) = IPSEC_LEVEL_NONE;
#else
m->m_len = sizeof(int);
switch (optname) {
case IPV6_AUTH_LEVEL:
optval = inp->inp_seclevel[SL_AUTH];
break;
case IPV6_ESP_TRANS_LEVEL:
optval =
inp->inp_seclevel[SL_ESP_TRANS];
break;
case IPV6_ESP_NETWORK_LEVEL:
optval =
inp->inp_seclevel[SL_ESP_NETWORK];
break;
case IPV6_IPCOMP_LEVEL:
optval = inp->inp_seclevel[SL_IPCOMP];
break;
}
*mtod(m, int *) = optval;
#endif
break;
case SO_RTABLE:
m->m_len = sizeof(u_int);
*mtod(m, u_int *) = inp->inp_rtableid;
break;
case IPV6_PIPEX:
m->m_len = sizeof(int);
*mtod(m, int *) = inp->inp_pipex;
break;
default:
error = ENOPROTOOPT;
break;
}
break;
}
return (error);
}
int
ip6_raw_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
int error = 0, optval;
const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
struct inpcb *inp = sotoinpcb(so);
if (level != IPPROTO_IPV6)
return (EINVAL);
switch (optname) {
case IPV6_CHECKSUM:
/*
* For ICMPv6 sockets, no modification allowed for checksum
* offset, permit "no change" values to help existing apps.
*
* RFC3542 says: "An attempt to set IPV6_CHECKSUM
* for an ICMPv6 socket will fail."
* The current behavior does not meet RFC3542.
*/
switch (op) {
case PRCO_SETOPT:
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
optval = *mtod(m, int *);
if (optval < -1 ||
(optval > 0 && (optval % 2) != 0)) {
/*
* The API assumes non-negative even offset
* values or -1 as a special value.
*/
error = EINVAL;
} else if (so->so_proto->pr_protocol ==
IPPROTO_ICMPV6) {
if (optval != icmp6off)
error = EINVAL;
} else
inp->inp_cksum6 = optval;
break;
case PRCO_GETOPT:
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
optval = icmp6off;
else
optval = inp->inp_cksum6;
m->m_len = sizeof(int);
*mtod(m, int *) = optval;
break;
default:
error = EINVAL;
break;
}
break;
default:
error = ENOPROTOOPT;
break;
}
return (error);
}
/*
* initialize ip6_pktopts. beware that there are non-zero default values in
* the struct.
*/
void
ip6_initpktopts(struct ip6_pktopts *opt)
{
bzero(opt, sizeof(*opt));
opt->ip6po_hlim = -1; /* -1 means default hop limit */
opt->ip6po_tclass = -1; /* -1 means default traffic class */
opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
}
int
ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt,
int priv, int uproto)
{
struct ip6_pktopts *opt;
if (*pktopt == NULL) {
*pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT,
M_WAITOK);
ip6_initpktopts(*pktopt);
}
opt = *pktopt;
return (ip6_setpktopt(optname, buf, len, opt, priv, 1, uproto));
}
int
ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct mbuf *m)
{
void *optdata = NULL;
int optdatalen = 0;
struct ip6_ext *ip6e;
int error = 0;
struct in6_pktinfo null_pktinfo;
int deftclass = 0, on;
int defminmtu = IP6PO_MINMTU_MCASTONLY;
switch (optname) {
case IPV6_PKTINFO:
if (pktopt && pktopt->ip6po_pktinfo)
optdata = (void *)pktopt->ip6po_pktinfo;
else {
/* XXX: we don't have to do this every time... */
bzero(&null_pktinfo, sizeof(null_pktinfo));
optdata = (void *)&null_pktinfo;
}
optdatalen = sizeof(struct in6_pktinfo);
break;
case IPV6_TCLASS:
if (pktopt && pktopt->ip6po_tclass >= 0)
optdata = (void *)&pktopt->ip6po_tclass;
else
optdata = (void *)&deftclass;
optdatalen = sizeof(int);
break;
case IPV6_HOPOPTS:
if (pktopt && pktopt->ip6po_hbh) {
optdata = (void *)pktopt->ip6po_hbh;
ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
optdatalen = (ip6e->ip6e_len + 1) << 3;
}
break;
case IPV6_RTHDR:
if (pktopt && pktopt->ip6po_rthdr) {
optdata = (void *)pktopt->ip6po_rthdr;
ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
optdatalen = (ip6e->ip6e_len + 1) << 3;
}
break;
case IPV6_RTHDRDSTOPTS:
if (pktopt && pktopt->ip6po_dest1) {
optdata = (void *)pktopt->ip6po_dest1;
ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
optdatalen = (ip6e->ip6e_len + 1) << 3;
}
break;
case IPV6_DSTOPTS:
if (pktopt && pktopt->ip6po_dest2) {
optdata = (void *)pktopt->ip6po_dest2;
ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
optdatalen = (ip6e->ip6e_len + 1) << 3;
}
break;
case IPV6_USE_MIN_MTU:
if (pktopt)
optdata = (void *)&pktopt->ip6po_minmtu;
else
optdata = (void *)&defminmtu;
optdatalen = sizeof(int);
break;
case IPV6_DONTFRAG:
if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
on = 1;
else
on = 0;
optdata = (void *)&on;
optdatalen = sizeof(on);
break;
default: /* should not happen */
#ifdef DIAGNOSTIC
panic("%s: unexpected option", __func__);
#endif
return (ENOPROTOOPT);
}
if (optdatalen > MCLBYTES)
return (EMSGSIZE); /* XXX */
if (optdatalen > MLEN)
MCLGET(m, M_WAIT);
m->m_len = optdatalen;
if (optdatalen)
bcopy(optdata, mtod(m, void *), optdatalen);
return (error);
}
void
ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname)
{
if (optname == -1 || optname == IPV6_PKTINFO) {
if (pktopt->ip6po_pktinfo)
free(pktopt->ip6po_pktinfo, M_IP6OPT, 0);
pktopt->ip6po_pktinfo = NULL;
}
if (optname == -1 || optname == IPV6_HOPLIMIT)
pktopt->ip6po_hlim = -1;
if (optname == -1 || optname == IPV6_TCLASS)
pktopt->ip6po_tclass = -1;
if (optname == -1 || optname == IPV6_HOPOPTS) {
if (pktopt->ip6po_hbh)
free(pktopt->ip6po_hbh, M_IP6OPT, 0);
pktopt->ip6po_hbh = NULL;
}
if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
if (pktopt->ip6po_dest1)
free(pktopt->ip6po_dest1, M_IP6OPT, 0);
pktopt->ip6po_dest1 = NULL;
}
if (optname == -1 || optname == IPV6_RTHDR) {
if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT, 0);
pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
if (pktopt->ip6po_route.ro_rt) {
rtfree(pktopt->ip6po_route.ro_rt);
pktopt->ip6po_route.ro_rt = NULL;
}
}
if (optname == -1 || optname == IPV6_DSTOPTS) {
if (pktopt->ip6po_dest2)
free(pktopt->ip6po_dest2, M_IP6OPT, 0);
pktopt->ip6po_dest2 = NULL;
}
}
#define PKTOPT_EXTHDRCPY(type) \
do {\
if (src->type) {\
size_t hlen;\
hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\
dst->type = malloc(hlen, M_IP6OPT, M_NOWAIT);\
if (dst->type == NULL)\
goto bad;\
memcpy(dst->type, src->type, hlen);\
}\
} while (/*CONSTCOND*/ 0)
int
copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src)
{
dst->ip6po_hlim = src->ip6po_hlim;
dst->ip6po_tclass = src->ip6po_tclass;
dst->ip6po_flags = src->ip6po_flags;
if (src->ip6po_pktinfo) {
dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo),
M_IP6OPT, M_NOWAIT);
if (dst->ip6po_pktinfo == NULL)
goto bad;
*dst->ip6po_pktinfo = *src->ip6po_pktinfo;
}
PKTOPT_EXTHDRCPY(ip6po_hbh);
PKTOPT_EXTHDRCPY(ip6po_dest1);
PKTOPT_EXTHDRCPY(ip6po_dest2);
PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */
return (0);
bad:
ip6_clearpktopts(dst, -1);
return (ENOBUFS);
}
#undef PKTOPT_EXTHDRCPY
void
ip6_freepcbopts(struct ip6_pktopts *pktopt)
{
if (pktopt == NULL)
return;
ip6_clearpktopts(pktopt, -1);
free(pktopt, M_IP6OPT, 0);
}
/*
* Set the IP6 multicast options in response to user setsockopt().
*/
int
ip6_setmoptions(int optname, struct ip6_moptions **im6op, struct mbuf *m,
unsigned int rtableid)
{
int error = 0;
u_int loop, ifindex;
struct ipv6_mreq *mreq;
struct ifnet *ifp;
struct ip6_moptions *im6o = *im6op;
struct in6_multi_mship *imm;
struct proc *p = curproc; /* XXX */
if (im6o == NULL) {
/*
* No multicast option buffer attached to the pcb;
* allocate one and initialize to default values.
*/
im6o = malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK);
if (im6o == NULL)
return (ENOBUFS);
*im6op = im6o;
im6o->im6o_ifidx = 0;
im6o->im6o_hlim = ip6_defmcasthlim;
im6o->im6o_loop = IPV6_DEFAULT_MULTICAST_LOOP;
LIST_INIT(&im6o->im6o_memberships);
}
switch (optname) {
case IPV6_MULTICAST_IF:
/*
* Select the interface for outgoing multicast packets.
*/
if (m == NULL || m->m_len != sizeof(u_int)) {
error = EINVAL;
break;
}
memcpy(&ifindex, mtod(m, u_int *), sizeof(ifindex));
if (ifindex != 0) {
ifp = if_get(ifindex);
if (ifp == NULL) {
error = ENXIO; /* XXX EINVAL? */
break;
}
if (ifp->if_rdomain != rtable_l2(rtableid) ||
(ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
if_put(ifp);
break;
}
if_put(ifp);
}
im6o->im6o_ifidx = ifindex;
break;
case IPV6_MULTICAST_HOPS:
{
/*
* Set the IP6 hoplimit for outgoing multicast packets.
*/
int optval;
if (m == NULL || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
memcpy(&optval, mtod(m, u_int *), sizeof(optval));
if (optval < -1 || optval >= 256)
error = EINVAL;
else if (optval == -1)
im6o->im6o_hlim = ip6_defmcasthlim;
else
im6o->im6o_hlim = optval;
break;
}
case IPV6_MULTICAST_LOOP:
/*
* Set the loopback flag for outgoing multicast packets.
* Must be zero or one.
*/
if (m == NULL || m->m_len != sizeof(u_int)) {
error = EINVAL;
break;
}
memcpy(&loop, mtod(m, u_int *), sizeof(loop));
if (loop > 1) {
error = EINVAL;
break;
}
im6o->im6o_loop = loop;
break;
case IPV6_JOIN_GROUP:
/*
* Add a multicast group membership.
* Group must be a valid IP6 multicast address.
*/
if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
error = EINVAL;
break;
}
mreq = mtod(m, struct ipv6_mreq *);
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
/*
* We use the unspecified address to specify to accept
* all multicast addresses. Only super user is allowed
* to do this.
*/
if (suser(p))
{
error = EACCES;
break;
}
} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
error = EINVAL;
break;
}
/*
* If no interface was explicitly specified, choose an
* appropriate one according to the given multicast address.
*/
if (mreq->ipv6mr_interface == 0) {
struct rtentry *rt;
struct sockaddr_in6 dst;
memset(&dst, 0, sizeof(dst));
dst.sin6_len = sizeof(dst);
dst.sin6_family = AF_INET6;
dst.sin6_addr = mreq->ipv6mr_multiaddr;
rt = rtalloc(sin6tosa(&dst), RT_RESOLVE, rtableid);
if (rt == NULL) {
error = EADDRNOTAVAIL;
break;
}
ifp = if_get(rt->rt_ifidx);
rtfree(rt);
} else {
/*
* If the interface is specified, validate it.
*/
ifp = if_get(mreq->ipv6mr_interface);
if (ifp == NULL) {
error = ENXIO; /* XXX EINVAL? */
break;
}
}
/*
* See if we found an interface, and confirm that it
* supports multicast
*/
if (ifp == NULL || ifp->if_rdomain != rtable_l2(rtableid) ||
(ifp->if_flags & IFF_MULTICAST) == 0) {
if_put(ifp);
error = EADDRNOTAVAIL;
break;
}
/*
* Put interface index into the multicast address,
* if the address has link/interface-local scope.
*/
if (IN6_IS_SCOPE_EMBED(&mreq->ipv6mr_multiaddr)) {
mreq->ipv6mr_multiaddr.s6_addr16[1] =
htons(ifp->if_index);
}
/*
* See if the membership already exists.
*/
LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain)
if (imm->i6mm_maddr->in6m_ifidx == ifp->if_index &&
IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
&mreq->ipv6mr_multiaddr))
break;
if (imm != NULL) {
if_put(ifp);
error = EADDRINUSE;
break;
}
/*
* Everything looks good; add a new record to the multicast
* address list for the given interface.
*/
imm = in6_joingroup(ifp, &mreq->ipv6mr_multiaddr, &error);
if_put(ifp);
if (!imm)
break;
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
break;
case IPV6_LEAVE_GROUP:
/*
* Drop a multicast group membership.
* Group must be a valid IP6 multicast address.
*/
if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
error = EINVAL;
break;
}
mreq = mtod(m, struct ipv6_mreq *);
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
if (suser(p)) {
error = EACCES;
break;
}
} else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
error = EINVAL;
break;
}
/*
* Put interface index into the multicast address,
* if the address has link-local scope.
*/
if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
mreq->ipv6mr_multiaddr.s6_addr16[1] =
htons(mreq->ipv6mr_interface);
}
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
*/
if (mreq->ipv6mr_interface == 0)
ifp = NULL;
else {
ifp = if_get(mreq->ipv6mr_interface);
if (ifp == NULL) {
error = ENXIO; /* XXX EINVAL? */
break;
}
}
/*
* Find the membership in the membership list.
*/
LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) {
if ((ifp == NULL ||
imm->i6mm_maddr->in6m_ifidx == ifp->if_index) &&
IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
&mreq->ipv6mr_multiaddr))
break;
}
if_put(ifp);
if (imm == NULL) {
/* Unable to resolve interface */
error = EADDRNOTAVAIL;
break;
}
/*
* Give up the multicast address record to which the
* membership points.
*/
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
break;
default:
error = EOPNOTSUPP;
break;
}
/*
* If all options have default values, no need to keep the option
* structure.
*/
if (im6o->im6o_ifidx == 0 &&
im6o->im6o_hlim == ip6_defmcasthlim &&
im6o->im6o_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
LIST_EMPTY(&im6o->im6o_memberships)) {
free(*im6op, M_IPMOPTS, sizeof(**im6op));
*im6op = NULL;
}
return (error);
}
/*
* Return the IP6 multicast options in response to user getsockopt().
*/
int
ip6_getmoptions(int optname, struct ip6_moptions *im6o, struct mbuf *m)
{
u_int *hlim, *loop, *ifindex;
switch (optname) {
case IPV6_MULTICAST_IF:
ifindex = mtod(m, u_int *);
m->m_len = sizeof(u_int);
if (im6o == NULL || im6o->im6o_ifidx == 0)
*ifindex = 0;
else
*ifindex = im6o->im6o_ifidx;
return (0);
case IPV6_MULTICAST_HOPS:
hlim = mtod(m, u_int *);
m->m_len = sizeof(u_int);
if (im6o == NULL)
*hlim = ip6_defmcasthlim;
else
*hlim = im6o->im6o_hlim;
return (0);
case IPV6_MULTICAST_LOOP:
loop = mtod(m, u_int *);
m->m_len = sizeof(u_int);
if (im6o == NULL)
*loop = ip6_defmcasthlim;
else
*loop = im6o->im6o_loop;
return (0);
default:
return (EOPNOTSUPP);
}
}
/*
* Discard the IP6 multicast options.
*/
void
ip6_freemoptions(struct ip6_moptions *im6o)
{
struct in6_multi_mship *imm;
if (im6o == NULL)
return;
while (!LIST_EMPTY(&im6o->im6o_memberships)) {
imm = LIST_FIRST(&im6o->im6o_memberships);
LIST_REMOVE(imm, i6mm_chain);
in6_leavegroup(imm);
}
free(im6o, M_IPMOPTS, sizeof(*im6o));
}
/*
* Set IPv6 outgoing packet options based on advanced API.
*/
int
ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt,
struct ip6_pktopts *stickyopt, int priv, int uproto)
{
u_int clen;
struct cmsghdr *cm = 0;
caddr_t cmsgs;
int error;
if (control == NULL || opt == NULL)
return (EINVAL);
ip6_initpktopts(opt);
if (stickyopt) {
int error;
/*
* If stickyopt is provided, make a local copy of the options
* for this particular packet, then override them by ancillary
* objects.
* XXX: copypktopts() does not copy the cached route to a next
* hop (if any). This is not very good in terms of efficiency,
* but we can allow this since this option should be rarely
* used.
*/
if ((error = copypktopts(opt, stickyopt)) != 0)
return (error);
}
/*
* XXX: Currently, we assume all the optional information is stored
* in a single mbuf.
*/
if (control->m_next)
return (EINVAL);
clen = control->m_len;
cmsgs = mtod(control, caddr_t);
do {
if (clen < CMSG_LEN(0))
return (EINVAL);
cm = (struct cmsghdr *)cmsgs;
if (cm->cmsg_len < CMSG_LEN(0) || cm->cmsg_len > clen ||
CMSG_ALIGN(cm->cmsg_len) > clen)
return (EINVAL);
if (cm->cmsg_level == IPPROTO_IPV6) {
error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm),
cm->cmsg_len - CMSG_LEN(0), opt, priv, 0, uproto);
if (error)
return (error);
}
clen -= CMSG_ALIGN(cm->cmsg_len);
cmsgs += CMSG_ALIGN(cm->cmsg_len);
} while (clen);
return (0);
}
/*
* Set a particular packet option, as a sticky option or an ancillary data
* item. "len" can be 0 only when it's a sticky option.
*/
int
ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
int priv, int sticky, int uproto)
{
int minmtupolicy;
switch (optname) {
case IPV6_PKTINFO:
{
struct ifnet *ifp = NULL;
struct in6_pktinfo *pktinfo;
if (len != sizeof(struct in6_pktinfo))
return (EINVAL);
pktinfo = (struct in6_pktinfo *)buf;
/*
* An application can clear any sticky IPV6_PKTINFO option by
* doing a "regular" setsockopt with ipi6_addr being
* in6addr_any and ipi6_ifindex being zero.
* [RFC 3542, Section 6]
*/
if (opt->ip6po_pktinfo &&
pktinfo->ipi6_ifindex == 0 &&
IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
ip6_clearpktopts(opt, optname);
break;
}
if (uproto == IPPROTO_TCP &&
sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
return (EINVAL);
}
if (pktinfo->ipi6_ifindex) {
ifp = if_get(pktinfo->ipi6_ifindex);
if (ifp == NULL)
return (ENXIO);
if_put(ifp);
}
/*
* We store the address anyway, and let in6_selectsrc()
* validate the specified address. This is because ipi6_addr
* may not have enough information about its scope zone, and
* we may need additional information (such as outgoing
* interface or the scope zone of a destination address) to
* disambiguate the scope.
* XXX: the delay of the validation may confuse the
* application when it is used as a sticky option.
*/
if (opt->ip6po_pktinfo == NULL) {
opt->ip6po_pktinfo = malloc(sizeof(*pktinfo),
M_IP6OPT, M_NOWAIT);
if (opt->ip6po_pktinfo == NULL)
return (ENOBUFS);
}
bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
break;
}
case IPV6_HOPLIMIT:
{
int *hlimp;
/*
* RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
* to simplify the ordering among hoplimit options.
*/
if (sticky)
return (ENOPROTOOPT);
if (len != sizeof(int))
return (EINVAL);
hlimp = (int *)buf;
if (*hlimp < -1 || *hlimp > 255)
return (EINVAL);
opt->ip6po_hlim = *hlimp;
break;
}
case IPV6_TCLASS:
{
int tclass;
if (len != sizeof(int))
return (EINVAL);
tclass = *(int *)buf;
if (tclass < -1 || tclass > 255)
return (EINVAL);
opt->ip6po_tclass = tclass;
break;
}
case IPV6_HOPOPTS:
{
struct ip6_hbh *hbh;
int hbhlen;
/*
* XXX: We don't allow a non-privileged user to set ANY HbH
* options, since per-option restriction has too much
* overhead.
*/
if (!priv)
return (EPERM);
if (len == 0) {
ip6_clearpktopts(opt, IPV6_HOPOPTS);
break; /* just remove the option */
}
/* message length validation */
if (len < sizeof(struct ip6_hbh))
return (EINVAL);
hbh = (struct ip6_hbh *)buf;
hbhlen = (hbh->ip6h_len + 1) << 3;
if (len != hbhlen)
return (EINVAL);
/* turn off the previous option, then set the new option. */
ip6_clearpktopts(opt, IPV6_HOPOPTS);
opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT);
if (opt->ip6po_hbh == NULL)
return (ENOBUFS);
memcpy(opt->ip6po_hbh, hbh, hbhlen);
break;
}
case IPV6_DSTOPTS:
case IPV6_RTHDRDSTOPTS:
{
struct ip6_dest *dest, **newdest = NULL;
int destlen;
if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */
return (EPERM);
if (len == 0) {
ip6_clearpktopts(opt, optname);
break; /* just remove the option */
}
/* message length validation */
if (len < sizeof(struct ip6_dest))
return (EINVAL);
dest = (struct ip6_dest *)buf;
destlen = (dest->ip6d_len + 1) << 3;
if (len != destlen)
return (EINVAL);
/*
* Determine the position that the destination options header
* should be inserted; before or after the routing header.
*/
switch (optname) {
case IPV6_RTHDRDSTOPTS:
newdest = &opt->ip6po_dest1;
break;
case IPV6_DSTOPTS:
newdest = &opt->ip6po_dest2;
break;
}
/* turn off the previous option, then set the new option. */
ip6_clearpktopts(opt, optname);
*newdest = malloc(destlen, M_IP6OPT, M_NOWAIT);
if (*newdest == NULL)
return (ENOBUFS);
memcpy(*newdest, dest, destlen);
break;
}
case IPV6_RTHDR:
{
struct ip6_rthdr *rth;
int rthlen;
if (len == 0) {
ip6_clearpktopts(opt, IPV6_RTHDR);
break; /* just remove the option */
}
/* message length validation */
if (len < sizeof(struct ip6_rthdr))
return (EINVAL);
rth = (struct ip6_rthdr *)buf;
rthlen = (rth->ip6r_len + 1) << 3;
if (len != rthlen)
return (EINVAL);
switch (rth->ip6r_type) {
case IPV6_RTHDR_TYPE_0:
if (rth->ip6r_len == 0) /* must contain one addr */
return (EINVAL);
if (rth->ip6r_len % 2) /* length must be even */
return (EINVAL);
if (rth->ip6r_len / 2 != rth->ip6r_segleft)
return (EINVAL);
break;
default:
return (EINVAL); /* not supported */
}
/* turn off the previous option */
ip6_clearpktopts(opt, IPV6_RTHDR);
opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT);
if (opt->ip6po_rthdr == NULL)
return (ENOBUFS);
memcpy(opt->ip6po_rthdr, rth, rthlen);
break;
}
case IPV6_USE_MIN_MTU:
if (len != sizeof(int))
return (EINVAL);
minmtupolicy = *(int *)buf;
if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
minmtupolicy != IP6PO_MINMTU_DISABLE &&
minmtupolicy != IP6PO_MINMTU_ALL) {
return (EINVAL);
}
opt->ip6po_minmtu = minmtupolicy;
break;
case IPV6_DONTFRAG:
if (len != sizeof(int))
return (EINVAL);
if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
/*
* we ignore this option for TCP sockets.
* (RFC3542 leaves this case unspecified.)
*/
opt->ip6po_flags &= ~IP6PO_DONTFRAG;
} else
opt->ip6po_flags |= IP6PO_DONTFRAG;
break;
default:
return (ENOPROTOOPT);
} /* end of switch */
return (0);
}
/*
* Routine called from ip6_output() to loop back a copy of an IP6 multicast
* packet to the input queue of a specified interface.
*/
void
ip6_mloopback(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst)
{
struct mbuf *copym;
struct ip6_hdr *ip6;
/*
* Duplicate the packet.
*/
copym = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (copym == NULL)
return;
/*
* Make sure to deep-copy IPv6 header portion in case the data
* is in an mbuf cluster, so that we can safely override the IPv6
* header portion later.
*/
if ((copym->m_flags & M_EXT) != 0 ||
copym->m_len < sizeof(struct ip6_hdr)) {
copym = m_pullup(copym, sizeof(struct ip6_hdr));
if (copym == NULL)
return;
}
#ifdef DIAGNOSTIC
if (copym->m_len < sizeof(*ip6)) {
m_freem(copym);
return;
}
#endif
ip6 = mtod(copym, struct ip6_hdr *);
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = 0;
if_input_local(ifp, copym, dst->sin6_family);
}
/*
* Chop IPv6 header off from the payload.
*/
int
ip6_splithdr(struct mbuf *m, struct ip6_exthdrs *exthdrs)
{
struct mbuf *mh;
struct ip6_hdr *ip6;
ip6 = mtod(m, struct ip6_hdr *);
if (m->m_len > sizeof(*ip6)) {
MGET(mh, M_DONTWAIT, MT_HEADER);
if (mh == NULL) {
m_freem(m);
return ENOBUFS;
}
M_MOVE_PKTHDR(mh, m);
m_align(mh, sizeof(*ip6));
m->m_len -= sizeof(*ip6);
m->m_data += sizeof(*ip6);
mh->m_next = m;
m = mh;
m->m_len = sizeof(*ip6);
bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
}
exthdrs->ip6e_ip6 = m;
return 0;
}
u_int32_t
ip6_randomid(void)
{
return idgen32(&ip6_id_ctx);
}
void
ip6_randomid_init(void)
{
idgen32_init(&ip6_id_ctx);
}
/*
* Compute significant parts of the IPv6 checksum pseudo-header
* for use in a delayed TCP/UDP checksum calculation.
*/
static __inline u_int16_t __attribute__((__unused__))
in6_cksum_phdr(const struct in6_addr *src, const struct in6_addr *dst,
u_int32_t len, u_int32_t nxt)
{
u_int32_t sum = 0;
const u_int16_t *w;
w = (const u_int16_t *) src;
sum += w[0];
if (!IN6_IS_SCOPE_EMBED(src))
sum += w[1];
sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
sum += w[6]; sum += w[7];
w = (const u_int16_t *) dst;
sum += w[0];
if (!IN6_IS_SCOPE_EMBED(dst))
sum += w[1];
sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
sum += w[6]; sum += w[7];
sum += (u_int16_t)(len >> 16) + (u_int16_t)(len /*& 0xffff*/);
sum += (u_int16_t)(nxt >> 16) + (u_int16_t)(nxt /*& 0xffff*/);
sum = (u_int16_t)(sum >> 16) + (u_int16_t)(sum /*& 0xffff*/);
if (sum > 0xffff)
sum -= 0xffff;
return (sum);
}
/*
* Process a delayed payload checksum calculation.
*/
void
in6_delayed_cksum(struct mbuf *m, u_int8_t nxt)
{
int nxtp, offset;
u_int16_t csum;
offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxtp);
if (offset <= 0 || nxtp != nxt)
/* If the desired next protocol isn't found, punt. */
return;
csum = (u_int16_t)(in6_cksum(m, 0, offset, m->m_pkthdr.len - offset));
switch (nxt) {
case IPPROTO_TCP:
offset += offsetof(struct tcphdr, th_sum);
break;
case IPPROTO_UDP:
offset += offsetof(struct udphdr, uh_sum);
if (csum == 0)
csum = 0xffff;
break;
case IPPROTO_ICMPV6:
offset += offsetof(struct icmp6_hdr, icmp6_cksum);
break;
}
if ((offset + sizeof(u_int16_t)) > m->m_len)
m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
else
*(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
}
void
in6_proto_cksum_out(struct mbuf *m, struct ifnet *ifp)
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
/* some hw and in6_delayed_cksum need the pseudo header cksum */
if (m->m_pkthdr.csum_flags &
(M_TCP_CSUM_OUT|M_UDP_CSUM_OUT|M_ICMP_CSUM_OUT)) {
int nxt, offset;
u_int16_t csum;
offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
csum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst,
htonl(m->m_pkthdr.len - offset), htonl(nxt));
if (nxt == IPPROTO_TCP)
offset += offsetof(struct tcphdr, th_sum);
else if (nxt == IPPROTO_UDP)
offset += offsetof(struct udphdr, uh_sum);
else if (nxt == IPPROTO_ICMPV6)
offset += offsetof(struct icmp6_hdr, icmp6_cksum);
if ((offset + sizeof(u_int16_t)) > m->m_len)
m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT);
else
*(u_int16_t *)(mtod(m, caddr_t) + offset) = csum;
}
if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) ||
ip6->ip6_nxt != IPPROTO_TCP ||
ifp->if_bridgeidx != 0) {
tcpstat_inc(tcps_outswcsum);
in6_delayed_cksum(m, IPPROTO_TCP);
m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
}
} else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv6) ||
ip6->ip6_nxt != IPPROTO_UDP ||
ifp->if_bridgeidx != 0) {
udpstat_inc(udps_outswcsum);
in6_delayed_cksum(m, IPPROTO_UDP);
m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
}
} else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) {
in6_delayed_cksum(m, IPPROTO_ICMPV6);
m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */
}
}
#ifdef IPSEC
int
ip6_output_ipsec_lookup(struct mbuf *m, struct inpcb *inp, struct tdb **tdbout)
{
struct tdb *tdb;
struct m_tag *mtag;
struct tdb_ident *tdbi;
int error;
/*
* Check if there was an outgoing SA bound to the flow
* from a transport protocol.
*/
/* Do we have any pending SAs to apply ? */
error = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr),
IPSP_DIRECTION_OUT, NULL, inp, &tdb, NULL);
if (error || tdb == NULL) {
*tdbout = NULL;
return error;
}
/* Loop detection */
for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) {
if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE)
continue;
tdbi = (struct tdb_ident *)(mtag + 1);
if (tdbi->spi == tdb->tdb_spi &&
tdbi->proto == tdb->tdb_sproto &&
tdbi->rdomain == tdb->tdb_rdomain &&
!memcmp(&tdbi->dst, &tdb->tdb_dst,
sizeof(union sockaddr_union))) {
/* no IPsec needed */
tdb_unref(tdb);
*tdbout = NULL;
return 0;
}
}
*tdbout = tdb;
return 0;
}
int
ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route_in6 *ro,
struct in6_addr *dst, int ifidx, int rtableid, int transportmode)
{
struct rtentry *rt = NULL;
int rt_mtucloned = 0;
/* Find a host route to store the mtu in */
if (ro != NULL)
rt = ro->ro_rt;
/* but don't add a PMTU route for transport mode SAs */
if (transportmode)
rt = NULL;
else if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0) {
struct sockaddr_in6 sin6;
int error;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(sin6);
sin6.sin6_addr = *dst;
sin6.sin6_scope_id = in6_addr2scopeid(ifidx, dst);
error = in6_embedscope(dst, &sin6, NULL);
if (error) {
/* should be impossible */
return error;
}
rt = icmp6_mtudisc_clone(&sin6, rtableid, 1);
rt_mtucloned = 1;
}
DPRINTF("spi %08x mtu %d rt %p cloned %d",
ntohl(tdb->tdb_spi), tdb->tdb_mtu, rt, rt_mtucloned);
if (rt != NULL) {
rt->rt_mtu = tdb->tdb_mtu;
if (ro != NULL && ro->ro_rt != NULL) {
rtfree(ro->ro_rt);
ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), RT_RESOLVE,
rtableid);
}
if (rt_mtucloned)
rtfree(rt);
}
return 0;
}
int
ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route_in6 *ro,
int tunalready, int fwd)
{
#if NPF > 0
struct ifnet *encif;
#endif
struct ip6_hdr *ip6;
struct in6_addr dst;
int error, ifidx, rtableid;
#if NPF > 0
/*
* Packet filter
*/
if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL ||
pf_test(AF_INET6, fwd ? PF_FWD : PF_OUT, encif, &m) != PF_PASS) {
m_freem(m);
return EACCES;
}
if (m == NULL)
return 0;
/*
* PF_TAG_REROUTE handling or not...
* Packet is entering IPsec so the routing is
* already overruled by the IPsec policy.
* Until now the change was not reconsidered.
* What's the behaviour?
*/
in6_proto_cksum_out(m, encif);
#endif
/* Check if we are allowed to fragment */
ip6 = mtod(m, struct ip6_hdr *);
dst = ip6->ip6_dst;
ifidx = m->m_pkthdr.ph_ifidx;
rtableid = m->m_pkthdr.ph_rtableid;
if (ip_mtudisc && tdb->tdb_mtu &&
sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) > tdb->tdb_mtu &&
tdb->tdb_mtutimeout > gettime()) {
int transportmode;
transportmode = (tdb->tdb_dst.sa.sa_family == AF_INET6) &&
(IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, &dst));
error = ip6_output_ipsec_pmtu_update(tdb, ro, &dst, ifidx,
rtableid, transportmode);
if (error) {
ipsecstat_inc(ipsec_odrops);
tdbstat_inc(tdb, tdb_odrops);
m_freem(m);
return error;
}
ipsec_adjust_mtu(m, tdb->tdb_mtu);
m_freem(m);
return EMSGSIZE;
}
/* propagate don't fragment for v6-over-v6 */
if (ip_mtudisc)
SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT);
/*
* Clear these -- they'll be set in the recursive invocation
* as needed.
*/
m->m_flags &= ~(M_BCAST | M_MCAST);
/* Callee frees mbuf */
KERNEL_LOCK();
error = ipsp_process_packet(m, tdb, AF_INET6, tunalready);
KERNEL_UNLOCK();
if (error) {
ipsecstat_inc(ipsec_odrops);
tdbstat_inc(tdb, tdb_odrops);
}
if (ip_mtudisc && error == EMSGSIZE)
ip6_output_ipsec_pmtu_update(tdb, ro, &dst, ifidx, rtableid, 0);
return error;
}
#endif /* IPSEC */
4
7
5
4
4
7
5
5
7
3
10
3
2
4
3
2
4
42
42
34
34
1
13
1
3
2
2
1
1
2
1
1
1
1
1
2
1
3
1
4
1
5
6
13
4
1
1
8
3
8
8
5
2
2
1
4
6
5
5
1
3
23
1
3
1
1
1
13
8
1
15
3
6
4
1
11
13
10
4
3
5
2
5
7
3
7
5
3
7
10
10
3
13
5
14
2
8
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
/* $OpenBSD: sysv_sem.c,v 1.61 2021/04/30 13:52:48 bluhm Exp $ */
/* $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $ */
/*
* Copyright (c) 2002,2003 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*
* Implementation of SVID semaphores
*
* Author: Daniel Boulet
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sem.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#ifdef SEM_DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif
int semtot = 0;
int semutot = 0;
struct semid_ds **sema; /* semaphore id list */
SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
struct pool sema_pool; /* pool for struct semid_ds */
struct pool semu_pool; /* pool for struct sem_undo (SEMUSZ) */
unsigned short *semseqs; /* array of sem sequence numbers */
struct sem_undo *semu_alloc(struct process *);
int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
void semundo_clear(int, int);
void
seminit(void)
{
pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK,
"semapl", NULL);
pool_init(&semu_pool, SEMUSZ, 0, 0, PR_WAITOK, "semupl", NULL);
sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *),
M_SEM, M_WAITOK|M_ZERO);
semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short),
M_SEM, M_WAITOK|M_ZERO);
SLIST_INIT(&semu_list);
}
/*
* Allocate a new sem_undo structure for a process
* (returns ptr to structure or NULL if no more room)
*/
struct sem_undo *
semu_alloc(struct process *pr)
{
struct sem_undo *suptr, *sutmp;
if (semutot == seminfo.semmnu)
return (NULL); /* no space */
/*
* Allocate a semu w/o waiting if possible.
* If we do have to wait, we must check to verify that a semu
* with un_proc == pr has not been allocated in the meantime.
*/
semutot++;
if ((suptr = pool_get(&semu_pool, PR_NOWAIT)) == NULL) {
sutmp = pool_get(&semu_pool, PR_WAITOK);
SLIST_FOREACH(suptr, &semu_list, un_next) {
if (suptr->un_proc == pr) {
pool_put(&semu_pool, sutmp);
semutot--;
return (suptr);
}
}
suptr = sutmp;
}
suptr->un_cnt = 0;
suptr->un_proc = pr;
SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
return (suptr);
}
/*
* Adjust a particular entry for a particular proc
*/
int
semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
int adjval)
{
struct process *pr = p->p_p;
struct sem_undo *suptr;
struct undo *sunptr;
int i;
/*
* Look for and remember the sem_undo if the caller doesn't provide it.
*/
suptr = *supptr;
if (suptr == NULL) {
SLIST_FOREACH(suptr, &semu_list, un_next) {
if (suptr->un_proc == pr) {
*supptr = suptr;
break;
}
}
if (suptr == NULL) {
if (adjval == 0)
return (0);
suptr = semu_alloc(p->p_p);
if (suptr == NULL)
return (ENOSPC);
*supptr = suptr;
}
}
/*
* Look for the requested entry and adjust it
* (delete if adjval becomes 0).
*/
sunptr = &suptr->un_ent[0];
for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
if (sunptr->un_id != semid || sunptr->un_num != semnum)
continue;
if (adjval == 0)
sunptr->un_adjval = 0;
else
sunptr->un_adjval += adjval;
if (sunptr->un_adjval != 0)
return (0);
if (--suptr->un_cnt == 0) {
*supptr = NULL;
SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
pool_put(&semu_pool, suptr);
semutot--;
} else if (i < suptr->un_cnt)
suptr->un_ent[i] =
suptr->un_ent[suptr->un_cnt];
return (0);
}
/* Didn't find the right entry - create it */
if (adjval == 0)
return (0);
if (suptr->un_cnt == SEMUME)
return (EINVAL);
sunptr = &suptr->un_ent[suptr->un_cnt];
suptr->un_cnt++;
sunptr->un_adjval = adjval;
sunptr->un_id = semid;
sunptr->un_num = semnum;
return (0);
}
void
semundo_clear(int semid, int semnum)
{
struct sem_undo *suptr = SLIST_FIRST(&semu_list);
struct sem_undo *suprev = NULL;
struct undo *sunptr;
int i;
while (suptr != NULL) {
sunptr = &suptr->un_ent[0];
for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
if (sunptr->un_id == semid) {
if (semnum == -1 || sunptr->un_num == semnum) {
suptr->un_cnt--;
if (i < suptr->un_cnt) {
suptr->un_ent[i] =
suptr->un_ent[suptr->un_cnt];
i--, sunptr--;
}
}
if (semnum != -1)
break;
}
}
if (suptr->un_cnt == 0) {
struct sem_undo *sutmp = suptr;
if (suptr == SLIST_FIRST(&semu_list))
SLIST_REMOVE_HEAD(&semu_list, un_next);
else
SLIST_REMOVE_AFTER(suprev, un_next);
suptr = SLIST_NEXT(suptr, un_next);
pool_put(&semu_pool, sutmp);
semutot--;
} else {
suprev = suptr;
suptr = SLIST_NEXT(suptr, un_next);
}
}
}
int
sys___semctl(struct proc *p, void *v, register_t *retval)
{
struct sys___semctl_args /* {
syscallarg(int) semid;
syscallarg(int) semnum;
syscallarg(int) cmd;
syscallarg(union semun *) arg;
} */ *uap = v;
union semun arg;
int error = 0, cmd = SCARG(uap, cmd);
switch (cmd) {
case IPC_SET:
case IPC_STAT:
case GETALL:
case SETVAL:
case SETALL:
error = copyin(SCARG(uap, arg), &arg, sizeof(arg));
break;
}
if (error == 0) {
error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum),
cmd, &arg, retval, copyin, copyout);
}
return (error);
}
int
semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg,
register_t *retval, int (*ds_copyin)(const void *, void *, size_t),
int (*ds_copyout)(const void *, void *, size_t))
{
struct ucred *cred = p->p_ucred;
int i, ix, error = 0;
struct semid_ds sbuf;
struct semid_ds *semaptr;
unsigned short *semval = NULL;
DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg));
ix = IPCID_TO_IX(semid);
if (ix < 0 || ix >= seminfo.semmni)
return (EINVAL);
if ((semaptr = sema[ix]) == NULL ||
semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
return (EINVAL);
switch (cmd) {
case IPC_RMID:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
return (error);
semaptr->sem_perm.cuid = cred->cr_uid;
semaptr->sem_perm.uid = cred->cr_uid;
semtot -= semaptr->sem_nsems;
free(semaptr->sem_base, M_SEM,
semaptr->sem_nsems * sizeof(struct sem));
pool_put(&sema_pool, semaptr);
sema[ix] = NULL;
semundo_clear(ix, -1);
wakeup(&sema[ix]);
break;
case IPC_SET:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
return (error);
if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0)
return (error);
semaptr->sem_perm.uid = sbuf.sem_perm.uid;
semaptr->sem_perm.gid = sbuf.sem_perm.gid;
semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
(sbuf.sem_perm.mode & 0777);
semaptr->sem_ctime = gettime();
break;
case IPC_STAT:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
return (error);
memcpy(&sbuf, semaptr, sizeof sbuf);
sbuf.sem_base = NULL;
error = ds_copyout(&sbuf, arg->buf, sizeof(struct semid_ds));
break;
case GETNCNT:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
return (error);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
return (EINVAL);
*retval = semaptr->sem_base[semnum].semncnt;
break;
case GETPID:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
return (error);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
return (EINVAL);
*retval = semaptr->sem_base[semnum].sempid;
break;
case GETVAL:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
return (error);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
return (EINVAL);
*retval = semaptr->sem_base[semnum].semval;
break;
case GETALL:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
return (error);
for (i = 0; i < semaptr->sem_nsems; i++) {
error = ds_copyout(&semaptr->sem_base[i].semval,
&arg->array[i], sizeof(arg->array[0]));
if (error != 0)
break;
}
break;
case GETZCNT:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
return (error);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
return (EINVAL);
*retval = semaptr->sem_base[semnum].semzcnt;
break;
case SETVAL:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
return (error);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
return (EINVAL);
if (arg->val > seminfo.semvmx)
return (ERANGE);
semaptr->sem_base[semnum].semval = arg->val;
semundo_clear(ix, semnum);
wakeup(&sema[ix]);
break;
case SETALL:
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
return (error);
semval = mallocarray(semaptr->sem_nsems, sizeof(arg->array[0]),
M_TEMP, M_WAITOK);
for (i = 0; i < semaptr->sem_nsems; i++) {
error = ds_copyin(&arg->array[i], &semval[i],
sizeof(arg->array[0]));
if (error != 0)
goto error;
if (semval[i] > seminfo.semvmx) {
error = ERANGE;
goto error;
}
}
for (i = 0; i < semaptr->sem_nsems; i++)
semaptr->sem_base[i].semval = semval[i];
semundo_clear(ix, -1);
wakeup(&sema[ix]);
break;
default:
return (EINVAL);
}
error:
if (semval)
free(semval, M_TEMP,
semaptr->sem_nsems * sizeof(arg->array[0]));
return (error);
}
int
sys_semget(struct proc *p, void *v, register_t *retval)
{
struct sys_semget_args /* {
syscallarg(key_t) key;
syscallarg(int) nsems;
syscallarg(int) semflg;
} */ *uap = v;
int semid, error;
int key = SCARG(uap, key);
int nsems = SCARG(uap, nsems);
int semflg = SCARG(uap, semflg);
struct semid_ds *semaptr, *semaptr_new = NULL;
struct ucred *cred = p->p_ucred;
DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
/*
* Preallocate space for the new semaphore. If we are going
* to sleep, we want to sleep now to eliminate any race
* condition in allocating a semaphore with a specific key.
*/
if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
if (nsems <= 0 || nsems > seminfo.semmsl) {
DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
seminfo.semmsl));
return (EINVAL);
}
if (nsems > seminfo.semmns - semtot) {
DPRINTF(("not enough semaphores left (need %d, got %d)\n",
nsems, seminfo.semmns - semtot));
return (ENOSPC);
}
semaptr_new = pool_get(&sema_pool, PR_WAITOK | PR_ZERO);
semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem),
M_SEM, M_WAITOK|M_ZERO);
}
if (key != IPC_PRIVATE) {
for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
if ((semaptr = sema[semid]) != NULL &&
semaptr->sem_perm.key == key) {
DPRINTF(("found public key\n"));
if ((error = ipcperm(cred, &semaptr->sem_perm,
semflg & 0700)))
goto error;
if (nsems > 0 && semaptr->sem_nsems < nsems) {
DPRINTF(("too small\n"));
error = EINVAL;
goto error;
}
if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
DPRINTF(("not exclusive\n"));
error = EEXIST;
goto error;
}
if (semaptr_new != NULL) {
free(semaptr_new->sem_base, M_SEM,
nsems * sizeof(struct sem));
pool_put(&sema_pool, semaptr_new);
}
goto found;
}
}
}
DPRINTF(("need to allocate the semid_ds\n"));
if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
for (semid = 0; semid < seminfo.semmni; semid++) {
if ((semaptr = sema[semid]) == NULL)
break;
}
if (semid == seminfo.semmni) {
DPRINTF(("no more semid_ds's available\n"));
error = ENOSPC;
goto error;
}
DPRINTF(("semid %d is available\n", semid));
semaptr_new->sem_perm.key = key;
semaptr_new->sem_perm.cuid = cred->cr_uid;
semaptr_new->sem_perm.uid = cred->cr_uid;
semaptr_new->sem_perm.cgid = cred->cr_gid;
semaptr_new->sem_perm.gid = cred->cr_gid;
semaptr_new->sem_perm.mode = (semflg & 0777);
semaptr_new->sem_perm.seq = semseqs[semid] =
(semseqs[semid] + 1) & 0x7fff;
semaptr_new->sem_nsems = nsems;
semaptr_new->sem_otime = 0;
semaptr_new->sem_ctime = gettime();
sema[semid] = semaptr_new;
semtot += nsems;
} else {
DPRINTF(("didn't find it and wasn't asked to create it\n"));
return (ENOENT);
}
found:
*retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
return (0);
error:
if (semaptr_new != NULL) {
free(semaptr_new->sem_base, M_SEM, nsems * sizeof(struct sem));
pool_put(&sema_pool, semaptr_new);
}
return (error);
}
int
sys_semop(struct proc *p, void *v, register_t *retval)
{
struct sys_semop_args /* {
syscallarg(int) semid;
syscallarg(struct sembuf *) sops;
syscallarg(size_t) nsops;
} */ *uap = v;
#define NSOPS 8
struct sembuf sopbuf[NSOPS];
int semid = SCARG(uap, semid);
size_t nsops = SCARG(uap, nsops);
struct sembuf *sops;
struct semid_ds *semaptr;
struct sembuf *sopptr = NULL;
struct sem *semptr = NULL;
struct sem_undo *suptr = NULL;
struct ucred *cred = p->p_ucred;
size_t i, j;
int do_wakeup, do_undos, error;
DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
(u_long)nsops));
semid = IPCID_TO_IX(semid); /* Convert back to zero origin */
if (semid < 0 || semid >= seminfo.semmni)
return (EINVAL);
if ((semaptr = sema[semid]) == NULL ||
semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
return (EINVAL);
if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
DPRINTF(("error = %d from ipaccess\n", error));
return (error);
}
if (nsops == 0) {
*retval = 0;
return (0);
} else if (nsops > (size_t)seminfo.semopm) {
DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
(u_long)nsops));
return (E2BIG);
}
if (nsops <= NSOPS)
sops = sopbuf;
else
sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK);
error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
if (error != 0) {
DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
goto done2;
}
/*
* Loop trying to satisfy the vector of requests.
* If we reach a point where we must wait, any requests already
* performed are rolled back and we go to sleep until some other
* process wakes us up. At this point, we start all over again.
*
* This ensures that from the perspective of other tasks, a set
* of requests is atomic (never partially satisfied).
*/
do_undos = 0;
for (;;) {
do_wakeup = 0;
for (i = 0; i < nsops; i++) {
sopptr = &sops[i];
if (sopptr->sem_num >= semaptr->sem_nsems) {
error = EFBIG;
goto done2;
}
semptr = &semaptr->sem_base[sopptr->sem_num];
DPRINTF(("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
semaptr, semaptr->sem_base, semptr,
sopptr->sem_num, semptr->semval, sopptr->sem_op,
(sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
if (sopptr->sem_op < 0) {
if ((int)(semptr->semval +
sopptr->sem_op) < 0) {
DPRINTF(("semop: can't do it now\n"));
break;
} else {
semptr->semval += sopptr->sem_op;
if (semptr->semval == 0 &&
semptr->semzcnt > 0)
do_wakeup = 1;
}
if (sopptr->sem_flg & SEM_UNDO)
do_undos++;
} else if (sopptr->sem_op == 0) {
if (semptr->semval > 0) {
DPRINTF(("semop: not zero now\n"));
break;
}
} else {
if (semptr->semncnt > 0)
do_wakeup = 1;
semptr->semval += sopptr->sem_op;
if (sopptr->sem_flg & SEM_UNDO)
do_undos++;
}
}
/*
* Did we get through the entire vector and can we undo it?
*/
if (i >= nsops && do_undos <= SEMUME)
goto done;
/*
* No ... rollback anything that we've already done
*/
DPRINTF(("semop: rollback 0 through %d\n", i - 1));
for (j = 0; j < i; j++)
semaptr->sem_base[sops[j].sem_num].semval -=
sops[j].sem_op;
/*
* Did we have too many SEM_UNDO's
*/
if (do_undos > SEMUME) {
error = ENOSPC;
goto done2;
}
/*
* If the request that we couldn't satisfy has the
* NOWAIT flag set then return with EAGAIN.
*/
if (sopptr->sem_flg & IPC_NOWAIT) {
error = EAGAIN;
goto done2;
}
if (sopptr->sem_op == 0)
semptr->semzcnt++;
else
semptr->semncnt++;
DPRINTF(("semop: good night!\n"));
error = tsleep_nsec(&sema[semid], PLOCK | PCATCH,
"semwait", INFSLP);
DPRINTF(("semop: good morning (error=%d)!\n", error));
suptr = NULL; /* sem_undo may have been reallocated */
/*
* Make sure that the semaphore still exists
*/
if (sema[semid] == NULL ||
semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
error = EIDRM;
goto done2;
}
/*
* The semaphore is still alive. Readjust the count of
* waiting processes.
*/
if (sopptr->sem_op == 0)
semptr->semzcnt--;
else
semptr->semncnt--;
/*
* Is it really morning, or was our sleep interrupted?
* (Delayed check of tsleep() return code because we
* need to decrement sem[nz]cnt either way.)
*/
if (error != 0) {
error = EINTR;
goto done2;
}
DPRINTF(("semop: good morning!\n"));
}
done:
/*
* Process any SEM_UNDO requests.
*/
if (do_undos) {
for (i = 0; i < nsops; i++) {
/*
* We only need to deal with SEM_UNDO's for non-zero
* op's.
*/
int adjval;
if ((sops[i].sem_flg & SEM_UNDO) == 0)
continue;
adjval = sops[i].sem_op;
if (adjval == 0)
continue;
error = semundo_adjust(p, &suptr, semid,
sops[i].sem_num, -adjval);
if (error == 0)
continue;
/*
* Uh-Oh! We ran out of either sem_undo's or undo's.
* Rollback the adjustments to this point and then
* rollback the semaphore ups and down so we can return
* with an error with all structures restored. We
* rollback the undo's in the exact reverse order that
* we applied them. This guarantees that we won't run
* out of space as we roll things back out.
*/
for (j = i; j > 0;) {
j--;
if ((sops[j].sem_flg & SEM_UNDO) == 0)
continue;
adjval = sops[j].sem_op;
if (adjval == 0)
continue;
if (semundo_adjust(p, &suptr, semid,
sops[j].sem_num, adjval) != 0)
panic("semop - can't undo undos");
}
for (j = 0; j < nsops; j++)
semaptr->sem_base[sops[j].sem_num].semval -=
sops[j].sem_op;
DPRINTF(("error = %d from semundo_adjust\n", error));
goto done2;
} /* loop through the sops */
} /* if (do_undos) */
/* We're definitely done - set the sempid's */
for (i = 0; i < nsops; i++) {
sopptr = &sops[i];
semptr = &semaptr->sem_base[sopptr->sem_num];
semptr->sempid = p->p_p->ps_pid;
}
semaptr->sem_otime = gettime();
/* Do a wakeup if any semaphore was up'd. */
if (do_wakeup) {
DPRINTF(("semop: doing wakeup\n"));
wakeup(&sema[semid]);
DPRINTF(("semop: back from wakeup\n"));
}
DPRINTF(("semop: done\n"));
*retval = 0;
done2:
if (sops != sopbuf)
free(sops, M_SEM, nsops * sizeof(struct sembuf));
return (error);
}
/*
* Go through the undo structures for this process and apply the adjustments to
* semaphores.
*/
void
semexit(struct process *pr)
{
struct sem_undo *suptr;
struct sem_undo **supptr;
/*
* Go through the chain of undo vectors looking for one associated with
* this process. Remember the pointer to the pointer to the element
* to dequeue it later.
*/
supptr = &SLIST_FIRST(&semu_list);
SLIST_FOREACH(suptr, &semu_list, un_next) {
if (suptr->un_proc == pr)
break;
supptr = &SLIST_NEXT(suptr, un_next);
}
/*
* If there is no undo vector, skip to the end.
*/
if (suptr == NULL)
return;
/*
* We now have an undo vector for this process.
*/
DPRINTF(("process @%p has undo structure with %d entries\n", pr,
suptr->un_cnt));
/*
* If there are any active undo elements then process them.
*/
if (suptr->un_cnt > 0) {
int ix;
for (ix = 0; ix < suptr->un_cnt; ix++) {
int semid = suptr->un_ent[ix].un_id;
int semnum = suptr->un_ent[ix].un_num;
int adjval = suptr->un_ent[ix].un_adjval;
struct semid_ds *semaptr;
if ((semaptr = sema[semid]) == NULL)
panic("semexit - semid not allocated");
if (semnum >= semaptr->sem_nsems)
panic("semexit - semnum out of range");
DPRINTF(("semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n",
suptr->un_proc, suptr->un_ent[ix].un_id,
suptr->un_ent[ix].un_num,
suptr->un_ent[ix].un_adjval,
semaptr->sem_base[semnum].semval));
if (adjval < 0 &&
semaptr->sem_base[semnum].semval < -adjval)
semaptr->sem_base[semnum].semval = 0;
else
semaptr->sem_base[semnum].semval += adjval;
wakeup(&sema[semid]);
DPRINTF(("semexit: back from wakeup\n"));
}
}
/*
* Deallocate the undo vector.
*/
DPRINTF(("removing vector\n"));
*supptr = SLIST_NEXT(suptr, un_next);
pool_put(&semu_pool, suptr);
semutot--;
}
/* Expand semsegs and semseqs arrays */
void
sema_reallocate(int val)
{
struct semid_ds **sema_new;
unsigned short *newseqs;
sema_new = mallocarray(val, sizeof(struct semid_ds *),
M_SEM, M_WAITOK|M_ZERO);
memcpy(sema_new, sema,
seminfo.semmni * sizeof(struct semid_ds *));
newseqs = mallocarray(val, sizeof(unsigned short), M_SEM,
M_WAITOK|M_ZERO);
memcpy(newseqs, semseqs,
seminfo.semmni * sizeof(unsigned short));
free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds *));
free(semseqs, M_SEM, seminfo.semmni * sizeof(unsigned short));
sema = sema_new;
semseqs = newseqs;
seminfo.semmni = val;
}
const struct sysctl_bounded_args sysvsem_vars[] = {
{ KERN_SEMINFO_SEMUME, &seminfo.semume, SYSCTL_INT_READONLY },
{ KERN_SEMINFO_SEMUSZ, &seminfo.semusz, SYSCTL_INT_READONLY },
{ KERN_SEMINFO_SEMVMX, &seminfo.semvmx, SYSCTL_INT_READONLY },
{ KERN_SEMINFO_SEMAEM, &seminfo.semaem, SYSCTL_INT_READONLY },
{ KERN_SEMINFO_SEMOPM, &seminfo.semopm, 1, INT_MAX },
};
/*
* Userland access to struct seminfo.
*/
int
sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error, val;
if (namelen != 1)
return (ENOTDIR); /* leaf-only */
switch (name[0]) {
case KERN_SEMINFO_SEMMNI:
val = seminfo.semmni;
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&val, val, 0xffff);
/* returns success and skips reallocation if val is unchanged */
if (error || val == seminfo.semmni)
return (error);
sema_reallocate(val);
return (0);
case KERN_SEMINFO_SEMMNS:
/* can't decrease semmns or go over 2^16 */
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&seminfo.semmns, seminfo.semmns, 0xffff));
case KERN_SEMINFO_SEMMNU:
/* can't decrease semmnu or go over 2^16 */
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&seminfo.semmnu, seminfo.semmnu, 0xffff));
case KERN_SEMINFO_SEMMSL:
/* can't decrease semmsl or go over 2^16 */
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&seminfo.semmsl, seminfo.semmsl, 0xffff));
default:
return (sysctl_bounded_arr(sysvsem_vars, nitems(sysvsem_vars),
name, namelen, oldp, oldlenp, newp, newlen));
}
/* NOTREACHED */
}
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
/* $OpenBSD: wsdisplay.c,v 1.149 2022/07/15 17:57:27 kettenis Exp $ */
/* $NetBSD: wsdisplay.c,v 1.82 2005/02/27 00:27:52 perry Exp $ */
/*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou
* for the NetBSD Project.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <sys/timeout.h>
#include <dev/wscons/wscons_features.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsemulvar.h>
#include <dev/wscons/wscons_callbacks.h>
#include <dev/cons.h>
#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmux.h"
#if NWSKBD > 0
#include <dev/wscons/wseventvar.h>
#include <dev/wscons/wsmuxvar.h>
#endif
#ifdef DDB
#include <ddb/db_output.h>
#endif
#include "wsmoused.h"
struct wsscreen_internal {
const struct wsdisplay_emulops *emulops;
void *emulcookie;
const struct wsscreen_descr *scrdata;
const struct wsemul_ops *wsemul;
void *wsemulcookie;
};
struct wsscreen {
struct wsscreen_internal *scr_dconf;
struct tty *scr_tty;
int scr_hold_screen; /* hold tty output */
int scr_flags;
#define SCR_OPEN 1 /* is it open? */
#define SCR_WAITACTIVE 2 /* someone waiting on activation */
#define SCR_GRAPHICS 4 /* graphics mode, no text (emulation) output */
#define SCR_DUMBFB 8 /* in use as dumb fb (iff SCR_GRAPHICS) */
#ifdef WSDISPLAY_COMPAT_USL
const struct wscons_syncops *scr_syncops;
void *scr_synccookie;
#endif
#ifdef WSDISPLAY_COMPAT_RAWKBD
int scr_rawkbd;
#endif
struct wsdisplay_softc *sc;
#ifdef HAVE_WSMOUSED_SUPPORT
/* mouse console support via wsmoused(8) */
u_int mouse; /* mouse cursor position */
u_int cursor; /* selection cursor position (if
different from mouse cursor pos) */
u_int cpy_start; /* position of the copy start mark*/
u_int cpy_end; /* position of the copy end mark */
u_int orig_start; /* position of the original sel. start*/
u_int orig_end; /* position of the original sel. end */
u_int mouse_flags; /* flags, status of the mouse */
#define MOUSE_VISIBLE 0x01 /* flag, the mouse cursor is visible */
#define SEL_EXISTS 0x02 /* flag, a selection exists */
#define SEL_IN_PROGRESS 0x04 /* flag, a selection is in progress */
#define SEL_EXT_AFTER 0x08 /* flag, selection is extended after */
#define BLANK_TO_EOL 0x10 /* flag, there are only blanks
characters to eol */
#define SEL_BY_CHAR 0x20 /* flag, select character by character*/
#define SEL_BY_WORD 0x40 /* flag, select word by word */
#define SEL_BY_LINE 0x80 /* flag, select line by line */
#define IS_MOUSE_VISIBLE(scr) ((scr)->mouse_flags & MOUSE_VISIBLE)
#define IS_SEL_EXISTS(scr) ((scr)->mouse_flags & SEL_EXISTS)
#define IS_SEL_IN_PROGRESS(scr) ((scr)->mouse_flags & SEL_IN_PROGRESS)
#define IS_SEL_EXT_AFTER(scr) ((scr)->mouse_flags & SEL_EXT_AFTER)
#define IS_BLANK_TO_EOL(scr) ((scr)->mouse_flags & BLANK_TO_EOL)
#define IS_SEL_BY_CHAR(scr) ((scr)->mouse_flags & SEL_BY_CHAR)
#define IS_SEL_BY_WORD(scr) ((scr)->mouse_flags & SEL_BY_WORD)
#define IS_SEL_BY_LINE(scr) ((scr)->mouse_flags & SEL_BY_LINE)
#endif /* HAVE_WSMOUSED_SUPPORT */
};
struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, const char *,
const struct wsscreen_descr *, void *, int, int, uint32_t);
void wsscreen_detach(struct wsscreen *);
int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *,
const char *);
int wsdisplay_getscreen(struct wsdisplay_softc *,
struct wsdisplay_addscreendata *);
void wsdisplay_resume_device(struct device *);
void wsdisplay_suspend_device(struct device *);
void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int);
void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *);
int wsdisplay_delscreen(struct wsdisplay_softc *, int, int);
int wsdisplay_driver_ioctl(struct wsdisplay_softc *, u_long, caddr_t,
int, struct proc *);
void wsdisplay_burner_setup(struct wsdisplay_softc *, struct wsscreen *);
void wsdisplay_burner(void *v);
struct wsdisplay_softc {
struct device sc_dv;
const struct wsdisplay_accessops *sc_accessops;
void *sc_accesscookie;
const struct wsscreen_list *sc_scrdata;
struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN];
int sc_focusidx; /* available only if sc_focus isn't null */
struct wsscreen *sc_focus;
#ifdef HAVE_BURNER_SUPPORT
struct timeout sc_burner;
int sc_burnoutintvl; /* delay before blanking (ms) */
int sc_burninintvl; /* delay before unblanking (ms) */
int sc_burnout; /* current sc_burner delay (ms) */
int sc_burnman; /* nonzero if screen blanked */
int sc_burnflags;
#endif
int sc_isconsole;
int sc_flags;
#define SC_SWITCHPENDING 0x01
#define SC_PASTE_AVAIL 0x02
int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */
int sc_resumescreen; /* if set, can't switch until resume. */
#if NWSKBD > 0
struct wsevsrc *sc_input;
#ifdef WSDISPLAY_COMPAT_RAWKBD
int sc_rawkbd;
#endif
#endif /* NWSKBD > 0 */
#ifdef HAVE_WSMOUSED_SUPPORT
char *sc_copybuffer;
u_int sc_copybuffer_size;
#endif
};
extern struct cfdriver wsdisplay_cd;
/* Autoconfiguration definitions. */
int wsdisplay_emul_match(struct device *, void *, void *);
void wsdisplay_emul_attach(struct device *, struct device *, void *);
int wsdisplay_emul_detach(struct device *, int);
int wsdisplay_activate(struct device *, int);
struct cfdriver wsdisplay_cd = {
NULL, "wsdisplay", DV_TTY
};
const struct cfattach wsdisplay_emul_ca = {
sizeof(struct wsdisplay_softc), wsdisplay_emul_match,
wsdisplay_emul_attach, wsdisplay_emul_detach, wsdisplay_activate
};
void wsdisplaystart(struct tty *);
int wsdisplayparam(struct tty *, struct termios *);
/* Internal macros, functions, and variables. */
#define WSDISPLAYUNIT(dev) (minor(dev) >> 8)
#define WSDISPLAYSCREEN(dev) (minor(dev) & 0xff)
#define ISWSDISPLAYCTL(dev) (WSDISPLAYSCREEN(dev) == 255)
#define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen))
#define WSSCREEN_HAS_TTY(scr) ((scr)->scr_tty != NULL)
void wsdisplay_common_attach(struct wsdisplay_softc *sc,
int console, int mux, const struct wsscreen_list *,
const struct wsdisplay_accessops *accessops,
void *accesscookie, u_int defaultscreens);
int wsdisplay_common_detach(struct wsdisplay_softc *, int);
void wsdisplay_kbdholdscr(struct wsscreen *, int);
#ifdef WSDISPLAY_COMPAT_RAWKBD
int wsdisplay_update_rawkbd(struct wsdisplay_softc *, struct wsscreen *);
#endif
int wsdisplay_console_initted;
struct wsdisplay_softc *wsdisplay_console_device;
struct wsscreen_internal wsdisplay_console_conf;
int wsdisplay_getc_dummy(dev_t);
void wsdisplay_pollc(dev_t, int);
int wsdisplay_cons_pollmode;
void (*wsdisplay_cons_kbd_pollc)(dev_t, int);
struct consdev wsdisplay_cons = {
NULL, NULL, wsdisplay_getc_dummy, wsdisplay_cnputc,
wsdisplay_pollc, NULL, NODEV, CN_LOWPRI
};
/*
* Function pointers for wsconsctl parameter handling.
* These are used for firmware-provided display brightness control.
*/
int (*ws_get_param)(struct wsdisplay_param *);
int (*ws_set_param)(struct wsdisplay_param *);
#ifndef WSDISPLAY_DEFAULTSCREENS
#define WSDISPLAY_DEFAULTSCREENS 1
#endif
int wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS;
int wsdisplay_switch1(void *, int, int);
int wsdisplay_switch2(void *, int, int);
int wsdisplay_switch3(void *, int, int);
int wsdisplay_clearonclose;
struct wsscreen *
wsscreen_attach(struct wsdisplay_softc *sc, int console, const char *emul,
const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
uint32_t defattr)
{
struct wsscreen_internal *dconf;
struct wsscreen *scr;
scr = malloc(sizeof(*scr), M_DEVBUF, M_ZERO | M_NOWAIT);
if (!scr)
return (NULL);
if (console) {
dconf = &wsdisplay_console_conf;
/*
* Tell the emulation about the callback argument.
* The other stuff is already there.
*/
(void)(*dconf->wsemul->attach)(1, 0, 0, 0, 0, scr, 0);
} else { /* not console */
dconf = malloc(sizeof(*dconf), M_DEVBUF, M_NOWAIT);
if (dconf == NULL)
goto fail;
dconf->emulops = type->textops;
dconf->emulcookie = cookie;
if (dconf->emulops == NULL ||
(dconf->wsemul = wsemul_pick(emul)) == NULL)
goto fail;
dconf->wsemulcookie = (*dconf->wsemul->attach)(0, type, cookie,
ccol, crow, scr, defattr);
if (dconf->wsemulcookie == NULL)
goto fail;
dconf->scrdata = type;
}
scr->scr_dconf = dconf;
scr->scr_tty = ttymalloc(0);
scr->sc = sc;
return (scr);
fail:
if (dconf != NULL)
free(dconf, M_DEVBUF, sizeof(*dconf));
free(scr, M_DEVBUF, sizeof(*scr));
return (NULL);
}
void
wsscreen_detach(struct wsscreen *scr)
{
int ccol, crow; /* XXX */
if (WSSCREEN_HAS_TTY(scr)) {
timeout_del(&scr->scr_tty->t_rstrt_to);
ttyfree(scr->scr_tty);
}
(*scr->scr_dconf->wsemul->detach)(scr->scr_dconf->wsemulcookie,
&ccol, &crow);
free(scr->scr_dconf, M_DEVBUF, sizeof(*scr->scr_dconf));
free(scr, M_DEVBUF, sizeof(*scr));
}
const struct wsscreen_descr *
wsdisplay_screentype_pick(const struct wsscreen_list *scrdata, const char *name)
{
int i;
const struct wsscreen_descr *scr;
KASSERT(scrdata->nscreens > 0);
if (name == NULL || *name == '\0')
return (scrdata->screens[0]);
for (i = 0; i < scrdata->nscreens; i++) {
scr = scrdata->screens[i];
if (!strncmp(name, scr->name, WSSCREEN_NAME_SIZE))
return (scr);
}
return (0);
}
/*
* print info about attached screen
*/
void
wsdisplay_addscreen_print(struct wsdisplay_softc *sc, int idx, int count)
{
printf("%s: screen %d", sc->sc_dv.dv_xname, idx);
if (count > 1)
printf("-%d", idx + (count-1));
printf(" added (%s, %s emulation)\n",
sc->sc_scr[idx]->scr_dconf->scrdata->name,
sc->sc_scr[idx]->scr_dconf->wsemul->name);
}
int
wsdisplay_addscreen(struct wsdisplay_softc *sc, int idx,
const char *screentype, const char *emul)
{
const struct wsscreen_descr *scrdesc;
int error;
void *cookie;
int ccol, crow;
uint32_t defattr;
struct wsscreen *scr;
int s;
if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
return (EINVAL);
if (sc->sc_scr[idx] != NULL)
return (EBUSY);
scrdesc = wsdisplay_screentype_pick(sc->sc_scrdata, screentype);
if (!scrdesc)
return (ENXIO);
error = (*sc->sc_accessops->alloc_screen)(sc->sc_accesscookie,
scrdesc, &cookie, &ccol, &crow, &defattr);
if (error)
return (error);
scr = wsscreen_attach(sc, 0, emul, scrdesc,
cookie, ccol, crow, defattr);
if (scr == NULL) {
(*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie);
return (ENXIO);
}
sc->sc_scr[idx] = scr;
/* if no screen has focus yet, activate the first we get */
s = spltty();
if (!sc->sc_focus) {
(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie, 0, 0, 0);
sc->sc_focusidx = idx;
sc->sc_focus = scr;
}
splx(s);
#ifdef HAVE_WSMOUSED_SUPPORT
allocate_copybuffer(sc); /* enlarge the copy buffer if necessary */
#endif
return (0);
}
int
wsdisplay_getscreen(struct wsdisplay_softc *sc,
struct wsdisplay_addscreendata *sd)
{
struct wsscreen *scr;
if (sd->idx < 0 && sc->sc_focus)
sd->idx = sc->sc_focusidx;
if (sd->idx < 0 || sd->idx >= WSDISPLAY_MAXSCREEN)
return (EINVAL);
scr = sc->sc_scr[sd->idx];
if (scr == NULL)
return (ENXIO);
strlcpy(sd->screentype, scr->scr_dconf->scrdata->name,
WSSCREEN_NAME_SIZE);
strlcpy(sd->emul, scr->scr_dconf->wsemul->name, WSEMUL_NAME_SIZE);
return (0);
}
void
wsdisplay_closescreen(struct wsdisplay_softc *sc, struct wsscreen *scr)
{
int maj, mn, idx;
/* hangup */
if (WSSCREEN_HAS_TTY(scr)) {
struct tty *tp = scr->scr_tty;
(*linesw[tp->t_line].l_modem)(tp, 0);
}
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == wsdisplayopen)
break;
/* locate the screen index */
for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++)
if (scr == sc->sc_scr[idx])
break;
#ifdef DIAGNOSTIC
if (idx == WSDISPLAY_MAXSCREEN)
panic("wsdisplay_forceclose: bad screen");
#endif
/* nuke the vnodes */
mn = WSDISPLAYMINOR(sc->sc_dv.dv_unit, idx);
vdevgone(maj, mn, mn, VCHR);
}
int
wsdisplay_delscreen(struct wsdisplay_softc *sc, int idx, int flags)
{
struct wsscreen *scr;
int s;
void *cookie;
if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
return (EINVAL);
if ((scr = sc->sc_scr[idx]) == NULL)
return (ENXIO);
if (scr->scr_dconf == &wsdisplay_console_conf ||
#ifdef WSDISPLAY_COMPAT_USL
scr->scr_syncops ||
#endif
((scr->scr_flags & SCR_OPEN) && !(flags & WSDISPLAY_DELSCR_FORCE)))
return (EBUSY);
wsdisplay_closescreen(sc, scr);
/*
* delete pointers, so neither device entries
* nor keyboard input can reference it anymore
*/
s = spltty();
if (sc->sc_focus == scr) {
sc->sc_focus = NULL;
#ifdef WSDISPLAY_COMPAT_RAWKBD
wsdisplay_update_rawkbd(sc, 0);
#endif
}
sc->sc_scr[idx] = NULL;
splx(s);
/*
* Wake up processes waiting for the screen to
* be activated. Sleepers must check whether
* the screen still exists.
*/
if (scr->scr_flags & SCR_WAITACTIVE)
wakeup(scr);
/* save a reference to the graphics screen */
cookie = scr->scr_dconf->emulcookie;
wsscreen_detach(scr);
(*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie);
if ((flags & WSDISPLAY_DELSCR_QUIET) == 0)
printf("%s: screen %d deleted\n", sc->sc_dv.dv_xname, idx);
return (0);
}
/*
* Autoconfiguration functions.
*/
int
wsdisplay_emul_match(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct wsemuldisplaydev_attach_args *ap = aux;
if (cf->wsemuldisplaydevcf_console != WSEMULDISPLAYDEVCF_CONSOLE_UNK) {
/*
* If console-ness of device specified, either match
* exactly (at high priority), or fail.
*/
if (cf->wsemuldisplaydevcf_console != 0 && ap->console != 0)
return (10);
else
return (0);
}
if (cf->wsemuldisplaydevcf_primary != WSEMULDISPLAYDEVCF_PRIMARY_UNK) {
/*
* If primary-ness of device specified, either match
* exactly (at high priority), or fail.
*/
if (cf->wsemuldisplaydevcf_primary != 0 && ap->primary != 0)
return (10);
else
return (0);
}
/* If console-ness and primary-ness unspecified, it wins. */
return (1);
}
void
wsdisplay_emul_attach(struct device *parent, struct device *self, void *aux)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self;
struct wsemuldisplaydev_attach_args *ap = aux;
wsdisplay_common_attach(sc, ap->console,
sc->sc_dv.dv_cfdata->wsemuldisplaydevcf_mux, ap->scrdata,
ap->accessops, ap->accesscookie, ap->defaultscreens);
if (ap->console && cn_tab == &wsdisplay_cons) {
int maj;
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == wsdisplayopen)
break;
cn_tab->cn_dev = makedev(maj, WSDISPLAYMINOR(self->dv_unit, 0));
}
}
/*
* Detach a display.
*/
int
wsdisplay_emul_detach(struct device *self, int flags)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self;
return (wsdisplay_common_detach(sc, flags));
}
int
wsdisplay_activate(struct device *self, int act)
{
int ret = 0;
switch (act) {
case DVACT_POWERDOWN:
wsdisplay_switchtoconsole();
break;
}
return (ret);
}
int
wsdisplay_common_detach(struct wsdisplay_softc *sc, int flags)
{
int i;
int rc;
/* We don't support detaching the console display yet. */
if (sc->sc_isconsole)
return (EBUSY);
/* Delete all screens managed by this display */
for (i = 0; i < WSDISPLAY_MAXSCREEN; i++)
if (sc->sc_scr[i] != NULL) {
if ((rc = wsdisplay_delscreen(sc, i,
WSDISPLAY_DELSCR_QUIET | (flags & DETACH_FORCE ?
WSDISPLAY_DELSCR_FORCE : 0))) != 0)
return (rc);
}
#ifdef HAVE_BURNER_SUPPORT
timeout_del(&sc->sc_burner);
#endif
#if NWSKBD > 0
if (sc->sc_input != NULL) {
#if NWSMUX > 0
/*
* If we are the display of the mux we are attached to,
* disconnect all input devices from us.
*/
if (sc->sc_input->me_dispdv == &sc->sc_dv) {
if ((rc = wsmux_set_display((struct wsmux_softc *)
sc->sc_input, NULL)) != 0)
return (rc);
}
/*
* XXX
* If we created a standalone mux (dmux), we should destroy it
* there, but there is currently no support for this in wsmux.
*/
#else
if ((rc = wskbd_set_display((struct device *)sc->sc_input,
NULL)) != 0)
return (rc);
#endif
}
#endif
return (0);
}
/* Print function (for parent devices). */
int
wsemuldisplaydevprint(void *aux, const char *pnp)
{
#if 0 /* -Wunused */
struct wsemuldisplaydev_attach_args *ap = aux;
#endif
if (pnp)
printf("wsdisplay at %s", pnp);
#if 0 /* don't bother; it's ugly */
printf(" console %d", ap->console);
#endif
return (UNCONF);
}
/* Submatch function (for parent devices). */
int
wsemuldisplaydevsubmatch(struct device *parent, void *match, void *aux)
{
extern struct cfdriver wsdisplay_cd;
struct cfdata *cf = match;
/* only allow wsdisplay to attach */
if (cf->cf_driver == &wsdisplay_cd)
return ((*cf->cf_attach->ca_match)(parent, match, aux));
return (0);
}
void
wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux,
const struct wsscreen_list *scrdata,
const struct wsdisplay_accessops *accessops, void *accesscookie,
u_int defaultscreens)
{
int i, start = 0;
#if NWSKBD > 0
struct wsevsrc *kme;
#if NWSMUX > 0
struct wsmux_softc *mux;
if (kbdmux >= 0)
mux = wsmux_getmux(kbdmux);
else
mux = wsmux_create("dmux", sc->sc_dv.dv_unit);
/* XXX panic()ing isn't nice, but attach cannot fail */
if (mux == NULL)
panic("wsdisplay_common_attach: no memory");
sc->sc_input = &mux->sc_base;
if (kbdmux >= 0)
printf(" mux %d", kbdmux);
#else
#if 0 /* not worth keeping, especially since the default value is not -1... */
if (kbdmux >= 0)
printf(" (mux ignored)");
#endif
#endif /* NWSMUX > 0 */
#endif /* NWSKBD > 0 */
sc->sc_isconsole = console;
sc->sc_resumescreen = WSDISPLAY_NULLSCREEN;
if (console) {
KASSERT(wsdisplay_console_initted);
KASSERT(wsdisplay_console_device == NULL);
sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0);
if (sc->sc_scr[0] == NULL)
return;
wsdisplay_console_device = sc;
printf(": console (%s, %s emulation)",
wsdisplay_console_conf.scrdata->name,
wsdisplay_console_conf.wsemul->name);
#if NWSKBD > 0
kme = wskbd_set_console_display(&sc->sc_dv, sc->sc_input);
if (kme != NULL)
printf(", using %s", kme->me_dv.dv_xname);
#if NWSMUX == 0
sc->sc_input = kme;
#endif
#endif
sc->sc_focusidx = 0;
sc->sc_focus = sc->sc_scr[0];
start = 1;
}
printf("\n");
#if NWSKBD > 0 && NWSMUX > 0
/*
* If this mux did not have a display device yet, volunteer for
* the job.
*/
if (mux->sc_displaydv == NULL)
wsmux_set_display(mux, &sc->sc_dv);
#endif
sc->sc_accessops = accessops;
sc->sc_accesscookie = accesscookie;
sc->sc_scrdata = scrdata;
/*
* Set up a number of virtual screens if wanted. The
* WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code
* is for special cases like installation kernels, as well as
* sane multihead defaults.
*/
if (defaultscreens == 0)
defaultscreens = wsdisplay_defaultscreens;
for (i = start; i < defaultscreens; i++) {
if (wsdisplay_addscreen(sc, i, 0, 0))
break;
}
if (i > start)
wsdisplay_addscreen_print(sc, start, i-start);
#ifdef HAVE_BURNER_SUPPORT
sc->sc_burnoutintvl = WSDISPLAY_DEFBURNOUT_MSEC;
sc->sc_burninintvl = WSDISPLAY_DEFBURNIN_MSEC;
sc->sc_burnflags = WSDISPLAY_BURN_OUTPUT | WSDISPLAY_BURN_KBD |
WSDISPLAY_BURN_MOUSE;
timeout_set(&sc->sc_burner, wsdisplay_burner, sc);
sc->sc_burnout = sc->sc_burnoutintvl;
wsdisplay_burn(sc, sc->sc_burnflags);
#endif
#if NWSKBD > 0 && NWSMUX == 0
if (console == 0) {
/*
* In the non-wsmux world, always connect wskbd0 and wsdisplay0
* together.
*/
extern struct cfdriver wskbd_cd;
if (wskbd_cd.cd_ndevs != 0 && sc->sc_dv.dv_unit == 0) {
if (wsdisplay_set_kbd(&sc->sc_dv,
(struct wsevsrc *)wskbd_cd.cd_devs[0]) == 0)
wskbd_set_display(wskbd_cd.cd_devs[0],
&sc->sc_dv);
}
}
#endif
}
void
wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
int crow, uint32_t defattr)
{
const struct wsemul_ops *wsemul;
const struct wsdisplay_emulops *emulops;
KASSERT(type->nrows > 0);
KASSERT(type->ncols > 0);
KASSERT(crow < type->nrows);
KASSERT(ccol < type->ncols);
wsdisplay_console_conf.emulops = emulops = type->textops;
wsdisplay_console_conf.emulcookie = cookie;
wsdisplay_console_conf.scrdata = type;
#ifdef WSEMUL_DUMB
/*
* If the emulops structure is crippled, force a dumb emulation.
*/
if (emulops->cursor == NULL ||
emulops->copycols == NULL || emulops->copyrows == NULL ||
emulops->erasecols == NULL || emulops->eraserows == NULL)
wsemul = wsemul_pick("dumb");
else
#endif
wsemul = wsemul_pick("");
wsdisplay_console_conf.wsemul = wsemul;
wsdisplay_console_conf.wsemulcookie =
(*wsemul->cnattach)(type, cookie, ccol, crow, defattr);
if (!wsdisplay_console_initted)
cn_tab = &wsdisplay_cons;
wsdisplay_console_initted = 1;
#ifdef DDB
db_resize(type->ncols, type->nrows);
#endif
}
/*
* Tty and cdevsw functions.
*/
int
wsdisplayopen(dev_t dev, int flag, int mode, struct proc *p)
{
struct wsdisplay_softc *sc;
struct tty *tp;
int unit, newopen, error;
struct wsscreen *scr;
unit = WSDISPLAYUNIT(dev);
if (unit >= wsdisplay_cd.cd_ndevs || /* make sure it was attached */
(sc = wsdisplay_cd.cd_devs[unit]) == NULL)
return (ENXIO);
if (ISWSDISPLAYCTL(dev))
return (0);
if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN)
return (ENXIO);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (ENXIO);
if (WSSCREEN_HAS_TTY(scr)) {
tp = scr->scr_tty;
tp->t_oproc = wsdisplaystart;
tp->t_param = wsdisplayparam;
tp->t_dev = dev;
newopen = (tp->t_state & TS_ISOPEN) == 0;
if (newopen) {
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
wsdisplayparam(tp, &tp->t_termios);
ttsetwater(tp);
} else if ((tp->t_state & TS_XCLUDE) != 0 &&
suser(p) != 0)
return (EBUSY);
tp->t_state |= TS_CARR_ON;
error = ((*linesw[tp->t_line].l_open)(dev, tp, p));
if (error)
return (error);
if (newopen) {
/* set window sizes as appropriate, and reset
the emulation */
tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows;
tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols;
}
}
scr->scr_flags |= SCR_OPEN;
return (0);
}
int
wsdisplayclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct wsdisplay_softc *sc;
struct tty *tp;
int unit;
struct wsscreen *scr;
unit = WSDISPLAYUNIT(dev);
sc = wsdisplay_cd.cd_devs[unit];
if (ISWSDISPLAYCTL(dev))
return (0);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (ENXIO);
if (WSSCREEN_HAS_TTY(scr)) {
if (scr->scr_hold_screen) {
int s;
/* XXX RESET KEYBOARD LEDS, etc. */
s = spltty(); /* avoid conflict with keyboard */
wsdisplay_kbdholdscr(scr, 0);
splx(s);
}
tp = scr->scr_tty;
(*linesw[tp->t_line].l_close)(tp, flag, p);
ttyclose(tp);
}
#ifdef WSDISPLAY_COMPAT_USL
if (scr->scr_syncops)
(*scr->scr_syncops->destroy)(scr->scr_synccookie);
#endif
scr->scr_flags &= ~SCR_GRAPHICS;
(*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie,
WSEMUL_RESET);
if (wsdisplay_clearonclose)
(*scr->scr_dconf->wsemul->reset)
(scr->scr_dconf->wsemulcookie, WSEMUL_CLEARSCREEN);
#ifdef WSDISPLAY_COMPAT_RAWKBD
if (scr->scr_rawkbd) {
int kbmode = WSKBD_TRANSLATED;
(void) wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE,
(caddr_t)&kbmode, FWRITE, p);
}
#endif
scr->scr_flags &= ~SCR_OPEN;
#ifdef HAVE_WSMOUSED_SUPPORT
/* remove the selection at logout */
if (sc->sc_copybuffer != NULL)
explicit_bzero(sc->sc_copybuffer, sc->sc_copybuffer_size);
CLR(sc->sc_flags, SC_PASTE_AVAIL);
#endif
return (0);
}
int
wsdisplayread(dev_t dev, struct uio *uio, int flag)
{
struct wsdisplay_softc *sc;
struct tty *tp;
int unit;
struct wsscreen *scr;
unit = WSDISPLAYUNIT(dev);
sc = wsdisplay_cd.cd_devs[unit];
if (ISWSDISPLAYCTL(dev))
return (0);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (ENXIO);
if (!WSSCREEN_HAS_TTY(scr))
return (ENODEV);
tp = scr->scr_tty;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
int
wsdisplaywrite(dev_t dev, struct uio *uio, int flag)
{
struct wsdisplay_softc *sc;
struct tty *tp;
int unit;
struct wsscreen *scr;
unit = WSDISPLAYUNIT(dev);
sc = wsdisplay_cd.cd_devs[unit];
if (ISWSDISPLAYCTL(dev))
return (0);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (ENXIO);
if (!WSSCREEN_HAS_TTY(scr))
return (ENODEV);
tp = scr->scr_tty;
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
struct tty *
wsdisplaytty(dev_t dev)
{
struct wsdisplay_softc *sc;
int unit;
struct wsscreen *scr;
unit = WSDISPLAYUNIT(dev);
sc = wsdisplay_cd.cd_devs[unit];
if (ISWSDISPLAYCTL(dev))
panic("wsdisplaytty() on ctl device");
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (NULL);
return (scr->scr_tty);
}
int
wsdisplayioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct wsdisplay_softc *sc;
struct tty *tp;
int unit, error;
struct wsscreen *scr;
unit = WSDISPLAYUNIT(dev);
sc = wsdisplay_cd.cd_devs[unit];
#ifdef WSDISPLAY_COMPAT_USL
error = wsdisplay_usl_ioctl1(sc, cmd, data, flag, p);
if (error >= 0)
return (error);
#endif
if (ISWSDISPLAYCTL(dev)) {
switch (cmd) {
case WSDISPLAYIO_GTYPE:
case WSDISPLAYIO_GETSCREENTYPE:
/* pass to the first screen */
dev = makedev(major(dev), WSDISPLAYMINOR(unit, 0));
break;
default:
return (wsdisplay_cfg_ioctl(sc, cmd, data, flag, p));
}
}
if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN)
return (ENODEV);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (ENXIO);
if (WSSCREEN_HAS_TTY(scr)) {
tp = scr->scr_tty;
/* printf("disc\n"); */
/* do the line discipline ioctls first */
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
/* printf("tty\n"); */
/* then the tty ioctls */
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
}
#ifdef WSDISPLAY_COMPAT_USL
error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, p);
if (error >= 0)
return (error);
#endif
error = wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, p);
return (error != -1 ? error : ENOTTY);
}
int
wsdisplay_param(struct device *dev, u_long cmd, struct wsdisplay_param *dp)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
return wsdisplay_driver_ioctl(sc, cmd, (caddr_t)dp, 0, NULL);
}
int
wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr,
u_long cmd, caddr_t data, int flag, struct proc *p)
{
int error;
#if NWSKBD > 0
struct wsevsrc *inp;
#ifdef WSDISPLAY_COMPAT_RAWKBD
switch (cmd) {
case WSKBDIO_SETMODE:
if ((flag & FWRITE) == 0)
return (EACCES);
scr->scr_rawkbd = (*(int *)data == WSKBD_RAW);
return (wsdisplay_update_rawkbd(sc, scr));
case WSKBDIO_GETMODE:
*(int *)data = (scr->scr_rawkbd ?
WSKBD_RAW : WSKBD_TRANSLATED);
return (0);
}
#endif
inp = sc->sc_input;
if (inp != NULL) {
error = wsevsrc_display_ioctl(inp, cmd, data, flag, p);
if (error >= 0)
return (error);
}
#endif /* NWSKBD > 0 */
switch (cmd) {
case WSDISPLAYIO_SMODE:
case WSDISPLAYIO_USEFONT:
#ifdef HAVE_BURNER_SUPPORT
case WSDISPLAYIO_SVIDEO:
case WSDISPLAYIO_SBURNER:
#endif
case WSDISPLAYIO_SETSCREEN:
if ((flag & FWRITE) == 0)
return (EACCES);
}
switch (cmd) {
case WSDISPLAYIO_GMODE:
if (scr->scr_flags & SCR_GRAPHICS) {
if (scr->scr_flags & SCR_DUMBFB)
*(u_int *)data = WSDISPLAYIO_MODE_DUMBFB;
else
*(u_int *)data = WSDISPLAYIO_MODE_MAPPED;
} else
*(u_int *)data = WSDISPLAYIO_MODE_EMUL;
return (0);
case WSDISPLAYIO_SMODE:
#define d (*(int *)data)
if (d != WSDISPLAYIO_MODE_EMUL &&
d != WSDISPLAYIO_MODE_MAPPED &&
d != WSDISPLAYIO_MODE_DUMBFB)
return (EINVAL);
scr->scr_flags &= ~SCR_GRAPHICS;
if (d == WSDISPLAYIO_MODE_MAPPED ||
d == WSDISPLAYIO_MODE_DUMBFB) {
scr->scr_flags |= SCR_GRAPHICS |
((d == WSDISPLAYIO_MODE_DUMBFB) ? SCR_DUMBFB : 0);
/* clear cursor */
(*scr->scr_dconf->wsemul->reset)
(scr->scr_dconf->wsemulcookie, WSEMUL_CLEARCURSOR);
}
#ifdef HAVE_BURNER_SUPPORT
wsdisplay_burner_setup(sc, scr);
#endif
(void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
flag, p);
return (0);
#undef d
case WSDISPLAYIO_USEFONT:
#define d ((struct wsdisplay_font *)data)
if (!sc->sc_accessops->load_font)
return (EINVAL);
d->data = NULL;
error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie, d);
if (!error)
(*scr->scr_dconf->wsemul->reset)
(scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT);
return (error);
#undef d
#ifdef HAVE_BURNER_SUPPORT
case WSDISPLAYIO_GVIDEO:
*(u_int *)data = !sc->sc_burnman;
break;
case WSDISPLAYIO_SVIDEO:
if (*(u_int *)data != WSDISPLAYIO_VIDEO_OFF &&
*(u_int *)data != WSDISPLAYIO_VIDEO_ON)
return (EINVAL);
if (sc->sc_accessops->burn_screen == NULL)
return (EOPNOTSUPP);
(*sc->sc_accessops->burn_screen)(sc->sc_accesscookie,
*(u_int *)data, sc->sc_burnflags);
sc->sc_burnman = *(u_int *)data == WSDISPLAYIO_VIDEO_OFF;
break;
case WSDISPLAYIO_GBURNER:
#define d ((struct wsdisplay_burner *)data)
d->on = sc->sc_burninintvl;
d->off = sc->sc_burnoutintvl;
d->flags = sc->sc_burnflags;
return (0);
case WSDISPLAYIO_SBURNER:
{
struct wsscreen *active;
if (d->flags & ~(WSDISPLAY_BURN_VBLANK | WSDISPLAY_BURN_KBD |
WSDISPLAY_BURN_MOUSE | WSDISPLAY_BURN_OUTPUT))
return EINVAL;
error = 0;
sc->sc_burnflags = d->flags;
/* disable timeout if necessary */
if (d->off==0 || (sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT |
WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) == 0) {
if (sc->sc_burnout)
timeout_del(&sc->sc_burner);
}
active = sc->sc_focus;
if (active == NULL)
active = scr;
if (d->on) {
sc->sc_burninintvl = d->on;
if (sc->sc_burnman) {
sc->sc_burnout = sc->sc_burninintvl;
/* reinit timeout if changed */
if ((active->scr_flags & SCR_GRAPHICS) == 0)
wsdisplay_burn(sc, sc->sc_burnflags);
}
}
sc->sc_burnoutintvl = d->off;
if (!sc->sc_burnman) {
sc->sc_burnout = sc->sc_burnoutintvl;
/* reinit timeout if changed */
if ((active->scr_flags & SCR_GRAPHICS) == 0)
wsdisplay_burn(sc, sc->sc_burnflags);
}
return (error);
}
#undef d
#endif /* HAVE_BURNER_SUPPORT */
case WSDISPLAYIO_GETSCREEN:
return (wsdisplay_getscreen(sc,
(struct wsdisplay_addscreendata *)data));
case WSDISPLAYIO_SETSCREEN:
return (wsdisplay_switch((void *)sc, *(int *)data, 1));
case WSDISPLAYIO_GETSCREENTYPE:
#define d ((struct wsdisplay_screentype *)data)
if (d->idx < 0 || d->idx >= sc->sc_scrdata->nscreens)
return(EINVAL);
d->nidx = sc->sc_scrdata->nscreens;
strlcpy(d->name, sc->sc_scrdata->screens[d->idx]->name,
WSSCREEN_NAME_SIZE);
d->ncols = sc->sc_scrdata->screens[d->idx]->ncols;
d->nrows = sc->sc_scrdata->screens[d->idx]->nrows;
d->fontwidth = sc->sc_scrdata->screens[d->idx]->fontwidth;
d->fontheight = sc->sc_scrdata->screens[d->idx]->fontheight;
return (0);
#undef d
case WSDISPLAYIO_GETEMULTYPE:
#define d ((struct wsdisplay_emultype *)data)
if (wsemul_getname(d->idx) == NULL)
return(EINVAL);
strlcpy(d->name, wsemul_getname(d->idx), WSEMUL_NAME_SIZE);
return (0);
#undef d
}
/* check ioctls for display */
return wsdisplay_driver_ioctl(sc, cmd, data, flag, p);
}
int
wsdisplay_driver_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data,
int flag, struct proc *p)
{
int error;
#if defined(OpenBSD7_1) || defined(OpenBSD7_2) || defined(OpenBSD7_3)
if (cmd == WSDISPLAYIO_OGINFO) {
struct wsdisplay_ofbinfo *oinfo =
(struct wsdisplay_ofbinfo *)data;
struct wsdisplay_fbinfo info;
error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
WSDISPLAYIO_GINFO, (caddr_t)&info, flag, p);
if (error)
return error;
oinfo->height = info.height;
oinfo->width = info.width;
oinfo->depth = info.depth;
oinfo->cmsize = info.cmsize;
return (0);
}
#endif
error = ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
flag, p));
/* Do not report parameters with empty ranges to userland. */
if (error == 0 && cmd == WSDISPLAYIO_GETPARAM) {
struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
switch (dp->param) {
case WSDISPLAYIO_PARAM_BACKLIGHT:
case WSDISPLAYIO_PARAM_BRIGHTNESS:
case WSDISPLAYIO_PARAM_CONTRAST:
if (dp->min == dp->max)
error = ENOTTY;
break;
}
}
return error;
}
int
wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data,
int flag, struct proc *p)
{
int error;
void *buf;
size_t fontsz;
#if NWSKBD > 0
struct wsevsrc *inp;
#endif
switch (cmd) {
#ifdef HAVE_WSMOUSED_SUPPORT
case WSDISPLAYIO_WSMOUSED:
error = wsmoused(sc, data, flag, p);
return (error);
#endif
case WSDISPLAYIO_ADDSCREEN:
#define d ((struct wsdisplay_addscreendata *)data)
if ((error = wsdisplay_addscreen(sc, d->idx,
d->screentype, d->emul)) == 0)
wsdisplay_addscreen_print(sc, d->idx, 0);
return (error);
#undef d
case WSDISPLAYIO_DELSCREEN:
#define d ((struct wsdisplay_delscreendata *)data)
return (wsdisplay_delscreen(sc, d->idx, d->flags));
#undef d
case WSDISPLAYIO_GETSCREEN:
return (wsdisplay_getscreen(sc,
(struct wsdisplay_addscreendata *)data));
case WSDISPLAYIO_SETSCREEN:
return (wsdisplay_switch((void *)sc, *(int *)data, 1));
case WSDISPLAYIO_LDFONT:
#define d ((struct wsdisplay_font *)data)
if (!sc->sc_accessops->load_font)
return (EINVAL);
if (d->fontheight > 64 || d->stride > 8) /* 64x64 pixels */
return (EINVAL);
if (d->numchars > 65536) /* unicode plane */
return (EINVAL);
fontsz = d->fontheight * d->stride * d->numchars;
if (fontsz > WSDISPLAY_MAXFONTSZ)
return (EINVAL);
buf = malloc(fontsz, M_DEVBUF, M_WAITOK);
error = copyin(d->data, buf, fontsz);
if (error) {
free(buf, M_DEVBUF, fontsz);
return (error);
}
d->data = buf;
error =
(*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d);
if (error)
free(buf, M_DEVBUF, fontsz);
return (error);
case WSDISPLAYIO_LSFONT:
if (!sc->sc_accessops->list_font)
return (EINVAL);
error =
(*sc->sc_accessops->list_font)(sc->sc_accesscookie, d);
return (error);
case WSDISPLAYIO_DELFONT:
return (EINVAL);
#undef d
#if NWSKBD > 0
case WSMUXIO_ADD_DEVICE:
#define d ((struct wsmux_device *)data)
if (d->idx == -1 && d->type == WSMUX_KBD)
d->idx = wskbd_pickfree();
#undef d
/* FALLTHROUGH */
case WSMUXIO_INJECTEVENT:
case WSMUXIO_REMOVE_DEVICE:
case WSMUXIO_LIST_DEVICES:
inp = sc->sc_input;
if (inp == NULL)
return (ENXIO);
return (wsevsrc_ioctl(inp, cmd, data, flag,p));
#endif /* NWSKBD > 0 */
}
return (EINVAL);
}
paddr_t
wsdisplaymmap(dev_t dev, off_t offset, int prot)
{
struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)];
struct wsscreen *scr;
if (ISWSDISPLAYCTL(dev))
return (-1);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (-1);
if (!(scr->scr_flags & SCR_GRAPHICS))
return (-1);
/* pass mmap to display */
return ((*sc->sc_accessops->mmap)(sc->sc_accesscookie, offset, prot));
}
int
wsdisplaykqfilter(dev_t dev, struct knote *kn)
{
struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)];
struct wsscreen *scr;
if (ISWSDISPLAYCTL(dev))
return (ENXIO);
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
return (ENXIO);
if (!WSSCREEN_HAS_TTY(scr))
return (ENXIO);
return (ttkqfilter(dev, kn));
}
void
wsdisplaystart(struct tty *tp)
{
struct wsdisplay_softc *sc;
struct wsscreen *scr;
int s, n, done, unit;
u_char *buf;
unit = WSDISPLAYUNIT(tp->t_dev);
if (unit >= wsdisplay_cd.cd_ndevs ||
(sc = wsdisplay_cd.cd_devs[unit]) == NULL)
return;
s = spltty();
if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
splx(s);
return;
}
if (tp->t_outq.c_cc == 0)
goto low;
if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) {
splx(s);
return;
}
if (scr->scr_hold_screen) {
tp->t_state |= TS_TIMEOUT;
splx(s);
return;
}
tp->t_state |= TS_BUSY;
splx(s);
/*
* Drain output from ring buffer.
* The output will normally be in one contiguous chunk, but when the
* ring wraps, it will be in two pieces.. one at the end of the ring,
* the other at the start. For performance, rather than loop here,
* we output one chunk, see if there's another one, and if so, output
* it too.
*/
n = ndqb(&tp->t_outq, 0);
buf = tp->t_outq.c_cf;
if (!(scr->scr_flags & SCR_GRAPHICS)) {
#ifdef HAVE_BURNER_SUPPORT
wsdisplay_burn(sc, WSDISPLAY_BURN_OUTPUT);
#endif
#ifdef HAVE_WSMOUSED_SUPPORT
if (scr == sc->sc_focus)
mouse_remove(scr);
#endif
done = (*scr->scr_dconf->wsemul->output)
(scr->scr_dconf->wsemulcookie, buf, n, 0);
} else
done = n;
ndflush(&tp->t_outq, done);
if (done == n) {
if ((n = ndqb(&tp->t_outq, 0)) > 0) {
buf = tp->t_outq.c_cf;
if (!(scr->scr_flags & SCR_GRAPHICS)) {
done = (*scr->scr_dconf->wsemul->output)
(scr->scr_dconf->wsemulcookie, buf, n, 0);
} else
done = n;
ndflush(&tp->t_outq, done);
}
}
s = spltty();
tp->t_state &= ~TS_BUSY;
/* Come back if there's more to do */
if (tp->t_outq.c_cc) {
tp->t_state |= TS_TIMEOUT;
timeout_add(&tp->t_rstrt_to, (hz > 128) ? (hz / 128) : 1);
}
low:
ttwakeupwr(tp);
splx(s);
}
int
wsdisplaystop(struct tty *tp, int flag)
{
int s;
s = spltty();
if (ISSET(tp->t_state, TS_BUSY))
if (!ISSET(tp->t_state, TS_TTSTOP))
SET(tp->t_state, TS_FLUSH);
splx(s);
return (0);
}
/* Set line parameters. */
int
wsdisplayparam(struct tty *tp, struct termios *t)
{
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = t->c_cflag;
return (0);
}
/*
* Callbacks for the emulation code.
*/
void
wsdisplay_emulbell(void *v)
{
struct wsscreen *scr = v;
if (scr == NULL) /* console, before real attach */
return;
if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */
return;
(void) wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL,
FWRITE, NULL);
}
#if !defined(WSEMUL_NO_VT100)
void
wsdisplay_emulinput(void *v, const u_char *data, u_int count)
{
struct wsscreen *scr = v;
struct tty *tp;
if (v == NULL) /* console, before real attach */
return;
if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */
return;
if (!WSSCREEN_HAS_TTY(scr))
return;
tp = scr->scr_tty;
while (count-- > 0)
(*linesw[tp->t_line].l_rint)(*data++, tp);
}
#endif
/*
* Calls from the keyboard interface.
*/
void
wsdisplay_kbdinput(struct device *dev, kbd_t layout, keysym_t *ks, int num)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsscreen *scr;
const u_char *dp;
int count;
struct tty *tp;
scr = sc->sc_focus;
if (!scr || !WSSCREEN_HAS_TTY(scr))
return;
tp = scr->scr_tty;
for (; num > 0; num--) {
count = (*scr->scr_dconf->wsemul->translate)
(scr->scr_dconf->wsemulcookie, layout, *ks++, &dp);
while (count-- > 0)
(*linesw[tp->t_line].l_rint)(*dp++, tp);
}
}
#ifdef WSDISPLAY_COMPAT_RAWKBD
void
wsdisplay_rawkbdinput(struct device *dev, u_char *buf, int num)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsscreen *scr;
struct tty *tp;
scr = sc->sc_focus;
if (!scr || !WSSCREEN_HAS_TTY(scr))
return;
tp = scr->scr_tty;
while (num-- > 0)
(*linesw[tp->t_line].l_rint)(*buf++, tp);
}
int
wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr)
{
#if NWSKBD > 0
int s, raw, data, error;
struct wsevsrc *inp;
s = spltty();
raw = (scr ? scr->scr_rawkbd : 0);
if (scr != sc->sc_focus || sc->sc_rawkbd == raw) {
splx(s);
return (0);
}
data = raw ? WSKBD_RAW : WSKBD_TRANSLATED;
inp = sc->sc_input;
if (inp == NULL) {
splx(s);
return (ENXIO);
}
error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, FWRITE, 0);
if (!error)
sc->sc_rawkbd = raw;
splx(s);
return (error);
#else
return (0);
#endif
}
#endif
int
wsdisplay_switch3(void *arg, int error, int waitok)
{
struct wsdisplay_softc *sc = arg;
int no;
struct wsscreen *scr;
#ifdef WSDISPLAY_COMPAT_USL
if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
printf("wsdisplay_switch3: not switching\n");
return (EINVAL);
}
no = sc->sc_screenwanted;
if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
panic("wsdisplay_switch3: invalid screen %d", no);
scr = sc->sc_scr[no];
if (!scr) {
printf("wsdisplay_switch3: screen %d disappeared\n", no);
error = ENXIO;
}
if (error) {
/* try to recover, avoid recursion */
if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) {
printf("wsdisplay_switch3: giving up\n");
sc->sc_focus = NULL;
#ifdef WSDISPLAY_COMPAT_RAWKBD
wsdisplay_update_rawkbd(sc, 0);
#endif
CLR(sc->sc_flags, SC_SWITCHPENDING);
return (error);
}
sc->sc_screenwanted = sc->sc_oldscreen;
sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
return (wsdisplay_switch1(arg, 0, waitok));
}
#else
/*
* If we do not have syncops support, we come straight from
* wsdisplay_switch2 which has already validated our arguments
* and did not sleep.
*/
no = sc->sc_screenwanted;
scr = sc->sc_scr[no];
#endif
CLR(sc->sc_flags, SC_SWITCHPENDING);
#ifdef HAVE_BURNER_SUPPORT
if (!error)
wsdisplay_burner_setup(sc, scr);
#endif
if (!error && (scr->scr_flags & SCR_WAITACTIVE))
wakeup(scr);
return (error);
}
int
wsdisplay_switch2(void *arg, int error, int waitok)
{
struct wsdisplay_softc *sc = arg;
int no;
struct wsscreen *scr;
if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
printf("wsdisplay_switch2: not switching\n");
return (EINVAL);
}
no = sc->sc_screenwanted;
if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
panic("wsdisplay_switch2: invalid screen %d", no);
scr = sc->sc_scr[no];
if (!scr) {
printf("wsdisplay_switch2: screen %d disappeared\n", no);
error = ENXIO;
}
if (error) {
/* try to recover, avoid recursion */
if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) {
printf("wsdisplay_switch2: giving up\n");
sc->sc_focus = NULL;
CLR(sc->sc_flags, SC_SWITCHPENDING);
return (error);
}
sc->sc_screenwanted = sc->sc_oldscreen;
sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
return (wsdisplay_switch1(arg, 0, waitok));
}
sc->sc_focusidx = no;
sc->sc_focus = scr;
#ifdef WSDISPLAY_COMPAT_RAWKBD
(void) wsdisplay_update_rawkbd(sc, scr);
#endif
/* keyboard map??? */
#ifdef WSDISPLAY_COMPAT_USL
#define wsswitch_cb3 ((void (*)(void *, int, int))wsdisplay_switch3)
if (scr->scr_syncops) {
error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok,
sc->sc_isconsole && wsdisplay_cons_pollmode ?
0 : wsswitch_cb3, sc);
if (error == EAGAIN) {
/* switch will be done asynchronously */
return (0);
}
}
#endif
return (wsdisplay_switch3(sc, error, waitok));
}
int
wsdisplay_switch1(void *arg, int error, int waitok)
{
struct wsdisplay_softc *sc = arg;
int no;
struct wsscreen *scr;
if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
printf("wsdisplay_switch1: not switching\n");
return (EINVAL);
}
no = sc->sc_screenwanted;
if (no == WSDISPLAY_NULLSCREEN) {
CLR(sc->sc_flags, SC_SWITCHPENDING);
if (!error) {
sc->sc_focus = NULL;
}
wakeup(sc);
return (error);
}
if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
panic("wsdisplay_switch1: invalid screen %d", no);
scr = sc->sc_scr[no];
if (!scr) {
printf("wsdisplay_switch1: screen %d disappeared\n", no);
error = ENXIO;
}
if (error) {
CLR(sc->sc_flags, SC_SWITCHPENDING);
return (error);
}
#define wsswitch_cb2 ((void (*)(void *, int, int))wsdisplay_switch2)
error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie, waitok,
sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsswitch_cb2, sc);
if (error == EAGAIN) {
/* switch will be done asynchronously */
return (0);
}
return (wsdisplay_switch2(sc, error, waitok));
}
int
wsdisplay_switch(struct device *dev, int no, int waitok)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
int s, res = 0;
struct wsscreen *scr;
if (no != WSDISPLAY_NULLSCREEN) {
if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
return (EINVAL);
if (sc->sc_scr[no] == NULL)
return (ENXIO);
}
s = spltty();
while (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && res == 0)
res = tsleep_nsec(&sc->sc_resumescreen, PCATCH, "wsrestore",
INFSLP);
if (res) {
splx(s);
return (res);
}
if ((sc->sc_focus && no == sc->sc_focusidx) ||
(sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) {
splx(s);
return (0);
}
if (ISSET(sc->sc_flags, SC_SWITCHPENDING)) {
splx(s);
return (EBUSY);
}
SET(sc->sc_flags, SC_SWITCHPENDING);
sc->sc_screenwanted = no;
splx(s);
scr = sc->sc_focus;
if (!scr) {
sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
return (wsdisplay_switch1(sc, 0, waitok));
} else
sc->sc_oldscreen = sc->sc_focusidx;
#ifdef WSDISPLAY_COMPAT_USL
#define wsswitch_cb1 ((void (*)(void *, int, int))wsdisplay_switch1)
if (scr->scr_syncops) {
res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok,
sc->sc_isconsole && wsdisplay_cons_pollmode ?
0 : wsswitch_cb1, sc);
if (res == EAGAIN) {
/* switch will be done asynchronously */
return (0);
}
} else if (scr->scr_flags & SCR_GRAPHICS) {
/* no way to save state */
res = EBUSY;
}
#endif
#ifdef HAVE_WSMOUSED_SUPPORT
mouse_remove(scr);
#endif
return (wsdisplay_switch1(sc, res, waitok));
}
void
wsdisplay_reset(struct device *dev, enum wsdisplay_resetops op)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsscreen *scr;
scr = sc->sc_focus;
if (!scr)
return;
switch (op) {
case WSDISPLAY_RESETEMUL:
(*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie,
WSEMUL_RESET);
break;
case WSDISPLAY_RESETCLOSE:
wsdisplay_closescreen(sc, scr);
break;
}
}
#ifdef WSDISPLAY_COMPAT_USL
/*
* Interface for (external) VT switch / process synchronization code
*/
int
wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops,
void *cookie)
{
if (scr->scr_syncops) {
/*
* The screen is already claimed.
* Check if the owner is still alive.
*/
if ((*scr->scr_syncops->check)(scr->scr_synccookie))
return (EBUSY);
}
scr->scr_syncops = ops;
scr->scr_synccookie = cookie;
return (0);
}
int
wsscreen_detach_sync(struct wsscreen *scr)
{
if (!scr->scr_syncops)
return (EINVAL);
scr->scr_syncops = NULL;
return (0);
}
int
wsscreen_lookup_sync(struct wsscreen *scr,
const struct wscons_syncops *ops, /* used as ID */
void **cookiep)
{
if (!scr->scr_syncops || ops != scr->scr_syncops)
return (EINVAL);
*cookiep = scr->scr_synccookie;
return (0);
}
#endif
/*
* Interface to virtual screen stuff
*/
int
wsdisplay_maxscreenidx(struct wsdisplay_softc *sc)
{
return (WSDISPLAY_MAXSCREEN - 1);
}
int
wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx)
{
if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
return (EINVAL);
if (!sc->sc_scr[idx])
return (ENXIO);
return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0);
}
int
wsdisplay_getactivescreen(struct wsdisplay_softc *sc)
{
return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN);
}
int
wsscreen_switchwait(struct wsdisplay_softc *sc, int no)
{
struct wsscreen *scr;
int s, res = 0;
if (no == WSDISPLAY_NULLSCREEN) {
s = spltty();
while (sc->sc_focus && res == 0) {
res = tsleep_nsec(sc, PCATCH, "wswait", INFSLP);
}
splx(s);
return (res);
}
if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
return (ENXIO);
scr = sc->sc_scr[no];
if (!scr)
return (ENXIO);
s = spltty();
if (scr != sc->sc_focus) {
scr->scr_flags |= SCR_WAITACTIVE;
res = tsleep_nsec(scr, PCATCH, "wswait2", INFSLP);
if (scr != sc->sc_scr[no])
res = ENXIO; /* disappeared in the meantime */
else
scr->scr_flags &= ~SCR_WAITACTIVE;
}
splx(s);
return (res);
}
void
wsdisplay_kbdholdscr(struct wsscreen *scr, int hold)
{
if (hold)
scr->scr_hold_screen = 1;
else {
scr->scr_hold_screen = 0;
timeout_add(&scr->scr_tty->t_rstrt_to, 0); /* "immediate" */
}
}
void
wsdisplay_kbdholdscreen(struct device *dev, int hold)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsscreen *scr;
scr = sc->sc_focus;
if (scr != NULL && WSSCREEN_HAS_TTY(scr))
wsdisplay_kbdholdscr(scr, hold);
}
#if NWSKBD > 0
void
wsdisplay_set_console_kbd(struct wsevsrc *src)
{
if (wsdisplay_console_device == NULL) {
src->me_dispdv = NULL;
return;
}
#if NWSMUX > 0
if (wsmux_attach_sc((struct wsmux_softc *)
wsdisplay_console_device->sc_input, src)) {
src->me_dispdv = NULL;
return;
}
#else
wsdisplay_console_device->sc_input = src;
#endif
src->me_dispdv = &wsdisplay_console_device->sc_dv;
}
#if NWSMUX == 0
int
wsdisplay_set_kbd(struct device *disp, struct wsevsrc *kbd)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)disp;
if (sc->sc_input != NULL)
return (EBUSY);
sc->sc_input = kbd;
return (0);
}
#endif
#endif /* NWSKBD > 0 */
/*
* Console interface.
*/
void
wsdisplay_cnputc(dev_t dev, int i)
{
struct wsscreen_internal *dc;
char c = i;
if (!wsdisplay_console_initted)
return;
if (wsdisplay_console_device != NULL &&
(wsdisplay_console_device->sc_scr[0] != NULL) &&
(wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS))
return;
dc = &wsdisplay_console_conf;
#ifdef HAVE_BURNER_SUPPORT
/*wsdisplay_burn(wsdisplay_console_device, WSDISPLAY_BURN_OUTPUT);*/
#endif
(void)(*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1);
}
int
wsdisplay_getc_dummy(dev_t dev)
{
/* panic? */
return (0);
}
void
wsdisplay_pollc(dev_t dev, int on)
{
wsdisplay_cons_pollmode = on;
/* notify to fb drivers */
if (wsdisplay_console_device != NULL &&
wsdisplay_console_device->sc_accessops->pollc != NULL)
(*wsdisplay_console_device->sc_accessops->pollc)
(wsdisplay_console_device->sc_accesscookie, on);
/* notify to kbd drivers */
if (wsdisplay_cons_kbd_pollc)
(*wsdisplay_cons_kbd_pollc)(dev, on);
}
void
wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int),
void (*bell)(dev_t, u_int, u_int, u_int))
{
wsdisplay_cons.cn_getc = get;
wsdisplay_cons.cn_bell = bell;
wsdisplay_cons_kbd_pollc = poll;
}
void
wsdisplay_unset_cons_kbd(void)
{
wsdisplay_cons.cn_getc = wsdisplay_getc_dummy;
wsdisplay_cons.cn_bell = NULL;
wsdisplay_cons_kbd_pollc = NULL;
}
/*
* Switch the console display to its first screen.
*/
void
wsdisplay_switchtoconsole(void)
{
struct wsdisplay_softc *sc;
struct wsscreen *scr;
if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) {
sc = wsdisplay_console_device;
if ((scr = sc->sc_scr[0]) == NULL)
return;
(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie, 0, NULL, NULL);
}
}
/*
* Switch rhe console display to its ddb screen, avoiding locking
* where we can.
*/
void
wsdisplay_enter_ddb(void)
{
struct wsdisplay_softc *sc;
struct wsscreen *scr;
if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) {
sc = wsdisplay_console_device;
if ((scr = sc->sc_scr[0]) == NULL)
return;
if (sc->sc_accessops->enter_ddb) {
(*sc->sc_accessops->enter_ddb)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie);
} else {
(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
scr->scr_dconf->emulcookie, 0, NULL, NULL);
}
}
}
/*
* Deal with the xserver doing driver in userland and thus screwing up suspend
* and resume by switching away from it at suspend/resume time.
*
* these functions must be called from the MD suspend callback, since we may
* need to sleep if we have a user (probably an X server) on a vt. therefore
* this can't be a config_suspend() hook.
*/
void
wsdisplay_suspend(void)
{
int i;
for (i = 0; i < wsdisplay_cd.cd_ndevs; i++)
if (wsdisplay_cd.cd_devs[i] != NULL)
wsdisplay_suspend_device(wsdisplay_cd.cd_devs[i]);
}
void
wsdisplay_suspend_device(struct device *dev)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsscreen *scr;
int active, idx, ret = 0, s;
if ((active = wsdisplay_getactivescreen(sc)) == WSDISPLAY_NULLSCREEN)
return;
scr = sc->sc_scr[active];
/*
* We want to switch out of graphics mode for the suspend, but
* only if we're in WSDISPLAY_MODE_MAPPED.
*/
retry:
idx = WSDISPLAY_MAXSCREEN;
if (scr->scr_flags & SCR_GRAPHICS &&
(scr->scr_flags & SCR_DUMBFB) == 0) {
for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) {
if (sc->sc_scr[idx] == NULL || sc->sc_scr[idx] == scr)
continue;
if ((sc->sc_scr[idx]->scr_flags & SCR_GRAPHICS) == 0)
break;
}
}
/* if we don't have anything to switch to, we can't do anything */
if (idx == WSDISPLAY_MAXSCREEN)
return;
/*
* we do a lot of magic here because we need to know that the
* switch has completed before we return
*/
ret = wsdisplay_switch((struct device *)sc, idx, 1);
if (ret == EBUSY) {
/* XXX sleep on what's going on */
goto retry;
} else if (ret)
return;
s = spltty();
sc->sc_resumescreen = active; /* block other vt switches until resume */
splx(s);
/*
* This will either return ENXIO (invalid (shouldn't happen) or
* wsdisplay disappeared (problem solved)), or EINTR/ERESTART.
* Not much we can do about the latter since we can't return to
* userland.
*/
(void)wsscreen_switchwait(sc, idx);
}
void
wsdisplay_resume(void)
{
int i;
for (i = 0; i < wsdisplay_cd.cd_ndevs; i++)
if (wsdisplay_cd.cd_devs[i] != NULL)
wsdisplay_resume_device(wsdisplay_cd.cd_devs[i]);
}
void
wsdisplay_resume_device(struct device *dev)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
int idx, s;
if (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN) {
s = spltty();
idx = sc->sc_resumescreen;
sc->sc_resumescreen = WSDISPLAY_NULLSCREEN;
wakeup(&sc->sc_resumescreen);
splx(s);
(void)wsdisplay_switch((struct device *)sc, idx, 1);
}
}
#ifdef HAVE_SCROLLBACK_SUPPORT
void
wsscrollback(void *arg, int op)
{
struct wsdisplay_softc *sc = arg;
int lines;
if (sc->sc_focus == NULL)
return;
if (op == WSDISPLAY_SCROLL_RESET)
lines = 0;
else {
lines = sc->sc_focus->scr_dconf->scrdata->nrows - 1;
if (op == WSDISPLAY_SCROLL_BACKWARD)
lines = -lines;
}
if (sc->sc_accessops->scrollback) {
(*sc->sc_accessops->scrollback)(sc->sc_accesscookie,
sc->sc_focus->scr_dconf->emulcookie, lines);
}
}
#endif
#ifdef HAVE_BURNER_SUPPORT
/*
* Update screen burner behaviour after either a screen focus change or
* a screen mode change.
* This is needed to allow X11 to manage screen blanking without any
* interference from the kernel.
*/
void
wsdisplay_burner_setup(struct wsdisplay_softc *sc, struct wsscreen *scr)
{
if (scr->scr_flags & SCR_GRAPHICS) {
/* enable video _immediately_ if it needs to be... */
if (sc->sc_burnman)
wsdisplay_burner(sc);
/* ...and disable the burner while X is running */
if (sc->sc_burnout) {
timeout_del(&sc->sc_burner);
sc->sc_burnout = 0;
}
} else {
/* reenable the burner after exiting from X */
if (!sc->sc_burnman) {
sc->sc_burnout = sc->sc_burnoutintvl;
wsdisplay_burn(sc, sc->sc_burnflags);
}
}
}
void
wsdisplay_burn(void *v, u_int flags)
{
struct wsdisplay_softc *sc = v;
if ((flags & sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT |
WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) &&
sc->sc_accessops->burn_screen) {
if (sc->sc_burnout)
timeout_add_msec(&sc->sc_burner, sc->sc_burnout);
if (sc->sc_burnman)
sc->sc_burnout = 0;
}
}
void
wsdisplay_burner(void *v)
{
struct wsdisplay_softc *sc = v;
int s;
if (sc->sc_accessops->burn_screen) {
(*sc->sc_accessops->burn_screen)(sc->sc_accesscookie,
sc->sc_burnman, sc->sc_burnflags);
s = spltty();
if (sc->sc_burnman) {
sc->sc_burnout = sc->sc_burnoutintvl;
timeout_add_msec(&sc->sc_burner, sc->sc_burnout);
} else
sc->sc_burnout = sc->sc_burninintvl;
sc->sc_burnman = !sc->sc_burnman;
splx(s);
}
}
#endif
int
wsdisplay_get_param(struct wsdisplay_softc *sc, struct wsdisplay_param *dp)
{
int error = ENXIO;
int i;
if (sc != NULL)
return wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_GETPARAM, dp);
for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) {
sc = wsdisplay_cd.cd_devs[i];
if (sc == NULL)
continue;
error = wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_GETPARAM, dp);
if (error == 0)
break;
}
if (error && ws_get_param)
error = ws_get_param(dp);
return error;
}
int
wsdisplay_set_param(struct wsdisplay_softc *sc, struct wsdisplay_param *dp)
{
int error = ENXIO;
int i;
if (sc != NULL)
return wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_SETPARAM, dp);
for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) {
sc = wsdisplay_cd.cd_devs[i];
if (sc == NULL)
continue;
error = wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_SETPARAM, dp);
if (error == 0)
break;
}
if (error && ws_set_param)
error = ws_set_param(dp);
return error;
}
void
wsdisplay_brightness_step(struct device *dev, int dir)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsdisplay_param dp;
int delta, new;
dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
if (wsdisplay_get_param(sc, &dp))
return;
/* Use a step size of approximately 5%. */
delta = max(1, ((dp.max - dp.min) * 5) / 100);
new = dp.curval;
if (dir > 0) {
if (delta > dp.max - dp.curval)
new = dp.max;
else
new += delta;
} else if (dir < 0) {
if (delta > dp.curval - dp.min)
new = dp.min;
else
new -= delta;
}
if (dp.curval == new)
return;
dp.curval = new;
wsdisplay_set_param(sc, &dp);
}
void
wsdisplay_brightness_zero(struct device *dev)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsdisplay_param dp;
dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
if (wsdisplay_get_param(sc, &dp))
return;
dp.curval = dp.min;
wsdisplay_set_param(sc, &dp);
}
void
wsdisplay_brightness_cycle(struct device *dev)
{
struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev;
struct wsdisplay_param dp;
dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS;
if (wsdisplay_get_param(sc, &dp))
return;
if (dp.curval == dp.max)
wsdisplay_brightness_zero(dev);
else
wsdisplay_brightness_step(dev, 1);
}
#ifdef HAVE_WSMOUSED_SUPPORT
/*
* wsmoused(8) support functions
*/
/*
* Main function, called from wsdisplay_cfg_ioctl.
*/
int
wsmoused(struct wsdisplay_softc *sc, caddr_t data, int flag, struct proc *p)
{
struct wscons_event mouse_event = *(struct wscons_event *)data;
if (IS_MOTION_EVENT(mouse_event.type)) {
if (sc->sc_focus != NULL)
motion_event(sc->sc_focus, mouse_event.type,
mouse_event.value);
return 0;
}
if (IS_BUTTON_EVENT(mouse_event.type)) {
if (sc->sc_focus != NULL) {
/* XXX tv_sec contains the number of clicks */
if (mouse_event.type ==
WSCONS_EVENT_MOUSE_DOWN) {
button_event(sc->sc_focus,
mouse_event.value,
mouse_event.time.tv_sec);
} else
button_event(sc->sc_focus,
mouse_event.value, 0);
}
return (0);
}
if (IS_CTRL_EVENT(mouse_event.type)) {
return ctrl_event(sc, mouse_event.type,
mouse_event.value, p);
}
return -1;
}
/*
* Mouse motion events
*/
void
motion_event(struct wsscreen *scr, u_int type, int value)
{
switch (type) {
case WSCONS_EVENT_MOUSE_DELTA_X:
mouse_moverel(scr, value, 0);
break;
case WSCONS_EVENT_MOUSE_DELTA_Y:
mouse_moverel(scr, 0, -value);
break;
#ifdef HAVE_SCROLLBACK_SUPPORT
case WSCONS_EVENT_MOUSE_DELTA_Z:
mouse_zaxis(scr, value);
break;
#endif
default:
break;
}
}
/*
* Button clicks events
*/
void
button_event(struct wsscreen *scr, int button, int clicks)
{
switch (button) {
case MOUSE_COPY_BUTTON:
switch (clicks % 4) {
case 0: /* button is up */
mouse_copy_end(scr);
mouse_copy_selection(scr);
break;
case 1: /* single click */
mouse_copy_start(scr);
mouse_copy_selection(scr);
break;
case 2: /* double click */
mouse_copy_word(scr);
mouse_copy_selection(scr);
break;
case 3: /* triple click */
mouse_copy_line(scr);
mouse_copy_selection(scr);
break;
}
break;
case MOUSE_PASTE_BUTTON:
if (clicks != 0)
mouse_paste(scr);
break;
case MOUSE_EXTEND_BUTTON:
if (clicks != 0)
mouse_copy_extend_after(scr);
break;
default:
break;
}
}
/*
* Control events
*/
int
ctrl_event(struct wsdisplay_softc *sc, u_int type, int value, struct proc *p)
{
struct wsscreen *scr;
int i;
switch (type) {
case WSCONS_EVENT_WSMOUSED_OFF:
CLR(sc->sc_flags, SC_PASTE_AVAIL);
return (0);
case WSCONS_EVENT_WSMOUSED_ON:
if (!sc->sc_accessops->getchar)
/* no wsmoused(8) support in the display driver */
return (1);
allocate_copybuffer(sc);
CLR(sc->sc_flags, SC_PASTE_AVAIL);
for (i = 0 ; i < WSDISPLAY_DEFAULTSCREENS ; i++)
if ((scr = sc->sc_scr[i]) != NULL) {
scr->mouse =
(WS_NCOLS(scr) * WS_NROWS(scr)) / 2;
scr->cursor = scr->mouse;
scr->cpy_start = 0;
scr->cpy_end = 0;
scr->orig_start = 0;
scr->orig_end = 0;
scr->mouse_flags = 0;
}
return (0);
default: /* can't happen, really */
return 0;
}
}
void
mouse_moverel(struct wsscreen *scr, int dx, int dy)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
u_int old_mouse = scr->mouse;
int mouse_col = scr->mouse % N_COLS(dconf);
int mouse_row = scr->mouse / N_COLS(dconf);
/* update position */
if (mouse_col + dx >= MAXCOL(dconf))
mouse_col = MAXCOL(dconf);
else {
if (mouse_col + dx <= 0)
mouse_col = 0;
else
mouse_col += dx;
}
if (mouse_row + dy >= MAXROW(dconf))
mouse_row = MAXROW(dconf);
else {
if (mouse_row + dy <= 0)
mouse_row = 0;
else
mouse_row += dy;
}
scr->mouse = mouse_row * N_COLS(dconf) + mouse_col;
/* if we have moved */
if (old_mouse != scr->mouse) {
/* XXX unblank screen if display.ms_act */
if (ISSET(scr->mouse_flags, SEL_IN_PROGRESS)) {
/* selection in progress */
mouse_copy_extend(scr);
} else {
inverse_char(scr, scr->mouse);
if (ISSET(scr->mouse_flags, MOUSE_VISIBLE))
inverse_char(scr, old_mouse);
else
SET(scr->mouse_flags, MOUSE_VISIBLE);
}
}
}
void
inverse_char(struct wsscreen *scr, u_int pos)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
struct wsdisplay_charcell cell;
int fg, bg, ul;
int flags;
int tmp;
uint32_t attr;
GETCHAR(scr, pos, &cell);
(*dconf->emulops->unpack_attr)(dconf->emulcookie, cell.attr, &fg,
&bg, &ul);
/*
* Display the mouse cursor as a color inverted cell whenever
* possible. If this is not possible, ask for the video reverse
* attribute.
*/
flags = 0;
if (dconf->scrdata->capabilities & WSSCREEN_WSCOLORS) {
flags |= WSATTR_WSCOLORS;
tmp = fg;
fg = bg;
bg = tmp;
} else if (dconf->scrdata->capabilities & WSSCREEN_REVERSE) {
flags |= WSATTR_REVERSE;
}
if ((*dconf->emulops->pack_attr)(dconf->emulcookie, fg, bg, flags |
(ul ? WSATTR_UNDERLINE : 0), &attr) == 0) {
cell.attr = attr;
PUTCHAR(dconf, pos, cell.uc, cell.attr);
}
}
void
inverse_region(struct wsscreen *scr, u_int start, u_int end)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
u_int current_pos;
u_int abs_end;
/* sanity check, useful because 'end' can be (u_int)-1 */
abs_end = N_COLS(dconf) * N_ROWS(dconf);
if (end > abs_end)
return;
current_pos = start;
while (current_pos <= end)
inverse_char(scr, current_pos++);
}
/*
* Return the number of contiguous blank characters between the right margin
* if border == 1 or between the next non-blank character and the current mouse
* cursor if border == 0
*/
u_int
skip_spc_right(struct wsscreen *scr, int border)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
struct wsdisplay_charcell cell;
u_int current = scr->cpy_end;
u_int mouse_col = scr->cpy_end % N_COLS(dconf);
u_int limit = current + (N_COLS(dconf) - mouse_col - 1);
u_int res = 0;
while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' &&
current <= limit) {
current++;
res++;
}
if (border == BORDER) {
if (current > limit)
return (res - 1);
else
return (0);
} else {
if (res != 0)
return (res - 1);
else
return (res);
}
}
/*
* Return the number of contiguous blank characters between the first of the
* contiguous blank characters and the current mouse cursor
*/
u_int
skip_spc_left(struct wsscreen *scr)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
struct wsdisplay_charcell cell;
u_int current = scr->cpy_start;
u_int mouse_col = scr->mouse % N_COLS(dconf);
u_int limit = current - mouse_col;
u_int res = 0;
while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' &&
current >= limit) {
current--;
res++;
}
if (res != 0)
res--;
return (res);
}
/*
* Class of characters
* Stolen from xterm sources of the Xfree project (see cvs tag below)
* $TOG: button.c /main/76 1997/07/30 16:56:19 kaleb $
*/
static const int charClass[256] = {
/* NUL SOH STX ETX EOT ENQ ACK BEL */
32, 1, 1, 1, 1, 1, 1, 1,
/* BS HT NL VT NP CR SO SI */
1, 32, 1, 1, 1, 1, 1, 1,
/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
1, 1, 1, 1, 1, 1, 1, 1,
/* CAN EM SUB ESC FS GS RS US */
1, 1, 1, 1, 1, 1, 1, 1,
/* SP ! " # $ % & ' */
32, 33, 34, 35, 36, 37, 38, 39,
/* ( ) * + , - . / */
40, 41, 42, 43, 44, 45, 46, 47,
/* 0 1 2 3 4 5 6 7 */
48, 48, 48, 48, 48, 48, 48, 48,
/* 8 9 : ; < = > ? */
48, 48, 58, 59, 60, 61, 62, 63,
/* @ A B C D E F G */
64, 48, 48, 48, 48, 48, 48, 48,
/* H I J K L M N O */
48, 48, 48, 48, 48, 48, 48, 48,
/* P Q R S T U V W */
48, 48, 48, 48, 48, 48, 48, 48,
/* X Y Z [ \ ] ^ _ */
48, 48, 48, 91, 92, 93, 94, 48,
/* ` a b c d e f g */
96, 48, 48, 48, 48, 48, 48, 48,
/* h i j k l m n o */
48, 48, 48, 48, 48, 48, 48, 48,
/* p q r s t u v w */
48, 48, 48, 48, 48, 48, 48, 48,
/* x y z { | } ~ DEL */
48, 48, 48, 123, 124, 125, 126, 1,
/* x80 x81 x82 x83 IND NEL SSA ESA */
1, 1, 1, 1, 1, 1, 1, 1,
/* HTS HTJ VTS PLD PLU RI SS2 SS3 */
1, 1, 1, 1, 1, 1, 1, 1,
/* DCS PU1 PU2 STS CCH MW SPA EPA */
1, 1, 1, 1, 1, 1, 1, 1,
/* x98 x99 x9A CSI ST OSC PM APC */
1, 1, 1, 1, 1, 1, 1, 1,
/* - i c/ L ox Y- | So */
160, 161, 162, 163, 164, 165, 166, 167,
/* .. c0 ip << _ R0 - */
168, 169, 170, 171, 172, 173, 174, 175,
/* o +- 2 3 ' u q| . */
176, 177, 178, 179, 180, 181, 182, 183,
/* , 1 2 >> 1/4 1/2 3/4 ? */
184, 185, 186, 187, 188, 189, 190, 191,
/* A` A' A^ A~ A: Ao AE C, */
48, 48, 48, 48, 48, 48, 48, 48,
/* E` E' E^ E: I` I' I^ I: */
48, 48, 48, 48, 48, 48, 48, 48,
/* D- N~ O` O' O^ O~ O: X */
48, 48, 48, 48, 48, 48, 48, 216,
/* O/ U` U' U^ U: Y' P B */
48, 48, 48, 48, 48, 48, 48, 48,
/* a` a' a^ a~ a: ao ae c, */
48, 48, 48, 48, 48, 48, 48, 48,
/* e` e' e^ e: i` i' i^ i: */
48, 48, 48, 48, 48, 48, 48, 48,
/* d n~ o` o' o^ o~ o: -: */
48, 48, 48, 48, 48, 48, 48, 248,
/* o/ u` u' u^ u: y' P y: */
48, 48, 48, 48, 48, 48, 48, 48
};
/*
* Find the first blank beginning after the current cursor position
*/
u_int
skip_char_right(struct wsscreen *scr, u_int offset)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
struct wsdisplay_charcell cell;
u_int current = offset;
u_int limit = current +
(N_COLS(dconf) - (scr->mouse % N_COLS(dconf)) - 1);
u_int class;
u_int res = 0;
GETCHAR(scr, current, &cell);
class = charClass[cell.uc & 0xff];
while (GETCHAR(scr, current, &cell) == 0 &&
charClass[cell.uc & 0xff] == class && current <= limit) {
current++;
res++;
}
if (res != 0)
res--;
return (res);
}
/*
* Find the first non-blank character before the cursor position
*/
u_int
skip_char_left(struct wsscreen *scr, u_int offset)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
struct wsdisplay_charcell cell;
u_int current = offset;
u_int limit = current - (scr->mouse % N_COLS(dconf));
u_int class;
u_int res = 0;
GETCHAR(scr, current, &cell);
class = charClass[cell.uc & 0xff];
while (GETCHAR(scr, current, &cell) == 0 &&
charClass[cell.uc & 0xff] == class && current >= limit) {
current--;
res++;
}
if (res != 0)
res--;
return (res);
}
/*
* Compare character classes
*/
u_int
class_cmp(struct wsscreen *scr, u_int first, u_int second)
{
struct wsdisplay_charcell cell;
u_int first_class;
u_int second_class;
if (GETCHAR(scr, first, &cell) != 0)
return (1);
first_class = charClass[cell.uc & 0xff];
if (GETCHAR(scr, second, &cell) != 0)
return (1);
second_class = charClass[cell.uc & 0xff];
if (first_class != second_class)
return (1);
else
return (0);
}
/*
* Beginning of a copy operation
*/
void
mouse_copy_start(struct wsscreen *scr)
{
u_int right;
/* if no selection, then that's the first one */
SET(scr->sc->sc_flags, SC_PASTE_AVAIL);
/* remove the previous selection */
if (ISSET(scr->mouse_flags, SEL_EXISTS))
remove_selection(scr);
/* initial show of the cursor */
if (!ISSET(scr->mouse_flags, MOUSE_VISIBLE))
inverse_char(scr, scr->mouse);
scr->cpy_start = scr->cpy_end = scr->mouse;
scr->orig_start = scr->cpy_start;
scr->orig_end = scr->cpy_end;
scr->cursor = scr->cpy_end + 1; /* init value */
/* useful later, in mouse_copy_extend */
right = skip_spc_right(scr, BORDER);
if (right)
SET(scr->mouse_flags, BLANK_TO_EOL);
SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_CHAR);
CLR(scr->mouse_flags, SEL_BY_WORD | SEL_BY_LINE);
CLR(scr->mouse_flags, MOUSE_VISIBLE); /* cursor hidden in selection */
}
/*
* Copy of the word under the cursor
*/
void
mouse_copy_word(struct wsscreen *scr)
{
struct wsdisplay_charcell cell;
u_int right;
u_int left;
if (ISSET(scr->mouse_flags, SEL_EXISTS))
remove_selection(scr);
if (ISSET(scr->mouse_flags, MOUSE_VISIBLE))
inverse_char(scr, scr->mouse);
scr->cpy_start = scr->cpy_end = scr->mouse;
if (GETCHAR(scr, scr->mouse, &cell) == 0 &&
IS_ALPHANUM(cell.uc)) {
right = skip_char_right(scr, scr->cpy_end);
left = skip_char_left(scr, scr->cpy_start);
} else {
right = skip_spc_right(scr, NO_BORDER);
left = skip_spc_left(scr);
}
scr->cpy_start -= left;
scr->cpy_end += right;
scr->orig_start = scr->cpy_start;
scr->orig_end = scr->cpy_end;
scr->cursor = scr->cpy_end + 1; /* init value, never happen */
inverse_region(scr, scr->cpy_start, scr->cpy_end);
SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_WORD);
CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_LINE);
/* mouse cursor hidden in the selection */
CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE);
}
/*
* Copy of the current line
*/
void
mouse_copy_line(struct wsscreen *scr)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
u_int row = scr->mouse / N_COLS(dconf);
if (ISSET(scr->mouse_flags, SEL_EXISTS))
remove_selection(scr);
if (ISSET(scr->mouse_flags, MOUSE_VISIBLE))
inverse_char(scr, scr->mouse);
scr->cpy_start = row * N_COLS(dconf);
scr->cpy_end = scr->cpy_start + (N_COLS(dconf) - 1);
scr->orig_start = scr->cpy_start;
scr->orig_end = scr->cpy_end;
scr->cursor = scr->cpy_end + 1;
inverse_region(scr, scr->cpy_start, scr->cpy_end);
SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_LINE);
CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_WORD);
/* mouse cursor hidden in the selection */
CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE);
}
/*
* End of a copy operation
*/
void
mouse_copy_end(struct wsscreen *scr)
{
CLR(scr->mouse_flags, SEL_IN_PROGRESS);
if (ISSET(scr->mouse_flags, SEL_BY_WORD) ||
ISSET(scr->mouse_flags, SEL_BY_LINE)) {
if (scr->cursor != scr->cpy_end + 1)
inverse_char(scr, scr->cursor);
scr->cursor = scr->cpy_end + 1;
}
}
/*
* Generic selection extend function
*/
void
mouse_copy_extend(struct wsscreen *scr)
{
if (ISSET(scr->mouse_flags, SEL_BY_CHAR))
mouse_copy_extend_char(scr);
if (ISSET(scr->mouse_flags, SEL_BY_WORD))
mouse_copy_extend_word(scr);
if (ISSET(scr->mouse_flags, SEL_BY_LINE))
mouse_copy_extend_line(scr);
}
/*
* Extend a selected region, character by character
*/
void
mouse_copy_extend_char(struct wsscreen *scr)
{
u_int right;
if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
if (ISSET(scr->mouse_flags, BLANK_TO_EOL)) {
/*
* First extension of selection. We handle special
* cases of blank characters to eol
*/
right = skip_spc_right(scr, BORDER);
if (scr->mouse > scr->orig_start) {
/* the selection goes to the lower part of
the screen */
/* remove the previous cursor, start of
selection is now next line */
inverse_char(scr, scr->cpy_start);
scr->cpy_start += (right + 1);
scr->cpy_end = scr->cpy_start;
scr->orig_start = scr->cpy_start;
/* simulate the initial mark */
inverse_char(scr, scr->cpy_start);
} else {
/* the selection goes to the upper part
of the screen */
/* remove the previous cursor, start of
selection is now at the eol */
inverse_char(scr, scr->cpy_start);
scr->orig_start += (right + 1);
scr->cpy_start = scr->orig_start - 1;
scr->cpy_end = scr->orig_start - 1;
/* simulate the initial mark */
inverse_char(scr, scr->cpy_start);
}
CLR(scr->mouse_flags, BLANK_TO_EOL);
}
if (scr->mouse < scr->orig_start &&
scr->cpy_end >= scr->orig_start) {
/* we go to the upper part of the screen */
/* reverse the old selection region */
remove_selection(scr);
scr->cpy_end = scr->orig_start - 1;
scr->cpy_start = scr->orig_start;
}
if (scr->cpy_start < scr->orig_start &&
scr->mouse >= scr->orig_start) {
/* we go to the lower part of the screen */
/* reverse the old selection region */
remove_selection(scr);
scr->cpy_start = scr->orig_start;
scr->cpy_end = scr->orig_start - 1;
}
/* restore flags cleared in remove_selection() */
SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS);
}
if (scr->mouse >= scr->orig_start) {
/* lower part of the screen */
if (scr->mouse > scr->cpy_end) {
/* extending selection */
inverse_region(scr, scr->cpy_end + 1, scr->mouse);
} else {
/* reducing selection */
inverse_region(scr, scr->mouse + 1, scr->cpy_end);
}
scr->cpy_end = scr->mouse;
} else {
/* upper part of the screen */
if (scr->mouse < scr->cpy_start) {
/* extending selection */
inverse_region(scr, scr->mouse, scr->cpy_start - 1);
} else {
/* reducing selection */
inverse_region(scr, scr->cpy_start, scr->mouse - 1);
}
scr->cpy_start = scr->mouse;
}
}
/*
* Extend a selected region, word by word
*/
void
mouse_copy_extend_word(struct wsscreen *scr)
{
u_int old_cpy_end;
u_int old_cpy_start;
if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
/* remove cursor in selection (black one) */
if (scr->cursor != scr->cpy_end + 1)
inverse_char(scr, scr->cursor);
/* now, switch between lower and upper part of the screen */
if (scr->mouse < scr->orig_start &&
scr->cpy_end >= scr->orig_start) {
/* going to the upper part of the screen */
inverse_region(scr, scr->orig_end + 1, scr->cpy_end);
scr->cpy_end = scr->orig_end;
}
if (scr->mouse > scr->orig_end &&
scr->cpy_start <= scr->orig_start) {
/* going to the lower part of the screen */
inverse_region(scr, scr->cpy_start,
scr->orig_start - 1);
scr->cpy_start = scr->orig_start;
}
}
if (scr->mouse >= scr->orig_start) {
/* lower part of the screen */
if (scr->mouse > scr->cpy_end) {
/* extending selection */
old_cpy_end = scr->cpy_end;
scr->cpy_end = scr->mouse +
skip_char_right(scr, scr->mouse);
inverse_region(scr, old_cpy_end + 1, scr->cpy_end);
} else {
if (class_cmp(scr, scr->mouse, scr->mouse + 1)) {
/* reducing selection (remove last word) */
old_cpy_end = scr->cpy_end;
scr->cpy_end = scr->mouse;
inverse_region(scr, scr->cpy_end + 1,
old_cpy_end);
} else {
old_cpy_end = scr->cpy_end;
scr->cpy_end = scr->mouse +
skip_char_right(scr, scr->mouse);
if (scr->cpy_end != old_cpy_end) {
/* reducing selection, from the end of
* next word */
inverse_region(scr, scr->cpy_end + 1,
old_cpy_end);
}
}
}
} else {
/* upper part of the screen */
if (scr->mouse < scr->cpy_start) {
/* extending selection */
old_cpy_start = scr->cpy_start;
scr->cpy_start = scr->mouse -
skip_char_left(scr, scr->mouse);
inverse_region(scr, scr->cpy_start, old_cpy_start - 1);
} else {
if (class_cmp(scr, scr->mouse - 1, scr->mouse)) {
/* reducing selection (remove last word) */
old_cpy_start = scr->cpy_start;
scr->cpy_start = scr->mouse;
inverse_region(scr, old_cpy_start,
scr->cpy_start - 1);
} else {
old_cpy_start = scr->cpy_start;
scr->cpy_start = scr->mouse -
skip_char_left(scr, scr->mouse);
if (scr->cpy_start != old_cpy_start) {
inverse_region(scr, old_cpy_start,
scr->cpy_start - 1);
}
}
}
}
if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
/* display new cursor */
scr->cursor = scr->mouse;
inverse_char(scr, scr->cursor);
}
}
/*
* Extend a selected region, line by line
*/
void
mouse_copy_extend_line(struct wsscreen *scr)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
u_int old_row;
u_int new_row;
u_int old_cpy_start;
u_int old_cpy_end;
if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
/* remove cursor in selection (black one) */
if (scr->cursor != scr->cpy_end + 1)
inverse_char(scr, scr->cursor);
/* now, switch between lower and upper part of the screen */
if (scr->mouse < scr->orig_start &&
scr->cpy_end >= scr->orig_start) {
/* going to the upper part of the screen */
inverse_region(scr, scr->orig_end + 1, scr->cpy_end);
scr->cpy_end = scr->orig_end;
}
if (scr->mouse > scr->orig_end &&
scr->cpy_start <= scr->orig_start) {
/* going to the lower part of the screen */
inverse_region(scr, scr->cpy_start,
scr->orig_start - 1);
scr->cpy_start = scr->orig_start;
}
}
if (scr->mouse >= scr->orig_start) {
/* lower part of the screen */
if (scr->cursor == scr->cpy_end + 1)
scr->cursor = scr->cpy_end;
old_row = scr->cursor / N_COLS(dconf);
new_row = scr->mouse / N_COLS(dconf);
old_cpy_end = scr->cpy_end;
scr->cpy_end = new_row * N_COLS(dconf) + MAXCOL(dconf);
if (new_row > old_row)
inverse_region(scr, old_cpy_end + 1, scr->cpy_end);
else if (new_row < old_row)
inverse_region(scr, scr->cpy_end + 1, old_cpy_end);
} else {
/* upper part of the screen */
old_row = scr->cursor / N_COLS(dconf);
new_row = scr->mouse / N_COLS(dconf);
old_cpy_start = scr->cpy_start;
scr->cpy_start = new_row * N_COLS(dconf);
if (new_row < old_row)
inverse_region(scr, scr->cpy_start, old_cpy_start - 1);
else if (new_row > old_row)
inverse_region(scr, old_cpy_start, scr->cpy_start - 1);
}
if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
/* display new cursor */
scr->cursor = scr->mouse;
inverse_char(scr, scr->cursor);
}
}
/*
* Add an extension to a selected region, word by word
*/
void
mouse_copy_extend_after(struct wsscreen *scr)
{
u_int start_dist;
u_int end_dist;
if (ISSET(scr->mouse_flags, SEL_EXISTS)) {
SET(scr->mouse_flags, SEL_EXT_AFTER);
mouse_hide(scr); /* hide current cursor */
if (scr->cpy_start > scr->mouse)
start_dist = scr->cpy_start - scr->mouse;
else
start_dist = scr->mouse - scr->cpy_start;
if (scr->mouse > scr->cpy_end)
end_dist = scr->mouse - scr->cpy_end;
else
end_dist = scr->cpy_end - scr->mouse;
if (start_dist < end_dist) {
/* upper part of the screen*/
scr->orig_start = scr->mouse + 1;
/* only used in mouse_copy_extend_line() */
scr->cursor = scr->cpy_start;
} else {
/* lower part of the screen */
scr->orig_start = scr->mouse;
/* only used in mouse_copy_extend_line() */
scr->cursor = scr->cpy_end;
}
if (ISSET(scr->mouse_flags, SEL_BY_CHAR))
mouse_copy_extend_char(scr);
if (ISSET(scr->mouse_flags, SEL_BY_WORD))
mouse_copy_extend_word(scr);
if (ISSET(scr->mouse_flags, SEL_BY_LINE))
mouse_copy_extend_line(scr);
mouse_copy_selection(scr);
}
}
void
mouse_hide(struct wsscreen *scr)
{
if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) {
inverse_char(scr, scr->mouse);
CLR(scr->mouse_flags, MOUSE_VISIBLE);
}
}
/*
* Remove a previously selected region
*/
void
remove_selection(struct wsscreen *scr)
{
if (ISSET(scr->mouse_flags, SEL_EXT_AFTER)) {
/* reset the flag indicating an extension of selection */
CLR(scr->mouse_flags, SEL_EXT_AFTER);
}
inverse_region(scr, scr->cpy_start, scr->cpy_end);
CLR(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS);
}
/*
* Put the current visual selection in the selection buffer
*/
void
mouse_copy_selection(struct wsscreen *scr)
{
struct wsscreen_internal *dconf = scr->scr_dconf;
struct wsdisplay_charcell cell;
u_int current = 0;
u_int blank = current;
u_int buf_end = (N_COLS(dconf) + 1) * N_ROWS(dconf);
u_int sel_cur;
u_int sel_end;
sel_cur = scr->cpy_start;
sel_end = scr->cpy_end;
while (sel_cur <= sel_end && current < buf_end - 1) {
if (GETCHAR(scr, sel_cur, &cell) != 0)
break;
scr->sc->sc_copybuffer[current] = cell.uc;
if (!IS_SPACE(cell.uc))
blank = current + 1; /* first blank after non-blank */
current++;
if (sel_cur % N_COLS(dconf) == MAXCOL(dconf)) {
/*
* If we are on the last column of the screen,
* insert a carriage return.
*/
scr->sc->sc_copybuffer[blank] = '\r';
current = ++blank;
}
sel_cur++;
}
scr->sc->sc_copybuffer[current] = '\0';
}
/*
* Paste the current selection
*/
void
mouse_paste(struct wsscreen *scr)
{
char *current = scr->sc->sc_copybuffer;
struct tty *tp;
u_int len;
if (ISSET(scr->sc->sc_flags, SC_PASTE_AVAIL)) {
if (!WSSCREEN_HAS_TTY(scr))
return;
tp = scr->scr_tty;
for (len = strlen(scr->sc->sc_copybuffer); len != 0; len--)
(*linesw[tp->t_line].l_rint)(*current++, tp);
}
}
#ifdef HAVE_SCROLLBACK_SUPPORT
/*
* Handle the z axis.
* The z axis (roller or wheel) is mapped by default to scrollback.
*/
void
mouse_zaxis(struct wsscreen *scr, int z)
{
if (z < 0)
wsscrollback(scr->sc, WSDISPLAY_SCROLL_BACKWARD);
else
wsscrollback(scr->sc, WSDISPLAY_SCROLL_FORWARD);
}
#endif
/*
* Allocate the copy buffer. The size is:
* (cols + 1) * (rows)
* (+1 for '\n' at the end of lines),
* where cols and rows are the maximum of column and rows of all screens.
*/
void
allocate_copybuffer(struct wsdisplay_softc *sc)
{
int nscreens = sc->sc_scrdata->nscreens;
int i, s;
const struct wsscreen_descr **screens_list = sc->sc_scrdata->screens;
const struct wsscreen_descr *current;
u_int size = sc->sc_copybuffer_size;
s = spltty();
for (i = 0; i < nscreens; i++) {
current = *screens_list;
if ((current->ncols + 1) * current->nrows > size)
size = (current->ncols + 1) * current->nrows;
screens_list++;
}
if (size != sc->sc_copybuffer_size && sc->sc_copybuffer_size != 0) {
bzero(sc->sc_copybuffer, sc->sc_copybuffer_size);
free(sc->sc_copybuffer, M_DEVBUF, sc->sc_copybuffer_size);
}
if ((sc->sc_copybuffer = (char *)malloc(size, M_DEVBUF, M_NOWAIT)) ==
NULL) {
printf("%s: couldn't allocate copy buffer\n",
sc->sc_dv.dv_xname);
size = 0;
}
sc->sc_copybuffer_size = size;
splx(s);
}
/* Remove selection and cursor on current screen */
void
mouse_remove(struct wsscreen *scr)
{
if (ISSET(scr->mouse_flags, SEL_EXISTS))
remove_selection(scr);
mouse_hide(scr);
}
#endif /* HAVE_WSMOUSED_SUPPORT */
253
248
5
5
3
241
250
8
38
244
1
46
1
250
448
3
2
44
405
443
2
444
440
445
431
445
441
4
444
11
39
424
11
438
7
9
442
11
434
9
9
8
1
2
7
7
7
7
9
9
122
122
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
/* $OpenBSD: ffs_vnops.c,v 1.100 2022/06/26 05:20:43 visa Exp $ */
/* $NetBSD: ffs_vnops.c,v 1.7 1996/05/11 18:27:24 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_vnops.c 8.10 (Berkeley) 8/10/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/pool.h>
#include <sys/event.h>
#include <sys/specdev.h>
#include <miscfs/fifofs/fifo.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
const struct vops ffs_vops = {
.vop_lookup = ufs_lookup,
.vop_create = ufs_create,
.vop_mknod = ufs_mknod,
.vop_open = ufs_open,
.vop_close = ufs_close,
.vop_access = ufs_access,
.vop_getattr = ufs_getattr,
.vop_setattr = ufs_setattr,
.vop_read = ffs_read,
.vop_write = ffs_write,
.vop_ioctl = ufs_ioctl,
.vop_kqfilter = ufs_kqfilter,
.vop_revoke = vop_generic_revoke,
.vop_fsync = ffs_fsync,
.vop_remove = ufs_remove,
.vop_link = ufs_link,
.vop_rename = ufs_rename,
.vop_mkdir = ufs_mkdir,
.vop_rmdir = ufs_rmdir,
.vop_symlink = ufs_symlink,
.vop_readdir = ufs_readdir,
.vop_readlink = ufs_readlink,
.vop_abortop = vop_generic_abortop,
.vop_inactive = ufs_inactive,
.vop_reclaim = ffs_reclaim,
.vop_lock = ufs_lock,
.vop_unlock = ufs_unlock,
.vop_bmap = ufs_bmap,
.vop_strategy = ufs_strategy,
.vop_print = ufs_print,
.vop_islocked = ufs_islocked,
.vop_pathconf = ufs_pathconf,
.vop_advlock = ufs_advlock,
.vop_bwrite = vop_generic_bwrite
};
const struct vops ffs_specvops = {
.vop_close = ufsspec_close,
.vop_access = ufs_access,
.vop_getattr = ufs_getattr,
.vop_setattr = ufs_setattr,
.vop_read = ufsspec_read,
.vop_write = ufsspec_write,
.vop_fsync = ffs_fsync,
.vop_inactive = ufs_inactive,
.vop_reclaim = ffs_reclaim,
.vop_lock = ufs_lock,
.vop_unlock = ufs_unlock,
.vop_print = ufs_print,
.vop_islocked = ufs_islocked,
/* XXX: Keep in sync with spec_vops */
.vop_lookup = vop_generic_lookup,
.vop_create = vop_generic_badop,
.vop_mknod = vop_generic_badop,
.vop_open = spec_open,
.vop_ioctl = spec_ioctl,
.vop_kqfilter = spec_kqfilter,
.vop_revoke = vop_generic_revoke,
.vop_remove = vop_generic_badop,
.vop_link = vop_generic_badop,
.vop_rename = vop_generic_badop,
.vop_mkdir = vop_generic_badop,
.vop_rmdir = vop_generic_badop,
.vop_symlink = vop_generic_badop,
.vop_readdir = vop_generic_badop,
.vop_readlink = vop_generic_badop,
.vop_abortop = vop_generic_badop,
.vop_bmap = vop_generic_bmap,
.vop_strategy = spec_strategy,
.vop_pathconf = spec_pathconf,
.vop_advlock = spec_advlock,
.vop_bwrite = vop_generic_bwrite,
};
#ifdef FIFO
const struct vops ffs_fifovops = {
.vop_close = ufsfifo_close,
.vop_access = ufs_access,
.vop_getattr = ufs_getattr,
.vop_setattr = ufs_setattr,
.vop_read = ufsfifo_read,
.vop_write = ufsfifo_write,
.vop_fsync = ffs_fsync,
.vop_inactive = ufs_inactive,
.vop_reclaim = ffsfifo_reclaim,
.vop_lock = ufs_lock,
.vop_unlock = ufs_unlock,
.vop_print = ufs_print,
.vop_islocked = ufs_islocked,
.vop_bwrite = vop_generic_bwrite,
/* XXX: Keep in sync with fifo_vops */
.vop_lookup = vop_generic_lookup,
.vop_create = vop_generic_badop,
.vop_mknod = vop_generic_badop,
.vop_open = fifo_open,
.vop_ioctl = fifo_ioctl,
.vop_kqfilter = fifo_kqfilter,
.vop_revoke = vop_generic_revoke,
.vop_remove = vop_generic_badop,
.vop_link = vop_generic_badop,
.vop_rename = vop_generic_badop,
.vop_mkdir = vop_generic_badop,
.vop_rmdir = vop_generic_badop,
.vop_symlink = vop_generic_badop,
.vop_readdir = vop_generic_badop,
.vop_readlink = vop_generic_badop,
.vop_abortop = vop_generic_badop,
.vop_bmap = vop_generic_bmap,
.vop_strategy = vop_generic_badop,
.vop_pathconf = fifo_pathconf,
.vop_advlock = fifo_advlock
};
#endif /* FIFO */
/*
* Vnode op for reading.
*/
int
ffs_read(void *v)
{
struct vop_read_args *ap = v;
struct vnode *vp;
struct inode *ip;
struct uio *uio;
struct fs *fs;
struct buf *bp;
daddr_t lbn, nextlbn;
off_t bytesinfile;
int size, xfersize, blkoffset;
mode_t mode;
int error;
vp = ap->a_vp;
ip = VTOI(vp);
mode = DIP(ip, mode);
uio = ap->a_uio;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("ffs_read: mode");
if (vp->v_type == VLNK) {
if (DIP(ip, size) < ip->i_ump->um_maxsymlinklen ||
(ip->i_ump->um_maxsymlinklen == 0 && DIP(ip, blocks) == 0))
panic("ffs_read: short symlink");
} else if (vp->v_type != VREG && vp->v_type != VDIR)
panic("ffs_read: type %d", vp->v_type);
#endif
fs = ip->i_fs;
if (uio->uio_offset < 0)
return (EINVAL);
if (uio->uio_resid == 0)
return (0);
for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
if ((bytesinfile = DIP(ip, size) - uio->uio_offset) <= 0)
break;
lbn = lblkno(fs, uio->uio_offset);
nextlbn = lbn + 1;
size = fs->fs_bsize; /* WAS blksize(fs, ip, lbn); */
blkoffset = blkoff(fs, uio->uio_offset);
xfersize = fs->fs_bsize - blkoffset;
if (uio->uio_resid < xfersize)
xfersize = uio->uio_resid;
if (bytesinfile < xfersize)
xfersize = bytesinfile;
if (lblktosize(fs, nextlbn) >= DIP(ip, size))
error = bread(vp, lbn, size, &bp);
else if (lbn - 1 == ip->i_ci.ci_lastr ||
uio->uio_resid > xfersize) {
error = bread_cluster(vp, lbn, size, &bp);
} else
error = bread(vp, lbn, size, &bp);
if (error)
break;
ip->i_ci.ci_lastr = lbn;
/*
* We should only get non-zero b_resid when an I/O error
* has occurred, which should cause us to break above.
* However, if the short read did not cause an error,
* then we want to ensure that we do not uiomove bad
* or uninitialized data.
*/
size -= bp->b_resid;
if (size < xfersize) {
if (size == 0)
break;
xfersize = size;
}
error = uiomove(bp->b_data + blkoffset, xfersize, uio);
if (error)
break;
brelse(bp);
}
if (bp != NULL)
brelse(bp);
if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
(ip->i_flag & (IN_CHANGE | IN_UPDATE))) {
ip->i_flag |= IN_ACCESS;
}
return (error);
}
/*
* Vnode op for writing.
*/
int
ffs_write(void *v)
{
struct vop_write_args *ap = v;
struct vnode *vp;
struct uio *uio;
struct inode *ip;
struct fs *fs;
struct buf *bp;
daddr_t lbn;
off_t osize;
int blkoffset, error, extended, flags, ioflag, size, xfersize;
size_t resid;
ssize_t overrun;
extended = 0;
ioflag = ap->a_ioflag;
uio = ap->a_uio;
vp = ap->a_vp;
ip = VTOI(vp);
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_WRITE)
panic("ffs_write: mode");
#endif
/*
* If writing 0 bytes, succeed and do not change
* update time or file offset (standards compliance)
*/
if (uio->uio_resid == 0)
return (0);
switch (vp->v_type) {
case VREG:
if (ioflag & IO_APPEND)
uio->uio_offset = DIP(ip, size);
if ((DIP(ip, flags) & APPEND) && uio->uio_offset != DIP(ip, size))
return (EPERM);
/* FALLTHROUGH */
case VLNK:
break;
case VDIR:
if ((ioflag & IO_SYNC) == 0)
panic("ffs_write: nonsync dir write");
break;
default:
panic("ffs_write: type %d", vp->v_type);
}
fs = ip->i_fs;
if (uio->uio_offset < 0 ||
(u_int64_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize)
return (EFBIG);
/* do the filesize rlimit check */
if ((error = vn_fsizechk(vp, uio, ioflag, &overrun)))
return (error);
resid = uio->uio_resid;
osize = DIP(ip, size);
flags = ioflag & IO_SYNC ? B_SYNC : 0;
for (error = 0; uio->uio_resid > 0;) {
lbn = lblkno(fs, uio->uio_offset);
blkoffset = blkoff(fs, uio->uio_offset);
xfersize = fs->fs_bsize - blkoffset;
if (uio->uio_resid < xfersize)
xfersize = uio->uio_resid;
if (fs->fs_bsize > xfersize)
flags |= B_CLRBUF;
else
flags &= ~B_CLRBUF;
if ((error = UFS_BUF_ALLOC(ip, uio->uio_offset, xfersize,
ap->a_cred, flags, &bp)) != 0)
break;
if (uio->uio_offset + xfersize > DIP(ip, size)) {
DIP_ASSIGN(ip, size, uio->uio_offset + xfersize);
uvm_vnp_setsize(vp, DIP(ip, size));
extended = 1;
}
(void)uvm_vnp_uncache(vp);
size = blksize(fs, ip, lbn) - bp->b_resid;
if (size < xfersize)
xfersize = size;
error = uiomove(bp->b_data + blkoffset, xfersize, uio);
/*
* If the buffer is not already filled and we encounter an
* error while trying to fill it, we have to clear out any
* garbage data from the pages instantiated for the buffer.
* If we do not, a failed uiomove() during a write can leave
* the prior contents of the pages exposed to a userland mmap.
*
* Note that we don't need to clear buffers that were
* allocated with the B_CLRBUF flag set.
*/
if (error != 0 && !(flags & B_CLRBUF))
memset(bp->b_data + blkoffset, 0, xfersize);
if (ioflag & IO_NOCACHE)
bp->b_flags |= B_NOCACHE;
if (ioflag & IO_SYNC)
(void)bwrite(bp);
else if (xfersize + blkoffset == fs->fs_bsize) {
bawrite(bp);
} else
bdwrite(bp);
if (error || xfersize == 0)
break;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
}
/*
* If we successfully wrote any data, and we are not the superuser
* we clear the setuid and setgid bits as a precaution against
* tampering.
*/
if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0 &&
!vnoperm(vp))
DIP_ASSIGN(ip, mode, DIP(ip, mode) & ~(ISUID | ISGID));
if (resid > uio->uio_resid)
VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
if (error) {
if (ioflag & IO_UNIT) {
(void)UFS_TRUNCATE(ip, osize,
ioflag & IO_SYNC, ap->a_cred);
uio->uio_offset -= resid - uio->uio_resid;
uio->uio_resid = resid;
}
} else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
error = UFS_UPDATE(ip, 1);
}
/* correct the result for writes clamped by vn_fsizechk() */
uio->uio_resid += overrun;
return (error);
}
/*
* Synch an open file.
*/
int
ffs_fsync(void *v)
{
struct vop_fsync_args *ap = v;
struct vnode *vp = ap->a_vp;
struct buf *bp, *nbp;
int s, error, passes, skipmeta;
if (vp->v_type == VBLK &&
vp->v_specmountpoint != NULL &&
(vp->v_specmountpoint->mnt_flag & MNT_SOFTDEP))
softdep_fsync_mountdev(vp, ap->a_waitfor);
/*
* Flush all dirty buffers associated with a vnode.
*/
passes = NIADDR + 1;
skipmeta = 0;
if (ap->a_waitfor == MNT_WAIT)
skipmeta = 1;
s = splbio();
loop:
LIST_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) {
bp->b_flags &= ~B_SCANNED;
}
LIST_FOREACH_SAFE(bp, &vp->v_dirtyblkhd, b_vnbufs, nbp) {
/*
* Reasons to skip this buffer: it has already been considered
* on this pass, this pass is the first time through on a
* synchronous flush request and the buffer being considered
* is metadata, the buffer has dependencies that will cause
* it to be redirtied and it has not already been deferred,
* or it is already being written.
*/
if (bp->b_flags & (B_BUSY | B_SCANNED))
continue;
if ((bp->b_flags & B_DELWRI) == 0)
panic("ffs_fsync: not dirty");
if (skipmeta && bp->b_lblkno < 0)
continue;
if (ap->a_waitfor != MNT_WAIT &&
LIST_FIRST(&bp->b_dep) != NULL &&
(bp->b_flags & B_DEFERRED) == 0 &&
buf_countdeps(bp, 0, 1)) {
bp->b_flags |= B_DEFERRED;
continue;
}
bremfree(bp);
buf_acquire(bp);
bp->b_flags |= B_SCANNED;
splx(s);
/*
* On our final pass through, do all I/O synchronously
* so that we can find out if our flush is failing
* because of write errors.
*/
if (passes > 0 || ap->a_waitfor != MNT_WAIT)
(void) bawrite(bp);
else if ((error = bwrite(bp)) != 0)
return (error);
s = splbio();
/*
* Since we may have slept during the I/O, we need
* to start from a known point.
*/
nbp = LIST_FIRST(&vp->v_dirtyblkhd);
}
if (skipmeta) {
skipmeta = 0;
goto loop;
}
if (ap->a_waitfor == MNT_WAIT) {
vwaitforio(vp, 0, "ffs_fsync", INFSLP);
/*
* Ensure that any filesystem metadata associated
* with the vnode has been written.
*/
splx(s);
if ((error = softdep_sync_metadata(ap)) != 0)
return (error);
s = splbio();
if (!LIST_EMPTY(&vp->v_dirtyblkhd)) {
/*
* Block devices associated with filesystems may
* have new I/O requests posted for them even if
* the vnode is locked, so no amount of trying will
* get them clean. Thus we give block devices a
* good effort, then just give up. For all other file
* types, go around and try again until it is clean.
*/
if (passes > 0) {
passes -= 1;
goto loop;
}
#ifdef DIAGNOSTIC
if (vp->v_type != VBLK)
vprint("ffs_fsync: dirty", vp);
#endif
}
}
splx(s);
return (UFS_UPDATE(VTOI(vp), ap->a_waitfor == MNT_WAIT));
}
/*
* Reclaim an inode so that it can be used for other purposes.
*/
int
ffs_reclaim(void *v)
{
struct vop_reclaim_args *ap = v;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
int error;
if ((error = ufs_reclaim(vp)) != 0)
return (error);
if (ip->i_din1 != NULL) {
#ifdef FFS2
if (ip->i_ump->um_fstype == UM_UFS2)
pool_put(&ffs_dinode2_pool, ip->i_din2);
else
#endif
pool_put(&ffs_dinode1_pool, ip->i_din1);
}
pool_put(&ffs_ino_pool, ip);
vp->v_data = NULL;
return (0);
}
#ifdef FIFO
int
ffsfifo_reclaim(void *v)
{
fifo_reclaim(v);
return (ffs_reclaim(v));
}
#endif
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
/* $OpenBSD: ipmi.c,v 1.118 2022/04/08 13:13:14 mbuhl Exp $ */
/*
* Copyright (c) 2015 Masao Uebayashi
* Copyright (c) 2005 Jordan Hargrave
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/extent.h>
#include <sys/sensors.h>
#include <sys/malloc.h>
#include <sys/kthread.h>
#include <sys/task.h>
#include <machine/bus.h>
#include <machine/smbiosvar.h>
#include <dev/ipmivar.h>
#include <dev/ipmi.h>
struct ipmi_sensor {
u_int8_t *i_sdr;
int i_num;
int stype;
int etype;
struct ksensor i_sensor;
SLIST_ENTRY(ipmi_sensor) list;
};
int ipmi_enabled = 0;
#define SENSOR_REFRESH_RATE 5 /* seconds */
#define DEVNAME(s) ((s)->sc_dev.dv_xname)
#define IPMI_BTMSG_LEN 0
#define IPMI_BTMSG_NFLN 1
#define IPMI_BTMSG_SEQ 2
#define IPMI_BTMSG_CMD 3
#define IPMI_BTMSG_CCODE 4
#define IPMI_BTMSG_DATASND 4
#define IPMI_BTMSG_DATARCV 5
/* IPMI 2.0, Table 42-3: Sensor Type Codes */
#define IPMI_SENSOR_TYPE_TEMP 0x0101
#define IPMI_SENSOR_TYPE_VOLT 0x0102
#define IPMI_SENSOR_TYPE_CURRENT 0x0103
#define IPMI_SENSOR_TYPE_FAN 0x0104
#define IPMI_SENSOR_TYPE_INTRUSION 0x6F05
#define IPMI_SENSOR_TYPE_PWRSUPPLY 0x6F08
/* IPMI 2.0, Table 43-15: Sensor Unit Type Codes */
#define IPMI_UNIT_TYPE_DEGREE_C 1
#define IPMI_UNIT_TYPE_DEGREE_F 2
#define IPMI_UNIT_TYPE_DEGREE_K 3
#define IPMI_UNIT_TYPE_VOLTS 4
#define IPMI_UNIT_TYPE_AMPS 5
#define IPMI_UNIT_TYPE_WATTS 6
#define IPMI_UNIT_TYPE_RPM 18
#define IPMI_NAME_UNICODE 0x00
#define IPMI_NAME_BCDPLUS 0x01
#define IPMI_NAME_ASCII6BIT 0x02
#define IPMI_NAME_ASCII8BIT 0x03
#define IPMI_ENTITY_PWRSUPPLY 0x0A
#define IPMI_INVALID_SENSOR (1L << 5)
#define IPMI_DISABLED_SENSOR (1L << 6)
#define IPMI_SDR_TYPEFULL 1
#define IPMI_SDR_TYPECOMPACT 2
#define byteof(x) ((x) >> 3)
#define bitof(x) (1L << ((x) & 0x7))
#define TB(b,m) (data[2+byteof(b)] & bitof(b))
#ifdef IPMI_DEBUG
int ipmi_dbg = 0;
#define dbg_printf(lvl, fmt...) \
if (ipmi_dbg >= lvl) \
printf(fmt);
#define dbg_dump(lvl, msg, len, buf) \
if (len && ipmi_dbg >= lvl) \
dumpb(msg, len, (const u_int8_t *)(buf));
#else
#define dbg_printf(lvl, fmt...)
#define dbg_dump(lvl, msg, len, buf)
#endif
long signextend(unsigned long, int);
SLIST_HEAD(ipmi_sensors_head, ipmi_sensor);
struct ipmi_sensors_head ipmi_sensor_list =
SLIST_HEAD_INITIALIZER(ipmi_sensor_list);
void dumpb(const char *, int, const u_int8_t *);
int read_sensor(struct ipmi_softc *, struct ipmi_sensor *);
int add_sdr_sensor(struct ipmi_softc *, u_int8_t *, int);
int get_sdr_partial(struct ipmi_softc *, u_int16_t, u_int16_t,
u_int8_t, u_int8_t, void *, u_int16_t *);
int get_sdr(struct ipmi_softc *, u_int16_t, u_int16_t *);
int ipmi_sendcmd(struct ipmi_cmd *);
int ipmi_recvcmd(struct ipmi_cmd *);
void ipmi_cmd(struct ipmi_cmd *);
void ipmi_cmd_poll(struct ipmi_cmd *);
void ipmi_cmd_wait(struct ipmi_cmd *);
void ipmi_cmd_wait_cb(void *);
int ipmi_watchdog(void *, int);
void ipmi_watchdog_tickle(void *);
void ipmi_watchdog_set(void *);
struct ipmi_softc *ipmilookup(dev_t dev);
int ipmiopen(dev_t, int, int, struct proc *);
int ipmiclose(dev_t, int, int, struct proc *);
int ipmiioctl(dev_t, u_long, caddr_t, int, struct proc *);
long ipow(long, int);
long ipmi_convert(u_int8_t, struct sdrtype1 *, long);
int ipmi_sensor_name(char *, int, u_int8_t, u_int8_t *, int);
/* BMC Helper Functions */
u_int8_t bmc_read(struct ipmi_softc *, int);
void bmc_write(struct ipmi_softc *, int, u_int8_t);
int bmc_io_wait(struct ipmi_softc *, struct ipmi_iowait *);
void bt_buildmsg(struct ipmi_cmd *);
void cmn_buildmsg(struct ipmi_cmd *);
int getbits(u_int8_t *, int, int);
int ipmi_sensor_type(int, int, int, int);
void ipmi_refresh_sensors(struct ipmi_softc *sc);
int ipmi_map_regs(struct ipmi_softc *sc, struct ipmi_attach_args *ia);
void ipmi_unmap_regs(struct ipmi_softc *);
int ipmi_sensor_status(struct ipmi_softc *, struct ipmi_sensor *,
u_int8_t *);
int add_child_sensors(struct ipmi_softc *, u_int8_t *, int, int, int,
int, int, int, const char *);
void ipmi_create_thread(void *);
void ipmi_poll_thread(void *);
int kcs_probe(struct ipmi_softc *);
int kcs_reset(struct ipmi_softc *);
int kcs_sendmsg(struct ipmi_cmd *);
int kcs_recvmsg(struct ipmi_cmd *);
int bt_probe(struct ipmi_softc *);
int bt_reset(struct ipmi_softc *);
int bt_sendmsg(struct ipmi_cmd *);
int bt_recvmsg(struct ipmi_cmd *);
int smic_probe(struct ipmi_softc *);
int smic_reset(struct ipmi_softc *);
int smic_sendmsg(struct ipmi_cmd *);
int smic_recvmsg(struct ipmi_cmd *);
struct ipmi_if kcs_if = {
"KCS",
IPMI_IF_KCS_NREGS,
cmn_buildmsg,
kcs_sendmsg,
kcs_recvmsg,
kcs_reset,
kcs_probe,
IPMI_MSG_DATASND,
IPMI_MSG_DATARCV,
};
struct ipmi_if smic_if = {
"SMIC",
IPMI_IF_SMIC_NREGS,
cmn_buildmsg,
smic_sendmsg,
smic_recvmsg,
smic_reset,
smic_probe,
IPMI_MSG_DATASND,
IPMI_MSG_DATARCV,
};
struct ipmi_if bt_if = {
"BT",
IPMI_IF_BT_NREGS,
bt_buildmsg,
bt_sendmsg,
bt_recvmsg,
bt_reset,
bt_probe,
IPMI_BTMSG_DATASND,
IPMI_BTMSG_DATARCV,
};
struct ipmi_if *ipmi_get_if(int);
struct ipmi_if *
ipmi_get_if(int iftype)
{
switch (iftype) {
case IPMI_IF_KCS:
return (&kcs_if);
case IPMI_IF_SMIC:
return (&smic_if);
case IPMI_IF_BT:
return (&bt_if);
}
return (NULL);
}
/*
* BMC Helper Functions
*/
u_int8_t
bmc_read(struct ipmi_softc *sc, int offset)
{
if (sc->sc_if_iosize == 4)
return (bus_space_read_4(sc->sc_iot, sc->sc_ioh,
offset * sc->sc_if_iospacing));
else
return (bus_space_read_1(sc->sc_iot, sc->sc_ioh,
offset * sc->sc_if_iospacing));
}
void
bmc_write(struct ipmi_softc *sc, int offset, u_int8_t val)
{
if (sc->sc_if_iosize == 4)
bus_space_write_4(sc->sc_iot, sc->sc_ioh,
offset * sc->sc_if_iospacing, val);
else
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
offset * sc->sc_if_iospacing, val);
}
int
bmc_io_wait(struct ipmi_softc *sc, struct ipmi_iowait *a)
{
volatile u_int8_t v;
int count = 5000000; /* == 5s XXX can be shorter */
while (count--) {
v = bmc_read(sc, a->offset);
if ((v & a->mask) == a->value)
return v;
delay(1);
}
dbg_printf(1, "%s: bmc_io_wait fails : *v=%.2x m=%.2x b=%.2x %s\n",
DEVNAME(sc), v, a->mask, a->value, a->lbl);
return (-1);
}
#define RSSA_MASK 0xff
#define LUN_MASK 0x3
#define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & LUN_MASK))
/*
* BT interface
*/
#define _BT_CTRL_REG 0
#define BT_CLR_WR_PTR (1L << 0)
#define BT_CLR_RD_PTR (1L << 1)
#define BT_HOST2BMC_ATN (1L << 2)
#define BT_BMC2HOST_ATN (1L << 3)
#define BT_EVT_ATN (1L << 4)
#define BT_HOST_BUSY (1L << 6)
#define BT_BMC_BUSY (1L << 7)
#define BT_READY (BT_HOST_BUSY|BT_HOST2BMC_ATN|BT_BMC2HOST_ATN)
#define _BT_DATAIN_REG 1
#define _BT_DATAOUT_REG 1
#define _BT_INTMASK_REG 2
#define BT_IM_HIRQ_PEND (1L << 1)
#define BT_IM_SCI_EN (1L << 2)
#define BT_IM_SMI_EN (1L << 3)
#define BT_IM_NMI2SMI (1L << 4)
int bt_read(struct ipmi_softc *, int);
int bt_write(struct ipmi_softc *, int, uint8_t);
int
bt_read(struct ipmi_softc *sc, int reg)
{
return bmc_read(sc, reg);
}
int
bt_write(struct ipmi_softc *sc, int reg, uint8_t data)
{
struct ipmi_iowait a;
a.offset = _BT_CTRL_REG;
a.mask = BT_BMC_BUSY;
a.value = 0;
a.lbl = "bt_write";
if (bmc_io_wait(sc, &a) < 0)
return (-1);
bmc_write(sc, reg, data);
return (0);
}
int
bt_sendmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
struct ipmi_iowait a;
int i;
bt_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR);
for (i = 0; i < c->c_txlen; i++)
bt_write(sc, _BT_DATAOUT_REG, sc->sc_buf[i]);
bt_write(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN);
a.offset = _BT_CTRL_REG;
a.mask = BT_HOST2BMC_ATN | BT_BMC_BUSY;
a.value = 0;
a.lbl = "bt_sendwait";
if (bmc_io_wait(sc, &a) < 0)
return (-1);
return (0);
}
int
bt_recvmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
struct ipmi_iowait a;
u_int8_t len, v, i, j;
a.offset = _BT_CTRL_REG;
a.mask = BT_BMC2HOST_ATN;
a.value = BT_BMC2HOST_ATN;
a.lbl = "bt_recvwait";
if (bmc_io_wait(sc, &a) < 0)
return (-1);
bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
bt_write(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN);
bt_write(sc, _BT_CTRL_REG, BT_CLR_RD_PTR);
len = bt_read(sc, _BT_DATAIN_REG);
for (i = IPMI_BTMSG_NFLN, j = 0; i <= len; i++) {
v = bt_read(sc, _BT_DATAIN_REG);
if (i != IPMI_BTMSG_SEQ)
*(sc->sc_buf + j++) = v;
}
bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
c->c_rxlen = len - 1;
return (0);
}
int
bt_reset(struct ipmi_softc *sc)
{
return (-1);
}
int
bt_probe(struct ipmi_softc *sc)
{
u_int8_t rv;
rv = bmc_read(sc, _BT_CTRL_REG);
rv &= BT_HOST_BUSY;
rv |= BT_CLR_WR_PTR|BT_CLR_RD_PTR|BT_BMC2HOST_ATN|BT_HOST2BMC_ATN;
bmc_write(sc, _BT_CTRL_REG, rv);
rv = bmc_read(sc, _BT_INTMASK_REG);
rv &= BT_IM_SCI_EN|BT_IM_SMI_EN|BT_IM_NMI2SMI;
rv |= BT_IM_HIRQ_PEND;
bmc_write(sc, _BT_INTMASK_REG, rv);
#if 0
printf("bt_probe: %2x\n", v);
printf(" WR : %2x\n", v & BT_CLR_WR_PTR);
printf(" RD : %2x\n", v & BT_CLR_RD_PTR);
printf(" H2B : %2x\n", v & BT_HOST2BMC_ATN);
printf(" B2H : %2x\n", v & BT_BMC2HOST_ATN);
printf(" EVT : %2x\n", v & BT_EVT_ATN);
printf(" HBSY : %2x\n", v & BT_HOST_BUSY);
printf(" BBSY : %2x\n", v & BT_BMC_BUSY);
#endif
return (0);
}
/*
* SMIC interface
*/
#define _SMIC_DATAIN_REG 0
#define _SMIC_DATAOUT_REG 0
#define _SMIC_CTRL_REG 1
#define SMS_CC_GET_STATUS 0x40
#define SMS_CC_START_TRANSFER 0x41
#define SMS_CC_NEXT_TRANSFER 0x42
#define SMS_CC_END_TRANSFER 0x43
#define SMS_CC_START_RECEIVE 0x44
#define SMS_CC_NEXT_RECEIVE 0x45
#define SMS_CC_END_RECEIVE 0x46
#define SMS_CC_TRANSFER_ABORT 0x47
#define SMS_SC_READY 0xc0
#define SMS_SC_WRITE_START 0xc1
#define SMS_SC_WRITE_NEXT 0xc2
#define SMS_SC_WRITE_END 0xc3
#define SMS_SC_READ_START 0xc4
#define SMS_SC_READ_NEXT 0xc5
#define SMS_SC_READ_END 0xc6
#define _SMIC_FLAG_REG 2
#define SMIC_BUSY (1L << 0)
#define SMIC_SMS_ATN (1L << 2)
#define SMIC_EVT_ATN (1L << 3)
#define SMIC_SMI (1L << 4)
#define SMIC_TX_DATA_RDY (1L << 6)
#define SMIC_RX_DATA_RDY (1L << 7)
int smic_wait(struct ipmi_softc *, u_int8_t, u_int8_t, const char *);
int smic_write_cmd_data(struct ipmi_softc *, u_int8_t, const u_int8_t *);
int smic_read_data(struct ipmi_softc *, u_int8_t *);
int
smic_wait(struct ipmi_softc *sc, u_int8_t mask, u_int8_t val, const char *lbl)
{
struct ipmi_iowait a;
int v;
/* Wait for expected flag bits */
a.offset = _SMIC_FLAG_REG;
a.mask = mask;
a.value = val;
a.lbl = "smicwait";
v = bmc_io_wait(sc, &a);
if (v < 0)
return (-1);
/* Return current status */
v = bmc_read(sc, _SMIC_CTRL_REG);
dbg_printf(99, "smic_wait = %.2x\n", v);
return (v);
}
int
smic_write_cmd_data(struct ipmi_softc *sc, u_int8_t cmd, const u_int8_t *data)
{
int sts, v;
dbg_printf(50, "smic_wcd: %.2x %.2x\n", cmd, data ? *data : -1);
sts = smic_wait(sc, SMIC_TX_DATA_RDY | SMIC_BUSY, SMIC_TX_DATA_RDY,
"smic_write_cmd_data ready");
if (sts < 0)
return (sts);
bmc_write(sc, _SMIC_CTRL_REG, cmd);
if (data)
bmc_write(sc, _SMIC_DATAOUT_REG, *data);
/* Toggle BUSY bit, then wait for busy bit to clear */
v = bmc_read(sc, _SMIC_FLAG_REG);
bmc_write(sc, _SMIC_FLAG_REG, v | SMIC_BUSY);
return (smic_wait(sc, SMIC_BUSY, 0, "smic_write_cmd_data busy"));
}
int
smic_read_data(struct ipmi_softc *sc, u_int8_t *data)
{
int sts;
sts = smic_wait(sc, SMIC_RX_DATA_RDY | SMIC_BUSY, SMIC_RX_DATA_RDY,
"smic_read_data");
if (sts >= 0) {
*data = bmc_read(sc, _SMIC_DATAIN_REG);
dbg_printf(50, "smic_readdata: %.2x\n", *data);
}
return (sts);
}
#define ErrStat(a,b) if (a) printf(b);
int
smic_sendmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
int sts, idx;
sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &sc->sc_buf[0]);
ErrStat(sts != SMS_SC_WRITE_START, "wstart");
for (idx = 1; idx < c->c_txlen - 1; idx++) {
sts = smic_write_cmd_data(sc, SMS_CC_NEXT_TRANSFER,
&sc->sc_buf[idx]);
ErrStat(sts != SMS_SC_WRITE_NEXT, "write");
}
sts = smic_write_cmd_data(sc, SMS_CC_END_TRANSFER, &sc->sc_buf[idx]);
if (sts != SMS_SC_WRITE_END) {
dbg_printf(50, "smic_sendmsg %d/%d = %.2x\n", idx, c->c_txlen, sts);
return (-1);
}
return (0);
}
int
smic_recvmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
int sts, idx;
c->c_rxlen = 0;
sts = smic_wait(sc, SMIC_RX_DATA_RDY, SMIC_RX_DATA_RDY, "smic_recvmsg");
if (sts < 0)
return (-1);
sts = smic_write_cmd_data(sc, SMS_CC_START_RECEIVE, NULL);
ErrStat(sts != SMS_SC_READ_START, "rstart");
for (idx = 0;; ) {
sts = smic_read_data(sc, &sc->sc_buf[idx++]);
if (sts != SMS_SC_READ_START && sts != SMS_SC_READ_NEXT)
break;
smic_write_cmd_data(sc, SMS_CC_NEXT_RECEIVE, NULL);
}
ErrStat(sts != SMS_SC_READ_END, "rend");
c->c_rxlen = idx;
sts = smic_write_cmd_data(sc, SMS_CC_END_RECEIVE, NULL);
if (sts != SMS_SC_READY) {
dbg_printf(50, "smic_recvmsg %d/%d = %.2x\n", idx, c->c_maxrxlen, sts);
return (-1);
}
return (0);
}
int
smic_reset(struct ipmi_softc *sc)
{
return (-1);
}
int
smic_probe(struct ipmi_softc *sc)
{
/* Flag register should not be 0xFF on a good system */
if (bmc_read(sc, _SMIC_FLAG_REG) == 0xFF)
return (-1);
return (0);
}
/*
* KCS interface
*/
#define _KCS_DATAIN_REGISTER 0
#define _KCS_DATAOUT_REGISTER 0
#define KCS_READ_NEXT 0x68
#define _KCS_COMMAND_REGISTER 1
#define KCS_GET_STATUS 0x60
#define KCS_WRITE_START 0x61
#define KCS_WRITE_END 0x62
#define _KCS_STATUS_REGISTER 1
#define KCS_OBF (1L << 0)
#define KCS_IBF (1L << 1)
#define KCS_SMS_ATN (1L << 2)
#define KCS_CD (1L << 3)
#define KCS_OEM1 (1L << 4)
#define KCS_OEM2 (1L << 5)
#define KCS_STATE_MASK 0xc0
#define KCS_IDLE_STATE 0x00
#define KCS_READ_STATE 0x40
#define KCS_WRITE_STATE 0x80
#define KCS_ERROR_STATE 0xC0
int kcs_wait(struct ipmi_softc *, u_int8_t, u_int8_t, const char *);
int kcs_write_cmd(struct ipmi_softc *, u_int8_t);
int kcs_write_data(struct ipmi_softc *, u_int8_t);
int kcs_read_data(struct ipmi_softc *, u_int8_t *);
int
kcs_wait(struct ipmi_softc *sc, u_int8_t mask, u_int8_t value, const char *lbl)
{
struct ipmi_iowait a;
int v;
a.offset = _KCS_STATUS_REGISTER;
a.mask = mask;
a.value = value;
a.lbl = lbl;
v = bmc_io_wait(sc, &a);
if (v < 0)
return (v);
/* Check if output buffer full, read dummy byte */
if ((v & (KCS_OBF | KCS_STATE_MASK)) == (KCS_OBF | KCS_WRITE_STATE))
bmc_read(sc, _KCS_DATAIN_REGISTER);
/* Check for error state */
if ((v & KCS_STATE_MASK) == KCS_ERROR_STATE) {
bmc_write(sc, _KCS_COMMAND_REGISTER, KCS_GET_STATUS);
while (bmc_read(sc, _KCS_STATUS_REGISTER) & KCS_IBF)
continue;
printf("%s: error code: %x\n", DEVNAME(sc),
bmc_read(sc, _KCS_DATAIN_REGISTER));
}
return (v & KCS_STATE_MASK);
}
int
kcs_write_cmd(struct ipmi_softc *sc, u_int8_t cmd)
{
/* ASSERT: IBF and OBF are clear */
dbg_printf(50, "kcswritecmd: %.2x\n", cmd);
bmc_write(sc, _KCS_COMMAND_REGISTER, cmd);
return (kcs_wait(sc, KCS_IBF, 0, "write_cmd"));
}
int
kcs_write_data(struct ipmi_softc *sc, u_int8_t data)
{
/* ASSERT: IBF and OBF are clear */
dbg_printf(50, "kcswritedata: %.2x\n", data);
bmc_write(sc, _KCS_DATAOUT_REGISTER, data);
return (kcs_wait(sc, KCS_IBF, 0, "write_data"));
}
int
kcs_read_data(struct ipmi_softc *sc, u_int8_t * data)
{
int sts;
sts = kcs_wait(sc, KCS_IBF | KCS_OBF, KCS_OBF, "read_data");
if (sts != KCS_READ_STATE)
return (sts);
/* ASSERT: OBF is set read data, request next byte */
*data = bmc_read(sc, _KCS_DATAIN_REGISTER);
bmc_write(sc, _KCS_DATAOUT_REGISTER, KCS_READ_NEXT);
dbg_printf(50, "kcsreaddata: %.2x\n", *data);
return (sts);
}
/* Exported KCS functions */
int
kcs_sendmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
int idx, sts;
/* ASSERT: IBF is clear */
dbg_dump(50, "kcs sendmsg", c->c_txlen, sc->sc_buf);
sts = kcs_write_cmd(sc, KCS_WRITE_START);
for (idx = 0; idx < c->c_txlen; idx++) {
if (idx == c->c_txlen - 1)
sts = kcs_write_cmd(sc, KCS_WRITE_END);
if (sts != KCS_WRITE_STATE)
break;
sts = kcs_write_data(sc, sc->sc_buf[idx]);
}
if (sts != KCS_READ_STATE) {
dbg_printf(1, "kcs sendmsg = %d/%d <%.2x>\n", idx, c->c_txlen, sts);
dbg_dump(1, "kcs_sendmsg", c->c_txlen, sc->sc_buf);
return (-1);
}
return (0);
}
int
kcs_recvmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
int idx, sts;
for (idx = 0; idx < c->c_maxrxlen; idx++) {
sts = kcs_read_data(sc, &sc->sc_buf[idx]);
if (sts != KCS_READ_STATE)
break;
}
sts = kcs_wait(sc, KCS_IBF, 0, "recv");
c->c_rxlen = idx;
if (sts != KCS_IDLE_STATE) {
dbg_printf(1, "kcs recvmsg = %d/%d <%.2x>\n", idx, c->c_maxrxlen, sts);
return (-1);
}
dbg_dump(50, "kcs recvmsg", idx, sc->sc_buf);
return (0);
}
int
kcs_reset(struct ipmi_softc *sc)
{
return (-1);
}
int
kcs_probe(struct ipmi_softc *sc)
{
u_int8_t v;
v = bmc_read(sc, _KCS_STATUS_REGISTER);
if ((v & KCS_STATE_MASK) == KCS_ERROR_STATE)
return (1);
#if 0
printf("kcs_probe: %2x\n", v);
printf(" STS: %2x\n", v & KCS_STATE_MASK);
printf(" ATN: %2x\n", v & KCS_SMS_ATN);
printf(" C/D: %2x\n", v & KCS_CD);
printf(" IBF: %2x\n", v & KCS_IBF);
printf(" OBF: %2x\n", v & KCS_OBF);
#endif
return (0);
}
/*
* IPMI code
*/
#define READ_SMS_BUFFER 0x37
#define WRITE_I2C 0x50
#define GET_MESSAGE_CMD 0x33
#define SEND_MESSAGE_CMD 0x34
#define IPMB_CHANNEL_NUMBER 0
#define PUBLIC_BUS 0
#define MIN_I2C_PACKET_SIZE 3
#define MIN_IMB_PACKET_SIZE 7 /* one byte for cksum */
#define MIN_BTBMC_REQ_SIZE 4
#define MIN_BTBMC_RSP_SIZE 5
#define MIN_BMC_REQ_SIZE 2
#define MIN_BMC_RSP_SIZE 3
#define BMC_SA 0x20 /* BMC/ESM3 */
#define FPC_SA 0x22 /* front panel */
#define BP_SA 0xC0 /* Primary Backplane */
#define BP2_SA 0xC2 /* Secondary Backplane */
#define PBP_SA 0xC4 /* Peripheral Backplane */
#define DRAC_SA 0x28 /* DRAC-III */
#define DRAC3_SA 0x30 /* DRAC-III */
#define BMC_LUN 0
#define SMS_LUN 2
struct ipmi_request {
u_int8_t rsSa;
u_int8_t rsLun;
u_int8_t netFn;
u_int8_t cmd;
u_int8_t data_len;
u_int8_t *data;
};
struct ipmi_response {
u_int8_t cCode;
u_int8_t data_len;
u_int8_t *data;
};
struct ipmi_bmc_request {
u_int8_t bmc_nfLn;
u_int8_t bmc_cmd;
u_int8_t bmc_data_len;
u_int8_t bmc_data[1];
};
struct ipmi_bmc_response {
u_int8_t bmc_nfLn;
u_int8_t bmc_cmd;
u_int8_t bmc_cCode;
u_int8_t bmc_data_len;
u_int8_t bmc_data[1];
};
struct cfdriver ipmi_cd = {
NULL, "ipmi", DV_DULL
};
void
dumpb(const char *lbl, int len, const u_int8_t *data)
{
int idx;
printf("%s: ", lbl);
for (idx = 0; idx < len; idx++)
printf("%.2x ", data[idx]);
printf("\n");
}
/*
* bt_buildmsg builds an IPMI message from a nfLun, cmd, and data
* This is used by BT protocol
*/
void
bt_buildmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
u_int8_t *buf = sc->sc_buf;
buf[IPMI_BTMSG_LEN] = c->c_txlen + (IPMI_BTMSG_DATASND - 1);
buf[IPMI_BTMSG_NFLN] = NETFN_LUN(c->c_netfn, c->c_rslun);
buf[IPMI_BTMSG_SEQ] = sc->sc_btseq++;
buf[IPMI_BTMSG_CMD] = c->c_cmd;
if (c->c_txlen && c->c_data)
memcpy(buf + IPMI_BTMSG_DATASND, c->c_data, c->c_txlen);
}
/*
* cmn_buildmsg builds an IPMI message from a nfLun, cmd, and data
* This is used by both SMIC and KCS protocols
*/
void
cmn_buildmsg(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
u_int8_t *buf = sc->sc_buf;
buf[IPMI_MSG_NFLN] = NETFN_LUN(c->c_netfn, c->c_rslun);
buf[IPMI_MSG_CMD] = c->c_cmd;
if (c->c_txlen && c->c_data)
memcpy(buf + IPMI_MSG_DATASND, c->c_data, c->c_txlen);
}
/* Send an IPMI command */
int
ipmi_sendcmd(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
int rc = -1;
dbg_printf(50, "ipmi_sendcmd: rssa=%.2x nfln=%.2x cmd=%.2x len=%.2x\n",
c->c_rssa, NETFN_LUN(c->c_netfn, c->c_rslun), c->c_cmd, c->c_txlen);
dbg_dump(10, " send", c->c_txlen, c->c_data);
if (c->c_rssa != BMC_SA) {
#if 0
sc->sc_if->buildmsg(c);
pI2C->bus = (sc->if_ver == 0x09) ?
PUBLIC_BUS :
IPMB_CHANNEL_NUMBER;
imbreq->rsSa = rssa;
imbreq->nfLn = NETFN_LUN(netfn, rslun);
imbreq->cSum1 = -(imbreq->rsSa + imbreq->nfLn);
imbreq->rqSa = BMC_SA;
imbreq->seqLn = NETFN_LUN(sc->imb_seq++, SMS_LUN);
imbreq->cmd = cmd;
if (txlen)
memcpy(imbreq->data, data, txlen);
/* Set message checksum */
imbreq->data[txlen] = cksum8(&imbreq->rqSa, txlen + 3);
#endif
goto done;
} else
sc->sc_if->buildmsg(c);
c->c_txlen += sc->sc_if->datasnd;
rc = sc->sc_if->sendmsg(c);
done:
return (rc);
}
/* Receive an IPMI command */
int
ipmi_recvcmd(struct ipmi_cmd *c)
{
struct ipmi_softc *sc = c->c_sc;
u_int8_t *buf = sc->sc_buf, rc = 0;
/* Receive message from interface, copy out result data */
c->c_maxrxlen += sc->sc_if->datarcv;
if (sc->sc_if->recvmsg(c) ||
c->c_rxlen < sc->sc_if->datarcv) {
return (-1);
}
c->c_rxlen -= sc->sc_if->datarcv;
if (c->c_rxlen > 0 && c->c_data)
memcpy(c->c_data, buf + sc->sc_if->datarcv, c->c_rxlen);
rc = buf[IPMI_MSG_CCODE];
#ifdef IPMI_DEBUG
if (rc != 0)
dbg_printf(1, "ipmi_recvcmd: nfln=%.2x cmd=%.2x err=%.2x\n",
buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE]);
#endif
dbg_printf(50, "ipmi_recvcmd: nfln=%.2x cmd=%.2x err=%.2x len=%.2x\n",
buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE],
c->c_rxlen);
dbg_dump(10, " recv", c->c_rxlen, c->c_data);
return (rc);
}
void
ipmi_cmd(struct ipmi_cmd *c)
{
if (cold || panicstr != NULL)
ipmi_cmd_poll(c);
else
ipmi_cmd_wait(c);
}
void
ipmi_cmd_poll(struct ipmi_cmd *c)
{
if ((c->c_ccode = ipmi_sendcmd(c)))
printf("%s: sendcmd fails\n", DEVNAME(c->c_sc));
else
c->c_ccode = ipmi_recvcmd(c);
}
void
ipmi_cmd_wait(struct ipmi_cmd *c)
{
struct task t;
int res;
task_set(&t, ipmi_cmd_wait_cb, c);
res = task_add(c->c_sc->sc_cmd_taskq, &t);
KASSERT(res == 1);
tsleep_nsec(c, PWAIT, "ipmicmd", INFSLP);
res = task_del(c->c_sc->sc_cmd_taskq, &t);
KASSERT(res == 0);
}
void
ipmi_cmd_wait_cb(void *arg)
{
struct ipmi_cmd *c = arg;
ipmi_cmd_poll(c);
wakeup(c);
}
/* Read a partial SDR entry */
int
get_sdr_partial(struct ipmi_softc *sc, u_int16_t recordId, u_int16_t reserveId,
u_int8_t offset, u_int8_t length, void *buffer, u_int16_t *nxtRecordId)
{
u_int8_t cmd[IPMI_GET_WDOG_MAX + 255]; /* 8 + max of length */
int len;
((u_int16_t *) cmd)[0] = reserveId;
((u_int16_t *) cmd)[1] = recordId;
cmd[4] = offset;
cmd[5] = length;
struct ipmi_cmd c;
c.c_sc = sc;
c.c_rssa = BMC_SA;
c.c_rslun = BMC_LUN;
c.c_netfn = STORAGE_NETFN;
c.c_cmd = STORAGE_GET_SDR;
c.c_txlen = IPMI_SET_WDOG_MAX;
c.c_rxlen = 0;
c.c_maxrxlen = 8 + length;
c.c_data = cmd;
ipmi_cmd(&c);
len = c.c_rxlen;
if (nxtRecordId)
*nxtRecordId = *(uint16_t *) cmd;
if (len > 2)
memcpy(buffer, cmd + 2, len - 2);
else
return (1);
return (0);
}
int maxsdrlen = 0x10;
/* Read an entire SDR; pass to add sensor */
int
get_sdr(struct ipmi_softc *sc, u_int16_t recid, u_int16_t *nxtrec)
{
u_int16_t resid = 0;
int len, sdrlen, offset;
u_int8_t *psdr;
struct sdrhdr shdr;
/* Reserve SDR */
struct ipmi_cmd c;
c.c_sc = sc;
c.c_rssa = BMC_SA;
c.c_rslun = BMC_LUN;
c.c_netfn = STORAGE_NETFN;
c.c_cmd = STORAGE_RESERVE_SDR;
c.c_txlen = 0;
c.c_maxrxlen = sizeof(resid);
c.c_rxlen = 0;
c.c_data = &resid;
ipmi_cmd(&c);
/* Get SDR Header */
if (get_sdr_partial(sc, recid, resid, 0, sizeof shdr, &shdr, nxtrec)) {
printf("%s: get header fails\n", DEVNAME(sc));
return (1);
}
/* Allocate space for entire SDR Length of SDR in header does not
* include header length */
sdrlen = sizeof(shdr) + shdr.record_length;
psdr = malloc(sdrlen, M_DEVBUF, M_NOWAIT);
if (psdr == NULL)
return (1);
memcpy(psdr, &shdr, sizeof(shdr));
/* Read SDR Data maxsdrlen bytes at a time */
for (offset = sizeof(shdr); offset < sdrlen; offset += maxsdrlen) {
len = sdrlen - offset;
if (len > maxsdrlen)
len = maxsdrlen;
if (get_sdr_partial(sc, recid, resid, offset, len,
psdr + offset, NULL)) {
printf("%s: get chunk: %d,%d fails\n", DEVNAME(sc),
offset, len);
free(psdr, M_DEVBUF, sdrlen);
return (1);
}
}
/* Add SDR to sensor list, if not wanted, free buffer */
if (add_sdr_sensor(sc, psdr, sdrlen) == 0)
free(psdr, M_DEVBUF, sdrlen);
return (0);
}
int
getbits(u_int8_t *bytes, int bitpos, int bitlen)
{
int v;
int mask;
bitpos += bitlen - 1;
for (v = 0; bitlen--;) {
v <<= 1;
mask = 1L << (bitpos & 7);
if (bytes[bitpos >> 3] & mask)
v |= 1;
bitpos--;
}
return (v);
}
/* Decode IPMI sensor name */
int
ipmi_sensor_name(char *name, int len, u_int8_t typelen, u_int8_t *bits,
int bitslen)
{
int i, slen;
char bcdplus[] = "0123456789 -.:,_";
slen = typelen & 0x1F;
switch (typelen >> 6) {
case IPMI_NAME_UNICODE:
//unicode
break;
case IPMI_NAME_BCDPLUS:
/* Characters are encoded in 4-bit BCDPLUS */
if (len < slen * 2 + 1)
slen = (len >> 1) - 1;
if (slen > bitslen)
return (0);
for (i = 0; i < slen; i++) {
*(name++) = bcdplus[bits[i] >> 4];
*(name++) = bcdplus[bits[i] & 0xF];
}
break;
case IPMI_NAME_ASCII6BIT:
/* Characters are encoded in 6-bit ASCII
* 0x00 - 0x3F maps to 0x20 - 0x5F */
/* XXX: need to calculate max len: slen = 3/4 * len */
if (len < slen + 1)
slen = len - 1;
if (slen * 6 / 8 > bitslen)
return (0);
for (i = 0; i < slen * 8; i += 6) {
*(name++) = getbits(bits, i, 6) + ' ';
}
break;
case IPMI_NAME_ASCII8BIT:
/* Characters are 8-bit ascii */
if (len < slen + 1)
slen = len - 1;
if (slen > bitslen)
return (0);
while (slen--)
*(name++) = *(bits++);
break;
}
*name = 0;
return (1);
}
/* Calculate val * 10^exp */
long
ipow(long val, int exp)
{
while (exp > 0) {
val *= 10;
exp--;
}
while (exp < 0) {
val /= 10;
exp++;
}
return (val);
}
/* Sign extend a n-bit value */
long
signextend(unsigned long val, int bits)
{
long msk = (1L << (bits-1))-1;
return (-(val & ~msk) | val);
}
/* Convert IPMI reading from sensor factors */
long
ipmi_convert(u_int8_t v, struct sdrtype1 *s1, long adj)
{
int16_t M, B;
int8_t K1, K2;
long val;
/* Calculate linear reading variables */
M = signextend((((short)(s1->m_tolerance & 0xC0)) << 2) + s1->m, 10);
B = signextend((((short)(s1->b_accuracy & 0xC0)) << 2) + s1->b, 10);
K1 = signextend(s1->rbexp & 0xF, 4);
K2 = signextend(s1->rbexp >> 4, 4);
/* Calculate sensor reading:
* y = L((M * v + (B * 10^K1)) * 10^(K2+adj)
*
* This commutes out to:
* y = L(M*v * 10^(K2+adj) + B * 10^(K1+K2+adj)); */
val = ipow(M * v, K2 + adj) + ipow(B, K1 + K2 + adj);
/* Linearization function: y = f(x) 0 : y = x 1 : y = ln(x) 2 : y =
* log10(x) 3 : y = log2(x) 4 : y = e^x 5 : y = 10^x 6 : y = 2^x 7 : y
* = 1/x 8 : y = x^2 9 : y = x^3 10 : y = square root(x) 11 : y = cube
* root(x) */
return (val);
}
int
ipmi_sensor_status(struct ipmi_softc *sc, struct ipmi_sensor *psensor,
u_int8_t *reading)
{
struct sdrtype1 *s1 = (struct sdrtype1 *)psensor->i_sdr;
int etype;
/* Get reading of sensor */
switch (psensor->i_sensor.type) {
case SENSOR_TEMP:
psensor->i_sensor.value = ipmi_convert(reading[0], s1, 6);
psensor->i_sensor.value += 273150000;
break;
case SENSOR_VOLTS_DC:
case SENSOR_VOLTS_AC:
case SENSOR_AMPS:
case SENSOR_WATTS:
psensor->i_sensor.value = ipmi_convert(reading[0], s1, 6);
break;
case SENSOR_FANRPM:
psensor->i_sensor.value = ipmi_convert(reading[0], s1, 0);
if (((s1->units1>>3)&0x7) == 0x3)
psensor->i_sensor.value *= 60; // RPS -> RPM
break;
default:
break;
}
/* Return Sensor Status */
etype = (psensor->etype << 8) + psensor->stype;
switch (etype) {
case IPMI_SENSOR_TYPE_TEMP:
case IPMI_SENSOR_TYPE_VOLT:
case IPMI_SENSOR_TYPE_CURRENT:
case IPMI_SENSOR_TYPE_FAN:
/* non-recoverable threshold */
if (reading[2] & ((1 << 5) | (1 << 2)))
return (SENSOR_S_CRIT);
/* critical threshold */
else if (reading[2] & ((1 << 4) | (1 << 1)))
return (SENSOR_S_CRIT);
/* non-critical threshold */
else if (reading[2] & ((1 << 3) | (1 << 0)))
return (SENSOR_S_WARN);
break;
case IPMI_SENSOR_TYPE_INTRUSION:
psensor->i_sensor.value = (reading[2] & 1) ? 1 : 0;
if (reading[2] & 0x1)
return (SENSOR_S_CRIT);
break;
case IPMI_SENSOR_TYPE_PWRSUPPLY:
/* Reading: 1 = present+powered, 0 = otherwise */
psensor->i_sensor.value = (reading[2] & 1) ? 1 : 0;
if (reading[2] & 0x10) {
/* XXX: Need sysctl type for Power Supply types
* ok: power supply installed && powered
* warn: power supply installed && !powered
* crit: power supply !installed
*/
return (SENSOR_S_CRIT);
}
if (reading[2] & 0x08) {
/* Power supply AC lost */
return (SENSOR_S_WARN);
}
break;
}
return (SENSOR_S_OK);
}
int
read_sensor(struct ipmi_softc *sc, struct ipmi_sensor *psensor)
{
struct sdrtype1 *s1 = (struct sdrtype1 *) psensor->i_sdr;
u_int8_t data[8];
int rv = -1;
memset(data, 0, sizeof(data));
data[0] = psensor->i_num;
struct ipmi_cmd c;
c.c_sc = sc;
c.c_rssa = s1->owner_id;
c.c_rslun = s1->owner_lun;
c.c_netfn = SE_NETFN;
c.c_cmd = SE_GET_SENSOR_READING;
c.c_txlen = 1;
c.c_maxrxlen = sizeof(data);
c.c_rxlen = 0;
c.c_data = data;
ipmi_cmd(&c);
if (c.c_ccode != 0) {
dbg_printf(1, "sensor reading command for %s failed: %.2x\n",
psensor->i_sensor.desc, c.c_ccode);
return (rv);
}
dbg_printf(10, "values=%.2x %.2x %.2x %.2x %s\n",
data[0],data[1],data[2],data[3], psensor->i_sensor.desc);
psensor->i_sensor.flags &= ~SENSOR_FINVALID;
if ((data[1] & IPMI_INVALID_SENSOR) ||
((data[1] & IPMI_DISABLED_SENSOR) == 0 && data[0] == 0))
psensor->i_sensor.flags |= SENSOR_FINVALID;
psensor->i_sensor.status = ipmi_sensor_status(sc, psensor, data);
rv = 0;
return (rv);
}
int
ipmi_sensor_type(int type, int ext_type, int units2, int entity)
{
switch (units2) {
case IPMI_UNIT_TYPE_AMPS:
return (SENSOR_AMPS);
case IPMI_UNIT_TYPE_RPM:
return (SENSOR_FANRPM);
/* XXX sensors framework distinguishes AC/DC but ipmi does not */
case IPMI_UNIT_TYPE_VOLTS:
return (SENSOR_VOLTS_DC);
case IPMI_UNIT_TYPE_WATTS:
return (SENSOR_WATTS);
}
switch (ext_type << 8L | type) {
case IPMI_SENSOR_TYPE_TEMP:
return (SENSOR_TEMP);
case IPMI_SENSOR_TYPE_PWRSUPPLY:
if (entity == IPMI_ENTITY_PWRSUPPLY)
return (SENSOR_INDICATOR);
break;
case IPMI_SENSOR_TYPE_INTRUSION:
return (SENSOR_INDICATOR);
}
return (-1);
}
/* Add Sensor to BSD Sysctl interface */
int
add_sdr_sensor(struct ipmi_softc *sc, u_int8_t *psdr, int sdrlen)
{
int rc;
struct sdrtype1 *s1 = (struct sdrtype1 *)psdr;
struct sdrtype2 *s2 = (struct sdrtype2 *)psdr;
char name[64];
switch (s1->sdrhdr.record_type) {
case IPMI_SDR_TYPEFULL:
rc = ipmi_sensor_name(name, sizeof(name), s1->typelen,
s1->name, sdrlen - (int)offsetof(struct sdrtype1, name));
if (rc == 0)
return (0);
rc = add_child_sensors(sc, psdr, 1, s1->sensor_num,
s1->sensor_type, s1->event_code, 0, s1->entity_id, name);
break;
case IPMI_SDR_TYPECOMPACT:
rc = ipmi_sensor_name(name, sizeof(name), s2->typelen,
s2->name, sdrlen - (int)offsetof(struct sdrtype2, name));
if (rc == 0)
return (0);
rc = add_child_sensors(sc, psdr, s2->share1 & 0xF,
s2->sensor_num, s2->sensor_type, s2->event_code,
s2->share2 & 0x7F, s2->entity_id, name);
break;
default:
return (0);
}
return rc;
}
int
add_child_sensors(struct ipmi_softc *sc, u_int8_t *psdr, int count,
int sensor_num, int sensor_type, int ext_type, int sensor_base,
int entity, const char *name)
{
int typ, idx, rc = 0;
struct ipmi_sensor *psensor;
struct sdrtype1 *s1 = (struct sdrtype1 *)psdr;
typ = ipmi_sensor_type(sensor_type, ext_type, s1->units2, entity);
if (typ == -1) {
dbg_printf(5, "Unknown sensor type:%.2x et:%.2x sn:%.2x "
"units2:%u name:%s\n", sensor_type, ext_type, sensor_num,
s1->units2, name);
return 0;
}
for (idx = 0; idx < count; idx++) {
psensor = malloc(sizeof(*psensor), M_DEVBUF, M_NOWAIT | M_ZERO);
if (psensor == NULL)
break;
/* Initialize BSD Sensor info */
psensor->i_sdr = psdr;
psensor->i_num = sensor_num + idx;
psensor->stype = sensor_type;
psensor->etype = ext_type;
psensor->i_sensor.type = typ;
if (count > 1)
snprintf(psensor->i_sensor.desc,
sizeof(psensor->i_sensor.desc),
"%s - %d", name, sensor_base + idx);
else
strlcpy(psensor->i_sensor.desc, name,
sizeof(psensor->i_sensor.desc));
dbg_printf(5, "add sensor:%.4x %.2x:%d ent:%.2x:%.2x %s\n",
s1->sdrhdr.record_id, s1->sensor_type,
typ, s1->entity_id, s1->entity_instance,
psensor->i_sensor.desc);
if (read_sensor(sc, psensor) == 0) {
SLIST_INSERT_HEAD(&ipmi_sensor_list, psensor, list);
sensor_attach(&sc->sc_sensordev, &psensor->i_sensor);
dbg_printf(5, " reading: %lld [%s]\n",
psensor->i_sensor.value,
psensor->i_sensor.desc);
rc = 1;
} else
free(psensor, M_DEVBUF, sizeof(*psensor));
}
return (rc);
}
/* Handle IPMI Timer - reread sensor values */
void
ipmi_refresh_sensors(struct ipmi_softc *sc)
{
if (SLIST_EMPTY(&ipmi_sensor_list))
return;
sc->current_sensor = SLIST_NEXT(sc->current_sensor, list);
if (sc->current_sensor == NULL)
sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list);
if (read_sensor(sc, sc->current_sensor)) {
dbg_printf(1, "%s: error reading: %s\n", DEVNAME(sc),
sc->current_sensor->i_sensor.desc);
return;
}
}
int
ipmi_map_regs(struct ipmi_softc *sc, struct ipmi_attach_args *ia)
{
if (sc->sc_if && sc->sc_if->nregs == 0)
return (0);
sc->sc_if = ipmi_get_if(ia->iaa_if_type);
if (sc->sc_if == NULL)
return (-1);
if (ia->iaa_if_iotype == 'i')
sc->sc_iot = ia->iaa_iot;
else
sc->sc_iot = ia->iaa_memt;
sc->sc_if_rev = ia->iaa_if_rev;
sc->sc_if_iosize = ia->iaa_if_iosize;
sc->sc_if_iospacing = ia->iaa_if_iospacing;
if (bus_space_map(sc->sc_iot, ia->iaa_if_iobase,
sc->sc_if->nregs * sc->sc_if_iospacing,
0, &sc->sc_ioh)) {
printf("%s: bus_space_map(%lx %lx %x 0 %p) failed\n",
DEVNAME(sc),
(unsigned long)sc->sc_iot, ia->iaa_if_iobase,
sc->sc_if->nregs * sc->sc_if_iospacing, &sc->sc_ioh);
return (-1);
}
return (0);
}
void
ipmi_unmap_regs(struct ipmi_softc *sc)
{
if (sc->sc_if->nregs > 0) {
bus_space_unmap(sc->sc_iot, sc->sc_ioh,
sc->sc_if->nregs * sc->sc_if_iospacing);
}
}
void
ipmi_poll_thread(void *arg)
{
struct ipmi_thread *thread = arg;
struct ipmi_softc *sc = thread->sc;
u_int16_t rec;
/* Scan SDRs, add sensors */
for (rec = 0; rec != 0xFFFF;) {
if (get_sdr(sc, rec, &rec)) {
ipmi_unmap_regs(sc);
printf("%s: no SDRs IPMI disabled\n", DEVNAME(sc));
goto done;
}
tsleep_nsec(sc, PWAIT, "ipmirun", MSEC_TO_NSEC(1));
}
/* initialize sensor list for thread */
if (SLIST_EMPTY(&ipmi_sensor_list))
goto done;
else
sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list);
strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sc_sensordev.xname));
sensordev_install(&sc->sc_sensordev);
while (thread->running) {
ipmi_refresh_sensors(sc);
tsleep_nsec(thread, PWAIT, "ipmi_poll",
SEC_TO_NSEC(SENSOR_REFRESH_RATE));
}
done:
kthread_exit(0);
}
void
ipmi_create_thread(void *arg)
{
struct ipmi_softc *sc = arg;
if (kthread_create(ipmi_poll_thread, sc->sc_thread, NULL,
DEVNAME(sc)) != 0) {
printf("%s: unable to create run thread, ipmi disabled\n",
DEVNAME(sc));
return;
}
}
void
ipmi_attach_common(struct ipmi_softc *sc, struct ipmi_attach_args *ia)
{
struct ipmi_cmd *c = &sc->sc_ioctl.cmd;
/* Map registers */
ipmi_map_regs(sc, ia);
sc->sc_thread = malloc(sizeof(struct ipmi_thread), M_DEVBUF, M_NOWAIT);
if (sc->sc_thread == NULL) {
printf(": unable to allocate thread\n");
return;
}
sc->sc_thread->sc = sc;
sc->sc_thread->running = 1;
/* Setup threads */
kthread_create_deferred(ipmi_create_thread, sc);
printf(": version %d.%d interface %s",
ia->iaa_if_rev >> 4, ia->iaa_if_rev & 0xF, sc->sc_if->name);
if (sc->sc_if->nregs > 0)
printf(" %sbase 0x%lx/%x spacing %d",
ia->iaa_if_iotype == 'i' ? "io" : "mem", ia->iaa_if_iobase,
ia->iaa_if_iospacing * sc->sc_if->nregs,
ia->iaa_if_iospacing);
if (ia->iaa_if_irq != -1)
printf(" irq %d", ia->iaa_if_irq);
printf("\n");
/* setup flag to exclude iic */
ipmi_enabled = 1;
/* Setup Watchdog timer */
sc->sc_wdog_period = 0;
task_set(&sc->sc_wdog_tickle_task, ipmi_watchdog_tickle, sc);
wdog_register(ipmi_watchdog, sc);
rw_init(&sc->sc_ioctl.lock, DEVNAME(sc));
sc->sc_ioctl.req.msgid = -1;
c->c_sc = sc;
c->c_ccode = -1;
sc->sc_cmd_taskq = taskq_create("ipmicmd", 1, IPL_NONE, TASKQ_MPSAFE);
}
int
ipmi_activate(struct device *self, int act)
{
switch (act) {
case DVACT_POWERDOWN:
wdog_shutdown(self);
break;
}
return (0);
}
struct ipmi_softc *
ipmilookup(dev_t dev)
{
return (struct ipmi_softc *)device_lookup(&ipmi_cd, minor(dev));
}
int
ipmiopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct ipmi_softc *sc = ipmilookup(dev);
if (sc == NULL)
return (ENXIO);
return (0);
}
int
ipmiclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct ipmi_softc *sc = ipmilookup(dev);
if (sc == NULL)
return (ENXIO);
return (0);
}
int
ipmiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *proc)
{
struct ipmi_softc *sc = ipmilookup(dev);
struct ipmi_req *req = (struct ipmi_req *)data;
struct ipmi_recv *recv = (struct ipmi_recv *)data;
struct ipmi_cmd *c = &sc->sc_ioctl.cmd;
int iv;
int len;
u_char ccode;
int rc = 0;
if (sc == NULL)
return (ENXIO);
rw_enter_write(&sc->sc_ioctl.lock);
c->c_maxrxlen = sizeof(sc->sc_ioctl.buf);
c->c_data = sc->sc_ioctl.buf;
switch (cmd) {
case IPMICTL_SEND_COMMAND:
if (req->msgid == -1) {
rc = EINVAL;
goto reset;
}
if (sc->sc_ioctl.req.msgid != -1) {
rc = EBUSY;
goto reset;
}
len = req->msg.data_len;
if (len < 0) {
rc = EINVAL;
goto reset;
}
if (len > c->c_maxrxlen) {
rc = E2BIG;
goto reset;
}
sc->sc_ioctl.req = *req;
c->c_ccode = -1;
rc = copyin(req->msg.data, c->c_data, len);
if (rc != 0)
goto reset;
KASSERT(c->c_ccode == -1);
/* Execute a command synchronously. */
c->c_netfn = req->msg.netfn;
c->c_cmd = req->msg.cmd;
c->c_txlen = req->msg.data_len;
c->c_rxlen = 0;
ipmi_cmd(c);
break;
case IPMICTL_RECEIVE_MSG_TRUNC:
case IPMICTL_RECEIVE_MSG:
if (sc->sc_ioctl.req.msgid == -1) {
rc = EINVAL;
goto reset;
}
if (c->c_ccode == -1) {
rc = EAGAIN;
goto reset;
}
ccode = c->c_ccode & 0xff;
rc = copyout(&ccode, recv->msg.data, 1);
if (rc != 0)
goto reset;
/* Return a command result. */
recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
recv->msgid = sc->sc_ioctl.req.msgid;
recv->msg.netfn = sc->sc_ioctl.req.msg.netfn;
recv->msg.cmd = sc->sc_ioctl.req.msg.cmd;
recv->msg.data_len = c->c_rxlen + 1;
rc = copyout(c->c_data, recv->msg.data + 1, c->c_rxlen);
/* Always reset state after command completion. */
goto reset;
case IPMICTL_SET_MY_ADDRESS_CMD:
iv = *(int *)data;
if (iv < 0 || iv > RSSA_MASK) {
rc = EINVAL;
goto reset;
}
c->c_rssa = iv;
break;
case IPMICTL_GET_MY_ADDRESS_CMD:
*(int *)data = c->c_rssa;
break;
case IPMICTL_SET_MY_LUN_CMD:
iv = *(int *)data;
if (iv < 0 || iv > LUN_MASK) {
rc = EINVAL;
goto reset;
}
c->c_rslun = iv;
break;
case IPMICTL_GET_MY_LUN_CMD:
*(int *)data = c->c_rslun;
break;
case IPMICTL_SET_GETS_EVENTS_CMD:
break;
case IPMICTL_REGISTER_FOR_CMD:
case IPMICTL_UNREGISTER_FOR_CMD:
default:
break;
}
done:
rw_exit_write(&sc->sc_ioctl.lock);
return (rc);
reset:
sc->sc_ioctl.req.msgid = -1;
c->c_ccode = -1;
goto done;
}
#define MIN_PERIOD 10
int
ipmi_watchdog(void *arg, int period)
{
struct ipmi_softc *sc = arg;
if (sc->sc_wdog_period == period) {
if (period != 0) {
struct task *t;
int res;
t = &sc->sc_wdog_tickle_task;
(void)task_del(systq, t);
res = task_add(systq, t);
KASSERT(res == 1);
}
return (period);
}
if (period < MIN_PERIOD && period > 0)
period = MIN_PERIOD;
sc->sc_wdog_period = period;
ipmi_watchdog_set(sc);
printf("%s: watchdog %sabled\n", DEVNAME(sc),
(period == 0) ? "dis" : "en");
return (period);
}
void
ipmi_watchdog_tickle(void *arg)
{
struct ipmi_softc *sc = arg;
struct ipmi_cmd c;
c.c_sc = sc;
c.c_rssa = BMC_SA;
c.c_rslun = BMC_LUN;
c.c_netfn = APP_NETFN;
c.c_cmd = APP_RESET_WATCHDOG;
c.c_txlen = 0;
c.c_maxrxlen = 0;
c.c_rxlen = 0;
c.c_data = NULL;
ipmi_cmd(&c);
}
void
ipmi_watchdog_set(void *arg)
{
struct ipmi_softc *sc = arg;
uint8_t wdog[IPMI_GET_WDOG_MAX];
struct ipmi_cmd c;
c.c_sc = sc;
c.c_rssa = BMC_SA;
c.c_rslun = BMC_LUN;
c.c_netfn = APP_NETFN;
c.c_cmd = APP_GET_WATCHDOG_TIMER;
c.c_txlen = 0;
c.c_maxrxlen = IPMI_GET_WDOG_MAX;
c.c_rxlen = 0;
c.c_data = wdog;
ipmi_cmd(&c);
/* Period is 10ths/sec */
uint16_t timo = htole16(sc->sc_wdog_period * 10);
memcpy(&wdog[IPMI_SET_WDOG_TIMOL], &timo, 2);
wdog[IPMI_SET_WDOG_TIMER] &= ~IPMI_WDOG_DONTSTOP;
wdog[IPMI_SET_WDOG_TIMER] |= (sc->sc_wdog_period == 0) ?
0 : IPMI_WDOG_DONTSTOP;
wdog[IPMI_SET_WDOG_ACTION] &= ~IPMI_WDOG_MASK;
wdog[IPMI_SET_WDOG_ACTION] |= (sc->sc_wdog_period == 0) ?
IPMI_WDOG_DISABLED : IPMI_WDOG_REBOOT;
c.c_cmd = APP_SET_WATCHDOG_TIMER;
c.c_txlen = IPMI_SET_WDOG_MAX;
c.c_maxrxlen = 0;
c.c_rxlen = 0;
c.c_data = wdog;
ipmi_cmd(&c);
}
#if defined(__amd64__) || defined(__i386__)
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
/*
* Format of SMBIOS IPMI Flags
*
* bit0: interrupt trigger mode (1=level, 0=edge)
* bit1: interrupt polarity (1=active high, 0=active low)
* bit2: reserved
* bit3: address LSB (1=odd,0=even)
* bit4: interrupt (1=specified, 0=not specified)
* bit5: reserved
* bit6/7: register spacing (1,4,2,err)
*/
#define SMIPMI_FLAG_IRQLVL (1L << 0)
#define SMIPMI_FLAG_IRQEN (1L << 3)
#define SMIPMI_FLAG_ODDOFFSET (1L << 4)
#define SMIPMI_FLAG_IFSPACING(x) (((x)>>6)&0x3)
#define IPMI_IOSPACING_BYTE 0
#define IPMI_IOSPACING_WORD 2
#define IPMI_IOSPACING_DWORD 1
struct dmd_ipmi {
u_int8_t dmd_sig[4]; /* Signature 'IPMI' */
u_int8_t dmd_i2c_address; /* Address of BMC */
u_int8_t dmd_nvram_address; /* Address of NVRAM */
u_int8_t dmd_if_type; /* IPMI Interface Type */
u_int8_t dmd_if_rev; /* IPMI Interface Revision */
} __packed;
void *scan_sig(long, long, int, int, const void *);
void ipmi_smbios_probe(struct smbios_ipmi *, struct ipmi_attach_args *);
int ipmi_match(struct device *, void *, void *);
void ipmi_attach(struct device *, struct device *, void *);
const struct cfattach ipmi_ca = {
sizeof(struct ipmi_softc), ipmi_match, ipmi_attach,
NULL, ipmi_activate
};
int
ipmi_match(struct device *parent, void *match, void *aux)
{
struct ipmi_softc *sc;
struct ipmi_attach_args *ia = aux;
struct cfdata *cf = match;
u_int8_t cmd[32];
int rv = 0;
if (strcmp(ia->iaa_name, cf->cf_driver->cd_name))
return (0);
/* XXX local softc is wrong wrong wrong */
sc = malloc(sizeof(*sc), M_TEMP, M_WAITOK | M_ZERO);
strlcpy(sc->sc_dev.dv_xname, "ipmi0", sizeof(sc->sc_dev.dv_xname));
/* Map registers */
if (ipmi_map_regs(sc, ia) == 0) {
sc->sc_if->probe(sc);
/* Identify BMC device early to detect lying bios */
struct ipmi_cmd c;
c.c_sc = sc;
c.c_rssa = BMC_SA;
c.c_rslun = BMC_LUN;
c.c_netfn = APP_NETFN;
c.c_cmd = APP_GET_DEVICE_ID;
c.c_txlen = 0;
c.c_maxrxlen = sizeof(cmd);
c.c_rxlen = 0;
c.c_data = cmd;
ipmi_cmd(&c);
dbg_dump(1, "bmc data", c.c_rxlen, cmd);
rv = 1; /* GETID worked, we got IPMI */
ipmi_unmap_regs(sc);
}
free(sc, M_TEMP, sizeof(*sc));
return (rv);
}
void
ipmi_attach(struct device *parent, struct device *self, void *aux)
{
ipmi_attach_common((struct ipmi_softc *)self, aux);
}
/* Scan memory for signature */
void *
scan_sig(long start, long end, int skip, int len, const void *data)
{
void *va;
while (start < end) {
va = ISA_HOLE_VADDR(start);
if (memcmp(va, data, len) == 0)
return (va);
start += skip;
}
return (NULL);
}
void
ipmi_smbios_probe(struct smbios_ipmi *pipmi, struct ipmi_attach_args *ia)
{
dbg_printf(1, "ipmi_smbios_probe: %02x %02x %02x %02x %08llx %02x "
"%02x\n",
pipmi->smipmi_if_type,
pipmi->smipmi_if_rev,
pipmi->smipmi_i2c_address,
pipmi->smipmi_nvram_address,
pipmi->smipmi_base_address,
pipmi->smipmi_base_flags,
pipmi->smipmi_irq);
ia->iaa_if_type = pipmi->smipmi_if_type;
ia->iaa_if_rev = pipmi->smipmi_if_rev;
ia->iaa_if_irq = (pipmi->smipmi_base_flags & SMIPMI_FLAG_IRQEN) ?
pipmi->smipmi_irq : -1;
ia->iaa_if_irqlvl = (pipmi->smipmi_base_flags & SMIPMI_FLAG_IRQLVL) ?
IST_LEVEL : IST_EDGE;
ia->iaa_if_iosize = 1;
switch (SMIPMI_FLAG_IFSPACING(pipmi->smipmi_base_flags)) {
case IPMI_IOSPACING_BYTE:
ia->iaa_if_iospacing = 1;
break;
case IPMI_IOSPACING_DWORD:
ia->iaa_if_iospacing = 4;
break;
case IPMI_IOSPACING_WORD:
ia->iaa_if_iospacing = 2;
break;
default:
ia->iaa_if_iospacing = 1;
printf("ipmi: unknown register spacing\n");
}
/* Calculate base address (PCI BAR format) */
if (pipmi->smipmi_base_address & 0x1) {
ia->iaa_if_iotype = 'i';
ia->iaa_if_iobase = pipmi->smipmi_base_address & ~0x1;
} else {
ia->iaa_if_iotype = 'm';
ia->iaa_if_iobase = pipmi->smipmi_base_address & ~0xF;
}
if (pipmi->smipmi_base_flags & SMIPMI_FLAG_ODDOFFSET)
ia->iaa_if_iobase++;
if (pipmi->smipmi_base_flags == 0x7f) {
/* IBM 325 eServer workaround */
ia->iaa_if_iospacing = 1;
ia->iaa_if_iobase = pipmi->smipmi_base_address;
ia->iaa_if_iotype = 'i';
return;
}
}
int
ipmi_probe(void *aux)
{
struct ipmi_attach_args *ia = aux;
struct dmd_ipmi *pipmi;
struct smbtable tbl;
tbl.cookie = 0;
if (smbios_find_table(SMBIOS_TYPE_IPMIDEV, &tbl))
ipmi_smbios_probe(tbl.tblhdr, ia);
else {
pipmi = (struct dmd_ipmi *)scan_sig(0xC0000L, 0xFFFFFL, 16, 4,
"IPMI");
/* XXX hack to find Dell PowerEdge 8450 */
if (pipmi == NULL) {
/* no IPMI found */
return (0);
}
/* we have an IPMI signature, fill in attach arg structure */
ia->iaa_if_type = pipmi->dmd_if_type;
ia->iaa_if_rev = pipmi->dmd_if_rev;
}
return (1);
}
#endif
14
17
1
1
1
65
11
53
7
59
7
58
9
21
21
21
19
3
2
24
19
6
6
6
1
7
6
1
19
19
6
5
33
30
3
3
3
1
3
1
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
/* $OpenBSD: nd6.c,v 1.246 2022/08/09 21:10:03 kn Exp $ */
/* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/queue.h>
#include <sys/stdint.h>
#include <sys/task.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip_ipsp.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */
/* timer values */
int nd6_timer_next = -1; /* at which uptime nd6_timer runs */
time_t nd6_expire_next = -1; /* at which uptime nd6_expire runs */
int nd6_delay = 5; /* delay first probe time 5 second */
int nd6_umaxtries = 3; /* maximum unicast query */
int nd6_mmaxtries = 3; /* maximum multicast query */
int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */
/* preventing too many loops in ND option parsing */
int nd6_maxndopt = 10; /* max # of ND options allowed */
int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */
#ifdef ND6_DEBUG
int nd6_debug = 1;
#else
int nd6_debug = 0;
#endif
TAILQ_HEAD(llinfo_nd6_head, llinfo_nd6) nd6_list;
struct pool nd6_pool; /* pool for llinfo_nd6 structures */
int nd6_inuse;
void nd6_timer(void *);
void nd6_slowtimo(void *);
void nd6_expire(void *);
void nd6_expire_timer(void *);
void nd6_invalidate(struct rtentry *);
void nd6_free(struct rtentry *);
int nd6_llinfo_timer(struct rtentry *);
struct timeout nd6_timer_to;
struct timeout nd6_slowtimo_ch;
struct timeout nd6_expire_timeout;
struct task nd6_expire_task;
void
nd6_init(void)
{
static int nd6_init_done = 0;
if (nd6_init_done) {
log(LOG_NOTICE, "%s called more than once\n", __func__);
return;
}
TAILQ_INIT(&nd6_list);
pool_init(&nd6_pool, sizeof(struct llinfo_nd6), 0,
IPL_SOFTNET, 0, "nd6", NULL);
task_set(&nd6_expire_task, nd6_expire, NULL);
nd6_init_done = 1;
/* start timer */
timeout_set_proc(&nd6_timer_to, nd6_timer, NULL);
timeout_set_proc(&nd6_slowtimo_ch, nd6_slowtimo, NULL);
timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL);
timeout_set(&nd6_expire_timeout, nd6_expire_timer, NULL);
}
struct nd_ifinfo *
nd6_ifattach(struct ifnet *ifp)
{
struct nd_ifinfo *nd;
nd = malloc(sizeof(*nd), M_IP6NDP, M_WAITOK | M_ZERO);
nd->initialized = 1;
nd->basereachable = REACHABLE_TIME;
nd->reachable = ND_COMPUTE_RTIME(nd->basereachable);
nd->retrans = RETRANS_TIMER;
return nd;
}
void
nd6_ifdetach(struct nd_ifinfo *nd)
{
free(nd, M_IP6NDP, sizeof(*nd));
}
void
nd6_option_init(void *opt, int icmp6len, union nd_opts *ndopts)
{
bzero(ndopts, sizeof(*ndopts));
ndopts->nd_opts_search = (struct nd_opt_hdr *)opt;
ndopts->nd_opts_last
= (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len);
if (icmp6len == 0) {
ndopts->nd_opts_done = 1;
ndopts->nd_opts_search = NULL;
}
}
/*
* Take one ND option.
*/
struct nd_opt_hdr *
nd6_option(union nd_opts *ndopts)
{
struct nd_opt_hdr *nd_opt;
int olen;
if (!ndopts)
panic("%s: ndopts == NULL", __func__);
if (!ndopts->nd_opts_last)
panic("%s: uninitialized ndopts", __func__);
if (!ndopts->nd_opts_search)
return NULL;
if (ndopts->nd_opts_done)
return NULL;
nd_opt = ndopts->nd_opts_search;
/* make sure nd_opt_len is inside the buffer */
if ((caddr_t)&nd_opt->nd_opt_len >= (caddr_t)ndopts->nd_opts_last) {
bzero(ndopts, sizeof(*ndopts));
return NULL;
}
olen = nd_opt->nd_opt_len << 3;
if (olen == 0) {
/*
* Message validation requires that all included
* options have a length that is greater than zero.
*/
bzero(ndopts, sizeof(*ndopts));
return NULL;
}
ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen);
if (ndopts->nd_opts_search > ndopts->nd_opts_last) {
/* option overruns the end of buffer, invalid */
bzero(ndopts, sizeof(*ndopts));
return NULL;
} else if (ndopts->nd_opts_search == ndopts->nd_opts_last) {
/* reached the end of options chain */
ndopts->nd_opts_done = 1;
ndopts->nd_opts_search = NULL;
}
return nd_opt;
}
/*
* Parse multiple ND options.
* This function is much easier to use, for ND routines that do not need
* multiple options of the same type.
*/
int
nd6_options(union nd_opts *ndopts)
{
struct nd_opt_hdr *nd_opt;
int i = 0;
if (!ndopts)
panic("%s: ndopts == NULL", __func__);
if (!ndopts->nd_opts_last)
panic("%s: uninitialized ndopts", __func__);
if (!ndopts->nd_opts_search)
return 0;
while (1) {
nd_opt = nd6_option(ndopts);
if (!nd_opt && !ndopts->nd_opts_last) {
/*
* Message validation requires that all included
* options have a length that is greater than zero.
*/
icmp6stat_inc(icp6s_nd_badopt);
bzero(ndopts, sizeof(*ndopts));
return -1;
}
if (!nd_opt)
goto skip1;
switch (nd_opt->nd_opt_type) {
case ND_OPT_SOURCE_LINKADDR:
case ND_OPT_TARGET_LINKADDR:
case ND_OPT_MTU:
case ND_OPT_REDIRECTED_HEADER:
if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
nd6log((LOG_INFO,
"duplicated ND6 option found (type=%d)\n",
nd_opt->nd_opt_type));
/* XXX bark? */
} else {
ndopts->nd_opt_array[nd_opt->nd_opt_type]
= nd_opt;
}
break;
case ND_OPT_PREFIX_INFORMATION:
if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
ndopts->nd_opt_array[nd_opt->nd_opt_type]
= nd_opt;
}
ndopts->nd_opts_pi_end =
(struct nd_opt_prefix_info *)nd_opt;
break;
case ND_OPT_DNSSL:
case ND_OPT_RDNSS:
/* Don't warn */
break;
default:
/*
* Unknown options must be silently ignored,
* to accommodate future extension to the protocol.
*/
nd6log((LOG_DEBUG,
"nd6_options: unsupported option %d - "
"option ignored\n", nd_opt->nd_opt_type));
}
skip1:
i++;
if (i > nd6_maxndopt) {
icmp6stat_inc(icp6s_nd_toomanyopt);
nd6log((LOG_INFO, "too many loop in nd opt\n"));
break;
}
if (ndopts->nd_opts_done)
break;
}
return 0;
}
/*
* ND6 timer routine to handle ND6 entries
*/
void
nd6_llinfo_settimer(const struct llinfo_nd6 *ln, unsigned int secs)
{
time_t expire = getuptime() + secs;
NET_ASSERT_LOCKED();
KASSERT(!ISSET(ln->ln_rt->rt_flags, RTF_LOCAL));
ln->ln_rt->rt_expire = expire;
if (!timeout_pending(&nd6_timer_to) || expire < nd6_timer_next) {
nd6_timer_next = expire;
timeout_add_sec(&nd6_timer_to, secs);
}
}
void
nd6_timer(void *unused)
{
struct llinfo_nd6 *ln, *nln;
time_t expire = getuptime() + nd6_gctimer;
int secs;
NET_LOCK();
TAILQ_FOREACH_SAFE(ln, &nd6_list, ln_list, nln) {
struct rtentry *rt = ln->ln_rt;
if (rt->rt_expire && rt->rt_expire <= getuptime())
if (nd6_llinfo_timer(rt))
continue;
if (rt->rt_expire && rt->rt_expire < expire)
expire = rt->rt_expire;
}
secs = expire - getuptime();
if (secs < 0)
secs = 0;
if (!TAILQ_EMPTY(&nd6_list)) {
nd6_timer_next = getuptime() + secs;
timeout_add_sec(&nd6_timer_to, secs);
}
NET_UNLOCK();
}
/*
* ND timer state handling.
*
* Returns 1 if `rt' should no longer be used, 0 otherwise.
*/
int
nd6_llinfo_timer(struct rtentry *rt)
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
struct sockaddr_in6 *dst = satosin6(rt_key(rt));
struct ifnet *ifp;
struct nd_ifinfo *ndi = NULL;
NET_ASSERT_LOCKED();
if ((ifp = if_get(rt->rt_ifidx)) == NULL)
return 1;
ndi = ND_IFINFO(ifp);
switch (ln->ln_state) {
case ND6_LLINFO_INCOMPLETE:
if (ln->ln_asked < nd6_mmaxtries) {
ln->ln_asked++;
nd6_llinfo_settimer(ln, ndi->retrans / 1000);
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
} else {
struct mbuf *m = ln->ln_hold;
if (m) {
ln->ln_hold = NULL;
/*
* Fake rcvif to make the ICMP error
* more helpful in diagnosing for the
* receiver.
* XXX: should we consider
* older rcvif?
*/
m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0);
if (ln->ln_hold == m) {
/* m is back in ln_hold. Discard. */
m_freem(ln->ln_hold);
ln->ln_hold = NULL;
}
}
nd6_free(rt);
ln = NULL;
}
break;
case ND6_LLINFO_REACHABLE:
if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_state = ND6_LLINFO_STALE;
nd6_llinfo_settimer(ln, nd6_gctimer);
}
break;
case ND6_LLINFO_STALE:
case ND6_LLINFO_PURGE:
/* Garbage Collection(RFC 2461 5.3) */
if (!ND6_LLINFO_PERMANENT(ln)) {
nd6_free(rt);
ln = NULL;
}
break;
case ND6_LLINFO_DELAY:
if (ndi) {
/* We need NUD */
ln->ln_asked = 1;
ln->ln_state = ND6_LLINFO_PROBE;
nd6_llinfo_settimer(ln, ndi->retrans / 1000);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
}
break;
case ND6_LLINFO_PROBE:
if (ln->ln_asked < nd6_umaxtries) {
ln->ln_asked++;
nd6_llinfo_settimer(ln, ndi->retrans / 1000);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
} else {
nd6_free(rt);
ln = NULL;
}
break;
}
if_put(ifp);
return (ln == NULL);
}
void
nd6_expire_timer_update(struct in6_ifaddr *ia6)
{
time_t expire_time = INT64_MAX;
int secs;
KERNEL_ASSERT_LOCKED();
if (ia6->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME)
expire_time = ia6->ia6_lifetime.ia6t_expire;
if (!(ia6->ia6_flags & IN6_IFF_DEPRECATED) &&
ia6->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME &&
expire_time > ia6->ia6_lifetime.ia6t_preferred)
expire_time = ia6->ia6_lifetime.ia6t_preferred;
if (expire_time == INT64_MAX)
return;
/*
* IFA6_IS_INVALID() and IFA6_IS_DEPRECATED() check for uptime
* greater than ia6t_expire or ia6t_preferred, not greater or equal.
* Schedule timeout one second later so that either IFA6_IS_INVALID()
* or IFA6_IS_DEPRECATED() is true.
*/
expire_time++;
if (!timeout_pending(&nd6_expire_timeout) ||
nd6_expire_next > expire_time) {
secs = expire_time - getuptime();
if (secs < 0)
secs = 0;
timeout_add_sec(&nd6_expire_timeout, secs);
nd6_expire_next = expire_time;
}
}
/*
* Expire interface addresses.
*/
void
nd6_expire(void *unused)
{
struct ifnet *ifp;
KERNEL_LOCK();
NET_LOCK();
TAILQ_FOREACH(ifp, &ifnet, if_list) {
struct ifaddr *ifa, *nifa;
struct in6_ifaddr *ia6;
TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, ifa_list, nifa) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ia6 = ifatoia6(ifa);
/* check address lifetime */
if (IFA6_IS_INVALID(ia6)) {
in6_purgeaddr(&ia6->ia_ifa);
} else {
if (IFA6_IS_DEPRECATED(ia6))
ia6->ia6_flags |= IN6_IFF_DEPRECATED;
nd6_expire_timer_update(ia6);
}
}
}
NET_UNLOCK();
KERNEL_UNLOCK();
}
void
nd6_expire_timer(void *unused)
{
task_add(net_tq(0), &nd6_expire_task);
}
/*
* Nuke neighbor cache/prefix/default router management table, right before
* ifp goes away.
*/
void
nd6_purge(struct ifnet *ifp)
{
struct llinfo_nd6 *ln, *nln;
NET_ASSERT_LOCKED();
/*
* Nuke neighbor cache entries for the ifp.
*/
TAILQ_FOREACH_SAFE(ln, &nd6_list, ln_list, nln) {
struct rtentry *rt;
struct sockaddr_dl *sdl;
rt = ln->ln_rt;
if (rt != NULL && rt->rt_gateway != NULL &&
rt->rt_gateway->sa_family == AF_LINK) {
sdl = satosdl(rt->rt_gateway);
if (sdl->sdl_index == ifp->if_index)
nd6_free(rt);
}
}
}
struct rtentry *
nd6_lookup(const struct in6_addr *addr6, int create, struct ifnet *ifp,
u_int rtableid)
{
struct rtentry *rt;
struct sockaddr_in6 sin6;
int flags;
bzero(&sin6, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr6;
flags = (create) ? RT_RESOLVE : 0;
rt = rtalloc(sin6tosa(&sin6), flags, rtableid);
if (rt != NULL && (rt->rt_flags & RTF_LLINFO) == 0) {
/*
* This is the case for the default route.
* If we want to create a neighbor cache for the address, we
* should free the route for the destination and allocate an
* interface route.
*/
if (create) {
rtfree(rt);
rt = NULL;
}
}
if (rt == NULL) {
if (create && ifp) {
struct rt_addrinfo info;
struct ifaddr *ifa;
int error;
/*
* If no route is available and create is set,
* we allocate a host route for the destination
* and treat it like an interface route.
* This hack is necessary for a neighbor which can't
* be covered by our own prefix.
*/
ifa = ifaof_ifpforaddr(sin6tosa(&sin6), ifp);
if (ifa == NULL)
return (NULL);
/*
* Create a new route. RTF_LLINFO is necessary
* to create a Neighbor Cache entry for the
* destination in nd6_rtrequest which will be
* called in rtrequest.
*/
bzero(&info, sizeof(info));
info.rti_ifa = ifa;
info.rti_flags = RTF_HOST | RTF_LLINFO;
info.rti_info[RTAX_DST] = sin6tosa(&sin6);
info.rti_info[RTAX_GATEWAY] = sdltosa(ifp->if_sadl);
error = rtrequest(RTM_ADD, &info, RTP_CONNECTED, &rt,
rtableid);
if (error)
return (NULL);
if (rt->rt_llinfo != NULL) {
struct llinfo_nd6 *ln =
(struct llinfo_nd6 *)rt->rt_llinfo;
ln->ln_state = ND6_LLINFO_NOSTATE;
}
} else
return (NULL);
}
/*
* Validation for the entry.
* Note that the check for rt_llinfo is necessary because a cloned
* route from a parent route that has the L flag (e.g. the default
* route to a p2p interface) may have the flag, too, while the
* destination is not actually a neighbor.
*/
if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL ||
(ifp != NULL && rt->rt_ifidx != ifp->if_index)) {
if (create) {
char addr[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, "%s: failed to lookup %s (if=%s)\n",
__func__,
inet_ntop(AF_INET6, addr6, addr, sizeof(addr)),
ifp ? ifp->if_xname : "unspec"));
}
rtfree(rt);
return (NULL);
}
return (rt);
}
/*
* Detect if a given IPv6 address identifies a neighbor on a given link.
* XXX: should take care of the destination of a p2p link?
*/
int
nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
{
struct in6_ifaddr *ia6;
struct ifaddr *ifa;
struct rtentry *rt;
/*
* A link-local address is always a neighbor.
* XXX: we should use the sin6_scope_id field rather than the embedded
* interface index.
* XXX: a link does not necessarily specify a single interface.
*/
if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) &&
ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index)
return (1);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ia6 = ifatoia6(ifa);
/* Prefix check down below. */
if (ia6->ia6_flags & IN6_IFF_AUTOCONF)
continue;
if (IN6_ARE_MASKED_ADDR_EQUAL(&addr->sin6_addr,
&ia6->ia_addr.sin6_addr,
&ia6->ia_prefixmask.sin6_addr))
return (1);
}
/*
* Even if the address matches none of our addresses, it might be
* in the neighbor cache.
*/
rt = nd6_lookup(&addr->sin6_addr, 0, ifp, ifp->if_rdomain);
if (rt != NULL) {
rtfree(rt);
return (1);
}
return (0);
}
void
nd6_invalidate(struct rtentry *rt)
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
struct sockaddr_dl *sdl = satosdl(rt->rt_gateway);
m_freem(ln->ln_hold);
sdl->sdl_alen = 0;
ln->ln_hold = NULL;
ln->ln_state = ND6_LLINFO_INCOMPLETE;
ln->ln_asked = 0;
}
/*
* Free an nd6 llinfo entry.
*/
void
nd6_free(struct rtentry *rt)
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
struct in6_addr in6 = satosin6(rt_key(rt))->sin6_addr;
struct ifnet *ifp;
NET_ASSERT_LOCKED();
ifp = if_get(rt->rt_ifidx);
if (!ip6_forwarding) {
if (ln->ln_router) {
/*
* rt6_flush must be called whether or not the neighbor
* is in the Default Router List.
* See a corresponding comment in nd6_na_input().
*/
rt6_flush(&in6, ifp);
}
}
KASSERT(!ISSET(rt->rt_flags, RTF_LOCAL));
nd6_invalidate(rt);
/*
* Detach the route from the routing tree and the list of neighbor
* caches, and disable the route entry not to be used in already
* cached routes.
*/
if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED))
rtdeletemsg(rt, ifp, ifp->if_rdomain);
if_put(ifp);
}
/*
* Upper-layer reachability hint for Neighbor Unreachability Detection.
*
* XXX cost-effective methods?
*/
void
nd6_nud_hint(struct rtentry *rt)
{
struct llinfo_nd6 *ln;
struct ifnet *ifp;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return;
if ((rt->rt_flags & RTF_GATEWAY) != 0 ||
(rt->rt_flags & RTF_LLINFO) == 0 ||
rt->rt_llinfo == NULL || rt->rt_gateway == NULL ||
rt->rt_gateway->sa_family != AF_LINK) {
/* This is not a host route. */
goto out;
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
if (ln->ln_state < ND6_LLINFO_REACHABLE)
goto out;
/*
* if we get upper-layer reachability confirmation many times,
* it is possible we have false information.
*/
ln->ln_byhint++;
if (ln->ln_byhint > nd6_maxnudhint)
goto out;
ln->ln_state = ND6_LLINFO_REACHABLE;
if (!ND6_LLINFO_PERMANENT(ln))
nd6_llinfo_settimer(ln, ND_IFINFO(ifp)->reachable);
out:
if_put(ifp);
}
void
nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
{
struct sockaddr *gate = rt->rt_gateway;
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
struct ifaddr *ifa;
struct in6_ifaddr *ifa6;
if (ISSET(rt->rt_flags, RTF_GATEWAY|RTF_MULTICAST|RTF_MPLS))
return;
if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) {
/*
* This is probably an interface direct route for a link
* which does not need neighbor caches (e.g. fe80::%lo0/64).
* We do not need special treatment below for such a route.
* Moreover, the RTF_LLINFO flag which would be set below
* would annoy the ndp(8) command.
*/
return;
}
if (req == RTM_RESOLVE && nd6_need_cache(ifp) == 0) {
/*
* For routing daemons like ospf6d we allow neighbor discovery
* based on the cloning route only. This allows us to sent
* packets directly into a network without having an address
* with matching prefix on the interface. If the cloning
* route is used for an stf interface, we would mistakenly
* make a neighbor cache for the host route, and would see
* strange neighbor solicitation for the corresponding
* destination. In order to avoid confusion, we check if the
* interface is suitable for neighbor discovery, and stop the
* process if not. Additionally, we remove the LLINFO flag
* so that ndp(8) will not try to get the neighbor information
* of the destination.
*/
rt->rt_flags &= ~RTF_LLINFO;
return;
}
switch (req) {
case RTM_ADD:
if ((rt->rt_flags & RTF_CLONING) ||
((rt->rt_flags & (RTF_LLINFO | RTF_LOCAL)) && ln == NULL)) {
if (ln != NULL)
nd6_llinfo_settimer(ln, 0);
if ((rt->rt_flags & RTF_CLONING) != 0)
break;
}
/*
* In IPv4 code, we try to announce new RTF_ANNOUNCE entry here.
* We don't do that here since llinfo is not ready yet.
*
* There are also couple of other things to be discussed:
* - unsolicited NA code needs improvement beforehand
* - RFC2461 says we MAY send multicast unsolicited NA
* (7.2.6 paragraph 4), however, it also says that we
* SHOULD provide a mechanism to prevent multicast NA storm.
* we don't have anything like it right now.
* note that the mechanism needs a mutual agreement
* between proxies, which means that we need to implement
* a new protocol, or a new kludge.
* - from RFC2461 6.2.4, host MUST NOT send an unsolicited NA.
* we need to check ip6forwarding before sending it.
* (or should we allow proxy ND configuration only for
* routers? there's no mention about proxy ND from hosts)
*/
#if 0
/* XXX it does not work */
if (rt->rt_flags & RTF_ANNOUNCE)
nd6_na_output(ifp,
&satosin6(rt_key(rt))->sin6_addr,
&satosin6(rt_key(rt))->sin6_addr,
ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
1, NULL);
#endif
/* FALLTHROUGH */
case RTM_RESOLVE:
if (gate->sa_family != AF_LINK ||
gate->sa_len < sizeof(struct sockaddr_dl)) {
log(LOG_DEBUG, "%s: bad gateway value: %s\n",
__func__, ifp->if_xname);
break;
}
satosdl(gate)->sdl_type = ifp->if_type;
satosdl(gate)->sdl_index = ifp->if_index;
if (ln != NULL)
break; /* This happens on a route change */
/*
* Case 2: This route may come from cloning, or a manual route
* add with a LL address.
*/
ln = pool_get(&nd6_pool, PR_NOWAIT | PR_ZERO);
rt->rt_llinfo = (caddr_t)ln;
if (ln == NULL) {
log(LOG_DEBUG, "%s: pool get failed\n", __func__);
break;
}
nd6_inuse++;
ln->ln_rt = rt;
/* this is required for "ndp" command. - shin */
if (req == RTM_ADD) {
/*
* gate should have some valid AF_LINK entry,
* and ln expire should have some lifetime
* which is specified by ndp command.
*/
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
} else {
/*
* When req == RTM_RESOLVE, rt is created and
* initialized in rtrequest(), so rt_expire is 0.
*/
ln->ln_state = ND6_LLINFO_NOSTATE;
nd6_llinfo_settimer(ln, 0);
}
rt->rt_flags |= RTF_LLINFO;
TAILQ_INSERT_HEAD(&nd6_list, ln, ln_list);
/*
* If we have too many cache entries, initiate immediate
* purging for some "less recently used" entries. Note that
* we cannot directly call nd6_free() here because it would
* cause re-entering rtable related routines triggering an LOR
* problem for FreeBSD.
*/
if (ip6_neighborgcthresh >= 0 &&
nd6_inuse >= ip6_neighborgcthresh) {
int i;
for (i = 0; i < 10; i++) {
struct llinfo_nd6 *ln_end;
ln_end = TAILQ_LAST(&nd6_list, llinfo_nd6_head);
if (ln_end == ln)
break;
/* Move this entry to the head */
TAILQ_REMOVE(&nd6_list, ln_end, ln_list);
TAILQ_INSERT_HEAD(&nd6_list, ln_end, ln_list);
if (ND6_LLINFO_PERMANENT(ln_end))
continue;
if (ln_end->ln_state > ND6_LLINFO_INCOMPLETE)
ln_end->ln_state = ND6_LLINFO_STALE;
else
ln_end->ln_state = ND6_LLINFO_PURGE;
nd6_llinfo_settimer(ln_end, 0);
}
}
/*
* check if rt_key(rt) is one of my address assigned
* to the interface.
*/
ifa6 = in6ifa_ifpwithaddr(ifp,
&satosin6(rt_key(rt))->sin6_addr);
ifa = ifa6 ? &ifa6->ia_ifa : NULL;
if (ifa) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
rt->rt_expire = 0;
KASSERT(ifa == rt->rt_ifa);
} else if (rt->rt_flags & RTF_ANNOUNCE) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
rt->rt_expire = 0;
/* join solicited node multicast for proxy ND */
if (ifp->if_flags & IFF_MULTICAST) {
struct in6_addr llsol;
int error;
llsol = satosin6(rt_key(rt))->sin6_addr;
llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff;
if (in6_addmulti(&llsol, ifp, &error)) {
char addr[INET6_ADDRSTRLEN];
nd6log((LOG_ERR, "%s: failed to join "
"%s (errno=%d)\n", ifp->if_xname,
inet_ntop(AF_INET6, &llsol,
addr, sizeof(addr)),
error));
}
}
}
break;
case RTM_DELETE:
if (ln == NULL)
break;
/* leave from solicited node multicast for proxy ND */
if ((rt->rt_flags & RTF_ANNOUNCE) != 0 &&
(ifp->if_flags & IFF_MULTICAST) != 0) {
struct in6_addr llsol;
struct in6_multi *in6m;
llsol = satosin6(rt_key(rt))->sin6_addr;
llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff;
IN6_LOOKUP_MULTI(llsol, ifp, in6m);
if (in6m)
in6_delmulti(in6m);
}
nd6_inuse--;
TAILQ_REMOVE(&nd6_list, ln, ln_list);
rt->rt_expire = 0;
rt->rt_llinfo = NULL;
rt->rt_flags &= ~RTF_LLINFO;
m_freem(ln->ln_hold);
pool_put(&nd6_pool, ln);
break;
case RTM_INVALIDATE:
if (ln == NULL)
break;
if (!ISSET(rt->rt_flags, RTF_LOCAL))
nd6_invalidate(rt);
break;
}
}
int
nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
{
struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
struct rtentry *rt;
switch (cmd) {
case SIOCGIFINFO_IN6:
NET_LOCK_SHARED();
ndi->ndi = *ND_IFINFO(ifp);
NET_UNLOCK_SHARED();
return (0);
case SIOCGNBRINFO_IN6:
{
struct llinfo_nd6 *ln;
struct in6_addr nb_addr = nbi->addr; /* make local for safety */
time_t expire;
NET_LOCK_SHARED();
/*
* XXX: KAME specific hack for scoped addresses
* XXXX: for other scopes than link-local?
*/
if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) ||
IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) {
u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2];
if (*idp == 0)
*idp = htons(ifp->if_index);
}
rt = nd6_lookup(&nb_addr, 0, ifp, ifp->if_rdomain);
if (rt == NULL ||
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) {
rtfree(rt);
NET_UNLOCK_SHARED();
return (EINVAL);
}
expire = ln->ln_rt->rt_expire;
if (expire != 0) {
expire -= getuptime();
expire += gettime();
}
nbi->state = ln->ln_state;
nbi->asked = ln->ln_asked;
nbi->isrouter = ln->ln_router;
nbi->expire = expire;
rtfree(rt);
NET_UNLOCK_SHARED();
return (0);
}
}
return (0);
}
/*
* Create neighbor cache entry and cache link-layer address,
* on reception of inbound ND6 packets. (RS/RA/NS/redirect)
*
* type - ICMP6 type
* code - type dependent information
*/
void
nd6_cache_lladdr(struct ifnet *ifp, const struct in6_addr *from, char *lladdr,
int lladdrlen, int type, int code)
{
struct rtentry *rt = NULL;
struct llinfo_nd6 *ln = NULL;
int is_newentry;
struct sockaddr_dl *sdl = NULL;
int do_update;
int olladdr;
int llchange;
int newstate = 0;
if (!ifp)
panic("%s: ifp == NULL", __func__);
if (!from)
panic("%s: from == NULL", __func__);
/* nothing must be updated for unspecified address */
if (IN6_IS_ADDR_UNSPECIFIED(from))
return;
/*
* Validation about ifp->if_addrlen and lladdrlen must be done in
* the caller.
*
* XXX If the link does not have link-layer address, what should
* we do? (ifp->if_addrlen == 0)
* Spec says nothing in sections for RA, RS and NA. There's small
* description on it in NS section (RFC 2461 7.2.3).
*/
rt = nd6_lookup(from, 0, ifp, ifp->if_rdomain);
if (rt == NULL) {
rt = nd6_lookup(from, 1, ifp, ifp->if_rdomain);
is_newentry = 1;
} else {
/* do not overwrite local or static entry */
if (ISSET(rt->rt_flags, RTF_STATIC|RTF_LOCAL)) {
rtfree(rt);
return;
}
is_newentry = 0;
}
if (!rt)
return;
if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
fail:
nd6_free(rt);
rtfree(rt);
return;
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
if (ln == NULL)
goto fail;
if (rt->rt_gateway == NULL)
goto fail;
if (rt->rt_gateway->sa_family != AF_LINK)
goto fail;
sdl = satosdl(rt->rt_gateway);
olladdr = (sdl->sdl_alen) ? 1 : 0;
if (olladdr && lladdr) {
if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
llchange = 1;
else
llchange = 0;
} else
llchange = 0;
/*
* newentry olladdr lladdr llchange (*=record)
* 0 n n -- (1)
* 0 y n -- (2)
* 0 n y -- (3) * STALE
* 0 y y n (4) *
* 0 y y y (5) * STALE
* 1 -- n -- (6) NOSTATE(= PASSIVE)
* 1 -- y -- (7) * STALE
*/
if (llchange) {
char addr[INET6_ADDRSTRLEN];
log(LOG_INFO, "ndp info overwritten for %s by %s on %s\n",
inet_ntop(AF_INET6, from, addr, sizeof(addr)),
ether_sprintf(lladdr), ifp->if_xname);
}
if (lladdr) { /* (3-5) and (7) */
/*
* Record source link-layer address
* XXX is it dependent to ifp->if_type?
*/
sdl->sdl_alen = ifp->if_addrlen;
bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
}
if (!is_newentry) {
if ((!olladdr && lladdr) || /* (3) */
(olladdr && lladdr && llchange)) { /* (5) */
do_update = 1;
newstate = ND6_LLINFO_STALE;
} else /* (1-2,4) */
do_update = 0;
} else {
do_update = 1;
if (!lladdr) /* (6) */
newstate = ND6_LLINFO_NOSTATE;
else /* (7) */
newstate = ND6_LLINFO_STALE;
}
if (do_update) {
/*
* Update the state of the neighbor cache.
*/
ln->ln_state = newstate;
if (ln->ln_state == ND6_LLINFO_STALE) {
/*
* Since nd6_resolve() in ifp->if_output() will cause
* state transition to DELAY and reset the timer,
* we must set the timer now, although it is actually
* meaningless.
*/
nd6_llinfo_settimer(ln, nd6_gctimer);
if (ln->ln_hold) {
struct mbuf *n = ln->ln_hold;
ln->ln_hold = NULL;
/*
* we assume ifp is not a p2p here, so just
* set the 2nd argument as the 1st one.
*/
ifp->if_output(ifp, n, rt_key(rt), rt);
if (ln->ln_hold == n) {
/* n is back in ln_hold. Discard. */
m_freem(ln->ln_hold);
ln->ln_hold = NULL;
}
}
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/* probe right away */
nd6_llinfo_settimer(ln, 0);
}
}
/*
* ICMP6 type dependent behavior.
*
* NS: clear IsRouter if new entry
* RS: clear IsRouter
* RA: set IsRouter if there's lladdr
* redir: clear IsRouter if new entry
*
* RA case, (1):
* The spec says that we must set IsRouter in the following cases:
* - If lladdr exist, set IsRouter. This means (1-5).
* - If it is old entry (!newentry), set IsRouter. This means (7).
* So, based on the spec, in (1-5) and (7) cases we must set IsRouter.
* A question arises for (1) case. (1) case has no lladdr in the
* neighbor cache, this is similar to (6).
* This case is rare but we figured that we MUST NOT set IsRouter.
*
* newentry olladdr lladdr llchange NS RS RA redir
* D R
* 0 n n -- (1) c ? s
* 0 y n -- (2) c s s
* 0 n y -- (3) c s s
* 0 y y n (4) c s s
* 0 y y y (5) c s s
* 1 -- n -- (6) c c c s
* 1 -- y -- (7) c c s c s
*
* (c=clear s=set)
*/
switch (type & 0xff) {
case ND_NEIGHBOR_SOLICIT:
/*
* New entry must have is_router flag cleared.
*/
if (is_newentry) /* (6-7) */
ln->ln_router = 0;
break;
case ND_REDIRECT:
/*
* If the icmp is a redirect to a better router, always set the
* is_router flag. Otherwise, if the entry is newly created,
* clear the flag. [RFC 2461, sec 8.3]
*/
if (code == ND_REDIRECT_ROUTER)
ln->ln_router = 1;
else if (is_newentry) /* (6-7) */
ln->ln_router = 0;
break;
case ND_ROUTER_SOLICIT:
/*
* is_router flag must always be cleared.
*/
ln->ln_router = 0;
break;
case ND_ROUTER_ADVERT:
/*
* Mark an entry with lladdr as a router.
*/
if ((!is_newentry && (olladdr || lladdr)) || /* (2-5) */
(is_newentry && lladdr)) { /* (7) */
ln->ln_router = 1;
}
break;
}
rtfree(rt);
}
void
nd6_slowtimo(void *ignored_arg)
{
struct nd_ifinfo *nd6if;
struct ifnet *ifp;
NET_LOCK();
timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL);
TAILQ_FOREACH(ifp, &ifnet, if_list) {
nd6if = ND_IFINFO(ifp);
if (nd6if->basereachable && /* already initialized */
(nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) {
/*
* Since reachable time rarely changes by router
* advertisements, we SHOULD insure that a new random
* value gets recomputed at least once every few hours.
* (RFC 2461, 6.3.4)
*/
nd6if->recalctm = ND6_RECALC_REACHTM_INTERVAL;
nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable);
}
}
NET_UNLOCK();
}
int
nd6_resolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
struct sockaddr *dst, u_char *desten)
{
struct sockaddr_dl *sdl;
struct rtentry *rt;
struct llinfo_nd6 *ln = NULL;
if (m->m_flags & M_MCAST) {
ETHER_MAP_IPV6_MULTICAST(&satosin6(dst)->sin6_addr, desten);
return (0);
}
rt = rt_getll(rt0);
if (ISSET(rt->rt_flags, RTF_REJECT) &&
(rt->rt_expire == 0 || getuptime() < rt->rt_expire)) {
m_freem(m);
return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
/*
* Address resolution or Neighbor Unreachability Detection
* for the next hop.
* At this point, the destination of the packet must be a unicast
* or an anycast address(i.e. not a multicast).
*/
if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
char addr[INET6_ADDRSTRLEN];
log(LOG_DEBUG, "%s: %s: route contains no ND information\n",
__func__, inet_ntop(AF_INET6,
&satosin6(rt_key(rt))->sin6_addr, addr, sizeof(addr)));
m_freem(m);
return (EINVAL);
}
if (rt->rt_gateway->sa_family != AF_LINK) {
printf("%s: something odd happens\n", __func__);
m_freem(m);
return (EINVAL);
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
KASSERT(ln != NULL);
/*
* Move this entry to the head of the queue so that it is less likely
* for this entry to be a target of forced garbage collection (see
* nd6_rtrequest()).
*/
TAILQ_REMOVE(&nd6_list, ln, ln_list);
TAILQ_INSERT_HEAD(&nd6_list, ln, ln_list);
/*
* The first time we send a packet to a neighbor whose entry is
* STALE, we have to change the state to DELAY and a sets a timer to
* expire in DELAY_FIRST_PROBE_TIME seconds to ensure do
* neighbor unreachability detection on expiration.
* (RFC 2461 7.3.3)
*/
if (ln->ln_state == ND6_LLINFO_STALE) {
ln->ln_asked = 0;
ln->ln_state = ND6_LLINFO_DELAY;
nd6_llinfo_settimer(ln, nd6_delay);
}
/*
* If the neighbor cache entry has a state other than INCOMPLETE
* (i.e. its link-layer address is already resolved), just
* send the packet.
*/
if (ln->ln_state > ND6_LLINFO_INCOMPLETE) {
sdl = satosdl(rt->rt_gateway);
if (sdl->sdl_alen != ETHER_ADDR_LEN) {
char addr[INET6_ADDRSTRLEN];
log(LOG_DEBUG, "%s: %s: incorrect nd6 information\n",
__func__,
inet_ntop(AF_INET6, &satosin6(dst)->sin6_addr,
addr, sizeof(addr)));
m_freem(m);
return (EINVAL);
}
bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
return (0);
}
/*
* There is a neighbor cache entry, but no ethernet address
* response yet. Replace the held mbuf (if any) with this
* latest one.
*/
if (ln->ln_state == ND6_LLINFO_NOSTATE)
ln->ln_state = ND6_LLINFO_INCOMPLETE;
m_freem(ln->ln_hold);
ln->ln_hold = m;
/*
* If there has been no NS for the neighbor after entering the
* INCOMPLETE state, send the first solicitation.
*/
if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) {
ln->ln_asked++;
nd6_llinfo_settimer(ln, ND_IFINFO(ifp)->retrans / 1000);
nd6_ns_output(ifp, NULL, &satosin6(dst)->sin6_addr, ln, 0);
}
return (EAGAIN);
}
int
nd6_need_cache(struct ifnet *ifp)
{
/*
* RFC2893 says:
* - unidirectional tunnels needs no ND
*/
switch (ifp->if_type) {
case IFT_ETHER:
case IFT_IEEE80211:
case IFT_CARP:
return (1);
default:
return (0);
}
}
120
120
119
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/* $OpenBSD: ip_id.c,v 1.25 2021/03/10 10:21:48 jsg Exp $ */
/*
* Copyright (c) 2008 Theo de Raadt, Ryan McBride
*
* Slightly different algorithm from the one designed by
* Matthew Dillon <dillon@backplane.com> for The DragonFly Project
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Random ip sequence number generator. Use the system PRNG to shuffle
* the 65536 entry ID space. We reshuffle the ID we pick out of the array
* into the previous 32767 cells, providing an guarantee that an ID will not
* be reused for at least 32768 calls.
*/
#include <sys/param.h>
#include <sys/systm.h>
static u_int16_t ip_shuffle[65536];
static int isindex = 0;
u_int16_t ip_randomid(void);
/*
* Return a random IP id. Shuffle the new value we get into the previous half
* of the ip_shuffle ring (-32767 or swap with ourself), to avoid duplicates
* occurring too quickly but also still be random.
*
* 0 is a special IP ID -- don't return it.
*/
u_int16_t
ip_randomid(void)
{
static int ipid_initialized;
u_int16_t si, r;
int i, i2;
if (!ipid_initialized) {
ipid_initialized = 1;
/*
* Initialize with a random permutation. Do so using Knuth
* which avoids the exchange in the Durstenfeld shuffle.
* (See "The Art of Computer Programming, Vol 2" 3rd ed, pg. 145).
*
* Even if our PRNG is imperfect at boot time, we have deferred
* doing this until the first packet being sent and now must
* generate an ID.
*/
for (i = 0; i < nitems(ip_shuffle); ++i) {
i2 = arc4random_uniform(i + 1);
ip_shuffle[i] = ip_shuffle[i2];
ip_shuffle[i2] = i;
}
}
do {
arc4random_buf(&si, sizeof(si));
i = isindex & 0xFFFF;
i2 = (isindex - (si & 0x7FFF)) & 0xFFFF;
r = ip_shuffle[i];
ip_shuffle[i] = ip_shuffle[i2];
ip_shuffle[i2] = r;
isindex++;
} while (r == 0);
return (r);
}
898
896
895
221
768
32
854
48
865
6
74
18
866
38
881
152
728
7
4
4
26
29
71
38
21
9
33
17
601
253
24
10
4
17
22
1
3
2
2
16
21
11
18
878
103
842
743
8
152
3
9
874
874
3
872
3
808
867
9
28
871
8
20
11
55
136
151
144
17
850
848
19
31
589
753
804
685
95
9
707
60
197
584
651
160
4
1
5
24
1
2
23
23
23
23
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
/* $OpenBSD: vfs_lookup.c,v 1.87 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_lookup.c 8.6 (Berkeley) 11/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/syslimits.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/mount.h>
#include <sys/errno.h>
#include <sys/pool.h>
#include <sys/filedesc.h>
#include <sys/proc.h>
#include <sys/pledge.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
int
component_push(struct componentname *cnp, char *component, size_t len)
{
if (cnp->cn_rpi + len + 1 >= MAXPATHLEN)
return 0;
if (cnp->cn_rpi > 1)
cnp->cn_rpbuf[cnp->cn_rpi++] = '/';
memcpy(cnp->cn_rpbuf + cnp->cn_rpi, component, len);
cnp->cn_rpi+=len;
cnp->cn_rpbuf[cnp->cn_rpi] = '\0';
return 1;
}
void
component_pop(struct componentname *cnp)
{
while(cnp->cn_rpi && cnp->cn_rpbuf[cnp->cn_rpi] != '/' )
cnp->cn_rpi--;
if (cnp->cn_rpi == 0 && cnp->cn_rpbuf[0] == '/')
cnp->cn_rpi++;
cnp->cn_rpbuf[cnp->cn_rpi] = '\0';
}
void
ndinitat(struct nameidata *ndp, u_long op, u_long flags,
enum uio_seg segflg, int dirfd, const char *namep, struct proc *p)
{
memset(ndp, 0, sizeof(*ndp));
ndp->ni_cnd.cn_nameiop = op;
ndp->ni_cnd.cn_flags = flags;
ndp->ni_segflg = segflg;
ndp->ni_dirfd = dirfd;
ndp->ni_dirp = namep;
ndp->ni_cnd.cn_proc = p;
}
/*
* Convert a pathname into a pointer to a vnode.
*
* The FOLLOW flag is set when symbolic links are to be followed
* when they occur at the end of the name translation process.
* Symbolic links are always followed for all other pathname
* components other than the last.
*
* If the LOCKLEAF flag is set, a locked vnode is returned.
*
* The segflg defines whether the name is to be copied from user
* space or kernel space.
*
* Overall outline of namei:
*
* copy in name
* get starting directory
* while (!done && !error) {
* call lookup to search path.
* if symbolic link, massage name in buffer and continue
* }
*/
int
namei(struct nameidata *ndp)
{
struct filedesc *fdp; /* pointer to file descriptor state */
char *cp; /* pointer into pathname argument */
struct vnode *dp; /* the directory we are searching */
struct iovec aiov; /* uio for reading symbolic links */
struct uio auio;
int error, linklen;
struct componentname *cnp = &ndp->ni_cnd;
struct proc *p = cnp->cn_proc;
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
#ifdef DIAGNOSTIC
if (!cnp->cn_cred || !cnp->cn_proc)
panic ("namei: bad cred/proc");
if (cnp->cn_nameiop & (~OPMASK))
panic ("namei: nameiop contaminated with flags");
if (cnp->cn_flags & OPMASK)
panic ("namei: flags contaminated with nameiops");
#endif
fdp = cnp->cn_proc->p_fd;
/*
* Get a buffer for the name to be translated, and copy the
* name into the buffer.
*/
if ((cnp->cn_flags & HASBUF) == 0)
cnp->cn_pnbuf = pool_get(&namei_pool, PR_WAITOK);
if (ndp->ni_segflg == UIO_SYSSPACE)
error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
MAXPATHLEN, &ndp->ni_pathlen);
else
error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
MAXPATHLEN, &ndp->ni_pathlen);
/*
* Fail on null pathnames
*/
if (error == 0 && ndp->ni_pathlen == 1)
error = ENOENT;
if (error)
goto fail;
#ifdef KTRACE
if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
ktrnamei(cnp->cn_proc, cnp->cn_pnbuf);
#endif
/*
* Strip trailing slashes, as requested
*/
if (cnp->cn_flags & STRIPSLASHES) {
char *end = cnp->cn_pnbuf + ndp->ni_pathlen - 2;
cp = end;
while (cp >= cnp->cn_pnbuf && (*cp == '/'))
cp--;
/* Still some remaining characters in the buffer */
if (cp >= cnp->cn_pnbuf) {
ndp->ni_pathlen -= (end - cp);
*(cp + 1) = '\0';
}
}
ndp->ni_loopcnt = 0;
/*
* Get starting point for the translation.
*/
if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL ||
(ndp->ni_cnd.cn_flags & KERNELPATH))
ndp->ni_rootdir = rootvnode;
if (ndp->ni_cnd.cn_flags & KERNELPATH) {
ndp->ni_cnd.cn_flags |= BYPASSUNVEIL;
} else {
error = pledge_namei(p, ndp, cnp->cn_pnbuf);
if (error)
goto fail;
}
/*
* Check if starting from root directory or current directory.
*/
if (cnp->cn_pnbuf[0] == '/') {
dp = ndp->ni_rootdir;
vref(dp);
if (cnp->cn_flags & REALPATH && cnp->cn_rpi == 0) {
cnp->cn_rpbuf[0] = '/';
cnp->cn_rpbuf[1] = '\0';
cnp->cn_rpi = 1;
}
} else if (ndp->ni_dirfd == AT_FDCWD) {
dp = fdp->fd_cdir;
vref(dp);
unveil_start_relative(p, ndp, dp);
unveil_check_component(p, ndp, dp);
} else {
struct file *fp = fd_getfile(fdp, ndp->ni_dirfd);
if (fp == NULL) {
error = EBADF;
goto fail;
}
dp = (struct vnode *)fp->f_data;
if (fp->f_type != DTYPE_VNODE || dp->v_type != VDIR) {
FRELE(fp, p);
error = ENOTDIR;
goto fail;
}
vref(dp);
unveil_start_relative(p, ndp, dp);
unveil_check_component(p, ndp, dp);
FRELE(fp, p);
}
for (;;) {
if (!dp->v_mount) {
/* Give up if the directory is no longer mounted */
vrele(dp);
error = ENOENT;
goto fail;
}
cnp->cn_nameptr = cnp->cn_pnbuf;
ndp->ni_startdir = dp;
if ((error = vfs_lookup(ndp)) != 0)
goto fail;
/*
* If not a symbolic link, return search result.
*/
if ((cnp->cn_flags & ISSYMLINK) == 0) {
if ((error = unveil_check_final(p, ndp))) {
if ((cnp->cn_flags & LOCKPARENT) &&
(cnp->cn_flags & ISLASTCN) &&
(ndp->ni_vp != ndp->ni_dvp))
vput(ndp->ni_dvp);
if (ndp->ni_vp) {
if ((cnp->cn_flags & LOCKLEAF))
vput(ndp->ni_vp);
else
vrele(ndp->ni_vp);
}
goto fail;
}
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
pool_put(&namei_pool, cnp->cn_pnbuf);
else
cnp->cn_flags |= HASBUF;
return (0);
}
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
VOP_UNLOCK(ndp->ni_dvp);
if (ndp->ni_loopcnt++ >= SYMLOOP_MAX) {
error = ELOOP;
break;
}
if (ndp->ni_pathlen > 1)
cp = pool_get(&namei_pool, PR_WAITOK);
else
cp = cnp->cn_pnbuf;
aiov.iov_base = cp;
aiov.iov_len = MAXPATHLEN;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_procp = cnp->cn_proc;
auio.uio_resid = MAXPATHLEN;
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
if (error) {
badlink:
if (ndp->ni_pathlen > 1)
pool_put(&namei_pool, cp);
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
if (linklen == 0) {
error = ENOENT;
goto badlink;
}
if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
error = ENAMETOOLONG;
goto badlink;
}
if (ndp->ni_pathlen > 1) {
memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen);
pool_put(&namei_pool, cnp->cn_pnbuf);
cnp->cn_pnbuf = cp;
} else
cnp->cn_pnbuf[linklen] = '\0';
ndp->ni_pathlen += linklen;
vput(ndp->ni_vp);
dp = ndp->ni_dvp;
/*
* Check if root directory should replace current directory.
*/
if (cnp->cn_pnbuf[0] == '/') {
vrele(dp);
dp = ndp->ni_rootdir;
vref(dp);
ndp->ni_unveil_match = NULL;
unveil_check_component(p, ndp, dp);
if (cnp->cn_flags & REALPATH) {
cnp->cn_rpbuf[0] = '/';
cnp->cn_rpbuf[1] = '\0';
cnp->cn_rpi = 1;
}
} else if (cnp->cn_flags & REALPATH) {
component_pop(cnp);
}
}
vrele(ndp->ni_dvp);
vput(ndp->ni_vp);
fail:
pool_put(&namei_pool, cnp->cn_pnbuf);
ndp->ni_vp = NULL;
return (error);
}
/*
* Search a pathname.
* This is a very central and rather complicated routine.
*
* The pathname is pointed to by ni_cnd.cn_nameptr and is of length
* ni_pathlen. The starting directory is taken from ni_startdir. The
* pathname is descended until done, or a symbolic link is encountered.
* If the path is completed the flag ISLASTCN is set in ni_cnd.cn_flags.
* If a symbolic link need interpretation is encountered, the flag ISSYMLINK
* is set in ni_cnd.cn_flags.
*
* The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
* whether the name is to be looked up, created, renamed, or deleted.
* When CREATE, RENAME, or DELETE is specified, information usable in
* creating, renaming, or deleting a directory entry may be calculated.
* If flag has LOCKPARENT or'ed into it, the parent directory is returned
* locked. If flag has WANTPARENT or'ed into it, the parent directory is
* returned unlocked. Otherwise the parent directory is not returned. If
* the target of the pathname exists and LOCKLEAF is or'ed into the flag
* the target is returned locked, otherwise it is returned unlocked.
* When creating or renaming and LOCKPARENT is specified, the target may not
* be ".". When deleting and LOCKPARENT is specified, the target may be ".".
*
* Overall outline of lookup:
*
* dirloop:
* identify next component of name at ndp->ni_ptr
* handle degenerate case where name is null string
* if .. and crossing mount points and on mounted filesys, find parent
* call VOP_LOOKUP routine for next component name
* directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
* component vnode returned in ni_vp (if it exists), locked.
* if result vnode is mounted on and crossing mount points,
* find mounted on vnode
* if more components of name, do next level at dirloop
* return the answer in ni_vp, locked if LOCKLEAF set
* if LOCKPARENT set, return locked parent in ni_dvp
* if WANTPARENT set, return unlocked parent in ni_dvp
*/
int
vfs_lookup(struct nameidata *ndp)
{
char *cp; /* pointer into pathname argument */
struct vnode *dp = NULL; /* the directory we are searching */
struct vnode *tdp; /* saved dp */
struct mount *mp; /* mount table entry */
int docache; /* == 0 do not cache last component */
int wantparent; /* 1 => wantparent or lockparent flag */
int rdonly; /* lookup read-only flag bit */
int error = 0;
int dpunlocked = 0; /* dp has already been unlocked */
int slashes;
struct componentname *cnp = &ndp->ni_cnd;
/*
* Setup: break out flag bits into variables.
*/
wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
if (cnp->cn_nameiop == DELETE ||
(wantparent && cnp->cn_nameiop != CREATE))
docache = 0;
rdonly = cnp->cn_flags & RDONLY;
ndp->ni_dvp = NULL;
cnp->cn_flags &= ~ISSYMLINK;
dp = ndp->ni_startdir;
ndp->ni_startdir = NULLVP;
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
/*
* If we have a leading string of slashes, remove them, and just make
* sure the current node is a directory.
*/
cp = cnp->cn_nameptr;
if (*cp == '/') {
do {
cp++;
} while (*cp == '/');
ndp->ni_pathlen -= cp - cnp->cn_nameptr;
cnp->cn_nameptr = cp;
if (dp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
/*
* If we've exhausted the path name, then just return the
* current node. If the caller requested the parent node (i.e.
* it's a CREATE, DELETE, or RENAME), and we don't have one
* (because this is the root directory), then we must fail.
*/
if (cnp->cn_nameptr[0] == '\0') {
if (ndp->ni_dvp == NULL && wantparent) {
error = EISDIR;
goto bad;
}
ndp->ni_vp = dp;
cnp->cn_flags |= ISLASTCN;
goto terminal;
}
}
dirloop:
/*
* Search a new directory.
*
* The last component of the filename is left accessible via
* cnp->cn_nameptr for callers that need the name. Callers needing
* the name set the SAVENAME flag. When done, they assume
* responsibility for freeing the pathname buffer.
*/
cnp->cn_consume = 0;
/* XXX: Figure out the length of the last component. */
cp = cnp->cn_nameptr;
while (*cp && (*cp != '/'))
cp++;
cnp->cn_namelen = cp - cnp->cn_nameptr;
if (cnp->cn_namelen > NAME_MAX) {
error = ENAMETOOLONG;
goto bad;
}
#ifdef NAMEI_DIAGNOSTIC
{ char c = *cp;
*cp = '\0';
printf("{%s}: ", cnp->cn_nameptr);
*cp = c; }
#endif
if (cnp->cn_flags & REALPATH) {
size_t len = cp - cnp->cn_nameptr;
if (len == 2 && cnp->cn_nameptr[0] == '.' &&
cnp->cn_nameptr[1] == '.')
component_pop(cnp);
else if (!(len == 1 && cnp->cn_nameptr[0] == '.')) {
if (!component_push(cnp, cnp->cn_nameptr, len)) {
error = ENAMETOOLONG;
goto bad;
}
}
}
ndp->ni_pathlen -= cnp->cn_namelen;
ndp->ni_next = cp;
/*
* If this component is followed by a slash, then move the pointer to
* the next component forward, and remember that this component must be
* a directory.
*/
if (*cp == '/') {
do {
cp++;
} while (*cp == '/');
slashes = cp - ndp->ni_next;
ndp->ni_pathlen -= slashes;
ndp->ni_next = cp;
cnp->cn_flags |= REQUIREDIR;
} else {
slashes = 0;
cnp->cn_flags &= ~REQUIREDIR;
}
/*
* We do special processing on the last component, whether or not it's
* a directory. Cache all intervening lookups, but not the final one.
*/
if (*cp == '\0') {
if (docache)
cnp->cn_flags |= MAKEENTRY;
else
cnp->cn_flags &= ~MAKEENTRY;
cnp->cn_flags |= ISLASTCN;
} else {
cnp->cn_flags |= MAKEENTRY;
cnp->cn_flags &= ~ISLASTCN;
}
if (cnp->cn_namelen == 2 &&
cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
cnp->cn_flags |= ISDOTDOT;
else
cnp->cn_flags &= ~ISDOTDOT;
/*
* Handle "..": two special cases.
* 1. If at root directory (e.g. after chroot)
* or at absolute root directory
* then ignore it so can't get out.
* 2. If this vnode is the root of a mounted
* filesystem, then replace it with the
* vnode which was mounted on so we take the
* .. in the other file system.
*/
if (cnp->cn_flags & ISDOTDOT) {
for (;;) {
if (dp == ndp->ni_rootdir || dp == rootvnode) {
ndp->ni_dvp = dp;
ndp->ni_vp = dp;
vref(dp);
ndp->ni_unveil_match = NULL;
goto nextname;
}
if ((dp->v_flag & VROOT) == 0 ||
(cnp->cn_flags & NOCROSSMOUNT))
break;
tdp = dp;
dp = dp->v_mount->mnt_vnodecovered;
vput(tdp);
vref(dp);
unveil_check_component(curproc, ndp, dp);
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
}
}
/*
* We now have a segment name to search for, and a directory to search.
*/
ndp->ni_dvp = dp;
ndp->ni_vp = NULL;
cnp->cn_flags &= ~PDIRUNLOCK;
unveil_check_component(curproc, ndp, dp);
if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
#ifdef DIAGNOSTIC
if (ndp->ni_vp != NULL)
panic("leaf should be empty");
#endif
#ifdef NAMEI_DIAGNOSTIC
printf("not found\n");
#endif
/*
* Allow for unveiling a file in a directory which we cannot
* create ourselves.
*/
if (ndp->ni_pledge == PLEDGE_UNVEIL &&
(error == EPERM || error == EACCES || error == EROFS))
error = EJUSTRETURN;
if (error != EJUSTRETURN)
goto bad;
/*
* If this was not the last component, or there were trailing
* slashes, then the name must exist.
*/
if (cnp->cn_flags & REQUIREDIR) {
error = ENOENT;
goto bad;
}
/*
* If creating and at end of pathname, then can consider
* allowing file to be created. Check for a read only
* filesystem and disallow this unless we are unveil'ing
*/
if (ndp->ni_pledge != PLEDGE_UNVEIL && (rdonly ||
(ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
error = EROFS;
goto bad;
}
/*
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory inode in ndp->ni_dvp.
*/
if (cnp->cn_flags & SAVESTART) {
ndp->ni_startdir = ndp->ni_dvp;
vref(ndp->ni_startdir);
}
return (0);
}
#ifdef NAMEI_DIAGNOSTIC
printf("found\n");
#endif
/*
* Take into account any additional components consumed by the
* underlying filesystem. This will include any trailing slashes after
* the last component consumed.
*/
if (cnp->cn_consume > 0) {
if (cnp->cn_consume >= slashes) {
cnp->cn_flags &= ~REQUIREDIR;
}
ndp->ni_pathlen -= cnp->cn_consume - slashes;
ndp->ni_next += cnp->cn_consume - slashes;
cnp->cn_consume = 0;
if (ndp->ni_next[0] == '\0')
cnp->cn_flags |= ISLASTCN;
}
dp = ndp->ni_vp;
/*
* Check to see if the vnode has been mounted on;
* if so find the root of the mounted file system.
*/
while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
(cnp->cn_flags & NOCROSSMOUNT) == 0) {
if (vfs_busy(mp, VB_READ|VB_WAIT))
continue;
VOP_UNLOCK(dp);
error = VFS_ROOT(mp, &tdp);
vfs_unbusy(mp);
if (error) {
dpunlocked = 1;
goto bad2;
}
vrele(dp);
ndp->ni_vp = dp = tdp;
}
/*
* Check for symbolic link. Back up over any slashes that we skipped,
* as we will need them again.
*/
if ((dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
ndp->ni_pathlen += slashes;
ndp->ni_next -= slashes;
cnp->cn_flags |= ISSYMLINK;
return (0);
}
/*
* Check for directory, if the component was followed by a series of
* slashes.
*/
if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
error = ENOTDIR;
goto bad2;
}
nextname:
/*
* Not a symbolic link. If this was not the last component, then
* continue at the next component, else return.
*/
if (!(cnp->cn_flags & ISLASTCN)) {
cnp->cn_nameptr = ndp->ni_next;
vrele(ndp->ni_dvp);
goto dirloop;
}
terminal:
/*
* Check for read-only file systems.
*/
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
/*
* Disallow directory write attempts on read-only
* file systems.
*/
if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
(wantparent &&
(ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
error = EROFS;
goto bad2;
}
}
if (ndp->ni_dvp != NULL) {
if (cnp->cn_flags & SAVESTART) {
ndp->ni_startdir = ndp->ni_dvp;
vref(ndp->ni_startdir);
}
if (!wantparent)
vrele(ndp->ni_dvp);
}
if ((cnp->cn_flags & LOCKLEAF) == 0)
VOP_UNLOCK(dp);
return (0);
bad2:
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN) &&
((cnp->cn_flags & PDIRUNLOCK) == 0))
VOP_UNLOCK(ndp->ni_dvp);
vrele(ndp->ni_dvp);
bad:
if (dpunlocked)
vrele(dp);
else
vput(dp);
ndp->ni_vp = NULL;
return (error);
}
/*
* Reacquire a path name component.
*/
int
vfs_relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
{
struct vnode *dp = NULL; /* the directory we are searching */
int wantparent; /* 1 => wantparent or lockparent flag */
int rdonly; /* lookup read-only flag bit */
int error = 0;
#ifdef NAMEI_DIAGNOSTIC
char *cp; /* DEBUG: check name ptr/len */
#endif
/*
* Setup: break out flag bits into variables.
*/
wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
rdonly = cnp->cn_flags & RDONLY;
cnp->cn_flags &= ~ISSYMLINK;
dp = dvp;
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
/* dirloop: */
/*
* Search a new directory.
*
* The last component of the filename is left accessible via
* cnp->cn_nameptr for callers that need the name. Callers needing
* the name set the SAVENAME flag. When done, they assume
* responsibility for freeing the pathname buffer.
*/
#ifdef NAMEI_DIAGNOSTIC
/* XXX: Figure out the length of the last component. */
cp = cnp->cn_nameptr;
while (*cp && (*cp != '/')) {
cp++;
}
if (cnp->cn_namelen != cp - cnp->cn_nameptr)
panic("relookup: bad len");
if (*cp != 0)
panic("relookup: not last component");
printf("{%s}: ", cnp->cn_nameptr);
#endif
/*
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
* e.g. like "/." or ".".
*/
if (cnp->cn_nameptr[0] == '\0')
panic("relookup: null name");
if (cnp->cn_flags & ISDOTDOT)
panic ("relookup: lookup on dot-dot");
/*
* We now have a segment name to search for, and a directory to search.
*/
if ((error = VOP_LOOKUP(dp, vpp, cnp)) != 0) {
#ifdef DIAGNOSTIC
if (*vpp != NULL)
panic("leaf should be empty");
#endif
if (error != EJUSTRETURN)
goto bad;
/*
* If creating and at end of pathname, then can consider
* allowing file to be created.
*/
if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) {
error = EROFS;
goto bad;
}
/* ASSERT(dvp == ndp->ni_startdir) */
if (cnp->cn_flags & SAVESTART)
vref(dvp);
/*
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory inode in ndp->ni_dvp.
*/
return (0);
}
dp = *vpp;
#ifdef DIAGNOSTIC
/*
* Check for symbolic link
*/
if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
panic ("relookup: symlink found.");
#endif
/*
* Check for read-only file systems.
*/
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
/*
* Disallow directory write attempts on read-only
* file systems.
*/
if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
(wantparent &&
(dvp->v_mount->mnt_flag & MNT_RDONLY))) {
error = EROFS;
goto bad2;
}
}
/* ASSERT(dvp == ndp->ni_startdir) */
if (cnp->cn_flags & SAVESTART)
vref(dvp);
if (!wantparent)
vrele(dvp);
if ((cnp->cn_flags & LOCKLEAF) == 0)
VOP_UNLOCK(dp);
return (0);
bad2:
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
VOP_UNLOCK(dvp);
vrele(dvp);
bad:
vput(dp);
*vpp = NULL;
return (error);
}
2161
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
/* $OpenBSD: tsc.c,v 1.26 2022/08/25 17:38:16 cheloha Exp $ */
/*
* Copyright (c) 2008 The NetBSD Foundation, Inc.
* Copyright (c) 2016,2017 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2017 Adam Steen <adam@adamsteen.com.au>
* Copyright (c) 2017 Mike Belopuhov <mike@openbsd.org>
* Copyright (c) 2019 Paul Irofti <paul@irofti.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timetc.h>
#include <sys/atomic.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#define RECALIBRATE_MAX_RETRIES 5
#define RECALIBRATE_SMI_THRESHOLD 50000
#define RECALIBRATE_DELAY_THRESHOLD 50
int tsc_recalibrate;
uint64_t tsc_frequency;
int tsc_is_invariant;
u_int tsc_get_timecount(struct timecounter *tc);
void tsc_delay(int usecs);
#include "lapic.h"
#if NLAPIC > 0
extern u_int32_t lapic_per_second;
#endif
struct timecounter tsc_timecounter = {
.tc_get_timecount = tsc_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_name = "tsc",
.tc_quality = -1000,
.tc_priv = NULL,
.tc_user = TC_TSC,
};
uint64_t
tsc_freq_cpuid(struct cpu_info *ci)
{
uint64_t count;
uint32_t eax, ebx, khz, dummy;
if (!strcmp(cpu_vendor, "GenuineIntel") &&
cpuid_level >= 0x15) {
eax = ebx = khz = dummy = 0;
CPUID(0x15, eax, ebx, khz, dummy);
khz /= 1000;
if (khz == 0) {
switch (ci->ci_model) {
case 0x4e: /* Skylake mobile */
case 0x5e: /* Skylake desktop */
case 0x8e: /* Kabylake mobile */
case 0x9e: /* Kabylake desktop */
case 0xa5: /* CML-H CML-S62 CML-S102 */
case 0xa6: /* CML-U62 */
khz = 24000; /* 24.0 MHz */
break;
case 0x5f: /* Atom Denverton */
khz = 25000; /* 25.0 MHz */
break;
case 0x5c: /* Atom Goldmont */
khz = 19200; /* 19.2 MHz */
break;
}
}
if (ebx == 0 || eax == 0)
count = 0;
else if ((count = (uint64_t)khz * (uint64_t)ebx / eax) != 0) {
#if NLAPIC > 0
lapic_per_second = khz * 1000;
#endif
return (count * 1000);
}
}
return (0);
}
void
tsc_identify(struct cpu_info *ci)
{
if (!(ci->ci_flags & CPUF_PRIMARY) ||
!(ci->ci_flags & CPUF_CONST_TSC) ||
!(ci->ci_flags & CPUF_INVAR_TSC))
return;
tsc_is_invariant = 1;
tsc_frequency = tsc_freq_cpuid(ci);
if (tsc_frequency > 0)
delay_init(tsc_delay, 5000);
}
static inline int
get_tsc_and_timecount(struct timecounter *tc, uint64_t *tsc, uint64_t *count)
{
uint64_t n, tsc1, tsc2;
int i;
for (i = 0; i < RECALIBRATE_MAX_RETRIES; i++) {
tsc1 = rdtsc_lfence();
n = (tc->tc_get_timecount(tc) & tc->tc_counter_mask);
tsc2 = rdtsc_lfence();
if ((tsc2 - tsc1) < RECALIBRATE_SMI_THRESHOLD) {
*count = n;
*tsc = tsc2;
return (0);
}
}
return (1);
}
static inline uint64_t
calculate_tsc_freq(uint64_t tsc1, uint64_t tsc2, int usec)
{
uint64_t delta;
delta = (tsc2 - tsc1);
return (delta * 1000000 / usec);
}
static inline uint64_t
calculate_tc_delay(struct timecounter *tc, uint64_t count1, uint64_t count2)
{
uint64_t delta;
if (count2 < count1)
count2 += tc->tc_counter_mask;
delta = (count2 - count1);
return (delta * 1000000 / tc->tc_frequency);
}
uint64_t
measure_tsc_freq(struct timecounter *tc)
{
uint64_t count1, count2, frequency, min_freq, tsc1, tsc2;
u_long s;
int delay_usec, i, err1, err2, usec, success = 0;
/* warmup the timers */
for (i = 0; i < 3; i++) {
(void)tc->tc_get_timecount(tc);
(void)rdtsc();
}
min_freq = ULLONG_MAX;
delay_usec = 100000;
for (i = 0; i < 3; i++) {
s = intr_disable();
err1 = get_tsc_and_timecount(tc, &tsc1, &count1);
delay(delay_usec);
err2 = get_tsc_and_timecount(tc, &tsc2, &count2);
intr_restore(s);
if (err1 || err2)
continue;
usec = calculate_tc_delay(tc, count1, count2);
if ((usec < (delay_usec - RECALIBRATE_DELAY_THRESHOLD)) ||
(usec > (delay_usec + RECALIBRATE_DELAY_THRESHOLD)))
continue;
frequency = calculate_tsc_freq(tsc1, tsc2, usec);
min_freq = MIN(min_freq, frequency);
success++;
}
return (success > 1 ? min_freq : 0);
}
void
calibrate_tsc_freq(void)
{
struct timecounter *reference = tsc_timecounter.tc_priv;
uint64_t freq;
if (!reference || !tsc_recalibrate)
return;
if ((freq = measure_tsc_freq(reference)) == 0)
return;
tsc_frequency = freq;
tsc_timecounter.tc_frequency = freq;
if (tsc_is_invariant)
tsc_timecounter.tc_quality = 2000;
}
void
cpu_recalibrate_tsc(struct timecounter *tc)
{
struct timecounter *reference = tsc_timecounter.tc_priv;
/* Prevent recalibration with a worse timecounter source */
if (reference && reference->tc_quality > tc->tc_quality)
return;
tsc_timecounter.tc_priv = tc;
calibrate_tsc_freq();
}
u_int
tsc_get_timecount(struct timecounter *tc)
{
return rdtsc_lfence();
}
void
tsc_timecounter_init(struct cpu_info *ci, uint64_t cpufreq)
{
if (!(ci->ci_flags & CPUF_PRIMARY) ||
!(ci->ci_flags & CPUF_CONST_TSC) ||
!(ci->ci_flags & CPUF_INVAR_TSC))
return;
/* Newer CPUs don't require recalibration */
if (tsc_frequency > 0) {
tsc_timecounter.tc_frequency = tsc_frequency;
tsc_timecounter.tc_quality = 2000;
} else {
tsc_recalibrate = 1;
tsc_frequency = cpufreq;
tsc_timecounter.tc_frequency = cpufreq;
calibrate_tsc_freq();
}
tc_init(&tsc_timecounter);
}
void
tsc_delay(int usecs)
{
uint64_t interval, start;
interval = (uint64_t)usecs * tsc_frequency / 1000000;
start = rdtsc_lfence();
while (rdtsc_lfence() - start < interval)
CPU_BUSY_CYCLE();
}
#ifdef MULTIPROCESSOR
#define TSC_DEBUG 1
/*
* Protections for global variables in this code:
*
* a Modified atomically
* b Protected by a barrier
* p Only modified by the primary CPU
*/
#define TSC_TEST_MSECS 1 /* Test round duration */
#define TSC_TEST_ROUNDS 2 /* Number of test rounds */
/*
* tsc_test_status.val is isolated to its own cache line to limit
* false sharing and reduce the test's margin of error.
*/
struct tsc_test_status {
volatile uint64_t val; /* [a] Latest RDTSC value */
uint64_t pad1[7];
uint64_t lag_count; /* [b] Number of lags seen by CPU */
uint64_t lag_max; /* [b] Biggest lag seen by CPU */
int64_t adj; /* [b] Initial IA32_TSC_ADJUST value */
uint64_t pad2[5];
} __aligned(64);
struct tsc_test_status tsc_ap_status; /* Test results from AP */
struct tsc_test_status tsc_bp_status; /* Test results from BP */
uint64_t tsc_test_cycles; /* [p] TSC cycles per test round */
const char *tsc_ap_name; /* [b] Name of AP running test */
volatile u_int tsc_egress_barrier; /* [a] Test end barrier */
volatile u_int tsc_ingress_barrier; /* [a] Test start barrier */
volatile u_int tsc_test_rounds; /* [p] Remaining test rounds */
int tsc_is_synchronized = 1; /* [p] Have we ever failed the test? */
void tsc_report_test_results(void);
void tsc_reset_adjust(struct tsc_test_status *);
void tsc_test_ap(void);
void tsc_test_bp(void);
void
tsc_test_sync_bp(struct cpu_info *ci)
{
if (!tsc_is_invariant)
return;
#ifndef TSC_DEBUG
/* No point in testing again if we already failed. */
if (!tsc_is_synchronized)
return;
#endif
/* Reset IA32_TSC_ADJUST if it exists. */
tsc_reset_adjust(&tsc_bp_status);
/* Reset the test cycle limit and round count. */
tsc_test_cycles = TSC_TEST_MSECS * tsc_frequency / 1000;
tsc_test_rounds = TSC_TEST_ROUNDS;
do {
/*
* Pass through the ingress barrier, run the test,
* then wait for the AP to reach the egress barrier.
*/
atomic_inc_int(&tsc_ingress_barrier);
while (tsc_ingress_barrier != 2)
CPU_BUSY_CYCLE();
tsc_test_bp();
while (tsc_egress_barrier != 1)
CPU_BUSY_CYCLE();
/*
* Report what happened. Adjust the TSC's quality
* if this is the first time we've failed the test.
*/
tsc_report_test_results();
if (tsc_ap_status.lag_count || tsc_bp_status.lag_count) {
if (tsc_is_synchronized) {
tsc_is_synchronized = 0;
tc_reset_quality(&tsc_timecounter, -1000);
}
tsc_test_rounds = 0;
} else
tsc_test_rounds--;
/*
* Clean up for the next round. It is safe to reset the
* ingress barrier because at this point we know the AP
* has reached the egress barrier.
*/
memset(&tsc_ap_status, 0, sizeof tsc_ap_status);
memset(&tsc_bp_status, 0, sizeof tsc_bp_status);
tsc_ingress_barrier = 0;
if (tsc_test_rounds == 0)
tsc_ap_name = NULL;
/*
* Pass through the egress barrier and release the AP.
* The AP is responsible for resetting the egress barrier.
*/
if (atomic_inc_int_nv(&tsc_egress_barrier) != 2)
panic("%s: unexpected egress count", __func__);
} while (tsc_test_rounds > 0);
}
void
tsc_test_sync_ap(struct cpu_info *ci)
{
if (!tsc_is_invariant)
return;
#ifndef TSC_DEBUG
if (!tsc_is_synchronized)
return;
#endif
/* The BP needs our name in order to report any problems. */
if (atomic_cas_ptr(&tsc_ap_name, NULL, ci->ci_dev->dv_xname) != NULL) {
panic("%s: %s: tsc_ap_name is not NULL: %s",
__func__, ci->ci_dev->dv_xname, tsc_ap_name);
}
tsc_reset_adjust(&tsc_ap_status);
/*
* The AP is only responsible for running the test and
* resetting the egress barrier. The BP handles everything
* else.
*/
do {
atomic_inc_int(&tsc_ingress_barrier);
while (tsc_ingress_barrier != 2)
CPU_BUSY_CYCLE();
tsc_test_ap();
atomic_inc_int(&tsc_egress_barrier);
while (atomic_cas_uint(&tsc_egress_barrier, 2, 0) != 2)
CPU_BUSY_CYCLE();
} while (tsc_test_rounds > 0);
}
void
tsc_report_test_results(void)
{
u_int round = TSC_TEST_ROUNDS - tsc_test_rounds + 1;
if (tsc_bp_status.adj != 0) {
printf("tsc: cpu0: IA32_TSC_ADJUST: %lld -> 0\n",
tsc_bp_status.adj);
}
if (tsc_ap_status.adj != 0) {
printf("tsc: %s: IA32_TSC_ADJUST: %lld -> 0\n",
tsc_ap_name, tsc_ap_status.adj);
}
if (tsc_ap_status.lag_count > 0 || tsc_bp_status.lag_count > 0) {
printf("tsc: cpu0/%s: sync test round %u/%u failed\n",
tsc_ap_name, round, TSC_TEST_ROUNDS);
}
if (tsc_bp_status.lag_count > 0) {
printf("tsc: cpu0/%s: cpu0: %llu lags %llu cycles\n",
tsc_ap_name, tsc_bp_status.lag_count,
tsc_bp_status.lag_max);
}
if (tsc_ap_status.lag_count > 0) {
printf("tsc: cpu0/%s: %s: %llu lags %llu cycles\n",
tsc_ap_name, tsc_ap_name, tsc_ap_status.lag_count,
tsc_ap_status.lag_max);
}
}
/*
* Reset IA32_TSC_ADJUST if we have it.
*
* XXX We should rearrange cpu_hatch() so that the feature
* flags are already set before we get here. Check CPUID
* by hand until then.
*/
void
tsc_reset_adjust(struct tsc_test_status *tts)
{
uint32_t eax, ebx, ecx, edx;
CPUID(0, eax, ebx, ecx, edx);
if (eax >= 7) {
CPUID_LEAF(7, 0, eax, ebx, ecx, edx);
if (ISSET(ebx, SEFF0EBX_TSC_ADJUST)) {
tts->adj = rdmsr(MSR_TSC_ADJUST);
if (tts->adj != 0)
wrmsr(MSR_TSC_ADJUST, 0);
}
}
}
void
tsc_test_ap(void)
{
uint64_t ap_val, bp_val, end, lag;
ap_val = rdtsc_lfence();
end = ap_val + tsc_test_cycles;
while (__predict_true(ap_val < end)) {
/*
* Get the BP's latest TSC value, then read the AP's
* TSC. LFENCE is a serializing instruction, so we
* know bp_val predates ap_val. If ap_val is smaller
* than bp_val then the AP's TSC must trail that of
* the BP and the counters cannot be synchronized.
*/
bp_val = tsc_bp_status.val;
ap_val = rdtsc_lfence();
tsc_ap_status.val = ap_val;
/*
* Record the magnitude of the problem if the AP's TSC
* trails the BP's TSC.
*/
if (__predict_false(ap_val < bp_val)) {
tsc_ap_status.lag_count++;
lag = bp_val - ap_val;
if (tsc_ap_status.lag_max < lag)
tsc_ap_status.lag_max = lag;
}
}
}
/*
* This is similar to tsc_test_ap(), but with all relevant variables
* flipped around to run the test from the BP's perspective.
*/
void
tsc_test_bp(void)
{
uint64_t ap_val, bp_val, end, lag;
bp_val = rdtsc_lfence();
end = bp_val + tsc_test_cycles;
while (__predict_true(bp_val < end)) {
ap_val = tsc_ap_status.val;
bp_val = rdtsc_lfence();
tsc_bp_status.val = bp_val;
if (__predict_false(bp_val < ap_val)) {
tsc_bp_status.lag_count++;
lag = ap_val - bp_val;
if (tsc_bp_status.lag_max < lag)
tsc_bp_status.lag_max = lag;
}
}
}
#endif /* MULTIPROCESSOR */
4
1
3
2
1
1
3
1
1
1
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/* $OpenBSD: pf_syncookies.c,v 1.7 2018/09/10 15:54:28 henning Exp $ */
/* Copyright (c) 2016,2017 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2016 Alexandr Nedvedicky <sashan@openbsd.org>
*
* syncookie parts based on FreeBSD sys/netinet/tcp_syncache.c
*
* Copyright (c) 2001 McAfee, Inc.
* Copyright (c) 2006,2013 Andre Oppermann, Internet Business Solutions AG
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Jonathan Lemon
* and McAfee Research, the Security Research Division of McAfee, Inc. under
* DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
* DARPA CHATS research program. [2001 McAfee, Inc.]
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* when we're under synflood, we use syncookies to prevent state table
* exhaustion. Trigger for the synflood mode is the number of half-open
* connections in the state table.
* We leave synflood mode when the number of half-open states - including
* in-flight syncookies - drops far enough again
*/
/*
* syncookie enabled Initial Sequence Number:
* 24 bit MAC
* 3 bit WSCALE index
* 3 bit MSS index
* 1 bit SACK permitted
* 1 bit odd/even secret
*
* References:
* RFC4987 TCP SYN Flooding Attacks and Common Mitigations
* http://cr.yp.to/syncookies.html (overview)
* http://cr.yp.to/syncookies/archive (details)
*/
#include "pflog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_fsm.h>
#include <netinet/udp_var.h>
#include <netinet/icmp_var.h>
#include <netinet/ip_divert.h>
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#if NPFLOG > 0
#include <net/if_pflog.h>
#endif /* NPFLOG > 0 */
union pf_syncookie {
uint8_t cookie;
struct {
uint8_t oddeven:1,
sack_ok:1,
wscale_idx:3,
mss_idx:3;
} flags;
};
#define PF_SYNCOOKIE_SECRET_SIZE SIPHASH_KEY_LENGTH
#define PF_SYNCOOKIE_SECRET_LIFETIME 15 /* seconds */
static struct {
struct timeout keytimeout;
volatile uint oddeven;
SIPHASH_KEY key[2];
uint32_t hiwat; /* absolute; # of states */
uint32_t lowat;
} pf_syncookie_status;
void pf_syncookie_rotate(void *);
void pf_syncookie_newkey(void);
uint32_t pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie,
uint32_t);
uint32_t pf_syncookie_generate(struct pf_pdesc *, uint16_t);
void
pf_syncookies_init(void)
{
timeout_set(&pf_syncookie_status.keytimeout,
pf_syncookie_rotate, NULL);
pf_syncookie_status.hiwat = PFSTATE_HIWAT * PF_SYNCOOKIES_HIWATPCT/100;
pf_syncookie_status.lowat = PFSTATE_HIWAT * PF_SYNCOOKIES_LOWATPCT/100;
pf_syncookies_setmode(PF_SYNCOOKIES_NEVER);
}
int
pf_syncookies_setmode(u_int8_t mode)
{
if (mode > PF_SYNCOOKIES_MODE_MAX)
return (EINVAL);
if (pf_status.syncookies_mode == mode)
return (0);
pf_status.syncookies_mode = mode;
if (pf_status.syncookies_mode == PF_SYNCOOKIES_ALWAYS) {
pf_syncookie_newkey();
pf_status.syncookies_active = 1;
}
return (0);
}
int
pf_syncookies_setwats(u_int32_t hiwat, u_int32_t lowat)
{
if (lowat > hiwat)
return (EINVAL);
pf_syncookie_status.hiwat = hiwat;
pf_syncookie_status.lowat = lowat;
return (0);
}
int
pf_syncookies_getwats(struct pfioc_synflwats *wats)
{
wats->hiwat = pf_syncookie_status.hiwat;
wats->lowat = pf_syncookie_status.lowat;
return (0);
}
int
pf_synflood_check(struct pf_pdesc *pd)
{
KASSERT (pd->proto == IPPROTO_TCP);
if (pd->m && (pd->m->m_pkthdr.pf.tag & PF_TAG_SYNCOOKIE_RECREATED))
return (0);
if (pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE)
return (pf_status.syncookies_mode);
if (!pf_status.syncookies_active &&
pf_status.states_halfopen > pf_syncookie_status.hiwat) {
pf_syncookie_newkey();
pf_status.syncookies_active = 1;
DPFPRINTF(LOG_WARNING,
"synflood detected, enabling syncookies");
pf_status.lcounters[LCNT_SYNFLOODS]++;
}
return (pf_status.syncookies_active);
}
void
pf_syncookie_send(struct pf_pdesc *pd)
{
uint16_t mss;
uint32_t iss;
mss = max(tcp_mssdflt, pf_get_mss(pd));
iss = pf_syncookie_generate(pd, mss);
pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
0, 1, 0, pd->rdomain);
pf_status.syncookies_inflight[pf_syncookie_status.oddeven]++;
pf_status.lcounters[LCNT_SYNCOOKIES_SENT]++;
}
uint8_t
pf_syncookie_validate(struct pf_pdesc *pd)
{
uint32_t hash, ack, seq;
union pf_syncookie cookie;
KASSERT(pd->proto == IPPROTO_TCP);
seq = ntohl(pd->hdr.tcp.th_seq) - 1;
ack = ntohl(pd->hdr.tcp.th_ack) - 1;
cookie.cookie = (ack & 0xff) ^ (ack >> 24);
/* we don't know oddeven before setting the cookie (union) */
if (pf_status.syncookies_inflight[cookie.flags.oddeven] == 0)
return (0);
hash = pf_syncookie_mac(pd, cookie, seq);
if ((ack & ~0xff) != (hash & ~0xff))
return (0);
pf_status.syncookies_inflight[cookie.flags.oddeven]--;
pf_status.lcounters[LCNT_SYNCOOKIES_VALID]++;
return (1);
}
/*
* all following functions private
*/
void
pf_syncookie_rotate(void *arg)
{
/* do we want to disable syncookies? */
if (pf_status.syncookies_active &&
((pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE &&
pf_status.states_halfopen + pf_status.syncookies_inflight[0] +
pf_status.syncookies_inflight[1] < pf_syncookie_status.lowat) ||
pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER)) {
pf_status.syncookies_active = 0;
DPFPRINTF(LOG_WARNING, "syncookies disabled");
}
/* nothing in flight any more? delete keys and return */
if (!pf_status.syncookies_active &&
pf_status.syncookies_inflight[0] == 0 &&
pf_status.syncookies_inflight[1] == 0) {
memset(&pf_syncookie_status.key[0], 0,
PF_SYNCOOKIE_SECRET_SIZE);
memset(&pf_syncookie_status.key[1], 0,
PF_SYNCOOKIE_SECRET_SIZE);
return;
}
/* new key, including timeout */
pf_syncookie_newkey();
}
void
pf_syncookie_newkey(void)
{
pf_syncookie_status.oddeven = (pf_syncookie_status.oddeven + 1) & 0x1;
pf_status.syncookies_inflight[pf_syncookie_status.oddeven] = 0;
arc4random_buf(&pf_syncookie_status.key[pf_syncookie_status.oddeven],
PF_SYNCOOKIE_SECRET_SIZE);
timeout_add_sec(&pf_syncookie_status.keytimeout,
PF_SYNCOOKIE_SECRET_LIFETIME);
}
/*
* Distribution and probability of certain MSS values. Those in between are
* rounded down to the next lower one.
* [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011]
* .2% .3% 5% 7% 7% 20% 15% 45%
*/
static int pf_syncookie_msstab[] =
{ 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 };
/*
* Distribution and probability of certain WSCALE values.
* The absence of the WSCALE option is encoded with index zero.
* [WSCALE values histograms, Allman, 2012]
* X 10 10 35 5 6 14 10% by host
* X 11 4 5 5 18 49 3% by connections
*/
static int pf_syncookie_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 };
uint32_t
pf_syncookie_mac(struct pf_pdesc *pd, union pf_syncookie cookie, uint32_t seq)
{
SIPHASH_CTX ctx;
uint32_t siphash[2];
KASSERT(pd->proto == IPPROTO_TCP);
SipHash24_Init(&ctx, &pf_syncookie_status.key[cookie.flags.oddeven]);
switch (pd->af) {
case AF_INET:
SipHash24_Update(&ctx, pd->src, sizeof(pd->src->v4));
SipHash24_Update(&ctx, pd->dst, sizeof(pd->dst->v4));
break;
case AF_INET6:
SipHash24_Update(&ctx, pd->src, sizeof(pd->src->v6));
SipHash24_Update(&ctx, pd->dst, sizeof(pd->dst->v6));
break;
default:
panic("unknown address family");
}
SipHash24_Update(&ctx, pd->sport, sizeof(*pd->sport));
SipHash24_Update(&ctx, pd->dport, sizeof(*pd->dport));
SipHash24_Update(&ctx, &seq, sizeof(seq));
SipHash24_Update(&ctx, &cookie, sizeof(cookie));
SipHash24_Final((uint8_t *)&siphash, &ctx);
return (siphash[0] ^ siphash[1]);
}
uint32_t
pf_syncookie_generate(struct pf_pdesc *pd, uint16_t mss)
{
uint8_t i, wscale;
uint32_t iss, hash;
union pf_syncookie cookie;
cookie.cookie = 0;
/* map MSS */
for (i = nitems(pf_syncookie_msstab) - 1;
pf_syncookie_msstab[i] > mss && i > 0; i--)
/* nada */;
cookie.flags.mss_idx = i;
/* map WSCALE */
wscale = pf_get_wscale(pd);
for (i = nitems(pf_syncookie_wstab) - 1;
pf_syncookie_wstab[i] > wscale && i > 0; i--)
/* nada */;
cookie.flags.wscale_idx = i;
cookie.flags.sack_ok = 0; /* XXX */
cookie.flags.oddeven = pf_syncookie_status.oddeven;
hash = pf_syncookie_mac(pd, cookie, ntohl(pd->hdr.tcp.th_seq));
/*
* Put the flags into the hash and XOR them to get better ISS number
* variance. This doesn't enhance the cryptographic strength and is
* done to prevent the 8 cookie bits from showing up directly on the
* wire.
*/
iss = hash & ~0xff;
iss |= cookie.cookie ^ (hash >> 24);
return (iss);
}
struct mbuf *
pf_syncookie_recreate_syn(struct pf_pdesc *pd)
{
uint8_t wscale;
uint16_t mss;
uint32_t ack, seq;
union pf_syncookie cookie;
seq = ntohl(pd->hdr.tcp.th_seq) - 1;
ack = ntohl(pd->hdr.tcp.th_ack) - 1;
cookie.cookie = (ack & 0xff) ^ (ack >> 24);
if (cookie.flags.mss_idx >= nitems(pf_syncookie_msstab) ||
cookie.flags.wscale_idx >= nitems(pf_syncookie_wstab))
return (NULL);
mss = pf_syncookie_msstab[cookie.flags.mss_idx];
wscale = pf_syncookie_wstab[cookie.flags.wscale_idx];
return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport,
*pd->dport, seq, 0, TH_SYN, wscale, mss, pd->ttl, 0,
PF_TAG_SYNCOOKIE_RECREATED, cookie.flags.sack_ok, pd->rdomain));
}
578
316
196
353
280
280
157
280
122
122
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* $OpenBSD: ufs_ihash.c,v 1.26 2021/10/19 06:11:45 semarie Exp $ */
/* $NetBSD: ufs_ihash.c,v 1.3 1996/02/09 22:36:04 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_ihash.c 8.4 (Berkeley) 12/30/93
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufs_extern.h>
#include <crypto/siphash.h>
/*
* Structures associated with inode caching.
*/
LIST_HEAD(ihashhead, inode) *ihashtbl;
u_long ihash; /* size of hash table - 1 */
SIPHASH_KEY ihashkey;
struct ihashhead *ufs_ihash(dev_t, ufsino_t);
#define INOHASH(device, inum) ufs_ihash((device), (inum))
struct ihashhead *
ufs_ihash(dev_t dev, ufsino_t inum)
{
SIPHASH_CTX ctx;
SipHash24_Init(&ctx, &ihashkey);
SipHash24_Update(&ctx, &dev, sizeof(dev));
SipHash24_Update(&ctx, &inum, sizeof(inum));
return (&ihashtbl[SipHash24_End(&ctx) & ihash]);
}
/*
* Initialize inode hash table.
*/
void
ufs_ihashinit(void)
{
ihashtbl = hashinit(initialvnodes, M_UFSMNT, M_WAITOK, &ihash);
arc4random_buf(&ihashkey, sizeof(ihashkey));
}
/*
* Use the device/inum pair to find the incore inode, and return a pointer
* to it. If it is in core, return it, even if it is locked.
*/
struct vnode *
ufs_ihashlookup(dev_t dev, ufsino_t inum)
{
struct inode *ip;
struct ihashhead *ipp;
/* XXXLOCKING lock hash list */
ipp = INOHASH(dev, inum);
LIST_FOREACH(ip, ipp, i_hash) {
if (inum == ip->i_number && dev == ip->i_dev)
break;
}
/* XXXLOCKING unlock hash list? */
if (ip)
return (ITOV(ip));
return (NULLVP);
}
/*
* Use the device/inum pair to find the incore inode, and return a pointer
* to it. If it is in core, but locked, wait for it.
*/
struct vnode *
ufs_ihashget(dev_t dev, ufsino_t inum)
{
struct ihashhead *ipp;
struct inode *ip;
struct vnode *vp;
loop:
/* XXXLOCKING lock hash list */
ipp = INOHASH(dev, inum);
LIST_FOREACH(ip, ipp, i_hash) {
if (inum == ip->i_number && dev == ip->i_dev) {
vp = ITOV(ip);
/* XXXLOCKING unlock hash list? */
if (vget(vp, LK_EXCLUSIVE))
goto loop;
return (vp);
}
}
/* XXXLOCKING unlock hash list? */
return (NULL);
}
/*
* Insert the inode into the hash table, and return it locked.
*/
int
ufs_ihashins(struct inode *ip)
{
struct inode *curip;
struct ihashhead *ipp;
dev_t dev = ip->i_dev;
ufsino_t inum = ip->i_number;
/* lock the inode, then put it on the appropriate hash list */
VOP_LOCK(ITOV(ip), LK_EXCLUSIVE);
/* XXXLOCKING lock hash list */
ipp = INOHASH(dev, inum);
LIST_FOREACH(curip, ipp, i_hash) {
if (inum == curip->i_number && dev == curip->i_dev) {
/* XXXLOCKING unlock hash list? */
VOP_UNLOCK(ITOV(ip));
return (EEXIST);
}
}
SET(ip->i_flag, IN_HASHED);
LIST_INSERT_HEAD(ipp, ip, i_hash);
/* XXXLOCKING unlock hash list? */
return (0);
}
/*
* Remove the inode from the hash table.
*/
void
ufs_ihashrem(struct inode *ip)
{
/* XXXLOCKING lock hash list */
if (ip->i_hash.le_prev == NULL)
return;
if (ISSET(ip->i_flag, IN_HASHED)) {
LIST_REMOVE(ip, i_hash);
CLR(ip->i_flag, IN_HASHED);
}
#ifdef DIAGNOSTIC
ip->i_hash.le_next = NULL;
ip->i_hash.le_prev = NULL;
#endif
/* XXXLOCKING unlock hash list? */
}
290
82
245
245
245
142
94
94
78
62
4
1
4
63
63
55
9
9
9
5
6
3
63
3
63
63
10
60
63
62
63
63
63
63
3
6
63
62
55
54
54
60
9
9
9
7
2
63
63
8
8
7
1
8
8
6
3
3
3
3
8
8
8
6
8
3
6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/* $OpenBSD: ffs_inode.c,v 1.81 2021/12/12 09:14:59 visa Exp $ */
/* $NetBSD: ffs_inode.c,v 1.10 1996/05/11 18:27:19 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_inode.c 8.8 (Berkeley) 10/19/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/resourcevar.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
int ffs_indirtrunc(struct inode *, daddr_t, daddr_t, daddr_t, int, long *);
/*
* Update the access, modified, and inode change times as specified by the
* IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. The IN_MODIFIED
* flag is used to specify that the inode needs to be updated but that the
* times have already been set. The IN_LAZYMOD flag is used to specify
* that the inode needs to be updated at some point, by reclaim if not
* in the course of other changes; this is used to defer writes just to
* update device timestamps. If waitfor is set, then wait for the disk
* write of the inode to complete.
*/
int
ffs_update(struct inode *ip, int waitfor)
{
struct vnode *vp;
struct fs *fs;
struct buf *bp;
int error;
vp = ITOV(ip);
ufs_itimes(vp);
if ((ip->i_flag & IN_MODIFIED) == 0 && waitfor == 0)
return (0);
ip->i_flag &= ~(IN_MODIFIED | IN_LAZYMOD);
fs = ip->i_fs;
/*
* Ensure that uid and gid are correct. This is a temporary
* fix until fsck has been changed to do the update.
*/
if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_inodefmt < FS_44INODEFMT) {
ip->i_din1->di_ouid = ip->i_ffs1_uid;
ip->i_din1->di_ogid = ip->i_ffs1_gid;
}
error = bread(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
(int)fs->fs_bsize, &bp);
if (error) {
brelse(bp);
return (error);
}
if (DOINGSOFTDEP(vp))
softdep_update_inodeblock(ip, bp, waitfor);
else if (ip->i_effnlink != DIP(ip, nlink))
panic("ffs_update: bad link cnt");
#ifdef FFS2
if (ip->i_ump->um_fstype == UM_UFS2)
*((struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
else
#endif
*((struct ufs1_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1;
if (waitfor && !DOINGASYNC(vp)) {
return (bwrite(bp));
} else {
bdwrite(bp);
return (0);
}
}
#define SINGLE 0 /* index of single indirect block */
#define DOUBLE 1 /* index of double indirect block */
#define TRIPLE 2 /* index of triple indirect block */
/*
* Truncate the inode oip to at most length size, freeing the
* disk blocks.
*/
int
ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred)
{
struct vnode *ovp;
daddr_t lastblock;
daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
struct fs *fs;
struct buf *bp;
int offset, size, level;
long count, nblocks, vflags, blocksreleased = 0;
int i, aflags, error, allerror;
off_t osize;
if (length < 0)
return (EINVAL);
ovp = ITOV(oip);
if (ovp->v_type != VREG &&
ovp->v_type != VDIR &&
ovp->v_type != VLNK)
return (0);
if (DIP(oip, size) == length)
return (0);
if (ovp->v_type == VLNK &&
(DIP(oip, size) < oip->i_ump->um_maxsymlinklen ||
(oip->i_ump->um_maxsymlinklen == 0 &&
oip->i_din1->di_blocks == 0))) {
#ifdef DIAGNOSTIC
if (length != 0)
panic("ffs_truncate: partial truncate of symlink");
#endif
memset(SHORTLINK(oip), 0, (size_t) DIP(oip, size));
DIP_ASSIGN(oip, size, 0);
oip->i_flag |= IN_CHANGE | IN_UPDATE;
return (UFS_UPDATE(oip, 1));
}
if ((error = getinoquota(oip)) != 0)
return (error);
fs = oip->i_fs;
if (length > fs->fs_maxfilesize)
return (EFBIG);
uvm_vnp_setsize(ovp, length);
oip->i_ci.ci_lasta = oip->i_ci.ci_clen
= oip->i_ci.ci_cstart = oip->i_ci.ci_lastw = 0;
if (DOINGSOFTDEP(ovp)) {
if (length > 0 || softdep_slowdown(ovp)) {
/*
* If a file is only partially truncated, then
* we have to clean up the data structures
* describing the allocation past the truncation
* point. Finding and deallocating those structures
* is a lot of work. Since partial truncation occurs
* rarely, we solve the problem by syncing the file
* so that it will have no data structures left.
*/
if ((error = VOP_FSYNC(ovp, cred, MNT_WAIT,
curproc)) != 0)
return (error);
} else {
(void)ufs_quota_free_blocks(oip, DIP(oip, blocks),
NOCRED);
softdep_setup_freeblocks(oip, length);
vinvalbuf(ovp, 0, cred, curproc, 0, INFSLP);
oip->i_flag |= IN_CHANGE | IN_UPDATE;
return (UFS_UPDATE(oip, 0));
}
}
osize = DIP(oip, size);
/*
* Lengthen the size of the file. We must ensure that the
* last byte of the file is allocated. Since the smallest
* value of osize is 0, length will be at least 1.
*/
if (osize < length) {
aflags = B_CLRBUF;
if (flags & IO_SYNC)
aflags |= B_SYNC;
error = UFS_BUF_ALLOC(oip, length - 1, 1,
cred, aflags, &bp);
if (error)
return (error);
DIP_ASSIGN(oip, size, length);
uvm_vnp_setsize(ovp, length);
(void) uvm_vnp_uncache(ovp);
if (aflags & B_SYNC)
bwrite(bp);
else
bawrite(bp);
oip->i_flag |= IN_CHANGE | IN_UPDATE;
return (UFS_UPDATE(oip, 1));
}
uvm_vnp_setsize(ovp, length);
/*
* Shorten the size of the file. If the file is not being
* truncated to a block boundary, the contents of the
* partial block following the end of the file must be
* zero'ed in case it ever becomes accessible again because
* of subsequent file growth. Directories however are not
* zero'ed as they should grow back initialized to empty.
*/
offset = blkoff(fs, length);
if (offset == 0) {
DIP_ASSIGN(oip, size, length);
} else {
lbn = lblkno(fs, length);
aflags = B_CLRBUF;
if (flags & IO_SYNC)
aflags |= B_SYNC;
error = UFS_BUF_ALLOC(oip, length - 1, 1,
cred, aflags, &bp);
if (error)
return (error);
/*
* When we are doing soft updates and the UFS_BALLOC
* above fills in a direct block hole with a full sized
* block that will be truncated down to a fragment below,
* we must flush out the block dependency with an FSYNC
* so that we do not get a soft updates inconsistency
* when we create the fragment below.
*/
if (DOINGSOFTDEP(ovp) && lbn < NDADDR &&
fragroundup(fs, blkoff(fs, length)) < fs->fs_bsize &&
(error = VOP_FSYNC(ovp, cred, MNT_WAIT, curproc)) != 0)
return (error);
DIP_ASSIGN(oip, size, length);
size = blksize(fs, oip, lbn);
(void) uvm_vnp_uncache(ovp);
if (ovp->v_type != VDIR)
memset(bp->b_data + offset, 0, size - offset);
buf_adjcnt(bp, size);
if (aflags & B_SYNC)
bwrite(bp);
else
bawrite(bp);
}
/*
* Calculate index into inode's block list of
* last direct and indirect blocks (if any)
* which we want to keep. Lastblock is -1 when
* the file is truncated to 0.
*/
lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
lastiblock[SINGLE] = lastblock - NDADDR;
lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
nblocks = btodb(fs->fs_bsize);
/*
* Update file and block pointers on disk before we start freeing
* blocks. If we crash before free'ing blocks below, the blocks
* will be returned to the free list. lastiblock values are also
* normalized to -1 for calls to ffs_indirtrunc below.
*/
for (level = TRIPLE; level >= SINGLE; level--) {
oldblks[NDADDR + level] = DIP(oip, ib[level]);
if (lastiblock[level] < 0) {
DIP_ASSIGN(oip, ib[level], 0);
lastiblock[level] = -1;
}
}
for (i = 0; i < NDADDR; i++) {
oldblks[i] = DIP(oip, db[i]);
if (i > lastblock)
DIP_ASSIGN(oip, db[i], 0);
}
oip->i_flag |= IN_CHANGE | IN_UPDATE;
if ((error = UFS_UPDATE(oip, 1)) != 0)
allerror = error;
/*
* Having written the new inode to disk, save its new configuration
* and put back the old block pointers long enough to process them.
* Note that we save the new block configuration so we can check it
* when we are done.
*/
for (i = 0; i < NDADDR; i++) {
newblks[i] = DIP(oip, db[i]);
DIP_ASSIGN(oip, db[i], oldblks[i]);
}
for (i = 0; i < NIADDR; i++) {
newblks[NDADDR + i] = DIP(oip, ib[i]);
DIP_ASSIGN(oip, ib[i], oldblks[NDADDR + i]);
}
DIP_ASSIGN(oip, size, osize);
vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
allerror = vinvalbuf(ovp, vflags, cred, curproc, 0, INFSLP);
/*
* Indirect blocks first.
*/
indir_lbn[SINGLE] = -NDADDR;
indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
for (level = TRIPLE; level >= SINGLE; level--) {
bn = DIP(oip, ib[level]);
if (bn != 0) {
error = ffs_indirtrunc(oip, indir_lbn[level],
fsbtodb(fs, bn), lastiblock[level], level, &count);
if (error)
allerror = error;
blocksreleased += count;
if (lastiblock[level] < 0) {
DIP_ASSIGN(oip, ib[level], 0);
ffs_blkfree(oip, bn, fs->fs_bsize);
blocksreleased += nblocks;
}
}
if (lastiblock[level] >= 0)
goto done;
}
/*
* All whole direct blocks or frags.
*/
for (i = NDADDR - 1; i > lastblock; i--) {
long bsize;
bn = DIP(oip, db[i]);
if (bn == 0)
continue;
DIP_ASSIGN(oip, db[i], 0);
bsize = blksize(fs, oip, i);
ffs_blkfree(oip, bn, bsize);
blocksreleased += btodb(bsize);
}
if (lastblock < 0)
goto done;
/*
* Finally, look for a change in size of the
* last direct block; release any frags.
*/
bn = DIP(oip, db[lastblock]);
if (bn != 0) {
long oldspace, newspace;
/*
* Calculate amount of space we're giving
* back as old block size minus new block size.
*/
oldspace = blksize(fs, oip, lastblock);
DIP_ASSIGN(oip, size, length);
newspace = blksize(fs, oip, lastblock);
if (newspace == 0)
panic("ffs_truncate: newspace");
if (oldspace - newspace > 0) {
/*
* Block number of space to be free'd is
* the old block # plus the number of frags
* required for the storage we're keeping.
*/
bn += numfrags(fs, newspace);
ffs_blkfree(oip, bn, oldspace - newspace);
blocksreleased += btodb(oldspace - newspace);
}
}
done:
#ifdef DIAGNOSTIC
for (level = SINGLE; level <= TRIPLE; level++)
if (newblks[NDADDR + level] != DIP(oip, ib[level]))
panic("ffs_truncate1");
for (i = 0; i < NDADDR; i++)
if (newblks[i] != DIP(oip, db[i]))
panic("ffs_truncate2");
#endif /* DIAGNOSTIC */
/*
* Put back the real size.
*/
DIP_ASSIGN(oip, size, length);
if (DIP(oip, blocks) >= blocksreleased)
DIP_ADD(oip, blocks, -blocksreleased);
else /* sanity */
DIP_ASSIGN(oip, blocks, 0);
oip->i_flag |= IN_CHANGE;
(void)ufs_quota_free_blocks(oip, blocksreleased, NOCRED);
return (allerror);
}
#ifdef FFS2
#define BAP(ip, i) (((ip)->i_ump->um_fstype == UM_UFS2) ? bap2[i] : bap1[i])
#define BAP_ASSIGN(ip, i, value) \
do { \
if ((ip)->i_ump->um_fstype == UM_UFS2) \
bap2[i] = (value); \
else \
bap1[i] = (value); \
} while (0)
#else
#define BAP(ip, i) bap1[i]
#define BAP_ASSIGN(ip, i, value) do { bap1[i] = (value); } while (0)
#endif /* FFS2 */
/*
* Release blocks associated with the inode ip and stored in the indirect
* block bn. Blocks are free'd in LIFO order up to (but not including)
* lastbn. If level is greater than SINGLE, the block is an indirect block
* and recursive calls to indirtrunc must be used to cleanse other indirect
* blocks.
*
* NB: triple indirect blocks are untested.
*/
int
ffs_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn,
daddr_t lastbn, int level, long *countp)
{
int i;
struct buf *bp;
struct fs *fs = ip->i_fs;
struct vnode *vp;
void *copy = NULL;
daddr_t nb, nlbn, last;
long blkcount, factor;
int nblocks, blocksreleased = 0;
int error = 0, allerror = 0;
int32_t *bap1 = NULL;
#ifdef FFS2
int64_t *bap2 = NULL;
#endif
/*
* Calculate index in current block of last
* block to be kept. -1 indicates the entire
* block so we need not calculate the index.
*/
factor = 1;
for (i = SINGLE; i < level; i++)
factor *= NINDIR(fs);
last = lastbn;
if (lastbn > 0)
last /= factor;
nblocks = btodb(fs->fs_bsize);
/*
* Get buffer of block pointers, zero those entries corresponding
* to blocks to be free'd, and update on disk copy first. Since
* double(triple) indirect before single(double) indirect, calls
* to bmap on these blocks will fail. However, we already have
* the on disk address, so we have to set the b_blkno field
* explicitly instead of letting bread do everything for us.
*/
vp = ITOV(ip);
bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, INFSLP);
if (!(bp->b_flags & (B_DONE | B_DELWRI))) {
curproc->p_ru.ru_inblock++; /* pay for read */
bcstats.pendingreads++;
bcstats.numreads++;
bp->b_flags |= B_READ;
if (bp->b_bcount > bp->b_bufsize)
panic("ffs_indirtrunc: bad buffer size");
bp->b_blkno = dbn;
VOP_STRATEGY(bp->b_vp, bp);
error = biowait(bp);
}
if (error) {
brelse(bp);
*countp = 0;
return (error);
}
#ifdef FFS2
if (ip->i_ump->um_fstype == UM_UFS2)
bap2 = (int64_t *)bp->b_data;
else
#endif
bap1 = (int32_t *)bp->b_data;
if (lastbn != -1) {
copy = malloc(fs->fs_bsize, M_TEMP, M_WAITOK);
memcpy(copy, bp->b_data, fs->fs_bsize);
for (i = last + 1; i < NINDIR(fs); i++)
BAP_ASSIGN(ip, i, 0);
if (!DOINGASYNC(vp)) {
error = bwrite(bp);
if (error)
allerror = error;
} else {
bawrite(bp);
}
#ifdef FFS2
if (ip->i_ump->um_fstype == UM_UFS2)
bap2 = (int64_t *)copy;
else
#endif
bap1 = (int32_t *)copy;
}
/*
* Recursively free totally unused blocks.
*/
for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
i--, nlbn += factor) {
nb = BAP(ip, i);
if (nb == 0)
continue;
if (level > SINGLE) {
error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
-1, level - 1, &blkcount);
if (error)
allerror = error;
blocksreleased += blkcount;
}
ffs_blkfree(ip, nb, fs->fs_bsize);
blocksreleased += nblocks;
}
/*
* Recursively free last partial block.
*/
if (level > SINGLE && lastbn >= 0) {
last = lastbn % factor;
nb = BAP(ip, i);
if (nb != 0) {
error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
last, level - 1, &blkcount);
if (error)
allerror = error;
blocksreleased += blkcount;
}
}
if (copy != NULL) {
free(copy, M_TEMP, fs->fs_bsize);
} else {
bp->b_flags |= B_INVAL;
brelse(bp);
}
*countp = blocksreleased;
return (allerror);
}
7
7
1
1
25
18
8
2
12
2
9
1
11
2
10
2
2
6
2
6
6
6
4
1
6
6
4
6
6
6
6
1
5
5
6
6
6
6
4
1
3
4
4
4
4
6
6
4
3
3
1
1
1
3
4
8
8
3
2
2
3
6
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
/* $OpenBSD: pipex.c,v 1.148 2022/08/30 19:42:29 bluhm Exp $ */
/*-
* Copyright (c) 2009 Internet Initiative Japan Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/conf.h>
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/percpu.h>
#include <sys/mutex.h>
#include <net/if.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_dl.h>
#include <net/radix.h>
#include <net/route.h>
#include <net/ppp_defs.h>
#include <net/ppp-comp.h>
#include <net/netisr.h>
#include "pf.h"
#if NPF > 0
#include <net/pfvar.h>
#endif
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <crypto/arc4.h>
#include <net/pipex.h>
#include "pipex_local.h"
struct mutex pipex_list_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct pool pipex_session_pool;
struct pool mppe_key_pool;
/*
* Global data
* Locks used to protect global data
* A atomic operation
* I immutable after creation
* L pipex_list_mtx
*/
int pipex_enable = 0; /* [A] */
struct pipex_hash_head
pipex_session_list, /* [L] master session list */
pipex_close_wait_list, /* [L] expired session list */
pipex_peer_addr_hashtable[PIPEX_HASH_SIZE], /* [L] peer's address hash */
pipex_id_hashtable[PIPEX_HASH_SIZE]; /* [L] peer id hash */
struct radix_node_head *pipex_rd_head4 = NULL; /* [L] */
struct radix_node_head *pipex_rd_head6 = NULL; /* [L] */
struct timeout pipex_timer_ch; /* callout timer context */
int pipex_prune = 1; /* [I] walk list every seconds */
struct mbuf_queue pipexoutq = MBUF_QUEUE_INITIALIZER(
IFQ_MAXLEN, IPL_SOFTNET);
/* borrow an mbuf pkthdr field */
#define ph_ppp_proto ether_vtag
#ifdef PIPEX_DEBUG
int pipex_debug = 0; /* [A] systcl net.inet.ip.pipex_debug */
#endif
/* PPP compression == MPPE is assumed, so don't answer CCP Reset-Request. */
#define PIPEX_NO_CCP_RESETACK 1
/************************************************************************
* Core functions
************************************************************************/
void
pipex_init(void)
{
int i;
static int pipex_init_done = 0;
if (pipex_init_done++)
return;
rn_init(sizeof(struct sockaddr_in6));
pool_init(&pipex_session_pool, sizeof(struct pipex_session), 0,
IPL_SOFTNET, PR_WAITOK, "ppxss", NULL);
pool_init(&mppe_key_pool, PIPEX_MPPE_KEYLEN * PIPEX_MPPE_NOLDKEY, 0,
IPL_SOFTNET, PR_WAITOK, "mppekey", NULL);
LIST_INIT(&pipex_session_list);
LIST_INIT(&pipex_close_wait_list);
for (i = 0; i < nitems(pipex_id_hashtable); i++)
LIST_INIT(&pipex_id_hashtable[i]);
for (i = 0; i < nitems(pipex_peer_addr_hashtable); i++)
LIST_INIT(&pipex_peer_addr_hashtable[i]);
}
void
pipex_destroy_all_sessions(void *ownersc)
{
struct pipex_session *session, *session_tmp;
mtx_enter(&pipex_list_mtx);
LIST_FOREACH_SAFE(session, &pipex_session_list, session_list,
session_tmp) {
if (session->ownersc == ownersc) {
KASSERT((session->flags & PIPEX_SFLAGS_PPPX) == 0);
pipex_unlink_session_locked(session);
pipex_rele_session(session);
}
}
mtx_leave(&pipex_list_mtx);
}
int
pipex_ioctl(void *ownersc, u_long cmd, caddr_t data)
{
int ret = 0;
switch (cmd) {
case PIPEXGSTAT:
ret = pipex_get_stat((struct pipex_session_stat_req *)data,
ownersc);
break;
case PIPEXGCLOSED:
ret = pipex_get_closed((struct pipex_session_list_req *)data,
ownersc);
break;
default:
ret = ENOTTY;
break;
}
return (ret);
}
/************************************************************************
* Software Interrupt Handler
************************************************************************/
void
pipexintr(void)
{
struct mbuf_list ml;
struct mbuf *m;
struct pipex_session *session;
NET_ASSERT_LOCKED();
mq_delist(&pipexoutq, &ml);
while ((m = ml_dequeue(&ml)) != NULL) {
struct ifnet *ifp;
session = m->m_pkthdr.ph_cookie;
ifp = if_get(session->proto.pppoe.over_ifidx);
if (ifp != NULL) {
struct pipex_pppoe_header *pppoe;
int len;
pppoe = mtod(m, struct pipex_pppoe_header *);
len = ntohs(pppoe->length);
ifp->if_output(ifp, m, &session->peer.sa, NULL);
counters_pkt(session->stat_counters, pxc_opackets,
pxc_obytes, len);
} else {
m_freem(m);
counters_inc(session->stat_counters, pxc_oerrors);
}
if_put(ifp);
pipex_rele_session(session);
}
}
/************************************************************************
* Session management functions
************************************************************************/
int
pipex_init_session(struct pipex_session **rsession,
struct pipex_session_req *req)
{
struct pipex_session *session;
#ifdef PIPEX_PPPOE
struct ifnet *over_ifp = NULL;
#endif
/* Checks requested parameters. */
switch (req->pr_protocol) {
#ifdef PIPEX_PPPOE
case PIPEX_PROTO_PPPOE:
if (req->pr_peer_address.ss_family != AF_UNSPEC)
return (EINVAL);
break;
#endif
#if defined(PIPEX_L2TP) || defined(PIPEX_PPTP)
case PIPEX_PROTO_PPTP:
case PIPEX_PROTO_L2TP:
switch (req->pr_peer_address.ss_family) {
case AF_INET:
if (req->pr_peer_address.ss_len !=
sizeof(struct sockaddr_in))
return (EINVAL);
break;
#ifdef INET6
case AF_INET6:
if (req->pr_peer_address.ss_len !=
sizeof(struct sockaddr_in6))
return (EINVAL);
break;
#endif
default:
return (EPROTONOSUPPORT);
}
if (req->pr_peer_address.ss_family !=
req->pr_local_address.ss_family ||
req->pr_peer_address.ss_len !=
req->pr_local_address.ss_len)
return (EINVAL);
break;
#endif /* defined(PIPEX_PPTP) || defined(PIPEX_L2TP) */
default:
return (EPROTONOSUPPORT);
}
#ifdef PIPEX_MPPE
if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) {
switch (req->pr_mppe_recv.keylenbits) {
case 40:
case 56:
case 128:
break;
default:
return (EINVAL);
}
}
if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) {
switch (req->pr_mppe_send.keylenbits) {
case 40:
case 56:
case 128:
break;
default:
return (EINVAL);
}
}
if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_REQUIRED) != 0) {
if ((req->pr_ppp_flags &
(PIPEX_PPP_MPPE_ACCEPTED | PIPEX_PPP_MPPE_ENABLED)) !=
(PIPEX_PPP_MPPE_ACCEPTED | PIPEX_PPP_MPPE_ENABLED))
return (EINVAL);
}
#endif
#ifdef PIPEX_PPPOE
if (req->pr_protocol == PIPEX_PROTO_PPPOE) {
over_ifp = if_unit(req->pr_proto.pppoe.over_ifname);
if (over_ifp == NULL)
return (EINVAL);
}
#endif
/* prepare a new session */
session = pool_get(&pipex_session_pool, PR_WAITOK | PR_ZERO);
refcnt_init(&session->pxs_refcnt);
mtx_init(&session->pxs_mtx, IPL_SOFTNET);
session->state = PIPEX_STATE_INITIAL;
session->protocol = req->pr_protocol;
session->session_id = req->pr_session_id;
session->peer_session_id = req->pr_peer_session_id;
session->peer_mru = req->pr_peer_mru;
session->timeout_sec = req->pr_timeout_sec;
session->ppp_flags = req->pr_ppp_flags;
session->ppp_id = req->pr_ppp_id;
session->stat_counters = counters_alloc(pxc_ncounters);
session->ip_address.sin_family = AF_INET;
session->ip_address.sin_len = sizeof(struct sockaddr_in);
session->ip_address.sin_addr = req->pr_ip_address;
session->ip_netmask.sin_family = AF_INET;
session->ip_netmask.sin_len = sizeof(struct sockaddr_in);
session->ip_netmask.sin_addr = req->pr_ip_netmask;
if (session->ip_netmask.sin_addr.s_addr == 0L)
session->ip_netmask.sin_addr.s_addr = 0xffffffffL;
session->ip_address.sin_addr.s_addr &=
session->ip_netmask.sin_addr.s_addr;
if (req->pr_peer_address.ss_len > 0)
memcpy(&session->peer, &req->pr_peer_address,
MIN(req->pr_peer_address.ss_len, sizeof(session->peer)));
if (req->pr_local_address.ss_len > 0)
memcpy(&session->local, &req->pr_local_address,
MIN(req->pr_local_address.ss_len, sizeof(session->local)));
#ifdef PIPEX_PPPOE
if (req->pr_protocol == PIPEX_PROTO_PPPOE) {
session->proto.pppoe.over_ifidx = over_ifp->if_index;
if_put(over_ifp);
}
#endif
#ifdef PIPEX_PPTP
if (req->pr_protocol == PIPEX_PROTO_PPTP) {
struct pipex_pptp_session *sess_pptp = &session->proto.pptp;
sess_pptp->snd_gap = 0;
sess_pptp->rcv_gap = 0;
sess_pptp->snd_una = req->pr_proto.pptp.snd_una;
sess_pptp->snd_nxt = req->pr_proto.pptp.snd_nxt;
sess_pptp->rcv_nxt = req->pr_proto.pptp.rcv_nxt;
sess_pptp->rcv_acked = req->pr_proto.pptp.rcv_acked;
sess_pptp->winsz = req->pr_proto.pptp.winsz;
sess_pptp->maxwinsz = req->pr_proto.pptp.maxwinsz;
sess_pptp->peer_maxwinsz = req->pr_proto.pptp.peer_maxwinsz;
/* last ack number */
sess_pptp->ul_snd_una = sess_pptp->snd_una - 1;
}
#endif
#ifdef PIPEX_L2TP
if (req->pr_protocol == PIPEX_PROTO_L2TP) {
struct pipex_l2tp_session *sess_l2tp = &session->proto.l2tp;
/* session keys */
sess_l2tp->tunnel_id = req->pr_proto.l2tp.tunnel_id;
sess_l2tp->peer_tunnel_id = req->pr_proto.l2tp.peer_tunnel_id;
/* protocol options */
sess_l2tp->option_flags = req->pr_proto.l2tp.option_flags;
/* initial state of dynamic context */
sess_l2tp->ns_gap = sess_l2tp->nr_gap = 0;
sess_l2tp->ns_nxt = req->pr_proto.l2tp.ns_nxt;
sess_l2tp->nr_nxt = req->pr_proto.l2tp.nr_nxt;
sess_l2tp->ns_una = req->pr_proto.l2tp.ns_una;
sess_l2tp->nr_acked = req->pr_proto.l2tp.nr_acked;
/* last ack number */
sess_l2tp->ul_ns_una = sess_l2tp->ns_una - 1;
sess_l2tp->ipsecflowinfo = req->pr_proto.l2tp.ipsecflowinfo;
}
#endif
#ifdef PIPEX_MPPE
if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) {
pipex_session_init_mppe_recv(session,
req->pr_mppe_recv.stateless, req->pr_mppe_recv.keylenbits,
req->pr_mppe_recv.master_key);
}
if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) {
pipex_session_init_mppe_send(session,
req->pr_mppe_send.stateless, req->pr_mppe_send.keylenbits,
req->pr_mppe_send.master_key);
}
#endif
*rsession = session;
return 0;
}
void
pipex_rele_session(struct pipex_session *session)
{
if (refcnt_rele(&session->pxs_refcnt) == 0)
return;
if (session->mppe_recv.old_session_keys)
pool_put(&mppe_key_pool, session->mppe_recv.old_session_keys);
counters_free(session->stat_counters, pxc_ncounters);
pool_put(&pipex_session_pool, session);
}
int
pipex_link_session(struct pipex_session *session, struct ifnet *ifp,
void *ownersc)
{
struct pipex_hash_head *chain;
struct radix_node *rn;
int error = 0;
mtx_enter(&pipex_list_mtx);
if (pipex_rd_head4 == NULL) {
if (!rn_inithead((void **)&pipex_rd_head4,
offsetof(struct sockaddr_in, sin_addr)))
panic("rn_inithead() failed on pipex_link_session()");
}
if (pipex_rd_head6 == NULL) {
if (!rn_inithead((void **)&pipex_rd_head6,
offsetof(struct sockaddr_in6, sin6_addr)))
panic("rn_inithead() failed on pipex_link_session()");
}
if (pipex_lookup_by_session_id_locked(session->protocol,
session->session_id)) {
error = EEXIST;
goto out;
}
session->ownersc = ownersc;
session->ifindex = ifp->if_index;
if (ifp->if_flags & IFF_POINTOPOINT)
session->flags |= PIPEX_SFLAGS_PPPX;
if ((session->flags & PIPEX_SFLAGS_PPPX) == 0 &&
!in_nullhost(session->ip_address.sin_addr)) {
if (pipex_lookup_by_ip_address_locked(
session->ip_address.sin_addr) != NULL) {
error = EADDRINUSE;
goto out;
}
rn = rn_addroute(&session->ip_address, &session->ip_netmask,
pipex_rd_head4, session->ps4_rn, RTP_STATIC);
if (rn == NULL) {
error = ENOMEM;
goto out;
}
}
LIST_INSERT_HEAD(&pipex_session_list, session, session_list);
chain = PIPEX_ID_HASHTABLE(session->session_id);
LIST_INSERT_HEAD(chain, session, id_chain);
#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
switch (session->protocol) {
case PIPEX_PROTO_PPTP:
case PIPEX_PROTO_L2TP:
chain = PIPEX_PEER_ADDR_HASHTABLE(
pipex_sockaddr_hash_key(&session->peer.sa));
LIST_INSERT_HEAD(chain, session, peer_addr_chain);
}
#endif
/* if first session is added, start timer */
if (LIST_NEXT(session, session_list) == NULL)
pipex_timer_start();
session->state = PIPEX_STATE_OPENED;
out:
mtx_leave(&pipex_list_mtx);
return error;
}
void
pipex_unlink_session_locked(struct pipex_session *session)
{
struct radix_node *rn;
MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
session->ifindex = 0;
if (session->state == PIPEX_STATE_CLOSED)
return;
if ((session->flags & PIPEX_SFLAGS_PPPX) == 0 &&
!in_nullhost(session->ip_address.sin_addr)) {
KASSERT(pipex_rd_head4 != NULL);
rn = rn_delete(&session->ip_address, &session->ip_netmask,
pipex_rd_head4, (struct radix_node *)session);
KASSERT(rn != NULL);
}
LIST_REMOVE(session, id_chain);
#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
switch (session->protocol) {
case PIPEX_PROTO_PPTP:
case PIPEX_PROTO_L2TP:
LIST_REMOVE(session, peer_addr_chain);
break;
}
#endif
if (session->state == PIPEX_STATE_CLOSE_WAIT)
LIST_REMOVE(session, state_list);
LIST_REMOVE(session, session_list);
session->state = PIPEX_STATE_CLOSED;
/* if final session is destroyed, stop timer */
if (LIST_EMPTY(&pipex_session_list))
pipex_timer_stop();
}
void
pipex_unlink_session(struct pipex_session *session)
{
mtx_enter(&pipex_list_mtx);
pipex_unlink_session_locked(session);
mtx_leave(&pipex_list_mtx);
}
int
pipex_notify_close_session(struct pipex_session *session)
{
MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
session->state = PIPEX_STATE_CLOSE_WAIT;
session->idle_time = 0;
LIST_INSERT_HEAD(&pipex_close_wait_list, session, state_list);
return (0);
}
void
pipex_export_session_stats(struct pipex_session *session,
struct pipex_statistics *stats)
{
uint64_t counters[pxc_ncounters];
memset(stats, 0, sizeof(*stats));
counters_read(session->stat_counters, counters, pxc_ncounters);
stats->ipackets = counters[pxc_ipackets];
stats->ierrors = counters[pxc_ierrors];
stats->ibytes = counters[pxc_ibytes];
stats->opackets = counters[pxc_opackets];
stats->oerrors = counters[pxc_oerrors];
stats->obytes = counters[pxc_obytes];
stats->idle_time = session->idle_time;
}
int
pipex_get_stat(struct pipex_session_stat_req *req, void *ownersc)
{
struct pipex_session *session;
int error = 0;
session = pipex_lookup_by_session_id(req->psr_protocol,
req->psr_session_id);
if (session == NULL)
return (EINVAL);
if (session->ownersc == ownersc)
pipex_export_session_stats(session, &req->psr_stat);
else
error = EINVAL;
pipex_rele_session(session);
return error;
}
int
pipex_get_closed(struct pipex_session_list_req *req, void *ownersc)
{
struct pipex_session *session, *session_tmp;
bzero(req, sizeof(*req));
mtx_enter(&pipex_list_mtx);
LIST_FOREACH_SAFE(session, &pipex_close_wait_list, state_list,
session_tmp) {
if (session->ownersc != ownersc)
continue;
req->plr_ppp_id[req->plr_ppp_id_count++] = session->ppp_id;
LIST_REMOVE(session, state_list);
session->state = PIPEX_STATE_CLOSE_WAIT2;
if (req->plr_ppp_id_count >= PIPEX_MAX_LISTREQ) {
if (!LIST_EMPTY(&pipex_close_wait_list))
req->plr_flags |= PIPEX_LISTREQ_MORE;
break;
}
}
mtx_leave(&pipex_list_mtx);
return (0);
}
struct pipex_session *
pipex_lookup_by_ip_address_locked(struct in_addr addr)
{
struct pipex_session *session;
struct sockaddr_in pipex_in4, pipex_in4mask;
MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
if (pipex_rd_head4 == NULL)
return (NULL);
bzero(&pipex_in4, sizeof(pipex_in4));
pipex_in4.sin_addr = addr;
pipex_in4.sin_family = AF_INET;
pipex_in4.sin_len = sizeof(pipex_in4);
bzero(&pipex_in4mask, sizeof(pipex_in4mask));
pipex_in4mask.sin_addr.s_addr = htonl(0xFFFFFFFFL);
pipex_in4mask.sin_family = AF_INET;
pipex_in4mask.sin_len = sizeof(pipex_in4mask);
session = (struct pipex_session *)rn_lookup(&pipex_in4, &pipex_in4mask,
pipex_rd_head4);
#ifdef PIPEX_DEBUG
if (session == NULL) {
char buf[INET_ADDRSTRLEN];
PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found (addr=%s)",
__func__, inet_ntop(AF_INET, &addr, buf, sizeof(buf))));
}
#endif
return (session);
}
struct pipex_session *
pipex_lookup_by_ip_address(struct in_addr addr)
{
struct pipex_session *session;
mtx_enter(&pipex_list_mtx);
session = pipex_lookup_by_ip_address_locked(addr);
if (session != NULL)
refcnt_take(&session->pxs_refcnt);
mtx_leave(&pipex_list_mtx);
return (session);
}
struct pipex_session *
pipex_lookup_by_session_id_locked(int protocol, int session_id)
{
struct pipex_hash_head *list;
struct pipex_session *session;
MUTEX_ASSERT_LOCKED(&pipex_list_mtx);
list = PIPEX_ID_HASHTABLE(session_id);
LIST_FOREACH(session, list, id_chain) {
if (session->protocol == protocol &&
session->session_id == session_id)
break;
}
#ifdef PIPEX_DEBUG
if (session == NULL)
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> session not found (session_id=%d)", __func__,
session_id));
#endif
return (session);
}
struct pipex_session *
pipex_lookup_by_session_id(int protocol, int session_id)
{
struct pipex_session *session;
mtx_enter(&pipex_list_mtx);
session = pipex_lookup_by_session_id_locked(protocol, session_id);
if (session != NULL)
refcnt_take(&session->pxs_refcnt);
mtx_leave(&pipex_list_mtx);
return (session);
}
/***********************************************************************
* Timer functions
***********************************************************************/
void
pipex_timer_start(void)
{
timeout_set_proc(&pipex_timer_ch, pipex_timer, NULL);
timeout_add_sec(&pipex_timer_ch, pipex_prune);
}
void
pipex_timer_stop(void)
{
timeout_del(&pipex_timer_ch);
}
void
pipex_timer(void *ignored_arg)
{
struct pipex_session *session, *session_tmp;
timeout_add_sec(&pipex_timer_ch, pipex_prune);
mtx_enter(&pipex_list_mtx);
/* walk through */
LIST_FOREACH_SAFE(session, &pipex_session_list, session_list,
session_tmp) {
switch (session->state) {
case PIPEX_STATE_OPENED:
if (session->timeout_sec == 0)
continue;
session->idle_time++;
if (session->idle_time < session->timeout_sec)
continue;
pipex_notify_close_session(session);
break;
case PIPEX_STATE_CLOSE_WAIT:
case PIPEX_STATE_CLOSE_WAIT2:
/* Waiting PIPEXDSESSION from userland */
session->idle_time++;
if (session->idle_time < PIPEX_CLOSE_TIMEOUT)
continue;
/* Release the sessions when timeout */
pipex_unlink_session_locked(session);
KASSERTMSG((session->flags & PIPEX_SFLAGS_PPPX) == 0,
"FIXME session must not be released when pppx");
pipex_rele_session(session);
break;
default:
break;
}
}
mtx_leave(&pipex_list_mtx);
}
/***********************************************************************
* Common network I/O functions. (tunnel protocol independent)
***********************************************************************/
void
pipex_ip_output(struct mbuf *m0, struct pipex_session *session)
{
int is_idle;
if ((session->flags & PIPEX_SFLAGS_MULTICAST) == 0) {
/*
* Multicast packet is a idle packet and it's not TCP.
*/
/* reset idle timer */
if (session->timeout_sec != 0) {
is_idle = 0;
m0 = ip_is_idle_packet(m0, &is_idle);
if (m0 == NULL)
goto dropped;
if (is_idle == 0) {
mtx_enter(&pipex_list_mtx);
/* update expire time */
if (session->state == PIPEX_STATE_OPENED)
session->idle_time = 0;
mtx_leave(&pipex_list_mtx);
}
}
/* adjust tcpmss */
if ((session->ppp_flags & PIPEX_PPP_ADJUST_TCPMSS) != 0) {
m0 = adjust_tcp_mss(m0, session->peer_mru);
if (m0 == NULL)
goto dropped;
}
pipex_ppp_output(m0, session, PPP_IP);
} else {
struct pipex_session *session_tmp;
struct mbuf *m;
m0->m_flags &= ~(M_BCAST|M_MCAST);
mtx_enter(&pipex_list_mtx);
session_tmp = LIST_FIRST(&pipex_session_list);
while (session_tmp != NULL) {
struct pipex_session *session_save = NULL;
if (session_tmp->ownersc != session->ownersc)
goto next;
refcnt_take(&session_tmp->pxs_refcnt);
mtx_leave(&pipex_list_mtx);
m = m_copym(m0, 0, M_COPYALL, M_NOWAIT);
if (m != NULL)
pipex_ppp_output(m, session_tmp, PPP_IP);
else
counters_inc(session_tmp->stat_counters,
pxc_oerrors);
mtx_enter(&pipex_list_mtx);
session_save = session_tmp;
next:
session_tmp = LIST_NEXT(session_tmp, session_list);
if (session_save != NULL)
pipex_rele_session(session_save);
}
mtx_leave(&pipex_list_mtx);
m_freem(m0);
}
return;
dropped:
counters_inc(session->stat_counters, pxc_oerrors);
}
void
pipex_ppp_output(struct mbuf *m0, struct pipex_session *session, int proto)
{
u_char *cp, hdr[16];
#ifdef PIPEX_MPPE
if (pipex_session_is_mppe_enabled(session)) {
if (proto == PPP_IP) {
m0 = pipex_mppe_output(m0, session, PPP_IP);
if (m0 == NULL)
goto drop;
proto = PPP_COMP;
}
}
#endif /* PIPEX_MPPE */
cp = hdr;
if (session->protocol != PIPEX_PROTO_PPPOE) {
/* PPPoE has not address and control field */
PUTCHAR(PPP_ALLSTATIONS, cp);
PUTCHAR(PPP_UI, cp);
}
PUTSHORT(proto, cp);
M_PREPEND(m0, cp - hdr, M_NOWAIT);
if (m0 == NULL)
goto drop;
memcpy(mtod(m0, u_char *), hdr, cp - hdr);
switch (session->protocol) {
#ifdef PIPEX_PPPOE
case PIPEX_PROTO_PPPOE:
pipex_pppoe_output(m0, session);
break;
#endif
#ifdef PIPEX_PPTP
case PIPEX_PROTO_PPTP:
mtx_enter(&session->pxs_mtx);
pipex_pptp_output(m0, session, 1, 1);
mtx_leave(&session->pxs_mtx);
break;
#endif
#ifdef PIPEX_L2TP
case PIPEX_PROTO_L2TP:
pipex_l2tp_output(m0, session);
break;
#endif
default:
goto drop;
}
return;
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_oerrors);
}
void
pipex_ppp_input(struct mbuf *m0, struct pipex_session *session, int decrypted)
{
int proto, hlen = 0;
struct mbuf *n;
#ifdef PIPEX_MPPE
again:
#endif
KASSERT(m0->m_pkthdr.len >= PIPEX_PPPMINLEN);
proto = pipex_ppp_proto(m0, session, 0, &hlen);
#ifdef PIPEX_MPPE
if (proto == PPP_COMP) {
if (decrypted)
goto drop;
/* checked this on ppp_common_input() already. */
KASSERT(pipex_session_is_mppe_accepted(session));
m_adj(m0, hlen);
m0 = pipex_mppe_input(m0, session);
if (m0 == NULL)
goto drop;
decrypted = 1;
goto again;
}
if (proto == PPP_CCP) {
if (decrypted)
goto drop;
#if NBPFILTER > 0
{
struct ifnet *ifp;
if ((ifp = if_get(session->ifindex)) != NULL) {
if (ifp->if_bpf && ifp->if_type == IFT_PPP)
bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_IN);
}
if_put(ifp);
}
#endif
m_adj(m0, hlen);
pipex_ccp_input(m0, session);
return;
}
#endif
m_adj(m0, hlen);
if (!ALIGNED_POINTER(mtod(m0, caddr_t), uint32_t)) {
n = m_dup_pkt(m0, 0, M_NOWAIT);
if (n == NULL)
goto drop;
m_freem(m0);
m0 = n;
}
switch (proto) {
case PPP_IP:
if (!decrypted && pipex_session_is_mppe_required(session))
/*
* if ip packet received when mppe
* is required, discard it.
*/
goto drop;
pipex_ip_input(m0, session);
return;
#ifdef INET6
case PPP_IPV6:
if (!decrypted && pipex_session_is_mppe_required(session))
/*
* if ip packet received when mppe
* is required, discard it.
*/
goto drop;
pipex_ip6_input(m0, session);
return;
#endif
default:
if (decrypted)
goto drop;
/* protocol must be checked on pipex_common_input() already */
KASSERT(0);
goto drop;
}
return;
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
}
void
pipex_ip_input(struct mbuf *m0, struct pipex_session *session)
{
struct ifnet *ifp;
struct ip *ip;
int len;
int is_idle;
/* change recvif */
m0->m_pkthdr.ph_ifidx = session->ifindex;
if (ISSET(session->ppp_flags, PIPEX_PPP_INGRESS_FILTER)) {
PIPEX_PULLUP(m0, sizeof(struct ip));
if (m0 == NULL)
goto drop;
/* ingress filter */
ip = mtod(m0, struct ip *);
if ((ip->ip_src.s_addr & session->ip_netmask.sin_addr.s_addr) !=
session->ip_address.sin_addr.s_addr) {
char src[INET_ADDRSTRLEN];
pipex_session_log(session, LOG_DEBUG,
"ip packet discarded by ingress filter (src %s)",
inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src)));
goto drop;
}
}
/* idle timer */
if (session->timeout_sec != 0) {
is_idle = 0;
m0 = ip_is_idle_packet(m0, &is_idle);
if (m0 == NULL)
goto drop;
if (is_idle == 0) {
/* update expire time */
mtx_enter(&pipex_list_mtx);
if (session->state == PIPEX_STATE_OPENED)
session->idle_time = 0;
mtx_leave(&pipex_list_mtx);
}
}
/* adjust tcpmss */
if (session->ppp_flags & PIPEX_PPP_ADJUST_TCPMSS) {
m0 = adjust_tcp_mss(m0, session->peer_mru);
if (m0 == NULL)
goto drop;
}
#if NPF > 0
pf_pkt_addr_changed(m0);
#endif
len = m0->m_pkthdr.len;
if ((ifp = if_get(session->ifindex)) == NULL)
goto drop;
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap_af(ifp->if_bpf, AF_INET, m0, BPF_DIRECTION_IN);
#endif
counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len);
counters_pkt(session->stat_counters, pxc_ipackets, pxc_ibytes, len);
ipv4_input(ifp, m0);
if_put(ifp);
return;
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
}
#ifdef INET6
void
pipex_ip6_input(struct mbuf *m0, struct pipex_session *session)
{
struct ifnet *ifp;
int len;
/* change recvif */
m0->m_pkthdr.ph_ifidx = session->ifindex;
/*
* XXX: what is reasonable ingress filter ???
* only one address is enough ??
*/
/* XXX: we must define idle packet for IPv6(ICMPv6). */
/*
* XXX: tcpmss adjustment for IPv6 is required???
* We may use PMTUD in IPv6....
*/
#if NPF > 0
pf_pkt_addr_changed(m0);
#endif
len = m0->m_pkthdr.len;
if ((ifp = if_get(session->ifindex)) == NULL)
goto drop;
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap_af(ifp->if_bpf, AF_INET6, m0, BPF_DIRECTION_IN);
#endif
counters_pkt(ifp->if_counters, ifc_ipackets, ifc_ibytes, len);
counters_pkt(session->stat_counters, pxc_ipackets, pxc_ibytes, len);
ipv6_input(ifp, m0);
if_put(ifp);
return;
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
}
#endif
struct mbuf *
pipex_common_input(struct pipex_session *session, struct mbuf *m0, int hlen,
int plen, int locked)
{
int proto, ppphlen;
u_char code;
if ((m0->m_pkthdr.len < hlen + PIPEX_PPPMINLEN) ||
(plen < PIPEX_PPPMINLEN)) {
if (locked)
mtx_leave(&session->pxs_mtx);
goto drop;
}
proto = pipex_ppp_proto(m0, session, hlen, &ppphlen);
switch (proto) {
#ifdef PIPEX_MPPE
case PPP_CCP:
code = 0;
KASSERT(m0->m_pkthdr.len >= hlen + ppphlen + 1);
m_copydata(m0, hlen + ppphlen, 1, &code);
if (code != CCP_RESETREQ && code != CCP_RESETACK)
goto not_ours;
break;
case PPP_COMP:
if (pipex_session_is_mppe_accepted(session))
break;
goto not_ours;
#endif
case PPP_IP:
#ifdef INET6
case PPP_IPV6:
#endif
break;
default:
goto not_ours;
}
if (locked)
mtx_leave(&session->pxs_mtx);
/* ok, The packet is for PIPEX */
m_adj(m0, hlen);/* cut off the tunnel protocol header */
/* ensure the mbuf length equals the PPP frame length */
if (m0->m_pkthdr.len < plen)
goto drop;
if (m0->m_pkthdr.len > plen) {
if (m0->m_len == m0->m_pkthdr.len) {
m0->m_len = plen;
m0->m_pkthdr.len = plen;
} else
m_adj(m0, plen - m0->m_pkthdr.len);
}
pipex_ppp_input(m0, session, 0);
return (NULL);
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
return (NULL);
not_ours:
return (m0); /* Not to be handled by PIPEX */
}
/*
* pipex_ppp_proto
*/
int
pipex_ppp_proto(struct mbuf *m0, struct pipex_session *session, int off,
int *hlenp)
{
int proto;
u_char *cp, pktbuf[4];
KASSERT(m0->m_pkthdr.len > sizeof(pktbuf));
m_copydata(m0, off, sizeof(pktbuf), pktbuf);
cp = pktbuf;
if (pipex_session_has_acf(session)) {
if (cp[0] == PPP_ALLSTATIONS && cp[1] == PPP_UI)
cp += 2;
#ifdef PIPEX_DEBUG
else if (!pipex_session_is_acfc_accepted(session))
PIPEX_DBG((session, LOG_DEBUG,
"no acf but acfc is not accepted by the peer."));
#endif
}
if ((*cp & 0x01) != 0) {
if (!pipex_session_is_pfc_accepted(session)) {
PIPEX_DBG((session, LOG_DEBUG, "Received a broken ppp "
"frame. No protocol field. %02x-%02x",
cp[0], cp[1]));
return (-1);
}
GETCHAR(proto, cp);
} else
GETSHORT(proto, cp);
if (hlenp != NULL)
*hlenp = cp - pktbuf;
return (proto);
}
#ifdef PIPEX_PPPOE
/***********************************************************************
* PPPoE
***********************************************************************/
static const u_char pipex_pppoe_padding[ETHERMIN];
/*
* pipex_pppoe_lookup_session
*/
struct pipex_session *
pipex_pppoe_lookup_session(struct mbuf *m0)
{
struct pipex_session *session;
struct pipex_pppoe_header pppoe;
/* short packet */
if (m0->m_pkthdr.len < (sizeof(struct ether_header) + sizeof(pppoe)))
return (NULL);
m_copydata(m0, sizeof(struct ether_header),
sizeof(struct pipex_pppoe_header), &pppoe);
pppoe.session_id = ntohs(pppoe.session_id);
session = pipex_lookup_by_session_id(PIPEX_PROTO_PPPOE,
pppoe.session_id);
#ifdef PIPEX_DEBUG
if (session == NULL)
PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found (id=%d)",
__func__, pppoe.session_id));
#endif
if (session && session->proto.pppoe.over_ifidx !=
m0->m_pkthdr.ph_ifidx) {
pipex_rele_session(session);
session = NULL;
}
return (session);
}
struct mbuf *
pipex_pppoe_input(struct mbuf *m0, struct pipex_session *session)
{
int hlen;
struct pipex_pppoe_header pppoe;
/* already checked at pipex_pppoe_lookup_session */
KASSERT(m0->m_pkthdr.len >= (sizeof(struct ether_header) +
sizeof(pppoe)));
m_copydata(m0, sizeof(struct ether_header),
sizeof(struct pipex_pppoe_header), &pppoe);
hlen = sizeof(struct ether_header) + sizeof(struct pipex_pppoe_header);
m0 = pipex_common_input(session, m0, hlen, ntohs(pppoe.length), 0);
if (m0 == NULL)
return (NULL);
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
return (NULL);
}
/*
* pipex_ppope_output
*/
void
pipex_pppoe_output(struct mbuf *m0, struct pipex_session *session)
{
struct pipex_pppoe_header *pppoe;
int len, padlen;
/* save length for pppoe header */
len = m0->m_pkthdr.len;
/* prepend protocol header */
M_PREPEND(m0, sizeof(struct pipex_pppoe_header), M_NOWAIT);
if (m0 == NULL) {
PIPEX_DBG((NULL, LOG_ERR,
"<%s> cannot prepend header.", __func__));
counters_inc(session->stat_counters, pxc_oerrors);
return;
}
padlen = ETHERMIN - m0->m_pkthdr.len;
if (padlen > 0)
m_copyback(m0, m0->m_pkthdr.len, padlen, pipex_pppoe_padding,
M_NOWAIT);
/* setup pppoe header information */
pppoe = mtod(m0, struct pipex_pppoe_header *);
pppoe->vertype = PIPEX_PPPOE_VERTYPE;
pppoe->code = PIPEX_PPPOE_CODE_SESSION;
pppoe->session_id = htons(session->session_id);
pppoe->length = htons(len);
m0->m_pkthdr.ph_ifidx = session->proto.pppoe.over_ifidx;
refcnt_take(&session->pxs_refcnt);
m0->m_pkthdr.ph_cookie = session;
m0->m_flags &= ~(M_BCAST|M_MCAST);
if (mq_enqueue(&pipexoutq, m0) != 0) {
counters_inc(session->stat_counters, pxc_oerrors);
pipex_rele_session(session);
} else
schednetisr(NETISR_PIPEX);
}
#endif /* PIPEX_PPPOE */
#ifdef PIPEX_PPTP
/***********************************************************************
* PPTP
***********************************************************************/
void
pipex_pptp_output(struct mbuf *m0, struct pipex_session *session,
int has_seq, int has_ack)
{
int len, reqlen;
struct pipex_gre_header *gre = NULL;
struct ip *ip;
u_char *cp;
MUTEX_ASSERT_LOCKED(&session->pxs_mtx);
reqlen = PIPEX_IPGRE_HDRLEN + (has_seq + has_ack) * 4;
len = 0;
if (m0 != NULL) {
/* save length for gre header */
len = m0->m_pkthdr.len;
/* prepend protocol header */
M_PREPEND(m0, reqlen, M_NOWAIT);
if (m0 == NULL)
goto drop;
} else {
MGETHDR(m0, M_DONTWAIT, MT_DATA);
if (m0 && reqlen > MHLEN) {
MCLGET(m0, M_DONTWAIT);
if ((m0->m_flags & M_EXT) == 0) {
m_freem(m0);
m0 = NULL;
}
}
if (m0 == NULL)
goto drop;
m0->m_pkthdr.len = m0->m_len = reqlen;
}
/* setup ip header information */
ip = mtod(m0, struct ip *);
ip->ip_len = htons(m0->m_pkthdr.len);
ip->ip_off = 0;
ip->ip_ttl = MAXTTL;
ip->ip_p = IPPROTO_GRE;
ip->ip_tos = 0;
ip->ip_src = session->local.sin4.sin_addr;
ip->ip_dst = session->peer.sin4.sin_addr;
#if NPF > 0
pf_pkt_addr_changed(m0);
#endif
/* setup gre(ver1) header information */
gre = PIPEX_SEEK_NEXTHDR(ip, sizeof(struct ip),
struct pipex_gre_header *);
gre->type = htons(PIPEX_GRE_PROTO_PPP);
gre->call_id = htons(session->peer_session_id);
gre->flags = PIPEX_GRE_KFLAG | PIPEX_GRE_VER; /* do htons later */
gre->len = htons(len);
cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *);
if (has_seq) {
gre->flags |= PIPEX_GRE_SFLAG;
PUTLONG(session->proto.pptp.snd_nxt, cp);
session->proto.pptp.snd_nxt++;
session->proto.pptp.snd_gap++;
}
if (has_ack) {
gre->flags |= PIPEX_GRE_AFLAG;
session->proto.pptp.rcv_acked = session->proto.pptp.rcv_nxt - 1;
PUTLONG(session->proto.pptp.rcv_acked, cp);
}
gre->flags = htons(gre->flags);
m0->m_pkthdr.ph_ifidx = session->ifindex;
ip_send(m0);
if (len > 0) { /* network layer only */
/* countup statistics */
counters_pkt(session->stat_counters, pxc_opackets,
pxc_obytes, len);
}
return;
drop:
counters_inc(session->stat_counters, pxc_oerrors);
}
struct pipex_session *
pipex_pptp_lookup_session(struct mbuf *m0)
{
struct pipex_session *session;
struct pipex_gre_header gre;
struct ip ip;
uint16_t flags;
uint16_t id;
int hlen;
if (m0->m_pkthdr.len < PIPEX_IPGRE_HDRLEN) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> packet length is too short", __func__));
goto not_ours;
}
/* get ip header info */
m_copydata(m0, 0, sizeof(struct ip), &ip);
hlen = ip.ip_hl << 2;
/*
* m0 has already passed ip_input(), so there is
* no necessity for ip packet inspection.
*/
/* get gre flags */
m_copydata(m0, hlen, sizeof(gre), &gre);
flags = ntohs(gre.flags);
/* gre version must be '1' */
if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> gre header wrong version.", __func__));
goto not_ours;
}
/* gre keys must be present */
if ((flags & PIPEX_GRE_KFLAG) == 0) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> gre header has no keys.", __func__));
goto not_ours;
}
/* flag check */
if ((flags & PIPEX_GRE_UNUSEDFLAGS) != 0) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> gre header has unused flags at pptp.", __func__));
goto not_ours;
}
/* lookup pipex session table */
id = ntohs(gre.call_id);
session = pipex_lookup_by_session_id(PIPEX_PROTO_PPTP, id);
#ifdef PIPEX_DEBUG
if (session == NULL) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> session not found (id=%d)", __func__, id));
goto not_ours;
}
#endif
return (session);
not_ours:
return (NULL);
}
struct mbuf *
pipex_pptp_input(struct mbuf *m0, struct pipex_session *session)
{
int hlen, has_seq, has_ack, nseq;
const char *reason = "";
u_char *cp, *seqp = NULL, *ackp = NULL;
uint32_t flags, seq = 0, ack = 0;
struct ip *ip;
struct pipex_gre_header *gre;
struct pipex_pptp_session *pptp_session;
int rewind = 0;
KASSERT(m0->m_pkthdr.len >= PIPEX_IPGRE_HDRLEN);
pptp_session = &session->proto.pptp;
/* get ip header */
ip = mtod(m0, struct ip *);
hlen = ip->ip_hl << 2;
/* seek gre header */
gre = PIPEX_SEEK_NEXTHDR(ip, hlen, struct pipex_gre_header *);
flags = ntohs(gre->flags);
/* pullup for seek sequences in header */
has_seq = (flags & PIPEX_GRE_SFLAG) ? 1 : 0;
has_ack = (flags & PIPEX_GRE_AFLAG) ? 1 : 0;
hlen = PIPEX_IPGRE_HDRLEN + 4 * (has_seq + has_ack);
if (m0->m_len < hlen) {
m0 = m_pullup(m0, hlen);
if (m0 == NULL) {
PIPEX_DBG((session, LOG_DEBUG, "pullup failed."));
goto drop;
}
}
/* check sequence */
cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header),u_char *);
if (has_seq) {
seqp = cp;
GETLONG(seq, cp);
}
mtx_enter(&session->pxs_mtx);
if (has_ack) {
ackp = cp;
GETLONG(ack, cp);
if (ack + 1 == pptp_session->snd_una) {
/* ack has not changed before */
} else if (SEQ32_LT(ack, pptp_session->snd_una)) {
/* OoO ack packets should not be dropped. */
rewind = 1;
} else if (SEQ32_GT(ack, pptp_session->snd_nxt)) {
reason = "ack for unknown sequence";
goto out_seq;
} else
pptp_session->snd_una = ack + 1;
}
if (!has_seq) {
/* ack only packet */
goto not_ours;
}
if (SEQ32_LT(seq, pptp_session->rcv_nxt)) {
rewind = 1;
if (SEQ32_LT(seq,
pptp_session->rcv_nxt - PIPEX_REWIND_LIMIT)) {
reason = "out of sequence";
goto out_seq;
}
} else if (SEQ32_GE(seq, pptp_session->rcv_nxt +
pptp_session->maxwinsz)) {
pipex_session_log(session, LOG_DEBUG,
"received packet caused window overflow. seq=%u(%u-%u)"
"may lost %d packets.", seq, pptp_session->rcv_nxt,
pptp_session->rcv_nxt + pptp_session->maxwinsz,
(int)SEQ32_SUB(seq, pptp_session->rcv_nxt));
}
seq++;
nseq = SEQ32_SUB(seq, pptp_session->rcv_nxt);
if (!rewind) {
pptp_session->rcv_nxt = seq;
if (SEQ32_SUB(seq, pptp_session->rcv_acked) >
roundup(pptp_session->winsz, 2) / 2) /* Send ack only packet. */
pipex_pptp_output(NULL, session, 0, 1);
}
/*
* The following pipex_common_input() will release `pxs_mtx'
* deep within if the packet will be consumed. In the error
* path lock will be held all the time. So increment `rcv_gap'
* here, and on the error path back it out, no atomicity will
* be lost in all cases.
*/
if (!rewind)
session->proto.pptp.rcv_gap += nseq;
m0 = pipex_common_input(session, m0, hlen, ntohs(gre->len), 1);
if (m0 == NULL) {
/*
* pipex_common_input() releases lock if the
* packet was consumed.
*/
return (NULL);
}
if (rewind)
goto out_seq;
else {
/* The packet is not ours, back out `rcv_gap'. */
session->proto.pptp.rcv_gap -= nseq;
}
not_ours:
seq--; /* revert original seq value */
/*
* overwrite sequence numbers to adjust a gap between pipex and
* userland.
*/
if (seqp != NULL) {
seq -= pptp_session->rcv_gap;
PUTLONG(seq, seqp);
}
if (ackp != NULL) {
if (pptp_session->snd_nxt == pptp_session->snd_una) {
ack -= session->proto.pptp.snd_gap;
pptp_session->ul_snd_una = ack;
} else {
/*
* There are sending packets they are not acked.
* In this situation, (ack - snd_gap) may points
* before sending window of userland. So we don't
* update the ack number.
*/
ack = pptp_session->ul_snd_una;
}
PUTLONG(ack, ackp);
}
mtx_leave(&session->pxs_mtx);
return (m0);
out_seq:
pipex_session_log(session, LOG_DEBUG,
"Received bad data packet: %s: seq=%u(%u-%u) ack=%u(%u-%u)",
reason, seq, pptp_session->rcv_nxt,
pptp_session->rcv_nxt + pptp_session->maxwinsz,
ack, pptp_session->snd_una,
pptp_session->snd_nxt);
mtx_leave(&session->pxs_mtx);
/* FALLTHROUGH */
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
return (NULL);
}
struct pipex_session *
pipex_pptp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst)
{
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = dst;
return pipex_pptp_userland_lookup_session(m0, sintosa(&sin));
}
#ifdef INET6
struct pipex_session *
pipex_pptp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst)
{
struct sockaddr_in6 sin6;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(sin6);
sin6.sin6_family = AF_INET6;
in6_recoverscope(&sin6, &dst);
return pipex_pptp_userland_lookup_session(m0, sin6tosa(&sin6));
}
#endif
struct pipex_session *
pipex_pptp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa)
{
struct pipex_gre_header gre;
struct pipex_hash_head *list;
struct pipex_session *session;
uint16_t id, flags;
/* pullup */
if (m0->m_pkthdr.len < sizeof(gre)) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> packet length is too short", __func__));
return (NULL);
}
/* get flags */
m_copydata(m0, 0, sizeof(struct pipex_gre_header), &gre);
flags = ntohs(gre.flags);
/* gre version must be '1' */
if ((flags & PIPEX_GRE_VERMASK) != PIPEX_GRE_VER) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> gre header wrong version.", __func__));
return (NULL);
}
/* gre keys must be present */
if ((flags & PIPEX_GRE_KFLAG) == 0) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> gre header has no keys.", __func__));
return (NULL);
}
/* lookup pipex session table */
id = ntohs(gre.call_id);
mtx_enter(&pipex_list_mtx);
list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa));
LIST_FOREACH(session, list, peer_addr_chain) {
if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0)
continue;
if (session->peer_session_id == id)
break;
}
if (session != NULL)
refcnt_take(&session->pxs_refcnt);
mtx_leave(&pipex_list_mtx);
#ifdef PIPEX_DEBUG
if (session == NULL) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> session not found (,call_id=%d)",
__func__, (int)gre.call_id));
}
#endif
return (session);
}
/*
* pipex_pptp_userland_output
*/
struct mbuf *
pipex_pptp_userland_output(struct mbuf *m0, struct pipex_session *session)
{
int len;
struct pipex_gre_header *gre, gre0;
uint16_t flags;
u_char *cp, *cp0;
uint32_t val32;
len = sizeof(struct pipex_gre_header);
m_copydata(m0, 0, len, &gre0);
gre = &gre0;
flags = ntohs(gre->flags);
if ((flags & PIPEX_GRE_SFLAG) != 0)
len += 4;
if ((flags & PIPEX_GRE_AFLAG) != 0)
len += 4;
/* check length */
PIPEX_PULLUP(m0, len);
if (m0 == NULL) {
PIPEX_DBG((session, LOG_DEBUG, "gre header is too short."));
return (NULL);
}
gre = mtod(m0, struct pipex_gre_header *);
cp = PIPEX_SEEK_NEXTHDR(gre, sizeof(struct pipex_gre_header), u_char *);
mtx_enter(&session->pxs_mtx);
/*
* overwrite sequence numbers to adjust a gap between pipex and
* userland.
*/
if ((flags & PIPEX_GRE_SFLAG) != 0) {
cp0 = cp;
GETLONG(val32, cp);
val32 += session->proto.pptp.snd_gap;
PUTLONG(val32, cp0);
session->proto.pptp.snd_nxt++;
}
if ((flags & PIPEX_GRE_AFLAG) != 0) {
cp0 = cp;
GETLONG(val32, cp);
val32 += session->proto.pptp.rcv_gap;
PUTLONG(val32, cp0);
if (SEQ32_GT(val32, session->proto.pptp.rcv_acked))
session->proto.pptp.rcv_acked = val32;
}
mtx_leave(&session->pxs_mtx);
return (m0);
}
#endif /* PIPEX_PPTP */
#ifdef PIPEX_L2TP
/***********************************************************************
* L2TP support
***********************************************************************/
void
pipex_l2tp_output(struct mbuf *m0, struct pipex_session *session)
{
int hlen, plen, datalen;
struct pipex_l2tp_header *l2tp = NULL;
struct pipex_l2tp_seq_header *seq = NULL;
struct udphdr *udp;
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif
struct m_tag *mtag;
hlen = sizeof(struct pipex_l2tp_header) +
((pipex_session_is_l2tp_data_sequencing_on(session))
? sizeof(struct pipex_l2tp_seq_header) : 0) +
sizeof(struct udphdr) +
#ifdef INET6
((session->peer.sin6.sin6_family == AF_INET6)
? sizeof(struct ip6_hdr) : sizeof(struct ip));
#else
sizeof(struct ip);
#endif
datalen = 0;
if (m0 != NULL) {
datalen = m0->m_pkthdr.len;
M_PREPEND(m0, hlen, M_NOWAIT);
if (m0 == NULL)
goto drop;
} else {
MGETHDR(m0, M_DONTWAIT, MT_DATA);
if (m0 == NULL)
goto drop;
KASSERT(hlen <= MHLEN);
m0->m_pkthdr.len = m0->m_len = hlen;
}
#ifdef INET6
hlen = (session->peer.sin6.sin6_family == AF_INET6)
? sizeof(struct ip6_hdr) : sizeof(struct ip);
#else
hlen = sizeof(struct ip);
#endif
plen = datalen + sizeof(struct pipex_l2tp_header) +
((pipex_session_is_l2tp_data_sequencing_on(session))
? sizeof(struct pipex_l2tp_seq_header) : 0);
l2tp = (struct pipex_l2tp_header *)
(mtod(m0, caddr_t) + hlen + sizeof(struct udphdr));
l2tp->flagsver = PIPEX_L2TP_VER | PIPEX_L2TP_FLAG_LENGTH;
l2tp->length = htons(plen);
l2tp->tunnel_id = htons(session->proto.l2tp.peer_tunnel_id);
l2tp->session_id = htons(session->peer_session_id);
if (pipex_session_is_l2tp_data_sequencing_on(session)) {
seq = (struct pipex_l2tp_seq_header *)(l2tp + 1);
l2tp->flagsver |= PIPEX_L2TP_FLAG_SEQUENCE;
mtx_enter(&session->pxs_mtx);
seq->ns = htons(session->proto.l2tp.ns_nxt);
session->proto.l2tp.ns_nxt++;
session->proto.l2tp.ns_gap++;
session->proto.l2tp.nr_acked = session->proto.l2tp.nr_nxt - 1;
seq->nr = htons(session->proto.l2tp.nr_acked);
mtx_leave(&session->pxs_mtx);
}
l2tp->flagsver = htons(l2tp->flagsver);
plen += sizeof(struct udphdr);
udp = (struct udphdr *)(mtod(m0, caddr_t) + hlen);
udp->uh_sport = session->local.sin6.sin6_port;
udp->uh_dport = session->peer.sin6.sin6_port;
udp->uh_ulen = htons(plen);
udp->uh_sum = 0;
m0->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
m0->m_pkthdr.ph_ifidx = session->ifindex;
#if NPF > 0
pf_pkt_addr_changed(m0);
#endif
switch (session->peer.sin6.sin6_family) {
case AF_INET:
ip = mtod(m0, struct ip *);
ip->ip_p = IPPROTO_UDP;
ip->ip_src = session->local.sin4.sin_addr;
ip->ip_dst = session->peer.sin4.sin_addr;
ip->ip_len = htons(hlen + plen);
ip->ip_ttl = MAXTTL;
ip->ip_tos = 0;
ip->ip_off = 0;
mtx_enter(&session->pxs_mtx);
if (session->proto.l2tp.ipsecflowinfo > 0) {
if ((mtag = m_tag_get(PACKET_TAG_IPSEC_FLOWINFO,
sizeof(u_int32_t), M_NOWAIT)) == NULL) {
mtx_leave(&session->pxs_mtx);
goto drop;
}
*(u_int32_t *)(mtag + 1) =
session->proto.l2tp.ipsecflowinfo;
m_tag_prepend(m0, mtag);
}
mtx_leave(&session->pxs_mtx);
ip_send(m0);
break;
#ifdef INET6
case AF_INET6:
ip6 = mtod(m0, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_UDP;
ip6->ip6_src = session->local.sin6.sin6_addr;
(void)in6_embedscope(&ip6->ip6_dst,
&session->peer.sin6, NULL);
/* ip6->ip6_plen will be filled in ip6_output. */
ip6_send(m0);
break;
#endif
}
udpstat_inc(udps_opackets);
if (datalen > 0) { /* network layer only */
/* countup statistics */
counters_pkt(session->stat_counters, pxc_opackets,
pxc_obytes, datalen);
}
return;
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_oerrors);
}
struct pipex_session *
pipex_l2tp_lookup_session(struct mbuf *m0, int off)
{
struct pipex_session *session;
uint16_t flags, session_id, ver;
u_char *cp, buf[PIPEX_L2TP_MINLEN];
if (m0->m_pkthdr.len < off + PIPEX_L2TP_MINLEN) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> packet length is too short", __func__));
goto not_ours;
}
/* get first 16bits of L2TP */
m_copydata(m0, off, sizeof(buf), buf);
cp = buf;
GETSHORT(flags, cp);
ver = flags & PIPEX_L2TP_VER_MASK;
/* l2tp version must be '2' */
if (ver != PIPEX_L2TP_VER) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> l2tp header wrong version %u.", __func__, ver));
goto not_ours;
}
if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0)
goto not_ours;
if (flags & PIPEX_L2TP_FLAG_LENGTH)
cp += 2; /* skip length field */
cp += 2; /* skip tunnel-id field */
GETSHORT(session_id, cp); /* get session-id field */
/* lookup pipex session table */
session = pipex_lookup_by_session_id(PIPEX_PROTO_L2TP, session_id);
#ifdef PIPEX_DEBUG
if (session == NULL) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> session not found (id=%d)", __func__, session_id));
goto not_ours;
}
#endif
return (session);
not_ours:
return (NULL);
}
struct mbuf *
pipex_l2tp_input(struct mbuf *m0, int off0, struct pipex_session *session,
uint32_t ipsecflowinfo)
{
struct pipex_l2tp_session *l2tp_session;
int length = 0, offset = 0, hlen, nseq;
u_char *cp, *nsp = NULL, *nrp = NULL;
uint16_t flags, ns = 0, nr = 0;
int rewind = 0;
mtx_enter(&session->pxs_mtx);
l2tp_session = &session->proto.l2tp;
l2tp_session->ipsecflowinfo = ipsecflowinfo;
m_copydata(m0, off0, sizeof(flags), &flags);
flags = ntohs(flags) & PIPEX_L2TP_FLAG_MASK;
KASSERT((flags & PIPEX_L2TP_FLAG_TYPE) == 0);
hlen = 2; /* flags and version fields */
if (flags & PIPEX_L2TP_FLAG_LENGTH) /* length */
hlen += 2;
hlen += 4; /* tunnel-id and session-id */
if (flags & PIPEX_L2TP_FLAG_SEQUENCE) /* ns and nr */
hlen += 4;
if (flags & PIPEX_L2TP_FLAG_OFFSET) /* offset */
hlen += 2;
PIPEX_PULLUP(m0, off0 + hlen);
if (m0 == NULL)
goto drop;
cp = mtod(m0, u_char *) + off0;
cp += 2; /* flags and version */
if (flags & PIPEX_L2TP_FLAG_LENGTH)
GETSHORT(length, cp);
else
length = m0->m_pkthdr.len - off0;
cp += 4; /* skip tunnel-id and session-id field */
/* pullup for seek sequences in header */
nseq = 0;
if (flags & PIPEX_L2TP_FLAG_SEQUENCE) {
nsp = cp;
GETSHORT(ns, cp);
nrp = cp;
GETSHORT(nr, cp);
nr++;
if (SEQ16_GT(nr, l2tp_session->ns_una) &&
SEQ16_LE(nr, l2tp_session->ns_nxt))
/* update 'ns_una' only if the ns is in valid range */
l2tp_session->ns_una = nr;
if (SEQ16_LT(ns, l2tp_session->nr_nxt)) {
rewind = 1;
if (SEQ16_LT(ns,
l2tp_session->nr_nxt - PIPEX_REWIND_LIMIT))
goto out_seq;
}
ns++;
nseq = SEQ16_SUB(ns, l2tp_session->nr_nxt);
if (!rewind)
l2tp_session->nr_nxt = ns;
}
if (flags & PIPEX_L2TP_FLAG_OFFSET)
GETSHORT(offset, cp);
length -= hlen + offset;
hlen += off0 + offset;
/*
* The following pipex_common_input() will release `pxs_mtx'
* deep within if the packet will be consumed. In the error
* path lock will be held all the time. So increment `nr_gap'
* here, and on the error path back it out, no atomicity will
* be lost in all cases.
*/
if (!rewind)
session->proto.l2tp.nr_gap += nseq;
m0 = pipex_common_input(session, m0, hlen, length, 1);
if (m0 == NULL) {
/*
* pipex_common_input() releases lock if the
* packet was consumed.
*/
return (NULL);
}
if (rewind)
goto out_seq;
else {
/* The packet is not ours, backout `nr_gap'. */
session->proto.l2tp.nr_gap -= nseq;
}
/*
* overwrite sequence numbers to adjust a gap between pipex and
* userland.
*/
if (flags & PIPEX_L2TP_FLAG_SEQUENCE) {
--ns; --nr; /* revert original values */
ns -= l2tp_session->nr_gap;
PUTSHORT(ns, nsp);
if (l2tp_session->ns_nxt == l2tp_session->ns_una) {
nr -= l2tp_session->ns_gap;
l2tp_session->ul_ns_una = nr;
} else {
/*
* There are sending packets they are not acked.
* In this situation, (ack - snd_gap) may points
* before sending window of userland. So we don't
* update the ack number.
*/
nr = l2tp_session->ul_ns_una;
}
PUTSHORT(nr, nrp);
}
mtx_leave(&session->pxs_mtx);
return (m0);
out_seq:
pipex_session_log(session, LOG_DEBUG,
"Received bad data packet: out of sequence: seq=%u(%u-) "
"ack=%u(%u-%u)", ns, l2tp_session->nr_nxt, nr, l2tp_session->ns_una,
l2tp_session->ns_nxt);
/* FALLTHROUGH */
drop:
mtx_leave(&session->pxs_mtx);
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
return (NULL);
}
struct pipex_session *
pipex_l2tp_userland_lookup_session_ipv4(struct mbuf *m0, struct in_addr dst)
{
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = dst;
return pipex_l2tp_userland_lookup_session(m0, sintosa(&sin));
}
#ifdef INET6
struct pipex_session *
pipex_l2tp_userland_lookup_session_ipv6(struct mbuf *m0, struct in6_addr dst)
{
struct sockaddr_in6 sin6;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(sin6);
sin6.sin6_family = AF_INET6;
in6_recoverscope(&sin6, &dst);
return pipex_l2tp_userland_lookup_session(m0, sin6tosa(&sin6));
}
#endif
struct pipex_session *
pipex_l2tp_userland_lookup_session(struct mbuf *m0, struct sockaddr *sa)
{
struct pipex_l2tp_header l2tp;
struct pipex_hash_head *list;
struct pipex_session *session;
uint16_t session_id, tunnel_id, flags;
if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
return (NULL);
/* pullup */
if (m0->m_pkthdr.len < sizeof(l2tp)) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> packet length is too short", __func__));
return (NULL);
}
/* get flags */
m_copydata(m0, 0, sizeof(l2tp), &l2tp);
flags = ntohs(l2tp.flagsver);
/* l2tp version must be '2' */
if ((flags & PIPEX_L2TP_VER_MASK) != PIPEX_L2TP_VER) {
PIPEX_DBG((NULL, LOG_DEBUG,
"<%s> l2tp header wrong version.", __func__));
return (NULL);
}
/* We need L2TP data messages only */
if ((flags & PIPEX_L2TP_FLAG_TYPE) != 0)
return (NULL);
/* No need to hook packets that don't have the sequence field */
if ((flags & PIPEX_L2TP_FLAG_SEQUENCE) == 0)
return (NULL);
session_id = ntohs(l2tp.session_id);
tunnel_id = ntohs(l2tp.tunnel_id);
mtx_enter(&pipex_list_mtx);
list = PIPEX_PEER_ADDR_HASHTABLE(pipex_sockaddr_hash_key(sa));
LIST_FOREACH(session, list, peer_addr_chain) {
if (pipex_sockaddr_compar_addr(&session->peer.sa, sa) != 0)
continue;
if (session->proto.l2tp.peer_tunnel_id != tunnel_id)
continue;
if (session->peer_session_id == session_id)
break;
}
if (session != NULL)
refcnt_take(&session->pxs_refcnt);
mtx_leave(&pipex_list_mtx);
#ifdef PIPEX_DEBUG
if (session == NULL) {
PIPEX_DBG((NULL, LOG_DEBUG, "<%s> session not found "
"(tunnel_id=%d, session_id=%d)", __func__,
tunnel_id, session_id));
}
#endif
return (session);
}
struct mbuf *
pipex_l2tp_userland_output(struct mbuf *m0, struct pipex_session *session)
{
struct pipex_l2tp_header *l2tp;
struct pipex_l2tp_seq_header *seq;
uint16_t ns, nr;
/* check length */
PIPEX_PULLUP(m0, sizeof(struct pipex_l2tp_header) +
sizeof(struct pipex_l2tp_seq_header));
if (m0 == NULL)
return (NULL);
l2tp = mtod(m0, struct pipex_l2tp_header *);
KASSERT(ntohs(l2tp->flagsver) & PIPEX_L2TP_FLAG_SEQUENCE);
/*
* overwrite sequence numbers to adjust a gap between pipex and
* userland.
*/
seq = (struct pipex_l2tp_seq_header *)(l2tp + 1);
ns = ntohs(seq->ns);
nr = ntohs(seq->nr);
mtx_enter(&session->pxs_mtx);
ns += session->proto.l2tp.ns_gap;
seq->ns = htons(ns);
session->proto.l2tp.ns_nxt++;
nr += session->proto.l2tp.nr_gap;
seq->nr = htons(nr);
if (SEQ16_GT(nr, session->proto.l2tp.nr_acked))
session->proto.l2tp.nr_acked = nr;
mtx_leave(&session->pxs_mtx);
return (m0);
}
#endif /* PIPEX_L2TP */
#ifdef PIPEX_MPPE
/**********************************************************************
* MPPE
***********************************************************************/
#define PIPEX_COHERENCY_CNT_MASK 0x0fff
static inline int
pipex_mppe_setkey(struct pipex_mppe *mppe)
{
rc4_keysetup(&mppe->rc4ctx, mppe->session_key, mppe->keylen);
return (0);
}
static inline int
pipex_mppe_setoldkey(struct pipex_mppe *mppe, uint16_t coher_cnt)
{
KASSERT(mppe->old_session_keys != NULL);
rc4_keysetup(&mppe->rc4ctx,
mppe->old_session_keys[coher_cnt & PIPEX_MPPE_OLDKEYMASK],
mppe->keylen);
return (0);
}
static inline void
pipex_mppe_crypt(struct pipex_mppe *mppe, int len, u_char *indata,
u_char *outdata)
{
rc4_crypt(&mppe->rc4ctx, indata, outdata, len);
}
void
pipex_mppe_init(struct pipex_mppe *mppe, int stateless, int keylenbits,
u_char *master_key, int has_oldkey)
{
memset(mppe, 0, sizeof(struct pipex_mppe));
mtx_init(&mppe->pxm_mtx, IPL_SOFTNET);
if (stateless)
mppe->flags |= PIPEX_MPPE_STATELESS;
if (has_oldkey)
mppe->old_session_keys =
pool_get(&mppe_key_pool, PR_WAITOK);
else
mppe->old_session_keys = NULL;
memcpy(mppe->master_key, master_key, sizeof(mppe->master_key));
mppe->keylenbits = keylenbits;
switch (keylenbits) {
case 40:
case 56:
mppe->keylen = 8;
break;
case 128:
mppe->keylen = 16;
break;
}
GetNewKeyFromSHA(mppe->master_key, mppe->master_key, mppe->keylen,
mppe->session_key);
pipex_mppe_reduce_key(mppe);
pipex_mppe_setkey(mppe);
}
void
pipex_session_init_mppe_recv(struct pipex_session *session, int stateless,
int keylenbits, u_char *master_key)
{
pipex_mppe_init(&session->mppe_recv, stateless, keylenbits,
master_key, stateless);
session->ppp_flags |= PIPEX_PPP_MPPE_ACCEPTED;
}
void
pipex_session_init_mppe_send(struct pipex_session *session, int stateless,
int keylenbits, u_char *master_key)
{
pipex_mppe_init(&session->mppe_send, stateless, keylenbits,
master_key, 0);
session->ppp_flags |= PIPEX_PPP_MPPE_ENABLED;
}
#include <crypto/sha1.h>
static u_char SHAPad1[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}, SHAPad2[] = {
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
};
void
GetNewKeyFromSHA(u_char *StartKey, u_char *SessionKey, int SessionKeyLength,
u_char *InterimKey)
{
u_char Digest[20];
SHA1_CTX Context;
SHA1Init(&Context);
SHA1Update(&Context, StartKey, SessionKeyLength);
SHA1Update(&Context, SHAPad1, 40);
SHA1Update(&Context, SessionKey, SessionKeyLength);
SHA1Update(&Context, SHAPad2, 40);
SHA1Final(Digest, &Context);
memcpy(InterimKey, Digest, SessionKeyLength);
}
void
pipex_mppe_reduce_key(struct pipex_mppe *mppe)
{
switch (mppe->keylenbits) {
case 40:
mppe->session_key[0] = 0xd1;
mppe->session_key[1] = 0x26;
mppe->session_key[2] = 0x9e;
break;
case 56:
mppe->session_key[0] = 0xd1;
break;
}
}
void
mppe_key_change(struct pipex_mppe *mppe)
{
u_char interim[16];
struct rc4_ctx keychg;
memset(&keychg, 0, sizeof(keychg));
GetNewKeyFromSHA(mppe->master_key, mppe->session_key, mppe->keylen,
interim);
rc4_keysetup(&keychg, interim, mppe->keylen);
rc4_crypt(&keychg, interim, mppe->session_key, mppe->keylen);
pipex_mppe_reduce_key(mppe);
if (mppe->old_session_keys) {
int idx = mppe->coher_cnt & PIPEX_MPPE_OLDKEYMASK;
memcpy(mppe->old_session_keys[idx],
mppe->session_key, PIPEX_MPPE_KEYLEN);
}
}
struct mbuf *
pipex_mppe_input(struct mbuf *m0, struct pipex_session *session)
{
int pktloss, encrypt, flushed, m, n, len;
struct pipex_mppe *mppe;
uint16_t coher_cnt;
struct mbuf *m1;
u_char *cp;
int rewind = 0;
/* pullup */
PIPEX_PULLUP(m0, sizeof(coher_cnt));
if (m0 == NULL)
goto drop;
mppe = &session->mppe_recv;
/* get header information */
cp = mtod(m0, u_char *);
GETSHORT(coher_cnt, cp);
flushed = ((coher_cnt & 0x8000) != 0) ? 1 : 0;
encrypt = ((coher_cnt & 0x1000) != 0) ? 1 : 0;
coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
pktloss = 0;
mtx_enter(&mppe->pxm_mtx);
PIPEX_MPPE_DBG((session, LOG_DEBUG, "in coher_cnt=%03x %s%s",
mppe->coher_cnt, (flushed) ? "[flushed]" : "",
(encrypt) ? "[encrypt]" : ""));
if (encrypt == 0) {
mtx_leave(&mppe->pxm_mtx);
pipex_session_log(session, LOG_DEBUG,
"Received unexpected MPPE packet.(no ecrypt)");
goto drop;
}
/* adjust mbuf */
m_adj(m0, sizeof(coher_cnt));
/*
* L2TP data session may be used without sequencing, PPP frames may
* arrive in disorder. The 'coherency counter' of MPPE detects such
* situations, but we cannot distinguish between 'disorder' and
* 'packet loss' exactly.
*
* When 'coherency counter' detects lost packets greater than
* (4096 - 256), we treat as 'disorder' otherwise treat as
* 'packet loss'.
*/
{
int coher_cnt0;
coher_cnt0 = coher_cnt;
if (coher_cnt < mppe->coher_cnt)
coher_cnt0 += 0x1000;
if (coher_cnt0 - mppe->coher_cnt > 0x0f00) {
if ((mppe->flags & PIPEX_MPPE_STATELESS) == 0 ||
coher_cnt0 - mppe->coher_cnt
<= 0x1000 - PIPEX_MPPE_NOLDKEY) {
pipex_session_log(session, LOG_DEBUG,
"Workaround the out-of-sequence PPP framing problem: "
"%d => %d", mppe->coher_cnt, coher_cnt);
mtx_leave(&mppe->pxm_mtx);
goto drop;
}
rewind = 1;
}
}
if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) {
if (!rewind) {
mppe_key_change(mppe);
while (mppe->coher_cnt != coher_cnt) {
mppe->coher_cnt++;
mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
mppe_key_change(mppe);
pktloss++;
}
}
pipex_mppe_setoldkey(mppe, coher_cnt);
} else {
if (flushed) {
if (coher_cnt < mppe->coher_cnt) {
coher_cnt += 0x1000;
}
pktloss += coher_cnt - mppe->coher_cnt;
m = mppe->coher_cnt / 256;
n = coher_cnt / 256;
while (m++ < n)
mppe_key_change(mppe);
coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
mppe->coher_cnt = coher_cnt;
} else if (mppe->coher_cnt != coher_cnt) {
int ccp_id;
mtx_leave(&mppe->pxm_mtx);
/* Send CCP ResetReq */
PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetReq"));
mtx_enter(&session->pxs_mtx);
ccp_id = session->ccp_id;
session->ccp_id++;
mtx_leave(&session->pxs_mtx);
pipex_ccp_output(session, CCP_RESETREQ, ccp_id);
goto drop;
}
if ((coher_cnt & 0xff) == 0xff) {
mppe_key_change(mppe);
flushed = 1;
}
if (flushed)
pipex_mppe_setkey(mppe);
}
if (pktloss > 1000) {
pipex_session_log(session, LOG_DEBUG,
"%d packets loss.", pktloss);
}
/* decrypt ppp payload */
for (m1 = m0; m1; m1 = m1->m_next) {
cp = mtod(m1, u_char *);
len = m1->m_len;
pipex_mppe_crypt(mppe, len, cp, cp);
}
if (!rewind) {
/* update coher_cnt */
mppe->coher_cnt++;
mppe->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
}
mtx_leave(&mppe->pxm_mtx);
if (m0->m_pkthdr.len < PIPEX_PPPMINLEN)
goto drop;
return (m0);
drop:
m_freem(m0);
return (NULL);
}
struct mbuf *
pipex_mppe_output(struct mbuf *m0, struct pipex_session *session,
uint16_t protocol)
{
int encrypt, flushed, len;
struct mppe_header {
uint16_t coher_cnt;
uint16_t protocol;
} __packed *hdr;
u_char *cp;
struct pipex_mppe *mppe;
struct mbuf *m;
mppe = &session->mppe_send;
/*
* create a deep-copy if the mbuf has a shared mbuf cluster.
* this is required to handle cases of tcp retransmission.
*/
for (m = m0; m != NULL; m = m->m_next) {
if (M_READONLY(m)) {
m = m_dup_pkt(m0, max_linkhdr, M_NOWAIT);
m_freem(m0);
if (m == NULL)
return (NULL);
m0 = m;
break;
}
}
/* prepend mppe header */
M_PREPEND(m0, sizeof(struct mppe_header), M_NOWAIT);
if (m0 == NULL)
return (NULL);
hdr = mtod(m0, struct mppe_header *);
hdr->protocol = protocol;
/* check coherency counter */
flushed = 0;
encrypt = 1;
mtx_enter(&mppe->pxm_mtx);
if ((mppe->flags & PIPEX_MPPE_STATELESS) != 0) {
flushed = 1;
mppe_key_change(mppe);
} else {
if ((mppe->coher_cnt % 0x100) == 0xff) {
flushed = 1;
mppe_key_change(mppe);
} else if ((mppe->flags & PIPEX_MPPE_RESETREQ) != 0) {
flushed = 1;
mppe->flags &= ~PIPEX_MPPE_RESETREQ;
}
}
if (flushed)
pipex_mppe_setkey(mppe);
PIPEX_MPPE_DBG((session, LOG_DEBUG, "out coher_cnt=%03x %s%s",
mppe->coher_cnt, (flushed) ? "[flushed]" : "",
(encrypt) ? "[encrypt]" : ""));
/* setup header information */
hdr->coher_cnt = (mppe->coher_cnt++) & PIPEX_COHERENCY_CNT_MASK;
hdr->coher_cnt &= PIPEX_COHERENCY_CNT_MASK;
if (flushed)
hdr->coher_cnt |= 0x8000;
if (encrypt)
hdr->coher_cnt |= 0x1000;
hdr->protocol = htons(hdr->protocol);
hdr->coher_cnt = htons(hdr->coher_cnt);
/* encrypt chain */
for (m = m0; m; m = m->m_next) {
cp = mtod(m, u_char *);
len = m->m_len;
if (m == m0 && len > offsetof(struct mppe_header, protocol)) {
len -= offsetof(struct mppe_header, protocol);
cp += offsetof(struct mppe_header, protocol);
}
pipex_mppe_crypt(mppe, len, cp, cp);
}
mtx_leave(&mppe->pxm_mtx);
return (m0);
}
void
pipex_ccp_input(struct mbuf *m0, struct pipex_session *session)
{
u_char *cp;
int code, id, len;
if (m0->m_pkthdr.len < PPP_HDRLEN)
goto drop;
if ((m0 = m_pullup(m0, PPP_HDRLEN)) == NULL)
goto drop;
cp = mtod(m0, u_char *);
GETCHAR(code, cp);
GETCHAR(id, cp);
GETSHORT(len, cp);
switch (code) {
case CCP_RESETREQ:
PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetReq"));
mtx_enter(&session->mppe_send.pxm_mtx);
session->mppe_send.flags |= PIPEX_MPPE_RESETREQ;
mtx_leave(&session->mppe_send.pxm_mtx);
#ifndef PIPEX_NO_CCP_RESETACK
PIPEX_DBG((session, LOG_DEBUG, "CCP SendResetAck"));
pipex_ccp_output(session, CCP_RESETACK, id);
#endif
/* ignore error */
break;
case CCP_RESETACK:
PIPEX_DBG((session, LOG_DEBUG, "CCP RecvResetAck"));
break;
default:
PIPEX_DBG((session, LOG_DEBUG, "CCP Recv code=%d", code));
goto drop;
}
m_freem(m0);
return;
drop:
m_freem(m0);
counters_inc(session->stat_counters, pxc_ierrors);
}
int
pipex_ccp_output(struct pipex_session *session, int code, int id)
{
u_char *cp;
struct mbuf *m;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
counters_inc(session->stat_counters, pxc_oerrors);
return (1);
}
m->m_pkthdr.len = m->m_len = 4;
cp = mtod(m, u_char *);
PUTCHAR(code, cp);
PUTCHAR(id, cp);
PUTSHORT(4, cp);
pipex_ppp_output(m, session, PPP_CCP);
return (0);
}
#endif
/***********************************************************************
* Miscellaneous functions
***********************************************************************/
/* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */
/*
* Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $
*/
#define TCP_OPTLEN_IN_SEGMENT 12 /* timestamp option and padding */
#define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \
TCP_OPTLEN_IN_SEGMENT)
/*
* The following macro is used to update an internet checksum. "acc" is a
* 32-bit accumulation of all the changes to the checksum (adding in old
* 16-bit words and subtracting out new words), and "cksum" is the checksum
* value to be updated.
*/
#define ADJUST_CHECKSUM(acc, cksum) { \
acc += cksum; \
if (acc < 0) { \
acc = -acc; \
acc = (acc >> 16) + (acc & 0xffff); \
acc += acc >> 16; \
cksum = (u_short) ~acc; \
} else { \
acc = (acc >> 16) + (acc & 0xffff); \
acc += acc >> 16; \
cksum = (u_short) acc; \
} \
}
/*
* Rewrite max-segment-size TCP option to avoid PMTU blackhole issues.
* The mtu parameter should be the MTU bottleneck (as far as we know)
* on the link between the source and the destination.
*/
struct mbuf *
adjust_tcp_mss(struct mbuf *m0, int mtu)
{
int opt, optlen, acc, mss, maxmss, lpktp;
struct ip *pip;
struct tcphdr *th;
u_char *pktp, *mssp;
u_int16_t ip_off;
lpktp = sizeof(struct ip) + sizeof(struct tcphdr) + PIPEX_TCP_OPTLEN;
lpktp = MIN(lpktp, m0->m_pkthdr.len);
PIPEX_PULLUP(m0, lpktp);
if (m0 == NULL)
goto drop;
pktp = mtod(m0, char *);
pip = (struct ip *)pktp;
ip_off = ntohs(pip->ip_off);
/* Non TCP or fragmented packet must not have a MSS option */
if (pip->ip_p != IPPROTO_TCP ||
(ip_off & IP_MF) != 0 || (ip_off & IP_OFFMASK) != 0)
goto handled;
pktp += pip->ip_hl << 2;
lpktp -= pip->ip_hl << 2;
/* packet is broken */
if (sizeof(struct tcphdr) > lpktp)
goto drop;
th = (struct tcphdr *)pktp;
/*
* As RFC 973, a MSS field must only be sent in the initial
* connection request(it must be with SYN).
*/
if ((th->th_flags & TH_SYN) == 0)
goto handled;
lpktp = MIN(th->th_off << 4, lpktp);
pktp += sizeof(struct tcphdr);
lpktp -= sizeof(struct tcphdr);
while (lpktp >= TCPOLEN_MAXSEG) {
GETCHAR(opt, pktp);
switch (opt) {
case TCPOPT_MAXSEG:
GETCHAR(optlen, pktp);
mssp = pktp; /* mss place holder */
GETSHORT(mss, pktp);
maxmss = MAXMSS(mtu);
if (mss > maxmss) {
PIPEX_DBG((NULL, LOG_DEBUG,
"change tcp-mss %d => %d", mss, maxmss));
PUTSHORT(maxmss, mssp);
acc = htons(mss);
acc -= htons(maxmss);
ADJUST_CHECKSUM(acc, th->th_sum);
}
goto handled;
/* NOTREACHED */
case TCPOPT_EOL:
goto handled;
/* NOTREACHED */
case TCPOPT_NOP:
lpktp--;
break;
default:
GETCHAR(optlen, pktp);
if (optlen < 2) /* packet is broken */
goto drop;
pktp += optlen - 2;
lpktp -= optlen;
break;
}
}
handled:
return (m0);
drop:
m_freem(m0);
return (NULL);
}
/*
* Check whether a packet should reset idle timer
* Returns 1 to don't reset timer (i.e. the packet is "idle" packet)
*/
struct mbuf *
ip_is_idle_packet(struct mbuf *m0, int *ris_idle)
{
u_int16_t ip_off;
const struct udphdr *uh;
struct ip *pip;
int len;
/* pullup ip header */
len = sizeof(struct ip);
PIPEX_PULLUP(m0, len);
if (m0 == NULL)
goto error;
pip = mtod(m0, struct ip *);
/*
* the packet which fragmentations was not the idle packet.
*/
ip_off = ntohs(pip->ip_off);
if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0))
goto is_active;
switch (pip->ip_p) {
case IPPROTO_IGMP:
goto is_active;
case IPPROTO_ICMP:
len = pip->ip_hl * 4 + 8;
PIPEX_PULLUP(m0, len);
if (m0 == NULL)
goto error;
pip = mtod(m0, struct ip *);
switch (((unsigned char *) pip)[pip->ip_hl * 4]) {
case 0: /* Echo Reply */
case 8: /* Echo Request */
goto is_active;
default:
goto is_idle;
}
case IPPROTO_UDP:
case IPPROTO_TCP:
len = pip->ip_hl * 4 + sizeof(struct udphdr);
PIPEX_PULLUP(m0, len);
if (m0 == NULL)
goto error;
pip = mtod(m0, struct ip *);
uh = (struct udphdr *)(mtod(m0, caddr_t) + pip->ip_hl * 4);
switch (ntohs(uh->uh_sport)) {
case 53: /* DOMAIN */
case 67: /* BOOTPS */
case 68: /* BOOTPC */
case 123: /* NTP */
case 137: /* NETBIOS-NS */
case 520: /* RIP */
goto is_idle;
}
switch (ntohs(uh->uh_dport)) {
case 53: /* DOMAIN */
case 67: /* BOOTPS */
case 68: /* BOOTPC */
case 123: /* NTP */
case 137: /* NETBIOS-NS */
case 520: /* RIP */
goto is_idle;
}
goto is_active;
default:
goto is_active;
}
is_active:
*ris_idle = 0;
return (m0);
is_idle:
*ris_idle = 1;
return (m0);
error:
return (NULL);
}
void
pipex_session_log(struct pipex_session *session, int prio, const char *fmt, ...)
{
char logbuf[1024];
va_list ap;
logpri(prio);
if (session != NULL) {
struct ifnet *ifp;
ifp = if_get(session->ifindex);
addlog("pipex: ppp=%d iface=%s protocol=%s id=%d ",
session->ppp_id,
ifp? ifp->if_xname : "Unknown",
(session->protocol == PIPEX_PROTO_PPPOE)? "PPPoE" :
(session->protocol == PIPEX_PROTO_PPTP)? "PPTP" :
(session->protocol == PIPEX_PROTO_L2TP) ? "L2TP" :
"Unknown", session->session_id);
if_put(ifp);
} else
addlog("pipex: ");
va_start(ap, fmt);
vsnprintf(logbuf, sizeof(logbuf), fmt, ap);
va_end(ap);
addlog("%s\n", logbuf);
}
uint32_t
pipex_sockaddr_hash_key(struct sockaddr *sa)
{
switch (sa->sa_family) {
case AF_INET:
return ntohl(satosin(sa)->sin_addr.s_addr);
case AF_INET6:
return ntohl(satosin6(sa)->sin6_addr.s6_addr32[3]);
}
panic("pipex_sockaddr_hash_key: unknown address family");
return (0);
}
/*
* Compare struct sockaddr_in{,6} with the address only.
* The port number is not covered.
*/
int
pipex_sockaddr_compar_addr(struct sockaddr *a, struct sockaddr *b)
{
int cmp;
cmp = b->sa_family - a->sa_family;
if (cmp != 0)
return cmp;
switch (a->sa_family) {
case AF_INET:
return (satosin(b)->sin_addr.s_addr -
satosin(a)->sin_addr.s_addr);
case AF_INET6:
cmp = (satosin6(b)->sin6_scope_id - satosin6(a)->sin6_scope_id);
if (cmp != 0)
return cmp;
return (memcmp(&satosin6(a)->sin6_addr,
&satosin6(b)->sin6_addr,
sizeof(struct in6_addr)));
}
panic("pipex_sockaddr_compar_addr: unknown address family");
return (-1);
}
int
pipex_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
switch (name[0]) {
case PIPEXCTL_ENABLE:
if (namelen != 1)
return (ENOTDIR);
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&pipex_enable, 0, 1));
default:
return (ENOPROTOOPT);
}
/* NOTREACHED */
}
70
70
1
70
70
70
70
70
2
264
264
264
1169
23
1176
6
1171
1176
8
8
6
5
2
5
208
208
208
43
196
140
195
208
43
196
612
600
171
43
1177
2
1179
1176
1172
6
1178
70
52
27
2
68
70
1174
19
1172
1177
1177
1178
136
1168
171
136
2
2
138
6
137
138
136
138
138
135
2
136
2
138
5
138
1176
1178
1172
6
18
18
18
17
17
1
8
10
18
939
939
1170
1170
1170
9
306
691
720
692
281
1174
21
1173
1172
1172
281
692
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
/* $OpenBSD: vfs_bio.c,v 1.210 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_bio.c,v 1.44 1996/06/11 11:15:36 pk Exp $ */
/*
* Copyright (c) 1994 Christopher G. Demetriou
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_bio.c 8.6 (Berkeley) 1/11/94
*/
/*
* Some references:
* Bach: The Design of the UNIX Operating System (Prentice Hall, 1986)
* Leffler, et al.: The Design and Implementation of the 4.3BSD
* UNIX Operating System (Addison Welley, 1989)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/specdev.h>
#include <sys/tracepoint.h>
#include <uvm/uvm_extern.h>
/* XXX Should really be in buf.h, but for uvm_constraint_range.. */
int buf_realloc_pages(struct buf *, struct uvm_constraint_range *, int);
struct uvm_constraint_range high_constraint;
int fliphigh;
int nobuffers;
int needbuffer;
struct bio_ops bioops;
/* private bufcache functions */
void bufcache_init(void);
void bufcache_adjust(void);
struct buf *bufcache_gethighcleanbuf(void);
struct buf *bufcache_getdmacleanbuf(void);
/*
* Buffer pool for I/O buffers.
*/
struct pool bufpool;
struct bufhead bufhead = LIST_HEAD_INITIALIZER(bufhead);
void buf_put(struct buf *);
struct buf *bio_doread(struct vnode *, daddr_t, int, int);
struct buf *buf_get(struct vnode *, daddr_t, size_t);
void bread_cluster_callback(struct buf *);
int64_t bufcache_recover_dmapages(int discard, int64_t howmany);
struct bcachestats bcstats; /* counters */
long lodirtypages; /* dirty page count low water mark */
long hidirtypages; /* dirty page count high water mark */
long targetpages; /* target number of pages for cache size */
long buflowpages; /* smallest size cache allowed */
long bufhighpages; /* largest size cache allowed */
long bufbackpages; /* minimum number of pages we shrink when asked to */
vsize_t bufkvm;
struct proc *cleanerproc;
int bd_req; /* Sleep point for cleaner daemon. */
#define NUM_CACHES 2
#define DMA_CACHE 0
struct bufcache cleancache[NUM_CACHES];
struct bufqueue dirtyqueue;
void
buf_put(struct buf *bp)
{
splassert(IPL_BIO);
#ifdef DIAGNOSTIC
if (bp->b_pobj != NULL)
KASSERT(bp->b_bufsize > 0);
if (ISSET(bp->b_flags, B_DELWRI))
panic("buf_put: releasing dirty buffer");
if (bp->b_freelist.tqe_next != NOLIST &&
bp->b_freelist.tqe_next != (void *)-1)
panic("buf_put: still on the free list");
if (bp->b_vnbufs.le_next != NOLIST &&
bp->b_vnbufs.le_next != (void *)-1)
panic("buf_put: still on the vnode list");
if (!LIST_EMPTY(&bp->b_dep))
panic("buf_put: b_dep is not empty");
#endif
LIST_REMOVE(bp, b_list);
bcstats.numbufs--;
if (buf_dealloc_mem(bp) != 0)
return;
pool_put(&bufpool, bp);
}
/*
* Initialize buffers and hash links for buffers.
*/
void
bufinit(void)
{
u_int64_t dmapages;
u_int64_t highpages;
dmapages = uvm_pagecount(&dma_constraint);
/* take away a guess at how much of this the kernel will consume */
dmapages -= (atop(physmem) - atop(uvmexp.free));
/* See if we have memory above the dma accessible region. */
high_constraint.ucr_low = dma_constraint.ucr_high;
high_constraint.ucr_high = no_constraint.ucr_high;
if (high_constraint.ucr_low != high_constraint.ucr_high)
high_constraint.ucr_low++;
highpages = uvm_pagecount(&high_constraint);
/*
* Do we have any significant amount of high memory above
* the DMA region? if so enable moving buffers there, if not,
* don't bother.
*/
if (highpages > dmapages / 4)
fliphigh = 1;
else
fliphigh = 0;
/*
* If MD code doesn't say otherwise, use up to 10% of DMA'able
* memory for buffers.
*/
if (bufcachepercent == 0)
bufcachepercent = 10;
/*
* XXX these values and their same use in kern_sysctl
* need to move into buf.h
*/
KASSERT(bufcachepercent <= 90);
KASSERT(bufcachepercent >= 5);
if (bufpages == 0)
bufpages = dmapages * bufcachepercent / 100;
if (bufpages < BCACHE_MIN)
bufpages = BCACHE_MIN;
KASSERT(bufpages < dmapages);
bufhighpages = bufpages;
/*
* Set the base backoff level for the buffer cache. We will
* not allow uvm to steal back more than this number of pages.
*/
buflowpages = dmapages * 5 / 100;
if (buflowpages < BCACHE_MIN)
buflowpages = BCACHE_MIN;
/*
* set bufbackpages to 100 pages, or 10 percent of the low water mark
* if we don't have that many pages.
*/
bufbackpages = buflowpages * 10 / 100;
if (bufbackpages > 100)
bufbackpages = 100;
/*
* If the MD code does not say otherwise, reserve 10% of kva
* space for mapping buffers.
*/
if (bufkvm == 0)
bufkvm = VM_KERNEL_SPACE_SIZE / 10;
/*
* Don't use more than twice the amount of bufpages for mappings.
* It's twice since we map things sparsely.
*/
if (bufkvm > bufpages * PAGE_SIZE)
bufkvm = bufpages * PAGE_SIZE;
/*
* Round bufkvm to MAXPHYS because we allocate chunks of va space
* in MAXPHYS chunks.
*/
bufkvm &= ~(MAXPHYS - 1);
pool_init(&bufpool, sizeof(struct buf), 0, IPL_BIO, 0, "bufpl", NULL);
bufcache_init();
/*
* hmm - bufkvm is an argument because it's static, while
* bufpages is global because it can change while running.
*/
buf_mem_init(bufkvm);
/*
* Set the dirty page high water mark to be less than the low
* water mark for pages in the buffer cache. This ensures we
* can always back off by throwing away clean pages, and give
* ourselves a chance to write out the dirty pages eventually.
*/
hidirtypages = (buflowpages / 4) * 3;
lodirtypages = buflowpages / 2;
/*
* We are allowed to use up to the reserve.
*/
targetpages = bufpages - RESERVE_PAGES;
}
/*
* Change cachepct
*/
void
bufadjust(int newbufpages)
{
int s;
int64_t npages;
if (newbufpages < buflowpages)
newbufpages = buflowpages;
s = splbio();
bufpages = newbufpages;
/*
* We are allowed to use up to the reserve
*/
targetpages = bufpages - RESERVE_PAGES;
npages = bcstats.dmapages - targetpages;
/*
* Shrinking the cache happens here only if someone has manually
* adjusted bufcachepercent - or the pagedaemon has told us
* to give back memory *now* - so we give it all back.
*/
if (bcstats.dmapages > targetpages)
(void) bufcache_recover_dmapages(0, bcstats.dmapages - targetpages);
bufcache_adjust();
/*
* Wake up the cleaner if we have lots of dirty pages,
* or if we are getting low on buffer cache kva.
*/
if ((UNCLEAN_PAGES >= hidirtypages) ||
bcstats.kvaslots_avail <= 2 * RESERVE_SLOTS)
wakeup(&bd_req);
splx(s);
}
/*
* Make the buffer cache back off from cachepct.
*/
int
bufbackoff(struct uvm_constraint_range *range, long size)
{
/*
* Back off "size" buffer cache pages. Called by the page
* daemon to consume buffer cache pages rather than scanning.
*
* It returns 0 to the pagedaemon to indicate that it has
* succeeded in freeing enough pages. It returns -1 to
* indicate that it could not and the pagedaemon should take
* other measures.
*
*/
long pdelta, oldbufpages;
/*
* If we will accept high memory for this backoff
* try to steal it from the high memory buffer cache.
*/
if (range != NULL && range->ucr_high > dma_constraint.ucr_high) {
struct buf *bp;
int64_t start = bcstats.numbufpages, recovered = 0;
int s = splbio();
while ((recovered < size) &&
(bp = bufcache_gethighcleanbuf())) {
bufcache_take(bp);
if (bp->b_vp) {
RBT_REMOVE(buf_rb_bufs,
&bp->b_vp->v_bufs_tree, bp);
brelvp(bp);
}
buf_put(bp);
recovered = start - bcstats.numbufpages;
}
bufcache_adjust();
splx(s);
/* If we got enough, return success */
if (recovered >= size)
return 0;
/*
* If we needed only memory above DMA,
* return failure
*/
if (range->ucr_low > dma_constraint.ucr_high)
return -1;
/* Otherwise get the rest from DMA */
size -= recovered;
}
/*
* XXX Otherwise do the dma memory cache dance. this needs
* refactoring later to get rid of 'bufpages'
*/
/*
* Back off by at least bufbackpages. If the page daemon gave us
* a larger size, back off by that much.
*/
pdelta = (size > bufbackpages) ? size : bufbackpages;
if (bufpages <= buflowpages)
return(-1);
if (bufpages - pdelta < buflowpages)
pdelta = bufpages - buflowpages;
oldbufpages = bufpages;
bufadjust(bufpages - pdelta);
if (oldbufpages - bufpages < size)
return (-1); /* we did not free what we were asked */
else
return(0);
}
/*
* Opportunistically flip a buffer into high memory. Will move the buffer
* if memory is available without sleeping, and return 0, otherwise will
* fail and return -1 with the buffer unchanged.
*/
int
buf_flip_high(struct buf *bp)
{
int s;
int ret = -1;
KASSERT(ISSET(bp->b_flags, B_BC));
KASSERT(ISSET(bp->b_flags, B_DMA));
KASSERT(bp->cache == DMA_CACHE);
KASSERT(fliphigh);
/* Attempt to move the buffer to high memory if we can */
s = splbio();
if (buf_realloc_pages(bp, &high_constraint, UVM_PLA_NOWAIT) == 0) {
KASSERT(!ISSET(bp->b_flags, B_DMA));
bcstats.highflips++;
ret = 0;
} else
bcstats.highflops++;
splx(s);
return ret;
}
/*
* Flip a buffer to dma reachable memory, when we need it there for
* I/O. This can sleep since it will wait for memory allocation in the
* DMA reachable area since we have to have the buffer there to proceed.
*/
void
buf_flip_dma(struct buf *bp)
{
KASSERT(ISSET(bp->b_flags, B_BC));
KASSERT(ISSET(bp->b_flags, B_BUSY));
KASSERT(bp->cache < NUM_CACHES);
if (!ISSET(bp->b_flags, B_DMA)) {
int s = splbio();
/* move buf to dma reachable memory */
(void) buf_realloc_pages(bp, &dma_constraint, UVM_PLA_WAITOK);
KASSERT(ISSET(bp->b_flags, B_DMA));
bcstats.dmaflips++;
splx(s);
}
if (bp->cache > DMA_CACHE) {
CLR(bp->b_flags, B_COLD);
CLR(bp->b_flags, B_WARM);
bp->cache = DMA_CACHE;
}
}
struct buf *
bio_doread(struct vnode *vp, daddr_t blkno, int size, int async)
{
struct buf *bp;
struct mount *mp;
bp = getblk(vp, blkno, size, 0, INFSLP);
/*
* If buffer does not have valid data, start a read.
* Note that if buffer is B_INVAL, getblk() won't return it.
* Therefore, it's valid if its I/O has completed or been delayed.
*/
if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) {
SET(bp->b_flags, B_READ | async);
bcstats.pendingreads++;
bcstats.numreads++;
VOP_STRATEGY(bp->b_vp, bp);
/* Pay for the read. */
curproc->p_ru.ru_inblock++; /* XXX */
} else if (async) {
brelse(bp);
}
mp = vp->v_type == VBLK ? vp->v_specmountpoint : vp->v_mount;
/*
* Collect statistics on synchronous and asynchronous reads.
* Reads from block devices are charged to their associated
* filesystem (if any).
*/
if (mp != NULL) {
if (async == 0)
mp->mnt_stat.f_syncreads++;
else
mp->mnt_stat.f_asyncreads++;
}
return (bp);
}
/*
* Read a disk block.
* This algorithm described in Bach (p.54).
*/
int
bread(struct vnode *vp, daddr_t blkno, int size, struct buf **bpp)
{
struct buf *bp;
/* Get buffer for block. */
bp = *bpp = bio_doread(vp, blkno, size, 0);
/* Wait for the read to complete, and return result. */
return (biowait(bp));
}
/*
* Read-ahead multiple disk blocks. The first is sync, the rest async.
* Trivial modification to the breada algorithm presented in Bach (p.55).
*/
int
breadn(struct vnode *vp, daddr_t blkno, int size, daddr_t rablks[],
int rasizes[], int nrablks, struct buf **bpp)
{
struct buf *bp;
int i;
bp = *bpp = bio_doread(vp, blkno, size, 0);
/*
* For each of the read-ahead blocks, start a read, if necessary.
*/
for (i = 0; i < nrablks; i++) {
/* If it's in the cache, just go on to next one. */
if (incore(vp, rablks[i]))
continue;
/* Get a buffer for the read-ahead block */
(void) bio_doread(vp, rablks[i], rasizes[i], B_ASYNC);
}
/* Otherwise, we had to start a read for it; wait until it's valid. */
return (biowait(bp));
}
/*
* Called from interrupt context.
*/
void
bread_cluster_callback(struct buf *bp)
{
struct buf **xbpp = bp->b_saveaddr;
int i;
if (xbpp[1] != NULL) {
size_t newsize = xbpp[1]->b_bufsize;
/*
* Shrink this buffer's mapping to only cover its part of
* the total I/O.
*/
buf_fix_mapping(bp, newsize);
bp->b_bcount = newsize;
}
/* Invalidate read-ahead buffers if read short */
if (bp->b_resid > 0) {
for (i = 1; xbpp[i] != NULL; i++)
continue;
for (i = i - 1; i != 0; i--) {
if (xbpp[i]->b_bufsize <= bp->b_resid) {
bp->b_resid -= xbpp[i]->b_bufsize;
SET(xbpp[i]->b_flags, B_INVAL);
} else if (bp->b_resid > 0) {
bp->b_resid = 0;
SET(xbpp[i]->b_flags, B_INVAL);
} else
break;
}
}
for (i = 1; xbpp[i] != NULL; i++) {
if (ISSET(bp->b_flags, B_ERROR))
SET(xbpp[i]->b_flags, B_INVAL | B_ERROR);
/*
* Move the pages from the master buffer's uvm object
* into the individual buffer's uvm objects.
*/
struct uvm_object *newobj = &xbpp[i]->b_uobj;
struct uvm_object *oldobj = &bp->b_uobj;
int page;
uvm_obj_init(newobj, &bufcache_pager, 1);
for (page = 0; page < atop(xbpp[i]->b_bufsize); page++) {
struct vm_page *pg = uvm_pagelookup(oldobj,
xbpp[i]->b_poffs + ptoa(page));
KASSERT(pg != NULL);
KASSERT(pg->wire_count == 1);
uvm_pagerealloc(pg, newobj, xbpp[i]->b_poffs + ptoa(page));
}
xbpp[i]->b_pobj = newobj;
biodone(xbpp[i]);
}
free(xbpp, M_TEMP, (i + 1) * sizeof(*xbpp));
if (ISSET(bp->b_flags, B_ASYNC)) {
brelse(bp);
} else {
CLR(bp->b_flags, B_WANTED);
wakeup(bp);
}
}
/*
* Read-ahead multiple disk blocks, but make sure only one (big) I/O
* request is sent to the disk.
* XXX This should probably be dropped and breadn should instead be optimized
* XXX to do fewer I/O requests.
*/
int
bread_cluster(struct vnode *vp, daddr_t blkno, int size, struct buf **rbpp)
{
struct buf *bp, **xbpp;
int howmany, maxra, i, inc;
daddr_t sblkno;
*rbpp = bio_doread(vp, blkno, size, 0);
/*
* If the buffer is in the cache skip any I/O operation.
*/
if (ISSET((*rbpp)->b_flags, B_CACHE))
goto out;
if (size != round_page(size))
goto out;
if (VOP_BMAP(vp, blkno + 1, NULL, &sblkno, &maxra))
goto out;
maxra++;
if (sblkno == -1 || maxra < 2)
goto out;
howmany = MAXPHYS / size;
if (howmany > maxra)
howmany = maxra;
xbpp = mallocarray(howmany + 1, sizeof(*xbpp), M_TEMP, M_NOWAIT);
if (xbpp == NULL)
goto out;
for (i = howmany - 1; i >= 0; i--) {
size_t sz;
/*
* First buffer allocates big enough size to cover what
* all the other buffers need.
*/
sz = i == 0 ? howmany * size : 0;
xbpp[i] = buf_get(vp, blkno + i + 1, sz);
if (xbpp[i] == NULL) {
for (++i; i < howmany; i++) {
SET(xbpp[i]->b_flags, B_INVAL);
brelse(xbpp[i]);
}
free(xbpp, M_TEMP, (howmany + 1) * sizeof(*xbpp));
goto out;
}
}
bp = xbpp[0];
xbpp[howmany] = NULL;
inc = btodb(size);
for (i = 1; i < howmany; i++) {
bcstats.pendingreads++;
bcstats.numreads++;
/*
* We set B_DMA here because bp above will be B_DMA,
* and we are playing buffer slice-n-dice games from
* the memory allocated in bp.
*/
SET(xbpp[i]->b_flags, B_DMA | B_READ | B_ASYNC);
xbpp[i]->b_blkno = sblkno + (i * inc);
xbpp[i]->b_bufsize = xbpp[i]->b_bcount = size;
xbpp[i]->b_data = NULL;
xbpp[i]->b_pobj = bp->b_pobj;
xbpp[i]->b_poffs = bp->b_poffs + (i * size);
}
KASSERT(bp->b_lblkno == blkno + 1);
KASSERT(bp->b_vp == vp);
bp->b_blkno = sblkno;
SET(bp->b_flags, B_READ | B_ASYNC | B_CALL);
bp->b_saveaddr = (void *)xbpp;
bp->b_iodone = bread_cluster_callback;
bcstats.pendingreads++;
bcstats.numreads++;
VOP_STRATEGY(bp->b_vp, bp);
curproc->p_ru.ru_inblock++;
out:
return (biowait(*rbpp));
}
/*
* Block write. Described in Bach (p.56)
*/
int
bwrite(struct buf *bp)
{
int rv, async, wasdelayed, s;
struct vnode *vp;
struct mount *mp;
vp = bp->b_vp;
if (vp != NULL)
mp = vp->v_type == VBLK? vp->v_specmountpoint : vp->v_mount;
else
mp = NULL;
/*
* Remember buffer type, to switch on it later. If the write was
* synchronous, but the file system was mounted with MNT_ASYNC,
* convert it to a delayed write.
* XXX note that this relies on delayed tape writes being converted
* to async, not sync writes (which is safe, but ugly).
*/
async = ISSET(bp->b_flags, B_ASYNC);
if (!async && mp && ISSET(mp->mnt_flag, MNT_ASYNC)) {
/*
* Don't convert writes from VND on async filesystems
* that already have delayed writes in the upper layer.
*/
if (!ISSET(bp->b_flags, B_NOCACHE)) {
bdwrite(bp);
return (0);
}
}
/*
* Collect statistics on synchronous and asynchronous writes.
* Writes to block devices are charged to their associated
* filesystem (if any).
*/
if (mp != NULL) {
if (async)
mp->mnt_stat.f_asyncwrites++;
else
mp->mnt_stat.f_syncwrites++;
}
bcstats.pendingwrites++;
bcstats.numwrites++;
wasdelayed = ISSET(bp->b_flags, B_DELWRI);
CLR(bp->b_flags, (B_READ | B_DONE | B_ERROR | B_DELWRI));
s = splbio();
/*
* If not synchronous, pay for the I/O operation and make
* sure the buf is on the correct vnode queue. We have
* to do this now, because if we don't, the vnode may not
* be properly notified that its I/O has completed.
*/
if (wasdelayed) {
reassignbuf(bp);
} else
curproc->p_ru.ru_oublock++;
/* Initiate disk write. Make sure the appropriate party is charged. */
bp->b_vp->v_numoutput++;
splx(s);
buf_flip_dma(bp);
SET(bp->b_flags, B_WRITEINPROG);
VOP_STRATEGY(bp->b_vp, bp);
/*
* If the queue is above the high water mark, wait till
* the number of outstanding write bufs drops below the low
* water mark.
*/
if (bp->b_bq)
bufq_wait(bp->b_bq);
if (async)
return (0);
/*
* If I/O was synchronous, wait for it to complete.
*/
rv = biowait(bp);
/* Release the buffer. */
brelse(bp);
return (rv);
}
/*
* Delayed write.
*
* The buffer is marked dirty, but is not queued for I/O.
* This routine should be used when the buffer is expected
* to be modified again soon, typically a small write that
* partially fills a buffer.
*
* NB: magnetic tapes cannot be delayed; they must be
* written in the order that the writes are requested.
*
* Described in Leffler, et al. (pp. 208-213).
*/
void
bdwrite(struct buf *bp)
{
int s;
/*
* If the block hasn't been seen before:
* (1) Mark it as having been seen,
* (2) Charge for the write.
* (3) Make sure it's on its vnode's correct block list,
* (4) If a buffer is rewritten, move it to end of dirty list
*/
if (!ISSET(bp->b_flags, B_DELWRI)) {
SET(bp->b_flags, B_DELWRI);
s = splbio();
buf_flip_dma(bp);
reassignbuf(bp);
splx(s);
curproc->p_ru.ru_oublock++; /* XXX */
}
/* The "write" is done, so mark and release the buffer. */
CLR(bp->b_flags, B_NEEDCOMMIT);
CLR(bp->b_flags, B_NOCACHE); /* Must cache delayed writes */
SET(bp->b_flags, B_DONE);
brelse(bp);
}
/*
* Asynchronous block write; just an asynchronous bwrite().
*/
void
bawrite(struct buf *bp)
{
SET(bp->b_flags, B_ASYNC);
VOP_BWRITE(bp);
}
/*
* Must be called at splbio()
*/
void
buf_dirty(struct buf *bp)
{
splassert(IPL_BIO);
#ifdef DIAGNOSTIC
if (!ISSET(bp->b_flags, B_BUSY))
panic("Trying to dirty buffer on freelist!");
#endif
if (ISSET(bp->b_flags, B_DELWRI) == 0) {
SET(bp->b_flags, B_DELWRI);
buf_flip_dma(bp);
reassignbuf(bp);
}
}
/*
* Must be called at splbio()
*/
void
buf_undirty(struct buf *bp)
{
splassert(IPL_BIO);
#ifdef DIAGNOSTIC
if (!ISSET(bp->b_flags, B_BUSY))
panic("Trying to undirty buffer on freelist!");
#endif
if (ISSET(bp->b_flags, B_DELWRI)) {
CLR(bp->b_flags, B_DELWRI);
reassignbuf(bp);
}
}
/*
* Release a buffer on to the free lists.
* Described in Bach (p. 46).
*/
void
brelse(struct buf *bp)
{
int s;
s = splbio();
if (bp->b_data != NULL)
KASSERT(bp->b_bufsize > 0);
/*
* softdep is basically incompatible with not caching buffers
* that have dependencies, so this buffer must be cached
*/
if (LIST_FIRST(&bp->b_dep) != NULL)
CLR(bp->b_flags, B_NOCACHE);
/*
* Determine which queue the buffer should be on, then put it there.
*/
/* If it's not cacheable, or an error, mark it invalid. */
if (ISSET(bp->b_flags, (B_NOCACHE|B_ERROR)))
SET(bp->b_flags, B_INVAL);
/* If it's a write error, also mark the vnode as damaged. */
if (ISSET(bp->b_flags, B_ERROR) && !ISSET(bp->b_flags, B_READ)) {
if (bp->b_vp && bp->b_vp->v_type == VREG)
SET(bp->b_vp->v_bioflag, VBIOERROR);
}
if (ISSET(bp->b_flags, B_INVAL)) {
/*
* If the buffer is invalid, free it now rather than leaving
* it in a queue and wasting memory.
*/
if (LIST_FIRST(&bp->b_dep) != NULL)
buf_deallocate(bp);
if (ISSET(bp->b_flags, B_DELWRI)) {
CLR(bp->b_flags, B_DELWRI);
}
if (bp->b_vp) {
RBT_REMOVE(buf_rb_bufs, &bp->b_vp->v_bufs_tree, bp);
brelvp(bp);
}
bp->b_vp = NULL;
/*
* Wake up any processes waiting for _this_ buffer to
* become free. They are not allowed to grab it
* since it will be freed. But the only sleeper is
* getblk and it will restart the operation after
* sleep.
*/
if (ISSET(bp->b_flags, B_WANTED)) {
CLR(bp->b_flags, B_WANTED);
wakeup(bp);
}
buf_put(bp);
} else {
/*
* It has valid data. Put it on the end of the appropriate
* queue, so that it'll stick around for as long as possible.
*/
bufcache_release(bp);
/* Unlock the buffer. */
CLR(bp->b_flags, (B_AGE | B_ASYNC | B_NOCACHE | B_DEFERRED));
buf_release(bp);
/* Wake up any processes waiting for _this_ buffer to
* become free. */
if (ISSET(bp->b_flags, B_WANTED)) {
CLR(bp->b_flags, B_WANTED);
wakeup(bp);
}
if (bcstats.dmapages > targetpages)
(void) bufcache_recover_dmapages(0,
bcstats.dmapages - targetpages);
bufcache_adjust();
}
/* Wake up syncer and cleaner processes waiting for buffers. */
if (nobuffers) {
nobuffers = 0;
wakeup(&nobuffers);
}
/* Wake up any processes waiting for any buffer to become free. */
if (needbuffer && bcstats.dmapages < targetpages &&
bcstats.kvaslots_avail > RESERVE_SLOTS) {
needbuffer = 0;
wakeup(&needbuffer);
}
splx(s);
}
/*
* Determine if a block is in the cache. Just look on what would be its hash
* chain. If it's there, return a pointer to it, unless it's marked invalid.
*/
struct buf *
incore(struct vnode *vp, daddr_t blkno)
{
struct buf *bp;
struct buf b;
int s;
s = splbio();
/* Search buf lookup tree */
b.b_lblkno = blkno;
bp = RBT_FIND(buf_rb_bufs, &vp->v_bufs_tree, &b);
if (bp != NULL && ISSET(bp->b_flags, B_INVAL))
bp = NULL;
splx(s);
return (bp);
}
/*
* Get a block of requested size that is associated with
* a given vnode and block offset. If it is found in the
* block cache, mark it as having been found, make it busy
* and return it. Otherwise, return an empty block of the
* correct size. It is up to the caller to ensure that the
* cached blocks be of the correct size.
*/
struct buf *
getblk(struct vnode *vp, daddr_t blkno, int size, int slpflag,
uint64_t slptimeo)
{
struct buf *bp;
struct buf b;
int s, error;
/*
* XXX
* The following is an inlined version of 'incore()', but with
* the 'invalid' test moved to after the 'busy' test. It's
* necessary because there are some cases in which the NFS
* code sets B_INVAL prior to writing data to the server, but
* in which the buffers actually contain valid data. In this
* case, we can't allow the system to allocate a new buffer for
* the block until the write is finished.
*/
start:
s = splbio();
b.b_lblkno = blkno;
bp = RBT_FIND(buf_rb_bufs, &vp->v_bufs_tree, &b);
if (bp != NULL) {
if (ISSET(bp->b_flags, B_BUSY)) {
SET(bp->b_flags, B_WANTED);
error = tsleep_nsec(bp, slpflag | (PRIBIO + 1),
"getblk", slptimeo);
splx(s);
if (error)
return (NULL);
goto start;
}
if (!ISSET(bp->b_flags, B_INVAL)) {
bcstats.cachehits++;
SET(bp->b_flags, B_CACHE);
bufcache_take(bp);
buf_acquire(bp);
splx(s);
return (bp);
}
}
splx(s);
if ((bp = buf_get(vp, blkno, size)) == NULL)
goto start;
return (bp);
}
/*
* Get an empty, disassociated buffer of given size.
*/
struct buf *
geteblk(size_t size)
{
struct buf *bp;
while ((bp = buf_get(NULL, 0, size)) == NULL)
continue;
return (bp);
}
/*
* Allocate a buffer.
* If vp is given, put it into the buffer cache for that vnode.
* If size != 0, allocate memory and call buf_map().
* If there is already a buffer for the given vnode/blkno, return NULL.
*/
struct buf *
buf_get(struct vnode *vp, daddr_t blkno, size_t size)
{
struct buf *bp;
int poolwait = size == 0 ? PR_NOWAIT : PR_WAITOK;
int npages;
int s;
s = splbio();
if (size) {
/*
* Wake up the cleaner if we have lots of dirty pages,
* or if we are getting low on buffer cache kva.
*/
if (UNCLEAN_PAGES >= hidirtypages ||
bcstats.kvaslots_avail <= 2 * RESERVE_SLOTS)
wakeup(&bd_req);
npages = atop(round_page(size));
/*
* if our cache has been previously shrunk,
* allow it to grow again with use up to
* bufhighpages (cachepercent)
*/
if (bufpages < bufhighpages)
bufadjust(bufhighpages);
/*
* If we would go over the page target with our
* new allocation, free enough buffers first
* to stay at the target with our new allocation.
*/
if (bcstats.dmapages + npages > targetpages) {
(void) bufcache_recover_dmapages(0, npages);
bufcache_adjust();
}
/*
* If we get here, we tried to free the world down
* above, and couldn't get down - Wake the cleaner
* and wait for it to push some buffers out.
*/
if ((bcstats.dmapages + npages > targetpages ||
bcstats.kvaslots_avail <= RESERVE_SLOTS) &&
curproc != syncerproc && curproc != cleanerproc) {
wakeup(&bd_req);
needbuffer++;
tsleep_nsec(&needbuffer, PRIBIO, "needbuffer", INFSLP);
splx(s);
return (NULL);
}
if (bcstats.dmapages + npages > bufpages) {
/* cleaner or syncer */
nobuffers = 1;
tsleep_nsec(&nobuffers, PRIBIO, "nobuffers", INFSLP);
splx(s);
return (NULL);
}
}
bp = pool_get(&bufpool, poolwait|PR_ZERO);
if (bp == NULL) {
splx(s);
return (NULL);
}
bp->b_freelist.tqe_next = NOLIST;
bp->b_dev = NODEV;
LIST_INIT(&bp->b_dep);
bp->b_bcount = size;
buf_acquire_nomap(bp);
if (vp != NULL) {
/*
* We insert the buffer into the hash with B_BUSY set
* while we allocate pages for it. This way any getblk
* that happens while we allocate pages will wait for
* this buffer instead of starting its own buf_get.
*
* But first, we check if someone beat us to it.
*/
if (incore(vp, blkno)) {
pool_put(&bufpool, bp);
splx(s);
return (NULL);
}
bp->b_blkno = bp->b_lblkno = blkno;
bgetvp(vp, bp);
if (RBT_INSERT(buf_rb_bufs, &vp->v_bufs_tree, bp))
panic("buf_get: dup lblk vp %p bp %p", vp, bp);
} else {
bp->b_vnbufs.le_next = NOLIST;
SET(bp->b_flags, B_INVAL);
bp->b_vp = NULL;
}
LIST_INSERT_HEAD(&bufhead, bp, b_list);
bcstats.numbufs++;
if (size) {
buf_alloc_pages(bp, round_page(size));
KASSERT(ISSET(bp->b_flags, B_DMA));
buf_map(bp);
}
SET(bp->b_flags, B_BC);
splx(s);
return (bp);
}
/*
* Buffer cleaning daemon.
*/
void
buf_daemon(void *arg)
{
struct buf *bp = NULL;
int s, pushed = 0;
s = splbio();
for (;;) {
if (bp == NULL || (pushed >= 16 &&
UNCLEAN_PAGES < hidirtypages &&
bcstats.kvaslots_avail > 2 * RESERVE_SLOTS)){
pushed = 0;
/*
* Wake up anyone who was waiting for buffers
* to be released.
*/
if (needbuffer) {
needbuffer = 0;
wakeup(&needbuffer);
}
tsleep_nsec(&bd_req, PRIBIO - 7, "cleaner", INFSLP);
}
while ((bp = bufcache_getdirtybuf())) {
TRACEPOINT(vfs, cleaner, bp->b_flags, pushed,
lodirtypages, hidirtypages);
if (UNCLEAN_PAGES < lodirtypages &&
bcstats.kvaslots_avail > 2 * RESERVE_SLOTS &&
pushed >= 16)
break;
bufcache_take(bp);
buf_acquire(bp);
splx(s);
if (ISSET(bp->b_flags, B_INVAL)) {
brelse(bp);
s = splbio();
continue;
}
#ifdef DIAGNOSTIC
if (!ISSET(bp->b_flags, B_DELWRI))
panic("Clean buffer on dirty queue");
#endif
if (LIST_FIRST(&bp->b_dep) != NULL &&
!ISSET(bp->b_flags, B_DEFERRED) &&
buf_countdeps(bp, 0, 0)) {
SET(bp->b_flags, B_DEFERRED);
s = splbio();
bufcache_release(bp);
buf_release(bp);
continue;
}
bawrite(bp);
pushed++;
sched_pause(yield);
s = splbio();
}
}
}
/*
* Wait for operations on the buffer to complete.
* When they do, extract and return the I/O's error value.
*/
int
biowait(struct buf *bp)
{
int s;
KASSERT(!(bp->b_flags & B_ASYNC));
s = splbio();
while (!ISSET(bp->b_flags, B_DONE))
tsleep_nsec(bp, PRIBIO + 1, "biowait", INFSLP);
splx(s);
/* check for interruption of I/O (e.g. via NFS), then errors. */
if (ISSET(bp->b_flags, B_EINTR)) {
CLR(bp->b_flags, B_EINTR);
return (EINTR);
}
if (ISSET(bp->b_flags, B_ERROR))
return (bp->b_error ? bp->b_error : EIO);
else
return (0);
}
/*
* Mark I/O complete on a buffer.
*
* If a callback has been requested, e.g. the pageout
* daemon, do so. Otherwise, awaken waiting processes.
*
* [ Leffler, et al., says on p.247:
* "This routine wakes up the blocked process, frees the buffer
* for an asynchronous write, or, for a request by the pagedaemon
* process, invokes a procedure specified in the buffer structure" ]
*
* In real life, the pagedaemon (or other system processes) wants
* to do async stuff to, and doesn't want the buffer brelse()'d.
* (for swap pager, that puts swap buffers on the free lists (!!!),
* for the vn device, that puts malloc'd buffers on the free lists!)
*
* Must be called at splbio().
*/
void
biodone(struct buf *bp)
{
splassert(IPL_BIO);
if (ISSET(bp->b_flags, B_DONE))
panic("biodone already");
SET(bp->b_flags, B_DONE); /* note that it's done */
if (bp->b_bq)
bufq_done(bp->b_bq, bp);
if (LIST_FIRST(&bp->b_dep) != NULL)
buf_complete(bp);
if (!ISSET(bp->b_flags, B_READ)) {
CLR(bp->b_flags, B_WRITEINPROG);
vwakeup(bp->b_vp);
}
if (bcstats.numbufs &&
(!(ISSET(bp->b_flags, B_RAW) || ISSET(bp->b_flags, B_PHYS)))) {
if (!ISSET(bp->b_flags, B_READ)) {
bcstats.pendingwrites--;
} else
bcstats.pendingreads--;
}
if (ISSET(bp->b_flags, B_CALL)) { /* if necessary, call out */
CLR(bp->b_flags, B_CALL); /* but note callout done */
(*bp->b_iodone)(bp);
} else {
if (ISSET(bp->b_flags, B_ASYNC)) {/* if async, release it */
brelse(bp);
} else { /* or just wakeup the buffer */
CLR(bp->b_flags, B_WANTED);
wakeup(bp);
}
}
}
#ifdef DDB
void bcstats_print(int (*)(const char *, ...)
__attribute__((__format__(__kprintf__,1,2))));
/*
* bcstats_print: ddb hook to print interesting buffer cache counters
*/
void
bcstats_print(
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
(*pr)("Current Buffer Cache status:\n");
(*pr)("numbufs %lld busymapped %lld, delwri %lld\n",
bcstats.numbufs, bcstats.busymapped, bcstats.delwribufs);
(*pr)("kvaslots %lld avail kva slots %lld\n",
bcstats.kvaslots, bcstats.kvaslots_avail);
(*pr)("bufpages %lld, dmapages %lld, dirtypages %lld\n",
bcstats.numbufpages, bcstats.dmapages, bcstats.numdirtypages);
(*pr)("pendingreads %lld, pendingwrites %lld\n",
bcstats.pendingreads, bcstats.pendingwrites);
(*pr)("highflips %lld, highflops %lld, dmaflips %lld\n",
bcstats.highflips, bcstats.highflops, bcstats.dmaflips);
}
#endif
void
buf_adjcnt(struct buf *bp, long ncount)
{
KASSERT(ncount <= bp->b_bufsize);
bp->b_bcount = ncount;
}
/* bufcache freelist code below */
/*
* Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* The code below implements a variant of the 2Q buffer cache algorithm by
* Johnson and Shasha.
*
* General Outline
* We divide the buffer cache into three working sets: current, previous,
* and long term. Each list is itself LRU and buffers get promoted and moved
* around between them. A buffer starts its life in the current working set.
* As time passes and newer buffers push it out, it will turn into the previous
* working set and is subject to recycling. But if it's accessed again from
* the previous working set, that's an indication that it's actually in the
* long term working set, so we promote it there. The separation of current
* and previous working sets prevents us from promoting a buffer that's only
* temporarily hot to the long term cache.
*
* The objective is to provide scan resistance by making the long term
* working set ineligible for immediate recycling, even as the current
* working set is rapidly turned over.
*
* Implementation
* The code below identifies the current, previous, and long term sets as
* hotqueue, coldqueue, and warmqueue. The hot and warm queues are capped at
* 1/3 of the total clean pages, after which point they start pushing their
* oldest buffers into coldqueue.
* A buf always starts out with neither WARM or COLD flags set (implying HOT).
* When released, it will be returned to the tail of the hotqueue list.
* When the hotqueue gets too large, the oldest hot buf will be moved to the
* coldqueue, with the B_COLD flag set. When a cold buf is released, we set
* the B_WARM flag and put it onto the warmqueue. Warm bufs are also
* directly returned to the end of the warmqueue. As with the hotqueue, when
* the warmqueue grows too large, B_WARM bufs are moved onto the coldqueue.
*
* Note that this design does still support large working sets, greater
* than the cap of hotqueue or warmqueue would imply. The coldqueue is still
* cached and has no maximum length. The hot and warm queues form a Y feeding
* into the coldqueue. Moving bufs between queues is constant time, so this
* design decays to one long warm->cold queue.
*
* In the 2Q paper, hotqueue and coldqueue are A1in and A1out. The warmqueue
* is Am. We always cache pages, as opposed to pointers to pages for A1.
*
* This implementation adds support for multiple 2q caches.
*
* If we have more than one 2q cache, as bufs fall off the cold queue
* for recycling, bufs that have been warm before (which retain the
* B_WARM flag in addition to B_COLD) can be put into the hot queue of
* a second level 2Q cache. buffers which are only B_COLD are
* recycled. Bufs falling off the last cache's cold queue are always
* recycled.
*
*/
/*
* this function is called when a hot or warm queue may have exceeded its
* size limit. it will move a buf to the coldqueue.
*/
int chillbufs(struct
bufcache *cache, struct bufqueue *queue, int64_t *queuepages);
void
bufcache_init(void)
{
int i;
for (i = 0; i < NUM_CACHES; i++) {
TAILQ_INIT(&cleancache[i].hotqueue);
TAILQ_INIT(&cleancache[i].coldqueue);
TAILQ_INIT(&cleancache[i].warmqueue);
}
TAILQ_INIT(&dirtyqueue);
}
/*
* if the buffer caches have shrunk, we may need to rebalance our queues.
*/
void
bufcache_adjust(void)
{
int i;
for (i = 0; i < NUM_CACHES; i++) {
while (chillbufs(&cleancache[i], &cleancache[i].warmqueue,
&cleancache[i].warmbufpages) ||
chillbufs(&cleancache[i], &cleancache[i].hotqueue,
&cleancache[i].hotbufpages))
continue;
}
}
/*
* Get a clean buffer from the cache. if "discard" is set do not promote
* previously warm buffers as normal, because we are tossing everything
* away such as in a hibernation
*/
struct buf *
bufcache_getcleanbuf(int cachenum, int discard)
{
struct buf *bp = NULL;
struct bufcache *cache = &cleancache[cachenum];
struct bufqueue * queue;
splassert(IPL_BIO);
/* try cold queue */
while ((bp = TAILQ_FIRST(&cache->coldqueue)) ||
(bp = TAILQ_FIRST(&cache->warmqueue)) ||
(bp = TAILQ_FIRST(&cache->hotqueue))) {
int64_t pages = atop(bp->b_bufsize);
struct bufcache *newcache;
if (discard || cachenum >= NUM_CACHES - 1) {
/* Victim selected, give it up */
return bp;
}
KASSERT(bp->cache == cachenum);
/*
* If this buffer was warm before, move it to
* the hot queue in the next cache
*/
if (fliphigh) {
/*
* If we are in the DMA cache, try to flip the
* buffer up high to move it on to the other
* caches. if we can't move the buffer to high
* memory without sleeping, we give it up and
* return it rather than fight for more memory
* against non buffer cache competitors.
*/
SET(bp->b_flags, B_BUSY);
if (bp->cache == 0 && buf_flip_high(bp) == -1) {
CLR(bp->b_flags, B_BUSY);
return bp;
}
CLR(bp->b_flags, B_BUSY);
}
/* Move the buffer to the hot queue in the next cache */
if (ISSET(bp->b_flags, B_COLD)) {
queue = &cache->coldqueue;
} else if (ISSET(bp->b_flags, B_WARM)) {
queue = &cache->warmqueue;
cache->warmbufpages -= pages;
} else {
queue = &cache->hotqueue;
cache->hotbufpages -= pages;
}
TAILQ_REMOVE(queue, bp, b_freelist);
cache->cachepages -= pages;
CLR(bp->b_flags, B_WARM);
CLR(bp->b_flags, B_COLD);
bp->cache++;
newcache= &cleancache[bp->cache];
newcache->cachepages += pages;
newcache->hotbufpages += pages;
chillbufs(newcache, &newcache->hotqueue,
&newcache->hotbufpages);
TAILQ_INSERT_TAIL(&newcache->hotqueue, bp, b_freelist);
}
return bp;
}
void
discard_buffer(struct buf *bp)
{
splassert(IPL_BIO);
bufcache_take(bp);
if (bp->b_vp) {
RBT_REMOVE(buf_rb_bufs,
&bp->b_vp->v_bufs_tree, bp);
brelvp(bp);
}
buf_put(bp);
}
int64_t
bufcache_recover_dmapages(int discard, int64_t howmany)
{
struct buf *bp = NULL;
struct bufcache *cache = &cleancache[DMA_CACHE];
struct bufqueue * queue;
int64_t recovered = 0;
splassert(IPL_BIO);
while ((recovered < howmany) &&
((bp = TAILQ_FIRST(&cache->coldqueue)) ||
(bp = TAILQ_FIRST(&cache->warmqueue)) ||
(bp = TAILQ_FIRST(&cache->hotqueue)))) {
int64_t pages = atop(bp->b_bufsize);
struct bufcache *newcache;
if (discard || DMA_CACHE >= NUM_CACHES - 1) {
discard_buffer(bp);
continue;
}
KASSERT(bp->cache == DMA_CACHE);
/*
* If this buffer was warm before, move it to
* the hot queue in the next cache
*/
/*
* One way or another, the pages for this
* buffer are leaving DMA memory
*/
recovered += pages;
if (!fliphigh) {
discard_buffer(bp);
continue;
}
/*
* If we are in the DMA cache, try to flip the
* buffer up high to move it on to the other
* caches. if we can't move the buffer to high
* memory without sleeping, we give it up
* now rather than fight for more memory
* against non buffer cache competitors.
*/
SET(bp->b_flags, B_BUSY);
if (bp->cache == 0 && buf_flip_high(bp) == -1) {
CLR(bp->b_flags, B_BUSY);
discard_buffer(bp);
continue;
}
CLR(bp->b_flags, B_BUSY);
/*
* Move the buffer to the hot queue in the next cache
*/
if (ISSET(bp->b_flags, B_COLD)) {
queue = &cache->coldqueue;
} else if (ISSET(bp->b_flags, B_WARM)) {
queue = &cache->warmqueue;
cache->warmbufpages -= pages;
} else {
queue = &cache->hotqueue;
cache->hotbufpages -= pages;
}
TAILQ_REMOVE(queue, bp, b_freelist);
cache->cachepages -= pages;
CLR(bp->b_flags, B_WARM);
CLR(bp->b_flags, B_COLD);
bp->cache++;
newcache= &cleancache[bp->cache];
newcache->cachepages += pages;
newcache->hotbufpages += pages;
chillbufs(newcache, &newcache->hotqueue,
&newcache->hotbufpages);
TAILQ_INSERT_TAIL(&newcache->hotqueue, bp, b_freelist);
}
return recovered;
}
struct buf *
bufcache_getcleanbuf_range(int start, int end, int discard)
{
int i, j = start, q = end;
struct buf *bp = NULL;
/*
* XXX in theory we could promote warm buffers into a previous queue
* so in the pathological case of where we go through all the caches
* without getting a buffer we have to start at the beginning again.
*/
while (j <= q) {
for (i = q; i >= j; i--)
if ((bp = bufcache_getcleanbuf(i, discard)))
return (bp);
j++;
}
return bp;
}
struct buf *
bufcache_gethighcleanbuf(void)
{
if (!fliphigh)
return NULL;
return bufcache_getcleanbuf_range(DMA_CACHE + 1, NUM_CACHES - 1, 0);
}
struct buf *
bufcache_getdmacleanbuf(void)
{
if (fliphigh)
return bufcache_getcleanbuf_range(DMA_CACHE, DMA_CACHE, 0);
return bufcache_getcleanbuf_range(DMA_CACHE, NUM_CACHES - 1, 0);
}
struct buf *
bufcache_getdirtybuf(void)
{
return TAILQ_FIRST(&dirtyqueue);
}
void
bufcache_take(struct buf *bp)
{
struct bufqueue *queue;
int64_t pages;
splassert(IPL_BIO);
KASSERT(ISSET(bp->b_flags, B_BC));
KASSERT(bp->cache >= DMA_CACHE);
KASSERT((bp->cache < NUM_CACHES));
pages = atop(bp->b_bufsize);
TRACEPOINT(vfs, bufcache_take, bp->b_flags, bp->cache, pages);
struct bufcache *cache = &cleancache[bp->cache];
if (!ISSET(bp->b_flags, B_DELWRI)) {
if (ISSET(bp->b_flags, B_COLD)) {
queue = &cache->coldqueue;
} else if (ISSET(bp->b_flags, B_WARM)) {
queue = &cache->warmqueue;
cache->warmbufpages -= pages;
} else {
queue = &cache->hotqueue;
cache->hotbufpages -= pages;
}
bcstats.numcleanpages -= pages;
cache->cachepages -= pages;
} else {
queue = &dirtyqueue;
bcstats.numdirtypages -= pages;
bcstats.delwribufs--;
}
TAILQ_REMOVE(queue, bp, b_freelist);
}
/* move buffers from a hot or warm queue to a cold queue in a cache */
int
chillbufs(struct bufcache *cache, struct bufqueue *queue, int64_t *queuepages)
{
struct buf *bp;
int64_t limit, pages;
/*
* We limit the hot queue to be small, with a max of 4096 pages.
* We limit the warm queue to half the cache size.
*
* We impose a minimum size of 96 to prevent too much "wobbling".
*/
if (queue == &cache->hotqueue)
limit = min(cache->cachepages / 20, 4096);
else if (queue == &cache->warmqueue)
limit = (cache->cachepages / 2);
else
panic("chillbufs: invalid queue");
if (*queuepages > 96 && *queuepages > limit) {
bp = TAILQ_FIRST(queue);
if (!bp)
panic("inconsistent bufpage counts");
pages = atop(bp->b_bufsize);
*queuepages -= pages;
TAILQ_REMOVE(queue, bp, b_freelist);
/* we do not clear B_WARM */
SET(bp->b_flags, B_COLD);
TAILQ_INSERT_TAIL(&cache->coldqueue, bp, b_freelist);
return 1;
}
return 0;
}
void
bufcache_release(struct buf *bp)
{
struct bufqueue *queue;
int64_t pages;
struct bufcache *cache = &cleancache[bp->cache];
KASSERT(ISSET(bp->b_flags, B_BC));
pages = atop(bp->b_bufsize);
TRACEPOINT(vfs, bufcache_rel, bp->b_flags, bp->cache, pages);
if (fliphigh) {
if (ISSET(bp->b_flags, B_DMA) && bp->cache > 0)
panic("B_DMA buffer release from cache %d",
bp->cache);
else if ((!ISSET(bp->b_flags, B_DMA)) && bp->cache == 0)
panic("Non B_DMA buffer release from cache %d",
bp->cache);
}
if (!ISSET(bp->b_flags, B_DELWRI)) {
int64_t *queuepages;
if (ISSET(bp->b_flags, B_WARM | B_COLD)) {
SET(bp->b_flags, B_WARM);
CLR(bp->b_flags, B_COLD);
queue = &cache->warmqueue;
queuepages = &cache->warmbufpages;
} else {
queue = &cache->hotqueue;
queuepages = &cache->hotbufpages;
}
*queuepages += pages;
bcstats.numcleanpages += pages;
cache->cachepages += pages;
chillbufs(cache, queue, queuepages);
} else {
queue = &dirtyqueue;
bcstats.numdirtypages += pages;
bcstats.delwribufs++;
}
TAILQ_INSERT_TAIL(queue, bp, b_freelist);
}
#ifdef HIBERNATE
/*
* Nuke the buffer cache from orbit when hibernating. We do not want to save
* any clean cache pages to swap and read them back. the original disk files
* are just as good.
*/
void
hibernate_suspend_bufcache(void)
{
struct buf *bp;
int s;
s = splbio();
/* Chuck away all the cache pages.. discard bufs, do not promote */
while ((bp = bufcache_getcleanbuf_range(DMA_CACHE, NUM_CACHES - 1, 1))) {
bufcache_take(bp);
if (bp->b_vp) {
RBT_REMOVE(buf_rb_bufs, &bp->b_vp->v_bufs_tree, bp);
brelvp(bp);
}
buf_put(bp);
}
splx(s);
}
void
hibernate_resume_bufcache(void)
{
/* XXX Nothing needed here for now */
}
#endif /* HIBERNATE */
122
122
122
122
122
84
84
84
84
84
60
59
60
37
37
13
24
37
37
37
93
40
58
40
58
93
93
93
93
40
2
2
2
2
2
2
2
38
38
38
32
32
30
30
30
9
30
9
174
3
23
174
23
136
137
40
101
66
180
254
83
15
20
61
70
16
60
46
27
47
60
60
60
135
135
135
57
74
96
10
10
96
96
70
67
96
65
65
65
35
57
65
65
93
92
93
92
93
93
93
93
93
58
40
93
89
16
16
16
83
83
83
83
83
83
16
52
51
52
52
52
44
25
37
15
130
130
130
127
10
9
129
95
130
82
82
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
/* $OpenBSD: ffs_alloc.c,v 1.114 2021/03/11 13:31:35 jsg Exp $ */
/* $NetBSD: ffs_alloc.c,v 1.11 1996/05/11 18:27:09 mycroft Exp $ */
/*
* Copyright (c) 2002 Networks Associates Technology, Inc.
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Marshall
* Kirk McKusick and Network Associates Laboratories, the Security
* Research Division of Network Associates, Inc. under DARPA/SPAWAR
* contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
* research program.
*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_alloc.c 8.11 (Berkeley) 10/27/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/syslog.h>
#include <sys/stdint.h>
#include <sys/time.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
#define ffs_fserr(fs, uid, cp) do { \
log(LOG_ERR, "uid %u on %s: %s\n", (uid), \
(fs)->fs_fsmnt, (cp)); \
} while (0)
daddr_t ffs_alloccg(struct inode *, u_int, daddr_t, int);
struct buf * ffs_cgread(struct fs *, struct inode *, u_int);
daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t);
ufsino_t ffs_dirpref(struct inode *);
daddr_t ffs_fragextend(struct inode *, u_int, daddr_t, int, int);
daddr_t ffs_hashalloc(struct inode *, u_int, daddr_t, int,
daddr_t (*)(struct inode *, u_int, daddr_t, int));
daddr_t ffs_nodealloccg(struct inode *, u_int, daddr_t, int);
daddr_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int);
static const struct timeval fserr_interval = { 2, 0 };
/*
* Allocate a block in the file system.
*
* The size of the requested block is given, which must be some
* multiple of fs_fsize and <= fs_bsize.
* A preference may be optionally specified. If a preference is given
* the following hierarchy is used to allocate a block:
* 1) allocate the requested block.
* 2) allocate a rotationally optimal block in the same cylinder.
* 3) allocate a block in the same cylinder group.
* 4) quadratically rehash into other cylinder groups, until an
* available block is located.
* If no block preference is given the following hierarchy is used
* to allocate a block:
* 1) allocate a block in the cylinder group that contains the
* inode for the file.
* 2) quadratically rehash into other cylinder groups, until an
* available block is located.
*/
int
ffs_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size,
struct ucred *cred, daddr_t *bnp)
{
static struct timeval fsfull_last;
struct fs *fs;
daddr_t bno;
u_int cg;
int error;
*bnp = 0;
fs = ip->i_fs;
#ifdef DIAGNOSTIC
if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {
printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n",
ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);
panic("ffs_alloc: bad size");
}
if (cred == NOCRED)
panic("ffs_alloc: missing credential");
#endif /* DIAGNOSTIC */
if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
goto nospace;
if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)
goto nospace;
if ((error = ufs_quota_alloc_blocks(ip, btodb(size), cred)) != 0)
return (error);
/*
* Start allocation in the preferred block's cylinder group or
* the file's inode's cylinder group if no preferred block was
* specified.
*/
if (bpref >= fs->fs_size)
bpref = 0;
if (bpref == 0)
cg = ino_to_cg(fs, ip->i_number);
else
cg = dtog(fs, bpref);
/* Try allocating a block. */
bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg);
if (bno > 0) {
/* allocation successful, update inode data */
DIP_ADD(ip, blocks, btodb(size));
ip->i_flag |= IN_CHANGE | IN_UPDATE;
*bnp = bno;
return (0);
}
/* Restore user's disk quota because allocation failed. */
(void) ufs_quota_free_blocks(ip, btodb(size), cred);
nospace:
if (ratecheck(&fsfull_last, &fserr_interval)) {
ffs_fserr(fs, cred->cr_uid, "file system full");
uprintf("\n%s: write failed, file system is full\n",
fs->fs_fsmnt);
}
return (ENOSPC);
}
/*
* Reallocate a fragment to a bigger size
*
* The number and size of the old block is given, and a preference
* and new size is also specified. The allocator attempts to extend
* the original block. Failing that, the regular block allocator is
* invoked to get an appropriate block.
*/
int
ffs_realloccg(struct inode *ip, daddr_t lbprev, daddr_t bpref, int osize,
int nsize, struct ucred *cred, struct buf **bpp, daddr_t *blknop)
{
static struct timeval fsfull_last;
struct fs *fs;
struct buf *bp = NULL;
daddr_t quota_updated = 0;
int request, error;
u_int cg;
daddr_t bprev, bno;
if (bpp != NULL)
*bpp = NULL;
fs = ip->i_fs;
#ifdef DIAGNOSTIC
if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 ||
(u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) {
printf(
"dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n",
ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt);
panic("ffs_realloccg: bad size");
}
if (cred == NOCRED)
panic("ffs_realloccg: missing credential");
#endif /* DIAGNOSTIC */
if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)
goto nospace;
bprev = DIP(ip, db[lbprev]);
if (bprev == 0) {
printf("dev = 0x%x, bsize = %d, bprev = %lld, fs = %s\n",
ip->i_dev, fs->fs_bsize, (long long)bprev, fs->fs_fsmnt);
panic("ffs_realloccg: bad bprev");
}
/*
* Allocate the extra space in the buffer.
*/
if (bpp != NULL) {
if ((error = bread(ITOV(ip), lbprev, fs->fs_bsize, &bp)) != 0)
goto error;
buf_adjcnt(bp, osize);
}
if ((error = ufs_quota_alloc_blocks(ip, btodb(nsize - osize), cred))
!= 0)
goto error;
quota_updated = btodb(nsize - osize);
/*
* Check for extension in the existing location.
*/
cg = dtog(fs, bprev);
if ((bno = ffs_fragextend(ip, cg, bprev, osize, nsize)) != 0) {
DIP_ADD(ip, blocks, btodb(nsize - osize));
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (bpp != NULL) {
if (bp->b_blkno != fsbtodb(fs, bno))
panic("ffs_realloccg: bad blockno");
#ifdef DIAGNOSTIC
if (nsize > bp->b_bufsize)
panic("ffs_realloccg: small buf");
#endif
buf_adjcnt(bp, nsize);
bp->b_flags |= B_DONE;
memset(bp->b_data + osize, 0, nsize - osize);
*bpp = bp;
}
if (blknop != NULL) {
*blknop = bno;
}
return (0);
}
/*
* Allocate a new disk location.
*/
if (bpref >= fs->fs_size)
bpref = 0;
switch (fs->fs_optim) {
case FS_OPTSPACE:
/*
* Allocate an exact sized fragment. Although this makes
* best use of space, we will waste time relocating it if
* the file continues to grow. If the fragmentation is
* less than half of the minimum free reserve, we choose
* to begin optimizing for time.
*/
request = nsize;
if (fs->fs_minfree < 5 ||
fs->fs_cstotal.cs_nffree >
fs->fs_dsize * fs->fs_minfree / (2 * 100))
break;
fs->fs_optim = FS_OPTTIME;
break;
case FS_OPTTIME:
/*
* At this point we have discovered a file that is trying to
* grow a small fragment to a larger fragment. To save time,
* we allocate a full sized block, then free the unused portion.
* If the file continues to grow, the `ffs_fragextend' call
* above will be able to grow it in place without further
* copying. If aberrant programs cause disk fragmentation to
* grow within 2% of the free reserve, we choose to begin
* optimizing for space.
*/
request = fs->fs_bsize;
if (fs->fs_cstotal.cs_nffree <
fs->fs_dsize * (fs->fs_minfree - 2) / 100)
break;
fs->fs_optim = FS_OPTSPACE;
break;
default:
printf("dev = 0x%x, optim = %d, fs = %s\n",
ip->i_dev, fs->fs_optim, fs->fs_fsmnt);
panic("ffs_realloccg: bad optim");
/* NOTREACHED */
}
bno = ffs_hashalloc(ip, cg, bpref, request, ffs_alloccg);
if (bno <= 0)
goto nospace;
(void) uvm_vnp_uncache(ITOV(ip));
if (!DOINGSOFTDEP(ITOV(ip)))
ffs_blkfree(ip, bprev, (long)osize);
if (nsize < request)
ffs_blkfree(ip, bno + numfrags(fs, nsize),
(long)(request - nsize));
DIP_ADD(ip, blocks, btodb(nsize - osize));
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (bpp != NULL) {
bp->b_blkno = fsbtodb(fs, bno);
#ifdef DIAGNOSTIC
if (nsize > bp->b_bufsize)
panic("ffs_realloccg: small buf 2");
#endif
buf_adjcnt(bp, nsize);
bp->b_flags |= B_DONE;
memset(bp->b_data + osize, 0, nsize - osize);
*bpp = bp;
}
if (blknop != NULL) {
*blknop = bno;
}
return (0);
nospace:
if (ratecheck(&fsfull_last, &fserr_interval)) {
ffs_fserr(fs, cred->cr_uid, "file system full");
uprintf("\n%s: write failed, file system is full\n",
fs->fs_fsmnt);
}
error = ENOSPC;
error:
if (bp != NULL) {
brelse(bp);
bp = NULL;
}
/*
* Restore user's disk quota because allocation failed.
*/
if (quota_updated != 0)
(void)ufs_quota_free_blocks(ip, quota_updated, cred);
return error;
}
/*
* Allocate an inode in the file system.
*
* If allocating a directory, use ffs_dirpref to select the inode.
* If allocating in a directory, the following hierarchy is followed:
* 1) allocate the preferred inode.
* 2) allocate an inode in the same cylinder group.
* 3) quadratically rehash into other cylinder groups, until an
* available inode is located.
* If no inode preference is given the following hierarchy is used
* to allocate an inode:
* 1) allocate an inode in cylinder group 0.
* 2) quadratically rehash into other cylinder groups, until an
* available inode is located.
*/
int
ffs_inode_alloc(struct inode *pip, mode_t mode, struct ucred *cred,
struct vnode **vpp)
{
static struct timeval fsnoinodes_last;
struct vnode *pvp = ITOV(pip);
struct fs *fs;
struct inode *ip;
ufsino_t ino, ipref;
u_int cg;
int error;
*vpp = NULL;
fs = pip->i_fs;
if (fs->fs_cstotal.cs_nifree == 0)
goto noinodes;
if ((mode & IFMT) == IFDIR)
ipref = ffs_dirpref(pip);
else
ipref = pip->i_number;
if (ipref >= fs->fs_ncg * fs->fs_ipg)
ipref = 0;
cg = ino_to_cg(fs, ipref);
/*
* Track number of dirs created one after another
* in a same cg without intervening by files.
*/
if ((mode & IFMT) == IFDIR) {
if (fs->fs_contigdirs[cg] < 255)
fs->fs_contigdirs[cg]++;
} else {
if (fs->fs_contigdirs[cg] > 0)
fs->fs_contigdirs[cg]--;
}
ino = (ufsino_t)ffs_hashalloc(pip, cg, ipref, mode, ffs_nodealloccg);
if (ino == 0)
goto noinodes;
error = VFS_VGET(pvp->v_mount, ino, vpp);
if (error) {
ffs_inode_free(pip, ino, mode);
return (error);
}
ip = VTOI(*vpp);
if (DIP(ip, mode)) {
printf("mode = 0%o, inum = %u, fs = %s\n",
DIP(ip, mode), ip->i_number, fs->fs_fsmnt);
panic("ffs_valloc: dup alloc");
}
if (DIP(ip, blocks)) {
printf("free inode %s/%d had %lld blocks\n",
fs->fs_fsmnt, ino, (long long)DIP(ip, blocks));
DIP_ASSIGN(ip, blocks, 0);
}
DIP_ASSIGN(ip, flags, 0);
/*
* Set up a new generation number for this inode.
* On wrap, we make sure to assign a number != 0 and != UINT_MAX
* (the original value).
*/
if (DIP(ip, gen) != 0)
DIP_ADD(ip, gen, 1);
while (DIP(ip, gen) == 0)
DIP_ASSIGN(ip, gen, arc4random_uniform(UINT_MAX));
return (0);
noinodes:
if (ratecheck(&fsnoinodes_last, &fserr_interval)) {
ffs_fserr(fs, cred->cr_uid, "out of inodes");
uprintf("\n%s: create/symlink failed, no inodes free\n",
fs->fs_fsmnt);
}
return (ENOSPC);
}
/*
* Find a cylinder group to place a directory.
*
* The policy implemented by this algorithm is to allocate a
* directory inode in the same cylinder group as its parent
* directory, but also to reserve space for its files inodes
* and data. Restrict the number of directories which may be
* allocated one after another in the same cylinder group
* without intervening allocation of files.
*
* If we allocate a first level directory then force allocation
* in another cylinder group.
*/
ufsino_t
ffs_dirpref(struct inode *pip)
{
struct fs *fs;
u_int cg, prefcg;
u_int dirsize, cgsize;
u_int avgifree, avgbfree, avgndir, curdirsize;
u_int minifree, minbfree, maxndir;
u_int mincg, minndir;
u_int maxcontigdirs;
fs = pip->i_fs;
avgifree = fs->fs_cstotal.cs_nifree / fs->fs_ncg;
avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
avgndir = fs->fs_cstotal.cs_ndir / fs->fs_ncg;
/*
* Force allocation in another cg if creating a first level dir.
*/
if (ITOV(pip)->v_flag & VROOT) {
prefcg = arc4random_uniform(fs->fs_ncg);
mincg = prefcg;
minndir = fs->fs_ipg;
for (cg = prefcg; cg < fs->fs_ncg; cg++)
if (fs->fs_cs(fs, cg).cs_ndir < minndir &&
fs->fs_cs(fs, cg).cs_nifree >= avgifree &&
fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
mincg = cg;
minndir = fs->fs_cs(fs, cg).cs_ndir;
}
for (cg = 0; cg < prefcg; cg++)
if (fs->fs_cs(fs, cg).cs_ndir < minndir &&
fs->fs_cs(fs, cg).cs_nifree >= avgifree &&
fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
mincg = cg;
minndir = fs->fs_cs(fs, cg).cs_ndir;
}
cg = mincg;
goto end;
} else
prefcg = ino_to_cg(fs, pip->i_number);
/*
* Count various limits which used for
* optimal allocation of a directory inode.
*/
maxndir = min(avgndir + fs->fs_ipg / 16, fs->fs_ipg);
minifree = avgifree - (avgifree / 4);
if (minifree < 1)
minifree = 1;
minbfree = avgbfree - (avgbfree / 4);
if (minbfree < 1)
minbfree = 1;
cgsize = fs->fs_fsize * fs->fs_fpg;
dirsize = fs->fs_avgfilesize * fs->fs_avgfpdir;
curdirsize = avgndir ? (cgsize - avgbfree * fs->fs_bsize) / avgndir : 0;
if (dirsize < curdirsize)
dirsize = curdirsize;
if (dirsize <= 0)
maxcontigdirs = 0; /* dirsize overflowed */
else
maxcontigdirs = min(avgbfree * fs->fs_bsize / dirsize, 255);
if (fs->fs_avgfpdir > 0)
maxcontigdirs = min(maxcontigdirs,
fs->fs_ipg / fs->fs_avgfpdir);
if (maxcontigdirs == 0)
maxcontigdirs = 1;
/*
* Limit number of dirs in one cg and reserve space for
* regular files, but only if we have no deficit in
* inodes or space.
*
* We are trying to find a suitable cylinder group nearby
* our preferred cylinder group to place a new directory.
* We scan from our preferred cylinder group forward looking
* for a cylinder group that meets our criterion. If we get
* to the final cylinder group and do not find anything,
* we start scanning forwards from the beginning of the
* filesystem. While it might seem sensible to start scanning
* backwards or even to alternate looking forward and backward,
* this approach fails badly when the filesystem is nearly full.
* Specifically, we first search all the areas that have no space
* and finally try the one preceding that. We repeat this on
* every request and in the case of the final block end up
* searching the entire filesystem. By jumping to the front
* of the filesystem, our future forward searches always look
* in new cylinder groups so finds every possible block after
* one pass over the filesystem.
*/
for (cg = prefcg; cg < fs->fs_ncg; cg++)
if (fs->fs_cs(fs, cg).cs_ndir < maxndir &&
fs->fs_cs(fs, cg).cs_nifree >= minifree &&
fs->fs_cs(fs, cg).cs_nbfree >= minbfree) {
if (fs->fs_contigdirs[cg] < maxcontigdirs)
goto end;
}
for (cg = 0; cg < prefcg; cg++)
if (fs->fs_cs(fs, cg).cs_ndir < maxndir &&
fs->fs_cs(fs, cg).cs_nifree >= minifree &&
fs->fs_cs(fs, cg).cs_nbfree >= minbfree) {
if (fs->fs_contigdirs[cg] < maxcontigdirs)
goto end;
}
/*
* This is a backstop when we have deficit in space.
*/
for (cg = prefcg; cg < fs->fs_ncg; cg++)
if (fs->fs_cs(fs, cg).cs_nifree >= avgifree)
goto end;
for (cg = 0; cg < prefcg; cg++)
if (fs->fs_cs(fs, cg).cs_nifree >= avgifree)
goto end;
end:
return ((ufsino_t)(fs->fs_ipg * cg));
}
/*
* Select the desired position for the next block in a file. The file is
* logically divided into sections. The first section is composed of the
* direct blocks. Each additional section contains fs_maxbpg blocks.
*
* If no blocks have been allocated in the first section, the policy is to
* request a block in the same cylinder group as the inode that describes
* the file. The first indirect is allocated immediately following the last
* direct block and the data blocks for the first indirect immediately
* follow it.
*
* If no blocks have been allocated in any other section, the indirect
* block(s) are allocated in the same cylinder group as its inode in an
* area reserved immediately following the inode blocks. The policy for
* the data blocks is to place them in a cylinder group with a greater than
* average number of free blocks. An appropriate cylinder group is found
* by using a rotor that sweeps the cylinder groups. When a new group of
* blocks is needed, the sweep begins in the cylinder group following the
* cylinder group from which the previous allocation was made. The sweep
* continues until a cylinder group with greater than the average number
* of free blocks is found. If the allocation is for the first block in an
* indirect block, the information on the previous allocation is unavailable;
* here a best guess is made based upon the logical block number being
* allocated.
*/
int32_t
ffs1_blkpref(struct inode *ip, daddr_t lbn, int indx, int32_t *bap)
{
struct fs *fs;
u_int cg, inocg;
u_int avgbfree, startcg;
uint32_t pref;
KASSERT(indx <= 0 || bap != NULL);
fs = ip->i_fs;
/*
* Allocation of indirect blocks is indicated by passing negative
* values in indx: -1 for single indirect, -2 for double indirect,
* -3 for triple indirect. As noted below, we attempt to allocate
* the first indirect inline with the file data. For all later
* indirect blocks, the data is often allocated in other cylinder
* groups. However to speed random file access and to speed up
* fsck, the filesystem reserves the first fs_metaspace blocks
* (typically half of fs_minfree) of the data area of each cylinder
* group to hold these later indirect blocks.
*/
inocg = ino_to_cg(fs, ip->i_number);
if (indx < 0) {
/*
* Our preference for indirect blocks is the zone at the
* beginning of the inode's cylinder group data area that
* we try to reserve for indirect blocks.
*/
pref = cgmeta(fs, inocg);
/*
* If we are allocating the first indirect block, try to
* place it immediately following the last direct block.
*/
if (indx == -1 && lbn < NDADDR + NINDIR(fs) &&
ip->i_din1->di_db[NDADDR - 1] != 0)
pref = ip->i_din1->di_db[NDADDR - 1] + fs->fs_frag;
return (pref);
}
/*
* If we are allocating the first data block in the first indirect
* block and the indirect has been allocated in the data block area,
* try to place it immediately following the indirect block.
*/
if (lbn == NDADDR) {
pref = ip->i_din1->di_ib[0];
if (pref != 0 && pref >= cgdata(fs, inocg) &&
pref < cgbase(fs, inocg + 1))
return (pref + fs->fs_frag);
}
/*
* If we are the beginning of a file, or we have already allocated
* the maximum number of blocks per cylinder group, or we do not
* have a block allocated immediately preceding us, then we need
* to decide where to start allocating new blocks.
*/
if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
/*
* If we are allocating a directory data block, we want
* to place it in the metadata area.
*/
if ((DIP(ip, mode) & IFMT) == IFDIR)
return (cgmeta(fs, inocg));
/*
* Until we fill all the direct and all the first indirect's
* blocks, we try to allocate in the data area of the inode's
* cylinder group.
*/
if (lbn < NDADDR + NINDIR(fs))
return (cgdata(fs, inocg));
/*
* Find a cylinder with greater than average number of
* unused data blocks.
*/
if (indx == 0 || bap[indx - 1] == 0)
startcg = inocg + lbn / fs->fs_maxbpg;
else
startcg = dtog(fs, bap[indx - 1]) + 1;
startcg %= fs->fs_ncg;
avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
for (cg = startcg; cg < fs->fs_ncg; cg++)
if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
fs->fs_cgrotor = cg;
return (cgdata(fs, cg));
}
for (cg = 0; cg <= startcg; cg++)
if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
fs->fs_cgrotor = cg;
return (cgdata(fs, cg));
}
return (0);
}
/*
* Otherwise, we just always try to lay things out contiguously.
*/
return (bap[indx - 1] + fs->fs_frag);
}
/*
* Same as above, for UFS2.
*/
#ifdef FFS2
int64_t
ffs2_blkpref(struct inode *ip, daddr_t lbn, int indx, int64_t *bap)
{
struct fs *fs;
u_int cg, inocg;
u_int avgbfree, startcg;
uint64_t pref;
KASSERT(indx <= 0 || bap != NULL);
fs = ip->i_fs;
/*
* Allocation of indirect blocks is indicated by passing negative
* values in indx: -1 for single indirect, -2 for double indirect,
* -3 for triple indirect. As noted below, we attempt to allocate
* the first indirect inline with the file data. For all later
* indirect blocks, the data is often allocated in other cylinder
* groups. However to speed random file access and to speed up
* fsck, the filesystem reserves the first fs_metaspace blocks
* (typically half of fs_minfree) of the data area of each cylinder
* group to hold these later indirect blocks.
*/
inocg = ino_to_cg(fs, ip->i_number);
if (indx < 0) {
/*
* Our preference for indirect blocks is the zone at the
* beginning of the inode's cylinder group data area that
* we try to reserve for indirect blocks.
*/
pref = cgmeta(fs, inocg);
/*
* If we are allocating the first indirect block, try to
* place it immediately following the last direct block.
*/
if (indx == -1 && lbn < NDADDR + NINDIR(fs) &&
ip->i_din2->di_db[NDADDR - 1] != 0)
pref = ip->i_din2->di_db[NDADDR - 1] + fs->fs_frag;
return (pref);
}
/*
* If we are allocating the first data block in the first indirect
* block and the indirect has been allocated in the data block area,
* try to place it immediately following the indirect block.
*/
if (lbn == NDADDR) {
pref = ip->i_din2->di_ib[0];
if (pref != 0 && pref >= cgdata(fs, inocg) &&
pref < cgbase(fs, inocg + 1))
return (pref + fs->fs_frag);
}
/*
* If we are the beginning of a file, or we have already allocated
* the maximum number of blocks per cylinder group, or we do not
* have a block allocated immediately preceding us, then we need
* to decide where to start allocating new blocks.
*/
if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
/*
* If we are allocating a directory data block, we want
* to place it in the metadata area.
*/
if ((DIP(ip, mode) & IFMT) == IFDIR)
return (cgmeta(fs, inocg));
/*
* Until we fill all the direct and all the first indirect's
* blocks, we try to allocate in the data area of the inode's
* cylinder group.
*/
if (lbn < NDADDR + NINDIR(fs))
return (cgdata(fs, inocg));
/*
* Find a cylinder with greater than average number of
* unused data blocks.
*/
if (indx == 0 || bap[indx - 1] == 0)
startcg = inocg + lbn / fs->fs_maxbpg;
else
startcg = dtog(fs, bap[indx - 1] + 1);
startcg %= fs->fs_ncg;
avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
for (cg = startcg; cg < fs->fs_ncg; cg++)
if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
return (cgbase(fs, cg) + fs->fs_frag);
for (cg = 0; cg < startcg; cg++)
if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
return (cgbase(fs, cg) + fs->fs_frag);
return (0);
}
/*
* Otherwise, we just always try to lay things out contiguously.
*/
return (bap[indx - 1] + fs->fs_frag);
}
#endif /* FFS2 */
/*
* Implement the cylinder overflow algorithm.
*
* The policy implemented by this algorithm is:
* 1) allocate the block in its requested cylinder group.
* 2) quadratically rehash on the cylinder group number.
* 3) brute force search for a free block.
*/
daddr_t
ffs_hashalloc(struct inode *ip, u_int cg, daddr_t pref, int size,
daddr_t (*allocator)(struct inode *, u_int, daddr_t, int))
{
struct fs *fs;
daddr_t result;
u_int i, icg = cg;
fs = ip->i_fs;
/*
* 1: preferred cylinder group
*/
result = (*allocator)(ip, cg, pref, size);
if (result)
return (result);
/*
* 2: quadratic rehash
*/
for (i = 1; i < fs->fs_ncg; i *= 2) {
cg += i;
if (cg >= fs->fs_ncg)
cg -= fs->fs_ncg;
result = (*allocator)(ip, cg, 0, size);
if (result)
return (result);
}
/*
* 3: brute force search
* Note that we start at i == 2, since 0 was checked initially,
* and 1 is always checked in the quadratic rehash.
*/
cg = (icg + 2) % fs->fs_ncg;
for (i = 2; i < fs->fs_ncg; i++) {
result = (*allocator)(ip, cg, 0, size);
if (result)
return (result);
cg++;
if (cg == fs->fs_ncg)
cg = 0;
}
return (0);
}
struct buf *
ffs_cgread(struct fs *fs, struct inode *ip, u_int cg)
{
struct buf *bp;
if (bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
(int)fs->fs_cgsize, &bp)) {
brelse(bp);
return (NULL);
}
if (!cg_chkmagic((struct cg *)bp->b_data)) {
brelse(bp);
return (NULL);
}
return bp;
}
/*
* Determine whether a fragment can be extended.
*
* Check to see if the necessary fragments are available, and
* if they are, allocate them.
*/
daddr_t
ffs_fragextend(struct inode *ip, u_int cg, daddr_t bprev, int osize, int nsize)
{
struct fs *fs;
struct cg *cgp;
struct buf *bp;
struct timespec now;
daddr_t bno;
int i, frags, bbase;
fs = ip->i_fs;
if (fs->fs_cs(fs, cg).cs_nffree < numfrags(fs, nsize - osize))
return (0);
frags = numfrags(fs, nsize);
bbase = fragnum(fs, bprev);
if (bbase > fragnum(fs, (bprev + frags - 1))) {
/* cannot extend across a block boundary */
return (0);
}
if (!(bp = ffs_cgread(fs, ip, cg)))
return (0);
cgp = (struct cg *)bp->b_data;
nanotime(&now);
cgp->cg_ffs2_time = now.tv_sec;
cgp->cg_time = now.tv_sec;
bno = dtogd(fs, bprev);
for (i = numfrags(fs, osize); i < frags; i++)
if (isclr(cg_blksfree(cgp), bno + i)) {
brelse(bp);
return (0);
}
/*
* the current fragment can be extended
* deduct the count on fragment being extended into
* increase the count on the remaining fragment (if any)
* allocate the extended piece
*/
for (i = frags; i < fs->fs_frag - bbase; i++)
if (isclr(cg_blksfree(cgp), bno + i))
break;
cgp->cg_frsum[i - numfrags(fs, osize)]--;
if (i != frags)
cgp->cg_frsum[i - frags]++;
for (i = numfrags(fs, osize); i < frags; i++) {
clrbit(cg_blksfree(cgp), bno + i);
cgp->cg_cs.cs_nffree--;
fs->fs_cstotal.cs_nffree--;
fs->fs_cs(fs, cg).cs_nffree--;
}
fs->fs_fmod = 1;
if (DOINGSOFTDEP(ITOV(ip)))
softdep_setup_blkmapdep(bp, fs, bprev);
bdwrite(bp);
return (bprev);
}
/*
* Determine whether a block can be allocated.
*
* Check to see if a block of the appropriate size is available,
* and if it is, allocate it.
*/
daddr_t
ffs_alloccg(struct inode *ip, u_int cg, daddr_t bpref, int size)
{
struct fs *fs;
struct cg *cgp;
struct buf *bp;
struct timespec now;
daddr_t bno, blkno;
int i, frags, allocsiz;
fs = ip->i_fs;
if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
return (0);
if (!(bp = ffs_cgread(fs, ip, cg)))
return (0);
cgp = (struct cg *)bp->b_data;
if (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize) {
brelse(bp);
return (0);
}
nanotime(&now);
cgp->cg_ffs2_time = now.tv_sec;
cgp->cg_time = now.tv_sec;
if (size == fs->fs_bsize) {
/* allocate and return a complete data block */
bno = ffs_alloccgblk(ip, bp, bpref);
bdwrite(bp);
return (bno);
}
/*
* check to see if any fragments are already available
* allocsiz is the size which will be allocated, hacking
* it down to a smaller size if necessary
*/
frags = numfrags(fs, size);
for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
if (cgp->cg_frsum[allocsiz] != 0)
break;
if (allocsiz == fs->fs_frag) {
/*
* no fragments were available, so a block will be
* allocated, and hacked up
*/
if (cgp->cg_cs.cs_nbfree == 0) {
brelse(bp);
return (0);
}
bno = ffs_alloccgblk(ip, bp, bpref);
bpref = dtogd(fs, bno);
for (i = frags; i < fs->fs_frag; i++)
setbit(cg_blksfree(cgp), bpref + i);
i = fs->fs_frag - frags;
cgp->cg_cs.cs_nffree += i;
fs->fs_cstotal.cs_nffree += i;
fs->fs_cs(fs, cg).cs_nffree += i;
fs->fs_fmod = 1;
cgp->cg_frsum[i]++;
bdwrite(bp);
return (bno);
}
bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
if (bno < 0) {
brelse(bp);
return (0);
}
for (i = 0; i < frags; i++)
clrbit(cg_blksfree(cgp), bno + i);
cgp->cg_cs.cs_nffree -= frags;
fs->fs_cstotal.cs_nffree -= frags;
fs->fs_cs(fs, cg).cs_nffree -= frags;
fs->fs_fmod = 1;
cgp->cg_frsum[allocsiz]--;
if (frags != allocsiz)
cgp->cg_frsum[allocsiz - frags]++;
blkno = cgbase(fs, cg) + bno;
if (DOINGSOFTDEP(ITOV(ip)))
softdep_setup_blkmapdep(bp, fs, blkno);
bdwrite(bp);
return (blkno);
}
/*
* Allocate a block in a cylinder group.
* Note that this routine only allocates fs_bsize blocks; these
* blocks may be fragmented by the routine that allocates them.
*/
daddr_t
ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref)
{
struct fs *fs;
struct cg *cgp;
daddr_t bno, blkno;
u_int8_t *blksfree;
int cylno, cgbpref;
fs = ip->i_fs;
cgp = (struct cg *) bp->b_data;
blksfree = cg_blksfree(cgp);
if (bpref == 0) {
bpref = cgp->cg_rotor;
} else if ((cgbpref = dtog(fs, bpref)) != cgp->cg_cgx) {
/* map bpref to correct zone in this cg */
if (bpref < cgdata(fs, cgbpref))
bpref = cgmeta(fs, cgp->cg_cgx);
else
bpref = cgdata(fs, cgp->cg_cgx);
}
/*
* If the requested block is available, use it.
*/
bno = dtogd(fs, blknum(fs, bpref));
if (ffs_isblock(fs, blksfree, fragstoblks(fs, bno)))
goto gotit;
/*
* Take the next available block in this cylinder group.
*/
bno = ffs_mapsearch(fs, cgp, bpref, (int) fs->fs_frag);
if (bno < 0)
return (0);
/* Update cg_rotor only if allocated from the data zone */
if (bno >= dtogd(fs, cgdata(fs, cgp->cg_cgx)))
cgp->cg_rotor = bno;
gotit:
blkno = fragstoblks(fs, bno);
ffs_clrblock(fs, blksfree, blkno);
ffs_clusteracct(fs, cgp, blkno, -1);
cgp->cg_cs.cs_nbfree--;
fs->fs_cstotal.cs_nbfree--;
fs->fs_cs(fs, cgp->cg_cgx).cs_nbfree--;
if (fs->fs_magic != FS_UFS2_MAGIC) {
cylno = cbtocylno(fs, bno);
cg_blks(fs, cgp, cylno)[cbtorpos(fs, bno)]--;
cg_blktot(cgp)[cylno]--;
}
fs->fs_fmod = 1;
blkno = cgbase(fs, cgp->cg_cgx) + bno;
if (DOINGSOFTDEP(ITOV(ip)))
softdep_setup_blkmapdep(bp, fs, blkno);
return (blkno);
}
/* inode allocation routine */
daddr_t
ffs_nodealloccg(struct inode *ip, u_int cg, daddr_t ipref, int mode)
{
struct fs *fs;
struct cg *cgp;
struct buf *bp;
struct timespec now;
int start, len, loc, map, i;
#ifdef FFS2
struct buf *ibp = NULL;
struct ufs2_dinode *dp2;
#endif
/*
* For efficiency, before looking at the bitmaps for free inodes,
* check the counters kept in the superblock cylinder group summaries,
* and in the cylinder group itself.
*/
fs = ip->i_fs;
if (fs->fs_cs(fs, cg).cs_nifree == 0)
return (0);
if (!(bp = ffs_cgread(fs, ip, cg)))
return (0);
cgp = (struct cg *)bp->b_data;
if (cgp->cg_cs.cs_nifree == 0) {
brelse(bp);
return (0);
}
/*
* We are committed to the allocation from now on, so update the time
* on the cylinder group.
*/
nanotime(&now);
cgp->cg_ffs2_time = now.tv_sec;
cgp->cg_time = now.tv_sec;
/*
* If there was a preferred location for the new inode, try to find it.
*/
if (ipref) {
ipref %= fs->fs_ipg;
if (isclr(cg_inosused(cgp), ipref))
goto gotit; /* inode is free, grab it. */
}
/*
* Otherwise, look for the next available inode, starting at cg_irotor
* (the position in the bitmap of the last used inode).
*/
start = cgp->cg_irotor / NBBY;
len = howmany(fs->fs_ipg - cgp->cg_irotor, NBBY);
loc = skpc(0xff, len, &cg_inosused(cgp)[start]);
if (loc == 0) {
/*
* If we didn't find a free inode in the upper part of the
* bitmap (from cg_irotor to the end), then look at the bottom
* part (from 0 to cg_irotor).
*/
len = start + 1;
start = 0;
loc = skpc(0xff, len, &cg_inosused(cgp)[0]);
if (loc == 0) {
/*
* If we failed again, then either the bitmap or the
* counters kept for the cylinder group are wrong.
*/
printf("cg = %d, irotor = %d, fs = %s\n",
cg, cgp->cg_irotor, fs->fs_fsmnt);
panic("ffs_nodealloccg: map corrupted");
/* NOTREACHED */
}
}
/* skpc() returns the position relative to the end */
i = start + len - loc;
/*
* Okay, so now in 'i' we have the location in the bitmap of a byte
* holding a free inode. Find the corresponding bit and set it,
* updating cg_irotor as well, accordingly.
*/
map = cg_inosused(cgp)[i];
ipref = i * NBBY;
for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) {
if ((map & i) == 0) {
cgp->cg_irotor = ipref;
goto gotit;
}
}
printf("fs = %s\n", fs->fs_fsmnt);
panic("ffs_nodealloccg: block not in map");
/* NOTREACHED */
gotit:
#ifdef FFS2
/*
* For FFS2, check if all inodes in this cylinder group have been used
* at least once. If they haven't, and we are allocating an inode past
* the last allocated block of inodes, read in a block and initialize
* all inodes in it.
*/
if (fs->fs_magic == FS_UFS2_MAGIC &&
/* Inode is beyond last initialized block of inodes? */
ipref + INOPB(fs) > cgp->cg_initediblk &&
/* Has any inode not been used at least once? */
cgp->cg_initediblk < cgp->cg_ffs2_niblk) {
ibp = getblk(ip->i_devvp, fsbtodb(fs,
ino_to_fsba(fs, cg * fs->fs_ipg + cgp->cg_initediblk)),
(int)fs->fs_bsize, 0, INFSLP);
memset(ibp->b_data, 0, fs->fs_bsize);
dp2 = (struct ufs2_dinode *)(ibp->b_data);
/* Give each inode a generation number */
for (i = 0; i < INOPB(fs); i++) {
while (dp2->di_gen == 0)
dp2->di_gen = arc4random();
dp2++;
}
/* Update the counter of initialized inodes */
cgp->cg_initediblk += INOPB(fs);
}
#endif /* FFS2 */
if (DOINGSOFTDEP(ITOV(ip)))
softdep_setup_inomapdep(bp, ip, cg * fs->fs_ipg + ipref);
setbit(cg_inosused(cgp), ipref);
/* Update the counters we keep on free inodes */
cgp->cg_cs.cs_nifree--;
fs->fs_cstotal.cs_nifree--;
fs->fs_cs(fs, cg).cs_nifree--;
fs->fs_fmod = 1; /* file system was modified */
/* Update the counters we keep on allocated directories */
if ((mode & IFMT) == IFDIR) {
cgp->cg_cs.cs_ndir++;
fs->fs_cstotal.cs_ndir++;
fs->fs_cs(fs, cg).cs_ndir++;
}
bdwrite(bp);
#ifdef FFS2
if (ibp != NULL)
bawrite(ibp);
#endif
/* Return the allocated inode number */
return (cg * fs->fs_ipg + ipref);
}
/*
* Free a block or fragment.
*
* The specified block or fragment is placed back in the
* free map. If a fragment is deallocated, a possible
* block reassembly is checked.
*/
void
ffs_blkfree(struct inode *ip, daddr_t bno, long size)
{
struct fs *fs;
struct cg *cgp;
struct buf *bp;
struct timespec now;
daddr_t blkno;
int i, cg, blk, frags, bbase;
fs = ip->i_fs;
if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0 ||
fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) {
printf("dev = 0x%x, bsize = %d, size = %ld, fs = %s\n",
ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);
panic("ffs_blkfree: bad size");
}
cg = dtog(fs, bno);
if ((u_int)bno >= fs->fs_size) {
printf("bad block %lld, ino %u\n", (long long)bno,
ip->i_number);
ffs_fserr(fs, DIP(ip, uid), "bad block");
return;
}
if (!(bp = ffs_cgread(fs, ip, cg)))
return;
cgp = (struct cg *)bp->b_data;
nanotime(&now);
cgp->cg_ffs2_time = now.tv_sec;
cgp->cg_time = now.tv_sec;
bno = dtogd(fs, bno);
if (size == fs->fs_bsize) {
blkno = fragstoblks(fs, bno);
if (!ffs_isfreeblock(fs, cg_blksfree(cgp), blkno)) {
printf("dev = 0x%x, block = %lld, fs = %s\n",
ip->i_dev, (long long)bno, fs->fs_fsmnt);
panic("ffs_blkfree: freeing free block");
}
ffs_setblock(fs, cg_blksfree(cgp), blkno);
ffs_clusteracct(fs, cgp, blkno, 1);
cgp->cg_cs.cs_nbfree++;
fs->fs_cstotal.cs_nbfree++;
fs->fs_cs(fs, cg).cs_nbfree++;
if (fs->fs_magic != FS_UFS2_MAGIC) {
i = cbtocylno(fs, bno);
cg_blks(fs, cgp, i)[cbtorpos(fs, bno)]++;
cg_blktot(cgp)[i]++;
}
} else {
bbase = bno - fragnum(fs, bno);
/*
* decrement the counts associated with the old frags
*/
blk = blkmap(fs, cg_blksfree(cgp), bbase);
ffs_fragacct(fs, blk, cgp->cg_frsum, -1);
/*
* deallocate the fragment
*/
frags = numfrags(fs, size);
for (i = 0; i < frags; i++) {
if (isset(cg_blksfree(cgp), bno + i)) {
printf("dev = 0x%x, block = %lld, fs = %s\n",
ip->i_dev, (long long)(bno + i),
fs->fs_fsmnt);
panic("ffs_blkfree: freeing free frag");
}
setbit(cg_blksfree(cgp), bno + i);
}
cgp->cg_cs.cs_nffree += i;
fs->fs_cstotal.cs_nffree += i;
fs->fs_cs(fs, cg).cs_nffree += i;
/*
* add back in counts associated with the new frags
*/
blk = blkmap(fs, cg_blksfree(cgp), bbase);
ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
/*
* if a complete block has been reassembled, account for it
*/
blkno = fragstoblks(fs, bbase);
if (ffs_isblock(fs, cg_blksfree(cgp), blkno)) {
cgp->cg_cs.cs_nffree -= fs->fs_frag;
fs->fs_cstotal.cs_nffree -= fs->fs_frag;
fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
ffs_clusteracct(fs, cgp, blkno, 1);
cgp->cg_cs.cs_nbfree++;
fs->fs_cstotal.cs_nbfree++;
fs->fs_cs(fs, cg).cs_nbfree++;
if (fs->fs_magic != FS_UFS2_MAGIC) {
i = cbtocylno(fs, bbase);
cg_blks(fs, cgp, i)[cbtorpos(fs, bbase)]++;
cg_blktot(cgp)[i]++;
}
}
}
fs->fs_fmod = 1;
bdwrite(bp);
}
int
ffs_inode_free(struct inode *pip, ufsino_t ino, mode_t mode)
{
struct vnode *pvp = ITOV(pip);
if (DOINGSOFTDEP(pvp)) {
softdep_freefile(pvp, ino, mode);
return (0);
}
return (ffs_freefile(pip, ino, mode));
}
/*
* Do the actual free operation.
* The specified inode is placed back in the free map.
*/
int
ffs_freefile(struct inode *pip, ufsino_t ino, mode_t mode)
{
struct fs *fs;
struct cg *cgp;
struct buf *bp;
struct timespec now;
u_int cg;
fs = pip->i_fs;
if (ino >= fs->fs_ipg * fs->fs_ncg)
panic("ffs_freefile: range: dev = 0x%x, ino = %d, fs = %s",
pip->i_dev, ino, fs->fs_fsmnt);
cg = ino_to_cg(fs, ino);
if (!(bp = ffs_cgread(fs, pip, cg)))
return (0);
cgp = (struct cg *)bp->b_data;
nanotime(&now);
cgp->cg_ffs2_time = now.tv_sec;
cgp->cg_time = now.tv_sec;
ino %= fs->fs_ipg;
if (isclr(cg_inosused(cgp), ino)) {
printf("dev = 0x%x, ino = %u, fs = %s\n",
pip->i_dev, ino, fs->fs_fsmnt);
if (fs->fs_ronly == 0)
panic("ffs_freefile: freeing free inode");
}
clrbit(cg_inosused(cgp), ino);
if (ino < cgp->cg_irotor)
cgp->cg_irotor = ino;
cgp->cg_cs.cs_nifree++;
fs->fs_cstotal.cs_nifree++;
fs->fs_cs(fs, cg).cs_nifree++;
if ((mode & IFMT) == IFDIR) {
cgp->cg_cs.cs_ndir--;
fs->fs_cstotal.cs_ndir--;
fs->fs_cs(fs, cg).cs_ndir--;
}
fs->fs_fmod = 1;
bdwrite(bp);
return (0);
}
/*
* Find a block of the specified size in the specified cylinder group.
*
* It is a panic if a request is made to find a block if none are
* available.
*/
daddr_t
ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz)
{
daddr_t bno;
int start, len, loc, i;
int blk, field, subfield, pos;
/*
* find the fragment by searching through the free block
* map for an appropriate bit pattern
*/
if (bpref)
start = dtogd(fs, bpref) / NBBY;
else
start = cgp->cg_frotor / NBBY;
len = howmany(fs->fs_fpg, NBBY) - start;
loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[start],
(u_char *)fragtbl[fs->fs_frag],
(u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
if (loc == 0) {
len = start + 1;
start = 0;
loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[0],
(u_char *)fragtbl[fs->fs_frag],
(u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
if (loc == 0) {
printf("start = %d, len = %d, fs = %s\n",
start, len, fs->fs_fsmnt);
panic("ffs_alloccg: map corrupted");
/* NOTREACHED */
}
}
bno = (start + len - loc) * NBBY;
cgp->cg_frotor = bno;
/*
* found the byte in the map
* sift through the bits to find the selected frag
*/
for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
blk = blkmap(fs, cg_blksfree(cgp), bno);
blk <<= 1;
field = around[allocsiz];
subfield = inside[allocsiz];
for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
if ((blk & field) == subfield)
return (bno + pos);
field <<= 1;
subfield <<= 1;
}
}
printf("bno = %lld, fs = %s\n", (long long)bno, fs->fs_fsmnt);
panic("ffs_alloccg: block not in map");
return (-1);
}
/*
* Update the cluster map because of an allocation or free.
*
* Cnt == 1 means free; cnt == -1 means allocating.
*/
void
ffs_clusteracct(struct fs *fs, struct cg *cgp, daddr_t blkno, int cnt)
{
int32_t *sump;
int32_t *lp;
u_char *freemapp, *mapp;
int i, start, end, forw, back, map, bit;
if (fs->fs_contigsumsize <= 0)
return;
freemapp = cg_clustersfree(cgp);
sump = cg_clustersum(cgp);
/*
* Allocate or clear the actual block.
*/
if (cnt > 0)
setbit(freemapp, blkno);
else
clrbit(freemapp, blkno);
/*
* Find the size of the cluster going forward.
*/
start = blkno + 1;
end = start + fs->fs_contigsumsize;
if (end >= cgp->cg_nclusterblks)
end = cgp->cg_nclusterblks;
mapp = &freemapp[start / NBBY];
map = *mapp++;
bit = 1 << (start % NBBY);
for (i = start; i < end; i++) {
if ((map & bit) == 0)
break;
if ((i & (NBBY - 1)) != (NBBY - 1)) {
bit <<= 1;
} else {
map = *mapp++;
bit = 1;
}
}
forw = i - start;
/*
* Find the size of the cluster going backward.
*/
start = blkno - 1;
end = start - fs->fs_contigsumsize;
if (end < 0)
end = -1;
mapp = &freemapp[start / NBBY];
map = *mapp--;
bit = 1 << (start % NBBY);
for (i = start; i > end; i--) {
if ((map & bit) == 0)
break;
if ((i & (NBBY - 1)) != 0) {
bit >>= 1;
} else {
map = *mapp--;
bit = 1 << (NBBY - 1);
}
}
back = start - i;
/*
* Account for old cluster and the possibly new forward and
* back clusters.
*/
i = back + forw + 1;
if (i > fs->fs_contigsumsize)
i = fs->fs_contigsumsize;
sump[i] += cnt;
if (back > 0)
sump[back] -= cnt;
if (forw > 0)
sump[forw] -= cnt;
/*
* Update cluster summary information.
*/
lp = &sump[fs->fs_contigsumsize];
for (i = fs->fs_contigsumsize; i > 0; i--)
if (*lp-- > 0)
break;
fs->fs_maxcluster[cgp->cg_cgx] = i;
}
1170
3
205
304
202
105
105
105
303
48
280
48
48
31
17
208
200
197
73
50
21
187
4
9
207
970
871
180
168
970
929
927
538
537
60
508
1175
905
473
919
918
208
120
232
32
83
157
133
25
157
133
25
133
25
157
157
52
52
9
157
1
54
122
49
40
82
3
111
87
37
35
37
33
77
87
38
17
17
37
6
6
6
182
181
21
19
21
55
2
2
34
2
1
2
2
9
1
16
1
13
4
4
4
975
829
106
52
892
179
885
6812
6809
2
2
1
7
2
9
174
174
8
1
1
1
185
186
54
167
3
183
45
27
63
63
1
63
186
136
136
129
9
136
68
68
67
14
50
27
228
227
74
204
126
31
121
76
228
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
/* $OpenBSD: vfs_subr.c,v 1.317 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_subr.c,v 1.53 1996/04/22 01:39:13 christos Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94
*/
/*
* External virtual filesystem routines
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/lockf.h>
#include <sys/stat.h>
#include <sys/acct.h>
#include <sys/namei.h>
#include <sys/ucred.h>
#include <sys/buf.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/syscallargs.h>
#include <sys/pool.h>
#include <sys/tree.h>
#include <sys/specdev.h>
#include <sys/atomic.h>
#include <netinet/in.h>
#include <uvm/uvm_extern.h>
#include <uvm/uvm_vnode.h>
#include "softraid.h"
void sr_quiesce(void);
enum vtype iftovt_tab[16] = {
VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
};
int vttoif_tab[9] = {
0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
S_IFSOCK, S_IFIFO, S_IFMT,
};
int prtactive = 0; /* 1 => print out reclaim of active vnodes */
int suid_clear = 1; /* 1 => clear SUID / SGID on owner change */
/*
* Insq/Remq for the vnode usage lists.
*/
#define bufinsvn(bp, dp) LIST_INSERT_HEAD(dp, bp, b_vnbufs)
#define bufremvn(bp) { \
LIST_REMOVE(bp, b_vnbufs); \
LIST_NEXT(bp, b_vnbufs) = NOLIST; \
}
TAILQ_HEAD(freelst, vnode);
struct freelst vnode_hold_list; /* list of vnodes referencing buffers */
struct freelst vnode_free_list; /* vnode free list */
struct mntlist mountlist; /* mounted filesystem list */
void vclean(struct vnode *, int, struct proc *);
void insmntque(struct vnode *, struct mount *);
int getdevvp(dev_t, struct vnode **, enum vtype);
int vfs_hang_addrlist(struct mount *, struct netexport *,
struct export_args *);
int vfs_free_netcred(struct radix_node *, void *, u_int);
void vfs_free_addrlist(struct netexport *);
void vputonfreelist(struct vnode *);
int vflush_vnode(struct vnode *, void *);
int maxvnodes;
struct mutex vnode_mtx = MUTEX_INITIALIZER(IPL_BIO);
void vfs_unmountall(void);
#ifdef DEBUG
void printlockedvnodes(void);
#endif
struct pool vnode_pool;
struct pool uvm_vnode_pool;
static inline int rb_buf_compare(const struct buf *b1, const struct buf *b2);
RBT_GENERATE(buf_rb_bufs, buf, b_rbbufs, rb_buf_compare);
static inline int
rb_buf_compare(const struct buf *b1, const struct buf *b2)
{
if (b1->b_lblkno < b2->b_lblkno)
return(-1);
if (b1->b_lblkno > b2->b_lblkno)
return(1);
return(0);
}
/*
* Initialize the vnode management data structures.
*/
void
vntblinit(void)
{
/* buffer cache may need a vnode for each buffer */
maxvnodes = 2 * initialvnodes;
pool_init(&vnode_pool, sizeof(struct vnode), 0, IPL_NONE,
PR_WAITOK, "vnodes", NULL);
pool_init(&uvm_vnode_pool, sizeof(struct uvm_vnode), 0, IPL_NONE,
PR_WAITOK, "uvmvnodes", NULL);
TAILQ_INIT(&vnode_hold_list);
TAILQ_INIT(&vnode_free_list);
TAILQ_INIT(&mountlist);
/*
* Initialize the filesystem syncer.
*/
vn_initialize_syncerd();
#ifdef NFSSERVER
rn_init(sizeof(struct sockaddr_in));
#endif /* NFSSERVER */
}
/*
* Allocate a mount point.
*
* The returned mount point is marked as busy.
*/
struct mount *
vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp)
{
struct mount *mp;
mp = malloc(sizeof(*mp), M_MOUNT, M_WAITOK|M_ZERO);
rw_init_flags(&mp->mnt_lock, "vfslock", RWL_IS_VNODE);
(void)vfs_busy(mp, VB_READ|VB_NOWAIT);
TAILQ_INIT(&mp->mnt_vnodelist);
mp->mnt_vnodecovered = vp;
atomic_inc_int(&vfsp->vfc_refcount);
mp->mnt_vfc = vfsp;
mp->mnt_op = vfsp->vfc_vfsops;
mp->mnt_flag = vfsp->vfc_flags;
strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
return (mp);
}
/*
* Release a mount point.
*/
void
vfs_mount_free(struct mount *mp)
{
atomic_dec_int(&mp->mnt_vfc->vfc_refcount);
free(mp, M_MOUNT, sizeof(*mp));
}
/*
* Mark a mount point as busy. Used to synchronize access and to delay
* unmounting.
*
* Default behaviour is to attempt getting a READ lock and in case of an
* ongoing unmount, to wait for it to finish and then return failure.
*/
int
vfs_busy(struct mount *mp, int flags)
{
int rwflags = 0;
if (flags & VB_WRITE)
rwflags |= RW_WRITE;
else
rwflags |= RW_READ;
if (flags & VB_WAIT)
rwflags |= RW_SLEEPFAIL;
else
rwflags |= RW_NOSLEEP;
#ifdef WITNESS
if (flags & VB_DUPOK)
rwflags |= RW_DUPOK;
#endif
if (rw_enter(&mp->mnt_lock, rwflags))
return (EBUSY);
return (0);
}
/*
* Free a busy file system
*/
void
vfs_unbusy(struct mount *mp)
{
rw_exit(&mp->mnt_lock);
}
int
vfs_isbusy(struct mount *mp)
{
if (RWLOCK_OWNER(&mp->mnt_lock) > 0)
return (1);
else
return (0);
}
/*
* Lookup a filesystem type, and if found allocate and initialize
* a mount structure for it.
*
* Devname is usually updated by mount(8) after booting.
*/
int
vfs_rootmountalloc(char *fstypename, char *devname, struct mount **mpp)
{
struct vfsconf *vfsp;
struct mount *mp;
vfsp = vfs_byname(fstypename);
if (vfsp == NULL)
return (ENODEV);
mp = vfs_mount_alloc(NULLVP, vfsp);
mp->mnt_flag |= MNT_RDONLY;
mp->mnt_stat.f_mntonname[0] = '/';
copystr(devname, mp->mnt_stat.f_mntfromname, MNAMELEN, NULL);
copystr(devname, mp->mnt_stat.f_mntfromspec, MNAMELEN, NULL);
*mpp = mp;
return (0);
}
/*
* Lookup a mount point by filesystem identifier.
*/
struct mount *
vfs_getvfs(fsid_t *fsid)
{
struct mount *mp;
TAILQ_FOREACH(mp, &mountlist, mnt_list) {
if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] &&
mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) {
return (mp);
}
}
return (NULL);
}
/*
* Get a new unique fsid
*/
void
vfs_getnewfsid(struct mount *mp)
{
static u_short xxxfs_mntid;
fsid_t tfsid;
int mtype;
mtype = mp->mnt_vfc->vfc_typenum;
mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev + mtype, 0);
mp->mnt_stat.f_fsid.val[1] = mtype;
if (xxxfs_mntid == 0)
++xxxfs_mntid;
tfsid.val[0] = makedev(nblkdev + mtype, xxxfs_mntid);
tfsid.val[1] = mtype;
if (!TAILQ_EMPTY(&mountlist)) {
while (vfs_getvfs(&tfsid)) {
tfsid.val[0]++;
xxxfs_mntid++;
}
}
mp->mnt_stat.f_fsid.val[0] = tfsid.val[0];
}
/*
* Set vnode attributes to VNOVAL
*/
void
vattr_null(struct vattr *vap)
{
vap->va_type = VNON;
/*
* Don't get fancy: u_quad_t = u_int = VNOVAL leaves the u_quad_t
* with 2^31-1 instead of 2^64-1. Just write'm out and let
* the compiler do its job.
*/
vap->va_mode = VNOVAL;
vap->va_nlink = VNOVAL;
vap->va_uid = VNOVAL;
vap->va_gid = VNOVAL;
vap->va_fsid = VNOVAL;
vap->va_fileid = VNOVAL;
vap->va_size = VNOVAL;
vap->va_blocksize = VNOVAL;
vap->va_atime.tv_sec = VNOVAL;
vap->va_atime.tv_nsec = VNOVAL;
vap->va_mtime.tv_sec = VNOVAL;
vap->va_mtime.tv_nsec = VNOVAL;
vap->va_ctime.tv_sec = VNOVAL;
vap->va_ctime.tv_nsec = VNOVAL;
vap->va_gen = VNOVAL;
vap->va_flags = VNOVAL;
vap->va_rdev = VNOVAL;
vap->va_bytes = VNOVAL;
vap->va_filerev = VNOVAL;
vap->va_vaflags = 0;
}
/*
* Routines having to do with the management of the vnode table.
*/
long numvnodes;
/*
* Return the next vnode from the free list.
*/
int
getnewvnode(enum vtagtype tag, struct mount *mp, const struct vops *vops,
struct vnode **vpp)
{
struct proc *p = curproc;
struct freelst *listhd;
static int toggle;
struct vnode *vp;
int s;
/*
* allow maxvnodes to increase if the buffer cache itself
* is big enough to justify it. (we don't shrink it ever)
*/
maxvnodes = maxvnodes < bcstats.numbufs ? bcstats.numbufs
: maxvnodes;
/*
* We must choose whether to allocate a new vnode or recycle an
* existing one. The criterion for allocating a new one is that
* the total number of vnodes is less than the number desired or
* there are no vnodes on either free list. Generally we only
* want to recycle vnodes that have no buffers associated with
* them, so we look first on the vnode_free_list. If it is empty,
* we next consider vnodes with referencing buffers on the
* vnode_hold_list. The toggle ensures that half the time we
* will use a buffer from the vnode_hold_list, and half the time
* we will allocate a new one unless the list has grown to twice
* the desired size. We are reticent to recycle vnodes from the
* vnode_hold_list because we will lose the identity of all its
* referencing buffers.
*/
toggle ^= 1;
if (numvnodes / 2 > maxvnodes)
toggle = 0;
s = splbio();
if ((numvnodes < maxvnodes) ||
((TAILQ_FIRST(listhd = &vnode_free_list) == NULL) &&
((TAILQ_FIRST(listhd = &vnode_hold_list) == NULL) || toggle))) {
splx(s);
vp = pool_get(&vnode_pool, PR_WAITOK | PR_ZERO);
vp->v_uvm = pool_get(&uvm_vnode_pool, PR_WAITOK | PR_ZERO);
vp->v_uvm->u_vnode = vp;
uvm_obj_init(&vp->v_uvm->u_obj, &uvm_vnodeops, 0);
RBT_INIT(buf_rb_bufs, &vp->v_bufs_tree);
cache_tree_init(&vp->v_nc_tree);
TAILQ_INIT(&vp->v_cache_dst);
numvnodes++;
} else {
TAILQ_FOREACH(vp, listhd, v_freelist) {
if (VOP_ISLOCKED(vp) == 0)
break;
}
/*
* Unless this is a bad time of the month, at most
* the first NCPUS items on the free list are
* locked, so this is close enough to being empty.
*/
if (vp == NULL) {
splx(s);
tablefull("vnode");
*vpp = NULL;
return (ENFILE);
}
#ifdef DIAGNOSTIC
if (vp->v_usecount) {
vprint("free vnode", vp);
panic("free vnode isn't");
}
#endif
TAILQ_REMOVE(listhd, vp, v_freelist);
vp->v_bioflag &= ~VBIOONFREELIST;
splx(s);
if (vp->v_type != VBAD)
vgonel(vp, p);
#ifdef DIAGNOSTIC
if (vp->v_data) {
vprint("cleaned vnode", vp);
panic("cleaned vnode isn't");
}
s = splbio();
if (vp->v_numoutput)
panic("Clean vnode has pending I/O's");
splx(s);
#endif
vp->v_flag = 0;
vp->v_socket = NULL;
}
cache_purge(vp);
vp->v_type = VNON;
vp->v_tag = tag;
vp->v_op = vops;
insmntque(vp, mp);
*vpp = vp;
vp->v_usecount = 1;
vp->v_data = NULL;
return (0);
}
/*
* Move a vnode from one mount queue to another.
*/
void
insmntque(struct vnode *vp, struct mount *mp)
{
/*
* Delete from old mount point vnode list, if on one.
*/
if (vp->v_mount != NULL)
TAILQ_REMOVE(&vp->v_mount->mnt_vnodelist, vp, v_mntvnodes);
/*
* Insert into list of vnodes for the new mount point, if available.
*/
if ((vp->v_mount = mp) != NULL)
TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
}
/*
* Create a vnode for a block device.
* Used for root filesystem, argdev, and swap areas.
* Also used for memory file system special devices.
*/
int
bdevvp(dev_t dev, struct vnode **vpp)
{
return (getdevvp(dev, vpp, VBLK));
}
/*
* Create a vnode for a character device.
* Used for console handling.
*/
int
cdevvp(dev_t dev, struct vnode **vpp)
{
return (getdevvp(dev, vpp, VCHR));
}
/*
* Create a vnode for a device.
* Used by bdevvp (block device) for root file system etc.,
* and by cdevvp (character device) for console.
*/
int
getdevvp(dev_t dev, struct vnode **vpp, enum vtype type)
{
struct vnode *vp;
struct vnode *nvp;
int error;
if (dev == NODEV) {
*vpp = NULLVP;
return (0);
}
error = getnewvnode(VT_NON, NULL, &spec_vops, &nvp);
if (error) {
*vpp = NULLVP;
return (error);
}
vp = nvp;
vp->v_type = type;
if ((nvp = checkalias(vp, dev, NULL)) != NULL) {
vput(vp);
vp = nvp;
}
if (vp->v_type == VCHR && cdevsw[major(vp->v_rdev)].d_type == D_TTY)
vp->v_flag |= VISTTY;
*vpp = vp;
return (0);
}
/*
* Check to see if the new vnode represents a special device
* for which we already have a vnode (either because of
* bdevvp() or because of a different vnode representing
* the same block device). If such an alias exists, deallocate
* the existing contents and return the aliased vnode. The
* caller is responsible for filling it with its new contents.
*/
struct vnode *
checkalias(struct vnode *nvp, dev_t nvp_rdev, struct mount *mp)
{
struct proc *p = curproc;
struct vnode *vp;
struct vnodechain *vchain;
if (nvp->v_type != VBLK && nvp->v_type != VCHR)
return (NULLVP);
vchain = &speclisth[SPECHASH(nvp_rdev)];
loop:
SLIST_FOREACH(vp, vchain, v_specnext) {
if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) {
continue;
}
/*
* Alias, but not in use, so flush it out.
*/
if (vp->v_usecount == 0) {
vgonel(vp, p);
goto loop;
}
if (vget(vp, LK_EXCLUSIVE)) {
goto loop;
}
break;
}
/*
* Common case is actually in the if statement
*/
if (vp == NULL || !(vp->v_tag == VT_NON && vp->v_type == VBLK)) {
nvp->v_specinfo = malloc(sizeof(struct specinfo), M_VNODE,
M_WAITOK);
nvp->v_rdev = nvp_rdev;
nvp->v_hashchain = vchain;
nvp->v_specmountpoint = NULL;
nvp->v_speclockf = NULL;
nvp->v_specbitmap = NULL;
if (nvp->v_type == VCHR &&
(cdevsw[major(nvp_rdev)].d_flags & D_CLONE) &&
(minor(nvp_rdev) >> CLONE_SHIFT == 0)) {
if (vp != NULLVP)
nvp->v_specbitmap = vp->v_specbitmap;
else
nvp->v_specbitmap = malloc(CLONE_MAPSZ,
M_VNODE, M_WAITOK | M_ZERO);
}
SLIST_INSERT_HEAD(vchain, nvp, v_specnext);
if (vp != NULLVP) {
nvp->v_flag |= VALIASED;
vp->v_flag |= VALIASED;
vput(vp);
}
return (NULLVP);
}
/*
* This code is the uncommon case. It is called in case
* we found an alias that was VT_NON && vtype of VBLK
* This means we found a block device that was created
* using bdevvp.
* An example of such a vnode is the root partition device vnode
* created in ffs_mountroot.
*
* The vnodes created by bdevvp should not be aliased (why?).
*/
VOP_UNLOCK(vp);
vclean(vp, 0, p);
vp->v_op = nvp->v_op;
vp->v_tag = nvp->v_tag;
nvp->v_type = VNON;
insmntque(vp, mp);
return (vp);
}
/*
* Grab a particular vnode from the free list, increment its
* reference count and lock it. If the vnode lock bit is set,
* the vnode is being eliminated in vgone. In that case, we
* cannot grab it, so the process is awakened when the
* transition is completed, and an error code is returned to
* indicate that the vnode is no longer usable, possibly
* having been changed to a new file system type.
*/
int
vget(struct vnode *vp, int flags)
{
int error, s, onfreelist;
/*
* If the vnode is in the process of being cleaned out for
* another use, we wait for the cleaning to finish and then
* return failure. Cleaning is determined by checking that
* the VXLOCK flag is set.
*/
mtx_enter(&vnode_mtx);
if (vp->v_lflag & VXLOCK) {
if (flags & LK_NOWAIT) {
mtx_leave(&vnode_mtx);
return (EBUSY);
}
vp->v_lflag |= VXWANT;
msleep_nsec(vp, &vnode_mtx, PINOD, "vget", INFSLP);
mtx_leave(&vnode_mtx);
return (ENOENT);
}
mtx_leave(&vnode_mtx);
s = splbio();
onfreelist = vp->v_bioflag & VBIOONFREELIST;
if (vp->v_usecount == 0 && onfreelist) {
if (vp->v_holdcnt > 0)
TAILQ_REMOVE(&vnode_hold_list, vp, v_freelist);
else
TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
vp->v_bioflag &= ~VBIOONFREELIST;
}
splx(s);
vp->v_usecount++;
if (flags & LK_TYPE_MASK) {
if ((error = vn_lock(vp, flags)) != 0) {
vp->v_usecount--;
if (vp->v_usecount == 0 && onfreelist)
vputonfreelist(vp);
}
return (error);
}
return (0);
}
/* Vnode reference. */
void
vref(struct vnode *vp)
{
KERNEL_ASSERT_LOCKED();
#ifdef DIAGNOSTIC
if (vp->v_usecount == 0)
panic("vref used where vget required");
if (vp->v_type == VNON)
panic("vref on a VNON vnode");
#endif
vp->v_usecount++;
}
void
vputonfreelist(struct vnode *vp)
{
int s;
struct freelst *lst;
s = splbio();
#ifdef DIAGNOSTIC
if (vp->v_usecount != 0)
panic("Use count is not zero!");
/*
* If the hold count is still positive, one or many threads could still
* be waiting on the vnode lock inside uvn_io().
*/
if (vp->v_holdcnt == 0 && vp->v_lockcount != 0)
panic("%s: lock count is not zero", __func__);
if (vp->v_bioflag & VBIOONFREELIST) {
vprint("vnode already on free list: ", vp);
panic("vnode already on free list");
}
#endif
vp->v_bioflag |= VBIOONFREELIST;
vp->v_bioflag &= ~VBIOERROR;
if (vp->v_holdcnt > 0)
lst = &vnode_hold_list;
else
lst = &vnode_free_list;
if (vp->v_type == VBAD)
TAILQ_INSERT_HEAD(lst, vp, v_freelist);
else
TAILQ_INSERT_TAIL(lst, vp, v_freelist);
splx(s);
}
/*
* vput(), just unlock and vrele()
*/
void
vput(struct vnode *vp)
{
struct proc *p = curproc;
int s;
#ifdef DIAGNOSTIC
if (vp == NULL)
panic("vput: null vp");
#endif
#ifdef DIAGNOSTIC
if (vp->v_usecount == 0) {
vprint("vput: bad ref count", vp);
panic("vput: ref cnt");
}
#endif
vp->v_usecount--;
KASSERT(vp->v_usecount > 0 || vp->v_uvcount == 0);
if (vp->v_usecount > 0) {
VOP_UNLOCK(vp);
return;
}
#ifdef DIAGNOSTIC
if (vp->v_writecount != 0) {
vprint("vput: bad writecount", vp);
panic("vput: v_writecount != 0");
}
#endif
VOP_INACTIVE(vp, p);
s = splbio();
if (vp->v_usecount == 0 && !(vp->v_bioflag & VBIOONFREELIST))
vputonfreelist(vp);
splx(s);
}
/*
* Vnode release - use for active VNODES.
* If count drops to zero, call inactive routine and return to freelist.
* Returns 0 if it did not sleep.
*/
int
vrele(struct vnode *vp)
{
struct proc *p = curproc;
int s;
#ifdef DIAGNOSTIC
if (vp == NULL)
panic("vrele: null vp");
#endif
#ifdef DIAGNOSTIC
if (vp->v_usecount == 0) {
vprint("vrele: bad ref count", vp);
panic("vrele: ref cnt");
}
#endif
vp->v_usecount--;
if (vp->v_usecount > 0) {
return (0);
}
#ifdef DIAGNOSTIC
if (vp->v_writecount != 0) {
vprint("vrele: bad writecount", vp);
panic("vrele: v_writecount != 0");
}
#endif
if (vn_lock(vp, LK_EXCLUSIVE)) {
#ifdef DIAGNOSTIC
vprint("vrele: cannot lock", vp);
#endif
return (1);
}
VOP_INACTIVE(vp, p);
s = splbio();
if (vp->v_usecount == 0 && !(vp->v_bioflag & VBIOONFREELIST))
vputonfreelist(vp);
splx(s);
return (1);
}
/* Page or buffer structure gets a reference. */
void
vhold(struct vnode *vp)
{
int s;
s = splbio();
/*
* If it is on the freelist and the hold count is currently
* zero, move it to the hold list.
*/
if ((vp->v_bioflag & VBIOONFREELIST) &&
vp->v_holdcnt == 0 && vp->v_usecount == 0) {
TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
TAILQ_INSERT_TAIL(&vnode_hold_list, vp, v_freelist);
}
vp->v_holdcnt++;
splx(s);
}
/* Lose interest in a vnode. */
void
vdrop(struct vnode *vp)
{
int s;
s = splbio();
#ifdef DIAGNOSTIC
if (vp->v_holdcnt == 0)
panic("vdrop: zero holdcnt");
#endif
vp->v_holdcnt--;
/*
* If it is on the holdlist and the hold count drops to
* zero, move it to the free list.
*/
if ((vp->v_bioflag & VBIOONFREELIST) &&
vp->v_holdcnt == 0 && vp->v_usecount == 0) {
TAILQ_REMOVE(&vnode_hold_list, vp, v_freelist);
TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist);
}
splx(s);
}
/*
* Remove any vnodes in the vnode table belonging to mount point mp.
*
* If MNT_NOFORCE is specified, there should not be any active ones,
* return error if any are found (nb: this is a user error, not a
* system error). If MNT_FORCE is specified, detach any active vnodes
* that are found.
*/
#ifdef DEBUG_SYSCTL
int busyprt = 0; /* print out busy vnodes */
struct ctldebug debug_vfs_busyprt = { "vfs_busyprt", &busyprt };
#endif
int
vfs_mount_foreach_vnode(struct mount *mp,
int (*func)(struct vnode *, void *), void *arg) {
struct vnode *vp, *nvp;
int error = 0;
loop:
TAILQ_FOREACH_SAFE(vp , &mp->mnt_vnodelist, v_mntvnodes, nvp) {
if (vp->v_mount != mp)
goto loop;
error = func(vp, arg);
if (error != 0)
break;
}
return (error);
}
struct vflush_args {
struct vnode *skipvp;
int busy;
int flags;
};
int
vflush_vnode(struct vnode *vp, void *arg)
{
struct vflush_args *va = arg;
struct proc *p = curproc;
int empty, s;
if (vp == va->skipvp) {
return (0);
}
if ((va->flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM)) {
return (0);
}
/*
* If WRITECLOSE is set, only flush out regular file
* vnodes open for writing.
*/
if ((va->flags & WRITECLOSE) &&
(vp->v_writecount == 0 || vp->v_type != VREG)) {
return (0);
}
/*
* With v_usecount == 0, all we need to do is clear
* out the vnode data structures and we are done.
*/
if (vp->v_usecount == 0) {
vgonel(vp, p);
return (0);
}
/*
* If FORCECLOSE is set, forcibly close the vnode.
* For block or character devices, revert to an
* anonymous device. For all other files, just kill them.
*/
if (va->flags & FORCECLOSE) {
if (vp->v_type != VBLK && vp->v_type != VCHR) {
vgonel(vp, p);
} else {
vclean(vp, 0, p);
vp->v_op = &spec_vops;
insmntque(vp, NULL);
}
return (0);
}
/*
* If set, this is allowed to ignore vnodes which don't
* have changes pending to disk.
* XXX Might be nice to check per-fs "inode" flags, but
* generally the filesystem is sync'd already, right?
*/
s = splbio();
empty = (va->flags & IGNORECLEAN) && LIST_EMPTY(&vp->v_dirtyblkhd);
splx(s);
if (empty)
return (0);
#ifdef DEBUG_SYSCTL
if (busyprt)
vprint("vflush: busy vnode", vp);
#endif
va->busy++;
return (0);
}
int
vflush(struct mount *mp, struct vnode *skipvp, int flags)
{
struct vflush_args va;
va.skipvp = skipvp;
va.busy = 0;
va.flags = flags;
vfs_mount_foreach_vnode(mp, vflush_vnode, &va);
if (va.busy)
return (EBUSY);
return (0);
}
/*
* Disassociate the underlying file system from a vnode.
*/
void
vclean(struct vnode *vp, int flags, struct proc *p)
{
int active, do_wakeup = 0;
int s;
/*
* Check to see if the vnode is in use.
* If so we have to reference it before we clean it out
* so that its count cannot fall to zero and generate a
* race against ourselves to recycle it.
*/
if ((active = vp->v_usecount) != 0)
vp->v_usecount++;
/*
* Prevent the vnode from being recycled or
* brought into use while we clean it out.
*/
mtx_enter(&vnode_mtx);
if (vp->v_lflag & VXLOCK)
panic("vclean: deadlock");
vp->v_lflag |= VXLOCK;
if (vp->v_lockcount > 0) {
/*
* Ensure that any thread currently waiting on the same lock has
* observed that the vnode is about to be exclusively locked
* before continuing.
*/
msleep_nsec(&vp->v_lockcount, &vnode_mtx, PINOD, "vop_lock",
INFSLP);
KASSERT(vp->v_lockcount == 0);
}
mtx_leave(&vnode_mtx);
/*
* Even if the count is zero, the VOP_INACTIVE routine may still
* have the object locked while it cleans it out. The VOP_LOCK
* ensures that the VOP_INACTIVE routine is done with its work.
* For active vnodes, it ensures that no other activity can
* occur while the underlying object is being cleaned out.
*/
VOP_LOCK(vp, LK_EXCLUSIVE | LK_DRAIN);
/*
* Clean out any VM data associated with the vnode.
*/
uvm_vnp_terminate(vp);
/*
* Clean out any buffers associated with the vnode.
*/
if (flags & DOCLOSE)
vinvalbuf(vp, V_SAVE, NOCRED, p, 0, INFSLP);
/*
* If purging an active vnode, it must be closed and
* deactivated before being reclaimed. Note that the
* VOP_INACTIVE will unlock the vnode
*/
if (active) {
if (flags & DOCLOSE)
VOP_CLOSE(vp, FNONBLOCK, NOCRED, p);
VOP_INACTIVE(vp, p);
} else {
/*
* Any other processes trying to obtain this lock must first
* wait for VXLOCK to clear, then call the new lock operation.
*/
VOP_UNLOCK(vp);
}
/*
* Reclaim the vnode.
*/
if (VOP_RECLAIM(vp, p))
panic("vclean: cannot reclaim");
if (active) {
vp->v_usecount--;
if (vp->v_usecount == 0) {
s = splbio();
if (vp->v_holdcnt > 0)
panic("vclean: not clean");
vputonfreelist(vp);
splx(s);
}
}
cache_purge(vp);
/*
* Done with purge, notify sleepers of the grim news.
*/
vp->v_op = &dead_vops;
VN_KNOTE(vp, NOTE_REVOKE);
vp->v_tag = VT_NON;
#ifdef VFSLCKDEBUG
vp->v_flag &= ~VLOCKSWORK;
#endif
mtx_enter(&vnode_mtx);
vp->v_lflag &= ~VXLOCK;
if (vp->v_lflag & VXWANT) {
vp->v_lflag &= ~VXWANT;
do_wakeup = 1;
}
mtx_leave(&vnode_mtx);
if (do_wakeup)
wakeup(vp);
}
/*
* Recycle an unused vnode to the front of the free list.
*/
int
vrecycle(struct vnode *vp, struct proc *p)
{
if (vp->v_usecount == 0) {
vgonel(vp, p);
return (1);
}
return (0);
}
/*
* Eliminate all activity associated with a vnode
* in preparation for reuse.
*/
void
vgone(struct vnode *vp)
{
struct proc *p = curproc;
vgonel(vp, p);
}
/*
* vgone, with struct proc.
*/
void
vgonel(struct vnode *vp, struct proc *p)
{
struct vnode *vq;
struct vnode *vx;
int s;
KASSERT(vp->v_uvcount == 0);
/*
* If a vgone (or vclean) is already in progress,
* wait until it is done and return.
*/
mtx_enter(&vnode_mtx);
if (vp->v_lflag & VXLOCK) {
vp->v_lflag |= VXWANT;
msleep_nsec(vp, &vnode_mtx, PINOD, "vgone", INFSLP);
mtx_leave(&vnode_mtx);
return;
}
mtx_leave(&vnode_mtx);
/*
* Clean out the filesystem specific data.
*/
vclean(vp, DOCLOSE, p);
/*
* Delete from old mount point vnode list, if on one.
*/
if (vp->v_mount != NULL)
insmntque(vp, NULL);
/*
* If special device, remove it from special device alias list
* if it is on one.
*/
if ((vp->v_type == VBLK || vp->v_type == VCHR) &&
vp->v_specinfo != NULL) {
if ((vp->v_flag & VALIASED) == 0 && vp->v_type == VCHR &&
(cdevsw[major(vp->v_rdev)].d_flags & D_CLONE) &&
(minor(vp->v_rdev) >> CLONE_SHIFT == 0)) {
free(vp->v_specbitmap, M_VNODE, CLONE_MAPSZ);
}
SLIST_REMOVE(vp->v_hashchain, vp, vnode, v_specnext);
if (vp->v_flag & VALIASED) {
vx = NULL;
SLIST_FOREACH(vq, vp->v_hashchain, v_specnext) {
if (vq->v_rdev != vp->v_rdev ||
vq->v_type != vp->v_type)
continue;
if (vx)
break;
vx = vq;
}
if (vx == NULL)
panic("missing alias");
if (vq == NULL)
vx->v_flag &= ~VALIASED;
vp->v_flag &= ~VALIASED;
}
lf_purgelocks(&vp->v_speclockf);
free(vp->v_specinfo, M_VNODE, sizeof(struct specinfo));
vp->v_specinfo = NULL;
}
/*
* If it is on the freelist and not already at the head,
* move it to the head of the list.
*/
vp->v_type = VBAD;
/*
* Move onto the free list, unless we were called from
* getnewvnode and we're not on any free list
*/
s = splbio();
if (vp->v_usecount == 0 &&
(vp->v_bioflag & VBIOONFREELIST)) {
if (vp->v_holdcnt > 0)
panic("vgonel: not clean");
if (TAILQ_FIRST(&vnode_free_list) != vp) {
TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);
TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist);
}
}
splx(s);
}
/*
* Lookup a vnode by device number.
*/
int
vfinddev(dev_t dev, enum vtype type, struct vnode **vpp)
{
struct vnode *vp;
int rc =0;
SLIST_FOREACH(vp, &speclisth[SPECHASH(dev)], v_specnext) {
if (dev != vp->v_rdev || type != vp->v_type)
continue;
*vpp = vp;
rc = 1;
break;
}
return (rc);
}
/*
* Revoke all the vnodes corresponding to the specified minor number
* range (endpoints inclusive) of the specified major.
*/
void
vdevgone(int maj, int minl, int minh, enum vtype type)
{
struct vnode *vp;
int mn;
for (mn = minl; mn <= minh; mn++)
if (vfinddev(makedev(maj, mn), type, &vp))
VOP_REVOKE(vp, REVOKEALL);
}
/*
* Calculate the total number of references to a special device.
*/
int
vcount(struct vnode *vp)
{
struct vnode *vq;
int count;
loop:
if ((vp->v_flag & VALIASED) == 0)
return (vp->v_usecount);
count = 0;
SLIST_FOREACH(vq, vp->v_hashchain, v_specnext) {
if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type)
continue;
/*
* Alias, but not in use, so flush it out.
*/
if (vq->v_usecount == 0 && vq != vp) {
vgone(vq);
goto loop;
}
count += vq->v_usecount;
}
return (count);
}
#if defined(DEBUG) || defined(DIAGNOSTIC)
/*
* Print out a description of a vnode.
*/
static char *typename[] =
{ "VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD" };
void
vprint(char *label, struct vnode *vp)
{
char buf[64];
if (label != NULL)
printf("%s: ", label);
printf("%p, type %s, use %u, write %u, hold %u,",
vp, typename[vp->v_type], vp->v_usecount, vp->v_writecount,
vp->v_holdcnt);
buf[0] = '\0';
if (vp->v_flag & VROOT)
strlcat(buf, "|VROOT", sizeof buf);
if (vp->v_flag & VTEXT)
strlcat(buf, "|VTEXT", sizeof buf);
if (vp->v_flag & VSYSTEM)
strlcat(buf, "|VSYSTEM", sizeof buf);
if (vp->v_lflag & VXLOCK)
strlcat(buf, "|VXLOCK", sizeof buf);
if (vp->v_lflag & VXWANT)
strlcat(buf, "|VXWANT", sizeof buf);
if (vp->v_bioflag & VBIOWAIT)
strlcat(buf, "|VBIOWAIT", sizeof buf);
if (vp->v_bioflag & VBIOONFREELIST)
strlcat(buf, "|VBIOONFREELIST", sizeof buf);
if (vp->v_bioflag & VBIOONSYNCLIST)
strlcat(buf, "|VBIOONSYNCLIST", sizeof buf);
if (vp->v_flag & VALIASED)
strlcat(buf, "|VALIASED", sizeof buf);
if (buf[0] != '\0')
printf(" flags (%s)", &buf[1]);
if (vp->v_data == NULL) {
printf("\n");
} else {
printf("\n\t");
VOP_PRINT(vp);
}
}
#endif /* DEBUG || DIAGNOSTIC */
#ifdef DEBUG
/*
* List all of the locked vnodes in the system.
* Called when debugging the kernel.
*/
void
printlockedvnodes(void)
{
struct mount *mp;
struct vnode *vp;
printf("Locked vnodes\n");
TAILQ_FOREACH(mp, &mountlist, mnt_list) {
if (vfs_busy(mp, VB_READ|VB_NOWAIT))
continue;
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
if (VOP_ISLOCKED(vp))
vprint(NULL, vp);
}
vfs_unbusy(mp);
}
}
#endif
/*
* Top level filesystem related information gathering.
*/
int
vfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
struct vfsconf *vfsp, *tmpvfsp;
int ret;
/* all sysctl names at this level are at least name and field */
if (namelen < 2)
return (ENOTDIR); /* overloaded */
if (name[0] != VFS_GENERIC) {
vfsp = vfs_bytypenum(name[0]);
if (vfsp == NULL || vfsp->vfc_vfsops->vfs_sysctl == NULL)
return (EOPNOTSUPP);
return ((*vfsp->vfc_vfsops->vfs_sysctl)(&name[1], namelen - 1,
oldp, oldlenp, newp, newlen, p));
}
switch (name[1]) {
case VFS_MAXTYPENUM:
return (sysctl_rdint(oldp, oldlenp, newp, maxvfsconf));
case VFS_CONF:
if (namelen < 3)
return (ENOTDIR); /* overloaded */
vfsp = vfs_bytypenum(name[2]);
if (vfsp == NULL)
return (EOPNOTSUPP);
/* Make a copy, clear out kernel pointers */
tmpvfsp = malloc(sizeof(*tmpvfsp), M_TEMP, M_WAITOK|M_ZERO);
memcpy(tmpvfsp, vfsp, sizeof(*tmpvfsp));
tmpvfsp->vfc_vfsops = NULL;
ret = sysctl_rdstruct(oldp, oldlenp, newp, tmpvfsp,
sizeof(struct vfsconf));
free(tmpvfsp, M_TEMP, sizeof(*tmpvfsp));
return (ret);
case VFS_BCACHESTAT: /* buffer cache statistics */
ret = sysctl_rdstruct(oldp, oldlenp, newp, &bcstats,
sizeof(struct bcachestats));
return(ret);
}
return (EOPNOTSUPP);
}
/*
* Check to see if a filesystem is mounted on a block device.
*/
int
vfs_mountedon(struct vnode *vp)
{
struct vnode *vq;
int error = 0;
if (vp->v_specmountpoint != NULL)
return (EBUSY);
if (vp->v_flag & VALIASED) {
SLIST_FOREACH(vq, vp->v_hashchain, v_specnext) {
if (vq->v_rdev != vp->v_rdev ||
vq->v_type != vp->v_type)
continue;
if (vq->v_specmountpoint != NULL) {
error = EBUSY;
break;
}
}
}
return (error);
}
#ifdef NFSSERVER
/*
* Build hash lists of net addresses and hang them off the mount point.
* Called by vfs_export() to set up the lists of export addresses.
*/
int
vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
struct export_args *argp)
{
struct netcred *np;
struct radix_node_head *rnh;
int nplen, i;
struct radix_node *rn;
struct sockaddr *saddr, *smask = NULL;
int error;
if (argp->ex_addrlen == 0) {
if (mp->mnt_flag & MNT_DEFEXPORTED)
return (EPERM);
np = &nep->ne_defexported;
/* fill in the kernel's ucred from userspace's xucred */
if ((error = crfromxucred(&np->netc_anon, &argp->ex_anon)))
return (error);
mp->mnt_flag |= MNT_DEFEXPORTED;
goto finish;
}
if (argp->ex_addrlen > MLEN || argp->ex_masklen > MLEN ||
argp->ex_addrlen < 0 || argp->ex_masklen < 0)
return (EINVAL);
nplen = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
np = (struct netcred *)malloc(nplen, M_NETADDR, M_WAITOK|M_ZERO);
np->netc_len = nplen;
saddr = (struct sockaddr *)(np + 1);
error = copyin(argp->ex_addr, saddr, argp->ex_addrlen);
if (error)
goto out;
if (saddr->sa_len > argp->ex_addrlen)
saddr->sa_len = argp->ex_addrlen;
if (argp->ex_masklen) {
smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
error = copyin(argp->ex_mask, smask, argp->ex_masklen);
if (error)
goto out;
if (smask->sa_len > argp->ex_masklen)
smask->sa_len = argp->ex_masklen;
}
/* fill in the kernel's ucred from userspace's xucred */
if ((error = crfromxucred(&np->netc_anon, &argp->ex_anon)))
goto out;
i = saddr->sa_family;
switch (i) {
case AF_INET:
if ((rnh = nep->ne_rtable_inet) == NULL) {
if (!rn_inithead((void **)&nep->ne_rtable_inet,
offsetof(struct sockaddr_in, sin_addr))) {
error = ENOBUFS;
goto out;
}
rnh = nep->ne_rtable_inet;
}
break;
default:
error = EINVAL;
goto out;
}
rn = rn_addroute(saddr, smask, rnh, np->netc_rnodes, 0);
if (rn == NULL || np != (struct netcred *)rn) { /* already exists */
error = EPERM;
goto out;
}
finish:
np->netc_exflags = argp->ex_flags;
return (0);
out:
free(np, M_NETADDR, np->netc_len);
return (error);
}
int
vfs_free_netcred(struct radix_node *rn, void *w, u_int id)
{
struct radix_node_head *rnh = (struct radix_node_head *)w;
struct netcred * np = (struct netcred *)rn;
rn_delete(rn->rn_key, rn->rn_mask, rnh, NULL);
free(np, M_NETADDR, np->netc_len);
return (0);
}
/*
* Free the net address hash lists that are hanging off the mount points.
*/
void
vfs_free_addrlist(struct netexport *nep)
{
struct radix_node_head *rnh;
if ((rnh = nep->ne_rtable_inet) != NULL) {
rn_walktree(rnh, vfs_free_netcred, rnh);
free(rnh, M_RTABLE, sizeof(*rnh));
nep->ne_rtable_inet = NULL;
}
}
#endif /* NFSSERVER */
int
vfs_export(struct mount *mp, struct netexport *nep, struct export_args *argp)
{
#ifdef NFSSERVER
int error;
if (argp->ex_flags & MNT_DELEXPORT) {
vfs_free_addrlist(nep);
mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
}
if (argp->ex_flags & MNT_EXPORTED) {
if ((error = vfs_hang_addrlist(mp, nep, argp)) != 0)
return (error);
mp->mnt_flag |= MNT_EXPORTED;
}
return (0);
#else
return (ENOTSUP);
#endif /* NFSSERVER */
}
struct netcred *
vfs_export_lookup(struct mount *mp, struct netexport *nep, struct mbuf *nam)
{
#ifdef NFSSERVER
struct netcred *np;
struct radix_node_head *rnh;
struct sockaddr *saddr;
np = NULL;
if (mp->mnt_flag & MNT_EXPORTED) {
/*
* Lookup in the export list first.
*/
if (nam != NULL) {
saddr = mtod(nam, struct sockaddr *);
switch(saddr->sa_family) {
case AF_INET:
rnh = nep->ne_rtable_inet;
break;
default:
rnh = NULL;
break;
}
if (rnh != NULL)
np = (struct netcred *)rn_match(saddr, rnh);
}
/*
* If no address match, use the default if it exists.
*/
if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
np = &nep->ne_defexported;
}
return (np);
#else
return (NULL);
#endif /* NFSSERVER */
}
/*
* Do the usual access checking.
* file_mode, uid and gid are from the vnode in question,
* while acc_mode and cred are from the VOP_ACCESS parameter list
*/
int
vaccess(enum vtype type, mode_t file_mode, uid_t uid, gid_t gid,
mode_t acc_mode, struct ucred *cred)
{
mode_t mask;
/* User id 0 always gets read/write access. */
if (cred->cr_uid == 0) {
/* For VEXEC, at least one of the execute bits must be set. */
if ((acc_mode & VEXEC) && type != VDIR &&
(file_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
return EACCES;
return 0;
}
mask = 0;
/* Otherwise, check the owner. */
if (cred->cr_uid == uid) {
if (acc_mode & VEXEC)
mask |= S_IXUSR;
if (acc_mode & VREAD)
mask |= S_IRUSR;
if (acc_mode & VWRITE)
mask |= S_IWUSR;
return (file_mode & mask) == mask ? 0 : EACCES;
}
/* Otherwise, check the groups. */
if (groupmember(gid, cred)) {
if (acc_mode & VEXEC)
mask |= S_IXGRP;
if (acc_mode & VREAD)
mask |= S_IRGRP;
if (acc_mode & VWRITE)
mask |= S_IWGRP;
return (file_mode & mask) == mask ? 0 : EACCES;
}
/* Otherwise, check everyone else. */
if (acc_mode & VEXEC)
mask |= S_IXOTH;
if (acc_mode & VREAD)
mask |= S_IROTH;
if (acc_mode & VWRITE)
mask |= S_IWOTH;
return (file_mode & mask) == mask ? 0 : EACCES;
}
int
vnoperm(struct vnode *vp)
{
if (vp->v_flag & VROOT || vp->v_mount == NULL)
return 0;
return (vp->v_mount->mnt_flag & MNT_NOPERM);
}
struct rwlock vfs_stall_lock = RWLOCK_INITIALIZER("vfs_stall");
unsigned int vfs_stalling = 0;
int
vfs_stall(struct proc *p, int stall)
{
struct mount *mp;
int allerror = 0, error;
if (stall) {
atomic_inc_int(&vfs_stalling);
rw_enter_write(&vfs_stall_lock);
}
/*
* The loop variable mp is protected by vfs_busy() so that it cannot
* be unmounted while VFS_SYNC() sleeps. Traverse forward to keep the
* lock order consistent with dounmount().
*/
TAILQ_FOREACH(mp, &mountlist, mnt_list) {
if (stall) {
error = vfs_busy(mp, VB_WRITE|VB_WAIT|VB_DUPOK);
if (error) {
printf("%s: busy\n", mp->mnt_stat.f_mntonname);
allerror = error;
continue;
}
uvm_vnp_sync(mp);
error = VFS_SYNC(mp, MNT_WAIT, stall, p->p_ucred, p);
if (error) {
printf("%s: failed to sync\n",
mp->mnt_stat.f_mntonname);
vfs_unbusy(mp);
allerror = error;
continue;
}
mp->mnt_flag |= MNT_STALLED;
} else {
if (mp->mnt_flag & MNT_STALLED) {
vfs_unbusy(mp);
mp->mnt_flag &= ~MNT_STALLED;
}
}
}
if (!stall) {
rw_exit_write(&vfs_stall_lock);
atomic_dec_int(&vfs_stalling);
}
return (allerror);
}
void
vfs_stall_barrier(void)
{
if (__predict_false(vfs_stalling)) {
rw_enter_read(&vfs_stall_lock);
rw_exit_read(&vfs_stall_lock);
}
}
/*
* Unmount all file systems.
* We traverse the list in reverse order under the assumption that doing so
* will avoid needing to worry about dependencies.
*/
void
vfs_unmountall(void)
{
struct mount *mp, *nmp;
int allerror, error, again = 1;
retry:
allerror = 0;
TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mntlist, mnt_list, nmp) {
if (vfs_busy(mp, VB_WRITE|VB_NOWAIT))
continue;
/* XXX Here is a race, the next pointer is not locked. */
if ((error = dounmount(mp, MNT_FORCE, curproc)) != 0) {
printf("unmount of %s failed with error %d\n",
mp->mnt_stat.f_mntonname, error);
allerror = 1;
}
}
if (allerror) {
printf("WARNING: some file systems would not unmount\n");
if (again) {
printf("retrying\n");
again = 0;
goto retry;
}
}
}
/*
* Sync and unmount file systems before shutting down.
*/
void
vfs_shutdown(struct proc *p)
{
#ifdef ACCOUNTING
acct_shutdown();
#endif
printf("syncing disks...");
if (panicstr == NULL) {
/* Sync before unmount, in case we hang on something. */
sys_sync(p, NULL, NULL);
vfs_unmountall();
}
#if NSOFTRAID > 0
sr_quiesce();
#endif
if (vfs_syncwait(p, 1))
printf(" giving up\n");
else
printf(" done\n");
}
/*
* perform sync() operation and wait for buffers to flush.
*/
int
vfs_syncwait(struct proc *p, int verbose)
{
struct buf *bp;
int iter, nbusy, dcount, s;
#ifdef MULTIPROCESSOR
int hold_count;
#endif
sys_sync(p, NULL, NULL);
/* Wait for sync to finish. */
dcount = 10000;
for (iter = 0; iter < 20; iter++) {
nbusy = 0;
LIST_FOREACH(bp, &bufhead, b_list) {
if ((bp->b_flags & (B_BUSY|B_INVAL|B_READ)) == B_BUSY)
nbusy++;
/*
* With soft updates, some buffers that are
* written will be remarked as dirty until other
* buffers are written.
*/
if (bp->b_flags & B_DELWRI) {
s = splbio();
bremfree(bp);
buf_acquire(bp);
splx(s);
nbusy++;
bawrite(bp);
if (dcount-- <= 0) {
if (verbose)
printf("softdep ");
return 1;
}
}
}
if (nbusy == 0)
break;
if (verbose)
printf("%d ", nbusy);
#ifdef MULTIPROCESSOR
if (_kernel_lock_held())
hold_count = __mp_release_all(&kernel_lock);
else
hold_count = 0;
#endif
DELAY(40000 * iter);
#ifdef MULTIPROCESSOR
if (hold_count)
__mp_acquire_count(&kernel_lock, hold_count);
#endif
}
return nbusy;
}
/*
* posix file system related system variables.
*/
int
fs_posix_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen, struct proc *p)
{
/* all sysctl names at this level are terminal */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case FS_POSIX_SETUID:
return (sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&suid_clear));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
/*
* file system related system variables.
*/
int
fs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
sysctlfn *fn;
switch (name[0]) {
case FS_POSIX:
fn = fs_posix_sysctl;
break;
default:
return (EOPNOTSUPP);
}
return (*fn)(name + 1, namelen - 1, oldp, oldlenp, newp, newlen, p);
}
/*
* Routines dealing with vnodes and buffers
*/
/*
* Wait for all outstanding I/Os to complete
*
* Manipulates v_numoutput. Must be called at splbio()
*/
int
vwaitforio(struct vnode *vp, int slpflag, char *wmesg, uint64_t timeo)
{
int error = 0;
splassert(IPL_BIO);
while (vp->v_numoutput) {
vp->v_bioflag |= VBIOWAIT;
error = tsleep_nsec(&vp->v_numoutput,
slpflag | (PRIBIO + 1), wmesg, timeo);
if (error)
break;
}
return (error);
}
/*
* Update outstanding I/O count and do wakeup if requested.
*
* Manipulates v_numoutput. Must be called at splbio()
*/
void
vwakeup(struct vnode *vp)
{
splassert(IPL_BIO);
if (vp != NULL) {
if (vp->v_numoutput-- == 0)
panic("vwakeup: neg numoutput");
if ((vp->v_bioflag & VBIOWAIT) && vp->v_numoutput == 0) {
vp->v_bioflag &= ~VBIOWAIT;
wakeup(&vp->v_numoutput);
}
}
}
/*
* Flush out and invalidate all buffers associated with a vnode.
* Called with the underlying object locked.
*/
int
vinvalbuf(struct vnode *vp, int flags, struct ucred *cred, struct proc *p,
int slpflag, uint64_t slptimeo)
{
struct buf *bp;
struct buf *nbp, *blist;
int s, error;
#ifdef VFSLCKDEBUG
if ((vp->v_flag & VLOCKSWORK) && !VOP_ISLOCKED(vp))
panic("%s: vp isn't locked, vp %p", __func__, vp);
#endif
if (flags & V_SAVE) {
s = splbio();
vwaitforio(vp, 0, "vinvalbuf", INFSLP);
if (!LIST_EMPTY(&vp->v_dirtyblkhd)) {
splx(s);
if ((error = VOP_FSYNC(vp, cred, MNT_WAIT, p)) != 0)
return (error);
s = splbio();
if (vp->v_numoutput > 0 ||
!LIST_EMPTY(&vp->v_dirtyblkhd))
panic("%s: dirty bufs, vp %p", __func__, vp);
}
splx(s);
}
loop:
s = splbio();
for (;;) {
int count = 0;
if ((blist = LIST_FIRST(&vp->v_cleanblkhd)) &&
(flags & V_SAVEMETA))
while (blist && blist->b_lblkno < 0)
blist = LIST_NEXT(blist, b_vnbufs);
if (blist == NULL &&
(blist = LIST_FIRST(&vp->v_dirtyblkhd)) &&
(flags & V_SAVEMETA))
while (blist && blist->b_lblkno < 0)
blist = LIST_NEXT(blist, b_vnbufs);
if (!blist)
break;
for (bp = blist; bp; bp = nbp) {
nbp = LIST_NEXT(bp, b_vnbufs);
if (flags & V_SAVEMETA && bp->b_lblkno < 0)
continue;
if (bp->b_flags & B_BUSY) {
bp->b_flags |= B_WANTED;
error = tsleep_nsec(bp, slpflag | (PRIBIO + 1),
"vinvalbuf", slptimeo);
if (error) {
splx(s);
return (error);
}
break;
}
bremfree(bp);
/*
* XXX Since there are no node locks for NFS, I believe
* there is a slight chance that a delayed write will
* occur while sleeping just above, so check for it.
*/
if ((bp->b_flags & B_DELWRI) && (flags & V_SAVE)) {
buf_acquire(bp);
splx(s);
(void) VOP_BWRITE(bp);
goto loop;
}
buf_acquire_nomap(bp);
bp->b_flags |= B_INVAL;
brelse(bp);
count++;
/*
* XXX Temporary workaround XXX
*
* If this is a gigantisch vnode and we are
* trashing a ton of buffers, drop the lock
* and yield every so often. The longer term
* fix is to add a separate list for these
* invalid buffers so we don't have to do the
* work to free these here.
*/
if (count > 100) {
splx(s);
sched_pause(yield);
goto loop;
}
}
}
if (!(flags & V_SAVEMETA) &&
(!LIST_EMPTY(&vp->v_dirtyblkhd) || !LIST_EMPTY(&vp->v_cleanblkhd)))
panic("%s: flush failed, vp %p", __func__, vp);
splx(s);
return (0);
}
void
vflushbuf(struct vnode *vp, int sync)
{
struct buf *bp, *nbp;
int s;
loop:
s = splbio();
LIST_FOREACH_SAFE(bp, &vp->v_dirtyblkhd, b_vnbufs, nbp) {
if ((bp->b_flags & B_BUSY))
continue;
if ((bp->b_flags & B_DELWRI) == 0)
panic("vflushbuf: not dirty");
bremfree(bp);
buf_acquire(bp);
splx(s);
/*
* Wait for I/O associated with indirect blocks to complete,
* since there is no way to quickly wait for them below.
*/
if (bp->b_vp == vp || sync == 0)
(void) bawrite(bp);
else
(void) bwrite(bp);
goto loop;
}
if (sync == 0) {
splx(s);
return;
}
vwaitforio(vp, 0, "vflushbuf", INFSLP);
if (!LIST_EMPTY(&vp->v_dirtyblkhd)) {
splx(s);
#ifdef DIAGNOSTIC
vprint("vflushbuf: dirty", vp);
#endif
goto loop;
}
splx(s);
}
/*
* Associate a buffer with a vnode.
*
* Manipulates buffer vnode queues. Must be called at splbio().
*/
void
bgetvp(struct vnode *vp, struct buf *bp)
{
splassert(IPL_BIO);
if (bp->b_vp)
panic("bgetvp: not free");
vhold(vp);
bp->b_vp = vp;
if (vp->v_type == VBLK || vp->v_type == VCHR)
bp->b_dev = vp->v_rdev;
else
bp->b_dev = NODEV;
/*
* Insert onto list for new vnode.
*/
bufinsvn(bp, &vp->v_cleanblkhd);
}
/*
* Disassociate a buffer from a vnode.
*
* Manipulates vnode buffer queues. Must be called at splbio().
*/
void
brelvp(struct buf *bp)
{
struct vnode *vp;
splassert(IPL_BIO);
if ((vp = bp->b_vp) == (struct vnode *) 0)
panic("brelvp: NULL");
/*
* Delete from old vnode list, if on one.
*/
if (LIST_NEXT(bp, b_vnbufs) != NOLIST)
bufremvn(bp);
if ((vp->v_bioflag & VBIOONSYNCLIST) &&
LIST_EMPTY(&vp->v_dirtyblkhd)) {
vp->v_bioflag &= ~VBIOONSYNCLIST;
LIST_REMOVE(vp, v_synclist);
}
bp->b_vp = NULL;
vdrop(vp);
}
/*
* Replaces the current vnode associated with the buffer, if any,
* with a new vnode.
*
* If an output I/O is pending on the buffer, the old vnode
* I/O count is adjusted.
*
* Ignores vnode buffer queues. Must be called at splbio().
*/
void
buf_replacevnode(struct buf *bp, struct vnode *newvp)
{
struct vnode *oldvp = bp->b_vp;
splassert(IPL_BIO);
if (oldvp)
brelvp(bp);
if ((bp->b_flags & (B_READ | B_DONE)) == 0) {
newvp->v_numoutput++; /* put it on swapdev */
vwakeup(oldvp);
}
bgetvp(newvp, bp);
bufremvn(bp);
}
/*
* Used to assign buffers to the appropriate clean or dirty list on
* the vnode and to add newly dirty vnodes to the appropriate
* filesystem syncer list.
*
* Manipulates vnode buffer queues. Must be called at splbio().
*/
void
reassignbuf(struct buf *bp)
{
struct buflists *listheadp;
int delay;
struct vnode *vp = bp->b_vp;
splassert(IPL_BIO);
/*
* Delete from old vnode list, if on one.
*/
if (LIST_NEXT(bp, b_vnbufs) != NOLIST)
bufremvn(bp);
/*
* If dirty, put on list of dirty buffers;
* otherwise insert onto list of clean buffers.
*/
if ((bp->b_flags & B_DELWRI) == 0) {
listheadp = &vp->v_cleanblkhd;
if ((vp->v_bioflag & VBIOONSYNCLIST) &&
LIST_EMPTY(&vp->v_dirtyblkhd)) {
vp->v_bioflag &= ~VBIOONSYNCLIST;
LIST_REMOVE(vp, v_synclist);
}
} else {
listheadp = &vp->v_dirtyblkhd;
if ((vp->v_bioflag & VBIOONSYNCLIST) == 0) {
switch (vp->v_type) {
case VDIR:
delay = syncdelay / 2;
break;
case VBLK:
if (vp->v_specmountpoint != NULL) {
delay = syncdelay / 3;
break;
}
/* FALLTHROUGH */
default:
delay = syncdelay;
}
vn_syncer_add_to_worklist(vp, delay);
}
}
bufinsvn(bp, listheadp);
}
/*
* Check if vnode represents a disk device
*/
int
vn_isdisk(struct vnode *vp, int *errp)
{
if (vp->v_type != VBLK && vp->v_type != VCHR)
return (0);
return (1);
}
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
void
vfs_buf_print(void *b, int full,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct buf *bp = b;
(*pr)(" vp %p lblkno 0x%llx blkno 0x%llx dev 0x%x\n"
" proc %p error %d flags %lb\n",
bp->b_vp, (int64_t)bp->b_lblkno, (int64_t)bp->b_blkno, bp->b_dev,
bp->b_proc, bp->b_error, bp->b_flags, B_BITS);
(*pr)(" bufsize 0x%lx bcount 0x%lx resid 0x%lx\n"
" data %p saveaddr %p dep %p iodone %p\n",
bp->b_bufsize, bp->b_bcount, (long)bp->b_resid,
bp->b_data, bp->b_saveaddr,
LIST_FIRST(&bp->b_dep), bp->b_iodone);
(*pr)(" dirty {off 0x%x end 0x%x} valid {off 0x%x end 0x%x}\n",
bp->b_dirtyoff, bp->b_dirtyend, bp->b_validoff, bp->b_validend);
#ifdef FFS_SOFTUPDATES
if (full)
softdep_print(bp, full, pr);
#endif
}
const char *vtypes[] = { VTYPE_NAMES };
const char *vtags[] = { VTAG_NAMES };
void
vfs_vnode_print(void *v, int full,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct vnode *vp = v;
(*pr)("tag %s(%d) type %s(%d) mount %p typedata %p\n",
(u_int)vp->v_tag >= nitems(vtags)? "<unk>":vtags[vp->v_tag],
vp->v_tag,
(u_int)vp->v_type >= nitems(vtypes)? "<unk>":vtypes[vp->v_type],
vp->v_type, vp->v_mount, vp->v_mountedhere);
(*pr)("data %p usecount %d writecount %d holdcnt %d numoutput %d\n",
vp->v_data, vp->v_usecount, vp->v_writecount,
vp->v_holdcnt, vp->v_numoutput);
/* uvm_object_printit(&vp->v_uobj, full, pr); */
if (full) {
struct buf *bp;
(*pr)("clean bufs:\n");
LIST_FOREACH(bp, &vp->v_cleanblkhd, b_vnbufs) {
(*pr)(" bp %p\n", bp);
vfs_buf_print(bp, full, pr);
}
(*pr)("dirty bufs:\n");
LIST_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) {
(*pr)(" bp %p\n", bp);
vfs_buf_print(bp, full, pr);
}
}
}
void
vfs_mount_print(struct mount *mp, int full,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct vfsconf *vfc = mp->mnt_vfc;
struct vnode *vp;
int cnt;
(*pr)("flags %b\nvnodecovered %p syncer %p data %p\n",
mp->mnt_flag, MNT_BITS,
mp->mnt_vnodecovered, mp->mnt_syncer, mp->mnt_data);
(*pr)("vfsconf: ops %p name \"%s\" num %d ref %u flags 0x%x\n",
vfc->vfc_vfsops, vfc->vfc_name, vfc->vfc_typenum,
vfc->vfc_refcount, vfc->vfc_flags);
(*pr)("statvfs cache: bsize %x iosize %x\n"
"blocks %llu free %llu avail %lld\n",
mp->mnt_stat.f_bsize, mp->mnt_stat.f_iosize, mp->mnt_stat.f_blocks,
mp->mnt_stat.f_bfree, mp->mnt_stat.f_bavail);
(*pr)(" files %llu ffiles %llu favail %lld\n", mp->mnt_stat.f_files,
mp->mnt_stat.f_ffree, mp->mnt_stat.f_favail);
(*pr)(" f_fsidx {0x%x, 0x%x} owner %u ctime 0x%llx\n",
mp->mnt_stat.f_fsid.val[0], mp->mnt_stat.f_fsid.val[1],
mp->mnt_stat.f_owner, mp->mnt_stat.f_ctime);
(*pr)(" syncwrites %llu asyncwrites = %llu\n",
mp->mnt_stat.f_syncwrites, mp->mnt_stat.f_asyncwrites);
(*pr)(" syncreads %llu asyncreads = %llu\n",
mp->mnt_stat.f_syncreads, mp->mnt_stat.f_asyncreads);
(*pr)(" fstype \"%s\" mnton \"%s\" mntfrom \"%s\" mntspec \"%s\"\n",
mp->mnt_stat.f_fstypename, mp->mnt_stat.f_mntonname,
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntfromspec);
(*pr)("locked vnodes:");
/* XXX would take mountlist lock, except ddb has no context */
cnt = 0;
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
if (VOP_ISLOCKED(vp)) {
if (cnt == 0)
(*pr)("\n %p", vp);
else if ((cnt % (72 / (sizeof(void *) * 2 + 4))) == 0)
(*pr)(",\n %p", vp);
else
(*pr)(", %p", vp);
cnt++;
}
}
(*pr)("\n");
if (full) {
(*pr)("all vnodes:");
/* XXX would take mountlist lock, except ddb has no context */
cnt = 0;
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
if (cnt == 0)
(*pr)("\n %p", vp);
else if ((cnt % (72 / (sizeof(void *) * 2 + 4))) == 0)
(*pr)(",\n %p", vp);
else
(*pr)(", %p", vp);
cnt++;
}
(*pr)("\n");
}
}
#endif /* DDB */
void
copy_statfs_info(struct statfs *sbp, const struct mount *mp)
{
const struct statfs *mbp;
strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN);
if (sbp == (mbp = &mp->mnt_stat))
return;
sbp->f_fsid = mbp->f_fsid;
sbp->f_owner = mbp->f_owner;
sbp->f_flags = mbp->f_flags;
sbp->f_syncwrites = mbp->f_syncwrites;
sbp->f_asyncwrites = mbp->f_asyncwrites;
sbp->f_syncreads = mbp->f_syncreads;
sbp->f_asyncreads = mbp->f_asyncreads;
sbp->f_namemax = mbp->f_namemax;
memcpy(sbp->f_mntonname, mp->mnt_stat.f_mntonname, MNAMELEN);
memcpy(sbp->f_mntfromname, mp->mnt_stat.f_mntfromname, MNAMELEN);
memcpy(sbp->f_mntfromspec, mp->mnt_stat.f_mntfromspec, MNAMELEN);
memcpy(&sbp->mount_info, &mp->mnt_stat.mount_info,
sizeof(union mount_info));
}
36
31
1
4
19
1
11
19
1
36
36
35
36
64
29
1
35
28
35
1
34
34
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
/* $OpenBSD: pf_lb.c,v 1.72 2022/08/31 11:29:12 benno Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002 - 2008 Henning Brauer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Effort sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
*
*/
#include "bpfilter.h"
#include "pflog.h"
#include "pfsync.h"
#include "pflow.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/pool.h>
#include <sys/rwlock.h>
#include <sys/syslog.h>
#include <sys/stdint.h>
#include <crypto/siphash.h>
#include <net/if.h>
#include <net/bpf.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/if_ether.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#if NPFLOG > 0
#include <net/if_pflog.h>
#endif /* NPFLOG > 0 */
#if NPFLOW > 0
#include <net/if_pflow.h>
#endif /* NPFLOW > 0 */
#if NPFSYNC > 0
#include <net/if_pfsync.h>
#endif /* NPFSYNC > 0 */
u_int64_t pf_hash(struct pf_addr *, struct pf_addr *,
struct pf_poolhashkey *, sa_family_t);
int pf_get_sport(struct pf_pdesc *, struct pf_rule *,
struct pf_addr *, u_int16_t *, u_int16_t,
u_int16_t, struct pf_src_node **);
int pf_map_addr_states_increase(sa_family_t,
struct pf_pool *, struct pf_addr *);
int pf_get_transaddr_af(struct pf_rule *,
struct pf_pdesc *, struct pf_src_node **);
int pf_map_addr_sticky(sa_family_t, struct pf_rule *,
struct pf_addr *, struct pf_addr *,
struct pf_src_node **, struct pf_pool *,
enum pf_sn_types);
u_int64_t
pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
struct pf_poolhashkey *key, sa_family_t af)
{
uint64_t res = 0;
#ifdef INET6
union {
uint64_t hash64;
uint32_t hash32[2];
} h;
#endif /* INET6 */
switch (af) {
case AF_INET:
res = SipHash24((SIPHASH_KEY *)key,
&inaddr->addr32[0], sizeof(inaddr->addr32[0]));
hash->addr32[0] = res;
break;
#ifdef INET6
case AF_INET6:
res = SipHash24((SIPHASH_KEY *)key, &inaddr->addr32[0],
4 * sizeof(inaddr->addr32[0]));
h.hash64 = res;
hash->addr32[0] = h.hash32[0];
hash->addr32[1] = h.hash32[1];
/*
* siphash isn't big enough, but flipping it around is
* good enough here.
*/
hash->addr32[2] = ~h.hash32[1];
hash->addr32[3] = ~h.hash32[0];
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
return (res);
}
int
pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high,
struct pf_src_node **sn)
{
struct pf_state_key_cmp key;
struct pf_addr init_addr;
u_int16_t cut;
int dir = (pd->dir == PF_IN) ? PF_OUT : PF_IN;
int sidx = pd->sidx;
int didx = pd->didx;
memset(&init_addr, 0, sizeof(init_addr));
if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
PF_SN_NAT))
return (1);
if (pd->proto == IPPROTO_ICMP) {
if (pd->ndport == htons(ICMP_ECHO)) {
low = 1;
high = 65535;
} else
return (0); /* Don't try to modify non-echo ICMP */
}
#ifdef INET6
if (pd->proto == IPPROTO_ICMPV6) {
if (pd->ndport == htons(ICMP6_ECHO_REQUEST)) {
low = 1;
high = 65535;
} else
return (0); /* Don't try to modify non-echo ICMP */
}
#endif /* INET6 */
do {
key.af = pd->naf;
key.proto = pd->proto;
key.rdomain = pd->rdomain;
pf_addrcpy(&key.addr[didx], &pd->ndaddr, key.af);
pf_addrcpy(&key.addr[sidx], naddr, key.af);
key.port[didx] = pd->ndport;
/*
* port search; start random, step;
* similar 2 portloop in in_pcbbind
*/
if (!(pd->proto == IPPROTO_TCP || pd->proto == IPPROTO_UDP ||
pd->proto == IPPROTO_ICMP || pd->proto == IPPROTO_ICMPV6)) {
/* XXX bug: icmp states dont use the id on both
* XXX sides (traceroute -I through nat) */
key.port[sidx] = pd->nsport;
if (pf_find_state_all(&key, dir, NULL) == NULL) {
*nport = pd->nsport;
return (0);
}
} else if (low == 0 && high == 0) {
key.port[sidx] = pd->nsport;
if (pf_find_state_all(&key, dir, NULL) == NULL) {
*nport = pd->nsport;
return (0);
}
} else if (low == high) {
key.port[sidx] = htons(low);
if (pf_find_state_all(&key, dir, NULL) == NULL) {
*nport = htons(low);
return (0);
}
} else {
u_int32_t tmp;
if (low > high) {
tmp = low;
low = high;
high = tmp;
}
/* low < high */
cut = arc4random_uniform(1 + high - low) + low;
/* low <= cut <= high */
for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) {
key.port[sidx] = htons(tmp);
if (pf_find_state_all(&key, dir, NULL) ==
NULL && !in_baddynamic(tmp, pd->proto)) {
*nport = htons(tmp);
return (0);
}
}
tmp = cut;
for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
key.port[sidx] = htons(tmp);
if (pf_find_state_all(&key, dir, NULL) ==
NULL && !in_baddynamic(tmp, pd->proto)) {
*nport = htons(tmp);
return (0);
}
}
}
switch (r->nat.opts & PF_POOL_TYPEMASK) {
case PF_POOL_RANDOM:
case PF_POOL_ROUNDROBIN:
case PF_POOL_LEASTSTATES:
/*
* pick a different source address since we're out
* of free port choices for the current one.
*/
if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr,
&init_addr, sn, &r->nat, PF_SN_NAT))
return (1);
break;
case PF_POOL_NONE:
case PF_POOL_SRCHASH:
case PF_POOL_BITMASK:
default:
return (1);
}
} while (! PF_AEQ(&init_addr, naddr, pd->naf) );
return (1); /* none available */
}
int
pf_map_addr_sticky(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pf_src_node **sns, struct pf_pool *rpool,
enum pf_sn_types type)
{
struct pf_addr *raddr, *rmask, *cached;
struct pf_state *s;
struct pf_src_node k;
int valid;
k.af = af;
k.type = type;
pf_addrcpy(&k.addr, saddr, af);
k.rule.ptr = r;
pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
sns[type] = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
if (sns[type] == NULL)
return (-1);
/* check if the cached entry is still valid */
cached = &(sns[type])->raddr;
valid = 0;
if (PF_AZERO(cached, af)) {
valid = 1;
} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_kentry_byaddr(rpool->addr.p.dyn->pfid_kt, cached,
af, 0))
valid = 1;
} else if (rpool->addr.type == PF_ADDR_TABLE) {
if (pfr_kentry_byaddr(rpool->addr.p.tbl, cached, af, 0))
valid = 1;
} else if (rpool->addr.type != PF_ADDR_NOROUTE) {
raddr = &rpool->addr.v.a.addr;
rmask = &rpool->addr.v.a.mask;
valid = pf_match_addr(0, raddr, rmask, cached, af);
}
if (!valid) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: pf_map_addr: "
"stale src tracking (%u) ", type);
pf_print_host(&k.addr, 0, af);
addlog(" to ");
pf_print_host(cached, 0, af);
addlog("\n");
}
if (sns[type]->states != 0) {
/* XXX expensive */
RB_FOREACH(s, pf_state_tree_id,
&tree_id)
pf_state_rm_src_node(s,
sns[type]);
}
sns[type]->expire = 1;
pf_remove_src_node(sns[type]);
sns[type] = NULL;
return (-1);
}
if (!PF_AZERO(cached, af)) {
pf_addrcpy(naddr, cached, af);
if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES &&
pf_map_addr_states_increase(af, rpool, cached) == -1)
return (-1);
}
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: pf_map_addr: "
"src tracking (%u) maps ", type);
pf_print_host(&k.addr, 0, af);
addlog(" to ");
pf_print_host(naddr, 0, af);
addlog("\n");
}
if (sns[type]->kif != NULL)
rpool->kif = sns[type]->kif;
return (0);
}
uint32_t
pf_rand_addr(uint32_t mask)
{
uint32_t addr;
mask = ~ntohl(mask);
addr = arc4random_uniform(mask + 1);
return (htonl(addr));
}
int
pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sns,
struct pf_pool *rpool, enum pf_sn_types type)
{
struct pf_addr hash;
struct pf_addr faddr;
struct pf_addr *raddr = &rpool->addr.v.a.addr;
struct pf_addr *rmask = &rpool->addr.v.a.mask;
struct pfr_ktable *kt;
struct pfi_kif *kif;
u_int64_t states;
u_int16_t weight;
u_int64_t load;
u_int64_t cload;
u_int64_t hashidx;
int cnt;
if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE &&
pf_map_addr_sticky(af, r, saddr, naddr, sns, rpool, type) == 0)
return (0);
if (rpool->addr.type == PF_ADDR_NOROUTE)
return (1);
if (rpool->addr.type == PF_ADDR_DYNIFTL) {
switch (af) {
case AF_INET:
if (rpool->addr.p.dyn->pfid_acnt4 < 1 &&
!PF_POOL_DYNTYPE(rpool->opts))
return (1);
raddr = &rpool->addr.p.dyn->pfid_addr4;
rmask = &rpool->addr.p.dyn->pfid_mask4;
break;
#ifdef INET6
case AF_INET6:
if (rpool->addr.p.dyn->pfid_acnt6 < 1 &&
!PF_POOL_DYNTYPE(rpool->opts))
return (1);
raddr = &rpool->addr.p.dyn->pfid_addr6;
rmask = &rpool->addr.p.dyn->pfid_mask6;
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
} else if (rpool->addr.type == PF_ADDR_TABLE) {
if (!PF_POOL_DYNTYPE(rpool->opts))
return (1); /* unsupported */
} else {
raddr = &rpool->addr.v.a.addr;
rmask = &rpool->addr.v.a.mask;
}
switch (rpool->opts & PF_POOL_TYPEMASK) {
case PF_POOL_NONE:
pf_addrcpy(naddr, raddr, af);
break;
case PF_POOL_BITMASK:
pf_poolmask(naddr, raddr, rmask, saddr, af);
break;
case PF_POOL_RANDOM:
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (rpool->addr.type == PF_ADDR_TABLE)
kt = rpool->addr.p.tbl;
else
kt = rpool->addr.p.dyn->pfid_kt;
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return (1);
cnt = kt->pfrkt_cnt;
if (cnt == 0)
rpool->tblidx = 0;
else
rpool->tblidx = (int)arc4random_uniform(cnt);
memset(&rpool->counter, 0, sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
pf_addrcpy(naddr, &rpool->counter, af);
} else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
switch (af) {
case AF_INET:
rpool->counter.addr32[0] = pf_rand_addr(
rmask->addr32[0]);
break;
#ifdef INET6
case AF_INET6:
if (rmask->addr32[3] != 0xffffffff)
rpool->counter.addr32[3] = pf_rand_addr(
rmask->addr32[3]);
else
break;
if (rmask->addr32[2] != 0xffffffff)
rpool->counter.addr32[2] = pf_rand_addr(
rmask->addr32[2]);
else
break;
if (rmask->addr32[1] != 0xffffffff)
rpool->counter.addr32[1] = pf_rand_addr(
rmask->addr32[1]);
else
break;
if (rmask->addr32[0] != 0xffffffff)
rpool->counter.addr32[0] = pf_rand_addr(
rmask->addr32[0]);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
pf_addrcpy(init_addr, naddr, af);
} else {
pf_addr_inc(&rpool->counter, af);
pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
}
break;
case PF_POOL_SRCHASH:
hashidx = pf_hash(saddr, &hash, &rpool->key, af);
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (rpool->addr.type == PF_ADDR_TABLE)
kt = rpool->addr.p.tbl;
else
kt = rpool->addr.p.dyn->pfid_kt;
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return (1);
cnt = kt->pfrkt_cnt;
if (cnt == 0)
rpool->tblidx = 0;
else
rpool->tblidx = (int)(hashidx % cnt);
memset(&rpool->counter, 0, sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
pf_addrcpy(naddr, &rpool->counter, af);
} else {
pf_poolmask(naddr, raddr, rmask, &hash, af);
}
break;
case PF_POOL_ROUNDROBIN:
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
/*
* reset counter in case its value
* has been removed from the pool.
*/
memset(&rpool->counter, 0,
sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
}
} else if (PF_AZERO(&rpool->counter, af)) {
/*
* fall back to POOL_NONE if there is a single host
* address in pool.
*/
if (af == AF_INET &&
rmask->addr32[0] == INADDR_BROADCAST) {
pf_addrcpy(naddr, raddr, af);
break;
}
#ifdef INET6
if (af == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&rmask->v6, &in6mask128)) {
pf_addrcpy(naddr, raddr, af);
break;
}
#endif
} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
return (1);
/* iterate over table if it contains entries which are weighted */
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0)) {
do {
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool,
&raddr, &rmask, af))
return (1);
} else {
log(LOG_ERR, "pf: pf_map_addr: "
"weighted RR failure");
return (1);
}
if (rpool->weight >= rpool->curweight)
break;
pf_addr_inc(&rpool->counter, af);
} while (1);
weight = rpool->weight;
}
pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
if (init_addr != NULL && PF_AZERO(init_addr, af))
pf_addrcpy(init_addr, &rpool->counter, af);
pf_addr_inc(&rpool->counter, af);
break;
case PF_POOL_LEASTSTATES:
/* retrieve an address first */
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
/* see PF_POOL_ROUNDROBIN */
memset(&rpool->counter, 0,
sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
}
} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
return (1);
states = rpool->states;
weight = rpool->weight;
kif = rpool->kif;
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
load = ((UINT16_MAX * rpool->states) / rpool->weight);
else
load = states;
pf_addrcpy(&faddr, &rpool->counter, af);
pf_addrcpy(naddr, &rpool->counter, af);
if (init_addr != NULL && PF_AZERO(init_addr, af))
pf_addrcpy(init_addr, naddr, af);
/*
* iterate *once* over whole table and find destination with
* least connection
*/
do {
pf_addr_inc(&rpool->counter, af);
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
} else if (pf_match_addr(0, raddr, rmask,
&rpool->counter, af))
return (1);
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
cload = ((UINT16_MAX * rpool->states)
/ rpool->weight);
else
cload = rpool->states;
/* find lc minimum */
if (cload < load) {
states = rpool->states;
weight = rpool->weight;
kif = rpool->kif;
load = cload;
pf_addrcpy(naddr, &rpool->counter, af);
if (init_addr != NULL &&
PF_AZERO(init_addr, af))
pf_addrcpy(init_addr, naddr, af);
}
} while (pf_match_addr(1, &faddr, rmask, &rpool->counter, af) &&
(states > 0));
if (pf_map_addr_states_increase(af, rpool, naddr) == -1)
return (1);
/* revert the kif which was set by pfr_pool_get() */
rpool->kif = kif;
break;
}
if (rpool->opts & PF_POOL_STICKYADDR) {
if (sns[type] != NULL) {
pf_remove_src_node(sns[type]);
sns[type] = NULL;
}
if (pf_insert_src_node(&sns[type], r, type, af, saddr, naddr,
rpool->kif))
return (1);
}
if (pf_status.debug >= LOG_INFO &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
log(LOG_INFO, "pf: pf_map_addr: selected address ");
pf_print_host(naddr, 0, af);
if ((rpool->opts & PF_POOL_TYPEMASK) ==
PF_POOL_LEASTSTATES)
addlog(" with state count %llu", states);
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
addlog(" with weight %u", weight);
addlog("\n");
}
return (0);
}
int
pf_map_addr_states_increase(sa_family_t af, struct pf_pool *rpool,
struct pf_addr *naddr)
{
if (rpool->addr.type == PF_ADDR_TABLE) {
if (pfr_states_increase(rpool->addr.p.tbl,
naddr, af) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG,
"pf: pf_map_addr_states_increase: "
"selected address ");
pf_print_host(naddr, 0, af);
addlog(". Failed to increase count!\n");
}
return (-1);
}
} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_states_increase(rpool->addr.p.dyn->pfid_kt,
naddr, af) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG,
"pf: pf_map_addr_states_increase: "
"selected address ");
pf_print_host(naddr, 0, af);
addlog(". Failed to increase count!\n");
}
return (-1);
}
}
return (0);
}
int
pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
struct pf_src_node **sns, struct pf_rule **nr)
{
struct pf_addr naddr;
u_int16_t nport;
#ifdef INET6
if (pd->af != pd->naf)
return (pf_get_transaddr_af(r, pd, sns));
#endif /* INET6 */
if (r->nat.addr.type != PF_ADDR_NONE) {
/* XXX is this right? what if rtable is changed at the same
* XXX time? where do I need to figure out the sport? */
nport = 0;
if (pf_get_sport(pd, r, &naddr, &nport,
r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
DPFPRINTF(LOG_NOTICE,
"pf: NAT proxy port allocation (%u-%u) failed",
r->nat.proxy_port[0],
r->nat.proxy_port[1]);
return (-1);
}
*nr = r;
pf_addrcpy(&pd->nsaddr, &naddr, pd->af);
pd->nsport = nport;
}
if (r->rdr.addr.type != PF_ADDR_NONE) {
if (pf_map_addr(pd->af, r, &pd->nsaddr, &naddr, NULL, sns,
&r->rdr, PF_SN_RDR))
return (-1);
if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
pf_poolmask(&naddr, &naddr, &r->rdr.addr.v.a.mask,
&pd->ndaddr, pd->af);
nport = 0;
if (r->rdr.proxy_port[1]) {
u_int32_t tmp_nport;
u_int16_t div;
div = r->rdr.proxy_port[1] - r->rdr.proxy_port[0] + 1;
div = (div == 0) ? 1 : div;
tmp_nport = ((ntohs(pd->ndport) - ntohs(r->dst.port[0])) % div) +
r->rdr.proxy_port[0];
/* wrap around if necessary */
if (tmp_nport > 65535)
tmp_nport -= 65535;
nport = htons((u_int16_t)tmp_nport);
} else if (r->rdr.proxy_port[0])
nport = htons(r->rdr.proxy_port[0]);
*nr = r;
pf_addrcpy(&pd->ndaddr, &naddr, pd->af);
if (nport)
pd->ndport = nport;
}
return (0);
}
#ifdef INET6
int
pf_get_transaddr_af(struct pf_rule *r, struct pf_pdesc *pd,
struct pf_src_node **sns)
{
struct pf_addr ndaddr, nsaddr, naddr;
u_int16_t nport;
int prefixlen = 96;
if (pf_status.debug >= LOG_INFO) {
log(LOG_INFO, "pf: af-to %s %s, ",
pd->naf == AF_INET ? "inet" : "inet6",
r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr");
pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
addlog(" -> ");
pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
addlog("\n");
}
if (r->nat.addr.type == PF_ADDR_NONE)
panic("pf_get_transaddr_af: no nat pool for source address");
/* get source address and port */
nport = 0;
if (pf_get_sport(pd, r, &nsaddr, &nport,
r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
DPFPRINTF(LOG_NOTICE,
"pf: af-to NAT proxy port allocation (%u-%u) failed",
r->nat.proxy_port[0],
r->nat.proxy_port[1]);
return (-1);
}
pd->nsport = nport;
if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
if (pd->dir == PF_IN) {
pd->ndport = ntohs(pd->ndport);
if (pd->ndport == ICMP6_ECHO_REQUEST)
pd->ndport = ICMP_ECHO;
else if (pd->ndport == ICMP6_ECHO_REPLY)
pd->ndport = ICMP_ECHOREPLY;
pd->ndport = htons(pd->ndport);
} else {
pd->nsport = ntohs(pd->nsport);
if (pd->nsport == ICMP6_ECHO_REQUEST)
pd->nsport = ICMP_ECHO;
else if (pd->nsport == ICMP6_ECHO_REPLY)
pd->nsport = ICMP_ECHOREPLY;
pd->nsport = htons(pd->nsport);
}
} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
if (pd->dir == PF_IN) {
pd->ndport = ntohs(pd->ndport);
if (pd->ndport == ICMP_ECHO)
pd->ndport = ICMP6_ECHO_REQUEST;
else if (pd->ndport == ICMP_ECHOREPLY)
pd->ndport = ICMP6_ECHO_REPLY;
pd->ndport = htons(pd->ndport);
} else {
pd->nsport = ntohs(pd->nsport);
if (pd->nsport == ICMP_ECHO)
pd->nsport = ICMP6_ECHO_REQUEST;
else if (pd->nsport == ICMP_ECHOREPLY)
pd->nsport = ICMP6_ECHO_REPLY;
pd->nsport = htons(pd->nsport);
}
}
/* get the destination address and port */
if (r->rdr.addr.type != PF_ADDR_NONE) {
if (pf_map_addr(pd->naf, r, &nsaddr, &naddr, NULL, sns,
&r->rdr, PF_SN_RDR))
return (-1);
if (r->rdr.proxy_port[0])
pd->ndport = htons(r->rdr.proxy_port[0]);
if (pd->naf == AF_INET) {
/* The prefix is the IPv4 rdr address */
prefixlen = in_mask2len((struct in_addr *)
&r->rdr.addr.v.a.mask);
inet_nat46(pd->naf, &pd->ndaddr,
&ndaddr, &naddr, prefixlen);
} else {
/* The prefix is the IPv6 rdr address */
prefixlen =
in6_mask2len((struct in6_addr *)
&r->rdr.addr.v.a.mask, NULL);
inet_nat64(pd->naf, &pd->ndaddr,
&ndaddr, &naddr, prefixlen);
}
} else {
if (pd->naf == AF_INET) {
/* The prefix is the IPv6 dst address */
prefixlen =
in6_mask2len((struct in6_addr *)
&r->dst.addr.v.a.mask, NULL);
if (prefixlen < 32)
prefixlen = 96;
inet_nat64(pd->naf, &pd->ndaddr,
&ndaddr, &pd->ndaddr, prefixlen);
} else {
/*
* The prefix is the IPv6 nat address
* (that was stored in pd->nsaddr)
*/
prefixlen = in6_mask2len((struct in6_addr *)
&r->nat.addr.v.a.mask, NULL);
if (prefixlen > 96)
prefixlen = 96;
inet_nat64(pd->naf, &pd->ndaddr,
&ndaddr, &nsaddr, prefixlen);
}
}
pf_addrcpy(&pd->nsaddr, &nsaddr, pd->naf);
pf_addrcpy(&pd->ndaddr, &ndaddr, pd->naf);
if (pf_status.debug >= LOG_INFO) {
log(LOG_INFO, "pf: af-to %s %s done, prefixlen %d, ",
pd->naf == AF_INET ? "inet" : "inet6",
r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr",
prefixlen);
pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
addlog(" -> ");
pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
addlog("\n");
}
return (0);
}
#endif /* INET6 */
int
pf_postprocess_addr(struct pf_state *cur)
{
struct pf_rule *nr;
struct pf_state_key *sks;
struct pf_pool rpool;
struct pf_addr lookup_addr;
int slbcount = -1;
nr = cur->natrule.ptr;
if (nr == NULL)
return (0);
/* decrease counter */
sks = cur->key[PF_SK_STACK];
/* check for outgoing or ingoing balancing */
if (nr->rt == PF_ROUTETO)
lookup_addr = cur->rt_addr;
else if (sks != NULL)
lookup_addr = sks->addr[1];
else {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: %s: unable to obtain address",
__func__);
}
return (1);
}
/* check for appropriate pool */
if (nr->rdr.addr.type != PF_ADDR_NONE)
rpool = nr->rdr;
else if (nr->nat.addr.type != PF_ADDR_NONE)
rpool = nr->nat;
else if (nr->route.addr.type != PF_ADDR_NONE)
rpool = nr->route;
else
return (0);
if (((rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_LEASTSTATES))
return (0);
if (rpool.addr.type == PF_ADDR_TABLE) {
if ((slbcount = pfr_states_decrease(
rpool.addr.p.tbl,
&lookup_addr, sks->af)) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: %s: selected address ",
__func__);
pf_print_host(&lookup_addr,
sks->port[0], sks->af);
addlog(". Failed to "
"decrease count!\n");
}
return (1);
}
} else if (rpool.addr.type == PF_ADDR_DYNIFTL) {
if ((slbcount = pfr_states_decrease(
rpool.addr.p.dyn->pfid_kt,
&lookup_addr, sks->af)) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: %s: selected address ",
__func__);
pf_print_host(&lookup_addr,
sks->port[0], sks->af);
addlog(". Failed to "
"decrease count!\n");
}
return (1);
}
}
if (slbcount > -1) {
if (pf_status.debug >= LOG_INFO) {
log(LOG_INFO, "pf: %s: selected address ", __func__);
pf_print_host(&lookup_addr, sks->port[0],
sks->af);
addlog(" decreased state count to %u\n",
slbcount);
}
}
return (0);
}
640
626
20
20
20
20
20
20
5
20
5
20
2
20
5
18
3
20
1
1
2
2
2
2
2
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
/* $OpenBSD: ufs_dirhash.c,v 1.42 2019/03/15 05:42:38 kevlo Exp $ */
/*
* Copyright (c) 2001, 2002 Ian Dowse. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This implements a hash-based lookup scheme for UFS directories.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/mutex.h>
#include <crypto/siphash.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/dirhash.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#define WRAPINCR(val, limit) (((val) + 1 == (limit)) ? 0 : ((val) + 1))
#define WRAPDECR(val, limit) (((val) == 0) ? ((limit) - 1) : ((val) - 1))
#define OFSFMT(ip) ((ip)->i_ump->um_maxsymlinklen == 0)
#define BLKFREE2IDX(n) ((n) > DH_NFSTATS ? DH_NFSTATS : (n))
int ufs_mindirhashsize;
int ufs_dirhashmaxmem;
int ufs_dirhashmem;
int ufs_dirhashcheck;
SIPHASH_KEY ufsdirhash_key;
int ufsdirhash_hash(struct dirhash *dh, char *name, int namelen);
void ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff);
void ufsdirhash_delslot(struct dirhash *dh, int slot);
int ufsdirhash_findslot(struct dirhash *dh, char *name, int namelen,
doff_t offset);
doff_t ufsdirhash_getprev(struct direct *dp, doff_t offset);
int ufsdirhash_recycle(int wanted);
struct pool ufsdirhash_pool;
#define DIRHASHLIST_LOCK() rw_enter_write(&ufsdirhash_mtx)
#define DIRHASHLIST_UNLOCK() rw_exit_write(&ufsdirhash_mtx)
#define DIRHASH_LOCK(dh) rw_enter_write(&(dh)->dh_mtx)
#define DIRHASH_UNLOCK(dh) rw_exit_write(&(dh)->dh_mtx)
#define DIRHASH_BLKALLOC_WAITOK() pool_get(&ufsdirhash_pool, PR_WAITOK)
#define DIRHASH_BLKFREE(v) pool_put(&ufsdirhash_pool, v)
#define mtx_assert(l, f) /* nothing */
#define DIRHASH_ASSERT(e, m) KASSERT((e))
/* Dirhash list; recently-used entries are near the tail. */
TAILQ_HEAD(, dirhash) ufsdirhash_list;
/* Protects: ufsdirhash_list, `dh_list' field, ufs_dirhashmem. */
struct rwlock ufsdirhash_mtx;
/*
* Locking order:
* ufsdirhash_mtx
* dh_mtx
*
* The dh_mtx mutex should be acquired either via the inode lock, or via
* ufsdirhash_mtx. Only the owner of the inode may free the associated
* dirhash, but anything can steal its memory and set dh_hash to NULL.
*/
/*
* Attempt to build up a hash table for the directory contents in
* inode 'ip'. Returns 0 on success, or -1 of the operation failed.
*/
int
ufsdirhash_build(struct inode *ip)
{
struct dirhash *dh;
struct buf *bp = NULL;
struct direct *ep;
struct vnode *vp;
doff_t bmask, pos;
int dirblocks, i, j, memreqd, nblocks, narrays, nslots, slot;
/* Check if we can/should use dirhash. */
if (ip->i_dirhash == NULL) {
if (DIP(ip, size) < ufs_mindirhashsize || OFSFMT(ip))
return (-1);
} else {
/* Hash exists, but sysctls could have changed. */
if (DIP(ip, size) < ufs_mindirhashsize ||
ufs_dirhashmem > ufs_dirhashmaxmem) {
ufsdirhash_free(ip);
return (-1);
}
/* Check if hash exists and is intact (note: unlocked read). */
if (ip->i_dirhash->dh_hash != NULL)
return (0);
/* Free the old, recycled hash and build a new one. */
ufsdirhash_free(ip);
}
/* Don't hash removed directories. */
if (ip->i_effnlink == 0)
return (-1);
vp = ip->i_vnode;
/* Allocate 50% more entries than this dir size could ever need. */
DIRHASH_ASSERT(DIP(ip, size) >= DIRBLKSIZ, ("ufsdirhash_build size"));
nslots = DIP(ip, size) / DIRECTSIZ(1);
nslots = (nslots * 3 + 1) / 2;
narrays = howmany(nslots, DH_NBLKOFF);
nslots = narrays * DH_NBLKOFF;
dirblocks = howmany(DIP(ip, size), DIRBLKSIZ);
nblocks = (dirblocks * 3 + 1) / 2;
memreqd = sizeof(*dh) + narrays * sizeof(*dh->dh_hash) +
narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
nblocks * sizeof(*dh->dh_blkfree);
DIRHASHLIST_LOCK();
if (memreqd + ufs_dirhashmem > ufs_dirhashmaxmem) {
DIRHASHLIST_UNLOCK();
if (memreqd > ufs_dirhashmaxmem / 2)
return (-1);
/* Try to free some space. */
if (ufsdirhash_recycle(memreqd) != 0)
return (-1);
/* Enough was freed, and list has been locked. */
}
ufs_dirhashmem += memreqd;
DIRHASHLIST_UNLOCK();
/*
* Use non-blocking mallocs so that we will revert to a linear
* lookup on failure rather than potentially blocking forever.
*/
dh = malloc(sizeof(*dh), M_DIRHASH, M_NOWAIT|M_ZERO);
if (dh == NULL) {
DIRHASHLIST_LOCK();
ufs_dirhashmem -= memreqd;
DIRHASHLIST_UNLOCK();
return (-1);
}
dh->dh_hash = mallocarray(narrays, sizeof(dh->dh_hash[0]),
M_DIRHASH, M_NOWAIT|M_ZERO);
dh->dh_blkfree = mallocarray(nblocks, sizeof(dh->dh_blkfree[0]),
M_DIRHASH, M_NOWAIT | M_ZERO);
if (dh->dh_hash == NULL || dh->dh_blkfree == NULL)
goto fail;
for (i = 0; i < narrays; i++) {
if ((dh->dh_hash[i] = DIRHASH_BLKALLOC_WAITOK()) == NULL)
goto fail;
for (j = 0; j < DH_NBLKOFF; j++)
dh->dh_hash[i][j] = DIRHASH_EMPTY;
}
/* Initialise the hash table and block statistics. */
rw_init(&dh->dh_mtx, "dirhash");
dh->dh_narrays = narrays;
dh->dh_hlen = nslots;
dh->dh_nblk = nblocks;
dh->dh_dirblks = dirblocks;
for (i = 0; i < dirblocks; i++)
dh->dh_blkfree[i] = DIRBLKSIZ / DIRALIGN;
for (i = 0; i < DH_NFSTATS; i++)
dh->dh_firstfree[i] = -1;
dh->dh_firstfree[DH_NFSTATS] = 0;
dh->dh_seqopt = 0;
dh->dh_seqoff = 0;
dh->dh_score = DH_SCOREINIT;
ip->i_dirhash = dh;
bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
pos = 0;
while (pos < DIP(ip, size)) {
/* If necessary, get the next directory block. */
if ((pos & bmask) == 0) {
if (bp != NULL)
brelse(bp);
if (UFS_BUFATOFF(ip, (off_t)pos, NULL, &bp) != 0)
goto fail;
}
/* Add this entry to the hash. */
ep = (struct direct *)((char *)bp->b_data + (pos & bmask));
if (ep->d_reclen == 0 || ep->d_reclen >
DIRBLKSIZ - (pos & (DIRBLKSIZ - 1))) {
/* Corrupted directory. */
brelse(bp);
goto fail;
}
if (ep->d_ino != 0) {
/* Add the entry (simplified ufsdirhash_add). */
slot = ufsdirhash_hash(dh, ep->d_name, ep->d_namlen);
while (DH_ENTRY(dh, slot) != DIRHASH_EMPTY)
slot = WRAPINCR(slot, dh->dh_hlen);
dh->dh_hused++;
DH_ENTRY(dh, slot) = pos;
ufsdirhash_adjfree(dh, pos, -DIRSIZ(0, ep));
}
pos += ep->d_reclen;
}
if (bp != NULL)
brelse(bp);
DIRHASHLIST_LOCK();
TAILQ_INSERT_TAIL(&ufsdirhash_list, dh, dh_list);
dh->dh_onlist = 1;
DIRHASHLIST_UNLOCK();
return (0);
fail:
if (dh->dh_hash != NULL) {
for (i = 0; i < narrays; i++)
if (dh->dh_hash[i] != NULL)
DIRHASH_BLKFREE(dh->dh_hash[i]);
free(dh->dh_hash, M_DIRHASH,
narrays * sizeof(dh->dh_hash[0]));
}
if (dh->dh_blkfree != NULL)
free(dh->dh_blkfree, M_DIRHASH,
nblocks * sizeof(dh->dh_blkfree[0]));
free(dh, M_DIRHASH, sizeof(*dh));
ip->i_dirhash = NULL;
DIRHASHLIST_LOCK();
ufs_dirhashmem -= memreqd;
DIRHASHLIST_UNLOCK();
return (-1);
}
/*
* Free any hash table associated with inode 'ip'.
*/
void
ufsdirhash_free(struct inode *ip)
{
struct dirhash *dh;
int i, mem;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASHLIST_LOCK();
DIRHASH_LOCK(dh);
if (dh->dh_onlist)
TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
DIRHASH_UNLOCK(dh);
DIRHASHLIST_UNLOCK();
/* The dirhash pointed to by 'dh' is exclusively ours now. */
mem = sizeof(*dh);
if (dh->dh_hash != NULL) {
for (i = 0; i < dh->dh_narrays; i++)
DIRHASH_BLKFREE(dh->dh_hash[i]);
free(dh->dh_hash, M_DIRHASH,
dh->dh_narrays * sizeof(dh->dh_hash[0]));
free(dh->dh_blkfree, M_DIRHASH,
dh->dh_nblk * sizeof(dh->dh_blkfree[0]));
mem += dh->dh_narrays * sizeof(*dh->dh_hash) +
dh->dh_narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
dh->dh_nblk * sizeof(*dh->dh_blkfree);
}
free(dh, M_DIRHASH, sizeof(*dh));
ip->i_dirhash = NULL;
DIRHASHLIST_LOCK();
ufs_dirhashmem -= mem;
DIRHASHLIST_UNLOCK();
}
/*
* Find the offset of the specified name within the given inode.
* Returns 0 on success, ENOENT if the entry does not exist, or
* EJUSTRETURN if the caller should revert to a linear search.
*
* If successful, the directory offset is stored in *offp, and a
* pointer to a struct buf containing the entry is stored in *bpp. If
* prevoffp is non-NULL, the offset of the previous entry within
* the DIRBLKSIZ-sized block is stored in *prevoffp (if the entry
* is the first in a block, the start of the block is used).
*/
int
ufsdirhash_lookup(struct inode *ip, char *name, int namelen, doff_t *offp,
struct buf **bpp, doff_t *prevoffp)
{
struct dirhash *dh, *dh_next;
struct direct *dp;
struct vnode *vp;
struct buf *bp;
doff_t blkoff, bmask, offset, prevoff;
int i, slot;
if ((dh = ip->i_dirhash) == NULL)
return (EJUSTRETURN);
/*
* Move this dirhash towards the end of the list if it has a
* score higher than the next entry, and acquire the dh_mtx.
* Optimise the case where it's already the last by performing
* an unlocked read of the TAILQ_NEXT pointer.
*
* In both cases, end up holding just dh_mtx.
*/
if (TAILQ_NEXT(dh, dh_list) != NULL) {
DIRHASHLIST_LOCK();
DIRHASH_LOCK(dh);
/*
* If the new score will be greater than that of the next
* entry, then move this entry past it. With both mutexes
* held, dh_next won't go away, but its dh_score could
* change; that's not important since it is just a hint.
*/
if (dh->dh_hash != NULL &&
(dh_next = TAILQ_NEXT(dh, dh_list)) != NULL &&
dh->dh_score >= dh_next->dh_score) {
DIRHASH_ASSERT(dh->dh_onlist, ("dirhash: not on list"));
TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
TAILQ_INSERT_AFTER(&ufsdirhash_list, dh_next, dh,
dh_list);
}
DIRHASHLIST_UNLOCK();
} else {
/* Already the last, though that could change as we wait. */
DIRHASH_LOCK(dh);
}
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return (EJUSTRETURN);
}
/* Update the score. */
if (dh->dh_score < DH_SCOREMAX)
dh->dh_score++;
vp = ip->i_vnode;
bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
blkoff = -1;
bp = NULL;
restart:
slot = ufsdirhash_hash(dh, name, namelen);
if (dh->dh_seqopt) {
/*
* Sequential access optimisation. dh_seqoff contains the
* offset of the directory entry immediately following
* the last entry that was looked up. Check if this offset
* appears in the hash chain for the name we are looking for.
*/
for (i = slot; (offset = DH_ENTRY(dh, i)) != DIRHASH_EMPTY;
i = WRAPINCR(i, dh->dh_hlen))
if (offset == dh->dh_seqoff)
break;
if (offset == dh->dh_seqoff) {
/*
* We found an entry with the expected offset. This
* is probably the entry we want, but if not, the
* code below will turn off seqopt and retry.
*/
slot = i;
} else
dh->dh_seqopt = 0;
}
for (; (offset = DH_ENTRY(dh, slot)) != DIRHASH_EMPTY;
slot = WRAPINCR(slot, dh->dh_hlen)) {
if (offset == DIRHASH_DEL)
continue;
DIRHASH_UNLOCK(dh);
if (offset < 0 || offset >= DIP(ip, size))
panic("ufsdirhash_lookup: bad offset in hash array");
if ((offset & ~bmask) != blkoff) {
if (bp != NULL)
brelse(bp);
blkoff = offset & ~bmask;
if (UFS_BUFATOFF(ip, (off_t)blkoff, NULL, &bp) != 0)
return (EJUSTRETURN);
}
dp = (struct direct *)(bp->b_data + (offset & bmask));
if (dp->d_reclen == 0 || dp->d_reclen >
DIRBLKSIZ - (offset & (DIRBLKSIZ - 1))) {
/* Corrupted directory. */
brelse(bp);
return (EJUSTRETURN);
}
if (dp->d_namlen == namelen &&
memcmp(dp->d_name, name, namelen) == 0) {
/* Found. Get the prev offset if needed. */
if (prevoffp != NULL) {
if (offset & (DIRBLKSIZ - 1)) {
prevoff = ufsdirhash_getprev(dp,
offset);
if (prevoff == -1) {
brelse(bp);
return (EJUSTRETURN);
}
} else
prevoff = offset;
*prevoffp = prevoff;
}
/* Check for sequential access, and update offset. */
if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset)
dh->dh_seqopt = 1;
dh->dh_seqoff = offset + DIRSIZ(0, dp);
*bpp = bp;
*offp = offset;
return (0);
}
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
if (bp != NULL)
brelse(bp);
ufsdirhash_free(ip);
return (EJUSTRETURN);
}
/*
* When the name doesn't match in the seqopt case, go back
* and search normally.
*/
if (dh->dh_seqopt) {
dh->dh_seqopt = 0;
goto restart;
}
}
DIRHASH_UNLOCK(dh);
if (bp != NULL)
brelse(bp);
return (ENOENT);
}
/*
* Find a directory block with room for 'slotneeded' bytes. Returns
* the offset of the directory entry that begins the free space.
* This will either be the offset of an existing entry that has free
* space at the end, or the offset of an entry with d_ino == 0 at
* the start of a DIRBLKSIZ block.
*
* To use the space, the caller may need to compact existing entries in
* the directory. The total number of bytes in all of the entries involved
* in the compaction is stored in *slotsize. In other words, all of
* the entries that must be compacted are exactly contained in the
* region beginning at the returned offset and spanning *slotsize bytes.
*
* Returns -1 if no space was found, indicating that the directory
* must be extended.
*/
doff_t
ufsdirhash_findfree(struct inode *ip, int slotneeded, int *slotsize)
{
struct direct *dp;
struct dirhash *dh;
struct buf *bp;
doff_t pos, slotstart;
int dirblock, error, freebytes, i;
if ((dh = ip->i_dirhash) == NULL)
return (-1);
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return (-1);
}
/* Find a directory block with the desired free space. */
dirblock = -1;
for (i = howmany(slotneeded, DIRALIGN); i <= DH_NFSTATS; i++)
if ((dirblock = dh->dh_firstfree[i]) != -1)
break;
if (dirblock == -1) {
DIRHASH_UNLOCK(dh);
return (-1);
}
DIRHASH_ASSERT(dirblock < dh->dh_nblk &&
dh->dh_blkfree[dirblock] >= howmany(slotneeded, DIRALIGN),
("ufsdirhash_findfree: bad stats"));
DIRHASH_UNLOCK(dh);
pos = dirblock * DIRBLKSIZ;
error = UFS_BUFATOFF(ip, (off_t)pos, (char **)&dp, &bp);
if (error)
return (-1);
/* Find the first entry with free space. */
for (i = 0; i < DIRBLKSIZ; ) {
if (dp->d_reclen == 0) {
brelse(bp);
return (-1);
}
if (dp->d_ino == 0 || dp->d_reclen > DIRSIZ(0, dp))
break;
i += dp->d_reclen;
dp = (struct direct *)((char *)dp + dp->d_reclen);
}
if (i > DIRBLKSIZ) {
brelse(bp);
return (-1);
}
slotstart = pos + i;
/* Find the range of entries needed to get enough space */
freebytes = 0;
while (i < DIRBLKSIZ && freebytes < slotneeded) {
freebytes += dp->d_reclen;
if (dp->d_ino != 0)
freebytes -= DIRSIZ(0, dp);
if (dp->d_reclen == 0) {
brelse(bp);
return (-1);
}
i += dp->d_reclen;
dp = (struct direct *)((char *)dp + dp->d_reclen);
}
if (i > DIRBLKSIZ) {
brelse(bp);
return (-1);
}
if (freebytes < slotneeded)
panic("ufsdirhash_findfree: free mismatch");
brelse(bp);
*slotsize = pos + i - slotstart;
return (slotstart);
}
/*
* Return the start of the unused space at the end of a directory, or
* -1 if there are no trailing unused blocks.
*/
doff_t
ufsdirhash_enduseful(struct inode *ip)
{
struct dirhash *dh;
int i;
if ((dh = ip->i_dirhash) == NULL)
return (-1);
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return (-1);
}
if (dh->dh_blkfree[dh->dh_dirblks - 1] != DIRBLKSIZ / DIRALIGN) {
DIRHASH_UNLOCK(dh);
return (-1);
}
for (i = dh->dh_dirblks - 1; i >= 0; i--)
if (dh->dh_blkfree[i] != DIRBLKSIZ / DIRALIGN)
break;
DIRHASH_UNLOCK(dh);
return ((doff_t)(i + 1) * DIRBLKSIZ);
}
/*
* Insert information into the hash about a new directory entry. dirp
* points to a struct direct containing the entry, and offset specifies
* the offset of this entry.
*/
void
ufsdirhash_add(struct inode *ip, struct direct *dirp, doff_t offset)
{
struct dirhash *dh;
int slot;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
DIRHASH_ASSERT(offset < dh->dh_dirblks * DIRBLKSIZ,
("ufsdirhash_add: bad offset"));
/*
* Normal hash usage is < 66%. If the usage gets too high then
* remove the hash entirely and let it be rebuilt later.
*/
if (dh->dh_hused >= (dh->dh_hlen * 3) / 4) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
/* Find a free hash slot (empty or deleted), and add the entry. */
slot = ufsdirhash_hash(dh, dirp->d_name, dirp->d_namlen);
while (DH_ENTRY(dh, slot) >= 0)
slot = WRAPINCR(slot, dh->dh_hlen);
if (DH_ENTRY(dh, slot) == DIRHASH_EMPTY)
dh->dh_hused++;
DH_ENTRY(dh, slot) = offset;
/* Update the per-block summary info. */
ufsdirhash_adjfree(dh, offset, -DIRSIZ(0, dirp));
DIRHASH_UNLOCK(dh);
}
/*
* Remove the specified directory entry from the hash. The entry to remove
* is defined by the name in `dirp', which must exist at the specified
* `offset' within the directory.
*/
void
ufsdirhash_remove(struct inode *ip, struct direct *dirp, doff_t offset)
{
struct dirhash *dh;
int slot;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
DIRHASH_ASSERT(offset < dh->dh_dirblks * DIRBLKSIZ,
("ufsdirhash_remove: bad offset"));
/* Find the entry */
slot = ufsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, offset);
/* Remove the hash entry. */
ufsdirhash_delslot(dh, slot);
/* Update the per-block summary info. */
ufsdirhash_adjfree(dh, offset, DIRSIZ(0, dirp));
DIRHASH_UNLOCK(dh);
}
/*
* Change the offset associated with a directory entry in the hash. Used
* when compacting directory blocks.
*/
void
ufsdirhash_move(struct inode *ip, struct direct *dirp, doff_t oldoff,
doff_t newoff)
{
struct dirhash *dh;
int slot;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
DIRHASH_ASSERT(oldoff < dh->dh_dirblks * DIRBLKSIZ &&
newoff < dh->dh_dirblks * DIRBLKSIZ,
("ufsdirhash_move: bad offset"));
/* Find the entry, and update the offset. */
slot = ufsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, oldoff);
DH_ENTRY(dh, slot) = newoff;
DIRHASH_UNLOCK(dh);
}
/*
* Inform dirhash that the directory has grown by one block that
* begins at offset (i.e. the new length is offset + DIRBLKSIZ).
*/
void
ufsdirhash_newblk(struct inode *ip, doff_t offset)
{
struct dirhash *dh;
int block;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
DIRHASH_ASSERT(offset == dh->dh_dirblks * DIRBLKSIZ,
("ufsdirhash_newblk: bad offset"));
block = offset / DIRBLKSIZ;
if (block >= dh->dh_nblk) {
/* Out of space; must rebuild. */
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
dh->dh_dirblks = block + 1;
/* Account for the new free block. */
dh->dh_blkfree[block] = DIRBLKSIZ / DIRALIGN;
if (dh->dh_firstfree[DH_NFSTATS] == -1)
dh->dh_firstfree[DH_NFSTATS] = block;
DIRHASH_UNLOCK(dh);
}
/*
* Inform dirhash that the directory is being truncated.
*/
void
ufsdirhash_dirtrunc(struct inode *ip, doff_t offset)
{
struct dirhash *dh;
int block, i;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
DIRHASH_ASSERT(offset <= dh->dh_dirblks * DIRBLKSIZ,
("ufsdirhash_dirtrunc: bad offset"));
block = howmany(offset, DIRBLKSIZ);
/*
* If the directory shrinks to less than 1/8 of dh_nblk blocks
* (about 20% of its original size due to the 50% extra added in
* ufsdirhash_build) then free it, and let the caller rebuild
* if necessary.
*/
if (block < dh->dh_nblk / 8 && dh->dh_narrays > 1) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
/*
* Remove any `first free' information pertaining to the
* truncated blocks. All blocks we're removing should be
* completely unused.
*/
if (dh->dh_firstfree[DH_NFSTATS] >= block)
dh->dh_firstfree[DH_NFSTATS] = -1;
for (i = block; i < dh->dh_dirblks; i++)
if (dh->dh_blkfree[i] != DIRBLKSIZ / DIRALIGN)
panic("ufsdirhash_dirtrunc: blocks in use");
for (i = 0; i < DH_NFSTATS; i++)
if (dh->dh_firstfree[i] >= block)
panic("ufsdirhash_dirtrunc: first free corrupt");
dh->dh_dirblks = block;
DIRHASH_UNLOCK(dh);
}
/*
* Debugging function to check that the dirhash information about
* a directory block matches its actual contents. Panics if a mismatch
* is detected.
*
* On entry, `buf' should point to the start of an in-core
* DIRBLKSIZ-sized directory block, and `offset' should contain the
* offset from the start of the directory of that block.
*/
void
ufsdirhash_checkblock(struct inode *ip, char *buf, doff_t offset)
{
struct dirhash *dh;
struct direct *dp;
int block, ffslot, i, nfree;
if (!ufs_dirhashcheck)
return;
if ((dh = ip->i_dirhash) == NULL)
return;
DIRHASH_LOCK(dh);
if (dh->dh_hash == NULL) {
DIRHASH_UNLOCK(dh);
ufsdirhash_free(ip);
return;
}
block = offset / DIRBLKSIZ;
if ((offset & (DIRBLKSIZ - 1)) != 0 || block >= dh->dh_dirblks)
panic("ufsdirhash_checkblock: bad offset");
nfree = 0;
for (i = 0; i < DIRBLKSIZ; i += dp->d_reclen) {
dp = (struct direct *)(buf + i);
if (dp->d_reclen == 0 || i + dp->d_reclen > DIRBLKSIZ)
panic("ufsdirhash_checkblock: bad dir");
if (dp->d_ino == 0) {
#if 0
/*
* XXX entries with d_ino == 0 should only occur
* at the start of a DIRBLKSIZ block. However the
* ufs code is tolerant of such entries at other
* offsets, and fsck does not fix them.
*/
if (i != 0)
panic("ufsdirhash_checkblock: bad dir inode");
#endif
nfree += dp->d_reclen;
continue;
}
/* Check that the entry exists (will panic if it doesn't). */
ufsdirhash_findslot(dh, dp->d_name, dp->d_namlen, offset + i);
nfree += dp->d_reclen - DIRSIZ(0, dp);
}
if (i != DIRBLKSIZ)
panic("ufsdirhash_checkblock: bad dir end");
if (dh->dh_blkfree[block] * DIRALIGN != nfree)
panic("ufsdirhash_checkblock: bad free count");
ffslot = BLKFREE2IDX(nfree / DIRALIGN);
for (i = 0; i <= DH_NFSTATS; i++)
if (dh->dh_firstfree[i] == block && i != ffslot)
panic("ufsdirhash_checkblock: bad first-free");
if (dh->dh_firstfree[ffslot] == -1)
panic("ufsdirhash_checkblock: missing first-free entry");
DIRHASH_UNLOCK(dh);
}
/*
* Hash the specified filename into a dirhash slot.
*/
int
ufsdirhash_hash(struct dirhash *dh, char *name, int namelen)
{
return SipHash24(&ufsdirhash_key, name, namelen) % dh->dh_hlen;
}
/*
* Adjust the number of free bytes in the block containing `offset'
* by the value specified by `diff'.
*
* The caller must ensure we have exclusive access to `dh'; normally
* that means that dh_mtx should be held, but this is also called
* from ufsdirhash_build() where exclusive access can be assumed.
*/
void
ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff)
{
int block, i, nfidx, ofidx;
/* Update the per-block summary info. */
block = offset / DIRBLKSIZ;
DIRHASH_ASSERT(block < dh->dh_nblk && block < dh->dh_dirblks,
("dirhash bad offset"));
ofidx = BLKFREE2IDX(dh->dh_blkfree[block]);
dh->dh_blkfree[block] = (int)dh->dh_blkfree[block] + (diff / DIRALIGN);
nfidx = BLKFREE2IDX(dh->dh_blkfree[block]);
/* Update the `first free' list if necessary. */
if (ofidx != nfidx) {
/* If removing, scan forward for the next block. */
if (dh->dh_firstfree[ofidx] == block) {
for (i = block + 1; i < dh->dh_dirblks; i++)
if (BLKFREE2IDX(dh->dh_blkfree[i]) == ofidx)
break;
dh->dh_firstfree[ofidx] = (i < dh->dh_dirblks) ? i : -1;
}
/* Make this the new `first free' if necessary */
if (dh->dh_firstfree[nfidx] > block ||
dh->dh_firstfree[nfidx] == -1)
dh->dh_firstfree[nfidx] = block;
}
}
/*
* Find the specified name which should have the specified offset.
* Returns a slot number, and panics on failure.
*
* `dh' must be locked on entry and remains so on return.
*/
int
ufsdirhash_findslot(struct dirhash *dh, char *name, int namelen, doff_t offset)
{
int slot;
mtx_assert(&dh->dh_mtx, MA_OWNED);
/* Find the entry. */
DIRHASH_ASSERT(dh->dh_hused < dh->dh_hlen, ("dirhash find full"));
slot = ufsdirhash_hash(dh, name, namelen);
while (DH_ENTRY(dh, slot) != offset &&
DH_ENTRY(dh, slot) != DIRHASH_EMPTY)
slot = WRAPINCR(slot, dh->dh_hlen);
if (DH_ENTRY(dh, slot) != offset)
panic("ufsdirhash_findslot: '%.*s' not found", namelen, name);
return (slot);
}
/*
* Remove the entry corresponding to the specified slot from the hash array.
*
* `dh' must be locked on entry and remains so on return.
*/
void
ufsdirhash_delslot(struct dirhash *dh, int slot)
{
int i;
mtx_assert(&dh->dh_mtx, MA_OWNED);
/* Mark the entry as deleted. */
DH_ENTRY(dh, slot) = DIRHASH_DEL;
/* If this is the end of a chain of DIRHASH_DEL slots, remove them. */
for (i = slot; DH_ENTRY(dh, i) == DIRHASH_DEL; )
i = WRAPINCR(i, dh->dh_hlen);
if (DH_ENTRY(dh, i) == DIRHASH_EMPTY) {
i = WRAPDECR(i, dh->dh_hlen);
while (DH_ENTRY(dh, i) == DIRHASH_DEL) {
DH_ENTRY(dh, i) = DIRHASH_EMPTY;
dh->dh_hused--;
i = WRAPDECR(i, dh->dh_hlen);
}
DIRHASH_ASSERT(dh->dh_hused >= 0, ("ufsdirhash_delslot neg hlen"));
}
}
/*
* Given a directory entry and its offset, find the offset of the
* previous entry in the same DIRBLKSIZ-sized block. Returns an
* offset, or -1 if there is no previous entry in the block or some
* other problem occurred.
*/
doff_t
ufsdirhash_getprev(struct direct *dirp, doff_t offset)
{
struct direct *dp;
char *blkbuf;
doff_t blkoff, prevoff;
int entrypos, i;
blkoff = offset & ~(DIRBLKSIZ - 1); /* offset of start of block */
entrypos = offset & (DIRBLKSIZ - 1); /* entry relative to block */
blkbuf = (char *)dirp - entrypos;
prevoff = blkoff;
/* If `offset' is the start of a block, there is no previous entry. */
if (entrypos == 0)
return (-1);
/* Scan from the start of the block until we get to the entry. */
for (i = 0; i < entrypos; i += dp->d_reclen) {
dp = (struct direct *)(blkbuf + i);
if (dp->d_reclen == 0 || i + dp->d_reclen > entrypos)
return (-1); /* Corrupted directory. */
prevoff = blkoff + i;
}
return (prevoff);
}
/*
* Try to free up `wanted' bytes by stealing memory from existing
* dirhashes. Returns zero with list locked if successful.
*/
int
ufsdirhash_recycle(int wanted)
{
struct dirhash *dh;
doff_t **hash;
u_int8_t *blkfree;
int i, mem, narrays, nblk;
DIRHASHLIST_LOCK();
while (wanted + ufs_dirhashmem > ufs_dirhashmaxmem) {
/* Find a dirhash, and lock it. */
if ((dh = TAILQ_FIRST(&ufsdirhash_list)) == NULL) {
DIRHASHLIST_UNLOCK();
return (-1);
}
DIRHASH_LOCK(dh);
DIRHASH_ASSERT(dh->dh_hash != NULL, ("dirhash: NULL hash on list"));
/* Decrement the score; only recycle if it becomes zero. */
if (--dh->dh_score > 0) {
DIRHASH_UNLOCK(dh);
DIRHASHLIST_UNLOCK();
return (-1);
}
/* Remove it from the list and detach its memory. */
TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list);
dh->dh_onlist = 0;
hash = dh->dh_hash;
dh->dh_hash = NULL;
blkfree = dh->dh_blkfree;
dh->dh_blkfree = NULL;
narrays = dh->dh_narrays;
nblk = dh->dh_nblk;
mem = narrays * sizeof(*dh->dh_hash) +
narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
dh->dh_nblk * sizeof(*dh->dh_blkfree);
/* Unlock everything, free the detached memory. */
DIRHASH_UNLOCK(dh);
DIRHASHLIST_UNLOCK();
for (i = 0; i < narrays; i++)
DIRHASH_BLKFREE(hash[i]);
free(hash, M_DIRHASH, narrays * sizeof(hash[0]));
free(blkfree, M_DIRHASH, nblk * sizeof(blkfree[0]));
/* Account for the returned memory, and repeat if necessary. */
DIRHASHLIST_LOCK();
ufs_dirhashmem -= mem;
}
/* Success; return with list locked. */
return (0);
}
void
ufsdirhash_init(void)
{
pool_init(&ufsdirhash_pool, DH_NBLKOFF * sizeof(doff_t), 0, IPL_NONE,
PR_WAITOK, "dirhash", NULL);
rw_init(&ufsdirhash_mtx, "dirhash_list");
arc4random_buf(&ufsdirhash_key, sizeof(ufsdirhash_key));
TAILQ_INIT(&ufsdirhash_list);
ufs_dirhashmaxmem = 5 * 1024 * 1024;
ufs_mindirhashsize = 5 * DIRBLKSIZ;
}
void
ufsdirhash_uninit(void)
{
DIRHASH_ASSERT(TAILQ_EMPTY(&ufsdirhash_list), ("ufsdirhash_uninit"));
pool_destroy(&ufsdirhash_pool);
}
1
102
21
102
58
97
19
102
22
96
90
8
15
101
102
7
92
16
29
9
13
4
8
8
88
5
9
29
13
26
51
58
58
47
41
70
89
3
92
9
27
30
22
22
2
29
30
68
9
4
52
28
92
89
14
91
4
90
92
92
86
10
81
89
2
3
87
33
14
55
55
13
87
17
16
85
88
22
84
3
87
9
8
80
10
89
48
40
88
2
12
42
61
78
44
48
78
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
/* $OpenBSD: tcp_output.c,v 1.133 2022/09/03 19:22:19 bluhm Exp $ */
/* $NetBSD: tcp_output.c,v 1.16 1997/06/03 16:17:09 kml Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include "pf.h"
#include "stoeplitz.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/route.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#define TCPOUTFLAGS
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>
#ifdef notyet
extern struct mbuf *m_copypack();
#endif
extern int tcprexmtthresh;
#ifdef TCP_SACK_DEBUG
void tcp_print_holes(struct tcpcb *tp);
void
tcp_print_holes(struct tcpcb *tp)
{
struct sackhole *p = tp->snd_holes;
if (p == NULL)
return;
printf("Hole report: start--end dups rxmit\n");
while (p) {
printf("%x--%x d %d r %x\n", p->start, p->end, p->dups,
p->rxmit);
p = p->next;
}
printf("\n");
}
#endif /* TCP_SACK_DEBUG */
/*
* Returns pointer to a sackhole if there are any pending retransmissions;
* NULL otherwise.
*/
struct sackhole *
tcp_sack_output(struct tcpcb *tp)
{
struct sackhole *p;
if (!tp->sack_enable)
return (NULL);
p = tp->snd_holes;
while (p) {
if (p->dups >= tcprexmtthresh && SEQ_LT(p->rxmit, p->end)) {
if (SEQ_LT(p->rxmit, tp->snd_una)) {/* old SACK hole */
p = p->next;
continue;
}
#ifdef TCP_SACK_DEBUG
if (p)
tcp_print_holes(tp);
#endif
return (p);
}
p = p->next;
}
return (NULL);
}
/*
* After a timeout, the SACK list may be rebuilt. This SACK information
* should be used to avoid retransmitting SACKed data. This function
* traverses the SACK list to see if snd_nxt should be moved forward.
*/
void
tcp_sack_adjust(struct tcpcb *tp)
{
struct sackhole *cur = tp->snd_holes;
if (cur == NULL)
return; /* No holes */
if (SEQ_GEQ(tp->snd_nxt, tp->rcv_lastsack))
return; /* We're already beyond any SACKed blocks */
/*
* Two cases for which we want to advance snd_nxt:
* i) snd_nxt lies between end of one hole and beginning of another
* ii) snd_nxt lies between end of last hole and rcv_lastsack
*/
while (cur->next) {
if (SEQ_LT(tp->snd_nxt, cur->end))
return;
if (SEQ_GEQ(tp->snd_nxt, cur->next->start))
cur = cur->next;
else {
tp->snd_nxt = cur->next->start;
return;
}
}
if (SEQ_LT(tp->snd_nxt, cur->end))
return;
tp->snd_nxt = tp->rcv_lastsack;
return;
}
/*
* Tcp output routine: figure out what should be sent and send it.
*/
int
tcp_output(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
long len, win, txmaxseg;
int off, flags, error;
struct mbuf *m;
struct tcphdr *th;
u_int32_t optbuf[howmany(MAX_TCPOPTLEN, sizeof(u_int32_t))];
u_char *opt = (u_char *)optbuf;
unsigned int optlen, hdrlen, packetlen;
int idle, sendalot = 0;
int i, sack_rxmit = 0;
struct sackhole *p;
uint32_t now;
#ifdef TCP_SIGNATURE
unsigned int sigoff;
#endif /* TCP_SIGNATURE */
#ifdef TCP_ECN
int needect;
#endif
if (tp->t_flags & TF_BLOCKOUTPUT) {
tp->t_flags |= TF_NEEDOUTPUT;
return (0);
} else
tp->t_flags &= ~TF_NEEDOUTPUT;
#if defined(TCP_SIGNATURE) && defined(DIAGNOSTIC)
if (tp->sack_enable && (tp->t_flags & TF_SIGNATURE))
return (EINVAL);
#endif /* defined(TCP_SIGNATURE) && defined(DIAGNOSTIC) */
now = READ_ONCE(tcp_now);
/*
* Determine length of data that should be transmitted,
* and flags that will be used.
* If there is some data or critical controls (SYN, RST)
* to send, then transmit; otherwise, investigate further.
*/
idle = (tp->t_flags & TF_LASTIDLE) || (tp->snd_max == tp->snd_una);
if (idle && (now - tp->t_rcvtime) >= tp->t_rxtcur)
/*
* We have been idle for "a while" and no acks are
* expected to clock out any data we send --
* slow start to get ack "clock" running again.
*/
tp->snd_cwnd = 2 * tp->t_maxseg;
/* remember 'idle' for next invocation of tcp_output */
if (idle && soissending(so)) {
tp->t_flags |= TF_LASTIDLE;
idle = 0;
} else
tp->t_flags &= ~TF_LASTIDLE;
again:
/*
* If we've recently taken a timeout, snd_max will be greater than
* snd_nxt. There may be SACK information that allows us to avoid
* resending already delivered data. Adjust snd_nxt accordingly.
*/
if (tp->sack_enable && SEQ_LT(tp->snd_nxt, tp->snd_max))
tcp_sack_adjust(tp);
off = tp->snd_nxt - tp->snd_una;
win = ulmin(tp->snd_wnd, tp->snd_cwnd);
flags = tcp_outflags[tp->t_state];
/*
* Send any SACK-generated retransmissions. If we're explicitly trying
* to send out new data (when sendalot is 1), bypass this function.
* If we retransmit in fast recovery mode, decrement snd_cwnd, since
* we're replacing a (future) new transmission with a retransmission
* now, and we previously incremented snd_cwnd in tcp_input().
*/
if (tp->sack_enable && !sendalot) {
if (tp->t_dupacks >= tcprexmtthresh &&
(p = tcp_sack_output(tp))) {
off = p->rxmit - tp->snd_una;
sack_rxmit = 1;
/* Coalesce holes into a single retransmission */
len = min(tp->t_maxseg, p->end - p->rxmit);
if (SEQ_LT(tp->snd_una, tp->snd_last))
tp->snd_cwnd -= tp->t_maxseg;
}
}
sendalot = 0;
/*
* If in persist timeout with window of 0, send 1 byte.
* Otherwise, if window is small but nonzero
* and timer expired, we will send what we can
* and go to transmit state.
*/
if (tp->t_force) {
if (win == 0) {
/*
* If we still have some data to send, then
* clear the FIN bit. Usually this would
* happen below when it realizes that we
* aren't sending all the data. However,
* if we have exactly 1 byte of unset data,
* then it won't clear the FIN bit below,
* and if we are in persist state, we wind
* up sending the packet without recording
* that we sent the FIN bit.
*
* We can't just blindly clear the FIN bit,
* because if we don't have any more data
* to send then the probe will be the FIN
* itself.
*/
if (off < so->so_snd.sb_cc)
flags &= ~TH_FIN;
win = 1;
} else {
TCP_TIMER_DISARM(tp, TCPT_PERSIST);
tp->t_rxtshift = 0;
}
}
if (!sack_rxmit) {
len = ulmin(so->so_snd.sb_cc, win) - off;
}
if (len < 0) {
/*
* If FIN has been sent but not acked,
* but we haven't been called to retransmit,
* len will be -1. Otherwise, window shrank
* after we sent into it. If window shrank to 0,
* cancel pending retransmit, pull snd_nxt back
* to (closed) window, and set the persist timer
* if it isn't already going. If the window didn't
* close completely, just wait for an ACK.
*/
len = 0;
if (win == 0) {
TCP_TIMER_DISARM(tp, TCPT_REXMT);
tp->t_rxtshift = 0;
tp->snd_nxt = tp->snd_una;
if (TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0)
tcp_setpersist(tp);
}
}
/*
* Never send more than half a buffer full. This insures that we can
* always keep 2 packets on the wire, no matter what SO_SNDBUF is, and
* therefore acks will never be delayed unless we run out of data to
* transmit.
*/
txmaxseg = ulmin(so->so_snd.sb_hiwat / 2, tp->t_maxseg);
if (len > txmaxseg) {
len = txmaxseg;
sendalot = 1;
}
if (off + len < so->so_snd.sb_cc)
flags &= ~TH_FIN;
win = sbspace(so, &so->so_rcv);
/*
* Sender silly window avoidance. If connection is idle
* and can send all data, a maximum segment,
* at least a maximum default-size segment do it,
* or are forced, do it; otherwise don't bother.
* If peer's buffer is tiny, then send
* when window is at least half open.
* If retransmitting (possibly after persist timer forced us
* to send into a small window), then must resend.
*/
if (len) {
if (len == txmaxseg)
goto send;
if ((idle || (tp->t_flags & TF_NODELAY)) &&
len + off >= so->so_snd.sb_cc && !soissending(so) &&
(tp->t_flags & TF_NOPUSH) == 0)
goto send;
if (tp->t_force)
goto send;
if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
goto send;
if (SEQ_LT(tp->snd_nxt, tp->snd_max))
goto send;
if (sack_rxmit)
goto send;
}
/*
* Compare available window to amount of window
* known to peer (as advertised window less
* next expected input). If the difference is at least two
* max size segments, or at least 50% of the maximum possible
* window, then want to send a window update to peer.
*/
if (win > 0) {
/*
* "adv" is the amount we can increase the window,
* taking into account that we are limited by
* TCP_MAXWIN << tp->rcv_scale.
*/
long adv = lmin(win, (long)TCP_MAXWIN << tp->rcv_scale) -
(tp->rcv_adv - tp->rcv_nxt);
if (adv >= (long) (2 * tp->t_maxseg))
goto send;
if (2 * adv >= (long) so->so_rcv.sb_hiwat)
goto send;
}
/*
* Send if we owe peer an ACK.
*/
if (tp->t_flags & TF_ACKNOW)
goto send;
if (flags & (TH_SYN|TH_RST))
goto send;
if (SEQ_GT(tp->snd_up, tp->snd_una))
goto send;
/*
* If our state indicates that FIN should be sent
* and we have not yet done so, or we're retransmitting the FIN,
* then we need to send.
*/
if (flags & TH_FIN &&
((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
goto send;
/*
* In SACK, it is possible for tcp_output to fail to send a segment
* after the retransmission timer has been turned off. Make sure
* that the retransmission timer is set.
*/
if (SEQ_GT(tp->snd_max, tp->snd_una) &&
TCP_TIMER_ISARMED(tp, TCPT_REXMT) == 0 &&
TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0) {
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
return (0);
}
/*
* TCP window updates are not reliable, rather a polling protocol
* using ``persist'' packets is used to insure receipt of window
* updates. The three ``states'' for the output side are:
* idle not doing retransmits or persists
* persisting to move a small or zero window
* (re)transmitting and thereby not persisting
*
* tp->t_timer[TCPT_PERSIST]
* is set when we are in persist state.
* tp->t_force
* is set when we are called to send a persist packet.
* tp->t_timer[TCPT_REXMT]
* is set when we are retransmitting
* The output side is idle when both timers are zero.
*
* If send window is too small, there is data to transmit, and no
* retransmit or persist is pending, then go to persist state.
* If nothing happens soon, send when timer expires:
* if window is nonzero, transmit what we can,
* otherwise force out a byte.
*/
if (so->so_snd.sb_cc && TCP_TIMER_ISARMED(tp, TCPT_REXMT) == 0 &&
TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0) {
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
/*
* No reason to send a segment, just return.
*/
return (0);
send:
/*
* Before ESTABLISHED, force sending of initial options
* unless TCP set not to do any options.
* NOTE: we assume that the IP/TCP header plus TCP options
* always fit in a single mbuf, leaving room for a maximum
* link header, i.e.
* max_linkhdr + sizeof(network header) + sizeof(struct tcphdr +
* optlen <= MHLEN
*/
optlen = 0;
switch (tp->pf) {
case 0: /*default to PF_INET*/
case PF_INET:
hdrlen = sizeof(struct ip) + sizeof(struct tcphdr);
break;
#ifdef INET6
case PF_INET6:
hdrlen = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
break;
#endif /* INET6 */
default:
return (EPFNOSUPPORT);
}
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
if ((tp->t_flags & TF_NOOPT) == 0) {
u_int16_t mss;
opt[0] = TCPOPT_MAXSEG;
opt[1] = 4;
mss = htons((u_int16_t) tcp_mss(tp, 0));
memcpy(opt + 2, &mss, sizeof(mss));
optlen = 4;
if (flags & TH_ACK)
tcp_mss_update(tp);
/*
* If this is the first SYN of connection (not a SYN
* ACK), include SACK_PERMIT_HDR option. If this is a
* SYN ACK, include SACK_PERMIT_HDR option if peer has
* already done so.
*/
if (tp->sack_enable && ((flags & TH_ACK) == 0 ||
(tp->t_flags & TF_SACK_PERMIT))) {
*((u_int32_t *) (opt + optlen)) =
htonl(TCPOPT_SACK_PERMIT_HDR);
optlen += 4;
}
if ((tp->t_flags & TF_REQ_SCALE) &&
((flags & TH_ACK) == 0 ||
(tp->t_flags & TF_RCVD_SCALE))) {
*((u_int32_t *) (opt + optlen)) = htonl(
TCPOPT_NOP << 24 |
TCPOPT_WINDOW << 16 |
TCPOLEN_WINDOW << 8 |
tp->request_r_scale);
optlen += 4;
}
}
}
/*
* Send a timestamp and echo-reply if this is a SYN and our side
* wants to use timestamps (TF_REQ_TSTMP is set) or both our side
* and our peer have sent timestamps in our SYN's.
*/
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
(flags & TH_RST) == 0 &&
((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
(tp->t_flags & TF_RCVD_TSTMP))) {
u_int32_t *lp = (u_int32_t *)(opt + optlen);
/* Form timestamp option as shown in appendix A of RFC 1323. */
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
*lp++ = htonl(now + tp->ts_modulate);
*lp = htonl(tp->ts_recent);
optlen += TCPOLEN_TSTAMP_APPA;
/* Set receive buffer autosizing timestamp. */
if (tp->rfbuf_ts == 0)
tp->rfbuf_ts = now;
}
#ifdef TCP_SIGNATURE
if (tp->t_flags & TF_SIGNATURE) {
u_int8_t *bp = (u_int8_t *)(opt + optlen);
/* Send signature option */
*(bp++) = TCPOPT_SIGNATURE;
*(bp++) = TCPOLEN_SIGNATURE;
sigoff = optlen + 2;
{
unsigned int i;
for (i = 0; i < 16; i++)
*(bp++) = 0;
}
/* Pad options list to the next 32 bit boundary and
* terminate it.
*/
*bp++ = TCPOPT_NOP;
*bp++ = TCPOPT_NOP;
optlen += TCPOLEN_SIGLEN;
}
#endif /* TCP_SIGNATURE */
/*
* Send SACKs if necessary. This should be the last option processed.
* Only as many SACKs are sent as are permitted by the maximum options
* size. No more than three SACKs are sent.
*/
if (tp->sack_enable && tp->t_state == TCPS_ESTABLISHED &&
(tp->t_flags & (TF_SACK_PERMIT|TF_NOOPT)) == TF_SACK_PERMIT &&
tp->rcv_numsacks) {
u_int32_t *lp = (u_int32_t *)(opt + optlen);
u_int32_t *olp = lp++;
int count = 0; /* actual number of SACKs inserted */
int maxsack = (MAX_TCPOPTLEN - (optlen + 4))/TCPOLEN_SACK;
tcpstat_inc(tcps_sack_snd_opts);
maxsack = min(maxsack, TCP_MAX_SACK);
for (i = 0; (i < tp->rcv_numsacks && count < maxsack); i++) {
struct sackblk sack = tp->sackblks[i];
if (sack.start == 0 && sack.end == 0)
continue;
*lp++ = htonl(sack.start);
*lp++ = htonl(sack.end);
count++;
}
*olp = htonl(TCPOPT_SACK_HDR|(TCPOLEN_SACK*count+2));
optlen += TCPOLEN_SACK*count + 4; /* including leading NOPs */
}
#ifdef DIAGNOSTIC
if (optlen > MAX_TCPOPTLEN)
panic("tcp_output: options too long");
#endif /* DIAGNOSTIC */
hdrlen += optlen;
/*
* Adjust data length if insertion of options will
* bump the packet length beyond the t_maxopd length.
*/
if (len > tp->t_maxopd - optlen) {
len = tp->t_maxopd - optlen;
sendalot = 1;
flags &= ~TH_FIN;
}
#ifdef DIAGNOSTIC
if (max_linkhdr + hdrlen > MCLBYTES)
panic("tcphdr too big");
#endif
/*
* Grab a header mbuf, attaching a copy of data to
* be transmitted, and initialize the header from
* the template for sends on this connection.
*/
if (len) {
if (tp->t_force && len == 1)
tcpstat_inc(tcps_sndprobe);
else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
tcpstat_pkt(tcps_sndrexmitpack, tcps_sndrexmitbyte,
len);
tp->t_sndrexmitpack++;
} else {
tcpstat_pkt(tcps_sndpack, tcps_sndbyte, len);
}
#ifdef notyet
if ((m = m_copypack(so->so_snd.sb_mb, off,
(int)len, max_linkhdr + hdrlen)) == 0) {
error = ENOBUFS;
goto out;
}
/*
* m_copypack left space for our hdr; use it.
*/
m->m_len += hdrlen;
m->m_data -= hdrlen;
#else
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m != NULL && max_linkhdr + hdrlen > MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
m = NULL;
}
}
if (m == NULL) {
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
if (len <= m_trailingspace(m)) {
m_copydata(so->so_snd.sb_mb, off, (int) len,
mtod(m, caddr_t) + hdrlen);
m->m_len += len;
} else {
m->m_next = m_copym(so->so_snd.sb_mb, off, (int) len,
M_NOWAIT);
if (m->m_next == 0) {
(void) m_free(m);
error = ENOBUFS;
goto out;
}
}
if (so->so_snd.sb_mb->m_flags & M_PKTHDR)
m->m_pkthdr.ph_loopcnt =
so->so_snd.sb_mb->m_pkthdr.ph_loopcnt;
#endif
/*
* If we're sending everything we've got, set PUSH.
* (This will keep happy those implementations which only
* give data to the user when a buffer fills or
* a PUSH comes in.)
*/
if (off + len == so->so_snd.sb_cc && !soissending(so))
flags |= TH_PUSH;
tp->t_sndtime = now;
} else {
if (tp->t_flags & TF_ACKNOW)
tcpstat_inc(tcps_sndacks);
else if (flags & (TH_SYN|TH_FIN|TH_RST))
tcpstat_inc(tcps_sndctrl);
else if (SEQ_GT(tp->snd_up, tp->snd_una))
tcpstat_inc(tcps_sndurg);
else
tcpstat_inc(tcps_sndwinup);
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m != NULL && max_linkhdr + hdrlen > MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
m = NULL;
}
}
if (m == NULL) {
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
}
m->m_pkthdr.ph_ifidx = 0;
m->m_pkthdr.len = hdrlen + len;
if (!tp->t_template)
panic("tcp_output");
#ifdef DIAGNOSTIC
if (tp->t_template->m_len != hdrlen - optlen)
panic("tcp_output: template len != hdrlen - optlen");
#endif /* DIAGNOSTIC */
memcpy(mtod(m, caddr_t), mtod(tp->t_template, caddr_t),
tp->t_template->m_len);
th = (struct tcphdr *)(mtod(m, caddr_t) + tp->t_template->m_len -
sizeof(struct tcphdr));
/*
* Fill in fields, remembering maximum advertised
* window for use in delaying messages about window sizes.
* If resending a FIN, be sure not to use a new sequence number.
*/
if ((flags & TH_FIN) && (tp->t_flags & TF_SENTFIN) &&
(tp->snd_nxt == tp->snd_max))
tp->snd_nxt--;
/*
* If we are doing retransmissions, then snd_nxt will
* not reflect the first unsent octet. For ACK only
* packets, we do not want the sequence number of the
* retransmitted packet, we want the sequence number
* of the next unsent octet. So, if there is no data
* (and no SYN or FIN), use snd_max instead of snd_nxt
* when filling in ti_seq. But if we are in persist
* state, snd_max might reflect one byte beyond the
* right edge of the window, so use snd_nxt in that
* case, since we know we aren't doing a retransmission.
* (retransmit and persist are mutually exclusive...)
*/
if (len || (flags & (TH_SYN|TH_FIN)) || TCP_TIMER_ISARMED(tp, TCPT_PERSIST))
th->th_seq = htonl(tp->snd_nxt);
else
th->th_seq = htonl(tp->snd_max);
if (sack_rxmit) {
/*
* If sendalot was turned on (due to option stuffing), turn it
* off. Properly set th_seq field. Advance the ret'x pointer
* by len.
*/
if (sendalot)
sendalot = 0;
th->th_seq = htonl(p->rxmit);
p->rxmit += len;
tcpstat_pkt(tcps_sack_rexmits, tcps_sack_rexmit_bytes, len);
}
th->th_ack = htonl(tp->rcv_nxt);
if (optlen) {
memcpy(th + 1, opt, optlen);
th->th_off = (sizeof (struct tcphdr) + optlen) >> 2;
}
#ifdef TCP_ECN
if (tcp_do_ecn) {
/*
* if we have received congestion experienced segs,
* set ECE bit.
*/
if (tp->t_flags & TF_RCVD_CE) {
flags |= TH_ECE;
tcpstat_inc(tcps_ecn_sndece);
}
if (!(tp->t_flags & TF_DISABLE_ECN)) {
/*
* if this is a SYN seg, set ECE and CWR.
* set only ECE for SYN-ACK if peer supports ECN.
*/
if ((flags & (TH_SYN|TH_ACK)) == TH_SYN)
flags |= (TH_ECE|TH_CWR);
else if ((tp->t_flags & TF_ECN_PERMIT) &&
(flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK))
flags |= TH_ECE;
}
/*
* if we have reduced the congestion window, notify
* the peer by setting CWR bit.
*/
if ((tp->t_flags & TF_ECN_PERMIT) &&
(tp->t_flags & TF_SEND_CWR)) {
flags |= TH_CWR;
tp->t_flags &= ~TF_SEND_CWR;
tcpstat_inc(tcps_ecn_sndcwr);
}
}
#endif
th->th_flags = flags;
/*
* Calculate receive window. Don't shrink window,
* but avoid silly window syndrome.
*/
if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
win = 0;
if (win > (long)TCP_MAXWIN << tp->rcv_scale)
win = (long)TCP_MAXWIN << tp->rcv_scale;
if (win < (long)(int32_t)(tp->rcv_adv - tp->rcv_nxt))
win = (long)(int32_t)(tp->rcv_adv - tp->rcv_nxt);
if (flags & TH_RST)
win = 0;
th->th_win = htons((u_int16_t) (win>>tp->rcv_scale));
if (th->th_win == 0)
tp->t_sndzerowin++;
if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
u_int32_t urp = tp->snd_up - tp->snd_nxt;
if (urp > IP_MAXPACKET)
urp = IP_MAXPACKET;
th->th_urp = htons((u_int16_t)urp);
th->th_flags |= TH_URG;
} else
/*
* If no urgent pointer to send, then we pull
* the urgent pointer to the left edge of the send window
* so that it doesn't drift into the send window on sequence
* number wraparound.
*/
tp->snd_up = tp->snd_una; /* drag it along */
#ifdef TCP_SIGNATURE
if (tp->t_flags & TF_SIGNATURE) {
int iphlen;
union sockaddr_union src, dst;
struct tdb *tdb;
bzero(&src, sizeof(union sockaddr_union));
bzero(&dst, sizeof(union sockaddr_union));
switch (tp->pf) {
case 0: /*default to PF_INET*/
case AF_INET:
iphlen = sizeof(struct ip);
src.sa.sa_len = sizeof(struct sockaddr_in);
src.sa.sa_family = AF_INET;
src.sin.sin_addr = mtod(m, struct ip *)->ip_src;
dst.sa.sa_len = sizeof(struct sockaddr_in);
dst.sa.sa_family = AF_INET;
dst.sin.sin_addr = mtod(m, struct ip *)->ip_dst;
break;
#ifdef INET6
case AF_INET6:
iphlen = sizeof(struct ip6_hdr);
src.sa.sa_len = sizeof(struct sockaddr_in6);
src.sa.sa_family = AF_INET6;
src.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_src;
dst.sa.sa_len = sizeof(struct sockaddr_in6);
dst.sa.sa_family = AF_INET6;
dst.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_dst;
break;
#endif /* INET6 */
}
tdb = gettdbbysrcdst(rtable_l2(tp->t_inpcb->inp_rtableid),
0, &src, &dst, IPPROTO_TCP);
if (tdb == NULL) {
m_freem(m);
return (EPERM);
}
if (tcp_signature(tdb, tp->pf, m, th, iphlen, 0,
mtod(m, caddr_t) + hdrlen - optlen + sigoff) < 0) {
m_freem(m);
tdb_unref(tdb);
return (EINVAL);
}
tdb_unref(tdb);
}
#endif /* TCP_SIGNATURE */
/* Defer checksumming until later (ip_output() or hardware) */
m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
/*
* In transmit state, time the transmission and arrange for
* the retransmit. In persist state, just set snd_max.
*/
if (tp->t_force == 0 || TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0) {
tcp_seq startseq = tp->snd_nxt;
/*
* Advance snd_nxt over sequence space of this segment.
*/
if (flags & (TH_SYN|TH_FIN)) {
if (flags & TH_SYN)
tp->snd_nxt++;
if (flags & TH_FIN) {
tp->snd_nxt++;
tp->t_flags |= TF_SENTFIN;
}
}
if (tp->sack_enable) {
if (sack_rxmit && (p->rxmit != tp->snd_nxt)) {
goto timer;
}
}
tp->snd_nxt += len;
if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
tp->snd_max = tp->snd_nxt;
/*
* Time this transmission if not a retransmission and
* not currently timing anything.
*/
if (tp->t_rtttime == 0) {
tp->t_rtttime = now;
tp->t_rtseq = startseq;
tcpstat_inc(tcps_segstimed);
}
}
/*
* Set retransmit timer if not currently set,
* and not doing an ack or a keep-alive probe.
* Initial value for retransmit timer is smoothed
* round-trip time + 2 * round-trip time variance.
* Initialize shift counter which is used for backoff
* of retransmit time.
*/
timer:
if (tp->sack_enable && sack_rxmit &&
TCP_TIMER_ISARMED(tp, TCPT_REXMT) == 0 &&
tp->snd_nxt != tp->snd_max) {
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
if (TCP_TIMER_ISARMED(tp, TCPT_PERSIST)) {
TCP_TIMER_DISARM(tp, TCPT_PERSIST);
tp->t_rxtshift = 0;
}
}
if (TCP_TIMER_ISARMED(tp, TCPT_REXMT) == 0 &&
tp->snd_nxt != tp->snd_una) {
TCP_TIMER_ARM(tp, TCPT_REXMT, tp->t_rxtcur);
if (TCP_TIMER_ISARMED(tp, TCPT_PERSIST)) {
TCP_TIMER_DISARM(tp, TCPT_PERSIST);
tp->t_rxtshift = 0;
}
}
if (len == 0 && so->so_snd.sb_cc &&
TCP_TIMER_ISARMED(tp, TCPT_REXMT) == 0 &&
TCP_TIMER_ISARMED(tp, TCPT_PERSIST) == 0) {
/*
* Avoid a situation where we do not set persist timer
* after a zero window condition. For example:
* 1) A -> B: packet with enough data to fill the window
* 2) B -> A: ACK for #1 + new data (0 window
* advertisement)
* 3) A -> B: ACK for #2, 0 len packet
*
* In this case, A will not activate the persist timer,
* because it chose to send a packet. Unless tcp_output
* is called for some other reason (delayed ack timer,
* another input packet from B, socket syscall), A will
* not send zero window probes.
*
* So, if you send a 0-length packet, but there is data
* in the socket buffer, and neither the rexmt or
* persist timer is already set, then activate the
* persist timer.
*/
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
} else
if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
tp->snd_max = tp->snd_nxt + len;
tcp_update_sndspace(tp);
/*
* Trace.
*/
if (so->so_options & SO_DEBUG)
tcp_trace(TA_OUTPUT, tp->t_state, tp, tp, mtod(m, caddr_t), 0,
len);
/*
* Fill in IP length and desired time to live and
* send to IP level. There should be a better way
* to handle ttl and tos; we could keep them in
* the template, but need a way to checksum without them.
*/
#ifdef TCP_ECN
/*
* if peer is ECN capable, set the ECT bit in the IP header.
* but don't set ECT for a pure ack, a retransmit or a window probe.
*/
needect = 0;
if (tcp_do_ecn && (tp->t_flags & TF_ECN_PERMIT)) {
if (len == 0 || SEQ_LT(tp->snd_nxt, tp->snd_max) ||
(tp->t_force && len == 1)) {
/* don't set ECT */
} else {
needect = 1;
tcpstat_inc(tcps_ecn_sndect);
}
}
#endif
/* force routing table */
m->m_pkthdr.ph_rtableid = tp->t_inpcb->inp_rtableid;
#if NPF > 0
pf_mbuf_link_inpcb(m, tp->t_inpcb);
#endif
switch (tp->pf) {
case 0: /*default to PF_INET*/
case AF_INET:
{
struct ip *ip;
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
packetlen = m->m_pkthdr.len;
ip->ip_ttl = tp->t_inpcb->inp_ip.ip_ttl;
ip->ip_tos = tp->t_inpcb->inp_ip.ip_tos;
#ifdef TCP_ECN
if (needect)
ip->ip_tos |= IPTOS_ECN_ECT0;
#endif
}
#if NSTOEPLITZ > 0
m->m_pkthdr.ph_flowid = tp->t_inpcb->inp_flowid;
SET(m->m_pkthdr.csum_flags, M_FLOWID);
#endif
error = ip_output(m, tp->t_inpcb->inp_options,
&tp->t_inpcb->inp_route,
(ip_mtudisc ? IP_MTUDISC : 0), NULL, tp->t_inpcb, 0);
break;
#ifdef INET6
case AF_INET6:
{
struct ip6_hdr *ip6;
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = m->m_pkthdr.len -
sizeof(struct ip6_hdr);
packetlen = m->m_pkthdr.len;
ip6->ip6_nxt = IPPROTO_TCP;
ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb);
#ifdef TCP_ECN
if (needect)
ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20);
#endif
}
error = ip6_output(m, tp->t_inpcb->inp_outputopts6,
&tp->t_inpcb->inp_route6,
0, NULL, tp->t_inpcb);
break;
#endif /* INET6 */
}
if (error) {
out:
if (error == ENOBUFS) {
/*
* If the interface queue is full, or IP cannot
* get an mbuf, trigger TCP slow start.
*/
tp->snd_cwnd = tp->t_maxseg;
return (0);
}
if (error == EMSGSIZE) {
/*
* ip_output() will have already fixed the route
* for us. tcp_mtudisc() will, as its last action,
* initiate retransmission, so it is important to
* not do so here.
*/
tcp_mtudisc(tp->t_inpcb, -1);
return (0);
}
if ((error == EHOSTUNREACH || error == ENETDOWN) &&
TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_softerror = error;
return (0);
}
/* Restart the delayed ACK timer, if necessary. */
if (TCP_TIMER_ISARMED(tp, TCPT_DELACK))
TCP_TIMER_ARM_MSEC(tp, TCPT_DELACK, tcp_delack_msecs);
return (error);
}
if (packetlen > tp->t_pmtud_mtu_sent)
tp->t_pmtud_mtu_sent = packetlen;
tcpstat_inc(tcps_sndtotal);
if (TCP_TIMER_ISARMED(tp, TCPT_DELACK))
tcpstat_inc(tcps_delack);
/*
* Data sent (as far as we can tell).
* If this advertises a larger window than any other segment,
* then remember the size of the advertised window.
* Any pending ACK has now been sent.
*/
if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + win;
tp->last_ack_sent = tp->rcv_nxt;
tp->t_sndacktime = now;
tp->t_flags &= ~TF_ACKNOW;
TCP_TIMER_DISARM(tp, TCPT_DELACK);
if (sendalot)
goto again;
return (0);
}
void
tcp_setpersist(struct tcpcb *tp)
{
int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> (1 + TCP_RTT_BASE_SHIFT);
int nticks;
if (TCP_TIMER_ISARMED(tp, TCPT_REXMT))
panic("tcp_output REXMT");
/*
* Start/restart persistence timer.
*/
if (t < tp->t_rttmin)
t = tp->t_rttmin;
TCPT_RANGESET(nticks, t * tcp_backoff[tp->t_rxtshift],
TCPTV_PERSMIN, TCPTV_PERSMAX);
TCP_TIMER_ARM(tp, TCPT_PERSIST, nticks);
if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
tp->t_rxtshift++;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* $OpenBSD: percpu.h,v 1.8 2018/08/28 15:15:02 mpi Exp $ */
/*
* Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SYS_PERCPU_H_
#define _SYS_PERCPU_H_
#ifndef CACHELINESIZE
#define CACHELINESIZE 64
#endif
#ifndef __upunused /* this should go in param.h */
#ifdef MULTIPROCESSOR
#define __upunused
#else
#define __upunused __attribute__((__unused__))
#endif
#endif
struct cpumem {
void *mem;
};
struct cpumem_iter {
unsigned int cpu;
} __upunused;
struct counters_ref {
uint64_t g;
uint64_t *c;
};
#ifdef _KERNEL
#include <sys/atomic.h>
struct pool;
struct cpumem *cpumem_get(struct pool *);
void cpumem_put(struct pool *, struct cpumem *);
struct cpumem *cpumem_malloc(size_t, int);
struct cpumem *cpumem_malloc_ncpus(struct cpumem *, size_t, int);
void cpumem_free(struct cpumem *, int, size_t);
void *cpumem_first(struct cpumem_iter *, struct cpumem *);
void *cpumem_next(struct cpumem_iter *, struct cpumem *);
static inline void *
cpumem_enter(struct cpumem *cm)
{
#ifdef MULTIPROCESSOR
return (cm[cpu_number()].mem);
#else
return (cm);
#endif
}
static inline void
cpumem_leave(struct cpumem *cm, void *mem)
{
/* KDASSERT? */
}
#ifdef MULTIPROCESSOR
#define CPUMEM_BOOT_MEMORY(_name, _sz) \
static struct { \
unsigned char mem[_sz]; \
struct cpumem cpumem; \
} __aligned(CACHELINESIZE) _name##_boot_cpumem = { \
.cpumem = { _name##_boot_cpumem.mem } \
}
#define CPUMEM_BOOT_INITIALIZER(_name) \
{ &_name##_boot_cpumem.cpumem }
#else /* MULTIPROCESSOR */
#define CPUMEM_BOOT_MEMORY(_name, _sz) \
static struct { \
unsigned char mem[_sz]; \
} __aligned(sizeof(uint64_t)) _name##_boot_cpumem
#define CPUMEM_BOOT_INITIALIZER(_name) \
{ (struct cpumem *)&_name##_boot_cpumem.mem }
#endif /* MULTIPROCESSOR */
#define CPUMEM_FOREACH(_var, _iter, _cpumem) \
for ((_var) = cpumem_first((_iter), (_cpumem)); \
(_var) != NULL; \
(_var) = cpumem_next((_iter), (_cpumem)))
/*
* per cpu counters
*/
struct cpumem *counters_alloc(unsigned int);
struct cpumem *counters_alloc_ncpus(struct cpumem *, unsigned int);
void counters_free(struct cpumem *, unsigned int);
void counters_read(struct cpumem *, uint64_t *, unsigned int);
void counters_zero(struct cpumem *, unsigned int);
static inline uint64_t *
counters_enter(struct counters_ref *ref, struct cpumem *cm)
{
ref->c = cpumem_enter(cm);
#ifdef MULTIPROCESSOR
ref->g = ++(*ref->c); /* make the generation number odd */
membar_producer();
return (ref->c + 1);
#else
return (ref->c);
#endif
}
static inline void
counters_leave(struct counters_ref *ref, struct cpumem *cm)
{
#ifdef MULTIPROCESSOR
membar_producer();
(*ref->c) = ++ref->g; /* make the generation number even again */
#endif
cpumem_leave(cm, ref->c);
}
static inline void
counters_inc(struct cpumem *cm, unsigned int c)
{
struct counters_ref ref;
uint64_t *counters;
counters = counters_enter(&ref, cm);
counters[c]++;
counters_leave(&ref, cm);
}
static inline void
counters_dec(struct cpumem *cm, unsigned int c)
{
struct counters_ref ref;
uint64_t *counters;
counters = counters_enter(&ref, cm);
counters[c]--;
counters_leave(&ref, cm);
}
static inline void
counters_add(struct cpumem *cm, unsigned int c, uint64_t v)
{
struct counters_ref ref;
uint64_t *counters;
counters = counters_enter(&ref, cm);
counters[c] += v;
counters_leave(&ref, cm);
}
static inline void
counters_pkt(struct cpumem *cm, unsigned int c, unsigned int b, uint64_t v)
{
struct counters_ref ref;
uint64_t *counters;
counters = counters_enter(&ref, cm);
counters[c]++;
counters[b] += v;
counters_leave(&ref, cm);
}
#ifdef MULTIPROCESSOR
#define COUNTERS_BOOT_MEMORY(_name, _n) \
CPUMEM_BOOT_MEMORY(_name, ((_n) + 1) * sizeof(uint64_t))
#else
#define COUNTERS_BOOT_MEMORY(_name, _n) \
CPUMEM_BOOT_MEMORY(_name, (_n) * sizeof(uint64_t))
#endif
#define COUNTERS_BOOT_INITIALIZER(_name) CPUMEM_BOOT_INITIALIZER(_name)
#endif /* _KERNEL */
#endif /* _SYS_PERCPU_H_ */
3
2
1
2
1
1
1
1
1
1
1
1
1
2
3
4
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/* $OpenBSD: ip_divert.c,v 1.87 2022/09/05 14:56:09 bluhm Exp $ */
/*
* Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/if_var.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_divert.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <net/pfvar.h>
struct inpcbtable divbtable;
struct cpumem *divcounters;
#ifndef DIVERT_SENDSPACE
#define DIVERT_SENDSPACE (65536 + 100)
#endif
u_int divert_sendspace = DIVERT_SENDSPACE;
#ifndef DIVERT_RECVSPACE
#define DIVERT_RECVSPACE (65536 + 100)
#endif
u_int divert_recvspace = DIVERT_RECVSPACE;
#ifndef DIVERTHASHSIZE
#define DIVERTHASHSIZE 128
#endif
const struct sysctl_bounded_args divertctl_vars[] = {
{ DIVERTCTL_RECVSPACE, &divert_recvspace, 0, INT_MAX },
{ DIVERTCTL_SENDSPACE, &divert_sendspace, 0, INT_MAX },
};
const struct pr_usrreqs divert_usrreqs = {
.pru_attach = divert_attach,
.pru_detach = divert_detach,
.pru_lock = divert_lock,
.pru_unlock = divert_unlock,
.pru_bind = divert_bind,
.pru_shutdown = divert_shutdown,
.pru_send = divert_send,
.pru_abort = divert_abort,
.pru_control = in_control,
.pru_sockaddr = in_sockaddr,
.pru_peeraddr = in_peeraddr,
};
int divbhashsize = DIVERTHASHSIZE;
int divert_output(struct inpcb *, struct mbuf *, struct mbuf *,
struct mbuf *);
void
divert_init(void)
{
in_pcbinit(&divbtable, divbhashsize);
divcounters = counters_alloc(divs_ncounters);
}
int
divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct sockaddr_in *sin;
int error, min_hdrlen, off, dir;
struct ip *ip;
m_freem(control);
if ((error = in_nam2sin(nam, &sin)))
goto fail;
/* Do basic sanity checks. */
if (m->m_pkthdr.len < sizeof(struct ip))
goto fail;
if ((m = m_pullup(m, sizeof(struct ip))) == NULL) {
/* m_pullup() has freed the mbuf, so just return. */
divstat_inc(divs_errors);
return (ENOBUFS);
}
ip = mtod(m, struct ip *);
if (ip->ip_v != IPVERSION)
goto fail;
off = ip->ip_hl << 2;
if (off < sizeof(struct ip) || ntohs(ip->ip_len) < off ||
m->m_pkthdr.len < ntohs(ip->ip_len))
goto fail;
dir = (sin->sin_addr.s_addr == INADDR_ANY ? PF_OUT : PF_IN);
switch (ip->ip_p) {
case IPPROTO_TCP:
min_hdrlen = sizeof(struct tcphdr);
m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
break;
case IPPROTO_UDP:
min_hdrlen = sizeof(struct udphdr);
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
break;
case IPPROTO_ICMP:
min_hdrlen = ICMP_MINLEN;
m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
break;
default:
min_hdrlen = 0;
break;
}
if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
goto fail;
m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
if (dir == PF_IN) {
struct rtentry *rt;
struct ifnet *ifp;
rt = rtalloc(sintosa(sin), 0, inp->inp_rtableid);
if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
rtfree(rt);
error = EADDRNOTAVAIL;
goto fail;
}
m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
rtfree(rt);
/*
* Recalculate IP and protocol checksums for the inbound packet
* since the userspace application may have modified the packet
* prior to reinjection.
*/
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, off);
in_proto_cksum_out(m, NULL);
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
error = ENETDOWN;
goto fail;
}
ipv4_input(ifp, m);
if_put(ifp);
} else {
m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
error = ip_output(m, NULL, &inp->inp_route,
IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL, 0);
}
divstat_inc(divs_opackets);
return (error);
fail:
m_freem(m);
divstat_inc(divs_errors);
return (error ? error : EINVAL);
}
void
divert_packet(struct mbuf *m, int dir, u_int16_t divert_port)
{
struct inpcb *inp = NULL;
struct socket *so;
struct sockaddr_in sin;
divstat_inc(divs_ipackets);
if (m->m_len < sizeof(struct ip) &&
(m = m_pullup(m, sizeof(struct ip))) == NULL) {
divstat_inc(divs_errors);
goto bad;
}
mtx_enter(&divbtable.inpt_mtx);
TAILQ_FOREACH(inp, &divbtable.inpt_queue, inp_queue) {
if (inp->inp_lport != divert_port)
continue;
in_pcbref(inp);
break;
}
mtx_leave(&divbtable.inpt_mtx);
if (inp == NULL) {
divstat_inc(divs_noport);
goto bad;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(sin);
if (dir == PF_IN) {
struct ifaddr *ifa;
struct ifnet *ifp;
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
divstat_inc(divs_errors);
goto bad;
}
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
sin.sin_addr = satosin(ifa->ifa_addr)->sin_addr;
break;
}
if_put(ifp);
}
mtx_enter(&inp->inp_mtx);
so = inp->inp_socket;
if (sbappendaddr(so, &so->so_rcv, sintosa(&sin), m, NULL) == 0) {
mtx_leave(&inp->inp_mtx);
divstat_inc(divs_fullsock);
goto bad;
}
mtx_leave(&inp->inp_mtx);
sorwakeup(so);
in_pcbunref(inp);
return;
bad:
if (inp != NULL)
in_pcbunref(inp);
m_freem(m);
}
int
divert_attach(struct socket *so, int proto)
{
int error;
if (so->so_pcb != NULL)
return EINVAL;
if ((so->so_state & SS_PRIV) == 0)
return EACCES;
error = in_pcballoc(so, &divbtable);
if (error)
return error;
error = soreserve(so, divert_sendspace, divert_recvspace);
if (error)
return error;
sotoinpcb(so)->inp_flags |= INP_HDRINCL;
return (0);
}
int
divert_detach(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
if (inp == NULL)
return (EINVAL);
in_pcbdetach(inp);
return (0);
}
void
divert_lock(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
NET_ASSERT_LOCKED();
mtx_enter(&inp->inp_mtx);
}
void
divert_unlock(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
NET_ASSERT_LOCKED();
mtx_leave(&inp->inp_mtx);
}
int
divert_bind(struct socket *so, struct mbuf *addr, struct proc *p)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return in_pcbbind(inp, addr, p);
}
int
divert_shutdown(struct socket *so)
{
soassertlocked(so);
socantsendmore(so);
return (0);
}
int
divert_send(struct socket *so, struct mbuf *m, struct mbuf *addr,
struct mbuf *control)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return (divert_output(inp, m, addr, control));
}
int
divert_abort(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
soisdisconnected(so);
in_pcbdetach(inp);
return (0);
}
int
divert_sysctl_divstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[divs_ncounters];
struct divstat divstat;
u_long *words = (u_long *)&divstat;
int i;
CTASSERT(sizeof(divstat) == (nitems(counters) * sizeof(u_long)));
memset(&divstat, 0, sizeof divstat);
counters_read(divcounters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&divstat, sizeof(divstat)));
}
/*
* Sysctl for divert variables.
*/
int
divert_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case DIVERTCTL_STATS:
return (divert_sysctl_divstat(oldp, oldlenp, newp));
default:
NET_LOCK();
error = sysctl_bounded_arr(divertctl_vars,
nitems(divertctl_vars), name, namelen, oldp, oldlenp, newp,
newlen);
NET_UNLOCK();
return (error);
}
/* NOTREACHED */
}
6
4
6
1
5
3
3
3
3
3
2
8
8
1
6
1
1
6
1
5
5
5
5
5
5
12
2
10
4
6
2
2
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
/* $OpenBSD: pfkeyv2.c,v 1.252 2022/09/03 22:43:38 mvs Exp $ */
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Craig Metz. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/mutex.h>
#include <net/route.h>
#include <netinet/ip_ipsp.h>
#include <net/pfkeyv2.h>
#include <net/radix.h>
#include <netinet/ip_ah.h>
#include <netinet/ip_esp.h>
#include <netinet/ip_ipcomp.h>
#include <crypto/blf.h>
#if NPF > 0
#include <net/if.h>
#include <net/pfvar.h>
#endif
#define PFKEYSNDQ 8192
#define PFKEYRCVQ 8192
static const struct sadb_alg ealgs[] = {
{ SADB_EALG_NULL, 0, 0, 0 },
{ SADB_EALG_3DESCBC, 64, 192, 192 },
{ SADB_X_EALG_BLF, 64, 40, BLF_MAXKEYLEN * 8},
{ SADB_X_EALG_CAST, 64, 40, 128},
{ SADB_X_EALG_AES, 128, 128, 256},
{ SADB_X_EALG_AESCTR, 128, 128 + 32, 256 + 32}
};
static const struct sadb_alg aalgs[] = {
{ SADB_AALG_SHA1HMAC, 0, 160, 160 },
{ SADB_AALG_MD5HMAC, 0, 128, 128 },
{ SADB_X_AALG_RIPEMD160HMAC, 0, 160, 160 },
{ SADB_X_AALG_SHA2_256, 0, 256, 256 },
{ SADB_X_AALG_SHA2_384, 0, 384, 384 },
{ SADB_X_AALG_SHA2_512, 0, 512, 512 }
};
static const struct sadb_alg calgs[] = {
{ SADB_X_CALG_DEFLATE, 0, 0, 0}
};
struct pool pkpcb_pool;
#define PFKEY_MSG_MAXSZ 4096
const struct sockaddr pfkey_addr = { 2, PF_KEY, };
const struct domain pfkeydomain;
/*
* pfkey PCB
*
* Locks used to protect struct members in this file:
* I immutable after creation
* a atomic operations
* l pkptable's lock
* s socket lock
*/
struct pkpcb {
struct socket *kcb_socket; /* [I] associated socket */
SRPL_ENTRY(pkpcb) kcb_list; /* [l] */
struct refcnt kcb_refcnt; /* [a] */
int kcb_flags; /* [s] */
uint32_t kcb_reg; /* [s] Inc if SATYPE_MAX > 31 */
uint32_t kcb_pid; /* [I] */
unsigned int kcb_rdomain; /* [I] routing domain */
};
#define sotokeycb(so) ((struct pkpcb *)(so)->so_pcb)
#define keylock(kp) solock((kp)->kcb_socket)
#define keyunlock(kp) sounlock((kp)->kcb_socket)
struct dump_state {
struct sadb_msg *sadb_msg;
struct socket *socket;
};
struct pkptable {
SRPL_HEAD(, pkpcb) pkp_list;
struct srpl_rc pkp_rc;
struct rwlock pkp_lk;
};
struct pkptable pkptable;
struct mutex pfkeyv2_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR);
static uint32_t pfkeyv2_seq = 1;
static int nregistered = 0;
static int npromisc = 0;
void pfkey_init(void);
int pfkeyv2_attach(struct socket *, int);
int pfkeyv2_detach(struct socket *);
int pfkeyv2_disconnect(struct socket *);
int pfkeyv2_shutdown(struct socket *);
int pfkeyv2_send(struct socket *, struct mbuf *, struct mbuf *,
struct mbuf *);
int pfkeyv2_abort(struct socket *);
int pfkeyv2_sockaddr(struct socket *, struct mbuf *);
int pfkeyv2_peeraddr(struct socket *, struct mbuf *);
int pfkeyv2_output(struct mbuf *, struct socket *);
int pfkey_sendup(struct pkpcb *, struct mbuf *, int);
int pfkeyv2_sa_flush(struct tdb *, void *, int);
int pfkeyv2_policy_flush(struct ipsec_policy *, void *, unsigned int);
int pfkeyv2_sysctl_policydumper(struct ipsec_policy *, void *, unsigned int);
void keycb_ref(void *, void *);
void keycb_unref(void *, void *);
/*
* Wrapper around m_devget(); copy data from contiguous buffer to mbuf
* chain.
*/
int
pfdatatopacket(void *data, int len, struct mbuf **packet)
{
if (!(*packet = m_devget(data, len, 0)))
return (ENOMEM);
/* Make sure, all data gets zeroized on free */
(*packet)->m_flags |= M_ZEROIZE;
return (0);
}
const struct pr_usrreqs pfkeyv2_usrreqs = {
.pru_attach = pfkeyv2_attach,
.pru_detach = pfkeyv2_detach,
.pru_disconnect = pfkeyv2_disconnect,
.pru_shutdown = pfkeyv2_shutdown,
.pru_send = pfkeyv2_send,
.pru_abort = pfkeyv2_abort,
.pru_sockaddr = pfkeyv2_sockaddr,
.pru_peeraddr = pfkeyv2_peeraddr,
};
const struct protosw pfkeysw[] = {
{
.pr_type = SOCK_RAW,
.pr_domain = &pfkeydomain,
.pr_protocol = PF_KEY_V2,
.pr_flags = PR_ATOMIC | PR_ADDR,
.pr_usrreqs = &pfkeyv2_usrreqs,
.pr_sysctl = pfkeyv2_sysctl,
}
};
const struct domain pfkeydomain = {
.dom_family = PF_KEY,
.dom_name = "PF_KEY",
.dom_init = pfkey_init,
.dom_protosw = pfkeysw,
.dom_protoswNPROTOSW = &pfkeysw[nitems(pfkeysw)],
};
void
keycb_ref(void *null, void *v)
{
struct pkpcb *kp = v;
refcnt_take(&kp->kcb_refcnt);
}
void
keycb_unref(void *null, void *v)
{
struct pkpcb *kp = v;
refcnt_rele_wake(&kp->kcb_refcnt);
}
void
pfkey_init(void)
{
rn_init(sizeof(struct sockaddr_encap));
srpl_rc_init(&pkptable.pkp_rc, keycb_ref, keycb_unref, NULL);
rw_init(&pkptable.pkp_lk, "pfkey");
SRPL_INIT(&pkptable.pkp_list);
pool_init(&pkpcb_pool, sizeof(struct pkpcb), 0,
IPL_SOFTNET, PR_WAITOK, "pkpcb", NULL);
pool_init(&ipsec_policy_pool, sizeof(struct ipsec_policy), 0,
IPL_SOFTNET, 0, "ipsec policy", NULL);
pool_init(&ipsec_acquire_pool, sizeof(struct ipsec_acquire), 0,
IPL_SOFTNET, 0, "ipsec acquire", NULL);
}
/*
* Attach a new PF_KEYv2 socket.
*/
int
pfkeyv2_attach(struct socket *so, int proto)
{
struct pkpcb *kp;
int error;
if ((so->so_state & SS_PRIV) == 0)
return EACCES;
error = soreserve(so, PFKEYSNDQ, PFKEYRCVQ);
if (error)
return (error);
kp = pool_get(&pkpcb_pool, PR_WAITOK|PR_ZERO);
so->so_pcb = kp;
refcnt_init(&kp->kcb_refcnt);
kp->kcb_socket = so;
kp->kcb_pid = curproc->p_p->ps_pid;
kp->kcb_rdomain = rtable_l2(curproc->p_p->ps_rtableid);
so->so_options |= SO_USELOOPBACK;
soisconnected(so);
rw_enter(&pkptable.pkp_lk, RW_WRITE);
SRPL_INSERT_HEAD_LOCKED(&pkptable.pkp_rc, &pkptable.pkp_list, kp, kcb_list);
rw_exit(&pkptable.pkp_lk);
return (0);
}
/*
* Close a PF_KEYv2 socket.
*/
int
pfkeyv2_detach(struct socket *so)
{
struct pkpcb *kp;
soassertlocked(so);
kp = sotokeycb(so);
if (kp == NULL)
return ENOTCONN;
if (kp->kcb_flags &
(PFKEYV2_SOCKETFLAGS_REGISTERED|PFKEYV2_SOCKETFLAGS_PROMISC)) {
mtx_enter(&pfkeyv2_mtx);
if (kp->kcb_flags & PFKEYV2_SOCKETFLAGS_REGISTERED)
nregistered--;
if (kp->kcb_flags & PFKEYV2_SOCKETFLAGS_PROMISC)
npromisc--;
mtx_leave(&pfkeyv2_mtx);
}
rw_enter(&pkptable.pkp_lk, RW_WRITE);
SRPL_REMOVE_LOCKED(&pkptable.pkp_rc, &pkptable.pkp_list, kp, pkpcb,
kcb_list);
rw_exit(&pkptable.pkp_lk);
sounlock(so);
/* wait for all references to drop */
refcnt_finalize(&kp->kcb_refcnt, "pfkeyrefs");
solock(so);
so->so_pcb = NULL;
KASSERT((so->so_state & SS_NOFDREF) == 0);
pool_put(&pkpcb_pool, kp);
return (0);
}
int
pfkeyv2_disconnect(struct socket *so)
{
soisdisconnected(so);
return (0);
}
int
pfkeyv2_shutdown(struct socket *so)
{
socantsendmore(so);
return (0);
}
int
pfkeyv2_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
int error;
soassertlocked(so);
if (control && control->m_len) {
error = EOPNOTSUPP;
goto out;
}
if (nam) {
error = EISCONN;
goto out;
}
error = pfkeyv2_output(m, so);
m = NULL;
out:
m_freem(control);
m_freem(m);
return (error);
}
int
pfkeyv2_abort(struct socket *so)
{
soisdisconnected(so);
return (0);
}
int
pfkeyv2_sockaddr(struct socket *so, struct mbuf *nam)
{
return (EINVAL);
}
int
pfkeyv2_peeraddr(struct socket *so, struct mbuf *nam)
{
/* minimal support, just implement a fake peer address */
bcopy(&pfkey_addr, mtod(nam, caddr_t), pfkey_addr.sa_len);
nam->m_len = pfkey_addr.sa_len;
return (0);
}
int
pfkeyv2_output(struct mbuf *mbuf, struct socket *so)
{
void *message;
int error = 0;
#ifdef DIAGNOSTIC
if (!mbuf || !(mbuf->m_flags & M_PKTHDR)) {
error = EINVAL;
goto ret;
}
#endif /* DIAGNOSTIC */
if (mbuf->m_pkthdr.len > PFKEY_MSG_MAXSZ) {
error = EMSGSIZE;
goto ret;
}
if (!(message = malloc((unsigned long) mbuf->m_pkthdr.len,
M_PFKEY, M_DONTWAIT))) {
error = ENOMEM;
goto ret;
}
m_copydata(mbuf, 0, mbuf->m_pkthdr.len, message);
/*
* The socket can't be closed concurrently because the file
* descriptor reference is still held.
*/
sounlock(so);
error = pfkeyv2_dosend(so, message, mbuf->m_pkthdr.len);
solock(so);
ret:
m_freem(mbuf);
return (error);
}
int
pfkey_sendup(struct pkpcb *kp, struct mbuf *m0, int more)
{
struct socket *so = kp->kcb_socket;
struct mbuf *m;
soassertlocked(so);
if (more) {
if (!(m = m_dup_pkt(m0, 0, M_DONTWAIT)))
return (ENOMEM);
} else
m = m0;
if (!sbappendaddr(so, &so->so_rcv, &pfkey_addr, m, NULL)) {
m_freem(m);
return (ENOBUFS);
}
sorwakeup(so);
return (0);
}
/*
* Send a PFKEYv2 message, possibly to many receivers, based on the
* satype of the socket (which is set by the REGISTER message), and the
* third argument.
*/
int
pfkeyv2_sendmessage(void **headers, int mode, struct socket *so,
u_int8_t satype, int count, u_int rdomain)
{
int i, j, rval;
void *p, *buffer = NULL;
struct mbuf *packet;
struct pkpcb *kp;
struct sadb_msg *smsg;
struct srp_ref sr;
/* Find out how much space we'll need... */
j = sizeof(struct sadb_msg);
for (i = 1; i <= SADB_EXT_MAX; i++)
if (headers[i])
j += ((struct sadb_ext *)headers[i])->sadb_ext_len *
sizeof(uint64_t);
/* ...and allocate it */
if (!(buffer = malloc(j + sizeof(struct sadb_msg), M_PFKEY,
M_NOWAIT))) {
rval = ENOMEM;
goto ret;
}
p = buffer + sizeof(struct sadb_msg);
bcopy(headers[0], p, sizeof(struct sadb_msg));
((struct sadb_msg *) p)->sadb_msg_len = j / sizeof(uint64_t);
p += sizeof(struct sadb_msg);
/* Copy payloads in the packet */
for (i = 1; i <= SADB_EXT_MAX; i++)
if (headers[i]) {
((struct sadb_ext *) headers[i])->sadb_ext_type = i;
bcopy(headers[i], p, EXTLEN(headers[i]));
p += EXTLEN(headers[i]);
}
if ((rval = pfdatatopacket(buffer + sizeof(struct sadb_msg),
j, &packet)) != 0)
goto ret;
switch (mode) {
case PFKEYV2_SENDMESSAGE_UNICAST:
/*
* Send message to the specified socket, plus all
* promiscuous listeners.
*/
solock(so);
pfkey_sendup(sotokeycb(so), packet, 0);
sounlock(so);
/*
* Promiscuous messages contain the original message
* encapsulated in another sadb_msg header.
*/
bzero(buffer, sizeof(struct sadb_msg));
smsg = (struct sadb_msg *) buffer;
smsg->sadb_msg_version = PF_KEY_V2;
smsg->sadb_msg_type = SADB_X_PROMISC;
smsg->sadb_msg_len = (sizeof(struct sadb_msg) + j) /
sizeof(uint64_t);
smsg->sadb_msg_seq = 0;
/* Copy to mbuf chain */
if ((rval = pfdatatopacket(buffer, sizeof(struct sadb_msg) + j,
&packet)) != 0)
goto ret;
/*
* Search for promiscuous listeners, skipping the
* original destination.
*/
SRPL_FOREACH(kp, &sr, &pkptable.pkp_list, kcb_list) {
if (kp->kcb_socket == so || kp->kcb_rdomain != rdomain)
continue;
keylock(kp);
if (kp->kcb_flags & PFKEYV2_SOCKETFLAGS_PROMISC)
pfkey_sendup(kp, packet, 1);
keyunlock(kp);
}
SRPL_LEAVE(&sr);
m_freem(packet);
break;
case PFKEYV2_SENDMESSAGE_REGISTERED:
/*
* Send the message to all registered sockets that match
* the specified satype (e.g., all IPSEC-ESP negotiators)
*/
SRPL_FOREACH(kp, &sr, &pkptable.pkp_list, kcb_list) {
if (kp->kcb_rdomain != rdomain)
continue;
keylock(kp);
if (kp->kcb_flags & PFKEYV2_SOCKETFLAGS_REGISTERED) {
if (!satype) {
/* Just send to everyone registered */
pfkey_sendup(kp, packet, 1);
} else {
/* Check for specified satype */
if ((1 << satype) & kp->kcb_reg)
pfkey_sendup(kp, packet, 1);
}
}
keyunlock(kp);
}
SRPL_LEAVE(&sr);
/* Free last/original copy of the packet */
m_freem(packet);
/* Encapsulate the original message "inside" an sadb_msg header */
bzero(buffer, sizeof(struct sadb_msg));
smsg = (struct sadb_msg *) buffer;
smsg->sadb_msg_version = PF_KEY_V2;
smsg->sadb_msg_type = SADB_X_PROMISC;
smsg->sadb_msg_len = (sizeof(struct sadb_msg) + j) /
sizeof(uint64_t);
smsg->sadb_msg_seq = 0;
/* Convert to mbuf chain */
if ((rval = pfdatatopacket(buffer, sizeof(struct sadb_msg) + j,
&packet)) != 0)
goto ret;
/* Send to all registered promiscuous listeners */
SRPL_FOREACH(kp, &sr, &pkptable.pkp_list, kcb_list) {
if (kp->kcb_rdomain != rdomain)
continue;
keylock(kp);
if ((kp->kcb_flags & PFKEYV2_SOCKETFLAGS_PROMISC) &&
!(kp->kcb_flags & PFKEYV2_SOCKETFLAGS_REGISTERED))
pfkey_sendup(kp, packet, 1);
keyunlock(kp);
}
SRPL_LEAVE(&sr);
m_freem(packet);
break;
case PFKEYV2_SENDMESSAGE_BROADCAST:
/* Send message to all sockets */
SRPL_FOREACH(kp, &sr, &pkptable.pkp_list, kcb_list) {
if (kp->kcb_rdomain != rdomain)
continue;
keylock(kp);
pfkey_sendup(kp, packet, 1);
keyunlock(kp);
}
SRPL_LEAVE(&sr);
m_freem(packet);
break;
}
ret:
if (buffer != NULL) {
explicit_bzero(buffer, j + sizeof(struct sadb_msg));
free(buffer, M_PFKEY, j + sizeof(struct sadb_msg));
}
return (rval);
}
/*
* Get SPD information for an ACQUIRE. We setup the message such that
* the SRC/DST payloads are relative to us (regardless of whether the
* SPD rule was for incoming or outgoing packets).
*/
int
pfkeyv2_policy(struct ipsec_acquire *ipa, void **headers, void **buffer,
int *bufferlen)
{
union sockaddr_union sunion;
struct sadb_protocol *sp;
int rval, i, dir;
void *p;
/* Find out how big a buffer we need */
i = 4 * sizeof(struct sadb_address) + sizeof(struct sadb_protocol);
bzero(&sunion, sizeof(union sockaddr_union));
switch (ipa->ipa_info.sen_type) {
case SENT_IP4:
i += 4 * PADUP(sizeof(struct sockaddr_in));
sunion.sa.sa_family = AF_INET;
sunion.sa.sa_len = sizeof(struct sockaddr_in);
dir = ipa->ipa_info.sen_direction;
break;
#ifdef INET6
case SENT_IP6:
i += 4 * PADUP(sizeof(struct sockaddr_in6));
sunion.sa.sa_family = AF_INET6;
sunion.sa.sa_len = sizeof(struct sockaddr_in6);
dir = ipa->ipa_info.sen_ip6_direction;
break;
#endif /* INET6 */
default:
return (EINVAL);
}
if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
} else {
*buffer = p;
*bufferlen = i;
}
if (dir == IPSP_DIRECTION_OUT)
headers[SADB_X_EXT_SRC_FLOW] = p;
else
headers[SADB_X_EXT_DST_FLOW] = p;
switch (sunion.sa.sa_family) {
case AF_INET:
sunion.sin.sin_addr = ipa->ipa_info.sen_ip_src;
sunion.sin.sin_port = ipa->ipa_info.sen_sport;
break;
#ifdef INET6
case AF_INET6:
sunion.sin6.sin6_addr = ipa->ipa_info.sen_ip6_src;
sunion.sin6.sin6_port = ipa->ipa_info.sen_ip6_sport;
break;
#endif /* INET6 */
}
export_address(&p, &sunion.sa);
if (dir == IPSP_DIRECTION_OUT)
headers[SADB_X_EXT_SRC_MASK] = p;
else
headers[SADB_X_EXT_DST_MASK] = p;
switch (sunion.sa.sa_family) {
case AF_INET:
sunion.sin.sin_addr = ipa->ipa_mask.sen_ip_src;
sunion.sin.sin_port = ipa->ipa_mask.sen_sport;
break;
#ifdef INET6
case AF_INET6:
sunion.sin6.sin6_addr = ipa->ipa_mask.sen_ip6_src;
sunion.sin6.sin6_port = ipa->ipa_mask.sen_ip6_sport;
break;
#endif /* INET6 */
}
export_address(&p, &sunion.sa);
if (dir == IPSP_DIRECTION_OUT)
headers[SADB_X_EXT_DST_FLOW] = p;
else
headers[SADB_X_EXT_SRC_FLOW] = p;
switch (sunion.sa.sa_family) {
case AF_INET:
sunion.sin.sin_addr = ipa->ipa_info.sen_ip_dst;
sunion.sin.sin_port = ipa->ipa_info.sen_dport;
break;
#ifdef INET6
case AF_INET6:
sunion.sin6.sin6_addr = ipa->ipa_info.sen_ip6_dst;
sunion.sin6.sin6_port = ipa->ipa_info.sen_ip6_dport;
break;
#endif /* INET6 */
}
export_address(&p, &sunion.sa);
if (dir == IPSP_DIRECTION_OUT)
headers[SADB_X_EXT_DST_MASK] = p;
else
headers[SADB_X_EXT_SRC_MASK] = p;
switch (sunion.sa.sa_family) {
case AF_INET:
sunion.sin.sin_addr = ipa->ipa_mask.sen_ip_dst;
sunion.sin.sin_port = ipa->ipa_mask.sen_dport;
break;
#ifdef INET6
case AF_INET6:
sunion.sin6.sin6_addr = ipa->ipa_mask.sen_ip6_dst;
sunion.sin6.sin6_port = ipa->ipa_mask.sen_ip6_dport;
break;
#endif /* INET6 */
}
export_address(&p, &sunion.sa);
headers[SADB_X_EXT_FLOW_TYPE] = p;
sp = p;
sp->sadb_protocol_len = sizeof(struct sadb_protocol) /
sizeof(u_int64_t);
switch (sunion.sa.sa_family) {
case AF_INET:
if (ipa->ipa_mask.sen_proto)
sp->sadb_protocol_proto = ipa->ipa_info.sen_proto;
sp->sadb_protocol_direction = ipa->ipa_info.sen_direction;
break;
#ifdef INET6
case AF_INET6:
if (ipa->ipa_mask.sen_ip6_proto)
sp->sadb_protocol_proto = ipa->ipa_info.sen_ip6_proto;
sp->sadb_protocol_direction = ipa->ipa_info.sen_ip6_direction;
break;
#endif /* INET6 */
}
rval = 0;
ret:
return (rval);
}
/*
* Get all the information contained in an SA to a PFKEYV2 message.
*/
int
pfkeyv2_get(struct tdb *tdb, void **headers, void **buffer, int *lenp,
int *lenused)
{
int rval, i;
void *p;
NET_ASSERT_LOCKED();
/* Find how much space we need */
i = sizeof(struct sadb_sa) + sizeof(struct sadb_lifetime) +
sizeof(struct sadb_x_counter);
if (tdb->tdb_soft_allocations || tdb->tdb_soft_bytes ||
tdb->tdb_soft_timeout || tdb->tdb_soft_first_use)
i += sizeof(struct sadb_lifetime);
if (tdb->tdb_exp_allocations || tdb->tdb_exp_bytes ||
tdb->tdb_exp_timeout || tdb->tdb_exp_first_use)
i += sizeof(struct sadb_lifetime);
if (tdb->tdb_last_used)
i += sizeof(struct sadb_lifetime);
i += sizeof(struct sadb_address) + PADUP(tdb->tdb_src.sa.sa_len);
i += sizeof(struct sadb_address) + PADUP(tdb->tdb_dst.sa.sa_len);
if (tdb->tdb_ids) {
i += sizeof(struct sadb_ident) + PADUP(tdb->tdb_ids->id_local->len);
i += sizeof(struct sadb_ident) + PADUP(tdb->tdb_ids->id_remote->len);
}
if (tdb->tdb_amxkey)
i += sizeof(struct sadb_key) + PADUP(tdb->tdb_amxkeylen);
if (tdb->tdb_emxkey)
i += sizeof(struct sadb_key) + PADUP(tdb->tdb_emxkeylen);
if (tdb->tdb_filter.sen_type) {
i += 2 * sizeof(struct sadb_protocol);
/* We'll need four of them: src, src mask, dst, dst mask. */
switch (tdb->tdb_filter.sen_type) {
case SENT_IP4:
i += 4 * PADUP(sizeof(struct sockaddr_in));
i += 4 * sizeof(struct sadb_address);
break;
#ifdef INET6
case SENT_IP6:
i += 4 * PADUP(sizeof(struct sockaddr_in6));
i += 4 * sizeof(struct sadb_address);
break;
#endif /* INET6 */
default:
rval = EINVAL;
goto ret;
}
}
if (tdb->tdb_onext) {
i += sizeof(struct sadb_sa);
i += sizeof(struct sadb_address) +
PADUP(tdb->tdb_onext->tdb_dst.sa.sa_len);
i += sizeof(struct sadb_protocol);
}
if (tdb->tdb_udpencap_port)
i += sizeof(struct sadb_x_udpencap);
i += sizeof(struct sadb_x_replay);
if (tdb->tdb_mtu > 0)
i+= sizeof(struct sadb_x_mtu);
if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
i += sizeof(struct sadb_x_rdomain);
#if NPF > 0
if (tdb->tdb_tag)
i += sizeof(struct sadb_x_tag) + PADUP(PF_TAG_NAME_SIZE);
if (tdb->tdb_tap)
i += sizeof(struct sadb_x_tap);
#endif
if (lenp)
*lenp = i;
if (buffer == NULL) {
rval = 0;
goto ret;
}
if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
} else
*buffer = p;
headers[SADB_EXT_SA] = p;
export_sa(&p, tdb); /* Export SA information (mostly flags) */
/* Export lifetimes where applicable */
headers[SADB_EXT_LIFETIME_CURRENT] = p;
export_lifetime(&p, tdb, PFKEYV2_LIFETIME_CURRENT);
if (tdb->tdb_soft_allocations || tdb->tdb_soft_bytes ||
tdb->tdb_soft_first_use || tdb->tdb_soft_timeout) {
headers[SADB_EXT_LIFETIME_SOFT] = p;
export_lifetime(&p, tdb, PFKEYV2_LIFETIME_SOFT);
}
if (tdb->tdb_exp_allocations || tdb->tdb_exp_bytes ||
tdb->tdb_exp_first_use || tdb->tdb_exp_timeout) {
headers[SADB_EXT_LIFETIME_HARD] = p;
export_lifetime(&p, tdb, PFKEYV2_LIFETIME_HARD);
}
if (tdb->tdb_last_used) {
headers[SADB_X_EXT_LIFETIME_LASTUSE] = p;
export_lifetime(&p, tdb, PFKEYV2_LIFETIME_LASTUSE);
}
/* Export TDB source address */
headers[SADB_EXT_ADDRESS_SRC] = p;
export_address(&p, &tdb->tdb_src.sa);
/* Export TDB destination address */
headers[SADB_EXT_ADDRESS_DST] = p;
export_address(&p, &tdb->tdb_dst.sa);
/* Export source/destination identities, if present */
if (tdb->tdb_ids)
export_identities(&p, tdb->tdb_ids, tdb->tdb_ids_swapped, headers);
/* Export authentication key, if present */
if (tdb->tdb_amxkey) {
headers[SADB_EXT_KEY_AUTH] = p;
export_key(&p, tdb, PFKEYV2_AUTHENTICATION_KEY);
}
/* Export encryption key, if present */
if (tdb->tdb_emxkey) {
headers[SADB_EXT_KEY_ENCRYPT] = p;
export_key(&p, tdb, PFKEYV2_ENCRYPTION_KEY);
}
/* Export flow/filter, if present */
if (tdb->tdb_filter.sen_type)
export_flow(&p, IPSP_IPSEC_USE, &tdb->tdb_filter,
&tdb->tdb_filtermask, headers);
if (tdb->tdb_onext) {
headers[SADB_X_EXT_SA2] = p;
export_sa(&p, tdb->tdb_onext);
headers[SADB_X_EXT_DST2] = p;
export_address(&p, &tdb->tdb_onext->tdb_dst.sa);
headers[SADB_X_EXT_SATYPE2] = p;
export_satype(&p, tdb->tdb_onext);
}
/* Export UDP encapsulation port, if present */
if (tdb->tdb_udpencap_port) {
headers[SADB_X_EXT_UDPENCAP] = p;
export_udpencap(&p, tdb);
}
headers[SADB_X_EXT_REPLAY] = p;
export_replay(&p, tdb);
if (tdb->tdb_mtu > 0) {
headers[SADB_X_EXT_MTU] = p;
export_mtu(&p, tdb);
}
/* Export rdomain switch, if present */
if (tdb->tdb_rdomain != tdb->tdb_rdomain_post) {
headers[SADB_X_EXT_RDOMAIN] = p;
export_rdomain(&p, tdb);
}
#if NPF > 0
/* Export tag information, if present */
if (tdb->tdb_tag) {
headers[SADB_X_EXT_TAG] = p;
export_tag(&p, tdb);
}
/* Export tap enc(4) device information, if present */
if (tdb->tdb_tap) {
headers[SADB_X_EXT_TAP] = p;
export_tap(&p, tdb);
}
#endif
headers[SADB_X_EXT_COUNTER] = p;
export_counter(&p, tdb);
if (lenused)
*lenused = p - *buffer;
rval = 0;
ret:
return (rval);
}
/*
* Dump a TDB.
*/
int
pfkeyv2_dump_walker(struct tdb *tdb, void *state, int last)
{
struct dump_state *dump_state = (struct dump_state *) state;
void *headers[SADB_EXT_MAX+1], *buffer;
int buflen;
int rval;
/* If not satype was specified, dump all TDBs */
if (!dump_state->sadb_msg->sadb_msg_satype ||
(tdb->tdb_satype == dump_state->sadb_msg->sadb_msg_satype)) {
bzero(headers, sizeof(headers));
headers[0] = (void *) dump_state->sadb_msg;
/* Get the information from the TDB to a PFKEYv2 message */
if ((rval = pfkeyv2_get(tdb, headers, &buffer, &buflen, NULL)) != 0)
return (rval);
if (last)
((struct sadb_msg *)headers[0])->sadb_msg_seq = 0;
/* Send the message to the specified socket */
rval = pfkeyv2_sendmessage(headers,
PFKEYV2_SENDMESSAGE_UNICAST, dump_state->socket, 0, 0,
tdb->tdb_rdomain);
explicit_bzero(buffer, buflen);
free(buffer, M_PFKEY, buflen);
if (rval)
return (rval);
}
return (0);
}
/*
* Delete an SA.
*/
int
pfkeyv2_sa_flush(struct tdb *tdb, void *satype_vp, int last)
{
if (!(*((u_int8_t *) satype_vp)) ||
tdb->tdb_satype == *((u_int8_t *) satype_vp))
tdb_delete(tdb);
return (0);
}
/*
* Convert between SATYPEs and IPsec protocols, taking into consideration
* sysctl variables enabling/disabling ESP/AH and the presence of the old
* IPsec transforms.
*/
int
pfkeyv2_get_proto_alg(u_int8_t satype, u_int8_t *sproto, int *alg)
{
switch (satype) {
#ifdef IPSEC
case SADB_SATYPE_AH:
if (!ah_enable)
return (EOPNOTSUPP);
*sproto = IPPROTO_AH;
if(alg != NULL)
*alg = satype = XF_AH;
break;
case SADB_SATYPE_ESP:
if (!esp_enable)
return (EOPNOTSUPP);
*sproto = IPPROTO_ESP;
if(alg != NULL)
*alg = satype = XF_ESP;
break;
case SADB_X_SATYPE_IPIP:
*sproto = IPPROTO_IPIP;
if (alg != NULL)
*alg = XF_IP4;
break;
case SADB_X_SATYPE_IPCOMP:
if (!ipcomp_enable)
return (EOPNOTSUPP);
*sproto = IPPROTO_IPCOMP;
if(alg != NULL)
*alg = satype = XF_IPCOMP;
break;
#endif /* IPSEC */
#ifdef TCP_SIGNATURE
case SADB_X_SATYPE_TCPSIGNATURE:
*sproto = IPPROTO_TCP;
if (alg != NULL)
*alg = XF_TCPSIGNATURE;
break;
#endif /* TCP_SIGNATURE */
default: /* Nothing else supported */
return (EOPNOTSUPP);
}
return (0);
}
/*
* Handle all messages from userland to kernel.
*/
int
pfkeyv2_dosend(struct socket *so, void *message, int len)
{
int i, j, rval = 0, mode = PFKEYV2_SENDMESSAGE_BROADCAST;
int delflag = 0;
struct sockaddr_encap encapdst, encapnetmask;
struct ipsec_policy *ipo;
struct ipsec_acquire *ipa;
struct radix_node_head *rnh;
struct radix_node *rn = NULL;
struct pkpcb *kp, *bkp;
void *freeme = NULL, *freeme2 = NULL, *freeme3 = NULL;
int freeme_sz = 0, freeme2_sz = 0, freeme3_sz = 0;
void *bckptr = NULL;
void *headers[SADB_EXT_MAX + 1];
union sockaddr_union *sunionp;
struct tdb *sa1 = NULL, *sa2 = NULL;
struct sadb_msg *smsg;
struct sadb_spirange *sprng;
struct sadb_sa *ssa;
struct sadb_supported *ssup;
struct sadb_ident *sid, *did;
struct srp_ref sr;
struct sadb_x_rdomain *srdomain;
u_int rdomain = 0;
int promisc;
mtx_enter(&pfkeyv2_mtx);
promisc = npromisc;
mtx_leave(&pfkeyv2_mtx);
/* Verify that we received this over a legitimate pfkeyv2 socket */
bzero(headers, sizeof(headers));
kp = sotokeycb(so);
if (!kp) {
rval = EINVAL;
goto ret;
}
rdomain = kp->kcb_rdomain;
/* If we have any promiscuous listeners, send them a copy of the message */
if (promisc) {
struct mbuf *packet;
freeme_sz = sizeof(struct sadb_msg) + len;
if (!(freeme = malloc(freeme_sz, M_PFKEY, M_NOWAIT))) {
rval = ENOMEM;
goto ret;
}
/* Initialize encapsulating header */
bzero(freeme, sizeof(struct sadb_msg));
smsg = (struct sadb_msg *) freeme;
smsg->sadb_msg_version = PF_KEY_V2;
smsg->sadb_msg_type = SADB_X_PROMISC;
smsg->sadb_msg_len = (sizeof(struct sadb_msg) + len) /
sizeof(uint64_t);
smsg->sadb_msg_seq = curproc->p_p->ps_pid;
bcopy(message, freeme + sizeof(struct sadb_msg), len);
/* Convert to mbuf chain */
if ((rval = pfdatatopacket(freeme, freeme_sz, &packet)) != 0)
goto ret;
/* Send to all promiscuous listeners */
SRPL_FOREACH(bkp, &sr, &pkptable.pkp_list, kcb_list) {
if (bkp->kcb_rdomain != kp->kcb_rdomain)
continue;
keylock(bkp);
if (bkp->kcb_flags & PFKEYV2_SOCKETFLAGS_PROMISC)
pfkey_sendup(bkp, packet, 1);
keyunlock(bkp);
}
SRPL_LEAVE(&sr);
m_freem(packet);
/* Paranoid */
explicit_bzero(freeme, freeme_sz);
free(freeme, M_PFKEY, freeme_sz);
freeme = NULL;
freeme_sz = 0;
}
/* Validate message format */
if ((rval = pfkeyv2_parsemessage(message, len, headers)) != 0)
goto ret;
/* use specified rdomain */
srdomain = (struct sadb_x_rdomain *) headers[SADB_X_EXT_RDOMAIN];
if (srdomain) {
if (!rtable_exists(srdomain->sadb_x_rdomain_dom1) ||
!rtable_exists(srdomain->sadb_x_rdomain_dom2)) {
rval = EINVAL;
goto ret;
}
rdomain = srdomain->sadb_x_rdomain_dom1;
}
smsg = (struct sadb_msg *) headers[0];
switch (smsg->sadb_msg_type) {
case SADB_GETSPI: /* Reserve an SPI */
sa1 = malloc(sizeof (*sa1), M_PFKEY, M_NOWAIT | M_ZERO);
if (sa1 == NULL) {
rval = ENOMEM;
goto ret;
}
sa1->tdb_satype = smsg->sadb_msg_satype;
if ((rval = pfkeyv2_get_proto_alg(sa1->tdb_satype,
&sa1->tdb_sproto, 0)))
goto ret;
import_address(&sa1->tdb_src.sa, headers[SADB_EXT_ADDRESS_SRC]);
import_address(&sa1->tdb_dst.sa, headers[SADB_EXT_ADDRESS_DST]);
/* Find an unused SA identifier */
sprng = (struct sadb_spirange *) headers[SADB_EXT_SPIRANGE];
NET_LOCK();
sa1->tdb_spi = reserve_spi(rdomain,
sprng->sadb_spirange_min, sprng->sadb_spirange_max,
&sa1->tdb_src, &sa1->tdb_dst, sa1->tdb_sproto, &rval);
if (sa1->tdb_spi == 0) {
NET_UNLOCK();
goto ret;
}
/* Send a message back telling what the SA (the SPI really) is */
freeme_sz = sizeof(struct sadb_sa);
if (!(freeme = malloc(freeme_sz, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
NET_UNLOCK();
goto ret;
}
headers[SADB_EXT_SPIRANGE] = NULL;
headers[SADB_EXT_SA] = freeme;
bckptr = freeme;
/* We really only care about the SPI, but we'll export the SA */
export_sa((void **) &bckptr, sa1);
NET_UNLOCK();
break;
case SADB_UPDATE:
ssa = (struct sadb_sa *) headers[SADB_EXT_SA];
sunionp = (union sockaddr_union *) (headers[SADB_EXT_ADDRESS_DST] +
sizeof(struct sadb_address));
/* Either all or none of the flow must be included */
if ((headers[SADB_X_EXT_SRC_FLOW] ||
headers[SADB_X_EXT_PROTOCOL] ||
headers[SADB_X_EXT_FLOW_TYPE] ||
headers[SADB_X_EXT_DST_FLOW] ||
headers[SADB_X_EXT_SRC_MASK] ||
headers[SADB_X_EXT_DST_MASK]) &&
!(headers[SADB_X_EXT_SRC_FLOW] &&
headers[SADB_X_EXT_PROTOCOL] &&
headers[SADB_X_EXT_FLOW_TYPE] &&
headers[SADB_X_EXT_DST_FLOW] &&
headers[SADB_X_EXT_SRC_MASK] &&
headers[SADB_X_EXT_DST_MASK])) {
rval = EINVAL;
goto ret;
}
#ifdef IPSEC
/* UDP encap has to be enabled and is only supported for ESP */
if (headers[SADB_X_EXT_UDPENCAP] &&
(!udpencap_enable ||
smsg->sadb_msg_satype != SADB_SATYPE_ESP)) {
rval = EINVAL;
goto ret;
}
#endif /* IPSEC */
/* Find TDB */
NET_LOCK();
sa2 = gettdb(rdomain, ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(smsg->sadb_msg_satype));
/* If there's no such SA, we're done */
if (sa2 == NULL) {
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
/* If this is a reserved SA */
if (sa2->tdb_flags & TDBF_INVALID) {
struct tdb *newsa;
struct ipsecinit ii;
int alg;
/* Create new TDB */
newsa = tdb_alloc(rdomain);
newsa->tdb_satype = smsg->sadb_msg_satype;
if ((rval = pfkeyv2_get_proto_alg(newsa->tdb_satype,
&newsa->tdb_sproto, &alg))) {
tdb_unref(newsa);
NET_UNLOCK();
goto ret;
}
/* Initialize SA */
bzero(&ii, sizeof(struct ipsecinit));
import_sa(newsa, headers[SADB_EXT_SA], &ii);
import_address(&newsa->tdb_src.sa,
headers[SADB_EXT_ADDRESS_SRC]);
import_address(&newsa->tdb_dst.sa,
headers[SADB_EXT_ADDRESS_DST]);
import_lifetime(newsa,
headers[SADB_EXT_LIFETIME_CURRENT],
PFKEYV2_LIFETIME_CURRENT);
import_lifetime(newsa, headers[SADB_EXT_LIFETIME_SOFT],
PFKEYV2_LIFETIME_SOFT);
import_lifetime(newsa, headers[SADB_EXT_LIFETIME_HARD],
PFKEYV2_LIFETIME_HARD);
import_key(&ii, headers[SADB_EXT_KEY_AUTH],
PFKEYV2_AUTHENTICATION_KEY);
import_key(&ii, headers[SADB_EXT_KEY_ENCRYPT],
PFKEYV2_ENCRYPTION_KEY);
newsa->tdb_ids_swapped = 1; /* only on TDB_UPDATE */
import_identities(&newsa->tdb_ids,
newsa->tdb_ids_swapped,
headers[SADB_EXT_IDENTITY_SRC],
headers[SADB_EXT_IDENTITY_DST]);
if ((rval = import_flow(&newsa->tdb_filter,
&newsa->tdb_filtermask,
headers[SADB_X_EXT_SRC_FLOW],
headers[SADB_X_EXT_SRC_MASK],
headers[SADB_X_EXT_DST_FLOW],
headers[SADB_X_EXT_DST_MASK],
headers[SADB_X_EXT_PROTOCOL],
headers[SADB_X_EXT_FLOW_TYPE]))) {
tdb_unref(newsa);
NET_UNLOCK();
goto ret;
}
import_udpencap(newsa, headers[SADB_X_EXT_UDPENCAP]);
import_rdomain(newsa, headers[SADB_X_EXT_RDOMAIN]);
#if NPF > 0
import_tag(newsa, headers[SADB_X_EXT_TAG]);
import_tap(newsa, headers[SADB_X_EXT_TAP]);
#endif
/* Exclude sensitive data from reply message. */
headers[SADB_EXT_KEY_AUTH] = NULL;
headers[SADB_EXT_KEY_ENCRYPT] = NULL;
headers[SADB_X_EXT_LOCAL_AUTH] = NULL;
headers[SADB_X_EXT_REMOTE_AUTH] = NULL;
newsa->tdb_seq = smsg->sadb_msg_seq;
rval = tdb_init(newsa, alg, &ii);
if (rval) {
rval = EINVAL;
tdb_unref(newsa);
NET_UNLOCK();
goto ret;
}
newsa->tdb_cur_allocations = sa2->tdb_cur_allocations;
/* Delete old version of the SA, insert new one */
tdb_delete(sa2);
puttdb(newsa);
} else {
/*
* The SA is already initialized, so we're only allowed to
* change lifetimes and some other information; we're
* not allowed to change keys, addresses or identities.
*/
if (headers[SADB_EXT_KEY_AUTH] ||
headers[SADB_EXT_KEY_ENCRYPT] ||
headers[SADB_EXT_IDENTITY_SRC] ||
headers[SADB_EXT_IDENTITY_DST] ||
headers[SADB_EXT_SENSITIVITY]) {
rval = EINVAL;
NET_UNLOCK();
goto ret;
}
import_sa(sa2, headers[SADB_EXT_SA], NULL);
import_lifetime(sa2,
headers[SADB_EXT_LIFETIME_CURRENT],
PFKEYV2_LIFETIME_CURRENT);
import_lifetime(sa2, headers[SADB_EXT_LIFETIME_SOFT],
PFKEYV2_LIFETIME_SOFT);
import_lifetime(sa2, headers[SADB_EXT_LIFETIME_HARD],
PFKEYV2_LIFETIME_HARD);
import_udpencap(sa2, headers[SADB_X_EXT_UDPENCAP]);
#if NPF > 0
import_tag(sa2, headers[SADB_X_EXT_TAG]);
import_tap(sa2, headers[SADB_X_EXT_TAP]);
#endif
if (headers[SADB_EXT_ADDRESS_SRC] ||
headers[SADB_EXT_ADDRESS_PROXY]) {
mtx_enter(&tdb_sadb_mtx);
tdb_unlink_locked(sa2);
import_address((struct sockaddr *)&sa2->tdb_src,
headers[SADB_EXT_ADDRESS_SRC]);
import_address((struct sockaddr *)&sa2->tdb_dst,
headers[SADB_EXT_ADDRESS_PROXY]);
puttdb_locked(sa2);
mtx_leave(&tdb_sadb_mtx);
}
}
NET_UNLOCK();
break;
case SADB_ADD:
ssa = (struct sadb_sa *) headers[SADB_EXT_SA];
sunionp = (union sockaddr_union *) (headers[SADB_EXT_ADDRESS_DST] +
sizeof(struct sadb_address));
/* Either all or none of the flow must be included */
if ((headers[SADB_X_EXT_SRC_FLOW] ||
headers[SADB_X_EXT_PROTOCOL] ||
headers[SADB_X_EXT_FLOW_TYPE] ||
headers[SADB_X_EXT_DST_FLOW] ||
headers[SADB_X_EXT_SRC_MASK] ||
headers[SADB_X_EXT_DST_MASK]) &&
!(headers[SADB_X_EXT_SRC_FLOW] &&
headers[SADB_X_EXT_PROTOCOL] &&
headers[SADB_X_EXT_FLOW_TYPE] &&
headers[SADB_X_EXT_DST_FLOW] &&
headers[SADB_X_EXT_SRC_MASK] &&
headers[SADB_X_EXT_DST_MASK])) {
rval = EINVAL;
goto ret;
}
#ifdef IPSEC
/* UDP encap has to be enabled and is only supported for ESP */
if (headers[SADB_X_EXT_UDPENCAP] &&
(!udpencap_enable ||
smsg->sadb_msg_satype != SADB_SATYPE_ESP)) {
rval = EINVAL;
goto ret;
}
#endif /* IPSEC */
NET_LOCK();
sa2 = gettdb(rdomain, ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(smsg->sadb_msg_satype));
/* We can't add an existing SA! */
if (sa2 != NULL) {
rval = EEXIST;
NET_UNLOCK();
goto ret;
}
/* We can only add "mature" SAs */
if (ssa->sadb_sa_state != SADB_SASTATE_MATURE) {
rval = EINVAL;
NET_UNLOCK();
goto ret;
}
{
struct tdb *newsa;
struct ipsecinit ii;
int alg;
/* Create new TDB */
newsa = tdb_alloc(rdomain);
newsa->tdb_satype = smsg->sadb_msg_satype;
if ((rval = pfkeyv2_get_proto_alg(newsa->tdb_satype,
&newsa->tdb_sproto, &alg))) {
tdb_unref(newsa);
NET_UNLOCK();
goto ret;
}
/* Initialize SA */
bzero(&ii, sizeof(struct ipsecinit));
import_sa(newsa, headers[SADB_EXT_SA], &ii);
import_address(&newsa->tdb_src.sa,
headers[SADB_EXT_ADDRESS_SRC]);
import_address(&newsa->tdb_dst.sa,
headers[SADB_EXT_ADDRESS_DST]);
import_lifetime(newsa,
headers[SADB_EXT_LIFETIME_CURRENT],
PFKEYV2_LIFETIME_CURRENT);
import_lifetime(newsa, headers[SADB_EXT_LIFETIME_SOFT],
PFKEYV2_LIFETIME_SOFT);
import_lifetime(newsa, headers[SADB_EXT_LIFETIME_HARD],
PFKEYV2_LIFETIME_HARD);
import_key(&ii, headers[SADB_EXT_KEY_AUTH],
PFKEYV2_AUTHENTICATION_KEY);
import_key(&ii, headers[SADB_EXT_KEY_ENCRYPT],
PFKEYV2_ENCRYPTION_KEY);
import_identities(&newsa->tdb_ids,
newsa->tdb_ids_swapped,
headers[SADB_EXT_IDENTITY_SRC],
headers[SADB_EXT_IDENTITY_DST]);
if ((rval = import_flow(&newsa->tdb_filter,
&newsa->tdb_filtermask,
headers[SADB_X_EXT_SRC_FLOW],
headers[SADB_X_EXT_SRC_MASK],
headers[SADB_X_EXT_DST_FLOW],
headers[SADB_X_EXT_DST_MASK],
headers[SADB_X_EXT_PROTOCOL],
headers[SADB_X_EXT_FLOW_TYPE]))) {
tdb_unref(newsa);
NET_UNLOCK();
goto ret;
}
import_udpencap(newsa, headers[SADB_X_EXT_UDPENCAP]);
import_rdomain(newsa, headers[SADB_X_EXT_RDOMAIN]);
#if NPF > 0
import_tag(newsa, headers[SADB_X_EXT_TAG]);
import_tap(newsa, headers[SADB_X_EXT_TAP]);
#endif
/* Exclude sensitive data from reply message. */
headers[SADB_EXT_KEY_AUTH] = NULL;
headers[SADB_EXT_KEY_ENCRYPT] = NULL;
headers[SADB_X_EXT_LOCAL_AUTH] = NULL;
headers[SADB_X_EXT_REMOTE_AUTH] = NULL;
newsa->tdb_seq = smsg->sadb_msg_seq;
rval = tdb_init(newsa, alg, &ii);
if (rval) {
rval = EINVAL;
tdb_unref(newsa);
NET_UNLOCK();
goto ret;
}
/* Add TDB in table */
puttdb(newsa);
}
NET_UNLOCK();
break;
case SADB_DELETE:
ssa = (struct sadb_sa *) headers[SADB_EXT_SA];
sunionp =
(union sockaddr_union *)(headers[SADB_EXT_ADDRESS_DST] +
sizeof(struct sadb_address));
NET_LOCK();
sa2 = gettdb(rdomain, ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(smsg->sadb_msg_satype));
if (sa2 == NULL) {
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
tdb_delete(sa2);
NET_UNLOCK();
break;
case SADB_X_ASKPOLICY:
/* Get the relevant policy */
NET_LOCK();
ipa = ipsec_get_acquire(((struct sadb_x_policy *)
headers[SADB_X_EXT_POLICY])->sadb_x_policy_seq);
if (ipa == NULL) {
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
rval = pfkeyv2_policy(ipa, headers, &freeme, &freeme_sz);
NET_UNLOCK();
ipsec_unref_acquire(ipa);
if (rval)
mode = PFKEYV2_SENDMESSAGE_UNICAST;
break;
case SADB_GET:
ssa = (struct sadb_sa *) headers[SADB_EXT_SA];
sunionp =
(union sockaddr_union *)(headers[SADB_EXT_ADDRESS_DST] +
sizeof(struct sadb_address));
NET_LOCK();
sa2 = gettdb(rdomain, ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(smsg->sadb_msg_satype));
if (sa2 == NULL) {
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
rval = pfkeyv2_get(sa2, headers, &freeme, &freeme_sz, NULL);
NET_UNLOCK();
if (rval)
mode = PFKEYV2_SENDMESSAGE_UNICAST;
break;
case SADB_REGISTER:
keylock(kp);
if (!(kp->kcb_flags & PFKEYV2_SOCKETFLAGS_REGISTERED)) {
kp->kcb_flags |= PFKEYV2_SOCKETFLAGS_REGISTERED;
mtx_enter(&pfkeyv2_mtx);
nregistered++;
mtx_leave(&pfkeyv2_mtx);
}
keyunlock(kp);
freeme_sz = sizeof(struct sadb_supported) + sizeof(ealgs);
if (!(freeme = malloc(freeme_sz, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
}
ssup = (struct sadb_supported *) freeme;
ssup->sadb_supported_len = freeme_sz / sizeof(uint64_t);
{
void *p = freeme + sizeof(struct sadb_supported);
bcopy(&ealgs[0], p, sizeof(ealgs));
}
headers[SADB_EXT_SUPPORTED_ENCRYPT] = freeme;
freeme2_sz = sizeof(struct sadb_supported) + sizeof(aalgs);
if (!(freeme2 = malloc(freeme2_sz, M_PFKEY,
M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
}
/* Keep track what this socket has registered for */
keylock(kp);
kp->kcb_reg |=
(1 << ((struct sadb_msg *)message)->sadb_msg_satype);
keyunlock(kp);
ssup = (struct sadb_supported *) freeme2;
ssup->sadb_supported_len = freeme2_sz / sizeof(uint64_t);
{
void *p = freeme2 + sizeof(struct sadb_supported);
bcopy(&aalgs[0], p, sizeof(aalgs));
}
headers[SADB_EXT_SUPPORTED_AUTH] = freeme2;
freeme3_sz = sizeof(struct sadb_supported) + sizeof(calgs);
if (!(freeme3 = malloc(freeme3_sz, M_PFKEY,
M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
}
ssup = (struct sadb_supported *) freeme3;
ssup->sadb_supported_len = freeme3_sz / sizeof(uint64_t);
{
void *p = freeme3 + sizeof(struct sadb_supported);
bcopy(&calgs[0], p, sizeof(calgs));
}
headers[SADB_X_EXT_SUPPORTED_COMP] = freeme3;
break;
case SADB_ACQUIRE:
case SADB_EXPIRE:
/* Nothing to handle */
rval = 0;
break;
case SADB_FLUSH:
rval = 0;
NET_LOCK();
switch (smsg->sadb_msg_satype) {
case SADB_SATYPE_UNSPEC:
spd_table_walk(rdomain, pfkeyv2_policy_flush, NULL);
/* FALLTHROUGH */
case SADB_SATYPE_AH:
case SADB_SATYPE_ESP:
case SADB_X_SATYPE_IPIP:
case SADB_X_SATYPE_IPCOMP:
#ifdef TCP_SIGNATURE
case SADB_X_SATYPE_TCPSIGNATURE:
#endif /* TCP_SIGNATURE */
tdb_walk(rdomain, pfkeyv2_sa_flush,
(u_int8_t *) &(smsg->sadb_msg_satype));
break;
default:
rval = EINVAL; /* Unknown/unsupported type */
}
NET_UNLOCK();
break;
case SADB_DUMP:
{
struct dump_state dump_state;
dump_state.sadb_msg = (struct sadb_msg *) headers[0];
dump_state.socket = so;
NET_LOCK();
rval = tdb_walk(rdomain, pfkeyv2_dump_walker, &dump_state);
NET_UNLOCK();
if (!rval)
goto realret;
if ((rval == ENOMEM) || (rval == ENOBUFS))
rval = 0;
}
break;
case SADB_X_GRPSPIS:
{
struct tdb *tdb1, *tdb2, *tdb3;
struct sadb_protocol *sa_proto;
ssa = (struct sadb_sa *) headers[SADB_EXT_SA];
sunionp = (union sockaddr_union *) (headers[SADB_EXT_ADDRESS_DST] +
sizeof(struct sadb_address));
NET_LOCK();
tdb1 = gettdb(rdomain, ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(smsg->sadb_msg_satype));
if (tdb1 == NULL) {
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
ssa = (struct sadb_sa *) headers[SADB_X_EXT_SA2];
sunionp = (union sockaddr_union *) (headers[SADB_X_EXT_DST2] +
sizeof(struct sadb_address));
sa_proto = (struct sadb_protocol *) headers[SADB_X_EXT_SATYPE2];
/* optionally fetch tdb2 from rdomain2 */
tdb2 = gettdb(srdomain ? srdomain->sadb_x_rdomain_dom2 : rdomain,
ssa->sadb_sa_spi, sunionp,
SADB_X_GETSPROTO(sa_proto->sadb_protocol_proto));
if (tdb2 == NULL) {
tdb_unref(tdb1);
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
/* Detect cycles */
for (tdb3 = tdb2; tdb3; tdb3 = tdb3->tdb_onext)
if (tdb3 == tdb1) {
tdb_unref(tdb1);
tdb_unref(tdb2);
rval = ESRCH;
NET_UNLOCK();
goto ret;
}
/* Maintenance */
if ((tdb1->tdb_onext) &&
(tdb1->tdb_onext->tdb_inext == tdb1)) {
tdb_unref(tdb1->tdb_onext->tdb_inext);
tdb1->tdb_onext->tdb_inext = NULL;
}
if ((tdb2->tdb_inext) &&
(tdb2->tdb_inext->tdb_onext == tdb2)) {
tdb_unref(tdb2->tdb_inext->tdb_onext);
tdb2->tdb_inext->tdb_onext = NULL;
}
/* Link them */
tdb1->tdb_onext = tdb2;
tdb2->tdb_inext = tdb1;
NET_UNLOCK();
}
break;
case SADB_X_DELFLOW:
delflag = 1;
/*FALLTHROUGH*/
case SADB_X_ADDFLOW:
{
struct sadb_protocol *sab;
union sockaddr_union *ssrc;
int exists = 0;
NET_LOCK();
if ((rnh = spd_table_add(rdomain)) == NULL) {
rval = ENOMEM;
NET_UNLOCK();
goto ret;
}
sab = (struct sadb_protocol *) headers[SADB_X_EXT_FLOW_TYPE];
if ((sab->sadb_protocol_direction != IPSP_DIRECTION_IN) &&
(sab->sadb_protocol_direction != IPSP_DIRECTION_OUT)) {
rval = EINVAL;
NET_UNLOCK();
goto ret;
}
/* If the security protocol wasn't specified, pretend it was ESP */
if (smsg->sadb_msg_satype == 0)
smsg->sadb_msg_satype = SADB_SATYPE_ESP;
if (headers[SADB_EXT_ADDRESS_DST])
sunionp = (union sockaddr_union *)
(headers[SADB_EXT_ADDRESS_DST] +
sizeof(struct sadb_address));
else
sunionp = NULL;
if (headers[SADB_EXT_ADDRESS_SRC])
ssrc = (union sockaddr_union *)
(headers[SADB_EXT_ADDRESS_SRC] +
sizeof(struct sadb_address));
else
ssrc = NULL;
if ((rval = import_flow(&encapdst, &encapnetmask,
headers[SADB_X_EXT_SRC_FLOW], headers[SADB_X_EXT_SRC_MASK],
headers[SADB_X_EXT_DST_FLOW], headers[SADB_X_EXT_DST_MASK],
headers[SADB_X_EXT_PROTOCOL],
headers[SADB_X_EXT_FLOW_TYPE]))) {
NET_UNLOCK();
goto ret;
}
/* Determine whether the exact same SPD entry already exists. */
if ((rn = rn_match(&encapdst, rnh)) != NULL) {
ipo = (struct ipsec_policy *)rn;
/* Verify that the entry is identical */
if (bcmp(&ipo->ipo_addr, &encapdst,
sizeof(struct sockaddr_encap)) ||
bcmp(&ipo->ipo_mask, &encapnetmask,
sizeof(struct sockaddr_encap)))
ipo = NULL; /* Fall through */
else
exists = 1;
} else
ipo = NULL;
/*
* If the existing policy is static, only delete or update
* it if the new one is also static.
*/
if (exists && (ipo->ipo_flags & IPSP_POLICY_STATIC)) {
if (!(sab->sadb_protocol_flags &
SADB_X_POLICYFLAGS_POLICY)) {
NET_UNLOCK();
goto ret;
}
}
/* Delete ? */
if (delflag) {
if (exists) {
rval = ipsec_delete_policy(ipo);
NET_UNLOCK();
goto ret;
}
/* If we were asked to delete something non-existent, error. */
rval = ESRCH;
NET_UNLOCK();
break;
}
if (!exists) {
/* Allocate policy entry */
ipo = pool_get(&ipsec_policy_pool, PR_NOWAIT|PR_ZERO);
if (ipo == NULL) {
rval = ENOMEM;
NET_UNLOCK();
goto ret;
}
}
switch (sab->sadb_protocol_proto) {
case SADB_X_FLOW_TYPE_USE:
ipo->ipo_type = IPSP_IPSEC_USE;
break;
case SADB_X_FLOW_TYPE_ACQUIRE:
ipo->ipo_type = IPSP_IPSEC_ACQUIRE;
break;
case SADB_X_FLOW_TYPE_REQUIRE:
ipo->ipo_type = IPSP_IPSEC_REQUIRE;
break;
case SADB_X_FLOW_TYPE_DENY:
ipo->ipo_type = IPSP_DENY;
break;
case SADB_X_FLOW_TYPE_BYPASS:
ipo->ipo_type = IPSP_PERMIT;
break;
case SADB_X_FLOW_TYPE_DONTACQ:
ipo->ipo_type = IPSP_IPSEC_DONTACQ;
break;
default:
if (!exists)
pool_put(&ipsec_policy_pool, ipo);
else
ipsec_delete_policy(ipo);
rval = EINVAL;
NET_UNLOCK();
goto ret;
}
if (sab->sadb_protocol_flags & SADB_X_POLICYFLAGS_POLICY)
ipo->ipo_flags |= IPSP_POLICY_STATIC;
if (sunionp)
bcopy(sunionp, &ipo->ipo_dst,
sizeof(union sockaddr_union));
else
bzero(&ipo->ipo_dst, sizeof(union sockaddr_union));
if (ssrc)
bcopy(ssrc, &ipo->ipo_src,
sizeof(union sockaddr_union));
else
bzero(&ipo->ipo_src, sizeof(union sockaddr_union));
ipo->ipo_sproto = SADB_X_GETSPROTO(smsg->sadb_msg_satype);
if (ipo->ipo_ids) {
ipsp_ids_free(ipo->ipo_ids);
ipo->ipo_ids = NULL;
}
if ((sid = headers[SADB_EXT_IDENTITY_SRC]) != NULL &&
(did = headers[SADB_EXT_IDENTITY_DST]) != NULL) {
import_identities(&ipo->ipo_ids, 0, sid, did);
if (ipo->ipo_ids == NULL) {
if (exists)
ipsec_delete_policy(ipo);
else
pool_put(&ipsec_policy_pool, ipo);
rval = ENOBUFS;
NET_UNLOCK();
goto ret;
}
}
/* Flow type */
if (!exists) {
/* Initialize policy entry */
bcopy(&encapdst, &ipo->ipo_addr,
sizeof(struct sockaddr_encap));
bcopy(&encapnetmask, &ipo->ipo_mask,
sizeof(struct sockaddr_encap));
TAILQ_INIT(&ipo->ipo_acquires);
ipo->ipo_rdomain = rdomain;
refcnt_init(&ipo->ipo_refcnt);
/* Add SPD entry */
if ((rnh = spd_table_get(rdomain)) == NULL ||
(rn = rn_addroute((caddr_t)&ipo->ipo_addr,
(caddr_t)&ipo->ipo_mask, rnh,
ipo->ipo_nodes, 0)) == NULL) {
/* Remove from linked list of policies on TDB */
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(
&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
}
mtx_leave(&ipo_tdb_mtx);
if (ipo->ipo_ids)
ipsp_ids_free(ipo->ipo_ids);
pool_put(&ipsec_policy_pool, ipo);
NET_UNLOCK();
goto ret;
}
TAILQ_INSERT_HEAD(&ipsec_policy_head, ipo, ipo_list);
ipsec_in_use++;
} else {
ipo->ipo_last_searched = ipo->ipo_flags = 0;
}
NET_UNLOCK();
}
break;
case SADB_X_PROMISC:
if (len >= 2 * sizeof(struct sadb_msg)) {
struct mbuf *packet;
if ((rval = pfdatatopacket(message, len, &packet)) != 0)
goto ret;
SRPL_FOREACH(bkp, &sr, &pkptable.pkp_list, kcb_list) {
if (bkp == kp || bkp->kcb_rdomain != kp->kcb_rdomain)
continue;
if (!smsg->sadb_msg_seq ||
(smsg->sadb_msg_seq == kp->kcb_pid)) {
keylock(bkp);
pfkey_sendup(bkp, packet, 1);
keyunlock(bkp);
}
}
SRPL_LEAVE(&sr);
m_freem(packet);
} else {
if (len != sizeof(struct sadb_msg)) {
rval = EINVAL;
goto ret;
}
keylock(kp);
i = (kp->kcb_flags &
PFKEYV2_SOCKETFLAGS_PROMISC) ? 1 : 0;
j = smsg->sadb_msg_satype ? 1 : 0;
if (i ^ j) {
if (j) {
kp->kcb_flags |=
PFKEYV2_SOCKETFLAGS_PROMISC;
mtx_enter(&pfkeyv2_mtx);
npromisc++;
mtx_leave(&pfkeyv2_mtx);
} else {
kp->kcb_flags &=
~PFKEYV2_SOCKETFLAGS_PROMISC;
mtx_enter(&pfkeyv2_mtx);
npromisc--;
mtx_leave(&pfkeyv2_mtx);
}
}
keyunlock(kp);
}
break;
default:
rval = EINVAL;
goto ret;
}
ret:
if (rval) {
if ((rval == EINVAL) || (rval == ENOMEM) || (rval == ENOBUFS))
goto realret;
for (i = 1; i <= SADB_EXT_MAX; i++)
headers[i] = NULL;
smsg->sadb_msg_errno = abs(rval);
} else {
uint64_t seen = 0LL;
for (i = 1; i <= SADB_EXT_MAX; i++)
if (headers[i])
seen |= (1LL << i);
if ((seen & sadb_exts_allowed_out[smsg->sadb_msg_type])
!= seen) {
rval = EPERM;
goto realret;
}
if ((seen & sadb_exts_required_out[smsg->sadb_msg_type]) !=
sadb_exts_required_out[smsg->sadb_msg_type]) {
rval = EPERM;
goto realret;
}
}
rval = pfkeyv2_sendmessage(headers, mode, so, 0, 0, kp->kcb_rdomain);
realret:
if (freeme != NULL)
explicit_bzero(freeme, freeme_sz);
free(freeme, M_PFKEY, freeme_sz);
free(freeme2, M_PFKEY, freeme2_sz);
free(freeme3, M_PFKEY, freeme3_sz);
explicit_bzero(message, len);
free(message, M_PFKEY, len);
free(sa1, M_PFKEY, sizeof(*sa1));
NET_LOCK();
tdb_unref(sa2);
NET_UNLOCK();
return (rval);
}
/*
* Send an ACQUIRE message to key management, to get a new SA.
*/
int
pfkeyv2_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw,
union sockaddr_union *laddr, u_int32_t *seq, struct sockaddr_encap *ddst)
{
void *p, *headers[SADB_EXT_MAX + 1], *buffer = NULL;
struct sadb_comb *sadb_comb;
struct sadb_address *sadd;
struct sadb_prop *sa_prop;
struct sadb_msg *smsg;
int rval = 0;
int i, j, registered;
mtx_enter(&pfkeyv2_mtx);
*seq = pfkeyv2_seq++;
registered = nregistered;
mtx_leave(&pfkeyv2_mtx);
if (!registered) {
rval = ESRCH;
goto ret;
}
/* How large a buffer do we need... XXX we only do one proposal for now */
i = sizeof(struct sadb_msg) +
(laddr == NULL ? 0 : sizeof(struct sadb_address) +
PADUP(ipo->ipo_src.sa.sa_len)) +
sizeof(struct sadb_address) + PADUP(gw->sa.sa_len) +
sizeof(struct sadb_prop) + 1 * sizeof(struct sadb_comb);
if (ipo->ipo_ids) {
i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_local->len);
i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_remote->len);
}
/* Allocate */
if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
}
bzero(headers, sizeof(headers));
buffer = p;
headers[0] = p;
p += sizeof(struct sadb_msg);
smsg = (struct sadb_msg *) headers[0];
smsg->sadb_msg_version = PF_KEY_V2;
smsg->sadb_msg_type = SADB_ACQUIRE;
smsg->sadb_msg_len = i / sizeof(uint64_t);
smsg->sadb_msg_seq = *seq;
if (ipo->ipo_sproto == IPPROTO_ESP)
smsg->sadb_msg_satype = SADB_SATYPE_ESP;
else if (ipo->ipo_sproto == IPPROTO_AH)
smsg->sadb_msg_satype = SADB_SATYPE_AH;
else if (ipo->ipo_sproto == IPPROTO_IPCOMP)
smsg->sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
if (laddr) {
headers[SADB_EXT_ADDRESS_SRC] = p;
p += sizeof(struct sadb_address) + PADUP(laddr->sa.sa_len);
sadd = (struct sadb_address *) headers[SADB_EXT_ADDRESS_SRC];
sadd->sadb_address_len = (sizeof(struct sadb_address) +
laddr->sa.sa_len + sizeof(uint64_t) - 1) /
sizeof(uint64_t);
bcopy(laddr, headers[SADB_EXT_ADDRESS_SRC] +
sizeof(struct sadb_address), laddr->sa.sa_len);
}
headers[SADB_EXT_ADDRESS_DST] = p;
p += sizeof(struct sadb_address) + PADUP(gw->sa.sa_len);
sadd = (struct sadb_address *) headers[SADB_EXT_ADDRESS_DST];
sadd->sadb_address_len = (sizeof(struct sadb_address) +
gw->sa.sa_len + sizeof(uint64_t) - 1) / sizeof(uint64_t);
bcopy(gw, headers[SADB_EXT_ADDRESS_DST] + sizeof(struct sadb_address),
gw->sa.sa_len);
if (ipo->ipo_ids)
export_identities(&p, ipo->ipo_ids, 0, headers);
headers[SADB_EXT_PROPOSAL] = p;
p += sizeof(struct sadb_prop);
sa_prop = (struct sadb_prop *) headers[SADB_EXT_PROPOSAL];
sa_prop->sadb_prop_num = 1; /* XXX One proposal only */
sa_prop->sadb_prop_len = (sizeof(struct sadb_prop) +
(sizeof(struct sadb_comb) * sa_prop->sadb_prop_num)) /
sizeof(uint64_t);
sadb_comb = p;
/* XXX Should actually ask the crypto layer what's supported */
for (j = 0; j < sa_prop->sadb_prop_num; j++) {
sadb_comb->sadb_comb_flags = 0;
#ifdef IPSEC
if (ipsec_require_pfs)
sadb_comb->sadb_comb_flags |= SADB_SAFLAGS_PFS;
/* Set the encryption algorithm */
if (ipo->ipo_sproto == IPPROTO_ESP) {
if (!strncasecmp(ipsec_def_enc, "aes",
sizeof("aes"))) {
sadb_comb->sadb_comb_encrypt = SADB_X_EALG_AES;
sadb_comb->sadb_comb_encrypt_minbits = 128;
sadb_comb->sadb_comb_encrypt_maxbits = 256;
} else if (!strncasecmp(ipsec_def_enc, "aesctr",
sizeof("aesctr"))) {
sadb_comb->sadb_comb_encrypt = SADB_X_EALG_AESCTR;
sadb_comb->sadb_comb_encrypt_minbits = 128+32;
sadb_comb->sadb_comb_encrypt_maxbits = 256+32;
} else if (!strncasecmp(ipsec_def_enc, "3des",
sizeof("3des"))) {
sadb_comb->sadb_comb_encrypt = SADB_EALG_3DESCBC;
sadb_comb->sadb_comb_encrypt_minbits = 192;
sadb_comb->sadb_comb_encrypt_maxbits = 192;
} else if (!strncasecmp(ipsec_def_enc, "blowfish",
sizeof("blowfish"))) {
sadb_comb->sadb_comb_encrypt = SADB_X_EALG_BLF;
sadb_comb->sadb_comb_encrypt_minbits = 40;
sadb_comb->sadb_comb_encrypt_maxbits = BLF_MAXKEYLEN * 8;
} else if (!strncasecmp(ipsec_def_enc, "cast128",
sizeof("cast128"))) {
sadb_comb->sadb_comb_encrypt = SADB_X_EALG_CAST;
sadb_comb->sadb_comb_encrypt_minbits = 40;
sadb_comb->sadb_comb_encrypt_maxbits = 128;
}
} else if (ipo->ipo_sproto == IPPROTO_IPCOMP) {
/* Set the compression algorithm */
if (!strncasecmp(ipsec_def_comp, "deflate",
sizeof("deflate"))) {
sadb_comb->sadb_comb_encrypt = SADB_X_CALG_DEFLATE;
sadb_comb->sadb_comb_encrypt_minbits = 0;
sadb_comb->sadb_comb_encrypt_maxbits = 0;
}
}
/* Set the authentication algorithm */
if (!strncasecmp(ipsec_def_auth, "hmac-sha1",
sizeof("hmac-sha1"))) {
sadb_comb->sadb_comb_auth = SADB_AALG_SHA1HMAC;
sadb_comb->sadb_comb_auth_minbits = 160;
sadb_comb->sadb_comb_auth_maxbits = 160;
} else if (!strncasecmp(ipsec_def_auth, "hmac-ripemd160",
sizeof("hmac_ripemd160"))) {
sadb_comb->sadb_comb_auth = SADB_X_AALG_RIPEMD160HMAC;
sadb_comb->sadb_comb_auth_minbits = 160;
sadb_comb->sadb_comb_auth_maxbits = 160;
} else if (!strncasecmp(ipsec_def_auth, "hmac-md5",
sizeof("hmac-md5"))) {
sadb_comb->sadb_comb_auth = SADB_AALG_MD5HMAC;
sadb_comb->sadb_comb_auth_minbits = 128;
sadb_comb->sadb_comb_auth_maxbits = 128;
} else if (!strncasecmp(ipsec_def_auth, "hmac-sha2-256",
sizeof("hmac-sha2-256"))) {
sadb_comb->sadb_comb_auth = SADB_X_AALG_SHA2_256;
sadb_comb->sadb_comb_auth_minbits = 256;
sadb_comb->sadb_comb_auth_maxbits = 256;
} else if (!strncasecmp(ipsec_def_auth, "hmac-sha2-384",
sizeof("hmac-sha2-384"))) {
sadb_comb->sadb_comb_auth = SADB_X_AALG_SHA2_384;
sadb_comb->sadb_comb_auth_minbits = 384;
sadb_comb->sadb_comb_auth_maxbits = 384;
} else if (!strncasecmp(ipsec_def_auth, "hmac-sha2-512",
sizeof("hmac-sha2-512"))) {
sadb_comb->sadb_comb_auth = SADB_X_AALG_SHA2_512;
sadb_comb->sadb_comb_auth_minbits = 512;
sadb_comb->sadb_comb_auth_maxbits = 512;
}
sadb_comb->sadb_comb_soft_allocations = ipsec_soft_allocations;
sadb_comb->sadb_comb_hard_allocations = ipsec_exp_allocations;
sadb_comb->sadb_comb_soft_bytes = ipsec_soft_bytes;
sadb_comb->sadb_comb_hard_bytes = ipsec_exp_bytes;
sadb_comb->sadb_comb_soft_addtime = ipsec_soft_timeout;
sadb_comb->sadb_comb_hard_addtime = ipsec_exp_timeout;
sadb_comb->sadb_comb_soft_usetime = ipsec_soft_first_use;
sadb_comb->sadb_comb_hard_usetime = ipsec_exp_first_use;
#endif
sadb_comb++;
}
/* Send the ACQUIRE message to all compliant registered listeners. */
if ((rval = pfkeyv2_sendmessage(headers,
PFKEYV2_SENDMESSAGE_REGISTERED, NULL, smsg->sadb_msg_satype, 0,
ipo->ipo_rdomain)) != 0)
goto ret;
rval = 0;
ret:
if (buffer != NULL) {
explicit_bzero(buffer, i);
free(buffer, M_PFKEY, i);
}
return (rval);
}
/*
* Notify key management that an expiration went off. The second argument
* specifies the type of expiration (soft or hard).
*/
int
pfkeyv2_expire(struct tdb *tdb, u_int16_t type)
{
void *p, *headers[SADB_EXT_MAX+1], *buffer = NULL;
struct sadb_msg *smsg;
int rval = 0;
int i;
NET_ASSERT_LOCKED();
switch (tdb->tdb_sproto) {
case IPPROTO_AH:
case IPPROTO_ESP:
case IPPROTO_IPIP:
case IPPROTO_IPCOMP:
#ifdef TCP_SIGNATURE
case IPPROTO_TCP:
#endif /* TCP_SIGNATURE */
break;
default:
rval = EOPNOTSUPP;
goto ret;
}
i = sizeof(struct sadb_msg) + sizeof(struct sadb_sa) +
2 * sizeof(struct sadb_lifetime) +
sizeof(struct sadb_address) + PADUP(tdb->tdb_src.sa.sa_len) +
sizeof(struct sadb_address) + PADUP(tdb->tdb_dst.sa.sa_len);
if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
}
bzero(headers, sizeof(headers));
buffer = p;
headers[0] = p;
p += sizeof(struct sadb_msg);
smsg = (struct sadb_msg *) headers[0];
smsg->sadb_msg_version = PF_KEY_V2;
smsg->sadb_msg_type = SADB_EXPIRE;
smsg->sadb_msg_satype = tdb->tdb_satype;
smsg->sadb_msg_len = i / sizeof(uint64_t);
mtx_enter(&pfkeyv2_mtx);
smsg->sadb_msg_seq = pfkeyv2_seq++;
mtx_leave(&pfkeyv2_mtx);
headers[SADB_EXT_SA] = p;
export_sa(&p, tdb);
headers[SADB_EXT_LIFETIME_CURRENT] = p;
export_lifetime(&p, tdb, PFKEYV2_LIFETIME_CURRENT);
headers[type] = p;
export_lifetime(&p, tdb, type == SADB_EXT_LIFETIME_SOFT ?
PFKEYV2_LIFETIME_SOFT : PFKEYV2_LIFETIME_HARD);
headers[SADB_EXT_ADDRESS_SRC] = p;
export_address(&p, &tdb->tdb_src.sa);
headers[SADB_EXT_ADDRESS_DST] = p;
export_address(&p, &tdb->tdb_dst.sa);
if ((rval = pfkeyv2_sendmessage(headers, PFKEYV2_SENDMESSAGE_BROADCAST,
NULL, 0, 0, tdb->tdb_rdomain)) != 0)
goto ret;
/* XXX */
if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
if ((rval = pfkeyv2_sendmessage(headers,
PFKEYV2_SENDMESSAGE_BROADCAST, NULL, 0, 0,
tdb->tdb_rdomain_post)) != 0)
goto ret;
rval = 0;
ret:
if (buffer != NULL) {
explicit_bzero(buffer, i);
free(buffer, M_PFKEY, i);
}
return (rval);
}
struct pfkeyv2_sysctl_walk {
void *w_where;
size_t w_len;
int w_op;
u_int8_t w_satype;
};
int
pfkeyv2_sysctl_walker(struct tdb *tdb, void *arg, int last)
{
struct pfkeyv2_sysctl_walk *w = (struct pfkeyv2_sysctl_walk *)arg;
void *buffer = NULL;
int error = 0;
int usedlen, buflen, i;
if (w->w_satype != SADB_SATYPE_UNSPEC &&
w->w_satype != tdb->tdb_satype)
return (0);
if (w->w_where) {
void *headers[SADB_EXT_MAX+1];
struct sadb_msg msg;
bzero(headers, sizeof(headers));
if ((error = pfkeyv2_get(tdb, headers, &buffer, &buflen,
&usedlen)) != 0)
goto done;
if (w->w_len < sizeof(msg) + usedlen) {
error = ENOMEM;
goto done;
}
/* prepend header */
bzero(&msg, sizeof(msg));
msg.sadb_msg_version = PF_KEY_V2;
msg.sadb_msg_satype = tdb->tdb_satype;
msg.sadb_msg_type = SADB_DUMP;
msg.sadb_msg_len = (sizeof(msg) + usedlen) / sizeof(uint64_t);
if ((error = copyout(&msg, w->w_where, sizeof(msg))) != 0)
goto done;
w->w_where += sizeof(msg);
w->w_len -= sizeof(msg);
/* set extension type */
for (i = 1; i <= SADB_EXT_MAX; i++)
if (headers[i])
((struct sadb_ext *)
headers[i])->sadb_ext_type = i;
if ((error = copyout(buffer, w->w_where, usedlen)) != 0)
goto done;
w->w_where += usedlen;
w->w_len -= usedlen;
} else {
if ((error = pfkeyv2_get(tdb, NULL, NULL, &buflen, NULL)) != 0)
return (error);
w->w_len += buflen;
w->w_len += sizeof(struct sadb_msg);
}
done:
if (buffer != NULL) {
explicit_bzero(buffer, buflen);
free(buffer, M_PFKEY, buflen);
}
return (error);
}
int
pfkeyv2_dump_policy(struct ipsec_policy *ipo, void **headers, void **buffer,
int *lenp)
{
int i, rval, perm;
void *p;
/* Find how much space we need. */
i = 2 * sizeof(struct sadb_protocol);
/* We'll need four of them: src, src mask, dst, dst mask. */
switch (ipo->ipo_addr.sen_type) {
case SENT_IP4:
i += 4 * PADUP(sizeof(struct sockaddr_in));
i += 4 * sizeof(struct sadb_address);
break;
#ifdef INET6
case SENT_IP6:
i += 4 * PADUP(sizeof(struct sockaddr_in6));
i += 4 * sizeof(struct sadb_address);
break;
#endif /* INET6 */
default:
return (EINVAL);
}
/* Local address, might be zeroed. */
switch (ipo->ipo_src.sa.sa_family) {
case 0:
break;
case AF_INET:
i += PADUP(sizeof(struct sockaddr_in));
i += sizeof(struct sadb_address);
break;
#ifdef INET6
case AF_INET6:
i += PADUP(sizeof(struct sockaddr_in6));
i += sizeof(struct sadb_address);
break;
#endif /* INET6 */
default:
return (EINVAL);
}
/* Remote address, might be zeroed. XXX ??? */
switch (ipo->ipo_dst.sa.sa_family) {
case 0:
break;
case AF_INET:
i += PADUP(sizeof(struct sockaddr_in));
i += sizeof(struct sadb_address);
break;
#ifdef INET6
case AF_INET6:
i += PADUP(sizeof(struct sockaddr_in6));
i += sizeof(struct sadb_address);
break;
#endif /* INET6 */
default:
return (EINVAL);
}
if (ipo->ipo_ids) {
i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_local->len);
i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_remote->len);
}
if (lenp)
*lenp = i;
if (buffer == NULL) {
rval = 0;
goto ret;
}
if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
rval = ENOMEM;
goto ret;
} else
*buffer = p;
/* Local address. */
if (ipo->ipo_src.sa.sa_family) {
headers[SADB_EXT_ADDRESS_SRC] = p;
export_address(&p, &ipo->ipo_src.sa);
}
/* Remote address. */
if (ipo->ipo_dst.sa.sa_family) {
headers[SADB_EXT_ADDRESS_DST] = p;
export_address(&p, &ipo->ipo_dst.sa);
}
/* Get actual flow. */
export_flow(&p, ipo->ipo_type, &ipo->ipo_addr, &ipo->ipo_mask,
headers);
/* Add ids only when we are root. */
perm = suser(curproc);
if (perm == 0 && ipo->ipo_ids)
export_identities(&p, ipo->ipo_ids, 0, headers);
rval = 0;
ret:
return (rval);
}
int
pfkeyv2_sysctl_policydumper(struct ipsec_policy *ipo, void *arg,
unsigned int tableid)
{
struct pfkeyv2_sysctl_walk *w = (struct pfkeyv2_sysctl_walk *)arg;
void *buffer = 0;
int i, buflen, error = 0;
if (w->w_where) {
void *headers[SADB_EXT_MAX + 1];
struct sadb_msg msg;
bzero(headers, sizeof(headers));
if ((error = pfkeyv2_dump_policy(ipo, headers, &buffer,
&buflen)) != 0)
goto done;
if (w->w_len < buflen) {
error = ENOMEM;
goto done;
}
/* prepend header */
bzero(&msg, sizeof(msg));
msg.sadb_msg_version = PF_KEY_V2;
if (ipo->ipo_sproto == IPPROTO_ESP)
msg.sadb_msg_satype = SADB_SATYPE_ESP;
else if (ipo->ipo_sproto == IPPROTO_AH)
msg.sadb_msg_satype = SADB_SATYPE_AH;
else if (ipo->ipo_sproto == IPPROTO_IPCOMP)
msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
else if (ipo->ipo_sproto == IPPROTO_IPIP)
msg.sadb_msg_satype = SADB_X_SATYPE_IPIP;
msg.sadb_msg_type = SADB_X_SPDDUMP;
msg.sadb_msg_len = (sizeof(msg) + buflen) / sizeof(uint64_t);
if ((error = copyout(&msg, w->w_where, sizeof(msg))) != 0)
goto done;
w->w_where += sizeof(msg);
w->w_len -= sizeof(msg);
/* set extension type */
for (i = 1; i <= SADB_EXT_MAX; i++)
if (headers[i])
((struct sadb_ext *)
headers[i])->sadb_ext_type = i;
if ((error = copyout(buffer, w->w_where, buflen)) != 0)
goto done;
w->w_where += buflen;
w->w_len -= buflen;
} else {
if ((error = pfkeyv2_dump_policy(ipo, NULL, NULL,
&buflen)) != 0)
goto done;
w->w_len += buflen;
w->w_len += sizeof(struct sadb_msg);
}
done:
if (buffer)
free(buffer, M_PFKEY, buflen);
return (error);
}
int
pfkeyv2_policy_flush(struct ipsec_policy *ipo, void *arg, unsigned int tableid)
{
int error;
error = ipsec_delete_policy(ipo);
if (error == 0)
error = EAGAIN;
return (error);
}
int
pfkeyv2_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *new, size_t newlen)
{
struct pfkeyv2_sysctl_walk w;
int error = EINVAL;
u_int rdomain;
u_int tableid;
if (new)
return (EPERM);
if (namelen < 1)
return (EINVAL);
w.w_op = name[0];
w.w_satype = name[1];
w.w_where = oldp;
w.w_len = oldp ? *oldlenp : 0;
if (namelen == 3) {
tableid = name[2];
if (!rtable_exists(tableid))
return (ENOENT);
} else
tableid = curproc->p_p->ps_rtableid;
rdomain = rtable_l2(tableid);
switch(w.w_op) {
case NET_KEY_SADB_DUMP:
if ((error = suser(curproc)) != 0)
return (error);
NET_LOCK();
error = tdb_walk(rdomain, pfkeyv2_sysctl_walker, &w);
NET_UNLOCK();
if (oldp)
*oldlenp = w.w_where - oldp;
else
*oldlenp = w.w_len;
break;
case NET_KEY_SPD_DUMP:
NET_LOCK();
error = spd_table_walk(rdomain,
pfkeyv2_sysctl_policydumper, &w);
NET_UNLOCK();
if (oldp)
*oldlenp = w.w_where - oldp;
else
*oldlenp = w.w_len;
break;
}
return (error);
}
8
5
1
3
1
2
2
1
2
2
1
2
1
2
1
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
/* $OpenBSD: tty_msts.c,v 1.21 2018/02/19 08:59:52 mpi Exp $ */
/*
* Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* A tty line discipline to decode the Meinberg Standard Time String
* to get the time (http://www.meinberg.de/english/specs/timestr.htm).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sensors.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/time.h>
#ifdef MSTS_DEBUG
#define DPRINTFN(n, x) do { if (mstsdebug > (n)) printf x; } while (0)
int mstsdebug = 0;
#else
#define DPRINTFN(n, x)
#endif
#define DPRINTF(x) DPRINTFN(0, x)
void mstsattach(int);
#define MSTSMAX 32
#define MAXFLDS 4
#ifdef MSTS_DEBUG
#define TRUSTTIME 30
#else
#define TRUSTTIME (10 * 60) /* 10 minutes */
#endif
int msts_count, msts_nxid;
struct msts {
char cbuf[MSTSMAX]; /* receive buffer */
struct ksensor time; /* the timedelta sensor */
struct ksensor signal; /* signal status */
struct ksensordev timedev;
struct timespec ts; /* current timestamp */
struct timespec lts; /* timestamp of last <STX> */
struct timeout msts_tout; /* invalidate sensor */
int64_t gap; /* gap between two sentences */
int64_t last; /* last time rcvd */
int sync; /* if 1, waiting for <STX> */
int pos; /* position in rcv buffer */
int no_pps; /* no PPS although requested */
};
/* MSTS decoding */
void msts_scan(struct msts *, struct tty *);
void msts_decode(struct msts *, struct tty *, char *fld[], int fldcnt);
/* date and time conversion */
int msts_date_to_nano(char *s, int64_t *nano);
int msts_time_to_nano(char *s, int64_t *nano);
/* degrade the timedelta sensor */
void msts_timeout(void *);
void
mstsattach(int dummy)
{
}
int
mstsopen(dev_t dev, struct tty *tp, struct proc *p)
{
struct msts *np;
int error;
DPRINTF(("mstsopen\n"));
if (tp->t_line == MSTSDISC)
return ENODEV;
if ((error = suser(p)) != 0)
return error;
np = malloc(sizeof(struct msts), M_DEVBUF, M_WAITOK|M_ZERO);
snprintf(np->timedev.xname, sizeof(np->timedev.xname), "msts%d",
msts_nxid++);
msts_count++;
np->time.status = SENSOR_S_UNKNOWN;
np->time.type = SENSOR_TIMEDELTA;
#ifndef MSTS_DEBUG
np->time.flags = SENSOR_FINVALID;
#endif
sensor_attach(&np->timedev, &np->time);
np->signal.type = SENSOR_PERCENT;
np->signal.status = SENSOR_S_UNKNOWN;
np->signal.value = 100000LL;
strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
sensor_attach(&np->timedev, &np->signal);
np->sync = 1;
tp->t_sc = (caddr_t)np;
error = linesw[TTYDISC].l_open(dev, tp, p);
if (error) {
free(np, M_DEVBUF, sizeof(*np));
tp->t_sc = NULL;
} else {
sensordev_install(&np->timedev);
timeout_set(&np->msts_tout, msts_timeout, np);
}
return error;
}
int
mstsclose(struct tty *tp, int flags, struct proc *p)
{
struct msts *np = (struct msts *)tp->t_sc;
tp->t_line = TTYDISC; /* switch back to termios */
timeout_del(&np->msts_tout);
sensordev_deinstall(&np->timedev);
free(np, M_DEVBUF, sizeof(*np));
tp->t_sc = NULL;
msts_count--;
if (msts_count == 0)
msts_nxid = 0;
return linesw[TTYDISC].l_close(tp, flags, p);
}
/* collect MSTS sentence from tty */
int
mstsinput(int c, struct tty *tp)
{
struct msts *np = (struct msts *)tp->t_sc;
struct timespec ts;
int64_t gap;
long tmin, tmax;
switch (c) {
case 2: /* ASCII <STX> */
nanotime(&ts);
np->pos = np->sync = 0;
gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
(np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
np->lts.tv_sec = ts.tv_sec;
np->lts.tv_nsec = ts.tv_nsec;
if (gap <= np->gap)
break;
np->ts.tv_sec = ts.tv_sec;
np->ts.tv_nsec = ts.tv_nsec;
np->gap = gap;
/*
* If a tty timestamp is available, make sure its value is
* reasonable by comparing against the timestamp just taken.
* If they differ by more than 2 seconds, assume no PPS signal
* is present, note the fact, and keep using the timestamp
* value. When this happens, the sensor state is set to
* CRITICAL later when the MSTS sentence is decoded.
*/
if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
if (tmax - tmin > 1)
np->no_pps = 1;
else {
np->ts.tv_sec = tp->t_tv.tv_sec;
np->ts.tv_nsec = tp->t_tv.tv_usec *
1000L;
np->no_pps = 0;
}
}
break;
case 3: /* ASCII <ETX> */
if (!np->sync) {
np->cbuf[np->pos] = '\0';
msts_scan(np, tp);
np->sync = 1;
}
break;
default:
if (!np->sync && np->pos < (MSTSMAX - 1))
np->cbuf[np->pos++] = c;
break;
}
/* pass data to termios */
return linesw[TTYDISC].l_rint(c, tp);
}
/* Scan the MSTS sentence just received */
void
msts_scan(struct msts *np, struct tty *tp)
{
int fldcnt = 0, n;
char *fld[MAXFLDS], *cs;
/* split into fields */
fld[fldcnt++] = &np->cbuf[0];
for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
switch (np->cbuf[n]) {
case 3: /* ASCII <ETX> */
np->cbuf[n] = '\0';
cs = &np->cbuf[n + 1];
break;
case ';':
if (fldcnt < MAXFLDS) {
np->cbuf[n] = '\0';
fld[fldcnt++] = &np->cbuf[n + 1];
} else {
DPRINTF(("nr of fields in sentence exceeds "
"maximum of %d\n", MAXFLDS));
return;
}
break;
}
}
msts_decode(np, tp, fld, fldcnt);
}
/* Decode the time string */
void
msts_decode(struct msts *np, struct tty *tp, char *fld[], int fldcnt)
{
int64_t date_nano, time_nano, msts_now;
int jumped = 0;
if (fldcnt != MAXFLDS) {
DPRINTF(("msts: field count mismatch, %d\n", fldcnt));
return;
}
if (msts_time_to_nano(fld[2], &time_nano)) {
DPRINTF(("msts: illegal time, %s\n", fld[2]));
return;
}
if (msts_date_to_nano(fld[0], &date_nano)) {
DPRINTF(("msts: illegal date, %s\n", fld[0]));
return;
}
msts_now = date_nano + time_nano;
if ( fld[3][2] == ' ' ) /* received time in CET */
msts_now = msts_now - 3600 * 1000000000LL;
if ( fld[3][2] == 'S' ) /* received time in CEST */
msts_now = msts_now - 2 * 3600 * 1000000000LL;
if (msts_now <= np->last) {
DPRINTF(("msts: time not monotonically increasing\n"));
jumped = 1;
}
np->last = msts_now;
np->gap = 0LL;
#ifdef MSTS_DEBUG
if (np->time.status == SENSOR_S_UNKNOWN) {
np->time.status = SENSOR_S_OK;
timeout_add_sec(&np->msts_tout, TRUSTTIME);
}
#endif
np->time.value = np->ts.tv_sec * 1000000000LL +
np->ts.tv_nsec - msts_now;
np->time.tv.tv_sec = np->ts.tv_sec;
np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
if (np->time.status == SENSOR_S_UNKNOWN) {
np->time.status = SENSOR_S_OK;
np->time.flags &= ~SENSOR_FINVALID;
strlcpy(np->time.desc, "MSTS", sizeof(np->time.desc));
}
/*
* only update the timeout if the clock reports the time a valid,
* the status is reported in fld[3][0] and fld[3][1] as follows:
* fld[3][0] == '#' critical
* fld[3][0] == ' ' && fld[3][1] == '*' warning
* fld[3][0] == ' ' && fld[3][1] == ' ' ok
*/
if (fld[3][0] == ' ' && fld[3][1] == ' ') {
np->time.status = SENSOR_S_OK;
np->signal.status = SENSOR_S_OK;
} else
np->signal.status = SENSOR_S_WARN;
if (jumped)
np->time.status = SENSOR_S_WARN;
if (np->time.status == SENSOR_S_OK)
timeout_add_sec(&np->msts_tout, TRUSTTIME);
/*
* If tty timestamping is requested, but no PPS signal is present, set
* the sensor state to CRITICAL.
*/
if (np->no_pps)
np->time.status = SENSOR_S_CRIT;
}
/*
* Convert date field from MSTS to nanoseconds since the epoch.
* The string must be of the form D:DD.MM.YY .
* Return 0 on success, -1 if illegal characters are encountered.
*/
int
msts_date_to_nano(char *s, int64_t *nano)
{
struct clock_ymdhms ymd;
time_t secs;
char *p;
int n;
if (s[0] != 'D' || s[1] != ':' || s[4] != '.' || s[7] != '.')
return -1;
/* shift numbers to DDMMYY */
s[0]=s[2];
s[1]=s[3];
s[2]=s[5];
s[3]=s[6];
s[4]=s[8];
s[5]=s[9];
s[6]='\0';
/* make sure the input contains only numbers and is six digits long */
for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
;
if (n != 6 || (*p != '\0'))
return -1;
ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
secs = clock_ymdhms_to_secs(&ymd);
*nano = secs * 1000000000LL;
return 0;
}
/*
* Convert time field from MSTS to nanoseconds since midnight.
* The string must be of the form U:HH.MM.SS .
* Return 0 on success, -1 if illegal characters are encountered.
*/
int
msts_time_to_nano(char *s, int64_t *nano)
{
long fac = 36000L, div = 6L, secs = 0L;
char ul = '2';
int n;
if (s[0] != 'U' || s[1] != ':' || s[4] != '.' || s[7] != '.')
return -1;
/* shift numbers to HHMMSS */
s[0]=s[2];
s[1]=s[3];
s[2]=s[5];
s[3]=s[6];
s[4]=s[8];
s[5]=s[9];
s[6]='\0';
for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
secs += (*s - '0') * fac;
div = 16 - div;
fac /= div;
switch (n) {
case 0:
if (*s <= '1')
ul = '9';
else
ul = '3';
break;
case 1:
case 3:
ul = '5';
break;
case 2:
case 4:
ul = '9';
break;
}
}
if (fac)
return -1;
if (*s != '\0')
return -1;
*nano = secs * 1000000000LL;
return 0;
}
/*
* Degrade the sensor state if we received no MSTS string for more than
* TRUSTTIME seconds.
*/
void
msts_timeout(void *xnp)
{
struct msts *np = xnp;
if (np->time.status == SENSOR_S_OK) {
np->time.status = SENSOR_S_WARN;
/*
* further degrade in TRUSTTIME seconds if no new valid MSTS
* strings are received.
*/
timeout_add_sec(&np->msts_tout, TRUSTTIME);
} else
np->time.status = SENSOR_S_CRIT;
}
6
2
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
/* $OpenBSD: ip_ipip.c,v 1.98 2022/01/02 22:36:04 jsg Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
/*
* IP-inside-IP processing
*/
#include "bpfilter.h"
#include "gif.h"
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/netisr.h>
#include <net/bpf.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_ecn.h>
#include <netinet/ip_ipip.h>
#ifdef MROUTING
#include <netinet/ip_mroute.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
/*
* We can control the acceptance of IP4 packets by altering the sysctl
* net.inet.ipip.allow value. Zero means drop them, all else is acceptance.
*/
int ipip_allow = 0;
struct cpumem *ipipcounters;
void
ipip_init(void)
{
ipipcounters = counters_alloc(ipips_ncounters);
}
/*
* Really only a wrapper for ipip_input_if(), for use with pr_input.
*/
int
ipip_input(struct mbuf **mp, int *offp, int nxt, int af)
{
struct ifnet *ifp;
/* If we do not accept IP-in-IP explicitly, drop. */
if (!ipip_allow && ((*mp)->m_flags & (M_AUTH|M_CONF)) == 0) {
DPRINTF("dropped due to policy");
ipipstat_inc(ipips_pdrops);
m_freemp(mp);
return IPPROTO_DONE;
}
ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
m_freemp(mp);
return IPPROTO_DONE;
}
nxt = ipip_input_if(mp, offp, nxt, af, ifp);
if_put(ifp);
return nxt;
}
/*
* ipip_input gets called when we receive an IP{46} encapsulated packet,
* either because we got it at a real interface, or because AH or ESP
* were being used in tunnel mode (in which case the ph_ifidx element
* will contain the index of the encX interface associated with the
* tunnel.
*/
int
ipip_input_if(struct mbuf **mp, int *offp, int proto, int oaf,
struct ifnet *ifp)
{
struct mbuf *m = *mp;
struct sockaddr_in *sin;
struct ip *ip;
#ifdef INET6
struct sockaddr_in6 *sin6;
struct ip6_hdr *ip6;
#endif
int mode, hlen;
u_int8_t itos, otos;
sa_family_t iaf;
ipipstat_inc(ipips_ipackets);
switch (oaf) {
case AF_INET:
hlen = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
hlen = sizeof(struct ip6_hdr);
break;
#endif
default:
unhandled_af(oaf);
}
/* Bring the IP header in the first mbuf, if not there already */
if (m->m_len < hlen) {
if ((m = *mp = m_pullup(m, hlen)) == NULL) {
DPRINTF("m_pullup() failed");
ipipstat_inc(ipips_hdrops);
goto bad;
}
}
/* Keep outer ecn field. */
switch (oaf) {
case AF_INET:
ip = mtod(m, struct ip *);
otos = ip->ip_tos;
break;
#ifdef INET6
case AF_INET6:
ip6 = mtod(m, struct ip6_hdr *);
otos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
break;
#endif
}
/* Remove outer IP header */
KASSERT(*offp > 0);
m_adj(m, *offp);
*offp = 0;
ip = NULL;
#ifdef INET6
ip6 = NULL;
#endif
switch (proto) {
case IPPROTO_IPV4:
hlen = sizeof(struct ip);
break;
#ifdef INET6
case IPPROTO_IPV6:
hlen = sizeof(struct ip6_hdr);
break;
#endif
default:
ipipstat_inc(ipips_family);
goto bad;
}
/* Sanity check */
if (m->m_pkthdr.len < hlen) {
ipipstat_inc(ipips_hdrops);
goto bad;
}
/*
* Bring the inner header into the first mbuf, if not there already.
*/
if (m->m_len < hlen) {
if ((m = *mp = m_pullup(m, hlen)) == NULL) {
DPRINTF("m_pullup() failed");
ipipstat_inc(ipips_hdrops);
goto bad;
}
}
/*
* RFC 1853 specifies that the inner TTL should not be touched on
* decapsulation. There's no reason this comment should be here, but
* this is as good as any a position.
*/
/* Some sanity checks in the inner IP header */
switch (proto) {
case IPPROTO_IPV4:
iaf = AF_INET;
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
if (m->m_pkthdr.len < hlen) {
ipipstat_inc(ipips_hdrops);
goto bad;
}
itos = ip->ip_tos;
mode = m->m_flags & (M_AUTH|M_CONF) ?
ECN_ALLOWED_IPSEC : ECN_ALLOWED;
if (!ip_ecn_egress(mode, &otos, &itos)) {
DPRINTF("ip_ecn_egress() failed");
ipipstat_inc(ipips_pdrops);
goto bad;
}
/* re-calculate the checksum if ip_tos was changed */
if (itos != ip->ip_tos)
ip_tos_patch(ip, itos);
break;
#ifdef INET6
case IPPROTO_IPV6:
iaf = AF_INET6;
ip6 = mtod(m, struct ip6_hdr *);
itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
if (!ip_ecn_egress(ECN_ALLOWED, &otos, &itos)) {
DPRINTF("ip_ecn_egress() failed");
ipipstat_inc(ipips_pdrops);
goto bad;
}
ip6->ip6_flow &= ~htonl(0xff << 20);
ip6->ip6_flow |= htonl((u_int32_t) itos << 20);
break;
#endif
}
/* Check for local address spoofing. */
if (!(ifp->if_flags & IFF_LOOPBACK) && ipip_allow != 2) {
struct sockaddr_storage ss;
struct rtentry *rt;
memset(&ss, 0, sizeof(ss));
if (ip) {
sin = (struct sockaddr_in *)&ss;
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = ip->ip_src;
#ifdef INET6
} else if (ip6) {
sin6 = (struct sockaddr_in6 *)&ss;
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_addr = ip6->ip6_src;
#endif /* INET6 */
}
rt = rtalloc(sstosa(&ss), 0, m->m_pkthdr.ph_rtableid);
if ((rt != NULL) && (rt->rt_flags & RTF_LOCAL)) {
ipipstat_inc(ipips_spoof);
rtfree(rt);
goto bad;
}
rtfree(rt);
}
/* Statistics */
ipipstat_add(ipips_ibytes, m->m_pkthdr.len - hlen);
#if NBPFILTER > 0 && NGIF > 0
if (ifp->if_type == IFT_GIF && ifp->if_bpf != NULL)
bpf_mtap_af(ifp->if_bpf, iaf, m, BPF_DIRECTION_IN);
#endif
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
/*
* Interface pointer stays the same; if no IPsec processing has
* been done (or will be done), this will point to a normal
* interface. Otherwise, it'll point to an enc interface, which
* will allow a packet filter to distinguish between secure and
* untrusted packets.
*/
switch (proto) {
case IPPROTO_IPV4:
return ip_input_if(mp, offp, proto, oaf, ifp);
#ifdef INET6
case IPPROTO_IPV6:
return ip6_input_if(mp, offp, proto, oaf, ifp);
#endif
}
bad:
m_freemp(mp);
return IPPROTO_DONE;
}
int
ipip_output(struct mbuf **mp, struct tdb *tdb)
{
struct mbuf *m = *mp;
u_int8_t tp, otos, itos;
u_int64_t obytes;
struct ip *ipo;
#ifdef INET6
struct ip6_hdr *ip6, *ip6o;
#endif /* INET6 */
#ifdef ENCDEBUG
char buf[INET6_ADDRSTRLEN];
#endif
int error;
/* XXX Deal with empty TDB source/destination addresses. */
m_copydata(m, 0, 1, &tp);
tp = (tp >> 4) & 0xff; /* Get the IP version number. */
switch (tdb->tdb_dst.sa.sa_family) {
case AF_INET:
if (tdb->tdb_src.sa.sa_family != AF_INET ||
tdb->tdb_src.sin.sin_addr.s_addr == INADDR_ANY ||
tdb->tdb_dst.sin.sin_addr.s_addr == INADDR_ANY) {
DPRINTF("unspecified tunnel endpoint address "
"in SA %s/%08x",
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
ntohl(tdb->tdb_spi));
ipipstat_inc(ipips_unspec);
error = EINVAL;
goto drop;
}
M_PREPEND(*mp, sizeof(struct ip), M_DONTWAIT);
if (*mp == NULL) {
DPRINTF("M_PREPEND failed");
ipipstat_inc(ipips_hdrops);
error = ENOBUFS;
goto drop;
}
m = *mp;
ipo = mtod(m, struct ip *);
ipo->ip_v = IPVERSION;
ipo->ip_hl = 5;
ipo->ip_len = htons(m->m_pkthdr.len);
ipo->ip_ttl = ip_defttl;
ipo->ip_sum = 0;
ipo->ip_src = tdb->tdb_src.sin.sin_addr;
ipo->ip_dst = tdb->tdb_dst.sin.sin_addr;
/*
* We do the htons() to prevent snoopers from determining our
* endianness.
*/
ipo->ip_id = htons(ip_randomid());
/* If the inner protocol is IP... */
if (tp == IPVERSION) {
/* Save ECN notification */
m_copydata(m, sizeof(struct ip) +
offsetof(struct ip, ip_tos),
sizeof(u_int8_t), (caddr_t) &itos);
ipo->ip_p = IPPROTO_IPIP;
/*
* We should be keeping tunnel soft-state and
* send back ICMPs if needed.
*/
m_copydata(m, sizeof(struct ip) +
offsetof(struct ip, ip_off),
sizeof(u_int16_t), (caddr_t) &ipo->ip_off);
ipo->ip_off = ntohs(ipo->ip_off);
ipo->ip_off &= ~(IP_DF | IP_MF | IP_OFFMASK);
ipo->ip_off = htons(ipo->ip_off);
}
#ifdef INET6
else if (tp == (IPV6_VERSION >> 4)) {
u_int32_t itos32;
/* Save ECN notification. */
m_copydata(m, sizeof(struct ip) +
offsetof(struct ip6_hdr, ip6_flow),
sizeof(u_int32_t), (caddr_t) &itos32);
itos = ntohl(itos32) >> 20;
ipo->ip_p = IPPROTO_IPV6;
ipo->ip_off = 0;
}
#endif /* INET6 */
else {
ipipstat_inc(ipips_family);
error = EAFNOSUPPORT;
goto drop;
}
otos = 0;
ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
ipo->ip_tos = otos;
obytes = m->m_pkthdr.len - sizeof(struct ip);
if (tdb->tdb_xform->xf_type == XF_IP4)
tdb->tdb_cur_bytes += obytes;
break;
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr) ||
tdb->tdb_src.sa.sa_family != AF_INET6 ||
IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_src.sin6.sin6_addr)) {
DPRINTF("unspecified tunnel endpoint address "
"in SA %s/%08x",
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
ntohl(tdb->tdb_spi));
ipipstat_inc(ipips_unspec);
error = EINVAL;
goto drop;
}
/* If the inner protocol is IPv6, clear link local scope */
if (tp == (IPV6_VERSION >> 4)) {
/* scoped address handling */
ip6 = mtod(m, struct ip6_hdr *);
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = 0;
}
M_PREPEND(*mp, sizeof(struct ip6_hdr), M_DONTWAIT);
if (*mp == NULL) {
DPRINTF("M_PREPEND failed");
ipipstat_inc(ipips_hdrops);
error = ENOBUFS;
goto drop;
}
m = *mp;
/* Initialize IPv6 header */
ip6o = mtod(m, struct ip6_hdr *);
ip6o->ip6_flow = 0;
ip6o->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6o->ip6_vfc |= IPV6_VERSION;
ip6o->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6o));
ip6o->ip6_hlim = ip_defttl;
in6_embedscope(&ip6o->ip6_src, &tdb->tdb_src.sin6, NULL);
in6_embedscope(&ip6o->ip6_dst, &tdb->tdb_dst.sin6, NULL);
if (tp == IPVERSION) {
/* Save ECN notification */
m_copydata(m, sizeof(struct ip6_hdr) +
offsetof(struct ip, ip_tos), sizeof(u_int8_t),
(caddr_t) &itos);
/* This is really IPVERSION. */
ip6o->ip6_nxt = IPPROTO_IPIP;
}
else
if (tp == (IPV6_VERSION >> 4)) {
u_int32_t itos32;
/* Save ECN notification. */
m_copydata(m, sizeof(struct ip6_hdr) +
offsetof(struct ip6_hdr, ip6_flow),
sizeof(u_int32_t), (caddr_t) &itos32);
itos = ntohl(itos32) >> 20;
ip6o->ip6_nxt = IPPROTO_IPV6;
} else {
ipipstat_inc(ipips_family);
error = EAFNOSUPPORT;
goto drop;
}
otos = 0;
ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
ip6o->ip6_flow |= htonl((u_int32_t) otos << 20);
obytes = m->m_pkthdr.len - sizeof(struct ip6_hdr);
if (tdb->tdb_xform->xf_type == XF_IP4)
tdb->tdb_cur_bytes += obytes;
break;
#endif /* INET6 */
default:
DPRINTF("unsupported protocol family %d",
tdb->tdb_dst.sa.sa_family);
ipipstat_inc(ipips_family);
error = EPFNOSUPPORT;
goto drop;
}
ipipstat_pkt(ipips_opackets, ipips_obytes, obytes);
return 0;
drop:
m_freemp(mp);
return error;
}
#ifdef IPSEC
int
ipe4_attach(void)
{
return 0;
}
int
ipe4_init(struct tdb *tdbp, const struct xformsw *xsp, struct ipsecinit *ii)
{
tdbp->tdb_xform = xsp;
return 0;
}
int
ipe4_zeroize(struct tdb *tdbp)
{
return 0;
}
int
ipe4_input(struct mbuf **mp, struct tdb *tdb, int hlen, int proto)
{
/* This is a rather serious mistake, so no conditional printing. */
printf("%s: should never be called\n", __func__);
m_freemp(mp);
return EINVAL;
}
#endif /* IPSEC */
int
ipip_sysctl_ipipstat(void *oldp, size_t *oldlenp, void *newp)
{
struct ipipstat ipipstat;
CTASSERT(sizeof(ipipstat) == (ipips_ncounters * sizeof(uint64_t)));
memset(&ipipstat, 0, sizeof ipipstat);
counters_read(ipipcounters, (uint64_t *)&ipipstat, ipips_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp,
&ipipstat, sizeof(ipipstat)));
}
int
ipip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case IPIPCTL_ALLOW:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&ipip_allow, 0, 2);
NET_UNLOCK();
return (error);
case IPIPCTL_STATS:
return (ipip_sysctl_ipipstat(oldp, oldlenp, newp));
default:
return (ENOPROTOOPT);
}
/* NOTREACHED */
}
56
13
92
56
112
94
21
3
3
19
76
3
9
10
66
38
92
72
27
8
72
13
2
14
11
9
19
16
5
5
14
1
13
1
48
2
2
2
6
11
11
8
12
3
3
1
31
17
13
2
4
5
3
2
8
4
72
10
92
77
67
51
3
4
6
30
19
34
22
6
4
110
109
2
2
2
10
6
4
4
44
6
4
5
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
/* $OpenBSD: vfs_lockf.c,v 1.50 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_lockf.c,v 1.7 1996/02/04 02:18:21 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Scooter Morris at Genentech Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ufs_lockf.c 8.3 (Berkeley) 1/6/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/rwlock.h>
#include <sys/unistd.h>
/*
* The lockf structure is a kernel structure which contains the information
* associated with a byte range lock. The lockf structures are linked into
* the inode structure. Locks are sorted by the starting byte of the lock for
* efficiency.
*/
TAILQ_HEAD(locklist, lockf);
struct lockf {
short lf_flags; /* Lock semantics: F_POSIX, F_FLOCK, F_WAIT */
short lf_type; /* Lock type: F_RDLCK, F_WRLCK */
off_t lf_start; /* The byte # of the start of the lock */
off_t lf_end; /* The byte # of the end of the lock (-1=EOF)*/
caddr_t lf_id; /* The id of the resource holding the lock */
struct lockf_state *lf_state; /* State associated with the lock */
TAILQ_ENTRY(lockf) lf_entry;
struct lockf *lf_blk; /* The lock that blocks us */
struct locklist lf_blkhd; /* The list of blocked locks */
TAILQ_ENTRY(lockf) lf_block; /* A request waiting for a lock */
uid_t lf_uid; /* User ID responsible */
pid_t lf_pid; /* POSIX - owner pid */
};
struct lockf_state {
TAILQ_HEAD(, lockf) ls_locks; /* list of active locks */
TAILQ_HEAD(, lockf) ls_pending; /* list of pending locks */
struct lockf_state **ls_owner; /* owner */
int ls_refs; /* reference counter */
};
struct pool lockf_state_pool;
struct pool lockf_pool;
#define SELF 0x1
#define OTHERS 0x2
#ifdef LOCKF_DEBUG
#define DEBUG_SETLOCK 0x01
#define DEBUG_CLEARLOCK 0x02
#define DEBUG_GETLOCK 0x04
#define DEBUG_FINDOVR 0x08
#define DEBUG_SPLIT 0x10
#define DEBUG_WAKELOCK 0x20
#define DEBUG_LINK 0x40
int lockf_debug = DEBUG_SETLOCK|DEBUG_CLEARLOCK|DEBUG_WAKELOCK;
void lf_print(const char *, struct lockf *);
void lf_printlist(const char *, struct lockf *);
#define DPRINTF(args, level) if (lockf_debug & (level)) printf args
#define LFPRINT(args, level) if (lockf_debug & (level)) lf_print args
#else
#define DPRINTF(args, level)
#define LFPRINT(args, level)
#endif
struct lockf *lf_alloc(uid_t, int);
void lf_free(struct lockf *);
int lf_clearlock(struct lockf *);
int lf_findoverlap(struct lockf *, struct lockf *, int, struct lockf **);
struct lockf *lf_getblock(struct lockf *, struct lockf *);
int lf_getlock(struct lockf *, struct flock *);
int lf_setlock(struct lockf *);
void lf_split(struct lockf *, struct lockf *);
void lf_wakelock(struct lockf *, int);
int lf_deadlock(struct lockf *);
void ls_ref(struct lockf_state *);
void ls_rele(struct lockf_state *);
/*
* Serializes access to each instance of struct lockf and struct lockf_state
* and each pointer from a vnode to struct lockf_state.
*/
struct rwlock lockf_lock = RWLOCK_INITIALIZER("lockflk");
void
lf_init(void)
{
pool_init(&lockf_state_pool, sizeof(struct lockf_state), 0, IPL_NONE,
PR_WAITOK | PR_RWLOCK, "lockfspl", NULL);
pool_init(&lockf_pool, sizeof(struct lockf), 0, IPL_NONE,
PR_WAITOK | PR_RWLOCK, "lockfpl", NULL);
}
void
ls_ref(struct lockf_state *ls)
{
rw_assert_wrlock(&lockf_lock);
ls->ls_refs++;
}
void
ls_rele(struct lockf_state *ls)
{
rw_assert_wrlock(&lockf_lock);
if (--ls->ls_refs > 0)
return;
KASSERT(TAILQ_EMPTY(&ls->ls_locks));
KASSERT(TAILQ_EMPTY(&ls->ls_pending));
*ls->ls_owner = NULL;
pool_put(&lockf_state_pool, ls);
}
/*
* We enforce a limit on locks by uid, so that a single user cannot
* run the kernel out of memory. For now, the limit is pretty coarse.
* There is no limit on root.
*
* Splitting a lock will always succeed, regardless of current allocations.
* If you're slightly above the limit, we still have to permit an allocation
* so that the unlock can succeed. If the unlocking causes too many splits,
* however, you're totally cutoff.
*/
int maxlocksperuid = 1024;
/*
* 3 options for allowfail.
* 0 - always allocate. 1 - cutoff at limit. 2 - cutoff at double limit.
*/
struct lockf *
lf_alloc(uid_t uid, int allowfail)
{
struct uidinfo *uip;
struct lockf *lock;
uip = uid_find(uid);
if (uid && allowfail && uip->ui_lockcnt >
(allowfail == 1 ? maxlocksperuid : (maxlocksperuid * 2))) {
uid_release(uip);
return (NULL);
}
uip->ui_lockcnt++;
uid_release(uip);
lock = pool_get(&lockf_pool, PR_WAITOK);
lock->lf_uid = uid;
return (lock);
}
void
lf_free(struct lockf *lock)
{
struct uidinfo *uip;
rw_assert_wrlock(&lockf_lock);
LFPRINT(("lf_free", lock), DEBUG_LINK);
KASSERT(TAILQ_EMPTY(&lock->lf_blkhd));
ls_rele(lock->lf_state);
uip = uid_find(lock->lf_uid);
uip->ui_lockcnt--;
uid_release(uip);
pool_put(&lockf_pool, lock);
}
/*
* Do an advisory lock operation.
*/
int
lf_advlock(struct lockf_state **state, off_t size, caddr_t id, int op,
struct flock *fl, int flags)
{
struct proc *p = curproc;
struct lockf_state *ls;
struct lockf *lock;
off_t start, end;
int error = 0;
/*
* Convert the flock structure into a start and end.
*/
switch (fl->l_whence) {
case SEEK_SET:
case SEEK_CUR:
/*
* Caller is responsible for adding any necessary offset
* when SEEK_CUR is used.
*/
start = fl->l_start;
break;
case SEEK_END:
start = size + fl->l_start;
break;
default:
return (EINVAL);
}
if (start < 0)
return (EINVAL);
if (fl->l_len > 0) {
if (fl->l_len - 1 > LLONG_MAX - start)
return (EOVERFLOW);
end = start + (fl->l_len - 1);
/* Avoid ambiguity at the end of the range. */
if (end == LLONG_MAX)
end = -1;
} else if (fl->l_len < 0) {
if (start + fl->l_len < 0)
return (EINVAL);
end = start - 1;
start += fl->l_len;
} else {
end = -1;
}
rw_enter_write(&lockf_lock);
ls = *state;
/*
* Avoid the common case of unlocking when inode has no locks.
*/
if (ls == NULL && op != F_SETLK) {
fl->l_type = F_UNLCK;
goto out;
}
if (ls == NULL) {
ls = pool_get(&lockf_state_pool, PR_WAITOK | PR_ZERO);
ls->ls_owner = state;
TAILQ_INIT(&ls->ls_locks);
TAILQ_INIT(&ls->ls_pending);
*state = ls;
}
ls_ref(ls);
lock = lf_alloc(p->p_ucred->cr_uid, op == F_SETLK ? 1 : 2);
if (!lock) {
ls_rele(ls);
error = ENOLCK;
goto out;
}
lock->lf_flags = flags;
lock->lf_type = fl->l_type;
lock->lf_start = start;
lock->lf_end = end;
lock->lf_id = id;
lock->lf_state = ls;
lock->lf_blk = NULL;
lock->lf_pid = (flags & F_POSIX) ? p->p_p->ps_pid : -1;
TAILQ_INIT(&lock->lf_blkhd);
switch (op) {
case F_SETLK:
error = lf_setlock(lock);
break;
case F_UNLCK:
error = lf_clearlock(lock);
lf_free(lock);
break;
case F_GETLK:
error = lf_getlock(lock, fl);
lf_free(lock);
break;
default:
lf_free(lock);
error = EINVAL;
break;
}
out:
rw_exit_write(&lockf_lock);
return (error);
}
/*
* Set a byte-range lock.
*/
int
lf_setlock(struct lockf *lock)
{
struct lockf *block;
struct lockf *overlap, *ltmp;
int ovcase, priority, needtolink, error;
rw_assert_wrlock(&lockf_lock);
LFPRINT(("lf_setlock", lock), DEBUG_SETLOCK);
priority = PLOCK;
if (lock->lf_type == F_WRLCK)
priority += 4;
priority |= PCATCH;
/*
* Scan lock list for this file looking for locks that would block us.
*/
for (;;) {
block = lf_getblock(TAILQ_FIRST(&lock->lf_state->ls_locks),
lock);
if (block == NULL)
break;
if ((lock->lf_flags & F_WAIT) == 0) {
lf_free(lock);
return (EAGAIN);
}
/*
* Lock is blocked, check for deadlock before proceeding.
* Note: flock style locks cover the whole file, there is no
* chance for deadlock.
*/
if ((lock->lf_flags & F_POSIX) && lf_deadlock(lock)) {
lf_free(lock);
return (EDEADLK);
}
/*
* For flock type locks, we must first remove
* any shared locks that we hold before we sleep
* waiting for an exclusive lock.
*/
if ((lock->lf_flags & F_FLOCK) && lock->lf_type == F_WRLCK) {
lock->lf_type = F_UNLCK;
(void)lf_clearlock(lock);
lock->lf_type = F_WRLCK;
}
/*
* Add our lock to the blocked list and sleep until we're free.
* Remember who blocked us (for deadlock detection).
*/
lock->lf_blk = block;
LFPRINT(("lf_setlock", lock), DEBUG_SETLOCK);
LFPRINT(("lf_setlock: blocking on", block), DEBUG_SETLOCK);
TAILQ_INSERT_TAIL(&block->lf_blkhd, lock, lf_block);
TAILQ_INSERT_TAIL(&lock->lf_state->ls_pending, lock, lf_entry);
error = rwsleep_nsec(lock, &lockf_lock, priority, "lockf",
INFSLP);
TAILQ_REMOVE(&lock->lf_state->ls_pending, lock, lf_entry);
wakeup_one(lock->lf_state);
if (lock->lf_blk != NULL) {
TAILQ_REMOVE(&lock->lf_blk->lf_blkhd, lock, lf_block);
lock->lf_blk = NULL;
}
if (error) {
lf_free(lock);
return (error);
}
if (lock->lf_flags & F_INTR) {
lf_free(lock);
return (EINTR);
}
}
/*
* No blocks!! Add the lock. Note that we will
* downgrade or upgrade any overlapping locks this
* process already owns.
*
* Skip over locks owned by other processes.
* Handle any locks that overlap and are owned by ourselves.
*/
block = TAILQ_FIRST(&lock->lf_state->ls_locks);
overlap = NULL;
needtolink = 1;
for (;;) {
ovcase = lf_findoverlap(block, lock, SELF, &overlap);
if (ovcase)
block = TAILQ_NEXT(overlap, lf_entry);
/*
* Six cases:
* 0) no overlap
* 1) overlap == lock
* 2) overlap contains lock
* 3) lock contains overlap
* 4) overlap starts before lock
* 5) overlap ends after lock
*/
switch (ovcase) {
case 0: /* no overlap */
if (needtolink) {
if (overlap) /* insert before overlap */
TAILQ_INSERT_BEFORE(overlap, lock,
lf_entry);
else /* first or last lock in list */
TAILQ_INSERT_TAIL(&lock->lf_state->ls_locks,
lock, lf_entry);
}
break;
case 1: /* overlap == lock */
/*
* If downgrading lock, others may be
* able to acquire it.
*/
if (lock->lf_type == F_RDLCK &&
overlap->lf_type == F_WRLCK)
lf_wakelock(overlap, 0);
overlap->lf_type = lock->lf_type;
lf_free(lock);
lock = overlap; /* for debug output below */
break;
case 2: /* overlap contains lock */
/*
* Check for common starting point and different types.
*/
if (overlap->lf_type == lock->lf_type) {
if (!needtolink)
TAILQ_REMOVE(&lock->lf_state->ls_locks,
lock, lf_entry);
lf_free(lock);
lock = overlap; /* for debug output below */
break;
}
if (overlap->lf_start == lock->lf_start) {
if (!needtolink)
TAILQ_REMOVE(&lock->lf_state->ls_locks,
lock, lf_entry);
TAILQ_INSERT_BEFORE(overlap, lock, lf_entry);
overlap->lf_start = lock->lf_end + 1;
} else
lf_split(overlap, lock);
lf_wakelock(overlap, 0);
break;
case 3: /* lock contains overlap */
/*
* If downgrading lock, others may be able to
* acquire it, otherwise take the list.
*/
if (lock->lf_type == F_RDLCK &&
overlap->lf_type == F_WRLCK) {
lf_wakelock(overlap, 0);
} else {
while ((ltmp =
TAILQ_FIRST(&overlap->lf_blkhd))) {
TAILQ_REMOVE(&overlap->lf_blkhd, ltmp,
lf_block);
ltmp->lf_blk = lock;
TAILQ_INSERT_TAIL(&lock->lf_blkhd,
ltmp, lf_block);
}
}
/*
* Add the new lock if necessary and delete the overlap.
*/
if (needtolink) {
TAILQ_INSERT_BEFORE(overlap, lock, lf_entry);
needtolink = 0;
}
TAILQ_REMOVE(&lock->lf_state->ls_locks, overlap, lf_entry);
lf_free(overlap);
continue;
case 4: /* overlap starts before lock */
/*
* Add lock after overlap on the list.
*/
if (!needtolink)
TAILQ_REMOVE(&lock->lf_state->ls_locks, lock,
lf_entry);
TAILQ_INSERT_AFTER(&lock->lf_state->ls_locks, overlap,
lock, lf_entry);
overlap->lf_end = lock->lf_start - 1;
lf_wakelock(overlap, 0);
needtolink = 0;
continue;
case 5: /* overlap ends after lock */
/*
* Add the new lock before overlap.
*/
if (needtolink)
TAILQ_INSERT_BEFORE(overlap, lock, lf_entry);
overlap->lf_start = lock->lf_end + 1;
lf_wakelock(overlap, 0);
break;
}
break;
}
LFPRINT(("lf_setlock: got the lock", lock), DEBUG_SETLOCK);
return (0);
}
/*
* Remove a byte-range lock on an inode.
*
* Generally, find the lock (or an overlap to that lock)
* and remove it (or shrink it), then wakeup anyone we can.
*/
int
lf_clearlock(struct lockf *lock)
{
struct lockf *lf, *overlap;
int ovcase;
rw_assert_wrlock(&lockf_lock);
lf = TAILQ_FIRST(&lock->lf_state->ls_locks);
if (lf == NULL)
return (0);
LFPRINT(("lf_clearlock", lock), DEBUG_CLEARLOCK);
while ((ovcase = lf_findoverlap(lf, lock, SELF, &overlap))) {
lf_wakelock(overlap, 0);
switch (ovcase) {
case 1: /* overlap == lock */
TAILQ_REMOVE(&lock->lf_state->ls_locks, overlap,
lf_entry);
lf_free(overlap);
break;
case 2: /* overlap contains lock: split it */
if (overlap->lf_start == lock->lf_start) {
overlap->lf_start = lock->lf_end + 1;
break;
}
lf_split(overlap, lock);
/*
* The lock is now part of the list, lf_clearlock() must
* ensure that the lock remains detached from the list.
*/
TAILQ_REMOVE(&lock->lf_state->ls_locks, lock, lf_entry);
break;
case 3: /* lock contains overlap */
lf = TAILQ_NEXT(overlap, lf_entry);
TAILQ_REMOVE(&lock->lf_state->ls_locks, overlap,
lf_entry);
lf_free(overlap);
continue;
case 4: /* overlap starts before lock */
overlap->lf_end = lock->lf_start - 1;
lf = TAILQ_NEXT(overlap, lf_entry);
continue;
case 5: /* overlap ends after lock */
overlap->lf_start = lock->lf_end + 1;
break;
}
break;
}
return (0);
}
/*
* Check whether there is a blocking lock,
* and if so return its process identifier.
*/
int
lf_getlock(struct lockf *lock, struct flock *fl)
{
struct lockf *block, *lf;
rw_assert_wrlock(&lockf_lock);
LFPRINT(("lf_getlock", lock), DEBUG_CLEARLOCK);
lf = TAILQ_FIRST(&lock->lf_state->ls_locks);
if ((block = lf_getblock(lf, lock)) != NULL) {
fl->l_type = block->lf_type;
fl->l_whence = SEEK_SET;
fl->l_start = block->lf_start;
if (block->lf_end == -1)
fl->l_len = 0;
else
fl->l_len = block->lf_end - block->lf_start + 1;
fl->l_pid = block->lf_pid;
} else {
fl->l_type = F_UNLCK;
}
return (0);
}
/*
* Walk the list of locks for an inode and
* return the first blocking lock.
*/
struct lockf *
lf_getblock(struct lockf *lf, struct lockf *lock)
{
struct lockf *overlap;
rw_assert_wrlock(&lockf_lock);
while (lf_findoverlap(lf, lock, OTHERS, &overlap) != 0) {
/*
* We've found an overlap, see if it blocks us
*/
if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK))
return (overlap);
/*
* Nope, point to the next one on the list and
* see if it blocks us
*/
lf = TAILQ_NEXT(overlap, lf_entry);
}
return (NULL);
}
/*
* Walk the list of locks for an inode to
* find an overlapping lock (if any).
*
* NOTE: this returns only the FIRST overlapping lock. There
* may be more than one.
*/
int
lf_findoverlap(struct lockf *lf, struct lockf *lock, int type,
struct lockf **overlap)
{
off_t start, end;
rw_assert_wrlock(&lockf_lock);
LFPRINT(("lf_findoverlap: looking for overlap in", lock), DEBUG_FINDOVR);
*overlap = lf;
start = lock->lf_start;
end = lock->lf_end;
while (lf != NULL) {
if (((type & SELF) && lf->lf_id != lock->lf_id) ||
((type & OTHERS) && lf->lf_id == lock->lf_id)) {
*overlap = lf = TAILQ_NEXT(lf, lf_entry);
continue;
}
LFPRINT(("\tchecking", lf), DEBUG_FINDOVR);
/*
* OK, check for overlap
*
* Six cases:
* 0) no overlap
* 1) overlap == lock
* 2) overlap contains lock
* 3) lock contains overlap
* 4) overlap starts before lock
* 5) overlap ends after lock
*/
/* Case 0 */
if ((lf->lf_end != -1 && start > lf->lf_end) ||
(end != -1 && lf->lf_start > end)) {
DPRINTF(("no overlap\n"), DEBUG_FINDOVR);
if ((type & SELF) && end != -1 && lf->lf_start > end)
return (0);
*overlap = lf = TAILQ_NEXT(lf, lf_entry);
continue;
}
/* Case 1 */
if ((lf->lf_start == start) && (lf->lf_end == end)) {
DPRINTF(("overlap == lock\n"), DEBUG_FINDOVR);
return (1);
}
/* Case 2 */
if ((lf->lf_start <= start) &&
(lf->lf_end == -1 || (end != -1 && lf->lf_end >= end))) {
DPRINTF(("overlap contains lock\n"), DEBUG_FINDOVR);
return (2);
}
/* Case 3 */
if (start <= lf->lf_start &&
(end == -1 || (lf->lf_end != -1 && end >= lf->lf_end))) {
DPRINTF(("lock contains overlap\n"), DEBUG_FINDOVR);
return (3);
}
/* Case 4 */
if ((lf->lf_start < start) &&
((lf->lf_end >= start) || (lf->lf_end == -1))) {
DPRINTF(("overlap starts before lock\n"),
DEBUG_FINDOVR);
return (4);
}
/* Case 5 */
if ((lf->lf_start > start) && (end != -1) &&
((lf->lf_end > end) || (lf->lf_end == -1))) {
DPRINTF(("overlap ends after lock\n"), DEBUG_FINDOVR);
return (5);
}
panic("lf_findoverlap: default");
}
return (0);
}
/*
* Purge all locks associated with the given lock state.
*/
void
lf_purgelocks(struct lockf_state **state)
{
struct lockf_state *ls;
struct lockf *lock;
rw_enter_write(&lockf_lock);
ls = *state;
if (ls == NULL)
goto out;
ls_ref(ls);
/* Interrupt blocked locks and wait for all of them to finish. */
TAILQ_FOREACH(lock, &ls->ls_locks, lf_entry) {
LFPRINT(("lf_purgelocks: wakeup", lock), DEBUG_SETLOCK);
lf_wakelock(lock, F_INTR);
}
while (!TAILQ_EMPTY(&ls->ls_pending))
rwsleep_nsec(ls, &lockf_lock, PLOCK, "lockfp", INFSLP);
/*
* Any remaining locks cannot block other locks at this point and can
* safely be removed.
*/
while ((lock = TAILQ_FIRST(&ls->ls_locks))) {
TAILQ_REMOVE(&ls->ls_locks, lock, lf_entry);
lf_free(lock);
}
/* This is the last expected thread to hold a lock state reference. */
KASSERT(ls->ls_refs == 1);
ls_rele(ls);
out:
rw_exit_write(&lockf_lock);
}
/*
* Split a lock and a contained region into
* two or three locks as necessary.
*/
void
lf_split(struct lockf *lock1, struct lockf *lock2)
{
struct lockf *splitlock;
rw_assert_wrlock(&lockf_lock);
LFPRINT(("lf_split", lock1), DEBUG_SPLIT);
LFPRINT(("splitting from", lock2), DEBUG_SPLIT);
/*
* Check to see if splitting into only two pieces.
*/
if (lock1->lf_start == lock2->lf_start) {
lock1->lf_start = lock2->lf_end + 1;
TAILQ_INSERT_BEFORE(lock1, lock2, lf_entry);
return;
}
if (lock1->lf_end == lock2->lf_end) {
lock1->lf_end = lock2->lf_start - 1;
TAILQ_INSERT_AFTER(&lock1->lf_state->ls_locks, lock1, lock2,
lf_entry);
return;
}
/*
* Make a new lock consisting of the last part of
* the encompassing lock
*/
splitlock = lf_alloc(lock1->lf_uid, 0);
splitlock->lf_flags = lock1->lf_flags;
splitlock->lf_type = lock1->lf_type;
splitlock->lf_start = lock2->lf_end + 1;
splitlock->lf_end = lock1->lf_end;
splitlock->lf_id = lock1->lf_id;
splitlock->lf_state = lock1->lf_state;
splitlock->lf_blk = NULL;
splitlock->lf_pid = lock1->lf_pid;
TAILQ_INIT(&splitlock->lf_blkhd);
ls_ref(splitlock->lf_state);
lock1->lf_end = lock2->lf_start - 1;
TAILQ_INSERT_AFTER(&lock1->lf_state->ls_locks, lock1, lock2, lf_entry);
TAILQ_INSERT_AFTER(&lock1->lf_state->ls_locks, lock2, splitlock,
lf_entry);
}
/*
* Wakeup a blocklist
*/
void
lf_wakelock(struct lockf *lock, int flags)
{
struct lockf *wakelock;
rw_assert_wrlock(&lockf_lock);
while ((wakelock = TAILQ_FIRST(&lock->lf_blkhd))) {
TAILQ_REMOVE(&lock->lf_blkhd, wakelock, lf_block);
wakelock->lf_blk = NULL;
wakelock->lf_flags |= flags;
wakeup_one(wakelock);
}
}
/*
* Returns non-zero if the given lock would cause a deadlock.
*/
int
lf_deadlock(struct lockf *lock)
{
struct lockf *block, *lf, *pending;
lf = TAILQ_FIRST(&lock->lf_state->ls_locks);
for (; (block = lf_getblock(lf, lock)) != NULL;
lf = TAILQ_NEXT(block, lf_entry)) {
if ((block->lf_flags & F_POSIX) == 0)
continue;
TAILQ_FOREACH(pending, &lock->lf_state->ls_pending, lf_entry) {
if (pending->lf_blk == NULL)
continue; /* lock already unblocked */
if (pending->lf_pid == block->lf_pid &&
pending->lf_blk->lf_pid == lock->lf_pid)
return (1);
}
}
return (0);
}
#ifdef LOCKF_DEBUG
/*
* Print out a lock.
*/
void
lf_print(const char *tag, struct lockf *lock)
{
struct lockf *block;
if (tag)
printf("%s: ", tag);
printf("lock %p", lock);
if (lock == NULL) {
printf("\n");
return;
}
printf(", %s %p %s, start %lld, end %lld",
lock->lf_flags & F_POSIX ? "posix" : "flock",
lock->lf_id,
lock->lf_type == F_RDLCK ? "shared" :
lock->lf_type == F_WRLCK ? "exclusive" :
lock->lf_type == F_UNLCK ? "unlock" :
"unknown", lock->lf_start, lock->lf_end);
printf(", next %p, state %p",
TAILQ_NEXT(lock, lf_entry), lock->lf_state);
block = TAILQ_FIRST(&lock->lf_blkhd);
if (block)
printf(", block");
TAILQ_FOREACH(block, &lock->lf_blkhd, lf_block)
printf(" %p,", block);
printf("\n");
}
void
lf_printlist(const char *tag, struct lockf *lock)
{
struct lockf *lf;
printf("%s: Lock list:\n", tag);
TAILQ_FOREACH(lf, &lock->lf_state->ls_locks, lf_entry) {
if (lock == lf)
printf(" * ");
else
printf(" ");
lf_print(NULL, lf);
}
}
#endif /* LOCKF_DEBUG */
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/* $OpenBSD: sys_futex.c,v 1.21 2022/08/14 01:58:28 jsg Exp $ */
/*
* Copyright (c) 2016-2017 Martin Pieuchot
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/pool.h>
#include <sys/time.h>
#include <sys/rwlock.h>
#include <sys/futex.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <uvm/uvm.h>
/*
* Atomicity is only needed on MULTIPROCESSOR kernels. Fall back on
* copyin(9) until non-MULTIPROCESSOR architectures have a copyin32(9)
* implementation.
*/
#ifndef MULTIPROCESSOR
#define copyin32(uaddr, kaddr) copyin((uaddr), (kaddr), sizeof(uint32_t))
#endif
/*
* Kernel representation of a futex.
*/
struct futex {
LIST_ENTRY(futex) ft_list; /* list of all futexes */
TAILQ_HEAD(, proc) ft_threads; /* sleeping queue */
struct uvm_object *ft_obj; /* UVM object */
struct vm_amap *ft_amap; /* UVM amap */
voff_t ft_off; /* UVM offset */
unsigned int ft_refcnt; /* # of references */
};
/* Syscall helpers. */
int futex_wait(uint32_t *, uint32_t, const struct timespec *, int);
int futex_wake(uint32_t *, uint32_t, int);
int futex_requeue(uint32_t *, uint32_t, uint32_t *, uint32_t, int);
/* Flags for futex_get(). */
#define FT_CREATE 0x1 /* Create a futex if it doesn't exist. */
#define FT_PRIVATE 0x2 /* Futex is process-private. */
struct futex *futex_get(uint32_t *, int);
void futex_put(struct futex *);
/*
* The global futex lock serializes futex(2) calls so that no wakeup
* event is lost, and protects all futex lists and futex states.
*/
struct rwlock ftlock = RWLOCK_INITIALIZER("futex");
static struct futex_list ftlist_shared =
LIST_HEAD_INITIALIZER(ftlist_shared);
struct pool ftpool;
void
futex_init(void)
{
pool_init(&ftpool, sizeof(struct futex), 0, IPL_NONE,
PR_WAITOK | PR_RWLOCK, "futexpl", NULL);
}
int
sys_futex(struct proc *p, void *v, register_t *retval)
{
struct sys_futex_args /* {
syscallarg(uint32_t *) f;
syscallarg(int) op;
syscallarg(inr) val;
syscallarg(const struct timespec *) timeout;
syscallarg(uint32_t *) g;
} */ *uap = v;
uint32_t *uaddr = SCARG(uap, f);
int op = SCARG(uap, op);
uint32_t val = SCARG(uap, val);
const struct timespec *timeout = SCARG(uap, timeout);
void *g = SCARG(uap, g);
int flags = 0;
int error = 0;
if (op & FUTEX_PRIVATE_FLAG)
flags |= FT_PRIVATE;
rw_enter_write(&ftlock);
switch (op) {
case FUTEX_WAIT:
case FUTEX_WAIT_PRIVATE:
error = futex_wait(uaddr, val, timeout, flags);
break;
case FUTEX_WAKE:
case FUTEX_WAKE_PRIVATE:
*retval = futex_wake(uaddr, val, flags);
break;
case FUTEX_REQUEUE:
case FUTEX_REQUEUE_PRIVATE:
*retval = futex_requeue(uaddr, val, g, (u_long)timeout, flags);
break;
default:
error = ENOSYS;
break;
}
rw_exit_write(&ftlock);
return error;
}
/*
* Return an existing futex matching userspace address ``uaddr''.
*
* If such futex does not exist and FT_CREATE is given, create it.
*/
struct futex *
futex_get(uint32_t *uaddr, int flags)
{
struct proc *p = curproc;
vm_map_t map = &p->p_vmspace->vm_map;
vm_map_entry_t entry;
struct uvm_object *obj = NULL;
struct vm_amap *amap = NULL;
voff_t off = (vaddr_t)uaddr;
struct futex *f;
struct futex_list *ftlist = &p->p_p->ps_ftlist;
rw_assert_wrlock(&ftlock);
if (!(flags & FT_PRIVATE)) {
vm_map_lock_read(map);
if (uvm_map_lookup_entry(map, (vaddr_t)uaddr, &entry) &&
entry->inheritance == MAP_INHERIT_SHARE) {
if (UVM_ET_ISOBJ(entry)) {
ftlist = &ftlist_shared;
obj = entry->object.uvm_obj;
off = entry->offset +
((vaddr_t)uaddr - entry->start);
} else if (entry->aref.ar_amap) {
ftlist = &ftlist_shared;
amap = entry->aref.ar_amap;
off = ptoa(entry->aref.ar_pageoff) +
((vaddr_t)uaddr - entry->start);
}
}
vm_map_unlock_read(map);
}
LIST_FOREACH(f, ftlist, ft_list) {
if (f->ft_obj == obj && f->ft_amap == amap &&
f->ft_off == off) {
f->ft_refcnt++;
break;
}
}
if ((f == NULL) && (flags & FT_CREATE)) {
/*
* We rely on the rwlock to ensure that no other thread
* create the same futex.
*/
f = pool_get(&ftpool, PR_WAITOK);
TAILQ_INIT(&f->ft_threads);
f->ft_obj = obj;
f->ft_amap = amap;
f->ft_off = off;
f->ft_refcnt = 1;
LIST_INSERT_HEAD(ftlist, f, ft_list);
}
return f;
}
/*
* Release a given futex.
*/
void
futex_put(struct futex *f)
{
rw_assert_wrlock(&ftlock);
KASSERT(f->ft_refcnt > 0);
--f->ft_refcnt;
if (f->ft_refcnt == 0) {
KASSERT(TAILQ_EMPTY(&f->ft_threads));
LIST_REMOVE(f, ft_list);
pool_put(&ftpool, f);
}
}
/*
* Put the current thread on the sleep queue of the futex at address
* ``uaddr''. Let it sleep for the specified ``timeout'' time, or
* indefinitely if the argument is NULL.
*/
int
futex_wait(uint32_t *uaddr, uint32_t val, const struct timespec *timeout,
int flags)
{
struct proc *p = curproc;
struct futex *f;
uint64_t nsecs = INFSLP;
uint32_t cval;
int error;
/*
* After reading the value a race is still possible but
* we deal with it by serializing all futex syscalls.
*/
rw_assert_wrlock(&ftlock);
/*
* Read user space futex value
*/
if ((error = copyin32(uaddr, &cval)))
return error;
/* If the value changed, stop here. */
if (cval != val)
return EAGAIN;
if (timeout != NULL) {
struct timespec ts;
if ((error = copyin(timeout, &ts, sizeof(ts))))
return error;
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrreltimespec(p, &ts);
#endif
if (ts.tv_sec < 0 || !timespecisvalid(&ts))
return EINVAL;
nsecs = MAX(1, MIN(TIMESPEC_TO_NSEC(&ts), MAXTSLP));
}
f = futex_get(uaddr, flags | FT_CREATE);
TAILQ_INSERT_TAIL(&f->ft_threads, p, p_fut_link);
p->p_futex = f;
error = rwsleep_nsec(p, &ftlock, PWAIT|PCATCH, "fsleep", nsecs);
if (error == ERESTART)
error = ECANCELED;
else if (error == EWOULDBLOCK) {
/* A race occurred between a wakeup and a timeout. */
if (p->p_futex == NULL)
error = 0;
else
error = ETIMEDOUT;
}
/* Remove ourself if we haven't been awaken. */
if ((f = p->p_futex) != NULL) {
p->p_futex = NULL;
TAILQ_REMOVE(&f->ft_threads, p, p_fut_link);
futex_put(f);
}
return error;
}
/*
* Wakeup at most ``n'' sibling threads sleeping on a futex at address
* ``uaddr'' and requeue at most ``m'' sibling threads on a futex at
* address ``uaddr2''.
*/
int
futex_requeue(uint32_t *uaddr, uint32_t n, uint32_t *uaddr2, uint32_t m,
int flags)
{
struct futex *f, *g;
struct proc *p;
uint32_t count = 0;
rw_assert_wrlock(&ftlock);
f = futex_get(uaddr, flags);
if (f == NULL)
return 0;
while ((p = TAILQ_FIRST(&f->ft_threads)) != NULL && (count < (n + m))) {
p->p_futex = NULL;
TAILQ_REMOVE(&f->ft_threads, p, p_fut_link);
futex_put(f);
if (count < n) {
wakeup_one(p);
} else if (uaddr2 != NULL) {
g = futex_get(uaddr2, FT_CREATE);
TAILQ_INSERT_TAIL(&g->ft_threads, p, p_fut_link);
p->p_futex = g;
}
count++;
}
futex_put(f);
return count;
}
/*
* Wakeup at most ``n'' sibling threads sleeping on a futex at address
* ``uaddr''.
*/
int
futex_wake(uint32_t *uaddr, uint32_t n, int flags)
{
return futex_requeue(uaddr, n, NULL, 0, flags);
}
7
6
3
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
/* $OpenBSD: time.h,v 1.62 2022/07/23 22:58:51 cheloha Exp $ */
/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)time.h 8.2 (Berkeley) 7/10/94
*/
#ifndef _SYS_TIME_H_
#define _SYS_TIME_H_
#include <sys/select.h>
#ifndef _TIMEVAL_DECLARED
#define _TIMEVAL_DECLARED
/*
* Structure returned by gettimeofday(2) system call,
* and used in other calls.
*/
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* and microseconds */
};
#endif
#ifndef _TIMESPEC_DECLARED
#define _TIMESPEC_DECLARED
/*
* Structure defined by POSIX.1b to be like a timeval.
*/
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* and nanoseconds */
};
#endif
#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
(ts)->tv_sec = (tv)->tv_sec; \
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
} while (0)
#define TIMESPEC_TO_TIMEVAL(tv, ts) do { \
(tv)->tv_sec = (ts)->tv_sec; \
(tv)->tv_usec = (ts)->tv_nsec / 1000; \
} while (0)
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
#define DST_NONE 0 /* not on dst */
#define DST_USA 1 /* USA style dst */
#define DST_AUST 2 /* Australian style dst */
#define DST_WET 3 /* Western European dst */
#define DST_MET 4 /* Middle European dst */
#define DST_EET 5 /* Eastern European dst */
#define DST_CAN 6 /* Canada */
/* Operations on timevals. */
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timerisvalid(tvp) \
((tvp)->tv_usec >= 0 && (tvp)->tv_usec < 1000000)
#define timercmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
/* Operations on timespecs. */
#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0
#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
#define timespecisvalid(tsp) \
((tsp)->tv_nsec >= 0 && (tsp)->tv_nsec < 1000000000L)
#define timespeccmp(tsp, usp, cmp) \
(((tsp)->tv_sec == (usp)->tv_sec) ? \
((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
((tsp)->tv_sec cmp (usp)->tv_sec))
#define timespecadd(tsp, usp, vsp) \
do { \
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
if ((vsp)->tv_nsec >= 1000000000L) { \
(vsp)->tv_sec++; \
(vsp)->tv_nsec -= 1000000000L; \
} \
} while (0)
#define timespecsub(tsp, usp, vsp) \
do { \
(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
if ((vsp)->tv_nsec < 0) { \
(vsp)->tv_sec--; \
(vsp)->tv_nsec += 1000000000L; \
} \
} while (0)
/*
* Names of the interval timers, and structure
* defining a timer setting.
*/
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
#if __BSD_VISIBLE
/*
* clock information structure for sysctl({CTL_KERN, KERN_CLOCKRATE})
*/
struct clockinfo {
int hz; /* clock frequency */
int tick; /* micro-seconds per hz tick */
int stathz; /* statistics clock frequency */
int profhz; /* profiling clock frequency */
};
#endif /* __BSD_VISIBLE */
#if defined(_KERNEL) || defined(_STANDALONE) || defined (_LIBC)
#include <sys/_time.h>
/* Time expressed as seconds and fractions of a second + operations on it. */
struct bintime {
time_t sec;
uint64_t frac;
};
#endif
#if defined(_KERNEL) || defined(_STANDALONE) || defined (_LIBC)
#define bintimecmp(btp, ctp, cmp) \
((btp)->sec == (ctp)->sec ? \
(btp)->frac cmp (ctp)->frac : \
(btp)->sec cmp (ctp)->sec)
static inline void
bintimeaddfrac(const struct bintime *bt, uint64_t x, struct bintime *ct)
{
ct->sec = bt->sec;
if (bt->frac > bt->frac + x)
ct->sec++;
ct->frac = bt->frac + x;
}
static inline void
bintimeadd(const struct bintime *bt, const struct bintime *ct,
struct bintime *dt)
{
dt->sec = bt->sec + ct->sec;
if (bt->frac > bt->frac + ct->frac)
dt->sec++;
dt->frac = bt->frac + ct->frac;
}
static inline void
bintimesub(const struct bintime *bt, const struct bintime *ct,
struct bintime *dt)
{
dt->sec = bt->sec - ct->sec;
if (bt->frac < bt->frac - ct->frac)
dt->sec--;
dt->frac = bt->frac - ct->frac;
}
static inline void
TIMECOUNT_TO_BINTIME(u_int count, uint64_t scale, struct bintime *bt)
{
uint64_t hi64;
hi64 = count * (scale >> 32);
bt->sec = hi64 >> 32;
bt->frac = hi64 << 32;
bintimeaddfrac(bt, count * (scale & 0xffffffff), bt);
}
/*-
* Background information:
*
* When converting between timestamps on parallel timescales of differing
* resolutions it is historical and scientific practice to round down rather
* than doing 4/5 rounding.
*
* The date changes at midnight, not at noon.
*
* Even at 15:59:59.999999999 it's not four'o'clock.
*
* time_second ticks after N.999999999 not after N.4999999999
*/
static inline uint32_t
FRAC_TO_NSEC(uint64_t frac)
{
return ((frac >> 32) * 1000000000ULL) >> 32;
}
static inline void
BINTIME_TO_TIMESPEC(const struct bintime *bt, struct timespec *ts)
{
ts->tv_sec = bt->sec;
ts->tv_nsec = FRAC_TO_NSEC(bt->frac);
}
static inline void
TIMESPEC_TO_BINTIME(const struct timespec *ts, struct bintime *bt)
{
bt->sec = ts->tv_sec;
/* 18446744073 = int(2^64 / 1000000000) */
bt->frac = (uint64_t)ts->tv_nsec * (uint64_t)18446744073ULL;
}
static inline void
BINTIME_TO_TIMEVAL(const struct bintime *bt, struct timeval *tv)
{
tv->tv_sec = bt->sec;
tv->tv_usec = (long)(((uint64_t)1000000 * (uint32_t)(bt->frac >> 32)) >> 32);
}
static inline void
TIMEVAL_TO_BINTIME(const struct timeval *tv, struct bintime *bt)
{
bt->sec = (time_t)tv->tv_sec;
/* 18446744073709 = int(2^64 / 1000000) */
bt->frac = (uint64_t)tv->tv_usec * (uint64_t)18446744073709ULL;
}
#endif
#if defined(_KERNEL) || defined(_STANDALONE)
/*
* Functions for looking at our clocks: [get]{bin,nano,micro}[boot|up]time()
*
* Functions without the "get" prefix returns the best timestamp
* we can produce in the given format.
*
* "bin" == struct bintime == seconds + 64 bit fraction of seconds.
* "nano" == struct timespec == seconds + nanoseconds.
* "micro" == struct timeval == seconds + microseconds.
*
* Functions containing "up" returns time relative to boot and
* should be used for calculating time intervals.
*
* Functions containing "boot" return the GMT time at which the
* system booted.
*
* Functions with just "time" return the current GMT time.
*
* Functions with the "get" prefix returns a less precise result
* much faster than the functions without "get" prefix and should
* be used where a precision of 10 msec is acceptable or where
* performance is priority. (NB: "precision", _not_ "resolution" !)
*/
void bintime(struct bintime *);
void nanotime(struct timespec *);
void microtime(struct timeval *);
void getnanotime(struct timespec *);
void getmicrotime(struct timeval *);
void binuptime(struct bintime *);
void nanouptime(struct timespec *);
void microuptime(struct timeval *);
void getbinuptime(struct bintime *);
void getnanouptime(struct timespec *);
void getmicrouptime(struct timeval *);
void binboottime(struct bintime *);
void microboottime(struct timeval *);
void nanoboottime(struct timespec *);
void binruntime(struct bintime *);
void nanoruntime(struct timespec *);
time_t gettime(void);
time_t getuptime(void);
uint64_t nsecuptime(void);
uint64_t getnsecuptime(void);
struct proc;
int clock_gettime(struct proc *, clockid_t, struct timespec *);
void cancel_all_itimers(void);
int itimerdecr(struct itimerspec *, long);
int settime(const struct timespec *);
int ratecheck(struct timeval *, const struct timeval *);
int ppsratecheck(struct timeval *, int *, int);
/*
* "POSIX time" to/from "YY/MM/DD/hh/mm/ss"
*/
struct clock_ymdhms {
u_short dt_year;
u_char dt_mon;
u_char dt_day;
u_char dt_wday; /* Day of week */
u_char dt_hour;
u_char dt_min;
u_char dt_sec;
};
time_t clock_ymdhms_to_secs(struct clock_ymdhms *);
void clock_secs_to_ymdhms(time_t, struct clock_ymdhms *);
/*
* BCD to decimal and decimal to BCD.
*/
#define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf))
#define TOBCD(x) (((x) / 10 * 16) + ((x) % 10))
/* Some handy constants. */
#define SECDAY 86400L
#define SECYR (SECDAY * 365)
/* Traditional POSIX base year */
#define POSIX_BASE_YEAR 1970
#include <sys/stdint.h>
static inline void
USEC_TO_TIMEVAL(uint64_t us, struct timeval *tv)
{
tv->tv_sec = us / 1000000;
tv->tv_usec = us % 1000000;
}
static inline void
NSEC_TO_TIMEVAL(uint64_t ns, struct timeval *tv)
{
tv->tv_sec = ns / 1000000000L;
tv->tv_usec = (ns % 1000000000L) / 1000;
}
static inline uint64_t
TIMEVAL_TO_NSEC(const struct timeval *tv)
{
uint64_t nsecs;
if (tv->tv_sec > UINT64_MAX / 1000000000ULL)
return UINT64_MAX;
nsecs = tv->tv_sec * 1000000000ULL;
if (tv->tv_usec * 1000ULL > UINT64_MAX - nsecs)
return UINT64_MAX;
return nsecs + tv->tv_usec * 1000ULL;
}
static inline void
NSEC_TO_TIMESPEC(uint64_t ns, struct timespec *ts)
{
ts->tv_sec = ns / 1000000000L;
ts->tv_nsec = ns % 1000000000L;
}
static inline uint64_t
SEC_TO_NSEC(uint64_t seconds)
{
if (seconds > UINT64_MAX / 1000000000ULL)
return UINT64_MAX;
return seconds * 1000000000ULL;
}
static inline uint64_t
MSEC_TO_NSEC(uint64_t milliseconds)
{
if (milliseconds > UINT64_MAX / 1000000ULL)
return UINT64_MAX;
return milliseconds * 1000000ULL;
}
static inline uint64_t
USEC_TO_NSEC(uint64_t microseconds)
{
if (microseconds > UINT64_MAX / 1000ULL)
return UINT64_MAX;
return microseconds * 1000ULL;
}
static inline uint64_t
TIMESPEC_TO_NSEC(const struct timespec *ts)
{
if (ts->tv_sec > (UINT64_MAX - ts->tv_nsec) / 1000000000ULL)
return UINT64_MAX;
return ts->tv_sec * 1000000000ULL + ts->tv_nsec;
}
static inline uint64_t
BINTIME_TO_NSEC(const struct bintime *bt)
{
return bt->sec * 1000000000ULL + FRAC_TO_NSEC(bt->frac);
}
#else /* !_KERNEL */
#include <time.h>
#if __BSD_VISIBLE || __XPG_VISIBLE
__BEGIN_DECLS
#if __BSD_VISIBLE
int adjtime(const struct timeval *, struct timeval *);
int adjfreq(const int64_t *, int64_t *);
#endif
#if __XPG_VISIBLE
int futimes(int, const struct timeval *);
int getitimer(int, struct itimerval *);
int gettimeofday(struct timeval *, struct timezone *);
int setitimer(int, const struct itimerval *, struct itimerval *);
int settimeofday(const struct timeval *, const struct timezone *);
int utimes(const char *, const struct timeval *);
#endif /* __XPG_VISIBLE */
__END_DECLS
#endif /* __BSD_VISIBLE || __XPG_VISIBLE */
#endif /* !_KERNEL */
#endif /* !_SYS_TIME_H_ */
26
1
5
5
1
14
4
2
1
1
6
2
1
3
4
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
/* $OpenBSD: ipsec_input.c,v 1.203 2022/02/22 01:35:40 guenther Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* This code was written by John Ioannidis for BSD/OS in Athens, Greece,
* in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/protosw.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/netisr.h>
#include <net/bpf.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif /* INET6 */
#include <netinet/ip_ipsp.h>
#include <netinet/ip_esp.h>
#include <netinet/ip_ah.h>
#include <netinet/ip_ipcomp.h>
#include <net/if_enc.h>
#include <crypto/cryptodev.h>
#include <crypto/xform.h>
#include "bpfilter.h"
void ipsec_common_ctlinput(u_int, int, struct sockaddr *, void *, int);
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
/* sysctl variables */
int encdebug = 0;
int ipsec_keep_invalid = IPSEC_DEFAULT_EMBRYONIC_SA_TIMEOUT;
int ipsec_require_pfs = IPSEC_DEFAULT_PFS;
int ipsec_soft_allocations = IPSEC_DEFAULT_SOFT_ALLOCATIONS;
int ipsec_exp_allocations = IPSEC_DEFAULT_EXP_ALLOCATIONS;
int ipsec_soft_bytes = IPSEC_DEFAULT_SOFT_BYTES;
int ipsec_exp_bytes = IPSEC_DEFAULT_EXP_BYTES;
int ipsec_soft_timeout = IPSEC_DEFAULT_SOFT_TIMEOUT;
int ipsec_exp_timeout = IPSEC_DEFAULT_EXP_TIMEOUT;
int ipsec_soft_first_use = IPSEC_DEFAULT_SOFT_FIRST_USE;
int ipsec_exp_first_use = IPSEC_DEFAULT_EXP_FIRST_USE;
int ipsec_expire_acquire = IPSEC_DEFAULT_EXPIRE_ACQUIRE;
int esp_enable = 1;
int ah_enable = 1;
int ipcomp_enable = 0;
const struct sysctl_bounded_args espctl_vars[] = {
{ESPCTL_ENABLE, &esp_enable, 0, 1},
{ESPCTL_UDPENCAP_ENABLE, &udpencap_enable, 0, 1},
{ESPCTL_UDPENCAP_PORT, &udpencap_port, 0, 65535},
};
const struct sysctl_bounded_args ahctl_vars[] = {
{AHCTL_ENABLE, &ah_enable, 0, 1},
};
const struct sysctl_bounded_args ipcompctl_vars[] = {
{IPCOMPCTL_ENABLE, &ipcomp_enable, 0, 1},
};
struct cpumem *espcounters;
struct cpumem *ahcounters;
struct cpumem *ipcompcounters;
struct cpumem *ipseccounters;
char ipsec_def_enc[20];
char ipsec_def_auth[20];
char ipsec_def_comp[20];
const struct sysctl_bounded_args ipsecctl_vars[] = {
{ IPSEC_ENCDEBUG, &encdebug, 0, 1 },
{ IPSEC_EXPIRE_ACQUIRE, &ipsec_expire_acquire, 0, INT_MAX },
{ IPSEC_EMBRYONIC_SA_TIMEOUT, &ipsec_keep_invalid, 0, INT_MAX },
{ IPSEC_REQUIRE_PFS, &ipsec_require_pfs, 0, 1 },
{ IPSEC_SOFT_ALLOCATIONS, &ipsec_soft_allocations, 0, INT_MAX },
{ IPSEC_ALLOCATIONS, &ipsec_exp_allocations, 0, INT_MAX },
{ IPSEC_SOFT_BYTES, &ipsec_soft_bytes, 0, INT_MAX },
{ IPSEC_BYTES, &ipsec_exp_bytes, 0, INT_MAX },
{ IPSEC_TIMEOUT, &ipsec_exp_timeout, 0, INT_MAX },
{ IPSEC_SOFT_TIMEOUT, &ipsec_soft_timeout,0, INT_MAX },
{ IPSEC_SOFT_FIRSTUSE, &ipsec_soft_first_use, 0, INT_MAX },
{ IPSEC_FIRSTUSE, &ipsec_exp_first_use, 0, INT_MAX },
};
int esp_sysctl_espstat(void *, size_t *, void *);
int ah_sysctl_ahstat(void *, size_t *, void *);
int ipcomp_sysctl_ipcompstat(void *, size_t *, void *);
int ipsec_sysctl_ipsecstat(void *, size_t *, void *);
void
ipsec_init(void)
{
espcounters = counters_alloc(esps_ncounters);
ahcounters = counters_alloc(ahs_ncounters);
ipcompcounters = counters_alloc(ipcomps_ncounters);
ipseccounters = counters_alloc(ipsec_ncounters);
strlcpy(ipsec_def_enc, IPSEC_DEFAULT_DEF_ENC, sizeof(ipsec_def_enc));
strlcpy(ipsec_def_auth, IPSEC_DEFAULT_DEF_AUTH, sizeof(ipsec_def_auth));
strlcpy(ipsec_def_comp, IPSEC_DEFAULT_DEF_COMP, sizeof(ipsec_def_comp));
ipsp_init();
}
/*
* ipsec_common_input() gets called when we receive an IPsec-protected packet
* in IPv4 or IPv6. All it does is find the right TDB and call the appropriate
* transform. The callback takes care of further processing (like ingress
* filtering).
*/
int
ipsec_common_input(struct mbuf **mp, int skip, int protoff, int af, int sproto,
int udpencap)
{
#define IPSEC_ISTAT(x,y,z) do { \
if (sproto == IPPROTO_ESP) \
espstat_inc(x); \
else if (sproto == IPPROTO_AH) \
ahstat_inc(y); \
else \
ipcompstat_inc(z); \
} while (0)
struct mbuf *m = *mp;
union sockaddr_union dst_address;
struct tdb *tdbp = NULL;
u_int32_t spi;
u_int16_t cpi;
int prot;
#ifdef ENCDEBUG
char buf[INET6_ADDRSTRLEN];
#endif
NET_ASSERT_LOCKED();
ipsecstat_pkt(ipsec_ipackets, ipsec_ibytes, m->m_pkthdr.len);
IPSEC_ISTAT(esps_input, ahs_input, ipcomps_input);
if ((sproto == IPPROTO_IPCOMP) && (m->m_flags & M_COMP)) {
DPRINTF("repeated decompression");
ipcompstat_inc(ipcomps_pdrops);
goto drop;
}
if (m->m_pkthdr.len - skip < 2 * sizeof(u_int32_t)) {
DPRINTF("packet too small");
IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
goto drop;
}
/* Retrieve the SPI from the relevant IPsec header */
switch (sproto) {
case IPPROTO_ESP:
m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
break;
case IPPROTO_AH:
m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
(caddr_t) &spi);
break;
case IPPROTO_IPCOMP:
m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
(caddr_t) &cpi);
spi = ntohl(htons(cpi));
break;
default:
panic("%s: unknown/unsupported security protocol %d",
__func__, sproto);
}
/*
* Find tunnel control block and (indirectly) call the appropriate
* kernel crypto routine. The resulting mbuf chain is a valid
* IP packet ready to go through input processing.
*/
memset(&dst_address, 0, sizeof(dst_address));
dst_address.sa.sa_family = af;
switch (af) {
case AF_INET:
dst_address.sin.sin_len = sizeof(struct sockaddr_in);
m_copydata(m, offsetof(struct ip, ip_dst),
sizeof(struct in_addr),
(caddr_t) &(dst_address.sin.sin_addr));
break;
#ifdef INET6
case AF_INET6:
dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
sizeof(struct in6_addr),
(caddr_t) &(dst_address.sin6.sin6_addr));
in6_recoverscope(&dst_address.sin6,
&dst_address.sin6.sin6_addr);
break;
#endif /* INET6 */
default:
DPRINTF("unsupported protocol family %d", af);
IPSEC_ISTAT(esps_nopf, ahs_nopf, ipcomps_nopf);
goto drop;
}
tdbp = gettdb(rtable_l2(m->m_pkthdr.ph_rtableid),
spi, &dst_address, sproto);
if (tdbp == NULL) {
DPRINTF("could not find SA for packet to %s, spi %08x",
ipsp_address(&dst_address, buf, sizeof(buf)), ntohl(spi));
IPSEC_ISTAT(esps_notdb, ahs_notdb, ipcomps_notdb);
goto drop;
}
if (tdbp->tdb_flags & TDBF_INVALID) {
DPRINTF("attempted to use invalid SA %s/%08x/%u",
ipsp_address(&dst_address, buf, sizeof(buf)),
ntohl(spi), tdbp->tdb_sproto);
IPSEC_ISTAT(esps_invalid, ahs_invalid, ipcomps_invalid);
goto drop;
}
if (udpencap && !(tdbp->tdb_flags & TDBF_UDPENCAP)) {
DPRINTF("attempted to use non-udpencap SA %s/%08x/%u",
ipsp_address(&dst_address, buf, sizeof(buf)),
ntohl(spi), tdbp->tdb_sproto);
espstat_inc(esps_udpinval);
goto drop;
}
if (!udpencap && (tdbp->tdb_flags & TDBF_UDPENCAP)) {
DPRINTF("attempted to use udpencap SA %s/%08x/%u",
ipsp_address(&dst_address, buf, sizeof(buf)),
ntohl(spi), tdbp->tdb_sproto);
espstat_inc(esps_udpneeded);
goto drop;
}
if (tdbp->tdb_xform == NULL) {
DPRINTF("attempted to use uninitialized SA %s/%08x/%u",
ipsp_address(&dst_address, buf, sizeof(buf)),
ntohl(spi), tdbp->tdb_sproto);
IPSEC_ISTAT(esps_noxform, ahs_noxform, ipcomps_noxform);
goto drop;
}
KERNEL_LOCK();
/* Register first use, setup expiration timer. */
if (tdbp->tdb_first_use == 0) {
tdbp->tdb_first_use = gettime();
if (tdbp->tdb_flags & TDBF_FIRSTUSE) {
if (timeout_add_sec(&tdbp->tdb_first_tmo,
tdbp->tdb_exp_first_use))
tdb_ref(tdbp);
}
if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE) {
if (timeout_add_sec(&tdbp->tdb_sfirst_tmo,
tdbp->tdb_soft_first_use))
tdb_ref(tdbp);
}
}
tdbstat_pkt(tdbp, tdb_ipackets, tdb_ibytes, m->m_pkthdr.len);
/*
* Call appropriate transform and return -- callback takes care of
* everything else.
*/
prot = (*(tdbp->tdb_xform->xf_input))(mp, tdbp, skip, protoff);
if (prot == IPPROTO_DONE) {
ipsecstat_inc(ipsec_idrops);
tdbstat_inc(tdbp, tdb_idrops);
}
tdb_unref(tdbp);
KERNEL_UNLOCK();
return prot;
drop:
m_freemp(mp);
ipsecstat_inc(ipsec_idrops);
if (tdbp != NULL)
tdbstat_inc(tdbp, tdb_idrops);
tdb_unref(tdbp);
return IPPROTO_DONE;
}
/*
* IPsec input callback, called by the transform callback. Takes care of
* filtering and other sanity checks on the processed packet.
*/
int
ipsec_common_input_cb(struct mbuf **mp, struct tdb *tdbp, int skip, int protoff)
{
struct mbuf *m = *mp;
int af, sproto;
u_int8_t prot;
#if NBPFILTER > 0
struct ifnet *encif;
#endif
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
struct m_tag *mtag;
struct tdb_ident *tdbi;
#ifdef ENCDEBUG
char buf[INET6_ADDRSTRLEN];
#endif
af = tdbp->tdb_dst.sa.sa_family;
sproto = tdbp->tdb_sproto;
tdbp->tdb_last_used = gettime();
/* Fix IPv4 header */
if (af == AF_INET) {
if (m->m_len < skip &&
(m = *mp = m_pullup(m, skip)) == NULL) {
DPRINTF("processing failed for SA %s/%08x",
ipsp_address(&tdbp->tdb_dst, buf, sizeof(buf)),
ntohl(tdbp->tdb_spi));
IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
goto baddone;
}
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
prot = ip->ip_p;
}
#ifdef INET6
/* Fix IPv6 header */
if (af == AF_INET6) {
if (m->m_len < sizeof(struct ip6_hdr) &&
(m = *mp = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
DPRINTF("processing failed for SA %s/%08x",
ipsp_address(&tdbp->tdb_dst, buf, sizeof(buf)),
ntohl(tdbp->tdb_spi));
IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
goto baddone;
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len - skip);
/* Save protocol */
m_copydata(m, protoff, 1, (caddr_t) &prot);
}
#endif /* INET6 */
/*
* Fix TCP/UDP checksum of UDP encapsulated transport mode ESP packet.
* (RFC3948 3.1.2)
*/
if ((af == AF_INET || af == AF_INET6) &&
(tdbp->tdb_flags & TDBF_UDPENCAP) &&
(tdbp->tdb_flags & TDBF_TUNNELING) == 0) {
u_int16_t cksum;
switch (prot) {
case IPPROTO_UDP:
if (m->m_pkthdr.len < skip + sizeof(struct udphdr)) {
IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
ipcomps_hdrops);
goto baddone;
}
cksum = 0;
m_copyback(m, skip + offsetof(struct udphdr, uh_sum),
sizeof(cksum), &cksum, M_NOWAIT);
#ifdef INET6
if (af == AF_INET6) {
cksum = in6_cksum(m, IPPROTO_UDP, skip,
m->m_pkthdr.len - skip);
m_copyback(m, skip + offsetof(struct udphdr,
uh_sum), sizeof(cksum), &cksum, M_NOWAIT);
}
#endif
break;
case IPPROTO_TCP:
if (m->m_pkthdr.len < skip + sizeof(struct tcphdr)) {
IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
ipcomps_hdrops);
goto baddone;
}
cksum = 0;
m_copyback(m, skip + offsetof(struct tcphdr, th_sum),
sizeof(cksum), &cksum, M_NOWAIT);
if (af == AF_INET)
cksum = in4_cksum(m, IPPROTO_TCP, skip,
m->m_pkthdr.len - skip);
#ifdef INET6
else if (af == AF_INET6)
cksum = in6_cksum(m, IPPROTO_TCP, skip,
m->m_pkthdr.len - skip);
#endif
m_copyback(m, skip + offsetof(struct tcphdr, th_sum),
sizeof(cksum), &cksum, M_NOWAIT);
break;
}
}
/*
* Record what we've done to the packet (under what SA it was
* processed).
*/
if (tdbp->tdb_sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
if (mtag == NULL) {
DPRINTF("failed to get tag");
IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
goto baddone;
}
tdbi = (struct tdb_ident *)(mtag + 1);
tdbi->dst = tdbp->tdb_dst;
tdbi->proto = tdbp->tdb_sproto;
tdbi->spi = tdbp->tdb_spi;
tdbi->rdomain = tdbp->tdb_rdomain;
m_tag_prepend(m, mtag);
}
switch (sproto) {
case IPPROTO_ESP:
/* Packet is confidential ? */
if (tdbp->tdb_encalgxform)
m->m_flags |= M_CONF;
/* Check if we had authenticated ESP. */
if (tdbp->tdb_authalgxform)
m->m_flags |= M_AUTH;
break;
case IPPROTO_AH:
m->m_flags |= M_AUTH;
break;
case IPPROTO_IPCOMP:
m->m_flags |= M_COMP;
break;
default:
panic("%s: unknown/unsupported security protocol %d",
__func__, sproto);
}
#if NPF > 0
/* Add pf tag if requested. */
pf_tag_packet(m, tdbp->tdb_tag, -1);
pf_pkt_addr_changed(m);
#endif
if (tdbp->tdb_rdomain != tdbp->tdb_rdomain_post)
m->m_pkthdr.ph_rtableid = tdbp->tdb_rdomain_post;
if (tdbp->tdb_flags & TDBF_TUNNELING)
m->m_flags |= M_TUNNEL;
ipsecstat_add(ipsec_idecompbytes, m->m_pkthdr.len);
tdbstat_add(tdbp, tdb_idecompbytes, m->m_pkthdr.len);
#if NBPFILTER > 0
encif = enc_getif(tdbp->tdb_rdomain_post, tdbp->tdb_tap);
if (encif != NULL) {
encif->if_ipackets++;
encif->if_ibytes += m->m_pkthdr.len;
if (sproto != IPPROTO_IPCOMP) {
/* XXX This conflicts with the scoped nature of IPv6 */
m->m_pkthdr.ph_ifidx = encif->if_index;
}
if (encif->if_bpf) {
struct enchdr hdr;
hdr.af = af;
hdr.spi = tdbp->tdb_spi;
hdr.flags = m->m_flags & (M_AUTH|M_CONF);
bpf_mtap_hdr(encif->if_bpf, (char *)&hdr,
ENC_HDRLEN, m, BPF_DIRECTION_IN);
}
}
#endif
#if NPF > 0
/*
* The ip_deliver() shortcut avoids running through ip_input() with the
* same IP header twice. Packets in transport mode have to be be
* passed to pf explicitly. In tunnel mode the inner IP header will
* run through ip_input() and pf anyway.
*/
if ((tdbp->tdb_flags & TDBF_TUNNELING) == 0) {
struct ifnet *ifp;
/* This is the enc0 interface unless for ipcomp. */
if ((ifp = if_get(m->m_pkthdr.ph_ifidx)) == NULL) {
goto baddone;
}
if (pf_test(af, PF_IN, ifp, mp) != PF_PASS) {
if_put(ifp);
goto baddone;
}
m = *mp;
if_put(ifp);
if (m == NULL)
return IPPROTO_DONE;
}
#endif
/* Return to the appropriate protocol handler in deliver loop. */
return prot;
baddone:
m_freemp(mp);
return IPPROTO_DONE;
#undef IPSEC_ISTAT
}
int
ipsec_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
switch (name[0]) {
case IPCTL_IPSEC_ENC_ALGORITHM:
NET_LOCK();
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
ipsec_def_enc, sizeof(ipsec_def_enc));
NET_UNLOCK();
return (error);
case IPCTL_IPSEC_AUTH_ALGORITHM:
NET_LOCK();
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
ipsec_def_auth, sizeof(ipsec_def_auth));
NET_UNLOCK();
return (error);
case IPCTL_IPSEC_IPCOMP_ALGORITHM:
NET_LOCK();
error = sysctl_tstring(oldp, oldlenp, newp, newlen,
ipsec_def_comp, sizeof(ipsec_def_comp));
NET_UNLOCK();
return (error);
case IPCTL_IPSEC_STATS:
return (ipsec_sysctl_ipsecstat(oldp, oldlenp, newp));
default:
NET_LOCK();
error = sysctl_bounded_arr(ipsecctl_vars, nitems(ipsecctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
}
int
esp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case ESPCTL_STATS:
return (esp_sysctl_espstat(oldp, oldlenp, newp));
default:
NET_LOCK();
error = sysctl_bounded_arr(espctl_vars, nitems(espctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
}
int
esp_sysctl_espstat(void *oldp, size_t *oldlenp, void *newp)
{
struct espstat espstat;
CTASSERT(sizeof(espstat) == (esps_ncounters * sizeof(uint64_t)));
memset(&espstat, 0, sizeof espstat);
counters_read(espcounters, (uint64_t *)&espstat, esps_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp, &espstat,
sizeof(espstat)));
}
int
ah_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case AHCTL_STATS:
return ah_sysctl_ahstat(oldp, oldlenp, newp);
default:
NET_LOCK();
error = sysctl_bounded_arr(ahctl_vars, nitems(ahctl_vars), name,
namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
}
int
ah_sysctl_ahstat(void *oldp, size_t *oldlenp, void *newp)
{
struct ahstat ahstat;
CTASSERT(sizeof(ahstat) == (ahs_ncounters * sizeof(uint64_t)));
memset(&ahstat, 0, sizeof ahstat);
counters_read(ahcounters, (uint64_t *)&ahstat, ahs_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp, &ahstat, sizeof(ahstat)));
}
int
ipcomp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case IPCOMPCTL_STATS:
return ipcomp_sysctl_ipcompstat(oldp, oldlenp, newp);
default:
NET_LOCK();
error = sysctl_bounded_arr(ipcompctl_vars,
nitems(ipcompctl_vars), name, namelen, oldp, oldlenp,
newp, newlen);
NET_UNLOCK();
return (error);
}
}
int
ipcomp_sysctl_ipcompstat(void *oldp, size_t *oldlenp, void *newp)
{
struct ipcompstat ipcompstat;
CTASSERT(sizeof(ipcompstat) == (ipcomps_ncounters * sizeof(uint64_t)));
memset(&ipcompstat, 0, sizeof ipcompstat);
counters_read(ipcompcounters, (uint64_t *)&ipcompstat,
ipcomps_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp, &ipcompstat,
sizeof(ipcompstat)));
}
int
ipsec_sysctl_ipsecstat(void *oldp, size_t *oldlenp, void *newp)
{
struct ipsecstat ipsecstat;
CTASSERT(sizeof(ipsecstat) == (ipsec_ncounters * sizeof(uint64_t)));
memset(&ipsecstat, 0, sizeof ipsecstat);
counters_read(ipseccounters, (uint64_t *)&ipsecstat, ipsec_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp, &ipsecstat,
sizeof(ipsecstat)));
}
int
ipsec_input_disabled(struct mbuf **mp, int *offp, int proto, int af)
{
switch (af) {
case AF_INET:
return rip_input(mp, offp, proto, af);
#ifdef INET6
case AF_INET6:
return rip6_input(mp, offp, proto, af);
#endif
default:
unhandled_af(af);
}
}
int
ah46_input(struct mbuf **mp, int *offp, int proto, int af)
{
int protoff;
if (
#if NPF > 0
((*mp)->m_pkthdr.pf.flags & PF_TAG_DIVERTED) ||
#endif
!ah_enable)
return ipsec_input_disabled(mp, offp, proto, af);
protoff = ipsec_protoff(*mp, *offp, af);
if (protoff < 0) {
DPRINTF("bad packet header chain");
ahstat_inc(ahs_hdrops);
m_freemp(mp);
return IPPROTO_DONE;
}
return ipsec_common_input(mp, *offp, protoff, af, proto, 0);
}
void
ah4_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
{
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in))
return;
ipsec_common_ctlinput(rdomain, cmd, sa, v, IPPROTO_AH);
}
int
esp46_input(struct mbuf **mp, int *offp, int proto, int af)
{
int protoff;
if (
#if NPF > 0
((*mp)->m_pkthdr.pf.flags & PF_TAG_DIVERTED) ||
#endif
!esp_enable)
return ipsec_input_disabled(mp, offp, proto, af);
protoff = ipsec_protoff(*mp, *offp, af);
if (protoff < 0) {
DPRINTF("bad packet header chain");
espstat_inc(esps_hdrops);
m_freemp(mp);
return IPPROTO_DONE;
}
return ipsec_common_input(mp, *offp, protoff, af, proto, 0);
}
/* IPv4 IPCOMP wrapper */
int
ipcomp46_input(struct mbuf **mp, int *offp, int proto, int af)
{
int protoff;
if (
#if NPF > 0
((*mp)->m_pkthdr.pf.flags & PF_TAG_DIVERTED) ||
#endif
!ipcomp_enable)
return ipsec_input_disabled(mp, offp, proto, af);
protoff = ipsec_protoff(*mp, *offp, af);
if (protoff < 0) {
DPRINTF("bad packet header chain");
ipcompstat_inc(ipcomps_hdrops);
m_freemp(mp);
return IPPROTO_DONE;
}
return ipsec_common_input(mp, *offp, protoff, af, proto, 0);
}
void
ipsec_set_mtu(struct tdb *tdbp, u_int32_t mtu)
{
ssize_t adjust;
NET_ASSERT_LOCKED();
/* Walk the chain backwards to the first tdb */
for (; tdbp != NULL; tdbp = tdbp->tdb_inext) {
if (tdbp->tdb_flags & TDBF_INVALID ||
(adjust = ipsec_hdrsz(tdbp)) == -1)
return;
mtu -= adjust;
/* Store adjusted MTU in tdb */
tdbp->tdb_mtu = mtu;
tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
DPRINTF("spi %08x mtu %d adjust %ld",
ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust);
}
}
void
ipsec_common_ctlinput(u_int rdomain, int cmd, struct sockaddr *sa,
void *v, int proto)
{
struct ip *ip = v;
if (cmd == PRC_MSGSIZE && ip && ip_mtudisc && ip->ip_v == 4) {
struct tdb *tdbp;
struct sockaddr_in dst;
struct icmp *icp;
int hlen = ip->ip_hl << 2;
u_int32_t spi, mtu;
/* Find the right MTU. */
icp = (struct icmp *)((caddr_t) ip -
offsetof(struct icmp, icmp_ip));
mtu = ntohs(icp->icmp_nextmtu);
/*
* Ignore the packet, if we do not receive a MTU
* or the MTU is too small to be acceptable.
*/
if (mtu < 296)
return;
memset(&dst, 0, sizeof(struct sockaddr_in));
dst.sin_family = AF_INET;
dst.sin_len = sizeof(struct sockaddr_in);
dst.sin_addr.s_addr = ip->ip_dst.s_addr;
memcpy(&spi, (caddr_t)ip + hlen, sizeof(u_int32_t));
tdbp = gettdb_rev(rdomain, spi, (union sockaddr_union *)&dst,
proto);
ipsec_set_mtu(tdbp, mtu);
tdb_unref(tdbp);
}
}
void
udpencap_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
{
struct ip *ip = v;
struct tdb *tdbp, *first;
struct icmp *icp;
u_int32_t mtu;
struct sockaddr_in dst, src;
union sockaddr_union *su_dst, *su_src;
NET_ASSERT_LOCKED();
icp = (struct icmp *)((caddr_t) ip - offsetof(struct icmp, icmp_ip));
mtu = ntohs(icp->icmp_nextmtu);
/*
* Ignore the packet, if we do not receive a MTU
* or the MTU is too small to be acceptable.
*/
if (mtu < 296)
return;
memset(&dst, 0, sizeof(dst));
dst.sin_family = AF_INET;
dst.sin_len = sizeof(struct sockaddr_in);
dst.sin_addr.s_addr = ip->ip_dst.s_addr;
su_dst = (union sockaddr_union *)&dst;
memset(&src, 0, sizeof(src));
src.sin_family = AF_INET;
src.sin_len = sizeof(struct sockaddr_in);
src.sin_addr.s_addr = ip->ip_src.s_addr;
su_src = (union sockaddr_union *)&src;
first = gettdbbysrcdst_rev(rdomain, 0, su_src, su_dst, IPPROTO_ESP);
mtx_enter(&tdb_sadb_mtx);
for (tdbp = first; tdbp != NULL; tdbp = tdbp->tdb_snext) {
if (tdbp->tdb_sproto == IPPROTO_ESP &&
((tdbp->tdb_flags & (TDBF_INVALID|TDBF_UDPENCAP)) ==
TDBF_UDPENCAP) &&
!memcmp(&tdbp->tdb_dst, &dst, su_dst->sa.sa_len) &&
!memcmp(&tdbp->tdb_src, &src, su_src->sa.sa_len))
ipsec_set_mtu(tdbp, mtu);
}
mtx_leave(&tdb_sadb_mtx);
tdb_unref(first);
}
void
esp4_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
{
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in))
return;
ipsec_common_ctlinput(rdomain, cmd, sa, v, IPPROTO_ESP);
}
/* Find the offset of the next protocol field in the previous header. */
int
ipsec_protoff(struct mbuf *m, int off, int af)
{
#ifdef INET6
struct ip6_ext ip6e;
int protoff, nxt, l;
#endif /* INET6 */
switch (af) {
case AF_INET:
return offsetof(struct ip, ip_p);
#ifdef INET6
case AF_INET6:
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
#ifdef INET6
if (off < sizeof(struct ip6_hdr))
return -1;
if (off == sizeof(struct ip6_hdr))
return offsetof(struct ip6_hdr, ip6_nxt);
/* Chase down the header chain... */
protoff = sizeof(struct ip6_hdr);
nxt = (mtod(m, struct ip6_hdr *))->ip6_nxt;
l = 0;
do {
protoff += l;
m_copydata(m, protoff, sizeof(ip6e),
(caddr_t) &ip6e);
if (nxt == IPPROTO_AH)
l = (ip6e.ip6e_len + 2) << 2;
else
l = (ip6e.ip6e_len + 1) << 3;
#ifdef DIAGNOSTIC
if (l <= 0)
panic("%s: l went zero or negative", __func__);
#endif
nxt = ip6e.ip6e_nxt;
} while (protoff + l < off);
/* Malformed packet check */
if (protoff + l != off)
return -1;
protoff += offsetof(struct ip6_ext, ip6e_nxt);
return protoff;
#endif /* INET6 */
}
int
ipsec_forward_check(struct mbuf *m, int hlen, int af)
{
struct tdb *tdb;
struct tdb_ident *tdbi;
struct m_tag *mtag;
int error = 0;
/*
* IPsec policy check for forwarded packets. Look at
* inner-most IPsec SA used.
*/
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
if (mtag != NULL) {
tdbi = (struct tdb_ident *)(mtag + 1);
tdb = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst, tdbi->proto);
} else
tdb = NULL;
error = ipsp_spd_lookup(m, af, hlen, IPSP_DIRECTION_IN,
tdb, NULL, NULL, NULL);
tdb_unref(tdb);
return error;
}
int
ipsec_local_check(struct mbuf *m, int hlen, int proto, int af)
{
struct tdb *tdb;
struct tdb_ident *tdbi;
struct m_tag *mtag;
int error = 0;
/*
* If it's a protected packet for us, skip the policy check.
* That's because we really only care about the properties of
* the protected packet, and not the intermediate versions.
* While this is not the most paranoid setting, it allows
* some flexibility in handling nested tunnels (in setting up
* the policies).
*/
if ((proto == IPPROTO_ESP) || (proto == IPPROTO_AH) ||
(proto == IPPROTO_IPCOMP))
return 0;
/*
* If the protected packet was tunneled, then we need to
* verify the protected packet's information, not the
* external headers. Thus, skip the policy lookup for the
* external packet, and keep the IPsec information linked on
* the packet header (the encapsulation routines know how
* to deal with that).
*/
if ((proto == IPPROTO_IPV4) || (proto == IPPROTO_IPV6))
return 0;
/*
* When processing IPv6 header chains, do not look at the
* outer header. The inner protocol is relevant and will
* be checked by the local delivery loop later.
*/
if ((af == AF_INET6) && ((proto == IPPROTO_DSTOPTS) ||
(proto == IPPROTO_ROUTING) || (proto == IPPROTO_FRAGMENT)))
return 0;
/*
* If the protected packet is TCP or UDP, we'll do the
* policy check in the respective input routine, so we can
* check for bypass sockets.
*/
if ((proto == IPPROTO_TCP) || (proto == IPPROTO_UDP))
return 0;
/*
* IPsec policy check for local-delivery packets. Look at the
* inner-most SA that protected the packet. This is in fact
* a bit too restrictive (it could end up causing packets to
* be dropped that semantically follow the policy, e.g., in
* certain SA-bundle configurations); but the alternative is
* very complicated (and requires keeping track of what
* kinds of tunneling headers have been seen in-between the
* IPsec headers), and I don't think we lose much functionality
* that's needed in the real world (who uses bundles anyway ?).
*/
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
if (mtag) {
tdbi = (struct tdb_ident *)(mtag + 1);
tdb = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
tdbi->proto);
} else
tdb = NULL;
error = ipsp_spd_lookup(m, af, hlen, IPSP_DIRECTION_IN,
tdb, NULL, NULL, NULL);
tdb_unref(tdb);
return error;
}
26
26
26
1
12
12
14
1
1
815
815
815
1258
1272
1
1
211
209
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/* $OpenBSD: uipc_mbuf2.c,v 1.45 2020/12/12 11:48:54 jan Exp $ */
/* $KAME: uipc_mbuf2.c,v 1.29 2001/02/14 13:42:10 itojun Exp $ */
/* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $ */
/*
* Copyright (C) 1999 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_mbuf.c 8.4 (Berkeley) 2/14/95
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/mbuf.h>
extern struct pool mtagpool;
/* can't call it m_dup(), as freebsd[34] uses m_dup() with different arg */
static struct mbuf *m_dup1(struct mbuf *, int, int, int);
/*
* ensure that [off, off + len] is contiguous on the mbuf chain "m".
* packet chain before "off" is kept untouched.
* if offp == NULL, the target will start at <retval, 0> on resulting chain.
* if offp != NULL, the target will start at <retval, *offp> on resulting chain.
*
* on error return (NULL return value), original "m" will be freed.
*
* XXX m_trailingspace/m_leadingspace on shared cluster (sharedcluster)
*/
struct mbuf *
m_pulldown(struct mbuf *m, int off, int len, int *offp)
{
struct mbuf *n, *o;
int hlen, tlen, olen;
int sharedcluster;
/* check invalid arguments. */
if (m == NULL)
panic("m == NULL in m_pulldown()");
if ((n = m_getptr(m, off, &off)) == NULL) {
m_freem(m);
return (NULL); /* mbuf chain too short */
}
sharedcluster = M_READONLY(n);
/*
* the target data is on <n, off>.
* if we got enough data on the mbuf "n", we're done.
*/
if ((off == 0 || offp) && len <= n->m_len - off && !sharedcluster)
goto ok;
/*
* when len <= n->m_len - off and off != 0, it is a special case.
* len bytes from <n, off> sits in single mbuf, but the caller does
* not like the starting position (off).
* chop the current mbuf into two pieces, set off to 0.
*/
if (len <= n->m_len - off) {
struct mbuf *mlast;
o = m_dup1(n, off, n->m_len - off, M_DONTWAIT);
if (o == NULL) {
m_freem(m);
return (NULL); /* ENOBUFS */
}
for (mlast = o; mlast->m_next != NULL; mlast = mlast->m_next)
;
n->m_len = off;
mlast->m_next = n->m_next;
n->m_next = o;
n = o;
off = 0;
goto ok;
}
/*
* we need to take hlen from <n, off> and tlen from <n->m_next, 0>,
* and construct contiguous mbuf with m_len == len.
* note that hlen + tlen == len, and tlen > 0.
*/
hlen = n->m_len - off;
tlen = len - hlen;
/*
* ensure that we have enough trailing data on mbuf chain.
* if not, we can do nothing about the chain.
*/
olen = 0;
for (o = n->m_next; o != NULL; o = o->m_next)
olen += o->m_len;
if (hlen + olen < len) {
m_freem(m);
return (NULL); /* mbuf chain too short */
}
/*
* easy cases first.
* we need to use m_copydata() to get data from <n->m_next, 0>.
*/
if ((off == 0 || offp) && m_trailingspace(n) >= tlen &&
!sharedcluster) {
m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len);
n->m_len += tlen;
m_adj(n->m_next, tlen);
goto ok;
}
if ((off == 0 || offp) && m_leadingspace(n->m_next) >= hlen &&
!sharedcluster && n->m_next->m_len >= tlen) {
n->m_next->m_data -= hlen;
n->m_next->m_len += hlen;
memmove(mtod(n->m_next, caddr_t), mtod(n, caddr_t) + off, hlen);
n->m_len -= hlen;
n = n->m_next;
off = 0;
goto ok;
}
/*
* now, we need to do the hard way. don't m_copym as there's no room
* on both ends.
*/
if (len > MAXMCLBYTES) {
m_freem(m);
return (NULL);
}
MGET(o, M_DONTWAIT, m->m_type);
if (o && len > MLEN) {
MCLGETL(o, M_DONTWAIT, len);
if ((o->m_flags & M_EXT) == 0) {
m_free(o);
o = NULL;
}
}
if (!o) {
m_freem(m);
return (NULL); /* ENOBUFS */
}
/* get hlen from <n, off> into <o, 0> */
o->m_len = hlen;
memmove(mtod(o, caddr_t), mtod(n, caddr_t) + off, hlen);
n->m_len -= hlen;
/* get tlen from <n->m_next, 0> into <o, hlen> */
m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len);
o->m_len += tlen;
m_adj(n->m_next, tlen);
o->m_next = n->m_next;
n->m_next = o;
n = o;
off = 0;
ok:
if (offp)
*offp = off;
return (n);
}
static struct mbuf *
m_dup1(struct mbuf *m, int off, int len, int wait)
{
struct mbuf *n;
int l;
if (len > MAXMCLBYTES)
return (NULL);
if (off == 0 && (m->m_flags & M_PKTHDR) != 0) {
MGETHDR(n, wait, m->m_type);
if (n == NULL)
return (NULL);
if (m_dup_pkthdr(n, m, wait)) {
m_free(n);
return (NULL);
}
l = MHLEN;
} else {
MGET(n, wait, m->m_type);
l = MLEN;
}
if (n && len > l) {
MCLGETL(n, wait, len);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (!n)
return (NULL);
m_copydata(m, off, len, mtod(n, caddr_t));
n->m_len = len;
return (n);
}
/* Get a packet tag structure along with specified data following. */
struct m_tag *
m_tag_get(int type, int len, int wait)
{
struct m_tag *t;
if (len < 0)
return (NULL);
if (len > PACKET_TAG_MAXSIZE)
panic("requested tag size for pool %#x is too big", type);
t = pool_get(&mtagpool, wait == M_WAITOK ? PR_WAITOK : PR_NOWAIT);
if (t == NULL)
return (NULL);
t->m_tag_id = type;
t->m_tag_len = len;
return (t);
}
/* Prepend a packet tag. */
void
m_tag_prepend(struct mbuf *m, struct m_tag *t)
{
SLIST_INSERT_HEAD(&m->m_pkthdr.ph_tags, t, m_tag_link);
m->m_pkthdr.ph_tagsset |= t->m_tag_id;
}
/* Unlink and free a packet tag. */
void
m_tag_delete(struct mbuf *m, struct m_tag *t)
{
u_int32_t ph_tagsset = 0;
struct m_tag *p;
SLIST_REMOVE(&m->m_pkthdr.ph_tags, t, m_tag, m_tag_link);
pool_put(&mtagpool, t);
SLIST_FOREACH(p, &m->m_pkthdr.ph_tags, m_tag_link)
ph_tagsset |= p->m_tag_id;
m->m_pkthdr.ph_tagsset = ph_tagsset;
}
/* Unlink and free a packet tag chain. */
void
m_tag_delete_chain(struct mbuf *m)
{
struct m_tag *p;
while ((p = SLIST_FIRST(&m->m_pkthdr.ph_tags)) != NULL) {
SLIST_REMOVE_HEAD(&m->m_pkthdr.ph_tags, m_tag_link);
pool_put(&mtagpool, p);
}
m->m_pkthdr.ph_tagsset = 0;
}
/* Find a tag, starting from a given position. */
struct m_tag *
m_tag_find(struct mbuf *m, int type, struct m_tag *t)
{
struct m_tag *p;
if (!(m->m_pkthdr.ph_tagsset & type))
return (NULL);
if (t == NULL)
p = SLIST_FIRST(&m->m_pkthdr.ph_tags);
else
p = SLIST_NEXT(t, m_tag_link);
while (p != NULL) {
if (p->m_tag_id == type)
return (p);
p = SLIST_NEXT(p, m_tag_link);
}
return (NULL);
}
/* Copy a single tag. */
struct m_tag *
m_tag_copy(struct m_tag *t, int wait)
{
struct m_tag *p;
p = m_tag_get(t->m_tag_id, t->m_tag_len, wait);
if (p == NULL)
return (NULL);
memcpy(p + 1, t + 1, t->m_tag_len); /* Copy the data */
return (p);
}
/*
* Copy two tag chains. The destination mbuf (to) loses any attached
* tags even if the operation fails. This should not be a problem, as
* m_tag_copy_chain() is typically called with a newly-allocated
* destination mbuf.
*/
int
m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int wait)
{
struct m_tag *p, *t, *tprev = NULL;
m_tag_delete_chain(to);
SLIST_FOREACH(p, &from->m_pkthdr.ph_tags, m_tag_link) {
t = m_tag_copy(p, wait);
if (t == NULL) {
m_tag_delete_chain(to);
return (ENOBUFS);
}
if (tprev == NULL)
SLIST_INSERT_HEAD(&to->m_pkthdr.ph_tags, t, m_tag_link);
else
SLIST_INSERT_AFTER(tprev, t, m_tag_link);
tprev = t;
to->m_pkthdr.ph_tagsset |= t->m_tag_id;
}
return (0);
}
/* Initialize tags on an mbuf. */
void
m_tag_init(struct mbuf *m)
{
SLIST_INIT(&m->m_pkthdr.ph_tags);
}
/* Get first tag in chain. */
struct m_tag *
m_tag_first(struct mbuf *m)
{
return (SLIST_FIRST(&m->m_pkthdr.ph_tags));
}
/* Get next tag in chain. */
struct m_tag *
m_tag_next(struct mbuf *m, struct m_tag *t)
{
return (SLIST_NEXT(t, m_tag_link));
}
32
32
32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/* $OpenBSD: exec_subr.c,v 1.57 2019/11/29 06:34:45 deraadt Exp $ */
/* $NetBSD: exec_subr.c,v 1.9 1994/12/04 03:10:42 mycroft Exp $ */
/*
* Copyright (c) 1993, 1994 Christopher G. Demetriou
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/exec.h>
#include <sys/mman.h>
#include <sys/resourcevar.h>
#include <uvm/uvm_extern.h>
#ifdef DEBUG
/*
* new_vmcmd():
* create a new vmcmd structure and fill in its fields based
* on function call arguments. make sure objects ref'd by
* the vmcmd are 'held'.
*
* If not debugging, this is a macro, so it's expanded inline.
*/
void
new_vmcmd(struct exec_vmcmd_set *evsp,
int (*proc)(struct proc *, struct exec_vmcmd *), u_long len, u_long addr,
struct vnode *vp, u_long offset, u_int prot, int flags)
{
struct exec_vmcmd *vcp;
if (evsp->evs_used >= evsp->evs_cnt)
vmcmdset_extend(evsp);
vcp = &evsp->evs_cmds[evsp->evs_used++];
vcp->ev_proc = proc;
vcp->ev_len = len;
vcp->ev_addr = addr;
if ((vcp->ev_vp = vp) != NULL)
vref(vp);
vcp->ev_offset = offset;
vcp->ev_prot = prot;
vcp->ev_flags = flags;
}
#endif /* DEBUG */
void
vmcmdset_extend(struct exec_vmcmd_set *evsp)
{
struct exec_vmcmd *nvcp;
u_int ocnt;
#ifdef DIAGNOSTIC
if (evsp->evs_used < evsp->evs_cnt)
panic("vmcmdset_extend: not necessary");
#endif
ocnt = evsp->evs_cnt;
KASSERT(ocnt > 0);
/* figure out number of entries in new set */
evsp->evs_cnt += ocnt;
/* reallocate the command set */
nvcp = mallocarray(evsp->evs_cnt, sizeof(*nvcp), M_EXEC,
M_WAITOK);
memcpy(nvcp, evsp->evs_cmds, ocnt * sizeof(*nvcp));
if (evsp->evs_cmds != evsp->evs_start)
free(evsp->evs_cmds, M_EXEC, ocnt * sizeof(*nvcp));
evsp->evs_cmds = nvcp;
}
void
kill_vmcmds(struct exec_vmcmd_set *evsp)
{
struct exec_vmcmd *vcp;
int i;
for (i = 0; i < evsp->evs_used; i++) {
vcp = &evsp->evs_cmds[i];
if (vcp->ev_vp != NULLVP)
vrele(vcp->ev_vp);
}
/*
* Free old vmcmds and reset the array.
*/
evsp->evs_used = 0;
if (evsp->evs_cmds != evsp->evs_start)
free(evsp->evs_cmds, M_EXEC,
evsp->evs_cnt * sizeof(struct exec_vmcmd));
evsp->evs_cmds = evsp->evs_start;
evsp->evs_cnt = EXEC_DEFAULT_VMCMD_SETSIZE;
}
int
exec_process_vmcmds(struct proc *p, struct exec_package *epp)
{
struct exec_vmcmd *base_vc = NULL;
int error = 0;
int i;
for (i = 0; i < epp->ep_vmcmds.evs_used && !error; i++) {
struct exec_vmcmd *vcp;
vcp = &epp->ep_vmcmds.evs_cmds[i];
if (vcp->ev_flags & VMCMD_RELATIVE) {
#ifdef DIAGNOSTIC
if (base_vc == NULL)
panic("exec_process_vmcmds: RELATIVE no base");
#endif
vcp->ev_addr += base_vc->ev_addr;
}
error = (*vcp->ev_proc)(p, vcp);
if (vcp->ev_flags & VMCMD_BASE) {
base_vc = vcp;
}
}
kill_vmcmds(&epp->ep_vmcmds);
return (error);
}
/*
* vmcmd_map_pagedvn():
* handle vmcmd which specifies that a vnode should be mmap'd.
* appropriate for handling demand-paged text and data segments.
*/
int
vmcmd_map_pagedvn(struct proc *p, struct exec_vmcmd *cmd)
{
/*
* note that if you're going to map part of a process as being
* paged from a vnode, that vnode had damn well better be marked as
* VTEXT. that's handled in the routine which sets up the vmcmd to
* call this routine.
*/
struct uvm_object *uobj;
unsigned int syscalls = 0;
int error;
/*
* map the vnode in using uvm_map.
*/
if (cmd->ev_len == 0)
return (0);
if (cmd->ev_offset & PAGE_MASK)
return (EINVAL);
if (cmd->ev_addr & PAGE_MASK)
return (EINVAL);
if (cmd->ev_len & PAGE_MASK)
return (EINVAL);
/*
* first, attach to the object
*/
uobj = uvn_attach(cmd->ev_vp, PROT_READ | PROT_EXEC);
if (uobj == NULL)
return (ENOMEM);
/*
* do the map
*/
if ((cmd->ev_flags & VMCMD_SYSCALL) && (cmd->ev_prot & PROT_EXEC))
syscalls |= UVM_FLAG_SYSCALL;
error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr, cmd->ev_len,
uobj, cmd->ev_offset, 0,
UVM_MAPFLAG(cmd->ev_prot, PROT_MASK, MAP_INHERIT_COPY,
MADV_NORMAL, UVM_FLAG_COPYONW | UVM_FLAG_FIXED | syscalls));
/*
* check for error
*/
if (error) {
/*
* error: detach from object
*/
uobj->pgops->pgo_detach(uobj);
}
return (error);
}
/*
* vmcmd_map_readvn():
* handle vmcmd which specifies that a vnode should be read from.
* appropriate for non-demand-paged text/data segments, i.e. impure
* objects (a la OMAGIC and NMAGIC).
*/
int
vmcmd_map_readvn(struct proc *p, struct exec_vmcmd *cmd)
{
int error;
vm_prot_t prot;
if (cmd->ev_len == 0)
return (0);
prot = cmd->ev_prot;
cmd->ev_addr = trunc_page(cmd->ev_addr); /* required by uvm_map */
error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr,
round_page(cmd->ev_len), NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(prot | PROT_WRITE, PROT_MASK, MAP_INHERIT_COPY,
MADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW));
if (error)
return (error);
error = vn_rdwr(UIO_READ, cmd->ev_vp, (caddr_t)cmd->ev_addr,
cmd->ev_len, cmd->ev_offset, UIO_USERSPACE, IO_UNIT,
p->p_ucred, NULL, p);
if (error)
return (error);
if ((prot & PROT_WRITE) == 0) {
/*
* we had to map in the area at PROT_WRITE so that vn_rdwr()
* could write to it. however, the caller seems to want
* it mapped read-only, so now we are going to have to call
* uvm_map_protect() to fix up the protection. ICK.
*/
return (uvm_map_protect(&p->p_vmspace->vm_map,
trunc_page(cmd->ev_addr),
round_page(cmd->ev_addr + cmd->ev_len),
prot, FALSE));
}
return (0);
}
/*
* vmcmd_map_zero():
* handle vmcmd which specifies a zero-filled address space region.
*/
int
vmcmd_map_zero(struct proc *p, struct exec_vmcmd *cmd)
{
if (cmd->ev_len == 0)
return (0);
cmd->ev_addr = trunc_page(cmd->ev_addr); /* required by uvm_map */
return (uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr,
round_page(cmd->ev_len), NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(cmd->ev_prot, PROT_MASK, MAP_INHERIT_COPY,
MADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_COPYONW |
(cmd->ev_flags & VMCMD_STACK ? UVM_FLAG_STACK : 0))));
}
/*
* vmcmd_randomize():
* handle vmcmd which specifies a randomized address space region.
*/
#define RANDOMIZE_CTX_THRESHOLD 512
int
vmcmd_randomize(struct proc *p, struct exec_vmcmd *cmd)
{
int error;
struct arc4random_ctx *ctx;
char *buf;
size_t sublen, off = 0;
size_t len = cmd->ev_len;
if (len == 0)
return (0);
if (len > ELF_RANDOMIZE_LIMIT)
return (EINVAL);
buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
if (len < RANDOMIZE_CTX_THRESHOLD) {
arc4random_buf(buf, len);
error = copyout(buf, (void *)cmd->ev_addr, len);
explicit_bzero(buf, len);
} else {
ctx = arc4random_ctx_new();
do {
sublen = MIN(len, PAGE_SIZE);
arc4random_ctx_buf(ctx, buf, sublen);
error = copyout(buf, (void *)cmd->ev_addr + off, sublen);
if (error)
break;
off += sublen;
len -= sublen;
sched_pause(yield);
} while (len);
arc4random_ctx_free(ctx);
explicit_bzero(buf, PAGE_SIZE);
}
free(buf, M_TEMP, PAGE_SIZE);
return (error);
}
#ifndef MAXSSIZ_GUARD
#define MAXSSIZ_GUARD (1024 * 1024)
#endif
/*
* exec_setup_stack(): Set up the stack segment for an executable.
*
* Note that the ep_ssize parameter must be set to be the current stack
* limit; this is adjusted in the body of execve() to yield the
* appropriate stack segment usage once the argument length is
* calculated.
*
* This function returns an int for uniformity with other (future) formats'
* stack setup functions. They might have errors to return.
*/
int
exec_setup_stack(struct proc *p, struct exec_package *epp)
{
vaddr_t sgap;
#ifdef MACHINE_STACK_GROWS_UP
epp->ep_maxsaddr = USRSTACK;
epp->ep_minsaddr = USRSTACK + MAXSSIZ;
#else
epp->ep_maxsaddr = USRSTACK - MAXSSIZ - MAXSSIZ_GUARD;
epp->ep_minsaddr = USRSTACK;
#endif
epp->ep_ssize = round_page(lim_cur(RLIMIT_STACK));
if (stackgap_random != 0) {
sgap = arc4random() & (stackgap_random - 1);
sgap = trunc_page(sgap);
#ifdef MACHINE_STACK_GROWS_UP
epp->ep_maxsaddr += sgap;
epp->ep_minsaddr += sgap;
#else
epp->ep_maxsaddr -= sgap;
epp->ep_minsaddr -= sgap;
#endif
}
/*
* set up commands for stack. note that this takes *two*, one to
* map the part of the stack which we can access, and one to map
* the part which we can't.
*
* arguably, it could be made into one, but that would require the
* addition of another mapping proc, which is unnecessary
*
* note that in memory, things assumed to be: 0 ....... ep_maxsaddr
* <stack> ep_minsaddr
*/
#ifdef MACHINE_STACK_GROWS_UP
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero,
((epp->ep_minsaddr - epp->ep_ssize) - epp->ep_maxsaddr),
epp->ep_maxsaddr + epp->ep_ssize, NULLVP, 0,
PROT_NONE);
NEW_VMCMD2(&epp->ep_vmcmds, vmcmd_map_zero, epp->ep_ssize,
epp->ep_maxsaddr, NULLVP, 0,
PROT_READ | PROT_WRITE, VMCMD_STACK);
#else
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero,
((epp->ep_minsaddr - epp->ep_ssize) - epp->ep_maxsaddr),
epp->ep_maxsaddr, NULLVP, 0,
PROT_NONE);
NEW_VMCMD2(&epp->ep_vmcmds, vmcmd_map_zero, epp->ep_ssize,
(epp->ep_minsaddr - epp->ep_ssize), NULLVP, 0,
PROT_READ | PROT_WRITE, VMCMD_STACK);
#endif
return (0);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/* $OpenBSD: scsi_ioctl.c,v 1.67 2020/09/22 19:32:53 krw Exp $ */
/* $NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Contributed by HD Associates (hd@world.std.com).
* Copyright (c) 1992, 1993 HD Associates
*
* Berkeley style copyright.
*/
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <sys/pool.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
#include <sys/scsiio.h>
#include <sys/ataio.h>
int scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
int scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *);
const unsigned char scsi_readsafe_cmd[256] = {
[0x00] = 1, /* TEST UNIT READY */
[0x03] = 1, /* REQUEST SENSE */
[0x08] = 1, /* READ(6) */
[0x12] = 1, /* INQUIRY */
[0x1a] = 1, /* MODE SENSE */
[0x1b] = 1, /* START STOP */
[0x23] = 1, /* READ FORMAT CAPACITIES */
[0x25] = 1, /* READ CDVD CAPACITY */
[0x28] = 1, /* READ(10) */
[0x2b] = 1, /* SEEK */
[0x2f] = 1, /* VERIFY(10) */
[0x3c] = 1, /* READ BUFFER */
[0x3e] = 1, /* READ LONG */
[0x42] = 1, /* READ SUBCHANNEL */
[0x43] = 1, /* READ TOC PMA ATIP */
[0x44] = 1, /* READ HEADER */
[0x45] = 1, /* PLAY AUDIO(10) */
[0x46] = 1, /* GET CONFIGURATION */
[0x47] = 1, /* PLAY AUDIO MSF */
[0x48] = 1, /* PLAY AUDIO TI */
[0x4a] = 1, /* GET EVENT STATUS NOTIFICATION */
[0x4b] = 1, /* PAUSE RESUME */
[0x4e] = 1, /* STOP PLAY SCAN */
[0x51] = 1, /* READ DISC INFO */
[0x52] = 1, /* READ TRACK RZONE INFO */
[0x5a] = 1, /* MODE SENSE(10) */
[0x88] = 1, /* READ(16) */
[0x8f] = 1, /* VERIFY(16) */
[0xa4] = 1, /* REPORT KEY */
[0xa5] = 1, /* PLAY AUDIO(12) */
[0xa8] = 1, /* READ(12) */
[0xac] = 1, /* GET PERFORMANCE */
[0xad] = 1, /* READ DVD STRUCTURE */
[0xb9] = 1, /* READ CD MSF */
[0xba] = 1, /* SCAN */
[0xbc] = 1, /* PLAY CD */
[0xbd] = 1, /* MECHANISM STATUS */
[0xbe] = 1 /* READ CD */
};
int
scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
{
struct scsi_xfer *xs;
int err = 0;
if (screq->cmdlen > sizeof(struct scsi_generic))
return EFAULT;
if (screq->datalen > MAXPHYS)
return EINVAL;
xs = scsi_xs_get(link, 0);
if (xs == NULL)
return ENOMEM;
memcpy(&xs->cmd, screq->cmd, screq->cmdlen);
xs->cmdlen = screq->cmdlen;
if (screq->datalen > 0) {
xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO);
if (xs->data == NULL) {
err = ENOMEM;
goto err;
}
xs->datalen = screq->datalen;
}
if (ISSET(screq->flags, SCCMD_READ))
SET(xs->flags, SCSI_DATA_IN);
if (ISSET(screq->flags, SCCMD_WRITE)) {
if (screq->datalen > 0) {
err = copyin(screq->databuf, xs->data, screq->datalen);
if (err != 0)
goto err;
}
SET(xs->flags, SCSI_DATA_OUT);
}
SET(xs->flags, SCSI_SILENT); /* User is responsible for errors. */
xs->timeout = screq->timeout;
xs->retries = 0; /* user must do the retries *//* ignored */
scsi_xs_sync(xs);
screq->retsts = 0;
screq->status = xs->status;
switch (xs->error) {
case XS_NOERROR:
/* probably rubbish */
screq->datalen_used = xs->datalen - xs->resid;
screq->retsts = SCCMD_OK;
break;
case XS_SENSE:
SC_DEBUG_SENSE(xs);
screq->senselen_used = min(sizeof(xs->sense),
sizeof(screq->sense));
memcpy(screq->sense, &xs->sense, screq->senselen_used);
screq->retsts = SCCMD_SENSE;
break;
case XS_SHORTSENSE:
SC_DEBUG_SENSE(xs);
printf("XS_SHORTSENSE\n");
screq->senselen_used = min(sizeof(xs->sense),
sizeof(screq->sense));
memcpy(screq->sense, &xs->sense, screq->senselen_used);
screq->retsts = SCCMD_UNKNOWN;
break;
case XS_DRIVER_STUFFUP:
screq->retsts = SCCMD_UNKNOWN;
break;
case XS_TIMEOUT:
screq->retsts = SCCMD_TIMEOUT;
break;
case XS_BUSY:
screq->retsts = SCCMD_BUSY;
break;
default:
screq->retsts = SCCMD_UNKNOWN;
break;
}
if (screq->datalen > 0 && ISSET(screq->flags, SCCMD_READ)) {
err = copyout(xs->data, screq->databuf, screq->datalen);
if (err != 0)
goto err;
}
err:
if (xs->data)
dma_free(xs->data, screq->datalen);
scsi_xs_put(xs);
return err;
}
int
scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq)
{
struct scsi_xfer *xs;
struct scsi_ata_passthru_12 *cdb;
int err = 0;
if (atareq->datalen > MAXPHYS)
return EINVAL;
xs = scsi_xs_get(link, 0);
if (xs == NULL)
return ENOMEM;
cdb = (struct scsi_ata_passthru_12 *)&xs->cmd;
cdb->opcode = ATA_PASSTHRU_12;
if (atareq->datalen > 0) {
if (ISSET(atareq->flags, ATACMD_READ)) {
cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN;
cdb->flags = ATA_PASSTHRU_T_DIR_READ;
} else {
cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT;
cdb->flags = ATA_PASSTHRU_T_DIR_WRITE;
}
SET(cdb->flags, ATA_PASSTHRU_T_LEN_SECTOR_COUNT);
} else {
cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA;
cdb->flags = ATA_PASSTHRU_T_LEN_NONE;
}
cdb->features = atareq->features;
cdb->sector_count = atareq->sec_count;
cdb->lba_low = atareq->sec_num;
cdb->lba_mid = atareq->cylinder;
cdb->lba_high = atareq->cylinder >> 8;
cdb->device = atareq->head & 0x0f;
cdb->command = atareq->command;
xs->cmdlen = sizeof(*cdb);
if (atareq->datalen > 0) {
xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO);
if (xs->data == NULL) {
err = ENOMEM;
goto err;
}
xs->datalen = atareq->datalen;
}
if (ISSET(atareq->flags, ATACMD_READ))
SET(xs->flags, SCSI_DATA_IN);
if (ISSET(atareq->flags, ATACMD_WRITE)) {
if (atareq->datalen > 0) {
err = copyin(atareq->databuf, xs->data,
atareq->datalen);
if (err != 0)
goto err;
}
SET(xs->flags, SCSI_DATA_OUT);
}
SET(xs->flags, SCSI_SILENT); /* User is responsible for errors. */
xs->retries = 0; /* user must do the retries *//* ignored */
scsi_xs_sync(xs);
atareq->retsts = ATACMD_ERROR;
switch (xs->error) {
case XS_SENSE:
case XS_SHORTSENSE:
SC_DEBUG_SENSE(xs);
/* XXX this is not right */
case XS_NOERROR:
atareq->retsts = ATACMD_OK;
break;
default:
atareq->retsts = ATACMD_ERROR;
break;
}
if (atareq->datalen > 0 && ISSET(atareq->flags, ATACMD_READ)) {
err = copyout(xs->data, atareq->databuf, atareq->datalen);
if (err != 0)
goto err;
}
err:
if (xs->data)
dma_free(xs->data, atareq->datalen);
scsi_xs_put(xs);
return err;
}
/*
* Something (e.g. another driver) has called us
* with a scsi_link for a target/lun/adapter, and a scsi
* specific ioctl to perform, better try.
*/
int
scsi_do_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
{
SC_DEBUG(link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
switch(cmd) {
case SCIOCIDENTIFY: {
struct scsi_addr *sca = (struct scsi_addr *)addr;
if (!ISSET(link->flags, (SDEV_ATAPI | SDEV_UMASS)))
/* A 'real' SCSI target. */
sca->type = TYPE_SCSI;
else
/* An 'emulated' SCSI target. */
sca->type = TYPE_ATAPI;
sca->scbus = link->bus->sc_dev.dv_unit;
sca->target = link->target;
sca->lun = link->lun;
return 0;
}
case SCIOCCOMMAND:
if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
break;
/* FALLTHROUGH */
case ATAIOCCOMMAND:
case SCIOCDEBUG:
if (!ISSET(flag, FWRITE))
return EPERM;
break;
default:
if (link->bus->sb_adapter->ioctl)
return (link->bus->sb_adapter->ioctl)(link, cmd, addr, flag);
else
return ENOTTY;
}
switch(cmd) {
case SCIOCCOMMAND:
return scsi_ioc_cmd(link, (scsireq_t *)addr);
case ATAIOCCOMMAND:
return scsi_ioc_ata_cmd(link, (atareq_t *)addr);
case SCIOCDEBUG: {
int level = *((int *)addr);
SC_DEBUG(link, SDEV_DB3, ("debug set to %d\n", level));
CLR(link->flags, SDEV_DBX); /* clear debug bits */
if (level & 1)
SET(link->flags, SDEV_DB1);
if (level & 2)
SET(link->flags, SDEV_DB2);
if (level & 4)
SET(link->flags, SDEV_DB3);
if (level & 8)
SET(link->flags, SDEV_DB4);
return 0;
}
default:
#ifdef DIAGNOSTIC
panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
#endif /* DIAGNOSTIC */
return 0;
}
}
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* $OpenBSD: kern_watchdog.c,v 1.16 2022/08/14 01:58:27 jsg Exp $ */
/*
* Copyright (c) 2003 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/sysctl.h>
void wdog_tickle(void *arg);
int (*wdog_ctl_cb)(void *, int) = NULL;
void *wdog_ctl_cb_arg = NULL;
int wdog_period = 0;
int wdog_auto = 1;
struct timeout wdog_timeout;
void
wdog_register(int (*cb)(void *, int), void *cb_arg)
{
if (wdog_ctl_cb != NULL)
return;
wdog_ctl_cb = cb;
wdog_ctl_cb_arg = cb_arg;
timeout_set(&wdog_timeout, wdog_tickle, NULL);
}
void
wdog_tickle(void *arg)
{
if (wdog_ctl_cb == NULL)
return;
(void) (*wdog_ctl_cb)(wdog_ctl_cb_arg, wdog_period);
timeout_add_msec(&wdog_timeout, wdog_period * 1000 / 2);
}
void
wdog_shutdown(void *arg)
{
if (wdog_ctl_cb == NULL || wdog_ctl_cb_arg != arg)
return;
timeout_del(&wdog_timeout);
(void) (*wdog_ctl_cb)(wdog_ctl_cb_arg, 0);
wdog_ctl_cb = NULL;
wdog_period = 0;
wdog_auto = 1;
}
int
sysctl_wdog(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error, period;
if (wdog_ctl_cb == NULL)
return (EOPNOTSUPP);
switch (name[0]) {
case KERN_WATCHDOG_PERIOD:
period = wdog_period;
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&period, 0, INT_MAX);
if (error)
return (error);
if (newp) {
timeout_del(&wdog_timeout);
wdog_period = (*wdog_ctl_cb)(wdog_ctl_cb_arg, period);
}
break;
case KERN_WATCHDOG_AUTO:
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&wdog_auto, 0, 1);
if (error)
return (error);
break;
default:
return (EINVAL);
}
if (wdog_auto && wdog_period > 0) {
(void) (*wdog_ctl_cb)(wdog_ctl_cb_arg, wdog_period);
timeout_add_msec(&wdog_timeout, wdog_period * 1000 / 2);
} else
timeout_del(&wdog_timeout);
return (error);
}
101
78
100
1
1
93
8
8
2
1
2
2
2
123
22
106
46
13
36
4
4
4
5
4
5
5
4
4
4
4
4
4
5
5
5
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
/* $OpenBSD: kern_proc.c,v 1.92 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_proc.c,v 1.14 1996/02/09 18:59:41 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_proc.c 8.4 (Berkeley) 1/4/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/wait.h>
#include <sys/rwlock.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/pool.h>
#include <sys/vnode.h>
struct rwlock uidinfolk;
#define UIHASH(uid) (&uihashtbl[(uid) & uihash])
LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
u_long uihash; /* size of hash table - 1 */
/*
* Other process lists
*/
struct tidhashhead *tidhashtbl;
u_long tidhash;
struct pidhashhead *pidhashtbl;
u_long pidhash;
struct pgrphashhead *pgrphashtbl;
u_long pgrphash;
struct processlist allprocess;
struct processlist zombprocess;
struct proclist allproc;
struct pool proc_pool;
struct pool process_pool;
struct pool rusage_pool;
struct pool ucred_pool;
struct pool pgrp_pool;
struct pool session_pool;
void pgdelete(struct pgrp *);
void fixjobc(struct process *, struct pgrp *, int);
static void orphanpg(struct pgrp *);
#ifdef DEBUG
void pgrpdump(void);
#endif
/*
* Initialize global process hashing structures.
*/
void
procinit(void)
{
LIST_INIT(&allprocess);
LIST_INIT(&zombprocess);
LIST_INIT(&allproc);
rw_init(&uidinfolk, "uidinfo");
tidhashtbl = hashinit(maxthread / 4, M_PROC, M_NOWAIT, &tidhash);
pidhashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pidhash);
pgrphashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pgrphash);
uihashtbl = hashinit(maxprocess / 16, M_PROC, M_NOWAIT, &uihash);
if (!tidhashtbl || !pidhashtbl || !pgrphashtbl || !uihashtbl)
panic("procinit: malloc");
pool_init(&proc_pool, sizeof(struct proc), 0, IPL_NONE,
PR_WAITOK, "procpl", NULL);
pool_init(&process_pool, sizeof(struct process), 0, IPL_NONE,
PR_WAITOK, "processpl", NULL);
pool_init(&rusage_pool, sizeof(struct rusage), 0, IPL_NONE,
PR_WAITOK, "zombiepl", NULL);
pool_init(&ucred_pool, sizeof(struct ucred), 0, IPL_MPFLOOR,
0, "ucredpl", NULL);
pool_init(&pgrp_pool, sizeof(struct pgrp), 0, IPL_NONE,
PR_WAITOK, "pgrppl", NULL);
pool_init(&session_pool, sizeof(struct session), 0, IPL_NONE,
PR_WAITOK, "sessionpl", NULL);
}
/*
* This returns with `uidinfolk' held: caller must call uid_release()
* after making whatever change they needed.
*/
struct uidinfo *
uid_find(uid_t uid)
{
struct uidinfo *uip, *nuip;
struct uihashhead *uipp;
uipp = UIHASH(uid);
rw_enter_write(&uidinfolk);
LIST_FOREACH(uip, uipp, ui_hash)
if (uip->ui_uid == uid)
break;
if (uip)
return (uip);
rw_exit_write(&uidinfolk);
nuip = malloc(sizeof(*nuip), M_PROC, M_WAITOK|M_ZERO);
rw_enter_write(&uidinfolk);
LIST_FOREACH(uip, uipp, ui_hash)
if (uip->ui_uid == uid)
break;
if (uip) {
free(nuip, M_PROC, sizeof(*nuip));
return (uip);
}
nuip->ui_uid = uid;
LIST_INSERT_HEAD(uipp, nuip, ui_hash);
return (nuip);
}
void
uid_release(struct uidinfo *uip)
{
rw_exit_write(&uidinfolk);
}
/*
* Change the count associated with number of threads
* a given user is using.
*/
int
chgproccnt(uid_t uid, int diff)
{
struct uidinfo *uip;
long count;
uip = uid_find(uid);
count = (uip->ui_proccnt += diff);
uid_release(uip);
if (count < 0)
panic("chgproccnt: procs < 0");
return count;
}
/*
* Is pr an inferior of parent?
*/
int
inferior(struct process *pr, struct process *parent)
{
for (; pr != parent; pr = pr->ps_pptr)
if (pr->ps_pid == 0 || pr->ps_pid == 1)
return (0);
return (1);
}
/*
* Locate a proc (thread) by number
*/
struct proc *
tfind(pid_t tid)
{
struct proc *p;
LIST_FOREACH(p, TIDHASH(tid), p_hash)
if (p->p_tid == tid)
return (p);
return (NULL);
}
/*
* Locate a process by number
*/
struct process *
prfind(pid_t pid)
{
struct process *pr;
LIST_FOREACH(pr, PIDHASH(pid), ps_hash)
if (pr->ps_pid == pid)
return (pr);
return (NULL);
}
/*
* Locate a process group by number
*/
struct pgrp *
pgfind(pid_t pgid)
{
struct pgrp *pgrp;
LIST_FOREACH(pgrp, PGRPHASH(pgid), pg_hash)
if (pgrp->pg_id == pgid)
return (pgrp);
return (NULL);
}
/*
* Locate a zombie process
*/
struct process *
zombiefind(pid_t pid)
{
struct process *pr;
LIST_FOREACH(pr, &zombprocess, ps_list)
if (pr->ps_pid == pid)
return (pr);
return (NULL);
}
/*
* Move process to a new process group. If a session is provided
* then it's a new session to contain this process group; otherwise
* the process is staying within its existing session.
*/
void
enternewpgrp(struct process *pr, struct pgrp *pgrp, struct session *newsess)
{
#ifdef DIAGNOSTIC
if (SESS_LEADER(pr))
panic("%s: session leader attempted setpgrp", __func__);
#endif
if (newsess != NULL) {
/*
* New session. Initialize it completely
*/
timeout_set(&newsess->s_verauthto, zapverauth, newsess);
newsess->s_leader = pr;
newsess->s_count = 1;
newsess->s_ttyvp = NULL;
newsess->s_ttyp = NULL;
memcpy(newsess->s_login, pr->ps_session->s_login,
sizeof(newsess->s_login));
atomic_clearbits_int(&pr->ps_flags, PS_CONTROLT);
pgrp->pg_session = newsess;
#ifdef DIAGNOSTIC
if (pr != curproc->p_p)
panic("%s: mksession but not curproc", __func__);
#endif
} else {
pgrp->pg_session = pr->ps_session;
pgrp->pg_session->s_count++;
}
pgrp->pg_id = pr->ps_pid;
LIST_INIT(&pgrp->pg_members);
LIST_INIT(&pgrp->pg_sigiolst);
LIST_INSERT_HEAD(PGRPHASH(pr->ps_pid), pgrp, pg_hash);
pgrp->pg_jobc = 0;
enterthispgrp(pr, pgrp);
}
/*
* move process to an existing process group
*/
void
enterthispgrp(struct process *pr, struct pgrp *pgrp)
{
struct pgrp *savepgrp = pr->ps_pgrp;
/*
* Adjust eligibility of affected pgrps to participate in job control.
* Increment eligibility counts before decrementing, otherwise we
* could reach 0 spuriously during the first call.
*/
fixjobc(pr, pgrp, 1);
fixjobc(pr, savepgrp, 0);
LIST_REMOVE(pr, ps_pglist);
pr->ps_pgrp = pgrp;
LIST_INSERT_HEAD(&pgrp->pg_members, pr, ps_pglist);
if (LIST_EMPTY(&savepgrp->pg_members))
pgdelete(savepgrp);
}
/*
* remove process from process group
*/
void
leavepgrp(struct process *pr)
{
if (pr->ps_session->s_verauthppid == pr->ps_pid)
zapverauth(pr->ps_session);
LIST_REMOVE(pr, ps_pglist);
if (LIST_EMPTY(&pr->ps_pgrp->pg_members))
pgdelete(pr->ps_pgrp);
pr->ps_pgrp = NULL;
}
/*
* delete a process group
*/
void
pgdelete(struct pgrp *pgrp)
{
sigio_freelist(&pgrp->pg_sigiolst);
if (pgrp->pg_session->s_ttyp != NULL &&
pgrp->pg_session->s_ttyp->t_pgrp == pgrp)
pgrp->pg_session->s_ttyp->t_pgrp = NULL;
LIST_REMOVE(pgrp, pg_hash);
SESSRELE(pgrp->pg_session);
pool_put(&pgrp_pool, pgrp);
}
void
zapverauth(void *v)
{
struct session *sess = v;
sess->s_verauthuid = 0;
sess->s_verauthppid = 0;
}
/*
* Adjust pgrp jobc counters when specified process changes process group.
* We count the number of processes in each process group that "qualify"
* the group for terminal job control (those with a parent in a different
* process group of the same session). If that count reaches zero, the
* process group becomes orphaned. Check both the specified process'
* process group and that of its children.
* entering == 0 => pr is leaving specified group.
* entering == 1 => pr is entering specified group.
* XXX need proctree lock
*/
void
fixjobc(struct process *pr, struct pgrp *pgrp, int entering)
{
struct pgrp *hispgrp;
struct session *mysession = pgrp->pg_session;
/*
* Check pr's parent to see whether pr qualifies its own process
* group; if so, adjust count for pr's process group.
*/
if ((hispgrp = pr->ps_pptr->ps_pgrp) != pgrp &&
hispgrp->pg_session == mysession) {
if (entering)
pgrp->pg_jobc++;
else if (--pgrp->pg_jobc == 0)
orphanpg(pgrp);
}
/*
* Check this process' children to see whether they qualify
* their process groups; if so, adjust counts for children's
* process groups.
*/
LIST_FOREACH(pr, &pr->ps_children, ps_sibling)
if ((hispgrp = pr->ps_pgrp) != pgrp &&
hispgrp->pg_session == mysession &&
(pr->ps_flags & PS_ZOMBIE) == 0) {
if (entering)
hispgrp->pg_jobc++;
else if (--hispgrp->pg_jobc == 0)
orphanpg(hispgrp);
}
}
void
killjobc(struct process *pr)
{
if (SESS_LEADER(pr)) {
struct session *sp = pr->ps_session;
if (sp->s_ttyvp) {
struct vnode *ovp;
/*
* Controlling process.
* Signal foreground pgrp,
* drain controlling terminal
* and revoke access to controlling terminal.
*/
if (sp->s_ttyp->t_session == sp) {
if (sp->s_ttyp->t_pgrp)
pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
ttywait(sp->s_ttyp);
/*
* The tty could have been revoked
* if we blocked.
*/
if (sp->s_ttyvp)
VOP_REVOKE(sp->s_ttyvp, REVOKEALL);
}
ovp = sp->s_ttyvp;
sp->s_ttyvp = NULL;
if (ovp)
vrele(ovp);
/*
* s_ttyp is not zero'd; we use this to
* indicate that the session once had a
* controlling terminal. (for logging and
* informational purposes)
*/
}
sp->s_leader = NULL;
}
fixjobc(pr, pr->ps_pgrp, 0);
}
/*
* A process group has become orphaned;
* if there are any stopped processes in the group,
* hang-up all process in that group.
*/
static void
orphanpg(struct pgrp *pg)
{
struct process *pr;
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
if (pr->ps_mainproc->p_stat == SSTOP) {
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
prsignal(pr, SIGHUP);
prsignal(pr, SIGCONT);
}
return;
}
}
}
#ifdef DDB
void
proc_printit(struct proc *p, const char *modif,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
static const char *const pstat[] = {
"idle", "run", "sleep", "stop", "zombie", "dead", "onproc"
};
char pstbuf[5];
const char *pst = pstbuf;
if (p->p_stat < 1 || p->p_stat > sizeof(pstat) / sizeof(pstat[0]))
snprintf(pstbuf, sizeof(pstbuf), "%d", p->p_stat);
else
pst = pstat[(int)p->p_stat - 1];
(*pr)("PROC (%s) pid=%d stat=%s\n", p->p_p->ps_comm, p->p_tid, pst);
(*pr)(" flags process=%b proc=%b\n",
p->p_p->ps_flags, PS_BITS, p->p_flag, P_BITS);
(*pr)(" pri=%u, usrpri=%u, nice=%d\n",
p->p_runpri, p->p_usrpri, p->p_p->ps_nice);
(*pr)(" forw=%p, list=%p,%p\n",
TAILQ_NEXT(p, p_runq), p->p_list.le_next, p->p_list.le_prev);
(*pr)(" process=%p user=%p, vmspace=%p\n",
p->p_p, p->p_addr, p->p_vmspace);
(*pr)(" estcpu=%u, cpticks=%d, pctcpu=%u.%u\n",
p->p_estcpu, p->p_cpticks, p->p_pctcpu / 100, p->p_pctcpu % 100);
(*pr)(" user=%u, sys=%u, intr=%u\n",
p->p_uticks, p->p_sticks, p->p_iticks);
}
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
void
db_kill_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
struct process *pr;
struct proc *p;
pr = prfind(addr);
if (pr == NULL) {
db_printf("%ld: No such process", addr);
return;
}
p = TAILQ_FIRST(&pr->ps_threads);
/* Send uncatchable SIGABRT for coredump */
sigabort(p);
}
void
db_show_all_procs(db_expr_t addr, int haddr, db_expr_t count, char *modif)
{
char *mode;
int skipzomb = 0;
int has_kernel_lock = 0;
struct proc *p;
struct process *pr, *ppr;
if (modif[0] == 0)
modif[0] = 'n'; /* default == normal mode */
mode = "mawno";
while (*mode && *mode != modif[0])
mode++;
if (*mode == 0 || *mode == 'm') {
db_printf("usage: show all procs [/a] [/n] [/w]\n");
db_printf("\t/a == show process address info\n");
db_printf("\t/n == show normal process info [default]\n");
db_printf("\t/w == show process pgrp/wait info\n");
db_printf("\t/o == show normal info for non-idle SONPROC\n");
return;
}
pr = LIST_FIRST(&allprocess);
switch (*mode) {
case 'a':
db_printf(" TID %-9s %18s %18s %18s\n",
"COMMAND", "STRUCT PROC *", "UAREA *", "VMSPACE/VM_MAP");
break;
case 'n':
db_printf(" PID %6s %5s %5s S %10s %-12s %-15s\n",
"TID", "PPID", "UID", "FLAGS", "WAIT", "COMMAND");
break;
case 'w':
db_printf(" TID %-15s %-5s %18s %s\n",
"COMMAND", "PGRP", "WAIT-CHANNEL", "WAIT-MSG");
break;
case 'o':
skipzomb = 1;
db_printf(" TID %5s %5s %10s %10s %3s %-30s\n",
"PID", "UID", "PRFLAGS", "PFLAGS", "CPU", "COMMAND");
break;
}
while (pr != NULL) {
ppr = pr->ps_pptr;
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
#ifdef MULTIPROCESSOR
if (__mp_lock_held(&kernel_lock, p->p_cpu))
has_kernel_lock = 1;
else
has_kernel_lock = 0;
#endif
if (p->p_stat) {
if (*mode == 'o') {
if (p->p_stat != SONPROC)
continue;
if (p->p_cpu != NULL && p->p_cpu->
ci_schedstate.spc_idleproc == p)
continue;
}
if (*mode == 'n') {
db_printf("%c%5d ", (p == curproc ?
'*' : ' '), pr->ps_pid);
} else {
db_printf("%c%6d ", (p == curproc ?
'*' : ' '), p->p_tid);
}
switch (*mode) {
case 'a':
db_printf("%-9.9s %18p %18p %18p\n",
pr->ps_comm, p, p->p_addr, p->p_vmspace);
break;
case 'n':
db_printf("%6d %5d %5d %d %#10x "
"%-12.12s %-15s\n",
p->p_tid, ppr ? ppr->ps_pid : -1,
pr->ps_ucred->cr_ruid, p->p_stat,
p->p_flag | pr->ps_flags,
(p->p_wchan && p->p_wmesg) ?
p->p_wmesg : "", pr->ps_comm);
break;
case 'w':
db_printf("%-15s %-5d %18p %s\n",
pr->ps_comm, (pr->ps_pgrp ?
pr->ps_pgrp->pg_id : -1),
p->p_wchan,
(p->p_wchan && p->p_wmesg) ?
p->p_wmesg : "");
break;
case 'o':
db_printf("%5d %5d %#10x %#10x %3d"
"%c %-31s\n",
pr->ps_pid, pr->ps_ucred->cr_ruid,
pr->ps_flags, p->p_flag,
CPU_INFO_UNIT(p->p_cpu),
has_kernel_lock ? 'K' : ' ',
pr->ps_comm);
break;
}
}
}
pr = LIST_NEXT(pr, ps_list);
if (pr == NULL && skipzomb == 0) {
skipzomb = 1;
pr = LIST_FIRST(&zombprocess);
}
}
}
#endif
#ifdef DEBUG
void
pgrpdump(void)
{
struct pgrp *pgrp;
struct process *pr;
int i;
for (i = 0; i <= pgrphash; i++) {
if (!LIST_EMPTY(&pgrphashtbl[i])) {
printf("\tindx %d\n", i);
LIST_FOREACH(pgrp, &pgrphashtbl[i], pg_hash) {
printf("\tpgrp %p, pgid %d, sess %p, sesscnt %d, mem %p\n",
pgrp, pgrp->pg_id, pgrp->pg_session,
pgrp->pg_session->s_count,
LIST_FIRST(&pgrp->pg_members));
LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
printf("\t\tpid %d addr %p pgrp %p\n",
pr->ps_pid, pr, pr->ps_pgrp);
}
}
}
}
}
#endif /* DEBUG */
2250
267
2151
2
2250
2213
3
89
27
27
15
2218
2216
135
2178
27
2214
2210
2240
114
2063
597
2252
2974
1636
1809
1780
58
57
1779
4
563
203
1788
12
4
4
2
1
2
1
2
783
783
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
/* $OpenBSD: kern_malloc.c,v 1.148 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_malloc.c,v 1.15.4.2 1996/06/13 17:10:56 cgd Exp $ */
/*
* Copyright (c) 1987, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_malloc.c 8.3 (Berkeley) 1/4/94
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/stdint.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/tracepoint.h>
#include <uvm/uvm_extern.h>
#if defined(DDB)
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
#endif
static
#ifndef SMALL_KERNEL
__inline__
#endif
long BUCKETINDX(size_t sz)
{
long b, d;
/* note that this relies upon MINALLOCSIZE being 1 << MINBUCKET */
b = 7 + MINBUCKET; d = 4;
while (d != 0) {
if (sz <= (1 << b))
b -= d;
else
b += d;
d >>= 1;
}
if (sz <= (1 << b))
b += 0;
else
b += 1;
return b;
}
static struct vm_map kmem_map_store;
struct vm_map *kmem_map = NULL;
/*
* Default number of pages in kmem_map. We attempt to calculate this
* at run-time, but allow it to be either patched or set in the kernel
* config file.
*/
#ifndef NKMEMPAGES
#define NKMEMPAGES 0
#endif
u_int nkmempages = NKMEMPAGES;
/*
* Defaults for lower- and upper-bounds for the kmem_map page count.
* Can be overridden by kernel config options.
*/
#ifndef NKMEMPAGES_MIN
#define NKMEMPAGES_MIN 0
#endif
u_int nkmempages_min = 0;
#ifndef NKMEMPAGES_MAX
#define NKMEMPAGES_MAX NKMEMPAGES_MAX_DEFAULT
#endif
u_int nkmempages_max = 0;
struct mutex malloc_mtx = MUTEX_INITIALIZER(IPL_VM);
struct kmembuckets bucket[MINBUCKET + 16];
#ifdef KMEMSTATS
struct kmemstats kmemstats[M_LAST];
#endif
struct kmemusage *kmemusage;
char *kmembase, *kmemlimit;
char buckstring[16 * sizeof("123456,")];
int buckstring_init = 0;
#if defined(KMEMSTATS) || defined(DIAGNOSTIC)
char *memname[] = INITKMEMNAMES;
char *memall = NULL;
struct rwlock sysctl_kmemlock = RWLOCK_INITIALIZER("sysctlklk");
#endif
/*
* Normally the freelist structure is used only to hold the list pointer
* for free objects. However, when running with diagnostics, the first
* 8 bytes of the structure is unused except for diagnostic information,
* and the free list pointer is at offset 8 in the structure. Since the
* first 8 bytes is the portion of the structure most often modified, this
* helps to detect memory reuse problems and avoid free list corruption.
*/
struct kmem_freelist {
int32_t kf_spare0;
int16_t kf_type;
int16_t kf_spare1;
XSIMPLEQ_ENTRY(kmem_freelist) kf_flist;
};
#ifdef DIAGNOSTIC
/*
* This structure provides a set of masks to catch unaligned frees.
*/
const long addrmask[] = { 0,
0x00000001, 0x00000003, 0x00000007, 0x0000000f,
0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
};
#endif /* DIAGNOSTIC */
#ifndef SMALL_KERNEL
struct timeval malloc_errintvl = { 5, 0 };
struct timeval malloc_lasterr;
#endif
/*
* Allocate a block of memory
*/
void *
malloc(size_t size, int type, int flags)
{
struct kmembuckets *kbp;
struct kmemusage *kup;
struct kmem_freelist *freep;
long indx, npg, allocsize;
caddr_t va, cp;
int s;
#ifdef DIAGNOSTIC
int freshalloc;
char *savedtype;
#endif
#ifdef KMEMSTATS
struct kmemstats *ksp = &kmemstats[type];
int wake;
if (((unsigned long)type) <= 1 || ((unsigned long)type) >= M_LAST)
panic("malloc: bogus type %d", type);
#endif
KASSERT(flags & (M_WAITOK | M_NOWAIT));
#ifdef DIAGNOSTIC
if ((flags & M_NOWAIT) == 0) {
extern int pool_debug;
assertwaitok();
if (pool_debug == 2)
yield();
}
#endif
if (size > 65535 * PAGE_SIZE) {
if (flags & M_CANFAIL) {
#ifndef SMALL_KERNEL
if (ratecheck(&malloc_lasterr, &malloc_errintvl))
printf("malloc(): allocation too large, "
"type = %d, size = %lu\n", type, size);
#endif
return (NULL);
} else
panic("malloc: allocation too large, "
"type = %d, size = %lu", type, size);
}
indx = BUCKETINDX(size);
if (size > MAXALLOCSAVE)
allocsize = round_page(size);
else
allocsize = 1 << indx;
kbp = &bucket[indx];
mtx_enter(&malloc_mtx);
#ifdef KMEMSTATS
while (ksp->ks_memuse >= ksp->ks_limit) {
if (flags & M_NOWAIT) {
mtx_leave(&malloc_mtx);
return (NULL);
}
#ifdef DIAGNOSTIC
if (ISSET(flags, M_WAITOK) && curproc == &proc0)
panic("%s: cannot sleep for memory during boot",
__func__);
#endif
if (ksp->ks_limblocks < 65535)
ksp->ks_limblocks++;
msleep_nsec(ksp, &malloc_mtx, PSWP+2, memname[type], INFSLP);
}
ksp->ks_memuse += allocsize; /* account for this early */
ksp->ks_size |= 1 << indx;
#endif
if (XSIMPLEQ_FIRST(&kbp->kb_freelist) == NULL) {
mtx_leave(&malloc_mtx);
npg = atop(round_page(allocsize));
s = splvm();
va = (caddr_t)uvm_km_kmemalloc_pla(kmem_map, NULL,
(vsize_t)ptoa(npg), 0,
((flags & M_NOWAIT) ? UVM_KMF_NOWAIT : 0) |
((flags & M_CANFAIL) ? UVM_KMF_CANFAIL : 0),
no_constraint.ucr_low, no_constraint.ucr_high,
0, 0, 0);
splx(s);
if (va == NULL) {
/*
* Kmem_malloc() can return NULL, even if it can
* wait, if there is no map space available, because
* it can't fix that problem. Neither can we,
* right now. (We should release pages which
* are completely free and which are in buckets
* with too many free elements.)
*/
if ((flags & (M_NOWAIT|M_CANFAIL)) == 0)
panic("malloc: out of space in kmem_map");
#ifdef KMEMSTATS
mtx_enter(&malloc_mtx);
ksp->ks_memuse -= allocsize;
wake = ksp->ks_memuse + allocsize >= ksp->ks_limit &&
ksp->ks_memuse < ksp->ks_limit;
mtx_leave(&malloc_mtx);
if (wake)
wakeup(ksp);
#endif
return (NULL);
}
mtx_enter(&malloc_mtx);
#ifdef KMEMSTATS
kbp->kb_total += kbp->kb_elmpercl;
#endif
kup = btokup(va);
kup->ku_indx = indx;
#ifdef DIAGNOSTIC
freshalloc = 1;
#endif
if (allocsize > MAXALLOCSAVE) {
kup->ku_pagecnt = npg;
goto out;
}
#ifdef KMEMSTATS
kup->ku_freecnt = kbp->kb_elmpercl;
kbp->kb_totalfree += kbp->kb_elmpercl;
#endif
cp = va + (npg * PAGE_SIZE) - allocsize;
for (;;) {
freep = (struct kmem_freelist *)cp;
#ifdef DIAGNOSTIC
/*
* Copy in known text to detect modification
* after freeing.
*/
poison_mem(cp, allocsize);
freep->kf_type = M_FREE;
#endif /* DIAGNOSTIC */
XSIMPLEQ_INSERT_HEAD(&kbp->kb_freelist, freep,
kf_flist);
if (cp <= va)
break;
cp -= allocsize;
}
} else {
#ifdef DIAGNOSTIC
freshalloc = 0;
#endif
}
freep = XSIMPLEQ_FIRST(&kbp->kb_freelist);
XSIMPLEQ_REMOVE_HEAD(&kbp->kb_freelist, kf_flist);
va = (caddr_t)freep;
#ifdef DIAGNOSTIC
savedtype = (unsigned)freep->kf_type < M_LAST ?
memname[freep->kf_type] : "???";
if (freshalloc == 0 && XSIMPLEQ_FIRST(&kbp->kb_freelist)) {
int rv;
vaddr_t addr = (vaddr_t)XSIMPLEQ_FIRST(&kbp->kb_freelist);
vm_map_lock(kmem_map);
rv = uvm_map_checkprot(kmem_map, addr,
addr + sizeof(struct kmem_freelist), PROT_WRITE);
vm_map_unlock(kmem_map);
if (!rv) {
printf("%s %zd of object %p size 0x%lx %s %s"
" (invalid addr %p)\n",
"Data modified on freelist: word",
(int32_t *)&addr - (int32_t *)kbp, va, size,
"previous type", savedtype, (void *)addr);
}
}
/* Fill the fields that we've used with poison */
poison_mem(freep, sizeof(*freep));
/* and check that the data hasn't been modified. */
if (freshalloc == 0) {
size_t pidx;
uint32_t pval;
if (poison_check(va, allocsize, &pidx, &pval)) {
panic("%s %zd of object %p size 0x%lx %s %s"
" (0x%x != 0x%x)\n",
"Data modified on freelist: word",
pidx, va, size, "previous type",
savedtype, ((int32_t*)va)[pidx], pval);
}
}
freep->kf_spare0 = 0;
#endif /* DIAGNOSTIC */
#ifdef KMEMSTATS
kup = btokup(va);
if (kup->ku_indx != indx)
panic("malloc: wrong bucket");
if (kup->ku_freecnt == 0)
panic("malloc: lost data");
kup->ku_freecnt--;
kbp->kb_totalfree--;
out:
kbp->kb_calls++;
ksp->ks_inuse++;
ksp->ks_calls++;
if (ksp->ks_memuse > ksp->ks_maxused)
ksp->ks_maxused = ksp->ks_memuse;
#else
out:
#endif
mtx_leave(&malloc_mtx);
if ((flags & M_ZERO) && va != NULL)
memset(va, 0, size);
TRACEPOINT(uvm, malloc, type, va, size, flags);
return (va);
}
/*
* Free a block of memory allocated by malloc.
*/
void
free(void *addr, int type, size_t freedsize)
{
struct kmembuckets *kbp;
struct kmemusage *kup;
struct kmem_freelist *freep;
long size;
int s;
#ifdef DIAGNOSTIC
long alloc;
#endif
#ifdef KMEMSTATS
struct kmemstats *ksp = &kmemstats[type];
int wake;
#endif
if (addr == NULL)
return;
#ifdef DIAGNOSTIC
if (addr < (void *)kmembase || addr >= (void *)kmemlimit)
panic("free: non-malloced addr %p type %s", addr,
memname[type]);
#endif
TRACEPOINT(uvm, free, type, addr, freedsize);
mtx_enter(&malloc_mtx);
kup = btokup(addr);
size = 1 << kup->ku_indx;
kbp = &bucket[kup->ku_indx];
if (size > MAXALLOCSAVE)
size = kup->ku_pagecnt << PAGE_SHIFT;
#ifdef DIAGNOSTIC
#if 0
if (freedsize == 0) {
static int zerowarnings;
if (zerowarnings < 5) {
zerowarnings++;
printf("free with zero size: (%d)\n", type);
#ifdef DDB
db_stack_dump();
#endif
}
#endif
if (freedsize != 0 && freedsize > size)
panic("free: size too large %zu > %ld (%p) type %s",
freedsize, size, addr, memname[type]);
if (freedsize != 0 && size > MINALLOCSIZE && freedsize <= size / 2)
panic("free: size too small %zu <= %ld / 2 (%p) type %s",
freedsize, size, addr, memname[type]);
/*
* Check for returns of data that do not point to the
* beginning of the allocation.
*/
if (size > PAGE_SIZE)
alloc = addrmask[BUCKETINDX(PAGE_SIZE)];
else
alloc = addrmask[kup->ku_indx];
if (((u_long)addr & alloc) != 0)
panic("free: unaligned addr %p, size %ld, type %s, mask %ld",
addr, size, memname[type], alloc);
#endif /* DIAGNOSTIC */
if (size > MAXALLOCSAVE) {
u_short pagecnt = kup->ku_pagecnt;
kup->ku_indx = 0;
kup->ku_pagecnt = 0;
mtx_leave(&malloc_mtx);
s = splvm();
uvm_km_free(kmem_map, (vaddr_t)addr, ptoa(pagecnt));
splx(s);
#ifdef KMEMSTATS
mtx_enter(&malloc_mtx);
ksp->ks_memuse -= size;
wake = ksp->ks_memuse + size >= ksp->ks_limit &&
ksp->ks_memuse < ksp->ks_limit;
ksp->ks_inuse--;
kbp->kb_total -= 1;
mtx_leave(&malloc_mtx);
if (wake)
wakeup(ksp);
#endif
return;
}
freep = (struct kmem_freelist *)addr;
#ifdef DIAGNOSTIC
/*
* Check for multiple frees. Use a quick check to see if
* it looks free before laboriously searching the freelist.
*/
if (freep->kf_spare0 == poison_value(freep)) {
struct kmem_freelist *fp;
XSIMPLEQ_FOREACH(fp, &kbp->kb_freelist, kf_flist) {
if (addr != fp)
continue;
printf("multiply freed item %p\n", addr);
panic("free: duplicated free");
}
}
/*
* Copy in known text to detect modification after freeing
* and to make it look free. Also, save the type being freed
* so we can list likely culprit if modification is detected
* when the object is reallocated.
*/
poison_mem(addr, size);
freep->kf_spare0 = poison_value(freep);
freep->kf_type = type;
#endif /* DIAGNOSTIC */
#ifdef KMEMSTATS
kup->ku_freecnt++;
if (kup->ku_freecnt >= kbp->kb_elmpercl) {
if (kup->ku_freecnt > kbp->kb_elmpercl)
panic("free: multiple frees");
else if (kbp->kb_totalfree > kbp->kb_highwat)
kbp->kb_couldfree++;
}
kbp->kb_totalfree++;
ksp->ks_memuse -= size;
wake = ksp->ks_memuse + size >= ksp->ks_limit &&
ksp->ks_memuse < ksp->ks_limit;
ksp->ks_inuse--;
#endif
XSIMPLEQ_INSERT_TAIL(&kbp->kb_freelist, freep, kf_flist);
mtx_leave(&malloc_mtx);
#ifdef KMEMSTATS
if (wake)
wakeup(ksp);
#endif
}
/*
* Compute the number of pages that kmem_map will map, that is,
* the size of the kernel malloc arena.
*/
void
kmeminit_nkmempages(void)
{
u_int npages;
if (nkmempages != 0) {
/*
* It's already been set (by us being here before, or
* by patching or kernel config options), bail out now.
*/
return;
}
/*
* We can't initialize these variables at compilation time, since
* the page size may not be known (on sparc GENERIC kernels, for
* example). But we still want the MD code to be able to provide
* better values.
*/
if (nkmempages_min == 0)
nkmempages_min = NKMEMPAGES_MIN;
if (nkmempages_max == 0)
nkmempages_max = NKMEMPAGES_MAX;
/*
* We use the following (simple) formula:
*
* - Starting point is physical memory / 4.
*
* - Clamp it down to nkmempages_max.
*
* - Round it up to nkmempages_min.
*/
npages = physmem / 4;
if (npages > nkmempages_max)
npages = nkmempages_max;
if (npages < nkmempages_min)
npages = nkmempages_min;
nkmempages = npages;
}
/*
* Initialize the kernel memory allocator
*/
void
kmeminit(void)
{
vaddr_t base, limit;
long indx;
#ifdef DIAGNOSTIC
if (sizeof(struct kmem_freelist) > (1 << MINBUCKET))
panic("kmeminit: minbucket too small/struct freelist too big");
#endif
/*
* Compute the number of kmem_map pages, if we have not
* done so already.
*/
kmeminit_nkmempages();
base = vm_map_min(kernel_map);
kmem_map = uvm_km_suballoc(kernel_map, &base, &limit,
(vsize_t)nkmempages << PAGE_SHIFT,
#ifdef KVA_GUARDPAGES
VM_MAP_INTRSAFE | VM_MAP_GUARDPAGES,
#else
VM_MAP_INTRSAFE,
#endif
FALSE, &kmem_map_store);
kmembase = (char *)base;
kmemlimit = (char *)limit;
kmemusage = km_alloc(round_page(nkmempages * sizeof(struct kmemusage)),
&kv_any, &kp_zero, &kd_waitok);
for (indx = 0; indx < MINBUCKET + 16; indx++) {
XSIMPLEQ_INIT(&bucket[indx].kb_freelist);
}
#ifdef KMEMSTATS
for (indx = 0; indx < MINBUCKET + 16; indx++) {
if (1 << indx >= PAGE_SIZE)
bucket[indx].kb_elmpercl = 1;
else
bucket[indx].kb_elmpercl = PAGE_SIZE / (1 << indx);
bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
}
for (indx = 0; indx < M_LAST; indx++)
kmemstats[indx].ks_limit = nkmempages * PAGE_SIZE * 6 / 10;
#endif
}
/*
* Return kernel malloc statistics information.
*/
int
sysctl_malloc(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
struct kmembuckets kb;
#ifdef KMEMSTATS
struct kmemstats km;
#endif
#if defined(KMEMSTATS) || defined(DIAGNOSTIC)
int error;
#endif
int i, siz;
if (namelen != 2 && name[0] != KERN_MALLOC_BUCKETS &&
name[0] != KERN_MALLOC_KMEMNAMES)
return (ENOTDIR); /* overloaded */
switch (name[0]) {
case KERN_MALLOC_BUCKETS:
/* Initialize the first time */
if (buckstring_init == 0) {
buckstring_init = 1;
memset(buckstring, 0, sizeof(buckstring));
for (siz = 0, i = MINBUCKET; i < MINBUCKET + 16; i++) {
snprintf(buckstring + siz,
sizeof buckstring - siz,
"%d,", (u_int)(1<<i));
siz += strlen(buckstring + siz);
}
/* Remove trailing comma */
if (siz)
buckstring[siz - 1] = '\0';
}
return (sysctl_rdstring(oldp, oldlenp, newp, buckstring));
case KERN_MALLOC_BUCKET:
mtx_enter(&malloc_mtx);
memcpy(&kb, &bucket[BUCKETINDX(name[1])], sizeof(kb));
mtx_leave(&malloc_mtx);
memset(&kb.kb_freelist, 0, sizeof(kb.kb_freelist));
return (sysctl_rdstruct(oldp, oldlenp, newp, &kb, sizeof(kb)));
case KERN_MALLOC_KMEMSTATS:
#ifdef KMEMSTATS
if ((name[1] < 0) || (name[1] >= M_LAST))
return (EINVAL);
mtx_enter(&malloc_mtx);
memcpy(&km, &kmemstats[name[1]], sizeof(km));
mtx_leave(&malloc_mtx);
return (sysctl_rdstruct(oldp, oldlenp, newp, &km, sizeof(km)));
#else
return (EOPNOTSUPP);
#endif
case KERN_MALLOC_KMEMNAMES:
#if defined(KMEMSTATS) || defined(DIAGNOSTIC)
error = rw_enter(&sysctl_kmemlock, RW_WRITE|RW_INTR);
if (error)
return (error);
if (memall == NULL) {
int totlen;
/* Figure out how large a buffer we need */
for (totlen = 0, i = 0; i < M_LAST; i++) {
if (memname[i])
totlen += strlen(memname[i]);
totlen++;
}
memall = malloc(totlen + M_LAST, M_SYSCTL,
M_WAITOK|M_ZERO);
for (siz = 0, i = 0; i < M_LAST; i++) {
snprintf(memall + siz,
totlen + M_LAST - siz,
"%s,", memname[i] ? memname[i] : "");
siz += strlen(memall + siz);
}
/* Remove trailing comma */
if (siz)
memall[siz - 1] = '\0';
/* Now, convert all spaces to underscores */
for (i = 0; i < totlen; i++)
if (memall[i] == ' ')
memall[i] = '_';
}
rw_exit_write(&sysctl_kmemlock);
return (sysctl_rdstring(oldp, oldlenp, newp, memall));
#else
return (EOPNOTSUPP);
#endif
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
#if defined(DDB)
void
malloc_printit(
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
#ifdef KMEMSTATS
struct kmemstats *km;
int i;
(*pr)("%15s %5s %6s %7s %6s %9s %8s\n",
"Type", "InUse", "MemUse", "HighUse", "Limit", "Requests",
"Type Lim");
for (i = 0, km = kmemstats; i < M_LAST; i++, km++) {
if (!km->ks_calls || !memname[i])
continue;
(*pr)("%15s %5ld %6ldK %7ldK %6ldK %9ld %8d\n",
memname[i], km->ks_inuse, km->ks_memuse / 1024,
km->ks_maxused / 1024, km->ks_limit / 1024,
km->ks_calls, km->ks_limblocks);
}
#else
(*pr)("No KMEMSTATS compiled in\n");
#endif
}
#endif /* DDB */
/*
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
*/
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
void *
mallocarray(size_t nmemb, size_t size, int type, int flags)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
if (flags & M_CANFAIL)
return (NULL);
panic("mallocarray: overflow %zu * %zu", nmemb, size);
}
return (malloc(size * nmemb, type, flags));
}
29
456
456
460
19
454
264
246
277
71
10
15
50
23
46
47
24
67
31
31
6
31
25
31
31
30
8
1
1
8
29
37
37
36
37
37
37
18
37
1
19
37
22
32
110
33
107
92
88
106
108
107
93
108
108
107
107
107
107
108
22
6
23
23
6
23
33
33
1
2
2
2
2
31
31
38
38
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
/* $OpenBSD: art.c,v 1.29 2020/11/12 15:25:28 mpi Exp $ */
/*
* Copyright (c) 2015 Martin Pieuchot
* Copyright (c) 2001 Yoichi Hariguchi
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Allotment Routing Table (ART).
*
* Yoichi Hariguchi paper can be found at:
* http://www.hariguchi.org/art/art.pdf
*/
#ifndef _KERNEL
#include "kern_compat.h"
#else
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/task.h>
#include <sys/socket.h>
#endif
#include <net/art.h>
int art_bindex(struct art_table *, uint8_t *, int);
void art_allot(struct art_table *at, int, struct art_node *,
struct art_node *);
struct art_table *art_table_get(struct art_root *, struct art_table *,
int);
struct art_table *art_table_put(struct art_root *, struct art_table *);
struct art_node *art_table_insert(struct art_root *, struct art_table *,
int, struct art_node *);
struct art_node *art_table_delete(struct art_root *, struct art_table *,
int, struct art_node *);
struct art_table *art_table_ref(struct art_root *, struct art_table *);
int art_table_free(struct art_root *, struct art_table *);
int art_table_walk(struct art_root *, struct art_table *,
int (*f)(struct art_node *, void *), void *);
int art_walk_apply(struct art_root *,
struct art_node *, struct art_node *,
int (*f)(struct art_node *, void *), void *);
void art_table_gc(void *);
void art_gc(void *);
struct pool an_pool, at_pool, at_heap_4_pool, at_heap_8_pool;
struct art_table *art_table_gc_list = NULL;
struct mutex art_table_gc_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct task art_table_gc_task =
TASK_INITIALIZER(art_table_gc, NULL);
struct art_node *art_node_gc_list = NULL;
struct mutex art_node_gc_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct task art_node_gc_task = TASK_INITIALIZER(art_gc, NULL);
void
art_init(void)
{
pool_init(&an_pool, sizeof(struct art_node), 0, IPL_SOFTNET, 0,
"art_node", NULL);
pool_init(&at_pool, sizeof(struct art_table), 0, IPL_SOFTNET, 0,
"art_table", NULL);
pool_init(&at_heap_4_pool, AT_HEAPSIZE(4), 0, IPL_SOFTNET, 0,
"art_heap4", NULL);
pool_init(&at_heap_8_pool, AT_HEAPSIZE(8), 0, IPL_SOFTNET, 0,
"art_heap8", &pool_allocator_single);
}
/*
* Per routing table initialization API function.
*/
struct art_root *
art_alloc(unsigned int rtableid, unsigned int alen, unsigned int off)
{
struct art_root *ar;
int i;
ar = malloc(sizeof(*ar), M_RTABLE, M_NOWAIT|M_ZERO);
if (ar == NULL)
return (NULL);
switch (alen) {
case 32:
ar->ar_alen = 32;
ar->ar_nlvl = 7;
ar->ar_bits[0] = 8;
for (i = 1; i < ar->ar_nlvl; i++)
ar->ar_bits[i] = 4;
break;
case 128:
ar->ar_alen = 128;
ar->ar_nlvl = 32;
for (i = 0; i < ar->ar_nlvl; i++)
ar->ar_bits[i] = 4;
break;
default:
printf("%s: incorrect address length %u\n", __func__, alen);
free(ar, M_RTABLE, sizeof(*ar));
return (NULL);
}
ar->ar_off = off;
rw_init(&ar->ar_lock, "art");
return (ar);
}
/*
* Return 1 if ``old'' and ``new`` are identical, 0 otherwise.
*/
static inline int
art_check_duplicate(struct art_root *ar, struct art_node *old,
struct art_node *new)
{
if (old == NULL)
return (0);
if (old->an_plen == new->an_plen)
return (1);
return (0);
}
/*
* Return the base index of the part of ``addr'' and ``plen''
* corresponding to the range covered by the table ``at''.
*
* In other words, this function take the multi-level (complete)
* address ``addr'' and prefix length ``plen'' and return the
* single level base index for the table ``at''.
*
* For example with an address size of 32bit divided into four
* 8bit-long tables, there's a maximum of 4 base indexes if the
* prefix length is > 24.
*/
int
art_bindex(struct art_table *at, uint8_t *addr, int plen)
{
uint8_t boff, bend;
uint32_t k;
if (plen < at->at_offset || plen > (at->at_offset + at->at_bits))
return (-1);
/*
* We are only interested in the part of the prefix length
* corresponding to the range of this table.
*/
plen -= at->at_offset;
/*
* Jump to the first byte of the address containing bits
* covered by this table.
*/
addr += (at->at_offset / 8);
/* ``at'' covers the bit range between ``boff'' & ``bend''. */
boff = (at->at_offset % 8);
bend = (at->at_bits + boff);
KASSERT(bend <= 32);
if (bend > 24) {
k = (addr[0] & ((1 << (8 - boff)) - 1)) << (bend - 8);
k |= addr[1] << (bend - 16);
k |= addr[2] << (bend - 24);
k |= addr[3] >> (32 - bend);
} else if (bend > 16) {
k = (addr[0] & ((1 << (8 - boff)) - 1)) << (bend - 8);
k |= addr[1] << (bend - 16);
k |= addr[2] >> (24 - bend);
} else if (bend > 8) {
k = (addr[0] & ((1 << (8 - boff)) - 1)) << (bend - 8);
k |= addr[1] >> (16 - bend);
} else {
k = (addr[0] >> (8 - bend)) & ((1 << at->at_bits) - 1);
}
/*
* Single level base index formula:
*/
return ((k >> (at->at_bits - plen)) + (1 << plen));
}
/*
* Single level lookup function.
*
* Return the fringe index of the part of ``addr''
* corresponding to the range covered by the table ``at''.
*/
static inline int
art_findex(struct art_table *at, uint8_t *addr)
{
return art_bindex(at, addr, (at->at_offset + at->at_bits));
}
/*
* (Non-perfect) lookup API function.
*
* Return the best existing match for a destination.
*/
struct art_node *
art_match(struct art_root *ar, void *addr, struct srp_ref *nsr)
{
struct srp_ref dsr, ndsr;
void *entry;
struct art_table *at;
struct art_node *dflt, *ndflt;
int j;
entry = srp_enter(nsr, &ar->ar_root);
at = entry;
if (at == NULL)
goto done;
/*
* Remember the default route of each table we visit in case
* we do not find a better matching route.
*/
dflt = srp_enter(&dsr, &at->at_default);
/*
* Iterate until we find a leaf.
*/
while (1) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
entry = srp_follow(nsr, &at->at_heap[j].node);
/* If this is a leaf (NULL is a leaf) we're done. */
if (ISLEAF(entry))
break;
at = SUBTABLE(entry);
ndflt = srp_enter(&ndsr, &at->at_default);
if (ndflt != NULL) {
srp_leave(&dsr);
dsr = ndsr;
dflt = ndflt;
} else
srp_leave(&ndsr);
}
if (entry == NULL) {
srp_leave(nsr);
*nsr = dsr;
KASSERT(ISLEAF(dflt));
return (dflt);
}
srp_leave(&dsr);
done:
KASSERT(ISLEAF(entry));
return (entry);
}
/*
* Perfect lookup API function.
*
* Return a perfect match for a destination/prefix-length pair or NULL if
* it does not exist.
*/
struct art_node *
art_lookup(struct art_root *ar, void *addr, int plen, struct srp_ref *nsr)
{
void *entry;
struct art_table *at;
int i, j;
KASSERT(plen >= 0 && plen <= ar->ar_alen);
entry = srp_enter(nsr, &ar->ar_root);
at = entry;
if (at == NULL)
goto done;
/* Default route */
if (plen == 0) {
entry = srp_follow(nsr, &at->at_default);
goto done;
}
/*
* If the prefix length is smaller than the sum of
* the stride length at this level the entry must
* be in the current table.
*/
while (plen > (at->at_offset + at->at_bits)) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
entry = srp_follow(nsr, &at->at_heap[j].node);
/* A leaf is a match, but not a perfect one, or NULL */
if (ISLEAF(entry))
return (NULL);
at = SUBTABLE(entry);
}
i = art_bindex(at, addr, plen);
if (i == -1)
return (NULL);
entry = srp_follow(nsr, &at->at_heap[i].node);
if (!ISLEAF(entry))
entry = srp_follow(nsr, &SUBTABLE(entry)->at_default);
done:
KASSERT(ISLEAF(entry));
return (entry);
}
/*
* Insertion API function.
*
* Insert the given node or return an existing one if a node with the
* same destination/mask pair is already present.
*/
struct art_node *
art_insert(struct art_root *ar, struct art_node *an, void *addr, int plen)
{
struct art_table *at, *child;
struct art_node *node;
int i, j;
rw_assert_wrlock(&ar->ar_lock);
KASSERT(plen >= 0 && plen <= ar->ar_alen);
at = srp_get_locked(&ar->ar_root);
if (at == NULL) {
at = art_table_get(ar, NULL, -1);
if (at == NULL)
return (NULL);
srp_swap_locked(&ar->ar_root, at);
}
/* Default route */
if (plen == 0) {
node = srp_get_locked(&at->at_default);
if (node != NULL)
return (node);
art_table_ref(ar, at);
srp_swap_locked(&at->at_default, an);
return (an);
}
/*
* If the prefix length is smaller than the sum of
* the stride length at this level the entry must
* be in the current table.
*/
while (plen > (at->at_offset + at->at_bits)) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
node = srp_get_locked(&at->at_heap[j].node);
/*
* If the node corresponding to the fringe index is
* a leaf we need to allocate a subtable. The route
* entry of this node will then become the default
* route of the subtable.
*/
if (ISLEAF(node)) {
child = art_table_get(ar, at, j);
if (child == NULL)
return (NULL);
art_table_ref(ar, at);
srp_swap_locked(&at->at_heap[j].node, ASNODE(child));
at = child;
} else
at = SUBTABLE(node);
}
i = art_bindex(at, addr, plen);
if (i == -1)
return (NULL);
return (art_table_insert(ar, at, i, an));
}
/*
* Single level insertion.
*/
struct art_node *
art_table_insert(struct art_root *ar, struct art_table *at, int i,
struct art_node *an)
{
struct art_node *prev, *node;
node = srp_get_locked(&at->at_heap[i].node);
if (!ISLEAF(node))
prev = srp_get_locked(&SUBTABLE(node)->at_default);
else
prev = node;
if (art_check_duplicate(ar, prev, an))
return (prev);
art_table_ref(ar, at);
/*
* If the index `i' of the route that we are inserting is not
* a fringe index, we need to allot this new route pointer to
* all the corresponding fringe indices.
*/
if (i < at->at_minfringe)
art_allot(at, i, prev, an);
else if (!ISLEAF(node))
srp_swap_locked(&SUBTABLE(node)->at_default, an);
else
srp_swap_locked(&at->at_heap[i].node, an);
return (an);
}
/*
* Deletion API function.
*/
struct art_node *
art_delete(struct art_root *ar, struct art_node *an, void *addr, int plen)
{
struct art_table *at;
struct art_node *node;
int i, j;
rw_assert_wrlock(&ar->ar_lock);
KASSERT(plen >= 0 && plen <= ar->ar_alen);
at = srp_get_locked(&ar->ar_root);
if (at == NULL)
return (NULL);
/* Default route */
if (plen == 0) {
node = srp_get_locked(&at->at_default);
srp_swap_locked(&at->at_default, NULL);
art_table_free(ar, at);
return (node);
}
/*
* If the prefix length is smaller than the sum of
* the stride length at this level the entry must
* be in the current table.
*/
while (plen > (at->at_offset + at->at_bits)) {
/* Do a single level route lookup. */
j = art_findex(at, addr);
node = srp_get_locked(&at->at_heap[j].node);
/* If this is a leaf, there is no route to delete. */
if (ISLEAF(node))
return (NULL);
at = SUBTABLE(node);
}
i = art_bindex(at, addr, plen);
if (i == -1)
return (NULL);
return (art_table_delete(ar, at, i, an));
}
/*
* Single level deletion.
*/
struct art_node *
art_table_delete(struct art_root *ar, struct art_table *at, int i,
struct art_node *an)
{
struct art_node *next, *node;
#ifdef DIAGNOSTIC
struct art_node *prev;
#endif
node = srp_get_locked(&at->at_heap[i].node);
#ifdef DIAGNOSTIC
if (!ISLEAF(node))
prev = srp_get_locked(&SUBTABLE(node)->at_default);
else
prev = node;
KASSERT(prev == an);
#endif
/* Get the next most specific route for the index `i'. */
if ((i >> 1) > 1)
next = srp_get_locked(&at->at_heap[i >> 1].node);
else
next = NULL;
/*
* If the index `i' of the route that we are removing is not
* a fringe index, we need to allot the next most specific
* route pointer to all the corresponding fringe indices.
*/
if (i < at->at_minfringe)
art_allot(at, i, an, next);
else if (!ISLEAF(node))
srp_swap_locked(&SUBTABLE(node)->at_default, next);
else
srp_swap_locked(&at->at_heap[i].node, next);
/* We have removed an entry from this table. */
art_table_free(ar, at);
return (an);
}
struct art_table *
art_table_ref(struct art_root *ar, struct art_table *at)
{
at->at_refcnt++;
return (at);
}
static inline int
art_table_rele(struct art_table *at)
{
if (at == NULL)
return (0);
return (--at->at_refcnt == 0);
}
int
art_table_free(struct art_root *ar, struct art_table *at)
{
if (art_table_rele(at)) {
/*
* Garbage collect this table and all its parents
* that are empty.
*/
do {
at = art_table_put(ar, at);
} while (art_table_rele(at));
return (1);
}
return (0);
}
/*
* Iteration API function.
*/
int
art_walk(struct art_root *ar, int (*f)(struct art_node *, void *), void *arg)
{
struct srp_ref sr;
struct art_table *at;
struct art_node *node;
int error = 0;
rw_enter_write(&ar->ar_lock);
at = srp_get_locked(&ar->ar_root);
if (at != NULL) {
art_table_ref(ar, at);
/*
* The default route should be processed here because the root
* table does not have a parent.
*/
node = srp_enter(&sr, &at->at_default);
error = art_walk_apply(ar, node, NULL, f, arg);
srp_leave(&sr);
if (error == 0)
error = art_table_walk(ar, at, f, arg);
art_table_free(ar, at);
}
rw_exit_write(&ar->ar_lock);
return (error);
}
int
art_table_walk(struct art_root *ar, struct art_table *at,
int (*f)(struct art_node *, void *), void *arg)
{
struct srp_ref sr;
struct art_node *node, *next;
struct art_table *nat;
int i, j, error = 0;
uint32_t maxfringe = (at->at_minfringe << 1);
/*
* Iterate non-fringe nodes in ``natural'' order.
*/
for (j = 1; j < at->at_minfringe; j += 2) {
/*
* The default route (index 1) is processed by the
* parent table (where it belongs) otherwise it could
* be processed more than once.
*/
for (i = max(j, 2); i < at->at_minfringe; i <<= 1) {
next = srp_get_locked(&at->at_heap[i >> 1].node);
node = srp_enter(&sr, &at->at_heap[i].node);
error = art_walk_apply(ar, node, next, f, arg);
srp_leave(&sr);
if (error != 0)
return (error);
}
}
/*
* Iterate fringe nodes.
*/
for (i = at->at_minfringe; i < maxfringe; i++) {
next = srp_get_locked(&at->at_heap[i >> 1].node);
node = srp_enter(&sr, &at->at_heap[i].node);
if (!ISLEAF(node)) {
nat = art_table_ref(ar, SUBTABLE(node));
node = srp_follow(&sr, &nat->at_default);
} else
nat = NULL;
error = art_walk_apply(ar, node, next, f, arg);
srp_leave(&sr);
if (error != 0) {
art_table_free(ar, nat);
return (error);
}
if (nat != NULL) {
error = art_table_walk(ar, nat, f, arg);
art_table_free(ar, nat);
if (error != 0)
return (error);
}
}
return (0);
}
int
art_walk_apply(struct art_root *ar,
struct art_node *an, struct art_node *next,
int (*f)(struct art_node *, void *), void *arg)
{
int error = 0;
if ((an != NULL) && (an != next)) {
rw_exit_write(&ar->ar_lock);
error = (*f)(an, arg);
rw_enter_write(&ar->ar_lock);
}
return (error);
}
/*
* Create a table and use the given index to set its default route.
*
* Note: This function does not modify the root or the parent.
*/
struct art_table *
art_table_get(struct art_root *ar, struct art_table *parent, int j)
{
struct art_table *at;
struct art_node *node;
void *at_heap;
uint32_t lvl;
KASSERT(j != 0 && j != 1);
KASSERT(parent != NULL || j == -1);
if (parent != NULL)
lvl = parent->at_level + 1;
else
lvl = 0;
KASSERT(lvl < ar->ar_nlvl);
at = pool_get(&at_pool, PR_NOWAIT|PR_ZERO);
if (at == NULL)
return (NULL);
switch (AT_HEAPSIZE(ar->ar_bits[lvl])) {
case AT_HEAPSIZE(4):
at_heap = pool_get(&at_heap_4_pool, PR_NOWAIT|PR_ZERO);
break;
case AT_HEAPSIZE(8):
at_heap = pool_get(&at_heap_8_pool, PR_NOWAIT|PR_ZERO);
break;
default:
panic("incorrect stride length %u", ar->ar_bits[lvl]);
}
if (at_heap == NULL) {
pool_put(&at_pool, at);
return (NULL);
}
at->at_parent = parent;
at->at_index = j;
at->at_minfringe = (1 << ar->ar_bits[lvl]);
at->at_level = lvl;
at->at_bits = ar->ar_bits[lvl];
at->at_heap = at_heap;
at->at_refcnt = 0;
if (parent != NULL) {
node = srp_get_locked(&parent->at_heap[j].node);
/* node isn't being deleted, no srp_finalize needed */
srp_swap_locked(&at->at_default, node);
at->at_offset = (parent->at_offset + parent->at_bits);
}
return (at);
}
/*
* Delete a table and use its index to restore its parent's default route.
*
* Note: Modify its parent to unlink the table from it.
*/
struct art_table *
art_table_put(struct art_root *ar, struct art_table *at)
{
struct art_table *parent = at->at_parent;
struct art_node *node;
uint32_t j = at->at_index;
KASSERT(at->at_refcnt == 0);
KASSERT(j != 0 && j != 1);
if (parent != NULL) {
KASSERT(j != -1);
KASSERT(at->at_level == parent->at_level + 1);
KASSERT(parent->at_refcnt >= 1);
/* Give the route back to its parent. */
node = srp_get_locked(&at->at_default);
srp_swap_locked(&parent->at_heap[j].node, node);
} else {
KASSERT(j == -1);
KASSERT(at->at_level == 0);
srp_swap_locked(&ar->ar_root, NULL);
}
mtx_enter(&art_table_gc_mtx);
at->at_parent = art_table_gc_list;
art_table_gc_list = at;
mtx_leave(&art_table_gc_mtx);
task_add(systqmp, &art_table_gc_task);
return (parent);
}
void
art_table_gc(void *null)
{
struct art_table *at, *next;
mtx_enter(&art_table_gc_mtx);
at = art_table_gc_list;
art_table_gc_list = NULL;
mtx_leave(&art_table_gc_mtx);
while (at != NULL) {
next = at->at_parent;
if (at->at_level == 0)
srp_finalize(at, "arttfini");
else
srp_finalize(ASNODE(at), "arttfini");
switch (AT_HEAPSIZE(at->at_bits)) {
case AT_HEAPSIZE(4):
pool_put(&at_heap_4_pool, at->at_heap);
break;
case AT_HEAPSIZE(8):
pool_put(&at_heap_8_pool, at->at_heap);
break;
default:
panic("incorrect stride length %u", at->at_bits);
}
pool_put(&at_pool, at);
at = next;
}
}
/*
* Substitute a node by another in the subtree whose root index is given.
*
* This function iterates on the table ``at'' at index ``i'' until no
* more ``old'' node can be replaced by ``new''.
*
* This function was originally written by Don Knuth in CWEB. The
* complicated ``goto''s are the result of expansion of the two
* following recursions:
*
* art_allot(at, i, old, new)
* {
* int k = i;
* if (at->at_heap[k] == old)
* at->at_heap[k] = new;
* if (k >= at->at_minfringe)
* return;
* k <<= 1;
* art_allot(at, k, old, new);
* k++;
* art_allot(at, k, old, new);
* }
*/
void
art_allot(struct art_table *at, int i, struct art_node *old,
struct art_node *new)
{
struct art_node *node, *dflt;
int k = i;
KASSERT(i < at->at_minfringe);
again:
k <<= 1;
if (k < at->at_minfringe)
goto nonfringe;
/* Change fringe nodes. */
while (1) {
node = srp_get_locked(&at->at_heap[k].node);
if (!ISLEAF(node)) {
dflt = srp_get_locked(&SUBTABLE(node)->at_default);
if (dflt == old) {
srp_swap_locked(&SUBTABLE(node)->at_default,
new);
}
} else if (node == old) {
srp_swap_locked(&at->at_heap[k].node, new);
}
if (k % 2)
goto moveup;
k++;
}
nonfringe:
node = srp_get_locked(&at->at_heap[k].node);
if (node == old)
goto again;
moveon:
if (k % 2)
goto moveup;
k++;
goto nonfringe;
moveup:
k >>= 1;
srp_swap_locked(&at->at_heap[k].node, new);
/* Change non-fringe node. */
if (k != i)
goto moveon;
}
struct art_node *
art_get(void *dst, uint8_t plen)
{
struct art_node *an;
an = pool_get(&an_pool, PR_NOWAIT | PR_ZERO);
if (an == NULL)
return (NULL);
an->an_plen = plen;
SRPL_INIT(&an->an_rtlist);
return (an);
}
void
art_put(struct art_node *an)
{
KASSERT(SRPL_EMPTY_LOCKED(&an->an_rtlist));
mtx_enter(&art_node_gc_mtx);
an->an_gc = art_node_gc_list;
art_node_gc_list = an;
mtx_leave(&art_node_gc_mtx);
task_add(systqmp, &art_node_gc_task);
}
void
art_gc(void *null)
{
struct art_node *an, *next;
mtx_enter(&art_node_gc_mtx);
an = art_node_gc_list;
art_node_gc_list = NULL;
mtx_leave(&art_node_gc_mtx);
while (an != NULL) {
next = an->an_gc;
srp_finalize(an, "artnfini");
pool_put(&an_pool, an);
an = next;
}
}
2
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
/* $OpenBSD: ppp_tty.c,v 1.54 2022/01/02 22:36:04 jsg Exp $ */
/* $NetBSD: ppp_tty.c,v 1.12 1997/03/24 21:23:10 christos Exp $ */
/*
* ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous
* tty devices.
*
* Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Based on:
* @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89
*
* Copyright (c) 1987 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Serial Line interface
*
* Rick Adams
* Center for Seismic Studies
* 1300 N 17th Street, Suite 1450
* Arlington, Virginia 22209
* (703)276-7900
* rick@seismo.ARPA
* seismo!rick
*
* Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
* Converted to 4.3BSD Beta by Chris Torek.
* Other changes made at Berkeley, based in part on code by Kirk Smith.
*
* Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
* Added VJ tcp header compression; more unified ioctls
*
* Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
* Cleaned up a lot of the mbuf-related code to fix bugs that
* caused system crashes and packet corruption. Changed pppstart
* so that it doesn't just give up with a collision if the whole
* packet doesn't fit in the output ring buffer.
*
* Added priority queueing for interactive IP packets, following
* the model of if_sl.c, plus hooks for bpf.
* Paul Mackerras (paulus@cs.anu.edu.au).
*/
/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
/* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */
#include "ppp.h"
#if NPPP > 0
#define VJC
#define PPP_COMPRESS
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/tty.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/systm.h>
#include <sys/rwlock.h>
#include <sys/pool.h>
#include <net/if.h>
#include <net/if_var.h>
#ifdef VJC
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/slcompress.h>
#endif
#include <net/bpf.h>
#include <net/ppp_defs.h>
#include <net/if_ppp.h>
#include <net/if_pppvar.h>
int pppstart_internal(struct tty *tp, int);
u_int16_t pppfcs(u_int16_t fcs, u_char *cp, int len);
void pppasyncstart(struct ppp_softc *);
void pppasyncctlp(struct ppp_softc *);
void pppasyncrelinq(struct ppp_softc *);
void ppp_timeout(void *);
void ppppkt(struct ppp_softc *sc);
void pppdumpb(u_char *b, int l);
void ppplogchar(struct ppp_softc *, int);
struct rwlock ppp_pkt_init = RWLOCK_INITIALIZER("ppppktini");
struct pool ppp_pkts;
#define PKT_MAXLEN(_sc) ((_sc)->sc_mru + PPP_HDRLEN + PPP_FCSLEN)
/*
* Does c need to be escaped?
*/
#define ESCAPE_P(c) (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F)))
/*
* Procedures for using an async tty interface for PPP.
*/
/* This is a NetBSD-1.0 or later kernel. */
#define CCOUNT(q) ((q)->c_cc)
/*
* Line specific open routine for async tty devices.
* Attach the given tty to the first available ppp unit.
* Called from device open routine or ttioctl.
*/
int
pppopen(dev_t dev, struct tty *tp, struct proc *p)
{
struct ppp_softc *sc;
int error, s;
if ((error = suser(p)) != 0)
return (error);
rw_enter_write(&ppp_pkt_init);
if (ppp_pkts.pr_size == 0) {
extern const struct kmem_pa_mode kp_dma_contig;
pool_init(&ppp_pkts, sizeof(struct ppp_pkt), 0,
IPL_TTY, 0, "ppppkts", NULL); /* IPL_SOFTTTY */
pool_set_constraints(&ppp_pkts, &kp_dma_contig);
}
rw_exit_write(&ppp_pkt_init);
s = spltty();
if (tp->t_line == PPPDISC) {
sc = (struct ppp_softc *) tp->t_sc;
if (sc != NULL && sc->sc_devp == (void *) tp) {
splx(s);
return (0);
}
}
if ((sc = pppalloc(p->p_p->ps_pid)) == NULL) {
splx(s);
return ENXIO;
}
if (sc->sc_relinq)
(*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */
timeout_set(&sc->sc_timo, ppp_timeout, sc);
sc->sc_ilen = 0;
sc->sc_pkt = NULL;
bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
sc->sc_asyncmap[0] = 0xffffffff;
sc->sc_asyncmap[3] = 0x60000000;
sc->sc_rasyncmap = 0;
sc->sc_devp = (void *) tp;
sc->sc_start = pppasyncstart;
sc->sc_ctlp = pppasyncctlp;
sc->sc_relinq = pppasyncrelinq;
sc->sc_outm = NULL;
ppppkt(sc);
sc->sc_if.if_flags |= IFF_RUNNING;
sc->sc_if.if_baudrate = tp->t_ospeed;
tp->t_sc = (caddr_t) sc;
ttyflush(tp, FREAD | FWRITE);
splx(s);
return (0);
}
/*
* Line specific close routine, called from device close routine
* and from ttioctl.
* Detach the tty from the ppp unit.
* Mimics part of ttyclose().
*/
int
pppclose(struct tty *tp, int flag, struct proc *p)
{
struct ppp_softc *sc;
int s;
s = spltty();
ttyflush(tp, FREAD|FWRITE);
tp->t_line = 0;
sc = (struct ppp_softc *) tp->t_sc;
if (sc != NULL) {
tp->t_sc = NULL;
if (tp == (struct tty *) sc->sc_devp) {
pppasyncrelinq(sc);
pppdealloc(sc);
}
}
splx(s);
return 0;
}
/*
* Relinquish the interface unit to another device.
*/
void
pppasyncrelinq(struct ppp_softc *sc)
{
int s;
KERNEL_LOCK();
s = spltty();
m_freem(sc->sc_outm);
sc->sc_outm = NULL;
if (sc->sc_pkt != NULL) {
ppp_pkt_free(sc->sc_pkt);
sc->sc_pkt = sc->sc_pktc = NULL;
}
if (sc->sc_flags & SC_TIMEOUT) {
timeout_del(&sc->sc_timo);
sc->sc_flags &= ~SC_TIMEOUT;
}
splx(s);
KERNEL_UNLOCK();
}
/*
* Line specific (tty) read routine.
*/
int
pppread(struct tty *tp, struct uio *uio, int flag)
{
struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
struct mbuf *m, *m0;
int s;
int error = 0;
if (sc == NULL)
return 0;
/*
* Loop waiting for input, checking that nothing disastrous
* happens in the meantime.
*/
s = spltty();
for (;;) {
if (tp != (struct tty *) sc->sc_devp || tp->t_line != PPPDISC) {
splx(s);
return 0;
}
/* Get the packet from the input queue */
m0 = mq_dequeue(&sc->sc_inq);
if (m0 != NULL)
break;
if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0
&& (tp->t_state & TS_ISOPEN)) {
splx(s);
return 0; /* end of file */
}
if (tp->t_state & TS_ASYNC || flag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
}
error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin);
if (error) {
splx(s);
return error;
}
}
/* Pull place-holder byte out of canonical queue */
getc(&tp->t_canq);
splx(s);
for (m = m0; m && uio->uio_resid; m = m->m_next)
if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0)
break;
m_freem(m0);
return (error);
}
/*
* Line specific (tty) write routine.
*/
int
pppwrite(struct tty *tp, struct uio *uio, int flag)
{
struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
struct mbuf *m, *m0, **mp;
struct sockaddr dst;
u_int len;
int error;
if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
return 0; /* wrote 0 bytes */
if (tp->t_line != PPPDISC)
return (EINVAL);
if (sc == NULL || tp != (struct tty *) sc->sc_devp)
return EIO;
if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN ||
uio->uio_resid < PPP_HDRLEN)
return (EMSGSIZE);
for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
if (mp == &m0) {
MGETHDR(m, M_WAIT, MT_DATA);
m->m_pkthdr.len = uio->uio_resid - PPP_HDRLEN;
m->m_pkthdr.ph_ifidx = 0;
} else
MGET(m, M_WAIT, MT_DATA);
*mp = m;
m->m_len = 0;
if (uio->uio_resid >= MCLBYTES / 2)
MCLGET(m, M_DONTWAIT);
len = m_trailingspace(m);
if (len > uio->uio_resid)
len = uio->uio_resid;
if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
m_freem(m0);
return (error);
}
m->m_len = len;
}
dst.sa_family = AF_UNSPEC;
bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN);
m0->m_data += PPP_HDRLEN;
m0->m_len -= PPP_HDRLEN;
return sc->sc_if.if_output(&sc->sc_if, m0, &dst, NULL);
}
/*
* Line specific (tty) ioctl routine.
* This discipline requires that tty device drivers call
* the line specific l_ioctl routine from their ioctl routines.
*/
int
ppptioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
int error, s;
if (sc == NULL || tp != (struct tty *) sc->sc_devp)
return -1;
error = 0;
switch (cmd) {
case PPPIOCSASYNCMAP:
if ((error = suser(p)) != 0)
break;
sc->sc_asyncmap[0] = *(u_int *)data;
break;
case PPPIOCGASYNCMAP:
*(u_int *)data = sc->sc_asyncmap[0];
break;
case PPPIOCSRASYNCMAP:
if ((error = suser(p)) != 0)
break;
sc->sc_rasyncmap = *(u_int *)data;
break;
case PPPIOCGRASYNCMAP:
*(u_int *)data = sc->sc_rasyncmap;
break;
case PPPIOCSXASYNCMAP:
if ((error = suser(p)) != 0)
break;
s = spltty();
bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */
sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */
sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */
splx(s);
break;
case PPPIOCGXASYNCMAP:
bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap));
break;
default:
NET_LOCK();
error = pppioctl(sc, cmd, data, flag, p);
NET_UNLOCK();
if (error == 0 && cmd == PPPIOCSMRU)
ppppkt(sc);
}
return error;
}
/*
* FCS lookup table as calculated by genfcstab.
*/
static u_int16_t fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
/*
* Calculate a new FCS given the current FCS and the new data.
*/
u_int16_t
pppfcs(u_int16_t fcs, u_char *cp, int len)
{
while (len--)
fcs = PPP_FCS(fcs, *cp++);
return (fcs);
}
/*
* This gets called from pppoutput when a new packet is
* put on a queue.
*/
void
pppasyncstart(struct ppp_softc *sc)
{
struct tty *tp = (struct tty *) sc->sc_devp;
struct mbuf *m;
int len;
u_char *start, *stop, *cp;
int n, ndone, done, idle;
struct mbuf *m2;
int s;
KERNEL_LOCK();
idle = 0;
while (CCOUNT(&tp->t_outq) < tp->t_hiwat) {
/*
* See if we have an existing packet partly sent.
* If not, get a new packet and start sending it.
*/
m = sc->sc_outm;
if (m == NULL) {
/*
* Get another packet to be sent.
*/
m = ppp_dequeue(sc);
if (m == NULL) {
idle = 1;
break;
}
/*
* The extra PPP_FLAG will start up a new packet, and thus
* will flush any accumulated garbage. We do this whenever
* the line may have been idle for some time.
*/
if (CCOUNT(&tp->t_outq) == 0) {
++sc->sc_stats.ppp_obytes;
(void) putc(PPP_FLAG, &tp->t_outq);
}
/* Calculate the FCS for the first mbuf's worth. */
sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len);
}
for (;;) {
start = mtod(m, u_char *);
len = m->m_len;
stop = start + len;
while (len > 0) {
/*
* Find out how many bytes in the string we can
* handle without doing something special.
*/
for (cp = start; cp < stop; cp++)
if (ESCAPE_P(*cp))
break;
n = cp - start;
if (n) {
/* NetBSD (0.9 or later), 4.3-Reno or similar. */
ndone = n - b_to_q(start, n, &tp->t_outq);
len -= ndone;
start += ndone;
sc->sc_stats.ppp_obytes += ndone;
if (ndone < n)
break; /* packet doesn't fit */
}
/*
* If there are characters left in the mbuf,
* the first one must be special.
* Put it out in a different form.
*/
if (len) {
s = spltty();
if (putc(PPP_ESCAPE, &tp->t_outq)) {
splx(s);
break;
}
if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
(void) unputc(&tp->t_outq);
splx(s);
break;
}
splx(s);
sc->sc_stats.ppp_obytes += 2;
start++;
len--;
}
}
/*
* If we didn't empty this mbuf, remember where we're up to.
* If we emptied the last mbuf, try to add the FCS and closing
* flag, and if we can't, leave sc_outm pointing to m, but with
* m->m_len == 0, to remind us to output the FCS and flag later.
*/
done = len == 0;
if (done && m->m_next == NULL) {
u_char *p, *q;
int c;
u_char endseq[8];
/*
* We may have to escape the bytes in the FCS.
*/
p = endseq;
c = ~sc->sc_outfcs & 0xFF;
if (ESCAPE_P(c)) {
*p++ = PPP_ESCAPE;
*p++ = c ^ PPP_TRANS;
} else
*p++ = c;
c = (~sc->sc_outfcs >> 8) & 0xFF;
if (ESCAPE_P(c)) {
*p++ = PPP_ESCAPE;
*p++ = c ^ PPP_TRANS;
} else
*p++ = c;
*p++ = PPP_FLAG;
/*
* Try to output the FCS and flag. If the bytes
* don't all fit, back out.
*/
s = spltty();
for (q = endseq; q < p; ++q)
if (putc(*q, &tp->t_outq)) {
done = 0;
for (; q > endseq; --q)
unputc(&tp->t_outq);
break;
}
splx(s);
if (done)
sc->sc_stats.ppp_obytes += q - endseq;
}
if (!done) {
/* remember where we got to */
m->m_data = start;
m->m_len = len;
break;
}
/* Finished with this mbuf; free it and move on. */
m2 = m_free(m);
m = m2;
if (m == NULL) {
/* Finished a packet */
break;
}
sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len);
}
/*
* If m == NULL, we have finished a packet.
* If m != NULL, we've either done as much work this time
* as we need to, or else we've filled up the output queue.
*/
sc->sc_outm = m;
if (m)
break;
}
/* Call pppstart to start output again if necessary. */
s = spltty();
pppstart_internal(tp, 0);
/*
* This timeout is needed for operation on a pseudo-tty,
* because the pty code doesn't call pppstart after it has
* drained the t_outq.
*/
if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) {
timeout_add(&sc->sc_timo, 1);
sc->sc_flags |= SC_TIMEOUT;
}
splx(s);
KERNEL_UNLOCK();
}
/*
* This gets called when a received packet is placed on
* the inq.
*/
void
pppasyncctlp(struct ppp_softc *sc)
{
struct tty *tp;
int s;
KERNEL_LOCK();
/* Put a placeholder byte in canq for ttpoll()/ttnread(). */
s = spltty();
tp = (struct tty *) sc->sc_devp;
putc(0, &tp->t_canq);
ttwakeup(tp);
splx(s);
KERNEL_UNLOCK();
}
/*
* Start output on async tty interface. If the transmit queue
* has drained sufficiently, arrange for pppasyncstart to be
* called later.
*/
int
pppstart_internal(struct tty *tp, int force)
{
struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
/*
* If there is stuff in the output queue, send it now.
* We are being called in lieu of ttstart and must do what it would.
*/
if (tp->t_oproc != NULL)
(*tp->t_oproc)(tp);
/*
* If the transmit queue has drained and the tty has not hung up
* or been disconnected from the ppp unit, then tell if_ppp.c that
* we need more output.
*/
if ((CCOUNT(&tp->t_outq) < tp->t_lowat || force)
&& !((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
&& sc != NULL && tp == (struct tty *) sc->sc_devp) {
ppp_restart(sc);
}
return 0;
}
int
pppstart(struct tty *tp)
{
return pppstart_internal(tp, 0);
}
/*
* Timeout routine - try to start some more output.
*/
void
ppp_timeout(void *x)
{
struct ppp_softc *sc = (struct ppp_softc *) x;
struct tty *tp = (struct tty *) sc->sc_devp;
int s;
s = spltty();
sc->sc_flags &= ~SC_TIMEOUT;
pppstart_internal(tp, 1);
splx(s);
}
/*
* Allocate enough mbuf to handle current MRU.
*/
void
ppppkt(struct ppp_softc *sc)
{
struct ppp_pkt **pktp, *pkt;
int len;
int s;
s = spltty();
pktp = &sc->sc_pkt;
for (len = PKT_MAXLEN(sc); len > 0; len -= sizeof(pkt->p_buf)) {
pkt = *pktp;
if (pkt == NULL) {
pkt = pool_get(&ppp_pkts, PR_NOWAIT);
if (pkt == NULL)
break;
PKT_NEXT(pkt) = NULL;
PKT_PREV(pkt) = *pktp;
PKT_LEN(pkt) = 0;
*pktp = pkt;
}
pktp = &PKT_NEXT(pkt);
}
splx(s);
}
void
ppp_pkt_free(struct ppp_pkt *pkt)
{
struct ppp_pkt *next;
while (pkt != NULL) {
next = PKT_NEXT(pkt);
pool_put(&ppp_pkts, pkt);
pkt = next;
}
}
/*
* tty interface receiver interrupt.
*/
static unsigned int paritytab[8] = {
0x96696996, 0x69969669, 0x69969669, 0x96696996,
0x69969669, 0x96696996, 0x96696996, 0x69969669
};
int
pppinput(int c, struct tty *tp)
{
struct ppp_softc *sc;
struct ppp_pkt *pkt;
int ilen, s;
sc = (struct ppp_softc *) tp->t_sc;
if (sc == NULL || tp != (struct tty *) sc->sc_devp)
return 0;
++tk_nin;
++sc->sc_stats.ppp_ibytes;
if (c & TTY_FE) {
/* framing error or overrun on this char - abort packet */
if (sc->sc_flags & SC_DEBUG)
printf("%s: bad char %x\n", sc->sc_if.if_xname, c);
goto flush;
}
c &= 0xff;
/*
* Handle software flow control of output.
*/
if (tp->t_iflag & IXON) {
if (c == tp->t_cc[VSTOP] && tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
if ((tp->t_state & TS_TTSTOP) == 0) {
tp->t_state |= TS_TTSTOP;
(*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
}
return 0;
}
if (c == tp->t_cc[VSTART] && tp->t_cc[VSTART] != _POSIX_VDISABLE) {
tp->t_state &= ~TS_TTSTOP;
if (tp->t_oproc != NULL)
(*tp->t_oproc)(tp);
return 0;
}
}
s = spltty();
if (c & 0x80)
sc->sc_flags |= SC_RCV_B7_1;
else
sc->sc_flags |= SC_RCV_B7_0;
if (paritytab[c >> 5] & (1 << (c & 0x1F)))
sc->sc_flags |= SC_RCV_ODDP;
else
sc->sc_flags |= SC_RCV_EVNP;
splx(s);
if (sc->sc_flags & SC_LOG_RAWIN)
ppplogchar(sc, c);
if (c == PPP_FLAG) {
ilen = sc->sc_ilen;
sc->sc_ilen = 0;
if (sc->sc_rawin_count > 0)
ppplogchar(sc, -1);
/*
* If SC_ESCAPED is set, then we've seen the packet
* abort sequence "}~".
*/
if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
|| (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) {
s = spltty();
sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */
if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){
if (sc->sc_flags & SC_DEBUG)
printf("%s: bad fcs %x\n", sc->sc_if.if_xname,
sc->sc_fcs);
sc->sc_if.if_ierrors++;
sc->sc_stats.ppp_ierrors++;
} else
sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
splx(s);
return 0;
}
if (ilen < PPP_HDRLEN + PPP_FCSLEN) {
if (ilen) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: too short (%d)\n", sc->sc_if.if_xname, ilen);
s = spltty();
sc->sc_if.if_ierrors++;
sc->sc_stats.ppp_ierrors++;
sc->sc_flags |= SC_PKTLOST;
splx(s);
}
return 0;
}
/*
* Remove FCS trailer.
*/
ilen -= 2;
pkt = sc->sc_pktc;
if (--PKT_LEN(pkt) == 0) {
pkt = PKT_PREV(pkt);
sc->sc_pktc = pkt;
}
PKT_LEN(pkt)--;
/* excise this mbuf chain */
pkt = sc->sc_pkt;
sc->sc_pkt = sc->sc_pktc = PKT_NEXT(sc->sc_pktc);
PKT_NEXT(pkt) = NULL;
ppppktin(sc, pkt, sc->sc_flags & SC_PKTLOST);
if (sc->sc_flags & SC_PKTLOST) {
s = spltty();
sc->sc_flags &= ~SC_PKTLOST;
splx(s);
}
ppppkt(sc);
return 0;
}
if (sc->sc_flags & SC_FLUSH) {
if (sc->sc_flags & SC_LOG_FLUSH)
ppplogchar(sc, c);
return 0;
}
if (c < 0x20 && (sc->sc_rasyncmap & (1 << c)))
return 0;
s = spltty();
if (sc->sc_flags & SC_ESCAPED) {
sc->sc_flags &= ~SC_ESCAPED;
c ^= PPP_TRANS;
} else if (c == PPP_ESCAPE) {
sc->sc_flags |= SC_ESCAPED;
splx(s);
return 0;
}
splx(s);
/*
* Initialize buffer on first octet received.
* First octet could be address or protocol (when compressing
* address/control).
* Second octet is control.
* Third octet is first or second (when compressing protocol)
* octet of protocol.
* Fourth octet is second octet of protocol.
*/
if (sc->sc_ilen == 0) {
/* reset the first input mbuf */
if (sc->sc_pkt == NULL) {
ppppkt(sc);
if (sc->sc_pkt == NULL) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: no input mbufs!\n", sc->sc_if.if_xname);
goto flush;
}
}
pkt = sc->sc_pkt;
PKT_LEN(pkt) = 0;
sc->sc_pktc = pkt;
sc->sc_pktp = pkt->p_buf;
sc->sc_fcs = PPP_INITFCS;
if (c != PPP_ALLSTATIONS) {
if (sc->sc_flags & SC_REJ_COMP_AC) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: garbage received: 0x%x (need 0xFF)\n",
sc->sc_if.if_xname, c);
goto flush;
}
*sc->sc_pktp++ = PPP_ALLSTATIONS;
*sc->sc_pktp++ = PPP_UI;
sc->sc_ilen += 2;
PKT_LEN(pkt) += 2;
}
}
if (sc->sc_ilen == 1 && c != PPP_UI) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: missing UI (0x3), got 0x%x\n",
sc->sc_if.if_xname, c);
goto flush;
}
if (sc->sc_ilen == 2 && (c & 1) == 1) {
/* a compressed protocol */
*sc->sc_pktp++ = 0;
sc->sc_ilen++;
PKT_LEN(sc->sc_pktc)++;
}
if (sc->sc_ilen == 3 && (c & 1) == 0) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: bad protocol %x\n", sc->sc_if.if_xname,
(sc->sc_pktp[-1] << 8) + c);
goto flush;
}
/* packet beyond configured mru? */
if (++sc->sc_ilen > PKT_MAXLEN(sc)) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: packet too big\n", sc->sc_if.if_xname);
goto flush;
}
/* is this packet full? */
pkt = sc->sc_pktc;
if (PKT_LEN(pkt) >= sizeof(pkt->p_buf)) {
if (PKT_NEXT(pkt) == NULL) {
ppppkt(sc);
if (PKT_NEXT(pkt) == NULL) {
if (sc->sc_flags & SC_DEBUG)
printf("%s: too few input packets!\n", sc->sc_if.if_xname);
goto flush;
}
}
sc->sc_pktc = pkt = PKT_NEXT(pkt);
PKT_LEN(pkt) = 0;
sc->sc_pktp = pkt->p_buf;
}
++PKT_LEN(pkt);
*sc->sc_pktp++ = c;
sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
return 0;
flush:
if (!(sc->sc_flags & SC_FLUSH)) {
s = spltty();
sc->sc_if.if_ierrors++;
sc->sc_stats.ppp_ierrors++;
sc->sc_flags |= SC_FLUSH;
splx(s);
if (sc->sc_flags & SC_LOG_FLUSH)
ppplogchar(sc, c);
}
return 0;
}
#define MAX_DUMP_BYTES 128
void
ppplogchar(struct ppp_softc *sc, int c)
{
if (c >= 0)
sc->sc_rawin[sc->sc_rawin_count++] = c;
if (sc->sc_rawin_count >= sizeof(sc->sc_rawin)
|| (c < 0 && sc->sc_rawin_count > 0)) {
printf("%s input: ", sc->sc_if.if_xname);
pppdumpb(sc->sc_rawin, sc->sc_rawin_count);
sc->sc_rawin_count = 0;
}
}
void
pppdumpb(u_char *b, int l)
{
char buf[3*MAX_DUMP_BYTES+4];
char *bp = buf;
static char digits[] = "0123456789abcdef";
while (l--) {
if (bp >= buf + sizeof(buf) - 3) {
*bp++ = '>';
break;
}
*bp++ = digits[*b >> 4]; /* convert byte to ascii hex */
*bp++ = digits[*b++ & 0xf];
*bp++ = ' ';
}
*bp = 0;
printf("%s\n", buf);
}
#endif /* NPPP > 0 */
1294
1295
1295
1201
620
1809
1809
1803
1811
1044
1460
1809
1807
1804
1809
7
1802
1291
11
10
5
5
5
10
10
6
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
/* $OpenBSD: kern_sched.c,v 1.75 2022/08/14 01:58:27 jsg Exp $ */
/*
* Copyright (c) 2007, 2008 Artur Grabowski <art@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/sched.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/systm.h>
#include <sys/task.h>
#include <sys/smr.h>
#include <sys/tracepoint.h>
#include <uvm/uvm_extern.h>
void sched_kthreads_create(void *);
int sched_proc_to_cpu_cost(struct cpu_info *ci, struct proc *p);
struct proc *sched_steal_proc(struct cpu_info *);
/*
* To help choosing which cpu should run which process we keep track
* of cpus which are currently idle and which cpus have processes
* queued.
*/
struct cpuset sched_idle_cpus;
struct cpuset sched_queued_cpus;
struct cpuset sched_all_cpus;
/*
* Some general scheduler counters.
*/
uint64_t sched_nmigrations; /* Cpu migration counter */
uint64_t sched_nomigrations; /* Cpu no migration counter */
uint64_t sched_noidle; /* Times we didn't pick the idle task */
uint64_t sched_stolen; /* Times we stole proc from other cpus */
uint64_t sched_choose; /* Times we chose a cpu */
uint64_t sched_wasidle; /* Times we came out of idle */
int sched_smt;
/*
* A few notes about cpu_switchto that is implemented in MD code.
*
* cpu_switchto takes two arguments, the old proc and the proc
* it should switch to. The new proc will never be NULL, so we always have
* a saved state that we need to switch to. The old proc however can
* be NULL if the process is exiting. NULL for the old proc simply
* means "don't bother saving old state".
*
* cpu_switchto is supposed to atomically load the new state of the process
* including the pcb, pmap and setting curproc, the p_cpu pointer in the
* proc and p_stat to SONPROC. Atomically with respect to interrupts, other
* cpus in the system must not depend on this state being consistent.
* Therefore no locking is necessary in cpu_switchto other than blocking
* interrupts during the context switch.
*/
/*
* sched_init_cpu is called from main() for the boot cpu, then it's the
* responsibility of the MD code to call it for all other cpus.
*/
void
sched_init_cpu(struct cpu_info *ci)
{
struct schedstate_percpu *spc = &ci->ci_schedstate;
int i;
for (i = 0; i < SCHED_NQS; i++)
TAILQ_INIT(&spc->spc_qs[i]);
spc->spc_idleproc = NULL;
kthread_create_deferred(sched_kthreads_create, ci);
LIST_INIT(&spc->spc_deadproc);
SIMPLEQ_INIT(&spc->spc_deferred);
/*
* Slight hack here until the cpuset code handles cpu_info
* structures.
*/
cpuset_init_cpu(ci);
#ifdef __HAVE_CPU_TOPOLOGY
if (!sched_smt && ci->ci_smt_id > 0)
return;
#endif
cpuset_add(&sched_all_cpus, ci);
}
void
sched_kthreads_create(void *v)
{
struct cpu_info *ci = v;
struct schedstate_percpu *spc = &ci->ci_schedstate;
static int num;
if (fork1(&proc0, FORK_SHAREVM|FORK_SHAREFILES|FORK_NOZOMBIE|
FORK_SYSTEM|FORK_IDLE, sched_idle, ci, NULL,
&spc->spc_idleproc))
panic("fork idle");
/* Name it as specified. */
snprintf(spc->spc_idleproc->p_p->ps_comm,
sizeof(spc->spc_idleproc->p_p->ps_comm),
"idle%d", num);
num++;
}
void
sched_idle(void *v)
{
struct schedstate_percpu *spc;
struct proc *p = curproc;
struct cpu_info *ci = v;
int s;
KERNEL_UNLOCK();
spc = &ci->ci_schedstate;
/*
* First time we enter here, we're not supposed to idle,
* just go away for a while.
*/
SCHED_LOCK(s);
cpuset_add(&sched_idle_cpus, ci);
p->p_stat = SSLEEP;
p->p_cpu = ci;
atomic_setbits_int(&p->p_flag, P_CPUPEG);
mi_switch();
cpuset_del(&sched_idle_cpus, ci);
SCHED_UNLOCK(s);
KASSERT(ci == curcpu());
KASSERT(curproc == spc->spc_idleproc);
while (1) {
while (!cpu_is_idle(curcpu())) {
struct proc *dead;
SCHED_LOCK(s);
p->p_stat = SSLEEP;
mi_switch();
SCHED_UNLOCK(s);
while ((dead = LIST_FIRST(&spc->spc_deadproc))) {
LIST_REMOVE(dead, p_hash);
exit2(dead);
}
}
splassert(IPL_NONE);
smr_idle();
cpuset_add(&sched_idle_cpus, ci);
cpu_idle_enter();
while (spc->spc_whichqs == 0) {
#ifdef MULTIPROCESSOR
if (spc->spc_schedflags & SPCF_SHOULDHALT &&
(spc->spc_schedflags & SPCF_HALTED) == 0) {
cpuset_del(&sched_idle_cpus, ci);
SCHED_LOCK(s);
atomic_setbits_int(&spc->spc_schedflags,
spc->spc_whichqs ? 0 : SPCF_HALTED);
SCHED_UNLOCK(s);
wakeup(spc);
}
#endif
cpu_idle_cycle();
}
cpu_idle_leave();
cpuset_del(&sched_idle_cpus, ci);
}
}
/*
* To free our address space we have to jump through a few hoops.
* The freeing is done by the reaper, but until we have one reaper
* per cpu, we have no way of putting this proc on the deadproc list
* and waking up the reaper without risking having our address space and
* stack torn from under us before we manage to switch to another proc.
* Therefore we have a per-cpu list of dead processes where we put this
* proc and have idle clean up that list and move it to the reaper list.
* All this will be unnecessary once we can bind the reaper this cpu
* and not risk having it switch to another in case it sleeps.
*/
void
sched_exit(struct proc *p)
{
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
struct timespec ts;
struct proc *idle;
int s;
nanouptime(&ts);
timespecsub(&ts, &spc->spc_runtime, &ts);
timespecadd(&p->p_rtime, &ts, &p->p_rtime);
LIST_INSERT_HEAD(&spc->spc_deadproc, p, p_hash);
#ifdef MULTIPROCESSOR
/* This process no longer needs to hold the kernel lock. */
KERNEL_ASSERT_LOCKED();
__mp_release_all(&kernel_lock);
#endif
SCHED_LOCK(s);
idle = spc->spc_idleproc;
idle->p_stat = SRUN;
cpu_switchto(NULL, idle);
panic("cpu_switchto returned");
}
/*
* Run queue management.
*/
void
sched_init_runqueues(void)
{
}
void
setrunqueue(struct cpu_info *ci, struct proc *p, uint8_t prio)
{
struct schedstate_percpu *spc;
int queue = prio >> 2;
if (ci == NULL)
ci = sched_choosecpu(p);
KASSERT(ci != NULL);
SCHED_ASSERT_LOCKED();
p->p_cpu = ci;
p->p_stat = SRUN;
p->p_runpri = prio;
spc = &p->p_cpu->ci_schedstate;
spc->spc_nrun++;
TRACEPOINT(sched, enqueue, p->p_tid + THREAD_PID_OFFSET,
p->p_p->ps_pid);
TAILQ_INSERT_TAIL(&spc->spc_qs[queue], p, p_runq);
spc->spc_whichqs |= (1U << queue);
cpuset_add(&sched_queued_cpus, p->p_cpu);
if (cpuset_isset(&sched_idle_cpus, p->p_cpu))
cpu_unidle(p->p_cpu);
if (prio < spc->spc_curpriority)
need_resched(ci);
}
void
remrunqueue(struct proc *p)
{
struct schedstate_percpu *spc;
int queue = p->p_runpri >> 2;
SCHED_ASSERT_LOCKED();
spc = &p->p_cpu->ci_schedstate;
spc->spc_nrun--;
TRACEPOINT(sched, dequeue, p->p_tid + THREAD_PID_OFFSET,
p->p_p->ps_pid);
TAILQ_REMOVE(&spc->spc_qs[queue], p, p_runq);
if (TAILQ_EMPTY(&spc->spc_qs[queue])) {
spc->spc_whichqs &= ~(1U << queue);
if (spc->spc_whichqs == 0)
cpuset_del(&sched_queued_cpus, p->p_cpu);
}
}
struct proc *
sched_chooseproc(void)
{
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
struct proc *p;
int queue;
SCHED_ASSERT_LOCKED();
#ifdef MULTIPROCESSOR
if (spc->spc_schedflags & SPCF_SHOULDHALT) {
if (spc->spc_whichqs) {
for (queue = 0; queue < SCHED_NQS; queue++) {
while ((p = TAILQ_FIRST(&spc->spc_qs[queue]))) {
remrunqueue(p);
setrunqueue(NULL, p, p->p_runpri);
if (p->p_cpu == curcpu()) {
KASSERT(p->p_flag & P_CPUPEG);
goto again;
}
}
}
}
p = spc->spc_idleproc;
KASSERT(p);
KASSERT(p->p_wchan == NULL);
p->p_stat = SRUN;
return (p);
}
#endif
again:
if (spc->spc_whichqs) {
queue = ffs(spc->spc_whichqs) - 1;
p = TAILQ_FIRST(&spc->spc_qs[queue]);
remrunqueue(p);
sched_noidle++;
if (p->p_stat != SRUN)
panic("thread %d not in SRUN: %d", p->p_tid, p->p_stat);
} else if ((p = sched_steal_proc(curcpu())) == NULL) {
p = spc->spc_idleproc;
if (p == NULL) {
int s;
/*
* We get here if someone decides to switch during
* boot before forking kthreads, bleh.
* This is kind of like a stupid idle loop.
*/
#ifdef MULTIPROCESSOR
__mp_unlock(&sched_lock);
#endif
spl0();
delay(10);
SCHED_LOCK(s);
goto again;
}
KASSERT(p);
p->p_stat = SRUN;
}
KASSERT(p->p_wchan == NULL);
return (p);
}
struct cpu_info *
sched_choosecpu_fork(struct proc *parent, int flags)
{
#ifdef MULTIPROCESSOR
struct cpu_info *choice = NULL;
fixpt_t load, best_load = ~0;
int run, best_run = INT_MAX;
struct cpu_info *ci;
struct cpuset set;
#if 0
/*
* XXX
* Don't do this until we have a painless way to move the cpu in exec.
* Preferably when nuking the old pmap and getting a new one on a
* new cpu.
*/
/*
* PPWAIT forks are simple. We know that the parent will not
* run until we exec and choose another cpu, so we just steal its
* cpu.
*/
if (flags & FORK_PPWAIT)
return (parent->p_cpu);
#endif
/*
* Look at all cpus that are currently idle and have nothing queued.
* If there are none, pick the one with least queued procs first,
* then the one with lowest load average.
*/
cpuset_complement(&set, &sched_queued_cpus, &sched_idle_cpus);
cpuset_intersection(&set, &set, &sched_all_cpus);
if (cpuset_first(&set) == NULL)
cpuset_copy(&set, &sched_all_cpus);
while ((ci = cpuset_first(&set)) != NULL) {
cpuset_del(&set, ci);
load = ci->ci_schedstate.spc_ldavg;
run = ci->ci_schedstate.spc_nrun;
if (choice == NULL || run < best_run ||
(run == best_run &&load < best_load)) {
choice = ci;
best_load = load;
best_run = run;
}
}
return (choice);
#else
return (curcpu());
#endif
}
struct cpu_info *
sched_choosecpu(struct proc *p)
{
#ifdef MULTIPROCESSOR
struct cpu_info *choice = NULL;
int last_cost = INT_MAX;
struct cpu_info *ci;
struct cpuset set;
/*
* If pegged to a cpu, don't allow it to move.
*/
if (p->p_flag & P_CPUPEG)
return (p->p_cpu);
sched_choose++;
/*
* Look at all cpus that are currently idle and have nothing queued.
* If there are none, pick the cheapest of those.
* (idle + queued could mean that the cpu is handling an interrupt
* at this moment and haven't had time to leave idle yet).
*/
cpuset_complement(&set, &sched_queued_cpus, &sched_idle_cpus);
cpuset_intersection(&set, &set, &sched_all_cpus);
/*
* First, just check if our current cpu is in that set, if it is,
* this is simple.
* Also, our cpu might not be idle, but if it's the current cpu
* and it has nothing else queued and we're curproc, take it.
*/
if (cpuset_isset(&set, p->p_cpu) ||
(p->p_cpu == curcpu() && p->p_cpu->ci_schedstate.spc_nrun == 0 &&
(p->p_cpu->ci_schedstate.spc_schedflags & SPCF_SHOULDHALT) == 0 &&
curproc == p)) {
sched_wasidle++;
return (p->p_cpu);
}
if (cpuset_first(&set) == NULL)
cpuset_copy(&set, &sched_all_cpus);
while ((ci = cpuset_first(&set)) != NULL) {
int cost = sched_proc_to_cpu_cost(ci, p);
if (choice == NULL || cost < last_cost) {
choice = ci;
last_cost = cost;
}
cpuset_del(&set, ci);
}
if (p->p_cpu != choice)
sched_nmigrations++;
else
sched_nomigrations++;
return (choice);
#else
return (curcpu());
#endif
}
/*
* Attempt to steal a proc from some cpu.
*/
struct proc *
sched_steal_proc(struct cpu_info *self)
{
struct proc *best = NULL;
#ifdef MULTIPROCESSOR
struct schedstate_percpu *spc;
int bestcost = INT_MAX;
struct cpu_info *ci;
struct cpuset set;
KASSERT((self->ci_schedstate.spc_schedflags & SPCF_SHOULDHALT) == 0);
/* Don't steal if we don't want to schedule processes in this CPU. */
if (!cpuset_isset(&sched_all_cpus, self))
return (NULL);
cpuset_copy(&set, &sched_queued_cpus);
while ((ci = cpuset_first(&set)) != NULL) {
struct proc *p;
int queue;
int cost;
cpuset_del(&set, ci);
spc = &ci->ci_schedstate;
queue = ffs(spc->spc_whichqs) - 1;
TAILQ_FOREACH(p, &spc->spc_qs[queue], p_runq) {
if (p->p_flag & P_CPUPEG)
continue;
cost = sched_proc_to_cpu_cost(self, p);
if (best == NULL || cost < bestcost) {
best = p;
bestcost = cost;
}
}
}
if (best == NULL)
return (NULL);
remrunqueue(best);
best->p_cpu = self;
sched_stolen++;
#endif
return (best);
}
#ifdef MULTIPROCESSOR
/*
* Base 2 logarithm of an int. returns 0 for 0 (yeye, I know).
*/
static int
log2(unsigned int i)
{
int ret = 0;
while (i >>= 1)
ret++;
return (ret);
}
/*
* Calculate the cost of moving the proc to this cpu.
*
* What we want is some guesstimate of how much "performance" it will
* cost us to move the proc here. Not just for caches and TLBs and NUMA
* memory, but also for the proc itself. A highly loaded cpu might not
* be the best candidate for this proc since it won't get run.
*
* Just total guesstimates for now.
*/
int sched_cost_load = 1;
int sched_cost_priority = 1;
int sched_cost_runnable = 3;
int sched_cost_resident = 1;
#endif
int
sched_proc_to_cpu_cost(struct cpu_info *ci, struct proc *p)
{
int cost = 0;
#ifdef MULTIPROCESSOR
struct schedstate_percpu *spc;
int l2resident = 0;
spc = &ci->ci_schedstate;
/*
* First, account for the priority of the proc we want to move.
* More willing to move, the lower the priority of the destination
* and the higher the priority of the proc.
*/
if (!cpuset_isset(&sched_idle_cpus, ci)) {
cost += (p->p_usrpri - spc->spc_curpriority) *
sched_cost_priority;
cost += sched_cost_runnable;
}
if (cpuset_isset(&sched_queued_cpus, ci))
cost += spc->spc_nrun * sched_cost_runnable;
/*
* Try to avoid the primary cpu as it handles hardware interrupts.
*
* XXX Needs to be revisited when we distribute interrupts
* over cpus.
*/
if (CPU_IS_PRIMARY(ci))
cost += sched_cost_runnable;
/*
* Higher load on the destination means we don't want to go there.
*/
cost += ((sched_cost_load * spc->spc_ldavg) >> FSHIFT);
/*
* If the proc is on this cpu already, lower the cost by how much
* it has been running and an estimate of its footprint.
*/
if (p->p_cpu == ci && p->p_slptime == 0) {
l2resident =
log2(pmap_resident_count(p->p_vmspace->vm_map.pmap));
cost -= l2resident * sched_cost_resident;
}
#endif
return (cost);
}
/*
* Peg a proc to a cpu.
*/
void
sched_peg_curproc(struct cpu_info *ci)
{
struct proc *p = curproc;
int s;
SCHED_LOCK(s);
atomic_setbits_int(&p->p_flag, P_CPUPEG);
setrunqueue(ci, p, p->p_usrpri);
p->p_ru.ru_nvcsw++;
mi_switch();
SCHED_UNLOCK(s);
}
#ifdef MULTIPROCESSOR
void
sched_start_secondary_cpus(void)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
CPU_INFO_FOREACH(cii, ci) {
struct schedstate_percpu *spc = &ci->ci_schedstate;
if (CPU_IS_PRIMARY(ci) || !CPU_IS_RUNNING(ci))
continue;
atomic_clearbits_int(&spc->spc_schedflags,
SPCF_SHOULDHALT | SPCF_HALTED);
#ifdef __HAVE_CPU_TOPOLOGY
if (!sched_smt && ci->ci_smt_id > 0)
continue;
#endif
cpuset_add(&sched_all_cpus, ci);
}
}
void
sched_stop_secondary_cpus(void)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
/*
* Make sure we stop the secondary CPUs.
*/
CPU_INFO_FOREACH(cii, ci) {
struct schedstate_percpu *spc = &ci->ci_schedstate;
if (CPU_IS_PRIMARY(ci) || !CPU_IS_RUNNING(ci))
continue;
cpuset_del(&sched_all_cpus, ci);
atomic_setbits_int(&spc->spc_schedflags, SPCF_SHOULDHALT);
}
CPU_INFO_FOREACH(cii, ci) {
struct schedstate_percpu *spc = &ci->ci_schedstate;
struct sleep_state sls;
if (CPU_IS_PRIMARY(ci) || !CPU_IS_RUNNING(ci))
continue;
while ((spc->spc_schedflags & SPCF_HALTED) == 0) {
sleep_setup(&sls, spc, PZERO, "schedstate", 0);
sleep_finish(&sls,
(spc->spc_schedflags & SPCF_HALTED) == 0);
}
}
}
struct sched_barrier_state {
struct cpu_info *ci;
struct cond cond;
};
void
sched_barrier_task(void *arg)
{
struct sched_barrier_state *sb = arg;
struct cpu_info *ci = sb->ci;
sched_peg_curproc(ci);
cond_signal(&sb->cond);
atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
}
void
sched_barrier(struct cpu_info *ci)
{
struct sched_barrier_state sb;
struct task task;
CPU_INFO_ITERATOR cii;
if (ci == NULL) {
CPU_INFO_FOREACH(cii, ci) {
if (CPU_IS_PRIMARY(ci))
break;
}
}
KASSERT(ci != NULL);
if (ci == curcpu())
return;
sb.ci = ci;
cond_init(&sb.cond);
task_set(&task, sched_barrier_task, &sb);
task_add(systqmp, &task);
cond_wait(&sb.cond, "sbar");
}
#else
void
sched_barrier(struct cpu_info *ci)
{
}
#endif
/*
* Functions to manipulate cpu sets.
*/
struct cpu_info *cpuset_infos[MAXCPUS];
static struct cpuset cpuset_all;
void
cpuset_init_cpu(struct cpu_info *ci)
{
cpuset_add(&cpuset_all, ci);
cpuset_infos[CPU_INFO_UNIT(ci)] = ci;
}
void
cpuset_clear(struct cpuset *cs)
{
memset(cs, 0, sizeof(*cs));
}
void
cpuset_add(struct cpuset *cs, struct cpu_info *ci)
{
unsigned int num = CPU_INFO_UNIT(ci);
atomic_setbits_int(&cs->cs_set[num/32], (1U << (num % 32)));
}
void
cpuset_del(struct cpuset *cs, struct cpu_info *ci)
{
unsigned int num = CPU_INFO_UNIT(ci);
atomic_clearbits_int(&cs->cs_set[num/32], (1U << (num % 32)));
}
int
cpuset_isset(struct cpuset *cs, struct cpu_info *ci)
{
unsigned int num = CPU_INFO_UNIT(ci);
return (cs->cs_set[num/32] & (1U << (num % 32)));
}
void
cpuset_add_all(struct cpuset *cs)
{
cpuset_copy(cs, &cpuset_all);
}
void
cpuset_copy(struct cpuset *to, struct cpuset *from)
{
memcpy(to, from, sizeof(*to));
}
struct cpu_info *
cpuset_first(struct cpuset *cs)
{
int i;
for (i = 0; i < CPUSET_ASIZE(ncpus); i++)
if (cs->cs_set[i])
return (cpuset_infos[i * 32 + ffs(cs->cs_set[i]) - 1]);
return (NULL);
}
void
cpuset_union(struct cpuset *to, struct cpuset *a, struct cpuset *b)
{
int i;
for (i = 0; i < CPUSET_ASIZE(ncpus); i++)
to->cs_set[i] = a->cs_set[i] | b->cs_set[i];
}
void
cpuset_intersection(struct cpuset *to, struct cpuset *a, struct cpuset *b)
{
int i;
for (i = 0; i < CPUSET_ASIZE(ncpus); i++)
to->cs_set[i] = a->cs_set[i] & b->cs_set[i];
}
void
cpuset_complement(struct cpuset *to, struct cpuset *a, struct cpuset *b)
{
int i;
for (i = 0; i < CPUSET_ASIZE(ncpus); i++)
to->cs_set[i] = b->cs_set[i] & ~a->cs_set[i];
}
int
cpuset_cardinality(struct cpuset *cs)
{
int cardinality, i, n;
cardinality = 0;
for (i = 0; i < CPUSET_ASIZE(ncpus); i++)
for (n = cs->cs_set[i]; n != 0; n &= n - 1)
cardinality++;
return (cardinality);
}
int
sysctl_hwncpuonline(void)
{
return cpuset_cardinality(&sched_all_cpus);
}
int
cpu_is_online(struct cpu_info *ci)
{
return cpuset_isset(&sched_all_cpus, ci);
}
#ifdef __HAVE_CPU_TOPOLOGY
#include <sys/sysctl.h>
int
sysctl_hwsmt(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
int err, newsmt;
newsmt = sched_smt;
err = sysctl_int_bounded(oldp, oldlenp, newp, newlen, &newsmt, 0, 1);
if (err)
return err;
if (newsmt == sched_smt)
return 0;
sched_smt = newsmt;
CPU_INFO_FOREACH(cii, ci) {
if (CPU_IS_PRIMARY(ci) || !CPU_IS_RUNNING(ci))
continue;
if (ci->ci_smt_id == 0)
continue;
if (sched_smt)
cpuset_add(&sched_all_cpus, ci);
else
cpuset_del(&sched_all_cpus, ci);
}
return 0;
}
#endif
5
5
4
4
4
3
2
2
8
4
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
/* $OpenBSD: dt_dev.c,v 1.22 2022/02/27 10:14:01 bluhm Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <dev/dt/dtvar.h>
/*
* Number of frames to skip in stack traces.
*
* The number of frames required to execute dt(4) profiling code
* depends on the probe, context, architecture and possibly the
* compiler.
*
* Static probes (tracepoints) are executed in the context of the
* current thread and only need to skip frames up to the recording
* function. For example the syscall provider:
*
* dt_prov_syscall_entry+0x141
* syscall+0x205 <--- start here
* Xsyscall+0x128
*
* Probes executed in their own context, like the profile provider,
* need to skip the frames of that context which are different for
* every architecture. For example the profile provider executed
* from hardclock(9) on amd64:
*
* dt_prov_profile_enter+0x6e
* hardclock+0x1a9
* lapic_clockintr+0x3f
* Xresume_lapic_ltimer+0x26
* acpicpu_idle+0x1d2 <---- start here.
* sched_idle+0x225
* proc_trampoline+0x1c
*/
#if defined(__amd64__)
#define DT_FA_PROFILE 5
#define DT_FA_STATIC 2
#elif defined(__octeon__)
#define DT_FA_PROFILE 6
#define DT_FA_STATIC 2
#elif defined(__powerpc64__)
#define DT_FA_PROFILE 6
#define DT_FA_STATIC 2
#elif defined(__sparc64__)
#define DT_FA_PROFILE 5
#define DT_FA_STATIC 1
#else
#define DT_FA_STATIC 0
#define DT_FA_PROFILE 0
#endif
#define DT_EVTRING_SIZE 16 /* # of slots in per PCB event ring */
#define DPRINTF(x...) /* nothing */
/*
* Descriptor associated with each program opening /dev/dt. It is used
* to keep track of enabled PCBs.
*
* Locks used to protect struct members in this file:
* m per-softc mutex
* K kernel lock
*/
struct dt_softc {
SLIST_ENTRY(dt_softc) ds_next; /* [K] descriptor list */
int ds_unit; /* [I] D_CLONE unique unit */
pid_t ds_pid; /* [I] PID of tracing program */
struct mutex ds_mtx;
struct dt_pcb_list ds_pcbs; /* [K] list of enabled PCBs */
struct dt_evt *ds_bufqueue; /* [K] copy evts to userland */
size_t ds_bufqlen; /* [K] length of the queue */
int ds_recording; /* [K] currently recording? */
int ds_evtcnt; /* [m] # of readable evts */
/* Counters */
uint64_t ds_readevt; /* [m] # of events read */
uint64_t ds_dropevt; /* [m] # of events dropped */
};
SLIST_HEAD(, dt_softc) dtdev_list; /* [K] list of open /dev/dt nodes */
/*
* Probes are created during dt_attach() and never modified/freed during
* the lifetime of the system. That's why we consider them as [I]mmutable.
*/
unsigned int dt_nprobes; /* [I] # of probes available */
SIMPLEQ_HEAD(, dt_probe) dt_probe_list; /* [I] list of probes */
struct rwlock dt_lock = RWLOCK_INITIALIZER("dtlk");
volatile uint32_t dt_tracing = 0; /* [K] # of processes tracing */
int allowdt;
void dtattach(struct device *, struct device *, void *);
int dtopen(dev_t, int, int, struct proc *);
int dtclose(dev_t, int, int, struct proc *);
int dtread(dev_t, struct uio *, int);
int dtioctl(dev_t, u_long, caddr_t, int, struct proc *);
struct dt_softc *dtlookup(int);
int dt_ioctl_list_probes(struct dt_softc *, struct dtioc_probe *);
int dt_ioctl_get_stats(struct dt_softc *, struct dtioc_stat *);
int dt_ioctl_record_start(struct dt_softc *);
void dt_ioctl_record_stop(struct dt_softc *);
int dt_ioctl_probe_enable(struct dt_softc *, struct dtioc_req *);
int dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *);
int dt_pcb_ring_copy(struct dt_pcb *, struct dt_evt *, size_t, uint64_t *);
void
dtattach(struct device *parent, struct device *self, void *aux)
{
SLIST_INIT(&dtdev_list);
SIMPLEQ_INIT(&dt_probe_list);
/* Init providers */
dt_nprobes += dt_prov_profile_init();
dt_nprobes += dt_prov_syscall_init();
dt_nprobes += dt_prov_static_init();
#ifdef DDBPROF
dt_nprobes += dt_prov_kprobe_init();
#endif
}
int
dtopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct dt_softc *sc;
int unit = minor(dev);
if (!allowdt)
return EPERM;
KASSERT(dtlookup(unit) == NULL);
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
if (sc == NULL)
return ENOMEM;
/*
* Enough space to empty 2 full rings of events in a single read.
*/
sc->ds_bufqlen = 2 * DT_EVTRING_SIZE;
sc->ds_bufqueue = mallocarray(sc->ds_bufqlen, sizeof(*sc->ds_bufqueue),
M_DEVBUF, M_WAITOK|M_CANFAIL);
if (sc->ds_bufqueue == NULL)
goto bad;
sc->ds_unit = unit;
sc->ds_pid = p->p_p->ps_pid;
TAILQ_INIT(&sc->ds_pcbs);
mtx_init(&sc->ds_mtx, IPL_HIGH);
sc->ds_evtcnt = 0;
sc->ds_readevt = 0;
sc->ds_dropevt = 0;
SLIST_INSERT_HEAD(&dtdev_list, sc, ds_next);
DPRINTF("dt%d: pid %d open\n", sc->ds_unit, sc->ds_pid);
return 0;
bad:
free(sc, M_DEVBUF, sizeof(*sc));
return ENOMEM;
}
int
dtclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct dt_softc *sc;
int unit = minor(dev);
sc = dtlookup(unit);
KASSERT(sc != NULL);
DPRINTF("dt%d: pid %d close\n", sc->ds_unit, sc->ds_pid);
SLIST_REMOVE(&dtdev_list, sc, dt_softc, ds_next);
dt_ioctl_record_stop(sc);
dt_pcb_purge(&sc->ds_pcbs);
free(sc->ds_bufqueue, M_DEVBUF,
sc->ds_bufqlen * sizeof(*sc->ds_bufqueue));
free(sc, M_DEVBUF, sizeof(*sc));
return 0;
}
int
dtread(dev_t dev, struct uio *uio, int flags)
{
struct sleep_state sls;
struct dt_softc *sc;
struct dt_evt *estq;
struct dt_pcb *dp;
int error = 0, unit = minor(dev);
size_t qlen, count, read = 0;
uint64_t dropped = 0;
sc = dtlookup(unit);
KASSERT(sc != NULL);
count = howmany(uio->uio_resid, sizeof(struct dt_evt));
if (count < 1)
return (EMSGSIZE);
while (!sc->ds_evtcnt) {
sleep_setup(&sls, sc, PWAIT | PCATCH, "dtread", 0);
error = sleep_finish(&sls, !sc->ds_evtcnt);
if (error == EINTR || error == ERESTART)
break;
}
if (error)
return error;
estq = sc->ds_bufqueue;
qlen = MIN(sc->ds_bufqlen, count);
KERNEL_ASSERT_LOCKED();
TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) {
count = dt_pcb_ring_copy(dp, estq, qlen, &dropped);
read += count;
estq += count; /* pointer arithmetic */
qlen -= count;
if (qlen == 0)
break;
}
if (read > 0)
uiomove(sc->ds_bufqueue, read * sizeof(struct dt_evt), uio);
mtx_enter(&sc->ds_mtx);
sc->ds_evtcnt -= read;
sc->ds_readevt += read;
sc->ds_dropevt += dropped;
mtx_leave(&sc->ds_mtx);
return 0;
}
int
dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct dt_softc *sc;
int unit = minor(dev);
int on, error = 0;
sc = dtlookup(unit);
KASSERT(sc != NULL);
switch (cmd) {
case DTIOCGPLIST:
return dt_ioctl_list_probes(sc, (struct dtioc_probe *)addr);
case DTIOCGSTATS:
return dt_ioctl_get_stats(sc, (struct dtioc_stat *)addr);
case DTIOCRECORD:
case DTIOCPRBENABLE:
case DTIOCPRBDISABLE:
/* root only ioctl(2) */
break;
default:
return ENOTTY;
}
if ((error = suser(p)) != 0)
return error;
switch (cmd) {
case DTIOCRECORD:
on = *(int *)addr;
if (on)
error = dt_ioctl_record_start(sc);
else
dt_ioctl_record_stop(sc);
break;
case DTIOCPRBENABLE:
error = dt_ioctl_probe_enable(sc, (struct dtioc_req *)addr);
break;
case DTIOCPRBDISABLE:
error = dt_ioctl_probe_disable(sc, (struct dtioc_req *)addr);
break;
default:
KASSERT(0);
}
return error;
}
struct dt_softc *
dtlookup(int unit)
{
struct dt_softc *sc;
KERNEL_ASSERT_LOCKED();
SLIST_FOREACH(sc, &dtdev_list, ds_next) {
if (sc->ds_unit == unit)
break;
}
return sc;
}
int
dtioc_req_isvalid(struct dtioc_req *dtrq)
{
switch (dtrq->dtrq_filter.dtf_operand) {
case DT_OP_NONE:
case DT_OP_EQ:
case DT_OP_NE:
break;
default:
return 0;
}
switch (dtrq->dtrq_filter.dtf_variable) {
case DT_FV_NONE:
case DT_FV_PID:
case DT_FV_TID:
break;
default:
return 0;
}
return 1;
}
int
dt_ioctl_list_probes(struct dt_softc *sc, struct dtioc_probe *dtpr)
{
struct dtioc_probe_info info, *dtpi;
struct dt_probe *dtp;
size_t size;
int error = 0;
size = dtpr->dtpr_size;
dtpr->dtpr_size = dt_nprobes * sizeof(*dtpi);
if (size == 0)
return 0;
dtpi = dtpr->dtpr_probes;
memset(&info, 0, sizeof(info));
SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
if (size < sizeof(*dtpi)) {
error = ENOSPC;
break;
}
info.dtpi_pbn = dtp->dtp_pbn;
info.dtpi_nargs = dtp->dtp_nargs;
strlcpy(info.dtpi_prov, dtp->dtp_prov->dtpv_name,
sizeof(info.dtpi_prov));
strlcpy(info.dtpi_func, dtp->dtp_func, sizeof(info.dtpi_func));
strlcpy(info.dtpi_name, dtp->dtp_name, sizeof(info.dtpi_name));
error = copyout(&info, dtpi, sizeof(*dtpi));
if (error)
break;
size -= sizeof(*dtpi);
dtpi++;
};
return error;
}
int
dt_ioctl_get_stats(struct dt_softc *sc, struct dtioc_stat *dtst)
{
mtx_enter(&sc->ds_mtx);
dtst->dtst_readevt = sc->ds_readevt;
dtst->dtst_dropevt = sc->ds_dropevt;
mtx_leave(&sc->ds_mtx);
return 0;
}
int
dt_ioctl_record_start(struct dt_softc *sc)
{
struct dt_pcb *dp;
if (sc->ds_recording)
return EBUSY;
KERNEL_ASSERT_LOCKED();
if (TAILQ_EMPTY(&sc->ds_pcbs))
return ENOENT;
rw_enter_write(&dt_lock);
TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) {
struct dt_probe *dtp = dp->dp_dtp;
SMR_SLIST_INSERT_HEAD_LOCKED(&dtp->dtp_pcbs, dp, dp_pnext);
dtp->dtp_recording++;
dtp->dtp_prov->dtpv_recording++;
}
rw_exit_write(&dt_lock);
sc->ds_recording = 1;
dt_tracing++;
return 0;
}
void
dt_ioctl_record_stop(struct dt_softc *sc)
{
struct dt_pcb *dp;
if (!sc->ds_recording)
return;
DPRINTF("dt%d: pid %d disable\n", sc->ds_unit, sc->ds_pid);
dt_tracing--;
sc->ds_recording = 0;
rw_enter_write(&dt_lock);
TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) {
struct dt_probe *dtp = dp->dp_dtp;
dtp->dtp_recording--;
dtp->dtp_prov->dtpv_recording--;
SMR_SLIST_REMOVE_LOCKED(&dtp->dtp_pcbs, dp, dt_pcb, dp_pnext);
}
rw_exit_write(&dt_lock);
/* Wait until readers cannot access the PCBs. */
smr_barrier();
}
int
dt_ioctl_probe_enable(struct dt_softc *sc, struct dtioc_req *dtrq)
{
struct dt_pcb_list plist;
struct dt_probe *dtp;
int error;
if (!dtioc_req_isvalid(dtrq))
return EINVAL;
SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
if (dtp->dtp_pbn == dtrq->dtrq_pbn)
break;
}
if (dtp == NULL)
return ENOENT;
TAILQ_INIT(&plist);
error = dtp->dtp_prov->dtpv_alloc(dtp, sc, &plist, dtrq);
if (error)
return error;
DPRINTF("dt%d: pid %d enable %u : %b\n", sc->ds_unit, sc->ds_pid,
dtrq->dtrq_pbn, (unsigned int)dtrq->dtrq_evtflags, DTEVT_FLAG_BITS);
/* Append all PCBs to this instance */
TAILQ_CONCAT(&sc->ds_pcbs, &plist, dp_snext);
return 0;
}
int
dt_ioctl_probe_disable(struct dt_softc *sc, struct dtioc_req *dtrq)
{
struct dt_probe *dtp;
int error;
if (!dtioc_req_isvalid(dtrq))
return EINVAL;
SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
if (dtp->dtp_pbn == dtrq->dtrq_pbn)
break;
}
if (dtp == NULL)
return ENOENT;
if (dtp->dtp_prov->dtpv_dealloc) {
error = dtp->dtp_prov->dtpv_dealloc(dtp, sc, dtrq);
if (error)
return error;
}
DPRINTF("dt%d: pid %d dealloc\n", sc->ds_unit, sc->ds_pid,
dtrq->dtrq_pbn);
return 0;
}
struct dt_probe *
dt_dev_alloc_probe(const char *func, const char *name, struct dt_provider *dtpv)
{
struct dt_probe *dtp;
dtp = malloc(sizeof(*dtp), M_DT, M_NOWAIT|M_ZERO);
if (dtp == NULL)
return NULL;
SMR_SLIST_INIT(&dtp->dtp_pcbs);
dtp->dtp_prov = dtpv;
dtp->dtp_func = func;
dtp->dtp_name = name;
dtp->dtp_sysnum = -1;
dtp->dtp_ref = 0;
return dtp;
}
void
dt_dev_register_probe(struct dt_probe *dtp)
{
static uint64_t probe_nb;
dtp->dtp_pbn = ++probe_nb;
SIMPLEQ_INSERT_TAIL(&dt_probe_list, dtp, dtp_next);
}
struct dt_pcb *
dt_pcb_alloc(struct dt_probe *dtp, struct dt_softc *sc)
{
struct dt_pcb *dp;
dp = malloc(sizeof(*dp), M_DT, M_WAITOK|M_CANFAIL|M_ZERO);
if (dp == NULL)
goto bad;
dp->dp_ring = mallocarray(DT_EVTRING_SIZE, sizeof(*dp->dp_ring), M_DT,
M_WAITOK|M_CANFAIL|M_ZERO);
if (dp->dp_ring == NULL)
goto bad;
mtx_init(&dp->dp_mtx, IPL_HIGH);
dp->dp_sc = sc;
dp->dp_dtp = dtp;
return dp;
bad:
dt_pcb_free(dp);
return NULL;
}
void
dt_pcb_free(struct dt_pcb *dp)
{
if (dp == NULL)
return;
free(dp->dp_ring, M_DT, DT_EVTRING_SIZE * sizeof(*dp->dp_ring));
free(dp, M_DT, sizeof(*dp));
}
void
dt_pcb_purge(struct dt_pcb_list *plist)
{
struct dt_pcb *dp;
while ((dp = TAILQ_FIRST(plist)) != NULL) {
TAILQ_REMOVE(plist, dp, dp_snext);
dt_pcb_free(dp);
}
}
int
dt_pcb_filter(struct dt_pcb *dp)
{
struct dt_filter *dtf = &dp->dp_filter;
struct proc *p = curproc;
unsigned int var = 0;
int match = 1;
/* Filter out tracing program. */
if (dp->dp_sc->ds_pid == p->p_p->ps_pid)
return 1;
switch (dtf->dtf_variable) {
case DT_FV_PID:
var = p->p_p->ps_pid;
break;
case DT_FV_TID:
var = p->p_tid + THREAD_PID_OFFSET;
break;
case DT_FV_NONE:
break;
default:
KASSERT(0);
}
switch (dtf->dtf_operand) {
case DT_OP_EQ:
match = !!(var == dtf->dtf_value);
break;
case DT_OP_NE:
match = !!(var != dtf->dtf_value);
break;
case DT_OP_NONE:
break;
default:
KASSERT(0);
}
return !match;
}
/*
* Get a reference to the next free event state from the ring.
*/
struct dt_evt *
dt_pcb_ring_get(struct dt_pcb *dp, int profiling)
{
struct proc *p = curproc;
struct dt_evt *dtev;
int distance;
if (dt_pcb_filter(dp))
return NULL;
mtx_enter(&dp->dp_mtx);
distance = dp->dp_prod - dp->dp_cons;
if (distance == 1 || distance == (1 - DT_EVTRING_SIZE)) {
/* read(2) isn't finished */
dp->dp_dropevt++;
mtx_leave(&dp->dp_mtx);
return NULL;
}
/*
* Save states in next free event slot.
*/
dtev = &dp->dp_ring[dp->dp_cons];
memset(dtev, 0, sizeof(*dtev));
dtev->dtev_pbn = dp->dp_dtp->dtp_pbn;
dtev->dtev_cpu = cpu_number();
dtev->dtev_pid = p->p_p->ps_pid;
dtev->dtev_tid = p->p_tid + THREAD_PID_OFFSET;
nanotime(&dtev->dtev_tsp);
if (ISSET(dp->dp_evtflags, DTEVT_EXECNAME))
strlcpy(dtev->dtev_comm, p->p_p->ps_comm, sizeof(dtev->dtev_comm));
if (ISSET(dp->dp_evtflags, DTEVT_KSTACK|DTEVT_USTACK)) {
if (profiling)
stacktrace_save_at(&dtev->dtev_kstack, DT_FA_PROFILE);
else
stacktrace_save_at(&dtev->dtev_kstack, DT_FA_STATIC);
}
return dtev;
}
void
dt_pcb_ring_consume(struct dt_pcb *dp, struct dt_evt *dtev)
{
MUTEX_ASSERT_LOCKED(&dp->dp_mtx);
KASSERT(dtev == &dp->dp_ring[dp->dp_cons]);
dp->dp_cons = (dp->dp_cons + 1) % DT_EVTRING_SIZE;
mtx_leave(&dp->dp_mtx);
mtx_enter(&dp->dp_sc->ds_mtx);
dp->dp_sc->ds_evtcnt++;
mtx_leave(&dp->dp_sc->ds_mtx);
wakeup(dp->dp_sc);
}
/*
* Copy at most `qlen' events from `dp', producing the same amount
* of free slots.
*/
int
dt_pcb_ring_copy(struct dt_pcb *dp, struct dt_evt *estq, size_t qlen,
uint64_t *dropped)
{
size_t count, copied = 0;
unsigned int cons, prod;
KASSERT(qlen > 0);
mtx_enter(&dp->dp_mtx);
cons = dp->dp_cons;
prod = dp->dp_prod;
if (cons < prod)
count = DT_EVTRING_SIZE - prod;
else
count = cons - prod;
if (count == 0)
goto out;
*dropped += dp->dp_dropevt;
dp->dp_dropevt = 0;
count = MIN(count, qlen);
memcpy(&estq[0], &dp->dp_ring[prod], count * sizeof(*estq));
copied += count;
/* Produce */
prod = (prod + count) % DT_EVTRING_SIZE;
/* If the queue is full or the ring didn't wrap, stop here. */
if (qlen == copied || prod != 0 || cons == 0)
goto out;
count = MIN(cons, (qlen - copied));
memcpy(&estq[copied], &dp->dp_ring[0], count * sizeof(*estq));
copied += count;
prod += count;
out:
dp->dp_prod = prod;
mtx_leave(&dp->dp_mtx);
return copied;
}
1170
1170
168
168
138
63
1178
1175
77
138
1170
1171
1173
1174
70
70
2
70
1
70
2
70
77
77
77
77
138
138
137
70
70
70
70
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/* $OpenBSD: vfs_biomem.c,v 1.51 2021/10/24 00:02:25 jsg Exp $ */
/*
* Copyright (c) 2007 Artur Grabowski <art@openbsd.org>
* Copyright (c) 2012-2016,2019 Bob Beck <beck@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/pool.h>
#include <sys/proc.h> /* XXX for atomic */
#include <sys/mount.h>
#include <uvm/uvm_extern.h>
vaddr_t buf_kva_start, buf_kva_end;
int buf_needva;
TAILQ_HEAD(,buf) buf_valist;
extern struct bcachestats bcstats;
vaddr_t buf_unmap(struct buf *);
void
buf_mem_init(vsize_t size)
{
TAILQ_INIT(&buf_valist);
buf_kva_start = vm_map_min(kernel_map);
if (uvm_map(kernel_map, &buf_kva_start, size, NULL,
UVM_UNKNOWN_OFFSET, PAGE_SIZE, UVM_MAPFLAG(PROT_NONE,
PROT_NONE, MAP_INHERIT_NONE, MADV_NORMAL, 0)))
panic("%s: can't reserve VM for buffers", __func__);
buf_kva_end = buf_kva_start + size;
/* Contiguous mapping */
bcstats.kvaslots = bcstats.kvaslots_avail = size / MAXPHYS;
}
/*
* buf_acquire and buf_release manage the kvm mappings of buffers.
*/
void
buf_acquire(struct buf *bp)
{
KASSERT((bp->b_flags & B_BUSY) == 0);
splassert(IPL_BIO);
/*
* Busy before waiting for kvm.
*/
SET(bp->b_flags, B_BUSY);
buf_map(bp);
}
/*
* Acquire a buf but do not map it. Preserve any mapping it did have.
*/
void
buf_acquire_nomap(struct buf *bp)
{
splassert(IPL_BIO);
SET(bp->b_flags, B_BUSY);
if (bp->b_data != NULL) {
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
bcstats.busymapped++;
}
}
void
buf_map(struct buf *bp)
{
vaddr_t va;
splassert(IPL_BIO);
if (bp->b_data == NULL) {
unsigned long i;
/*
* First, just use the pre-allocated space until we run out.
*/
if (buf_kva_start < buf_kva_end) {
va = buf_kva_start;
buf_kva_start += MAXPHYS;
bcstats.kvaslots_avail--;
} else {
struct buf *vbp;
/*
* Find some buffer we can steal the space from.
*/
vbp = TAILQ_FIRST(&buf_valist);
while ((curproc != syncerproc &&
curproc != cleanerproc &&
bcstats.kvaslots_avail <= RESERVE_SLOTS) ||
vbp == NULL) {
buf_needva++;
tsleep_nsec(&buf_needva, PRIBIO, "buf_needva",
INFSLP);
vbp = TAILQ_FIRST(&buf_valist);
}
va = buf_unmap(vbp);
}
for (i = 0; i < atop(bp->b_bufsize); i++) {
struct vm_page *pg = uvm_pagelookup(bp->b_pobj,
bp->b_poffs + ptoa(i));
KASSERT(pg != NULL);
pmap_kenter_pa(va + ptoa(i), VM_PAGE_TO_PHYS(pg),
PROT_READ | PROT_WRITE);
}
pmap_update(pmap_kernel());
bp->b_data = (caddr_t)va;
} else {
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
}
bcstats.busymapped++;
}
void
buf_release(struct buf *bp)
{
KASSERT(bp->b_flags & B_BUSY);
splassert(IPL_BIO);
if (bp->b_data) {
bcstats.busymapped--;
TAILQ_INSERT_TAIL(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail++;
if (buf_needva) {
buf_needva=0;
wakeup(&buf_needva);
}
}
CLR(bp->b_flags, B_BUSY);
}
/*
* Deallocate all memory resources for this buffer. We need to be careful
* to not drop kvm since we have no way to reclaim it. So, if the buffer
* has kvm, we need to free it later. We put it on the front of the
* freelist just so it gets picked up faster.
*
* Also, lots of assertions count on bp->b_data being NULL, so we
* set it temporarily to NULL.
*
* Return non-zero if we take care of the freeing later.
*/
int
buf_dealloc_mem(struct buf *bp)
{
caddr_t data;
splassert(IPL_BIO);
data = bp->b_data;
bp->b_data = NULL;
if (data) {
if (bp->b_flags & B_BUSY)
bcstats.busymapped--;
pmap_kremove((vaddr_t)data, bp->b_bufsize);
pmap_update(pmap_kernel());
}
if (bp->b_pobj)
buf_free_pages(bp);
if (data == NULL)
return (0);
bp->b_data = data;
if (!(bp->b_flags & B_BUSY)) { /* XXX - need better test */
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
} else {
CLR(bp->b_flags, B_BUSY);
if (buf_needva) {
buf_needva = 0;
wakeup(&buf_needva);
}
}
SET(bp->b_flags, B_RELEASED);
TAILQ_INSERT_HEAD(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail++;
return (1);
}
/*
* Only used by bread_cluster.
*/
void
buf_fix_mapping(struct buf *bp, vsize_t newsize)
{
vaddr_t va = (vaddr_t)bp->b_data;
if (newsize < bp->b_bufsize) {
pmap_kremove(va + newsize, bp->b_bufsize - newsize);
pmap_update(pmap_kernel());
/*
* Note: the size we lost is actually with the other
* buffers read in by bread_cluster
*/
bp->b_bufsize = newsize;
}
}
vaddr_t
buf_unmap(struct buf *bp)
{
vaddr_t va;
KASSERT((bp->b_flags & B_BUSY) == 0);
KASSERT(bp->b_data != NULL);
splassert(IPL_BIO);
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
va = (vaddr_t)bp->b_data;
bp->b_data = NULL;
pmap_kremove(va, bp->b_bufsize);
pmap_update(pmap_kernel());
if (bp->b_flags & B_RELEASED)
pool_put(&bufpool, bp);
return (va);
}
/* Always allocates in dma-reachable memory */
void
buf_alloc_pages(struct buf *bp, vsize_t size)
{
int i;
KASSERT(size == round_page(size));
KASSERT(bp->b_pobj == NULL);
KASSERT(bp->b_data == NULL);
splassert(IPL_BIO);
uvm_obj_init(&bp->b_uobj, &bufcache_pager, 1);
/*
* Attempt to allocate with NOWAIT. if we can't, then throw
* away some clean pages and try again. Finally, if that
* fails, do a WAITOK allocation so the page daemon can find
* memory for us.
*/
do {
i = uvm_pagealloc_multi(&bp->b_uobj, 0, size,
UVM_PLA_NOWAIT | UVM_PLA_NOWAKE);
if (i == 0)
break;
} while (bufbackoff(&dma_constraint, size) == 0);
if (i != 0)
i = uvm_pagealloc_multi(&bp->b_uobj, 0, size,
UVM_PLA_WAITOK);
/* should not happen */
if (i != 0)
panic("uvm_pagealloc_multi unable to allocate an buf_object "
"of size %lu", size);
bcstats.numbufpages += atop(size);
bcstats.dmapages += atop(size);
SET(bp->b_flags, B_DMA);
bp->b_pobj = &bp->b_uobj;
bp->b_poffs = 0;
bp->b_bufsize = size;
}
void
buf_free_pages(struct buf *bp)
{
struct uvm_object *uobj = bp->b_pobj;
struct vm_page *pg;
voff_t off, i;
KASSERT(bp->b_data == NULL);
KASSERT(uobj != NULL);
splassert(IPL_BIO);
off = bp->b_poffs;
bp->b_pobj = NULL;
bp->b_poffs = 0;
for (i = 0; i < atop(bp->b_bufsize); i++) {
pg = uvm_pagelookup(uobj, off + ptoa(i));
KASSERT(pg != NULL);
KASSERT(pg->wire_count == 1);
pg->wire_count = 0;
bcstats.numbufpages--;
if (ISSET(bp->b_flags, B_DMA))
bcstats.dmapages--;
}
CLR(bp->b_flags, B_DMA);
/* XXX refactor to do this without splbio later */
uvm_obj_free(uobj);
}
/* Reallocate a buf into a particular pmem range specified by "where". */
int
buf_realloc_pages(struct buf *bp, struct uvm_constraint_range *where,
int flags)
{
vaddr_t va;
int dma;
int i, r;
KASSERT(!(flags & UVM_PLA_WAITOK) ^ !(flags & UVM_PLA_NOWAIT));
splassert(IPL_BIO);
KASSERT(ISSET(bp->b_flags, B_BUSY));
dma = ISSET(bp->b_flags, B_DMA);
/* if the original buf is mapped, unmap it */
if (bp->b_data != NULL) {
va = (vaddr_t)bp->b_data;
pmap_kremove(va, bp->b_bufsize);
pmap_update(pmap_kernel());
}
do {
r = uvm_pagerealloc_multi(bp->b_pobj, bp->b_poffs,
bp->b_bufsize, UVM_PLA_NOWAIT | UVM_PLA_NOWAKE, where);
if (r == 0)
break;
} while ((bufbackoff(where, atop(bp->b_bufsize)) == 0));
/*
* bufbackoff() failed, so there's no more we can do without
* waiting. If allowed do, make that attempt.
*/
if (r != 0 && (flags & UVM_PLA_WAITOK))
r = uvm_pagerealloc_multi(bp->b_pobj, bp->b_poffs,
bp->b_bufsize, flags, where);
/*
* If the allocation has succeeded, we may be somewhere different.
* If the allocation has failed, we are in the same place.
*
* We still have to re-map the buffer before returning.
*/
/* take it out of dma stats until we know where we are */
if (dma)
bcstats.dmapages -= atop(bp->b_bufsize);
dma = 1;
/* if the original buf was mapped, re-map it */
for (i = 0; i < atop(bp->b_bufsize); i++) {
struct vm_page *pg = uvm_pagelookup(bp->b_pobj,
bp->b_poffs + ptoa(i));
KASSERT(pg != NULL);
if (!PADDR_IS_DMA_REACHABLE(VM_PAGE_TO_PHYS(pg)))
dma = 0;
if (bp->b_data != NULL) {
pmap_kenter_pa(va + ptoa(i), VM_PAGE_TO_PHYS(pg),
PROT_READ|PROT_WRITE);
pmap_update(pmap_kernel());
}
}
if (dma) {
SET(bp->b_flags, B_DMA);
bcstats.dmapages += atop(bp->b_bufsize);
} else
CLR(bp->b_flags, B_DMA);
return(r);
}
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
/* $OpenBSD: identcpu.c,v 1.127 2022/08/30 17:09:21 dv Exp $ */
/* $NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */
/*
* Copyright (c) 2003 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Frank van der Linden for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include "vmm.h"
#include "pvbus.h"
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#if NPVBUS > 0
#include <dev/pv/pvvar.h>
#endif
void replacesmap(void);
void replacemeltdown(void);
uint64_t cpu_freq(struct cpu_info *);
void tsc_identify(struct cpu_info *);
void tsc_timecounter_init(struct cpu_info *, uint64_t);
#if NVMM > 0
void cpu_check_vmm_cap(struct cpu_info *);
#endif /* NVMM > 0 */
/* sysctl wants this. */
char cpu_model[48];
int cpuspeed;
int amd64_has_xcrypt;
#ifdef CRYPTO
int amd64_has_pclmul;
int amd64_has_aesni;
#endif
int has_rdrand;
int has_rdseed;
const struct {
u_int32_t bit;
char str[12];
} cpu_cpuid_features[] = {
{ CPUID_FPU, "FPU" },
{ CPUID_VME, "VME" },
{ CPUID_DE, "DE" },
{ CPUID_PSE, "PSE" },
{ CPUID_TSC, "TSC" },
{ CPUID_MSR, "MSR" },
{ CPUID_PAE, "PAE" },
{ CPUID_MCE, "MCE" },
{ CPUID_CX8, "CX8" },
{ CPUID_APIC, "APIC" },
{ CPUID_SEP, "SEP" },
{ CPUID_MTRR, "MTRR" },
{ CPUID_PGE, "PGE" },
{ CPUID_MCA, "MCA" },
{ CPUID_CMOV, "CMOV" },
{ CPUID_PAT, "PAT" },
{ CPUID_PSE36, "PSE36" },
{ CPUID_PSN, "PSN" },
{ CPUID_CFLUSH, "CFLUSH" },
{ CPUID_DS, "DS" },
{ CPUID_ACPI, "ACPI" },
{ CPUID_MMX, "MMX" },
{ CPUID_FXSR, "FXSR" },
{ CPUID_SSE, "SSE" },
{ CPUID_SSE2, "SSE2" },
{ CPUID_SS, "SS" },
{ CPUID_HTT, "HTT" },
{ CPUID_TM, "TM" },
{ CPUID_PBE, "PBE" }
}, cpu_ecpuid_features[] = {
{ CPUID_MPC, "MPC" },
{ CPUID_NXE, "NXE" },
{ CPUID_MMXX, "MMXX" },
{ CPUID_FFXSR, "FFXSR" },
{ CPUID_PAGE1GB, "PAGE1GB" },
{ CPUID_RDTSCP, "RDTSCP" },
{ CPUID_LONG, "LONG" },
{ CPUID_3DNOW2, "3DNOW2" },
{ CPUID_3DNOW, "3DNOW" }
}, cpu_cpuid_ecxfeatures[] = {
{ CPUIDECX_SSE3, "SSE3" },
{ CPUIDECX_PCLMUL, "PCLMUL" },
{ CPUIDECX_DTES64, "DTES64" },
{ CPUIDECX_MWAIT, "MWAIT" },
{ CPUIDECX_DSCPL, "DS-CPL" },
{ CPUIDECX_VMX, "VMX" },
{ CPUIDECX_SMX, "SMX" },
{ CPUIDECX_EST, "EST" },
{ CPUIDECX_TM2, "TM2" },
{ CPUIDECX_SSSE3, "SSSE3" },
{ CPUIDECX_CNXTID, "CNXT-ID" },
{ CPUIDECX_SDBG, "SDBG" },
{ CPUIDECX_FMA3, "FMA3" },
{ CPUIDECX_CX16, "CX16" },
{ CPUIDECX_XTPR, "xTPR" },
{ CPUIDECX_PDCM, "PDCM" },
{ CPUIDECX_PCID, "PCID" },
{ CPUIDECX_DCA, "DCA" },
{ CPUIDECX_SSE41, "SSE4.1" },
{ CPUIDECX_SSE42, "SSE4.2" },
{ CPUIDECX_X2APIC, "x2APIC" },
{ CPUIDECX_MOVBE, "MOVBE" },
{ CPUIDECX_POPCNT, "POPCNT" },
{ CPUIDECX_DEADLINE, "DEADLINE" },
{ CPUIDECX_AES, "AES" },
{ CPUIDECX_XSAVE, "XSAVE" },
{ CPUIDECX_OSXSAVE, "OSXSAVE" },
{ CPUIDECX_AVX, "AVX" },
{ CPUIDECX_F16C, "F16C" },
{ CPUIDECX_RDRAND, "RDRAND" },
{ CPUIDECX_HV, "HV" },
}, cpu_ecpuid_ecxfeatures[] = {
{ CPUIDECX_LAHF, "LAHF" },
{ CPUIDECX_CMPLEG, "CMPLEG" },
{ CPUIDECX_SVM, "SVM" },
{ CPUIDECX_EAPICSP, "EAPICSP"},
{ CPUIDECX_AMCR8, "AMCR8"},
{ CPUIDECX_ABM, "ABM" },
{ CPUIDECX_SSE4A, "SSE4A" },
{ CPUIDECX_MASSE, "MASSE" },
{ CPUIDECX_3DNOWP, "3DNOWP" },
{ CPUIDECX_OSVW, "OSVW" },
{ CPUIDECX_IBS, "IBS" },
{ CPUIDECX_XOP, "XOP" },
{ CPUIDECX_SKINIT, "SKINIT" },
{ CPUIDECX_LWP, "WDT" },
{ CPUIDECX_FMA4, "FMA4" },
{ CPUIDECX_TCE, "TCE" },
{ CPUIDECX_NODEID, "NODEID" },
{ CPUIDECX_TBM, "TBM" },
{ CPUIDECX_TOPEXT, "TOPEXT" },
{ CPUIDECX_CPCTR, "CPCTR" },
{ CPUIDECX_DBKP, "DBKP" },
{ CPUIDECX_PERFTSC, "PERFTSC" },
{ CPUIDECX_PCTRL3, "PCTRL3" },
{ CPUIDECX_MWAITX, "MWAITX" },
}, cpu_seff0_ebxfeatures[] = {
{ SEFF0EBX_FSGSBASE, "FSGSBASE" },
{ SEFF0EBX_TSC_ADJUST, "TSC_ADJUST" },
{ SEFF0EBX_SGX, "SGX" },
{ SEFF0EBX_BMI1, "BMI1" },
{ SEFF0EBX_HLE, "HLE" },
{ SEFF0EBX_AVX2, "AVX2" },
{ SEFF0EBX_SMEP, "SMEP" },
{ SEFF0EBX_BMI2, "BMI2" },
{ SEFF0EBX_ERMS, "ERMS" },
{ SEFF0EBX_INVPCID, "INVPCID" },
{ SEFF0EBX_RTM, "RTM" },
{ SEFF0EBX_PQM, "PQM" },
{ SEFF0EBX_MPX, "MPX" },
{ SEFF0EBX_AVX512F, "AVX512F" },
{ SEFF0EBX_AVX512DQ, "AVX512DQ" },
{ SEFF0EBX_RDSEED, "RDSEED" },
{ SEFF0EBX_ADX, "ADX" },
{ SEFF0EBX_SMAP, "SMAP" },
{ SEFF0EBX_AVX512IFMA, "AVX512IFMA" },
{ SEFF0EBX_PCOMMIT, "PCOMMIT" },
{ SEFF0EBX_CLFLUSHOPT, "CLFLUSHOPT" },
{ SEFF0EBX_CLWB, "CLWB" },
{ SEFF0EBX_PT, "PT" },
{ SEFF0EBX_AVX512PF, "AVX512PF" },
{ SEFF0EBX_AVX512ER, "AVX512ER" },
{ SEFF0EBX_AVX512CD, "AVX512CD" },
{ SEFF0EBX_SHA, "SHA" },
{ SEFF0EBX_AVX512BW, "AVX512BW" },
{ SEFF0EBX_AVX512VL, "AVX512VL" },
}, cpu_seff0_ecxfeatures[] = {
{ SEFF0ECX_PREFETCHWT1, "PREFETCHWT1" },
{ SEFF0ECX_AVX512VBMI, "AVX512VBMI" },
{ SEFF0ECX_UMIP, "UMIP" },
{ SEFF0ECX_PKU, "PKU" },
}, cpu_seff0_edxfeatures[] = {
{ SEFF0EDX_AVX512_4FNNIW, "AVX512FNNIW" },
{ SEFF0EDX_AVX512_4FMAPS, "AVX512FMAPS" },
{ SEFF0EDX_SRBDS_CTRL, "SRBDS_CTRL" },
{ SEFF0EDX_MD_CLEAR, "MD_CLEAR" },
{ SEFF0EDX_TSXFA, "TSXFA" },
{ SEFF0EDX_IBRS, "IBRS,IBPB" },
{ SEFF0EDX_STIBP, "STIBP" },
{ SEFF0EDX_L1DF, "L1DF" },
/* SEFF0EDX_ARCH_CAP (not printed) */
{ SEFF0EDX_SSBD, "SSBD" },
}, cpu_tpm_eaxfeatures[] = {
{ TPM_SENSOR, "SENSOR" },
{ TPM_ARAT, "ARAT" },
}, cpu_cpuid_perf_eax[] = {
{ CPUIDEAX_VERID, "PERF" },
}, cpu_cpuid_apmi_edx[] = {
{ CPUIDEDX_ITSC, "ITSC" },
}, cpu_amdspec_ebxfeatures[] = {
{ CPUIDEBX_IBPB, "IBPB" },
{ CPUIDEBX_IBRS, "IBRS" },
{ CPUIDEBX_STIBP, "STIBP" },
{ CPUIDEBX_SSBD, "SSBD" },
{ CPUIDEBX_VIRT_SSBD, "VIRTSSBD" },
{ CPUIDEBX_SSBD_NOTREQ, "SSBDNR" },
}, cpu_xsave_extfeatures[] = {
{ XSAVE_XSAVEOPT, "XSAVEOPT" },
{ XSAVE_XSAVEC, "XSAVEC" },
{ XSAVE_XGETBV1, "XGETBV1" },
{ XSAVE_XSAVES, "XSAVES" },
};
int
cpu_amd64speed(int *freq)
{
*freq = cpuspeed;
return (0);
}
#ifndef SMALL_KERNEL
void intelcore_update_sensor(void *);
void cpu_hz_update_sensor(void *);
/*
* Temperature read on the CPU is relative to the maximum
* temperature supported by the CPU, Tj(Max).
* Refer to:
* 64-ia-32-architectures-software-developer-vol-3c-part-3-manual.pdf
* Section 35 and
* http://www.intel.com/content/dam/www/public/us/en/documents/
* white-papers/cpu-monitoring-dts-peci-paper.pdf
*
* The temperature on Intel CPUs can be between 70 and 105 degC, since
* Westmere we can read the TJmax from the die. For older CPUs we have
* to guess or use undocumented MSRs. Then we subtract the temperature
* portion of thermal status from max to get current temperature.
*/
void
intelcore_update_sensor(void *args)
{
struct cpu_info *ci = (struct cpu_info *) args;
u_int64_t msr;
int max = 100;
/* Only some Core family chips have MSR_TEMPERATURE_TARGET. */
if (ci->ci_model == 0x0e &&
(rdmsr(MSR_TEMPERATURE_TARGET_UNDOCUMENTED) &
MSR_TEMPERATURE_TARGET_LOW_BIT_UNDOCUMENTED))
max = 85;
/*
* Newer CPUs can tell you what their max temperature is.
* See: '64-ia-32-architectures-software-developer-
* vol-3c-part-3-manual.pdf'
*/
if (ci->ci_model > 0x17 && ci->ci_model != 0x1c &&
ci->ci_model != 0x26 && ci->ci_model != 0x27 &&
ci->ci_model != 0x35 && ci->ci_model != 0x36)
max = MSR_TEMPERATURE_TARGET_TJMAX(
rdmsr(MSR_TEMPERATURE_TARGET));
msr = rdmsr(MSR_THERM_STATUS);
if (msr & MSR_THERM_STATUS_VALID_BIT) {
ci->ci_sensor.value = max - MSR_THERM_STATUS_TEMP(msr);
/* micro degrees */
ci->ci_sensor.value *= 1000000;
/* kelvin */
ci->ci_sensor.value += 273150000;
ci->ci_sensor.flags &= ~SENSOR_FINVALID;
} else {
ci->ci_sensor.value = 0;
ci->ci_sensor.flags |= SENSOR_FINVALID;
}
}
/*
* Effective CPU frequency measurement
*
* Refer to:
* 64-ia-32-architectures-software-developer-vol-3b-part-2-manual.pdf
* Section 14.2 and
* OSRR for AMD Family 17h processors Section 2.1.2
* Round to 50Mhz which is the accuracy of this measurement.
*/
#define FREQ_50MHZ (50ULL * 1000000ULL * 1000000ULL)
void
cpu_hz_update_sensor(void *args)
{
extern uint64_t tsc_frequency;
struct cpu_info *ci = args;
uint64_t mperf, aperf, mdelta, adelta, val;
unsigned long s;
sched_peg_curproc(ci);
s = intr_disable();
mperf = rdmsr(MSR_MPERF);
aperf = rdmsr(MSR_APERF);
intr_restore(s);
mdelta = mperf - ci->ci_hz_mperf;
adelta = aperf - ci->ci_hz_aperf;
ci->ci_hz_mperf = mperf;
ci->ci_hz_aperf = aperf;
if (mdelta > 0) {
val = (adelta * 1000000) / mdelta * tsc_frequency;
val = ((val + FREQ_50MHZ / 2) / FREQ_50MHZ) * FREQ_50MHZ;
ci->ci_hz_sensor.value = val;
}
atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
}
#endif
void (*setperf_setup)(struct cpu_info *);
void via_nano_setup(struct cpu_info *ci);
void cpu_topology(struct cpu_info *ci);
void
via_nano_setup(struct cpu_info *ci)
{
u_int32_t regs[4], val;
u_int64_t msreg;
int model = (ci->ci_signature >> 4) & 15;
if (model >= 9) {
CPUID(0xC0000000, regs[0], regs[1], regs[2], regs[3]);
val = regs[0];
if (val >= 0xC0000001) {
CPUID(0xC0000001, regs[0], regs[1], regs[2], regs[3]);
val = regs[3];
} else
val = 0;
if (val & (C3_CPUID_HAS_RNG | C3_CPUID_HAS_ACE))
printf("%s:", ci->ci_dev->dv_xname);
/* Enable RNG if present and disabled */
if (val & C3_CPUID_HAS_RNG) {
extern int viac3_rnd_present;
if (!(val & C3_CPUID_DO_RNG)) {
msreg = rdmsr(0x110B);
msreg |= 0x40;
wrmsr(0x110B, msreg);
}
viac3_rnd_present = 1;
printf(" RNG");
}
/* Enable AES engine if present and disabled */
if (val & C3_CPUID_HAS_ACE) {
#ifdef CRYPTO
if (!(val & C3_CPUID_DO_ACE)) {
msreg = rdmsr(0x1107);
msreg |= (0x01 << 28);
wrmsr(0x1107, msreg);
}
amd64_has_xcrypt |= C3_HAS_AES;
#endif /* CRYPTO */
printf(" AES");
}
/* Enable ACE2 engine if present and disabled */
if (val & C3_CPUID_HAS_ACE2) {
#ifdef CRYPTO
if (!(val & C3_CPUID_DO_ACE2)) {
msreg = rdmsr(0x1107);
msreg |= (0x01 << 28);
wrmsr(0x1107, msreg);
}
amd64_has_xcrypt |= C3_HAS_AESCTR;
#endif /* CRYPTO */
printf(" AES-CTR");
}
/* Enable SHA engine if present and disabled */
if (val & C3_CPUID_HAS_PHE) {
#ifdef CRYPTO
if (!(val & C3_CPUID_DO_PHE)) {
msreg = rdmsr(0x1107);
msreg |= (0x01 << 28/**/);
wrmsr(0x1107, msreg);
}
amd64_has_xcrypt |= C3_HAS_SHA;
#endif /* CRYPTO */
printf(" SHA1 SHA256");
}
/* Enable MM engine if present and disabled */
if (val & C3_CPUID_HAS_PMM) {
#ifdef CRYPTO
if (!(val & C3_CPUID_DO_PMM)) {
msreg = rdmsr(0x1107);
msreg |= (0x01 << 28/**/);
wrmsr(0x1107, msreg);
}
amd64_has_xcrypt |= C3_HAS_MM;
#endif /* CRYPTO */
printf(" RSA");
}
printf("\n");
}
}
#ifndef SMALL_KERNEL
void via_update_sensor(void *args);
void
via_update_sensor(void *args)
{
struct cpu_info *ci = (struct cpu_info *) args;
u_int64_t msr;
msr = rdmsr(MSR_CENT_TMTEMPERATURE);
ci->ci_sensor.value = (msr & 0xffffff);
/* micro degrees */
ci->ci_sensor.value *= 1000000;
ci->ci_sensor.value += 273150000;
ci->ci_sensor.flags &= ~SENSOR_FINVALID;
}
#endif
uint64_t
cpu_freq_ctr(struct cpu_info *ci)
{
uint64_t count, last_count, msr;
if ((ci->ci_flags & CPUF_CONST_TSC) == 0 ||
(cpu_perf_eax & CPUIDEAX_VERID) <= 1 ||
CPUIDEDX_NUM_FC(cpu_perf_edx) <= 1)
return (0);
msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
if (msr & MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK)) {
/* some hypervisor is dicking us around */
return (0);
}
msr |= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_1);
wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
msr = rdmsr(MSR_PERF_GLOBAL_CTRL) | MSR_PERF_GLOBAL_CTR1_EN;
wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
last_count = rdmsr(MSR_PERF_FIXED_CTR1);
delay(100000);
count = rdmsr(MSR_PERF_FIXED_CTR1);
msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
msr &= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK);
wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
msr = rdmsr(MSR_PERF_GLOBAL_CTRL);
msr &= ~MSR_PERF_GLOBAL_CTR1_EN;
wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
return ((count - last_count) * 10);
}
uint64_t
cpu_freq(struct cpu_info *ci)
{
uint64_t last_count, count;
count = cpu_freq_ctr(ci);
if (count != 0)
return (count);
last_count = rdtsc();
delay(100000);
count = rdtsc();
return ((count - last_count) * 10);
}
void
identifycpu(struct cpu_info *ci)
{
uint64_t freq = 0;
u_int32_t dummy, val, cpu_tpm_ecxflags = 0;
char mycpu_model[48];
int i;
char *brandstr_from, *brandstr_to;
int skipspace;
CPUID(1, ci->ci_signature, val, dummy, ci->ci_feature_flags);
CPUID(0x80000000, ci->ci_pnfeatset, dummy, dummy, dummy);
if (ci->ci_pnfeatset >= 0x80000001) {
CPUID(0x80000001, ci->ci_efeature_eax, dummy,
ci->ci_efeature_ecx, ci->ci_feature_eflags);
/* Other bits may clash */
ci->ci_feature_flags |= (ci->ci_feature_eflags & CPUID_NXE);
if (CPU_IS_PRIMARY(ci))
ecpu_ecxfeature = ci->ci_efeature_ecx;
/* Let cpu_feature be the common bits */
cpu_feature &= ci->ci_feature_flags;
}
CPUID(0x80000002, ci->ci_brand[0],
ci->ci_brand[1], ci->ci_brand[2], ci->ci_brand[3]);
CPUID(0x80000003, ci->ci_brand[4],
ci->ci_brand[5], ci->ci_brand[6], ci->ci_brand[7]);
CPUID(0x80000004, ci->ci_brand[8],
ci->ci_brand[9], ci->ci_brand[10], ci->ci_brand[11]);
strlcpy(mycpu_model, (char *)ci->ci_brand, sizeof(mycpu_model));
/* Remove leading, trailing and duplicated spaces from mycpu_model */
brandstr_from = brandstr_to = mycpu_model;
skipspace = 1;
while (*brandstr_from != '\0') {
if (!skipspace || *brandstr_from != ' ') {
skipspace = 0;
*(brandstr_to++) = *brandstr_from;
}
if (*brandstr_from == ' ')
skipspace = 1;
brandstr_from++;
}
if (skipspace && brandstr_to > mycpu_model)
brandstr_to--;
*brandstr_to = '\0';
if (mycpu_model[0] == 0)
strlcpy(mycpu_model, "Opteron or Athlon 64",
sizeof(mycpu_model));
/* If primary cpu, fill in the global cpu_model used by sysctl */
if (CPU_IS_PRIMARY(ci))
strlcpy(cpu_model, mycpu_model, sizeof(cpu_model));
ci->ci_family = (ci->ci_signature >> 8) & 0x0f;
ci->ci_model = (ci->ci_signature >> 4) & 0x0f;
if (ci->ci_family == 0x6 || ci->ci_family == 0xf) {
ci->ci_family += (ci->ci_signature >> 20) & 0xff;
ci->ci_model += ((ci->ci_signature >> 16) & 0x0f) << 4;
}
#if NPVBUS > 0
/* Detect hypervisors early, attach the paravirtual bus later */
if (CPU_IS_PRIMARY(ci) && cpu_ecxfeature & CPUIDECX_HV)
pvbus_identify();
#endif
if (ci->ci_feature_flags && ci->ci_feature_flags & CPUID_TSC) {
/* Has TSC, check if it's constant */
if (!strcmp(cpu_vendor, "GenuineIntel")) {
if ((ci->ci_family == 0x0f && ci->ci_model >= 0x03) ||
(ci->ci_family == 0x06 && ci->ci_model >= 0x0e)) {
atomic_setbits_int(&ci->ci_flags, CPUF_CONST_TSC);
}
} else if (!strcmp(cpu_vendor, "CentaurHauls")) {
/* VIA */
if (ci->ci_model >= 0x0f) {
atomic_setbits_int(&ci->ci_flags, CPUF_CONST_TSC);
}
} else if (!strcmp(cpu_vendor, "AuthenticAMD")) {
if (cpu_apmi_edx & CPUIDEDX_ITSC) {
/* Invariant TSC indicates constant TSC on AMD */
atomic_setbits_int(&ci->ci_flags, CPUF_CONST_TSC);
}
}
/* Check if it's an invariant TSC */
if (cpu_apmi_edx & CPUIDEDX_ITSC)
atomic_setbits_int(&ci->ci_flags, CPUF_INVAR_TSC);
tsc_identify(ci);
}
freq = cpu_freq(ci);
printf("%s: %s", ci->ci_dev->dv_xname, mycpu_model);
if (freq != 0)
printf(", %llu.%02llu MHz", (freq + 4999) / 1000000,
((freq + 4999) / 10000) % 100);
if (CPU_IS_PRIMARY(ci)) {
cpuspeed = (freq + 4999) / 1000000;
cpu_cpuspeed = cpu_amd64speed;
}
printf(", %02x-%02x-%02x", ci->ci_family, ci->ci_model,
ci->ci_signature & 0x0f);
printf("\n%s: ", ci->ci_dev->dv_xname);
for (i = 0; i < nitems(cpu_cpuid_features); i++)
if (ci->ci_feature_flags & cpu_cpuid_features[i].bit)
printf("%s%s", i? "," : "", cpu_cpuid_features[i].str);
for (i = 0; i < nitems(cpu_cpuid_ecxfeatures); i++)
if (cpu_ecxfeature & cpu_cpuid_ecxfeatures[i].bit)
printf(",%s", cpu_cpuid_ecxfeatures[i].str);
for (i = 0; i < nitems(cpu_ecpuid_features); i++)
if (ci->ci_feature_eflags & cpu_ecpuid_features[i].bit)
printf(",%s", cpu_ecpuid_features[i].str);
for (i = 0; i < nitems(cpu_ecpuid_ecxfeatures); i++)
if (ecpu_ecxfeature & cpu_ecpuid_ecxfeatures[i].bit)
printf(",%s", cpu_ecpuid_ecxfeatures[i].str);
for (i = 0; i < nitems(cpu_cpuid_perf_eax); i++)
if (cpu_perf_eax & cpu_cpuid_perf_eax[i].bit)
printf(",%s", cpu_cpuid_perf_eax[i].str);
for (i = 0; i < nitems(cpu_cpuid_apmi_edx); i++)
if (cpu_apmi_edx & cpu_cpuid_apmi_edx[i].bit)
printf(",%s", cpu_cpuid_apmi_edx[i].str);
if (cpuid_level >= 0x07) {
/* "Structured Extended Feature Flags" */
CPUID_LEAF(0x7, 0, dummy, ci->ci_feature_sefflags_ebx,
ci->ci_feature_sefflags_ecx, ci->ci_feature_sefflags_edx);
for (i = 0; i < nitems(cpu_seff0_ebxfeatures); i++)
if (ci->ci_feature_sefflags_ebx &
cpu_seff0_ebxfeatures[i].bit)
printf(",%s", cpu_seff0_ebxfeatures[i].str);
for (i = 0; i < nitems(cpu_seff0_ecxfeatures); i++)
if (ci->ci_feature_sefflags_ecx &
cpu_seff0_ecxfeatures[i].bit)
printf(",%s", cpu_seff0_ecxfeatures[i].str);
for (i = 0; i < nitems(cpu_seff0_edxfeatures); i++)
if (ci->ci_feature_sefflags_edx &
cpu_seff0_edxfeatures[i].bit)
printf(",%s", cpu_seff0_edxfeatures[i].str);
}
if (!strcmp(cpu_vendor, "GenuineIntel") && cpuid_level >= 0x06) {
CPUID(0x06, ci->ci_feature_tpmflags, dummy, cpu_tpm_ecxflags,
dummy);
for (i = 0; i < nitems(cpu_tpm_eaxfeatures); i++)
if (ci->ci_feature_tpmflags &
cpu_tpm_eaxfeatures[i].bit)
printf(",%s", cpu_tpm_eaxfeatures[i].str);
} else if (!strcmp(cpu_vendor, "AuthenticAMD")) {
CPUID(0x06, ci->ci_feature_tpmflags, dummy, cpu_tpm_ecxflags,
dummy);
if (ci->ci_family >= 0x12)
ci->ci_feature_tpmflags |= TPM_ARAT;
}
/* AMD speculation control features */
if (!strcmp(cpu_vendor, "AuthenticAMD")) {
if (ci->ci_pnfeatset >= 0x80000008) {
CPUID(0x80000008, dummy, ci->ci_feature_amdspec_ebx,
dummy, dummy);
for (i = 0; i < nitems(cpu_amdspec_ebxfeatures); i++)
if (ci->ci_feature_amdspec_ebx &
cpu_amdspec_ebxfeatures[i].bit)
printf(",%s",
cpu_amdspec_ebxfeatures[i].str);
}
}
/* xsave subfeatures */
if (cpuid_level >= 0xd) {
CPUID_LEAF(0xd, 1, val, dummy, dummy, dummy);
for (i = 0; i < nitems(cpu_xsave_extfeatures); i++)
if (val & cpu_xsave_extfeatures[i].bit)
printf(",%s", cpu_xsave_extfeatures[i].str);
}
if (cpu_meltdown)
printf(",MELTDOWN");
printf("\n");
replacemeltdown();
x86_print_cacheinfo(ci);
/*
* "Mitigation G-2" per AMD's Whitepaper "Software Techniques
* for Managing Speculation on AMD Processors"
*
* By setting MSR C001_1029[1]=1, LFENCE becomes a dispatch
* serializing instruction.
*
* This MSR is available on all AMD families >= 10h, except 11h
* where LFENCE is always serializing.
*/
if (!strcmp(cpu_vendor, "AuthenticAMD")) {
if (ci->ci_family >= 0x10 && ci->ci_family != 0x11) {
uint64_t msr;
msr = rdmsr(MSR_DE_CFG);
if ((msr & DE_CFG_SERIALIZE_LFENCE) == 0) {
msr |= DE_CFG_SERIALIZE_LFENCE;
wrmsr(MSR_DE_CFG, msr);
}
}
}
/*
* Attempt to disable Silicon Debug and lock the configuration
* if it's enabled and unlocked.
*/
if (!strcmp(cpu_vendor, "GenuineIntel") &&
(cpu_ecxfeature & CPUIDECX_SDBG)) {
uint64_t msr;
msr = rdmsr(IA32_DEBUG_INTERFACE);
if ((msr & IA32_DEBUG_INTERFACE_ENABLE) &&
(msr & IA32_DEBUG_INTERFACE_LOCK) == 0) {
msr &= IA32_DEBUG_INTERFACE_MASK;
msr |= IA32_DEBUG_INTERFACE_LOCK;
wrmsr(IA32_DEBUG_INTERFACE, msr);
} else if (msr & IA32_DEBUG_INTERFACE_ENABLE)
printf("%s: cannot disable silicon debug\n",
ci->ci_dev->dv_xname);
}
if (CPU_IS_PRIMARY(ci)) {
#ifndef SMALL_KERNEL
if (!strcmp(cpu_vendor, "AuthenticAMD") &&
ci->ci_pnfeatset >= 0x80000007) {
CPUID(0x80000007, dummy, dummy, dummy, val);
if (val & 0x06) {
if ((ci->ci_signature & 0xF00) == 0xF00)
setperf_setup = k8_powernow_init;
}
if (ci->ci_family >= 0x10)
setperf_setup = k1x_init;
}
if (cpu_ecxfeature & CPUIDECX_EST)
setperf_setup = est_init;
#endif
if (cpu_ecxfeature & CPUIDECX_RDRAND)
has_rdrand = 1;
if (ci->ci_feature_sefflags_ebx & SEFF0EBX_RDSEED)
has_rdseed = 1;
if (ci->ci_feature_sefflags_ebx & SEFF0EBX_SMAP)
replacesmap();
}
if (ci->ci_feature_flags & CPUID_CFLUSH) {
u_int32_t cflushsz;
CPUID(0x01, dummy, cflushsz, dummy, dummy);
/* cflush cacheline size is equal to bits 15-8 of ebx * 8 */
ci->ci_cflushsz = ((cflushsz >> 8) & 0xff) * 8;
}
#ifndef SMALL_KERNEL
if (CPU_IS_PRIMARY(ci) && (ci->ci_feature_tpmflags & TPM_SENSOR)) {
ci->ci_sensor.type = SENSOR_TEMP;
sensor_task_register(ci, intelcore_update_sensor, 5);
sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
}
#endif
#ifdef CRYPTO
if (CPU_IS_PRIMARY(ci)) {
if (cpu_ecxfeature & CPUIDECX_PCLMUL)
amd64_has_pclmul = 1;
if (cpu_ecxfeature & CPUIDECX_AES)
amd64_has_aesni = 1;
}
#endif
if (!strcmp(cpu_vendor, "AuthenticAMD"))
amd64_errata(ci);
if (CPU_IS_PRIMARY(ci) && !strcmp(cpu_vendor, "CentaurHauls")) {
ci->cpu_setup = via_nano_setup;
#ifndef SMALL_KERNEL
ci->ci_sensor.type = SENSOR_TEMP;
sensor_task_register(ci, via_update_sensor, 5);
sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
#endif
}
tsc_timecounter_init(ci, freq);
cpu_topology(ci);
#if NVMM > 0
cpu_check_vmm_cap(ci);
#endif /* NVMM > 0 */
/* Check for effective frequency via MPERF, APERF */
if ((cpu_tpm_ecxflags & TPM_EFFFREQ) && ci->ci_smt_id == 0) {
#ifndef SMALL_KERNEL
ci->ci_hz_sensor.type = SENSOR_FREQ;
sensor_task_register(ci, cpu_hz_update_sensor, 1);
sensor_attach(&ci->ci_sensordev, &ci->ci_hz_sensor);
#endif
}
}
#ifndef SMALL_KERNEL
/*
* Base 2 logarithm of an int. returns 0 for 0 (yeye, I know).
*/
static int
log2(unsigned int i)
{
int ret = 0;
while (i >>= 1)
ret++;
return (ret);
}
static int
mask_width(u_int x)
{
int bit;
int mask;
int powerof2;
powerof2 = ((x - 1) & x) == 0;
mask = (x << (1 - powerof2)) - 1;
/* fls */
if (mask == 0)
return (0);
for (bit = 1; mask != 1; bit++)
mask = (unsigned int)mask >> 1;
return (bit);
}
#endif
/*
* Build up cpu topology for given cpu, must run on the core itself.
*/
void
cpu_topology(struct cpu_info *ci)
{
#ifndef SMALL_KERNEL
u_int32_t eax, ebx, ecx, edx;
u_int32_t apicid, max_apicid = 0, max_coreid = 0;
u_int32_t smt_bits = 0, core_bits, pkg_bits = 0;
u_int32_t smt_mask = 0, core_mask, pkg_mask = 0;
/* We need at least apicid at CPUID 1 */
if (cpuid_level < 1)
goto no_topology;
/* Initial apicid */
CPUID(1, eax, ebx, ecx, edx);
apicid = (ebx >> 24) & 0xff;
if (strcmp(cpu_vendor, "AuthenticAMD") == 0) {
uint32_t nthreads = 1; /* per core */
uint32_t thread_id; /* within a package */
/* We need at least apicid at CPUID 0x80000008 */
if (ci->ci_pnfeatset < 0x80000008)
goto no_topology;
CPUID(0x80000008, eax, ebx, ecx, edx);
core_bits = (ecx >> 12) & 0xf;
if (ci->ci_pnfeatset >= 0x8000001e) {
CPUID(0x8000001e, eax, ebx, ecx, edx);
nthreads = ((ebx >> 8) & 0xf) + 1;
}
/* Shift the core_bits off to get at the pkg bits */
ci->ci_pkg_id = apicid >> core_bits;
/* Get rid of the package bits */
core_mask = (1U << core_bits) - 1;
thread_id = apicid & core_mask;
/* Cut logical thread_id into core id, and smt id in a core */
ci->ci_core_id = thread_id / nthreads;
ci->ci_smt_id = thread_id % nthreads;
} else if (strcmp(cpu_vendor, "GenuineIntel") == 0) {
/* We only support leaf 1/4 detection */
if (cpuid_level < 4)
goto no_topology;
/* Get max_apicid */
CPUID(1, eax, ebx, ecx, edx);
max_apicid = (ebx >> 16) & 0xff;
/* Get max_coreid */
CPUID_LEAF(4, 0, eax, ebx, ecx, edx);
max_coreid = ((eax >> 26) & 0x3f) + 1;
/* SMT */
smt_bits = mask_width(max_apicid / max_coreid);
smt_mask = (1U << smt_bits) - 1;
/* Core */
core_bits = log2(max_coreid);
core_mask = (1U << (core_bits + smt_bits)) - 1;
core_mask ^= smt_mask;
/* Pkg */
pkg_bits = core_bits + smt_bits;
pkg_mask = ~0U << core_bits;
ci->ci_smt_id = apicid & smt_mask;
ci->ci_core_id = (apicid & core_mask) >> smt_bits;
ci->ci_pkg_id = (apicid & pkg_mask) >> pkg_bits;
} else
goto no_topology;
#ifdef DEBUG
printf("cpu%d: smt %u, core %u, pkg %u "
"(apicid 0x%x, max_apicid 0x%x, max_coreid 0x%x, smt_bits 0x%x, smt_mask 0x%x, "
"core_bits 0x%x, core_mask 0x%x, pkg_bits 0x%x, pkg_mask 0x%x)\n",
ci->ci_cpuid, ci->ci_smt_id, ci->ci_core_id, ci->ci_pkg_id,
apicid, max_apicid, max_coreid, smt_bits, smt_mask, core_bits,
core_mask, pkg_bits, pkg_mask);
#else
printf("cpu%d: smt %u, core %u, package %u\n", ci->ci_cpuid,
ci->ci_smt_id, ci->ci_core_id, ci->ci_pkg_id);
#endif
return;
/* We can't map, so consider ci_core_id as ci_cpuid */
no_topology:
#endif
ci->ci_smt_id = 0;
ci->ci_core_id = ci->ci_cpuid;
ci->ci_pkg_id = 0;
}
#if NVMM > 0
/*
* cpu_check_vmm_cap
*
* Checks for VMM capabilities for 'ci'. Initializes certain per-cpu VMM
* state in 'ci' if virtualization extensions are found.
*
* Parameters:
* ci: the cpu being checked
*/
void
cpu_check_vmm_cap(struct cpu_info *ci)
{
uint64_t msr;
uint32_t cap, dummy, edx;
/*
* Check for workable VMX
*/
if (cpu_ecxfeature & CPUIDECX_VMX) {
msr = rdmsr(MSR_IA32_FEATURE_CONTROL);
if (!(msr & IA32_FEATURE_CONTROL_LOCK))
ci->ci_vmm_flags |= CI_VMM_VMX;
else {
if (msr & IA32_FEATURE_CONTROL_VMX_EN)
ci->ci_vmm_flags |= CI_VMM_VMX;
else
ci->ci_vmm_flags |= CI_VMM_DIS;
}
}
/*
* Check for EPT (Intel Nested Paging) and other secondary
* controls
*/
if (ci->ci_vmm_flags & CI_VMM_VMX) {
/* Secondary controls available? */
/* XXX should we check true procbased ctls here if avail? */
msr = rdmsr(IA32_VMX_PROCBASED_CTLS);
if (msr & (IA32_VMX_ACTIVATE_SECONDARY_CONTROLS) << 32) {
msr = rdmsr(IA32_VMX_PROCBASED2_CTLS);
/* EPT available? */
if (msr & (IA32_VMX_ENABLE_EPT) << 32)
ci->ci_vmm_flags |= CI_VMM_EPT;
/* VM Functions available? */
if (msr & (IA32_VMX_ENABLE_VM_FUNCTIONS) << 32) {
ci->ci_vmm_cap.vcc_vmx.vmx_vm_func =
rdmsr(IA32_VMX_VMFUNC);
}
}
}
/*
* Check startup config (VMX)
*/
if (ci->ci_vmm_flags & CI_VMM_VMX) {
/* CR0 fixed and flexible bits */
msr = rdmsr(IA32_VMX_CR0_FIXED0);
ci->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed0 = msr;
msr = rdmsr(IA32_VMX_CR0_FIXED1);
ci->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed1 = msr;
/* CR4 fixed and flexible bits */
msr = rdmsr(IA32_VMX_CR4_FIXED0);
ci->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0 = msr;
msr = rdmsr(IA32_VMX_CR4_FIXED1);
ci->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1 = msr;
/* VMXON region revision ID (bits 30:0 of IA32_VMX_BASIC) */
msr = rdmsr(IA32_VMX_BASIC);
ci->ci_vmm_cap.vcc_vmx.vmx_vmxon_revision =
(uint32_t)(msr & 0x7FFFFFFF);
/* MSR save / load table size */
msr = rdmsr(IA32_VMX_MISC);
ci->ci_vmm_cap.vcc_vmx.vmx_msr_table_size =
(uint32_t)(msr & IA32_VMX_MSR_LIST_SIZE_MASK) >> 25;
/* CR3 target count size */
ci->ci_vmm_cap.vcc_vmx.vmx_cr3_tgt_count =
(uint32_t)(msr & IA32_VMX_CR3_TGT_SIZE_MASK) >> 16;
}
/*
* Check for workable SVM
*/
if (ecpu_ecxfeature & CPUIDECX_SVM) {
msr = rdmsr(MSR_AMD_VM_CR);
if (!(msr & AMD_SVMDIS))
ci->ci_vmm_flags |= CI_VMM_SVM;
CPUID(CPUID_AMD_SVM_CAP, dummy,
ci->ci_vmm_cap.vcc_svm.svm_max_asid, dummy, edx);
if (ci->ci_vmm_cap.vcc_svm.svm_max_asid > 0xFFF)
ci->ci_vmm_cap.vcc_svm.svm_max_asid = 0xFFF;
if (edx & AMD_SVM_FLUSH_BY_ASID_CAP)
ci->ci_vmm_cap.vcc_svm.svm_flush_by_asid = 1;
if (edx & AMD_SVM_VMCB_CLEAN_CAP)
ci->ci_vmm_cap.vcc_svm.svm_vmcb_clean = 1;
if (edx & AMD_SVM_DECODE_ASSIST_CAP)
ci->ci_vmm_cap.vcc_svm.svm_decode_assist = 1;
}
/*
* Check for SVM Nested Paging
*/
if ((ci->ci_vmm_flags & CI_VMM_SVM) &&
ci->ci_pnfeatset >= CPUID_AMD_SVM_CAP) {
CPUID(CPUID_AMD_SVM_CAP, dummy, dummy, dummy, cap);
if (cap & AMD_SVM_NESTED_PAGING_CAP)
ci->ci_vmm_flags |= CI_VMM_RVI;
}
/*
* Check "L1 flush on VM entry" (Intel L1TF vuln) semantics
* Full details can be found here:
* https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-l1-terminal-fault
*/
if (!strcmp(cpu_vendor, "GenuineIntel")) {
if (ci->ci_feature_sefflags_edx & SEFF0EDX_L1DF)
ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr = 1;
else
ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr = 0;
/*
* Certain CPUs may have the vulnerability remedied in
* hardware (RDCL_NO), or we may be nested in an VMM that
* is doing flushes (SKIP_L1DFL_VMENTRY) using the MSR.
* In either case no mitigation at all is necessary.
*/
if (ci->ci_feature_sefflags_edx & SEFF0EDX_ARCH_CAP) {
msr = rdmsr(MSR_ARCH_CAPABILITIES);
if ((msr & ARCH_CAPABILITIES_RDCL_NO) ||
((msr & ARCH_CAPABILITIES_SKIP_L1DFL_VMENTRY) &&
ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr))
ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr =
VMX_SKIP_L1D_FLUSH;
}
}
}
#endif /* NVMM > 0 */
21
2
7
8
1
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* $OpenBSD: db_usrreq.c,v 1.22 2021/01/09 20:58:12 gnezdo Exp $ */
/*
* Copyright (c) 1996 Michael Shalayeff. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/sysctl.h>
#include <dev/cons.h>
#include <ddb/db_var.h>
int db_log = 1;
int db_profile; /* Allow dynamic profiling */
const struct sysctl_bounded_args ddb_vars[] = {
{ DBCTL_RADIX, &db_radix, 8, 16 },
{ DBCTL_MAXWIDTH, &db_max_width, 0, INT_MAX },
{ DBCTL_TABSTOP, &db_tab_stop_width, 1, 16 },
{ DBCTL_MAXLINE, &db_max_line, 0, INT_MAX },
{ DBCTL_LOG, &db_log, 0, 1 },
};
int
ddb_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case DBCTL_PANIC:
if (securelevel > 0)
return (sysctl_int_lower(oldp, oldlenp, newp, newlen,
&db_panic));
else {
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&db_panic, 0, 1));
}
break;
case DBCTL_CONSOLE:
if (securelevel > 0)
return (sysctl_int_lower(oldp, oldlenp, newp, newlen,
&db_console));
else {
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&db_console, 0, 1));
}
break;
case DBCTL_TRIGGER:
if (newp && db_console) {
struct process *pr = curproc->p_p;
if (securelevel < 1 ||
(pr->ps_flags & PS_CONTROLT && cn_tab &&
cn_tab->cn_dev == pr->ps_session->s_ttyp->t_dev)) {
db_enter();
newp = NULL;
} else
return (ENODEV);
}
return (sysctl_rdint(oldp, oldlenp, newp, 0));
#if defined(DDBPROF)
case DBCTL_PROFILE:
if (securelevel > 0)
return (sysctl_int_lower(oldp, oldlenp, newp, newlen,
&db_profile));
else {
return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&db_profile, 0, 1));
}
break;
#endif /* DDBPROF */
default:
return (sysctl_bounded_arr(ddb_vars, nitems(ddb_vars), name,
namelen, oldp, oldlenp, newp, newlen));
}
/* NOTREACHED */
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/* $OpenBSD: ip6_var.h,v 1.102 2022/09/03 22:43:38 mvs Exp $ */
/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_var.h 8.1 (Berkeley) 6/10/93
*/
#ifndef _NETINET6_IP6_VAR_H_
#define _NETINET6_IP6_VAR_H_
/*
* IP6 reassembly queue structure. Each fragment
* being reassembled is attached to one of these structures.
*/
struct ip6q {
TAILQ_ENTRY(ip6q) ip6q_queue;
LIST_HEAD(ip6asfrag_list, ip6asfrag) ip6q_asfrag;
struct in6_addr ip6q_src, ip6q_dst;
int ip6q_unfrglen; /* len of unfragmentable part */
int ip6q_nfrag; /* # of fragments */
u_int32_t ip6q_ident; /* fragment identification */
u_int8_t ip6q_nxt; /* ip6f_nxt in first fragment */
u_int8_t ip6q_ecn;
u_int8_t ip6q_ttl; /* time to live in slowtimo units */
};
struct ip6asfrag {
LIST_ENTRY(ip6asfrag) ip6af_list;
struct mbuf *ip6af_m;
int ip6af_offset; /* offset in ip6af_m to next header */
int ip6af_frglen; /* fragmentable part length */
int ip6af_off; /* fragment offset */
u_int16_t ip6af_mff; /* more fragment bit in frag off */
};
struct ip6_moptions {
LIST_HEAD(, in6_multi_mship) im6o_memberships;
unsigned short im6o_ifidx; /* ifp index for outgoing multicasts */
u_char im6o_hlim; /* hoplimit for outgoing multicasts */
u_char im6o_loop; /* 1 >= hear sends if a member */
};
/*
* Control options for outgoing packets
*/
/* Routing header related info */
struct ip6po_rhinfo {
struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */
struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */
};
#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr
#define ip6po_route ip6po_rhinfo.ip6po_rhi_route
struct ip6_pktopts {
/* Hoplimit for outgoing packets */
int ip6po_hlim;
/* Outgoing IF/address information */
struct in6_pktinfo *ip6po_pktinfo;
/* Hop-by-Hop options header */
struct ip6_hbh *ip6po_hbh;
/* Destination options header (before a routing header) */
struct ip6_dest *ip6po_dest1;
/* Routing header related info. */
struct ip6po_rhinfo ip6po_rhinfo;
/* Destination options header (after a routing header) */
struct ip6_dest *ip6po_dest2;
/* traffic class */
int ip6po_tclass;
/* fragment vs PMTU discovery policy */
int ip6po_minmtu;
#define IP6PO_MINMTU_MCASTONLY -1 /* default: send at min MTU for multicast */
#define IP6PO_MINMTU_DISABLE 0 /* always perform pmtu disc */
#define IP6PO_MINMTU_ALL 1 /* always send at min MTU */
int ip6po_flags;
#define IP6PO_DONTFRAG 0x04 /* disable fragmentation (IPV6_DONTFRAG) */
};
struct ip6stat {
u_int64_t ip6s_total; /* total packets received */
u_int64_t ip6s_tooshort; /* packet too short */
u_int64_t ip6s_toosmall; /* not enough data */
u_int64_t ip6s_fragments; /* fragments received */
u_int64_t ip6s_fragdropped; /* frags dropped(dups, out of space) */
u_int64_t ip6s_fragtimeout; /* fragments timed out */
u_int64_t ip6s_fragoverflow; /* fragments that exceeded limit */
u_int64_t ip6s_forward; /* packets forwarded */
u_int64_t ip6s_cantforward; /* packets rcvd for unreachable dest */
u_int64_t ip6s_redirectsent; /* packets forwarded on same net */
u_int64_t ip6s_delivered; /* datagrams delivered to upper level*/
u_int64_t ip6s_localout; /* total ip packets generated here */
u_int64_t ip6s_odropped; /* lost output due to nobufs, etc. */
u_int64_t ip6s_reassembled; /* total packets reassembled ok */
u_int64_t ip6s_fragmented; /* datagrams successfully fragmented */
u_int64_t ip6s_ofragments; /* output fragments created */
u_int64_t ip6s_cantfrag; /* don't fragment flag was set, etc. */
u_int64_t ip6s_badoptions; /* error in option processing */
u_int64_t ip6s_noroute; /* packets discarded due to no route */
u_int64_t ip6s_badvers; /* ip6 version != 6 */
u_int64_t ip6s_rawout; /* total raw ip packets generated */
u_int64_t ip6s_badscope; /* scope error */
u_int64_t ip6s_notmember; /* don't join this multicast group */
u_int64_t ip6s_nxthist[256]; /* next header history */
u_int64_t ip6s_m1; /* one mbuf */
u_int64_t ip6s_m2m[32]; /* two or more mbuf */
u_int64_t ip6s_mext1; /* one ext mbuf */
u_int64_t ip6s_mext2m; /* two or more ext mbuf */
u_int64_t ip6s_nogif; /* no match gif found */
u_int64_t ip6s_toomanyhdr; /* discarded due to too many headers */
/*
* statistics for improvement of the source address selection
* algorithm:
* XXX: hardcoded 16 = # of ip6 multicast scope types + 1
*/
/* number of times that address selection fails */
u_int64_t ip6s_sources_none;
/* number of times that an address on the outgoing I/F is chosen */
u_int64_t ip6s_sources_sameif[16];
/* number of times that an address on a non-outgoing I/F is chosen */
u_int64_t ip6s_sources_otherif[16];
/*
* number of times that an address that has the same scope
* from the destination is chosen.
*/
u_int64_t ip6s_sources_samescope[16];
/*
* number of times that an address that has a different scope
* from the destination is chosen.
*/
u_int64_t ip6s_sources_otherscope[16];
/* number of times that an deprecated address is chosen */
u_int64_t ip6s_sources_deprecated[16];
u_int64_t ip6s_forward_cachehit;
u_int64_t ip6s_forward_cachemiss;
u_int64_t ip6s_wrongif; /* packet received on wrong interface */
u_int64_t ip6s_idropped; /* lost input due to nobufs, etc. */
};
#ifdef _KERNEL
#include <sys/percpu.h>
enum ip6stat_counters {
ip6s_total,
ip6s_tooshort,
ip6s_toosmall,
ip6s_fragments,
ip6s_fragdropped,
ip6s_fragtimeout,
ip6s_fragoverflow,
ip6s_forward,
ip6s_cantforward,
ip6s_redirectsent,
ip6s_delivered,
ip6s_localout,
ip6s_odropped,
ip6s_reassembled,
ip6s_fragmented,
ip6s_ofragments,
ip6s_cantfrag,
ip6s_badoptions,
ip6s_noroute,
ip6s_badvers,
ip6s_rawout,
ip6s_badscope,
ip6s_notmember,
ip6s_nxthist,
ip6s_m1 = ip6s_nxthist + 256,
ip6s_m2m,
ip6s_mext1 = ip6s_m2m + 32,
ip6s_mext2m,
ip6s_nogif,
ip6s_toomanyhdr,
ip6s_sources_none,
ip6s_sources_sameif,
ip6s_sources_otherif = ip6s_sources_sameif + 16,
ip6s_sources_samescope = ip6s_sources_otherif + 16,
ip6s_sources_otherscope = ip6s_sources_samescope + 16,
ip6s_sources_deprecated = ip6s_sources_otherscope + 16,
ip6s_forward_cachehit = ip6s_sources_deprecated + 16,
ip6s_forward_cachemiss,
ip6s_wrongif,
ip6s_idropped,
ip6s_ncounters,
};
extern struct cpumem *ip6counters;
static inline void
ip6stat_inc(enum ip6stat_counters c)
{
counters_inc(ip6counters, c);
}
static inline void
ip6stat_add(enum ip6stat_counters c, uint64_t v)
{
counters_add(ip6counters, c, v);
}
/* flags passed to ip6_output as last parameter */
#define IPV6_UNSPECSRC 0x01 /* allow :: as the source address */
#define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */
#define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */
extern int ip6_mtudisc_timeout; /* mtu discovery */
extern struct rttimer_queue icmp6_mtudisc_timeout_q;
extern int ip6_defhlim; /* default hop limit */
extern int ip6_defmcasthlim; /* default multicast hop limit */
extern int ip6_forwarding; /* act as router? */
extern int ip6_mforwarding; /* act as multicast router? */
extern int ip6_multipath; /* use multipath routes */
extern int ip6_sendredirect; /* send ICMPv6 redirect? */
extern int ip6_use_deprecated; /* allow deprecated addr as source */
extern int ip6_mcast_pmtu; /* path MTU discovery for multicast */
extern int ip6_neighborgcthresh; /* Threshold # of NDP entries for GC */
extern int ip6_maxdynroutes; /* Max # of routes created via redirect */
extern struct socket *ip6_mrouter[RT_TABLEID_MAX + 1]; /* multicast routing daemon */
extern int ip6_sendredirects; /* send IP redirects when forwarding? */
extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */
extern int ip6_maxfrags; /* Maximum fragments in reassembly queue */
extern int ip6_log_interval;
extern time_t ip6_log_time;
extern int ip6_hdrnestlimit; /* upper limit of # of extension headers */
extern int ip6_dad_count; /* DupAddrDetectionTransmits */
extern int ip6_dad_pending; /* number of currently running DADs */
extern int ip6_auto_flowlabel;
extern int ip6_auto_linklocal;
#define IP6_SOIIKEY_LEN 16
extern uint8_t ip6_soiikey[IP6_SOIIKEY_LEN];
extern const struct pr_usrreqs rip6_usrreqs;
struct in6pcb;
struct inpcb;
int icmp6_ctloutput(int, struct socket *, int, int, struct mbuf *);
void ip6_init(void);
void ip6intr(void);
int ip6_input_if(struct mbuf **, int *, int, int, struct ifnet *);
void ip6_freepcbopts(struct ip6_pktopts *);
void ip6_freemoptions(struct ip6_moptions *);
int ip6_unknown_opt(struct mbuf **, u_int8_t *, int);
int ip6_get_prevhdr(struct mbuf *, int);
int ip6_nexthdr(struct mbuf *, int, int, int *);
int ip6_lasthdr(struct mbuf *, int, int, int *);
int ip6_mforward(struct ip6_hdr *, struct ifnet *, struct mbuf *);
int ip6_process_hopopts(struct mbuf **, u_int8_t *, int, u_int32_t *,
u_int32_t *);
void ip6_savecontrol(struct inpcb *, struct mbuf *, struct mbuf **);
int ip6_sysctl(int *, u_int, void *, size_t *, void *, size_t);
void ip6_forward(struct mbuf *, struct rtentry *, int);
void ip6_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in6 *);
int ip6_output(struct mbuf *, struct ip6_pktopts *, struct route_in6 *, int,
struct ip6_moptions *, struct inpcb *);
int ip6_fragment(struct mbuf *, struct mbuf_list *, int, u_char, u_long);
int ip6_ctloutput(int, struct socket *, int, int, struct mbuf *);
int ip6_raw_ctloutput(int, struct socket *, int, int, struct mbuf *);
void ip6_initpktopts(struct ip6_pktopts *);
int ip6_setpktopts(struct mbuf *, struct ip6_pktopts *,
struct ip6_pktopts *, int, int);
void ip6_clearpktopts(struct ip6_pktopts *, int);
void ip6_randomid_init(void);
u_int32_t ip6_randomid(void);
void ip6_send(struct mbuf *);
int route6_input(struct mbuf **, int *, int, int);
void frag6_init(void);
int frag6_input(struct mbuf **, int *, int, int);
int frag6_deletefraghdr(struct mbuf *, int);
void frag6_slowtimo(void);
void rip6_init(void);
int rip6_input(struct mbuf **, int *, int, int);
void rip6_ctlinput(int, struct sockaddr *, u_int, void *);
int rip6_ctloutput(int, struct socket *, int, int, struct mbuf *);
int rip6_output(struct mbuf *, struct socket *, struct sockaddr *,
struct mbuf *);
int rip6_attach(struct socket *, int);
int rip6_detach(struct socket *);
int rip6_bind(struct socket *, struct mbuf *, struct proc *);
int rip6_connect(struct socket *, struct mbuf *);
int rip6_disconnect(struct socket *);
int rip6_shutdown(struct socket *);
int rip6_send(struct socket *, struct mbuf *, struct mbuf *,
struct mbuf *);
int rip6_abort(struct socket *);
int rip6_sysctl(int *, u_int, void *, size_t *, void *, size_t);
int dest6_input(struct mbuf **, int *, int, int);
int none_input(struct mbuf **, int *, int);
int in6_pcbselsrc(struct in6_addr **, struct sockaddr_in6 *,
struct inpcb *, struct ip6_pktopts *);
int in6_selectsrc(struct in6_addr **, struct sockaddr_in6 *,
struct ip6_moptions *, unsigned int);
struct rtentry *in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
struct route_in6 *, unsigned int rtableid);
u_int32_t ip6_randomflowlabel(void);
#ifdef IPSEC
struct tdb;
int ip6_output_ipsec_lookup(struct mbuf *, struct inpcb *, struct tdb **);
int ip6_output_ipsec_send(struct tdb *, struct mbuf *, struct route_in6 *,
int, int);
#endif /* IPSEC */
#endif /* _KERNEL */
#endif /* !_NETINET6_IP6_VAR_H_ */
2
1
2
225
228
210
3
2
215
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
/* $OpenBSD: ip_spd.c,v 1.117 2022/06/17 13:40:21 bluhm Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
*
* Copyright (c) 2000-2001 Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/socketvar.h>
#include <sys/pool.h>
#include <sys/timeout.h>
#include <net/route.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_ipsp.h>
#include <net/pfkeyv2.h>
int ipsp_spd_inp(struct mbuf *, struct inpcb *, struct ipsec_policy *,
struct tdb **);
int ipsp_acquire_sa(struct ipsec_policy *, union sockaddr_union *,
union sockaddr_union *, struct sockaddr_encap *, struct mbuf *);
int ipsp_pending_acquire(struct ipsec_policy *, union sockaddr_union *);
void ipsp_delete_acquire_timer(void *);
void ipsp_delete_acquire_locked(struct ipsec_acquire *);
void ipsp_delete_acquire(struct ipsec_acquire *);
void ipsp_unref_acquire_locked(struct ipsec_acquire *);
struct pool ipsec_policy_pool;
struct pool ipsec_acquire_pool;
/*
* For tdb_walk() calling tdb_delete_locked() we need lock order
* tdb_sadb_mtx before ipo_tdb_mtx.
*/
struct mutex ipo_tdb_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
/* Protected by the NET_LOCK(). */
struct radix_node_head **spd_tables;
unsigned int spd_table_max;
struct mutex ipsec_acquire_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct ipsec_acquire_head ipsec_acquire_head =
TAILQ_HEAD_INITIALIZER(ipsec_acquire_head);
struct radix_node_head *
spd_table_get(unsigned int rtableid)
{
unsigned int rdomain;
NET_ASSERT_LOCKED();
if (spd_tables == NULL)
return (NULL);
rdomain = rtable_l2(rtableid);
if (rdomain > spd_table_max)
return (NULL);
return (spd_tables[rdomain]);
}
struct radix_node_head *
spd_table_add(unsigned int rtableid)
{
struct radix_node_head *rnh = NULL;
unsigned int rdomain;
void *p;
NET_ASSERT_LOCKED();
rdomain = rtable_l2(rtableid);
if (spd_tables == NULL || rdomain > spd_table_max) {
if ((p = mallocarray(rdomain + 1, sizeof(*rnh),
M_RTABLE, M_NOWAIT|M_ZERO)) == NULL)
return (NULL);
if (spd_tables != NULL) {
memcpy(p, spd_tables, sizeof(*rnh) * (spd_table_max+1));
free(spd_tables, M_RTABLE,
sizeof(*rnh) * (spd_table_max+1));
}
spd_tables = p;
spd_table_max = rdomain;
}
if (spd_tables[rdomain] == NULL) {
if (rn_inithead((void **)&rnh,
offsetof(struct sockaddr_encap, sen_type)) == 0)
rnh = NULL;
spd_tables[rdomain] = rnh;
}
return (spd_tables[rdomain]);
}
int
spd_table_walk(unsigned int rtableid,
int (*func)(struct ipsec_policy *, void *, unsigned int), void *arg)
{
struct radix_node_head *rnh;
int (*walker)(struct radix_node *, void *, u_int) = (void *)func;
int error;
rnh = spd_table_get(rtableid);
if (rnh == NULL)
return (0);
/* EGAIN means the tree changed. */
while ((error = rn_walktree(rnh, walker, arg)) == EAGAIN)
continue;
return (error);
}
/*
* Lookup at the SPD based on the headers contained on the mbuf. The second
* argument indicates what protocol family the header at the beginning of
* the mbuf is. hlen is the offset of the transport protocol header
* in the mbuf.
*
* Return combinations (of return value and *tdbout):
* - -EINVAL -> silently drop the packet
* - errno -> drop packet and return error
* - 0/NULL -> no IPsec required on packet
* - 0/TDB -> do IPsec
*
* In the case of incoming flows, only the first three combinations are
* returned.
*/
int
ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int direction,
struct tdb *tdbin, struct inpcb *inp, struct tdb **tdbout,
struct ipsec_ids *ipsecflowinfo_ids)
{
struct radix_node_head *rnh;
struct radix_node *rn;
union sockaddr_union sdst, ssrc;
struct sockaddr_encap *ddst, dst;
struct ipsec_policy *ipo;
struct ipsec_ids *ids = NULL;
int error, signore = 0, dignore = 0;
u_int rdomain;
NET_ASSERT_LOCKED();
/*
* If there are no flows in place, there's no point
* continuing with the SPD lookup.
*/
if (!ipsec_in_use)
return ipsp_spd_inp(m, inp, NULL, tdbout);
/*
* If an input packet is destined to a BYPASS socket, just accept it.
*/
if ((inp != NULL) && (direction == IPSP_DIRECTION_IN) &&
(inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_BYPASS) &&
(inp->inp_seclevel[SL_ESP_NETWORK] == IPSEC_LEVEL_BYPASS) &&
(inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_BYPASS)) {
if (tdbout != NULL)
*tdbout = NULL;
return 0;
}
memset(&dst, 0, sizeof(dst));
memset(&sdst, 0, sizeof(union sockaddr_union));
memset(&ssrc, 0, sizeof(union sockaddr_union));
ddst = (struct sockaddr_encap *)&dst;
ddst->sen_family = PF_KEY;
ddst->sen_len = SENT_LEN;
switch (af) {
case AF_INET:
if (hlen < sizeof (struct ip) || m->m_pkthdr.len < hlen)
return EINVAL;
ddst->sen_direction = direction;
ddst->sen_type = SENT_IP4;
m_copydata(m, offsetof(struct ip, ip_src),
sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_src));
m_copydata(m, offsetof(struct ip, ip_dst),
sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_dst));
m_copydata(m, offsetof(struct ip, ip_p), sizeof(u_int8_t),
(caddr_t) &(ddst->sen_proto));
sdst.sin.sin_family = ssrc.sin.sin_family = AF_INET;
sdst.sin.sin_len = ssrc.sin.sin_len =
sizeof(struct sockaddr_in);
ssrc.sin.sin_addr = ddst->sen_ip_src;
sdst.sin.sin_addr = ddst->sen_ip_dst;
/*
* If TCP/UDP, extract the port numbers to use in the lookup.
*/
switch (ddst->sen_proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
/* Make sure there's enough data in the packet. */
if (m->m_pkthdr.len < hlen + 2 * sizeof(u_int16_t))
return EINVAL;
/*
* Luckily, the offset of the src/dst ports in
* both the UDP and TCP headers is the same (first
* two 16-bit values in the respective headers),
* so we can just copy them.
*/
m_copydata(m, hlen, sizeof(u_int16_t),
(caddr_t) &(ddst->sen_sport));
m_copydata(m, hlen + sizeof(u_int16_t),
sizeof(u_int16_t),
(caddr_t) &(ddst->sen_dport));
break;
default:
ddst->sen_sport = 0;
ddst->sen_dport = 0;
}
break;
#ifdef INET6
case AF_INET6:
if (hlen < sizeof (struct ip6_hdr) || m->m_pkthdr.len < hlen)
return EINVAL;
ddst->sen_type = SENT_IP6;
ddst->sen_ip6_direction = direction;
m_copydata(m, offsetof(struct ip6_hdr, ip6_src),
sizeof(struct in6_addr),
(caddr_t) &(ddst->sen_ip6_src));
m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
sizeof(struct in6_addr),
(caddr_t) &(ddst->sen_ip6_dst));
m_copydata(m, offsetof(struct ip6_hdr, ip6_nxt),
sizeof(u_int8_t),
(caddr_t) &(ddst->sen_ip6_proto));
sdst.sin6.sin6_family = ssrc.sin6.sin6_family = AF_INET6;
sdst.sin6.sin6_len = ssrc.sin6.sin6_len =
sizeof(struct sockaddr_in6);
in6_recoverscope(&ssrc.sin6, &ddst->sen_ip6_src);
in6_recoverscope(&sdst.sin6, &ddst->sen_ip6_dst);
/*
* If TCP/UDP, extract the port numbers to use in the lookup.
*/
switch (ddst->sen_ip6_proto) {
case IPPROTO_UDP:
case IPPROTO_TCP:
/* Make sure there's enough data in the packet. */
if (m->m_pkthdr.len < hlen + 2 * sizeof(u_int16_t))
return EINVAL;
/*
* Luckily, the offset of the src/dst ports in
* both the UDP and TCP headers is the same
* (first two 16-bit values in the respective
* headers), so we can just copy them.
*/
m_copydata(m, hlen, sizeof(u_int16_t),
(caddr_t) &(ddst->sen_ip6_sport));
m_copydata(m, hlen + sizeof(u_int16_t),
sizeof(u_int16_t),
(caddr_t) &(ddst->sen_ip6_dport));
break;
default:
ddst->sen_ip6_sport = 0;
ddst->sen_ip6_dport = 0;
}
break;
#endif /* INET6 */
default:
return EAFNOSUPPORT;
}
/* Actual SPD lookup. */
rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
if ((rnh = spd_table_get(rdomain)) == NULL ||
(rn = rn_match((caddr_t)&dst, rnh)) == NULL) {
/*
* Return whatever the socket requirements are, there are no
* system-wide policies.
*/
return ipsp_spd_inp(m, inp, NULL, tdbout);
}
ipo = (struct ipsec_policy *)rn;
switch (ipo->ipo_type) {
case IPSP_PERMIT:
return ipsp_spd_inp(m, inp, ipo, tdbout);
case IPSP_DENY:
return EHOSTUNREACH;
case IPSP_IPSEC_USE:
case IPSP_IPSEC_ACQUIRE:
case IPSP_IPSEC_REQUIRE:
case IPSP_IPSEC_DONTACQ:
/* Nothing more needed here. */
break;
default:
return EINVAL;
}
/* Check for non-specific destination in the policy. */
switch (ipo->ipo_dst.sa.sa_family) {
case AF_INET:
if ((ipo->ipo_dst.sin.sin_addr.s_addr == INADDR_ANY) ||
(ipo->ipo_dst.sin.sin_addr.s_addr == INADDR_BROADCAST))
dignore = 1;
break;
#ifdef INET6
case AF_INET6:
if ((IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_dst.sin6.sin6_addr)) ||
(memcmp(&ipo->ipo_dst.sin6.sin6_addr, &in6mask128,
sizeof(in6mask128)) == 0))
dignore = 1;
break;
#endif /* INET6 */
}
/* Likewise for source. */
switch (ipo->ipo_src.sa.sa_family) {
case AF_INET:
if (ipo->ipo_src.sin.sin_addr.s_addr == INADDR_ANY)
signore = 1;
break;
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_src.sin6.sin6_addr))
signore = 1;
break;
#endif /* INET6 */
}
/* Do we have a cached entry ? If so, check if it's still valid. */
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL &&
(ipo->ipo_tdb->tdb_flags & TDBF_INVALID)) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
}
mtx_leave(&ipo_tdb_mtx);
/* Outgoing packet policy check. */
if (direction == IPSP_DIRECTION_OUT) {
/*
* If the packet is destined for the policy-specified
* gateway/endhost, and the socket has the BYPASS
* option set, skip IPsec processing.
*/
if ((inp != NULL) &&
(inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_BYPASS) &&
(inp->inp_seclevel[SL_ESP_NETWORK] ==
IPSEC_LEVEL_BYPASS) &&
(inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_BYPASS)) {
/* Direct match. */
if (dignore ||
!memcmp(&sdst, &ipo->ipo_dst, sdst.sa.sa_len)) {
if (tdbout != NULL)
*tdbout = NULL;
return 0;
}
}
/* Check that the cached TDB (if present), is appropriate. */
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
if ((ipo->ipo_last_searched <= ipsec_last_added) ||
(ipo->ipo_sproto != ipo->ipo_tdb->tdb_sproto) ||
memcmp(dignore ? &sdst : &ipo->ipo_dst,
&ipo->ipo_tdb->tdb_dst,
ipo->ipo_tdb->tdb_dst.sa.sa_len))
goto nomatchout;
if (!ipsp_aux_match(ipo->ipo_tdb,
ipsecflowinfo_ids? ipsecflowinfo_ids: ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask))
goto nomatchout;
/* Cached entry is good. */
error = ipsp_spd_inp(m, inp, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
nomatchout:
/* Cached TDB was not good. */
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
ipo->ipo_last_searched = 0;
}
/*
* If no SA has been added since the last time we did a
* lookup, there's no point searching for one. However, if the
* destination gateway is left unspecified (or is all-1's),
* always lookup since this is a generic-match rule
* (otherwise, we can have situations where SAs to some
* destinations exist but are not used, possibly leading to an
* explosion in the number of acquired SAs).
*/
if (ipo->ipo_last_searched <= ipsec_last_added) {
struct tdb *tdbp_new;
/* "Touch" the entry. */
if (dignore == 0)
ipo->ipo_last_searched = getuptime();
/* gettdb() takes tdb_sadb_mtx, preserve lock order */
mtx_leave(&ipo_tdb_mtx);
/* Find an appropriate SA from the existing ones. */
tdbp_new = gettdbbydst(rdomain,
dignore ? &sdst : &ipo->ipo_dst,
ipo->ipo_sproto,
ipsecflowinfo_ids? ipsecflowinfo_ids: ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask);
ids = NULL;
mtx_enter(&ipo_tdb_mtx);
if ((tdbp_new != NULL) &&
(tdbp_new->tdb_flags & TDBF_DELETED)) {
/*
* After tdb_delete() has released ipo_tdb_mtx
* in tdb_unlink(), never add a new one.
* tdb_cleanspd() has to catch all of them.
*/
tdb_unref(tdbp_new);
tdbp_new = NULL;
}
if (ipo->ipo_tdb != NULL) {
/* Remove cached TDB from parallel thread. */
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
}
ipo->ipo_tdb = tdbp_new;
if (ipo->ipo_tdb != NULL) {
/* gettdbbydst() has already refcounted tdb */
TAILQ_INSERT_TAIL(
&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
error = ipsp_spd_inp(m, inp, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
}
}
mtx_leave(&ipo_tdb_mtx);
/* So, we don't have an SA -- just a policy. */
switch (ipo->ipo_type) {
case IPSP_IPSEC_REQUIRE:
/* Acquire SA through key management. */
if (ipsp_acquire_sa(ipo,
dignore ? &sdst : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, m) != 0) {
return EACCES;
}
/* FALLTHROUGH */
case IPSP_IPSEC_DONTACQ:
return -EINVAL; /* Silently drop packet. */
case IPSP_IPSEC_ACQUIRE:
/* Acquire SA through key management. */
ipsp_acquire_sa(ipo, dignore ? &sdst : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, NULL);
/* FALLTHROUGH */
case IPSP_IPSEC_USE:
return ipsp_spd_inp(m, inp, ipo, tdbout);
}
} else { /* IPSP_DIRECTION_IN */
if (tdbin != NULL) {
/*
* Special case for bundled IPcomp/ESP SAs:
* 1) only IPcomp flows are loaded into kernel
* 2) input processing processes ESP SA first
* 3) then optional IPcomp processing happens
* 4) we only update m_tag for ESP
* => 'tdbin' is always set to ESP SA
* => flow has ipo_proto for IPcomp
* So if 'tdbin' points to an ESP SA and this 'tdbin' is
* bundled with an IPcomp SA, then we replace 'tdbin'
* with the IPcomp SA at tdbin->tdb_inext.
*/
if (ipo->ipo_sproto == IPPROTO_IPCOMP &&
tdbin->tdb_sproto == IPPROTO_ESP &&
tdbin->tdb_inext != NULL &&
tdbin->tdb_inext->tdb_sproto == IPPROTO_IPCOMP)
tdbin = tdbin->tdb_inext;
/* Direct match in the cache. */
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb == tdbin) {
error = ipsp_spd_inp(m, inp, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
}
mtx_leave(&ipo_tdb_mtx);
if (memcmp(dignore ? &ssrc : &ipo->ipo_dst,
&tdbin->tdb_src, tdbin->tdb_src.sa.sa_len) ||
(ipo->ipo_sproto != tdbin->tdb_sproto))
goto nomatchin;
/* Match source/dest IDs. */
if (ipo->ipo_ids)
if (tdbin->tdb_ids == NULL ||
!ipsp_ids_match(ipo->ipo_ids,
tdbin->tdb_ids))
goto nomatchin;
/* Add it to the cache. */
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
}
ipo->ipo_tdb = tdb_ref(tdbin);
TAILQ_INSERT_TAIL(&tdbin->tdb_policy_head, ipo,
ipo_tdb_next);
error = ipsp_spd_inp(m, inp, ipo, tdbout);
mtx_leave(&ipo_tdb_mtx);
return error;
nomatchin: /* Nothing needed here, falling through */
;
}
/* Check whether cached entry applies. */
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
/*
* We only need to check that the correct
* security protocol and security gateway are
* set; IDs will be the same since the cached
* entry is linked on this policy.
*/
if (ipo->ipo_sproto == ipo->ipo_tdb->tdb_sproto &&
!memcmp(&ipo->ipo_tdb->tdb_src,
dignore ? &ssrc : &ipo->ipo_dst,
ipo->ipo_tdb->tdb_src.sa.sa_len))
goto skipinputsearch;
/* Not applicable, unlink. */
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
ipo->ipo_last_searched = 0;
}
/* Find whether there exists an appropriate SA. */
if (ipo->ipo_last_searched <= ipsec_last_added) {
struct tdb *tdbp_new;
if (dignore == 0)
ipo->ipo_last_searched = getuptime();
/* gettdb() takes tdb_sadb_mtx, preserve lock order */
mtx_leave(&ipo_tdb_mtx);
tdbp_new = gettdbbysrc(rdomain,
dignore ? &ssrc : &ipo->ipo_dst,
ipo->ipo_sproto, ipo->ipo_ids,
&ipo->ipo_addr, &ipo->ipo_mask);
mtx_enter(&ipo_tdb_mtx);
if ((tdbp_new != NULL) &&
(tdbp_new->tdb_flags & TDBF_DELETED)) {
/*
* After tdb_delete() has released ipo_tdb_mtx
* in tdb_unlink(), never add a new one.
* tdb_cleanspd() has to catch all of them.
*/
tdb_unref(tdbp_new);
tdbp_new = NULL;
}
if (ipo->ipo_tdb != NULL) {
/* Remove cached TDB from parallel thread. */
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
}
ipo->ipo_tdb = tdbp_new;
if (ipo->ipo_tdb != NULL) {
/* gettdbbysrc() has already refcounted tdb */
TAILQ_INSERT_TAIL(
&ipo->ipo_tdb->tdb_policy_head,
ipo, ipo_tdb_next);
}
}
skipinputsearch:
mtx_leave(&ipo_tdb_mtx);
switch (ipo->ipo_type) {
case IPSP_IPSEC_REQUIRE:
/* If appropriate SA exists, don't acquire another. */
if (ipo->ipo_tdb != NULL)
return -EINVAL; /* Silently drop packet. */
/* Acquire SA through key management. */
if ((error = ipsp_acquire_sa(ipo,
dignore ? &ssrc : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, m)) != 0)
return error;
/* FALLTHROUGH */
case IPSP_IPSEC_DONTACQ:
return -EINVAL; /* Silently drop packet. */
case IPSP_IPSEC_ACQUIRE:
/* If appropriate SA exists, don't acquire another. */
if (ipo->ipo_tdb != NULL)
return ipsp_spd_inp(m, inp, ipo, tdbout);
/* Acquire SA through key management. */
ipsp_acquire_sa(ipo, dignore ? &ssrc : &ipo->ipo_dst,
signore ? NULL : &ipo->ipo_src, ddst, NULL);
/* FALLTHROUGH */
case IPSP_IPSEC_USE:
return ipsp_spd_inp(m, inp, ipo, tdbout);
}
}
/* Shouldn't ever get this far. */
return EINVAL;
}
/*
* Delete a policy from the SPD.
*/
int
ipsec_delete_policy(struct ipsec_policy *ipo)
{
struct ipsec_acquire *ipa;
struct radix_node_head *rnh;
struct radix_node *rn = (struct radix_node *)ipo;
NET_ASSERT_LOCKED();
if (refcnt_rele(&ipo->ipo_refcnt) == 0)
return 0;
/* Delete from SPD. */
if ((rnh = spd_table_get(ipo->ipo_rdomain)) == NULL ||
rn_delete(&ipo->ipo_addr, &ipo->ipo_mask, rnh, rn) == NULL)
return (ESRCH);
mtx_enter(&ipo_tdb_mtx);
if (ipo->ipo_tdb != NULL) {
TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo,
ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
}
mtx_leave(&ipo_tdb_mtx);
mtx_enter(&ipsec_acquire_mtx);
while ((ipa = TAILQ_FIRST(&ipo->ipo_acquires)) != NULL)
ipsp_delete_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list);
if (ipo->ipo_ids)
ipsp_ids_free(ipo->ipo_ids);
ipsec_in_use--;
pool_put(&ipsec_policy_pool, ipo);
return 0;
}
void
ipsp_delete_acquire_timer(void *v)
{
struct ipsec_acquire *ipa = v;
mtx_enter(&ipsec_acquire_mtx);
refcnt_rele(&ipa->ipa_refcnt);
ipsp_delete_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
}
/*
* Delete a pending IPsec acquire record.
*/
void
ipsp_delete_acquire(struct ipsec_acquire *ipa)
{
mtx_enter(&ipsec_acquire_mtx);
ipsp_delete_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
}
void
ipsp_delete_acquire_locked(struct ipsec_acquire *ipa)
{
if (timeout_del(&ipa->ipa_timeout) == 1)
refcnt_rele(&ipa->ipa_refcnt);
ipsp_unref_acquire_locked(ipa);
}
void
ipsec_unref_acquire(struct ipsec_acquire *ipa)
{
mtx_enter(&ipsec_acquire_mtx);
ipsp_unref_acquire_locked(ipa);
mtx_leave(&ipsec_acquire_mtx);
}
void
ipsp_unref_acquire_locked(struct ipsec_acquire *ipa)
{
MUTEX_ASSERT_LOCKED(&ipsec_acquire_mtx);
if (refcnt_rele(&ipa->ipa_refcnt) == 0)
return;
TAILQ_REMOVE(&ipsec_acquire_head, ipa, ipa_next);
TAILQ_REMOVE(&ipa->ipa_policy->ipo_acquires, ipa, ipa_ipo_next);
ipa->ipa_policy = NULL;
pool_put(&ipsec_acquire_pool, ipa);
}
/*
* Find out if there's an ACQUIRE pending.
* XXX Need a better structure.
*/
int
ipsp_pending_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw)
{
struct ipsec_acquire *ipa;
NET_ASSERT_LOCKED();
mtx_enter(&ipsec_acquire_mtx);
TAILQ_FOREACH(ipa, &ipo->ipo_acquires, ipa_ipo_next) {
if (!memcmp(gw, &ipa->ipa_addr, gw->sa.sa_len))
break;
}
mtx_leave(&ipsec_acquire_mtx);
return (ipa != NULL);
}
/*
* Signal key management that we need an SA.
* XXX For outgoing policies, we could try to hold on to the mbuf.
*/
int
ipsp_acquire_sa(struct ipsec_policy *ipo, union sockaddr_union *gw,
union sockaddr_union *laddr, struct sockaddr_encap *ddst, struct mbuf *m)
{
struct ipsec_acquire *ipa;
NET_ASSERT_LOCKED();
/* Check whether request has been made already. */
if (ipsp_pending_acquire(ipo, gw))
return 0;
/* Add request in cache and proceed. */
ipa = pool_get(&ipsec_acquire_pool, PR_NOWAIT|PR_ZERO);
if (ipa == NULL)
return ENOMEM;
ipa->ipa_addr = *gw;
refcnt_init(&ipa->ipa_refcnt);
timeout_set(&ipa->ipa_timeout, ipsp_delete_acquire_timer, ipa);
ipa->ipa_info.sen_len = ipa->ipa_mask.sen_len = SENT_LEN;
ipa->ipa_info.sen_family = ipa->ipa_mask.sen_family = PF_KEY;
/* Just copy the right information. */
switch (ipo->ipo_addr.sen_type) {
case SENT_IP4:
ipa->ipa_info.sen_type = ipa->ipa_mask.sen_type = SENT_IP4;
ipa->ipa_info.sen_direction = ipo->ipo_addr.sen_direction;
ipa->ipa_mask.sen_direction = ipo->ipo_mask.sen_direction;
if (ipsp_is_unspecified(ipo->ipo_dst)) {
ipa->ipa_info.sen_ip_src = ddst->sen_ip_src;
ipa->ipa_mask.sen_ip_src.s_addr = INADDR_BROADCAST;
ipa->ipa_info.sen_ip_dst = ddst->sen_ip_dst;
ipa->ipa_mask.sen_ip_dst.s_addr = INADDR_BROADCAST;
} else {
ipa->ipa_info.sen_ip_src = ipo->ipo_addr.sen_ip_src;
ipa->ipa_mask.sen_ip_src = ipo->ipo_mask.sen_ip_src;
ipa->ipa_info.sen_ip_dst = ipo->ipo_addr.sen_ip_dst;
ipa->ipa_mask.sen_ip_dst = ipo->ipo_mask.sen_ip_dst;
}
ipa->ipa_info.sen_proto = ipo->ipo_addr.sen_proto;
ipa->ipa_mask.sen_proto = ipo->ipo_mask.sen_proto;
if (ipo->ipo_addr.sen_proto) {
ipa->ipa_info.sen_sport = ipo->ipo_addr.sen_sport;
ipa->ipa_mask.sen_sport = ipo->ipo_mask.sen_sport;
ipa->ipa_info.sen_dport = ipo->ipo_addr.sen_dport;
ipa->ipa_mask.sen_dport = ipo->ipo_mask.sen_dport;
}
break;
#ifdef INET6
case SENT_IP6:
ipa->ipa_info.sen_type = ipa->ipa_mask.sen_type = SENT_IP6;
ipa->ipa_info.sen_ip6_direction =
ipo->ipo_addr.sen_ip6_direction;
ipa->ipa_mask.sen_ip6_direction =
ipo->ipo_mask.sen_ip6_direction;
if (ipsp_is_unspecified(ipo->ipo_dst)) {
ipa->ipa_info.sen_ip6_src = ddst->sen_ip6_src;
ipa->ipa_mask.sen_ip6_src = in6mask128;
ipa->ipa_info.sen_ip6_dst = ddst->sen_ip6_dst;
ipa->ipa_mask.sen_ip6_dst = in6mask128;
} else {
ipa->ipa_info.sen_ip6_src = ipo->ipo_addr.sen_ip6_src;
ipa->ipa_mask.sen_ip6_src = ipo->ipo_mask.sen_ip6_src;
ipa->ipa_info.sen_ip6_dst = ipo->ipo_addr.sen_ip6_dst;
ipa->ipa_mask.sen_ip6_dst = ipo->ipo_mask.sen_ip6_dst;
}
ipa->ipa_info.sen_ip6_proto = ipo->ipo_addr.sen_ip6_proto;
ipa->ipa_mask.sen_ip6_proto = ipo->ipo_mask.sen_ip6_proto;
if (ipo->ipo_mask.sen_ip6_proto) {
ipa->ipa_info.sen_ip6_sport =
ipo->ipo_addr.sen_ip6_sport;
ipa->ipa_mask.sen_ip6_sport =
ipo->ipo_mask.sen_ip6_sport;
ipa->ipa_info.sen_ip6_dport =
ipo->ipo_addr.sen_ip6_dport;
ipa->ipa_mask.sen_ip6_dport =
ipo->ipo_mask.sen_ip6_dport;
}
break;
#endif /* INET6 */
default:
pool_put(&ipsec_acquire_pool, ipa);
return 0;
}
mtx_enter(&ipsec_acquire_mtx);
#ifdef IPSEC
if (timeout_add_sec(&ipa->ipa_timeout, ipsec_expire_acquire) == 1)
refcnt_take(&ipa->ipa_refcnt);
#endif
TAILQ_INSERT_TAIL(&ipsec_acquire_head, ipa, ipa_next);
TAILQ_INSERT_TAIL(&ipo->ipo_acquires, ipa, ipa_ipo_next);
ipa->ipa_policy = ipo;
mtx_leave(&ipsec_acquire_mtx);
/* PF_KEYv2 notification message. */
return pfkeyv2_acquire(ipo, gw, laddr, &ipa->ipa_seq, ddst);
}
/*
* Deal with PCB security requirements.
*/
int
ipsp_spd_inp(struct mbuf *m, struct inpcb *inp, struct ipsec_policy *ipo,
struct tdb **tdbout)
{
/* Sanity check. */
if (inp == NULL)
goto justreturn;
/* We only support IPSEC_LEVEL_BYPASS or IPSEC_LEVEL_AVAIL */
if (inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_BYPASS &&
inp->inp_seclevel[SL_ESP_NETWORK] == IPSEC_LEVEL_BYPASS &&
inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_BYPASS)
goto justreturn;
if (inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_AVAIL &&
inp->inp_seclevel[SL_ESP_NETWORK] == IPSEC_LEVEL_AVAIL &&
inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_AVAIL)
goto justreturn;
return -EINVAL; /* Silently drop packet. */
justreturn:
if (tdbout != NULL) {
if (ipo != NULL)
*tdbout = tdb_ref(ipo->ipo_tdb);
else
*tdbout = NULL;
}
return 0;
}
/*
* Find a pending ACQUIRE record based on its sequence number.
* XXX Need to use a better data structure.
*/
struct ipsec_acquire *
ipsec_get_acquire(u_int32_t seq)
{
struct ipsec_acquire *ipa;
NET_ASSERT_LOCKED();
mtx_enter(&ipsec_acquire_mtx);
TAILQ_FOREACH(ipa, &ipsec_acquire_head, ipa_next) {
if (ipa->ipa_seq == seq) {
refcnt_take(&ipa->ipa_refcnt);
break;
}
}
mtx_leave(&ipsec_acquire_mtx);
return ipa;
}
1
2
1
6
2
2
2
2
2
1
1
6
1
1
4
13
1
9
3
8
4
2
4
1
5
11
17
17
6
2
4
6
5
2
10
3
2
2
8
3
6
10
4
18
18
8
3
5
3
5
2
3
8
3
1
4
2
7
3
6
7
4
2
4
4
8
7
1
2
5
5
1
4
4
1
3
5
3
1
3
1
3
5
1
4
6
1
1
2
149
103
61
7
1197
24
38
861
890
903
72
26
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
/* $OpenBSD: kern_prot.c,v 1.80 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_prot.c,v 1.33 1996/02/09 18:59:42 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_prot.c 8.6 (Berkeley) 1/21/94
*/
/*
* System calls related to processes and protection
*/
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/systm.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/pool.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <machine/tcb.h>
inline void
crset(struct ucred *newcr, const struct ucred *cr)
{
KASSERT(cr->cr_refcnt.r_refs > 0);
memcpy(
(char *)newcr + offsetof(struct ucred, cr_startcopy),
(const char *)cr + offsetof(struct ucred, cr_startcopy),
sizeof(*cr) - offsetof(struct ucred, cr_startcopy));
}
int
sys_getpid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_p->ps_pid;
return (0);
}
int
sys_getthrid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_tid + THREAD_PID_OFFSET;
return (0);
}
int
sys_getppid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_p->ps_ppid;
return (0);
}
/* Get process group ID; note that POSIX getpgrp takes no parameter */
int
sys_getpgrp(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_p->ps_pgrp->pg_id;
return (0);
}
/*
* SysVR.4 compatible getpgid()
*/
int
sys_getpgid(struct proc *curp, void *v, register_t *retval)
{
struct sys_getpgid_args /* {
syscallarg(pid_t) pid;
} */ *uap = v;
struct process *targpr = curp->p_p;
if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
goto found;
if ((targpr = prfind(SCARG(uap, pid))) == NULL)
return (ESRCH);
if (targpr->ps_session != curp->p_p->ps_session)
return (EPERM);
found:
*retval = targpr->ps_pgid;
return (0);
}
int
sys_getsid(struct proc *curp, void *v, register_t *retval)
{
struct sys_getsid_args /* {
syscallarg(pid_t) pid;
} */ *uap = v;
struct process *targpr = curp->p_p;
if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
goto found;
if ((targpr = prfind(SCARG(uap, pid))) == NULL)
return (ESRCH);
if (targpr->ps_session != curp->p_p->ps_session)
return (EPERM);
found:
/* Skip exiting processes */
if (targpr->ps_pgrp->pg_session->s_leader == NULL)
return (ESRCH);
*retval = targpr->ps_pgrp->pg_session->s_leader->ps_pid;
return (0);
}
int
sys_getuid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_ruid;
return (0);
}
int
sys_geteuid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_uid;
return (0);
}
int
sys_issetugid(struct proc *p, void *v, register_t *retval)
{
if (p->p_p->ps_flags & PS_SUGIDEXEC)
*retval = 1;
else
*retval = 0;
return (0);
}
int
sys_getgid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_rgid;
return (0);
}
/*
* Get effective group ID. The "egid" is groups[0], and could be obtained
* via getgroups. This syscall exists because it is somewhat painful to do
* correctly in a library function.
*/
int
sys_getegid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_gid;
return (0);
}
int
sys_getgroups(struct proc *p, void *v, register_t *retval)
{
struct sys_getgroups_args /* {
syscallarg(int) gidsetsize;
syscallarg(gid_t *) gidset;
} */ *uap = v;
struct ucred *uc = p->p_ucred;
int ngrp;
int error;
if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
*retval = uc->cr_ngroups;
return (0);
}
if (ngrp < uc->cr_ngroups)
return (EINVAL);
ngrp = uc->cr_ngroups;
error = copyout(uc->cr_groups, SCARG(uap, gidset),
ngrp * sizeof(gid_t));
if (error)
return (error);
*retval = ngrp;
return (0);
}
int
sys_setsid(struct proc *p, void *v, register_t *retval)
{
struct session *newsess;
struct pgrp *newpgrp;
struct process *pr = p->p_p;
pid_t pid = pr->ps_pid;
newsess = pool_get(&session_pool, PR_WAITOK);
newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
if (pr->ps_pgid == pid || pgfind(pid) != NULL) {
pool_put(&pgrp_pool, newpgrp);
pool_put(&session_pool, newsess);
return (EPERM);
} else {
enternewpgrp(pr, newpgrp, newsess);
*retval = pid;
return (0);
}
}
/*
* set process group (setpgid/old setpgrp)
*
* caller does setpgid(targpid, targpgid)
*
* pid must be caller or child of caller (ESRCH)
* if a child
* pid must be in same session (EPERM)
* pid can't have done an exec (EACCES)
* if pgid != pid
* there must exist some pid in same session having pgid (EPERM)
* pid must not be session leader (EPERM)
*/
int
sys_setpgid(struct proc *curp, void *v, register_t *retval)
{
struct sys_setpgid_args /* {
syscallarg(pid_t) pid;
syscallarg(pid_t) pgid;
} */ *uap = v;
struct process *curpr = curp->p_p;
struct process *targpr; /* target process */
struct pgrp *pgrp, *newpgrp; /* target pgrp */
pid_t pid, pgid;
int error;
pid = SCARG(uap, pid);
pgid = SCARG(uap, pgid);
if (pgid < 0)
return (EINVAL);
newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
if (pid != 0 && pid != curpr->ps_pid) {
if ((targpr = prfind(pid)) == NULL ||
!inferior(targpr, curpr)) {
error = ESRCH;
goto out;
}
if (targpr->ps_session != curpr->ps_session) {
error = EPERM;
goto out;
}
if (targpr->ps_flags & PS_EXEC) {
error = EACCES;
goto out;
}
} else
targpr = curpr;
if (SESS_LEADER(targpr)) {
error = EPERM;
goto out;
}
if (pgid == 0)
pgid = targpr->ps_pid;
error = 0;
if ((pgrp = pgfind(pgid)) == NULL) {
/* can only create a new process group with pgid == pid */
if (pgid != targpr->ps_pid)
error = EPERM;
else {
enternewpgrp(targpr, newpgrp, NULL);
newpgrp = NULL;
}
} else if (pgrp != targpr->ps_pgrp) { /* anything to do? */
if (pgid != targpr->ps_pid &&
pgrp->pg_session != curpr->ps_session)
error = EPERM;
else
enterthispgrp(targpr, pgrp);
}
out:
if (newpgrp != NULL)
pool_put(&pgrp_pool, newpgrp);
return (error);
}
int
sys_getresuid(struct proc *p, void *v, register_t *retval)
{
struct sys_getresuid_args /* {
syscallarg(uid_t *) ruid;
syscallarg(uid_t *) euid;
syscallarg(uid_t *) suid;
} */ *uap = v;
struct ucred *uc = p->p_ucred;
uid_t *ruid, *euid, *suid;
int error1 = 0, error2 = 0, error3 = 0;
ruid = SCARG(uap, ruid);
euid = SCARG(uap, euid);
suid = SCARG(uap, suid);
if (ruid != NULL)
error1 = copyout(&uc->cr_ruid, ruid, sizeof(*ruid));
if (euid != NULL)
error2 = copyout(&uc->cr_uid, euid, sizeof(*euid));
if (suid != NULL)
error3 = copyout(&uc->cr_svuid, suid, sizeof(*suid));
return (error1 ? error1 : error2 ? error2 : error3);
}
int
sys_setresuid(struct proc *p, void *v, register_t *retval)
{
struct sys_setresuid_args /* {
syscallarg(uid_t) ruid;
syscallarg(uid_t) euid;
syscallarg(uid_t) suid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t ruid, euid, suid;
int error;
ruid = SCARG(uap, ruid);
euid = SCARG(uap, euid);
suid = SCARG(uap, suid);
/*
* make permission checks against the thread's ucred,
* but the actual changes will be to the process's ucred
*/
pruc = pr->ps_ucred;
if ((ruid == (uid_t)-1 || ruid == pruc->cr_ruid) &&
(euid == (uid_t)-1 || euid == pruc->cr_uid) &&
(suid == (uid_t)-1 || suid == pruc->cr_svuid))
return (0); /* no change */
/*
* Any of the real, effective, and saved uids may be changed
* to the current value of one of the three (root is not limited).
*/
if (ruid != (uid_t)-1 &&
ruid != uc->cr_ruid &&
ruid != uc->cr_uid &&
ruid != uc->cr_svuid &&
(error = suser(p)))
return (error);
if (euid != (uid_t)-1 &&
euid != uc->cr_ruid &&
euid != uc->cr_uid &&
euid != uc->cr_svuid &&
(error = suser(p)))
return (error);
if (suid != (uid_t)-1 &&
suid != uc->cr_ruid &&
suid != uc->cr_uid &&
suid != uc->cr_svuid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
/*
* Note that unlike the other set*uid() calls, each
* uid type is set independently of the others.
*/
if (ruid != (uid_t)-1)
newcred->cr_ruid = ruid;
if (euid != (uid_t)-1)
newcred->cr_uid = euid;
if (suid != (uid_t)-1)
newcred->cr_svuid = suid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
/* now that we can sleep, transfer proc count to new user */
if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
chgproccnt(pruc->cr_ruid, -1);
chgproccnt(ruid, 1);
}
crfree(pruc);
return (0);
}
int
sys_getresgid(struct proc *p, void *v, register_t *retval)
{
struct sys_getresgid_args /* {
syscallarg(gid_t *) rgid;
syscallarg(gid_t *) egid;
syscallarg(gid_t *) sgid;
} */ *uap = v;
struct ucred *uc = p->p_ucred;
gid_t *rgid, *egid, *sgid;
int error1 = 0, error2 = 0, error3 = 0;
rgid = SCARG(uap, rgid);
egid = SCARG(uap, egid);
sgid = SCARG(uap, sgid);
if (rgid != NULL)
error1 = copyout(&uc->cr_rgid, rgid, sizeof(*rgid));
if (egid != NULL)
error2 = copyout(&uc->cr_gid, egid, sizeof(*egid));
if (sgid != NULL)
error3 = copyout(&uc->cr_svgid, sgid, sizeof(*sgid));
return (error1 ? error1 : error2 ? error2 : error3);
}
int
sys_setresgid(struct proc *p, void *v, register_t *retval)
{
struct sys_setresgid_args /* {
syscallarg(gid_t) rgid;
syscallarg(gid_t) egid;
syscallarg(gid_t) sgid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t rgid, egid, sgid;
int error;
rgid = SCARG(uap, rgid);
egid = SCARG(uap, egid);
sgid = SCARG(uap, sgid);
/*
* make permission checks against the thread's ucred,
* but the actual changes will be to the process's ucred
*/
pruc = pr->ps_ucred;
if ((rgid == (gid_t)-1 || rgid == pruc->cr_rgid) &&
(egid == (gid_t)-1 || egid == pruc->cr_gid) &&
(sgid == (gid_t)-1 || sgid == pruc->cr_svgid))
return (0); /* no change */
/*
* Any of the real, effective, and saved gids may be changed
* to the current value of one of the three (root is not limited).
*/
if (rgid != (gid_t)-1 &&
rgid != uc->cr_rgid &&
rgid != uc->cr_gid &&
rgid != uc->cr_svgid &&
(error = suser(p)))
return (error);
if (egid != (gid_t)-1 &&
egid != uc->cr_rgid &&
egid != uc->cr_gid &&
egid != uc->cr_svgid &&
(error = suser(p)))
return (error);
if (sgid != (gid_t)-1 &&
sgid != uc->cr_rgid &&
sgid != uc->cr_gid &&
sgid != uc->cr_svgid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
/*
* Note that unlike the other set*gid() calls, each
* gid type is set independently of the others.
*/
if (rgid != (gid_t)-1)
newcred->cr_rgid = rgid;
if (egid != (gid_t)-1)
newcred->cr_gid = egid;
if (sgid != (gid_t)-1)
newcred->cr_svgid = sgid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setregid(struct proc *p, void *v, register_t *retval)
{
struct sys_setregid_args /* {
syscallarg(gid_t) rgid;
syscallarg(gid_t) egid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t rgid, egid;
int error;
rgid = SCARG(uap, rgid);
egid = SCARG(uap, egid);
/*
* make permission checks against the thread's ucred,
* but the actual changes will be to the process's ucred
*
* The saved gid check here is complicated: we reset the
* saved gid to the real gid if the real gid is specified
* *and* either it's changing _or_ the saved gid won't equal
* the effective gid. So, the svgid *won't* change when
* the rgid isn't specified or when the rgid isn't changing
* and the svgid equals the requested egid.
*/
pruc = pr->ps_ucred;
if ((rgid == (gid_t)-1 || rgid == pruc->cr_rgid) &&
(egid == (gid_t)-1 || egid == pruc->cr_gid) &&
(rgid == (gid_t)-1 || (rgid == pruc->cr_rgid &&
pruc->cr_svgid == (egid != (gid_t)-1 ? egid : pruc->cr_gid))))
return (0); /* no change */
/*
* Any of the real, effective, and saved gids may be changed
* to the current value of one of the three (root is not limited).
*/
if (rgid != (gid_t)-1 &&
rgid != uc->cr_rgid &&
rgid != uc->cr_gid &&
rgid != uc->cr_svgid &&
(error = suser(p)))
return (error);
if (egid != (gid_t)-1 &&
egid != uc->cr_rgid &&
egid != uc->cr_gid &&
egid != uc->cr_svgid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (rgid != (gid_t)-1)
newcred->cr_rgid = rgid;
if (egid != (gid_t)-1)
newcred->cr_gid = egid;
/*
* The saved gid presents a bit of a dilemma, as it did not
* exist when setregid(2) was conceived. We only set the saved
* gid when the real gid is specified and either its value would
* change, or where the saved and effective gids are different.
*/
if (rgid != (gid_t)-1 && (rgid != pruc->cr_rgid ||
pruc->cr_svgid != (egid != (gid_t)-1 ? egid : pruc->cr_gid)))
newcred->cr_svgid = rgid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setreuid(struct proc *p, void *v, register_t *retval)
{
struct sys_setreuid_args /* {
syscallarg(uid_t) ruid;
syscallarg(uid_t) euid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t ruid, euid;
int error;
ruid = SCARG(uap, ruid);
euid = SCARG(uap, euid);
/*
* make permission checks against the thread's ucred,
* but the actual changes will be to the process's ucred
*
* The saved uid check here is complicated: we reset the
* saved uid to the real uid if the real uid is specified
* *and* either it's changing _or_ the saved uid won't equal
* the effective uid. So, the svuid *won't* change when
* the ruid isn't specified or when the ruid isn't changing
* and the svuid equals the requested euid.
*/
pruc = pr->ps_ucred;
if ((ruid == (uid_t)-1 || ruid == pruc->cr_ruid) &&
(euid == (uid_t)-1 || euid == pruc->cr_uid) &&
(ruid == (uid_t)-1 || (ruid == pruc->cr_ruid &&
pruc->cr_svuid == (euid != (uid_t)-1 ? euid : pruc->cr_uid))))
return (0); /* no change */
/*
* Any of the real, effective, and saved uids may be changed
* to the current value of one of the three (root is not limited).
*/
if (ruid != (uid_t)-1 &&
ruid != uc->cr_ruid &&
ruid != uc->cr_uid &&
ruid != uc->cr_svuid &&
(error = suser(p)))
return (error);
if (euid != (uid_t)-1 &&
euid != uc->cr_ruid &&
euid != uc->cr_uid &&
euid != uc->cr_svuid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (ruid != (uid_t)-1)
newcred->cr_ruid = ruid;
if (euid != (uid_t)-1)
newcred->cr_uid = euid;
/*
* The saved uid presents a bit of a dilemma, as it did not
* exist when setreuid(2) was conceived. We only set the saved
* uid when the real uid is specified and either its value would
* change, or where the saved and effective uids are different.
*/
if (ruid != (uid_t)-1 && (ruid != pruc->cr_ruid ||
pruc->cr_svuid != (euid != (uid_t)-1 ? euid : pruc->cr_uid)))
newcred->cr_svuid = ruid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
/* now that we can sleep, transfer proc count to new user */
if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
chgproccnt(pruc->cr_ruid, -1);
chgproccnt(ruid, 1);
}
crfree(pruc);
return (0);
}
int
sys_setuid(struct proc *p, void *v, register_t *retval)
{
struct sys_setuid_args /* {
syscallarg(uid_t) uid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t uid;
int did_real, error;
uid = SCARG(uap, uid);
pruc = pr->ps_ucred;
if (pruc->cr_uid == uid &&
pruc->cr_ruid == uid &&
pruc->cr_svuid == uid)
return (0);
if (uid != uc->cr_ruid &&
uid != uc->cr_svuid &&
uid != uc->cr_uid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
/*
* Everything's okay, do it.
*/
if (uid == pruc->cr_uid || suser(p) == 0) {
did_real = 1;
newcred->cr_ruid = uid;
newcred->cr_svuid = uid;
} else
did_real = 0;
newcred->cr_uid = uid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
/*
* Transfer proc count to new user.
*/
if (did_real && uid != pruc->cr_ruid) {
chgproccnt(pruc->cr_ruid, -1);
chgproccnt(uid, 1);
}
crfree(pruc);
return (0);
}
int
sys_seteuid(struct proc *p, void *v, register_t *retval)
{
struct sys_seteuid_args /* {
syscallarg(uid_t) euid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t euid;
int error;
euid = SCARG(uap, euid);
if (pr->ps_ucred->cr_uid == euid)
return (0);
if (euid != uc->cr_ruid && euid != uc->cr_svuid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
newcred->cr_uid = euid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setgid(struct proc *p, void *v, register_t *retval)
{
struct sys_setgid_args /* {
syscallarg(gid_t) gid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t gid;
int error;
gid = SCARG(uap, gid);
pruc = pr->ps_ucred;
if (pruc->cr_gid == gid &&
pruc->cr_rgid == gid &&
pruc->cr_svgid == gid)
return (0);
if (gid != uc->cr_rgid &&
gid != uc->cr_svgid &&
gid != uc->cr_gid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (gid == pruc->cr_gid || suser(p) == 0) {
newcred->cr_rgid = gid;
newcred->cr_svgid = gid;
}
newcred->cr_gid = gid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setegid(struct proc *p, void *v, register_t *retval)
{
struct sys_setegid_args /* {
syscallarg(gid_t) egid;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t egid;
int error;
egid = SCARG(uap, egid);
if (pr->ps_ucred->cr_gid == egid)
return (0);
if (egid != uc->cr_rgid && egid != uc->cr_svgid &&
(error = suser(p)))
return (error);
/*
* Copy credentials so other references do not see our changes.
* ps_ucred may change during the crget().
*/
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
newcred->cr_gid = egid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setgroups(struct proc *p, void *v, register_t *retval)
{
struct sys_setgroups_args /* {
syscallarg(int) gidsetsize;
syscallarg(const gid_t *) gidset;
} */ *uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred;
gid_t groups[NGROUPS_MAX];
int ngrp;
int error;
if ((error = suser(p)) != 0)
return (error);
ngrp = SCARG(uap, gidsetsize);
if (ngrp > NGROUPS_MAX || ngrp < 0)
return (EINVAL);
error = copyin(SCARG(uap, gidset), groups, ngrp * sizeof(gid_t));
if (error == 0) {
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
memcpy(newcred->cr_groups, groups, ngrp * sizeof(gid_t));
newcred->cr_ngroups = ngrp;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
}
return (error);
}
/*
* Check if gid is a member of the group set.
*/
int
groupmember(gid_t gid, struct ucred *cred)
{
gid_t *gp;
gid_t *egp;
if (cred->cr_gid == gid)
return (1);
egp = &(cred->cr_groups[cred->cr_ngroups]);
for (gp = cred->cr_groups; gp < egp; gp++)
if (*gp == gid)
return (1);
return (0);
}
/*
* Test whether this process has special user powers.
* Returns 0 or error.
*/
int
suser(struct proc *p)
{
struct ucred *cred = p->p_ucred;
if (cred->cr_uid == 0)
return (0);
return (EPERM);
}
/*
* replacement for old suser, for callers who don't have a process
*/
int
suser_ucred(struct ucred *cred)
{
if (cred->cr_uid == 0)
return (0);
return (EPERM);
}
/*
* Allocate a zeroed cred structure.
*/
struct ucred *
crget(void)
{
struct ucred *cr;
cr = pool_get(&ucred_pool, PR_WAITOK|PR_ZERO);
refcnt_init(&cr->cr_refcnt);
return (cr);
}
/*
* Increment the reference count of a cred structure.
* Returns the passed structure.
*/
struct ucred *
crhold(struct ucred *cr)
{
refcnt_take(&cr->cr_refcnt);
return (cr);
}
/*
* Free a cred structure.
* Throws away space when ref count gets to 0.
*/
void
crfree(struct ucred *cr)
{
if (refcnt_rele(&cr->cr_refcnt))
pool_put(&ucred_pool, cr);
}
/*
* Copy cred structure to a new one and free the old one.
*/
struct ucred *
crcopy(struct ucred *cr)
{
struct ucred *newcr;
if (!refcnt_shared(&cr->cr_refcnt))
return (cr);
newcr = crget();
*newcr = *cr;
crfree(cr);
refcnt_init(&newcr->cr_refcnt);
return (newcr);
}
/*
* Dup cred struct to a new held one.
*/
struct ucred *
crdup(struct ucred *cr)
{
struct ucred *newcr;
newcr = crget();
*newcr = *cr;
refcnt_init(&newcr->cr_refcnt);
return (newcr);
}
/*
* Convert the userspace xucred to a kernel ucred
*/
int
crfromxucred(struct ucred *cr, const struct xucred *xcr)
{
if (xcr->cr_ngroups < 0 || xcr->cr_ngroups > NGROUPS_MAX)
return (EINVAL);
refcnt_init(&cr->cr_refcnt);
cr->cr_uid = xcr->cr_uid;
cr->cr_gid = xcr->cr_gid;
cr->cr_ngroups = xcr->cr_ngroups;
memcpy(cr->cr_groups, xcr->cr_groups,
sizeof(cr->cr_groups[0]) * xcr->cr_ngroups);
return (0);
}
/*
* Get login name, if available.
*/
int
sys_getlogin_r(struct proc *p, void *v, register_t *retval)
{
struct sys_getlogin_r_args /* {
syscallarg(char *) namebuf;
syscallarg(size_t) namelen;
} */ *uap = v;
size_t namelen = SCARG(uap, namelen);
struct session *s = p->p_p->ps_pgrp->pg_session;
int error;
if (namelen > sizeof(s->s_login))
namelen = sizeof(s->s_login);
error = copyoutstr(s->s_login, SCARG(uap, namebuf), namelen, NULL);
if (error == ENAMETOOLONG)
error = ERANGE;
*retval = error;
return (0);
}
/*
* Set login name.
*/
int
sys_setlogin(struct proc *p, void *v, register_t *retval)
{
struct sys_setlogin_args /* {
syscallarg(const char *) namebuf;
} */ *uap = v;
struct session *s = p->p_p->ps_pgrp->pg_session;
char buf[sizeof(s->s_login)];
int error;
if ((error = suser(p)) != 0)
return (error);
error = copyinstr(SCARG(uap, namebuf), buf, sizeof(buf), NULL);
if (error == 0)
strlcpy(s->s_login, buf, sizeof(s->s_login));
else if (error == ENAMETOOLONG)
error = EINVAL;
return (error);
}
/*
* Check if a process is allowed to raise its privileges.
*/
int
proc_cansugid(struct proc *p)
{
/* ptrace(2)d processes shouldn't. */
if ((p->p_p->ps_flags & PS_TRACED) != 0)
return (0);
/* processes with shared filedescriptors shouldn't. */
if (p->p_fd->fd_refcnt > 1)
return (0);
/* Allow. */
return (1);
}
/*
* Set address of the proc's thread-control-block
*/
int
sys___set_tcb(struct proc *p, void *v, register_t *retval)
{
struct sys___set_tcb_args /* {
syscallarg(void *) tcb;
} */ *uap = v;
void *tcb = SCARG(uap, tcb);
#ifdef TCB_INVALID
if (TCB_INVALID(tcb))
return EINVAL;
#endif /* TCB_INVALID */
TCB_SET(p, tcb);
return (0);
}
/*
* Get address of the proc's thread-control-block
*/
int
sys___get_tcb(struct proc *p, void *v, register_t *retval)
{
*retval = (register_t)TCB_GET(p);
return (0);
}
/*
* Refresh the thread's reference to the process's credentials
*/
void
dorefreshcreds(struct process *pr, struct proc *p)
{
struct ucred *uc = p->p_ucred;
KERNEL_LOCK(); /* XXX should be PROCESS_RLOCK(pr) */
if (uc != pr->ps_ucred) {
p->p_ucred = pr->ps_ucred;
crhold(p->p_ucred);
crfree(uc);
}
KERNEL_UNLOCK();
}
170
133
188
188
188
4
188
5
5
5
191
191
191
3
188
5
19
5
407
405
9
9
194
194
194
194
182
179
3
178
4
19
193
194
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
/* $OpenBSD: uvm_addr.c,v 1.31 2022/02/21 10:26:20 jsg Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* #define DEBUG */
#include <sys/param.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#include <uvm/uvm_addr.h>
#include <sys/pool.h>
/* Max gap between hint allocations. */
#define UADDR_HINT_MAXGAP (4 * PAGE_SIZE)
/* Number of pivots in pivot allocator. */
#define NUM_PIVOTS 16
/*
* Max number (inclusive) of pages the pivot allocator
* will place between allocations.
*
* The uaddr_pivot_random() function attempts to bias towards
* small space between allocations, so putting a large number here is fine.
*/
#define PIVOT_RND 8
/*
* Number of allocations that a pivot can supply before expiring.
* When a pivot expires, a new pivot has to be found.
*
* Must be at least 1.
*/
#define PIVOT_EXPIRE 1024
/* Pool with uvm_addr_state structures. */
struct pool uaddr_pool;
struct pool uaddr_bestfit_pool;
struct pool uaddr_pivot_pool;
struct pool uaddr_rnd_pool;
/* uvm_addr state for bestfit selector. */
struct uaddr_bestfit_state {
struct uvm_addr_state ubf_uaddr;
struct uaddr_free_rbtree ubf_free;
};
/* uvm_addr state for rnd selector. */
struct uaddr_rnd_state {
struct uvm_addr_state ur_uaddr;
#if 0
TAILQ_HEAD(, vm_map_entry) ur_free;
#endif
};
/*
* Definition of a pivot in pivot selector.
*/
struct uaddr_pivot {
vaddr_t addr; /* End of prev. allocation. */
int expire;/* Best before date. */
int dir; /* Direction. */
struct vm_map_entry *entry; /* Will contain next alloc. */
};
/* uvm_addr state for pivot selector. */
struct uaddr_pivot_state {
struct uvm_addr_state up_uaddr;
/* Free space tree, for fast pivot selection. */
struct uaddr_free_rbtree up_free;
/* List of pivots. The pointers point to after the last allocation. */
struct uaddr_pivot up_pivots[NUM_PIVOTS];
};
/* Forward declaration (see below). */
extern const struct uvm_addr_functions uaddr_kernel_functions;
struct uvm_addr_state uaddr_kbootstrap;
/*
* Support functions.
*/
#ifndef SMALL_KERNEL
struct vm_map_entry *uvm_addr_entrybyspace(struct uaddr_free_rbtree*,
vsize_t);
#endif /* !SMALL_KERNEL */
void uaddr_kinsert(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_kremove(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_kbootstrapdestroy(struct uvm_addr_state *);
void uaddr_destroy(struct uvm_addr_state *);
void uaddr_kbootstrap_destroy(struct uvm_addr_state *);
void uaddr_rnd_destroy(struct uvm_addr_state *);
void uaddr_bestfit_destroy(struct uvm_addr_state *);
void uaddr_pivot_destroy(struct uvm_addr_state *);
#if 0
int uaddr_lin_select(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry **,
vaddr_t *, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
#endif
int uaddr_kbootstrap_select(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry **,
vaddr_t *, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
int uaddr_rnd_select(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry **,
vaddr_t *, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
int uaddr_bestfit_select(struct vm_map *,
struct uvm_addr_state*, struct vm_map_entry **,
vaddr_t *, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
#ifndef SMALL_KERNEL
int uaddr_pivot_select(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry **,
vaddr_t *, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
int uaddr_stack_brk_select(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry **,
vaddr_t *, vsize_t, vaddr_t, vaddr_t, vm_prot_t,
vaddr_t);
#endif /* !SMALL_KERNEL */
void uaddr_rnd_insert(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_rnd_remove(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_bestfit_insert(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_bestfit_remove(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_pivot_insert(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
void uaddr_pivot_remove(struct vm_map *,
struct uvm_addr_state *, struct vm_map_entry *);
#ifndef SMALL_KERNEL
vsize_t uaddr_pivot_random(void);
int uaddr_pivot_newpivot(struct vm_map *,
struct uaddr_pivot_state *, struct uaddr_pivot *,
struct vm_map_entry **, vaddr_t *,
vsize_t, vaddr_t, vaddr_t, vsize_t, vsize_t);
#endif /* !SMALL_KERNEL */
#if defined(DEBUG) || defined(DDB)
void uaddr_pivot_print(struct uvm_addr_state *, boolean_t,
int (*)(const char *, ...));
#if 0
void uaddr_rnd_print(struct uvm_addr_state *, boolean_t,
int (*)(const char *, ...));
#endif
#endif /* DEBUG || DDB */
#ifndef SMALL_KERNEL
/*
* Find smallest entry in tree that will fit sz bytes.
*/
struct vm_map_entry *
uvm_addr_entrybyspace(struct uaddr_free_rbtree *free, vsize_t sz)
{
struct vm_map_entry *tmp, *res;
tmp = RBT_ROOT(uaddr_free_rbtree, free);
res = NULL;
while (tmp) {
if (tmp->fspace >= sz) {
res = tmp;
tmp = RBT_LEFT(uaddr_free_rbtree, tmp);
} else if (tmp->fspace < sz)
tmp = RBT_RIGHT(uaddr_free_rbtree, tmp);
}
return res;
}
#endif /* !SMALL_KERNEL */
static inline vaddr_t
uvm_addr_align_forward(vaddr_t addr, vaddr_t align, vaddr_t offset)
{
vaddr_t adjusted;
KASSERT(offset < align || (align == 0 && offset == 0));
KASSERT((align & (align - 1)) == 0);
KASSERT((offset & PAGE_MASK) == 0);
align = MAX(align, PAGE_SIZE);
adjusted = addr & ~(align - 1);
adjusted += offset;
return (adjusted < addr ? adjusted + align : adjusted);
}
static inline vaddr_t
uvm_addr_align_backward(vaddr_t addr, vaddr_t align, vaddr_t offset)
{
vaddr_t adjusted;
KASSERT(offset < align || (align == 0 && offset == 0));
KASSERT((align & (align - 1)) == 0);
KASSERT((offset & PAGE_MASK) == 0);
align = MAX(align, PAGE_SIZE);
adjusted = addr & ~(align - 1);
adjusted += offset;
return (adjusted > addr ? adjusted - align : adjusted);
}
/*
* Try to fit the requested space into the entry.
*/
int
uvm_addr_fitspace(vaddr_t *min_result, vaddr_t *max_result,
vaddr_t low_addr, vaddr_t high_addr, vsize_t sz,
vaddr_t align, vaddr_t offset,
vsize_t before_gap, vsize_t after_gap)
{
vaddr_t tmp;
vsize_t fspace;
if (low_addr > high_addr)
return ENOMEM;
fspace = high_addr - low_addr;
if (fspace < before_gap + after_gap)
return ENOMEM;
if (fspace - before_gap - after_gap < sz)
return ENOMEM;
/*
* Calculate lowest address.
*/
low_addr += before_gap;
low_addr = uvm_addr_align_forward(tmp = low_addr, align, offset);
if (low_addr < tmp) /* Overflow during alignment. */
return ENOMEM;
if (high_addr - after_gap - sz < low_addr)
return ENOMEM;
/*
* Calculate highest address.
*/
high_addr -= after_gap + sz;
high_addr = uvm_addr_align_backward(tmp = high_addr, align, offset);
if (high_addr > tmp) /* Overflow during alignment. */
return ENOMEM;
if (low_addr > high_addr)
return ENOMEM;
*min_result = low_addr;
*max_result = high_addr;
return 0;
}
/*
* Initialize uvm_addr.
*/
void
uvm_addr_init(void)
{
pool_init(&uaddr_pool, sizeof(struct uvm_addr_state), 0,
IPL_VM, PR_WAITOK, "uaddr", NULL);
pool_init(&uaddr_bestfit_pool, sizeof(struct uaddr_bestfit_state), 0,
IPL_VM, PR_WAITOK, "uaddrbest", NULL);
pool_init(&uaddr_pivot_pool, sizeof(struct uaddr_pivot_state), 0,
IPL_VM, PR_WAITOK, "uaddrpivot", NULL);
pool_init(&uaddr_rnd_pool, sizeof(struct uaddr_rnd_state), 0,
IPL_VM, PR_WAITOK, "uaddrrnd", NULL);
uaddr_kbootstrap.uaddr_minaddr = PAGE_SIZE;
uaddr_kbootstrap.uaddr_maxaddr = -(vaddr_t)PAGE_SIZE;
uaddr_kbootstrap.uaddr_functions = &uaddr_kernel_functions;
}
/*
* Invoke destructor function of uaddr.
*/
void
uvm_addr_destroy(struct uvm_addr_state *uaddr)
{
if (uaddr)
(*uaddr->uaddr_functions->uaddr_destroy)(uaddr);
}
/*
* Move address forward to satisfy align, offset.
*/
vaddr_t
uvm_addr_align(vaddr_t addr, vaddr_t align, vaddr_t offset)
{
vaddr_t result = (addr & ~(align - 1)) + offset;
if (result < addr)
result += align;
return result;
}
/*
* Move address backwards to satisfy align, offset.
*/
vaddr_t
uvm_addr_align_back(vaddr_t addr, vaddr_t align, vaddr_t offset)
{
vaddr_t result = (addr & ~(align - 1)) + offset;
if (result > addr)
result -= align;
return result;
}
/*
* Directional first fit.
*
* Do a linear search for free space, starting at addr in entry.
* direction == 1: search forward
* direction == -1: search backward
*
* Output: low <= addr <= high and entry will contain addr.
* 0 will be returned if no space is available.
*
* gap describes the space that must appear between the preceding entry.
*/
int
uvm_addr_linsearch(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vaddr_t hint, vsize_t sz, vaddr_t align, vaddr_t offset,
int direction, vaddr_t low, vaddr_t high,
vsize_t before_gap, vsize_t after_gap)
{
struct vm_map_entry *entry;
vaddr_t low_addr, high_addr;
KASSERT(entry_out != NULL && addr_out != NULL);
KASSERT(direction == -1 || direction == 1);
KASSERT((hint & PAGE_MASK) == 0 && (high & PAGE_MASK) == 0 &&
(low & PAGE_MASK) == 0 &&
(before_gap & PAGE_MASK) == 0 && (after_gap & PAGE_MASK) == 0);
KASSERT(high + sz > high); /* Check for overflow. */
/*
* Hint magic.
*/
if (hint == 0)
hint = (direction == 1 ? low : high);
else if (hint > high) {
if (direction != -1)
return ENOMEM;
hint = high;
} else if (hint < low) {
if (direction != 1)
return ENOMEM;
hint = low;
}
for (entry = uvm_map_entrybyaddr(&map->addr,
hint - (direction == -1 ? 1 : 0)); entry != NULL;
entry = (direction == 1 ?
RBT_NEXT(uvm_map_addr, entry) :
RBT_PREV(uvm_map_addr, entry))) {
if ((direction == 1 && VMMAP_FREE_START(entry) > high) ||
(direction == -1 && VMMAP_FREE_END(entry) < low)) {
break;
}
if (uvm_addr_fitspace(&low_addr, &high_addr,
MAX(low, VMMAP_FREE_START(entry)),
MIN(high, VMMAP_FREE_END(entry)),
sz, align, offset, before_gap, after_gap) == 0) {
*entry_out = entry;
if (hint >= low_addr && hint <= high_addr) {
*addr_out = hint;
} else {
*addr_out = (direction == 1 ?
low_addr : high_addr);
}
return 0;
}
}
return ENOMEM;
}
/*
* Invoke address selector of uaddr.
* uaddr may be NULL, in which case the algorithm will fail with ENOMEM.
*
* Will invoke uvm_addr_isavail to fill in last_out.
*/
int
uvm_addr_invoke(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **entry_out, struct vm_map_entry **last_out,
vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset, vm_prot_t prot, vaddr_t hint)
{
int error;
if (uaddr == NULL)
return ENOMEM;
hint &= ~((vaddr_t)PAGE_MASK);
if (hint != 0 &&
!(hint >= uaddr->uaddr_minaddr && hint < uaddr->uaddr_maxaddr))
return ENOMEM;
error = (*uaddr->uaddr_functions->uaddr_select)(map, uaddr,
entry_out, addr_out, sz, align, offset, prot, hint);
if (error == 0) {
KASSERT(*entry_out != NULL);
*last_out = NULL;
if (!uvm_map_isavail(map, uaddr, entry_out, last_out,
*addr_out, sz)) {
panic("uvm_addr_invoke: address selector %p "
"(%s 0x%lx-0x%lx) "
"returned unavailable address 0x%lx sz 0x%lx",
uaddr, uaddr->uaddr_functions->uaddr_name,
uaddr->uaddr_minaddr, uaddr->uaddr_maxaddr,
*addr_out, sz);
}
}
return error;
}
#if defined(DEBUG) || defined(DDB)
void
uvm_addr_print(struct uvm_addr_state *uaddr, const char *slot, boolean_t full,
int (*pr)(const char *, ...))
{
if (uaddr == NULL) {
(*pr)("- uvm_addr %s: NULL\n", slot);
return;
}
(*pr)("- uvm_addr %s: %p (%s 0x%lx-0x%lx)\n", slot, uaddr,
uaddr->uaddr_functions->uaddr_name,
uaddr->uaddr_minaddr, uaddr->uaddr_maxaddr);
if (uaddr->uaddr_functions->uaddr_print == NULL)
return;
(*uaddr->uaddr_functions->uaddr_print)(uaddr, full, pr);
}
#endif /* DEBUG || DDB */
/*
* Destroy a uvm_addr_state structure.
* The uaddr must have been previously allocated from uaddr_state_pool.
*/
void
uaddr_destroy(struct uvm_addr_state *uaddr)
{
pool_put(&uaddr_pool, uaddr);
}
#if 0
/*
* Linear allocator.
* This allocator uses a first-fit algorithm.
*
* If hint is set, search will start at the hint position.
* Only searches forward.
*/
const struct uvm_addr_functions uaddr_lin_functions = {
.uaddr_select = &uaddr_lin_select,
.uaddr_destroy = &uaddr_destroy,
.uaddr_name = "uaddr_lin"
};
struct uvm_addr_state *
uaddr_lin_create(vaddr_t minaddr, vaddr_t maxaddr)
{
struct uvm_addr_state *uaddr;
uaddr = pool_get(&uaddr_pool, PR_WAITOK);
uaddr->uaddr_minaddr = minaddr;
uaddr->uaddr_maxaddr = maxaddr;
uaddr->uaddr_functions = &uaddr_lin_functions;
return uaddr;
}
int
uaddr_lin_select(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset,
vm_prot_t prot, vaddr_t hint)
{
vaddr_t guard_sz;
/*
* Deal with guardpages: search for space with one extra page.
*/
guard_sz = ((map->flags & VM_MAP_GUARDPAGES) == 0 ? 0 : PAGE_SIZE);
if (uaddr->uaddr_maxaddr - uaddr->uaddr_minaddr - guard_sz < sz)
return ENOMEM;
return uvm_addr_linsearch(map, uaddr, entry_out, addr_out, 0, sz,
align, offset, 1, uaddr->uaddr_minaddr, uaddr->uaddr_maxaddr - sz,
0, guard_sz);
}
#endif
/*
* Randomized allocator.
* This allocator use uvm_map_hint to acquire a random address and searches
* from there.
*/
const struct uvm_addr_functions uaddr_rnd_functions = {
.uaddr_select = &uaddr_rnd_select,
.uaddr_free_insert = &uaddr_rnd_insert,
.uaddr_free_remove = &uaddr_rnd_remove,
.uaddr_destroy = &uaddr_rnd_destroy,
#if defined(DEBUG) || defined(DDB)
#if 0
.uaddr_print = &uaddr_rnd_print,
#endif
#endif /* DEBUG || DDB */
.uaddr_name = "uaddr_rnd"
};
struct uvm_addr_state *
uaddr_rnd_create(vaddr_t minaddr, vaddr_t maxaddr)
{
struct uaddr_rnd_state *uaddr;
uaddr = pool_get(&uaddr_rnd_pool, PR_WAITOK);
uaddr->ur_uaddr.uaddr_minaddr = minaddr;
uaddr->ur_uaddr.uaddr_maxaddr = maxaddr;
uaddr->ur_uaddr.uaddr_functions = &uaddr_rnd_functions;
#if 0
TAILQ_INIT(&uaddr->ur_free);
#endif
return &uaddr->ur_uaddr;
}
int
uaddr_rnd_select(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset,
vm_prot_t prot, vaddr_t hint)
{
struct vmspace *vm;
vaddr_t minaddr, maxaddr;
vaddr_t guard_sz;
vaddr_t low_addr, high_addr;
struct vm_map_entry *entry, *next;
vsize_t before_gap, after_gap;
vaddr_t tmp;
KASSERT((map->flags & VM_MAP_ISVMSPACE) != 0);
vm = (struct vmspace *)map;
/* Deal with guardpages: search for space with one extra page. */
guard_sz = ((map->flags & VM_MAP_GUARDPAGES) == 0 ? 0 : PAGE_SIZE);
if (uaddr->uaddr_maxaddr - guard_sz < sz)
return ENOMEM;
minaddr = uvm_addr_align_forward(uaddr->uaddr_minaddr, align, offset);
maxaddr = uvm_addr_align_backward(uaddr->uaddr_maxaddr - sz - guard_sz,
align, offset);
/* Quick fail if the allocation won't fit. */
if (minaddr >= maxaddr)
return ENOMEM;
/* Select a hint. */
if (hint == 0)
hint = uvm_map_hint(vm, prot, minaddr, maxaddr);
/* Clamp hint to uaddr range. */
hint = MIN(MAX(hint, minaddr), maxaddr);
/* Align hint to align,offset parameters. */
tmp = hint;
hint = uvm_addr_align_forward(tmp, align, offset);
/* Check for overflow during alignment. */
if (hint < tmp || hint > maxaddr)
return ENOMEM; /* Compatibility mode: never look backwards. */
before_gap = 0;
after_gap = guard_sz;
hint -= MIN(hint, before_gap);
/*
* Use the augmented address tree to look up the first entry
* at or after hint with sufficient space.
*
* This code is the original optimized code, but will fail if the
* subtree it looks at does have sufficient space, but fails to meet
* the align constraint.
*
* Guard: subtree is not exhausted and max(fspace) >= required.
*/
entry = uvm_map_entrybyaddr(&map->addr, hint);
/* Walk up the tree, until there is at least sufficient space. */
while (entry != NULL &&
entry->fspace_augment < before_gap + after_gap + sz)
entry = RBT_PARENT(uvm_map_addr, entry);
while (entry != NULL) {
/* Test if this fits. */
if (VMMAP_FREE_END(entry) > hint &&
uvm_map_uaddr_e(map, entry) == uaddr &&
uvm_addr_fitspace(&low_addr, &high_addr,
MAX(uaddr->uaddr_minaddr, VMMAP_FREE_START(entry)),
MIN(uaddr->uaddr_maxaddr, VMMAP_FREE_END(entry)),
sz, align, offset, before_gap, after_gap) == 0) {
*entry_out = entry;
if (hint >= low_addr && hint <= high_addr)
*addr_out = hint;
else
*addr_out = low_addr;
return 0;
}
/* RBT_NEXT, but skip subtrees that cannot possible fit. */
next = RBT_RIGHT(uvm_map_addr, entry);
if (next != NULL &&
next->fspace_augment >= before_gap + after_gap + sz) {
entry = next;
while ((next = RBT_LEFT(uvm_map_addr, entry)) !=
NULL)
entry = next;
} else {
do_parent:
next = RBT_PARENT(uvm_map_addr, entry);
if (next == NULL)
entry = NULL;
else if (RBT_LEFT(uvm_map_addr, next) == entry)
entry = next;
else {
entry = next;
goto do_parent;
}
}
}
/* Lookup failed. */
return ENOMEM;
}
/*
* Destroy a uaddr_rnd_state structure.
*/
void
uaddr_rnd_destroy(struct uvm_addr_state *uaddr)
{
pool_put(&uaddr_rnd_pool, uaddr);
}
/*
* Add entry to tailq.
*/
void
uaddr_rnd_insert(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry *entry)
{
return;
}
/*
* Remove entry from tailq.
*/
void
uaddr_rnd_remove(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry *entry)
{
return;
}
#if 0
#if defined(DEBUG) || defined(DDB)
void
uaddr_rnd_print(struct uvm_addr_state *uaddr_p, boolean_t full,
int (*pr)(const char*, ...))
{
struct vm_map_entry *entry;
struct uaddr_rnd_state *uaddr;
vaddr_t addr;
size_t count;
vsize_t space;
uaddr = (struct uaddr_rnd_state *)uaddr_p;
addr = 0;
count = 0;
space = 0;
TAILQ_FOREACH(entry, &uaddr->ur_free, dfree.tailq) {
count++;
space += entry->fspace;
if (full) {
(*pr)("\tentry %p: 0x%lx-0x%lx G=0x%lx F=0x%lx\n",
entry, entry->start, entry->end,
entry->guard, entry->fspace);
(*pr)("\t\tfree: 0x%lx-0x%lx\n",
VMMAP_FREE_START(entry), VMMAP_FREE_END(entry));
}
if (entry->start < addr) {
if (!full)
(*pr)("\tentry %p: 0x%lx-0x%lx "
"G=0x%lx F=0x%lx\n",
entry, entry->start, entry->end,
entry->guard, entry->fspace);
(*pr)("\t\tstart=0x%lx, expected at least 0x%lx\n",
entry->start, addr);
}
addr = VMMAP_FREE_END(entry);
}
(*pr)("\t0x%lu entries, 0x%lx free bytes\n", count, space);
}
#endif /* DEBUG || DDB */
#endif
/*
* Kernel allocation bootstrap logic.
*/
const struct uvm_addr_functions uaddr_kernel_functions = {
.uaddr_select = &uaddr_kbootstrap_select,
.uaddr_destroy = &uaddr_kbootstrap_destroy,
.uaddr_name = "uaddr_kbootstrap"
};
/*
* Select an address from the map.
*
* This function ignores the uaddr spec and instead uses the map directly.
* Because of that property, the uaddr algorithm can be shared across all
* kernel maps.
*/
int
uaddr_kbootstrap_select(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset, vm_prot_t prot, vaddr_t hint)
{
vaddr_t tmp;
RBT_FOREACH(*entry_out, uvm_map_addr, &map->addr) {
if (VMMAP_FREE_END(*entry_out) <= uvm_maxkaddr &&
uvm_addr_fitspace(addr_out, &tmp,
VMMAP_FREE_START(*entry_out), VMMAP_FREE_END(*entry_out),
sz, align, offset, 0, 0) == 0)
return 0;
}
return ENOMEM;
}
/*
* Don't destroy the kernel bootstrap allocator.
*/
void
uaddr_kbootstrap_destroy(struct uvm_addr_state *uaddr)
{
KASSERT(uaddr == (struct uvm_addr_state *)&uaddr_kbootstrap);
}
#ifndef SMALL_KERNEL
/*
* Best fit algorithm.
*/
const struct uvm_addr_functions uaddr_bestfit_functions = {
.uaddr_select = &uaddr_bestfit_select,
.uaddr_free_insert = &uaddr_bestfit_insert,
.uaddr_free_remove = &uaddr_bestfit_remove,
.uaddr_destroy = &uaddr_bestfit_destroy,
.uaddr_name = "uaddr_bestfit"
};
struct uvm_addr_state *
uaddr_bestfit_create(vaddr_t minaddr, vaddr_t maxaddr)
{
struct uaddr_bestfit_state *uaddr;
uaddr = pool_get(&uaddr_bestfit_pool, PR_WAITOK);
uaddr->ubf_uaddr.uaddr_minaddr = minaddr;
uaddr->ubf_uaddr.uaddr_maxaddr = maxaddr;
uaddr->ubf_uaddr.uaddr_functions = &uaddr_bestfit_functions;
RBT_INIT(uaddr_free_rbtree, &uaddr->ubf_free);
return &uaddr->ubf_uaddr;
}
void
uaddr_bestfit_destroy(struct uvm_addr_state *uaddr)
{
pool_put(&uaddr_bestfit_pool, uaddr);
}
void
uaddr_bestfit_insert(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry *entry)
{
struct uaddr_bestfit_state *uaddr;
struct vm_map_entry *rb_rv;
uaddr = (struct uaddr_bestfit_state *)uaddr_p;
if ((rb_rv = RBT_INSERT(uaddr_free_rbtree, &uaddr->ubf_free, entry)) !=
NULL) {
panic("%s: duplicate insertion: state %p "
"inserting %p, colliding with %p", __func__,
uaddr, entry, rb_rv);
}
}
void
uaddr_bestfit_remove(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry *entry)
{
struct uaddr_bestfit_state *uaddr;
uaddr = (struct uaddr_bestfit_state *)uaddr_p;
if (RBT_REMOVE(uaddr_free_rbtree, &uaddr->ubf_free, entry) != entry)
panic("%s: entry was not in tree", __func__);
}
int
uaddr_bestfit_select(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset,
vm_prot_t prot, vaddr_t hint)
{
vaddr_t min, max;
struct uaddr_bestfit_state *uaddr;
struct vm_map_entry *entry;
vsize_t guardsz;
uaddr = (struct uaddr_bestfit_state *)uaddr_p;
guardsz = ((map->flags & VM_MAP_GUARDPAGES) ? PAGE_SIZE : 0);
if (sz + guardsz < sz)
return ENOMEM;
/*
* Find smallest item on freelist capable of holding item.
* Deal with guardpages: search for space with one extra page.
*/
entry = uvm_addr_entrybyspace(&uaddr->ubf_free, sz + guardsz);
if (entry == NULL)
return ENOMEM;
/*
* Walk the tree until we find an entry that fits.
*/
while (uvm_addr_fitspace(&min, &max,
VMMAP_FREE_START(entry), VMMAP_FREE_END(entry),
sz, align, offset, 0, guardsz) != 0) {
entry = RBT_NEXT(uaddr_free_rbtree, entry);
if (entry == NULL)
return ENOMEM;
}
/*
* Return the address that generates the least fragmentation.
*/
*entry_out = entry;
*addr_out = (min - VMMAP_FREE_START(entry) <=
VMMAP_FREE_END(entry) - guardsz - sz - max ?
min : max);
return 0;
}
#endif /* !SMALL_KERNEL */
#ifndef SMALL_KERNEL
/*
* A userspace allocator based on pivots.
*/
const struct uvm_addr_functions uaddr_pivot_functions = {
.uaddr_select = &uaddr_pivot_select,
.uaddr_free_insert = &uaddr_pivot_insert,
.uaddr_free_remove = &uaddr_pivot_remove,
.uaddr_destroy = &uaddr_pivot_destroy,
#if defined(DEBUG) || defined(DDB)
.uaddr_print = &uaddr_pivot_print,
#endif /* DEBUG || DDB */
.uaddr_name = "uaddr_pivot"
};
/*
* A special random function for pivots.
*
* This function will return:
* - a random number
* - a multiple of PAGE_SIZE
* - at least PAGE_SIZE
*
* The random function has a slightly higher change to return a small number.
*/
vsize_t
uaddr_pivot_random(void)
{
int r;
/*
* The sum of two six-sided dice will have a normal distribution.
* We map the highest probable number to 1, by folding the curve
* (think of a graph on a piece of paper, that you fold).
*
* Because the fold happens at PIVOT_RND - 1, the numbers 0 and 1
* have the same and highest probability of happening.
*/
r = arc4random_uniform(PIVOT_RND) + arc4random_uniform(PIVOT_RND) -
(PIVOT_RND - 1);
if (r < 0)
r = -r;
/*
* Make the returned value at least PAGE_SIZE and a multiple of
* PAGE_SIZE.
*/
return (vaddr_t)(1 + r) << PAGE_SHIFT;
}
/*
* Select a new pivot.
*
* A pivot must:
* - be chosen random
* - have a randomly chosen gap before it, where the uaddr_state starts
* - have a randomly chosen gap after it, before the uaddr_state ends
*
* Furthermore, the pivot must provide sufficient space for the allocation.
* The addr will be set to the selected address.
*
* Returns ENOMEM on failure.
*/
int
uaddr_pivot_newpivot(struct vm_map *map, struct uaddr_pivot_state *uaddr,
struct uaddr_pivot *pivot,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset,
vsize_t before_gap, vsize_t after_gap)
{
struct vm_map_entry *entry, *found;
vaddr_t minaddr, maxaddr;
vsize_t dist;
vaddr_t found_minaddr, found_maxaddr;
vaddr_t min, max;
vsize_t arc4_arg;
int fit_error;
u_int32_t path;
minaddr = uaddr->up_uaddr.uaddr_minaddr;
maxaddr = uaddr->up_uaddr.uaddr_maxaddr;
KASSERT(minaddr < maxaddr);
#ifdef DIAGNOSTIC
if (minaddr + 2 * PAGE_SIZE > maxaddr) {
panic("uaddr_pivot_newpivot: cannot grant random pivot "
"in area less than 2 pages (size = 0x%lx)",
maxaddr - minaddr);
}
#endif /* DIAGNOSTIC */
/*
* Gap calculation: 1/32 of the size of the managed area.
*
* At most: sufficient to not get truncated at arc4random.
* At least: 2 PAGE_SIZE
*
* minaddr and maxaddr will be changed according to arc4random.
*/
dist = MAX((maxaddr - minaddr) / 32, 2 * (vaddr_t)PAGE_SIZE);
if (dist >> PAGE_SHIFT > 0xffffffff) {
minaddr += (vsize_t)arc4random() << PAGE_SHIFT;
maxaddr -= (vsize_t)arc4random() << PAGE_SHIFT;
} else {
minaddr += (vsize_t)arc4random_uniform(dist >> PAGE_SHIFT) <<
PAGE_SHIFT;
maxaddr -= (vsize_t)arc4random_uniform(dist >> PAGE_SHIFT) <<
PAGE_SHIFT;
}
/*
* A very fast way to find an entry that will be large enough
* to hold the allocation, but still is found more or less
* randomly: the tree path selector has a 50% chance to go for
* a bigger or smaller entry.
*
* Note that the memory may actually be available,
* but the fragmentation may be so bad and the gaps chosen
* so unfortunately, that the allocation will not succeed.
* Or the alignment can only be satisfied by an entry that
* is not visited in the randomly selected path.
*
* This code finds an entry with sufficient space in O(log n) time.
*/
path = arc4random();
found = NULL;
entry = RBT_ROOT(uaddr_free_rbtree, &uaddr->up_free);
while (entry != NULL) {
fit_error = uvm_addr_fitspace(&min, &max,
MAX(VMMAP_FREE_START(entry), minaddr),
MIN(VMMAP_FREE_END(entry), maxaddr),
sz, align, offset, before_gap, after_gap);
/* It fits, save this entry. */
if (fit_error == 0) {
found = entry;
found_minaddr = min;
found_maxaddr = max;
}
/* Next. */
if (fit_error != 0)
entry = RBT_RIGHT(uaddr_free_rbtree, entry);
else if ((path & 0x1) == 0) {
path >>= 1;
entry = RBT_RIGHT(uaddr_free_rbtree, entry);
} else {
path >>= 1;
entry = RBT_LEFT(uaddr_free_rbtree, entry);
}
}
if (found == NULL)
return ENOMEM; /* Not found a large enough region. */
/*
* Calculate a random address within found.
*
* found_minaddr and found_maxaddr are already aligned, so be sure
* to select a multiple of align as the offset in the entry.
* Preferably, arc4random_uniform is used to provide no bias within
* the entry.
* However if the size of the entry exceeds arc4random_uniforms
* argument limit, we simply use arc4random (thus limiting ourselves
* to 4G * PAGE_SIZE bytes offset).
*/
if (found_maxaddr == found_minaddr)
*addr_out = found_minaddr;
else {
KASSERT(align >= PAGE_SIZE && (align & (align - 1)) == 0);
arc4_arg = found_maxaddr - found_minaddr;
if (arc4_arg > 0xffffffff) {
*addr_out = found_minaddr +
(arc4random() & ~(align - 1));
} else {
*addr_out = found_minaddr +
(arc4random_uniform(arc4_arg) & ~(align - 1));
}
}
/* Address was found in this entry. */
*entry_out = found;
/*
* Set up new pivot and return selected address.
*
* Depending on the direction of the pivot, the pivot must be placed
* at the bottom or the top of the allocation:
* - if the pivot moves upwards, place the pivot at the top of the
* allocation,
* - if the pivot moves downwards, place the pivot at the bottom
* of the allocation.
*/
pivot->entry = found;
pivot->dir = (arc4random() & 0x1 ? 1 : -1);
if (pivot->dir > 0)
pivot->addr = *addr_out + sz;
else
pivot->addr = *addr_out;
pivot->expire = PIVOT_EXPIRE - 1; /* First use is right now. */
return 0;
}
/*
* Pivot selector.
*
* Each time the selector is invoked, it will select a random pivot, which
* it will use to select memory with. The memory will be placed at the pivot,
* with a randomly sized gap between the allocation and the pivot.
* The pivot will then move so it will never revisit this address.
*
* Each allocation, the pivot expiry timer ticks. Once the pivot becomes
* expired, it will be replaced with a newly created pivot. Pivots also
* automatically expire if they fail to provide memory for an allocation.
*
* Expired pivots are replaced using the uaddr_pivot_newpivot() function,
* which will ensure the pivot points at memory in such a way that the
* allocation will succeed.
* As an added bonus, the uaddr_pivot_newpivot() function will perform the
* allocation immediately and move the pivot as appropriate.
*
* If uaddr_pivot_newpivot() fails to find a new pivot that will allow the
* allocation to succeed, it will not create a new pivot and the allocation
* will fail.
*
* A pivot running into used memory will automatically expire (because it will
* fail to allocate).
*
* Characteristics of the allocator:
* - best case, an allocation is O(log N)
* (it would be O(1), if it werent for the need to check if the memory is
* free; although that can be avoided...)
* - worst case, an allocation is O(log N)
* (the uaddr_pivot_newpivot() function has that complexity)
* - failed allocations always take O(log N)
* (the uaddr_pivot_newpivot() function will walk that deep into the tree).
*/
int
uaddr_pivot_select(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset,
vm_prot_t prot, vaddr_t hint)
{
struct uaddr_pivot_state *uaddr;
struct vm_map_entry *entry;
struct uaddr_pivot *pivot;
vaddr_t min, max;
vsize_t before_gap, after_gap;
int err;
/*
* When we have a hint, use the rnd allocator that finds the
* area that is closest to the hint, if there is such an area.
*/
if (hint != 0) {
if (uaddr_rnd_select(map, uaddr_p, entry_out, addr_out,
sz, align, offset, prot, hint) == 0)
return 0;
return ENOMEM;
}
/*
* Select a random pivot and a random gap sizes around the allocation.
*/
uaddr = (struct uaddr_pivot_state *)uaddr_p;
pivot = &uaddr->up_pivots[
arc4random_uniform(nitems(uaddr->up_pivots))];
before_gap = uaddr_pivot_random();
after_gap = uaddr_pivot_random();
if (pivot->addr == 0 || pivot->entry == NULL || pivot->expire == 0)
goto expired; /* Pivot is invalid (null or expired). */
/*
* Attempt to use the pivot to map the entry.
*/
entry = pivot->entry;
if (pivot->dir > 0) {
if (uvm_addr_fitspace(&min, &max,
MAX(VMMAP_FREE_START(entry), pivot->addr),
VMMAP_FREE_END(entry), sz, align, offset,
before_gap, after_gap) == 0) {
*addr_out = min;
*entry_out = entry;
pivot->addr = min + sz;
pivot->expire--;
return 0;
}
} else {
if (uvm_addr_fitspace(&min, &max,
VMMAP_FREE_START(entry),
MIN(VMMAP_FREE_END(entry), pivot->addr),
sz, align, offset, before_gap, after_gap) == 0) {
*addr_out = max;
*entry_out = entry;
pivot->addr = max;
pivot->expire--;
return 0;
}
}
expired:
/*
* Pivot expired or allocation failed.
* Use pivot selector to do the allocation and find a new pivot.
*/
err = uaddr_pivot_newpivot(map, uaddr, pivot, entry_out, addr_out,
sz, align, offset, before_gap, after_gap);
return err;
}
/*
* Free the pivot.
*/
void
uaddr_pivot_destroy(struct uvm_addr_state *uaddr)
{
pool_put(&uaddr_pivot_pool, uaddr);
}
/*
* Insert an entry with free space in the space tree.
*/
void
uaddr_pivot_insert(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry *entry)
{
struct uaddr_pivot_state *uaddr;
struct vm_map_entry *rb_rv;
struct uaddr_pivot *p;
vaddr_t check_addr;
vaddr_t start, end;
uaddr = (struct uaddr_pivot_state *)uaddr_p;
if ((rb_rv = RBT_INSERT(uaddr_free_rbtree, &uaddr->up_free, entry)) !=
NULL) {
panic("%s: duplicate insertion: state %p "
"inserting entry %p which collides with %p", __func__,
uaddr, entry, rb_rv);
}
start = VMMAP_FREE_START(entry);
end = VMMAP_FREE_END(entry);
/*
* Update all pivots that are contained in this entry.
*/
for (p = &uaddr->up_pivots[0];
p != &uaddr->up_pivots[nitems(uaddr->up_pivots)]; p++) {
check_addr = p->addr;
if (check_addr == 0)
continue;
if (p->dir < 0)
check_addr--;
if (start <= check_addr &&
check_addr < end) {
KASSERT(p->entry == NULL);
p->entry = entry;
}
}
}
/*
* Remove an entry with free space from the space tree.
*/
void
uaddr_pivot_remove(struct vm_map *map, struct uvm_addr_state *uaddr_p,
struct vm_map_entry *entry)
{
struct uaddr_pivot_state *uaddr;
struct uaddr_pivot *p;
uaddr = (struct uaddr_pivot_state *)uaddr_p;
if (RBT_REMOVE(uaddr_free_rbtree, &uaddr->up_free, entry) != entry)
panic("%s: entry was not in tree", __func__);
/*
* Inform any pivot with this entry that the entry is gone.
* Note that this does not automatically invalidate the pivot.
*/
for (p = &uaddr->up_pivots[0];
p != &uaddr->up_pivots[nitems(uaddr->up_pivots)]; p++) {
if (p->entry == entry)
p->entry = NULL;
}
}
/*
* Create a new pivot selector.
*
* Initially, all pivots are in the expired state.
* Two reasons for this:
* - it means this allocator will not take a huge amount of time
* - pivots select better on demand, because the pivot selection will be
* affected by preceding allocations:
* the next pivots will likely end up in different segments of free memory,
* that was segmented by an earlier allocation; better spread.
*/
struct uvm_addr_state *
uaddr_pivot_create(vaddr_t minaddr, vaddr_t maxaddr)
{
struct uaddr_pivot_state *uaddr;
uaddr = pool_get(&uaddr_pivot_pool, PR_WAITOK);
uaddr->up_uaddr.uaddr_minaddr = minaddr;
uaddr->up_uaddr.uaddr_maxaddr = maxaddr;
uaddr->up_uaddr.uaddr_functions = &uaddr_pivot_functions;
RBT_INIT(uaddr_free_rbtree, &uaddr->up_free);
memset(uaddr->up_pivots, 0, sizeof(uaddr->up_pivots));
return &uaddr->up_uaddr;
}
#if defined(DEBUG) || defined(DDB)
/*
* Print the uaddr_pivot_state.
*
* If full, a listing of all entries in the state will be provided.
*/
void
uaddr_pivot_print(struct uvm_addr_state *uaddr_p, boolean_t full,
int (*pr)(const char *, ...))
{
struct uaddr_pivot_state *uaddr;
struct uaddr_pivot *pivot;
struct vm_map_entry *entry;
int i;
vaddr_t check_addr;
uaddr = (struct uaddr_pivot_state *)uaddr_p;
for (i = 0; i < NUM_PIVOTS; i++) {
pivot = &uaddr->up_pivots[i];
(*pr)("\tpivot 0x%lx, epires in %d, direction %d\n",
pivot->addr, pivot->expire, pivot->dir);
}
if (!full)
return;
if (RBT_EMPTY(uaddr_free_rbtree, &uaddr->up_free))
(*pr)("\tempty\n");
/* Print list of free space. */
RBT_FOREACH(entry, uaddr_free_rbtree, &uaddr->up_free) {
(*pr)("\t0x%lx - 0x%lx free (0x%lx bytes)\n",
VMMAP_FREE_START(entry), VMMAP_FREE_END(entry),
VMMAP_FREE_END(entry) - VMMAP_FREE_START(entry));
for (i = 0; i < NUM_PIVOTS; i++) {
pivot = &uaddr->up_pivots[i];
check_addr = pivot->addr;
if (check_addr == 0)
continue;
if (pivot->dir < 0)
check_addr--;
if (VMMAP_FREE_START(entry) <= check_addr &&
check_addr < VMMAP_FREE_END(entry)) {
(*pr)("\t\tcontains pivot %d (0x%lx)\n",
i, pivot->addr);
}
}
}
}
#endif /* DEBUG || DDB */
#endif /* !SMALL_KERNEL */
#ifndef SMALL_KERNEL
/*
* Stack/break allocator.
*
* Stack area is grown into in the opposite direction of the stack growth,
* brk area is grown downward (because sbrk() grows upward).
*
* Both areas are grown into proportially: a weighted chance is used to
* select which one (stack or brk area) to try. If the allocation fails,
* the other one is tested.
*/
const struct uvm_addr_functions uaddr_stack_brk_functions = {
.uaddr_select = &uaddr_stack_brk_select,
.uaddr_destroy = &uaddr_destroy,
.uaddr_name = "uaddr_stckbrk"
};
/*
* Stack/brk address selector.
*/
int
uaddr_stack_brk_select(struct vm_map *map, struct uvm_addr_state *uaddr,
struct vm_map_entry **entry_out, vaddr_t *addr_out,
vsize_t sz, vaddr_t align, vaddr_t offset,
vm_prot_t prot, vaddr_t hint)
{
vaddr_t start;
vaddr_t end;
vsize_t before_gap;
vsize_t after_gap;
int dir;
/* Set up brk search strategy. */
start = MAX(map->b_start, uaddr->uaddr_minaddr);
end = MIN(map->b_end, uaddr->uaddr_maxaddr);
before_gap = 0;
after_gap = 0;
dir = -1; /* Opposite of brk() growth. */
if (end - start >= sz) {
if (uvm_addr_linsearch(map, uaddr, entry_out, addr_out,
0, sz, align, offset, dir, start, end - sz,
before_gap, after_gap) == 0)
return 0;
}
/* Set up stack search strategy. */
start = MAX(map->s_start, uaddr->uaddr_minaddr);
end = MIN(map->s_end, uaddr->uaddr_maxaddr);
before_gap = ((arc4random() & 0x3) + 1) << PAGE_SHIFT;
after_gap = ((arc4random() & 0x3) + 1) << PAGE_SHIFT;
#ifdef MACHINE_STACK_GROWS_UP
dir = -1;
#else
dir = 1;
#endif
if (end - start >= before_gap + after_gap &&
end - start - before_gap - after_gap >= sz) {
if (uvm_addr_linsearch(map, uaddr, entry_out, addr_out,
0, sz, align, offset, dir, start, end - sz,
before_gap, after_gap) == 0)
return 0;
}
return ENOMEM;
}
struct uvm_addr_state *
uaddr_stack_brk_create(vaddr_t minaddr, vaddr_t maxaddr)
{
struct uvm_addr_state* uaddr;
uaddr = pool_get(&uaddr_pool, PR_WAITOK);
uaddr->uaddr_minaddr = minaddr;
uaddr->uaddr_maxaddr = maxaddr;
uaddr->uaddr_functions = &uaddr_stack_brk_functions;
return uaddr;
}
#endif /* !SMALL_KERNEL */
#ifndef SMALL_KERNEL
/*
* Free space comparison.
* Compares smaller free-space before larger free-space.
*/
static inline int
uvm_mapent_fspace_cmp(const struct vm_map_entry *e1,
const struct vm_map_entry *e2)
{
if (e1->fspace != e2->fspace)
return (e1->fspace < e2->fspace ? -1 : 1);
return (e1->start < e2->start ? -1 : e1->start > e2->start);
}
RBT_GENERATE(uaddr_free_rbtree, vm_map_entry, dfree.rbtree,
uvm_mapent_fspace_cmp);
#endif /* !SMALL_KERNEL */
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
/* $OpenBSD: udf_vfsops.c,v 1.70 2022/01/11 03:13:59 jsg Exp $ */
/*
* Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/fs/udf/udf_vfsops.c,v 1.25 2005/01/25 15:52:03 phk Exp $
*/
/*
* Ported to OpenBSD by Pedro Martelletto in February 2005.
*/
/*
* Ok, here's how it goes. The UDF specs are pretty clear on how each data
* structure is made up, but not very clear on how they relate to each other.
* Here is the skinny... This demonstrates a filesystem with one file in the
* root directory. Subdirectories are treated just as normal files, but they
* have File Id Descriptors of their children as their file data. As for the
* Anchor Volume Descriptor Pointer, it can exist in two of the following three
* places: sector 256, sector n (the max sector of the disk), or sector
* n - 256. It's a pretty good bet that one will exist at sector 256 though.
* One caveat is unclosed CD media. For that, sector 256 cannot be written,
* so the Anchor Volume Descriptor Pointer can exist at sector 512 until the
* media is closed.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/queue.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/endian.h>
#include <sys/specdev.h>
#include <crypto/siphash.h>
#include <isofs/udf/ecma167-udf.h>
#include <isofs/udf/udf.h>
#include <isofs/udf/udf_extern.h>
struct pool udf_trans_pool;
struct pool unode_pool;
struct pool udf_ds_pool;
int udf_find_partmaps(struct umount *, struct logvol_desc *);
int udf_get_vpartmap(struct umount *, struct part_map_virt *);
int udf_get_spartmap(struct umount *, struct part_map_spare *);
int udf_get_mpartmap(struct umount *, struct part_map_meta *);
int udf_mountfs(struct vnode *, struct mount *, uint32_t, struct proc *);
const struct vfsops udf_vfsops = {
.vfs_mount = udf_mount,
.vfs_start = udf_start,
.vfs_unmount = udf_unmount,
.vfs_root = udf_root,
.vfs_quotactl = udf_quotactl,
.vfs_statfs = udf_statfs,
.vfs_sync = udf_sync,
.vfs_vget = udf_vget,
.vfs_fhtovp = udf_fhtovp,
.vfs_vptofh = udf_vptofh,
.vfs_init = udf_init,
.vfs_sysctl = udf_sysctl,
.vfs_checkexp = udf_checkexp,
};
int
udf_init(struct vfsconf *foo)
{
pool_init(&udf_trans_pool, MAXNAMLEN * sizeof(unicode_t), 0, IPL_NONE,
PR_WAITOK, "udftrpl", NULL);
pool_init(&unode_pool, sizeof(struct unode), 0, IPL_NONE,
PR_WAITOK, "udfndpl", NULL);
pool_init(&udf_ds_pool, sizeof(struct udf_dirstream), 0, IPL_NONE,
PR_WAITOK, "udfdspl", NULL);
return (0);
}
int
udf_start(struct mount *mp, int flags, struct proc *p)
{
return (0);
}
int
udf_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
struct vnode *devvp; /* vnode of the mount device */
struct udf_args *args = data;
char fspec[MNAMELEN];
int error;
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
mp->mnt_flag |= MNT_RDONLY;
#ifdef UDF_DEBUG
printf("udf_mount: enforcing read-only mode\n");
#endif
}
/*
* No root filesystem support. Probably not a big deal, since the
* bootloader doesn't understand UDF.
*/
if (mp->mnt_flag & MNT_ROOTFS)
return (EOPNOTSUPP);
/*
* If updating, check whether changing from read-only to
* read/write; if there is no device name, that's all we do.
*/
if (mp->mnt_flag & MNT_UPDATE) {
return (0);
}
error = copyinstr(args->fspec, fspec, sizeof(fspec), NULL);
if (error)
return (error);
NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec, p);
if ((error = namei(ndp)))
return (error);
devvp = ndp->ni_vp;
if (devvp->v_type != VBLK) {
vrele(devvp);
return (ENOTBLK);
}
if (major(devvp->v_rdev) >= nblkdev) {
vrele(devvp);
return (ENXIO);
}
if ((error = udf_mountfs(devvp, mp, args->lastblock, p))) {
vrele(devvp);
return (error);
}
/*
* Keep a copy of the mount information.
*/
bzero(mp->mnt_stat.f_mntonname, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, fspec, MNAMELEN);
bzero(mp->mnt_stat.f_mntfromspec, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
return (0);
};
/*
* Check the descriptor tag for both the correct id and correct checksum.
* Return zero if all is good, EINVAL if not.
*/
int
udf_checktag(struct desc_tag *tag, uint16_t id)
{
uint8_t *itag;
uint8_t i, cksum = 0;
itag = (uint8_t *)tag;
if (letoh16(tag->id) != id)
return (EINVAL);
for (i = 0; i < 15; i++)
cksum = cksum + itag[i];
cksum = cksum - itag[4];
if (cksum == tag->cksum)
return (0);
return (EINVAL);
}
int
udf_mountfs(struct vnode *devvp, struct mount *mp, uint32_t lb, struct proc *p)
{
struct buf *bp = NULL;
struct anchor_vdp avdp;
struct umount *ump = NULL;
struct part_desc *pd;
struct logvol_desc *lvd;
struct fileset_desc *fsd;
struct extfile_entry *xfentry;
struct file_entry *fentry;
uint32_t sector, size, mvds_start, mvds_end;
uint32_t fsd_offset = 0;
uint16_t part_num = 0, fsd_part = 0;
int error = EINVAL;
int logvol_found = 0, part_found = 0, fsd_found = 0;
int bsize;
/*
* Disallow multiple mounts of the same device.
* Disallow mounting of a device that is currently in use
* (except for root, which might share swap device for miniroot).
* Flush out any old buffers remaining from a previous use.
*/
if ((error = vfs_mountedon(devvp)))
return (error);
if (vcount(devvp) > 1 && devvp != rootvp)
return (EBUSY);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error)
return (error);
error = VOP_OPEN(devvp, FREAD, FSCRED, p);
if (error)
return (error);
ump = malloc(sizeof(*ump), M_UDFMOUNT, M_WAITOK | M_ZERO);
mp->mnt_data = ump;
mp->mnt_stat.f_fsid.val[0] = devvp->v_rdev;
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
mp->mnt_stat.f_namemax = NAME_MAX;
mp->mnt_flag |= MNT_LOCAL;
ump->um_mountp = mp;
ump->um_dev = devvp->v_rdev;
ump->um_devvp = devvp;
bsize = 2048; /* Should probe the media for its size. */
/*
* Get the Anchor Volume Descriptor Pointer from sector 256.
* Should also check sector n - 256, n, and 512.
*/
sector = 256;
if ((error = bread(devvp, sector * btodb(bsize), bsize, &bp)) != 0)
goto bail;
if ((error = udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR)))
goto bail;
bcopy(bp->b_data, &avdp, sizeof(struct anchor_vdp));
brelse(bp);
bp = NULL;
/*
* Extract the Partition Descriptor and Logical Volume Descriptor
* from the Volume Descriptor Sequence.
* Should we care about the partition type right now?
* What about multiple partitions?
*/
mvds_start = letoh32(avdp.main_vds_ex.loc);
mvds_end = mvds_start + (letoh32(avdp.main_vds_ex.len) - 1) / bsize;
for (sector = mvds_start; sector < mvds_end; sector++) {
if ((error = bread(devvp, sector * btodb(bsize), bsize,
&bp)) != 0) {
printf("Can't read sector %d of VDS\n", sector);
goto bail;
}
lvd = (struct logvol_desc *)bp->b_data;
if (!udf_checktag(&lvd->tag, TAGID_LOGVOL)) {
ump->um_bsize = letoh32(lvd->lb_size);
ump->um_bmask = ump->um_bsize - 1;
ump->um_bshift = ffs(ump->um_bsize) - 1;
fsd_part = letoh16(lvd->_lvd_use.fsd_loc.loc.part_num);
fsd_offset = letoh32(lvd->_lvd_use.fsd_loc.loc.lb_num);
if (udf_find_partmaps(ump, lvd))
break;
logvol_found = 1;
}
pd = (struct part_desc *)bp->b_data;
if (!udf_checktag(&pd->tag, TAGID_PARTITION)) {
part_found = 1;
part_num = letoh16(pd->part_num);
ump->um_len = ump->um_reallen = letoh32(pd->part_len);
ump->um_start = ump->um_realstart = letoh32(pd->start_loc);
}
brelse(bp);
bp = NULL;
if ((part_found) && (logvol_found))
break;
}
if (!part_found || !logvol_found) {
error = EINVAL;
goto bail;
}
if (ISSET(ump->um_flags, UDF_MNT_USES_META)) {
/* Read Metadata File 'File Entry' to find Metadata file. */
struct long_ad *la;
sector = ump->um_start + ump->um_meta_start; /* Set in udf_get_mpartmap() */
if ((error = RDSECTOR(devvp, sector, ump->um_bsize, &bp)) != 0) {
printf("Cannot read sector %d for Metadata File Entry\n", sector);
error = EINVAL;
goto bail;
}
xfentry = (struct extfile_entry *)bp->b_data;
fentry = (struct file_entry *)bp->b_data;
if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0)
la = (struct long_ad *)&xfentry->data[letoh32(xfentry->l_ea)];
else if (udf_checktag(&fentry->tag, TAGID_FENTRY) == 0)
la = (struct long_ad *)&fentry->data[letoh32(fentry->l_ea)];
else {
printf("Invalid Metadata File FE @ sector %d! (tag.id %d)\n",
sector, fentry->tag.id);
error = EINVAL;
goto bail;
}
ump->um_meta_start = letoh32(la->loc.lb_num);
ump->um_meta_len = letoh32(la->len);
if (bp != NULL) {
brelse(bp);
bp = NULL;
}
} else if (fsd_part != part_num) {
printf("FSD does not lie within the partition!\n");
error = EINVAL;
goto bail;
}
mtx_init(&ump->um_hashmtx, IPL_NONE);
ump->um_hashtbl = hashinit(UDF_HASHTBLSIZE, M_UDFMOUNT, M_WAITOK,
&ump->um_hashsz);
arc4random_buf(&ump->um_hashkey, sizeof(ump->um_hashkey));
/* Get the VAT, if needed */
if (ump->um_flags & UDF_MNT_FIND_VAT) {
error = udf_vat_get(ump, lb);
if (error)
goto bail;
}
/*
* Grab the Fileset Descriptor
* Thanks to Chuck McCrobie <mccrobie@cablespeed.com> for pointing
* me in the right direction here.
*/
if (ISSET(ump->um_flags, UDF_MNT_USES_META))
sector = ump->um_meta_start;
else
sector = fsd_offset;
udf_vat_map(ump, §or);
if ((error = RDSECTOR(devvp, sector, ump->um_bsize, &bp)) != 0) {
printf("Cannot read sector %d of FSD\n", sector);
goto bail;
}
fsd = (struct fileset_desc *)bp->b_data;
if (!udf_checktag(&fsd->tag, TAGID_FSD)) {
fsd_found = 1;
bcopy(&fsd->rootdir_icb, &ump->um_root_icb,
sizeof(struct long_ad));
if (ISSET(ump->um_flags, UDF_MNT_USES_META)) {
ump->um_root_icb.loc.lb_num += ump->um_meta_start;
ump->um_root_icb.loc.part_num = part_num;
}
}
brelse(bp);
bp = NULL;
if (!fsd_found) {
printf("Couldn't find the fsd\n");
error = EINVAL;
goto bail;
}
/*
* Find the file entry for the root directory.
*/
sector = letoh32(ump->um_root_icb.loc.lb_num);
size = letoh32(ump->um_root_icb.len);
udf_vat_map(ump, §or);
if ((error = udf_readlblks(ump, sector, size, &bp)) != 0) {
printf("Cannot read sector %d\n", sector);
goto bail;
}
xfentry = (struct extfile_entry *)bp->b_data;
fentry = (struct file_entry *)bp->b_data;
error = udf_checktag(&xfentry->tag, TAGID_EXTFENTRY);
if (error) {
error = udf_checktag(&fentry->tag, TAGID_FENTRY);
if (error) {
printf("Invalid root file entry!\n");
goto bail;
}
}
brelse(bp);
bp = NULL;
devvp->v_specmountpoint = mp;
return (0);
bail:
if (ump != NULL) {
hashfree(ump->um_hashtbl, UDF_HASHTBLSIZE, M_UDFMOUNT);
free(ump, M_UDFMOUNT, 0);
mp->mnt_data = NULL;
mp->mnt_flag &= ~MNT_LOCAL;
}
if (devvp->v_specinfo)
devvp->v_specmountpoint = NULL;
if (bp != NULL)
brelse(bp);
vn_lock(devvp, LK_EXCLUSIVE|LK_RETRY);
VOP_CLOSE(devvp, FREAD, FSCRED, p);
VOP_UNLOCK(devvp);
return (error);
}
int
udf_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct umount *ump;
struct vnode *devvp;
int error, flags = 0;
ump = VFSTOUDFFS(mp);
devvp = ump->um_devvp;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
if ((error = vflush(mp, NULL, flags)))
return (error);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
vinvalbuf(devvp, V_SAVE, NOCRED, p, 0, INFSLP);
(void)VOP_CLOSE(devvp, FREAD, NOCRED, p);
VOP_UNLOCK(devvp);
devvp->v_specmountpoint = NULL;
vrele(devvp);
if (ump->um_flags & UDF_MNT_USES_VAT)
free(ump->um_vat, M_UDFMOUNT, 0);
if (ump->um_stbl != NULL)
free(ump->um_stbl, M_UDFMOUNT, 0);
hashfree(ump->um_hashtbl, UDF_HASHTBLSIZE, M_UDFMOUNT);
free(ump, M_UDFMOUNT, 0);
mp->mnt_data = NULL;
mp->mnt_flag &= ~MNT_LOCAL;
return (0);
}
int
udf_root(struct mount *mp, struct vnode **vpp)
{
struct umount *ump;
struct vnode *vp;
udfino_t id;
int error;
ump = VFSTOUDFFS(mp);
id = udf_getid(&ump->um_root_icb);
error = udf_vget(mp, id, vpp);
if (error)
return (error);
vp = *vpp;
vp->v_flag |= VROOT;
return (0);
}
int
udf_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
struct proc *p)
{
return (EOPNOTSUPP);
}
int
udf_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct umount *ump;
ump = VFSTOUDFFS(mp);
sbp->f_bsize = ump->um_bsize;
sbp->f_iosize = ump->um_bsize;
sbp->f_blocks = ump->um_len;
sbp->f_bfree = 0;
sbp->f_bavail = 0;
sbp->f_files = 0;
sbp->f_ffree = 0;
sbp->f_favail = 0;
copy_statfs_info(sbp, mp);
return (0);
}
int
udf_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, struct proc *p)
{
return (0);
}
int
udf_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
struct buf *bp;
struct vnode *devvp;
struct umount *ump;
struct proc *p;
struct vnode *vp, *nvp;
struct unode *up;
struct extfile_entry *xfe;
struct file_entry *fe;
uint32_t sector;
int error, size;
if (ino > (udfino_t)-1)
panic("udf_vget: alien ino_t %llu", (unsigned long long)ino);
p = curproc;
bp = NULL;
*vpp = NULL;
ump = VFSTOUDFFS(mp);
/* See if we already have this in the cache */
if ((error = udf_hashlookup(ump, ino, LK_EXCLUSIVE, vpp)) != 0)
return (error);
if (*vpp != NULL)
return (0);
/*
* Allocate memory and check the tag id's before grabbing a new
* vnode, since it's hard to roll back if there is a problem.
*/
up = pool_get(&unode_pool, PR_WAITOK | PR_ZERO);
/*
* Copy in the file entry. Per the spec, the size can only be 1 block.
*/
sector = ino;
devvp = ump->um_devvp;
udf_vat_map(ump, §or);
if ((error = RDSECTOR(devvp, sector, ump->um_bsize, &bp)) != 0) {
printf("Cannot read sector %d\n", sector);
pool_put(&unode_pool, up);
if (bp != NULL)
brelse(bp);
return (error);
}
xfe = (struct extfile_entry *)bp->b_data;
fe = (struct file_entry *)bp->b_data;
error = udf_checktag(&xfe->tag, TAGID_EXTFENTRY);
if (error == 0) {
size = letoh32(xfe->l_ea) + letoh32(xfe->l_ad);
} else {
error = udf_checktag(&fe->tag, TAGID_FENTRY);
if (error) {
printf("Invalid file entry!\n");
pool_put(&unode_pool, up);
if (bp != NULL)
brelse(bp);
return (ENOMEM);
} else
size = letoh32(fe->l_ea) + letoh32(fe->l_ad);
}
/* Allocate max size of FE/XFE. */
up->u_fentry = malloc(size + UDF_EXTFENTRY_SIZE, M_UDFFENTRY, M_NOWAIT | M_ZERO);
if (up->u_fentry == NULL) {
pool_put(&unode_pool, up);
if (bp != NULL)
brelse(bp);
return (ENOMEM); /* Cannot allocate file entry block */
}
if (udf_checktag(&xfe->tag, TAGID_EXTFENTRY) == 0)
bcopy(bp->b_data, up->u_fentry, size + UDF_EXTFENTRY_SIZE);
else
bcopy(bp->b_data, up->u_fentry, size + UDF_FENTRY_SIZE);
brelse(bp);
bp = NULL;
if ((error = udf_allocv(mp, &vp, p))) {
free(up->u_fentry, M_UDFFENTRY, 0);
pool_put(&unode_pool, up);
return (error); /* Error from udf_allocv() */
}
up->u_vnode = vp;
up->u_ino = ino;
up->u_devvp = ump->um_devvp;
up->u_dev = ump->um_dev;
up->u_ump = ump;
vp->v_data = up;
vref(ump->um_devvp);
rrw_init_flags(&up->u_lock, "unode", RWL_DUPOK | RWL_IS_VNODE);
/*
* udf_hashins() will lock the vnode for us.
*/
udf_hashins(up);
switch (up->u_fentry->icbtag.file_type) {
default:
printf("Unrecognized file type (%d)\n", vp->v_type);
vp->v_type = VREG;
break;
case UDF_ICB_FILETYPE_DIRECTORY:
vp->v_type = VDIR;
break;
case UDF_ICB_FILETYPE_BLOCKDEVICE:
vp->v_type = VBLK;
break;
case UDF_ICB_FILETYPE_CHARDEVICE:
vp->v_type = VCHR;
break;
case UDF_ICB_FILETYPE_FIFO:
vp->v_type = VFIFO;
break;
case UDF_ICB_FILETYPE_SOCKET:
vp->v_type = VSOCK;
break;
case UDF_ICB_FILETYPE_SYMLINK:
vp->v_type = VLNK;
break;
case UDF_ICB_FILETYPE_RANDOMACCESS:
case UDF_ICB_FILETYPE_REALTIME:
case UDF_ICB_FILETYPE_UNKNOWN:
vp->v_type = VREG;
break;
}
/* check if this is a vnode alias */
if ((nvp = checkalias(vp, up->u_dev, ump->um_mountp)) != NULL) {
printf("found a vnode alias\n");
/*
* Discard unneeded vnode, but save its udf_node.
* Note that the lock is carried over in the udf_node
*/
nvp->v_data = vp->v_data;
vp->v_data = NULL;
vp->v_op = &spec_vops;
vrele(vp);
vgone(vp);
/*
* Reinitialize aliased inode.
*/
vp = nvp;
ump->um_devvp = vp;
}
*vpp = vp;
return (0);
}
struct ifid {
u_short ifid_len;
u_short ifid_pad;
int ifid_ino;
long ifid_start;
};
int
udf_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
struct ifid *ifhp;
struct vnode *nvp;
int error;
ifhp = (struct ifid *)fhp;
if ((error = VFS_VGET(mp, ifhp->ifid_ino, &nvp)) != 0) {
*vpp = NULLVP;
return (error);
}
*vpp = nvp;
return (0);
}
int
udf_vptofh(struct vnode *vp, struct fid *fhp)
{
struct unode *up;
struct ifid *ifhp;
up = VTOU(vp);
ifhp = (struct ifid *)fhp;
ifhp->ifid_len = sizeof(struct ifid);
ifhp->ifid_ino = up->u_ino;
return (0);
}
int
udf_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
return (EINVAL);
}
int
udf_checkexp(struct mount *mp, struct mbuf *nam, int *exflagsp,
struct ucred **credanonp)
{
return (EACCES); /* For the time being */
}
/* Handle a virtual partition map */
int
udf_get_vpartmap(struct umount *ump, struct part_map_virt *pmv)
{
ump->um_flags |= UDF_MNT_FIND_VAT; /* Should do more than this */
return (0);
}
/* Handle a sparable partition map */
int
udf_get_spartmap(struct umount *ump, struct part_map_spare *pms)
{
struct buf *bp;
int i, error;
ump->um_stbl = malloc(letoh32(pms->st_size), M_UDFMOUNT, M_NOWAIT);
if (ump->um_stbl == NULL)
return (ENOMEM);
bzero(ump->um_stbl, letoh32(pms->st_size));
/* Calculate the number of sectors per packet */
ump->um_psecs = letoh16(pms->packet_len) / ump->um_bsize;
error = udf_readlblks(ump, letoh32(pms->st_loc[0]),
letoh32(pms->st_size), &bp);
if (error) {
if (bp != NULL)
brelse(bp);
free(ump->um_stbl, M_UDFMOUNT, 0);
return (error); /* Failed to read sparing table */
}
bcopy(bp->b_data, ump->um_stbl, letoh32(pms->st_size));
brelse(bp);
bp = NULL;
if (udf_checktag(&ump->um_stbl->tag, 0)) {
free(ump->um_stbl, M_UDFMOUNT, 0);
return (EINVAL); /* Invalid sparing table found */
}
/*
* See how many valid entries there are here. The list is
* supposed to be sorted, 0xfffffff0 and higher are not valid.
*/
for (i = 0; i < letoh16(ump->um_stbl->rt_l); i++) {
ump->um_stbl_len = i;
if (letoh32(ump->um_stbl->entries[i].org) >= 0xfffffff0)
break;
}
return (0);
}
/* Handle a metadata partition map */
int
udf_get_mpartmap(struct umount *ump, struct part_map_meta *pmm)
{
ump->um_flags |= UDF_MNT_USES_META;
ump->um_meta_start = pmm->meta_file_lbn;
return (0);
}
/* Scan the partition maps */
int
udf_find_partmaps(struct umount *ump, struct logvol_desc *lvd)
{
struct regid *pmap_id;
unsigned char regid_id[UDF_REGID_ID_SIZE + 1];
int i, ptype, psize, error;
uint8_t *pmap = (uint8_t *) &lvd->maps[0];
for (i = 0; i < letoh32(lvd->n_pm); i++) {
ptype = pmap[0];
psize = pmap[1];
if (ptype != 1 && ptype != 2)
return (EINVAL); /* Invalid partition map type */
if (psize != sizeof(struct part_map_1) &&
psize != sizeof(struct part_map_2))
return (EINVAL); /* Invalid partition map size */
if (ptype == 1) {
pmap += sizeof(struct part_map_1);
continue;
}
/* Type 2 map. Find out the details */
pmap_id = (struct regid *) &pmap[4];
regid_id[UDF_REGID_ID_SIZE] = '\0';
bcopy(&pmap_id->id[0], ®id_id[0], UDF_REGID_ID_SIZE);
if (!bcmp(®id_id[0], "*UDF Virtual Partition",
UDF_REGID_ID_SIZE))
error = udf_get_vpartmap(ump,
(struct part_map_virt *) pmap);
else if (!bcmp(®id_id[0], "*UDF Sparable Partition",
UDF_REGID_ID_SIZE))
error = udf_get_spartmap(ump,
(struct part_map_spare *) pmap);
else if (!bcmp(®id_id[0], "*UDF Metadata Partition",
UDF_REGID_ID_SIZE))
error = udf_get_mpartmap(ump,
(struct part_map_meta *) pmap);
else
return (EINVAL); /* Unsupported partition map */
if (error)
return (error); /* Error getting partition */
pmap += sizeof(struct part_map_2);
}
return (0);
}
4
4
4
4
2
4
4
4
4
4
1
4
4
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/* $OpenBSD: inet_ntop.c,v 1.4 2016/09/04 17:05:24 claudio Exp $ */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define IN6ADDRSZ 16
#define INT16SZ 2
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static const char *inet_ntop4(const u_char *src, char *dst, size_t size);
#ifdef INET6
static const char *inet_ntop6(const u_char *src, char *dst, size_t size);
#endif /* INET6 */
/* char *
* inet_ntop(af, src, dst, size)
* convert a network format address to presentation format.
* return:
* pointer to presentation format address (`dst'), or NULL (see errno).
* author:
* Paul Vixie, 1996.
*/
const char *
inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
switch (af) {
case AF_INET:
return (inet_ntop4(src, dst, (size_t)size));
#ifdef INET6
case AF_INET6:
return (inet_ntop6(src, dst, (size_t)size));
#endif /* INET6 */
default:
return (NULL);
}
/* NOTREACHED */
}
const char *
sockaddr_ntop(struct sockaddr *sa, char *dst, size_t size)
{
u_int8_t l;
size_t n;
if (sa->sa_len < 2)
return "bad sa";
switch (sa->sa_family) {
case AF_INET:
return inet_ntop4((u_char *)&satosin(sa)->sin_addr, dst, size);
#ifdef INET6
case AF_INET6:
return inet_ntop6((u_char *)&satosin6(sa)->sin6_addr, dst, size);
#endif
default:
n = snprintf(dst, size, "%d ", sa->sa_family);
for (l = 0; l < sa->sa_len - offsetof(struct sockaddr, sa_data); l++) {
int r = snprintf(dst + n, size - n, "%02x", sa->sa_data[l]);
if (r == -1)
return "bad sa";
n += r;
if (n > size)
n = size;
}
return (dst);
}
}
/* const char *
* inet_ntop4(src, dst, size)
* format an IPv4 address, more or less like inet_ntoa()
* return:
* `dst' (as a const)
* notes:
* (1) uses no statics
* (2) takes a u_char* not an in_addr as input
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop4(const u_char *src, char *dst, size_t size)
{
char tmp[sizeof "255.255.255.255"];
int l;
l = snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
src[0], src[1], src[2], src[3]);
if (l <= 0 || l >= size) {
return (NULL);
}
strlcpy(dst, tmp, size);
return (dst);
}
#ifdef INET6
/* const char *
* inet_ntop6(src, dst, size)
* convert IPv6 binary address into presentation (printable) format
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop6(const u_char *src, char *dst, size_t size)
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
char *tp, *ep;
struct { int base, len; } best, cur;
u_int words[IN6ADDRSZ / INT16SZ];
int i;
int advance;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
cur.base = -1;
for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
ep = tmp + sizeof(tmp);
for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
return (NULL);
tp += strlen(tp);
break;
}
advance = snprintf(tp, ep - tp, "%x", words[i]);
if (advance <= 0 || advance >= ep - tp)
return (NULL);
tp += advance;
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) {
if (tp + 1 >= ep)
return (NULL);
*tp++ = ':';
}
if (tp + 1 >= ep)
return (NULL);
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size) {
return (NULL);
}
strlcpy(dst, tmp, size);
return (dst);
}
#endif /* INET6 */
119
19
8
7
79
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* $OpenBSD: sysv_ipc.c,v 1.8 2015/03/14 03:38:50 jsg Exp $ */
/* $NetBSD: sysv_ipc.c,v 1.10 1995/06/03 05:53:28 mycroft Exp $ */
/*
* Copyright (c) 1995 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/ipc.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/vnode.h>
/*
* Check for ipc permission
*/
int
ipcperm(struct ucred *cred, struct ipc_perm *perm, int mode)
{
if (mode == IPC_M) {
if (cred->cr_uid == 0 ||
cred->cr_uid == perm->uid ||
cred->cr_uid == perm->cuid)
return (0);
return (EPERM);
}
if (vaccess(VNON, perm->mode, perm->uid, perm->gid, mode, cred) == 0 ||
vaccess(VNON, perm->mode, perm->cuid, perm->cgid, mode, cred) == 0)
return (0);
return (EACCES);
}
1
8
3
1
6
1
2
1
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
/* $OpenBSD: pvbus.c,v 1.25 2022/08/25 17:38:16 cheloha Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if !defined(__i386__) && !defined(__amd64__)
#error pvbus(4) is currently only supported on i386 and amd64
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/timeout.h>
#include <sys/signalvar.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <machine/specialreg.h>
#include <machine/cpu.h>
#include <machine/conf.h>
#include <machine/bus.h>
#include <machine/vmmvar.h>
#include <dev/pv/pvvar.h>
#include <dev/pv/pvreg.h>
#include <dev/pv/hypervreg.h>
#include "hyperv.h"
int has_hv_cpuid = 0;
extern void rdrand(void *);
int pvbus_activate(struct device *, int);
int pvbus_match(struct device *, void *, void *);
void pvbus_attach(struct device *, struct device *, void *);
int pvbus_print(void *, const char *);
int pvbus_search(struct device *, void *, void *);
void pvbus_kvm(struct pvbus_hv *);
void pvbus_hyperv(struct pvbus_hv *);
void pvbus_hyperv_print(struct pvbus_hv *);
void pvbus_xen(struct pvbus_hv *);
void pvbus_xen_print(struct pvbus_hv *);
int pvbus_minor(struct pvbus_softc *, dev_t);
int pvbusgetstr(size_t, const char *, char **);
const struct cfattach pvbus_ca = {
sizeof(struct pvbus_softc),
pvbus_match,
pvbus_attach,
NULL,
pvbus_activate
};
struct cfdriver pvbus_cd = {
NULL,
"pvbus",
DV_DULL
};
struct pvbus_type {
const char *signature;
const char *name;
void (*init)(struct pvbus_hv *);
void (*print)(struct pvbus_hv *);
} pvbus_types[PVBUS_MAX] = {
{ "KVMKVMKVM\0\0\0", "KVM", pvbus_kvm },
{ "Microsoft Hv", "Hyper-V", pvbus_hyperv, pvbus_hyperv_print },
{ "VMwareVMware", "VMware" },
{ "XenVMMXenVMM", "Xen", pvbus_xen, pvbus_xen_print },
{ "bhyve bhyve ", "bhyve" },
{ VMM_HV_SIGNATURE, "OpenBSD", pvbus_kvm },
};
struct bus_dma_tag pvbus_dma_tag = {
NULL,
_bus_dmamap_create,
_bus_dmamap_destroy,
_bus_dmamap_load,
_bus_dmamap_load_mbuf,
_bus_dmamap_load_uio,
_bus_dmamap_load_raw,
_bus_dmamap_unload,
_bus_dmamap_sync,
_bus_dmamem_alloc,
_bus_dmamem_alloc_range,
_bus_dmamem_free,
_bus_dmamem_map,
_bus_dmamem_unmap,
_bus_dmamem_mmap,
};
struct pvbus_hv pvbus_hv[PVBUS_MAX];
struct pvbus_softc *pvbus_softc;
int
pvbus_probe(void)
{
/* Must be set in identcpu */
if (!has_hv_cpuid)
return (0);
return (1);
}
int
pvbus_match(struct device *parent, void *match, void *aux)
{
const char **busname = (const char **)aux;
return (strcmp(*busname, pvbus_cd.cd_name) == 0);
}
void
pvbus_attach(struct device *parent, struct device *self, void *aux)
{
struct pvbus_softc *sc = (struct pvbus_softc *)self;
int i, cnt;
sc->pvbus_hv = pvbus_hv;
pvbus_softc = sc;
printf(":");
for (i = 0, cnt = 0; i < PVBUS_MAX; i++) {
if (pvbus_hv[i].hv_base == 0)
continue;
if (cnt++)
printf(",");
printf(" %s", pvbus_types[i].name);
if (pvbus_types[i].print != NULL)
(pvbus_types[i].print)(&pvbus_hv[i]);
}
printf("\n");
config_search(pvbus_search, self, sc);
}
void
pvbus_identify(void)
{
struct pvbus_hv *hv;
uint32_t reg0, base;
union {
uint32_t regs[3];
char str[CPUID_HV_SIGNATURE_STRLEN];
} r;
int i, cnt;
const char *pv_name;
for (base = CPUID_HV_SIGNATURE_START, cnt = 0;
base < CPUID_HV_SIGNATURE_END;
base += CPUID_HV_SIGNATURE_STEP) {
CPUID(base, reg0, r.regs[0], r.regs[1], r.regs[2]);
for (i = 0; i < 4; i++) {
/*
* Check if first 4 chars are printable ASCII as
* minimal validity check
*/
if (r.str[i] < 32 || r.str[i] > 126)
goto out;
}
for (i = 0; i < PVBUS_MAX; i++) {
if (pvbus_types[i].signature == NULL ||
memcmp(pvbus_types[i].signature, r.str,
CPUID_HV_SIGNATURE_STRLEN) != 0)
continue;
hv = &pvbus_hv[i];
hv->hv_base = base;
if (pvbus_types[i].init != NULL)
(pvbus_types[i].init)(hv);
if (hw_vendor == NULL) {
pv_name = pvbus_types[i].name;
/*
* Use the HV name as a fallback if we didn't
* get the vendor name from the firmware/BIOS.
*/
if ((hw_vendor = malloc(strlen(pv_name) + 1,
M_DEVBUF, M_NOWAIT)) != NULL) {
strlcpy(hw_vendor, pv_name,
strlen(pv_name) + 1);
}
}
cnt++;
}
}
out:
if (cnt)
has_hv_cpuid = 1;
}
void
pvbus_init_cpu(void)
{
int i;
for (i = 0; i < PVBUS_MAX; i++) {
if (pvbus_hv[i].hv_base == 0)
continue;
if (pvbus_hv[i].hv_init_cpu != NULL)
(pvbus_hv[i].hv_init_cpu)(&pvbus_hv[i]);
}
}
int
pvbus_activate(struct device *self, int act)
{
int rv = 0;
switch (act) {
case DVACT_SUSPEND:
rv = config_activate_children(self, act);
break;
case DVACT_RESUME:
rv = config_activate_children(self, act);
break;
case DVACT_POWERDOWN:
rv = config_activate_children(self, act);
break;
default:
rv = config_activate_children(self, act);
break;
}
return (rv);
}
int
pvbus_search(struct device *parent, void *arg, void *aux)
{
struct pvbus_softc *sc = (struct pvbus_softc *)aux;
struct cfdata *cf = arg;
struct pv_attach_args pva;
pva.pva_busname = cf->cf_driver->cd_name;
pva.pva_hv = sc->pvbus_hv;
pva.pva_dmat = &pvbus_dma_tag;
if (cf->cf_attach->ca_match(parent, cf, &pva) > 0)
config_attach(parent, cf, &pva, pvbus_print);
return (0);
}
int
pvbus_print(void *aux, const char *pnp)
{
struct pv_attach_args *pva = aux;
if (pnp)
printf("%s at %s", pva->pva_busname, pnp);
return (UNCONF);
}
void
pvbus_shutdown(struct device *dev)
{
suspend_randomness();
log(LOG_KERN | LOG_NOTICE, "Shutting down in response to request"
" from %s host\n", dev->dv_xname);
prsignal(initprocess, SIGUSR2);
}
void
pvbus_reboot(struct device *dev)
{
suspend_randomness();
log(LOG_KERN | LOG_NOTICE, "Rebooting in response to request"
" from %s host\n", dev->dv_xname);
prsignal(initprocess, SIGINT);
}
void
pvbus_kvm(struct pvbus_hv *hv)
{
uint32_t regs[4];
CPUID(hv->hv_base + CPUID_OFFSET_KVM_FEATURES,
regs[0], regs[1], regs[2], regs[3]);
hv->hv_features = regs[0];
}
extern void hv_delay(int usecs);
void
pvbus_hyperv(struct pvbus_hv *hv)
{
uint32_t regs[4];
CPUID(hv->hv_base + CPUID_OFFSET_HYPERV_FEATURES,
regs[0], regs[1], regs[2], regs[3]);
hv->hv_features = regs[0];
CPUID(hv->hv_base + CPUID_OFFSET_HYPERV_VERSION,
regs[0], regs[1], regs[2], regs[3]);
hv->hv_major = (regs[1] & HYPERV_VERSION_EBX_MAJOR_M) >>
HYPERV_VERSION_EBX_MAJOR_S;
hv->hv_minor = (regs[1] & HYPERV_VERSION_EBX_MINOR_M) >>
HYPERV_VERSION_EBX_MINOR_S;
#if NHYPERV > 0
if (hv->hv_features & CPUID_HV_MSR_TIME_REFCNT)
delay_init(hv_delay, 4000);
#endif
}
void
pvbus_hyperv_print(struct pvbus_hv *hv)
{
printf(" %u.%u", hv->hv_major, hv->hv_minor);
}
void
pvbus_xen(struct pvbus_hv *hv)
{
uint32_t regs[4];
CPUID(hv->hv_base + CPUID_OFFSET_XEN_VERSION,
regs[0], regs[1], regs[2], regs[3]);
hv->hv_major = regs[0] >> XEN_VERSION_MAJOR_S;
hv->hv_minor = regs[0] & XEN_VERSION_MINOR_M;
/* x2apic is broken in Xen 4.2 or older */
if ((hv->hv_major < 4) ||
(hv->hv_major == 4 && hv->hv_minor < 3)) {
/* Remove CPU flag for x2apic */
cpu_ecxfeature &= ~CPUIDECX_X2APIC;
}
}
void
pvbus_xen_print(struct pvbus_hv *hv)
{
printf(" %u.%u", hv->hv_major, hv->hv_minor);
}
int
pvbus_minor(struct pvbus_softc *sc, dev_t dev)
{
int hvid, cnt;
struct pvbus_hv *hv;
for (hvid = 0, cnt = 0; hvid < PVBUS_MAX; hvid++) {
hv = &sc->pvbus_hv[hvid];
if (hv->hv_base == 0)
continue;
if (minor(dev) == cnt++)
return (hvid);
}
return (-1);
}
int
pvbusopen(dev_t dev, int flags, int mode, struct proc *p)
{
if (pvbus_softc == NULL)
return (ENODEV);
if (pvbus_minor(pvbus_softc, dev) == -1)
return (ENXIO);
return (0);
}
int
pvbusclose(dev_t dev, int flags, int mode, struct proc *p)
{
if (pvbus_softc == NULL)
return (ENODEV);
if (pvbus_minor(pvbus_softc, dev) == -1)
return (ENXIO);
return (0);
}
int
pvbusgetstr(size_t srclen, const char *src, char **dstp)
{
int error = 0;
char *dst;
/*
* Reject size that is too short or obviously too long:
* - at least one byte for the nul terminator.
* - PAGE_SIZE is an arbitrary value, but known pv backends seem
* to have a hard (PAGE_SIZE - x) limit in their messaging.
*/
if (srclen < 1)
return (EINVAL);
else if (srclen > PAGE_SIZE)
return (ENAMETOOLONG);
*dstp = dst = malloc(srclen + 1, M_TEMP|M_ZERO, M_WAITOK);
if (src != NULL) {
error = copyin(src, dst, srclen);
dst[srclen] = '\0';
}
return (error);
}
int
pvbusioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
struct pvbus_req *pvr = (struct pvbus_req *)data;
struct pvbus_softc *sc = pvbus_softc;
char *value = NULL, *key = NULL;
const char *str = NULL;
size_t valuelen = 0, keylen = 0, sz;
int hvid, error = 0, op;
struct pvbus_hv *hv;
if (sc == NULL)
return (ENODEV);
if ((hvid = pvbus_minor(sc, dev)) == -1)
return (ENXIO);
switch (cmd) {
case PVBUSIOC_KVWRITE:
if ((flags & FWRITE) == 0)
return (EPERM);
case PVBUSIOC_KVREAD:
hv = &sc->pvbus_hv[hvid];
if (hv->hv_base == 0 || hv->hv_kvop == NULL)
return (ENXIO);
break;
case PVBUSIOC_TYPE:
str = pvbus_types[hvid].name;
sz = strlen(str) + 1;
if (sz > pvr->pvr_keylen)
return (ENOMEM);
error = copyout(str, pvr->pvr_key, sz);
return (error);
default:
return (ENOTTY);
}
str = NULL;
op = PVBUS_KVREAD;
switch (cmd) {
case PVBUSIOC_KVWRITE:
str = pvr->pvr_value;
op = PVBUS_KVWRITE;
/* FALLTHROUGH */
case PVBUSIOC_KVREAD:
keylen = pvr->pvr_keylen;
if ((error = pvbusgetstr(keylen, pvr->pvr_key, &key)) != 0)
break;
valuelen = pvr->pvr_valuelen;
if ((error = pvbusgetstr(valuelen, str, &value)) != 0)
break;
/* Call driver-specific callback */
if ((error = (hv->hv_kvop)(hv->hv_arg, op,
key, value, valuelen)) != 0)
break;
sz = strlen(value) + 1;
if ((error = copyout(value, pvr->pvr_value, sz)) != 0)
break;
break;
default:
error = ENOTTY;
break;
}
free(key, M_TEMP, keylen + 1);
free(value, M_TEMP, valuelen + 1);
return (error);
}
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/* $OpenBSD: ip_var.h,v 1.104 2022/09/03 22:43:38 mvs Exp $ */
/* $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_var.h 8.1 (Berkeley) 6/10/93
*/
#ifndef _NETINET_IP_VAR_H_
#define _NETINET_IP_VAR_H_
/*
* Structure stored in mbuf in inpcb.ip_options
* and passed to ip_output when ip options are in use.
* The actual length of the options (including ipopt_dst)
* is in m_len.
*/
#define MAX_IPOPTLEN 40
/*
* Overlay for ip header used by other protocols (tcp, udp).
*/
struct ipovly {
u_int8_t ih_x1[9]; /* (unused) */
u_int8_t ih_pr; /* protocol */
u_int16_t ih_len; /* protocol length */
struct in_addr ih_src; /* source internet address */
struct in_addr ih_dst; /* destination internet address */
};
struct ipstat {
u_long ips_total; /* total packets received */
u_long ips_badsum; /* checksum bad */
u_long ips_tooshort; /* packet too short */
u_long ips_toosmall; /* not enough data */
u_long ips_badhlen; /* ip header length < data size */
u_long ips_badlen; /* ip length < ip header length */
u_long ips_fragments; /* fragments received */
u_long ips_fragdropped; /* frags dropped (dups, out of space) */
u_long ips_fragtimeout; /* fragments timed out */
u_long ips_forward; /* packets forwarded */
u_long ips_cantforward; /* packets rcvd for unreachable dest */
u_long ips_redirectsent; /* packets forwarded on same net */
u_long ips_noproto; /* unknown or unsupported protocol */
u_long ips_delivered; /* datagrams delivered to upper level*/
u_long ips_localout; /* total ip packets generated here */
u_long ips_odropped; /* lost output due to nobufs, etc. */
u_long ips_reassembled; /* total packets reassembled ok */
u_long ips_fragmented; /* datagrams successfully fragmented */
u_long ips_ofragments; /* output fragments created */
u_long ips_cantfrag; /* don't fragment flag was set, etc. */
u_long ips_badoptions; /* error in option processing */
u_long ips_noroute; /* packets discarded due to no route */
u_long ips_badvers; /* ip version != 4 */
u_long ips_rawout; /* total raw ip packets generated */
u_long ips_badfrags; /* malformed fragments (bad length) */
u_long ips_rcvmemdrop; /* frags dropped for lack of memory */
u_long ips_toolong; /* ip length > max ip packet size */
u_long ips_nogif; /* no match gif found */
u_long ips_badaddr; /* invalid address on header */
u_long ips_inswcsum; /* software checksummed on input */
u_long ips_outswcsum; /* software checksummed on output */
u_long ips_notmember; /* multicasts for unregistered groups */
u_long ips_wrongif; /* packet received on wrong interface */
u_long ips_idropped; /* lost input due to nobufs, etc. */
};
struct ipoption {
struct in_addr ipopt_dst; /* first-hop dst if source routed */
int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */
};
#ifdef _KERNEL
#include <sys/percpu.h>
enum ipstat_counters {
ips_total, /* total packets received */
ips_badsum, /* checksum bad */
ips_tooshort, /* packet too short */
ips_toosmall, /* not enough data */
ips_badhlen, /* ip header length < data size */
ips_badlen, /* ip length < ip header length */
ips_fragments, /* fragments received */
ips_fragdropped, /* frags dropped (dups, out of space) */
ips_fragtimeout, /* fragments timed out */
ips_forward, /* packets forwarded */
ips_cantforward, /* packets rcvd for unreachable dest */
ips_redirectsent, /* packets forwarded on same net */
ips_noproto, /* unknown or unsupported protocol */
ips_delivered, /* datagrams delivered to upper level*/
ips_localout, /* total ip packets generated here */
ips_odropped, /* lost output packets due to nobufs, etc. */
ips_reassembled, /* total packets reassembled ok */
ips_fragmented, /* datagrams successfully fragmented */
ips_ofragments, /* output fragments created */
ips_cantfrag, /* don't fragment flag was set, etc. */
ips_badoptions, /* error in option processing */
ips_noroute, /* packets discarded due to no route */
ips_badvers, /* ip version != 4 */
ips_rawout, /* total raw ip packets generated */
ips_badfrags, /* malformed fragments (bad length) */
ips_rcvmemdrop, /* frags dropped for lack of memory */
ips_toolong, /* ip length > max ip packet size */
ips_nogif, /* no match gif found */
ips_badaddr, /* invalid address on header */
ips_inswcsum, /* software checksummed on input */
ips_outswcsum, /* software checksummed on output */
ips_notmember, /* multicasts for unregistered groups */
ips_wrongif, /* packet received on wrong interface */
ips_idropped, /* lost input packets due to nobufs, etc. */
ips_ncounters
};
extern struct cpumem *ipcounters;
static inline void
ipstat_inc(enum ipstat_counters c)
{
counters_inc(ipcounters, c);
}
static inline void
ipstat_add(enum ipstat_counters c, uint64_t v)
{
counters_add(ipcounters, c, v);
}
/*
* Structure attached to inpcb.ip_moptions and
* passed to ip_output when IP multicast options are in use.
*/
struct ip_moptions {
struct in_multi **imo_membership; /* group memberships */
unsigned short imo_ifidx; /* ifp index for outgoing multicasts */
u_int8_t imo_ttl; /* TTL for outgoing multicasts */
u_int8_t imo_loop; /* 1 => hear sends if a member */
u_int16_t imo_num_memberships; /* no. memberships this socket */
u_int16_t imo_max_memberships; /* max memberships this socket */
};
#include <sys/queue.h>
/*
* Ip reassembly queue structures.
*/
LIST_HEAD(ipqehead, ipqent);
struct ipqent {
LIST_ENTRY(ipqent) ipqe_q;
struct ip *ipqe_ip;
struct mbuf *ipqe_m; /* mbuf contains packet */
uint16_t ipqe_mff; /* for IP fragmentation */
};
/*
* Ip reassembly queue structure. Each fragment
* being reassembled is attached to one of these structures.
* They are timed out after ipq_ttl drops to 0, and may also
* be reclaimed if memory becomes tight.
*/
struct ipq {
LIST_ENTRY(ipq) ipq_q; /* to other reass headers */
u_int8_t ipq_ttl; /* time for reass q to live */
u_int8_t ipq_p; /* protocol of this fragment */
u_int16_t ipq_id; /* sequence id for reassembly */
struct ipqehead ipq_fragq; /* to ip fragment queue */
struct in_addr ipq_src, ipq_dst;
};
/* flags passed to ip_output */
#define IP_FORWARDING 0x1 /* most of ip header exists */
#define IP_RAWOUTPUT 0x2 /* raw ip header exists */
#define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */
#define IP_MTUDISC 0x0800 /* pmtu discovery, set DF */
extern struct ipstat ipstat;
extern int ip_defttl; /* default IP ttl */
#define IPMTUDISCTIMEOUT (10 * 60) /* as per RFC 1191 */
extern int ip_mtudisc; /* mtu discovery */
extern int ip_mtudisc_timeout; /* seconds to timeout mtu discovery */
extern int ipport_firstauto; /* min port for port allocation */
extern int ipport_lastauto; /* max port for port allocation */
extern int ipport_hifirstauto; /* min dynamic/private port number */
extern int ipport_hilastauto; /* max dynamic/private port number */
extern int ipforwarding; /* enable IP forwarding */
#ifdef MROUTING
extern int ipmforwarding; /* enable multicast forwarding */
#endif
extern int ipmultipath; /* enable multipath routing */
extern int la_hold_total;
extern const struct pr_usrreqs rip_usrreqs;
extern struct rttimer_queue ip_mtudisc_timeout_q;
extern struct pool ipqent_pool;
struct route;
struct inpcb;
int ip_ctloutput(int, struct socket *, int, int, struct mbuf *);
int ip_fragment(struct mbuf *, struct mbuf_list *, struct ifnet *, u_long);
void ip_freemoptions(struct ip_moptions *);
int ip_getmoptions(int, struct ip_moptions *, struct mbuf *);
void ip_init(void);
struct mbuf*
ip_insertoptions(struct mbuf *, struct mbuf *, int *);
int ip_mforward(struct mbuf *, struct ifnet *);
int ip_optcopy(struct ip *, struct ip *);
int ip_output(struct mbuf *, struct mbuf *, struct route *, int,
struct ip_moptions *, struct inpcb *, u_int32_t);
u_int16_t
ip_randomid(void);
void ip_send(struct mbuf *);
void ip_send_raw(struct mbuf *);
void ip_slowtimo(void);
struct mbuf *
ip_srcroute(struct mbuf *);
void ip_stripoptions(struct mbuf *);
int ip_sysctl(int *, u_int, void *, size_t *, void *, size_t);
void ip_savecontrol(struct inpcb *, struct mbuf **, struct ip *,
struct mbuf *);
int ip_input_if(struct mbuf **, int *, int, int, struct ifnet *);
int ip_deliver(struct mbuf **, int *, int, int);
void ip_forward(struct mbuf *, struct ifnet *, struct rtentry *, int);
int rip_ctloutput(int, struct socket *, int, int, struct mbuf *);
void rip_init(void);
int rip_input(struct mbuf **, int *, int, int);
int rip_output(struct mbuf *, struct socket *, struct sockaddr *,
struct mbuf *);
int rip_attach(struct socket *, int);
int rip_detach(struct socket *);
int rip_bind(struct socket *so, struct mbuf *, struct proc *);
int rip_connect(struct socket *, struct mbuf *);
int rip_disconnect(struct socket *);
int rip_shutdown(struct socket *);
int rip_send(struct socket *, struct mbuf *, struct mbuf *,
struct mbuf *);
int rip_abort(struct socket *);
#ifdef MROUTING
extern struct socket *ip_mrouter[]; /* multicast routing daemon */
#endif
#endif /* _KERNEL */
#endif /* _NETINET_IP_VAR_H_ */
415
415
48
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/* $OpenBSD: strlcpy.c,v 1.9 2019/01/25 00:19:26 millert Exp $ */
/*
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <lib/libkern/libkern.h>
/*
* Copy string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}
7
1
1
1
1
1
1
1
2
1
1
1
1
3
2
2
3
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
/* $OpenBSD: acpi.c,v 1.415 2022/09/03 18:05:10 kettenis Exp $ */
/*
* Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
* Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/fcntl.h>
#include <sys/event.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/sched.h>
#ifdef HIBERNATE
#include <sys/hibernate.h>
#endif
#include <machine/conf.h>
#include <machine/cpufunc.h>
#include <dev/pci/pcivar.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/amltypes.h>
#include <dev/acpi/acpidev.h>
#include <dev/acpi/dsdt.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/ppbreg.h>
#include <dev/pci/pciidevar.h>
#include <machine/apmvar.h>
#define APMUNIT(dev) (minor(dev)&0xf0)
#define APMDEV(dev) (minor(dev)&0x0f)
#define APMDEV_NORMAL 0
#define APMDEV_CTL 8
#include "wd.h"
#ifdef ACPI_DEBUG
int acpi_debug = 16;
#endif
int acpi_poll_enabled;
int acpi_hasprocfvs;
int acpi_haspci;
struct pool acpiwqpool;
#define ACPIEN_RETRIES 15
struct aml_node *acpi_pci_match(struct device *, struct pci_attach_args *);
pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
void acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int);
int acpi_pci_notify(struct aml_node *, int, void *);
int acpi_submatch(struct device *, void *, void *);
int acpi_print(void *, const char *);
void acpi_map_pmregs(struct acpi_softc *);
void acpi_unmap_pmregs(struct acpi_softc *);
int acpi_loadtables(struct acpi_softc *, struct acpi_rsdp *);
int _acpi_matchhids(const char *, const char *[]);
int acpi_inidev(struct aml_node *, void *);
int acpi_foundprt(struct aml_node *, void *);
int acpi_enable(struct acpi_softc *);
void acpi_init_states(struct acpi_softc *);
void acpi_gpe_task(void *, int);
void acpi_sbtn_task(void *, int);
void acpi_pbtn_task(void *, int);
int acpi_enabled;
void acpi_init_gpes(struct acpi_softc *);
void acpi_disable_allgpes(struct acpi_softc *);
struct gpe_block *acpi_find_gpe(struct acpi_softc *, int);
void acpi_enable_onegpe(struct acpi_softc *, int);
int acpi_gpe(struct acpi_softc *, int, void *);
void acpi_enable_rungpes(struct acpi_softc *);
int acpi_foundec(struct aml_node *, void *);
int acpi_foundsony(struct aml_node *node, void *arg);
int acpi_foundhid(struct aml_node *, void *);
int acpi_add_device(struct aml_node *node, void *arg);
void acpi_thread(void *);
void acpi_create_thread(void *);
#ifndef SMALL_KERNEL
void acpi_init_pm(struct acpi_softc *);
int acpi_founddock(struct aml_node *, void *);
int acpi_foundpss(struct aml_node *, void *);
int acpi_foundtmp(struct aml_node *, void *);
int acpi_foundprw(struct aml_node *, void *);
int acpi_foundvideo(struct aml_node *, void *);
int acpi_foundsbs(struct aml_node *node, void *);
int acpi_foundide(struct aml_node *node, void *arg);
int acpiide_notify(struct aml_node *, int, void *);
void wdcattach(struct channel_softc *);
int wdcdetach(struct channel_softc *, int);
int is_ejectable_bay(struct aml_node *node);
int is_ata(struct aml_node *node);
int is_ejectable(struct aml_node *node);
struct idechnl {
struct acpi_softc *sc;
int64_t addr;
int64_t chnl;
int64_t sta;
};
/*
* This is a list of Synaptics devices with a 'top button area'
* based on the list in Linux supplied by Synaptics
* Synaptics clickpads with the following pnp ids will get a unique
* wscons mouse type that is used to define trackpad regions that will
* emulate mouse buttons
*/
static const char *sbtn_pnp[] = {
"LEN0017",
"LEN0018",
"LEN0019",
"LEN0023",
"LEN002A",
"LEN002B",
"LEN002C",
"LEN002D",
"LEN002E",
"LEN0033",
"LEN0034",
"LEN0035",
"LEN0036",
"LEN0037",
"LEN0038",
"LEN0039",
"LEN0041",
"LEN0042",
"LEN0045",
"LEN0047",
"LEN0049",
"LEN2000",
"LEN2001",
"LEN2002",
"LEN2003",
"LEN2004",
"LEN2005",
"LEN2006",
"LEN2007",
"LEN2008",
"LEN2009",
"LEN200A",
"LEN200B",
};
int mouse_has_softbtn;
#endif /* SMALL_KERNEL */
struct acpi_softc *acpi_softc;
extern struct aml_node aml_root;
struct cfdriver acpi_cd = {
NULL, "acpi", DV_DULL
};
uint8_t
acpi_pci_conf_read_1(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
uint32_t val = pci_conf_read(pc, tag, reg & ~0x3);
return (val >> ((reg & 0x3) << 3));
}
uint16_t
acpi_pci_conf_read_2(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
uint32_t val = pci_conf_read(pc, tag, reg & ~0x2);
return (val >> ((reg & 0x2) << 3));
}
uint32_t
acpi_pci_conf_read_4(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
return pci_conf_read(pc, tag, reg);
}
void
acpi_pci_conf_write_1(pci_chipset_tag_t pc, pcitag_t tag, int reg, uint8_t val)
{
uint32_t tmp = pci_conf_read(pc, tag, reg & ~0x3);
tmp &= ~(0xff << ((reg & 0x3) << 3));
tmp |= (val << ((reg & 0x3) << 3));
pci_conf_write(pc, tag, reg & ~0x3, tmp);
}
void
acpi_pci_conf_write_2(pci_chipset_tag_t pc, pcitag_t tag, int reg, uint16_t val)
{
uint32_t tmp = pci_conf_read(pc, tag, reg & ~0x2);
tmp &= ~(0xffff << ((reg & 0x2) << 3));
tmp |= (val << ((reg & 0x2) << 3));
pci_conf_write(pc, tag, reg & ~0x2, tmp);
}
void
acpi_pci_conf_write_4(pci_chipset_tag_t pc, pcitag_t tag, int reg, uint32_t val)
{
pci_conf_write(pc, tag, reg, val);
}
int
acpi_gasio(struct acpi_softc *sc, int iodir, int iospace, uint64_t address,
int access_size, int len, void *buffer)
{
uint8_t *pb;
bus_space_tag_t iot;
bus_space_handle_t ioh;
pci_chipset_tag_t pc;
pcitag_t tag;
int reg, idx;
dnprintf(50, "gasio: %.2x 0x%.8llx %s\n",
iospace, address, (iodir == ACPI_IOWRITE) ? "write" : "read");
KASSERT((len % access_size) == 0);
pb = (uint8_t *)buffer;
switch (iospace) {
case GAS_SYSTEM_MEMORY:
case GAS_SYSTEM_IOSPACE:
if (iospace == GAS_SYSTEM_MEMORY)
iot = sc->sc_memt;
else
iot = sc->sc_iot;
if (acpi_bus_space_map(iot, address, len, 0, &ioh) != 0) {
printf("%s: unable to map iospace\n", DEVNAME(sc));
return (-1);
}
for (reg = 0; reg < len; reg += access_size) {
if (iodir == ACPI_IOREAD) {
switch (access_size) {
case 1:
*(uint8_t *)(pb + reg) =
bus_space_read_1(iot, ioh, reg);
dnprintf(80, "os_in8(%llx) = %x\n",
reg+address, *(uint8_t *)(pb+reg));
break;
case 2:
*(uint16_t *)(pb + reg) =
bus_space_read_2(iot, ioh, reg);
dnprintf(80, "os_in16(%llx) = %x\n",
reg+address, *(uint16_t *)(pb+reg));
break;
case 4:
*(uint32_t *)(pb + reg) =
bus_space_read_4(iot, ioh, reg);
break;
default:
printf("%s: rdio: invalid size %d\n",
DEVNAME(sc), access_size);
return (-1);
}
} else {
switch (access_size) {
case 1:
bus_space_write_1(iot, ioh, reg,
*(uint8_t *)(pb + reg));
dnprintf(80, "os_out8(%llx,%x)\n",
reg+address, *(uint8_t *)(pb+reg));
break;
case 2:
bus_space_write_2(iot, ioh, reg,
*(uint16_t *)(pb + reg));
dnprintf(80, "os_out16(%llx,%x)\n",
reg+address, *(uint16_t *)(pb+reg));
break;
case 4:
bus_space_write_4(iot, ioh, reg,
*(uint32_t *)(pb + reg));
break;
default:
printf("%s: wrio: invalid size %d\n",
DEVNAME(sc), access_size);
return (-1);
}
}
}
acpi_bus_space_unmap(iot, ioh, len);
break;
case GAS_PCI_CFG_SPACE:
/*
* The ACPI standard says that a function number of
* FFFF can be used to refer to all functions on a
* device. This makes no sense though in the context
* of accessing PCI config space. Yet there is AML
* out there that does this. We simulate a read from
* a nonexistent device here. Writes will panic when
* we try to construct the tag below.
*/
if (ACPI_PCI_FN(address) == 0xffff && iodir == ACPI_IOREAD) {
memset(buffer, 0xff, len);
return (0);
}
pc = pci_lookup_segment(ACPI_PCI_SEG(address));
tag = pci_make_tag(pc,
ACPI_PCI_BUS(address), ACPI_PCI_DEV(address),
ACPI_PCI_FN(address));
reg = ACPI_PCI_REG(address);
for (idx = 0; idx < len; idx += access_size) {
if (iodir == ACPI_IOREAD) {
switch (access_size) {
case 1:
*(uint8_t *)(pb + idx) =
acpi_pci_conf_read_1(pc, tag, reg + idx);
break;
case 2:
*(uint16_t *)(pb + idx) =
acpi_pci_conf_read_2(pc, tag, reg + idx);
break;
case 4:
*(uint32_t *)(pb + idx) =
acpi_pci_conf_read_4(pc, tag, reg + idx);
break;
default:
printf("%s: rdcfg: invalid size %d\n",
DEVNAME(sc), access_size);
return (-1);
}
} else {
switch (access_size) {
case 1:
acpi_pci_conf_write_1(pc, tag, reg + idx,
*(uint8_t *)(pb + idx));
break;
case 2:
acpi_pci_conf_write_2(pc, tag, reg + idx,
*(uint16_t *)(pb + idx));
break;
case 4:
acpi_pci_conf_write_4(pc, tag, reg + idx,
*(uint32_t *)(pb + idx));
break;
default:
printf("%s: wrcfg: invalid size %d\n",
DEVNAME(sc), access_size);
return (-1);
}
}
}
break;
case GAS_EMBEDDED:
if (sc->sc_ec == NULL) {
printf("%s: WARNING EC not initialized\n", DEVNAME(sc));
return (-1);
}
if (iodir == ACPI_IOREAD)
acpiec_read(sc->sc_ec, (uint8_t)address, len, buffer);
else
acpiec_write(sc->sc_ec, (uint8_t)address, len, buffer);
break;
}
return (0);
}
int
acpi_inidev(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
int64_t sta;
/*
* Per the ACPI spec 6.5.1, only run _INI when device is there or
* when there is no _STA. We terminate the tree walk (with return 1)
* early if necessary.
*/
/* Evaluate _STA to decide _INI fate and walk fate */
sta = acpi_getsta(sc, node->parent);
/* Evaluate _INI if we are present */
if (sta & STA_PRESENT)
aml_evalnode(sc, node, 0, NULL, NULL);
/* If we are functioning, we walk/search our children */
if (sta & STA_DEV_OK)
return 0;
/* If we are not enabled, or not present, terminate search */
if (!(sta & (STA_PRESENT|STA_ENABLED)))
return 1;
/* Default just continue search */
return 0;
}
int
acpi_foundprt(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
struct acpi_attach_args aaa;
int64_t sta;
dnprintf(10, "found prt entry: %s\n", node->parent->name);
/* Evaluate _STA to decide _PRT fate and walk fate */
sta = acpi_getsta(sc, node->parent);
if (sta & STA_PRESENT) {
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_node = node;
aaa.aaa_name = "acpiprt";
config_found(self, &aaa, acpi_print);
}
/* If we are functioning, we walk/search our children */
if (sta & STA_DEV_OK)
return 0;
/* If we are not enabled, or not present, terminate search */
if (!(sta & (STA_PRESENT|STA_ENABLED)))
return 1;
/* Default just continue search */
return 0;
}
TAILQ_HEAD(, acpi_pci) acpi_pcidevs =
TAILQ_HEAD_INITIALIZER(acpi_pcidevs);
TAILQ_HEAD(, acpi_pci) acpi_pcirootdevs =
TAILQ_HEAD_INITIALIZER(acpi_pcirootdevs);
int acpi_getpci(struct aml_node *node, void *arg);
int acpi_getminbus(int crsidx, union acpi_resource *crs, void *arg);
int
acpi_getminbus(int crsidx, union acpi_resource *crs, void *arg)
{
int *bbn = arg;
int typ = AML_CRSTYPE(crs);
/* Check for embedded bus number */
if (typ == LR_WORD && crs->lr_word.type == 2) {
/* If _MIN > _MAX, the resource is considered to be invalid. */
if (crs->lr_word._min > crs->lr_word._max)
return -1;
*bbn = crs->lr_word._min;
}
return 0;
}
int
acpi_matchcls(struct acpi_attach_args *aaa, int class, int subclass,
int interface)
{
struct acpi_softc *sc = acpi_softc;
struct aml_value res;
if (aaa->aaa_dev == NULL || aaa->aaa_node == NULL)
return (0);
if (aml_evalname(sc, aaa->aaa_node, "_CLS", 0, NULL, &res))
return (0);
if (res.type != AML_OBJTYPE_PACKAGE || res.length != 3 ||
res.v_package[0]->type != AML_OBJTYPE_INTEGER ||
res.v_package[1]->type != AML_OBJTYPE_INTEGER ||
res.v_package[2]->type != AML_OBJTYPE_INTEGER)
return (0);
if (res.v_package[0]->v_integer == class &&
res.v_package[1]->v_integer == subclass &&
res.v_package[2]->v_integer == interface)
return (1);
return (0);
}
int
_acpi_matchhids(const char *hid, const char *hids[])
{
int i;
for (i = 0; hids[i]; i++)
if (!strcmp(hid, hids[i]))
return (1);
return (0);
}
int
acpi_matchhids(struct acpi_attach_args *aa, const char *hids[],
const char *driver)
{
if (aa->aaa_dev == NULL || aa->aaa_node == NULL)
return (0);
if (_acpi_matchhids(aa->aaa_dev, hids)) {
dnprintf(5, "driver %s matches at least one hid\n", driver);
return (2);
}
if (aa->aaa_cdev && _acpi_matchhids(aa->aaa_cdev, hids)) {
dnprintf(5, "driver %s matches at least one cid\n", driver);
return (1);
}
return (0);
}
int64_t
acpi_getsta(struct acpi_softc *sc, struct aml_node *node)
{
int64_t sta;
if (aml_evalinteger(sc, node, "_STA", 0, NULL, &sta))
sta = STA_PRESENT | STA_ENABLED | STA_SHOW_UI |
STA_DEV_OK | STA_BATTERY;
return sta;
}
/* Map ACPI device node to PCI */
int
acpi_getpci(struct aml_node *node, void *arg)
{
const char *pcihid[] = { ACPI_DEV_PCIB, ACPI_DEV_PCIEB, "HWP0002", 0 };
struct acpi_pci *pci, *ppci;
struct aml_value res;
struct acpi_softc *sc = arg;
pci_chipset_tag_t pc;
pcitag_t tag;
uint64_t val;
int64_t sta;
uint32_t reg;
sta = acpi_getsta(sc, node);
if ((sta & STA_PRESENT) == 0)
return 0;
if (!node->value || node->value->type != AML_OBJTYPE_DEVICE)
return 0;
if (!aml_evalhid(node, &res)) {
/* Check if this is a PCI Root node */
if (_acpi_matchhids(res.v_string, pcihid)) {
aml_freevalue(&res);
pci = malloc(sizeof(*pci), M_DEVBUF, M_WAITOK|M_ZERO);
pci->bus = -1;
if (!aml_evalinteger(sc, node, "_SEG", 0, NULL, &val))
pci->seg = val;
if (!aml_evalname(sc, node, "_CRS", 0, NULL, &res)) {
aml_parse_resource(&res, acpi_getminbus,
&pci->bus);
dnprintf(10, "%s post-crs: %d\n",
aml_nodename(node), pci->bus);
}
if (!aml_evalinteger(sc, node, "_BBN", 0, NULL, &val)) {
dnprintf(10, "%s post-bbn: %d, %lld\n",
aml_nodename(node), pci->bus, val);
if (pci->bus == -1)
pci->bus = val;
}
pci->sub = pci->bus;
node->pci = pci;
dnprintf(10, "found PCI root: %s %d\n",
aml_nodename(node), pci->bus);
TAILQ_INSERT_TAIL(&acpi_pcirootdevs, pci, next);
}
aml_freevalue(&res);
return 0;
}
/* If parent is not PCI, or device does not have _ADR, return */
if (!node->parent || (ppci = node->parent->pci) == NULL)
return 0;
if (aml_evalinteger(sc, node, "_ADR", 0, NULL, &val))
return 0;
pci = malloc(sizeof(*pci), M_DEVBUF, M_WAITOK|M_ZERO);
pci->seg = ppci->seg;
pci->bus = ppci->sub;
pci->dev = ACPI_ADR_PCIDEV(val);
pci->fun = ACPI_ADR_PCIFUN(val);
pci->node = node;
pci->sub = -1;
dnprintf(10, "%.2x:%.2x.%x -> %s\n",
pci->bus, pci->dev, pci->fun,
aml_nodename(node));
/* Collect device power state information. */
if (aml_evalinteger(sc, node, "_S3D", 0, NULL, &val) == 0)
pci->_s3d = val;
else
pci->_s3d = -1;
if (aml_evalinteger(sc, node, "_S3W", 0, NULL, &val) == 0)
pci->_s3w = val;
else
pci->_s3w = -1;
if (aml_evalinteger(sc, node, "_S4D", 0, NULL, &val) == 0)
pci->_s4d = val;
else
pci->_s4d = -1;
if (aml_evalinteger(sc, node, "_S4W", 0, NULL, &val) == 0)
pci->_s4w = val;
else
pci->_s4w = -1;
/* Check if PCI device exists */
if (pci->dev > 0x1F || pci->fun > 7) {
free(pci, M_DEVBUF, sizeof(*pci));
return (1);
}
pc = pci_lookup_segment(pci->seg);
tag = pci_make_tag(pc, pci->bus, pci->dev, pci->fun);
reg = pci_conf_read(pc, tag, PCI_ID_REG);
if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID) {
free(pci, M_DEVBUF, sizeof(*pci));
return (1);
}
node->pci = pci;
TAILQ_INSERT_TAIL(&acpi_pcidevs, pci, next);
/* Check if this is a PCI bridge */
reg = pci_conf_read(pc, tag, PCI_CLASS_REG);
if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_PCI) {
reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
pci->sub = PPB_BUSINFO_SECONDARY(reg);
dnprintf(10, "found PCI bridge: %s %d\n",
aml_nodename(node), pci->sub);
/* Continue scanning */
return (0);
}
/* Device does not have children, stop scanning */
return (1);
}
struct aml_node *
acpi_find_pci(pci_chipset_tag_t pc, pcitag_t tag)
{
struct acpi_pci *pdev;
int bus, dev, fun;
pci_decompose_tag(pc, tag, &bus, &dev, &fun);
TAILQ_FOREACH(pdev, &acpi_pcidevs, next) {
if (pdev->bus == bus && pdev->dev == dev && pdev->fun == fun)
return pdev->node;
}
return NULL;
}
struct aml_node *
acpi_pci_match(struct device *dev, struct pci_attach_args *pa)
{
struct acpi_pci *pdev;
int state;
TAILQ_FOREACH(pdev, &acpi_pcidevs, next) {
if (pdev->bus != pa->pa_bus ||
pdev->dev != pa->pa_device ||
pdev->fun != pa->pa_function)
continue;
dnprintf(10,"%s at acpi0 %s\n", dev->dv_xname,
aml_nodename(pdev->node));
pdev->device = dev;
/*
* If some Power Resources are dependent on this device
* initialize them.
*/
state = pci_get_powerstate(pa->pa_pc, pa->pa_tag);
acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 1);
acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 0);
aml_register_notify(pdev->node, NULL, acpi_pci_notify, pdev, 0);
return pdev->node;
}
return NULL;
}
pcireg_t
acpi_pci_min_powerstate(pci_chipset_tag_t pc, pcitag_t tag)
{
struct acpi_pci *pdev;
int bus, dev, fun;
int state = -1, defaultstate = pci_get_powerstate(pc, tag);
pci_decompose_tag(pc, tag, &bus, &dev, &fun);
TAILQ_FOREACH(pdev, &acpi_pcidevs, next) {
if (pdev->bus == bus && pdev->dev == dev && pdev->fun == fun) {
switch (acpi_softc->sc_state) {
case ACPI_STATE_S3:
defaultstate = PCI_PMCSR_STATE_D3;
state = MAX(pdev->_s3d, pdev->_s3w);
break;
case ACPI_STATE_S4:
state = MAX(pdev->_s4d, pdev->_s4w);
break;
case ACPI_STATE_S5:
default:
break;
}
if (state >= PCI_PMCSR_STATE_D0 &&
state <= PCI_PMCSR_STATE_D3)
return state;
}
}
return defaultstate;
}
void
acpi_pci_set_powerstate(pci_chipset_tag_t pc, pcitag_t tag, int state, int pre)
{
#if NACPIPWRRES > 0
struct acpi_softc *sc = acpi_softc;
struct acpi_pwrres *pr;
struct acpi_pci *pdev;
int bus, dev, fun;
char name[5];
pci_decompose_tag(pc, tag, &bus, &dev, &fun);
TAILQ_FOREACH(pdev, &acpi_pcidevs, next) {
if (pdev->bus == bus && pdev->dev == dev && pdev->fun == fun)
break;
}
/* XXX Add a check to discard nodes without Power Resources? */
if (pdev == NULL)
return;
SIMPLEQ_FOREACH(pr, &sc->sc_pwrresdevs, p_next) {
if (pr->p_node != pdev->node)
continue;
/*
* If the firmware is already aware that the device
* is in the given state, there's nothing to do.
*/
if (pr->p_state == state)
continue;
if (pre) {
/*
* If a Resource is dependent on this device for
* the given state, make sure it is turned "_ON".
*/
if (pr->p_res_state == state)
acpipwrres_ref_incr(pr->p_res_sc, pr->p_node);
} else {
/*
* If a Resource was referenced for the state we
* left, drop a reference and turn it "_OFF" if
* it was the last one.
*/
if (pr->p_res_state == pr->p_state)
acpipwrres_ref_decr(pr->p_res_sc, pr->p_node);
if (pr->p_res_state == state) {
snprintf(name, sizeof(name), "_PS%d", state);
aml_evalname(sc, pr->p_node, name, 0,
NULL, NULL);
}
pr->p_state = state;
}
}
#endif /* NACPIPWRRES > 0 */
}
int
acpi_pci_notify(struct aml_node *node, int ntype, void *arg)
{
struct acpi_pci *pdev = arg;
pci_chipset_tag_t pc;
pcitag_t tag;
pcireg_t reg;
int offset;
/* We're only interested in Device Wake notifications. */
if (ntype != 2)
return (0);
pc = pci_lookup_segment(pdev->seg);
tag = pci_make_tag(pc, pdev->bus, pdev->dev, pdev->fun);
if (pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, 0)) {
/* Clear the PME Status bit if it is set. */
reg = pci_conf_read(pc, tag, offset + PCI_PMCSR);
pci_conf_write(pc, tag, offset + PCI_PMCSR, reg);
}
return (0);
}
void
acpi_pciroots_attach(struct device *dev, void *aux, cfprint_t pr)
{
struct acpi_pci *pdev;
struct pcibus_attach_args *pba = aux;
KASSERT(pba->pba_busex != NULL);
TAILQ_FOREACH(pdev, &acpi_pcirootdevs, next) {
if (extent_alloc_region(pba->pba_busex, pdev->bus,
1, EX_NOWAIT) != 0)
continue;
pba->pba_bus = pdev->bus;
config_found(dev, pba, pr);
}
}
/* GPIO support */
struct acpi_gpio_event {
struct aml_node *node;
uint16_t pin;
};
void
acpi_gpio_event_task(void *arg0, int arg1)
{
struct aml_node *node = arg0;
struct aml_value evt;
uint16_t pin = arg1;
char name[5];
if (pin < 256) {
snprintf(name, sizeof(name), "_E%.2X", pin);
if (aml_evalname(acpi_softc, node, name, 0, NULL, NULL) == 0)
return;
}
memset(&evt, 0, sizeof(evt));
evt.v_integer = pin;
evt.type = AML_OBJTYPE_INTEGER;
aml_evalname(acpi_softc, node, "_EVT", 1, &evt, NULL);
}
int
acpi_gpio_event(void *arg)
{
struct acpi_gpio_event *ev = arg;
acpi_addtask(acpi_softc, acpi_gpio_event_task, ev->node, ev->pin);
acpi_wakeup(acpi_softc);
return 1;
}
int
acpi_gpio_parse_events(int crsidx, union acpi_resource *crs, void *arg)
{
struct aml_node *devnode = arg;
struct aml_node *node;
uint16_t pin;
switch (AML_CRSTYPE(crs)) {
case LR_GPIO:
node = aml_searchname(devnode,
(char *)&crs->pad[crs->lr_gpio.res_off]);
pin = *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off];
if (crs->lr_gpio.type == LR_GPIO_INT && pin < 256 &&
node && node->gpio && node->gpio->intr_establish) {
struct acpi_gpio *gpio = node->gpio;
struct acpi_gpio_event *ev;
ev = malloc(sizeof(*ev), M_DEVBUF, M_WAITOK);
ev->node = devnode;
ev->pin = pin;
gpio->intr_establish(gpio->cookie, pin,
crs->lr_gpio.tflags, acpi_gpio_event, ev);
}
break;
default:
printf("%s: unknown resource type %d\n", __func__,
AML_CRSTYPE(crs));
}
return 0;
}
void
acpi_register_gpio(struct acpi_softc *sc, struct aml_node *devnode)
{
struct aml_value arg[2];
struct aml_node *node;
struct aml_value res;
/* Register GeneralPurposeIO address space. */
memset(&arg, 0, sizeof(arg));
arg[0].type = AML_OBJTYPE_INTEGER;
arg[0].v_integer = ACPI_OPREG_GPIO;
arg[1].type = AML_OBJTYPE_INTEGER;
arg[1].v_integer = 1;
node = aml_searchname(devnode, "_REG");
if (node && aml_evalnode(sc, node, 2, arg, NULL))
printf("%s: _REG failed\n", node->name);
/* Register GPIO signaled ACPI events. */
if (aml_evalname(sc, devnode, "_AEI", 0, NULL, &res))
return;
aml_parse_resource(&res, acpi_gpio_parse_events, devnode);
}
#ifndef SMALL_KERNEL
void
acpi_register_gsb(struct acpi_softc *sc, struct aml_node *devnode)
{
struct aml_value arg[2];
struct aml_node *node;
/* Register GenericSerialBus address space. */
memset(&arg, 0, sizeof(arg));
arg[0].type = AML_OBJTYPE_INTEGER;
arg[0].v_integer = ACPI_OPREG_GSB;
arg[1].type = AML_OBJTYPE_INTEGER;
arg[1].v_integer = 1;
node = aml_searchname(devnode, "_REG");
if (node && aml_evalnode(sc, node, 2, arg, NULL))
printf("%s: _REG failed\n", node->name);
}
#endif
void
acpi_attach_common(struct acpi_softc *sc, paddr_t base)
{
struct acpi_mem_map handle;
struct acpi_rsdp *rsdp;
struct acpi_q *entry;
struct acpi_dsdt *p_dsdt;
#ifndef SMALL_KERNEL
int wakeup_dev_ct;
struct acpi_wakeq *wentry;
struct device *dev;
#endif /* SMALL_KERNEL */
paddr_t facspa;
uint16_t pm1;
int s;
rw_init(&sc->sc_lck, "acpilk");
acpi_softc = sc;
sc->sc_root = &aml_root;
if (acpi_map(base, sizeof(struct acpi_rsdp), &handle)) {
printf(": can't map memory\n");
return;
}
rsdp = (struct acpi_rsdp *)handle.va;
pool_init(&acpiwqpool, sizeof(struct acpi_taskq), 0, IPL_BIO, 0,
"acpiwqpl", NULL);
pool_setlowat(&acpiwqpool, 16);
SIMPLEQ_INIT(&sc->sc_tables);
SIMPLEQ_INIT(&sc->sc_wakedevs);
#if NACPIPWRRES > 0
SIMPLEQ_INIT(&sc->sc_pwrresdevs);
#endif /* NACPIPWRRES > 0 */
if (acpi_loadtables(sc, rsdp)) {
printf(": can't load tables\n");
acpi_unmap(&handle);
return;
}
acpi_unmap(&handle);
/*
* Find the FADT
*/
SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) {
if (memcmp(entry->q_table, FADT_SIG,
sizeof(FADT_SIG) - 1) == 0) {
sc->sc_fadt = entry->q_table;
break;
}
}
if (sc->sc_fadt == NULL) {
printf(": no FADT\n");
return;
}
sc->sc_major = sc->sc_fadt->hdr.revision;
if (sc->sc_major > 4)
sc->sc_minor = sc->sc_fadt->fadt_minor;
printf(": ACPI %d.%d", sc->sc_major, sc->sc_minor);
/*
* A bunch of things need to be done differently for
* Hardware-reduced ACPI.
*/
if (sc->sc_fadt->hdr_revision >= 5 &&
sc->sc_fadt->flags & FADT_HW_REDUCED_ACPI)
sc->sc_hw_reduced = 1;
/* Map Power Management registers */
acpi_map_pmregs(sc);
/*
* Check if we can and need to enable ACPI control.
*/
pm1 = acpi_read_pmreg(sc, ACPIREG_PM1_CNT, 0);
if ((pm1 & ACPI_PM1_SCI_EN) == 0 && sc->sc_fadt->smi_cmd &&
(!sc->sc_fadt->acpi_enable && !sc->sc_fadt->acpi_disable)) {
printf(", ACPI control unavailable\n");
acpi_unmap_pmregs(sc);
return;
}
/*
* Set up a pointer to the firmware control structure
*/
if (sc->sc_fadt->hdr_revision < 3 || sc->sc_fadt->x_firmware_ctl == 0)
facspa = sc->sc_fadt->firmware_ctl;
else
facspa = sc->sc_fadt->x_firmware_ctl;
if (acpi_map(facspa, sizeof(struct acpi_facs), &handle))
printf(" !FACS");
else
sc->sc_facs = (struct acpi_facs *)handle.va;
/* Create opcode hashtable */
aml_hashopcodes();
/* Create Default AML objects */
aml_create_defaultobjects();
/*
* Load the DSDT from the FADT pointer -- use the
* extended (64-bit) pointer if it exists
*/
if (sc->sc_fadt->hdr_revision < 3 || sc->sc_fadt->x_dsdt == 0)
entry = acpi_maptable(sc, sc->sc_fadt->dsdt, NULL, NULL, NULL,
-1);
else
entry = acpi_maptable(sc, sc->sc_fadt->x_dsdt, NULL, NULL, NULL,
-1);
if (entry == NULL)
printf(" !DSDT");
p_dsdt = entry->q_table;
acpi_parse_aml(sc, p_dsdt->aml, p_dsdt->hdr_length -
sizeof(p_dsdt->hdr));
/* Load SSDT's */
SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) {
if (memcmp(entry->q_table, SSDT_SIG,
sizeof(SSDT_SIG) - 1) == 0) {
p_dsdt = entry->q_table;
acpi_parse_aml(sc, p_dsdt->aml, p_dsdt->hdr_length -
sizeof(p_dsdt->hdr));
}
}
/* Perform post-parsing fixups */
aml_postparse();
#ifndef SMALL_KERNEL
/* Find available sleeping states */
acpi_init_states(sc);
/* Find available sleep/resume related methods. */
acpi_init_pm(sc);
#endif /* SMALL_KERNEL */
/* Initialize GPE handlers */
s = splbio();
acpi_init_gpes(sc);
splx(s);
/* some devices require periodic polling */
timeout_set(&sc->sc_dev_timeout, acpi_poll, sc);
acpi_enabled = 1;
/*
* Take over ACPI control. Note that once we do this, we
* effectively tell the system that we have ownership of
* the ACPI hardware registers, and that SMI should leave
* them alone
*
* This may prevent thermal control on some systems where
* that actually does work
*/
if ((pm1 & ACPI_PM1_SCI_EN) == 0 && sc->sc_fadt->smi_cmd) {
if (acpi_enable(sc)) {
printf(", can't enable ACPI\n");
return;
}
}
printf("\n%s: tables", DEVNAME(sc));
SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) {
printf(" %.4s", (char *)entry->q_table);
}
printf("\n");
#ifndef SMALL_KERNEL
/* Display wakeup devices and lowest S-state */
wakeup_dev_ct = 0;
printf("%s: wakeup devices", DEVNAME(sc));
SIMPLEQ_FOREACH(wentry, &sc->sc_wakedevs, q_next) {
if (wakeup_dev_ct < 16)
printf(" %.4s(S%d)", wentry->q_node->name,
wentry->q_state);
else if (wakeup_dev_ct == 16)
printf(" [...]");
wakeup_dev_ct++;
}
printf("\n");
#ifdef SUSPEND
if (wakeup_dev_ct > 0)
device_register_wakeup(&sc->sc_dev);
#endif
/*
* ACPI is enabled now -- attach timer
*/
if (!sc->sc_hw_reduced &&
(sc->sc_fadt->pm_tmr_blk || sc->sc_fadt->x_pm_tmr_blk.address)) {
struct acpi_attach_args aaa;
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_name = "acpitimer";
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
config_found(&sc->sc_dev, &aaa, acpi_print);
}
#endif /* SMALL_KERNEL */
/*
* Attach table-defined devices
*/
SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) {
struct acpi_attach_args aaa;
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_dmat = sc->sc_ci_dmat;
aaa.aaa_table = entry->q_table;
config_found_sm(&sc->sc_dev, &aaa, acpi_print, acpi_submatch);
}
/* initialize runtime environment */
aml_find_node(sc->sc_root, "_INI", acpi_inidev, sc);
/* Get PCI mapping */
aml_walknodes(sc->sc_root, AML_WALK_PRE, acpi_getpci, sc);
#if defined (__amd64__) || defined(__i386__)
/* attach pci interrupt routing tables */
aml_find_node(sc->sc_root, "_PRT", acpi_foundprt, sc);
#endif
aml_find_node(sc->sc_root, "_HID", acpi_foundec, sc);
/* check if we're running on a sony */
aml_find_node(sc->sc_root, "GBRT", acpi_foundsony, sc);
#ifndef SMALL_KERNEL
/* try to find smart battery first */
aml_find_node(sc->sc_root, "_HID", acpi_foundsbs, sc);
#endif /* SMALL_KERNEL */
/* attach battery, power supply and button devices */
aml_find_node(sc->sc_root, "_HID", acpi_foundhid, sc);
aml_walknodes(sc->sc_root, AML_WALK_PRE, acpi_add_device, sc);
#ifndef SMALL_KERNEL
#if NWD > 0
/* Attach IDE bay */
aml_walknodes(sc->sc_root, AML_WALK_PRE, acpi_foundide, sc);
#endif
/* attach docks */
aml_find_node(sc->sc_root, "_DCK", acpi_founddock, sc);
/* attach video */
aml_find_node(sc->sc_root, "_DOS", acpi_foundvideo, sc);
/* create list of devices we want to query when APM comes in */
SLIST_INIT(&sc->sc_ac);
SLIST_INIT(&sc->sc_bat);
TAILQ_FOREACH(dev, &alldevs, dv_list) {
if (!strcmp(dev->dv_cfdata->cf_driver->cd_name, "acpiac")) {
struct acpi_ac *ac;
ac = malloc(sizeof(*ac), M_DEVBUF, M_WAITOK | M_ZERO);
ac->aac_softc = (struct acpiac_softc *)dev;
SLIST_INSERT_HEAD(&sc->sc_ac, ac, aac_link);
} else if (!strcmp(dev->dv_cfdata->cf_driver->cd_name, "acpibat")) {
struct acpi_bat *bat;
bat = malloc(sizeof(*bat), M_DEVBUF, M_WAITOK | M_ZERO);
bat->aba_softc = (struct acpibat_softc *)dev;
SLIST_INSERT_HEAD(&sc->sc_bat, bat, aba_link);
} else if (!strcmp(dev->dv_cfdata->cf_driver->cd_name, "acpisbs")) {
struct acpi_sbs *sbs;
sbs = malloc(sizeof(*sbs), M_DEVBUF, M_WAITOK | M_ZERO);
sbs->asbs_softc = (struct acpisbs_softc *)dev;
SLIST_INSERT_HEAD(&sc->sc_sbs, sbs, asbs_link);
}
}
#endif /* SMALL_KERNEL */
/* Setup threads */
sc->sc_thread = malloc(sizeof(struct acpi_thread), M_DEVBUF, M_WAITOK);
sc->sc_thread->sc = sc;
sc->sc_thread->running = 1;
/* Enable PCI Power Management. */
pci_dopm = 1;
acpi_attach_machdep(sc);
kthread_create_deferred(acpi_create_thread, sc);
}
int
acpi_submatch(struct device *parent, void *match, void *aux)
{
struct acpi_attach_args *aaa = (struct acpi_attach_args *)aux;
struct cfdata *cf = match;
if (aaa->aaa_table == NULL)
return (0);
return ((*cf->cf_attach->ca_match)(parent, match, aux));
}
int
acpi_print(void *aux, const char *pnp)
{
struct acpi_attach_args *aa = aux;
if (pnp) {
if (aa->aaa_name)
printf("%s at %s", aa->aaa_name, pnp);
else if (aa->aaa_dev)
printf("\"%s\" at %s", aa->aaa_dev, pnp);
else
return (QUIET);
}
return (UNCONF);
}
struct acpi_q *
acpi_maptable(struct acpi_softc *sc, paddr_t addr, const char *sig,
const char *oem, const char *tbl, int flag)
{
static int tblid;
struct acpi_mem_map handle;
struct acpi_table_header *hdr;
struct acpi_q *entry;
size_t len;
/* Check if we can map address */
if (addr == 0)
return NULL;
if (acpi_map(addr, sizeof(*hdr), &handle))
return NULL;
hdr = (struct acpi_table_header *)handle.va;
len = hdr->length;
acpi_unmap(&handle);
/* Validate length/checksum */
if (acpi_map(addr, len, &handle))
return NULL;
hdr = (struct acpi_table_header *)handle.va;
if (acpi_checksum(hdr, len))
printf("\n%s: %.4s checksum error",
DEVNAME(sc), hdr->signature);
if ((sig && memcmp(sig, hdr->signature, 4)) ||
(oem && memcmp(oem, hdr->oemid, 6)) ||
(tbl && memcmp(tbl, hdr->oemtableid, 8))) {
acpi_unmap(&handle);
return NULL;
}
/* Allocate copy */
entry = malloc(sizeof(*entry) + len, M_DEVBUF, M_NOWAIT);
if (entry != NULL) {
memcpy(entry->q_data, handle.va, len);
entry->q_table = entry->q_data;
entry->q_id = ++tblid;
if (flag < 0)
SIMPLEQ_INSERT_HEAD(&sc->sc_tables, entry,
q_next);
else if (flag > 0)
SIMPLEQ_INSERT_TAIL(&sc->sc_tables, entry,
q_next);
}
acpi_unmap(&handle);
return entry;
}
int
acpi_loadtables(struct acpi_softc *sc, struct acpi_rsdp *rsdp)
{
struct acpi_q *sdt;
int i, ntables;
size_t len;
if (rsdp->rsdp_revision == 2 && rsdp->rsdp_xsdt) {
struct acpi_xsdt *xsdt;
sdt = acpi_maptable(sc, rsdp->rsdp_xsdt, NULL, NULL, NULL, 0);
if (sdt == NULL) {
printf("couldn't map xsdt\n");
return (ENOMEM);
}
xsdt = (struct acpi_xsdt *)sdt->q_data;
len = xsdt->hdr.length;
ntables = (len - sizeof(struct acpi_table_header)) /
sizeof(xsdt->table_offsets[0]);
for (i = 0; i < ntables; i++)
acpi_maptable(sc, xsdt->table_offsets[i], NULL, NULL,
NULL, 1);
free(sdt, M_DEVBUF, sizeof(*sdt) + len);
} else {
struct acpi_rsdt *rsdt;
sdt = acpi_maptable(sc, rsdp->rsdp_rsdt, NULL, NULL, NULL, 0);
if (sdt == NULL) {
printf("couldn't map rsdt\n");
return (ENOMEM);
}
rsdt = (struct acpi_rsdt *)sdt->q_data;
len = rsdt->hdr.length;
ntables = (len - sizeof(struct acpi_table_header)) /
sizeof(rsdt->table_offsets[0]);
for (i = 0; i < ntables; i++)
acpi_maptable(sc, rsdt->table_offsets[i], NULL, NULL,
NULL, 1);
free(sdt, M_DEVBUF, sizeof(*sdt) + len);
}
return (0);
}
/* Read from power management register */
int
acpi_read_pmreg(struct acpi_softc *sc, int reg, int offset)
{
bus_space_handle_t ioh;
bus_size_t size;
int regval;
/*
* For Hardware-reduced ACPI we emulate PM1B_CNT to reflect
* that the system is always in ACPI mode.
*/
if (sc->sc_hw_reduced && reg == ACPIREG_PM1B_CNT) {
KASSERT(offset == 0);
return ACPI_PM1_SCI_EN;
}
/*
* For Hardware-reduced ACPI we also emulate PM1A_STS using
* SLEEP_STATUS_REG.
*/
if (sc->sc_hw_reduced && reg == ACPIREG_PM1A_STS &&
sc->sc_fadt->sleep_status_reg.register_bit_width > 0) {
uint8_t value;
KASSERT(offset == 0);
acpi_gasio(sc, ACPI_IOREAD,
sc->sc_fadt->sleep_status_reg.address_space_id,
sc->sc_fadt->sleep_status_reg.address,
sc->sc_fadt->sleep_status_reg.register_bit_width / 8,
sc->sc_fadt->sleep_status_reg.access_size, &value);
return ((int)value << 8);
}
/* Special cases: 1A/1B blocks can be OR'ed together */
switch (reg) {
case ACPIREG_PM1_EN:
return (acpi_read_pmreg(sc, ACPIREG_PM1A_EN, offset) |
acpi_read_pmreg(sc, ACPIREG_PM1B_EN, offset));
case ACPIREG_PM1_STS:
return (acpi_read_pmreg(sc, ACPIREG_PM1A_STS, offset) |
acpi_read_pmreg(sc, ACPIREG_PM1B_STS, offset));
case ACPIREG_PM1_CNT:
return (acpi_read_pmreg(sc, ACPIREG_PM1A_CNT, offset) |
acpi_read_pmreg(sc, ACPIREG_PM1B_CNT, offset));
case ACPIREG_GPE_STS:
dnprintf(50, "read GPE_STS offset: %.2x %.2x %.2x\n", offset,
sc->sc_fadt->gpe0_blk_len>>1, sc->sc_fadt->gpe1_blk_len>>1);
if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) {
reg = ACPIREG_GPE0_STS;
}
break;
case ACPIREG_GPE_EN:
dnprintf(50, "read GPE_EN offset: %.2x %.2x %.2x\n",
offset, sc->sc_fadt->gpe0_blk_len>>1,
sc->sc_fadt->gpe1_blk_len>>1);
if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) {
reg = ACPIREG_GPE0_EN;
}
break;
}
if (reg >= ACPIREG_MAXREG || sc->sc_pmregs[reg].size == 0)
return (0);
regval = 0;
ioh = sc->sc_pmregs[reg].ioh;
size = sc->sc_pmregs[reg].size;
if (size > sc->sc_pmregs[reg].access)
size = sc->sc_pmregs[reg].access;
switch (size) {
case 1:
regval = bus_space_read_1(sc->sc_iot, ioh, offset);
break;
case 2:
regval = bus_space_read_2(sc->sc_iot, ioh, offset);
break;
case 4:
regval = bus_space_read_4(sc->sc_iot, ioh, offset);
break;
}
dnprintf(30, "acpi_readpm: %s = %.4x:%.4x %x\n",
sc->sc_pmregs[reg].name,
sc->sc_pmregs[reg].addr, offset, regval);
return (regval);
}
/* Write to power management register */
void
acpi_write_pmreg(struct acpi_softc *sc, int reg, int offset, int regval)
{
bus_space_handle_t ioh;
bus_size_t size;
/*
* For Hardware-reduced ACPI we also emulate PM1A_STS using
* SLEEP_STATUS_REG.
*/
if (sc->sc_hw_reduced && reg == ACPIREG_PM1A_STS &&
sc->sc_fadt->sleep_status_reg.register_bit_width > 0) {
uint8_t value = (regval >> 8);
KASSERT(offset == 0);
acpi_gasio(sc, ACPI_IOWRITE,
sc->sc_fadt->sleep_status_reg.address_space_id,
sc->sc_fadt->sleep_status_reg.address,
sc->sc_fadt->sleep_status_reg.register_bit_width / 8,
sc->sc_fadt->sleep_status_reg.access_size, &value);
return;
}
/*
* For Hardware-reduced ACPI we also emulate PM1A_CNT using
* SLEEP_CONTROL_REG.
*/
if (sc->sc_hw_reduced && reg == ACPIREG_PM1A_CNT &&
sc->sc_fadt->sleep_control_reg.register_bit_width > 0) {
uint8_t value = (regval >> 8);
KASSERT(offset == 0);
acpi_gasio(sc, ACPI_IOWRITE,
sc->sc_fadt->sleep_control_reg.address_space_id,
sc->sc_fadt->sleep_control_reg.address,
sc->sc_fadt->sleep_control_reg.register_bit_width / 8,
sc->sc_fadt->sleep_control_reg.access_size, &value);
return;
}
/* Special cases: 1A/1B blocks can be written with same value */
switch (reg) {
case ACPIREG_PM1_EN:
acpi_write_pmreg(sc, ACPIREG_PM1A_EN, offset, regval);
acpi_write_pmreg(sc, ACPIREG_PM1B_EN, offset, regval);
break;
case ACPIREG_PM1_STS:
acpi_write_pmreg(sc, ACPIREG_PM1A_STS, offset, regval);
acpi_write_pmreg(sc, ACPIREG_PM1B_STS, offset, regval);
break;
case ACPIREG_PM1_CNT:
acpi_write_pmreg(sc, ACPIREG_PM1A_CNT, offset, regval);
acpi_write_pmreg(sc, ACPIREG_PM1B_CNT, offset, regval);
break;
case ACPIREG_GPE_STS:
dnprintf(50, "write GPE_STS offset: %.2x %.2x %.2x %.2x\n",
offset, sc->sc_fadt->gpe0_blk_len>>1,
sc->sc_fadt->gpe1_blk_len>>1, regval);
if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) {
reg = ACPIREG_GPE0_STS;
}
break;
case ACPIREG_GPE_EN:
dnprintf(50, "write GPE_EN offset: %.2x %.2x %.2x %.2x\n",
offset, sc->sc_fadt->gpe0_blk_len>>1,
sc->sc_fadt->gpe1_blk_len>>1, regval);
if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) {
reg = ACPIREG_GPE0_EN;
}
break;
}
/* All special case return here */
if (reg >= ACPIREG_MAXREG)
return;
ioh = sc->sc_pmregs[reg].ioh;
size = sc->sc_pmregs[reg].size;
if (size > sc->sc_pmregs[reg].access)
size = sc->sc_pmregs[reg].access;
switch (size) {
case 1:
bus_space_write_1(sc->sc_iot, ioh, offset, regval);
break;
case 2:
bus_space_write_2(sc->sc_iot, ioh, offset, regval);
break;
case 4:
bus_space_write_4(sc->sc_iot, ioh, offset, regval);
break;
}
dnprintf(30, "acpi_writepm: %s = %.4x:%.4x %x\n",
sc->sc_pmregs[reg].name, sc->sc_pmregs[reg].addr, offset, regval);
}
/* Map Power Management registers */
void
acpi_map_pmregs(struct acpi_softc *sc)
{
struct acpi_fadt *fadt = sc->sc_fadt;
bus_addr_t addr;
bus_size_t size, access;
const char *name;
int reg;
for (reg = 0; reg < ACPIREG_MAXREG; reg++) {
size = 0;
access = 0;
switch (reg) {
case ACPIREG_SMICMD:
name = "smi";
size = access = 1;
addr = fadt->smi_cmd;
break;
case ACPIREG_PM1A_STS:
case ACPIREG_PM1A_EN:
name = "pm1a_sts";
size = fadt->pm1_evt_len >> 1;
if (fadt->pm1a_evt_blk) {
addr = fadt->pm1a_evt_blk;
access = 2;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_pm1a_evt_blk.address;
access = 1 << fadt->x_pm1a_evt_blk.access_size;
}
if (reg == ACPIREG_PM1A_EN && addr) {
addr += size;
name = "pm1a_en";
}
break;
case ACPIREG_PM1A_CNT:
name = "pm1a_cnt";
size = fadt->pm1_cnt_len;
if (fadt->pm1a_cnt_blk) {
addr = fadt->pm1a_cnt_blk;
access = 2;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_pm1a_cnt_blk.address;
access = 1 << fadt->x_pm1a_cnt_blk.access_size;
}
break;
case ACPIREG_PM1B_STS:
case ACPIREG_PM1B_EN:
name = "pm1b_sts";
size = fadt->pm1_evt_len >> 1;
if (fadt->pm1b_evt_blk) {
addr = fadt->pm1b_evt_blk;
access = 2;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_pm1b_evt_blk.address;
access = 1 << fadt->x_pm1b_evt_blk.access_size;
}
if (reg == ACPIREG_PM1B_EN && addr) {
addr += size;
name = "pm1b_en";
}
break;
case ACPIREG_PM1B_CNT:
name = "pm1b_cnt";
size = fadt->pm1_cnt_len;
if (fadt->pm1b_cnt_blk) {
addr = fadt->pm1b_cnt_blk;
access = 2;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_pm1b_cnt_blk.address;
access = 1 << fadt->x_pm1b_cnt_blk.access_size;
}
break;
case ACPIREG_PM2_CNT:
name = "pm2_cnt";
size = fadt->pm2_cnt_len;
if (fadt->pm2_cnt_blk) {
addr = fadt->pm2_cnt_blk;
access = size;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_pm2_cnt_blk.address;
access = 1 << fadt->x_pm2_cnt_blk.access_size;
}
break;
#if 0
case ACPIREG_PM_TMR:
/* Allocated in acpitimer */
name = "pm_tmr";
size = fadt->pm_tmr_len;
if (fadt->pm_tmr_blk) {
addr = fadt->pm_tmr_blk;
access = 4;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_pm_tmr_blk.address;
access = 1 << fadt->x_pm_tmr_blk.access_size;
}
break;
#endif
case ACPIREG_GPE0_STS:
case ACPIREG_GPE0_EN:
name = "gpe0_sts";
size = fadt->gpe0_blk_len >> 1;
if (fadt->gpe0_blk) {
addr = fadt->gpe0_blk;
access = 1;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_gpe0_blk.address;
access = 1 << fadt->x_gpe0_blk.access_size;
}
dnprintf(20, "gpe0 block len : %x\n",
fadt->gpe0_blk_len >> 1);
dnprintf(20, "gpe0 block addr: %x\n",
fadt->gpe0_blk);
if (reg == ACPIREG_GPE0_EN && addr) {
addr += size;
name = "gpe0_en";
}
break;
case ACPIREG_GPE1_STS:
case ACPIREG_GPE1_EN:
name = "gpe1_sts";
size = fadt->gpe1_blk_len >> 1;
if (fadt->gpe1_blk) {
addr = fadt->gpe1_blk;
access = 1;
} else if (fadt->hdr_revision >= 3) {
addr = fadt->x_gpe1_blk.address;
access = 1 << fadt->x_gpe1_blk.access_size;
}
dnprintf(20, "gpe1 block len : %x\n",
fadt->gpe1_blk_len >> 1);
dnprintf(20, "gpe1 block addr: %x\n",
fadt->gpe1_blk);
if (reg == ACPIREG_GPE1_EN && addr) {
addr += size;
name = "gpe1_en";
}
break;
}
if (size && addr) {
dnprintf(50, "mapping: %.4lx %.4lx %s\n",
addr, size, name);
/* Size and address exist; map register space */
bus_space_map(sc->sc_iot, addr, size, 0,
&sc->sc_pmregs[reg].ioh);
sc->sc_pmregs[reg].name = name;
sc->sc_pmregs[reg].size = size;
sc->sc_pmregs[reg].addr = addr;
sc->sc_pmregs[reg].access = min(access, 4);
}
}
}
void
acpi_unmap_pmregs(struct acpi_softc *sc)
{
int reg;
for (reg = 0; reg < ACPIREG_MAXREG; reg++) {
if (sc->sc_pmregs[reg].size && sc->sc_pmregs[reg].addr)
bus_space_unmap(sc->sc_iot, sc->sc_pmregs[reg].ioh,
sc->sc_pmregs[reg].size);
}
}
int
acpi_enable(struct acpi_softc *sc)
{
int idx;
acpi_write_pmreg(sc, ACPIREG_SMICMD, 0, sc->sc_fadt->acpi_enable);
idx = 0;
do {
if (idx++ > ACPIEN_RETRIES) {
return ETIMEDOUT;
}
} while (!(acpi_read_pmreg(sc, ACPIREG_PM1_CNT, 0) & ACPI_PM1_SCI_EN));
return 0;
}
/* ACPI Workqueue support */
SIMPLEQ_HEAD(,acpi_taskq) acpi_taskq =
SIMPLEQ_HEAD_INITIALIZER(acpi_taskq);
void
acpi_addtask(struct acpi_softc *sc, void (*handler)(void *, int),
void *arg0, int arg1)
{
struct acpi_taskq *wq;
int s;
wq = pool_get(&acpiwqpool, PR_ZERO | PR_NOWAIT);
if (wq == NULL) {
printf("unable to create task");
return;
}
wq->handler = handler;
wq->arg0 = arg0;
wq->arg1 = arg1;
s = splbio();
SIMPLEQ_INSERT_TAIL(&acpi_taskq, wq, next);
splx(s);
}
int
acpi_dotask(struct acpi_softc *sc)
{
struct acpi_taskq *wq;
int s;
s = splbio();
if (SIMPLEQ_EMPTY(&acpi_taskq)) {
splx(s);
/* we don't have anything to do */
return (0);
}
wq = SIMPLEQ_FIRST(&acpi_taskq);
SIMPLEQ_REMOVE_HEAD(&acpi_taskq, next);
splx(s);
wq->handler(wq->arg0, wq->arg1);
pool_put(&acpiwqpool, wq);
/* We did something */
return (1);
}
#ifndef SMALL_KERNEL
int
is_ata(struct aml_node *node)
{
return (aml_searchname(node, "_GTM") != NULL ||
aml_searchname(node, "_GTF") != NULL ||
aml_searchname(node, "_STM") != NULL ||
aml_searchname(node, "_SDD") != NULL);
}
int
is_ejectable(struct aml_node *node)
{
return (aml_searchname(node, "_EJ0") != NULL);
}
int
is_ejectable_bay(struct aml_node *node)
{
return ((is_ata(node) || is_ata(node->parent)) && is_ejectable(node));
}
#if NWD > 0
int
acpiide_notify(struct aml_node *node, int ntype, void *arg)
{
struct idechnl *ide = arg;
struct acpi_softc *sc = ide->sc;
struct pciide_softc *wsc;
struct device *dev;
int b,d,f;
int64_t sta;
if (aml_evalinteger(sc, node, "_STA", 0, NULL, &sta) != 0)
return (0);
dnprintf(10, "IDE notify! %s %d status:%llx\n", aml_nodename(node),
ntype, sta);
/* Walk device list looking for IDE device match */
TAILQ_FOREACH(dev, &alldevs, dv_list) {
if (strcmp(dev->dv_cfdata->cf_driver->cd_name, "pciide"))
continue;
wsc = (struct pciide_softc *)dev;
pci_decompose_tag(NULL, wsc->sc_tag, &b, &d, &f);
if (b != ACPI_PCI_BUS(ide->addr) ||
d != ACPI_PCI_DEV(ide->addr) ||
f != ACPI_PCI_FN(ide->addr))
continue;
dnprintf(10, "Found pciide: %s %x.%x.%x channel:%llx\n",
dev->dv_xname, b,d,f, ide->chnl);
if (sta == 0 && ide->sta)
wdcdetach(
&wsc->pciide_channels[ide->chnl].wdc_channel, 0);
else if (sta && !ide->sta)
wdcattach(
&wsc->pciide_channels[ide->chnl].wdc_channel);
ide->sta = sta;
}
return (0);
}
int
acpi_foundide(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = arg;
struct aml_node *pp;
struct idechnl *ide;
union amlpci_t pi;
int lvl;
/* Check if this is an ejectable bay */
if (!is_ejectable_bay(node))
return (0);
ide = malloc(sizeof(struct idechnl), M_DEVBUF, M_NOWAIT | M_ZERO);
ide->sc = sc;
/* GTM/GTF can be at 2/3 levels: pciX.ideX.channelX[.driveX] */
lvl = 0;
for (pp=node->parent; pp; pp=pp->parent) {
lvl++;
if (aml_searchname(pp, "_HID"))
break;
}
/* Get PCI address and channel */
if (lvl == 3) {
aml_evalinteger(sc, node->parent, "_ADR", 0, NULL,
&ide->chnl);
aml_rdpciaddr(node->parent->parent, &pi);
ide->addr = pi.addr;
} else if (lvl == 4) {
aml_evalinteger(sc, node->parent->parent, "_ADR", 0, NULL,
&ide->chnl);
aml_rdpciaddr(node->parent->parent->parent, &pi);
ide->addr = pi.addr;
}
dnprintf(10, "%s %llx channel:%llx\n",
aml_nodename(node), ide->addr, ide->chnl);
aml_evalinteger(sc, node, "_STA", 0, NULL, &ide->sta);
dnprintf(10, "Got Initial STA: %llx\n", ide->sta);
aml_register_notify(node, "acpiide", acpiide_notify, ide, 0);
return (0);
}
#endif /* NWD > 0 */
void
acpi_sleep_task(void *arg0, int sleepmode)
{
struct acpi_softc *sc = arg0;
#ifdef SUSPEND
sleep_state(sc, sleepmode);
#endif
/* Tell userland to recheck A/C and battery status */
acpi_record_event(sc, APM_POWER_CHANGE);
}
#endif /* SMALL_KERNEL */
void
acpi_reset(void)
{
uint32_t reset_as, reset_len;
uint32_t value;
struct acpi_softc *sc = acpi_softc;
struct acpi_fadt *fadt = sc->sc_fadt;
if (acpi_enabled == 0)
return;
/*
* RESET_REG_SUP is not properly set in some implementations,
* but not testing against it breaks more machines than it fixes
*/
if (fadt->hdr_revision <= 1 ||
!(fadt->flags & FADT_RESET_REG_SUP) || fadt->reset_reg.address == 0)
return;
value = fadt->reset_value;
reset_as = fadt->reset_reg.register_bit_width / 8;
if (reset_as == 0)
reset_as = 1;
reset_len = fadt->reset_reg.access_size;
if (reset_len == 0)
reset_len = reset_as;
acpi_gasio(sc, ACPI_IOWRITE,
fadt->reset_reg.address_space_id,
fadt->reset_reg.address, reset_as, reset_len, &value);
delay(100000);
}
void
acpi_gpe_task(void *arg0, int gpe)
{
struct acpi_softc *sc = acpi_softc;
struct gpe_block *pgpe = &sc->gpe_table[gpe];
dnprintf(10, "handle gpe: %x\n", gpe);
if (pgpe->handler && pgpe->active) {
pgpe->active = 0;
pgpe->handler(sc, gpe, pgpe->arg);
}
}
void
acpi_pbtn_task(void *arg0, int dummy)
{
struct acpi_softc *sc = arg0;
extern int pwr_action;
uint16_t en;
int s;
dnprintf(1,"power button pressed\n");
/* Reset the latch and re-enable the GPE */
s = splbio();
en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0);
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0,
en | ACPI_PM1_PWRBTN_EN);
splx(s);
switch (pwr_action) {
case 0:
break;
case 1:
acpi_addtask(sc, acpi_powerdown_task, sc, 0);
break;
#ifndef SMALL_KERNEL
case 2:
acpi_addtask(sc, acpi_sleep_task, sc, SLEEP_SUSPEND);
break;
#endif
}
}
void
acpi_sbtn_task(void *arg0, int dummy)
{
struct acpi_softc *sc = arg0;
uint16_t en;
int s;
dnprintf(1,"sleep button pressed\n");
aml_notify_dev(ACPI_DEV_SBD, 0x80);
/* Reset the latch and re-enable the GPE */
s = splbio();
en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0);
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0,
en | ACPI_PM1_SLPBTN_EN);
splx(s);
}
void
acpi_powerdown_task(void *arg0, int dummy)
{
extern int allowpowerdown;
if (allowpowerdown == 1) {
allowpowerdown = 0;
prsignal(initprocess, SIGUSR2);
}
}
int
acpi_interrupt(void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
uint32_t processed = 0, idx, jdx;
uint16_t sts, en;
int gpe;
dnprintf(40, "ACPI Interrupt\n");
for (idx = 0; idx < sc->sc_lastgpe; idx += 8) {
sts = acpi_read_pmreg(sc, ACPIREG_GPE_STS, idx>>3);
en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, idx>>3);
if (en & sts) {
dnprintf(10, "GPE block: %.2x %.2x %.2x\n", idx, sts,
en);
/* Mask the GPE until it is serviced */
acpi_write_pmreg(sc, ACPIREG_GPE_EN, idx>>3, en & ~sts);
for (jdx = 0; jdx < 8; jdx++) {
if (!(en & sts & (1L << jdx)))
continue;
/* Signal this GPE */
gpe = idx + jdx;
sc->gpe_table[gpe].active = 1;
dnprintf(10, "queue gpe: %x\n", gpe);
acpi_addtask(sc, acpi_gpe_task, NULL, gpe);
/*
* Edge interrupts need their STS bits cleared
* now. Level interrupts will have their STS
* bits cleared just before they are
* re-enabled.
*/
if (sc->gpe_table[gpe].flags & GPE_EDGE)
acpi_write_pmreg(sc,
ACPIREG_GPE_STS, idx>>3, 1L << jdx);
processed = 1;
}
}
}
sts = acpi_read_pmreg(sc, ACPIREG_PM1_STS, 0);
en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0);
if (sts & en) {
dnprintf(10,"GEN interrupt: %.4x\n", sts & en);
sts &= en;
if (sts & ACPI_PM1_PWRBTN_STS) {
/* Mask and acknowledge */
en &= ~ACPI_PM1_PWRBTN_EN;
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en);
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0,
ACPI_PM1_PWRBTN_STS);
sts &= ~ACPI_PM1_PWRBTN_STS;
acpi_addtask(sc, acpi_pbtn_task, sc, 0);
}
if (sts & ACPI_PM1_SLPBTN_STS) {
/* Mask and acknowledge */
en &= ~ACPI_PM1_SLPBTN_EN;
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en);
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0,
ACPI_PM1_SLPBTN_STS);
sts &= ~ACPI_PM1_SLPBTN_STS;
acpi_addtask(sc, acpi_sbtn_task, sc, 0);
}
if (sts) {
printf("%s: PM1 stuck (en 0x%x st 0x%x), clearing\n",
sc->sc_dev.dv_xname, en, sts);
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en & ~sts);
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, sts);
}
processed = 1;
}
if (processed) {
acpi_wakeup(sc);
}
return (processed);
}
int
acpi_add_device(struct aml_node *node, void *arg)
{
static int nacpicpus = 0;
struct device *self = arg;
struct acpi_softc *sc = arg;
struct acpi_attach_args aaa;
struct aml_value res;
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
int proc_id = -1;
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_node = node;
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
if (node == NULL || node->value == NULL)
return 0;
switch (node->value->type) {
case AML_OBJTYPE_PROCESSOR:
if (sc->sc_skip_processor != 0)
return 0;
if (nacpicpus >= ncpus)
return 0;
if (aml_evalnode(sc, aaa.aaa_node, 0, NULL, &res) == 0) {
if (res.type == AML_OBJTYPE_PROCESSOR)
proc_id = res.v_processor.proc_id;
aml_freevalue(&res);
}
CPU_INFO_FOREACH(cii, ci) {
if (ci->ci_acpi_proc_id == proc_id)
break;
}
if (ci == NULL)
return 0;
nacpicpus++;
aaa.aaa_name = "acpicpu";
break;
case AML_OBJTYPE_THERMZONE:
aaa.aaa_name = "acpitz";
break;
case AML_OBJTYPE_POWERRSRC:
aaa.aaa_name = "acpipwrres";
break;
default:
return 0;
}
config_found(self, &aaa, acpi_print);
return 0;
}
void
acpi_enable_onegpe(struct acpi_softc *sc, int gpe)
{
uint8_t mask, en;
/* Read enabled register */
mask = (1L << (gpe & 7));
en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3);
dnprintf(50, "enabling GPE %.2x (current: %sabled) %.2x\n",
gpe, (en & mask) ? "en" : "dis", en);
acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask);
}
/* Clear all GPEs */
void
acpi_disable_allgpes(struct acpi_softc *sc)
{
int idx;
for (idx = 0; idx < sc->sc_lastgpe; idx += 8) {
acpi_write_pmreg(sc, ACPIREG_GPE_EN, idx >> 3, 0);
acpi_write_pmreg(sc, ACPIREG_GPE_STS, idx >> 3, -1);
}
}
/* Enable runtime GPEs */
void
acpi_enable_rungpes(struct acpi_softc *sc)
{
int idx;
for (idx = 0; idx < sc->sc_lastgpe; idx++)
if (sc->gpe_table[idx].handler)
acpi_enable_onegpe(sc, idx);
}
/* Enable wakeup GPEs */
void
acpi_enable_wakegpes(struct acpi_softc *sc, int state)
{
struct acpi_wakeq *wentry;
SIMPLEQ_FOREACH(wentry, &sc->sc_wakedevs, q_next) {
dnprintf(10, "%.4s(S%d) gpe %.2x\n", wentry->q_node->name,
wentry->q_state,
wentry->q_gpe);
if (state <= wentry->q_state)
acpi_enable_onegpe(sc, wentry->q_gpe);
}
}
int
acpi_set_gpehandler(struct acpi_softc *sc, int gpe, int (*handler)
(struct acpi_softc *, int, void *), void *arg, int flags)
{
struct gpe_block *ptbl;
ptbl = acpi_find_gpe(sc, gpe);
if (ptbl == NULL || handler == NULL)
return -EINVAL;
if ((flags & GPE_LEVEL) && (flags & GPE_EDGE))
return -EINVAL;
if (!(flags & (GPE_LEVEL | GPE_EDGE)))
return -EINVAL;
if (ptbl->handler != NULL)
printf("%s: GPE 0x%.2x already enabled\n", DEVNAME(sc), gpe);
dnprintf(50, "Adding GPE handler 0x%.2x (%s)\n", gpe,
(flags & GPE_EDGE ? "edge" : "level"));
ptbl->handler = handler;
ptbl->arg = arg;
ptbl->flags = flags;
return (0);
}
int
acpi_gpe(struct acpi_softc *sc, int gpe, void *arg)
{
struct aml_node *node = arg;
uint8_t mask, en;
dnprintf(10, "handling GPE %.2x\n", gpe);
aml_evalnode(sc, node, 0, NULL, NULL);
mask = (1L << (gpe & 7));
if (sc->gpe_table[gpe].flags & GPE_LEVEL)
acpi_write_pmreg(sc, ACPIREG_GPE_STS, gpe>>3, mask);
en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3);
acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask);
return (0);
}
/* Discover Devices that can wakeup the system
* _PRW returns a package
* pkg[0] = integer (FADT gpe bit) or package (gpe block,gpe bit)
* pkg[1] = lowest sleep state
* pkg[2+] = power resource devices (optional)
*
* To enable wakeup devices:
* Evaluate _ON method in each power resource device
* Evaluate _PSW method
*/
int
acpi_foundprw(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = arg;
struct acpi_wakeq *wq;
int64_t sta;
sta = acpi_getsta(sc, node->parent);
if ((sta & STA_PRESENT) == 0)
return 0;
wq = malloc(sizeof(struct acpi_wakeq), M_DEVBUF, M_NOWAIT | M_ZERO);
if (wq == NULL)
return 0;
wq->q_wakepkg = malloc(sizeof(struct aml_value), M_DEVBUF,
M_NOWAIT | M_ZERO);
if (wq->q_wakepkg == NULL) {
free(wq, M_DEVBUF, sizeof(*wq));
return 0;
}
dnprintf(10, "Found _PRW (%s)\n", node->parent->name);
aml_evalnode(sc, node, 0, NULL, wq->q_wakepkg);
wq->q_node = node->parent;
wq->q_gpe = -1;
/* Get GPE of wakeup device, and lowest sleep level */
if (wq->q_wakepkg->type == AML_OBJTYPE_PACKAGE &&
wq->q_wakepkg->length >= 2) {
if (wq->q_wakepkg->v_package[0]->type == AML_OBJTYPE_INTEGER)
wq->q_gpe = wq->q_wakepkg->v_package[0]->v_integer;
if (wq->q_wakepkg->v_package[1]->type == AML_OBJTYPE_INTEGER)
wq->q_state = wq->q_wakepkg->v_package[1]->v_integer;
}
SIMPLEQ_INSERT_TAIL(&sc->sc_wakedevs, wq, q_next);
return 0;
}
struct gpe_block *
acpi_find_gpe(struct acpi_softc *sc, int gpe)
{
if (gpe >= sc->sc_lastgpe)
return NULL;
return &sc->gpe_table[gpe];
}
void
acpi_init_gpes(struct acpi_softc *sc)
{
struct aml_node *gpe;
char name[12];
int idx;
sc->sc_lastgpe = sc->sc_fadt->gpe0_blk_len << 2;
dnprintf(50, "Last GPE: %.2x\n", sc->sc_lastgpe);
/* Allocate GPE table */
sc->gpe_table = mallocarray(sc->sc_lastgpe, sizeof(struct gpe_block),
M_DEVBUF, M_WAITOK | M_ZERO);
/* Clear GPE status */
acpi_disable_allgpes(sc);
for (idx = 0; idx < sc->sc_lastgpe; idx++) {
/* Search Level-sensitive GPES */
snprintf(name, sizeof(name), "\\_GPE._L%.2X", idx);
gpe = aml_searchname(sc->sc_root, name);
if (gpe != NULL)
acpi_set_gpehandler(sc, idx, acpi_gpe, gpe, GPE_LEVEL);
if (gpe == NULL) {
/* Search Edge-sensitive GPES */
snprintf(name, sizeof(name), "\\_GPE._E%.2X", idx);
gpe = aml_searchname(sc->sc_root, name);
if (gpe != NULL)
acpi_set_gpehandler(sc, idx, acpi_gpe, gpe,
GPE_EDGE);
}
}
aml_find_node(sc->sc_root, "_PRW", acpi_foundprw, sc);
}
void
acpi_init_pm(struct acpi_softc *sc)
{
sc->sc_tts = aml_searchname(sc->sc_root, "_TTS");
sc->sc_pts = aml_searchname(sc->sc_root, "_PTS");
sc->sc_wak = aml_searchname(sc->sc_root, "_WAK");
sc->sc_bfs = aml_searchname(sc->sc_root, "_BFS");
sc->sc_gts = aml_searchname(sc->sc_root, "_GTS");
sc->sc_sst = aml_searchname(sc->sc_root, "_SI_._SST");
}
#ifndef SMALL_KERNEL
void
acpi_init_states(struct acpi_softc *sc)
{
struct aml_value res;
char name[8];
int i;
printf("\n%s: sleep states", DEVNAME(sc));
for (i = ACPI_STATE_S0; i <= ACPI_STATE_S5; i++) {
snprintf(name, sizeof(name), "_S%d_", i);
sc->sc_sleeptype[i].slp_typa = -1;
sc->sc_sleeptype[i].slp_typb = -1;
if (aml_evalname(sc, sc->sc_root, name, 0, NULL, &res) == 0) {
if (res.type == AML_OBJTYPE_PACKAGE) {
sc->sc_sleeptype[i].slp_typa =
aml_val2int(res.v_package[0]);
sc->sc_sleeptype[i].slp_typb =
aml_val2int(res.v_package[1]);
printf(" S%d", i);
}
aml_freevalue(&res);
}
}
}
void
acpi_sleep_pm(struct acpi_softc *sc, int state)
{
uint16_t rega, regb, regra, regrb;
int retry = 0;
intr_disable();
/* Clear WAK_STS bit */
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_WAK_STS);
/* Disable BM arbitration at deep sleep and beyond */
if (state >= ACPI_STATE_S3 &&
sc->sc_fadt->pm2_cnt_blk && sc->sc_fadt->pm2_cnt_len)
acpi_write_pmreg(sc, ACPIREG_PM2_CNT, 0, ACPI_PM2_ARB_DIS);
/* Write SLP_TYPx values */
rega = acpi_read_pmreg(sc, ACPIREG_PM1A_CNT, 0);
regb = acpi_read_pmreg(sc, ACPIREG_PM1B_CNT, 0);
rega &= ~(ACPI_PM1_SLP_TYPX_MASK | ACPI_PM1_SLP_EN);
regb &= ~(ACPI_PM1_SLP_TYPX_MASK | ACPI_PM1_SLP_EN);
rega |= ACPI_PM1_SLP_TYPX(sc->sc_sleeptype[state].slp_typa);
regb |= ACPI_PM1_SLP_TYPX(sc->sc_sleeptype[state].slp_typb);
acpi_write_pmreg(sc, ACPIREG_PM1A_CNT, 0, rega);
acpi_write_pmreg(sc, ACPIREG_PM1B_CNT, 0, regb);
/* Loop on WAK_STS, setting the SLP_EN bits once in a while */
rega |= ACPI_PM1_SLP_EN;
regb |= ACPI_PM1_SLP_EN;
while (1) {
if (retry == 0) {
acpi_write_pmreg(sc, ACPIREG_PM1A_CNT, 0, rega);
acpi_write_pmreg(sc, ACPIREG_PM1B_CNT, 0, regb);
}
retry = (retry + 1) % 100000;
regra = acpi_read_pmreg(sc, ACPIREG_PM1A_STS, 0);
regrb = acpi_read_pmreg(sc, ACPIREG_PM1B_STS, 0);
if ((regra & ACPI_PM1_WAK_STS) ||
(regrb & ACPI_PM1_WAK_STS))
break;
}
}
uint32_t acpi_force_bm;
void
acpi_resume_pm(struct acpi_softc *sc, int fromstate)
{
uint16_t rega, regb, en;
/* Write SLP_TYPx values */
rega = acpi_read_pmreg(sc, ACPIREG_PM1A_CNT, 0);
regb = acpi_read_pmreg(sc, ACPIREG_PM1B_CNT, 0);
rega &= ~(ACPI_PM1_SLP_TYPX_MASK | ACPI_PM1_SLP_EN);
regb &= ~(ACPI_PM1_SLP_TYPX_MASK | ACPI_PM1_SLP_EN);
rega |= ACPI_PM1_SLP_TYPX(sc->sc_sleeptype[ACPI_STATE_S0].slp_typa);
regb |= ACPI_PM1_SLP_TYPX(sc->sc_sleeptype[ACPI_STATE_S0].slp_typb);
acpi_write_pmreg(sc, ACPIREG_PM1A_CNT, 0, rega);
acpi_write_pmreg(sc, ACPIREG_PM1B_CNT, 0, regb);
/* Force SCI_EN on resume to fix horribly broken machines */
acpi_write_pmreg(sc, ACPIREG_PM1_CNT, 0,
ACPI_PM1_SCI_EN | acpi_force_bm);
/* Clear fixed event status */
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_ALL_STS);
/* acpica-reference.pdf page 148 says do not call _BFS */
/* 1st resume AML step: _BFS(fromstate) */
aml_node_setval(sc, sc->sc_bfs, fromstate);
/* Enable runtime GPEs */
acpi_disable_allgpes(sc);
acpi_enable_rungpes(sc);
acpi_indicator(sc, ACPI_SST_WAKING);
/* 2nd resume AML step: _WAK(fromstate) */
aml_node_setval(sc, sc->sc_wak, fromstate);
/* Clear WAK_STS bit */
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_WAK_STS);
en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0);
if (!(sc->sc_fadt->flags & FADT_PWR_BUTTON))
en |= ACPI_PM1_PWRBTN_EN;
if (!(sc->sc_fadt->flags & FADT_SLP_BUTTON))
en |= ACPI_PM1_SLPBTN_EN;
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en);
/*
* If PM2 exists, re-enable BM arbitration (reportedly some
* BIOS forget to)
*/
if (sc->sc_fadt->pm2_cnt_blk && sc->sc_fadt->pm2_cnt_len) {
rega = acpi_read_pmreg(sc, ACPIREG_PM2_CNT, 0);
rega &= ~ACPI_PM2_ARB_DIS;
acpi_write_pmreg(sc, ACPIREG_PM2_CNT, 0, rega);
}
}
/* Set the indicator light to some state */
void
acpi_indicator(struct acpi_softc *sc, int led_state)
{
static int save_led_state = -1;
if (save_led_state != led_state) {
aml_node_setval(sc, sc->sc_sst, led_state);
save_led_state = led_state;
}
}
/* XXX
* We are going to do AML execution but are not in the acpi thread.
* We do not know if the acpi thread is sleeping on acpiec in some
* intermediate context. Wish us luck.
*/
void
acpi_powerdown(void)
{
int state = ACPI_STATE_S5, s;
struct acpi_softc *sc = acpi_softc;
if (acpi_enabled == 0)
return;
s = splhigh();
intr_disable();
cold = 1;
/* 1st powerdown AML step: _PTS(tostate) */
aml_node_setval(sc, sc->sc_pts, state);
acpi_disable_allgpes(sc);
acpi_enable_wakegpes(sc, state);
/* 2nd powerdown AML step: _GTS(tostate) */
aml_node_setval(sc, sc->sc_gts, state);
acpi_sleep_pm(sc, state);
panic("acpi S5 transition did not happen");
while (1)
;
}
#endif /* SMALL_KERNEL */
int
acpi_map_address(struct acpi_softc *sc, struct acpi_gas *gas, bus_addr_t base,
bus_size_t size, bus_space_handle_t *pioh, bus_space_tag_t *piot)
{
int iospace = GAS_SYSTEM_IOSPACE;
/* No GAS structure, default to I/O space */
if (gas != NULL) {
base += gas->address;
iospace = gas->address_space_id;
}
switch (iospace) {
case GAS_SYSTEM_MEMORY:
*piot = sc->sc_memt;
break;
case GAS_SYSTEM_IOSPACE:
*piot = sc->sc_iot;
break;
default:
return -1;
}
if (bus_space_map(*piot, base, size, 0, pioh))
return -1;
return 0;
}
void
acpi_wakeup(void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
sc->sc_threadwaiting = 0;
wakeup(sc);
}
void
acpi_thread(void *arg)
{
struct acpi_thread *thread = arg;
struct acpi_softc *sc = thread->sc;
extern int aml_busy;
int s;
/* AML/SMI cannot be trusted -- only run on the BSP */
sched_peg_curproc(&cpu_info_primary);
rw_enter_write(&sc->sc_lck);
/*
* If we have an interrupt handler, we can get notification
* when certain status bits changes in the ACPI registers,
* so let us enable some events we can forward to userland
*/
if (sc->sc_interrupt) {
int16_t en;
dnprintf(1,"slpbtn:%c pwrbtn:%c\n",
sc->sc_fadt->flags & FADT_SLP_BUTTON ? 'n' : 'y',
sc->sc_fadt->flags & FADT_PWR_BUTTON ? 'n' : 'y');
dnprintf(10, "Enabling acpi interrupts...\n");
sc->sc_threadwaiting = 1;
/* Enable Sleep/Power buttons if they exist */
s = splbio();
en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0);
if (!(sc->sc_fadt->flags & FADT_PWR_BUTTON))
en |= ACPI_PM1_PWRBTN_EN;
if (!(sc->sc_fadt->flags & FADT_SLP_BUTTON))
en |= ACPI_PM1_SLPBTN_EN;
acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en);
/* Enable handled GPEs here */
acpi_enable_rungpes(sc);
splx(s);
}
while (thread->running) {
s = splbio();
while (sc->sc_threadwaiting) {
dnprintf(10, "acpi thread going to sleep...\n");
rw_exit_write(&sc->sc_lck);
tsleep_nsec(sc, PWAIT, "acpi0", INFSLP);
rw_enter_write(&sc->sc_lck);
}
sc->sc_threadwaiting = 1;
splx(s);
if (aml_busy) {
panic("thread woke up to find aml was busy");
continue;
}
/* Run ACPI taskqueue */
while(acpi_dotask(acpi_softc))
;
}
free(thread, M_DEVBUF, sizeof(*thread));
kthread_exit(0);
}
void
acpi_create_thread(void *arg)
{
struct acpi_softc *sc = arg;
if (kthread_create(acpi_thread, sc->sc_thread, NULL, DEVNAME(sc))
!= 0)
printf("%s: unable to create isr thread, GPEs disabled\n",
DEVNAME(sc));
}
int
acpi_foundec(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
const char *dev;
struct aml_value res;
struct acpi_attach_args aaa;
if (aml_evalnode(sc, node, 0, NULL, &res) != 0)
return 0;
switch (res.type) {
case AML_OBJTYPE_STRING:
dev = res.v_string;
break;
case AML_OBJTYPE_INTEGER:
dev = aml_eisaid(aml_val2int(&res));
break;
default:
dev = "unknown";
break;
}
if (strcmp(dev, ACPI_DEV_ECD))
return 0;
/* Check if we're already attached */
if (sc->sc_ec && sc->sc_ec->sc_devnode == node->parent)
return 0;
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_node = node->parent;
aaa.aaa_dev = dev;
aaa.aaa_name = "acpiec";
config_found(self, &aaa, acpi_print);
aml_freevalue(&res);
return 0;
}
int
acpi_foundsony(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
struct acpi_attach_args aaa;
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_node = node->parent;
aaa.aaa_name = "acpisony";
config_found(self, &aaa, acpi_print);
return 0;
}
/* Support for _DSD Device Properties. */
int
acpi_getprop(struct aml_node *node, const char *prop, void *buf, int buflen)
{
struct aml_value dsd;
int i;
/* daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
static uint8_t prop_guid[] = {
0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01,
};
if (aml_evalname(acpi_softc, node, "_DSD", 0, NULL, &dsd))
return -1;
if (dsd.type != AML_OBJTYPE_PACKAGE || dsd.length != 2 ||
dsd.v_package[0]->type != AML_OBJTYPE_BUFFER ||
dsd.v_package[1]->type != AML_OBJTYPE_PACKAGE)
return -1;
/* Check UUID. */
if (dsd.v_package[0]->length != sizeof(prop_guid) ||
memcmp(dsd.v_package[0]->v_buffer, prop_guid,
sizeof(prop_guid)) != 0)
return -1;
/* Check properties. */
for (i = 0; i < dsd.v_package[1]->length; i++) {
struct aml_value *res = dsd.v_package[1]->v_package[i];
struct aml_value *val;
int len;
if (res->type != AML_OBJTYPE_PACKAGE || res->length != 2 ||
res->v_package[0]->type != AML_OBJTYPE_STRING ||
strcmp(res->v_package[0]->v_string, prop) != 0)
continue;
val = res->v_package[1];
if (val->type == AML_OBJTYPE_OBJREF)
val = val->v_objref.ref;
len = val->length;
switch (val->type) {
case AML_OBJTYPE_BUFFER:
memcpy(buf, val->v_buffer, min(len, buflen));
return len;
case AML_OBJTYPE_STRING:
memcpy(buf, val->v_string, min(len, buflen));
return len;
}
}
return -1;
}
uint64_t
acpi_getpropint(struct aml_node *node, const char *prop, uint64_t defval)
{
struct aml_value dsd;
int i;
/* daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
static uint8_t prop_guid[] = {
0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01,
};
if (aml_evalname(acpi_softc, node, "_DSD", 0, NULL, &dsd))
return defval;
if (dsd.type != AML_OBJTYPE_PACKAGE || dsd.length != 2 ||
dsd.v_package[0]->type != AML_OBJTYPE_BUFFER ||
dsd.v_package[1]->type != AML_OBJTYPE_PACKAGE)
return defval;
/* Check UUID. */
if (dsd.v_package[0]->length != sizeof(prop_guid) ||
memcmp(dsd.v_package[0]->v_buffer, prop_guid,
sizeof(prop_guid)) != 0)
return defval;
/* Check properties. */
for (i = 0; i < dsd.v_package[1]->length; i++) {
struct aml_value *res = dsd.v_package[1]->v_package[i];
struct aml_value *val;
if (res->type != AML_OBJTYPE_PACKAGE || res->length != 2 ||
res->v_package[0]->type != AML_OBJTYPE_STRING ||
strcmp(res->v_package[0]->v_string, prop) != 0)
continue;
val = res->v_package[1];
if (val->type == AML_OBJTYPE_OBJREF)
val = val->v_objref.ref;
if (val->type == AML_OBJTYPE_INTEGER)
return val->v_integer;
}
return defval;
}
int
acpi_parsehid(struct aml_node *node, void *arg, char *outcdev, char *outdev,
size_t devlen)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct aml_value res;
const char *dev;
/* NB aml_eisaid returns a static buffer, this must come first */
if (aml_evalname(acpi_softc, node->parent, "_CID", 0, NULL, &res) == 0) {
switch (res.type) {
case AML_OBJTYPE_STRING:
dev = res.v_string;
break;
case AML_OBJTYPE_INTEGER:
dev = aml_eisaid(aml_val2int(&res));
break;
default:
dev = "unknown";
break;
}
strlcpy(outcdev, dev, devlen);
aml_freevalue(&res);
dnprintf(10, "compatible with device: %s\n", outcdev);
} else {
outcdev[0] = '\0';
}
dnprintf(10, "found hid device: %s ", node->parent->name);
if (aml_evalnode(sc, node, 0, NULL, &res) != 0)
return (1);
switch (res.type) {
case AML_OBJTYPE_STRING:
dev = res.v_string;
break;
case AML_OBJTYPE_INTEGER:
dev = aml_eisaid(aml_val2int(&res));
break;
default:
dev = "unknown";
break;
}
dnprintf(10, " device: %s\n", dev);
strlcpy(outdev, dev, devlen);
aml_freevalue(&res);
return (0);
}
/* Devices for which we don't want to attach a driver */
const char *acpi_skip_hids[] = {
"INT0800", /* Intel 82802Firmware Hub Device */
"PNP0000", /* 8259-compatible Programmable Interrupt Controller */
"PNP0001", /* EISA Interrupt Controller */
"PNP0100", /* PC-class System Timer */
"PNP0103", /* HPET System Timer */
"PNP0200", /* PC-class DMA Controller */
"PNP0201", /* EISA DMA Controller */
"PNP0800", /* Microsoft Sound System Compatible Device */
"PNP0C01", /* System Board */
"PNP0C02", /* PNP Motherboard Resources */
"PNP0C04", /* x87-compatible Floating Point Processing Unit */
"PNP0C09", /* Embedded Controller Device */
"PNP0C0F", /* PCI Interrupt Link Device */
NULL
};
/* ISA devices for which we attach a driver later */
const char *acpi_isa_hids[] = {
"PNP0303", /* IBM Enhanced Keyboard (101/102-key, PS/2 Mouse) */
"PNP0400", /* Standard LPT Parallel Port */
"PNP0401", /* ECP Parallel Port */
"PNP0700", /* PC-class Floppy Disk Controller */
"PNP0F03", /* Microsoft PS/2-style Mouse */
"PNP0F13", /* PS/2 Mouse */
NULL
};
void
acpi_attach_deps(struct acpi_softc *sc, struct aml_node *node)
{
struct aml_value res, *val;
struct aml_node *dep;
int i;
if (aml_evalname(sc, node, "_DEP", 0, NULL, &res))
return;
if (res.type != AML_OBJTYPE_PACKAGE)
return;
for (i = 0; i < res.length; i++) {
val = res.v_package[i];
if (val->type == AML_OBJTYPE_OBJREF)
val = val->v_objref.ref;
if (val->type != AML_OBJTYPE_DEVICE)
continue;
dep = val->node;
if (dep == NULL || dep->attached)
continue;
dep = aml_searchname(dep, "_HID");
if (dep)
acpi_foundhid(dep, sc);
}
aml_freevalue(&res);
}
int
acpi_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
{
struct acpi_attach_args *aaa = arg;
int type = AML_CRSTYPE(crs);
uint8_t flags;
switch (type) {
case SR_IOPORT:
case SR_FIXEDPORT:
case LR_MEM24:
case LR_MEM32:
case LR_MEM32FIXED:
case LR_WORD:
case LR_DWORD:
case LR_QWORD:
if (aaa->aaa_naddr >= nitems(aaa->aaa_addr))
return 0;
break;
case SR_IRQ:
case LR_EXTIRQ:
if (aaa->aaa_nirq >= nitems(aaa->aaa_irq))
return 0;
}
switch (type) {
case SR_IOPORT:
case SR_FIXEDPORT:
aaa->aaa_bst[aaa->aaa_naddr] = aaa->aaa_iot;
break;
case LR_MEM24:
case LR_MEM32:
case LR_MEM32FIXED:
aaa->aaa_bst[aaa->aaa_naddr] = aaa->aaa_memt;
break;
case LR_WORD:
case LR_DWORD:
case LR_QWORD:
switch (crs->lr_word.type) {
case LR_TYPE_MEMORY:
aaa->aaa_bst[aaa->aaa_naddr] = aaa->aaa_memt;
break;
case LR_TYPE_IO:
aaa->aaa_bst[aaa->aaa_naddr] = aaa->aaa_iot;
break;
default:
/* Bus number range or something else; skip. */
return 0;
}
}
switch (type) {
case SR_IOPORT:
aaa->aaa_addr[aaa->aaa_naddr] = crs->sr_ioport._min;
aaa->aaa_size[aaa->aaa_naddr] = crs->sr_ioport._len;
aaa->aaa_naddr++;
break;
case SR_FIXEDPORT:
aaa->aaa_addr[aaa->aaa_naddr] = crs->sr_fioport._bas;
aaa->aaa_size[aaa->aaa_naddr] = crs->sr_fioport._len;
aaa->aaa_naddr++;
break;
case LR_MEM24:
aaa->aaa_addr[aaa->aaa_naddr] = crs->lr_m24._min;
aaa->aaa_size[aaa->aaa_naddr] = crs->lr_m24._len;
aaa->aaa_naddr++;
break;
case LR_MEM32:
aaa->aaa_addr[aaa->aaa_naddr] = crs->lr_m32._min;
aaa->aaa_size[aaa->aaa_naddr] = crs->lr_m32._len;
aaa->aaa_naddr++;
break;
case LR_MEM32FIXED:
aaa->aaa_addr[aaa->aaa_naddr] = crs->lr_m32fixed._bas;
aaa->aaa_size[aaa->aaa_naddr] = crs->lr_m32fixed._len;
aaa->aaa_naddr++;
break;
case LR_WORD:
aaa->aaa_addr[aaa->aaa_naddr] = crs->lr_word._min;
aaa->aaa_size[aaa->aaa_naddr] = crs->lr_word._len;
aaa->aaa_naddr++;
break;
case LR_DWORD:
aaa->aaa_addr[aaa->aaa_naddr] = crs->lr_dword._min;
aaa->aaa_size[aaa->aaa_naddr] = crs->lr_dword._len;
aaa->aaa_naddr++;
break;
case LR_QWORD:
aaa->aaa_addr[aaa->aaa_naddr] = crs->lr_qword._min;
aaa->aaa_size[aaa->aaa_naddr] = crs->lr_qword._len;
aaa->aaa_naddr++;
break;
case SR_IRQ:
aaa->aaa_irq[aaa->aaa_nirq] = ffs(crs->sr_irq.irq_mask) - 1;
/* Default is exclusive, active-high, edge triggered. */
if (AML_CRSLEN(crs) < 4)
flags = SR_IRQ_MODE;
else
flags = crs->sr_irq.irq_flags;
/* Map flags to those of the extended interrupt descriptor. */
if (flags & SR_IRQ_SHR)
aaa->aaa_irq_flags[aaa->aaa_nirq] |= LR_EXTIRQ_SHR;
if (flags & SR_IRQ_POLARITY)
aaa->aaa_irq_flags[aaa->aaa_nirq] |= LR_EXTIRQ_POLARITY;
if (flags & SR_IRQ_MODE)
aaa->aaa_irq_flags[aaa->aaa_nirq] |= LR_EXTIRQ_MODE;
aaa->aaa_nirq++;
break;
case LR_EXTIRQ:
aaa->aaa_irq[aaa->aaa_nirq] = crs->lr_extirq.irq[0];
aaa->aaa_irq_flags[aaa->aaa_nirq] = crs->lr_extirq.flags;
aaa->aaa_nirq++;
break;
}
return 0;
}
void
acpi_parse_crs(struct acpi_softc *sc, struct acpi_attach_args *aaa)
{
struct aml_value res;
if (aml_evalname(sc, aaa->aaa_node, "_CRS", 0, NULL, &res))
return;
aml_parse_resource(&res, acpi_parse_resources, aaa);
}
int
acpi_foundhid(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
char cdev[32];
char dev[32];
struct acpi_attach_args aaa;
int64_t sta;
int64_t cca;
#ifndef SMALL_KERNEL
int i;
#endif
if (acpi_parsehid(node, arg, cdev, dev, sizeof(dev)) != 0)
return (0);
sta = acpi_getsta(sc, node->parent);
if ((sta & (STA_PRESENT | STA_ENABLED)) != (STA_PRESENT | STA_ENABLED))
return (0);
if (aml_evalinteger(sc, node->parent, "_CCA", 0, NULL, &cca))
cca = 1;
acpi_attach_deps(sc, node->parent);
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_dmat = cca ? sc->sc_cc_dmat : sc->sc_ci_dmat;
aaa.aaa_node = node->parent;
aaa.aaa_dev = dev;
aaa.aaa_cdev = cdev;
acpi_parse_crs(sc, &aaa);
#ifndef SMALL_KERNEL
if (!strcmp(cdev, ACPI_DEV_MOUSE)) {
for (i = 0; i < nitems(sbtn_pnp); i++) {
if (!strcmp(dev, sbtn_pnp[i])) {
mouse_has_softbtn = 1;
break;
}
}
}
#endif
if (acpi_matchhids(&aaa, acpi_skip_hids, "none") ||
acpi_matchhids(&aaa, acpi_isa_hids, "none"))
return (0);
aaa.aaa_dmat = acpi_iommu_device_map(node->parent, aaa.aaa_dmat);
if (!node->parent->attached) {
node->parent->attached = 1;
config_found(self, &aaa, acpi_print);
}
return (0);
}
#ifndef SMALL_KERNEL
int
acpi_founddock(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
struct acpi_attach_args aaa;
dnprintf(10, "found dock entry: %s\n", node->parent->name);
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_node = node->parent;
aaa.aaa_name = "acpidock";
config_found(self, &aaa, acpi_print);
return 0;
}
int
acpi_foundvideo(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
struct acpi_attach_args aaa;
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_node = node->parent;
aaa.aaa_name = "acpivideo";
config_found(self, &aaa, acpi_print);
return (0);
}
int
acpi_foundsbs(struct aml_node *node, void *arg)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
struct device *self = (struct device *)arg;
char cdev[32], dev[32];
struct acpi_attach_args aaa;
int64_t sta;
if (acpi_parsehid(node, arg, cdev, dev, sizeof(dev)) != 0)
return (0);
sta = acpi_getsta(sc, node->parent);
if ((sta & STA_PRESENT) == 0)
return (0);
acpi_attach_deps(sc, node->parent);
if (strcmp(dev, ACPI_DEV_SBS) != 0)
return (0);
if (node->parent->attached)
return (0);
memset(&aaa, 0, sizeof(aaa));
aaa.aaa_iot = sc->sc_iot;
aaa.aaa_memt = sc->sc_memt;
aaa.aaa_node = node->parent;
aaa.aaa_dev = dev;
aaa.aaa_cdev = cdev;
config_found(self, &aaa, acpi_print);
node->parent->attached = 1;
return (0);
}
int
acpiopen(dev_t dev, int flag, int mode, struct proc *p)
{
int error = 0;
struct acpi_softc *sc;
int s;
if (!acpi_cd.cd_ndevs || APMUNIT(dev) != 0 ||
!(sc = acpi_cd.cd_devs[APMUNIT(dev)]))
return (ENXIO);
s = splbio();
switch (APMDEV(dev)) {
case APMDEV_CTL:
if (!(flag & FWRITE)) {
error = EINVAL;
break;
}
if (sc->sc_flags & SCFLAG_OWRITE) {
error = EBUSY;
break;
}
sc->sc_flags |= SCFLAG_OWRITE;
break;
case APMDEV_NORMAL:
if (!(flag & FREAD) || (flag & FWRITE)) {
error = EINVAL;
break;
}
sc->sc_flags |= SCFLAG_OREAD;
break;
default:
error = ENXIO;
break;
}
splx(s);
return (error);
}
int
acpiclose(dev_t dev, int flag, int mode, struct proc *p)
{
int error = 0;
struct acpi_softc *sc;
int s;
if (!acpi_cd.cd_ndevs || APMUNIT(dev) != 0 ||
!(sc = acpi_cd.cd_devs[APMUNIT(dev)]))
return (ENXIO);
s = splbio();
switch (APMDEV(dev)) {
case APMDEV_CTL:
sc->sc_flags &= ~SCFLAG_OWRITE;
break;
case APMDEV_NORMAL:
sc->sc_flags &= ~SCFLAG_OREAD;
break;
default:
error = ENXIO;
break;
}
splx(s);
return (error);
}
int
acpiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
int error = 0;
struct acpi_softc *sc;
struct acpi_ac *ac;
struct acpi_bat *bat;
struct acpi_sbs *sbs;
struct apm_power_info *pi = (struct apm_power_info *)data;
int bats;
unsigned int capacity, remaining, minutes, rate;
int s;
if (!acpi_cd.cd_ndevs || APMUNIT(dev) != 0 ||
!(sc = acpi_cd.cd_devs[APMUNIT(dev)]))
return (ENXIO);
s = splbio();
/* fake APM */
switch (cmd) {
case APM_IOC_SUSPEND:
case APM_IOC_STANDBY:
if ((flag & FWRITE) == 0) {
error = EBADF;
break;
}
acpi_addtask(sc, acpi_sleep_task, sc, SLEEP_SUSPEND);
acpi_wakeup(sc);
break;
#ifdef HIBERNATE
case APM_IOC_HIBERNATE:
if ((error = suser(p)) != 0)
break;
if ((flag & FWRITE) == 0) {
error = EBADF;
break;
}
if (get_hibernate_io_function(swdevt[0].sw_dev) == NULL) {
error = EOPNOTSUPP;
break;
}
acpi_addtask(sc, acpi_sleep_task, sc, SLEEP_HIBERNATE);
acpi_wakeup(sc);
break;
#endif
case APM_IOC_GETPOWER:
/* A/C */
pi->ac_state = APM_AC_UNKNOWN;
// XXX replace with new power code
SLIST_FOREACH(ac, &sc->sc_ac, aac_link) {
if (ac->aac_softc->sc_ac_stat == PSR_ONLINE)
pi->ac_state = APM_AC_ON;
else if (ac->aac_softc->sc_ac_stat == PSR_OFFLINE)
if (pi->ac_state == APM_AC_UNKNOWN)
pi->ac_state = APM_AC_OFF;
}
/* battery */
pi->battery_state = APM_BATT_UNKNOWN;
pi->battery_life = 0;
pi->minutes_left = 0;
bats = 0;
capacity = 0;
remaining = 0;
minutes = 0;
rate = 0;
SLIST_FOREACH(bat, &sc->sc_bat, aba_link) {
if (bat->aba_softc->sc_bat_present == 0)
continue;
if (bat->aba_softc->sc_bix.bix_last_capacity == 0)
continue;
bats++;
capacity += bat->aba_softc->sc_bix.bix_last_capacity;
remaining += min(bat->aba_softc->sc_bst.bst_capacity,
bat->aba_softc->sc_bix.bix_last_capacity);
if (bat->aba_softc->sc_bst.bst_state & BST_CHARGE)
pi->battery_state = APM_BATT_CHARGING;
if (bat->aba_softc->sc_bst.bst_rate == BST_UNKNOWN)
continue;
else if (bat->aba_softc->sc_bst.bst_rate > 1)
rate = bat->aba_softc->sc_bst.bst_rate;
minutes += bat->aba_softc->sc_bst.bst_capacity;
}
SLIST_FOREACH(sbs, &sc->sc_sbs, asbs_link) {
if (sbs->asbs_softc->sc_batteries_present == 0)
continue;
if (sbs->asbs_softc->sc_battery.rel_charge == 0)
continue;
bats++;
capacity += 100;
remaining += min(100,
sbs->asbs_softc->sc_battery.rel_charge);
if (sbs->asbs_softc->sc_battery.run_time ==
ACPISBS_VALUE_UNKNOWN)
continue;
rate = 60; /* XXX */
minutes += sbs->asbs_softc->sc_battery.run_time;
}
if (bats == 0) {
pi->battery_state = APM_BATTERY_ABSENT;
pi->battery_life = 0;
pi->minutes_left = (unsigned int)-1;
break;
}
if (rate == 0)
pi->minutes_left = (unsigned int)-1;
else if (pi->battery_state == APM_BATT_CHARGING)
pi->minutes_left = 60 * (capacity - remaining) / rate;
else
pi->minutes_left = 60 * minutes / rate;
pi->battery_life = remaining * 100 / capacity;
if (pi->battery_state == APM_BATT_CHARGING)
break;
/* running on battery */
if (pi->battery_life > 50)
pi->battery_state = APM_BATT_HIGH;
else if (pi->battery_life > 25)
pi->battery_state = APM_BATT_LOW;
else
pi->battery_state = APM_BATT_CRITICAL;
break;
default:
error = ENOTTY;
}
splx(s);
return (error);
}
void acpi_filtdetach(struct knote *);
int acpi_filtread(struct knote *, long);
const struct filterops acpiread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = acpi_filtdetach,
.f_event = acpi_filtread,
};
int acpi_evindex;
int
acpi_record_event(struct acpi_softc *sc, u_int type)
{
if ((sc->sc_flags & SCFLAG_OPEN) == 0)
return (1);
acpi_evindex++;
KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, acpi_evindex));
return (0);
}
void
acpi_filtdetach(struct knote *kn)
{
struct acpi_softc *sc = kn->kn_hook;
int s;
s = splbio();
klist_remove_locked(&sc->sc_note, kn);
splx(s);
}
int
acpi_filtread(struct knote *kn, long hint)
{
/* XXX weird kqueue_scan() semantics */
if (hint && !kn->kn_data)
kn->kn_data = hint;
return (1);
}
int
acpikqfilter(dev_t dev, struct knote *kn)
{
struct acpi_softc *sc;
int s;
if (!acpi_cd.cd_ndevs || APMUNIT(dev) != 0 ||
!(sc = acpi_cd.cd_devs[APMUNIT(dev)]))
return (ENXIO);
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &acpiread_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = sc;
s = splbio();
klist_insert_locked(&sc->sc_note, kn);
splx(s);
return (0);
}
#else /* SMALL_KERNEL */
int
acpiopen(dev_t dev, int flag, int mode, struct proc *p)
{
return (ENXIO);
}
int
acpiclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (ENXIO);
}
int
acpiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
return (ENXIO);
}
int
acpikqfilter(dev_t dev, struct knote *kn)
{
return (EOPNOTSUPP);
}
#endif /* SMALL_KERNEL */
862
2
2
1
853
3
1
4
853
47
809
19
35
25
12
7
8
10
5
20
1
19
18
18
11
1
1
6
5
3
6
3
1
3
3
5
2
2
8
24
22
2
24
22
2
863
3
1
1
53
1
807
1
52
3
155
145
13
9
1
1
2
1
3
38
50
1
1
3
1
1
1
2
1
1
22
2
2
2
1
1
2
1
1
1
2
1
2
1
1
2
87
3
1
45
39
15
26
40
1
22
22
22
21
1
3
18
2
13
7
11
9
7
10
6
2
11
18
5
380
5
379
2
16
368
364
2
4
18
378
377
18
1
5
13
6
18
18
1006
17
14
14
23
24
24
24
23
24
24
24
17
24
9
17
1
2
1
6
2
438
1008
6
6
2
1
3
2
1
1
1
1
7
86
2
51
45
39
3
2
37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
/* $OpenBSD: bpf.c,v 1.219 2022/07/09 12:48:21 visa Exp $ */
/* $NetBSD: bpf.c,v 1.33 1997/02/21 23:59:35 thorpej Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* Copyright (c) 2010, 2014 Henning Brauer <henning@openbsd.org>
*
* This code is derived from the Stanford/CMU enet packet filter,
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
* Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)bpf.c 8.2 (Berkeley) 3/28/94
*/
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/rwlock.h>
#include <sys/atomic.h>
#include <sys/event.h>
#include <sys/mutex.h>
#include <sys/refcnt.h>
#include <sys/smr.h>
#include <sys/specdev.h>
#include <sys/sigio.h>
#include <sys/task.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/bpf.h>
#include <net/bpfdesc.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include "vlan.h"
#if NVLAN > 0
#include <net/if_vlan_var.h>
#endif
#define BPF_BUFSIZE 32768
#define PRINET 26 /* interruptible */
/*
* The default read buffer size is patchable.
*/
int bpf_bufsize = BPF_BUFSIZE;
int bpf_maxbufsize = BPF_MAXBUFSIZE;
/*
* bpf_iflist is the list of interfaces; each corresponds to an ifnet
* bpf_d_list is the list of descriptors
*/
struct bpf_if *bpf_iflist;
LIST_HEAD(, bpf_d) bpf_d_list;
int bpf_allocbufs(struct bpf_d *);
void bpf_ifname(struct bpf_if*, struct ifreq *);
void bpf_mcopy(const void *, void *, size_t);
int bpf_movein(struct uio *, struct bpf_d *, struct mbuf **,
struct sockaddr *);
int bpf_setif(struct bpf_d *, struct ifreq *);
int bpfkqfilter(dev_t, struct knote *);
void bpf_wakeup(struct bpf_d *);
void bpf_wakeup_cb(void *);
int _bpf_mtap(caddr_t, const struct mbuf *, const struct mbuf *, u_int);
void bpf_catchpacket(struct bpf_d *, u_char *, size_t, size_t,
const struct bpf_hdr *);
int bpf_getdltlist(struct bpf_d *, struct bpf_dltlist *);
int bpf_setdlt(struct bpf_d *, u_int);
void filt_bpfrdetach(struct knote *);
int filt_bpfread(struct knote *, long);
int filt_bpfreadmodify(struct kevent *, struct knote *);
int filt_bpfreadprocess(struct knote *, struct kevent *);
int bpf_sysctl_locked(int *, u_int, void *, size_t *, void *, size_t);
struct bpf_d *bpfilter_lookup(int);
/*
* Called holding ``bd_mtx''.
*/
void bpf_attachd(struct bpf_d *, struct bpf_if *);
void bpf_detachd(struct bpf_d *);
void bpf_resetd(struct bpf_d *);
void bpf_prog_smr(void *);
void bpf_d_smr(void *);
/*
* Reference count access to descriptor buffers
*/
void bpf_get(struct bpf_d *);
void bpf_put(struct bpf_d *);
struct rwlock bpf_sysctl_lk = RWLOCK_INITIALIZER("bpfsz");
int
bpf_movein(struct uio *uio, struct bpf_d *d, struct mbuf **mp,
struct sockaddr *sockp)
{
struct bpf_program_smr *bps;
struct bpf_insn *fcode = NULL;
struct mbuf *m;
struct m_tag *mtag;
int error;
u_int hlen, alen, mlen;
u_int len;
u_int linktype;
u_int slen;
/*
* Build a sockaddr based on the data link layer type.
* We do this at this level because the ethernet header
* is copied directly into the data field of the sockaddr.
* In the case of SLIP, there is no header and the packet
* is forwarded as is.
* Also, we are careful to leave room at the front of the mbuf
* for the link level header.
*/
linktype = d->bd_bif->bif_dlt;
switch (linktype) {
case DLT_SLIP:
sockp->sa_family = AF_INET;
hlen = 0;
break;
case DLT_PPP:
sockp->sa_family = AF_UNSPEC;
hlen = 0;
break;
case DLT_EN10MB:
sockp->sa_family = AF_UNSPEC;
/* XXX Would MAXLINKHDR be better? */
hlen = ETHER_HDR_LEN;
break;
case DLT_IEEE802_11:
case DLT_IEEE802_11_RADIO:
sockp->sa_family = AF_UNSPEC;
hlen = 0;
break;
case DLT_RAW:
case DLT_NULL:
sockp->sa_family = AF_UNSPEC;
hlen = 0;
break;
case DLT_LOOP:
sockp->sa_family = AF_UNSPEC;
hlen = sizeof(u_int32_t);
break;
default:
return (EIO);
}
if (uio->uio_resid > MAXMCLBYTES)
return (EMSGSIZE);
len = uio->uio_resid;
if (len < hlen)
return (EINVAL);
/*
* Get the length of the payload so we can align it properly.
*/
alen = len - hlen;
/*
* Allocate enough space for headers and the aligned payload.
*/
mlen = max(max_linkhdr, hlen) + roundup(alen, sizeof(long));
if (mlen > MAXMCLBYTES)
return (EMSGSIZE);
MGETHDR(m, M_WAIT, MT_DATA);
if (mlen > MHLEN) {
MCLGETL(m, M_WAIT, mlen);
if ((m->m_flags & M_EXT) == 0) {
error = ENOBUFS;
goto bad;
}
}
m_align(m, alen); /* Align the payload. */
m->m_data -= hlen;
m->m_pkthdr.ph_ifidx = 0;
m->m_pkthdr.len = len;
m->m_len = len;
error = uiomove(mtod(m, caddr_t), len, uio);
if (error)
goto bad;
smr_read_enter();
bps = SMR_PTR_GET(&d->bd_wfilter);
if (bps != NULL)
fcode = bps->bps_bf.bf_insns;
slen = bpf_filter(fcode, mtod(m, u_char *), len, len);
smr_read_leave();
if (slen < len) {
error = EPERM;
goto bad;
}
/*
* Make room for link header, and copy it to sockaddr
*/
if (hlen != 0) {
if (linktype == DLT_LOOP) {
u_int32_t af;
/* the link header indicates the address family */
KASSERT(hlen == sizeof(u_int32_t));
memcpy(&af, m->m_data, hlen);
sockp->sa_family = ntohl(af);
} else
memcpy(sockp->sa_data, m->m_data, hlen);
m->m_pkthdr.len -= hlen;
m->m_len -= hlen;
m->m_data += hlen;
}
/*
* Prepend the data link type as a mbuf tag
*/
mtag = m_tag_get(PACKET_TAG_DLT, sizeof(u_int), M_WAIT);
*(u_int *)(mtag + 1) = linktype;
m_tag_prepend(m, mtag);
*mp = m;
return (0);
bad:
m_freem(m);
return (error);
}
/*
* Attach file to the bpf interface, i.e. make d listen on bp.
*/
void
bpf_attachd(struct bpf_d *d, struct bpf_if *bp)
{
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
/*
* Point d at bp, and add d to the interface's list of listeners.
* Finally, point the driver's bpf cookie at the interface so
* it will divert packets to bpf.
*/
d->bd_bif = bp;
KERNEL_ASSERT_LOCKED();
SMR_SLIST_INSERT_HEAD_LOCKED(&bp->bif_dlist, d, bd_next);
*bp->bif_driverp = bp;
}
/*
* Detach a file from its interface.
*/
void
bpf_detachd(struct bpf_d *d)
{
struct bpf_if *bp;
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
bp = d->bd_bif;
/* Not attached. */
if (bp == NULL)
return;
/* Remove ``d'' from the interface's descriptor list. */
KERNEL_ASSERT_LOCKED();
SMR_SLIST_REMOVE_LOCKED(&bp->bif_dlist, d, bpf_d, bd_next);
if (SMR_SLIST_EMPTY_LOCKED(&bp->bif_dlist)) {
/*
* Let the driver know that there are no more listeners.
*/
*bp->bif_driverp = NULL;
}
d->bd_bif = NULL;
/*
* Check if this descriptor had requested promiscuous mode.
* If so, turn it off.
*/
if (d->bd_promisc) {
int error;
KASSERT(bp->bif_ifp != NULL);
d->bd_promisc = 0;
bpf_get(d);
mtx_leave(&d->bd_mtx);
NET_LOCK();
error = ifpromisc(bp->bif_ifp, 0);
NET_UNLOCK();
mtx_enter(&d->bd_mtx);
bpf_put(d);
if (error && !(error == EINVAL || error == ENODEV ||
error == ENXIO))
/*
* Something is really wrong if we were able to put
* the driver into promiscuous mode, but can't
* take it out.
*/
panic("bpf: ifpromisc failed");
}
}
void
bpfilterattach(int n)
{
LIST_INIT(&bpf_d_list);
}
/*
* Open ethernet device. Returns ENXIO for illegal minor device number,
* EBUSY if file is open by another process.
*/
int
bpfopen(dev_t dev, int flag, int mode, struct proc *p)
{
struct bpf_d *bd;
int unit = minor(dev);
if (unit & ((1 << CLONE_SHIFT) - 1))
return (ENXIO);
KASSERT(bpfilter_lookup(unit) == NULL);
/* create on demand */
if ((bd = malloc(sizeof(*bd), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
return (EBUSY);
/* Mark "free" and do most initialization. */
bd->bd_unit = unit;
bd->bd_bufsize = bpf_bufsize;
bd->bd_sig = SIGIO;
mtx_init(&bd->bd_mtx, IPL_NET);
task_set(&bd->bd_wake_task, bpf_wakeup_cb, bd);
smr_init(&bd->bd_smr);
sigio_init(&bd->bd_sigio);
klist_init_mutex(&bd->bd_klist, &bd->bd_mtx);
bd->bd_rtout = 0; /* no timeout by default */
refcnt_init(&bd->bd_refcnt);
LIST_INSERT_HEAD(&bpf_d_list, bd, bd_list);
return (0);
}
/*
* Close the descriptor by detaching it from its interface,
* deallocating its buffers, and marking it free.
*/
int
bpfclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct bpf_d *d;
d = bpfilter_lookup(minor(dev));
mtx_enter(&d->bd_mtx);
bpf_detachd(d);
bpf_wakeup(d);
LIST_REMOVE(d, bd_list);
mtx_leave(&d->bd_mtx);
bpf_put(d);
return (0);
}
/*
* Rotate the packet buffers in descriptor d. Move the store buffer
* into the hold slot, and the free buffer into the store slot.
* Zero the length of the new store buffer.
*/
#define ROTATE_BUFFERS(d) \
KASSERT(d->bd_in_uiomove == 0); \
MUTEX_ASSERT_LOCKED(&d->bd_mtx); \
(d)->bd_hbuf = (d)->bd_sbuf; \
(d)->bd_hlen = (d)->bd_slen; \
(d)->bd_sbuf = (d)->bd_fbuf; \
(d)->bd_slen = 0; \
(d)->bd_fbuf = NULL;
/*
* bpfread - read next chunk of packets from buffers
*/
int
bpfread(dev_t dev, struct uio *uio, int ioflag)
{
uint64_t end, now;
struct bpf_d *d;
caddr_t hbuf;
int error, hlen;
KERNEL_ASSERT_LOCKED();
d = bpfilter_lookup(minor(dev));
if (d->bd_bif == NULL)
return (ENXIO);
bpf_get(d);
mtx_enter(&d->bd_mtx);
/*
* Restrict application to use a buffer the same size as
* as kernel buffers.
*/
if (uio->uio_resid != d->bd_bufsize) {
error = EINVAL;
goto out;
}
/*
* If there's a timeout, mark when the read should end.
*/
if (d->bd_rtout != 0) {
now = nsecuptime();
end = now + d->bd_rtout;
if (end < now)
end = UINT64_MAX;
}
/*
* If the hold buffer is empty, then do a timed sleep, which
* ends when the timeout expires or when enough packets
* have arrived to fill the store buffer.
*/
while (d->bd_hbuf == NULL) {
if (d->bd_bif == NULL) {
/* interface is gone */
if (d->bd_slen == 0) {
error = EIO;
goto out;
}
ROTATE_BUFFERS(d);
break;
}
if (d->bd_immediate && d->bd_slen != 0) {
/*
* A packet(s) either arrived since the previous
* read or arrived while we were asleep.
* Rotate the buffers and return what's here.
*/
ROTATE_BUFFERS(d);
break;
}
if (ISSET(ioflag, IO_NDELAY)) {
/* User requested non-blocking I/O */
error = EWOULDBLOCK;
} else if (d->bd_rtout == 0) {
/* No read timeout set. */
d->bd_nreaders++;
error = msleep_nsec(d, &d->bd_mtx, PRINET|PCATCH,
"bpf", INFSLP);
d->bd_nreaders--;
} else if ((now = nsecuptime()) < end) {
/* Read timeout has not expired yet. */
d->bd_nreaders++;
error = msleep_nsec(d, &d->bd_mtx, PRINET|PCATCH,
"bpf", end - now);
d->bd_nreaders--;
} else {
/* Read timeout has expired. */
error = EWOULDBLOCK;
}
if (error == EINTR || error == ERESTART)
goto out;
if (error == EWOULDBLOCK) {
/*
* On a timeout, return what's in the buffer,
* which may be nothing. If there is something
* in the store buffer, we can rotate the buffers.
*/
if (d->bd_hbuf != NULL)
/*
* We filled up the buffer in between
* getting the timeout and arriving
* here, so we don't need to rotate.
*/
break;
if (d->bd_slen == 0) {
error = 0;
goto out;
}
ROTATE_BUFFERS(d);
break;
}
}
/*
* At this point, we know we have something in the hold slot.
*/
hbuf = d->bd_hbuf;
hlen = d->bd_hlen;
d->bd_hbuf = NULL;
d->bd_hlen = 0;
d->bd_fbuf = NULL;
d->bd_in_uiomove = 1;
/*
* Move data from hold buffer into user space.
* We know the entire buffer is transferred since
* we checked above that the read buffer is bpf_bufsize bytes.
*/
mtx_leave(&d->bd_mtx);
error = uiomove(hbuf, hlen, uio);
mtx_enter(&d->bd_mtx);
/* Ensure that bpf_resetd() or ROTATE_BUFFERS() haven't been called. */
KASSERT(d->bd_fbuf == NULL);
KASSERT(d->bd_hbuf == NULL);
d->bd_fbuf = hbuf;
d->bd_in_uiomove = 0;
out:
mtx_leave(&d->bd_mtx);
bpf_put(d);
return (error);
}
/*
* If there are processes sleeping on this descriptor, wake them up.
*/
void
bpf_wakeup(struct bpf_d *d)
{
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
if (d->bd_nreaders)
wakeup(d);
KNOTE(&d->bd_klist, 0);
/*
* As long as pgsigio() needs to be protected
* by the KERNEL_LOCK() we have to delay the wakeup to
* another context to keep the hot path KERNEL_LOCK()-free.
*/
if (d->bd_async && d->bd_sig) {
bpf_get(d);
if (!task_add(systq, &d->bd_wake_task))
bpf_put(d);
}
}
void
bpf_wakeup_cb(void *xd)
{
struct bpf_d *d = xd;
if (d->bd_async && d->bd_sig)
pgsigio(&d->bd_sigio, d->bd_sig, 0);
bpf_put(d);
}
int
bpfwrite(dev_t dev, struct uio *uio, int ioflag)
{
struct bpf_d *d;
struct ifnet *ifp;
struct mbuf *m;
int error;
struct sockaddr_storage dst;
KERNEL_ASSERT_LOCKED();
d = bpfilter_lookup(minor(dev));
if (d->bd_bif == NULL)
return (ENXIO);
bpf_get(d);
ifp = d->bd_bif->bif_ifp;
if (ifp == NULL || (ifp->if_flags & IFF_UP) == 0) {
error = ENETDOWN;
goto out;
}
if (uio->uio_resid == 0) {
error = 0;
goto out;
}
error = bpf_movein(uio, d, &m, sstosa(&dst));
if (error)
goto out;
if (m->m_pkthdr.len > ifp->if_mtu) {
m_freem(m);
error = EMSGSIZE;
goto out;
}
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
m->m_pkthdr.pf.prio = ifp->if_llprio;
if (d->bd_hdrcmplt && dst.ss_family == AF_UNSPEC)
dst.ss_family = pseudo_AF_HDRCMPLT;
NET_LOCK();
error = ifp->if_output(ifp, m, sstosa(&dst), NULL);
NET_UNLOCK();
out:
bpf_put(d);
return (error);
}
/*
* Reset a descriptor by flushing its packet buffer and clearing the
* receive and drop counts.
*/
void
bpf_resetd(struct bpf_d *d)
{
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
KASSERT(d->bd_in_uiomove == 0);
if (d->bd_hbuf != NULL) {
/* Free the hold buffer. */
d->bd_fbuf = d->bd_hbuf;
d->bd_hbuf = NULL;
}
d->bd_slen = 0;
d->bd_hlen = 0;
d->bd_rcount = 0;
d->bd_dcount = 0;
}
/*
* FIONREAD Check for read packet available.
* BIOCGBLEN Get buffer len [for read()].
* BIOCSETF Set ethernet read filter.
* BIOCFLUSH Flush read packet buffer.
* BIOCPROMISC Put interface into promiscuous mode.
* BIOCGDLTLIST Get supported link layer types.
* BIOCGDLT Get link layer type.
* BIOCSDLT Set link layer type.
* BIOCGETIF Get interface name.
* BIOCSETIF Set interface.
* BIOCSRTIMEOUT Set read timeout.
* BIOCGRTIMEOUT Get read timeout.
* BIOCGSTATS Get packet stats.
* BIOCIMMEDIATE Set immediate mode.
* BIOCVERSION Get filter language version.
* BIOCGHDRCMPLT Get "header already complete" flag
* BIOCSHDRCMPLT Set "header already complete" flag
*/
int
bpfioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct bpf_d *d;
int error = 0;
d = bpfilter_lookup(minor(dev));
if (d->bd_locked && suser(p) != 0) {
/* list of allowed ioctls when locked and not root */
switch (cmd) {
case BIOCGBLEN:
case BIOCFLUSH:
case BIOCGDLT:
case BIOCGDLTLIST:
case BIOCGETIF:
case BIOCGRTIMEOUT:
case BIOCGSTATS:
case BIOCVERSION:
case BIOCGRSIG:
case BIOCGHDRCMPLT:
case FIONREAD:
case BIOCLOCK:
case BIOCSRTIMEOUT:
case BIOCIMMEDIATE:
case TIOCGPGRP:
case BIOCGDIRFILT:
break;
default:
return (EPERM);
}
}
bpf_get(d);
switch (cmd) {
default:
error = EINVAL;
break;
/*
* Check for read packet available.
*/
case FIONREAD:
{
int n;
mtx_enter(&d->bd_mtx);
n = d->bd_slen;
if (d->bd_hbuf != NULL)
n += d->bd_hlen;
mtx_leave(&d->bd_mtx);
*(int *)addr = n;
break;
}
/*
* Get buffer len [for read()].
*/
case BIOCGBLEN:
*(u_int *)addr = d->bd_bufsize;
break;
/*
* Set buffer length.
*/
case BIOCSBLEN:
if (d->bd_bif != NULL)
error = EINVAL;
else {
u_int size = *(u_int *)addr;
if (size > bpf_maxbufsize)
*(u_int *)addr = size = bpf_maxbufsize;
else if (size < BPF_MINBUFSIZE)
*(u_int *)addr = size = BPF_MINBUFSIZE;
mtx_enter(&d->bd_mtx);
d->bd_bufsize = size;
mtx_leave(&d->bd_mtx);
}
break;
/*
* Set link layer read filter.
*/
case BIOCSETF:
error = bpf_setf(d, (struct bpf_program *)addr, 0);
break;
/*
* Set link layer write filter.
*/
case BIOCSETWF:
error = bpf_setf(d, (struct bpf_program *)addr, 1);
break;
/*
* Flush read packet buffer.
*/
case BIOCFLUSH:
mtx_enter(&d->bd_mtx);
bpf_resetd(d);
mtx_leave(&d->bd_mtx);
break;
/*
* Put interface into promiscuous mode.
*/
case BIOCPROMISC:
if (d->bd_bif == NULL) {
/*
* No interface attached yet.
*/
error = EINVAL;
} else if (d->bd_bif->bif_ifp != NULL) {
if (d->bd_promisc == 0) {
MUTEX_ASSERT_UNLOCKED(&d->bd_mtx);
NET_LOCK();
error = ifpromisc(d->bd_bif->bif_ifp, 1);
NET_UNLOCK();
if (error == 0)
d->bd_promisc = 1;
}
}
break;
/*
* Get a list of supported device parameters.
*/
case BIOCGDLTLIST:
if (d->bd_bif == NULL)
error = EINVAL;
else
error = bpf_getdltlist(d, (struct bpf_dltlist *)addr);
break;
/*
* Get device parameters.
*/
case BIOCGDLT:
if (d->bd_bif == NULL)
error = EINVAL;
else
*(u_int *)addr = d->bd_bif->bif_dlt;
break;
/*
* Set device parameters.
*/
case BIOCSDLT:
if (d->bd_bif == NULL)
error = EINVAL;
else {
mtx_enter(&d->bd_mtx);
error = bpf_setdlt(d, *(u_int *)addr);
mtx_leave(&d->bd_mtx);
}
break;
/*
* Set interface name.
*/
case BIOCGETIF:
if (d->bd_bif == NULL)
error = EINVAL;
else
bpf_ifname(d->bd_bif, (struct ifreq *)addr);
break;
/*
* Set interface.
*/
case BIOCSETIF:
error = bpf_setif(d, (struct ifreq *)addr);
break;
/*
* Set read timeout.
*/
case BIOCSRTIMEOUT:
{
struct timeval *tv = (struct timeval *)addr;
uint64_t rtout;
if (tv->tv_sec < 0 || !timerisvalid(tv)) {
error = EINVAL;
break;
}
rtout = TIMEVAL_TO_NSEC(tv);
if (rtout > MAXTSLP) {
error = EOVERFLOW;
break;
}
mtx_enter(&d->bd_mtx);
d->bd_rtout = rtout;
mtx_leave(&d->bd_mtx);
break;
}
/*
* Get read timeout.
*/
case BIOCGRTIMEOUT:
{
struct timeval *tv = (struct timeval *)addr;
memset(tv, 0, sizeof(*tv));
mtx_enter(&d->bd_mtx);
NSEC_TO_TIMEVAL(d->bd_rtout, tv);
mtx_leave(&d->bd_mtx);
break;
}
/*
* Get packet stats.
*/
case BIOCGSTATS:
{
struct bpf_stat *bs = (struct bpf_stat *)addr;
bs->bs_recv = d->bd_rcount;
bs->bs_drop = d->bd_dcount;
break;
}
/*
* Set immediate mode.
*/
case BIOCIMMEDIATE:
d->bd_immediate = *(u_int *)addr;
break;
case BIOCVERSION:
{
struct bpf_version *bv = (struct bpf_version *)addr;
bv->bv_major = BPF_MAJOR_VERSION;
bv->bv_minor = BPF_MINOR_VERSION;
break;
}
case BIOCGHDRCMPLT: /* get "header already complete" flag */
*(u_int *)addr = d->bd_hdrcmplt;
break;
case BIOCSHDRCMPLT: /* set "header already complete" flag */
d->bd_hdrcmplt = *(u_int *)addr ? 1 : 0;
break;
case BIOCLOCK: /* set "locked" flag (no reset) */
d->bd_locked = 1;
break;
case BIOCGFILDROP: /* get "filter-drop" flag */
*(u_int *)addr = d->bd_fildrop;
break;
case BIOCSFILDROP: { /* set "filter-drop" flag */
unsigned int fildrop = *(u_int *)addr;
switch (fildrop) {
case BPF_FILDROP_PASS:
case BPF_FILDROP_CAPTURE:
case BPF_FILDROP_DROP:
d->bd_fildrop = fildrop;
break;
default:
error = EINVAL;
break;
}
break;
}
case BIOCGDIRFILT: /* get direction filter */
*(u_int *)addr = d->bd_dirfilt;
break;
case BIOCSDIRFILT: /* set direction filter */
d->bd_dirfilt = (*(u_int *)addr) &
(BPF_DIRECTION_IN|BPF_DIRECTION_OUT);
break;
case FIONBIO: /* Non-blocking I/O */
/* let vfs to keep track of this */
break;
case FIOASYNC: /* Send signal on receive packets */
d->bd_async = *(int *)addr;
break;
case FIOSETOWN: /* Process or group to send signals to */
case TIOCSPGRP:
error = sigio_setown(&d->bd_sigio, cmd, addr);
break;
case FIOGETOWN:
case TIOCGPGRP:
sigio_getown(&d->bd_sigio, cmd, addr);
break;
case BIOCSRSIG: /* Set receive signal */
{
u_int sig;
sig = *(u_int *)addr;
if (sig >= NSIG)
error = EINVAL;
else
d->bd_sig = sig;
break;
}
case BIOCGRSIG:
*(u_int *)addr = d->bd_sig;
break;
}
bpf_put(d);
return (error);
}
/*
* Set d's packet filter program to fp. If this file already has a filter,
* free it and replace it. Returns EINVAL for bogus requests.
*/
int
bpf_setf(struct bpf_d *d, struct bpf_program *fp, int wf)
{
struct bpf_program_smr *bps, *old_bps;
struct bpf_insn *fcode;
u_int flen, size;
KERNEL_ASSERT_LOCKED();
if (fp->bf_insns == 0) {
if (fp->bf_len != 0)
return (EINVAL);
bps = NULL;
} else {
flen = fp->bf_len;
if (flen > BPF_MAXINSNS)
return (EINVAL);
fcode = mallocarray(flen, sizeof(*fp->bf_insns), M_DEVBUF,
M_WAITOK | M_CANFAIL);
if (fcode == NULL)
return (ENOMEM);
size = flen * sizeof(*fp->bf_insns);
if (copyin(fp->bf_insns, fcode, size) != 0 ||
bpf_validate(fcode, (int)flen) == 0) {
free(fcode, M_DEVBUF, size);
return (EINVAL);
}
bps = malloc(sizeof(*bps), M_DEVBUF, M_WAITOK);
smr_init(&bps->bps_smr);
bps->bps_bf.bf_len = flen;
bps->bps_bf.bf_insns = fcode;
}
if (wf == 0) {
old_bps = SMR_PTR_GET_LOCKED(&d->bd_rfilter);
SMR_PTR_SET_LOCKED(&d->bd_rfilter, bps);
} else {
old_bps = SMR_PTR_GET_LOCKED(&d->bd_wfilter);
SMR_PTR_SET_LOCKED(&d->bd_wfilter, bps);
}
mtx_enter(&d->bd_mtx);
bpf_resetd(d);
mtx_leave(&d->bd_mtx);
if (old_bps != NULL)
smr_call(&old_bps->bps_smr, bpf_prog_smr, old_bps);
return (0);
}
/*
* Detach a file from its current interface (if attached at all) and attach
* to the interface indicated by the name stored in ifr.
* Return an errno or 0.
*/
int
bpf_setif(struct bpf_d *d, struct ifreq *ifr)
{
struct bpf_if *bp, *candidate = NULL;
int error = 0;
/*
* Look through attached interfaces for the named one.
*/
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (strcmp(bp->bif_name, ifr->ifr_name) != 0)
continue;
if (candidate == NULL || candidate->bif_dlt > bp->bif_dlt)
candidate = bp;
}
/* Not found. */
if (candidate == NULL)
return (ENXIO);
/*
* Allocate the packet buffers if we need to.
* If we're already attached to requested interface,
* just flush the buffer.
*/
mtx_enter(&d->bd_mtx);
if (d->bd_sbuf == NULL) {
if ((error = bpf_allocbufs(d)))
goto out;
}
if (candidate != d->bd_bif) {
/*
* Detach if attached to something else.
*/
bpf_detachd(d);
bpf_attachd(d, candidate);
}
bpf_resetd(d);
out:
mtx_leave(&d->bd_mtx);
return (error);
}
/*
* Copy the interface name to the ifreq.
*/
void
bpf_ifname(struct bpf_if *bif, struct ifreq *ifr)
{
bcopy(bif->bif_name, ifr->ifr_name, sizeof(ifr->ifr_name));
}
const struct filterops bpfread_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_bpfrdetach,
.f_event = filt_bpfread,
.f_modify = filt_bpfreadmodify,
.f_process = filt_bpfreadprocess,
};
int
bpfkqfilter(dev_t dev, struct knote *kn)
{
struct bpf_d *d;
struct klist *klist;
KERNEL_ASSERT_LOCKED();
d = bpfilter_lookup(minor(dev));
if (d == NULL)
return (ENXIO);
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &d->bd_klist;
kn->kn_fop = &bpfread_filtops;
break;
default:
return (EINVAL);
}
bpf_get(d);
kn->kn_hook = d;
klist_insert(klist, kn);
return (0);
}
void
filt_bpfrdetach(struct knote *kn)
{
struct bpf_d *d = kn->kn_hook;
klist_remove(&d->bd_klist, kn);
bpf_put(d);
}
int
filt_bpfread(struct knote *kn, long hint)
{
struct bpf_d *d = kn->kn_hook;
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
kn->kn_data = d->bd_hlen;
if (d->bd_immediate)
kn->kn_data += d->bd_slen;
return (kn->kn_data > 0);
}
int
filt_bpfreadmodify(struct kevent *kev, struct knote *kn)
{
struct bpf_d *d = kn->kn_hook;
int active;
mtx_enter(&d->bd_mtx);
active = knote_modify_fn(kev, kn, filt_bpfread);
mtx_leave(&d->bd_mtx);
return (active);
}
int
filt_bpfreadprocess(struct knote *kn, struct kevent *kev)
{
struct bpf_d *d = kn->kn_hook;
int active;
mtx_enter(&d->bd_mtx);
active = knote_process_fn(kn, kev, filt_bpfread);
mtx_leave(&d->bd_mtx);
return (active);
}
/*
* Copy data from an mbuf chain into a buffer. This code is derived
* from m_copydata in sys/uipc_mbuf.c.
*/
void
bpf_mcopy(const void *src_arg, void *dst_arg, size_t len)
{
const struct mbuf *m;
u_int count;
u_char *dst;
m = src_arg;
dst = dst_arg;
while (len > 0) {
if (m == NULL)
panic("bpf_mcopy");
count = min(m->m_len, len);
bcopy(mtod(m, caddr_t), (caddr_t)dst, count);
m = m->m_next;
dst += count;
len -= count;
}
}
int
bpf_mtap(caddr_t arg, const struct mbuf *m, u_int direction)
{
return _bpf_mtap(arg, m, m, direction);
}
int
_bpf_mtap(caddr_t arg, const struct mbuf *mp, const struct mbuf *m,
u_int direction)
{
struct bpf_if *bp = (struct bpf_if *)arg;
struct bpf_d *d;
size_t pktlen, slen;
const struct mbuf *m0;
struct bpf_hdr tbh;
int gothdr = 0;
int drop = 0;
if (m == NULL)
return (0);
if (bp == NULL)
return (0);
pktlen = 0;
for (m0 = m; m0 != NULL; m0 = m0->m_next)
pktlen += m0->m_len;
smr_read_enter();
SMR_SLIST_FOREACH(d, &bp->bif_dlist, bd_next) {
struct bpf_program_smr *bps;
struct bpf_insn *fcode = NULL;
atomic_inc_long(&d->bd_rcount);
if (ISSET(d->bd_dirfilt, direction))
continue;
bps = SMR_PTR_GET(&d->bd_rfilter);
if (bps != NULL)
fcode = bps->bps_bf.bf_insns;
slen = bpf_mfilter(fcode, m, pktlen);
if (slen == 0)
continue;
if (d->bd_fildrop != BPF_FILDROP_PASS)
drop = 1;
if (d->bd_fildrop != BPF_FILDROP_DROP) {
if (!gothdr) {
struct timeval tv;
memset(&tbh, 0, sizeof(tbh));
if (ISSET(mp->m_flags, M_PKTHDR)) {
tbh.bh_ifidx = mp->m_pkthdr.ph_ifidx;
tbh.bh_flowid = mp->m_pkthdr.ph_flowid;
tbh.bh_flags = mp->m_pkthdr.pf.prio;
if (ISSET(mp->m_pkthdr.csum_flags,
M_FLOWID))
SET(tbh.bh_flags, BPF_F_FLOWID);
m_microtime(mp, &tv);
} else
microtime(&tv);
tbh.bh_tstamp.tv_sec = tv.tv_sec;
tbh.bh_tstamp.tv_usec = tv.tv_usec;
SET(tbh.bh_flags, direction << BPF_F_DIR_SHIFT);
gothdr = 1;
}
mtx_enter(&d->bd_mtx);
bpf_catchpacket(d, (u_char *)m, pktlen, slen, &tbh);
mtx_leave(&d->bd_mtx);
}
}
smr_read_leave();
return (drop);
}
/*
* Incoming linkage from device drivers, where a data buffer should be
* prepended by an arbitrary header. In this situation we already have a
* way of representing a chain of memory buffers, ie, mbufs, so reuse
* the existing functionality by attaching the buffers to mbufs.
*
* Con up a minimal mbuf chain to pacify bpf by allocating (only) a
* struct m_hdr each for the header and data on the stack.
*/
int
bpf_tap_hdr(caddr_t arg, const void *hdr, unsigned int hdrlen,
const void *buf, unsigned int buflen, u_int direction)
{
struct m_hdr mh, md;
struct mbuf *m0 = NULL;
struct mbuf **mp = &m0;
if (hdr != NULL) {
mh.mh_flags = 0;
mh.mh_next = NULL;
mh.mh_len = hdrlen;
mh.mh_data = (void *)hdr;
*mp = (struct mbuf *)&mh;
mp = &mh.mh_next;
}
if (buf != NULL) {
md.mh_flags = 0;
md.mh_next = NULL;
md.mh_len = buflen;
md.mh_data = (void *)buf;
*mp = (struct mbuf *)&md;
}
return bpf_mtap(arg, m0, direction);
}
/*
* Incoming linkage from device drivers, where we have a mbuf chain
* but need to prepend some arbitrary header from a linear buffer.
*
* Con up a minimal dummy header to pacify bpf. Allocate (only) a
* struct m_hdr on the stack. This is safe as bpf only reads from the
* fields in this header that we initialize, and will not try to free
* it or keep a pointer to it.
*/
int
bpf_mtap_hdr(caddr_t arg, const void *data, u_int dlen, const struct mbuf *m,
u_int direction)
{
struct m_hdr mh;
const struct mbuf *m0;
if (dlen > 0) {
mh.mh_flags = 0;
mh.mh_next = (struct mbuf *)m;
mh.mh_len = dlen;
mh.mh_data = (void *)data;
m0 = (struct mbuf *)&mh;
} else
m0 = m;
return _bpf_mtap(arg, m, m0, direction);
}
/*
* Incoming linkage from device drivers, where we have a mbuf chain
* but need to prepend the address family.
*
* Con up a minimal dummy header to pacify bpf. We allocate (only) a
* struct m_hdr on the stack. This is safe as bpf only reads from the
* fields in this header that we initialize, and will not try to free
* it or keep a pointer to it.
*/
int
bpf_mtap_af(caddr_t arg, u_int32_t af, const struct mbuf *m, u_int direction)
{
u_int32_t afh;
afh = htonl(af);
return bpf_mtap_hdr(arg, &afh, sizeof(afh), m, direction);
}
/*
* Incoming linkage from device drivers, where we have a mbuf chain
* but need to prepend a VLAN encapsulation header.
*
* Con up a minimal dummy header to pacify bpf. Allocate (only) a
* struct m_hdr on the stack. This is safe as bpf only reads from the
* fields in this header that we initialize, and will not try to free
* it or keep a pointer to it.
*/
int
bpf_mtap_ether(caddr_t arg, const struct mbuf *m, u_int direction)
{
#if NVLAN > 0
struct ether_vlan_header evh;
struct m_hdr mh, md;
if ((m->m_flags & M_VLANTAG) == 0)
#endif
{
return _bpf_mtap(arg, m, m, direction);
}
#if NVLAN > 0
KASSERT(m->m_len >= ETHER_HDR_LEN);
memcpy(&evh, mtod(m, char *), ETHER_HDR_LEN);
evh.evl_proto = evh.evl_encap_proto;
evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
evh.evl_tag = htons(m->m_pkthdr.ether_vtag);
mh.mh_flags = 0;
mh.mh_data = (caddr_t)&evh;
mh.mh_len = sizeof(evh);
mh.mh_next = (struct mbuf *)&md;
md.mh_flags = 0;
md.mh_data = m->m_data + ETHER_HDR_LEN;
md.mh_len = m->m_len - ETHER_HDR_LEN;
md.mh_next = m->m_next;
return _bpf_mtap(arg, m, (struct mbuf *)&mh, direction);
#endif
}
/*
* Move the packet data from interface memory (pkt) into the
* store buffer. Wake up listeners if needed.
* "copy" is the routine called to do the actual data
* transfer. bcopy is passed in to copy contiguous chunks, while
* bpf_mcopy is passed in to copy mbuf chains. In the latter case,
* pkt is really an mbuf.
*/
void
bpf_catchpacket(struct bpf_d *d, u_char *pkt, size_t pktlen, size_t snaplen,
const struct bpf_hdr *tbh)
{
struct bpf_hdr *bh;
int totlen, curlen;
int hdrlen, do_wakeup = 0;
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
if (d->bd_bif == NULL)
return;
hdrlen = d->bd_bif->bif_hdrlen;
/*
* Figure out how many bytes to move. If the packet is
* greater or equal to the snapshot length, transfer that
* much. Otherwise, transfer the whole packet (unless
* we hit the buffer size limit).
*/
totlen = hdrlen + min(snaplen, pktlen);
if (totlen > d->bd_bufsize)
totlen = d->bd_bufsize;
/*
* Round up the end of the previous packet to the next longword.
*/
curlen = BPF_WORDALIGN(d->bd_slen);
if (curlen + totlen > d->bd_bufsize) {
/*
* This packet will overflow the storage buffer.
* Rotate the buffers if we can, then wakeup any
* pending reads.
*/
if (d->bd_fbuf == NULL) {
/*
* We haven't completed the previous read yet,
* so drop the packet.
*/
++d->bd_dcount;
return;
}
ROTATE_BUFFERS(d);
do_wakeup = 1;
curlen = 0;
}
/*
* Append the bpf header.
*/
bh = (struct bpf_hdr *)(d->bd_sbuf + curlen);
*bh = *tbh;
bh->bh_datalen = pktlen;
bh->bh_hdrlen = hdrlen;
bh->bh_caplen = totlen - hdrlen;
/*
* Copy the packet data into the store buffer and update its length.
*/
bpf_mcopy(pkt, (u_char *)bh + hdrlen, bh->bh_caplen);
d->bd_slen = curlen + totlen;
if (d->bd_immediate) {
/*
* Immediate mode is set. A packet arrived so any
* reads should be woken up.
*/
do_wakeup = 1;
}
if (do_wakeup)
bpf_wakeup(d);
}
/*
* Initialize all nonzero fields of a descriptor.
*/
int
bpf_allocbufs(struct bpf_d *d)
{
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
d->bd_fbuf = malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT);
if (d->bd_fbuf == NULL)
return (ENOMEM);
d->bd_sbuf = malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT);
if (d->bd_sbuf == NULL) {
free(d->bd_fbuf, M_DEVBUF, d->bd_bufsize);
d->bd_fbuf = NULL;
return (ENOMEM);
}
d->bd_slen = 0;
d->bd_hlen = 0;
return (0);
}
void
bpf_prog_smr(void *bps_arg)
{
struct bpf_program_smr *bps = bps_arg;
free(bps->bps_bf.bf_insns, M_DEVBUF,
bps->bps_bf.bf_len * sizeof(struct bpf_insn));
free(bps, M_DEVBUF, sizeof(struct bpf_program_smr));
}
void
bpf_d_smr(void *smr)
{
struct bpf_d *bd = smr;
sigio_free(&bd->bd_sigio);
free(bd->bd_sbuf, M_DEVBUF, bd->bd_bufsize);
free(bd->bd_hbuf, M_DEVBUF, bd->bd_bufsize);
free(bd->bd_fbuf, M_DEVBUF, bd->bd_bufsize);
if (bd->bd_rfilter != NULL)
bpf_prog_smr(bd->bd_rfilter);
if (bd->bd_wfilter != NULL)
bpf_prog_smr(bd->bd_wfilter);
klist_free(&bd->bd_klist);
free(bd, M_DEVBUF, sizeof(*bd));
}
void
bpf_get(struct bpf_d *bd)
{
refcnt_take(&bd->bd_refcnt);
}
/*
* Free buffers currently in use by a descriptor
* when the reference count drops to zero.
*/
void
bpf_put(struct bpf_d *bd)
{
if (refcnt_rele(&bd->bd_refcnt) == 0)
return;
smr_call(&bd->bd_smr, bpf_d_smr, bd);
}
void *
bpfsattach(caddr_t *bpfp, const char *name, u_int dlt, u_int hdrlen)
{
struct bpf_if *bp;
if ((bp = malloc(sizeof(*bp), M_DEVBUF, M_NOWAIT)) == NULL)
panic("bpfattach");
SMR_SLIST_INIT(&bp->bif_dlist);
bp->bif_driverp = (struct bpf_if **)bpfp;
bp->bif_name = name;
bp->bif_ifp = NULL;
bp->bif_dlt = dlt;
bp->bif_next = bpf_iflist;
bpf_iflist = bp;
*bp->bif_driverp = NULL;
/*
* Compute the length of the bpf header. This is not necessarily
* equal to SIZEOF_BPF_HDR because we want to insert spacing such
* that the network layer header begins on a longword boundary (for
* performance reasons and to alleviate alignment restrictions).
*/
bp->bif_hdrlen = BPF_WORDALIGN(hdrlen + SIZEOF_BPF_HDR) - hdrlen;
return (bp);
}
void
bpfattach(caddr_t *driverp, struct ifnet *ifp, u_int dlt, u_int hdrlen)
{
struct bpf_if *bp;
bp = bpfsattach(driverp, ifp->if_xname, dlt, hdrlen);
bp->bif_ifp = ifp;
}
/* Detach an interface from its attached bpf device. */
void
bpfdetach(struct ifnet *ifp)
{
struct bpf_if *bp, *nbp;
KERNEL_ASSERT_LOCKED();
for (bp = bpf_iflist; bp; bp = nbp) {
nbp = bp->bif_next;
if (bp->bif_ifp == ifp)
bpfsdetach(bp);
}
ifp->if_bpf = NULL;
}
void
bpfsdetach(void *p)
{
struct bpf_if *bp = p, *tbp;
struct bpf_d *bd;
int maj;
KERNEL_ASSERT_LOCKED();
/* Locate the major number. */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == bpfopen)
break;
while ((bd = SMR_SLIST_FIRST_LOCKED(&bp->bif_dlist))) {
vdevgone(maj, bd->bd_unit, bd->bd_unit, VCHR);
klist_invalidate(&bd->bd_klist);
}
for (tbp = bpf_iflist; tbp; tbp = tbp->bif_next) {
if (tbp->bif_next == bp) {
tbp->bif_next = bp->bif_next;
break;
}
}
if (bpf_iflist == bp)
bpf_iflist = bp->bif_next;
free(bp, M_DEVBUF, sizeof(*bp));
}
int
bpf_sysctl_locked(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
switch (name[0]) {
case NET_BPF_BUFSIZE:
return sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&bpf_bufsize, BPF_MINBUFSIZE, bpf_maxbufsize);
case NET_BPF_MAXBUFSIZE:
return sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&bpf_maxbufsize, BPF_MINBUFSIZE, INT_MAX);
default:
return (EOPNOTSUPP);
}
}
int
bpf_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int flags = RW_INTR;
int error;
if (namelen != 1)
return (ENOTDIR);
flags |= (newp == NULL) ? RW_READ : RW_WRITE;
error = rw_enter(&bpf_sysctl_lk, flags);
if (error != 0)
return (error);
error = bpf_sysctl_locked(name, namelen, oldp, oldlenp, newp, newlen);
rw_exit(&bpf_sysctl_lk);
return (error);
}
struct bpf_d *
bpfilter_lookup(int unit)
{
struct bpf_d *bd;
KERNEL_ASSERT_LOCKED();
LIST_FOREACH(bd, &bpf_d_list, bd_list)
if (bd->bd_unit == unit)
return (bd);
return (NULL);
}
/*
* Get a list of available data link type of the interface.
*/
int
bpf_getdltlist(struct bpf_d *d, struct bpf_dltlist *bfl)
{
int n, error;
struct bpf_if *bp;
const char *name;
name = d->bd_bif->bif_name;
n = 0;
error = 0;
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (strcmp(name, bp->bif_name) != 0)
continue;
if (bfl->bfl_list != NULL) {
if (n >= bfl->bfl_len)
return (ENOMEM);
error = copyout(&bp->bif_dlt,
bfl->bfl_list + n, sizeof(u_int));
if (error)
break;
}
n++;
}
bfl->bfl_len = n;
return (error);
}
/*
* Set the data link type of a BPF instance.
*/
int
bpf_setdlt(struct bpf_d *d, u_int dlt)
{
const char *name;
struct bpf_if *bp;
MUTEX_ASSERT_LOCKED(&d->bd_mtx);
if (d->bd_bif->bif_dlt == dlt)
return (0);
name = d->bd_bif->bif_name;
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (strcmp(name, bp->bif_name) != 0)
continue;
if (bp->bif_dlt == dlt)
break;
}
if (bp == NULL)
return (EINVAL);
bpf_detachd(d);
bpf_attachd(d, bp);
bpf_resetd(d);
return (0);
}
u_int32_t bpf_mbuf_ldw(const void *, u_int32_t, int *);
u_int32_t bpf_mbuf_ldh(const void *, u_int32_t, int *);
u_int32_t bpf_mbuf_ldb(const void *, u_int32_t, int *);
int bpf_mbuf_copy(const struct mbuf *, u_int32_t,
void *, u_int32_t);
const struct bpf_ops bpf_mbuf_ops = {
bpf_mbuf_ldw,
bpf_mbuf_ldh,
bpf_mbuf_ldb,
};
int
bpf_mbuf_copy(const struct mbuf *m, u_int32_t off, void *buf, u_int32_t len)
{
u_int8_t *cp = buf;
u_int32_t count;
while (off >= m->m_len) {
off -= m->m_len;
m = m->m_next;
if (m == NULL)
return (-1);
}
for (;;) {
count = min(m->m_len - off, len);
memcpy(cp, m->m_data + off, count);
len -= count;
if (len == 0)
return (0);
m = m->m_next;
if (m == NULL)
break;
cp += count;
off = 0;
}
return (-1);
}
u_int32_t
bpf_mbuf_ldw(const void *m0, u_int32_t k, int *err)
{
u_int32_t v;
if (bpf_mbuf_copy(m0, k, &v, sizeof(v)) != 0) {
*err = 1;
return (0);
}
*err = 0;
return ntohl(v);
}
u_int32_t
bpf_mbuf_ldh(const void *m0, u_int32_t k, int *err)
{
u_int16_t v;
if (bpf_mbuf_copy(m0, k, &v, sizeof(v)) != 0) {
*err = 1;
return (0);
}
*err = 0;
return ntohs(v);
}
u_int32_t
bpf_mbuf_ldb(const void *m0, u_int32_t k, int *err)
{
const struct mbuf *m = m0;
u_int8_t v;
while (k >= m->m_len) {
k -= m->m_len;
m = m->m_next;
if (m == NULL) {
*err = 1;
return (0);
}
}
v = m->m_data[k];
*err = 0;
return v;
}
u_int
bpf_mfilter(const struct bpf_insn *pc, const struct mbuf *m, u_int wirelen)
{
return _bpf_filter(pc, &bpf_mbuf_ops, m, wirelen);
}
10
3467
2251
3443
3
3
3
3
6132
10
6120
6123
6128
5735
1698
6128
6126
6121
6130
6132
394
5857
6102
1201
6098
4876
4871
4876
4873
11
4866
4881
4869
251
4724
4879
80
4870
4763
2277
4808
176
72
177
173
177
176
175
5
174
172
15
15
2
13
5
10
177
122
72
27
155
5
2899
1109
19
16
2
1
4
10
3
4
148
5
27
18
9
10
10
7
6
1
6278
6286
6074
6085
191
191
121
122
106
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
/* $OpenBSD: subr_pool.c,v 1.236 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: subr_pool.c,v 1.61 2001/09/26 07:14:56 chs Exp $ */
/*-
* Copyright (c) 1997, 1999, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Paul Kranenburg; by Jason R. Thorpe of the Numerical Aerospace
* Simulation Facility, NASA Ames Research Center.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/task.h>
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/percpu.h>
#include <sys/tracepoint.h>
#include <uvm/uvm_extern.h>
/*
* Pool resource management utility.
*
* Memory is allocated in pages which are split into pieces according to
* the pool item size. Each page is kept on one of three lists in the
* pool structure: `pr_emptypages', `pr_fullpages' and `pr_partpages',
* for empty, full and partially-full pages respectively. The individual
* pool items are on a linked list headed by `ph_items' in each page
* header. The memory for building the page list is either taken from
* the allocated pages themselves (for small pool items) or taken from
* an internal pool of page headers (`phpool').
*/
/* List of all pools */
SIMPLEQ_HEAD(,pool) pool_head = SIMPLEQ_HEAD_INITIALIZER(pool_head);
/*
* Every pool gets a unique serial number assigned to it. If this counter
* wraps, we're screwed, but we shouldn't create so many pools anyway.
*/
unsigned int pool_serial;
unsigned int pool_count;
/* Lock the previous variables making up the global pool state */
struct rwlock pool_lock = RWLOCK_INITIALIZER("pools");
/* Private pool for page header structures */
struct pool phpool;
struct pool_lock_ops {
void (*pl_init)(struct pool *, union pool_lock *,
const struct lock_type *);
void (*pl_enter)(union pool_lock *);
int (*pl_enter_try)(union pool_lock *);
void (*pl_leave)(union pool_lock *);
void (*pl_assert_locked)(union pool_lock *);
void (*pl_assert_unlocked)(union pool_lock *);
int (*pl_sleep)(void *, union pool_lock *, int, const char *);
};
static const struct pool_lock_ops pool_lock_ops_mtx;
static const struct pool_lock_ops pool_lock_ops_rw;
#ifdef WITNESS
#define pl_init(pp, pl) do { \
static const struct lock_type __lock_type = { .lt_name = #pl }; \
(pp)->pr_lock_ops->pl_init(pp, pl, &__lock_type); \
} while (0)
#else /* WITNESS */
#define pl_init(pp, pl) (pp)->pr_lock_ops->pl_init(pp, pl, NULL)
#endif /* WITNESS */
static inline void
pl_enter(struct pool *pp, union pool_lock *pl)
{
pp->pr_lock_ops->pl_enter(pl);
}
static inline int
pl_enter_try(struct pool *pp, union pool_lock *pl)
{
return pp->pr_lock_ops->pl_enter_try(pl);
}
static inline void
pl_leave(struct pool *pp, union pool_lock *pl)
{
pp->pr_lock_ops->pl_leave(pl);
}
static inline void
pl_assert_locked(struct pool *pp, union pool_lock *pl)
{
pp->pr_lock_ops->pl_assert_locked(pl);
}
static inline void
pl_assert_unlocked(struct pool *pp, union pool_lock *pl)
{
pp->pr_lock_ops->pl_assert_unlocked(pl);
}
static inline int
pl_sleep(struct pool *pp, void *ident, union pool_lock *lock, int priority,
const char *wmesg)
{
return pp->pr_lock_ops->pl_sleep(ident, lock, priority, wmesg);
}
struct pool_item {
u_long pi_magic;
XSIMPLEQ_ENTRY(pool_item) pi_list;
};
#define POOL_IMAGIC(ph, pi) ((u_long)(pi) ^ (ph)->ph_magic)
struct pool_page_header {
/* Page headers */
TAILQ_ENTRY(pool_page_header)
ph_entry; /* pool page list */
XSIMPLEQ_HEAD(, pool_item)
ph_items; /* free items on the page */
RBT_ENTRY(pool_page_header)
ph_node; /* off-page page headers */
unsigned int ph_nmissing; /* # of chunks in use */
caddr_t ph_page; /* this page's address */
caddr_t ph_colored; /* page's colored address */
unsigned long ph_magic;
uint64_t ph_timestamp;
};
#define POOL_MAGICBIT (1 << 3) /* keep away from perturbed low bits */
#define POOL_PHPOISON(ph) ISSET((ph)->ph_magic, POOL_MAGICBIT)
#ifdef MULTIPROCESSOR
struct pool_cache_item {
struct pool_cache_item *ci_next; /* next item in list */
unsigned long ci_nitems; /* number of items in list */
TAILQ_ENTRY(pool_cache_item)
ci_nextl; /* entry in list of lists */
};
/* we store whether the cached item is poisoned in the high bit of nitems */
#define POOL_CACHE_ITEM_NITEMS_MASK 0x7ffffffUL
#define POOL_CACHE_ITEM_NITEMS_POISON 0x8000000UL
#define POOL_CACHE_ITEM_NITEMS(_ci) \
((_ci)->ci_nitems & POOL_CACHE_ITEM_NITEMS_MASK)
#define POOL_CACHE_ITEM_POISONED(_ci) \
ISSET((_ci)->ci_nitems, POOL_CACHE_ITEM_NITEMS_POISON)
struct pool_cache {
struct pool_cache_item *pc_actv; /* active list of items */
unsigned long pc_nactv; /* actv head nitems cache */
struct pool_cache_item *pc_prev; /* previous list of items */
uint64_t pc_gen; /* generation number */
uint64_t pc_nget; /* # of successful requests */
uint64_t pc_nfail; /* # of unsuccessful reqs */
uint64_t pc_nput; /* # of releases */
uint64_t pc_nlget; /* # of list requests */
uint64_t pc_nlfail; /* # of fails getting a list */
uint64_t pc_nlput; /* # of list releases */
int pc_nout;
};
void *pool_cache_get(struct pool *);
void pool_cache_put(struct pool *, void *);
void pool_cache_destroy(struct pool *);
void pool_cache_gc(struct pool *);
#endif
void pool_cache_pool_info(struct pool *, struct kinfo_pool *);
int pool_cache_info(struct pool *, void *, size_t *);
int pool_cache_cpus_info(struct pool *, void *, size_t *);
#ifdef POOL_DEBUG
int pool_debug = 1;
#else
int pool_debug = 0;
#endif
#define POOL_INPGHDR(pp) ((pp)->pr_phoffset != 0)
struct pool_page_header *
pool_p_alloc(struct pool *, int, int *);
void pool_p_insert(struct pool *, struct pool_page_header *);
void pool_p_remove(struct pool *, struct pool_page_header *);
void pool_p_free(struct pool *, struct pool_page_header *);
void pool_update_curpage(struct pool *);
void *pool_do_get(struct pool *, int, int *);
void pool_do_put(struct pool *, void *);
int pool_chk_page(struct pool *, struct pool_page_header *, int);
int pool_chk(struct pool *);
void pool_get_done(struct pool *, void *, void *);
void pool_runqueue(struct pool *, int);
void *pool_allocator_alloc(struct pool *, int, int *);
void pool_allocator_free(struct pool *, void *);
/*
* The default pool allocator.
*/
void *pool_page_alloc(struct pool *, int, int *);
void pool_page_free(struct pool *, void *);
/*
* safe for interrupts; this is the default allocator
*/
struct pool_allocator pool_allocator_single = {
pool_page_alloc,
pool_page_free,
POOL_ALLOC_SIZE(PAGE_SIZE, POOL_ALLOC_ALIGNED)
};
void *pool_multi_alloc(struct pool *, int, int *);
void pool_multi_free(struct pool *, void *);
struct pool_allocator pool_allocator_multi = {
pool_multi_alloc,
pool_multi_free,
POOL_ALLOC_SIZES(PAGE_SIZE, (1UL << 31), POOL_ALLOC_ALIGNED)
};
void *pool_multi_alloc_ni(struct pool *, int, int *);
void pool_multi_free_ni(struct pool *, void *);
struct pool_allocator pool_allocator_multi_ni = {
pool_multi_alloc_ni,
pool_multi_free_ni,
POOL_ALLOC_SIZES(PAGE_SIZE, (1UL << 31), POOL_ALLOC_ALIGNED)
};
#ifdef DDB
void pool_print_pagelist(struct pool_pagelist *, int (*)(const char *, ...)
__attribute__((__format__(__kprintf__,1,2))));
void pool_print1(struct pool *, const char *, int (*)(const char *, ...)
__attribute__((__format__(__kprintf__,1,2))));
#endif
/* stale page garbage collectors */
void pool_gc_sched(void *);
struct timeout pool_gc_tick = TIMEOUT_INITIALIZER(pool_gc_sched, NULL);
void pool_gc_pages(void *);
struct task pool_gc_task = TASK_INITIALIZER(pool_gc_pages, NULL);
#define POOL_WAIT_FREE SEC_TO_NSEC(1)
#define POOL_WAIT_GC SEC_TO_NSEC(8)
RBT_PROTOTYPE(phtree, pool_page_header, ph_node, phtree_compare);
static inline int
phtree_compare(const struct pool_page_header *a,
const struct pool_page_header *b)
{
vaddr_t va = (vaddr_t)a->ph_page;
vaddr_t vb = (vaddr_t)b->ph_page;
/* the compares in this order are important for the NFIND to work */
if (vb < va)
return (-1);
if (vb > va)
return (1);
return (0);
}
RBT_GENERATE(phtree, pool_page_header, ph_node, phtree_compare);
/*
* Return the pool page header based on page address.
*/
static inline struct pool_page_header *
pr_find_pagehead(struct pool *pp, void *v)
{
struct pool_page_header *ph, key;
if (POOL_INPGHDR(pp)) {
caddr_t page;
page = (caddr_t)((vaddr_t)v & pp->pr_pgmask);
return ((struct pool_page_header *)(page + pp->pr_phoffset));
}
key.ph_page = v;
ph = RBT_NFIND(phtree, &pp->pr_phtree, &key);
if (ph == NULL)
panic("%s: %s: page header missing", __func__, pp->pr_wchan);
KASSERT(ph->ph_page <= (caddr_t)v);
if (ph->ph_page + pp->pr_pgsize <= (caddr_t)v)
panic("%s: %s: incorrect page", __func__, pp->pr_wchan);
return (ph);
}
/*
* Initialize the given pool resource structure.
*
* We export this routine to allow other kernel parts to declare
* static pools that must be initialized before malloc() is available.
*/
void
pool_init(struct pool *pp, size_t size, u_int align, int ipl, int flags,
const char *wchan, struct pool_allocator *palloc)
{
int off = 0, space;
unsigned int pgsize = PAGE_SIZE, items;
size_t pa_pagesz;
#ifdef DIAGNOSTIC
struct pool *iter;
#endif
if (align == 0)
align = ALIGN(1);
if (size < sizeof(struct pool_item))
size = sizeof(struct pool_item);
size = roundup(size, align);
while (size * 8 > pgsize)
pgsize <<= 1;
if (palloc == NULL) {
if (pgsize > PAGE_SIZE) {
palloc = ISSET(flags, PR_WAITOK) ?
&pool_allocator_multi_ni : &pool_allocator_multi;
} else
palloc = &pool_allocator_single;
pa_pagesz = palloc->pa_pagesz;
} else {
size_t pgsizes;
pa_pagesz = palloc->pa_pagesz;
if (pa_pagesz == 0)
pa_pagesz = POOL_ALLOC_DEFAULT;
pgsizes = pa_pagesz & ~POOL_ALLOC_ALIGNED;
/* make sure the allocator can fit at least one item */
if (size > pgsizes) {
panic("%s: pool %s item size 0x%zx > "
"allocator %p sizes 0x%zx", __func__, wchan,
size, palloc, pgsizes);
}
/* shrink pgsize until it fits into the range */
while (!ISSET(pgsizes, pgsize))
pgsize >>= 1;
}
KASSERT(ISSET(pa_pagesz, pgsize));
items = pgsize / size;
/*
* Decide whether to put the page header off page to avoid
* wasting too large a part of the page. Off-page page headers
* go into an RB tree, so we can match a returned item with
* its header based on the page address.
*/
if (ISSET(pa_pagesz, POOL_ALLOC_ALIGNED)) {
if (pgsize - (size * items) >
sizeof(struct pool_page_header)) {
off = pgsize - sizeof(struct pool_page_header);
} else if (sizeof(struct pool_page_header) * 2 >= size) {
off = pgsize - sizeof(struct pool_page_header);
items = off / size;
}
}
KASSERT(items > 0);
/*
* Initialize the pool structure.
*/
memset(pp, 0, sizeof(*pp));
if (ISSET(flags, PR_RWLOCK)) {
KASSERT(flags & PR_WAITOK);
pp->pr_lock_ops = &pool_lock_ops_rw;
} else
pp->pr_lock_ops = &pool_lock_ops_mtx;
TAILQ_INIT(&pp->pr_emptypages);
TAILQ_INIT(&pp->pr_fullpages);
TAILQ_INIT(&pp->pr_partpages);
pp->pr_curpage = NULL;
pp->pr_npages = 0;
pp->pr_minitems = 0;
pp->pr_minpages = 0;
pp->pr_maxpages = 8;
pp->pr_size = size;
pp->pr_pgsize = pgsize;
pp->pr_pgmask = ~0UL ^ (pgsize - 1);
pp->pr_phoffset = off;
pp->pr_itemsperpage = items;
pp->pr_wchan = wchan;
pp->pr_alloc = palloc;
pp->pr_nitems = 0;
pp->pr_nout = 0;
pp->pr_hardlimit = UINT_MAX;
pp->pr_hardlimit_warning = NULL;
pp->pr_hardlimit_ratecap.tv_sec = 0;
pp->pr_hardlimit_ratecap.tv_usec = 0;
pp->pr_hardlimit_warning_last.tv_sec = 0;
pp->pr_hardlimit_warning_last.tv_usec = 0;
RBT_INIT(phtree, &pp->pr_phtree);
/*
* Use the space between the chunks and the page header
* for cache coloring.
*/
space = POOL_INPGHDR(pp) ? pp->pr_phoffset : pp->pr_pgsize;
space -= pp->pr_itemsperpage * pp->pr_size;
pp->pr_align = align;
pp->pr_maxcolors = (space / align) + 1;
pp->pr_nget = 0;
pp->pr_nfail = 0;
pp->pr_nput = 0;
pp->pr_npagealloc = 0;
pp->pr_npagefree = 0;
pp->pr_hiwat = 0;
pp->pr_nidle = 0;
pp->pr_ipl = ipl;
pp->pr_flags = flags;
pl_init(pp, &pp->pr_lock);
pl_init(pp, &pp->pr_requests_lock);
TAILQ_INIT(&pp->pr_requests);
if (phpool.pr_size == 0) {
pool_init(&phpool, sizeof(struct pool_page_header), 0,
IPL_HIGH, 0, "phpool", NULL);
/* make sure phpool won't "recurse" */
KASSERT(POOL_INPGHDR(&phpool));
}
/* pglistalloc/constraint parameters */
pp->pr_crange = &kp_dirty;
/* Insert this into the list of all pools. */
rw_enter_write(&pool_lock);
#ifdef DIAGNOSTIC
SIMPLEQ_FOREACH(iter, &pool_head, pr_poollist) {
if (iter == pp)
panic("%s: pool %s already on list", __func__, wchan);
}
#endif
pp->pr_serial = ++pool_serial;
if (pool_serial == 0)
panic("%s: too much uptime", __func__);
SIMPLEQ_INSERT_HEAD(&pool_head, pp, pr_poollist);
pool_count++;
rw_exit_write(&pool_lock);
}
/*
* Decommission a pool resource.
*/
void
pool_destroy(struct pool *pp)
{
struct pool_page_header *ph;
struct pool *prev, *iter;
#ifdef MULTIPROCESSOR
if (pp->pr_cache != NULL)
pool_cache_destroy(pp);
#endif
#ifdef DIAGNOSTIC
if (pp->pr_nout != 0)
panic("%s: pool busy: still out: %u", __func__, pp->pr_nout);
#endif
/* Remove from global pool list */
rw_enter_write(&pool_lock);
pool_count--;
if (pp == SIMPLEQ_FIRST(&pool_head))
SIMPLEQ_REMOVE_HEAD(&pool_head, pr_poollist);
else {
prev = SIMPLEQ_FIRST(&pool_head);
SIMPLEQ_FOREACH(iter, &pool_head, pr_poollist) {
if (iter == pp) {
SIMPLEQ_REMOVE_AFTER(&pool_head, prev,
pr_poollist);
break;
}
prev = iter;
}
}
rw_exit_write(&pool_lock);
/* Remove all pages */
while ((ph = TAILQ_FIRST(&pp->pr_emptypages)) != NULL) {
pl_enter(pp, &pp->pr_lock);
pool_p_remove(pp, ph);
pl_leave(pp, &pp->pr_lock);
pool_p_free(pp, ph);
}
KASSERT(TAILQ_EMPTY(&pp->pr_fullpages));
KASSERT(TAILQ_EMPTY(&pp->pr_partpages));
}
void
pool_request_init(struct pool_request *pr,
void (*handler)(struct pool *, void *, void *), void *cookie)
{
pr->pr_handler = handler;
pr->pr_cookie = cookie;
pr->pr_item = NULL;
}
void
pool_request(struct pool *pp, struct pool_request *pr)
{
pl_enter(pp, &pp->pr_requests_lock);
TAILQ_INSERT_TAIL(&pp->pr_requests, pr, pr_entry);
pool_runqueue(pp, PR_NOWAIT);
pl_leave(pp, &pp->pr_requests_lock);
}
struct pool_get_memory {
union pool_lock lock;
void * volatile v;
};
/*
* Grab an item from the pool.
*/
void *
pool_get(struct pool *pp, int flags)
{
void *v = NULL;
int slowdown = 0;
KASSERT(flags & (PR_WAITOK | PR_NOWAIT));
if (pp->pr_flags & PR_RWLOCK)
KASSERT(flags & PR_WAITOK);
#ifdef MULTIPROCESSOR
if (pp->pr_cache != NULL) {
v = pool_cache_get(pp);
if (v != NULL)
goto good;
}
#endif
pl_enter(pp, &pp->pr_lock);
if (pp->pr_nout >= pp->pr_hardlimit) {
if (ISSET(flags, PR_NOWAIT|PR_LIMITFAIL))
goto fail;
} else if ((v = pool_do_get(pp, flags, &slowdown)) == NULL) {
if (ISSET(flags, PR_NOWAIT))
goto fail;
}
pl_leave(pp, &pp->pr_lock);
if ((slowdown || pool_debug == 2) && ISSET(flags, PR_WAITOK))
yield();
if (v == NULL) {
struct pool_get_memory mem = { .v = NULL };
struct pool_request pr;
#ifdef DIAGNOSTIC
if (ISSET(flags, PR_WAITOK) && curproc == &proc0)
panic("%s: cannot sleep for memory during boot",
__func__);
#endif
pl_init(pp, &mem.lock);
pool_request_init(&pr, pool_get_done, &mem);
pool_request(pp, &pr);
pl_enter(pp, &mem.lock);
while (mem.v == NULL)
pl_sleep(pp, &mem, &mem.lock, PSWP, pp->pr_wchan);
pl_leave(pp, &mem.lock);
v = mem.v;
}
#ifdef MULTIPROCESSOR
good:
#endif
if (ISSET(flags, PR_ZERO))
memset(v, 0, pp->pr_size);
TRACEPOINT(uvm, pool_get, pp, v, flags);
return (v);
fail:
pp->pr_nfail++;
pl_leave(pp, &pp->pr_lock);
return (NULL);
}
void
pool_get_done(struct pool *pp, void *xmem, void *v)
{
struct pool_get_memory *mem = xmem;
pl_enter(pp, &mem->lock);
mem->v = v;
pl_leave(pp, &mem->lock);
wakeup_one(mem);
}
void
pool_runqueue(struct pool *pp, int flags)
{
struct pool_requests prl = TAILQ_HEAD_INITIALIZER(prl);
struct pool_request *pr;
pl_assert_unlocked(pp, &pp->pr_lock);
pl_assert_locked(pp, &pp->pr_requests_lock);
if (pp->pr_requesting++)
return;
do {
pp->pr_requesting = 1;
TAILQ_CONCAT(&prl, &pp->pr_requests, pr_entry);
if (TAILQ_EMPTY(&prl))
continue;
pl_leave(pp, &pp->pr_requests_lock);
pl_enter(pp, &pp->pr_lock);
pr = TAILQ_FIRST(&prl);
while (pr != NULL) {
int slowdown = 0;
if (pp->pr_nout >= pp->pr_hardlimit)
break;
pr->pr_item = pool_do_get(pp, flags, &slowdown);
if (pr->pr_item == NULL) /* || slowdown ? */
break;
pr = TAILQ_NEXT(pr, pr_entry);
}
pl_leave(pp, &pp->pr_lock);
while ((pr = TAILQ_FIRST(&prl)) != NULL &&
pr->pr_item != NULL) {
TAILQ_REMOVE(&prl, pr, pr_entry);
(*pr->pr_handler)(pp, pr->pr_cookie, pr->pr_item);
}
pl_enter(pp, &pp->pr_requests_lock);
} while (--pp->pr_requesting);
TAILQ_CONCAT(&pp->pr_requests, &prl, pr_entry);
}
void *
pool_do_get(struct pool *pp, int flags, int *slowdown)
{
struct pool_item *pi;
struct pool_page_header *ph;
pl_assert_locked(pp, &pp->pr_lock);
splassert(pp->pr_ipl);
/*
* Account for this item now to avoid races if we need to give up
* pr_lock to allocate a page.
*/
pp->pr_nout++;
if (pp->pr_curpage == NULL) {
pl_leave(pp, &pp->pr_lock);
ph = pool_p_alloc(pp, flags, slowdown);
pl_enter(pp, &pp->pr_lock);
if (ph == NULL) {
pp->pr_nout--;
return (NULL);
}
pool_p_insert(pp, ph);
}
ph = pp->pr_curpage;
pi = XSIMPLEQ_FIRST(&ph->ph_items);
if (__predict_false(pi == NULL))
panic("%s: %s: page empty", __func__, pp->pr_wchan);
if (__predict_false(pi->pi_magic != POOL_IMAGIC(ph, pi))) {
panic("%s: %s free list modified: "
"page %p; item addr %p; offset 0x%x=0x%lx != 0x%lx",
__func__, pp->pr_wchan, ph->ph_page, pi,
0, pi->pi_magic, POOL_IMAGIC(ph, pi));
}
XSIMPLEQ_REMOVE_HEAD(&ph->ph_items, pi_list);
#ifdef DIAGNOSTIC
if (pool_debug && POOL_PHPOISON(ph)) {
size_t pidx;
uint32_t pval;
if (poison_check(pi + 1, pp->pr_size - sizeof(*pi),
&pidx, &pval)) {
int *ip = (int *)(pi + 1);
panic("%s: %s free list modified: "
"page %p; item addr %p; offset 0x%zx=0x%x",
__func__, pp->pr_wchan, ph->ph_page, pi,
(pidx * sizeof(int)) + sizeof(*pi), ip[pidx]);
}
}
#endif /* DIAGNOSTIC */
if (ph->ph_nmissing++ == 0) {
/*
* This page was previously empty. Move it to the list of
* partially-full pages. This page is already curpage.
*/
TAILQ_REMOVE(&pp->pr_emptypages, ph, ph_entry);
TAILQ_INSERT_TAIL(&pp->pr_partpages, ph, ph_entry);
pp->pr_nidle--;
}
if (ph->ph_nmissing == pp->pr_itemsperpage) {
/*
* This page is now full. Move it to the full list
* and select a new current page.
*/
TAILQ_REMOVE(&pp->pr_partpages, ph, ph_entry);
TAILQ_INSERT_TAIL(&pp->pr_fullpages, ph, ph_entry);
pool_update_curpage(pp);
}
pp->pr_nget++;
return (pi);
}
/*
* Return resource to the pool.
*/
void
pool_put(struct pool *pp, void *v)
{
struct pool_page_header *ph, *freeph = NULL;
#ifdef DIAGNOSTIC
if (v == NULL)
panic("%s: NULL item", __func__);
#endif
TRACEPOINT(uvm, pool_put, pp, v);
#ifdef MULTIPROCESSOR
if (pp->pr_cache != NULL && TAILQ_EMPTY(&pp->pr_requests)) {
pool_cache_put(pp, v);
return;
}
#endif
pl_enter(pp, &pp->pr_lock);
pool_do_put(pp, v);
pp->pr_nout--;
pp->pr_nput++;
/* is it time to free a page? */
if (pp->pr_nidle > pp->pr_maxpages &&
(ph = TAILQ_FIRST(&pp->pr_emptypages)) != NULL &&
getnsecuptime() - ph->ph_timestamp > POOL_WAIT_FREE) {
freeph = ph;
pool_p_remove(pp, freeph);
}
pl_leave(pp, &pp->pr_lock);
if (freeph != NULL)
pool_p_free(pp, freeph);
pool_wakeup(pp);
}
void
pool_wakeup(struct pool *pp)
{
if (!TAILQ_EMPTY(&pp->pr_requests)) {
pl_enter(pp, &pp->pr_requests_lock);
pool_runqueue(pp, PR_NOWAIT);
pl_leave(pp, &pp->pr_requests_lock);
}
}
void
pool_do_put(struct pool *pp, void *v)
{
struct pool_item *pi = v;
struct pool_page_header *ph;
splassert(pp->pr_ipl);
ph = pr_find_pagehead(pp, v);
#ifdef DIAGNOSTIC
if (pool_debug) {
struct pool_item *qi;
XSIMPLEQ_FOREACH(qi, &ph->ph_items, pi_list) {
if (pi == qi) {
panic("%s: %s: double pool_put: %p", __func__,
pp->pr_wchan, pi);
}
}
}
#endif /* DIAGNOSTIC */
pi->pi_magic = POOL_IMAGIC(ph, pi);
XSIMPLEQ_INSERT_HEAD(&ph->ph_items, pi, pi_list);
#ifdef DIAGNOSTIC
if (POOL_PHPOISON(ph))
poison_mem(pi + 1, pp->pr_size - sizeof(*pi));
#endif /* DIAGNOSTIC */
if (ph->ph_nmissing-- == pp->pr_itemsperpage) {
/*
* The page was previously completely full, move it to the
* partially-full list.
*/
TAILQ_REMOVE(&pp->pr_fullpages, ph, ph_entry);
TAILQ_INSERT_TAIL(&pp->pr_partpages, ph, ph_entry);
}
if (ph->ph_nmissing == 0) {
/*
* The page is now empty, so move it to the empty page list.
*/
pp->pr_nidle++;
ph->ph_timestamp = getnsecuptime();
TAILQ_REMOVE(&pp->pr_partpages, ph, ph_entry);
TAILQ_INSERT_TAIL(&pp->pr_emptypages, ph, ph_entry);
pool_update_curpage(pp);
}
}
/*
* Add N items to the pool.
*/
int
pool_prime(struct pool *pp, int n)
{
struct pool_pagelist pl = TAILQ_HEAD_INITIALIZER(pl);
struct pool_page_header *ph;
int newpages;
newpages = roundup(n, pp->pr_itemsperpage) / pp->pr_itemsperpage;
while (newpages-- > 0) {
int slowdown = 0;
ph = pool_p_alloc(pp, PR_NOWAIT, &slowdown);
if (ph == NULL) /* or slowdown? */
break;
TAILQ_INSERT_TAIL(&pl, ph, ph_entry);
}
pl_enter(pp, &pp->pr_lock);
while ((ph = TAILQ_FIRST(&pl)) != NULL) {
TAILQ_REMOVE(&pl, ph, ph_entry);
pool_p_insert(pp, ph);
}
pl_leave(pp, &pp->pr_lock);
return (0);
}
struct pool_page_header *
pool_p_alloc(struct pool *pp, int flags, int *slowdown)
{
struct pool_page_header *ph;
struct pool_item *pi;
caddr_t addr;
unsigned int order;
int o;
int n;
pl_assert_unlocked(pp, &pp->pr_lock);
KASSERT(pp->pr_size >= sizeof(*pi));
addr = pool_allocator_alloc(pp, flags, slowdown);
if (addr == NULL)
return (NULL);
if (POOL_INPGHDR(pp))
ph = (struct pool_page_header *)(addr + pp->pr_phoffset);
else {
ph = pool_get(&phpool, flags);
if (ph == NULL) {
pool_allocator_free(pp, addr);
return (NULL);
}
}
XSIMPLEQ_INIT(&ph->ph_items);
ph->ph_page = addr;
addr += pp->pr_align * (pp->pr_npagealloc % pp->pr_maxcolors);
ph->ph_colored = addr;
ph->ph_nmissing = 0;
arc4random_buf(&ph->ph_magic, sizeof(ph->ph_magic));
#ifdef DIAGNOSTIC
/* use a bit in ph_magic to record if we poison page items */
if (pool_debug)
SET(ph->ph_magic, POOL_MAGICBIT);
else
CLR(ph->ph_magic, POOL_MAGICBIT);
#endif /* DIAGNOSTIC */
n = pp->pr_itemsperpage;
o = 32;
while (n--) {
pi = (struct pool_item *)addr;
pi->pi_magic = POOL_IMAGIC(ph, pi);
if (o == 32) {
order = arc4random();
o = 0;
}
if (ISSET(order, 1U << o++))
XSIMPLEQ_INSERT_TAIL(&ph->ph_items, pi, pi_list);
else
XSIMPLEQ_INSERT_HEAD(&ph->ph_items, pi, pi_list);
#ifdef DIAGNOSTIC
if (POOL_PHPOISON(ph))
poison_mem(pi + 1, pp->pr_size - sizeof(*pi));
#endif /* DIAGNOSTIC */
addr += pp->pr_size;
}
return (ph);
}
void
pool_p_free(struct pool *pp, struct pool_page_header *ph)
{
struct pool_item *pi;
pl_assert_unlocked(pp, &pp->pr_lock);
KASSERT(ph->ph_nmissing == 0);
XSIMPLEQ_FOREACH(pi, &ph->ph_items, pi_list) {
if (__predict_false(pi->pi_magic != POOL_IMAGIC(ph, pi))) {
panic("%s: %s free list modified: "
"page %p; item addr %p; offset 0x%x=0x%lx",
__func__, pp->pr_wchan, ph->ph_page, pi,
0, pi->pi_magic);
}
#ifdef DIAGNOSTIC
if (POOL_PHPOISON(ph)) {
size_t pidx;
uint32_t pval;
if (poison_check(pi + 1, pp->pr_size - sizeof(*pi),
&pidx, &pval)) {
int *ip = (int *)(pi + 1);
panic("%s: %s free list modified: "
"page %p; item addr %p; offset 0x%zx=0x%x",
__func__, pp->pr_wchan, ph->ph_page, pi,
pidx * sizeof(int), ip[pidx]);
}
}
#endif
}
pool_allocator_free(pp, ph->ph_page);
if (!POOL_INPGHDR(pp))
pool_put(&phpool, ph);
}
void
pool_p_insert(struct pool *pp, struct pool_page_header *ph)
{
pl_assert_locked(pp, &pp->pr_lock);
/* If the pool was depleted, point at the new page */
if (pp->pr_curpage == NULL)
pp->pr_curpage = ph;
TAILQ_INSERT_TAIL(&pp->pr_emptypages, ph, ph_entry);
if (!POOL_INPGHDR(pp))
RBT_INSERT(phtree, &pp->pr_phtree, ph);
pp->pr_nitems += pp->pr_itemsperpage;
pp->pr_nidle++;
pp->pr_npagealloc++;
if (++pp->pr_npages > pp->pr_hiwat)
pp->pr_hiwat = pp->pr_npages;
}
void
pool_p_remove(struct pool *pp, struct pool_page_header *ph)
{
pl_assert_locked(pp, &pp->pr_lock);
pp->pr_npagefree++;
pp->pr_npages--;
pp->pr_nidle--;
pp->pr_nitems -= pp->pr_itemsperpage;
if (!POOL_INPGHDR(pp))
RBT_REMOVE(phtree, &pp->pr_phtree, ph);
TAILQ_REMOVE(&pp->pr_emptypages, ph, ph_entry);
pool_update_curpage(pp);
}
void
pool_update_curpage(struct pool *pp)
{
pp->pr_curpage = TAILQ_LAST(&pp->pr_partpages, pool_pagelist);
if (pp->pr_curpage == NULL) {
pp->pr_curpage = TAILQ_LAST(&pp->pr_emptypages, pool_pagelist);
}
}
void
pool_setlowat(struct pool *pp, int n)
{
int prime = 0;
pl_enter(pp, &pp->pr_lock);
pp->pr_minitems = n;
pp->pr_minpages = (n == 0)
? 0
: roundup(n, pp->pr_itemsperpage) / pp->pr_itemsperpage;
if (pp->pr_nitems < n)
prime = n - pp->pr_nitems;
pl_leave(pp, &pp->pr_lock);
if (prime > 0)
pool_prime(pp, prime);
}
void
pool_sethiwat(struct pool *pp, int n)
{
pp->pr_maxpages = (n == 0)
? 0
: roundup(n, pp->pr_itemsperpage) / pp->pr_itemsperpage;
}
int
pool_sethardlimit(struct pool *pp, u_int n, const char *warnmsg, int ratecap)
{
int error = 0;
if (n < pp->pr_nout) {
error = EINVAL;
goto done;
}
pp->pr_hardlimit = n;
pp->pr_hardlimit_warning = warnmsg;
pp->pr_hardlimit_ratecap.tv_sec = ratecap;
pp->pr_hardlimit_warning_last.tv_sec = 0;
pp->pr_hardlimit_warning_last.tv_usec = 0;
done:
return (error);
}
void
pool_set_constraints(struct pool *pp, const struct kmem_pa_mode *mode)
{
pp->pr_crange = mode;
}
/*
* Release all complete pages that have not been used recently.
*
* Returns non-zero if any pages have been reclaimed.
*/
int
pool_reclaim(struct pool *pp)
{
struct pool_page_header *ph, *phnext;
struct pool_pagelist pl = TAILQ_HEAD_INITIALIZER(pl);
pl_enter(pp, &pp->pr_lock);
for (ph = TAILQ_FIRST(&pp->pr_emptypages); ph != NULL; ph = phnext) {
phnext = TAILQ_NEXT(ph, ph_entry);
/* Check our minimum page claim */
if (pp->pr_npages <= pp->pr_minpages)
break;
/*
* If freeing this page would put us below
* the low water mark, stop now.
*/
if ((pp->pr_nitems - pp->pr_itemsperpage) <
pp->pr_minitems)
break;
pool_p_remove(pp, ph);
TAILQ_INSERT_TAIL(&pl, ph, ph_entry);
}
pl_leave(pp, &pp->pr_lock);
if (TAILQ_EMPTY(&pl))
return (0);
while ((ph = TAILQ_FIRST(&pl)) != NULL) {
TAILQ_REMOVE(&pl, ph, ph_entry);
pool_p_free(pp, ph);
}
return (1);
}
/*
* Release all complete pages that have not been used recently
* from all pools.
*/
void
pool_reclaim_all(void)
{
struct pool *pp;
rw_enter_read(&pool_lock);
SIMPLEQ_FOREACH(pp, &pool_head, pr_poollist)
pool_reclaim(pp);
rw_exit_read(&pool_lock);
}
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
/*
* Diagnostic helpers.
*/
void
pool_printit(struct pool *pp, const char *modif,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
pool_print1(pp, modif, pr);
}
void
pool_print_pagelist(struct pool_pagelist *pl,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct pool_page_header *ph;
struct pool_item *pi;
TAILQ_FOREACH(ph, pl, ph_entry) {
(*pr)("\t\tpage %p, color %p, nmissing %d\n",
ph->ph_page, ph->ph_colored, ph->ph_nmissing);
XSIMPLEQ_FOREACH(pi, &ph->ph_items, pi_list) {
if (pi->pi_magic != POOL_IMAGIC(ph, pi)) {
(*pr)("\t\t\titem %p, magic 0x%lx\n",
pi, pi->pi_magic);
}
}
}
}
void
pool_print1(struct pool *pp, const char *modif,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct pool_page_header *ph;
int print_pagelist = 0;
char c;
while ((c = *modif++) != '\0') {
if (c == 'p')
print_pagelist = 1;
modif++;
}
(*pr)("POOL %s: size %u maxcolors %u\n", pp->pr_wchan, pp->pr_size,
pp->pr_maxcolors);
(*pr)("\talloc %p\n", pp->pr_alloc);
(*pr)("\tminitems %u, minpages %u, maxpages %u, npages %u\n",
pp->pr_minitems, pp->pr_minpages, pp->pr_maxpages, pp->pr_npages);
(*pr)("\titemsperpage %u, nitems %u, nout %u, hardlimit %u\n",
pp->pr_itemsperpage, pp->pr_nitems, pp->pr_nout, pp->pr_hardlimit);
(*pr)("\n\tnget %lu, nfail %lu, nput %lu\n",
pp->pr_nget, pp->pr_nfail, pp->pr_nput);
(*pr)("\tnpagealloc %lu, npagefree %lu, hiwat %u, nidle %lu\n",
pp->pr_npagealloc, pp->pr_npagefree, pp->pr_hiwat, pp->pr_nidle);
if (print_pagelist == 0)
return;
if ((ph = TAILQ_FIRST(&pp->pr_emptypages)) != NULL)
(*pr)("\n\tempty page list:\n");
pool_print_pagelist(&pp->pr_emptypages, pr);
if ((ph = TAILQ_FIRST(&pp->pr_fullpages)) != NULL)
(*pr)("\n\tfull page list:\n");
pool_print_pagelist(&pp->pr_fullpages, pr);
if ((ph = TAILQ_FIRST(&pp->pr_partpages)) != NULL)
(*pr)("\n\tpartial-page list:\n");
pool_print_pagelist(&pp->pr_partpages, pr);
if (pp->pr_curpage == NULL)
(*pr)("\tno current page\n");
else
(*pr)("\tcurpage %p\n", pp->pr_curpage->ph_page);
}
void
db_show_all_pools(db_expr_t expr, int haddr, db_expr_t count, char *modif)
{
struct pool *pp;
char maxp[16];
int ovflw;
char mode;
mode = modif[0];
if (mode != '\0' && mode != 'a') {
db_printf("usage: show all pools [/a]\n");
return;
}
if (mode == '\0')
db_printf("%-10s%4s%9s%5s%9s%6s%6s%6s%6s%6s%6s%5s\n",
"Name",
"Size",
"Requests",
"Fail",
"Releases",
"Pgreq",
"Pgrel",
"Npage",
"Hiwat",
"Minpg",
"Maxpg",
"Idle");
else
db_printf("%-12s %18s %18s\n",
"Name", "Address", "Allocator");
SIMPLEQ_FOREACH(pp, &pool_head, pr_poollist) {
if (mode == 'a') {
db_printf("%-12s %18p %18p\n", pp->pr_wchan, pp,
pp->pr_alloc);
continue;
}
if (!pp->pr_nget)
continue;
if (pp->pr_maxpages == UINT_MAX)
snprintf(maxp, sizeof maxp, "inf");
else
snprintf(maxp, sizeof maxp, "%u", pp->pr_maxpages);
#define PRWORD(ovflw, fmt, width, fixed, val) do { \
(ovflw) += db_printf((fmt), \
(width) - (fixed) - (ovflw) > 0 ? \
(width) - (fixed) - (ovflw) : 0, \
(val)) - (width); \
if ((ovflw) < 0) \
(ovflw) = 0; \
} while (/* CONSTCOND */0)
ovflw = 0;
PRWORD(ovflw, "%-*s", 10, 0, pp->pr_wchan);
PRWORD(ovflw, " %*u", 4, 1, pp->pr_size);
PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget);
PRWORD(ovflw, " %*lu", 5, 1, pp->pr_nfail);
PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nput);
PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagealloc);
PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagefree);
PRWORD(ovflw, " %*d", 6, 1, pp->pr_npages);
PRWORD(ovflw, " %*d", 6, 1, pp->pr_hiwat);
PRWORD(ovflw, " %*d", 6, 1, pp->pr_minpages);
PRWORD(ovflw, " %*s", 6, 1, maxp);
PRWORD(ovflw, " %*lu\n", 5, 1, pp->pr_nidle);
pool_chk(pp);
}
}
#endif /* DDB */
#if defined(POOL_DEBUG) || defined(DDB)
int
pool_chk_page(struct pool *pp, struct pool_page_header *ph, int expected)
{
struct pool_item *pi;
caddr_t page;
int n;
const char *label = pp->pr_wchan;
page = (caddr_t)((u_long)ph & pp->pr_pgmask);
if (page != ph->ph_page && POOL_INPGHDR(pp)) {
printf("%s: ", label);
printf("pool(%p:%s): page inconsistency: page %p; "
"at page head addr %p (p %p)\n",
pp, pp->pr_wchan, ph->ph_page, ph, page);
return 1;
}
for (pi = XSIMPLEQ_FIRST(&ph->ph_items), n = 0;
pi != NULL;
pi = XSIMPLEQ_NEXT(&ph->ph_items, pi, pi_list), n++) {
if ((caddr_t)pi < ph->ph_page ||
(caddr_t)pi >= ph->ph_page + pp->pr_pgsize) {
printf("%s: ", label);
printf("pool(%p:%s): page inconsistency: page %p;"
" item ordinal %d; addr %p\n", pp,
pp->pr_wchan, ph->ph_page, n, pi);
return (1);
}
if (pi->pi_magic != POOL_IMAGIC(ph, pi)) {
printf("%s: ", label);
printf("pool(%p:%s): free list modified: "
"page %p; item ordinal %d; addr %p "
"(p %p); offset 0x%x=0x%lx\n",
pp, pp->pr_wchan, ph->ph_page, n, pi, page,
0, pi->pi_magic);
}
#ifdef DIAGNOSTIC
if (POOL_PHPOISON(ph)) {
size_t pidx;
uint32_t pval;
if (poison_check(pi + 1, pp->pr_size - sizeof(*pi),
&pidx, &pval)) {
int *ip = (int *)(pi + 1);
printf("pool(%s): free list modified: "
"page %p; item ordinal %d; addr %p "
"(p %p); offset 0x%zx=0x%x\n",
pp->pr_wchan, ph->ph_page, n, pi,
page, pidx * sizeof(int), ip[pidx]);
}
}
#endif /* DIAGNOSTIC */
}
if (n + ph->ph_nmissing != pp->pr_itemsperpage) {
printf("pool(%p:%s): page inconsistency: page %p;"
" %d on list, %d missing, %d items per page\n", pp,
pp->pr_wchan, ph->ph_page, n, ph->ph_nmissing,
pp->pr_itemsperpage);
return 1;
}
if (expected >= 0 && n != expected) {
printf("pool(%p:%s): page inconsistency: page %p;"
" %d on list, %d missing, %d expected\n", pp,
pp->pr_wchan, ph->ph_page, n, ph->ph_nmissing,
expected);
return 1;
}
return 0;
}
int
pool_chk(struct pool *pp)
{
struct pool_page_header *ph;
int r = 0;
TAILQ_FOREACH(ph, &pp->pr_emptypages, ph_entry)
r += pool_chk_page(pp, ph, pp->pr_itemsperpage);
TAILQ_FOREACH(ph, &pp->pr_fullpages, ph_entry)
r += pool_chk_page(pp, ph, 0);
TAILQ_FOREACH(ph, &pp->pr_partpages, ph_entry)
r += pool_chk_page(pp, ph, -1);
return (r);
}
#endif /* defined(POOL_DEBUG) || defined(DDB) */
#ifdef DDB
void
pool_walk(struct pool *pp, int full,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))),
void (*func)(void *, int, int (*)(const char *, ...)
__attribute__((__format__(__kprintf__,1,2)))))
{
struct pool_page_header *ph;
struct pool_item *pi;
caddr_t cp;
int n;
TAILQ_FOREACH(ph, &pp->pr_fullpages, ph_entry) {
cp = ph->ph_colored;
n = ph->ph_nmissing;
while (n--) {
func(cp, full, pr);
cp += pp->pr_size;
}
}
TAILQ_FOREACH(ph, &pp->pr_partpages, ph_entry) {
cp = ph->ph_colored;
n = ph->ph_nmissing;
do {
XSIMPLEQ_FOREACH(pi, &ph->ph_items, pi_list) {
if (cp == (caddr_t)pi)
break;
}
if (cp != (caddr_t)pi) {
func(cp, full, pr);
n--;
}
cp += pp->pr_size;
} while (n > 0);
}
}
#endif
/*
* We have three different sysctls.
* kern.pool.npools - the number of pools.
* kern.pool.pool.<pool#> - the pool struct for the pool#.
* kern.pool.name.<pool#> - the name for pool#.
*/
int
sysctl_dopool(int *name, u_int namelen, char *oldp, size_t *oldlenp)
{
struct kinfo_pool pi;
struct pool *pp;
int rv = ENOENT;
switch (name[0]) {
case KERN_POOL_NPOOLS:
if (namelen != 1)
return (ENOTDIR);
return (sysctl_rdint(oldp, oldlenp, NULL, pool_count));
case KERN_POOL_NAME:
case KERN_POOL_POOL:
case KERN_POOL_CACHE:
case KERN_POOL_CACHE_CPUS:
break;
default:
return (EOPNOTSUPP);
}
if (namelen != 2)
return (ENOTDIR);
rw_enter_read(&pool_lock);
SIMPLEQ_FOREACH(pp, &pool_head, pr_poollist) {
if (name[1] == pp->pr_serial)
break;
}
if (pp == NULL)
goto done;
switch (name[0]) {
case KERN_POOL_NAME:
rv = sysctl_rdstring(oldp, oldlenp, NULL, pp->pr_wchan);
break;
case KERN_POOL_POOL:
memset(&pi, 0, sizeof(pi));
pl_enter(pp, &pp->pr_lock);
pi.pr_size = pp->pr_size;
pi.pr_pgsize = pp->pr_pgsize;
pi.pr_itemsperpage = pp->pr_itemsperpage;
pi.pr_npages = pp->pr_npages;
pi.pr_minpages = pp->pr_minpages;
pi.pr_maxpages = pp->pr_maxpages;
pi.pr_hardlimit = pp->pr_hardlimit;
pi.pr_nout = pp->pr_nout;
pi.pr_nitems = pp->pr_nitems;
pi.pr_nget = pp->pr_nget;
pi.pr_nput = pp->pr_nput;
pi.pr_nfail = pp->pr_nfail;
pi.pr_npagealloc = pp->pr_npagealloc;
pi.pr_npagefree = pp->pr_npagefree;
pi.pr_hiwat = pp->pr_hiwat;
pi.pr_nidle = pp->pr_nidle;
pl_leave(pp, &pp->pr_lock);
pool_cache_pool_info(pp, &pi);
rv = sysctl_rdstruct(oldp, oldlenp, NULL, &pi, sizeof(pi));
break;
case KERN_POOL_CACHE:
rv = pool_cache_info(pp, oldp, oldlenp);
break;
case KERN_POOL_CACHE_CPUS:
rv = pool_cache_cpus_info(pp, oldp, oldlenp);
break;
}
done:
rw_exit_read(&pool_lock);
return (rv);
}
void
pool_gc_sched(void *null)
{
task_add(systqmp, &pool_gc_task);
}
void
pool_gc_pages(void *null)
{
struct pool *pp;
struct pool_page_header *ph, *freeph;
int s;
rw_enter_read(&pool_lock);
s = splvm(); /* XXX go to splvm until all pools _setipl properly */
SIMPLEQ_FOREACH(pp, &pool_head, pr_poollist) {
#ifdef MULTIPROCESSOR
if (pp->pr_cache != NULL)
pool_cache_gc(pp);
#endif
if (pp->pr_nidle <= pp->pr_minpages || /* guess */
!pl_enter_try(pp, &pp->pr_lock)) /* try */
continue;
/* is it time to free a page? */
if (pp->pr_nidle > pp->pr_minpages &&
(ph = TAILQ_FIRST(&pp->pr_emptypages)) != NULL &&
getnsecuptime() - ph->ph_timestamp > POOL_WAIT_GC) {
freeph = ph;
pool_p_remove(pp, freeph);
} else
freeph = NULL;
pl_leave(pp, &pp->pr_lock);
if (freeph != NULL)
pool_p_free(pp, freeph);
}
splx(s);
rw_exit_read(&pool_lock);
timeout_add_sec(&pool_gc_tick, 1);
}
/*
* Pool backend allocators.
*/
void *
pool_allocator_alloc(struct pool *pp, int flags, int *slowdown)
{
void *v;
v = (*pp->pr_alloc->pa_alloc)(pp, flags, slowdown);
#ifdef DIAGNOSTIC
if (v != NULL && POOL_INPGHDR(pp)) {
vaddr_t addr = (vaddr_t)v;
if ((addr & pp->pr_pgmask) != addr) {
panic("%s: %s page address %p isn't aligned to %u",
__func__, pp->pr_wchan, v, pp->pr_pgsize);
}
}
#endif
return (v);
}
void
pool_allocator_free(struct pool *pp, void *v)
{
struct pool_allocator *pa = pp->pr_alloc;
(*pa->pa_free)(pp, v);
}
void *
pool_page_alloc(struct pool *pp, int flags, int *slowdown)
{
struct kmem_dyn_mode kd = KMEM_DYN_INITIALIZER;
kd.kd_waitok = ISSET(flags, PR_WAITOK);
kd.kd_slowdown = slowdown;
return (km_alloc(pp->pr_pgsize, &kv_page, pp->pr_crange, &kd));
}
void
pool_page_free(struct pool *pp, void *v)
{
km_free(v, pp->pr_pgsize, &kv_page, pp->pr_crange);
}
void *
pool_multi_alloc(struct pool *pp, int flags, int *slowdown)
{
struct kmem_va_mode kv = kv_intrsafe;
struct kmem_dyn_mode kd = KMEM_DYN_INITIALIZER;
void *v;
int s;
if (POOL_INPGHDR(pp))
kv.kv_align = pp->pr_pgsize;
kd.kd_waitok = ISSET(flags, PR_WAITOK);
kd.kd_slowdown = slowdown;
s = splvm();
v = km_alloc(pp->pr_pgsize, &kv, pp->pr_crange, &kd);
splx(s);
return (v);
}
void
pool_multi_free(struct pool *pp, void *v)
{
struct kmem_va_mode kv = kv_intrsafe;
int s;
if (POOL_INPGHDR(pp))
kv.kv_align = pp->pr_pgsize;
s = splvm();
km_free(v, pp->pr_pgsize, &kv, pp->pr_crange);
splx(s);
}
void *
pool_multi_alloc_ni(struct pool *pp, int flags, int *slowdown)
{
struct kmem_va_mode kv = kv_any;
struct kmem_dyn_mode kd = KMEM_DYN_INITIALIZER;
void *v;
if (POOL_INPGHDR(pp))
kv.kv_align = pp->pr_pgsize;
kd.kd_waitok = ISSET(flags, PR_WAITOK);
kd.kd_slowdown = slowdown;
KERNEL_LOCK();
v = km_alloc(pp->pr_pgsize, &kv, pp->pr_crange, &kd);
KERNEL_UNLOCK();
return (v);
}
void
pool_multi_free_ni(struct pool *pp, void *v)
{
struct kmem_va_mode kv = kv_any;
if (POOL_INPGHDR(pp))
kv.kv_align = pp->pr_pgsize;
KERNEL_LOCK();
km_free(v, pp->pr_pgsize, &kv, pp->pr_crange);
KERNEL_UNLOCK();
}
#ifdef MULTIPROCESSOR
struct pool pool_caches; /* per cpu cache entries */
void
pool_cache_init(struct pool *pp)
{
struct cpumem *cm;
struct pool_cache *pc;
struct cpumem_iter i;
if (pool_caches.pr_size == 0) {
pool_init(&pool_caches, sizeof(struct pool_cache),
CACHELINESIZE, IPL_NONE, PR_WAITOK | PR_RWLOCK,
"plcache", NULL);
}
/* must be able to use the pool items as cache list items */
KASSERT(pp->pr_size >= sizeof(struct pool_cache_item));
cm = cpumem_get(&pool_caches);
pl_init(pp, &pp->pr_cache_lock);
arc4random_buf(pp->pr_cache_magic, sizeof(pp->pr_cache_magic));
TAILQ_INIT(&pp->pr_cache_lists);
pp->pr_cache_nitems = 0;
pp->pr_cache_timestamp = getnsecuptime();
pp->pr_cache_items = 8;
pp->pr_cache_contention = 0;
pp->pr_cache_ngc = 0;
CPUMEM_FOREACH(pc, &i, cm) {
pc->pc_actv = NULL;
pc->pc_nactv = 0;
pc->pc_prev = NULL;
pc->pc_nget = 0;
pc->pc_nfail = 0;
pc->pc_nput = 0;
pc->pc_nlget = 0;
pc->pc_nlfail = 0;
pc->pc_nlput = 0;
pc->pc_nout = 0;
}
membar_producer();
pp->pr_cache = cm;
}
static inline void
pool_cache_item_magic(struct pool *pp, struct pool_cache_item *ci)
{
unsigned long *entry = (unsigned long *)&ci->ci_nextl;
entry[0] = pp->pr_cache_magic[0] ^ (u_long)ci;
entry[1] = pp->pr_cache_magic[1] ^ (u_long)ci->ci_next;
}
static inline void
pool_cache_item_magic_check(struct pool *pp, struct pool_cache_item *ci)
{
unsigned long *entry;
unsigned long val;
entry = (unsigned long *)&ci->ci_nextl;
val = pp->pr_cache_magic[0] ^ (u_long)ci;
if (*entry != val)
goto fail;
entry++;
val = pp->pr_cache_magic[1] ^ (u_long)ci->ci_next;
if (*entry != val)
goto fail;
return;
fail:
panic("%s: %s cpu free list modified: item addr %p+%zu 0x%lx!=0x%lx",
__func__, pp->pr_wchan, ci, (caddr_t)entry - (caddr_t)ci,
*entry, val);
}
static inline void
pool_list_enter(struct pool *pp)
{
if (pl_enter_try(pp, &pp->pr_cache_lock) == 0) {
pl_enter(pp, &pp->pr_cache_lock);
pp->pr_cache_contention++;
}
}
static inline void
pool_list_leave(struct pool *pp)
{
pl_leave(pp, &pp->pr_cache_lock);
}
static inline struct pool_cache_item *
pool_cache_list_alloc(struct pool *pp, struct pool_cache *pc)
{
struct pool_cache_item *pl;
pool_list_enter(pp);
pl = TAILQ_FIRST(&pp->pr_cache_lists);
if (pl != NULL) {
TAILQ_REMOVE(&pp->pr_cache_lists, pl, ci_nextl);
pp->pr_cache_nitems -= POOL_CACHE_ITEM_NITEMS(pl);
pool_cache_item_magic(pp, pl);
pc->pc_nlget++;
} else
pc->pc_nlfail++;
/* fold this cpus nout into the global while we have the lock */
pp->pr_cache_nout += pc->pc_nout;
pc->pc_nout = 0;
pool_list_leave(pp);
return (pl);
}
static inline void
pool_cache_list_free(struct pool *pp, struct pool_cache *pc,
struct pool_cache_item *ci)
{
pool_list_enter(pp);
if (TAILQ_EMPTY(&pp->pr_cache_lists))
pp->pr_cache_timestamp = getnsecuptime();
pp->pr_cache_nitems += POOL_CACHE_ITEM_NITEMS(ci);
TAILQ_INSERT_TAIL(&pp->pr_cache_lists, ci, ci_nextl);
pc->pc_nlput++;
/* fold this cpus nout into the global while we have the lock */
pp->pr_cache_nout += pc->pc_nout;
pc->pc_nout = 0;
pool_list_leave(pp);
}
static inline struct pool_cache *
pool_cache_enter(struct pool *pp, int *s)
{
struct pool_cache *pc;
pc = cpumem_enter(pp->pr_cache);
*s = splraise(pp->pr_ipl);
pc->pc_gen++;
return (pc);
}
static inline void
pool_cache_leave(struct pool *pp, struct pool_cache *pc, int s)
{
pc->pc_gen++;
splx(s);
cpumem_leave(pp->pr_cache, pc);
}
void *
pool_cache_get(struct pool *pp)
{
struct pool_cache *pc;
struct pool_cache_item *ci;
int s;
pc = pool_cache_enter(pp, &s);
if (pc->pc_actv != NULL) {
ci = pc->pc_actv;
} else if (pc->pc_prev != NULL) {
ci = pc->pc_prev;
pc->pc_prev = NULL;
} else if ((ci = pool_cache_list_alloc(pp, pc)) == NULL) {
pc->pc_nfail++;
goto done;
}
pool_cache_item_magic_check(pp, ci);
#ifdef DIAGNOSTIC
if (pool_debug && POOL_CACHE_ITEM_POISONED(ci)) {
size_t pidx;
uint32_t pval;
if (poison_check(ci + 1, pp->pr_size - sizeof(*ci),
&pidx, &pval)) {
int *ip = (int *)(ci + 1);
ip += pidx;
panic("%s: %s cpu free list modified: "
"item addr %p+%zu 0x%x!=0x%x",
__func__, pp->pr_wchan, ci,
(caddr_t)ip - (caddr_t)ci, *ip, pval);
}
}
#endif
pc->pc_actv = ci->ci_next;
pc->pc_nactv = POOL_CACHE_ITEM_NITEMS(ci) - 1;
pc->pc_nget++;
pc->pc_nout++;
done:
pool_cache_leave(pp, pc, s);
return (ci);
}
void
pool_cache_put(struct pool *pp, void *v)
{
struct pool_cache *pc;
struct pool_cache_item *ci = v;
unsigned long nitems;
int s;
#ifdef DIAGNOSTIC
int poison = pool_debug && pp->pr_size > sizeof(*ci);
if (poison)
poison_mem(ci + 1, pp->pr_size - sizeof(*ci));
#endif
pc = pool_cache_enter(pp, &s);
nitems = pc->pc_nactv;
if (nitems >= pp->pr_cache_items) {
if (pc->pc_prev != NULL)
pool_cache_list_free(pp, pc, pc->pc_prev);
pc->pc_prev = pc->pc_actv;
pc->pc_actv = NULL;
pc->pc_nactv = 0;
nitems = 0;
}
ci->ci_next = pc->pc_actv;
ci->ci_nitems = ++nitems;
#ifdef DIAGNOSTIC
ci->ci_nitems |= poison ? POOL_CACHE_ITEM_NITEMS_POISON : 0;
#endif
pool_cache_item_magic(pp, ci);
pc->pc_actv = ci;
pc->pc_nactv = nitems;
pc->pc_nput++;
pc->pc_nout--;
pool_cache_leave(pp, pc, s);
}
struct pool_cache_item *
pool_cache_list_put(struct pool *pp, struct pool_cache_item *pl)
{
struct pool_cache_item *rpl, *next;
if (pl == NULL)
return (NULL);
rpl = TAILQ_NEXT(pl, ci_nextl);
pl_enter(pp, &pp->pr_lock);
do {
next = pl->ci_next;
pool_do_put(pp, pl);
pl = next;
} while (pl != NULL);
pl_leave(pp, &pp->pr_lock);
return (rpl);
}
void
pool_cache_destroy(struct pool *pp)
{
struct pool_cache *pc;
struct pool_cache_item *pl;
struct cpumem_iter i;
struct cpumem *cm;
rw_enter_write(&pool_lock); /* serialise with the gc */
cm = pp->pr_cache;
pp->pr_cache = NULL; /* make pool_put avoid the cache */
rw_exit_write(&pool_lock);
CPUMEM_FOREACH(pc, &i, cm) {
pool_cache_list_put(pp, pc->pc_actv);
pool_cache_list_put(pp, pc->pc_prev);
}
cpumem_put(&pool_caches, cm);
pl = TAILQ_FIRST(&pp->pr_cache_lists);
while (pl != NULL)
pl = pool_cache_list_put(pp, pl);
}
void
pool_cache_gc(struct pool *pp)
{
unsigned int contention, delta;
if (getnsecuptime() - pp->pr_cache_timestamp > POOL_WAIT_GC &&
!TAILQ_EMPTY(&pp->pr_cache_lists) &&
pl_enter_try(pp, &pp->pr_cache_lock)) {
struct pool_cache_item *pl = NULL;
pl = TAILQ_FIRST(&pp->pr_cache_lists);
if (pl != NULL) {
TAILQ_REMOVE(&pp->pr_cache_lists, pl, ci_nextl);
pp->pr_cache_nitems -= POOL_CACHE_ITEM_NITEMS(pl);
pp->pr_cache_timestamp = getnsecuptime();
pp->pr_cache_ngc++;
}
pl_leave(pp, &pp->pr_cache_lock);
pool_cache_list_put(pp, pl);
}
/*
* if there's a lot of contention on the pr_cache_mtx then consider
* growing the length of the list to reduce the need to access the
* global pool.
*/
contention = pp->pr_cache_contention;
delta = contention - pp->pr_cache_contention_prev;
if (delta > 8 /* magic */) {
if ((ncpusfound * 8 * 2) <= pp->pr_cache_nitems)
pp->pr_cache_items += 8;
} else if (delta == 0) {
if (pp->pr_cache_items > 8)
pp->pr_cache_items--;
}
pp->pr_cache_contention_prev = contention;
}
void
pool_cache_pool_info(struct pool *pp, struct kinfo_pool *pi)
{
struct pool_cache *pc;
struct cpumem_iter i;
if (pp->pr_cache == NULL)
return;
/* loop through the caches twice to collect stats */
/* once without the lock so we can yield while reading nget/nput */
CPUMEM_FOREACH(pc, &i, pp->pr_cache) {
uint64_t gen, nget, nput;
do {
while ((gen = pc->pc_gen) & 1)
yield();
nget = pc->pc_nget;
nput = pc->pc_nput;
} while (gen != pc->pc_gen);
pi->pr_nget += nget;
pi->pr_nput += nput;
}
/* and once with the mtx so we can get consistent nout values */
pl_enter(pp, &pp->pr_cache_lock);
CPUMEM_FOREACH(pc, &i, pp->pr_cache)
pi->pr_nout += pc->pc_nout;
pi->pr_nout += pp->pr_cache_nout;
pl_leave(pp, &pp->pr_cache_lock);
}
int
pool_cache_info(struct pool *pp, void *oldp, size_t *oldlenp)
{
struct kinfo_pool_cache kpc;
if (pp->pr_cache == NULL)
return (EOPNOTSUPP);
memset(&kpc, 0, sizeof(kpc)); /* don't leak padding */
pl_enter(pp, &pp->pr_cache_lock);
kpc.pr_ngc = pp->pr_cache_ngc;
kpc.pr_len = pp->pr_cache_items;
kpc.pr_nitems = pp->pr_cache_nitems;
kpc.pr_contention = pp->pr_cache_contention;
pl_leave(pp, &pp->pr_cache_lock);
return (sysctl_rdstruct(oldp, oldlenp, NULL, &kpc, sizeof(kpc)));
}
int
pool_cache_cpus_info(struct pool *pp, void *oldp, size_t *oldlenp)
{
struct pool_cache *pc;
struct kinfo_pool_cache_cpu *kpcc, *info;
unsigned int cpu = 0;
struct cpumem_iter i;
int error = 0;
size_t len;
if (pp->pr_cache == NULL)
return (EOPNOTSUPP);
if (*oldlenp % sizeof(*kpcc))
return (EINVAL);
kpcc = mallocarray(ncpusfound, sizeof(*kpcc), M_TEMP,
M_WAITOK|M_CANFAIL|M_ZERO);
if (kpcc == NULL)
return (EIO);
len = ncpusfound * sizeof(*kpcc);
CPUMEM_FOREACH(pc, &i, pp->pr_cache) {
uint64_t gen;
if (cpu >= ncpusfound) {
error = EIO;
goto err;
}
info = &kpcc[cpu];
info->pr_cpu = cpu;
do {
while ((gen = pc->pc_gen) & 1)
yield();
info->pr_nget = pc->pc_nget;
info->pr_nfail = pc->pc_nfail;
info->pr_nput = pc->pc_nput;
info->pr_nlget = pc->pc_nlget;
info->pr_nlfail = pc->pc_nlfail;
info->pr_nlput = pc->pc_nlput;
} while (gen != pc->pc_gen);
cpu++;
}
error = sysctl_rdstruct(oldp, oldlenp, NULL, kpcc, len);
err:
free(kpcc, M_TEMP, len);
return (error);
}
#else /* MULTIPROCESSOR */
void
pool_cache_init(struct pool *pp)
{
/* nop */
}
void
pool_cache_pool_info(struct pool *pp, struct kinfo_pool *pi)
{
/* nop */
}
int
pool_cache_info(struct pool *pp, void *oldp, size_t *oldlenp)
{
return (EOPNOTSUPP);
}
int
pool_cache_cpus_info(struct pool *pp, void *oldp, size_t *oldlenp)
{
return (EOPNOTSUPP);
}
#endif /* MULTIPROCESSOR */
void
pool_lock_mtx_init(struct pool *pp, union pool_lock *lock,
const struct lock_type *type)
{
_mtx_init_flags(&lock->prl_mtx, pp->pr_ipl, pp->pr_wchan, 0, type);
}
void
pool_lock_mtx_enter(union pool_lock *lock)
{
mtx_enter(&lock->prl_mtx);
}
int
pool_lock_mtx_enter_try(union pool_lock *lock)
{
return (mtx_enter_try(&lock->prl_mtx));
}
void
pool_lock_mtx_leave(union pool_lock *lock)
{
mtx_leave(&lock->prl_mtx);
}
void
pool_lock_mtx_assert_locked(union pool_lock *lock)
{
MUTEX_ASSERT_LOCKED(&lock->prl_mtx);
}
void
pool_lock_mtx_assert_unlocked(union pool_lock *lock)
{
MUTEX_ASSERT_UNLOCKED(&lock->prl_mtx);
}
int
pool_lock_mtx_sleep(void *ident, union pool_lock *lock, int priority,
const char *wmesg)
{
return msleep_nsec(ident, &lock->prl_mtx, priority, wmesg, INFSLP);
}
static const struct pool_lock_ops pool_lock_ops_mtx = {
pool_lock_mtx_init,
pool_lock_mtx_enter,
pool_lock_mtx_enter_try,
pool_lock_mtx_leave,
pool_lock_mtx_assert_locked,
pool_lock_mtx_assert_unlocked,
pool_lock_mtx_sleep,
};
void
pool_lock_rw_init(struct pool *pp, union pool_lock *lock,
const struct lock_type *type)
{
_rw_init_flags(&lock->prl_rwlock, pp->pr_wchan, 0, type);
}
void
pool_lock_rw_enter(union pool_lock *lock)
{
rw_enter_write(&lock->prl_rwlock);
}
int
pool_lock_rw_enter_try(union pool_lock *lock)
{
return (rw_enter(&lock->prl_rwlock, RW_WRITE | RW_NOSLEEP) == 0);
}
void
pool_lock_rw_leave(union pool_lock *lock)
{
rw_exit_write(&lock->prl_rwlock);
}
void
pool_lock_rw_assert_locked(union pool_lock *lock)
{
rw_assert_wrlock(&lock->prl_rwlock);
}
void
pool_lock_rw_assert_unlocked(union pool_lock *lock)
{
KASSERT(rw_status(&lock->prl_rwlock) != RW_WRITE);
}
int
pool_lock_rw_sleep(void *ident, union pool_lock *lock, int priority,
const char *wmesg)
{
return rwsleep_nsec(ident, &lock->prl_rwlock, priority, wmesg, INFSLP);
}
static const struct pool_lock_ops pool_lock_ops_rw = {
pool_lock_rw_init,
pool_lock_rw_enter,
pool_lock_rw_enter_try,
pool_lock_rw_leave,
pool_lock_rw_assert_locked,
pool_lock_rw_assert_unlocked,
pool_lock_rw_sleep,
};
447
209
22
246
91
426
123
328
1
353
343
243
195
2
2
2
2
44
44
44
384
766
602
299
18
35
49
34
34
30
34
1
6
24
24
22
1
24
24
1
1
24
46
45
18
38
2
11
37
24
38
47
47
33
32
47
47
1
27
16
36
36
35
1
30
31
6
36
36
35
33
2
44
4
40
39
19
37
48
48
48
18
36
1
37
38
31
25
31
25
18
21
16
40
33
31
41
33
37
33
39
20
24
23
27
10
5
14
4
42
27
5
28
1
33
34
35
29
35
29
35
35
9
6
6
6
6
3
3
3
38
38
38
18
18
9
9
1
1
3
57
31
18
11
8
5
1
5
1
5
3
3
29
75
64
8
41
5
1
4
77
77
55
77
77
1
77
77
77
76
19
60
2
1
17
18
51
26
54
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
/* $OpenBSD: route.c,v 1.414 2022/08/29 07:51:45 bluhm Exp $ */
/* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1980, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)route.c 8.2 (Berkeley) 11/15/93
*/
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/timeout.h>
#include <sys/domain.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/pool.h>
#include <sys/atomic.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip_var.h>
#include <netinet/in_var.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_var.h>
#endif
#ifdef MPLS
#include <netmpls/mpls.h>
#endif
#ifdef BFD
#include <net/bfd.h>
#endif
#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
/* Give some jitter to hash, to avoid synchronization between routers. */
static uint32_t rt_hashjitter;
extern unsigned int rtmap_limit;
struct cpumem * rtcounters;
int rttrash; /* routes not in table but not freed */
struct pool rtentry_pool; /* pool for rtentry structures */
struct pool rttimer_pool; /* pool for rttimer structures */
int rt_setgwroute(struct rtentry *, u_int);
void rt_putgwroute(struct rtentry *);
int rtflushclone1(struct rtentry *, void *, u_int);
int rtflushclone(struct rtentry *, unsigned int);
int rt_ifa_purge_walker(struct rtentry *, void *, unsigned int);
struct rtentry *rt_match(struct sockaddr *, uint32_t *, int, unsigned int);
int rt_clone(struct rtentry **, struct sockaddr *, unsigned int);
struct sockaddr *rt_plentosa(sa_family_t, int, struct sockaddr_in6 *);
static int rt_copysa(struct sockaddr *, struct sockaddr *, struct sockaddr **);
#define LABELID_MAX 50000
struct rt_label {
TAILQ_ENTRY(rt_label) rtl_entry;
char rtl_name[RTLABEL_LEN];
u_int16_t rtl_id;
int rtl_ref;
};
TAILQ_HEAD(rt_labels, rt_label) rt_labels = TAILQ_HEAD_INITIALIZER(rt_labels);
void
route_init(void)
{
rtcounters = counters_alloc(rts_ncounters);
pool_init(&rtentry_pool, sizeof(struct rtentry), 0, IPL_MPFLOOR, 0,
"rtentry", NULL);
while (rt_hashjitter == 0)
rt_hashjitter = arc4random();
#ifdef BFD
bfdinit();
#endif
}
/*
* Returns 1 if the (cached) ``rt'' entry is still valid, 0 otherwise.
*/
int
rtisvalid(struct rtentry *rt)
{
if (rt == NULL)
return (0);
if (!ISSET(rt->rt_flags, RTF_UP))
return (0);
if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
KASSERT(rt->rt_gwroute != NULL);
KASSERT(!ISSET(rt->rt_gwroute->rt_flags, RTF_GATEWAY));
if (!ISSET(rt->rt_gwroute->rt_flags, RTF_UP))
return (0);
}
return (1);
}
/*
* Do the actual lookup for rtalloc(9), do not use directly!
*
* Return the best matching entry for the destination ``dst''.
*
* "RT_RESOLVE" means that a corresponding L2 entry should
* be added to the routing table and resolved (via ARP or
* NDP), if it does not exist.
*/
struct rtentry *
rt_match(struct sockaddr *dst, uint32_t *src, int flags, unsigned int tableid)
{
struct rtentry *rt = NULL;
rt = rtable_match(tableid, dst, src);
if (rt == NULL) {
rtstat_inc(rts_unreach);
return (NULL);
}
if (ISSET(rt->rt_flags, RTF_CLONING) && ISSET(flags, RT_RESOLVE))
rt_clone(&rt, dst, tableid);
rt->rt_use++;
return (rt);
}
int
rt_clone(struct rtentry **rtp, struct sockaddr *dst, unsigned int rtableid)
{
struct rt_addrinfo info;
struct rtentry *rt = *rtp;
int error = 0;
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_DST] = dst;
/*
* The priority of cloned route should be different
* to avoid conflict with /32 cloning routes.
*
* It should also be higher to let the ARP layer find
* cloned routes instead of the cloning one.
*/
KERNEL_LOCK();
error = rtrequest(RTM_RESOLVE, &info, rt->rt_priority - 1, &rt,
rtableid);
KERNEL_UNLOCK();
if (error) {
rtm_miss(RTM_MISS, &info, 0, RTP_NONE, 0, error, rtableid);
} else {
/* Inform listeners of the new route */
rtm_send(rt, RTM_ADD, 0, rtableid);
rtfree(*rtp);
*rtp = rt;
}
return (error);
}
/*
* Originated from bridge_hash() in if_bridge.c
*/
#define mix(a, b, c) do { \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
} while (0)
int
rt_hash(struct rtentry *rt, struct sockaddr *dst, uint32_t *src)
{
uint32_t a, b, c;
if (src == NULL || !rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_MPATH))
return (-1);
a = b = 0x9e3779b9;
c = rt_hashjitter;
switch (dst->sa_family) {
case AF_INET:
{
struct sockaddr_in *sin;
if (!ipmultipath)
return (-1);
sin = satosin(dst);
a += sin->sin_addr.s_addr;
b += src[0];
mix(a, b, c);
break;
}
#ifdef INET6
case AF_INET6:
{
struct sockaddr_in6 *sin6;
if (!ip6_multipath)
return (-1);
sin6 = satosin6(dst);
a += sin6->sin6_addr.s6_addr32[0];
b += sin6->sin6_addr.s6_addr32[2];
c += src[0];
mix(a, b, c);
a += sin6->sin6_addr.s6_addr32[1];
b += sin6->sin6_addr.s6_addr32[3];
c += src[1];
mix(a, b, c);
a += sin6->sin6_addr.s6_addr32[2];
b += sin6->sin6_addr.s6_addr32[1];
c += src[2];
mix(a, b, c);
a += sin6->sin6_addr.s6_addr32[3];
b += sin6->sin6_addr.s6_addr32[0];
c += src[3];
mix(a, b, c);
break;
}
#endif /* INET6 */
}
return (c & 0xffff);
}
/*
* Allocate a route, potentially using multipath to select the peer.
*/
struct rtentry *
rtalloc_mpath(struct sockaddr *dst, uint32_t *src, unsigned int rtableid)
{
return (rt_match(dst, src, RT_RESOLVE, rtableid));
}
/*
* Look in the routing table for the best matching entry for
* ``dst''.
*
* If a route with a gateway is found and its next hop is no
* longer valid, try to cache it.
*/
struct rtentry *
rtalloc(struct sockaddr *dst, int flags, unsigned int rtableid)
{
return (rt_match(dst, NULL, flags, rtableid));
}
/*
* Cache the route entry corresponding to a reachable next hop in
* the gateway entry ``rt''.
*/
int
rt_setgwroute(struct rtentry *rt, u_int rtableid)
{
struct rtentry *prt, *nhrt;
unsigned int rdomain = rtable_l2(rtableid);
int error;
NET_ASSERT_LOCKED();
KASSERT(ISSET(rt->rt_flags, RTF_GATEWAY));
/* If we cannot find a valid next hop bail. */
nhrt = rt_match(rt->rt_gateway, NULL, RT_RESOLVE, rdomain);
if (nhrt == NULL)
return (ENOENT);
/* Next hop entry must be on the same interface. */
if (nhrt->rt_ifidx != rt->rt_ifidx) {
struct sockaddr_in6 sa_mask;
if (!ISSET(nhrt->rt_flags, RTF_LLINFO) ||
!ISSET(nhrt->rt_flags, RTF_CLONED)) {
rtfree(nhrt);
return (EHOSTUNREACH);
}
/*
* We found a L2 entry, so we might have multiple
* RTF_CLONING routes for the same subnet. Query
* the first route of the multipath chain and iterate
* until we find the correct one.
*/
prt = rtable_lookup(rdomain, rt_key(nhrt->rt_parent),
rt_plen2mask(nhrt->rt_parent, &sa_mask), NULL, RTP_ANY);
rtfree(nhrt);
while (prt != NULL && prt->rt_ifidx != rt->rt_ifidx)
prt = rtable_iterate(prt);
/* We found nothing or a non-cloning MPATH route. */
if (prt == NULL || !ISSET(prt->rt_flags, RTF_CLONING)) {
rtfree(prt);
return (EHOSTUNREACH);
}
error = rt_clone(&prt, rt->rt_gateway, rdomain);
if (error) {
rtfree(prt);
return (error);
}
nhrt = prt;
}
/*
* Next hop must be reachable, this also prevents rtentry
* loops for example when rt->rt_gwroute points to rt.
*/
if (ISSET(nhrt->rt_flags, RTF_CLONING|RTF_GATEWAY)) {
rtfree(nhrt);
return (ENETUNREACH);
}
/* Next hop is valid so remove possible old cache. */
rt_putgwroute(rt);
KASSERT(rt->rt_gwroute == NULL);
/*
* If the MTU of next hop is 0, this will reset the MTU of the
* route to run PMTUD again from scratch.
*/
if (!ISSET(rt->rt_locks, RTV_MTU) && (rt->rt_mtu > nhrt->rt_mtu))
rt->rt_mtu = nhrt->rt_mtu;
/*
* To avoid reference counting problems when writing link-layer
* addresses in an outgoing packet, we ensure that the lifetime
* of a cached entry is greater than the bigger lifetime of the
* gateway entries it is pointed by.
*/
nhrt->rt_flags |= RTF_CACHED;
nhrt->rt_cachecnt++;
rt->rt_gwroute = nhrt;
return (0);
}
/*
* Invalidate the cached route entry of the gateway entry ``rt''.
*/
void
rt_putgwroute(struct rtentry *rt)
{
struct rtentry *nhrt = rt->rt_gwroute;
NET_ASSERT_LOCKED();
if (!ISSET(rt->rt_flags, RTF_GATEWAY) || nhrt == NULL)
return;
KASSERT(ISSET(nhrt->rt_flags, RTF_CACHED));
KASSERT(nhrt->rt_cachecnt > 0);
--nhrt->rt_cachecnt;
if (nhrt->rt_cachecnt == 0)
nhrt->rt_flags &= ~RTF_CACHED;
rtfree(rt->rt_gwroute);
rt->rt_gwroute = NULL;
}
void
rtref(struct rtentry *rt)
{
refcnt_take(&rt->rt_refcnt);
}
void
rtfree(struct rtentry *rt)
{
if (rt == NULL)
return;
if (refcnt_rele(&rt->rt_refcnt) == 0)
return;
KASSERT(!ISSET(rt->rt_flags, RTF_UP));
KASSERT(!RT_ROOT(rt));
atomic_dec_int(&rttrash);
KERNEL_LOCK();
rt_timer_remove_all(rt);
ifafree(rt->rt_ifa);
rtlabel_unref(rt->rt_labelid);
#ifdef MPLS
rt_mpls_clear(rt);
#endif
free(rt->rt_gateway, M_RTABLE, ROUNDUP(rt->rt_gateway->sa_len));
free(rt_key(rt), M_RTABLE, rt_key(rt)->sa_len);
KERNEL_UNLOCK();
pool_put(&rtentry_pool, rt);
}
struct ifaddr *
ifaref(struct ifaddr *ifa)
{
refcnt_take(&ifa->ifa_refcnt);
return ifa;
}
void
ifafree(struct ifaddr *ifa)
{
if (refcnt_rele(&ifa->ifa_refcnt) == 0)
return;
free(ifa, M_IFADDR, 0);
}
/*
* Force a routing table entry to the specified
* destination to go through the given gateway.
* Normally called as a result of a routing redirect
* message from the network layer.
*/
void
rtredirect(struct sockaddr *dst, struct sockaddr *gateway,
struct sockaddr *src, struct rtentry **rtp, unsigned int rdomain)
{
struct rtentry *rt;
int error = 0;
enum rtstat_counters stat = rts_ncounters;
struct rt_addrinfo info;
struct ifaddr *ifa;
unsigned int ifidx = 0;
int flags = RTF_GATEWAY|RTF_HOST;
uint8_t prio = RTP_NONE;
NET_ASSERT_LOCKED();
/* verify the gateway is directly reachable */
rt = rtalloc(gateway, 0, rdomain);
if (!rtisvalid(rt) || ISSET(rt->rt_flags, RTF_GATEWAY)) {
rtfree(rt);
error = ENETUNREACH;
goto out;
}
ifidx = rt->rt_ifidx;
ifa = rt->rt_ifa;
rtfree(rt);
rt = NULL;
rt = rtable_lookup(rdomain, dst, NULL, NULL, RTP_ANY);
/*
* If the redirect isn't from our current router for this dst,
* it's either old or wrong. If it redirects us to ourselves,
* we have a routing loop, perhaps as a result of an interface
* going down recently.
*/
#define equal(a1, a2) \
((a1)->sa_len == (a2)->sa_len && \
bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
if (rt != NULL && (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
error = EINVAL;
else if (ifa_ifwithaddr(gateway, rdomain) != NULL ||
(gateway->sa_family == AF_INET &&
in_broadcast(satosin(gateway)->sin_addr, rdomain)))
error = EHOSTUNREACH;
if (error)
goto done;
/*
* Create a new entry if we just got back a wildcard entry
* or the lookup failed. This is necessary for hosts
* which use routing redirects generated by smart gateways
* to dynamically build the routing tables.
*/
if (rt == NULL)
goto create;
/*
* Don't listen to the redirect if it's
* for a route to an interface.
*/
if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
if (!ISSET(rt->rt_flags, RTF_HOST)) {
/*
* Changing from route to net => route to host.
* Create new route, rather than smashing route to net.
*/
create:
rtfree(rt);
flags |= RTF_DYNAMIC;
bzero(&info, sizeof(info));
info.rti_info[RTAX_DST] = dst;
info.rti_info[RTAX_GATEWAY] = gateway;
info.rti_ifa = ifa;
info.rti_flags = flags;
rt = NULL;
error = rtrequest(RTM_ADD, &info, RTP_DEFAULT, &rt,
rdomain);
if (error == 0) {
flags = rt->rt_flags;
prio = rt->rt_priority;
}
stat = rts_dynamic;
} else {
/*
* Smash the current notion of the gateway to
* this destination. Should check about netmask!!!
*/
rt->rt_flags |= RTF_MODIFIED;
flags |= RTF_MODIFIED;
prio = rt->rt_priority;
stat = rts_newgateway;
rt_setgate(rt, gateway, rdomain);
}
} else
error = EHOSTUNREACH;
done:
if (rt) {
if (rtp && !error)
*rtp = rt;
else
rtfree(rt);
}
out:
if (error)
rtstat_inc(rts_badredirect);
else if (stat != rts_ncounters)
rtstat_inc(stat);
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] = dst;
info.rti_info[RTAX_GATEWAY] = gateway;
info.rti_info[RTAX_AUTHOR] = src;
rtm_miss(RTM_REDIRECT, &info, flags, prio, ifidx, error, rdomain);
}
/*
* Delete a route and generate a message
*/
int
rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
{
int error;
struct rt_addrinfo info;
struct sockaddr_rtlabel sa_rl;
struct sockaddr_in6 sa_mask;
KASSERT(rt->rt_ifidx == ifp->if_index);
/*
* Request the new route so that the entry is not actually
* deleted. That will allow the information being reported to
* be accurate (and consistent with route_output()).
*/
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(rt);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
if (!ISSET(rt->rt_flags, RTF_HOST))
info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
info.rti_info[RTAX_LABEL] = rtlabel_id2sa(rt->rt_labelid, &sa_rl);
info.rti_flags = rt->rt_flags;
info.rti_info[RTAX_IFP] = sdltosa(ifp->if_sadl);
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
error = rtrequest_delete(&info, rt->rt_priority, ifp, &rt, tableid);
rtm_miss(RTM_DELETE, &info, info.rti_flags, rt->rt_priority,
rt->rt_ifidx, error, tableid);
if (error == 0)
rtfree(rt);
return (error);
}
static inline int
rtequal(struct rtentry *a, struct rtentry *b)
{
if (a == b)
return 1;
if (memcmp(rt_key(a), rt_key(b), rt_key(a)->sa_len) == 0 &&
rt_plen(a) == rt_plen(b))
return 1;
else
return 0;
}
int
rtflushclone1(struct rtentry *rt, void *arg, u_int id)
{
struct rtentry *cloningrt = arg;
struct ifnet *ifp;
if (!ISSET(rt->rt_flags, RTF_CLONED))
return 0;
/* Cached route must stay alive as long as their parent are alive. */
if (ISSET(rt->rt_flags, RTF_CACHED) && (rt->rt_parent != cloningrt))
return 0;
if (!rtequal(rt->rt_parent, cloningrt))
return 0;
/*
* This happens when an interface with a RTF_CLONING route is
* being detached. In this case it's safe to bail because all
* the routes are being purged by rt_ifa_purge().
*/
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return 0;
if_put(ifp);
return EEXIST;
}
int
rtflushclone(struct rtentry *parent, unsigned int rtableid)
{
struct rtentry *rt = NULL;
struct ifnet *ifp;
int error;
#ifdef DIAGNOSTIC
if (!parent || (parent->rt_flags & RTF_CLONING) == 0)
panic("rtflushclone: called with a non-cloning route");
#endif
do {
error = rtable_walk(rtableid, rt_key(parent)->sa_family, &rt,
rtflushclone1, parent);
if (rt != NULL && error == EEXIST) {
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
error = EAGAIN;
} else {
error = rtdeletemsg(rt, ifp, rtableid);
if (error == 0)
error = EAGAIN;
if_put(ifp);
}
}
rtfree(rt);
rt = NULL;
} while (error == EAGAIN);
return error;
}
int
rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
struct rtentry **ret_nrt, u_int tableid)
{
struct rtentry *rt;
int error;
NET_ASSERT_LOCKED();
if (!rtable_exists(tableid))
return (EAFNOSUPPORT);
rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio);
if (rt == NULL)
return (ESRCH);
/* Make sure that's the route the caller want to delete. */
if (ifp != NULL && ifp->if_index != rt->rt_ifidx) {
rtfree(rt);
return (ESRCH);
}
#ifdef BFD
if (ISSET(rt->rt_flags, RTF_BFD))
bfdclear(rt);
#endif
error = rtable_delete(tableid, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], rt);
if (error != 0) {
rtfree(rt);
return (ESRCH);
}
/* Release next hop cache before flushing cloned entries. */
rt_putgwroute(rt);
/* Clean up any cloned children. */
if (ISSET(rt->rt_flags, RTF_CLONING))
rtflushclone(rt, tableid);
rtfree(rt->rt_parent);
rt->rt_parent = NULL;
rt->rt_flags &= ~RTF_UP;
KASSERT(ifp->if_index == rt->rt_ifidx);
ifp->if_rtrequest(ifp, RTM_DELETE, rt);
atomic_inc_int(&rttrash);
if (ret_nrt != NULL)
*ret_nrt = rt;
else
rtfree(rt);
return (0);
}
int
rtrequest(int req, struct rt_addrinfo *info, u_int8_t prio,
struct rtentry **ret_nrt, u_int tableid)
{
struct ifnet *ifp;
struct rtentry *rt, *crt;
struct ifaddr *ifa;
struct sockaddr *ndst;
struct sockaddr_rtlabel *sa_rl, sa_rl2;
struct sockaddr_dl sa_dl = { sizeof(sa_dl), AF_LINK };
int error;
NET_ASSERT_LOCKED();
if (!rtable_exists(tableid))
return (EAFNOSUPPORT);
if (info->rti_flags & RTF_HOST)
info->rti_info[RTAX_NETMASK] = NULL;
switch (req) {
case RTM_DELETE:
return (EINVAL);
case RTM_RESOLVE:
if (ret_nrt == NULL || (rt = *ret_nrt) == NULL)
return (EINVAL);
if ((rt->rt_flags & RTF_CLONING) == 0)
return (EINVAL);
KASSERT(rt->rt_ifa->ifa_ifp != NULL);
info->rti_ifa = rt->rt_ifa;
info->rti_flags = rt->rt_flags | (RTF_CLONED|RTF_HOST);
info->rti_flags &= ~(RTF_CLONING|RTF_CONNECTED|RTF_STATIC);
info->rti_info[RTAX_GATEWAY] = sdltosa(&sa_dl);
info->rti_info[RTAX_LABEL] =
rtlabel_id2sa(rt->rt_labelid, &sa_rl2);
/* FALLTHROUGH */
case RTM_ADD:
if (info->rti_ifa == NULL)
return (EINVAL);
ifa = info->rti_ifa;
ifp = ifa->ifa_ifp;
if (prio == 0)
prio = ifp->if_priority + RTP_STATIC;
error = rt_copysa(info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], &ndst);
if (error)
return (error);
rt = pool_get(&rtentry_pool, PR_NOWAIT | PR_ZERO);
if (rt == NULL) {
free(ndst, M_RTABLE, ndst->sa_len);
return (ENOBUFS);
}
refcnt_init(&rt->rt_refcnt);
rt->rt_flags = info->rti_flags | RTF_UP;
rt->rt_priority = prio; /* init routing priority */
LIST_INIT(&rt->rt_timer);
/* Check the link state if the table supports it. */
if (rtable_mpath_capable(tableid, ndst->sa_family) &&
!ISSET(rt->rt_flags, RTF_LOCAL) &&
(!LINK_STATE_IS_UP(ifp->if_link_state) ||
!ISSET(ifp->if_flags, IFF_UP))) {
rt->rt_flags &= ~RTF_UP;
rt->rt_priority |= RTP_DOWN;
}
if (info->rti_info[RTAX_LABEL] != NULL) {
sa_rl = (struct sockaddr_rtlabel *)
info->rti_info[RTAX_LABEL];
rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label);
}
#ifdef MPLS
/* We have to allocate additional space for MPLS infos */
if (info->rti_flags & RTF_MPLS &&
(info->rti_info[RTAX_SRC] != NULL ||
info->rti_info[RTAX_DST]->sa_family == AF_MPLS)) {
error = rt_mpls_set(rt, info->rti_info[RTAX_SRC],
info->rti_mpls);
if (error) {
free(ndst, M_RTABLE, ndst->sa_len);
pool_put(&rtentry_pool, rt);
return (error);
}
} else
rt_mpls_clear(rt);
#endif
rt->rt_ifa = ifaref(ifa);
rt->rt_ifidx = ifp->if_index;
/*
* Copy metrics and a back pointer from the cloned
* route's parent.
*/
if (ISSET(rt->rt_flags, RTF_CLONED)) {
rtref(*ret_nrt);
rt->rt_parent = *ret_nrt;
rt->rt_rmx = (*ret_nrt)->rt_rmx;
}
/*
* We must set rt->rt_gateway before adding ``rt'' to
* the routing table because the radix MPATH code use
* it to (re)order routes.
*/
if ((error = rt_setgate(rt, info->rti_info[RTAX_GATEWAY],
tableid))) {
ifafree(ifa);
rtfree(rt->rt_parent);
rt_putgwroute(rt);
free(rt->rt_gateway, M_RTABLE,
ROUNDUP(rt->rt_gateway->sa_len));
free(ndst, M_RTABLE, ndst->sa_len);
pool_put(&rtentry_pool, rt);
return (error);
}
error = rtable_insert(tableid, ndst,
info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
rt->rt_priority, rt);
if (error != 0 &&
(crt = rtable_match(tableid, ndst, NULL)) != NULL) {
/* overwrite cloned route */
if (ISSET(crt->rt_flags, RTF_CLONED) &&
!ISSET(crt->rt_flags, RTF_CACHED)) {
struct ifnet *cifp;
cifp = if_get(crt->rt_ifidx);
KASSERT(cifp != NULL);
rtdeletemsg(crt, cifp, tableid);
if_put(cifp);
error = rtable_insert(tableid, ndst,
info->rti_info[RTAX_NETMASK],
info->rti_info[RTAX_GATEWAY],
rt->rt_priority, rt);
}
rtfree(crt);
}
if (error != 0) {
ifafree(ifa);
rtfree(rt->rt_parent);
rt_putgwroute(rt);
free(rt->rt_gateway, M_RTABLE,
ROUNDUP(rt->rt_gateway->sa_len));
free(ndst, M_RTABLE, ndst->sa_len);
pool_put(&rtentry_pool, rt);
return (EEXIST);
}
ifp->if_rtrequest(ifp, req, rt);
if_group_routechange(info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK]);
if (ret_nrt != NULL)
*ret_nrt = rt;
else
rtfree(rt);
break;
}
return (0);
}
int
rt_setgate(struct rtentry *rt, struct sockaddr *gate, u_int rtableid)
{
int glen = ROUNDUP(gate->sa_len);
struct sockaddr *sa;
if (rt->rt_gateway == NULL || glen != ROUNDUP(rt->rt_gateway->sa_len)) {
sa = malloc(glen, M_RTABLE, M_NOWAIT);
if (sa == NULL)
return (ENOBUFS);
if (rt->rt_gateway != NULL) {
free(rt->rt_gateway, M_RTABLE,
ROUNDUP(rt->rt_gateway->sa_len));
}
rt->rt_gateway = sa;
}
memmove(rt->rt_gateway, gate, glen);
if (ISSET(rt->rt_flags, RTF_GATEWAY))
return (rt_setgwroute(rt, rtableid));
return (0);
}
/*
* Return the route entry containing the next hop link-layer
* address corresponding to ``rt''.
*/
struct rtentry *
rt_getll(struct rtentry *rt)
{
if (ISSET(rt->rt_flags, RTF_GATEWAY)) {
KASSERT(rt->rt_gwroute != NULL);
return (rt->rt_gwroute);
}
return (rt);
}
void
rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst,
struct sockaddr *netmask)
{
u_char *cp1 = (u_char *)src;
u_char *cp2 = (u_char *)dst;
u_char *cp3 = (u_char *)netmask;
u_char *cplim = cp2 + *cp3;
u_char *cplim2 = cp2 + *cp1;
*cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
cp3 += 2;
if (cplim > cplim2)
cplim = cplim2;
while (cp2 < cplim)
*cp2++ = *cp1++ & *cp3++;
if (cp2 < cplim2)
bzero(cp2, cplim2 - cp2);
}
/*
* allocate new sockaddr structure based on the user supplied src and mask
* that is useable for the routing table.
*/
static int
rt_copysa(struct sockaddr *src, struct sockaddr *mask, struct sockaddr **dst)
{
static const u_char maskarray[] = {
0x0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
struct sockaddr *ndst;
const struct domain *dp;
u_char *csrc, *cdst;
int i, plen;
for (i = 0; (dp = domains[i]) != NULL; i++) {
if (dp->dom_rtoffset == 0)
continue;
if (src->sa_family == dp->dom_family)
break;
}
if (dp == NULL)
return (EAFNOSUPPORT);
if (src->sa_len < dp->dom_sasize)
return (EINVAL);
plen = rtable_satoplen(src->sa_family, mask);
if (plen == -1)
return (EINVAL);
ndst = malloc(dp->dom_sasize, M_RTABLE, M_NOWAIT|M_ZERO);
if (ndst == NULL)
return (ENOBUFS);
ndst->sa_family = src->sa_family;
ndst->sa_len = dp->dom_sasize;
csrc = (u_char *)src + dp->dom_rtoffset;
cdst = (u_char *)ndst + dp->dom_rtoffset;
memcpy(cdst, csrc, plen / 8);
if (plen % 8 != 0)
cdst[plen / 8] = csrc[plen / 8] & maskarray[plen % 8];
*dst = ndst;
return (0);
}
int
rt_ifa_add(struct ifaddr *ifa, int flags, struct sockaddr *dst,
unsigned int rdomain)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct rtentry *rt;
struct sockaddr_rtlabel sa_rl;
struct rt_addrinfo info;
uint8_t prio = ifp->if_priority + RTP_STATIC;
int error;
KASSERT(rdomain == rtable_l2(rdomain));
memset(&info, 0, sizeof(info));
info.rti_ifa = ifa;
info.rti_flags = flags;
info.rti_info[RTAX_DST] = dst;
if (flags & RTF_LLINFO)
info.rti_info[RTAX_GATEWAY] = sdltosa(ifp->if_sadl);
else
info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
info.rti_info[RTAX_LABEL] = rtlabel_id2sa(ifp->if_rtlabelid, &sa_rl);
#ifdef MPLS
if ((flags & RTF_MPLS) == RTF_MPLS)
info.rti_mpls = MPLS_OP_POP;
#endif /* MPLS */
if ((flags & RTF_HOST) == 0)
info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
if (flags & (RTF_LOCAL|RTF_BROADCAST))
prio = RTP_LOCAL;
if (flags & RTF_CONNECTED)
prio = ifp->if_priority + RTP_CONNECTED;
error = rtrequest(RTM_ADD, &info, prio, &rt, rdomain);
if (error == 0) {
/*
* A local route is created for every address configured
* on an interface, so use this information to notify
* userland that a new address has been added.
*/
if (flags & RTF_LOCAL)
rtm_addr(RTM_NEWADDR, ifa);
rtm_send(rt, RTM_ADD, 0, rdomain);
rtfree(rt);
}
return (error);
}
int
rt_ifa_del(struct ifaddr *ifa, int flags, struct sockaddr *dst,
unsigned int rdomain)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct rtentry *rt;
struct mbuf *m = NULL;
struct sockaddr *deldst;
struct rt_addrinfo info;
struct sockaddr_rtlabel sa_rl;
uint8_t prio = ifp->if_priority + RTP_STATIC;
int error;
KASSERT(rdomain == rtable_l2(rdomain));
if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
m = m_get(M_DONTWAIT, MT_SONAME);
if (m == NULL)
return (ENOBUFS);
deldst = mtod(m, struct sockaddr *);
rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
dst = deldst;
}
memset(&info, 0, sizeof(info));
info.rti_ifa = ifa;
info.rti_flags = flags;
info.rti_info[RTAX_DST] = dst;
if ((flags & RTF_LLINFO) == 0)
info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
info.rti_info[RTAX_LABEL] = rtlabel_id2sa(ifp->if_rtlabelid, &sa_rl);
if ((flags & RTF_HOST) == 0)
info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
if (flags & (RTF_LOCAL|RTF_BROADCAST))
prio = RTP_LOCAL;
if (flags & RTF_CONNECTED)
prio = ifp->if_priority + RTP_CONNECTED;
rtable_clearsource(rdomain, ifa->ifa_addr);
error = rtrequest_delete(&info, prio, ifp, &rt, rdomain);
if (error == 0) {
rtm_send(rt, RTM_DELETE, 0, rdomain);
if (flags & RTF_LOCAL)
rtm_addr(RTM_DELADDR, ifa);
rtfree(rt);
}
m_free(m);
return (error);
}
/*
* Add ifa's address as a local rtentry.
*/
int
rt_ifa_addlocal(struct ifaddr *ifa)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct rtentry *rt;
u_int flags = RTF_HOST|RTF_LOCAL;
int error = 0;
/*
* If the configured address correspond to the magical "any"
* address do not add a local route entry because that might
* corrupt the routing tree which uses this value for the
* default routes.
*/
switch (ifa->ifa_addr->sa_family) {
case AF_INET:
if (satosin(ifa->ifa_addr)->sin_addr.s_addr == INADDR_ANY)
return (0);
break;
#ifdef INET6
case AF_INET6:
if (IN6_ARE_ADDR_EQUAL(&satosin6(ifa->ifa_addr)->sin6_addr,
&in6addr_any))
return (0);
break;
#endif
default:
break;
}
if (!ISSET(ifp->if_flags, (IFF_LOOPBACK|IFF_POINTOPOINT)))
flags |= RTF_LLINFO;
/* If there is no local entry, allocate one. */
rt = rtalloc(ifa->ifa_addr, 0, ifp->if_rdomain);
if (rt == NULL || ISSET(rt->rt_flags, flags) != flags) {
error = rt_ifa_add(ifa, flags | RTF_MPATH, ifa->ifa_addr,
ifp->if_rdomain);
}
rtfree(rt);
return (error);
}
/*
* Remove local rtentry of ifa's address if it exists.
*/
int
rt_ifa_dellocal(struct ifaddr *ifa)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct rtentry *rt;
u_int flags = RTF_HOST|RTF_LOCAL;
int error = 0;
/*
* We do not add local routes for such address, so do not bother
* removing them.
*/
switch (ifa->ifa_addr->sa_family) {
case AF_INET:
if (satosin(ifa->ifa_addr)->sin_addr.s_addr == INADDR_ANY)
return (0);
break;
#ifdef INET6
case AF_INET6:
if (IN6_ARE_ADDR_EQUAL(&satosin6(ifa->ifa_addr)->sin6_addr,
&in6addr_any))
return (0);
break;
#endif
default:
break;
}
if (!ISSET(ifp->if_flags, (IFF_LOOPBACK|IFF_POINTOPOINT)))
flags |= RTF_LLINFO;
/*
* Before deleting, check if a corresponding local host
* route surely exists. With this check, we can avoid to
* delete an interface direct route whose destination is same
* as the address being removed. This can happen when removing
* a subnet-router anycast address on an interface attached
* to a shared medium.
*/
rt = rtalloc(ifa->ifa_addr, 0, ifp->if_rdomain);
if (rt != NULL && ISSET(rt->rt_flags, flags) == flags) {
error = rt_ifa_del(ifa, flags, ifa->ifa_addr,
ifp->if_rdomain);
}
rtfree(rt);
return (error);
}
/*
* Remove all addresses attached to ``ifa''.
*/
void
rt_ifa_purge(struct ifaddr *ifa)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct rtentry *rt = NULL;
unsigned int rtableid;
int error, af = ifa->ifa_addr->sa_family;
KASSERT(ifp != NULL);
for (rtableid = 0; rtableid < rtmap_limit; rtableid++) {
/* skip rtables that are not in the rdomain of the ifp */
if (rtable_l2(rtableid) != ifp->if_rdomain)
continue;
do {
error = rtable_walk(rtableid, af, &rt,
rt_ifa_purge_walker, ifa);
if (rt != NULL && error == EEXIST) {
error = rtdeletemsg(rt, ifp, rtableid);
if (error == 0)
error = EAGAIN;
}
rtfree(rt);
rt = NULL;
} while (error == EAGAIN);
if (error == EAFNOSUPPORT)
error = 0;
if (error)
break;
}
}
int
rt_ifa_purge_walker(struct rtentry *rt, void *vifa, unsigned int rtableid)
{
struct ifaddr *ifa = vifa;
if (rt->rt_ifa == ifa)
return EEXIST;
return 0;
}
/*
* Route timer routines. These routes allow functions to be called
* for various routes at any time. This is useful in supporting
* path MTU discovery and redirect route deletion.
*
* This is similar to some BSDI internal functions, but it provides
* for multiple queues for efficiency's sake...
*/
struct mutex rttimer_mtx;
struct rttimer {
TAILQ_ENTRY(rttimer) rtt_next; /* [T] entry on timer queue */
LIST_ENTRY(rttimer) rtt_link; /* [T] timers per rtentry */
struct timeout rtt_timeout; /* [I] timeout for this entry */
struct rttimer_queue *rtt_queue; /* [I] back pointer to queue */
struct rtentry *rtt_rt; /* [T] back pointer to route */
time_t rtt_expire; /* [I] rt expire time */
u_int rtt_tableid; /* [I] rtable id of rtt_rt */
};
#define RTTIMER_CALLOUT(r) { \
if (r->rtt_queue->rtq_func != NULL) { \
(*r->rtt_queue->rtq_func)(r->rtt_rt, r->rtt_tableid); \
} else { \
struct ifnet *ifp; \
\
ifp = if_get(r->rtt_rt->rt_ifidx); \
if (ifp != NULL && \
(r->rtt_rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == \
(RTF_DYNAMIC|RTF_HOST)) \
rtdeletemsg(r->rtt_rt, ifp, r->rtt_tableid); \
if_put(ifp); \
} \
}
/*
* Some subtle order problems with domain initialization mean that
* we cannot count on this being run from rt_init before various
* protocol initializations are done. Therefore, we make sure
* that this is run when the first queue is added...
*/
void
rt_timer_init(void)
{
pool_init(&rttimer_pool, sizeof(struct rttimer), 0,
IPL_MPFLOOR, 0, "rttmr", NULL);
mtx_init(&rttimer_mtx, IPL_MPFLOOR);
}
void
rt_timer_queue_init(struct rttimer_queue *rtq, int timeout,
void (*func)(struct rtentry *, u_int))
{
rtq->rtq_timeout = timeout;
rtq->rtq_count = 0;
rtq->rtq_func = func;
TAILQ_INIT(&rtq->rtq_head);
}
void
rt_timer_queue_change(struct rttimer_queue *rtq, int timeout)
{
mtx_enter(&rttimer_mtx);
rtq->rtq_timeout = timeout;
mtx_leave(&rttimer_mtx);
}
void
rt_timer_queue_flush(struct rttimer_queue *rtq)
{
struct rttimer *r;
TAILQ_HEAD(, rttimer) rttlist;
NET_ASSERT_LOCKED();
TAILQ_INIT(&rttlist);
mtx_enter(&rttimer_mtx);
while ((r = TAILQ_FIRST(&rtq->rtq_head)) != NULL) {
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(&rtq->rtq_head, r, rtt_next);
TAILQ_INSERT_TAIL(&rttlist, r, rtt_next);
KASSERT(rtq->rtq_count > 0);
rtq->rtq_count--;
}
mtx_leave(&rttimer_mtx);
while ((r = TAILQ_FIRST(&rttlist)) != NULL) {
TAILQ_REMOVE(&rttlist, r, rtt_next);
RTTIMER_CALLOUT(r);
pool_put(&rttimer_pool, r);
}
}
unsigned long
rt_timer_queue_count(struct rttimer_queue *rtq)
{
return (rtq->rtq_count);
}
static inline struct rttimer *
rt_timer_unlink(struct rttimer *r)
{
MUTEX_ASSERT_LOCKED(&rttimer_mtx);
LIST_REMOVE(r, rtt_link);
r->rtt_rt = NULL;
if (timeout_del(&r->rtt_timeout) == 0) {
/* timeout fired, so rt_timer_timer will do the cleanup */
return NULL;
}
TAILQ_REMOVE(&r->rtt_queue->rtq_head, r, rtt_next);
KASSERT(r->rtt_queue->rtq_count > 0);
r->rtt_queue->rtq_count--;
return r;
}
void
rt_timer_remove_all(struct rtentry *rt)
{
struct rttimer *r;
TAILQ_HEAD(, rttimer) rttlist;
TAILQ_INIT(&rttlist);
mtx_enter(&rttimer_mtx);
while ((r = LIST_FIRST(&rt->rt_timer)) != NULL) {
r = rt_timer_unlink(r);
if (r != NULL)
TAILQ_INSERT_TAIL(&rttlist, r, rtt_next);
}
mtx_leave(&rttimer_mtx);
while ((r = TAILQ_FIRST(&rttlist)) != NULL) {
TAILQ_REMOVE(&rttlist, r, rtt_next);
pool_put(&rttimer_pool, r);
}
}
time_t
rt_timer_get_expire(const struct rtentry *rt)
{
const struct rttimer *r;
time_t expire = 0;
mtx_enter(&rttimer_mtx);
LIST_FOREACH(r, &rt->rt_timer, rtt_link) {
if (expire == 0 || expire > r->rtt_expire)
expire = r->rtt_expire;
}
mtx_leave(&rttimer_mtx);
return expire;
}
int
rt_timer_add(struct rtentry *rt, struct rttimer_queue *queue, u_int rtableid)
{
struct rttimer *r, *rnew;
rnew = pool_get(&rttimer_pool, PR_NOWAIT | PR_ZERO);
if (rnew == NULL)
return (ENOBUFS);
rnew->rtt_rt = rt;
rnew->rtt_queue = queue;
rnew->rtt_tableid = rtableid;
rnew->rtt_expire = getuptime() + queue->rtq_timeout;
timeout_set_proc(&rnew->rtt_timeout, rt_timer_timer, rnew);
mtx_enter(&rttimer_mtx);
/*
* If there's already a timer with this action, destroy it before
* we add a new one.
*/
LIST_FOREACH(r, &rt->rt_timer, rtt_link) {
if (r->rtt_queue == queue) {
r = rt_timer_unlink(r);
break; /* only one per list, so we can quit... */
}
}
LIST_INSERT_HEAD(&rt->rt_timer, rnew, rtt_link);
TAILQ_INSERT_TAIL(&queue->rtq_head, rnew, rtt_next);
timeout_add_sec(&rnew->rtt_timeout, queue->rtq_timeout);
rnew->rtt_queue->rtq_count++;
mtx_leave(&rttimer_mtx);
if (r != NULL)
pool_put(&rttimer_pool, r);
return (0);
}
void
rt_timer_timer(void *arg)
{
struct rttimer *r = arg;
struct rttimer_queue *rtq = r->rtt_queue;
NET_LOCK();
mtx_enter(&rttimer_mtx);
if (r->rtt_rt != NULL)
LIST_REMOVE(r, rtt_link);
TAILQ_REMOVE(&rtq->rtq_head, r, rtt_next);
KASSERT(rtq->rtq_count > 0);
rtq->rtq_count--;
mtx_leave(&rttimer_mtx);
if (r->rtt_rt != NULL)
RTTIMER_CALLOUT(r);
NET_UNLOCK();
pool_put(&rttimer_pool, r);
}
#ifdef MPLS
int
rt_mpls_set(struct rtentry *rt, struct sockaddr *src, uint8_t op)
{
struct sockaddr_mpls *psa_mpls = (struct sockaddr_mpls *)src;
struct rt_mpls *rt_mpls;
if (psa_mpls == NULL && op != MPLS_OP_POP)
return (EOPNOTSUPP);
if (psa_mpls != NULL && psa_mpls->smpls_len != sizeof(*psa_mpls))
return (EINVAL);
if (psa_mpls != NULL && psa_mpls->smpls_family != AF_MPLS)
return (EAFNOSUPPORT);
rt->rt_llinfo = malloc(sizeof(struct rt_mpls), M_TEMP, M_NOWAIT|M_ZERO);
if (rt->rt_llinfo == NULL)
return (ENOMEM);
rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
if (psa_mpls != NULL)
rt_mpls->mpls_label = psa_mpls->smpls_label;
rt_mpls->mpls_operation = op;
/* XXX: set experimental bits */
rt->rt_flags |= RTF_MPLS;
return (0);
}
void
rt_mpls_clear(struct rtentry *rt)
{
if (rt->rt_llinfo != NULL && rt->rt_flags & RTF_MPLS) {
free(rt->rt_llinfo, M_TEMP, sizeof(struct rt_mpls));
rt->rt_llinfo = NULL;
}
rt->rt_flags &= ~RTF_MPLS;
}
#endif
u_int16_t
rtlabel_name2id(char *name)
{
struct rt_label *label, *p;
u_int16_t new_id = 1;
if (!name[0])
return (0);
TAILQ_FOREACH(label, &rt_labels, rtl_entry)
if (strcmp(name, label->rtl_name) == 0) {
label->rtl_ref++;
return (label->rtl_id);
}
/*
* to avoid fragmentation, we do a linear search from the beginning
* and take the first free slot we find. if there is none or the list
* is empty, append a new entry at the end.
*/
TAILQ_FOREACH(p, &rt_labels, rtl_entry) {
if (p->rtl_id != new_id)
break;
new_id = p->rtl_id + 1;
}
if (new_id > LABELID_MAX)
return (0);
label = malloc(sizeof(*label), M_RTABLE, M_NOWAIT|M_ZERO);
if (label == NULL)
return (0);
strlcpy(label->rtl_name, name, sizeof(label->rtl_name));
label->rtl_id = new_id;
label->rtl_ref++;
if (p != NULL) /* insert new entry before p */
TAILQ_INSERT_BEFORE(p, label, rtl_entry);
else /* either list empty or no free slot in between */
TAILQ_INSERT_TAIL(&rt_labels, label, rtl_entry);
return (label->rtl_id);
}
const char *
rtlabel_id2name(u_int16_t id)
{
struct rt_label *label;
TAILQ_FOREACH(label, &rt_labels, rtl_entry)
if (label->rtl_id == id)
return (label->rtl_name);
return (NULL);
}
struct sockaddr *
rtlabel_id2sa(u_int16_t labelid, struct sockaddr_rtlabel *sa_rl)
{
const char *label;
if (labelid == 0 || (label = rtlabel_id2name(labelid)) == NULL)
return (NULL);
bzero(sa_rl, sizeof(*sa_rl));
sa_rl->sr_len = sizeof(*sa_rl);
sa_rl->sr_family = AF_UNSPEC;
strlcpy(sa_rl->sr_label, label, sizeof(sa_rl->sr_label));
return ((struct sockaddr *)sa_rl);
}
void
rtlabel_unref(u_int16_t id)
{
struct rt_label *p, *next;
if (id == 0)
return;
TAILQ_FOREACH_SAFE(p, &rt_labels, rtl_entry, next) {
if (id == p->rtl_id) {
if (--p->rtl_ref == 0) {
TAILQ_REMOVE(&rt_labels, p, rtl_entry);
free(p, M_RTABLE, sizeof(*p));
}
break;
}
}
}
int
rt_if_track(struct ifnet *ifp)
{
unsigned int rtableid;
struct rtentry *rt = NULL;
int i, error = 0;
for (rtableid = 0; rtableid < rtmap_limit; rtableid++) {
/* skip rtables that are not in the rdomain of the ifp */
if (rtable_l2(rtableid) != ifp->if_rdomain)
continue;
for (i = 1; i <= AF_MAX; i++) {
if (!rtable_mpath_capable(rtableid, i))
continue;
do {
error = rtable_walk(rtableid, i, &rt,
rt_if_linkstate_change, ifp);
if (rt != NULL && error == EEXIST) {
error = rtdeletemsg(rt, ifp, rtableid);
if (error == 0)
error = EAGAIN;
}
rtfree(rt);
rt = NULL;
} while (error == EAGAIN);
if (error == EAFNOSUPPORT)
error = 0;
if (error)
break;
}
}
return (error);
}
int
rt_if_linkstate_change(struct rtentry *rt, void *arg, u_int id)
{
struct ifnet *ifp = arg;
struct sockaddr_in6 sa_mask;
int error;
if (rt->rt_ifidx != ifp->if_index)
return (0);
/* Local routes are always usable. */
if (rt->rt_flags & RTF_LOCAL) {
rt->rt_flags |= RTF_UP;
return (0);
}
if (LINK_STATE_IS_UP(ifp->if_link_state) && ifp->if_flags & IFF_UP) {
if (ISSET(rt->rt_flags, RTF_UP))
return (0);
/* bring route up */
rt->rt_flags |= RTF_UP;
error = rtable_mpath_reprio(id, rt_key(rt), rt_plen(rt),
rt->rt_priority & RTP_MASK, rt);
} else {
/*
* Remove redirected and cloned routes (mainly ARP)
* from down interfaces so we have a chance to get
* new routes from a better source.
*/
if (ISSET(rt->rt_flags, RTF_CLONED|RTF_DYNAMIC) &&
!ISSET(rt->rt_flags, RTF_CACHED|RTF_BFD)) {
return (EEXIST);
}
if (!ISSET(rt->rt_flags, RTF_UP))
return (0);
/* take route down */
rt->rt_flags &= ~RTF_UP;
error = rtable_mpath_reprio(id, rt_key(rt), rt_plen(rt),
rt->rt_priority | RTP_DOWN, rt);
}
if_group_routechange(rt_key(rt), rt_plen2mask(rt, &sa_mask));
return (error);
}
struct sockaddr *
rt_plentosa(sa_family_t af, int plen, struct sockaddr_in6 *sa_mask)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa_mask;
#ifdef INET6
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa_mask;
#endif
KASSERT(plen >= 0 || plen == -1);
if (plen == -1)
return (NULL);
memset(sa_mask, 0, sizeof(*sa_mask));
switch (af) {
case AF_INET:
sin->sin_family = AF_INET;
sin->sin_len = sizeof(struct sockaddr_in);
in_prefixlen2mask(&sin->sin_addr, plen);
break;
#ifdef INET6
case AF_INET6:
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
in6_prefixlen2mask(&sin6->sin6_addr, plen);
break;
#endif /* INET6 */
default:
return (NULL);
}
return ((struct sockaddr *)sa_mask);
}
struct sockaddr *
rt_plen2mask(struct rtentry *rt, struct sockaddr_in6 *sa_mask)
{
return (rt_plentosa(rt_key(rt)->sa_family, rt_plen(rt), sa_mask));
}
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
void db_print_sa(struct sockaddr *);
void db_print_ifa(struct ifaddr *);
void
db_print_sa(struct sockaddr *sa)
{
int len;
u_char *p;
if (sa == NULL) {
db_printf("[NULL]");
return;
}
p = (u_char *)sa;
len = sa->sa_len;
db_printf("[");
while (len > 0) {
db_printf("%d", *p);
p++;
len--;
if (len)
db_printf(",");
}
db_printf("]\n");
}
void
db_print_ifa(struct ifaddr *ifa)
{
if (ifa == NULL)
return;
db_printf(" ifa_addr=");
db_print_sa(ifa->ifa_addr);
db_printf(" ifa_dsta=");
db_print_sa(ifa->ifa_dstaddr);
db_printf(" ifa_mask=");
db_print_sa(ifa->ifa_netmask);
db_printf(" flags=0x%x, refcnt=%u, metric=%d\n",
ifa->ifa_flags, ifa->ifa_refcnt.r_refs, ifa->ifa_metric);
}
/*
* Function to pass to rtable_walk().
* Return non-zero error to abort walk.
*/
int
db_show_rtentry(struct rtentry *rt, void *w, unsigned int id)
{
db_printf("rtentry=%p", rt);
db_printf(" flags=0x%x refcnt=%u use=%llu expire=%lld\n",
rt->rt_flags, rt->rt_refcnt.r_refs, rt->rt_use, rt->rt_expire);
db_printf(" key="); db_print_sa(rt_key(rt));
db_printf(" plen=%d", rt_plen(rt));
db_printf(" gw="); db_print_sa(rt->rt_gateway);
db_printf(" ifidx=%u ", rt->rt_ifidx);
db_printf(" ifa=%p\n", rt->rt_ifa);
db_print_ifa(rt->rt_ifa);
db_printf(" gwroute=%p llinfo=%p priority=%d\n",
rt->rt_gwroute, rt->rt_llinfo, rt->rt_priority);
return (0);
}
/*
* Function to print all the route trees.
*/
int
db_show_rtable(int af, unsigned int rtableid)
{
db_printf("Route tree for af %d, rtableid %u\n", af, rtableid);
rtable_walk(rtableid, af, NULL, db_show_rtentry, NULL);
return (0);
}
#endif /* DDB */
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
/* $OpenBSD: ipsec_output.c,v 1.97 2022/01/02 22:36:04 jsg Exp $ */
/*
* The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
*
* Copyright (c) 2000-2001 Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <netinet/udp.h>
#include <netinet/ip_ipip.h>
#include <netinet/ip_ah.h>
#include <netinet/ip_esp.h>
#include <netinet/ip_ipcomp.h>
#include <crypto/cryptodev.h>
#include <crypto/xform.h>
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
int udpencap_enable = 1; /* enabled by default */
int udpencap_port = 4500; /* triggers decapsulation */
/*
* Loop over a tdb chain, taking into consideration protocol tunneling. The
* fourth argument is set if the first encapsulation header is already in
* place.
*/
int
ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
{
int hlen, off, error;
#ifdef INET6
struct ip6_ext ip6e;
int nxt;
int dstopt = 0;
#endif
int setdf = 0;
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
#ifdef ENCDEBUG
char buf[INET6_ADDRSTRLEN];
#endif
/* Check that the transform is allowed by the administrator. */
if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
(tdb->tdb_sproto == IPPROTO_AH && !ah_enable) ||
(tdb->tdb_sproto == IPPROTO_IPCOMP && !ipcomp_enable)) {
DPRINTF("IPsec outbound packet dropped due to policy "
"(check your sysctls)");
error = EHOSTUNREACH;
goto drop;
}
/* Sanity check. */
if (!tdb->tdb_xform) {
DPRINTF("uninitialized TDB");
error = EHOSTUNREACH;
goto drop;
}
/* Check if the SPI is invalid. */
if (tdb->tdb_flags & TDBF_INVALID) {
DPRINTF("attempt to use invalid SA %s/%08x/%u",
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
ntohl(tdb->tdb_spi), tdb->tdb_sproto);
error = ENXIO;
goto drop;
}
/* Check that the network protocol is supported */
switch (tdb->tdb_dst.sa.sa_family) {
case AF_INET:
break;
#ifdef INET6
case AF_INET6:
break;
#endif /* INET6 */
default:
DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d",
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
ntohl(tdb->tdb_spi), tdb->tdb_sproto,
tdb->tdb_dst.sa.sa_family);
error = EPFNOSUPPORT;
goto drop;
}
/*
* Register first use if applicable, setup relevant expiration timer.
*/
if (tdb->tdb_first_use == 0) {
tdb->tdb_first_use = gettime();
if (tdb->tdb_flags & TDBF_FIRSTUSE) {
if (timeout_add_sec(&tdb->tdb_first_tmo,
tdb->tdb_exp_first_use))
tdb_ref(tdb);
}
if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
if (timeout_add_sec(&tdb->tdb_sfirst_tmo,
tdb->tdb_soft_first_use))
tdb_ref(tdb);
}
}
/*
* Check for tunneling if we don't have the first header in place.
* When doing Ethernet-over-IP, we are handed an already-encapsulated
* frame, so we don't need to re-encapsulate.
*/
if (tunalready == 0) {
/*
* If the target protocol family is different, we know we'll be
* doing tunneling.
*/
if (af == tdb->tdb_dst.sa.sa_family) {
switch (af) {
case AF_INET:
hlen = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
hlen = sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
}
/* Bring the network header in the first mbuf. */
if (m->m_len < hlen) {
if ((m = m_pullup(m, hlen)) == NULL) {
error = ENOBUFS;
goto drop;
}
}
if (af == AF_INET) {
ip = mtod(m, struct ip *);
/*
* This is not a bridge packet, remember if we
* had IP_DF.
*/
setdf = ip->ip_off & htons(IP_DF);
}
#ifdef INET6
if (af == AF_INET6)
ip6 = mtod(m, struct ip6_hdr *);
#endif /* INET6 */
}
/* Do the appropriate encapsulation, if necessary. */
if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */
(tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */
(tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */
((tdb->tdb_dst.sa.sa_family == AF_INET) &&
(tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
(tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
#ifdef INET6
((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
(!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) &&
(!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
&ip6->ip6_dst))) ||
#endif /* INET6 */
0) {
/* Fix IPv4 header checksum and length. */
if (af == AF_INET) {
if (m->m_len < sizeof(struct ip))
if ((m = m_pullup(m,
sizeof(struct ip))) == NULL) {
error = ENOBUFS;
goto drop;
}
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
}
#ifdef INET6
/* Fix IPv6 header payload length. */
if (af == AF_INET6) {
if (m->m_len < sizeof(struct ip6_hdr))
if ((m = m_pullup(m,
sizeof(struct ip6_hdr))) == NULL) {
error = ENOBUFS;
goto drop;
}
if (m->m_pkthdr.len - sizeof(*ip6) >
IPV6_MAXPACKET) {
/* No jumbogram support. */
error = ENXIO; /*?*/
goto drop;
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len
- sizeof(*ip6));
}
#endif /* INET6 */
/* Encapsulate -- m may be changed or set to NULL. */
error = ipip_output(&m, tdb);
if ((m == NULL) && (!error))
error = EFAULT;
if (error)
goto drop;
if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) {
if (m->m_len < sizeof(struct ip))
if ((m = m_pullup(m,
sizeof(struct ip))) == NULL) {
error = ENOBUFS;
goto drop;
}
ip = mtod(m, struct ip *);
ip->ip_off |= htons(IP_DF);
}
/* Remember that we appended a tunnel header. */
mtx_enter(&tdb->tdb_mtx);
tdb->tdb_flags |= TDBF_USEDTUNNEL;
mtx_leave(&tdb->tdb_mtx);
}
}
/*
* If this is just an IP-IP TDB and we're told there's already an
* encapsulation header or ipip_output() has encapsulated it, move on.
*/
if (tdb->tdb_xform->xf_type == XF_IP4)
return ipsp_process_done(m, tdb);
/* Extract some information off the headers. */
switch (tdb->tdb_dst.sa.sa_family) {
case AF_INET:
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
off = offsetof(struct ip, ip_p);
break;
#ifdef INET6
case AF_INET6:
ip6 = mtod(m, struct ip6_hdr *);
hlen = sizeof(struct ip6_hdr);
off = offsetof(struct ip6_hdr, ip6_nxt);
nxt = ip6->ip6_nxt;
/*
* chase mbuf chain to find the appropriate place to
* put AH/ESP/IPcomp header.
* IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
*/
do {
switch (nxt) {
case IPPROTO_AH:
case IPPROTO_ESP:
case IPPROTO_IPCOMP:
/*
* we should not skip security header added
* beforehand.
*/
goto exitip6loop;
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
/*
* if we see 2nd destination option header,
* we should stop there.
*/
if (nxt == IPPROTO_DSTOPTS && dstopt)
goto exitip6loop;
if (nxt == IPPROTO_DSTOPTS) {
/*
* seen 1st or 2nd destination option.
* next time we see one, it must be 2nd.
*/
dstopt = 1;
} else if (nxt == IPPROTO_ROUTING) {
/*
* if we see destination option next
* time, it must be dest2.
*/
dstopt = 2;
}
if (m->m_pkthdr.len < hlen + sizeof(ip6e)) {
error = EINVAL;
goto drop;
}
/* skip this header */
m_copydata(m, hlen, sizeof(ip6e),
(caddr_t)&ip6e);
nxt = ip6e.ip6e_nxt;
off = hlen + offsetof(struct ip6_ext, ip6e_nxt);
/*
* we will never see nxt == IPPROTO_AH
* so it is safe to omit AH case.
*/
hlen += (ip6e.ip6e_len + 1) << 3;
break;
default:
goto exitip6loop;
}
} while (hlen < m->m_pkthdr.len);
exitip6loop:
break;
#endif /* INET6 */
default:
error = EPFNOSUPPORT;
goto drop;
}
if (m->m_pkthdr.len < hlen) {
error = EINVAL;
goto drop;
}
ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len);
tdbstat_add(tdb, tdb_ouncompbytes, m->m_pkthdr.len);
/* Non expansion policy for IPCOMP */
if (tdb->tdb_sproto == IPPROTO_IPCOMP) {
if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) {
/* No need to compress, leave the packet untouched */
ipcompstat_inc(ipcomps_minlen);
return ipsp_process_done(m, tdb);
}
}
/* Invoke the IPsec transform. */
return (*(tdb->tdb_xform->xf_output))(m, tdb, hlen, off);
drop:
m_freem(m);
return error;
}
/*
* Called by the IPsec output transform callbacks, to transmit the packet
* or do further processing, as necessary.
*/
int
ipsp_process_done(struct mbuf *m, struct tdb *tdb)
{
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
struct tdb *tdbo;
struct tdb_ident *tdbi;
struct m_tag *mtag;
int roff, error;
NET_ASSERT_LOCKED();
tdb->tdb_last_used = gettime();
if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) {
struct mbuf *mi;
struct udphdr *uh;
int iphlen;
if (!udpencap_enable || !udpencap_port) {
error = ENXIO;
goto drop;
}
switch (tdb->tdb_dst.sa.sa_family) {
case AF_INET:
iphlen = sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
iphlen = sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
default:
DPRINTF("unknown protocol family (%d)",
tdb->tdb_dst.sa.sa_family);
error = EPFNOSUPPORT;
goto drop;
}
mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff);
if (mi == NULL) {
error = ENOMEM;
goto drop;
}
uh = (struct udphdr *)(mtod(mi, caddr_t) + roff);
uh->uh_sport = uh->uh_dport = htons(udpencap_port);
if (tdb->tdb_udpencap_port)
uh->uh_dport = tdb->tdb_udpencap_port;
uh->uh_ulen = htons(m->m_pkthdr.len - iphlen);
uh->uh_sum = 0;
#ifdef INET6
if (tdb->tdb_dst.sa.sa_family == AF_INET6)
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
#endif /* INET6 */
espstat_inc(esps_udpencout);
}
switch (tdb->tdb_dst.sa.sa_family) {
case AF_INET:
/* Fix the header length, for AH processing. */
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
ip->ip_p = IPPROTO_UDP;
break;
#ifdef INET6
case AF_INET6:
/* Fix the header length, for AH processing. */
if (m->m_pkthdr.len < sizeof(*ip6)) {
error = ENXIO;
goto drop;
}
if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
/* No jumbogram support. */
error = ENXIO;
goto drop;
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
ip6->ip6_nxt = IPPROTO_UDP;
break;
#endif /* INET6 */
default:
DPRINTF("unknown protocol family (%d)",
tdb->tdb_dst.sa.sa_family);
error = EPFNOSUPPORT;
goto drop;
}
/*
* Add a record of what we've done or what needs to be done to the
* packet.
*/
mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident),
M_NOWAIT);
if (mtag == NULL) {
DPRINTF("could not allocate packet tag");
error = ENOMEM;
goto drop;
}
tdbi = (struct tdb_ident *)(mtag + 1);
tdbi->dst = tdb->tdb_dst;
tdbi->proto = tdb->tdb_sproto;
tdbi->spi = tdb->tdb_spi;
tdbi->rdomain = tdb->tdb_rdomain;
m_tag_prepend(m, mtag);
ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdr.len);
tdbstat_pkt(tdb, tdb_opackets, tdb_obytes, m->m_pkthdr.len);
/* If there's another (bundled) TDB to apply, do so. */
tdbo = tdb_ref(tdb->tdb_onext);
if (tdbo != NULL) {
KERNEL_ASSERT_LOCKED();
error = ipsp_process_packet(m, tdbo,
tdb->tdb_dst.sa.sa_family, 0);
tdb_unref(tdbo);
return error;
}
#if NPF > 0
/* Add pf tag if requested. */
pf_tag_packet(m, tdb->tdb_tag, -1);
pf_pkt_addr_changed(m);
#endif
if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post;
/*
* We're done with IPsec processing, transmit the packet using the
* appropriate network protocol (IP or IPv6). SPD lookup will be
* performed again there.
*/
switch (tdb->tdb_dst.sa.sa_family) {
case AF_INET:
error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0);
break;
#ifdef INET6
case AF_INET6:
/*
* We don't need massage, IPv6 header fields are always in
* net endian.
*/
error = ip6_output(m, NULL, NULL, 0, NULL, NULL);
break;
#endif /* INET6 */
default:
error = EPFNOSUPPORT;
break;
}
return error;
drop:
m_freem(m);
return error;
}
ssize_t
ipsec_hdrsz(struct tdb *tdbp)
{
ssize_t adjust;
switch (tdbp->tdb_sproto) {
case IPPROTO_IPIP:
adjust = 0;
break;
case IPPROTO_ESP:
if (tdbp->tdb_encalgxform == NULL)
return (-1);
/* Header length */
adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
if (tdbp->tdb_flags & TDBF_UDPENCAP)
adjust += sizeof(struct udphdr);
/* Authenticator */
if (tdbp->tdb_authalgxform != NULL)
adjust += tdbp->tdb_authalgxform->authsize;
/* Padding */
adjust += MAX(4, tdbp->tdb_encalgxform->blocksize);
break;
case IPPROTO_AH:
if (tdbp->tdb_authalgxform == NULL)
return (-1);
adjust = AH_FLENGTH + sizeof(u_int32_t);
adjust += tdbp->tdb_authalgxform->authsize;
break;
default:
return (-1);
}
if (!(tdbp->tdb_flags & TDBF_TUNNELING) &&
!(tdbp->tdb_flags & TDBF_USEDTUNNEL))
return (adjust);
switch (tdbp->tdb_dst.sa.sa_family) {
case AF_INET:
adjust += sizeof(struct ip);
break;
#ifdef INET6
case AF_INET6:
adjust += sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
}
return (adjust);
}
void
ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
{
struct tdb_ident *tdbi;
struct tdb *tdbp;
struct m_tag *mtag;
ssize_t adjust;
NET_ASSERT_LOCKED();
for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag;
mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) {
tdbi = (struct tdb_ident *)(mtag + 1);
tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
tdbi->proto);
if (tdbp == NULL)
break;
if ((adjust = ipsec_hdrsz(tdbp)) == -1) {
tdb_unref(tdbp);
break;
}
mtu -= adjust;
tdbp->tdb_mtu = mtu;
tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",
ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m);
tdb_unref(tdbp);
}
}
5
1
1
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
/* $OpenBSD: pfkeyv2_parsemessage.c,v 1.60 2021/07/14 22:39:26 tobhe Exp $ */
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Craig Metz. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <netinet/ip_ipsp.h>
#include <net/pfkeyv2.h>
#if NPF > 0
#include <net/if.h>
#include <net/pfvar.h>
#endif
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
#define BITMAP_SA (1LL << SADB_EXT_SA)
#define BITMAP_LIFETIME_CURRENT (1LL << SADB_EXT_LIFETIME_CURRENT)
#define BITMAP_LIFETIME_HARD (1LL << SADB_EXT_LIFETIME_HARD)
#define BITMAP_LIFETIME_SOFT (1LL << SADB_EXT_LIFETIME_SOFT)
#define BITMAP_ADDRESS_SRC (1LL << SADB_EXT_ADDRESS_SRC)
#define BITMAP_ADDRESS_DST (1LL << SADB_EXT_ADDRESS_DST)
#define BITMAP_ADDRESS_PROXY (1LL << SADB_EXT_ADDRESS_PROXY)
#define BITMAP_KEY_AUTH (1LL << SADB_EXT_KEY_AUTH)
#define BITMAP_KEY_ENCRYPT (1LL << SADB_EXT_KEY_ENCRYPT)
#define BITMAP_IDENTITY_SRC (1LL << SADB_EXT_IDENTITY_SRC)
#define BITMAP_IDENTITY_DST (1LL << SADB_EXT_IDENTITY_DST)
#define BITMAP_SENSITIVITY (1LL << SADB_EXT_SENSITIVITY)
#define BITMAP_PROPOSAL (1LL << SADB_EXT_PROPOSAL)
#define BITMAP_SUPPORTED_AUTH (1LL << SADB_EXT_SUPPORTED_AUTH)
#define BITMAP_SUPPORTED_ENCRYPT (1LL << SADB_EXT_SUPPORTED_ENCRYPT)
#define BITMAP_SPIRANGE (1LL << SADB_EXT_SPIRANGE)
#define BITMAP_LIFETIME (BITMAP_LIFETIME_CURRENT | BITMAP_LIFETIME_HARD | BITMAP_LIFETIME_SOFT)
#define BITMAP_ADDRESS (BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST)
#define BITMAP_KEY (BITMAP_KEY_AUTH | BITMAP_KEY_ENCRYPT)
#define BITMAP_IDENTITY (BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST)
#define BITMAP_MSG 1
#define BITMAP_X_SRC_MASK (1LL << SADB_X_EXT_SRC_MASK)
#define BITMAP_X_DST_MASK (1LL << SADB_X_EXT_DST_MASK)
#define BITMAP_X_PROTOCOL (1LL << SADB_X_EXT_PROTOCOL)
#define BITMAP_X_SRC_FLOW (1LL << SADB_X_EXT_SRC_FLOW)
#define BITMAP_X_DST_FLOW (1LL << SADB_X_EXT_DST_FLOW)
#define BITMAP_X_FLOW_TYPE (1LL << SADB_X_EXT_FLOW_TYPE)
#define BITMAP_X_SA2 (1LL << SADB_X_EXT_SA2)
#define BITMAP_X_DST2 (1LL << SADB_X_EXT_DST2)
#define BITMAP_X_POLICY (1LL << SADB_X_EXT_POLICY)
#define BITMAP_X_FLOW (BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE)
#define BITMAP_X_SUPPORTED_COMP (1LL << SADB_X_EXT_SUPPORTED_COMP)
#define BITMAP_X_UDPENCAP (1LL << SADB_X_EXT_UDPENCAP)
#define BITMAP_X_LIFETIME_LASTUSE (1LL << SADB_X_EXT_LIFETIME_LASTUSE)
#define BITMAP_X_TAG (1LL << SADB_X_EXT_TAG)
#define BITMAP_X_TAP (1LL << SADB_X_EXT_TAP)
#define BITMAP_X_SATYPE2 (1LL << SADB_X_EXT_SATYPE2)
#define BITMAP_X_RDOMAIN (1LL << SADB_X_EXT_RDOMAIN)
#define BITMAP_X_COUNTER (1LL << SADB_X_EXT_COUNTER)
#define BITMAP_X_MTU (1LL << SADB_X_EXT_MTU)
#define BITMAP_X_REPLAY (1LL << SADB_X_EXT_REPLAY)
uint64_t sadb_exts_allowed_in[SADB_MAX+1] =
{
/* RESERVED */
~0,
/* GETSPI */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_SPIRANGE,
/* UPDATE */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN,
/* ADD */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
/* GET */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
/* ACQUIRE */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY | BITMAP_PROPOSAL,
/* REGISTER */
0,
/* EXPIRE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* FLUSH */
0,
/* DUMP */
0,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST | BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
/* X_DELFLOW */
BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
/* X_ASKPOLICY */
BITMAP_X_POLICY,
};
uint64_t sadb_exts_required_in[SADB_MAX+1] =
{
/* RESERVED */
0,
/* GETSPI */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_SPIRANGE,
/* UPDATE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* ADD */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* GET */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* ACQUIRE */
0,
/* REGISTER */
0,
/* EXPIRE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* FLUSH */
0,
/* DUMP */
0,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_DELFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2,
/* X_ASKPOLICY */
BITMAP_X_POLICY,
};
const uint64_t sadb_exts_allowed_out[SADB_MAX+1] =
{
/* RESERVED */
~0,
/* GETSPI */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
/* UPDATE */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN,
/* ADD */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
/* GET */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_FLOW_TYPE | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_COUNTER | BITMAP_X_RDOMAIN | BITMAP_X_MTU | BITMAP_X_REPLAY,
/* ACQUIRE */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY | BITMAP_PROPOSAL,
/* REGISTER */
BITMAP_SUPPORTED_AUTH | BITMAP_SUPPORTED_ENCRYPT | BITMAP_X_SUPPORTED_COMP,
/* EXPIRE */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS,
/* FLUSH */
0,
/* DUMP */
BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_IDENTITY,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE | BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST | BITMAP_X_RDOMAIN,
/* X_DELFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE | BITMAP_X_RDOMAIN,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
/* X_ASKPOLICY */
BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_FLOW_TYPE | BITMAP_X_POLICY,
};
const uint64_t sadb_exts_required_out[SADB_MAX+1] =
{
/* RESERVED */
0,
/* GETSPI */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* UPDATE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* ADD */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* DELETE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* GET */
BITMAP_SA | BITMAP_LIFETIME_CURRENT | BITMAP_ADDRESS_DST,
/* ACQUIRE */
0,
/* REGISTER */
BITMAP_SUPPORTED_AUTH | BITMAP_SUPPORTED_ENCRYPT | BITMAP_X_SUPPORTED_COMP,
/* EXPIRE */
BITMAP_SA | BITMAP_ADDRESS_DST,
/* FLUSH */
0,
/* DUMP */
0,
/* X_PROMISC */
0,
/* X_ADDFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_DELFLOW */
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
/* X_GRPSPIS */
BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | BITMAP_X_SATYPE2,
/* X_REPPOLICY */
BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_FLOW_TYPE,
};
int
pfkeyv2_parsemessage(void *p, int len, void **headers)
{
struct sadb_ext *sadb_ext;
int i, left = len;
uint64_t allow, seen = 1;
struct sadb_msg *sadb_msg = (struct sadb_msg *) p;
bzero(headers, (SADB_EXT_MAX + 1) * sizeof(void *));
if (left < sizeof(struct sadb_msg)) {
DPRINTF("message too short");
return (EINVAL);
}
headers[0] = p;
if (sadb_msg->sadb_msg_len * sizeof(uint64_t) != left) {
DPRINTF("length not a multiple of 64");
return (EINVAL);
}
p += sizeof(struct sadb_msg);
left -= sizeof(struct sadb_msg);
if (sadb_msg->sadb_msg_reserved) {
DPRINTF("message header reserved field set");
return (EINVAL);
}
if (sadb_msg->sadb_msg_type > SADB_MAX) {
DPRINTF("message type > %d", SADB_MAX);
return (EINVAL);
}
if (!sadb_msg->sadb_msg_type) {
DPRINTF("message type unset");
return (EINVAL);
}
if (sadb_msg->sadb_msg_pid != curproc->p_p->ps_pid) {
DPRINTF("bad PID value");
return (EINVAL);
}
if (sadb_msg->sadb_msg_errno) {
if (left) {
DPRINTF("too-large error message");
return (EINVAL);
}
return (0);
}
if (sadb_msg->sadb_msg_type == SADB_X_PROMISC) {
DPRINTF("message type promiscuous");
return (0);
}
allow = sadb_exts_allowed_in[sadb_msg->sadb_msg_type];
while (left > 0) {
sadb_ext = (struct sadb_ext *)p;
if (left < sizeof(struct sadb_ext)) {
DPRINTF("extension header too short");
return (EINVAL);
}
i = sadb_ext->sadb_ext_len * sizeof(uint64_t);
if (left < i) {
DPRINTF("extension header exceeds message length");
return (EINVAL);
}
if (sadb_ext->sadb_ext_type > SADB_EXT_MAX) {
DPRINTF("unknown extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (!sadb_ext->sadb_ext_type) {
DPRINTF("unset extension header");
return (EINVAL);
}
if (!(allow & (1LL << sadb_ext->sadb_ext_type))) {
DPRINTF("extension header %d not permitted on message "
"type %d",
sadb_ext->sadb_ext_type, sadb_msg->sadb_msg_type);
return (EINVAL);
}
if (headers[sadb_ext->sadb_ext_type]) {
DPRINTF("duplicate extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
seen |= (1LL << sadb_ext->sadb_ext_type);
switch (sadb_ext->sadb_ext_type) {
case SADB_EXT_SA:
case SADB_X_EXT_SA2:
{
struct sadb_sa *sadb_sa = (struct sadb_sa *)p;
if (i != sizeof(struct sadb_sa)) {
DPRINTF("bad header length for SA extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_state > SADB_SASTATE_MAX) {
DPRINTF("unknown SA state %d in SA extension "
"header %d",
sadb_sa->sadb_sa_state,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_state == SADB_SASTATE_DEAD) {
DPRINTF("cannot set SA state to dead, "
"SA extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_encrypt > SADB_EALG_MAX) {
DPRINTF("unknown encryption algorithm %d "
"in SA extension header %d",
sadb_sa->sadb_sa_encrypt,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_auth > SADB_AALG_MAX) {
DPRINTF("unknown authentication algorithm %d "
"in SA extension header %d",
sadb_sa->sadb_sa_auth,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_sa->sadb_sa_replay > 64) {
DPRINTF("unsupported replay window size %d "
"in SA extension header %d",
sadb_sa->sadb_sa_replay,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
case SADB_X_EXT_PROTOCOL:
case SADB_X_EXT_FLOW_TYPE:
case SADB_X_EXT_SATYPE2:
if (i != sizeof(struct sadb_protocol)) {
DPRINTF("bad PROTOCOL/FLOW/SATYPE2 header "
"length in extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
case SADB_X_EXT_POLICY:
if (i != sizeof(struct sadb_x_policy)) {
DPRINTF("bad POLICY header length");
return (EINVAL);
}
break;
case SADB_EXT_LIFETIME_CURRENT:
case SADB_EXT_LIFETIME_HARD:
case SADB_EXT_LIFETIME_SOFT:
case SADB_X_EXT_LIFETIME_LASTUSE:
if (i != sizeof(struct sadb_lifetime)) {
DPRINTF("bad header length for LIFETIME "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
case SADB_EXT_ADDRESS_PROXY:
case SADB_X_EXT_SRC_MASK:
case SADB_X_EXT_DST_MASK:
case SADB_X_EXT_SRC_FLOW:
case SADB_X_EXT_DST_FLOW:
case SADB_X_EXT_DST2:
{
struct sadb_address *sadb_address =
(struct sadb_address *)p;
struct sockaddr *sa = (struct sockaddr *)(p +
sizeof(struct sadb_address));
if (i < sizeof(struct sadb_address) +
sizeof(struct sockaddr)) {
DPRINTF("bad ADDRESS extension header %d "
"length",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_address->sadb_address_reserved) {
DPRINTF("ADDRESS extension header %d reserved "
"field set",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sa->sa_len &&
(i != sizeof(struct sadb_address) +
PADUP(sa->sa_len))) {
DPRINTF("bad sockaddr length field in ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
switch (sa->sa_family) {
case AF_INET:
if (sizeof(struct sadb_address) +
PADUP(sizeof(struct sockaddr_in)) != i) {
DPRINTF("invalid ADDRESS extension "
"header %d length",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sa->sa_len != sizeof(struct sockaddr_in)) {
DPRINTF("bad sockaddr_in length in "
"ADDRESS extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
/* Only check the right pieces */
switch (sadb_ext->sadb_ext_type)
{
case SADB_X_EXT_SRC_MASK:
case SADB_X_EXT_DST_MASK:
case SADB_X_EXT_SRC_FLOW:
case SADB_X_EXT_DST_FLOW:
break;
default:
if (((struct sockaddr_in *)sa)->sin_port) {
DPRINTF("port field set in "
"sockaddr_in of ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
}
{
char zero[sizeof(((struct sockaddr_in *)sa)->sin_zero)];
bzero(zero, sizeof(zero));
if (bcmp(&((struct sockaddr_in *)sa)->sin_zero, zero, sizeof(zero))) {
DPRINTF("reserved sockaddr_in "
"field non-zero'ed in "
"ADDRESS extension header "
"%d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
#ifdef INET6
case AF_INET6:
if (i != sizeof(struct sadb_address) +
PADUP(sizeof(struct sockaddr_in6))) {
DPRINTF("invalid sockaddr_in6 length "
"in ADDRESS extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sa->sa_len !=
sizeof(struct sockaddr_in6)) {
DPRINTF("bad sockaddr_in6 length in "
"ADDRESS extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (((struct sockaddr_in6 *)sa)->sin6_flowinfo) {
DPRINTF("flowinfo field set in "
"sockaddr_in6 of ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
/* Only check the right pieces */
switch (sadb_ext->sadb_ext_type)
{
case SADB_X_EXT_SRC_MASK:
case SADB_X_EXT_DST_MASK:
case SADB_X_EXT_SRC_FLOW:
case SADB_X_EXT_DST_FLOW:
break;
default:
if (((struct sockaddr_in6 *)sa)->sin6_port) {
DPRINTF("port field set in "
"sockaddr_in6 of ADDRESS "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
break;
}
break;
#endif /* INET6 */
default:
if (sadb_msg->sadb_msg_satype ==
SADB_X_SATYPE_TCPSIGNATURE &&
sa->sa_family == 0)
break;
DPRINTF("unknown address family %d in ADDRESS "
"extension header %d",
sa->sa_family, sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
case SADB_EXT_KEY_AUTH:
case SADB_EXT_KEY_ENCRYPT:
{
struct sadb_key *sadb_key = (struct sadb_key *)p;
if (i < sizeof(struct sadb_key)) {
DPRINTF("bad header length in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (!sadb_key->sadb_key_bits) {
DPRINTF("key length unset in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (((sadb_key->sadb_key_bits + 63) / 64) * sizeof(uint64_t) != i - sizeof(struct sadb_key)) {
DPRINTF("invalid key length in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_key->sadb_key_reserved) {
DPRINTF("reserved field set in KEY extension "
"header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
break;
case SADB_EXT_IDENTITY_SRC:
case SADB_EXT_IDENTITY_DST:
{
struct sadb_ident *sadb_ident = (struct sadb_ident *)p;
if (i < sizeof(struct sadb_ident)) {
DPRINTF("bad header length of IDENTITY "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) {
DPRINTF("unknown identity type %d in IDENTITY "
"extension header %d",
sadb_ident->sadb_ident_type,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_ident->sadb_ident_reserved) {
DPRINTF("reserved field set in IDENTITY "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (i > sizeof(struct sadb_ident)) {
char *c =
(char *)(p + sizeof(struct sadb_ident));
int j;
if (*(char *)(p + i - 1)) {
DPRINTF("non NUL-terminated identity "
"in IDENTITY extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
j = PADUP(strlen(c) + 1) +
sizeof(struct sadb_ident);
if (i != j) {
DPRINTF("actual identity length does "
"not match expected length in "
"identity extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
}
}
break;
case SADB_EXT_SENSITIVITY:
{
struct sadb_sens *sadb_sens = (struct sadb_sens *)p;
if (i < sizeof(struct sadb_sens)) {
DPRINTF("bad header length for SENSITIVITY "
"extension header");
return (EINVAL);
}
if (i != (sadb_sens->sadb_sens_sens_len +
sadb_sens->sadb_sens_integ_len) *
sizeof(uint64_t) +
sizeof(struct sadb_sens)) {
DPRINTF("bad payload length for SENSITIVITY "
"extension header");
return (EINVAL);
}
}
break;
case SADB_EXT_PROPOSAL:
{
struct sadb_prop *sadb_prop = (struct sadb_prop *)p;
if (i < sizeof(struct sadb_prop)) {
DPRINTF("bad PROPOSAL header length");
return (EINVAL);
}
if (sadb_prop->sadb_prop_reserved) {
DPRINTF("reserved fieldset in PROPOSAL "
"extension header");
return (EINVAL);
}
if ((i - sizeof(struct sadb_prop)) %
sizeof(struct sadb_comb)) {
DPRINTF("bad proposal length");
return (EINVAL);
}
{
struct sadb_comb *sadb_comb =
(struct sadb_comb *)(p +
sizeof(struct sadb_prop));
int j;
for (j = 0;
j < (i - sizeof(struct sadb_prop))/
sizeof(struct sadb_comb);
j++) {
if (sadb_comb->sadb_comb_auth >
SADB_AALG_MAX) {
DPRINTF("unknown "
"authentication algorithm "
"%d in PROPOSAL",
sadb_comb->sadb_comb_auth);
return (EINVAL);
}
if (sadb_comb->sadb_comb_encrypt >
SADB_EALG_MAX) {
DPRINTF("unknown encryption "
"algorithm %d in PROPOSAL",
sadb_comb->
sadb_comb_encrypt);
return (EINVAL);
}
if (sadb_comb->sadb_comb_reserved) {
DPRINTF("reserved field set "
"in COMB header");
return (EINVAL);
}
}
}
}
break;
case SADB_EXT_SUPPORTED_AUTH:
case SADB_EXT_SUPPORTED_ENCRYPT:
case SADB_X_EXT_SUPPORTED_COMP:
{
struct sadb_supported *sadb_supported =
(struct sadb_supported *)p;
int j;
if (i < sizeof(struct sadb_supported)) {
DPRINTF("bad header length for SUPPORTED " "extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_supported->sadb_supported_reserved) {
DPRINTF("reserved field set in SUPPORTED "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
{
struct sadb_alg *sadb_alg =
(struct sadb_alg *)(p +
sizeof(struct sadb_supported));
int max_alg;
max_alg = sadb_ext->sadb_ext_type ==
SADB_EXT_SUPPORTED_AUTH ?
SADB_AALG_MAX : SADB_EXT_SUPPORTED_ENCRYPT ?
SADB_EALG_MAX : SADB_X_CALG_MAX;
for (j = 0;
j < sadb_supported->sadb_supported_len - 1;
j++) {
if (sadb_alg->sadb_alg_id > max_alg) {
DPRINTF("unknown algorithm %d "
"in SUPPORTED extension "
"header %d",
sadb_alg->sadb_alg_id,
sadb_ext->sadb_ext_type);
return (EINVAL);
}
if (sadb_alg->sadb_alg_reserved) {
DPRINTF("reserved field set "
"in supported algorithms "
"header inside SUPPORTED "
"extension header %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
sadb_alg++;
}
}
}
break;
case SADB_EXT_SPIRANGE:
{
struct sadb_spirange *sadb_spirange =
(struct sadb_spirange *)p;
if (i != sizeof(struct sadb_spirange)) {
DPRINTF("bad header length of SPIRANGE "
"extension header");
return (EINVAL);
}
if (sadb_spirange->sadb_spirange_min >
sadb_spirange->sadb_spirange_max) {
DPRINTF("bad SPI range");
return (EINVAL);
}
}
break;
case SADB_X_EXT_UDPENCAP:
if (i != sizeof(struct sadb_x_udpencap)) {
DPRINTF("bad UDPENCAP header length");
return (EINVAL);
}
break;
case SADB_X_EXT_RDOMAIN:
if (i != sizeof(struct sadb_x_rdomain)) {
DPRINTF("bad RDOMAIN header length");
return (EINVAL);
}
break;
#if NPF > 0
case SADB_X_EXT_TAG:
if (i < sizeof(struct sadb_x_tag)) {
DPRINTF("TAG extension header too small");
return (EINVAL);
}
if (i > (sizeof(struct sadb_x_tag) +
PF_TAG_NAME_SIZE)) {
DPRINTF("TAG extension header too long");
return (EINVAL);
}
break;
case SADB_X_EXT_TAP:
if (i < sizeof(struct sadb_x_tap)) {
DPRINTF("TAP extension header too small");
return (EINVAL);
}
if (i > sizeof(struct sadb_x_tap)) {
DPRINTF("TAP extension header too long");
return (EINVAL);
}
break;
#endif
default:
DPRINTF("unknown extension header type %d",
sadb_ext->sadb_ext_type);
return (EINVAL);
}
headers[sadb_ext->sadb_ext_type] = p;
p += i;
left -= i;
}
if (left) {
DPRINTF("message too long");
return (EINVAL);
}
{
uint64_t required;
required = sadb_exts_required_in[sadb_msg->sadb_msg_type];
if ((seen & required) != required) {
DPRINTF("required fields missing");
return (EINVAL);
}
}
switch (((struct sadb_msg *)headers[0])->sadb_msg_type) {
case SADB_UPDATE:
if (((struct sadb_sa *)headers[SADB_EXT_SA])->sadb_sa_state !=
SADB_SASTATE_MATURE) {
DPRINTF("updating non-mature SA prohibited");
return (EINVAL);
}
break;
case SADB_ADD:
if (((struct sadb_sa *)headers[SADB_EXT_SA])->sadb_sa_state !=
SADB_SASTATE_MATURE) {
DPRINTF("adding non-mature SA prohibited");
return (EINVAL);
}
break;
}
return (0);
}
6
1
11
9
6
8
25
12
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/* $OpenBSD: dead_vnops.c,v 1.41 2022/06/26 05:20:42 visa Exp $ */
/* $NetBSD: dead_vnops.c,v 1.16 1996/02/13 13:12:48 mycroft Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)dead_vnops.c 8.2 (Berkeley) 11/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/errno.h>
#include <sys/buf.h>
/*
* Prototypes for dead operations on vnodes.
*/
int dead_ebadf(void *);
int dead_open(void *);
int dead_read(void *);
int dead_write(void *);
int dead_ioctl(void *);
int dead_kqfilter(void *v);
int dead_inactive(void *);
int dead_lock(void *);
int dead_bmap(void *);
int dead_strategy(void *);
int dead_print(void *);
int chkvnlock(struct vnode *);
const struct vops dead_vops = {
.vop_lookup = vop_generic_lookup,
.vop_create = vop_generic_badop,
.vop_mknod = vop_generic_badop,
.vop_open = dead_open,
.vop_close = nullop,
.vop_access = dead_ebadf,
.vop_getattr = dead_ebadf,
.vop_setattr = dead_ebadf,
.vop_read = dead_read,
.vop_write = dead_write,
.vop_ioctl = dead_ioctl,
.vop_kqfilter = dead_kqfilter,
.vop_revoke = NULL,
.vop_fsync = nullop,
.vop_remove = vop_generic_badop,
.vop_link = vop_generic_badop,
.vop_rename = vop_generic_badop,
.vop_mkdir = vop_generic_badop,
.vop_rmdir = vop_generic_badop,
.vop_symlink = vop_generic_badop,
.vop_readdir = dead_ebadf,
.vop_readlink = dead_ebadf,
.vop_abortop = vop_generic_badop,
.vop_inactive = dead_inactive,
.vop_reclaim = nullop,
.vop_lock = dead_lock,
.vop_unlock = nullop,
.vop_islocked = nullop,
.vop_bmap = dead_bmap,
.vop_strategy = dead_strategy,
.vop_print = dead_print,
.vop_pathconf = dead_ebadf,
.vop_advlock = dead_ebadf,
.vop_bwrite = nullop,
};
/*
* Open always fails as if device did not exist.
*/
/* ARGSUSED */
int
dead_open(void *v)
{
return (ENXIO);
}
/*
* Vnode op for read
*/
/* ARGSUSED */
int
dead_read(void *v)
{
struct vop_read_args *ap = v;
if (chkvnlock(ap->a_vp))
panic("dead_read: lock");
/*
* Return EOF for tty devices, EIO for others
*/
if ((ap->a_vp->v_flag & VISTTY) == 0)
return (EIO);
return (0);
}
/*
* Vnode op for write
*/
/* ARGSUSED */
int
dead_write(void *v)
{
struct vop_write_args *ap = v;
if (chkvnlock(ap->a_vp))
panic("dead_write: lock");
return (EIO);
}
/*
* Device ioctl operation.
*/
/* ARGSUSED */
int
dead_ioctl(void *v)
{
struct vop_ioctl_args *ap = v;
if (!chkvnlock(ap->a_vp))
return (EBADF);
return ((ap->a_vp->v_op->vop_ioctl)(ap));
}
int
dead_kqfilter(void *v)
{
struct vop_kqfilter_args *ap = v;
switch (ap->a_kn->kn_filter) {
case EVFILT_READ:
case EVFILT_WRITE:
ap->a_kn->kn_fop = &dead_filtops;
break;
case EVFILT_EXCEPT:
if ((ap->a_kn->kn_flags & __EV_POLL) == 0)
return (EINVAL);
ap->a_kn->kn_fop = &dead_filtops;
break;
default:
return (EINVAL);
}
return (0);
}
/*
* Just call the device strategy routine
*/
int
dead_strategy(void *v)
{
struct vop_strategy_args *ap = v;
int s;
if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) {
ap->a_bp->b_flags |= B_ERROR;
s = splbio();
biodone(ap->a_bp);
splx(s);
return (EIO);
}
return (VOP_STRATEGY(ap->a_bp->b_vp, ap->a_bp));
}
int
dead_inactive(void *v)
{
struct vop_inactive_args *ap = v;
VOP_UNLOCK(ap->a_vp);
return (0);
}
/*
* Wait until the vnode has finished changing state.
*/
int
dead_lock(void *v)
{
struct vop_lock_args *ap = v;
struct vnode *vp = ap->a_vp;
if (ap->a_flags & LK_DRAIN || !chkvnlock(vp))
return (0);
return VOP_LOCK(vp, ap->a_flags);
}
/*
* Wait until the vnode has finished changing state.
*/
int
dead_bmap(void *v)
{
struct vop_bmap_args *ap = v;
if (!chkvnlock(ap->a_vp))
return (EIO);
return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp));
}
/*
* Print out the contents of a dead vnode.
*/
/* ARGSUSED */
int
dead_print(void *v)
{
printf("tag VT_NON, dead vnode\n");
return 0;
}
/*
* Empty vnode failed operation
*/
/*ARGSUSED*/
int
dead_ebadf(void *v)
{
return (EBADF);
}
/*
* We have to wait during times when the vnode is
* in a state of change.
*/
int
chkvnlock(struct vnode *vp)
{
int locked = 0;
mtx_enter(&vnode_mtx);
while (vp->v_lflag & VXLOCK) {
vp->v_lflag |= VXWANT;
msleep_nsec(vp, &vnode_mtx, PINOD, "chkvnlock", INFSLP);
locked = 1;
}
mtx_leave(&vnode_mtx);
return (locked);
}
19
1
3
2
14
5
12
14
2
2
2
98
1
97
2
1
9
85
93
91
2
1
65
54
10
19
6
32
44
12
10
10
33
16
1
17
48
24
2
24
29
12
58
8
19
99
29
17
12
27
1
24
54
54
1
1
3
48
42
1
41
41
1
40
12
12
1
11
63
63
1
27
13
19
2
2
45
45
1
2
1
42
25
25
1
24
6
1
5
2
59
37
23
4
23
48
33
5
6
4
3
6
39
37
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
/* $OpenBSD: uvm_mmap.c,v 1.172 2022/08/01 14:56:59 deraadt Exp $ */
/* $NetBSD: uvm_mmap.c,v 1.49 2001/02/18 21:19:08 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1991, 1993 The Regents of the University of California.
* Copyright (c) 1988 University of Utah.
*
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Charles D. Cranor,
* Washington University, University of California, Berkeley and
* its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: vm_mmap.c 1.6 91/10/21$
* @(#)vm_mmap.c 8.5 (Berkeley) 5/19/94
* from: Id: uvm_mmap.c,v 1.1.2.14 1998/01/05 21:04:26 chuck Exp
*/
/*
* uvm_mmap.c: system call interface into VM system, plus kernel vm_mmap
* function.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/resourcevar.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/conf.h>
#include <sys/signalvar.h>
#include <sys/syslog.h>
#include <sys/stat.h>
#include <sys/specdev.h>
#include <sys/stdint.h>
#include <sys/pledge.h>
#include <sys/unistd.h> /* for KBIND* */
#include <sys/user.h>
#include <machine/exec.h> /* for __LDPGSZ */
#include <sys/syscallargs.h>
#include <uvm/uvm.h>
#include <uvm/uvm_device.h>
#include <uvm/uvm_vnode.h>
int uvm_mmapanon(vm_map_t, vaddr_t *, vsize_t, vm_prot_t, vm_prot_t, int,
vsize_t, struct proc *);
int uvm_mmapfile(vm_map_t, vaddr_t *, vsize_t, vm_prot_t, vm_prot_t, int,
struct vnode *, voff_t, vsize_t, struct proc *);
/*
* Page align addr and size, returning EINVAL on wraparound.
*/
#define ALIGN_ADDR(addr, size, pageoff) do { \
pageoff = (addr & PAGE_MASK); \
if (pageoff != 0) { \
if (size > SIZE_MAX - pageoff) \
return EINVAL; /* wraparound */ \
addr -= pageoff; \
size += pageoff; \
} \
if (size != 0) { \
size = (vsize_t)round_page(size); \
if (size == 0) \
return EINVAL; /* wraparound */ \
} \
} while (0)
/*
* sys_mquery: provide mapping hints to applications that do fixed mappings
*
* flags: 0 or MAP_FIXED (MAP_FIXED - means that we insist on this addr and
* don't care about PMAP_PREFER or such)
* addr: hint where we'd like to place the mapping.
* size: size of the mapping
* fd: fd of the file we want to map
* off: offset within the file
*/
int
sys_mquery(struct proc *p, void *v, register_t *retval)
{
struct sys_mquery_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
syscallarg(int) prot;
syscallarg(int) flags;
syscallarg(int) fd;
syscallarg(off_t) pos;
} */ *uap = v;
struct file *fp;
voff_t uoff;
int error;
vaddr_t vaddr;
int flags = 0;
vsize_t size;
vm_prot_t prot;
int fd;
vaddr = (vaddr_t) SCARG(uap, addr);
prot = SCARG(uap, prot);
size = (vsize_t) SCARG(uap, len);
fd = SCARG(uap, fd);
if ((prot & PROT_MASK) != prot)
return EINVAL;
if (SCARG(uap, flags) & MAP_FIXED)
flags |= UVM_FLAG_FIXED;
if (fd >= 0) {
if ((error = getvnode(p, fd, &fp)) != 0)
return error;
uoff = SCARG(uap, pos);
} else {
fp = NULL;
uoff = UVM_UNKNOWN_OFFSET;
}
if (vaddr == 0)
vaddr = uvm_map_hint(p->p_vmspace, prot, VM_MIN_ADDRESS,
VM_MAXUSER_ADDRESS);
error = uvm_map_mquery(&p->p_vmspace->vm_map, &vaddr, size, uoff,
flags);
if (error == 0)
*retval = (register_t)(vaddr);
if (fp != NULL)
FRELE(fp, p);
return error;
}
int uvm_wxabort;
/*
* W^X violations are only allowed on permitted filesystems.
*/
static inline int
uvm_wxcheck(struct proc *p, char *call)
{
struct process *pr = p->p_p;
int wxallowed = (pr->ps_textvp->v_mount &&
(pr->ps_textvp->v_mount->mnt_flag & MNT_WXALLOWED));
if (wxallowed && (pr->ps_flags & PS_WXNEEDED))
return 0;
if (uvm_wxabort) {
KERNEL_LOCK();
/* Report W^X failures */
if (pr->ps_wxcounter++ == 0)
log(LOG_NOTICE, "%s(%d): %s W^X violation\n",
pr->ps_comm, pr->ps_pid, call);
/* Send uncatchable SIGABRT for coredump */
sigexit(p, SIGABRT);
KERNEL_UNLOCK();
}
return ENOTSUP;
}
/*
* sys_mmap: mmap system call.
*
* => file offset and address may not be page aligned
* - if MAP_FIXED, offset and address must have remainder mod PAGE_SIZE
* - if address isn't page aligned the mapping starts at trunc_page(addr)
* and the return value is adjusted up by the page offset.
*/
int
sys_mmap(struct proc *p, void *v, register_t *retval)
{
struct sys_mmap_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
syscallarg(int) prot;
syscallarg(int) flags;
syscallarg(int) fd;
syscallarg(off_t) pos;
} */ *uap = v;
vaddr_t addr;
struct vattr va;
off_t pos;
vsize_t limit, pageoff, size;
vm_prot_t prot, maxprot;
int flags, fd;
vaddr_t vm_min_address = VM_MIN_ADDRESS;
struct filedesc *fdp = p->p_fd;
struct file *fp = NULL;
struct vnode *vp;
int error;
/* first, extract syscall args from the uap. */
addr = (vaddr_t) SCARG(uap, addr);
size = (vsize_t) SCARG(uap, len);
prot = SCARG(uap, prot);
flags = SCARG(uap, flags);
fd = SCARG(uap, fd);
pos = SCARG(uap, pos);
/*
* Validate the flags.
*/
if ((prot & PROT_MASK) != prot)
return EINVAL;
if ((prot & (PROT_WRITE | PROT_EXEC)) == (PROT_WRITE | PROT_EXEC) &&
(error = uvm_wxcheck(p, "mmap")))
return error;
if ((flags & MAP_FLAGMASK) != flags)
return EINVAL;
if ((flags & (MAP_SHARED|MAP_PRIVATE)) == (MAP_SHARED|MAP_PRIVATE))
return EINVAL;
if ((flags & (MAP_FIXED|__MAP_NOREPLACE)) == __MAP_NOREPLACE)
return EINVAL;
if (flags & MAP_STACK) {
if ((flags & (MAP_ANON|MAP_PRIVATE)) != (MAP_ANON|MAP_PRIVATE))
return EINVAL;
if (flags & ~(MAP_STACK|MAP_FIXED|MAP_ANON|MAP_PRIVATE))
return EINVAL;
if (pos != 0)
return EINVAL;
if ((prot & (PROT_READ|PROT_WRITE)) != (PROT_READ|PROT_WRITE))
return EINVAL;
}
if (size == 0)
return EINVAL;
error = pledge_protexec(p, prot);
if (error)
return error;
/* align file position and save offset. adjust size. */
ALIGN_ADDR(pos, size, pageoff);
/* now check (MAP_FIXED) or get (!MAP_FIXED) the "addr" */
if (flags & MAP_FIXED) {
/* adjust address by the same amount as we did the offset */
addr -= pageoff;
if (addr & PAGE_MASK)
return EINVAL; /* not page aligned */
if (addr > SIZE_MAX - size)
return EINVAL; /* no wrapping! */
if (VM_MAXUSER_ADDRESS > 0 &&
(addr + size) > VM_MAXUSER_ADDRESS)
return EINVAL;
if (vm_min_address > 0 && addr < vm_min_address)
return EINVAL;
}
/* check for file mappings (i.e. not anonymous) and verify file. */
if ((flags & MAP_ANON) == 0) {
KERNEL_LOCK();
if ((fp = fd_getfile(fdp, fd)) == NULL) {
error = EBADF;
goto out;
}
if (fp->f_type != DTYPE_VNODE) {
error = ENODEV; /* only mmap vnodes! */
goto out;
}
vp = (struct vnode *)fp->f_data; /* convert to vnode */
if (vp->v_type != VREG && vp->v_type != VCHR &&
vp->v_type != VBLK) {
error = ENODEV; /* only REG/CHR/BLK support mmap */
goto out;
}
if (vp->v_type == VREG && (pos + size) < pos) {
error = EINVAL; /* no offset wrapping */
goto out;
}
/* special case: catch SunOS style /dev/zero */
if (vp->v_type == VCHR && iszerodev(vp->v_rdev)) {
flags |= MAP_ANON;
FRELE(fp, p);
fp = NULL;
KERNEL_UNLOCK();
goto is_anon;
}
/*
* Old programs may not select a specific sharing type, so
* default to an appropriate one.
*/
if ((flags & (MAP_SHARED|MAP_PRIVATE)) == 0) {
#if defined(DEBUG)
printf("WARNING: defaulted mmap() share type to"
" %s (pid %d comm %s)\n",
vp->v_type == VCHR ? "MAP_SHARED" : "MAP_PRIVATE",
p->p_p->ps_pid, p->p_p->ps_comm);
#endif
if (vp->v_type == VCHR)
flags |= MAP_SHARED; /* for a device */
else
flags |= MAP_PRIVATE; /* for a file */
}
/*
* MAP_PRIVATE device mappings don't make sense (and aren't
* supported anyway). However, some programs rely on this,
* so just change it to MAP_SHARED.
*/
if (vp->v_type == VCHR && (flags & MAP_PRIVATE) != 0) {
flags = (flags & ~MAP_PRIVATE) | MAP_SHARED;
}
/* now check protection */
maxprot = PROT_EXEC;
/* check read access */
if (fp->f_flag & FREAD)
maxprot |= PROT_READ;
else if (prot & PROT_READ) {
error = EACCES;
goto out;
}
/* check write access, shared case first */
if (flags & MAP_SHARED) {
/*
* if the file is writable, only add PROT_WRITE to
* maxprot if the file is not immutable, append-only.
* otherwise, if we have asked for PROT_WRITE, return
* EPERM.
*/
if (fp->f_flag & FWRITE) {
error = VOP_GETATTR(vp, &va, p->p_ucred, p);
if (error)
goto out;
if ((va.va_flags & (IMMUTABLE|APPEND)) == 0)
maxprot |= PROT_WRITE;
else if (prot & PROT_WRITE) {
error = EPERM;
goto out;
}
} else if (prot & PROT_WRITE) {
error = EACCES;
goto out;
}
} else {
/* MAP_PRIVATE mappings can always write to */
maxprot |= PROT_WRITE;
}
if ((flags & __MAP_NOFAULT) != 0 ||
((flags & MAP_PRIVATE) != 0 && (prot & PROT_WRITE) != 0)) {
limit = lim_cur(RLIMIT_DATA);
if (limit < size ||
limit - size < ptoa(p->p_vmspace->vm_dused)) {
error = ENOMEM;
goto out;
}
}
error = uvm_mmapfile(&p->p_vmspace->vm_map, &addr, size, prot,
maxprot, flags, vp, pos, lim_cur(RLIMIT_MEMLOCK), p);
FRELE(fp, p);
KERNEL_UNLOCK();
} else { /* MAP_ANON case */
if (fd != -1)
return EINVAL;
is_anon: /* label for SunOS style /dev/zero */
/* __MAP_NOFAULT only makes sense with a backing object */
if ((flags & __MAP_NOFAULT) != 0)
return EINVAL;
if (prot != PROT_NONE || (flags & MAP_SHARED)) {
limit = lim_cur(RLIMIT_DATA);
if (limit < size ||
limit - size < ptoa(p->p_vmspace->vm_dused)) {
return ENOMEM;
}
}
/*
* We've been treating (MAP_SHARED|MAP_PRIVATE) == 0 as
* MAP_PRIVATE, so make that clear.
*/
if ((flags & MAP_SHARED) == 0)
flags |= MAP_PRIVATE;
maxprot = PROT_MASK;
error = uvm_mmapanon(&p->p_vmspace->vm_map, &addr, size, prot,
maxprot, flags, lim_cur(RLIMIT_MEMLOCK), p);
}
if (error == 0)
/* remember to add offset */
*retval = (register_t)(addr + pageoff);
return error;
out:
KERNEL_UNLOCK();
if (fp)
FRELE(fp, p);
return error;
}
#if 1
int
sys_pad_mquery(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_mquery_args *uap = v;
struct sys_mquery_args unpad;
SCARG(&unpad, addr) = SCARG(uap, addr);
SCARG(&unpad, len) = SCARG(uap, len);
SCARG(&unpad, prot) = SCARG(uap, prot);
SCARG(&unpad, flags) = SCARG(uap, flags);
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, pos) = SCARG(uap, pos);
return sys_mquery(p, &unpad, retval);
}
int
sys_pad_mmap(struct proc *p, void *v, register_t *retval)
{
struct sys_pad_mmap_args *uap = v;
struct sys_mmap_args unpad;
SCARG(&unpad, addr) = SCARG(uap, addr);
SCARG(&unpad, len) = SCARG(uap, len);
SCARG(&unpad, prot) = SCARG(uap, prot);
SCARG(&unpad, flags) = SCARG(uap, flags);
SCARG(&unpad, fd) = SCARG(uap, fd);
SCARG(&unpad, pos) = SCARG(uap, pos);
return sys_mmap(p, &unpad, retval);
}
#endif
/*
* sys_msync: the msync system call (a front-end for flush)
*/
int
sys_msync(struct proc *p, void *v, register_t *retval)
{
struct sys_msync_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
syscallarg(int) flags;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
vm_map_t map;
int flags, uvmflags;
/* extract syscall args from the uap */
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
flags = SCARG(uap, flags);
/* sanity check flags */
if ((flags & ~(MS_ASYNC | MS_SYNC | MS_INVALIDATE)) != 0 ||
(flags & (MS_ASYNC | MS_SYNC | MS_INVALIDATE)) == 0 ||
(flags & (MS_ASYNC | MS_SYNC)) == (MS_ASYNC | MS_SYNC))
return EINVAL;
if ((flags & (MS_ASYNC | MS_SYNC)) == 0)
flags |= MS_SYNC;
/* align the address to a page boundary, and adjust the size accordingly */
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
/* get map */
map = &p->p_vmspace->vm_map;
/* translate MS_ flags into PGO_ flags */
uvmflags = PGO_CLEANIT;
if (flags & MS_INVALIDATE)
uvmflags |= PGO_FREE;
if (flags & MS_SYNC)
uvmflags |= PGO_SYNCIO;
else
uvmflags |= PGO_SYNCIO; /* XXXCDC: force sync for now! */
return uvm_map_clean(map, addr, addr+size, uvmflags);
}
/*
* sys_munmap: unmap a users memory
*/
int
sys_munmap(struct proc *p, void *v, register_t *retval)
{
struct sys_munmap_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
vm_map_t map;
vaddr_t vm_min_address = VM_MIN_ADDRESS;
struct uvm_map_deadq dead_entries;
/* get syscall args... */
addr = (vaddr_t) SCARG(uap, addr);
size = (vsize_t) SCARG(uap, len);
/* align address to a page boundary, and adjust size accordingly */
ALIGN_ADDR(addr, size, pageoff);
/*
* Check for illegal addresses. Watch out for address wrap...
* Note that VM_*_ADDRESS are not constants due to casts (argh).
*/
if (addr > SIZE_MAX - size)
return EINVAL;
if (VM_MAXUSER_ADDRESS > 0 && addr + size > VM_MAXUSER_ADDRESS)
return EINVAL;
if (vm_min_address > 0 && addr < vm_min_address)
return EINVAL;
map = &p->p_vmspace->vm_map;
vm_map_lock(map); /* lock map so we can checkprot */
/*
* interesting system call semantic: make sure entire range is
* allocated before allowing an unmap.
*/
if (!uvm_map_checkprot(map, addr, addr + size, PROT_NONE)) {
vm_map_unlock(map);
return EINVAL;
}
TAILQ_INIT(&dead_entries);
uvm_unmap_remove(map, addr, addr + size, &dead_entries, FALSE, TRUE);
vm_map_unlock(map); /* and unlock */
uvm_unmap_detach(&dead_entries, 0);
return 0;
}
/*
* sys_mprotect: the mprotect system call
*/
int
sys_mprotect(struct proc *p, void *v, register_t *retval)
{
struct sys_mprotect_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
syscallarg(int) prot;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
vm_prot_t prot;
int error;
/*
* extract syscall args from uap
*/
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
prot = SCARG(uap, prot);
if ((prot & PROT_MASK) != prot)
return EINVAL;
if ((prot & (PROT_WRITE | PROT_EXEC)) == (PROT_WRITE | PROT_EXEC) &&
(error = uvm_wxcheck(p, "mprotect")))
return error;
error = pledge_protexec(p, prot);
if (error)
return error;
/*
* align the address to a page boundary, and adjust the size accordingly
*/
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
return (uvm_map_protect(&p->p_vmspace->vm_map, addr, addr+size,
prot, FALSE));
}
/*
* sys_msyscall: the msyscall system call
*/
int
sys_msyscall(struct proc *p, void *v, register_t *retval)
{
struct sys_msyscall_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
/*
* align the address to a page boundary, and adjust the size accordingly
*/
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
return uvm_map_syscall(&p->p_vmspace->vm_map, addr, addr+size);
}
/*
* sys_minherit: the minherit system call
*/
int
sys_minherit(struct proc *p, void *v, register_t *retval)
{
struct sys_minherit_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
syscallarg(int) inherit;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
vm_inherit_t inherit;
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
inherit = SCARG(uap, inherit);
/*
* align the address to a page boundary, and adjust the size accordingly
*/
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
return (uvm_map_inherit(&p->p_vmspace->vm_map, addr, addr+size,
inherit));
}
/*
* sys_madvise: give advice about memory usage.
*/
/* ARGSUSED */
int
sys_madvise(struct proc *p, void *v, register_t *retval)
{
struct sys_madvise_args /* {
syscallarg(void *) addr;
syscallarg(size_t) len;
syscallarg(int) behav;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
int advice, error;
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
advice = SCARG(uap, behav);
/*
* align the address to a page boundary, and adjust the size accordingly
*/
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
switch (advice) {
case MADV_NORMAL:
case MADV_RANDOM:
case MADV_SEQUENTIAL:
error = uvm_map_advice(&p->p_vmspace->vm_map, addr,
addr + size, advice);
break;
case MADV_WILLNEED:
/*
* Activate all these pages, pre-faulting them in if
* necessary.
*/
/*
* XXX IMPLEMENT ME.
* Should invent a "weak" mode for uvm_fault()
* which would only do the PGO_LOCKED pgo_get().
*/
return 0;
case MADV_DONTNEED:
/*
* Deactivate all these pages. We don't need them
* any more. We don't, however, toss the data in
* the pages.
*/
error = uvm_map_clean(&p->p_vmspace->vm_map, addr, addr + size,
PGO_DEACTIVATE);
break;
case MADV_FREE:
/*
* These pages contain no valid data, and may be
* garbage-collected. Toss all resources, including
* any swap space in use.
*/
error = uvm_map_clean(&p->p_vmspace->vm_map, addr, addr + size,
PGO_FREE);
break;
case MADV_SPACEAVAIL:
/*
* XXXMRG What is this? I think it's:
*
* Ensure that we have allocated backing-store
* for these pages.
*
* This is going to require changes to the page daemon,
* as it will free swap space allocated to pages in core.
* There's also what to do for device/file/anonymous memory.
*/
return EINVAL;
default:
return EINVAL;
}
return error;
}
/*
* sys_mlock: memory lock
*/
int
sys_mlock(struct proc *p, void *v, register_t *retval)
{
struct sys_mlock_args /* {
syscallarg(const void *) addr;
syscallarg(size_t) len;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
int error;
/* extract syscall args from uap */
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
/* align address to a page boundary and adjust size accordingly */
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
if (atop(size) + uvmexp.wired > uvmexp.wiredmax)
return EAGAIN;
#ifdef pmap_wired_count
if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) >
lim_cur(RLIMIT_MEMLOCK))
return EAGAIN;
#else
if ((error = suser(p)) != 0)
return error;
#endif
error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, FALSE,
0);
return error == 0 ? 0 : ENOMEM;
}
/*
* sys_munlock: unlock wired pages
*/
int
sys_munlock(struct proc *p, void *v, register_t *retval)
{
struct sys_munlock_args /* {
syscallarg(const void *) addr;
syscallarg(size_t) len;
} */ *uap = v;
vaddr_t addr;
vsize_t size, pageoff;
int error;
/* extract syscall args from uap */
addr = (vaddr_t)SCARG(uap, addr);
size = (vsize_t)SCARG(uap, len);
/* align address to a page boundary, and adjust size accordingly */
ALIGN_ADDR(addr, size, pageoff);
if (addr > SIZE_MAX - size)
return EINVAL; /* disallow wrap-around. */
#ifndef pmap_wired_count
if ((error = suser(p)) != 0)
return error;
#endif
error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, TRUE,
0);
return error == 0 ? 0 : ENOMEM;
}
/*
* sys_mlockall: lock all pages mapped into an address space.
*/
int
sys_mlockall(struct proc *p, void *v, register_t *retval)
{
struct sys_mlockall_args /* {
syscallarg(int) flags;
} */ *uap = v;
int error, flags;
flags = SCARG(uap, flags);
if (flags == 0 ||
(flags & ~(MCL_CURRENT|MCL_FUTURE)) != 0)
return EINVAL;
#ifndef pmap_wired_count
if ((error = suser(p)) != 0)
return error;
#endif
error = uvm_map_pageable_all(&p->p_vmspace->vm_map, flags,
lim_cur(RLIMIT_MEMLOCK));
if (error != 0 && error != ENOMEM)
return EAGAIN;
return error;
}
/*
* sys_munlockall: unlock all pages mapped into an address space.
*/
int
sys_munlockall(struct proc *p, void *v, register_t *retval)
{
(void) uvm_map_pageable_all(&p->p_vmspace->vm_map, 0, 0);
return 0;
}
/*
* common code for mmapanon and mmapfile to lock a mmaping
*/
int
uvm_mmaplock(vm_map_t map, vaddr_t *addr, vsize_t size, vm_prot_t prot,
vsize_t locklimit)
{
int error;
/*
* POSIX 1003.1b -- if our address space was configured
* to lock all future mappings, wire the one we just made.
*/
if (prot == PROT_NONE) {
/*
* No more work to do in this case.
*/
return 0;
}
vm_map_lock(map);
if (map->flags & VM_MAP_WIREFUTURE) {
KERNEL_LOCK();
if ((atop(size) + uvmexp.wired) > uvmexp.wiredmax
#ifdef pmap_wired_count
|| (locklimit != 0 && (size +
ptoa(pmap_wired_count(vm_map_pmap(map)))) >
locklimit)
#endif
) {
error = ENOMEM;
vm_map_unlock(map);
/* unmap the region! */
uvm_unmap(map, *addr, *addr + size);
KERNEL_UNLOCK();
return error;
}
/*
* uvm_map_pageable() always returns the map
* unlocked.
*/
error = uvm_map_pageable(map, *addr, *addr + size,
FALSE, UVM_LK_ENTER);
if (error != 0) {
/* unmap the region! */
uvm_unmap(map, *addr, *addr + size);
KERNEL_UNLOCK();
return error;
}
KERNEL_UNLOCK();
return 0;
}
vm_map_unlock(map);
return 0;
}
/*
* uvm_mmapanon: internal version of mmap for anons
*
* - used by sys_mmap
*/
int
uvm_mmapanon(vm_map_t map, vaddr_t *addr, vsize_t size, vm_prot_t prot,
vm_prot_t maxprot, int flags, vsize_t locklimit, struct proc *p)
{
int error;
int advice = MADV_NORMAL;
unsigned int uvmflag = 0;
vsize_t align = 0; /* userland page size */
/*
* for non-fixed mappings, round off the suggested address.
* for fixed mappings, check alignment and zap old mappings.
*/
if ((flags & MAP_FIXED) == 0) {
*addr = round_page(*addr); /* round */
} else {
if (*addr & PAGE_MASK)
return EINVAL;
uvmflag |= UVM_FLAG_FIXED;
if ((flags & __MAP_NOREPLACE) == 0)
uvmflag |= UVM_FLAG_UNMAP;
}
if ((flags & MAP_FIXED) == 0 && size >= __LDPGSZ)
align = __LDPGSZ;
if ((flags & MAP_SHARED) == 0)
/* XXX: defer amap create */
uvmflag |= UVM_FLAG_COPYONW;
else
/* shared: create amap now */
uvmflag |= UVM_FLAG_OVERLAY;
if (flags & MAP_STACK)
uvmflag |= UVM_FLAG_STACK;
if (flags & MAP_CONCEAL)
uvmflag |= UVM_FLAG_CONCEAL;
/* set up mapping flags */
uvmflag = UVM_MAPFLAG(prot, maxprot,
(flags & MAP_SHARED) ? MAP_INHERIT_SHARE : MAP_INHERIT_COPY,
advice, uvmflag);
error = uvm_mapanon(map, addr, size, align, uvmflag);
if (error == 0)
error = uvm_mmaplock(map, addr, size, prot, locklimit);
return error;
}
/*
* uvm_mmapfile: internal version of mmap for non-anons
*
* - used by sys_mmap
* - caller must page-align the file offset
*/
int
uvm_mmapfile(vm_map_t map, vaddr_t *addr, vsize_t size, vm_prot_t prot,
vm_prot_t maxprot, int flags, struct vnode *vp, voff_t foff,
vsize_t locklimit, struct proc *p)
{
struct uvm_object *uobj;
int error;
int advice = MADV_NORMAL;
unsigned int uvmflag = 0;
vsize_t align = 0; /* userland page size */
/*
* for non-fixed mappings, round off the suggested address.
* for fixed mappings, check alignment and zap old mappings.
*/
if ((flags & MAP_FIXED) == 0) {
*addr = round_page(*addr); /* round */
} else {
if (*addr & PAGE_MASK)
return EINVAL;
uvmflag |= UVM_FLAG_FIXED;
if ((flags & __MAP_NOREPLACE) == 0)
uvmflag |= UVM_FLAG_UNMAP;
}
/*
* attach to underlying vm object.
*/
if (vp->v_type != VCHR) {
uobj = uvn_attach(vp, (flags & MAP_SHARED) ?
maxprot : (maxprot & ~PROT_WRITE));
/*
* XXXCDC: hack from old code
* don't allow vnodes which have been mapped
* shared-writeable to persist [forces them to be
* flushed out when last reference goes].
* XXXCDC: interesting side effect: avoids a bug.
* note that in WRITE [ufs_readwrite.c] that we
* allocate buffer, uncache, and then do the write.
* the problem with this is that if the uncache causes
* VM data to be flushed to the same area of the file
* we are writing to... in that case we've got the
* buffer locked and our process goes to sleep forever.
*
* XXXCDC: checking maxprot protects us from the
* "persistbug" program but this is not a long term
* solution.
*
* XXXCDC: we don't bother calling uncache with the vp
* VOP_LOCKed since we know that we are already
* holding a valid reference to the uvn (from the
* uvn_attach above), and thus it is impossible for
* the uncache to kill the uvn and trigger I/O.
*/
if (flags & MAP_SHARED) {
if ((prot & PROT_WRITE) ||
(maxprot & PROT_WRITE)) {
uvm_vnp_uncache(vp);
}
}
} else {
uobj = udv_attach(vp->v_rdev,
(flags & MAP_SHARED) ? maxprot :
(maxprot & ~PROT_WRITE), foff, size);
/*
* XXX Some devices don't like to be mapped with
* XXX PROT_EXEC, but we don't really have a
* XXX better way of handling this, right now
*/
if (uobj == NULL && (prot & PROT_EXEC) == 0) {
maxprot &= ~PROT_EXEC;
uobj = udv_attach(vp->v_rdev,
(flags & MAP_SHARED) ? maxprot :
(maxprot & ~PROT_WRITE), foff, size);
}
advice = MADV_RANDOM;
}
if (uobj == NULL)
return vp->v_type == VREG ? ENOMEM : EINVAL;
if ((flags & MAP_SHARED) == 0)
uvmflag |= UVM_FLAG_COPYONW;
if (flags & __MAP_NOFAULT)
uvmflag |= (UVM_FLAG_NOFAULT | UVM_FLAG_OVERLAY);
if (flags & MAP_STACK)
uvmflag |= UVM_FLAG_STACK;
if (flags & MAP_CONCEAL)
uvmflag |= UVM_FLAG_CONCEAL;
/* set up mapping flags */
uvmflag = UVM_MAPFLAG(prot, maxprot,
(flags & MAP_SHARED) ? MAP_INHERIT_SHARE : MAP_INHERIT_COPY,
advice, uvmflag);
error = uvm_map(map, addr, size, uobj, foff, align, uvmflag);
if (error == 0)
return uvm_mmaplock(map, addr, size, prot, locklimit);
/* errors: first detach from the uobj, if any. */
if (uobj)
uobj->pgops->pgo_detach(uobj);
return error;
}
/* an address that can't be in userspace or kernelspace */
#define BOGO_PC (u_long)-1
int
sys_kbind(struct proc *p, void *v, register_t *retval)
{
struct sys_kbind_args /* {
syscallarg(const struct __kbind *) param;
syscallarg(size_t) psize;
syscallarg(uint64_t) proc_cookie;
} */ *uap = v;
const struct __kbind *paramp;
union {
struct __kbind uk[KBIND_BLOCK_MAX];
char upad[KBIND_BLOCK_MAX * sizeof(*paramp) + KBIND_DATA_MAX];
} param;
struct uvm_map_deadq dead_entries;
struct process *pr = p->p_p;
const char *data;
vaddr_t baseva, last_baseva, endva, pageoffset, kva;
size_t psize, s;
u_long pc;
int count, i, extra;
int error, sigill = 0;
/*
* extract syscall args from uap
*/
paramp = SCARG(uap, param);
psize = SCARG(uap, psize);
/*
* If paramp is NULL and we're uninitialized, disable the syscall
* for the process. Raise SIGILL if paramp is NULL and we're
* already initialized.
*
* If paramp is non-NULL and we're uninitialized, do initialization.
* Otherwise, do security checks and raise SIGILL on failure.
*/
pc = PROC_PC(p);
mtx_enter(&pr->ps_mtx);
if (paramp == NULL) {
if (pr->ps_kbind_addr == 0)
pr->ps_kbind_addr = BOGO_PC;
else
sigill = 1;
} else if (pr->ps_kbind_addr == 0) {
pr->ps_kbind_addr = pc;
pr->ps_kbind_cookie = SCARG(uap, proc_cookie);
} else if (pc != pr->ps_kbind_addr || pc == BOGO_PC ||
pr->ps_kbind_cookie != SCARG(uap, proc_cookie)) {
sigill = 1;
}
mtx_leave(&pr->ps_mtx);
/* Raise SIGILL if something is off. */
if (sigill) {
KERNEL_LOCK();
sigexit(p, SIGILL);
/* NOTREACHED */
KERNEL_UNLOCK();
}
/* We're done if we were disabling the syscall. */
if (paramp == NULL)
return 0;
if (psize < sizeof(struct __kbind) || psize > sizeof(param))
return EINVAL;
if ((error = copyin(paramp, ¶m, psize)))
return error;
/*
* The param argument points to an array of __kbind structures
* followed by the corresponding new data areas for them. Verify
* that the sizes in the __kbind structures add up to the total
* size and find the start of the new area.
*/
paramp = ¶m.uk[0];
s = psize;
for (count = 0; s > 0 && count < KBIND_BLOCK_MAX; count++) {
if (s < sizeof(*paramp))
return EINVAL;
s -= sizeof(*paramp);
baseva = (vaddr_t)paramp[count].kb_addr;
endva = baseva + paramp[count].kb_size - 1;
if (paramp[count].kb_addr == NULL ||
paramp[count].kb_size == 0 ||
paramp[count].kb_size > KBIND_DATA_MAX ||
baseva >= VM_MAXUSER_ADDRESS ||
endva >= VM_MAXUSER_ADDRESS ||
s < paramp[count].kb_size)
return EINVAL;
s -= paramp[count].kb_size;
}
if (s > 0)
return EINVAL;
data = (const char *)¶mp[count];
/* all looks good, so do the bindings */
last_baseva = VM_MAXUSER_ADDRESS;
kva = 0;
TAILQ_INIT(&dead_entries);
KERNEL_LOCK();
for (i = 0; i < count; i++) {
baseva = (vaddr_t)paramp[i].kb_addr;
s = paramp[i].kb_size;
pageoffset = baseva & PAGE_MASK;
baseva = trunc_page(baseva);
/* hppa at least runs PLT entries over page edge */
extra = (pageoffset + s) & PAGE_MASK;
if (extra > pageoffset)
extra = 0;
else
s -= extra;
redo:
/* make sure sure the desired page is mapped into kernel_map */
if (baseva != last_baseva) {
if (kva != 0) {
vm_map_lock(kernel_map);
uvm_unmap_remove(kernel_map, kva,
kva+PAGE_SIZE, &dead_entries, FALSE, TRUE);
vm_map_unlock(kernel_map);
kva = 0;
}
if ((error = uvm_map_extract(&p->p_vmspace->vm_map,
baseva, PAGE_SIZE, &kva, UVM_EXTRACT_FIXPROT)))
break;
last_baseva = baseva;
}
/* do the update */
if ((error = kcopy(data, (char *)kva + pageoffset, s)))
break;
data += s;
if (extra > 0) {
baseva += PAGE_SIZE;
s = extra;
pageoffset = 0;
extra = 0;
goto redo;
}
}
if (kva != 0) {
vm_map_lock(kernel_map);
uvm_unmap_remove(kernel_map, kva, kva+PAGE_SIZE,
&dead_entries, FALSE, TRUE);
vm_map_unlock(kernel_map);
}
uvm_unmap_detach(&dead_entries, AMAP_REFALL);
KERNEL_UNLOCK();
return error;
}
8488
8486
8484
407
8483
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
/* $OpenBSD: intr.c,v 1.55 2020/12/28 14:23:30 mpi Exp $ */
/* $NetBSD: intr.c,v 1.3 2003/03/03 22:16:20 fvdl Exp $ */
/*
* Copyright 2002 (c) Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Frank van der Linden for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* #define INTRDEBUG */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <machine/atomic.h>
#include <machine/i8259.h>
#include <machine/cpu.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include "lapic.h"
#include "xen.h"
#include "hyperv.h"
#if NLAPIC > 0
#include <machine/i82489var.h>
#endif
struct pic softintr_pic = {
{0, {NULL}, NULL, 0, "softintr_pic0", NULL, 0, 0},
PIC_SOFT,
#ifdef MULTIPROCESSOR
{},
#endif
NULL,
NULL,
NULL,
NULL,
NULL,
};
/*
* Fill in default interrupt table (in case of spurious interrupt
* during configuration of kernel), setup interrupt control unit
*/
void
intr_default_setup(void)
{
int i;
/* icu vectors */
for (i = 0; i < NUM_LEGACY_IRQS; i++) {
idt_allocmap[ICU_OFFSET + i] = 1;
setgate(&idt[ICU_OFFSET + i],
i8259_stubs[i].ist_entry, 0, SDT_SYS386IGT,
SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
}
/*
* Eventually might want to check if it's actually there.
*/
i8259_default_setup();
}
/*
* Handle a NMI, possibly a machine check.
* return true to panic system, false to ignore.
*/
int
x86_nmi(void)
{
log(LOG_CRIT, "NMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
return(0);
}
/*
* Recalculate the interrupt masks from scratch.
*/
void
intr_calculatemasks(struct cpu_info *ci)
{
int irq, level;
u_int64_t unusedirqs, intrlevel[MAX_INTR_SOURCES];
struct intrhand *q;
/* First, figure out which levels each IRQ uses. */
unusedirqs = 0xffffffffffffffffUL;
for (irq = 0; irq < MAX_INTR_SOURCES; irq++) {
int levels = 0;
if (ci->ci_isources[irq] == NULL) {
intrlevel[irq] = 0;
continue;
}
for (q = ci->ci_isources[irq]->is_handlers; q; q = q->ih_next)
levels |= (1 << q->ih_level);
intrlevel[irq] = levels;
if (levels)
unusedirqs &= ~(1UL << irq);
}
/* Then figure out which IRQs use each level. */
for (level = 0; level < NIPL; level++) {
u_int64_t irqs = 0;
for (irq = 0; irq < MAX_INTR_SOURCES; irq++)
if (intrlevel[irq] & (1 << level))
irqs |= (1UL << irq);
ci->ci_imask[level] = irqs | unusedirqs;
}
for (level = 0; level< (NIPL - 1); level++)
ci->ci_imask[level + 1] |= ci->ci_imask[level];
for (irq = 0; irq < MAX_INTR_SOURCES; irq++) {
int maxlevel = IPL_NONE;
int minlevel = IPL_HIGH;
if (ci->ci_isources[irq] == NULL)
continue;
for (q = ci->ci_isources[irq]->is_handlers; q;
q = q->ih_next) {
if (q->ih_level < minlevel)
minlevel = q->ih_level;
if (q->ih_level > maxlevel)
maxlevel = q->ih_level;
}
ci->ci_isources[irq]->is_maxlevel = maxlevel;
ci->ci_isources[irq]->is_minlevel = minlevel;
}
for (level = 0; level < NIPL; level++)
ci->ci_iunmask[level] = ~ci->ci_imask[level];
}
int
intr_allocate_slot_cpu(struct cpu_info *ci, struct pic *pic, int pin,
int *index)
{
int start, slot, i;
struct intrsource *isp;
start = CPU_IS_PRIMARY(ci) ? NUM_LEGACY_IRQS : 0;
slot = -1;
for (i = 0; i < start; i++) {
isp = ci->ci_isources[i];
if (isp != NULL && isp->is_pic == pic && isp->is_pin == pin) {
slot = i;
start = MAX_INTR_SOURCES;
break;
}
}
for (i = start; i < MAX_INTR_SOURCES ; i++) {
isp = ci->ci_isources[i];
if (isp != NULL && isp->is_pic == pic && isp->is_pin == pin) {
slot = i;
break;
}
if (isp == NULL && slot == -1) {
slot = i;
continue;
}
}
if (slot == -1) {
return EBUSY;
}
isp = ci->ci_isources[slot];
if (isp == NULL) {
isp = malloc(sizeof (struct intrsource), M_DEVBUF,
M_NOWAIT|M_ZERO);
if (isp == NULL) {
return ENOMEM;
}
snprintf(isp->is_evname, sizeof (isp->is_evname),
"pin %d", pin);
ci->ci_isources[slot] = isp;
}
*index = slot;
return 0;
}
/*
* A simple round-robin allocator to assign interrupts to CPUs.
*/
int
intr_allocate_slot(struct pic *pic, int legacy_irq, int pin, int level,
struct cpu_info **cip, int *index, int *idt_slot)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
struct intrsource *isp;
int slot, idtvec, error;
/*
* If a legacy IRQ is wanted, try to use a fixed slot pointing
* at the primary CPU. In the case of IO APICs, multiple pins
* may map to one legacy IRQ, but they should not be shared
* in that case, so the first one gets the legacy slot, but
* a subsequent allocation with a different pin will get
* a different slot.
*/
if (legacy_irq != -1) {
ci = &cpu_info_primary;
/* must check for duplicate pic + pin first */
for (slot = 0 ; slot < MAX_INTR_SOURCES ; slot++) {
isp = ci->ci_isources[slot];
if (isp != NULL && isp->is_pic == pic &&
isp->is_pin == pin ) {
goto duplicate;
}
}
slot = legacy_irq;
isp = ci->ci_isources[slot];
if (isp == NULL) {
isp = malloc(sizeof (struct intrsource), M_DEVBUF,
M_NOWAIT|M_ZERO);
if (isp == NULL)
return ENOMEM;
snprintf(isp->is_evname, sizeof (isp->is_evname),
"pin %d", pin);
ci->ci_isources[slot] = isp;
} else {
if (isp->is_pic != pic || isp->is_pin != pin) {
if (pic == &i8259_pic)
return EINVAL;
goto other;
}
}
duplicate:
if (pic == &i8259_pic)
idtvec = ICU_OFFSET + legacy_irq;
else {
#ifdef IOAPIC_HWMASK
if (level > isp->is_maxlevel) {
#else
if (isp->is_minlevel == 0 || level < isp->is_minlevel) {
#endif
idtvec = idt_vec_alloc(APIC_LEVEL(level),
IDT_INTR_HIGH);
if (idtvec == 0)
return EBUSY;
} else
idtvec = isp->is_idtvec;
}
} else {
other:
/*
* Otherwise, look for a free slot elsewhere. If cip is null, it
* means try primary cpu but accept secondary, otherwise we need
* a slot on the requested cpu.
*/
if (*cip == NULL)
ci = &cpu_info_primary;
else
ci = *cip;
error = intr_allocate_slot_cpu(ci, pic, pin, &slot);
if (error == 0)
goto found;
/* Can't alloc on the requested cpu, fail. */
if (*cip != NULL)
return EBUSY;
/*
* ..now try the others.
*/
CPU_INFO_FOREACH(cii, ci) {
if (CPU_IS_PRIMARY(ci))
continue;
error = intr_allocate_slot_cpu(ci, pic, pin, &slot);
if (error == 0)
goto found;
}
return EBUSY;
found:
idtvec = idt_vec_alloc(APIC_LEVEL(level), IDT_INTR_HIGH);
if (idtvec == 0) {
free(ci->ci_isources[slot], M_DEVBUF, sizeof (struct intrsource));
ci->ci_isources[slot] = NULL;
return EBUSY;
}
}
*idt_slot = idtvec;
*index = slot;
*cip = ci;
return 0;
}
/*
* True if the system has any non-level interrupts which are shared
* on the same pin.
*/
int intr_shared_edge;
void *
intr_establish(int legacy_irq, struct pic *pic, int pin, int type, int level,
struct cpu_info *ci, int (*handler)(void *), void *arg, const char *what)
{
struct intrhand **p, *q, *ih;
int slot, error, idt_vec;
struct intrsource *source;
struct intrstub *stubp;
int flags;
#ifdef DIAGNOSTIC
if (legacy_irq != -1 && (legacy_irq < 0 || legacy_irq > 15))
panic("intr_establish: bad legacy IRQ value");
if (legacy_irq == -1 && pic == &i8259_pic)
panic("intr_establish: non-legacy IRQ on i8259");
#endif
flags = level & IPL_MPSAFE;
level &= ~IPL_MPSAFE;
KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE);
error = intr_allocate_slot(pic, legacy_irq, pin, level, &ci, &slot,
&idt_vec);
if (error != 0) {
printf("failed to allocate interrupt slot for PIC %s pin %d\n",
pic->pic_dev.dv_xname, pin);
return NULL;
}
/* no point in sleeping unless someone can free memory. */
ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
if (ih == NULL) {
printf("intr_establish: can't allocate handler info\n");
return NULL;
}
source = ci->ci_isources[slot];
if (source->is_handlers != NULL &&
source->is_pic->pic_type != pic->pic_type) {
free(ih, M_DEVBUF, sizeof(*ih));
printf("intr_establish: can't share intr source between "
"different PIC types (legacy_irq %d pin %d slot %d)\n",
legacy_irq, pin, slot);
return NULL;
}
source->is_pin = pin;
source->is_pic = pic;
switch (source->is_type) {
case IST_NONE:
source->is_type = type;
break;
case IST_EDGE:
intr_shared_edge = 1;
/* FALLTHROUGH */
case IST_LEVEL:
if (source->is_type == type)
break;
case IST_PULSE:
if (type != IST_NONE) {
printf("intr_establish: pic %s pin %d: can't share "
"type %d with %d\n", pic->pic_name, pin,
source->is_type, type);
free(ih, M_DEVBUF, sizeof(*ih));
return NULL;
}
break;
default:
panic("intr_establish: bad intr type %d for pic %s pin %d",
source->is_type, pic->pic_dev.dv_xname, pin);
}
if (!cold)
pic->pic_hwmask(pic, pin);
/*
* Figure out where to put the handler.
* This is O(N^2), but we want to preserve the order, and N is
* generally small.
*/
for (p = &ci->ci_isources[slot]->is_handlers;
(q = *p) != NULL && q->ih_level > level;
p = &q->ih_next)
;
ih->ih_fun = handler;
ih->ih_arg = arg;
ih->ih_next = *p;
ih->ih_level = level;
ih->ih_flags = flags;
ih->ih_pin = pin;
ih->ih_cpu = ci;
ih->ih_slot = slot;
evcount_attach(&ih->ih_count, what, &source->is_idtvec);
*p = ih;
intr_calculatemasks(ci);
if (ci->ci_isources[slot]->is_resume == NULL ||
source->is_idtvec != idt_vec) {
if (source->is_idtvec != 0 && source->is_idtvec != idt_vec)
idt_vec_free(source->is_idtvec);
source->is_idtvec = idt_vec;
stubp = type == IST_LEVEL ?
&pic->pic_level_stubs[slot] : &pic->pic_edge_stubs[slot];
ci->ci_isources[slot]->is_resume = stubp->ist_resume;
ci->ci_isources[slot]->is_recurse = stubp->ist_recurse;
setgate(&idt[idt_vec], stubp->ist_entry, 0, SDT_SYS386IGT,
SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
}
pic->pic_addroute(pic, ci, pin, idt_vec, type);
if (!cold)
pic->pic_hwunmask(pic, pin);
#ifdef INTRDEBUG
printf("allocated pic %s type %s pin %d level %d to cpu%u slot %d idt entry %d\n",
pic->pic_name, type == IST_EDGE ? "edge" : "level", pin, level,
ci->ci_apicid, slot, idt_vec);
#endif
return (ih);
}
/*
* Deregister an interrupt handler.
*/
void
intr_disestablish(struct intrhand *ih)
{
struct intrhand **p, *q;
struct cpu_info *ci;
struct pic *pic;
struct intrsource *source;
int idtvec;
ci = ih->ih_cpu;
pic = ci->ci_isources[ih->ih_slot]->is_pic;
source = ci->ci_isources[ih->ih_slot];
idtvec = source->is_idtvec;
pic->pic_hwmask(pic, ih->ih_pin);
x86_atomic_clearbits_u64(&ci->ci_ipending, (1UL << ih->ih_slot));
/*
* Remove the handler from the chain.
*/
for (p = &source->is_handlers; (q = *p) != NULL && q != ih;
p = &q->ih_next)
;
if (q == NULL) {
panic("intr_disestablish: handler not registered");
}
*p = q->ih_next;
intr_calculatemasks(ci);
if (source->is_handlers == NULL)
pic->pic_delroute(pic, ci, ih->ih_pin, idtvec, source->is_type);
else
pic->pic_hwunmask(pic, ih->ih_pin);
#ifdef INTRDEBUG
printf("cpu%u: remove slot %d (pic %s pin %d vec %d)\n",
ci->ci_apicid, ih->ih_slot, pic->pic_dev.dv_xname, ih->ih_pin,
idtvec);
#endif
if (source->is_handlers == NULL) {
free(source, M_DEVBUF, sizeof (struct intrsource));
ci->ci_isources[ih->ih_slot] = NULL;
if (pic != &i8259_pic)
idt_vec_free(idtvec);
}
evcount_detach(&ih->ih_count);
free(ih, M_DEVBUF, sizeof(*ih));
}
int
intr_handler(struct intrframe *frame, struct intrhand *ih)
{
struct cpu_info *ci = curcpu();
int floor;
int rc;
#ifdef MULTIPROCESSOR
int need_lock;
if (ih->ih_flags & IPL_MPSAFE)
need_lock = 0;
else
need_lock = 1;
if (need_lock)
__mp_lock(&kernel_lock);
#endif
floor = ci->ci_handled_intr_level;
ci->ci_handled_intr_level = ih->ih_level;
rc = (*ih->ih_fun)(ih->ih_arg ? ih->ih_arg : frame);
ci->ci_handled_intr_level = floor;
#ifdef MULTIPROCESSOR
if (need_lock)
__mp_unlock(&kernel_lock);
#endif
return rc;
}
#define CONCAT(x,y) __CONCAT(x,y)
/*
* Fake interrupt handler structures for the benefit of symmetry with
* other interrupt sources, and the benefit of intr_calculatemasks()
*/
struct intrhand fake_softclock_intrhand;
struct intrhand fake_softnet_intrhand;
struct intrhand fake_softtty_intrhand;
struct intrhand fake_timer_intrhand;
struct intrhand fake_ipi_intrhand;
#if NXEN > 0
struct intrhand fake_xen_intrhand;
#endif
#if NHYPERV > 0
struct intrhand fake_hyperv_intrhand;
#endif
/*
* Initialize all handlers that aren't dynamically allocated, and exist
* for each CPU.
*/
void
cpu_intr_init(struct cpu_info *ci)
{
struct intrsource *isp;
#if NLAPIC > 0 && defined(MULTIPROCESSOR) && 0
int i;
#endif
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xsoftclock;
isp->is_resume = Xsoftclock;
fake_softclock_intrhand.ih_level = IPL_SOFTCLOCK;
isp->is_handlers = &fake_softclock_intrhand;
isp->is_pic = &softintr_pic;
ci->ci_isources[SIR_CLOCK] = isp;
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xsoftnet;
isp->is_resume = Xsoftnet;
fake_softnet_intrhand.ih_level = IPL_SOFTNET;
isp->is_handlers = &fake_softnet_intrhand;
isp->is_pic = &softintr_pic;
ci->ci_isources[SIR_NET] = isp;
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xsofttty;
isp->is_resume = Xsofttty;
fake_softtty_intrhand.ih_level = IPL_SOFTTTY;
isp->is_handlers = &fake_softtty_intrhand;
isp->is_pic = &softintr_pic;
ci->ci_isources[SIR_TTY] = isp;
#if NLAPIC > 0
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xrecurse_lapic_ltimer;
isp->is_resume = Xresume_lapic_ltimer;
fake_timer_intrhand.ih_level = IPL_CLOCK;
isp->is_handlers = &fake_timer_intrhand;
isp->is_pic = &local_pic;
ci->ci_isources[LIR_TIMER] = isp;
#ifdef MULTIPROCESSOR
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xrecurse_lapic_ipi;
isp->is_resume = Xresume_lapic_ipi;
fake_ipi_intrhand.ih_level = IPL_IPI;
isp->is_handlers = &fake_ipi_intrhand;
isp->is_pic = &local_pic;
ci->ci_isources[LIR_IPI] = isp;
#endif
#if NXEN > 0
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xrecurse_xen_upcall;
isp->is_resume = Xresume_xen_upcall;
fake_xen_intrhand.ih_level = IPL_NET;
isp->is_handlers = &fake_xen_intrhand;
isp->is_pic = &local_pic;
ci->ci_isources[LIR_XEN] = isp;
#endif
#if NHYPERV > 0
isp = malloc(sizeof (struct intrsource), M_DEVBUF, M_NOWAIT|M_ZERO);
if (isp == NULL)
panic("can't allocate fixed interrupt source");
isp->is_recurse = Xrecurse_hyperv_upcall;
isp->is_resume = Xresume_hyperv_upcall;
fake_hyperv_intrhand.ih_level = IPL_NET;
isp->is_handlers = &fake_hyperv_intrhand;
isp->is_pic = &local_pic;
ci->ci_isources[LIR_HYPERV] = isp;
#endif
#endif /* NLAPIC */
intr_calculatemasks(ci);
}
void
intr_printconfig(void)
{
#ifdef INTRDEBUG
int i;
struct intrhand *ih;
struct intrsource *isp;
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
CPU_INFO_FOREACH(cii, ci) {
printf("cpu%d: interrupt masks:\n", ci->ci_apicid);
for (i = 0; i < NIPL; i++)
printf("IPL %d mask %lx unmask %lx\n", i,
(u_long)ci->ci_imask[i], (u_long)ci->ci_iunmask[i]);
for (i = 0; i < MAX_INTR_SOURCES; i++) {
isp = ci->ci_isources[i];
if (isp == NULL)
continue;
printf("cpu%u source %d is pin %d from pic %s maxlevel %d\n",
ci->ci_apicid, i, isp->is_pin,
isp->is_pic->pic_name, isp->is_maxlevel);
for (ih = isp->is_handlers; ih != NULL;
ih = ih->ih_next)
printf("\thandler %p level %d\n",
ih->ih_fun, ih->ih_level);
}
}
#endif
}
void
intr_barrier(void *cookie)
{
struct intrhand *ih = cookie;
sched_barrier(ih->ih_cpu);
}
/*
* Add a mask to cpl, and return the old value of cpl.
*/
int
splraise(int nlevel)
{
int olevel;
struct cpu_info *ci = curcpu();
KASSERT(nlevel >= IPL_NONE);
olevel = ci->ci_ilevel;
ci->ci_ilevel = MAX(ci->ci_ilevel, nlevel);
return (olevel);
}
/*
* Restore a value to cpl (unmasking interrupts). If any unmasked
* interrupts are pending, call Xspllower() to process them.
*/
int
spllower(int nlevel)
{
int olevel;
struct cpu_info *ci = curcpu();
u_int64_t imask;
u_long flags;
imask = IUNMASK(ci, nlevel);
olevel = ci->ci_ilevel;
flags = intr_disable();
if (ci->ci_ipending & imask) {
Xspllower(nlevel);
} else {
ci->ci_ilevel = nlevel;
intr_restore(flags);
}
return (olevel);
}
/*
* Software interrupt registration
*
* We hand-code this to ensure that it's atomic.
*
* XXX always scheduled on the current CPU.
*/
void
softintr(int sir)
{
struct cpu_info *ci = curcpu();
__asm volatile("lock; orq %1, %0" :
"=m"(ci->ci_ipending) : "ir" (1UL << sir));
}
14
14
14
14
7
14
7
14
12
12
7
5
5
2
8
5
4
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/* $OpenBSD: kern_sensors.c,v 1.39 2019/12/19 17:40:11 mpi Exp $ */
/*
* Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
* Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <sys/hotplug.h>
#include <sys/timeout.h>
#include <sys/task.h>
#include <sys/rwlock.h>
#include <sys/atomic.h>
#include <sys/sensors.h>
#include "hotplug.h"
struct taskq *sensors_taskq;
int sensordev_count;
SLIST_HEAD(, ksensordev) sensordev_list =
SLIST_HEAD_INITIALIZER(sensordev_list);
void
sensordev_install(struct ksensordev *sensdev)
{
struct ksensordev *v, *nv;
int s;
s = splhigh();
if (sensordev_count == 0) {
sensdev->num = 0;
SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
} else {
for (v = SLIST_FIRST(&sensordev_list);
(nv = SLIST_NEXT(v, list)) != NULL; v = nv)
if (nv->num - v->num > 1)
break;
sensdev->num = v->num + 1;
SLIST_INSERT_AFTER(v, sensdev, list);
}
sensordev_count++;
splx(s);
#if NHOTPLUG > 0
hotplug_device_attach(DV_DULL, "sensordev");
#endif
}
void
sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
{
struct ksensor *v, *nv;
struct ksensors_head *sh;
int s, i;
s = splhigh();
sh = &sensdev->sensors_list;
if (sensdev->sensors_count == 0) {
for (i = 0; i < SENSOR_MAX_TYPES; i++)
sensdev->maxnumt[i] = 0;
sens->numt = 0;
SLIST_INSERT_HEAD(sh, sens, list);
} else {
for (v = SLIST_FIRST(sh);
(nv = SLIST_NEXT(v, list)) != NULL; v = nv)
if (v->type == sens->type && (v->type != nv->type ||
(v->type == nv->type && nv->numt - v->numt > 1)))
break;
/* sensors of the same type go after each other */
if (v->type == sens->type)
sens->numt = v->numt + 1;
else
sens->numt = 0;
SLIST_INSERT_AFTER(v, sens, list);
}
/* we only increment maxnumt[] if the sensor was added
* to the last position of sensors of this type
*/
if (sensdev->maxnumt[sens->type] == sens->numt)
sensdev->maxnumt[sens->type]++;
sensdev->sensors_count++;
splx(s);
}
void
sensordev_deinstall(struct ksensordev *sensdev)
{
int s;
s = splhigh();
sensordev_count--;
SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
splx(s);
#if NHOTPLUG > 0
hotplug_device_detach(DV_DULL, "sensordev");
#endif
}
void
sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
{
struct ksensors_head *sh;
int s;
s = splhigh();
sh = &sensdev->sensors_list;
sensdev->sensors_count--;
SLIST_REMOVE(sh, sens, ksensor, list);
/* we only decrement maxnumt[] if this is the tail
* sensor of this type
*/
if (sens->numt == sensdev->maxnumt[sens->type] - 1)
sensdev->maxnumt[sens->type]--;
splx(s);
}
int
sensordev_get(int num, struct ksensordev **sensdev)
{
struct ksensordev *sd;
SLIST_FOREACH(sd, &sensordev_list, list) {
if (sd->num == num) {
*sensdev = sd;
return (0);
}
if (sd->num > num)
return (ENXIO);
}
return (ENOENT);
}
int
sensor_find(int dev, enum sensor_type type, int numt, struct ksensor **ksensorp)
{
struct ksensor *s;
struct ksensordev *sensdev;
struct ksensors_head *sh;
int ret;
ret = sensordev_get(dev, &sensdev);
if (ret)
return (ret);
sh = &sensdev->sensors_list;
SLIST_FOREACH(s, sh, list)
if (s->type == type && s->numt == numt) {
*ksensorp = s;
return (0);
}
return (ENOENT);
}
struct sensor_task {
void (*func)(void *);
void *arg;
unsigned int period;
struct timeout timeout;
struct task task;
struct rwlock lock;
};
void sensor_task_tick(void *);
void sensor_task_work(void *);
struct sensor_task *
sensor_task_register(void *arg, void (*func)(void *), unsigned int period)
{
struct sensor_task *st;
#ifdef DIAGNOSTIC
if (period == 0)
panic("sensor_task_register: period is 0");
#endif
if (sensors_taskq == NULL &&
(sensors_taskq = taskq_create("sensors", 1, IPL_HIGH, 0)) == NULL)
sensors_taskq = systq;
st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT);
if (st == NULL)
return (NULL);
st->func = func;
st->arg = arg;
st->period = period;
timeout_set(&st->timeout, sensor_task_tick, st);
task_set(&st->task, sensor_task_work, st);
rw_init(&st->lock, "sensor");
sensor_task_tick(st);
return (st);
}
void
sensor_task_unregister(struct sensor_task *st)
{
/*
* we can't reliably timeout_del or task_del because there's a window
* between when they come off the lists and the timeout or task code
* actually runs the respective handlers for them. mark the sensor_task
* as dying by setting period to 0 and let sensor_task_work mop up.
*/
rw_enter_write(&st->lock);
st->period = 0;
rw_exit_write(&st->lock);
}
void
sensor_task_tick(void *arg)
{
struct sensor_task *st = arg;
task_add(sensors_taskq, &st->task);
}
static int sensors_quiesced;
static int sensors_running;
void
sensor_quiesce(void)
{
sensors_quiesced = 1;
while (sensors_running > 0)
tsleep_nsec(&sensors_running, PZERO, "sensorpause", INFSLP);
}
void
sensor_restart(void)
{
sensors_quiesced = 0;
}
void
sensor_task_work(void *xst)
{
struct sensor_task *st = xst;
unsigned int period = 0;
atomic_inc_int(&sensors_running);
rw_enter_write(&st->lock);
period = st->period;
if (period > 0 && !sensors_quiesced)
st->func(st->arg);
rw_exit_write(&st->lock);
if (atomic_dec_int_nv(&sensors_running) == 0) {
if (sensors_quiesced)
wakeup(&sensors_running);
}
if (period == 0)
free(st, M_DEVBUF, sizeof(*st));
else
timeout_add_sec(&st->timeout, period);
}
6
2
4
2
4
2
3
7
6
2
6
13
3
10
12
1
2
9
2
7
4
2
4
6
12
11
2
6
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/* $OpenBSD: vfs_getcwd.c,v 1.37 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
/*
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Bill Sommerfeld.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/filedesc.h>
#include <sys/stat.h>
#include <sys/lock.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/ktrace.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/dirent.h>
#include <ufs/ufs/dir.h> /* only for DIRBLKSIZ */
#include <sys/syscallargs.h>
/* Find parent vnode of *lvpp, return in *uvpp */
int
vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
char *bufp, struct proc *p)
{
int eofflag, tries, dirbuflen = 0, len, reclen, error = 0;
off_t off;
struct uio uio;
struct iovec iov;
char *dirbuf = NULL;
ino_t fileno;
struct vattr va;
struct vnode *uvp = NULL;
struct vnode *lvp = *lvpp;
struct componentname cn;
tries = 0;
/*
* If we want the filename, get some info we need while the
* current directory is still locked.
*/
if (bufp != NULL) {
error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
if (error) {
vput(lvp);
*lvpp = NULL;
*uvpp = NULL;
return (error);
}
}
cn.cn_nameiop = LOOKUP;
cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
cn.cn_proc = p;
cn.cn_cred = p->p_ucred;
cn.cn_pnbuf = NULL;
cn.cn_nameptr = "..";
cn.cn_namelen = 2;
cn.cn_consume = 0;
/* Get parent vnode using lookup of '..' */
error = VOP_LOOKUP(lvp, uvpp, &cn);
if (error) {
vput(lvp);
*lvpp = NULL;
*uvpp = NULL;
return (error);
}
uvp = *uvpp;
/* If we don't care about the pathname, we're done */
if (bufp == NULL) {
error = 0;
goto out;
}
fileno = va.va_fileid;
dirbuflen = DIRBLKSIZ;
if (dirbuflen < va.va_blocksize)
dirbuflen = va.va_blocksize;
/* XXX we need some limit for fuse, 1 MB should be enough */
if (dirbuflen > 0xfffff) {
error = EINVAL;
goto out;
}
dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
off = 0;
do {
char *cpos;
struct dirent *dp;
iov.iov_base = dirbuf;
iov.iov_len = dirbuflen;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = off;
uio.uio_resid = dirbuflen;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = p;
eofflag = 0;
/* Call VOP_READDIR of parent */
error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag);
off = uio.uio_offset;
/* Try again if NFS tosses its cookies */
if (error == EINVAL && tries < 3) {
tries++;
off = 0;
continue;
} else if (error) {
goto out; /* Old userland getcwd() behaviour */
}
cpos = dirbuf;
tries = 0;
/* Scan directory page looking for matching vnode */
for (len = (dirbuflen - uio.uio_resid); len > 0;
len -= reclen) {
dp = (struct dirent *)cpos;
reclen = dp->d_reclen;
/* Check for malformed directory */
if (reclen < DIRENT_RECSIZE(1) || reclen > len) {
error = EINVAL;
goto out;
}
if (dp->d_fileno == fileno) {
char *bp = *bpp;
if (offsetof(struct dirent, d_name) +
dp->d_namlen > reclen) {
error = EINVAL;
goto out;
}
bp -= dp->d_namlen;
if (bp <= bufp) {
error = ERANGE;
goto out;
}
memmove(bp, dp->d_name, dp->d_namlen);
error = 0;
*bpp = bp;
goto out;
}
cpos += reclen;
}
} while (!eofflag);
error = ENOENT;
out:
vrele(lvp);
*lvpp = NULL;
free(dirbuf, M_TEMP, dirbuflen);
return (error);
}
/* Do a lookup in the vnode-to-name reverse */
int
vfs_getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
char *bufp)
{
struct vnode *lvp, *uvp = NULL;
char *obp;
int error, vpid;
lvp = *lvpp;
obp = *bpp; /* Save original position to restore to on error */
error = cache_revlookup(lvp, uvpp, bpp, bufp);
if (error) {
if (error != -1) {
vput(lvp);
*lvpp = NULL;
*uvpp = NULL;
}
return (error);
}
uvp = *uvpp;
vpid = uvp->v_id;
/* Release current lock before acquiring the parent lock */
VOP_UNLOCK(lvp);
error = vget(uvp, LK_EXCLUSIVE | LK_RETRY);
if (error)
*uvpp = NULL;
/*
* Verify that vget() succeeded, and check that vnode capability
* didn't change while we were waiting for the lock.
*/
if (error || (vpid != uvp->v_id)) {
/*
* Try to get our lock back. If that works, tell the caller to
* try things the hard way, otherwise give up.
*/
if (!error)
vput(uvp);
*uvpp = NULL;
error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
if (!error) {
*bpp = obp; /* restore the buffer */
return (-1);
}
}
vrele(lvp);
*lvpp = NULL;
return (error);
}
/* Common routine shared by sys___getcwd() and vn_isunder() and sys___realpath() */
int
vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
int limit, int flags, struct proc *p)
{
struct filedesc *fdp = p->p_fd;
struct vnode *uvp = NULL;
char *bp = NULL;
int error, perms = VEXEC;
if (rvp == NULL) {
rvp = fdp->fd_rdir;
if (rvp == NULL)
rvp = rootvnode;
}
vref(rvp);
vref(lvp);
error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
if (error) {
vrele(lvp);
lvp = NULL;
goto out;
}
if (bufp)
bp = *bpp;
if (lvp == rvp) {
if (bp)
*(--bp) = '/';
goto out;
}
/*
* This loop will terminate when we hit the root, VOP_READDIR() or
* VOP_LOOKUP() fails, or we run out of space in the user buffer.
*/
do {
if (lvp->v_type != VDIR) {
error = ENOTDIR;
goto out;
}
/* Check for access if caller cares */
if (flags & GETCWD_CHECK_ACCESS) {
error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
if (error)
goto out;
perms = VEXEC|VREAD;
}
/* Step up if we're a covered vnode */
while (lvp->v_flag & VROOT) {
struct vnode *tvp;
if (lvp == rvp)
goto out;
tvp = lvp;
lvp = lvp->v_mount->mnt_vnodecovered;
vput(tvp);
if (lvp == NULL) {
error = ENOENT;
goto out;
}
vref(lvp);
error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
if (error) {
vrele(lvp);
lvp = NULL;
goto out;
}
}
/* Look in the name cache */
error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
if (error == -1) {
/* If that fails, look in the directory */
error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
}
if (error)
goto out;
#ifdef DIAGNOSTIC
if (lvp != NULL)
panic("getcwd: oops, forgot to null lvp");
if (bufp && (bp <= bufp)) {
panic("getcwd: oops, went back too far");
}
#endif
if (bp)
*(--bp) = '/';
lvp = uvp;
uvp = NULL;
limit--;
} while ((lvp != rvp) && (limit > 0));
out:
if (bpp)
*bpp = bp;
if (uvp)
vput(uvp);
if (lvp)
vput(lvp);
vrele(rvp);
return (error);
}
/* Find pathname of a process's current directory */
int
sys___getcwd(struct proc *p, void *v, register_t *retval)
{
struct sys___getcwd_args *uap = v;
int error, len = SCARG(uap, len);
char *path, *bp;
if (len > MAXPATHLEN * 4)
len = MAXPATHLEN * 4;
else if (len < 2)
return (ERANGE);
path = malloc(len, M_TEMP, M_WAITOK);
bp = &path[len - 1];
*bp = '\0';
/*
* 5th argument here is "max number of vnodes to traverse".
* Since each entry takes up at least 2 bytes in the output
* buffer, limit it to N/2 vnodes for an N byte buffer.
*/
error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
GETCWD_CHECK_ACCESS, p);
if (error)
goto out;
/* Put the result into user buffer */
error = copyoutstr(bp, SCARG(uap, buf), MAXPATHLEN, NULL);
#ifdef KTRACE
if (KTRPOINT(p, KTR_NAMEI))
ktrnamei(p, bp);
#endif
out:
free(path, M_TEMP, len);
return (error);
}
19
2
2
1
1
1
3
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
/* $OpenBSD: nfs_vfsops.c,v 1.127 2022/08/12 14:30:53 visa Exp $ */
/* $NetBSD: nfs_vfsops.c,v 1.46.4.1 1996/05/25 22:40:35 fvdl Exp $ */
/*
* Copyright (c) 1989, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)nfs_vfsops.c 8.12 (Berkeley) 5/20/95
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/swap.h>
#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/dirent.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs.h>
#include <nfs/nfsmount.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nfsdiskless.h>
#include <nfs/nfs_var.h>
extern struct nfsstats nfsstats;
extern int nfs_ticks;
extern u_int32_t nfs_procids[NFS_NPROCS];
int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
struct proc *);
int nfs_checkexp(struct mount *, struct mbuf *, int *, struct ucred **);
struct mount *nfs_mount_diskless(struct nfs_dlmount *, char *, int,
struct vnode **, struct proc *p);
int mountnfs(struct nfs_args *, struct mount *, struct mbuf *,
const char *, char *, struct vnode **, struct proc *p);
int nfs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
int nfs_root(struct mount *, struct vnode **);
int nfs_start(struct mount *, int, struct proc *);
int nfs_statfs(struct mount *, struct statfs *, struct proc *);
int nfs_sync(struct mount *, int, int, struct ucred *, struct proc *);
int nfs_unmount(struct mount *, int, struct proc *);
void nfs_reaper(void *);
int nfs_vget(struct mount *, ino_t, struct vnode **);
int nfs_vptofh(struct vnode *, struct fid *);
int nfs_mountroot(void);
void nfs_decode_args(struct nfsmount *, struct nfs_args *,
struct nfs_args *);
int nfs_fhtovp(struct mount *, struct fid *, struct vnode **);
/*
* nfs vfs operations.
*/
const struct vfsops nfs_vfsops = {
.vfs_mount = nfs_mount,
.vfs_start = nfs_start,
.vfs_unmount = nfs_unmount,
.vfs_root = nfs_root,
.vfs_quotactl = nfs_quotactl,
.vfs_statfs = nfs_statfs,
.vfs_sync = nfs_sync,
.vfs_vget = nfs_vget,
.vfs_fhtovp = nfs_fhtovp,
.vfs_vptofh = nfs_vptofh,
.vfs_init = nfs_vfs_init,
.vfs_sysctl = nfs_sysctl,
.vfs_checkexp = nfs_checkexp,
};
/*
* nfs statfs call
*/
int
nfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct vnode *vp;
struct nfs_statfs *sfp = NULL;
struct nfsm_info info;
u_int32_t *tl;
int32_t t1;
caddr_t cp2;
struct nfsmount *nmp = VFSTONFS(mp);
int error = 0, retattr;
struct ucred *cred;
u_quad_t tquad;
info.nmi_v3 = (nmp->nm_flag & NFSMNT_NFSV3);
error = nfs_root(mp, &vp);
if (error)
return (error);
cred = crget();
cred->cr_ngroups = 0;
if (info.nmi_v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0)
(void)nfs_fsinfo(nmp, vp, cred, p);
nfsstats.rpccnt[NFSPROC_FSSTAT]++;
info.nmi_mb = info.nmi_mreq = nfsm_reqhead(NFSX_FH(info.nmi_v3));
nfsm_fhtom(&info, vp, info.nmi_v3);
info.nmi_procp = p;
info.nmi_cred = cred;
error = nfs_request(vp, NFSPROC_FSSTAT, &info);
if (info.nmi_v3)
nfsm_postop_attr(vp, retattr);
if (error) {
m_freem(info.nmi_mrep);
goto nfsmout;
}
nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(info.nmi_v3));
sbp->f_iosize = min(nmp->nm_rsize, nmp->nm_wsize);
if (info.nmi_v3) {
sbp->f_bsize = NFS_FABLKSIZE;
tquad = fxdr_hyper(&sfp->sf_tbytes);
sbp->f_blocks = tquad / (u_quad_t)NFS_FABLKSIZE;
tquad = fxdr_hyper(&sfp->sf_fbytes);
sbp->f_bfree = tquad / (u_quad_t)NFS_FABLKSIZE;
tquad = fxdr_hyper(&sfp->sf_abytes);
sbp->f_bavail = (quad_t)tquad / (quad_t)NFS_FABLKSIZE;
tquad = fxdr_hyper(&sfp->sf_tfiles);
sbp->f_files = tquad;
tquad = fxdr_hyper(&sfp->sf_ffiles);
sbp->f_ffree = tquad;
sbp->f_favail = tquad;
} else {
sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
sbp->f_files = 0;
sbp->f_ffree = 0;
sbp->f_favail = 0;
}
copy_statfs_info(sbp, mp);
m_freem(info.nmi_mrep);
nfsmout:
vput(vp);
crfree(cred);
return (error);
}
/*
* nfs version 3 fsinfo rpc call
*/
int
nfs_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
struct proc *p)
{
struct nfsv3_fsinfo *fsp;
struct nfsm_info info;
int32_t t1;
u_int32_t *tl, pref, max;
caddr_t cp2;
int error = 0, retattr;
nfsstats.rpccnt[NFSPROC_FSINFO]++;
info.nmi_mb = info.nmi_mreq = nfsm_reqhead(NFSX_FH(1));
nfsm_fhtom(&info, vp, 1);
info.nmi_procp = p;
info.nmi_cred = cred;
error = nfs_request(vp, NFSPROC_FSINFO, &info);
nfsm_postop_attr(vp, retattr);
if (error) {
m_freem(info.nmi_mrep);
goto nfsmout;
}
nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
pref = fxdr_unsigned(u_int32_t, fsp->fs_wtpref);
if (pref < nmp->nm_wsize)
nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
max = fxdr_unsigned(u_int32_t, fsp->fs_wtmax);
if (max < nmp->nm_wsize) {
nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize == 0)
nmp->nm_wsize = max;
}
pref = fxdr_unsigned(u_int32_t, fsp->fs_rtpref);
if (pref < nmp->nm_rsize)
nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
max = fxdr_unsigned(u_int32_t, fsp->fs_rtmax);
if (max < nmp->nm_rsize) {
nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize == 0)
nmp->nm_rsize = max;
}
pref = fxdr_unsigned(u_int32_t, fsp->fs_dtpref);
if (pref < nmp->nm_readdirsize)
nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) &
~(NFS_DIRBLKSIZ - 1);
if (max < nmp->nm_readdirsize) {
nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
if (nmp->nm_readdirsize == 0)
nmp->nm_readdirsize = max;
}
nmp->nm_flag |= NFSMNT_GOTFSINFO;
m_freem(info.nmi_mrep);
nfsmout:
return (error);
}
struct nfs_diskless nfs_diskless;
/*
* Mount a remote root fs via. NFS. It goes like this:
* - Call nfs_boot_init() to fill in the nfs_diskless struct
* (using RARP, bootparam RPC, mountd RPC)
* - hand craft the swap nfs vnode hanging off a fake mount point
* if swdevt[0].sw_dev == NODEV
* - build the rootfs mount point and call mountnfs() to do the rest.
*/
int
nfs_mountroot(void)
{
struct vattr attr;
struct mount *mp;
struct vnode *vp;
struct proc *procp;
long n;
int error;
procp = curproc; /* XXX */
/*
* Call nfs_boot_init() to fill in the nfs_diskless struct.
* Side effect: Finds and configures a network interface.
*/
nfs_boot_init(&nfs_diskless, procp);
/*
* Create the root mount point.
*/
if (nfs_boot_getfh(&nfs_diskless.nd_boot, "root", &nfs_diskless.nd_root, -1))
panic("nfs_mountroot: root");
mp = nfs_mount_diskless(&nfs_diskless.nd_root, "/", 0, &vp, procp);
printf("root on %s\n", nfs_diskless.nd_root.ndm_host);
/*
* Link it into the mount list.
*/
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
rootvp = vp;
vfs_unbusy(mp);
/* Get root attributes (for the time). */
error = VOP_GETATTR(rootvp, &attr, procp->p_ucred, procp);
if (error) panic("nfs_mountroot: getattr for root");
n = attr.va_atime.tv_sec;
#ifdef DEBUG
printf("root time: 0x%lx\n", n);
#endif
inittodr(n);
#ifdef notyet
/* Set up swap credentials. */
proc0.p_ucred->cr_uid = ntohl(nfs_diskless.swap_ucred.cr_uid);
proc0.p_ucred->cr_gid = ntohl(nfs_diskless.swap_ucred.cr_gid);
if ((proc0.p_ucred->cr_ngroups = ntohs(nfs_diskless.swap_ucred.cr_ngroups)) >
NGROUPS_MAX)
proc0.p_ucred->cr_ngroups = NGROUPS_MAX;
for (i = 0; i < proc0.p_ucred->cr_ngroups; i++)
proc0.p_ucred->cr_groups[i] = ntohl(nfs_diskless.swap_ucred.cr_groups[i]);
#endif
/*
* "Mount" the swap device.
*
* On a "dataless" configuration (swap on disk) we will have:
* (swdevt[0].sw_dev != NODEV) identifying the swap device.
*/
if (swdevt[0].sw_dev != NODEV) {
if (bdevvp(swapdev, &swapdev_vp))
panic("nfs_mountroot: can't setup swap vp");
printf("swap on device 0x%x\n", swdevt[0].sw_dev);
return (0);
}
/*
* If swapping to an nfs node: (swdevt[0].sw_dev == NODEV)
* Create a fake mount point just for the swap vnode so that the
* swap file can be on a different server from the rootfs.
*
* Wait 5 retries, finally no swap is cool. -mickey
*/
error = nfs_boot_getfh(&nfs_diskless.nd_boot, "swap", &nfs_diskless.nd_swap, 5);
if (!error) {
mp = nfs_mount_diskless(&nfs_diskless.nd_swap, "/swap", 0, &vp,
procp);
vfs_unbusy(mp);
/*
* Since the swap file is not the root dir of a file system,
* hack it to a regular file.
*/
vp->v_type = VREG;
vp->v_flag = 0;
/*
* Next line is a hack to make swapmount() work on NFS
* swap files.
*/
swdevt[0].sw_dev = NETDEV;
/* end hack */
nfs_diskless.sw_vp = vp;
/*
* Find out how large the swap file is.
*/
error = VOP_GETATTR(vp, &attr, procp->p_ucred, procp);
if (error)
printf("nfs_mountroot: getattr for swap\n");
n = (long) (attr.va_size >> DEV_BSHIFT);
printf("swap on %s\n", nfs_diskless.nd_swap.ndm_host);
#ifdef DEBUG
printf("swap size: 0x%lx (blocks)\n", n);
#endif
return (0);
}
printf("WARNING: no swap\n");
swdevt[0].sw_dev = NODEV;
return (0);
}
/*
* Internal version of mount system call for diskless setup.
*/
struct mount *
nfs_mount_diskless(struct nfs_dlmount *ndmntp, char *mntname, int mntflag,
struct vnode **vpp, struct proc *p)
{
struct mount *mp;
struct mbuf *m;
int error;
if (vfs_rootmountalloc("nfs", mntname, &mp))
panic("nfs_mount_diskless: vfs_rootmountalloc failed");
mp->mnt_flag |= mntflag;
/* Get mbuf for server sockaddr. */
m = m_get(M_WAIT, MT_SONAME);
bcopy(ndmntp->ndm_args.addr, mtod(m, caddr_t),
(m->m_len = ndmntp->ndm_args.addr->sa_len));
error = mountnfs(&ndmntp->ndm_args, mp, m, mntname,
ndmntp->ndm_args.hostname, vpp, p);
if (error)
panic("nfs_mountroot: mount %s failed: %d", mntname, error);
return (mp);
}
void
nfs_decode_args(struct nfsmount *nmp, struct nfs_args *argp,
struct nfs_args *nargp)
{
int adjsock = 0;
int maxio;
#if 0
/* Re-bind if rsrvd port requested and wasn't on one */
adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
&& (argp->flags & NFSMNT_RESVPORT);
#endif
/* Also re-bind if we're switching to/from a connected UDP socket */
adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
(argp->flags & NFSMNT_NOCONN));
nmp->nm_flag =
(argp->flags & ~NFSMNT_INTERNAL) | (nmp->nm_flag & NFSMNT_INTERNAL);
if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
if (nmp->nm_timeo < NFS_MINTIMEO)
nmp->nm_timeo = NFS_MINTIMEO;
else if (nmp->nm_timeo > NFS_MAXTIMEO)
nmp->nm_timeo = NFS_MAXTIMEO;
}
if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1)
nmp->nm_retry = MIN(argp->retrans, NFS_MAXREXMIT);
if (!(nmp->nm_flag & NFSMNT_SOFT))
nmp->nm_retry = NFS_MAXREXMIT + 1; /* past clip limit */
if (argp->flags & NFSMNT_NFSV3) {
if (argp->sotype == SOCK_DGRAM)
maxio = NFS_MAXDGRAMDATA;
else
maxio = NFS_MAXDATA;
} else
maxio = NFS_V2MAXDATA;
if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
int osize = nmp->nm_wsize;
nmp->nm_wsize = argp->wsize;
/* Round down to multiple of blocksize */
nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize <= 0)
nmp->nm_wsize = NFS_FABLKSIZE;
adjsock |= (nmp->nm_wsize != osize);
}
if (nmp->nm_wsize > maxio)
nmp->nm_wsize = maxio;
if (nmp->nm_wsize > MAXBSIZE)
nmp->nm_wsize = MAXBSIZE;
if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
int osize = nmp->nm_rsize;
nmp->nm_rsize = argp->rsize;
/* Round down to multiple of blocksize */
nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize <= 0)
nmp->nm_rsize = NFS_FABLKSIZE;
adjsock |= (nmp->nm_rsize != osize);
}
if (nmp->nm_rsize > maxio)
nmp->nm_rsize = maxio;
if (nmp->nm_rsize > MAXBSIZE)
nmp->nm_rsize = MAXBSIZE;
if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
nmp->nm_readdirsize = argp->readdirsize;
/* Round down to multiple of blocksize */
nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
nmp->nm_readdirsize = NFS_DIRBLKSIZ;
} else if (argp->flags & NFSMNT_RSIZE)
nmp->nm_readdirsize = nmp->nm_rsize;
if (nmp->nm_readdirsize > maxio)
nmp->nm_readdirsize = maxio;
if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
argp->maxgrouplist <= NFS_MAXGRPS)
nmp->nm_numgrps = argp->maxgrouplist;
if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
argp->readahead <= NFS_MAXRAHEAD)
nmp->nm_readahead = argp->readahead;
if (argp->flags & NFSMNT_ACREGMIN && argp->acregmin >= 0) {
if (argp->acregmin > 0xffff)
nmp->nm_acregmin = 0xffff;
else
nmp->nm_acregmin = argp->acregmin;
}
if (argp->flags & NFSMNT_ACREGMAX && argp->acregmax >= 0) {
if (argp->acregmax > 0xffff)
nmp->nm_acregmax = 0xffff;
else
nmp->nm_acregmax = argp->acregmax;
}
if (nmp->nm_acregmin > nmp->nm_acregmax)
nmp->nm_acregmin = nmp->nm_acregmax;
if (argp->flags & NFSMNT_ACDIRMIN && argp->acdirmin >= 0) {
if (argp->acdirmin > 0xffff)
nmp->nm_acdirmin = 0xffff;
else
nmp->nm_acdirmin = argp->acdirmin;
}
if (argp->flags & NFSMNT_ACDIRMAX && argp->acdirmax >= 0) {
if (argp->acdirmax > 0xffff)
nmp->nm_acdirmax = 0xffff;
else
nmp->nm_acdirmax = argp->acdirmax;
}
if (nmp->nm_acdirmin > nmp->nm_acdirmax)
nmp->nm_acdirmin = nmp->nm_acdirmax;
if (nmp->nm_so && adjsock) {
nfs_disconnect(nmp);
if (nmp->nm_sotype == SOCK_DGRAM)
while (nfs_connect(nmp, NULL)) {
printf("nfs_args: retrying connect\n");
tsleep_nsec(&nowake, PSOCK, "nfscon",
SEC_TO_NSEC(1));
}
}
/* Update nargp based on nmp */
nargp->wsize = nmp->nm_wsize;
nargp->rsize = nmp->nm_rsize;
nargp->readdirsize = nmp->nm_readdirsize;
nargp->timeo = nmp->nm_timeo;
nargp->retrans = nmp->nm_retry;
nargp->maxgrouplist = nmp->nm_numgrps;
nargp->readahead = nmp->nm_readahead;
nargp->acregmin = nmp->nm_acregmin;
nargp->acregmax = nmp->nm_acregmax;
nargp->acdirmin = nmp->nm_acdirmin;
nargp->acdirmax = nmp->nm_acdirmax;
}
/*
* VFS Operations.
*
* mount system call
* It seems a bit dumb to copyinstr() the host here and then
* bcopy() it in mountnfs(), but I wanted to detect errors before
* doing the sockargs() call because sockargs() allocates an mbuf and
* an error after that means that I have to release the mbuf.
*/
/* ARGSUSED */
int
nfs_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
int error;
struct nfs_args *args = data;
struct mbuf *nam;
struct vnode *vp;
char hst[MNAMELEN];
size_t len;
u_char nfh[NFSX_V3FHMAX];
if (args &&
(args->flags & (NFSMNT_NFSV3|NFSMNT_RDIRPLUS)) == NFSMNT_RDIRPLUS)
return (EINVAL);
if (nfs_niothreads < 0) {
nfs_niothreads = 4;
nfs_getset_niothreads(1);
}
if (mp->mnt_flag & MNT_UPDATE) {
struct nfsmount *nmp = VFSTONFS(mp);
if (nmp == NULL)
return (EIO);
/*
* When doing an update, we can't change from or to
* v3.
*/
if (args) {
args->flags = (args->flags & ~(NFSMNT_NFSV3)) |
(nmp->nm_flag & (NFSMNT_NFSV3));
nfs_decode_args(nmp, args, &mp->mnt_stat.mount_info.nfs_args);
}
return (0);
}
if (args->fhsize < 0 || args->fhsize > NFSX_V3FHMAX)
return (EINVAL);
error = copyin(args->fh, nfh, args->fhsize);
if (error)
return (error);
error = copyinstr(args->hostname, hst, MNAMELEN-1, &len);
if (error)
return (error);
memset(&hst[len], 0, MNAMELEN - len);
/* sockargs() call must be after above copyin() calls */
error = sockargs(&nam, args->addr, args->addrlen, MT_SONAME);
if (error)
return (error);
args->fh = nfh;
error = mountnfs(args, mp, nam, path, hst, &vp, p);
return (error);
}
/*
* Common code for mount and mountroot
*/
int
mountnfs(struct nfs_args *argp, struct mount *mp, struct mbuf *nam,
const char *pth, char *hst, struct vnode **vpp, struct proc *p)
{
struct nfsmount *nmp;
struct nfsnode *np;
struct vnode *vp;
struct vattr attr;
int error;
if (mp->mnt_flag & MNT_UPDATE) {
nmp = VFSTONFS(mp);
/* update paths, file handles, etc, here XXX */
m_freem(nam);
return (0);
} else {
nmp = malloc(sizeof(*nmp), M_NFSMNT,
M_WAITOK|M_ZERO);
mp->mnt_data = nmp;
}
vfs_getnewfsid(mp);
nmp->nm_mountp = mp;
nmp->nm_timeo = NFS_TIMEO;
nmp->nm_retry = NFS_RETRANS;
nmp->nm_wsize = NFS_WSIZE;
nmp->nm_rsize = NFS_RSIZE;
nmp->nm_readdirsize = NFS_READDIRSIZE;
nmp->nm_numgrps = NFS_MAXGRPS;
nmp->nm_readahead = NFS_DEFRAHEAD;
nmp->nm_acregmin = NFS_MINATTRTIMO;
nmp->nm_acregmax = NFS_MAXATTRTIMO;
nmp->nm_acdirmin = NFS_MINATTRTIMO;
nmp->nm_acdirmax = NFS_MAXATTRTIMO;
mp->mnt_stat.f_namemax = MAXNAMLEN;
memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntonname, pth, MNAMELEN);
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, hst, MNAMELEN);
memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, hst, MNAMELEN);
bcopy(argp, &mp->mnt_stat.mount_info.nfs_args, sizeof(*argp));
nmp->nm_nam = nam;
nfs_decode_args(nmp, argp, &mp->mnt_stat.mount_info.nfs_args);
nfs_ninit(nmp);
TAILQ_INIT(&nmp->nm_reqsq);
timeout_set_proc(&nmp->nm_rtimeout, nfs_timer, nmp);
/* Set up the sockets and per-host congestion */
nmp->nm_sotype = argp->sotype;
nmp->nm_soproto = argp->proto;
/*
* For Connection based sockets (TCP,...) defer the connect until
* the first request, in case the server is not responding.
*/
if (nmp->nm_sotype == SOCK_DGRAM &&
(error = nfs_connect(nmp, NULL)))
goto bad;
/*
* This is silly, but it has to be set so that vinifod() works.
* We do not want to do an nfs_statfs() here since we can get
* stuck on a dead server and we are holding a lock on the mount
* point.
*/
mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
error = nfs_nget(mp, (nfsfh_t *)argp->fh, argp->fhsize, &np);
if (error)
goto bad;
vp = NFSTOV(np);
error = VOP_GETATTR(vp, &attr, p->p_ucred, p);
if (error) {
vput(vp);
goto bad;
}
/*
* A reference count is needed on the nfsnode representing the
* remote root. If this object is not persistent, then backward
* traversals of the mount point (i.e. "..") will not work if
* the nfsnode gets flushed out of the cache. Ufs does not have
* this problem, because one can identify root inodes by their
* number == ROOTINO (2). So, just unlock, but no rele.
*/
nmp->nm_vnode = vp;
if (vp->v_type == VNON)
vp->v_type = VDIR;
vp->v_flag = VROOT;
VOP_UNLOCK(vp);
*vpp = vp;
return (0);
bad:
nfs_disconnect(nmp);
free(nmp, M_NFSMNT, sizeof(*nmp));
m_freem(nam);
return (error);
}
/* unmount system call */
int
nfs_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct nfsmount *nmp;
struct vnode *vp;
int error, flags = 0;
nmp = VFSTONFS(mp);
error = nfs_root(mp, &vp);
if (error)
return (error);
if ((mntflags & MNT_FORCE) == 0 && vp->v_usecount > 2) {
vput(vp);
return (EBUSY);
}
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
error = vflush(mp, vp, flags);
if (error) {
vput(vp);
return (error);
}
/*
* There are two references count to get rid of here: one
* from mountnfs() and one from nfs_root() above.
*/
vrele(vp);
vput(vp);
vgone(vp);
nfs_disconnect(nmp);
m_freem(nmp->nm_nam);
timeout_del(&nmp->nm_rtimeout);
timeout_set_proc(&nmp->nm_rtimeout, nfs_reaper, nmp);
timeout_add(&nmp->nm_rtimeout, 0);
mp->mnt_data = NULL;
return (0);
}
/*
* Delay nfs mount point free until pending or sleeping timeouts have finished.
*/
void
nfs_reaper(void *arg)
{
struct nfsmount *nmp = arg;
free(nmp, M_NFSMNT, sizeof(*nmp));
}
/*
* Return root of a filesystem
*/
int
nfs_root(struct mount *mp, struct vnode **vpp)
{
struct vnode *vp;
struct nfsmount *nmp;
int error;
nmp = VFSTONFS(mp);
vp = nmp->nm_vnode;
vref(vp);
error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error) {
vrele(vp);
return (error);
}
*vpp = vp;
return (0);
}
/*
* Flush out the buffer cache
*/
int
nfs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, struct proc *p)
{
struct vnode *vp;
int allerror = 0;
int empty, error, s;
/*
* Don't traverse the vnode list if we want to skip all of them.
*/
if (waitfor == MNT_LAZY)
return (allerror);
/*
* Force stale buffer cache information to be flushed.
*/
loop:
TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
/*
* If the vnode that we are about to sync is no longer
* associated with this mount point, start over.
*/
if (vp->v_mount != mp)
goto loop;
if (VOP_ISLOCKED(vp))
continue;
s = splbio();
empty = LIST_EMPTY(&vp->v_dirtyblkhd);
splx(s);
if (empty)
continue;
if (vget(vp, LK_EXCLUSIVE))
goto loop;
error = VOP_FSYNC(vp, cred, waitfor, p);
if (error)
allerror = error;
vput(vp);
}
return (allerror);
}
/*
* NFS flat namespace lookup.
* Currently unsupported.
*/
/* ARGSUSED */
int
nfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
return (EOPNOTSUPP);
}
/*
* Do that sysctl thang...
*/
int
nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
int rv;
/*
* All names at this level are terminal.
*/
if(namelen > 1)
return ENOTDIR; /* overloaded */
switch(name[0]) {
case NFS_NFSSTATS:
if(!oldp) {
*oldlenp = sizeof nfsstats;
return 0;
}
if(*oldlenp < sizeof nfsstats) {
*oldlenp = sizeof nfsstats;
return ENOMEM;
}
rv = copyout(&nfsstats, oldp, sizeof nfsstats);
if(rv) return rv;
if(newp && newlen != sizeof nfsstats)
return EINVAL;
if(newp) {
return copyin(newp, &nfsstats, sizeof nfsstats);
}
return 0;
case NFS_NIOTHREADS:
nfs_getset_niothreads(0);
rv = sysctl_int(oldp, oldlenp, newp, newlen, &nfs_niothreads);
if (newp)
nfs_getset_niothreads(1);
return rv;
default:
return EOPNOTSUPP;
}
}
/*
* At this point, this should never happen
*/
/* ARGSUSED */
int
nfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
return (EINVAL);
}
/*
* Vnode pointer to File handle, should never happen either
*/
/* ARGSUSED */
int
nfs_vptofh(struct vnode *vp, struct fid *fhp)
{
return (EINVAL);
}
/*
* Vfs start routine, a no-op.
*/
/* ARGSUSED */
int
nfs_start(struct mount *mp, int flags, struct proc *p)
{
return (0);
}
/*
* Do operations associated with quotas, not supported
*/
/* ARGSUSED */
int
nfs_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg, struct proc *p)
{
return (EOPNOTSUPP);
}
/*
* check export permission, not supported
*/
/* ARGUSED */
int
nfs_checkexp(struct mount *mp, struct mbuf *nam, int *exflagsp,
struct ucred **credanonp)
{
return (EOPNOTSUPP);
}
72
19
2
2
1
1
2
5
2
3
5
2
3
5
2
3
2
1
8
3
3
3
3
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/* $OpenBSD: uvm_meter.c,v 1.42 2020/12/28 14:01:23 mpi Exp $ */
/* $NetBSD: uvm_meter.c,v 1.21 2001/07/14 06:36:03 matt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vm_meter.c 8.4 (Berkeley) 1/4/94
* from: Id: uvm_meter.c,v 1.1.2.1 1997/08/14 19:10:35 chuck Exp
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/percpu.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <uvm/uvm.h>
#include <uvm/uvm_ddb.h>
#ifdef UVM_SWAP_ENCRYPT
#include <uvm/uvm_swap.h>
#include <uvm/uvm_swap_encrypt.h>
#endif
/*
* The time for a process to be blocked before being very swappable.
* This is a number of seconds which the system takes as being a non-trivial
* amount of real time. You probably shouldn't change this;
* it is used in subtle ways (fractions and multiples of it are, that is, like
* half of a ``long time'', almost a long time, etc.)
* It is related to human patience and other factors which don't really
* change over time.
*/
#define MAXSLP 20
int maxslp = MAXSLP; /* patchable ... */
struct loadavg averunnable;
/*
* constants for averages over 1, 5, and 15 minutes when sampling at
* 5 second intervals.
*/
static fixpt_t cexp[3] = {
0.9200444146293232 * FSCALE, /* exp(-1/12) */
0.9834714538216174 * FSCALE, /* exp(-1/60) */
0.9944598480048967 * FSCALE, /* exp(-1/180) */
};
static void uvm_loadav(struct loadavg *);
void uvm_total(struct vmtotal *);
void uvmexp_read(struct uvmexp *);
/*
* uvm_meter: calculate load average and wake up the swapper (if needed)
*/
void
uvm_meter(void)
{
if ((gettime() % 5) == 0)
uvm_loadav(&averunnable);
if (proc0.p_slptime > (maxslp / 2))
wakeup(&proc0);
}
/*
* uvm_loadav: compute a tenex style load average of a quantity on
* 1, 5, and 15 minute intervals.
*/
static void
uvm_loadav(struct loadavg *avg)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
int i, nrun;
struct proc *p;
int nrun_cpu[MAXCPUS];
nrun = 0;
memset(nrun_cpu, 0, sizeof(nrun_cpu));
LIST_FOREACH(p, &allproc, p_list) {
switch (p->p_stat) {
case SSTOP:
case SSLEEP:
break;
case SRUN:
case SONPROC:
if (p == p->p_cpu->ci_schedstate.spc_idleproc)
continue;
/* FALLTHROUGH */
case SIDL:
nrun++;
if (p->p_cpu)
nrun_cpu[CPU_INFO_UNIT(p->p_cpu)]++;
}
}
for (i = 0; i < 3; i++) {
avg->ldavg[i] = (cexp[i] * avg->ldavg[i] +
nrun * FSCALE * (FSCALE - cexp[i])) >> FSHIFT;
}
CPU_INFO_FOREACH(cii, ci) {
struct schedstate_percpu *spc = &ci->ci_schedstate;
if (nrun_cpu[CPU_INFO_UNIT(ci)] == 0)
continue;
spc->spc_ldavg = (cexp[0] * spc->spc_ldavg +
nrun_cpu[CPU_INFO_UNIT(ci)] * FSCALE *
(FSCALE - cexp[0])) >> FSHIFT;
}
}
char malloc_conf[16];
/*
* uvm_sysctl: sysctl hook into UVM system.
*/
int
uvm_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
struct process *pr = p->p_p;
struct vmtotal vmtotals;
struct uvmexp uexp;
int rv, t;
switch (name[0]) {
case VM_SWAPENCRYPT:
#ifdef UVM_SWAP_ENCRYPT
return (swap_encrypt_ctl(name + 1, namelen - 1, oldp, oldlenp,
newp, newlen, p));
#else
return (EOPNOTSUPP);
#endif
default:
/* all sysctl names at this level are terminal */
if (namelen != 1)
return (ENOTDIR); /* overloaded */
break;
}
switch (name[0]) {
case VM_LOADAVG:
return (sysctl_rdstruct(oldp, oldlenp, newp, &averunnable,
sizeof(averunnable)));
case VM_METER:
uvm_total(&vmtotals);
return (sysctl_rdstruct(oldp, oldlenp, newp, &vmtotals,
sizeof(vmtotals)));
case VM_UVMEXP:
uvmexp_read(&uexp);
return (sysctl_rdstruct(oldp, oldlenp, newp, &uexp,
sizeof(uexp)));
case VM_NKMEMPAGES:
return (sysctl_rdint(oldp, oldlenp, newp, nkmempages));
case VM_PSSTRINGS:
return (sysctl_rdstruct(oldp, oldlenp, newp, &pr->ps_strings,
sizeof(pr->ps_strings)));
case VM_ANONMIN:
t = uvmexp.anonminpct;
rv = sysctl_int(oldp, oldlenp, newp, newlen, &t);
if (rv) {
return rv;
}
if (t + uvmexp.vtextminpct + uvmexp.vnodeminpct > 95 || t < 0) {
return EINVAL;
}
uvmexp.anonminpct = t;
uvmexp.anonmin = t * 256 / 100;
return rv;
case VM_VTEXTMIN:
t = uvmexp.vtextminpct;
rv = sysctl_int(oldp, oldlenp, newp, newlen, &t);
if (rv) {
return rv;
}
if (uvmexp.anonminpct + t + uvmexp.vnodeminpct > 95 || t < 0) {
return EINVAL;
}
uvmexp.vtextminpct = t;
uvmexp.vtextmin = t * 256 / 100;
return rv;
case VM_VNODEMIN:
t = uvmexp.vnodeminpct;
rv = sysctl_int(oldp, oldlenp, newp, newlen, &t);
if (rv) {
return rv;
}
if (uvmexp.anonminpct + uvmexp.vtextminpct + t > 95 || t < 0) {
return EINVAL;
}
uvmexp.vnodeminpct = t;
uvmexp.vnodemin = t * 256 / 100;
return rv;
case VM_MAXSLP:
return (sysctl_rdint(oldp, oldlenp, newp, maxslp));
case VM_USPACE:
return (sysctl_rdint(oldp, oldlenp, newp, USPACE));
case VM_MALLOC_CONF:
return (sysctl_string(oldp, oldlenp, newp, newlen,
malloc_conf, sizeof(malloc_conf)));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
/*
* uvm_total: calculate the current state of the system.
*/
void
uvm_total(struct vmtotal *totalp)
{
struct proc *p;
#if 0
struct vm_map_entry * entry;
struct vm_map *map;
int paging;
#endif
memset(totalp, 0, sizeof *totalp);
/* calculate process statistics */
LIST_FOREACH(p, &allproc, p_list) {
switch (p->p_stat) {
case 0:
continue;
case SSLEEP:
case SSTOP:
totalp->t_sl++;
break;
case SRUN:
case SONPROC:
if (p == p->p_cpu->ci_schedstate.spc_idleproc)
continue;
/* FALLTHROUGH */
case SIDL:
totalp->t_rq++;
if (p->p_stat == SIDL)
continue;
break;
}
/*
* note active objects
*/
#if 0
/*
* XXXCDC: BOGUS! rethink this. in the mean time
* don't do it.
*/
paging = 0;
vm_map_lock(map);
for (map = &p->p_vmspace->vm_map, entry = map->header.next;
entry != &map->header; entry = entry->next) {
if (entry->is_a_map || entry->is_sub_map ||
entry->object.uvm_obj == NULL)
continue;
/* XXX how to do this with uvm */
}
vm_map_unlock(map);
if (paging)
totalp->t_pw++;
#endif
}
/*
* Calculate object memory usage statistics.
*/
totalp->t_free = uvmexp.free;
totalp->t_vm = uvmexp.npages - uvmexp.free + uvmexp.swpginuse;
totalp->t_avm = uvmexp.active + uvmexp.swpginuse; /* XXX */
totalp->t_rm = uvmexp.npages - uvmexp.free;
totalp->t_arm = uvmexp.active;
totalp->t_vmshr = 0; /* XXX */
totalp->t_avmshr = 0; /* XXX */
totalp->t_rmshr = 0; /* XXX */
totalp->t_armshr = 0; /* XXX */
}
void
uvmexp_read(struct uvmexp *uexp)
{
uint64_t counters[exp_ncounters];
memcpy(uexp, &uvmexp, sizeof(*uexp));
counters_read(uvmexp_counters, counters, exp_ncounters);
/* stat counters */
uexp->faults = (int)counters[faults];
uexp->pageins = (int)counters[pageins];
/* fault subcounters */
uexp->fltnoram = (int)counters[flt_noram];
uexp->fltnoanon = (int)counters[flt_noanon];
uexp->fltnoamap = (int)counters[flt_noamap];
uexp->fltpgwait = (int)counters[flt_pgwait];
uexp->fltpgrele = (int)counters[flt_pgrele];
uexp->fltrelck = (int)counters[flt_relck];
uexp->fltrelckok = (int)counters[flt_relckok];
uexp->fltanget = (int)counters[flt_anget];
uexp->fltanretry = (int)counters[flt_anretry];
uexp->fltamcopy = (int)counters[flt_amcopy];
uexp->fltnamap = (int)counters[flt_namap];
uexp->fltnomap = (int)counters[flt_nomap];
uexp->fltlget = (int)counters[flt_lget];
uexp->fltget = (int)counters[flt_get];
uexp->flt_anon = (int)counters[flt_anon];
uexp->flt_acow = (int)counters[flt_acow];
uexp->flt_obj = (int)counters[flt_obj];
uexp->flt_prcopy = (int)counters[flt_prcopy];
uexp->flt_przero = (int)counters[flt_przero];
}
#ifdef DDB
/*
* uvmexp_print: ddb hook to print interesting uvm counters
*/
void
uvmexp_print(int (*pr)(const char *, ...))
{
struct uvmexp uexp;
uvmexp_read(&uexp);
(*pr)("Current UVM status:\n");
(*pr)(" pagesize=%d (0x%x), pagemask=0x%x, pageshift=%d\n",
uexp.pagesize, uexp.pagesize, uexp.pagemask,
uexp.pageshift);
(*pr)(" %d VM pages: %d active, %d inactive, %d wired, %d free (%d zero)\n",
uexp.npages, uexp.active, uexp.inactive, uexp.wired,
uexp.free, uexp.zeropages);
(*pr)(" min %d%% (%d) anon, %d%% (%d) vnode, %d%% (%d) vtext\n",
uexp.anonminpct, uexp.anonmin, uexp.vnodeminpct,
uexp.vnodemin, uexp.vtextminpct, uexp.vtextmin);
(*pr)(" freemin=%d, free-target=%d, inactive-target=%d, "
"wired-max=%d\n", uexp.freemin, uexp.freetarg, uexp.inactarg,
uexp.wiredmax);
(*pr)(" faults=%d, traps=%d, intrs=%d, ctxswitch=%d fpuswitch=%d\n",
uexp.faults, uexp.traps, uexp.intrs, uexp.swtch,
uexp.fpswtch);
(*pr)(" softint=%d, syscalls=%d, kmapent=%d\n",
uexp.softs, uexp.syscalls, uexp.kmapent);
(*pr)(" fault counts:\n");
(*pr)(" noram=%d, noanon=%d, noamap=%d, pgwait=%d, pgrele=%d\n",
uexp.fltnoram, uexp.fltnoanon, uexp.fltnoamap,
uexp.fltpgwait, uexp.fltpgrele);
(*pr)(" ok relocks(total)=%d(%d), anget(retries)=%d(%d), "
"amapcopy=%d\n", uexp.fltrelckok, uexp.fltrelck,
uexp.fltanget, uexp.fltanretry, uexp.fltamcopy);
(*pr)(" neighbor anon/obj pg=%d/%d, gets(lock/unlock)=%d/%d\n",
uexp.fltnamap, uexp.fltnomap, uexp.fltlget, uexp.fltget);
(*pr)(" cases: anon=%d, anoncow=%d, obj=%d, prcopy=%d, przero=%d\n",
uexp.flt_anon, uexp.flt_acow, uexp.flt_obj, uexp.flt_prcopy,
uexp.flt_przero);
(*pr)(" daemon and swap counts:\n");
(*pr)(" woke=%d, revs=%d, scans=%d, obscans=%d, anscans=%d\n",
uexp.pdwoke, uexp.pdrevs, uexp.pdscans, uexp.pdobscan,
uexp.pdanscan);
(*pr)(" busy=%d, freed=%d, reactivate=%d, deactivate=%d\n",
uexp.pdbusy, uexp.pdfreed, uexp.pdreact, uexp.pddeact);
(*pr)(" pageouts=%d, pending=%d, nswget=%d\n", uexp.pdpageouts,
uexp.pdpending, uexp.nswget);
(*pr)(" nswapdev=%d\n",
uexp.nswapdev);
(*pr)(" swpages=%d, swpginuse=%d, swpgonly=%d paging=%d\n",
uexp.swpages, uexp.swpginuse, uexp.swpgonly, uexp.paging);
(*pr)(" kernel pointers:\n");
(*pr)(" objs(kern)=%p\n", uvm.kernel_object);
}
#endif
7
65
65
65
65
65
7
58
7
7
7
58
7
65
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
/* $OpenBSD: sha2.c,v 1.20 2022/08/29 06:08:03 jsg Exp $ */
/*
* FILE: sha2.c
* AUTHOR: Aaron D. Gifford <me@aarongifford.com>
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
*/
#include <sys/time.h>
#include <sys/systm.h>
#include <crypto/sha2.h>
/*
* UNROLLED TRANSFORM LOOP NOTE:
* You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
* loop version for the hash transform rounds (defined using macros
* later in this file). Either define on the command line, for example:
*
* cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
*
* or define below:
*
* #define SHA2_UNROLL_TRANSFORM
*
*/
#ifndef SMALL_KERNEL
#if defined(__amd64__) || defined(__i386__)
#define SHA2_UNROLL_TRANSFORM
#endif
#endif
/*** SHA-256/384/512 Machine Architecture Definitions *****************/
/*
* BYTE_ORDER NOTE:
*
* Please make sure that your system defines BYTE_ORDER. If your
* architecture is little-endian, make sure it also defines
* LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
* equivalent.
*
* If your system does not define the above, then you can do so by
* hand like this:
*
* #define LITTLE_ENDIAN 1234
* #define BIG_ENDIAN 4321
*
* And for little-endian machines, add:
*
* #define BYTE_ORDER LITTLE_ENDIAN
*
* Or for big-endian machines:
*
* #define BYTE_ORDER BIG_ENDIAN
*
* The FreeBSD machine this was written on defines BYTE_ORDER
* appropriately by including <sys/types.h> (which in turn includes
* <machine/endian.h> where the appropriate definitions are actually
* made).
*/
#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
#endif
/*** SHA-256/384/512 Various Length Definitions ***********************/
/* NOTE: Most of these are in sha2.h */
#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
/*
* Macro for incrementally adding the unsigned 64-bit integer n to the
* unsigned 128-bit integer (represented using a two-element array of
* 64-bit words):
*/
#define ADDINC128(w,n) { \
(w)[0] += (u_int64_t)(n); \
if ((w)[0] < (n)) { \
(w)[1]++; \
} \
}
/*** THE SIX LOGICAL FUNCTIONS ****************************************/
/*
* Bit shifting and rotation (used by the six SHA-XYZ logical functions:
*
* NOTE: The naming of R and S appears backwards here (R is a SHIFT and
* S is a ROTATION) because the SHA-256/384/512 description document
* (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
* same "backwards" definition.
*/
/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
#define R(b,x) ((x) >> (b))
/* 32-bit Rotate-right (used in SHA-256): */
#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
/* Four of six logical functions used in SHA-256: */
#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
/* Four of six logical functions used in SHA-384 and SHA-512: */
#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
/*** INTERNAL FUNCTION PROTOTYPES *************************************/
/* NOTE: These should not be accessed directly from outside this
* library -- they are intended for private internal visibility/use
* only.
*/
void SHA512Last(SHA2_CTX *);
void SHA256Transform(u_int32_t *, const u_int8_t *);
void SHA512Transform(u_int64_t *, const u_int8_t *);
/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
/* Hash constant words K for SHA-256: */
static const u_int32_t K256[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
};
/* Initial hash value H for SHA-256: */
static const u_int32_t sha256_initial_hash_value[8] = {
0x6a09e667UL,
0xbb67ae85UL,
0x3c6ef372UL,
0xa54ff53aUL,
0x510e527fUL,
0x9b05688cUL,
0x1f83d9abUL,
0x5be0cd19UL
};
/* Hash constant words K for SHA-384 and SHA-512: */
static const u_int64_t K512[80] = {
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
/* Initial hash value H for SHA-384 */
static const u_int64_t sha384_initial_hash_value[8] = {
0xcbbb9d5dc1059ed8ULL,
0x629a292a367cd507ULL,
0x9159015a3070dd17ULL,
0x152fecd8f70e5939ULL,
0x67332667ffc00b31ULL,
0x8eb44a8768581511ULL,
0xdb0c2e0d64f98fa7ULL,
0x47b5481dbefa4fa4ULL
};
/* Initial hash value H for SHA-512 */
static const u_int64_t sha512_initial_hash_value[8] = {
0x6a09e667f3bcc908ULL,
0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL,
0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL,
0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL,
0x5be0cd19137e2179ULL
};
/*** SHA-256: *********************************************************/
void
SHA256Init(SHA2_CTX *context)
{
memcpy(context->state.st32, sha256_initial_hash_value,
SHA256_DIGEST_LENGTH);
memset(context->buffer, 0, SHA256_BLOCK_LENGTH);
context->bitcount[0] = 0;
}
#ifdef SHA2_UNROLL_TRANSFORM
/* Unrolled SHA-256 round macros: */
#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \
W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) | \
((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24); \
data += 4; \
T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \
(d) += T1; \
(h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \
j++; \
} while(0)
#define ROUND256(a,b,c,d,e,f,g,h) do { \
s0 = W256[(j+1)&0x0f]; \
s0 = sigma0_256(s0); \
s1 = W256[(j+14)&0x0f]; \
s1 = sigma1_256(s1); \
T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
(d) += T1; \
(h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \
j++; \
} while(0)
void
SHA256Transform(u_int32_t *state, const u_int8_t *data)
{
u_int32_t a, b, c, d, e, f, g, h, s0, s1;
u_int32_t T1, W256[16];
int j;
/* Initialize registers with the prev. intermediate value */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
j = 0;
do {
/* Rounds 0 to 15 (unrolled): */
ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
} while (j < 16);
/* Now for the remaining rounds to 64: */
do {
ROUND256(a,b,c,d,e,f,g,h);
ROUND256(h,a,b,c,d,e,f,g);
ROUND256(g,h,a,b,c,d,e,f);
ROUND256(f,g,h,a,b,c,d,e);
ROUND256(e,f,g,h,a,b,c,d);
ROUND256(d,e,f,g,h,a,b,c);
ROUND256(c,d,e,f,g,h,a,b);
ROUND256(b,c,d,e,f,g,h,a);
} while (j < 64);
/* Compute the current intermediate hash value */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = 0;
}
#else /* SHA2_UNROLL_TRANSFORM */
void
SHA256Transform(u_int32_t *state, const u_int8_t *data)
{
u_int32_t a, b, c, d, e, f, g, h, s0, s1;
u_int32_t T1, T2, W256[16];
int j;
/* Initialize registers with the prev. intermediate value */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
j = 0;
do {
W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) |
((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24);
data += 4;
/* Apply the SHA-256 compression function to update a..h */
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
T2 = Sigma0_256(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 16);
do {
/* Part of the message block expansion: */
s0 = W256[(j+1)&0x0f];
s0 = sigma0_256(s0);
s1 = W256[(j+14)&0x0f];
s1 = sigma1_256(s1);
/* Apply the SHA-256 compression function to update a..h */
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
T2 = Sigma0_256(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 64);
/* Compute the current intermediate hash value */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = T2 = 0;
}
#endif /* SHA2_UNROLL_TRANSFORM */
void
SHA256Update(SHA2_CTX *context, const void *dataptr, size_t len)
{
const uint8_t *data = dataptr;
size_t freespace, usedspace;
/* Calling with no data is valid (we do nothing) */
if (len == 0)
return;
usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH;
if (usedspace > 0) {
/* Calculate how much free space is available in the buffer */
freespace = SHA256_BLOCK_LENGTH - usedspace;
if (len >= freespace) {
/* Fill the buffer completely and process it */
memcpy(&context->buffer[usedspace], data, freespace);
context->bitcount[0] += freespace << 3;
len -= freespace;
data += freespace;
SHA256Transform(context->state.st32, context->buffer);
} else {
/* The buffer is not yet full */
memcpy(&context->buffer[usedspace], data, len);
context->bitcount[0] += len << 3;
/* Clean up: */
usedspace = freespace = 0;
return;
}
}
while (len >= SHA256_BLOCK_LENGTH) {
/* Process as many complete blocks as we can */
SHA256Transform(context->state.st32, data);
context->bitcount[0] += SHA256_BLOCK_LENGTH << 3;
len -= SHA256_BLOCK_LENGTH;
data += SHA256_BLOCK_LENGTH;
}
if (len > 0) {
/* There's left-overs, so save 'em */
memcpy(context->buffer, data, len);
context->bitcount[0] += len << 3;
}
/* Clean up: */
usedspace = freespace = 0;
}
void
SHA256Final(u_int8_t digest[], SHA2_CTX *context)
{
unsigned int usedspace;
usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH;
#if BYTE_ORDER == LITTLE_ENDIAN
/* Convert FROM host byte order */
context->bitcount[0] = swap64(context->bitcount[0]);
#endif
if (usedspace > 0) {
/* Begin padding with a 1 bit: */
context->buffer[usedspace++] = 0x80;
if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
/* Set-up for the last transform: */
memset(&context->buffer[usedspace], 0,
SHA256_SHORT_BLOCK_LENGTH - usedspace);
} else {
if (usedspace < SHA256_BLOCK_LENGTH) {
memset(&context->buffer[usedspace], 0,
SHA256_BLOCK_LENGTH - usedspace);
}
/* Do second-to-last transform: */
SHA256Transform(context->state.st32, context->buffer);
/* And set-up for the last transform: */
memset(context->buffer, 0,
SHA256_SHORT_BLOCK_LENGTH);
}
} else {
/* Set-up for the last transform: */
memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH);
/* Begin padding with a 1 bit: */
*context->buffer = 0x80;
}
/* Set the bit count: */
*(u_int64_t *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount[0];
/* Final transform: */
SHA256Transform(context->state.st32, context->buffer);
#if BYTE_ORDER == LITTLE_ENDIAN
{
/* Convert TO host byte order */
int j;
for (j = 0; j < 8; j++) {
context->state.st32[j] = swap32(context->state.st32[j]);
}
}
#endif
memcpy(digest, context->state.st32, SHA256_DIGEST_LENGTH);
/* Clean up state data: */
explicit_bzero(context, sizeof(*context));
usedspace = 0;
}
/*** SHA-512: *********************************************************/
void
SHA512Init(SHA2_CTX *context)
{
memcpy(context->state.st64, sha512_initial_hash_value,
SHA512_DIGEST_LENGTH);
memset(context->buffer, 0, SHA512_BLOCK_LENGTH);
context->bitcount[0] = context->bitcount[1] = 0;
}
#ifdef SHA2_UNROLL_TRANSFORM
/* Unrolled SHA-512 round macros: */
#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \
W512[j] = (u_int64_t)data[7] | ((u_int64_t)data[6] << 8) | \
((u_int64_t)data[5] << 16) | ((u_int64_t)data[4] << 24) | \
((u_int64_t)data[3] << 32) | ((u_int64_t)data[2] << 40) | \
((u_int64_t)data[1] << 48) | ((u_int64_t)data[0] << 56); \
data += 8; \
T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \
(d) += T1; \
(h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \
j++; \
} while(0)
#define ROUND512(a,b,c,d,e,f,g,h) do { \
s0 = W512[(j+1)&0x0f]; \
s0 = sigma0_512(s0); \
s1 = W512[(j+14)&0x0f]; \
s1 = sigma1_512(s1); \
T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \
(W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
(d) += T1; \
(h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \
j++; \
} while(0)
void
SHA512Transform(u_int64_t *state, const u_int8_t *data)
{
u_int64_t a, b, c, d, e, f, g, h, s0, s1;
u_int64_t T1, W512[16];
int j;
/* Initialize registers with the prev. intermediate value */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
j = 0;
do {
ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
} while (j < 16);
/* Now for the remaining rounds up to 79: */
do {
ROUND512(a,b,c,d,e,f,g,h);
ROUND512(h,a,b,c,d,e,f,g);
ROUND512(g,h,a,b,c,d,e,f);
ROUND512(f,g,h,a,b,c,d,e);
ROUND512(e,f,g,h,a,b,c,d);
ROUND512(d,e,f,g,h,a,b,c);
ROUND512(c,d,e,f,g,h,a,b);
ROUND512(b,c,d,e,f,g,h,a);
} while (j < 80);
/* Compute the current intermediate hash value */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = 0;
}
#else /* SHA2_UNROLL_TRANSFORM */
void
SHA512Transform(u_int64_t *state, const u_int8_t *data)
{
u_int64_t a, b, c, d, e, f, g, h, s0, s1;
u_int64_t T1, T2, W512[16];
int j;
/* Initialize registers with the prev. intermediate value */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
j = 0;
do {
W512[j] = (u_int64_t)data[7] | ((u_int64_t)data[6] << 8) |
((u_int64_t)data[5] << 16) | ((u_int64_t)data[4] << 24) |
((u_int64_t)data[3] << 32) | ((u_int64_t)data[2] << 40) |
((u_int64_t)data[1] << 48) | ((u_int64_t)data[0] << 56);
data += 8;
/* Apply the SHA-512 compression function to update a..h */
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
T2 = Sigma0_512(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 16);
do {
/* Part of the message block expansion: */
s0 = W512[(j+1)&0x0f];
s0 = sigma0_512(s0);
s1 = W512[(j+14)&0x0f];
s1 = sigma1_512(s1);
/* Apply the SHA-512 compression function to update a..h */
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
(W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
T2 = Sigma0_512(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
j++;
} while (j < 80);
/* Compute the current intermediate hash value */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
/* Clean up */
a = b = c = d = e = f = g = h = T1 = T2 = 0;
}
#endif /* SHA2_UNROLL_TRANSFORM */
void
SHA512Update(SHA2_CTX *context, const void *dataptr, size_t len)
{
const uint8_t *data = dataptr;
size_t freespace, usedspace;
/* Calling with no data is valid (we do nothing) */
if (len == 0)
return;
usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
if (usedspace > 0) {
/* Calculate how much free space is available in the buffer */
freespace = SHA512_BLOCK_LENGTH - usedspace;
if (len >= freespace) {
/* Fill the buffer completely and process it */
memcpy(&context->buffer[usedspace], data, freespace);
ADDINC128(context->bitcount, freespace << 3);
len -= freespace;
data += freespace;
SHA512Transform(context->state.st64, context->buffer);
} else {
/* The buffer is not yet full */
memcpy(&context->buffer[usedspace], data, len);
ADDINC128(context->bitcount, len << 3);
/* Clean up: */
usedspace = freespace = 0;
return;
}
}
while (len >= SHA512_BLOCK_LENGTH) {
/* Process as many complete blocks as we can */
SHA512Transform(context->state.st64, data);
ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
len -= SHA512_BLOCK_LENGTH;
data += SHA512_BLOCK_LENGTH;
}
if (len > 0) {
/* There's left-overs, so save 'em */
memcpy(context->buffer, data, len);
ADDINC128(context->bitcount, len << 3);
}
/* Clean up: */
usedspace = freespace = 0;
}
void
SHA512Last(SHA2_CTX *context)
{
unsigned int usedspace;
usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
#if BYTE_ORDER == LITTLE_ENDIAN
/* Convert FROM host byte order */
context->bitcount[0] = swap64(context->bitcount[0]);
context->bitcount[1] = swap64(context->bitcount[1]);
#endif
if (usedspace > 0) {
/* Begin padding with a 1 bit: */
context->buffer[usedspace++] = 0x80;
if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
/* Set-up for the last transform: */
memset(&context->buffer[usedspace], 0,
SHA512_SHORT_BLOCK_LENGTH - usedspace);
} else {
if (usedspace < SHA512_BLOCK_LENGTH) {
memset(&context->buffer[usedspace], 0,
SHA512_BLOCK_LENGTH - usedspace);
}
/* Do second-to-last transform: */
SHA512Transform(context->state.st64, context->buffer);
/* And set-up for the last transform: */
memset(context->buffer, 0, SHA512_BLOCK_LENGTH - 2);
}
} else {
/* Prepare for final transform: */
memset(context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH);
/* Begin padding with a 1 bit: */
*context->buffer = 0x80;
}
/* Store the length of input data (in bits): */
*(u_int64_t *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];
*(u_int64_t *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];
/* Final transform: */
SHA512Transform(context->state.st64, context->buffer);
}
void
SHA512Final(u_int8_t digest[], SHA2_CTX *context)
{
SHA512Last(context);
/* Save the hash data for output: */
#if BYTE_ORDER == LITTLE_ENDIAN
{
/* Convert TO host byte order */
int j;
for (j = 0; j < 8; j++) {
context->state.st64[j] = swap64(context->state.st64[j]);
}
}
#endif
memcpy(digest, context->state.st64, SHA512_DIGEST_LENGTH);
/* Zero out state data */
explicit_bzero(context, sizeof(*context));
}
/*** SHA-384: *********************************************************/
void
SHA384Init(SHA2_CTX *context)
{
memcpy(context->state.st64, sha384_initial_hash_value,
SHA512_DIGEST_LENGTH);
memset(context->buffer, 0, SHA384_BLOCK_LENGTH);
context->bitcount[0] = context->bitcount[1] = 0;
}
void
SHA384Update(SHA2_CTX *context, const void *data, size_t len)
{
SHA512Update(context, data, len);
}
void
SHA384Final(u_int8_t digest[], SHA2_CTX *context)
{
SHA512Last(context);
/* Save the hash data for output: */
#if BYTE_ORDER == LITTLE_ENDIAN
{
/* Convert TO host byte order */
int j;
for (j = 0; j < 6; j++) {
context->state.st64[j] = swap64(context->state.st64[j]);
}
}
#endif
memcpy(digest, context->state.st64, SHA384_DIGEST_LENGTH);
/* Zero out state data */
explicit_bzero(context, sizeof(*context));
}
22
1
4
4
1
1
2
7
4
3
2
5
1
3
14
12
4
1
3
5
3
5
19
1
1
1
5
7
2
8
13
18
1
10
2
1
7
9
9
13
5
5
1
1
5
47
3
8
4
4
5
3
4
12
1
11
6
2
8
1
9
5
1
1
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
/* $OpenBSD: sysv_msg.c,v 1.39 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: sysv_msg.c,v 1.19 1996/02/09 19:00:18 christos Exp $ */
/*
* Copyright (c) 2009 Bret S. Lambert <blambert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Implementation of SVID messages
*
* Author: Daniel Boulet
*
* Copyright 1993 Daniel Boulet and RTMX Inc.
*
* This system call was implemented by Daniel Boulet under contract from RTMX.
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msg.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/syscallargs.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
struct que *que_create(key_t, struct ucred *, int);
struct que *que_lookup(int);
struct que *que_key_lookup(key_t);
void que_wakewriters(void);
void que_free(struct que *);
struct msg *msg_create(struct que *);
void msg_free(struct msg *);
void msg_enqueue(struct que *, struct msg *, struct proc *);
void msg_dequeue(struct que *, struct msg *, struct proc *);
struct msg *msg_lookup(struct que *, int);
int msg_copyin(struct msg *, const char *, size_t, struct proc *);
int msg_copyout(struct msg *, char *, size_t *, struct proc *);
struct pool sysvmsgpl;
struct msginfo msginfo;
TAILQ_HEAD(, que) msg_queues;
int num_ques;
int num_msgs;
int sequence;
int maxmsgs;
void
msginit(void)
{
msginfo.msgmax = MSGMAX;
msginfo.msgmni = MSGMNI;
msginfo.msgmnb = MSGMNB;
msginfo.msgtql = MSGTQL;
msginfo.msgssz = MSGSSZ;
msginfo.msgseg = MSGSEG;
pool_init(&sysvmsgpl, sizeof(struct msg), 0, IPL_NONE, PR_WAITOK,
"sysvmsgpl", NULL);
TAILQ_INIT(&msg_queues);
num_ques = 0;
num_msgs = 0;
sequence = 1;
maxmsgs = 0;
}
int
sys_msgctl(struct proc *p, void *v, register_t *retval)
{
struct sys_msgctl_args /* {
syscallarg(int) msqid;
syscallarg(int) cmd;
syscallarg(struct msqid_ds *) buf;
} */ *uap = v;
return (msgctl1(p, SCARG(uap, msqid), SCARG(uap, cmd),
(caddr_t)SCARG(uap, buf), copyin, copyout));
}
int
msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf,
int (*ds_copyin)(const void *, void *, size_t),
int (*ds_copyout)(const void *, void *, size_t))
{
struct msqid_ds tmp;
struct ucred *cred = p->p_ucred;
struct que *que;
int error = 0;
if ((que = que_lookup(msqid)) == NULL)
return (EINVAL);
QREF(que);
switch (cmd) {
case IPC_RMID:
if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
goto out;
TAILQ_REMOVE(&msg_queues, que, que_next);
que->que_flags |= MSGQ_DYING;
/* lose interest in the queue and wait for others to too */
if (--que->que_references > 0) {
wakeup(que);
tsleep_nsec(&que->que_references, PZERO, "msgqrm",
INFSLP);
}
que_free(que);
return (0);
case IPC_SET:
if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
goto out;
if ((error = ds_copyin(buf, &tmp, sizeof(struct msqid_ds))))
goto out;
/* only superuser can bump max bytes in queue */
if (tmp.msg_qbytes > que->msqid_ds.msg_qbytes &&
cred->cr_uid != 0) {
error = EPERM;
goto out;
}
/* restrict max bytes in queue to system limit */
if (tmp.msg_qbytes > msginfo.msgmnb)
tmp.msg_qbytes = msginfo.msgmnb;
/* can't reduce msg_bytes to 0 */
if (tmp.msg_qbytes == 0) {
error = EINVAL; /* non-standard errno! */
goto out;
}
que->msqid_ds.msg_perm.uid = tmp.msg_perm.uid;
que->msqid_ds.msg_perm.gid = tmp.msg_perm.gid;
que->msqid_ds.msg_perm.mode =
(que->msqid_ds.msg_perm.mode & ~0777) |
(tmp.msg_perm.mode & 0777);
que->msqid_ds.msg_qbytes = tmp.msg_qbytes;
que->msqid_ds.msg_ctime = gettime();
break;
case IPC_STAT:
if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
goto out;
error = ds_copyout(&que->msqid_ds, buf,
sizeof(struct msqid_ds));
break;
default:
error = EINVAL;
break;
}
out:
QRELE(que);
return (error);
}
int
sys_msgget(struct proc *p, void *v, register_t *retval)
{
struct sys_msgget_args /* {
syscallarg(key_t) key;
syscallarg(int) msgflg;
} */ *uap = v;
struct ucred *cred = p->p_ucred;
struct que *que;
key_t key = SCARG(uap, key);
int msgflg = SCARG(uap, msgflg);
int error = 0;
again:
if (key != IPC_PRIVATE) {
que = que_key_lookup(key);
if (que) {
if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL))
return (EEXIST);
if ((error = ipcperm(cred, &que->msqid_ds.msg_perm,
msgflg & 0700)))
return (error);
goto found;
}
}
/* don't create a new message queue if the caller doesn't want to */
if (key != IPC_PRIVATE && !(msgflg & IPC_CREAT))
return (ENOENT);
/* enforce limits on the maximum number of message queues */
if (num_ques >= msginfo.msgmni)
return (ENOSPC);
/*
* if que_create returns NULL, it means that a que with an identical
* key was created while this process was sleeping, so start over
*/
if ((que = que_create(key, cred, msgflg & 0777)) == NULL)
goto again;
found:
*retval = IXSEQ_TO_IPCID(que->que_ix, que->msqid_ds.msg_perm);
return (error);
}
#define MSGQ_SPACE(q) ((q)->msqid_ds.msg_qbytes - (q)->msqid_ds.msg_cbytes)
int
sys_msgsnd(struct proc *p, void *v, register_t *retval)
{
struct sys_msgsnd_args /* {
syscallarg(int) msqid;
syscallarg(const void *) msgp;
syscallarg(size_t) msgsz;
syscallarg(int) msgflg;
} */ *uap = v;
struct ucred *cred = p->p_ucred;
struct que *que;
struct msg *msg;
size_t msgsz = SCARG(uap, msgsz);
int error;
if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
return (EINVAL);
if (msgsz > que->msqid_ds.msg_qbytes || msgsz > msginfo.msgmax)
return (EINVAL);
if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_W)))
return (error);
QREF(que);
while (MSGQ_SPACE(que) < msgsz || num_msgs >= msginfo.msgtql) {
if (SCARG(uap, msgflg) & IPC_NOWAIT) {
error = EAGAIN;
goto out;
}
/* notify world that process may wedge here */
if (num_msgs >= msginfo.msgtql)
maxmsgs = 1;
que->que_flags |= MSGQ_WRITERS;
if ((error = tsleep_nsec(que, PZERO|PCATCH, "msgwait", INFSLP)))
goto out;
if (que->que_flags & MSGQ_DYING) {
error = EIDRM;
goto out;
}
}
/* if msg_create returns NULL, the queue is being removed */
if ((msg = msg_create(que)) == NULL) {
error = EIDRM;
goto out;
}
/* msg_copyin frees msg on error */
if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz, p)))
goto out;
msg_enqueue(que, msg, p);
if (que->que_flags & MSGQ_READERS) {
que->que_flags &= ~MSGQ_READERS;
wakeup(que);
}
if (que->que_flags & MSGQ_DYING) {
error = EIDRM;
wakeup(que);
}
out:
QRELE(que);
return (error);
}
int
sys_msgrcv(struct proc *p, void *v, register_t *retval)
{
struct sys_msgrcv_args /* {
syscallarg(int) msqid;
syscallarg(void *) msgp;
syscallarg(size_t) msgsz;
syscallarg(long) msgtyp;
syscallarg(int) msgflg;
} */ *uap = v;
struct ucred *cred = p->p_ucred;
char *msgp = SCARG(uap, msgp);
struct que *que;
struct msg *msg;
size_t msgsz = SCARG(uap, msgsz);
long msgtyp = SCARG(uap, msgtyp);
int error;
if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
return (EINVAL);
if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
return (error);
QREF(que);
/* msg_lookup handles matching; sleeping gets handled here */
while ((msg = msg_lookup(que, msgtyp)) == NULL) {
if (SCARG(uap, msgflg) & IPC_NOWAIT) {
error = ENOMSG;
goto out;
}
que->que_flags |= MSGQ_READERS;
if ((error = tsleep_nsec(que, PZERO|PCATCH, "msgwait", INFSLP)))
goto out;
/* make sure the queue still alive */
if (que->que_flags & MSGQ_DYING) {
error = EIDRM;
goto out;
}
}
/* if msg_copyout fails, keep the message around so it isn't lost */
if ((error = msg_copyout(msg, msgp, &msgsz, p)))
goto out;
msg_dequeue(que, msg, p);
msg_free(msg);
if (que->que_flags & MSGQ_WRITERS) {
que->que_flags &= ~MSGQ_WRITERS;
wakeup(que);
}
/* ensure processes waiting on the global limit don't wedge */
if (maxmsgs) {
maxmsgs = 0;
que_wakewriters();
}
*retval = msgsz;
out:
QRELE(que);
return (error);
}
/*
* que management functions
*/
struct que *
que_create(key_t key, struct ucred *cred, int mode)
{
struct que *que, *que2;
int nextix = 1;
que = malloc(sizeof(*que), M_TEMP, M_WAIT|M_ZERO);
/* if malloc slept, a queue with the same key may have been created */
if (que_key_lookup(key)) {
free(que, M_TEMP, sizeof *que);
return (NULL);
}
/* find next available "index" */
TAILQ_FOREACH(que2, &msg_queues, que_next) {
if (nextix < que2->que_ix)
break;
nextix = que2->que_ix + 1;
}
que->que_ix = nextix;
que->msqid_ds.msg_perm.key = key;
que->msqid_ds.msg_perm.cuid = cred->cr_uid;
que->msqid_ds.msg_perm.uid = cred->cr_uid;
que->msqid_ds.msg_perm.cgid = cred->cr_gid;
que->msqid_ds.msg_perm.gid = cred->cr_gid;
que->msqid_ds.msg_perm.mode = mode & 0777;
que->msqid_ds.msg_perm.seq = ++sequence & 0x7fff;
que->msqid_ds.msg_qbytes = msginfo.msgmnb;
que->msqid_ds.msg_ctime = gettime();
TAILQ_INIT(&que->que_msgs);
/* keep queues in "index" order */
if (que2)
TAILQ_INSERT_BEFORE(que2, que, que_next);
else
TAILQ_INSERT_TAIL(&msg_queues, que, que_next);
num_ques++;
return (que);
}
struct que *
que_lookup(int id)
{
struct que *que;
TAILQ_FOREACH(que, &msg_queues, que_next)
if (que->que_ix == IPCID_TO_IX(id))
break;
/* don't return queues marked for removal */
if (que && que->que_flags & MSGQ_DYING)
return (NULL);
return (que);
}
struct que *
que_key_lookup(key_t key)
{
struct que *que;
if (key == IPC_PRIVATE)
return (NULL);
TAILQ_FOREACH(que, &msg_queues, que_next)
if (que->msqid_ds.msg_perm.key == key)
break;
/* don't return queues marked for removal */
if (que && que->que_flags & MSGQ_DYING)
return (NULL);
return (que);
}
void
que_wakewriters(void)
{
struct que *que;
TAILQ_FOREACH(que, &msg_queues, que_next) {
if (que->que_flags & MSGQ_WRITERS) {
que->que_flags &= ~MSGQ_WRITERS;
wakeup(que);
}
}
}
void
que_free(struct que *que)
{
struct msg *msg;
#ifdef DIAGNOSTIC
if (que->que_references > 0)
panic("freeing message queue with active references");
#endif
while ((msg = TAILQ_FIRST(&que->que_msgs))) {
TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
msg_free(msg);
}
free(que, M_TEMP, sizeof *que);
num_ques--;
}
/*
* msg management functions
*/
struct msg *
msg_create(struct que *que)
{
struct msg *msg;
msg = pool_get(&sysvmsgpl, PR_WAITOK|PR_ZERO);
/* if the queue has died during allocation, return NULL */
if (que->que_flags & MSGQ_DYING) {
pool_put(&sysvmsgpl, msg);
wakeup(que);
return(NULL);
}
num_msgs++;
return (msg);
}
struct msg *
msg_lookup(struct que *que, int msgtyp)
{
struct msg *msg;
/*
* Three different matches are performed based on the value of msgtyp:
* 1) msgtyp > 0 => match exactly
* 2) msgtyp = 0 => match any
* 3) msgtyp < 0 => match any up to absolute value of msgtyp
*/
TAILQ_FOREACH(msg, &que->que_msgs, msg_next)
if (msgtyp == 0 || msgtyp == msg->msg_type ||
(msgtyp < 0 && -msgtyp <= msg->msg_type))
break;
return (msg);
}
void
msg_free(struct msg *msg)
{
m_freem(msg->msg_data);
pool_put(&sysvmsgpl, msg);
num_msgs--;
}
void
msg_enqueue(struct que *que, struct msg *msg, struct proc *p)
{
que->msqid_ds.msg_cbytes += msg->msg_len;
que->msqid_ds.msg_qnum++;
que->msqid_ds.msg_lspid = p->p_p->ps_pid;
que->msqid_ds.msg_stime = gettime();
TAILQ_INSERT_TAIL(&que->que_msgs, msg, msg_next);
}
void
msg_dequeue(struct que *que, struct msg *msg, struct proc *p)
{
que->msqid_ds.msg_cbytes -= msg->msg_len;
que->msqid_ds.msg_qnum--;
que->msqid_ds.msg_lrpid = p->p_p->ps_pid;
que->msqid_ds.msg_rtime = gettime();
TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
}
/*
* The actual I/O routines. A note concerning the layout of SysV msg buffers:
*
* The data to be copied is laid out as a single userspace buffer, with a
* long preceding an opaque buffer of len bytes. The long value ends
* up being the message type, which needs to be copied separately from
* the buffer data, which is stored in in mbufs.
*/
int
msg_copyin(struct msg *msg, const char *ubuf, size_t len, struct proc *p)
{
struct mbuf **mm, *m;
size_t xfer;
int error;
if (msg == NULL)
panic ("msg NULL");
if ((error = copyin(ubuf, &msg->msg_type, sizeof(long)))) {
msg_free(msg);
return (error);
}
if (msg->msg_type < 1) {
msg_free(msg);
return (EINVAL);
}
ubuf += sizeof(long);
msg->msg_len = 0;
mm = &msg->msg_data;
while (msg->msg_len < len) {
m = m_get(M_WAIT, MT_DATA);
if (len >= MINCLSIZE) {
MCLGET(m, M_WAIT);
xfer = min(len, MCLBYTES);
} else {
xfer = min(len, MLEN);
}
m->m_len = xfer;
msg->msg_len += xfer;
*mm = m;
mm = &m->m_next;
}
for (m = msg->msg_data; m; m = m->m_next) {
if ((error = copyin(ubuf, mtod(m, void *), m->m_len))) {
msg_free(msg);
return (error);
}
ubuf += m->m_len;
}
return (0);
}
int
msg_copyout(struct msg *msg, char *ubuf, size_t *len, struct proc *p)
{
struct mbuf *m;
size_t xfer;
int error;
#ifdef DIAGNOSTIC
if (msg->msg_len > MSGMAX)
panic("SysV message longer than MSGMAX");
#endif
/* silently truncate messages too large for user buffer */
xfer = min(*len, msg->msg_len);
if ((error = copyout(&msg->msg_type, ubuf, sizeof(long))))
return (error);
ubuf += sizeof(long);
*len = xfer;
for (m = msg->msg_data; m; m = m->m_next) {
if ((error = copyout(mtod(m, void *), ubuf, m->m_len)))
return (error);
ubuf += m->m_len;
}
return (0);
}
int
sysctl_sysvmsg(int *name, u_int namelen, void *where, size_t *sizep)
{
struct msg_sysctl_info *info;
struct que *que;
size_t infolen, infolen0;
int error;
switch (*name) {
case KERN_SYSVIPC_MSG_INFO:
if (namelen != 1)
return (ENOTDIR);
/*
* The userland ipcs(1) utility expects to be able
* to iterate over at least msginfo.msgmni queues,
* even if those queues don't exist. This is an
* artifact of the previous implementation of
* message queues; for now, emulate this behavior
* until a more thorough fix can be made.
*/
infolen0 = sizeof(msginfo) +
msginfo.msgmni * sizeof(struct msqid_ds);
if (where == NULL) {
*sizep = infolen0;
return (0);
}
/*
* More special-casing due to previous implementation:
* if the caller just wants the msginfo struct, then
* sizep will point to the value sizeof(struct msginfo).
* In that case, only copy out the msginfo struct to
* the caller.
*/
if (*sizep == sizeof(struct msginfo))
return (copyout(&msginfo, where, sizeof(msginfo)));
info = malloc(infolen0, M_TEMP, M_WAIT|M_ZERO);
/* if the malloc slept, this may have changed */
infolen = sizeof(msginfo) +
msginfo.msgmni * sizeof(struct msqid_ds);
if (*sizep < infolen) {
free(info, M_TEMP, infolen0);
return (ENOMEM);
}
memcpy(&info->msginfo, &msginfo, sizeof(struct msginfo));
/*
* Special case #3: the previous array-based implementation
* exported the array indices and userland has come to rely
* upon these indices, so keep behavior consistent.
*/
TAILQ_FOREACH(que, &msg_queues, que_next)
memcpy(&info->msgids[que->que_ix], &que->msqid_ds,
sizeof(struct msqid_ds));
error = copyout(info, where, infolen);
free(info, M_TEMP, infolen0);
return (error);
default:
return (EINVAL);
}
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
/* $OpenBSD: midi.c,v 1.55 2022/07/02 08:50:41 visa Exp $ */
/*
* Copyright (c) 2003, 2004 Alexandre Ratchov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <sys/vnode.h>
#include <sys/signalvar.h>
#include <sys/device.h>
#include <dev/midi_if.h>
#include <dev/audio_if.h>
#include <dev/midivar.h>
#define IPL_SOFTMIDI IPL_SOFTNET
#define DEVNAME(sc) ((sc)->dev.dv_xname)
int midiopen(dev_t, int, int, struct proc *);
int midiclose(dev_t, int, int, struct proc *);
int midiread(dev_t, struct uio *, int);
int midiwrite(dev_t, struct uio *, int);
int midikqfilter(dev_t, struct knote *);
int midiioctl(dev_t, u_long, caddr_t, int, struct proc *);
int midiprobe(struct device *, void *, void *);
void midiattach(struct device *, struct device *, void *);
int mididetach(struct device *, int);
int midiprint(void *, const char *);
void midi_iintr(void *, int);
void midi_ointr(void *);
void midi_timeout(void *);
void midi_out_start(struct midi_softc *);
void midi_out_stop(struct midi_softc *);
void midi_out_do(struct midi_softc *);
void midi_attach(struct midi_softc *, struct device *);
const struct cfattach midi_ca = {
sizeof(struct midi_softc), midiprobe, midiattach, mididetach
};
struct cfdriver midi_cd = {
NULL, "midi", DV_DULL
};
void filt_midiwdetach(struct knote *);
int filt_midiwrite(struct knote *, long);
const struct filterops midiwrite_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_midiwdetach,
.f_event = filt_midiwrite,
};
void filt_midirdetach(struct knote *);
int filt_midiread(struct knote *, long);
const struct filterops midiread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_midirdetach,
.f_event = filt_midiread,
};
void
midi_buf_wakeup(void *addr)
{
struct midi_buffer *buf = addr;
if (buf->blocking) {
wakeup(&buf->blocking);
buf->blocking = 0;
}
/*
* As long as selwakeup() grabs the KERNEL_LOCK() make sure it is
* already held here to avoid lock ordering problems with `audio_lock'
*/
KERNEL_ASSERT_LOCKED();
mtx_enter(&audio_lock);
selwakeup(&buf->sel);
mtx_leave(&audio_lock);
}
void
midi_iintr(void *addr, int data)
{
struct midi_softc *sc = (struct midi_softc *)addr;
struct midi_buffer *mb = &sc->inbuf;
MUTEX_ASSERT_LOCKED(&audio_lock);
if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FREAD))
return;
if (MIDIBUF_ISFULL(mb))
return; /* discard data */
MIDIBUF_WRITE(mb, data);
/*
* As long as selwakeup() needs to be protected by the
* KERNEL_LOCK() we have to delay the wakeup to another
* context to keep the interrupt context KERNEL_LOCK()
* free.
*/
softintr_schedule(sc->inbuf.softintr);
}
int
midiread(dev_t dev, struct uio *uio, int ioflag)
{
struct midi_softc *sc;
struct midi_buffer *mb;
size_t count;
int error;
sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
if (!(sc->flags & FREAD)) {
error = ENXIO;
goto done;
}
mb = &sc->inbuf;
/* if there is no data then sleep (unless IO_NDELAY flag is set) */
error = 0;
mtx_enter(&audio_lock);
while (MIDIBUF_ISEMPTY(mb)) {
if (ioflag & IO_NDELAY) {
error = EWOULDBLOCK;
goto done_mtx;
}
sc->inbuf.blocking = 1;
error = msleep_nsec(&sc->inbuf.blocking, &audio_lock,
PWAIT | PCATCH, "mid_rd", INFSLP);
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error)
goto done_mtx;
}
/* at this stage, there is at least 1 byte */
while (uio->uio_resid > 0 && mb->used > 0) {
count = MIDIBUF_SIZE - mb->start;
if (count > mb->used)
count = mb->used;
if (count > uio->uio_resid)
count = uio->uio_resid;
mtx_leave(&audio_lock);
error = uiomove(mb->data + mb->start, count, uio);
if (error)
goto done;
mtx_enter(&audio_lock);
MIDIBUF_REMOVE(mb, count);
}
done_mtx:
mtx_leave(&audio_lock);
done:
device_unref(&sc->dev);
return error;
}
void
midi_ointr(void *addr)
{
struct midi_softc *sc = (struct midi_softc *)addr;
struct midi_buffer *mb;
MUTEX_ASSERT_LOCKED(&audio_lock);
if (!(sc->dev.dv_flags & DVF_ACTIVE) || !(sc->flags & FWRITE))
return;
mb = &sc->outbuf;
if (mb->used > 0) {
#ifdef MIDI_DEBUG
if (!sc->isbusy) {
printf("midi_ointr: output must be busy\n");
}
#endif
midi_out_do(sc);
} else if (sc->isbusy)
midi_out_stop(sc);
}
void
midi_timeout(void *addr)
{
mtx_enter(&audio_lock);
midi_ointr(addr);
mtx_leave(&audio_lock);
}
void
midi_out_start(struct midi_softc *sc)
{
if (!sc->isbusy) {
sc->isbusy = 1;
midi_out_do(sc);
}
}
void
midi_out_stop(struct midi_softc *sc)
{
sc->isbusy = 0;
/*
* As long as selwakeup() needs to be protected by the
* KERNEL_LOCK() we have to delay the wakeup to another
* context to keep the interrupt context KERNEL_LOCK()
* free.
*/
softintr_schedule(sc->outbuf.softintr);
}
void
midi_out_do(struct midi_softc *sc)
{
struct midi_buffer *mb = &sc->outbuf;
while (mb->used > 0) {
if (!sc->hw_if->output(sc->hw_hdl, mb->data[mb->start]))
break;
MIDIBUF_REMOVE(mb, 1);
if (MIDIBUF_ISEMPTY(mb)) {
if (sc->hw_if->flush != NULL)
sc->hw_if->flush(sc->hw_hdl);
midi_out_stop(sc);
return;
}
}
if (!(sc->props & MIDI_PROP_OUT_INTR)) {
if (MIDIBUF_ISEMPTY(mb))
midi_out_stop(sc);
else
timeout_add(&sc->timeo, 1);
}
}
int
midiwrite(dev_t dev, struct uio *uio, int ioflag)
{
struct midi_softc *sc;
struct midi_buffer *mb;
size_t count;
int error;
sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
if (!(sc->flags & FWRITE)) {
error = ENXIO;
goto done;
}
mb = &sc->outbuf;
/*
* If IO_NDELAY flag is set then check if there is enough room
* in the buffer to store at least one byte. If not then dont
* start the write process.
*/
error = 0;
mtx_enter(&audio_lock);
if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && (uio->uio_resid > 0)) {
error = EWOULDBLOCK;
goto done_mtx;
}
while (uio->uio_resid > 0) {
while (MIDIBUF_ISFULL(mb)) {
if (ioflag & IO_NDELAY) {
/*
* At this stage at least one byte is already
* moved so we do not return EWOULDBLOCK
*/
goto done_mtx;
}
sc->outbuf.blocking = 1;
error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
PWAIT | PCATCH, "mid_wr", INFSLP);
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error)
goto done_mtx;
}
count = MIDIBUF_SIZE - MIDIBUF_END(mb);
if (count > MIDIBUF_AVAIL(mb))
count = MIDIBUF_AVAIL(mb);
if (count > uio->uio_resid)
count = uio->uio_resid;
mtx_leave(&audio_lock);
error = uiomove(mb->data + MIDIBUF_END(mb), count, uio);
if (error)
goto done;
mtx_enter(&audio_lock);
mb->used += count;
midi_out_start(sc);
}
done_mtx:
mtx_leave(&audio_lock);
done:
device_unref(&sc->dev);
return error;
}
int
midikqfilter(dev_t dev, struct knote *kn)
{
struct midi_softc *sc;
struct klist *klist;
int error;
sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
error = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sc->inbuf.sel.si_note;
kn->kn_fop = &midiread_filtops;
break;
case EVFILT_WRITE:
klist = &sc->outbuf.sel.si_note;
kn->kn_fop = &midiwrite_filtops;
break;
default:
error = EINVAL;
goto done;
}
kn->kn_hook = (void *)sc;
mtx_enter(&audio_lock);
klist_insert_locked(klist, kn);
mtx_leave(&audio_lock);
done:
device_unref(&sc->dev);
return error;
}
void
filt_midirdetach(struct knote *kn)
{
struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
mtx_enter(&audio_lock);
klist_remove_locked(&sc->inbuf.sel.si_note, kn);
mtx_leave(&audio_lock);
}
int
filt_midiread(struct knote *kn, long hint)
{
struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
int retval;
if ((hint & NOTE_SUBMIT) == 0)
mtx_enter(&audio_lock);
retval = !MIDIBUF_ISEMPTY(&sc->inbuf);
if ((hint & NOTE_SUBMIT) == 0)
mtx_leave(&audio_lock);
return (retval);
}
void
filt_midiwdetach(struct knote *kn)
{
struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
mtx_enter(&audio_lock);
klist_remove_locked(&sc->outbuf.sel.si_note, kn);
mtx_leave(&audio_lock);
}
int
filt_midiwrite(struct knote *kn, long hint)
{
struct midi_softc *sc = (struct midi_softc *)kn->kn_hook;
int retval;
if ((hint & NOTE_SUBMIT) == 0)
mtx_enter(&audio_lock);
retval = !MIDIBUF_ISFULL(&sc->outbuf);
if ((hint & NOTE_SUBMIT) == 0)
mtx_leave(&audio_lock);
return (retval);
}
int
midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct midi_softc *sc;
int error;
sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
error = 0;
switch(cmd) {
case FIONBIO:
/* All handled in the upper FS layer */
break;
default:
error = ENOTTY;
}
device_unref(&sc->dev);
return error;
}
int
midiopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct midi_softc *sc;
int error;
sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
error = 0;
if (sc->flags) {
error = EBUSY;
goto done;
}
MIDIBUF_INIT(&sc->inbuf);
MIDIBUF_INIT(&sc->outbuf);
sc->isbusy = 0;
sc->inbuf.blocking = sc->outbuf.blocking = 0;
sc->flags = flags;
error = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc);
if (error)
sc->flags = 0;
done:
device_unref(&sc->dev);
return error;
}
int
midiclose(dev_t dev, int fflag, int devtype, struct proc *p)
{
struct midi_softc *sc;
struct midi_buffer *mb;
int error;
sc = (struct midi_softc *)device_lookup(&midi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
/* start draining output buffer */
error = 0;
mb = &sc->outbuf;
mtx_enter(&audio_lock);
if (!MIDIBUF_ISEMPTY(mb))
midi_out_start(sc);
while (sc->isbusy) {
sc->outbuf.blocking = 1;
error = msleep_nsec(&sc->outbuf.blocking, &audio_lock,
PWAIT, "mid_dr", SEC_TO_NSEC(5));
if (!(sc->dev.dv_flags & DVF_ACTIVE))
error = EIO;
if (error)
break;
}
mtx_leave(&audio_lock);
/*
* some hw_if->close() reset immediately the midi uart
* which flushes the internal buffer of the uart device,
* so we may lose some (important) data. To avoid this,
* sleep 20ms (around 64 bytes) to give the time to the
* uart to drain its internal buffers.
*/
tsleep_nsec(&sc->outbuf.blocking, PWAIT, "mid_cl", MSEC_TO_NSEC(20));
sc->hw_if->close(sc->hw_hdl);
sc->flags = 0;
device_unref(&sc->dev);
return 0;
}
int
midiprobe(struct device *parent, void *match, void *aux)
{
struct audio_attach_args *sa = aux;
return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0);
}
void
midiattach(struct device *parent, struct device *self, void *aux)
{
struct midi_info mi;
struct midi_softc *sc = (struct midi_softc *)self;
struct audio_attach_args *sa = (struct audio_attach_args *)aux;
const struct midi_hw_if *hwif = sa->hwif;
void *hdl = sa->hdl;
#ifdef DIAGNOSTIC
if (hwif == 0 ||
hwif->open == 0 ||
hwif->close == 0 ||
hwif->output == 0 ||
hwif->getinfo == 0) {
printf("%s: missing method\n", DEVNAME(sc));
return;
}
#endif
sc->inbuf.softintr = softintr_establish(IPL_SOFTMIDI,
midi_buf_wakeup, &sc->inbuf);
if (sc->inbuf.softintr == NULL) {
printf("%s: can't establish input softintr\n", DEVNAME(sc));
return;
}
sc->outbuf.softintr = softintr_establish(IPL_SOFTMIDI,
midi_buf_wakeup, &sc->outbuf);
if (sc->outbuf.softintr == NULL) {
printf("%s: can't establish output softintr\n", DEVNAME(sc));
softintr_disestablish(sc->inbuf.softintr);
return;
}
sc->hw_if = hwif;
sc->hw_hdl = hdl;
sc->hw_if->getinfo(sc->hw_hdl, &mi);
sc->props = mi.props;
sc->flags = 0;
timeout_set(&sc->timeo, midi_timeout, sc);
printf(": <%s>\n", mi.name);
}
int
mididetach(struct device *self, int flags)
{
struct midi_softc *sc = (struct midi_softc *)self;
int maj, mn;
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++) {
if (cdevsw[maj].d_open == midiopen) {
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
}
}
/*
* The close() method did nothing (device_lookup() returns
* NULL), so quickly halt transfers (normally parent is already
* gone, and code below is no-op), and wake-up user-land blocked
* in read/write/ioctl, which return EIO.
*/
if (sc->flags) {
KERNEL_ASSERT_LOCKED();
if (sc->flags & FREAD) {
wakeup(&sc->inbuf.blocking);
mtx_enter(&audio_lock);
selwakeup(&sc->inbuf.sel);
mtx_leave(&audio_lock);
}
if (sc->flags & FWRITE) {
wakeup(&sc->outbuf.blocking);
mtx_enter(&audio_lock);
selwakeup(&sc->outbuf.sel);
mtx_leave(&audio_lock);
}
sc->hw_if->close(sc->hw_hdl);
sc->flags = 0;
}
klist_invalidate(&sc->inbuf.sel.si_note);
klist_invalidate(&sc->outbuf.sel.si_note);
if (sc->inbuf.softintr)
softintr_disestablish(sc->inbuf.softintr);
if (sc->outbuf.softintr)
softintr_disestablish(sc->outbuf.softintr);
return 0;
}
int
midiprint(void *aux, const char *pnp)
{
if (pnp)
printf("midi at %s", pnp);
return (UNCONF);
}
struct device *
midi_attach_mi(const struct midi_hw_if *hwif, void *hdl, struct device *dev)
{
struct audio_attach_args arg;
arg.type = AUDIODEV_TYPE_MIDI;
arg.hwif = hwif;
arg.hdl = hdl;
return config_found(dev, &arg, midiprint);
}
7
2
2
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
/* $OpenBSD: if_pflow.c,v 1.96 2022/08/12 16:38:50 mvs Exp $ */
/*
* Copyright (c) 2011 Florian Obser <florian@narrans.de>
* Copyright (c) 2011 Sebastian Benoit <benoit-lists@fb12.de>
* Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/in_pcb.h>
#include <net/pfvar.h>
#include <net/if_pflow.h>
#include "bpfilter.h"
#include "pflow.h"
#define PFLOW_MINMTU \
(sizeof(struct pflow_header) + sizeof(struct pflow_flow))
#ifdef PFLOWDEBUG
#define DPRINTF(x) do { printf x ; } while (0)
#else
#define DPRINTF(x)
#endif
SLIST_HEAD(, pflow_softc) pflowif_list;
struct pflowstats pflowstats;
void pflowattach(int);
int pflow_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt);
void pflow_output_process(void *);
int pflow_clone_create(struct if_clone *, int);
int pflow_clone_destroy(struct ifnet *);
int pflow_set(struct pflow_softc *, struct pflowreq *);
void pflow_init_timeouts(struct pflow_softc *);
int pflow_calc_mtu(struct pflow_softc *, int, int);
void pflow_setmtu(struct pflow_softc *, int);
int pflowvalidsockaddr(const struct sockaddr *, int);
int pflowioctl(struct ifnet *, u_long, caddr_t);
struct mbuf *pflow_get_mbuf(struct pflow_softc *, u_int16_t);
void pflow_flush(struct pflow_softc *);
int pflow_sendout_v5(struct pflow_softc *);
int pflow_sendout_ipfix(struct pflow_softc *, sa_family_t);
int pflow_sendout_ipfix_tmpl(struct pflow_softc *);
int pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
void pflow_timeout(void *);
void pflow_timeout6(void *);
void pflow_timeout_tmpl(void *);
void copy_flow_data(struct pflow_flow *, struct pflow_flow *,
struct pf_state *, struct pf_state_key *, int, int);
void copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *,
struct pflow_ipfix_flow4 *, struct pf_state *, struct pf_state_key *,
struct pflow_softc *, int, int);
void copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *,
struct pflow_ipfix_flow6 *, struct pf_state *, struct pf_state_key *,
struct pflow_softc *, int, int);
int pflow_pack_flow(struct pf_state *, struct pf_state_key *,
struct pflow_softc *);
int pflow_pack_flow_ipfix(struct pf_state *, struct pf_state_key *,
struct pflow_softc *);
int export_pflow_if(struct pf_state*, struct pf_state_key *,
struct pflow_softc *);
int copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
int copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow,
struct pflow_softc *sc);
int copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow,
struct pflow_softc *sc);
struct if_clone pflow_cloner =
IF_CLONE_INITIALIZER("pflow", pflow_clone_create,
pflow_clone_destroy);
void
pflowattach(int npflow)
{
SLIST_INIT(&pflowif_list);
if_clone_attach(&pflow_cloner);
}
int
pflow_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
m_freem(m); /* drop packet */
return (EAFNOSUPPORT);
}
void
pflow_output_process(void *arg)
{
struct mbuf_list ml;
struct pflow_softc *sc = arg;
struct mbuf *m;
mq_delist(&sc->sc_outputqueue, &ml);
KERNEL_LOCK();
while ((m = ml_dequeue(&ml)) != NULL) {
pflow_sendout_mbuf(sc, m);
}
KERNEL_UNLOCK();
}
int
pflow_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
struct pflow_softc *pflowif;
pflowif = malloc(sizeof(*pflowif), M_DEVBUF, M_WAITOK|M_ZERO);
MGET(pflowif->send_nam, M_WAIT, MT_SONAME);
pflowif->sc_version = PFLOW_PROTO_DEFAULT;
/* ipfix template init */
bzero(&pflowif->sc_tmpl_ipfix,sizeof(pflowif->sc_tmpl_ipfix));
pflowif->sc_tmpl_ipfix.set_header.set_id =
htons(PFLOW_IPFIX_TMPL_SET_ID);
pflowif->sc_tmpl_ipfix.set_header.set_length =
htons(sizeof(struct pflow_ipfix_tmpl));
/* ipfix IPv4 template */
pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.tmpl_id =
htons(PFLOW_IPFIX_TMPL_IPV4_ID);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.field_count
= htons(PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.field_id =
htons(PFIX_IE_sourceIPv4Address);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.len = htons(4);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.field_id =
htons(PFIX_IE_destinationIPv4Address);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.len = htons(4);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.field_id =
htons(PFIX_IE_ingressInterface);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.len = htons(4);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.field_id =
htons(PFIX_IE_egressInterface);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.len = htons(4);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.field_id =
htons(PFIX_IE_packetDeltaCount);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.field_id =
htons(PFIX_IE_octetDeltaCount);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.field_id =
htons(PFIX_IE_flowStartMilliseconds);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.field_id =
htons(PFIX_IE_flowEndMilliseconds);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.field_id =
htons(PFIX_IE_sourceTransportPort);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.len = htons(2);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.field_id =
htons(PFIX_IE_destinationTransportPort);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.len = htons(2);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.field_id =
htons(PFIX_IE_ipClassOfService);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.len = htons(1);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.field_id =
htons(PFIX_IE_protocolIdentifier);
pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.len = htons(1);
/* ipfix IPv6 template */
pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.tmpl_id =
htons(PFLOW_IPFIX_TMPL_IPV6_ID);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.field_count =
htons(PFLOW_IPFIX_TMPL_IPV6_FIELD_COUNT);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.field_id =
htons(PFIX_IE_sourceIPv6Address);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.len = htons(16);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.field_id =
htons(PFIX_IE_destinationIPv6Address);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.len = htons(16);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.field_id =
htons(PFIX_IE_ingressInterface);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.len = htons(4);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.field_id =
htons(PFIX_IE_egressInterface);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.len = htons(4);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.field_id =
htons(PFIX_IE_packetDeltaCount);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.field_id =
htons(PFIX_IE_octetDeltaCount);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.field_id =
htons(PFIX_IE_flowStartMilliseconds);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.field_id =
htons(PFIX_IE_flowEndMilliseconds);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.len = htons(8);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.field_id =
htons(PFIX_IE_sourceTransportPort);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.len = htons(2);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.field_id =
htons(PFIX_IE_destinationTransportPort);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.len = htons(2);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.field_id =
htons(PFIX_IE_ipClassOfService);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.len = htons(1);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.field_id =
htons(PFIX_IE_protocolIdentifier);
pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.len = htons(1);
ifp = &pflowif->sc_if;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflow%d", unit);
ifp->if_softc = pflowif;
ifp->if_ioctl = pflowioctl;
ifp->if_output = pflow_output;
ifp->if_start = NULL;
ifp->if_xflags = IFXF_CLONED;
ifp->if_type = IFT_PFLOW;
ifp->if_hdrlen = PFLOW_HDRLEN;
ifp->if_flags = IFF_UP;
ifp->if_flags &= ~IFF_RUNNING; /* not running, need receiver */
mq_init(&pflowif->sc_outputqueue, 8192, IPL_SOFTNET);
pflow_setmtu(pflowif, ETHERMTU);
pflow_init_timeouts(pflowif);
if_attach(ifp);
if_alloc_sadl(ifp);
task_set(&pflowif->sc_outputtask, pflow_output_process, pflowif);
/* Insert into list of pflows */
NET_LOCK();
SLIST_INSERT_HEAD(&pflowif_list, pflowif, sc_next);
NET_UNLOCK();
return (0);
}
int
pflow_clone_destroy(struct ifnet *ifp)
{
struct pflow_softc *sc = ifp->if_softc;
int error;
error = 0;
NET_LOCK();
SLIST_REMOVE(&pflowif_list, sc, pflow_softc, sc_next);
NET_UNLOCK();
if (timeout_initialized(&sc->sc_tmo))
timeout_del(&sc->sc_tmo);
if (timeout_initialized(&sc->sc_tmo6))
timeout_del(&sc->sc_tmo6);
if (timeout_initialized(&sc->sc_tmo_tmpl))
timeout_del(&sc->sc_tmo_tmpl);
pflow_flush(sc);
task_del(net_tq(ifp->if_index), &sc->sc_outputtask);
taskq_barrier(net_tq(ifp->if_index));
mq_purge(&sc->sc_outputqueue);
m_freem(sc->send_nam);
if (sc->so != NULL) {
error = soclose(sc->so, MSG_DONTWAIT);
sc->so = NULL;
}
if (sc->sc_flowdst != NULL)
free(sc->sc_flowdst, M_DEVBUF, sc->sc_flowdst->sa_len);
if (sc->sc_flowsrc != NULL)
free(sc->sc_flowsrc, M_DEVBUF, sc->sc_flowsrc->sa_len);
if_detach(ifp);
free(sc, M_DEVBUF, sizeof(*sc));
return (error);
}
int
pflowvalidsockaddr(const struct sockaddr *sa, int ignore_port)
{
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
if (sa == NULL)
return (0);
switch(sa->sa_family) {
case AF_INET:
sin = (struct sockaddr_in*) sa;
return (sin->sin_addr.s_addr != INADDR_ANY &&
(ignore_port || sin->sin_port != 0));
case AF_INET6:
sin6 = (struct sockaddr_in6*) sa;
return (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
(ignore_port || sin6->sin6_port != 0));
default:
return (0);
}
}
int
pflow_set(struct pflow_softc *sc, struct pflowreq *pflowr)
{
struct proc *p = curproc;
struct socket *so;
struct sockaddr *sa;
int error = 0;
if (pflowr->addrmask & PFLOW_MASK_VERSION) {
switch(pflowr->version) {
case PFLOW_PROTO_5:
case PFLOW_PROTO_10:
break;
default:
return(EINVAL);
}
}
pflow_flush(sc);
if (pflowr->addrmask & PFLOW_MASK_DSTIP) {
if (sc->sc_flowdst != NULL &&
sc->sc_flowdst->sa_family != pflowr->flowdst.ss_family) {
free(sc->sc_flowdst, M_DEVBUF, sc->sc_flowdst->sa_len);
sc->sc_flowdst = NULL;
if (sc->so != NULL) {
soclose(sc->so, MSG_DONTWAIT);
sc->so = NULL;
}
}
switch (pflowr->flowdst.ss_family) {
case AF_INET:
if (sc->sc_flowdst == NULL) {
if ((sc->sc_flowdst = malloc(
sizeof(struct sockaddr_in),
M_DEVBUF, M_NOWAIT)) == NULL)
return (ENOMEM);
}
memcpy(sc->sc_flowdst, &pflowr->flowdst,
sizeof(struct sockaddr_in));
sc->sc_flowdst->sa_len = sizeof(struct
sockaddr_in);
break;
case AF_INET6:
if (sc->sc_flowdst == NULL) {
if ((sc->sc_flowdst = malloc(
sizeof(struct sockaddr_in6),
M_DEVBUF, M_NOWAIT)) == NULL)
return (ENOMEM);
}
memcpy(sc->sc_flowdst, &pflowr->flowdst,
sizeof(struct sockaddr_in6));
sc->sc_flowdst->sa_len = sizeof(struct
sockaddr_in6);
break;
default:
break;
}
if (sc->sc_flowdst != NULL) {
sc->send_nam->m_len = sc->sc_flowdst->sa_len;
sa = mtod(sc->send_nam, struct sockaddr *);
memcpy(sa, sc->sc_flowdst, sc->sc_flowdst->sa_len);
}
}
if (pflowr->addrmask & PFLOW_MASK_SRCIP) {
if (sc->sc_flowsrc != NULL)
free(sc->sc_flowsrc, M_DEVBUF, sc->sc_flowsrc->sa_len);
sc->sc_flowsrc = NULL;
if (sc->so != NULL) {
soclose(sc->so, MSG_DONTWAIT);
sc->so = NULL;
}
switch(pflowr->flowsrc.ss_family) {
case AF_INET:
if ((sc->sc_flowsrc = malloc(
sizeof(struct sockaddr_in),
M_DEVBUF, M_NOWAIT)) == NULL)
return (ENOMEM);
memcpy(sc->sc_flowsrc, &pflowr->flowsrc,
sizeof(struct sockaddr_in));
sc->sc_flowsrc->sa_len = sizeof(struct
sockaddr_in);
break;
case AF_INET6:
if ((sc->sc_flowsrc = malloc(
sizeof(struct sockaddr_in6),
M_DEVBUF, M_NOWAIT)) == NULL)
return (ENOMEM);
memcpy(sc->sc_flowsrc, &pflowr->flowsrc,
sizeof(struct sockaddr_in6));
sc->sc_flowsrc->sa_len = sizeof(struct
sockaddr_in6);
break;
default:
break;
}
}
if (sc->so == NULL) {
if (pflowvalidsockaddr(sc->sc_flowdst, 0)) {
error = socreate(sc->sc_flowdst->sa_family,
&so, SOCK_DGRAM, 0);
if (error)
return (error);
if (pflowvalidsockaddr(sc->sc_flowsrc, 1)) {
struct mbuf *m;
MGET(m, M_WAIT, MT_SONAME);
m->m_len = sc->sc_flowsrc->sa_len;
sa = mtod(m, struct sockaddr *);
memcpy(sa, sc->sc_flowsrc,
sc->sc_flowsrc->sa_len);
solock(so);
error = sobind(so, m, p);
sounlock(so);
m_freem(m);
if (error) {
soclose(so, MSG_DONTWAIT);
return (error);
}
}
sc->so = so;
}
} else if (!pflowvalidsockaddr(sc->sc_flowdst, 0)) {
soclose(sc->so, MSG_DONTWAIT);
sc->so = NULL;
}
/* error check is above */
if (pflowr->addrmask & PFLOW_MASK_VERSION)
sc->sc_version = pflowr->version;
pflow_setmtu(sc, ETHERMTU);
pflow_init_timeouts(sc);
return (0);
}
int
pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct proc *p = curproc;
struct pflow_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct pflowreq pflowr;
int error;
switch (cmd) {
case SIOCSIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP) && sc->so != NULL) {
ifp->if_flags |= IFF_RUNNING;
sc->sc_gcounter=pflowstats.pflow_flows;
/* send templates on startup */
if (sc->sc_version == PFLOW_PROTO_10)
pflow_sendout_ipfix_tmpl(sc);
} else
ifp->if_flags &= ~IFF_RUNNING;
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu < PFLOW_MINMTU)
return (EINVAL);
if (ifr->ifr_mtu > MCLBYTES)
ifr->ifr_mtu = MCLBYTES;
if (ifr->ifr_mtu < ifp->if_mtu)
pflow_flush(sc);
pflow_setmtu(sc, ifr->ifr_mtu);
break;
case SIOCGETPFLOW:
bzero(&pflowr, sizeof(pflowr));
if (sc->sc_flowsrc != NULL)
memcpy(&pflowr.flowsrc, sc->sc_flowsrc,
sc->sc_flowsrc->sa_len);
if (sc->sc_flowdst != NULL)
memcpy(&pflowr.flowdst, sc->sc_flowdst,
sc->sc_flowdst->sa_len);
pflowr.version = sc->sc_version;
if ((error = copyout(&pflowr, ifr->ifr_data,
sizeof(pflowr))))
return (error);
break;
case SIOCSETPFLOW:
if ((error = suser(p)) != 0)
return (error);
if ((error = copyin(ifr->ifr_data, &pflowr,
sizeof(pflowr))))
return (error);
/* XXXSMP breaks atomicity */
NET_UNLOCK();
error = pflow_set(sc, &pflowr);
NET_LOCK();
if (error != 0)
return (error);
if ((ifp->if_flags & IFF_UP) && sc->so != NULL) {
ifp->if_flags |= IFF_RUNNING;
sc->sc_gcounter=pflowstats.pflow_flows;
if (sc->sc_version == PFLOW_PROTO_10)
pflow_sendout_ipfix_tmpl(sc);
} else
ifp->if_flags &= ~IFF_RUNNING;
break;
default:
return (ENOTTY);
}
return (0);
}
void
pflow_init_timeouts(struct pflow_softc *sc)
{
switch (sc->sc_version) {
case PFLOW_PROTO_5:
if (timeout_initialized(&sc->sc_tmo6))
timeout_del(&sc->sc_tmo6);
if (timeout_initialized(&sc->sc_tmo_tmpl))
timeout_del(&sc->sc_tmo_tmpl);
if (!timeout_initialized(&sc->sc_tmo))
timeout_set_proc(&sc->sc_tmo, pflow_timeout, sc);
break;
case PFLOW_PROTO_10:
if (!timeout_initialized(&sc->sc_tmo_tmpl))
timeout_set_proc(&sc->sc_tmo_tmpl, pflow_timeout_tmpl,
sc);
if (!timeout_initialized(&sc->sc_tmo))
timeout_set_proc(&sc->sc_tmo, pflow_timeout, sc);
if (!timeout_initialized(&sc->sc_tmo6))
timeout_set_proc(&sc->sc_tmo6, pflow_timeout6, sc);
timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
break;
default: /* NOTREACHED */
break;
}
}
int
pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz)
{
sc->sc_maxcount4 = (mtu - hdrsz -
sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow4);
sc->sc_maxcount6 = (mtu - hdrsz -
sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow6);
if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
sc->sc_maxcount4 = PFLOW_MAXFLOWS;
if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
sc->sc_maxcount6 = PFLOW_MAXFLOWS;
return (hdrsz + sizeof(struct udpiphdr) +
MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_flow4),
sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6)));
}
void
pflow_setmtu(struct pflow_softc *sc, int mtu_req)
{
int mtu;
mtu = mtu_req;
switch (sc->sc_version) {
case PFLOW_PROTO_5:
sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
sizeof(struct udpiphdr)) / sizeof(struct pflow_flow);
if (sc->sc_maxcount > PFLOW_MAXFLOWS)
sc->sc_maxcount = PFLOW_MAXFLOWS;
sc->sc_if.if_mtu = sizeof(struct pflow_header) +
sizeof(struct udpiphdr) +
sc->sc_maxcount * sizeof(struct pflow_flow);
break;
case PFLOW_PROTO_10:
sc->sc_if.if_mtu =
pflow_calc_mtu(sc, mtu, sizeof(struct pflow_v10_header));
break;
default: /* NOTREACHED */
break;
}
}
struct mbuf *
pflow_get_mbuf(struct pflow_softc *sc, u_int16_t set_id)
{
struct pflow_set_header set_hdr;
struct pflow_header h;
struct mbuf *m;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
pflowstats.pflow_onomem++;
return (NULL);
}
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
pflowstats.pflow_onomem++;
return (NULL);
}
m->m_len = m->m_pkthdr.len = 0;
m->m_pkthdr.ph_ifidx = 0;
if (sc == NULL) /* get only a new empty mbuf */
return (m);
switch (sc->sc_version) {
case PFLOW_PROTO_5:
/* populate pflow_header */
h.reserved1 = 0;
h.reserved2 = 0;
h.count = 0;
h.version = htons(PFLOW_PROTO_5);
h.flow_sequence = htonl(sc->sc_gcounter);
h.engine_type = PFLOW_ENGINE_TYPE;
h.engine_id = PFLOW_ENGINE_ID;
m_copyback(m, 0, PFLOW_HDRLEN, &h, M_NOWAIT);
sc->sc_count = 0;
timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
break;
case PFLOW_PROTO_10:
/* populate pflow_set_header */
set_hdr.set_length = 0;
set_hdr.set_id = htons(set_id);
m_copyback(m, 0, PFLOW_SET_HDRLEN, &set_hdr, M_NOWAIT);
break;
default: /* NOTREACHED */
break;
}
return (m);
}
void
copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
struct pf_state *st, struct pf_state_key *sk, int src, int dst)
{
flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
flow1->src_port = flow2->dest_port = sk->port[src];
flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
flow1->dest_port = flow2->src_port = sk->port[dst];
flow1->dest_as = flow2->src_as =
flow1->src_as = flow2->dest_as = 0;
flow1->if_index_in = htons(st->if_index_in);
flow1->if_index_out = htons(st->if_index_out);
flow2->if_index_in = htons(st->if_index_out);
flow2->if_index_out = htons(st->if_index_in);
flow1->dest_mask = flow2->src_mask =
flow1->src_mask = flow2->dest_mask = 0;
flow1->flow_packets = htonl(st->packets[0]);
flow2->flow_packets = htonl(st->packets[1]);
flow1->flow_octets = htonl(st->bytes[0]);
flow2->flow_octets = htonl(st->bytes[1]);
/*
* Pretend the flow was created or expired when the machine came up
* when creation is in the future of the last time a package was seen
* or was created / expired before this machine came up due to pfsync.
*/
flow1->flow_start = flow2->flow_start = st->creation < 0 ||
st->creation > st->expire ? htonl(0) : htonl(st->creation * 1000);
flow1->flow_finish = flow2->flow_finish = st->expire < 0 ? htonl(0) :
htonl(st->expire * 1000);
flow1->tcp_flags = flow2->tcp_flags = 0;
flow1->protocol = flow2->protocol = sk->proto;
flow1->tos = flow2->tos = st->rule.ptr->tos;
}
void
copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *flow1,
struct pflow_ipfix_flow4 *flow2, struct pf_state *st,
struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
{
flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
flow1->src_port = flow2->dest_port = sk->port[src];
flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
flow1->dest_port = flow2->src_port = sk->port[dst];
flow1->if_index_in = htonl(st->if_index_in);
flow1->if_index_out = htonl(st->if_index_out);
flow2->if_index_in = htonl(st->if_index_out);
flow2->if_index_out = htonl(st->if_index_in);
flow1->flow_packets = htobe64(st->packets[0]);
flow2->flow_packets = htobe64(st->packets[1]);
flow1->flow_octets = htobe64(st->bytes[0]);
flow2->flow_octets = htobe64(st->bytes[1]);
/*
* Pretend the flow was created when the machine came up when creation
* is in the future of the last time a package was seen due to pfsync.
*/
if (st->creation > st->expire)
flow1->flow_start = flow2->flow_start = htobe64((gettime() -
getuptime())*1000);
else
flow1->flow_start = flow2->flow_start = htobe64((gettime() -
(getuptime() - st->creation))*1000);
flow1->flow_finish = flow2->flow_finish = htobe64((gettime() -
(getuptime() - st->expire))*1000);
flow1->protocol = flow2->protocol = sk->proto;
flow1->tos = flow2->tos = st->rule.ptr->tos;
}
void
copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
struct pflow_ipfix_flow6 *flow2, struct pf_state *st,
struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
{
bcopy(&sk->addr[src].v6, &flow1->src_ip, sizeof(flow1->src_ip));
bcopy(&sk->addr[src].v6, &flow2->dest_ip, sizeof(flow2->dest_ip));
flow1->src_port = flow2->dest_port = sk->port[src];
bcopy(&sk->addr[dst].v6, &flow1->dest_ip, sizeof(flow1->dest_ip));
bcopy(&sk->addr[dst].v6, &flow2->src_ip, sizeof(flow2->src_ip));
flow1->dest_port = flow2->src_port = sk->port[dst];
flow1->if_index_in = htonl(st->if_index_in);
flow1->if_index_out = htonl(st->if_index_out);
flow2->if_index_in = htonl(st->if_index_out);
flow2->if_index_out = htonl(st->if_index_in);
flow1->flow_packets = htobe64(st->packets[0]);
flow2->flow_packets = htobe64(st->packets[1]);
flow1->flow_octets = htobe64(st->bytes[0]);
flow2->flow_octets = htobe64(st->bytes[1]);
/*
* Pretend the flow was created when the machine came up when creation
* is in the future of the last time a package was seen due to pfsync.
*/
if (st->creation > st->expire)
flow1->flow_start = flow2->flow_start = htobe64((gettime() -
getuptime())*1000);
else
flow1->flow_start = flow2->flow_start = htobe64((gettime() -
(getuptime() - st->creation))*1000);
flow1->flow_finish = flow2->flow_finish = htobe64((gettime() -
(getuptime() - st->expire))*1000);
flow1->protocol = flow2->protocol = sk->proto;
flow1->tos = flow2->tos = st->rule.ptr->tos;
}
int
export_pflow(struct pf_state *st)
{
struct pflow_softc *sc = NULL;
struct pf_state_key *sk;
sk = st->key[st->direction == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
SLIST_FOREACH(sc, &pflowif_list, sc_next) {
switch (sc->sc_version) {
case PFLOW_PROTO_5:
if( sk->af == AF_INET )
export_pflow_if(st, sk, sc);
break;
case PFLOW_PROTO_10:
if( sk->af == AF_INET || sk->af == AF_INET6 )
export_pflow_if(st, sk, sc);
break;
default: /* NOTREACHED */
break;
}
}
return (0);
}
int
export_pflow_if(struct pf_state *st, struct pf_state_key *sk,
struct pflow_softc *sc)
{
struct pf_state pfs_copy;
struct ifnet *ifp = &sc->sc_if;
u_int64_t bytes[2];
int ret = 0;
if (!(ifp->if_flags & IFF_RUNNING))
return (0);
if (sc->sc_version == PFLOW_PROTO_10)
return (pflow_pack_flow_ipfix(st, sk, sc));
/* PFLOW_PROTO_5 */
if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
&& (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
return (pflow_pack_flow(st, sk, sc));
/* flow > PFLOW_MAXBYTES need special handling */
bcopy(st, &pfs_copy, sizeof(pfs_copy));
bytes[0] = pfs_copy.bytes[0];
bytes[1] = pfs_copy.bytes[1];
while (bytes[0] > PFLOW_MAXBYTES) {
pfs_copy.bytes[0] = PFLOW_MAXBYTES;
pfs_copy.bytes[1] = 0;
if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
return (ret);
if ((bytes[0] - PFLOW_MAXBYTES) > 0)
bytes[0] -= PFLOW_MAXBYTES;
}
while (bytes[1] > (u_int64_t)PFLOW_MAXBYTES) {
pfs_copy.bytes[1] = PFLOW_MAXBYTES;
pfs_copy.bytes[0] = 0;
if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
return (ret);
if ((bytes[1] - PFLOW_MAXBYTES) > 0)
bytes[1] -= PFLOW_MAXBYTES;
}
pfs_copy.bytes[0] = bytes[0];
pfs_copy.bytes[1] = bytes[1];
return (pflow_pack_flow(&pfs_copy, sk, sc));
}
int
copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
{
int ret = 0;
if (sc->sc_mbuf == NULL) {
if ((sc->sc_mbuf = pflow_get_mbuf(sc, 0)) == NULL)
return (ENOBUFS);
}
m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
(sc->sc_count * sizeof(struct pflow_flow)),
sizeof(struct pflow_flow), flow, M_NOWAIT);
if (pflowstats.pflow_flows == sc->sc_gcounter)
pflowstats.pflow_flows++;
sc->sc_gcounter++;
sc->sc_count++;
if (sc->sc_count >= sc->sc_maxcount)
ret = pflow_sendout_v5(sc);
return(ret);
}
int
copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc)
{
int ret = 0;
if (sc->sc_mbuf == NULL) {
if ((sc->sc_mbuf =
pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV4_ID)) == NULL) {
return (ENOBUFS);
}
sc->sc_count4 = 0;
timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
}
m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
(sc->sc_count4 * sizeof(struct pflow_ipfix_flow4)),
sizeof(struct pflow_ipfix_flow4), flow, M_NOWAIT);
if (pflowstats.pflow_flows == sc->sc_gcounter)
pflowstats.pflow_flows++;
sc->sc_gcounter++;
sc->sc_count4++;
if (sc->sc_count4 >= sc->sc_maxcount4)
ret = pflow_sendout_ipfix(sc, AF_INET);
return(ret);
}
int
copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
{
int ret = 0;
if (sc->sc_mbuf6 == NULL) {
if ((sc->sc_mbuf6 =
pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV6_ID)) == NULL) {
return (ENOBUFS);
}
sc->sc_count6 = 0;
timeout_add_sec(&sc->sc_tmo6, PFLOW_TIMEOUT);
}
m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
(sc->sc_count6 * sizeof(struct pflow_ipfix_flow6)),
sizeof(struct pflow_ipfix_flow6), flow, M_NOWAIT);
if (pflowstats.pflow_flows == sc->sc_gcounter)
pflowstats.pflow_flows++;
sc->sc_gcounter++;
sc->sc_count6++;
if (sc->sc_count6 >= sc->sc_maxcount6)
ret = pflow_sendout_ipfix(sc, AF_INET6);
return(ret);
}
int
pflow_pack_flow(struct pf_state *st, struct pf_state_key *sk,
struct pflow_softc *sc)
{
struct pflow_flow flow1;
struct pflow_flow flow2;
int ret = 0;
bzero(&flow1, sizeof(flow1));
bzero(&flow2, sizeof(flow2));
if (st->direction == PF_OUT)
copy_flow_data(&flow1, &flow2, st, sk, 1, 0);
else
copy_flow_data(&flow1, &flow2, st, sk, 0, 1);
if (st->bytes[0] != 0) /* first flow from state */
ret = copy_flow_to_m(&flow1, sc);
if (st->bytes[1] != 0) /* second flow from state */
ret = copy_flow_to_m(&flow2, sc);
return (ret);
}
int
pflow_pack_flow_ipfix(struct pf_state *st, struct pf_state_key *sk,
struct pflow_softc *sc)
{
struct pflow_ipfix_flow4 flow4_1, flow4_2;
struct pflow_ipfix_flow6 flow6_1, flow6_2;
int ret = 0;
if (sk->af == AF_INET) {
bzero(&flow4_1, sizeof(flow4_1));
bzero(&flow4_2, sizeof(flow4_2));
if (st->direction == PF_OUT)
copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
1, 0);
else
copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
0, 1);
if (st->bytes[0] != 0) /* first flow from state */
ret = copy_flow_ipfix_4_to_m(&flow4_1, sc);
if (st->bytes[1] != 0) /* second flow from state */
ret = copy_flow_ipfix_4_to_m(&flow4_2, sc);
} else if (sk->af == AF_INET6) {
bzero(&flow6_1, sizeof(flow6_1));
bzero(&flow6_2, sizeof(flow6_2));
if (st->direction == PF_OUT)
copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
1, 0);
else
copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
0, 1);
if (st->bytes[0] != 0) /* first flow from state */
ret = copy_flow_ipfix_6_to_m(&flow6_1, sc);
if (st->bytes[1] != 0) /* second flow from state */
ret = copy_flow_ipfix_6_to_m(&flow6_2, sc);
}
return (ret);
}
void
pflow_timeout(void *v)
{
struct pflow_softc *sc = v;
switch (sc->sc_version) {
case PFLOW_PROTO_5:
pflow_sendout_v5(sc);
break;
case PFLOW_PROTO_10:
pflow_sendout_ipfix(sc, AF_INET);
break;
default: /* NOTREACHED */
break;
}
}
void
pflow_timeout6(void *v)
{
struct pflow_softc *sc = v;
pflow_sendout_ipfix(sc, AF_INET6);
}
void
pflow_timeout_tmpl(void *v)
{
struct pflow_softc *sc = v;
pflow_sendout_ipfix_tmpl(sc);
}
void
pflow_flush(struct pflow_softc *sc)
{
switch (sc->sc_version) {
case PFLOW_PROTO_5:
pflow_sendout_v5(sc);
break;
case PFLOW_PROTO_10:
pflow_sendout_ipfix(sc, AF_INET);
pflow_sendout_ipfix(sc, AF_INET6);
break;
default: /* NOTREACHED */
break;
}
}
int
pflow_sendout_v5(struct pflow_softc *sc)
{
struct mbuf *m = sc->sc_mbuf;
struct pflow_header *h;
struct ifnet *ifp = &sc->sc_if;
struct timespec tv;
timeout_del(&sc->sc_tmo);
if (m == NULL)
return (0);
sc->sc_mbuf = NULL;
if (!(ifp->if_flags & IFF_RUNNING)) {
m_freem(m);
return (0);
}
pflowstats.pflow_packets++;
h = mtod(m, struct pflow_header *);
h->count = htons(sc->sc_count);
/* populate pflow_header */
h->uptime_ms = htonl(getuptime() * 1000);
getnanotime(&tv);
h->time_sec = htonl(tv.tv_sec); /* XXX 2038 */
h->time_nanosec = htonl(tv.tv_nsec);
if (mq_enqueue(&sc->sc_outputqueue, m) == 0)
task_add(net_tq(ifp->if_index), &sc->sc_outputtask);
return (0);
}
int
pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af)
{
struct mbuf *m;
struct pflow_v10_header *h10;
struct pflow_set_header *set_hdr;
struct ifnet *ifp = &sc->sc_if;
u_int32_t count;
int set_length;
switch (af) {
case AF_INET:
m = sc->sc_mbuf;
timeout_del(&sc->sc_tmo);
if (m == NULL)
return (0);
sc->sc_mbuf = NULL;
count = sc->sc_count4;
set_length = sizeof(struct pflow_set_header)
+ sc->sc_count4 * sizeof(struct pflow_ipfix_flow4);
break;
case AF_INET6:
m = sc->sc_mbuf6;
timeout_del(&sc->sc_tmo6);
if (m == NULL)
return (0);
sc->sc_mbuf6 = NULL;
count = sc->sc_count6;
set_length = sizeof(struct pflow_set_header)
+ sc->sc_count6 * sizeof(struct pflow_ipfix_flow6);
break;
default:
unhandled_af(af);
}
if (!(ifp->if_flags & IFF_RUNNING)) {
m_freem(m);
return (0);
}
pflowstats.pflow_packets++;
set_hdr = mtod(m, struct pflow_set_header *);
set_hdr->set_length = htons(set_length);
/* populate pflow_header */
M_PREPEND(m, sizeof(struct pflow_v10_header), M_DONTWAIT);
if (m == NULL) {
pflowstats.pflow_onomem++;
return (ENOBUFS);
}
h10 = mtod(m, struct pflow_v10_header *);
h10->version = htons(PFLOW_PROTO_10);
h10->length = htons(PFLOW_IPFIX_HDRLEN + set_length);
h10->time_sec = htonl(gettime()); /* XXX 2038 */
h10->flow_sequence = htonl(sc->sc_sequence);
sc->sc_sequence += count;
h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
if (mq_enqueue(&sc->sc_outputqueue, m) == 0)
task_add(net_tq(ifp->if_index), &sc->sc_outputtask);
return (0);
}
int
pflow_sendout_ipfix_tmpl(struct pflow_softc *sc)
{
struct mbuf *m;
struct pflow_v10_header *h10;
struct ifnet *ifp = &sc->sc_if;
timeout_del(&sc->sc_tmo_tmpl);
if (!(ifp->if_flags & IFF_RUNNING)) {
return (0);
}
m = pflow_get_mbuf(NULL, 0);
if (m == NULL)
return (0);
if (m_copyback(m, 0, sizeof(struct pflow_ipfix_tmpl),
&sc->sc_tmpl_ipfix, M_NOWAIT)) {
m_freem(m);
return (0);
}
pflowstats.pflow_packets++;
/* populate pflow_header */
M_PREPEND(m, sizeof(struct pflow_v10_header), M_DONTWAIT);
if (m == NULL) {
pflowstats.pflow_onomem++;
return (ENOBUFS);
}
h10 = mtod(m, struct pflow_v10_header *);
h10->version = htons(PFLOW_PROTO_10);
h10->length = htons(PFLOW_IPFIX_HDRLEN + sizeof(struct
pflow_ipfix_tmpl));
h10->time_sec = htonl(gettime()); /* XXX 2038 */
h10->flow_sequence = htonl(sc->sc_sequence);
h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
if (mq_enqueue(&sc->sc_outputqueue, m) == 0)
task_add(net_tq(ifp->if_index), &sc->sc_outputtask);
return (0);
}
int
pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
{
sc->sc_if.if_opackets++;
sc->sc_if.if_obytes += m->m_pkthdr.len;
if (sc->so == NULL) {
m_freem(m);
return (EINVAL);
}
return (sosend(sc->so, sc->send_nam, NULL, m, NULL, 0));
}
int
pflow_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case NET_PFLOW_STATS:
if (newp != NULL)
return (EPERM);
return (sysctl_struct(oldp, oldlenp, newp, newlen,
&pflowstats, sizeof(pflowstats)));
default:
return (EOPNOTSUPP);
}
return (0);
}
23
53
175
177
178
175
1
1
173
1
173
2
8
1
163
1
1
155
5
178
16
22
70
49
51
2
26
5
14
14
74
51
15
10
17
5
3
14
20
2
1
20
20
2
61
58
3
58
18
23
3
2
1
2
19
20
3
4
2
15
19
5
3
13
3
6
3
6
2
6
2
5
2
2
2
76
37
28
1
4
29
20
3
3
10
2
16
3
2
3
3
27
72
6
29
10
6
6
3
1
1
2
2
4
2
2
1
1
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
/* $OpenBSD: ip_input.c,v 1.381 2022/08/29 14:43:56 bluhm Exp $ */
/* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94
*/
#include "pf.h"
#include "carp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/mutex.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
#include <sys/task.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <net/if_types.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
#ifdef MROUTING
#include <netinet/ip_mroute.h>
#endif
#ifdef IPSEC
#include <netinet/ip_ipsp.h>
#endif /* IPSEC */
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
/* values controllable via sysctl */
int ipforwarding = 0;
int ipmforwarding = 0;
int ipmultipath = 0;
int ipsendredirects = 1;
int ip_dosourceroute = 0;
int ip_defttl = IPDEFTTL;
int ip_mtudisc = 1;
int ip_mtudisc_timeout = IPMTUDISCTIMEOUT;
int ip_directedbcast = 0;
/* Protects `ipq' and `ip_frags'. */
struct mutex ipq_mutex = MUTEX_INITIALIZER(IPL_SOFTNET);
/* IP reassembly queue */
LIST_HEAD(, ipq) ipq;
/* Keep track of memory used for reassembly */
int ip_maxqueue = 300;
int ip_frags = 0;
const struct sysctl_bounded_args ipctl_vars[] = {
#ifdef MROUTING
{ IPCTL_MRTPROTO, &ip_mrtproto, SYSCTL_INT_READONLY },
#endif
{ IPCTL_FORWARDING, &ipforwarding, 0, 2 },
{ IPCTL_SENDREDIRECTS, &ipsendredirects, 0, 1 },
{ IPCTL_DEFTTL, &ip_defttl, 0, 255 },
{ IPCTL_DIRECTEDBCAST, &ip_directedbcast, 0, 1 },
{ IPCTL_IPPORT_FIRSTAUTO, &ipport_firstauto, 0, 65535 },
{ IPCTL_IPPORT_LASTAUTO, &ipport_lastauto, 0, 65535 },
{ IPCTL_IPPORT_HIFIRSTAUTO, &ipport_hifirstauto, 0, 65535 },
{ IPCTL_IPPORT_HILASTAUTO, &ipport_hilastauto, 0, 65535 },
{ IPCTL_IPPORT_MAXQUEUE, &ip_maxqueue, 0, 10000 },
{ IPCTL_MFORWARDING, &ipmforwarding, 0, 1 },
{ IPCTL_MULTIPATH, &ipmultipath, 0, 1 },
{ IPCTL_ARPTIMEOUT, &arpt_keep, 0, INT_MAX },
{ IPCTL_ARPDOWN, &arpt_down, 0, INT_MAX },
};
struct niqueue ipintrq = NIQUEUE_INITIALIZER(IPQ_MAXLEN, NETISR_IP);
struct pool ipqent_pool;
struct pool ipq_pool;
struct cpumem *ipcounters;
int ip_sysctl_ipstat(void *, size_t *, void *);
static struct mbuf_queue ipsend_mq;
static struct mbuf_queue ipsendraw_mq;
extern struct niqueue arpinq;
int ip_ours(struct mbuf **, int *, int, int);
int ip_dooptions(struct mbuf *, struct ifnet *);
int in_ouraddr(struct mbuf *, struct ifnet *, struct rtentry **);
int ip_fragcheck(struct mbuf **, int *);
struct mbuf * ip_reass(struct ipqent *, struct ipq *);
void ip_freef(struct ipq *);
void ip_flush(void);
static void ip_send_dispatch(void *);
static void ip_sendraw_dispatch(void *);
static struct task ipsend_task = TASK_INITIALIZER(ip_send_dispatch, &ipsend_mq);
static struct task ipsendraw_task =
TASK_INITIALIZER(ip_sendraw_dispatch, &ipsendraw_mq);
/*
* Used to save the IP options in case a protocol wants to respond
* to an incoming packet over the same route if the packet got here
* using IP source routing. This allows connection establishment and
* maintenance when the remote end is on a network that is not known
* to us.
*/
struct ip_srcrt {
int isr_nhops; /* number of hops */
struct in_addr isr_dst; /* final destination */
char isr_nop; /* one NOP to align */
char isr_hdr[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN & OFFSET */
struct in_addr isr_routes[MAX_IPOPTLEN/sizeof(struct in_addr)];
};
void save_rte(struct mbuf *, u_char *, struct in_addr);
/*
* IP initialization: fill in IP protocol switch table.
* All protocols not implemented in kernel go to raw IP protocol handler.
*/
void
ip_init(void)
{
const struct protosw *pr;
int i;
const u_int16_t defbaddynamicports_tcp[] = DEFBADDYNAMICPORTS_TCP;
const u_int16_t defbaddynamicports_udp[] = DEFBADDYNAMICPORTS_UDP;
const u_int16_t defrootonlyports_tcp[] = DEFROOTONLYPORTS_TCP;
const u_int16_t defrootonlyports_udp[] = DEFROOTONLYPORTS_UDP;
ipcounters = counters_alloc(ips_ncounters);
pool_init(&ipqent_pool, sizeof(struct ipqent), 0,
IPL_SOFTNET, 0, "ipqe", NULL);
pool_init(&ipq_pool, sizeof(struct ipq), 0,
IPL_SOFTNET, 0, "ipq", NULL);
pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
if (pr == NULL)
panic("ip_init");
for (i = 0; i < IPPROTO_MAX; i++)
ip_protox[i] = pr - inetsw;
for (pr = inetdomain.dom_protosw;
pr < inetdomain.dom_protoswNPROTOSW; pr++)
if (pr->pr_domain->dom_family == PF_INET &&
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW &&
pr->pr_protocol < IPPROTO_MAX)
ip_protox[pr->pr_protocol] = pr - inetsw;
LIST_INIT(&ipq);
/* Fill in list of ports not to allocate dynamically. */
memset(&baddynamicports, 0, sizeof(baddynamicports));
for (i = 0; defbaddynamicports_tcp[i] != 0; i++)
DP_SET(baddynamicports.tcp, defbaddynamicports_tcp[i]);
for (i = 0; defbaddynamicports_udp[i] != 0; i++)
DP_SET(baddynamicports.udp, defbaddynamicports_udp[i]);
/* Fill in list of ports only root can bind to. */
memset(&rootonlyports, 0, sizeof(rootonlyports));
for (i = 0; defrootonlyports_tcp[i] != 0; i++)
DP_SET(rootonlyports.tcp, defrootonlyports_tcp[i]);
for (i = 0; defrootonlyports_udp[i] != 0; i++)
DP_SET(rootonlyports.udp, defrootonlyports_udp[i]);
mq_init(&ipsend_mq, 64, IPL_SOFTNET);
mq_init(&ipsendraw_mq, 64, IPL_SOFTNET);
arpinit();
#ifdef IPSEC
ipsec_init();
#endif
#ifdef MROUTING
rt_timer_queue_init(&ip_mrouterq, MCAST_EXPIRE_FREQUENCY,
&mfc_expire_route);
#endif
}
/*
* Enqueue packet for local delivery. Queuing is used as a boundary
* between the network layer (input/forward path) running with
* NET_LOCK_SHARED() and the transport layer needing it exclusively.
*/
int
ip_ours(struct mbuf **mp, int *offp, int nxt, int af)
{
nxt = ip_fragcheck(mp, offp);
if (nxt == IPPROTO_DONE)
return IPPROTO_DONE;
/* We are already in a IPv4/IPv6 local deliver loop. */
if (af != AF_UNSPEC)
return nxt;
niq_enqueue(&ipintrq, *mp);
*mp = NULL;
return IPPROTO_DONE;
}
/*
* Dequeue and process locally delivered packets.
* This is called with exclusive NET_LOCK().
*/
void
ipintr(void)
{
struct mbuf *m;
while ((m = niq_dequeue(&ipintrq)) != NULL) {
struct ip *ip;
int off, nxt;
#ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0)
panic("ipintr no HDR");
#endif
ip = mtod(m, struct ip *);
off = ip->ip_hl << 2;
nxt = ip->ip_p;
nxt = ip_deliver(&m, &off, nxt, AF_INET);
KASSERT(nxt == IPPROTO_DONE);
}
}
/*
* IPv4 input routine.
*
* Checksum and byte swap header. Process options. Forward or deliver.
*/
void
ipv4_input(struct ifnet *ifp, struct mbuf *m)
{
int off, nxt;
off = 0;
nxt = ip_input_if(&m, &off, IPPROTO_IPV4, AF_UNSPEC, ifp);
KASSERT(nxt == IPPROTO_DONE);
}
struct mbuf *
ipv4_check(struct ifnet *ifp, struct mbuf *m)
{
struct ip *ip;
int hlen, len;
if (m->m_len < sizeof(*ip)) {
m = m_pullup(m, sizeof(*ip));
if (m == NULL) {
ipstat_inc(ips_toosmall);
return (NULL);
}
}
ip = mtod(m, struct ip *);
if (ip->ip_v != IPVERSION) {
ipstat_inc(ips_badvers);
goto bad;
}
hlen = ip->ip_hl << 2;
if (hlen < sizeof(*ip)) { /* minimum header length */
ipstat_inc(ips_badhlen);
goto bad;
}
if (hlen > m->m_len) {
m = m_pullup(m, hlen);
if (m == NULL) {
ipstat_inc(ips_badhlen);
return (NULL);
}
ip = mtod(m, struct ip *);
}
/* 127/8 must not appear on wire - RFC1122 */
if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
(ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
ipstat_inc(ips_badaddr);
goto bad;
}
}
if (!ISSET(m->m_pkthdr.csum_flags, M_IPV4_CSUM_IN_OK)) {
if (ISSET(m->m_pkthdr.csum_flags, M_IPV4_CSUM_IN_BAD)) {
ipstat_inc(ips_badsum);
goto bad;
}
ipstat_inc(ips_inswcsum);
if (in_cksum(m, hlen) != 0) {
ipstat_inc(ips_badsum);
goto bad;
}
SET(m->m_pkthdr.csum_flags, M_IPV4_CSUM_IN_OK);
}
/* Retrieve the packet length. */
len = ntohs(ip->ip_len);
/*
* Convert fields to host representation.
*/
if (len < hlen) {
ipstat_inc(ips_badlen);
goto bad;
}
/*
* Check that the amount of data in the buffers
* is at least as much as the IP header would have us expect.
* Trim mbufs if longer than we expect.
* Drop packet if shorter than we expect.
*/
if (m->m_pkthdr.len < len) {
ipstat_inc(ips_tooshort);
goto bad;
}
if (m->m_pkthdr.len > len) {
if (m->m_len == m->m_pkthdr.len) {
m->m_len = len;
m->m_pkthdr.len = len;
} else
m_adj(m, len - m->m_pkthdr.len);
}
return (m);
bad:
m_freem(m);
return (NULL);
}
int
ip_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp)
{
struct mbuf *m;
struct rtentry *rt = NULL;
struct ip *ip;
int hlen;
in_addr_t pfrdr = 0;
KASSERT(*offp == 0);
ipstat_inc(ips_total);
m = *mp = ipv4_check(ifp, *mp);
if (m == NULL)
goto bad;
ip = mtod(m, struct ip *);
#if NCARP > 0
if (carp_lsdrop(ifp, m, AF_INET, &ip->ip_src.s_addr,
&ip->ip_dst.s_addr, (ip->ip_p == IPPROTO_ICMP ? 0 : 1)))
goto bad;
#endif
#if NPF > 0
/*
* Packet filter
*/
pfrdr = ip->ip_dst.s_addr;
if (pf_test(AF_INET, PF_IN, ifp, mp) != PF_PASS)
goto bad;
m = *mp;
if (m == NULL)
goto bad;
ip = mtod(m, struct ip *);
pfrdr = (pfrdr != ip->ip_dst.s_addr);
#endif
hlen = ip->ip_hl << 2;
/*
* Process options and, if not destined for us,
* ship it on. ip_dooptions returns 1 when an
* error was detected (causing an icmp message
* to be sent and the original packet to be freed).
*/
if (hlen > sizeof (struct ip) && ip_dooptions(m, ifp)) {
m = *mp = NULL;
goto bad;
}
if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
ip->ip_dst.s_addr == INADDR_ANY) {
nxt = ip_ours(mp, offp, nxt, af);
goto out;
}
switch(in_ouraddr(m, ifp, &rt)) {
case 2:
goto bad;
case 1:
nxt = ip_ours(mp, offp, nxt, af);
goto out;
}
if (IN_MULTICAST(ip->ip_dst.s_addr)) {
/*
* Make sure M_MCAST is set. It should theoretically
* already be there, but let's play safe because upper
* layers check for this flag.
*/
m->m_flags |= M_MCAST;
#ifdef MROUTING
if (ipmforwarding && ip_mrouter[ifp->if_rdomain]) {
int error;
if (m->m_flags & M_EXT) {
if ((m = *mp = m_pullup(m, hlen)) == NULL) {
ipstat_inc(ips_toosmall);
goto bad;
}
ip = mtod(m, struct ip *);
}
/*
* If we are acting as a multicast router, all
* incoming multicast packets are passed to the
* kernel-level multicast forwarding function.
* The packet is returned (relatively) intact; if
* ip_mforward() returns a non-zero value, the packet
* must be discarded, else it may be accepted below.
*
* (The IP ident field is put in the same byte order
* as expected when ip_mforward() is called from
* ip_output().)
*/
KERNEL_LOCK();
error = ip_mforward(m, ifp);
KERNEL_UNLOCK();
if (error) {
ipstat_inc(ips_cantforward);
goto bad;
}
/*
* The process-level routing daemon needs to receive
* all multicast IGMP packets, whether or not this
* host belongs to their destination groups.
*/
if (ip->ip_p == IPPROTO_IGMP) {
nxt = ip_ours(mp, offp, nxt, af);
goto out;
}
ipstat_inc(ips_forward);
}
#endif
/*
* See if we belong to the destination multicast group on the
* arrival interface.
*/
if (!in_hasmulti(&ip->ip_dst, ifp)) {
ipstat_inc(ips_notmember);
if (!IN_LOCAL_GROUP(ip->ip_dst.s_addr))
ipstat_inc(ips_cantforward);
goto bad;
}
nxt = ip_ours(mp, offp, nxt, af);
goto out;
}
#if NCARP > 0
if (ip->ip_p == IPPROTO_ICMP &&
carp_lsdrop(ifp, m, AF_INET, &ip->ip_src.s_addr,
&ip->ip_dst.s_addr, 1))
goto bad;
#endif
/*
* Not for us; forward if possible and desirable.
*/
if (ipforwarding == 0) {
ipstat_inc(ips_cantforward);
goto bad;
}
#ifdef IPSEC
if (ipsec_in_use) {
int rv;
rv = ipsec_forward_check(m, hlen, AF_INET);
if (rv != 0) {
ipstat_inc(ips_cantforward);
goto bad;
}
/*
* Fall through, forward packet. Outbound IPsec policy
* checking will occur in ip_output().
*/
}
#endif /* IPSEC */
ip_forward(m, ifp, rt, pfrdr);
*mp = NULL;
return IPPROTO_DONE;
bad:
nxt = IPPROTO_DONE;
m_freemp(mp);
out:
rtfree(rt);
return nxt;
}
int
ip_fragcheck(struct mbuf **mp, int *offp)
{
struct ip *ip;
struct ipq *fp;
struct ipqent *ipqe;
int hlen;
uint16_t mff;
ip = mtod(*mp, struct ip *);
hlen = ip->ip_hl << 2;
/*
* If offset or more fragments are set, must reassemble.
* Otherwise, nothing need be done.
* (We could look in the reassembly queue to see
* if the packet was previously fragmented,
* but it's not worth the time; just let them time out.)
*/
if (ISSET(ip->ip_off, htons(IP_OFFMASK | IP_MF))) {
if ((*mp)->m_flags & M_EXT) { /* XXX */
if ((*mp = m_pullup(*mp, hlen)) == NULL) {
ipstat_inc(ips_toosmall);
return IPPROTO_DONE;
}
ip = mtod(*mp, struct ip *);
}
/*
* Adjust ip_len to not reflect header,
* set ipqe_mff if more fragments are expected,
* convert offset of this to bytes.
*/
ip->ip_len = htons(ntohs(ip->ip_len) - hlen);
mff = ISSET(ip->ip_off, htons(IP_MF));
if (mff) {
/*
* Make sure that fragments have a data length
* that's a non-zero multiple of 8 bytes.
*/
if (ntohs(ip->ip_len) == 0 ||
(ntohs(ip->ip_len) & 0x7) != 0) {
ipstat_inc(ips_badfrags);
m_freemp(mp);
return IPPROTO_DONE;
}
}
ip->ip_off = htons(ntohs(ip->ip_off) << 3);
mtx_enter(&ipq_mutex);
/*
* Look for queue of fragments
* of this datagram.
*/
LIST_FOREACH(fp, &ipq, ipq_q) {
if (ip->ip_id == fp->ipq_id &&
ip->ip_src.s_addr == fp->ipq_src.s_addr &&
ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
ip->ip_p == fp->ipq_p)
break;
}
/*
* If datagram marked as having more fragments
* or if this is not the first fragment,
* attempt reassembly; if it succeeds, proceed.
*/
if (mff || ip->ip_off) {
ipstat_inc(ips_fragments);
if (ip_frags + 1 > ip_maxqueue) {
ip_flush();
ipstat_inc(ips_rcvmemdrop);
goto bad;
}
ipqe = pool_get(&ipqent_pool, PR_NOWAIT);
if (ipqe == NULL) {
ipstat_inc(ips_rcvmemdrop);
goto bad;
}
ip_frags++;
ipqe->ipqe_mff = mff;
ipqe->ipqe_m = *mp;
ipqe->ipqe_ip = ip;
*mp = ip_reass(ipqe, fp);
if (*mp == NULL)
goto bad;
ipstat_inc(ips_reassembled);
ip = mtod(*mp, struct ip *);
hlen = ip->ip_hl << 2;
ip->ip_len = htons(ntohs(ip->ip_len) + hlen);
} else {
if (fp != NULL)
ip_freef(fp);
}
mtx_leave(&ipq_mutex);
}
*offp = hlen;
return ip->ip_p;
bad:
mtx_leave(&ipq_mutex);
m_freemp(mp);
return IPPROTO_DONE;
}
#ifndef INET6
#define IPSTAT_INC(name) ipstat_inc(ips_##name)
#else
#define IPSTAT_INC(name) (af == AF_INET ? \
ipstat_inc(ips_##name) : ip6stat_inc(ip6s_##name))
#endif
int
ip_deliver(struct mbuf **mp, int *offp, int nxt, int af)
{
const struct protosw *psw;
int naf = af;
#ifdef INET6
int nest = 0;
#endif /* INET6 */
NET_ASSERT_LOCKED_EXCLUSIVE();
/*
* Tell launch routine the next header
*/
IPSTAT_INC(delivered);
while (nxt != IPPROTO_DONE) {
#ifdef INET6
if (af == AF_INET6 &&
ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) {
ip6stat_inc(ip6s_toomanyhdr);
goto bad;
}
#endif /* INET6 */
/*
* protection against faulty packet - there should be
* more sanity checks in header chain processing.
*/
if ((*mp)->m_pkthdr.len < *offp) {
IPSTAT_INC(tooshort);
goto bad;
}
#ifdef IPSEC
if (ipsec_in_use) {
if (ipsec_local_check(*mp, *offp, nxt, af) != 0) {
IPSTAT_INC(cantforward);
goto bad;
}
}
/* Otherwise, just fall through and deliver the packet */
#endif /* IPSEC */
switch (nxt) {
case IPPROTO_IPV4:
naf = AF_INET;
ipstat_inc(ips_delivered);
break;
#ifdef INET6
case IPPROTO_IPV6:
naf = AF_INET6;
ip6stat_inc(ip6s_delivered);
break;
#endif /* INET6 */
}
switch (af) {
case AF_INET:
psw = &inetsw[ip_protox[nxt]];
break;
#ifdef INET6
case AF_INET6:
psw = &inet6sw[ip6_protox[nxt]];
break;
#endif /* INET6 */
}
nxt = (*psw->pr_input)(mp, offp, nxt, af);
af = naf;
}
return nxt;
bad:
m_freemp(mp);
return IPPROTO_DONE;
}
#undef IPSTAT_INC
int
in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct rtentry **prt)
{
struct rtentry *rt;
struct ip *ip;
struct sockaddr_in sin;
int match = 0;
#if NPF > 0
switch (pf_ouraddr(m)) {
case 0:
return (0);
case 1:
return (1);
default:
/* pf does not know it */
break;
}
#endif
ip = mtod(m, struct ip *);
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = ip->ip_dst;
rt = rtalloc_mpath(sintosa(&sin), &ip->ip_src.s_addr,
m->m_pkthdr.ph_rtableid);
if (rtisvalid(rt)) {
if (ISSET(rt->rt_flags, RTF_LOCAL))
match = 1;
/*
* If directedbcast is enabled we only consider it local
* if it is received on the interface with that address.
*/
if (ISSET(rt->rt_flags, RTF_BROADCAST) &&
(!ip_directedbcast || rt->rt_ifidx == ifp->if_index)) {
match = 1;
/* Make sure M_BCAST is set */
m->m_flags |= M_BCAST;
}
}
*prt = rt;
if (!match) {
struct ifaddr *ifa;
/*
* No local address or broadcast address found, so check for
* ancient classful broadcast addresses.
* It must have been broadcast on the link layer, and for an
* address on the interface it was received on.
*/
if (!ISSET(m->m_flags, M_BCAST) ||
!IN_CLASSFULBROADCAST(ip->ip_dst.s_addr, ip->ip_dst.s_addr))
return (0);
if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid))
return (0);
/*
* The check in the loop assumes you only rx a packet on an UP
* interface, and that M_BCAST will only be set on a BROADCAST
* interface.
*/
NET_ASSERT_LOCKED();
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
if (IN_CLASSFULBROADCAST(ip->ip_dst.s_addr,
ifatoia(ifa)->ia_addr.sin_addr.s_addr)) {
match = 1;
break;
}
}
} else if (ipforwarding == 0 && rt->rt_ifidx != ifp->if_index &&
!((ifp->if_flags & IFF_LOOPBACK) || (ifp->if_type == IFT_ENC) ||
(m->m_pkthdr.pf.flags & PF_TAG_TRANSLATE_LOCALHOST))) {
/* received on wrong interface. */
#if NCARP > 0
struct ifnet *out_if;
/*
* Virtual IPs on carp interfaces need to be checked also
* against the parent interface and other carp interfaces
* sharing the same parent.
*/
out_if = if_get(rt->rt_ifidx);
if (!(out_if && carp_strict_addr_chk(out_if, ifp))) {
ipstat_inc(ips_wrongif);
match = 2;
}
if_put(out_if);
#else
ipstat_inc(ips_wrongif);
match = 2;
#endif
}
return (match);
}
/*
* Take incoming datagram fragment and try to
* reassemble it into whole datagram. If a chain for
* reassembly of this datagram already exists, then it
* is given as fp; otherwise have to make a chain.
*/
struct mbuf *
ip_reass(struct ipqent *ipqe, struct ipq *fp)
{
struct mbuf *m = ipqe->ipqe_m;
struct ipqent *nq, *p, *q;
struct ip *ip;
struct mbuf *t;
int hlen = ipqe->ipqe_ip->ip_hl << 2;
int i, next;
u_int8_t ecn, ecn0;
MUTEX_ASSERT_LOCKED(&ipq_mutex);
/*
* Presence of header sizes in mbufs
* would confuse code below.
*/
m->m_data += hlen;
m->m_len -= hlen;
/*
* If first fragment to arrive, create a reassembly queue.
*/
if (fp == NULL) {
fp = pool_get(&ipq_pool, PR_NOWAIT);
if (fp == NULL)
goto dropfrag;
LIST_INSERT_HEAD(&ipq, fp, ipq_q);
fp->ipq_ttl = IPFRAGTTL;
fp->ipq_p = ipqe->ipqe_ip->ip_p;
fp->ipq_id = ipqe->ipqe_ip->ip_id;
LIST_INIT(&fp->ipq_fragq);
fp->ipq_src = ipqe->ipqe_ip->ip_src;
fp->ipq_dst = ipqe->ipqe_ip->ip_dst;
p = NULL;
goto insert;
}
/*
* Handle ECN by comparing this segment with the first one;
* if CE is set, do not lose CE.
* drop if CE and not-ECT are mixed for the same packet.
*/
ecn = ipqe->ipqe_ip->ip_tos & IPTOS_ECN_MASK;
ecn0 = LIST_FIRST(&fp->ipq_fragq)->ipqe_ip->ip_tos & IPTOS_ECN_MASK;
if (ecn == IPTOS_ECN_CE) {
if (ecn0 == IPTOS_ECN_NOTECT)
goto dropfrag;
if (ecn0 != IPTOS_ECN_CE)
LIST_FIRST(&fp->ipq_fragq)->ipqe_ip->ip_tos |=
IPTOS_ECN_CE;
}
if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT)
goto dropfrag;
/*
* Find a segment which begins after this one does.
*/
for (p = NULL, q = LIST_FIRST(&fp->ipq_fragq); q != NULL;
p = q, q = LIST_NEXT(q, ipqe_q))
if (ntohs(q->ipqe_ip->ip_off) > ntohs(ipqe->ipqe_ip->ip_off))
break;
/*
* If there is a preceding segment, it may provide some of
* our data already. If so, drop the data from the incoming
* segment. If it provides all of our data, drop us.
*/
if (p != NULL) {
i = ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) -
ntohs(ipqe->ipqe_ip->ip_off);
if (i > 0) {
if (i >= ntohs(ipqe->ipqe_ip->ip_len))
goto dropfrag;
m_adj(ipqe->ipqe_m, i);
ipqe->ipqe_ip->ip_off =
htons(ntohs(ipqe->ipqe_ip->ip_off) + i);
ipqe->ipqe_ip->ip_len =
htons(ntohs(ipqe->ipqe_ip->ip_len) - i);
}
}
/*
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
*/
for (; q != NULL &&
ntohs(ipqe->ipqe_ip->ip_off) + ntohs(ipqe->ipqe_ip->ip_len) >
ntohs(q->ipqe_ip->ip_off); q = nq) {
i = (ntohs(ipqe->ipqe_ip->ip_off) +
ntohs(ipqe->ipqe_ip->ip_len)) - ntohs(q->ipqe_ip->ip_off);
if (i < ntohs(q->ipqe_ip->ip_len)) {
q->ipqe_ip->ip_len =
htons(ntohs(q->ipqe_ip->ip_len) - i);
q->ipqe_ip->ip_off =
htons(ntohs(q->ipqe_ip->ip_off) + i);
m_adj(q->ipqe_m, i);
break;
}
nq = LIST_NEXT(q, ipqe_q);
m_freem(q->ipqe_m);
LIST_REMOVE(q, ipqe_q);
pool_put(&ipqent_pool, q);
ip_frags--;
}
insert:
/*
* Stick new segment in its place;
* check for complete reassembly.
*/
if (p == NULL) {
LIST_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q);
} else {
LIST_INSERT_AFTER(p, ipqe, ipqe_q);
}
next = 0;
for (p = NULL, q = LIST_FIRST(&fp->ipq_fragq); q != NULL;
p = q, q = LIST_NEXT(q, ipqe_q)) {
if (ntohs(q->ipqe_ip->ip_off) != next)
return (0);
next += ntohs(q->ipqe_ip->ip_len);
}
if (p->ipqe_mff)
return (0);
/*
* Reassembly is complete. Check for a bogus message size and
* concatenate fragments.
*/
q = LIST_FIRST(&fp->ipq_fragq);
ip = q->ipqe_ip;
if ((next + (ip->ip_hl << 2)) > IP_MAXPACKET) {
ipstat_inc(ips_toolong);
ip_freef(fp);
return (0);
}
m = q->ipqe_m;
t = m->m_next;
m->m_next = 0;
m_cat(m, t);
nq = LIST_NEXT(q, ipqe_q);
pool_put(&ipqent_pool, q);
ip_frags--;
for (q = nq; q != NULL; q = nq) {
t = q->ipqe_m;
nq = LIST_NEXT(q, ipqe_q);
pool_put(&ipqent_pool, q);
ip_frags--;
m_removehdr(t);
m_cat(m, t);
}
/*
* Create header for new ip packet by
* modifying header of first packet;
* dequeue and discard fragment reassembly header.
* Make header visible.
*/
ip->ip_len = htons(next);
ip->ip_src = fp->ipq_src;
ip->ip_dst = fp->ipq_dst;
LIST_REMOVE(fp, ipq_q);
pool_put(&ipq_pool, fp);
m->m_len += (ip->ip_hl << 2);
m->m_data -= (ip->ip_hl << 2);
m_calchdrlen(m);
return (m);
dropfrag:
ipstat_inc(ips_fragdropped);
m_freem(m);
pool_put(&ipqent_pool, ipqe);
ip_frags--;
return (NULL);
}
/*
* Free a fragment reassembly header and all
* associated datagrams.
*/
void
ip_freef(struct ipq *fp)
{
struct ipqent *q;
MUTEX_ASSERT_LOCKED(&ipq_mutex);
while ((q = LIST_FIRST(&fp->ipq_fragq)) != NULL) {
LIST_REMOVE(q, ipqe_q);
m_freem(q->ipqe_m);
pool_put(&ipqent_pool, q);
ip_frags--;
}
LIST_REMOVE(fp, ipq_q);
pool_put(&ipq_pool, fp);
}
/*
* IP timer processing;
* if a timer expires on a reassembly queue, discard it.
*/
void
ip_slowtimo(void)
{
struct ipq *fp, *nfp;
mtx_enter(&ipq_mutex);
LIST_FOREACH_SAFE(fp, &ipq, ipq_q, nfp) {
if (--fp->ipq_ttl == 0) {
ipstat_inc(ips_fragtimeout);
ip_freef(fp);
}
}
mtx_leave(&ipq_mutex);
}
/*
* Flush a bunch of datagram fragments, till we are down to 75%.
*/
void
ip_flush(void)
{
int max = 50;
MUTEX_ASSERT_LOCKED(&ipq_mutex);
while (!LIST_EMPTY(&ipq) && ip_frags > ip_maxqueue * 3 / 4 && --max) {
ipstat_inc(ips_fragdropped);
ip_freef(LIST_FIRST(&ipq));
}
}
/*
* Do option processing on a datagram,
* possibly discarding it if bad options are encountered,
* or forwarding it if source-routed.
* Returns 1 if packet has been forwarded/freed,
* 0 if the packet should be processed further.
*/
int
ip_dooptions(struct mbuf *m, struct ifnet *ifp)
{
struct ip *ip = mtod(m, struct ip *);
unsigned int rtableid = m->m_pkthdr.ph_rtableid;
struct rtentry *rt;
struct sockaddr_in ipaddr;
u_char *cp;
struct ip_timestamp ipt;
struct in_ifaddr *ia;
int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
struct in_addr sin, dst;
u_int32_t ntime;
dst = ip->ip_dst;
cp = (u_char *)(ip + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
KERNEL_LOCK();
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
optlen = 1;
else {
if (cnt < IPOPT_OLEN + sizeof(*cp)) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
optlen = cp[IPOPT_OLEN];
if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
}
switch (opt) {
default:
break;
/*
* Source routing with record.
* Find interface with current destination address.
* If none on this machine then drop if strictly routed,
* or do nothing if loosely routed.
* Record interface address and bring up next address
* component. If strictly routed make sure next
* address is on directly accessible net.
*/
case IPOPT_LSRR:
case IPOPT_SSRR:
if (!ip_dosourceroute) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.sin_family = AF_INET;
ipaddr.sin_len = sizeof(ipaddr);
ipaddr.sin_addr = ip->ip_dst;
ia = ifatoia(ifa_ifwithaddr(sintosa(&ipaddr),
m->m_pkthdr.ph_rtableid));
if (ia == NULL) {
if (opt == IPOPT_SSRR) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
/*
* Loose routing, and not at next destination
* yet; nothing to do except forward.
*/
break;
}
off--; /* 0 origin */
if ((off + sizeof(struct in_addr)) > optlen) {
/*
* End of source route. Should be for us.
*/
save_rte(m, cp, ip->ip_src);
break;
}
/*
* locate outgoing interface
*/
memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.sin_family = AF_INET;
ipaddr.sin_len = sizeof(ipaddr);
memcpy(&ipaddr.sin_addr, cp + off,
sizeof(ipaddr.sin_addr));
/* keep packet in the virtual instance */
rt = rtalloc(sintosa(&ipaddr), RT_RESOLVE, rtableid);
if (!rtisvalid(rt) || ((opt == IPOPT_SSRR) &&
ISSET(rt->rt_flags, RTF_GATEWAY))) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
rtfree(rt);
goto bad;
}
ia = ifatoia(rt->rt_ifa);
memcpy(cp + off, &ia->ia_addr.sin_addr,
sizeof(struct in_addr));
rtfree(rt);
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
ip->ip_dst = ipaddr.sin_addr;
/*
* Let ip_intr's mcast routing check handle mcast pkts
*/
forward = !IN_MULTICAST(ip->ip_dst.s_addr);
break;
case IPOPT_RR:
if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
/*
* If no space remains, ignore.
*/
off--; /* 0 origin */
if ((off + sizeof(struct in_addr)) > optlen)
break;
memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.sin_family = AF_INET;
ipaddr.sin_len = sizeof(ipaddr);
ipaddr.sin_addr = ip->ip_dst;
/*
* locate outgoing interface; if we're the destination,
* use the incoming interface (should be same).
* Again keep the packet inside the virtual instance.
*/
rt = rtalloc(sintosa(&ipaddr), RT_RESOLVE, rtableid);
if (!rtisvalid(rt)) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_HOST;
rtfree(rt);
goto bad;
}
ia = ifatoia(rt->rt_ifa);
memcpy(cp + off, &ia->ia_addr.sin_addr,
sizeof(struct in_addr));
rtfree(rt);
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
break;
case IPOPT_TS:
code = cp - (u_char *)ip;
if (optlen < sizeof(struct ip_timestamp))
goto bad;
memcpy(&ipt, cp, sizeof(struct ip_timestamp));
if (ipt.ipt_ptr < 5 || ipt.ipt_len < 5)
goto bad;
if (ipt.ipt_ptr - 1 + sizeof(u_int32_t) > ipt.ipt_len) {
if (++ipt.ipt_oflw == 0)
goto bad;
break;
}
memcpy(&sin, cp + ipt.ipt_ptr - 1, sizeof sin);
switch (ipt.ipt_flg) {
case IPOPT_TS_TSONLY:
break;
case IPOPT_TS_TSANDADDR:
if (ipt.ipt_ptr - 1 + sizeof(u_int32_t) +
sizeof(struct in_addr) > ipt.ipt_len)
goto bad;
memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.sin_family = AF_INET;
ipaddr.sin_len = sizeof(ipaddr);
ipaddr.sin_addr = dst;
ia = ifatoia(ifaof_ifpforaddr(sintosa(&ipaddr),
ifp));
if (ia == NULL)
continue;
memcpy(&sin, &ia->ia_addr.sin_addr,
sizeof(struct in_addr));
ipt.ipt_ptr += sizeof(struct in_addr);
break;
case IPOPT_TS_PRESPEC:
if (ipt.ipt_ptr - 1 + sizeof(u_int32_t) +
sizeof(struct in_addr) > ipt.ipt_len)
goto bad;
memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.sin_family = AF_INET;
ipaddr.sin_len = sizeof(ipaddr);
ipaddr.sin_addr = sin;
if (ifa_ifwithaddr(sintosa(&ipaddr),
m->m_pkthdr.ph_rtableid) == NULL)
continue;
ipt.ipt_ptr += sizeof(struct in_addr);
break;
default:
/* XXX can't take &ipt->ipt_flg */
code = (u_char *)&ipt.ipt_ptr -
(u_char *)ip + 1;
goto bad;
}
ntime = iptime();
memcpy(cp + ipt.ipt_ptr - 1, &ntime, sizeof(u_int32_t));
ipt.ipt_ptr += sizeof(u_int32_t);
}
}
KERNEL_UNLOCK();
if (forward && ipforwarding > 0) {
ip_forward(m, ifp, NULL, 1);
return (1);
}
return (0);
bad:
KERNEL_UNLOCK();
icmp_error(m, type, code, 0, 0);
ipstat_inc(ips_badoptions);
return (1);
}
/*
* Save incoming source route for use in replies,
* to be picked up later by ip_srcroute if the receiver is interested.
*/
void
save_rte(struct mbuf *m, u_char *option, struct in_addr dst)
{
struct ip_srcrt *isr;
struct m_tag *mtag;
unsigned olen;
olen = option[IPOPT_OLEN];
if (olen > sizeof(isr->isr_hdr) + sizeof(isr->isr_routes))
return;
mtag = m_tag_get(PACKET_TAG_SRCROUTE, sizeof(*isr), M_NOWAIT);
if (mtag == NULL) {
ipstat_inc(ips_idropped);
return;
}
isr = (struct ip_srcrt *)(mtag + 1);
memcpy(isr->isr_hdr, option, olen);
isr->isr_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
isr->isr_dst = dst;
m_tag_prepend(m, mtag);
}
/*
* Retrieve incoming source route for use in replies,
* in the same form used by setsockopt.
* The first hop is placed before the options, will be removed later.
*/
struct mbuf *
ip_srcroute(struct mbuf *m0)
{
struct in_addr *p, *q;
struct mbuf *m;
struct ip_srcrt *isr;
struct m_tag *mtag;
if (!ip_dosourceroute)
return (NULL);
mtag = m_tag_find(m0, PACKET_TAG_SRCROUTE, NULL);
if (mtag == NULL)
return (NULL);
isr = (struct ip_srcrt *)(mtag + 1);
if (isr->isr_nhops == 0)
return (NULL);
m = m_get(M_DONTWAIT, MT_SOOPTS);
if (m == NULL) {
ipstat_inc(ips_idropped);
return (NULL);
}
#define OPTSIZ (sizeof(isr->isr_nop) + sizeof(isr->isr_hdr))
/* length is (nhops+1)*sizeof(addr) + sizeof(nop + header) */
m->m_len = (isr->isr_nhops + 1) * sizeof(struct in_addr) + OPTSIZ;
/*
* First save first hop for return route
*/
p = &(isr->isr_routes[isr->isr_nhops - 1]);
*(mtod(m, struct in_addr *)) = *p--;
/*
* Copy option fields and padding (nop) to mbuf.
*/
isr->isr_nop = IPOPT_NOP;
isr->isr_hdr[IPOPT_OFFSET] = IPOPT_MINOFF;
memcpy(mtod(m, caddr_t) + sizeof(struct in_addr), &isr->isr_nop,
OPTSIZ);
q = (struct in_addr *)(mtod(m, caddr_t) +
sizeof(struct in_addr) + OPTSIZ);
#undef OPTSIZ
/*
* Record return path as an IP source route,
* reversing the path (pointers are now aligned).
*/
while (p >= isr->isr_routes) {
*q++ = *p--;
}
/*
* Last hop goes to final destination.
*/
*q = isr->isr_dst;
m_tag_delete(m0, (struct m_tag *)isr);
return (m);
}
/*
* Strip out IP options, at higher level protocol in the kernel.
*/
void
ip_stripoptions(struct mbuf *m)
{
int i;
struct ip *ip = mtod(m, struct ip *);
caddr_t opts;
int olen;
olen = (ip->ip_hl<<2) - sizeof (struct ip);
opts = (caddr_t)(ip + 1);
i = m->m_len - (sizeof (struct ip) + olen);
memmove(opts, opts + olen, i);
m->m_len -= olen;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len -= olen;
ip->ip_hl = sizeof(struct ip) >> 2;
ip->ip_len = htons(ntohs(ip->ip_len) - olen);
}
const u_char inetctlerrmap[PRC_NCMDS] = {
0, 0, 0, 0,
0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
EMSGSIZE, EHOSTUNREACH, 0, 0,
0, 0, 0, 0,
ENOPROTOOPT
};
/*
* Forward a packet. If some error occurs return the sender
* an icmp packet. Note we can't always generate a meaningful
* icmp message because icmp doesn't have a large enough repertoire
* of codes and types.
*
* If not forwarding, just drop the packet. This could be confusing
* if ipforwarding was zero but some routing protocol was advancing
* us as a gateway to somewhere. However, we must let the routing
* protocol deal with that.
*
* The srcrt parameter indicates whether the packet is being forwarded
* via a source route.
*/
void
ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
{
struct mbuf mfake, *mcopy = NULL;
struct ip *ip = mtod(m, struct ip *);
struct sockaddr_in *sin;
struct route ro;
int error = 0, type = 0, code = 0, destmtu = 0, fake = 0, len;
u_int32_t dest;
dest = 0;
if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) {
ipstat_inc(ips_cantforward);
m_freem(m);
goto freecopy;
}
if (ip->ip_ttl <= IPTTLDEC) {
icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
goto freecopy;
}
memset(&ro, 0, sizeof(ro));
sin = satosin(&ro.ro_dst);
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr = ip->ip_dst;
if (!rtisvalid(rt)) {
rtfree(rt);
rt = rtalloc_mpath(sintosa(sin), &ip->ip_src.s_addr,
m->m_pkthdr.ph_rtableid);
if (rt == NULL) {
ipstat_inc(ips_noroute);
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
return;
}
}
/*
* Save at most 68 bytes of the packet in case
* we need to generate an ICMP message to the src.
* The data is saved in the mbuf on the stack that
* acts as a temporary storage not intended to be
* passed down the IP stack or to the mfree.
*/
memset(&mfake.m_hdr, 0, sizeof(mfake.m_hdr));
mfake.m_type = m->m_type;
if (m_dup_pkthdr(&mfake, m, M_DONTWAIT) == 0) {
mfake.m_data = mfake.m_pktdat;
len = min(ntohs(ip->ip_len), 68);
m_copydata(m, 0, len, mfake.m_pktdat);
mfake.m_pkthdr.len = mfake.m_len = len;
#if NPF > 0
pf_pkt_addr_changed(&mfake);
#endif /* NPF > 0 */
fake = 1;
}
ip->ip_ttl -= IPTTLDEC;
/*
* If forwarding packet using same interface that it came in on,
* perhaps should send a redirect to sender to shortcut a hop.
* Only send redirect if source is sending directly to us,
* and if packet was not source routed (or has any options).
* Also, don't send redirect if forwarding using a default route
* or a route modified by a redirect.
* Don't send redirect if we advertise destination's arp address
* as ours (proxy arp).
*/
if ((rt->rt_ifidx == ifp->if_index) &&
(rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
ipsendredirects && !srcrt &&
!arpproxy(satosin(rt_key(rt))->sin_addr, m->m_pkthdr.ph_rtableid)) {
if ((ip->ip_src.s_addr & ifatoia(rt->rt_ifa)->ia_netmask) ==
ifatoia(rt->rt_ifa)->ia_net) {
if (rt->rt_flags & RTF_GATEWAY)
dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
else
dest = ip->ip_dst.s_addr;
/* Router requirements says to only send host redirects */
type = ICMP_REDIRECT;
code = ICMP_REDIRECT_HOST;
}
}
ro.ro_rt = rt;
ro.ro_tableid = m->m_pkthdr.ph_rtableid;
error = ip_output(m, NULL, &ro,
(IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)),
NULL, NULL, 0);
rt = ro.ro_rt;
if (error)
ipstat_inc(ips_cantforward);
else {
ipstat_inc(ips_forward);
if (type)
ipstat_inc(ips_redirectsent);
else
goto freecopy;
}
if (!fake)
goto freecopy;
switch (error) {
case 0: /* forwarded, but need redirect */
/* type, code set above */
break;
case EMSGSIZE:
type = ICMP_UNREACH;
code = ICMP_UNREACH_NEEDFRAG;
if (rt != NULL) {
if (rt->rt_mtu) {
destmtu = rt->rt_mtu;
} else {
struct ifnet *destifp;
destifp = if_get(rt->rt_ifidx);
if (destifp != NULL)
destmtu = destifp->if_mtu;
if_put(destifp);
}
}
ipstat_inc(ips_cantfrag);
if (destmtu == 0)
goto freecopy;
break;
case EACCES:
/*
* pf(4) blocked the packet. There is no need to send an ICMP
* packet back since pf(4) takes care of it.
*/
goto freecopy;
case ENOBUFS:
/*
* a router should not generate ICMP_SOURCEQUENCH as
* required in RFC1812 Requirements for IP Version 4 Routers.
* source quench could be a big problem under DoS attacks,
* or the underlying interface is rate-limited.
*/
goto freecopy;
case ENETUNREACH: /* shouldn't happen, checked above */
case EHOSTUNREACH:
case ENETDOWN:
case EHOSTDOWN:
default:
type = ICMP_UNREACH;
code = ICMP_UNREACH_HOST;
break;
}
mcopy = m_copym(&mfake, 0, len, M_DONTWAIT);
if (mcopy)
icmp_error(mcopy, type, code, dest, destmtu);
freecopy:
if (fake)
m_tag_delete_chain(&mfake);
rtfree(rt);
}
int
ip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
#ifdef MROUTING
extern struct mrtstat mrtstat;
#endif
/* Almost all sysctl names at this level are terminal. */
if (namelen != 1 && name[0] != IPCTL_IFQUEUE &&
name[0] != IPCTL_ARPQUEUE)
return (ENOTDIR);
switch (name[0]) {
case IPCTL_SOURCEROUTE:
NET_LOCK();
error = sysctl_securelevel_int(oldp, oldlenp, newp, newlen,
&ip_dosourceroute);
NET_UNLOCK();
return (error);
case IPCTL_MTUDISC:
NET_LOCK();
error = sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtudisc);
if (ip_mtudisc == 0)
rt_timer_queue_flush(&ip_mtudisc_timeout_q);
NET_UNLOCK();
return error;
case IPCTL_MTUDISCTIMEOUT:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&ip_mtudisc_timeout, 0, INT_MAX);
rt_timer_queue_change(&ip_mtudisc_timeout_q,
ip_mtudisc_timeout);
NET_UNLOCK();
return (error);
#ifdef IPSEC
case IPCTL_ENCDEBUG:
case IPCTL_IPSEC_STATS:
case IPCTL_IPSEC_EXPIRE_ACQUIRE:
case IPCTL_IPSEC_EMBRYONIC_SA_TIMEOUT:
case IPCTL_IPSEC_REQUIRE_PFS:
case IPCTL_IPSEC_SOFT_ALLOCATIONS:
case IPCTL_IPSEC_ALLOCATIONS:
case IPCTL_IPSEC_SOFT_BYTES:
case IPCTL_IPSEC_BYTES:
case IPCTL_IPSEC_TIMEOUT:
case IPCTL_IPSEC_SOFT_TIMEOUT:
case IPCTL_IPSEC_SOFT_FIRSTUSE:
case IPCTL_IPSEC_FIRSTUSE:
case IPCTL_IPSEC_ENC_ALGORITHM:
case IPCTL_IPSEC_AUTH_ALGORITHM:
case IPCTL_IPSEC_IPCOMP_ALGORITHM:
return (ipsec_sysctl(name, namelen, oldp, oldlenp, newp,
newlen));
#endif
case IPCTL_IFQUEUE:
return (sysctl_niq(name + 1, namelen - 1,
oldp, oldlenp, newp, newlen, &ipintrq));
case IPCTL_ARPQUEUE:
return (sysctl_niq(name + 1, namelen - 1,
oldp, oldlenp, newp, newlen, &arpinq));
case IPCTL_ARPQUEUED:
return (sysctl_rdint(oldp, oldlenp, newp, la_hold_total));
case IPCTL_STATS:
return (ip_sysctl_ipstat(oldp, oldlenp, newp));
#ifdef MROUTING
case IPCTL_MRTSTATS:
return (sysctl_rdstruct(oldp, oldlenp, newp,
&mrtstat, sizeof(mrtstat)));
case IPCTL_MRTMFC:
if (newp)
return (EPERM);
NET_LOCK();
error = mrt_sysctl_mfc(oldp, oldlenp);
NET_UNLOCK();
return (error);
case IPCTL_MRTVIF:
if (newp)
return (EPERM);
NET_LOCK();
error = mrt_sysctl_vif(oldp, oldlenp);
NET_UNLOCK();
return (error);
#else
case IPCTL_MRTPROTO:
case IPCTL_MRTSTATS:
case IPCTL_MRTMFC:
case IPCTL_MRTVIF:
return (EOPNOTSUPP);
#endif
default:
NET_LOCK();
error = sysctl_bounded_arr(ipctl_vars, nitems(ipctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
return (error);
}
/* NOTREACHED */
}
int
ip_sysctl_ipstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[ips_ncounters];
struct ipstat ipstat;
u_long *words = (u_long *)&ipstat;
int i;
CTASSERT(sizeof(ipstat) == (nitems(counters) * sizeof(u_long)));
memset(&ipstat, 0, sizeof ipstat);
counters_read(ipcounters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp, &ipstat, sizeof(ipstat)));
}
void
ip_savecontrol(struct inpcb *inp, struct mbuf **mp, struct ip *ip,
struct mbuf *m)
{
if (inp->inp_socket->so_options & SO_TIMESTAMP) {
struct timeval tv;
m_microtime(m, &tv);
*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
SCM_TIMESTAMP, SOL_SOCKET);
if (*mp)
mp = &(*mp)->m_next;
}
if (inp->inp_flags & INP_RECVDSTADDR) {
*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
if (*mp)
mp = &(*mp)->m_next;
}
#ifdef notyet
/* this code is broken and will probably never be fixed. */
/* options were tossed already */
if (inp->inp_flags & INP_RECVOPTS) {
*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
if (*mp)
mp = &(*mp)->m_next;
}
/* ip_srcroute doesn't do what we want here, need to fix */
if (inp->inp_flags & INP_RECVRETOPTS) {
*mp = sbcreatecontrol((caddr_t) ip_srcroute(m),
sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
if (*mp)
mp = &(*mp)->m_next;
}
#endif
if (inp->inp_flags & INP_RECVIF) {
struct sockaddr_dl sdl;
struct ifnet *ifp;
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL || ifp->if_sadl == NULL) {
memset(&sdl, 0, sizeof(sdl));
sdl.sdl_len = offsetof(struct sockaddr_dl, sdl_data[0]);
sdl.sdl_family = AF_LINK;
sdl.sdl_index = ifp != NULL ? ifp->if_index : 0;
sdl.sdl_nlen = sdl.sdl_alen = sdl.sdl_slen = 0;
*mp = sbcreatecontrol((caddr_t) &sdl, sdl.sdl_len,
IP_RECVIF, IPPROTO_IP);
} else {
*mp = sbcreatecontrol((caddr_t) ifp->if_sadl,
ifp->if_sadl->sdl_len, IP_RECVIF, IPPROTO_IP);
}
if (*mp)
mp = &(*mp)->m_next;
if_put(ifp);
}
if (inp->inp_flags & INP_RECVTTL) {
*mp = sbcreatecontrol((caddr_t) &ip->ip_ttl,
sizeof(u_int8_t), IP_RECVTTL, IPPROTO_IP);
if (*mp)
mp = &(*mp)->m_next;
}
if (inp->inp_flags & INP_RECVRTABLE) {
u_int rtableid = inp->inp_rtableid;
#if NPF > 0
if (m && m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
struct pf_divert *divert;
divert = pf_find_divert(m);
KASSERT(divert != NULL);
rtableid = divert->rdomain;
}
#endif
*mp = sbcreatecontrol((caddr_t) &rtableid,
sizeof(u_int), IP_RECVRTABLE, IPPROTO_IP);
if (*mp)
mp = &(*mp)->m_next;
}
}
void
ip_send_do_dispatch(void *xmq, int flags)
{
struct mbuf_queue *mq = xmq;
struct mbuf *m;
struct mbuf_list ml;
struct m_tag *mtag;
mq_delist(mq, &ml);
if (ml_empty(&ml))
return;
NET_LOCK();
while ((m = ml_dequeue(&ml)) != NULL) {
u_int32_t ipsecflowinfo = 0;
if ((mtag = m_tag_find(m, PACKET_TAG_IPSEC_FLOWINFO, NULL))
!= NULL) {
ipsecflowinfo = *(u_int32_t *)(mtag + 1);
m_tag_delete(m, mtag);
}
ip_output(m, NULL, NULL, flags, NULL, NULL, ipsecflowinfo);
}
NET_UNLOCK();
}
void
ip_sendraw_dispatch(void *xmq)
{
ip_send_do_dispatch(xmq, IP_RAWOUTPUT);
}
void
ip_send_dispatch(void *xmq)
{
ip_send_do_dispatch(xmq, 0);
}
void
ip_send(struct mbuf *m)
{
mq_enqueue(&ipsend_mq, m);
task_add(net_tq(0), &ipsend_task);
}
void
ip_send_raw(struct mbuf *m)
{
mq_enqueue(&ipsendraw_mq, m);
task_add(net_tq(0), &ipsendraw_task);
}
27
28
24
5
1
1
1
21
21
1
62
9
12
40
40
1
40
1
1
1
1
56
56
24
1
1
28
48
27
21
8
4
4
4
3
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
/* $OpenBSD: if_ether.c,v 1.251 2022/07/16 15:25:30 bluhm Exp $ */
/* $NetBSD: if_ether.c,v 1.31 1996/05/11 12:59:58 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)if_ether.c 8.1 (Berkeley) 6/10/93
*/
/*
* Ethernet address resolution protocol.
* TODO:
* add "inuse/lock" bit (or ref. count) along with valid bit
*/
#include "carp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/queue.h>
#include <sys/pool.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netinet/ip_var.h>
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
/*
* Locks used to protect struct members in this file:
* a atomic operations
* I immutable after creation
* K kernel lock
* m arp mutex, needed when net lock is shared
* N net lock
*/
struct llinfo_arp {
LIST_ENTRY(llinfo_arp) la_list; /* [mN] global arp_list */
struct rtentry *la_rt; /* [I] backpointer to rtentry */
struct mbuf_queue la_mq; /* packet hold queue */
time_t la_refreshed; /* when was refresh sent */
int la_asked; /* number of queries sent */
};
#define LA_HOLD_QUEUE 10
#define LA_HOLD_TOTAL 100
/* timer values */
int arpt_prune = (5 * 60); /* [I] walk list every 5 minutes */
int arpt_keep = (20 * 60); /* [a] once resolved, cache for 20 minutes */
int arpt_down = 20; /* [a] once declared down, don't send for 20 secs */
struct mbuf *arppullup(struct mbuf *m);
void arpinvalidate(struct rtentry *);
void arptfree(struct rtentry *);
void arptimer(void *);
struct rtentry *arplookup(struct in_addr *, int, int, unsigned int);
void in_arpinput(struct ifnet *, struct mbuf *);
void in_revarpinput(struct ifnet *, struct mbuf *);
int arpcache(struct ifnet *, struct ether_arp *, struct rtentry *);
void arpreply(struct ifnet *, struct mbuf *, struct in_addr *, uint8_t *,
unsigned int);
struct niqueue arpinq = NIQUEUE_INITIALIZER(50, NETISR_ARP);
/* llinfo_arp live time, rt_llinfo and RTF_LLINFO are protected by arp_mtx */
struct mutex arp_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
LIST_HEAD(, llinfo_arp) arp_list; /* [mN] list of all llinfo_arp structures */
struct pool arp_pool; /* [I] pool for llinfo_arp structures */
int arp_maxtries = 5; /* [I] arp requests before set to rejected */
int la_hold_total; /* [a] packets currently in the arp queue */
#ifdef NFSCLIENT
/* revarp state */
struct in_addr revarp_myip, revarp_srvip;
int revarp_finished;
unsigned int revarp_ifidx;
#endif /* NFSCLIENT */
/*
* Timeout routine. Age arp_tab entries periodically.
*/
/* ARGSUSED */
void
arptimer(void *arg)
{
struct timeout *to = arg;
struct llinfo_arp *la, *nla;
time_t uptime;
NET_LOCK();
uptime = getuptime();
timeout_add_sec(to, arpt_prune);
/* Net lock is exclusive, no arp mutex needed for arp_list here. */
LIST_FOREACH_SAFE(la, &arp_list, la_list, nla) {
struct rtentry *rt = la->la_rt;
if (rt->rt_expire && rt->rt_expire < uptime)
arptfree(rt); /* timer has expired; clear */
}
NET_UNLOCK();
}
void
arpinit(void)
{
static struct timeout arptimer_to;
pool_init(&arp_pool, sizeof(struct llinfo_arp), 0,
IPL_SOFTNET, 0, "arp", NULL);
timeout_set_proc(&arptimer_to, arptimer, &arptimer_to);
timeout_add_sec(&arptimer_to, arpt_prune);
}
void
arp_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
{
struct sockaddr *gate = rt->rt_gateway;
struct llinfo_arp *la;
time_t uptime;
NET_ASSERT_LOCKED();
if (ISSET(rt->rt_flags,
RTF_GATEWAY|RTF_BROADCAST|RTF_MULTICAST|RTF_MPLS))
return;
uptime = getuptime();
switch (req) {
case RTM_ADD:
if (rt->rt_flags & RTF_CLONING) {
rt->rt_expire = 0;
break;
}
if ((rt->rt_flags & RTF_LOCAL) && rt->rt_llinfo == NULL)
rt->rt_expire = 0;
/*
* Announce a new entry if requested or warn the user
* if another station has this IP address.
*/
if (rt->rt_flags & (RTF_ANNOUNCE|RTF_LOCAL))
arprequest(ifp,
&satosin(rt_key(rt))->sin_addr.s_addr,
&satosin(rt_key(rt))->sin_addr.s_addr,
(u_char *)LLADDR(satosdl(gate)));
/*FALLTHROUGH*/
case RTM_RESOLVE:
if (gate->sa_family != AF_LINK ||
gate->sa_len < sizeof(struct sockaddr_dl)) {
log(LOG_DEBUG, "%s: bad gateway value: %s\n", __func__,
ifp->if_xname);
break;
}
satosdl(gate)->sdl_type = ifp->if_type;
satosdl(gate)->sdl_index = ifp->if_index;
/*
* Case 2: This route may come from cloning, or a manual route
* add with a LL address.
*/
la = pool_get(&arp_pool, PR_NOWAIT | PR_ZERO);
if (la == NULL) {
log(LOG_DEBUG, "%s: pool get failed\n", __func__);
break;
}
mtx_enter(&arp_mtx);
if (rt->rt_llinfo != NULL) {
/* we lost the race, another thread has entered it */
mtx_leave(&arp_mtx);
pool_put(&arp_pool, la);
break;
}
mq_init(&la->la_mq, LA_HOLD_QUEUE, IPL_SOFTNET);
rt->rt_llinfo = (caddr_t)la;
la->la_rt = rt;
rt->rt_flags |= RTF_LLINFO;
LIST_INSERT_HEAD(&arp_list, la, la_list);
if ((rt->rt_flags & RTF_LOCAL) == 0)
rt->rt_expire = uptime;
mtx_leave(&arp_mtx);
break;
case RTM_DELETE:
mtx_enter(&arp_mtx);
la = (struct llinfo_arp *)rt->rt_llinfo;
if (la == NULL) {
/* we lost the race, another thread has removed it */
mtx_leave(&arp_mtx);
break;
}
LIST_REMOVE(la, la_list);
rt->rt_llinfo = NULL;
rt->rt_flags &= ~RTF_LLINFO;
atomic_sub_int(&la_hold_total, mq_purge(&la->la_mq));
mtx_leave(&arp_mtx);
pool_put(&arp_pool, la);
break;
case RTM_INVALIDATE:
if (!ISSET(rt->rt_flags, RTF_LOCAL))
arpinvalidate(rt);
break;
}
}
/*
* Broadcast an ARP request. Caller specifies:
* - arp header source ip address
* - arp header target ip address
* - arp header source ethernet address
*/
void
arprequest(struct ifnet *ifp, u_int32_t *sip, u_int32_t *tip, u_int8_t *enaddr)
{
struct mbuf *m;
struct ether_header *eh;
struct ether_arp *ea;
struct sockaddr sa;
if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
return;
m->m_len = sizeof(*ea);
m->m_pkthdr.len = sizeof(*ea);
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
m->m_pkthdr.pf.prio = ifp->if_llprio;
m_align(m, sizeof(*ea));
ea = mtod(m, struct ether_arp *);
eh = (struct ether_header *)sa.sa_data;
memset(ea, 0, sizeof(*ea));
memcpy(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost));
eh->ether_type = htons(ETHERTYPE_ARP); /* if_output will not swap */
ea->arp_hrd = htons(ARPHRD_ETHER);
ea->arp_pro = htons(ETHERTYPE_IP);
ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */
ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */
ea->arp_op = htons(ARPOP_REQUEST);
memcpy(eh->ether_shost, enaddr, sizeof(eh->ether_shost));
memcpy(ea->arp_sha, enaddr, sizeof(ea->arp_sha));
memcpy(ea->arp_spa, sip, sizeof(ea->arp_spa));
memcpy(ea->arp_tpa, tip, sizeof(ea->arp_tpa));
sa.sa_family = pseudo_AF_HDRCMPLT;
sa.sa_len = sizeof(sa);
m->m_flags |= M_BCAST;
ifp->if_output(ifp, m, &sa, NULL);
}
void
arpreply(struct ifnet *ifp, struct mbuf *m, struct in_addr *sip, uint8_t *eaddr,
unsigned int rdomain)
{
struct ether_header *eh;
struct ether_arp *ea;
struct sockaddr sa;
m_resethdr(m);
m->m_pkthdr.ph_rtableid = rdomain;
ea = mtod(m, struct ether_arp *);
ea->arp_op = htons(ARPOP_REPLY);
ea->arp_pro = htons(ETHERTYPE_IP); /* let's be sure! */
/* We're replying to a request. */
memcpy(ea->arp_tha, ea->arp_sha, sizeof(ea->arp_sha));
memcpy(ea->arp_tpa, ea->arp_spa, sizeof(ea->arp_spa));
memcpy(ea->arp_sha, eaddr, sizeof(ea->arp_sha));
memcpy(ea->arp_spa, sip, sizeof(ea->arp_spa));
eh = (struct ether_header *)sa.sa_data;
memcpy(eh->ether_dhost, ea->arp_tha, sizeof(eh->ether_dhost));
memcpy(eh->ether_shost, eaddr, sizeof(eh->ether_shost));
eh->ether_type = htons(ETHERTYPE_ARP);
sa.sa_family = pseudo_AF_HDRCMPLT;
sa.sa_len = sizeof(sa);
ifp->if_output(ifp, m, &sa, NULL);
}
/*
* Resolve an IP address into an ethernet address. If success,
* desten is filled in. If there is no entry in arptab,
* set one up and broadcast a request for the IP address.
* Hold onto this mbuf and resend it once the address
* is finally resolved. A return value of 0 indicates
* that desten has been filled in and the packet should be sent
* normally; A return value of EAGAIN indicates that the packet
* has been taken over here, either now or for later transmission.
* Any other return value indicates an error.
*/
int
arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
struct sockaddr *dst, u_char *desten)
{
struct arpcom *ac = (struct arpcom *)ifp;
struct llinfo_arp *la;
struct sockaddr_dl *sdl;
struct rtentry *rt = NULL;
char addr[INET_ADDRSTRLEN];
time_t uptime;
if (m->m_flags & M_BCAST) { /* broadcast */
memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr));
return (0);
}
if (m->m_flags & M_MCAST) { /* multicast */
ETHER_MAP_IP_MULTICAST(&satosin(dst)->sin_addr, desten);
return (0);
}
uptime = getuptime();
rt = rt_getll(rt0);
if (ISSET(rt->rt_flags, RTF_REJECT) &&
(rt->rt_expire == 0 || rt->rt_expire > uptime)) {
m_freem(m);
return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
log(LOG_DEBUG, "%s: %s: route contains no arp information\n",
__func__, inet_ntop(AF_INET, &satosin(rt_key(rt))->sin_addr,
addr, sizeof(addr)));
goto bad;
}
sdl = satosdl(rt->rt_gateway);
if (sdl->sdl_alen > 0 && sdl->sdl_alen != ETHER_ADDR_LEN) {
log(LOG_DEBUG, "%s: %s: incorrect arp information\n", __func__,
inet_ntop(AF_INET, &satosin(dst)->sin_addr,
addr, sizeof(addr)));
goto bad;
}
/*
* Check the address family and length is valid, the address
* is resolved; otherwise, try to resolve.
*/
if ((rt->rt_expire == 0 || rt->rt_expire > uptime) &&
sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {
int refresh = 0;
memcpy(desten, LLADDR(sdl), sdl->sdl_alen);
/* refresh ARP entry when timeout gets close */
if (rt->rt_expire != 0 &&
rt->rt_expire - arpt_keep / 8 < uptime) {
mtx_enter(&arp_mtx);
if (ISSET(rt->rt_flags, RTF_LLINFO)) {
la = (struct llinfo_arp *)rt->rt_llinfo;
KASSERT(la != NULL);
if (la->la_refreshed + 30 < uptime) {
la->la_refreshed = uptime;
refresh = 1;
}
}
mtx_leave(&arp_mtx);
}
if (refresh) {
arprequest(ifp,
&satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr,
&satosin(dst)->sin_addr.s_addr,
ac->ac_enaddr);
}
return (0);
}
if (ifp->if_flags & (IFF_NOARP|IFF_STATICARP))
goto bad;
KERNEL_LOCK();
/*
* Re-check since we grab the kernel lock after the first check.
* rtrequest_delete() can be called with shared netlock. From
* there arp_rtrequest() is reached which touches RTF_LLINFO
* and rt_llinfo. As this is called with kernel lock we grab the
* kernel lock here and are safe. XXXSMP
*/
if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
KERNEL_UNLOCK();
goto bad;
}
la = (struct llinfo_arp *)rt->rt_llinfo;
KASSERT(la != NULL);
/*
* There is an arptab entry, but no ethernet address
* response yet. Insert mbuf in hold queue if below limit
* if above the limit free the queue without queuing the new packet.
*/
if (atomic_inc_int_nv(&la_hold_total) <= LA_HOLD_TOTAL) {
if (mq_push(&la->la_mq, m) != 0)
atomic_dec_int(&la_hold_total);
} else {
atomic_sub_int(&la_hold_total, mq_purge(&la->la_mq) + 1);
m_freem(m);
}
/*
* Re-send the ARP request when appropriate.
*/
#ifdef DIAGNOSTIC
if (rt->rt_expire == 0) {
/* This should never happen. (Should it? -gwr) */
printf("%s: unresolved and rt_expire == 0\n", __func__);
/* Set expiration time to now (expired). */
rt->rt_expire = uptime;
}
#endif
if (rt->rt_expire) {
rt->rt_flags &= ~RTF_REJECT;
if (la->la_asked == 0 || rt->rt_expire != uptime) {
rt->rt_expire = uptime;
if (la->la_asked++ < arp_maxtries)
arprequest(ifp,
&satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr,
&satosin(dst)->sin_addr.s_addr,
ac->ac_enaddr);
else {
rt->rt_flags |= RTF_REJECT;
rt->rt_expire += arpt_down;
la->la_asked = 0;
la->la_refreshed = 0;
atomic_sub_int(&la_hold_total,
mq_purge(&la->la_mq));
}
}
}
KERNEL_UNLOCK();
return (EAGAIN);
bad:
m_freem(m);
return (EINVAL);
}
struct mbuf *
arppullup(struct mbuf *m)
{
struct arphdr *ar;
int len;
#ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0)
panic("arp without packet header");
#endif
len = sizeof(struct arphdr);
if (m->m_len < len && (m = m_pullup(m, len)) == NULL)
return NULL;
ar = mtod(m, struct arphdr *);
if (ntohs(ar->ar_hrd) != ARPHRD_ETHER ||
ntohs(ar->ar_pro) != ETHERTYPE_IP ||
ar->ar_hln != ETHER_ADDR_LEN ||
ar->ar_pln != sizeof(struct in_addr)) {
m_freem(m);
return NULL;
}
len += 2 * (ar->ar_hln + ar->ar_pln);
if (m->m_len < len && (m = m_pullup(m, len)) == NULL)
return NULL;
return m;
}
/*
* Common length and type checks are done here,
* then the protocol-specific routine is called.
*/
void
arpinput(struct ifnet *ifp, struct mbuf *m)
{
if ((m = arppullup(m)) == NULL)
return;
niq_enqueue(&arpinq, m);
}
void
arpintr(void)
{
struct mbuf_list ml;
struct mbuf *m;
struct ifnet *ifp;
niq_delist(&arpinq, &ml);
while ((m = ml_dequeue(&ml)) != NULL) {
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp != NULL)
in_arpinput(ifp, m);
else
m_freem(m);
if_put(ifp);
}
}
/*
* ARP for Internet protocols on Ethernet, RFC 826.
* In addition, a sanity check is performed on the sender
* protocol address, to catch impersonators.
*/
void
in_arpinput(struct ifnet *ifp, struct mbuf *m)
{
struct ether_arp *ea;
struct rtentry *rt = NULL;
struct sockaddr_in sin;
struct in_addr isaddr, itaddr;
char addr[INET_ADDRSTRLEN];
int op, target = 0;
unsigned int rdomain;
rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
ea = mtod(m, struct ether_arp *);
op = ntohs(ea->arp_op);
if ((op != ARPOP_REQUEST) && (op != ARPOP_REPLY))
goto out;
memcpy(&itaddr, ea->arp_tpa, sizeof(itaddr));
memcpy(&isaddr, ea->arp_spa, sizeof(isaddr));
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
if (ETHER_IS_MULTICAST(ea->arp_sha) &&
ETHER_IS_BROADCAST(ea->arp_sha)) {
inet_ntop(AF_INET, &isaddr, addr, sizeof(addr));
log(LOG_ERR, "arp: ether address is broadcast for IP address "
"%s!\n", addr);
goto out;
}
if (!memcmp(ea->arp_sha, LLADDR(ifp->if_sadl), sizeof(ea->arp_sha)))
goto out; /* it's from me, ignore it. */
/* Check target against our interface addresses. */
sin.sin_addr = itaddr;
rt = rtalloc(sintosa(&sin), 0, rdomain);
if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) &&
rt->rt_ifidx == ifp->if_index)
target = 1;
rtfree(rt);
rt = NULL;
#if NCARP > 0
if (target && op == ARPOP_REQUEST && ifp->if_type == IFT_CARP &&
!carp_iamatch(ifp))
goto out;
#endif
/* Do we have an ARP cache for the sender? Create if we are target. */
rt = arplookup(&isaddr, target, 0, rdomain);
/* Check sender against our interface addresses. */
if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) &&
rt->rt_ifidx == ifp->if_index && isaddr.s_addr != INADDR_ANY) {
inet_ntop(AF_INET, &isaddr, addr, sizeof(addr));
log(LOG_ERR, "duplicate IP address %s sent from ethernet "
"address %s\n", addr, ether_sprintf(ea->arp_sha));
itaddr = isaddr;
} else if (rt != NULL) {
int error;
KERNEL_LOCK();
error = arpcache(ifp, ea, rt);
KERNEL_UNLOCK();
if (error)
goto out;
}
if (op == ARPOP_REQUEST) {
uint8_t *eaddr;
if (target) {
/* We already have all info for the reply */
eaddr = LLADDR(ifp->if_sadl);
} else {
rtfree(rt);
rt = arplookup(&itaddr, 0, SIN_PROXY, rdomain);
/*
* Protect from possible duplicates, only owner
* should respond
*/
if ((rt == NULL) || (rt->rt_ifidx != ifp->if_index))
goto out;
eaddr = LLADDR(satosdl(rt->rt_gateway));
}
arpreply(ifp, m, &itaddr, eaddr, rdomain);
rtfree(rt);
return;
}
out:
rtfree(rt);
m_freem(m);
}
int
arpcache(struct ifnet *ifp, struct ether_arp *ea, struct rtentry *rt)
{
struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
struct sockaddr_dl *sdl = satosdl(rt->rt_gateway);
struct in_addr *spa = (struct in_addr *)ea->arp_spa;
char addr[INET_ADDRSTRLEN];
struct ifnet *rifp;
struct mbuf_list ml;
struct mbuf *m;
time_t uptime;
unsigned int len;
int changed = 0;
KERNEL_ASSERT_LOCKED();
KASSERT(sdl != NULL);
/*
* This can happen if the entry has been deleted by another CPU
* after we found it.
*/
if (la == NULL)
return (0);
uptime = getuptime();
if (sdl->sdl_alen > 0) {
if (memcmp(ea->arp_sha, LLADDR(sdl), sdl->sdl_alen)) {
if (ISSET(rt->rt_flags, RTF_PERMANENT_ARP|RTF_LOCAL)) {
inet_ntop(AF_INET, spa, addr, sizeof(addr));
log(LOG_WARNING, "arp: attempt to overwrite "
"permanent entry for %s by %s on %s\n", addr,
ether_sprintf(ea->arp_sha), ifp->if_xname);
return (-1);
} else if (rt->rt_ifidx != ifp->if_index) {
#if NCARP > 0
if (ifp->if_type != IFT_CARP)
#endif
{
rifp = if_get(rt->rt_ifidx);
if (rifp == NULL)
return (-1);
inet_ntop(AF_INET, spa, addr,
sizeof(addr));
log(LOG_WARNING, "arp: attempt to "
"overwrite entry for %s on %s by "
"%s on %s\n", addr, rifp->if_xname,
ether_sprintf(ea->arp_sha),
ifp->if_xname);
if_put(rifp);
}
return (-1);
} else {
inet_ntop(AF_INET, spa, addr, sizeof(addr));
log(LOG_INFO, "arp info overwritten for %s by "
"%s on %s\n", addr,
ether_sprintf(ea->arp_sha), ifp->if_xname);
rt->rt_expire = 1;/* no longer static */
}
changed = 1;
}
} else if (!if_isconnected(ifp, rt->rt_ifidx)) {
rifp = if_get(rt->rt_ifidx);
if (rifp == NULL)
return (-1);
inet_ntop(AF_INET, spa, addr, sizeof(addr));
log(LOG_WARNING, "arp: attempt to add entry for %s on %s by %s"
" on %s\n", addr, rifp->if_xname,
ether_sprintf(ea->arp_sha), ifp->if_xname);
if_put(rifp);
return (-1);
}
sdl->sdl_alen = sizeof(ea->arp_sha);
memcpy(LLADDR(sdl), ea->arp_sha, sizeof(ea->arp_sha));
if (rt->rt_expire)
rt->rt_expire = uptime + arpt_keep;
rt->rt_flags &= ~RTF_REJECT;
/* Notify userland that an ARP resolution has been done. */
if (la->la_asked || changed) {
rtm_send(rt, RTM_RESOLVE, 0, ifp->if_rdomain);
}
la->la_asked = 0;
la->la_refreshed = 0;
mq_delist(&la->la_mq, &ml);
len = ml_len(&ml);
while ((m = ml_dequeue(&ml)) != NULL)
ifp->if_output(ifp, m, rt_key(rt), rt);
/* XXXSMP we discard if other CPU enqueues */
if (mq_len(&la->la_mq) > 0) {
/* mbuf is back in queue. Discard. */
atomic_sub_int(&la_hold_total, len + mq_purge(&la->la_mq));
} else
atomic_sub_int(&la_hold_total, len);
return (0);
}
void
arpinvalidate(struct rtentry *rt)
{
struct llinfo_arp *la;
struct sockaddr_dl *sdl = satosdl(rt->rt_gateway);
mtx_enter(&arp_mtx);
la = (struct llinfo_arp *)rt->rt_llinfo;
if (la == NULL) {
mtx_leave(&arp_mtx);
return;
}
atomic_sub_int(&la_hold_total, mq_purge(&la->la_mq));
sdl->sdl_alen = 0;
la->la_asked = 0;
mtx_leave(&arp_mtx);
}
/*
* Free an arp entry.
*/
void
arptfree(struct rtentry *rt)
{
struct ifnet *ifp;
KASSERT(!ISSET(rt->rt_flags, RTF_LOCAL));
arpinvalidate(rt);
ifp = if_get(rt->rt_ifidx);
KASSERT(ifp != NULL);
if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED))
rtdeletemsg(rt, ifp, ifp->if_rdomain);
if_put(ifp);
}
/*
* Lookup or enter a new address in arptab.
*/
struct rtentry *
arplookup(struct in_addr *inp, int create, int proxy, u_int tableid)
{
struct rtentry *rt;
struct sockaddr_inarp sin;
int flags;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inp->s_addr;
sin.sin_other = proxy ? SIN_PROXY : 0;
flags = (create) ? RT_RESOLVE : 0;
rt = rtalloc((struct sockaddr *)&sin, flags, tableid);
if (!rtisvalid(rt) || ISSET(rt->rt_flags, RTF_GATEWAY) ||
!ISSET(rt->rt_flags, RTF_LLINFO) ||
rt->rt_gateway->sa_family != AF_LINK) {
rtfree(rt);
return (NULL);
}
if (proxy && !ISSET(rt->rt_flags, RTF_ANNOUNCE)) {
while ((rt = rtable_iterate(rt)) != NULL) {
if (ISSET(rt->rt_flags, RTF_ANNOUNCE)) {
break;
}
}
}
return (rt);
}
/*
* Check whether we do proxy ARP for this address and we point to ourselves.
*/
int
arpproxy(struct in_addr in, unsigned int rtableid)
{
struct sockaddr_dl *sdl;
struct rtentry *rt;
struct ifnet *ifp;
int found = 0;
rt = arplookup(&in, 0, SIN_PROXY, rtableid);
if (!rtisvalid(rt)) {
rtfree(rt);
return (0);
}
/* Check that arp information are correct. */
sdl = satosdl(rt->rt_gateway);
if (sdl->sdl_alen != ETHER_ADDR_LEN) {
rtfree(rt);
return (0);
}
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
rtfree(rt);
return (0);
}
if (!memcmp(LLADDR(sdl), LLADDR(ifp->if_sadl), sdl->sdl_alen))
found = 1;
if_put(ifp);
rtfree(rt);
return (found);
}
/*
* Called from Ethernet interrupt handlers
* when ether packet type ETHERTYPE_REVARP
* is received. Common length and type checks are done here,
* then the protocol-specific routine is called.
*/
void
revarpinput(struct ifnet *ifp, struct mbuf *m)
{
if ((m = arppullup(m)) == NULL)
return;
in_revarpinput(ifp, m);
}
/*
* RARP for Internet protocols on Ethernet.
* Algorithm is that given in RFC 903.
* We are only using for bootstrap purposes to get an ip address for one of
* our interfaces. Thus we support no user-interface.
*
* Since the contents of the RARP reply are specific to the interface that
* sent the request, this code must ensure that they are properly associated.
*
* Note: also supports ARP via RARP packets, per the RFC.
*/
void
in_revarpinput(struct ifnet *ifp, struct mbuf *m)
{
struct ether_arp *ar;
int op;
ar = mtod(m, struct ether_arp *);
op = ntohs(ar->arp_op);
switch (op) {
case ARPOP_REQUEST:
case ARPOP_REPLY: /* per RFC */
niq_enqueue(&arpinq, m);
return;
case ARPOP_REVREPLY:
break;
case ARPOP_REVREQUEST: /* handled by rarpd(8) */
default:
goto out;
}
#ifdef NFSCLIENT
if (revarp_ifidx == 0)
goto out;
if (revarp_ifidx != m->m_pkthdr.ph_ifidx) /* !same interface */
goto out;
if (revarp_finished)
goto wake;
if (memcmp(ar->arp_tha, LLADDR(ifp->if_sadl), sizeof(ar->arp_tha)))
goto out;
memcpy(&revarp_srvip, ar->arp_spa, sizeof(revarp_srvip));
memcpy(&revarp_myip, ar->arp_tpa, sizeof(revarp_myip));
revarp_finished = 1;
wake: /* Do wakeup every time in case it was missed. */
wakeup((caddr_t)&revarp_myip);
#endif /* NFSCLIENT */
out:
m_freem(m);
}
/*
* Send a RARP request for the ip address of the specified interface.
* The request should be RFC 903-compliant.
*/
void
revarprequest(struct ifnet *ifp)
{
struct sockaddr sa;
struct mbuf *m;
struct ether_header *eh;
struct ether_arp *ea;
struct arpcom *ac = (struct arpcom *)ifp;
if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
return;
m->m_len = sizeof(*ea);
m->m_pkthdr.len = sizeof(*ea);
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
m->m_pkthdr.pf.prio = ifp->if_llprio;
m_align(m, sizeof(*ea));
ea = mtod(m, struct ether_arp *);
eh = (struct ether_header *)sa.sa_data;
memset(ea, 0, sizeof(*ea));
memcpy(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost));
eh->ether_type = htons(ETHERTYPE_REVARP);
ea->arp_hrd = htons(ARPHRD_ETHER);
ea->arp_pro = htons(ETHERTYPE_IP);
ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */
ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */
ea->arp_op = htons(ARPOP_REVREQUEST);
memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(ea->arp_tha));
memcpy(ea->arp_sha, ac->ac_enaddr, sizeof(ea->arp_sha));
memcpy(ea->arp_tha, ac->ac_enaddr, sizeof(ea->arp_tha));
sa.sa_family = pseudo_AF_HDRCMPLT;
sa.sa_len = sizeof(sa);
m->m_flags |= M_BCAST;
ifp->if_output(ifp, m, &sa, NULL);
}
#ifdef NFSCLIENT
/*
* RARP for the ip address of the specified interface, but also
* save the ip address of the server that sent the answer.
* Timeout if no response is received.
*/
int
revarpwhoarewe(struct ifnet *ifp, struct in_addr *serv_in,
struct in_addr *clnt_in)
{
int result, count = 20;
if (revarp_finished)
return EIO;
revarp_ifidx = ifp->if_index;
while (count--) {
revarprequest(ifp);
result = tsleep_nsec(&revarp_myip, PSOCK, "revarp",
MSEC_TO_NSEC(500));
if (result != EWOULDBLOCK)
break;
}
revarp_ifidx = 0;
if (!revarp_finished)
return ENETUNREACH;
memcpy(serv_in, &revarp_srvip, sizeof(*serv_in));
memcpy(clnt_in, &revarp_myip, sizeof(*clnt_in));
return 0;
}
/* For compatibility: only saves interface address. */
int
revarpwhoami(struct in_addr *in, struct ifnet *ifp)
{
struct in_addr server;
return (revarpwhoarewe(ifp, &server, in));
}
#endif /* NFSCLIENT */
10
4
1
3
3
3
3
4
2
2
2
2
4
2
1
1
5
3
3
2
1
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/* $OpenBSD: fuse_device.c,v 1.38 2022/08/29 06:08:04 jsg Exp $ */
/*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/vnode.h>
#include <sys/fusebuf.h>
#include "fusefs_node.h"
#include "fusefs.h"
#ifdef FUSE_DEBUG
#define DPRINTF(fmt, arg...) printf("%s: " fmt, "fuse", ##arg)
#else
#define DPRINTF(fmt, arg...)
#endif
SIMPLEQ_HEAD(fusebuf_head, fusebuf);
struct fuse_d {
struct fusefs_mnt *fd_fmp;
int fd_unit;
/*fusebufs queues*/
struct fusebuf_head fd_fbufs_in;
struct fusebuf_head fd_fbufs_wait;
/* kq fields */
struct selinfo fd_rsel;
LIST_ENTRY(fuse_d) fd_list;
};
int stat_fbufs_in = 0;
int stat_fbufs_wait = 0;
int stat_opened_fusedev = 0;
LIST_HEAD(, fuse_d) fuse_d_list;
struct fuse_d *fuse_lookup(int);
void fuseattach(int);
int fuseopen(dev_t, int, int, struct proc *);
int fuseclose(dev_t, int, int, struct proc *);
int fuseioctl(dev_t, u_long, caddr_t, int, struct proc *);
int fuseread(dev_t, struct uio *, int);
int fusewrite(dev_t, struct uio *, int);
int fusekqfilter(dev_t dev, struct knote *kn);
int filt_fuse_read(struct knote *, long);
void filt_fuse_rdetach(struct knote *);
static const struct filterops fuse_rd_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_fuse_rdetach,
.f_event = filt_fuse_read,
};
#ifdef FUSE_DEBUG
static void
fuse_dump_buff(char *buff, int len)
{
char text[17];
int i;
if (len < 0) {
printf("invalid len: %d", len);
return;
}
if (buff == NULL) {
printf("invalid buff");
return;
}
memset(text, 0, 17);
for (i = 0; i < len; i++) {
if (i != 0 && (i % 16) == 0) {
printf(": %s\n", text);
memset(text, 0, 17);
}
printf("%.2x ", buff[i] & 0xff);
if (buff[i] > ' ' && buff[i] < '~')
text[i%16] = buff[i] & 0xff;
else
text[i%16] = '.';
}
if ((i % 16) != 0)
while ((i % 16) != 0) {
printf(" ");
i++;
}
printf(": %s\n", text);
}
#endif
struct fuse_d *
fuse_lookup(int unit)
{
struct fuse_d *fd;
LIST_FOREACH(fd, &fuse_d_list, fd_list)
if (fd->fd_unit == unit)
return (fd);
return (NULL);
}
/*
* Cleanup all msgs from sc_fbufs_in and sc_fbufs_wait.
*/
void
fuse_device_cleanup(dev_t dev)
{
struct fuse_d *fd;
struct fusebuf *f, *ftmp, *lprev;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return;
/* clear FIFO IN */
lprev = NULL;
SIMPLEQ_FOREACH_SAFE(f, &fd->fd_fbufs_in, fb_next, ftmp) {
DPRINTF("cleanup unprocessed msg in sc_fbufs_in\n");
if (lprev == NULL)
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
else
SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_in, lprev,
fb_next);
stat_fbufs_in--;
f->fb_err = ENXIO;
wakeup(f);
lprev = f;
}
/* clear FIFO WAIT*/
lprev = NULL;
SIMPLEQ_FOREACH_SAFE(f, &fd->fd_fbufs_wait, fb_next, ftmp) {
DPRINTF("umount unprocessed msg in sc_fbufs_wait\n");
if (lprev == NULL)
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
else
SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lprev,
fb_next);
stat_fbufs_wait--;
f->fb_err = ENXIO;
wakeup(f);
lprev = f;
}
}
void
fuse_device_queue_fbuf(dev_t dev, struct fusebuf *fbuf)
{
struct fuse_d *fd;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return;
SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_in, fbuf, fb_next);
stat_fbufs_in++;
selwakeup(&fd->fd_rsel);
}
void
fuse_device_set_fmp(struct fusefs_mnt *fmp, int set)
{
struct fuse_d *fd;
fd = fuse_lookup(minor(fmp->dev));
if (fd == NULL)
return;
fd->fd_fmp = set ? fmp : NULL;
}
void
fuseattach(int num)
{
LIST_INIT(&fuse_d_list);
}
int
fuseopen(dev_t dev, int flags, int fmt, struct proc * p)
{
struct fuse_d *fd;
int unit = minor(dev);
if (flags & O_EXCL)
return (EBUSY); /* No exclusive opens */
if ((fd = fuse_lookup(unit)) != NULL)
return (EBUSY);
fd = malloc(sizeof(*fd), M_DEVBUF, M_WAITOK | M_ZERO);
fd->fd_unit = unit;
SIMPLEQ_INIT(&fd->fd_fbufs_in);
SIMPLEQ_INIT(&fd->fd_fbufs_wait);
LIST_INSERT_HEAD(&fuse_d_list, fd, fd_list);
stat_opened_fusedev++;
return (0);
}
int
fuseclose(dev_t dev, int flags, int fmt, struct proc *p)
{
struct fuse_d *fd;
int error;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return (EINVAL);
if (fd->fd_fmp) {
printf("fuse: device close without umount\n");
fd->fd_fmp->sess_init = 0;
fuse_device_cleanup(dev);
if ((vfs_busy(fd->fd_fmp->mp, VB_WRITE | VB_NOWAIT)) != 0)
goto end;
error = dounmount(fd->fd_fmp->mp, MNT_FORCE, p);
if (error)
printf("fuse: unmount failed with error %d\n", error);
fd->fd_fmp = NULL;
}
end:
LIST_REMOVE(fd, fd_list);
free(fd, M_DEVBUF, sizeof(*fd));
stat_opened_fusedev--;
return (0);
}
/*
* FIOCGETFBDAT Get fusebuf data from kernel to user
* FIOCSETFBDAT Set fusebuf data from user to kernel
*/
int
fuseioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
{
struct fb_ioctl_xch *ioexch;
struct fusebuf *lastfbuf;
struct fusebuf *fbuf;
struct fuse_d *fd;
int error = 0;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return (ENXIO);
switch (cmd) {
case FIOCGETFBDAT:
ioexch = (struct fb_ioctl_xch *)addr;
/* Looking for uuid in fd_fbufs_in */
SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_in, fb_next) {
if (fbuf->fb_uuid == ioexch->fbxch_uuid)
break;
lastfbuf = fbuf;
}
if (fbuf == NULL) {
printf("fuse: Cannot find fusebuf\n");
return (EINVAL);
}
/* Remove the fbuf from fd_fbufs_in */
if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_in))
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
else
SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_in, lastfbuf,
fb_next);
stat_fbufs_in--;
/* Do not handle fbufs with bad len */
if (fbuf->fb_len != ioexch->fbxch_len) {
printf("fuse: Bad fusebuf len\n");
return (EINVAL);
}
/* Update the userland fbuf */
error = copyout(fbuf->fb_dat, ioexch->fbxch_data,
ioexch->fbxch_len);
if (error) {
printf("fuse: cannot copyout\n");
return (error);
}
#ifdef FUSE_DEBUG
fuse_dump_buff(fbuf->fb_dat, fbuf->fb_len);
#endif
/* Adding fbuf in fd_fbufs_wait */
free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
fbuf->fb_dat = NULL;
SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
stat_fbufs_wait++;
break;
case FIOCSETFBDAT:
DPRINTF("SET BUFFER\n");
ioexch = (struct fb_ioctl_xch *)addr;
/* looking for uuid in fd_fbufs_wait */
SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
if (fbuf->fb_uuid == ioexch->fbxch_uuid)
break;
lastfbuf = fbuf;
}
if (fbuf == NULL) {
printf("fuse: Cannot find fusebuf\n");
return (EINVAL);
}
/* Do not handle fbufs with bad len */
if (fbuf->fb_len != ioexch->fbxch_len) {
printf("fuse: Bad fusebuf size\n");
return (EINVAL);
}
/* fetching data from userland */
fbuf->fb_dat = malloc(ioexch->fbxch_len, M_FUSEFS,
M_WAITOK | M_ZERO);
error = copyin(ioexch->fbxch_data, fbuf->fb_dat,
ioexch->fbxch_len);
if (error) {
printf("fuse: Cannot copyin\n");
free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
fbuf->fb_dat = NULL;
return (error);
}
#ifdef FUSE_DEBUG
fuse_dump_buff(fbuf->fb_dat, fbuf->fb_len);
#endif
/* Remove fbuf from fd_fbufs_wait */
if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
else
SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lastfbuf,
fb_next);
stat_fbufs_wait--;
wakeup(fbuf);
break;
default:
error = EINVAL;
}
return (error);
}
int
fuseread(dev_t dev, struct uio *uio, int ioflag)
{
struct fuse_d *fd;
struct fusebuf *fbuf;
struct fb_hdr hdr;
void *tmpaddr;
int error = 0;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return (ENXIO);
if (SIMPLEQ_EMPTY(&fd->fd_fbufs_in)) {
if (ioflag & O_NONBLOCK)
return (EAGAIN);
goto end;
}
fbuf = SIMPLEQ_FIRST(&fd->fd_fbufs_in);
/* We get the whole fusebuf or nothing */
if (uio->uio_resid != FUSEBUFSIZE)
return (EINVAL);
/* Do not send kernel pointers */
memcpy(&hdr.fh_next, &fbuf->fb_next, sizeof(fbuf->fb_next));
memset(&fbuf->fb_next, 0, sizeof(fbuf->fb_next));
tmpaddr = fbuf->fb_dat;
fbuf->fb_dat = NULL;
error = uiomove(fbuf, FUSEBUFSIZE, uio);
if (error)
goto end;
#ifdef FUSE_DEBUG
fuse_dump_buff((char *)fbuf, FUSEBUFSIZE);
#endif
/* Restore kernel pointers */
memcpy(&fbuf->fb_next, &hdr.fh_next, sizeof(fbuf->fb_next));
fbuf->fb_dat = tmpaddr;
/* Remove the fbuf if it does not contains data */
if (fbuf->fb_len == 0) {
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
stat_fbufs_in--;
SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
stat_fbufs_wait++;
}
end:
return (error);
}
int
fusewrite(dev_t dev, struct uio *uio, int ioflag)
{
struct fusebuf *lastfbuf;
struct fuse_d *fd;
struct fusebuf *fbuf;
struct fb_hdr hdr;
int error = 0;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return (ENXIO);
/* We get the whole fusebuf or nothing */
if (uio->uio_resid != FUSEBUFSIZE)
return (EINVAL);
if ((error = uiomove(&hdr, sizeof(hdr), uio)) != 0)
return (error);
/* looking for uuid in fd_fbufs_wait */
SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
if (fbuf->fb_uuid == hdr.fh_uuid)
break;
lastfbuf = fbuf;
}
if (fbuf == NULL)
return (EINVAL);
/* Update fb_hdr */
fbuf->fb_len = hdr.fh_len;
fbuf->fb_err = hdr.fh_err;
fbuf->fb_ino = hdr.fh_ino;
/* Check for corrupted fbufs */
if ((fbuf->fb_len && fbuf->fb_err) ||
SIMPLEQ_EMPTY(&fd->fd_fbufs_wait)) {
printf("fuse: dropping corrupted fusebuf\n");
error = EINVAL;
goto end;
}
/* Get the missing data from the fbuf */
error = uiomove(&fbuf->FD, uio->uio_resid, uio);
if (error)
return error;
fbuf->fb_dat = NULL;
#ifdef FUSE_DEBUG
fuse_dump_buff((char *)fbuf, FUSEBUFSIZE);
#endif
switch (fbuf->fb_type) {
case FBT_INIT:
fd->fd_fmp->sess_init = 1;
break ;
case FBT_DESTROY:
fd->fd_fmp = NULL;
break ;
}
end:
/* Remove the fbuf if it does not contains data */
if (fbuf->fb_len == 0) {
if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
else
SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lastfbuf,
fb_next);
stat_fbufs_wait--;
if (fbuf->fb_type == FBT_INIT)
fb_delete(fbuf);
else
wakeup(fbuf);
}
return (error);
}
int
fusekqfilter(dev_t dev, struct knote *kn)
{
struct fuse_d *fd;
struct klist *klist;
fd = fuse_lookup(minor(dev));
if (fd == NULL)
return (EINVAL);
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &fd->fd_rsel.si_note;
kn->kn_fop = &fuse_rd_filtops;
break;
case EVFILT_WRITE:
return (seltrue_kqfilter(dev, kn));
default:
return (EINVAL);
}
kn->kn_hook = fd;
klist_insert_locked(klist, kn);
return (0);
}
void
filt_fuse_rdetach(struct knote *kn)
{
struct fuse_d *fd = kn->kn_hook;
struct klist *klist = &fd->fd_rsel.si_note;
klist_remove_locked(klist, kn);
}
int
filt_fuse_read(struct knote *kn, long hint)
{
struct fuse_d *fd = kn->kn_hook;
int event = 0;
if (!SIMPLEQ_EMPTY(&fd->fd_fbufs_in))
event = 1;
return (event);
}
206
9
168
8
159
16
3
38
38
18
29
37
51
52
15
3
29
10
1
9
57
11
33
33
3
30
140
113
33
87
31
2
15
33
5
3
36
4
34
30
7
1
8
1
45
32
45
42
44
5
2
4
5
1
4
26
26
15
4
2
1
2
1
2
1
2
2
52
52
48
7
52
2
52
16
14
17
48
4
15
1
1
1
1
1
8
44
1
1
3
4
4
2
9
9
8
8
24
38
49
30
19
67
34
33
6
61
11
4
4
18
4
17
1
15
12
4
15
13
3
3
8
3
11
2
15
12
5
33
1
15
17
17
11
28
26
16
3
4
4
8
2
6
64
48
18
11
11
7
5
5
3
41
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
/* $OpenBSD: uipc_usrreq.c,v 1.185 2022/09/03 22:43:38 mvs Exp $ */
/* $NetBSD: uipc_usrreq.c,v 1.18 1996/02/09 19:00:50 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/unpcb.h>
#include <sys/un.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mbuf.h>
#include <sys/task.h>
#include <sys/pledge.h>
#include <sys/pool.h>
#include <sys/rwlock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/refcnt.h>
#include "kcov.h"
#if NKCOV > 0
#include <sys/kcov.h>
#endif
/*
* Locks used to protect global data and struct members:
* I immutable after creation
* D unp_df_lock
* G unp_gc_lock
* M unp_ino_mtx
* R unp_rights_mtx
* a atomic
* s socket lock
*/
struct rwlock unp_lock = RWLOCK_INITIALIZER("unplock");
struct rwlock unp_df_lock = RWLOCK_INITIALIZER("unpdflk");
struct rwlock unp_gc_lock = RWLOCK_INITIALIZER("unpgclk");
struct mutex unp_rights_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
struct mutex unp_ino_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
/*
* Stack of sets of files that were passed over a socket but were
* not received and need to be closed.
*/
struct unp_deferral {
SLIST_ENTRY(unp_deferral) ud_link; /* [D] */
int ud_n; /* [I] */
/* followed by ud_n struct fdpass */
struct fdpass ud_fp[]; /* [I] */
};
void uipc_setaddr(const struct unpcb *, struct mbuf *);
void unp_discard(struct fdpass *, int);
void unp_remove_gcrefs(struct fdpass *, int);
void unp_restore_gcrefs(struct fdpass *, int);
void unp_scan(struct mbuf *, void (*)(struct fdpass *, int));
int unp_nam2sun(struct mbuf *, struct sockaddr_un **, size_t *);
static inline void unp_ref(struct unpcb *);
static inline void unp_rele(struct unpcb *);
struct socket *unp_solock_peer(struct socket *);
struct pool unpcb_pool;
struct task unp_gc_task = TASK_INITIALIZER(unp_gc, NULL);
/*
* Unix communications domain.
*
* TODO:
* RDM
* rethink name space problems
* need a proper out-of-band
*/
const struct sockaddr sun_noname = { sizeof(sun_noname), AF_UNIX };
/* [G] list of all UNIX domain sockets, for unp_gc() */
LIST_HEAD(unp_head, unpcb) unp_head =
LIST_HEAD_INITIALIZER(unp_head);
/* [D] list of sets of files that were sent over sockets that are now closed */
SLIST_HEAD(,unp_deferral) unp_deferred =
SLIST_HEAD_INITIALIZER(unp_deferred);
ino_t unp_ino; /* [U] prototype for fake inode numbers */
int unp_rights; /* [R] file descriptors in flight */
int unp_defer; /* [G] number of deferred fp to close by the GC task */
int unp_gcing; /* [G] GC task currently running */
const struct pr_usrreqs uipc_usrreqs = {
.pru_attach = uipc_attach,
.pru_detach = uipc_detach,
.pru_bind = uipc_bind,
.pru_listen = uipc_listen,
.pru_connect = uipc_connect,
.pru_accept = uipc_accept,
.pru_disconnect = uipc_disconnect,
.pru_shutdown = uipc_shutdown,
.pru_rcvd = uipc_rcvd,
.pru_send = uipc_send,
.pru_abort = uipc_abort,
.pru_sense = uipc_sense,
.pru_sockaddr = uipc_sockaddr,
.pru_peeraddr = uipc_peeraddr,
.pru_connect2 = uipc_connect2,
};
void
unp_init(void)
{
pool_init(&unpcb_pool, sizeof(struct unpcb), 0,
IPL_SOFTNET, 0, "unpcb", NULL);
}
static inline void
unp_ref(struct unpcb *unp)
{
refcnt_take(&unp->unp_refcnt);
}
static inline void
unp_rele(struct unpcb *unp)
{
refcnt_rele_wake(&unp->unp_refcnt);
}
struct socket *
unp_solock_peer(struct socket *so)
{
struct unpcb *unp, *unp2;
struct socket *so2;
unp = so->so_pcb;
again:
if ((unp2 = unp->unp_conn) == NULL)
return NULL;
so2 = unp2->unp_socket;
if (so < so2)
solock(so2);
else if (so > so2){
unp_ref(unp2);
sounlock(so);
solock(so2);
solock(so);
/* Datagram socket could be reconnected due to re-lock. */
if (unp->unp_conn != unp2) {
sounlock(so2);
unp_rele(unp2);
goto again;
}
unp_rele(unp2);
}
return so2;
}
void
uipc_setaddr(const struct unpcb *unp, struct mbuf *nam)
{
if (unp != NULL && unp->unp_addr != NULL) {
nam->m_len = unp->unp_addr->m_len;
memcpy(mtod(nam, caddr_t), mtod(unp->unp_addr, caddr_t),
nam->m_len);
} else {
nam->m_len = sizeof(sun_noname);
memcpy(mtod(nam, struct sockaddr *), &sun_noname,
nam->m_len);
}
}
/*
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
* for stream sockets, although the total for sender and receiver is
* actually only PIPSIZ.
* Datagram sockets really use the sendspace as the maximum datagram size,
* and don't really want to reserve the sendspace. Their recvspace should
* be large enough for at least one max-size datagram plus address.
*/
#define PIPSIZ 8192
u_int unpst_sendspace = PIPSIZ;
u_int unpst_recvspace = PIPSIZ;
u_int unpsq_sendspace = PIPSIZ;
u_int unpsq_recvspace = PIPSIZ;
u_int unpdg_sendspace = 2*1024; /* really max datagram size */
u_int unpdg_recvspace = 16*1024;
const struct sysctl_bounded_args unpstctl_vars[] = {
{ UNPCTL_RECVSPACE, &unpst_recvspace, 0, SB_MAX },
{ UNPCTL_SENDSPACE, &unpst_sendspace, 0, SB_MAX },
};
const struct sysctl_bounded_args unpsqctl_vars[] = {
{ UNPCTL_RECVSPACE, &unpsq_recvspace, 0, SB_MAX },
{ UNPCTL_SENDSPACE, &unpsq_sendspace, 0, SB_MAX },
};
const struct sysctl_bounded_args unpdgctl_vars[] = {
{ UNPCTL_RECVSPACE, &unpdg_recvspace, 0, SB_MAX },
{ UNPCTL_SENDSPACE, &unpdg_sendspace, 0, SB_MAX },
};
int
uipc_attach(struct socket *so, int proto)
{
struct unpcb *unp;
int error;
if (so->so_pcb)
return EISCONN;
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
switch (so->so_type) {
case SOCK_STREAM:
error = soreserve(so, unpst_sendspace, unpst_recvspace);
break;
case SOCK_SEQPACKET:
error = soreserve(so, unpsq_sendspace, unpsq_recvspace);
break;
case SOCK_DGRAM:
error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
break;
default:
panic("unp_attach");
}
if (error)
return (error);
}
unp = pool_get(&unpcb_pool, PR_NOWAIT|PR_ZERO);
if (unp == NULL)
return (ENOBUFS);
refcnt_init(&unp->unp_refcnt);
unp->unp_socket = so;
so->so_pcb = unp;
getnanotime(&unp->unp_ctime);
/*
* Enforce `unp_gc_lock' -> `solock()' lock order.
*/
sounlock(so);
rw_enter_write(&unp_gc_lock);
LIST_INSERT_HEAD(&unp_head, unp, unp_link);
rw_exit_write(&unp_gc_lock);
solock(so);
return (0);
}
int
uipc_detach(struct socket *so)
{
struct unpcb *unp = sotounpcb(so);
if (unp == NULL)
return (EINVAL);
unp_detach(unp);
return (0);
}
int
uipc_bind(struct socket *so, struct mbuf *nam, struct proc *p)
{
struct unpcb *unp = sotounpcb(so);
return unp_bind(unp, nam, p);
}
int
uipc_listen(struct socket *so)
{
struct unpcb *unp = sotounpcb(so);
if (unp->unp_vnode == NULL)
return (EINVAL);
return (0);
}
int
uipc_connect(struct socket *so, struct mbuf *nam)
{
return unp_connect(so, nam, curproc);
}
int
uipc_accept(struct socket *so, struct mbuf *nam)
{
struct socket *so2;
struct unpcb *unp = sotounpcb(so);
/*
* Pass back name of connected socket, if it was bound and
* we are still connected (our peer may have closed already!).
*/
so2 = unp_solock_peer(so);
uipc_setaddr(unp->unp_conn, nam);
if (so2 != NULL && so2 != so)
sounlock(so2);
return (0);
}
int
uipc_disconnect(struct socket *so)
{
struct unpcb *unp = sotounpcb(so);
unp_disconnect(unp);
return (0);
}
int
uipc_shutdown(struct socket *so)
{
struct unpcb *unp = sotounpcb(so);
socantsendmore(so);
unp_shutdown(unp);
return (0);
}
int
uipc_rcvd(struct socket *so)
{
struct socket *so2;
switch (so->so_type) {
case SOCK_DGRAM:
panic("uipc 1");
/*NOTREACHED*/
case SOCK_STREAM:
case SOCK_SEQPACKET:
if ((so2 = unp_solock_peer(so)) == NULL)
break;
/*
* Adjust backpressure on sender
* and wakeup any waiting to write.
*/
so2->so_snd.sb_mbcnt = so->so_rcv.sb_mbcnt;
so2->so_snd.sb_cc = so->so_rcv.sb_cc;
sowwakeup(so2);
sounlock(so2);
break;
default:
panic("uipc 2");
}
return (0);
}
int
uipc_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct unpcb *unp = sotounpcb(so);
struct socket *so2;
int error = 0;
if (control) {
sounlock(so);
error = unp_internalize(control, curproc);
solock(so);
if (error)
goto out;
}
switch (so->so_type) {
case SOCK_DGRAM: {
const struct sockaddr *from;
if (nam) {
if (unp->unp_conn) {
error = EISCONN;
break;
}
error = unp_connect(so, nam, curproc);
if (error)
break;
}
if ((so2 = unp_solock_peer(so)) == NULL) {
if (nam != NULL)
error = ECONNREFUSED;
else
error = ENOTCONN;
break;
}
if (unp->unp_addr)
from = mtod(unp->unp_addr, struct sockaddr *);
else
from = &sun_noname;
if (sbappendaddr(so2, &so2->so_rcv, from, m, control)) {
sorwakeup(so2);
m = NULL;
control = NULL;
} else
error = ENOBUFS;
if (so2 != so)
sounlock(so2);
if (nam)
unp_disconnect(unp);
break;
}
case SOCK_STREAM:
case SOCK_SEQPACKET:
if (so->so_state & SS_CANTSENDMORE) {
error = EPIPE;
break;
}
if ((so2 = unp_solock_peer(so)) == NULL) {
error = ENOTCONN;
break;
}
/*
* Send to paired receive port, and then raise
* send buffer counts to maintain backpressure.
* Wake up readers.
*/
if (control) {
if (sbappendcontrol(so2, &so2->so_rcv, m, control)) {
control = NULL;
} else {
sounlock(so2);
error = ENOBUFS;
break;
}
} else if (so->so_type == SOCK_SEQPACKET)
sbappendrecord(so2, &so2->so_rcv, m);
else
sbappend(so2, &so2->so_rcv, m);
so->so_snd.sb_mbcnt = so2->so_rcv.sb_mbcnt;
so->so_snd.sb_cc = so2->so_rcv.sb_cc;
if (so2->so_rcv.sb_cc > 0)
sorwakeup(so2);
sounlock(so2);
m = NULL;
break;
default:
panic("uipc 4");
}
/* we need to undo unp_internalize in case of errors */
if (control && error)
unp_dispose(control);
out:
m_freem(control);
m_freem(m);
return (error);
}
int
uipc_abort(struct socket *so)
{
struct unpcb *unp = sotounpcb(so);
unp_detach(unp);
sofree(so, 0);
return (0);
}
int
uipc_sense(struct socket *so, struct stat *sb)
{
struct unpcb *unp = sotounpcb(so);
sb->st_blksize = so->so_snd.sb_hiwat;
sb->st_dev = NODEV;
mtx_enter(&unp_ino_mtx);
if (unp->unp_ino == 0)
unp->unp_ino = unp_ino++;
mtx_leave(&unp_ino_mtx);
sb->st_atim.tv_sec =
sb->st_mtim.tv_sec =
sb->st_ctim.tv_sec = unp->unp_ctime.tv_sec;
sb->st_atim.tv_nsec =
sb->st_mtim.tv_nsec =
sb->st_ctim.tv_nsec = unp->unp_ctime.tv_nsec;
sb->st_ino = unp->unp_ino;
return (0);
}
int
uipc_sockaddr(struct socket *so, struct mbuf *nam)
{
struct unpcb *unp = sotounpcb(so);
uipc_setaddr(unp, nam);
return (0);
}
int
uipc_peeraddr(struct socket *so, struct mbuf *nam)
{
struct unpcb *unp = sotounpcb(so);
struct socket *so2;
so2 = unp_solock_peer(so);
uipc_setaddr(unp->unp_conn, nam);
if (so2 != NULL && so2 != so)
sounlock(so2);
return (0);
}
int
uipc_connect2(struct socket *so, struct socket *so2)
{
struct unpcb *unp = sotounpcb(so), *unp2;
int error;
if ((error = unp_connect2(so, so2)))
return (error);
unp->unp_connid.uid = curproc->p_ucred->cr_uid;
unp->unp_connid.gid = curproc->p_ucred->cr_gid;
unp->unp_connid.pid = curproc->p_p->ps_pid;
unp->unp_flags |= UNP_FEIDS;
unp2 = sotounpcb(so2);
unp2->unp_connid.uid = curproc->p_ucred->cr_uid;
unp2->unp_connid.gid = curproc->p_ucred->cr_gid;
unp2->unp_connid.pid = curproc->p_p->ps_pid;
unp2->unp_flags |= UNP_FEIDS;
return (0);
}
int
uipc_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int *valp = &unp_defer;
/* All sysctl names at this level are terminal. */
switch (name[0]) {
case SOCK_STREAM:
if (namelen != 2)
return (ENOTDIR);
return sysctl_bounded_arr(unpstctl_vars, nitems(unpstctl_vars),
name + 1, namelen - 1, oldp, oldlenp, newp, newlen);
case SOCK_SEQPACKET:
if (namelen != 2)
return (ENOTDIR);
return sysctl_bounded_arr(unpsqctl_vars, nitems(unpsqctl_vars),
name + 1, namelen - 1, oldp, oldlenp, newp, newlen);
case SOCK_DGRAM:
if (namelen != 2)
return (ENOTDIR);
return sysctl_bounded_arr(unpdgctl_vars, nitems(unpdgctl_vars),
name + 1, namelen - 1, oldp, oldlenp, newp, newlen);
case NET_UNIX_INFLIGHT:
valp = &unp_rights;
/* FALLTHOUGH */
case NET_UNIX_DEFERRED:
if (namelen != 1)
return (ENOTDIR);
return sysctl_rdint(oldp, oldlenp, newp, *valp);
default:
return (ENOPROTOOPT);
}
}
void
unp_detach(struct unpcb *unp)
{
struct socket *so = unp->unp_socket;
struct vnode *vp = unp->unp_vnode;
struct unpcb *unp2;
unp->unp_vnode = NULL;
/*
* Enforce `unp_gc_lock' -> `solock()' lock order.
* Enforce `i_lock' -> `solock()' lock order.
*/
sounlock(so);
rw_enter_write(&unp_gc_lock);
LIST_REMOVE(unp, unp_link);
rw_exit_write(&unp_gc_lock);
if (vp != NULL) {
VOP_LOCK(vp, LK_EXCLUSIVE);
vp->v_socket = NULL;
KERNEL_LOCK();
vput(vp);
KERNEL_UNLOCK();
}
solock(so);
if (unp->unp_conn != NULL) {
/*
* Datagram socket could be connected to itself.
* Such socket will be disconnected here.
*/
unp_disconnect(unp);
}
while ((unp2 = SLIST_FIRST(&unp->unp_refs)) != NULL) {
struct socket *so2 = unp2->unp_socket;
if (so < so2)
solock(so2);
else {
unp_ref(unp2);
sounlock(so);
solock(so2);
solock(so);
if (unp2->unp_conn != unp) {
/* `unp2' was disconnected due to re-lock. */
sounlock(so2);
unp_rele(unp2);
continue;
}
unp_rele(unp2);
}
unp2->unp_conn = NULL;
SLIST_REMOVE(&unp->unp_refs, unp2, unpcb, unp_nextref);
so2->so_error = ECONNRESET;
so2->so_state &= ~SS_ISCONNECTED;
sounlock(so2);
}
sounlock(so);
refcnt_finalize(&unp->unp_refcnt, "unpfinal");
solock(so);
soisdisconnected(so);
so->so_pcb = NULL;
m_freem(unp->unp_addr);
pool_put(&unpcb_pool, unp);
if (unp_rights)
task_add(systqmp, &unp_gc_task);
}
int
unp_bind(struct unpcb *unp, struct mbuf *nam, struct proc *p)
{
struct sockaddr_un *soun;
struct mbuf *nam2;
struct vnode *vp;
struct vattr vattr;
int error;
struct nameidata nd;
size_t pathlen;
if (unp->unp_flags & (UNP_BINDING | UNP_CONNECTING))
return (EINVAL);
if (unp->unp_vnode != NULL)
return (EINVAL);
if ((error = unp_nam2sun(nam, &soun, &pathlen)))
return (error);
unp->unp_flags |= UNP_BINDING;
/*
* Enforce `i_lock' -> `unplock' because fifo subsystem
* requires it. The socket can't be closed concurrently
* because the file descriptor reference is still held.
*/
sounlock(unp->unp_socket);
nam2 = m_getclr(M_WAITOK, MT_SONAME);
nam2->m_len = sizeof(struct sockaddr_un);
memcpy(mtod(nam2, struct sockaddr_un *), soun,
offsetof(struct sockaddr_un, sun_path) + pathlen);
/* No need to NUL terminate: m_getclr() returns zero'd mbufs. */
soun = mtod(nam2, struct sockaddr_un *);
/* Fixup sun_len to keep it in sync with m_len. */
soun->sun_len = nam2->m_len;
NDINIT(&nd, CREATE, NOFOLLOW | LOCKPARENT, UIO_SYSSPACE,
soun->sun_path, p);
nd.ni_pledge = PLEDGE_UNIX;
KERNEL_LOCK();
/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
error = namei(&nd);
if (error != 0) {
m_freem(nam2);
solock(unp->unp_socket);
goto out;
}
vp = nd.ni_vp;
if (vp != NULL) {
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
if (nd.ni_dvp == vp)
vrele(nd.ni_dvp);
else
vput(nd.ni_dvp);
vrele(vp);
m_freem(nam2);
error = EADDRINUSE;
solock(unp->unp_socket);
goto out;
}
VATTR_NULL(&vattr);
vattr.va_type = VSOCK;
vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
vput(nd.ni_dvp);
if (error) {
m_freem(nam2);
solock(unp->unp_socket);
goto out;
}
solock(unp->unp_socket);
unp->unp_addr = nam2;
vp = nd.ni_vp;
vp->v_socket = unp->unp_socket;
unp->unp_vnode = vp;
unp->unp_connid.uid = p->p_ucred->cr_uid;
unp->unp_connid.gid = p->p_ucred->cr_gid;
unp->unp_connid.pid = p->p_p->ps_pid;
unp->unp_flags |= UNP_FEIDSBIND;
VOP_UNLOCK(vp);
out:
KERNEL_UNLOCK();
unp->unp_flags &= ~UNP_BINDING;
return (error);
}
int
unp_connect(struct socket *so, struct mbuf *nam, struct proc *p)
{
struct sockaddr_un *soun;
struct vnode *vp;
struct socket *so2, *so3;
struct unpcb *unp, *unp2, *unp3;
struct nameidata nd;
int error;
unp = sotounpcb(so);
if (unp->unp_flags & (UNP_BINDING | UNP_CONNECTING))
return (EISCONN);
if ((error = unp_nam2sun(nam, &soun, NULL)))
return (error);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, soun->sun_path, p);
nd.ni_pledge = PLEDGE_UNIX;
unp->unp_flags |= UNP_CONNECTING;
/*
* Enforce `i_lock' -> `unplock' because fifo subsystem
* requires it. The socket can't be closed concurrently
* because the file descriptor reference is still held.
*/
sounlock(so);
KERNEL_LOCK();
error = namei(&nd);
if (error != 0)
goto unlock;
vp = nd.ni_vp;
if (vp->v_type != VSOCK) {
error = ENOTSOCK;
goto put;
}
if ((error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) != 0)
goto put;
so2 = vp->v_socket;
if (so2 == NULL) {
error = ECONNREFUSED;
goto put;
}
if (so->so_type != so2->so_type) {
error = EPROTOTYPE;
goto put;
}
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
solock(so2);
if ((so2->so_options & SO_ACCEPTCONN) == 0 ||
(so3 = sonewconn(so2, 0)) == NULL) {
error = ECONNREFUSED;
}
sounlock(so2);
if (error != 0)
goto put;
/*
* Since `so2' is protected by vnode(9) lock, `so3'
* can't be PRU_ABORT'ed here.
*/
solock_pair(so, so3);
unp2 = sotounpcb(so2);
unp3 = sotounpcb(so3);
/*
* `unp_addr', `unp_connid' and 'UNP_FEIDSBIND' flag
* are immutable since we set them in unp_bind().
*/
if (unp2->unp_addr)
unp3->unp_addr =
m_copym(unp2->unp_addr, 0, M_COPYALL, M_NOWAIT);
unp3->unp_connid.uid = p->p_ucred->cr_uid;
unp3->unp_connid.gid = p->p_ucred->cr_gid;
unp3->unp_connid.pid = p->p_p->ps_pid;
unp3->unp_flags |= UNP_FEIDS;
if (unp2->unp_flags & UNP_FEIDSBIND) {
unp->unp_connid = unp2->unp_connid;
unp->unp_flags |= UNP_FEIDS;
}
so2 = so3;
} else {
if (so2 != so)
solock_pair(so, so2);
else
solock(so);
}
error = unp_connect2(so, so2);
sounlock(so);
/*
* `so2' can't be PRU_ABORT'ed concurrently
*/
if (so2 != so)
sounlock(so2);
put:
vput(vp);
unlock:
KERNEL_UNLOCK();
solock(so);
unp->unp_flags &= ~UNP_CONNECTING;
/*
* The peer socket could be closed by concurrent thread
* when `so' and `vp' are unlocked.
*/
if (error == 0 && unp->unp_conn == NULL)
error = ECONNREFUSED;
return (error);
}
int
unp_connect2(struct socket *so, struct socket *so2)
{
struct unpcb *unp = sotounpcb(so);
struct unpcb *unp2;
soassertlocked(so);
soassertlocked(so2);
if (so2->so_type != so->so_type)
return (EPROTOTYPE);
unp2 = sotounpcb(so2);
unp->unp_conn = unp2;
switch (so->so_type) {
case SOCK_DGRAM:
SLIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_nextref);
soisconnected(so);
break;
case SOCK_STREAM:
case SOCK_SEQPACKET:
unp2->unp_conn = unp;
soisconnected(so);
soisconnected(so2);
break;
default:
panic("unp_connect2");
}
return (0);
}
void
unp_disconnect(struct unpcb *unp)
{
struct socket *so2;
struct unpcb *unp2;
if ((so2 = unp_solock_peer(unp->unp_socket)) == NULL)
return;
unp2 = unp->unp_conn;
unp->unp_conn = NULL;
switch (unp->unp_socket->so_type) {
case SOCK_DGRAM:
SLIST_REMOVE(&unp2->unp_refs, unp, unpcb, unp_nextref);
unp->unp_socket->so_state &= ~SS_ISCONNECTED;
break;
case SOCK_STREAM:
case SOCK_SEQPACKET:
unp->unp_socket->so_snd.sb_mbcnt = 0;
unp->unp_socket->so_snd.sb_cc = 0;
soisdisconnected(unp->unp_socket);
unp2->unp_conn = NULL;
unp2->unp_socket->so_snd.sb_mbcnt = 0;
unp2->unp_socket->so_snd.sb_cc = 0;
soisdisconnected(unp2->unp_socket);
break;
}
if (so2 != unp->unp_socket)
sounlock(so2);
}
void
unp_shutdown(struct unpcb *unp)
{
struct socket *so2;
switch (unp->unp_socket->so_type) {
case SOCK_STREAM:
case SOCK_SEQPACKET:
if ((so2 = unp_solock_peer(unp->unp_socket)) == NULL)
break;
socantrcvmore(so2);
sounlock(so2);
break;
default:
break;
}
}
#ifdef notdef
unp_drain(void)
{
}
#endif
static struct unpcb *
fptounp(struct file *fp)
{
struct socket *so;
if (fp->f_type != DTYPE_SOCKET)
return (NULL);
if ((so = fp->f_data) == NULL)
return (NULL);
if (so->so_proto->pr_domain != &unixdomain)
return (NULL);
return (sotounpcb(so));
}
int
unp_externalize(struct mbuf *rights, socklen_t controllen, int flags)
{
struct proc *p = curproc; /* XXX */
struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
struct filedesc *fdp = p->p_fd;
int i, *fds = NULL;
struct fdpass *rp;
struct file *fp;
int nfds, error = 0;
/*
* This code only works because SCM_RIGHTS is the only supported
* control message type on unix sockets. Enforce this here.
*/
if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET)
return EINVAL;
nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) /
sizeof(struct fdpass);
if (controllen < CMSG_ALIGN(sizeof(struct cmsghdr)))
controllen = 0;
else
controllen -= CMSG_ALIGN(sizeof(struct cmsghdr));
if (nfds > controllen / sizeof(int)) {
error = EMSGSIZE;
goto out;
}
/* Make sure the recipient should be able to see the descriptors.. */
rp = (struct fdpass *)CMSG_DATA(cm);
/* fdp->fd_rdir requires KERNEL_LOCK() */
KERNEL_LOCK();
for (i = 0; i < nfds; i++) {
fp = rp->fp;
rp++;
error = pledge_recvfd(p, fp);
if (error)
break;
/*
* No to block devices. If passing a directory,
* make sure that it is underneath the root.
*/
if (fdp->fd_rdir != NULL && fp->f_type == DTYPE_VNODE) {
struct vnode *vp = (struct vnode *)fp->f_data;
if (vp->v_type == VBLK ||
(vp->v_type == VDIR &&
!vn_isunder(vp, fdp->fd_rdir, p))) {
error = EPERM;
break;
}
}
}
KERNEL_UNLOCK();
if (error)
goto out;
fds = mallocarray(nfds, sizeof(int), M_TEMP, M_WAITOK);
fdplock(fdp);
restart:
/*
* First loop -- allocate file descriptor table slots for the
* new descriptors.
*/
rp = ((struct fdpass *)CMSG_DATA(cm));
for (i = 0; i < nfds; i++) {
if ((error = fdalloc(p, 0, &fds[i])) != 0) {
/*
* Back out what we've done so far.
*/
for (--i; i >= 0; i--)
fdremove(fdp, fds[i]);
if (error == ENOSPC) {
fdexpand(p);
goto restart;
}
fdpunlock(fdp);
/*
* This is the error that has historically
* been returned, and some callers may
* expect it.
*/
error = EMSGSIZE;
goto out;
}
/*
* Make the slot reference the descriptor so that
* fdalloc() works properly.. We finalize it all
* in the loop below.
*/
mtx_enter(&fdp->fd_fplock);
KASSERT(fdp->fd_ofiles[fds[i]] == NULL);
fdp->fd_ofiles[fds[i]] = rp->fp;
mtx_leave(&fdp->fd_fplock);
fdp->fd_ofileflags[fds[i]] = (rp->flags & UF_PLEDGED);
if (flags & MSG_CMSG_CLOEXEC)
fdp->fd_ofileflags[fds[i]] |= UF_EXCLOSE;
rp++;
}
/*
* Keep `fdp' locked to prevent concurrent close() of just
* inserted descriptors. Such descriptors could have the only
* `f_count' reference which is now shared between control
* message and `fdp'.
*/
/*
* Now that adding them has succeeded, update all of the
* descriptor passing state.
*/
rp = (struct fdpass *)CMSG_DATA(cm);
for (i = 0; i < nfds; i++) {
struct unpcb *unp;
fp = rp->fp;
rp++;
if ((unp = fptounp(fp)) != NULL) {
rw_enter_write(&unp_gc_lock);
unp->unp_msgcount--;
rw_exit_write(&unp_gc_lock);
}
}
fdpunlock(fdp);
mtx_enter(&unp_rights_mtx);
unp_rights -= nfds;
mtx_leave(&unp_rights_mtx);
/*
* Copy temporary array to message and adjust length, in case of
* transition from large struct file pointers to ints.
*/
memcpy(CMSG_DATA(cm), fds, nfds * sizeof(int));
cm->cmsg_len = CMSG_LEN(nfds * sizeof(int));
rights->m_len = CMSG_LEN(nfds * sizeof(int));
out:
if (fds != NULL)
free(fds, M_TEMP, nfds * sizeof(int));
if (error) {
if (nfds > 0) {
/*
* No lock required. We are the only `cm' holder.
*/
rp = ((struct fdpass *)CMSG_DATA(cm));
unp_discard(rp, nfds);
}
}
return (error);
}
int
unp_internalize(struct mbuf *control, struct proc *p)
{
struct filedesc *fdp = p->p_fd;
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
struct fdpass *rp;
struct file *fp;
struct unpcb *unp;
int i, error;
int nfds, *ip, fd, neededspace;
/*
* Check for two potential msg_controllen values because
* IETF stuck their nose in a place it does not belong.
*/
if (control->m_len < CMSG_LEN(0) || cm->cmsg_len < CMSG_LEN(0))
return (EINVAL);
if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
!(cm->cmsg_len == control->m_len ||
control->m_len == CMSG_ALIGN(cm->cmsg_len)))
return (EINVAL);
nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) / sizeof (int);
mtx_enter(&unp_rights_mtx);
if (unp_rights + nfds > maxfiles / 10) {
mtx_leave(&unp_rights_mtx);
return (EMFILE);
}
unp_rights += nfds;
mtx_leave(&unp_rights_mtx);
/* Make sure we have room for the struct file pointers */
morespace:
neededspace = CMSG_SPACE(nfds * sizeof(struct fdpass)) -
control->m_len;
if (neededspace > m_trailingspace(control)) {
char *tmp;
/* if we already have a cluster, the message is just too big */
if (control->m_flags & M_EXT) {
error = E2BIG;
goto nospace;
}
/* copy cmsg data temporarily out of the mbuf */
tmp = malloc(control->m_len, M_TEMP, M_WAITOK);
memcpy(tmp, mtod(control, caddr_t), control->m_len);
/* allocate a cluster and try again */
MCLGET(control, M_WAIT);
if ((control->m_flags & M_EXT) == 0) {
free(tmp, M_TEMP, control->m_len);
error = ENOBUFS; /* allocation failed */
goto nospace;
}
/* copy the data back into the cluster */
cm = mtod(control, struct cmsghdr *);
memcpy(cm, tmp, control->m_len);
free(tmp, M_TEMP, control->m_len);
goto morespace;
}
/* adjust message & mbuf to note amount of space actually used. */
cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct fdpass));
control->m_len = CMSG_SPACE(nfds * sizeof(struct fdpass));
ip = ((int *)CMSG_DATA(cm)) + nfds - 1;
rp = ((struct fdpass *)CMSG_DATA(cm)) + nfds - 1;
fdplock(fdp);
for (i = 0; i < nfds; i++) {
memcpy(&fd, ip, sizeof fd);
ip--;
if ((fp = fd_getfile(fdp, fd)) == NULL) {
error = EBADF;
goto fail;
}
if (fp->f_count >= FDUP_MAX_COUNT) {
error = EDEADLK;
goto fail;
}
error = pledge_sendfd(p, fp);
if (error)
goto fail;
/* kqueue descriptors cannot be copied */
if (fp->f_type == DTYPE_KQUEUE) {
error = EINVAL;
goto fail;
}
#if NKCOV > 0
/* kcov descriptors cannot be copied */
if (fp->f_type == DTYPE_VNODE && kcov_vnode(fp->f_data)) {
error = EINVAL;
goto fail;
}
#endif
rp->fp = fp;
rp->flags = fdp->fd_ofileflags[fd] & UF_PLEDGED;
rp--;
if ((unp = fptounp(fp)) != NULL) {
rw_enter_write(&unp_gc_lock);
unp->unp_msgcount++;
unp->unp_file = fp;
rw_exit_write(&unp_gc_lock);
}
}
fdpunlock(fdp);
return (0);
fail:
fdpunlock(fdp);
if (fp != NULL)
FRELE(fp, p);
/* Back out what we just did. */
for ( ; i > 0; i--) {
rp++;
fp = rp->fp;
if ((unp = fptounp(fp)) != NULL) {
rw_enter_write(&unp_gc_lock);
unp->unp_msgcount--;
rw_exit_write(&unp_gc_lock);
}
FRELE(fp, p);
}
nospace:
mtx_enter(&unp_rights_mtx);
unp_rights -= nfds;
mtx_leave(&unp_rights_mtx);
return (error);
}
void
unp_gc(void *arg __unused)
{
struct unp_deferral *defer;
struct file *fp;
struct socket *so;
struct unpcb *unp;
int nunref, i;
rw_enter_write(&unp_gc_lock);
if (unp_gcing)
goto unlock;
unp_gcing = 1;
rw_exit_write(&unp_gc_lock);
rw_enter_write(&unp_df_lock);
/* close any fds on the deferred list */
while ((defer = SLIST_FIRST(&unp_deferred)) != NULL) {
SLIST_REMOVE_HEAD(&unp_deferred, ud_link);
rw_exit_write(&unp_df_lock);
for (i = 0; i < defer->ud_n; i++) {
fp = defer->ud_fp[i].fp;
if (fp == NULL)
continue;
if ((unp = fptounp(fp)) != NULL) {
rw_enter_write(&unp_gc_lock);
unp->unp_msgcount--;
rw_exit_write(&unp_gc_lock);
}
mtx_enter(&unp_rights_mtx);
unp_rights--;
mtx_leave(&unp_rights_mtx);
/* closef() expects a refcount of 2 */
FREF(fp);
(void) closef(fp, NULL);
}
free(defer, M_TEMP, sizeof(*defer) +
sizeof(struct fdpass) * defer->ud_n);
rw_enter_write(&unp_df_lock);
}
rw_exit_write(&unp_df_lock);
nunref = 0;
rw_enter_write(&unp_gc_lock);
/*
* Determine sockets which may be prospectively dead. Such
* sockets have their `unp_msgcount' equal to the `f_count'.
* If `unp_msgcount' is 0, the socket has not been passed
* and can't be unreferenced.
*/
LIST_FOREACH(unp, &unp_head, unp_link) {
unp->unp_gcflags = 0;
if (unp->unp_msgcount == 0)
continue;
if ((fp = unp->unp_file) == NULL)
continue;
if (fp->f_count == unp->unp_msgcount) {
unp->unp_gcflags |= UNP_GCDEAD;
unp->unp_gcrefs = unp->unp_msgcount;
nunref++;
}
}
/*
* Scan all sockets previously marked as dead. Remove
* the `unp_gcrefs' reference each socket holds on any
* dead socket in its buffer.
*/
LIST_FOREACH(unp, &unp_head, unp_link) {
if ((unp->unp_gcflags & UNP_GCDEAD) == 0)
continue;
so = unp->unp_socket;
solock(so);
unp_scan(so->so_rcv.sb_mb, unp_remove_gcrefs);
sounlock(so);
}
/*
* If the dead socket has `unp_gcrefs' reference counter
* greater than 0, it can't be unreferenced. Mark it as
* alive and increment the `unp_gcrefs' reference for each
* dead socket within its buffer. Repeat this until we
* have no new alive sockets found.
*/
do {
unp_defer = 0;
LIST_FOREACH(unp, &unp_head, unp_link) {
if ((unp->unp_gcflags & UNP_GCDEAD) == 0)
continue;
if (unp->unp_gcrefs == 0)
continue;
unp->unp_gcflags &= ~UNP_GCDEAD;
so = unp->unp_socket;
solock(so);
unp_scan(so->so_rcv.sb_mb, unp_restore_gcrefs);
sounlock(so);
KASSERT(nunref > 0);
nunref--;
}
} while (unp_defer > 0);
/*
* If there are any unreferenced sockets, then for each dispose
* of files in its receive buffer and then close it.
*/
if (nunref) {
LIST_FOREACH(unp, &unp_head, unp_link) {
if (unp->unp_gcflags & UNP_GCDEAD) {
/*
* This socket could still be connected
* and if so it's `so_rcv' is still
* accessible by concurrent PRU_SEND
* thread.
*/
so = unp->unp_socket;
solock(so);
unp_scan(so->so_rcv.sb_mb, unp_discard);
sounlock(so);
}
}
}
unp_gcing = 0;
unlock:
rw_exit_write(&unp_gc_lock);
}
void
unp_dispose(struct mbuf *m)
{
if (m)
unp_scan(m, unp_discard);
}
void
unp_scan(struct mbuf *m0, void (*op)(struct fdpass *, int))
{
struct mbuf *m;
struct fdpass *rp;
struct cmsghdr *cm;
int qfds;
while (m0) {
for (m = m0; m; m = m->m_next) {
if (m->m_type == MT_CONTROL &&
m->m_len >= sizeof(*cm)) {
cm = mtod(m, struct cmsghdr *);
if (cm->cmsg_level != SOL_SOCKET ||
cm->cmsg_type != SCM_RIGHTS)
continue;
qfds = (cm->cmsg_len - CMSG_ALIGN(sizeof *cm))
/ sizeof(struct fdpass);
if (qfds > 0) {
rp = (struct fdpass *)CMSG_DATA(cm);
op(rp, qfds);
}
break; /* XXX, but saves time */
}
}
m0 = m0->m_nextpkt;
}
}
void
unp_discard(struct fdpass *rp, int nfds)
{
struct unp_deferral *defer;
/* copy the file pointers to a deferral structure */
defer = malloc(sizeof(*defer) + sizeof(*rp) * nfds, M_TEMP, M_WAITOK);
defer->ud_n = nfds;
memcpy(&defer->ud_fp[0], rp, sizeof(*rp) * nfds);
memset(rp, 0, sizeof(*rp) * nfds);
rw_enter_write(&unp_df_lock);
SLIST_INSERT_HEAD(&unp_deferred, defer, ud_link);
rw_exit_write(&unp_df_lock);
task_add(systqmp, &unp_gc_task);
}
void
unp_remove_gcrefs(struct fdpass *rp, int nfds)
{
struct unpcb *unp;
int i;
rw_assert_wrlock(&unp_gc_lock);
for (i = 0; i < nfds; i++) {
if (rp[i].fp == NULL)
continue;
if ((unp = fptounp(rp[i].fp)) == NULL)
continue;
if (unp->unp_gcflags & UNP_GCDEAD) {
KASSERT(unp->unp_gcrefs > 0);
unp->unp_gcrefs--;
}
}
}
void
unp_restore_gcrefs(struct fdpass *rp, int nfds)
{
struct unpcb *unp;
int i;
rw_assert_wrlock(&unp_gc_lock);
for (i = 0; i < nfds; i++) {
if (rp[i].fp == NULL)
continue;
if ((unp = fptounp(rp[i].fp)) == NULL)
continue;
if (unp->unp_gcflags & UNP_GCDEAD) {
unp->unp_gcrefs++;
unp_defer++;
}
}
}
int
unp_nam2sun(struct mbuf *nam, struct sockaddr_un **sun, size_t *pathlen)
{
struct sockaddr *sa = mtod(nam, struct sockaddr *);
size_t size, len;
if (nam->m_len < offsetof(struct sockaddr, sa_data))
return EINVAL;
if (sa->sa_family != AF_UNIX)
return EAFNOSUPPORT;
if (sa->sa_len != nam->m_len)
return EINVAL;
if (sa->sa_len > sizeof(struct sockaddr_un))
return EINVAL;
*sun = (struct sockaddr_un *)sa;
/* ensure that sun_path is NUL terminated and fits */
size = (*sun)->sun_len - offsetof(struct sockaddr_un, sun_path);
len = strnlen((*sun)->sun_path, size);
if (len == sizeof((*sun)->sun_path))
return EINVAL;
if (len == size) {
if (m_trailingspace(nam) == 0)
return EINVAL;
nam->m_len++;
(*sun)->sun_len++;
(*sun)->sun_path[len] = '\0';
}
if (pathlen != NULL)
*pathlen = len;
return 0;
}
88
51
44
35
35
35
219
6
212
182
32
212
3
3
214
8
206
76
31
10
42
27
171
8
9
41
70
57
28
30
131
22
21
109
31
14
92
194
62
59
98
35
47
58
62
110
15
59
27
26
38
18
53
17
15
30
35
11
32
158
5
10
13
2
5
2
2
156
151
149
155
12
154
62
211
211
98
2
1
32
24
26
13
54
46
31
115
19
19
7
114
111
65
76
82
36
199
198
128
78
2
1
8
10
2
2
5
3
2
4
2
2
1
4
2
5
3
2
1
3
17
2
22
21
14
6
34
4
27
5
35
19
10
14
2
2
10
17
2
1
4
2
2
3
18
3
1
2
4
3
3
3
3
3
3
41
3
18
14
4
2
14
6
22
16
7
6
18
1
13
6
20
15
5
30
21
17
14
88
76
21
17
2
18
45
113
5
107
107
74
110
45
96
95
2
5
16
2
75
18
248
54
12
45
44
1
1
6
15
117
117
47
43
47
6
5
3
2
1
1
5
8
21
30
13
3
25
1
24
6
14
4
1
2
1
4
17
6
14
37
8
121
121
111
4
2
16
40
39
1
1
3
39
36
3
10
5
2
4
31
32
12
22
24
3
31
8
34
1
1
17
94
14
1
1
38
46
15
23
1
18
18
9
15
14
63
63
63
63
191
18
191
163
31
75
85
33
7
25
2
19
260
12
45
3
3
20
15
3
3
3
3
3
3
18
7
11
16
16
6
31
28
4
4
1
1
1
1
1
1
9
2
1
1
1
1
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
/* $OpenBSD: tty.c,v 1.176 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: tty.c,v 1.68.4.2 1996/06/06 16:04:52 thorpej Exp $ */
/*-
* Copyright (c) 1982, 1986, 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tty.c 8.8 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#define TTYDEFCHARS
#include <sys/tty.h>
#undef TTYDEFCHARS
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/msgbuf.h>
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
#include <sys/unistd.h>
#include <sys/pledge.h>
#include <sys/namei.h>
#include <uvm/uvm_extern.h>
#include <dev/cons.h>
#include "pty.h"
static int ttnread(struct tty *);
static void ttyblock(struct tty *);
void ttyunblock(struct tty *);
static void ttyecho(int, struct tty *);
static void ttyrubo(struct tty *, int);
int filt_ttyread(struct knote *kn, long hint);
void filt_ttyrdetach(struct knote *kn);
int filt_ttywrite(struct knote *kn, long hint);
void filt_ttywdetach(struct knote *kn);
int filt_ttyexcept(struct knote *kn, long hint);
void ttystats_init(struct itty **, int *, size_t *);
int ttywait_nsec(struct tty *, uint64_t);
int ttysleep_nsec(struct tty *, void *, int, char *, uint64_t);
/* Symbolic sleep message strings. */
char ttclos[] = "ttycls";
char ttopen[] = "ttyopn";
char ttybg[] = "ttybg";
char ttyin[] = "ttyin";
char ttyout[] = "ttyout";
/*
* Table with character classes and parity. The 8th bit indicates parity,
* the 7th bit indicates the character is an alphameric or underscore (for
* ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits
* are 0 then the character needs no special processing on output; classes
* other than 0 might be translated or (not currently) require delays.
*/
#define E 0x00 /* Even parity. */
#define O 0x80 /* Odd parity. */
#define PARITY(c) (char_type[c] & O)
#define ALPHA 0x40 /* Alpha or underscore. */
#define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA)
#define CCLASSMASK 0x3f
#define CCLASS(c) (char_type[c] & CCLASSMASK)
#define BS BACKSPACE
#define CC CONTROL
#define CR RETURN
#define NA ORDINARY | ALPHA
#define NL NEWLINE
#define NO ORDINARY
#define TB TAB
#define VT VTAB
u_char const char_type[] = {
E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */
O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */
O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */
E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */
O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */
E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */
E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */
O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */
O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */
E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */
E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */
O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */
E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */
O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */
O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */
E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */
/*
* Meta chars; should be settable per character set;
* for now, treat them all as normal characters.
*/
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
NA, NA, NA, NA, NA, NA, NA, NA,
};
#undef BS
#undef CC
#undef CR
#undef NA
#undef NL
#undef NO
#undef TB
#undef VT
#define islower(c) ((c) >= 'a' && (c) <= 'z')
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
#define tolower(c) ((c) - 'A' + 'a')
#define toupper(c) ((c) - 'a' + 'A')
struct ttylist_head ttylist; /* TAILQ_HEAD */
int tty_count;
struct rwlock ttylist_lock = RWLOCK_INITIALIZER("ttylist");
int64_t tk_cancc, tk_nin, tk_nout, tk_rawcc;
/*
* Initial open of tty, or (re)entry to standard tty line discipline.
*/
int
ttyopen(dev_t device, struct tty *tp, struct proc *p)
{
int s;
s = spltty();
tp->t_dev = device;
if (!ISSET(tp->t_state, TS_ISOPEN)) {
SET(tp->t_state, TS_ISOPEN);
memset(&tp->t_winsize, 0, sizeof(tp->t_winsize));
tp->t_column = 0;
}
CLR(tp->t_state, TS_WOPEN);
splx(s);
return (0);
}
/*
* Handle close() on a tty line: flush and set to initial state,
* bumping generation number so that pending read/write calls
* can detect recycling of the tty.
*/
int
ttyclose(struct tty *tp)
{
if (constty == tp)
constty = NULL;
ttyflush(tp, FREAD | FWRITE);
tp->t_gen++;
tp->t_pgrp = NULL;
if (tp->t_session)
SESSRELE(tp->t_session);
tp->t_session = NULL;
tp->t_state = 0;
return (0);
}
#define FLUSHQ(q) { \
if ((q)->c_cc) \
ndflush(q, (q)->c_cc); \
}
/* Is 'c' a line delimiter ("break" character)? */
#define TTBREAKC(c, lflag) \
((c) == '\n' || (((c) == cc[VEOF] || (c) == cc[VEOL] || \
((c) == cc[VEOL2] && (lflag & IEXTEN))) && (c) != _POSIX_VDISABLE))
/*
* Process input of a single character received on a tty. Returns 0 normally,
* 1 if a costly operation was reached.
*/
int
ttyinput(int c, struct tty *tp)
{
int iflag, lflag;
u_char *cc;
int i, error, ret = 0;
int s;
enqueue_randomness(tp->t_dev << 8 | c);
/*
* If receiver is not enabled, drop it.
*/
if (!ISSET(tp->t_cflag, CREAD))
return (0);
/*
* If input is pending take it first.
*/
lflag = tp->t_lflag;
s = spltty();
if (ISSET(lflag, PENDIN))
ttypend(tp);
splx(s);
/*
* Gather stats.
*/
if (ISSET(lflag, ICANON)) {
++tk_cancc;
++tp->t_cancc;
} else {
++tk_rawcc;
++tp->t_rawcc;
}
++tk_nin;
/* Handle exceptional conditions (break, parity, framing). */
cc = tp->t_cc;
iflag = tp->t_iflag;
if ((error = (ISSET(c, TTY_ERRORMASK))) != 0) {
CLR(c, TTY_ERRORMASK);
if (ISSET(error, TTY_FE) && !c) { /* Break. */
if (ISSET(iflag, IGNBRK))
return (0);
ttyflush(tp, FREAD | FWRITE);
if (ISSET(iflag, BRKINT)) {
pgsignal(tp->t_pgrp, SIGINT, 1);
goto endcase;
}
else if (ISSET(iflag, PARMRK))
goto parmrk;
} else if ((ISSET(error, TTY_PE) && ISSET(iflag, INPCK)) ||
ISSET(error, TTY_FE)) {
if (ISSET(iflag, IGNPAR))
goto endcase;
else if (ISSET(iflag, PARMRK)) {
parmrk: (void)putc(0377 | TTY_QUOTE, &tp->t_rawq);
if (ISSET(iflag, ISTRIP) || c != 0377)
(void)putc(0 | TTY_QUOTE, &tp->t_rawq);
(void)putc(c | TTY_QUOTE, &tp->t_rawq);
goto endcase;
} else
c = 0;
}
}
if (c == 0377 && !ISSET(iflag, ISTRIP) && ISSET(iflag, PARMRK))
goto parmrk;
/*
* In tandem mode, check high water mark.
*/
if (ISSET(iflag, IXOFF) || ISSET(tp->t_cflag, CHWFLOW))
ttyblock(tp);
if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP))
CLR(c, 0x80);
if (!ISSET(lflag, EXTPROC)) {
/*
* Check for literal nexting very first
*/
if (ISSET(tp->t_state, TS_LNCH)) {
SET(c, TTY_QUOTE);
CLR(tp->t_state, TS_LNCH);
}
/*
* Scan for special characters. This code
* is really just a big case statement with
* non-constant cases. The bottom of the
* case statement is labeled ``endcase'', so goto
* it after a case match, or similar.
*/
/*
* Control chars which aren't controlled
* by ICANON, ISIG, or IXON.
*/
if (ISSET(lflag, IEXTEN)) {
if (CCEQ(cc[VLNEXT], c)) {
if (ISSET(lflag, ECHO)) {
if (ISSET(lflag, ECHOE)) {
(void)ttyoutput('^', tp);
(void)ttyoutput('\b', tp);
} else
ttyecho(c, tp);
}
SET(tp->t_state, TS_LNCH);
goto endcase;
}
if (CCEQ(cc[VDISCARD], c)) {
if (ISSET(lflag, FLUSHO))
CLR(tp->t_lflag, FLUSHO);
else {
ttyflush(tp, FWRITE);
ttyecho(c, tp);
if (tp->t_rawq.c_cc + tp->t_canq.c_cc)
ret = ttyretype(tp);
SET(tp->t_lflag, FLUSHO);
}
goto startoutput;
}
}
/*
* Signals.
*/
if (ISSET(lflag, ISIG)) {
if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) {
if (!ISSET(lflag, NOFLSH))
ttyflush(tp, FREAD | FWRITE);
ttyecho(c, tp);
pgsignal(tp->t_pgrp,
CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1);
goto endcase;
}
if (CCEQ(cc[VSUSP], c)) {
if (!ISSET(lflag, NOFLSH))
ttyflush(tp, FREAD);
ttyecho(c, tp);
pgsignal(tp->t_pgrp, SIGTSTP, 1);
goto endcase;
}
}
/*
* Handle start/stop characters.
*/
if (ISSET(iflag, IXON)) {
if (CCEQ(cc[VSTOP], c)) {
if (!ISSET(tp->t_state, TS_TTSTOP)) {
SET(tp->t_state, TS_TTSTOP);
(*cdevsw[major(tp->t_dev)].d_stop)(tp,
0);
return (0);
}
if (!CCEQ(cc[VSTART], c))
return (0);
/*
* if VSTART == VSTOP then toggle
*/
goto endcase;
}
if (CCEQ(cc[VSTART], c))
goto restartoutput;
}
/*
* IGNCR, ICRNL, & INLCR
*/
if (c == '\r') {
if (ISSET(iflag, IGNCR))
goto endcase;
else if (ISSET(iflag, ICRNL))
c = '\n';
} else if (c == '\n' && ISSET(iflag, INLCR))
c = '\r';
}
if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) {
/*
* From here on down canonical mode character
* processing takes place.
*/
/*
* upper case or specials with IUCLC and XCASE
*/
if (ISSET(lflag, XCASE) && ISSET(iflag, IUCLC)) {
if (ISSET(tp->t_state, TS_BKSL)) {
CLR(tp->t_state, TS_BKSL);
switch (c) {
case '\'':
c = '`';
break;
case '!':
c = '|';
break;
case '^':
c = '~';
break;
case '(':
c = '{';
break;
case ')':
c = '}';
break;
}
}
else if (c == '\\') {
SET(tp->t_state, TS_BKSL);
goto endcase;
}
else if (isupper(c))
c = tolower(c);
}
else if (ISSET(iflag, IUCLC) && isupper(c))
c = tolower(c);
/*
* erase (^H / ^?)
*/
if (CCEQ(cc[VERASE], c)) {
if (tp->t_rawq.c_cc)
ret = ttyrub(unputc(&tp->t_rawq), tp);
goto endcase;
}
/*
* kill (^U)
*/
if (CCEQ(cc[VKILL], c)) {
if (ISSET(lflag, ECHOKE) &&
tp->t_rawq.c_cc == tp->t_rocount &&
!ISSET(lflag, ECHOPRT)) {
while (tp->t_rawq.c_cc)
if (ttyrub(unputc(&tp->t_rawq), tp))
ret = 1;
} else {
ttyecho(c, tp);
if (ISSET(lflag, ECHOK) ||
ISSET(lflag, ECHOKE))
ttyecho('\n', tp);
FLUSHQ(&tp->t_rawq);
tp->t_rocount = 0;
}
CLR(tp->t_state, TS_LOCAL);
goto endcase;
}
/*
* word erase (^W)
*/
if (CCEQ(cc[VWERASE], c) && ISSET(lflag, IEXTEN)) {
int alt = ISSET(lflag, ALTWERASE);
int ctype;
/*
* erase whitespace
*/
while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t')
if (ttyrub(c, tp))
ret = 1;
if (c == -1)
goto endcase;
/*
* erase last char of word and remember the
* next chars type (for ALTWERASE)
*/
if (ttyrub(c, tp))
ret = 1;
c = unputc(&tp->t_rawq);
if (c == -1)
goto endcase;
if (c == ' ' || c == '\t') {
(void)putc(c, &tp->t_rawq);
goto endcase;
}
ctype = ISALPHA(c);
/*
* erase rest of word
*/
do {
if (ttyrub(c, tp))
ret = 1;
c = unputc(&tp->t_rawq);
if (c == -1)
goto endcase;
} while (c != ' ' && c != '\t' &&
(alt == 0 || ISALPHA(c) == ctype));
(void)putc(c, &tp->t_rawq);
goto endcase;
}
/*
* reprint line (^R)
*/
if (CCEQ(cc[VREPRINT], c) && ISSET(lflag, IEXTEN)) {
ret = ttyretype(tp);
goto endcase;
}
/*
* ^T - kernel info and generate SIGINFO
*/
if (CCEQ(cc[VSTATUS], c) && ISSET(lflag, IEXTEN)) {
if (ISSET(lflag, ISIG))
pgsignal(tp->t_pgrp, SIGINFO, 1);
if (!ISSET(lflag, NOKERNINFO))
ttyinfo(tp);
goto endcase;
}
}
/*
* Check for input buffer overflow
*/
if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG(tp)) {
if (ISSET(iflag, IMAXBEL)) {
if (tp->t_outq.c_cc < tp->t_hiwat)
(void)ttyoutput(CTRL('g'), tp);
} else
ttyflush(tp, FREAD | FWRITE);
goto endcase;
}
/*
* Put data char in q for user and
* wakeup on seeing a line delimiter.
*/
if (putc(c, &tp->t_rawq) >= 0) {
if (!ISSET(lflag, ICANON)) {
ttwakeup(tp);
ttyecho(c, tp);
goto endcase;
}
if (TTBREAKC(c, lflag)) {
tp->t_rocount = 0;
catq(&tp->t_rawq, &tp->t_canq);
ttwakeup(tp);
} else if (tp->t_rocount++ == 0)
tp->t_rocol = tp->t_column;
if (ISSET(tp->t_state, TS_ERASE)) {
/*
* end of prterase \.../
*/
CLR(tp->t_state, TS_ERASE);
(void)ttyoutput('/', tp);
}
i = tp->t_column;
ttyecho(c, tp);
if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) {
/*
* Place the cursor over the '^' of the ^D.
*/
i = min(2, tp->t_column - i);
while (i > 0) {
(void)ttyoutput('\b', tp);
i--;
}
}
}
endcase:
/*
* IXANY means allow any character to restart output.
*/
if (ISSET(tp->t_state, TS_TTSTOP) &&
!ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP])
return (ret);
restartoutput:
CLR(tp->t_lflag, FLUSHO);
CLR(tp->t_state, TS_TTSTOP);
startoutput:
ttstart(tp);
return (ret);
}
/*
* Output a single character on a tty, doing output processing
* as needed (expanding tabs, newline processing, etc.).
* Returns < 0 if succeeds, otherwise returns char to resend.
* Must be recursive.
*/
int
ttyoutput(int c, struct tty *tp)
{
long oflag;
int col, notout, s, c2;
oflag = tp->t_oflag;
if (!ISSET(oflag, OPOST)) {
tk_nout++;
tp->t_outcc++;
if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq))
return (c);
return (-1);
}
/*
* Do tab expansion if OXTABS is set. Special case if we external
* processing, we don't do the tab expansion because we'll probably
* get it wrong. If tab expansion needs to be done, let it happen
* externally.
*/
CLR(c, ~TTY_CHARMASK);
if (c == '\t' &&
ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) {
c = 8 - (tp->t_column & 7);
if (ISSET(tp->t_lflag, FLUSHO)) {
notout = 0;
} else {
s = spltty(); /* Don't interrupt tabs. */
notout = b_to_q(" ", c, &tp->t_outq);
c -= notout;
tk_nout += c;
tp->t_outcc += c;
splx(s);
}
tp->t_column += c;
return (notout ? '\t' : -1);
}
if (c == CEOT && ISSET(oflag, ONOEOT))
return (-1);
/*
* Newline translation: if ONLCR is set,
* translate newline into "\r\n". If OCRNL
* is set, translate '\r' into '\n'.
*/
if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) {
tk_nout++;
tp->t_outcc++;
if (!ISSET(tp->t_lflag, FLUSHO) && putc('\r', &tp->t_outq))
return (c);
tp->t_column = 0;
}
else if (c == '\r' && ISSET(tp->t_oflag, OCRNL))
c = '\n';
if (ISSET(tp->t_oflag, OLCUC) && islower(c))
c = toupper(c);
else if (ISSET(tp->t_oflag, OLCUC) && ISSET(tp->t_lflag, XCASE)) {
c2 = c;
switch (c) {
case '`':
c2 = '\'';
break;
case '|':
c2 = '!';
break;
case '~':
c2 = '^';
break;
case '{':
c2 = '(';
break;
case '}':
c2 = ')';
break;
}
if (c == '\\' || isupper(c) || c != c2) {
tk_nout++;
tp->t_outcc++;
if (putc('\\', &tp->t_outq))
return (c);
c = c2;
}
}
if (ISSET(tp->t_oflag, ONOCR) && c == '\r' && tp->t_column == 0)
return (-1);
tk_nout++;
tp->t_outcc++;
if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq))
return (c);
col = tp->t_column;
switch (CCLASS(c)) {
case BACKSPACE:
if (col > 0)
--col;
break;
case CONTROL:
break;
case NEWLINE:
if (ISSET(tp->t_oflag, ONLRET) || ISSET(tp->t_oflag, OCRNL))
col = 0;
break;
case RETURN:
col = 0;
break;
case ORDINARY:
++col;
break;
case TAB:
col = (col + 8) & ~7;
break;
}
tp->t_column = col;
return (-1);
}
/*
* Ioctls for all tty devices. Called after line-discipline specific ioctl
* has been called to do discipline-specific functions and/or reject any
* of these ioctl commands.
*/
int
ttioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p)
{
extern int nlinesw;
struct process *pr = p->p_p;
int s, error;
/* If the ioctl involves modification, hang if in the background. */
switch (cmd) {
case FIOSETOWN:
case TIOCFLUSH:
case TIOCDRAIN:
case TIOCSBRK:
case TIOCCBRK:
case TIOCSETA:
case TIOCSETD:
case TIOCSETAF:
case TIOCSETAW:
case TIOCSPGRP:
case TIOCSTAT:
case TIOCSWINSZ:
while (isbackground(pr, tp) &&
(pr->ps_flags & PS_PPWAIT) == 0 &&
!sigismasked(p, SIGTTOU)) {
if (pr->ps_pgrp->pg_jobc == 0)
return (EIO);
pgsignal(pr->ps_pgrp, SIGTTOU, 1);
error = ttysleep(tp, &lbolt, TTOPRI | PCATCH,
ttybg);
if (error)
return (error);
}
break;
}
switch (cmd) { /* Process the ioctl. */
case FIOASYNC: /* set/clear async i/o */
s = spltty();
if (*(int *)data)
SET(tp->t_state, TS_ASYNC);
else
CLR(tp->t_state, TS_ASYNC);
splx(s);
break;
case FIONBIO: /* set/clear non-blocking i/o */
break; /* XXX: delete. */
case FIONREAD: /* get # bytes to read */
s = spltty();
*(int *)data = ttnread(tp);
splx(s);
break;
case TIOCEXCL: /* set exclusive use of tty */
s = spltty();
SET(tp->t_state, TS_XCLUDE);
splx(s);
break;
case TIOCFLUSH: { /* flush buffers */
int flags = *(int *)data;
if (flags == 0)
flags = FREAD | FWRITE;
else
flags &= FREAD | FWRITE;
ttyflush(tp, flags);
break;
}
case TIOCCONS: { /* become virtual console */
if (*(int *)data) {
struct nameidata nid;
if (constty != NULL && constty != tp &&
ISSET(constty->t_state, TS_CARR_ON | TS_ISOPEN) ==
(TS_CARR_ON | TS_ISOPEN))
return (EBUSY);
/* ensure user can open the real console */
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, "/dev/console", p);
nid.ni_pledge = PLEDGE_RPATH | PLEDGE_WPATH;
nid.ni_unveil = UNVEIL_READ | UNVEIL_WRITE;
error = namei(&nid);
if (error)
return (error);
vn_lock(nid.ni_vp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_ACCESS(nid.ni_vp, VREAD, p->p_ucred, p);
VOP_UNLOCK(nid.ni_vp);
vrele(nid.ni_vp);
if (error)
return (error);
constty = tp;
} else if (tp == constty)
constty = NULL;
break;
}
case TIOCDRAIN: /* wait till output drained */
if ((error = ttywait(tp)) != 0)
return (error);
break;
case TIOCGETA: { /* get termios struct */
struct termios *t = (struct termios *)data;
memcpy(t, &tp->t_termios, sizeof(struct termios));
break;
}
case TIOCGETD: /* get line discipline */
*(int *)data = tp->t_line;
break;
case TIOCGWINSZ: /* get window size */
*(struct winsize *)data = tp->t_winsize;
break;
case TIOCGTSTAMP:
s = spltty();
*(struct timeval *)data = tp->t_tv;
splx(s);
break;
case FIOGETOWN: /* get pgrp of tty */
if (!isctty(pr, tp) && suser(p))
return (ENOTTY);
*(int *)data = tp->t_pgrp ? -tp->t_pgrp->pg_id : 0;
break;
case TIOCGPGRP: /* get pgrp of tty */
if (!isctty(pr, tp) && suser(p))
return (ENOTTY);
*(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
break;
case TIOCGSID: /* get sid of tty */
if (!isctty(pr, tp))
return (ENOTTY);
*(int *)data = tp->t_session->s_leader->ps_pid;
break;
case TIOCNXCL: /* reset exclusive use of tty */
s = spltty();
CLR(tp->t_state, TS_XCLUDE);
splx(s);
break;
case TIOCOUTQ: /* output queue size */
*(int *)data = tp->t_outq.c_cc;
break;
case TIOCSETA: /* set termios struct */
case TIOCSETAW: /* drain output, set */
case TIOCSETAF: { /* drn out, fls in, set */
struct termios *t = (struct termios *)data;
s = spltty();
if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
if ((error = ttywait(tp)) != 0) {
splx(s);
return (error);
}
if (cmd == TIOCSETAF)
ttyflush(tp, FREAD);
}
if (!ISSET(t->c_cflag, CIGNORE)) {
/*
* Some minor validation is necessary.
*/
if (t->c_ispeed < 0 || t->c_ospeed < 0) {
splx(s);
return (EINVAL);
}
/*
* Set device hardware.
*/
if (tp->t_param && (error = (*tp->t_param)(tp, t))) {
splx(s);
return (error);
} else {
if (!ISSET(tp->t_state, TS_CARR_ON) &&
ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(t->c_cflag, CLOCAL)) {
CLR(tp->t_state, TS_ISOPEN);
SET(tp->t_state, TS_WOPEN);
ttwakeup(tp);
}
tp->t_cflag = t->c_cflag;
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
if (t->c_ospeed == 0 && tp->t_session &&
tp->t_session->s_leader)
prsignal(tp->t_session->s_leader,
SIGHUP);
}
ttsetwater(tp);
}
if (cmd != TIOCSETAF) {
if (ISSET(t->c_lflag, ICANON) !=
ISSET(tp->t_lflag, ICANON)) {
if (ISSET(t->c_lflag, ICANON)) {
SET(tp->t_lflag, PENDIN);
ttwakeup(tp);
} else {
struct clist tq;
catq(&tp->t_rawq, &tp->t_canq);
tq = tp->t_rawq;
tp->t_rawq = tp->t_canq;
tp->t_canq = tq;
CLR(tp->t_lflag, PENDIN);
}
}
}
tp->t_iflag = t->c_iflag;
tp->t_oflag = t->c_oflag;
/*
* Make the EXTPROC bit read only.
*/
if (ISSET(tp->t_lflag, EXTPROC))
SET(t->c_lflag, EXTPROC);
else
CLR(t->c_lflag, EXTPROC);
tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN);
memcpy(tp->t_cc, t->c_cc, sizeof(t->c_cc));
splx(s);
break;
}
case TIOCSETD: { /* set line discipline */
int t = *(int *)data;
dev_t device = tp->t_dev;
if ((u_int)t >= nlinesw)
return (ENXIO);
if (t != tp->t_line) {
s = spltty();
(*linesw[tp->t_line].l_close)(tp, flag, p);
error = (*linesw[t].l_open)(device, tp, p);
if (error) {
(*linesw[tp->t_line].l_open)(device, tp, p);
splx(s);
return (error);
}
tp->t_line = t;
splx(s);
}
break;
}
case TIOCSTART: /* start output, like ^Q */
s = spltty();
if (ISSET(tp->t_state, TS_TTSTOP) ||
ISSET(tp->t_lflag, FLUSHO)) {
CLR(tp->t_lflag, FLUSHO);
CLR(tp->t_state, TS_TTSTOP);
ttstart(tp);
}
splx(s);
break;
case TIOCSTOP: /* stop output, like ^S */
s = spltty();
if (!ISSET(tp->t_state, TS_TTSTOP)) {
SET(tp->t_state, TS_TTSTOP);
(*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
}
splx(s);
break;
case TIOCSCTTY: /* become controlling tty */
/* Session ctty vnode pointer set in vnode layer. */
if (!SESS_LEADER(pr) ||
((pr->ps_session->s_ttyvp || tp->t_session) &&
(tp->t_session != pr->ps_session)))
return (EPERM);
if (tp->t_session)
SESSRELE(tp->t_session);
SESSHOLD(pr->ps_session);
tp->t_session = pr->ps_session;
tp->t_pgrp = pr->ps_pgrp;
pr->ps_session->s_ttyp = tp;
atomic_setbits_int(&pr->ps_flags, PS_CONTROLT);
break;
case FIOSETOWN: { /* set pgrp of tty */
struct pgrp *pgrp;
struct process *pr1;
pid_t pgid = *(int *)data;
if (!isctty(pr, tp))
return (ENOTTY);
if (pgid < 0) {
pgrp = pgfind(-pgid);
} else {
pr1 = prfind(pgid);
if (pr1 == NULL)
return (ESRCH);
pgrp = pr1->ps_pgrp;
}
if (pgrp == NULL)
return (EINVAL);
else if (pgrp->pg_session != pr->ps_session)
return (EPERM);
tp->t_pgrp = pgrp;
break;
}
case TIOCSPGRP: { /* set pgrp of tty */
struct pgrp *pgrp = pgfind(*(int *)data);
if (!isctty(pr, tp))
return (ENOTTY);
else if (pgrp == NULL)
return (EINVAL);
else if (pgrp->pg_session != pr->ps_session)
return (EPERM);
tp->t_pgrp = pgrp;
break;
}
case TIOCSTAT: /* get load avg stats */
ttyinfo(tp);
break;
case TIOCSWINSZ: /* set window size */
if (bcmp((caddr_t)&tp->t_winsize, data,
sizeof (struct winsize))) {
tp->t_winsize = *(struct winsize *)data;
pgsignal(tp->t_pgrp, SIGWINCH, 1);
}
break;
case TIOCSTSTAMP: {
struct tstamps *ts = (struct tstamps *)data;
s = spltty();
CLR(tp->t_flags, TS_TSTAMPDCDSET);
CLR(tp->t_flags, TS_TSTAMPCTSSET);
CLR(tp->t_flags, TS_TSTAMPDCDCLR);
CLR(tp->t_flags, TS_TSTAMPCTSCLR);
if (ISSET(ts->ts_set, TIOCM_CAR))
SET(tp->t_flags, TS_TSTAMPDCDSET);
if (ISSET(ts->ts_set, TIOCM_CTS))
SET(tp->t_flags, TS_TSTAMPCTSSET);
if (ISSET(ts->ts_clr, TIOCM_CAR))
SET(tp->t_flags, TS_TSTAMPDCDCLR);
if (ISSET(ts->ts_clr, TIOCM_CTS))
SET(tp->t_flags, TS_TSTAMPCTSCLR);
splx(s);
break;
}
default:
return (-1);
}
return (0);
}
const struct filterops ttyread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ttyrdetach,
.f_event = filt_ttyread,
};
const struct filterops ttywrite_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ttywdetach,
.f_event = filt_ttywrite,
};
const struct filterops ttyexcept_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_ttyrdetach,
.f_event = filt_ttyexcept,
};
int
ttkqfilter(dev_t dev, struct knote *kn)
{
struct tty *tp = (*cdevsw[major(dev)].d_tty)(dev);
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &tp->t_rsel.si_note;
kn->kn_fop = &ttyread_filtops;
break;
case EVFILT_WRITE:
klist = &tp->t_wsel.si_note;
kn->kn_fop = &ttywrite_filtops;
break;
case EVFILT_EXCEPT:
if (kn->kn_flags & __EV_SELECT) {
/* Prevent triggering exceptfds. */
return (EPERM);
}
if ((kn->kn_flags & __EV_POLL) == 0) {
/* Disallow usage through kevent(2). */
return (EINVAL);
}
klist = &tp->t_rsel.si_note;
kn->kn_fop = &ttyexcept_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = tp;
s = spltty();
klist_insert_locked(klist, kn);
splx(s);
return (0);
}
void
filt_ttyrdetach(struct knote *kn)
{
struct tty *tp = kn->kn_hook;
int s;
s = spltty();
klist_remove_locked(&tp->t_rsel.si_note, kn);
splx(s);
}
int
filt_ttyread(struct knote *kn, long hint)
{
struct tty *tp = kn->kn_hook;
int active, s;
s = spltty();
kn->kn_data = ttnread(tp);
active = (kn->kn_data > 0);
if (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL)
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
kn->kn_flags &= ~(EV_EOF | __EV_HUP);
}
splx(s);
return (active);
}
void
filt_ttywdetach(struct knote *kn)
{
struct tty *tp = kn->kn_hook;
int s;
s = spltty();
klist_remove_locked(&tp->t_wsel.si_note, kn);
splx(s);
}
int
filt_ttywrite(struct knote *kn, long hint)
{
struct tty *tp = kn->kn_hook;
int active, s;
s = spltty();
kn->kn_data = tp->t_outq.c_cn - tp->t_outq.c_cc;
active = (tp->t_outq.c_cc <= tp->t_lowat);
/* Write-side HUP condition is only for poll(2) and select(2). */
if (kn->kn_flags & (__EV_POLL | __EV_SELECT)) {
if (!ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
kn->kn_flags &= ~__EV_HUP;
}
}
splx(s);
return (active);
}
int
filt_ttyexcept(struct knote *kn, long hint)
{
struct tty *tp = kn->kn_hook;
int active = 0;
int s;
s = spltty();
if (kn->kn_flags & __EV_POLL) {
if (!ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(tp->t_state, TS_CARR_ON)) {
kn->kn_flags |= __EV_HUP;
active = 1;
} else {
kn->kn_flags &= ~__EV_HUP;
}
}
splx(s);
return (active);
}
static int
ttnread(struct tty *tp)
{
int nread;
splassert(IPL_TTY);
if (ISSET(tp->t_lflag, PENDIN))
ttypend(tp);
nread = tp->t_canq.c_cc;
if (!ISSET(tp->t_lflag, ICANON)) {
nread += tp->t_rawq.c_cc;
if (nread < tp->t_cc[VMIN] && !tp->t_cc[VTIME])
nread = 0;
}
return (nread);
}
/*
* Wait for output to drain, or if this times out, flush it.
*/
int
ttywait_nsec(struct tty *tp, uint64_t nsecs)
{
int error, s;
error = 0;
s = spltty();
while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) &&
(ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL)) &&
tp->t_oproc) {
(*tp->t_oproc)(tp);
if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) &&
(ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL))
&& tp->t_oproc) {
SET(tp->t_state, TS_ASLEEP);
error = ttysleep_nsec(tp, &tp->t_outq, TTOPRI | PCATCH,
ttyout, nsecs);
if (error == EWOULDBLOCK)
ttyflush(tp, FWRITE);
if (error)
break;
} else
break;
}
splx(s);
return (error);
}
int
ttywait(struct tty *tp)
{
return (ttywait_nsec(tp, INFSLP));
}
/*
* Flush if successfully wait.
*/
int
ttywflush(struct tty *tp)
{
int error;
error = ttywait_nsec(tp, SEC_TO_NSEC(5));
if (error == 0 || error == EWOULDBLOCK)
ttyflush(tp, FREAD);
return (error);
}
/*
* Flush tty read and/or write queues, notifying anyone waiting.
*/
void
ttyflush(struct tty *tp, int rw)
{
int s;
s = spltty();
if (rw & FREAD) {
FLUSHQ(&tp->t_canq);
FLUSHQ(&tp->t_rawq);
tp->t_rocount = 0;
tp->t_rocol = 0;
CLR(tp->t_state, TS_LOCAL);
ttyunblock(tp);
ttwakeup(tp);
}
if (rw & FWRITE) {
CLR(tp->t_state, TS_TTSTOP);
(*cdevsw[major(tp->t_dev)].d_stop)(tp, rw);
FLUSHQ(&tp->t_outq);
wakeup((caddr_t)&tp->t_outq);
selwakeup(&tp->t_wsel);
}
splx(s);
}
/*
* Copy in the default termios characters.
*/
void
ttychars(struct tty *tp)
{
memcpy(tp->t_cc, ttydefchars, sizeof(ttydefchars));
}
/*
* Send stop character on input overflow.
*/
static void
ttyblock(struct tty *tp)
{
int total;
total = tp->t_rawq.c_cc + tp->t_canq.c_cc;
if (tp->t_rawq.c_cc > TTYHOG(tp)) {
ttyflush(tp, FREAD | FWRITE);
CLR(tp->t_state, TS_TBLOCK);
}
/*
* Block further input iff: current input > threshold
* AND input is available to user program.
*/
if ((total >= TTYHOG(tp) / 2 &&
!ISSET(tp->t_state, TS_TBLOCK) &&
!ISSET(tp->t_lflag, ICANON)) || tp->t_canq.c_cc > 0) {
if (ISSET(tp->t_iflag, IXOFF) &&
tp->t_cc[VSTOP] != _POSIX_VDISABLE &&
putc(tp->t_cc[VSTOP], &tp->t_outq) == 0) {
SET(tp->t_state, TS_TBLOCK);
ttstart(tp);
}
/* Try to block remote output via hardware flow control. */
if (ISSET(tp->t_cflag, CHWFLOW) && tp->t_hwiflow &&
(*tp->t_hwiflow)(tp, 1) != 0)
SET(tp->t_state, TS_TBLOCK);
}
}
void
ttrstrt(void *arg)
{
struct tty *tp = (struct tty *)arg;
int s;
#ifdef DIAGNOSTIC
if (tp == NULL)
panic("ttrstrt");
#endif
s = spltty();
CLR(tp->t_state, TS_TIMEOUT);
ttstart(tp);
splx(s);
}
int
ttstart(struct tty *tp)
{
if (tp->t_oproc != NULL) /* XXX: Kludge for pty. */
(*tp->t_oproc)(tp);
return (0);
}
/*
* "close" a line discipline
*/
int
ttylclose(struct tty *tp, int flag, struct proc *p)
{
if (flag & FNONBLOCK)
ttyflush(tp, FREAD | FWRITE);
else
ttywflush(tp);
return (0);
}
/*
* Handle modem control transition on a tty.
* Flag indicates new state of carrier.
* Returns 0 if the line should be turned off, otherwise 1.
*/
int
ttymodem(struct tty *tp, int flag)
{
if (!ISSET(tp->t_state, TS_WOPEN) && ISSET(tp->t_cflag, MDMBUF)) {
/*
* MDMBUF: do flow control according to carrier flag
*/
if (flag) {
CLR(tp->t_state, TS_TTSTOP);
ttstart(tp);
} else if (!ISSET(tp->t_state, TS_TTSTOP)) {
SET(tp->t_state, TS_TTSTOP);
(*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
}
} else if (flag == 0) {
/*
* Lost carrier.
*/
CLR(tp->t_state, TS_CARR_ON);
if (ISSET(tp->t_state, TS_ISOPEN) &&
!ISSET(tp->t_cflag, CLOCAL)) {
if (tp->t_session && tp->t_session->s_leader)
prsignal(tp->t_session->s_leader, SIGHUP);
ttyflush(tp, FREAD | FWRITE);
return (0);
}
} else {
/*
* Carrier now on.
*/
SET(tp->t_state, TS_CARR_ON);
ttwakeup(tp);
}
return (1);
}
/*
* Default modem control routine (for other line disciplines).
* Return argument flag, to turn off device on carrier drop.
*/
int
nullmodem(struct tty *tp, int flag)
{
if (flag)
SET(tp->t_state, TS_CARR_ON);
else {
CLR(tp->t_state, TS_CARR_ON);
if (ISSET(tp->t_state, TS_ISOPEN) &&
!ISSET(tp->t_cflag, CLOCAL)) {
if (tp->t_session && tp->t_session->s_leader)
prsignal(tp->t_session->s_leader, SIGHUP);
ttyflush(tp, FREAD | FWRITE);
return (0);
}
}
return (1);
}
/*
* Reinput pending characters after state switch
* call at spltty().
*/
void
ttypend(struct tty *tp)
{
struct clist tq;
int c;
splassert(IPL_TTY);
CLR(tp->t_lflag, PENDIN);
SET(tp->t_state, TS_TYPEN);
tq = tp->t_rawq;
tp->t_rawq.c_cc = 0;
tp->t_rawq.c_cf = tp->t_rawq.c_cl = NULL;
while ((c = getc(&tq)) >= 0)
ttyinput(c, tp);
CLR(tp->t_state, TS_TYPEN);
}
void ttvtimeout(void *);
void
ttvtimeout(void *arg)
{
struct tty *tp = (struct tty *)arg;
wakeup(&tp->t_rawq);
}
/*
* Process a read call on a tty device.
*/
int
ttread(struct tty *tp, struct uio *uio, int flag)
{
struct timeout *stime = NULL;
struct proc *p = curproc;
struct process *pr = p->p_p;
int s, first, error = 0;
u_char *cc = tp->t_cc;
struct clist *qp;
int last_cc = 0;
long lflag;
int c;
loop: lflag = tp->t_lflag;
s = spltty();
/*
* take pending input first
*/
if (ISSET(lflag, PENDIN))
ttypend(tp);
splx(s);
/*
* Hang process if it's in the background.
*/
if (isbackground(pr, tp)) {
if (sigismasked(p, SIGTTIN) ||
pr->ps_flags & PS_PPWAIT || pr->ps_pgrp->pg_jobc == 0) {
error = EIO;
goto out;
}
pgsignal(pr->ps_pgrp, SIGTTIN, 1);
error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg);
if (error)
goto out;
goto loop;
}
s = spltty();
if (!ISSET(lflag, ICANON)) {
int min = cc[VMIN];
int time = cc[VTIME] * 100; /* tenths of a second (ms) */
qp = &tp->t_rawq;
/*
* Check each of the four combinations.
* (min > 0 && time == 0) is the normal read case.
* It should be fairly efficient, so we check that and its
* companion case (min == 0 && time == 0) first.
*/
if (time == 0) {
if (qp->c_cc < min)
goto sleep;
goto read;
}
if (min > 0) {
if (qp->c_cc <= 0)
goto sleep;
if (qp->c_cc >= min)
goto read;
if (stime == NULL) {
alloc_timer:
stime = malloc(sizeof(*stime), M_TEMP, M_WAITOK);
timeout_set(stime, ttvtimeout, tp);
timeout_add_msec(stime, time);
} else if (qp->c_cc > last_cc) {
/* got a character, restart timer */
timeout_add_msec(stime, time);
}
} else { /* min == 0 */
if (qp->c_cc > 0)
goto read;
if (stime == NULL) {
goto alloc_timer;
}
}
last_cc = qp->c_cc;
if (stime && !timeout_triggered(stime)) {
goto sleep;
}
} else if ((qp = &tp->t_canq)->c_cc <= 0) {
int carrier;
sleep:
/*
* If there is no input, sleep on rawq
* awaiting hardware receipt and notification.
* If we have data, we don't need to check for carrier.
*/
carrier = ISSET(tp->t_state, TS_CARR_ON) ||
ISSET(tp->t_cflag, CLOCAL);
if (!carrier && ISSET(tp->t_state, TS_ISOPEN)) {
splx(s);
error = 0;
goto out;
}
if (flag & IO_NDELAY) {
splx(s);
error = EWOULDBLOCK;
goto out;
}
error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
carrier ? ttyin : ttopen);
splx(s);
if (stime && timeout_triggered(stime))
error = EWOULDBLOCK;
if (cc[VMIN] == 0 && error == EWOULDBLOCK) {
error = 0;
goto out;
}
if (error && error != EWOULDBLOCK)
goto out;
error = 0;
goto loop;
}
read:
splx(s);
/*
* Input present, check for input mapping and processing.
*/
first = 1;
while ((c = getc(qp)) >= 0) {
/*
* delayed suspend (^Y)
*/
if (CCEQ(cc[VDSUSP], c) &&
ISSET(lflag, IEXTEN | ISIG) == (IEXTEN | ISIG)) {
pgsignal(tp->t_pgrp, SIGTSTP, 1);
if (first) {
error = ttysleep(tp, &lbolt, TTIPRI | PCATCH,
ttybg);
if (error)
break;
goto loop;
}
break;
}
/*
* Interpret EOF only in canonical mode.
*/
if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON))
break;
/*
* Give user character.
*/
error = ureadc(c, uio);
if (error)
break;
if (uio->uio_resid == 0)
break;
/*
* In canonical mode check for a "break character"
* marking the end of a "line of input".
*/
if (ISSET(lflag, ICANON) && TTBREAKC(c, lflag))
break;
first = 0;
}
/*
* Look to unblock output now that (presumably)
* the input queue has gone down.
*/
s = spltty();
if (tp->t_rawq.c_cc < TTYHOG(tp)/5)
ttyunblock(tp);
splx(s);
out:
if (stime) {
timeout_del(stime);
free(stime, M_TEMP, sizeof(*stime));
}
return (error);
}
/* Call at spltty */
void
ttyunblock(struct tty *tp)
{
u_char *cc = tp->t_cc;
splassert(IPL_TTY);
if (ISSET(tp->t_state, TS_TBLOCK)) {
if (ISSET(tp->t_iflag, IXOFF) &&
cc[VSTART] != _POSIX_VDISABLE &&
putc(cc[VSTART], &tp->t_outq) == 0) {
CLR(tp->t_state, TS_TBLOCK);
ttstart(tp);
}
/* Try to unblock remote output via hardware flow control. */
if (ISSET(tp->t_cflag, CHWFLOW) && tp->t_hwiflow &&
(*tp->t_hwiflow)(tp, 0) != 0)
CLR(tp->t_state, TS_TBLOCK);
}
}
/*
* Check the output queue on tp for space for a kernel message (from uprintf
* or tprintf). Allow some space over the normal hiwater mark so we don't
* lose messages due to normal flow control, but don't let the tty run amok.
* Sleeps here are not interruptible, but we return prematurely if new signals
* arrive.
*/
int
ttycheckoutq(struct tty *tp, int wait)
{
int hiwat, s, oldsig;
hiwat = tp->t_hiwat;
s = spltty();
oldsig = wait ? SIGPENDING(curproc) : 0;
if (tp->t_outq.c_cc > hiwat + TTHIWATMINSPACE)
while (tp->t_outq.c_cc > hiwat) {
ttstart(tp);
if (wait == 0 || SIGPENDING(curproc) != oldsig) {
splx(s);
return (0);
}
SET(tp->t_state, TS_ASLEEP);
tsleep_nsec(&tp->t_outq, PZERO - 1, "ttckoutq",
SEC_TO_NSEC(1));
}
splx(s);
return (1);
}
/*
* Process a write call on a tty device.
*/
int
ttwrite(struct tty *tp, struct uio *uio, int flag)
{
u_char *cp = NULL;
int cc, ce, obufcc = 0;
struct proc *p;
struct process *pr;
int hiwat, error, s;
size_t cnt;
u_char obuf[OBUFSIZ];
hiwat = tp->t_hiwat;
cnt = uio->uio_resid;
error = 0;
cc = 0;
loop:
s = spltty();
if (!ISSET(tp->t_state, TS_CARR_ON) &&
!ISSET(tp->t_cflag, CLOCAL)) {
if (ISSET(tp->t_state, TS_ISOPEN)) {
splx(s);
error = EIO;
goto done;
} else if (flag & IO_NDELAY) {
splx(s);
error = EWOULDBLOCK;
goto out;
} else {
/* Sleep awaiting carrier. */
error = ttysleep(tp,
&tp->t_rawq, TTIPRI | PCATCH, ttopen);
splx(s);
if (error)
goto out;
goto loop;
}
}
splx(s);
/*
* Hang the process if it's in the background.
*/
p = curproc;
pr = p->p_p;
if (isbackground(pr, tp) &&
ISSET(tp->t_lflag, TOSTOP) && (pr->ps_flags & PS_PPWAIT) == 0 &&
!sigismasked(p, SIGTTOU)) {
if (pr->ps_pgrp->pg_jobc == 0) {
error = EIO;
goto out;
}
pgsignal(pr->ps_pgrp, SIGTTOU, 1);
error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg);
if (error)
goto out;
goto loop;
}
/*
* Process the user's data in at most OBUFSIZ chunks. Perform any
* output translation. Keep track of high water mark, sleep on
* overflow awaiting device aid in acquiring new space.
*/
while (uio->uio_resid > 0 || cc > 0) {
if (ISSET(tp->t_lflag, FLUSHO)) {
uio->uio_resid = 0;
goto done;
}
if (tp->t_outq.c_cc > hiwat)
goto ovhiwat;
/*
* Grab a hunk of data from the user, unless we have some
* leftover from last time.
*/
if (cc == 0) {
cc = MIN(uio->uio_resid, OBUFSIZ);
cp = obuf;
error = uiomove(cp, cc, uio);
if (error) {
cc = 0;
break;
}
if (cc > obufcc)
obufcc = cc;
/* duplicate /dev/console output into console buffer */
if (consbufp && cn_tab &&
cn_tab->cn_dev == tp->t_dev && tp->t_gen == 0) {
int i;
for (i = 0; i < cc; i++) {
char c = cp[i];
if (c != '\0' && c != '\r' && c != 0177)
msgbuf_putchar(consbufp, c);
}
}
}
/*
* If nothing fancy need be done, grab those characters we
* can handle without any of ttyoutput's processing and
* just transfer them to the output q. For those chars
* which require special processing (as indicated by the
* bits in char_type), call ttyoutput. After processing
* a hunk of data, look for FLUSHO so ^O's will take effect
* immediately.
*/
while (cc > 0) {
int i;
if (!ISSET(tp->t_oflag, OPOST))
ce = cc;
else {
ce = cc - scanc((u_int)cc, cp, char_type,
CCLASSMASK);
/*
* If ce is zero, then we're processing
* a special character through ttyoutput.
*/
if (ce == 0) {
tp->t_rocount = 0;
if (ttyoutput(*cp, tp) >= 0) {
/* out of space */
goto ovhiwat;
}
cp++;
cc--;
if (ISSET(tp->t_lflag, FLUSHO) ||
tp->t_outq.c_cc > hiwat)
goto ovhiwat;
continue;
}
}
/*
* A bunch of normal characters have been found.
* Transfer them en masse to the output queue and
* continue processing at the top of the loop.
* If there are any further characters in this
* <= OBUFSIZ chunk, the first should be a character
* requiring special handling by ttyoutput.
*/
tp->t_rocount = 0;
i = b_to_q(cp, ce, &tp->t_outq);
ce -= i;
tp->t_column += ce;
cp += ce, cc -= ce, tk_nout += ce;
tp->t_outcc += ce;
if (i > 0) {
/* out of space */
goto ovhiwat;
}
if (ISSET(tp->t_lflag, FLUSHO) ||
tp->t_outq.c_cc > hiwat)
break;
}
ttstart(tp);
}
out:
/*
* If cc is nonzero, we leave the uio structure inconsistent, as the
* offset and iov pointers have moved forward, but it doesn't matter
* (the call will either return short or restart with a new uio).
*/
uio->uio_resid += cc;
done:
if (obufcc)
explicit_bzero(obuf, obufcc);
return (error);
ovhiwat:
ttstart(tp);
s = spltty();
/*
* This can only occur if FLUSHO is set in t_lflag,
* or if ttstart/oproc is synchronous (or very fast).
*/
if (tp->t_outq.c_cc <= hiwat) {
splx(s);
goto loop;
}
if (flag & IO_NDELAY) {
splx(s);
uio->uio_resid += cc;
if (obufcc)
explicit_bzero(obuf, obufcc);
return (uio->uio_resid == cnt ? EWOULDBLOCK : 0);
}
SET(tp->t_state, TS_ASLEEP);
error = ttysleep(tp, &tp->t_outq, TTOPRI | PCATCH, ttyout);
splx(s);
if (error)
goto out;
goto loop;
}
/*
* Rubout one character from the rawq of tp
* as cleanly as possible.
*/
int
ttyrub(int c, struct tty *tp)
{
u_char *cp;
int savecol;
int tabc, s, cc;
if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC))
return 0;
CLR(tp->t_lflag, FLUSHO);
if (ISSET(tp->t_lflag, ECHOE)) {
if (tp->t_rocount == 0) {
/*
* Screwed by ttwrite; retype
*/
return ttyretype(tp);
}
if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE))
ttyrubo(tp, 2);
else {
CLR(c, ~TTY_CHARMASK);
switch (CCLASS(c)) {
case ORDINARY:
ttyrubo(tp, 1);
break;
case BACKSPACE:
case CONTROL:
case NEWLINE:
case RETURN:
case VTAB:
if (ISSET(tp->t_lflag, ECHOCTL))
ttyrubo(tp, 2);
break;
case TAB:
if (tp->t_rocount < tp->t_rawq.c_cc)
return ttyretype(tp);
s = spltty();
savecol = tp->t_column;
SET(tp->t_state, TS_CNTTB);
SET(tp->t_lflag, FLUSHO);
tp->t_column = tp->t_rocol;
for (cp = firstc(&tp->t_rawq, &tabc, &cc); cp;
cp = nextc(&tp->t_rawq, cp, &tabc, &cc))
ttyecho(tabc, tp);
CLR(tp->t_lflag, FLUSHO);
CLR(tp->t_state, TS_CNTTB);
splx(s);
/* savecol will now be length of the tab. */
savecol -= tp->t_column;
tp->t_column += savecol;
if (savecol > 8)
savecol = 8; /* overflow screw */
while (--savecol >= 0)
(void)ttyoutput('\b', tp);
break;
default: /* XXX */
#define PANICSTR "ttyrub: would panic c = %d, val = %d"
(void)printf(PANICSTR "\n", c, CCLASS(c));
#ifdef notdef
panic(PANICSTR, c, CCLASS(c));
#endif
}
}
} else if (ISSET(tp->t_lflag, ECHOPRT)) {
if (!ISSET(tp->t_state, TS_ERASE)) {
SET(tp->t_state, TS_ERASE);
(void)ttyoutput('\\', tp);
}
ttyecho(c, tp);
} else
ttyecho(tp->t_cc[VERASE], tp);
--tp->t_rocount;
return 0;
}
/*
* Back over cnt characters, erasing them.
*/
static void
ttyrubo(struct tty *tp, int cnt)
{
while (cnt-- > 0) {
(void)ttyoutput('\b', tp);
(void)ttyoutput(' ', tp);
(void)ttyoutput('\b', tp);
}
}
/*
* ttyretype --
* Reprint the rawq line. Note, it is assumed that c_cc has already
* been checked.
*/
int
ttyretype(struct tty *tp)
{
u_char *cp;
int s, c, cc;
/* Echo the reprint character. */
if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE)
ttyecho(tp->t_cc[VREPRINT], tp);
(void)ttyoutput('\n', tp);
s = spltty();
for (cp = firstc(&tp->t_canq, &c, &cc); cp;
cp = nextc(&tp->t_canq, cp, &c, &cc))
ttyecho(c, tp);
for (cp = firstc(&tp->t_rawq, &c, &cc); cp;
cp = nextc(&tp->t_rawq, cp, &c, &cc))
ttyecho(c, tp);
CLR(tp->t_state, TS_ERASE);
splx(s);
tp->t_rocount = tp->t_rawq.c_cc;
tp->t_rocol = 0;
return (1);
}
/*
* Echo a typed character to the terminal.
*/
static void
ttyecho(int c, struct tty *tp)
{
if (!ISSET(tp->t_state, TS_CNTTB))
CLR(tp->t_lflag, FLUSHO);
if ((!ISSET(tp->t_lflag, ECHO) &&
(!ISSET(tp->t_lflag, ECHONL) || c != '\n')) ||
ISSET(tp->t_lflag, EXTPROC))
return;
if (((ISSET(tp->t_lflag, ECHOCTL) &&
(ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n')) ||
ISSET(c, TTY_CHARMASK) == 0177)) {
(void)ttyoutput('^', tp);
CLR(c, ~TTY_CHARMASK);
if (c == 0177)
c = '?';
else
c += 'A' - 1;
}
(void)ttyoutput(c, tp);
}
/*
* Wakeup any writers if necessary.
*/
void
ttwakeupwr(struct tty *tp)
{
if (tp->t_outq.c_cc <= tp->t_lowat) {
if (ISSET(tp->t_state, TS_ASLEEP)) {
CLR(tp->t_state, TS_ASLEEP);
wakeup(&tp->t_outq);
}
selwakeup(&tp->t_wsel);
}
}
/*
* Wake up any readers on a tty.
*/
void
ttwakeup(struct tty *tp)
{
selwakeup(&tp->t_rsel);
if (ISSET(tp->t_state, TS_ASYNC))
pgsignal(tp->t_pgrp, SIGIO, 1);
wakeup((caddr_t)&tp->t_rawq);
}
/*
* Look up a code for a specified speed in a conversion table;
* used by drivers to map software speed values to hardware parameters.
*/
int
ttspeedtab(int speed, const struct speedtab *table)
{
for ( ; table->sp_speed != -1; table++)
if (table->sp_speed == speed)
return (table->sp_code);
return (-1);
}
/*
* Set tty hi and low water marks.
*
* Try to arrange the dynamics so there's about one second
* from hi to low water.
*/
void
ttsetwater(struct tty *tp)
{
int cps, x;
#define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x))
cps = tp->t_ospeed / 10;
tp->t_lowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
x += cps;
tp->t_hiwat = CLAMP(x, tp->t_outq.c_cn - TTHIWATMINSPACE, TTMINHIWAT);
#undef CLAMP
}
/*
* Get the total estcpu for a process, summing across threads.
* Returns true if at least one thread is runnable/running.
*/
static int
process_sum(struct process *pr, fixpt_t *estcpup)
{
struct proc *p;
fixpt_t estcpu;
int ret;
ret = 0;
estcpu = 0;
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
if (p->p_stat == SRUN || p->p_stat == SONPROC)
ret = 1;
estcpu += p->p_pctcpu;
}
*estcpup = estcpu;
return (ret);
}
/*
* Report on state of foreground process group.
*/
void
ttyinfo(struct tty *tp)
{
struct process *pr, *pickpr;
struct proc *p, *pick;
struct timespec utime, stime;
int tmp;
if (ttycheckoutq(tp,0) == 0)
return;
/* Print load average. */
tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT;
ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100);
if (tp->t_session == NULL)
ttyprintf(tp, "not a controlling terminal\n");
else if (tp->t_pgrp == NULL)
ttyprintf(tp, "no foreground process group\n");
else if ((pr = LIST_FIRST(&tp->t_pgrp->pg_members)) == NULL)
empty: ttyprintf(tp, "empty foreground process group\n");
else {
const char *state;
fixpt_t pctcpu, pctcpu2;
int run, run2;
int calc_pctcpu;
long rss = 0;
/*
* Pick the most active process:
* - prefer at least one running/runnable thread
* - prefer higher total pctcpu
* - prefer non-zombie
* Otherwise take the most recently added to this process group
*/
pickpr = pr;
run = process_sum(pickpr, &pctcpu);
while ((pr = LIST_NEXT(pr, ps_pglist)) != NULL) {
run2 = process_sum(pr, &pctcpu2);
if (run) {
/*
* pick is running; is p running w/same or
* more cpu?
*/
if (run2 && pctcpu2 >= pctcpu)
goto update_pickpr;
continue;
}
/* pick isn't running; is p running *or* w/more cpu? */
if (run2 || pctcpu2 > pctcpu)
goto update_pickpr;
/* if p has less cpu or is zombie, then it's worse */
if (pctcpu2 < pctcpu || (pr->ps_flags & PS_ZOMBIE))
continue;
update_pickpr:
pickpr = pr;
run = run2;
pctcpu = pctcpu2;
}
/* Calculate percentage cpu, resident set size. */
calc_pctcpu = (pctcpu * 10000 + FSCALE / 2) >> FSHIFT;
if ((pickpr->ps_flags & (PS_EMBRYO | PS_ZOMBIE)) == 0 &&
pickpr->ps_vmspace != NULL)
rss = vm_resident_count(pickpr->ps_vmspace);
calctsru(&pickpr->ps_tu, &utime, &stime, NULL);
/* Round up and print user time. */
utime.tv_nsec += 5000000;
if (utime.tv_nsec >= 1000000000) {
utime.tv_sec += 1;
utime.tv_nsec -= 1000000000;
}
/* Round up and print system time. */
stime.tv_nsec += 5000000;
if (stime.tv_nsec >= 1000000000) {
stime.tv_sec += 1;
stime.tv_nsec -= 1000000000;
}
/*
* Find the most active thread:
* - prefer runnable
* - prefer higher pctcpu
* - prefer living
* Otherwise take the newest thread
*/
pick = p = TAILQ_FIRST(&pickpr->ps_threads);
if (p == NULL)
goto empty;
run = p->p_stat == SRUN || p->p_stat == SONPROC;
pctcpu = p->p_pctcpu;
while ((p = TAILQ_NEXT(p, p_thr_link)) != NULL) {
run2 = p->p_stat == SRUN || p->p_stat == SONPROC;
pctcpu2 = p->p_pctcpu;
if (run) {
/*
* pick is running; is p running w/same or
* more cpu?
*/
if (run2 && pctcpu2 >= pctcpu)
goto update_pick;
continue;
}
/* pick isn't running; is p running *or* w/more cpu? */
if (run2 || pctcpu2 > pctcpu)
goto update_pick;
/* if p has less cpu or is exiting, then it's worse */
if (pctcpu2 < pctcpu || p->p_flag & P_WEXIT)
continue;
update_pick:
pick = p;
run = run2;
pctcpu = p->p_pctcpu;
}
state = pick->p_stat == SONPROC ? "running" :
pick->p_stat == SRUN ? "runnable" :
pick->p_wmesg ? pick->p_wmesg : "iowait";
ttyprintf(tp,
" cmd: %s %d [%s] %lld.%02ldu %lld.%02lds %d%% %ldk\n",
pickpr->ps_comm, pickpr->ps_pid, state,
(long long)utime.tv_sec, utime.tv_nsec / 10000000,
(long long)stime.tv_sec, stime.tv_nsec / 10000000,
calc_pctcpu / 100, rss);
}
tp->t_rocount = 0; /* so pending input will be retyped if BS */
}
/*
* Output char to tty; console putchar style.
*/
int
tputchar(int c, struct tty *tp)
{
int s;
s = spltty();
if (ISSET(tp->t_state, TS_ISOPEN) == 0 ||
!(ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL))) {
splx(s);
return (-1);
}
if (c == '\n')
(void)ttyoutput('\r', tp);
(void)ttyoutput(c, tp);
ttstart(tp);
splx(s);
return (0);
}
/*
* Sleep on chan, returning ERESTART if tty changed while we napped and
* returning any errors (e.g. EINTR/ETIMEDOUT) reported by tsleep. If
* the tty is revoked, restarting a pending call will redo validation done
* at the start of the call.
*/
int
ttysleep(struct tty *tp, void *chan, int pri, char *wmesg)
{
return (ttysleep_nsec(tp, chan, pri, wmesg, INFSLP));
}
int
ttysleep_nsec(struct tty *tp, void *chan, int pri, char *wmesg, uint64_t nsecs)
{
int error;
short gen;
gen = tp->t_gen;
if ((error = tsleep_nsec(chan, pri, wmesg, nsecs)) != 0)
return (error);
return (tp->t_gen == gen ? 0 : ERESTART);
}
/*
* Initialise the global tty list.
*/
void
tty_init(void)
{
TAILQ_INIT(&ttylist);
tty_count = 0;
}
/*
* Allocate a tty structure and its associated buffers, and attach it to the
* tty list.
*/
struct tty *
ttymalloc(int baud)
{
struct tty *tp;
tp = malloc(sizeof(struct tty), M_TTYS, M_WAITOK|M_ZERO);
if (baud == 0)
baud = 115200;
if (baud <= 9600)
tp->t_qlen = 1024;
else if (baud <= 115200)
tp->t_qlen = 4096;
else
tp->t_qlen = 8192;
clalloc(&tp->t_rawq, tp->t_qlen, 1);
clalloc(&tp->t_canq, tp->t_qlen, 1);
/* output queue doesn't need quoting */
clalloc(&tp->t_outq, tp->t_qlen, 0);
rw_enter_write(&ttylist_lock);
TAILQ_INSERT_TAIL(&ttylist, tp, tty_link);
++tty_count;
rw_exit_write(&ttylist_lock);
timeout_set(&tp->t_rstrt_to, ttrstrt, tp);
return(tp);
}
/*
* Free a tty structure and its buffers, after removing it from the tty list.
*/
void
ttyfree(struct tty *tp)
{
int s;
rw_enter_write(&ttylist_lock);
--tty_count;
#ifdef DIAGNOSTIC
if (tty_count < 0)
panic("ttyfree: tty_count < 0");
#endif
TAILQ_REMOVE(&ttylist, tp, tty_link);
rw_exit_write(&ttylist_lock);
s = spltty();
klist_invalidate(&tp->t_rsel.si_note);
klist_invalidate(&tp->t_wsel.si_note);
splx(s);
clfree(&tp->t_rawq);
clfree(&tp->t_canq);
clfree(&tp->t_outq);
free(tp, M_TTYS, sizeof(*tp));
}
void
ttystats_init(struct itty **ttystats, int *ttycp, size_t *ttystatssiz)
{
int ntty = 0, ttyc;
struct itty *itp;
struct tty *tp;
ttyc = tty_count;
*ttystatssiz = ttyc * sizeof(struct itty);
*ttystats = mallocarray(ttyc, sizeof(struct itty),
M_SYSCTL, M_WAITOK|M_ZERO);
rw_enter_write(&ttylist_lock);
for (tp = TAILQ_FIRST(&ttylist), itp = *ttystats; tp && ntty++ < ttyc;
tp = TAILQ_NEXT(tp, tty_link), itp++) {
itp->t_dev = tp->t_dev;
itp->t_rawq_c_cc = tp->t_rawq.c_cc;
itp->t_canq_c_cc = tp->t_canq.c_cc;
itp->t_outq_c_cc = tp->t_outq.c_cc;
itp->t_hiwat = tp->t_hiwat;
itp->t_lowat = tp->t_lowat;
if (ISSET(tp->t_oflag, OPOST))
itp->t_column = tp->t_column;
itp->t_state = tp->t_state;
itp->t_session = tp->t_session;
if (tp->t_pgrp)
itp->t_pgrp_pg_id = tp->t_pgrp->pg_id;
else
itp->t_pgrp_pg_id = 0;
itp->t_line = tp->t_line;
}
rw_exit_write(&ttylist_lock);
*ttycp = ntty;
}
/*
* Return tty-related information.
*/
int
sysctl_tty(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int err;
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case KERN_TTY_TKNIN:
return (sysctl_rdquad(oldp, oldlenp, newp, tk_nin));
case KERN_TTY_TKNOUT:
return (sysctl_rdquad(oldp, oldlenp, newp, tk_nout));
case KERN_TTY_TKRAWCC:
return (sysctl_rdquad(oldp, oldlenp, newp, tk_rawcc));
case KERN_TTY_TKCANCC:
return (sysctl_rdquad(oldp, oldlenp, newp, tk_cancc));
case KERN_TTY_INFO:
{
struct itty *ttystats;
size_t ttystatssiz;
int ttyc;
ttystats_init(&ttystats, &ttyc, &ttystatssiz);
err = sysctl_rdstruct(oldp, oldlenp, newp, ttystats,
ttyc * sizeof(struct itty));
free(ttystats, M_SYSCTL, ttystatssiz);
return (err);
}
default:
#if NPTY > 0
return (sysctl_pty(name, namelen, oldp, oldlenp, newp, newlen));
#else
return (EOPNOTSUPP);
#endif
}
/* NOTREACHED */
}
void
ttytstamp(struct tty *tp, int octs, int ncts, int odcd, int ndcd)
{
int doit = 0;
if (ncts ^ octs)
doit |= ncts ? ISSET(tp->t_flags, TS_TSTAMPCTSSET) :
ISSET(tp->t_flags, TS_TSTAMPCTSCLR);
if (ndcd ^ odcd)
doit |= ndcd ? ISSET(tp->t_flags, TS_TSTAMPDCDSET) :
ISSET(tp->t_flags, TS_TSTAMPDCDCLR);
if (doit)
microtime(&tp->t_tv);
}
504
503
874
877
40
40
40
9
9
9
456
457
308
309
309
309
885
887
887
151
150
123
123
123
409
411
410
2300
2301
2301
1458
1461
156
156
25
25
10
10
10
14
14
14
14
9
9
9
38
38
38
40
40
40
15
15
15
15
9
9
9
12
12
12
36
36
36
93
93
550
551
550
156
157
3395
3388
3392
3394
15
15
15
121
121
219
219
219
154
154
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
/* $OpenBSD: vfs_vops.c,v 1.35 2022/06/26 05:20:42 visa Exp $ */
/*
* Copyright (c) 2010 Thordur I. Bjornsson <thib@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/vnode.h>
#include <sys/unistd.h>
#include <sys/systm.h>
#ifdef VFSLCKDEBUG
#define ASSERT_VP_ISLOCKED(vp) do { \
if (((vp)->v_flag & VLOCKSWORK) && !VOP_ISLOCKED(vp)) { \
VOP_PRINT(vp); \
panic("vp not locked"); \
} \
} while (0)
#else
#define ASSERT_VP_ISLOCKED(vp) /* nothing */
#endif
int
VOP_ISLOCKED(struct vnode *vp)
{
struct vop_islocked_args a;
a.a_vp = vp;
if (vp->v_op->vop_islocked == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_islocked)(&a));
}
int
VOP_LOOKUP(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp)
{
struct vop_lookup_args a;
a.a_dvp = dvp;
a.a_vpp = vpp;
a.a_cnp = cnp;
if (dvp->v_op->vop_lookup == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_lookup)(&a));
}
int
VOP_CREATE(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap)
{
struct vop_create_args a;
a.a_dvp = dvp;
a.a_vpp = vpp;
a.a_cnp = cnp;
a.a_vap = vap;
ASSERT_VP_ISLOCKED(dvp);
if (dvp->v_op->vop_create == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_create)(&a));
}
int
VOP_MKNOD(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap)
{
struct vop_mknod_args a;
a.a_dvp = dvp;
a.a_vpp = vpp;
a.a_cnp = cnp;
a.a_vap = vap;
ASSERT_VP_ISLOCKED(dvp);
if (dvp->v_op->vop_mknod == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_mknod)(&a));
}
int
VOP_OPEN(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
{
struct vop_open_args a;
a.a_vp = vp;
a.a_mode = mode;
a.a_cred = cred;
a.a_p = p;
KASSERT(p == curproc);
if (vp->v_op->vop_open == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_open)(&a));
}
int
VOP_CLOSE(struct vnode *vp, int fflag, struct ucred *cred, struct proc *p)
{
struct vop_close_args a;
a.a_vp = vp;
a.a_fflag = fflag;
a.a_cred = cred;
a.a_p = p;
KASSERT(p == NULL || p == curproc);
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_close == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_close)(&a));
}
int
VOP_ACCESS(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
{
struct vop_access_args a;
a.a_vp = vp;
a.a_mode = mode;
a.a_cred = cred;
a.a_p = p;
KASSERT(p == curproc);
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_access == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_access)(&a));
}
int
VOP_GETATTR(struct vnode *vp, struct vattr *vap, struct ucred *cred,
struct proc *p)
{
struct vop_getattr_args a;
a.a_vp = vp;
a.a_vap = vap;
a.a_cred = cred;
a.a_p = p;
KASSERT(p == curproc);
if (vp->v_op->vop_getattr == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_getattr)(&a));
}
int
VOP_SETATTR(struct vnode *vp, struct vattr *vap, struct ucred *cred,
struct proc *p)
{
struct vop_setattr_args a;
a.a_vp = vp;
a.a_vap = vap;
a.a_cred = cred;
a.a_p = p;
KASSERT(p == curproc);
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_setattr == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_setattr)(&a));
}
int
VOP_READ(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
{
struct vop_read_args a;
a.a_vp = vp;
a.a_uio = uio;
a.a_ioflag = ioflag;
a.a_cred = cred;
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_read == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_read)(&a));
}
int
VOP_WRITE(struct vnode *vp, struct uio *uio, int ioflag,
struct ucred *cred)
{
struct vop_write_args a;
a.a_vp = vp;
a.a_uio = uio;
a.a_ioflag = ioflag;
a.a_cred = cred;
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_write == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_write)(&a));
}
int
VOP_IOCTL(struct vnode *vp, u_long command, void *data, int fflag,
struct ucred *cred, struct proc *p)
{
struct vop_ioctl_args a;
a.a_vp = vp;
a.a_command = command;
a.a_data = data;
a.a_fflag = fflag;
a.a_cred = cred;
a.a_p = p;
KASSERT(p == curproc);
if (vp->v_op->vop_ioctl == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_ioctl)(&a));
}
int
VOP_KQFILTER(struct vnode *vp, int fflag, struct knote *kn)
{
struct vop_kqfilter_args a;
a.a_vp = vp;
a.a_fflag = fflag;
a.a_kn = kn;
if (vp->v_op->vop_kqfilter == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_kqfilter)(&a));
}
int
VOP_REVOKE(struct vnode *vp, int flags)
{
struct vop_revoke_args a;
a.a_vp = vp;
a.a_flags = flags;
if (vp->v_op->vop_revoke == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_revoke)(&a));
}
int
VOP_FSYNC(struct vnode *vp, struct ucred *cred, int waitfor,
struct proc *p)
{
int r, s;
struct vop_fsync_args a;
a.a_vp = vp;
a.a_cred = cred;
a.a_waitfor = waitfor;
a.a_p = p;
KASSERT(p == curproc);
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_fsync == NULL)
return (EOPNOTSUPP);
r = (vp->v_op->vop_fsync)(&a);
s = splbio();
if (r == 0 && vp->v_bioflag & VBIOERROR)
r = EIO;
splx(s);
return r;
}
int
VOP_REMOVE(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
{
struct vop_remove_args a;
a.a_dvp = dvp;
a.a_vp = vp;
a.a_cnp = cnp;
ASSERT_VP_ISLOCKED(dvp);
ASSERT_VP_ISLOCKED(vp);
if (dvp->v_op->vop_remove == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_remove)(&a));
}
int
VOP_LINK(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
{
struct vop_link_args a;
a.a_dvp = dvp;
a.a_vp = vp;
a.a_cnp = cnp;
ASSERT_VP_ISLOCKED(dvp);
if (dvp->v_op->vop_link == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_link)(&a));
}
int
VOP_RENAME(struct vnode *fdvp, struct vnode *fvp,
struct componentname *fcnp, struct vnode *tdvp, struct vnode *tvp,
struct componentname *tcnp)
{
struct vop_rename_args a;
a.a_fdvp = fdvp;
a.a_fvp = fvp;
a.a_fcnp = fcnp;
a.a_tdvp = tdvp;
a.a_tvp = tvp;
a.a_tcnp = tcnp;
ASSERT_VP_ISLOCKED(tdvp);
if (fdvp->v_op->vop_rename == NULL)
return (EOPNOTSUPP);
return ((fdvp->v_op->vop_rename)(&a));
}
int
VOP_MKDIR(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap)
{
struct vop_mkdir_args a;
a.a_dvp = dvp;
a.a_vpp = vpp;
a.a_cnp = cnp;
a.a_vap = vap;
ASSERT_VP_ISLOCKED(dvp);
if (dvp->v_op->vop_mkdir == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_mkdir)(&a));
}
int
VOP_RMDIR(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
{
struct vop_rmdir_args a;
a.a_dvp = dvp;
a.a_vp = vp;
a.a_cnp = cnp;
ASSERT_VP_ISLOCKED(dvp);
ASSERT_VP_ISLOCKED(vp);
KASSERT(dvp != vp);
if (dvp->v_op->vop_rmdir == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_rmdir)(&a));
}
int
VOP_SYMLINK(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vattr *vap, char *target)
{
struct vop_symlink_args a;
a.a_dvp = dvp;
a.a_vpp = vpp;
a.a_cnp = cnp;
a.a_vap = vap;
a.a_target = target;
ASSERT_VP_ISLOCKED(dvp);
if (dvp->v_op->vop_symlink == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_symlink)(&a));
}
int
VOP_READDIR(struct vnode *vp, struct uio *uio, struct ucred *cred,
int *eofflag)
{
struct vop_readdir_args a;
a.a_vp = vp;
a.a_uio = uio;
a.a_cred = cred;
a.a_eofflag = eofflag;
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_readdir == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_readdir)(&a));
}
int
VOP_READLINK(struct vnode *vp, struct uio *uio, struct ucred *cred)
{
struct vop_readlink_args a;
a.a_vp = vp;
a.a_uio = uio;
a.a_cred = cred;
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_readlink == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_readlink)(&a));
}
int
VOP_ABORTOP(struct vnode *dvp, struct componentname *cnp)
{
struct vop_abortop_args a;
a.a_dvp = dvp;
a.a_cnp = cnp;
if (dvp->v_op->vop_abortop == NULL)
return (EOPNOTSUPP);
return ((dvp->v_op->vop_abortop)(&a));
}
int
VOP_INACTIVE(struct vnode *vp, struct proc *p)
{
struct vop_inactive_args a;
a.a_vp = vp;
a.a_p = p;
KASSERT(p == curproc);
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_inactive == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_inactive)(&a));
}
int
VOP_RECLAIM(struct vnode *vp, struct proc *p)
{
struct vop_reclaim_args a;
a.a_vp = vp;
a.a_p = p;
KASSERT(p == curproc);
if (vp->v_op->vop_reclaim == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_reclaim)(&a));
}
int
VOP_LOCK(struct vnode *vp, int flags)
{
struct vop_lock_args a;
a.a_vp = vp;
a.a_flags = flags;
MUTEX_ASSERT_UNLOCKED(&vnode_mtx);
if (vp->v_op->vop_lock == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_lock)(&a));
}
int
VOP_UNLOCK(struct vnode *vp)
{
struct vop_unlock_args a;
a.a_vp = vp;
if (vp->v_op->vop_unlock == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_unlock)(&a));
}
int
VOP_BMAP(struct vnode *vp, daddr_t bn, struct vnode **vpp,
daddr_t *bnp, int *runp)
{
struct vop_bmap_args a;
a.a_vp = vp;
a.a_bn = bn;
a.a_vpp = vpp;
a.a_bnp = bnp;
a.a_runp = runp;
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_bmap == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_bmap)(&a));
}
int
VOP_PRINT(struct vnode *vp)
{
struct vop_print_args a;
a.a_vp = vp;
if (vp->v_op->vop_print == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_print)(&a));
}
int
VOP_PATHCONF(struct vnode *vp, int name, register_t *retval)
{
struct vop_pathconf_args a;
/*
* Handle names that are constant across filesystem
*/
switch (name) {
case _PC_PATH_MAX:
*retval = PATH_MAX;
return (0);
case _PC_PIPE_BUF:
*retval = PIPE_BUF;
return (0);
case _PC_ASYNC_IO:
case _PC_PRIO_IO:
case _PC_SYNC_IO:
*retval = 0;
return (0);
}
a.a_vp = vp;
a.a_name = name;
a.a_retval = retval;
ASSERT_VP_ISLOCKED(vp);
if (vp->v_op->vop_pathconf == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_pathconf)(&a));
}
int
VOP_ADVLOCK(struct vnode *vp, void *id, int op, struct flock *fl, int flags)
{
struct vop_advlock_args a;
a.a_vp = vp;
a.a_id = id;
a.a_op = op;
a.a_fl = fl;
a.a_flags = flags;
if (vp->v_op->vop_advlock == NULL)
return (EOPNOTSUPP);
return (vp->v_op->vop_advlock)(&a);
}
int
VOP_STRATEGY(struct vnode *vp, struct buf *bp)
{
struct vop_strategy_args a;
a.a_vp = vp;
a.a_bp = bp;
if ((ISSET(bp->b_flags, B_BC)) && (!ISSET(bp->b_flags, B_DMA)))
panic("Non dma reachable buffer passed to VOP_STRATEGY");
if (vp->v_op->vop_strategy == NULL)
return (EOPNOTSUPP);
return ((vp->v_op->vop_strategy)(&a));
}
int
VOP_BWRITE(struct buf *bp)
{
struct vop_bwrite_args a;
a.a_bp = bp;
if (bp->b_vp->v_op->vop_bwrite == NULL)
return (EOPNOTSUPP);
return ((bp->b_vp->v_op->vop_bwrite)(&a));
}
19
12
9
12
9
4
2
3
15
12
9
12
12
9
12
9
9
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
/* $OpenBSD: pms.c,v 1.97 2022/07/23 05:55:16 sdk Exp $ */
/* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */
/*-
* Copyright (c) 1994 Charles M. Hannum.
* Copyright (c) 1992, 1993 Erik Forsberg.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/rwlock.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/task.h>
#include <sys/timeout.h>
#include <machine/bus.h>
#include <dev/ic/pckbcvar.h>
#include <dev/pckbc/pmsreg.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#if defined(__i386__) || defined(__amd64__)
#include "acpi.h"
#endif
#if !defined(SMALL_KERNEL) && NACPI > 0
extern int mouse_has_softbtn;
#else
int mouse_has_softbtn;
#endif
#ifdef DEBUG
#define DPRINTF(x...) do { printf(x); } while (0);
#else
#define DPRINTF(x...)
#endif
#define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
#define WSMOUSE_BUTTON(x) (1 << ((x) - 1))
struct pms_softc;
struct pms_protocol {
int type;
#define PMS_STANDARD 0
#define PMS_INTELLI 1
#define PMS_SYNAPTICS 2
#define PMS_ALPS 3
#define PMS_ELANTECH_V1 4
#define PMS_ELANTECH_V2 5
#define PMS_ELANTECH_V3 6
#define PMS_ELANTECH_V4 7
u_int packetsize;
int (*enable)(struct pms_softc *);
int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
int (*sync)(struct pms_softc *, int);
void (*proc)(struct pms_softc *);
void (*disable)(struct pms_softc *);
};
struct synaptics_softc {
int identify;
int capabilities, ext_capabilities, ext2_capabilities;
int model, ext_model;
int modes;
int mode;
int mask;
#define SYNAPTICS_MASK_NEWABS_STRICT 0xc8
#define SYNAPTICS_MASK_NEWABS_RELAXED 0xc0
#define SYNAPTICS_VALID_NEWABS_FIRST 0x80
#define SYNAPTICS_VALID_NEWABS_NEXT 0xc0
u_int sec_buttons;
#define SYNAPTICS_PRESSURE_HI 30
#define SYNAPTICS_PRESSURE_LO 25
#define SYNAPTICS_PRESSURE SYNAPTICS_PRESSURE_HI
#define SYNAPTICS_SCALE 4
#define SYNAPTICS_MAX_FINGERS 3
};
struct alps_softc {
int model;
#define ALPS_GLIDEPOINT (1 << 1)
#define ALPS_DUALPOINT (1 << 2)
#define ALPS_PASSTHROUGH (1 << 3)
#define ALPS_INTERLEAVED (1 << 4)
int mask;
int version;
u_int gesture;
u_int sec_buttons; /* trackpoint */
int old_x, old_y;
#define ALPS_PRESSURE 40
};
struct elantech_softc {
int flags;
#define ELANTECH_F_REPORTS_PRESSURE 0x01
#define ELANTECH_F_HAS_ROCKER 0x02
#define ELANTECH_F_2FINGER_PACKET 0x04
#define ELANTECH_F_HW_V1_OLD 0x08
#define ELANTECH_F_CRC_ENABLED 0x10
#define ELANTECH_F_TRACKPOINT 0x20
int fw_version;
u_int mt_slots;
int width;
u_char parity[256];
u_char p1, p2, p3;
int max_x, max_y;
int old_x, old_y;
int initial_pkt;
};
#define ELANTECH_IS_CLICKPAD(sc) (((sc)->elantech->fw_version & 0x1000) != 0)
struct pms_softc { /* driver status information */
struct device sc_dev;
pckbc_tag_t sc_kbctag;
int sc_state;
#define PMS_STATE_DISABLED 0
#define PMS_STATE_ENABLED 1
#define PMS_STATE_SUSPENDED 2
struct rwlock sc_state_lock;
int sc_dev_enable;
#define PMS_DEV_IGNORE 0x00
#define PMS_DEV_PRIMARY 0x01
#define PMS_DEV_SECONDARY 0x02
struct task sc_rsttask;
struct timeout sc_rsttimo;
int sc_rststate;
#define PMS_RST_COMMENCE 0x01
#define PMS_RST_ANNOUNCED 0x02
int poll;
int inputstate;
const struct pms_protocol *protocol;
struct synaptics_softc *synaptics;
struct alps_softc *alps;
struct elantech_softc *elantech;
u_char packet[8];
struct device *sc_wsmousedev;
struct device *sc_sec_wsmousedev;
};
static const u_int butmap[8] = {
0,
WSMOUSE_BUTTON(1),
WSMOUSE_BUTTON(3),
WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3),
WSMOUSE_BUTTON(2),
WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2),
WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3),
WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3)
};
static const struct alps_model {
int version;
int mask;
int model;
} alps_models[] = {
{ 0x2021, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
{ 0x2221, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
{ 0x2222, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
{ 0x3222, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
{ 0x5212, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED },
{ 0x5321, 0xf8, ALPS_GLIDEPOINT },
{ 0x5322, 0xf8, ALPS_GLIDEPOINT },
{ 0x603b, 0xf8, ALPS_GLIDEPOINT },
{ 0x6222, 0xcf, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED },
{ 0x6321, 0xf8, ALPS_GLIDEPOINT },
{ 0x6322, 0xf8, ALPS_GLIDEPOINT },
{ 0x6323, 0xf8, ALPS_GLIDEPOINT },
{ 0x6324, 0x8f, ALPS_GLIDEPOINT },
{ 0x6325, 0xef, ALPS_GLIDEPOINT },
{ 0x6326, 0xf8, ALPS_GLIDEPOINT },
{ 0x7301, 0xf8, ALPS_DUALPOINT },
{ 0x7321, 0xf8, ALPS_GLIDEPOINT },
{ 0x7322, 0xf8, ALPS_GLIDEPOINT },
{ 0x7325, 0xcf, ALPS_GLIDEPOINT },
#if 0
/*
* This model has a clitpad sending almost compatible PS2
* packets but not compatible enough to be used with the
* ALPS protocol.
*/
{ 0x633b, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
{ 0x7326, 0, 0 }, /* XXX Uses unknown v3 protocol */
{ 0x7331, 0x8f, ALPS_DUALPOINT }, /* not supported */
#endif
};
static struct wsmouse_param synaptics_params[] = {
{ WSMOUSECFG_PRESSURE_LO, SYNAPTICS_PRESSURE_LO },
{ WSMOUSECFG_PRESSURE_HI, SYNAPTICS_PRESSURE_HI }
};
static struct wsmouse_param alps_params[] = {
{ WSMOUSECFG_SMOOTHING, 3 }
};
int pmsprobe(struct device *, void *, void *);
void pmsattach(struct device *, struct device *, void *);
int pmsactivate(struct device *, int);
void pmsinput(void *, int);
int pms_change_state(struct pms_softc *, int, int);
int pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
int pms_enable(void *);
void pms_disable(void *);
int pms_sec_ioctl(void *, u_long, caddr_t, int, struct proc *);
int pms_sec_enable(void *);
void pms_sec_disable(void *);
int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int);
int pms_spec_cmd(struct pms_softc *, int);
int pms_get_devid(struct pms_softc *, u_char *);
int pms_get_status(struct pms_softc *, u_char *);
int pms_set_rate(struct pms_softc *, int);
int pms_set_resolution(struct pms_softc *, int);
int pms_set_scaling(struct pms_softc *, int);
int pms_reset(struct pms_softc *);
int pms_dev_enable(struct pms_softc *);
int pms_dev_disable(struct pms_softc *);
void pms_protocol_lookup(struct pms_softc *);
void pms_reset_detect(struct pms_softc *, int);
void pms_reset_task(void *);
void pms_reset_timo(void *);
int pms_enable_intelli(struct pms_softc *);
int pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *);
int pms_sync_mouse(struct pms_softc *, int);
void pms_proc_mouse(struct pms_softc *);
int pms_enable_synaptics(struct pms_softc *);
int pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *);
int pms_sync_synaptics(struct pms_softc *, int);
void pms_proc_synaptics(struct pms_softc *);
void pms_disable_synaptics(struct pms_softc *);
int pms_enable_alps(struct pms_softc *);
int pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *);
int pms_sync_alps(struct pms_softc *, int);
void pms_proc_alps(struct pms_softc *);
int pms_enable_elantech_v1(struct pms_softc *);
int pms_enable_elantech_v2(struct pms_softc *);
int pms_enable_elantech_v3(struct pms_softc *);
int pms_enable_elantech_v4(struct pms_softc *);
int pms_ioctl_elantech(struct pms_softc *, u_long, caddr_t, int,
struct proc *);
int pms_sync_elantech_v1(struct pms_softc *, int);
int pms_sync_elantech_v2(struct pms_softc *, int);
int pms_sync_elantech_v3(struct pms_softc *, int);
int pms_sync_elantech_v4(struct pms_softc *, int);
void pms_proc_elantech_v1(struct pms_softc *);
void pms_proc_elantech_v2(struct pms_softc *);
void pms_proc_elantech_v3(struct pms_softc *);
void pms_proc_elantech_v4(struct pms_softc *);
int synaptics_knock(struct pms_softc *);
int synaptics_set_mode(struct pms_softc *, int, int);
int synaptics_query(struct pms_softc *, int, int *);
int synaptics_get_hwinfo(struct pms_softc *);
void synaptics_sec_proc(struct pms_softc *);
int alps_sec_proc(struct pms_softc *);
int alps_get_hwinfo(struct pms_softc *);
int elantech_knock(struct pms_softc *);
int elantech_get_hwinfo_v1(struct pms_softc *);
int elantech_get_hwinfo_v2(struct pms_softc *);
int elantech_get_hwinfo_v3(struct pms_softc *);
int elantech_get_hwinfo_v4(struct pms_softc *);
int elantech_ps2_cmd(struct pms_softc *, u_char);
int elantech_set_absolute_mode_v1(struct pms_softc *);
int elantech_set_absolute_mode_v2(struct pms_softc *);
int elantech_set_absolute_mode_v3(struct pms_softc *);
int elantech_set_absolute_mode_v4(struct pms_softc *);
const struct cfattach pms_ca = {
sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
pmsactivate
};
struct cfdriver pms_cd = {
NULL, "pms", DV_DULL
};
const struct wsmouse_accessops pms_accessops = {
pms_enable,
pms_ioctl,
pms_disable,
};
const struct wsmouse_accessops pms_sec_accessops = {
pms_sec_enable,
pms_sec_ioctl,
pms_sec_disable,
};
const struct pms_protocol pms_protocols[] = {
/* Generic PS/2 mouse */
{
PMS_STANDARD, 3,
NULL,
pms_ioctl_mouse,
pms_sync_mouse,
pms_proc_mouse,
NULL
},
/* Synaptics touchpad */
{
PMS_SYNAPTICS, 6,
pms_enable_synaptics,
pms_ioctl_synaptics,
pms_sync_synaptics,
pms_proc_synaptics,
pms_disable_synaptics
},
/* ALPS touchpad */
{
PMS_ALPS, 6,
pms_enable_alps,
pms_ioctl_alps,
pms_sync_alps,
pms_proc_alps,
NULL
},
/* Elantech touchpad (hardware version 1) */
{
PMS_ELANTECH_V1, 4,
pms_enable_elantech_v1,
pms_ioctl_elantech,
pms_sync_elantech_v1,
pms_proc_elantech_v1,
NULL
},
/* Elantech touchpad (hardware version 2) */
{
PMS_ELANTECH_V2, 6,
pms_enable_elantech_v2,
pms_ioctl_elantech,
pms_sync_elantech_v2,
pms_proc_elantech_v2,
NULL
},
/* Elantech touchpad (hardware version 3) */
{
PMS_ELANTECH_V3, 6,
pms_enable_elantech_v3,
pms_ioctl_elantech,
pms_sync_elantech_v3,
pms_proc_elantech_v3,
NULL
},
/* Elantech touchpad (hardware version 4) */
{
PMS_ELANTECH_V4, 6,
pms_enable_elantech_v4,
pms_ioctl_elantech,
pms_sync_elantech_v4,
pms_proc_elantech_v4,
NULL
},
/* Microsoft IntelliMouse */
{
PMS_INTELLI, 4,
pms_enable_intelli,
pms_ioctl_mouse,
pms_sync_mouse,
pms_proc_mouse,
NULL
},
};
int
pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen)
{
if (sc->poll) {
return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
cmd, len, resplen, resp, 1);
} else {
return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
cmd, len, resplen, 1, resp);
}
}
int
pms_spec_cmd(struct pms_softc *sc, int cmd)
{
if (pms_set_scaling(sc, 1) ||
pms_set_resolution(sc, (cmd >> 6) & 0x03) ||
pms_set_resolution(sc, (cmd >> 4) & 0x03) ||
pms_set_resolution(sc, (cmd >> 2) & 0x03) ||
pms_set_resolution(sc, (cmd >> 0) & 0x03))
return (-1);
return (0);
}
int
pms_get_devid(struct pms_softc *sc, u_char *resp)
{
u_char cmd[1];
cmd[0] = PMS_SEND_DEV_ID;
return (pms_cmd(sc, cmd, 1, resp, 1));
}
int
pms_get_status(struct pms_softc *sc, u_char *resp)
{
u_char cmd[1];
cmd[0] = PMS_SEND_DEV_STATUS;
return (pms_cmd(sc, cmd, 1, resp, 3));
}
int
pms_set_rate(struct pms_softc *sc, int value)
{
u_char cmd[2];
cmd[0] = PMS_SET_SAMPLE;
cmd[1] = value;
return (pms_cmd(sc, cmd, 2, NULL, 0));
}
int
pms_set_resolution(struct pms_softc *sc, int value)
{
u_char cmd[2];
cmd[0] = PMS_SET_RES;
cmd[1] = value;
return (pms_cmd(sc, cmd, 2, NULL, 0));
}
int
pms_set_scaling(struct pms_softc *sc, int scale)
{
u_char cmd[1];
switch (scale) {
case 1:
default:
cmd[0] = PMS_SET_SCALE11;
break;
case 2:
cmd[0] = PMS_SET_SCALE21;
break;
}
return (pms_cmd(sc, cmd, 1, NULL, 0));
}
int
pms_reset(struct pms_softc *sc)
{
u_char cmd[1], resp[2];
int res;
cmd[0] = PMS_RESET;
res = pms_cmd(sc, cmd, 1, resp, 2);
#ifdef DEBUG
if (res || resp[0] != PMS_RSTDONE || resp[1] != 0)
printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n",
DEVNAME(sc), res, resp[0], resp[1]);
#endif
return (res);
}
int
pms_dev_enable(struct pms_softc *sc)
{
u_char cmd[1];
int res;
cmd[0] = PMS_DEV_ENABLE;
res = pms_cmd(sc, cmd, 1, NULL, 0);
if (res)
printf("%s: enable error\n", DEVNAME(sc));
return (res);
}
int
pms_dev_disable(struct pms_softc *sc)
{
u_char cmd[1];
int res;
cmd[0] = PMS_DEV_DISABLE;
res = pms_cmd(sc, cmd, 1, NULL, 0);
if (res)
printf("%s: disable error\n", DEVNAME(sc));
return (res);
}
void
pms_protocol_lookup(struct pms_softc *sc)
{
int i;
sc->protocol = &pms_protocols[0];
for (i = 1; i < nitems(pms_protocols); i++) {
pms_reset(sc);
if (pms_protocols[i].enable(sc)) {
sc->protocol = &pms_protocols[i];
break;
}
}
DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
}
/*
* Detect reset announcement ([0xaa, 0x0]).
* The sequence will be sent as input on rare occasions when the touchpad was
* reset due to a power failure.
*/
void
pms_reset_detect(struct pms_softc *sc, int data)
{
switch (sc->sc_rststate) {
case PMS_RST_COMMENCE:
if (data == 0x0) {
sc->sc_rststate = PMS_RST_ANNOUNCED;
timeout_add_msec(&sc->sc_rsttimo, 100);
} else if (data != PMS_RSTDONE) {
sc->sc_rststate = 0;
}
break;
default:
if (data == PMS_RSTDONE)
sc->sc_rststate = PMS_RST_COMMENCE;
else
sc->sc_rststate = 0;
}
}
void
pms_reset_timo(void *v)
{
struct pms_softc *sc = v;
int s = spltty();
/*
* Do nothing if the reset was a false positive or if the device already
* is disabled.
*/
if (sc->sc_rststate == PMS_RST_ANNOUNCED &&
sc->sc_state != PMS_STATE_DISABLED)
task_add(systq, &sc->sc_rsttask);
splx(s);
}
void
pms_reset_task(void *v)
{
struct pms_softc *sc = v;
int s = spltty();
#ifdef DIAGNOSTIC
printf("%s: device reset (state = %d)\n", DEVNAME(sc), sc->sc_rststate);
#endif
rw_enter_write(&sc->sc_state_lock);
if (sc->sc_sec_wsmousedev != NULL)
pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
if (sc->sc_sec_wsmousedev != NULL)
pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY);
rw_exit_write(&sc->sc_state_lock);
splx(s);
}
int
pms_enable_intelli(struct pms_softc *sc)
{
u_char resp;
/* the special sequence to enable the third button and the roller */
if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) ||
pms_set_rate(sc, PMS_INTELLI_MAGIC2) ||
pms_set_rate(sc, PMS_INTELLI_MAGIC3) ||
pms_get_devid(sc, &resp) ||
resp != PMS_INTELLI_ID)
return (0);
return (1);
}
int
pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
int i;
switch (cmd) {
case WSMOUSEIO_GTYPE:
*(u_int *)data = WSMOUSE_TYPE_PS2;
break;
case WSMOUSEIO_SRES:
i = ((int) *(u_int *)data - 12) / 25;
/* valid values are {0,1,2,3} */
if (i < 0)
i = 0;
if (i > 3)
i = 3;
if (pms_set_resolution(sc, i))
printf("%s: SET_RES command error\n", DEVNAME(sc));
break;
default:
return (-1);
}
return (0);
}
int
pms_sync_mouse(struct pms_softc *sc, int data)
{
if (sc->inputstate != 0)
return (0);
switch (sc->protocol->type) {
case PMS_STANDARD:
if ((data & 0xc0) != 0)
return (-1);
break;
case PMS_INTELLI:
if ((data & 0x08) != 0x08)
return (-1);
break;
}
return (0);
}
void
pms_proc_mouse(struct pms_softc *sc)
{
u_int buttons;
int dx, dy, dz;
buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
dx = (sc->packet[0] & PMS_PS2_XNEG) ?
(int)sc->packet[1] - 256 : sc->packet[1];
dy = (sc->packet[0] & PMS_PS2_YNEG) ?
(int)sc->packet[2] - 256 : sc->packet[2];
if (sc->protocol->type == PMS_INTELLI)
dz = (signed char)sc->packet[3];
else
dz = 0;
WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, dz, 0);
}
int
pmsprobe(struct device *parent, void *match, void *aux)
{
struct pckbc_attach_args *pa = aux;
u_char cmd[1], resp[2];
int res;
if (pa->pa_slot != PCKBC_AUX_SLOT)
return (0);
/* Flush any garbage. */
pckbc_flush(pa->pa_tag, pa->pa_slot);
/* reset the device */
cmd[0] = PMS_RESET;
res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
#ifdef DEBUG
printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n",
res, resp[0], resp[1]);
#endif
return (0);
}
return (1);
}
void
pmsattach(struct device *parent, struct device *self, void *aux)
{
struct pms_softc *sc = (void *)self;
struct pckbc_attach_args *pa = aux;
struct wsmousedev_attach_args a;
sc->sc_kbctag = pa->pa_tag;
pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT,
pmsinput, sc, DEVNAME(sc));
printf("\n");
a.accessops = &pms_accessops;
a.accesscookie = sc;
rw_init(&sc->sc_state_lock, "pmsst");
/*
* Attach the wsmouse, saving a handle to it.
* Note that we don't need to check this pointer against NULL
* here or in pmsintr, because if this fails pms_enable() will
* never be called, so pmsinput() will never be called.
*/
sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
task_set(&sc->sc_rsttask, pms_reset_task, sc);
timeout_set(&sc->sc_rsttimo, pms_reset_timo, sc);
sc->poll = 1;
sc->sc_dev_enable = 0;
/* See if the device understands an extended (touchpad) protocol. */
pms_protocol_lookup(sc);
/* no interrupts until enabled */
pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE);
}
int
pmsactivate(struct device *self, int act)
{
struct pms_softc *sc = (struct pms_softc *)self;
switch (act) {
case DVACT_SUSPEND:
if (sc->sc_state == PMS_STATE_ENABLED)
pms_change_state(sc, PMS_STATE_SUSPENDED,
PMS_DEV_IGNORE);
break;
case DVACT_RESUME:
if (sc->sc_state == PMS_STATE_SUSPENDED)
pms_change_state(sc, PMS_STATE_ENABLED,
PMS_DEV_IGNORE);
break;
}
return (0);
}
int
pms_change_state(struct pms_softc *sc, int newstate, int dev)
{
if (dev != PMS_DEV_IGNORE) {
switch (newstate) {
case PMS_STATE_ENABLED:
if (sc->sc_dev_enable & dev)
return (EBUSY);
sc->sc_dev_enable |= dev;
if (sc->sc_state == PMS_STATE_ENABLED)
return (0);
break;
case PMS_STATE_DISABLED:
sc->sc_dev_enable &= ~dev;
if (sc->sc_dev_enable)
return (0);
break;
}
}
switch (newstate) {
case PMS_STATE_ENABLED:
sc->inputstate = 0;
sc->sc_rststate = 0;
pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
if (sc->poll)
pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
pms_reset(sc);
if (sc->protocol->enable != NULL &&
sc->protocol->enable(sc) == 0)
pms_protocol_lookup(sc);
pms_dev_enable(sc);
break;
case PMS_STATE_DISABLED:
case PMS_STATE_SUSPENDED:
pms_dev_disable(sc);
if (sc->protocol->disable)
sc->protocol->disable(sc);
pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
break;
}
sc->sc_state = newstate;
sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
return (0);
}
int
pms_enable(void *v)
{
struct pms_softc *sc = v;
int rv;
rw_enter_write(&sc->sc_state_lock);
rv = pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
rw_exit_write(&sc->sc_state_lock);
return (rv);
}
void
pms_disable(void *v)
{
struct pms_softc *sc = v;
rw_enter_write(&sc->sc_state_lock);
pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
rw_exit_write(&sc->sc_state_lock);
}
int
pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct pms_softc *sc = v;
if (sc->protocol->ioctl)
return (sc->protocol->ioctl(sc, cmd, data, flag, p));
else
return (-1);
}
int
pms_sec_enable(void *v)
{
struct pms_softc *sc = v;
int rv;
rw_enter_write(&sc->sc_state_lock);
rv = pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY);
rw_exit_write(&sc->sc_state_lock);
return (rv);
}
void
pms_sec_disable(void *v)
{
struct pms_softc *sc = v;
rw_enter_write(&sc->sc_state_lock);
pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
rw_exit_write(&sc->sc_state_lock);
}
int
pms_sec_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
switch (cmd) {
case WSMOUSEIO_GTYPE:
*(u_int *)data = WSMOUSE_TYPE_PS2;
break;
default:
return (-1);
}
return (0);
}
#ifdef DIAGNOSTIC
static inline void
pms_print_packet(struct pms_softc *sc)
{
int i, state, size;
state = sc->inputstate;
size = sc->protocol->packetsize;
for (i = 0; i < size; i++)
printf(i == state ? " %02x |" : " %02x", sc->packet[i]);
}
#endif
void
pmsinput(void *vsc, int data)
{
struct pms_softc *sc = vsc;
if (sc->sc_state != PMS_STATE_ENABLED) {
/* Interrupts are not expected. Discard the byte. */
return;
}
sc->packet[sc->inputstate] = data;
pms_reset_detect(sc, data);
if (sc->protocol->sync(sc, data)) {
#ifdef DIAGNOSTIC
printf("%s: not in sync yet, discard input "
"(state = %d,",
DEVNAME(sc), sc->inputstate);
pms_print_packet(sc);
printf(")\n");
#endif
sc->inputstate = 0;
return;
}
sc->inputstate++;
if (sc->inputstate != sc->protocol->packetsize)
return;
sc->inputstate = 0;
sc->protocol->proc(sc);
}
int
synaptics_set_mode(struct pms_softc *sc, int mode, int rate)
{
struct synaptics_softc *syn = sc->synaptics;
if (pms_spec_cmd(sc, mode) ||
pms_set_rate(sc, rate == 0 ? SYNAPTICS_CMD_SET_MODE : rate))
return (-1);
/*
* Make sure that the set mode command has finished.
* Otherwise enabling the device before that will make it fail.
*/
delay(10000);
if (rate == 0)
syn->mode = mode;
return (0);
}
int
synaptics_query(struct pms_softc *sc, int query, int *val)
{
u_char resp[3];
if (pms_spec_cmd(sc, query) ||
pms_get_status(sc, resp))
return (-1);
if (val)
*val = (resp[0] << 16) | (resp[1] << 8) | resp[2];
return (0);
}
int
synaptics_get_hwinfo(struct pms_softc *sc)
{
struct synaptics_softc *syn = sc->synaptics;
struct wsmousehw *hw;
int resolution = 0, max_coords = 0, min_coords = 0;
hw = wsmouse_get_hw(sc->sc_wsmousedev);
if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify))
return (-1);
if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES,
&syn->capabilities))
return (-1);
if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model))
return (-1);
if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) &&
synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model))
return (-1);
if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) &&
synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES,
&syn->ext_capabilities))
return (-1);
if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) &&
synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &resolution))
return (-1);
if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) &&
(syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_COORDS) &&
synaptics_query(sc, SYNAPTICS_QUE_EXT_MAX_COORDS, &max_coords))
return (-1);
if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 7 ||
SYNAPTICS_ID_FULL(syn->identify) == 0x801) &&
(syn->ext_capabilities & SYNAPTICS_EXT_CAP_MIN_COORDS) &&
synaptics_query(sc, SYNAPTICS_QUE_EXT_MIN_COORDS, &min_coords))
return (-1);
if (SYNAPTICS_ID_FULL(syn->identify) >= 0x705) {
if (synaptics_query(sc, SYNAPTICS_QUE_MODES, &syn->modes))
return (-1);
if ((syn->modes & SYNAPTICS_EXT2_CAP) &&
synaptics_query(sc, SYNAPTICS_QUE_EXT2_CAPABILITIES,
&syn->ext2_capabilities))
return (-1);
}
if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) &&
!(syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK)
&& mouse_has_softbtn)
hw->type = WSMOUSE_TYPE_SYNAP_SBTN;
else
hw->type = WSMOUSE_TYPE_SYNAPTICS;
hw->hw_type = (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD)
? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD;
if (resolution & SYNAPTICS_RESOLUTION_VALID) {
hw->h_res = SYNAPTICS_RESOLUTION_X(resolution);
hw->v_res = SYNAPTICS_RESOLUTION_Y(resolution);
}
hw->x_min = (min_coords ?
SYNAPTICS_X_LIMIT(min_coords) : SYNAPTICS_XMIN_BEZEL);
hw->y_min = (min_coords ?
SYNAPTICS_Y_LIMIT(min_coords) : SYNAPTICS_YMIN_BEZEL);
hw->x_max = (max_coords ?
SYNAPTICS_X_LIMIT(max_coords) : SYNAPTICS_XMAX_BEZEL);
hw->y_max = (max_coords ?
SYNAPTICS_Y_LIMIT(max_coords) : SYNAPTICS_YMAX_BEZEL);
hw->contacts_max = SYNAPTICS_MAX_FINGERS;
syn->sec_buttons = 0;
if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8)
syn->ext_model &= ~0xf000;
if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) {
printf("%s: don't support Synaptics OLDABS\n", DEVNAME(sc));
return (-1);
}
if ((SYNAPTICS_ID_MAJOR(syn->identify) == 5) &&
(SYNAPTICS_ID_MINOR(syn->identify) == 9))
syn->mask = SYNAPTICS_MASK_NEWABS_RELAXED;
else
syn->mask = SYNAPTICS_MASK_NEWABS_STRICT;
return (0);
}
void
synaptics_sec_proc(struct pms_softc *sc)
{
struct synaptics_softc *syn = sc->synaptics;
u_int buttons;
int dx, dy;
if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
return;
buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK];
buttons |= syn->sec_buttons;
dx = (sc->packet[1] & PMS_PS2_XNEG) ?
(int)sc->packet[4] - 256 : sc->packet[4];
dy = (sc->packet[1] & PMS_PS2_YNEG) ?
(int)sc->packet[5] - 256 : sc->packet[5];
WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
}
int
synaptics_knock(struct pms_softc *sc)
{
u_char resp[3];
if (pms_set_resolution(sc, 0) ||
pms_set_resolution(sc, 0) ||
pms_set_resolution(sc, 0) ||
pms_set_resolution(sc, 0) ||
pms_get_status(sc, resp) ||
resp[1] != SYNAPTICS_ID_MAGIC)
return (-1);
return (0);
}
int
pms_enable_synaptics(struct pms_softc *sc)
{
struct synaptics_softc *syn = sc->synaptics;
struct wsmousedev_attach_args a;
int mode, i;
if (synaptics_knock(sc)) {
if (sc->synaptics == NULL)
goto err;
/*
* Some synaptics touchpads don't resume quickly.
* Retry a few times.
*/
for (i = 10; i > 0; --i) {
printf("%s: device not resuming, retrying\n",
DEVNAME(sc));
pms_reset(sc);
if (synaptics_knock(sc) == 0)
break;
delay(100000);
}
if (i == 0) {
printf("%s: lost device\n", DEVNAME(sc));
goto err;
}
}
if (sc->synaptics == NULL) {
sc->synaptics = syn = malloc(sizeof(struct synaptics_softc),
M_DEVBUF, M_WAITOK | M_ZERO);
if (syn == NULL) {
printf("%s: synaptics: not enough memory\n",
DEVNAME(sc));
goto err;
}
if (synaptics_get_hwinfo(sc)) {
free(sc->synaptics, M_DEVBUF,
sizeof(struct synaptics_softc));
sc->synaptics = NULL;
goto err;
}
/* enable pass-through PS/2 port if supported */
if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) {
a.accessops = &pms_sec_accessops;
a.accesscookie = sc;
sc->sc_sec_wsmousedev = config_found((void *)sc, &a,
wsmousedevprint);
}
if (wsmouse_configure(sc->sc_wsmousedev, synaptics_params,
nitems(synaptics_params)))
goto err;
printf("%s: Synaptics %s, firmware %d.%d, "
"0x%x 0x%x 0x%x 0x%x 0x%x\n",
DEVNAME(sc),
(syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ?
"clickpad" : "touchpad"),
SYNAPTICS_ID_MAJOR(syn->identify),
SYNAPTICS_ID_MINOR(syn->identify),
syn->model, syn->ext_model, syn->modes,
syn->capabilities, syn->ext_capabilities);
}
/*
* Enable absolute mode, plain W-mode and "advanced gesture mode"
* (AGM), if possible. AGM, which seems to be a prerequisite for the
* extended W-mode, might not always be necessary here, but at least
* some older Synaptics models do not report finger counts without it.
*/
mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE;
if (syn->capabilities & SYNAPTICS_CAP_EXTENDED)
mode |= SYNAPTICS_W_MODE;
else if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4)
mode |= SYNAPTICS_DISABLE_GESTURE;
if (synaptics_set_mode(sc, mode, 0))
goto err;
if (SYNAPTICS_SUPPORTS_AGM(syn->ext_capabilities) &&
synaptics_set_mode(sc, SYNAPTICS_QUE_MODEL,
SYNAPTICS_CMD_SET_ADV_GESTURE_MODE))
goto err;
return (1);
err:
pms_reset(sc);
return (0);
}
int
pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
struct wsmousehw *hw;
int wsmode;
hw = wsmouse_get_hw(sc->sc_wsmousedev);
switch (cmd) {
case WSMOUSEIO_GTYPE:
*(u_int *)data = hw->type;
break;
case WSMOUSEIO_GCALIBCOORDS:
wsmc->minx = hw->x_min;
wsmc->maxx = hw->x_max;
wsmc->miny = hw->y_min;
wsmc->maxy = hw->y_max;
wsmc->swapxy = 0;
wsmc->resx = hw->h_res;
wsmc->resy = hw->v_res;
break;
case WSMOUSEIO_SETMODE:
wsmode = *(u_int *)data;
if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
return (EINVAL);
wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
break;
default:
return (-1);
}
return (0);
}
int
pms_sync_synaptics(struct pms_softc *sc, int data)
{
struct synaptics_softc *syn = sc->synaptics;
switch (sc->inputstate) {
case 0:
if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_FIRST)
return (-1);
break;
case 3:
if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_NEXT)
return (-1);
break;
}
return (0);
}
void
pms_proc_synaptics(struct pms_softc *sc)
{
struct synaptics_softc *syn = sc->synaptics;
u_int buttons;
int x, y, z, w, fingerwidth;
w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
((sc->packet[3] & 0x04) >> 2);
z = sc->packet[2];
if ((syn->capabilities & SYNAPTICS_CAP_EXTENDED) == 0) {
/*
* Emulate W mode for models that don't provide it. Bit 3
* of the w-input signals a touch ("finger"), Bit 2 and
* the "gesture" bits 1-0 can be ignored.
*/
if (w & 8)
w = 4;
else
z = w = 0;
}
if (w == 3) {
if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH)
synaptics_sec_proc(sc);
return;
}
if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
return;
if (w == 2)
return; /* EW-mode packets are not expected here. */
x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) |
sc->packet[4];
y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) |
sc->packet[5];
buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ?
WSMOUSE_BUTTON(1) : 0;
buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ?
WSMOUSE_BUTTON(3) : 0;
if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) {
buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
WSMOUSE_BUTTON(1) : 0;
} else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) {
buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
WSMOUSE_BUTTON(2) : 0;
}
if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) {
buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
WSMOUSE_BUTTON(4) : 0;
buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ?
WSMOUSE_BUTTON(5) : 0;
} else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) &&
((sc->packet[0] ^ sc->packet[3]) & 0x02)) {
if (syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK) {
/*
* Trackstick buttons on this machine are wired to the
* trackpad as extra buttons, so route the event
* through the trackstick interface as normal buttons
*/
syn->sec_buttons =
(sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(1) : 0;
syn->sec_buttons |=
(sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(3) : 0;
syn->sec_buttons |=
(sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(2) : 0;
wsmouse_buttons(
sc->sc_sec_wsmousedev, syn->sec_buttons);
wsmouse_input_sync(sc->sc_sec_wsmousedev);
return;
}
buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0;
buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0;
buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0;
buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0;
buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0;
buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0;
buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0;
buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0;
x &= ~0x0f;
y &= ~0x0f;
}
if (z) {
fingerwidth = max(w, 4);
w = (w < 2 ? w + 2 : 1);
} else {
fingerwidth = 0;
w = 0;
}
wsmouse_set(sc->sc_wsmousedev, WSMOUSE_TOUCH_WIDTH, fingerwidth, 0);
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
}
void
pms_disable_synaptics(struct pms_softc *sc)
{
struct synaptics_softc *syn = sc->synaptics;
if (syn->capabilities & SYNAPTICS_CAP_SLEEP)
synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE |
SYNAPTICS_DISABLE_GESTURE, 0);
}
int
alps_sec_proc(struct pms_softc *sc)
{
struct alps_softc *alps = sc->alps;
int dx, dy, pos = 0;
if ((sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) {
/*
* We need to keep buttons states because interleaved
* packets only signalize x/y movements.
*/
alps->sec_buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
} else if ((sc->packet[3] & PMS_ALPS_INTERLEAVED_MASK) ==
PMS_ALPS_INTERLEAVED_VALID) {
sc->inputstate = 3;
pos = 3;
} else {
return (0);
}
if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
return (1);
dx = (sc->packet[pos] & PMS_PS2_XNEG) ?
(int)sc->packet[pos + 1] - 256 : sc->packet[pos + 1];
dy = (sc->packet[pos] & PMS_PS2_YNEG) ?
(int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2];
WSMOUSE_INPUT(sc->sc_sec_wsmousedev, alps->sec_buttons, dx, dy, 0, 0);
return (1);
}
int
alps_get_hwinfo(struct pms_softc *sc)
{
struct alps_softc *alps = sc->alps;
u_char resp[3];
int i;
struct wsmousehw *hw;
if (pms_set_resolution(sc, 0) ||
pms_set_scaling(sc, 2) ||
pms_set_scaling(sc, 2) ||
pms_set_scaling(sc, 2) ||
pms_get_status(sc, resp)) {
DPRINTF("%s: alps: model query error\n", DEVNAME(sc));
return (-1);
}
alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1);
for (i = 0; i < nitems(alps_models); i++)
if (alps->version == alps_models[i].version) {
alps->model = alps_models[i].model;
alps->mask = alps_models[i].mask;
hw = wsmouse_get_hw(sc->sc_wsmousedev);
hw->type = WSMOUSE_TYPE_ALPS;
hw->hw_type = WSMOUSEHW_TOUCHPAD;
hw->x_min = ALPS_XMIN_BEZEL;
hw->y_min = ALPS_YMIN_BEZEL;
hw->x_max = ALPS_XMAX_BEZEL;
hw->y_max = ALPS_YMAX_BEZEL;
hw->contacts_max = 1;
return (0);
}
return (-1);
}
int
pms_enable_alps(struct pms_softc *sc)
{
struct alps_softc *alps = sc->alps;
struct wsmousedev_attach_args a;
u_char resp[3];
if (pms_set_resolution(sc, 0) ||
pms_set_scaling(sc, 1) ||
pms_set_scaling(sc, 1) ||
pms_set_scaling(sc, 1) ||
pms_get_status(sc, resp) ||
resp[0] != PMS_ALPS_MAGIC1 ||
resp[1] != PMS_ALPS_MAGIC2 ||
(resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2 &&
resp[2] != PMS_ALPS_MAGIC3_3))
goto err;
if (sc->alps == NULL) {
sc->alps = alps = malloc(sizeof(struct alps_softc),
M_DEVBUF, M_WAITOK | M_ZERO);
if (alps == NULL) {
printf("%s: alps: not enough memory\n", DEVNAME(sc));
goto err;
}
if (alps_get_hwinfo(sc)) {
free(sc->alps, M_DEVBUF, sizeof(struct alps_softc));
sc->alps = NULL;
goto err;
}
if (wsmouse_configure(sc->sc_wsmousedev, alps_params,
nitems(alps_params))) {
free(sc->alps, M_DEVBUF, sizeof(struct alps_softc));
sc->alps = NULL;
printf("%s: setup failed\n", DEVNAME(sc));
goto err;
}
printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc),
(alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"),
alps->version);
if (alps->model & ALPS_DUALPOINT) {
a.accessops = &pms_sec_accessops;
a.accesscookie = sc;
sc->sc_sec_wsmousedev = config_found((void *)sc, &a,
wsmousedevprint);
}
}
if (alps->model == 0)
goto err;
if ((alps->model & ALPS_PASSTHROUGH) &&
(pms_set_scaling(sc, 2) ||
pms_set_scaling(sc, 2) ||
pms_set_scaling(sc, 2) ||
pms_dev_disable(sc))) {
DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc));
goto err;
}
if (pms_dev_disable(sc) ||
pms_dev_disable(sc) ||
pms_set_rate(sc, 0x0a)) {
DPRINTF("%s: alps: tapping error\n", DEVNAME(sc));
goto err;
}
if (pms_dev_disable(sc) ||
pms_dev_disable(sc) ||
pms_dev_disable(sc) ||
pms_dev_disable(sc) ||
pms_dev_enable(sc)) {
DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc));
goto err;
}
if ((alps->model & ALPS_PASSTHROUGH) &&
(pms_set_scaling(sc, 1) ||
pms_set_scaling(sc, 1) ||
pms_set_scaling(sc, 1) ||
pms_dev_disable(sc))) {
DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc));
goto err;
}
alps->sec_buttons = 0;
return (1);
err:
pms_reset(sc);
return (0);
}
int
pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
int wsmode;
struct wsmousehw *hw;
switch (cmd) {
case WSMOUSEIO_GTYPE:
*(u_int *)data = WSMOUSE_TYPE_ALPS;
break;
case WSMOUSEIO_GCALIBCOORDS:
hw = wsmouse_get_hw(sc->sc_wsmousedev);
wsmc->minx = hw->x_min;
wsmc->maxx = hw->x_max;
wsmc->miny = hw->y_min;
wsmc->maxy = hw->y_max;
wsmc->swapxy = 0;
break;
case WSMOUSEIO_SETMODE:
wsmode = *(u_int *)data;
if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
return (EINVAL);
wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
break;
default:
return (-1);
}
return (0);
}
int
pms_sync_alps(struct pms_softc *sc, int data)
{
struct alps_softc *alps = sc->alps;
if ((alps->model & ALPS_DUALPOINT) &&
(sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) {
if (sc->inputstate == 2)
sc->inputstate += 3;
return (0);
}
switch (sc->inputstate) {
case 0:
if ((data & alps->mask) != alps->mask)
return (-1);
break;
case 1:
case 2:
case 3:
if ((data & PMS_ALPS_MASK) != PMS_ALPS_VALID)
return (-1);
break;
case 4:
case 5:
if ((alps->model & ALPS_INTERLEAVED) == 0 &&
(data & PMS_ALPS_MASK) != PMS_ALPS_VALID)
return (-1);
break;
}
return (0);
}
void
pms_proc_alps(struct pms_softc *sc)
{
struct alps_softc *alps = sc->alps;
int x, y, z, dx, dy;
u_int buttons, gesture;
if ((alps->model & ALPS_DUALPOINT) && alps_sec_proc(sc))
return;
x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4);
y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3);
z = sc->packet[5];
buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) |
((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) |
((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0);
if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) && z == ALPS_Z_MAGIC) {
dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x;
dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y;
WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
return;
}
if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
return;
/*
* XXX The Y-axis is in the oposit direction compared to
* Synaptics touchpads and PS/2 mouses.
* It's why we need to translate the y value here for both
* NATIVE and COMPAT modes.
*/
y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL;
if (alps->gesture == ALPS_TAP) {
/* Report a touch with the tap coordinates. */
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
alps->old_x, alps->old_y, ALPS_PRESSURE, 0);
if (z > 0) {
/*
* The hardware doesn't send a null pressure
* event when dragging starts.
*/
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
alps->old_x, alps->old_y, 0, 0);
}
}
gesture = sc->packet[2] & 0x03;
if (gesture != ALPS_TAP)
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, 0);
if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP)
alps->gesture = gesture;
alps->old_x = x;
alps->old_y = y;
}
int
elantech_set_absolute_mode_v1(struct pms_softc *sc)
{
int i;
u_char resp[3];
/* Enable absolute mode. Magic numbers from Linux driver. */
if (pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
pms_spec_cmd(sc, 0x10) ||
pms_spec_cmd(sc, 0x16) ||
pms_set_scaling(sc, 1) ||
pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
pms_spec_cmd(sc, 0x11) ||
pms_spec_cmd(sc, 0x8f) ||
pms_set_scaling(sc, 1))
return (-1);
/* Read back reg 0x10 to ensure hardware is ready. */
for (i = 0; i < 5; i++) {
if (pms_spec_cmd(sc, ELANTECH_CMD_READ_REG) ||
pms_spec_cmd(sc, 0x10) ||
pms_get_status(sc, resp) == 0)
break;
delay(2000);
}
if (i == 5)
return (-1);
if ((resp[0] & ELANTECH_ABSOLUTE_MODE) == 0)
return (-1);
return (0);
}
int
elantech_set_absolute_mode_v2(struct pms_softc *sc)
{
int i;
u_char resp[3];
u_char reg10 = (sc->elantech->fw_version == 0x20030 ? 0x54 : 0xc4);
/* Enable absolute mode. Magic numbers from Linux driver. */
if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x10) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, reg10) ||
pms_set_scaling(sc, 1) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x11) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x88) ||
pms_set_scaling(sc, 1))
return (-1);
/* Read back reg 0x10 to ensure hardware is ready. */
for (i = 0; i < 5; i++) {
if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_READ_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x10) ||
pms_get_status(sc, resp) == 0)
break;
delay(2000);
}
if (i == 5)
return (-1);
return (0);
}
int
elantech_set_absolute_mode_v3(struct pms_softc *sc)
{
int i;
u_char resp[3];
/* Enable absolute mode. Magic numbers from Linux driver. */
if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x10) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x0b) ||
pms_set_scaling(sc, 1))
return (-1);
/* Read back reg 0x10 to ensure hardware is ready. */
for (i = 0; i < 5; i++) {
if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x10) ||
pms_get_status(sc, resp) == 0)
break;
delay(2000);
}
if (i == 5)
return (-1);
return (0);
}
int
elantech_set_absolute_mode_v4(struct pms_softc *sc)
{
/* Enable absolute mode. Magic numbers from Linux driver. */
if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x07) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
elantech_ps2_cmd(sc, 0x01) ||
pms_set_scaling(sc, 1))
return (-1);
/* v4 has no register 0x10 to read response from */
return (0);
}
int
elantech_get_hwinfo_v1(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
struct wsmousehw *hw;
int fw_version;
u_char capabilities[3];
if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
return (-1);
if (fw_version < 0x20030 || fw_version == 0x20600) {
if (fw_version < 0x20000)
elantech->flags |= ELANTECH_F_HW_V1_OLD;
} else
return (-1);
elantech->fw_version = fw_version;
if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
pms_get_status(sc, capabilities))
return (-1);
if (capabilities[0] & ELANTECH_CAP_HAS_ROCKER)
elantech->flags |= ELANTECH_F_HAS_ROCKER;
if (elantech_set_absolute_mode_v1(sc))
return (-1);
hw = wsmouse_get_hw(sc->sc_wsmousedev);
hw->type = WSMOUSE_TYPE_ELANTECH;
hw->hw_type = WSMOUSEHW_TOUCHPAD;
hw->x_min = ELANTECH_V1_X_MIN;
hw->x_max = ELANTECH_V1_X_MAX;
hw->y_min = ELANTECH_V1_Y_MIN;
hw->y_max = ELANTECH_V1_Y_MAX;
return (0);
}
int
elantech_get_hwinfo_v2(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
struct wsmousehw *hw;
int fw_version, ic_ver;
u_char capabilities[3];
int i, fixed_dpi;
u_char resp[3];
if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
return (-1);
ic_ver = (fw_version & 0x0f0000) >> 16;
if (ic_ver != 2 && ic_ver != 4)
return (-1);
elantech->fw_version = fw_version;
if (fw_version >= 0x20800)
elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
pms_get_status(sc, capabilities))
return (-1);
if (elantech_set_absolute_mode_v2(sc))
return (-1);
hw = wsmouse_get_hw(sc->sc_wsmousedev);
hw->type = WSMOUSE_TYPE_ELANTECH;
hw->hw_type = WSMOUSEHW_TOUCHPAD;
if (fw_version == 0x20800 || fw_version == 0x20b00 ||
fw_version == 0x20030) {
hw->x_max = ELANTECH_V2_X_MAX;
hw->y_max = ELANTECH_V2_Y_MAX;
} else {
if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
pms_get_status(sc, resp))
return (-1);
fixed_dpi = resp[1] & 0x10;
i = (fw_version > 0x20800 && fw_version < 0x20900) ? 1 : 2;
if ((fw_version >> 16) == 0x14 && fixed_dpi) {
if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE) ||
pms_get_status(sc, resp))
return (-1);
hw->x_max = (capabilities[1] - i) * resp[1] / 2;
hw->y_max = (capabilities[2] - i) * resp[2] / 2;
} else if (fw_version == 0x040216) {
hw->x_max = 819;
hw->y_max = 405;
} else if (fw_version == 0x040219 || fw_version == 0x040215) {
hw->x_max = 900;
hw->y_max = 500;
} else {
hw->x_max = (capabilities[1] - i) * 64;
hw->y_max = (capabilities[2] - i) * 64;
}
}
return (0);
}
int
elantech_get_hwinfo_v3(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
struct wsmousehw *hw;
int fw_version;
u_char resp[3];
if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
return (-1);
if (((fw_version & 0x0f0000) >> 16) != 5)
return (-1);
elantech->fw_version = fw_version;
elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
if ((fw_version & 0x4000) == 0x4000)
elantech->flags |= ELANTECH_F_CRC_ENABLED;
if (elantech_set_absolute_mode_v3(sc))
return (-1);
if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
pms_get_status(sc, resp))
return (-1);
hw = wsmouse_get_hw(sc->sc_wsmousedev);
hw->x_max = elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1];
hw->y_max = elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2];
hw->type = WSMOUSE_TYPE_ELANTECH;
hw->hw_type = WSMOUSEHW_TOUCHPAD;
return (0);
}
int
elantech_get_hwinfo_v4(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
struct wsmousehw *hw;
int fw_version;
u_char capabilities[3];
u_char resp[3];
if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
return (-1);
if ((fw_version & 0x0f0000) >> 16 < 6)
return (-1);
elantech->fw_version = fw_version;
elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
if ((fw_version & 0x4000) == 0x4000)
elantech->flags |= ELANTECH_F_CRC_ENABLED;
if (elantech_set_absolute_mode_v4(sc))
return (-1);
if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
pms_get_status(sc, capabilities))
return (-1);
if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
pms_get_status(sc, resp))
return (-1);
hw = wsmouse_get_hw(sc->sc_wsmousedev);
hw->x_max = (resp[0] & 0x0f) << 8 | resp[1];
hw->y_max = (resp[0] & 0xf0) << 4 | resp[2];
if ((capabilities[1] < 2) || (capabilities[1] > hw->x_max))
return (-1);
if (capabilities[0] & ELANTECH_CAP_TRACKPOINT)
elantech->flags |= ELANTECH_F_TRACKPOINT;
hw->type = WSMOUSE_TYPE_ELANTECH;
hw->hw_type = (ELANTECH_IS_CLICKPAD(sc)
? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD);
hw->mt_slots = ELANTECH_MAX_FINGERS;
elantech->width = hw->x_max / (capabilities[1] - 1);
return (0);
}
int
elantech_ps2_cmd(struct pms_softc *sc, u_char command)
{
u_char cmd[1];
cmd[0] = command;
return (pms_cmd(sc, cmd, 1, NULL, 0));
}
int
elantech_knock(struct pms_softc *sc)
{
u_char resp[3];
if (pms_dev_disable(sc) ||
pms_set_scaling(sc, 1) ||
pms_set_scaling(sc, 1) ||
pms_set_scaling(sc, 1) ||
pms_get_status(sc, resp) ||
resp[0] != PMS_ELANTECH_MAGIC1 ||
resp[1] != PMS_ELANTECH_MAGIC2 ||
(resp[2] != PMS_ELANTECH_MAGIC3_1 &&
resp[2] != PMS_ELANTECH_MAGIC3_2))
return (-1);
return (0);
}
int
pms_enable_elantech_v1(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
int i;
if (elantech_knock(sc))
goto err;
if (sc->elantech == NULL) {
sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
M_DEVBUF, M_WAITOK | M_ZERO);
if (elantech == NULL) {
printf("%s: elantech: not enough memory\n",
DEVNAME(sc));
goto err;
}
if (elantech_get_hwinfo_v1(sc)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
goto err;
}
if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
printf("%s: elantech: setup failed\n", DEVNAME(sc));
goto err;
}
printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n",
DEVNAME(sc), 1, sc->elantech->fw_version);
} else if (elantech_set_absolute_mode_v1(sc))
goto err;
for (i = 0; i < nitems(sc->elantech->parity); i++)
sc->elantech->parity[i] = sc->elantech->parity[i & (i - 1)] ^ 1;
return (1);
err:
pms_reset(sc);
return (0);
}
int
pms_enable_elantech_v2(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
if (elantech_knock(sc))
goto err;
if (sc->elantech == NULL) {
sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
M_DEVBUF, M_WAITOK | M_ZERO);
if (elantech == NULL) {
printf("%s: elantech: not enough memory\n",
DEVNAME(sc));
goto err;
}
if (elantech_get_hwinfo_v2(sc)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
goto err;
}
if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
printf("%s: elantech: setup failed\n", DEVNAME(sc));
goto err;
}
printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n",
DEVNAME(sc), 2, sc->elantech->fw_version);
} else if (elantech_set_absolute_mode_v2(sc))
goto err;
return (1);
err:
pms_reset(sc);
return (0);
}
int
pms_enable_elantech_v3(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
if (elantech_knock(sc))
goto err;
if (sc->elantech == NULL) {
sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
M_DEVBUF, M_WAITOK | M_ZERO);
if (elantech == NULL) {
printf("%s: elantech: not enough memory\n",
DEVNAME(sc));
goto err;
}
if (elantech_get_hwinfo_v3(sc)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
goto err;
}
if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
printf("%s: elantech: setup failed\n", DEVNAME(sc));
goto err;
}
printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n",
DEVNAME(sc), 3, sc->elantech->fw_version);
} else if (elantech_set_absolute_mode_v3(sc))
goto err;
return (1);
err:
pms_reset(sc);
return (0);
}
int
pms_enable_elantech_v4(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
struct wsmousedev_attach_args a;
if (elantech_knock(sc))
goto err;
if (sc->elantech == NULL) {
sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
M_DEVBUF, M_WAITOK | M_ZERO);
if (elantech == NULL) {
printf("%s: elantech: not enough memory\n",
DEVNAME(sc));
goto err;
}
if (elantech_get_hwinfo_v4(sc)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
goto err;
}
if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
free(sc->elantech, M_DEVBUF,
sizeof(struct elantech_softc));
sc->elantech = NULL;
printf("%s: elantech: setup failed\n", DEVNAME(sc));
goto err;
}
printf("%s: Elantech %s, version 4, firmware 0x%x\n",
DEVNAME(sc), (ELANTECH_IS_CLICKPAD(sc) ? "Clickpad"
: "Touchpad"), sc->elantech->fw_version);
if (sc->elantech->flags & ELANTECH_F_TRACKPOINT) {
a.accessops = &pms_sec_accessops;
a.accesscookie = sc;
sc->sc_sec_wsmousedev = config_found((void *) sc, &a,
wsmousedevprint);
}
} else if (elantech_set_absolute_mode_v4(sc))
goto err;
return (1);
err:
pms_reset(sc);
return (0);
}
int
pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
struct wsmousehw *hw;
int wsmode;
switch (cmd) {
case WSMOUSEIO_GTYPE:
*(u_int *)data = WSMOUSE_TYPE_ELANTECH;
break;
case WSMOUSEIO_GCALIBCOORDS:
hw = wsmouse_get_hw(sc->sc_wsmousedev);
wsmc->minx = hw->x_min;
wsmc->maxx = hw->x_max;
wsmc->miny = hw->y_min;
wsmc->maxy = hw->y_max;
wsmc->swapxy = 0;
wsmc->resx = hw->h_res;
wsmc->resy = hw->v_res;
break;
case WSMOUSEIO_SETMODE:
wsmode = *(u_int *)data;
if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
return (EINVAL);
wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
break;
default:
return (-1);
}
return (0);
}
int
pms_sync_elantech_v1(struct pms_softc *sc, int data)
{
struct elantech_softc *elantech = sc->elantech;
u_char p;
switch (sc->inputstate) {
case 0:
if (elantech->flags & ELANTECH_F_HW_V1_OLD) {
elantech->p1 = (data & 0x20) >> 5;
elantech->p2 = (data & 0x10) >> 4;
} else {
elantech->p1 = (data & 0x10) >> 4;
elantech->p2 = (data & 0x20) >> 5;
}
elantech->p3 = (data & 0x04) >> 2;
return (0);
case 1:
p = elantech->p1;
break;
case 2:
p = elantech->p2;
break;
case 3:
p = elantech->p3;
break;
default:
return (-1);
}
if (data < 0 || data >= nitems(elantech->parity) ||
/*
* FW 0x20022 sends inverted parity bits on cold boot, returning
* to normal after suspend & resume, so the parity check is
* disabled for this one.
*/
(elantech->fw_version != 0x20022 && elantech->parity[data] != p))
return (-1);
return (0);
}
int
pms_sync_elantech_v2(struct pms_softc *sc, int data)
{
struct elantech_softc *elantech = sc->elantech;
/* Variants reporting pressure always have the same constant bits. */
if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) {
if (sc->inputstate == 0 && (data & 0x0c) != 0x04)
return (-1);
if (sc->inputstate == 3 && (data & 0x0f) != 0x02)
return (-1);
return (0);
}
/* For variants not reporting pressure, 1 and 3 finger touch packets
* have different constant bits than 2 finger touch packets. */
switch (sc->inputstate) {
case 0:
if ((data & 0xc0) == 0x80) {
if ((data & 0x0c) != 0x0c)
return (-1);
elantech->flags |= ELANTECH_F_2FINGER_PACKET;
} else {
if ((data & 0x3c) != 0x3c)
return (-1);
elantech->flags &= ~ELANTECH_F_2FINGER_PACKET;
}
break;
case 1:
case 4:
if (elantech->flags & ELANTECH_F_2FINGER_PACKET)
break;
if ((data & 0xf0) != 0x00)
return (-1);
break;
case 3:
if (elantech->flags & ELANTECH_F_2FINGER_PACKET) {
if ((data & 0x0e) != 0x08)
return (-1);
} else {
if ((data & 0x3e) != 0x38)
return (-1);
}
break;
default:
break;
}
return (0);
}
int
pms_sync_elantech_v3(struct pms_softc *sc, int data)
{
struct elantech_softc *elantech = sc->elantech;
switch (sc->inputstate) {
case 0:
if (elantech->flags & ELANTECH_F_CRC_ENABLED)
break;
if ((data & 0x0c) != 0x04 && (data & 0x0c) != 0x0c)
return (-1);
break;
case 3:
if (elantech->flags & ELANTECH_F_CRC_ENABLED) {
if ((data & 0x09) != 0x08 && (data & 0x09) != 0x09)
return (-1);
} else {
if ((data & 0xcf) != 0x02 && (data & 0xce) != 0x0c)
return (-1);
}
break;
}
return (0);
}
/* Extract the type bits from packet[3]. */
static inline int
elantech_packet_type(struct elantech_softc *elantech, u_char b)
{
/*
* This looks dubious, but in the "crc-enabled" format bit 2 may
* be set even in MOTION packets.
*/
if ((elantech->flags & ELANTECH_F_TRACKPOINT) && ((b & 0x0f) == 0x06))
return (ELANTECH_PKT_TRACKPOINT);
else
return (b & 0x03);
}
int
pms_sync_elantech_v4(struct pms_softc *sc, int data)
{
if (sc->inputstate == 0)
return ((data & 0x08) == 0 ? 0 : -1);
if (sc->inputstate == 3) {
switch (elantech_packet_type(sc->elantech, data)) {
case ELANTECH_V4_PKT_STATUS:
case ELANTECH_V4_PKT_HEAD:
case ELANTECH_V4_PKT_MOTION:
if (sc->elantech->flags & ELANTECH_F_CRC_ENABLED)
return ((data & 0x08) == 0 ? 0 : -1);
else
return ((data & 0x1c) == 0x10 ? 0 : -1);
case ELANTECH_PKT_TRACKPOINT:
return ((sc->packet[0] & 0xc8) == 0
&& sc->packet[1] == ((data & 0x10) << 3)
&& sc->packet[2] == ((data & 0x20) << 2)
&& (data ^ (sc->packet[0] & 0x30)) == 0x36
? 0 : -1);
}
return (-1);
}
return (0);
}
void
pms_proc_elantech_v1(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
int x, y, w, z;
u_int buttons;
buttons = butmap[sc->packet[0] & 3];
if (elantech->flags & ELANTECH_F_HAS_ROCKER) {
if (sc->packet[0] & 0x40) /* up */
buttons |= WSMOUSE_BUTTON(4);
if (sc->packet[0] & 0x80) /* down */
buttons |= WSMOUSE_BUTTON(5);
}
if (elantech->flags & ELANTECH_F_HW_V1_OLD)
w = ((sc->packet[1] & 0x80) >> 7) +
((sc->packet[1] & 0x30) >> 4);
else
w = (sc->packet[0] & 0xc0) >> 6;
/*
* Firmwares 0x20022 and 0x20600 have a bug, position data in the
* first two reports for single-touch contacts may be corrupt.
*/
if (elantech->fw_version == 0x20022 ||
elantech->fw_version == 0x20600) {
if (w == 1) {
if (elantech->initial_pkt < 2) {
elantech->initial_pkt++;
return;
}
} else if (elantech->initial_pkt) {
elantech->initial_pkt = 0;
}
}
/* Hardware version 1 doesn't report pressure. */
if (w) {
x = ((sc->packet[1] & 0x0c) << 6) | sc->packet[2];
y = ((sc->packet[1] & 0x03) << 8) | sc->packet[3];
z = SYNAPTICS_PRESSURE;
} else {
x = y = z = 0;
}
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
}
void
pms_proc_elantech_v2(struct pms_softc *sc)
{
const u_char debounce_pkt[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
struct elantech_softc *elantech = sc->elantech;
int x, y, w, z;
u_int buttons;
/*
* The hardware sends this packet when in debounce state.
* The packet should be ignored.
*/
if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
return;
buttons = butmap[sc->packet[0] & 3];
w = (sc->packet[0] & 0xc0) >> 6;
if (w == 1 || w == 3) {
x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
z = ((sc->packet[1] & 0xf0) |
(sc->packet[4] & 0xf0) >> 4);
else
z = SYNAPTICS_PRESSURE;
} else if (w == 2) {
x = (((sc->packet[0] & 0x10) << 4) | sc->packet[1]) << 2;
y = (((sc->packet[0] & 0x20) << 3) | sc->packet[2]) << 2;
z = SYNAPTICS_PRESSURE;
} else {
x = y = z = 0;
}
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
}
void
pms_proc_elantech_v3(struct pms_softc *sc)
{
const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
struct elantech_softc *elantech = sc->elantech;
int x, y, w, z;
u_int buttons;
buttons = butmap[sc->packet[0] & 3];
x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]);
y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]);
z = 0;
w = (sc->packet[0] & 0xc0) >> 6;
if (w == 2) {
/*
* Two-finger touch causes two packets -- a head packet
* and a tail packet. We report a single event and ignore
* the tail packet.
*/
if (elantech->flags & ELANTECH_F_CRC_ENABLED) {
if ((sc->packet[3] & 0x09) != 0x08)
return;
} else {
/* The hardware sends this packet when in debounce state.
* The packet should be ignored. */
if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
return;
if ((sc->packet[0] & 0x0c) != 0x04 &&
(sc->packet[3] & 0xcf) != 0x02) {
/* not the head packet -- ignore */
return;
}
}
}
/* Prevent jumping cursor if pad isn't touched or reports garbage. */
if (w == 0 ||
((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y)
&& (x != elantech->old_x || y != elantech->old_y))) {
x = elantech->old_x;
y = elantech->old_y;
}
if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
z = (sc->packet[1] & 0xf0) | ((sc->packet[4] & 0xf0) >> 4);
else if (w)
z = SYNAPTICS_PRESSURE;
WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
elantech->old_x = x;
elantech->old_y = y;
}
void
pms_proc_elantech_v4(struct pms_softc *sc)
{
struct elantech_softc *elantech = sc->elantech;
struct device *sc_wsmousedev = sc->sc_wsmousedev;
int id, weight, n, x, y, z;
u_int buttons, slots;
switch (elantech_packet_type(elantech, sc->packet[3])) {
case ELANTECH_V4_PKT_STATUS:
slots = elantech->mt_slots;
elantech->mt_slots = sc->packet[1] & 0x1f;
slots &= ~elantech->mt_slots;
for (id = 0; slots; id++, slots >>= 1) {
if (slots & 1)
wsmouse_mtstate(sc_wsmousedev, id, 0, 0, 0);
}
break;
case ELANTECH_V4_PKT_HEAD:
id = ((sc->packet[3] & 0xe0) >> 5) - 1;
if (id > -1 && id < ELANTECH_MAX_FINGERS) {
x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
z = (sc->packet[1] & 0xf0)
| ((sc->packet[4] & 0xf0) >> 4);
wsmouse_mtstate(sc_wsmousedev, id, x, y, z);
}
break;
case ELANTECH_V4_PKT_MOTION:
weight = (sc->packet[0] & 0x10) ? ELANTECH_V4_WEIGHT_VALUE : 1;
for (n = 0; n < 6; n += 3) {
id = ((sc->packet[n] & 0xe0) >> 5) - 1;
if (id < 0 || id >= ELANTECH_MAX_FINGERS)
continue;
x = weight * (signed char)sc->packet[n + 1];
y = weight * (signed char)sc->packet[n + 2];
z = WSMOUSE_DEFAULT_PRESSURE;
wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_X, x, id);
wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_Y, y, id);
wsmouse_set(sc_wsmousedev, WSMOUSE_MT_PRESSURE, z, id);
}
break;
case ELANTECH_PKT_TRACKPOINT:
if (sc->sc_dev_enable & PMS_DEV_SECONDARY) {
/*
* This firmware misreport coordinates for trackpoint
* occasionally. Discard packets outside of [-127, 127] range
* to prevent cursor jumps.
*/
if (sc->packet[4] == 0x80 || sc->packet[5] == 0x80 ||
sc->packet[1] >> 7 == sc->packet[4] >> 7 ||
sc->packet[2] >> 7 == sc->packet[5] >> 7)
return;
x = sc->packet[4] - 0x100 + (sc->packet[1] << 1);
y = sc->packet[5] - 0x100 + (sc->packet[2] << 1);
buttons = butmap[sc->packet[0] & 7];
WSMOUSE_INPUT(sc->sc_sec_wsmousedev,
buttons, x, y, 0, 0);
}
return;
default:
printf("%s: unknown packet type 0x%x\n", DEVNAME(sc),
sc->packet[3] & 0x1f);
return;
}
buttons = butmap[sc->packet[0] & 3];
wsmouse_buttons(sc_wsmousedev, buttons);
wsmouse_input_sync(sc_wsmousedev);
}
2174
5
5
12
12
1840
237
394
5
1045
1043
5
573
69
201
51
51
282
282
463
465
76
77
9
9
7
7
7
6
7
7
7
7
7
7
7
5
2
5
5
5
15
2
7
5
1
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
/* $OpenBSD: kern_tc.c,v 1.77 2022/08/12 02:20:36 cheloha Exp $ */
/*
* Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* If we meet some day, and you think this stuff is worth it, you
* can buy me a beer in return. Poul-Henning Kamp
*/
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/kernel.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/stdint.h>
#include <sys/timeout.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/timetc.h>
#include <sys/queue.h>
#include <sys/malloc.h>
u_int dummy_get_timecount(struct timecounter *);
int sysctl_tc_hardware(void *, size_t *, void *, size_t);
int sysctl_tc_choice(void *, size_t *, void *, size_t);
/*
* Implement a dummy timecounter which we can use until we get a real one
* in the air. This allows the console and other early stuff to use
* time services.
*/
u_int
dummy_get_timecount(struct timecounter *tc)
{
static u_int now;
return atomic_inc_int_nv(&now);
}
static struct timecounter dummy_timecounter = {
.tc_get_timecount = dummy_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = 1000000,
.tc_name = "dummy",
.tc_quality = -1000000,
.tc_priv = NULL,
.tc_user = 0,
};
/*
* Locks used to protect struct members, global variables in this file:
* I immutable after initialization
* T tc_lock
* W windup_mtx
*/
struct timehands {
/* These fields must be initialized by the driver. */
struct timecounter *th_counter; /* [W] */
int64_t th_adjtimedelta; /* [T,W] */
struct bintime th_next_ntp_update; /* [T,W] */
int64_t th_adjustment; /* [W] */
u_int64_t th_scale; /* [W] */
u_int th_offset_count; /* [W] */
struct bintime th_boottime; /* [T,W] */
struct bintime th_offset; /* [W] */
struct bintime th_naptime; /* [W] */
struct timeval th_microtime; /* [W] */
struct timespec th_nanotime; /* [W] */
/* Fields not to be copied in tc_windup start with th_generation. */
volatile u_int th_generation; /* [W] */
struct timehands *th_next; /* [I] */
};
static struct timehands th0;
static struct timehands th1 = {
.th_next = &th0
};
static struct timehands th0 = {
.th_counter = &dummy_timecounter,
.th_scale = UINT64_MAX / 1000000,
.th_offset = { .sec = 1, .frac = 0 },
.th_generation = 1,
.th_next = &th1
};
struct rwlock tc_lock = RWLOCK_INITIALIZER("tc_lock");
/*
* tc_windup() must be called before leaving this mutex.
*/
struct mutex windup_mtx = MUTEX_INITIALIZER(IPL_CLOCK);
static struct timehands *volatile timehands = &th0; /* [W] */
struct timecounter *timecounter = &dummy_timecounter; /* [T] */
static SLIST_HEAD(, timecounter) tc_list = SLIST_HEAD_INITIALIZER(tc_list);
/*
* These are updated from tc_windup(). They are useful when
* examining kernel core dumps.
*/
volatile time_t naptime = 0;
volatile time_t time_second = 1;
volatile time_t time_uptime = 0;
static int timestepwarnings;
void ntp_update_second(struct timehands *);
void tc_windup(struct bintime *, struct bintime *, int64_t *);
/*
* Return the difference between the timehands' counter value now and what
* was when we copied it to the timehands' offset_count.
*/
static __inline u_int
tc_delta(struct timehands *th)
{
struct timecounter *tc;
tc = th->th_counter;
return ((tc->tc_get_timecount(tc) - th->th_offset_count) &
tc->tc_counter_mask);
}
/*
* Functions for reading the time. We have to loop until we are sure that
* the timehands that we operated on was not updated under our feet. See
* the comment in <sys/time.h> for a description of these functions.
*/
void
binboottime(struct bintime *bt)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
*bt = th->th_boottime;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
microboottime(struct timeval *tvp)
{
struct bintime bt;
binboottime(&bt);
BINTIME_TO_TIMEVAL(&bt, tvp);
}
void
nanoboottime(struct timespec *tsp)
{
struct bintime bt;
binboottime(&bt);
BINTIME_TO_TIMESPEC(&bt, tsp);
}
void
binuptime(struct bintime *bt)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt);
bintimeadd(bt, &th->th_offset, bt);
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
getbinuptime(struct bintime *bt)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
*bt = th->th_offset;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
nanouptime(struct timespec *tsp)
{
struct bintime bt;
binuptime(&bt);
BINTIME_TO_TIMESPEC(&bt, tsp);
}
void
microuptime(struct timeval *tvp)
{
struct bintime bt;
binuptime(&bt);
BINTIME_TO_TIMEVAL(&bt, tvp);
}
time_t
getuptime(void)
{
#if defined(__LP64__)
return time_uptime; /* atomic */
#else
time_t now;
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
now = th->th_offset.sec;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
return now;
#endif
}
uint64_t
nsecuptime(void)
{
struct bintime bt;
binuptime(&bt);
return BINTIME_TO_NSEC(&bt);
}
uint64_t
getnsecuptime(void)
{
struct bintime bt;
getbinuptime(&bt);
return BINTIME_TO_NSEC(&bt);
}
void
binruntime(struct bintime *bt)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt);
bintimeadd(bt, &th->th_offset, bt);
bintimesub(bt, &th->th_naptime, bt);
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
nanoruntime(struct timespec *ts)
{
struct bintime bt;
binruntime(&bt);
BINTIME_TO_TIMESPEC(&bt, ts);
}
void
bintime(struct bintime *bt)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
TIMECOUNT_TO_BINTIME(tc_delta(th), th->th_scale, bt);
bintimeadd(bt, &th->th_offset, bt);
bintimeadd(bt, &th->th_boottime, bt);
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
nanotime(struct timespec *tsp)
{
struct bintime bt;
bintime(&bt);
BINTIME_TO_TIMESPEC(&bt, tsp);
}
void
microtime(struct timeval *tvp)
{
struct bintime bt;
bintime(&bt);
BINTIME_TO_TIMEVAL(&bt, tvp);
}
time_t
gettime(void)
{
#if defined(__LP64__)
return time_second; /* atomic */
#else
time_t now;
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
now = th->th_microtime.tv_sec;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
return now;
#endif
}
void
getnanouptime(struct timespec *tsp)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
BINTIME_TO_TIMESPEC(&th->th_offset, tsp);
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
getmicrouptime(struct timeval *tvp)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
BINTIME_TO_TIMEVAL(&th->th_offset, tvp);
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
getnanotime(struct timespec *tsp)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
*tsp = th->th_nanotime;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
void
getmicrotime(struct timeval *tvp)
{
struct timehands *th;
u_int gen;
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
*tvp = th->th_microtime;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
/*
* Initialize a new timecounter and possibly use it.
*/
void
tc_init(struct timecounter *tc)
{
u_int64_t tmp;
u_int u;
u = tc->tc_frequency / tc->tc_counter_mask;
/* XXX: We need some margin here, 10% is a guess */
u *= 11;
u /= 10;
if (tc->tc_quality >= 0) {
if (u > hz) {
tc->tc_quality = -2000;
printf("Timecounter \"%s\" frequency %lu Hz",
tc->tc_name, (unsigned long)tc->tc_frequency);
printf(" -- Insufficient hz, needs at least %u\n", u);
}
}
/* Determine the counter's precision. */
for (tmp = 1; (tmp & tc->tc_counter_mask) == 0; tmp <<= 1)
continue;
tc->tc_precision = tmp;
SLIST_INSERT_HEAD(&tc_list, tc, tc_next);
/*
* Never automatically use a timecounter with negative quality.
* Even though we run on the dummy counter, switching here may be
* worse since this timecounter may not be monotonic.
*/
if (tc->tc_quality < 0)
return;
if (tc->tc_quality < timecounter->tc_quality)
return;
if (tc->tc_quality == timecounter->tc_quality &&
tc->tc_frequency < timecounter->tc_frequency)
return;
(void)tc->tc_get_timecount(tc);
enqueue_randomness(tc->tc_get_timecount(tc));
timecounter = tc;
}
/*
* Change the given timecounter's quality. If it is the active
* counter and it is no longer the best counter, activate the
* best counter.
*/
void
tc_reset_quality(struct timecounter *tc, int quality)
{
struct timecounter *best = &dummy_timecounter, *tmp;
if (tc == &dummy_timecounter)
panic("%s: cannot change dummy counter quality", __func__);
tc->tc_quality = quality;
if (timecounter == tc) {
SLIST_FOREACH(tmp, &tc_list, tc_next) {
if (tmp->tc_quality < 0)
continue;
if (tmp->tc_quality < best->tc_quality)
continue;
if (tmp->tc_quality == best->tc_quality &&
tmp->tc_frequency < best->tc_frequency)
continue;
best = tmp;
}
if (best != tc) {
enqueue_randomness(best->tc_get_timecount(best));
timecounter = best;
}
}
}
/* Report the frequency of the current timecounter. */
u_int64_t
tc_getfrequency(void)
{
return (timehands->th_counter->tc_frequency);
}
/* Report the precision of the current timecounter. */
u_int64_t
tc_getprecision(void)
{
return (timehands->th_counter->tc_precision);
}
/*
* Step our concept of UTC, aka the realtime clock.
* This is done by modifying our estimate of when we booted.
*
* Any ongoing adjustment is meaningless after a clock jump,
* so we zero adjtimedelta here as well.
*/
void
tc_setrealtimeclock(const struct timespec *ts)
{
struct bintime boottime, old_utc, uptime, utc;
struct timespec tmp;
int64_t zero = 0;
TIMESPEC_TO_BINTIME(ts, &utc);
rw_enter_write(&tc_lock);
mtx_enter(&windup_mtx);
binuptime(&uptime);
bintimesub(&utc, &uptime, &boottime);
bintimeadd(&timehands->th_boottime, &uptime, &old_utc);
/* XXX fiddle all the little crinkly bits around the fiords... */
tc_windup(&boottime, NULL, &zero);
mtx_leave(&windup_mtx);
rw_exit_write(&tc_lock);
enqueue_randomness(ts->tv_sec);
if (timestepwarnings) {
BINTIME_TO_TIMESPEC(&old_utc, &tmp);
log(LOG_INFO, "Time stepped from %lld.%09ld to %lld.%09ld\n",
(long long)tmp.tv_sec, tmp.tv_nsec,
(long long)ts->tv_sec, ts->tv_nsec);
}
}
/*
* Step the monotonic and realtime clocks, triggering any timeouts that
* should have occurred across the interval.
*/
void
tc_setclock(const struct timespec *ts)
{
struct bintime new_naptime, old_naptime, uptime, utc;
struct timespec tmp;
static int first = 1;
#ifndef SMALL_KERNEL
struct bintime elapsed;
long long adj_ticks;
#endif
/*
* When we're called for the first time, during boot when
* the root partition is mounted, we need to set boottime.
*/
if (first) {
tc_setrealtimeclock(ts);
first = 0;
return;
}
enqueue_randomness(ts->tv_sec);
TIMESPEC_TO_BINTIME(ts, &utc);
mtx_enter(&windup_mtx);
bintimesub(&utc, &timehands->th_boottime, &uptime);
old_naptime = timehands->th_naptime;
/* XXX fiddle all the little crinkly bits around the fiords... */
tc_windup(NULL, &uptime, NULL);
new_naptime = timehands->th_naptime;
mtx_leave(&windup_mtx);
if (bintimecmp(&old_naptime, &new_naptime, ==)) {
BINTIME_TO_TIMESPEC(&uptime, &tmp);
printf("%s: cannot rewind uptime to %lld.%09ld\n",
__func__, (long long)tmp.tv_sec, tmp.tv_nsec);
}
#ifndef SMALL_KERNEL
/* convert the bintime to ticks */
bintimesub(&new_naptime, &old_naptime, &elapsed);
adj_ticks = BINTIME_TO_NSEC(&elapsed) / tick_nsec;
if (adj_ticks > 0) {
if (adj_ticks > INT_MAX)
adj_ticks = INT_MAX;
timeout_adjust_ticks(adj_ticks);
}
#endif
}
void
tc_update_timekeep(void)
{
static struct timecounter *last_tc = NULL;
struct timehands *th;
MUTEX_ASSERT_LOCKED(&windup_mtx);
if (timekeep == NULL)
return;
th = timehands;
timekeep->tk_generation = 0;
membar_producer();
timekeep->tk_scale = th->th_scale;
timekeep->tk_offset_count = th->th_offset_count;
timekeep->tk_offset = th->th_offset;
timekeep->tk_naptime = th->th_naptime;
timekeep->tk_boottime = th->th_boottime;
if (last_tc != th->th_counter) {
timekeep->tk_counter_mask = th->th_counter->tc_counter_mask;
timekeep->tk_user = th->th_counter->tc_user;
last_tc = th->th_counter;
}
membar_producer();
timekeep->tk_generation = th->th_generation;
return;
}
/*
* Initialize the next struct timehands in the ring and make
* it the active timehands. Along the way we might switch to a different
* timecounter and/or do seconds processing in NTP. Slightly magic.
*/
void
tc_windup(struct bintime *new_boottime, struct bintime *new_offset,
int64_t *new_adjtimedelta)
{
struct bintime bt;
struct timecounter *active_tc;
struct timehands *th, *tho;
u_int64_t scale;
u_int delta, ncount, ogen;
if (new_boottime != NULL || new_adjtimedelta != NULL)
rw_assert_wrlock(&tc_lock);
MUTEX_ASSERT_LOCKED(&windup_mtx);
active_tc = timecounter;
/*
* Make the next timehands a copy of the current one, but do not
* overwrite the generation or next pointer. While we update
* the contents, the generation must be zero.
*/
tho = timehands;
ogen = tho->th_generation;
th = tho->th_next;
th->th_generation = 0;
membar_producer();
memcpy(th, tho, offsetof(struct timehands, th_generation));
/*
* Capture a timecounter delta on the current timecounter and if
* changing timecounters, a counter value from the new timecounter.
* Update the offset fields accordingly.
*/
delta = tc_delta(th);
if (th->th_counter != active_tc)
ncount = active_tc->tc_get_timecount(active_tc);
else
ncount = 0;
th->th_offset_count += delta;
th->th_offset_count &= th->th_counter->tc_counter_mask;
TIMECOUNT_TO_BINTIME(delta, th->th_scale, &bt);
bintimeadd(&th->th_offset, &bt, &th->th_offset);
/*
* Ignore new offsets that predate the current offset.
* If changing the offset, first increase the naptime
* accordingly.
*/
if (new_offset != NULL && bintimecmp(&th->th_offset, new_offset, <)) {
bintimesub(new_offset, &th->th_offset, &bt);
bintimeadd(&th->th_naptime, &bt, &th->th_naptime);
naptime = th->th_naptime.sec;
th->th_offset = *new_offset;
}
#ifdef notyet
/*
* Hardware latching timecounters may not generate interrupts on
* PPS events, so instead we poll them. There is a finite risk that
* the hardware might capture a count which is later than the one we
* got above, and therefore possibly in the next NTP second which might
* have a different rate than the current NTP second. It doesn't
* matter in practice.
*/
if (tho->th_counter->tc_poll_pps)
tho->th_counter->tc_poll_pps(tho->th_counter);
#endif
/*
* If changing the boot time or clock adjustment, do so before
* NTP processing.
*/
if (new_boottime != NULL)
th->th_boottime = *new_boottime;
if (new_adjtimedelta != NULL) {
th->th_adjtimedelta = *new_adjtimedelta;
/* Reset the NTP update period. */
bintimesub(&th->th_offset, &th->th_naptime,
&th->th_next_ntp_update);
}
/*
* Deal with NTP second processing. The while-loop normally
* iterates at most once, but in extreme situations it might
* keep NTP sane if tc_windup() is not run for several seconds.
*/
bintimesub(&th->th_offset, &th->th_naptime, &bt);
while (bintimecmp(&th->th_next_ntp_update, &bt, <=)) {
ntp_update_second(th);
th->th_next_ntp_update.sec++;
}
/* Update the UTC timestamps used by the get*() functions. */
bintimeadd(&th->th_boottime, &th->th_offset, &bt);
BINTIME_TO_TIMEVAL(&bt, &th->th_microtime);
BINTIME_TO_TIMESPEC(&bt, &th->th_nanotime);
/* Now is a good time to change timecounters. */
if (th->th_counter != active_tc) {
th->th_counter = active_tc;
th->th_offset_count = ncount;
}
/*-
* Recalculate the scaling factor. We want the number of 1/2^64
* fractions of a second per period of the hardware counter, taking
* into account the th_adjustment factor which the NTP PLL/adjtime(2)
* processing provides us with.
*
* The th_adjustment is nanoseconds per second with 32 bit binary
* fraction and we want 64 bit binary fraction of second:
*
* x = a * 2^32 / 10^9 = a * 4.294967296
*
* The range of th_adjustment is +/- 5000PPM so inside a 64bit int
* we can only multiply by about 850 without overflowing, but that
* leaves suitably precise fractions for multiply before divide.
*
* Divide before multiply with a fraction of 2199/512 results in a
* systematic undercompensation of 10PPM of th_adjustment. On a
* 5000PPM adjustment this is a 0.05PPM error. This is acceptable.
*
* We happily sacrifice the lowest of the 64 bits of our result
* to the goddess of code clarity.
*
*/
scale = (u_int64_t)1 << 63;
scale += \
((th->th_adjustment + th->th_counter->tc_freq_adj) / 1024) * 2199;
scale /= th->th_counter->tc_frequency;
th->th_scale = scale * 2;
/*
* Now that the struct timehands is again consistent, set the new
* generation number, making sure to not make it zero.
*/
if (++ogen == 0)
ogen = 1;
membar_producer();
th->th_generation = ogen;
/* Go live with the new struct timehands. */
time_second = th->th_microtime.tv_sec;
time_uptime = th->th_offset.sec;
membar_producer();
timehands = th;
tc_update_timekeep();
}
/* Report or change the active timecounter hardware. */
int
sysctl_tc_hardware(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
char newname[32];
struct timecounter *newtc, *tc;
int error;
tc = timecounter;
strlcpy(newname, tc->tc_name, sizeof(newname));
error = sysctl_string(oldp, oldlenp, newp, newlen, newname, sizeof(newname));
if (error != 0 || strcmp(newname, tc->tc_name) == 0)
return (error);
SLIST_FOREACH(newtc, &tc_list, tc_next) {
if (strcmp(newname, newtc->tc_name) != 0)
continue;
/* Warm up new timecounter. */
(void)newtc->tc_get_timecount(newtc);
(void)newtc->tc_get_timecount(newtc);
rw_enter_write(&tc_lock);
timecounter = newtc;
rw_exit_write(&tc_lock);
return (0);
}
return (EINVAL);
}
/* Report or change the active timecounter hardware. */
int
sysctl_tc_choice(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
char buf[32], *spc, *choices;
struct timecounter *tc;
int error, maxlen;
if (SLIST_EMPTY(&tc_list))
return (sysctl_rdstring(oldp, oldlenp, newp, ""));
spc = "";
maxlen = 0;
SLIST_FOREACH(tc, &tc_list, tc_next)
maxlen += sizeof(buf);
choices = malloc(maxlen, M_TEMP, M_WAITOK);
*choices = '\0';
SLIST_FOREACH(tc, &tc_list, tc_next) {
snprintf(buf, sizeof(buf), "%s%s(%d)",
spc, tc->tc_name, tc->tc_quality);
spc = " ";
strlcat(choices, buf, maxlen);
}
error = sysctl_rdstring(oldp, oldlenp, newp, choices);
free(choices, M_TEMP, maxlen);
return (error);
}
/*
* Timecounters need to be updated every so often to prevent the hardware
* counter from overflowing. Updating also recalculates the cached values
* used by the get*() family of functions, so their precision depends on
* the update frequency.
*/
static int tc_tick;
void
tc_ticktock(void)
{
static int count;
if (++count < tc_tick)
return;
if (!mtx_enter_try(&windup_mtx))
return;
count = 0;
tc_windup(NULL, NULL, NULL);
mtx_leave(&windup_mtx);
}
void
inittimecounter(void)
{
#ifdef DEBUG
u_int p;
#endif
/*
* Set the initial timeout to
* max(1, <approx. number of hardclock ticks in a millisecond>).
* People should probably not use the sysctl to set the timeout
* to smaller than its initial value, since that value is the
* smallest reasonable one. If they want better timestamps they
* should use the non-"get"* functions.
*/
if (hz > 1000)
tc_tick = (hz + 500) / 1000;
else
tc_tick = 1;
#ifdef DEBUG
p = (tc_tick * 1000000) / hz;
printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000);
#endif
/* warm up new timecounter (again) and get rolling. */
(void)timecounter->tc_get_timecount(timecounter);
(void)timecounter->tc_get_timecount(timecounter);
}
const struct sysctl_bounded_args tc_vars[] = {
{ KERN_TIMECOUNTER_TICK, &tc_tick, SYSCTL_INT_READONLY },
{ KERN_TIMECOUNTER_TIMESTEPWARNINGS, ×tepwarnings, 0, 1 },
};
/*
* Return timecounter-related information.
*/
int
sysctl_tc(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case KERN_TIMECOUNTER_HARDWARE:
return (sysctl_tc_hardware(oldp, oldlenp, newp, newlen));
case KERN_TIMECOUNTER_CHOICE:
return (sysctl_tc_choice(oldp, oldlenp, newp, newlen));
default:
return (sysctl_bounded_arr(tc_vars, nitems(tc_vars), name,
namelen, oldp, oldlenp, newp, newlen));
}
/* NOTREACHED */
}
/*
* Skew the timehands according to any adjtime(2) adjustment.
*/
void
ntp_update_second(struct timehands *th)
{
int64_t adj;
MUTEX_ASSERT_LOCKED(&windup_mtx);
if (th->th_adjtimedelta > 0)
adj = MIN(5000, th->th_adjtimedelta);
else
adj = MAX(-5000, th->th_adjtimedelta);
th->th_adjtimedelta -= adj;
th->th_adjustment = (adj * 1000) << 32;
}
void
tc_adjfreq(int64_t *old, int64_t *new)
{
if (old != NULL) {
rw_assert_anylock(&tc_lock);
*old = timecounter->tc_freq_adj;
}
if (new != NULL) {
rw_assert_wrlock(&tc_lock);
mtx_enter(&windup_mtx);
timecounter->tc_freq_adj = *new;
tc_windup(NULL, NULL, NULL);
mtx_leave(&windup_mtx);
}
}
void
tc_adjtime(int64_t *old, int64_t *new)
{
struct timehands *th;
u_int gen;
if (old != NULL) {
do {
th = timehands;
gen = th->th_generation;
membar_consumer();
*old = th->th_adjtimedelta;
membar_consumer();
} while (gen == 0 || gen != th->th_generation);
}
if (new != NULL) {
rw_assert_wrlock(&tc_lock);
mtx_enter(&windup_mtx);
tc_windup(NULL, NULL, new);
mtx_leave(&windup_mtx);
}
}
open /syzkaller/managers/main/kernel/machine/cpufunc.h: no such file or directory
3
3
3
3
3
2
2
2
2
2
269
271
106
104
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
/* $OpenBSD: ip_ipsp.c,v 1.273 2022/08/06 15:57:59 bluhm Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr),
* Niels Provos (provos@physnet.uni-hamburg.de) and
* Niklas Hallqvist (niklas@appli.se).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis and Niklas Hallqvist.
*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 1999 Niklas Hallqvist.
* Copyright (c) 2001, Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include "pf.h"
#include "pfsync.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <sys/pool.h>
#include <sys/atomic.h>
#include <sys/mutex.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_ipip.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#if NPFSYNC > 0
#include <net/if_pfsync.h>
#endif
#include <netinet/ip_ipsp.h>
#include <net/pfkeyv2.h>
#ifdef DDB
#include <ddb/db_output.h>
void tdb_hashstats(void);
#endif
#ifdef ENCDEBUG
#define DPRINTF(fmt, args...) \
do { \
if (encdebug) \
printf("%s: " fmt "\n", __func__, ## args); \
} while (0)
#else
#define DPRINTF(fmt, args...) \
do { } while (0)
#endif
/*
* Locks used to protect global data and struct members:
* D tdb_sadb_mtx
* F ipsec_flows_mtx SA database global mutex
*/
struct mutex ipsec_flows_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
int tdb_rehash(void);
void tdb_timeout(void *);
void tdb_firstuse(void *);
void tdb_soft_timeout(void *);
void tdb_soft_firstuse(void *);
int tdb_hash(u_int32_t, union sockaddr_union *, u_int8_t);
int ipsec_in_use = 0;
u_int64_t ipsec_last_added = 0;
int ipsec_ids_idle = 100; /* keep free ids for 100s */
struct pool tdb_pool;
/* Protected by the NET_LOCK(). */
u_int32_t ipsec_ids_next_flow = 1; /* [F] may not be zero */
struct ipsec_ids_tree ipsec_ids_tree; /* [F] */
struct ipsec_ids_flows ipsec_ids_flows; /* [F] */
struct ipsec_policy_head ipsec_policy_head =
TAILQ_HEAD_INITIALIZER(ipsec_policy_head);
void ipsp_ids_gc(void *);
LIST_HEAD(, ipsec_ids) ipsp_ids_gc_list =
LIST_HEAD_INITIALIZER(ipsp_ids_gc_list); /* [F] */
struct timeout ipsp_ids_gc_timeout =
TIMEOUT_INITIALIZER_FLAGS(ipsp_ids_gc, NULL, TIMEOUT_PROC);
static inline int ipsp_ids_cmp(const struct ipsec_ids *,
const struct ipsec_ids *);
static inline int ipsp_ids_flow_cmp(const struct ipsec_ids *,
const struct ipsec_ids *);
RBT_PROTOTYPE(ipsec_ids_tree, ipsec_ids, id_node_flow, ipsp_ids_cmp);
RBT_PROTOTYPE(ipsec_ids_flows, ipsec_ids, id_node_id, ipsp_ids_flow_cmp);
RBT_GENERATE(ipsec_ids_tree, ipsec_ids, id_node_flow, ipsp_ids_cmp);
RBT_GENERATE(ipsec_ids_flows, ipsec_ids, id_node_id, ipsp_ids_flow_cmp);
/*
* This is the proper place to define the various encapsulation transforms.
*/
const struct xformsw xformsw[] = {
#ifdef IPSEC
{
.xf_type = XF_IP4,
.xf_flags = 0,
.xf_name = "IPv4 Simple Encapsulation",
.xf_attach = ipe4_attach,
.xf_init = ipe4_init,
.xf_zeroize = ipe4_zeroize,
.xf_input = ipe4_input,
.xf_output = NULL,
},
{
.xf_type = XF_AH,
.xf_flags = XFT_AUTH,
.xf_name = "IPsec AH",
.xf_attach = ah_attach,
.xf_init = ah_init,
.xf_zeroize = ah_zeroize,
.xf_input = ah_input,
.xf_output = ah_output,
},
{
.xf_type = XF_ESP,
.xf_flags = XFT_CONF|XFT_AUTH,
.xf_name = "IPsec ESP",
.xf_attach = esp_attach,
.xf_init = esp_init,
.xf_zeroize = esp_zeroize,
.xf_input = esp_input,
.xf_output = esp_output,
},
{
.xf_type = XF_IPCOMP,
.xf_flags = XFT_COMP,
.xf_name = "IPcomp",
.xf_attach = ipcomp_attach,
.xf_init = ipcomp_init,
.xf_zeroize = ipcomp_zeroize,
.xf_input = ipcomp_input,
.xf_output = ipcomp_output,
},
#endif /* IPSEC */
#ifdef TCP_SIGNATURE
{
.xf_type = XF_TCPSIGNATURE,
.xf_flags = XFT_AUTH,
.xf_name = "TCP MD5 Signature Option, RFC 2385",
.xf_attach = tcp_signature_tdb_attach,
.xf_init = tcp_signature_tdb_init,
.xf_zeroize = tcp_signature_tdb_zeroize,
.xf_input = tcp_signature_tdb_input,
.xf_output = tcp_signature_tdb_output,
}
#endif /* TCP_SIGNATURE */
};
const struct xformsw *const xformswNXFORMSW = &xformsw[nitems(xformsw)];
#define TDB_HASHSIZE_INIT 32
struct mutex tdb_sadb_mtx = MUTEX_INITIALIZER(IPL_SOFTNET);
static SIPHASH_KEY tdbkey; /* [D] */
static struct tdb **tdbh; /* [D] */
static struct tdb **tdbdst; /* [D] */
static struct tdb **tdbsrc; /* [D] */
static u_int tdb_hashmask = TDB_HASHSIZE_INIT - 1; /* [D] */
static int tdb_count; /* [D] */
void
ipsp_init(void)
{
pool_init(&tdb_pool, sizeof(struct tdb), 0, IPL_SOFTNET, 0,
"tdb", NULL);
arc4random_buf(&tdbkey, sizeof(tdbkey));
tdbh = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_WAITOK | M_ZERO);
tdbdst = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_WAITOK | M_ZERO);
tdbsrc = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_WAITOK | M_ZERO);
}
/*
* Our hashing function needs to stir things with a non-zero random multiplier
* so we cannot be DoS-attacked via choosing of the data to hash.
*/
int
tdb_hash(u_int32_t spi, union sockaddr_union *dst,
u_int8_t proto)
{
SIPHASH_CTX ctx;
MUTEX_ASSERT_LOCKED(&tdb_sadb_mtx);
SipHash24_Init(&ctx, &tdbkey);
SipHash24_Update(&ctx, &spi, sizeof(spi));
SipHash24_Update(&ctx, &proto, sizeof(proto));
SipHash24_Update(&ctx, dst, dst->sa.sa_len);
return (SipHash24_End(&ctx) & tdb_hashmask);
}
/*
* Reserve an SPI; the SA is not valid yet though. We use 0 as
* an error return value.
*/
u_int32_t
reserve_spi(u_int rdomain, u_int32_t sspi, u_int32_t tspi,
union sockaddr_union *src, union sockaddr_union *dst,
u_int8_t sproto, int *errval)
{
struct tdb *tdbp, *exists;
u_int32_t spi;
int nums;
/* Don't accept ranges only encompassing reserved SPIs. */
if (sproto != IPPROTO_IPCOMP &&
(tspi < sspi || tspi <= SPI_RESERVED_MAX)) {
(*errval) = EINVAL;
return 0;
}
if (sproto == IPPROTO_IPCOMP && (tspi < sspi ||
tspi <= CPI_RESERVED_MAX ||
tspi >= CPI_PRIVATE_MIN)) {
(*errval) = EINVAL;
return 0;
}
/* Limit the range to not include reserved areas. */
if (sspi <= SPI_RESERVED_MAX)
sspi = SPI_RESERVED_MAX + 1;
/* For IPCOMP the CPI is only 16 bits long, what a good idea.... */
if (sproto == IPPROTO_IPCOMP) {
u_int32_t t;
if (sspi >= 0x10000)
sspi = 0xffff;
if (tspi >= 0x10000)
tspi = 0xffff;
if (sspi > tspi) {
t = sspi; sspi = tspi; tspi = t;
}
}
if (sspi == tspi) /* Asking for a specific SPI. */
nums = 1;
else
nums = 100; /* Arbitrarily chosen */
/* allocate ahead of time to avoid potential sleeping race in loop */
tdbp = tdb_alloc(rdomain);
while (nums--) {
if (sspi == tspi) /* Specific SPI asked. */
spi = tspi;
else /* Range specified */
spi = sspi + arc4random_uniform(tspi - sspi);
/* Don't allocate reserved SPIs. */
if (spi >= SPI_RESERVED_MIN && spi <= SPI_RESERVED_MAX)
continue;
else
spi = htonl(spi);
/* Check whether we're using this SPI already. */
exists = gettdb(rdomain, spi, dst, sproto);
if (exists != NULL) {
tdb_unref(exists);
continue;
}
tdbp->tdb_spi = spi;
memcpy(&tdbp->tdb_dst.sa, &dst->sa, dst->sa.sa_len);
memcpy(&tdbp->tdb_src.sa, &src->sa, src->sa.sa_len);
tdbp->tdb_sproto = sproto;
tdbp->tdb_flags |= TDBF_INVALID; /* Mark SA invalid for now. */
tdbp->tdb_satype = SADB_SATYPE_UNSPEC;
puttdb(tdbp);
#ifdef IPSEC
/* Setup a "silent" expiration (since TDBF_INVALID's set). */
if (ipsec_keep_invalid > 0) {
mtx_enter(&tdbp->tdb_mtx);
tdbp->tdb_flags |= TDBF_TIMER;
tdbp->tdb_exp_timeout = ipsec_keep_invalid;
if (timeout_add_sec(&tdbp->tdb_timer_tmo,
ipsec_keep_invalid))
tdb_ref(tdbp);
mtx_leave(&tdbp->tdb_mtx);
}
#endif
return spi;
}
(*errval) = EEXIST;
tdb_unref(tdbp);
return 0;
}
/*
* An IPSP SAID is really the concatenation of the SPI found in the
* packet, the destination address of the packet and the IPsec protocol.
* When we receive an IPSP packet, we need to look up its tunnel descriptor
* block, based on the SPI in the packet and the destination address (which
* is really one of our addresses if we received the packet!
*/
struct tdb *
gettdb_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *dst,
u_int8_t proto, int reverse)
{
u_int32_t hashval;
struct tdb *tdbp;
NET_ASSERT_LOCKED();
mtx_enter(&tdb_sadb_mtx);
hashval = tdb_hash(spi, dst, proto);
for (tdbp = tdbh[hashval]; tdbp != NULL; tdbp = tdbp->tdb_hnext)
if ((tdbp->tdb_spi == spi) && (tdbp->tdb_sproto == proto) &&
((!reverse && tdbp->tdb_rdomain == rdomain) ||
(reverse && tdbp->tdb_rdomain_post == rdomain)) &&
!memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len))
break;
tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
/*
* Same as gettdb() but compare SRC as well, so we
* use the tdbsrc[] hash table. Setting spi to 0
* matches all SPIs.
*/
struct tdb *
gettdbbysrcdst_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
union sockaddr_union *dst, u_int8_t proto, int reverse)
{
u_int32_t hashval;
struct tdb *tdbp;
union sockaddr_union su_null;
mtx_enter(&tdb_sadb_mtx);
hashval = tdb_hash(0, src, proto);
for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext) {
if (tdbp->tdb_sproto == proto &&
(spi == 0 || tdbp->tdb_spi == spi) &&
((!reverse && tdbp->tdb_rdomain == rdomain) ||
(reverse && tdbp->tdb_rdomain_post == rdomain)) &&
((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
(tdbp->tdb_dst.sa.sa_family == AF_UNSPEC ||
!memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len)) &&
!memcmp(&tdbp->tdb_src, src, src->sa.sa_len))
break;
}
if (tdbp != NULL) {
tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
memset(&su_null, 0, sizeof(su_null));
su_null.sa.sa_len = sizeof(struct sockaddr);
hashval = tdb_hash(0, &su_null, proto);
for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext) {
if (tdbp->tdb_sproto == proto &&
(spi == 0 || tdbp->tdb_spi == spi) &&
((!reverse && tdbp->tdb_rdomain == rdomain) ||
(reverse && tdbp->tdb_rdomain_post == rdomain)) &&
((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
(tdbp->tdb_dst.sa.sa_family == AF_UNSPEC ||
!memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len)) &&
tdbp->tdb_src.sa.sa_family == AF_UNSPEC)
break;
}
tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
/*
* Check that IDs match. Return true if so. The t* range of
* arguments contains information from TDBs; the p* range of
* arguments contains information from policies or already
* established TDBs.
*/
int
ipsp_aux_match(struct tdb *tdb,
struct ipsec_ids *ids,
struct sockaddr_encap *pfilter,
struct sockaddr_encap *pfiltermask)
{
if (ids != NULL)
if (tdb->tdb_ids == NULL ||
!ipsp_ids_match(tdb->tdb_ids, ids))
return 0;
/* Check for filter matches. */
if (pfilter != NULL && pfiltermask != NULL &&
tdb->tdb_filter.sen_type) {
/*
* XXX We should really be doing a subnet-check (see
* whether the TDB-associated filter is a subset
* of the policy's. For now, an exact match will solve
* most problems (all this will do is make every
* policy get its own SAs).
*/
if (memcmp(&tdb->tdb_filter, pfilter,
sizeof(struct sockaddr_encap)) ||
memcmp(&tdb->tdb_filtermask, pfiltermask,
sizeof(struct sockaddr_encap)))
return 0;
}
return 1;
}
/*
* Get an SA given the remote address, the security protocol type, and
* the desired IDs.
*/
struct tdb *
gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
struct ipsec_ids *ids,
struct sockaddr_encap *filter, struct sockaddr_encap *filtermask)
{
u_int32_t hashval;
struct tdb *tdbp;
mtx_enter(&tdb_sadb_mtx);
hashval = tdb_hash(0, dst, sproto);
for (tdbp = tdbdst[hashval]; tdbp != NULL; tdbp = tdbp->tdb_dnext)
if ((tdbp->tdb_sproto == sproto) &&
(tdbp->tdb_rdomain == rdomain) &&
((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
(!memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len))) {
/* Check whether IDs match */
if (!ipsp_aux_match(tdbp, ids, filter, filtermask))
continue;
break;
}
tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
/*
* Get an SA given the source address, the security protocol type, and
* the desired IDs.
*/
struct tdb *
gettdbbysrc(u_int rdomain, union sockaddr_union *src, u_int8_t sproto,
struct ipsec_ids *ids,
struct sockaddr_encap *filter, struct sockaddr_encap *filtermask)
{
u_int32_t hashval;
struct tdb *tdbp;
mtx_enter(&tdb_sadb_mtx);
hashval = tdb_hash(0, src, sproto);
for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext) {
if ((tdbp->tdb_sproto == sproto) &&
(tdbp->tdb_rdomain == rdomain) &&
((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
(!memcmp(&tdbp->tdb_src, src, src->sa.sa_len))) {
/* Check whether IDs match */
if (!ipsp_aux_match(tdbp, ids, filter, filtermask))
continue;
break;
}
}
tdb_ref(tdbp);
mtx_leave(&tdb_sadb_mtx);
return tdbp;
}
#ifdef DDB
#define NBUCKETS 16
void
tdb_hashstats(void)
{
int i, cnt, buckets[NBUCKETS];
struct tdb *tdbp;
if (tdbh == NULL) {
db_printf("no tdb hash table\n");
return;
}
memset(buckets, 0, sizeof(buckets));
for (i = 0; i <= tdb_hashmask; i++) {
cnt = 0;
for (tdbp = tdbh[i]; cnt < NBUCKETS - 1 && tdbp != NULL;
tdbp = tdbp->tdb_hnext)
cnt++;
buckets[cnt]++;
}
db_printf("tdb cnt\t\tbucket cnt\n");
for (i = 0; i < NBUCKETS; i++)
if (buckets[i] > 0)
db_printf("%d%s\t\t%d\n", i, i == NBUCKETS - 1 ?
"+" : "", buckets[i]);
}
#define DUMP(m, f) pr("%18s: " f "\n", #m, tdb->tdb_##m)
void
tdb_printit(void *addr, int full, int (*pr)(const char *, ...))
{
struct tdb *tdb = addr;
char buf[INET6_ADDRSTRLEN];
if (full) {
pr("tdb at %p\n", tdb);
DUMP(hnext, "%p");
DUMP(dnext, "%p");
DUMP(snext, "%p");
DUMP(inext, "%p");
DUMP(onext, "%p");
DUMP(xform, "%p");
pr("%18s: %d\n", "refcnt", tdb->tdb_refcnt.r_refs);
DUMP(encalgxform, "%p");
DUMP(authalgxform, "%p");
DUMP(compalgxform, "%p");
pr("%18s: %b\n", "flags", tdb->tdb_flags, TDBF_BITS);
/* tdb_XXX_tmo */
DUMP(seq, "%d");
DUMP(exp_allocations, "%d");
DUMP(soft_allocations, "%d");
DUMP(cur_allocations, "%d");
DUMP(exp_bytes, "%lld");
DUMP(soft_bytes, "%lld");
DUMP(cur_bytes, "%lld");
DUMP(exp_timeout, "%lld");
DUMP(soft_timeout, "%lld");
DUMP(established, "%lld");
DUMP(first_use, "%lld");
DUMP(soft_first_use, "%lld");
DUMP(exp_first_use, "%lld");
DUMP(last_used, "%lld");
DUMP(last_marked, "%lld");
/* tdb_data */
DUMP(cryptoid, "%lld");
pr("%18s: %08x\n", "tdb_spi", ntohl(tdb->tdb_spi));
DUMP(amxkeylen, "%d");
DUMP(emxkeylen, "%d");
DUMP(ivlen, "%d");
DUMP(sproto, "%d");
DUMP(wnd, "%d");
DUMP(satype, "%d");
DUMP(updates, "%d");
pr("%18s: %s\n", "dst",
ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)));
pr("%18s: %s\n", "src",
ipsp_address(&tdb->tdb_src, buf, sizeof(buf)));
DUMP(amxkey, "%p");
DUMP(emxkey, "%p");
DUMP(rpl, "%lld");
/* tdb_seen */
/* tdb_iv */
DUMP(ids, "%p");
DUMP(ids_swapped, "%d");
DUMP(mtu, "%d");
DUMP(mtutimeout, "%lld");
pr("%18s: %d\n", "udpencap_port",
ntohs(tdb->tdb_udpencap_port));
DUMP(tag, "%d");
DUMP(tap, "%d");
DUMP(rdomain, "%d");
DUMP(rdomain_post, "%d");
/* tdb_filter */
/* tdb_filtermask */
/* tdb_policy_head */
/* tdb_sync_entry */
} else {
pr("%p:", tdb);
pr(" %08x", ntohl(tdb->tdb_spi));
pr(" %s", ipsp_address(&tdb->tdb_src, buf, sizeof(buf)));
pr("->%s", ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)));
pr(":%d", tdb->tdb_sproto);
pr(" #%d", tdb->tdb_refcnt.r_refs);
pr(" %08x\n", tdb->tdb_flags);
}
}
#undef DUMP
#endif /* DDB */
int
tdb_walk(u_int rdomain, int (*walker)(struct tdb *, void *, int), void *arg)
{
SIMPLEQ_HEAD(, tdb) tdblist;
struct tdb *tdbp;
int i, rval;
/*
* The walker may sleep. So we cannot hold the tdb_sadb_mtx while
* traversing the tdb_hnext list. Create a new tdb_walk list with
* exclusive netlock protection.
*/
NET_ASSERT_LOCKED_EXCLUSIVE();
SIMPLEQ_INIT(&tdblist);
mtx_enter(&tdb_sadb_mtx);
for (i = 0; i <= tdb_hashmask; i++) {
for (tdbp = tdbh[i]; tdbp != NULL; tdbp = tdbp->tdb_hnext) {
if (rdomain != tdbp->tdb_rdomain)
continue;
tdb_ref(tdbp);
SIMPLEQ_INSERT_TAIL(&tdblist, tdbp, tdb_walk);
}
}
mtx_leave(&tdb_sadb_mtx);
rval = 0;
while ((tdbp = SIMPLEQ_FIRST(&tdblist)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&tdblist, tdb_walk);
if (rval == 0)
rval = walker(tdbp, arg, SIMPLEQ_EMPTY(&tdblist));
tdb_unref(tdbp);
}
return rval;
}
void
tdb_timeout(void *v)
{
struct tdb *tdb = v;
NET_LOCK();
if (tdb->tdb_flags & TDBF_TIMER) {
/* If it's an "invalid" TDB do a silent expiration. */
if (!(tdb->tdb_flags & TDBF_INVALID)) {
#ifdef IPSEC
ipsecstat_inc(ipsec_exctdb);
#endif /* IPSEC */
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
}
tdb_delete(tdb);
}
/* decrement refcount of the timeout argument */
tdb_unref(tdb);
NET_UNLOCK();
}
void
tdb_firstuse(void *v)
{
struct tdb *tdb = v;
NET_LOCK();
if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
/* If the TDB hasn't been used, don't renew it. */
if (tdb->tdb_first_use != 0) {
#ifdef IPSEC
ipsecstat_inc(ipsec_exctdb);
#endif /* IPSEC */
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
}
tdb_delete(tdb);
}
/* decrement refcount of the timeout argument */
tdb_unref(tdb);
NET_UNLOCK();
}
void
tdb_soft_timeout(void *v)
{
struct tdb *tdb = v;
NET_LOCK();
mtx_enter(&tdb->tdb_mtx);
if (tdb->tdb_flags & TDBF_SOFT_TIMER) {
tdb->tdb_flags &= ~TDBF_SOFT_TIMER;
mtx_leave(&tdb->tdb_mtx);
/* Soft expirations. */
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
} else
mtx_leave(&tdb->tdb_mtx);
/* decrement refcount of the timeout argument */
tdb_unref(tdb);
NET_UNLOCK();
}
void
tdb_soft_firstuse(void *v)
{
struct tdb *tdb = v;
NET_LOCK();
mtx_enter(&tdb->tdb_mtx);
if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) {
tdb->tdb_flags &= ~TDBF_SOFT_FIRSTUSE;
mtx_leave(&tdb->tdb_mtx);
/* If the TDB hasn't been used, don't renew it. */
if (tdb->tdb_first_use != 0)
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
} else
mtx_leave(&tdb->tdb_mtx);
/* decrement refcount of the timeout argument */
tdb_unref(tdb);
NET_UNLOCK();
}
int
tdb_rehash(void)
{
struct tdb **new_tdbh, **new_tdbdst, **new_srcaddr, *tdbp, *tdbnp;
u_int i, old_hashmask;
u_int32_t hashval;
MUTEX_ASSERT_LOCKED(&tdb_sadb_mtx);
old_hashmask = tdb_hashmask;
tdb_hashmask = (tdb_hashmask << 1) | 1;
arc4random_buf(&tdbkey, sizeof(tdbkey));
new_tdbh = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_NOWAIT | M_ZERO);
new_tdbdst = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_NOWAIT | M_ZERO);
new_srcaddr = mallocarray(tdb_hashmask + 1, sizeof(struct tdb *), M_TDB,
M_NOWAIT | M_ZERO);
if (new_tdbh == NULL ||
new_tdbdst == NULL ||
new_srcaddr == NULL) {
free(new_tdbh, M_TDB, 0);
free(new_tdbdst, M_TDB, 0);
free(new_srcaddr, M_TDB, 0);
return (ENOMEM);
}
for (i = 0; i <= old_hashmask; i++) {
for (tdbp = tdbh[i]; tdbp != NULL; tdbp = tdbnp) {
tdbnp = tdbp->tdb_hnext;
hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst,
tdbp->tdb_sproto);
tdbp->tdb_hnext = new_tdbh[hashval];
new_tdbh[hashval] = tdbp;
}
for (tdbp = tdbdst[i]; tdbp != NULL; tdbp = tdbnp) {
tdbnp = tdbp->tdb_dnext;
hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto);
tdbp->tdb_dnext = new_tdbdst[hashval];
new_tdbdst[hashval] = tdbp;
}
for (tdbp = tdbsrc[i]; tdbp != NULL; tdbp = tdbnp) {
tdbnp = tdbp->tdb_snext;
hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto);
tdbp->tdb_snext = new_srcaddr[hashval];
new_srcaddr[hashval] = tdbp;
}
}
free(tdbh, M_TDB, 0);
tdbh = new_tdbh;
free(tdbdst, M_TDB, 0);
tdbdst = new_tdbdst;
free(tdbsrc, M_TDB, 0);
tdbsrc = new_srcaddr;
return 0;
}
/*
* Add TDB in the hash table.
*/
void
puttdb(struct tdb *tdbp)
{
mtx_enter(&tdb_sadb_mtx);
puttdb_locked(tdbp);
mtx_leave(&tdb_sadb_mtx);
}
void
puttdb_locked(struct tdb *tdbp)
{
u_int32_t hashval;
MUTEX_ASSERT_LOCKED(&tdb_sadb_mtx);
hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto);
/*
* Rehash if this tdb would cause a bucket to have more than
* two items and if the number of tdbs exceed 10% of the
* bucket count. This number is arbitrarily chosen and is
* just a measure to not keep rehashing when adding and
* removing tdbs which happens to always end up in the same
* bucket, which is not uncommon when doing manual keying.
*/
if (tdbh[hashval] != NULL && tdbh[hashval]->tdb_hnext != NULL &&
tdb_count * 10 > tdb_hashmask + 1) {
if (tdb_rehash() == 0)
hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst,
tdbp->tdb_sproto);
}
tdbp->tdb_hnext = tdbh[hashval];
tdbh[hashval] = tdbp;
hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto);
tdbp->tdb_dnext = tdbdst[hashval];
tdbdst[hashval] = tdbp;
hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto);
tdbp->tdb_snext = tdbsrc[hashval];
tdbsrc[hashval] = tdbp;
tdb_count++;
#ifdef IPSEC
if ((tdbp->tdb_flags & (TDBF_INVALID|TDBF_TUNNELING)) == TDBF_TUNNELING)
ipsecstat_inc(ipsec_tunnels);
#endif /* IPSEC */
ipsec_last_added = getuptime();
}
void
tdb_unlink(struct tdb *tdbp)
{
mtx_enter(&tdb_sadb_mtx);
tdb_unlink_locked(tdbp);
mtx_leave(&tdb_sadb_mtx);
}
void
tdb_unlink_locked(struct tdb *tdbp)
{
struct tdb *tdbpp;
u_int32_t hashval;
MUTEX_ASSERT_LOCKED(&tdb_sadb_mtx);
hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto);
if (tdbh[hashval] == tdbp) {
tdbh[hashval] = tdbp->tdb_hnext;
} else {
for (tdbpp = tdbh[hashval]; tdbpp != NULL;
tdbpp = tdbpp->tdb_hnext) {
if (tdbpp->tdb_hnext == tdbp) {
tdbpp->tdb_hnext = tdbp->tdb_hnext;
break;
}
}
}
tdbp->tdb_hnext = NULL;
hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto);
if (tdbdst[hashval] == tdbp) {
tdbdst[hashval] = tdbp->tdb_dnext;
} else {
for (tdbpp = tdbdst[hashval]; tdbpp != NULL;
tdbpp = tdbpp->tdb_dnext) {
if (tdbpp->tdb_dnext == tdbp) {
tdbpp->tdb_dnext = tdbp->tdb_dnext;
break;
}
}
}
tdbp->tdb_dnext = NULL;
hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto);
if (tdbsrc[hashval] == tdbp) {
tdbsrc[hashval] = tdbp->tdb_snext;
} else {
for (tdbpp = tdbsrc[hashval]; tdbpp != NULL;
tdbpp = tdbpp->tdb_snext) {
if (tdbpp->tdb_snext == tdbp) {
tdbpp->tdb_snext = tdbp->tdb_snext;
break;
}
}
}
tdbp->tdb_snext = NULL;
tdb_count--;
#ifdef IPSEC
if ((tdbp->tdb_flags & (TDBF_INVALID|TDBF_TUNNELING)) ==
TDBF_TUNNELING) {
ipsecstat_dec(ipsec_tunnels);
ipsecstat_inc(ipsec_prevtunnels);
}
#endif /* IPSEC */
}
void
tdb_cleanspd(struct tdb *tdbp)
{
struct ipsec_policy *ipo;
mtx_enter(&ipo_tdb_mtx);
while ((ipo = TAILQ_FIRST(&tdbp->tdb_policy_head)) != NULL) {
TAILQ_REMOVE(&tdbp->tdb_policy_head, ipo, ipo_tdb_next);
tdb_unref(ipo->ipo_tdb);
ipo->ipo_tdb = NULL;
ipo->ipo_last_searched = 0; /* Force a re-search. */
}
mtx_leave(&ipo_tdb_mtx);
}
void
tdb_unbundle(struct tdb *tdbp)
{
if (tdbp->tdb_onext != NULL) {
if (tdbp->tdb_onext->tdb_inext == tdbp) {
tdb_unref(tdbp); /* to us */
tdbp->tdb_onext->tdb_inext = NULL;
}
tdb_unref(tdbp->tdb_onext); /* to other */
tdbp->tdb_onext = NULL;
}
if (tdbp->tdb_inext != NULL) {
if (tdbp->tdb_inext->tdb_onext == tdbp) {
tdb_unref(tdbp); /* to us */
tdbp->tdb_inext->tdb_onext = NULL;
}
tdb_unref(tdbp->tdb_inext); /* to other */
tdbp->tdb_inext = NULL;
}
}
void
tdb_deltimeouts(struct tdb *tdbp)
{
mtx_enter(&tdbp->tdb_mtx);
tdbp->tdb_flags &= ~(TDBF_FIRSTUSE | TDBF_SOFT_FIRSTUSE | TDBF_TIMER |
TDBF_SOFT_TIMER);
if (timeout_del(&tdbp->tdb_timer_tmo))
tdb_unref(tdbp);
if (timeout_del(&tdbp->tdb_first_tmo))
tdb_unref(tdbp);
if (timeout_del(&tdbp->tdb_stimer_tmo))
tdb_unref(tdbp);
if (timeout_del(&tdbp->tdb_sfirst_tmo))
tdb_unref(tdbp);
mtx_leave(&tdbp->tdb_mtx);
}
struct tdb *
tdb_ref(struct tdb *tdb)
{
if (tdb == NULL)
return NULL;
refcnt_take(&tdb->tdb_refcnt);
return tdb;
}
void
tdb_unref(struct tdb *tdb)
{
if (tdb == NULL)
return;
if (refcnt_rele(&tdb->tdb_refcnt) == 0)
return;
tdb_free(tdb);
}
void
tdb_delete(struct tdb *tdbp)
{
NET_ASSERT_LOCKED();
mtx_enter(&tdbp->tdb_mtx);
if (tdbp->tdb_flags & TDBF_DELETED) {
mtx_leave(&tdbp->tdb_mtx);
return;
}
tdbp->tdb_flags |= TDBF_DELETED;
mtx_leave(&tdbp->tdb_mtx);
tdb_unlink(tdbp);
/* cleanup SPD references */
tdb_cleanspd(tdbp);
/* release tdb_onext/tdb_inext references */
tdb_unbundle(tdbp);
/* delete timeouts and release references */
tdb_deltimeouts(tdbp);
/* release the reference for tdb_unlink() */
tdb_unref(tdbp);
}
/*
* Allocate a TDB and initialize a few basic fields.
*/
struct tdb *
tdb_alloc(u_int rdomain)
{
struct tdb *tdbp;
tdbp = pool_get(&tdb_pool, PR_WAITOK | PR_ZERO);
refcnt_init_trace(&tdbp->tdb_refcnt, DT_REFCNT_IDX_TDB);
mtx_init(&tdbp->tdb_mtx, IPL_SOFTNET);
TAILQ_INIT(&tdbp->tdb_policy_head);
/* Record establishment time. */
tdbp->tdb_established = gettime();
/* Save routing domain */
tdbp->tdb_rdomain = rdomain;
tdbp->tdb_rdomain_post = rdomain;
/* Initialize counters. */
tdbp->tdb_counters = counters_alloc(tdb_ncounters);
/* Initialize timeouts. */
timeout_set_proc(&tdbp->tdb_timer_tmo, tdb_timeout, tdbp);
timeout_set_proc(&tdbp->tdb_first_tmo, tdb_firstuse, tdbp);
timeout_set_proc(&tdbp->tdb_stimer_tmo, tdb_soft_timeout, tdbp);
timeout_set_proc(&tdbp->tdb_sfirst_tmo, tdb_soft_firstuse, tdbp);
return tdbp;
}
void
tdb_free(struct tdb *tdbp)
{
NET_ASSERT_LOCKED();
if (tdbp->tdb_xform) {
(*(tdbp->tdb_xform->xf_zeroize))(tdbp);
tdbp->tdb_xform = NULL;
}
#if NPFSYNC > 0
/* Cleanup pfsync references */
pfsync_delete_tdb(tdbp);
#endif
KASSERT(TAILQ_EMPTY(&tdbp->tdb_policy_head));
if (tdbp->tdb_ids) {
ipsp_ids_free(tdbp->tdb_ids);
tdbp->tdb_ids = NULL;
}
#if NPF > 0
if (tdbp->tdb_tag) {
pf_tag_unref(tdbp->tdb_tag);
tdbp->tdb_tag = 0;
}
#endif
counters_free(tdbp->tdb_counters, tdb_ncounters);
KASSERT(tdbp->tdb_onext == NULL);
KASSERT(tdbp->tdb_inext == NULL);
/* Remove expiration timeouts. */
KASSERT(timeout_pending(&tdbp->tdb_timer_tmo) == 0);
KASSERT(timeout_pending(&tdbp->tdb_first_tmo) == 0);
KASSERT(timeout_pending(&tdbp->tdb_stimer_tmo) == 0);
KASSERT(timeout_pending(&tdbp->tdb_sfirst_tmo) == 0);
pool_put(&tdb_pool, tdbp);
}
/*
* Do further initializations of a TDB.
*/
int
tdb_init(struct tdb *tdbp, u_int16_t alg, struct ipsecinit *ii)
{
const struct xformsw *xsp;
int err;
#ifdef ENCDEBUG
char buf[INET6_ADDRSTRLEN];
#endif
for (xsp = xformsw; xsp < xformswNXFORMSW; xsp++) {
if (xsp->xf_type == alg) {
err = (*(xsp->xf_init))(tdbp, xsp, ii);
return err;
}
}
DPRINTF("no alg %d for spi %08x, addr %s, proto %d",
alg, ntohl(tdbp->tdb_spi),
ipsp_address(&tdbp->tdb_dst, buf, sizeof(buf)),
tdbp->tdb_sproto);
return EINVAL;
}
#if defined(DDB) || defined(ENCDEBUG)
/* Return a printable string for the address. */
const char *
ipsp_address(union sockaddr_union *sa, char *buf, socklen_t size)
{
switch (sa->sa.sa_family) {
case AF_INET:
return inet_ntop(AF_INET, &sa->sin.sin_addr,
buf, (size_t)size);
#ifdef INET6
case AF_INET6:
return inet_ntop(AF_INET6, &sa->sin6.sin6_addr,
buf, (size_t)size);
#endif /* INET6 */
default:
return "(unknown address family)";
}
}
#endif /* DDB || ENCDEBUG */
/* Check whether an IP{4,6} address is unspecified. */
int
ipsp_is_unspecified(union sockaddr_union addr)
{
switch (addr.sa.sa_family) {
case AF_INET:
if (addr.sin.sin_addr.s_addr == INADDR_ANY)
return 1;
else
return 0;
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&addr.sin6.sin6_addr))
return 1;
else
return 0;
#endif /* INET6 */
case 0: /* No family set. */
default:
return 1;
}
}
int
ipsp_ids_match(struct ipsec_ids *a, struct ipsec_ids *b)
{
return a == b;
}
struct ipsec_ids *
ipsp_ids_insert(struct ipsec_ids *ids)
{
struct ipsec_ids *found;
u_int32_t start_flow;
mtx_enter(&ipsec_flows_mtx);
found = RBT_INSERT(ipsec_ids_tree, &ipsec_ids_tree, ids);
if (found) {
/* if refcount was zero, then timeout is running */
if ((++found->id_refcount) == 1) {
LIST_REMOVE(found, id_gc_list);
if (LIST_EMPTY(&ipsp_ids_gc_list))
timeout_del(&ipsp_ids_gc_timeout);
}
mtx_leave (&ipsec_flows_mtx);
DPRINTF("ids %p count %d", found, found->id_refcount);
return found;
}
ids->id_refcount = 1;
ids->id_flow = start_flow = ipsec_ids_next_flow;
if (++ipsec_ids_next_flow == 0)
ipsec_ids_next_flow = 1;
while (RBT_INSERT(ipsec_ids_flows, &ipsec_ids_flows, ids) != NULL) {
ids->id_flow = ipsec_ids_next_flow;
if (++ipsec_ids_next_flow == 0)
ipsec_ids_next_flow = 1;
if (ipsec_ids_next_flow == start_flow) {
RBT_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
mtx_leave(&ipsec_flows_mtx);
DPRINTF("ipsec_ids_next_flow exhausted %u",
start_flow);
return NULL;
}
}
mtx_leave(&ipsec_flows_mtx);
DPRINTF("new ids %p flow %u", ids, ids->id_flow);
return ids;
}
struct ipsec_ids *
ipsp_ids_lookup(u_int32_t ipsecflowinfo)
{
struct ipsec_ids key;
struct ipsec_ids *ids;
key.id_flow = ipsecflowinfo;
mtx_enter(&ipsec_flows_mtx);
ids = RBT_FIND(ipsec_ids_flows, &ipsec_ids_flows, &key);
if (ids != NULL) {
if (ids->id_refcount != 0)
ids->id_refcount++;
else
ids = NULL;
}
mtx_leave(&ipsec_flows_mtx);
return ids;
}
/* free ids only from delayed timeout */
void
ipsp_ids_gc(void *arg)
{
struct ipsec_ids *ids, *tids;
mtx_enter(&ipsec_flows_mtx);
LIST_FOREACH_SAFE(ids, &ipsp_ids_gc_list, id_gc_list, tids) {
KASSERT(ids->id_refcount == 0);
DPRINTF("ids %p count %d", ids, ids->id_refcount);
if ((--ids->id_gc_ttl) > 0)
continue;
LIST_REMOVE(ids, id_gc_list);
RBT_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
RBT_REMOVE(ipsec_ids_flows, &ipsec_ids_flows, ids);
free(ids->id_local, M_CREDENTIALS, 0);
free(ids->id_remote, M_CREDENTIALS, 0);
free(ids, M_CREDENTIALS, 0);
}
if (!LIST_EMPTY(&ipsp_ids_gc_list))
timeout_add_sec(&ipsp_ids_gc_timeout, 1);
mtx_leave(&ipsec_flows_mtx);
}
/* decrements refcount, actual free happens in gc */
void
ipsp_ids_free(struct ipsec_ids *ids)
{
if (ids == NULL)
return;
mtx_enter(&ipsec_flows_mtx);
/*
* If the refcount becomes zero, then a timeout is started. This
* timeout must be cancelled if refcount is increased from zero.
*/
DPRINTF("ids %p count %d", ids, ids->id_refcount);
KASSERT(ids->id_refcount > 0);
if ((--ids->id_refcount) > 0) {
mtx_leave(&ipsec_flows_mtx);
return;
}
/*
* Add second for the case ipsp_ids_gc() is already running and
* awaits netlock to be released.
*/
ids->id_gc_ttl = ipsec_ids_idle + 1;
if (LIST_EMPTY(&ipsp_ids_gc_list))
timeout_add_sec(&ipsp_ids_gc_timeout, 1);
LIST_INSERT_HEAD(&ipsp_ids_gc_list, ids, id_gc_list);
mtx_leave(&ipsec_flows_mtx);
}
static int
ipsp_id_cmp(struct ipsec_id *a, struct ipsec_id *b)
{
if (a->type > b->type)
return 1;
if (a->type < b->type)
return -1;
if (a->len > b->len)
return 1;
if (a->len < b->len)
return -1;
return memcmp(a + 1, b + 1, a->len);
}
static inline int
ipsp_ids_cmp(const struct ipsec_ids *a, const struct ipsec_ids *b)
{
int ret;
ret = ipsp_id_cmp(a->id_remote, b->id_remote);
if (ret != 0)
return ret;
return ipsp_id_cmp(a->id_local, b->id_local);
}
static inline int
ipsp_ids_flow_cmp(const struct ipsec_ids *a, const struct ipsec_ids *b)
{
if (a->id_flow > b->id_flow)
return 1;
if (a->id_flow < b->id_flow)
return -1;
return 0;
}
15
8
1
22
22
1
16
4
20
20
1
37
37
22
80
1
8
43
18
9
57
49
14
17
14
1
8
3
2
6
1
2
1
3
2
15
3
11
4
2
33
20
5
17
30
29
2
20
15
29
15
1
2
7
7
3
27
1
3
28
21
16
14
3
1
24
22
36
1
25
1
25
8
24
8
41
2
72
2
29
5
20
8
2
21
6
36
4
33
23
24
20
4
10
63
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
/* $OpenBSD: sys_pipe.c,v 1.142 2022/08/14 01:58:28 jsg Exp $ */
/*
* Copyright (c) 1996 John S. Dyson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Absolutely no warranty of function or purpose is made by the author
* John S. Dyson.
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*/
/*
* This file contains a high-performance replacement for the socket-based
* pipes scheme originally used in FreeBSD/4.4Lite. It does not support
* all features of sockets, but does do everything that pipes normally
* do.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/pool.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/signalvar.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/event.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <uvm/uvm_extern.h>
#include <sys/pipe.h>
struct pipe_pair {
struct pipe pp_wpipe;
struct pipe pp_rpipe;
struct rwlock pp_lock;
};
/*
* interfaces to the outside world
*/
int pipe_read(struct file *, struct uio *, int);
int pipe_write(struct file *, struct uio *, int);
int pipe_close(struct file *, struct proc *);
int pipe_kqfilter(struct file *fp, struct knote *kn);
int pipe_ioctl(struct file *, u_long, caddr_t, struct proc *);
int pipe_stat(struct file *fp, struct stat *ub, struct proc *p);
static const struct fileops pipeops = {
.fo_read = pipe_read,
.fo_write = pipe_write,
.fo_ioctl = pipe_ioctl,
.fo_kqfilter = pipe_kqfilter,
.fo_stat = pipe_stat,
.fo_close = pipe_close
};
void filt_pipedetach(struct knote *kn);
int filt_piperead(struct knote *kn, long hint);
int filt_pipewrite(struct knote *kn, long hint);
int filt_pipeexcept(struct knote *kn, long hint);
int filt_pipemodify(struct kevent *kev, struct knote *kn);
int filt_pipeprocess(struct knote *kn, struct kevent *kev);
const struct filterops pipe_rfiltops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_pipedetach,
.f_event = filt_piperead,
.f_modify = filt_pipemodify,
.f_process = filt_pipeprocess,
};
const struct filterops pipe_wfiltops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_pipedetach,
.f_event = filt_pipewrite,
.f_modify = filt_pipemodify,
.f_process = filt_pipeprocess,
};
const struct filterops pipe_efiltops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_pipedetach,
.f_event = filt_pipeexcept,
.f_modify = filt_pipemodify,
.f_process = filt_pipeprocess,
};
/*
* Default pipe buffer size(s), this can be kind-of large now because pipe
* space is pageable. The pipe code will try to maintain locality of
* reference for performance reasons, so small amounts of outstanding I/O
* will not wipe the cache.
*/
#define MINPIPESIZE (PIPE_SIZE/3)
/*
* Limit the number of "big" pipes
*/
#define LIMITBIGPIPES 32
unsigned int nbigpipe;
static unsigned int amountpipekva;
struct pool pipe_pair_pool;
int dopipe(struct proc *, int *, int);
void pipeselwakeup(struct pipe *);
int pipe_create(struct pipe *);
void pipe_destroy(struct pipe *);
int pipe_rundown(struct pipe *);
struct pipe *pipe_peer(struct pipe *);
int pipe_buffer_realloc(struct pipe *, u_int);
void pipe_buffer_free(struct pipe *);
int pipe_iolock(struct pipe *);
void pipe_iounlock(struct pipe *);
int pipe_iosleep(struct pipe *, const char *);
struct pipe_pair *pipe_pair_create(void);
void pipe_pair_destroy(struct pipe_pair *);
/*
* The pipe system call for the DTYPE_PIPE type of pipes
*/
int
sys_pipe(struct proc *p, void *v, register_t *retval)
{
struct sys_pipe_args /* {
syscallarg(int *) fdp;
} */ *uap = v;
return (dopipe(p, SCARG(uap, fdp), 0));
}
int
sys_pipe2(struct proc *p, void *v, register_t *retval)
{
struct sys_pipe2_args /* {
syscallarg(int *) fdp;
syscallarg(int) flags;
} */ *uap = v;
if (SCARG(uap, flags) & ~(O_CLOEXEC | FNONBLOCK))
return (EINVAL);
return (dopipe(p, SCARG(uap, fdp), SCARG(uap, flags)));
}
int
dopipe(struct proc *p, int *ufds, int flags)
{
struct filedesc *fdp = p->p_fd;
struct file *rf, *wf;
struct pipe_pair *pp;
struct pipe *rpipe, *wpipe = NULL;
int fds[2], cloexec, error;
cloexec = (flags & O_CLOEXEC) ? UF_EXCLOSE : 0;
pp = pipe_pair_create();
if (pp == NULL)
return (ENOMEM);
wpipe = &pp->pp_wpipe;
rpipe = &pp->pp_rpipe;
fdplock(fdp);
error = falloc(p, &rf, &fds[0]);
if (error != 0)
goto free2;
rf->f_flag = FREAD | FWRITE | (flags & FNONBLOCK);
rf->f_type = DTYPE_PIPE;
rf->f_data = rpipe;
rf->f_ops = &pipeops;
error = falloc(p, &wf, &fds[1]);
if (error != 0)
goto free3;
wf->f_flag = FREAD | FWRITE | (flags & FNONBLOCK);
wf->f_type = DTYPE_PIPE;
wf->f_data = wpipe;
wf->f_ops = &pipeops;
fdinsert(fdp, fds[0], cloexec, rf);
fdinsert(fdp, fds[1], cloexec, wf);
error = copyout(fds, ufds, sizeof(fds));
if (error == 0) {
fdpunlock(fdp);
#ifdef KTRACE
if (KTRPOINT(p, KTR_STRUCT))
ktrfds(p, fds, 2);
#endif
} else {
/* fdrelease() unlocks fdp. */
fdrelease(p, fds[0]);
fdplock(fdp);
fdrelease(p, fds[1]);
}
FRELE(rf, p);
FRELE(wf, p);
return (error);
free3:
fdremove(fdp, fds[0]);
closef(rf, p);
rpipe = NULL;
free2:
fdpunlock(fdp);
pipe_destroy(wpipe);
pipe_destroy(rpipe);
return (error);
}
/*
* Allocate kva for pipe circular buffer, the space is pageable.
* This routine will 'realloc' the size of a pipe safely, if it fails
* it will retain the old buffer.
* If it fails it will return ENOMEM.
*/
int
pipe_buffer_realloc(struct pipe *cpipe, u_int size)
{
caddr_t buffer;
/* buffer uninitialized or pipe locked */
KASSERT((cpipe->pipe_buffer.buffer == NULL) ||
(cpipe->pipe_state & PIPE_LOCK));
/* buffer should be empty */
KASSERT(cpipe->pipe_buffer.cnt == 0);
KERNEL_LOCK();
buffer = km_alloc(size, &kv_any, &kp_pageable, &kd_waitok);
KERNEL_UNLOCK();
if (buffer == NULL)
return (ENOMEM);
/* free old resources if we are resizing */
pipe_buffer_free(cpipe);
cpipe->pipe_buffer.buffer = buffer;
cpipe->pipe_buffer.size = size;
cpipe->pipe_buffer.in = 0;
cpipe->pipe_buffer.out = 0;
atomic_add_int(&amountpipekva, cpipe->pipe_buffer.size);
return (0);
}
/*
* initialize and allocate VM and memory for pipe
*/
int
pipe_create(struct pipe *cpipe)
{
int error;
error = pipe_buffer_realloc(cpipe, PIPE_SIZE);
if (error != 0)
return (error);
sigio_init(&cpipe->pipe_sigio);
getnanotime(&cpipe->pipe_ctime);
cpipe->pipe_atime = cpipe->pipe_ctime;
cpipe->pipe_mtime = cpipe->pipe_ctime;
return (0);
}
struct pipe *
pipe_peer(struct pipe *cpipe)
{
struct pipe *peer;
rw_assert_anylock(cpipe->pipe_lock);
peer = cpipe->pipe_peer;
if (peer == NULL || (peer->pipe_state & PIPE_EOF))
return (NULL);
return (peer);
}
/*
* Lock a pipe for exclusive I/O access.
*/
int
pipe_iolock(struct pipe *cpipe)
{
int error;
rw_assert_wrlock(cpipe->pipe_lock);
while (cpipe->pipe_state & PIPE_LOCK) {
cpipe->pipe_state |= PIPE_LWANT;
error = rwsleep_nsec(cpipe, cpipe->pipe_lock, PRIBIO | PCATCH,
"pipeiolk", INFSLP);
if (error)
return (error);
}
cpipe->pipe_state |= PIPE_LOCK;
return (0);
}
/*
* Unlock a pipe I/O lock.
*/
void
pipe_iounlock(struct pipe *cpipe)
{
rw_assert_wrlock(cpipe->pipe_lock);
KASSERT(cpipe->pipe_state & PIPE_LOCK);
cpipe->pipe_state &= ~PIPE_LOCK;
if (cpipe->pipe_state & PIPE_LWANT) {
cpipe->pipe_state &= ~PIPE_LWANT;
wakeup(cpipe);
}
}
/*
* Unlock the pipe I/O lock and go to sleep. Returns 0 on success and the I/O
* lock is relocked. Otherwise if a signal was caught, non-zero is returned and
* the I/O lock is not locked.
*
* Any caller must obtain a reference to the pipe by incrementing `pipe_busy'
* before calling this function in order ensure that the same pipe is not
* destroyed while sleeping.
*/
int
pipe_iosleep(struct pipe *cpipe, const char *wmesg)
{
int error;
pipe_iounlock(cpipe);
error = rwsleep_nsec(cpipe, cpipe->pipe_lock, PRIBIO | PCATCH, wmesg,
INFSLP);
if (error)
return (error);
return (pipe_iolock(cpipe));
}
void
pipeselwakeup(struct pipe *cpipe)
{
rw_assert_wrlock(cpipe->pipe_lock);
KNOTE(&cpipe->pipe_klist, 0);
if (cpipe->pipe_state & PIPE_ASYNC)
pgsigio(&cpipe->pipe_sigio, SIGIO, 0);
}
int
pipe_read(struct file *fp, struct uio *uio, int fflags)
{
struct pipe *rpipe = fp->f_data;
size_t nread = 0, size;
int error;
rw_enter_write(rpipe->pipe_lock);
++rpipe->pipe_busy;
error = pipe_iolock(rpipe);
if (error) {
--rpipe->pipe_busy;
pipe_rundown(rpipe);
rw_exit_write(rpipe->pipe_lock);
return (error);
}
while (uio->uio_resid) {
/* Normal pipe buffer receive. */
if (rpipe->pipe_buffer.cnt > 0) {
size = rpipe->pipe_buffer.size - rpipe->pipe_buffer.out;
if (size > rpipe->pipe_buffer.cnt)
size = rpipe->pipe_buffer.cnt;
if (size > uio->uio_resid)
size = uio->uio_resid;
rw_exit_write(rpipe->pipe_lock);
error = uiomove(&rpipe->pipe_buffer.buffer[rpipe->pipe_buffer.out],
size, uio);
rw_enter_write(rpipe->pipe_lock);
if (error) {
break;
}
rpipe->pipe_buffer.out += size;
if (rpipe->pipe_buffer.out >= rpipe->pipe_buffer.size)
rpipe->pipe_buffer.out = 0;
rpipe->pipe_buffer.cnt -= size;
/*
* If there is no more to read in the pipe, reset
* its pointers to the beginning. This improves
* cache hit stats.
*/
if (rpipe->pipe_buffer.cnt == 0) {
rpipe->pipe_buffer.in = 0;
rpipe->pipe_buffer.out = 0;
}
nread += size;
} else {
/*
* detect EOF condition
* read returns 0 on EOF, no need to set error
*/
if (rpipe->pipe_state & PIPE_EOF)
break;
/* If the "write-side" has been blocked, wake it up. */
if (rpipe->pipe_state & PIPE_WANTW) {
rpipe->pipe_state &= ~PIPE_WANTW;
wakeup(rpipe);
}
/* Break if some data was read. */
if (nread > 0)
break;
/* Handle non-blocking mode operation. */
if (fp->f_flag & FNONBLOCK) {
error = EAGAIN;
break;
}
/* Wait for more data. */
rpipe->pipe_state |= PIPE_WANTR;
error = pipe_iosleep(rpipe, "piperd");
if (error)
goto unlocked_error;
}
}
pipe_iounlock(rpipe);
if (error == 0)
getnanotime(&rpipe->pipe_atime);
unlocked_error:
--rpipe->pipe_busy;
if (pipe_rundown(rpipe) == 0 && rpipe->pipe_buffer.cnt < MINPIPESIZE) {
/* Handle write blocking hysteresis. */
if (rpipe->pipe_state & PIPE_WANTW) {
rpipe->pipe_state &= ~PIPE_WANTW;
wakeup(rpipe);
}
}
if (rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt >= PIPE_BUF)
pipeselwakeup(rpipe);
rw_exit_write(rpipe->pipe_lock);
return (error);
}
int
pipe_write(struct file *fp, struct uio *uio, int fflags)
{
struct pipe *rpipe = fp->f_data, *wpipe;
struct rwlock *lock = rpipe->pipe_lock;
size_t orig_resid;
int error;
rw_enter_write(lock);
wpipe = pipe_peer(rpipe);
/* Detect loss of pipe read side, issue SIGPIPE if lost. */
if (wpipe == NULL) {
rw_exit_write(lock);
return (EPIPE);
}
++wpipe->pipe_busy;
error = pipe_iolock(wpipe);
if (error) {
--wpipe->pipe_busy;
pipe_rundown(wpipe);
rw_exit_write(lock);
return (error);
}
/* If it is advantageous to resize the pipe buffer, do so. */
if (uio->uio_resid > PIPE_SIZE &&
wpipe->pipe_buffer.size <= PIPE_SIZE &&
wpipe->pipe_buffer.cnt == 0) {
unsigned int npipe;
npipe = atomic_inc_int_nv(&nbigpipe);
if (npipe > LIMITBIGPIPES ||
pipe_buffer_realloc(wpipe, BIG_PIPE_SIZE) != 0)
atomic_dec_int(&nbigpipe);
}
orig_resid = uio->uio_resid;
while (uio->uio_resid) {
size_t space;
if (wpipe->pipe_state & PIPE_EOF) {
error = EPIPE;
break;
}
space = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt;
/* Writes of size <= PIPE_BUF must be atomic. */
if (space < uio->uio_resid && orig_resid <= PIPE_BUF)
space = 0;
if (space > 0) {
size_t size; /* Transfer size */
size_t segsize; /* first segment to transfer */
/*
* Transfer size is minimum of uio transfer
* and free space in pipe buffer.
*/
if (space > uio->uio_resid)
size = uio->uio_resid;
else
size = space;
/*
* First segment to transfer is minimum of
* transfer size and contiguous space in
* pipe buffer. If first segment to transfer
* is less than the transfer size, we've got
* a wraparound in the buffer.
*/
segsize = wpipe->pipe_buffer.size -
wpipe->pipe_buffer.in;
if (segsize > size)
segsize = size;
/* Transfer first segment */
rw_exit_write(lock);
error = uiomove(&wpipe->pipe_buffer.buffer[wpipe->pipe_buffer.in],
segsize, uio);
rw_enter_write(lock);
if (error == 0 && segsize < size) {
/*
* Transfer remaining part now, to
* support atomic writes. Wraparound
* happened.
*/
#ifdef DIAGNOSTIC
if (wpipe->pipe_buffer.in + segsize !=
wpipe->pipe_buffer.size)
panic("Expected pipe buffer wraparound disappeared");
#endif
rw_exit_write(lock);
error = uiomove(&wpipe->pipe_buffer.buffer[0],
size - segsize, uio);
rw_enter_write(lock);
}
if (error == 0) {
wpipe->pipe_buffer.in += size;
if (wpipe->pipe_buffer.in >=
wpipe->pipe_buffer.size) {
#ifdef DIAGNOSTIC
if (wpipe->pipe_buffer.in != size - segsize + wpipe->pipe_buffer.size)
panic("Expected wraparound bad");
#endif
wpipe->pipe_buffer.in = size - segsize;
}
wpipe->pipe_buffer.cnt += size;
#ifdef DIAGNOSTIC
if (wpipe->pipe_buffer.cnt > wpipe->pipe_buffer.size)
panic("Pipe buffer overflow");
#endif
}
if (error)
break;
} else {
/* If the "read-side" has been blocked, wake it up. */
if (wpipe->pipe_state & PIPE_WANTR) {
wpipe->pipe_state &= ~PIPE_WANTR;
wakeup(wpipe);
}
/* Don't block on non-blocking I/O. */
if (fp->f_flag & FNONBLOCK) {
error = EAGAIN;
break;
}
/*
* We have no more space and have something to offer,
* wake up select/poll.
*/
pipeselwakeup(wpipe);
wpipe->pipe_state |= PIPE_WANTW;
error = pipe_iosleep(wpipe, "pipewr");
if (error)
goto unlocked_error;
/*
* If read side wants to go away, we just issue a
* signal to ourselves.
*/
if (wpipe->pipe_state & PIPE_EOF) {
error = EPIPE;
break;
}
}
}
pipe_iounlock(wpipe);
unlocked_error:
--wpipe->pipe_busy;
if (pipe_rundown(wpipe) == 0 && wpipe->pipe_buffer.cnt > 0) {
/*
* If we have put any characters in the buffer, we wake up
* the reader.
*/
if (wpipe->pipe_state & PIPE_WANTR) {
wpipe->pipe_state &= ~PIPE_WANTR;
wakeup(wpipe);
}
}
/* Don't return EPIPE if I/O was successful. */
if (wpipe->pipe_buffer.cnt == 0 &&
uio->uio_resid == 0 &&
error == EPIPE) {
error = 0;
}
if (error == 0)
getnanotime(&wpipe->pipe_mtime);
/* We have something to offer, wake up select/poll. */
if (wpipe->pipe_buffer.cnt)
pipeselwakeup(wpipe);
rw_exit_write(lock);
return (error);
}
/*
* we implement a very minimal set of ioctls for compatibility with sockets.
*/
int
pipe_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p)
{
struct pipe *mpipe = fp->f_data;
int error = 0;
switch (cmd) {
case FIONBIO:
break;
case FIOASYNC:
rw_enter_write(mpipe->pipe_lock);
if (*(int *)data) {
mpipe->pipe_state |= PIPE_ASYNC;
} else {
mpipe->pipe_state &= ~PIPE_ASYNC;
}
rw_exit_write(mpipe->pipe_lock);
break;
case FIONREAD:
rw_enter_read(mpipe->pipe_lock);
*(int *)data = mpipe->pipe_buffer.cnt;
rw_exit_read(mpipe->pipe_lock);
break;
case FIOSETOWN:
case SIOCSPGRP:
case TIOCSPGRP:
error = sigio_setown(&mpipe->pipe_sigio, cmd, data);
break;
case FIOGETOWN:
case SIOCGPGRP:
case TIOCGPGRP:
sigio_getown(&mpipe->pipe_sigio, cmd, data);
break;
default:
error = ENOTTY;
}
return (error);
}
int
pipe_stat(struct file *fp, struct stat *ub, struct proc *p)
{
struct pipe *pipe = fp->f_data;
memset(ub, 0, sizeof(*ub));
rw_enter_read(pipe->pipe_lock);
ub->st_mode = S_IFIFO;
ub->st_blksize = pipe->pipe_buffer.size;
ub->st_size = pipe->pipe_buffer.cnt;
ub->st_blocks = (ub->st_size + ub->st_blksize - 1) / ub->st_blksize;
ub->st_atim.tv_sec = pipe->pipe_atime.tv_sec;
ub->st_atim.tv_nsec = pipe->pipe_atime.tv_nsec;
ub->st_mtim.tv_sec = pipe->pipe_mtime.tv_sec;
ub->st_mtim.tv_nsec = pipe->pipe_mtime.tv_nsec;
ub->st_ctim.tv_sec = pipe->pipe_ctime.tv_sec;
ub->st_ctim.tv_nsec = pipe->pipe_ctime.tv_nsec;
ub->st_uid = fp->f_cred->cr_uid;
ub->st_gid = fp->f_cred->cr_gid;
rw_exit_read(pipe->pipe_lock);
/*
* Left as 0: st_dev, st_ino, st_nlink, st_rdev, st_flags, st_gen.
* XXX (st_dev, st_ino) should be unique.
*/
return (0);
}
int
pipe_close(struct file *fp, struct proc *p)
{
struct pipe *cpipe = fp->f_data;
fp->f_ops = NULL;
fp->f_data = NULL;
pipe_destroy(cpipe);
return (0);
}
/*
* Free kva for pipe circular buffer.
* No pipe lock check as only called from pipe_buffer_realloc() and pipeclose()
*/
void
pipe_buffer_free(struct pipe *cpipe)
{
u_int size;
if (cpipe->pipe_buffer.buffer == NULL)
return;
size = cpipe->pipe_buffer.size;
KERNEL_LOCK();
km_free(cpipe->pipe_buffer.buffer, size, &kv_any, &kp_pageable);
KERNEL_UNLOCK();
cpipe->pipe_buffer.buffer = NULL;
atomic_sub_int(&amountpipekva, size);
if (size > PIPE_SIZE)
atomic_dec_int(&nbigpipe);
}
/*
* shutdown the pipe, and free resources.
*/
void
pipe_destroy(struct pipe *cpipe)
{
struct pipe *ppipe;
if (cpipe == NULL)
return;
rw_enter_write(cpipe->pipe_lock);
pipeselwakeup(cpipe);
sigio_free(&cpipe->pipe_sigio);
/*
* If the other side is blocked, wake it up saying that
* we want to close it down.
*/
cpipe->pipe_state |= PIPE_EOF;
while (cpipe->pipe_busy) {
wakeup(cpipe);
cpipe->pipe_state |= PIPE_WANTD;
rwsleep_nsec(cpipe, cpipe->pipe_lock, PRIBIO, "pipecl", INFSLP);
}
/* Disconnect from peer. */
if ((ppipe = cpipe->pipe_peer) != NULL) {
pipeselwakeup(ppipe);
ppipe->pipe_state |= PIPE_EOF;
wakeup(ppipe);
ppipe->pipe_peer = NULL;
}
pipe_buffer_free(cpipe);
rw_exit_write(cpipe->pipe_lock);
if (ppipe == NULL)
pipe_pair_destroy(cpipe->pipe_pair);
}
/*
* Returns non-zero if a rundown is currently ongoing.
*/
int
pipe_rundown(struct pipe *cpipe)
{
rw_assert_wrlock(cpipe->pipe_lock);
if (cpipe->pipe_busy > 0 || (cpipe->pipe_state & PIPE_WANTD) == 0)
return (0);
/* Only wakeup pipe_destroy() once the pipe is no longer busy. */
cpipe->pipe_state &= ~(PIPE_WANTD | PIPE_WANTR | PIPE_WANTW);
wakeup(cpipe);
return (1);
}
int
pipe_kqfilter(struct file *fp, struct knote *kn)
{
struct pipe *rpipe = kn->kn_fp->f_data, *wpipe;
struct rwlock *lock = rpipe->pipe_lock;
int error = 0;
rw_enter_write(lock);
wpipe = pipe_peer(rpipe);
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &pipe_rfiltops;
kn->kn_hook = rpipe;
klist_insert_locked(&rpipe->pipe_klist, kn);
break;
case EVFILT_WRITE:
if (wpipe == NULL) {
/* other end of pipe has been closed */
error = EPIPE;
break;
}
kn->kn_fop = &pipe_wfiltops;
kn->kn_hook = wpipe;
klist_insert_locked(&wpipe->pipe_klist, kn);
break;
case EVFILT_EXCEPT:
if (kn->kn_flags & __EV_SELECT) {
/* Prevent triggering exceptfds. */
error = EPERM;
break;
}
if ((kn->kn_flags & __EV_POLL) == 0) {
/* Disallow usage through kevent(2). */
error = EINVAL;
break;
}
kn->kn_fop = &pipe_efiltops;
kn->kn_hook = rpipe;
klist_insert_locked(&rpipe->pipe_klist, kn);
break;
default:
error = EINVAL;
}
rw_exit_write(lock);
return (error);
}
void
filt_pipedetach(struct knote *kn)
{
struct pipe *cpipe = kn->kn_hook;
klist_remove(&cpipe->pipe_klist, kn);
}
int
filt_piperead(struct knote *kn, long hint)
{
struct pipe *rpipe = kn->kn_fp->f_data, *wpipe;
rw_assert_wrlock(rpipe->pipe_lock);
wpipe = pipe_peer(rpipe);
kn->kn_data = rpipe->pipe_buffer.cnt;
if ((rpipe->pipe_state & PIPE_EOF) || wpipe == NULL) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL)
kn->kn_flags |= __EV_HUP;
return (1);
}
return (kn->kn_data > 0);
}
int
filt_pipewrite(struct knote *kn, long hint)
{
struct pipe *rpipe = kn->kn_fp->f_data, *wpipe;
rw_assert_wrlock(rpipe->pipe_lock);
wpipe = pipe_peer(rpipe);
if (wpipe == NULL) {
kn->kn_data = 0;
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL)
kn->kn_flags |= __EV_HUP;
return (1);
}
kn->kn_data = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt;
return (kn->kn_data >= PIPE_BUF);
}
int
filt_pipeexcept(struct knote *kn, long hint)
{
struct pipe *rpipe = kn->kn_fp->f_data, *wpipe;
int active = 0;
rw_assert_wrlock(rpipe->pipe_lock);
wpipe = pipe_peer(rpipe);
if (kn->kn_flags & __EV_POLL) {
if ((rpipe->pipe_state & PIPE_EOF) || wpipe == NULL) {
kn->kn_flags |= __EV_HUP;
active = 1;
}
}
return (active);
}
int
filt_pipemodify(struct kevent *kev, struct knote *kn)
{
struct pipe *rpipe = kn->kn_fp->f_data;
int active;
rw_enter_write(rpipe->pipe_lock);
active = knote_modify(kev, kn);
rw_exit_write(rpipe->pipe_lock);
return (active);
}
int
filt_pipeprocess(struct knote *kn, struct kevent *kev)
{
struct pipe *rpipe = kn->kn_fp->f_data;
int active;
rw_enter_write(rpipe->pipe_lock);
active = knote_process(kn, kev);
rw_exit_write(rpipe->pipe_lock);
return (active);
}
void
pipe_init(void)
{
pool_init(&pipe_pair_pool, sizeof(struct pipe_pair), 0, IPL_MPFLOOR,
PR_WAITOK, "pipepl", NULL);
}
struct pipe_pair *
pipe_pair_create(void)
{
struct pipe_pair *pp;
pp = pool_get(&pipe_pair_pool, PR_WAITOK | PR_ZERO);
pp->pp_wpipe.pipe_pair = pp;
pp->pp_rpipe.pipe_pair = pp;
pp->pp_wpipe.pipe_peer = &pp->pp_rpipe;
pp->pp_rpipe.pipe_peer = &pp->pp_wpipe;
/*
* One lock is used per pipe pair in order to obtain exclusive access to
* the pipe pair.
*/
rw_init(&pp->pp_lock, "pipelk");
pp->pp_wpipe.pipe_lock = &pp->pp_lock;
pp->pp_rpipe.pipe_lock = &pp->pp_lock;
klist_init_rwlock(&pp->pp_wpipe.pipe_klist, &pp->pp_lock);
klist_init_rwlock(&pp->pp_rpipe.pipe_klist, &pp->pp_lock);
if (pipe_create(&pp->pp_wpipe) || pipe_create(&pp->pp_rpipe))
goto err;
return (pp);
err:
pipe_destroy(&pp->pp_wpipe);
pipe_destroy(&pp->pp_rpipe);
return (NULL);
}
void
pipe_pair_destroy(struct pipe_pair *pp)
{
klist_free(&pp->pp_wpipe.pipe_klist);
klist_free(&pp->pp_rpipe.pipe_klist);
pool_put(&pipe_pair_pool, pp);
}
22
22
11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* $OpenBSD: strncpy.c,v 1.9 2014/06/10 04:16:57 deraadt Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <lib/libkern/libkern.h>
/*
* Copy src to dst, truncating or null-padding to always copy n bytes.
* Return dst.
*/
char *
strncpy(char *dst, const char *src, size_t n)
{
if (n != 0) {
char *d = dst;
const char *s = src;
do {
if ((*d++ = *s++) == 0) {
/* NUL pad the remaining n-1 bytes */
while (--n != 0)
*d++ = 0;
break;
}
} while (--n != 0);
}
return (dst);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* $OpenBSD: uk.c,v 1.26 2021/10/24 16:57:30 mpi Exp $ */
/* $NetBSD: uk.c,v 1.15 1996/03/17 00:59:57 thorpej Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Dummy driver for a device we can't identify.
* Originally by Julian Elischer (julian@tfs.com)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/vnode.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
#define UKUNIT(z) (minor(z))
struct uk_softc {
struct device sc_dev;
struct scsi_link *sc_link; /* all the inter level info */
};
int ukmatch(struct device *, void *, void *);
void ukattach(struct device *, struct device *, void *);
int ukdetach(struct device *, int);
const struct cfattach uk_ca = {
sizeof(struct uk_softc), ukmatch, ukattach, ukdetach
};
struct cfdriver uk_cd = {
NULL, "uk", DV_DULL
};
#define uklookup(unit) (struct uk_softc *)device_lookup(&uk_cd, (unit))
int
ukmatch(struct device *parent, void *match, void *aux)
{
return 1;
}
/*
* The routine called by the low level scsi routine when it discovers
* a device suitable for this driver.
*/
void
ukattach(struct device *parent, struct device *self, void *aux)
{
struct uk_softc *sc = (void *)self;
struct scsi_attach_args *sa = aux;
struct scsi_link *link = sa->sa_sc_link;
SC_DEBUG(link, SDEV_DB2, ("ukattach: "));
/* Store information needed to contact our base driver. */
sc->sc_link = link;
link->device_softc = sc;
link->openings = 1;
printf("\n");
}
int
ukdetach(struct device *self, int flags)
{
int bmaj, cmaj, mn;
mn = self->dv_unit;
for (bmaj = 0; bmaj < nblkdev; bmaj++)
if (bdevsw[bmaj].d_open == ukopen)
vdevgone(bmaj, mn, mn, VBLK);
for (cmaj = 0; cmaj < nchrdev; cmaj++)
if (cdevsw[cmaj].d_open == ukopen)
vdevgone(cmaj, mn, mn, VCHR);
return 0;
}
/*
* Open the device.
*/
int
ukopen(dev_t dev, int flag, int fmt, struct proc *p)
{
struct uk_softc *sc;
struct scsi_link *link;
int unit;
unit = UKUNIT(dev);
sc = uklookup(unit);
if (sc == NULL)
return ENXIO;
link = sc->sc_link;
SC_DEBUG(link, SDEV_DB1, ("ukopen: dev=0x%x (unit %d (of %d))\n",
dev, unit, uk_cd.cd_ndevs));
/* Only allow one at a time. */
if (ISSET(link->flags, SDEV_OPEN)) {
device_unref(&sc->sc_dev);
return EBUSY;
}
SET(link->flags, SDEV_OPEN);
SC_DEBUG(link, SDEV_DB3, ("open complete\n"));
device_unref(&sc->sc_dev);
return 0;
}
/*
* Close the device. Called only if we are the LAST
* occurrence of an open device.
*/
int
ukclose(dev_t dev, int flag, int fmt, struct proc *p)
{
struct uk_softc *sc;
sc = uklookup(UKUNIT(dev));
if (sc == NULL)
return ENXIO;
SC_DEBUG(sc->sc_link, SDEV_DB1, ("closing\n"));
CLR(sc->sc_link->flags, SDEV_OPEN);
device_unref(&sc->sc_dev);
return 0;
}
/*
* Perform special action on behalf of the user
* Only does generic scsi ioctls.
*/
int
ukioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct uk_softc *sc;
int rv;
sc = uklookup(UKUNIT(dev));
if (sc == NULL)
return ENXIO;
rv = scsi_do_ioctl(sc->sc_link, cmd, addr, flag);
device_unref(&sc->sc_dev);
return rv;
}
21
11
13
8
4
9
3
10
15
6
2
7
6
10
3
12
3
6
1
2
4
1
4
4
43
1
14
16
14
27
21
5
9
8
8
5
11
15
7
1
1
6
17
1
1
7
3
4
1
1
4
3
10
1
4
7
3
7
1
7
7
7
6
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
/* $OpenBSD: fifo_vnops.c,v 1.96 2022/07/01 09:56:17 mvs Exp $ */
/* $NetBSD: fifo_vnops.c,v 1.18 1996/03/16 23:52:42 christos Exp $ */
/*
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)fifo_vnops.c 8.4 (Berkeley) 8/10/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/event.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/unistd.h>
#include <miscfs/fifofs/fifo.h>
/*
* This structure is associated with the FIFO vnode and stores
* the state associated with the FIFO.
*/
struct fifoinfo {
struct socket *fi_readsock;
struct socket *fi_writesock;
long fi_readers;
long fi_writers;
};
const struct vops fifo_vops = {
.vop_lookup = vop_generic_lookup,
.vop_create = vop_generic_badop,
.vop_mknod = vop_generic_badop,
.vop_open = fifo_open,
.vop_close = fifo_close,
.vop_access = fifo_ebadf,
.vop_getattr = fifo_ebadf,
.vop_setattr = fifo_ebadf,
.vop_read = fifo_read,
.vop_write = fifo_write,
.vop_ioctl = fifo_ioctl,
.vop_kqfilter = fifo_kqfilter,
.vop_revoke = vop_generic_revoke,
.vop_fsync = nullop,
.vop_remove = vop_generic_badop,
.vop_link = vop_generic_badop,
.vop_rename = vop_generic_badop,
.vop_mkdir = vop_generic_badop,
.vop_rmdir = vop_generic_badop,
.vop_symlink = vop_generic_badop,
.vop_readdir = vop_generic_badop,
.vop_readlink = vop_generic_badop,
.vop_abortop = vop_generic_badop,
.vop_inactive = fifo_inactive,
.vop_reclaim = fifo_reclaim,
.vop_lock = nullop,
.vop_unlock = nullop,
.vop_islocked = nullop,
.vop_bmap = vop_generic_bmap,
.vop_strategy = vop_generic_badop,
.vop_print = fifo_print,
.vop_pathconf = fifo_pathconf,
.vop_advlock = fifo_advlock,
.vop_bwrite = nullop
};
void filt_fifordetach(struct knote *kn);
int filt_fiforead(struct knote *kn, long hint);
void filt_fifowdetach(struct knote *kn);
int filt_fifowrite(struct knote *kn, long hint);
int filt_fifoexcept(struct knote *kn, long hint);
int filt_fifomodify(struct kevent *kev, struct knote *kn);
int filt_fifoprocess(struct knote *kn, struct kevent *kev);
const struct filterops fiforead_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_fifordetach,
.f_event = filt_fiforead,
.f_modify = filt_fifomodify,
.f_process = filt_fifoprocess,
};
const struct filterops fifowrite_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_fifowdetach,
.f_event = filt_fifowrite,
.f_modify = filt_fifomodify,
.f_process = filt_fifoprocess,
};
const struct filterops fifoexcept_filtops = {
.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
.f_attach = NULL,
.f_detach = filt_fifordetach,
.f_event = filt_fifoexcept,
.f_modify = filt_fifomodify,
.f_process = filt_fifoprocess,
};
/*
* Open called to set up a new instance of a fifo or
* to find an active instance of a fifo.
*/
/* ARGSUSED */
int
fifo_open(void *v)
{
struct vop_open_args *ap = v;
struct vnode *vp = ap->a_vp;
struct fifoinfo *fip;
struct socket *rso, *wso;
int error;
if ((fip = vp->v_fifoinfo) == NULL) {
fip = malloc(sizeof(*fip), M_VNODE, M_WAITOK);
vp->v_fifoinfo = fip;
if ((error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) != 0) {
free(fip, M_VNODE, sizeof *fip);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_readsock = rso;
if ((error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0)) != 0) {
(void)soclose(rso, 0);
free(fip, M_VNODE, sizeof *fip);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_writesock = wso;
if ((error = soconnect2(wso, rso)) != 0) {
(void)soclose(wso, 0);
(void)soclose(rso, 0);
free(fip, M_VNODE, sizeof *fip);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_readers = fip->fi_writers = 0;
solock(wso);
wso->so_state |= SS_CANTSENDMORE;
wso->so_snd.sb_lowat = PIPE_BUF;
sounlock(wso);
} else {
rso = fip->fi_readsock;
wso = fip->fi_writesock;
}
if (ap->a_mode & FREAD) {
fip->fi_readers++;
if (fip->fi_readers == 1) {
solock(wso);
wso->so_state &= ~SS_CANTSENDMORE;
sounlock(wso);
if (fip->fi_writers > 0)
wakeup(&fip->fi_writers);
}
}
if (ap->a_mode & FWRITE) {
fip->fi_writers++;
if ((ap->a_mode & O_NONBLOCK) && fip->fi_readers == 0) {
error = ENXIO;
goto bad;
}
if (fip->fi_writers == 1) {
solock(rso);
rso->so_state &= ~(SS_CANTRCVMORE|SS_ISDISCONNECTED);
sounlock(rso);
if (fip->fi_readers > 0)
wakeup(&fip->fi_readers);
}
}
if ((ap->a_mode & O_NONBLOCK) == 0) {
if ((ap->a_mode & FREAD) && fip->fi_writers == 0) {
VOP_UNLOCK(vp);
error = tsleep_nsec(&fip->fi_readers,
PCATCH | PSOCK, "fifor", INFSLP);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error)
goto bad;
}
if ((ap->a_mode & FWRITE) && fip->fi_readers == 0) {
VOP_UNLOCK(vp);
error = tsleep_nsec(&fip->fi_writers,
PCATCH | PSOCK, "fifow", INFSLP);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error)
goto bad;
}
}
return (0);
bad:
VOP_CLOSE(vp, ap->a_mode, ap->a_cred, ap->a_p);
return (error);
}
/*
* Vnode op for read
*/
/* ARGSUSED */
int
fifo_read(void *v)
{
struct vop_read_args *ap = v;
struct uio *uio = ap->a_uio;
struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
int error, flags = 0;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("fifo_read mode");
#endif
if (uio->uio_resid == 0)
return (0);
if (ap->a_ioflag & IO_NDELAY)
flags |= MSG_DONTWAIT;
VOP_UNLOCK(ap->a_vp);
error = soreceive(rso, NULL, uio, NULL, NULL, &flags, 0);
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
if (ap->a_ioflag & IO_NDELAY) {
if (error == EWOULDBLOCK &&
ap->a_vp->v_fifoinfo->fi_writers == 0)
error = 0;
}
return (error);
}
/*
* Vnode op for write
*/
/* ARGSUSED */
int
fifo_write(void *v)
{
struct vop_write_args *ap = v;
struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
int error, flags = 0;
#ifdef DIAGNOSTIC
if (ap->a_uio->uio_rw != UIO_WRITE)
panic("fifo_write mode");
#endif
if (ap->a_ioflag & IO_NDELAY)
flags |= MSG_DONTWAIT;
VOP_UNLOCK(ap->a_vp);
error = sosend(wso, NULL, ap->a_uio, NULL, NULL, flags);
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
return (error);
}
/*
* Device ioctl operation.
*/
/* ARGSUSED */
int
fifo_ioctl(void *v)
{
struct vop_ioctl_args *ap = v;
struct file filetmp;
int error;
if (ap->a_command == FIONBIO)
return (0);
if (ap->a_fflag & FREAD) {
filetmp.f_data = ap->a_vp->v_fifoinfo->fi_readsock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
if (error)
return (error);
}
if (ap->a_fflag & FWRITE) {
filetmp.f_data = ap->a_vp->v_fifoinfo->fi_writesock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
if (error)
return (error);
}
return (0);
}
int
fifo_inactive(void *v)
{
struct vop_inactive_args *ap = v;
VOP_UNLOCK(ap->a_vp);
return (0);
}
/*
* Device close routine
*/
/* ARGSUSED */
int
fifo_close(void *v)
{
struct vop_close_args *ap = v;
struct vnode *vp = ap->a_vp;
struct fifoinfo *fip = vp->v_fifoinfo;
int error1 = 0, error2 = 0;
if (fip == NULL)
return (0);
if (ap->a_fflag & FREAD) {
if (--fip->fi_readers == 0) {
struct socket *wso = fip->fi_writesock;
solock(wso);
socantsendmore(wso);
sounlock(wso);
}
}
if (ap->a_fflag & FWRITE) {
if (--fip->fi_writers == 0) {
struct socket *rso = fip->fi_readsock;
solock(rso);
/* SS_ISDISCONNECTED will result in POLLHUP */
rso->so_state |= SS_ISDISCONNECTED;
socantrcvmore(rso);
sounlock(rso);
}
}
if (fip->fi_readers == 0 && fip->fi_writers == 0) {
error1 = soclose(fip->fi_readsock, 0);
error2 = soclose(fip->fi_writesock, 0);
free(fip, M_VNODE, sizeof *fip);
vp->v_fifoinfo = NULL;
}
return (error1 ? error1 : error2);
}
int
fifo_reclaim(void *v)
{
struct vop_reclaim_args *ap = v;
struct vnode *vp = ap->a_vp;
struct fifoinfo *fip = vp->v_fifoinfo;
if (fip == NULL)
return (0);
soclose(fip->fi_readsock, 0);
soclose(fip->fi_writesock, 0);
free(fip, M_VNODE, sizeof *fip);
vp->v_fifoinfo = NULL;
return (0);
}
/*
* Print out the contents of a fifo vnode.
*/
int
fifo_print(void *v)
{
struct vop_print_args *ap = v;
printf("tag VT_NON");
fifo_printinfo(ap->a_vp);
printf("\n");
return 0;
}
/*
* Print out internal contents of a fifo vnode.
*/
void
fifo_printinfo(struct vnode *vp)
{
struct fifoinfo *fip = vp->v_fifoinfo;
printf(", fifo with %ld readers and %ld writers",
fip->fi_readers, fip->fi_writers);
}
/*
* Return POSIX pathconf information applicable to fifo's.
*/
int
fifo_pathconf(void *v)
{
struct vop_pathconf_args *ap = v;
int error = 0;
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
break;
case _PC_TIMESTAMP_RESOLUTION:
*ap->a_retval = 1;
break;
default:
error = EINVAL;
break;
}
return (error);
}
/*
* Fifo failed operation
*/
/*ARGSUSED*/
int
fifo_ebadf(void *v)
{
return (EBADF);
}
/*
* Fifo advisory byte-level locks.
*/
/* ARGSUSED */
int
fifo_advlock(void *v)
{
return (EOPNOTSUPP);
}
int
fifo_kqfilter(void *v)
{
struct vop_kqfilter_args *ap = v;
struct fifoinfo *fip = ap->a_vp->v_fifoinfo;
struct sockbuf *sb;
struct socket *so;
switch (ap->a_kn->kn_filter) {
case EVFILT_READ:
if (!(ap->a_fflag & FREAD))
return (EINVAL);
ap->a_kn->kn_fop = &fiforead_filtops;
so = fip->fi_readsock;
sb = &so->so_rcv;
break;
case EVFILT_WRITE:
if (!(ap->a_fflag & FWRITE)) {
/* Tell upper layer to ask for POLLUP only */
if (ap->a_kn->kn_flags & (__EV_POLL | __EV_SELECT))
return (EPERM);
return (EINVAL);
}
ap->a_kn->kn_fop = &fifowrite_filtops;
so = fip->fi_writesock;
sb = &so->so_snd;
break;
case EVFILT_EXCEPT:
if (ap->a_kn->kn_flags & __EV_SELECT) {
/* Prevent triggering exceptfds. */
return (EPERM);
}
if ((ap->a_kn->kn_flags & __EV_POLL) == 0) {
/* Disallow usage through kevent(2). */
return (EINVAL);
}
ap->a_kn->kn_fop = &fifoexcept_filtops;
so = fip->fi_readsock;
sb = &so->so_rcv;
break;
default:
return (EINVAL);
}
ap->a_kn->kn_hook = so;
klist_insert(&sb->sb_sel.si_note, ap->a_kn);
return (0);
}
void
filt_fifordetach(struct knote *kn)
{
struct socket *so = (struct socket *)kn->kn_hook;
klist_remove(&so->so_rcv.sb_sel.si_note, kn);
}
int
filt_fiforead(struct knote *kn, long hint)
{
struct socket *so = kn->kn_hook;
int rv;
soassertlocked(so);
kn->kn_data = so->so_rcv.sb_cc;
if (so->so_state & SS_CANTRCVMORE) {
kn->kn_flags |= EV_EOF;
if (kn->kn_flags & __EV_POLL) {
if (so->so_state & SS_ISDISCONNECTED)
kn->kn_flags |= __EV_HUP;
else
kn->kn_flags &= ~__EV_HUP;
}
rv = 1;
} else {
kn->kn_flags &= ~(EV_EOF | __EV_HUP);
rv = (kn->kn_data > 0);
}
return (rv);
}
void
filt_fifowdetach(struct knote *kn)
{
struct socket *so = (struct socket *)kn->kn_hook;
klist_remove(&so->so_snd.sb_sel.si_note, kn);
}
int
filt_fifowrite(struct knote *kn, long hint)
{
struct socket *so = kn->kn_hook;
int rv;
soassertlocked(so);
kn->kn_data = sbspace(so, &so->so_snd);
if (so->so_state & SS_CANTSENDMORE) {
kn->kn_flags |= EV_EOF;
rv = 1;
} else {
kn->kn_flags &= ~EV_EOF;
rv = (kn->kn_data >= so->so_snd.sb_lowat);
}
return (rv);
}
int
filt_fifoexcept(struct knote *kn, long hint)
{
struct socket *so = kn->kn_hook;
int rv = 0;
soassertlocked(so);
if (kn->kn_flags & __EV_POLL) {
if (so->so_state & SS_ISDISCONNECTED) {
kn->kn_flags |= __EV_HUP;
rv = 1;
} else {
kn->kn_flags &= ~__EV_HUP;
}
}
return (rv);
}
int
filt_fifomodify(struct kevent *kev, struct knote *kn)
{
struct socket *so = kn->kn_hook;
int rv;
solock(so);
rv = knote_modify(kev, kn);
sounlock(so);
return (rv);
}
int
filt_fifoprocess(struct knote *kn, struct kevent *kev)
{
struct socket *so = kn->kn_hook;
int rv;
solock(so);
rv = knote_process(kn, kev);
sounlock(so);
return (rv);
}
6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/* $OpenBSD: mbuf.h,v 1.255 2022/08/15 16:15:37 bluhm Exp $ */
/* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)mbuf.h 8.5 (Berkeley) 2/19/95
*/
#ifndef _SYS_MBUF_H_
#define _SYS_MBUF_H_
#include <sys/queue.h>
/*
* Constants related to network buffer management.
* MCLBYTES must be no larger than PAGE_SIZE (the software page size) and,
* on machines that exchange pages of input or output buffers with mbuf
* clusters (MAPPED_MBUFS), MCLBYTES must also be an integral multiple
* of the hardware page size.
*/
#define MSIZE 256 /* size of an mbuf */
/*
* Mbufs are of a single size, MSIZE, which includes overhead. An mbuf may
* add a single "mbuf cluster" of size MCLBYTES, which has no additional
* overhead and is used instead of the internal data area; this is done when
* at least MINCLSIZE of data must be stored.
*/
#define MLEN (MSIZE - sizeof(struct m_hdr)) /* normal data len */
#define MHLEN (MLEN - sizeof(struct pkthdr)) /* data len w/pkthdr */
#define MAXMCLBYTES (64 * 1024) /* largest cluster from the stack */
#define MINCLSIZE (MHLEN + MLEN + 1) /* smallest amount to put in cluster */
#define M_MAXCOMPRESS (MHLEN / 2) /* max amount to copy for compression */
#define MCLSHIFT 11 /* convert bytes to m_buf clusters */
/* 2K cluster can hold Ether frame */
#define MCLBYTES (1 << MCLSHIFT) /* size of a m_buf cluster */
#define MCLOFSET (MCLBYTES - 1)
/* Packet tags structure */
struct m_tag {
SLIST_ENTRY(m_tag) m_tag_link; /* List of packet tags */
u_int16_t m_tag_id; /* Tag ID */
u_int16_t m_tag_len; /* Length of data */
};
/*
* Macros for type conversion
* mtod(m,t) - convert mbuf pointer to data pointer of correct type
*/
#define mtod(m,t) ((t)((m)->m_data))
/* header at beginning of each mbuf: */
struct m_hdr {
struct mbuf *mh_next; /* next buffer in chain */
struct mbuf *mh_nextpkt; /* next chain in queue/record */
caddr_t mh_data; /* location of data */
u_int mh_len; /* amount of data in this mbuf */
short mh_type; /* type of data in this mbuf */
u_short mh_flags; /* flags; see below */
#ifndef __LP64__
u_int mh_pad; /* pad to 8-byte boundary */
#endif
};
/* pf stuff */
struct pf_state_key;
struct inpcb;
struct pkthdr_pf {
struct pf_state_key *statekey; /* pf stackside statekey */
struct inpcb *inp; /* connected pcb for outgoing packet */
u_int32_t qid; /* queue id */
u_int16_t tag; /* tag id */
u_int16_t delay; /* delay packet by X ms */
u_int8_t flags;
u_int8_t routed;
u_int8_t prio;
u_int8_t pad[1];
};
/* pkthdr_pf.flags */
#define PF_TAG_GENERATED 0x01
#define PF_TAG_SYNCOOKIE_RECREATED 0x02
#define PF_TAG_TRANSLATE_LOCALHOST 0x04
#define PF_TAG_DIVERTED 0x08
#define PF_TAG_DIVERTED_PACKET 0x10
#define PF_TAG_REROUTE 0x20
#define PF_TAG_REFRAGMENTED 0x40 /* refragmented ipv6 packet */
#define PF_TAG_PROCESSED 0x80 /* packet was checked by pf */
#ifdef _KERNEL
#define MPF_BITS \
("\20\1GENERATED\2SYNCOOKIE_RECREATED\3TRANSLATE_LOCALHOST\4DIVERTED" \
"\5DIVERTED_PACKET\6REROUTE\7REFRAGMENTED\10PROCESSED")
#endif
/* record/packet header in first mbuf of chain; valid if M_PKTHDR set */
struct pkthdr {
void *ph_cookie; /* additional data */
SLIST_HEAD(, m_tag) ph_tags; /* list of packet tags */
int64_t ph_timestamp; /* packet timestamp */
int len; /* total packet length */
u_int16_t ph_tagsset; /* mtags attached */
u_int16_t ph_flowid; /* pseudo unique flow id */
u_int16_t csum_flags; /* checksum flags */
u_int16_t ether_vtag; /* Ethernet 802.1p+Q vlan tag */
u_int ph_rtableid; /* routing table id */
u_int ph_ifidx; /* rcv interface index */
u_int8_t ph_loopcnt; /* mbuf is looping in kernel */
u_int8_t ph_family; /* af, used when queueing */
struct pkthdr_pf pf;
};
/* description of external storage mapped into mbuf, valid if M_EXT set */
struct mbuf_ext {
caddr_t ext_buf; /* start of buffer */
void *ext_arg;
u_int ext_free_fn; /* index of free function */
u_int ext_size; /* size of buffer, for ext_free_fn */
struct mbuf *ext_nextref;
struct mbuf *ext_prevref;
#ifdef DEBUG
const char *ext_ofile;
const char *ext_nfile;
int ext_oline;
int ext_nline;
#endif
};
struct mbuf {
struct m_hdr m_hdr;
union {
struct {
struct pkthdr MH_pkthdr; /* M_PKTHDR set */
union {
struct mbuf_ext MH_ext; /* M_EXT set */
char MH_databuf[MHLEN];
} MH_dat;
} MH;
char M_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */
} M_dat;
};
#define m_next m_hdr.mh_next
#define m_len m_hdr.mh_len
#define m_data m_hdr.mh_data
#define m_type m_hdr.mh_type
#define m_flags m_hdr.mh_flags
#define m_nextpkt m_hdr.mh_nextpkt
#define m_pkthdr M_dat.MH.MH_pkthdr
#define m_ext M_dat.MH.MH_dat.MH_ext
#define m_pktdat M_dat.MH.MH_dat.MH_databuf
#define m_dat M_dat.M_databuf
/* mbuf flags */
#define M_EXT 0x0001 /* has associated external storage */
#define M_PKTHDR 0x0002 /* start of record */
#define M_EOR 0x0004 /* end of record */
#define M_EXTWR 0x0008 /* external storage is writable */
#define M_PROTO1 0x0010 /* protocol-specific */
/* mbuf pkthdr flags, also in m_flags */
#define M_VLANTAG 0x0020 /* ether_vtag is valid */
#define M_LOOP 0x0040 /* packet has been sent from local machine */
#define M_BCAST 0x0100 /* sent/received as link-level broadcast */
#define M_MCAST 0x0200 /* sent/received as link-level multicast */
#define M_CONF 0x0400 /* payload was encrypted (ESP-transport) */
#define M_AUTH 0x0800 /* payload was authenticated (AH or ESP auth) */
#define M_TUNNEL 0x1000 /* IP-in-IP added by tunnel mode IPsec */
#define M_ZEROIZE 0x2000 /* Zeroize data part on free */
#define M_COMP 0x4000 /* header was decompressed */
#define M_LINK0 0x8000 /* link layer specific flag */
#ifdef _KERNEL
#define M_BITS \
("\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_EXTWR\5M_PROTO1\6M_VLANTAG\7M_LOOP" \
"\11M_BCAST\12M_MCAST\13M_CONF\14M_AUTH\15M_TUNNEL" \
"\16M_ZEROIZE\17M_COMP\20M_LINK0")
#endif
/* flags copied when copying m_pkthdr */
#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_PROTO1|M_BCAST|M_MCAST|M_CONF|M_COMP|\
M_AUTH|M_LOOP|M_TUNNEL|M_LINK0|M_VLANTAG|M_ZEROIZE)
/* Checksumming flags */
#define M_IPV4_CSUM_OUT 0x0001 /* IPv4 checksum needed */
#define M_TCP_CSUM_OUT 0x0002 /* TCP checksum needed */
#define M_UDP_CSUM_OUT 0x0004 /* UDP checksum needed */
#define M_IPV4_CSUM_IN_OK 0x0008 /* IPv4 checksum verified */
#define M_IPV4_CSUM_IN_BAD 0x0010 /* IPv4 checksum bad */
#define M_TCP_CSUM_IN_OK 0x0020 /* TCP checksum verified */
#define M_TCP_CSUM_IN_BAD 0x0040 /* TCP checksum bad */
#define M_UDP_CSUM_IN_OK 0x0080 /* UDP checksum verified */
#define M_UDP_CSUM_IN_BAD 0x0100 /* UDP checksum bad */
#define M_ICMP_CSUM_OUT 0x0200 /* ICMP/ICMPv6 checksum needed */
#define M_ICMP_CSUM_IN_OK 0x0400 /* ICMP/ICMPv6 checksum verified */
#define M_ICMP_CSUM_IN_BAD 0x0800 /* ICMP/ICMPv6 checksum bad */
#define M_IPV6_DF_OUT 0x1000 /* don't fragment outgoing IPv6 */
#define M_TIMESTAMP 0x2000 /* ph_timestamp is set */
#define M_FLOWID 0x4000 /* ph_flowid is set */
#ifdef _KERNEL
#define MCS_BITS \
("\20\1IPV4_CSUM_OUT\2TCP_CSUM_OUT\3UDP_CSUM_OUT\4IPV4_CSUM_IN_OK" \
"\5IPV4_CSUM_IN_BAD\6TCP_CSUM_IN_OK\7TCP_CSUM_IN_BAD\10UDP_CSUM_IN_OK" \
"\11UDP_CSUM_IN_BAD\12ICMP_CSUM_OUT\13ICMP_CSUM_IN_OK\14ICMP_CSUM_IN_BAD" \
"\15IPV6_NODF_OUT" "\16TIMESTAMP" "\17FLOWID")
#endif
/* mbuf types */
#define MT_FREE 0 /* should be on free list */
#define MT_DATA 1 /* dynamic (data) allocation */
#define MT_HEADER 2 /* packet header */
#define MT_SONAME 3 /* socket name */
#define MT_SOOPTS 4 /* socket options */
#define MT_FTABLE 5 /* fragment reassembly header */
#define MT_CONTROL 6 /* extra-data protocol message */
#define MT_OOBDATA 7 /* expedited data */
#define MT_NTYPES 8
/* flags to m_get/MGET */
#include <sys/malloc.h>
#define M_DONTWAIT M_NOWAIT
#define M_WAIT M_WAITOK
/*
* mbuf allocation/deallocation macros:
*
* MGET(struct mbuf *m, int how, int type)
* allocates an mbuf and initializes it to contain internal data.
*
* MGETHDR(struct mbuf *m, int how, int type)
* allocates an mbuf and initializes it to contain a packet header
* and internal data.
*/
#define MGET(m, how, type) m = m_get((how), (type))
#define MGETHDR(m, how, type) m = m_gethdr((how), (type))
/*
* Macros for tracking external storage associated with an mbuf.
*/
#ifdef DEBUG
#define MCLREFDEBUGN(m, file, line) do { \
(m)->m_ext.ext_nfile = (file); \
(m)->m_ext.ext_nline = (line); \
} while (/* CONSTCOND */ 0)
#define MCLREFDEBUGO(m, file, line) do { \
(m)->m_ext.ext_ofile = (file); \
(m)->m_ext.ext_oline = (line); \
} while (/* CONSTCOND */ 0)
#else
#define MCLREFDEBUGN(m, file, line)
#define MCLREFDEBUGO(m, file, line)
#endif
#define MCLISREFERENCED(m) ((m)->m_ext.ext_nextref != (m))
#define MCLADDREFERENCE(o, n) m_extref((o), (n))
#define MCLINITREFERENCE(m) do { \
(m)->m_ext.ext_prevref = (m); \
(m)->m_ext.ext_nextref = (m); \
MCLREFDEBUGO((m), __FILE__, __LINE__); \
MCLREFDEBUGN((m), NULL, 0); \
} while (/* CONSTCOND */ 0)
/*
* Macros for mbuf external storage.
*
* MEXTADD adds pre-allocated external storage to
* a normal mbuf; the flag M_EXT is set.
*
* MCLGET allocates and adds an mbuf cluster to a normal mbuf;
* the flag M_EXT is set upon success.
*/
#define MEXTADD(m, buf, size, mflags, freefn, arg) do { \
(m)->m_data = (m)->m_ext.ext_buf = (caddr_t)(buf); \
(m)->m_flags |= M_EXT | (mflags & M_EXTWR); \
(m)->m_ext.ext_size = (size); \
(m)->m_ext.ext_free_fn = (freefn); \
(m)->m_ext.ext_arg = (arg); \
MCLINITREFERENCE(m); \
} while (/* CONSTCOND */ 0)
#define MCLGET(m, how) (void) m_clget((m), (how), MCLBYTES)
#define MCLGETL(m, how, l) m_clget((m), (how), (l))
u_int mextfree_register(void (*)(caddr_t, u_int, void *));
#define MEXTFREE_POOL 0
/*
* Move just m_pkthdr from from to to,
* remove M_PKTHDR and clean flags/tags for from.
*/
#define M_MOVE_HDR(to, from) do { \
(to)->m_pkthdr = (from)->m_pkthdr; \
(from)->m_flags &= ~M_PKTHDR; \
SLIST_INIT(&(from)->m_pkthdr.ph_tags); \
(from)->m_pkthdr.pf.statekey = NULL; \
} while (/* CONSTCOND */ 0)
/*
* MOVE mbuf pkthdr from from to to.
* from must have M_PKTHDR set, and to must be empty.
*/
#define M_MOVE_PKTHDR(to, from) do { \
(to)->m_flags = ((to)->m_flags & (M_EXT | M_EXTWR)); \
(to)->m_flags |= (from)->m_flags & M_COPYFLAGS; \
M_MOVE_HDR((to), (from)); \
if (((to)->m_flags & M_EXT) == 0) \
(to)->m_data = (to)->m_pktdat; \
} while (/* CONSTCOND */ 0)
/*
* Determine if an mbuf's data area is read-only. This is true for
* non-cluster external storage and for clusters that are being
* referenced by more than one mbuf.
*/
#define M_READONLY(m) \
(((m)->m_flags & M_EXT) != 0 && \
(((m)->m_flags & M_EXTWR) == 0 || MCLISREFERENCED(m)))
/*
* Arrange to prepend space of size plen to mbuf m.
* If a new mbuf must be allocated, how specifies whether to wait.
* If how is M_DONTWAIT and allocation fails, the original mbuf chain
* is freed and m is set to NULL.
*/
#define M_PREPEND(m, plen, how) \
(m) = m_prepend((m), (plen), (how))
/* length to m_copy to copy all */
#define M_COPYALL 1000000000
/*
* Mbuf statistics.
* For statistics related to mbuf and cluster allocations, see also the
* pool headers (mbpool and mclpool).
*/
struct mbstat {
u_long m_drops; /* times failed to find space */
u_long m_wait; /* times waited for space */
u_long m_drain; /* times drained protocols for space */
u_short m_mtypes[256]; /* type specific mbuf allocations */
};
#define MBSTAT_TYPES MT_NTYPES
#define MBSTAT_DROPS (MBSTAT_TYPES + 0)
#define MBSTAT_WAIT (MBSTAT_TYPES + 1)
#define MBSTAT_DRAIN (MBSTAT_TYPES + 2)
#define MBSTAT_COUNT (MBSTAT_TYPES + 3)
#include <sys/mutex.h>
struct mbuf_list {
struct mbuf *ml_head;
struct mbuf *ml_tail;
u_int ml_len;
};
struct mbuf_queue {
struct mutex mq_mtx;
struct mbuf_list mq_list;
u_int mq_maxlen;
u_int mq_drops;
};
#ifdef _KERNEL
struct pool;
extern long nmbclust; /* limit on the # of clusters */
extern int mblowat; /* mbuf low water mark */
extern int mcllowat; /* mbuf cluster low water mark */
extern int max_linkhdr; /* largest link-level header */
extern int max_protohdr; /* largest protocol header */
extern int max_hdr; /* largest link+protocol header */
void mbinit(void);
void mbcpuinit(void);
int nmbclust_update(long);
struct mbuf *m_copym(struct mbuf *, int, int, int);
struct mbuf *m_free(struct mbuf *);
struct mbuf *m_get(int, int);
struct mbuf *m_getclr(int, int);
struct mbuf *m_gethdr(int, int);
struct mbuf *m_inithdr(struct mbuf *);
void m_removehdr(struct mbuf *);
void m_resethdr(struct mbuf *);
void m_calchdrlen(struct mbuf *);
int m_defrag(struct mbuf *, int);
struct mbuf *m_prepend(struct mbuf *, int, int);
struct mbuf *m_pulldown(struct mbuf *, int, int, int *);
struct mbuf *m_pullup(struct mbuf *, int);
struct mbuf *m_split(struct mbuf *, int, int);
struct mbuf *m_makespace(struct mbuf *, int, int, int *);
struct mbuf *m_getptr(struct mbuf *, int, int *);
int m_leadingspace(struct mbuf *);
int m_trailingspace(struct mbuf *);
void m_align(struct mbuf *, int);
struct mbuf *m_clget(struct mbuf *, int, u_int);
void m_extref(struct mbuf *, struct mbuf *);
void m_pool_init(struct pool *, u_int, u_int, const char *);
u_int m_pool_used(void);
void m_extfree_pool(caddr_t, u_int, void *);
void m_adj(struct mbuf *, int);
int m_copyback(struct mbuf *, int, int, const void *, int);
struct mbuf *m_freem(struct mbuf *);
void m_purge(struct mbuf *);
void m_reclaim(void *, int);
void m_copydata(struct mbuf *, int, int, void *);
void m_cat(struct mbuf *, struct mbuf *);
struct mbuf *m_devget(char *, int, int);
int m_apply(struct mbuf *, int, int,
int (*)(caddr_t, caddr_t, unsigned int), caddr_t);
struct mbuf *m_dup_pkt(struct mbuf *, unsigned int, int);
int m_dup_pkthdr(struct mbuf *, struct mbuf *, int);
void m_microtime(const struct mbuf *, struct timeval *);
static inline struct mbuf *
m_freemp(struct mbuf **mp)
{
struct mbuf *m = *mp;
*mp = NULL;
return m_freem(m);
}
/* Packet tag routines */
struct m_tag *m_tag_get(int, int, int);
void m_tag_prepend(struct mbuf *, struct m_tag *);
void m_tag_delete(struct mbuf *, struct m_tag *);
void m_tag_delete_chain(struct mbuf *);
struct m_tag *m_tag_find(struct mbuf *, int, struct m_tag *);
struct m_tag *m_tag_copy(struct m_tag *, int);
int m_tag_copy_chain(struct mbuf *, struct mbuf *, int);
void m_tag_init(struct mbuf *);
struct m_tag *m_tag_first(struct mbuf *);
struct m_tag *m_tag_next(struct mbuf *, struct m_tag *);
/* Packet tag types */
#define PACKET_TAG_IPSEC_IN_DONE 0x0001 /* IPsec applied, in */
#define PACKET_TAG_IPSEC_OUT_DONE 0x0002 /* IPsec applied, out */
#define PACKET_TAG_IPSEC_FLOWINFO 0x0004 /* IPsec flowinfo */
#define PACKET_TAG_WIREGUARD 0x0040 /* WireGuard data */
#define PACKET_TAG_GRE 0x0080 /* GRE processing done */
#define PACKET_TAG_DLT 0x0100 /* data link layer type */
#define PACKET_TAG_PF_DIVERT 0x0200 /* pf(4) diverted packet */
#define PACKET_TAG_PF_REASSEMBLED 0x0800 /* pf reassembled ipv6 packet */
#define PACKET_TAG_SRCROUTE 0x1000 /* IPv4 source routing options */
#define PACKET_TAG_TUNNEL 0x2000 /* Tunnel endpoint address */
#define PACKET_TAG_CARP_BAL_IP 0x4000 /* carp(4) ip balanced marker */
#define PACKET_TAG_IP6_OFFNXT 0x8000 /* IPv6 offset and next proto */
#define MTAG_BITS \
("\20\1IPSEC_IN_DONE\2IPSEC_OUT_DONE\3IPSEC_FLOWINFO" \
"\4IPSEC_OUT_CRYPTO_NEEDED\5IPSEC_PENDING_TDB\6BRIDGE\7WG\10GRE\11DLT" \
"\12PF_DIVERT\14PF_REASSEMBLED\15SRCROUTE\16TUNNEL\17CARP_BAL_IP")
/*
* Maximum tag payload length (that is excluding the m_tag structure).
* Please make sure to update this value when increasing the payload
* length for an existing packet tag type or when adding a new one that
* has payload larger than the value below.
*/
#define PACKET_TAG_MAXSIZE 80
/* Detect mbufs looping in the kernel when spliced too often. */
#define M_MAXLOOP 128
/*
* mbuf lists
*/
#define MBUF_LIST_INITIALIZER() { NULL, NULL, 0 }
void ml_init(struct mbuf_list *);
void ml_enqueue(struct mbuf_list *, struct mbuf *);
struct mbuf * ml_dequeue(struct mbuf_list *);
void ml_enlist(struct mbuf_list *, struct mbuf_list *);
struct mbuf * ml_dechain(struct mbuf_list *);
unsigned int ml_purge(struct mbuf_list *);
unsigned int ml_hdatalen(struct mbuf_list *);
#define ml_len(_ml) ((_ml)->ml_len)
#define ml_empty(_ml) ((_ml)->ml_len == 0)
#define MBUF_LIST_FIRST(_ml) ((_ml)->ml_head)
#define MBUF_LIST_NEXT(_m) ((_m)->m_nextpkt)
#define MBUF_LIST_FOREACH(_ml, _m) \
for ((_m) = MBUF_LIST_FIRST(_ml); \
(_m) != NULL; \
(_m) = MBUF_LIST_NEXT(_m))
/*
* mbuf queues
*/
#define MBUF_QUEUE_INITIALIZER(_maxlen, _ipl) \
{ MUTEX_INITIALIZER(_ipl), MBUF_LIST_INITIALIZER(), (_maxlen), 0 }
void mq_init(struct mbuf_queue *, u_int, int);
int mq_push(struct mbuf_queue *, struct mbuf *);
int mq_enqueue(struct mbuf_queue *, struct mbuf *);
struct mbuf * mq_dequeue(struct mbuf_queue *);
int mq_enlist(struct mbuf_queue *, struct mbuf_list *);
void mq_delist(struct mbuf_queue *, struct mbuf_list *);
struct mbuf * mq_dechain(struct mbuf_queue *);
unsigned int mq_purge(struct mbuf_queue *);
unsigned int mq_hdatalen(struct mbuf_queue *);
#define mq_len(_mq) ml_len(&(_mq)->mq_list)
#define mq_empty(_mq) ml_empty(&(_mq)->mq_list)
#define mq_full(_mq) (mq_len((_mq)) >= (_mq)->mq_maxlen)
#define mq_drops(_mq) ((_mq)->mq_drops)
#define mq_set_maxlen(_mq, _l) ((_mq)->mq_maxlen = (_l))
#endif /* _KERNEL */
#endif /* _SYS_MBUF_H_ */
34
32
34
23
11
12
30
24
2
2
22
3
25
25
25
34
9
7
19
25
25
17
7
10
5
1
1
1
1
1
7
2
2
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
/* $OpenBSD: icmp6.c,v 1.242 2022/05/05 13:57:40 claudio Exp $ */
/* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
*/
#include "carp.h"
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/mld6_var.h>
#include <netinet/in_pcb.h>
#include <netinet6/nd6.h>
#include <netinet6/ip6protosw.h>
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
struct cpumem *icmp6counters;
extern int icmp6errppslim;
static int icmp6errpps_count = 0;
static struct timeval icmp6errppslim_last;
/*
* List of callbacks to notify when Path MTU changes are made.
*/
struct icmp6_mtudisc_callback {
LIST_ENTRY(icmp6_mtudisc_callback) mc_list;
void (*mc_func)(struct sockaddr_in6 *, u_int);
};
LIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks =
LIST_HEAD_INITIALIZER(icmp6_mtudisc_callbacks);
struct rttimer_queue icmp6_mtudisc_timeout_q;
/* XXX do these values make any sense? */
static int icmp6_mtudisc_hiwat = 1280;
static int icmp6_mtudisc_lowat = 256;
/*
* keep track of # of redirect routes.
*/
struct rttimer_queue icmp6_redirect_timeout_q;
/* XXX experimental, turned off */
static int icmp6_redirect_lowat = -1;
void icmp6_errcount(int, int);
int icmp6_ratelimit(const struct in6_addr *, const int, const int);
const char *icmp6_redirect_diag(struct in6_addr *, struct in6_addr *,
struct in6_addr *);
int icmp6_notify_error(struct mbuf *, int, int, int);
void icmp6_mtudisc_timeout(struct rtentry *, u_int);
void
icmp6_init(void)
{
mld6_init();
rt_timer_queue_init(&icmp6_mtudisc_timeout_q, ip6_mtudisc_timeout,
&icmp6_mtudisc_timeout);
rt_timer_queue_init(&icmp6_redirect_timeout_q, icmp6_redirtimeout,
NULL);
icmp6counters = counters_alloc(icp6s_ncounters);
}
void
icmp6_errcount(int type, int code)
{
enum icmp6stat_counters c = icp6s_ounknown;
switch (type) {
case ICMP6_DST_UNREACH:
switch (code) {
case ICMP6_DST_UNREACH_NOROUTE:
c = icp6s_odst_unreach_noroute;
break;
case ICMP6_DST_UNREACH_ADMIN:
c = icp6s_odst_unreach_admin;
break;
case ICMP6_DST_UNREACH_BEYONDSCOPE:
c = icp6s_odst_unreach_beyondscope;
break;
case ICMP6_DST_UNREACH_ADDR:
c = icp6s_odst_unreach_addr;
break;
case ICMP6_DST_UNREACH_NOPORT:
c = icp6s_odst_unreach_noport;
break;
}
break;
case ICMP6_PACKET_TOO_BIG:
c = icp6s_opacket_too_big;
break;
case ICMP6_TIME_EXCEEDED:
switch (code) {
case ICMP6_TIME_EXCEED_TRANSIT:
c = icp6s_otime_exceed_transit;
break;
case ICMP6_TIME_EXCEED_REASSEMBLY:
c = icp6s_otime_exceed_reassembly;
break;
}
break;
case ICMP6_PARAM_PROB:
switch (code) {
case ICMP6_PARAMPROB_HEADER:
c = icp6s_oparamprob_header;
break;
case ICMP6_PARAMPROB_NEXTHEADER:
c = icp6s_oparamprob_nextheader;
break;
case ICMP6_PARAMPROB_OPTION:
c = icp6s_oparamprob_option;
break;
}
break;
case ND_REDIRECT:
c = icp6s_oredirect;
break;
}
icmp6stat_inc(c);
}
/*
* Register a Path MTU Discovery callback.
*/
void
icmp6_mtudisc_callback_register(void (*func)(struct sockaddr_in6 *, u_int))
{
struct icmp6_mtudisc_callback *mc;
LIST_FOREACH(mc, &icmp6_mtudisc_callbacks, mc_list) {
if (mc->mc_func == func)
return;
}
mc = malloc(sizeof(*mc), M_PCB, M_NOWAIT);
if (mc == NULL)
panic("%s", __func__);
mc->mc_func = func;
LIST_INSERT_HEAD(&icmp6_mtudisc_callbacks, mc, mc_list);
}
struct mbuf *
icmp6_do_error(struct mbuf *m, int type, int code, int param)
{
struct ip6_hdr *oip6, *nip6;
struct icmp6_hdr *icmp6;
u_int preplen;
int off;
int nxt;
icmp6stat_inc(icp6s_error);
/* count per-type-code statistics */
icmp6_errcount(type, code);
if (m->m_len < sizeof(struct ip6_hdr)) {
m = m_pullup(m, sizeof(struct ip6_hdr));
if (m == NULL)
return (NULL);
}
oip6 = mtod(m, struct ip6_hdr *);
/*
* If the destination address of the erroneous packet is a multicast
* address, or the packet was sent using link-layer multicast,
* we should basically suppress sending an error (RFC 2463, Section
* 2.4).
* We have two exceptions (the item e.2 in that section):
* - the Packet Too Big message can be sent for path MTU discovery.
* - the Parameter Problem Message that can be allowed an icmp6 error
* in the option type field. This check has been done in
* ip6_unknown_opt(), so we can just check the type and code.
*/
if ((m->m_flags & (M_BCAST|M_MCAST) ||
IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
(type != ICMP6_PACKET_TOO_BIG &&
(type != ICMP6_PARAM_PROB ||
code != ICMP6_PARAMPROB_OPTION)))
goto freeit;
/*
* RFC 2463, 2.4 (e.5): source address check.
* XXX: the case of anycast source?
*/
if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
goto freeit;
/*
* If we are about to send ICMPv6 against ICMPv6 error/redirect,
* don't do it.
*/
nxt = -1;
off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
if (off >= 0 && nxt == IPPROTO_ICMPV6) {
struct icmp6_hdr *icp;
IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
sizeof(*icp));
if (icp == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (NULL);
}
if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
icp->icmp6_type == ND_REDIRECT) {
/*
* ICMPv6 error
* Special case: for redirect (which is
* informational) we must not send icmp6 error.
*/
icmp6stat_inc(icp6s_canterror);
goto freeit;
} else {
/* ICMPv6 informational - send the error */
}
}
else {
/* non-ICMPv6 - send the error */
}
oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
/* Finally, do rate limitation check. */
if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
icmp6stat_inc(icp6s_toofreq);
goto freeit;
}
/*
* OK, ICMP6 can be generated.
*/
if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
M_PREPEND(m, preplen, M_DONTWAIT);
if (m && m->m_len < preplen)
m = m_pullup(m, preplen);
if (m == NULL) {
nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
return (NULL);
}
nip6 = mtod(m, struct ip6_hdr *);
nip6->ip6_src = oip6->ip6_src;
nip6->ip6_dst = oip6->ip6_dst;
if (IN6_IS_SCOPE_EMBED(&oip6->ip6_src))
oip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_SCOPE_EMBED(&oip6->ip6_dst))
oip6->ip6_dst.s6_addr16[1] = 0;
icmp6 = (struct icmp6_hdr *)(nip6 + 1);
icmp6->icmp6_type = type;
icmp6->icmp6_code = code;
icmp6->icmp6_pptr = htonl((u_int32_t)param);
/*
* icmp6_reflect() is designed to be in the input path.
* icmp6_error() can be called from both input and output path,
* and if we are in output path rcvif could contain bogus value.
* clear m->m_pkthdr.ph_ifidx for safety, we should have enough
* scope information in ip header (nip6).
*/
m->m_pkthdr.ph_ifidx = 0;
icmp6stat_inc(icp6s_outhist + type);
return (m);
freeit:
/*
* If we can't tell whether or not we can generate ICMP6, free it.
*/
return (m_freem(m));
}
/*
* Generate an error packet of type error in response to bad IP6 packet.
*/
void
icmp6_error(struct mbuf *m, int type, int code, int param)
{
struct mbuf *n;
n = icmp6_do_error(m, type, code, param);
if (n != NULL) {
/* header order: IPv6 - ICMPv6 */
if (!icmp6_reflect(&n, sizeof(struct ip6_hdr), NULL))
ip6_send(n);
}
}
/*
* Process a received ICMP6 message.
*/
int
icmp6_input(struct mbuf **mp, int *offp, int proto, int af)
{
#if NCARP > 0
struct ifnet *ifp;
#endif
struct mbuf *m = *mp, *n;
struct ip6_hdr *ip6, *nip6;
struct icmp6_hdr *icmp6, *nicmp6;
int off = *offp;
int icmp6len = m->m_pkthdr.len - *offp;
int code, sum, noff;
char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN];
/*
* Locate icmp6 structure in mbuf, and check
* that not corrupted and of at least minimum length
*/
ip6 = mtod(m, struct ip6_hdr *);
if (icmp6len < sizeof(struct icmp6_hdr)) {
icmp6stat_inc(icp6s_tooshort);
goto freeit;
}
/*
* calculate the checksum
*/
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
if (icmp6 == NULL) {
icmp6stat_inc(icp6s_tooshort);
return IPPROTO_DONE;
}
code = icmp6->icmp6_code;
if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
nd6log((LOG_ERR,
"ICMP6 checksum error(%d|%x) %s\n",
icmp6->icmp6_type, sum,
inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src))));
icmp6stat_inc(icp6s_checksum);
goto freeit;
}
#if NPF > 0
if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
switch (icmp6->icmp6_type) {
/*
* These ICMP6 types map to other connections. They must be
* delivered to pr_ctlinput() also for diverted connections.
*/
case ICMP6_DST_UNREACH:
case ICMP6_PACKET_TOO_BIG:
case ICMP6_TIME_EXCEEDED:
case ICMP6_PARAM_PROB:
/*
* Do not use the divert-to property of the TCP or UDP
* rule when doing the PCB lookup for the raw socket.
*/
m->m_pkthdr.pf.flags &=~ PF_TAG_DIVERTED;
break;
default:
goto raw;
}
}
#endif /* NPF */
#if NCARP > 0
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL)
goto freeit;
if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST &&
carp_lsdrop(ifp, m, AF_INET6, ip6->ip6_src.s6_addr32,
ip6->ip6_dst.s6_addr32, 1)) {
if_put(ifp);
goto freeit;
}
if_put(ifp);
#endif
icmp6stat_inc(icp6s_inhist + icmp6->icmp6_type);
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
switch (code) {
case ICMP6_DST_UNREACH_NOROUTE:
code = PRC_UNREACH_NET;
break;
case ICMP6_DST_UNREACH_ADMIN:
code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
break;
case ICMP6_DST_UNREACH_ADDR:
code = PRC_HOSTDEAD;
break;
case ICMP6_DST_UNREACH_BEYONDSCOPE:
/* I mean "source address was incorrect." */
code = PRC_PARAMPROB;
break;
case ICMP6_DST_UNREACH_NOPORT:
code = PRC_UNREACH_PORT;
break;
default:
goto badcode;
}
goto deliver;
case ICMP6_PACKET_TOO_BIG:
/* MTU is checked in icmp6_mtudisc_update. */
code = PRC_MSGSIZE;
/*
* Updating the path MTU will be done after examining
* intermediate extension headers.
*/
goto deliver;
case ICMP6_TIME_EXCEEDED:
switch (code) {
case ICMP6_TIME_EXCEED_TRANSIT:
code = PRC_TIMXCEED_INTRANS;
break;
case ICMP6_TIME_EXCEED_REASSEMBLY:
code = PRC_TIMXCEED_REASS;
break;
default:
goto badcode;
}
goto deliver;
case ICMP6_PARAM_PROB:
switch (code) {
case ICMP6_PARAMPROB_NEXTHEADER:
code = PRC_UNREACH_PROTOCOL;
break;
case ICMP6_PARAMPROB_HEADER:
case ICMP6_PARAMPROB_OPTION:
code = PRC_PARAMPROB;
break;
default:
goto badcode;
}
goto deliver;
case ICMP6_ECHO_REQUEST:
if (code != 0)
goto badcode;
/*
* Copy mbuf to send to two data paths: userland socket(s),
* and to the querier (echo reply).
* m: a copy for socket, n: a copy for querier
*/
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* Give up local */
n = m;
m = *mp = NULL;
goto deliverecho;
}
/*
* If the first mbuf is shared, or the first mbuf is too short,
* copy the first part of the data into a fresh mbuf.
* Otherwise, we will wrongly overwrite both copies.
*/
if ((n->m_flags & M_EXT) != 0 ||
n->m_len < off + sizeof(struct icmp6_hdr)) {
struct mbuf *n0 = n;
const int maxlen = sizeof(*nip6) + sizeof(*nicmp6);
/*
* Prepare an internal mbuf. m_pullup() doesn't
* always copy the length we specified.
*/
if (maxlen >= MCLBYTES) {
/* Give up remote */
m_freem(n0);
break;
}
MGETHDR(n, M_DONTWAIT, n0->m_type);
if (n && maxlen >= MHLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
/* Give up local */
m_freem(n0);
n = m;
m = *mp = NULL;
goto deliverecho;
}
M_MOVE_PKTHDR(n, n0);
/*
* Copy IPv6 and ICMPv6 only.
*/
nip6 = mtod(n, struct ip6_hdr *);
bcopy(ip6, nip6, sizeof(struct ip6_hdr));
nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
noff = sizeof(struct ip6_hdr);
n->m_len = noff + sizeof(struct icmp6_hdr);
/*
* Adjust mbuf. ip6_plen will be adjusted in
* ip6_output().
* n->m_pkthdr.len == n0->m_pkthdr.len at this point.
*/
n->m_pkthdr.len += noff + sizeof(struct icmp6_hdr);
n->m_pkthdr.len -= (off + sizeof(struct icmp6_hdr));
m_adj(n0, off + sizeof(struct icmp6_hdr));
n->m_next = n0;
} else {
deliverecho:
IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off,
sizeof(*nicmp6));
noff = off;
}
if (n) {
nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
nicmp6->icmp6_code = 0;
icmp6stat_inc(icp6s_reflect);
icmp6stat_inc(icp6s_outhist + ICMP6_ECHO_REPLY);
if (!icmp6_reflect(&n, noff, NULL))
ip6_send(n);
}
if (!m)
goto freeit;
break;
case ICMP6_ECHO_REPLY:
if (code != 0)
goto badcode;
break;
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
if (icmp6len < sizeof(struct mld_hdr))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
mld6_input(m, off);
m = NULL;
goto freeit;
}
mld6_input(n, off);
/* m stays. */
break;
case MLD_LISTENER_DONE:
if (icmp6len < sizeof(struct mld_hdr)) /* necessary? */
goto badlen;
break; /* nothing to be done in kernel */
case MLD_MTRACE_RESP:
case MLD_MTRACE:
/* XXX: these two are experimental. not officially defined. */
/* XXX: per-interface statistics? */
break; /* just pass it to applications */
case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
/* IPv6 Node Information Queries are not supported */
break;
case ICMP6_WRUREPLY:
break;
case ND_ROUTER_SOLICIT:
case ND_ROUTER_ADVERT:
if (code != 0)
goto badcode;
if ((icmp6->icmp6_type == ND_ROUTER_SOLICIT && icmp6len <
sizeof(struct nd_router_solicit)) ||
(icmp6->icmp6_type == ND_ROUTER_ADVERT && icmp6len <
sizeof(struct nd_router_advert)))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_rtr_cache(m, off, icmp6len,
icmp6->icmp6_type);
m = NULL;
goto freeit;
}
nd6_rtr_cache(n, off, icmp6len, icmp6->icmp6_type);
/* m stays. */
break;
case ND_NEIGHBOR_SOLICIT:
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_neighbor_solicit))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_ns_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_ns_input(n, off, icmp6len);
/* m stays. */
break;
case ND_NEIGHBOR_ADVERT:
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_neighbor_advert))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
nd6_na_input(m, off, icmp6len);
m = NULL;
goto freeit;
}
nd6_na_input(n, off, icmp6len);
/* m stays. */
break;
case ND_REDIRECT:
if (code != 0)
goto badcode;
if (icmp6len < sizeof(struct nd_redirect))
goto badlen;
if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
/* give up local */
icmp6_redirect_input(m, off);
m = NULL;
goto freeit;
}
icmp6_redirect_input(n, off);
/* m stays. */
break;
case ICMP6_ROUTER_RENUMBERING:
if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
code != ICMP6_ROUTER_RENUMBERING_RESULT)
goto badcode;
if (icmp6len < sizeof(struct icmp6_router_renum))
goto badlen;
break;
default:
nd6log((LOG_DEBUG,
"icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%u)\n",
icmp6->icmp6_type,
inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)),
inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst)),
m->m_pkthdr.ph_ifidx));
if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
/* ICMPv6 error: MUST deliver it by spec... */
code = PRC_NCMDS;
/* deliver */
} else {
/* ICMPv6 informational: MUST not deliver */
break;
}
deliver:
if (icmp6_notify_error(m, off, icmp6len, code)) {
/* In this case, m should've been freed. */
return (IPPROTO_DONE);
}
break;
badcode:
icmp6stat_inc(icp6s_badcode);
break;
badlen:
icmp6stat_inc(icp6s_badlen);
break;
}
#if NPF > 0
raw:
#endif
/* deliver the packet to appropriate sockets */
return rip6_input(mp, offp, proto, af);
freeit:
m_freem(m);
return IPPROTO_DONE;
}
int
icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code)
{
struct icmp6_hdr *icmp6;
struct ip6_hdr *eip6;
u_int32_t notifymtu;
struct sockaddr_in6 icmp6src, icmp6dst;
if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
icmp6stat_inc(icp6s_tooshort);
goto freeit;
}
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (icmp6 == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (-1);
}
eip6 = (struct ip6_hdr *)(icmp6 + 1);
/* Detect the upper level protocol */
{
void (*ctlfunc)(int, struct sockaddr *, u_int, void *);
u_int8_t nxt = eip6->ip6_nxt;
int eoff = off + sizeof(struct icmp6_hdr) +
sizeof(struct ip6_hdr);
struct ip6ctlparam ip6cp;
struct in6_addr *finaldst = NULL;
int icmp6type = icmp6->icmp6_type;
struct ip6_frag *fh;
struct ip6_rthdr *rth;
struct ip6_rthdr0 *rth0;
int rthlen;
while (1) { /* XXX: should avoid infinite loop explicitly? */
struct ip6_ext *eh;
switch (nxt) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
case IPPROTO_AH:
IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
eoff, sizeof(*eh));
if (eh == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (-1);
}
if (nxt == IPPROTO_AH)
eoff += (eh->ip6e_len + 2) << 2;
else
eoff += (eh->ip6e_len + 1) << 3;
nxt = eh->ip6e_nxt;
break;
case IPPROTO_ROUTING:
/*
* When the erroneous packet contains a
* routing header, we should examine the
* header to determine the final destination.
* Otherwise, we can't properly update
* information that depends on the final
* destination (e.g. path MTU).
*/
IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
eoff, sizeof(*rth));
if (rth == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (-1);
}
rthlen = (rth->ip6r_len + 1) << 3;
/*
* XXX: currently there is no
* officially defined type other
* than type-0.
* Note that if the segment left field
* is 0, all intermediate hops must
* have been passed.
*/
if (rth->ip6r_segleft &&
rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
int hops;
IP6_EXTHDR_GET(rth0,
struct ip6_rthdr0 *, m,
eoff, rthlen);
if (rth0 == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (-1);
}
/* just ignore a bogus header */
if ((rth0->ip6r0_len % 2) == 0 &&
(hops = rth0->ip6r0_len/2))
finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
}
eoff += rthlen;
nxt = rth->ip6r_nxt;
break;
case IPPROTO_FRAGMENT:
IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
eoff, sizeof(*fh));
if (fh == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (-1);
}
/*
* Data after a fragment header is meaningless
* unless it is the first fragment, but
* we'll go to the notify label for path MTU
* discovery.
*/
if (fh->ip6f_offlg & IP6F_OFF_MASK)
goto notify;
eoff += sizeof(struct ip6_frag);
nxt = fh->ip6f_nxt;
break;
default:
/*
* This case includes ESP and the No Next
* Header. In such cases going to the notify
* label does not have any meaning
* (i.e. ctlfunc will be NULL), but we go
* anyway since we might have to update
* path MTU information.
*/
goto notify;
}
}
notify:
IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
sizeof(*icmp6) + sizeof(struct ip6_hdr));
if (icmp6 == NULL) {
icmp6stat_inc(icp6s_tooshort);
return (-1);
}
eip6 = (struct ip6_hdr *)(icmp6 + 1);
bzero(&icmp6dst, sizeof(icmp6dst));
icmp6dst.sin6_len = sizeof(struct sockaddr_in6);
icmp6dst.sin6_family = AF_INET6;
if (finaldst == NULL)
icmp6dst.sin6_addr = eip6->ip6_dst;
else
icmp6dst.sin6_addr = *finaldst;
icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
&icmp6dst.sin6_addr);
if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst, NULL)) {
/* should be impossible */
nd6log((LOG_DEBUG,
"icmp6_notify_error: in6_embedscope failed\n"));
goto freeit;
}
/*
* retrieve parameters from the inner IPv6 header, and convert
* them into sockaddr structures.
*/
bzero(&icmp6src, sizeof(icmp6src));
icmp6src.sin6_len = sizeof(struct sockaddr_in6);
icmp6src.sin6_family = AF_INET6;
icmp6src.sin6_addr = eip6->ip6_src;
icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
&icmp6src.sin6_addr);
if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src, NULL)) {
/* should be impossible */
nd6log((LOG_DEBUG,
"icmp6_notify_error: in6_embedscope failed\n"));
goto freeit;
}
icmp6src.sin6_flowinfo =
(eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
if (finaldst == NULL)
finaldst = &eip6->ip6_dst;
ip6cp.ip6c_m = m;
ip6cp.ip6c_icmp6 = icmp6;
ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
ip6cp.ip6c_off = eoff;
ip6cp.ip6c_finaldst = finaldst;
ip6cp.ip6c_src = &icmp6src;
ip6cp.ip6c_nxt = nxt;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
if (icmp6type == ICMP6_PACKET_TOO_BIG) {
notifymtu = ntohl(icmp6->icmp6_mtu);
ip6cp.ip6c_cmdarg = (void *)¬ifymtu;
}
ctlfunc = inet6sw[ip6_protox[nxt]].pr_ctlinput;
if (ctlfunc)
(*ctlfunc)(code, sin6tosa(&icmp6dst),
m->m_pkthdr.ph_rtableid, &ip6cp);
}
return (0);
freeit:
m_freem(m);
return (-1);
}
void
icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated)
{
unsigned long rtcount;
struct icmp6_mtudisc_callback *mc;
struct in6_addr *dst = ip6cp->ip6c_finaldst;
struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */
u_int mtu = ntohl(icmp6->icmp6_mtu);
struct rtentry *rt = NULL;
struct sockaddr_in6 sin6;
if (mtu < IPV6_MMTU)
return;
/*
* allow non-validated cases if memory is plenty, to make traffic
* from non-connected pcb happy.
*/
rtcount = rt_timer_queue_count(&icmp6_mtudisc_timeout_q);
if (validated) {
if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat)
return;
else if (0 <= icmp6_mtudisc_lowat &&
rtcount > icmp6_mtudisc_lowat) {
/*
* XXX nuke a victim, install the new one.
*/
}
} else {
if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat)
return;
}
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = PF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = *dst;
/* XXX normally, this won't happen */
if (IN6_IS_ADDR_LINKLOCAL(dst)) {
sin6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.ph_ifidx);
}
sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
&sin6.sin6_addr);
rt = icmp6_mtudisc_clone(&sin6, m->m_pkthdr.ph_rtableid, 0);
if (rt != NULL && ISSET(rt->rt_flags, RTF_HOST) &&
!(rt->rt_locks & RTV_MTU) &&
(rt->rt_mtu > mtu || rt->rt_mtu == 0)) {
struct ifnet *ifp;
ifp = if_get(rt->rt_ifidx);
if (ifp != NULL && mtu < ifp->if_mtu) {
icmp6stat_inc(icp6s_pmtuchg);
rt->rt_mtu = mtu;
}
if_put(ifp);
}
rtfree(rt);
/*
* Notify protocols that the MTU for this destination
* has changed.
*/
LIST_FOREACH(mc, &icmp6_mtudisc_callbacks, mc_list)
(*mc->mc_func)(&sin6, m->m_pkthdr.ph_rtableid);
}
/*
* Reflect the ip6 packet back to the source.
* OFF points to the icmp6 header, counted from the top of the mbuf.
*/
int
icmp6_reflect(struct mbuf **mp, size_t off, struct sockaddr *sa)
{
struct mbuf *m = *mp;
struct rtentry *rt = NULL;
struct ip6_hdr *ip6;
struct icmp6_hdr *icmp6;
struct in6_addr t, *src = NULL;
struct sockaddr_in6 sa6_src, sa6_dst;
u_int rtableid;
u_int8_t pfflags;
CTASSERT(sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) <= MHLEN);
/* too short to reflect */
if (off < sizeof(struct ip6_hdr)) {
nd6log((LOG_DEBUG,
"sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
(u_long)off, (u_long)sizeof(struct ip6_hdr),
__FILE__, __LINE__));
goto bad;
}
if (m->m_pkthdr.ph_loopcnt++ >= M_MAXLOOP) {
m_freemp(mp);
return (ELOOP);
}
rtableid = m->m_pkthdr.ph_rtableid;
pfflags = m->m_pkthdr.pf.flags;
m_resethdr(m);
m->m_pkthdr.ph_rtableid = rtableid;
m->m_pkthdr.pf.flags = pfflags & PF_TAG_GENERATED;
/*
* If there are extra headers between IPv6 and ICMPv6, strip
* off that header first.
*/
if (off > sizeof(struct ip6_hdr)) {
size_t l;
struct ip6_hdr nip6;
l = off - sizeof(struct ip6_hdr);
m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6);
m_adj(m, l);
l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
if (m->m_len < l) {
if ((m = *mp = m_pullup(m, l)) == NULL)
return (EMSGSIZE);
}
memcpy(mtod(m, caddr_t), &nip6, sizeof(nip6));
} else /* off == sizeof(struct ip6_hdr) */ {
size_t l;
l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
if (m->m_len < l) {
if ((m = *mp = m_pullup(m, l)) == NULL)
return (EMSGSIZE);
}
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_nxt = IPPROTO_ICMPV6;
icmp6 = (struct icmp6_hdr *)(ip6 + 1);
t = ip6->ip6_dst;
/*
* ip6_input() drops a packet if its src is multicast.
* So, the src is never multicast.
*/
ip6->ip6_dst = ip6->ip6_src;
/*
* XXX: make sure to embed scope zone information, using
* already embedded IDs or the received interface (if any).
* Note that rcvif may be NULL.
* TODO: scoped routing case (XXX).
*/
bzero(&sa6_src, sizeof(sa6_src));
sa6_src.sin6_family = AF_INET6;
sa6_src.sin6_len = sizeof(sa6_src);
sa6_src.sin6_addr = ip6->ip6_dst;
bzero(&sa6_dst, sizeof(sa6_dst));
sa6_dst.sin6_family = AF_INET6;
sa6_dst.sin6_len = sizeof(sa6_dst);
sa6_dst.sin6_addr = t;
if (sa == NULL) {
/*
* If the incoming packet was addressed directly to us (i.e.
* unicast), use dst as the src for the reply. The
* IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED case would be VERY rare,
* but is possible (for example) when we encounter an error
* while forwarding procedure destined to a duplicated address
* of ours.
*/
rt = rtalloc(sin6tosa(&sa6_dst), 0, rtableid);
if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) &&
!ISSET(ifatoia6(rt->rt_ifa)->ia6_flags,
IN6_IFF_ANYCAST|IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)) {
src = &t;
}
rtfree(rt);
rt = NULL;
sa = sin6tosa(&sa6_src);
}
if (src == NULL) {
struct in6_ifaddr *ia6;
/*
* This case matches to multicasts, our anycast, or unicasts
* that we do not own. Select a source address based on the
* source address of the erroneous packet.
*/
rt = rtalloc(sa, RT_RESOLVE, rtableid);
if (!rtisvalid(rt)) {
char addr[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG,
"%s: source can't be determined: dst=%s\n",
__func__, inet_ntop(AF_INET6, &sa6_src.sin6_addr,
addr, sizeof(addr))));
rtfree(rt);
goto bad;
}
ia6 = in6_ifawithscope(rt->rt_ifa->ifa_ifp, &t, rtableid);
if (ia6 != NULL)
src = &ia6->ia_addr.sin6_addr;
if (src == NULL)
src = &ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr;
}
ip6->ip6_src = *src;
rtfree(rt);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = ip6_defhlim;
icmp6->icmp6_cksum = 0;
m->m_pkthdr.csum_flags = M_ICMP_CSUM_OUT;
/*
* XXX option handling
*/
m->m_flags &= ~(M_BCAST|M_MCAST);
return (0);
bad:
m_freemp(mp);
return (EHOSTUNREACH);
}
void
icmp6_fasttimo(void)
{
mld6_fasttimeo();
}
const char *
icmp6_redirect_diag(struct in6_addr *src6, struct in6_addr *dst6,
struct in6_addr *tgt6)
{
static char buf[1024]; /* XXX */
char src[INET6_ADDRSTRLEN];
char dst[INET6_ADDRSTRLEN];
char tgt[INET6_ADDRSTRLEN];
snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
inet_ntop(AF_INET6, src6, src, sizeof(src)),
inet_ntop(AF_INET6, dst6, dst, sizeof(dst)),
inet_ntop(AF_INET6, tgt6, tgt, sizeof(tgt)));
return buf;
}
void
icmp6_redirect_input(struct mbuf *m, int off)
{
struct ifnet *ifp;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_redirect *nd_rd;
int icmp6len = ntohs(ip6->ip6_plen);
char *lladdr = NULL;
int lladdrlen = 0;
struct rtentry *rt = NULL;
int is_router;
int is_onlink;
struct in6_addr src6 = ip6->ip6_src;
struct in6_addr redtgt6;
struct in6_addr reddst6;
union nd_opts ndopts;
char addr[INET6_ADDRSTRLEN];
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL)
return;
/* XXX if we are router, we don't update route by icmp6 redirect */
if (ip6_forwarding)
goto freeit;
if (!(ifp->if_xflags & IFXF_AUTOCONF6))
goto freeit;
IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
if (nd_rd == NULL) {
icmp6stat_inc(icp6s_tooshort);
if_put(ifp);
return;
}
redtgt6 = nd_rd->nd_rd_target;
reddst6 = nd_rd->nd_rd_dst;
if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
redtgt6.s6_addr16[1] = htons(ifp->if_index);
if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
reddst6.s6_addr16[1] = htons(ifp->if_index);
/* validation */
if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
nd6log((LOG_ERR,
"ICMP6 redirect sent from %s rejected; "
"must be from linklocal\n",
inet_ntop(AF_INET6, &src6, addr, sizeof(addr))));
goto bad;
}
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
"ICMP6 redirect sent from %s rejected; "
"hlim=%d (must be 255)\n",
inet_ntop(AF_INET6, &src6, addr, sizeof(addr)),
ip6->ip6_hlim));
goto bad;
}
if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
nd6log((LOG_ERR,
"ICMP6 redirect rejected; "
"redirect dst must be unicast: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
goto bad;
}
{
/* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
struct sockaddr_in6 sin6;
struct in6_addr *gw6;
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
memcpy(&sin6.sin6_addr, &reddst6, sizeof(reddst6));
rt = rtalloc(sin6tosa(&sin6), 0, m->m_pkthdr.ph_rtableid);
if (rt) {
if (rt->rt_gateway == NULL ||
rt->rt_gateway->sa_family != AF_INET6) {
nd6log((LOG_ERR,
"ICMP6 redirect rejected; no route "
"with inet6 gateway found for redirect dst: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
rtfree(rt);
goto bad;
}
gw6 = &(satosin6(rt->rt_gateway)->sin6_addr);
if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
nd6log((LOG_ERR,
"ICMP6 redirect rejected; "
"not equal to gw-for-src=%s (must be same): "
"%s\n",
inet_ntop(AF_INET6, gw6, addr, sizeof(addr)),
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
rtfree(rt);
goto bad;
}
} else {
nd6log((LOG_ERR,
"ICMP6 redirect rejected; "
"no route found for redirect dst: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
goto bad;
}
rtfree(rt);
rt = NULL;
}
is_router = is_onlink = 0;
if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
is_router = 1; /* router case */
if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
is_onlink = 1; /* on-link destination case */
if (!is_router && !is_onlink) {
nd6log((LOG_ERR,
"ICMP6 redirect rejected; "
"neither router case nor onlink case: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
goto bad;
}
/* validation passed */
icmp6len -= sizeof(*nd_rd);
nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
nd6log((LOG_INFO, "icmp6_redirect_input: "
"invalid ND option, rejected: %s\n",
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
/* nd6_options have incremented stats */
goto freeit;
}
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
nd6log((LOG_INFO,
"icmp6_redirect_input: lladdrlen mismatch for %s "
"(if %d, icmp6 packet %d): %s\n",
inet_ntop(AF_INET6, &redtgt6, addr, sizeof(addr)),
ifp->if_addrlen, lladdrlen - 2,
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
goto bad;
}
/* RFC 2461 8.3 */
nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
if (!is_onlink) { /* better router case. perform rtredirect. */
/* perform rtredirect */
struct sockaddr_in6 sdst;
struct sockaddr_in6 sgw;
struct sockaddr_in6 ssrc;
unsigned long rtcount;
struct rtentry *newrt = NULL;
/*
* do not install redirect route, if the number of entries
* is too much (> hiwat). note that, the node (= host) will
* work just fine even if we do not install redirect route
* (there will be additional hops, though).
*/
rtcount = rt_timer_queue_count(&icmp6_redirect_timeout_q);
if (0 <= ip6_maxdynroutes && rtcount >= ip6_maxdynroutes)
goto freeit;
else if (0 <= icmp6_redirect_lowat &&
rtcount > icmp6_redirect_lowat) {
/*
* XXX nuke a victim, install the new one.
*/
}
bzero(&sdst, sizeof(sdst));
bzero(&sgw, sizeof(sgw));
bzero(&ssrc, sizeof(ssrc));
sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
sizeof(struct sockaddr_in6);
memcpy(&sgw.sin6_addr, &redtgt6, sizeof(struct in6_addr));
memcpy(&sdst.sin6_addr, &reddst6, sizeof(struct in6_addr));
memcpy(&ssrc.sin6_addr, &src6, sizeof(struct in6_addr));
rtredirect(sin6tosa(&sdst), sin6tosa(&sgw), sin6tosa(&ssrc),
&newrt, m->m_pkthdr.ph_rtableid);
if (newrt != NULL && icmp6_redirtimeout > 0) {
rt_timer_add(newrt, &icmp6_redirect_timeout_q,
m->m_pkthdr.ph_rtableid);
}
rtfree(newrt);
}
/* finally update cached route in each socket via pfctlinput */
{
struct sockaddr_in6 sdst;
bzero(&sdst, sizeof(sdst));
sdst.sin6_family = AF_INET6;
sdst.sin6_len = sizeof(struct sockaddr_in6);
memcpy(&sdst.sin6_addr, &reddst6, sizeof(struct in6_addr));
pfctlinput(PRC_REDIRECT_HOST, sin6tosa(&sdst));
}
freeit:
if_put(ifp);
m_freem(m);
return;
bad:
if_put(ifp);
icmp6stat_inc(icp6s_badredirect);
m_freem(m);
}
void
icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
{
struct ifnet *ifp = NULL;
struct in6_addr *ifp_ll6;
struct in6_addr *nexthop;
struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */
struct mbuf *m = NULL; /* newly allocated one */
struct ip6_hdr *ip6; /* m as struct ip6_hdr */
struct nd_redirect *nd_rd;
size_t maxlen;
u_char *p;
struct sockaddr_in6 src_sa;
icmp6_errcount(ND_REDIRECT, 0);
/* if we are not router, we don't send icmp6 redirect */
if (!ip6_forwarding)
goto fail;
/* sanity check */
if (m0 == NULL || !rtisvalid(rt))
goto fail;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
goto fail;
/*
* Address check:
* the source address must identify a neighbor, and
* the destination address must not be a multicast address
* [RFC 2461, sec 8.2]
*/
sip6 = mtod(m0, struct ip6_hdr *);
bzero(&src_sa, sizeof(src_sa));
src_sa.sin6_family = AF_INET6;
src_sa.sin6_len = sizeof(src_sa);
src_sa.sin6_addr = sip6->ip6_src;
/* we don't currently use sin6_scope_id, but eventually use it */
src_sa.sin6_scope_id = in6_addr2scopeid(ifp->if_index, &sip6->ip6_src);
if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
goto fail;
if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
goto fail; /* what should we do here? */
/* rate limit */
if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
goto fail;
/*
* Since we are going to append up to 1280 bytes (= IPV6_MMTU),
* we almost always ask for an mbuf cluster for simplicity.
* (MHLEN < IPV6_MMTU is almost always true)
*/
#if IPV6_MMTU >= MCLBYTES
# error assumption failed about IPV6_MMTU and MCLBYTES
#endif
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m && IPV6_MMTU >= MHLEN)
MCLGET(m, M_DONTWAIT);
if (!m)
goto fail;
m->m_pkthdr.ph_ifidx = 0;
m->m_len = 0;
maxlen = m_trailingspace(m);
maxlen = min(IPV6_MMTU, maxlen);
/* just for safety */
if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
goto fail;
}
{
/* get ip6 linklocal address for ifp(my outgoing interface). */
struct in6_ifaddr *ia6;
if ((ia6 = in6ifa_ifpforlinklocal(ifp, IN6_IFF_TENTATIVE|
IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST)) == NULL)
goto fail;
ifp_ll6 = &ia6->ia_addr.sin6_addr;
}
/* get ip6 linklocal address for the router. */
if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
struct sockaddr_in6 *sin6;
sin6 = satosin6(rt->rt_gateway);
nexthop = &sin6->sin6_addr;
if (!IN6_IS_ADDR_LINKLOCAL(nexthop))
nexthop = NULL;
} else
nexthop = NULL;
/* ip6 */
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
/* ip6->ip6_plen will be set later */
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
/* ip6->ip6_src must be linklocal addr for my outgoing if. */
bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
/* ND Redirect */
nd_rd = (struct nd_redirect *)(ip6 + 1);
nd_rd->nd_rd_type = ND_REDIRECT;
nd_rd->nd_rd_code = 0;
nd_rd->nd_rd_reserved = 0;
if (rt->rt_flags & RTF_GATEWAY) {
/*
* nd_rd->nd_rd_target must be a link-local address in
* better router cases.
*/
if (!nexthop)
goto fail;
bcopy(nexthop, &nd_rd->nd_rd_target,
sizeof(nd_rd->nd_rd_target));
bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
sizeof(nd_rd->nd_rd_dst));
} else {
/* make sure redtgt == reddst */
nexthop = &sip6->ip6_dst;
bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
sizeof(nd_rd->nd_rd_target));
bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
sizeof(nd_rd->nd_rd_dst));
}
p = (u_char *)(nd_rd + 1);
{
/* target lladdr option */
struct rtentry *nrt;
int len;
struct sockaddr_dl *sdl;
struct nd_opt_hdr *nd_opt;
char *lladdr;
len = sizeof(*nd_opt) + ifp->if_addrlen;
len = (len + 7) & ~7; /* round by 8 */
/* safety check */
if (len + (p - (u_char *)ip6) > maxlen)
goto nolladdropt;
nrt = nd6_lookup(nexthop, 0, ifp, ifp->if_rdomain);
if ((nrt != NULL) &&
(nrt->rt_flags & (RTF_GATEWAY|RTF_LLINFO)) == RTF_LLINFO &&
(nrt->rt_gateway->sa_family == AF_LINK) &&
(sdl = satosdl(nrt->rt_gateway)) &&
sdl->sdl_alen) {
nd_opt = (struct nd_opt_hdr *)p;
nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
nd_opt->nd_opt_len = len >> 3;
lladdr = (char *)(nd_opt + 1);
bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
p += len;
}
rtfree(nrt);
}
nolladdropt:;
m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
/* just to be safe */
if (p - (u_char *)ip6 > maxlen)
goto noredhdropt;
{
/* redirected header option */
int len;
struct nd_opt_rd_hdr *nd_opt_rh;
/*
* compute the maximum size for icmp6 redirect header option.
* XXX room for auth header?
*/
len = maxlen - (p - (u_char *)ip6);
len &= ~7;
/*
* Redirected header option spec (RFC2461 4.6.3) talks nothing
* about padding/truncate rule for the original IP packet.
* From the discussion on IPv6imp in Feb 1999,
* the consensus was:
* - "attach as much as possible" is the goal
* - pad if not aligned (original size can be guessed by
* original ip6 header)
* Following code adds the padding if it is simple enough,
* and truncates if not.
*/
if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
/* not enough room, truncate */
m_adj(m0, (len - sizeof(*nd_opt_rh)) -
m0->m_pkthdr.len);
} else {
/*
* enough room, truncate if not aligned.
* we don't pad here for simplicity.
*/
size_t extra;
extra = m0->m_pkthdr.len % 8;
if (extra) {
/* truncate */
m_adj(m0, -extra);
}
len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
}
nd_opt_rh = (struct nd_opt_rd_hdr *)p;
bzero(nd_opt_rh, sizeof(*nd_opt_rh));
nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
nd_opt_rh->nd_opt_rh_len = len >> 3;
p += sizeof(*nd_opt_rh);
m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
/* connect m0 to m */
m->m_pkthdr.len += m0->m_pkthdr.len;
m_cat(m, m0);
m0 = NULL;
}
noredhdropt:
m_freem(m0);
m0 = NULL;
sip6 = mtod(m, struct ip6_hdr *);
if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
sip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst))
sip6->ip6_dst.s6_addr16[1] = 0;
#if 0
if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = 0;
#endif
if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target))
nd_rd->nd_rd_target.s6_addr16[1] = 0;
if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst))
nd_rd->nd_rd_dst.s6_addr16[1] = 0;
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
nd_rd->nd_rd_cksum = 0;
m->m_pkthdr.csum_flags = M_ICMP_CSUM_OUT;
/* send the packet to outside... */
ip6_output(m, NULL, NULL, 0, NULL, NULL);
icmp6stat_inc(icp6s_outhist + ND_REDIRECT);
if_put(ifp);
return;
fail:
if_put(ifp);
m_freem(m);
m_freem(m0);
}
/*
* ICMPv6 socket option processing.
*/
int
icmp6_ctloutput(int op, struct socket *so, int level, int optname,
struct mbuf *m)
{
int error = 0;
struct inpcb *in6p = sotoinpcb(so);
if (level != IPPROTO_ICMPV6)
return EINVAL;
switch (op) {
case PRCO_SETOPT:
switch (optname) {
case ICMP6_FILTER:
{
struct icmp6_filter *p;
if (m == NULL || m->m_len != sizeof(*p)) {
error = EMSGSIZE;
break;
}
p = mtod(m, struct icmp6_filter *);
if (!p || !in6p->inp_icmp6filt) {
error = EINVAL;
break;
}
bcopy(p, in6p->inp_icmp6filt,
sizeof(struct icmp6_filter));
error = 0;
break;
}
default:
error = ENOPROTOOPT;
break;
}
break;
case PRCO_GETOPT:
switch (optname) {
case ICMP6_FILTER:
{
struct icmp6_filter *p;
if (!in6p->inp_icmp6filt) {
error = EINVAL;
break;
}
m->m_len = sizeof(struct icmp6_filter);
p = mtod(m, struct icmp6_filter *);
bcopy(in6p->inp_icmp6filt, p,
sizeof(struct icmp6_filter));
error = 0;
break;
}
default:
error = ENOPROTOOPT;
break;
}
break;
}
return (error);
}
/*
* Perform rate limit check.
* Returns 0 if it is okay to send the icmp6 packet.
* Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
* limitation.
*
* XXX per-destination/type check necessary?
*
* dst - not used at this moment
* type - not used at this moment
* code - not used at this moment
*/
int
icmp6_ratelimit(const struct in6_addr *dst, const int type, const int code)
{
/* PPS limit */
if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
icmp6errppslim))
return 1; /* The packet is subject to rate limit */
return 0; /* okay to send */
}
struct rtentry *
icmp6_mtudisc_clone(struct sockaddr_in6 *dst, u_int rtableid, int ipsec)
{
struct rtentry *rt;
int error;
rt = rtalloc(sin6tosa(dst), RT_RESOLVE, rtableid);
/* Check if the route is actually usable */
if (!rtisvalid(rt))
goto bad;
/* IPsec needs the route only for PMTU, it can use reject for that */
if (!ipsec && (rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)))
goto bad;
/*
* No PMTU for local routes and permanent neighbors,
* ARP and NDP use the same expire timer as the route.
*/
if (ISSET(rt->rt_flags, RTF_LOCAL) ||
(ISSET(rt->rt_flags, RTF_LLINFO) && rt->rt_expire == 0))
goto bad;
/* If we didn't get a host route, allocate one */
if ((rt->rt_flags & RTF_HOST) == 0) {
struct rtentry *nrt;
struct rt_addrinfo info;
struct sockaddr_rtlabel sa_rl;
memset(&info, 0, sizeof(info));
info.rti_ifa = rt->rt_ifa;
info.rti_flags = RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC;
info.rti_info[RTAX_DST] = sin6tosa(dst);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_LABEL] =
rtlabel_id2sa(rt->rt_labelid, &sa_rl);
error = rtrequest(RTM_ADD, &info, rt->rt_priority, &nrt,
rtableid);
if (error)
goto bad;
nrt->rt_rmx = rt->rt_rmx;
rtfree(rt);
rt = nrt;
rtm_send(rt, RTM_ADD, 0, rtableid);
}
error = rt_timer_add(rt, &icmp6_mtudisc_timeout_q, rtableid);
if (error)
goto bad;
return (rt);
bad:
rtfree(rt);
return (NULL);
}
void
icmp6_mtudisc_timeout(struct rtentry *rt, u_int rtableid)
{
struct ifnet *ifp;
NET_ASSERT_LOCKED();
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return;
if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
rtdeletemsg(rt, ifp, rtableid);
} else {
if (!(rt->rt_locks & RTV_MTU))
rt->rt_mtu = 0;
}
if_put(ifp);
}
const struct sysctl_bounded_args icmpv6ctl_vars[] = {
{ ICMPV6CTL_ND6_DELAY, &nd6_delay, 0, INT_MAX },
{ ICMPV6CTL_ND6_UMAXTRIES, &nd6_umaxtries, 0, INT_MAX },
{ ICMPV6CTL_ND6_MMAXTRIES, &nd6_mmaxtries, 0, INT_MAX },
{ ICMPV6CTL_ERRPPSLIMIT, &icmp6errppslim, -1, 1000 },
{ ICMPV6CTL_ND6_MAXNUDHINT, &nd6_maxnudhint, 0, INT_MAX },
{ ICMPV6CTL_MTUDISC_HIWAT, &icmp6_mtudisc_hiwat, -1, INT_MAX },
{ ICMPV6CTL_MTUDISC_LOWAT, &icmp6_mtudisc_lowat, -1, INT_MAX },
{ ICMPV6CTL_ND6_DEBUG, &nd6_debug, 0, 1 },
};
int
icmp6_sysctl_icmp6stat(void *oldp, size_t *oldlenp, void *newp)
{
struct icmp6stat *icmp6stat;
int ret;
CTASSERT(sizeof(*icmp6stat) == icp6s_ncounters * sizeof(uint64_t));
icmp6stat = malloc(sizeof(*icmp6stat), M_TEMP, M_WAITOK|M_ZERO);
counters_read(icmp6counters, (uint64_t *)icmp6stat, icp6s_ncounters);
ret = sysctl_rdstruct(oldp, oldlenp, newp,
icmp6stat, sizeof(*icmp6stat));
free(icmp6stat, M_TEMP, sizeof(*icmp6stat));
return (ret);
}
int
icmp6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case ICMPV6CTL_REDIRTIMEOUT:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&icmp6_redirtimeout, 0, INT_MAX);
rt_timer_queue_change(&icmp6_redirect_timeout_q,
icmp6_redirtimeout);
NET_UNLOCK();
break;
case ICMPV6CTL_STATS:
error = icmp6_sysctl_icmp6stat(oldp, oldlenp, newp);
break;
default:
NET_LOCK();
error = sysctl_bounded_arr(icmpv6ctl_vars,
nitems(icmpv6ctl_vars), name, namelen, oldp, oldlenp, newp,
newlen);
NET_UNLOCK();
break;
}
return (error);
}
41
46
53
1
3
48
1
48
1
31
20
27
1
14
3
1
2
40
20
29
17
25
34
3
5
41
30
9
6
27
2
34
35
35
15
12
22
20
14
16
16
16
5
2
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
/* $OpenBSD: if_pfsync.c,v 1.305 2022/04/21 15:22:49 sashan Exp $ */
/*
* Copyright (c) 2002 Michael Shalayeff
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2009 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/timeout.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#include <netinet/ip_ipsp.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_fsm.h>
#include <netinet/udp.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#endif /* INET6 */
#include "carp.h"
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
#define PF_DEBUGNAME "pfsync: "
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#include <net/if_pfsync.h>
#include "bpfilter.h"
#include "pfsync.h"
#define PFSYNC_DEFER_NSEC 20000000ULL
#define PFSYNC_MINPKT ( \
sizeof(struct ip) + \
sizeof(struct pfsync_header))
int pfsync_upd_tcp(struct pf_state *, struct pfsync_state_peer *,
struct pfsync_state_peer *);
int pfsync_in_clr(caddr_t, int, int, int);
int pfsync_in_iack(caddr_t, int, int, int);
int pfsync_in_upd_c(caddr_t, int, int, int);
int pfsync_in_ureq(caddr_t, int, int, int);
int pfsync_in_del(caddr_t, int, int, int);
int pfsync_in_del_c(caddr_t, int, int, int);
int pfsync_in_bus(caddr_t, int, int, int);
int pfsync_in_tdb(caddr_t, int, int, int);
int pfsync_in_ins(caddr_t, int, int, int);
int pfsync_in_upd(caddr_t, int, int, int);
int pfsync_in_eof(caddr_t, int, int, int);
int pfsync_in_error(caddr_t, int, int, int);
void pfsync_update_state_locked(struct pf_state *);
struct {
int (*in)(caddr_t, int, int, int);
size_t len;
} pfsync_acts[] = {
/* PFSYNC_ACT_CLR */
{ pfsync_in_clr, sizeof(struct pfsync_clr) },
/* PFSYNC_ACT_OINS */
{ pfsync_in_error, 0 },
/* PFSYNC_ACT_INS_ACK */
{ pfsync_in_iack, sizeof(struct pfsync_ins_ack) },
/* PFSYNC_ACT_OUPD */
{ pfsync_in_error, 0 },
/* PFSYNC_ACT_UPD_C */
{ pfsync_in_upd_c, sizeof(struct pfsync_upd_c) },
/* PFSYNC_ACT_UPD_REQ */
{ pfsync_in_ureq, sizeof(struct pfsync_upd_req) },
/* PFSYNC_ACT_DEL */
{ pfsync_in_del, sizeof(struct pfsync_state) },
/* PFSYNC_ACT_DEL_C */
{ pfsync_in_del_c, sizeof(struct pfsync_del_c) },
/* PFSYNC_ACT_INS_F */
{ pfsync_in_error, 0 },
/* PFSYNC_ACT_DEL_F */
{ pfsync_in_error, 0 },
/* PFSYNC_ACT_BUS */
{ pfsync_in_bus, sizeof(struct pfsync_bus) },
/* PFSYNC_ACT_OTDB */
{ pfsync_in_error, 0 },
/* PFSYNC_ACT_EOF */
{ pfsync_in_error, 0 },
/* PFSYNC_ACT_INS */
{ pfsync_in_ins, sizeof(struct pfsync_state) },
/* PFSYNC_ACT_UPD */
{ pfsync_in_upd, sizeof(struct pfsync_state) },
/* PFSYNC_ACT_TDB */
{ pfsync_in_tdb, sizeof(struct pfsync_tdb) },
};
struct pfsync_q {
void (*write)(struct pf_state *, void *);
size_t len;
u_int8_t action;
};
/* we have one of these for every PFSYNC_S_ */
void pfsync_out_state(struct pf_state *, void *);
void pfsync_out_iack(struct pf_state *, void *);
void pfsync_out_upd_c(struct pf_state *, void *);
void pfsync_out_del(struct pf_state *, void *);
struct pfsync_q pfsync_qs[] = {
{ pfsync_out_iack, sizeof(struct pfsync_ins_ack), PFSYNC_ACT_INS_ACK },
{ pfsync_out_upd_c, sizeof(struct pfsync_upd_c), PFSYNC_ACT_UPD_C },
{ pfsync_out_del, sizeof(struct pfsync_del_c), PFSYNC_ACT_DEL_C },
{ pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_INS },
{ pfsync_out_state, sizeof(struct pfsync_state), PFSYNC_ACT_UPD }
};
void pfsync_q_ins(struct pf_state *, int);
void pfsync_q_del(struct pf_state *);
struct pfsync_upd_req_item {
TAILQ_ENTRY(pfsync_upd_req_item) ur_entry;
TAILQ_ENTRY(pfsync_upd_req_item) ur_snap;
struct pfsync_upd_req ur_msg;
};
TAILQ_HEAD(pfsync_upd_reqs, pfsync_upd_req_item);
struct pfsync_deferral {
TAILQ_ENTRY(pfsync_deferral) pd_entry;
struct pf_state *pd_st;
struct mbuf *pd_m;
uint64_t pd_deadline;
};
TAILQ_HEAD(pfsync_deferrals, pfsync_deferral);
#define PFSYNC_PLSIZE MAX(sizeof(struct pfsync_upd_req_item), \
sizeof(struct pfsync_deferral))
void pfsync_out_tdb(struct tdb *, void *);
struct pfsync_softc {
struct ifnet sc_if;
unsigned int sc_sync_ifidx;
struct pool sc_pool;
struct ip_moptions sc_imo;
struct in_addr sc_sync_peer;
u_int8_t sc_maxupdates;
struct ip sc_template;
struct pf_state_queue sc_qs[PFSYNC_S_COUNT];
struct mutex sc_st_mtx;
size_t sc_len;
struct pfsync_upd_reqs sc_upd_req_list;
struct mutex sc_upd_req_mtx;
int sc_initial_bulk;
int sc_link_demoted;
int sc_defer;
struct pfsync_deferrals sc_deferrals;
u_int sc_deferred;
struct mutex sc_deferrals_mtx;
struct timeout sc_deferrals_tmo;
void *sc_plus;
size_t sc_pluslen;
u_int32_t sc_ureq_sent;
int sc_bulk_tries;
struct timeout sc_bulkfail_tmo;
u_int32_t sc_ureq_received;
struct pf_state *sc_bulk_next;
struct pf_state *sc_bulk_last;
struct timeout sc_bulk_tmo;
TAILQ_HEAD(, tdb) sc_tdb_q;
struct mutex sc_tdb_mtx;
struct task sc_ltask;
struct task sc_dtask;
struct timeout sc_tmo;
};
struct pfsync_snapshot {
struct pfsync_softc *sn_sc;
struct pf_state_queue sn_qs[PFSYNC_S_COUNT];
struct pfsync_upd_reqs sn_upd_req_list;
TAILQ_HEAD(, tdb) sn_tdb_q;
size_t sn_len;
void *sn_plus;
size_t sn_pluslen;
};
struct pfsync_softc *pfsyncif = NULL;
struct cpumem *pfsynccounters;
void pfsyncattach(int);
int pfsync_clone_create(struct if_clone *, int);
int pfsync_clone_destroy(struct ifnet *);
int pfsync_alloc_scrub_memory(struct pfsync_state_peer *,
struct pf_state_peer *);
void pfsync_update_net_tdb(struct pfsync_tdb *);
int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int pfsyncioctl(struct ifnet *, u_long, caddr_t);
void pfsyncstart(struct ifqueue *);
void pfsync_syncdev_state(void *);
void pfsync_ifdetach(void *);
void pfsync_deferred(struct pf_state *, int);
void pfsync_undefer(struct pfsync_deferral *, int);
void pfsync_deferrals_tmo(void *);
void pfsync_cancel_full_update(struct pfsync_softc *);
void pfsync_request_full_update(struct pfsync_softc *);
void pfsync_request_update(u_int32_t, u_int64_t);
void pfsync_update_state_req(struct pf_state *);
void pfsync_drop(struct pfsync_softc *);
void pfsync_sendout(void);
void pfsync_send_plus(void *, size_t);
void pfsync_timeout(void *);
void pfsync_tdb_timeout(void *);
void pfsync_bulk_start(void);
void pfsync_bulk_status(u_int8_t);
void pfsync_bulk_update(void *);
void pfsync_bulk_fail(void *);
void pfsync_grab_snapshot(struct pfsync_snapshot *, struct pfsync_softc *);
void pfsync_drop_snapshot(struct pfsync_snapshot *);
void pfsync_send_dispatch(void *);
void pfsync_send_pkt(struct mbuf *);
static struct mbuf_queue pfsync_mq;
static struct task pfsync_task =
TASK_INITIALIZER(pfsync_send_dispatch, &pfsync_mq);
#define PFSYNC_MAX_BULKTRIES 12
int pfsync_sync_ok;
struct if_clone pfsync_cloner =
IF_CLONE_INITIALIZER("pfsync", pfsync_clone_create, pfsync_clone_destroy);
void
pfsyncattach(int npfsync)
{
if_clone_attach(&pfsync_cloner);
pfsynccounters = counters_alloc(pfsyncs_ncounters);
mq_init(&pfsync_mq, 4096, IPL_MPFLOOR);
}
int
pfsync_clone_create(struct if_clone *ifc, int unit)
{
struct pfsync_softc *sc;
struct ifnet *ifp;
int q;
if (unit != 0)
return (EINVAL);
pfsync_sync_ok = 1;
sc = malloc(sizeof(*pfsyncif), M_DEVBUF, M_WAITOK|M_ZERO);
for (q = 0; q < PFSYNC_S_COUNT; q++)
TAILQ_INIT(&sc->sc_qs[q]);
mtx_init(&sc->sc_st_mtx, IPL_MPFLOOR);
pool_init(&sc->sc_pool, PFSYNC_PLSIZE, 0, IPL_MPFLOOR, 0, "pfsync",
NULL);
TAILQ_INIT(&sc->sc_upd_req_list);
mtx_init(&sc->sc_upd_req_mtx, IPL_MPFLOOR);
TAILQ_INIT(&sc->sc_deferrals);
mtx_init(&sc->sc_deferrals_mtx, IPL_MPFLOOR);
timeout_set_proc(&sc->sc_deferrals_tmo, pfsync_deferrals_tmo, sc);
task_set(&sc->sc_ltask, pfsync_syncdev_state, sc);
task_set(&sc->sc_dtask, pfsync_ifdetach, sc);
sc->sc_deferred = 0;
TAILQ_INIT(&sc->sc_tdb_q);
mtx_init(&sc->sc_tdb_mtx, IPL_MPFLOOR);
sc->sc_len = PFSYNC_MINPKT;
sc->sc_maxupdates = 128;
sc->sc_imo.imo_membership = mallocarray(IP_MIN_MEMBERSHIPS,
sizeof(struct in_multi *), M_IPMOPTS, M_WAITOK|M_ZERO);
sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
ifp = &sc->sc_if;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "pfsync%d", unit);
ifp->if_softc = sc;
ifp->if_ioctl = pfsyncioctl;
ifp->if_output = pfsyncoutput;
ifp->if_qstart = pfsyncstart;
ifp->if_type = IFT_PFSYNC;
ifp->if_hdrlen = sizeof(struct pfsync_header);
ifp->if_mtu = ETHERMTU;
ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE;
timeout_set_proc(&sc->sc_tmo, pfsync_timeout, NULL);
timeout_set_proc(&sc->sc_bulk_tmo, pfsync_bulk_update, NULL);
timeout_set_proc(&sc->sc_bulkfail_tmo, pfsync_bulk_fail, NULL);
if_attach(ifp);
if_alloc_sadl(ifp);
#if NCARP > 0
if_addgroup(ifp, "carp");
#endif
#if NBPFILTER > 0
bpfattach(&sc->sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
#endif
pfsyncif = sc;
return (0);
}
int
pfsync_clone_destroy(struct ifnet *ifp)
{
struct pfsync_softc *sc = ifp->if_softc;
struct ifnet *ifp0;
struct pfsync_deferral *pd;
struct pfsync_deferrals deferrals;
NET_LOCK();
#if NCARP > 0
if (!pfsync_sync_ok)
carp_group_demote_adj(&sc->sc_if, -1, "pfsync destroy");
if (sc->sc_link_demoted)
carp_group_demote_adj(&sc->sc_if, -1, "pfsync destroy");
#endif
if ((ifp0 = if_get(sc->sc_sync_ifidx)) != NULL) {
if_linkstatehook_del(ifp0, &sc->sc_ltask);
if_detachhook_del(ifp0, &sc->sc_dtask);
}
if_put(ifp0);
/* XXXSMP breaks atomicity */
NET_UNLOCK();
if_detach(ifp);
NET_LOCK();
pfsync_drop(sc);
if (sc->sc_deferred > 0) {
TAILQ_INIT(&deferrals);
mtx_enter(&sc->sc_deferrals_mtx);
TAILQ_CONCAT(&deferrals, &sc->sc_deferrals, pd_entry);
sc->sc_deferred = 0;
mtx_leave(&sc->sc_deferrals_mtx);
while ((pd = TAILQ_FIRST(&deferrals)) != NULL) {
TAILQ_REMOVE(&deferrals, pd, pd_entry);
pfsync_undefer(pd, 0);
}
}
pfsyncif = NULL;
timeout_del(&sc->sc_bulkfail_tmo);
timeout_del(&sc->sc_bulk_tmo);
timeout_del(&sc->sc_tmo);
NET_UNLOCK();
pool_destroy(&sc->sc_pool);
free(sc->sc_imo.imo_membership, M_IPMOPTS,
sc->sc_imo.imo_max_memberships * sizeof(struct in_multi *));
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
/*
* Start output on the pfsync interface.
*/
void
pfsyncstart(struct ifqueue *ifq)
{
ifq_purge(ifq);
}
void
pfsync_syncdev_state(void *arg)
{
struct pfsync_softc *sc = arg;
struct ifnet *ifp;
if ((sc->sc_if.if_flags & IFF_UP) == 0)
return;
if ((ifp = if_get(sc->sc_sync_ifidx)) == NULL)
return;
if (ifp->if_link_state == LINK_STATE_DOWN) {
sc->sc_if.if_flags &= ~IFF_RUNNING;
if (!sc->sc_link_demoted) {
#if NCARP > 0
carp_group_demote_adj(&sc->sc_if, 1,
"pfsync link state down");
#endif
sc->sc_link_demoted = 1;
}
/* drop everything */
timeout_del(&sc->sc_tmo);
pfsync_drop(sc);
pfsync_cancel_full_update(sc);
} else if (sc->sc_link_demoted) {
sc->sc_if.if_flags |= IFF_RUNNING;
pfsync_request_full_update(sc);
}
if_put(ifp);
}
void
pfsync_ifdetach(void *arg)
{
struct pfsync_softc *sc = arg;
struct ifnet *ifp;
if ((ifp = if_get(sc->sc_sync_ifidx)) != NULL) {
if_linkstatehook_del(ifp, &sc->sc_ltask);
if_detachhook_del(ifp, &sc->sc_dtask);
}
if_put(ifp);
sc->sc_sync_ifidx = 0;
}
int
pfsync_alloc_scrub_memory(struct pfsync_state_peer *s,
struct pf_state_peer *d)
{
if (s->scrub.scrub_flag && d->scrub == NULL) {
d->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT | PR_ZERO);
if (d->scrub == NULL)
return (ENOMEM);
}
return (0);
}
void
pfsync_state_export(struct pfsync_state *sp, struct pf_state *st)
{
pf_state_export(sp, st);
}
int
pfsync_state_import(struct pfsync_state *sp, int flags)
{
struct pf_state *st = NULL;
struct pf_state_key *skw = NULL, *sks = NULL;
struct pf_rule *r = NULL;
struct pfi_kif *kif;
int pool_flags;
int error = ENOMEM;
int n = 0;
if (sp->creatorid == 0) {
DPFPRINTF(LOG_NOTICE, "pfsync_state_import: "
"invalid creator id: %08x", ntohl(sp->creatorid));
return (EINVAL);
}
if ((kif = pfi_kif_get(sp->ifname, NULL)) == NULL) {
DPFPRINTF(LOG_NOTICE, "pfsync_state_import: "
"unknown interface: %s", sp->ifname);
if (flags & PFSYNC_SI_IOCTL)
return (EINVAL);
return (0); /* skip this state */
}
if (sp->af == 0)
return (0); /* skip this state */
/*
* If the ruleset checksums match or the state is coming from the ioctl,
* it's safe to associate the state with the rule of that number.
*/
if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) &&
(flags & (PFSYNC_SI_IOCTL | PFSYNC_SI_CKSUM)) && ntohl(sp->rule) <
pf_main_ruleset.rules.active.rcount) {
TAILQ_FOREACH(r, pf_main_ruleset.rules.active.ptr, entries)
if (ntohl(sp->rule) == n++)
break;
} else
r = &pf_default_rule;
if ((r->max_states && r->states_cur >= r->max_states))
goto cleanup;
if (flags & PFSYNC_SI_IOCTL)
pool_flags = PR_WAITOK | PR_LIMITFAIL | PR_ZERO;
else
pool_flags = PR_NOWAIT | PR_LIMITFAIL | PR_ZERO;
if ((st = pool_get(&pf_state_pl, pool_flags)) == NULL)
goto cleanup;
if ((skw = pf_alloc_state_key(pool_flags)) == NULL)
goto cleanup;
if ((sp->key[PF_SK_WIRE].af &&
(sp->key[PF_SK_WIRE].af != sp->key[PF_SK_STACK].af)) ||
PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0],
&sp->key[PF_SK_STACK].addr[0], sp->af) ||
PF_ANEQ(&sp->key[PF_SK_WIRE].addr[1],
&sp->key[PF_SK_STACK].addr[1], sp->af) ||
sp->key[PF_SK_WIRE].port[0] != sp->key[PF_SK_STACK].port[0] ||
sp->key[PF_SK_WIRE].port[1] != sp->key[PF_SK_STACK].port[1] ||
sp->key[PF_SK_WIRE].rdomain != sp->key[PF_SK_STACK].rdomain) {
if ((sks = pf_alloc_state_key(pool_flags)) == NULL)
goto cleanup;
} else
sks = skw;
/* allocate memory for scrub info */
if (pfsync_alloc_scrub_memory(&sp->src, &st->src) ||
pfsync_alloc_scrub_memory(&sp->dst, &st->dst))
goto cleanup;
/* copy to state key(s) */
skw->addr[0] = sp->key[PF_SK_WIRE].addr[0];
skw->addr[1] = sp->key[PF_SK_WIRE].addr[1];
skw->port[0] = sp->key[PF_SK_WIRE].port[0];
skw->port[1] = sp->key[PF_SK_WIRE].port[1];
skw->rdomain = ntohs(sp->key[PF_SK_WIRE].rdomain);
PF_REF_INIT(skw->refcnt);
skw->proto = sp->proto;
if (!(skw->af = sp->key[PF_SK_WIRE].af))
skw->af = sp->af;
if (sks != skw) {
sks->addr[0] = sp->key[PF_SK_STACK].addr[0];
sks->addr[1] = sp->key[PF_SK_STACK].addr[1];
sks->port[0] = sp->key[PF_SK_STACK].port[0];
sks->port[1] = sp->key[PF_SK_STACK].port[1];
sks->rdomain = ntohs(sp->key[PF_SK_STACK].rdomain);
PF_REF_INIT(sks->refcnt);
if (!(sks->af = sp->key[PF_SK_STACK].af))
sks->af = sp->af;
if (sks->af != skw->af) {
switch (sp->proto) {
case IPPROTO_ICMP:
sks->proto = IPPROTO_ICMPV6;
break;
case IPPROTO_ICMPV6:
sks->proto = IPPROTO_ICMP;
break;
default:
sks->proto = sp->proto;
}
} else
sks->proto = sp->proto;
if (((sks->af != AF_INET) && (sks->af != AF_INET6)) ||
((skw->af != AF_INET) && (skw->af != AF_INET6))) {
error = EINVAL;
goto cleanup;
}
} else if ((sks->af != AF_INET) && (sks->af != AF_INET6)) {
error = EINVAL;
goto cleanup;
}
st->rtableid[PF_SK_WIRE] = ntohl(sp->rtableid[PF_SK_WIRE]);
st->rtableid[PF_SK_STACK] = ntohl(sp->rtableid[PF_SK_STACK]);
/* copy to state */
st->rt_addr = sp->rt_addr;
st->rt = sp->rt;
st->creation = getuptime() - ntohl(sp->creation);
st->expire = getuptime();
if (ntohl(sp->expire)) {
u_int32_t timeout;
timeout = r->timeout[sp->timeout];
if (!timeout)
timeout = pf_default_rule.timeout[sp->timeout];
/* sp->expire may have been adaptively scaled by export. */
st->expire -= timeout - ntohl(sp->expire);
}
st->direction = sp->direction;
st->log = sp->log;
st->timeout = sp->timeout;
st->state_flags = ntohs(sp->state_flags);
st->max_mss = ntohs(sp->max_mss);
st->min_ttl = sp->min_ttl;
st->set_tos = sp->set_tos;
st->set_prio[0] = sp->set_prio[0];
st->set_prio[1] = sp->set_prio[1];
st->id = sp->id;
st->creatorid = sp->creatorid;
pf_state_peer_ntoh(&sp->src, &st->src);
pf_state_peer_ntoh(&sp->dst, &st->dst);
st->rule.ptr = r;
st->anchor.ptr = NULL;
st->pfsync_time = getuptime();
st->sync_state = PFSYNC_S_NONE;
refcnt_init(&st->refcnt);
/* XXX when we have anchors, use STATE_INC_COUNTERS */
r->states_cur++;
r->states_tot++;
if (!ISSET(flags, PFSYNC_SI_IOCTL))
SET(st->state_flags, PFSTATE_NOSYNC);
/*
* We just set PFSTATE_NOSYNC bit, which prevents
* pfsync_insert_state() to insert state to pfsync.
*/
if (pf_state_insert(kif, &skw, &sks, st) != 0) {
/* XXX when we have anchors, use STATE_DEC_COUNTERS */
r->states_cur--;
error = EEXIST;
goto cleanup_state;
}
if (!ISSET(flags, PFSYNC_SI_IOCTL)) {
CLR(st->state_flags, PFSTATE_NOSYNC);
if (ISSET(st->state_flags, PFSTATE_ACK)) {
pfsync_q_ins(st, PFSYNC_S_IACK);
schednetisr(NETISR_PFSYNC);
}
}
CLR(st->state_flags, PFSTATE_ACK);
return (0);
cleanup:
if (skw == sks)
sks = NULL;
if (skw != NULL)
pool_put(&pf_state_key_pl, skw);
if (sks != NULL)
pool_put(&pf_state_key_pl, sks);
cleanup_state: /* pf_state_insert frees the state keys */
if (st) {
if (st->dst.scrub)
pool_put(&pf_state_scrub_pl, st->dst.scrub);
if (st->src.scrub)
pool_put(&pf_state_scrub_pl, st->src.scrub);
pool_put(&pf_state_pl, st);
}
return (error);
}
int
pfsync_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *n, *m = *mp;
struct pfsync_softc *sc = pfsyncif;
struct ip *ip = mtod(m, struct ip *);
struct pfsync_header *ph;
struct pfsync_subheader subh;
int offset, noff, len, count, mlen, flags = 0;
int e;
NET_ASSERT_LOCKED();
pfsyncstat_inc(pfsyncs_ipackets);
/* verify that we have a sync interface configured */
if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
sc->sc_sync_ifidx == 0 || !pf_status.running)
goto done;
/* verify that the packet came in on the right interface */
if (sc->sc_sync_ifidx != m->m_pkthdr.ph_ifidx) {
pfsyncstat_inc(pfsyncs_badif);
goto done;
}
sc->sc_if.if_ipackets++;
sc->sc_if.if_ibytes += m->m_pkthdr.len;
/* verify that the IP TTL is 255. */
if (ip->ip_ttl != PFSYNC_DFLTTL) {
pfsyncstat_inc(pfsyncs_badttl);
goto done;
}
offset = ip->ip_hl << 2;
n = m_pulldown(m, offset, sizeof(*ph), &noff);
if (n == NULL) {
pfsyncstat_inc(pfsyncs_hdrops);
return IPPROTO_DONE;
}
ph = (struct pfsync_header *)(n->m_data + noff);
/* verify the version */
if (ph->version != PFSYNC_VERSION) {
pfsyncstat_inc(pfsyncs_badver);
goto done;
}
len = ntohs(ph->len) + offset;
if (m->m_pkthdr.len < len) {
pfsyncstat_inc(pfsyncs_badlen);
goto done;
}
if (!bcmp(&ph->pfcksum, &pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH))
flags = PFSYNC_SI_CKSUM;
offset += sizeof(*ph);
while (offset <= len - sizeof(subh)) {
m_copydata(m, offset, sizeof(subh), &subh);
offset += sizeof(subh);
mlen = subh.len << 2;
count = ntohs(subh.count);
if (subh.action >= PFSYNC_ACT_MAX ||
subh.action >= nitems(pfsync_acts) ||
mlen < pfsync_acts[subh.action].len) {
/*
* subheaders are always followed by at least one
* message, so if the peer is new
* enough to tell us how big its messages are then we
* know enough to skip them.
*/
if (count > 0 && mlen > 0) {
offset += count * mlen;
continue;
}
pfsyncstat_inc(pfsyncs_badact);
goto done;
}
n = m_pulldown(m, offset, mlen * count, &noff);
if (n == NULL) {
pfsyncstat_inc(pfsyncs_badlen);
return IPPROTO_DONE;
}
e = pfsync_acts[subh.action].in(n->m_data + noff, mlen, count,
flags);
if (e != 0)
goto done;
offset += mlen * count;
}
done:
m_freem(m);
return IPPROTO_DONE;
}
int
pfsync_in_clr(caddr_t buf, int len, int count, int flags)
{
struct pfsync_clr *clr;
struct pf_state *st, *nexts;
struct pfi_kif *kif;
u_int32_t creatorid;
int i;
PF_LOCK();
for (i = 0; i < count; i++) {
clr = (struct pfsync_clr *)buf + len * i;
kif = NULL;
creatorid = clr->creatorid;
if (strlen(clr->ifname) &&
(kif = pfi_kif_find(clr->ifname)) == NULL)
continue;
PF_STATE_ENTER_WRITE();
for (st = RB_MIN(pf_state_tree_id, &tree_id); st; st = nexts) {
nexts = RB_NEXT(pf_state_tree_id, &tree_id, st);
if (st->creatorid == creatorid &&
((kif && st->kif == kif) || !kif)) {
SET(st->state_flags, PFSTATE_NOSYNC);
pf_remove_state(st);
}
}
PF_STATE_EXIT_WRITE();
}
PF_UNLOCK();
return (0);
}
int
pfsync_in_ins(caddr_t buf, int len, int count, int flags)
{
struct pfsync_state *sp;
sa_family_t af1, af2;
int i;
PF_LOCK();
for (i = 0; i < count; i++) {
sp = (struct pfsync_state *)(buf + len * i);
af1 = sp->key[0].af;
af2 = sp->key[1].af;
/* check for invalid values */
if (sp->timeout >= PFTM_MAX ||
sp->src.state > PF_TCPS_PROXY_DST ||
sp->dst.state > PF_TCPS_PROXY_DST ||
sp->direction > PF_OUT ||
(((af1 || af2) &&
((af1 != AF_INET && af1 != AF_INET6) ||
(af2 != AF_INET && af2 != AF_INET6))) ||
(sp->af != AF_INET && sp->af != AF_INET6))) {
DPFPRINTF(LOG_NOTICE,
"pfsync_input: PFSYNC5_ACT_INS: invalid value");
pfsyncstat_inc(pfsyncs_badval);
continue;
}
if (pfsync_state_import(sp, flags) == ENOMEM) {
/* drop out, but process the rest of the actions */
break;
}
}
PF_UNLOCK();
return (0);
}
int
pfsync_in_iack(caddr_t buf, int len, int count, int flags)
{
struct pfsync_ins_ack *ia;
struct pf_state_cmp id_key;
struct pf_state *st;
int i;
for (i = 0; i < count; i++) {
ia = (struct pfsync_ins_ack *)(buf + len * i);
id_key.id = ia->id;
id_key.creatorid = ia->creatorid;
PF_STATE_ENTER_READ();
st = pf_find_state_byid(&id_key);
pf_state_ref(st);
PF_STATE_EXIT_READ();
if (st == NULL)
continue;
if (ISSET(st->state_flags, PFSTATE_ACK))
pfsync_deferred(st, 0);
pf_state_unref(st);
}
return (0);
}
int
pfsync_upd_tcp(struct pf_state *st, struct pfsync_state_peer *src,
struct pfsync_state_peer *dst)
{
int sync = 0;
/*
* The state should never go backwards except
* for syn-proxy states. Neither should the
* sequence window slide backwards.
*/
if ((st->src.state > src->state &&
(st->src.state < PF_TCPS_PROXY_SRC ||
src->state >= PF_TCPS_PROXY_SRC)) ||
(st->src.state == src->state &&
SEQ_GT(st->src.seqlo, ntohl(src->seqlo))))
sync++;
else
pf_state_peer_ntoh(src, &st->src);
if ((st->dst.state > dst->state) ||
(st->dst.state >= TCPS_SYN_SENT &&
SEQ_GT(st->dst.seqlo, ntohl(dst->seqlo))))
sync++;
else
pf_state_peer_ntoh(dst, &st->dst);
return (sync);
}
int
pfsync_in_upd(caddr_t buf, int len, int count, int flags)
{
struct pfsync_state *sp;
struct pf_state_cmp id_key;
struct pf_state *st;
int sync, error;
int i;
for (i = 0; i < count; i++) {
sp = (struct pfsync_state *)(buf + len * i);
/* check for invalid values */
if (sp->timeout >= PFTM_MAX ||
sp->src.state > PF_TCPS_PROXY_DST ||
sp->dst.state > PF_TCPS_PROXY_DST) {
DPFPRINTF(LOG_NOTICE,
"pfsync_input: PFSYNC_ACT_UPD: invalid value");
pfsyncstat_inc(pfsyncs_badval);
continue;
}
id_key.id = sp->id;
id_key.creatorid = sp->creatorid;
PF_STATE_ENTER_READ();
st = pf_find_state_byid(&id_key);
pf_state_ref(st);
PF_STATE_EXIT_READ();
if (st == NULL) {
/* insert the update */
PF_LOCK();
error = pfsync_state_import(sp, flags);
if (error)
pfsyncstat_inc(pfsyncs_badstate);
PF_UNLOCK();
continue;
}
if (ISSET(st->state_flags, PFSTATE_ACK))
pfsync_deferred(st, 1);
if (st->key[PF_SK_WIRE]->proto == IPPROTO_TCP)
sync = pfsync_upd_tcp(st, &sp->src, &sp->dst);
else {
sync = 0;
/*
* Non-TCP protocol state machine always go
* forwards
*/
if (st->src.state > sp->src.state)
sync++;
else
pf_state_peer_ntoh(&sp->src, &st->src);
if (st->dst.state > sp->dst.state)
sync++;
else
pf_state_peer_ntoh(&sp->dst, &st->dst);
}
if (sync < 2) {
pfsync_alloc_scrub_memory(&sp->dst, &st->dst);
pf_state_peer_ntoh(&sp->dst, &st->dst);
st->expire = getuptime();
st->timeout = sp->timeout;
}
st->pfsync_time = getuptime();
if (sync) {
pfsyncstat_inc(pfsyncs_stale);
pfsync_update_state(st);
schednetisr(NETISR_PFSYNC);
}
pf_state_unref(st);
}
return (0);
}
int
pfsync_in_upd_c(caddr_t buf, int len, int count, int flags)
{
struct pfsync_upd_c *up;
struct pf_state_cmp id_key;
struct pf_state *st;
int sync;
int i;
for (i = 0; i < count; i++) {
up = (struct pfsync_upd_c *)(buf + len * i);
/* check for invalid values */
if (up->timeout >= PFTM_MAX ||
up->src.state > PF_TCPS_PROXY_DST ||
up->dst.state > PF_TCPS_PROXY_DST) {
DPFPRINTF(LOG_NOTICE,
"pfsync_input: PFSYNC_ACT_UPD_C: invalid value");
pfsyncstat_inc(pfsyncs_badval);
continue;
}
id_key.id = up->id;
id_key.creatorid = up->creatorid;
PF_STATE_ENTER_READ();
st = pf_find_state_byid(&id_key);
pf_state_ref(st);
PF_STATE_EXIT_READ();
if (st == NULL) {
/* We don't have this state. Ask for it. */
pfsync_request_update(id_key.creatorid, id_key.id);
continue;
}
if (ISSET(st->state_flags, PFSTATE_ACK))
pfsync_deferred(st, 1);
if (st->key[PF_SK_WIRE]->proto == IPPROTO_TCP)
sync = pfsync_upd_tcp(st, &up->src, &up->dst);
else {
sync = 0;
/*
* Non-TCP protocol state machine always go
* forwards
*/
if (st->src.state > up->src.state)
sync++;
else
pf_state_peer_ntoh(&up->src, &st->src);
if (st->dst.state > up->dst.state)
sync++;
else
pf_state_peer_ntoh(&up->dst, &st->dst);
}
if (sync < 2) {
pfsync_alloc_scrub_memory(&up->dst, &st->dst);
pf_state_peer_ntoh(&up->dst, &st->dst);
st->expire = getuptime();
st->timeout = up->timeout;
}
st->pfsync_time = getuptime();
if (sync) {
pfsyncstat_inc(pfsyncs_stale);
pfsync_update_state(st);
schednetisr(NETISR_PFSYNC);
}
pf_state_unref(st);
}
return (0);
}
int
pfsync_in_ureq(caddr_t buf, int len, int count, int flags)
{
struct pfsync_upd_req *ur;
int i;
struct pf_state_cmp id_key;
struct pf_state *st;
for (i = 0; i < count; i++) {
ur = (struct pfsync_upd_req *)(buf + len * i);
id_key.id = ur->id;
id_key.creatorid = ur->creatorid;
if (id_key.id == 0 && id_key.creatorid == 0)
pfsync_bulk_start();
else {
PF_STATE_ENTER_READ();
st = pf_find_state_byid(&id_key);
pf_state_ref(st);
PF_STATE_EXIT_READ();
if (st == NULL) {
pfsyncstat_inc(pfsyncs_badstate);
continue;
}
if (ISSET(st->state_flags, PFSTATE_NOSYNC)) {
pf_state_unref(st);
continue;
}
pfsync_update_state_req(st);
pf_state_unref(st);
}
}
return (0);
}
int
pfsync_in_del(caddr_t buf, int len, int count, int flags)
{
struct pfsync_state *sp;
struct pf_state_cmp id_key;
struct pf_state *st;
int i;
PF_STATE_ENTER_WRITE();
for (i = 0; i < count; i++) {
sp = (struct pfsync_state *)(buf + len * i);
id_key.id = sp->id;
id_key.creatorid = sp->creatorid;
st = pf_find_state_byid(&id_key);
if (st == NULL) {
pfsyncstat_inc(pfsyncs_badstate);
continue;
}
SET(st->state_flags, PFSTATE_NOSYNC);
pf_remove_state(st);
}
PF_STATE_EXIT_WRITE();
return (0);
}
int
pfsync_in_del_c(caddr_t buf, int len, int count, int flags)
{
struct pfsync_del_c *sp;
struct pf_state_cmp id_key;
struct pf_state *st;
int i;
PF_LOCK();
PF_STATE_ENTER_WRITE();
for (i = 0; i < count; i++) {
sp = (struct pfsync_del_c *)(buf + len * i);
id_key.id = sp->id;
id_key.creatorid = sp->creatorid;
st = pf_find_state_byid(&id_key);
if (st == NULL) {
pfsyncstat_inc(pfsyncs_badstate);
continue;
}
SET(st->state_flags, PFSTATE_NOSYNC);
pf_remove_state(st);
}
PF_STATE_EXIT_WRITE();
PF_UNLOCK();
return (0);
}
int
pfsync_in_bus(caddr_t buf, int len, int count, int flags)
{
struct pfsync_softc *sc = pfsyncif;
struct pfsync_bus *bus;
/* If we're not waiting for a bulk update, who cares. */
if (sc->sc_ureq_sent == 0)
return (0);
bus = (struct pfsync_bus *)buf;
switch (bus->status) {
case PFSYNC_BUS_START:
timeout_add(&sc->sc_bulkfail_tmo, 4 * hz +
pf_pool_limits[PF_LIMIT_STATES].limit /
((sc->sc_if.if_mtu - PFSYNC_MINPKT) /
sizeof(struct pfsync_state)));
DPFPRINTF(LOG_INFO, "received bulk update start");
break;
case PFSYNC_BUS_END:
if (getuptime() - ntohl(bus->endtime) >=
sc->sc_ureq_sent) {
/* that's it, we're happy */
sc->sc_ureq_sent = 0;
sc->sc_bulk_tries = 0;
timeout_del(&sc->sc_bulkfail_tmo);
#if NCARP > 0
if (!pfsync_sync_ok)
carp_group_demote_adj(&sc->sc_if, -1,
sc->sc_link_demoted ?
"pfsync link state up" :
"pfsync bulk done");
if (sc->sc_initial_bulk) {
carp_group_demote_adj(&sc->sc_if, -32,
"pfsync init");
sc->sc_initial_bulk = 0;
}
#endif
pfsync_sync_ok = 1;
sc->sc_link_demoted = 0;
DPFPRINTF(LOG_INFO, "received valid bulk update end");
} else {
DPFPRINTF(LOG_WARNING, "received invalid "
"bulk update end: bad timestamp");
}
break;
}
return (0);
}
int
pfsync_in_tdb(caddr_t buf, int len, int count, int flags)
{
#if defined(IPSEC)
struct pfsync_tdb *tp;
int i;
for (i = 0; i < count; i++) {
tp = (struct pfsync_tdb *)(buf + len * i);
pfsync_update_net_tdb(tp);
}
#endif
return (0);
}
#if defined(IPSEC)
/* Update an in-kernel tdb. Silently fail if no tdb is found. */
void
pfsync_update_net_tdb(struct pfsync_tdb *pt)
{
struct tdb *tdb;
NET_ASSERT_LOCKED();
/* check for invalid values */
if (ntohl(pt->spi) <= SPI_RESERVED_MAX ||
(pt->dst.sa.sa_family != AF_INET &&
pt->dst.sa.sa_family != AF_INET6))
goto bad;
tdb = gettdb(ntohs(pt->rdomain), pt->spi,
(union sockaddr_union *)&pt->dst, pt->sproto);
if (tdb) {
pt->rpl = betoh64(pt->rpl);
pt->cur_bytes = betoh64(pt->cur_bytes);
/* Neither replay nor byte counter should ever decrease. */
if (pt->rpl < tdb->tdb_rpl ||
pt->cur_bytes < tdb->tdb_cur_bytes) {
tdb_unref(tdb);
goto bad;
}
tdb->tdb_rpl = pt->rpl;
tdb->tdb_cur_bytes = pt->cur_bytes;
tdb_unref(tdb);
}
return;
bad:
DPFPRINTF(LOG_WARNING, "pfsync_insert: PFSYNC_ACT_TDB_UPD: "
"invalid value");
pfsyncstat_inc(pfsyncs_badstate);
return;
}
#endif
int
pfsync_in_eof(caddr_t buf, int len, int count, int flags)
{
if (len > 0 || count > 0)
pfsyncstat_inc(pfsyncs_badact);
/* we're done. let the caller return */
return (1);
}
int
pfsync_in_error(caddr_t buf, int len, int count, int flags)
{
pfsyncstat_inc(pfsyncs_badact);
return (-1);
}
int
pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
m_freem(m); /* drop packet */
return (EAFNOSUPPORT);
}
int
pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct proc *p = curproc;
struct pfsync_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct ip_moptions *imo = &sc->sc_imo;
struct pfsyncreq pfsyncr;
struct ifnet *ifp0, *sifp;
struct ip *ip;
int error;
switch (cmd) {
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_RUNNING) == 0 &&
(ifp->if_flags & IFF_UP)) {
ifp->if_flags |= IFF_RUNNING;
#if NCARP > 0
sc->sc_initial_bulk = 1;
carp_group_demote_adj(&sc->sc_if, 32, "pfsync init");
#endif
pfsync_request_full_update(sc);
}
if ((ifp->if_flags & IFF_RUNNING) &&
(ifp->if_flags & IFF_UP) == 0) {
ifp->if_flags &= ~IFF_RUNNING;
/* drop everything */
timeout_del(&sc->sc_tmo);
pfsync_drop(sc);
pfsync_cancel_full_update(sc);
}
break;
case SIOCSIFMTU:
if ((ifp0 = if_get(sc->sc_sync_ifidx)) == NULL)
return (EINVAL);
error = 0;
if (ifr->ifr_mtu <= PFSYNC_MINPKT ||
ifr->ifr_mtu > ifp0->if_mtu) {
error = EINVAL;
}
if_put(ifp0);
if (error)
return error;
if (ifr->ifr_mtu < ifp->if_mtu)
pfsync_sendout();
ifp->if_mtu = ifr->ifr_mtu;
break;
case SIOCGETPFSYNC:
bzero(&pfsyncr, sizeof(pfsyncr));
if ((ifp0 = if_get(sc->sc_sync_ifidx)) != NULL) {
strlcpy(pfsyncr.pfsyncr_syncdev,
ifp0->if_xname, IFNAMSIZ);
}
if_put(ifp0);
pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer;
pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates;
pfsyncr.pfsyncr_defer = sc->sc_defer;
return (copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr)));
case SIOCSETPFSYNC:
if ((error = suser(p)) != 0)
return (error);
if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr))))
return (error);
if (pfsyncr.pfsyncr_syncpeer.s_addr == 0)
sc->sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP;
else
sc->sc_sync_peer.s_addr =
pfsyncr.pfsyncr_syncpeer.s_addr;
if (pfsyncr.pfsyncr_maxupdates > 255)
return (EINVAL);
sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates;
sc->sc_defer = pfsyncr.pfsyncr_defer;
if (pfsyncr.pfsyncr_syncdev[0] == 0) {
if ((ifp0 = if_get(sc->sc_sync_ifidx)) != NULL) {
if_linkstatehook_del(ifp0, &sc->sc_ltask);
if_detachhook_del(ifp0, &sc->sc_dtask);
}
if_put(ifp0);
sc->sc_sync_ifidx = 0;
if (imo->imo_num_memberships > 0) {
in_delmulti(imo->imo_membership[
--imo->imo_num_memberships]);
imo->imo_ifidx = 0;
}
break;
}
if ((sifp = if_unit(pfsyncr.pfsyncr_syncdev)) == NULL)
return (EINVAL);
ifp0 = if_get(sc->sc_sync_ifidx);
if (sifp->if_mtu < sc->sc_if.if_mtu || (ifp0 != NULL &&
sifp->if_mtu < ifp0->if_mtu) ||
sifp->if_mtu < MCLBYTES - sizeof(struct ip))
pfsync_sendout();
if (ifp0) {
if_linkstatehook_del(ifp0, &sc->sc_ltask);
if_detachhook_del(ifp0, &sc->sc_dtask);
}
if_put(ifp0);
sc->sc_sync_ifidx = sifp->if_index;
if (imo->imo_num_memberships > 0) {
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
imo->imo_ifidx = 0;
}
if (sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) {
struct in_addr addr;
if (!(sifp->if_flags & IFF_MULTICAST)) {
sc->sc_sync_ifidx = 0;
if_put(sifp);
return (EADDRNOTAVAIL);
}
addr.s_addr = INADDR_PFSYNC_GROUP;
if ((imo->imo_membership[0] =
in_addmulti(&addr, sifp)) == NULL) {
sc->sc_sync_ifidx = 0;
if_put(sifp);
return (ENOBUFS);
}
imo->imo_num_memberships++;
imo->imo_ifidx = sc->sc_sync_ifidx;
imo->imo_ttl = PFSYNC_DFLTTL;
imo->imo_loop = 0;
}
ip = &sc->sc_template;
bzero(ip, sizeof(*ip));
ip->ip_v = IPVERSION;
ip->ip_hl = sizeof(sc->sc_template) >> 2;
ip->ip_tos = IPTOS_LOWDELAY;
/* len and id are set later */
ip->ip_off = htons(IP_DF);
ip->ip_ttl = PFSYNC_DFLTTL;
ip->ip_p = IPPROTO_PFSYNC;
ip->ip_src.s_addr = INADDR_ANY;
ip->ip_dst.s_addr = sc->sc_sync_peer.s_addr;
if_linkstatehook_add(sifp, &sc->sc_ltask);
if_detachhook_add(sifp, &sc->sc_dtask);
if_put(sifp);
pfsync_request_full_update(sc);
break;
default:
return (ENOTTY);
}
return (0);
}
void
pfsync_out_state(struct pf_state *st, void *buf)
{
struct pfsync_state *sp = buf;
pfsync_state_export(sp, st);
}
void
pfsync_out_iack(struct pf_state *st, void *buf)
{
struct pfsync_ins_ack *iack = buf;
iack->id = st->id;
iack->creatorid = st->creatorid;
}
void
pfsync_out_upd_c(struct pf_state *st, void *buf)
{
struct pfsync_upd_c *up = buf;
bzero(up, sizeof(*up));
up->id = st->id;
pf_state_peer_hton(&st->src, &up->src);
pf_state_peer_hton(&st->dst, &up->dst);
up->creatorid = st->creatorid;
up->timeout = st->timeout;
}
void
pfsync_out_del(struct pf_state *st, void *buf)
{
struct pfsync_del_c *dp = buf;
dp->id = st->id;
dp->creatorid = st->creatorid;
SET(st->state_flags, PFSTATE_NOSYNC);
}
void
pfsync_grab_snapshot(struct pfsync_snapshot *sn, struct pfsync_softc *sc)
{
int q;
struct pf_state *st;
struct pfsync_upd_req_item *ur;
struct tdb *tdb;
sn->sn_sc = sc;
mtx_enter(&sc->sc_st_mtx);
mtx_enter(&sc->sc_upd_req_mtx);
mtx_enter(&sc->sc_tdb_mtx);
for (q = 0; q < PFSYNC_S_COUNT; q++) {
TAILQ_INIT(&sn->sn_qs[q]);
while ((st = TAILQ_FIRST(&sc->sc_qs[q])) != NULL) {
KASSERT(st->snapped == 0);
TAILQ_REMOVE(&sc->sc_qs[q], st, sync_list);
TAILQ_INSERT_TAIL(&sn->sn_qs[q], st, sync_snap);
st->snapped = 1;
}
}
TAILQ_INIT(&sn->sn_upd_req_list);
while ((ur = TAILQ_FIRST(&sc->sc_upd_req_list)) != NULL) {
TAILQ_REMOVE(&sc->sc_upd_req_list, ur, ur_entry);
TAILQ_INSERT_TAIL(&sn->sn_upd_req_list, ur, ur_snap);
}
TAILQ_INIT(&sn->sn_tdb_q);
while ((tdb = TAILQ_FIRST(&sc->sc_tdb_q)) != NULL) {
TAILQ_REMOVE(&sc->sc_tdb_q, tdb, tdb_sync_entry);
TAILQ_INSERT_TAIL(&sn->sn_tdb_q, tdb, tdb_sync_snap);
mtx_enter(&tdb->tdb_mtx);
KASSERT(!ISSET(tdb->tdb_flags, TDBF_PFSYNC_SNAPPED));
SET(tdb->tdb_flags, TDBF_PFSYNC_SNAPPED);
mtx_leave(&tdb->tdb_mtx);
}
sn->sn_len = sc->sc_len;
sc->sc_len = PFSYNC_MINPKT;
sn->sn_plus = sc->sc_plus;
sc->sc_plus = NULL;
sn->sn_pluslen = sc->sc_pluslen;
sc->sc_pluslen = 0;
mtx_leave(&sc->sc_tdb_mtx);
mtx_leave(&sc->sc_upd_req_mtx);
mtx_leave(&sc->sc_st_mtx);
}
void
pfsync_drop_snapshot(struct pfsync_snapshot *sn)
{
struct pf_state *st;
struct pfsync_upd_req_item *ur;
struct tdb *t;
int q;
for (q = 0; q < PFSYNC_S_COUNT; q++) {
if (TAILQ_EMPTY(&sn->sn_qs[q]))
continue;
while ((st = TAILQ_FIRST(&sn->sn_qs[q])) != NULL) {
KASSERT(st->sync_state == q);
KASSERT(st->snapped == 1);
TAILQ_REMOVE(&sn->sn_qs[q], st, sync_snap);
st->sync_state = PFSYNC_S_NONE;
st->snapped = 0;
pf_state_unref(st);
}
}
while ((ur = TAILQ_FIRST(&sn->sn_upd_req_list)) != NULL) {
TAILQ_REMOVE(&sn->sn_upd_req_list, ur, ur_snap);
pool_put(&sn->sn_sc->sc_pool, ur);
}
while ((t = TAILQ_FIRST(&sn->sn_tdb_q)) != NULL) {
TAILQ_REMOVE(&sn->sn_tdb_q, t, tdb_sync_snap);
mtx_enter(&t->tdb_mtx);
KASSERT(ISSET(t->tdb_flags, TDBF_PFSYNC_SNAPPED));
CLR(t->tdb_flags, TDBF_PFSYNC_SNAPPED);
CLR(t->tdb_flags, TDBF_PFSYNC);
mtx_leave(&t->tdb_mtx);
}
}
int
pfsync_is_snapshot_empty(struct pfsync_snapshot *sn)
{
int q;
for (q = 0; q < PFSYNC_S_COUNT; q++)
if (!TAILQ_EMPTY(&sn->sn_qs[q]))
return (0);
if (!TAILQ_EMPTY(&sn->sn_upd_req_list))
return (0);
if (!TAILQ_EMPTY(&sn->sn_tdb_q))
return (0);
return (sn->sn_plus == NULL);
}
void
pfsync_drop(struct pfsync_softc *sc)
{
struct pfsync_snapshot sn;
pfsync_grab_snapshot(&sn, sc);
pfsync_drop_snapshot(&sn);
}
void
pfsync_send_dispatch(void *xmq)
{
struct mbuf_queue *mq = xmq;
struct pfsync_softc *sc;
struct mbuf *m;
struct mbuf_list ml;
int error;
mq_delist(mq, &ml);
if (ml_empty(&ml))
return;
NET_LOCK();
sc = pfsyncif;
if (sc == NULL) {
ml_purge(&ml);
goto done;
}
while ((m = ml_dequeue(&ml)) != NULL) {
if ((error = ip_output(m, NULL, NULL, IP_RAWOUTPUT,
&sc->sc_imo, NULL, 0)) == 0)
pfsyncstat_inc(pfsyncs_opackets);
else {
DPFPRINTF(LOG_DEBUG,
"ip_output() @ %s failed (%d)\n", __func__, error);
pfsyncstat_inc(pfsyncs_oerrors);
}
}
done:
NET_UNLOCK();
}
void
pfsync_send_pkt(struct mbuf *m)
{
if (mq_enqueue(&pfsync_mq, m) != 0) {
pfsyncstat_inc(pfsyncs_oerrors);
DPFPRINTF(LOG_DEBUG, "mq_enqueue() @ %s failed, queue full\n",
__func__);
} else
task_add(net_tq(0), &pfsync_task);
}
void
pfsync_sendout(void)
{
struct pfsync_snapshot sn;
struct pfsync_softc *sc = pfsyncif;
#if NBPFILTER > 0
struct ifnet *ifp = &sc->sc_if;
#endif
struct mbuf *m;
struct ip *ip;
struct pfsync_header *ph;
struct pfsync_subheader *subh;
struct pf_state *st;
struct pfsync_upd_req_item *ur;
struct tdb *t;
int offset;
int q, count = 0;
if (sc == NULL || sc->sc_len == PFSYNC_MINPKT)
return;
if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
#if NBPFILTER > 0
(ifp->if_bpf == NULL && sc->sc_sync_ifidx == 0)) {
#else
sc->sc_sync_ifidx == 0) {
#endif
pfsync_drop(sc);
return;
}
pfsync_grab_snapshot(&sn, sc);
/*
* Check below is sufficient to prevent us from sending empty packets,
* but it does not stop us from sending short packets.
*/
if (pfsync_is_snapshot_empty(&sn))
return;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
sc->sc_if.if_oerrors++;
pfsyncstat_inc(pfsyncs_onomem);
pfsync_drop_snapshot(&sn);
return;
}
if (max_linkhdr + sn.sn_len > MHLEN) {
MCLGETL(m, M_DONTWAIT, max_linkhdr + sn.sn_len);
if (!ISSET(m->m_flags, M_EXT)) {
m_free(m);
sc->sc_if.if_oerrors++;
pfsyncstat_inc(pfsyncs_onomem);
pfsync_drop_snapshot(&sn);
return;
}
}
m->m_data += max_linkhdr;
m->m_len = m->m_pkthdr.len = sn.sn_len;
/* build the ip header */
ip = mtod(m, struct ip *);
bcopy(&sc->sc_template, ip, sizeof(*ip));
offset = sizeof(*ip);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_id = htons(ip_randomid());
/* build the pfsync header */
ph = (struct pfsync_header *)(m->m_data + offset);
bzero(ph, sizeof(*ph));
offset += sizeof(*ph);
ph->version = PFSYNC_VERSION;
ph->len = htons(sn.sn_len - sizeof(*ip));
bcopy(pf_status.pf_chksum, ph->pfcksum, PF_MD5_DIGEST_LENGTH);
if (!TAILQ_EMPTY(&sn.sn_upd_req_list)) {
subh = (struct pfsync_subheader *)(m->m_data + offset);
offset += sizeof(*subh);
count = 0;
while ((ur = TAILQ_FIRST(&sn.sn_upd_req_list)) != NULL) {
TAILQ_REMOVE(&sn.sn_upd_req_list, ur, ur_snap);
bcopy(&ur->ur_msg, m->m_data + offset,
sizeof(ur->ur_msg));
offset += sizeof(ur->ur_msg);
pool_put(&sc->sc_pool, ur);
count++;
}
bzero(subh, sizeof(*subh));
subh->len = sizeof(ur->ur_msg) >> 2;
subh->action = PFSYNC_ACT_UPD_REQ;
subh->count = htons(count);
}
/* has someone built a custom region for us to add? */
if (sn.sn_plus != NULL) {
bcopy(sn.sn_plus, m->m_data + offset, sn.sn_pluslen);
offset += sn.sn_pluslen;
sn.sn_plus = NULL; /* XXX memory leak ? */
}
if (!TAILQ_EMPTY(&sn.sn_tdb_q)) {
subh = (struct pfsync_subheader *)(m->m_data + offset);
offset += sizeof(*subh);
count = 0;
while ((t = TAILQ_FIRST(&sn.sn_tdb_q)) != NULL) {
TAILQ_REMOVE(&sn.sn_tdb_q, t, tdb_sync_snap);
pfsync_out_tdb(t, m->m_data + offset);
offset += sizeof(struct pfsync_tdb);
mtx_enter(&t->tdb_mtx);
KASSERT(ISSET(t->tdb_flags, TDBF_PFSYNC_SNAPPED));
CLR(t->tdb_flags, TDBF_PFSYNC_SNAPPED);
CLR(t->tdb_flags, TDBF_PFSYNC);
mtx_leave(&t->tdb_mtx);
tdb_unref(t);
count++;
}
bzero(subh, sizeof(*subh));
subh->action = PFSYNC_ACT_TDB;
subh->len = sizeof(struct pfsync_tdb) >> 2;
subh->count = htons(count);
}
/* walk the queues */
for (q = 0; q < PFSYNC_S_COUNT; q++) {
if (TAILQ_EMPTY(&sn.sn_qs[q]))
continue;
subh = (struct pfsync_subheader *)(m->m_data + offset);
offset += sizeof(*subh);
count = 0;
while ((st = TAILQ_FIRST(&sn.sn_qs[q])) != NULL) {
TAILQ_REMOVE(&sn.sn_qs[q], st, sync_snap);
KASSERT(st->sync_state == q);
KASSERT(st->snapped == 1);
st->sync_state = PFSYNC_S_NONE;
st->snapped = 0;
pfsync_qs[q].write(st, m->m_data + offset);
offset += pfsync_qs[q].len;
pf_state_unref(st);
count++;
}
bzero(subh, sizeof(*subh));
subh->action = pfsync_qs[q].action;
subh->len = pfsync_qs[q].len >> 2;
subh->count = htons(count);
}
/* we're done, let's put it on the wire */
#if NBPFILTER > 0
if (ifp->if_bpf) {
m->m_data += sizeof(*ip);
m->m_len = m->m_pkthdr.len = sn.sn_len - sizeof(*ip);
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
m->m_data -= sizeof(*ip);
m->m_len = m->m_pkthdr.len = sn.sn_len;
}
if (sc->sc_sync_ifidx == 0) {
sc->sc_len = PFSYNC_MINPKT;
m_freem(m);
return;
}
#endif
sc->sc_if.if_opackets++;
sc->sc_if.if_obytes += m->m_pkthdr.len;
m->m_pkthdr.ph_rtableid = sc->sc_if.if_rdomain;
pfsync_send_pkt(m);
}
void
pfsync_insert_state(struct pf_state *st)
{
struct pfsync_softc *sc = pfsyncif;
NET_ASSERT_LOCKED();
if (ISSET(st->rule.ptr->rule_flag, PFRULE_NOSYNC) ||
st->key[PF_SK_WIRE]->proto == IPPROTO_PFSYNC) {
SET(st->state_flags, PFSTATE_NOSYNC);
return;
}
if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING) ||
ISSET(st->state_flags, PFSTATE_NOSYNC))
return;
KASSERT(st->sync_state == PFSYNC_S_NONE);
if (sc->sc_len == PFSYNC_MINPKT)
timeout_add_sec(&sc->sc_tmo, 1);
pfsync_q_ins(st, PFSYNC_S_INS);
st->sync_updates = 0;
}
int
pfsync_defer(struct pf_state *st, struct mbuf *m, struct pfsync_deferral **ppd)
{
struct pfsync_softc *sc = pfsyncif;
struct pfsync_deferral *pd;
unsigned int sched;
NET_ASSERT_LOCKED();
if (!sc->sc_defer ||
ISSET(st->state_flags, PFSTATE_NOSYNC) ||
m->m_flags & (M_BCAST|M_MCAST))
return (0);
pd = pool_get(&sc->sc_pool, M_NOWAIT);
if (pd == NULL)
return (0);
/*
* deferral queue grows faster, than timeout can consume,
* we have to ask packet (caller) to help timer and dispatch
* one deferral for us.
*
* We wish to call pfsync_undefer() here. Unfortunately we can't,
* because pfsync_undefer() will be calling to ip_output(),
* which in turn will call to pf_test(), which would then attempt
* to grab PF_LOCK() we currently hold.
*/
if (sc->sc_deferred >= 128) {
mtx_enter(&sc->sc_deferrals_mtx);
*ppd = TAILQ_FIRST(&sc->sc_deferrals);
if (*ppd != NULL) {
TAILQ_REMOVE(&sc->sc_deferrals, *ppd, pd_entry);
sc->sc_deferred--;
}
mtx_leave(&sc->sc_deferrals_mtx);
} else
*ppd = NULL;
m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
SET(st->state_flags, PFSTATE_ACK);
pd->pd_st = pf_state_ref(st);
pd->pd_m = m;
pd->pd_deadline = getnsecuptime() + PFSYNC_DEFER_NSEC;
mtx_enter(&sc->sc_deferrals_mtx);
sched = TAILQ_EMPTY(&sc->sc_deferrals);
TAILQ_INSERT_TAIL(&sc->sc_deferrals, pd, pd_entry);
sc->sc_deferred++;
mtx_leave(&sc->sc_deferrals_mtx);
if (sched)
timeout_add_nsec(&sc->sc_deferrals_tmo, PFSYNC_DEFER_NSEC);
schednetisr(NETISR_PFSYNC);
return (1);
}
void
pfsync_undefer_notify(struct pfsync_deferral *pd)
{
struct pf_pdesc pdesc;
struct pf_state *st = pd->pd_st;
/*
* pf_remove_state removes the state keys and sets st->timeout
* to PFTM_UNLINKED. this is done under NET_LOCK which should
* be held here, so we can use PFTM_UNLINKED as a test for
* whether the state keys are set for the address family
* lookup.
*/
if (st->timeout == PFTM_UNLINKED)
return;
if (st->rt == PF_ROUTETO) {
if (pf_setup_pdesc(&pdesc, st->key[PF_SK_WIRE]->af,
st->direction, st->kif, pd->pd_m, NULL) != PF_PASS)
return;
switch (st->key[PF_SK_WIRE]->af) {
case AF_INET:
pf_route(&pdesc, st);
break;
#ifdef INET6
case AF_INET6:
pf_route6(&pdesc, st);
break;
#endif /* INET6 */
default:
unhandled_af(st->key[PF_SK_WIRE]->af);
}
pd->pd_m = pdesc.m;
} else {
switch (st->key[PF_SK_WIRE]->af) {
case AF_INET:
ip_output(pd->pd_m, NULL, NULL, 0, NULL, NULL, 0);
break;
#ifdef INET6
case AF_INET6:
ip6_output(pd->pd_m, NULL, NULL, 0, NULL, NULL);
break;
#endif /* INET6 */
default:
unhandled_af(st->key[PF_SK_WIRE]->af);
}
pd->pd_m = NULL;
}
}
void
pfsync_free_deferral(struct pfsync_deferral *pd)
{
struct pfsync_softc *sc = pfsyncif;
pf_state_unref(pd->pd_st);
m_freem(pd->pd_m);
pool_put(&sc->sc_pool, pd);
}
void
pfsync_undefer(struct pfsync_deferral *pd, int drop)
{
struct pfsync_softc *sc = pfsyncif;
NET_ASSERT_LOCKED();
if (sc == NULL)
return;
CLR(pd->pd_st->state_flags, PFSTATE_ACK);
if (!drop)
pfsync_undefer_notify(pd);
pfsync_free_deferral(pd);
}
void
pfsync_deferrals_tmo(void *arg)
{
struct pfsync_softc *sc = arg;
struct pfsync_deferral *pd;
uint64_t now, nsec = 0;
struct pfsync_deferrals pds = TAILQ_HEAD_INITIALIZER(pds);
now = getnsecuptime();
mtx_enter(&sc->sc_deferrals_mtx);
for (;;) {
pd = TAILQ_FIRST(&sc->sc_deferrals);
if (pd == NULL)
break;
if (now < pd->pd_deadline) {
nsec = pd->pd_deadline - now;
break;
}
TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry);
sc->sc_deferred--;
TAILQ_INSERT_TAIL(&pds, pd, pd_entry);
}
mtx_leave(&sc->sc_deferrals_mtx);
if (nsec > 0) {
/* we were looking at a pd, but it wasn't old enough */
timeout_add_nsec(&sc->sc_deferrals_tmo, nsec);
}
if (TAILQ_EMPTY(&pds))
return;
NET_LOCK();
while ((pd = TAILQ_FIRST(&pds)) != NULL) {
TAILQ_REMOVE(&pds, pd, pd_entry);
pfsync_undefer(pd, 0);
}
NET_UNLOCK();
}
void
pfsync_deferred(struct pf_state *st, int drop)
{
struct pfsync_softc *sc = pfsyncif;
struct pfsync_deferral *pd;
NET_ASSERT_LOCKED();
mtx_enter(&sc->sc_deferrals_mtx);
TAILQ_FOREACH(pd, &sc->sc_deferrals, pd_entry) {
if (pd->pd_st == st) {
TAILQ_REMOVE(&sc->sc_deferrals, pd, pd_entry);
sc->sc_deferred--;
break;
}
}
mtx_leave(&sc->sc_deferrals_mtx);
if (pd != NULL)
pfsync_undefer(pd, drop);
}
void
pfsync_update_state(struct pf_state *st)
{
struct pfsync_softc *sc = pfsyncif;
int sync = 0;
NET_ASSERT_LOCKED();
if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING))
return;
if (ISSET(st->state_flags, PFSTATE_ACK))
pfsync_deferred(st, 0);
if (ISSET(st->state_flags, PFSTATE_NOSYNC)) {
if (st->sync_state != PFSYNC_S_NONE)
pfsync_q_del(st);
return;
}
if (sc->sc_len == PFSYNC_MINPKT)
timeout_add_sec(&sc->sc_tmo, 1);
switch (st->sync_state) {
case PFSYNC_S_UPD_C:
case PFSYNC_S_UPD:
case PFSYNC_S_INS:
/* we're already handling it */
if (st->key[PF_SK_WIRE]->proto == IPPROTO_TCP) {
st->sync_updates++;
if (st->sync_updates >= sc->sc_maxupdates)
sync = 1;
}
break;
case PFSYNC_S_IACK:
pfsync_q_del(st);
case PFSYNC_S_NONE:
pfsync_q_ins(st, PFSYNC_S_UPD_C);
st->sync_updates = 0;
break;
default:
panic("pfsync_update_state: unexpected sync state %d",
st->sync_state);
}
if (sync || (getuptime() - st->pfsync_time) < 2)
schednetisr(NETISR_PFSYNC);
}
void
pfsync_cancel_full_update(struct pfsync_softc *sc)
{
if (timeout_pending(&sc->sc_bulkfail_tmo) ||
timeout_pending(&sc->sc_bulk_tmo)) {
#if NCARP > 0
if (!pfsync_sync_ok)
carp_group_demote_adj(&sc->sc_if, -1,
"pfsync bulk cancelled");
if (sc->sc_initial_bulk) {
carp_group_demote_adj(&sc->sc_if, -32,
"pfsync init");
sc->sc_initial_bulk = 0;
}
#endif
pfsync_sync_ok = 1;
DPFPRINTF(LOG_INFO, "cancelling bulk update");
}
timeout_del(&sc->sc_bulkfail_tmo);
timeout_del(&sc->sc_bulk_tmo);
sc->sc_bulk_next = NULL;
sc->sc_bulk_last = NULL;
sc->sc_ureq_sent = 0;
sc->sc_bulk_tries = 0;
}
void
pfsync_request_full_update(struct pfsync_softc *sc)
{
if (sc->sc_sync_ifidx != 0 && ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
/* Request a full state table update. */
sc->sc_ureq_sent = getuptime();
#if NCARP > 0
if (!sc->sc_link_demoted && pfsync_sync_ok)
carp_group_demote_adj(&sc->sc_if, 1,
"pfsync bulk start");
#endif
pfsync_sync_ok = 0;
DPFPRINTF(LOG_INFO, "requesting bulk update");
timeout_add(&sc->sc_bulkfail_tmo, 4 * hz +
pf_pool_limits[PF_LIMIT_STATES].limit /
((sc->sc_if.if_mtu - PFSYNC_MINPKT) /
sizeof(struct pfsync_state)));
pfsync_request_update(0, 0);
}
}
void
pfsync_request_update(u_int32_t creatorid, u_int64_t id)
{
struct pfsync_softc *sc = pfsyncif;
struct pfsync_upd_req_item *item;
size_t nlen, sclen;
int retry;
/*
* this code does nothing to prevent multiple update requests for the
* same state being generated.
*/
item = pool_get(&sc->sc_pool, PR_NOWAIT);
if (item == NULL) {
/* XXX stats */
return;
}
item->ur_msg.id = id;
item->ur_msg.creatorid = creatorid;
for (;;) {
mtx_enter(&sc->sc_upd_req_mtx);
nlen = sizeof(struct pfsync_upd_req);
if (TAILQ_EMPTY(&sc->sc_upd_req_list))
nlen += sizeof(struct pfsync_subheader);
sclen = atomic_add_long_nv(&sc->sc_len, nlen);
retry = (sclen > sc->sc_if.if_mtu);
if (retry)
atomic_sub_long(&sc->sc_len, nlen);
else
TAILQ_INSERT_TAIL(&sc->sc_upd_req_list, item, ur_entry);
mtx_leave(&sc->sc_upd_req_mtx);
if (!retry)
break;
pfsync_sendout();
}
schednetisr(NETISR_PFSYNC);
}
void
pfsync_update_state_req(struct pf_state *st)
{
struct pfsync_softc *sc = pfsyncif;
if (sc == NULL)
panic("pfsync_update_state_req: nonexistent instance");
if (ISSET(st->state_flags, PFSTATE_NOSYNC)) {
if (st->sync_state != PFSYNC_S_NONE)
pfsync_q_del(st);
return;
}
switch (st->sync_state) {
case PFSYNC_S_UPD_C:
case PFSYNC_S_IACK:
pfsync_q_del(st);
case PFSYNC_S_NONE:
pfsync_q_ins(st, PFSYNC_S_UPD);
schednetisr(NETISR_PFSYNC);
return;
case PFSYNC_S_INS:
case PFSYNC_S_UPD:
case PFSYNC_S_DEL:
/* we're already handling it */
return;
default:
panic("pfsync_update_state_req: unexpected sync state %d",
st->sync_state);
}
}
void
pfsync_delete_state(struct pf_state *st)
{
struct pfsync_softc *sc = pfsyncif;
NET_ASSERT_LOCKED();
if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING))
return;
if (ISSET(st->state_flags, PFSTATE_ACK))
pfsync_deferred(st, 1);
if (ISSET(st->state_flags, PFSTATE_NOSYNC)) {
if (st->sync_state != PFSYNC_S_NONE)
pfsync_q_del(st);
return;
}
if (sc->sc_len == PFSYNC_MINPKT)
timeout_add_sec(&sc->sc_tmo, 1);
switch (st->sync_state) {
case PFSYNC_S_INS:
/* we never got to tell the world so just forget about it */
pfsync_q_del(st);
return;
case PFSYNC_S_UPD_C:
case PFSYNC_S_UPD:
case PFSYNC_S_IACK:
pfsync_q_del(st);
/*
* FALLTHROUGH to putting it on the del list
* Note on reference count bookkeeping:
* pfsync_q_del() drops reference for queue
* ownership. But the st entry survives, because
* our caller still holds a reference.
*/
case PFSYNC_S_NONE:
/*
* We either fall through here, or there is no reference to
* st owned by pfsync queues at this point.
*
* Calling pfsync_q_ins() puts st to del queue. The pfsync_q_ins()
* grabs a reference for delete queue.
*/
pfsync_q_ins(st, PFSYNC_S_DEL);
return;
default:
panic("pfsync_delete_state: unexpected sync state %d",
st->sync_state);
}
}
void
pfsync_clear_states(u_int32_t creatorid, const char *ifname)
{
struct pfsync_softc *sc = pfsyncif;
struct {
struct pfsync_subheader subh;
struct pfsync_clr clr;
} __packed r;
NET_ASSERT_LOCKED();
if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING))
return;
bzero(&r, sizeof(r));
r.subh.action = PFSYNC_ACT_CLR;
r.subh.len = sizeof(struct pfsync_clr) >> 2;
r.subh.count = htons(1);
strlcpy(r.clr.ifname, ifname, sizeof(r.clr.ifname));
r.clr.creatorid = creatorid;
pfsync_send_plus(&r, sizeof(r));
}
void
pfsync_q_ins(struct pf_state *st, int q)
{
struct pfsync_softc *sc = pfsyncif;
size_t nlen, sclen;
if (sc->sc_len < PFSYNC_MINPKT)
panic("pfsync pkt len is too low %zd", sc->sc_len);
do {
mtx_enter(&sc->sc_st_mtx);
/*
* There are either two threads trying to update the
* the same state, or the state is just being processed
* (is on snapshot queue).
*/
if (st->sync_state != PFSYNC_S_NONE) {
mtx_leave(&sc->sc_st_mtx);
break;
}
nlen = pfsync_qs[q].len;
if (TAILQ_EMPTY(&sc->sc_qs[q]))
nlen += sizeof(struct pfsync_subheader);
sclen = atomic_add_long_nv(&sc->sc_len, nlen);
if (sclen > sc->sc_if.if_mtu) {
atomic_sub_long(&sc->sc_len, nlen);
mtx_leave(&sc->sc_st_mtx);
pfsync_sendout();
continue;
}
pf_state_ref(st);
TAILQ_INSERT_TAIL(&sc->sc_qs[q], st, sync_list);
st->sync_state = q;
mtx_leave(&sc->sc_st_mtx);
} while (0);
}
void
pfsync_q_del(struct pf_state *st)
{
struct pfsync_softc *sc = pfsyncif;
int q;
KASSERT(st->sync_state != PFSYNC_S_NONE);
mtx_enter(&sc->sc_st_mtx);
q = st->sync_state;
/*
* re-check under mutex
* if state is snapped already, then just bail out, because we came
* too late, the state is being just processed/dispatched to peer.
*/
if ((q == PFSYNC_S_NONE) || (st->snapped)) {
mtx_leave(&sc->sc_st_mtx);
return;
}
atomic_sub_long(&sc->sc_len, pfsync_qs[q].len);
TAILQ_REMOVE(&sc->sc_qs[q], st, sync_list);
if (TAILQ_EMPTY(&sc->sc_qs[q]))
atomic_sub_long(&sc->sc_len, sizeof (struct pfsync_subheader));
st->sync_state = PFSYNC_S_NONE;
mtx_leave(&sc->sc_st_mtx);
pf_state_unref(st);
}
void
pfsync_update_tdb(struct tdb *t, int output)
{
struct pfsync_softc *sc = pfsyncif;
size_t nlen, sclen;
if (sc == NULL)
return;
if (!ISSET(t->tdb_flags, TDBF_PFSYNC)) {
do {
mtx_enter(&sc->sc_tdb_mtx);
nlen = sizeof(struct pfsync_tdb);
mtx_enter(&t->tdb_mtx);
if (ISSET(t->tdb_flags, TDBF_PFSYNC)) {
/* we've lost race, no action for us then */
mtx_leave(&t->tdb_mtx);
mtx_leave(&sc->sc_tdb_mtx);
break;
}
if (TAILQ_EMPTY(&sc->sc_tdb_q))
nlen += sizeof(struct pfsync_subheader);
sclen = atomic_add_long_nv(&sc->sc_len, nlen);
if (sclen > sc->sc_if.if_mtu) {
atomic_sub_long(&sc->sc_len, nlen);
mtx_leave(&t->tdb_mtx);
mtx_leave(&sc->sc_tdb_mtx);
pfsync_sendout();
continue;
}
TAILQ_INSERT_TAIL(&sc->sc_tdb_q, t, tdb_sync_entry);
tdb_ref(t);
SET(t->tdb_flags, TDBF_PFSYNC);
mtx_leave(&t->tdb_mtx);
mtx_leave(&sc->sc_tdb_mtx);
t->tdb_updates = 0;
} while (0);
} else {
if (++t->tdb_updates >= sc->sc_maxupdates)
schednetisr(NETISR_PFSYNC);
}
mtx_enter(&t->tdb_mtx);
if (output)
SET(t->tdb_flags, TDBF_PFSYNC_RPL);
else
CLR(t->tdb_flags, TDBF_PFSYNC_RPL);
mtx_leave(&t->tdb_mtx);
}
void
pfsync_delete_tdb(struct tdb *t)
{
struct pfsync_softc *sc = pfsyncif;
size_t nlen;
if (sc == NULL || !ISSET(t->tdb_flags, TDBF_PFSYNC))
return;
mtx_enter(&sc->sc_tdb_mtx);
/*
* if tdb entry is just being processed (found in snapshot),
* then it can not be deleted. we just came too late
*/
if (ISSET(t->tdb_flags, TDBF_PFSYNC_SNAPPED)) {
mtx_leave(&sc->sc_tdb_mtx);
return;
}
TAILQ_REMOVE(&sc->sc_tdb_q, t, tdb_sync_entry);
mtx_enter(&t->tdb_mtx);
CLR(t->tdb_flags, TDBF_PFSYNC);
mtx_leave(&t->tdb_mtx);
nlen = sizeof(struct pfsync_tdb);
if (TAILQ_EMPTY(&sc->sc_tdb_q))
nlen += sizeof(struct pfsync_subheader);
atomic_sub_long(&sc->sc_len, nlen);
mtx_leave(&sc->sc_tdb_mtx);
tdb_unref(t);
}
void
pfsync_out_tdb(struct tdb *t, void *buf)
{
struct pfsync_tdb *ut = buf;
bzero(ut, sizeof(*ut));
ut->spi = t->tdb_spi;
bcopy(&t->tdb_dst, &ut->dst, sizeof(ut->dst));
/*
* When a failover happens, the master's rpl is probably above
* what we see here (we may be up to a second late), so
* increase it a bit for outbound tdbs to manage most such
* situations.
*
* For now, just add an offset that is likely to be larger
* than the number of packets we can see in one second. The RFC
* just says the next packet must have a higher seq value.
*
* XXX What is a good algorithm for this? We could use
* a rate-determined increase, but to know it, we would have
* to extend struct tdb.
* XXX pt->rpl can wrap over MAXINT, but if so the real tdb
* will soon be replaced anyway. For now, just don't handle
* this edge case.
*/
#define RPL_INCR 16384
ut->rpl = htobe64(t->tdb_rpl + (ISSET(t->tdb_flags, TDBF_PFSYNC_RPL) ?
RPL_INCR : 0));
ut->cur_bytes = htobe64(t->tdb_cur_bytes);
ut->sproto = t->tdb_sproto;
ut->rdomain = htons(t->tdb_rdomain);
}
void
pfsync_bulk_start(void)
{
struct pfsync_softc *sc = pfsyncif;
NET_ASSERT_LOCKED();
/*
* pf gc via pfsync_state_in_use reads sc_bulk_next and
* sc_bulk_last while exclusively holding the pf_state_list
* rwlock. make sure it can't race with us setting these
* pointers. they basically act as hazards, and borrow the
* lists state reference count.
*/
rw_enter_read(&pf_state_list.pfs_rwl);
/* get a consistent view of the list pointers */
mtx_enter(&pf_state_list.pfs_mtx);
if (sc->sc_bulk_next == NULL)
sc->sc_bulk_next = TAILQ_FIRST(&pf_state_list.pfs_list);
sc->sc_bulk_last = TAILQ_LAST(&pf_state_list.pfs_list, pf_state_queue);
mtx_leave(&pf_state_list.pfs_mtx);
rw_exit_read(&pf_state_list.pfs_rwl);
DPFPRINTF(LOG_INFO, "received bulk update request");
if (sc->sc_bulk_last == NULL)
pfsync_bulk_status(PFSYNC_BUS_END);
else {
sc->sc_ureq_received = getuptime();
pfsync_bulk_status(PFSYNC_BUS_START);
timeout_add(&sc->sc_bulk_tmo, 0);
}
}
void
pfsync_bulk_update(void *arg)
{
struct pfsync_softc *sc;
struct pf_state *st;
int i = 0;
NET_LOCK();
sc = pfsyncif;
if (sc == NULL)
goto out;
rw_enter_read(&pf_state_list.pfs_rwl);
st = sc->sc_bulk_next;
sc->sc_bulk_next = NULL;
for (;;) {
if (st->sync_state == PFSYNC_S_NONE &&
st->timeout < PFTM_MAX &&
st->pfsync_time <= sc->sc_ureq_received) {
pfsync_update_state_req(st);
i++;
}
st = TAILQ_NEXT(st, entry_list);
if ((st == NULL) || (st == sc->sc_bulk_last)) {
/* we're done */
sc->sc_bulk_last = NULL;
pfsync_bulk_status(PFSYNC_BUS_END);
break;
}
if (i > 1 && (sc->sc_if.if_mtu - sc->sc_len) <
sizeof(struct pfsync_state)) {
/* we've filled a packet */
sc->sc_bulk_next = st;
timeout_add(&sc->sc_bulk_tmo, 1);
break;
}
}
rw_exit_read(&pf_state_list.pfs_rwl);
out:
NET_UNLOCK();
}
void
pfsync_bulk_status(u_int8_t status)
{
struct {
struct pfsync_subheader subh;
struct pfsync_bus bus;
} __packed r;
struct pfsync_softc *sc = pfsyncif;
bzero(&r, sizeof(r));
r.subh.action = PFSYNC_ACT_BUS;
r.subh.len = sizeof(struct pfsync_bus) >> 2;
r.subh.count = htons(1);
r.bus.creatorid = pf_status.hostid;
r.bus.endtime = htonl(getuptime() - sc->sc_ureq_received);
r.bus.status = status;
pfsync_send_plus(&r, sizeof(r));
}
void
pfsync_bulk_fail(void *arg)
{
struct pfsync_softc *sc;
NET_LOCK();
sc = pfsyncif;
if (sc == NULL)
goto out;
if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) {
/* Try again */
timeout_add_sec(&sc->sc_bulkfail_tmo, 5);
pfsync_request_update(0, 0);
} else {
/* Pretend like the transfer was ok */
sc->sc_ureq_sent = 0;
sc->sc_bulk_tries = 0;
#if NCARP > 0
if (!pfsync_sync_ok)
carp_group_demote_adj(&sc->sc_if, -1,
sc->sc_link_demoted ?
"pfsync link state up" :
"pfsync bulk fail");
if (sc->sc_initial_bulk) {
carp_group_demote_adj(&sc->sc_if, -32,
"pfsync init");
sc->sc_initial_bulk = 0;
}
#endif
pfsync_sync_ok = 1;
sc->sc_link_demoted = 0;
DPFPRINTF(LOG_ERR, "failed to receive bulk update");
}
out:
NET_UNLOCK();
}
void
pfsync_send_plus(void *plus, size_t pluslen)
{
struct pfsync_softc *sc = pfsyncif;
if (sc->sc_len + pluslen > sc->sc_if.if_mtu)
pfsync_sendout();
sc->sc_plus = plus;
sc->sc_pluslen = pluslen;
atomic_add_long(&sc->sc_len, pluslen);
pfsync_sendout();
}
int
pfsync_up(void)
{
struct pfsync_softc *sc = pfsyncif;
if (sc == NULL || !ISSET(sc->sc_if.if_flags, IFF_RUNNING))
return (0);
return (1);
}
int
pfsync_state_in_use(struct pf_state *st)
{
struct pfsync_softc *sc = pfsyncif;
if (sc == NULL)
return (0);
rw_assert_wrlock(&pf_state_list.pfs_rwl);
if (st->sync_state != PFSYNC_S_NONE ||
st == sc->sc_bulk_next ||
st == sc->sc_bulk_last)
return (1);
return (0);
}
void
pfsync_timeout(void *arg)
{
NET_LOCK();
pfsync_sendout();
NET_UNLOCK();
}
/* this is a softnet/netisr handler */
void
pfsyncintr(void)
{
pfsync_sendout();
}
int
pfsync_sysctl_pfsyncstat(void *oldp, size_t *oldlenp, void *newp)
{
struct pfsyncstats pfsyncstat;
CTASSERT(sizeof(pfsyncstat) == (pfsyncs_ncounters * sizeof(uint64_t)));
memset(&pfsyncstat, 0, sizeof pfsyncstat);
counters_read(pfsynccounters, (uint64_t *)&pfsyncstat,
pfsyncs_ncounters);
return (sysctl_rdstruct(oldp, oldlenp, newp,
&pfsyncstat, sizeof(pfsyncstat)));
}
int
pfsync_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case PFSYNCCTL_STATS:
return (pfsync_sysctl_pfsyncstat(oldp, oldlenp, newp));
default:
return (ENOPROTOOPT);
}
}
1561
1563
1562
4
4
4
2
1792
1790
1787
4
1790
29
29
29
17
17
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/* $OpenBSD: kern_smr.c,v 1.16 2022/08/14 01:58:27 jsg Exp $ */
/*
* Copyright (c) 2019-2020 Visa Hankala
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kthread.h>
#include <sys/mutex.h>
#include <sys/percpu.h>
#include <sys/proc.h>
#include <sys/smr.h>
#include <sys/time.h>
#include <sys/tracepoint.h>
#include <sys/witness.h>
#include <machine/cpu.h>
#define SMR_PAUSE 100 /* pause between rounds in msec */
void smr_dispatch(struct schedstate_percpu *);
void smr_grace_wait(void);
void smr_thread(void *);
void smr_wakeup(void *);
struct mutex smr_lock = MUTEX_INITIALIZER(IPL_HIGH);
struct smr_entry_list smr_deferred;
struct timeout smr_wakeup_tmo;
unsigned int smr_expedite;
unsigned int smr_ndeferred;
unsigned char smr_grace_period;
#ifdef WITNESS
static const char smr_lock_name[] = "smr";
struct lock_object smr_lock_obj = {
.lo_name = smr_lock_name,
.lo_flags = LO_WITNESS | LO_INITIALIZED | LO_SLEEPABLE |
(LO_CLASS_RWLOCK << LO_CLASSSHIFT)
};
struct lock_type smr_lock_type = {
.lt_name = smr_lock_name
};
#endif
static inline int
smr_cpu_is_idle(struct cpu_info *ci)
{
return ci->ci_curproc == ci->ci_schedstate.spc_idleproc;
}
void
smr_startup(void)
{
SIMPLEQ_INIT(&smr_deferred);
WITNESS_INIT(&smr_lock_obj, &smr_lock_type);
timeout_set(&smr_wakeup_tmo, smr_wakeup, NULL);
}
void
smr_startup_thread(void)
{
if (kthread_create(smr_thread, NULL, NULL, "smr") != 0)
panic("could not create smr thread");
}
struct timeval smr_logintvl = { 300, 0 };
void
smr_thread(void *arg)
{
struct timeval elapsed, end, loglast, start;
struct smr_entry_list deferred;
struct smr_entry *smr;
unsigned long count;
KERNEL_ASSERT_LOCKED();
KERNEL_UNLOCK();
memset(&loglast, 0, sizeof(loglast));
SIMPLEQ_INIT(&deferred);
for (;;) {
mtx_enter(&smr_lock);
if (smr_ndeferred == 0) {
while (smr_ndeferred == 0)
msleep_nsec(&smr_ndeferred, &smr_lock, PVM,
"bored", INFSLP);
} else {
if (smr_expedite == 0)
msleep_nsec(&smr_ndeferred, &smr_lock, PVM,
"pause", MSEC_TO_NSEC(SMR_PAUSE));
}
SIMPLEQ_CONCAT(&deferred, &smr_deferred);
smr_ndeferred = 0;
smr_expedite = 0;
mtx_leave(&smr_lock);
getmicrouptime(&start);
smr_grace_wait();
WITNESS_CHECKORDER(&smr_lock_obj, LOP_NEWORDER, NULL);
WITNESS_LOCK(&smr_lock_obj, 0);
count = 0;
while ((smr = SIMPLEQ_FIRST(&deferred)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&deferred, smr_list);
TRACEPOINT(smr, called, smr->smr_func, smr->smr_arg);
smr->smr_func(smr->smr_arg);
count++;
}
WITNESS_UNLOCK(&smr_lock_obj, 0);
getmicrouptime(&end);
timersub(&end, &start, &elapsed);
if (elapsed.tv_sec >= 2 &&
ratecheck(&loglast, &smr_logintvl)) {
printf("smr: dispatch took %ld.%06lds\n",
(long)elapsed.tv_sec,
(long)elapsed.tv_usec);
}
TRACEPOINT(smr, thread, TIMEVAL_TO_NSEC(&elapsed), count);
}
}
/*
* Announce next grace period and wait until all CPUs have entered it
* by crossing quiescent state.
*/
void
smr_grace_wait(void)
{
#ifdef MULTIPROCESSOR
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
unsigned char smrgp;
smrgp = READ_ONCE(smr_grace_period) + 1;
WRITE_ONCE(smr_grace_period, smrgp);
curcpu()->ci_schedstate.spc_smrgp = smrgp;
CPU_INFO_FOREACH(cii, ci) {
if (!CPU_IS_RUNNING(ci))
continue;
if (READ_ONCE(ci->ci_schedstate.spc_smrgp) == smrgp)
continue;
sched_peg_curproc(ci);
KASSERT(ci->ci_schedstate.spc_smrgp == smrgp);
}
atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
#endif /* MULTIPROCESSOR */
}
void
smr_wakeup(void *arg)
{
TRACEPOINT(smr, wakeup, NULL);
wakeup(&smr_ndeferred);
}
void
smr_read_enter(void)
{
#ifdef DIAGNOSTIC
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
spc->spc_smrdepth++;
#endif
}
void
smr_read_leave(void)
{
#ifdef DIAGNOSTIC
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
KASSERT(spc->spc_smrdepth > 0);
spc->spc_smrdepth--;
#endif
}
/*
* Move SMR entries from the local queue to the system-wide queue.
*/
void
smr_dispatch(struct schedstate_percpu *spc)
{
int expedite = 0, wake = 0;
mtx_enter(&smr_lock);
if (smr_ndeferred == 0)
wake = 1;
SIMPLEQ_CONCAT(&smr_deferred, &spc->spc_deferred);
smr_ndeferred += spc->spc_ndeferred;
spc->spc_ndeferred = 0;
smr_expedite |= spc->spc_smrexpedite;
spc->spc_smrexpedite = 0;
expedite = smr_expedite;
mtx_leave(&smr_lock);
if (expedite)
smr_wakeup(NULL);
else if (wake)
timeout_add_msec(&smr_wakeup_tmo, SMR_PAUSE);
}
/*
* Signal that the current CPU is in quiescent state.
*/
void
smr_idle(void)
{
struct schedstate_percpu *spc = &curcpu()->ci_schedstate;
unsigned char smrgp;
SMR_ASSERT_NONCRITICAL();
if (spc->spc_ndeferred > 0)
smr_dispatch(spc);
/*
* Update this CPU's view of the system's grace period.
* The update must become visible after any preceding reads
* of SMR-protected data.
*/
smrgp = READ_ONCE(smr_grace_period);
if (__predict_false(spc->spc_smrgp != smrgp)) {
membar_exit();
WRITE_ONCE(spc->spc_smrgp, smrgp);
}
}
void
smr_call_impl(struct smr_entry *smr, void (*func)(void *), void *arg,
int expedite)
{
struct cpu_info *ci = curcpu();
struct schedstate_percpu *spc = &ci->ci_schedstate;
int s;
KASSERT(smr->smr_func == NULL);
smr->smr_func = func;
smr->smr_arg = arg;
s = splhigh();
SIMPLEQ_INSERT_TAIL(&spc->spc_deferred, smr, smr_list);
spc->spc_ndeferred++;
spc->spc_smrexpedite |= expedite;
splx(s);
TRACEPOINT(smr, call, func, arg, expedite);
/*
* If this call was made from an interrupt context that
* preempted idle state, dispatch the local queue to the shared
* queue immediately.
* The entries would linger in the local queue long if the CPU
* went to sleep without calling smr_idle().
*/
if (smr_cpu_is_idle(ci))
smr_dispatch(spc);
}
void
smr_barrier_func(void *arg)
{
struct cond *c = arg;
cond_signal(c);
}
void
smr_barrier_impl(int expedite)
{
struct cond c = COND_INITIALIZER();
struct smr_entry smr;
if (panicstr != NULL || db_active)
return;
WITNESS_CHECKORDER(&smr_lock_obj, LOP_NEWORDER, NULL);
TRACEPOINT(smr, barrier_enter, expedite);
smr_init(&smr);
smr_call_impl(&smr, smr_barrier_func, &c, expedite);
cond_wait(&c, "smrbar");
TRACEPOINT(smr, barrier_exit, expedite);
}
183
73
108
96
58
65
18
18
119
140
43
63
47
15
33
34
23
4
17
40
42
42
14
14
14
14
17
9
9
8
33
21
20
2
1
1
79
78
29
27
14
26
2
28
1
29
1
28
2
28
2
2
2
140
141
141
107
29
18
18
15
6
18
3
6
6
6
6
6
6
6
6
6
6
6
6
4
6
34
34
13
11
11
3
85
85
5
3
1
2
2
3
10
10
3
4
4
3
1
6
6
2
6
2
1
1
5
2
2
2
2
49
49
49
29
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
/* $OpenBSD: pf_if.c,v 1.106 2022/06/26 11:37:08 mbuhl Exp $ */
/*
* Copyright 2005 Henning Brauer <henning@openbsd.org>
* Copyright 2005 Ryan McBride <mcbride@openbsd.org>
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2003 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/time.h>
#include <sys/pool.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <net/pfvar.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif /* INET6 */
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
#define islower(c) ((c) >= 'a' && (c) <= 'z')
#define isalpha(c) (isupper(c)||islower(c))
struct pfi_kif *pfi_all = NULL;
struct pool pfi_addr_pl;
struct pfi_ifhead pfi_ifs;
long pfi_update = 1;
struct pfr_addr *pfi_buffer;
int pfi_buffer_cnt;
int pfi_buffer_max;
void pfi_kif_update(struct pfi_kif *);
void pfi_dynaddr_update(struct pfi_dynaddr *dyn);
void pfi_table_update(struct pfr_ktable *, struct pfi_kif *,
u_int8_t, int);
void pfi_kifaddr_update(void *);
void pfi_instance_add(struct ifnet *, u_int8_t, int);
void pfi_address_add(struct sockaddr *, sa_family_t, u_int8_t);
int pfi_if_compare(struct pfi_kif *, struct pfi_kif *);
int pfi_skip_if(const char *, struct pfi_kif *);
int pfi_unmask(void *);
void pfi_group_change(const char *);
RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
#define PFI_BUFFER_MAX 0x10000
#define PFI_MTYPE M_IFADDR
struct pfi_kif *
pfi_kif_alloc(const char *kif_name, int mflags)
{
struct pfi_kif *kif;
kif = malloc(sizeof(*pfi_all), PFI_MTYPE, mflags|M_ZERO);
if (kif == NULL)
return (NULL);
strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name));
kif->pfik_tzero = gettime();
TAILQ_INIT(&kif->pfik_dynaddrs);
if (!strcmp(kif->pfik_name, "any")) {
/* both so it works in the ioctl and the regular case */
kif->pfik_flags |= PFI_IFLAG_ANY;
kif->pfik_flags_new |= PFI_IFLAG_ANY;
}
return (kif);
}
void
pfi_kif_free(struct pfi_kif *kif)
{
if (kif == NULL)
return;
if (kif->pfik_rules || kif->pfik_states || kif->pfik_routes ||
kif->pfik_srcnodes || kif->pfik_flagrefs)
panic("kif is still alive");
free(kif, PFI_MTYPE, sizeof(*kif));
}
void
pfi_initialize(void)
{
/*
* The first time we arrive here is during kernel boot,
* when if_attachsetup() for the first time. No locking
* is needed in this case, because it's granted there
* is a single thread, which sets pfi_all global var.
*/
if (pfi_all != NULL) /* already initialized */
return;
pool_init(&pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, IPL_SOFTNET, 0,
"pfiaddrpl", NULL);
pfi_buffer_max = 64;
pfi_buffer = mallocarray(pfi_buffer_max, sizeof(*pfi_buffer),
PFI_MTYPE, M_WAITOK);
pfi_all = pfi_kif_alloc(IFG_ALL, M_WAITOK);
if (RB_INSERT(pfi_ifhead, &pfi_ifs, pfi_all) != NULL)
panic("IFG_ALL kif found already");
}
struct pfi_kif *
pfi_kif_find(const char *kif_name)
{
struct pfi_kif_cmp s;
memset(&s, 0, sizeof(s));
strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name));
return (RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&s));
}
struct pfi_kif *
pfi_kif_get(const char *kif_name, struct pfi_kif **prealloc)
{
struct pfi_kif *kif;
if ((kif = pfi_kif_find(kif_name)))
return (kif);
/* create new one */
if ((prealloc == NULL) || (*prealloc == NULL)) {
kif = pfi_kif_alloc(kif_name, M_NOWAIT);
if (kif == NULL)
return (NULL);
} else {
kif = *prealloc;
*prealloc = NULL;
}
RB_INSERT(pfi_ifhead, &pfi_ifs, kif);
return (kif);
}
void
pfi_kif_ref(struct pfi_kif *kif, enum pfi_kif_refs what)
{
switch (what) {
case PFI_KIF_REF_RULE:
kif->pfik_rules++;
break;
case PFI_KIF_REF_STATE:
kif->pfik_states++;
break;
case PFI_KIF_REF_ROUTE:
kif->pfik_routes++;
break;
case PFI_KIF_REF_SRCNODE:
kif->pfik_srcnodes++;
break;
case PFI_KIF_REF_FLAG:
kif->pfik_flagrefs++;
break;
default:
panic("pfi_kif_ref with unknown type");
}
}
void
pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what)
{
if (kif == NULL)
return;
switch (what) {
case PFI_KIF_REF_NONE:
break;
case PFI_KIF_REF_RULE:
if (kif->pfik_rules <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): rules refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_rules--;
break;
case PFI_KIF_REF_STATE:
if (kif->pfik_states <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): state refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_states--;
break;
case PFI_KIF_REF_ROUTE:
if (kif->pfik_routes <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): route refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_routes--;
break;
case PFI_KIF_REF_SRCNODE:
if (kif->pfik_srcnodes <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): src-node refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_srcnodes--;
break;
case PFI_KIF_REF_FLAG:
if (kif->pfik_flagrefs <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): flags refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_flagrefs--;
break;
default:
panic("pfi_kif_unref (%s) with unknown type", kif->pfik_name);
}
if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == pfi_all)
return;
if (kif->pfik_rules || kif->pfik_states || kif->pfik_routes ||
kif->pfik_srcnodes || kif->pfik_flagrefs)
return;
RB_REMOVE(pfi_ifhead, &pfi_ifs, kif);
free(kif, PFI_MTYPE, sizeof(*kif));
}
int
pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif)
{
struct ifg_list *p;
if (rule_kif == NULL || rule_kif == packet_kif)
return (1);
if (rule_kif->pfik_group != NULL)
TAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next)
if (p->ifgl_group == rule_kif->pfik_group)
return (1);
if (rule_kif->pfik_flags & PFI_IFLAG_ANY && packet_kif->pfik_ifp &&
!(packet_kif->pfik_ifp->if_flags & IFF_LOOPBACK))
return (1);
return (0);
}
void
pfi_attach_ifnet(struct ifnet *ifp)
{
struct pfi_kif *kif;
struct task *t;
pfi_initialize();
pfi_update++;
if ((kif = pfi_kif_get(ifp->if_xname, NULL)) == NULL)
panic("%s: pfi_kif_get failed", __func__);
kif->pfik_ifp = ifp;
ifp->if_pf_kif = (caddr_t)kif;
t = malloc(sizeof(*t), PFI_MTYPE, M_WAITOK);
task_set(t, pfi_kifaddr_update, kif);
if_addrhook_add(ifp, t);
kif->pfik_ah_cookie = t;
pfi_kif_update(kif);
}
void
pfi_detach_ifnet(struct ifnet *ifp)
{
struct pfi_kif *kif;
struct task *t;
if ((kif = (struct pfi_kif *)ifp->if_pf_kif) == NULL)
return;
pfi_update++;
t = kif->pfik_ah_cookie;
kif->pfik_ah_cookie = NULL;
if_addrhook_del(ifp, t);
free(t, PFI_MTYPE, sizeof(*t));
pfi_kif_update(kif);
kif->pfik_ifp = NULL;
ifp->if_pf_kif = NULL;
pfi_kif_unref(kif, PFI_KIF_REF_NONE);
}
void
pfi_attach_ifgroup(struct ifg_group *ifg)
{
struct pfi_kif *kif;
pfi_initialize();
pfi_update++;
if ((kif = pfi_kif_get(ifg->ifg_group, NULL)) == NULL)
panic("%s: pfi_kif_get failed", __func__);
kif->pfik_group = ifg;
ifg->ifg_pf_kif = (caddr_t)kif;
}
void
pfi_detach_ifgroup(struct ifg_group *ifg)
{
struct pfi_kif *kif;
if ((kif = (struct pfi_kif *)ifg->ifg_pf_kif) == NULL)
return;
pfi_update++;
kif->pfik_group = NULL;
ifg->ifg_pf_kif = NULL;
pfi_kif_unref(kif, PFI_KIF_REF_NONE);
}
void
pfi_group_change(const char *group)
{
struct pfi_kif *kif;
pfi_update++;
if ((kif = pfi_kif_get(group, NULL)) == NULL)
panic("%s: pfi_kif_get failed", __func__);
pfi_kif_update(kif);
}
void
pfi_group_delmember(const char *group)
{
pfi_group_change(group);
pfi_xcommit();
}
void
pfi_group_addmember(const char *group)
{
pfi_group_change(group);
pfi_xcommit();
}
int
pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af)
{
switch (af) {
case AF_INET:
switch (dyn->pfid_acnt4) {
case 0:
return (0);
case 1:
return (pf_match_addr(0, &dyn->pfid_addr4,
&dyn->pfid_mask4, a, AF_INET));
default:
return (pfr_match_addr(dyn->pfid_kt, a, AF_INET));
}
break;
#ifdef INET6
case AF_INET6:
switch (dyn->pfid_acnt6) {
case 0:
return (0);
case 1:
return (pf_match_addr(0, &dyn->pfid_addr6,
&dyn->pfid_mask6, a, AF_INET6));
default:
return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6));
}
break;
#endif /* INET6 */
default:
return (0);
}
}
int
pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af, int wait)
{
struct pfi_dynaddr *dyn;
char tblname[PF_TABLE_NAME_SIZE];
struct pf_ruleset *ruleset = NULL;
int rv = 0;
if (aw->type != PF_ADDR_DYNIFTL)
return (0);
if ((dyn = pool_get(&pfi_addr_pl, wait|PR_LIMITFAIL|PR_ZERO)) == NULL)
return (1);
if (!strcmp(aw->v.ifname, "self"))
dyn->pfid_kif = pfi_kif_get(IFG_ALL, NULL);
else
dyn->pfid_kif = pfi_kif_get(aw->v.ifname, NULL);
if (dyn->pfid_kif == NULL) {
rv = 1;
goto _bad;
}
pfi_kif_ref(dyn->pfid_kif, PFI_KIF_REF_RULE);
dyn->pfid_net = pfi_unmask(&aw->v.a.mask);
if (af == AF_INET && dyn->pfid_net == 32)
dyn->pfid_net = 128;
strlcpy(tblname, aw->v.ifname, sizeof(tblname));
if (aw->iflags & PFI_AFLAG_NETWORK)
strlcat(tblname, ":network", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_BROADCAST)
strlcat(tblname, ":broadcast", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_PEER)
strlcat(tblname, ":peer", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_NOALIAS)
strlcat(tblname, ":0", sizeof(tblname));
if (dyn->pfid_net != 128)
snprintf(tblname + strlen(tblname),
sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net);
if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == NULL) {
rv = 1;
goto _bad;
}
if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, wait)) == NULL) {
rv = 1;
goto _bad;
}
dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE;
dyn->pfid_iflags = aw->iflags;
dyn->pfid_af = af;
TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry);
aw->p.dyn = dyn;
pfi_kif_update(dyn->pfid_kif);
return (0);
_bad:
if (dyn->pfid_kt != NULL)
pfr_detach_table(dyn->pfid_kt);
if (ruleset != NULL)
pf_remove_if_empty_ruleset(ruleset);
if (dyn->pfid_kif != NULL)
pfi_kif_unref(dyn->pfid_kif, PFI_KIF_REF_RULE);
pool_put(&pfi_addr_pl, dyn);
return (rv);
}
void
pfi_kif_update(struct pfi_kif *kif)
{
struct ifg_list *ifgl;
struct pfi_dynaddr *p;
/* update all dynaddr */
TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry)
pfi_dynaddr_update(p);
/* again for all groups kif is member of */
if (kif->pfik_ifp != NULL)
TAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next)
pfi_kif_update((struct pfi_kif *)
ifgl->ifgl_group->ifg_pf_kif);
}
void
pfi_dynaddr_update(struct pfi_dynaddr *dyn)
{
struct pfi_kif *kif;
struct pfr_ktable *kt;
if (dyn == NULL || dyn->pfid_kif == NULL || dyn->pfid_kt == NULL)
panic("pfi_dynaddr_update");
kif = dyn->pfid_kif;
kt = dyn->pfid_kt;
if (kt->pfrkt_larg != pfi_update) {
/* this table needs to be brought up-to-date */
pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags);
kt->pfrkt_larg = pfi_update;
}
pfr_dynaddr_update(kt, dyn);
}
void
pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, u_int8_t net, int flags)
{
int e, size2 = 0;
struct ifg_member *ifgm;
pfi_buffer_cnt = 0;
if (kif->pfik_ifp != NULL)
pfi_instance_add(kif->pfik_ifp, net, flags);
else if (kif->pfik_group != NULL)
TAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next)
pfi_instance_add(ifgm->ifgm_ifp, net, flags);
if ((e = pfr_set_addrs(&kt->pfrkt_t, pfi_buffer, pfi_buffer_cnt, &size2,
NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK)))
DPFPRINTF(LOG_ERR,
"pfi_table_update: cannot set %d new addresses "
"into table %s: %d", pfi_buffer_cnt, kt->pfrkt_name, e);
}
void
pfi_instance_add(struct ifnet *ifp, u_int8_t net, int flags)
{
struct ifaddr *ifa;
int got4 = 0, got6 = 0;
int net2, af;
if (ifp == NULL)
return;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr == NULL)
continue;
af = ifa->ifa_addr->sa_family;
if (af != AF_INET && af != AF_INET6)
continue;
if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6)
continue;
if ((flags & PFI_AFLAG_BROADCAST) &&
!(ifp->if_flags & IFF_BROADCAST))
continue;
if ((flags & PFI_AFLAG_PEER) &&
!(ifp->if_flags & IFF_POINTOPOINT))
continue;
if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(
&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
continue;
if (flags & PFI_AFLAG_NOALIAS) {
if (af == AF_INET && got4)
continue;
if (af == AF_INET6 && got6)
continue;
}
if (af == AF_INET)
got4 = 1;
else if (af == AF_INET6)
got6 = 1;
net2 = net;
if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) {
if (af == AF_INET)
net2 = pfi_unmask(&((struct sockaddr_in *)
ifa->ifa_netmask)->sin_addr);
else if (af == AF_INET6)
net2 = pfi_unmask(&((struct sockaddr_in6 *)
ifa->ifa_netmask)->sin6_addr);
}
if (af == AF_INET && net2 > 32)
net2 = 32;
if (flags & PFI_AFLAG_BROADCAST)
pfi_address_add(ifa->ifa_broadaddr, af, net2);
else if (flags & PFI_AFLAG_PEER)
pfi_address_add(ifa->ifa_dstaddr, af, net2);
else
pfi_address_add(ifa->ifa_addr, af, net2);
}
}
void
pfi_address_add(struct sockaddr *sa, sa_family_t af, u_int8_t net)
{
struct pfr_addr *p;
int i;
if (pfi_buffer_cnt >= pfi_buffer_max) {
int new_max = pfi_buffer_max * 2;
if (new_max > PFI_BUFFER_MAX) {
DPFPRINTF(LOG_ERR,
"pfi_address_add: address buffer full (%d/%d)",
pfi_buffer_cnt, PFI_BUFFER_MAX);
return;
}
p = mallocarray(new_max, sizeof(*pfi_buffer), PFI_MTYPE,
M_DONTWAIT);
if (p == NULL) {
DPFPRINTF(LOG_ERR,
"pfi_address_add: no memory to grow buffer "
"(%d/%d)", pfi_buffer_cnt, PFI_BUFFER_MAX);
return;
}
memcpy(p, pfi_buffer, pfi_buffer_max * sizeof(*pfi_buffer));
/* no need to zero buffer */
free(pfi_buffer, PFI_MTYPE, pfi_buffer_max * sizeof(*pfi_buffer));
pfi_buffer = p;
pfi_buffer_max = new_max;
}
if (af == AF_INET && net > 32)
net = 128;
p = pfi_buffer + pfi_buffer_cnt++;
memset(p, 0, sizeof(*p));
p->pfra_af = af;
p->pfra_net = net;
if (af == AF_INET)
p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr;
else if (af == AF_INET6) {
p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
if (IN6_IS_SCOPE_EMBED(&p->pfra_ip6addr))
p->pfra_ip6addr.s6_addr16[1] = 0;
}
/* mask network address bits */
if (net < 128)
((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8));
for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++)
((caddr_t)p)[i] = 0;
}
void
pfi_dynaddr_remove(struct pf_addr_wrap *aw)
{
if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL)
return;
TAILQ_REMOVE(&aw->p.dyn->pfid_kif->pfik_dynaddrs, aw->p.dyn, entry);
pfi_kif_unref(aw->p.dyn->pfid_kif, PFI_KIF_REF_RULE);
aw->p.dyn->pfid_kif = NULL;
pfr_detach_table(aw->p.dyn->pfid_kt);
aw->p.dyn->pfid_kt = NULL;
pool_put(&pfi_addr_pl, aw->p.dyn);
aw->p.dyn = NULL;
}
void
pfi_dynaddr_copyout(struct pf_addr_wrap *aw)
{
if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
aw->p.dyn->pfid_kif == NULL)
return;
aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6;
}
void
pfi_kifaddr_update(void *v)
{
struct pfi_kif *kif = (struct pfi_kif *)v;
NET_ASSERT_LOCKED();
pfi_update++;
pfi_kif_update(kif);
}
int
pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q)
{
return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ));
}
void
pfi_update_status(const char *name, struct pf_status *pfs)
{
struct pfi_kif *p;
struct pfi_kif_cmp key;
struct ifg_member p_member, *ifgm;
TAILQ_HEAD(, ifg_member) ifg_members;
int i, j, k;
if (*name == '\0' && pfs == NULL) {
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
memset(p->pfik_packets, 0, sizeof(p->pfik_packets));
memset(p->pfik_bytes, 0, sizeof(p->pfik_bytes));
p->pfik_tzero = gettime();
}
return;
}
strlcpy(key.pfik_name, name, sizeof(key.pfik_name));
p = RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&key);
if (p == NULL) {
return;
}
if (p->pfik_group != NULL) {
memcpy(&ifg_members, &p->pfik_group->ifg_members,
sizeof(ifg_members));
} else {
/* build a temporary list for p only */
memset(&p_member, 0, sizeof(p_member));
p_member.ifgm_ifp = p->pfik_ifp;
TAILQ_INIT(&ifg_members);
TAILQ_INSERT_TAIL(&ifg_members, &p_member, ifgm_next);
}
if (pfs) {
memset(pfs->pcounters, 0, sizeof(pfs->pcounters));
memset(pfs->bcounters, 0, sizeof(pfs->bcounters));
}
TAILQ_FOREACH(ifgm, &ifg_members, ifgm_next) {
if (ifgm->ifgm_ifp == NULL)
continue;
p = (struct pfi_kif *)ifgm->ifgm_ifp->if_pf_kif;
/* just clear statistics */
if (pfs == NULL) {
memset(p->pfik_packets, 0, sizeof(p->pfik_packets));
memset(p->pfik_bytes, 0, sizeof(p->pfik_bytes));
p->pfik_tzero = gettime();
continue;
}
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
for (k = 0; k < 2; k++) {
pfs->pcounters[i][j][k] +=
p->pfik_packets[i][j][k];
pfs->bcounters[i][j] +=
p->pfik_bytes[i][j][k];
}
}
}
void
pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size)
{
struct pfi_kif *p, *nextp;
int n = 0;
for (p = RB_MIN(pfi_ifhead, &pfi_ifs); p; p = nextp) {
nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p);
if (pfi_skip_if(name, p))
continue;
if (*size > n++) {
if (!p->pfik_tzero)
p->pfik_tzero = gettime();
memcpy(buf++, p, sizeof(*buf));
nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p);
}
}
*size = n;
}
int
pfi_skip_if(const char *filter, struct pfi_kif *p)
{
struct ifg_list *i;
int n;
if (filter == NULL || !*filter)
return (0);
if (!strcmp(p->pfik_name, filter))
return (0); /* exact match */
n = strlen(filter);
if (n < 1 || n >= IFNAMSIZ)
return (1); /* sanity check */
if (filter[n-1] >= '0' && filter[n-1] <= '9')
return (1); /* group names may not end in a digit */
if (p->pfik_ifp != NULL)
TAILQ_FOREACH(i, &p->pfik_ifp->if_groups, ifgl_next)
if (!strncmp(i->ifgl_group->ifg_group, filter, IFNAMSIZ))
return (0); /* iface is in group "filter" */
return (1);
}
int
pfi_set_flags(const char *name, int flags)
{
struct pfi_kif *p;
size_t n;
if (name != NULL && name[0] != '\0') {
p = pfi_kif_find(name);
if (p == NULL) {
n = strlen(name);
if (n < 1 || n >= IFNAMSIZ)
return (EINVAL);
if (!isalpha(name[0]))
return (EINVAL);
p = pfi_kif_get(name, NULL);
if (p != NULL) {
p->pfik_flags_new = p->pfik_flags | flags;
/*
* We use pfik_flagrefs counter as an
* indication whether the kif has been created
* on behalf of 'pfi_set_flags()' or not.
*/
KASSERT(p->pfik_flagrefs == 0);
if (ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP))
pfi_kif_ref(p, PFI_KIF_REF_FLAG);
} else
panic("%s pfi_kif_get() returned NULL\n",
__func__);
} else
p->pfik_flags_new = p->pfik_flags | flags;
} else {
RB_FOREACH(p, pfi_ifhead, &pfi_ifs)
p->pfik_flags_new = p->pfik_flags | flags;
}
return (0);
}
int
pfi_clear_flags(const char *name, int flags)
{
struct pfi_kif *p, *w;
if (name != NULL && name[0] != '\0') {
p = pfi_kif_find(name);
if (p != NULL) {
p->pfik_flags_new = p->pfik_flags & ~flags;
KASSERT((p->pfik_flagrefs == 0) ||
(p->pfik_flagrefs == 1));
if (!ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
(p->pfik_flagrefs == 1))
pfi_kif_unref(p, PFI_KIF_REF_FLAG);
} else
return (ESRCH);
} else
RB_FOREACH_SAFE(p, pfi_ifhead, &pfi_ifs, w) {
p->pfik_flags_new = p->pfik_flags & ~flags;
KASSERT((p->pfik_flagrefs == 0) ||
(p->pfik_flagrefs == 1));
if (!ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
(p->pfik_flagrefs == 1))
pfi_kif_unref(p, PFI_KIF_REF_FLAG);
}
return (0);
}
void
pfi_xcommit(void)
{
struct pfi_kif *p, *gkif;
struct ifg_list *g;
struct ifnet *ifp;
size_t n;
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
p->pfik_flags = p->pfik_flags_new;
n = strlen(p->pfik_name);
ifp = p->pfik_ifp;
/*
* if kif is backed by existing interface, then we must use
* skip flags found in groups. We use pfik_flags_new, otherwise
* we would need to do two RB_FOREACH() passes: the first to
* commit group changes the second to commit flag changes for
* interfaces.
*/
if (ifp != NULL)
TAILQ_FOREACH(g, &ifp->if_groups, ifgl_next) {
gkif =
(struct pfi_kif *)g->ifgl_group->ifg_pf_kif;
KASSERT(gkif != NULL);
p->pfik_flags |= gkif->pfik_flags_new;
}
}
}
/* from pf_print_state.c */
int
pfi_unmask(void *addr)
{
struct pf_addr *m = addr;
int i = 31, j = 0, b = 0;
u_int32_t tmp;
while (j < 4 && m->addr32[j] == 0xffffffff) {
b += 32;
j++;
}
if (j < 4) {
tmp = ntohl(m->addr32[j]);
for (i = 31; tmp & (1 << i); --i)
b++;
}
return (b);
}
53
71
403
9
5
396
2
36
208
9
5
6
239
161
187
129
125
5
5
2
3
2
3
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
/* $OpenBSD: kern_timeout.c,v 1.85 2021/06/19 02:05:33 cheloha Exp $ */
/*
* Copyright (c) 2001 Thomas Nordin <nordin@openbsd.org>
* Copyright (c) 2000-2001 Artur Grabowski <art@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kthread.h>
#include <sys/proc.h>
#include <sys/timeout.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/queue.h> /* _Q_INVALIDATE */
#include <sys/sysctl.h>
#include <sys/witness.h>
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
#include <ddb/db_sym.h>
#include <ddb/db_output.h>
#endif
#include "kcov.h"
#if NKCOV > 0
#include <sys/kcov.h>
#endif
/*
* Locks used to protect global variables in this file:
*
* I immutable after initialization
* T timeout_mutex
*/
struct mutex timeout_mutex = MUTEX_INITIALIZER(IPL_HIGH);
void *softclock_si; /* [I] softclock() interrupt handle */
struct timeoutstat tostat; /* [T] statistics and totals */
/*
* Timeouts are kept in a hierarchical timing wheel. The to_time is the value
* of the global variable "ticks" when the timeout should be called. There are
* four levels with 256 buckets each.
*/
#define WHEELCOUNT 4
#define WHEELSIZE 256
#define WHEELMASK 255
#define WHEELBITS 8
#define BUCKETS (WHEELCOUNT * WHEELSIZE)
struct circq timeout_wheel[BUCKETS]; /* [T] Tick-based timeouts */
struct circq timeout_wheel_kc[BUCKETS]; /* [T] Clock-based timeouts */
struct circq timeout_new; /* [T] New, unscheduled timeouts */
struct circq timeout_todo; /* [T] Due or needs rescheduling */
struct circq timeout_proc; /* [T] Due + needs process context */
time_t timeout_level_width[WHEELCOUNT]; /* [I] Wheel level width (seconds) */
struct timespec tick_ts; /* [I] Length of a tick (1/hz secs) */
struct kclock {
struct timespec kc_lastscan; /* [T] Clock time at last wheel scan */
struct timespec kc_late; /* [T] Late if due prior */
struct timespec kc_offset; /* [T] Offset from primary kclock */
} timeout_kclock[KCLOCK_MAX];
#define MASKWHEEL(wheel, time) (((time) >> ((wheel)*WHEELBITS)) & WHEELMASK)
#define BUCKET(rel, abs) \
(timeout_wheel[ \
((rel) <= (1 << (2*WHEELBITS))) \
? ((rel) <= (1 << WHEELBITS)) \
? MASKWHEEL(0, (abs)) \
: MASKWHEEL(1, (abs)) + WHEELSIZE \
: ((rel) <= (1 << (3*WHEELBITS))) \
? MASKWHEEL(2, (abs)) + 2*WHEELSIZE \
: MASKWHEEL(3, (abs)) + 3*WHEELSIZE])
#define MOVEBUCKET(wheel, time) \
CIRCQ_CONCAT(&timeout_todo, \
&timeout_wheel[MASKWHEEL((wheel), (time)) + (wheel)*WHEELSIZE])
/*
* Circular queue definitions.
*/
#define CIRCQ_INIT(elem) do { \
(elem)->next = (elem); \
(elem)->prev = (elem); \
} while (0)
#define CIRCQ_INSERT_TAIL(list, elem) do { \
(elem)->prev = (list)->prev; \
(elem)->next = (list); \
(list)->prev->next = (elem); \
(list)->prev = (elem); \
tostat.tos_pending++; \
} while (0)
#define CIRCQ_CONCAT(fst, snd) do { \
if (!CIRCQ_EMPTY(snd)) { \
(fst)->prev->next = (snd)->next;\
(snd)->next->prev = (fst)->prev;\
(snd)->prev->next = (fst); \
(fst)->prev = (snd)->prev; \
CIRCQ_INIT(snd); \
} \
} while (0)
#define CIRCQ_REMOVE(elem) do { \
(elem)->next->prev = (elem)->prev; \
(elem)->prev->next = (elem)->next; \
_Q_INVALIDATE((elem)->prev); \
_Q_INVALIDATE((elem)->next); \
tostat.tos_pending--; \
} while (0)
#define CIRCQ_FIRST(elem) ((elem)->next)
#define CIRCQ_EMPTY(elem) (CIRCQ_FIRST(elem) == (elem))
#define CIRCQ_FOREACH(elem, list) \
for ((elem) = CIRCQ_FIRST(list); \
(elem) != (list); \
(elem) = CIRCQ_FIRST(elem))
#ifdef WITNESS
struct lock_object timeout_sleeplock_obj = {
.lo_name = "timeout",
.lo_flags = LO_WITNESS | LO_INITIALIZED | LO_SLEEPABLE |
(LO_CLASS_RWLOCK << LO_CLASSSHIFT)
};
struct lock_object timeout_spinlock_obj = {
.lo_name = "timeout",
.lo_flags = LO_WITNESS | LO_INITIALIZED |
(LO_CLASS_MUTEX << LO_CLASSSHIFT)
};
struct lock_type timeout_sleeplock_type = {
.lt_name = "timeout"
};
struct lock_type timeout_spinlock_type = {
.lt_name = "timeout"
};
#define TIMEOUT_LOCK_OBJ(needsproc) \
((needsproc) ? &timeout_sleeplock_obj : &timeout_spinlock_obj)
#endif
void kclock_nanotime(int, struct timespec *);
void softclock(void *);
void softclock_create_thread(void *);
void softclock_process_kclock_timeout(struct timeout *, int);
void softclock_process_tick_timeout(struct timeout *, int);
void softclock_thread(void *);
void timeout_barrier_timeout(void *);
uint32_t timeout_bucket(const struct timeout *);
uint32_t timeout_maskwheel(uint32_t, const struct timespec *);
void timeout_run(struct timeout *);
/*
* The first thing in a struct timeout is its struct circq, so we
* can get back from a pointer to the latter to a pointer to the
* whole timeout with just a cast.
*/
static inline struct timeout *
timeout_from_circq(struct circq *p)
{
return ((struct timeout *)(p));
}
static inline void
timeout_sync_order(int needsproc)
{
WITNESS_CHECKORDER(TIMEOUT_LOCK_OBJ(needsproc), LOP_NEWORDER, NULL);
}
static inline void
timeout_sync_enter(int needsproc)
{
timeout_sync_order(needsproc);
WITNESS_LOCK(TIMEOUT_LOCK_OBJ(needsproc), 0);
}
static inline void
timeout_sync_leave(int needsproc)
{
WITNESS_UNLOCK(TIMEOUT_LOCK_OBJ(needsproc), 0);
}
/*
* Some of the "math" in here is a bit tricky.
*
* We have to beware of wrapping ints.
* We use the fact that any element added to the queue must be added with a
* positive time. That means that any element `to' on the queue cannot be
* scheduled to timeout further in time than INT_MAX, but to->to_time can
* be positive or negative so comparing it with anything is dangerous.
* The only way we can use the to->to_time value in any predictable way
* is when we calculate how far in the future `to' will timeout -
* "to->to_time - ticks". The result will always be positive for future
* timeouts and 0 or negative for due timeouts.
*/
void
timeout_startup(void)
{
int b, level;
CIRCQ_INIT(&timeout_new);
CIRCQ_INIT(&timeout_todo);
CIRCQ_INIT(&timeout_proc);
for (b = 0; b < nitems(timeout_wheel); b++)
CIRCQ_INIT(&timeout_wheel[b]);
for (b = 0; b < nitems(timeout_wheel_kc); b++)
CIRCQ_INIT(&timeout_wheel_kc[b]);
for (level = 0; level < nitems(timeout_level_width); level++)
timeout_level_width[level] = 2 << (level * WHEELBITS);
NSEC_TO_TIMESPEC(tick_nsec, &tick_ts);
}
void
timeout_proc_init(void)
{
softclock_si = softintr_establish(IPL_SOFTCLOCK, softclock, NULL);
if (softclock_si == NULL)
panic("%s: unable to register softclock interrupt", __func__);
WITNESS_INIT(&timeout_sleeplock_obj, &timeout_sleeplock_type);
WITNESS_INIT(&timeout_spinlock_obj, &timeout_spinlock_type);
kthread_create_deferred(softclock_create_thread, NULL);
}
static inline void
_timeout_set(struct timeout *to, void (*fn)(void *), void *arg, int kclock,
int flags)
{
to->to_func = fn;
to->to_arg = arg;
to->to_kclock = kclock;
to->to_flags = flags | TIMEOUT_INITIALIZED;
}
void
timeout_set(struct timeout *new, void (*fn)(void *), void *arg)
{
_timeout_set(new, fn, arg, KCLOCK_NONE, 0);
}
void
timeout_set_flags(struct timeout *to, void (*fn)(void *), void *arg, int flags)
{
_timeout_set(to, fn, arg, KCLOCK_NONE, flags);
}
void
timeout_set_proc(struct timeout *new, void (*fn)(void *), void *arg)
{
_timeout_set(new, fn, arg, KCLOCK_NONE, TIMEOUT_PROC);
}
void
timeout_set_kclock(struct timeout *to, void (*fn)(void *), void *arg,
int kclock, int flags)
{
_timeout_set(to, fn, arg, kclock, flags | TIMEOUT_KCLOCK);
}
int
timeout_add(struct timeout *new, int to_ticks)
{
int old_time;
int ret = 1;
KASSERT(ISSET(new->to_flags, TIMEOUT_INITIALIZED));
KASSERT(!ISSET(new->to_flags, TIMEOUT_KCLOCK));
KASSERT(new->to_kclock == KCLOCK_NONE);
KASSERT(to_ticks >= 0);
mtx_enter(&timeout_mutex);
/* Initialize the time here, it won't change. */
old_time = new->to_time;
new->to_time = to_ticks + ticks;
CLR(new->to_flags, TIMEOUT_TRIGGERED);
/*
* If this timeout already is scheduled and now is moved
* earlier, reschedule it now. Otherwise leave it in place
* and let it be rescheduled later.
*/
if (ISSET(new->to_flags, TIMEOUT_ONQUEUE)) {
if (new->to_time - ticks < old_time - ticks) {
CIRCQ_REMOVE(&new->to_list);
CIRCQ_INSERT_TAIL(&timeout_new, &new->to_list);
}
tostat.tos_readded++;
ret = 0;
} else {
SET(new->to_flags, TIMEOUT_ONQUEUE);
CIRCQ_INSERT_TAIL(&timeout_new, &new->to_list);
}
#if NKCOV > 0
new->to_process = curproc->p_p;
#endif
tostat.tos_added++;
mtx_leave(&timeout_mutex);
return ret;
}
int
timeout_add_tv(struct timeout *to, const struct timeval *tv)
{
uint64_t to_ticks;
to_ticks = (uint64_t)hz * tv->tv_sec + tv->tv_usec / tick;
if (to_ticks > INT_MAX)
to_ticks = INT_MAX;
if (to_ticks == 0 && tv->tv_usec > 0)
to_ticks = 1;
return timeout_add(to, (int)to_ticks);
}
int
timeout_add_sec(struct timeout *to, int secs)
{
uint64_t to_ticks;
to_ticks = (uint64_t)hz * secs;
if (to_ticks > INT_MAX)
to_ticks = INT_MAX;
if (to_ticks == 0)
to_ticks = 1;
return timeout_add(to, (int)to_ticks);
}
int
timeout_add_msec(struct timeout *to, int msecs)
{
uint64_t to_ticks;
to_ticks = (uint64_t)msecs * 1000 / tick;
if (to_ticks > INT_MAX)
to_ticks = INT_MAX;
if (to_ticks == 0 && msecs > 0)
to_ticks = 1;
return timeout_add(to, (int)to_ticks);
}
int
timeout_add_usec(struct timeout *to, int usecs)
{
int to_ticks = usecs / tick;
if (to_ticks == 0 && usecs > 0)
to_ticks = 1;
return timeout_add(to, to_ticks);
}
int
timeout_add_nsec(struct timeout *to, int nsecs)
{
int to_ticks = nsecs / (tick * 1000);
if (to_ticks == 0 && nsecs > 0)
to_ticks = 1;
return timeout_add(to, to_ticks);
}
int
timeout_at_ts(struct timeout *to, const struct timespec *abstime)
{
struct timespec old_abstime;
int ret = 1;
mtx_enter(&timeout_mutex);
KASSERT(ISSET(to->to_flags, TIMEOUT_INITIALIZED | TIMEOUT_KCLOCK));
KASSERT(to->to_kclock != KCLOCK_NONE);
old_abstime = to->to_abstime;
to->to_abstime = *abstime;
CLR(to->to_flags, TIMEOUT_TRIGGERED);
if (ISSET(to->to_flags, TIMEOUT_ONQUEUE)) {
if (timespeccmp(abstime, &old_abstime, <)) {
CIRCQ_REMOVE(&to->to_list);
CIRCQ_INSERT_TAIL(&timeout_new, &to->to_list);
}
tostat.tos_readded++;
ret = 0;
} else {
SET(to->to_flags, TIMEOUT_ONQUEUE);
CIRCQ_INSERT_TAIL(&timeout_new, &to->to_list);
}
#if NKCOV > 0
to->to_process = curproc->p_p;
#endif
tostat.tos_added++;
mtx_leave(&timeout_mutex);
return ret;
}
int
timeout_in_nsec(struct timeout *to, uint64_t nsecs)
{
struct timespec abstime, interval, now;
kclock_nanotime(to->to_kclock, &now);
NSEC_TO_TIMESPEC(nsecs, &interval);
timespecadd(&now, &interval, &abstime);
return timeout_at_ts(to, &abstime);
}
void
kclock_nanotime(int kclock, struct timespec *now)
{
switch (kclock) {
case KCLOCK_UPTIME:
nanouptime(now);
break;
default:
panic("invalid kclock: 0x%x", kclock);
}
}
int
timeout_del(struct timeout *to)
{
int ret = 0;
mtx_enter(&timeout_mutex);
if (ISSET(to->to_flags, TIMEOUT_ONQUEUE)) {
CIRCQ_REMOVE(&to->to_list);
CLR(to->to_flags, TIMEOUT_ONQUEUE);
tostat.tos_cancelled++;
ret = 1;
}
CLR(to->to_flags, TIMEOUT_TRIGGERED);
tostat.tos_deleted++;
mtx_leave(&timeout_mutex);
return ret;
}
int
timeout_del_barrier(struct timeout *to)
{
int removed;
timeout_sync_order(ISSET(to->to_flags, TIMEOUT_PROC));
removed = timeout_del(to);
if (!removed)
timeout_barrier(to);
return removed;
}
void
timeout_barrier(struct timeout *to)
{
struct timeout barrier;
struct cond c;
int procflag;
procflag = (to->to_flags & TIMEOUT_PROC);
timeout_sync_order(procflag);
timeout_set_flags(&barrier, timeout_barrier_timeout, &c, procflag);
barrier.to_process = curproc->p_p;
cond_init(&c);
mtx_enter(&timeout_mutex);
barrier.to_time = ticks;
SET(barrier.to_flags, TIMEOUT_ONQUEUE);
if (procflag)
CIRCQ_INSERT_TAIL(&timeout_proc, &barrier.to_list);
else
CIRCQ_INSERT_TAIL(&timeout_todo, &barrier.to_list);
mtx_leave(&timeout_mutex);
if (procflag)
wakeup_one(&timeout_proc);
else
softintr_schedule(softclock_si);
cond_wait(&c, "tmobar");
}
void
timeout_barrier_timeout(void *arg)
{
struct cond *c = arg;
cond_signal(c);
}
uint32_t
timeout_bucket(const struct timeout *to)
{
struct kclock *kc = &timeout_kclock[to->to_kclock];
struct timespec diff, shifted_abstime;
uint32_t level;
KASSERT(ISSET(to->to_flags, TIMEOUT_KCLOCK));
KASSERT(timespeccmp(&kc->kc_lastscan, &to->to_abstime, <));
timespecsub(&to->to_abstime, &kc->kc_lastscan, &diff);
for (level = 0; level < nitems(timeout_level_width) - 1; level++) {
if (diff.tv_sec < timeout_level_width[level])
break;
}
timespecadd(&to->to_abstime, &kc->kc_offset, &shifted_abstime);
return level * WHEELSIZE + timeout_maskwheel(level, &shifted_abstime);
}
/*
* Hash the absolute time into a bucket on a given level of the wheel.
*
* The complete hash is 32 bits. The upper 25 bits are seconds, the
* lower 7 bits are nanoseconds. tv_nsec is a positive value less
* than one billion so we need to divide it to isolate the desired
* bits. We can't just shift it.
*
* The level is used to isolate an 8-bit portion of the hash. The
* resulting number indicates which bucket the absolute time belongs
* in on the given level of the wheel.
*/
uint32_t
timeout_maskwheel(uint32_t level, const struct timespec *abstime)
{
uint32_t hi, lo;
hi = abstime->tv_sec << 7;
lo = abstime->tv_nsec / 7812500;
return ((hi | lo) >> (level * WHEELBITS)) & WHEELMASK;
}
/*
* This is called from hardclock() on the primary CPU at the start of
* every tick.
*/
void
timeout_hardclock_update(void)
{
struct timespec elapsed, now;
struct kclock *kc;
struct timespec *lastscan;
int b, done, first, i, last, level, need_softclock, off;
nanouptime(&now);
lastscan = &timeout_kclock[KCLOCK_UPTIME].kc_lastscan;
timespecsub(&now, lastscan, &elapsed);
need_softclock = 1;
mtx_enter(&timeout_mutex);
MOVEBUCKET(0, ticks);
if (MASKWHEEL(0, ticks) == 0) {
MOVEBUCKET(1, ticks);
if (MASKWHEEL(1, ticks) == 0) {
MOVEBUCKET(2, ticks);
if (MASKWHEEL(2, ticks) == 0)
MOVEBUCKET(3, ticks);
}
}
/*
* Dump the buckets that expired while we were away.
*
* If the elapsed time has exceeded a level's limit then we need
* to dump every bucket in the level. We have necessarily completed
* a lap of that level, too, so we need to process buckets in the
* next level.
*
* Otherwise we need to compare indices: if the index of the first
* expired bucket is greater than that of the last then we have
* completed a lap of the level and need to process buckets in the
* next level.
*/
for (level = 0; level < nitems(timeout_level_width); level++) {
first = timeout_maskwheel(level, lastscan);
if (elapsed.tv_sec >= timeout_level_width[level]) {
last = (first == 0) ? WHEELSIZE - 1 : first - 1;
done = 0;
} else {
last = timeout_maskwheel(level, &now);
done = first <= last;
}
off = level * WHEELSIZE;
for (b = first;; b = (b + 1) % WHEELSIZE) {
CIRCQ_CONCAT(&timeout_todo, &timeout_wheel_kc[off + b]);
if (b == last)
break;
}
if (done)
break;
}
/*
* Update the cached state for each kclock.
*/
for (i = 0; i < nitems(timeout_kclock); i++) {
kc = &timeout_kclock[i];
timespecadd(&now, &kc->kc_offset, &kc->kc_lastscan);
timespecsub(&kc->kc_lastscan, &tick_ts, &kc->kc_late);
}
if (CIRCQ_EMPTY(&timeout_new) && CIRCQ_EMPTY(&timeout_todo))
need_softclock = 0;
mtx_leave(&timeout_mutex);
if (need_softclock)
softintr_schedule(softclock_si);
}
void
timeout_run(struct timeout *to)
{
void (*fn)(void *);
void *arg;
int needsproc;
MUTEX_ASSERT_LOCKED(&timeout_mutex);
CLR(to->to_flags, TIMEOUT_ONQUEUE);
SET(to->to_flags, TIMEOUT_TRIGGERED);
fn = to->to_func;
arg = to->to_arg;
needsproc = ISSET(to->to_flags, TIMEOUT_PROC);
#if NKCOV > 0
struct process *kcov_process = to->to_process;
#endif
mtx_leave(&timeout_mutex);
timeout_sync_enter(needsproc);
#if NKCOV > 0
kcov_remote_enter(KCOV_REMOTE_COMMON, kcov_process);
#endif
fn(arg);
#if NKCOV > 0
kcov_remote_leave(KCOV_REMOTE_COMMON, kcov_process);
#endif
timeout_sync_leave(needsproc);
mtx_enter(&timeout_mutex);
}
void
softclock_process_kclock_timeout(struct timeout *to, int new)
{
struct kclock *kc = &timeout_kclock[to->to_kclock];
if (timespeccmp(&to->to_abstime, &kc->kc_lastscan, >)) {
tostat.tos_scheduled++;
if (!new)
tostat.tos_rescheduled++;
CIRCQ_INSERT_TAIL(&timeout_wheel_kc[timeout_bucket(to)],
&to->to_list);
return;
}
if (!new && timespeccmp(&to->to_abstime, &kc->kc_late, <=))
tostat.tos_late++;
if (ISSET(to->to_flags, TIMEOUT_PROC)) {
CIRCQ_INSERT_TAIL(&timeout_proc, &to->to_list);
return;
}
timeout_run(to);
tostat.tos_run_softclock++;
}
void
softclock_process_tick_timeout(struct timeout *to, int new)
{
int delta = to->to_time - ticks;
if (delta > 0) {
tostat.tos_scheduled++;
if (!new)
tostat.tos_rescheduled++;
CIRCQ_INSERT_TAIL(&BUCKET(delta, to->to_time), &to->to_list);
return;
}
if (!new && delta < 0)
tostat.tos_late++;
if (ISSET(to->to_flags, TIMEOUT_PROC)) {
CIRCQ_INSERT_TAIL(&timeout_proc, &to->to_list);
return;
}
timeout_run(to);
tostat.tos_run_softclock++;
}
/*
* Timeouts are processed here instead of timeout_hardclock_update()
* to avoid doing any more work at IPL_CLOCK than absolutely necessary.
* Down here at IPL_SOFTCLOCK other interrupts can be serviced promptly
* so the system remains responsive even if there is a surge of timeouts.
*/
void
softclock(void *arg)
{
struct timeout *first_new, *to;
int needsproc, new;
first_new = NULL;
new = 0;
mtx_enter(&timeout_mutex);
if (!CIRCQ_EMPTY(&timeout_new))
first_new = timeout_from_circq(CIRCQ_FIRST(&timeout_new));
CIRCQ_CONCAT(&timeout_todo, &timeout_new);
while (!CIRCQ_EMPTY(&timeout_todo)) {
to = timeout_from_circq(CIRCQ_FIRST(&timeout_todo));
CIRCQ_REMOVE(&to->to_list);
if (to == first_new)
new = 1;
if (ISSET(to->to_flags, TIMEOUT_KCLOCK))
softclock_process_kclock_timeout(to, new);
else
softclock_process_tick_timeout(to, new);
}
tostat.tos_softclocks++;
needsproc = !CIRCQ_EMPTY(&timeout_proc);
mtx_leave(&timeout_mutex);
if (needsproc)
wakeup(&timeout_proc);
}
void
softclock_create_thread(void *arg)
{
if (kthread_create(softclock_thread, NULL, NULL, "softclock"))
panic("fork softclock");
}
void
softclock_thread(void *arg)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
struct sleep_state sls;
struct timeout *to;
int s;
KERNEL_ASSERT_LOCKED();
/* Be conservative for the moment */
CPU_INFO_FOREACH(cii, ci) {
if (CPU_IS_PRIMARY(ci))
break;
}
KASSERT(ci != NULL);
sched_peg_curproc(ci);
s = splsoftclock();
for (;;) {
sleep_setup(&sls, &timeout_proc, PSWP, "bored", 0);
sleep_finish(&sls, CIRCQ_EMPTY(&timeout_proc));
mtx_enter(&timeout_mutex);
while (!CIRCQ_EMPTY(&timeout_proc)) {
to = timeout_from_circq(CIRCQ_FIRST(&timeout_proc));
CIRCQ_REMOVE(&to->to_list);
timeout_run(to);
tostat.tos_run_thread++;
}
tostat.tos_thread_wakeups++;
mtx_leave(&timeout_mutex);
}
splx(s);
}
#ifndef SMALL_KERNEL
void
timeout_adjust_ticks(int adj)
{
struct timeout *to;
struct circq *p;
int new_ticks, b;
/* adjusting the monotonic clock backwards would be a Bad Thing */
if (adj <= 0)
return;
mtx_enter(&timeout_mutex);
new_ticks = ticks + adj;
for (b = 0; b < nitems(timeout_wheel); b++) {
p = CIRCQ_FIRST(&timeout_wheel[b]);
while (p != &timeout_wheel[b]) {
to = timeout_from_circq(p);
p = CIRCQ_FIRST(p);
/* when moving a timeout forward need to reinsert it */
if (to->to_time - ticks < adj)
to->to_time = new_ticks;
CIRCQ_REMOVE(&to->to_list);
CIRCQ_INSERT_TAIL(&timeout_todo, &to->to_list);
}
}
ticks = new_ticks;
mtx_leave(&timeout_mutex);
}
#endif
int
timeout_sysctl(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
struct timeoutstat status;
mtx_enter(&timeout_mutex);
memcpy(&status, &tostat, sizeof(status));
mtx_leave(&timeout_mutex);
return sysctl_rdstruct(oldp, oldlenp, newp, &status, sizeof(status));
}
#ifdef DDB
const char *db_kclock(int);
void db_show_callout_bucket(struct circq *);
void db_show_timeout(struct timeout *, struct circq *);
const char *db_timespec(const struct timespec *);
const char *
db_kclock(int kclock)
{
switch (kclock) {
case KCLOCK_UPTIME:
return "uptime";
default:
return "invalid";
}
}
const char *
db_timespec(const struct timespec *ts)
{
static char buf[32];
struct timespec tmp, zero;
if (ts->tv_sec >= 0) {
snprintf(buf, sizeof(buf), "%lld.%09ld",
ts->tv_sec, ts->tv_nsec);
return buf;
}
timespecclear(&zero);
timespecsub(&zero, ts, &tmp);
snprintf(buf, sizeof(buf), "-%lld.%09ld", tmp.tv_sec, tmp.tv_nsec);
return buf;
}
void
db_show_callout_bucket(struct circq *bucket)
{
struct circq *p;
CIRCQ_FOREACH(p, bucket)
db_show_timeout(timeout_from_circq(p), bucket);
}
void
db_show_timeout(struct timeout *to, struct circq *bucket)
{
struct timespec remaining;
struct kclock *kc;
char buf[8];
db_expr_t offset;
struct circq *wheel;
char *name, *where;
int width = sizeof(long) * 2;
db_find_sym_and_offset((vaddr_t)to->to_func, &name, &offset);
name = name ? name : "?";
if (bucket == &timeout_new)
where = "new";
else if (bucket == &timeout_todo)
where = "softint";
else if (bucket == &timeout_proc)
where = "thread";
else {
if (ISSET(to->to_flags, TIMEOUT_KCLOCK))
wheel = timeout_wheel_kc;
else
wheel = timeout_wheel;
snprintf(buf, sizeof(buf), "%3ld/%1ld",
(bucket - wheel) % WHEELSIZE,
(bucket - wheel) / WHEELSIZE);
where = buf;
}
if (ISSET(to->to_flags, TIMEOUT_KCLOCK)) {
kc = &timeout_kclock[to->to_kclock];
timespecsub(&to->to_abstime, &kc->kc_lastscan, &remaining);
db_printf("%20s %8s %7s 0x%0*lx %s\n",
db_timespec(&remaining), db_kclock(to->to_kclock), where,
width, (ulong)to->to_arg, name);
} else {
db_printf("%20d %8s %7s 0x%0*lx %s\n",
to->to_time - ticks, "ticks", where,
width, (ulong)to->to_arg, name);
}
}
void
db_show_callout(db_expr_t addr, int haddr, db_expr_t count, char *modif)
{
struct kclock *kc;
int width = sizeof(long) * 2 + 2;
int b, i;
db_printf("%20s %8s\n", "lastscan", "clock");
db_printf("%20d %8s\n", ticks, "ticks");
for (i = 0; i < nitems(timeout_kclock); i++) {
kc = &timeout_kclock[i];
db_printf("%20s %8s\n",
db_timespec(&kc->kc_lastscan), db_kclock(i));
}
db_printf("\n");
db_printf("%20s %8s %7s %*s %s\n",
"remaining", "clock", "wheel", width, "arg", "func");
db_show_callout_bucket(&timeout_new);
db_show_callout_bucket(&timeout_todo);
db_show_callout_bucket(&timeout_proc);
for (b = 0; b < nitems(timeout_wheel); b++)
db_show_callout_bucket(&timeout_wheel[b]);
for (b = 0; b < nitems(timeout_wheel_kc); b++)
db_show_callout_bucket(&timeout_wheel_kc[b]);
}
#endif
217
217
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
/* $OpenBSD: virtio_pci.c,v 1.31 2022/03/11 18:00:52 mpi Exp $ */
/* $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */
/*
* Copyright (c) 2012 Stefan Fritsch.
* Copyright (c) 2010 Minoura Makoto.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/mutex.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/virtio_pcireg.h>
#include <dev/pv/virtioreg.h>
#include <dev/pv/virtiovar.h>
#include <dev/pci/virtio_pcireg.h>
#define DNPRINTF(n,x...) \
do { if (VIRTIO_DEBUG >= n) printf(x); } while(0)
/*
* XXX: Before being used on big endian arches, the access to config registers
* XXX: needs to be reviewed/fixed. The non-device specific registers are
* XXX: PCI-endian while the device specific registers are native endian.
*/
#define MAX_MSIX_VECS 8
struct virtio_pci_softc;
int virtio_pci_match(struct device *, void *, void *);
void virtio_pci_attach(struct device *, struct device *, void *);
int virtio_pci_attach_09(struct virtio_pci_softc *sc, struct pci_attach_args *pa);
int virtio_pci_attach_10(struct virtio_pci_softc *sc, struct pci_attach_args *pa);
int virtio_pci_detach(struct device *, int);
void virtio_pci_kick(struct virtio_softc *, uint16_t);
int virtio_pci_adjust_config_region(struct virtio_pci_softc *);
uint8_t virtio_pci_read_device_config_1(struct virtio_softc *, int);
uint16_t virtio_pci_read_device_config_2(struct virtio_softc *, int);
uint32_t virtio_pci_read_device_config_4(struct virtio_softc *, int);
uint64_t virtio_pci_read_device_config_8(struct virtio_softc *, int);
void virtio_pci_write_device_config_1(struct virtio_softc *, int, uint8_t);
void virtio_pci_write_device_config_2(struct virtio_softc *, int, uint16_t);
void virtio_pci_write_device_config_4(struct virtio_softc *, int, uint32_t);
void virtio_pci_write_device_config_8(struct virtio_softc *, int, uint64_t);
uint16_t virtio_pci_read_queue_size(struct virtio_softc *, uint16_t);
void virtio_pci_setup_queue(struct virtio_softc *, struct virtqueue *, uint64_t);
void virtio_pci_set_status(struct virtio_softc *, int);
int virtio_pci_negotiate_features(struct virtio_softc *, const struct virtio_feature_name *);
int virtio_pci_negotiate_features_10(struct virtio_softc *, const struct virtio_feature_name *);
void virtio_pci_set_msix_queue_vector(struct virtio_pci_softc *, uint32_t, uint16_t);
void virtio_pci_set_msix_config_vector(struct virtio_pci_softc *, uint16_t);
int virtio_pci_msix_establish(struct virtio_pci_softc *, struct pci_attach_args *, int, int (*)(void *), void *);
int virtio_pci_setup_msix(struct virtio_pci_softc *, struct pci_attach_args *, int);
void virtio_pci_free_irqs(struct virtio_pci_softc *);
int virtio_pci_poll_intr(void *);
int virtio_pci_legacy_intr(void *);
int virtio_pci_legacy_intr_mpsafe(void *);
int virtio_pci_config_intr(void *);
int virtio_pci_queue_intr(void *);
int virtio_pci_shared_queue_intr(void *);
int virtio_pci_find_cap(struct virtio_pci_softc *sc, int cfg_type, void *buf, int buflen);
#if VIRTIO_DEBUG
void virtio_pci_dump_caps(struct virtio_pci_softc *sc);
#endif
enum irq_type {
IRQ_NO_MSIX,
IRQ_MSIX_SHARED, /* vec 0: config irq, vec 1 shared by all vqs */
IRQ_MSIX_PER_VQ, /* vec 0: config irq, vec n: irq of vq[n-1] */
};
struct virtio_pci_softc {
struct virtio_softc sc_sc;
pci_chipset_tag_t sc_pc;
pcitag_t sc_ptag;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
bus_size_t sc_iosize;
bus_space_tag_t sc_bars_iot[4];
bus_space_handle_t sc_bars_ioh[4];
bus_size_t sc_bars_iosize[4];
bus_space_tag_t sc_notify_iot;
bus_space_handle_t sc_notify_ioh;
bus_size_t sc_notify_iosize;
unsigned int sc_notify_off_multiplier;
bus_space_tag_t sc_devcfg_iot;
bus_space_handle_t sc_devcfg_ioh;
bus_size_t sc_devcfg_iosize;
/*
* With 0.9, the offset of the devcfg region in the io bar changes
* depending on MSI-X being enabled or not.
* With 1.0, this field is still used to remember if MSI-X is enabled
* or not.
*/
unsigned int sc_devcfg_offset;
bus_space_tag_t sc_isr_iot;
bus_space_handle_t sc_isr_ioh;
bus_size_t sc_isr_iosize;
void *sc_ih[MAX_MSIX_VECS];
enum irq_type sc_irq_type;
};
const struct cfattach virtio_pci_ca = {
sizeof(struct virtio_pci_softc),
virtio_pci_match,
virtio_pci_attach,
virtio_pci_detach,
NULL
};
struct virtio_ops virtio_pci_ops = {
virtio_pci_kick,
virtio_pci_read_device_config_1,
virtio_pci_read_device_config_2,
virtio_pci_read_device_config_4,
virtio_pci_read_device_config_8,
virtio_pci_write_device_config_1,
virtio_pci_write_device_config_2,
virtio_pci_write_device_config_4,
virtio_pci_write_device_config_8,
virtio_pci_read_queue_size,
virtio_pci_setup_queue,
virtio_pci_set_status,
virtio_pci_negotiate_features,
virtio_pci_poll_intr,
};
static inline
uint64_t _cread(struct virtio_pci_softc *sc, unsigned off, unsigned size)
{
uint64_t val;
switch (size) {
case 1:
val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, off);
break;
case 2:
val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, off);
break;
case 4:
val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
break;
case 8:
val = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
off + sizeof(uint32_t));
val <<= 32;
val += bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
break;
}
return val;
}
#define CREAD(sc, memb) _cread(sc, offsetof(struct virtio_pci_common_cfg, memb), \
sizeof(((struct virtio_pci_common_cfg *)0)->memb))
#define CWRITE(sc, memb, val) \
do { \
struct virtio_pci_common_cfg c; \
size_t off = offsetof(struct virtio_pci_common_cfg, memb); \
size_t size = sizeof(c.memb); \
\
DNPRINTF(2, "%s: %d: off %#zx size %#zx write %#llx\n", \
__func__, __LINE__, off, size, (unsigned long long)val); \
switch (size) { \
case 1: \
bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val); \
break; \
case 2: \
bus_space_write_2(sc->sc_iot, sc->sc_ioh, off, val); \
break; \
case 4: \
bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val); \
break; \
case 8: \
bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, \
(val) & 0xffffffff); \
bus_space_write_4(sc->sc_iot, sc->sc_ioh, \
(off) + sizeof(uint32_t), (uint64_t)(val) >> 32); \
break; \
} \
} while (0)
uint16_t
virtio_pci_read_queue_size(struct virtio_softc *vsc, uint16_t idx)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
uint16_t ret;
if (sc->sc_sc.sc_version_1) {
CWRITE(sc, queue_select, idx);
ret = CREAD(sc, queue_size);
} else {
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_QUEUE_SELECT, idx);
ret = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_QUEUE_SIZE);
}
return ret;
}
void
virtio_pci_setup_queue(struct virtio_softc *vsc, struct virtqueue *vq,
uint64_t addr)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
if (sc->sc_sc.sc_version_1) {
CWRITE(sc, queue_select, vq->vq_index);
if (addr == 0) {
CWRITE(sc, queue_enable, 0);
CWRITE(sc, queue_desc, 0);
CWRITE(sc, queue_avail, 0);
CWRITE(sc, queue_used, 0);
} else {
CWRITE(sc, queue_desc, addr);
CWRITE(sc, queue_avail, addr + vq->vq_availoffset);
CWRITE(sc, queue_used, addr + vq->vq_usedoffset);
CWRITE(sc, queue_enable, 1);
vq->vq_notify_off = CREAD(sc, queue_notify_off);
}
} else {
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_QUEUE_SELECT, vq->vq_index);
bus_space_write_4(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_QUEUE_ADDRESS, addr / VIRTIO_PAGE_SIZE);
}
/*
* This path is only executed if this function is called after
* the child's attach function has finished. In other cases,
* it's done in virtio_pci_setup_msix().
*/
if (sc->sc_irq_type != IRQ_NO_MSIX) {
int vec = 1;
if (sc->sc_irq_type == IRQ_MSIX_PER_VQ)
vec += vq->vq_index;
if (sc->sc_sc.sc_version_1) {
CWRITE(sc, queue_msix_vector, vec);
} else {
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_MSI_QUEUE_VECTOR, vec);
}
}
}
void
virtio_pci_set_status(struct virtio_softc *vsc, int status)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
int old = 0;
if (sc->sc_sc.sc_version_1) {
if (status != 0)
old = CREAD(sc, device_status);
CWRITE(sc, device_status, status|old);
} else {
if (status != 0)
old = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_DEVICE_STATUS);
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_DEVICE_STATUS, status|old);
}
}
int
virtio_pci_match(struct device *parent, void *match, void *aux)
{
struct pci_attach_args *pa;
pa = (struct pci_attach_args *)aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_OPENBSD &&
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_OPENBSD_CONTROL)
return 1;
if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_QUMRANET)
return 0;
/* virtio 0.9 */
if (PCI_PRODUCT(pa->pa_id) >= 0x1000 &&
PCI_PRODUCT(pa->pa_id) <= 0x103f &&
PCI_REVISION(pa->pa_class) == 0)
return 1;
/* virtio 1.0 */
if (PCI_PRODUCT(pa->pa_id) >= 0x1040 &&
PCI_PRODUCT(pa->pa_id) <= 0x107f &&
PCI_REVISION(pa->pa_class) == 1)
return 1;
return 0;
}
#if VIRTIO_DEBUG
void
virtio_pci_dump_caps(struct virtio_pci_softc *sc)
{
pci_chipset_tag_t pc = sc->sc_pc;
pcitag_t tag = sc->sc_ptag;
int offset;
union {
pcireg_t reg[4];
struct virtio_pci_cap vcap;
} v;
if (!pci_get_capability(pc, tag, PCI_CAP_VENDSPEC, &offset, &v.reg[0]))
return;
printf("\n");
do {
for (int i = 0; i < 4; i++)
v.reg[i] = pci_conf_read(pc, tag, offset + i * 4);
printf("%s: cfgoff %#x len %#x type %#x bar %#x: off %#x len %#x\n",
__func__, offset, v.vcap.cap_len, v.vcap.cfg_type, v.vcap.bar,
v.vcap.offset, v.vcap.length);
offset = v.vcap.cap_next;
} while (offset != 0);
}
#endif
int
virtio_pci_find_cap(struct virtio_pci_softc *sc, int cfg_type, void *buf, int buflen)
{
pci_chipset_tag_t pc = sc->sc_pc;
pcitag_t tag = sc->sc_ptag;
unsigned int offset, i, len;
union {
pcireg_t reg[8];
struct virtio_pci_cap vcap;
} *v = buf;
if (buflen < sizeof(struct virtio_pci_cap))
return ERANGE;
if (!pci_get_capability(pc, tag, PCI_CAP_VENDSPEC, &offset, &v->reg[0]))
return ENOENT;
do {
for (i = 0; i < 4; i++)
v->reg[i] = pci_conf_read(pc, tag, offset + i * 4);
if (v->vcap.cfg_type == cfg_type)
break;
offset = v->vcap.cap_next;
} while (offset != 0);
if (offset == 0)
return ENOENT;
if (v->vcap.cap_len > sizeof(struct virtio_pci_cap)) {
len = roundup(v->vcap.cap_len, sizeof(pcireg_t));
if (len > buflen) {
printf("%s: cap too large\n", __func__);
return ERANGE;
}
for (i = 4; i < len / sizeof(pcireg_t); i++)
v->reg[i] = pci_conf_read(pc, tag, offset + i * 4);
}
return 0;
}
#define NMAPREG ((PCI_MAPREG_END - PCI_MAPREG_START) / \
sizeof(pcireg_t))
int
virtio_pci_attach_10(struct virtio_pci_softc *sc, struct pci_attach_args *pa)
{
struct virtio_pci_cap common, isr, device;
struct virtio_pci_notify_cap notify;
int have_device_cfg = 0;
bus_size_t bars[NMAPREG] = { 0 };
int bars_idx[NMAPREG] = { 0 };
struct virtio_pci_cap *caps[] = { &common, &isr, &device, ¬ify.cap };
int i, j = 0, ret = 0;
if (virtio_pci_find_cap(sc, VIRTIO_PCI_CAP_COMMON_CFG, &common, sizeof(common)) != 0)
return ENODEV;
if (virtio_pci_find_cap(sc, VIRTIO_PCI_CAP_NOTIFY_CFG, ¬ify, sizeof(notify)) != 0)
return ENODEV;
if (virtio_pci_find_cap(sc, VIRTIO_PCI_CAP_ISR_CFG, &isr, sizeof(isr)) != 0)
return ENODEV;
if (virtio_pci_find_cap(sc, VIRTIO_PCI_CAP_DEVICE_CFG, &device, sizeof(device)) != 0)
memset(&device, 0, sizeof(device));
else
have_device_cfg = 1;
/*
* XXX Maybe there are devices that offer the pci caps but not the
* XXX VERSION_1 feature bit? Then we should check the feature bit
* XXX here and fall back to 0.9 out if not present.
*/
/* Figure out which bars we need to map */
for (i = 0; i < nitems(caps); i++) {
int bar = caps[i]->bar;
bus_size_t len = caps[i]->offset + caps[i]->length;
if (caps[i]->length == 0)
continue;
if (bars[bar] < len)
bars[bar] = len;
}
for (i = 0; i < nitems(bars); i++) {
int reg;
pcireg_t type;
if (bars[i] == 0)
continue;
reg = PCI_MAPREG_START + i * 4;
type = pci_mapreg_type(sc->sc_pc, sc->sc_ptag, reg);
if (pci_mapreg_map(pa, reg, type, 0, &sc->sc_bars_iot[j],
&sc->sc_bars_ioh[j], NULL, &sc->sc_bars_iosize[j],
bars[i])) {
printf("%s: can't map bar %u \n",
sc->sc_sc.sc_dev.dv_xname, i);
ret = EIO;
goto err;
}
bars_idx[i] = j;
j++;
}
i = bars_idx[notify.cap.bar];
if (bus_space_subregion(sc->sc_bars_iot[i], sc->sc_bars_ioh[i],
notify.cap.offset, notify.cap.length, &sc->sc_notify_ioh) != 0) {
printf("%s: can't map notify i/o space\n",
sc->sc_sc.sc_dev.dv_xname);
ret = EIO;
goto err;
}
sc->sc_notify_iosize = notify.cap.length;
sc->sc_notify_iot = sc->sc_bars_iot[i];
sc->sc_notify_off_multiplier = notify.notify_off_multiplier;
if (have_device_cfg) {
i = bars_idx[device.bar];
if (bus_space_subregion(sc->sc_bars_iot[i], sc->sc_bars_ioh[i],
device.offset, device.length, &sc->sc_devcfg_ioh) != 0) {
printf("%s: can't map devcfg i/o space\n",
sc->sc_sc.sc_dev.dv_xname);
ret = EIO;
goto err;
}
sc->sc_devcfg_iosize = device.length;
sc->sc_devcfg_iot = sc->sc_bars_iot[i];
}
i = bars_idx[isr.bar];
if (bus_space_subregion(sc->sc_bars_iot[i], sc->sc_bars_ioh[i],
isr.offset, isr.length, &sc->sc_isr_ioh) != 0) {
printf("%s: can't map isr i/o space\n",
sc->sc_sc.sc_dev.dv_xname);
ret = EIO;
goto err;
}
sc->sc_isr_iosize = isr.length;
sc->sc_isr_iot = sc->sc_bars_iot[i];
i = bars_idx[common.bar];
if (bus_space_subregion(sc->sc_bars_iot[i], sc->sc_bars_ioh[i],
common.offset, common.length, &sc->sc_ioh) != 0) {
printf("%s: can't map common i/o space\n",
sc->sc_sc.sc_dev.dv_xname);
ret = EIO;
goto err;
}
sc->sc_iosize = common.length;
sc->sc_iot = sc->sc_bars_iot[i];
sc->sc_sc.sc_version_1 = 1;
return 0;
err:
/* there is no pci_mapreg_unmap() */
return ret;
}
int
virtio_pci_attach_09(struct virtio_pci_softc *sc, struct pci_attach_args *pa)
{
struct virtio_softc *vsc = &sc->sc_sc;
pcireg_t type;
type = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START);
if (pci_mapreg_map(pa, PCI_MAPREG_START, type, 0,
&sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_iosize, 0)) {
printf("%s: can't map i/o space\n", vsc->sc_dev.dv_xname);
return EIO;
}
if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_QUEUE_NOTIFY, 2, &sc->sc_notify_ioh) != 0) {
printf("%s: can't map notify i/o space\n",
vsc->sc_dev.dv_xname);
return EIO;
}
sc->sc_notify_iosize = 2;
sc->sc_notify_iot = sc->sc_iot;
if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_ISR_STATUS, 1, &sc->sc_isr_ioh) != 0) {
printf("%s: can't map isr i/o space\n",
vsc->sc_dev.dv_xname);
return EIO;
}
sc->sc_isr_iosize = 1;
sc->sc_isr_iot = sc->sc_iot;
return 0;
}
void
virtio_pci_attach(struct device *parent, struct device *self, void *aux)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)self;
struct virtio_softc *vsc = &sc->sc_sc;
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t tag = pa->pa_tag;
int revision, ret = ENODEV;
pcireg_t id;
char const *intrstr;
pci_intr_handle_t ih;
revision = PCI_REVISION(pa->pa_class);
switch (revision) {
case 0:
/* subsystem ID shows what I am */
id = PCI_PRODUCT(pci_conf_read(pc, tag, PCI_SUBSYS_ID_REG));
break;
case 1:
id = PCI_PRODUCT(pa->pa_id) - 0x1040;
break;
default:
printf("unknown revision 0x%02x; giving up\n", revision);
return;
}
sc->sc_pc = pc;
sc->sc_ptag = pa->pa_tag;
vsc->sc_dmat = pa->pa_dmat;
#if defined(__i386__) || defined(__amd64__)
/*
* For virtio, ignore normal MSI black/white-listing depending on the
* PCI bridge but enable it unconditionally.
*/
pa->pa_flags |= PCI_FLAGS_MSI_ENABLED;
#endif
#if VIRTIO_DEBUG
virtio_pci_dump_caps(sc);
#endif
vsc->sc_ops = &virtio_pci_ops;
if ((vsc->sc_dev.dv_cfdata->cf_flags & VIRTIO_CF_NO_VERSION_1) == 0 &&
(revision == 1 ||
(vsc->sc_dev.dv_cfdata->cf_flags & VIRTIO_CF_PREFER_VERSION_1))) {
ret = virtio_pci_attach_10(sc, pa);
}
if (ret != 0 && revision == 0) {
/* revision 0 means 0.9 only or both 0.9 and 1.0 */
ret = virtio_pci_attach_09(sc, pa);
}
if (ret != 0) {
printf(": Cannot attach (%d)\n", ret);
return;
}
sc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI;
sc->sc_irq_type = IRQ_NO_MSIX;
if (virtio_pci_adjust_config_region(sc) != 0)
return;
virtio_device_reset(vsc);
virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
printf("\n");
vsc->sc_childdevid = id;
vsc->sc_child = NULL;
config_found(self, sc, NULL);
if (vsc->sc_child == NULL) {
printf("%s: no matching child driver; not configured\n",
vsc->sc_dev.dv_xname);
goto fail_1;
}
if (vsc->sc_child == VIRTIO_CHILD_ERROR) {
printf("%s: virtio configuration failed\n",
vsc->sc_dev.dv_xname);
goto fail_1;
}
if (virtio_pci_setup_msix(sc, pa, 0) == 0) {
sc->sc_irq_type = IRQ_MSIX_PER_VQ;
intrstr = "msix per-VQ";
} else if (virtio_pci_setup_msix(sc, pa, 1) == 0) {
sc->sc_irq_type = IRQ_MSIX_SHARED;
intrstr = "msix shared";
} else {
int (*ih_func)(void *) = virtio_pci_legacy_intr;
if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) {
printf("%s: couldn't map interrupt\n", vsc->sc_dev.dv_xname);
goto fail_2;
}
intrstr = pci_intr_string(pc, ih);
/*
* We always set the IPL_MPSAFE flag in order to do the relatively
* expensive ISR read without lock, and then grab the kernel lock in
* the interrupt handler.
*/
if (vsc->sc_ipl & IPL_MPSAFE)
ih_func = virtio_pci_legacy_intr_mpsafe;
sc->sc_ih[0] = pci_intr_establish(pc, ih, vsc->sc_ipl | IPL_MPSAFE,
ih_func, sc, vsc->sc_dev.dv_xname);
if (sc->sc_ih[0] == NULL) {
printf("%s: couldn't establish interrupt", vsc->sc_dev.dv_xname);
if (intrstr != NULL)
printf(" at %s", intrstr);
printf("\n");
goto fail_2;
}
}
printf("%s: %s\n", vsc->sc_dev.dv_xname, intrstr);
virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK);
return;
fail_2:
config_detach(vsc->sc_child, 0);
fail_1:
/* no pci_mapreg_unmap() or pci_intr_unmap() */
virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
}
int
virtio_pci_detach(struct device *self, int flags)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)self;
struct virtio_softc *vsc = &sc->sc_sc;
int r;
if (vsc->sc_child != 0 && vsc->sc_child != VIRTIO_CHILD_ERROR) {
r = config_detach(vsc->sc_child, flags);
if (r)
return r;
}
KASSERT(vsc->sc_child == 0 || vsc->sc_child == VIRTIO_CHILD_ERROR);
KASSERT(vsc->sc_vqs == 0);
virtio_pci_free_irqs(sc);
if (sc->sc_iosize)
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
sc->sc_iosize = 0;
return 0;
}
int
virtio_pci_adjust_config_region(struct virtio_pci_softc *sc)
{
if (sc->sc_sc.sc_version_1)
return 0;
sc->sc_devcfg_iosize = sc->sc_iosize - sc->sc_devcfg_offset;
sc->sc_devcfg_iot = sc->sc_iot;
if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, sc->sc_devcfg_offset,
sc->sc_devcfg_iosize, &sc->sc_devcfg_ioh) != 0) {
printf("%s: can't map config i/o space\n",
sc->sc_sc.sc_dev.dv_xname);
return 1;
}
return 0;
}
/*
* Feature negotiation.
* Prints available / negotiated features if guest_feature_names != NULL and
* VIRTIO_DEBUG is 1
*/
int
virtio_pci_negotiate_features(struct virtio_softc *vsc,
const struct virtio_feature_name *guest_feature_names)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
uint64_t host, negotiated;
vsc->sc_active_features = 0;
/*
* We enable indirect descriptors by default. They can be switched
* off by setting bit 1 in the driver flags, see config(8)
*/
if (!(vsc->sc_dev.dv_cfdata->cf_flags & VIRTIO_CF_NO_INDIRECT) &&
!(vsc->sc_child->dv_cfdata->cf_flags & VIRTIO_CF_NO_INDIRECT)) {
vsc->sc_driver_features |= VIRTIO_F_RING_INDIRECT_DESC;
} else if (guest_feature_names != NULL) {
printf(" RingIndirectDesc disabled by UKC");
}
/*
* The driver must add VIRTIO_F_RING_EVENT_IDX if it supports it.
* If it did, check if it is disabled by bit 2 in the driver flags.
*/
if ((vsc->sc_driver_features & VIRTIO_F_RING_EVENT_IDX) &&
((vsc->sc_dev.dv_cfdata->cf_flags & VIRTIO_CF_NO_EVENT_IDX) ||
(vsc->sc_child->dv_cfdata->cf_flags & VIRTIO_CF_NO_EVENT_IDX))) {
if (guest_feature_names != NULL)
printf(" RingEventIdx disabled by UKC");
vsc->sc_driver_features &= ~VIRTIO_F_RING_EVENT_IDX;
}
if (vsc->sc_version_1) {
return virtio_pci_negotiate_features_10(vsc,
guest_feature_names);
}
/* virtio 0.9 only */
host = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_DEVICE_FEATURES);
negotiated = host & vsc->sc_driver_features;
#if VIRTIO_DEBUG
if (guest_feature_names)
virtio_log_features(host, negotiated, guest_feature_names);
#endif
bus_space_write_4(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_GUEST_FEATURES, negotiated);
vsc->sc_active_features = negotiated;
if (negotiated & VIRTIO_F_RING_INDIRECT_DESC)
vsc->sc_indirect = 1;
else
vsc->sc_indirect = 0;
return 0;
}
int
virtio_pci_negotiate_features_10(struct virtio_softc *vsc,
const struct virtio_feature_name *guest_feature_names)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
uint64_t host, negotiated;
vsc->sc_driver_features |= VIRTIO_F_VERSION_1;
/* notify on empty is 0.9 only */
vsc->sc_driver_features &= ~VIRTIO_F_NOTIFY_ON_EMPTY;
CWRITE(sc, device_feature_select, 0);
host = CREAD(sc, device_feature);
CWRITE(sc, device_feature_select, 1);
host |= (uint64_t)CREAD(sc, device_feature) << 32;
negotiated = host & vsc->sc_driver_features;
#if VIRTIO_DEBUG
if (guest_feature_names)
virtio_log_features(host, negotiated, guest_feature_names);
#endif
CWRITE(sc, driver_feature_select, 0);
CWRITE(sc, driver_feature, negotiated & 0xffffffff);
CWRITE(sc, driver_feature_select, 1);
CWRITE(sc, driver_feature, negotiated >> 32);
virtio_pci_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK);
if ((CREAD(sc, device_status) &
VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) == 0) {
printf("%s: Feature negotiation failed\n",
vsc->sc_dev.dv_xname);
CWRITE(sc, device_status, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
return ENXIO;
}
vsc->sc_active_features = negotiated;
if (negotiated & VIRTIO_F_RING_INDIRECT_DESC)
vsc->sc_indirect = 1;
else
vsc->sc_indirect = 0;
if ((negotiated & VIRTIO_F_VERSION_1) == 0) {
#if VIRTIO_DEBUG
printf("%s: Host rejected Version_1\n", __func__);
#endif
CWRITE(sc, device_status, VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
return EINVAL;
}
return 0;
}
/*
* Device configuration registers.
*/
uint8_t
virtio_pci_read_device_config_1(struct virtio_softc *vsc, int index)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
return bus_space_read_1(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index);
}
uint16_t
virtio_pci_read_device_config_2(struct virtio_softc *vsc, int index)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
return bus_space_read_2(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index);
}
uint32_t
virtio_pci_read_device_config_4(struct virtio_softc *vsc, int index)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
return bus_space_read_4(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index);
}
uint64_t
virtio_pci_read_device_config_8(struct virtio_softc *vsc, int index)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
uint64_t r;
r = bus_space_read_4(sc->sc_devcfg_iot, sc->sc_devcfg_ioh,
index + sizeof(uint32_t));
r <<= 32;
r += bus_space_read_4(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index);
return r;
}
void
virtio_pci_write_device_config_1(struct virtio_softc *vsc, int index,
uint8_t value)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
bus_space_write_1(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index, value);
}
void
virtio_pci_write_device_config_2(struct virtio_softc *vsc, int index,
uint16_t value)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
bus_space_write_2(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index, value);
}
void
virtio_pci_write_device_config_4(struct virtio_softc *vsc,
int index, uint32_t value)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
bus_space_write_4(sc->sc_devcfg_iot, sc->sc_devcfg_ioh, index, value);
}
void
virtio_pci_write_device_config_8(struct virtio_softc *vsc,
int index, uint64_t value)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
bus_space_write_4(sc->sc_devcfg_iot, sc->sc_devcfg_ioh,
index, value & 0xffffffff);
bus_space_write_4(sc->sc_devcfg_iot, sc->sc_devcfg_ioh,
index + sizeof(uint32_t), value >> 32);
}
int
virtio_pci_msix_establish(struct virtio_pci_softc *sc,
struct pci_attach_args *pa, int idx, int (*handler)(void *), void *ih_arg)
{
struct virtio_softc *vsc = &sc->sc_sc;
pci_intr_handle_t ih;
if (pci_intr_map_msix(pa, idx, &ih) != 0) {
#if VIRTIO_DEBUG
printf("%s[%d]: pci_intr_map_msix failed\n",
vsc->sc_dev.dv_xname, idx);
#endif
return 1;
}
sc->sc_ih[idx] = pci_intr_establish(sc->sc_pc, ih, vsc->sc_ipl,
handler, ih_arg, vsc->sc_dev.dv_xname);
if (sc->sc_ih[idx] == NULL) {
printf("%s[%d]: couldn't establish msix interrupt\n",
vsc->sc_dev.dv_xname, idx);
return 1;
}
return 0;
}
void
virtio_pci_set_msix_queue_vector(struct virtio_pci_softc *sc, uint32_t idx, uint16_t vector)
{
if (sc->sc_sc.sc_version_1) {
CWRITE(sc, queue_select, idx);
CWRITE(sc, queue_msix_vector, vector);
} else {
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_CONFIG_QUEUE_SELECT, idx);
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_MSI_QUEUE_VECTOR, vector);
}
}
void
virtio_pci_set_msix_config_vector(struct virtio_pci_softc *sc, uint16_t vector)
{
if (sc->sc_sc.sc_version_1) {
CWRITE(sc, config_msix_vector, vector);
} else {
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
VIRTIO_MSI_CONFIG_VECTOR, vector);
}
}
void
virtio_pci_free_irqs(struct virtio_pci_softc *sc)
{
struct virtio_softc *vsc = &sc->sc_sc;
int i;
if (sc->sc_devcfg_offset == VIRTIO_CONFIG_DEVICE_CONFIG_MSI) {
for (i = 0; i < vsc->sc_nvqs; i++) {
virtio_pci_set_msix_queue_vector(sc, i,
VIRTIO_MSI_NO_VECTOR);
}
}
for (i = 0; i < MAX_MSIX_VECS; i++) {
if (sc->sc_ih[i]) {
pci_intr_disestablish(sc->sc_pc, sc->sc_ih[i]);
sc->sc_ih[i] = NULL;
}
}
sc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI;
virtio_pci_adjust_config_region(sc);
}
int
virtio_pci_setup_msix(struct virtio_pci_softc *sc, struct pci_attach_args *pa,
int shared)
{
struct virtio_softc *vsc = &sc->sc_sc;
int i;
if (virtio_pci_msix_establish(sc, pa, 0, virtio_pci_config_intr, vsc))
return 1;
sc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI;
virtio_pci_adjust_config_region(sc);
virtio_pci_set_msix_config_vector(sc, 0);
if (shared) {
if (virtio_pci_msix_establish(sc, pa, 1,
virtio_pci_shared_queue_intr, vsc)) {
goto fail;
}
for (i = 0; i < vsc->sc_nvqs; i++)
virtio_pci_set_msix_queue_vector(sc, i, 1);
} else {
for (i = 0; i <= vsc->sc_nvqs; i++) {
if (virtio_pci_msix_establish(sc, pa, i + 1,
virtio_pci_queue_intr, &vsc->sc_vqs[i])) {
goto fail;
}
virtio_pci_set_msix_queue_vector(sc, i, i + 1);
}
}
return 0;
fail:
virtio_pci_free_irqs(sc);
return 1;
}
/*
* Interrupt handler.
*/
/*
* Only used without MSI-X
*/
int
virtio_pci_legacy_intr(void *arg)
{
struct virtio_pci_softc *sc = arg;
struct virtio_softc *vsc = &sc->sc_sc;
int isr, r = 0;
/* check and ack the interrupt */
isr = bus_space_read_1(sc->sc_isr_iot, sc->sc_isr_ioh, 0);
if (isr == 0)
return 0;
KERNEL_LOCK();
if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) &&
(vsc->sc_config_change != NULL)) {
r = (vsc->sc_config_change)(vsc);
}
r |= virtio_check_vqs(vsc);
KERNEL_UNLOCK();
return r;
}
int
virtio_pci_legacy_intr_mpsafe(void *arg)
{
struct virtio_pci_softc *sc = arg;
struct virtio_softc *vsc = &sc->sc_sc;
int isr, r = 0;
/* check and ack the interrupt */
isr = bus_space_read_1(sc->sc_isr_iot, sc->sc_isr_ioh, 0);
if (isr == 0)
return 0;
if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) &&
(vsc->sc_config_change != NULL)) {
r = (vsc->sc_config_change)(vsc);
}
r |= virtio_check_vqs(vsc);
return r;
}
/*
* Only used with MSI-X
*/
int
virtio_pci_config_intr(void *arg)
{
struct virtio_softc *vsc = arg;
if (vsc->sc_config_change != NULL)
return vsc->sc_config_change(vsc);
return 0;
}
/*
* Only used with MSI-X
*/
int
virtio_pci_queue_intr(void *arg)
{
struct virtqueue *vq = arg;
if (vq->vq_done)
return (vq->vq_done)(vq);
return 0;
}
int
virtio_pci_shared_queue_intr(void *arg)
{
struct virtio_softc *vsc = arg;
return virtio_check_vqs(vsc);
}
/*
* Interrupt handler to be used when polling.
* We cannot use isr here because it is not defined in MSI-X mode.
*/
int
virtio_pci_poll_intr(void *arg)
{
struct virtio_pci_softc *sc = arg;
struct virtio_softc *vsc = &sc->sc_sc;
int r = 0;
if (vsc->sc_config_change != NULL)
r = (vsc->sc_config_change)(vsc);
r |= virtio_check_vqs(vsc);
return r;
}
void
virtio_pci_kick(struct virtio_softc *vsc, uint16_t idx)
{
struct virtio_pci_softc *sc = (struct virtio_pci_softc *)vsc;
unsigned offset = 0;
if (vsc->sc_version_1) {
offset = vsc->sc_vqs[idx].vq_notify_off *
sc->sc_notify_off_multiplier;
}
bus_space_write_2(sc->sc_notify_iot, sc->sc_notify_ioh, offset, idx);
}
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
/* $OpenBSD: ext2fs_vfsops.c,v 1.117 2022/08/12 14:30:53 visa Exp $ */
/* $NetBSD: ext2fs_vfsops.c,v 1.1 1997/06/11 09:34:07 bouyer Exp $ */
/*
* Copyright (c) 1997 Manuel Bouyer.
* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94
* Modified for ext2fs by Manuel Bouyer.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/disk.h>
#include <sys/mbuf.h>
#include <sys/fcntl.h>
#include <sys/disklabel.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/lock.h>
#include <sys/dkio.h>
#include <sys/specdev.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ext2fs/ext2fs.h>
#include <ufs/ext2fs/ext2fs_extern.h>
extern struct lock ufs_hashlock;
int ext2fs_sbupdate(struct ufsmount *, int);
static int e2fs_sbcheck(struct ext2fs *, int);
const struct vfsops ext2fs_vfsops = {
.vfs_mount = ext2fs_mount,
.vfs_start = ufs_start,
.vfs_unmount = ext2fs_unmount,
.vfs_root = ufs_root,
.vfs_quotactl = ufs_quotactl,
.vfs_statfs = ext2fs_statfs,
.vfs_sync = ext2fs_sync,
.vfs_vget = ext2fs_vget,
.vfs_fhtovp = ext2fs_fhtovp,
.vfs_vptofh = ext2fs_vptofh,
.vfs_init = ext2fs_init,
.vfs_sysctl = ext2fs_sysctl,
.vfs_checkexp = ufs_check_export,
};
struct pool ext2fs_inode_pool;
struct pool ext2fs_dinode_pool;
extern u_long ext2gennumber;
int
ext2fs_init(struct vfsconf *vfsp)
{
pool_init(&ext2fs_inode_pool, sizeof(struct inode), 0,
IPL_NONE, PR_WAITOK, "ext2inopl", NULL);
pool_init(&ext2fs_dinode_pool, sizeof(struct ext2fs_dinode), 0,
IPL_NONE, PR_WAITOK, "ext2dinopl", NULL);
return (ufs_init(vfsp));
}
/*
* Called by main() when ext2fs is going to be mounted as root.
*
* Name is updated by mount(8) after booting.
*/
#define ROOTNAME "root_device"
int
ext2fs_mountroot(void)
{
struct m_ext2fs *fs;
struct mount *mp;
struct proc *p = curproc; /* XXX */
struct ufsmount *ump;
int error;
/*
* Get vnodes for swapdev and rootdev.
*/
if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
panic("ext2fs_mountroot: can't setup bdevvp's");
if ((error = vfs_rootmountalloc("ext2fs", "root_device", &mp)) != 0) {
vrele(rootvp);
return (error);
}
if ((error = ext2fs_mountfs(rootvp, mp, p)) != 0) {
vfs_unbusy(mp);
vfs_mount_free(mp);
vrele(rootvp);
return (error);
}
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
ump = VFSTOUFS(mp);
fs = ump->um_e2fs;
memset(fs->e2fs_fsmnt, 0, sizeof(fs->e2fs_fsmnt));
strlcpy(fs->e2fs_fsmnt, mp->mnt_stat.f_mntonname, sizeof(fs->e2fs_fsmnt));
if (fs->e2fs.e2fs_rev > E2FS_REV0) {
memset(fs->e2fs.e2fs_fsmnt, 0, sizeof(fs->e2fs.e2fs_fsmnt));
strlcpy(fs->e2fs.e2fs_fsmnt, mp->mnt_stat.f_mntonname,
sizeof(fs->e2fs.e2fs_fsmnt));
}
(void)ext2fs_statfs(mp, &mp->mnt_stat, p);
vfs_unbusy(mp);
inittodr(fs->e2fs.e2fs_wtime);
return (0);
}
/*
* VFS Operations.
*
* mount system call
*/
int
ext2fs_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
struct vnode *devvp;
struct ufs_args *args = data;
struct ufsmount *ump = NULL;
struct m_ext2fs *fs;
char fname[MNAMELEN];
char fspec[MNAMELEN];
int error, flags;
/*
* If updating, check whether changing from read-only to
* read/write; if there is no device name, that's all we do.
*/
if (mp->mnt_flag & MNT_UPDATE) {
ump = VFSTOUFS(mp);
fs = ump->um_e2fs;
if (fs->e2fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
flags = WRITECLOSE;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
error = ext2fs_flushfiles(mp, flags, p);
if (error == 0 &&
ext2fs_cgupdate(ump, MNT_WAIT) == 0 &&
(fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) {
fs->e2fs.e2fs_state = E2FS_ISCLEAN;
(void)ext2fs_sbupdate(ump, MNT_WAIT);
}
if (error)
return (error);
fs->e2fs_ronly = 1;
}
if (mp->mnt_flag & MNT_RELOAD) {
error = ext2fs_reload(mp, ndp->ni_cnd.cn_cred, p);
if (error)
return (error);
}
if (fs->e2fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) {
fs->e2fs_ronly = 0;
if (fs->e2fs.e2fs_state == E2FS_ISCLEAN)
fs->e2fs.e2fs_state = 0;
else
fs->e2fs.e2fs_state = E2FS_ERRORS;
fs->e2fs_fmod = 1;
}
if (args && args->fspec == NULL) {
/*
* Process export requests.
*/
return (vfs_export(mp, &ump->um_export,
&args->export_info));
}
if (args == NULL)
goto success;
}
/*
* Not an update, or updating the name: look up the name
* and verify that it refers to a sensible block device.
*/
error = copyinstr(args->fspec, fspec, sizeof(fspec), NULL);
if (error)
goto error;
if (disk_map(fspec, fname, MNAMELEN, DM_OPENBLCK) == -1)
memcpy(fname, fspec, sizeof(fname));
NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fname, p);
if ((error = namei(ndp)) != 0)
goto error;
devvp = ndp->ni_vp;
if (devvp->v_type != VBLK) {
error = ENOTBLK;
goto error_devvp;
}
if (major(devvp->v_rdev) >= nblkdev) {
error = ENXIO;
goto error_devvp;
}
if ((mp->mnt_flag & MNT_UPDATE) == 0)
error = ext2fs_mountfs(devvp, mp, p);
else {
if (devvp != ump->um_devvp)
error = EINVAL; /* XXX needs translation */
else
vrele(devvp);
}
if (error)
goto error_devvp;
ump = VFSTOUFS(mp);
fs = ump->um_e2fs;
memset(fs->e2fs_fsmnt, 0, sizeof(fs->e2fs_fsmnt));
strlcpy(fs->e2fs_fsmnt, path, sizeof(fs->e2fs_fsmnt));
if (fs->e2fs.e2fs_rev > E2FS_REV0) {
memset(fs->e2fs.e2fs_fsmnt, 0, sizeof(fs->e2fs.e2fs_fsmnt));
strlcpy(fs->e2fs.e2fs_fsmnt, mp->mnt_stat.f_mntonname,
sizeof(fs->e2fs.e2fs_fsmnt));
}
memcpy(mp->mnt_stat.f_mntonname, fs->e2fs_fsmnt, MNAMELEN);
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, fname, MNAMELEN);
memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
memcpy(&mp->mnt_stat.mount_info.ufs_args, args, sizeof(*args));
if (fs->e2fs_fmod != 0) { /* XXX */
fs->e2fs_fmod = 0;
if (fs->e2fs.e2fs_state == 0)
fs->e2fs.e2fs_wtime = gettime();
else
printf("%s: file system not clean; please fsck(8)\n",
mp->mnt_stat.f_mntfromname);
ext2fs_cgupdate(ump, MNT_WAIT);
}
goto success;
error_devvp:
/* Error with devvp held. */
vrele(devvp);
error:
/* Error with no state to backout. */
success:
return (error);
}
int ext2fs_reload_vnode(struct vnode *, void *args);
struct ext2fs_reload_args {
struct m_ext2fs *fs;
struct proc *p;
struct ucred *cred;
struct vnode *devvp;
};
int
ext2fs_reload_vnode(struct vnode *vp, void *args)
{
struct ext2fs_reload_args *era = args;
struct buf *bp;
struct inode *ip;
int error;
caddr_t cp;
/*
* Step 4: invalidate all inactive vnodes.
*/
if (vp->v_usecount == 0) {
vgonel(vp, era->p);
return (0);
}
/*
* Step 5: invalidate all cached file data.
*/
if (vget(vp, LK_EXCLUSIVE))
return (0);
if (vinvalbuf(vp, 0, era->cred, era->p, 0, INFSLP))
panic("ext2fs_reload: dirty2");
/*
* Step 6: re-read inode data for all active vnodes.
*/
ip = VTOI(vp);
error = bread(era->devvp,
fsbtodb(era->fs, ino_to_fsba(era->fs, ip->i_number)),
(int)era->fs->e2fs_bsize, &bp);
if (error) {
vput(vp);
return (error);
}
cp = (caddr_t)bp->b_data +
(ino_to_fsbo(era->fs, ip->i_number) * EXT2_DINODE_SIZE(era->fs));
e2fs_iload(era->fs, (struct ext2fs_dinode *)cp, ip->i_e2din);
brelse(bp);
vput(vp);
return (0);
}
static off_t
ext2fs_maxfilesize(struct m_ext2fs *fs)
{
bool huge = fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_HUGE_FILE;
off_t b = fs->e2fs_bsize / 4;
off_t physically, logically;
physically = dbtob(huge ? ((1ULL << 48) - 1) : UINT_MAX);
logically = (12ULL + b + b*b + b*b*b) * fs->e2fs_bsize;
return MIN(logically, physically);
}
static int
e2fs_sbfill(struct vnode *devvp, struct m_ext2fs *fs)
{
struct buf *bp = NULL;
int i, error;
/* XXX assume hardware block size == 512 */
fs->e2fs_ncg = howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
fs->e2fs.e2fs_bpg);
fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
fs->e2fs_bsize = 1024 << fs->e2fs.e2fs_log_bsize;
fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
fs->e2fs_fsize = 1024 << fs->e2fs.e2fs_log_fsize;
fs->e2fs_qbmask = fs->e2fs_bsize - 1;
fs->e2fs_bmask = ~fs->e2fs_qbmask;
fs->e2fs_ipb = fs->e2fs_bsize / EXT2_DINODE_SIZE(fs);
fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb;
/* Re-read group descriptors from the disk. */
fs->e2fs_ngdb = howmany(fs->e2fs_ncg,
fs->e2fs_bsize / sizeof(struct ext2_gd));
fs->e2fs_gd = mallocarray(fs->e2fs_ngdb, fs->e2fs_bsize,
M_UFSMNT, M_WAITOK);
for (i = 0; i < fs->e2fs_ngdb; ++i) {
daddr_t dblk = ((fs->e2fs_bsize > 1024) ? 0 : 1) + i + 1;
size_t gdesc = i * fs->e2fs_bsize / sizeof(struct ext2_gd);
struct ext2_gd *gd;
error = bread(devvp, fsbtodb(fs, dblk), fs->e2fs_bsize, &bp);
if (error) {
size_t gdescs_space = fs->e2fs_ngdb * fs->e2fs_bsize;
free(fs->e2fs_gd, M_UFSMNT, gdescs_space);
fs->e2fs_gd = NULL;
brelse(bp);
return (error);
}
gd = (struct ext2_gd *) bp->b_data;
e2fs_cgload(gd, fs->e2fs_gd + gdesc, fs->e2fs_bsize);
brelse(bp);
bp = NULL;
}
if (!(fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_LARGE_FILE) ||
(fs->e2fs.e2fs_rev == E2FS_REV0))
fs->e2fs_maxfilesize = INT_MAX;
else
fs->e2fs_maxfilesize = ext2fs_maxfilesize(fs);
if (fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_EXTENTS)
fs->e2fs_maxfilesize *= 4;
return (0);
}
/*
* Reload all incore data for a filesystem (used after running fsck on
* the root filesystem and finding things to fix). The filesystem must
* be mounted read-only.
*
* Things to do to update the mount:
* 1) invalidate all cached meta-data.
* 2) re-read superblock from disk.
* 3) re-read summary information from disk.
* 4) invalidate all inactive vnodes.
* 5) invalidate all cached file data.
* 6) re-read inode data for all active vnodes.
*/
int
ext2fs_reload(struct mount *mountp, struct ucred *cred, struct proc *p)
{
struct vnode *devvp;
struct buf *bp;
struct m_ext2fs *fs;
struct ext2fs *newfs;
int error;
struct ext2fs_reload_args era;
if ((mountp->mnt_flag & MNT_RDONLY) == 0)
return (EINVAL);
/*
* Step 1: invalidate all cached meta-data.
*/
devvp = VFSTOUFS(mountp)->um_devvp;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, 0, cred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error != 0)
panic("ext2fs_reload: dirty1");
/*
* Step 2: re-read superblock from disk.
*/
error = bread(devvp, (daddr_t)(SBOFF / DEV_BSIZE), SBSIZE, &bp);
if (error) {
brelse(bp);
return (error);
}
newfs = (struct ext2fs *)bp->b_data;
error = e2fs_sbcheck(newfs, (mountp->mnt_flag & MNT_RDONLY));
if (error) {
brelse(bp);
return (error);
}
fs = VFSTOUFS(mountp)->um_e2fs;
/*
* Copy in the new superblock, compute in-memory values
* and load group descriptors.
*/
e2fs_sbload(newfs, &fs->e2fs);
if ((error = e2fs_sbfill(devvp, fs)) != 0)
return (error);
era.p = p;
era.cred = cred;
era.fs = fs;
era.devvp = devvp;
error = vfs_mount_foreach_vnode(mountp, ext2fs_reload_vnode, &era);
return (error);
}
/*
* Common code for mount and mountroot
*/
int
ext2fs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p)
{
struct ufsmount *ump;
struct buf *bp;
struct ext2fs *fs;
dev_t dev;
int error, ronly;
struct ucred *cred;
dev = devvp->v_rdev;
cred = p ? p->p_ucred : NOCRED;
/*
* Disallow multiple mounts of the same device.
* Disallow mounting of a device that is currently in use
* (except for root, which might share swap device for miniroot).
* Flush out any old buffers remaining from a previous use.
*/
if ((error = vfs_mountedon(devvp)) != 0)
return (error);
if (vcount(devvp) > 1 && devvp != rootvp)
return (EBUSY);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, V_SAVE, cred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error != 0)
return (error);
ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
if (error)
return (error);
bp = NULL;
ump = NULL;
/*
* Read the superblock from disk.
*/
error = bread(devvp, (daddr_t)(SBOFF / DEV_BSIZE), SBSIZE, &bp);
if (error)
goto out;
fs = (struct ext2fs *)bp->b_data;
error = e2fs_sbcheck(fs, ronly);
if (error)
goto out;
ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK | M_ZERO);
ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT,
M_WAITOK | M_ZERO);
/*
* Copy in the superblock, compute in-memory values
* and load group descriptors.
*/
e2fs_sbload(fs, &ump->um_e2fs->e2fs);
if ((error = e2fs_sbfill(devvp, ump->um_e2fs)) != 0)
goto out;
brelse(bp);
bp = NULL;
fs = &ump->um_e2fs->e2fs;
ump->um_e2fs->e2fs_ronly = ronly;
ump->um_fstype = UM_EXT2FS;
if (ronly == 0) {
if (fs->e2fs_state == E2FS_ISCLEAN)
fs->e2fs_state = 0;
else
fs->e2fs_state = E2FS_ERRORS;
ump->um_e2fs->e2fs_fmod = 1;
}
mp->mnt_data = ump;
mp->mnt_stat.f_fsid.val[0] = (long)dev;
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
mp->mnt_stat.f_namemax = MAXNAMLEN;
mp->mnt_flag |= MNT_LOCAL;
ump->um_mountp = mp;
ump->um_dev = dev;
ump->um_devvp = devvp;
ump->um_nindir = NINDIR(ump->um_e2fs);
ump->um_bptrtodb = ump->um_e2fs->e2fs_fsbtodb;
ump->um_seqinc = 1; /* no frags */
ump->um_maxsymlinklen = EXT2_MAXSYMLINKLEN;
devvp->v_specmountpoint = mp;
return (0);
out:
if (devvp->v_specinfo)
devvp->v_specmountpoint = NULL;
if (bp)
brelse(bp);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
VOP_UNLOCK(devvp);
if (ump) {
free(ump->um_e2fs, M_UFSMNT, sizeof *ump->um_e2fs);
free(ump, M_UFSMNT, sizeof *ump);
mp->mnt_data = NULL;
}
return (error);
}
/*
* unmount system call
*/
int
ext2fs_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct ufsmount *ump;
struct m_ext2fs *fs;
int error, flags;
size_t gdescs_space;
flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
if ((error = ext2fs_flushfiles(mp, flags, p)) != 0)
return (error);
ump = VFSTOUFS(mp);
fs = ump->um_e2fs;
gdescs_space = fs->e2fs_ngdb * fs->e2fs_bsize;
if (!fs->e2fs_ronly && ext2fs_cgupdate(ump, MNT_WAIT) == 0 &&
(fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) {
fs->e2fs.e2fs_state = E2FS_ISCLEAN;
(void) ext2fs_sbupdate(ump, MNT_WAIT);
}
if (ump->um_devvp->v_type != VBAD)
ump->um_devvp->v_specmountpoint = NULL;
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_CLOSE(ump->um_devvp, fs->e2fs_ronly ? FREAD : FREAD|FWRITE,
NOCRED, p);
vput(ump->um_devvp);
free(fs->e2fs_gd, M_UFSMNT, gdescs_space);
free(fs, M_UFSMNT, sizeof *fs);
free(ump, M_UFSMNT, sizeof *ump);
mp->mnt_data = NULL;
mp->mnt_flag &= ~MNT_LOCAL;
return (0);
}
/*
* Flush out all the files in a filesystem.
*/
int
ext2fs_flushfiles(struct mount *mp, int flags, struct proc *p)
{
struct ufsmount *ump;
int error;
ump = VFSTOUFS(mp);
/*
* Flush all the files.
*/
if ((error = vflush(mp, NULL, flags)) != 0)
return (error);
/*
* Flush filesystem metadata.
*/
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_FSYNC(ump->um_devvp, p->p_ucred, MNT_WAIT, p);
VOP_UNLOCK(ump->um_devvp);
return (error);
}
/*
* Get file system statistics.
*/
int
ext2fs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct ufsmount *ump;
struct m_ext2fs *fs;
u_int32_t overhead, overhead_per_group;
int i, ngroups;
ump = VFSTOUFS(mp);
fs = ump->um_e2fs;
if (fs->e2fs.e2fs_magic != E2FS_MAGIC)
panic("ext2fs_statfs");
/*
* Compute the overhead (FS structures)
*/
overhead_per_group = 1 /* block bitmap */ + 1 /* inode bitmap */ +
fs->e2fs_itpg;
overhead = fs->e2fs.e2fs_first_dblock +
fs->e2fs_ncg * overhead_per_group;
if (fs->e2fs.e2fs_rev > E2FS_REV0 &&
fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSE_SUPER) {
for (i = 0, ngroups = 0; i < fs->e2fs_ncg; i++) {
if (cg_has_sb(i))
ngroups++;
}
} else {
ngroups = fs->e2fs_ncg;
}
overhead += ngroups * (1 + fs->e2fs_ngdb);
sbp->f_bsize = fs->e2fs_bsize;
sbp->f_iosize = fs->e2fs_bsize;
sbp->f_blocks = fs->e2fs.e2fs_bcount - overhead;
sbp->f_bfree = fs->e2fs.e2fs_fbcount;
sbp->f_bavail = sbp->f_bfree - fs->e2fs.e2fs_rbcount;
sbp->f_files = fs->e2fs.e2fs_icount;
sbp->f_favail = sbp->f_ffree = fs->e2fs.e2fs_ficount;
copy_statfs_info(sbp, mp);
return (0);
}
int ext2fs_sync_vnode(struct vnode *vp, void *);
struct ext2fs_sync_args {
int allerror;
int waitfor;
int nlink0;
int inflight;
struct proc *p;
struct ucred *cred;
};
int
ext2fs_sync_vnode(struct vnode *vp, void *args)
{
struct ext2fs_sync_args *esa = args;
struct inode *ip;
int error, nlink0 = 0;
int s, skip = 0;
if (vp->v_type == VNON)
return (0);
ip = VTOI(vp);
if (ip->i_e2fs_nlink == 0)
nlink0 = 1;
s = splbio();
if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
LIST_EMPTY(&vp->v_dirtyblkhd)) {
skip = 1;
}
splx(s);
if (skip)
goto end;
if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT)) {
esa->inflight = MIN(esa->inflight+1, 65536);
goto end;
}
if ((error = VOP_FSYNC(vp, esa->cred, esa->waitfor, esa->p)) != 0)
esa->allerror = error;
vput(vp);
end:
esa->nlink0 = MIN(esa->nlink0 + nlink0, 65536);
return (0);
}
/*
* Go through the disk queues to initiate sandbagged IO;
* go through the inodes to write those that have been modified;
* initiate the writing of the super block if it has been modified.
*
* Should always be called with the mount point locked.
*/
int
ext2fs_sync(struct mount *mp, int waitfor, int stall,
struct ucred *cred, struct proc *p)
{
struct ufsmount *ump = VFSTOUFS(mp);
struct m_ext2fs *fs;
int error, allerror = 0, state, fmod;
struct ext2fs_sync_args esa;
fs = ump->um_e2fs;
if (fs->e2fs_ronly != 0) { /* XXX */
printf("fs = %s\n", fs->e2fs_fsmnt);
panic("update: rofs mod");
}
/*
* Write back each (modified) inode.
*/
esa.p = p;
esa.cred = cred;
esa.allerror = 0;
esa.waitfor = waitfor;
esa.nlink0 = 0;
esa.inflight = 0;
vfs_mount_foreach_vnode(mp, ext2fs_sync_vnode, &esa);
if (esa.allerror != 0)
allerror = esa.allerror;
/*
* Force stale file system control information to be flushed.
*/
if (waitfor != MNT_LAZY) {
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0)
allerror = error;
VOP_UNLOCK(ump->um_devvp);
}
/*
* Write back modified superblock.
*/
state = fs->e2fs.e2fs_state;
fmod = fs->e2fs_fmod;
if (stall && fs->e2fs_ronly == 0) {
fs->e2fs_fmod = 1;
if (allerror == 0 && esa.nlink0 == 0 && esa.inflight == 0) {
if ((fs->e2fs.e2fs_state & E2FS_ERRORS) == 0)
fs->e2fs.e2fs_state = E2FS_ISCLEAN;
#if 0
printf("%s force clean (dangling %d inflight %d)\n",
mp->mnt_stat.f_mntonname, esa.nlink0, esa.inflight);
#endif
} else {
fs->e2fs.e2fs_state = 0;
#if 0
printf("%s force dirty (dangling %d inflight %d)\n",
mp->mnt_stat.f_mntonname, esa.nlink0, esa.inflight);
#endif
}
}
if (fs->e2fs_fmod != 0) {
fs->e2fs_fmod = 0;
fs->e2fs.e2fs_wtime = gettime();
if ((error = ext2fs_cgupdate(ump, waitfor)))
allerror = error;
}
fs->e2fs.e2fs_state = state;
fs->e2fs_fmod = fmod;
return (allerror);
}
/*
* Look up a EXT2FS dinode number to find its incore vnode, otherwise read it
* in from disk. If it is in core, wait for the lock bit to clear, then
* return the inode locked. Detection and handling of mount points must be
* done by the calling routine.
*/
int
ext2fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
struct m_ext2fs *fs;
struct inode *ip;
struct ext2fs_dinode *dp;
struct ufsmount *ump;
struct buf *bp;
struct vnode *vp;
dev_t dev;
int error;
if (ino > (ufsino_t)-1)
panic("ext2fs_vget: alien ino_t %llu",
(unsigned long long)ino);
ump = VFSTOUFS(mp);
dev = ump->um_dev;
retry:
if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
return (0);
/* Allocate a new vnode/inode. */
if ((error = getnewvnode(VT_EXT2FS, mp, &ext2fs_vops, &vp)) != 0) {
*vpp = NULL;
return (error);
}
ip = pool_get(&ext2fs_inode_pool, PR_WAITOK|PR_ZERO);
rrw_init_flags(&ip->i_lock, "inode", RWL_DUPOK | RWL_IS_VNODE);
vp->v_data = ip;
ip->i_vnode = vp;
ip->i_ump = ump;
ip->i_e2fs = fs = ump->um_e2fs;
ip->i_dev = dev;
ip->i_number = ino;
ip->i_e2fs_last_lblk = 0;
ip->i_e2fs_last_blk = 0;
/*
* Put it onto its hash chain and lock it so that other requests for
* this inode will block if they arrive while we are sleeping waiting
* for old data structures to be purged or for the contents of the
* disk portion of this inode to be read.
*/
error = ufs_ihashins(ip);
if (error) {
vrele(vp);
if (error == EEXIST)
goto retry;
return (error);
}
/* Read in the disk contents for the inode, copy into the inode. */
error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
(int)fs->e2fs_bsize, &bp);
if (error) {
/*
* The inode does not contain anything useful, so it would
* be misleading to leave it on its hash chain. With mode
* still zero, it will be unlinked and returned to the free
* list by vput().
*/
vput(vp);
brelse(bp);
*vpp = NULL;
return (error);
}
dp = (struct ext2fs_dinode *) ((char *)bp->b_data
+ EXT2_DINODE_SIZE(fs) * ino_to_fsbo(fs, ino));
ip->i_e2din = pool_get(&ext2fs_dinode_pool, PR_WAITOK);
e2fs_iload(fs, dp, ip->i_e2din);
brelse(bp);
ip->i_effnlink = ip->i_e2fs_nlink;
/*
* The fields for storing the UID and GID of an ext2fs inode are
* limited to 16 bits. To overcome this limitation, Linux decided to
* scatter the highest bits of these values into a previously reserved
* area on the disk inode. We deal with this situation by having two
* 32-bit fields *out* of the disk inode to hold the complete values.
* Now that we are reading in the inode, compute these fields.
*/
ip->i_e2fs_uid = ip->i_e2fs_uid_low | (ip->i_e2fs_uid_high << 16);
ip->i_e2fs_gid = ip->i_e2fs_gid_low | (ip->i_e2fs_gid_high << 16);
/* If the inode was deleted, reset all fields */
if (ip->i_e2fs_dtime != 0) {
ip->i_e2fs_mode = ip->i_e2fs_nblock = 0;
(void)ext2fs_setsize(ip, 0);
}
/*
* Initialize the vnode from the inode, check for aliases.
* Note that the underlying vnode may have changed.
*/
error = ext2fs_vinit(mp, &vp);
if (error) {
vput(vp);
*vpp = NULL;
return (error);
}
/*
* Finish inode initialization now that aliasing has been resolved.
*/
vref(ip->i_devvp);
/*
* Set up a generation number for this inode if it does not
* already have one. This should only happen on old filesystems.
*/
if (ip->i_e2fs_gen == 0) {
if (++ext2gennumber < (u_long)gettime())
ext2gennumber = gettime();
ip->i_e2fs_gen = ext2gennumber;
if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
ip->i_flag |= IN_MODIFIED;
}
*vpp = vp;
return (0);
}
/*
* File handle to vnode
*
* Have to be really careful about stale file handles:
* - check that the inode number is valid
* - call ext2fs_vget() to get the locked inode
* - check for an unallocated inode (i_mode == 0)
* - check that the given client host has export rights and return
* those rights via. exflagsp and credanonp
*/
int
ext2fs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
struct inode *ip;
struct vnode *nvp;
int error;
struct ufid *ufhp;
struct m_ext2fs *fs;
ufhp = (struct ufid *)fhp;
fs = VFSTOUFS(mp)->um_e2fs;
if ((ufhp->ufid_ino < EXT2_FIRSTINO && ufhp->ufid_ino != EXT2_ROOTINO) ||
ufhp->ufid_ino > fs->e2fs_ncg * fs->e2fs.e2fs_ipg)
return (ESTALE);
if ((error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) != 0) {
*vpp = NULLVP;
return (error);
}
ip = VTOI(nvp);
if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0 ||
ip->i_e2fs_gen != ufhp->ufid_gen) {
vput(nvp);
*vpp = NULLVP;
return (ESTALE);
}
*vpp = nvp;
return (0);
}
/*
* Vnode pointer to File handle
*/
/* ARGSUSED */
int
ext2fs_vptofh(struct vnode *vp, struct fid *fhp)
{
struct inode *ip;
struct ufid *ufhp;
ip = VTOI(vp);
ufhp = (struct ufid *)fhp;
ufhp->ufid_len = sizeof(struct ufid);
ufhp->ufid_ino = ip->i_number;
ufhp->ufid_gen = ip->i_e2fs_gen;
return (0);
}
/*
* no sysctl for ext2fs
*/
int
ext2fs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen, struct proc *p)
{
return (EOPNOTSUPP);
}
/*
* Write a superblock and associated information back to disk.
*/
int
ext2fs_sbupdate(struct ufsmount *mp, int waitfor)
{
struct m_ext2fs *fs = mp->um_e2fs;
struct buf *bp;
int error = 0;
bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, INFSLP);
e2fs_sbsave(&fs->e2fs, (struct ext2fs *) bp->b_data);
if (waitfor == MNT_WAIT)
error = bwrite(bp);
else
bawrite(bp);
fs->e2fs_fmod = 0;
return (error);
}
int
ext2fs_cgupdate(struct ufsmount *mp, int waitfor)
{
struct m_ext2fs *fs = mp->um_e2fs;
struct buf *bp;
int i, error = 0, allerror = 0;
allerror = ext2fs_sbupdate(mp, waitfor);
for (i = 0; i < fs->e2fs_ngdb; i++) {
bp = getblk(mp->um_devvp, fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1),
fs->e2fs_bsize, 0, INFSLP);
e2fs_cgsave(&fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)], (struct ext2_gd*)bp->b_data, fs->e2fs_bsize);
if (waitfor == MNT_WAIT)
error = bwrite(bp);
else
bawrite(bp);
}
if (!allerror && error)
allerror = error;
return (allerror);
}
/* This is called before the superblock is copied. Watch out for endianity! */
static int
e2fs_sbcheck(struct ext2fs *fs, int ronly)
{
u_int32_t mask, tmp;
int i;
tmp = letoh16(fs->e2fs_magic);
if (tmp != E2FS_MAGIC) {
printf("ext2fs: wrong magic number 0x%x\n", tmp);
return (EIO); /* XXX needs translation */
}
tmp = letoh32(fs->e2fs_log_bsize);
if (tmp > 2) {
/* skewed log(block size): 1024 -> 0 | 2048 -> 1 | 4096 -> 2 */
tmp += 10;
printf("ext2fs: wrong log2(block size) %d\n", tmp);
return (EIO); /* XXX needs translation */
}
if (fs->e2fs_bpg == 0) {
printf("ext2fs: zero blocks per group\n");
return (EIO);
}
tmp = letoh32(fs->e2fs_rev);
if (tmp > E2FS_REV1) {
printf("ext2fs: wrong revision number 0x%x\n", tmp);
return (EIO); /* XXX needs translation */
}
else if (tmp == E2FS_REV0)
return (0);
tmp = letoh32(fs->e2fs_first_ino);
if (tmp != EXT2_FIRSTINO) {
printf("ext2fs: first inode at 0x%x\n", tmp);
return (EINVAL); /* XXX needs translation */
}
tmp = letoh32(fs->e2fs_features_incompat);
mask = tmp & ~(EXT2F_INCOMPAT_SUPP | EXT4F_RO_INCOMPAT_SUPP);
if (mask) {
printf("ext2fs: unsupported incompat features: ");
for (i = 0; i < nitems(incompat); i++)
if (mask & incompat[i].mask)
printf("%s ", incompat[i].name);
printf("\n");
return (EINVAL); /* XXX needs translation */
}
if (!ronly && (tmp & EXT4F_RO_INCOMPAT_SUPP)) {
printf("ext4fs: only read-only support right now\n");
return (EROFS); /* XXX needs translation */
}
if (tmp & EXT2F_INCOMPAT_RECOVER) {
printf("ext2fs: your file system says it needs recovery\n");
if (!ronly)
return (EROFS); /* XXX needs translation */
}
tmp = letoh32(fs->e2fs_features_rocompat) & ~EXT2F_ROCOMPAT_SUPP;
if (!ronly && tmp) {
printf("ext2fs: unsupported R/O compat features: ");
for (i = 0; i < nitems(ro_compat); i++)
if (tmp & ro_compat[i].mask)
printf("%s ", ro_compat[i].name);
printf("\n");
return (EROFS); /* XXX needs translation */
}
return (0);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
/* $OpenBSD: cy.c,v 1.41 2021/09/01 16:10:39 jan Exp $ */
/*
* Copyright (c) 1996 Timo Rossi.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* cy.c
*
* Driver for Cyclades Cyclom-8/16/32 multiport serial cards
* (currently not tested with Cyclom-32 cards)
*
* Timo Rossi, 1996
*
* Supports both ISA and PCI Cyclom cards
*
* Uses CD1400 automatic CTS flow control, and
* if CY_HW_RTS is defined, uses CD1400 automatic input flow control.
* This requires a special cable that exchanges the RTS and DTR lines.
*
* Lots of debug output can be enabled by defining CY_DEBUG
* Some debugging counters (number of receive/transmit interrupts etc.)
* can be enabled by defining CY_DEBUG1
*
* This version uses the bus_space/io_??() stuff
*
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/fcntl.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/selinfo.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ic/cd1400reg.h>
#include <dev/ic/cyreg.h>
int cy_intr(void *);
int cyparam(struct tty *, struct termios *);
void cystart(struct tty *);
void cy_poll(void *);
int cy_modem_control(struct cy_port *, int, int);
void cy_enable_transmitter(struct cy_port *);
void cd1400_channel_cmd(struct cy_port *, int);
int cy_speed(speed_t, int *, int *, int);
struct cfdriver cy_cd = {
NULL, "cy", DV_TTY
};
/*
* Common probe routine
*
* returns the number of chips found.
*/
int
cy_probe_common(bus_space_tag_t memt, bus_space_handle_t memh, int bustype)
{
int cy_chip, chip_offs;
u_char firmware_ver;
int nchips;
/* Cyclom card hardware reset */
bus_space_write_1(memt, memh, CY16_RESET<<bustype, 0);
DELAY(500); /* wait for reset to complete */
bus_space_write_1(memt, memh, CY_CLEAR_INTR<<bustype, 0);
#ifdef CY_DEBUG
printf("cy: card reset done\n");
#endif
nchips = 0;
for (cy_chip = 0, chip_offs = 0;
cy_chip < CY_MAX_CD1400s;
cy_chip++, chip_offs += (CY_CD1400_MEMSPACING << bustype)) {
int i;
/* the last 4 cd1400s are 'interleaved'
with the first 4 on 32-port boards */
if (cy_chip == 4)
chip_offs -= (CY32_ADDR_FIX << bustype);
#ifdef CY_DEBUG
printf("cy: probe chip %d offset 0x%x ... ",
cy_chip, chip_offs);
#endif
/* wait until the chip is ready for command */
DELAY(1000);
if (bus_space_read_1(memt, memh, chip_offs +
((CD1400_CCR << 1) << bustype)) != 0) {
#ifdef CY_DEBUG
printf("not ready for command\n");
#endif
break;
}
/* clear the firmware version reg. */
bus_space_write_1(memt, memh, chip_offs +
((CD1400_GFRCR << 1) << bustype), 0);
/*
* On Cyclom-16 references to non-existent chip 4
* actually access chip 0 (address line 9 not decoded).
* Here we check if the clearing of chip 4 GFRCR actually
* cleared chip 0 GFRCR. In that case we have a 16 port card.
*/
if (cy_chip == 4 &&
bus_space_read_1(memt, memh, chip_offs +
((CD1400_GFRCR << 1) << bustype)) == 0)
break;
/* reset the chip */
bus_space_write_1(memt, memh, chip_offs +
((CD1400_CCR << 1) << bustype),
CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET);
/* wait for the chip to initialize itself */
for (i = 0; i < 200; i++) {
DELAY(50);
firmware_ver = bus_space_read_1(memt, memh, chip_offs +
((CD1400_GFRCR << 1) << bustype));
if ((firmware_ver & 0xf0) == 0x40) /* found a CD1400 */
break;
}
#ifdef CY_DEBUG
printf("firmware version 0x%x\n", firmware_ver);
#endif
if ((firmware_ver & 0xf0) != 0x40)
break;
/* firmware version OK, CD1400 found */
nchips++;
}
if (nchips == 0) {
#ifdef CY_DEBUG
printf("no CD1400s found\n");
#endif
return (0);
}
#ifdef CY_DEBUG
printf("found %d CD1400s\n", nchips);
#endif
return (nchips);
}
void
cy_attach(struct device *parent, struct device *self)
{
int card, port, cy_chip, num_chips, cdu, chip_offs, cy_clock;
struct cy_softc *sc = (void *)self;
card = sc->sc_dev.dv_unit;
num_chips = sc->sc_nr_cd1400s;
if (num_chips == 0)
return;
timeout_set(&sc->sc_poll_to, cy_poll, sc);
bzero(sc->sc_ports, sizeof(sc->sc_ports));
sc->sc_nports = num_chips * CD1400_NO_OF_CHANNELS;
port = 0;
for (cy_chip = 0, chip_offs = 0;
cy_chip < num_chips;
cy_chip++, chip_offs += (CY_CD1400_MEMSPACING<<sc->sc_bustype)) {
if (cy_chip == 4)
chip_offs -= (CY32_ADDR_FIX<<sc->sc_bustype);
#ifdef CY_DEBUG
printf("attach CD1400 #%d offset 0x%x\n", cy_chip, chip_offs);
#endif
sc->sc_cd1400_offs[cy_chip] = chip_offs;
/* configure port 0 as serial port
(should already be after reset) */
cd_write_reg_sc(sc, cy_chip, CD1400_GCR, 0);
/* Set cy_clock depending on firmware version */
if (cd_read_reg_sc(sc, cy_chip, CD1400_GFRCR) <= 0x46)
cy_clock = CY_CLOCK;
else
cy_clock = CY_CLOCK_60;
/* set up a receive timeout period (1ms) */
cd_write_reg_sc(sc, cy_chip, CD1400_PPR,
(cy_clock / CD1400_PPR_PRESCALER / 1000) + 1);
for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; cdu++) {
sc->sc_ports[port].cy_port_num = port;
sc->sc_ports[port].cy_memt = sc->sc_memt;
sc->sc_ports[port].cy_memh = sc->sc_memh;
sc->sc_ports[port].cy_chip_offs = chip_offs;
sc->sc_ports[port].cy_bustype = sc->sc_bustype;
sc->sc_ports[port].cy_clock = cy_clock;
/* should we initialize anything else here? */
port++;
} /* for(each port on one CD1400...) */
} /* for(each CD1400 on a card... ) */
printf(": %d ports\n", port);
/* ensure an edge for the next interrupt */
bus_space_write_1(sc->sc_memt, sc->sc_memh,
CY_CLEAR_INTR<<sc->sc_bustype, 0);
}
/*
* open routine. returns zero if successful, else error code
*/
int cyopen(dev_t, int, int, struct proc *);
int cyclose(dev_t, int, int, struct proc *);
int cyread(dev_t, struct uio *, int);
int cywrite(dev_t, struct uio *, int);
struct tty *cytty(dev_t);
int cyioctl(dev_t, u_long, caddr_t, int, struct proc *);
int cystop(struct tty *, int flag);
int
cyopen(dev_t dev, int flag, int mode, struct proc *p)
{
int card = CY_CARD(dev);
int port = CY_PORT(dev);
struct cy_softc *sc;
struct cy_port *cy;
struct tty *tp;
int s, error;
if (card >= cy_cd.cd_ndevs ||
(sc = cy_cd.cd_devs[card]) == NULL) {
return (ENXIO);
}
#ifdef CY_DEBUG
printf("%s open port %d flag 0x%x mode 0x%x\n", sc->sc_dev.dv_xname,
port, flag, mode);
#endif
cy = &sc->sc_ports[port];
s = spltty();
if (cy->cy_tty == NULL) {
cy->cy_tty = ttymalloc(0);
}
splx(s);
tp = cy->cy_tty;
tp->t_oproc = cystart;
tp->t_param = cyparam;
tp->t_dev = dev;
if (!ISSET(tp->t_state, TS_ISOPEN)) {
SET(tp->t_state, TS_WOPEN);
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG;
if (ISSET(cy->cy_openflags, TIOCFLAG_CLOCAL))
SET(tp->t_cflag, CLOCAL);
if (ISSET(cy->cy_openflags, TIOCFLAG_CRTSCTS))
SET(tp->t_cflag, CRTSCTS);
if (ISSET(cy->cy_openflags, TIOCFLAG_MDMBUF))
SET(tp->t_cflag, MDMBUF);
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
s = spltty();
/*
* Allocate input ring buffer if we don't already have one
*/
if (cy->cy_ibuf == NULL) {
cy->cy_ibuf = malloc(IBUF_SIZE, M_DEVBUF, M_NOWAIT);
if (cy->cy_ibuf == NULL) {
printf("%s: (port %d) can't allocate input buffer\n",
sc->sc_dev.dv_xname, port);
splx(s);
return (ENOMEM);
}
cy->cy_ibuf_end = cy->cy_ibuf + IBUF_SIZE;
}
/* mark the ring buffer as empty */
cy->cy_ibuf_rd_ptr = cy->cy_ibuf_wr_ptr = cy->cy_ibuf;
/* select CD1400 channel */
cd_write_reg(cy, CD1400_CAR, port & CD1400_CAR_CHAN);
/* reset the channel */
cd1400_channel_cmd(cy, CD1400_CCR_CMDRESET);
/* encode unit (port) number in LIVR */
/* there is just enough space for 5 bits (32 ports) */
cd_write_reg(cy, CD1400_LIVR, port << 3);
cy->cy_channel_control = 0;
if (!timeout_pending(&sc->sc_poll_to))
timeout_add(&sc->sc_poll_to, 1);
/* this sets parameters and raises DTR */
cyparam(tp, &tp->t_termios);
ttsetwater(tp);
/* raise RTS too */
cy_modem_control(cy, TIOCM_RTS, DMBIS);
cy->cy_carrier_stat = cd_read_reg(cy, CD1400_MSVR2);
/* enable receiver and modem change interrupts */
cd_write_reg(cy, CD1400_SRER,
CD1400_SRER_MDMCH | CD1400_SRER_RXDATA);
if (CY_DIALOUT(dev) ||
ISSET(cy->cy_openflags, TIOCFLAG_SOFTCAR) ||
ISSET(tp->t_cflag, MDMBUF) ||
ISSET(cy->cy_carrier_stat, CD1400_MSVR2_CD))
SET(tp->t_state, TS_CARR_ON);
else
CLR(tp->t_state, TS_CARR_ON);
} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0) {
return (EBUSY);
} else {
s = spltty();
}
/* wait for carrier if necessary */
if (!ISSET(flag, O_NONBLOCK)) {
while (!ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(tp->t_state, TS_CARR_ON)) {
SET(tp->t_state, TS_WOPEN);
error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
ttopen);
if (error != 0) {
splx(s);
CLR(tp->t_state, TS_WOPEN);
return (error);
}
}
}
splx(s);
return (*linesw[tp->t_line].l_open)(dev, tp, p);
}
/*
* close routine. returns zero if successful, else error code
*/
int
cyclose(dev_t dev, int flag, int mode, struct proc *p)
{
int card = CY_CARD(dev);
int port = CY_PORT(dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
struct tty *tp = cy->cy_tty;
int s;
#ifdef CY_DEBUG
printf("%s close port %d, flag 0x%x, mode 0x%x\n", sc->sc_dev.dv_xname,
port, flag, mode);
#endif
(*linesw[tp->t_line].l_close)(tp, flag, p);
s = spltty();
if (ISSET(tp->t_cflag, HUPCL) &&
!ISSET(cy->cy_openflags, TIOCFLAG_SOFTCAR)) {
/* drop DTR and RTS
(should we wait for output buffer to become empty first?) */
cy_modem_control(cy, 0, DMSET);
}
/*
* XXX should we disable modem change and
* receive interrupts here or somewhere ?
*/
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
splx(s);
ttyclose(tp);
return (0);
}
/*
* Read routine
*/
int
cyread(dev_t dev, struct uio *uio, int flag)
{
int card = CY_CARD(dev);
int port = CY_PORT(dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
struct tty *tp = cy->cy_tty;
#ifdef CY_DEBUG
printf("%s read port %d uio %p flag 0x%x\n", sc->sc_dev.dv_xname,
port, uio, flag);
#endif
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
/*
* Write routine
*/
int
cywrite(dev_t dev, struct uio *uio, int flag)
{
int card = CY_CARD(dev);
int port = CY_PORT(dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
struct tty *tp = cy->cy_tty;
#ifdef CY_DEBUG
printf("%s write port %d uio %p flag 0x%x\n", sc->sc_dev.dv_xname,
port, uio, flag);
#endif
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
/*
* return tty pointer
*/
struct tty *
cytty(dev_t dev)
{
int card = CY_CARD(dev);
int port = CY_PORT(dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
struct tty *tp = cy->cy_tty;
return (tp);
}
/*
* ioctl routine
*/
int
cyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
int card = CY_CARD(dev);
int port = CY_PORT(dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
struct tty *tp = cy->cy_tty;
int error;
#ifdef CY_DEBUG
printf("%s port %d ioctl cmd 0x%lx data %p flag 0x%x\n",
sc->sc_dev.dv_xname, port, cmd, data, flag);
#endif
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
/* XXX should not allow dropping DTR when dialin? */
switch (cmd) {
case TIOCSBRK: /* start break */
SET(cy->cy_flags, CYF_START_BREAK);
cy_enable_transmitter(cy);
break;
case TIOCCBRK: /* stop break */
SET(cy->cy_flags, CYF_END_BREAK);
cy_enable_transmitter(cy);
break;
case TIOCSDTR: /* DTR on */
cy_modem_control(cy, TIOCM_DTR, DMBIS);
break;
case TIOCCDTR: /* DTR off */
cy_modem_control(cy, TIOCM_DTR, DMBIC);
break;
case TIOCMSET: /* set new modem control line values */
cy_modem_control(cy, *((int *)data), DMSET);
break;
case TIOCMBIS: /* turn modem control bits on */
cy_modem_control(cy, *((int *)data), DMBIS);
break;
case TIOCMBIC: /* turn modem control bits off */
cy_modem_control(cy, *((int *)data), DMBIC);
break;
case TIOCMGET: /* get modem control/status line state */
*((int *)data) = cy_modem_control(cy, 0, DMGET);
break;
case TIOCGFLAGS:
*((int *)data) = cy->cy_openflags |
(CY_DIALOUT(dev) ? TIOCFLAG_SOFTCAR : 0);
break;
case TIOCSFLAGS:
error = suser(p);
if (error != 0)
return (EPERM);
cy->cy_openflags = *((int *)data) &
(TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL |
TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF);
break;
default:
return (ENOTTY);
}
return (0);
}
/*
* start output
*/
void
cystart(struct tty *tp)
{
int card = CY_CARD(tp->t_dev);
int port = CY_PORT(tp->t_dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
int s;
#ifdef CY_DEBUG
printf("%s port %d start, tty %p\n", sc->sc_dev.dv_xname, port, tp);
#endif
s = spltty();
#ifdef CY_DEBUG1
cy->cy_start_count++;
#endif
if (!ISSET(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY)) {
ttwakeupwr(tp);
if (tp->t_outq.c_cc == 0)
goto out;
SET(tp->t_state, TS_BUSY);
cy_enable_transmitter(cy);
}
out:
splx(s);
}
/*
* stop output
*/
int
cystop(struct tty *tp, int flag)
{
int card = CY_CARD(tp->t_dev);
int port = CY_PORT(tp->t_dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
int s;
#ifdef CY_DEBUG
printf("%s port %d stop tty %p flag 0x%x\n", sc->sc_dev.dv_xname,
port, tp, flag);
#endif
s = spltty();
if (ISSET(tp->t_state, TS_BUSY)) {
if (!ISSET(tp->t_state, TS_TTSTOP))
SET(tp->t_state, TS_FLUSH);
/*
* the transmit interrupt routine will disable transmit when it
* notices that CYF_STOP has been set.
*/
SET(cy->cy_flags, CYF_STOP);
}
splx(s);
return (0);
}
/*
* parameter setting routine.
* returns 0 if successful, else returns error code
*/
int
cyparam(struct tty *tp, struct termios *t)
{
int card = CY_CARD(tp->t_dev);
int port = CY_PORT(tp->t_dev);
struct cy_softc *sc = cy_cd.cd_devs[card];
struct cy_port *cy = &sc->sc_ports[port];
int ibpr, obpr, i_clk_opt, o_clk_opt;
int s, opt;
#ifdef CY_DEBUG
printf("%s port %d param tty %p termios %p\n", sc->sc_dev.dv_xname,
port, tp, t);
printf("ispeed %d ospeed %d\n", t->c_ispeed, t->c_ospeed);
#endif
if (t->c_ospeed != 0 &&
cy_speed(t->c_ospeed, &o_clk_opt, &obpr, cy->cy_clock) < 0)
return (EINVAL);
if (t->c_ispeed != 0 &&
cy_speed(t->c_ispeed, &i_clk_opt, &ibpr, cy->cy_clock) < 0)
return (EINVAL);
s = spltty();
/* hang up the line is ospeed is zero, else turn DTR on */
cy_modem_control(cy, TIOCM_DTR, (t->c_ospeed == 0 ? DMBIC : DMBIS));
/* channel was selected by the above call to cy_modem_control() */
/* cd_write_reg(cy, CD1400_CAR, port & CD1400_CAR_CHAN); */
/* set transmit speed */
if (t->c_ospeed != 0) {
cd_write_reg(cy, CD1400_TCOR, o_clk_opt);
cd_write_reg(cy, CD1400_TBPR, obpr);
}
/* set receive speed */
if (t->c_ispeed != 0) {
cd_write_reg(cy, CD1400_RCOR, i_clk_opt);
cd_write_reg(cy, CD1400_RBPR, ibpr);
}
opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN
| (ISSET(t->c_cflag, CREAD) ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS);
if (opt != cy->cy_channel_control) {
cy->cy_channel_control = opt;
cd1400_channel_cmd(cy, opt);
}
/* compute COR1 contents */
opt = 0;
if (ISSET(t->c_cflag, PARENB)) {
if (ISSET(t->c_cflag, PARODD))
opt |= CD1400_COR1_PARODD;
opt |= CD1400_COR1_PARNORMAL;
}
if (!ISSET(t->c_iflag, INPCK))
opt |= CD1400_COR1_NOINPCK; /* no parity checking */
if (ISSET(t->c_cflag, CSTOPB))
opt |= CD1400_COR1_STOP2;
switch (t->c_cflag & CSIZE) {
case CS5:
opt |= CD1400_COR1_CS5;
break;
case CS6:
opt |= CD1400_COR1_CS6;
break;
case CS7:
opt |= CD1400_COR1_CS7;
break;
default:
opt |= CD1400_COR1_CS8;
break;
}
cd_write_reg(cy, CD1400_COR1, opt);
#ifdef CY_DEBUG
printf("cor1 = 0x%x...", opt);
#endif
/*
* use the CD1400 automatic CTS flow control if CRTSCTS is set
*
* CD1400_COR2_ETC is used because breaks are generated with
* embedded transmit commands
*/
cd_write_reg(cy, CD1400_COR2,
CD1400_COR2_ETC |
(ISSET(t->c_cflag, CRTSCTS) ? CD1400_COR2_CCTS_OFLOW : 0));
cd_write_reg(cy, CD1400_COR3, RX_FIFO_THRESHOLD);
cd1400_channel_cmd(cy,
CD1400_CCR_CMDCORCHG |
CD1400_CCR_COR1 | CD1400_CCR_COR2 | CD1400_CCR_COR3);
cd_write_reg(cy, CD1400_COR4, CD1400_COR4_PFO_EXCEPTION);
cd_write_reg(cy, CD1400_COR5, 0);
/*
* set modem change option registers to generate interrupts
* on carrier detect changes.
*
* if hardware RTS handshaking is used (CY_HW_RTS, DTR and RTS lines
* exchanged), also set the handshaking threshold.
*/
#ifdef CY_HW_RTS
cd_write_reg(cy, CD1400_MCOR1, CD1400_MCOR1_CDzd |
(ISSET(t->c_cflag, CRTSCTS) ? RX_DTR_THRESHOLD : 0));
#else
cd_write_reg(cy, CD1400_MCOR1, CD1400_MCOR1_CDzd);
#endif /* CY_HW_RTS */
cd_write_reg(cy, CD1400_MCOR2, CD1400_MCOR2_CDod);
/*
* set receive timeout to approx. 2ms
* could use more complex logic here...
* (but is it actually needed or even useful?)
*/
cd_write_reg(cy, CD1400_RTPR, 2);
/*
* should do anything else here?
* XXX check MDMBUF handshaking like in com.c?
*/
splx(s);
return (0);
}
/*
* set/get modem line status
*
* bits can be: TIOCM_DTR, TIOCM_RTS, TIOCM_CTS, TIOCM_CD, TIOCM_RI, TIOCM_DSR
*
* RTS and DTR are exchanged if CY_HW_RTS is set
*
*/
int
cy_modem_control(struct cy_port *cy, int bits, int howto)
{
int s, msvr;
s = spltty();
/* select channel */
cd_write_reg(cy, CD1400_CAR, cy->cy_port_num & CD1400_CAR_CHAN);
/* does not manipulate RTS if it is used for flow control */
switch (howto) {
case DMGET:
bits = 0;
if (cy->cy_channel_control & CD1400_CCR_RCVEN)
bits |= TIOCM_LE;
msvr = cd_read_reg(cy, CD1400_MSVR2);
#ifdef CY_HW_RTS
if (cd_read_reg(cy, CD1400_MSVR1) & CD1400_MSVR1_RTS)
bits |= TIOCM_DTR;
if (msvr & CD1400_MSVR2_DTR)
bits |= TIOCM_RTS;
#else
if (cd_read_reg(cy, CD1400_MSVR1) & CD1400_MSVR1_RTS)
bits |= TIOCM_RTS;
if (msvr & CD1400_MSVR2_DTR)
bits |= TIOCM_DTR;
#endif /* CY_HW_RTS */
if (msvr & CD1400_MSVR2_CTS)
bits |= TIOCM_CTS;
if (msvr & CD1400_MSVR2_CD)
bits |= TIOCM_CD;
if (msvr & CD1400_MSVR2_DSR) /* not connected on some
Cyclom cards? */
bits |= TIOCM_DSR;
if (msvr & CD1400_MSVR2_RI) /* not connected on
Cyclom-8Y cards? */
bits |= TIOCM_RI;
splx(s);
return (bits);
case DMSET: /* replace old values with new ones */
#ifdef CY_HW_RTS
if (!ISSET(cy->cy_tty->t_cflag, CRTSCTS))
cd_write_reg(cy, CD1400_MSVR2,
((bits & TIOCM_RTS) ? CD1400_MSVR2_DTR : 0));
cd_write_reg(cy, CD1400_MSVR1,
((bits & TIOCM_DTR) ? CD1400_MSVR1_RTS : 0));
#else
if (!ISSET(cy->cy_tty->t_cflag, CRTSCTS))
cd_write_reg(cy, CD1400_MSVR1,
((bits & TIOCM_RTS) ? CD1400_MSVR1_RTS : 0));
cd_write_reg(cy, CD1400_MSVR2,
((bits & TIOCM_DTR) ? CD1400_MSVR2_DTR : 0));
#endif /* CY_HW_RTS */
break;
case DMBIS: /* set bits */
#ifdef CY_HW_RTS
if (!ISSET(cy->cy_tty->t_cflag, CRTSCTS) &&
(bits & TIOCM_RTS) != 0)
cd_write_reg(cy, CD1400_MSVR2, CD1400_MSVR2_DTR);
if (bits & TIOCM_DTR)
cd_write_reg(cy, CD1400_MSVR1, CD1400_MSVR1_RTS);
#else
if (!ISSET(cy->cy_tty->t_cflag, CRTSCTS) &&
(bits & TIOCM_RTS) != 0)
cd_write_reg(cy, CD1400_MSVR1, CD1400_MSVR1_RTS);
if (bits & TIOCM_DTR)
cd_write_reg(cy, CD1400_MSVR2, CD1400_MSVR2_DTR);
#endif /* CY_HW_RTS */
break;
case DMBIC: /* clear bits */
#ifdef CY_HW_RTS
if (!ISSET(cy->cy_tty->t_cflag, CRTSCTS) &&
(bits & TIOCM_RTS))
cd_write_reg(cy, CD1400_MSVR2, 0);
if (bits & TIOCM_DTR)
cd_write_reg(cy, CD1400_MSVR1, 0);
#else
if (!ISSET(cy->cy_tty->t_cflag, CRTSCTS) &&
(bits & TIOCM_RTS))
cd_write_reg(cy, CD1400_MSVR1, 0);
if (bits & TIOCM_DTR)
cd_write_reg(cy, CD1400_MSVR2, 0);
#endif /* CY_HW_RTS */
break;
}
splx(s);
return (0);
}
/*
* Upper-level handler loop (called from timer interrupt?)
* This routine is common for multiple cards
*/
void
cy_poll(void *arg)
{
int port;
struct cy_softc *sc = arg;
struct cy_port *cy;
struct tty *tp;
static int counter = 0;
#ifdef CY_DEBUG1
int did_something;
#endif
int s;
s = spltty();
if (sc->sc_events == 0 && ++counter < 200) {
splx(s);
goto out;
}
sc->sc_events = 0;
splx(s);
#ifdef CY_DEBUG1
sc->sc_poll_count1++;
did_something = 0;
#endif
for (port = 0; port < sc->sc_nports; port++) {
cy = &sc->sc_ports[port];
if ((tp = cy->cy_tty) == NULL || cy->cy_ibuf == NULL ||
!ISSET(tp->t_state, TS_ISOPEN | TS_WOPEN))
continue;
/*
* handle received data
*/
while (cy->cy_ibuf_rd_ptr != cy->cy_ibuf_wr_ptr) {
u_char line_stat;
int chr;
line_stat = cy->cy_ibuf_rd_ptr[0];
chr = cy->cy_ibuf_rd_ptr[1];
if (line_stat &
(CD1400_RDSR_BREAK|CD1400_RDSR_FE))
chr |= TTY_FE;
if (line_stat & CD1400_RDSR_PE)
chr |= TTY_PE;
/*
* on an overrun error the data is treated as
* good just as it should be.
*/
#ifdef CY_DEBUG
printf("%s port %d ttyinput 0x%x\n",
sc->sc_dev.dv_xname, port, chr);
#endif
(*linesw[tp->t_line].l_rint)(chr, tp);
s = spltty(); /* really necessary? */
if ((cy->cy_ibuf_rd_ptr += 2) ==
cy->cy_ibuf_end)
cy->cy_ibuf_rd_ptr = cy->cy_ibuf;
splx(s);
#ifdef CY_DEBUG1
did_something = 1;
#endif
}
#ifndef CY_HW_RTS
/*
* If we don't have any received data in ibuf and
* CRTSCTS is on and RTS is turned off, it is time
* to turn RTS back on
*/
if (ISSET(tp->t_cflag, CRTSCTS)) {
/* we can't use cy_modem_control() here as it
doesn't change RTS if RTSCTS is on */
cd_write_reg(cy, CD1400_CAR,
port & CD1400_CAR_CHAN);
if ((cd_read_reg(cy,
CD1400_MSVR1) & CD1400_MSVR1_RTS) == 0) {
cd_write_reg(cy, CD1400_MSVR1,
CD1400_MSVR1_RTS);
#ifdef CY_DEBUG1
did_something = 1;
#endif
}
}
#endif /* CY_HW_RTS */
/*
* handle carrier changes
*/
s = spltty();
if (ISSET(cy->cy_flags, CYF_CARRIER_CHANGED)) {
int carrier;
CLR(cy->cy_flags, CYF_CARRIER_CHANGED);
splx(s);
carrier = ((cy->cy_carrier_stat &
CD1400_MSVR2_CD) != 0);
#ifdef CY_DEBUG
printf("%s: cy_poll: carrier change "
"(port %d, carrier %d)\n",
sc->sc_dev.dv_xname, port, carrier);
#endif
if (CY_DIALIN(tp->t_dev) &&
!(*linesw[tp->t_line].l_modem)(tp, carrier))
cy_modem_control(cy, TIOCM_DTR, DMBIC);
#ifdef CY_DEBUG1
did_something = 1;
#endif
} else {
splx(s);
}
s = spltty();
if (ISSET(cy->cy_flags, CYF_START)) {
CLR(cy->cy_flags, CYF_START);
splx(s);
(*linesw[tp->t_line].l_start)(tp);
#ifdef CY_DEBUG1
did_something = 1;
#endif
} else {
splx(s);
}
/* could move this to even upper level... */
if (cy->cy_fifo_overruns) {
cy->cy_fifo_overruns = 0;
/* doesn't report overrun count,
but shouldn't really matter */
log(LOG_WARNING, "%s: port %d fifo overrun\n",
sc->sc_dev.dv_xname, port);
}
if (cy->cy_ibuf_overruns) {
cy->cy_ibuf_overruns = 0;
log(LOG_WARNING, "%s: port %d ibuf overrun\n",
sc->sc_dev.dv_xname, port);
}
} /* for(port...) */
#ifdef CY_DEBUG1
if (did_something && counter >= 200)
sc->sc_poll_count2++;
#endif
counter = 0;
out:
timeout_add(&sc->sc_poll_to, 1);
}
/*
* hardware interrupt routine
*/
int
cy_intr(void *arg)
{
struct cy_softc *sc = arg;
struct cy_port *cy;
int cy_chip, stat;
int int_serviced = -1;
/*
* Check interrupt status of each CD1400 chip on this card
* (multiple cards cannot share the same interrupt)
*/
for (cy_chip = 0; cy_chip < sc->sc_nr_cd1400s; cy_chip++) {
stat = cd_read_reg_sc(sc, cy_chip, CD1400_SVRR);
if (stat == 0)
continue;
if (ISSET(stat, CD1400_SVRR_RXRDY)) {
u_char save_car, save_rir, serv_type;
u_char line_stat, recv_data, n_chars;
u_char *buf_p;
save_rir = cd_read_reg_sc(sc, cy_chip, CD1400_RIR);
save_car = cd_read_reg_sc(sc, cy_chip, CD1400_CAR);
/* enter rx service */
cd_write_reg_sc(sc, cy_chip, CD1400_CAR, save_rir);
serv_type = cd_read_reg_sc(sc, cy_chip, CD1400_RIVR);
cy = &sc->sc_ports[serv_type >> 3];
#ifdef CY_DEBUG1
cy->cy_rx_int_count++;
#endif
buf_p = cy->cy_ibuf_wr_ptr;
if (ISSET(serv_type, CD1400_RIVR_EXCEPTION)) {
line_stat = cd_read_reg(cy, CD1400_RDSR);
recv_data = cd_read_reg(cy, CD1400_RDSR);
if (cy->cy_tty == NULL ||
!ISSET(cy->cy_tty->t_state, TS_ISOPEN))
goto end_rx_serv;
#ifdef CY_DEBUG
printf("%s port %d recv exception, "
"line_stat 0x%x, char 0x%x\n",
sc->sc_dev.dv_xname, cy->cy_port_num,
line_stat, recv_data);
#endif
if (ISSET(line_stat, CD1400_RDSR_OE))
cy->cy_fifo_overruns++;
*buf_p++ = line_stat;
*buf_p++ = recv_data;
if (buf_p == cy->cy_ibuf_end)
buf_p = cy->cy_ibuf;
if (buf_p == cy->cy_ibuf_rd_ptr) {
if (buf_p == cy->cy_ibuf)
buf_p = cy->cy_ibuf_end;
buf_p -= 2;
cy->cy_ibuf_overruns++;
}
sc->sc_events = 1;
} else { /* no exception, received data OK */
n_chars = cd_read_reg(cy, CD1400_RDCR);
/* If no tty or not open, discard data */
if (cy->cy_tty == NULL ||
!ISSET(cy->cy_tty->t_state, TS_ISOPEN)) {
while (n_chars--)
cd_read_reg(cy, CD1400_RDSR);
goto end_rx_serv;
}
#ifdef CY_DEBUG
printf("%s port %d receive ok %d chars\n",
sc->sc_dev.dv_xname, cy->cy_port_num,
n_chars);
#endif
while (n_chars--) {
*buf_p++ = 0; /* status: OK */
*buf_p++ = cd_read_reg(cy,
CD1400_RDSR); /* data byte */
if (buf_p == cy->cy_ibuf_end)
buf_p = cy->cy_ibuf;
if (buf_p == cy->cy_ibuf_rd_ptr) {
if (buf_p == cy->cy_ibuf)
buf_p = cy->cy_ibuf_end;
buf_p -= 2;
cy->cy_ibuf_overruns++;
break;
}
}
sc->sc_events = 1;
}
cy->cy_ibuf_wr_ptr = buf_p;
#ifndef CY_HW_RTS
/* RTS handshaking for incoming data */
if (ISSET(cy->cy_tty->t_cflag, CRTSCTS)) {
int bf;
bf = buf_p - cy->cy_ibuf_rd_ptr;
if (bf < 0)
bf += IBUF_SIZE;
if (bf > (IBUF_SIZE/2)) /* turn RTS off */
cd_write_reg(cy, CD1400_MSVR1, 0);
}
#endif /* CY_HW_RTS */
end_rx_serv:
/* terminate service context */
cd_write_reg(cy, CD1400_RIR, save_rir & 0x3f);
cd_write_reg(cy, CD1400_CAR, save_car);
int_serviced = 1;
} /* if(rx_service...) */
if (ISSET(stat, CD1400_SVRR_MDMCH)) {
u_char save_car, save_mir, serv_type, modem_stat;
save_mir = cd_read_reg_sc(sc, cy_chip, CD1400_MIR);
save_car = cd_read_reg_sc(sc, cy_chip, CD1400_CAR);
/* enter modem service */
cd_write_reg_sc(sc, cy_chip, CD1400_CAR, save_mir);
serv_type = cd_read_reg_sc(sc, cy_chip, CD1400_MIVR);
cy = &sc->sc_ports[serv_type >> 3];
#ifdef CY_DEBUG1
cy->cy_modem_int_count++;
#endif
modem_stat = cd_read_reg(cy, CD1400_MSVR2);
#ifdef CY_DEBUG
printf("%s port %d modem line change, new stat 0x%x\n",
sc->sc_dev.dv_xname, cy->cy_port_num, modem_stat);
#endif
if (ISSET((cy->cy_carrier_stat ^ modem_stat),
CD1400_MSVR2_CD)) {
SET(cy->cy_flags, CYF_CARRIER_CHANGED);
sc->sc_events = 1;
}
cy->cy_carrier_stat = modem_stat;
/* terminate service context */
cd_write_reg(cy, CD1400_MIR, save_mir & 0x3f);
cd_write_reg(cy, CD1400_CAR, save_car);
int_serviced = 1;
} /* if(modem_service...) */
if (ISSET(stat, CD1400_SVRR_TXRDY)) {
u_char save_car, save_tir, serv_type, count, ch;
struct tty *tp;
save_tir = cd_read_reg_sc(sc, cy_chip, CD1400_TIR);
save_car = cd_read_reg_sc(sc, cy_chip, CD1400_CAR);
/* enter tx service */
cd_write_reg_sc(sc, cy_chip, CD1400_CAR, save_tir);
serv_type = cd_read_reg_sc(sc, cy_chip, CD1400_TIVR);
cy = &sc->sc_ports[serv_type >> 3];
#ifdef CY_DEBUG1
cy->cy_tx_int_count++;
#endif
#ifdef CY_DEBUG
printf("%s port %d tx service\n", sc->sc_dev.dv_xname,
cy->cy_port_num);
#endif
/* stop transmitting if no tty or CYF_STOP set */
tp = cy->cy_tty;
if (tp == NULL || ISSET(cy->cy_flags, CYF_STOP))
goto txdone;
count = 0;
if (ISSET(cy->cy_flags, CYF_SEND_NUL)) {
cd_write_reg(cy, CD1400_TDR, 0);
cd_write_reg(cy, CD1400_TDR, 0);
count += 2;
CLR(cy->cy_flags, CYF_SEND_NUL);
}
if (tp->t_outq.c_cc > 0) {
SET(tp->t_state, TS_BUSY);
while (tp->t_outq.c_cc > 0 &&
count < CD1400_TX_FIFO_SIZE) {
ch = getc(&tp->t_outq);
/* remember to double NUL characters
because embedded transmit commands
are enabled */
if (ch == 0) {
if (count >=
CD1400_TX_FIFO_SIZE-2) {
SET(cy->cy_flags,
CYF_SEND_NUL);
break;
}
cd_write_reg(cy, CD1400_TDR, ch);
count++;
}
cd_write_reg(cy, CD1400_TDR, ch);
count++;
}
} else {
/* no data to send -- check if we should
start/stop a break */
/* XXX does this cause too much delay before
breaks? */
if (ISSET(cy->cy_flags, CYF_START_BREAK)) {
cd_write_reg(cy, CD1400_TDR, 0);
cd_write_reg(cy, CD1400_TDR, 0x81);
CLR(cy->cy_flags, CYF_START_BREAK);
}
if (ISSET(cy->cy_flags, CYF_END_BREAK)) {
cd_write_reg(cy, CD1400_TDR, 0);
cd_write_reg(cy, CD1400_TDR, 0x83);
CLR(cy->cy_flags, CYF_END_BREAK);
}
}
if (tp->t_outq.c_cc == 0) {
txdone:
/*
* No data to send or requested to stop.
* Disable transmit interrupt
*/
cd_write_reg(cy, CD1400_SRER,
cd_read_reg(cy, CD1400_SRER)
& ~CD1400_SRER_TXRDY);
CLR(cy->cy_flags, CYF_STOP);
CLR(tp->t_state, TS_BUSY);
}
if (tp->t_outq.c_cc <= tp->t_lowat) {
SET(cy->cy_flags, CYF_START);
sc->sc_events = 1;
}
/* terminate service context */
cd_write_reg(cy, CD1400_TIR, save_tir & 0x3f);
cd_write_reg(cy, CD1400_CAR, save_car);
int_serviced = 1;
} /* if(tx_service...) */
} /* for(...all CD1400s on a card) */
/* ensure an edge for next interrupt */
bus_space_write_1(sc->sc_memt, sc->sc_memh,
CY_CLEAR_INTR<<sc->sc_bustype, 0);
return (int_serviced);
}
/*
* subroutine to enable CD1400 transmitter
*/
void
cy_enable_transmitter(struct cy_port *cy)
{
int s;
s = spltty();
cd_write_reg(cy, CD1400_CAR, cy->cy_port_num & CD1400_CAR_CHAN);
cd_write_reg(cy, CD1400_SRER, cd_read_reg(cy, CD1400_SRER)
| CD1400_SRER_TXRDY);
splx(s);
}
/*
* Execute a CD1400 channel command
*/
void
cd1400_channel_cmd(struct cy_port *cy, int cmd)
{
u_int waitcnt = 5 * 8 * 1024; /* approx 5 ms */
#ifdef CY_DEBUG
printf("c1400_channel_cmd cy %p command 0x%x\n", cy, cmd);
#endif
/* wait until cd1400 is ready to process a new command */
while (cd_read_reg(cy, CD1400_CCR) != 0 && waitcnt-- > 0)
;
if (waitcnt == 0)
log(LOG_ERR, "cy: channel command timeout\n");
cd_write_reg(cy, CD1400_CCR, cmd);
}
/*
* Compute clock option register and baud rate register values
* for a given speed. Return 0 on success, -1 on failure.
*
* The error between requested and actual speed seems
* to be well within allowed limits (less than 3%)
* with every speed value between 50 and 150000 bps.
*/
int
cy_speed(speed_t speed, int *cor, int *bpr, int cy_clock)
{
int c, co, br;
if (speed < 50 || speed > 150000)
return (-1);
for (c = 0, co = 8; co <= 2048; co <<= 2, c++) {
br = (cy_clock + (co * speed) / 2) / (co * speed);
if (br < 0x100) {
*bpr = br;
*cor = c;
return (0);
}
}
return (-1);
}
44
89
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/* $OpenBSD: toeplitz.c,v 1.10 2021/02/21 02:37:38 dlg Exp $ */
/*
* Copyright (c) 2009 The DragonFly Project. All rights reserved.
*
* This code is derived from software contributed to The DragonFly Project
* by Sepherosa Ziehau <sepherosa@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of The DragonFly Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 2019 David Gwynne <dlg@openbsd.org>
* Copyright (c) 2020 Theo Buehler <tb@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <net/toeplitz.h>
/*
* symmetric toeplitz
*/
static stoeplitz_key stoeplitz_keyseed = STOEPLITZ_KEYSEED;
static struct stoeplitz_cache stoeplitz_syskey_cache;
const struct stoeplitz_cache *const
stoeplitz_cache = &stoeplitz_syskey_cache;
/* parity of n16: count (mod 2) of ones in the binary representation. */
int
parity(uint16_t n16)
{
n16 = ((n16 & 0xaaaa) >> 1) ^ (n16 & 0x5555);
n16 = ((n16 & 0xcccc) >> 2) ^ (n16 & 0x3333);
n16 = ((n16 & 0xf0f0) >> 4) ^ (n16 & 0x0f0f);
n16 = ((n16 & 0xff00) >> 8) ^ (n16 & 0x00ff);
return (n16);
}
/*
* The Toeplitz matrix obtained from a seed is invertible if and only if the
* parity of the seed is 1. Generate such a seed uniformly at random.
*/
stoeplitz_key
stoeplitz_random_seed(void)
{
stoeplitz_key seed;
seed = arc4random() & UINT16_MAX;
if (parity(seed) == 0)
seed ^= 1;
return (seed);
}
void
stoeplitz_init(void)
{
stoeplitz_keyseed = stoeplitz_random_seed();
stoeplitz_cache_init(&stoeplitz_syskey_cache, stoeplitz_keyseed);
}
#define NBSK (NBBY * sizeof(stoeplitz_key))
/*
* The Toeplitz hash of a 16-bit number considered as a column vector over
* the field with two elements is calculated as a matrix multiplication with
* a 16x16 circulant Toeplitz matrix T generated by skey.
*
* The first eight columns H of T generate the remaining eight columns using
* the byteswap operation J = swap16: T = [H JH]. Thus, the Toeplitz hash of
* n = [hi lo] is computed via the formula T * n = (H * hi) ^ swap16(H * lo).
*
* Therefore the results H * val for all values of a byte are cached in scache.
*/
void
stoeplitz_cache_init(struct stoeplitz_cache *scache, stoeplitz_key skey)
{
uint16_t column[NBBY];
unsigned int b, shift, val;
bzero(column, sizeof(column));
/* Calculate the first eight columns H of the Toeplitz matrix T. */
for (b = 0; b < NBBY; ++b)
column[b] = skey << b | skey >> (NBSK - b);
/* Cache the results of H * val for all possible values of a byte. */
for (val = 0; val < 256; ++val) {
uint16_t res = 0;
for (b = 0; b < NBBY; ++b) {
shift = NBBY - b - 1;
if (val & (1 << shift))
res ^= column[b];
}
scache->bytes[val] = res;
}
}
uint16_t
stoeplitz_hash_ip4(const struct stoeplitz_cache *scache,
in_addr_t faddr, in_addr_t laddr)
{
return (stoeplitz_hash_n32(scache, faddr ^ laddr));
}
uint16_t
stoeplitz_hash_ip4port(const struct stoeplitz_cache *scache,
in_addr_t faddr, in_addr_t laddr, in_port_t fport, in_port_t lport)
{
return (stoeplitz_hash_n32(scache, faddr ^ laddr ^ fport ^ lport));
}
#ifdef INET6
uint16_t
stoeplitz_hash_ip6(const struct stoeplitz_cache *scache,
const struct in6_addr *faddr6, const struct in6_addr *laddr6)
{
uint32_t n32 = 0;
size_t i;
for (i = 0; i < nitems(faddr6->s6_addr32); i++)
n32 ^= faddr6->s6_addr32[i] ^ laddr6->s6_addr32[i];
return (stoeplitz_hash_n32(scache, n32));
}
uint16_t
stoeplitz_hash_ip6port(const struct stoeplitz_cache *scache,
const struct in6_addr *faddr6, const struct in6_addr *laddr6,
in_port_t fport, in_port_t lport)
{
uint32_t n32 = 0;
size_t i;
for (i = 0; i < nitems(faddr6->s6_addr32); i++)
n32 ^= faddr6->s6_addr32[i] ^ laddr6->s6_addr32[i];
n32 ^= fport ^ lport;
return (stoeplitz_hash_n32(scache, n32));
}
#endif /* INET6 */
uint16_t
stoeplitz_hash_eaddr(const struct stoeplitz_cache *scache,
const uint8_t ea[static 6])
{
const uint16_t *ea16 = (const uint16_t *)ea;
return (stoeplitz_hash_n16(scache, ea16[0] ^ ea16[1] ^ ea16[2]));
}
void
stoeplitz_to_key(void *key, size_t klen)
{
uint8_t *k = key;
uint16_t skey = htons(stoeplitz_keyseed);
size_t i;
KASSERT((klen % 2) == 0);
for (i = 0; i < klen; i += sizeof(skey)) {
k[i + 0] = skey >> 8;
k[i + 1] = skey;
}
}
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
/* $OpenBSD: route.h,v 1.196 2022/06/28 10:01:13 bluhm Exp $ */
/* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */
/*
* Copyright (c) 1980, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)route.h 8.3 (Berkeley) 4/19/94
*/
#ifndef _NET_ROUTE_H_
#define _NET_ROUTE_H_
/*
* Locks used to protect struct members in this file:
* I immutable after creation
* T rttimer_mtx route timer lists
*/
/*
* Kernel resident routing tables.
*
* The routing tables are initialized when interface addresses
* are set by making entries for all directly connected interfaces.
*/
#ifdef _KERNEL
/*
* These numbers are used by reliable protocols for determining
* retransmission behavior and are included in the routing structure.
*/
struct rt_kmetrics {
u_int64_t rmx_pksent; /* packets sent using this route */
int64_t rmx_expire; /* lifetime for route, e.g. redirect */
u_int rmx_locks; /* Kernel must leave these values */
u_int rmx_mtu; /* MTU for this path */
};
#endif
/*
* Huge version for userland compatibility.
*/
struct rt_metrics {
u_int64_t rmx_pksent; /* packets sent using this route */
int64_t rmx_expire; /* lifetime for route, e.g. redirect */
u_int rmx_locks; /* Kernel must leave these values */
u_int rmx_mtu; /* MTU for this path */
u_int rmx_refcnt; /* # references hold */
/* some apps may still need these no longer used metrics */
u_int rmx_hopcount; /* max hops expected */
u_int rmx_recvpipe; /* inbound delay-bandwidth product */
u_int rmx_sendpipe; /* outbound delay-bandwidth product */
u_int rmx_ssthresh; /* outbound gateway buffer limit */
u_int rmx_rtt; /* estimated round trip time */
u_int rmx_rttvar; /* estimated rtt variance */
u_int rmx_pad;
};
#ifdef _KERNEL
/*
* rmx_rtt and rmx_rttvar are stored as microseconds;
* RTTTOPRHZ(rtt) converts to a value suitable for use
* by a protocol slowtimo counter.
*/
#define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */
#define RTTTOPRHZ(r) ((r) / (RTM_RTTUNIT / PR_SLOWHZ))
#include <sys/queue.h>
#include <net/rtable.h>
struct rttimer;
/*
* We distinguish between routes to hosts and routes to networks,
* preferring the former if available. For each route we infer
* the interface to use from the gateway address supplied when
* the route was entered. Routes that forward packets through
* gateways are marked so that the output routines know to address the
* gateway rather than the ultimate destination.
*/
struct rtentry {
struct sockaddr *rt_dest; /* destination */
SRPL_ENTRY(rtentry) rt_next; /* Next multipath entry to our dst. */
struct sockaddr *rt_gateway; /* value */
struct ifaddr *rt_ifa; /* the answer: interface addr to use */
caddr_t rt_llinfo; /* pointer to link level info cache or
to an MPLS structure */
union {
struct rtentry *_nh; /* implied entry for gatewayed routes */
unsigned int _ref; /* # gatewayed caching this route */
} RT_gw;
#define rt_gwroute RT_gw._nh
#define rt_cachecnt RT_gw._ref
struct rtentry *rt_parent; /* If cloned, parent of this route. */
LIST_HEAD(, rttimer) rt_timer; /* queue of timeouts for misc funcs */
struct rt_kmetrics rt_rmx; /* metrics used by rx'ing protocols */
unsigned int rt_ifidx; /* the answer: interface to use */
unsigned int rt_flags; /* up/down?, host/net */
struct refcnt rt_refcnt; /* # held references */
int rt_plen; /* prefix length */
uint16_t rt_labelid; /* route label ID */
uint8_t rt_priority; /* routing priority to use */
};
#define rt_use rt_rmx.rmx_pksent
#define rt_expire rt_rmx.rmx_expire
#define rt_locks rt_rmx.rmx_locks
#define rt_mtu rt_rmx.rmx_mtu
#endif /* _KERNEL */
/* bitmask values for rtm_flags */
#define RTF_UP 0x1 /* route usable */
#define RTF_GATEWAY 0x2 /* destination is a gateway */
#define RTF_HOST 0x4 /* host entry (net otherwise) */
#define RTF_REJECT 0x8 /* host or net unreachable */
#define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */
#define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */
#define RTF_DONE 0x40 /* message confirmed */
#define RTF_CLONING 0x100 /* generate new routes on use */
#define RTF_MULTICAST 0x200 /* route associated to a mcast addr. */
#define RTF_LLINFO 0x400 /* generated by ARP or ND */
#define RTF_STATIC 0x800 /* manually added */
#define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */
#define RTF_PROTO3 0x2000 /* protocol specific routing flag */
#define RTF_PROTO2 0x4000 /* protocol specific routing flag */
#define RTF_ANNOUNCE RTF_PROTO2 /* announce L2 entry */
#define RTF_PROTO1 0x8000 /* protocol specific routing flag */
#define RTF_CLONED 0x10000 /* this is a cloned route */
#define RTF_CACHED 0x20000 /* cached by a RTF_GATEWAY entry */
#define RTF_MPATH 0x40000 /* multipath route or operation */
#define RTF_MPLS 0x100000 /* MPLS additional infos */
#define RTF_LOCAL 0x200000 /* route to a local address */
#define RTF_BROADCAST 0x400000 /* route associated to a bcast addr. */
#define RTF_CONNECTED 0x800000 /* interface route */
#define RTF_BFD 0x1000000 /* Link state controlled by BFD */
/* mask of RTF flags that are allowed to be modified by RTM_CHANGE */
#define RTF_FMASK \
(RTF_LLINFO | RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_BLACKHOLE | \
RTF_REJECT | RTF_STATIC | RTF_MPLS | RTF_BFD)
/* Routing priorities used by the different routing protocols */
#define RTP_NONE 0 /* unset priority use sane default */
#define RTP_LOCAL 1 /* local address routes (must be the highest) */
#define RTP_CONNECTED 4 /* directly connected routes */
#define RTP_STATIC 8 /* static routes base priority */
#define RTP_EIGRP 28 /* EIGRP routes */
#define RTP_OSPF 32 /* OSPF routes */
#define RTP_ISIS 36 /* IS-IS routes */
#define RTP_RIP 40 /* RIP routes */
#define RTP_BGP 48 /* BGP routes */
#define RTP_DEFAULT 56 /* routes that have nothing set */
#define RTP_PROPOSAL_STATIC 57
#define RTP_PROPOSAL_DHCLIENT 58
#define RTP_PROPOSAL_SLAAC 59
#define RTP_PROPOSAL_UMB 60
#define RTP_PROPOSAL_PPP 61
#define RTP_PROPOSAL_SOLICIT 62 /* request reply of all RTM_PROPOSAL */
#define RTP_MAX 63 /* maximum priority */
#define RTP_ANY 64 /* any of the above */
#define RTP_MASK 0x7f
#define RTP_DOWN 0x80 /* route/link is down */
/*
* Routing statistics.
*/
struct rtstat {
u_int32_t rts_badredirect; /* bogus redirect calls */
u_int32_t rts_dynamic; /* routes created by redirects */
u_int32_t rts_newgateway; /* routes modified by redirects */
u_int32_t rts_unreach; /* lookups which failed */
u_int32_t rts_wildcard; /* lookups satisfied by a wildcard */
};
/*
* Routing Table Info.
*/
struct rt_tableinfo {
u_short rti_tableid; /* routing table id */
u_short rti_domainid; /* routing domain id */
};
/*
* Structures for routing messages.
*/
struct rt_msghdr {
u_short rtm_msglen; /* to skip over non-understood messages */
u_char rtm_version; /* future binary compatibility */
u_char rtm_type; /* message type */
u_short rtm_hdrlen; /* sizeof(rt_msghdr) to skip over the header */
u_short rtm_index; /* index for associated ifp */
u_short rtm_tableid; /* routing table id */
u_char rtm_priority; /* routing priority */
u_char rtm_mpls; /* MPLS additional infos */
int rtm_addrs; /* bitmask identifying sockaddrs in msg */
int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
int rtm_fmask; /* bitmask used in RTM_CHANGE message */
pid_t rtm_pid; /* identify sender */
int rtm_seq; /* for sender to identify action */
int rtm_errno; /* why failed */
u_int rtm_inits; /* which metrics we are initializing */
struct rt_metrics rtm_rmx; /* metrics themselves */
};
/* overload no longer used field */
#define rtm_use rtm_rmx.rmx_pksent
#define RTM_VERSION 5 /* Up the ante and ignore older versions */
#define RTM_MAXSIZE 2048 /* Maximum size of an accepted route msg */
/* values for rtm_type */
#define RTM_ADD 0x1 /* Add Route */
#define RTM_DELETE 0x2 /* Delete Route */
#define RTM_CHANGE 0x3 /* Change Metrics or flags */
#define RTM_GET 0x4 /* Report Metrics */
#define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */
#define RTM_REDIRECT 0x6 /* Told to use different route */
#define RTM_MISS 0x7 /* Lookup failed on this address */
#define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */
#define RTM_NEWADDR 0xc /* address being added to iface */
#define RTM_DELADDR 0xd /* address being removed from iface */
#define RTM_IFINFO 0xe /* iface going up/down etc. */
#define RTM_IFANNOUNCE 0xf /* iface arrival/departure */
#define RTM_DESYNC 0x10 /* route socket buffer overflow */
#define RTM_INVALIDATE 0x11 /* Invalidate cache of L2 route */
#define RTM_BFD 0x12 /* bidirectional forwarding detection */
#define RTM_PROPOSAL 0x13 /* proposal for resolvd(8) */
#define RTM_CHGADDRATTR 0x14 /* address attribute change */
#define RTM_80211INFO 0x15 /* 80211 iface change */
#define RTM_SOURCE 0x16 /* set source address */
#define RTV_MTU 0x1 /* init or lock _mtu */
#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */
#define RTV_EXPIRE 0x4 /* init or lock _expire */
#define RTV_RPIPE 0x8 /* init or lock _recvpipe */
#define RTV_SPIPE 0x10 /* init or lock _sendpipe */
#define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */
#define RTV_RTT 0x40 /* init or lock _rtt */
#define RTV_RTTVAR 0x80 /* init or lock _rttvar */
/*
* Bitmask values for rtm_addrs.
*/
#define RTA_DST 0x1 /* destination sockaddr present */
#define RTA_GATEWAY 0x2 /* gateway sockaddr present */
#define RTA_NETMASK 0x4 /* netmask sockaddr present */
#define RTA_GENMASK 0x8 /* cloning mask sockaddr present */
#define RTA_IFP 0x10 /* interface name sockaddr present */
#define RTA_IFA 0x20 /* interface addr sockaddr present */
#define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */
#define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */
#define RTA_SRC 0x100 /* source sockaddr present */
#define RTA_SRCMASK 0x200 /* source netmask present */
#define RTA_LABEL 0x400 /* route label present */
#define RTA_BFD 0x800 /* bfd present */
#define RTA_DNS 0x1000 /* DNS Servers sockaddr present */
#define RTA_STATIC 0x2000 /* RFC 3442 encoded static routes present */
#define RTA_SEARCH 0x4000 /* RFC 3397 encoded search path present */
/*
* Index offsets for sockaddr array for alternate internal encoding.
*/
#define RTAX_DST 0 /* destination sockaddr present */
#define RTAX_GATEWAY 1 /* gateway sockaddr present */
#define RTAX_NETMASK 2 /* netmask sockaddr present */
#define RTAX_GENMASK 3 /* cloning mask sockaddr present */
#define RTAX_IFP 4 /* interface name sockaddr present */
#define RTAX_IFA 5 /* interface addr sockaddr present */
#define RTAX_AUTHOR 6 /* sockaddr for author of redirect */
#define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */
#define RTAX_SRC 8 /* source sockaddr present */
#define RTAX_SRCMASK 9 /* source netmask present */
#define RTAX_LABEL 10 /* route label present */
#define RTAX_BFD 11 /* bfd present */
#define RTAX_DNS 12 /* DNS Server(s) sockaddr present */
#define RTAX_STATIC 13 /* RFC 3442 encoded static routes present */
#define RTAX_SEARCH 14 /* RFC 3397 encoded search path present */
#define RTAX_MAX 15 /* size of array to allocate */
/*
* setsockopt defines used for the filtering.
*/
#define ROUTE_MSGFILTER 1 /* bitmask to specify which types should be
sent to the client. */
#define ROUTE_TABLEFILTER 2 /* change routing table the socket is listening
on, RTABLE_ANY listens on all tables. */
#define ROUTE_PRIOFILTER 3 /* only pass updates with a priority higher or
equal (actual value lower) to the specified
priority. */
#define ROUTE_FLAGFILTER 4 /* do not pass updates for routes with flags
in this bitmask. */
#define ROUTE_FILTER(m) (1 << (m))
#define RTABLE_ANY 0xffffffff
#define RTLABEL_LEN 32
struct sockaddr_rtlabel {
u_int8_t sr_len; /* total length */
sa_family_t sr_family; /* address family */
char sr_label[RTLABEL_LEN];
};
#define RTDNS_LEN 128
struct sockaddr_rtdns {
u_int8_t sr_len; /* total length */
sa_family_t sr_family; /* address family */
char sr_dns[RTDNS_LEN];
};
#ifdef _KERNEL
static inline struct sockaddr *
srtdnstosa(struct sockaddr_rtdns *sdns)
{
return ((struct sockaddr *)(sdns));
}
#endif
#define RTSTATIC_LEN 128
struct sockaddr_rtstatic {
u_int8_t sr_len; /* total length */
sa_family_t sr_family; /* address family */
char sr_static[RTSTATIC_LEN];
};
#define RTSEARCH_LEN 128
struct sockaddr_rtsearch {
u_int8_t sr_len; /* total length */
sa_family_t sr_family; /* address family */
char sr_search[RTSEARCH_LEN];
};
/*
* A route consists of a destination address and a reference
* to a routing entry. These are often held by protocols
* in their control blocks, e.g. inpcb.
*/
struct route {
struct rtentry *ro_rt;
u_long ro_tableid; /* u_long because of alignment */
struct sockaddr ro_dst;
};
struct rt_addrinfo {
int rti_addrs;
struct sockaddr *rti_info[RTAX_MAX];
int rti_flags;
struct ifaddr *rti_ifa;
struct rt_msghdr *rti_rtm;
u_char rti_mpls;
};
#ifdef _KERNEL
#include <sys/percpu.h>
enum rtstat_counters {
rts_badredirect, /* bogus redirect calls */
rts_dynamic, /* routes created by redirects */
rts_newgateway, /* routes modified by redirects */
rts_unreach, /* lookups which failed */
rts_wildcard, /* lookups satisfied by a wildcard */
rts_ncounters
};
static inline void
rtstat_inc(enum rtstat_counters c)
{
extern struct cpumem *rtcounters;
counters_inc(rtcounters, c);
}
/*
* This structure, and the prototypes for the rt_timer_{init,remove_all,
* add,timer} functions all used with the kind permission of BSDI.
* These allow functions to be called for routes at specific times.
*/
struct rttimer_queue {
TAILQ_HEAD(, rttimer) rtq_head; /* [T] */
LIST_ENTRY(rttimer_queue) rtq_link; /* [T] */
void (*rtq_func) /* [I] callback */
(struct rtentry *, u_int);
unsigned long rtq_count; /* [T] */
int rtq_timeout; /* [T] */
};
const char *rtlabel_id2name(u_int16_t);
u_int16_t rtlabel_name2id(char *);
struct sockaddr *rtlabel_id2sa(u_int16_t, struct sockaddr_rtlabel *);
void rtlabel_unref(u_int16_t);
/*
* Values for additional argument to rtalloc()
*/
#define RT_RESOLVE 1
extern struct rtstat rtstat;
struct mbuf;
struct socket;
struct ifnet;
struct sockaddr_in6;
struct if_ieee80211_data;
struct bfd_config;
void route_init(void);
void rtm_ifchg(struct ifnet *);
void rtm_ifannounce(struct ifnet *, int);
void rtm_bfd(struct bfd_config *);
void rtm_80211info(struct ifnet *, struct if_ieee80211_data *);
void rt_maskedcopy(struct sockaddr *,
struct sockaddr *, struct sockaddr *);
struct sockaddr *rt_plen2mask(struct rtentry *, struct sockaddr_in6 *);
void rtm_send(struct rtentry *, int, int, unsigned int);
void rtm_addr(int, struct ifaddr *);
void rtm_miss(int, struct rt_addrinfo *, int, uint8_t, u_int, int, u_int);
void rtm_proposal(struct ifnet *, struct rt_addrinfo *, int, uint8_t);
int rt_setgate(struct rtentry *, struct sockaddr *, u_int);
struct rtentry *rt_getll(struct rtentry *);
void rt_timer_init(void);
int rt_timer_add(struct rtentry *,
struct rttimer_queue *, u_int);
void rt_timer_remove_all(struct rtentry *);
time_t rt_timer_get_expire(const struct rtentry *);
void rt_timer_queue_init(struct rttimer_queue *, int,
void(*)(struct rtentry *, u_int));
void rt_timer_queue_change(struct rttimer_queue *, int);
void rt_timer_queue_flush(struct rttimer_queue *);
unsigned long rt_timer_queue_count(struct rttimer_queue *);
void rt_timer_timer(void *);
int rt_mpls_set(struct rtentry *, struct sockaddr *, uint8_t);
void rt_mpls_clear(struct rtentry *);
int rtisvalid(struct rtentry *);
int rt_hash(struct rtentry *, struct sockaddr *, uint32_t *);
struct rtentry *rtalloc_mpath(struct sockaddr *, uint32_t *, u_int);
struct rtentry *rtalloc(struct sockaddr *, int, unsigned int);
void rtref(struct rtentry *);
void rtfree(struct rtentry *);
int rt_ifa_add(struct ifaddr *, int, struct sockaddr *, unsigned int);
int rt_ifa_del(struct ifaddr *, int, struct sockaddr *, unsigned int);
void rt_ifa_purge(struct ifaddr *);
int rt_ifa_addlocal(struct ifaddr *);
int rt_ifa_dellocal(struct ifaddr *);
void rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *,
struct rtentry **, unsigned int);
int rtrequest(int, struct rt_addrinfo *, u_int8_t, struct rtentry **,
u_int);
int rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
struct rtentry **, u_int);
int rt_if_track(struct ifnet *);
int rt_if_linkstate_change(struct rtentry *, void *, u_int);
int rtdeletemsg(struct rtentry *, struct ifnet *, u_int);
#endif /* _KERNEL */
#endif /* _NET_ROUTE_H_ */
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/* $OpenBSD: udf_subr.c,v 1.26 2022/09/01 13:45:26 krw Exp $ */
/*
* Copyright (c) 2006, Miodrag Vallat
* Copyright (c) 2006, Pedro Martelletto
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/dirent.h>
#include <sys/disklabel.h>
#include <crypto/siphash.h>
#include <isofs/udf/ecma167-udf.h>
#include <isofs/udf/udf.h>
#include <isofs/udf/udf_extern.h>
int udf_vat_read(struct umount *, uint32_t *);
/*
* Convert a CS0 dstring to a 16-bit Unicode string.
* Returns the length of the Unicode string, in unicode characters (not
* bytes!), or -1 if an error arises.
* Note that the transname destination buffer is expected to be large
* enough to hold the result, and will not be terminated in any way.
*/
int
udf_rawnametounicode(u_int len, char *cs0string, unicode_t *transname)
{
unicode_t *origname = transname;
if (len-- == 0)
return (-1);
switch (*cs0string++) {
case 8: /* bytes string */
while (len-- != 0)
*transname++ = (unicode_t)*cs0string++;
break;
case 16: /* 16 bit unicode string */
if (len & 1)
return (-1);
len >>= 1;
while (len-- != 0) {
unicode_t tmpchar;
tmpchar = (unicode_t)*cs0string++;
tmpchar = (tmpchar << 8) | (unicode_t)*cs0string++;
*transname++ = tmpchar;
}
break;
default:
return (-1);
}
return (transname - origname);
}
/*
* Do a lazy probe on the underlying media to check if it's a UDF volume, in
* which case we fake a disk label for it.
*/
int
udf_disklabelspoof(dev_t dev, void (*strat)(struct buf *),
struct disklabel *lp)
{
char vid[32];
int i, bsize = 2048, error = EINVAL;
uint32_t sector = 256, mvds_start, mvds_end;
struct buf *bp;
struct anchor_vdp avdp;
struct pri_vol_desc *pvd;
/*
* Get a buffer to work with.
*/
bp = geteblk(bsize);
bp->b_dev = dev;
/*
* Look for an Anchor Volume Descriptor at sector 256.
*/
bp->b_blkno = sector * btodb(bsize);
bp->b_bcount = bsize;
CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
bp->b_resid = bp->b_blkno / lp->d_secpercyl;
(*strat)(bp);
if (biowait(bp))
goto out;
if (udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR))
goto out;
bcopy(bp->b_data, &avdp, sizeof(avdp));
mvds_start = letoh32(avdp.main_vds_ex.loc);
mvds_end = mvds_start + (letoh32(avdp.main_vds_ex.len) - 1) / bsize;
/*
* Then try to find a reference to a Primary Volume Descriptor.
*/
for (sector = mvds_start; sector < mvds_end; sector++) {
bp->b_blkno = sector * btodb(bsize);
bp->b_bcount = bsize;
CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
bp->b_resid = bp->b_blkno / lp->d_secpercyl;
(*strat)(bp);
if (biowait(bp))
goto out;
pvd = (struct pri_vol_desc *)bp->b_data;
if (!udf_checktag(&pvd->tag, TAGID_PRI_VOL))
break;
}
/*
* If we couldn't find a reference, bail out.
*/
if (sector == mvds_end)
goto out;
/*
* Okay, it's a UDF volume. Spoof a disk label for it.
*/
if (udf_transname(pvd->vol_id, vid, sizeof(pvd->vol_id) - 1, NULL))
strlcpy(lp->d_typename, vid, sizeof(lp->d_typename));
for (i = 0; i < MAXPARTITIONS; i++) {
DL_SETPSIZE(&lp->d_partitions[i], 0);
DL_SETPOFFSET(&lp->d_partitions[i], 0);
}
/*
* Fake two partitions, 'a' and 'c'.
*/
DL_SETPSIZE(&lp->d_partitions[0], DL_GETDSIZE(lp));
lp->d_partitions[0].p_fstype = FS_UDF;
DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
lp->d_partitions[RAW_PART].p_fstype = FS_UDF;
lp->d_npartitions = MAXPARTITIONS;
lp->d_version = 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
error = 0;
out:
bp->b_flags |= B_INVAL;
brelse(bp);
return (error);
}
/* Get a vnode for the Virtual Allocation Table (VAT) */
int
udf_vat_get(struct umount *ump, uint32_t lb)
{
struct vnode *vp;
struct unode *up;
int error;
error = udf_vget(ump->um_mountp, lb - ump->um_start - 3, &vp);
if (error)
return (error);
up = VTOU(vp);
up->u_vatlen = (letoh64(up->u_fentry->inf_len) - 36) >> 2;
ump->um_vat = malloc(sizeof(struct unode), M_UDFMOUNT, M_WAITOK);
*ump->um_vat = *up;
ump->um_flags &= ~UDF_MNT_FIND_VAT;
ump->um_flags |= UDF_MNT_USES_VAT;
vput(vp);
return (0);
}
/* Look up a sector in the VAT */
int
udf_vat_map(struct umount *ump, uint32_t *sector)
{
/* If there's no VAT, then it's easy */
if (!(ump->um_flags & UDF_MNT_USES_VAT)) {
*sector += ump->um_start;
return (0);
}
/* Sanity check the given sector */
if (*sector >= ump->um_vat->u_vatlen)
return (EINVAL);
return (udf_vat_read(ump, sector));
}
/* Read from the VAT */
int
udf_vat_read(struct umount *ump, uint32_t *sector)
{
struct buf *bp;
uint8_t *data;
int error, size;
size = 4;
/*
* Note that we rely on the buffer cache to keep frequently accessed
* buffers around to avoid reading them from the disk all the time.
*/
error = udf_readatoffset(ump->um_vat, &size, *sector << 2, &bp, &data);
if (error) {
if (bp != NULL)
brelse(bp);
return (error);
}
/* Make sure we read at least a whole entry */
if (size < 4) {
if (bp != NULL)
brelse(bp);
return (EINVAL);
}
/* Map the sector */
*sector = letoh32(*(uint32_t *)data) + ump->um_start;
brelse(bp);
return (0);
}
open /syzkaller/managers/main/kernel/machine/atomic.h: no such file or directory
2
11
30
30
22
19
8
17
11
6
14
11
7
2
2
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
/* $OpenBSD: igmp.c,v 1.81 2022/09/04 06:49:11 jsg Exp $ */
/* $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1988 Stephen Deering.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Stephen Deering of Stanford University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)igmp.c 8.2 (Berkeley) 5/3/95
*/
/*
* Internet Group Management Protocol (IGMP) routines.
*
* Written by Steve Deering, Stanford, May 1988.
* Modified by Rosen Sharma, Stanford, Aug 1994.
* Modified by Bill Fenner, Xerox PARC, Feb 1995.
*
* MULTICAST Revision: 1.3
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/igmp.h>
#include <netinet/igmp_var.h>
#include <sys/stdarg.h>
#define IP_MULTICASTOPTS 0
int igmp_timers_are_running; /* [N] shortcut for fast timer */
static LIST_HEAD(, router_info) rti_head;
static struct mbuf *router_alert;
struct cpumem *igmpcounters;
void igmp_checktimer(struct ifnet *);
void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t);
int rti_fill(struct in_multi *);
struct router_info * rti_find(struct ifnet *);
int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
int igmp_sysctl_igmpstat(void *, size_t *, void *);
void
igmp_init(void)
{
struct ipoption *ra;
igmp_timers_are_running = 0;
LIST_INIT(&rti_head);
igmpcounters = counters_alloc(igps_ncounters);
router_alert = m_get(M_WAIT, MT_DATA);
/*
* Construct a Router Alert option (RAO) to use in report
* messages as required by RFC2236. This option has the
* following format:
*
* | 10010100 | 00000100 | 2 octet value |
*
* where a value of "0" indicates that routers shall examine
* the packet.
*/
ra = mtod(router_alert, struct ipoption *);
ra->ipopt_dst.s_addr = INADDR_ANY;
ra->ipopt_list[0] = IPOPT_RA;
ra->ipopt_list[1] = 0x04;
ra->ipopt_list[2] = 0x00;
ra->ipopt_list[3] = 0x00;
router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
}
int
rti_fill(struct in_multi *inm)
{
struct router_info *rti;
LIST_FOREACH(rti, &rti_head, rti_list) {
if (rti->rti_ifidx == inm->inm_ifidx) {
inm->inm_rti = rti;
if (rti->rti_type == IGMP_v1_ROUTER)
return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
else
return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
}
}
rti = malloc(sizeof(*rti), M_MRTABLE, M_WAITOK);
rti->rti_ifidx = inm->inm_ifidx;
rti->rti_type = IGMP_v2_ROUTER;
LIST_INSERT_HEAD(&rti_head, rti, rti_list);
inm->inm_rti = rti;
return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
}
struct router_info *
rti_find(struct ifnet *ifp)
{
struct router_info *rti;
KERNEL_ASSERT_LOCKED();
LIST_FOREACH(rti, &rti_head, rti_list) {
if (rti->rti_ifidx == ifp->if_index)
return (rti);
}
rti = malloc(sizeof(*rti), M_MRTABLE, M_NOWAIT);
if (rti == NULL)
return (NULL);
rti->rti_ifidx = ifp->if_index;
rti->rti_type = IGMP_v2_ROUTER;
LIST_INSERT_HEAD(&rti_head, rti, rti_list);
return (rti);
}
void
rti_delete(struct ifnet *ifp)
{
struct router_info *rti, *trti;
LIST_FOREACH_SAFE(rti, &rti_head, rti_list, trti) {
if (rti->rti_ifidx == ifp->if_index) {
LIST_REMOVE(rti, rti_list);
free(rti, M_MRTABLE, sizeof(*rti));
break;
}
}
}
int
igmp_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct ifnet *ifp;
igmpstat_inc(igps_rcv_total);
ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
m_freemp(mp);
return IPPROTO_DONE;
}
KERNEL_LOCK();
proto = igmp_input_if(ifp, mp, offp, proto, af);
KERNEL_UNLOCK();
if_put(ifp);
return proto;
}
int
igmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
int iphlen = *offp;
struct ip *ip = mtod(m, struct ip *);
struct igmp *igmp;
int igmplen;
int minlen;
struct ifmaddr *ifma;
struct in_multi *inm;
struct router_info *rti;
struct in_ifaddr *ia;
int timer;
igmplen = ntohs(ip->ip_len) - iphlen;
/*
* Validate lengths
*/
if (igmplen < IGMP_MINLEN) {
igmpstat_inc(igps_rcv_tooshort);
m_freem(m);
return IPPROTO_DONE;
}
minlen = iphlen + IGMP_MINLEN;
if ((m->m_flags & M_EXT || m->m_len < minlen) &&
(m = *mp = m_pullup(m, minlen)) == NULL) {
igmpstat_inc(igps_rcv_tooshort);
return IPPROTO_DONE;
}
/*
* Validate checksum
*/
m->m_data += iphlen;
m->m_len -= iphlen;
igmp = mtod(m, struct igmp *);
if (in_cksum(m, igmplen)) {
igmpstat_inc(igps_rcv_badsum);
m_freem(m);
return IPPROTO_DONE;
}
m->m_data -= iphlen;
m->m_len += iphlen;
ip = mtod(m, struct ip *);
switch (igmp->igmp_type) {
case IGMP_HOST_MEMBERSHIP_QUERY:
igmpstat_inc(igps_rcv_queries);
if (ifp->if_flags & IFF_LOOPBACK)
break;
if (igmp->igmp_code == 0) {
rti = rti_find(ifp);
if (rti == NULL) {
m_freem(m);
return IPPROTO_DONE;
}
rti->rti_type = IGMP_v1_ROUTER;
rti->rti_age = 0;
if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
igmpstat_inc(igps_rcv_badqueries);
m_freem(m);
return IPPROTO_DONE;
}
/*
* Start the timers in all of our membership records
* for the interface on which the query arrived,
* except those that are already running and those
* that belong to a "local" group (224.0.0.X).
*/
TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
if (ifma->ifma_addr->sa_family != AF_INET)
continue;
inm = ifmatoinm(ifma);
if (inm->inm_timer == 0 &&
!IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
inm->inm_state = IGMP_DELAYING_MEMBER;
inm->inm_timer = IGMP_RANDOM_DELAY(
IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
igmp_timers_are_running = 1;
}
}
} else {
if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
igmpstat_inc(igps_rcv_badqueries);
m_freem(m);
return IPPROTO_DONE;
}
timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
if (timer == 0)
timer = 1;
/*
* Start the timers in all of our membership records
* for the interface on which the query arrived,
* except those that are already running and those
* that belong to a "local" group (224.0.0.X). For
* timers already running, check if they need to be
* reset.
*/
TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
if (ifma->ifma_addr->sa_family != AF_INET)
continue;
inm = ifmatoinm(ifma);
if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
(ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
switch (inm->inm_state) {
case IGMP_DELAYING_MEMBER:
if (inm->inm_timer <= timer)
break;
/* FALLTHROUGH */
case IGMP_IDLE_MEMBER:
case IGMP_LAZY_MEMBER:
case IGMP_AWAKENING_MEMBER:
inm->inm_state =
IGMP_DELAYING_MEMBER;
inm->inm_timer =
IGMP_RANDOM_DELAY(timer);
igmp_timers_are_running = 1;
break;
case IGMP_SLEEPING_MEMBER:
inm->inm_state =
IGMP_AWAKENING_MEMBER;
break;
}
}
}
}
break;
case IGMP_v1_HOST_MEMBERSHIP_REPORT:
igmpstat_inc(igps_rcv_reports);
if (ifp->if_flags & IFF_LOOPBACK)
break;
if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
igmpstat_inc(igps_rcv_badreports);
m_freem(m);
return IPPROTO_DONE;
}
/*
* KLUDGE: if the IP source address of the report has an
* unspecified (i.e., zero) subnet number, as is allowed for
* a booting host, replace it with the correct subnet number
* so that a process-level multicast routing daemon can
* determine which subnet it arrived from. This is necessary
* to compensate for the lack of any way for a process to
* determine the arrival interface of an incoming packet.
*/
if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
IFP_TO_IA(ifp, ia);
if (ia)
ip->ip_src.s_addr = ia->ia_net;
}
/*
* If we belong to the group being reported, stop
* our timer for that group.
*/
IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
if (inm != NULL) {
inm->inm_timer = 0;
igmpstat_inc(igps_rcv_ourreports);
switch (inm->inm_state) {
case IGMP_IDLE_MEMBER:
case IGMP_LAZY_MEMBER:
case IGMP_AWAKENING_MEMBER:
case IGMP_SLEEPING_MEMBER:
inm->inm_state = IGMP_SLEEPING_MEMBER;
break;
case IGMP_DELAYING_MEMBER:
if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
inm->inm_state = IGMP_LAZY_MEMBER;
else
inm->inm_state = IGMP_SLEEPING_MEMBER;
break;
}
}
break;
case IGMP_v2_HOST_MEMBERSHIP_REPORT:
#ifdef MROUTING
/*
* Make sure we don't hear our own membership report. Fast
* leave requires knowing that we are the only member of a
* group.
*/
IFP_TO_IA(ifp, ia);
if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
break;
#endif
igmpstat_inc(igps_rcv_reports);
if (ifp->if_flags & IFF_LOOPBACK)
break;
if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
igmpstat_inc(igps_rcv_badreports);
m_freem(m);
return IPPROTO_DONE;
}
/*
* KLUDGE: if the IP source address of the report has an
* unspecified (i.e., zero) subnet number, as is allowed for
* a booting host, replace it with the correct subnet number
* so that a process-level multicast routing daemon can
* determine which subnet it arrived from. This is necessary
* to compensate for the lack of any way for a process to
* determine the arrival interface of an incoming packet.
*/
if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
#ifndef MROUTING
IFP_TO_IA(ifp, ia);
#endif
if (ia)
ip->ip_src.s_addr = ia->ia_net;
}
/*
* If we belong to the group being reported, stop
* our timer for that group.
*/
IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
if (inm != NULL) {
inm->inm_timer = 0;
igmpstat_inc(igps_rcv_ourreports);
switch (inm->inm_state) {
case IGMP_DELAYING_MEMBER:
case IGMP_IDLE_MEMBER:
case IGMP_AWAKENING_MEMBER:
inm->inm_state = IGMP_LAZY_MEMBER;
break;
case IGMP_LAZY_MEMBER:
case IGMP_SLEEPING_MEMBER:
break;
}
}
break;
}
/*
* Pass all valid IGMP packets up to any process(es) listening
* on a raw IGMP socket.
*/
return rip_input(mp, offp, proto, af);
}
void
igmp_joingroup(struct in_multi *inm, struct ifnet *ifp)
{
int i;
inm->inm_state = IGMP_IDLE_MEMBER;
if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
(ifp->if_flags & IFF_LOOPBACK) == 0) {
i = rti_fill(inm);
igmp_sendpkt(ifp, inm, i, 0);
inm->inm_state = IGMP_DELAYING_MEMBER;
inm->inm_timer = IGMP_RANDOM_DELAY(
IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
igmp_timers_are_running = 1;
} else
inm->inm_timer = 0;
}
void
igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp)
{
switch (inm->inm_state) {
case IGMP_DELAYING_MEMBER:
case IGMP_IDLE_MEMBER:
if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
(ifp->if_flags & IFF_LOOPBACK) == 0)
if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
igmp_sendpkt(ifp, inm,
IGMP_HOST_LEAVE_MESSAGE,
INADDR_ALLROUTERS_GROUP);
break;
case IGMP_LAZY_MEMBER:
case IGMP_AWAKENING_MEMBER:
case IGMP_SLEEPING_MEMBER:
break;
}
}
void
igmp_fasttimo(void)
{
struct ifnet *ifp;
/*
* Quick check to see if any work needs to be done, in order
* to minimize the overhead of fasttimo processing.
* Variable igmp_timers_are_running is read atomically, but without
* lock intentionally. In case it is not set due to MP races, we may
* miss to check the timers. Then run the loop at next fast timeout.
*/
if (!igmp_timers_are_running)
return;
NET_LOCK();
igmp_timers_are_running = 0;
TAILQ_FOREACH(ifp, &ifnet, if_list)
igmp_checktimer(ifp);
NET_UNLOCK();
}
void
igmp_checktimer(struct ifnet *ifp)
{
struct in_multi *inm;
struct ifmaddr *ifma;
NET_ASSERT_LOCKED();
TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
if (ifma->ifma_addr->sa_family != AF_INET)
continue;
inm = ifmatoinm(ifma);
if (inm->inm_timer == 0) {
/* do nothing */
} else if (--inm->inm_timer == 0) {
if (inm->inm_state == IGMP_DELAYING_MEMBER) {
if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
igmp_sendpkt(ifp, inm,
IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
else
igmp_sendpkt(ifp, inm,
IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
inm->inm_state = IGMP_IDLE_MEMBER;
}
} else {
igmp_timers_are_running = 1;
}
}
}
void
igmp_slowtimo(void)
{
struct router_info *rti;
NET_LOCK();
LIST_FOREACH(rti, &rti_head, rti_list) {
if (rti->rti_type == IGMP_v1_ROUTER &&
++rti->rti_age >= IGMP_AGE_THRESHOLD) {
rti->rti_type = IGMP_v2_ROUTER;
}
}
NET_UNLOCK();
}
void
igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type,
in_addr_t addr)
{
struct mbuf *m;
struct igmp *igmp;
struct ip *ip;
struct ip_moptions imo;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == NULL)
return;
/*
* Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
* is smaller than mbuf size returned by MGETHDR.
*/
m->m_data += max_linkhdr;
m->m_len = sizeof(struct ip) + IGMP_MINLEN;
m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
ip = mtod(m, struct ip *);
ip->ip_tos = 0;
ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
ip->ip_off = 0;
ip->ip_p = IPPROTO_IGMP;
ip->ip_src.s_addr = INADDR_ANY;
if (addr) {
ip->ip_dst.s_addr = addr;
} else {
ip->ip_dst = inm->inm_addr;
}
m->m_data += sizeof(struct ip);
m->m_len -= sizeof(struct ip);
igmp = mtod(m, struct igmp *);
igmp->igmp_type = type;
igmp->igmp_code = 0;
igmp->igmp_group = inm->inm_addr;
igmp->igmp_cksum = 0;
igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
m->m_data -= sizeof(struct ip);
m->m_len += sizeof(struct ip);
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
imo.imo_ifidx = inm->inm_ifidx;
imo.imo_ttl = 1;
/*
* Request loopback of the report if we are acting as a multicast
* router, so that the process-level routing daemon can hear it.
*/
#ifdef MROUTING
imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL);
#else
imo.imo_loop = 0;
#endif /* MROUTING */
ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
igmpstat_inc(igps_snd_reports);
}
/*
* Sysctl for igmp variables.
*/
int
igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case IGMPCTL_STATS:
if (newp != NULL)
return (EPERM);
return (igmp_sysctl_igmpstat(oldp, oldlenp, newp));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
int
igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[igps_ncounters];
struct igmpstat igmpstat;
u_long *words = (u_long *)&igmpstat;
int i;
CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long)));
memset(&igmpstat, 0, sizeof igmpstat);
counters_read(igmpcounters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&igmpstat, sizeof(igmpstat)));
}
18
18
17
52
52
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/* $OpenBSD: subr_percpu.c,v 1.9 2021/03/10 10:21:47 jsg Exp $ */
/*
* Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/pool.h>
#include <sys/malloc.h>
#include <sys/percpu.h>
#ifdef MULTIPROCESSOR
struct pool cpumem_pl;
void
percpu_init(void)
{
pool_init(&cpumem_pl, sizeof(struct cpumem) * ncpusfound, 0,
IPL_NONE, PR_WAITOK, "percpumem", &pool_allocator_single);
}
struct cpumem *
cpumem_get(struct pool *pp)
{
struct cpumem *cm;
unsigned int cpu;
cm = pool_get(&cpumem_pl, PR_WAITOK);
for (cpu = 0; cpu < ncpusfound; cpu++)
cm[cpu].mem = pool_get(pp, PR_WAITOK | PR_ZERO);
return (cm);
}
void
cpumem_put(struct pool *pp, struct cpumem *cm)
{
unsigned int cpu;
for (cpu = 0; cpu < ncpusfound; cpu++)
pool_put(pp, cm[cpu].mem);
pool_put(&cpumem_pl, cm);
}
struct cpumem *
cpumem_malloc(size_t sz, int type)
{
struct cpumem *cm;
unsigned int cpu;
sz = roundup(sz, CACHELINESIZE);
cm = pool_get(&cpumem_pl, PR_WAITOK);
for (cpu = 0; cpu < ncpusfound; cpu++)
cm[cpu].mem = malloc(sz, type, M_WAITOK | M_ZERO);
return (cm);
}
struct cpumem *
cpumem_malloc_ncpus(struct cpumem *bootcm, size_t sz, int type)
{
struct cpumem *cm;
unsigned int cpu;
sz = roundup(sz, CACHELINESIZE);
cm = pool_get(&cpumem_pl, PR_WAITOK);
cm[0].mem = bootcm[0].mem;
for (cpu = 1; cpu < ncpusfound; cpu++)
cm[cpu].mem = malloc(sz, type, M_WAITOK | M_ZERO);
return (cm);
}
void
cpumem_free(struct cpumem *cm, int type, size_t sz)
{
unsigned int cpu;
sz = roundup(sz, CACHELINESIZE);
for (cpu = 0; cpu < ncpusfound; cpu++)
free(cm[cpu].mem, type, sz);
pool_put(&cpumem_pl, cm);
}
void *
cpumem_first(struct cpumem_iter *i, struct cpumem *cm)
{
i->cpu = 0;
return (cm[0].mem);
}
void *
cpumem_next(struct cpumem_iter *i, struct cpumem *cm)
{
unsigned int cpu = ++i->cpu;
if (cpu >= ncpusfound)
return (NULL);
return (cm[cpu].mem);
}
struct cpumem *
counters_alloc(unsigned int n)
{
struct cpumem *cm;
struct cpumem_iter cmi;
uint64_t *counters;
unsigned int i;
KASSERT(n > 0);
n++; /* add space for a generation number */
cm = cpumem_malloc(n * sizeof(uint64_t), M_COUNTERS);
CPUMEM_FOREACH(counters, &cmi, cm) {
for (i = 0; i < n; i++)
counters[i] = 0;
}
return (cm);
}
struct cpumem *
counters_alloc_ncpus(struct cpumem *cm, unsigned int n)
{
n++; /* the generation number */
return (cpumem_malloc_ncpus(cm, n * sizeof(uint64_t), M_COUNTERS));
}
void
counters_free(struct cpumem *cm, unsigned int n)
{
n++; /* generation number */
cpumem_free(cm, M_COUNTERS, n * sizeof(uint64_t));
}
void
counters_read(struct cpumem *cm, uint64_t *output, unsigned int n)
{
struct cpumem_iter cmi;
uint64_t *gen, *counters, *temp;
uint64_t enter, leave;
unsigned int i;
for (i = 0; i < n; i++)
output[i] = 0;
temp = mallocarray(n, sizeof(uint64_t), M_TEMP, M_WAITOK);
gen = cpumem_first(&cmi, cm);
do {
counters = gen + 1;
enter = *gen;
for (;;) {
/* the generation number is odd during an update */
while (enter & 1) {
yield();
enter = *gen;
}
membar_consumer();
for (i = 0; i < n; i++)
temp[i] = counters[i];
membar_consumer();
leave = *gen;
if (enter == leave)
break;
enter = leave;
}
for (i = 0; i < n; i++)
output[i] += temp[i];
gen = cpumem_next(&cmi, cm);
} while (gen != NULL);
free(temp, M_TEMP, n * sizeof(uint64_t));
}
void
counters_zero(struct cpumem *cm, unsigned int n)
{
struct cpumem_iter cmi;
uint64_t *counters;
unsigned int i;
counters = cpumem_first(&cmi, cm);
do {
for (i = 0; i < n; i++)
counters[i] = 0;
/* zero the generation numbers too */
membar_producer();
counters[i] = 0;
counters = cpumem_next(&cmi, cm);
} while (counters != NULL);
}
#else /* MULTIPROCESSOR */
/*
* Uniprocessor implementation of per-CPU data structures.
*
* UP percpu memory is a single memory allocation cast to/from the
* cpumem struct. It is not scaled up to the size of cacheline because
* there's no other cache to contend with.
*/
void
percpu_init(void)
{
/* nop */
}
struct cpumem *
cpumem_get(struct pool *pp)
{
return (pool_get(pp, PR_WAITOK | PR_ZERO));
}
void
cpumem_put(struct pool *pp, struct cpumem *cm)
{
pool_put(pp, cm);
}
struct cpumem *
cpumem_malloc(size_t sz, int type)
{
return (malloc(sz, type, M_WAITOK | M_ZERO));
}
struct cpumem *
cpumem_malloc_ncpus(struct cpumem *cm, size_t sz, int type)
{
return (cm);
}
void
cpumem_free(struct cpumem *cm, int type, size_t sz)
{
free(cm, type, sz);
}
void *
cpumem_first(struct cpumem_iter *i, struct cpumem *cm)
{
return (cm);
}
void *
cpumem_next(struct cpumem_iter *i, struct cpumem *cm)
{
return (NULL);
}
struct cpumem *
counters_alloc(unsigned int n)
{
KASSERT(n > 0);
return (cpumem_malloc(n * sizeof(uint64_t), M_COUNTERS));
}
struct cpumem *
counters_alloc_ncpus(struct cpumem *cm, unsigned int n)
{
/* this is unnecessary, but symmetrical */
return (cpumem_malloc_ncpus(cm, n * sizeof(uint64_t), M_COUNTERS));
}
void
counters_free(struct cpumem *cm, unsigned int n)
{
cpumem_free(cm, M_COUNTERS, n * sizeof(uint64_t));
}
void
counters_read(struct cpumem *cm, uint64_t *output, unsigned int n)
{
uint64_t *counters;
unsigned int i;
int s;
counters = (uint64_t *)cm;
s = splhigh();
for (i = 0; i < n; i++)
output[i] = counters[i];
splx(s);
}
void
counters_zero(struct cpumem *cm, unsigned int n)
{
uint64_t *counters;
unsigned int i;
int s;
counters = (uint64_t *)cm;
s = splhigh();
for (i = 0; i < n; i++)
counters[i] = 0;
splx(s);
}
#endif /* MULTIPROCESSOR */
open /syzkaller/managers/main/kernel/machine/pmap.h: no such file or directory
75
76
76
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/* $OpenBSD: vfs_sync.c,v 1.68 2022/08/14 01:58:28 jsg Exp $ */
/*
* Portions of this code are:
*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Syncer daemon
*/
#include <sys/queue.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/time.h>
#ifdef FFS_SOFTUPDATES
int softdep_process_worklist(struct mount *);
#endif
/*
* The workitem queue.
*/
#define SYNCER_MAXDELAY 32 /* maximum sync delay time */
#define SYNCER_DEFAULT 30 /* default sync delay time */
int syncer_maxdelay = SYNCER_MAXDELAY; /* maximum delay time */
int syncdelay = SYNCER_DEFAULT; /* time to delay syncing vnodes */
int rushjob = 0; /* number of slots to run ASAP */
int stat_rush_requests = 0; /* number of rush requests */
int syncer_delayno = 0;
long syncer_mask;
LIST_HEAD(synclist, vnode);
static struct synclist *syncer_workitem_pending;
struct proc *syncerproc;
int syncer_chan;
/*
* The workitem queue.
*
* It is useful to delay writes of file data and filesystem metadata
* for tens of seconds so that quickly created and deleted files need
* not waste disk bandwidth being created and removed. To realize this,
* we append vnodes to a "workitem" queue. When running with a soft
* updates implementation, most pending metadata dependencies should
* not wait for more than a few seconds. Thus, mounted block devices
* are delayed only about half the time that file data is delayed.
* Similarly, directory updates are more critical, so are only delayed
* about a third the time that file data is delayed. Thus, there are
* SYNCER_MAXDELAY queues that are processed round-robin at a rate of
* one each second (driven off the filesystem syncer process). The
* syncer_delayno variable indicates the next queue that is to be processed.
* Items that need to be processed soon are placed in this queue:
*
* syncer_workitem_pending[syncer_delayno]
*
* A delay of fifteen seconds is done by placing the request fifteen
* entries later in the queue:
*
* syncer_workitem_pending[(syncer_delayno + 15) & syncer_mask]
*
*/
void
vn_initialize_syncerd(void)
{
syncer_workitem_pending = hashinit(syncer_maxdelay, M_VNODE, M_WAITOK,
&syncer_mask);
syncer_maxdelay = syncer_mask + 1;
}
/*
* Add an item to the syncer work queue.
*/
void
vn_syncer_add_to_worklist(struct vnode *vp, int delay)
{
int s, slot;
if (delay > syncer_maxdelay - 2)
delay = syncer_maxdelay - 2;
slot = (syncer_delayno + delay) & syncer_mask;
s = splbio();
if (vp->v_bioflag & VBIOONSYNCLIST)
LIST_REMOVE(vp, v_synclist);
vp->v_bioflag |= VBIOONSYNCLIST;
LIST_INSERT_HEAD(&syncer_workitem_pending[slot], vp, v_synclist);
splx(s);
}
/*
* System filesystem synchronizer daemon.
*/
void
syncer_thread(void *arg)
{
uint64_t elapsed, start;
struct proc *p = curproc;
struct synclist *slp;
struct vnode *vp;
int s;
for (;;) {
start = getnsecuptime();
/*
* Push files whose dirty time has expired.
*/
s = splbio();
slp = &syncer_workitem_pending[syncer_delayno];
syncer_delayno += 1;
if (syncer_delayno == syncer_maxdelay)
syncer_delayno = 0;
while ((vp = LIST_FIRST(slp)) != NULL) {
if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT)) {
/*
* If we fail to get the lock, we move this
* vnode one second ahead in time.
* XXX - no good, but the best we can do.
*/
vn_syncer_add_to_worklist(vp, 1);
continue;
}
splx(s);
(void) VOP_FSYNC(vp, p->p_ucred, MNT_LAZY, p);
vput(vp);
s = splbio();
if (LIST_FIRST(slp) == vp) {
/*
* Note: disk vps can remain on the
* worklist too with no dirty blocks, but
* since sync_fsync() moves it to a different
* slot we are safe.
*/
#ifdef DIAGNOSTIC
if (LIST_FIRST(&vp->v_dirtyblkhd) == NULL &&
vp->v_type != VBLK) {
vprint("fsync failed", vp);
if (vp->v_mount != NULL)
printf("mounted on: %s\n",
vp->v_mount->mnt_stat.f_mntonname);
panic("%s: fsync failed", __func__);
}
#endif /* DIAGNOSTIC */
/*
* Put us back on the worklist. The worklist
* routine will remove us from our current
* position and then add us back in at a later
* position.
*/
vn_syncer_add_to_worklist(vp, syncdelay);
}
sched_pause(yield);
}
splx(s);
#ifdef FFS_SOFTUPDATES
/*
* Do soft update processing.
*/
softdep_process_worklist(NULL);
#endif
/*
* The variable rushjob allows the kernel to speed up the
* processing of the filesystem syncer process. A rushjob
* value of N tells the filesystem syncer to process the next
* N seconds worth of work on its queue ASAP. Currently rushjob
* is used by the soft update code to speed up the filesystem
* syncer process when the incore state is getting so far
* ahead of the disk that the kernel memory pool is being
* threatened with exhaustion.
*/
if (rushjob > 0) {
rushjob -= 1;
continue;
}
/*
* If it has taken us less than a second to process the
* current work, then wait. Otherwise start right over
* again. We can still lose time if any single round
* takes more than two seconds, but it does not really
* matter as we are just trying to generally pace the
* filesystem activity.
*/
elapsed = getnsecuptime() - start;
if (elapsed < SEC_TO_NSEC(1)) {
tsleep_nsec(&syncer_chan, PPAUSE, "syncer",
SEC_TO_NSEC(1) - elapsed);
}
}
}
/*
* Request the syncer daemon to speed up its work.
* We never push it to speed up more than half of its
* normal turn time, otherwise it could take over the cpu.
*/
int
speedup_syncer(void)
{
if (syncerproc)
wakeup_proc(syncerproc, &syncer_chan);
if (rushjob < syncdelay / 2) {
rushjob += 1;
stat_rush_requests += 1;
return 1;
}
return 0;
}
/* Routine to create and manage a filesystem syncer vnode. */
int sync_fsync(void *);
int sync_inactive(void *);
int sync_print(void *);
const struct vops sync_vops = {
.vop_close = nullop,
.vop_fsync = sync_fsync,
.vop_inactive = sync_inactive,
.vop_reclaim = nullop,
.vop_lock = nullop,
.vop_unlock = nullop,
.vop_islocked = nullop,
.vop_print = sync_print
};
/*
* Create a new filesystem syncer vnode for the specified mount point.
*/
int
vfs_allocate_syncvnode(struct mount *mp)
{
struct vnode *vp;
static long start, incr, next;
int error;
/* Allocate a new vnode */
if ((error = getnewvnode(VT_VFS, mp, &sync_vops, &vp)) != 0) {
mp->mnt_syncer = NULL;
return (error);
}
vp->v_writecount = 1;
vp->v_type = VNON;
/*
* Place the vnode onto the syncer worklist. We attempt to
* scatter them about on the list so that they will go off
* at evenly distributed times even if all the filesystems
* are mounted at once.
*/
next += incr;
if (next == 0 || next > syncer_maxdelay) {
start /= 2;
incr /= 2;
if (start == 0) {
start = syncer_maxdelay / 2;
incr = syncer_maxdelay;
}
next = start;
}
vn_syncer_add_to_worklist(vp, next);
mp->mnt_syncer = vp;
return (0);
}
/*
* Do a lazy sync of the filesystem.
*/
int
sync_fsync(void *v)
{
struct vop_fsync_args *ap = v;
struct vnode *syncvp = ap->a_vp;
struct mount *mp = syncvp->v_mount;
int asyncflag;
/*
* We only need to do something if this is a lazy evaluation.
*/
if (ap->a_waitfor != MNT_LAZY)
return (0);
/*
* Move ourselves to the back of the sync list.
*/
vn_syncer_add_to_worklist(syncvp, syncdelay);
/*
* Walk the list of vnodes pushing all that are dirty and
* not already on the sync list.
*/
if (vfs_busy(mp, VB_READ|VB_NOWAIT) == 0) {
asyncflag = mp->mnt_flag & MNT_ASYNC;
mp->mnt_flag &= ~MNT_ASYNC;
VFS_SYNC(mp, MNT_LAZY, 0, ap->a_cred, ap->a_p);
if (asyncflag)
mp->mnt_flag |= MNT_ASYNC;
vfs_unbusy(mp);
}
return (0);
}
/*
* The syncer vnode is no longer needed and is being decommissioned.
*/
int
sync_inactive(void *v)
{
struct vop_inactive_args *ap = v;
struct vnode *vp = ap->a_vp;
int s;
if (vp->v_usecount == 0) {
VOP_UNLOCK(vp);
return (0);
}
vp->v_mount->mnt_syncer = NULL;
s = splbio();
LIST_REMOVE(vp, v_synclist);
vp->v_bioflag &= ~VBIOONSYNCLIST;
splx(s);
vp->v_writecount = 0;
vput(vp);
return (0);
}
/*
* Print out a syncer vnode.
*/
int
sync_print(void *v)
{
printf("syncer vnode\n");
return (0);
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
/* $OpenBSD: ucom.c,v 1.74 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: ucom.c,v 1.49 2003/01/01 00:10:25 thorpej Exp $ */
/*
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This code is very heavily based on the 16550 driver, com.c.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <sys/device.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/uhidev.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/ucomvar.h>
#include "ucom.h"
#if NUCOM > 0
#ifdef UCOM_DEBUG
#define DPRINTFN(n, x) do { if (ucomdebug > (n)) printf x; } while (0)
int ucomdebug = 0;
#else
#define DPRINTFN(n, x)
#endif
#define DPRINTF(x) DPRINTFN(0, x)
#define UCOMUNIT_MASK 0x7f
#define UCOMCUA_MASK 0x80
#define LINESW(tp, func) (linesw[(tp)->t_line].func)
#define UCOMUNIT(x) (minor(x) & UCOMUNIT_MASK)
#define UCOMCUA(x) (minor(x) & UCOMCUA_MASK)
struct ucom_softc {
struct device sc_dev; /* base device */
struct usbd_device *sc_uparent; /* USB device */
struct uhidev_softc *sc_uhidev; /* hid device (if deeper) */
struct usbd_interface *sc_iface; /* data interface */
int sc_bulkin_no; /* bulk in endpoint address */
struct usbd_pipe *sc_bulkin_pipe;/* bulk in pipe */
struct usbd_xfer *sc_ixfer; /* read request */
u_char *sc_ibuf; /* read buffer */
u_int sc_ibufsize; /* read buffer size */
u_int sc_ibufsizepad; /* read buffer size padded */
int sc_bulkout_no; /* bulk out endpoint address */
struct usbd_pipe *sc_bulkout_pipe;/* bulk out pipe */
struct usbd_xfer *sc_oxfer; /* write request */
u_char *sc_obuf; /* write buffer */
u_int sc_obufsize; /* write buffer size */
u_int sc_opkthdrlen; /* header length of
* output packet */
struct usbd_pipe *sc_ipipe; /* hid interrupt input pipe */
struct usbd_pipe *sc_opipe; /* hid interrupt pipe */
const struct ucom_methods *sc_methods;
void *sc_parent;
int sc_portno;
struct tty *sc_tty; /* our tty */
u_char sc_lsr;
u_char sc_msr;
u_char sc_mcr;
u_char sc_tx_stopped;
int sc_swflags;
u_char sc_cua;
int sc_error;
struct rwlock sc_lock; /* lock during open */
int sc_open;
int sc_refcnt;
};
void ucom_cleanup(struct ucom_softc *);
void ucom_hwiflow(struct ucom_softc *);
int ucomparam(struct tty *, struct termios *);
void ucomstart(struct tty *);
void ucom_shutdown(struct ucom_softc *);
int ucom_do_open(dev_t, int, int, struct proc *);
int ucom_do_ioctl(struct ucom_softc *, u_long, caddr_t, int, struct proc *);
int ucom_do_close(struct ucom_softc *, int, int , struct proc *);
void ucom_dtr(struct ucom_softc *, int);
void ucom_rts(struct ucom_softc *, int);
void ucom_break(struct ucom_softc *, int);
usbd_status ucomstartread(struct ucom_softc *);
void ucomreadcb(struct usbd_xfer *, void *, usbd_status);
void ucomwritecb(struct usbd_xfer *, void *, usbd_status);
void tiocm_to_ucom(struct ucom_softc *, u_long, int);
int ucom_to_tiocm(struct ucom_softc *);
void ucom_lock(struct ucom_softc *);
void ucom_unlock(struct ucom_softc *);
int ucom_match(struct device *, void *, void *);
void ucom_attach(struct device *, struct device *, void *);
int ucom_detach(struct device *, int);
struct cfdriver ucom_cd = {
NULL, "ucom", DV_TTY
};
const struct cfattach ucom_ca = {
sizeof(struct ucom_softc),
ucom_match,
ucom_attach,
ucom_detach,
};
void
ucom_lock(struct ucom_softc *sc)
{
rw_enter_write(&sc->sc_lock);
}
void
ucom_unlock(struct ucom_softc *sc)
{
rw_exit_write(&sc->sc_lock);
}
int
ucom_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
ucom_attach(struct device *parent, struct device *self, void *aux)
{
struct ucom_softc *sc = (struct ucom_softc *)self;
struct ucom_attach_args *uca = aux;
struct tty *tp;
if (uca->info != NULL)
printf(", %s", uca->info);
printf("\n");
sc->sc_uparent = uca->device;
sc->sc_iface = uca->iface;
sc->sc_bulkout_no = uca->bulkout;
sc->sc_bulkin_no = uca->bulkin;
sc->sc_uhidev = uca->uhidev;
sc->sc_ibufsize = uca->ibufsize;
sc->sc_ibufsizepad = uca->ibufsizepad;
sc->sc_obufsize = uca->obufsize;
sc->sc_opkthdrlen = uca->opkthdrlen;
sc->sc_methods = uca->methods;
sc->sc_parent = uca->arg;
sc->sc_portno = uca->portno;
tp = ttymalloc(1000000);
tp->t_oproc = ucomstart;
tp->t_param = ucomparam;
sc->sc_tty = tp;
sc->sc_cua = 0;
rw_init(&sc->sc_lock, "ucomlk");
}
int
ucom_detach(struct device *self, int flags)
{
struct ucom_softc *sc = (struct ucom_softc *)self;
struct tty *tp = sc->sc_tty;
int maj, mn;
int s;
DPRINTF(("ucom_detach: sc=%p flags=%d tp=%p, pipe=%d,%d\n",
sc, flags, tp, sc->sc_bulkin_no, sc->sc_bulkout_no));
if (sc->sc_bulkin_pipe != NULL) {
usbd_close_pipe(sc->sc_bulkin_pipe);
sc->sc_bulkin_pipe = NULL;
}
if (sc->sc_bulkout_pipe != NULL) {
usbd_close_pipe(sc->sc_bulkout_pipe);
sc->sc_bulkout_pipe = NULL;
}
if (sc->sc_ixfer != NULL) {
if (sc->sc_bulkin_no != -1) {
usbd_free_buffer(sc->sc_ixfer);
sc->sc_ibuf = NULL;
usbd_free_xfer(sc->sc_ixfer);
}
sc->sc_ixfer = NULL;
}
if (sc->sc_oxfer != NULL) {
usbd_free_buffer(sc->sc_oxfer);
sc->sc_obuf = NULL;
if (sc->sc_bulkin_no != -1)
usbd_free_xfer(sc->sc_oxfer);
sc->sc_oxfer = NULL;
}
s = splusb();
if (--sc->sc_refcnt >= 0) {
/* Wake up anyone waiting */
if (tp != NULL) {
CLR(tp->t_state, TS_CARR_ON);
CLR(tp->t_cflag, CLOCAL | MDMBUF);
ttyflush(tp, FREAD|FWRITE);
}
usb_detach_wait(&sc->sc_dev);
}
splx(s);
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == ucomopen)
break;
/* Nuke the vnodes for any open instances. */
mn = self->dv_unit;
DPRINTF(("ucom_detach: maj=%d mn=%d\n", maj, mn));
vdevgone(maj, mn, mn, VCHR);
vdevgone(maj, mn | UCOMCUA_MASK, mn | UCOMCUA_MASK, VCHR);
/* Detach and free the tty. */
if (tp != NULL) {
(*LINESW(tp, l_close))(tp, FNONBLOCK, curproc);
s = spltty();
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
ttyclose(tp);
splx(s);
ttyfree(tp);
sc->sc_tty = NULL;
}
return (0);
}
void
ucom_shutdown(struct ucom_softc *sc)
{
struct tty *tp = sc->sc_tty;
DPRINTF(("ucom_shutdown\n"));
/*
* Hang up if necessary. Wait a bit, so the other side has time to
* notice even if we immediately open the port again.
*/
if (ISSET(tp->t_cflag, HUPCL)) {
ucom_dtr(sc, 0);
ucom_rts(sc, 0);
tsleep_nsec(sc, TTIPRI, ttclos, SEC_TO_NSEC(1));
}
}
int
ucomopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = UCOMUNIT(dev);
struct ucom_softc *sc;
int error;
if (unit >= ucom_cd.cd_ndevs)
return (ENXIO);
sc = ucom_cd.cd_devs[unit];
if (sc == NULL)
return (ENXIO);
sc->sc_error = 0;
if (usbd_is_dying(sc->sc_uparent))
return (EIO);
if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
return (ENXIO);
sc->sc_refcnt++;
error = ucom_do_open(dev, flag, mode, p);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ucom_do_open(dev_t dev, int flag, int mode, struct proc *p)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
usbd_status err;
struct tty *tp;
struct termios t;
int error, s;
/* open the pipes if this is the first open */
ucom_lock(sc);
s = splusb();
if (sc->sc_open == 0) {
DPRINTF(("ucomopen: open pipes in=%d out=%d\n",
sc->sc_bulkin_no, sc->sc_bulkout_no));
DPRINTF(("ucomopen: hid %p pipes in=%p out=%p\n",
sc->sc_uhidev, sc->sc_ipipe, sc->sc_opipe));
if (sc->sc_bulkin_no != -1) {
/* Open the bulk pipes */
err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
&sc->sc_bulkin_pipe);
if (err) {
DPRINTF(("%s: open bulk out error (addr %d), err=%s\n",
sc->sc_dev.dv_xname, sc->sc_bulkin_no,
usbd_errstr(err)));
error = EIO;
goto fail_0;
}
err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
if (err) {
DPRINTF(("%s: open bulk in error (addr %d), err=%s\n",
sc->sc_dev.dv_xname, sc->sc_bulkout_no,
usbd_errstr(err)));
error = EIO;
goto fail_1;
}
/* Allocate a request and an input buffer and start reading. */
sc->sc_ixfer = usbd_alloc_xfer(sc->sc_uparent);
if (sc->sc_ixfer == NULL) {
error = ENOMEM;
goto fail_2;
}
sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
sc->sc_ibufsizepad);
if (sc->sc_ibuf == NULL) {
error = ENOMEM;
goto fail_2;
}
sc->sc_oxfer = usbd_alloc_xfer(sc->sc_uparent);
if (sc->sc_oxfer == NULL) {
error = ENOMEM;
goto fail_3;
}
} else {
/*
* input/output pipes and xfers already allocated
* as is the input buffer.
*/
sc->sc_ipipe = sc->sc_uhidev->sc_ipipe;
sc->sc_ixfer = sc->sc_uhidev->sc_ixfer;
sc->sc_opipe = sc->sc_uhidev->sc_opipe;
sc->sc_oxfer = sc->sc_uhidev->sc_oxfer;
}
sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
sc->sc_obufsize + sc->sc_opkthdrlen);
if (sc->sc_obuf == NULL) {
error = ENOMEM;
goto fail_4;
}
if (sc->sc_methods->ucom_open != NULL) {
error = sc->sc_methods->ucom_open(sc->sc_parent,
sc->sc_portno);
if (error) {
ucom_cleanup(sc);
splx(s);
ucom_unlock(sc);
return (error);
}
}
ucom_status_change(sc);
ucomstartread(sc);
sc->sc_open = 1;
}
splx(s);
s = spltty();
ucom_unlock(sc);
tp = sc->sc_tty;
splx(s);
DPRINTF(("ucomopen: unit=%d, tp=%p\n", UCOMUNIT(dev), tp));
tp->t_dev = dev;
if (!ISSET(tp->t_state, TS_ISOPEN)) {
SET(tp->t_state, TS_WOPEN);
ttychars(tp);
/*
* Initialize the termios status to the defaults. Add in the
* sticky bits from TIOCSFLAGS.
*/
t.c_ispeed = 0;
t.c_ospeed = TTYDEF_SPEED;
t.c_cflag = TTYDEF_CFLAG;
if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
SET(t.c_cflag, CLOCAL);
if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
SET(t.c_cflag, CRTSCTS);
if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
SET(t.c_cflag, MDMBUF);
/* Make sure ucomparam() will do something. */
tp->t_ospeed = 0;
(void) ucomparam(tp, &t);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_lflag = TTYDEF_LFLAG;
s = spltty();
ttsetwater(tp);
/*
* Turn on DTR. We must always do this, even if carrier is not
* present, because otherwise we'd have to use TIOCSDTR
* immediately after setting CLOCAL, which applications do not
* expect. We always assert DTR while the device is open
* unless explicitly requested to deassert it.
*/
ucom_dtr(sc, 1);
/* When not using CRTSCTS, RTS follows DTR. */
if (!ISSET(t.c_cflag, CRTSCTS))
ucom_rts(sc, 1);
/* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/
ucom_hwiflow(sc);
if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) || UCOMCUA(dev) ||
ISSET(sc->sc_msr, UMSR_DCD) || ISSET(tp->t_cflag, MDMBUF))
SET(tp->t_state, TS_CARR_ON);
else
CLR(tp->t_state, TS_CARR_ON);
} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
return (EBUSY);
else
s = spltty();
if (UCOMCUA(dev)) {
if (ISSET(tp->t_state, TS_ISOPEN)) {
/* Someone is already dialed in */
splx(s);
return (EBUSY);
}
sc->sc_cua = 1;
} else {
/* tty (not cua) device, wait for carrier */
if (ISSET(flag, O_NONBLOCK)) {
if (sc->sc_cua) {
splx(s);
return (EBUSY);
}
} else {
while (sc->sc_cua || (!ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(tp->t_state, TS_CARR_ON))) {
SET(tp->t_state, TS_WOPEN);
error = ttysleep(tp, &tp->t_rawq,
TTIPRI | PCATCH, ttopen);
if (usbd_is_dying(sc->sc_uparent)) {
splx(s);
return (EIO);
}
/*
* If TS_WOPEN has been reset, that means the
* cua device has been closed. We don't want
* to fail in that case, so just go around
* again.
*/
if (error && ISSET(tp->t_state, TS_WOPEN)) {
CLR(tp->t_state, TS_WOPEN);
splx(s);
goto bad;
}
}
}
}
splx(s);
error = (*LINESW(tp, l_open))(dev, tp, p);
if (error)
goto bad;
return (0);
fail_4:
if (sc->sc_bulkin_no != -1)
usbd_free_xfer(sc->sc_oxfer);
sc->sc_oxfer = NULL;
fail_3:
usbd_free_xfer(sc->sc_ixfer);
sc->sc_ixfer = NULL;
fail_2:
usbd_close_pipe(sc->sc_bulkout_pipe);
sc->sc_bulkout_pipe = NULL;
fail_1:
usbd_close_pipe(sc->sc_bulkin_pipe);
sc->sc_bulkin_pipe = NULL;
fail_0:
splx(s);
ucom_unlock(sc);
return (error);
bad:
ucom_lock(sc);
ucom_cleanup(sc);
ucom_unlock(sc);
return (error);
}
int
ucomclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
int error;
if (sc == NULL || usbd_is_dying(sc->sc_uparent))
return (EIO);
DPRINTF(("ucomclose: unit=%d\n", UCOMUNIT(dev)));
sc->sc_refcnt++;
error = ucom_do_close(sc, flag, mode, p);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ucom_do_close(struct ucom_softc *sc, int flag, int mode, struct proc *p)
{
struct tty *tp = sc->sc_tty;
int s;
if (!ISSET(tp->t_state, TS_ISOPEN))
return (0);
ucom_lock(sc);
(*LINESW(tp, l_close))(tp, flag, p);
s = spltty();
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
sc->sc_cua = 0;
ttyclose(tp);
splx(s);
ucom_cleanup(sc);
if (sc->sc_methods->ucom_close != NULL)
sc->sc_methods->ucom_close(sc->sc_parent, sc->sc_portno);
ucom_unlock(sc);
return (0);
}
int
ucomread(dev_t dev, struct uio *uio, int flag)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
struct tty *tp;
int error;
if (sc == NULL || usbd_is_dying(sc->sc_uparent))
return (EIO);
if (sc->sc_error)
return (sc->sc_error);
sc->sc_refcnt++;
tp = sc->sc_tty;
error = (*LINESW(tp, l_read))(tp, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ucomwrite(dev_t dev, struct uio *uio, int flag)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
struct tty *tp;
int error;
if (sc == NULL || usbd_is_dying(sc->sc_uparent))
return (EIO);
sc->sc_refcnt++;
tp = sc->sc_tty;
error = (*LINESW(tp, l_write))(tp, uio, flag);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
struct tty *
ucomtty(dev_t dev)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
/*
* Return a pointer to our tty even if the device is dying
* in order to properly close it in the detach routine.
*/
if (sc == NULL)
return (NULL);
return (sc->sc_tty);
}
int
ucomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(dev)];
int error;
if (sc == NULL || usbd_is_dying(sc->sc_uparent))
return (EIO);
sc->sc_refcnt++;
error = ucom_do_ioctl(sc, cmd, data, flag, p);
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(&sc->sc_dev);
return (error);
}
int
ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, caddr_t data,
int flag, struct proc *p)
{
struct tty *tp = sc->sc_tty;
int error;
int s;
DPRINTF(("ucomioctl: cmd=0x%08lx\n", cmd));
error = (*LINESW(tp, l_ioctl))(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
if (sc->sc_methods->ucom_ioctl != NULL) {
error = sc->sc_methods->ucom_ioctl(sc->sc_parent,
sc->sc_portno, cmd, data, flag, p);
if (error != ENOTTY)
return (error);
}
error = 0;
DPRINTF(("ucomioctl: our cmd=0x%08lx\n", cmd));
s = spltty();
switch (cmd) {
case TIOCSBRK:
ucom_break(sc, 1);
break;
case TIOCCBRK:
ucom_break(sc, 0);
break;
case TIOCSDTR:
ucom_dtr(sc, 1);
break;
case TIOCCDTR:
ucom_dtr(sc, 0);
break;
case TIOCGFLAGS:
*(int *)data = sc->sc_swflags;
break;
case TIOCSFLAGS:
error = suser(p);
if (error)
break;
sc->sc_swflags = *(int *)data;
break;
case TIOCMSET:
case TIOCMBIS:
case TIOCMBIC:
tiocm_to_ucom(sc, cmd, *(int *)data);
break;
case TIOCMGET:
*(int *)data = ucom_to_tiocm(sc);
break;
default:
error = ENOTTY;
break;
}
splx(s);
return (error);
}
void
tiocm_to_ucom(struct ucom_softc *sc, u_long how, int ttybits)
{
u_char combits;
combits = 0;
if (ISSET(ttybits, TIOCM_DTR))
SET(combits, UMCR_DTR);
if (ISSET(ttybits, TIOCM_RTS))
SET(combits, UMCR_RTS);
switch (how) {
case TIOCMBIC:
CLR(sc->sc_mcr, combits);
break;
case TIOCMBIS:
SET(sc->sc_mcr, combits);
break;
case TIOCMSET:
CLR(sc->sc_mcr, UMCR_DTR | UMCR_RTS);
SET(sc->sc_mcr, combits);
break;
}
if (how == TIOCMSET || ISSET(combits, UMCR_DTR))
ucom_dtr(sc, (sc->sc_mcr & UMCR_DTR) != 0);
if (how == TIOCMSET || ISSET(combits, UMCR_RTS))
ucom_rts(sc, (sc->sc_mcr & UMCR_RTS) != 0);
}
int
ucom_to_tiocm(struct ucom_softc *sc)
{
u_char combits;
int ttybits = 0;
combits = sc->sc_mcr;
if (ISSET(combits, UMCR_DTR))
SET(ttybits, TIOCM_DTR);
if (ISSET(combits, UMCR_RTS))
SET(ttybits, TIOCM_RTS);
combits = sc->sc_msr;
if (ISSET(combits, UMSR_DCD))
SET(ttybits, TIOCM_CD);
if (ISSET(combits, UMSR_CTS))
SET(ttybits, TIOCM_CTS);
if (ISSET(combits, UMSR_DSR))
SET(ttybits, TIOCM_DSR);
if (ISSET(combits, UMSR_RI | UMSR_TERI))
SET(ttybits, TIOCM_RI);
#if 0
XXX;
if (sc->sc_ier != 0)
SET(ttybits, TIOCM_LE);
#endif
return (ttybits);
}
void
ucom_break(struct ucom_softc *sc, int onoff)
{
DPRINTF(("ucom_break: onoff=%d\n", onoff));
if (sc->sc_methods->ucom_set != NULL)
sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
UCOM_SET_BREAK, onoff);
}
void
ucom_dtr(struct ucom_softc *sc, int onoff)
{
DPRINTF(("ucom_dtr: onoff=%d\n", onoff));
if (sc->sc_methods->ucom_set != NULL)
sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
UCOM_SET_DTR, onoff);
}
void
ucom_rts(struct ucom_softc *sc, int onoff)
{
DPRINTF(("ucom_rts: onoff=%d\n", onoff));
if (sc->sc_methods->ucom_set != NULL)
sc->sc_methods->ucom_set(sc->sc_parent, sc->sc_portno,
UCOM_SET_RTS, onoff);
}
void
ucom_status_change(struct ucom_softc *sc)
{
struct tty *tp = sc->sc_tty;
u_char old_msr;
if (sc->sc_methods->ucom_get_status != NULL) {
old_msr = sc->sc_msr;
sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno,
&sc->sc_lsr, &sc->sc_msr);
ttytstamp(tp, old_msr & UMSR_CTS, sc->sc_msr & UMSR_CTS,
old_msr & UMSR_DCD, sc->sc_msr & UMSR_DCD);
if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD))
(*LINESW(tp, l_modem))(tp,
ISSET(sc->sc_msr, UMSR_DCD));
} else {
sc->sc_lsr = 0;
sc->sc_msr = 0;
}
}
int
ucomparam(struct tty *tp, struct termios *t)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
int error;
if (sc == NULL || usbd_is_dying(sc->sc_uparent))
return (EIO);
/* Check requested parameters. */
if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
return (EINVAL);
/*
* For the console, always force CLOCAL and !HUPCL, so that the port
* is always active.
*/
if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) {
SET(t->c_cflag, CLOCAL);
CLR(t->c_cflag, HUPCL);
}
/*
* If there were no changes, don't do anything. This avoids dropping
* input and improves performance when all we did was frob things like
* VMIN and VTIME.
*/
if (tp->t_ospeed == t->c_ospeed &&
tp->t_cflag == t->c_cflag)
return (0);
/* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */
/* And copy to tty. */
tp->t_ispeed = 0;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = t->c_cflag;
/*
* When not using CRTSCTS, RTS follows DTR.
* This assumes that the ucom_param() call will enable these signals
* for real.
*/
if (!ISSET(t->c_cflag, CRTSCTS))
sc->sc_mcr = UMCR_DTR | UMCR_RTS;
else
sc->sc_mcr = UMCR_DTR;
if (sc->sc_methods->ucom_param != NULL) {
error = sc->sc_methods->ucom_param(sc->sc_parent, sc->sc_portno,
t);
if (error)
return (error);
}
/* XXX worry about CHWFLOW */
/*
* Update the tty layer's idea of the carrier bit, in case we changed
* CLOCAL or MDMBUF. We don't hang up here; we only do that by
* explicit request.
*/
DPRINTF(("ucomparam: l_modem\n"));
(void) (*LINESW(tp, l_modem))(tp, 1 /* XXX carrier */ );
#if 0
XXX what if the hardware is not open
if (!ISSET(t->c_cflag, CHWFLOW)) {
if (sc->sc_tx_stopped) {
sc->sc_tx_stopped = 0;
ucomstart(tp);
}
}
#endif
return (0);
}
/*
* (un)block input via hw flowcontrol
*/
void
ucom_hwiflow(struct ucom_softc *sc)
{
DPRINTF(("ucom_hwiflow:\n"));
#if 0
XXX
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
if (sc->sc_mcr_rts == 0)
return;
if (ISSET(sc->sc_rx_flags, RX_ANY_BLOCK)) {
CLR(sc->sc_mcr, sc->sc_mcr_rts);
CLR(sc->sc_mcr_active, sc->sc_mcr_rts);
} else {
SET(sc->sc_mcr, sc->sc_mcr_rts);
SET(sc->sc_mcr_active, sc->sc_mcr_rts);
}
bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active);
#endif
}
void
ucomstart(struct tty *tp)
{
struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];
usbd_status err;
int s;
u_char *data;
int cnt;
if (sc == NULL || usbd_is_dying(sc->sc_uparent))
return;
s = spltty();
if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
DPRINTFN(4,("ucomstart: no go, state=0x%x\n", tp->t_state));
goto out;
}
if (sc->sc_tx_stopped)
goto out;
ttwakeupwr(tp);
if (tp->t_outq.c_cc == 0)
goto out;
/* Grab the first contiguous region of buffer space. */
data = tp->t_outq.c_cf;
cnt = ndqb(&tp->t_outq, 0);
if (cnt == 0) {
DPRINTF(("ucomstart: cnt==0\n"));
goto out;
}
SET(tp->t_state, TS_BUSY);
if (cnt > sc->sc_obufsize) {
DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
cnt = sc->sc_obufsize;
}
if (sc->sc_methods->ucom_write != NULL)
sc->sc_methods->ucom_write(sc->sc_parent, sc->sc_portno,
sc->sc_obuf, data, &cnt);
else
memcpy(sc->sc_obuf, data, cnt);
DPRINTFN(4,("ucomstart: %d chars\n", cnt));
#ifdef DIAGNOSTIC
if (sc->sc_oxfer == NULL) {
printf("ucomstart: null oxfer\n");
goto out;
}
#endif
if (sc->sc_bulkout_pipe != NULL) {
usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
(void *)sc, sc->sc_obuf, cnt,
USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
} else {
usbd_setup_xfer(sc->sc_oxfer, sc->sc_opipe,
(void *)sc, sc->sc_obuf, cnt,
USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
}
/* What can we do on error? */
err = usbd_transfer(sc->sc_oxfer);
#ifdef DIAGNOSTIC
if (err != USBD_IN_PROGRESS)
printf("ucomstart: err=%s\n", usbd_errstr(err));
#endif
out:
splx(s);
}
int
ucomstop(struct tty *tp, int flag)
{
DPRINTF(("ucomstop: flag=%d\n", flag));
#if 0
/*struct ucom_softc *sc = ucom_cd.cd_devs[UCOMUNIT(tp->t_dev)];*/
int s;
s = spltty();
if (ISSET(tp->t_state, TS_BUSY)) {
DPRINTF(("ucomstop: XXX\n"));
/* sc->sc_tx_stopped = 1; */
if (!ISSET(tp->t_state, TS_TTSTOP))
SET(tp->t_state, TS_FLUSH);
}
splx(s);
#endif
return (0);
}
void
ucomwritecb(struct usbd_xfer *xfer, void *p, usbd_status status)
{
struct ucom_softc *sc = (struct ucom_softc *)p;
struct tty *tp = sc->sc_tty;
u_int32_t cc;
int s;
DPRINTFN(5,("ucomwritecb: %p %p status=%d\n", xfer, p, status));
if (status == USBD_CANCELLED || usbd_is_dying(sc->sc_uparent))
goto error;
if (sc->sc_bulkin_pipe != NULL) {
if (status) {
usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
/* XXX we should restart after some delay. */
goto error;
}
usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
} else {
usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
// XXX above gives me wrong cc, no?
}
DPRINTFN(5,("ucomwritecb: cc=%d\n", cc));
/* convert from USB bytes to tty bytes */
cc -= sc->sc_opkthdrlen;
s = spltty();
CLR(tp->t_state, TS_BUSY);
if (ISSET(tp->t_state, TS_FLUSH))
CLR(tp->t_state, TS_FLUSH);
else
ndflush(&tp->t_outq, cc);
(*LINESW(tp, l_start))(tp);
splx(s);
return;
error:
s = spltty();
CLR(tp->t_state, TS_BUSY);
splx(s);
}
usbd_status
ucomstartread(struct ucom_softc *sc)
{
usbd_status err;
DPRINTFN(5,("ucomstartread: start\n"));
#ifdef DIAGNOSTIC
if (sc->sc_ixfer == NULL) {
DPRINTF(("ucomstartread: null ixfer\n"));
return (USBD_INVAL);
}
#endif
if (sc->sc_bulkin_pipe != NULL) {
usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
(void *)sc,
sc->sc_ibuf, sc->sc_ibufsize,
USBD_SHORT_XFER_OK | USBD_NO_COPY,
USBD_NO_TIMEOUT, ucomreadcb);
err = usbd_transfer(sc->sc_ixfer);
if (err != USBD_IN_PROGRESS) {
DPRINTF(("ucomstartread: err=%s\n", usbd_errstr(err)));
return (err);
}
}
return (USBD_NORMAL_COMPLETION);
}
void
ucomreadcb(struct usbd_xfer *xfer, void *p, usbd_status status)
{
struct ucom_softc *sc = (struct ucom_softc *)p;
struct tty *tp = sc->sc_tty;
int (*rint)(int c, struct tty *tp) = LINESW(tp, l_rint);
usbd_status err;
u_int32_t cc;
u_char *cp;
int s;
DPRINTFN(5,("ucomreadcb: status=%d\n", status));
if (status == USBD_CANCELLED || status == USBD_IOERROR ||
usbd_is_dying(sc->sc_uparent)) {
DPRINTF(("ucomreadcb: dying\n"));
/* Send something to wake upper layer */
sc->sc_error = EIO;
s = spltty();
(*rint)('\n', tp);
ttwakeup(tp);
splx(s);
return;
}
if (status) {
if (sc->sc_bulkin_pipe != NULL) {
usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
/* XXX we should restart after some delay. */
return;
}
}
usbd_get_xfer_status(xfer, NULL, (void *)&cp, &cc, NULL);
DPRINTFN(5,("ucomreadcb: got %d chars, tp=%p\n", cc, tp));
if (sc->sc_methods->ucom_read != NULL)
sc->sc_methods->ucom_read(sc->sc_parent, sc->sc_portno,
&cp, &cc);
s = spltty();
/* Give characters to tty layer. */
while (cc-- > 0) {
DPRINTFN(7,("ucomreadcb: char=0x%02x\n", *cp));
if ((*rint)(*cp++, tp) == -1) {
/* XXX what should we do? */
printf("%s: lost %d chars\n", sc->sc_dev.dv_xname,
cc);
break;
}
}
splx(s);
err = ucomstartread(sc);
if (err) {
printf("%s: read start failed\n", sc->sc_dev.dv_xname);
/* XXX what should we dow now? */
}
}
void
ucom_cleanup(struct ucom_softc *sc)
{
DPRINTF(("ucom_cleanup: closing pipes\n"));
sc->sc_open = 0;
ucom_shutdown(sc);
if (sc->sc_bulkin_pipe != NULL) {
usbd_close_pipe(sc->sc_bulkin_pipe);
sc->sc_bulkin_pipe = NULL;
}
if (sc->sc_bulkout_pipe != NULL) {
usbd_close_pipe(sc->sc_bulkout_pipe);
sc->sc_bulkout_pipe = NULL;
}
if (sc->sc_ixfer != NULL) {
if (sc->sc_bulkin_no != -1) {
usbd_free_buffer(sc->sc_ixfer);
sc->sc_ibuf = NULL;
usbd_free_xfer(sc->sc_ixfer);
}
sc->sc_ixfer = NULL;
}
if (sc->sc_oxfer != NULL) {
usbd_free_buffer(sc->sc_oxfer);
sc->sc_obuf = NULL;
if (sc->sc_bulkin_no != -1)
usbd_free_xfer(sc->sc_oxfer);
sc->sc_oxfer = NULL;
}
}
#endif /* NUCOM > 0 */
int
ucomprint(void *aux, const char *pnp)
{
struct ucom_attach_args *uca = aux;
if (pnp)
printf("ucom at %s", pnp);
if (uca->portno != UCOM_UNK_PORTNO)
printf(" portno %d", uca->portno);
return (UNCONF);
}
int
ucomsubmatch(struct device *parent, void *match, void *aux)
{
struct ucom_attach_args *uca = aux;
struct cfdata *cf = match;
if (uca->portno != UCOM_UNK_PORTNO &&
cf->ucomcf_portno != UCOM_UNK_PORTNO &&
cf->ucomcf_portno != uca->portno)
return (0);
return ((*cf->cf_attach->ca_match)(parent, cf, aux));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/* $OpenBSD: kref.h,v 1.4 2020/06/17 02:58:15 jsg Exp $ */
/*
* Copyright (c) 2015 Mark Kettenis
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _LINUX_KREF_H
#define _LINUX_KREF_H
#include <sys/types.h>
#include <sys/rwlock.h>
#include <sys/atomic.h>
#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/refcount.h>
struct kref {
uint32_t refcount;
};
static inline void
kref_init(struct kref *ref)
{
atomic_set(&ref->refcount, 1);
}
static inline unsigned int
kref_read(const struct kref *ref)
{
return atomic_read(&ref->refcount);
}
static inline void
kref_get(struct kref *ref)
{
atomic_inc_int(&ref->refcount);
}
static inline int
kref_get_unless_zero(struct kref *ref)
{
if (ref->refcount != 0) {
atomic_inc_int(&ref->refcount);
return (1);
} else {
return (0);
}
}
static inline int
kref_put(struct kref *ref, void (*release)(struct kref *ref))
{
if (atomic_dec_int_nv(&ref->refcount) == 0) {
release(ref);
return 1;
}
return 0;
}
static inline int
kref_put_mutex(struct kref *kref, void (*release)(struct kref *kref),
struct rwlock *lock)
{
if (!atomic_add_unless(&kref->refcount, -1, 1)) {
rw_enter_write(lock);
if (likely(atomic_dec_and_test(&kref->refcount))) {
release(kref);
return 1;
}
rw_exit_write(lock);
return 0;
}
return 0;
}
static inline int
kref_put_lock(struct kref *kref, void (*release)(struct kref *kref),
struct mutex *lock)
{
if (!atomic_add_unless(&kref->refcount, -1, 1)) {
mtx_enter(lock);
if (likely(atomic_dec_and_test(&kref->refcount))) {
release(kref);
return 1;
}
mtx_leave(lock);
return 0;
}
return 0;
}
#endif
2
5
5
5
24
24
64
64
18
47
44
20
65
18
20
20
87
85
140
122
20
65
86
138
127
136
18
18
13
116
7
32
32
20
8
131
13
135
2
18
139
1
137
7
136
18
5
138
138
138
138
64
86
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
/* $OpenBSD: subr_prf.c,v 1.106 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: subr_prf.c,v 1.45 1997/10/24 18:14:25 chuck Exp $ */
/*-
* Copyright (c) 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/reboot.h>
#include <sys/msgbuf.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/tprintf.h>
#include <sys/syslog.h>
#include <sys/pool.h>
#include <sys/mutex.h>
#include <dev/cons.h>
/*
* note that stdarg.h and the ansi style va_start macro is used for both
* ansi and traditional c compilers.
*/
#include <sys/stdarg.h>
#ifdef DDB
#include <ddb/db_output.h> /* db_printf, db_putchar prototypes */
#include <ddb/db_var.h> /* db_log, db_radix */
#endif
/*
* defines
*/
/* flags for kprintf */
#define TOCONS 0x01 /* to the console */
#define TOTTY 0x02 /* to the process' tty */
#define TOLOG 0x04 /* to the kernel message buffer */
#define TOBUFONLY 0x08 /* to the buffer (only) [for snprintf] */
#define TODDB 0x10 /* to ddb console */
#define TOCOUNT 0x20 /* act like [v]snprintf */
/* max size buffer kprintf needs to print quad_t [size in base 8 + \0] */
#define KPRINTF_BUFSIZE (sizeof(quad_t) * NBBY / 3 + 2)
/*
* local prototypes
*/
int kprintf(const char *, int, void *, char *, va_list);
void kputchar(int, int, struct tty *);
struct mutex kprintf_mutex =
MUTEX_INITIALIZER_FLAGS(IPL_HIGH, "kprintf", MTX_NOWITNESS);
/*
* globals
*/
extern int log_open; /* subr_log: is /dev/klog open? */
const char *panicstr; /* arg to first call to panic (used as a flag
to indicate that panic has already been called). */
#ifdef DDB
/*
* Enter ddb on panic.
*/
int db_panic = 1;
/*
* db_console controls if we can be able to enter ddb by a special key
* combination (machine dependent).
* If DDB_SAFE_CONSOLE is defined in the kernel configuration it allows
* to break into console during boot. It's _really_ useful when debugging
* some things in the kernel that can cause init(8) to crash.
*/
#ifdef DDB_SAFE_CONSOLE
int db_console = 1;
#else
int db_console = 0;
#endif
#endif
/*
* panic on spl assertion failure?
*/
#ifdef SPLASSERT_WATCH
int splassert_ctl = 3;
#else
int splassert_ctl = 1;
#endif
/*
* v_putc: routine to putc on virtual console
*
* the v_putc pointer can be used to redirect the console cnputc elsewhere
* [e.g. to a "virtual console"].
*/
void (*v_putc)(int) = cnputc; /* start with cnputc (normal cons) */
/*
* Silence kernel printf when masquerading as a bootloader.
*/
#ifdef BOOT_QUIET
int printf_flags = TOLOG;
#else
int printf_flags = TOCONS | TOLOG;
#endif
/*
* functions
*/
/*
* Partial support (the failure case) of the assertion facility
* commonly found in userland.
*/
void
__assert(const char *t, const char *f, int l, const char *e)
{
panic(__KASSERTSTR, t, e, f, l);
}
/*
* tablefull: warn that a system table is full
*/
void
tablefull(const char *tab)
{
log(LOG_ERR, "%s: table is full\n", tab);
}
/*
* If we have panicked, prefer db_printf() and db_vprintf() where
* available.
*/
#ifdef DDB
#define panic_printf(...) db_printf(__VA_ARGS__)
#define panic_vprintf(...) db_vprintf(__VA_ARGS__)
#else
#define panic_printf(...) printf(__VA_ARGS__)
#define panic_vprintf(...) vprintf(__VA_ARGS__)
#endif
/*
* panic: handle an unresolvable fatal error
*
* prints "panic: <message>" and reboots. if called twice (i.e. recursive
* call) we avoid trying to sync the disk and just reboot (to avoid
* recursive panics).
*/
void
panic(const char *fmt, ...)
{
struct cpu_info *ci = curcpu();
int bootopt;
va_list ap;
bootopt = RB_AUTOBOOT | RB_DUMP;
if (atomic_cas_ptr(&panicstr, NULL, ci->ci_panicbuf) != NULL)
bootopt |= RB_NOSYNC;
/* do not trigger assertions, we know that we are inconsistent */
splassert_ctl = 0;
#ifdef BOOT_QUIET
printf_flags |= TOCONS; /* make sure we see kernel printf output */
#endif
/*
* All panic messages are printed, but only the first panic on a
* given CPU is written to its panicbuf.
*/
if (ci->ci_panicbuf[0] == '\0') {
va_start(ap, fmt);
vsnprintf(ci->ci_panicbuf, sizeof(ci->ci_panicbuf), fmt, ap);
va_end(ap);
panic_printf("panic: %s\n", ci->ci_panicbuf);
} else {
panic_printf("panic: ");
va_start(ap, fmt);
panic_vprintf(fmt, ap);
va_end(ap);
panic_printf("\n");
}
#ifdef DDB
if (db_panic)
db_enter();
else
db_stack_dump();
#endif
reboot(bootopt);
/* NOTREACHED */
}
/*
* We print only the function name. The file name is usually very long and
* would eat tons of space in the kernel.
*/
void
splassert_fail(int wantipl, int haveipl, const char *func)
{
if (panicstr || db_active)
return;
printf("splassert: %s: want %d have %d\n", func, wantipl, haveipl);
switch (splassert_ctl) {
case 1:
break;
case 2:
#ifdef DDB
db_stack_dump();
#endif
break;
case 3:
#ifdef DDB
db_stack_dump();
db_enter();
#endif
break;
default:
panic("spl assertion failure in %s", func);
}
}
/*
* kernel logging functions: log, logpri, addlog
*/
/*
* log: write to the log buffer
*
* => will not sleep [so safe to call from interrupt]
* => will log to console if /dev/klog isn't open
*/
void
log(int level, const char *fmt, ...)
{
int s;
va_list ap;
s = splhigh();
logpri(level); /* log the level first */
va_start(ap, fmt);
kprintf(fmt, TOLOG, NULL, NULL, ap);
va_end(ap);
splx(s);
if (!log_open) {
va_start(ap, fmt);
mtx_enter(&kprintf_mutex);
kprintf(fmt, TOCONS, NULL, NULL, ap);
mtx_leave(&kprintf_mutex);
va_end(ap);
}
logwakeup(); /* wake up anyone waiting for log msgs */
}
/*
* logpri: log the priority level to the klog
*/
void
logpri(int level)
{
char *p;
char snbuf[KPRINTF_BUFSIZE];
kputchar('<', TOLOG, NULL);
snprintf(snbuf, sizeof snbuf, "%d", level);
for (p = snbuf ; *p ; p++)
kputchar(*p, TOLOG, NULL);
kputchar('>', TOLOG, NULL);
}
/*
* addlog: add info to previous log message
*/
int
addlog(const char *fmt, ...)
{
int s;
va_list ap;
s = splhigh();
va_start(ap, fmt);
kprintf(fmt, TOLOG, NULL, NULL, ap);
va_end(ap);
splx(s);
if (!log_open) {
va_start(ap, fmt);
mtx_enter(&kprintf_mutex);
kprintf(fmt, TOCONS, NULL, NULL, ap);
mtx_leave(&kprintf_mutex);
va_end(ap);
}
logwakeup();
return(0);
}
/*
* kputchar: print a single character on console or user terminal.
*
* => if console, then the last MSGBUFS chars are saved in msgbuf
* for inspection later (e.g. dmesg/syslog)
*/
void
kputchar(int c, int flags, struct tty *tp)
{
extern int msgbufmapped;
if (panicstr)
constty = NULL;
if ((flags & TOCONS) && tp == NULL && constty != NULL && !db_active) {
tp = constty;
flags |= TOTTY;
}
if ((flags & TOTTY) && tp && tputchar(c, tp) < 0 &&
(flags & TOCONS) && tp == constty)
constty = NULL;
if ((flags & TOLOG) &&
c != '\0' && c != '\r' && c != 0177 && msgbufmapped)
msgbuf_putchar(msgbufp, c);
if ((flags & TOCONS) && (constty == NULL || db_active) && c != '\0')
(*v_putc)(c);
#ifdef DDB
if (flags & TODDB)
db_putchar(c);
#endif
}
/*
* uprintf: print to the controlling tty of the current process
*
* => we may block if the tty queue is full
* => no message is printed if the queue doesn't clear in a reasonable
* time
*/
void
uprintf(const char *fmt, ...)
{
struct process *pr = curproc->p_p;
va_list ap;
if (pr->ps_flags & PS_CONTROLT && pr->ps_session->s_ttyvp) {
va_start(ap, fmt);
kprintf(fmt, TOTTY, pr->ps_session->s_ttyp, NULL, ap);
va_end(ap);
}
}
#if defined(NFSSERVER) || defined(NFSCLIENT)
/*
* tprintf functions: used to send messages to a specific process
*
* usage:
* get a tpr_t handle on a process "p" by using "tprintf_open(p)"
* use the handle when calling "tprintf"
* when done, do a "tprintf_close" to drop the handle
*/
/*
* tprintf_open: get a tprintf handle on a process "p"
* XXX change s/proc/process
*
* => returns NULL if process can't be printed to
*/
tpr_t
tprintf_open(struct proc *p)
{
struct process *pr = p->p_p;
if (pr->ps_flags & PS_CONTROLT && pr->ps_session->s_ttyvp) {
SESSHOLD(pr->ps_session);
return ((tpr_t)pr->ps_session);
}
return ((tpr_t) NULL);
}
/*
* tprintf_close: dispose of a tprintf handle obtained with tprintf_open
*/
void
tprintf_close(tpr_t sess)
{
if (sess)
SESSRELE((struct session *) sess);
}
/*
* tprintf: given tprintf handle to a process [obtained with tprintf_open],
* send a message to the controlling tty for that process.
*
* => also sends message to /dev/klog
*/
void
tprintf(tpr_t tpr, const char *fmt, ...)
{
struct session *sess = (struct session *)tpr;
struct tty *tp = NULL;
int flags = TOLOG;
va_list ap;
logpri(LOG_INFO);
if (sess && sess->s_ttyvp && ttycheckoutq(sess->s_ttyp, 0)) {
flags |= TOTTY;
tp = sess->s_ttyp;
}
va_start(ap, fmt);
kprintf(fmt, flags, tp, NULL, ap);
va_end(ap);
logwakeup();
}
#endif /* NFSSERVER || NFSCLIENT */
/*
* ttyprintf: send a message to a specific tty
*
* => should be used only by tty driver or anything that knows the
* underlying tty will not be revoked(2)'d away. [otherwise,
* use tprintf]
*/
void
ttyprintf(struct tty *tp, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
kprintf(fmt, TOTTY, tp, NULL, ap);
va_end(ap);
}
#ifdef DDB
/*
* db_printf: printf for DDB (via db_putchar)
*/
int
db_printf(const char *fmt, ...)
{
va_list ap;
int retval;
va_start(ap, fmt);
retval = db_vprintf(fmt, ap);
va_end(ap);
return(retval);
}
int
db_vprintf(const char *fmt, va_list ap)
{
int flags;
flags = TODDB;
if (db_log)
flags |= TOLOG;
return (kprintf(fmt, flags, NULL, NULL, ap));
}
#endif /* DDB */
/*
* normal kernel printf functions: printf, vprintf, snprintf
*/
/*
* printf: print a message to the console and the log
*/
int
printf(const char *fmt, ...)
{
va_list ap;
int retval;
va_start(ap, fmt);
mtx_enter(&kprintf_mutex);
retval = kprintf(fmt, printf_flags, NULL, NULL, ap);
mtx_leave(&kprintf_mutex);
va_end(ap);
if (!panicstr)
logwakeup();
return(retval);
}
/*
* vprintf: print a message to the console and the log [already have a
* va_list]
*/
int
vprintf(const char *fmt, va_list ap)
{
int retval;
mtx_enter(&kprintf_mutex);
retval = kprintf(fmt, TOCONS | TOLOG, NULL, NULL, ap);
mtx_leave(&kprintf_mutex);
if (!panicstr)
logwakeup();
return (retval);
}
/*
* snprintf: print a message to a buffer
*/
int
snprintf(char *buf, size_t size, const char *fmt, ...)
{
int retval;
va_list ap;
char *p;
p = buf;
if (size > 0)
p += size - 1;
va_start(ap, fmt);
retval = kprintf(fmt, TOBUFONLY | TOCOUNT, &p, buf, ap);
va_end(ap);
if (size > 0)
*p = '\0'; /* null terminate */
return(retval);
}
/*
* vsnprintf: print a message to a buffer [already have va_alist]
*/
int
vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
{
int retval;
char *p;
p = buf + size - 1;
if (size < 1)
p = buf;
retval = kprintf(fmt, TOBUFONLY | TOCOUNT, &p, buf, ap);
if (size > 0)
*(p) = 0; /* null terminate */
return(retval);
}
/*
* kprintf: scaled down version of printf(3).
*
* this version based on vfprintf() from libc which was derived from
* software contributed to Berkeley by Chris Torek.
*
* The additional format %b is supported to decode error registers.
* Its usage is:
*
* printf("reg=%b\n", regval, "<base><arg>*");
*
* where <base> is the output base expressed as a control character, e.g.
* \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
* the first of which gives the bit number to be inspected (origin 1), and
* the next characters (up to a control character, i.e. a character <= 32),
* give the name of the register. Thus:
*
* kprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
*
* would produce output:
*
* reg=3<BITTWO,BITONE>
*
* To support larger integers (> 32 bits), %b formatting will also accept
* control characters in the region 0x80 - 0xff. 0x80 refers to bit 0,
* 0x81 refers to bit 1, and so on. The equivalent string to the above is:
*
* kprintf("reg=%b\n", 3, "\10\201BITTWO\200BITONE\n");
*
* and would produce the same output.
*
* Like the rest of printf, %b can be prefixed to handle various size
* modifiers, eg. %b is for "int", %lb is for "long", and %llb supports
* "long long".
*
* This code is large and complicated...
*/
/*
* macros for converting digits to letters and vice versa
*/
#define to_digit(c) ((c) - '0')
#define is_digit(c) ((unsigned)to_digit(c) <= 9)
#define to_char(n) ((n) + '0')
/*
* flags used during conversion.
*/
#define ALT 0x001 /* alternate form */
#define HEXPREFIX 0x002 /* add 0x or 0X prefix */
#define LADJUST 0x004 /* left adjustment */
#define LONGDBL 0x008 /* long double; unimplemented */
#define LONGINT 0x010 /* long integer */
#define QUADINT 0x020 /* quad integer */
#define SHORTINT 0x040 /* short integer */
#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
#define FPT 0x100 /* Floating point number */
#define SIZEINT 0x200 /* (signed) size_t */
/*
* To extend shorts properly, we need both signed and unsigned
* argument extraction methods.
*/
#define SARG() \
(flags&QUADINT ? va_arg(ap, quad_t) : \
flags&LONGINT ? va_arg(ap, long) : \
flags&SIZEINT ? va_arg(ap, ssize_t) : \
flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
(long)va_arg(ap, int))
#define UARG() \
(flags&QUADINT ? va_arg(ap, u_quad_t) : \
flags&LONGINT ? va_arg(ap, u_long) : \
flags&SIZEINT ? va_arg(ap, size_t) : \
flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
(u_long)va_arg(ap, u_int))
#define KPRINTF_PUTCHAR(C) do { \
int chr = (C); \
ret += 1; \
if (oflags & TOBUFONLY) { \
if ((vp != NULL) && (sbuf == tailp)) { \
if (!(oflags & TOCOUNT)) \
goto overflow; \
} else \
*sbuf++ = chr; \
} else { \
kputchar(chr, oflags, (struct tty *)vp); \
} \
} while(0)
int
kprintf(const char *fmt0, int oflags, void *vp, char *sbuf, va_list ap)
{
char *fmt; /* format string */
int ch; /* character from fmt */
int n; /* handy integer (short term usage) */
char *cp = NULL; /* handy char pointer (short term usage) */
int flags; /* flags as above */
int ret; /* return value accumulator */
int width; /* width from format (%8d), or 0 */
int prec; /* precision from format (%.3d), or -1 */
char sign; /* sign prefix (' ', '+', '-', or \0) */
u_quad_t _uquad; /* integer arguments %[diouxX] */
enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
int realsz; /* field size expanded by dprec */
int size = 0; /* size of converted field or string */
char *xdigs = NULL; /* digits for [xX] conversion */
char buf[KPRINTF_BUFSIZE]; /* space for %c, %[diouxX] */
char *tailp = NULL; /* tail pointer for snprintf */
if (oflags & TOCONS)
MUTEX_ASSERT_LOCKED(&kprintf_mutex);
if ((oflags & TOBUFONLY) && (vp != NULL))
tailp = *(char **)vp;
fmt = (char *)fmt0;
ret = 0;
/*
* Scan the format for conversions (`%' character).
*/
for (;;) {
while (*fmt != '%' && *fmt) {
KPRINTF_PUTCHAR(*fmt++);
}
if (*fmt == 0)
goto done;
fmt++; /* skip over '%' */
flags = 0;
dprec = 0;
width = 0;
prec = -1;
sign = '\0';
rflag: ch = *fmt++;
reswitch: switch (ch) {
/* XXX: non-standard '%b' format */
case 'b': {
char *b, *z;
int tmp;
_uquad = UARG();
b = va_arg(ap, char *);
if (*b == 8)
snprintf(buf, sizeof buf, "%llo", _uquad);
else if (*b == 10)
snprintf(buf, sizeof buf, "%lld", _uquad);
else if (*b == 16)
snprintf(buf, sizeof buf, "%llx", _uquad);
else
break;
b++;
z = buf;
while (*z) {
KPRINTF_PUTCHAR(*z++);
}
if (_uquad) {
tmp = 0;
while ((n = *b++) != 0) {
if (n & 0x80)
n &= 0x7f;
else if (n <= ' ')
n = n - 1;
if (_uquad & (1LL << n)) {
KPRINTF_PUTCHAR(tmp ? ',':'<');
while (*b > ' ' &&
(*b & 0x80) == 0) {
KPRINTF_PUTCHAR(*b);
b++;
}
tmp = 1;
} else {
while (*b > ' ' &&
(*b & 0x80) == 0)
b++;
}
}
if (tmp) {
KPRINTF_PUTCHAR('>');
}
}
continue; /* no output */
}
case ' ':
/*
* ``If the space and + flags both appear, the space
* flag will be ignored.''
* -- ANSI X3J11
*/
if (!sign)
sign = ' ';
goto rflag;
case '#':
flags |= ALT;
goto rflag;
case '*':
/*
* ``A negative field width argument is taken as a
* - flag followed by a positive field width.''
* -- ANSI X3J11
* They don't exclude field widths read from args.
*/
if ((width = va_arg(ap, int)) >= 0)
goto rflag;
width = -width;
/* FALLTHROUGH */
case '-':
flags |= LADJUST;
goto rflag;
case '+':
sign = '+';
goto rflag;
case '.':
if ((ch = *fmt++) == '*') {
n = va_arg(ap, int);
prec = n < 0 ? -1 : n;
goto rflag;
}
n = 0;
while (is_digit(ch)) {
n = 10 * n + to_digit(ch);
ch = *fmt++;
}
prec = n < 0 ? -1 : n;
goto reswitch;
case '0':
/*
* ``Note that 0 is taken as a flag, not as the
* beginning of a field width.''
* -- ANSI X3J11
*/
flags |= ZEROPAD;
goto rflag;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
do {
n = 10 * n + to_digit(ch);
ch = *fmt++;
} while (is_digit(ch));
width = n;
goto reswitch;
case 'h':
flags |= SHORTINT;
goto rflag;
case 'l':
if (*fmt == 'l') {
fmt++;
flags |= QUADINT;
} else {
flags |= LONGINT;
}
goto rflag;
case 'q':
flags |= QUADINT;
goto rflag;
case 'z':
flags |= SIZEINT;
goto rflag;
case 'c':
*(cp = buf) = va_arg(ap, int);
size = 1;
sign = '\0';
break;
case 't':
/* ptrdiff_t */
/* FALLTHROUGH */
case 'D':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'd':
case 'i':
_uquad = SARG();
if ((quad_t)_uquad < 0) {
_uquad = -_uquad;
sign = '-';
}
base = DEC;
goto number;
case 'n':
panic("no %%n support");
break;
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'o':
_uquad = UARG();
base = OCT;
goto nosign;
case 'p':
/*
* ``The argument shall be a pointer to void. The
* value of the pointer is converted to a sequence
* of printable characters, in an implementation-
* defined manner.''
* -- ANSI X3J11
*/
_uquad = (u_long)va_arg(ap, void *);
base = HEX;
xdigs = "0123456789abcdef";
flags |= HEXPREFIX;
ch = 'x';
goto nosign;
case 's':
if ((cp = va_arg(ap, char *)) == NULL)
cp = "(null)";
if (prec >= 0) {
/*
* can't use strlen; can only look for the
* NUL in the first `prec' characters, and
* strlen() will go further.
*/
char *p = memchr(cp, 0, prec);
if (p != NULL) {
size = p - cp;
if (size > prec)
size = prec;
} else
size = prec;
} else
size = strlen(cp);
sign = '\0';
break;
case 'U':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'u':
_uquad = UARG();
base = DEC;
goto nosign;
case 'X':
xdigs = "0123456789ABCDEF";
goto hex;
case 'x':
xdigs = "0123456789abcdef";
hex: _uquad = UARG();
base = HEX;
/* leading 0x/X only if non-zero */
if (flags & ALT && _uquad != 0)
flags |= HEXPREFIX;
/* unsigned conversions */
nosign: sign = '\0';
/*
* ``... diouXx conversions ... if a precision is
* specified, the 0 flag will be ignored.''
* -- ANSI X3J11
*/
number: if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
/*
* ``The result of converting a zero value with an
* explicit precision of zero is no characters.''
* -- ANSI X3J11
*/
cp = buf + KPRINTF_BUFSIZE;
if (_uquad != 0 || prec != 0) {
/*
* Unsigned mod is hard, and unsigned mod
* by a constant is easier than that by
* a variable; hence this switch.
*/
switch (base) {
case OCT:
do {
*--cp = to_char(_uquad & 7);
_uquad >>= 3;
} while (_uquad);
/* handle octal leading 0 */
if (flags & ALT && *cp != '0')
*--cp = '0';
break;
case DEC:
/* many numbers are 1 digit */
while (_uquad >= 10) {
*--cp = to_char(_uquad % 10);
_uquad /= 10;
}
*--cp = to_char(_uquad);
break;
case HEX:
do {
*--cp = xdigs[_uquad & 15];
_uquad >>= 4;
} while (_uquad);
break;
default:
cp = "bug in kprintf: bad base";
size = strlen(cp);
goto skipsize;
}
}
size = buf + KPRINTF_BUFSIZE - cp;
skipsize:
break;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0')
goto done;
/* pretend it was %c with argument ch */
cp = buf;
*cp = ch;
size = 1;
sign = '\0';
break;
}
/*
* All reasonable formats wind up here. At this point, `cp'
* points to a string which (if not flags&LADJUST) should be
* padded out to `width' places. If flags&ZEROPAD, it should
* first be prefixed by any sign or other prefix; otherwise,
* it should be blank padded before the prefix is emitted.
* After any left-hand padding and prefixing, emit zeroes
* required by a decimal [diouxX] precision, then print the
* string proper, then emit zeroes required by any leftover
* floating precision; finally, if LADJUST, pad with blanks.
*
* Compute actual size, so we know how much to pad.
* size excludes decimal prec; realsz includes it.
*/
realsz = dprec > size ? dprec : size;
if (sign)
realsz++;
else if (flags & HEXPREFIX)
realsz+= 2;
/* right-adjusting blank padding */
if ((flags & (LADJUST|ZEROPAD)) == 0) {
n = width - realsz;
while (n-- > 0)
KPRINTF_PUTCHAR(' ');
}
/* prefix */
if (sign) {
KPRINTF_PUTCHAR(sign);
} else if (flags & HEXPREFIX) {
KPRINTF_PUTCHAR('0');
KPRINTF_PUTCHAR(ch);
}
/* right-adjusting zero padding */
if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) {
n = width - realsz;
while (n-- > 0)
KPRINTF_PUTCHAR('0');
}
/* leading zeroes from decimal precision */
n = dprec - size;
while (n-- > 0)
KPRINTF_PUTCHAR('0');
/* the string or number proper */
while (size--)
KPRINTF_PUTCHAR(*cp++);
/* left-adjusting padding (always blank) */
if (flags & LADJUST) {
n = width - realsz;
while (n-- > 0)
KPRINTF_PUTCHAR(' ');
}
}
done:
if ((oflags & TOBUFONLY) && (vp != NULL))
*(char **)vp = sbuf;
overflow:
return (ret);
/* NOTREACHED */
}
#if __GNUC_PREREQ__(2,96)
/*
* XXX - these functions shouldn't be in the kernel, but gcc 3.X feels like
* translating some printf calls to puts and since it doesn't seem
* possible to just turn off parts of those optimizations (some of
* them are really useful), we have to provide a dummy puts and putchar
* that are wrappers around printf.
*/
int puts(const char *);
int putchar(int c);
int
puts(const char *str)
{
printf("%s\n", str);
return (0);
}
int
putchar(int c)
{
printf("%c", c);
return (c);
}
#endif
54
48
81
57
69
70
61
592
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/* $OpenBSD: kern_srp.c,v 1.13 2020/12/06 19:18:30 cheloha Exp $ */
/*
* Copyright (c) 2014 Jonathan Matthew <jmatthew@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/srp.h>
#include <sys/atomic.h>
void srp_v_gc_start(struct srp_gc *, struct srp *, void *);
void
srpl_rc_init(struct srpl_rc *rc, void (*ref)(void *, void *),
void (*unref)(void *, void *), void *cookie)
{
rc->srpl_ref = ref;
srp_gc_init(&rc->srpl_gc, unref, cookie);
}
void
srp_gc_init(struct srp_gc *srp_gc, void (*dtor)(void *, void *), void *cookie)
{
srp_gc->srp_gc_dtor = dtor;
srp_gc->srp_gc_cookie = cookie;
refcnt_init(&srp_gc->srp_gc_refcnt);
}
void
srp_init(struct srp *srp)
{
srp->ref = NULL;
}
void *
srp_swap_locked(struct srp *srp, void *nv)
{
void *ov;
/*
* this doesn't have to be as careful as the caller has already
* prevented concurrent updates, eg. by holding the kernel lock.
* can't be mixed with non-locked updates though.
*/
ov = srp->ref;
srp->ref = nv;
return (ov);
}
void
srp_update_locked(struct srp_gc *srp_gc, struct srp *srp, void *v)
{
if (v != NULL)
refcnt_take(&srp_gc->srp_gc_refcnt);
v = srp_swap_locked(srp, v);
if (v != NULL)
srp_v_gc_start(srp_gc, srp, v);
}
void *
srp_get_locked(struct srp *srp)
{
return (srp->ref);
}
void
srp_gc_finalize(struct srp_gc *srp_gc)
{
refcnt_finalize(&srp_gc->srp_gc_refcnt, "srpfini");
}
#ifdef MULTIPROCESSOR
#include <machine/cpu.h>
#include <sys/pool.h>
struct srp_gc_ctx {
struct srp_gc *srp_gc;
struct timeout tick;
struct srp_hazard hzrd;
};
int srp_v_referenced(struct srp *, void *);
void srp_v_gc(void *);
struct pool srp_gc_ctx_pool;
void
srp_startup(void)
{
pool_init(&srp_gc_ctx_pool, sizeof(struct srp_gc_ctx), 0,
IPL_SOFTCLOCK, PR_WAITOK, "srpgc", NULL);
}
int
srp_v_referenced(struct srp *srp, void *v)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
u_int i;
struct srp_hazard *hzrd;
CPU_INFO_FOREACH(cii, ci) {
for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
hzrd = &ci->ci_srp_hazards[i];
if (hzrd->sh_p != srp)
continue;
membar_consumer();
if (hzrd->sh_v != v)
continue;
return (1);
}
}
return (0);
}
void
srp_v_dtor(struct srp_gc *srp_gc, void *v)
{
(*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
}
void
srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
{
struct srp_gc_ctx *ctx;
if (!srp_v_referenced(srp, v)) {
/* we win */
srp_v_dtor(srp_gc, v);
return;
}
/* in use, try later */
ctx = pool_get(&srp_gc_ctx_pool, PR_WAITOK);
ctx->srp_gc = srp_gc;
ctx->hzrd.sh_p = srp;
ctx->hzrd.sh_v = v;
timeout_set(&ctx->tick, srp_v_gc, ctx);
timeout_add(&ctx->tick, 1);
}
void
srp_v_gc(void *x)
{
struct srp_gc_ctx *ctx = x;
if (srp_v_referenced(ctx->hzrd.sh_p, ctx->hzrd.sh_v)) {
/* oh well, try again later */
timeout_add(&ctx->tick, 1);
return;
}
srp_v_dtor(ctx->srp_gc, ctx->hzrd.sh_v);
pool_put(&srp_gc_ctx_pool, ctx);
}
void *
srp_swap(struct srp *srp, void *v)
{
return (atomic_swap_ptr(&srp->ref, v));
}
void
srp_update(struct srp_gc *srp_gc, struct srp *srp, void *v)
{
if (v != NULL)
refcnt_take(&srp_gc->srp_gc_refcnt);
v = srp_swap(srp, v);
if (v != NULL)
srp_v_gc_start(srp_gc, srp, v);
}
static inline void *
srp_v(struct srp_hazard *hzrd, struct srp *srp)
{
void *v;
hzrd->sh_p = srp;
/*
* ensure we update this cpu's hazard pointer to a value that's still
* current after the store finishes, otherwise the gc task may already
* be destroying it
*/
do {
v = srp->ref;
hzrd->sh_v = v;
membar_consumer();
} while (__predict_false(v != srp->ref));
return (v);
}
void *
srp_enter(struct srp_ref *sr, struct srp *srp)
{
struct cpu_info *ci = curcpu();
struct srp_hazard *hzrd;
u_int i;
for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
hzrd = &ci->ci_srp_hazards[i];
if (hzrd->sh_p == NULL) {
sr->hz = hzrd;
return (srp_v(hzrd, srp));
}
}
panic("%s: not enough srp hazard records", __func__);
/* NOTREACHED */
return (NULL);
}
void *
srp_follow(struct srp_ref *sr, struct srp *srp)
{
return (srp_v(sr->hz, srp));
}
void
srp_leave(struct srp_ref *sr)
{
sr->hz->sh_p = NULL;
}
static inline int
srp_referenced(void *v)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
u_int i;
struct srp_hazard *hzrd;
CPU_INFO_FOREACH(cii, ci) {
for (i = 0; i < nitems(ci->ci_srp_hazards); i++) {
hzrd = &ci->ci_srp_hazards[i];
if (hzrd->sh_p != NULL && hzrd->sh_v == v)
return (1);
}
}
return (0);
}
void
srp_finalize(void *v, const char *wmesg)
{
while (srp_referenced(v))
tsleep_nsec(v, PWAIT, wmesg, MSEC_TO_NSEC(1));
}
#else /* MULTIPROCESSOR */
void
srp_startup(void)
{
}
void
srp_v_gc_start(struct srp_gc *srp_gc, struct srp *srp, void *v)
{
(*srp_gc->srp_gc_dtor)(srp_gc->srp_gc_cookie, v);
refcnt_rele_wake(&srp_gc->srp_gc_refcnt);
}
#endif /* MULTIPROCESSOR */
18
2
1
2
13
9
4
2
1
2
1
2
1
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* $OpenBSD: subr_evcount.c,v 1.13 2022/08/14 01:58:28 jsg Exp $ */
/*
* Copyright (c) 2004 Artur Grabowski <art@openbsd.org>
* Copyright (c) 2004 Aaron Campbell <aaron@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/evcount.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
static TAILQ_HEAD(,evcount) evcount_list = TAILQ_HEAD_INITIALIZER(evcount_list);
void
evcount_attach(struct evcount *ec, const char *name, void *data)
{
static int nextid = 0;
memset(ec, 0, sizeof(*ec));
ec->ec_name = name;
ec->ec_id = ++nextid;
ec->ec_data = data;
TAILQ_INSERT_TAIL(&evcount_list, ec, next);
}
void
evcount_detach(struct evcount *ec)
{
TAILQ_REMOVE(&evcount_list, ec, next);
}
#ifndef SMALL_KERNEL
int
evcount_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error = 0, s, nintr, i;
struct evcount *ec;
u_int64_t count;
if (newp != NULL)
return (EPERM);
if (name[0] != KERN_INTRCNT_NUM) {
if (namelen != 2)
return (ENOTDIR);
if (name[1] < 0)
return (EINVAL);
i = name[1];
} else
i = -1;
nintr = 0;
TAILQ_FOREACH(ec, &evcount_list, next) {
if (nintr++ == i)
break;
}
switch (name[0]) {
case KERN_INTRCNT_NUM:
error = sysctl_rdint(oldp, oldlenp, NULL, nintr);
break;
case KERN_INTRCNT_CNT:
if (ec == NULL)
return (ENOENT);
s = splhigh();
count = ec->ec_count;
splx(s);
error = sysctl_rdquad(oldp, oldlenp, NULL, count);
break;
case KERN_INTRCNT_NAME:
if (ec == NULL)
return (ENOENT);
error = sysctl_rdstring(oldp, oldlenp, NULL, ec->ec_name);
break;
case KERN_INTRCNT_VECTOR:
if (ec == NULL || ec->ec_data == NULL)
return (ENOENT);
error = sysctl_rdint(oldp, oldlenp, NULL,
*((int *)ec->ec_data));
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
#endif /* SMALL_KERNEL */
8
18
5
1
4
4
1
1
3
1
1
1
2
2
13
1
10
1
1
4
1
2
1
1
2
3
1
4
4
4
2
10
1
9
6
28
9
1
2
8
2
2
2
2
19
1
1
3
2
1
2
1
1
2
2
7
7
11
6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
/* $OpenBSD: if_pppx.c,v 1.122 2022/08/29 07:51:45 bluhm Exp $ */
/*
* Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2010 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*-
* Copyright (c) 2009 Internet Initiative Japan Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/queue.h>
#include <sys/pool.h>
#include <sys/mbuf.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/vnode.h>
#include <sys/selinfo.h>
#include <net/if.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_dl.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/nd6.h>
#endif /* INET6 */
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include "pf.h"
#if NPF > 0
#include <net/pfvar.h>
#endif
#include <net/ppp_defs.h>
#include <net/ppp-comp.h>
#include <crypto/arc4.h>
#ifdef PIPEX
#include <net/radix.h>
#include <net/pipex.h>
#include <net/pipex_local.h>
#else
#error PIPEX option not enabled
#endif
#ifdef PPPX_DEBUG
#define PPPX_D_INIT (1<<0)
int pppxdebug = 0;
#define DPRINTF(_m, _p...) do { \
if (ISSET(pppxdebug, (_m))) \
printf(_p); \
} while (0)
#else
#define DPRINTF(_m, _p...) /* _m, _p */
#endif
struct pppx_if;
/*
* Locks used to protect struct members and global data
* I immutable after creation
* K kernel lock
* N net lock
*/
struct pppx_dev {
LIST_ENTRY(pppx_dev) pxd_entry; /* [K] */
int pxd_unit; /* [I] */
/* kq shizz */
struct selinfo pxd_rsel;
struct mutex pxd_rsel_mtx;
struct selinfo pxd_wsel;
struct mutex pxd_wsel_mtx;
/* queue of packets for userland to service - protected by splnet */
struct mbuf_queue pxd_svcq;
int pxd_waiting; /* [N] */
LIST_HEAD(,pppx_if) pxd_pxis; /* [N] */
};
LIST_HEAD(, pppx_dev) pppx_devs =
LIST_HEAD_INITIALIZER(pppx_devs); /* [K] */
struct pool pppx_if_pl;
struct pppx_dev *pppx_dev_lookup(dev_t);
struct pppx_dev *pppx_dev2pxd(dev_t);
struct pppx_if_key {
int pxik_session_id; /* [I] */
int pxik_protocol; /* [I] */
};
struct pppx_if {
struct pppx_if_key pxi_key; /* [I] must be first
in the struct */
RBT_ENTRY(pppx_if) pxi_entry; /* [N] */
LIST_ENTRY(pppx_if) pxi_list; /* [N] */
int pxi_ready; /* [N] */
int pxi_unit; /* [I] */
struct ifnet pxi_if;
struct pppx_dev *pxi_dev; /* [I] */
struct pipex_session *pxi_session; /* [I] */
};
static inline int
pppx_if_cmp(const struct pppx_if *a, const struct pppx_if *b)
{
return memcmp(&a->pxi_key, &b->pxi_key, sizeof(a->pxi_key));
}
RBT_HEAD(pppx_ifs, pppx_if) pppx_ifs = RBT_INITIALIZER(&pppx_ifs); /* [N] */
RBT_PROTOTYPE(pppx_ifs, pppx_if, pxi_entry, pppx_if_cmp);
int pppx_if_next_unit(void);
struct pppx_if *pppx_if_find(struct pppx_dev *, int, int);
int pppx_add_session(struct pppx_dev *,
struct pipex_session_req *);
int pppx_del_session(struct pppx_dev *,
struct pipex_session_close_req *);
int pppx_set_session_descr(struct pppx_dev *,
struct pipex_session_descr_req *);
void pppx_if_destroy(struct pppx_dev *, struct pppx_if *);
void pppx_if_qstart(struct ifqueue *);
int pppx_if_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *);
int pppx_if_ioctl(struct ifnet *, u_long, caddr_t);
void pppxattach(int);
void filt_pppx_rdetach(struct knote *);
int filt_pppx_read(struct knote *, long);
const struct filterops pppx_rd_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_pppx_rdetach,
.f_event = filt_pppx_read,
};
void filt_pppx_wdetach(struct knote *);
int filt_pppx_write(struct knote *, long);
const struct filterops pppx_wr_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_pppx_wdetach,
.f_event = filt_pppx_write,
};
struct pppx_dev *
pppx_dev_lookup(dev_t dev)
{
struct pppx_dev *pxd;
int unit = minor(dev);
LIST_FOREACH(pxd, &pppx_devs, pxd_entry) {
if (pxd->pxd_unit == unit)
return (pxd);
}
return (NULL);
}
struct pppx_dev *
pppx_dev2pxd(dev_t dev)
{
struct pppx_dev *pxd;
pxd = pppx_dev_lookup(dev);
return (pxd);
}
void
pppxattach(int n)
{
pool_init(&pppx_if_pl, sizeof(struct pppx_if), 0, IPL_NONE,
PR_WAITOK, "pppxif", NULL);
pipex_init();
}
int
pppxopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct pppx_dev *pxd;
pxd = malloc(sizeof(*pxd), M_DEVBUF, M_WAITOK | M_ZERO);
if (pppx_dev_lookup(dev) != NULL) {
free(pxd, M_DEVBUF, sizeof(*pxd));
return (EBUSY);
}
pxd->pxd_unit = minor(dev);
mtx_init(&pxd->pxd_rsel_mtx, IPL_NET);
mtx_init(&pxd->pxd_wsel_mtx, IPL_NET);
LIST_INIT(&pxd->pxd_pxis);
mq_init(&pxd->pxd_svcq, 128, IPL_NET);
LIST_INSERT_HEAD(&pppx_devs, pxd, pxd_entry);
return 0;
}
int
pppxread(dev_t dev, struct uio *uio, int ioflag)
{
struct pppx_dev *pxd = pppx_dev2pxd(dev);
struct mbuf *m, *m0;
int error = 0;
size_t len;
if (!pxd)
return (ENXIO);
while ((m0 = mq_dequeue(&pxd->pxd_svcq)) == NULL) {
if (ISSET(ioflag, IO_NDELAY))
return (EWOULDBLOCK);
NET_LOCK();
pxd->pxd_waiting = 1;
error = rwsleep_nsec(pxd, &netlock,
(PZERO + 1)|PCATCH, "pppxread", INFSLP);
NET_UNLOCK();
if (error != 0) {
return (error);
}
}
while (m0 != NULL && uio->uio_resid > 0 && error == 0) {
len = ulmin(uio->uio_resid, m0->m_len);
if (len != 0)
error = uiomove(mtod(m0, caddr_t), len, uio);
m = m_free(m0);
m0 = m;
}
m_freem(m0);
return (error);
}
int
pppxwrite(dev_t dev, struct uio *uio, int ioflag)
{
struct pppx_dev *pxd = pppx_dev2pxd(dev);
struct pppx_hdr *th;
struct pppx_if *pxi;
uint32_t proto;
struct mbuf *top, **mp, *m;
int tlen;
int error = 0;
size_t mlen;
if (uio->uio_resid < sizeof(*th) + sizeof(uint32_t) ||
uio->uio_resid > MCLBYTES)
return (EMSGSIZE);
tlen = uio->uio_resid;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return (ENOBUFS);
mlen = MHLEN;
if (uio->uio_resid > MHLEN) {
MCLGET(m, M_DONTWAIT);
if (!(m->m_flags & M_EXT)) {
m_free(m);
return (ENOBUFS);
}
mlen = MCLBYTES;
}
top = NULL;
mp = ⊤
while (error == 0 && uio->uio_resid > 0) {
m->m_len = ulmin(mlen, uio->uio_resid);
error = uiomove(mtod (m, caddr_t), m->m_len, uio);
*mp = m;
mp = &m->m_next;
if (error == 0 && uio->uio_resid > 0) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
error = ENOBUFS;
break;
}
mlen = MLEN;
if (uio->uio_resid >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (!(m->m_flags & M_EXT)) {
error = ENOBUFS;
m_free(m);
break;
}
mlen = MCLBYTES;
}
}
}
if (error) {
m_freem(top);
return (error);
}
top->m_pkthdr.len = tlen;
/* Find the interface */
th = mtod(top, struct pppx_hdr *);
m_adj(top, sizeof(struct pppx_hdr));
NET_LOCK();
pxi = pppx_if_find(pxd, th->pppx_id, th->pppx_proto);
if (pxi == NULL) {
NET_UNLOCK();
m_freem(top);
return (EINVAL);
}
top->m_pkthdr.ph_ifidx = pxi->pxi_if.if_index;
#if NBPFILTER > 0
if (pxi->pxi_if.if_bpf)
bpf_mtap(pxi->pxi_if.if_bpf, top, BPF_DIRECTION_IN);
#endif
/* strip the tunnel header */
proto = ntohl(*(uint32_t *)(th + 1));
m_adj(top, sizeof(uint32_t));
switch (proto) {
case AF_INET:
ipv4_input(&pxi->pxi_if, top);
break;
#ifdef INET6
case AF_INET6:
ipv6_input(&pxi->pxi_if, top);
break;
#endif
default:
m_freem(top);
error = EAFNOSUPPORT;
break;
}
NET_UNLOCK();
return (error);
}
int
pppxioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
{
struct pppx_dev *pxd = pppx_dev2pxd(dev);
int error = 0;
NET_LOCK();
switch (cmd) {
case PIPEXASESSION:
error = pppx_add_session(pxd,
(struct pipex_session_req *)addr);
break;
case PIPEXDSESSION:
error = pppx_del_session(pxd,
(struct pipex_session_close_req *)addr);
break;
case PIPEXSIFDESCR:
error = pppx_set_session_descr(pxd,
(struct pipex_session_descr_req *)addr);
break;
case FIONBIO:
break;
case FIONREAD:
*(int *)addr = mq_hdatalen(&pxd->pxd_svcq);
break;
default:
error = pipex_ioctl(pxd, cmd, addr);
break;
}
NET_UNLOCK();
return (error);
}
int
pppxkqfilter(dev_t dev, struct knote *kn)
{
struct pppx_dev *pxd = pppx_dev2pxd(dev);
struct mutex *mtx;
struct klist *klist;
switch (kn->kn_filter) {
case EVFILT_READ:
mtx = &pxd->pxd_rsel_mtx;
klist = &pxd->pxd_rsel.si_note;
kn->kn_fop = &pppx_rd_filtops;
break;
case EVFILT_WRITE:
mtx = &pxd->pxd_wsel_mtx;
klist = &pxd->pxd_wsel.si_note;
kn->kn_fop = &pppx_wr_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = (caddr_t)pxd;
mtx_enter(mtx);
klist_insert_locked(klist, kn);
mtx_leave(mtx);
return (0);
}
void
filt_pppx_rdetach(struct knote *kn)
{
struct pppx_dev *pxd = (struct pppx_dev *)kn->kn_hook;
struct klist *klist = &pxd->pxd_rsel.si_note;
mtx_enter(&pxd->pxd_rsel_mtx);
klist_remove_locked(klist, kn);
mtx_leave(&pxd->pxd_rsel_mtx);
}
int
filt_pppx_read(struct knote *kn, long hint)
{
struct pppx_dev *pxd = (struct pppx_dev *)kn->kn_hook;
kn->kn_data = mq_hdatalen(&pxd->pxd_svcq);
return (kn->kn_data > 0);
}
void
filt_pppx_wdetach(struct knote *kn)
{
struct pppx_dev *pxd = (struct pppx_dev *)kn->kn_hook;
struct klist *klist = &pxd->pxd_wsel.si_note;
mtx_enter(&pxd->pxd_wsel_mtx);
klist_remove_locked(klist, kn);
mtx_leave(&pxd->pxd_wsel_mtx);
}
int
filt_pppx_write(struct knote *kn, long hint)
{
/* We're always ready to accept a write. */
return (1);
}
int
pppxclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct pppx_dev *pxd;
struct pppx_if *pxi;
pxd = pppx_dev_lookup(dev);
/* XXX */
NET_LOCK();
while ((pxi = LIST_FIRST(&pxd->pxd_pxis)))
pppx_if_destroy(pxd, pxi);
NET_UNLOCK();
LIST_REMOVE(pxd, pxd_entry);
mq_purge(&pxd->pxd_svcq);
free(pxd, M_DEVBUF, sizeof(*pxd));
return (0);
}
int
pppx_if_next_unit(void)
{
struct pppx_if *pxi;
int unit = 0;
/* this is safe without splnet since we're not modifying it */
do {
int found = 0;
RBT_FOREACH(pxi, pppx_ifs, &pppx_ifs) {
if (pxi->pxi_unit == unit) {
found = 1;
break;
}
}
if (found == 0)
break;
unit++;
} while (unit > 0);
return (unit);
}
struct pppx_if *
pppx_if_find(struct pppx_dev *pxd, int session_id, int protocol)
{
struct pppx_if_key key;
struct pppx_if *pxi;
memset(&key, 0, sizeof(key));
key.pxik_session_id = session_id;
key.pxik_protocol = protocol;
pxi = RBT_FIND(pppx_ifs, &pppx_ifs, (struct pppx_if *)&key);
if (pxi && pxi->pxi_ready == 0)
pxi = NULL;
return pxi;
}
int
pppx_add_session(struct pppx_dev *pxd, struct pipex_session_req *req)
{
struct pppx_if *pxi;
struct pipex_session *session;
struct ifnet *ifp;
int unit, error = 0;
struct in_ifaddr *ia;
struct sockaddr_in ifaddr;
/*
* XXX: As long as `session' is allocated as part of a `pxi'
* it isn't possible to free it separately. So disallow
* the timeout feature until this is fixed.
*/
if (req->pr_timeout_sec != 0)
return (EINVAL);
error = pipex_init_session(&session, req);
if (error)
return (error);
pxi = pool_get(&pppx_if_pl, PR_WAITOK | PR_ZERO);
ifp = &pxi->pxi_if;
pxi->pxi_session = session;
/* try to set the interface up */
unit = pppx_if_next_unit();
if (unit < 0) {
error = ENOMEM;
goto out;
}
pxi->pxi_unit = unit;
pxi->pxi_key.pxik_session_id = req->pr_session_id;
pxi->pxi_key.pxik_protocol = req->pr_protocol;
pxi->pxi_dev = pxd;
if (RBT_INSERT(pppx_ifs, &pppx_ifs, pxi) != NULL) {
error = EADDRINUSE;
goto out;
}
LIST_INSERT_HEAD(&pxd->pxd_pxis, pxi, pxi_list);
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", "pppx", unit);
ifp->if_mtu = req->pr_peer_mru; /* XXX */
ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST | IFF_UP;
ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE;
ifp->if_qstart = pppx_if_qstart;
ifp->if_output = pppx_if_output;
ifp->if_ioctl = pppx_if_ioctl;
ifp->if_rtrequest = p2p_rtrequest;
ifp->if_type = IFT_PPP;
ifp->if_softc = pxi;
/* ifp->if_rdomain = req->pr_rdomain; */
if_counters_alloc(ifp);
/* XXXSMP breaks atomicity */
NET_UNLOCK();
if_attach(ifp);
NET_LOCK();
if_addgroup(ifp, "pppx");
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
#endif
/* XXX ipv6 support? how does the caller indicate it wants ipv6
* instead of ipv4?
*/
memset(&ifaddr, 0, sizeof(ifaddr));
ifaddr.sin_family = AF_INET;
ifaddr.sin_len = sizeof(ifaddr);
ifaddr.sin_addr = req->pr_ip_srcaddr;
ia = malloc(sizeof (*ia), M_IFADDR, M_WAITOK | M_ZERO);
refcnt_init_trace(&ia->ia_ifa.ifa_refcnt, DT_REFCNT_IDX_IFADDR);
ia->ia_addr.sin_family = AF_INET;
ia->ia_addr.sin_len = sizeof(struct sockaddr_in);
ia->ia_addr.sin_addr = req->pr_ip_srcaddr;
ia->ia_dstaddr.sin_family = AF_INET;
ia->ia_dstaddr.sin_len = sizeof(struct sockaddr_in);
ia->ia_dstaddr.sin_addr = req->pr_ip_address;
ia->ia_sockmask.sin_family = AF_INET;
ia->ia_sockmask.sin_len = sizeof(struct sockaddr_in);
ia->ia_sockmask.sin_addr = req->pr_ip_netmask;
ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
ia->ia_ifa.ifa_ifp = ifp;
ia->ia_netmask = ia->ia_sockmask.sin_addr.s_addr;
error = in_ifinit(ifp, ia, &ifaddr, 1);
if (error) {
printf("pppx: unable to set addresses for %s, error=%d\n",
ifp->if_xname, error);
} else {
if_addrhooks_run(ifp);
}
error = pipex_link_session(session, ifp, pxd);
if (error)
goto detach;
SET(ifp->if_flags, IFF_RUNNING);
pxi->pxi_ready = 1;
return (error);
detach:
/* XXXSMP breaks atomicity */
NET_UNLOCK();
if_detach(ifp);
NET_LOCK();
if (RBT_REMOVE(pppx_ifs, &pppx_ifs, pxi) == NULL)
panic("%s: inconsistent RB tree", __func__);
LIST_REMOVE(pxi, pxi_list);
out:
pool_put(&pppx_if_pl, pxi);
pipex_rele_session(session);
return (error);
}
int
pppx_del_session(struct pppx_dev *pxd, struct pipex_session_close_req *req)
{
struct pppx_if *pxi;
pxi = pppx_if_find(pxd, req->pcr_session_id, req->pcr_protocol);
if (pxi == NULL)
return (EINVAL);
pipex_export_session_stats(pxi->pxi_session, &req->pcr_stat);
pppx_if_destroy(pxd, pxi);
return (0);
}
int
pppx_set_session_descr(struct pppx_dev *pxd,
struct pipex_session_descr_req *req)
{
struct pppx_if *pxi;
pxi = pppx_if_find(pxd, req->pdr_session_id, req->pdr_protocol);
if (pxi == NULL)
return (EINVAL);
(void)memset(pxi->pxi_if.if_description, 0, IFDESCRSIZE);
strlcpy(pxi->pxi_if.if_description, req->pdr_descr, IFDESCRSIZE);
return (0);
}
void
pppx_if_destroy(struct pppx_dev *pxd, struct pppx_if *pxi)
{
struct ifnet *ifp;
struct pipex_session *session;
NET_ASSERT_LOCKED();
session = pxi->pxi_session;
ifp = &pxi->pxi_if;
pxi->pxi_ready = 0;
CLR(ifp->if_flags, IFF_RUNNING);
pipex_unlink_session(session);
/* XXXSMP breaks atomicity */
NET_UNLOCK();
if_detach(ifp);
NET_LOCK();
pipex_rele_session(session);
if (RBT_REMOVE(pppx_ifs, &pppx_ifs, pxi) == NULL)
panic("%s: inconsistent RB tree", __func__);
LIST_REMOVE(pxi, pxi_list);
pool_put(&pppx_if_pl, pxi);
}
void
pppx_if_qstart(struct ifqueue *ifq)
{
struct ifnet *ifp = ifq->ifq_if;
struct pppx_if *pxi = (struct pppx_if *)ifp->if_softc;
struct mbuf *m;
int proto;
while ((m = ifq_dequeue(ifq)) != NULL) {
proto = *mtod(m, int *);
m_adj(m, sizeof(proto));
pipex_ppp_output(m, pxi->pxi_session, proto);
}
}
int
pppx_if_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
struct pppx_if *pxi = (struct pppx_if *)ifp->if_softc;
struct pppx_hdr *th;
int error = 0;
int pipex_enable_local, proto;
pipex_enable_local = atomic_load_int(&pipex_enable);
NET_ASSERT_LOCKED();
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
m_freem(m);
error = ENETDOWN;
goto out;
}
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap_af(ifp->if_bpf, dst->sa_family, m, BPF_DIRECTION_OUT);
#endif
if (pipex_enable_local) {
switch (dst->sa_family) {
#ifdef INET6
case AF_INET6:
proto = PPP_IPV6;
break;
#endif
case AF_INET:
proto = PPP_IP;
break;
default:
m_freem(m);
error = EPFNOSUPPORT;
goto out;
}
} else
proto = htonl(dst->sa_family);
M_PREPEND(m, sizeof(int), M_DONTWAIT);
if (m == NULL) {
error = ENOBUFS;
goto out;
}
*mtod(m, int *) = proto;
if (pipex_enable_local)
error = if_enqueue(ifp, m);
else {
M_PREPEND(m, sizeof(struct pppx_hdr), M_DONTWAIT);
if (m == NULL) {
error = ENOBUFS;
goto out;
}
th = mtod(m, struct pppx_hdr *);
th->pppx_proto = 0; /* not used */
th->pppx_id = pxi->pxi_session->ppp_id;
error = mq_enqueue(&pxi->pxi_dev->pxd_svcq, m);
if (error == 0) {
if (pxi->pxi_dev->pxd_waiting) {
wakeup((caddr_t)pxi->pxi_dev);
pxi->pxi_dev->pxd_waiting = 0;
}
selwakeup(&pxi->pxi_dev->pxd_rsel);
}
}
out:
if (error)
counters_inc(ifp->if_counters, ifc_oerrors);
return (error);
}
int
pppx_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
struct pppx_if *pxi = (struct pppx_if *)ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)addr;
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
break;
case SIOCSIFFLAGS:
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu < 512 ||
ifr->ifr_mtu > pxi->pxi_session->peer_mru)
error = EINVAL;
else
ifp->if_mtu = ifr->ifr_mtu;
break;
default:
error = ENOTTY;
break;
}
return (error);
}
RBT_GENERATE(pppx_ifs, pppx_if, pxi_entry, pppx_if_cmp);
struct pppac_softc {
struct ifnet sc_if;
dev_t sc_dev; /* [I] */
int sc_ready; /* [K] */
LIST_ENTRY(pppac_softc)
sc_entry; /* [K] */
struct mutex sc_rsel_mtx;
struct selinfo sc_rsel;
struct mutex sc_wsel_mtx;
struct selinfo sc_wsel;
struct pipex_session
*sc_multicast_session;
struct mbuf_queue
sc_mq;
};
LIST_HEAD(pppac_list, pppac_softc); /* [K] */
static void filt_pppac_rdetach(struct knote *);
static int filt_pppac_read(struct knote *, long);
static const struct filterops pppac_rd_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_pppac_rdetach,
.f_event = filt_pppac_read
};
static void filt_pppac_wdetach(struct knote *);
static int filt_pppac_write(struct knote *, long);
static const struct filterops pppac_wr_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_pppac_wdetach,
.f_event = filt_pppac_write
};
static struct pppac_list pppac_devs = LIST_HEAD_INITIALIZER(pppac_devs);
static int pppac_ioctl(struct ifnet *, u_long, caddr_t);
static int pppac_add_session(struct pppac_softc *,
struct pipex_session_req *);
static int pppac_del_session(struct pppac_softc *,
struct pipex_session_close_req *);
static int pppac_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
static void pppac_qstart(struct ifqueue *);
static inline struct pppac_softc *
pppac_lookup(dev_t dev)
{
struct pppac_softc *sc;
LIST_FOREACH(sc, &pppac_devs, sc_entry) {
if (sc->sc_dev == dev) {
if (sc->sc_ready == 0)
break;
return (sc);
}
}
return (NULL);
}
void
pppacattach(int n)
{
pipex_init(); /* to be sure, to be sure */
}
int
pppacopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct pppac_softc *sc, *tmp;
struct ifnet *ifp;
struct pipex_session *session;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
sc->sc_dev = dev;
LIST_FOREACH(tmp, &pppac_devs, sc_entry) {
if (tmp->sc_dev == dev) {
free(sc, M_DEVBUF, sizeof(*sc));
return (EBUSY);
}
}
LIST_INSERT_HEAD(&pppac_devs, sc, sc_entry);
/* virtual pipex_session entry for multicast */
session = pool_get(&pipex_session_pool, PR_WAITOK | PR_ZERO);
session->flags |= PIPEX_SFLAGS_MULTICAST;
session->ownersc = sc;
sc->sc_multicast_session = session;
mtx_init(&sc->sc_rsel_mtx, IPL_SOFTNET);
mtx_init(&sc->sc_wsel_mtx, IPL_SOFTNET);
mq_init(&sc->sc_mq, IFQ_MAXLEN, IPL_SOFTNET);
ifp = &sc->sc_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "pppac%u", minor(dev));
ifp->if_softc = sc;
ifp->if_type = IFT_L3IPVLAN;
ifp->if_hdrlen = sizeof(uint32_t); /* for BPF */;
ifp->if_mtu = MAXMCLBYTES - sizeof(uint32_t);
ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST;
ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE;
ifp->if_rtrequest = p2p_rtrequest; /* XXX */
ifp->if_output = pppac_output;
ifp->if_qstart = pppac_qstart;
ifp->if_ioctl = pppac_ioctl;
if_counters_alloc(ifp);
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
#endif
sc->sc_ready = 1;
return (0);
}
int
pppacread(dev_t dev, struct uio *uio, int ioflag)
{
struct pppac_softc *sc = pppac_lookup(dev);
struct ifnet *ifp = &sc->sc_if;
struct mbuf *m0, *m;
int error = 0;
size_t len;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return (EHOSTDOWN);
m0 = mq_dequeue(&sc->sc_mq);
if (m0 == NULL) {
if (ISSET(ioflag, IO_NDELAY))
return (EWOULDBLOCK);
do {
error = tsleep_nsec(sc, (PZERO + 1)|PCATCH,
"pppacrd", INFSLP);
if (error != 0)
return (error);
m0 = mq_dequeue(&sc->sc_mq);
} while (m0 == NULL);
}
m = m0;
while (uio->uio_resid > 0) {
len = ulmin(uio->uio_resid, m->m_len);
if (len != 0) {
error = uiomove(mtod(m, caddr_t), len, uio);
if (error != 0)
break;
}
m = m->m_next;
if (m == NULL)
break;
}
m_freem(m0);
return (error);
}
int
pppacwrite(dev_t dev, struct uio *uio, int ioflag)
{
struct pppac_softc *sc = pppac_lookup(dev);
struct ifnet *ifp = &sc->sc_if;
uint32_t proto;
int error;
struct mbuf *m;
if (!ISSET(ifp->if_flags, IFF_RUNNING))
return (EHOSTDOWN);
if (uio->uio_resid < ifp->if_hdrlen || uio->uio_resid > MAXMCLBYTES)
return (EMSGSIZE);
m = m_gethdr(M_DONTWAIT, MT_DATA);
if (m == NULL)
return (ENOMEM);
if (uio->uio_resid > MHLEN) {
m_clget(m, M_WAITOK, uio->uio_resid);
if (!ISSET(m->m_flags, M_EXT)) {
m_free(m);
return (ENOMEM);
}
}
m->m_pkthdr.len = m->m_len = uio->uio_resid;
error = uiomove(mtod(m, void *), m->m_len, uio);
if (error != 0) {
m_freem(m);
return (error);
}
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif
/* strip the tunnel header */
proto = ntohl(*mtod(m, uint32_t *));
m_adj(m, sizeof(uint32_t));
m->m_flags &= ~(M_MCAST|M_BCAST);
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
counters_pkt(ifp->if_counters,
ifc_ipackets, ifc_ibytes, m->m_pkthdr.len);
NET_LOCK();
switch (proto) {
case AF_INET:
ipv4_input(ifp, m);
break;
#ifdef INET6
case AF_INET6:
ipv6_input(ifp, m);
break;
#endif
default:
m_freem(m);
error = EAFNOSUPPORT;
break;
}
NET_UNLOCK();
return (error);
}
int
pppacioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
struct pppac_softc *sc = pppac_lookup(dev);
int error = 0;
switch (cmd) {
case FIONBIO:
break;
case FIONREAD:
*(int *)data = mq_hdatalen(&sc->sc_mq);
break;
case PIPEXASESSION:
error = pppac_add_session(sc, (struct pipex_session_req *)data);
break;
case PIPEXDSESSION:
error = pppac_del_session(sc,
(struct pipex_session_close_req *)data);
break;
default:
error = pipex_ioctl(sc, cmd, data);
break;
}
return (error);
}
int
pppackqfilter(dev_t dev, struct knote *kn)
{
struct pppac_softc *sc = pppac_lookup(dev);
struct mutex *mtx;
struct klist *klist;
switch (kn->kn_filter) {
case EVFILT_READ:
mtx = &sc->sc_rsel_mtx;
klist = &sc->sc_rsel.si_note;
kn->kn_fop = &pppac_rd_filtops;
break;
case EVFILT_WRITE:
mtx = &sc->sc_wsel_mtx;
klist = &sc->sc_wsel.si_note;
kn->kn_fop = &pppac_wr_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = sc;
mtx_enter(mtx);
klist_insert_locked(klist, kn);
mtx_leave(mtx);
return (0);
}
static void
filt_pppac_rdetach(struct knote *kn)
{
struct pppac_softc *sc = kn->kn_hook;
struct klist *klist = &sc->sc_rsel.si_note;
mtx_enter(&sc->sc_rsel_mtx);
klist_remove_locked(klist, kn);
mtx_leave(&sc->sc_rsel_mtx);
}
static int
filt_pppac_read(struct knote *kn, long hint)
{
struct pppac_softc *sc = kn->kn_hook;
kn->kn_data = mq_hdatalen(&sc->sc_mq);
return (kn->kn_data > 0);
}
static void
filt_pppac_wdetach(struct knote *kn)
{
struct pppac_softc *sc = kn->kn_hook;
struct klist *klist = &sc->sc_wsel.si_note;
mtx_enter(&sc->sc_wsel_mtx);
klist_remove_locked(klist, kn);
mtx_leave(&sc->sc_wsel_mtx);
}
static int
filt_pppac_write(struct knote *kn, long hint)
{
/* We're always ready to accept a write. */
return (1);
}
int
pppacclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct pppac_softc *sc = pppac_lookup(dev);
struct ifnet *ifp = &sc->sc_if;
int s;
sc->sc_ready = 0;
NET_LOCK();
CLR(ifp->if_flags, IFF_RUNNING);
NET_UNLOCK();
if_detach(ifp);
s = splhigh();
klist_invalidate(&sc->sc_rsel.si_note);
klist_invalidate(&sc->sc_wsel.si_note);
splx(s);
pool_put(&pipex_session_pool, sc->sc_multicast_session);
pipex_destroy_all_sessions(sc);
LIST_REMOVE(sc, sc_entry);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
static int
pppac_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
/* struct ifreq *ifr = (struct ifreq *)data; */
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
SET(ifp->if_flags, IFF_UP); /* XXX cry cry */
/* FALLTHROUGH */
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP))
SET(ifp->if_flags, IFF_RUNNING);
else
CLR(ifp->if_flags, IFF_RUNNING);
break;
case SIOCSIFMTU:
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
/* XXX */
break;
default:
error = ENOTTY;
break;
}
return (error);
}
static int
pppac_add_session(struct pppac_softc *sc, struct pipex_session_req *req)
{
int error;
struct pipex_session *session;
error = pipex_init_session(&session, req);
if (error != 0)
return (error);
error = pipex_link_session(session, &sc->sc_if, sc);
if (error != 0)
pipex_rele_session(session);
return (error);
}
static int
pppac_del_session(struct pppac_softc *sc, struct pipex_session_close_req *req)
{
struct pipex_session *session;
mtx_enter(&pipex_list_mtx);
session = pipex_lookup_by_session_id_locked(req->pcr_protocol,
req->pcr_session_id);
if (session == NULL || session->ownersc != sc) {
mtx_leave(&pipex_list_mtx);
return (EINVAL);
}
pipex_unlink_session_locked(session);
pipex_rele_session(session);
mtx_leave(&pipex_list_mtx);
return (0);
}
static int
pppac_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
int error;
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EHOSTDOWN;
goto drop;
}
switch (dst->sa_family) {
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
break;
default:
error = EAFNOSUPPORT;
goto drop;
}
m->m_pkthdr.ph_family = dst->sa_family;
return (if_enqueue(ifp, m));
drop:
m_freem(m);
return (error);
}
static void
pppac_qstart(struct ifqueue *ifq)
{
struct ifnet *ifp = ifq->ifq_if;
struct pppac_softc *sc = ifp->if_softc;
struct mbuf *m, *m0;
struct pipex_session *session;
struct ip ip;
int rv;
while ((m = ifq_dequeue(ifq)) != NULL) {
#if NBPFILTER > 0
if (ifp->if_bpf) {
bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family, m,
BPF_DIRECTION_OUT);
}
#endif
switch (m->m_pkthdr.ph_family) {
case AF_INET:
if (m->m_pkthdr.len < sizeof(struct ip))
goto bad;
m_copydata(m, 0, sizeof(struct ip), &ip);
if (IN_MULTICAST(ip.ip_dst.s_addr)) {
/* pass a copy to pipex */
m0 = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (m0 != NULL)
pipex_ip_output(m0,
sc->sc_multicast_session);
else
goto bad;
} else {
session = pipex_lookup_by_ip_address(ip.ip_dst);
if (session != NULL) {
pipex_ip_output(m, session);
pipex_rele_session(session);
m = NULL;
}
}
break;
}
if (m == NULL) /* handled by pipex */
continue;
m = m_prepend(m, sizeof(uint32_t), M_DONTWAIT);
if (m == NULL)
goto bad;
*mtod(m, uint32_t *) = htonl(m->m_pkthdr.ph_family);
rv = mq_enqueue(&sc->sc_mq, m);
if (rv == 1)
counters_inc(ifp->if_counters, ifc_collisions);
continue;
bad:
counters_inc(ifp->if_counters, ifc_oerrors);
if (m != NULL)
m_freem(m);
continue;
}
if (!mq_empty(&sc->sc_mq)) {
wakeup(sc);
selwakeup(&sc->sc_rsel);
}
}
7
7
7
7
7
7
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* $OpenBSD: clock.c,v 1.36 2022/02/13 19:15:09 mlarkin Exp $ */
/* $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $ */
/*-
* Copyright (c) 1993, 1994 Charles M. Hannum.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz and Don Ahn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)clock.c 7.2 (Berkeley) 5/12/91
*/
/*
* Mach Operating System
* Copyright (c) 1991,1990,1989 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appears in all
copies and that both the copyright notice and this permission notice
appear in supporting documentation, and that the name of Intel
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.
INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Primitive clock interrupt routines.
*/
/* #define CLOCK_DEBUG */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <sys/timetc.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include <dev/clock_subr.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
#include <dev/ic/mc146818reg.h>
#include <dev/ic/i8253reg.h>
#include <amd64/isa/nvram.h>
/* Timecounter on the i8254 */
u_int32_t i8254_lastcount;
u_int32_t i8254_offset;
int i8254_ticked;
u_int i8254_get_timecount(struct timecounter *tc);
u_int i8254_simple_get_timecount(struct timecounter *tc);
static struct timecounter i8254_timecounter = {
.tc_get_timecount = i8254_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = TIMER_FREQ,
.tc_name = "i8254",
.tc_quality = 0,
.tc_priv = NULL,
.tc_user = 0,
};
int clockintr(void *);
int rtcintr(void *);
int gettick(void);
void rtcdrain(void *v);
int rtcget(mc_todregs *);
void rtcput(mc_todregs *);
int bcdtobin(int);
int bintobcd(int);
u_int mc146818_read(void *, u_int);
void mc146818_write(void *, u_int, u_int);
u_int
mc146818_read(void *sc, u_int reg)
{
outb(IO_RTC, reg);
DELAY(1);
return (inb(IO_RTC+1));
}
void
mc146818_write(void *sc, u_int reg, u_int datum)
{
outb(IO_RTC, reg);
DELAY(1);
outb(IO_RTC+1, datum);
DELAY(1);
}
struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
u_long rtclock_tval;
void
startclocks(void)
{
mtx_enter(&timer_mutex);
rtclock_tval = TIMER_DIV(hz);
i8254_startclock();
mtx_leave(&timer_mutex);
}
int
clockintr(void *arg)
{
struct clockframe *frame = arg;
if (timecounter->tc_get_timecount == i8254_get_timecount) {
if (i8254_ticked) {
i8254_ticked = 0;
} else {
i8254_offset += rtclock_tval;
i8254_lastcount = 0;
}
}
hardclock(frame);
return 1;
}
int
rtcintr(void *arg)
{
struct clockframe *frame = arg;
u_int stat = 0;
/*
* If rtcintr is 'late', next intr may happen immediately.
* Get them all. (Also, see comment in cpu_initclocks().)
*/
while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
statclock(frame);
stat = 1;
}
return (stat);
}
int
gettick(void)
{
u_long s;
u_char lo, hi;
/* Don't want someone screwing with the counter while we're here. */
mtx_enter(&timer_mutex);
s = intr_disable();
/* Select counter 0 and latch it. */
outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
lo = inb(IO_TIMER1+TIMER_CNTR0);
hi = inb(IO_TIMER1+TIMER_CNTR0);
intr_restore(s);
mtx_leave(&timer_mutex);
return ((hi << 8) | lo);
}
/*
* Wait "n" microseconds.
* Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
* Note: timer had better have been programmed before this is first used!
* (Note that we use `rate generator' mode, which counts at 1:1; `square
* wave' mode counts at 2:1).
*/
void
i8254_delay(int n)
{
int limit, tick, otick;
static const int delaytab[26] = {
0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
24, 25, 27, 28, 29, 30,
};
/*
* Read the counter first, so that the rest of the setup overhead is
* counted.
*/
otick = gettick();
if (n <= 25)
n = delaytab[n];
else {
/* Force 64-bit math to avoid 32-bit overflow if possible. */
n = (int64_t)n * TIMER_FREQ / 1000000;
}
limit = TIMER_FREQ / hz;
while (n > 0) {
tick = gettick();
if (tick > otick)
n -= limit - (tick - otick);
else
n -= otick - tick;
otick = tick;
}
}
void
rtcdrain(void *v)
{
struct timeout *to = (struct timeout *)v;
if (to != NULL)
timeout_del(to);
/* Drain any un-acknowledged RTC interrupts. */
while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
; /* Nothing. */
}
void
i8254_initclocks(void)
{
stathz = 128;
profhz = 1024;
/*
* While the clock interrupt handler isn't really MPSAFE, the
* i8254 can't really be used as a clock on a true MP system.
*/
isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
clockintr, 0, "clock");
isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
rtcintr, 0, "rtc");
rtcstart(); /* start the mc146818 clock */
i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
}
void
rtcstart(void)
{
static struct timeout rtcdrain_timeout;
mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
/*
* On a number of i386 systems, the rtc will fail to start when booting
* the system. This is due to us missing to acknowledge an interrupt
* during early stages of the boot process. If we do not acknowledge
* the interrupt, the rtc clock will not generate further interrupts.
* To solve this, once interrupts are enabled, use a timeout (once)
* to drain any un-acknowledged rtc interrupt(s).
*/
timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
timeout_add(&rtcdrain_timeout, 1);
}
void
rtcstop(void)
{
mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
}
int
rtcget(mc_todregs *regs)
{
if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
return (-1);
MC146818_GETTOD(NULL, regs); /* XXX softc */
return (0);
}
void
rtcput(mc_todregs *regs)
{
MC146818_PUTTOD(NULL, regs); /* XXX softc */
}
int
bcdtobin(int n)
{
return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
}
int
bintobcd(int n)
{
return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
}
/*
* check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
* to be called at splclock()
*/
static int cmoscheck(void);
static int
cmoscheck(void)
{
int i;
unsigned short cksum = 0;
for (i = 0x10; i <= 0x2d; i++)
cksum += mc146818_read(NULL, i); /* XXX softc */
return (cksum == (mc146818_read(NULL, 0x2e) << 8)
+ mc146818_read(NULL, 0x2f));
}
/*
* patchable to control century byte handling:
* 1: always update
* -1: never touch
* 0: try to figure out itself
*/
int rtc_update_century = 0;
/*
* Expand a two-digit year as read from the clock chip
* into full width.
* Being here, deal with the CMOS century byte.
*/
static int centb = NVRAM_CENTURY;
static int clock_expandyear(int);
static int
clock_expandyear(int clockyear)
{
int s, clockcentury, cmoscentury;
clockcentury = (clockyear < 70) ? 20 : 19;
clockyear += 100 * clockcentury;
if (rtc_update_century < 0)
return (clockyear);
s = splclock();
if (cmoscheck())
cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
else
cmoscentury = 0;
splx(s);
if (!cmoscentury)
return (clockyear);
cmoscentury = bcdtobin(cmoscentury);
if (cmoscentury != clockcentury) {
/* XXX note: saying "century is 20" might confuse the naive. */
printf("WARNING: NVRAM century is %d but RTC year is %d\n",
cmoscentury, clockyear);
/* Kludge to roll over century. */
if ((rtc_update_century > 0) ||
((cmoscentury == 19) && (clockcentury == 20) &&
(clockyear == 2000))) {
printf("WARNING: Setting NVRAM century to %d\n",
clockcentury);
s = splclock();
mc146818_write(NULL, centb, bintobcd(clockcentury));
splx(s);
}
} else if (cmoscentury == 19 && rtc_update_century == 0)
rtc_update_century = 1; /* will update later in resettodr() */
return (clockyear);
}
int
rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
{
mc_todregs rtclk;
struct clock_ymdhms dt;
int s;
s = splclock();
if (rtcget(&rtclk)) {
splx(s);
return EINVAL;
}
splx(s);
#ifdef CLOCK_DEBUG
printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
rtclk[MC_SEC]);
#endif
dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
dt.dt_min = bcdtobin(rtclk[MC_MIN]);
dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
dt.dt_day = bcdtobin(rtclk[MC_DOM]);
dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
tv->tv_usec = 0;
return 0;
}
int
rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
{
mc_todregs rtclk;
struct clock_ymdhms dt;
int century, s;
s = splclock();
if (rtcget(&rtclk))
memset(&rtclk, 0, sizeof(rtclk));
splx(s);
clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
rtclk[MC_SEC] = bintobcd(dt.dt_sec);
rtclk[MC_MIN] = bintobcd(dt.dt_min);
rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
rtclk[MC_DOW] = dt.dt_wday + 1;
rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
rtclk[MC_DOM] = bintobcd(dt.dt_day);
#ifdef CLOCK_DEBUG
printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
#endif
s = splclock();
rtcput(&rtclk);
if (rtc_update_century > 0) {
century = bintobcd(dt.dt_year / 100);
mc146818_write(NULL, centb, century); /* XXX softc */
}
splx(s);
return 0;
}
extern todr_chip_handle_t todr_handle;
struct todr_chip_handle rtc_todr;
void
rtcinit(void)
{
rtc_todr.todr_gettime = rtcgettime;
rtc_todr.todr_settime = rtcsettime;
todr_handle = &rtc_todr;
}
void
setstatclockrate(int arg)
{
if (initclock_func == i8254_initclocks) {
if (arg == stathz)
mc146818_write(NULL, MC_REGA,
MC_BASE_32_KHz | MC_RATE_128_Hz);
else
mc146818_write(NULL, MC_REGA,
MC_BASE_32_KHz | MC_RATE_1024_Hz);
}
}
void
i8254_inittimecounter(void)
{
tc_init(&i8254_timecounter);
}
/*
* If we're using lapic to drive hardclock, we can use a simpler
* algorithm for the i8254 timecounters.
*/
void
i8254_inittimecounter_simple(void)
{
i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
i8254_timecounter.tc_counter_mask = 0x7fff;
i8254_timecounter.tc_frequency = TIMER_FREQ;
mtx_enter(&timer_mutex);
rtclock_tval = 0x8000;
i8254_startclock();
mtx_leave(&timer_mutex);
tc_init(&i8254_timecounter);
}
void
i8254_startclock(void)
{
u_long tval = rtclock_tval;
outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
}
u_int
i8254_simple_get_timecount(struct timecounter *tc)
{
return (rtclock_tval - gettick());
}
u_int
i8254_get_timecount(struct timecounter *tc)
{
u_char hi, lo;
u_int count;
u_long s;
s = intr_disable();
outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
lo = inb(IO_TIMER1+TIMER_CNTR0);
hi = inb(IO_TIMER1+TIMER_CNTR0);
count = rtclock_tval - ((hi << 8) | lo);
if (count < i8254_lastcount) {
i8254_ticked = 1;
i8254_offset += rtclock_tval;
}
i8254_lastcount = count;
count += i8254_offset;
intr_restore(s);
return (count);
}
74
11
1
5
1
2
6
1
5
6
1
5
22
1
7
18
6
3
3
1
6
3
6
18
18
18
18
18
17
6
1
5
5
1
4
6
1
5
6
4
6
24
23
23
15
20
18
18
18
6
6
4
5
4
4
4
4
1
3
17
18
3
3
3
18
18
4
4
18
16
18
14
17
4
6
5
4
4
1
3
4
4
1
3
4
8
3
3
3
4
7
6
12
1
4
4
9
1
9
10
13
1
8
4
12
12
4
1
3
10
7
4
2
5
9
6
5
6
9
9
59
10
52
7
45
7
41
9
9
41
8
5
4
1
5
14
14
11
10
15
15
11
15
14
14
14
14
13
22
18
20
22
22
18
15
44
46
43
30
28
12
11
31
15
18
5
16
30
30
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
/* $OpenBSD: pf_table.c,v 1.143 2022/06/26 11:37:08 mbuhl Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/pool.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_ipsp.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#define ACCEPT_FLAGS(flags, oklist) \
do { \
if ((flags & ~(oklist)) & \
PFR_FLAG_ALLMASK) \
return (EINVAL); \
} while (0)
#define COPYIN(from, to, size, flags) \
((flags & PFR_FLAG_USERIOCTL) ? \
copyin((from), (to), (size)) : \
(bcopy((from), (to), (size)), 0))
#define COPYOUT(from, to, size, flags) \
((flags & PFR_FLAG_USERIOCTL) ? \
copyout((from), (to), (size)) : \
(bcopy((from), (to), (size)), 0))
#define YIELD(ok) \
do { \
if (ok) \
sched_pause(preempt); \
} while (0)
#define FILLIN_SIN(sin, addr) \
do { \
(sin).sin_len = sizeof(sin); \
(sin).sin_family = AF_INET; \
(sin).sin_addr = (addr); \
} while (0)
#define FILLIN_SIN6(sin6, addr) \
do { \
(sin6).sin6_len = sizeof(sin6); \
(sin6).sin6_family = AF_INET6; \
(sin6).sin6_addr = (addr); \
} while (0)
#define SWAP(type, a1, a2) \
do { \
type tmp = a1; \
a1 = a2; \
a2 = tmp; \
} while (0)
#define SUNION2PF(su, af) (((af)==AF_INET) ? \
(struct pf_addr *)&(su)->sin.sin_addr : \
(struct pf_addr *)&(su)->sin6.sin6_addr)
#define AF_BITS(af) (((af)==AF_INET)?32:128)
#define ADDR_NETWORK(ad) ((ad)->pfra_net < AF_BITS((ad)->pfra_af))
#define KENTRY_NETWORK(ke) ((ke)->pfrke_net < AF_BITS((ke)->pfrke_af))
#define NO_ADDRESSES (-1)
#define ENQUEUE_UNMARKED_ONLY (1)
#define INVERT_NEG_FLAG (1)
struct pfr_walktree {
enum pfrw_op {
PFRW_MARK,
PFRW_SWEEP,
PFRW_ENQUEUE,
PFRW_GET_ADDRS,
PFRW_GET_ASTATS,
PFRW_POOL_GET,
PFRW_DYNADDR_UPDATE
} pfrw_op;
union {
struct pfr_addr *pfrw1_addr;
struct pfr_astats *pfrw1_astats;
struct pfr_kentryworkq *pfrw1_workq;
struct pfr_kentry *pfrw1_kentry;
struct pfi_dynaddr *pfrw1_dyn;
} pfrw_1;
int pfrw_free;
int pfrw_flags;
};
#define pfrw_addr pfrw_1.pfrw1_addr
#define pfrw_astats pfrw_1.pfrw1_astats
#define pfrw_workq pfrw_1.pfrw1_workq
#define pfrw_kentry pfrw_1.pfrw1_kentry
#define pfrw_dyn pfrw_1.pfrw1_dyn
#define pfrw_cnt pfrw_free
#define senderr(e) do { rv = (e); goto _bad; } while (0)
struct pool pfr_ktable_pl;
struct pool pfr_kentry_pl[PFRKE_MAX];
struct pool pfr_kcounters_pl;
union sockaddr_union pfr_mask;
struct pf_addr pfr_ffaddr;
int pfr_gcd(int, int);
void pfr_copyout_addr(struct pfr_addr *,
struct pfr_kentry *ke);
int pfr_validate_addr(struct pfr_addr *);
void pfr_enqueue_addrs(struct pfr_ktable *,
struct pfr_kentryworkq *, int *, int);
void pfr_mark_addrs(struct pfr_ktable *);
struct pfr_kentry *pfr_lookup_addr(struct pfr_ktable *,
struct pfr_addr *, int);
struct pfr_kentry *pfr_lookup_kentry(struct pfr_ktable *,
struct pfr_kentry *, int);
struct pfr_kentry *pfr_create_kentry(struct pfr_addr *);
struct pfr_kentry *pfr_create_kentry_unlocked(struct pfr_addr *, int);
void pfr_kentry_kif_ref(struct pfr_kentry *);
void pfr_destroy_kentries(struct pfr_kentryworkq *);
void pfr_destroy_ioq(struct pfr_kentryworkq *, int);
void pfr_destroy_kentry(struct pfr_kentry *);
void pfr_insert_kentries(struct pfr_ktable *,
struct pfr_kentryworkq *, time_t);
void pfr_remove_kentries(struct pfr_ktable *,
struct pfr_kentryworkq *);
void pfr_clstats_kentries(struct pfr_kentryworkq *, time_t,
int);
void pfr_reset_feedback(struct pfr_addr *, int, int);
void pfr_prepare_network(union sockaddr_union *, int, int);
int pfr_route_kentry(struct pfr_ktable *,
struct pfr_kentry *);
int pfr_unroute_kentry(struct pfr_ktable *,
struct pfr_kentry *);
int pfr_walktree(struct radix_node *, void *, u_int);
int pfr_validate_table(struct pfr_table *, int, int);
int pfr_fix_anchor(char *);
void pfr_commit_ktable(struct pfr_ktable *, time_t);
void pfr_insert_ktables(struct pfr_ktableworkq *);
void pfr_insert_ktable(struct pfr_ktable *);
void pfr_setflags_ktables(struct pfr_ktableworkq *);
void pfr_setflags_ktable(struct pfr_ktable *, int);
void pfr_clstats_ktables(struct pfr_ktableworkq *, time_t,
int);
void pfr_clstats_ktable(struct pfr_ktable *, time_t, int);
struct pfr_ktable *pfr_create_ktable(struct pfr_table *, time_t, int,
int);
void pfr_destroy_ktables(struct pfr_ktableworkq *, int);
void pfr_destroy_ktables_aux(struct pfr_ktableworkq *);
void pfr_destroy_ktable(struct pfr_ktable *, int);
int pfr_ktable_compare(struct pfr_ktable *,
struct pfr_ktable *);
void pfr_ktable_winfo_update(struct pfr_ktable *,
struct pfr_kentry *);
struct pfr_ktable *pfr_lookup_table(struct pfr_table *);
void pfr_clean_node_mask(struct pfr_ktable *,
struct pfr_kentryworkq *);
int pfr_table_count(struct pfr_table *, int);
int pfr_skip_table(struct pfr_table *,
struct pfr_ktable *, int);
struct pfr_kentry *pfr_kentry_byidx(struct pfr_ktable *, int, int);
int pfr_islinklocal(sa_family_t, struct pf_addr *);
RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
struct pfr_ktablehead pfr_ktables;
struct pfr_table pfr_nulltable;
int pfr_ktable_cnt;
int
pfr_gcd(int m, int n)
{
int t;
while (m > 0) {
t = n % m;
n = m;
m = t;
}
return (n);
}
void
pfr_initialize(void)
{
rn_init(sizeof(struct sockaddr_in6));
pool_init(&pfr_ktable_pl, sizeof(struct pfr_ktable),
0, IPL_SOFTNET, 0, "pfrktable", NULL);
pool_init(&pfr_kentry_pl[PFRKE_PLAIN], sizeof(struct pfr_kentry),
0, IPL_SOFTNET, 0, "pfrke_plain", NULL);
pool_init(&pfr_kentry_pl[PFRKE_ROUTE], sizeof(struct pfr_kentry_route),
0, IPL_SOFTNET, 0, "pfrke_route", NULL);
pool_init(&pfr_kentry_pl[PFRKE_COST], sizeof(struct pfr_kentry_cost),
0, IPL_SOFTNET, 0, "pfrke_cost", NULL);
pool_init(&pfr_kcounters_pl, sizeof(struct pfr_kcounters),
0, IPL_SOFTNET, 0, "pfrkcounters", NULL);
memset(&pfr_ffaddr, 0xff, sizeof(pfr_ffaddr));
}
int
pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
{
struct pfr_ktable *kt;
struct pfr_kentryworkq workq;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
if (kt->pfrkt_flags & PFR_TFLAG_CONST)
return (EPERM);
pfr_enqueue_addrs(kt, &workq, ndel, 0);
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_remove_kentries(kt, &workq);
if (kt->pfrkt_cnt) {
DPFPRINTF(LOG_NOTICE,
"pfr_clr_addrs: corruption detected (%d).",
kt->pfrkt_cnt);
kt->pfrkt_cnt = 0;
}
}
return (0);
}
void
pfr_fill_feedback(struct pfr_kentry_all *ke, struct pfr_addr *ad)
{
ad->pfra_type = ke->pfrke_type;
switch (ke->pfrke_type) {
case PFRKE_PLAIN:
break;
case PFRKE_COST:
((struct pfr_kentry_cost *)ke)->weight = ad->pfra_weight;
/* FALLTHROUGH */
case PFRKE_ROUTE:
if (ke->pfrke_rifname[0])
strlcpy(ad->pfra_ifname, ke->pfrke_rifname, IFNAMSIZ);
break;
}
switch (ke->pfrke_af) {
case AF_INET:
ad->pfra_ip4addr = ke->pfrke_sa.sin.sin_addr;
break;
#ifdef INET6
case AF_INET6:
ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr;
break;
#endif /* INET6 */
default:
unhandled_af(ke->pfrke_af);
}
ad->pfra_weight = ((struct pfr_kentry_cost *)ke)->weight;
ad->pfra_af = ke->pfrke_af;
ad->pfra_net = ke->pfrke_net;
if (ke->pfrke_flags & PFRKE_FLAG_NOT)
ad->pfra_not = 1;
ad->pfra_fback = ke->pfrke_fb;
}
int
pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nadd, int flags)
{
struct pfr_ktable *kt, *tmpkt;
struct pfr_kentryworkq workq, ioq;
struct pfr_kentry *p, *q, *ke;
struct pfr_addr ad;
int i, rv, xadd = 0;
time_t tzero = gettime();
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK);
if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL))
return (EINVAL);
tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (tmpkt == NULL)
return (ENOMEM);
SLIST_INIT(&workq);
SLIST_INIT(&ioq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
senderr(EFAULT);
if (pfr_validate_addr(&ad))
senderr(EINVAL);
ke = pfr_create_kentry_unlocked(&ad, flags);
if (ke == NULL)
senderr(ENOMEM);
ke->pfrke_fb = PFR_FB_NONE;
SLIST_INSERT_HEAD(&ioq, ke, pfrke_ioq);
}
NET_LOCK();
PF_LOCK();
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) {
PF_UNLOCK();
NET_UNLOCK();
senderr(ESRCH);
}
if (kt->pfrkt_flags & PFR_TFLAG_CONST) {
PF_UNLOCK();
NET_UNLOCK();
senderr(EPERM);
}
SLIST_FOREACH(ke, &ioq, pfrke_ioq) {
pfr_kentry_kif_ref(ke);
p = pfr_lookup_kentry(kt, ke, 1);
q = pfr_lookup_kentry(tmpkt, ke, 1);
if (flags & PFR_FLAG_FEEDBACK) {
if (q != NULL)
ke->pfrke_fb = PFR_FB_DUPLICATE;
else if (p == NULL)
ke->pfrke_fb = PFR_FB_ADDED;
else if ((p->pfrke_flags & PFRKE_FLAG_NOT) !=
(ke->pfrke_flags & PFRKE_FLAG_NOT))
ke->pfrke_fb = PFR_FB_CONFLICT;
else
ke->pfrke_fb = PFR_FB_NONE;
}
if (p == NULL && q == NULL) {
if (pfr_route_kentry(tmpkt, ke)) {
/* defer destroy after feedback is processed */
ke->pfrke_fb = PFR_FB_NONE;
} else {
/*
* mark entry as added to table, so we won't
* kill it with rest of the ioq
*/
ke->pfrke_fb = PFR_FB_ADDED;
SLIST_INSERT_HEAD(&workq, ke, pfrke_workq);
xadd++;
}
}
}
/* remove entries, which we will insert from tmpkt */
pfr_clean_node_mask(tmpkt, &workq);
if (!(flags & PFR_FLAG_DUMMY))
pfr_insert_kentries(kt, &workq, tzero);
PF_UNLOCK();
NET_UNLOCK();
if (flags & PFR_FLAG_FEEDBACK) {
i = 0;
while ((ke = SLIST_FIRST(&ioq)) != NULL) {
YIELD(flags & PFR_FLAG_USERIOCTL);
pfr_fill_feedback((struct pfr_kentry_all *)ke, &ad);
if (COPYOUT(&ad, addr+i, sizeof(ad), flags))
senderr(EFAULT);
i++;
SLIST_REMOVE_HEAD(&ioq, pfrke_ioq);
switch (ke->pfrke_fb) {
case PFR_FB_CONFLICT:
case PFR_FB_DUPLICATE:
case PFR_FB_NONE:
pfr_destroy_kentry(ke);
break;
case PFR_FB_ADDED:
if (flags & PFR_FLAG_DUMMY)
pfr_destroy_kentry(ke);
}
}
} else
pfr_destroy_ioq(&ioq, flags);
if (nadd != NULL)
*nadd = xadd;
pfr_destroy_ktable(tmpkt, 0);
return (0);
_bad:
pfr_destroy_ioq(&ioq, flags);
if (flags & PFR_FLAG_FEEDBACK)
pfr_reset_feedback(addr, size, flags);
pfr_destroy_ktable(tmpkt, 0);
return (rv);
}
int
pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *ndel, int flags)
{
struct pfr_ktable *kt;
struct pfr_kentryworkq workq;
struct pfr_kentry *p;
struct pfr_addr ad;
int i, rv, xdel = 0, log = 1;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK);
if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
if (kt->pfrkt_flags & PFR_TFLAG_CONST)
return (EPERM);
/*
* there are two algorithms to choose from here.
* with:
* n: number of addresses to delete
* N: number of addresses in the table
*
* one is O(N) and is better for large 'n'
* one is O(n*LOG(N)) and is better for small 'n'
*
* following code try to decide which one is best.
*/
for (i = kt->pfrkt_cnt; i > 0; i >>= 1)
log++;
if (size > kt->pfrkt_cnt/log) {
/* full table scan */
pfr_mark_addrs(kt);
} else {
/* iterate over addresses to delete */
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
return (EFAULT);
if (pfr_validate_addr(&ad))
return (EINVAL);
p = pfr_lookup_addr(kt, &ad, 1);
if (p != NULL)
p->pfrke_flags &= ~PFRKE_FLAG_MARK;
}
}
SLIST_INIT(&workq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
senderr(EFAULT);
if (pfr_validate_addr(&ad))
senderr(EINVAL);
p = pfr_lookup_addr(kt, &ad, 1);
if (flags & PFR_FLAG_FEEDBACK) {
if (p == NULL)
ad.pfra_fback = PFR_FB_NONE;
else if ((p->pfrke_flags & PFRKE_FLAG_NOT) !=
ad.pfra_not)
ad.pfra_fback = PFR_FB_CONFLICT;
else if (p->pfrke_flags & PFRKE_FLAG_MARK)
ad.pfra_fback = PFR_FB_DUPLICATE;
else
ad.pfra_fback = PFR_FB_DELETED;
}
if (p != NULL &&
(p->pfrke_flags & PFRKE_FLAG_NOT) == ad.pfra_not &&
!(p->pfrke_flags & PFRKE_FLAG_MARK)) {
p->pfrke_flags |= PFRKE_FLAG_MARK;
SLIST_INSERT_HEAD(&workq, p, pfrke_workq);
xdel++;
}
if (flags & PFR_FLAG_FEEDBACK)
if (COPYOUT(&ad, addr+i, sizeof(ad), flags))
senderr(EFAULT);
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_remove_kentries(kt, &workq);
}
if (ndel != NULL)
*ndel = xdel;
return (0);
_bad:
if (flags & PFR_FLAG_FEEDBACK)
pfr_reset_feedback(addr, size, flags);
return (rv);
}
int
pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *size2, int *nadd, int *ndel, int *nchange, int flags,
u_int32_t ignore_pfrt_flags)
{
struct pfr_ktable *kt, *tmpkt;
struct pfr_kentryworkq addq, delq, changeq;
struct pfr_kentry *p, *q;
struct pfr_addr ad;
int i, rv, xadd = 0, xdel = 0, xchange = 0;
time_t tzero = gettime();
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK);
if (pfr_validate_table(tbl, ignore_pfrt_flags, flags &
PFR_FLAG_USERIOCTL))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
if (kt->pfrkt_flags & PFR_TFLAG_CONST)
return (EPERM);
tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (tmpkt == NULL)
return (ENOMEM);
pfr_mark_addrs(kt);
SLIST_INIT(&addq);
SLIST_INIT(&delq);
SLIST_INIT(&changeq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
senderr(EFAULT);
if (pfr_validate_addr(&ad))
senderr(EINVAL);
ad.pfra_fback = PFR_FB_NONE;
p = pfr_lookup_addr(kt, &ad, 1);
if (p != NULL) {
if (p->pfrke_flags & PFRKE_FLAG_MARK) {
ad.pfra_fback = PFR_FB_DUPLICATE;
goto _skip;
}
p->pfrke_flags |= PFRKE_FLAG_MARK;
if ((p->pfrke_flags & PFRKE_FLAG_NOT) != ad.pfra_not) {
SLIST_INSERT_HEAD(&changeq, p, pfrke_workq);
ad.pfra_fback = PFR_FB_CHANGED;
xchange++;
}
} else {
q = pfr_lookup_addr(tmpkt, &ad, 1);
if (q != NULL) {
ad.pfra_fback = PFR_FB_DUPLICATE;
goto _skip;
}
p = pfr_create_kentry(&ad);
if (p == NULL)
senderr(ENOMEM);
if (pfr_route_kentry(tmpkt, p)) {
pfr_destroy_kentry(p);
ad.pfra_fback = PFR_FB_NONE;
goto _skip;
}
SLIST_INSERT_HEAD(&addq, p, pfrke_workq);
ad.pfra_fback = PFR_FB_ADDED;
xadd++;
if (p->pfrke_type == PFRKE_COST)
kt->pfrkt_refcntcost++;
pfr_ktable_winfo_update(kt, p);
}
_skip:
if (flags & PFR_FLAG_FEEDBACK)
if (COPYOUT(&ad, addr+i, sizeof(ad), flags))
senderr(EFAULT);
}
pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY);
if ((flags & PFR_FLAG_FEEDBACK) && *size2) {
if (*size2 < size+xdel) {
*size2 = size+xdel;
senderr(0);
}
i = 0;
SLIST_FOREACH(p, &delq, pfrke_workq) {
pfr_copyout_addr(&ad, p);
ad.pfra_fback = PFR_FB_DELETED;
if (COPYOUT(&ad, addr+size+i, sizeof(ad), flags))
senderr(EFAULT);
i++;
}
}
pfr_clean_node_mask(tmpkt, &addq);
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_insert_kentries(kt, &addq, tzero);
pfr_remove_kentries(kt, &delq);
pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG);
} else
pfr_destroy_kentries(&addq);
if (nadd != NULL)
*nadd = xadd;
if (ndel != NULL)
*ndel = xdel;
if (nchange != NULL)
*nchange = xchange;
if ((flags & PFR_FLAG_FEEDBACK) && size2)
*size2 = size+xdel;
pfr_destroy_ktable(tmpkt, 0);
return (0);
_bad:
pfr_clean_node_mask(tmpkt, &addq);
pfr_destroy_kentries(&addq);
if (flags & PFR_FLAG_FEEDBACK)
pfr_reset_feedback(addr, size, flags);
pfr_destroy_ktable(tmpkt, 0);
return (rv);
}
int
pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nmatch, int flags)
{
struct pfr_ktable *kt;
struct pfr_kentry *p;
struct pfr_addr ad;
int i, xmatch = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_REPLACE);
if (pfr_validate_table(tbl, 0, 0))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
return (EFAULT);
if (pfr_validate_addr(&ad))
return (EINVAL);
if (ADDR_NETWORK(&ad))
return (EINVAL);
p = pfr_lookup_addr(kt, &ad, 0);
if (flags & PFR_FLAG_REPLACE)
pfr_copyout_addr(&ad, p);
ad.pfra_fback = (p == NULL) ? PFR_FB_NONE :
((p->pfrke_flags & PFRKE_FLAG_NOT) ?
PFR_FB_NOTMATCH : PFR_FB_MATCH);
if (p != NULL && !(p->pfrke_flags & PFRKE_FLAG_NOT))
xmatch++;
if (COPYOUT(&ad, addr+i, sizeof(ad), flags))
return (EFAULT);
}
if (nmatch != NULL)
*nmatch = xmatch;
return (0);
}
int
pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
int flags)
{
struct pfr_ktable *kt;
struct pfr_walktree w;
int rv;
ACCEPT_FLAGS(flags, 0);
if (pfr_validate_table(tbl, 0, 0))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
if (kt->pfrkt_cnt > *size) {
*size = kt->pfrkt_cnt;
return (0);
}
bzero(&w, sizeof(w));
w.pfrw_op = PFRW_GET_ADDRS;
w.pfrw_addr = addr;
w.pfrw_free = kt->pfrkt_cnt;
w.pfrw_flags = flags;
rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w);
if (!rv)
rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w);
if (rv)
return (rv);
if (w.pfrw_free) {
DPFPRINTF(LOG_ERR,
"pfr_get_addrs: corruption detected (%d)", w.pfrw_free);
return (ENOTTY);
}
*size = kt->pfrkt_cnt;
return (0);
}
int
pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
int flags)
{
struct pfr_ktable *kt;
struct pfr_walktree w;
struct pfr_kentryworkq workq;
int rv;
time_t tzero = gettime();
if (pfr_validate_table(tbl, 0, 0))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
if (kt->pfrkt_cnt > *size) {
*size = kt->pfrkt_cnt;
return (0);
}
bzero(&w, sizeof(w));
w.pfrw_op = PFRW_GET_ASTATS;
w.pfrw_astats = addr;
w.pfrw_free = kt->pfrkt_cnt;
w.pfrw_flags = flags;
rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w);
if (!rv)
rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w);
if (!rv && (flags & PFR_FLAG_CLSTATS)) {
pfr_enqueue_addrs(kt, &workq, NULL, 0);
pfr_clstats_kentries(&workq, tzero, 0);
}
if (rv)
return (rv);
if (w.pfrw_free) {
DPFPRINTF(LOG_ERR,
"pfr_get_astats: corruption detected (%d)", w.pfrw_free);
return (ENOTTY);
}
*size = kt->pfrkt_cnt;
return (0);
}
int
pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nzero, int flags)
{
struct pfr_ktable *kt;
struct pfr_kentryworkq workq;
struct pfr_kentry *p;
struct pfr_addr ad;
int i, rv, xzero = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK);
if (pfr_validate_table(tbl, 0, 0))
return (EINVAL);
kt = pfr_lookup_table(tbl);
if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (ESRCH);
SLIST_INIT(&workq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
senderr(EFAULT);
if (pfr_validate_addr(&ad))
senderr(EINVAL);
p = pfr_lookup_addr(kt, &ad, 1);
if (flags & PFR_FLAG_FEEDBACK) {
ad.pfra_fback = (p != NULL) ?
PFR_FB_CLEARED : PFR_FB_NONE;
if (COPYOUT(&ad, addr+i, sizeof(ad), flags))
senderr(EFAULT);
}
if (p != NULL) {
SLIST_INSERT_HEAD(&workq, p, pfrke_workq);
xzero++;
}
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_clstats_kentries(&workq, gettime(), 0);
}
if (nzero != NULL)
*nzero = xzero;
return (0);
_bad:
if (flags & PFR_FLAG_FEEDBACK)
pfr_reset_feedback(addr, size, flags);
return (rv);
}
int
pfr_validate_addr(struct pfr_addr *ad)
{
int i;
switch (ad->pfra_af) {
case AF_INET:
if (ad->pfra_net > 32)
return (-1);
break;
#ifdef INET6
case AF_INET6:
if (ad->pfra_net > 128)
return (-1);
break;
#endif /* INET6 */
default:
return (-1);
}
if (ad->pfra_net < 128 &&
(((caddr_t)ad)[ad->pfra_net/8] & (0xFF >> (ad->pfra_net%8))))
return (-1);
for (i = (ad->pfra_net+7)/8; i < sizeof(ad->pfra_u); i++)
if (((caddr_t)ad)[i])
return (-1);
if (ad->pfra_not && ad->pfra_not != 1)
return (-1);
if (ad->pfra_fback != PFR_FB_NONE)
return (-1);
if (ad->pfra_type >= PFRKE_MAX)
return (-1);
return (0);
}
void
pfr_enqueue_addrs(struct pfr_ktable *kt, struct pfr_kentryworkq *workq,
int *naddr, int sweep)
{
struct pfr_walktree w;
SLIST_INIT(workq);
bzero(&w, sizeof(w));
w.pfrw_op = sweep ? PFRW_SWEEP : PFRW_ENQUEUE;
w.pfrw_workq = workq;
if (kt->pfrkt_ip4 != NULL)
if (rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w))
DPFPRINTF(LOG_ERR,
"pfr_enqueue_addrs: IPv4 walktree failed.");
if (kt->pfrkt_ip6 != NULL)
if (rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w))
DPFPRINTF(LOG_ERR,
"pfr_enqueue_addrs: IPv6 walktree failed.");
if (naddr != NULL)
*naddr = w.pfrw_cnt;
}
void
pfr_mark_addrs(struct pfr_ktable *kt)
{
struct pfr_walktree w;
bzero(&w, sizeof(w));
w.pfrw_op = PFRW_MARK;
if (rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w))
DPFPRINTF(LOG_ERR,
"pfr_mark_addrs: IPv4 walktree failed.");
if (rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w))
DPFPRINTF(LOG_ERR,
"pfr_mark_addrs: IPv6 walktree failed.");
}
struct pfr_kentry *
pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact)
{
union sockaddr_union sa, mask;
struct radix_node_head *head;
struct pfr_kentry *ke;
bzero(&sa, sizeof(sa));
switch (ad->pfra_af) {
case AF_INET:
FILLIN_SIN(sa.sin, ad->pfra_ip4addr);
head = kt->pfrkt_ip4;
break;
#ifdef INET6
case AF_INET6:
FILLIN_SIN6(sa.sin6, ad->pfra_ip6addr);
head = kt->pfrkt_ip6;
break;
#endif /* INET6 */
default:
unhandled_af(ad->pfra_af);
}
if (ADDR_NETWORK(ad)) {
pfr_prepare_network(&mask, ad->pfra_af, ad->pfra_net);
ke = (struct pfr_kentry *)rn_lookup(&sa, &mask, head);
} else {
ke = (struct pfr_kentry *)rn_match(&sa, head);
if (exact && ke && KENTRY_NETWORK(ke))
ke = NULL;
}
return (ke);
}
struct pfr_kentry *
pfr_lookup_kentry(struct pfr_ktable *kt, struct pfr_kentry *key, int exact)
{
union sockaddr_union mask;
struct radix_node_head *head;
struct pfr_kentry *ke;
switch (key->pfrke_af) {
case AF_INET:
head = kt->pfrkt_ip4;
break;
#ifdef INET6
case AF_INET6:
head = kt->pfrkt_ip6;
break;
#endif /* INET6 */
default:
unhandled_af(key->pfrke_af);
}
if (KENTRY_NETWORK(key)) {
pfr_prepare_network(&mask, key->pfrke_af, key->pfrke_net);
ke = (struct pfr_kentry *)rn_lookup(&key->pfrke_sa, &mask,
head);
} else {
ke = (struct pfr_kentry *)rn_match(&key->pfrke_sa, head);
if (exact && ke && KENTRY_NETWORK(ke))
ke = NULL;
}
return (ke);
}
struct pfr_kentry *
pfr_create_kentry(struct pfr_addr *ad)
{
struct pfr_kentry_all *ke;
if (ad->pfra_type >= PFRKE_MAX)
panic("unknown pfra_type %d", ad->pfra_type);
ke = pool_get(&pfr_kentry_pl[ad->pfra_type], PR_NOWAIT | PR_ZERO);
if (ke == NULL)
return (NULL);
ke->pfrke_type = ad->pfra_type;
/* set weight allowing implicit weights */
if (ad->pfra_weight == 0)
ad->pfra_weight = 1;
switch (ke->pfrke_type) {
case PFRKE_PLAIN:
break;
case PFRKE_COST:
((struct pfr_kentry_cost *)ke)->weight = ad->pfra_weight;
/* FALLTHROUGH */
case PFRKE_ROUTE:
if (ad->pfra_ifname[0])
ke->pfrke_rkif = pfi_kif_get(ad->pfra_ifname, NULL);
if (ke->pfrke_rkif)
pfi_kif_ref(ke->pfrke_rkif, PFI_KIF_REF_ROUTE);
break;
}
switch (ad->pfra_af) {
case AF_INET:
FILLIN_SIN(ke->pfrke_sa.sin, ad->pfra_ip4addr);
break;
#ifdef INET6
case AF_INET6:
FILLIN_SIN6(ke->pfrke_sa.sin6, ad->pfra_ip6addr);
break;
#endif /* INET6 */
default:
unhandled_af(ad->pfra_af);
}
ke->pfrke_af = ad->pfra_af;
ke->pfrke_net = ad->pfra_net;
if (ad->pfra_not)
ke->pfrke_flags |= PFRKE_FLAG_NOT;
return ((struct pfr_kentry *)ke);
}
struct pfr_kentry *
pfr_create_kentry_unlocked(struct pfr_addr *ad, int flags)
{
struct pfr_kentry_all *ke;
int mflags = PR_ZERO;
if (ad->pfra_type >= PFRKE_MAX)
panic("unknown pfra_type %d", ad->pfra_type);
if (flags & PFR_FLAG_USERIOCTL)
mflags |= PR_WAITOK;
else
mflags |= PR_NOWAIT;
ke = pool_get(&pfr_kentry_pl[ad->pfra_type], mflags);
if (ke == NULL)
return (NULL);
ke->pfrke_type = ad->pfra_type;
/* set weight allowing implicit weights */
if (ad->pfra_weight == 0)
ad->pfra_weight = 1;
switch (ke->pfrke_type) {
case PFRKE_PLAIN:
break;
case PFRKE_COST:
((struct pfr_kentry_cost *)ke)->weight = ad->pfra_weight;
/* FALLTHROUGH */
case PFRKE_ROUTE:
if (ad->pfra_ifname[0])
(void) strlcpy(ke->pfrke_rifname, ad->pfra_ifname,
IFNAMSIZ);
break;
}
switch (ad->pfra_af) {
case AF_INET:
FILLIN_SIN(ke->pfrke_sa.sin, ad->pfra_ip4addr);
break;
#ifdef INET6
case AF_INET6:
FILLIN_SIN6(ke->pfrke_sa.sin6, ad->pfra_ip6addr);
break;
#endif /* INET6 */
default:
unhandled_af(ad->pfra_af);
}
ke->pfrke_af = ad->pfra_af;
ke->pfrke_net = ad->pfra_net;
if (ad->pfra_not)
ke->pfrke_flags |= PFRKE_FLAG_NOT;
return ((struct pfr_kentry *)ke);
}
void
pfr_kentry_kif_ref(struct pfr_kentry *ke_all)
{
struct pfr_kentry_all *ke = (struct pfr_kentry_all *)ke_all;
NET_ASSERT_LOCKED();
switch (ke->pfrke_type) {
case PFRKE_PLAIN:
break;
case PFRKE_COST:
case PFRKE_ROUTE:
if (ke->pfrke_rifname[0])
ke->pfrke_rkif = pfi_kif_get(ke->pfrke_rifname, NULL);
if (ke->pfrke_rkif)
pfi_kif_ref(ke->pfrke_rkif, PFI_KIF_REF_ROUTE);
break;
}
}
void
pfr_destroy_kentries(struct pfr_kentryworkq *workq)
{
struct pfr_kentry *p;
while ((p = SLIST_FIRST(workq)) != NULL) {
YIELD(1);
SLIST_REMOVE_HEAD(workq, pfrke_workq);
pfr_destroy_kentry(p);
}
}
void
pfr_destroy_ioq(struct pfr_kentryworkq *ioq, int flags)
{
struct pfr_kentry *p;
while ((p = SLIST_FIRST(ioq)) != NULL) {
YIELD(flags & PFR_FLAG_USERIOCTL);
SLIST_REMOVE_HEAD(ioq, pfrke_ioq);
/*
* we destroy only those entries, which did not make it to
* table
*/
if ((p->pfrke_fb != PFR_FB_ADDED) || (flags & PFR_FLAG_DUMMY))
pfr_destroy_kentry(p);
}
}
void
pfr_destroy_kentry(struct pfr_kentry *ke)
{
if (ke->pfrke_counters)
pool_put(&pfr_kcounters_pl, ke->pfrke_counters);
if (ke->pfrke_type == PFRKE_COST || ke->pfrke_type == PFRKE_ROUTE)
pfi_kif_unref(((struct pfr_kentry_all *)ke)->pfrke_rkif,
PFI_KIF_REF_ROUTE);
pool_put(&pfr_kentry_pl[ke->pfrke_type], ke);
}
void
pfr_insert_kentries(struct pfr_ktable *kt,
struct pfr_kentryworkq *workq, time_t tzero)
{
struct pfr_kentry *p;
int rv, n = 0;
SLIST_FOREACH(p, workq, pfrke_workq) {
rv = pfr_route_kentry(kt, p);
if (rv) {
DPFPRINTF(LOG_ERR,
"pfr_insert_kentries: cannot route entry "
"(code=%d).", rv);
break;
}
p->pfrke_tzero = tzero;
++n;
if (p->pfrke_type == PFRKE_COST)
kt->pfrkt_refcntcost++;
pfr_ktable_winfo_update(kt, p);
YIELD(1);
}
kt->pfrkt_cnt += n;
}
int
pfr_insert_kentry(struct pfr_ktable *kt, struct pfr_addr *ad, time_t tzero)
{
struct pfr_kentry *p;
int rv;
p = pfr_lookup_addr(kt, ad, 1);
if (p != NULL)
return (0);
p = pfr_create_kentry(ad);
if (p == NULL)
return (EINVAL);
rv = pfr_route_kentry(kt, p);
if (rv)
return (rv);
p->pfrke_tzero = tzero;
if (p->pfrke_type == PFRKE_COST)
kt->pfrkt_refcntcost++;
kt->pfrkt_cnt++;
pfr_ktable_winfo_update(kt, p);
return (0);
}
void
pfr_remove_kentries(struct pfr_ktable *kt,
struct pfr_kentryworkq *workq)
{
struct pfr_kentry *p;
struct pfr_kentryworkq addrq;
int n = 0;
SLIST_FOREACH(p, workq, pfrke_workq) {
pfr_unroute_kentry(kt, p);
++n;
YIELD(1);
if (p->pfrke_type == PFRKE_COST)
kt->pfrkt_refcntcost--;
}
kt->pfrkt_cnt -= n;
pfr_destroy_kentries(workq);
/* update maxweight and gcd for load balancing */
if (kt->pfrkt_refcntcost > 0) {
kt->pfrkt_gcdweight = 0;
kt->pfrkt_maxweight = 1;
pfr_enqueue_addrs(kt, &addrq, NULL, 0);
SLIST_FOREACH(p, &addrq, pfrke_workq)
pfr_ktable_winfo_update(kt, p);
}
}
void
pfr_clean_node_mask(struct pfr_ktable *kt,
struct pfr_kentryworkq *workq)
{
struct pfr_kentry *p;
SLIST_FOREACH(p, workq, pfrke_workq) {
pfr_unroute_kentry(kt, p);
}
}
void
pfr_clstats_kentries(struct pfr_kentryworkq *workq, time_t tzero, int negchange)
{
struct pfr_kentry *p;
SLIST_FOREACH(p, workq, pfrke_workq) {
if (negchange)
p->pfrke_flags ^= PFRKE_FLAG_NOT;
if (p->pfrke_counters) {
pool_put(&pfr_kcounters_pl, p->pfrke_counters);
p->pfrke_counters = NULL;
}
p->pfrke_tzero = tzero;
}
}
void
pfr_reset_feedback(struct pfr_addr *addr, int size, int flags)
{
struct pfr_addr ad;
int i;
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
break;
ad.pfra_fback = PFR_FB_NONE;
if (COPYOUT(&ad, addr+i, sizeof(ad), flags))
break;
}
}
void
pfr_prepare_network(union sockaddr_union *sa, int af, int net)
{
#ifdef INET6
int i;
#endif /* INET6 */
bzero(sa, sizeof(*sa));
switch (af) {
case AF_INET:
sa->sin.sin_len = sizeof(sa->sin);
sa->sin.sin_family = AF_INET;
sa->sin.sin_addr.s_addr = net ? htonl(-1 << (32-net)) : 0;
break;
#ifdef INET6
case AF_INET6:
sa->sin6.sin6_len = sizeof(sa->sin6);
sa->sin6.sin6_family = AF_INET6;
for (i = 0; i < 4; i++) {
if (net <= 32) {
sa->sin6.sin6_addr.s6_addr32[i] =
net ? htonl(-1 << (32-net)) : 0;
break;
}
sa->sin6.sin6_addr.s6_addr32[i] = 0xFFFFFFFF;
net -= 32;
}
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
}
int
pfr_route_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke)
{
union sockaddr_union mask;
struct radix_node *rn;
struct radix_node_head *head;
bzero(ke->pfrke_node, sizeof(ke->pfrke_node));
switch (ke->pfrke_af) {
case AF_INET:
head = kt->pfrkt_ip4;
break;
#ifdef INET6
case AF_INET6:
head = kt->pfrkt_ip6;
break;
#endif /* INET6 */
default:
unhandled_af(ke->pfrke_af);
}
if (KENTRY_NETWORK(ke)) {
pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net);
rn = rn_addroute(&ke->pfrke_sa, &mask, head, ke->pfrke_node, 0);
} else
rn = rn_addroute(&ke->pfrke_sa, NULL, head, ke->pfrke_node, 0);
return (rn == NULL ? -1 : 0);
}
int
pfr_unroute_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke)
{
union sockaddr_union mask;
struct radix_node *rn;
struct radix_node_head *head;
switch (ke->pfrke_af) {
case AF_INET:
head = kt->pfrkt_ip4;
break;
#ifdef INET6
case AF_INET6:
head = kt->pfrkt_ip6;
break;
#endif /* INET6 */
default:
unhandled_af(ke->pfrke_af);
}
if (KENTRY_NETWORK(ke)) {
pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net);
rn = rn_delete(&ke->pfrke_sa, &mask, head, NULL);
} else
rn = rn_delete(&ke->pfrke_sa, NULL, head, NULL);
if (rn == NULL) {
DPFPRINTF(LOG_ERR, "pfr_unroute_kentry: delete failed.\n");
return (-1);
}
return (0);
}
void
pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke)
{
bzero(ad, sizeof(*ad));
if (ke == NULL)
return;
ad->pfra_af = ke->pfrke_af;
ad->pfra_net = ke->pfrke_net;
ad->pfra_type = ke->pfrke_type;
if (ke->pfrke_flags & PFRKE_FLAG_NOT)
ad->pfra_not = 1;
switch (ad->pfra_af) {
case AF_INET:
ad->pfra_ip4addr = ke->pfrke_sa.sin.sin_addr;
break;
#ifdef INET6
case AF_INET6:
ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr;
break;
#endif /* INET6 */
default:
unhandled_af(ad->pfra_af);
}
if (ke->pfrke_counters != NULL)
ad->pfra_states = ke->pfrke_counters->states;
switch (ke->pfrke_type) {
case PFRKE_COST:
ad->pfra_weight = ((struct pfr_kentry_cost *)ke)->weight;
/* FALLTHROUGH */
case PFRKE_ROUTE:
if (((struct pfr_kentry_route *)ke)->kif != NULL)
strlcpy(ad->pfra_ifname,
((struct pfr_kentry_route *)ke)->kif->pfik_name,
IFNAMSIZ);
break;
default:
break;
}
}
int
pfr_walktree(struct radix_node *rn, void *arg, u_int id)
{
struct pfr_kentry *ke = (struct pfr_kentry *)rn;
struct pfr_walktree *w = arg;
union sockaddr_union mask;
int flags = w->pfrw_flags;
switch (w->pfrw_op) {
case PFRW_MARK:
ke->pfrke_flags &= ~PFRKE_FLAG_MARK;
break;
case PFRW_SWEEP:
if (ke->pfrke_flags & PFRKE_FLAG_MARK)
break;
/* FALLTHROUGH */
case PFRW_ENQUEUE:
SLIST_INSERT_HEAD(w->pfrw_workq, ke, pfrke_workq);
w->pfrw_cnt++;
break;
case PFRW_GET_ADDRS:
if (w->pfrw_free-- > 0) {
struct pfr_addr ad;
pfr_copyout_addr(&ad, ke);
if (copyout(&ad, w->pfrw_addr, sizeof(ad)))
return (EFAULT);
w->pfrw_addr++;
}
break;
case PFRW_GET_ASTATS:
if (w->pfrw_free-- > 0) {
struct pfr_astats as;
pfr_copyout_addr(&as.pfras_a, ke);
if (ke->pfrke_counters) {
bcopy(ke->pfrke_counters->pfrkc_packets,
as.pfras_packets, sizeof(as.pfras_packets));
bcopy(ke->pfrke_counters->pfrkc_bytes,
as.pfras_bytes, sizeof(as.pfras_bytes));
} else {
bzero(as.pfras_packets,
sizeof(as.pfras_packets));
bzero(as.pfras_bytes, sizeof(as.pfras_bytes));
as.pfras_a.pfra_fback = PFR_FB_NOCOUNT;
}
as.pfras_tzero = ke->pfrke_tzero;
if (COPYOUT(&as, w->pfrw_astats, sizeof(as), flags))
return (EFAULT);
w->pfrw_astats++;
}
break;
case PFRW_POOL_GET:
if (ke->pfrke_flags & PFRKE_FLAG_NOT)
break; /* negative entries are ignored */
if (!w->pfrw_cnt--) {
w->pfrw_kentry = ke;
return (1); /* finish search */
}
break;
case PFRW_DYNADDR_UPDATE:
switch (ke->pfrke_af) {
case AF_INET:
if (w->pfrw_dyn->pfid_acnt4++ > 0)
break;
pfr_prepare_network(&mask, AF_INET, ke->pfrke_net);
w->pfrw_dyn->pfid_addr4 = *SUNION2PF(
&ke->pfrke_sa, AF_INET);
w->pfrw_dyn->pfid_mask4 = *SUNION2PF(
&mask, AF_INET);
break;
#ifdef INET6
case AF_INET6:
if (w->pfrw_dyn->pfid_acnt6++ > 0)
break;
pfr_prepare_network(&mask, AF_INET6, ke->pfrke_net);
w->pfrw_dyn->pfid_addr6 = *SUNION2PF(
&ke->pfrke_sa, AF_INET6);
w->pfrw_dyn->pfid_mask6 = *SUNION2PF(
&mask, AF_INET6);
break;
#endif /* INET6 */
default:
unhandled_af(ke->pfrke_af);
}
break;
}
return (0);
}
int
pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
{
struct pfr_ktableworkq workq;
struct pfr_ktable *p;
int xdel = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ALLRSETS);
if (pfr_fix_anchor(filter->pfrt_anchor))
return (EINVAL);
if (pfr_table_count(filter, flags) < 0)
return (ENOENT);
SLIST_INIT(&workq);
RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
if (pfr_skip_table(filter, p, flags))
continue;
if (!strcmp(p->pfrkt_anchor, PF_RESERVED_ANCHOR))
continue;
if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE))
continue;
p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE;
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
xdel++;
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_setflags_ktables(&workq);
}
if (ndel != NULL)
*ndel = xdel;
return (0);
}
int
pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
{
struct pfr_ktableworkq addq, changeq, auxq;
struct pfr_ktable *p, *q, *r, *n, *w, key;
int i, rv, xadd = 0;
time_t tzero = gettime();
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
SLIST_INIT(&addq);
SLIST_INIT(&changeq);
SLIST_INIT(&auxq);
/* pre-allocate all memory outside of locks */
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags))
senderr(EFAULT);
if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK,
flags & PFR_FLAG_USERIOCTL))
senderr(EINVAL);
key.pfrkt_flags |= PFR_TFLAG_ACTIVE;
p = pfr_create_ktable(&key.pfrkt_t, tzero, 0,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (p == NULL)
senderr(ENOMEM);
/*
* Note: we also pre-allocate a root table here. We keep it
* at ->pfrkt_root, which we must not forget about.
*/
key.pfrkt_flags = 0;
memset(key.pfrkt_anchor, 0, sizeof(key.pfrkt_anchor));
p->pfrkt_root = pfr_create_ktable(&key.pfrkt_t, 0, 0,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (p->pfrkt_root == NULL) {
pfr_destroy_ktable(p, 0);
senderr(ENOMEM);
}
SLIST_FOREACH(q, &auxq, pfrkt_workq) {
if (!pfr_ktable_compare(p, q)) {
/*
* We need no lock here, because `p` is empty,
* there are no rules or shadow tables
* attached.
*/
pfr_destroy_ktable(p->pfrkt_root, 0);
p->pfrkt_root = NULL;
pfr_destroy_ktable(p, 0);
p = NULL;
break;
}
}
if (q != NULL)
continue;
SLIST_INSERT_HEAD(&auxq, p, pfrkt_workq);
}
/*
* auxq contains freshly allocated tables with no dups.
* also note there are no rulesets attached, because
* the attach operation requires PF_LOCK().
*/
NET_LOCK();
PF_LOCK();
SLIST_FOREACH_SAFE(n, &auxq, pfrkt_workq, w) {
p = RB_FIND(pfr_ktablehead, &pfr_ktables, n);
if (p == NULL) {
SLIST_REMOVE(&auxq, n, pfr_ktable, pfrkt_workq);
SLIST_INSERT_HEAD(&addq, n, pfrkt_workq);
xadd++;
} else if (!(flags & PFR_FLAG_DUMMY) &&
!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) {
p->pfrkt_nflags = (p->pfrkt_flags &
~PFR_TFLAG_USRMASK) | key.pfrkt_flags;
SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq);
}
}
if (!(flags & PFR_FLAG_DUMMY)) {
/*
* addq contains tables we have to insert and attach rules to
* them
*
* changeq contains tables we need to update
*
* auxq contains pre-allocated tables, we won't use and we must
* free them
*/
SLIST_FOREACH_SAFE(p, &addq, pfrkt_workq, w) {
p->pfrkt_rs = pf_find_or_create_ruleset(
p->pfrkt_anchor);
if (p->pfrkt_rs == NULL) {
xadd--;
SLIST_REMOVE(&addq, p, pfr_ktable, pfrkt_workq);
SLIST_INSERT_HEAD(&auxq, p, pfrkt_workq);
continue;
}
p->pfrkt_rs->tables++;
if (!p->pfrkt_anchor[0]) {
q = p->pfrkt_root;
p->pfrkt_root = NULL;
SLIST_INSERT_HEAD(&auxq, q, pfrkt_workq);
continue;
}
/* use pre-allocated root table as a key */
q = p->pfrkt_root;
p->pfrkt_root = NULL;
r = RB_FIND(pfr_ktablehead, &pfr_ktables, q);
if (r != NULL) {
p->pfrkt_root = r;
SLIST_INSERT_HEAD(&auxq, q, pfrkt_workq);
continue;
}
/*
* there is a chance we could create root table in
* earlier iteration. such table may exist in addq only
* then.
*/
SLIST_FOREACH(r, &addq, pfrkt_workq) {
if (!pfr_ktable_compare(r, q)) {
/*
* `r` is our root table we've found
* earlier, `q` can get dropped.
*/
p->pfrkt_root = r;
SLIST_INSERT_HEAD(&auxq, q,
pfrkt_workq);
break;
}
}
if (r != NULL)
continue;
q->pfrkt_rs = pf_find_or_create_ruleset(q->pfrkt_anchor);
/*
* root tables are attached to main ruleset,
* because ->pfrkt_anchor[0] == '\0'
*/
KASSERT(q->pfrkt_rs == &pf_main_ruleset);
q->pfrkt_rs->tables++;
p->pfrkt_root = q;
SLIST_INSERT_HEAD(&addq, q, pfrkt_workq);
}
pfr_insert_ktables(&addq);
pfr_setflags_ktables(&changeq);
}
PF_UNLOCK();
NET_UNLOCK();
pfr_destroy_ktables_aux(&auxq);
if (flags & PFR_FLAG_DUMMY)
pfr_destroy_ktables_aux(&addq);
if (nadd != NULL)
*nadd = xadd;
return (0);
_bad:
pfr_destroy_ktables_aux(&auxq);
return (rv);
}
int
pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
{
struct pfr_ktableworkq workq;
struct pfr_ktable *p, *q, key;
int i, xdel = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
SLIST_INIT(&workq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags))
return (EFAULT);
if (pfr_validate_table(&key.pfrkt_t, 0,
flags & PFR_FLAG_USERIOCTL))
return (EINVAL);
p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key);
if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) {
SLIST_FOREACH(q, &workq, pfrkt_workq)
if (!pfr_ktable_compare(p, q))
goto _skip;
p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE;
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
xdel++;
}
_skip:
;
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_setflags_ktables(&workq);
}
if (ndel != NULL)
*ndel = xdel;
return (0);
}
int
pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
int flags)
{
struct pfr_ktable *p;
int n, nn;
ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS);
if (pfr_fix_anchor(filter->pfrt_anchor))
return (EINVAL);
n = nn = pfr_table_count(filter, flags);
if (n < 0)
return (ENOENT);
if (n > *size) {
*size = n;
return (0);
}
RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
if (pfr_skip_table(filter, p, flags))
continue;
if (n-- <= 0)
continue;
if (COPYOUT(&p->pfrkt_t, tbl++, sizeof(*tbl), flags))
return (EFAULT);
}
if (n) {
DPFPRINTF(LOG_ERR,
"pfr_get_tables: corruption detected (%d).", n);
return (ENOTTY);
}
*size = nn;
return (0);
}
int
pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
int flags)
{
struct pfr_ktable *p;
struct pfr_ktableworkq workq;
int n, nn;
time_t tzero = gettime();
/* XXX PFR_FLAG_CLSTATS disabled */
ACCEPT_FLAGS(flags, PFR_FLAG_ALLRSETS);
if (pfr_fix_anchor(filter->pfrt_anchor))
return (EINVAL);
n = nn = pfr_table_count(filter, flags);
if (n < 0)
return (ENOENT);
if (n > *size) {
*size = n;
return (0);
}
SLIST_INIT(&workq);
RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
if (pfr_skip_table(filter, p, flags))
continue;
if (n-- <= 0)
continue;
if (COPYOUT(&p->pfrkt_ts, tbl++, sizeof(*tbl), flags))
return (EFAULT);
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
}
if (flags & PFR_FLAG_CLSTATS)
pfr_clstats_ktables(&workq, tzero,
flags & PFR_FLAG_ADDRSTOO);
if (n) {
DPFPRINTF(LOG_ERR,
"pfr_get_tstats: corruption detected (%d).", n);
return (ENOTTY);
}
*size = nn;
return (0);
}
int
pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
{
struct pfr_ktableworkq workq;
struct pfr_ktable *p, key;
int i, xzero = 0;
time_t tzero = gettime();
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO);
SLIST_INIT(&workq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags))
return (EFAULT);
if (pfr_validate_table(&key.pfrkt_t, 0, 0))
return (EINVAL);
p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key);
if (p != NULL) {
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
xzero++;
}
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_clstats_ktables(&workq, tzero, flags & PFR_FLAG_ADDRSTOO);
}
if (nzero != NULL)
*nzero = xzero;
return (0);
}
int
pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag,
int *nchange, int *ndel, int flags)
{
struct pfr_ktableworkq workq;
struct pfr_ktable *p, *q, key;
int i, xchange = 0, xdel = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
if ((setflag & ~PFR_TFLAG_USRMASK) ||
(clrflag & ~PFR_TFLAG_USRMASK) ||
(setflag & clrflag))
return (EINVAL);
SLIST_INIT(&workq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), flags))
return (EFAULT);
if (pfr_validate_table(&key.pfrkt_t, 0,
flags & PFR_FLAG_USERIOCTL))
return (EINVAL);
p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key);
if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) {
p->pfrkt_nflags = (p->pfrkt_flags | setflag) &
~clrflag;
if (p->pfrkt_nflags == p->pfrkt_flags)
goto _skip;
SLIST_FOREACH(q, &workq, pfrkt_workq)
if (!pfr_ktable_compare(p, q))
goto _skip;
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
if ((p->pfrkt_flags & PFR_TFLAG_PERSIST) &&
(clrflag & PFR_TFLAG_PERSIST) &&
!(p->pfrkt_flags & PFR_TFLAG_REFERENCED))
xdel++;
else
xchange++;
}
_skip:
;
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_setflags_ktables(&workq);
}
if (nchange != NULL)
*nchange = xchange;
if (ndel != NULL)
*ndel = xdel;
return (0);
}
int
pfr_ina_begin(struct pfr_table *trs, u_int32_t *ticket, int *ndel, int flags)
{
struct pfr_ktableworkq workq;
struct pfr_ktable *p;
struct pf_ruleset *rs;
int xdel = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
rs = pf_find_or_create_ruleset(trs->pfrt_anchor);
if (rs == NULL)
return (ENOMEM);
SLIST_INIT(&workq);
RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) ||
pfr_skip_table(trs, p, 0))
continue;
p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE;
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
xdel++;
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_setflags_ktables(&workq);
if (ticket != NULL)
*ticket = ++rs->tticket;
rs->topen = 1;
} else
pf_remove_if_empty_ruleset(rs);
if (ndel != NULL)
*ndel = xdel;
return (0);
}
int
pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nadd, int *naddr, u_int32_t ticket, int flags)
{
struct pfr_ktableworkq tableq;
struct pfr_kentryworkq addrq;
struct pfr_ktable *kt, *rt, *shadow, key;
struct pfr_kentry *p;
struct pfr_addr ad;
struct pf_ruleset *rs;
int i, rv, xadd = 0, xaddr = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_ADDRSTOO);
if (size && !(flags & PFR_FLAG_ADDRSTOO))
return (EINVAL);
if (pfr_validate_table(tbl, PFR_TFLAG_USRMASK,
flags & PFR_FLAG_USERIOCTL))
return (EINVAL);
rs = pf_find_ruleset(tbl->pfrt_anchor);
if (rs == NULL || !rs->topen || ticket != rs->tticket)
return (EBUSY);
tbl->pfrt_flags |= PFR_TFLAG_INACTIVE;
SLIST_INIT(&tableq);
kt = RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl);
if (kt == NULL) {
kt = pfr_create_ktable(tbl, 0, 1,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (kt == NULL)
return (ENOMEM);
SLIST_INSERT_HEAD(&tableq, kt, pfrkt_workq);
xadd++;
if (!tbl->pfrt_anchor[0])
goto _skip;
/* find or create root table */
bzero(&key, sizeof(key));
strlcpy(key.pfrkt_name, tbl->pfrt_name, sizeof(key.pfrkt_name));
rt = RB_FIND(pfr_ktablehead, &pfr_ktables, &key);
if (rt != NULL) {
kt->pfrkt_root = rt;
goto _skip;
}
rt = pfr_create_ktable(&key.pfrkt_t, 0, 1,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (rt == NULL) {
pfr_destroy_ktables(&tableq, 0);
return (ENOMEM);
}
SLIST_INSERT_HEAD(&tableq, rt, pfrkt_workq);
kt->pfrkt_root = rt;
} else if (!(kt->pfrkt_flags & PFR_TFLAG_INACTIVE))
xadd++;
_skip:
shadow = pfr_create_ktable(tbl, 0, 0,
(flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
if (shadow == NULL) {
pfr_destroy_ktables(&tableq, 0);
return (ENOMEM);
}
SLIST_INIT(&addrq);
for (i = 0; i < size; i++) {
YIELD(flags & PFR_FLAG_USERIOCTL);
if (COPYIN(addr+i, &ad, sizeof(ad), flags))
senderr(EFAULT);
if (pfr_validate_addr(&ad))
senderr(EINVAL);
if (pfr_lookup_addr(shadow, &ad, 1) != NULL)
continue;
p = pfr_create_kentry(&ad);
if (p == NULL)
senderr(ENOMEM);
if (pfr_route_kentry(shadow, p)) {
pfr_destroy_kentry(p);
continue;
}
SLIST_INSERT_HEAD(&addrq, p, pfrke_workq);
xaddr++;
if (p->pfrke_type == PFRKE_COST)
kt->pfrkt_refcntcost++;
pfr_ktable_winfo_update(kt, p);
}
if (!(flags & PFR_FLAG_DUMMY)) {
if (kt->pfrkt_shadow != NULL)
pfr_destroy_ktable(kt->pfrkt_shadow, 1);
kt->pfrkt_flags |= PFR_TFLAG_INACTIVE;
pfr_insert_ktables(&tableq);
shadow->pfrkt_cnt = (flags & PFR_FLAG_ADDRSTOO) ?
xaddr : NO_ADDRESSES;
kt->pfrkt_shadow = shadow;
} else {
pfr_clean_node_mask(shadow, &addrq);
pfr_destroy_ktable(shadow, 0);
pfr_destroy_ktables(&tableq, 0);
pfr_destroy_kentries(&addrq);
}
if (nadd != NULL)
*nadd = xadd;
if (naddr != NULL)
*naddr = xaddr;
return (0);
_bad:
pfr_destroy_ktable(shadow, 0);
pfr_destroy_ktables(&tableq, 0);
pfr_destroy_kentries(&addrq);
return (rv);
}
int
pfr_ina_rollback(struct pfr_table *trs, u_int32_t ticket, int *ndel, int flags)
{
struct pfr_ktableworkq workq;
struct pfr_ktable *p;
struct pf_ruleset *rs;
int xdel = 0;
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
rs = pf_find_ruleset(trs->pfrt_anchor);
if (rs == NULL || !rs->topen || ticket != rs->tticket)
return (0);
SLIST_INIT(&workq);
RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) ||
pfr_skip_table(trs, p, 0))
continue;
p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE;
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
xdel++;
}
if (!(flags & PFR_FLAG_DUMMY)) {
pfr_setflags_ktables(&workq);
rs->topen = 0;
pf_remove_if_empty_ruleset(rs);
}
if (ndel != NULL)
*ndel = xdel;
return (0);
}
int
pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd,
int *nchange, int flags)
{
struct pfr_ktable *p, *q;
struct pfr_ktableworkq workq;
struct pf_ruleset *rs;
int xadd = 0, xchange = 0;
time_t tzero = gettime();
ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY);
rs = pf_find_ruleset(trs->pfrt_anchor);
if (rs == NULL || !rs->topen || ticket != rs->tticket)
return (EBUSY);
SLIST_INIT(&workq);
RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) {
if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) ||
pfr_skip_table(trs, p, 0))
continue;
SLIST_INSERT_HEAD(&workq, p, pfrkt_workq);
if (p->pfrkt_flags & PFR_TFLAG_ACTIVE)
xchange++;
else
xadd++;
}
if (!(flags & PFR_FLAG_DUMMY)) {
SLIST_FOREACH_SAFE(p, &workq, pfrkt_workq, q) {
pfr_commit_ktable(p, tzero);
}
rs->topen = 0;
pf_remove_if_empty_ruleset(rs);
}
if (nadd != NULL)
*nadd = xadd;
if (nchange != NULL)
*nchange = xchange;
return (0);
}
void
pfr_commit_ktable(struct pfr_ktable *kt, time_t tzero)
{
struct pfr_ktable *shadow = kt->pfrkt_shadow;
int nflags;
if (shadow->pfrkt_cnt == NO_ADDRESSES) {
if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
pfr_clstats_ktable(kt, tzero, 1);
} else if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) {
/* kt might contain addresses */
struct pfr_kentryworkq addrq, addq, changeq, delq, garbageq;
struct pfr_kentry *p, *q;
struct pfr_addr ad;
pfr_enqueue_addrs(shadow, &addrq, NULL, 0);
pfr_mark_addrs(kt);
SLIST_INIT(&addq);
SLIST_INIT(&changeq);
SLIST_INIT(&delq);
SLIST_INIT(&garbageq);
pfr_clean_node_mask(shadow, &addrq);
while ((p = SLIST_FIRST(&addrq)) != NULL) {
SLIST_REMOVE_HEAD(&addrq, pfrke_workq);
pfr_copyout_addr(&ad, p);
q = pfr_lookup_addr(kt, &ad, 1);
if (q != NULL) {
if ((q->pfrke_flags & PFRKE_FLAG_NOT) !=
(p->pfrke_flags & PFRKE_FLAG_NOT))
SLIST_INSERT_HEAD(&changeq, q,
pfrke_workq);
q->pfrke_flags |= PFRKE_FLAG_MARK;
SLIST_INSERT_HEAD(&garbageq, p, pfrke_workq);
} else {
p->pfrke_tzero = tzero;
SLIST_INSERT_HEAD(&addq, p, pfrke_workq);
}
}
pfr_enqueue_addrs(kt, &delq, NULL, ENQUEUE_UNMARKED_ONLY);
pfr_insert_kentries(kt, &addq, tzero);
pfr_remove_kentries(kt, &delq);
pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG);
pfr_destroy_kentries(&garbageq);
} else {
/* kt cannot contain addresses */
SWAP(struct radix_node_head *, kt->pfrkt_ip4,
shadow->pfrkt_ip4);
SWAP(struct radix_node_head *, kt->pfrkt_ip6,
shadow->pfrkt_ip6);
SWAP(int, kt->pfrkt_cnt, shadow->pfrkt_cnt);
pfr_clstats_ktable(kt, tzero, 1);
}
nflags = ((shadow->pfrkt_flags & PFR_TFLAG_USRMASK) |
(kt->pfrkt_flags & PFR_TFLAG_SETMASK) | PFR_TFLAG_ACTIVE)
& ~PFR_TFLAG_INACTIVE;
pfr_destroy_ktable(shadow, 0);
kt->pfrkt_shadow = NULL;
pfr_setflags_ktable(kt, nflags);
}
int
pfr_validate_table(struct pfr_table *tbl, int allowedflags, int no_reserved)
{
int i;
if (!tbl->pfrt_name[0])
return (-1);
if (no_reserved && !strcmp(tbl->pfrt_anchor, PF_RESERVED_ANCHOR))
return (-1);
if (tbl->pfrt_name[PF_TABLE_NAME_SIZE-1])
return (-1);
for (i = strlen(tbl->pfrt_name); i < PF_TABLE_NAME_SIZE; i++)
if (tbl->pfrt_name[i])
return (-1);
if (pfr_fix_anchor(tbl->pfrt_anchor))
return (-1);
if (tbl->pfrt_flags & ~allowedflags)
return (-1);
return (0);
}
/*
* Rewrite anchors referenced by tables to remove slashes
* and check for validity.
*/
int
pfr_fix_anchor(char *anchor)
{
size_t siz = MAXPATHLEN;
int i;
if (anchor[0] == '/') {
char *path;
int off;
path = anchor;
off = 1;
while (*++path == '/')
off++;
bcopy(path, anchor, siz - off);
memset(anchor + siz - off, 0, off);
}
if (anchor[siz - 1])
return (-1);
for (i = strlen(anchor); i < siz; i++)
if (anchor[i])
return (-1);
return (0);
}
int
pfr_table_count(struct pfr_table *filter, int flags)
{
struct pf_ruleset *rs;
if (flags & PFR_FLAG_ALLRSETS)
return (pfr_ktable_cnt);
if (filter->pfrt_anchor[0]) {
rs = pf_find_ruleset(filter->pfrt_anchor);
return ((rs != NULL) ? rs->tables : -1);
}
return (pf_main_ruleset.tables);
}
int
pfr_skip_table(struct pfr_table *filter, struct pfr_ktable *kt, int flags)
{
if (flags & PFR_FLAG_ALLRSETS)
return (0);
if (strcmp(filter->pfrt_anchor, kt->pfrkt_anchor))
return (1);
return (0);
}
void
pfr_insert_ktables(struct pfr_ktableworkq *workq)
{
struct pfr_ktable *p;
SLIST_FOREACH(p, workq, pfrkt_workq)
pfr_insert_ktable(p);
}
void
pfr_insert_ktable(struct pfr_ktable *kt)
{
RB_INSERT(pfr_ktablehead, &pfr_ktables, kt);
pfr_ktable_cnt++;
if (kt->pfrkt_root != NULL)
if (!kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]++)
pfr_setflags_ktable(kt->pfrkt_root,
kt->pfrkt_root->pfrkt_flags|PFR_TFLAG_REFDANCHOR);
}
void
pfr_setflags_ktables(struct pfr_ktableworkq *workq)
{
struct pfr_ktable *p, *q;
SLIST_FOREACH_SAFE(p, workq, pfrkt_workq, q) {
pfr_setflags_ktable(p, p->pfrkt_nflags);
}
}
void
pfr_setflags_ktable(struct pfr_ktable *kt, int newf)
{
struct pfr_kentryworkq addrq;
if (!(newf & PFR_TFLAG_REFERENCED) &&
!(newf & PFR_TFLAG_REFDANCHOR) &&
!(newf & PFR_TFLAG_PERSIST))
newf &= ~PFR_TFLAG_ACTIVE;
if (!(newf & PFR_TFLAG_ACTIVE))
newf &= ~PFR_TFLAG_USRMASK;
if (!(newf & PFR_TFLAG_SETMASK)) {
RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt);
if (kt->pfrkt_root != NULL)
if (!--kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR])
pfr_setflags_ktable(kt->pfrkt_root,
kt->pfrkt_root->pfrkt_flags &
~PFR_TFLAG_REFDANCHOR);
pfr_destroy_ktable(kt, 1);
pfr_ktable_cnt--;
return;
}
if (!(newf & PFR_TFLAG_ACTIVE) && kt->pfrkt_cnt) {
pfr_enqueue_addrs(kt, &addrq, NULL, 0);
pfr_remove_kentries(kt, &addrq);
}
if (!(newf & PFR_TFLAG_INACTIVE) && kt->pfrkt_shadow != NULL) {
pfr_destroy_ktable(kt->pfrkt_shadow, 1);
kt->pfrkt_shadow = NULL;
}
kt->pfrkt_flags = newf;
}
void
pfr_clstats_ktables(struct pfr_ktableworkq *workq, time_t tzero, int recurse)
{
struct pfr_ktable *p;
SLIST_FOREACH(p, workq, pfrkt_workq)
pfr_clstats_ktable(p, tzero, recurse);
}
void
pfr_clstats_ktable(struct pfr_ktable *kt, time_t tzero, int recurse)
{
struct pfr_kentryworkq addrq;
if (recurse) {
pfr_enqueue_addrs(kt, &addrq, NULL, 0);
pfr_clstats_kentries(&addrq, tzero, 0);
}
bzero(kt->pfrkt_packets, sizeof(kt->pfrkt_packets));
bzero(kt->pfrkt_bytes, sizeof(kt->pfrkt_bytes));
kt->pfrkt_match = kt->pfrkt_nomatch = 0;
kt->pfrkt_tzero = tzero;
}
struct pfr_ktable *
pfr_create_ktable(struct pfr_table *tbl, time_t tzero, int attachruleset,
int wait)
{
struct pfr_ktable *kt;
struct pf_ruleset *rs;
kt = pool_get(&pfr_ktable_pl, wait|PR_ZERO|PR_LIMITFAIL);
if (kt == NULL)
return (NULL);
kt->pfrkt_t = *tbl;
if (attachruleset) {
PF_ASSERT_LOCKED();
rs = pf_find_or_create_ruleset(tbl->pfrt_anchor);
if (!rs) {
pfr_destroy_ktable(kt, 0);
return (NULL);
}
kt->pfrkt_rs = rs;
rs->tables++;
}
if (!rn_inithead((void **)&kt->pfrkt_ip4,
offsetof(struct sockaddr_in, sin_addr)) ||
!rn_inithead((void **)&kt->pfrkt_ip6,
offsetof(struct sockaddr_in6, sin6_addr))) {
pfr_destroy_ktable(kt, 0);
return (NULL);
}
kt->pfrkt_tzero = tzero;
kt->pfrkt_refcntcost = 0;
kt->pfrkt_gcdweight = 0;
kt->pfrkt_maxweight = 1;
return (kt);
}
void
pfr_destroy_ktables(struct pfr_ktableworkq *workq, int flushaddr)
{
struct pfr_ktable *p;
while ((p = SLIST_FIRST(workq)) != NULL) {
SLIST_REMOVE_HEAD(workq, pfrkt_workq);
pfr_destroy_ktable(p, flushaddr);
}
}
void
pfr_destroy_ktables_aux(struct pfr_ktableworkq *auxq)
{
struct pfr_ktable *p;
while ((p = SLIST_FIRST(auxq)) != NULL) {
SLIST_REMOVE_HEAD(auxq, pfrkt_workq);
/*
* There must be no extra data (rules, shadow tables, ...)
* attached, because auxq holds just empty memory to be
* initialized. Therefore we can also be called with no lock.
*/
if (p->pfrkt_root != NULL) {
KASSERT(p->pfrkt_root->pfrkt_rs == NULL);
KASSERT(p->pfrkt_root->pfrkt_shadow == NULL);
KASSERT(p->pfrkt_root->pfrkt_root == NULL);
pfr_destroy_ktable(p->pfrkt_root, 0);
p->pfrkt_root = NULL;
}
KASSERT(p->pfrkt_rs == NULL);
KASSERT(p->pfrkt_shadow == NULL);
pfr_destroy_ktable(p, 0);
}
}
void
pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr)
{
struct pfr_kentryworkq addrq;
if (flushaddr) {
pfr_enqueue_addrs(kt, &addrq, NULL, 0);
pfr_clean_node_mask(kt, &addrq);
pfr_destroy_kentries(&addrq);
}
if (kt->pfrkt_ip4 != NULL)
free(kt->pfrkt_ip4, M_RTABLE, sizeof(*kt->pfrkt_ip4));
if (kt->pfrkt_ip6 != NULL)
free(kt->pfrkt_ip6, M_RTABLE, sizeof(*kt->pfrkt_ip6));
if (kt->pfrkt_shadow != NULL)
pfr_destroy_ktable(kt->pfrkt_shadow, flushaddr);
if (kt->pfrkt_rs != NULL) {
kt->pfrkt_rs->tables--;
pf_remove_if_empty_ruleset(kt->pfrkt_rs);
}
pool_put(&pfr_ktable_pl, kt);
}
int
pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q)
{
int d;
if ((d = strncmp(p->pfrkt_name, q->pfrkt_name, PF_TABLE_NAME_SIZE)))
return (d);
return (strcmp(p->pfrkt_anchor, q->pfrkt_anchor));
}
struct pfr_ktable *
pfr_lookup_table(struct pfr_table *tbl)
{
/* struct pfr_ktable start like a struct pfr_table */
return (RB_FIND(pfr_ktablehead, &pfr_ktables,
(struct pfr_ktable *)tbl));
}
int
pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af)
{
struct pfr_kentry *ke = NULL;
int match;
ke = pfr_kentry_byaddr(kt, a, af, 0);
match = (ke && !(ke->pfrke_flags & PFRKE_FLAG_NOT));
if (match)
kt->pfrkt_match++;
else
kt->pfrkt_nomatch++;
return (match);
}
struct pfr_kentry *
pfr_kentry_byaddr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af,
int exact)
{
struct pfr_kentry *ke = NULL;
struct sockaddr_in tmp4;
#ifdef INET6
struct sockaddr_in6 tmp6;
#endif /* INET6 */
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return (0);
switch (af) {
case AF_INET:
bzero(&tmp4, sizeof(tmp4));
tmp4.sin_len = sizeof(tmp4);
tmp4.sin_family = AF_INET;
tmp4.sin_addr.s_addr = a->addr32[0];
ke = (struct pfr_kentry *)rn_match(&tmp4, kt->pfrkt_ip4);
break;
#ifdef INET6
case AF_INET6:
bzero(&tmp6, sizeof(tmp6));
tmp6.sin6_len = sizeof(tmp6);
tmp6.sin6_family = AF_INET6;
bcopy(a, &tmp6.sin6_addr, sizeof(tmp6.sin6_addr));
ke = (struct pfr_kentry *)rn_match(&tmp6, kt->pfrkt_ip6);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
if (exact && ke && KENTRY_NETWORK(ke))
ke = NULL;
return (ke);
}
void
pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, struct pf_pdesc *pd,
int op, int notrule)
{
struct pfr_kentry *ke = NULL;
struct sockaddr_in tmp4;
#ifdef INET6
struct sockaddr_in6 tmp6;
#endif /* INET6 */
sa_family_t af = pd->af;
u_int64_t len = pd->tot_len;
int dir_idx = (pd->dir == PF_OUT);
int op_idx;
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return;
switch (af) {
case AF_INET:
bzero(&tmp4, sizeof(tmp4));
tmp4.sin_len = sizeof(tmp4);
tmp4.sin_family = AF_INET;
tmp4.sin_addr.s_addr = a->addr32[0];
ke = (struct pfr_kentry *)rn_match(&tmp4, kt->pfrkt_ip4);
break;
#ifdef INET6
case AF_INET6:
bzero(&tmp6, sizeof(tmp6));
tmp6.sin6_len = sizeof(tmp6);
tmp6.sin6_family = AF_INET6;
bcopy(a, &tmp6.sin6_addr, sizeof(tmp6.sin6_addr));
ke = (struct pfr_kentry *)rn_match(&tmp6, kt->pfrkt_ip6);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
switch (op) {
case PF_PASS:
op_idx = PFR_OP_PASS;
break;
case PF_MATCH:
op_idx = PFR_OP_MATCH;
break;
case PF_DROP:
op_idx = PFR_OP_BLOCK;
break;
default:
panic("unhandled op");
}
if ((ke == NULL || (ke->pfrke_flags & PFRKE_FLAG_NOT)) != notrule) {
if (op_idx != PFR_OP_PASS)
DPFPRINTF(LOG_DEBUG,
"pfr_update_stats: assertion failed.");
op_idx = PFR_OP_XPASS;
}
kt->pfrkt_packets[dir_idx][op_idx]++;
kt->pfrkt_bytes[dir_idx][op_idx] += len;
if (ke != NULL && op_idx != PFR_OP_XPASS &&
(kt->pfrkt_flags & PFR_TFLAG_COUNTERS)) {
if (ke->pfrke_counters == NULL)
ke->pfrke_counters = pool_get(&pfr_kcounters_pl,
PR_NOWAIT | PR_ZERO);
if (ke->pfrke_counters != NULL) {
ke->pfrke_counters->pfrkc_packets[dir_idx][op_idx]++;
ke->pfrke_counters->pfrkc_bytes[dir_idx][op_idx] += len;
}
}
}
struct pfr_ktable *
pfr_attach_table(struct pf_ruleset *rs, char *name, int wait)
{
struct pfr_ktable *kt, *rt;
struct pfr_table tbl;
struct pf_anchor *ac = rs->anchor;
bzero(&tbl, sizeof(tbl));
strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name));
if (ac != NULL)
strlcpy(tbl.pfrt_anchor, ac->path, sizeof(tbl.pfrt_anchor));
kt = pfr_lookup_table(&tbl);
if (kt == NULL) {
kt = pfr_create_ktable(&tbl, gettime(), 1, wait);
if (kt == NULL)
return (NULL);
if (ac != NULL) {
bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor));
rt = pfr_lookup_table(&tbl);
if (rt == NULL) {
rt = pfr_create_ktable(&tbl, 0, 1, wait);
if (rt == NULL) {
pfr_destroy_ktable(kt, 0);
return (NULL);
}
pfr_insert_ktable(rt);
}
kt->pfrkt_root = rt;
}
pfr_insert_ktable(kt);
}
if (!kt->pfrkt_refcnt[PFR_REFCNT_RULE]++)
pfr_setflags_ktable(kt, kt->pfrkt_flags|PFR_TFLAG_REFERENCED);
return (kt);
}
void
pfr_detach_table(struct pfr_ktable *kt)
{
if (kt->pfrkt_refcnt[PFR_REFCNT_RULE] <= 0)
DPFPRINTF(LOG_NOTICE, "pfr_detach_table: refcount = %d.",
kt->pfrkt_refcnt[PFR_REFCNT_RULE]);
else if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE])
pfr_setflags_ktable(kt, kt->pfrkt_flags&~PFR_TFLAG_REFERENCED);
}
int
pfr_islinklocal(sa_family_t af, struct pf_addr *addr)
{
#ifdef INET6
if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&addr->v6))
return (1);
#endif /* INET6 */
return (0);
}
int
pfr_pool_get(struct pf_pool *rpool, struct pf_addr **raddr,
struct pf_addr **rmask, sa_family_t af)
{
struct pfr_ktable *kt;
struct pfr_kentry *ke, *ke2;
struct pf_addr *addr, *counter;
union sockaddr_union mask;
struct sockaddr_in tmp4;
#ifdef INET6
struct sockaddr_in6 tmp6;
#endif
int startidx, idx = -1, loop = 0, use_counter = 0;
switch (af) {
case AF_INET:
bzero(&tmp4, sizeof(tmp4));
tmp4.sin_len = sizeof(tmp4);
tmp4.sin_family = AF_INET;
addr = (struct pf_addr *)&tmp4.sin_addr;
break;
#ifdef INET6
case AF_INET6:
bzero(&tmp6, sizeof(tmp6));
tmp6.sin6_len = sizeof(tmp6);
tmp6.sin6_family = AF_INET6;
addr = (struct pf_addr *)&tmp6.sin6_addr;
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
if (rpool->addr.type == PF_ADDR_TABLE)
kt = rpool->addr.p.tbl;
else if (rpool->addr.type == PF_ADDR_DYNIFTL)
kt = rpool->addr.p.dyn->pfid_kt;
else
return (-1);
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return (-1);
counter = &rpool->counter;
idx = rpool->tblidx;
if (idx < 0 || idx >= kt->pfrkt_cnt)
idx = 0;
else
use_counter = 1;
startidx = idx;
_next_block:
if (loop && startidx == idx) {
kt->pfrkt_nomatch++;
return (1);
}
ke = pfr_kentry_byidx(kt, idx, af);
if (ke == NULL) {
/* we don't have this idx, try looping */
if (loop || (ke = pfr_kentry_byidx(kt, 0, af)) == NULL) {
kt->pfrkt_nomatch++;
return (1);
}
idx = 0;
loop++;
}
/* Get current weight for weighted round-robin */
if (idx == 0 && use_counter == 1 && kt->pfrkt_refcntcost > 0) {
rpool->curweight = rpool->curweight - kt->pfrkt_gcdweight;
if (rpool->curweight < 1)
rpool->curweight = kt->pfrkt_maxweight;
}
pfr_prepare_network(&pfr_mask, af, ke->pfrke_net);
*raddr = SUNION2PF(&ke->pfrke_sa, af);
*rmask = SUNION2PF(&pfr_mask, af);
if (use_counter && !PF_AZERO(counter, af)) {
/* is supplied address within block? */
if (!pf_match_addr(0, *raddr, *rmask, counter, af)) {
/* no, go to next block in table */
idx++;
use_counter = 0;
goto _next_block;
}
pf_addrcpy(addr, counter, af);
} else {
/* use first address of block */
pf_addrcpy(addr, *raddr, af);
}
if (!KENTRY_NETWORK(ke)) {
/* this is a single IP address - no possible nested block */
if (rpool->addr.type == PF_ADDR_DYNIFTL &&
pfr_islinklocal(af, addr)) {
idx++;
goto _next_block;
}
pf_addrcpy(counter, addr, af);
rpool->tblidx = idx;
kt->pfrkt_match++;
rpool->states = 0;
if (ke->pfrke_counters != NULL)
rpool->states = ke->pfrke_counters->states;
switch (ke->pfrke_type) {
case PFRKE_COST:
rpool->weight = ((struct pfr_kentry_cost *)ke)->weight;
/* FALLTHROUGH */
case PFRKE_ROUTE:
rpool->kif = ((struct pfr_kentry_route *)ke)->kif;
break;
default:
rpool->weight = 1;
break;
}
return (0);
}
for (;;) {
/* we don't want to use a nested block */
switch (af) {
case AF_INET:
ke2 = (struct pfr_kentry *)rn_match(&tmp4,
kt->pfrkt_ip4);
break;
#ifdef INET6
case AF_INET6:
ke2 = (struct pfr_kentry *)rn_match(&tmp6,
kt->pfrkt_ip6);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
if (ke2 == ke) {
/* lookup return the same block - perfect */
if (rpool->addr.type == PF_ADDR_DYNIFTL &&
pfr_islinklocal(af, addr))
goto _next_entry;
pf_addrcpy(counter, addr, af);
rpool->tblidx = idx;
kt->pfrkt_match++;
rpool->states = 0;
if (ke->pfrke_counters != NULL)
rpool->states = ke->pfrke_counters->states;
switch (ke->pfrke_type) {
case PFRKE_COST:
rpool->weight =
((struct pfr_kentry_cost *)ke)->weight;
/* FALLTHROUGH */
case PFRKE_ROUTE:
rpool->kif = ((struct pfr_kentry_route *)ke)->kif;
break;
default:
rpool->weight = 1;
break;
}
return (0);
}
_next_entry:
/* we need to increase the counter past the nested block */
pfr_prepare_network(&mask, AF_INET, ke2->pfrke_net);
pf_poolmask(addr, addr, SUNION2PF(&mask, af), &pfr_ffaddr, af);
pf_addr_inc(addr, af);
if (!pf_match_addr(0, *raddr, *rmask, addr, af)) {
/* ok, we reached the end of our main block */
/* go to next block in table */
idx++;
use_counter = 0;
goto _next_block;
}
}
}
struct pfr_kentry *
pfr_kentry_byidx(struct pfr_ktable *kt, int idx, int af)
{
struct pfr_walktree w;
bzero(&w, sizeof(w));
w.pfrw_op = PFRW_POOL_GET;
w.pfrw_cnt = idx;
switch (af) {
case AF_INET:
rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w);
return (w.pfrw_kentry);
#ifdef INET6
case AF_INET6:
rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w);
return (w.pfrw_kentry);
#endif /* INET6 */
default:
return (NULL);
}
}
/* Added for load balancing state counter use. */
int
pfr_states_increase(struct pfr_ktable *kt, struct pf_addr *addr, int af)
{
struct pfr_kentry *ke;
ke = pfr_kentry_byaddr(kt, addr, af, 1);
if (ke == NULL)
return (-1);
if (ke->pfrke_counters == NULL)
ke->pfrke_counters = pool_get(&pfr_kcounters_pl,
PR_NOWAIT | PR_ZERO);
if (ke->pfrke_counters == NULL)
return (-1);
ke->pfrke_counters->states++;
return ke->pfrke_counters->states;
}
/* Added for load balancing state counter use. */
int
pfr_states_decrease(struct pfr_ktable *kt, struct pf_addr *addr, int af)
{
struct pfr_kentry *ke;
ke = pfr_kentry_byaddr(kt, addr, af, 1);
if (ke == NULL)
return (-1);
if (ke->pfrke_counters == NULL)
ke->pfrke_counters = pool_get(&pfr_kcounters_pl,
PR_NOWAIT | PR_ZERO);
if (ke->pfrke_counters == NULL)
return (-1);
if (ke->pfrke_counters->states > 0)
ke->pfrke_counters->states--;
else
DPFPRINTF(LOG_DEBUG,
"pfr_states_decrease: states-- when states <= 0");
return ke->pfrke_counters->states;
}
void
pfr_dynaddr_update(struct pfr_ktable *kt, struct pfi_dynaddr *dyn)
{
struct pfr_walktree w;
bzero(&w, sizeof(w));
w.pfrw_op = PFRW_DYNADDR_UPDATE;
w.pfrw_dyn = dyn;
dyn->pfid_acnt4 = 0;
dyn->pfid_acnt6 = 0;
switch (dyn->pfid_af) {
case AF_UNSPEC: /* look up all both addresses IPv4 + IPv6 */
rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w);
rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w);
break;
case AF_INET:
rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w);
break;
#ifdef INET6
case AF_INET6:
rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w);
break;
#endif /* INET6 */
default:
unhandled_af(dyn->pfid_af);
}
}
void
pfr_ktable_winfo_update(struct pfr_ktable *kt, struct pfr_kentry *p) {
/*
* If cost flag is set,
* gcdweight is needed for round-robin.
*/
if (kt->pfrkt_refcntcost > 0) {
u_int16_t weight;
weight = (p->pfrke_type == PFRKE_COST) ?
((struct pfr_kentry_cost *)p)->weight : 1;
if (kt->pfrkt_gcdweight == 0)
kt->pfrkt_gcdweight = weight;
kt->pfrkt_gcdweight =
pfr_gcd(weight, kt->pfrkt_gcdweight);
if (kt->pfrkt_maxweight < weight)
kt->pfrkt_maxweight = weight;
}
}
struct pfr_ktable *
pfr_ktable_select_active(struct pfr_ktable *kt)
{
if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL)
kt = kt->pfrkt_root;
if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE))
return (NULL);
return (kt);
}
47
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* $OpenBSD: vfs_init.c,v 1.43 2019/12/26 13:30:54 bluhm Exp $ */
/* $NetBSD: vfs_init.c,v 1.6 1996/02/09 19:00:58 christos Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed
* to Berkeley by John Heidemann of the UCLA Ficus project.
*
* Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_init.c 8.3 (Berkeley) 1/4/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/pool.h>
struct pool namei_pool;
/* This defines the root filesystem. */
struct vnode *rootvnode;
/* Set up the filesystem operations for vnodes. */
static struct vfsconf vfsconflist[] = {
#ifdef FFS
{ &ffs_vfsops, MOUNT_FFS, 1, 0, MNT_LOCAL | MNT_SWAPPABLE,
sizeof(struct ufs_args) },
#endif
#ifdef MFS
{ &mfs_vfsops, MOUNT_MFS, 3, 0, MNT_LOCAL,
sizeof(struct mfs_args) },
#endif
#ifdef EXT2FS
{ &ext2fs_vfsops, MOUNT_EXT2FS, 17, 0, MNT_LOCAL | MNT_SWAPPABLE,
sizeof(struct ufs_args) },
#endif
#ifdef CD9660
{ &cd9660_vfsops, MOUNT_CD9660, 14, 0, MNT_LOCAL,
sizeof(struct iso_args) },
#endif
#ifdef MSDOSFS
{ &msdosfs_vfsops, MOUNT_MSDOS, 4, 0, MNT_LOCAL | MNT_SWAPPABLE,
sizeof(struct msdosfs_args) },
#endif
#ifdef NFSCLIENT
{ &nfs_vfsops, MOUNT_NFS, 2, 0, MNT_SWAPPABLE,
sizeof(struct nfs_args) },
#endif
#ifdef NTFS
{ &ntfs_vfsops, MOUNT_NTFS, 6, 0, MNT_LOCAL,
sizeof(struct ntfs_args) },
#endif
#ifdef UDF
{ &udf_vfsops, MOUNT_UDF, 13, 0, MNT_LOCAL,
sizeof(struct iso_args) },
#endif
#ifdef FUSE
{ &fusefs_vfsops, MOUNT_FUSEFS, 18, 0, MNT_LOCAL,
sizeof(struct fusefs_args) },
#endif
#ifdef TMPFS
{ &tmpfs_vfsops, MOUNT_TMPFS, 19, 0, MNT_LOCAL,
sizeof(struct tmpfs_args) },
#endif
};
/*
* Initially the size of the list, vfsinit will set maxvfsconf
* to the highest defined type number.
*/
int maxvfsconf = sizeof(vfsconflist) / sizeof(struct vfsconf);
/* Initialize the vnode structures and initialize each file system type. */
void
vfsinit(void)
{
struct vfsconf *vfsp;
int i;
pool_init(&namei_pool, MAXPATHLEN, 0, IPL_NONE, PR_WAITOK, "namei",
NULL);
/* Initialize the vnode table. */
vntblinit();
/* Initialize the vnode name cache. */
nchinit();
maxvfsconf = 0;
for (i = 0; i < nitems(vfsconflist); i++) {
vfsp = &vfsconflist[i];
if (vfsp->vfc_typenum > maxvfsconf)
maxvfsconf = vfsp->vfc_typenum;
if (vfsp->vfc_vfsops->vfs_init != NULL)
(*vfsp->vfc_vfsops->vfs_init)(vfsp);
}
}
struct vfsconf *
vfs_byname(const char *name)
{
int i;
for (i = 0; i < nitems(vfsconflist); i++) {
if (strcmp(vfsconflist[i].vfc_name, name) == 0)
return &vfsconflist[i];
}
return NULL;
}
struct vfsconf *
vfs_bytypenum(int typenum)
{
int i;
for (i = 0; i < nitems(vfsconflist); i++) {
if (vfsconflist[i].vfc_typenum == typenum)
return &vfsconflist[i];
}
return NULL;
}
8
1
9
4
1
1
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
/* $OpenBSD: pckbd.c,v 1.47 2022/04/06 18:59:29 naddy Exp $ */
/* $NetBSD: pckbd.c,v 1.24 2000/06/05 22:20:57 sommerfeld Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz and Don Ahn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)pccons.c 5.11 (Berkeley) 5/21/91
*/
/*
* code to work keyboard for PC-style console
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <machine/bus.h>
#include <dev/ic/pckbcvar.h>
#include <dev/pckbc/pckbdreg.h>
#include <dev/pckbc/pmsreg.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/pckbc/wskbdmap_mfii.h>
struct pckbd_internal {
int t_isconsole;
pckbc_tag_t t_kbctag;
pckbc_slot_t t_kbcslot;
int t_translating;
int t_table;
int t_lastchar;
int t_extended;
int t_extended1;
int t_releasing;
struct pckbd_softc *t_sc; /* back pointer */
};
struct pckbd_softc {
struct device sc_dev;
struct pckbd_internal *id;
int sc_enabled;
int sc_ledstate;
struct device *sc_wskbddev;
#ifdef WSDISPLAY_COMPAT_RAWKBD
int rawkbd;
u_int sc_rawcnt;
char sc_rawbuf[3];
#endif
};
static int pckbd_is_console(pckbc_tag_t, pckbc_slot_t);
int pckbdprobe(struct device *, void *, void *);
void pckbdattach(struct device *, struct device *, void *);
int pckbdactivate(struct device *, int);
const struct cfattach pckbd_ca = {
sizeof(struct pckbd_softc),
pckbdprobe,
pckbdattach,
NULL,
pckbdactivate
};
int pckbd_enable(void *, int);
void pckbd_set_leds(void *, int);
int pckbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
const struct wskbd_accessops pckbd_accessops = {
pckbd_enable,
pckbd_set_leds,
pckbd_ioctl,
};
void pckbd_cngetc(void *, u_int *, int *);
void pckbd_cnpollc(void *, int);
void pckbd_cnbell(void *, u_int, u_int, u_int);
const struct wskbd_consops pckbd_consops = {
pckbd_cngetc,
pckbd_cnpollc,
pckbd_cnbell,
};
const struct wskbd_mapdata pckbd_keymapdata = {
pckbd_keydesctab,
#ifdef PCKBD_LAYOUT
PCKBD_LAYOUT,
#else
KB_US | KB_DEFAULT,
#endif
};
/*
* Hackish support for a bell on the PC Keyboard; when a suitable beeper
* is found, it attaches itself into the pckbd driver here.
*/
void (*pckbd_bell_fn)(void *, u_int, u_int, u_int, int);
void *pckbd_bell_fn_arg;
void pckbd_bell(u_int, u_int, u_int, int);
int pckbd_scancode_translate(struct pckbd_internal *, int);
int pckbd_set_xtscancode(pckbc_tag_t, pckbc_slot_t,
struct pckbd_internal *);
void pckbd_init(struct pckbd_internal *, pckbc_tag_t, pckbc_slot_t, int);
void pckbd_input(void *, int);
static int pckbd_decode(struct pckbd_internal *, int,
u_int *, int *);
static int pckbd_led_encode(int);
struct pckbd_internal pckbd_consdata;
int
pckbdactivate(struct device *self, int act)
{
struct pckbd_softc *sc = (struct pckbd_softc *)self;
int rv = 0;
u_char cmd[1];
switch(act) {
case DVACT_RESUME:
if (sc->sc_enabled) {
/*
* Some keyboards are not enabled after a reset,
* so make sure it is enabled now.
*/
cmd[0] = KBC_ENABLE;
(void) pckbc_poll_cmd(sc->id->t_kbctag,
sc->id->t_kbcslot, cmd, 1, 0, NULL, 0);
/* XXX - also invoke pckbd_set_xtscancode() too? */
}
break;
}
rv = config_activate_children(self, act);
return (rv);
}
int
pckbd_set_xtscancode(pckbc_tag_t kbctag, pckbc_slot_t kbcslot,
struct pckbd_internal *id)
{
int table = 3;
if (pckbc_xt_translation(kbctag)) {
#ifdef DEBUG
printf("pckbd: enabling of translation failed\n");
#endif
/*
* Since the keyboard controller can not translate scan
* codes to the XT set (#1), we would like to request
* this exact set. However it is likely that the
* controller does not support it either.
*
* So try scan code set #2 as well, which this driver
* knows how to translate.
*/
table = 2;
if (id != NULL)
id->t_translating = 0;
} else {
if (id != NULL) {
id->t_translating = 1;
if (id->t_table == 0) {
/*
* Don't bother explicitly setting into set 2,
* it's the default.
*/
id->t_table = 2;
return (0);
}
}
}
/* keep falling back until we hit a table that looks usable. */
for (; table >= 1; table--) {
u_char cmd[2];
#ifdef DEBUG
printf("pckbd: trying table %d\n", table);
#endif
cmd[0] = KBC_SETTABLE;
cmd[1] = table;
if (pckbc_poll_cmd(kbctag, kbcslot, cmd, 2, 0, NULL, 0)) {
#ifdef DEBUG
printf("pckbd: table set of %d failed\n", table);
#endif
if (table > 1) {
cmd[0] = KBC_RESET;
(void)pckbc_poll_cmd(kbctag, kbcslot, cmd,
1, 1, NULL, 1);
pckbc_flush(kbctag, kbcslot);
continue;
}
}
/*
* the 8042 took the table set request, however, not all that
* report they can work with table 3 actually work, so ask what
* table it reports it's in.
*/
if (table == 3) {
u_char resp[1];
cmd[0] = KBC_SETTABLE;
cmd[1] = 0;
if (pckbc_poll_cmd(kbctag, kbcslot, cmd, 2, 1, resp, 0)) {
/*
* query failed, step down to table 2 to be
* safe.
*/
#ifdef DEBUG
printf("pckbd: table 3 verification failed\n");
#endif
continue;
} else if (resp[0] == 3) {
#ifdef DEBUG
printf("pckbd: settling on table 3\n");
#endif
break;
}
#ifdef DEBUG
else
printf("pckbd: table \"%x\" != 3, trying 2\n",
resp[0]);
#endif
} else {
#ifdef DEBUG
printf("pckbd: settling on table %d\n", table);
#endif
break;
}
}
if (table == 0)
return (1);
if (id != NULL)
id->t_table = table;
return (0);
}
static int
pckbd_is_console(pckbc_tag_t tag, pckbc_slot_t slot)
{
return (pckbd_consdata.t_isconsole &&
(tag == pckbd_consdata.t_kbctag) &&
(slot == pckbd_consdata.t_kbcslot));
}
/*
* these are both bad jokes
*/
int
pckbdprobe(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct pckbc_attach_args *pa = aux;
u_char cmd[1], resp[1];
int res;
/*
* XXX There are rumours that a keyboard can be connected
* to the aux port as well. For me, this didn't work.
* For further experiments, allow it if explicitly
* wired in the config file.
*/
if ((pa->pa_slot != PCKBC_KBD_SLOT) &&
(cf->cf_loc[PCKBCCF_SLOT] == PCKBCCF_SLOT_DEFAULT))
return (0);
/* Flush any garbage. */
pckbc_flush(pa->pa_tag, pa->pa_slot);
/* Reset the keyboard. */
cmd[0] = KBC_RESET;
res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1);
if (res) {
#ifdef DEBUG
printf("pckbdprobe: reset error %d\n", res);
#endif
/*
* There is probably no keyboard connected.
* Let the probe succeed if the keyboard is used
* as console input - it can be connected later.
*/
#if defined(__i386__) || defined(__amd64__)
/*
* However, on legacy-free PCs, there might really
* be no PS/2 connector at all; in that case, do not
* even try to attach; ukbd will take over as console.
*/
if (res == ENXIO) {
/* check cf_flags from parent */
struct cfdata *cf = parent->dv_cfdata;
if (!ISSET(cf->cf_flags, PCKBCF_FORCE_KEYBOARD_PRESENT))
return 0;
}
#endif
return (pckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0);
}
if (resp[0] != KBR_RSTDONE) {
printf("pckbdprobe: reset response 0x%x\n", resp[0]);
return (0);
}
/*
* Some keyboards seem to leave a second ack byte after the reset.
* This is kind of stupid, but we account for them anyway by just
* flushing the buffer.
*/
pckbc_flush(pa->pa_tag, pa->pa_slot);
return (2);
}
void
pckbdattach(struct device *parent, struct device *self, void *aux)
{
struct pckbd_softc *sc = (void *)self;
struct pckbc_attach_args *pa = aux;
int isconsole;
struct wskbddev_attach_args a;
u_char cmd[1];
isconsole = pckbd_is_console(pa->pa_tag, pa->pa_slot);
if (isconsole) {
sc->id = &pckbd_consdata;
if (sc->id->t_table == 0)
pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, sc->id);
/*
* Some keyboards are not enabled after a reset,
* so make sure it is enabled now.
*/
cmd[0] = KBC_ENABLE;
(void) pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
cmd, 1, 0, NULL, 0);
sc->sc_enabled = 1;
} else {
sc->id = malloc(sizeof(struct pckbd_internal),
M_DEVBUF, M_WAITOK);
pckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0);
pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, sc->id);
/* no interrupts until enabled */
cmd[0] = KBC_DISABLE;
(void) pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
cmd, 1, 0, NULL, 0);
sc->sc_enabled = 0;
}
sc->id->t_sc = sc;
pckbc_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot,
pckbd_input, sc, sc->sc_dev.dv_xname);
a.console = isconsole;
a.keymap = &pckbd_keymapdata;
a.accessops = &pckbd_accessops;
a.accesscookie = sc;
printf("\n");
/*
* Attach the wskbd, saving a handle to it.
*/
sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
}
int
pckbd_enable(void *v, int on)
{
struct pckbd_softc *sc = v;
u_char cmd[1];
int res;
if (on) {
if (sc->sc_enabled)
return (EBUSY);
pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1);
cmd[0] = KBC_ENABLE;
res = pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
cmd, 1, 0, NULL, 0);
if (res) {
printf("pckbd_enable: command error\n");
return (res);
}
res = pckbd_set_xtscancode(sc->id->t_kbctag,
sc->id->t_kbcslot, sc->id);
if (res)
return (res);
sc->sc_enabled = 1;
} else {
if (sc->id->t_isconsole)
return (EBUSY);
cmd[0] = KBC_DISABLE;
res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
cmd, 1, 0, 1, 0);
if (res) {
printf("pckbd_disable: command error\n");
return (res);
}
pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0);
sc->sc_enabled = 0;
}
return (0);
}
const u_int8_t pckbd_xtbl[] = {
/* 0x00 */
0,
0x43, /* F9 */
0,
0x3f, /* F5 */
0x3d, /* F3 */
0x3b, /* F1 */
0x3c, /* F2 */
0x58, /* F12 */
0x40, /* F6 according to documentation */
0x44, /* F10 */
0x42, /* F8 */
0x40, /* F6 according to experimentation */
0x3e, /* F4 */
0x0f, /* Tab */
0x29, /* ` ~ */
0,
/* 0x10 */
0,
0x38, /* Left Alt */
0x2a, /* Left Shift */
0,
0x1d, /* Left Ctrl */
0x10, /* q */
0x02, /* 1 ! */
0,
0,
0,
0x2c, /* z */
0x1f, /* s */
0x1e, /* a */
0x11, /* w */
0x03, /* 2 @ */
0,
/* 0x20 */
0,
0x2e, /* c */
0x2d, /* x */
0x20, /* d */
0x12, /* e */
0x05, /* 4 $ */
0x04, /* 3 # */
0,
0,
0x39, /* Space */
0x2f, /* v */
0x21, /* f */
0x14, /* t */
0x13, /* r */
0x06, /* 5 % */
0,
/* 0x30 */
0,
0x31, /* n */
0x30, /* b */
0x23, /* h */
0x22, /* g */
0x15, /* y */
0x07, /* 6 ^ */
0,
0,
0,
0x32, /* m */
0x24, /* j */
0x16, /* u */
0x08, /* 7 & */
0x09, /* 8 * */
0,
/* 0x40 */
0,
0x33, /* , < */
0x25, /* k */
0x17, /* i */
0x18, /* o */
0x0b, /* 0 ) */
0x0a, /* 9 ( */
0,
0,
0x34, /* . > */
0x35, /* / ? */
0x26, /* l */
0x27, /* ; : */
0x19, /* p */
0x0c, /* - _ */
0,
/* 0x50 */
0,
0,
0x28, /* ' " */
0,
0x1a, /* [ { */
0x0d, /* = + */
0,
0,
0x3a, /* Caps Lock */
0x36, /* Right Shift */
0x1c, /* Return */
0x1b, /* ] } */
0,
0x2b, /* \ | */
0,
0,
/* 0x60 */
0,
0,
0,
0,
0,
0,
0x0e, /* Back Space */
0,
0,
0x4f, /* KP 1 */
0,
0x4b, /* KP 4 */
0x47, /* KP 7 */
0,
0,
0,
/* 0x70 */
0x52, /* KP 0 */
0x53, /* KP . */
0x50, /* KP 2 */
0x4c, /* KP 5 */
0x4d, /* KP 6 */
0x48, /* KP 8 */
0x01, /* Escape */
0x45, /* Num Lock */
0x57, /* F11 */
0x4e, /* KP + */
0x51, /* KP 3 */
0x4a, /* KP - */
0x37, /* KP * */
0x49, /* KP 9 */
0x46, /* Scroll Lock */
0,
/* 0x80 */
0,
0,
0,
0x41, /* F7 (produced as an actual 8 bit code) */
0 /* Alt-Print Screen */
};
const u_int8_t pckbd_xtbl_ext[] = {
/* 0x00 */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
/* 0x10 */
0,
0x38, /* Right Alt */
0, /* E0 12, to be ignored */
0,
0x1d, /* Right Ctrl */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
/* 0x20 */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
/* 0x30 */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
/* 0x40 */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x55, /* KP / */
0,
0,
0,
0,
0,
/* 0x50 */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x1c, /* KP Return */
0,
0,
0,
0,
0,
/* 0x60 */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x4f, /* End */
0,
0x4b, /* Left */
0x47, /* Home */
0,
0,
0,
/* 0x70 */
0x52, /* Insert */
0x53, /* Delete */
0x50, /* Down */
0,
0x4d, /* Right */
0x48, /* Up */
0,
0,
0,
0,
0x51, /* Page Down */
0,
0x37, /* Print Screen */
0x49, /* Page Up */
0x46, /* Ctrl-Break */
0
};
/*
* Translate scan codes from set 2 to set 1
*/
int
pckbd_scancode_translate(struct pckbd_internal *id, int datain)
{
if (id->t_translating != 0 || id->t_table == 1)
return datain;
if (datain == KBR_BREAK) {
id->t_releasing = 0x80; /* next keycode is a release */
return 0; /* consume scancode */
}
/*
* Convert BREAK sequence (14 77 -> 1D 45)
*/
if (id->t_extended1 == 2 && datain == 0x14)
return 0x1d | id->t_releasing;
else if (id->t_extended1 == 1 && datain == 0x77)
return 0x45 | id->t_releasing;
if (id->t_extended != 0) {
if (datain >= sizeof pckbd_xtbl_ext)
datain = 0;
else
datain = pckbd_xtbl_ext[datain];
} else {
if (datain >= sizeof pckbd_xtbl)
datain = 0;
else
datain = pckbd_xtbl[datain];
}
if (datain == 0) {
/*
* We don't know how to translate this scan code, but
* we can't silently eat it either (because there might
* have been an extended byte transmitted already).
* Hopefully this value will be harmless to the upper
* layers.
*/
return 0xff;
}
return datain | id->t_releasing;
}
static int
pckbd_decode(struct pckbd_internal *id, int datain, u_int *type, int *dataout)
{
int key;
int releasing;
if (datain == KBR_EXTENDED0) {
id->t_extended = 0x80;
return 0;
} else if (datain == KBR_EXTENDED1) {
id->t_extended1 = 2;
return 0;
}
releasing = datain & 0x80;
datain &= 0x7f;
/*
* process BREAK key sequence (EXT1 1D 45 / EXT1 9D C5):
* map to (unused) code 7F
*/
if (id->t_extended1 == 2 && datain == 0x1d) {
id->t_extended1 = 1;
return 0;
} else if (id->t_extended1 == 1 && datain == 0x45) {
id->t_extended1 = 0;
datain = 0x7f;
} else
id->t_extended1 = 0;
if (id->t_translating != 0 || id->t_table == 1) {
id->t_releasing = releasing;
} else {
/* id->t_releasing computed in pckbd_scancode_translate() */
}
/* map extended keys to (unused) codes 128-254 */
key = datain | id->t_extended;
id->t_extended = 0;
if (id->t_releasing) {
id->t_releasing = 0;
id->t_lastchar = 0;
*type = WSCONS_EVENT_KEY_UP;
} else {
/* Always ignore typematic keys */
if (key == id->t_lastchar)
return 0;
id->t_lastchar = key;
*type = WSCONS_EVENT_KEY_DOWN;
}
*dataout = key;
return 1;
}
void
pckbd_init(struct pckbd_internal *t, pckbc_tag_t kbctag, pckbc_slot_t kbcslot,
int console)
{
bzero(t, sizeof(struct pckbd_internal));
t->t_isconsole = console;
t->t_kbctag = kbctag;
t->t_kbcslot = kbcslot;
}
static int
pckbd_led_encode(int led)
{
int res;
res = 0;
if (led & WSKBD_LED_SCROLL)
res |= 0x01;
if (led & WSKBD_LED_NUM)
res |= 0x02;
if (led & WSKBD_LED_CAPS)
res |= 0x04;
return(res);
}
void
pckbd_set_leds(void *v, int leds)
{
struct pckbd_softc *sc = v;
u_char cmd[2];
cmd[0] = KBC_MODEIND;
cmd[1] = pckbd_led_encode(leds);
sc->sc_ledstate = leds;
(void) pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
cmd, 2, 0, 0, 0);
}
/*
* Got a console receive interrupt -
* the console processor wants to give us a character.
*/
void
pckbd_input(void *vsc, int data)
{
struct pckbd_softc *sc = vsc;
int rc, type, key;
data = pckbd_scancode_translate(sc->id, data);
if (data == 0)
return;
rc = pckbd_decode(sc->id, data, &type, &key);
#ifdef WSDISPLAY_COMPAT_RAWKBD
if (sc->rawkbd) {
sc->sc_rawbuf[sc->sc_rawcnt++] = (char)data;
if (rc != 0 || sc->sc_rawcnt == sizeof(sc->sc_rawbuf)) {
wskbd_rawinput(sc->sc_wskbddev, sc->sc_rawbuf,
sc->sc_rawcnt);
sc->sc_rawcnt = 0;
}
/*
* Pass audio keys to wskbd_input anyway.
*/
if (rc == 0 || (key != 160 && key != 174 && key != 176))
return;
}
#endif
if (rc != 0)
wskbd_input(sc->sc_wskbddev, type, key);
}
int
pckbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct pckbd_softc *sc = v;
switch (cmd) {
case WSKBDIO_GTYPE:
*(int *)data = WSKBD_TYPE_PC_XT;
return 0;
case WSKBDIO_SETLEDS: {
char cmd[2];
int res;
cmd[0] = KBC_MODEIND;
cmd[1] = pckbd_led_encode(*(int *)data);
sc->sc_ledstate = *(int *)data & (WSKBD_LED_SCROLL |
WSKBD_LED_NUM | WSKBD_LED_CAPS);
res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
cmd, 2, 0, 1, 0);
return (res);
}
case WSKBDIO_GETLEDS:
*(int *)data = sc->sc_ledstate;
return (0);
case WSKBDIO_COMPLEXBELL:
#define d ((struct wskbd_bell_data *)data)
/*
* Keyboard can't beep directly; we have an
* externally-provided global hook to do this.
*/
pckbd_bell(d->pitch, d->period, d->volume, 0);
#undef d
return (0);
#ifdef WSDISPLAY_COMPAT_RAWKBD
case WSKBDIO_SETMODE:
sc->rawkbd = (*(int *)data == WSKBD_RAW);
return (0);
#endif
}
return -1;
}
void
pckbd_bell(u_int pitch, u_int period, u_int volume, int poll)
{
if (pckbd_bell_fn != NULL)
(*pckbd_bell_fn)(pckbd_bell_fn_arg, pitch, period,
volume, poll);
}
void
pckbd_hookup_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg)
{
if (pckbd_bell_fn == NULL) {
pckbd_bell_fn = fn;
pckbd_bell_fn_arg = arg;
}
}
int
pckbd_cnattach(pckbc_tag_t kbctag)
{
pckbd_init(&pckbd_consdata, kbctag, PCKBC_KBD_SLOT, 1);
wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata);
return (0);
}
/* ARGSUSED */
void
pckbd_cngetc(void *v, u_int *type, int *data)
{
struct pckbd_internal *t = v;
int val;
for (;;) {
val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot);
if (val == -1)
continue;
val = pckbd_scancode_translate(t, val);
if (val == 0)
continue;
if (pckbd_decode(t, val, type, data))
return;
}
}
void
pckbd_cnpollc(void *v, int on)
{
struct pckbd_internal *t = v;
pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on);
/*
* If we enter ukc or ddb before having attached the console
* keyboard we need to probe its scan code set.
*/
if (t->t_table == 0) {
char cmd[1];
pckbc_flush(t->t_kbctag, t->t_kbcslot);
pckbd_set_xtscancode(t->t_kbctag, t->t_kbcslot, t);
/* Just to be sure. */
cmd[0] = KBC_ENABLE;
pckbc_poll_cmd(t->t_kbctag, PCKBC_KBD_SLOT, cmd, 1, 0, NULL, 0);
}
}
void
pckbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
{
pckbd_bell(pitch, period, volume, 1);
}
struct cfdriver pckbd_cd = {
NULL, "pckbd", DV_DULL
};
577
352
280
279
280
279
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
/* $OpenBSD: ffs_vfsops.c,v 1.193 2022/08/12 14:30:53 visa Exp $ */
/* $NetBSD: ffs_vfsops.c,v 1.19 1996/02/09 22:22:26 christos Exp $ */
/*
* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/mbuf.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
#include <sys/dkio.h>
#include <sys/disk.h>
#include <sys/specdev.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/ufs/dirhash.h>
#include <ufs/ffs/fs.h>
#include <ufs/ffs/ffs_extern.h>
#include <uvm/uvm_extern.h>
int ffs_sbupdate(struct ufsmount *, int);
int ffs_reload_vnode(struct vnode *, void *);
int ffs_sync_vnode(struct vnode *, void *);
int ffs_validate(struct fs *);
void ffs1_compat_read(struct fs *, struct ufsmount *, daddr_t);
void ffs1_compat_write(struct fs *, struct ufsmount *);
const struct vfsops ffs_vfsops = {
.vfs_mount = ffs_mount,
.vfs_start = ufs_start,
.vfs_unmount = ffs_unmount,
.vfs_root = ufs_root,
.vfs_quotactl = ufs_quotactl,
.vfs_statfs = ffs_statfs,
.vfs_sync = ffs_sync,
.vfs_vget = ffs_vget,
.vfs_fhtovp = ffs_fhtovp,
.vfs_vptofh = ffs_vptofh,
.vfs_init = ffs_init,
.vfs_sysctl = ffs_sysctl,
.vfs_checkexp = ufs_check_export,
};
struct inode_vtbl ffs_vtbl = {
ffs_truncate,
ffs_update,
ffs_inode_alloc,
ffs_inode_free,
ffs_balloc,
ffs_bufatoff
};
int
ffs_checkrange(struct mount *mp, uint32_t ino)
{
struct buf *bp;
struct cg *cgp;
struct fs *fs;
struct ufsmount *ump;
int cg, error;
fs = VFSTOUFS(mp)->um_fs;
if (ino < ROOTINO || ino >= fs->fs_ncg * fs->fs_ipg)
return ESTALE;
/*
* Need to check if inode is initialized because ffsv2 does
* lazy initialization and we can get here from nfs_fhtovp
*/
if (fs->fs_magic != FS_UFS2_MAGIC)
return 0;
cg = ino_to_cg(fs, ino);
ump = VFSTOUFS(mp);
error = bread(ump->um_devvp, fsbtodb(fs, cgtod(fs, cg)),
(int)fs->fs_cgsize, &bp);
if (error)
return error;
cgp = (struct cg *)bp->b_data;
if (!cg_chkmagic(cgp)) {
brelse(bp);
return ESTALE;
}
brelse(bp);
if (cg * fs->fs_ipg + cgp->cg_initediblk < ino)
return ESTALE;
return 0;
}
/*
* Called by main() when ufs is going to be mounted as root.
*/
struct pool ffs_ino_pool;
struct pool ffs_dinode1_pool;
#ifdef FFS2
struct pool ffs_dinode2_pool;
#endif
int
ffs_mountroot(void)
{
struct fs *fs;
struct mount *mp;
struct proc *p = curproc; /* XXX */
struct ufsmount *ump;
int error;
/*
* Get vnodes for swapdev and rootdev.
*/
swapdev_vp = NULL;
if ((error = bdevvp(swapdev, &swapdev_vp)) ||
(error = bdevvp(rootdev, &rootvp))) {
printf("ffs_mountroot: can't setup bdevvp's\n");
if (swapdev_vp)
vrele(swapdev_vp);
return (error);
}
if ((error = vfs_rootmountalloc("ffs", "root_device", &mp)) != 0) {
vrele(swapdev_vp);
vrele(rootvp);
return (error);
}
if ((error = ffs_mountfs(rootvp, mp, p)) != 0) {
vfs_unbusy(mp);
vfs_mount_free(mp);
vrele(swapdev_vp);
vrele(rootvp);
return (error);
}
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
ump = VFSTOUFS(mp);
fs = ump->um_fs;
strlcpy(fs->fs_fsmnt, mp->mnt_stat.f_mntonname, sizeof(fs->fs_fsmnt));
(void)ffs_statfs(mp, &mp->mnt_stat, p);
vfs_unbusy(mp);
inittodr(fs->fs_time);
return (0);
}
/*
* VFS Operations.
*
* mount system call
*/
int
ffs_mount(struct mount *mp, const char *path, void *data,
struct nameidata *ndp, struct proc *p)
{
struct vnode *devvp;
struct ufs_args *args = data;
struct ufsmount *ump = NULL;
struct fs *fs;
char fname[MNAMELEN];
char fspec[MNAMELEN];
int error = 0, flags;
int ronly;
#ifndef FFS_SOFTUPDATES
if (mp->mnt_flag & MNT_SOFTDEP) {
printf("WARNING: soft updates isn't compiled in\n");
mp->mnt_flag &= ~MNT_SOFTDEP;
}
#endif
/*
* Soft updates is incompatible with "async",
* so if we are doing softupdates stop the user
* from setting the async flag.
*/
if ((mp->mnt_flag & (MNT_SOFTDEP | MNT_ASYNC)) ==
(MNT_SOFTDEP | MNT_ASYNC)) {
return (EINVAL);
}
/*
* If updating, check whether changing from read-only to
* read/write; if there is no device name, that's all we do.
*/
if (mp->mnt_flag & MNT_UPDATE) {
ump = VFSTOUFS(mp);
fs = ump->um_fs;
devvp = ump->um_devvp;
error = 0;
ronly = fs->fs_ronly;
/*
* Soft updates won't be set if read/write,
* so "async" will be illegal.
*/
if (ronly == 0 && (mp->mnt_flag & MNT_ASYNC) &&
(fs->fs_flags & FS_DOSOFTDEP)) {
error = EINVAL;
goto error_1;
}
if (ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
/* Flush any dirty data */
VFS_SYNC(mp, MNT_WAIT, 0, p->p_ucred, p);
/*
* Get rid of files open for writing.
*/
flags = WRITECLOSE;
if (args == NULL)
flags |= IGNORECLEAN;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
if (fs->fs_flags & FS_DOSOFTDEP) {
error = softdep_flushfiles(mp, flags, p);
mp->mnt_flag &= ~MNT_SOFTDEP;
} else
error = ffs_flushfiles(mp, flags, p);
mp->mnt_flag |= MNT_RDONLY;
ronly = 1;
}
/*
* Flush soft dependencies if disabling it via an update
* mount. This may leave some items to be processed,
* so don't do this yet XXX.
*/
if ((fs->fs_flags & FS_DOSOFTDEP) &&
!(mp->mnt_flag & MNT_SOFTDEP) &&
!(mp->mnt_flag & MNT_RDONLY) && fs->fs_ronly == 0) {
#if 0
flags = WRITECLOSE;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
error = softdep_flushfiles(mp, flags, p);
#elif FFS_SOFTUPDATES
mp->mnt_flag |= MNT_SOFTDEP;
#endif
}
/*
* When upgrading to a softdep mount, we must first flush
* all vnodes. (not done yet -- see above)
*/
if (!(fs->fs_flags & FS_DOSOFTDEP) &&
(mp->mnt_flag & MNT_SOFTDEP) && fs->fs_ronly == 0) {
#if 0
flags = WRITECLOSE;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
error = ffs_flushfiles(mp, flags, p);
#else
mp->mnt_flag &= ~MNT_SOFTDEP;
#endif
}
if (!error && (mp->mnt_flag & MNT_RELOAD))
error = ffs_reload(mp, ndp->ni_cnd.cn_cred, p);
if (error)
goto error_1;
if (ronly && (mp->mnt_flag & MNT_WANTRDWR)) {
if (fs->fs_clean == 0) {
#if 0
/*
* It is safe to mount an unclean file system
* if it was previously mounted with softdep
* but we may lose space and must
* sometimes run fsck manually.
*/
if (fs->fs_flags & FS_DOSOFTDEP)
printf(
"WARNING: %s was not properly unmounted\n",
fs->fs_fsmnt);
else
#endif
if (mp->mnt_flag & MNT_FORCE) {
printf(
"WARNING: %s was not properly unmounted\n",
fs->fs_fsmnt);
} else {
printf(
"WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n",
fs->fs_fsmnt);
error = EROFS;
goto error_1;
}
}
if ((fs->fs_flags & FS_DOSOFTDEP)) {
error = softdep_mount(devvp, mp, fs,
p->p_ucred);
if (error)
goto error_1;
}
fs->fs_contigdirs = malloc((u_long)fs->fs_ncg,
M_UFSMNT, M_WAITOK|M_ZERO);
ronly = 0;
}
if (args == NULL)
goto success;
if (args->fspec == NULL) {
/*
* Process export requests.
*/
error = vfs_export(mp, &ump->um_export,
&args->export_info);
if (error)
goto error_1;
else
goto success;
}
}
/*
* Not an update, or updating the name: look up the name
* and verify that it refers to a sensible block device.
*/
error = copyinstr(args->fspec, fspec, sizeof(fspec), NULL);
if (error)
goto error_1;
if (disk_map(fspec, fname, MNAMELEN, DM_OPENBLCK) == -1)
memcpy(fname, fspec, sizeof(fname));
NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fname, p);
if ((error = namei(ndp)) != 0)
goto error_1;
devvp = ndp->ni_vp;
if (devvp->v_type != VBLK) {
error = ENOTBLK;
goto error_2;
}
if (major(devvp->v_rdev) >= nblkdev) {
error = ENXIO;
goto error_2;
}
if (mp->mnt_flag & MNT_UPDATE) {
/*
* UPDATE
* If it's not the same vnode, or at least the same device
* then it's not correct.
*/
if (devvp != ump->um_devvp) {
if (devvp->v_rdev == ump->um_devvp->v_rdev) {
vrele(devvp);
} else {
error = EINVAL; /* needs translation */
}
} else
vrele(devvp);
/*
* Update device name only on success
*/
if (!error) {
/*
* Save "mounted from" info for mount point (NULL pad)
*/
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, fname, MNAMELEN);
memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
}
} else {
/*
* Since this is a new mount, we want the names for
* the device and the mount point copied in. If an
* error occurs, the mountpoint is discarded by the
* upper level code.
*/
memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromname, fname, MNAMELEN);
memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
error = ffs_mountfs(devvp, mp, p);
}
if (error)
goto error_2;
/*
* Initialize FS stat information in mount struct; uses both
* mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname
*
* This code is common to root and non-root mounts
*/
if (args)
memcpy(&mp->mnt_stat.mount_info.ufs_args, args, sizeof(*args));
VFS_STATFS(mp, &mp->mnt_stat, p);
success:
if (path && (mp->mnt_flag & MNT_UPDATE)) {
/* Update clean flag after changing read-onlyness. */
fs = ump->um_fs;
if (ronly != fs->fs_ronly) {
fs->fs_ronly = ronly;
fs->fs_clean = ronly &&
(fs->fs_flags & FS_UNCLEAN) == 0 ? 1 : 0;
if (ronly)
free(fs->fs_contigdirs, M_UFSMNT, fs->fs_ncg);
}
if (!ronly) {
if (mp->mnt_flag & MNT_SOFTDEP)
fs->fs_flags |= FS_DOSOFTDEP;
else
fs->fs_flags &= ~FS_DOSOFTDEP;
}
ffs_sbupdate(ump, MNT_WAIT);
#if 0
if (ronly) {
int force = 0;
/*
* Updating mount to readonly. Try a cache flush.
* Ignore error because the ioctl may not be supported.
*/
VOP_IOCTL(ump->um_devvp, DIOCCACHESYNC, &force,
FWRITE, FSCRED, p);
}
#endif
}
return (0);
error_2: /* error with devvp held */
vrele (devvp);
error_1: /* no state to back out */
return (error);
}
struct ffs_reload_args {
struct fs *fs;
struct proc *p;
struct ucred *cred;
struct vnode *devvp;
};
int
ffs_reload_vnode(struct vnode *vp, void *args)
{
struct ffs_reload_args *fra = args;
struct inode *ip;
struct buf *bp;
int error;
/*
* Step 4: invalidate all inactive vnodes.
*/
if (vp->v_usecount == 0) {
vgonel(vp, fra->p);
return (0);
}
/*
* Step 5: invalidate all cached file data.
*/
if (vget(vp, LK_EXCLUSIVE))
return (0);
if (vinvalbuf(vp, 0, fra->cred, fra->p, 0, INFSLP))
panic("ffs_reload: dirty2");
/*
* Step 6: re-read inode data for all active vnodes.
*/
ip = VTOI(vp);
error = bread(fra->devvp,
fsbtodb(fra->fs, ino_to_fsba(fra->fs, ip->i_number)),
(int)fra->fs->fs_bsize, &bp);
if (error) {
brelse(bp);
vput(vp);
return (error);
}
if (fra->fs->fs_magic == FS_UFS1_MAGIC)
*ip->i_din1 = *((struct ufs1_dinode *)bp->b_data +
ino_to_fsbo(fra->fs, ip->i_number));
#ifdef FFS2
else
*ip->i_din2 = *((struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fra->fs, ip->i_number));
#endif /* FFS2 */
ip->i_effnlink = DIP(ip, nlink);
brelse(bp);
vput(vp);
return (0);
}
/*
* Reload all incore data for a filesystem (used after running fsck on
* the root filesystem and finding things to fix). The filesystem must
* be mounted read-only.
*
* Things to do to update the mount:
* 1) invalidate all cached meta-data.
* 2) re-read superblock from disk.
* 3) re-read summary information from disk.
* 4) invalidate all inactive vnodes.
* 5) invalidate all cached file data.
* 6) re-read inode data for all active vnodes.
*/
int
ffs_reload(struct mount *mountp, struct ucred *cred, struct proc *p)
{
struct vnode *devvp;
caddr_t space;
struct fs *fs, *newfs;
int i, blks, size, error;
int32_t *lp;
struct buf *bp = NULL;
struct ffs_reload_args fra;
if ((mountp->mnt_flag & MNT_RDONLY) == 0)
return (EINVAL);
/*
* Step 1: invalidate all cached meta-data.
*/
devvp = VFSTOUFS(mountp)->um_devvp;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, 0, cred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error)
panic("ffs_reload: dirty1");
/*
* Step 2: re-read superblock from disk.
*/
fs = VFSTOUFS(mountp)->um_fs;
error = bread(devvp, fs->fs_sblockloc / DEV_BSIZE, SBSIZE, &bp);
if (error) {
brelse(bp);
return (error);
}
newfs = (struct fs *)bp->b_data;
if (ffs_validate(newfs) == 0) {
brelse(bp);
return (EINVAL);
}
/*
* Copy pointer fields back into superblock before copying in XXX
* new superblock. These should really be in the ufsmount. XXX
* Note that important parameters (eg fs_ncg) are unchanged.
*/
newfs->fs_csp = fs->fs_csp;
newfs->fs_maxcluster = fs->fs_maxcluster;
newfs->fs_ronly = fs->fs_ronly;
memcpy(fs, newfs, fs->fs_sbsize);
if (fs->fs_sbsize < SBSIZE)
bp->b_flags |= B_INVAL;
brelse(bp);
VFSTOUFS(mountp)->um_maxsymlinklen = fs->fs_maxsymlinklen;
ffs1_compat_read(fs, VFSTOUFS(mountp), fs->fs_sblockloc);
ffs_oldfscompat(fs);
(void)ffs_statfs(mountp, &mountp->mnt_stat, p);
/*
* Step 3: re-read summary information from disk.
*/
blks = howmany(fs->fs_cssize, fs->fs_fsize);
space = (caddr_t)fs->fs_csp;
for (i = 0; i < blks; i += fs->fs_frag) {
size = fs->fs_bsize;
if (i + fs->fs_frag > blks)
size = (blks - i) * fs->fs_fsize;
error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, &bp);
if (error) {
brelse(bp);
return (error);
}
memcpy(space, bp->b_data, size);
space += size;
brelse(bp);
}
if ((fs->fs_flags & FS_DOSOFTDEP))
(void) softdep_mount(devvp, mountp, fs, cred);
/*
* We no longer know anything about clusters per cylinder group.
*/
if (fs->fs_contigsumsize > 0) {
lp = fs->fs_maxcluster;
for (i = 0; i < fs->fs_ncg; i++)
*lp++ = fs->fs_contigsumsize;
}
fra.p = p;
fra.cred = cred;
fra.fs = fs;
fra.devvp = devvp;
error = vfs_mount_foreach_vnode(mountp, ffs_reload_vnode, &fra);
return (error);
}
/*
* Checks if a super block is sane enough to be mounted.
*/
int
ffs_validate(struct fs *fsp)
{
#ifdef FFS2
if (fsp->fs_magic != FS_UFS2_MAGIC && fsp->fs_magic != FS_UFS1_MAGIC)
return (0); /* Invalid magic */
#else
if (fsp->fs_magic != FS_UFS1_MAGIC)
return (0); /* Invalid magic */
#endif /* FFS2 */
if ((u_int)fsp->fs_bsize > MAXBSIZE)
return (0); /* Invalid block size */
if ((u_int)fsp->fs_bsize < sizeof(struct fs))
return (0); /* Invalid block size */
if ((u_int)fsp->fs_sbsize > SBSIZE)
return (0); /* Invalid super block size */
if ((u_int)fsp->fs_frag > MAXFRAG || fragtbl[fsp->fs_frag] == NULL)
return (0); /* Invalid number of fragments */
if (fsp->fs_inodefmt == FS_42INODEFMT)
fsp->fs_maxsymlinklen = 0;
else if (fsp->fs_maxsymlinklen < 0)
return (0); /* Invalid max size of short symlink */
return (1); /* Super block is okay */
}
/*
* Possible locations for the super-block.
*/
const int sbtry[] = SBLOCKSEARCH;
/*
* Common code for mount and mountroot
*/
int
ffs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p)
{
struct ufsmount *ump;
struct buf *bp;
struct fs *fs;
dev_t dev;
caddr_t space;
daddr_t sbloc;
int error, i, blks, size, ronly;
int32_t *lp;
struct ucred *cred;
u_int64_t maxfilesize; /* XXX */
dev = devvp->v_rdev;
cred = p ? p->p_ucred : NOCRED;
/*
* Disallow multiple mounts of the same device.
* Disallow mounting of a device that is currently in use
* (except for root, which might share swap device for miniroot).
* Flush out any old buffers remaining from a previous use.
*/
if ((error = vfs_mountedon(devvp)) != 0)
return (error);
if (vcount(devvp) > 1 && devvp != rootvp)
return (EBUSY);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = vinvalbuf(devvp, V_SAVE, cred, p, 0, INFSLP);
VOP_UNLOCK(devvp);
if (error)
return (error);
ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
if (error)
return (error);
bp = NULL;
ump = NULL;
/*
* Try reading the super-block in each of its possible locations.
*/
for (i = 0; sbtry[i] != -1; i++) {
if (bp != NULL) {
bp->b_flags |= B_NOCACHE;
brelse(bp);
bp = NULL;
}
error = bread(devvp, sbtry[i] / DEV_BSIZE, SBSIZE, &bp);
if (error)
goto out;
fs = (struct fs *) bp->b_data;
sbloc = sbtry[i];
/*
* Do not look for an FFS1 file system at SBLOCK_UFS2. Doing so
* will find the wrong super-block for file systems with 64k
* block size.
*/
if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2)
continue;
if (ffs_validate(fs))
break; /* Super block validated */
}
if (sbtry[i] == -1) {
error = EINVAL;
goto out;
}
fs->fs_fmod = 0;
fs->fs_flags &= ~FS_UNCLEAN;
if (fs->fs_clean == 0) {
#if 0
/*
* It is safe to mount an unclean file system
* if it was previously mounted with softdep
* but we may lose space and must
* sometimes run fsck manually.
*/
if (fs->fs_flags & FS_DOSOFTDEP)
printf(
"WARNING: %s was not properly unmounted\n",
fs->fs_fsmnt);
else
#endif
if (ronly || (mp->mnt_flag & MNT_FORCE)) {
printf(
"WARNING: %s was not properly unmounted\n",
fs->fs_fsmnt);
} else {
printf(
"WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n",
fs->fs_fsmnt);
error = EROFS;
goto out;
}
}
if (fs->fs_postblformat == FS_42POSTBLFMT && !ronly) {
#ifndef SMALL_KERNEL
printf("ffs_mountfs(): obsolete rotational table format, "
"please use fsck_ffs(8) -c 1\n");
#endif
error = EROFS;
goto out;
}
ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK|M_ZERO);
ump->um_fs = malloc((u_long)fs->fs_sbsize, M_UFSMNT,
M_WAITOK);
if (fs->fs_magic == FS_UFS1_MAGIC)
ump->um_fstype = UM_UFS1;
#ifdef FFS2
else
ump->um_fstype = UM_UFS2;
#endif
memcpy(ump->um_fs, bp->b_data, fs->fs_sbsize);
if (fs->fs_sbsize < SBSIZE)
bp->b_flags |= B_INVAL;
brelse(bp);
bp = NULL;
fs = ump->um_fs;
ffs1_compat_read(fs, ump, sbloc);
if (fs->fs_clean == 0)
fs->fs_flags |= FS_UNCLEAN;
fs->fs_ronly = ronly;
size = fs->fs_cssize;
blks = howmany(size, fs->fs_fsize);
if (fs->fs_contigsumsize > 0)
size += fs->fs_ncg * sizeof(int32_t);
space = malloc((u_long)size, M_UFSMNT, M_WAITOK);
fs->fs_csp = (struct csum *)space;
for (i = 0; i < blks; i += fs->fs_frag) {
size = fs->fs_bsize;
if (i + fs->fs_frag > blks)
size = (blks - i) * fs->fs_fsize;
error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, &bp);
if (error) {
free(fs->fs_csp, M_UFSMNT, 0);
goto out;
}
memcpy(space, bp->b_data, size);
space += size;
brelse(bp);
bp = NULL;
}
if (fs->fs_contigsumsize > 0) {
fs->fs_maxcluster = lp = (int32_t *)space;
for (i = 0; i < fs->fs_ncg; i++)
*lp++ = fs->fs_contigsumsize;
}
mp->mnt_data = ump;
mp->mnt_stat.f_fsid.val[0] = (long)dev;
/* Use on-disk fsid if it exists, else fake it */
if (fs->fs_id[0] != 0 && fs->fs_id[1] != 0)
mp->mnt_stat.f_fsid.val[1] = fs->fs_id[1];
else
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
mp->mnt_stat.f_namemax = MAXNAMLEN;
mp->mnt_flag |= MNT_LOCAL;
ump->um_mountp = mp;
ump->um_dev = dev;
ump->um_devvp = devvp;
ump->um_nindir = fs->fs_nindir;
ump->um_bptrtodb = fs->fs_fsbtodb;
ump->um_seqinc = fs->fs_frag;
ump->um_maxsymlinklen = fs->fs_maxsymlinklen;
for (i = 0; i < MAXQUOTAS; i++)
ump->um_quotas[i] = NULLVP;
devvp->v_specmountpoint = mp;
ffs_oldfscompat(fs);
if (ronly)
fs->fs_contigdirs = NULL;
else {
fs->fs_contigdirs = malloc((u_long)fs->fs_ncg,
M_UFSMNT, M_WAITOK|M_ZERO);
}
/*
* Set FS local "last mounted on" information (NULL pad)
*/
memset(fs->fs_fsmnt, 0, sizeof(fs->fs_fsmnt));
strlcpy(fs->fs_fsmnt, mp->mnt_stat.f_mntonname, sizeof(fs->fs_fsmnt));
#if 0
if( mp->mnt_flag & MNT_ROOTFS) {
/*
* Root mount; update timestamp in mount structure.
* this will be used by the common root mount code
* to update the system clock.
*/
mp->mnt_time = fs->fs_time;
}
#endif
/*
* XXX
* Limit max file size. Even though ffs can handle files up to 16TB,
* we do limit the max file to 2^31 pages to prevent overflow of
* a 32-bit unsigned int. The buffer cache has its own checks but
* a little added paranoia never hurts.
*/
ump->um_savedmaxfilesize = fs->fs_maxfilesize; /* XXX */
maxfilesize = FS_KERNMAXFILESIZE(PAGE_SIZE, fs);
if (fs->fs_maxfilesize > maxfilesize) /* XXX */
fs->fs_maxfilesize = maxfilesize; /* XXX */
if (ronly == 0) {
if ((fs->fs_flags & FS_DOSOFTDEP) &&
(error = softdep_mount(devvp, mp, fs, cred)) != 0) {
free(fs->fs_csp, M_UFSMNT, 0);
free(fs->fs_contigdirs, M_UFSMNT, fs->fs_ncg);
goto out;
}
fs->fs_fmod = 1;
fs->fs_clean = 0;
if (mp->mnt_flag & MNT_SOFTDEP)
fs->fs_flags |= FS_DOSOFTDEP;
else
fs->fs_flags &= ~FS_DOSOFTDEP;
error = ffs_sbupdate(ump, MNT_WAIT);
if (error == EROFS)
goto out;
}
return (0);
out:
if (devvp->v_specinfo)
devvp->v_specmountpoint = NULL;
if (bp)
brelse(bp);
vn_lock(devvp, LK_EXCLUSIVE|LK_RETRY);
(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
VOP_UNLOCK(devvp);
if (ump) {
free(ump->um_fs, M_UFSMNT, ump->um_fs->fs_sbsize);
free(ump, M_UFSMNT, sizeof(*ump));
mp->mnt_data = NULL;
}
return (error);
}
/*
* Sanity checks for old file systems.
*/
int
ffs_oldfscompat(struct fs *fs)
{
int i;
fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */
fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */
if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
fs->fs_nrpos = 8; /* XXX */
if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
u_int64_t sizepb = fs->fs_bsize; /* XXX */
/* XXX */
fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */
for (i = 0; i < NIADDR; i++) { /* XXX */
sizepb *= NINDIR(fs); /* XXX */
fs->fs_maxfilesize += sizepb; /* XXX */
} /* XXX */
fs->fs_qbmask = ~fs->fs_bmask; /* XXX */
fs->fs_qfmask = ~fs->fs_fmask; /* XXX */
} /* XXX */
if (fs->fs_avgfilesize <= 0) /* XXX */
fs->fs_avgfilesize = AVFILESIZ; /* XXX */
if (fs->fs_avgfpdir <= 0) /* XXX */
fs->fs_avgfpdir = AFPDIR; /* XXX */
return (0);
}
/*
* Auxiliary function for reading FFS1 super blocks.
*/
void
ffs1_compat_read(struct fs *fs, struct ufsmount *ump, daddr_t sbloc)
{
if (fs->fs_magic == FS_UFS2_MAGIC)
return; /* UFS2 */
#if 0
if (fs->fs_ffs1_flags & FS_FLAGS_UPDATED)
return; /* Already updated */
#endif
fs->fs_flags = fs->fs_ffs1_flags;
fs->fs_sblockloc = sbloc;
fs->fs_maxbsize = fs->fs_bsize;
fs->fs_time = fs->fs_ffs1_time;
fs->fs_size = fs->fs_ffs1_size;
fs->fs_dsize = fs->fs_ffs1_dsize;
fs->fs_csaddr = fs->fs_ffs1_csaddr;
fs->fs_cstotal.cs_ndir = fs->fs_ffs1_cstotal.cs_ndir;
fs->fs_cstotal.cs_nbfree = fs->fs_ffs1_cstotal.cs_nbfree;
fs->fs_cstotal.cs_nifree = fs->fs_ffs1_cstotal.cs_nifree;
fs->fs_cstotal.cs_nffree = fs->fs_ffs1_cstotal.cs_nffree;
fs->fs_ffs1_flags |= FS_FLAGS_UPDATED;
}
/*
* Auxiliary function for writing FFS1 super blocks.
*/
void
ffs1_compat_write(struct fs *fs, struct ufsmount *ump)
{
if (fs->fs_magic != FS_UFS1_MAGIC)
return; /* UFS2 */
fs->fs_ffs1_time = fs->fs_time;
fs->fs_ffs1_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir;
fs->fs_ffs1_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree;
fs->fs_ffs1_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree;
fs->fs_ffs1_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree;
}
/*
* unmount system call
*/
int
ffs_unmount(struct mount *mp, int mntflags, struct proc *p)
{
struct ufsmount *ump;
struct fs *fs;
int error, flags;
flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
ump = VFSTOUFS(mp);
fs = ump->um_fs;
if (mp->mnt_flag & MNT_SOFTDEP)
error = softdep_flushfiles(mp, flags, p);
else
error = ffs_flushfiles(mp, flags, p);
if (error != 0)
return (error);
if (fs->fs_ronly == 0) {
fs->fs_clean = (fs->fs_flags & FS_UNCLEAN) ? 0 : 1;
error = ffs_sbupdate(ump, MNT_WAIT);
/* ignore write errors if mounted RW on read-only device */
if (error && error != EROFS) {
fs->fs_clean = 0;
return (error);
}
free(fs->fs_contigdirs, M_UFSMNT, fs->fs_ncg);
}
ump->um_devvp->v_specmountpoint = NULL;
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
vinvalbuf(ump->um_devvp, V_SAVE, NOCRED, p, 0, INFSLP);
(void)VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE,
NOCRED, p);
vput(ump->um_devvp);
free(fs->fs_csp, M_UFSMNT, 0);
free(fs, M_UFSMNT, fs->fs_sbsize);
free(ump, M_UFSMNT, sizeof(*ump));
mp->mnt_data = NULL;
mp->mnt_flag &= ~MNT_LOCAL;
return (0);
}
/*
* Flush out all the files in a filesystem.
*/
int
ffs_flushfiles(struct mount *mp, int flags, struct proc *p)
{
struct ufsmount *ump;
int error;
ump = VFSTOUFS(mp);
if (mp->mnt_flag & MNT_QUOTA) {
int i;
if ((error = vflush(mp, NULLVP, SKIPSYSTEM|flags)) != 0)
return (error);
for (i = 0; i < MAXQUOTAS; i++) {
if (ump->um_quotas[i] == NULLVP)
continue;
quotaoff(p, mp, i);
}
/*
* Here we fall through to vflush again to ensure
* that we have gotten rid of all the system vnodes.
*/
}
/*
* Flush all the files.
*/
if ((error = vflush(mp, NULL, flags)) != 0)
return (error);
/*
* Flush filesystem metadata.
*/
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_FSYNC(ump->um_devvp, p->p_ucred, MNT_WAIT, p);
VOP_UNLOCK(ump->um_devvp);
return (error);
}
/*
* Get file system statistics.
*/
int
ffs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
{
struct ufsmount *ump;
struct fs *fs;
ump = VFSTOUFS(mp);
fs = ump->um_fs;
#ifdef FFS2
if (fs->fs_magic != FS_MAGIC && fs->fs_magic != FS_UFS2_MAGIC)
panic("ffs_statfs");
#else
if (fs->fs_magic != FS_MAGIC)
panic("ffs_statfs");
#endif /* FFS2 */
sbp->f_bsize = fs->fs_fsize;
sbp->f_iosize = fs->fs_bsize;
sbp->f_blocks = fs->fs_dsize;
sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag +
fs->fs_cstotal.cs_nffree;
sbp->f_bavail = sbp->f_bfree -
((int64_t)fs->fs_dsize * fs->fs_minfree / 100);
sbp->f_files = fs->fs_ncg * fs->fs_ipg - ROOTINO;
sbp->f_ffree = fs->fs_cstotal.cs_nifree;
sbp->f_favail = sbp->f_ffree;
copy_statfs_info(sbp, mp);
return (0);
}
struct ffs_sync_args {
int allerror;
struct proc *p;
int waitfor;
int nlink0;
int inflight;
struct ucred *cred;
};
int
ffs_sync_vnode(struct vnode *vp, void *arg)
{
struct ffs_sync_args *fsa = arg;
struct inode *ip;
int error, nlink0 = 0;
int s, skip = 0;
if (vp->v_type == VNON)
return (0);
ip = VTOI(vp);
/*
* If unmounting or converting rw to ro, then stop deferring
* timestamp writes.
*/
if (fsa->waitfor == MNT_WAIT && (ip->i_flag & IN_LAZYMOD)) {
ip->i_flag |= IN_MODIFIED;
UFS_UPDATE(ip, 1);
}
if (ip->i_effnlink == 0)
nlink0 = 1;
s = splbio();
if ((ip->i_flag &
(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
LIST_EMPTY(&vp->v_dirtyblkhd)) {
skip = 1;
}
splx(s);
if (skip)
goto end;
if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT)) {
fsa->inflight = MIN(fsa->inflight+1, 65536);
goto end;
}
if ((error = VOP_FSYNC(vp, fsa->cred, fsa->waitfor, fsa->p)))
fsa->allerror = error;
VOP_UNLOCK(vp);
vrele(vp);
end:
fsa->nlink0 = MIN(fsa->nlink0 + nlink0, 65536);
return (0);
}
/*
* Go through the disk queues to initiate sandbagged IO;
* go through the inodes to write those that have been modified;
* initiate the writing of the super block if it has been modified.
*
* Should always be called with the mount point locked.
*/
int
ffs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, struct proc *p)
{
struct ufsmount *ump = VFSTOUFS(mp);
struct fs *fs;
int error, allerror = 0, count, clean, fmod;
struct ffs_sync_args fsa;
fs = ump->um_fs;
/*
* Write back modified superblock.
* Consistency check that the superblock
* is still in the buffer cache.
*/
if (fs->fs_fmod != 0 && fs->fs_ronly != 0) {
printf("fs = %s\n", fs->fs_fsmnt);
panic("update: rofs mod");
}
loop:
/*
* Write back each (modified) inode.
*/
fsa.allerror = 0;
fsa.p = p;
fsa.cred = cred;
fsa.waitfor = waitfor;
fsa.nlink0 = 0;
fsa.inflight = 0;
/*
* Don't traverse the vnode list if we want to skip all of them.
*/
if (waitfor != MNT_LAZY) {
vfs_mount_foreach_vnode(mp, ffs_sync_vnode, &fsa);
allerror = fsa.allerror;
}
/*
* Force stale file system control information to be flushed.
*/
if ((ump->um_mountp->mnt_flag & MNT_SOFTDEP) && waitfor == MNT_WAIT) {
if ((error = softdep_flushworklist(ump->um_mountp, &count, p)))
allerror = error;
/* Flushed work items may create new vnodes to clean */
if (count)
goto loop;
}
if (waitfor != MNT_LAZY) {
vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0)
allerror = error;
VOP_UNLOCK(ump->um_devvp);
}
qsync(mp);
/*
* Write back modified superblock.
*/
clean = fs->fs_clean;
fmod = fs->fs_fmod;
if (stall && fs->fs_ronly == 0) {
fs->fs_fmod = 1;
if (allerror == 0 && fsa.nlink0 == 0 && fsa.inflight == 0) {
fs->fs_clean = (fs->fs_flags & FS_UNCLEAN) ? 0 : 1;
#if 0
printf("%s force clean (dangling %d inflight %d)\n",
mp->mnt_stat.f_mntonname, fsa.nlink0, fsa.inflight);
#endif
} else {
fs->fs_clean = 0;
#if 0
printf("%s force dirty (dangling %d inflight %d)\n",
mp->mnt_stat.f_mntonname, fsa.nlink0, fsa.inflight);
#endif
}
}
if (fs->fs_fmod != 0 && (error = ffs_sbupdate(ump, waitfor)) != 0)
allerror = error;
fs->fs_clean = clean;
fs->fs_fmod = fmod;
return (allerror);
}
/*
* Look up a FFS dinode number to find its incore vnode, otherwise read it
* in from disk. If it is in core, wait for the lock bit to clear, then
* return the inode locked. Detection and handling of mount points must be
* done by the calling routine.
*/
int
ffs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
{
struct fs *fs;
struct inode *ip;
struct ufs1_dinode *dp1;
#ifdef FFS2
struct ufs2_dinode *dp2;
#endif
struct ufsmount *ump;
struct buf *bp;
struct vnode *vp;
dev_t dev;
int error;
if (ino > (ufsino_t)-1)
panic("ffs_vget: alien ino_t %llu", (unsigned long long)ino);
ump = VFSTOUFS(mp);
dev = ump->um_dev;
retry:
if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
return (0);
/* Allocate a new vnode/inode. */
if ((error = getnewvnode(VT_UFS, mp, &ffs_vops, &vp)) != 0) {
*vpp = NULL;
return (error);
}
#ifdef VFSLCKDEBUG
vp->v_flag |= VLOCKSWORK;
#endif
ip = pool_get(&ffs_ino_pool, PR_WAITOK|PR_ZERO);
rrw_init_flags(&ip->i_lock, "inode", RWL_DUPOK | RWL_IS_VNODE);
ip->i_ump = ump;
vref(ip->i_devvp);
vp->v_data = ip;
ip->i_vnode = vp;
ip->i_fs = fs = ump->um_fs;
ip->i_dev = dev;
ip->i_number = ino;
ip->i_vtbl = &ffs_vtbl;
/*
* Put it onto its hash chain and lock it so that other requests for
* this inode will block if they arrive while we are sleeping waiting
* for old data structures to be purged or for the contents of the
* disk portion of this inode to be read.
*/
error = ufs_ihashins(ip);
if (error) {
/*
* VOP_INACTIVE will treat this as a stale file
* and recycle it quickly
*/
vrele(vp);
if (error == EEXIST)
goto retry;
return (error);
}
/* Read in the disk contents for the inode, copy into the inode. */
error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
(int)fs->fs_bsize, &bp);
if (error) {
/*
* The inode does not contain anything useful, so it would
* be misleading to leave it on its hash chain. With mode
* still zero, it will be unlinked and returned to the free
* list by vput().
*/
vput(vp);
brelse(bp);
*vpp = NULL;
return (error);
}
#ifdef FFS2
if (ip->i_ump->um_fstype == UM_UFS2) {
ip->i_din2 = pool_get(&ffs_dinode2_pool, PR_WAITOK);
dp2 = (struct ufs2_dinode *) bp->b_data + ino_to_fsbo(fs, ino);
*ip->i_din2 = *dp2;
} else
#endif
{
ip->i_din1 = pool_get(&ffs_dinode1_pool, PR_WAITOK);
dp1 = (struct ufs1_dinode *) bp->b_data + ino_to_fsbo(fs, ino);
*ip->i_din1 = *dp1;
}
brelse(bp);
if (DOINGSOFTDEP(vp))
softdep_load_inodeblock(ip);
else
ip->i_effnlink = DIP(ip, nlink);
/*
* Initialize the vnode from the inode, check for aliases.
* Note that the underlying vnode may have changed.
*/
if ((error = ffs_vinit(mp, &vp)) != 0) {
vput(vp);
*vpp = NULL;
return (error);
}
/*
* Set up a generation number for this inode if it does not
* already have one. This should only happen on old filesystems.
*/
if (DIP(ip, gen) == 0) {
while (DIP(ip, gen) == 0)
DIP_ASSIGN(ip, gen, arc4random());
if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
ip->i_flag |= IN_MODIFIED;
}
/*
* Ensure that uid and gid are correct. This is a temporary
* fix until fsck has been changed to do the update.
*/
if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_inodefmt < FS_44INODEFMT) {
ip->i_ffs1_uid = ip->i_din1->di_ouid;
ip->i_ffs1_gid = ip->i_din1->di_ogid;
}
*vpp = vp;
return (0);
}
/*
* File handle to vnode
*
* Have to be really careful about stale file handles.
*/
int
ffs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
{
struct ufid *ufhp;
int error;
ufhp = (struct ufid *)fhp;
if (ufhp->ufid_len != sizeof(*ufhp))
return EINVAL;
if ((error = ffs_checkrange(mp, ufhp->ufid_ino)) != 0)
return error;
return (ufs_fhtovp(mp, ufhp, vpp));
}
/*
* Vnode pointer to File handle
*/
int
ffs_vptofh(struct vnode *vp, struct fid *fhp)
{
struct inode *ip;
struct ufid *ufhp;
ip = VTOI(vp);
ufhp = (struct ufid *)fhp;
ufhp->ufid_len = sizeof(struct ufid);
ufhp->ufid_ino = ip->i_number;
ufhp->ufid_gen = DIP(ip, gen);
return (0);
}
/*
* Write a superblock and associated information back to disk.
*/
int
ffs_sbupdate(struct ufsmount *mp, int waitfor)
{
struct fs *dfs, *fs = mp->um_fs;
struct buf *bp;
int blks;
caddr_t space;
int i, size, error, allerror = 0;
/*
* First write back the summary information.
*/
blks = howmany(fs->fs_cssize, fs->fs_fsize);
space = (caddr_t)fs->fs_csp;
for (i = 0; i < blks; i += fs->fs_frag) {
size = fs->fs_bsize;
if (i + fs->fs_frag > blks)
size = (blks - i) * fs->fs_fsize;
bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i),
size, 0, INFSLP);
memcpy(bp->b_data, space, size);
space += size;
if (waitfor != MNT_WAIT)
bawrite(bp);
else if ((error = bwrite(bp)))
allerror = error;
}
/*
* Now write back the superblock itself. If any errors occurred
* up to this point, then fail so that the superblock avoids
* being written out as clean.
*/
if (allerror) {
return (allerror);
}
bp = getblk(mp->um_devvp,
fs->fs_sblockloc >> (fs->fs_fshift - fs->fs_fsbtodb),
(int)fs->fs_sbsize, 0, INFSLP);
fs->fs_fmod = 0;
fs->fs_time = gettime();
memcpy(bp->b_data, fs, fs->fs_sbsize);
/* Restore compatibility to old file systems. XXX */
dfs = (struct fs *)bp->b_data; /* XXX */
if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
dfs->fs_nrpos = -1; /* XXX */
if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
int32_t *lp, tmp; /* XXX */
/* XXX */
lp = (int32_t *)&dfs->fs_qbmask; /* XXX */
tmp = lp[4]; /* XXX */
for (i = 4; i > 0; i--) /* XXX */
lp[i] = lp[i-1]; /* XXX */
lp[0] = tmp; /* XXX */
} /* XXX */
dfs->fs_maxfilesize = mp->um_savedmaxfilesize; /* XXX */
ffs1_compat_write(dfs, mp);
if (waitfor != MNT_WAIT)
bawrite(bp);
else if ((error = bwrite(bp)))
allerror = error;
return (allerror);
}
int
ffs_init(struct vfsconf *vfsp)
{
static int done;
if (done)
return (0);
done = 1;
pool_init(&ffs_ino_pool, sizeof(struct inode), 0, IPL_NONE,
PR_WAITOK, "ffsino", NULL);
pool_init(&ffs_dinode1_pool, sizeof(struct ufs1_dinode), 0, IPL_NONE,
PR_WAITOK, "dino1pl", NULL);
#ifdef FFS2
pool_init(&ffs_dinode2_pool, sizeof(struct ufs2_dinode), 0, IPL_NONE,
PR_WAITOK, "dino2pl", NULL);
#endif
softdep_initialize();
return (ufs_init(vfsp));
}
#ifdef FFS_SOFTUPDATES
extern int max_softdeps, tickdelay, stat_worklist_push;
extern int stat_blk_limit_push, stat_ino_limit_push, stat_blk_limit_hit;
extern int stat_ino_limit_hit, stat_sync_limit_hit, stat_indir_blk_ptrs;
extern int stat_inode_bitmap, stat_direct_blk_ptrs, stat_dir_entry;
#endif
const struct sysctl_bounded_args ffs_vars[] = {
#ifdef FFS_SOFTUPDATES
{ FFS_MAX_SOFTDEPS, &max_softdeps, 0, INT_MAX },
{ FFS_SD_TICKDELAY, &tickdelay, 2, INT_MAX },
{ FFS_SD_WORKLIST_PUSH, &stat_worklist_push, SYSCTL_INT_READONLY },
{ FFS_SD_BLK_LIMIT_PUSH, &stat_blk_limit_push, SYSCTL_INT_READONLY },
{ FFS_SD_INO_LIMIT_PUSH, &stat_ino_limit_push, SYSCTL_INT_READONLY },
{ FFS_SD_BLK_LIMIT_HIT, &stat_blk_limit_hit, SYSCTL_INT_READONLY },
{ FFS_SD_INO_LIMIT_HIT, &stat_ino_limit_hit, SYSCTL_INT_READONLY },
{ FFS_SD_SYNC_LIMIT_HIT, &stat_sync_limit_hit, SYSCTL_INT_READONLY },
{ FFS_SD_INDIR_BLK_PTRS, &stat_indir_blk_ptrs, SYSCTL_INT_READONLY },
{ FFS_SD_INODE_BITMAP, &stat_inode_bitmap, SYSCTL_INT_READONLY },
{ FFS_SD_DIRECT_BLK_PTRS, &stat_direct_blk_ptrs, SYSCTL_INT_READONLY },
{ FFS_SD_DIR_ENTRY, &stat_dir_entry, SYSCTL_INT_READONLY },
#endif
#ifdef UFS_DIRHASH
{ FFS_DIRHASH_DIRSIZE, &ufs_mindirhashsize, 0, INT_MAX },
{ FFS_DIRHASH_MAXMEM, &ufs_dirhashmaxmem, 0, INT_MAX },
{ FFS_DIRHASH_MEM, &ufs_dirhashmem, SYSCTL_INT_READONLY },
#endif
};
/*
* fast filesystem related variables.
*/
int
ffs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
return sysctl_bounded_arr(ffs_vars, nitems(ffs_vars), name,
namelen, oldp, oldlenp, newp, newlen);
}
72
2
69
1
80
3
856
1235
20
359
866
67
55
46
50
59
61
50
2
83
92
58
59
80
74
77
48
38
39
47
50
54
48
49
51
52
51
33
26
49
51
48
52
79
36
47
34
49
57
54
40
52
82
1
55
61
35
17
5
13
31
8
19
7
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
/* $OpenBSD: bpf_filter.c,v 1.34 2020/08/03 03:21:24 dlg Exp $ */
/* $NetBSD: bpf_filter.c,v 1.12 1996/02/13 22:00:00 christos Exp $ */
/*
* Copyright (c) 1990, 1991, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from the Stanford/CMU enet packet filter,
* (net/enet.c) distributed as part of 4.3BSD, and code contributed
* to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
* Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)bpf_filter.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/time.h>
#ifndef _KERNEL
#include <stdlib.h>
#include <string.h>
#include "pcap.h"
#else
#include <sys/systm.h>
#endif
#include <sys/endian.h>
#ifdef _KERNEL
extern int bpf_maxbufsize;
#define Static
#else /* _KERNEL */
#define Static static
#endif /* _KERNEL */
#include <net/bpf.h>
struct bpf_mem {
const u_char *pkt;
u_int len;
};
Static u_int32_t bpf_mem_ldw(const void *, u_int32_t, int *);
Static u_int32_t bpf_mem_ldh(const void *, u_int32_t, int *);
Static u_int32_t bpf_mem_ldb(const void *, u_int32_t, int *);
static const struct bpf_ops bpf_mem_ops = {
bpf_mem_ldw,
bpf_mem_ldh,
bpf_mem_ldb,
};
Static u_int32_t
bpf_mem_ldw(const void *mem, u_int32_t k, int *err)
{
const struct bpf_mem *bm = mem;
u_int32_t v;
*err = 1;
if (k + sizeof(v) > bm->len)
return (0);
memcpy(&v, bm->pkt + k, sizeof(v));
*err = 0;
return ntohl(v);
}
Static u_int32_t
bpf_mem_ldh(const void *mem, u_int32_t k, int *err)
{
const struct bpf_mem *bm = mem;
u_int16_t v;
*err = 1;
if (k + sizeof(v) > bm->len)
return (0);
memcpy(&v, bm->pkt + k, sizeof(v));
*err = 0;
return ntohs(v);
}
Static u_int32_t
bpf_mem_ldb(const void *mem, u_int32_t k, int *err)
{
const struct bpf_mem *bm = mem;
*err = 1;
if (k >= bm->len)
return (0);
*err = 0;
return bm->pkt[k];
}
/*
* Execute the filter program starting at pc on the packet p
* wirelen is the length of the original packet
* buflen is the amount of data present
*/
u_int
bpf_filter(const struct bpf_insn *pc, const u_char *pkt,
u_int wirelen, u_int buflen)
{
struct bpf_mem bm;
bm.pkt = pkt;
bm.len = buflen;
return _bpf_filter(pc, &bpf_mem_ops, &bm, wirelen);
}
u_int
_bpf_filter(const struct bpf_insn *pc, const struct bpf_ops *ops,
const void *pkt, u_int wirelen)
{
u_int32_t A = 0, X = 0;
u_int32_t k;
int32_t mem[BPF_MEMWORDS];
int err;
if (pc == NULL) {
/*
* No filter means accept all.
*/
return (u_int)-1;
}
memset(mem, 0, sizeof(mem));
--pc;
while (1) {
++pc;
switch (pc->code) {
default:
#ifdef _KERNEL
return 0;
#else
abort();
#endif
case BPF_RET|BPF_K:
return (u_int)pc->k;
case BPF_RET|BPF_A:
return (u_int)A;
case BPF_LD|BPF_W|BPF_ABS:
A = ops->ldw(pkt, pc->k, &err);
if (err != 0)
return 0;
continue;
case BPF_LD|BPF_H|BPF_ABS:
A = ops->ldh(pkt, pc->k, &err);
if (err != 0)
return 0;
continue;
case BPF_LD|BPF_B|BPF_ABS:
A = ops->ldb(pkt, pc->k, &err);
if (err != 0)
return 0;
continue;
case BPF_LD|BPF_W|BPF_LEN:
A = wirelen;
continue;
case BPF_LDX|BPF_W|BPF_LEN:
X = wirelen;
continue;
case BPF_LD|BPF_W|BPF_RND:
A = arc4random();
continue;
case BPF_LD|BPF_W|BPF_IND:
k = X + pc->k;
A = ops->ldw(pkt, k, &err);
if (err != 0)
return 0;
continue;
case BPF_LD|BPF_H|BPF_IND:
k = X + pc->k;
A = ops->ldh(pkt, k, &err);
if (err != 0)
return 0;
continue;
case BPF_LD|BPF_B|BPF_IND:
k = X + pc->k;
A = ops->ldb(pkt, k, &err);
if (err != 0)
return 0;
continue;
case BPF_LDX|BPF_MSH|BPF_B:
X = ops->ldb(pkt, pc->k, &err);
if (err != 0)
return 0;
X &= 0xf;
X <<= 2;
continue;
case BPF_LD|BPF_IMM:
A = pc->k;
continue;
case BPF_LDX|BPF_IMM:
X = pc->k;
continue;
case BPF_LD|BPF_MEM:
A = mem[pc->k];
continue;
case BPF_LDX|BPF_MEM:
X = mem[pc->k];
continue;
case BPF_ST:
mem[pc->k] = A;
continue;
case BPF_STX:
mem[pc->k] = X;
continue;
case BPF_JMP|BPF_JA:
pc += pc->k;
continue;
case BPF_JMP|BPF_JGT|BPF_K:
pc += (A > pc->k) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JGE|BPF_K:
pc += (A >= pc->k) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JEQ|BPF_K:
pc += (A == pc->k) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JSET|BPF_K:
pc += (A & pc->k) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JGT|BPF_X:
pc += (A > X) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JGE|BPF_X:
pc += (A >= X) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JEQ|BPF_X:
pc += (A == X) ? pc->jt : pc->jf;
continue;
case BPF_JMP|BPF_JSET|BPF_X:
pc += (A & X) ? pc->jt : pc->jf;
continue;
case BPF_ALU|BPF_ADD|BPF_X:
A += X;
continue;
case BPF_ALU|BPF_SUB|BPF_X:
A -= X;
continue;
case BPF_ALU|BPF_MUL|BPF_X:
A *= X;
continue;
case BPF_ALU|BPF_DIV|BPF_X:
if (X == 0)
return 0;
A /= X;
continue;
case BPF_ALU|BPF_AND|BPF_X:
A &= X;
continue;
case BPF_ALU|BPF_OR|BPF_X:
A |= X;
continue;
case BPF_ALU|BPF_LSH|BPF_X:
A <<= X;
continue;
case BPF_ALU|BPF_RSH|BPF_X:
A >>= X;
continue;
case BPF_ALU|BPF_ADD|BPF_K:
A += pc->k;
continue;
case BPF_ALU|BPF_SUB|BPF_K:
A -= pc->k;
continue;
case BPF_ALU|BPF_MUL|BPF_K:
A *= pc->k;
continue;
case BPF_ALU|BPF_DIV|BPF_K:
A /= pc->k;
continue;
case BPF_ALU|BPF_AND|BPF_K:
A &= pc->k;
continue;
case BPF_ALU|BPF_OR|BPF_K:
A |= pc->k;
continue;
case BPF_ALU|BPF_LSH|BPF_K:
A <<= pc->k;
continue;
case BPF_ALU|BPF_RSH|BPF_K:
A >>= pc->k;
continue;
case BPF_ALU|BPF_NEG:
A = -A;
continue;
case BPF_MISC|BPF_TAX:
X = A;
continue;
case BPF_MISC|BPF_TXA:
A = X;
continue;
}
}
}
#ifdef _KERNEL
/*
* Return true if the 'fcode' is a valid filter program.
* The constraints are that each jump be forward and to a valid
* code and memory operations use valid addresses. The code
* must terminate with either an accept or reject.
*
* The kernel needs to be able to verify an application's filter code.
* Otherwise, a bogus program could easily crash the system.
*/
int
bpf_validate(struct bpf_insn *f, int len)
{
u_int i, from;
struct bpf_insn *p;
if (len < 1 || len > BPF_MAXINSNS)
return 0;
for (i = 0; i < len; ++i) {
p = &f[i];
switch (BPF_CLASS(p->code)) {
/*
* Check that memory operations use valid addresses.
*/
case BPF_LD:
case BPF_LDX:
switch (BPF_MODE(p->code)) {
case BPF_IMM:
break;
case BPF_ABS:
case BPF_IND:
case BPF_MSH:
/*
* More strict check with actual packet length
* is done runtime.
*/
if (p->k >= bpf_maxbufsize)
return 0;
break;
case BPF_MEM:
if (p->k >= BPF_MEMWORDS)
return 0;
break;
case BPF_LEN:
case BPF_RND:
break;
default:
return 0;
}
break;
case BPF_ST:
case BPF_STX:
if (p->k >= BPF_MEMWORDS)
return 0;
break;
case BPF_ALU:
switch (BPF_OP(p->code)) {
case BPF_ADD:
case BPF_SUB:
case BPF_MUL:
case BPF_OR:
case BPF_AND:
case BPF_LSH:
case BPF_RSH:
case BPF_NEG:
break;
case BPF_DIV:
/*
* Check for constant division by 0.
*/
if (BPF_SRC(p->code) == BPF_K && p->k == 0)
return 0;
break;
default:
return 0;
}
break;
case BPF_JMP:
/*
* Check that jumps are forward, and within
* the code block.
*/
from = i + 1;
switch (BPF_OP(p->code)) {
case BPF_JA:
if (from + p->k < from || from + p->k >= len)
return 0;
break;
case BPF_JEQ:
case BPF_JGT:
case BPF_JGE:
case BPF_JSET:
if (from + p->jt >= len || from + p->jf >= len)
return 0;
break;
default:
return 0;
}
break;
case BPF_RET:
break;
case BPF_MISC:
break;
default:
return 0;
}
}
return BPF_CLASS(f[len - 1].code) == BPF_RET;
}
#endif
1646
1678
2277
2280
2
2
60
60
2
2
2
51
375
374
374
376
253
2420
53
2412
3
2
1652
1236
2226
313
28
108
255
94
315
253
2561
1162
2173
152
151
1035
1027
19
1036
227
226
226
188
101
182
19
131
22
214
388
389
189
190
27
78
190
191
76
172
21
2
2
1
2
1565
9
1563
1564
5
12
30
14
16
34
529
40
9
4
4
4
1036
77
10
98
78
1873
1874
212
211
3
2
1
18
18
6
6
10
104
37
1000
183
864
30
30
19
20
16
17
15
31
38
5
13
1
1
1
122
122
4
25
5
9
4
2
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
/* $OpenBSD: uipc_mbuf.c,v 1.284 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
*/
/*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/mbuf.h>
#include <sys/pool.h>
#include <sys/percpu.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <uvm/uvm_extern.h>
#ifdef DDB
#include <machine/db_machdep.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif /* NPF > 0 */
/* mbuf stats */
COUNTERS_BOOT_MEMORY(mbstat_boot, MBSTAT_COUNT);
struct cpumem *mbstat = COUNTERS_BOOT_INITIALIZER(mbstat_boot);
/* mbuf pools */
struct pool mbpool;
struct pool mtagpool;
/* mbuf cluster pools */
u_int mclsizes[MCLPOOLS] = {
MCLBYTES, /* must be at slot 0 */
MCLBYTES + 2, /* ETHER_ALIGNED 2k mbufs */
4 * 1024,
8 * 1024,
9 * 1024,
12 * 1024,
16 * 1024,
64 * 1024
};
static char mclnames[MCLPOOLS][8];
struct pool mclpools[MCLPOOLS];
struct pool *m_clpool(u_int);
int max_linkhdr; /* largest link-level header */
int max_protohdr; /* largest protocol header */
int max_hdr; /* largest link+protocol header */
struct mutex m_extref_mtx = MUTEX_INITIALIZER(IPL_NET);
void m_extfree(struct mbuf *);
void m_zero(struct mbuf *);
unsigned long mbuf_mem_limit; /* how much memory can be allocated */
unsigned long mbuf_mem_alloc; /* how much memory has been allocated */
void *m_pool_alloc(struct pool *, int, int *);
void m_pool_free(struct pool *, void *);
struct pool_allocator m_pool_allocator = {
m_pool_alloc,
m_pool_free,
0 /* will be copied from pool_allocator_multi */
};
static void (*mextfree_fns[4])(caddr_t, u_int, void *);
static u_int num_extfree_fns;
#define M_DATABUF(m) ((m)->m_flags & M_EXT ? (m)->m_ext.ext_buf : \
(m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat)
#define M_SIZE(m) ((m)->m_flags & M_EXT ? (m)->m_ext.ext_size : \
(m)->m_flags & M_PKTHDR ? MHLEN : MLEN)
/*
* Initialize the mbuf allocator.
*/
void
mbinit(void)
{
int i, error;
unsigned int lowbits;
CTASSERT(MSIZE == sizeof(struct mbuf));
m_pool_allocator.pa_pagesz = pool_allocator_multi.pa_pagesz;
mbuf_mem_alloc = 0;
#if DIAGNOSTIC
if (mclsizes[0] != MCLBYTES)
panic("mbinit: the smallest cluster size != MCLBYTES");
if (mclsizes[nitems(mclsizes) - 1] != MAXMCLBYTES)
panic("mbinit: the largest cluster size != MAXMCLBYTES");
#endif
m_pool_init(&mbpool, MSIZE, 64, "mbufpl");
pool_init(&mtagpool, PACKET_TAG_MAXSIZE + sizeof(struct m_tag), 0,
IPL_NET, 0, "mtagpl", NULL);
for (i = 0; i < nitems(mclsizes); i++) {
lowbits = mclsizes[i] & ((1 << 10) - 1);
if (lowbits) {
snprintf(mclnames[i], sizeof(mclnames[0]),
"mcl%dk%u", mclsizes[i] >> 10, lowbits);
} else {
snprintf(mclnames[i], sizeof(mclnames[0]), "mcl%dk",
mclsizes[i] >> 10);
}
m_pool_init(&mclpools[i], mclsizes[i], 64, mclnames[i]);
}
error = nmbclust_update(nmbclust);
KASSERT(error == 0);
(void)mextfree_register(m_extfree_pool);
KASSERT(num_extfree_fns == 1);
}
void
mbcpuinit(void)
{
int i;
mbstat = counters_alloc_ncpus(mbstat, MBSTAT_COUNT);
pool_cache_init(&mbpool);
pool_cache_init(&mtagpool);
for (i = 0; i < nitems(mclsizes); i++)
pool_cache_init(&mclpools[i]);
}
int
nmbclust_update(long newval)
{
int i;
if (newval < 0 || newval > LONG_MAX / MCLBYTES)
return ERANGE;
/* update the global mbuf memory limit */
nmbclust = newval;
mbuf_mem_limit = nmbclust * MCLBYTES;
pool_wakeup(&mbpool);
for (i = 0; i < nitems(mclsizes); i++)
pool_wakeup(&mclpools[i]);
return 0;
}
/*
* Space allocation routines.
*/
struct mbuf *
m_get(int nowait, int type)
{
struct mbuf *m;
struct counters_ref cr;
uint64_t *counters;
int s;
KASSERT(type >= 0 && type < MT_NTYPES);
m = pool_get(&mbpool, nowait == M_WAIT ? PR_WAITOK : PR_NOWAIT);
if (m == NULL)
return (NULL);
s = splnet();
counters = counters_enter(&cr, mbstat);
counters[type]++;
counters_leave(&cr, mbstat);
splx(s);
m->m_type = type;
m->m_next = NULL;
m->m_nextpkt = NULL;
m->m_data = m->m_dat;
m->m_flags = 0;
return (m);
}
/*
* ATTN: When changing anything here check m_inithdr() and m_defrag() those
* may need to change as well.
*/
struct mbuf *
m_gethdr(int nowait, int type)
{
struct mbuf *m;
struct counters_ref cr;
uint64_t *counters;
int s;
KASSERT(type >= 0 && type < MT_NTYPES);
m = pool_get(&mbpool, nowait == M_WAIT ? PR_WAITOK : PR_NOWAIT);
if (m == NULL)
return (NULL);
s = splnet();
counters = counters_enter(&cr, mbstat);
counters[type]++;
counters_leave(&cr, mbstat);
splx(s);
m->m_type = type;
return (m_inithdr(m));
}
struct mbuf *
m_inithdr(struct mbuf *m)
{
/* keep in sync with m_gethdr */
m->m_next = NULL;
m->m_nextpkt = NULL;
m->m_data = m->m_pktdat;
m->m_flags = M_PKTHDR;
memset(&m->m_pkthdr, 0, sizeof(m->m_pkthdr));
m->m_pkthdr.pf.prio = IFQ_DEFPRIO;
return (m);
}
static inline void
m_clearhdr(struct mbuf *m)
{
/* delete all mbuf tags to reset the state */
m_tag_delete_chain(m);
#if NPF > 0
pf_mbuf_unlink_state_key(m);
pf_mbuf_unlink_inpcb(m);
#endif /* NPF > 0 */
memset(&m->m_pkthdr, 0, sizeof(m->m_pkthdr));
}
void
m_removehdr(struct mbuf *m)
{
KASSERT(m->m_flags & M_PKTHDR);
m_clearhdr(m);
m->m_flags &= ~M_PKTHDR;
}
void
m_resethdr(struct mbuf *m)
{
int len = m->m_pkthdr.len;
u_int8_t loopcnt = m->m_pkthdr.ph_loopcnt;
KASSERT(m->m_flags & M_PKTHDR);
m->m_flags &= (M_EXT|M_PKTHDR|M_EOR|M_EXTWR|M_ZEROIZE);
m_clearhdr(m);
/* like m_inithdr(), but keep any associated data and mbufs */
m->m_pkthdr.pf.prio = IFQ_DEFPRIO;
m->m_pkthdr.len = len;
m->m_pkthdr.ph_loopcnt = loopcnt;
}
void
m_calchdrlen(struct mbuf *m)
{
struct mbuf *n;
int plen = 0;
KASSERT(m->m_flags & M_PKTHDR);
for (n = m; n; n = n->m_next)
plen += n->m_len;
m->m_pkthdr.len = plen;
}
struct mbuf *
m_getclr(int nowait, int type)
{
struct mbuf *m;
MGET(m, nowait, type);
if (m == NULL)
return (NULL);
memset(mtod(m, caddr_t), 0, MLEN);
return (m);
}
struct pool *
m_clpool(u_int pktlen)
{
struct pool *pp;
int pi;
for (pi = 0; pi < nitems(mclpools); pi++) {
pp = &mclpools[pi];
if (pktlen <= pp->pr_size)
return (pp);
}
return (NULL);
}
struct mbuf *
m_clget(struct mbuf *m, int how, u_int pktlen)
{
struct mbuf *m0 = NULL;
struct pool *pp;
caddr_t buf;
pp = m_clpool(pktlen);
#ifdef DIAGNOSTIC
if (pp == NULL)
panic("m_clget: request for %u byte cluster", pktlen);
#endif
if (m == NULL) {
m0 = m_gethdr(how, MT_DATA);
if (m0 == NULL)
return (NULL);
m = m0;
}
buf = pool_get(pp, how == M_WAIT ? PR_WAITOK : PR_NOWAIT);
if (buf == NULL) {
m_freem(m0);
return (NULL);
}
MEXTADD(m, buf, pp->pr_size, M_EXTWR, MEXTFREE_POOL, pp);
return (m);
}
void
m_extfree_pool(caddr_t buf, u_int size, void *pp)
{
pool_put(pp, buf);
}
struct mbuf *
m_free(struct mbuf *m)
{
struct mbuf *n;
struct counters_ref cr;
uint64_t *counters;
int s;
if (m == NULL)
return (NULL);
s = splnet();
counters = counters_enter(&cr, mbstat);
counters[m->m_type]--;
counters_leave(&cr, mbstat);
splx(s);
n = m->m_next;
if (m->m_flags & M_ZEROIZE) {
m_zero(m);
/* propagate M_ZEROIZE to the next mbuf in the chain */
if (n)
n->m_flags |= M_ZEROIZE;
}
if (m->m_flags & M_PKTHDR) {
m_tag_delete_chain(m);
#if NPF > 0
pf_mbuf_unlink_state_key(m);
pf_mbuf_unlink_inpcb(m);
#endif /* NPF > 0 */
}
if (m->m_flags & M_EXT)
m_extfree(m);
pool_put(&mbpool, m);
return (n);
}
void
m_extref(struct mbuf *o, struct mbuf *n)
{
int refs = MCLISREFERENCED(o);
n->m_flags |= o->m_flags & (M_EXT|M_EXTWR);
if (refs)
mtx_enter(&m_extref_mtx);
n->m_ext.ext_nextref = o->m_ext.ext_nextref;
n->m_ext.ext_prevref = o;
o->m_ext.ext_nextref = n;
n->m_ext.ext_nextref->m_ext.ext_prevref = n;
if (refs)
mtx_leave(&m_extref_mtx);
MCLREFDEBUGN((n), __FILE__, __LINE__);
}
static inline u_int
m_extunref(struct mbuf *m)
{
int refs = 0;
if (!MCLISREFERENCED(m))
return (0);
mtx_enter(&m_extref_mtx);
if (MCLISREFERENCED(m)) {
m->m_ext.ext_nextref->m_ext.ext_prevref =
m->m_ext.ext_prevref;
m->m_ext.ext_prevref->m_ext.ext_nextref =
m->m_ext.ext_nextref;
refs = 1;
}
mtx_leave(&m_extref_mtx);
return (refs);
}
/*
* Returns a number for use with MEXTADD.
* Should only be called once per function.
* Drivers can be assured that the index will be non zero.
*/
u_int
mextfree_register(void (*fn)(caddr_t, u_int, void *))
{
KASSERT(num_extfree_fns < nitems(mextfree_fns));
mextfree_fns[num_extfree_fns] = fn;
return num_extfree_fns++;
}
void
m_extfree(struct mbuf *m)
{
if (m_extunref(m) == 0) {
KASSERT(m->m_ext.ext_free_fn < num_extfree_fns);
mextfree_fns[m->m_ext.ext_free_fn](m->m_ext.ext_buf,
m->m_ext.ext_size, m->m_ext.ext_arg);
}
m->m_flags &= ~(M_EXT|M_EXTWR);
}
struct mbuf *
m_freem(struct mbuf *m)
{
struct mbuf *n;
if (m == NULL)
return (NULL);
n = m->m_nextpkt;
do
m = m_free(m);
while (m != NULL);
return (n);
}
void
m_purge(struct mbuf *m)
{
while (m != NULL)
m = m_freem(m);
}
/*
* mbuf chain defragmenter. This function uses some evil tricks to defragment
* an mbuf chain into a single buffer without changing the mbuf pointer.
* This needs to know a lot of the mbuf internals to make this work.
*/
int
m_defrag(struct mbuf *m, int how)
{
struct mbuf *m0;
if (m->m_next == NULL)
return (0);
KASSERT(m->m_flags & M_PKTHDR);
if ((m0 = m_gethdr(how, m->m_type)) == NULL)
return (ENOBUFS);
if (m->m_pkthdr.len > MHLEN) {
MCLGETL(m0, how, m->m_pkthdr.len);
if (!(m0->m_flags & M_EXT)) {
m_free(m0);
return (ENOBUFS);
}
}
m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t));
m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len;
/* free chain behind and possible ext buf on the first mbuf */
m_freem(m->m_next);
m->m_next = NULL;
if (m->m_flags & M_EXT)
m_extfree(m);
/*
* Bounce copy mbuf over to the original mbuf and set everything up.
* This needs to reset or clear all pointers that may go into the
* original mbuf chain.
*/
if (m0->m_flags & M_EXT) {
memcpy(&m->m_ext, &m0->m_ext, sizeof(struct mbuf_ext));
MCLINITREFERENCE(m);
m->m_flags |= m0->m_flags & (M_EXT|M_EXTWR);
m->m_data = m->m_ext.ext_buf;
} else {
m->m_data = m->m_pktdat;
memcpy(m->m_data, m0->m_data, m0->m_len);
}
m->m_pkthdr.len = m->m_len = m0->m_len;
m0->m_flags &= ~(M_EXT|M_EXTWR); /* cluster is gone */
m_free(m0);
return (0);
}
/*
* Mbuffer utility routines.
*/
/*
* Ensure len bytes of contiguous space at the beginning of the mbuf chain
*/
struct mbuf *
m_prepend(struct mbuf *m, int len, int how)
{
struct mbuf *mn;
if (len > MHLEN)
panic("mbuf prepend length too big");
if (m_leadingspace(m) >= len) {
m->m_data -= len;
m->m_len += len;
} else {
MGET(mn, how, m->m_type);
if (mn == NULL) {
m_freem(m);
return (NULL);
}
if (m->m_flags & M_PKTHDR)
M_MOVE_PKTHDR(mn, m);
mn->m_next = m;
m = mn;
m_align(m, len);
m->m_len = len;
}
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len += len;
return (m);
}
/*
* Make a copy of an mbuf chain starting "off" bytes from the beginning,
* continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf.
* The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller.
*/
struct mbuf *
m_copym(struct mbuf *m0, int off, int len, int wait)
{
struct mbuf *m, *n, **np;
struct mbuf *top;
int copyhdr = 0;
if (off < 0 || len < 0)
panic("m_copym0: off %d, len %d", off, len);
if (off == 0 && m0->m_flags & M_PKTHDR)
copyhdr = 1;
if ((m = m_getptr(m0, off, &off)) == NULL)
panic("m_copym0: short mbuf chain");
np = ⊤
top = NULL;
while (len > 0) {
if (m == NULL) {
if (len != M_COPYALL)
panic("m_copym0: m == NULL and not COPYALL");
break;
}
MGET(n, wait, m->m_type);
*np = n;
if (n == NULL)
goto nospace;
if (copyhdr) {
if (m_dup_pkthdr(n, m0, wait))
goto nospace;
if (len != M_COPYALL)
n->m_pkthdr.len = len;
copyhdr = 0;
}
n->m_len = min(len, m->m_len - off);
if (m->m_flags & M_EXT) {
n->m_data = m->m_data + off;
n->m_ext = m->m_ext;
MCLADDREFERENCE(m, n);
} else {
n->m_data += m->m_data -
(m->m_flags & M_PKTHDR ? m->m_pktdat : m->m_dat);
n->m_data += off;
memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + off,
n->m_len);
}
if (len != M_COPYALL)
len -= n->m_len;
off += n->m_len;
#ifdef DIAGNOSTIC
if (off > m->m_len)
panic("m_copym0 overrun");
#endif
if (off == m->m_len) {
m = m->m_next;
off = 0;
}
np = &n->m_next;
}
return (top);
nospace:
m_freem(top);
return (NULL);
}
/*
* Copy data from an mbuf chain starting "off" bytes from the beginning,
* continuing for "len" bytes, into the indicated buffer.
*/
void
m_copydata(struct mbuf *m, int off, int len, void *p)
{
caddr_t cp = p;
unsigned count;
if (off < 0)
panic("m_copydata: off %d < 0", off);
if (len < 0)
panic("m_copydata: len %d < 0", len);
if ((m = m_getptr(m, off, &off)) == NULL)
panic("m_copydata: short mbuf chain");
while (len > 0) {
if (m == NULL)
panic("m_copydata: null mbuf");
count = min(m->m_len - off, len);
memmove(cp, mtod(m, caddr_t) + off, count);
len -= count;
cp += count;
off = 0;
m = m->m_next;
}
}
/*
* Copy data from a buffer back into the indicated mbuf chain,
* starting "off" bytes from the beginning, extending the mbuf
* chain if necessary. The mbuf needs to be properly initialized
* including the setting of m_len.
*/
int
m_copyback(struct mbuf *m0, int off, int len, const void *_cp, int wait)
{
int mlen, totlen = 0;
struct mbuf *m = m0, *n;
caddr_t cp = (caddr_t)_cp;
int error = 0;
if (m0 == NULL)
return (0);
while (off > (mlen = m->m_len)) {
off -= mlen;
totlen += mlen;
if (m->m_next == NULL) {
if ((n = m_get(wait, m->m_type)) == NULL) {
error = ENOBUFS;
goto out;
}
if (off + len > MLEN) {
MCLGETL(n, wait, off + len);
if (!(n->m_flags & M_EXT)) {
m_free(n);
error = ENOBUFS;
goto out;
}
}
memset(mtod(n, caddr_t), 0, off);
n->m_len = len + off;
m->m_next = n;
}
m = m->m_next;
}
while (len > 0) {
/* extend last packet to be filled fully */
if (m->m_next == NULL && (len > m->m_len - off))
m->m_len += min(len - (m->m_len - off),
m_trailingspace(m));
mlen = min(m->m_len - off, len);
memmove(mtod(m, caddr_t) + off, cp, mlen);
cp += mlen;
len -= mlen;
totlen += mlen + off;
if (len == 0)
break;
off = 0;
if (m->m_next == NULL) {
if ((n = m_get(wait, m->m_type)) == NULL) {
error = ENOBUFS;
goto out;
}
if (len > MLEN) {
MCLGETL(n, wait, len);
if (!(n->m_flags & M_EXT)) {
m_free(n);
error = ENOBUFS;
goto out;
}
}
n->m_len = len;
m->m_next = n;
}
m = m->m_next;
}
out:
if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
m->m_pkthdr.len = totlen;
return (error);
}
/*
* Concatenate mbuf chain n to m.
* n might be copied into m (when n->m_len is small), therefore data portion of
* n could be copied into an mbuf of different mbuf type.
* Therefore both chains should be of the same type (e.g. MT_DATA).
* Any m_pkthdr is not updated.
*/
void
m_cat(struct mbuf *m, struct mbuf *n)
{
while (m->m_next)
m = m->m_next;
while (n) {
if (M_READONLY(m) || n->m_len > m_trailingspace(m)) {
/* just join the two chains */
m->m_next = n;
return;
}
/* splat the data from one into the other */
memcpy(mtod(m, caddr_t) + m->m_len, mtod(n, caddr_t),
n->m_len);
m->m_len += n->m_len;
n = m_free(n);
}
}
void
m_adj(struct mbuf *mp, int req_len)
{
int len = req_len;
struct mbuf *m;
int count;
if (mp == NULL)
return;
if (len >= 0) {
/*
* Trim from head.
*/
m = mp;
while (m != NULL && len > 0) {
if (m->m_len <= len) {
len -= m->m_len;
m->m_data += m->m_len;
m->m_len = 0;
m = m->m_next;
} else {
m->m_data += len;
m->m_len -= len;
len = 0;
}
}
if (mp->m_flags & M_PKTHDR)
mp->m_pkthdr.len -= (req_len - len);
} else {
/*
* Trim from tail. Scan the mbuf chain,
* calculating its length and finding the last mbuf.
* If the adjustment only affects this mbuf, then just
* adjust and return. Otherwise, rescan and truncate
* after the remaining size.
*/
len = -len;
count = 0;
m = mp;
for (;;) {
count += m->m_len;
if (m->m_next == NULL)
break;
m = m->m_next;
}
if (m->m_len >= len) {
m->m_len -= len;
if (mp->m_flags & M_PKTHDR)
mp->m_pkthdr.len -= len;
return;
}
count -= len;
if (count < 0)
count = 0;
/*
* Correct length for chain is "count".
* Find the mbuf with last data, adjust its length,
* and toss data from remaining mbufs on chain.
*/
if (mp->m_flags & M_PKTHDR)
mp->m_pkthdr.len = count;
m = mp;
for (;;) {
if (m->m_len >= count) {
m->m_len = count;
break;
}
count -= m->m_len;
m = m->m_next;
}
while ((m = m->m_next) != NULL)
m->m_len = 0;
}
}
/*
* Rearrange an mbuf chain so that len bytes are contiguous
* and in the data area of an mbuf (so that mtod will work
* for a structure of size len). Returns the resulting
* mbuf chain on success, frees it and returns null on failure.
*/
struct mbuf *
m_pullup(struct mbuf *m0, int len)
{
struct mbuf *m;
unsigned int adj;
caddr_t head, tail;
unsigned int space;
/* if len is already contig in m0, then don't do any work */
if (len <= m0->m_len)
return (m0);
/* look for some data */
m = m0->m_next;
if (m == NULL)
goto freem0;
head = M_DATABUF(m0);
if (m0->m_len == 0) {
while (m->m_len == 0) {
m = m_free(m);
if (m == NULL)
goto freem0;
}
adj = mtod(m, unsigned long) & (sizeof(long) - 1);
} else
adj = mtod(m0, unsigned long) & (sizeof(long) - 1);
tail = head + M_SIZE(m0);
head += adj;
if (!M_READONLY(m0) && len <= tail - head) {
/* we can copy everything into the first mbuf */
if (m0->m_len == 0) {
m0->m_data = head;
} else if (len > tail - mtod(m0, caddr_t)) {
/* need to memmove to make space at the end */
memmove(head, mtod(m0, caddr_t), m0->m_len);
m0->m_data = head;
}
len -= m0->m_len;
} else {
/* the first mbuf is too small or read-only, make a new one */
space = adj + len;
if (space > MAXMCLBYTES)
goto bad;
m0->m_next = m;
m = m0;
MGET(m0, M_DONTWAIT, m->m_type);
if (m0 == NULL)
goto bad;
if (space > MHLEN) {
MCLGETL(m0, M_DONTWAIT, space);
if ((m0->m_flags & M_EXT) == 0)
goto bad;
}
if (m->m_flags & M_PKTHDR)
M_MOVE_PKTHDR(m0, m);
m0->m_len = 0;
m0->m_data += adj;
}
KDASSERT(m_trailingspace(m0) >= len);
for (;;) {
space = min(len, m->m_len);
memcpy(mtod(m0, caddr_t) + m0->m_len, mtod(m, caddr_t), space);
len -= space;
m0->m_len += space;
m->m_len -= space;
if (m->m_len > 0)
m->m_data += space;
else
m = m_free(m);
if (len == 0)
break;
if (m == NULL)
goto bad;
}
m0->m_next = m; /* link the chain back up */
return (m0);
bad:
m_freem(m);
freem0:
m_free(m0);
return (NULL);
}
/*
* Return a pointer to mbuf/offset of location in mbuf chain.
*/
struct mbuf *
m_getptr(struct mbuf *m, int loc, int *off)
{
while (loc >= 0) {
/* Normal end of search */
if (m->m_len > loc) {
*off = loc;
return (m);
} else {
loc -= m->m_len;
if (m->m_next == NULL) {
if (loc == 0) {
/* Point at the end of valid data */
*off = m->m_len;
return (m);
} else {
return (NULL);
}
} else {
m = m->m_next;
}
}
}
return (NULL);
}
/*
* Partition an mbuf chain in two pieces, returning the tail --
* all but the first len0 bytes. In case of failure, it returns NULL and
* attempts to restore the chain to its original state.
*/
struct mbuf *
m_split(struct mbuf *m0, int len0, int wait)
{
struct mbuf *m, *n;
unsigned len = len0, remain, olen;
for (m = m0; m && len > m->m_len; m = m->m_next)
len -= m->m_len;
if (m == NULL)
return (NULL);
remain = m->m_len - len;
if (m0->m_flags & M_PKTHDR) {
MGETHDR(n, wait, m0->m_type);
if (n == NULL)
return (NULL);
if (m_dup_pkthdr(n, m0, wait)) {
m_freem(n);
return (NULL);
}
n->m_pkthdr.len -= len0;
olen = m0->m_pkthdr.len;
m0->m_pkthdr.len = len0;
if (remain == 0) {
n->m_next = m->m_next;
m->m_next = NULL;
n->m_len = 0;
return (n);
}
if (m->m_flags & M_EXT)
goto extpacket;
if (remain > MHLEN) {
/* m can't be the lead packet */
m_align(n, 0);
n->m_next = m_split(m, len, wait);
if (n->m_next == NULL) {
(void) m_free(n);
m0->m_pkthdr.len = olen;
return (NULL);
} else {
n->m_len = 0;
return (n);
}
} else
m_align(n, remain);
} else if (remain == 0) {
n = m->m_next;
m->m_next = NULL;
return (n);
} else {
MGET(n, wait, m->m_type);
if (n == NULL)
return (NULL);
m_align(n, remain);
}
extpacket:
if (m->m_flags & M_EXT) {
n->m_ext = m->m_ext;
MCLADDREFERENCE(m, n);
n->m_data = m->m_data + len;
} else {
memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + len, remain);
}
n->m_len = remain;
m->m_len = len;
n->m_next = m->m_next;
m->m_next = NULL;
return (n);
}
/*
* Make space for a new header of length hlen at skip bytes
* into the packet. When doing this we allocate new mbufs only
* when absolutely necessary. The mbuf where the new header
* is to go is returned together with an offset into the mbuf.
* If NULL is returned then the mbuf chain may have been modified;
* the caller is assumed to always free the chain.
*/
struct mbuf *
m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
{
struct mbuf *m;
unsigned remain;
KASSERT(m0->m_flags & M_PKTHDR);
/*
* Limit the size of the new header to MHLEN. In case
* skip = 0 and the first buffer is not a cluster this
* is the maximum space available in that mbuf.
* In other words this code never prepends a mbuf.
*/
KASSERT(hlen < MHLEN);
for (m = m0; m && skip > m->m_len; m = m->m_next)
skip -= m->m_len;
if (m == NULL)
return (NULL);
/*
* At this point skip is the offset into the mbuf m
* where the new header should be placed. Figure out
* if there's space to insert the new header. If so,
* and copying the remainder makes sense then do so.
* Otherwise insert a new mbuf in the chain, splitting
* the contents of m as needed.
*/
remain = m->m_len - skip; /* data to move */
if (skip < remain && hlen <= m_leadingspace(m)) {
if (skip)
memmove(m->m_data-hlen, m->m_data, skip);
m->m_data -= hlen;
m->m_len += hlen;
*off = skip;
} else if (hlen > m_trailingspace(m)) {
struct mbuf *n;
if (remain > 0) {
MGET(n, M_DONTWAIT, m->m_type);
if (n && remain > MLEN) {
MCLGETL(n, M_DONTWAIT, remain);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL)
return (NULL);
memcpy(n->m_data, mtod(m, char *) + skip, remain);
n->m_len = remain;
m->m_len -= remain;
n->m_next = m->m_next;
m->m_next = n;
}
if (hlen <= m_trailingspace(m)) {
m->m_len += hlen;
*off = skip;
} else {
n = m_get(M_DONTWAIT, m->m_type);
if (n == NULL)
return NULL;
n->m_len = hlen;
n->m_next = m->m_next;
m->m_next = n;
*off = 0; /* header is at front ... */
m = n; /* ... of new mbuf */
}
} else {
/*
* Copy the remainder to the back of the mbuf
* so there's space to write the new header.
*/
if (remain > 0)
memmove(mtod(m, caddr_t) + skip + hlen,
mtod(m, caddr_t) + skip, remain);
m->m_len += hlen;
*off = skip;
}
m0->m_pkthdr.len += hlen; /* adjust packet length */
return m;
}
/*
* Routine to copy from device local memory into mbufs.
*/
struct mbuf *
m_devget(char *buf, int totlen, int off)
{
struct mbuf *m;
struct mbuf *top, **mp;
int len;
top = NULL;
mp = ⊤
if (off < 0 || off > MHLEN)
return (NULL);
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return (NULL);
m->m_pkthdr.len = totlen;
len = MHLEN;
while (totlen > 0) {
if (top != NULL) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
/*
* As we might get called by pfkey, make sure
* we do not leak sensitive data.
*/
top->m_flags |= M_ZEROIZE;
m_freem(top);
return (NULL);
}
len = MLEN;
}
if (totlen + off >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT)
len = MCLBYTES;
} else {
/* Place initial small packet/header at end of mbuf. */
if (top == NULL && totlen + off + max_linkhdr <= len) {
m->m_data += max_linkhdr;
len -= max_linkhdr;
}
}
if (off) {
m->m_data += off;
len -= off;
off = 0;
}
m->m_len = len = min(totlen, len);
memcpy(mtod(m, void *), buf, (size_t)len);
buf += len;
*mp = m;
mp = &m->m_next;
totlen -= len;
}
return (top);
}
void
m_zero(struct mbuf *m)
{
if (M_READONLY(m)) {
mtx_enter(&m_extref_mtx);
if ((m->m_flags & M_EXT) && MCLISREFERENCED(m)) {
m->m_ext.ext_nextref->m_flags |= M_ZEROIZE;
m->m_ext.ext_prevref->m_flags |= M_ZEROIZE;
}
mtx_leave(&m_extref_mtx);
return;
}
explicit_bzero(M_DATABUF(m), M_SIZE(m));
}
/*
* Apply function f to the data in an mbuf chain starting "off" bytes from the
* beginning, continuing for "len" bytes.
*/
int
m_apply(struct mbuf *m, int off, int len,
int (*f)(caddr_t, caddr_t, unsigned int), caddr_t fstate)
{
int rval;
unsigned int count;
if (len < 0)
panic("m_apply: len %d < 0", len);
if (off < 0)
panic("m_apply: off %d < 0", off);
while (off > 0) {
if (m == NULL)
panic("m_apply: null mbuf in skip");
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
while (len > 0) {
if (m == NULL)
panic("m_apply: null mbuf");
count = min(m->m_len - off, len);
rval = f(fstate, mtod(m, caddr_t) + off, count);
if (rval)
return (rval);
len -= count;
off = 0;
m = m->m_next;
}
return (0);
}
/*
* Compute the amount of space available before the current start of data
* in an mbuf. Read-only clusters never have space available.
*/
int
m_leadingspace(struct mbuf *m)
{
if (M_READONLY(m))
return 0;
KASSERT(m->m_data >= M_DATABUF(m));
return m->m_data - M_DATABUF(m);
}
/*
* Compute the amount of space available after the end of data in an mbuf.
* Read-only clusters never have space available.
*/
int
m_trailingspace(struct mbuf *m)
{
if (M_READONLY(m))
return 0;
KASSERT(M_DATABUF(m) + M_SIZE(m) >= (m->m_data + m->m_len));
return M_DATABUF(m) + M_SIZE(m) - (m->m_data + m->m_len);
}
/*
* Set the m_data pointer of a newly-allocated mbuf to place an object of
* the specified size at the end of the mbuf, longword aligned.
*/
void
m_align(struct mbuf *m, int len)
{
KASSERT(len >= 0 && !M_READONLY(m));
KASSERT(m->m_data == M_DATABUF(m)); /* newly-allocated check */
KASSERT(((len + sizeof(long) - 1) &~ (sizeof(long) - 1)) <= M_SIZE(m));
m->m_data = M_DATABUF(m) + ((M_SIZE(m) - (len)) &~ (sizeof(long) - 1));
}
/*
* Duplicate mbuf pkthdr from from to to.
* from must have M_PKTHDR set, and to must be empty.
*/
int
m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int wait)
{
int error;
KASSERT(from->m_flags & M_PKTHDR);
to->m_flags = (to->m_flags & (M_EXT | M_EXTWR));
to->m_flags |= (from->m_flags & M_COPYFLAGS);
to->m_pkthdr = from->m_pkthdr;
#if NPF > 0
to->m_pkthdr.pf.statekey = NULL;
pf_mbuf_link_state_key(to, from->m_pkthdr.pf.statekey);
to->m_pkthdr.pf.inp = NULL;
pf_mbuf_link_inpcb(to, from->m_pkthdr.pf.inp);
#endif /* NPF > 0 */
SLIST_INIT(&to->m_pkthdr.ph_tags);
if ((error = m_tag_copy_chain(to, from, wait)) != 0)
return (error);
if ((to->m_flags & M_EXT) == 0)
to->m_data = to->m_pktdat;
return (0);
}
struct mbuf *
m_dup_pkt(struct mbuf *m0, unsigned int adj, int wait)
{
struct mbuf *m;
int len;
KASSERT(m0->m_flags & M_PKTHDR);
len = m0->m_pkthdr.len + adj;
if (len > MAXMCLBYTES) /* XXX */
return (NULL);
m = m_get(wait, m0->m_type);
if (m == NULL)
return (NULL);
if (m_dup_pkthdr(m, m0, wait) != 0)
goto fail;
if (len > MHLEN) {
MCLGETL(m, wait, len);
if (!ISSET(m->m_flags, M_EXT))
goto fail;
}
m->m_len = m->m_pkthdr.len = len;
m_adj(m, adj);
m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t));
return (m);
fail:
m_freem(m);
return (NULL);
}
void
m_microtime(const struct mbuf *m, struct timeval *tv)
{
if (ISSET(m->m_pkthdr.csum_flags, M_TIMESTAMP)) {
struct timeval btv, utv;
NSEC_TO_TIMEVAL(m->m_pkthdr.ph_timestamp, &utv);
microboottime(&btv);
timeradd(&btv, &utv, tv);
} else
microtime(tv);
}
void *
m_pool_alloc(struct pool *pp, int flags, int *slowdown)
{
void *v;
if (atomic_add_long_nv(&mbuf_mem_alloc, pp->pr_pgsize) > mbuf_mem_limit)
goto fail;
v = (*pool_allocator_multi.pa_alloc)(pp, flags, slowdown);
if (v != NULL)
return (v);
fail:
atomic_sub_long(&mbuf_mem_alloc, pp->pr_pgsize);
return (NULL);
}
void
m_pool_free(struct pool *pp, void *v)
{
(*pool_allocator_multi.pa_free)(pp, v);
atomic_sub_long(&mbuf_mem_alloc, pp->pr_pgsize);
}
void
m_pool_init(struct pool *pp, u_int size, u_int align, const char *wmesg)
{
pool_init(pp, size, align, IPL_NET, 0, wmesg, &m_pool_allocator);
pool_set_constraints(pp, &kp_dma_contig);
}
u_int
m_pool_used(void)
{
return ((mbuf_mem_alloc * 100) / mbuf_mem_limit);
}
#ifdef DDB
void
m_print(void *v,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
struct mbuf *m = v;
(*pr)("mbuf %p\n", m);
(*pr)("m_type: %i\tm_flags: %b\n", m->m_type, m->m_flags, M_BITS);
(*pr)("m_next: %p\tm_nextpkt: %p\n", m->m_next, m->m_nextpkt);
(*pr)("m_data: %p\tm_len: %u\n", m->m_data, m->m_len);
(*pr)("m_dat: %p\tm_pktdat: %p\n", m->m_dat, m->m_pktdat);
if (m->m_flags & M_PKTHDR) {
(*pr)("m_ptkhdr.ph_ifidx: %u\tm_pkthdr.len: %i\n",
m->m_pkthdr.ph_ifidx, m->m_pkthdr.len);
(*pr)("m_ptkhdr.ph_tags: %p\tm_pkthdr.ph_tagsset: %b\n",
SLIST_FIRST(&m->m_pkthdr.ph_tags),
m->m_pkthdr.ph_tagsset, MTAG_BITS);
(*pr)("m_pkthdr.ph_flowid: %u\tm_pkthdr.ph_loopcnt: %u\n",
m->m_pkthdr.ph_flowid, m->m_pkthdr.ph_loopcnt);
(*pr)("m_pkthdr.csum_flags: %b\n",
m->m_pkthdr.csum_flags, MCS_BITS);
(*pr)("m_pkthdr.ether_vtag: %u\tm_ptkhdr.ph_rtableid: %u\n",
m->m_pkthdr.ether_vtag, m->m_pkthdr.ph_rtableid);
(*pr)("m_pkthdr.pf.statekey: %p\tm_pkthdr.pf.inp %p\n",
m->m_pkthdr.pf.statekey, m->m_pkthdr.pf.inp);
(*pr)("m_pkthdr.pf.qid: %u\tm_pkthdr.pf.tag: %u\n",
m->m_pkthdr.pf.qid, m->m_pkthdr.pf.tag);
(*pr)("m_pkthdr.pf.flags: %b\n",
m->m_pkthdr.pf.flags, MPF_BITS);
(*pr)("m_pkthdr.pf.routed: %u\tm_pkthdr.pf.prio: %u\n",
m->m_pkthdr.pf.routed, m->m_pkthdr.pf.prio);
}
if (m->m_flags & M_EXT) {
(*pr)("m_ext.ext_buf: %p\tm_ext.ext_size: %u\n",
m->m_ext.ext_buf, m->m_ext.ext_size);
(*pr)("m_ext.ext_free_fn: %u\tm_ext.ext_arg: %p\n",
m->m_ext.ext_free_fn, m->m_ext.ext_arg);
(*pr)("m_ext.ext_nextref: %p\tm_ext.ext_prevref: %p\n",
m->m_ext.ext_nextref, m->m_ext.ext_prevref);
}
}
#endif
/*
* mbuf lists
*/
void
ml_init(struct mbuf_list *ml)
{
ml->ml_head = ml->ml_tail = NULL;
ml->ml_len = 0;
}
void
ml_enqueue(struct mbuf_list *ml, struct mbuf *m)
{
if (ml->ml_tail == NULL)
ml->ml_head = ml->ml_tail = m;
else {
ml->ml_tail->m_nextpkt = m;
ml->ml_tail = m;
}
m->m_nextpkt = NULL;
ml->ml_len++;
}
void
ml_enlist(struct mbuf_list *mla, struct mbuf_list *mlb)
{
if (!ml_empty(mlb)) {
if (ml_empty(mla))
mla->ml_head = mlb->ml_head;
else
mla->ml_tail->m_nextpkt = mlb->ml_head;
mla->ml_tail = mlb->ml_tail;
mla->ml_len += mlb->ml_len;
ml_init(mlb);
}
}
struct mbuf *
ml_dequeue(struct mbuf_list *ml)
{
struct mbuf *m;
m = ml->ml_head;
if (m != NULL) {
ml->ml_head = m->m_nextpkt;
if (ml->ml_head == NULL)
ml->ml_tail = NULL;
m->m_nextpkt = NULL;
ml->ml_len--;
}
return (m);
}
struct mbuf *
ml_dechain(struct mbuf_list *ml)
{
struct mbuf *m0;
m0 = ml->ml_head;
ml_init(ml);
return (m0);
}
unsigned int
ml_purge(struct mbuf_list *ml)
{
struct mbuf *m, *n;
unsigned int len;
for (m = ml->ml_head; m != NULL; m = n) {
n = m->m_nextpkt;
m_freem(m);
}
len = ml->ml_len;
ml_init(ml);
return (len);
}
unsigned int
ml_hdatalen(struct mbuf_list *ml)
{
struct mbuf *m;
m = ml->ml_head;
if (m == NULL)
return (0);
KASSERT(ISSET(m->m_flags, M_PKTHDR));
return (m->m_pkthdr.len);
}
/*
* mbuf queues
*/
void
mq_init(struct mbuf_queue *mq, u_int maxlen, int ipl)
{
mtx_init(&mq->mq_mtx, ipl);
ml_init(&mq->mq_list);
mq->mq_maxlen = maxlen;
}
int
mq_push(struct mbuf_queue *mq, struct mbuf *m)
{
struct mbuf *dropped = NULL;
mtx_enter(&mq->mq_mtx);
if (mq_len(mq) >= mq->mq_maxlen) {
mq->mq_drops++;
dropped = ml_dequeue(&mq->mq_list);
}
ml_enqueue(&mq->mq_list, m);
mtx_leave(&mq->mq_mtx);
if (dropped)
m_freem(dropped);
return (dropped != NULL);
}
int
mq_enqueue(struct mbuf_queue *mq, struct mbuf *m)
{
int dropped = 0;
mtx_enter(&mq->mq_mtx);
if (mq_len(mq) < mq->mq_maxlen)
ml_enqueue(&mq->mq_list, m);
else {
mq->mq_drops++;
dropped = 1;
}
mtx_leave(&mq->mq_mtx);
if (dropped)
m_freem(m);
return (dropped);
}
struct mbuf *
mq_dequeue(struct mbuf_queue *mq)
{
struct mbuf *m;
mtx_enter(&mq->mq_mtx);
m = ml_dequeue(&mq->mq_list);
mtx_leave(&mq->mq_mtx);
return (m);
}
int
mq_enlist(struct mbuf_queue *mq, struct mbuf_list *ml)
{
struct mbuf *m;
int dropped = 0;
mtx_enter(&mq->mq_mtx);
if (mq_len(mq) < mq->mq_maxlen)
ml_enlist(&mq->mq_list, ml);
else {
dropped = ml_len(ml);
mq->mq_drops += dropped;
}
mtx_leave(&mq->mq_mtx);
if (dropped) {
while ((m = ml_dequeue(ml)) != NULL)
m_freem(m);
}
return (dropped);
}
void
mq_delist(struct mbuf_queue *mq, struct mbuf_list *ml)
{
mtx_enter(&mq->mq_mtx);
*ml = mq->mq_list;
ml_init(&mq->mq_list);
mtx_leave(&mq->mq_mtx);
}
struct mbuf *
mq_dechain(struct mbuf_queue *mq)
{
struct mbuf *m0;
mtx_enter(&mq->mq_mtx);
m0 = ml_dechain(&mq->mq_list);
mtx_leave(&mq->mq_mtx);
return (m0);
}
unsigned int
mq_purge(struct mbuf_queue *mq)
{
struct mbuf_list ml;
mq_delist(mq, &ml);
return (ml_purge(&ml));
}
unsigned int
mq_hdatalen(struct mbuf_queue *mq)
{
unsigned int hdatalen;
mtx_enter(&mq->mq_mtx);
hdatalen = ml_hdatalen(&mq->mq_list);
mtx_leave(&mq->mq_mtx);
return (hdatalen);
}
int
sysctl_mq(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen, struct mbuf_queue *mq)
{
unsigned int maxlen;
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case IFQCTL_LEN:
return (sysctl_rdint(oldp, oldlenp, newp, mq_len(mq)));
case IFQCTL_MAXLEN:
maxlen = mq->mq_maxlen;
error = sysctl_int(oldp, oldlenp, newp, newlen, &maxlen);
if (!error && maxlen != mq->mq_maxlen) {
mtx_enter(&mq->mq_mtx);
mq->mq_maxlen = maxlen;
mtx_leave(&mq->mq_mtx);
}
return (error);
case IFQCTL_DROPS:
return (sysctl_rdint(oldp, oldlenp, newp, mq_drops(mq)));
default:
return (EOPNOTSUPP);
}
/* NOTREACHED */
}
2
21
2
3
17
4
5
11
4
7
7
8
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* $OpenBSD: diskmap.c,v 1.26 2019/08/06 07:16:48 anton Exp $ */
/*
* Copyright (c) 2009, 2010 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Disk mapper.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/dkio.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/pledge.h>
#include <sys/namei.h>
int
diskmapopen(dev_t dev, int flag, int fmt, struct proc *p)
{
return 0;
}
int
diskmapclose(dev_t dev, int flag, int fmt, struct proc *p)
{
return 0;
}
int
diskmapioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct dk_diskmap *dm;
struct nameidata ndp;
struct filedesc *fdp = p->p_fd;
struct file *fp0 = NULL, *fp = NULL;
struct vnode *vp = NULL;
char *devname, flags;
int error, fd;
if (cmd != DIOCMAP)
return ENOTTY;
/*
* Map a request for a disk to the correct device. We should be
* supplied with either a diskname or a disklabel UID.
*/
dm = (struct dk_diskmap *)addr;
fd = dm->fd;
devname = malloc(PATH_MAX, M_DEVBUF, M_WAITOK);
if ((error = copyinstr(dm->device, devname, PATH_MAX, NULL)) != 0)
goto invalid;
if (disk_map(devname, devname, PATH_MAX, dm->flags) == 0) {
error = copyoutstr(devname, dm->device, PATH_MAX, NULL);
if (error != 0)
goto invalid;
}
/* Attempt to open actual device. */
if ((error = getvnode(p, fd, &fp0)) != 0)
goto invalid;
NDINIT(&ndp, 0, 0, UIO_SYSSPACE, devname, p);
ndp.ni_pledge = PLEDGE_RPATH;
ndp.ni_unveil = UNVEIL_READ;
if ((error = vn_open(&ndp, fp0->f_flag, 0)) != 0)
goto invalid;
vp = ndp.ni_vp;
VOP_UNLOCK(vp);
fdplock(fdp);
/*
* Stop here if the 'struct file *' has been replaced,
* for example by another thread calling dup2(2), while
* this thread was sleeping in vn_open().
*
* Note that this would not happen for correct usages of
* "/dev/diskmap".
*/
if (fdp->fd_ofiles[fd] != fp0) {
error = EAGAIN;
goto bad;
}
fp = fnew(p);
if (fp == NULL) {
error = ENFILE;
goto bad;
}
/* Zap old file. */
mtx_enter(&fdp->fd_fplock);
KASSERT(fdp->fd_ofiles[fd] == fp0);
flags = fdp->fd_ofileflags[fd];
fdp->fd_ofiles[fd] = NULL;
fdp->fd_ofileflags[fd] = 0;
mtx_leave(&fdp->fd_fplock);
/* Insert new file. */
fp->f_flag = fp0->f_flag;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = (caddr_t)vp;
fdinsert(fdp, fd, flags, fp);
fdpunlock(fdp);
closef(fp0, p);
free(devname, M_DEVBUF, PATH_MAX);
return 0;
bad:
fdpunlock(fdp);
(void)vn_close(vp, fp0->f_flag, p->p_ucred, p);
invalid:
if (fp0)
FRELE(fp0, p);
free(devname, M_DEVBUF, PATH_MAX);
return error;
}
int
diskmapread(dev_t dev, struct uio *uio, int flag)
{
return ENXIO;
}
int
diskmapwrite(dev_t dev, struct uio *uio, int flag)
{
return ENXIO;
}
2
2
2
2
2
2
27
27
2
27
1
1
1
2
2
2
2
27
27
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
/* $OpenBSD: if_vio.c,v 1.22 2022/03/07 18:52:16 dv Exp $ */
/*
* Copyright (c) 2012 Stefan Fritsch, Alexander Fiveg.
* Copyright (c) 2010 Minoura Makoto.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "bpfilter.h"
#include "vlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/timeout.h>
#include <dev/pv/virtioreg.h>
#include <dev/pv/virtiovar.h>
#include <net/if.h>
#include <net/if_media.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#if VIRTIO_DEBUG
#define DPRINTF(x...) printf(x)
#else
#define DPRINTF(x...)
#endif
/*
* if_vioreg.h:
*/
/* Configuration registers */
#define VIRTIO_NET_CONFIG_MAC 0 /* 8bit x 6byte */
#define VIRTIO_NET_CONFIG_STATUS 6 /* 16bit */
/* Feature bits */
#define VIRTIO_NET_F_CSUM (1ULL<<0)
#define VIRTIO_NET_F_GUEST_CSUM (1ULL<<1)
#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS (1ULL<<2)
#define VIRTIO_NET_F_MTU (1ULL<<3)
#define VIRTIO_NET_F_MAC (1ULL<<5)
#define VIRTIO_NET_F_GSO (1ULL<<6)
#define VIRTIO_NET_F_GUEST_TSO4 (1ULL<<7)
#define VIRTIO_NET_F_GUEST_TSO6 (1ULL<<8)
#define VIRTIO_NET_F_GUEST_ECN (1ULL<<9)
#define VIRTIO_NET_F_GUEST_UFO (1ULL<<10)
#define VIRTIO_NET_F_HOST_TSO4 (1ULL<<11)
#define VIRTIO_NET_F_HOST_TSO6 (1ULL<<12)
#define VIRTIO_NET_F_HOST_ECN (1ULL<<13)
#define VIRTIO_NET_F_HOST_UFO (1ULL<<14)
#define VIRTIO_NET_F_MRG_RXBUF (1ULL<<15)
#define VIRTIO_NET_F_STATUS (1ULL<<16)
#define VIRTIO_NET_F_CTRL_VQ (1ULL<<17)
#define VIRTIO_NET_F_CTRL_RX (1ULL<<18)
#define VIRTIO_NET_F_CTRL_VLAN (1ULL<<19)
#define VIRTIO_NET_F_CTRL_RX_EXTRA (1ULL<<20)
#define VIRTIO_NET_F_GUEST_ANNOUNCE (1ULL<<21)
#define VIRTIO_NET_F_MQ (1ULL<<22)
#define VIRTIO_NET_F_CTRL_MAC_ADDR (1ULL<<23)
/*
* Config(8) flags. The lowest byte is reserved for generic virtio stuff.
*/
/* Workaround for vlan related bug in qemu < version 2.0 */
#define CONFFLAG_QEMU_VLAN_BUG (1<<8)
static const struct virtio_feature_name virtio_net_feature_names[] = {
#if VIRTIO_DEBUG
{ VIRTIO_NET_F_CSUM, "CSum" },
{ VIRTIO_NET_F_GUEST_CSUM, "GuestCSum" },
{ VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, "CtrlGuestOffl" },
{ VIRTIO_NET_F_MTU, "MTU", },
{ VIRTIO_NET_F_MAC, "MAC" },
{ VIRTIO_NET_F_GSO, "GSO" },
{ VIRTIO_NET_F_GUEST_TSO4, "GuestTSO4" },
{ VIRTIO_NET_F_GUEST_TSO6, "GuestTSO6" },
{ VIRTIO_NET_F_GUEST_ECN, "GuestECN" },
{ VIRTIO_NET_F_GUEST_UFO, "GuestUFO" },
{ VIRTIO_NET_F_HOST_TSO4, "HostTSO4" },
{ VIRTIO_NET_F_HOST_TSO6, "HostTSO6" },
{ VIRTIO_NET_F_HOST_ECN, "HostECN" },
{ VIRTIO_NET_F_HOST_UFO, "HostUFO" },
{ VIRTIO_NET_F_MRG_RXBUF, "MrgRXBuf" },
{ VIRTIO_NET_F_STATUS, "Status" },
{ VIRTIO_NET_F_CTRL_VQ, "CtrlVQ" },
{ VIRTIO_NET_F_CTRL_RX, "CtrlRX" },
{ VIRTIO_NET_F_CTRL_VLAN, "CtrlVLAN" },
{ VIRTIO_NET_F_CTRL_RX_EXTRA, "CtrlRXExtra" },
{ VIRTIO_NET_F_GUEST_ANNOUNCE, "GuestAnnounce" },
{ VIRTIO_NET_F_MQ, "MQ" },
{ VIRTIO_NET_F_CTRL_MAC_ADDR, "CtrlMAC" },
#endif
{ 0, NULL }
};
/* Status */
#define VIRTIO_NET_S_LINK_UP 1
/* Packet header structure */
struct virtio_net_hdr {
uint8_t flags;
uint8_t gso_type;
uint16_t hdr_len;
uint16_t gso_size;
uint16_t csum_start;
uint16_t csum_offset;
/* only present if VIRTIO_NET_F_MRG_RXBUF is negotiated */
uint16_t num_buffers;
} __packed;
#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* flags */
#define VIRTIO_NET_HDR_GSO_NONE 0 /* gso_type */
#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* gso_type */
#define VIRTIO_NET_HDR_GSO_UDP 3 /* gso_type */
#define VIRTIO_NET_HDR_GSO_TCPV6 4 /* gso_type */
#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* gso_type, |'ed */
#define VIRTIO_NET_MAX_GSO_LEN (65536+ETHER_HDR_LEN)
/* Control virtqueue */
struct virtio_net_ctrl_cmd {
uint8_t class;
uint8_t command;
} __packed;
#define VIRTIO_NET_CTRL_RX 0
# define VIRTIO_NET_CTRL_RX_PROMISC 0
# define VIRTIO_NET_CTRL_RX_ALLMULTI 1
#define VIRTIO_NET_CTRL_MAC 1
# define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
#define VIRTIO_NET_CTRL_VLAN 2
# define VIRTIO_NET_CTRL_VLAN_ADD 0
# define VIRTIO_NET_CTRL_VLAN_DEL 1
struct virtio_net_ctrl_status {
uint8_t ack;
} __packed;
#define VIRTIO_NET_OK 0
#define VIRTIO_NET_ERR 1
struct virtio_net_ctrl_rx {
uint8_t onoff;
} __packed;
struct virtio_net_ctrl_mac_tbl {
uint32_t nentries;
uint8_t macs[][ETHER_ADDR_LEN];
} __packed;
struct virtio_net_ctrl_vlan {
uint16_t id;
} __packed;
/*
* if_viovar.h:
*/
enum vio_ctrl_state {
FREE, INUSE, DONE, RESET
};
struct vio_softc {
struct device sc_dev;
struct virtio_softc *sc_virtio;
#define VQRX 0
#define VQTX 1
#define VQCTL 2
struct virtqueue sc_vq[3];
struct arpcom sc_ac;
struct ifmedia sc_media;
short sc_ifflags;
/* bus_dmamem */
bus_dma_segment_t sc_dma_seg;
bus_dmamap_t sc_dma_map;
size_t sc_dma_size;
caddr_t sc_dma_kva;
int sc_hdr_size;
struct virtio_net_hdr *sc_tx_hdrs;
struct virtio_net_ctrl_cmd *sc_ctrl_cmd;
struct virtio_net_ctrl_status *sc_ctrl_status;
struct virtio_net_ctrl_rx *sc_ctrl_rx;
struct virtio_net_ctrl_mac_tbl *sc_ctrl_mac_tbl_uc;
#define sc_ctrl_mac_info sc_ctrl_mac_tbl_uc
struct virtio_net_ctrl_mac_tbl *sc_ctrl_mac_tbl_mc;
/* kmem */
bus_dmamap_t *sc_arrays;
#define sc_rx_dmamaps sc_arrays
bus_dmamap_t *sc_tx_dmamaps;
struct mbuf **sc_rx_mbufs;
struct mbuf **sc_tx_mbufs;
struct if_rxring sc_rx_ring;
enum vio_ctrl_state sc_ctrl_inuse;
struct timeout sc_txtick, sc_rxtick;
};
#define VIO_DMAMEM_OFFSET(sc, p) ((caddr_t)(p) - (sc)->sc_dma_kva)
#define VIO_DMAMEM_SYNC(vsc, sc, p, size, flags) \
bus_dmamap_sync((vsc)->sc_dmat, (sc)->sc_dma_map, \
VIO_DMAMEM_OFFSET((sc), (p)), (size), (flags))
#define VIO_DMAMEM_ENQUEUE(sc, vq, slot, p, size, write) \
virtio_enqueue_p((vq), (slot), (sc)->sc_dma_map, \
VIO_DMAMEM_OFFSET((sc), (p)), (size), (write))
#define VIO_HAVE_MRG_RXBUF(sc) \
((sc)->sc_hdr_size == sizeof(struct virtio_net_hdr))
#define VIRTIO_NET_TX_MAXNSEGS 16 /* for larger chains, defrag */
#define VIRTIO_NET_CTRL_MAC_MC_ENTRIES 64 /* for more entries, use ALLMULTI */
#define VIRTIO_NET_CTRL_MAC_UC_ENTRIES 1 /* one entry for own unicast addr */
#define VIO_CTRL_MAC_INFO_SIZE \
(2*sizeof(struct virtio_net_ctrl_mac_tbl) + \
(VIRTIO_NET_CTRL_MAC_MC_ENTRIES + \
VIRTIO_NET_CTRL_MAC_UC_ENTRIES) * ETHER_ADDR_LEN)
/* cfattach interface functions */
int vio_match(struct device *, void *, void *);
void vio_attach(struct device *, struct device *, void *);
/* ifnet interface functions */
int vio_init(struct ifnet *);
void vio_stop(struct ifnet *, int);
void vio_start(struct ifnet *);
int vio_ioctl(struct ifnet *, u_long, caddr_t);
void vio_get_lladr(struct arpcom *ac, struct virtio_softc *vsc);
void vio_put_lladr(struct arpcom *ac, struct virtio_softc *vsc);
/* rx */
int vio_add_rx_mbuf(struct vio_softc *, int);
void vio_free_rx_mbuf(struct vio_softc *, int);
void vio_populate_rx_mbufs(struct vio_softc *);
int vio_rxeof(struct vio_softc *);
int vio_rx_intr(struct virtqueue *);
void vio_rx_drain(struct vio_softc *);
void vio_rxtick(void *);
/* tx */
int vio_tx_intr(struct virtqueue *);
int vio_txeof(struct virtqueue *);
void vio_tx_drain(struct vio_softc *);
int vio_encap(struct vio_softc *, int, struct mbuf *);
void vio_txtick(void *);
/* other control */
void vio_link_state(struct ifnet *);
int vio_config_change(struct virtio_softc *);
int vio_ctrl_rx(struct vio_softc *, int, int);
int vio_set_rx_filter(struct vio_softc *);
void vio_iff(struct vio_softc *);
int vio_media_change(struct ifnet *);
void vio_media_status(struct ifnet *, struct ifmediareq *);
int vio_ctrleof(struct virtqueue *);
int vio_wait_ctrl(struct vio_softc *sc);
int vio_wait_ctrl_done(struct vio_softc *sc);
void vio_ctrl_wakeup(struct vio_softc *, enum vio_ctrl_state);
int vio_alloc_mem(struct vio_softc *);
int vio_alloc_dmamem(struct vio_softc *);
void vio_free_dmamem(struct vio_softc *);
#if VIRTIO_DEBUG
void vio_dump(struct vio_softc *);
#endif
int
vio_match(struct device *parent, void *match, void *aux)
{
struct virtio_softc *va = aux;
if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_NETWORK)
return 1;
return 0;
}
const struct cfattach vio_ca = {
sizeof(struct vio_softc), vio_match, vio_attach, NULL
};
struct cfdriver vio_cd = {
NULL, "vio", DV_IFNET
};
int
vio_alloc_dmamem(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
int nsegs;
if (bus_dmamap_create(vsc->sc_dmat, sc->sc_dma_size, 1,
sc->sc_dma_size, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
&sc->sc_dma_map) != 0)
goto err;
if (bus_dmamem_alloc(vsc->sc_dmat, sc->sc_dma_size, 16, 0,
&sc->sc_dma_seg, 1, &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
goto destroy;
if (bus_dmamem_map(vsc->sc_dmat, &sc->sc_dma_seg, nsegs,
sc->sc_dma_size, &sc->sc_dma_kva, BUS_DMA_NOWAIT) != 0)
goto free;
if (bus_dmamap_load(vsc->sc_dmat, sc->sc_dma_map, sc->sc_dma_kva,
sc->sc_dma_size, NULL, BUS_DMA_NOWAIT) != 0)
goto unmap;
return (0);
unmap:
bus_dmamem_unmap(vsc->sc_dmat, sc->sc_dma_kva, sc->sc_dma_size);
free:
bus_dmamem_free(vsc->sc_dmat, &sc->sc_dma_seg, 1);
destroy:
bus_dmamap_destroy(vsc->sc_dmat, sc->sc_dma_map);
err:
return (1);
}
void
vio_free_dmamem(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
bus_dmamap_unload(vsc->sc_dmat, sc->sc_dma_map);
bus_dmamem_unmap(vsc->sc_dmat, sc->sc_dma_kva, sc->sc_dma_size);
bus_dmamem_free(vsc->sc_dmat, &sc->sc_dma_seg, 1);
bus_dmamap_destroy(vsc->sc_dmat, sc->sc_dma_map);
}
/* allocate memory */
/*
* dma memory is used for:
* sc_tx_hdrs[slot]: metadata array for frames to be sent (WRITE)
* sc_ctrl_cmd: command to be sent via ctrl vq (WRITE)
* sc_ctrl_status: return value for a command via ctrl vq (READ)
* sc_ctrl_rx: parameter for a VIRTIO_NET_CTRL_RX class command
* (WRITE)
* sc_ctrl_mac_tbl_uc: unicast MAC address filter for a VIRTIO_NET_CTRL_MAC
* class command (WRITE)
* sc_ctrl_mac_tbl_mc: multicast MAC address filter for a VIRTIO_NET_CTRL_MAC
* class command (WRITE)
* sc_ctrl_* structures are allocated only one each; they are protected by
* sc_ctrl_inuse, which must only be accessed at splnet
*
* metadata headers for received frames are stored at the start of the
* rx mbufs.
*/
/*
* dynamically allocated memory is used for:
* sc_rx_dmamaps[slot]: bus_dmamap_t array for received payload
* sc_tx_dmamaps[slot]: bus_dmamap_t array for sent payload
* sc_rx_mbufs[slot]: mbuf pointer array for received frames
* sc_tx_mbufs[slot]: mbuf pointer array for sent frames
*/
int
vio_alloc_mem(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
struct ifnet *ifp = &sc->sc_ac.ac_if;
int allocsize, r, i, txsize;
unsigned int offset = 0;
int rxqsize, txqsize;
caddr_t kva;
rxqsize = vsc->sc_vqs[0].vq_num;
txqsize = vsc->sc_vqs[1].vq_num;
/*
* For simplicity, we always allocate the full virtio_net_hdr size
* even if VIRTIO_NET_F_MRG_RXBUF is not negotiated and
* only a part of the memory is ever used.
*/
allocsize = sizeof(struct virtio_net_hdr) * txqsize;
if (vsc->sc_nvqs == 3) {
allocsize += sizeof(struct virtio_net_ctrl_cmd) * 1;
allocsize += sizeof(struct virtio_net_ctrl_status) * 1;
allocsize += sizeof(struct virtio_net_ctrl_rx) * 1;
allocsize += VIO_CTRL_MAC_INFO_SIZE;
}
sc->sc_dma_size = allocsize;
if (vio_alloc_dmamem(sc) != 0) {
printf("unable to allocate dma region\n");
return -1;
}
kva = sc->sc_dma_kva;
sc->sc_tx_hdrs = (struct virtio_net_hdr*)(kva + offset);
offset += sizeof(struct virtio_net_hdr) * txqsize;
if (vsc->sc_nvqs == 3) {
sc->sc_ctrl_cmd = (void*)(kva + offset);
offset += sizeof(*sc->sc_ctrl_cmd);
sc->sc_ctrl_status = (void*)(kva + offset);
offset += sizeof(*sc->sc_ctrl_status);
sc->sc_ctrl_rx = (void*)(kva + offset);
offset += sizeof(*sc->sc_ctrl_rx);
sc->sc_ctrl_mac_tbl_uc = (void*)(kva + offset);
offset += sizeof(*sc->sc_ctrl_mac_tbl_uc) +
ETHER_ADDR_LEN * VIRTIO_NET_CTRL_MAC_UC_ENTRIES;
sc->sc_ctrl_mac_tbl_mc = (void*)(kva + offset);
}
sc->sc_arrays = mallocarray(rxqsize + txqsize,
2 * sizeof(bus_dmamap_t) + sizeof(struct mbuf *), M_DEVBUF,
M_WAITOK | M_CANFAIL | M_ZERO);
if (sc->sc_arrays == NULL) {
printf("unable to allocate mem for dmamaps\n");
goto err_hdr;
}
allocsize = (rxqsize + txqsize) *
(2 * sizeof(bus_dmamap_t) + sizeof(struct mbuf *));
sc->sc_tx_dmamaps = sc->sc_arrays + rxqsize;
sc->sc_rx_mbufs = (void*) (sc->sc_tx_dmamaps + txqsize);
sc->sc_tx_mbufs = sc->sc_rx_mbufs + rxqsize;
for (i = 0; i < rxqsize; i++) {
r = bus_dmamap_create(vsc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_rx_dmamaps[i]);
if (r != 0)
goto err_reqs;
}
txsize = ifp->if_hardmtu + sc->sc_hdr_size + ETHER_HDR_LEN;
for (i = 0; i < txqsize; i++) {
r = bus_dmamap_create(vsc->sc_dmat, txsize,
VIRTIO_NET_TX_MAXNSEGS, txsize, 0,
BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW,
&sc->sc_tx_dmamaps[i]);
if (r != 0)
goto err_reqs;
}
return 0;
err_reqs:
printf("dmamap creation failed, error %d\n", r);
for (i = 0; i < txqsize; i++) {
if (sc->sc_tx_dmamaps[i])
bus_dmamap_destroy(vsc->sc_dmat, sc->sc_tx_dmamaps[i]);
}
for (i = 0; i < rxqsize; i++) {
if (sc->sc_rx_dmamaps[i])
bus_dmamap_destroy(vsc->sc_dmat, sc->sc_rx_dmamaps[i]);
}
if (sc->sc_arrays) {
free(sc->sc_arrays, M_DEVBUF, allocsize);
sc->sc_arrays = 0;
}
err_hdr:
vio_free_dmamem(sc);
return -1;
}
void
vio_get_lladr(struct arpcom *ac, struct virtio_softc *vsc)
{
int i;
for (i = 0; i < ETHER_ADDR_LEN; i++) {
ac->ac_enaddr[i] = virtio_read_device_config_1(vsc,
VIRTIO_NET_CONFIG_MAC + i);
}
}
void
vio_put_lladr(struct arpcom *ac, struct virtio_softc *vsc)
{
int i;
for (i = 0; i < ETHER_ADDR_LEN; i++) {
virtio_write_device_config_1(vsc, VIRTIO_NET_CONFIG_MAC + i,
ac->ac_enaddr[i]);
}
}
void
vio_attach(struct device *parent, struct device *self, void *aux)
{
struct vio_softc *sc = (struct vio_softc *)self;
struct virtio_softc *vsc = (struct virtio_softc *)parent;
int i;
struct ifnet *ifp = &sc->sc_ac.ac_if;
if (vsc->sc_child != NULL) {
printf(": child already attached for %s; something wrong...\n",
parent->dv_xname);
return;
}
sc->sc_virtio = vsc;
vsc->sc_child = self;
vsc->sc_ipl = IPL_NET;
vsc->sc_vqs = &sc->sc_vq[0];
vsc->sc_config_change = 0;
vsc->sc_driver_features = VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS |
VIRTIO_NET_F_CTRL_VQ | VIRTIO_NET_F_CTRL_RX |
VIRTIO_NET_F_MRG_RXBUF | VIRTIO_NET_F_CSUM |
VIRTIO_F_RING_EVENT_IDX;
virtio_negotiate_features(vsc, virtio_net_feature_names);
if (virtio_has_feature(vsc, VIRTIO_NET_F_MAC)) {
vio_get_lladr(&sc->sc_ac, vsc);
} else {
ether_fakeaddr(ifp);
vio_put_lladr(&sc->sc_ac, vsc);
}
printf(": address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr));
if (virtio_has_feature(vsc, VIRTIO_NET_F_MRG_RXBUF) ||
vsc->sc_version_1) {
sc->sc_hdr_size = sizeof(struct virtio_net_hdr);
} else {
sc->sc_hdr_size = offsetof(struct virtio_net_hdr, num_buffers);
}
if (virtio_has_feature(vsc, VIRTIO_NET_F_MRG_RXBUF))
ifp->if_hardmtu = 16000; /* arbitrary limit */
else
ifp->if_hardmtu = MCLBYTES - sc->sc_hdr_size - ETHER_HDR_LEN;
if (virtio_alloc_vq(vsc, &sc->sc_vq[VQRX], 0, MCLBYTES, 2, "rx") != 0)
goto err;
vsc->sc_nvqs = 1;
sc->sc_vq[VQRX].vq_done = vio_rx_intr;
if (virtio_alloc_vq(vsc, &sc->sc_vq[VQTX], 1,
sc->sc_hdr_size + ifp->if_hardmtu + ETHER_HDR_LEN,
VIRTIO_NET_TX_MAXNSEGS + 1, "tx") != 0) {
goto err;
}
vsc->sc_nvqs = 2;
sc->sc_vq[VQTX].vq_done = vio_tx_intr;
virtio_start_vq_intr(vsc, &sc->sc_vq[VQRX]);
if (virtio_has_feature(vsc, VIRTIO_F_RING_EVENT_IDX))
virtio_postpone_intr_far(&sc->sc_vq[VQTX]);
else
virtio_stop_vq_intr(vsc, &sc->sc_vq[VQTX]);
if (virtio_has_feature(vsc, VIRTIO_NET_F_CTRL_VQ)
&& virtio_has_feature(vsc, VIRTIO_NET_F_CTRL_RX)) {
if (virtio_alloc_vq(vsc, &sc->sc_vq[VQCTL], 2, NBPG, 1,
"control") == 0) {
sc->sc_vq[VQCTL].vq_done = vio_ctrleof;
virtio_start_vq_intr(vsc, &sc->sc_vq[VQCTL]);
vsc->sc_nvqs = 3;
}
}
if (vio_alloc_mem(sc) < 0)
goto err;
strlcpy(ifp->if_xname, self->dv_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_start = vio_start;
ifp->if_ioctl = vio_ioctl;
ifp->if_capabilities = IFCAP_VLAN_MTU;
if (virtio_has_feature(vsc, VIRTIO_NET_F_CSUM))
ifp->if_capabilities |= IFCAP_CSUM_TCPv4|IFCAP_CSUM_UDPv4;
ifq_set_maxlen(&ifp->if_snd, vsc->sc_vqs[1].vq_num - 1);
ifmedia_init(&sc->sc_media, 0, vio_media_change, vio_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
vsc->sc_config_change = vio_config_change;
timeout_set(&sc->sc_txtick, vio_txtick, &sc->sc_vq[VQTX]);
timeout_set(&sc->sc_rxtick, vio_rxtick, &sc->sc_vq[VQRX]);
if_attach(ifp);
ether_ifattach(ifp);
return;
err:
for (i = 0; i < vsc->sc_nvqs; i++)
virtio_free_vq(vsc, &sc->sc_vq[i]);
vsc->sc_nvqs = 0;
vsc->sc_child = VIRTIO_CHILD_ERROR;
return;
}
/* check link status */
void
vio_link_state(struct ifnet *ifp)
{
struct vio_softc *sc = ifp->if_softc;
struct virtio_softc *vsc = sc->sc_virtio;
int link_state = LINK_STATE_FULL_DUPLEX;
if (virtio_has_feature(vsc, VIRTIO_NET_F_STATUS)) {
int status = virtio_read_device_config_2(vsc,
VIRTIO_NET_CONFIG_STATUS);
if (!(status & VIRTIO_NET_S_LINK_UP))
link_state = LINK_STATE_DOWN;
}
if (ifp->if_link_state != link_state) {
ifp->if_link_state = link_state;
if_link_state_change(ifp);
}
}
int
vio_config_change(struct virtio_softc *vsc)
{
struct vio_softc *sc = (struct vio_softc *)vsc->sc_child;
vio_link_state(&sc->sc_ac.ac_if);
return 1;
}
int
vio_media_change(struct ifnet *ifp)
{
/* Ignore */
return (0);
}
void
vio_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
imr->ifm_active = IFM_ETHER | IFM_AUTO;
imr->ifm_status = IFM_AVALID;
vio_link_state(ifp);
if (LINK_STATE_IS_UP(ifp->if_link_state) && ifp->if_flags & IFF_UP)
imr->ifm_status |= IFM_ACTIVE|IFM_FDX;
}
/*
* Interface functions for ifnet
*/
int
vio_init(struct ifnet *ifp)
{
struct vio_softc *sc = ifp->if_softc;
vio_stop(ifp, 0);
if_rxr_init(&sc->sc_rx_ring, 2 * ((ifp->if_hardmtu / MCLBYTES) + 1),
sc->sc_vq[VQRX].vq_num);
vio_populate_rx_mbufs(sc);
ifp->if_flags |= IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
vio_iff(sc);
vio_link_state(ifp);
return 0;
}
void
vio_stop(struct ifnet *ifp, int disable)
{
struct vio_softc *sc = ifp->if_softc;
struct virtio_softc *vsc = sc->sc_virtio;
timeout_del(&sc->sc_txtick);
timeout_del(&sc->sc_rxtick);
ifp->if_flags &= ~IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
/* only way to stop I/O and DMA is resetting... */
virtio_reset(vsc);
vio_rxeof(sc);
if (vsc->sc_nvqs >= 3)
vio_ctrleof(&sc->sc_vq[VQCTL]);
vio_tx_drain(sc);
if (disable)
vio_rx_drain(sc);
virtio_reinit_start(vsc);
virtio_start_vq_intr(vsc, &sc->sc_vq[VQRX]);
virtio_stop_vq_intr(vsc, &sc->sc_vq[VQTX]);
if (vsc->sc_nvqs >= 3)
virtio_start_vq_intr(vsc, &sc->sc_vq[VQCTL]);
virtio_reinit_end(vsc);
if (vsc->sc_nvqs >= 3) {
if (sc->sc_ctrl_inuse != FREE)
sc->sc_ctrl_inuse = RESET;
wakeup(&sc->sc_ctrl_inuse);
}
}
void
vio_start(struct ifnet *ifp)
{
struct vio_softc *sc = ifp->if_softc;
struct virtio_softc *vsc = sc->sc_virtio;
struct virtqueue *vq = &sc->sc_vq[VQTX];
struct mbuf *m;
int queued = 0;
vio_txeof(vq);
if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
return;
if (ifq_empty(&ifp->if_snd))
return;
again:
for (;;) {
int slot, r;
struct virtio_net_hdr *hdr;
m = ifq_deq_begin(&ifp->if_snd);
if (m == NULL)
break;
r = virtio_enqueue_prep(vq, &slot);
if (r == EAGAIN) {
ifq_deq_rollback(&ifp->if_snd, m);
ifq_set_oactive(&ifp->if_snd);
break;
}
if (r != 0)
panic("enqueue_prep for a tx buffer: %d", r);
hdr = &sc->sc_tx_hdrs[slot];
memset(hdr, 0, sc->sc_hdr_size);
if (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT|M_UDP_CSUM_OUT)) {
struct mbuf *mip;
struct ip *ip;
int ehdrlen = ETHER_HDR_LEN;
int ipoff;
#if NVLAN > 0
struct ether_vlan_header *eh;
eh = mtod(m, struct ether_vlan_header *);
if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN))
ehdrlen += ETHER_VLAN_ENCAP_LEN;
#endif
if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT)
hdr->csum_offset = offsetof(struct tcphdr, th_sum);
else
hdr->csum_offset = offsetof(struct udphdr, uh_sum);
mip = m_getptr(m, ehdrlen, &ipoff);
KASSERT(mip != NULL && mip->m_len - ipoff >= sizeof(*ip));
ip = (struct ip *)(mip->m_data + ipoff);
hdr->csum_start = ehdrlen + (ip->ip_hl << 2);
hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
}
r = vio_encap(sc, slot, m);
if (r != 0) {
virtio_enqueue_abort(vq, slot);
ifq_deq_commit(&ifp->if_snd, m);
m_freem(m);
ifp->if_oerrors++;
continue;
}
r = virtio_enqueue_reserve(vq, slot,
sc->sc_tx_dmamaps[slot]->dm_nsegs + 1);
if (r != 0) {
bus_dmamap_unload(vsc->sc_dmat,
sc->sc_tx_dmamaps[slot]);
ifq_deq_rollback(&ifp->if_snd, m);
sc->sc_tx_mbufs[slot] = NULL;
ifq_set_oactive(&ifp->if_snd);
break;
}
ifq_deq_commit(&ifp->if_snd, m);
bus_dmamap_sync(vsc->sc_dmat, sc->sc_tx_dmamaps[slot], 0,
sc->sc_tx_dmamaps[slot]->dm_mapsize, BUS_DMASYNC_PREWRITE);
VIO_DMAMEM_SYNC(vsc, sc, hdr, sc->sc_hdr_size,
BUS_DMASYNC_PREWRITE);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, hdr, sc->sc_hdr_size, 1);
virtio_enqueue(vq, slot, sc->sc_tx_dmamaps[slot], 1);
virtio_enqueue_commit(vsc, vq, slot, 0);
queued++;
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif
}
if (ifq_is_oactive(&ifp->if_snd)) {
int r;
if (virtio_has_feature(vsc, VIRTIO_F_RING_EVENT_IDX))
r = virtio_postpone_intr_smart(&sc->sc_vq[VQTX]);
else
r = virtio_start_vq_intr(vsc, &sc->sc_vq[VQTX]);
if (r) {
vio_txeof(vq);
goto again;
}
}
if (queued > 0) {
virtio_notify(vsc, vq);
timeout_add_sec(&sc->sc_txtick, 1);
}
}
#if VIRTIO_DEBUG
void
vio_dump(struct vio_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct virtio_softc *vsc = sc->sc_virtio;
printf("%s status dump:\n", ifp->if_xname);
printf("TX virtqueue:\n");
virtio_vq_dump(&vsc->sc_vqs[VQTX]);
printf("tx tick active: %d\n", !timeout_triggered(&sc->sc_txtick));
printf("rx tick active: %d\n", !timeout_triggered(&sc->sc_rxtick));
printf("RX virtqueue:\n");
virtio_vq_dump(&vsc->sc_vqs[VQRX]);
if (vsc->sc_nvqs == 3) {
printf("CTL virtqueue:\n");
virtio_vq_dump(&vsc->sc_vqs[VQCTL]);
printf("ctrl_inuse: %d\n", sc->sc_ctrl_inuse);
}
}
#endif
int
vio_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct vio_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int s, r = 0;
s = splnet();
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
if (!(ifp->if_flags & IFF_RUNNING))
vio_init(ifp);
break;
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
#if VIRTIO_DEBUG
if (ifp->if_flags & IFF_DEBUG)
vio_dump(sc);
#endif
if (ifp->if_flags & IFF_RUNNING)
r = ENETRESET;
else
vio_init(ifp);
} else {
if (ifp->if_flags & IFF_RUNNING)
vio_stop(ifp, 1);
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
r = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
break;
case SIOCGIFRXR:
r = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
NULL, MCLBYTES, &sc->sc_rx_ring);
break;
default:
r = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
}
if (r == ENETRESET) {
if (ifp->if_flags & IFF_RUNNING)
vio_iff(sc);
r = 0;
}
splx(s);
return r;
}
/*
* Receive implementation
*/
/* allocate and initialize a mbuf for receive */
int
vio_add_rx_mbuf(struct vio_softc *sc, int i)
{
struct mbuf *m;
int r;
m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
if (m == NULL)
return ENOBUFS;
sc->sc_rx_mbufs[i] = m;
m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
r = bus_dmamap_load_mbuf(sc->sc_virtio->sc_dmat, sc->sc_rx_dmamaps[i],
m, BUS_DMA_READ|BUS_DMA_NOWAIT);
if (r) {
m_freem(m);
sc->sc_rx_mbufs[i] = NULL;
return r;
}
return 0;
}
/* free a mbuf for receive */
void
vio_free_rx_mbuf(struct vio_softc *sc, int i)
{
bus_dmamap_unload(sc->sc_virtio->sc_dmat, sc->sc_rx_dmamaps[i]);
m_freem(sc->sc_rx_mbufs[i]);
sc->sc_rx_mbufs[i] = NULL;
}
/* add mbufs for all the empty receive slots */
void
vio_populate_rx_mbufs(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
int r, done = 0;
u_int slots;
struct virtqueue *vq = &sc->sc_vq[VQRX];
int mrg_rxbuf = VIO_HAVE_MRG_RXBUF(sc);
for (slots = if_rxr_get(&sc->sc_rx_ring, vq->vq_num);
slots > 0; slots--) {
int slot;
r = virtio_enqueue_prep(vq, &slot);
if (r == EAGAIN)
break;
if (r != 0)
panic("enqueue_prep for rx buffers: %d", r);
if (sc->sc_rx_mbufs[slot] == NULL) {
r = vio_add_rx_mbuf(sc, slot);
if (r != 0) {
virtio_enqueue_abort(vq, slot);
break;
}
}
r = virtio_enqueue_reserve(vq, slot,
sc->sc_rx_dmamaps[slot]->dm_nsegs + (mrg_rxbuf ? 0 : 1));
if (r != 0) {
vio_free_rx_mbuf(sc, slot);
break;
}
bus_dmamap_sync(vsc->sc_dmat, sc->sc_rx_dmamaps[slot], 0,
MCLBYTES, BUS_DMASYNC_PREREAD);
if (mrg_rxbuf) {
virtio_enqueue(vq, slot, sc->sc_rx_dmamaps[slot], 0);
} else {
/*
* Buggy kvm wants a buffer of exactly the size of
* the header in this case, so we have to split in
* two.
*/
virtio_enqueue_p(vq, slot, sc->sc_rx_dmamaps[slot],
0, sc->sc_hdr_size, 0);
virtio_enqueue_p(vq, slot, sc->sc_rx_dmamaps[slot],
sc->sc_hdr_size, MCLBYTES - sc->sc_hdr_size, 0);
}
virtio_enqueue_commit(vsc, vq, slot, 0);
done = 1;
}
if_rxr_put(&sc->sc_rx_ring, slots);
if (done)
virtio_notify(vsc, vq);
timeout_add_sec(&sc->sc_rxtick, 1);
}
/* dequeue received packets */
int
vio_rxeof(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
struct virtqueue *vq = &sc->sc_vq[VQRX];
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf *m, *m0 = NULL, *mlast;
int r = 0;
int slot, len, bufs_left;
struct virtio_net_hdr *hdr;
while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
r = 1;
bus_dmamap_sync(vsc->sc_dmat, sc->sc_rx_dmamaps[slot], 0,
MCLBYTES, BUS_DMASYNC_POSTREAD);
m = sc->sc_rx_mbufs[slot];
KASSERT(m != NULL);
bus_dmamap_unload(vsc->sc_dmat, sc->sc_rx_dmamaps[slot]);
sc->sc_rx_mbufs[slot] = NULL;
virtio_dequeue_commit(vq, slot);
if_rxr_put(&sc->sc_rx_ring, 1);
m->m_len = m->m_pkthdr.len = len;
m->m_pkthdr.csum_flags = 0;
if (m0 == NULL) {
hdr = mtod(m, struct virtio_net_hdr *);
m_adj(m, sc->sc_hdr_size);
m0 = mlast = m;
if (VIO_HAVE_MRG_RXBUF(sc))
bufs_left = hdr->num_buffers - 1;
else
bufs_left = 0;
} else {
m->m_flags &= ~M_PKTHDR;
m0->m_pkthdr.len += m->m_len;
mlast->m_next = m;
mlast = m;
bufs_left--;
}
if (bufs_left == 0) {
ml_enqueue(&ml, m0);
m0 = NULL;
}
}
if (m0 != NULL) {
DPRINTF("%s: expected %d buffers, got %d\n", __func__,
(int)hdr->num_buffers,
(int)hdr->num_buffers - bufs_left);
ifp->if_ierrors++;
m_freem(m0);
}
if (ifiq_input(&ifp->if_rcv, &ml))
if_rxr_livelocked(&sc->sc_rx_ring);
return r;
}
int
vio_rx_intr(struct virtqueue *vq)
{
struct virtio_softc *vsc = vq->vq_owner;
struct vio_softc *sc = (struct vio_softc *)vsc->sc_child;
int r, sum = 0;
again:
r = vio_rxeof(sc);
sum += r;
if (r) {
vio_populate_rx_mbufs(sc);
/* set used event index to the next slot */
if (virtio_has_feature(vsc, VIRTIO_F_RING_EVENT_IDX)) {
if (virtio_start_vq_intr(vq->vq_owner, vq))
goto again;
}
}
return sum;
}
void
vio_rxtick(void *arg)
{
struct virtqueue *vq = arg;
struct virtio_softc *vsc = vq->vq_owner;
struct vio_softc *sc = (struct vio_softc *)vsc->sc_child;
int s;
s = splnet();
vio_populate_rx_mbufs(sc);
splx(s);
}
/* free all the mbufs; called from if_stop(disable) */
void
vio_rx_drain(struct vio_softc *sc)
{
struct virtqueue *vq = &sc->sc_vq[VQRX];
int i;
for (i = 0; i < vq->vq_num; i++) {
if (sc->sc_rx_mbufs[i] == NULL)
continue;
vio_free_rx_mbuf(sc, i);
}
}
/*
* Transmission implementation
*/
/* actual transmission is done in if_start */
/* tx interrupt; dequeue and free mbufs */
/*
* tx interrupt is actually disabled unless the tx queue is full, i.e.
* IFF_OACTIVE is set. vio_txtick is used to make sure that mbufs
* are dequeued and freed even if no further transfer happens.
*/
int
vio_tx_intr(struct virtqueue *vq)
{
struct virtio_softc *vsc = vq->vq_owner;
struct vio_softc *sc = (struct vio_softc *)vsc->sc_child;
struct ifnet *ifp = &sc->sc_ac.ac_if;
int r;
r = vio_txeof(vq);
vio_start(ifp);
return r;
}
void
vio_txtick(void *arg)
{
struct virtqueue *vq = arg;
int s = splnet();
vio_tx_intr(vq);
splx(s);
}
int
vio_txeof(struct virtqueue *vq)
{
struct virtio_softc *vsc = vq->vq_owner;
struct vio_softc *sc = (struct vio_softc *)vsc->sc_child;
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct mbuf *m;
int r = 0;
int slot, len;
while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
struct virtio_net_hdr *hdr = &sc->sc_tx_hdrs[slot];
r++;
VIO_DMAMEM_SYNC(vsc, sc, hdr, sc->sc_hdr_size,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_sync(vsc->sc_dmat, sc->sc_tx_dmamaps[slot], 0,
sc->sc_tx_dmamaps[slot]->dm_mapsize,
BUS_DMASYNC_POSTWRITE);
m = sc->sc_tx_mbufs[slot];
bus_dmamap_unload(vsc->sc_dmat, sc->sc_tx_dmamaps[slot]);
sc->sc_tx_mbufs[slot] = NULL;
virtio_dequeue_commit(vq, slot);
m_freem(m);
}
if (r) {
ifq_clr_oactive(&ifp->if_snd);
virtio_stop_vq_intr(vsc, &sc->sc_vq[VQTX]);
}
if (vq->vq_used_idx == vq->vq_avail_idx)
timeout_del(&sc->sc_txtick);
else if (r)
timeout_add_sec(&sc->sc_txtick, 1);
return r;
}
int
vio_encap(struct vio_softc *sc, int slot, struct mbuf *m)
{
struct virtio_softc *vsc = sc->sc_virtio;
bus_dmamap_t dmap= sc->sc_tx_dmamaps[slot];
int r;
r = bus_dmamap_load_mbuf(vsc->sc_dmat, dmap, m,
BUS_DMA_WRITE|BUS_DMA_NOWAIT);
switch (r) {
case 0:
break;
case EFBIG:
if (m_defrag(m, M_DONTWAIT) == 0 &&
bus_dmamap_load_mbuf(vsc->sc_dmat, dmap, m,
BUS_DMA_WRITE|BUS_DMA_NOWAIT) == 0)
break;
/* FALLTHROUGH */
default:
return ENOBUFS;
}
sc->sc_tx_mbufs[slot] = m;
return 0;
}
/* free all the mbufs already put on vq; called from if_stop(disable) */
void
vio_tx_drain(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
struct virtqueue *vq = &sc->sc_vq[VQTX];
int i;
for (i = 0; i < vq->vq_num; i++) {
if (sc->sc_tx_mbufs[i] == NULL)
continue;
bus_dmamap_unload(vsc->sc_dmat, sc->sc_tx_dmamaps[i]);
m_freem(sc->sc_tx_mbufs[i]);
sc->sc_tx_mbufs[i] = NULL;
}
}
/*
* Control vq
*/
/* issue a VIRTIO_NET_CTRL_RX class command and wait for completion */
int
vio_ctrl_rx(struct vio_softc *sc, int cmd, int onoff)
{
struct virtio_softc *vsc = sc->sc_virtio;
struct virtqueue *vq = &sc->sc_vq[VQCTL];
int r, slot;
splassert(IPL_NET);
if ((r = vio_wait_ctrl(sc)) != 0)
return r;
sc->sc_ctrl_cmd->class = VIRTIO_NET_CTRL_RX;
sc->sc_ctrl_cmd->command = cmd;
sc->sc_ctrl_rx->onoff = onoff;
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_cmd,
sizeof(*sc->sc_ctrl_cmd), BUS_DMASYNC_PREWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_rx,
sizeof(*sc->sc_ctrl_rx), BUS_DMASYNC_PREWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_status,
sizeof(*sc->sc_ctrl_status), BUS_DMASYNC_PREREAD);
r = virtio_enqueue_prep(vq, &slot);
if (r != 0)
panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
r = virtio_enqueue_reserve(vq, slot, 3);
if (r != 0)
panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_cmd,
sizeof(*sc->sc_ctrl_cmd), 1);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_rx,
sizeof(*sc->sc_ctrl_rx), 1);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_status,
sizeof(*sc->sc_ctrl_status), 0);
virtio_enqueue_commit(vsc, vq, slot, 1);
if ((r = vio_wait_ctrl_done(sc)) != 0)
goto out;
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_cmd,
sizeof(*sc->sc_ctrl_cmd), BUS_DMASYNC_POSTWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_rx,
sizeof(*sc->sc_ctrl_rx), BUS_DMASYNC_POSTWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_status,
sizeof(*sc->sc_ctrl_status), BUS_DMASYNC_POSTREAD);
if (sc->sc_ctrl_status->ack == VIRTIO_NET_OK) {
r = 0;
} else {
printf("%s: ctrl cmd %d failed\n", sc->sc_dev.dv_xname, cmd);
r = EIO;
}
DPRINTF("%s: cmd %d %d: %d\n", __func__, cmd, (int)onoff, r);
out:
vio_ctrl_wakeup(sc, FREE);
return r;
}
/*
* XXXSMP As long as some per-ifp ioctl(2)s are executed with the
* NET_LOCK() deadlocks are possible. So release it here.
*/
static inline int
vio_sleep(struct vio_softc *sc, const char *wmesg)
{
int status = rw_status(&netlock);
if (status != RW_WRITE && status != RW_READ)
return tsleep_nsec(&sc->sc_ctrl_inuse, PRIBIO|PCATCH, wmesg,
INFSLP);
return rwsleep_nsec(&sc->sc_ctrl_inuse, &netlock, PRIBIO|PCATCH, wmesg,
INFSLP);
}
int
vio_wait_ctrl(struct vio_softc *sc)
{
int r = 0;
while (sc->sc_ctrl_inuse != FREE) {
r = vio_sleep(sc, "viowait");
if (r == EINTR)
return r;
}
sc->sc_ctrl_inuse = INUSE;
return r;
}
int
vio_wait_ctrl_done(struct vio_softc *sc)
{
int r = 0;
while (sc->sc_ctrl_inuse != DONE && sc->sc_ctrl_inuse != RESET) {
if (sc->sc_ctrl_inuse == RESET) {
r = 1;
break;
}
r = vio_sleep(sc, "viodone");
if (r == EINTR)
break;
}
return r;
}
void
vio_ctrl_wakeup(struct vio_softc *sc, enum vio_ctrl_state new)
{
sc->sc_ctrl_inuse = new;
wakeup(&sc->sc_ctrl_inuse);
}
int
vio_ctrleof(struct virtqueue *vq)
{
struct virtio_softc *vsc = vq->vq_owner;
struct vio_softc *sc = (struct vio_softc *)vsc->sc_child;
int r = 0, ret, slot;
again:
ret = virtio_dequeue(vsc, vq, &slot, NULL);
if (ret == ENOENT)
return r;
virtio_dequeue_commit(vq, slot);
r++;
vio_ctrl_wakeup(sc, DONE);
if (virtio_start_vq_intr(vsc, vq))
goto again;
return r;
}
/* issue VIRTIO_NET_CTRL_MAC_TABLE_SET command and wait for completion */
int
vio_set_rx_filter(struct vio_softc *sc)
{
/* filter already set in sc_ctrl_mac_tbl */
struct virtio_softc *vsc = sc->sc_virtio;
struct virtqueue *vq = &sc->sc_vq[VQCTL];
int r, slot;
splassert(IPL_NET);
if ((r = vio_wait_ctrl(sc)) != 0)
return r;
sc->sc_ctrl_cmd->class = VIRTIO_NET_CTRL_MAC;
sc->sc_ctrl_cmd->command = VIRTIO_NET_CTRL_MAC_TABLE_SET;
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_cmd,
sizeof(*sc->sc_ctrl_cmd), BUS_DMASYNC_PREWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_mac_info,
VIO_CTRL_MAC_INFO_SIZE, BUS_DMASYNC_PREWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_status,
sizeof(*sc->sc_ctrl_status), BUS_DMASYNC_PREREAD);
r = virtio_enqueue_prep(vq, &slot);
if (r != 0)
panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
r = virtio_enqueue_reserve(vq, slot, 4);
if (r != 0)
panic("%s: control vq busy!?", sc->sc_dev.dv_xname);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_cmd,
sizeof(*sc->sc_ctrl_cmd), 1);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_mac_tbl_uc,
sizeof(*sc->sc_ctrl_mac_tbl_uc) +
sc->sc_ctrl_mac_tbl_uc->nentries * ETHER_ADDR_LEN, 1);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_mac_tbl_mc,
sizeof(*sc->sc_ctrl_mac_tbl_mc) +
sc->sc_ctrl_mac_tbl_mc->nentries * ETHER_ADDR_LEN, 1);
VIO_DMAMEM_ENQUEUE(sc, vq, slot, sc->sc_ctrl_status,
sizeof(*sc->sc_ctrl_status), 0);
virtio_enqueue_commit(vsc, vq, slot, 1);
if ((r = vio_wait_ctrl_done(sc)) != 0)
goto out;
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_cmd,
sizeof(*sc->sc_ctrl_cmd), BUS_DMASYNC_POSTWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_mac_info,
VIO_CTRL_MAC_INFO_SIZE, BUS_DMASYNC_POSTWRITE);
VIO_DMAMEM_SYNC(vsc, sc, sc->sc_ctrl_status,
sizeof(*sc->sc_ctrl_status), BUS_DMASYNC_POSTREAD);
if (sc->sc_ctrl_status->ack == VIRTIO_NET_OK) {
r = 0;
} else {
/* The host's filter table is not large enough */
printf("%s: failed setting rx filter\n", sc->sc_dev.dv_xname);
r = EIO;
}
out:
vio_ctrl_wakeup(sc, FREE);
return r;
}
void
vio_iff(struct vio_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct arpcom *ac = &sc->sc_ac;
struct ether_multi *enm;
struct ether_multistep step;
int nentries = 0;
int promisc = 0, allmulti = 0, rxfilter = 0;
int r;
splassert(IPL_NET);
ifp->if_flags &= ~IFF_ALLMULTI;
if (vsc->sc_nvqs < 3) {
/* no ctrl vq; always promisc */
ifp->if_flags |= IFF_ALLMULTI | IFF_PROMISC;
return;
}
if (sc->sc_dev.dv_cfdata->cf_flags & CONFFLAG_QEMU_VLAN_BUG)
ifp->if_flags |= IFF_PROMISC;
if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0 ||
ac->ac_multicnt >= VIRTIO_NET_CTRL_MAC_MC_ENTRIES) {
ifp->if_flags |= IFF_ALLMULTI;
if (ifp->if_flags & IFF_PROMISC)
promisc = 1;
else
allmulti = 1;
} else {
rxfilter = 1;
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
memcpy(sc->sc_ctrl_mac_tbl_mc->macs[nentries++],
enm->enm_addrlo, ETHER_ADDR_LEN);
ETHER_NEXT_MULTI(step, enm);
}
}
/* set unicast address, VirtualBox wants that */
memcpy(sc->sc_ctrl_mac_tbl_uc->macs[0], ac->ac_enaddr, ETHER_ADDR_LEN);
sc->sc_ctrl_mac_tbl_uc->nentries = 1;
sc->sc_ctrl_mac_tbl_mc->nentries = rxfilter ? nentries : 0;
if (vsc->sc_nvqs < 3)
return;
r = vio_set_rx_filter(sc);
if (r == EIO)
allmulti = 1; /* fallback */
else if (r != 0)
return;
r = vio_ctrl_rx(sc, VIRTIO_NET_CTRL_RX_ALLMULTI, allmulti);
if (r == EIO)
promisc = 1; /* fallback */
else if (r != 0)
return;
vio_ctrl_rx(sc, VIRTIO_NET_CTRL_RX_PROMISC, promisc);
}
23
14
17
11
1
11
11
10
1
9
5
3
2
56
2
55
5
1
4
5
21
21
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/* $OpenBSD: cons.c,v 1.30 2022/07/02 08:50:41 visa Exp $ */
/* $NetBSD: cons.c,v 1.30 1996/04/08 19:57:30 jonathan Exp $ */
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: cons.c 1.7 92/01/21$
*
* @(#)cons.c 8.2 (Berkeley) 1/12/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <dev/cons.h>
struct tty *constty = NULL; /* virtual console output device */
struct vnode *cn_devvp = NULLVP; /* vnode for underlying device. */
int
cnopen(dev_t dev, int flag, int mode, struct proc *p)
{
dev_t cndev;
if (cn_tab == NULL)
return (0);
/*
* always open the 'real' console device, so we don't get nailed
* later. This follows normal device semantics; they always get
* open() calls.
*/
cndev = cn_tab->cn_dev;
if (cndev == NODEV)
return (ENXIO);
#ifdef DIAGNOSTIC
if (cndev == dev)
panic("cnopen: recursive");
#endif
if (cn_devvp == NULLVP) {
/* try to get a reference on its vnode, but fail silently */
cdevvp(cndev, &cn_devvp);
}
return ((*cdevsw[major(cndev)].d_open)(cndev, flag, mode, p));
}
int
cnclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct vnode *vp;
if (cn_tab == NULL)
return (0);
/*
* If the real console isn't otherwise open, close it.
* If it's otherwise open, don't close it, because that'll
* screw up others who have it open.
*/
dev = cn_tab->cn_dev;
if (cn_devvp != NULLVP) {
/* release our reference to real dev's vnode */
vrele(cn_devvp);
cn_devvp = NULLVP;
}
if (vfinddev(dev, VCHR, &vp) && vcount(vp))
return (0);
return ((*cdevsw[major(dev)].d_close)(dev, flag, mode, p));
}
int
cnread(dev_t dev, struct uio *uio, int flag)
{
/*
* If we would redirect input, punt. This will keep strange
* things from happening to people who are using the real
* console. Nothing should be using /dev/console for
* input (except a shell in single-user mode, but then,
* one wouldn't TIOCCONS then).
*/
if (constty != NULL)
return 0;
else if (cn_tab == NULL)
return ENXIO;
dev = cn_tab->cn_dev;
return ((*cdevsw[major(dev)].d_read)(dev, uio, flag));
}
int
cnwrite(dev_t dev, struct uio *uio, int flag)
{
/*
* Redirect output, if that's appropriate.
* If there's no real console, return ENXIO.
*/
if (constty != NULL)
dev = constty->t_dev;
else if (cn_tab == NULL)
return ENXIO;
else
dev = cn_tab->cn_dev;
return ((*cdevsw[major(dev)].d_write)(dev, uio, flag));
}
int
cnstop(struct tty *tp, int flag)
{
return (0);
}
int
cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag,
struct proc *p)
{
int error;
/*
* Superuser can always use this to wrest control of console
* output from the "virtual" console.
*/
if (cmd == TIOCCONS && constty != NULL) {
error = suser(p);
if (error)
return (error);
constty = NULL;
return (0);
}
/*
* Redirect the ioctl, if that's appropriate.
* Note that strange things can happen, if a program does
* ioctls on /dev/console, then the console is redirected
* out from under it.
*/
if (constty != NULL)
dev = constty->t_dev;
else if (cn_tab == NULL)
return ENXIO;
else
dev = cn_tab->cn_dev;
return ((*cdevsw[major(dev)].d_ioctl)(dev, cmd, data, flag, p));
}
int
cnkqfilter(dev_t dev, struct knote *kn)
{
/*
* Redirect output, if that's appropriate.
* If there's no real console, return 1.
*/
if (constty != NULL)
dev = constty->t_dev;
else if (cn_tab == NULL)
return (ENXIO);
else
dev = cn_tab->cn_dev;
if (cdevsw[major(dev)].d_kqfilter)
return ((*cdevsw[major(dev)].d_kqfilter)(dev, kn));
return (EOPNOTSUPP);
}
int
cngetc(void)
{
if (cn_tab == NULL)
return (0);
return ((*cn_tab->cn_getc)(cn_tab->cn_dev));
}
void
cnputc(int c)
{
if (cn_tab == NULL)
return;
if (c) {
(*cn_tab->cn_putc)(cn_tab->cn_dev, c);
if (c == '\n')
(*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
}
}
void
cnpollc(int on)
{
static int refcount = 0;
if (cn_tab == NULL)
return;
if (!on)
--refcount;
if (refcount == 0)
(*cn_tab->cn_pollc)(cn_tab->cn_dev, on);
if (on)
++refcount;
}
void
nullcnpollc(dev_t dev, int on)
{
}
void
cnbell(u_int pitch, u_int period, u_int volume)
{
if (cn_tab == NULL || cn_tab->cn_bell == NULL)
return;
(*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume);
}
5
1
8
6
6
5
1
17
4
13
13
17
17
3
14
568
7
1
8
7
2
6
3
14
17
13
11
11
9
3
1
4
3
1
4
9
9
32
16
11
5
2
1
1
10
821
1
822
822
11
7
18
7
1
1
1
3
1
1
1
1
2
8
10
4
4
3
7
1
660
660
3
541
124
2
656
1
1
2
2
4
1
3
3
3
3
4
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
/* $OpenBSD: if_tun.c,v 1.237 2022/07/02 08:50:42 visa Exp $ */
/* $NetBSD: if_tun.c,v 1.24 1996/05/07 02:40:48 thorpej Exp $ */
/*
* Copyright (c) 1988, Julian Onions <Julian.Onions@nexor.co.uk>
* Nottingham University 1987.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This driver takes packets off the IP i/f and hands them up to a
* user process to have its wicked way with. This driver has its
* roots in a similar driver written by Phil Cockcroft (formerly) at
* UCL. This driver is based much more on read/write/select mode of
* operation though.
*/
/* #define TUN_DEBUG 9 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/sigio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/selinfo.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/device.h>
#include <sys/vnode.h>
#include <sys/signalvar.h>
#include <sys/conf.h>
#include <sys/smr.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/rtable.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#ifdef MPLS
#include <netmpls/mpls.h>
#endif /* MPLS */
#include <net/if_tun.h>
struct tun_softc {
struct arpcom sc_ac; /* ethernet common data */
#define sc_if sc_ac.ac_if
struct selinfo sc_rsel; /* read select */
struct selinfo sc_wsel; /* write select (not used) */
SMR_LIST_ENTRY(tun_softc)
sc_entry; /* all tunnel interfaces */
int sc_unit;
struct sigio_ref sc_sigio; /* async I/O registration */
unsigned int sc_flags; /* misc flags */
#define TUN_DEAD (1 << 16)
dev_t sc_dev;
struct refcnt sc_refs;
unsigned int sc_reading;
};
#ifdef TUN_DEBUG
int tundebug = TUN_DEBUG;
#define TUNDEBUG(a) (tundebug? printf a : 0)
#else
#define TUNDEBUG(a) /* (tundebug? printf a : 0) */
#endif
/* Only these IFF flags are changeable by TUNSIFINFO */
#define TUN_IFF_FLAGS (IFF_UP|IFF_POINTOPOINT|IFF_MULTICAST|IFF_BROADCAST)
void tunattach(int);
int tun_dev_open(dev_t, const struct if_clone *, int, struct proc *);
int tun_dev_close(dev_t, struct proc *);
int tun_dev_ioctl(dev_t, u_long, void *);
int tun_dev_read(dev_t, struct uio *, int);
int tun_dev_write(dev_t, struct uio *, int, int);
int tun_dev_kqfilter(dev_t, struct knote *);
int tun_ioctl(struct ifnet *, u_long, caddr_t);
void tun_input(struct ifnet *, struct mbuf *);
int tun_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int tun_enqueue(struct ifnet *, struct mbuf *);
int tun_clone_create(struct if_clone *, int);
int tap_clone_create(struct if_clone *, int);
int tun_create(struct if_clone *, int, int);
int tun_clone_destroy(struct ifnet *);
void tun_wakeup(struct tun_softc *);
int tun_init(struct tun_softc *);
void tun_start(struct ifnet *);
int filt_tunread(struct knote *, long);
int filt_tunwrite(struct knote *, long);
void filt_tunrdetach(struct knote *);
void filt_tunwdetach(struct knote *);
void tun_link_state(struct ifnet *, int);
const struct filterops tunread_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_tunrdetach,
.f_event = filt_tunread,
};
const struct filterops tunwrite_filtops = {
.f_flags = FILTEROP_ISFD,
.f_attach = NULL,
.f_detach = filt_tunwdetach,
.f_event = filt_tunwrite,
};
SMR_LIST_HEAD(tun_list, tun_softc);
struct if_clone tun_cloner =
IF_CLONE_INITIALIZER("tun", tun_clone_create, tun_clone_destroy);
struct if_clone tap_cloner =
IF_CLONE_INITIALIZER("tap", tap_clone_create, tun_clone_destroy);
void
tunattach(int n)
{
if_clone_attach(&tun_cloner);
if_clone_attach(&tap_cloner);
}
int
tun_clone_create(struct if_clone *ifc, int unit)
{
return (tun_create(ifc, unit, 0));
}
int
tap_clone_create(struct if_clone *ifc, int unit)
{
return (tun_create(ifc, unit, TUN_LAYER2));
}
struct tun_list tun_devs_list = SMR_LIST_HEAD_INITIALIZER(tun_list);
struct tun_softc *
tun_name_lookup(const char *name)
{
struct tun_softc *sc;
KERNEL_ASSERT_LOCKED();
SMR_LIST_FOREACH_LOCKED(sc, &tun_devs_list, sc_entry) {
if (strcmp(sc->sc_if.if_xname, name) == 0)
return (sc);
}
return (NULL);
}
int
tun_insert(struct tun_softc *sc)
{
int error = 0;
/* check for a race */
if (tun_name_lookup(sc->sc_if.if_xname) != NULL)
error = EEXIST;
else {
/* tun_name_lookup checks for the right lock already */
SMR_LIST_INSERT_HEAD_LOCKED(&tun_devs_list, sc, sc_entry);
}
return (error);
}
int
tun_create(struct if_clone *ifc, int unit, int flags)
{
struct tun_softc *sc;
struct ifnet *ifp;
if (unit > minor(~0U))
return (ENXIO);
KERNEL_ASSERT_LOCKED();
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
refcnt_init(&sc->sc_refs);
ifp = &sc->sc_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname),
"%s%d", ifc->ifc_name, unit);
ifp->if_softc = sc;
/* this is enough state for tun_dev_open to work with */
if (tun_insert(sc) != 0)
goto exists;
/* build the interface */
ifp->if_ioctl = tun_ioctl;
ifp->if_enqueue = tun_enqueue;
ifp->if_start = tun_start;
ifp->if_hardmtu = TUNMRU;
ifp->if_link_state = LINK_STATE_DOWN;
if_counters_alloc(ifp);
if ((flags & TUN_LAYER2) == 0) {
#if NBPFILTER > 0
ifp->if_bpf_mtap = bpf_mtap;
#endif
ifp->if_input = tun_input;
ifp->if_output = tun_output;
ifp->if_mtu = ETHERMTU;
ifp->if_flags = (IFF_POINTOPOINT|IFF_MULTICAST);
ifp->if_type = IFT_TUNNEL;
ifp->if_hdrlen = sizeof(u_int32_t);
ifp->if_rtrequest = p2p_rtrequest;
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
#endif
} else {
sc->sc_flags |= TUN_LAYER2;
ether_fakeaddr(ifp);
ifp->if_flags =
(IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST);
if_attach(ifp);
ether_ifattach(ifp);
}
sigio_init(&sc->sc_sigio);
/* tell tun_dev_open we're initialised */
sc->sc_flags |= TUN_INITED|TUN_STAYUP;
wakeup(sc);
return (0);
exists:
free(sc, M_DEVBUF, sizeof(*sc));
return (EEXIST);
}
int
tun_clone_destroy(struct ifnet *ifp)
{
struct tun_softc *sc = ifp->if_softc;
dev_t dev;
int s;
KERNEL_ASSERT_LOCKED();
if (ISSET(sc->sc_flags, TUN_DEAD))
return (ENXIO);
SET(sc->sc_flags, TUN_DEAD);
/* kick userland off the device */
dev = sc->sc_dev;
if (dev) {
struct vnode *vp;
if (vfinddev(dev, VCHR, &vp))
VOP_REVOKE(vp, REVOKEALL);
KASSERT(sc->sc_dev == 0);
}
/* prevent userland from getting to the device again */
SMR_LIST_REMOVE_LOCKED(sc, sc_entry);
smr_barrier();
/* help read() give up */
if (sc->sc_reading)
wakeup(&ifp->if_snd);
/* wait for device entrypoints to finish */
refcnt_finalize(&sc->sc_refs, "tundtor");
s = splhigh();
klist_invalidate(&sc->sc_rsel.si_note);
klist_invalidate(&sc->sc_wsel.si_note);
splx(s);
if (ISSET(sc->sc_flags, TUN_LAYER2))
ether_ifdetach(ifp);
if_detach(ifp);
sigio_free(&sc->sc_sigio);
free(sc, M_DEVBUF, sizeof *sc);
return (0);
}
static struct tun_softc *
tun_get(dev_t dev)
{
struct tun_softc *sc;
smr_read_enter();
SMR_LIST_FOREACH(sc, &tun_devs_list, sc_entry) {
if (sc->sc_dev == dev) {
refcnt_take(&sc->sc_refs);
break;
}
}
smr_read_leave();
return (sc);
}
static inline void
tun_put(struct tun_softc *sc)
{
refcnt_rele_wake(&sc->sc_refs);
}
int
tunopen(dev_t dev, int flag, int mode, struct proc *p)
{
return (tun_dev_open(dev, &tun_cloner, mode, p));
}
int
tapopen(dev_t dev, int flag, int mode, struct proc *p)
{
return (tun_dev_open(dev, &tap_cloner, mode, p));
}
int
tun_dev_open(dev_t dev, const struct if_clone *ifc, int mode, struct proc *p)
{
struct tun_softc *sc;
struct ifnet *ifp;
int error;
u_short stayup = 0;
struct vnode *vp;
char name[IFNAMSIZ];
unsigned int rdomain;
/*
* Find the vnode associated with this open before we sleep
* and let something else revoke it. Our caller has a reference
* to it so we don't need to account for it.
*/
if (!vfinddev(dev, VCHR, &vp))
panic("%s vfinddev failed", __func__);
snprintf(name, sizeof(name), "%s%u", ifc->ifc_name, minor(dev));
rdomain = rtable_l2(p->p_p->ps_rtableid);
/* let's find or make an interface to work with */
while ((sc = tun_name_lookup(name)) == NULL) {
error = if_clone_create(name, rdomain);
switch (error) {
case 0: /* it's probably ours */
stayup = TUN_STAYUP;
/* FALLTHROUGH */
case EEXIST: /* we may have lost a race with someone else */
break;
default:
return (error);
}
}
refcnt_take(&sc->sc_refs);
/* wait for it to be fully constructed before we use it */
for (;;) {
if (ISSET(sc->sc_flags, TUN_DEAD)) {
error = ENXIO;
goto done;
}
if (ISSET(sc->sc_flags, TUN_INITED))
break;
error = tsleep_nsec(sc, PCATCH, "tuninit", INFSLP);
if (error != 0) {
/* XXX if_clone_destroy if stayup? */
goto done;
}
}
/* Has tun_clone_destroy torn the rug out under us? */
if (vp->v_type == VBAD) {
error = ENXIO;
goto done;
}
if (sc->sc_dev != 0) {
/* aww, we lost */
error = EBUSY;
goto done;
}
/* it's ours now */
sc->sc_dev = dev;
CLR(sc->sc_flags, stayup);
/* automatically mark the interface running on open */
ifp = &sc->sc_if;
NET_LOCK();
SET(ifp->if_flags, IFF_UP | IFF_RUNNING);
NET_UNLOCK();
tun_link_state(ifp, LINK_STATE_FULL_DUPLEX);
error = 0;
done:
tun_put(sc);
return (error);
}
/*
* tunclose - close the device; if closing the real device, flush pending
* output and unless STAYUP bring down and destroy the interface.
*/
int
tunclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (tun_dev_close(dev, p));
}
int
tapclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (tun_dev_close(dev, p));
}
int
tun_dev_close(dev_t dev, struct proc *p)
{
struct tun_softc *sc;
struct ifnet *ifp;
int error = 0;
char name[IFNAMSIZ];
int destroy = 0;
sc = tun_get(dev);
if (sc == NULL)
return (ENXIO);
ifp = &sc->sc_if;
/*
* junk all pending output
*/
NET_LOCK();
CLR(ifp->if_flags, IFF_UP | IFF_RUNNING);
NET_UNLOCK();
ifq_purge(&ifp->if_snd);
CLR(sc->sc_flags, TUN_ASYNC);
selwakeup(&sc->sc_rsel);
sigio_free(&sc->sc_sigio);
if (!ISSET(sc->sc_flags, TUN_DEAD)) {
/* we can't hold a reference to sc before we start a dtor */
if (!ISSET(sc->sc_flags, TUN_STAYUP)) {
destroy = 1;
strlcpy(name, ifp->if_xname, sizeof(name));
} else {
tun_link_state(ifp, LINK_STATE_DOWN);
}
}
sc->sc_dev = 0;
tun_put(sc);
if (destroy)
if_clone_destroy(name);
return (error);
}
int
tun_init(struct tun_softc *sc)
{
struct ifnet *ifp = &sc->sc_if;
struct ifaddr *ifa;
TUNDEBUG(("%s: tun_init\n", ifp->if_xname));
ifp->if_flags |= IFF_UP | IFF_RUNNING;
sc->sc_flags &= ~(TUN_IASET|TUN_DSTADDR|TUN_BRDADDR);
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *sin;
sin = satosin(ifa->ifa_addr);
if (sin && sin->sin_addr.s_addr)
sc->sc_flags |= TUN_IASET;
if (ifp->if_flags & IFF_POINTOPOINT) {
sin = satosin(ifa->ifa_dstaddr);
if (sin && sin->sin_addr.s_addr)
sc->sc_flags |= TUN_DSTADDR;
} else
sc->sc_flags &= ~TUN_DSTADDR;
if (ifp->if_flags & IFF_BROADCAST) {
sin = satosin(ifa->ifa_broadaddr);
if (sin && sin->sin_addr.s_addr)
sc->sc_flags |= TUN_BRDADDR;
} else
sc->sc_flags &= ~TUN_BRDADDR;
}
#ifdef INET6
if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6;
sin6 = satosin6(ifa->ifa_addr);
if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
sc->sc_flags |= TUN_IASET;
if (ifp->if_flags & IFF_POINTOPOINT) {
sin6 = satosin6(ifa->ifa_dstaddr);
if (sin6 &&
!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
sc->sc_flags |= TUN_DSTADDR;
} else
sc->sc_flags &= ~TUN_DSTADDR;
}
#endif /* INET6 */
}
return (0);
}
/*
* Process an ioctl request.
*/
int
tun_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct tun_softc *sc = (struct tun_softc *)(ifp->if_softc);
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
switch (cmd) {
case SIOCSIFADDR:
tun_init(sc);
break;
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP))
SET(ifp->if_flags, IFF_RUNNING);
else
CLR(ifp->if_flags, IFF_RUNNING);
break;
case SIOCSIFDSTADDR:
tun_init(sc);
TUNDEBUG(("%s: destination address set\n", ifp->if_xname));
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > TUNMRU)
error = EINVAL;
else
ifp->if_mtu = ifr->ifr_mtu;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
break;
default:
if (sc->sc_flags & TUN_LAYER2)
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
else
error = ENOTTY;
}
return (error);
}
/*
* tun_output - queue packets from higher level ready to put out.
*/
int
tun_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
struct rtentry *rt)
{
u_int32_t *af;
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
m_freem(m0);
return (EHOSTDOWN);
}
M_PREPEND(m0, sizeof(*af), M_DONTWAIT);
if (m0 == NULL)
return (ENOBUFS);
af = mtod(m0, u_int32_t *);
*af = htonl(dst->sa_family);
return (if_enqueue(ifp, m0));
}
int
tun_enqueue(struct ifnet *ifp, struct mbuf *m0)
{
struct tun_softc *sc = ifp->if_softc;
int error;
error = ifq_enqueue(&ifp->if_snd, m0);
if (error != 0)
return (error);
tun_wakeup(sc);
return (0);
}
void
tun_wakeup(struct tun_softc *sc)
{
if (sc->sc_reading)
wakeup(&sc->sc_if.if_snd);
selwakeup(&sc->sc_rsel);
if (sc->sc_flags & TUN_ASYNC)
pgsigio(&sc->sc_sigio, SIGIO, 0);
}
/*
* the cdevsw interface is now pretty minimal.
*/
int
tunioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
return (tun_dev_ioctl(dev, cmd, data));
}
int
tapioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
return (tun_dev_ioctl(dev, cmd, data));
}
int
tun_dev_ioctl(dev_t dev, u_long cmd, void *data)
{
struct tun_softc *sc;
struct tuninfo *tunp;
int error = 0;
sc = tun_get(dev);
if (sc == NULL)
return (ENXIO);
switch (cmd) {
case TUNSIFINFO:
tunp = (struct tuninfo *)data;
if (tunp->mtu < ETHERMIN || tunp->mtu > TUNMRU) {
error = EINVAL;
break;
}
if (tunp->type != sc->sc_if.if_type) {
error = EINVAL;
break;
}
sc->sc_if.if_mtu = tunp->mtu;
sc->sc_if.if_flags =
(tunp->flags & TUN_IFF_FLAGS) |
(sc->sc_if.if_flags & ~TUN_IFF_FLAGS);
sc->sc_if.if_baudrate = tunp->baudrate;
break;
case TUNGIFINFO:
tunp = (struct tuninfo *)data;
tunp->mtu = sc->sc_if.if_mtu;
tunp->type = sc->sc_if.if_type;
tunp->flags = sc->sc_if.if_flags;
tunp->baudrate = sc->sc_if.if_baudrate;
break;
#ifdef TUN_DEBUG
case TUNSDEBUG:
tundebug = *(int *)data;
break;
case TUNGDEBUG:
*(int *)data = tundebug;
break;
#endif
case TUNSIFMODE:
switch (*(int *)data & (IFF_POINTOPOINT|IFF_BROADCAST)) {
case IFF_POINTOPOINT:
case IFF_BROADCAST:
sc->sc_if.if_flags &= ~TUN_IFF_FLAGS;
sc->sc_if.if_flags |= *(int *)data & TUN_IFF_FLAGS;
break;
default:
error = EINVAL;
break;
}
break;
case FIONBIO:
break;
case FIOASYNC:
if (*(int *)data)
sc->sc_flags |= TUN_ASYNC;
else
sc->sc_flags &= ~TUN_ASYNC;
break;
case FIONREAD:
*(int *)data = ifq_hdatalen(&sc->sc_if.if_snd);
break;
case FIOSETOWN:
case TIOCSPGRP:
error = sigio_setown(&sc->sc_sigio, cmd, data);
break;
case FIOGETOWN:
case TIOCGPGRP:
sigio_getown(&sc->sc_sigio, cmd, data);
break;
case SIOCGIFADDR:
if (!(sc->sc_flags & TUN_LAYER2)) {
error = EINVAL;
break;
}
bcopy(sc->sc_ac.ac_enaddr, data,
sizeof(sc->sc_ac.ac_enaddr));
break;
case SIOCSIFADDR:
if (!(sc->sc_flags & TUN_LAYER2)) {
error = EINVAL;
break;
}
bcopy(data, sc->sc_ac.ac_enaddr,
sizeof(sc->sc_ac.ac_enaddr));
break;
default:
error = ENOTTY;
break;
}
tun_put(sc);
return (error);
}
/*
* The cdevsw read interface - reads a packet at a time, or at
* least as much of a packet as can be read.
*/
int
tunread(dev_t dev, struct uio *uio, int ioflag)
{
return (tun_dev_read(dev, uio, ioflag));
}
int
tapread(dev_t dev, struct uio *uio, int ioflag)
{
return (tun_dev_read(dev, uio, ioflag));
}
int
tun_dev_read(dev_t dev, struct uio *uio, int ioflag)
{
struct tun_softc *sc;
struct ifnet *ifp;
struct mbuf *m, *m0;
int error = 0;
sc = tun_get(dev);
if (sc == NULL)
return (ENXIO);
ifp = &sc->sc_if;
error = ifq_deq_sleep(&ifp->if_snd, &m0, ISSET(ioflag, IO_NDELAY),
(PZERO + 1)|PCATCH, "tunread", &sc->sc_reading, &sc->sc_dev);
if (error != 0)
goto put;
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
#endif
m = m0;
while (uio->uio_resid > 0) {
size_t len = ulmin(uio->uio_resid, m->m_len);
if (len > 0) {
error = uiomove(mtod(m, void *), len, uio);
if (error != 0)
break;
}
m = m->m_next;
if (m == NULL)
break;
}
m_freem(m0);
put:
tun_put(sc);
return (error);
}
/*
* the cdevsw write interface - an atomic write is a packet - or else!
*/
int
tunwrite(dev_t dev, struct uio *uio, int ioflag)
{
return (tun_dev_write(dev, uio, ioflag, 0));
}
int
tapwrite(dev_t dev, struct uio *uio, int ioflag)
{
return (tun_dev_write(dev, uio, ioflag, ETHER_ALIGN));
}
int
tun_dev_write(dev_t dev, struct uio *uio, int ioflag, int align)
{
struct tun_softc *sc;
struct ifnet *ifp;
struct mbuf *m0;
int error = 0;
size_t mlen;
sc = tun_get(dev);
if (sc == NULL)
return (ENXIO);
ifp = &sc->sc_if;
if (uio->uio_resid < ifp->if_hdrlen ||
uio->uio_resid > (ifp->if_hdrlen + ifp->if_hardmtu)) {
error = EMSGSIZE;
goto put;
}
align += max_linkhdr;
mlen = align + uio->uio_resid;
m0 = m_gethdr(M_DONTWAIT, MT_DATA);
if (m0 == NULL) {
error = ENOMEM;
goto put;
}
if (mlen > MHLEN) {
m_clget(m0, M_DONTWAIT, mlen);
if (!ISSET(m0->m_flags, M_EXT)) {
error = ENOMEM;
goto drop;
}
}
m_align(m0, mlen);
m0->m_pkthdr.len = m0->m_len = mlen;
m_adj(m0, align);
error = uiomove(mtod(m0, void *), m0->m_len, uio);
if (error != 0)
goto drop;
NET_LOCK();
if_vinput(ifp, m0);
NET_UNLOCK();
tun_put(sc);
return (0);
drop:
m_freem(m0);
put:
tun_put(sc);
return (error);
}
void
tun_input(struct ifnet *ifp, struct mbuf *m0)
{
uint32_t af;
KASSERT(m0->m_len >= sizeof(af));
af = *mtod(m0, uint32_t *);
/* strip the tunnel header */
m_adj(m0, sizeof(af));
switch (ntohl(af)) {
case AF_INET:
ipv4_input(ifp, m0);
break;
#ifdef INET6
case AF_INET6:
ipv6_input(ifp, m0);
break;
#endif
#ifdef MPLS
case AF_MPLS:
mpls_input(ifp, m0);
break;
#endif
default:
m_freem(m0);
break;
}
}
int
tunkqfilter(dev_t dev, struct knote *kn)
{
return (tun_dev_kqfilter(dev, kn));
}
int
tapkqfilter(dev_t dev, struct knote *kn)
{
return (tun_dev_kqfilter(dev, kn));
}
int
tun_dev_kqfilter(dev_t dev, struct knote *kn)
{
struct tun_softc *sc;
struct ifnet *ifp;
struct klist *klist;
int error = 0;
int s;
sc = tun_get(dev);
if (sc == NULL)
return (ENXIO);
ifp = &sc->sc_if;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sc->sc_rsel.si_note;
kn->kn_fop = &tunread_filtops;
break;
case EVFILT_WRITE:
klist = &sc->sc_wsel.si_note;
kn->kn_fop = &tunwrite_filtops;
break;
default:
error = EINVAL;
goto put;
}
kn->kn_hook = (caddr_t)sc; /* XXX give the sc_ref to the hook? */
s = splhigh();
klist_insert_locked(klist, kn);
splx(s);
put:
tun_put(sc);
return (error);
}
void
filt_tunrdetach(struct knote *kn)
{
int s;
struct tun_softc *sc = kn->kn_hook;
s = splhigh();
klist_remove_locked(&sc->sc_rsel.si_note, kn);
splx(s);
}
int
filt_tunread(struct knote *kn, long hint)
{
struct tun_softc *sc = kn->kn_hook;
struct ifnet *ifp = &sc->sc_if;
kn->kn_data = ifq_hdatalen(&ifp->if_snd);
return (kn->kn_data > 0);
}
void
filt_tunwdetach(struct knote *kn)
{
int s;
struct tun_softc *sc = kn->kn_hook;
s = splhigh();
klist_remove_locked(&sc->sc_wsel.si_note, kn);
splx(s);
}
int
filt_tunwrite(struct knote *kn, long hint)
{
struct tun_softc *sc = kn->kn_hook;
struct ifnet *ifp = &sc->sc_if;
kn->kn_data = ifp->if_hdrlen + ifp->if_hardmtu;
return (1);
}
void
tun_start(struct ifnet *ifp)
{
struct tun_softc *sc = ifp->if_softc;
splassert(IPL_NET);
if (ifq_len(&ifp->if_snd))
tun_wakeup(sc);
}
void
tun_link_state(struct ifnet *ifp, int link_state)
{
if (ifp->if_link_state != link_state) {
ifp->if_link_state = link_state;
if_link_state_change(ifp);
}
}
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
/* $OpenBSD: st.c,v 1.189 2022/02/27 02:27:55 krw Exp $ */
/* $NetBSD: st.c,v 1.71 1997/02/21 23:03:49 thorpej Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Originally written by Julian Elischer (julian@tfs.com)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
*
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
*
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
* major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993
*/
/*
* To do:
* work out some better way of guessing what a good timeout is going
* to be depending on whether we expect to retension or not.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/timeout.h>
#include <sys/fcntl.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/pool.h>
#include <sys/buf.h>
#include <sys/mtio.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsi_tape.h>
#include <scsi/scsiconf.h>
/* Defines for device specific stuff */
#define DEF_FIXED_BSIZE 512
#define STMODE(z) ( minor(z) & 0x03)
#define STUNIT(z) ((minor(z) >> 4) )
#define STMINOR(unit, mode) (((unit) << 4) + (mode))
#define MAXSTMODES 16 /* Old max retained so minor's don't change. */
#define ST_IO_TIME (3 * 60 * 1000) /* 3 minutes */
#define ST_CTL_TIME (30 * 1000) /* 30 seconds */
#define ST_SPC_TIME (4 * 60 * 60 * 1000) /* 4 hours */
/*
* Maximum density code allowed in SCSI spec (SSC2R08f, Section 8.3).
*/
#define SCSI_MAX_DENSITY_CODE 0xff
/*
* Define various devices that we know mis-behave in some way,
* and note how they are bad, so we can correct for them
*/
struct mode {
int blksize;
u_int8_t density;
};
struct quirkdata {
u_int quirks;
#define ST_Q_SENSE_HELP 0x0001 /* must do READ for good MODE SENSE */
#define ST_Q_IGNORE_LOADS 0x0002
#define ST_Q_UNIMODAL 0x0004 /* unimode drive rejects mode select */
struct mode mode;
};
struct st_quirk_inquiry_pattern {
struct scsi_inquiry_pattern pattern;
struct quirkdata quirkdata;
};
const struct st_quirk_inquiry_pattern st_quirk_patterns[] = {
{{T_SEQUENTIAL, T_REMOV,
" ", " ", " "}, {0,
{512, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"TANDBERG", " TDC 3800 ", ""}, {0,
{512, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"ARCHIVE ", "VIPER 2525 25462", ""}, {ST_Q_SENSE_HELP,
{0, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"SANKYO ", "CP525 ", ""}, {0,
{512, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"ANRITSU ", "DMT780 ", ""}, {0,
{512, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"ARCHIVE ", "VIPER 150 21531", ""}, {ST_Q_SENSE_HELP,
{0, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"WANGTEK ", "5099ES SCSI", ""}, {0,
{512, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"WANGTEK ", "5150ES SCSI", ""}, {0,
{512, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"HP ", "T4000s ", ""}, {ST_Q_UNIMODAL,
{0, QIC_3095}}},
{{T_SEQUENTIAL, T_REMOV,
"WANGTEK ", "5150ES SCSI FA15", "01 A"}, {ST_Q_IGNORE_LOADS,
{0, 0}}},
{{T_SEQUENTIAL, T_REMOV,
"TEAC ", "MT-2ST/N50 ", ""}, {ST_Q_IGNORE_LOADS,
{0, 0}}},
};
#define NOEJECT 0
#define EJECT 1
#define NOREWIND 0
#define DOREWIND 1
struct st_softc {
struct device sc_dev;
int flags;
#define ST_INFO_VALID 0x00000001
#define ST_WRITTEN 0x00000004
#define ST_FIXEDBLOCKS 0x00000008
#define ST_AT_FILEMARK 0x00000010
#define ST_EIO_PENDING 0x00000020
#define ST_EOM_PENDING 0x00000040
#define ST_EOD_DETECTED 0x00000080
#define ST_FM_WRITTEN 0x00000100
#define ST_BLANK_READ 0x00000200
#define ST_2FM_AT_EOD 0x00000400
#define ST_MOUNTED 0x00000800
#define ST_DONTBUFFER 0x00001000
#define ST_DYING 0x00004000
#define ST_BOD_DETECTED 0x00008000
#define ST_MODE_DENSITY 0x00010000
#define ST_MODE_BLKSIZE 0x00040000
u_int quirks; /* quirks for the open mode */
int blksize; /* blksize we are using */
u_int8_t density; /* present density */
short mt_resid; /* last (short) resid */
short mt_erreg; /* last error (sense key) seen */
struct scsi_link *sc_link; /* our link to the adapter etc. */
int blkmin; /* min blk size */
int blkmax; /* max blk size */
u_int32_t media_blksize; /* 0 if not ST_FIXEDBLOCKS */
u_int32_t media_density; /* this is what it said when asked */
int media_fileno; /* relative to BOT. -1 means unknown. */
int media_blkno; /* relative to BOF. -1 means unknown. */
int media_eom; /* relative to BOT. -1 means unknown. */
struct mode mode;
struct bufq sc_bufq;
struct scsi_xshandler sc_xsh;
};
int stmatch(struct device *, void *, void *);
void stattach(struct device *, struct device *, void *);
int stactivate(struct device *, int);
int stdetach(struct device *, int);
void stminphys(struct buf *);
void ststart(struct scsi_xfer *);
int st_mount_tape(struct st_softc *, int);
void st_unmount(struct st_softc *, int, int);
int st_decide_mode(struct st_softc *, int);
void st_buf_done(struct scsi_xfer *);
int st_read(struct st_softc *, char *, int, int);
int st_read_block_limits(struct st_softc *, int);
int st_mode_sense(struct st_softc *, int);
int st_mode_select(struct st_softc *, int);
int st_space(struct st_softc *, int, u_int, int);
int st_write_filemarks(struct st_softc *, int, int);
int st_check_eod(struct st_softc *, int, int *, int);
int st_load(struct st_softc *, u_int, int);
int st_rewind(struct st_softc *, u_int, int);
int st_interpret_sense(struct scsi_xfer *);
int st_touch_tape(struct st_softc *);
int st_erase(struct st_softc *, int, int);
const struct cfattach st_ca = {
sizeof(struct st_softc), stmatch, stattach,
stdetach, stactivate
};
struct cfdriver st_cd = {
NULL, "st", DV_TAPE
};
#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_EOM_PENDING | \
ST_BLANK_READ)
#define stlookup(unit) (struct st_softc *)device_lookup(&st_cd, (unit))
const struct scsi_inquiry_pattern st_patterns[] = {
{T_SEQUENTIAL, T_REMOV,
"", "", ""},
};
int
stmatch(struct device *parent, void *match, void *aux)
{
struct scsi_attach_args *sa = aux;
struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
int priority;
(void)scsi_inqmatch(inq, st_patterns, nitems(st_patterns),
sizeof(st_patterns[0]), &priority);
return priority;
}
/*
* The routine called by the low level scsi routine when it discovers
* a device suitable for this driver.
*/
void
stattach(struct device *parent, struct device *self, void *aux)
{
const struct st_quirk_inquiry_pattern *finger;
struct st_softc *st = (void *)self;
struct scsi_attach_args *sa = aux;
struct scsi_link *link = sa->sa_sc_link;
int priority;
SC_DEBUG(link, SDEV_DB2, ("stattach:\n"));
/*
* Store information needed to contact our base driver
*/
st->sc_link = link;
link->interpret_sense = st_interpret_sense;
link->device_softc = st;
/* Get any quirks and mode information. */
finger = (const struct st_quirk_inquiry_pattern *)scsi_inqmatch(
&link->inqdata,
st_quirk_patterns,
nitems(st_quirk_patterns),
sizeof(st_quirk_patterns[0]), &priority);
if (finger != NULL) {
st->quirks = finger->quirkdata.quirks;
st->mode = finger->quirkdata.mode;
CLR(st->flags, ST_MODE_BLKSIZE | ST_MODE_DENSITY);
if (st->mode.blksize != 0)
SET(st->flags, ST_MODE_BLKSIZE);
if (st->mode.density != 0)
SET(st->flags, ST_MODE_DENSITY);
}
printf("\n");
scsi_xsh_set(&st->sc_xsh, link, ststart);
/* Set up the buf queue for this device. */
bufq_init(&st->sc_bufq, BUFQ_FIFO);
/* Start up with media position unknown. */
st->media_fileno = -1;
st->media_blkno = -1;
st->media_eom = -1;
/*
* Reset the media loaded flag, sometimes the data
* acquired at boot time is not quite accurate. This
* will be checked again at the first open.
*/
CLR(link->flags, SDEV_MEDIA_LOADED);
}
int
stactivate(struct device *self, int act)
{
struct st_softc *st = (struct st_softc *)self;
switch (act) {
case DVACT_DEACTIVATE:
SET(st->flags, ST_DYING);
scsi_xsh_del(&st->sc_xsh);
break;
}
return 0;
}
int
stdetach(struct device *self, int flags)
{
struct st_softc *st = (struct st_softc *)self;
int cmaj, mn;
bufq_drain(&st->sc_bufq);
/* Locate the lowest minor number to be detached. */
mn = STMINOR(self->dv_unit, 0);
for (cmaj = 0; cmaj < nchrdev; cmaj++)
if (cdevsw[cmaj].d_open == stopen)
vdevgone(cmaj, mn, mn + MAXSTMODES - 1, VCHR);
bufq_destroy(&st->sc_bufq);
return 0;
}
/*
* open the device.
*/
int
stopen(dev_t dev, int flags, int fmt, struct proc *p)
{
struct scsi_link *link;
struct st_softc *st;
int error = 0;
st = stlookup(STUNIT(dev));
if (st == NULL)
return ENXIO;
if (ISSET(st->flags, ST_DYING)) {
device_unref(&st->sc_dev);
return ENXIO;
}
link = st->sc_link;
if (ISSET(flags, FWRITE) && ISSET(link->flags, SDEV_READONLY)) {
error = EACCES;
goto done;
}
SC_DEBUG(link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n", dev,
STUNIT(dev), st_cd.cd_ndevs));
/*
* Tape is an exclusive media. Only one open at a time.
*/
if (ISSET(link->flags, SDEV_OPEN)) {
SC_DEBUG(link, SDEV_DB4, ("already open\n"));
error = EBUSY;
goto done;
}
/* Use st_interpret_sense() now. */
SET(link->flags, SDEV_OPEN);
/*
* Check the unit status. This clears any outstanding errors and
* will ensure that media is present.
*/
error = scsi_test_unit_ready(link, TEST_READY_RETRIES,
SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE |
SCSI_IGNORE_ILLEGAL_REQUEST);
/*
* Terminate any existing mount session if there is no media.
*/
if (!ISSET(link->flags, SDEV_MEDIA_LOADED))
st_unmount(st, NOEJECT, DOREWIND);
if (error != 0) {
CLR(link->flags, SDEV_OPEN);
goto done;
}
error = st_mount_tape(st, flags);
if (error != 0) {
CLR(link->flags, SDEV_OPEN);
goto done;
}
/*
* Make sure that a tape opened in write-only mode will have
* file marks written on it when closed, even if not written to.
* This is for SUN compatibility
*/
if ((flags & O_ACCMODE) == FWRITE)
SET(st->flags, ST_WRITTEN);
done:
SC_DEBUG(link, SDEV_DB2, ("open complete\n"));
device_unref(&st->sc_dev);
return error;
}
/*
* close the device.. only called if we are the LAST
* occurrence of an open device
*/
int
stclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct scsi_link *link;
struct st_softc *st;
int error = 0;
st = stlookup(STUNIT(dev));
if (st == NULL)
return ENXIO;
if (ISSET(st->flags, ST_DYING)) {
error = ENXIO;
goto done;
}
link = st->sc_link;
SC_DEBUG(link, SDEV_DB1, ("closing\n"));
if (ISSET(st->flags, ST_WRITTEN) && !ISSET(st->flags, ST_FM_WRITTEN))
st_write_filemarks(st, 1, 0);
switch (STMODE(dev)) {
case 0: /* /dev/rstN */
st_unmount(st, NOEJECT, DOREWIND);
break;
case 1: /* /dev/nrstN */
st_unmount(st, NOEJECT, NOREWIND);
break;
case 2: /* /dev/erstN */
st_unmount(st, EJECT, DOREWIND);
break;
case 3: /* /dev/enrstN */
st_unmount(st, EJECT, NOREWIND);
break;
}
CLR(link->flags, SDEV_OPEN);
scsi_xsh_del(&st->sc_xsh);
done:
device_unref(&st->sc_dev);
return error;
}
/*
* Start a new mount session if needed.
*/
int
st_mount_tape(struct st_softc *st, int flags)
{
struct scsi_link *link = st->sc_link;
int error = 0;
if (ISSET(st->flags, ST_MOUNTED))
return 0;
SC_DEBUG(link, SDEV_DB1, ("mounting\n"));
/*
* Assume the media is new and give it a chance to
* to do a 'load' instruction.
*/
if ((error = st_load(st, LD_LOAD, 0)) != 0)
goto done;
/*
* Throw another dummy instruction to catch
* 'Unit attention' errors. Some drives appear to give
* these after doing a Load instruction.
* (notably some DAT drives)
*/
/* XXX */
scsi_test_unit_ready(link, TEST_READY_RETRIES, SCSI_SILENT);
/*
* Some devices can't tell you much until they have been
* asked to look at the media. This quirk does this.
*/
if (ISSET(st->quirks, ST_Q_SENSE_HELP))
if ((error = st_touch_tape(st)) != 0)
return error;
/*
* Load the physical device parameters
* loads: blkmin, blkmax
*/
if (!ISSET(link->flags, SDEV_ATAPI) &&
(error = st_read_block_limits(st, 0)) != 0)
goto done;
/*
* Load the media dependent parameters
* includes: media_blksize,media_density
* As we have a tape in, it should be reflected here.
* If not you may need the "quirk" above.
*/
if ((error = st_mode_sense(st, 0)) != 0)
goto done;
/*
* If we have gained a permanent density from somewhere,
* then use it in preference to the one supplied by
* default by the driver.
*/
if (ISSET(st->flags, ST_MODE_DENSITY))
st->density = st->mode.density;
else
st->density = st->media_density;
/*
* If we have gained a permanent blocksize
* then use it in preference to the one supplied by
* default by the driver.
*/
CLR(st->flags, ST_FIXEDBLOCKS);
if (ISSET(st->flags, ST_MODE_BLKSIZE)) {
st->blksize = st->mode.blksize;
if (st->blksize)
SET(st->flags, ST_FIXEDBLOCKS);
} else {
if ((error = st_decide_mode(st, 0)) != 0)
goto done;
}
if ((error = st_mode_select(st, 0)) != 0) {
printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname);
goto done;
}
scsi_prevent(link, PR_PREVENT,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
SET(st->flags, ST_MOUNTED);
SET(link->flags, SDEV_MEDIA_LOADED); /* move earlier? */
st->media_fileno = 0;
st->media_blkno = 0;
st->media_eom = -1;
done:
return error;
}
/*
* End the present mount session.
* Rewind, and optionally eject the tape.
* Reset various flags to indicate that all new
* operations require another mount operation
*/
void
st_unmount(struct st_softc *st, int eject, int rewind)
{
struct scsi_link *link = st->sc_link;
int nmarks;
if (eject == NOEJECT && rewind == NOREWIND) {
if (ISSET(link->flags, SDEV_MEDIA_LOADED))
return;
}
st->media_fileno = -1;
st->media_blkno = -1;
if (!ISSET(st->flags, ST_MOUNTED))
return;
SC_DEBUG(link, SDEV_DB1, ("unmounting\n"));
st_check_eod(st, 0, &nmarks, SCSI_IGNORE_NOT_READY);
if (rewind == DOREWIND)
st_rewind(st, 0, SCSI_IGNORE_NOT_READY);
scsi_prevent(link, PR_ALLOW,
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
if (eject == EJECT)
st_load(st, LD_UNLOAD, SCSI_IGNORE_NOT_READY);
CLR(st->flags, ST_MOUNTED);
CLR(link->flags, SDEV_MEDIA_LOADED);
}
/*
* Given all we know about the device, media, mode, 'quirks' and
* initial operation, make a decision as to how we should be set
* to run (regarding blocking and EOD marks)
*/
int
st_decide_mode(struct st_softc *st, int first_read)
{
struct scsi_link *link = st->sc_link;
SC_DEBUG(link, SDEV_DB2, ("starting block mode decision\n"));
/* ATAPI tapes are always fixed blocksize. */
if (ISSET(link->flags, SDEV_ATAPI)) {
SET(st->flags, ST_FIXEDBLOCKS);
if (st->media_blksize > 0)
st->blksize = st->media_blksize;
else
st->blksize = DEF_FIXED_BSIZE;
goto done;
}
/*
* If the drive can only handle fixed-length blocks and only at
* one size, perhaps we should just do that.
*/
if (st->blkmin && (st->blkmin == st->blkmax)) {
SET(st->flags, ST_FIXEDBLOCKS);
st->blksize = st->blkmin;
SC_DEBUG(link, SDEV_DB3,
("blkmin == blkmax of %d\n", st->blkmin));
goto done;
}
/*
* If the tape density mandates (or even suggests) use of fixed
* or variable-length blocks, comply.
*/
switch (st->density) {
case HALFINCH_800:
case HALFINCH_1600:
case HALFINCH_6250:
case DDS:
CLR(st->flags, ST_FIXEDBLOCKS);
st->blksize = 0;
SC_DEBUG(link, SDEV_DB3, ("density specified variable\n"));
goto done;
case QIC_11:
case QIC_24:
case QIC_120:
case QIC_150:
case QIC_525:
case QIC_1320:
SET(st->flags, ST_FIXEDBLOCKS);
if (st->media_blksize > 0)
st->blksize = st->media_blksize;
else
st->blksize = DEF_FIXED_BSIZE;
SC_DEBUG(link, SDEV_DB3, ("density specified fixed\n"));
goto done;
}
/*
* If we're about to read the tape, perhaps we should choose
* fixed or variable-length blocks and block size according to
* what the drive found on the tape.
*/
if (first_read) {
if (st->media_blksize > 0)
SET(st->flags, ST_FIXEDBLOCKS);
else
CLR(st->flags, ST_FIXEDBLOCKS);
st->blksize = st->media_blksize;
SC_DEBUG(link, SDEV_DB3,
("Used media_blksize of %d\n", st->media_blksize));
goto done;
}
/*
* We're getting no hints from any direction. Choose variable-
* length blocks arbitrarily.
*/
CLR(st->flags, ST_FIXEDBLOCKS);
st->blksize = 0;
SC_DEBUG(link, SDEV_DB3,
("Give up and default to variable mode\n"));
done:
/*
* Decide whether or not to write two file marks to signify end-
* of-data. Make the decision as a function of density. If
* the decision is not to use a second file mark, the SCSI BLANK
* CHECK condition code will be recognized as end-of-data when
* first read.
* (I think this should be a by-product of fixed/variable..julian)
*/
switch (st->density) {
/* case 8 mm: What is the SCSI density code for 8 mm, anyway? */
case QIC_11:
case QIC_24:
case QIC_120:
case QIC_150:
case QIC_525:
case QIC_1320:
CLR(st->flags, ST_2FM_AT_EOD);
break;
default:
SET(st->flags, ST_2FM_AT_EOD);
}
return 0;
}
/*
* Actually translate the requested transfer into
* one the physical driver can understand
* The transfer is described by a buf and will include
* only one physical transfer.
*/
void
ststrategy(struct buf *bp)
{
struct scsi_link *link;
struct st_softc *st;
int s;
st = stlookup(STUNIT(bp->b_dev));
if (st == NULL) {
bp->b_error = ENXIO;
goto bad;
}
if (ISSET(st->flags, ST_DYING)) {
bp->b_error = ENXIO;
goto bad;
}
link = st->sc_link;
SC_DEBUG(link, SDEV_DB2, ("ststrategy: %ld bytes @ blk %lld\n",
bp->b_bcount, (long long)bp->b_blkno));
/*
* If it's a null transfer, return immediately.
*/
if (bp->b_bcount == 0)
goto done;
/*
* Odd sized request on fixed drives are verboten
*/
if (ISSET(st->flags, ST_FIXEDBLOCKS)) {
if (bp->b_bcount % st->blksize) {
printf("%s: bad request, must be multiple of %d\n",
st->sc_dev.dv_xname, st->blksize);
bp->b_error = EIO;
goto bad;
}
}
/*
* as are out-of-range requests on variable drives.
*/
else if (bp->b_bcount < st->blkmin ||
(st->blkmax && bp->b_bcount > st->blkmax)) {
printf("%s: bad request, must be between %d and %d\n",
st->sc_dev.dv_xname, st->blkmin, st->blkmax);
bp->b_error = EIO;
goto bad;
}
/*
* Place it in the queue of activities for this tape
* at the end (a bit silly because we only have on user..
* (but it could fork()))
*/
bufq_queue(&st->sc_bufq, bp);
/*
* Tell the device to get going on the transfer if it's
* not doing anything, otherwise just wait for completion
* (All a bit silly if we're only allowing 1 open but..)
*/
scsi_xsh_add(&st->sc_xsh);
device_unref(&st->sc_dev);
return;
bad:
SET(bp->b_flags, B_ERROR);
done:
/* Set b_resid to indicate no xfer was done. */
bp->b_resid = bp->b_bcount;
s = splbio();
biodone(bp);
splx(s);
if (st)
device_unref(&st->sc_dev);
}
void
ststart(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct st_softc *st = link->device_softc;
struct buf *bp;
struct scsi_rw_tape *cmd;
int s;
SC_DEBUG(link, SDEV_DB2, ("ststart\n"));
if (ISSET(st->flags, ST_DYING)) {
scsi_xs_put(xs);
return;
}
/*
* if the device has been unmounted by the user
* then throw away all requests until done
*/
if (!ISSET(st->flags, ST_MOUNTED) ||
!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
/* make sure that one implies the other.. */
CLR(link->flags, SDEV_MEDIA_LOADED);
bufq_drain(&st->sc_bufq);
scsi_xs_put(xs);
return;
}
for (;;) {
bp = bufq_dequeue(&st->sc_bufq);
if (bp == NULL) {
scsi_xs_put(xs);
return;
}
/*
* Only FIXEDBLOCK devices have pending I/O or space
* operations.
*/
if (ISSET(st->flags, ST_FIXEDBLOCKS)) {
/*
* If we are at a filemark but have not reported it yet
* then we should report it now
*/
if (ISSET(st->flags, ST_AT_FILEMARK)) {
if ((bp->b_flags & B_READ) == B_WRITE) {
/*
* Handling of ST_AT_FILEMARK in
* st_space will fill in the right file
* mark count.
* Back up over filemark
*/
if (st_space(st, 0, SP_FILEMARKS, 0)) {
SET(bp->b_flags, B_ERROR);
bp->b_resid = bp->b_bcount;
bp->b_error = EIO;
s = splbio();
biodone(bp);
splx(s);
continue;
}
} else {
bp->b_resid = bp->b_bcount;
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
CLR(st->flags, ST_AT_FILEMARK);
s = splbio();
biodone(bp);
splx(s);
continue; /* seek more work */
}
}
}
/*
* If we are at EIO or EOM but have not reported it
* yet then we should report it now.
*/
if (ISSET(st->flags, ST_EOM_PENDING | ST_EIO_PENDING)) {
bp->b_resid = bp->b_bcount;
if (ISSET(st->flags, ST_EIO_PENDING)) {
bp->b_error = EIO;
SET(bp->b_flags, B_ERROR);
}
CLR(st->flags, ST_EOM_PENDING | ST_EIO_PENDING);
s = splbio();
biodone(bp);
splx(s);
continue; /* seek more work */
}
break;
}
/*
* Fill out the scsi command
*/
cmd = (struct scsi_rw_tape *)&xs->cmd;
bzero(cmd, sizeof(*cmd));
if ((bp->b_flags & B_READ) == B_WRITE) {
cmd->opcode = WRITE;
CLR(st->flags, ST_FM_WRITTEN);
SET(st->flags, ST_WRITTEN);
SET(xs->flags, SCSI_DATA_OUT);
} else {
cmd->opcode = READ;
SET(xs->flags, SCSI_DATA_IN);
}
/*
* Handle "fixed-block-mode" tape drives by using the
* block count instead of the length.
*/
if (ISSET(st->flags, ST_FIXEDBLOCKS)) {
SET(cmd->byte2, SRW_FIXED);
_lto3b(bp->b_bcount / st->blksize, cmd->len);
} else
_lto3b(bp->b_bcount, cmd->len);
if (st->media_blkno != -1) {
/* Update block count now, errors will set it to -1. */
if (ISSET(st->flags, ST_FIXEDBLOCKS))
st->media_blkno += _3btol(cmd->len);
else if (_3btol(cmd->len) != 0)
st->media_blkno++;
}
xs->cmdlen = sizeof(*cmd);
xs->timeout = ST_IO_TIME;
xs->data = bp->b_data;
xs->datalen = bp->b_bcount;
xs->done = st_buf_done;
xs->cookie = bp;
xs->bp = bp;
/*
* go ask the adapter to do all this for us
*/
scsi_xs_exec(xs);
/*
* should we try do more work now?
*/
if (bufq_peek(&st->sc_bufq))
scsi_xsh_add(&st->sc_xsh);
}
void
st_buf_done(struct scsi_xfer *xs)
{
struct buf *bp = xs->cookie;
int error, s;
switch (xs->error) {
case XS_NOERROR:
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
bp->b_resid = xs->resid;
break;
case XS_SENSE:
case XS_SHORTSENSE:
SC_DEBUG_SENSE(xs);
error = st_interpret_sense(xs);
if (error == 0) {
bp->b_error = 0;
CLR(bp->b_flags, B_ERROR);
bp->b_resid = xs->resid;
break;
}
if (error != ERESTART)
xs->retries = 0;
goto retry;
case XS_BUSY:
if (xs->retries) {
if (scsi_delay(xs, 1) != ERESTART)
xs->retries = 0;
}
goto retry;
case XS_TIMEOUT:
retry:
if (xs->retries--) {
scsi_xs_exec(xs);
return;
}
/* FALLTHROUGH */
default:
bp->b_error = EIO;
SET(bp->b_flags, B_ERROR);
bp->b_resid = bp->b_bcount;
break;
}
s = splbio();
biodone(bp);
splx(s);
scsi_xs_put(xs);
}
void
stminphys(struct buf *bp)
{
struct scsi_link *link;
struct st_softc *sc;
sc = stlookup(STUNIT(bp->b_dev));
if (sc == NULL)
return;
link = sc->sc_link;
if (link->bus->sb_adapter->dev_minphys != NULL)
(*link->bus->sb_adapter->dev_minphys)(bp, link);
else
minphys(bp);
device_unref(&sc->sc_dev);
}
int
stread(dev_t dev, struct uio *uio, int iomode)
{
return physio(ststrategy, dev, B_READ, stminphys, uio);
}
int
stwrite(dev_t dev, struct uio *uio, int iomode)
{
return physio(ststrategy, dev, B_WRITE, stminphys, uio);
}
/*
* Perform special action on behalf of the user;
* knows about the internals of this device
*/
int
stioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
{
int error = 0;
int nmarks;
int flags = 0;
struct st_softc *st;
int hold_blksize;
u_int8_t hold_density;
struct mtop *mt = (struct mtop *) arg;
int number;
/*
* Find the device that the user is talking about
*/
st = stlookup(STUNIT(dev));
if (st == NULL)
return ENXIO;
if (ISSET(st->flags, ST_DYING)) {
error = ENXIO;
goto done;
}
hold_blksize = st->blksize;
hold_density = st->density;
switch (cmd) {
case MTIOCGET: {
struct mtget *g = (struct mtget *) arg;
/*
* (to get the current state of READONLY)
*/
error = st_mode_sense(st, SCSI_SILENT);
if (error != 0)
break;
SC_DEBUG(st->sc_link, SDEV_DB1, ("[ioctl: get status]\n"));
bzero(g, sizeof(struct mtget));
g->mt_type = 0x7; /* Ultrix compat *//*? */
g->mt_blksiz = st->blksize;
g->mt_density = st->density;
g->mt_mblksiz = st->mode.blksize;
g->mt_mdensity = st->mode.density;
if (ISSET(st->sc_link->flags, SDEV_READONLY))
SET(g->mt_dsreg, MT_DS_RDONLY);
if (ISSET(st->flags, ST_MOUNTED))
SET(g->mt_dsreg, MT_DS_MOUNTED);
g->mt_resid = st->mt_resid;
g->mt_erreg = st->mt_erreg;
g->mt_fileno = st->media_fileno;
g->mt_blkno = st->media_blkno;
/*
* clear latched errors.
*/
st->mt_resid = 0;
st->mt_erreg = 0;
break;
}
case MTIOCTOP: {
SC_DEBUG(st->sc_link, SDEV_DB1,
("[ioctl: op=0x%x count=0x%x]\n", mt->mt_op, mt->mt_count));
number = mt->mt_count;
switch (mt->mt_op) {
case MTWEOF: /* write an end-of-file record */
error = st_write_filemarks(st, number, flags);
break;
case MTBSF: /* backward space file */
number = -number;
case MTFSF: /* forward space file */
error = st_check_eod(st, 0, &nmarks, flags);
if (error == 0)
error = st_space(st, number - nmarks,
SP_FILEMARKS, flags);
break;
case MTBSR: /* backward space record */
number = -number;
case MTFSR: /* forward space record */
error = st_check_eod(st, 1, &nmarks, flags);
if (error == 0)
error = st_space(st, number, SP_BLKS, flags);
break;
case MTREW: /* rewind */
error = st_rewind(st, 0, flags);
break;
case MTOFFL: /* rewind and put the drive offline */
st_unmount(st, EJECT, DOREWIND);
break;
case MTNOP: /* no operation, sets status only */
break;
case MTRETEN: /* retension the tape */
error = st_load(st, LD_RETENSION, flags);
if (error == 0)
error = st_load(st, LD_LOAD, flags);
break;
case MTEOM: /* forward space to end of media */
error = st_check_eod(st, 0, &nmarks, flags);
if (error == 0)
error = st_space(st, 1, SP_EOM, flags);
break;
case MTCACHE: /* enable controller cache */
CLR(st->flags, ST_DONTBUFFER);
goto try_new_value;
case MTNOCACHE: /* disable controller cache */
SET(st->flags, ST_DONTBUFFER);
goto try_new_value;
case MTERASE: /* erase volume */
error = st_erase(st, number, flags);
break;
case MTSETBSIZ: /* Set block size for device and mode. */
if (number == 0) {
CLR(st->flags, ST_FIXEDBLOCKS);
} else {
if ((st->blkmin || st->blkmax) &&
(number < st->blkmin ||
number > st->blkmax)) {
error = EINVAL;
break;
}
SET(st->flags, ST_FIXEDBLOCKS);
}
st->blksize = number;
goto try_new_value;
case MTSETDNSTY: /* Set density for device and mode. */
if (number < 0 || number > SCSI_MAX_DENSITY_CODE) {
error = EINVAL;
break;
}
st->density = number;
goto try_new_value;
default:
error = EINVAL;
}
break;
}
case MTIOCIEOT:
case MTIOCEEOT:
break;
#if 0
case MTIOCRDSPOS:
error = st_rdpos(st, 0, (u_int32_t *) arg);
break;
case MTIOCRDHPOS:
error = st_rdpos(st, 1, (u_int32_t *) arg);
break;
case MTIOCSLOCATE:
error = st_setpos(st, 0, (u_int32_t *) arg);
break;
case MTIOCHLOCATE:
error = st_setpos(st, 1, (u_int32_t *) arg);
break;
#endif /* 0 */
default:
error = scsi_do_ioctl(st->sc_link, cmd, arg, flag);
break;
}
goto done;
try_new_value:
/*
* Check that the mode being asked for is aggreeable to the
* drive. If not, put it back the way it was.
*/
if ((error = st_mode_select(st, 0)) != 0) {/* put it back as it was */
printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname);
st->density = hold_density;
st->blksize = hold_blksize;
if (st->blksize)
SET(st->flags, ST_FIXEDBLOCKS);
else
CLR(st->flags, ST_FIXEDBLOCKS);
goto done;
}
/*
* As the drive liked it, if we are setting a new default,
* set it into the structures as such.
*/
switch (mt->mt_op) {
case MTSETBSIZ:
st->mode.blksize = st->blksize;
SET(st->flags, ST_MODE_BLKSIZE);
break;
case MTSETDNSTY:
st->mode.density = st->density;
SET(st->flags, ST_MODE_DENSITY);
break;
}
done:
device_unref(&st->sc_dev);
return error;
}
/*
* Do a synchronous read.
*/
int
st_read(struct st_softc *st, char *buf, int size, int flags)
{
struct scsi_rw_tape *cmd;
struct scsi_xfer *xs;
int error;
if (size == 0)
return 0;
xs = scsi_xs_get(st->sc_link, flags | SCSI_DATA_IN);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->data = buf;
xs->datalen = size;
xs->retries = 0;
xs->timeout = ST_IO_TIME;
cmd = (struct scsi_rw_tape *)&xs->cmd;
cmd->opcode = READ;
if (ISSET(st->flags, ST_FIXEDBLOCKS)) {
SET(cmd->byte2, SRW_FIXED);
_lto3b(size / (st->blksize ? st->blksize : DEF_FIXED_BSIZE),
cmd->len);
} else
_lto3b(size, cmd->len);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Ask the drive what its min and max blk sizes are.
*/
int
st_read_block_limits(struct st_softc *st, int flags)
{
struct scsi_block_limits_data *block_limits = NULL;
struct scsi_block_limits *cmd;
struct scsi_link *link = st->sc_link;
struct scsi_xfer *xs;
int error = 0;
if (ISSET(link->flags, SDEV_MEDIA_LOADED))
return 0;
block_limits = dma_alloc(sizeof(*block_limits), PR_NOWAIT);
if (block_limits == NULL)
return ENOMEM;
xs = scsi_xs_get(link, flags | SCSI_DATA_IN);
if (xs == NULL) {
error = ENOMEM;
goto done;
}
xs->cmdlen = sizeof(*cmd);
xs->data = (void *)block_limits;
xs->datalen = sizeof(*block_limits);
xs->timeout = ST_CTL_TIME;
cmd = (struct scsi_block_limits *)&xs->cmd;
cmd->opcode = READ_BLOCK_LIMITS;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
st->blkmin = _2btol(block_limits->min_length);
st->blkmax = _3btol(block_limits->max_length);
SC_DEBUG(link, SDEV_DB3,
("(%d <= blksize <= %d)\n", st->blkmin, st->blkmax));
}
done:
if (block_limits)
dma_free(block_limits, sizeof(*block_limits));
return error;
}
/*
* Get the scsi driver to send a full inquiry to the
* device and use the results to fill out the global
* parameter structure.
*
* called from:
* attach
* open
* ioctl (to reset original blksize)
*/
int
st_mode_sense(struct st_softc *st, int flags)
{
union scsi_mode_sense_buf *data = NULL;
struct scsi_link *link = st->sc_link;
u_int64_t block_count;
u_int32_t density, block_size;
u_char *page0 = NULL;
u_int8_t dev_spec;
int error = 0, big;
data = dma_alloc(sizeof(*data), PR_NOWAIT);
if (data == NULL)
return ENOMEM;
/*
* Ask for page 0 (vendor specific) mode sense data.
*/
density = 0;
block_count = 0;
block_size = 0;
error = scsi_do_mode_sense(link, 0, data, (void **)&page0, 1,
flags | SCSI_SILENT, &big);
if (error != 0)
goto done;
scsi_parse_blkdesc(link, data, big, &density, &block_count,
&block_size);
/* It is valid for no page0 to be available. */
if (big)
dev_spec = data->hdr_big.dev_spec;
else
dev_spec = data->hdr.dev_spec;
if (ISSET(dev_spec, SMH_DSP_WRITE_PROT))
SET(link->flags, SDEV_READONLY);
else
CLR(link->flags, SDEV_READONLY);
st->media_blksize = block_size;
st->media_density = density;
SC_DEBUG(link, SDEV_DB3,
("density code 0x%x, %d-byte blocks, write-%s, %sbuffered\n",
st->media_density, st->media_blksize,
ISSET(link->flags, SDEV_READONLY) ? "protected" : "enabled",
ISSET(dev_spec, SMH_DSP_BUFF_MODE) ? "" : "un"));
SET(link->flags, SDEV_MEDIA_LOADED);
done:
if (data)
dma_free(data, sizeof(*data));
return error;
}
/*
* Send a filled out parameter structure to the drive to
* set it into the desire mode etc.
*/
int
st_mode_select(struct st_softc *st, int flags)
{
union scsi_mode_sense_buf *inbuf = NULL, *outbuf = NULL;
struct scsi_blk_desc general;
struct scsi_link *link = st->sc_link;
u_int8_t *page0 = NULL;
int error = 0, big, page0_size;
inbuf = dma_alloc(sizeof(*inbuf), PR_NOWAIT);
if (inbuf == NULL) {
error = ENOMEM;
goto done;
}
outbuf = dma_alloc(sizeof(*outbuf), PR_NOWAIT | PR_ZERO);
if (outbuf == NULL) {
error = ENOMEM;
goto done;
}
/*
* This quirk deals with drives that have only one valid mode and think
* this gives them license to reject all mode selects, even if the
* selected mode is the one that is supported.
*/
if (ISSET(st->quirks, ST_Q_UNIMODAL)) {
SC_DEBUG(link, SDEV_DB3,
("not setting density 0x%x blksize 0x%x\n",
st->density, st->blksize));
error = 0;
goto done;
}
if (ISSET(link->flags, SDEV_ATAPI)) {
error = 0;
goto done;
}
bzero(&general, sizeof(general));
general.density = st->density;
if (ISSET(st->flags, ST_FIXEDBLOCKS))
_lto3b(st->blksize, general.blklen);
/*
* Ask for page 0 (vendor specific) mode sense data.
*
* page0 == NULL is a valid situation.
*/
error = scsi_do_mode_sense(link, 0, inbuf, (void **)&page0, 1,
flags | SCSI_SILENT, &big);
if (error != 0)
goto done;
if (page0 == NULL) {
page0_size = 0;
} else if (big == 0) {
page0_size = inbuf->hdr.data_length +
sizeof(inbuf->hdr.data_length) - sizeof(inbuf->hdr) -
inbuf->hdr.blk_desc_len;
memcpy(&outbuf->buf[sizeof(outbuf->hdr)+ sizeof(general)],
page0, page0_size);
} else {
page0_size = _2btol(inbuf->hdr_big.data_length) +
sizeof(inbuf->hdr_big.data_length) -
sizeof(inbuf->hdr_big) -
_2btol(inbuf->hdr_big.blk_desc_len);
memcpy(&outbuf->buf[sizeof(outbuf->hdr_big) + sizeof(general)],
page0, page0_size);
}
/*
* Set up for a mode select.
*/
if (big == 0) {
outbuf->hdr.data_length = sizeof(outbuf->hdr) +
sizeof(general) + page0_size -
sizeof(outbuf->hdr.data_length);
if (!ISSET(st->flags, ST_DONTBUFFER))
outbuf->hdr.dev_spec = SMH_DSP_BUFF_MODE_ON;
outbuf->hdr.blk_desc_len = sizeof(general);
memcpy(&outbuf->buf[sizeof(outbuf->hdr)],
&general, sizeof(general));
error = scsi_mode_select(st->sc_link, 0, &outbuf->hdr,
flags, ST_CTL_TIME);
goto done;
}
/* MODE SENSE (10) header was returned, so use MODE SELECT (10). */
_lto2b((sizeof(outbuf->hdr_big) + sizeof(general) + page0_size -
sizeof(outbuf->hdr_big.data_length)), outbuf->hdr_big.data_length);
if (!ISSET(st->flags, ST_DONTBUFFER))
outbuf->hdr_big.dev_spec = SMH_DSP_BUFF_MODE_ON;
_lto2b(sizeof(general), outbuf->hdr_big.blk_desc_len);
memcpy(&outbuf->buf[sizeof(outbuf->hdr_big)], &general,
sizeof(general));
error = scsi_mode_select_big(st->sc_link, 0, &outbuf->hdr_big,
flags, ST_CTL_TIME);
done:
if (inbuf)
dma_free(inbuf, sizeof(*inbuf));
if (outbuf)
dma_free(outbuf, sizeof(*outbuf));
return error;
}
/*
* issue an erase command
*/
int
st_erase(struct st_softc *st, int full, int flags)
{
struct scsi_erase *cmd;
struct scsi_xfer *xs;
int error;
xs = scsi_xs_get(st->sc_link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
/*
* Full erase means set LONG bit in erase command, which asks
* the drive to erase the entire unit. Without this bit, we're
* asking the drive to write an erase gap.
*/
cmd = (struct scsi_erase *)&xs->cmd;
cmd->opcode = ERASE;
if (full) {
cmd->byte2 = SE_IMMED|SE_LONG;
xs->timeout = ST_SPC_TIME;
} else {
cmd->byte2 = SE_IMMED;
xs->timeout = ST_IO_TIME;
}
/*
* XXX We always do this asynchronously, for now. How long should
* we wait if we want to (eventually) to it synchronously?
*/
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* skip N blocks/filemarks/seq filemarks/eom
*/
int
st_space(struct st_softc *st, int number, u_int what, int flags)
{
struct scsi_space *cmd;
struct scsi_xfer *xs;
int error;
switch (what) {
case SP_BLKS:
if (ISSET(st->flags, ST_PER_ACTION)) {
if (number > 0) {
CLR(st->flags, ST_PER_ACTION);
return EIO;
} else if (number < 0) {
if (ISSET(st->flags, ST_AT_FILEMARK)) {
/*
* Handling of ST_AT_FILEMARK
* in st_space will fill in the
* right file mark count.
*/
error = st_space(st, 0, SP_FILEMARKS,
flags);
if (error != 0)
return error;
}
if (ISSET(st->flags, ST_BLANK_READ)) {
CLR(st->flags, ST_BLANK_READ);
return EIO;
}
CLR(st->flags, ST_EIO_PENDING | ST_EOM_PENDING);
}
}
break;
case SP_FILEMARKS:
if (ISSET(st->flags, ST_EIO_PENDING)) {
if (number > 0) {
/* pretend we just discovered the error */
CLR(st->flags, ST_EIO_PENDING);
return EIO;
} else if (number < 0) {
/* back away from the error */
CLR(st->flags, ST_EIO_PENDING);
}
}
if (ISSET(st->flags, ST_AT_FILEMARK)) {
CLR(st->flags, ST_AT_FILEMARK);
number--;
}
if (ISSET(st->flags, ST_BLANK_READ) && (number < 0)) {
/* back away from unwritten tape */
CLR(st->flags, ST_BLANK_READ);
number++; /* XXX dubious */
}
break;
case SP_EOM:
if (ISSET(st->flags, ST_EOM_PENDING)) {
/* We are already there. */
CLR(st->flags, ST_EOM_PENDING);
return 0;
}
if (ISSET(st->flags, ST_EIO_PENDING)) {
/* pretend we just discovered the error */
CLR(st->flags, ST_EIO_PENDING);
return EIO;
}
if (ISSET(st->flags, ST_AT_FILEMARK))
CLR(st->flags, ST_AT_FILEMARK);
break;
}
if (number == 0)
return 0;
xs = scsi_xs_get(st->sc_link, flags);
if (xs == NULL)
return ENOMEM;
cmd = (struct scsi_space *)&xs->cmd;
cmd->opcode = SPACE;
cmd->byte2 = what;
_lto3b(number, cmd->number);
xs->cmdlen = sizeof(*cmd);
xs->timeout = ST_SPC_TIME;
CLR(st->flags, ST_EOD_DETECTED);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error != 0) {
st->media_fileno = -1;
st->media_blkno = -1;
} else {
switch (what) {
case SP_BLKS:
if (st->media_blkno != -1) {
st->media_blkno += number;
if (st->media_blkno < 0)
st->media_blkno = -1;
}
break;
case SP_FILEMARKS:
if (st->media_fileno != -1) {
if (!ISSET(st->flags, ST_EOD_DETECTED))
st->media_fileno += number;
if (st->media_fileno > st->media_eom)
st->media_eom = st->media_fileno;
st->media_blkno = 0;
}
break;
case SP_EOM:
if (st->media_eom != -1) {
st->media_fileno = st->media_eom;
st->media_blkno = 0;
} else {
st->media_fileno = -1;
st->media_blkno = -1;
}
break;
default:
st->media_fileno = -1;
st->media_blkno = -1;
break;
}
}
return error;
}
/*
* write N filemarks
*/
int
st_write_filemarks(struct st_softc *st, int number, int flags)
{
struct scsi_write_filemarks *cmd;
struct scsi_xfer *xs;
int error;
if (number < 0)
return EINVAL;
xs = scsi_xs_get(st->sc_link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = ST_IO_TIME * 4;
switch (number) {
case 0: /* really a command to sync the drive's buffers */
break;
case 1:
if (ISSET(st->flags, ST_FM_WRITTEN)) /* already have one down */
CLR(st->flags, ST_WRITTEN);
else
SET(st->flags, ST_FM_WRITTEN);
CLR(st->flags, ST_PER_ACTION);
break;
default:
CLR(st->flags, ST_PER_ACTION | ST_WRITTEN);
break;
}
cmd = (struct scsi_write_filemarks *)&xs->cmd;
cmd->opcode = WRITE_FILEMARKS;
_lto3b(number, cmd->number);
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error != 0) {
st->media_fileno = -1;
st->media_blkno = -1;
st->media_eom = -1;
} else if (st->media_fileno != -1) {
st->media_fileno += number;
st->media_eom = st->media_fileno;
st->media_blkno = 0;
}
return error;
}
/*
* Make sure the right number of file marks is on tape if the
* tape has been written. If the position argument is true,
* leave the tape positioned where it was originally.
*
* nmarks returns the number of marks to skip (or, if position
* true, which were skipped) to get back original position.
*/
int
st_check_eod(struct st_softc *st, int position, int *nmarks, int flags)
{
int error;
switch (st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD)) {
default:
*nmarks = 0;
return 0;
case ST_WRITTEN:
case ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD:
*nmarks = 1;
break;
case ST_WRITTEN | ST_2FM_AT_EOD:
*nmarks = 2;
}
error = st_write_filemarks(st, *nmarks, flags);
if (error == 0 && position != 0)
error = st_space(st, -*nmarks, SP_FILEMARKS, flags);
return error;
}
/*
* load/unload/retension
*/
int
st_load(struct st_softc *st, u_int type, int flags)
{
struct scsi_load *cmd;
struct scsi_xfer *xs;
int error, nmarks;
st->media_fileno = -1;
st->media_blkno = -1;
st->media_eom = -1;
if (type != LD_LOAD) {
error = st_check_eod(st, 0, &nmarks, flags);
if (error != 0)
return error;
}
if (ISSET(st->quirks, ST_Q_IGNORE_LOADS)) {
if (type == LD_LOAD) {
/*
* If we ignore loads, at least we should try a rewind.
*/
return st_rewind(st, 0, flags);
}
return 0;
}
xs = scsi_xs_get(st->sc_link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = ST_SPC_TIME;
cmd = (struct scsi_load *)&xs->cmd;
cmd->opcode = LOAD;
cmd->how = type;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
return error;
}
/*
* Rewind the device
*/
int
st_rewind(struct st_softc *st, u_int immediate, int flags)
{
struct scsi_rewind *cmd;
struct scsi_xfer *xs;
int error, nmarks;
error = st_check_eod(st, 0, &nmarks, flags);
if (error != 0)
return error;
CLR(st->flags, ST_PER_ACTION);
xs = scsi_xs_get(st->sc_link, flags);
if (xs == NULL)
return ENOMEM;
xs->cmdlen = sizeof(*cmd);
xs->timeout = immediate ? ST_CTL_TIME : ST_SPC_TIME;
cmd = (struct scsi_rewind *)&xs->cmd;
cmd->opcode = REWIND;
cmd->byte2 = immediate;
error = scsi_xs_sync(xs);
scsi_xs_put(xs);
if (error == 0) {
st->media_fileno = 0;
st->media_blkno = 0;
}
return error;
}
/*
* Look at the returned sense and act on the error to determine
* the unix error number to pass back... (0 = report no error)
* (-1 = continue processing)
*/
int
st_interpret_sense(struct scsi_xfer *xs)
{
struct scsi_sense_data *sense = &xs->sense;
struct scsi_link *link = xs->sc_link;
struct scsi_space *space;
struct st_softc *st = link->device_softc;
u_int8_t serr = sense->error_code & SSD_ERRCODE;
u_int8_t skey = sense->flags & SSD_KEY;
int32_t resid, info, number;
int datalen;
if (!ISSET(link->flags, SDEV_OPEN) ||
(serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
return scsi_interpret_sense(xs);
info = (int32_t)_4btol(sense->info);
switch (skey) {
/*
* We do custom processing in st for the unit becoming ready case.
* in this case we do not allow xs->retries to be decremented
* only on the "Unit Becoming Ready" case. This is because tape
* drives report "Unit Becoming Ready" when loading media, etc.
* and can take a long time. Rather than having a massive timeout
* for all operations (which would cause other problems) we allow
* operations to wait (but be interruptible with Ctrl-C) forever
* as long as the drive is reporting that it is becoming ready.
* all other cases are handled as per the default.
*/
case SKEY_NOT_READY:
if (ISSET(xs->flags, SCSI_IGNORE_NOT_READY))
return 0;
switch (ASC_ASCQ(sense)) {
case SENSE_NOT_READY_BECOMING_READY:
SC_DEBUG(link, SDEV_DB1, ("not ready: busy (%#x)\n",
sense->add_sense_code_qual));
/* don't count this as a retry */
xs->retries++;
return scsi_delay(xs, 1);
default:
return scsi_interpret_sense(xs);
}
break;
case SKEY_BLANK_CHECK:
if (sense->error_code & SSD_ERRCODE_VALID &&
xs->cmd.opcode == SPACE) {
switch (ASC_ASCQ(sense)) {
case SENSE_END_OF_DATA_DETECTED:
SET(st->flags, ST_EOD_DETECTED);
space = (struct scsi_space *)&xs->cmd;
number = _3btol(space->number);
st->media_fileno = number - info;
st->media_eom = st->media_fileno;
return 0;
case SENSE_BEGINNING_OF_MEDIUM_DETECTED:
/* Standard says: Position is undefined! */
SET(st->flags, ST_BOD_DETECTED);
st->media_fileno = -1;
st->media_blkno = -1;
return 0;
}
}
break;
case SKEY_NO_SENSE:
case SKEY_RECOVERED_ERROR:
case SKEY_MEDIUM_ERROR:
case SKEY_VOLUME_OVERFLOW:
break;
default:
return scsi_interpret_sense(xs);
}
/*
* 'resid' can be in units of st->blksize or bytes. xs->resid and
* xs->datalen are always in units of bytes. So we need a variable
* to store datalen in the same units as resid and to adjust
* xs->resid to be in bytes.
*/
if (ISSET(sense->error_code, SSD_ERRCODE_VALID)) {
if (ISSET(st->flags, ST_FIXEDBLOCKS))
resid = info * st->blksize; /* XXXX overflow? */
else
resid = info;
} else {
resid = xs->datalen;
}
if (resid < 0 || resid > xs->datalen)
xs->resid = xs->datalen;
else
xs->resid = resid;
datalen = xs->datalen;
if (ISSET(st->flags, ST_FIXEDBLOCKS)) {
resid /= st->blksize;
datalen /= st->blksize;
}
if (ISSET(sense->flags, SSD_FILEMARK)) {
if (st->media_fileno != -1) {
st->media_fileno++;
if (st->media_fileno > st->media_eom)
st->media_eom = st->media_fileno;
st->media_blkno = 0;
}
if (!ISSET(st->flags, ST_FIXEDBLOCKS))
return 0;
SET(st->flags, ST_AT_FILEMARK);
}
if (ISSET(sense->flags, SSD_EOM)) {
SET(st->flags, ST_EOM_PENDING);
xs->resid = 0;
if (ISSET(st->flags, ST_FIXEDBLOCKS))
return 0;
}
if (ISSET(sense->flags, SSD_ILI)) {
if (!ISSET(st->flags, ST_FIXEDBLOCKS)) {
if (resid >= 0 && resid <= datalen)
return 0;
if (!ISSET(xs->flags, SCSI_SILENT))
printf( "%s: bad residual %d out of "
"%d\n", st->sc_dev.dv_xname, resid,
datalen);
return EIO;
}
/* Fixed size blocks. */
if (ISSET(sense->error_code, SSD_ERRCODE_VALID))
if (!ISSET(xs->flags, SCSI_SILENT))
printf("%s: block wrong size, %d blocks "
"residual\n", st->sc_dev.dv_xname, resid);
SET(st->flags, ST_EIO_PENDING);
/*
* This quirk code helps the drive read the first tape block,
* regardless of format. That is required for these drives to
* return proper MODE SENSE information.
*/
if (ISSET(st->quirks, ST_Q_SENSE_HELP) &&
!ISSET(link->flags, SDEV_MEDIA_LOADED))
st->blksize -= 512;
}
if (ISSET(st->flags, ST_FIXEDBLOCKS) && xs->resid == xs->datalen) {
if (ISSET(st->flags, ST_EIO_PENDING))
return EIO;
if (ISSET(st->flags, ST_AT_FILEMARK))
return 0;
}
if (skey == SKEY_BLANK_CHECK) {
/*
* This quirk code helps the drive read the first tape block,
* regardless of format. That is required for these drives to
* return proper MODE SENSE information.
*/
if (ISSET(st->quirks, ST_Q_SENSE_HELP) &&
!ISSET(link->flags, SDEV_MEDIA_LOADED)) {
/* still starting */
st->blksize -= 512;
} else if (!ISSET(st->flags, ST_2FM_AT_EOD | ST_BLANK_READ)) {
SET(st->flags, ST_BLANK_READ);
SET(st->flags, ST_EOM_PENDING);
xs->resid = xs->datalen;
return 0;
}
}
return scsi_interpret_sense(xs);
}
/*
* The quirk here is that the drive returns some value to st_mode_sense
* incorrectly until the tape has actually passed by the head.
*
* The method is to set the drive to large fixed-block state (user-specified
* density and 1024-byte blocks), then read and rewind to get it to sense the
* tape. If that doesn't work, try 512-byte fixed blocks. If that doesn't
* work, as a last resort, try variable- length blocks. The result will be
* the ability to do an accurate st_mode_sense.
*
* We know we can do a rewind because we just did a load, which implies rewind.
* Rewind seems preferable to space backward if we have a virgin tape.
*
* The rest of the code for this quirk is in ILI processing and BLANK CHECK
* error processing, both part of st_interpret_sense.
*/
int
st_touch_tape(struct st_softc *st)
{
char *buf = NULL;
int readsize, maxblksize = 1024;
int error = 0;
if ((error = st_mode_sense(st, 0)) != 0)
goto done;
buf = dma_alloc(maxblksize, PR_NOWAIT);
if (buf == NULL) {
error = ENOMEM;
goto done;
}
st->blksize = 1024;
do {
switch (st->blksize) {
case 512:
case 1024:
readsize = st->blksize;
SET(st->flags, ST_FIXEDBLOCKS);
break;
default:
readsize = 1;
CLR(st->flags, ST_FIXEDBLOCKS);
}
if ((error = st_mode_select(st, 0)) != 0)
goto done;
st_read(st, buf, readsize, SCSI_SILENT); /* XXX */
if ((error = st_rewind(st, 0, 0)) != 0)
goto done;
} while (readsize != 1 && readsize > st->blksize);
done:
dma_free(buf, maxblksize);
return error;
}
819
834
204
113
103
11
112
58
9
23
38
9
1
98
32
874
25
592
65
9
5
2
28
175
771
648
9
1
57
358
781
7
6
7
3
2
2
4
503
501
30
494
427
120
449
59
17
3
30
499
359
345
21
360
358
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/* $OpenBSD: vfs_cache.c,v 1.58 2022/08/14 01:58:28 jsg Exp $ */
/* $NetBSD: vfs_cache.c,v 1.13 1996/02/04 02:18:09 christos Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)vfs_cache.c 8.3 (Berkeley) 8/22/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/lock.h>
#include <sys/namei.h>
#include <sys/errno.h>
#include <sys/pool.h>
/*
* TODO: namecache access should really be locked.
*/
/*
* For simplicity (and economy of storage), names longer than
* a maximum length of NAMECACHE_MAXLEN are not cached; they occur
* infrequently in any case, and are almost never of interest.
*
* Upon reaching the last segment of a path, if the reference
* is for DELETE, or NOCACHE is set (rewrite), and the
* name is located in the cache, it will be dropped.
*/
/*
* Structures associated with name caching.
*/
long numcache; /* total number of cache entries allocated */
long numneg; /* number of negative cache entries */
TAILQ_HEAD(, namecache) nclruhead; /* Regular Entry LRU chain */
TAILQ_HEAD(, namecache) nclruneghead; /* Negative Entry LRU chain */
struct nchstats nchstats; /* cache effectiveness statistics */
int doingcache = 1; /* 1 => enable the cache */
struct pool nch_pool;
void cache_zap(struct namecache *);
u_long nextvnodeid;
static inline int
namecache_compare(const struct namecache *n1, const struct namecache *n2)
{
if (n1->nc_nlen == n2->nc_nlen)
return (memcmp(n1->nc_name, n2->nc_name, n1->nc_nlen));
else
return (n1->nc_nlen - n2->nc_nlen);
}
RBT_PROTOTYPE(namecache_rb_cache, namecache, n_rbcache, namecache_compare);
RBT_GENERATE(namecache_rb_cache, namecache, n_rbcache, namecache_compare);
void
cache_tree_init(struct namecache_rb_cache *tree)
{
RBT_INIT(namecache_rb_cache, tree);
}
/*
* blow away a namecache entry
*/
void
cache_zap(struct namecache *ncp)
{
struct vnode *dvp = NULL;
if (ncp->nc_vp != NULL) {
TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
numcache--;
} else {
TAILQ_REMOVE(&nclruneghead, ncp, nc_neg);
numneg--;
}
if (ncp->nc_dvp) {
RBT_REMOVE(namecache_rb_cache, &ncp->nc_dvp->v_nc_tree, ncp);
if (RBT_EMPTY(namecache_rb_cache, &ncp->nc_dvp->v_nc_tree))
dvp = ncp->nc_dvp;
}
if (ncp->nc_vp && (ncp->nc_vpid == ncp->nc_vp->v_id)) {
if (ncp->nc_vp != ncp->nc_dvp &&
ncp->nc_vp->v_type == VDIR &&
(ncp->nc_nlen > 2 ||
(ncp->nc_nlen > 1 &&
ncp->nc_name[1] != '.') ||
(ncp->nc_nlen > 0 &&
ncp->nc_name[0] != '.'))) {
TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_me);
}
}
pool_put(&nch_pool, ncp);
if (dvp)
vdrop(dvp);
}
/*
* Look for a name in the cache.
* dvp points to the directory to search. The componentname cnp holds
* the information on the entry being sought, such as its length
* and its name. If the lookup succeeds, vpp is set to point to the vnode
* and an error of 0 is returned. If the lookup determines the name does
* not exist (negative caching) an error of ENOENT is returned. If the
* lookup fails, an error of -1 is returned.
*/
int
cache_lookup(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp)
{
struct namecache *ncp;
struct namecache n;
struct vnode *vp;
u_long vpid;
int error;
*vpp = NULL;
if (!doingcache) {
cnp->cn_flags &= ~MAKEENTRY;
return (-1);
}
if (cnp->cn_namelen > NAMECACHE_MAXLEN) {
nchstats.ncs_long++;
cnp->cn_flags &= ~MAKEENTRY;
return (-1);
}
/* lookup in directory vnode's redblack tree */
n.nc_nlen = cnp->cn_namelen;
memcpy(n.nc_name, cnp->cn_nameptr, n.nc_nlen);
ncp = RBT_FIND(namecache_rb_cache, &dvp->v_nc_tree, &n);
if (ncp == NULL) {
nchstats.ncs_miss++;
return (-1);
}
if ((cnp->cn_flags & MAKEENTRY) == 0) {
nchstats.ncs_badhits++;
goto remove;
} else if (ncp->nc_vp == NULL) {
if (cnp->cn_nameiop != CREATE ||
(cnp->cn_flags & ISLASTCN) == 0) {
nchstats.ncs_neghits++;
/*
* Move this slot to end of the negative LRU chain,
*/
if (TAILQ_NEXT(ncp, nc_neg) != NULL) {
TAILQ_REMOVE(&nclruneghead, ncp, nc_neg);
TAILQ_INSERT_TAIL(&nclruneghead, ncp,
nc_neg);
}
return (ENOENT);
} else {
nchstats.ncs_badhits++;
goto remove;
}
} else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
nchstats.ncs_falsehits++;
goto remove;
}
/*
* Move this slot to end of the regular LRU chain.
*/
if (TAILQ_NEXT(ncp, nc_lru) != NULL) {
TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
}
vp = ncp->nc_vp;
vpid = vp->v_id;
if (vp == dvp) { /* lookup on "." */
vref(dvp);
error = 0;
} else if (cnp->cn_flags & ISDOTDOT) {
VOP_UNLOCK(dvp);
cnp->cn_flags |= PDIRUNLOCK;
error = vget(vp, LK_EXCLUSIVE);
/*
* If the above vget() succeeded and both LOCKPARENT and
* ISLASTCN is set, lock the directory vnode as well.
*/
if (!error && (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) == 0) {
if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0) {
vput(vp);
return (error);
}
cnp->cn_flags &= ~PDIRUNLOCK;
}
} else {
error = vget(vp, LK_EXCLUSIVE);
/*
* If the above vget() failed or either of LOCKPARENT or
* ISLASTCN is set, unlock the directory vnode.
*/
if (error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
VOP_UNLOCK(dvp);
cnp->cn_flags |= PDIRUNLOCK;
}
}
/*
* Check that the lock succeeded, and that the capability number did
* not change while we were waiting for the lock.
*/
if (error || vpid != vp->v_id) {
if (!error) {
vput(vp);
nchstats.ncs_falsehits++;
} else
nchstats.ncs_badhits++;
/*
* The parent needs to be locked when we return to VOP_LOOKUP().
* The `.' case here should be extremely rare (if it can happen
* at all), so we don't bother optimizing out the unlock/relock.
*/
if (vp == dvp || error ||
(~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0)
return (error);
cnp->cn_flags &= ~PDIRUNLOCK;
}
return (-1);
}
nchstats.ncs_goodhits++;
*vpp = vp;
return (0);
remove:
/*
* Last component and we are renaming or deleting,
* the cache entry is invalid, or otherwise don't
* want cache entry to exist.
*/
cache_zap(ncp);
return (-1);
}
/*
* Scan cache looking for name of directory entry pointing at vp.
*
* Fill in dvpp.
*
* If bufp is non-NULL, also place the name in the buffer which starts
* at bufp, immediately before *bpp, and move bpp backwards to point
* at the start of it. (Yes, this is a little baroque, but it's done
* this way to cater to the whims of getcwd).
*
* Returns 0 on success, -1 on cache miss, positive errno on failure.
*
* TODO: should we return *dvpp locked?
*/
int
cache_revlookup(struct vnode *vp, struct vnode **dvpp, char **bpp, char *bufp)
{
struct namecache *ncp;
struct vnode *dvp = NULL;
char *bp;
if (!doingcache)
goto out;
TAILQ_FOREACH(ncp, &vp->v_cache_dst, nc_me) {
dvp = ncp->nc_dvp;
if (dvp && dvp != vp && ncp->nc_dvpid == dvp->v_id)
goto found;
}
goto miss;
found:
#ifdef DIAGNOSTIC
if (ncp->nc_nlen == 1 &&
ncp->nc_name[0] == '.')
panic("cache_revlookup: found entry for .");
if (ncp->nc_nlen == 2 &&
ncp->nc_name[0] == '.' &&
ncp->nc_name[1] == '.')
panic("cache_revlookup: found entry for ..");
#endif
nchstats.ncs_revhits++;
if (bufp != NULL) {
bp = *bpp;
bp -= ncp->nc_nlen;
if (bp <= bufp) {
*dvpp = NULL;
return (ERANGE);
}
memcpy(bp, ncp->nc_name, ncp->nc_nlen);
*bpp = bp;
}
*dvpp = dvp;
/*
* XXX: Should we vget() here to have more
* consistent semantics with cache_lookup()?
*/
return (0);
miss:
nchstats.ncs_revmiss++;
out:
*dvpp = NULL;
return (-1);
}
/*
* Add an entry to the cache
*/
void
cache_enter(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
{
struct namecache *ncp, *lncp;
if (!doingcache || cnp->cn_namelen > NAMECACHE_MAXLEN)
return;
/*
* allocate, or recycle (free and allocate) an ncp.
*/
if (numcache >= initialvnodes) {
if ((ncp = TAILQ_FIRST(&nclruhead)) != NULL)
cache_zap(ncp);
else if ((ncp = TAILQ_FIRST(&nclruneghead)) != NULL)
cache_zap(ncp);
else
panic("wtf? leak?");
}
ncp = pool_get(&nch_pool, PR_WAITOK|PR_ZERO);
/* grab the vnode we just found */
ncp->nc_vp = vp;
if (vp)
ncp->nc_vpid = vp->v_id;
/* fill in cache info */
ncp->nc_dvp = dvp;
ncp->nc_dvpid = dvp->v_id;
ncp->nc_nlen = cnp->cn_namelen;
memcpy(ncp->nc_name, cnp->cn_nameptr, ncp->nc_nlen);
if (RBT_EMPTY(namecache_rb_cache, &dvp->v_nc_tree)) {
vhold(dvp);
}
if ((lncp = RBT_INSERT(namecache_rb_cache, &dvp->v_nc_tree, ncp))
!= NULL) {
/* someone has raced us and added a different entry
* for the same vnode (different ncp) - we don't need
* this entry, so free it and we are done.
*/
pool_put(&nch_pool, ncp);
/* we know now dvp->v_nc_tree is not empty, no need
* to vdrop here
*/
goto done;
}
if (vp) {
TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
numcache++;
/* don't put . or .. in the reverse map */
if (vp != dvp && vp->v_type == VDIR &&
(ncp->nc_nlen > 2 ||
(ncp->nc_nlen > 1 &&
ncp->nc_name[1] != '.') ||
(ncp->nc_nlen > 0 &&
ncp->nc_name[0] != '.')))
TAILQ_INSERT_TAIL(&vp->v_cache_dst, ncp,
nc_me);
} else {
TAILQ_INSERT_TAIL(&nclruneghead, ncp, nc_neg);
numneg++;
}
if (numneg > initialvnodes) {
if ((ncp = TAILQ_FIRST(&nclruneghead))
!= NULL)
cache_zap(ncp);
}
done:
return;
}
/*
* Name cache initialization, from vfs_init() when we are booting
*/
void
nchinit(void)
{
TAILQ_INIT(&nclruhead);
TAILQ_INIT(&nclruneghead);
pool_init(&nch_pool, sizeof(struct namecache), 0, IPL_NONE, PR_WAITOK,
"nchpl", NULL);
}
/*
* Cache flush, a particular vnode; called when a vnode is renamed to
* hide entries that would now be invalid
*/
void
cache_purge(struct vnode *vp)
{
struct namecache *ncp;
/* We should never have destinations cached for a non-VDIR vnode. */
KASSERT(vp->v_type == VDIR || TAILQ_EMPTY(&vp->v_cache_dst));
while ((ncp = TAILQ_FIRST(&vp->v_cache_dst)))
cache_zap(ncp);
while ((ncp = RBT_ROOT(namecache_rb_cache, &vp->v_nc_tree)))
cache_zap(ncp);
/* XXX this blows goats */
vp->v_id = ++nextvnodeid;
if (vp->v_id == 0)
vp->v_id = ++nextvnodeid;
}
/*
* Cache flush, a whole filesystem; called when filesys is umounted to
* remove entries that would now be invalid
*/
void
cache_purgevfs(struct mount *mp)
{
struct namecache *ncp, *nxtcp;
/* whack the regular entries */
TAILQ_FOREACH_SAFE(ncp, &nclruhead, nc_lru, nxtcp) {
if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp)
continue;
/* free the resources we had */
cache_zap(ncp);
}
/* whack the negative entries */
TAILQ_FOREACH_SAFE(ncp, &nclruneghead, nc_neg, nxtcp) {
if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp)
continue;
/* free the resources we had */
cache_zap(ncp);
}
}
182
166
16
5
18
13
5
50
63
8
49
11
1
2
50
51
2
41
13
42
63
63
4
7
6
9
3
4
81
26
56
28
55
75
3
2
22
13
9
13
4
1
4
171
171
227
227
39
3
5
13
20
3
14
8
22
22
3
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
/* $OpenBSD: in6_src.c,v 1.86 2022/02/22 01:15:02 guenther Exp $ */
/* $KAME: in6_src.c,v 1.36 2001/02/06 04:08:17 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **, u_int);
/*
* Return an IPv6 address, which is the most appropriate for a given
* destination and pcb. We need the additional opt parameter because
* the values set at pcb level can be overridden via cmsg.
*/
int
in6_pcbselsrc(struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
struct inpcb *inp, struct ip6_pktopts *opts)
{
struct ip6_moptions *mopts = inp->inp_moptions6;
struct route_in6 *ro = &inp->inp_route6;
struct in6_addr *laddr = &inp->inp_laddr6;
u_int rtableid = inp->inp_rtableid;
struct ifnet *ifp = NULL;
struct sockaddr *ip6_source = NULL;
struct in6_addr *dst;
struct in6_ifaddr *ia6 = NULL;
struct in6_pktinfo *pi = NULL;
int error;
dst = &dstsock->sin6_addr;
/*
* If the source address is explicitly specified by the caller,
* check if the requested source address is indeed a unicast address
* assigned to the node, and can be used as the packet's source
* address. If everything is okay, use the address as source.
*/
if (opts && (pi = opts->ip6po_pktinfo) &&
!IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) {
struct sockaddr_in6 sa6;
/* get the outgoing interface */
error = in6_selectif(dstsock, opts, mopts, ro, &ifp, rtableid);
if (error)
return (error);
bzero(&sa6, sizeof(sa6));
sa6.sin6_family = AF_INET6;
sa6.sin6_len = sizeof(sa6);
sa6.sin6_addr = pi->ipi6_addr;
if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr))
sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
if_put(ifp); /* put reference from in6_selectif */
ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&sa6), rtableid));
if (ia6 == NULL || (ia6->ia6_flags &
(IN6_IFF_ANYCAST|IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)))
return (EADDRNOTAVAIL);
pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */
*in6src = &pi->ipi6_addr;
return (0);
}
/*
* If the source address is not specified but the socket(if any)
* is already bound, use the bound address.
*/
if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) {
*in6src = laddr;
return (0);
}
/*
* If the caller doesn't specify the source address but
* the outgoing interface, use an address associated with
* the interface.
*/
if (pi && pi->ipi6_ifindex) {
ifp = if_get(pi->ipi6_ifindex);
if (ifp == NULL)
return (ENXIO); /* XXX: better error? */
ia6 = in6_ifawithscope(ifp, dst, rtableid);
if_put(ifp);
if (ia6 == NULL)
return (EADDRNOTAVAIL);
*in6src = &ia6->ia_addr.sin6_addr;
return (0);
}
error = in6_selectsrc(in6src, dstsock, mopts, rtableid);
if (error != EADDRNOTAVAIL)
return (error);
/*
* If route is known or can be allocated now,
* our src addr is taken from the i/f, else punt.
*/
if (!rtisvalid(ro->ro_rt) || (ro->ro_tableid != rtableid) ||
!IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) {
rtfree(ro->ro_rt);
ro->ro_rt = NULL;
}
if (ro->ro_rt == NULL) {
struct sockaddr_in6 *sa6;
/* No route yet, so try to acquire one */
bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
ro->ro_tableid = rtableid;
sa6 = &ro->ro_dst;
sa6->sin6_family = AF_INET6;
sa6->sin6_len = sizeof(struct sockaddr_in6);
sa6->sin6_addr = *dst;
sa6->sin6_scope_id = dstsock->sin6_scope_id;
ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst),
RT_RESOLVE, ro->ro_tableid);
}
/*
* in_pcbconnect() checks out IFF_LOOPBACK to skip using
* the address. But we don't know why it does so.
* It is necessary to ensure the scope even for lo0
* so doesn't check out IFF_LOOPBACK.
*/
if (ro->ro_rt) {
ifp = if_get(ro->ro_rt->rt_ifidx);
if (ifp != NULL) {
ia6 = in6_ifawithscope(ifp, dst, rtableid);
if_put(ifp);
}
if (ia6 == NULL) /* xxx scope error ?*/
ia6 = ifatoia6(ro->ro_rt->rt_ifa);
}
/*
* Use preferred source address if :
* - destination is not onlink
* - preferred source address is set
* - output interface is UP
*/
if (ro->ro_rt && !(ro->ro_rt->rt_flags & RTF_LLINFO) &&
!(ro->ro_rt->rt_flags & RTF_HOST)) {
ip6_source = rtable_getsource(rtableid, AF_INET6);
if (ip6_source != NULL) {
struct ifaddr *ifa;
if ((ifa = ifa_ifwithaddr(ip6_source, rtableid)) !=
NULL && ISSET(ifa->ifa_ifp->if_flags, IFF_UP)) {
*in6src = &satosin6(ip6_source)->sin6_addr;
return (0);
}
}
}
if (ia6 == NULL)
return (EHOSTUNREACH); /* no route */
*in6src = &ia6->ia_addr.sin6_addr;
return (0);
}
/*
* Return an IPv6 address, which is the most appropriate for a given
* destination and multicast options.
* If necessary, this function lookups the routing table and returns
* an entry to the caller for later use.
*/
int
in6_selectsrc(struct in6_addr **in6src, struct sockaddr_in6 *dstsock,
struct ip6_moptions *mopts, unsigned int rtableid)
{
struct ifnet *ifp = NULL;
struct in6_addr *dst;
struct in6_ifaddr *ia6 = NULL;
dst = &dstsock->sin6_addr;
/*
* If the destination address is a link-local unicast address or
* a link/interface-local multicast address, and if the outgoing
* interface is specified by the sin6_scope_id filed, use an address
* associated with the interface.
* XXX: We're now trying to define more specific semantics of
* sin6_scope_id field, so this part will be rewritten in
* the near future.
*/
if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) ||
IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) {
ifp = if_get(dstsock->sin6_scope_id);
if (ifp == NULL)
return (ENXIO); /* XXX: better error? */
ia6 = in6_ifawithscope(ifp, dst, rtableid);
if_put(ifp);
if (ia6 == NULL)
return (EADDRNOTAVAIL);
*in6src = &ia6->ia_addr.sin6_addr;
return (0);
}
/*
* If the destination address is a multicast address and
* the outgoing interface for the address is specified
* by the caller, use an address associated with the interface.
* Even if the outgoing interface is not specified, we also
* choose a loopback interface as the outgoing interface.
*/
if (IN6_IS_ADDR_MULTICAST(dst)) {
ifp = mopts ? if_get(mopts->im6o_ifidx) : NULL;
if (!ifp && dstsock->sin6_scope_id)
ifp = if_get(htons(dstsock->sin6_scope_id));
if (ifp) {
ia6 = in6_ifawithscope(ifp, dst, rtableid);
if_put(ifp);
if (ia6 == NULL)
return (EADDRNOTAVAIL);
*in6src = &ia6->ia_addr.sin6_addr;
return (0);
}
}
return (EADDRNOTAVAIL);
}
struct rtentry *
in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct route_in6 *ro, unsigned int rtableid)
{
struct in6_addr *dst;
dst = &dstsock->sin6_addr;
/*
* Use a cached route if it exists and is valid, else try to allocate
* a new one.
*/
if (ro) {
if (rtisvalid(ro->ro_rt))
KASSERT(sin6tosa(&ro->ro_dst)->sa_family == AF_INET6);
if (!rtisvalid(ro->ro_rt) ||
!IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) {
rtfree(ro->ro_rt);
ro->ro_rt = NULL;
}
if (ro->ro_rt == NULL) {
struct sockaddr_in6 *sa6;
/* No route yet, so try to acquire one */
bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
ro->ro_tableid = rtableid;
sa6 = &ro->ro_dst;
*sa6 = *dstsock;
sa6->sin6_scope_id = 0;
ro->ro_tableid = rtableid;
ro->ro_rt = rtalloc_mpath(sin6tosa(&ro->ro_dst),
NULL, ro->ro_tableid);
}
/*
* Check if the outgoing interface conflicts with
* the interface specified by ipi6_ifindex (if specified).
* Note that loopback interface is always okay.
* (this may happen when we are sending a packet to one of
* our own addresses.)
*/
if (opts && opts->ip6po_pktinfo &&
opts->ip6po_pktinfo->ipi6_ifindex) {
if (ro->ro_rt != NULL &&
!ISSET(ro->ro_rt->rt_flags, RTF_LOCAL) &&
ro->ro_rt->rt_ifidx !=
opts->ip6po_pktinfo->ipi6_ifindex) {
return (NULL);
}
}
return (ro->ro_rt);
}
return (NULL);
}
int
in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp,
u_int rtableid)
{
struct rtentry *rt = NULL;
struct in6_pktinfo *pi = NULL;
/* If the caller specify the outgoing interface explicitly, use it. */
if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
*retifp = if_get(pi->ipi6_ifindex);
if (*retifp != NULL)
return (0);
}
/*
* If the destination address is a multicast address and the outgoing
* interface for the address is specified by the caller, use it.
*/
if (IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) &&
mopts != NULL && (*retifp = if_get(mopts->im6o_ifidx)) != NULL)
return (0);
rt = in6_selectroute(dstsock, opts, ro, rtableid);
if (rt == NULL)
return (EHOSTUNREACH);
/*
* do not use a rejected or black hole route.
* XXX: this check should be done in the L2 output routine.
* However, if we skipped this check here, we'd see the following
* scenario:
* - install a rejected route for a scoped address prefix
* (like fe80::/10)
* - send a packet to a destination that matches the scoped prefix,
* with ambiguity about the scope zone.
* - pick the outgoing interface from the route, and disambiguate the
* scope zone with the interface.
* - ip6_output() would try to get another route with the "new"
* destination, which may be valid.
* - we'd see no error on output.
* Although this may not be very harmful, it should still be confusing.
* We thus reject the case here.
*/
if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE)))
return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
if (rt != NULL)
*retifp = if_get(rt->rt_ifidx);
return (0);
}
int
in6_selecthlim(struct inpcb *in6p)
{
if (in6p && in6p->inp_hops >= 0)
return (in6p->inp_hops);
return (ip6_defhlim);
}
/*
* generate kernel-internal form (scopeid embedded into s6_addr16[1]).
* If the address scope of is link-local, embed the interface index in the
* address. The routine determines our precedence
* between advanced API scope/interface specification and basic API
* specification.
*
* this function should be nuked in the future, when we get rid of
* embedded scopeid thing.
*
* XXX actually, it is over-specification to return ifp against sin6_scope_id.
* there can be multiple interfaces that belong to a particular scope zone
* (in specification, we have 1:N mapping between a scope zone and interfaces).
* we may want to change the function to return something other than ifp.
*/
int
in6_embedscope(struct in6_addr *in6, const struct sockaddr_in6 *sin6,
struct inpcb *in6p)
{
struct ifnet *ifp = NULL;
u_int32_t scopeid;
*in6 = sin6->sin6_addr;
scopeid = sin6->sin6_scope_id;
/*
* don't try to read sin6->sin6_addr beyond here, since the caller may
* ask us to overwrite existing sockaddr_in6
*/
if (IN6_IS_SCOPE_EMBED(in6)) {
struct in6_pktinfo *pi;
/*
* KAME assumption: link id == interface id
*/
if (in6p && in6p->inp_outputopts6 &&
(pi = in6p->inp_outputopts6->ip6po_pktinfo) &&
pi->ipi6_ifindex) {
ifp = if_get(pi->ipi6_ifindex);
if (ifp == NULL)
return ENXIO; /* XXX EINVAL? */
in6->s6_addr16[1] = htons(pi->ipi6_ifindex);
} else if (in6p && IN6_IS_ADDR_MULTICAST(in6) &&
in6p->inp_moptions6 &&
(ifp = if_get(in6p->inp_moptions6->im6o_ifidx))) {
in6->s6_addr16[1] = htons(ifp->if_index);
} else if (scopeid) {
ifp = if_get(scopeid);
if (ifp == NULL)
return ENXIO; /* XXX EINVAL? */
/*XXX assignment to 16bit from 32bit variable */
in6->s6_addr16[1] = htons(scopeid & 0xffff);
}
if_put(ifp);
}
return 0;
}
/*
* generate standard sockaddr_in6 from embedded form.
* touches sin6_addr and sin6_scope_id only.
*
* this function should be nuked in the future, when we get rid of
* embedded scopeid thing.
*/
void
in6_recoverscope(struct sockaddr_in6 *sin6, const struct in6_addr *in6)
{
u_int32_t scopeid;
sin6->sin6_addr = *in6;
/*
* don't try to read *in6 beyond here, since the caller may
* ask us to overwrite existing sockaddr_in6
*/
sin6->sin6_scope_id = 0;
if (IN6_IS_SCOPE_EMBED(in6)) {
/*
* KAME assumption: link id == interface id
*/
scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]);
if (scopeid) {
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id = scopeid;
}
}
}
/*
* just clear the embedded scope identifier.
*/
void
in6_clearscope(struct in6_addr *addr)
{
if (IN6_IS_SCOPE_EMBED(addr))
addr->s6_addr16[1] = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/* $OpenBSD: buf.h,v 1.113 2022/09/01 05:24:51 jsg Exp $ */
/* $NetBSD: buf.h,v 1.25 1997/04/09 21:12:17 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)buf.h 8.7 (Berkeley) 1/21/94
*/
#ifndef _SYS_BUF_H_
#define _SYS_BUF_H_
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/mutex.h>
#include <uvm/uvm_extern.h>
#define NOLIST ((struct buf *)0x87654321)
struct buf;
struct vnode;
LIST_HEAD(bufhead, buf);
/*
* To avoid including <ufs/ffs/softdep.h>
*/
LIST_HEAD(workhead, worklist);
/*
* Buffer queues
*/
#define BUFQ_NSCAN_N 128
#define BUFQ_FIFO 0
#define BUFQ_NSCAN 1
#define BUFQ_DEFAULT BUFQ_NSCAN
#define BUFQ_HOWMANY 2
/*
* Write limits for bufq - defines high and low water marks for how
* many kva slots are allowed to be consumed to parallelize writes from
* the buffer cache from any individual bufq.
*/
#define BUFQ_HI 128
#define BUFQ_LOW 64
struct bufq_impl;
struct bufq {
SLIST_ENTRY(bufq) bufq_entries;
struct mutex bufq_mtx;
void *bufq_data;
u_int bufq_outstanding;
u_int bufq_hi;
u_int bufq_low;
int bufq_waiting;
int bufq_stop;
int bufq_type;
const struct bufq_impl *bufq_impl;
};
int bufq_init(struct bufq *, int);
int bufq_switch(struct bufq *, int);
void bufq_destroy(struct bufq *);
void bufq_queue(struct bufq *, struct buf *);
struct buf *bufq_dequeue(struct bufq *);
void bufq_requeue(struct bufq *, struct buf *);
int bufq_peek(struct bufq *);
void bufq_drain(struct bufq *);
void bufq_wait(struct bufq *);
void bufq_done(struct bufq *, struct buf *);
void bufq_quiesce(void);
void bufq_restart(void);
/* fifo */
SIMPLEQ_HEAD(bufq_fifo_head, buf);
struct bufq_fifo {
SIMPLEQ_ENTRY(buf) bqf_entries;
};
/* nscan */
SIMPLEQ_HEAD(bufq_nscan_head, buf);
struct bufq_nscan {
SIMPLEQ_ENTRY(buf) bqf_entries;
};
/* bufq link in struct buf */
union bufq_data {
struct bufq_fifo bufq_data_fifo;
struct bufq_nscan bufq_data_nscan;
};
/*
* These are currently used only by the soft dependency code, hence
* are stored once in a global variable. If other subsystems wanted
* to use these hooks, a pointer to a set of bio_ops could be added
* to each buffer.
*/
extern struct bio_ops {
void (*io_start)(struct buf *);
void (*io_complete)(struct buf *);
void (*io_deallocate)(struct buf *);
void (*io_movedeps)(struct buf *, struct buf *);
int (*io_countdeps)(struct buf *, int, int);
} bioops;
/* The buffer header describes an I/O operation in the kernel. */
struct buf {
RBT_ENTRY(buf) b_rbbufs; /* vnode "hash" tree */
LIST_ENTRY(buf) b_list; /* All allocated buffers. */
LIST_ENTRY(buf) b_vnbufs; /* Buffer's associated vnode. */
TAILQ_ENTRY(buf) b_freelist; /* Free list position if not active. */
int cache; /* which cache are we in */
struct proc *b_proc; /* Associated proc; NULL if kernel. */
volatile long b_flags; /* B_* flags. */
long b_bufsize; /* Allocated buffer size. */
long b_bcount; /* Valid bytes in buffer. */
size_t b_resid; /* Remaining I/O. */
int b_error; /* Errno value. */
dev_t b_dev; /* Device associated with buffer. */
caddr_t b_data; /* associated data */
void *b_saveaddr; /* Original b_data for physio. */
TAILQ_ENTRY(buf) b_valist; /* LRU of va to reuse. */
union bufq_data b_bufq;
struct bufq *b_bq; /* What bufq this buf is on */
struct uvm_object *b_pobj;
struct uvm_object b_uobj; /* Object containing the pages */
off_t b_poffs; /* Offset within object */
daddr_t b_lblkno; /* Logical block number. */
daddr_t b_blkno; /* Underlying physical block number. */
/* Function to call upon completion.
* Will be called at splbio(). */
void (*b_iodone)(struct buf *);
struct vnode *b_vp; /* Device vnode. */
int b_dirtyoff; /* Offset in buffer of dirty region. */
int b_dirtyend; /* Offset of end of dirty region. */
int b_validoff; /* Offset in buffer of valid region. */
int b_validend; /* Offset of end of valid region. */
struct workhead b_dep; /* List of filesystem dependencies. */
};
TAILQ_HEAD(bufqueue, buf);
struct bufcache {
int64_t hotbufpages;
int64_t warmbufpages;
int64_t cachepages;
struct bufqueue hotqueue;
struct bufqueue coldqueue;
struct bufqueue warmqueue;
};
/* Device driver compatibility definitions. */
#define b_active b_bcount /* Driver queue head: drive active. */
/*
* These flags are kept in b_flags.
*/
#define B_WRITE 0x00000000 /* Write buffer (pseudo flag). */
#define B_AGE 0x00000001 /* Move to age queue when I/O done. */
#define B_NEEDCOMMIT 0x00000002 /* Needs committing to stable storage */
#define B_ASYNC 0x00000004 /* Start I/O, do not wait. */
#define B_BAD 0x00000008 /* Bad block revectoring in progress. */
#define B_BUSY 0x00000010 /* I/O in progress. */
#define B_CACHE 0x00000020 /* Bread found us in the cache. */
#define B_CALL 0x00000040 /* Call b_iodone from biodone. */
#define B_DELWRI 0x00000080 /* Delay I/O until buffer reused. */
#define B_DONE 0x00000100 /* I/O completed. */
#define B_EINTR 0x00000200 /* I/O was interrupted */
#define B_ERROR 0x00000400 /* I/O error occurred. */
#define B_INVAL 0x00000800 /* Does not contain valid info. */
#define B_NOCACHE 0x00001000 /* Do not cache block after use. */
#define B_PHYS 0x00002000 /* I/O to user memory. */
#define B_RAW 0x00004000 /* Set by physio for raw transfers. */
#define B_READ 0x00008000 /* Read buffer. */
#define B_WANTED 0x00010000 /* Process wants this buffer. */
#define B_WRITEINPROG 0x00020000 /* Write in progress. */
#define B_XXX 0x00040000 /* Debugging flag. */
#define B_DEFERRED 0x00080000 /* Skipped over for cleaning */
#define B_SCANNED 0x00100000 /* Block already pushed during sync */
#define B_PDAEMON 0x00200000 /* I/O started by pagedaemon */
#define B_RELEASED 0x00400000 /* free this buffer after its kvm */
#define B_WARM 0x00800000 /* buffer is or has been on the warm queue */
#define B_COLD 0x01000000 /* buffer is on the cold queue */
#define B_BC 0x02000000 /* buffer is managed by the cache */
#define B_DMA 0x04000000 /* buffer is DMA reachable */
#define B_BITS "\20\001AGE\002NEEDCOMMIT\003ASYNC\004BAD\005BUSY" \
"\006CACHE\007CALL\010DELWRI\011DONE\012EINTR\013ERROR" \
"\014INVAL\015NOCACHE\016PHYS\017RAW\020READ" \
"\021WANTED\022WRITEINPROG\023XXX(FORMAT)\024DEFERRED" \
"\025SCANNED\026DAEMON\027RELEASED\030WARM\031COLD\032BC\033DMA"
/*
* Zero out the buffer's data area.
*/
#define clrbuf(bp) { \
bzero((bp)->b_data, (bp)->b_bcount); \
(bp)->b_resid = 0; \
}
/* Flags to low-level allocation routines. */
#define B_CLRBUF 0x01 /* Request allocated buffer be cleared. */
#define B_SYNC 0x02 /* Do all allocations synchronously. */
struct cluster_info {
daddr_t ci_lastr; /* last read (read-ahead) */
daddr_t ci_lastw; /* last write (write cluster) */
daddr_t ci_cstart; /* start block of cluster */
daddr_t ci_lasta; /* last allocation */
int ci_clen; /* length of current cluster */
int ci_ralen; /* Read-ahead length */
daddr_t ci_maxra; /* last readahead block */
};
#ifdef _KERNEL
__BEGIN_DECLS
/* Kva slots (of size MAXPHYS) reserved for syncer and cleaner. */
#define RESERVE_SLOTS 4
/* Buffer cache pages reserved for syncer and cleaner. */
#define RESERVE_PAGES (RESERVE_SLOTS * MAXPHYS / PAGE_SIZE)
/* Minimum size of the buffer cache, in pages. */
#define BCACHE_MIN (RESERVE_PAGES * 2)
#define UNCLEAN_PAGES (bcstats.numbufpages - bcstats.numcleanpages)
extern struct proc *cleanerproc;
extern long bufpages; /* Max number of pages for buffers' data */
extern struct pool bufpool;
extern struct bufhead bufhead;
void bawrite(struct buf *);
void bdwrite(struct buf *);
void biodone(struct buf *);
int biowait(struct buf *);
int bread(struct vnode *, daddr_t, int, struct buf **);
int breadn(struct vnode *, daddr_t, int, daddr_t *, int *, int,
struct buf **);
void brelse(struct buf *);
#define bremfree bufcache_take
void bufinit(void);
void buf_dirty(struct buf *);
void buf_undirty(struct buf *);
void buf_adjcnt(struct buf *, long);
int bwrite(struct buf *);
struct buf *getblk(struct vnode *, daddr_t, int, int, uint64_t);
struct buf *geteblk(size_t);
struct buf *incore(struct vnode *, daddr_t);
/*
* bufcache functions
*/
void bufcache_take(struct buf *);
void bufcache_release(struct buf *);
int buf_flip_high(struct buf *);
void buf_flip_dma(struct buf *);
struct buf *bufcache_getcleanbuf(int, int);
struct buf *bufcache_getdirtybuf(void);
/*
* buf_kvm_init initializes the kvm handling for buffers.
* buf_acquire sets the B_BUSY flag and ensures that the buffer is
* mapped in the kvm.
* buf_release clears the B_BUSY flag and allows the buffer to become
* unmapped.
* buf_unmap is for internal use only. Unmaps the buffer from kvm.
*/
void buf_mem_init(vsize_t);
void buf_acquire(struct buf *);
void buf_acquire_nomap(struct buf *);
void buf_map(struct buf *);
void buf_release(struct buf *);
int buf_dealloc_mem(struct buf *);
void buf_fix_mapping(struct buf *, vsize_t);
void buf_alloc_pages(struct buf *, vsize_t);
void buf_free_pages(struct buf *);
void minphys(struct buf *bp);
int physio(void (*strategy)(struct buf *), dev_t dev, int flags,
void (*minphys)(struct buf *), struct uio *uio);
void brelvp(struct buf *);
void reassignbuf(struct buf *);
void bgetvp(struct vnode *, struct buf *);
void buf_replacevnode(struct buf *, struct vnode *);
void buf_daemon(void *);
void buf_replacevnode(struct buf *, struct vnode *);
int bread_cluster(struct vnode *, daddr_t, int, struct buf **);
static __inline void
buf_start(struct buf *bp)
{
if (bioops.io_start)
(*bioops.io_start)(bp);
}
static __inline void
buf_complete(struct buf *bp)
{
if (bioops.io_complete)
(*bioops.io_complete)(bp);
}
static __inline void
buf_deallocate(struct buf *bp)
{
if (bioops.io_deallocate)
(*bioops.io_deallocate)(bp);
}
static __inline void
buf_movedeps(struct buf *bp, struct buf *bp2)
{
if (bioops.io_movedeps)
(*bioops.io_movedeps)(bp, bp2);
}
static __inline int
buf_countdeps(struct buf *bp, int i, int islocked)
{
if (bioops.io_countdeps)
return ((*bioops.io_countdeps)(bp, i, islocked));
else
return (0);
}
__END_DECLS
#endif /* _KERNEL */
#endif /* !_SYS_BUF_H_ */
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
/* $OpenBSD: gpio.c,v 1.17 2022/04/11 14:30:05 visa Exp $ */
/*
* Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
* Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* General Purpose Input/Output framework.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/gpio.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <dev/gpio/gpiovar.h>
struct gpio_softc {
struct device sc_dev;
gpio_chipset_tag_t sc_gc; /* GPIO controller */
gpio_pin_t *sc_pins; /* pins array */
int sc_npins; /* number of pins */
int sc_opened;
LIST_HEAD(, gpio_dev) sc_devs; /* devices */
LIST_HEAD(, gpio_name) sc_names; /* named pins */
};
int gpio_match(struct device *, void *, void *);
int gpio_submatch(struct device *, void *, void *);
void gpio_attach(struct device *, struct device *, void *);
int gpio_detach(struct device *, int);
int gpio_search(struct device *, void *, void *);
int gpio_print(void *, const char *);
int gpio_pinbyname(struct gpio_softc *, char *gp_name);
int gpio_ioctl(struct gpio_softc *, u_long, caddr_t, int);
const struct cfattach gpio_ca = {
sizeof (struct gpio_softc),
gpio_match,
gpio_attach,
gpio_detach
};
struct cfdriver gpio_cd = {
NULL, "gpio", DV_DULL
};
int
gpio_match(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct gpiobus_attach_args *gba = aux;
return (strcmp(gba->gba_name, cf->cf_driver->cd_name) == 0);
}
int
gpio_submatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct gpio_attach_args *ga = aux;
if (strcmp(ga->ga_dvname, cf->cf_driver->cd_name) != 0)
return (0);
return ((*cf->cf_attach->ca_match)(parent, match, aux));
}
void
gpio_attach(struct device *parent, struct device *self, void *aux)
{
struct gpio_softc *sc = (struct gpio_softc *)self;
struct gpiobus_attach_args *gba = aux;
sc->sc_gc = gba->gba_gc;
sc->sc_pins = gba->gba_pins;
sc->sc_npins = gba->gba_npins;
printf(": %d pins\n", sc->sc_npins);
/*
* Attach all devices that can be connected to the GPIO pins
* described in the kernel configuration file.
*/
config_search(gpio_search, self, sc);
}
int
gpio_detach(struct device *self, int flags)
{
int maj, mn;
/* Locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == gpioopen)
break;
/* Nuke the vnodes for any open instances (calls close) */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
return (0);
}
int
gpio_search(struct device *parent, void *arg, void *aux)
{
struct cfdata *cf = arg;
struct gpio_attach_args ga;
ga.ga_gpio = aux;
ga.ga_offset = cf->cf_loc[0];
ga.ga_mask = cf->cf_loc[1];
ga.ga_flags = cf->cf_loc[2];
if (cf->cf_attach->ca_match(parent, cf, &ga) > 0)
config_attach(parent, cf, &ga, gpio_print);
return (0);
}
int
gpio_print(void *aux, const char *pnp)
{
struct gpio_attach_args *ga = aux;
int i;
printf(" pins");
for (i = 0; i < 32; i++)
if (ga->ga_mask & (1 << i))
printf(" %d", ga->ga_offset + i);
return (UNCONF);
}
int
gpiobus_print(void *aux, const char *pnp)
{
struct gpiobus_attach_args *gba = aux;
if (pnp != NULL)
printf("%s at %s", gba->gba_name, pnp);
return (UNCONF);
}
int
gpio_pin_map(void *gpio, int offset, u_int32_t mask, struct gpio_pinmap *map)
{
struct gpio_softc *sc = gpio;
int npins, pin, i;
npins = gpio_npins(mask);
if (npins > sc->sc_npins)
return (1);
for (npins = 0, i = 0; i < 32; i++)
if (mask & (1 << i)) {
pin = offset + i;
if (pin < 0 || pin >= sc->sc_npins)
return (1);
if (sc->sc_pins[pin].pin_mapped)
return (1);
sc->sc_pins[pin].pin_mapped = 1;
map->pm_map[npins++] = pin;
}
map->pm_size = npins;
return (0);
}
void
gpio_pin_unmap(void *gpio, struct gpio_pinmap *map)
{
struct gpio_softc *sc = gpio;
int pin, i;
for (i = 0; i < map->pm_size; i++) {
pin = map->pm_map[i];
sc->sc_pins[pin].pin_mapped = 0;
}
}
int
gpio_pin_read(void *gpio, struct gpio_pinmap *map, int pin)
{
struct gpio_softc *sc = gpio;
return (gpiobus_pin_read(sc->sc_gc, map->pm_map[pin]));
}
void
gpio_pin_write(void *gpio, struct gpio_pinmap *map, int pin, int value)
{
struct gpio_softc *sc = gpio;
return (gpiobus_pin_write(sc->sc_gc, map->pm_map[pin], value));
}
void
gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags)
{
struct gpio_softc *sc = gpio;
return (gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags));
}
int
gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
{
struct gpio_softc *sc = gpio;
return (sc->sc_pins[map->pm_map[pin]].pin_caps);
}
int
gpio_npins(u_int32_t mask)
{
int npins, i;
for (npins = 0, i = 0; i < 32; i++)
if (mask & (1 << i))
npins++;
return (npins);
}
int
gpioopen(dev_t dev, int flag, int mode, struct proc *p)
{
struct gpio_softc *sc;
int error = 0;
sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
if (sc == NULL)
return (ENXIO);
if (sc->sc_opened)
error = EBUSY;
else
sc->sc_opened = 1;
device_unref(&sc->sc_dev);
return (error);
}
int
gpioclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct gpio_softc *sc;
sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
if (sc == NULL)
return (ENXIO);
sc->sc_opened = 0;
device_unref(&sc->sc_dev);
return (0);
}
int
gpio_pinbyname(struct gpio_softc *sc, char *gp_name)
{
struct gpio_name *nm;
LIST_FOREACH(nm, &sc->sc_names, gp_next)
if (!strcmp(nm->gp_name, gp_name))
return (nm->gp_pin);
return (-1);
}
int
gpio_ioctl(struct gpio_softc *sc, u_long cmd, caddr_t data, int flag)
{
gpio_chipset_tag_t gc;
struct gpio_info *info;
struct gpio_pin_op *op;
struct gpio_attach *attach;
struct gpio_attach_args ga;
struct gpio_dev *gdev;
struct gpio_name *nm;
struct gpio_pin_set *set;
struct device *dv;
int pin, value, flags, npins, found;
gc = sc->sc_gc;
switch (cmd) {
case GPIOINFO:
info = (struct gpio_info *)data;
if (securelevel < 1)
info->gpio_npins = sc->sc_npins;
else {
for (pin = npins = 0; pin < sc->sc_npins; pin++)
if (sc->sc_pins[pin].pin_flags & GPIO_PIN_SET)
++npins;
info->gpio_npins = npins;
}
break;
case GPIOPINREAD:
op = (struct gpio_pin_op *)data;
if (op->gp_name[0] != '\0') {
pin = gpio_pinbyname(sc, op->gp_name);
if (pin == -1)
return (EINVAL);
} else
pin = op->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
return (EINVAL);
if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
securelevel > 0)
return (EPERM);
/* return read value */
op->gp_value = gpiobus_pin_read(gc, pin);
break;
case GPIOPINWRITE:
if ((flag & FWRITE) == 0)
return (EBADF);
op = (struct gpio_pin_op *)data;
if (op->gp_name[0] != '\0') {
pin = gpio_pinbyname(sc, op->gp_name);
if (pin == -1)
return (EINVAL);
} else
pin = op->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
return (EINVAL);
if (sc->sc_pins[pin].pin_mapped)
return (EBUSY);
if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
securelevel > 0)
return (EPERM);
value = op->gp_value;
if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH)
return (EINVAL);
gpiobus_pin_write(gc, pin, value);
/* return old value */
op->gp_value = sc->sc_pins[pin].pin_state;
/* update current value */
sc->sc_pins[pin].pin_state = value;
break;
case GPIOPINTOGGLE:
if ((flag & FWRITE) == 0)
return (EBADF);
op = (struct gpio_pin_op *)data;
if (op->gp_name[0] != '\0') {
pin = gpio_pinbyname(sc, op->gp_name);
if (pin == -1)
return (EINVAL);
} else
pin = op->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
return (EINVAL);
if (sc->sc_pins[pin].pin_mapped)
return (EBUSY);
if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
securelevel > 0)
return (EPERM);
value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ?
GPIO_PIN_HIGH : GPIO_PIN_LOW);
gpiobus_pin_write(gc, pin, value);
/* return old value */
op->gp_value = sc->sc_pins[pin].pin_state;
/* update current value */
sc->sc_pins[pin].pin_state = value;
break;
case GPIOATTACH:
if (securelevel > 0)
return (EPERM);
attach = (struct gpio_attach *)data;
bzero(&ga, sizeof(ga));
ga.ga_gpio = sc;
ga.ga_dvname = attach->ga_dvname;
ga.ga_offset = attach->ga_offset;
ga.ga_mask = attach->ga_mask;
ga.ga_flags = attach->ga_flags;
dv = config_found_sm((struct device *)sc, &ga, gpiobus_print,
gpio_submatch);
if (dv != NULL) {
gdev = malloc(sizeof(*gdev), M_DEVBUF,
M_WAITOK);
gdev->sc_dev = dv;
LIST_INSERT_HEAD(&sc->sc_devs, gdev, sc_next);
}
break;
case GPIODETACH:
if (securelevel > 0)
return (EPERM);
attach = (struct gpio_attach *)data;
LIST_FOREACH(gdev, &sc->sc_devs, sc_next) {
if (strcmp(gdev->sc_dev->dv_xname, attach->ga_dvname)
== 0) {
if (config_detach(gdev->sc_dev, 0) == 0) {
LIST_REMOVE(gdev, sc_next);
free(gdev, M_DEVBUF, sizeof(*gdev));
}
break;
}
}
break;
case GPIOPINSET:
if (securelevel > 0)
return (EPERM);
set = (struct gpio_pin_set *)data;
if (set->gp_name[0] != '\0') {
pin = gpio_pinbyname(sc, set->gp_name);
if (pin == -1)
return (EINVAL);
} else
pin = set->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
return (EINVAL);
flags = set->gp_flags;
/* check that the controller supports all requested flags */
if ((flags & sc->sc_pins[pin].pin_caps) != flags)
return (ENODEV);
flags = set->gp_flags | GPIO_PIN_SET;
set->gp_caps = sc->sc_pins[pin].pin_caps;
/* return old value */
set->gp_flags = sc->sc_pins[pin].pin_flags;
if (flags > 0) {
gpiobus_pin_ctl(gc, pin, flags);
/* update current value */
sc->sc_pins[pin].pin_flags = flags;
}
/* rename pin or new pin? */
if (set->gp_name2[0] != '\0') {
found = 0;
LIST_FOREACH(nm, &sc->sc_names, gp_next)
if (nm->gp_pin == pin) {
strlcpy(nm->gp_name, set->gp_name2,
sizeof(nm->gp_name));
found = 1;
break;
}
if (!found) {
nm = malloc(sizeof(*nm), M_DEVBUF, M_WAITOK);
strlcpy(nm->gp_name, set->gp_name2,
sizeof(nm->gp_name));
nm->gp_pin = set->gp_pin;
LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next);
}
}
break;
case GPIOPINUNSET:
if (securelevel > 0)
return (EPERM);
set = (struct gpio_pin_set *)data;
if (set->gp_name[0] != '\0') {
pin = gpio_pinbyname(sc, set->gp_name);
if (pin == -1)
return (EINVAL);
} else
pin = set->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
return (EINVAL);
if (sc->sc_pins[pin].pin_mapped)
return (EBUSY);
if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET))
return (EINVAL);
LIST_FOREACH(nm, &sc->sc_names, gp_next) {
if (nm->gp_pin == pin) {
LIST_REMOVE(nm, gp_next);
free(nm, M_DEVBUF, sizeof(*nm));
break;
}
}
sc->sc_pins[pin].pin_flags &= ~GPIO_PIN_SET;
break;
default:
return (ENOTTY);
}
return (0);
}
int
gpioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct gpio_softc *sc;
int error;
sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
if (sc == NULL)
return (ENXIO);
error = gpio_ioctl(sc, cmd, data, flag);
device_unref(&sc->sc_dev);
return (error);
}
4
2
2
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
/* $OpenBSD: wstpad.c,v 1.31 2022/06/09 22:17:18 bru Exp $ */
/*
* Copyright (c) 2015, 2016 Ulf Brosziewski
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* touchpad input processing
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/timeout.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#include <dev/wscons/wseventvar.h>
#include <dev/wscons/wsmouseinput.h>
#define BTNMASK(n) ((n) > 0 && (n) <= 32 ? 1 << ((n) - 1) : 0)
#define LEFTBTN BTNMASK(1)
#define MIDDLEBTN BTNMASK(2)
#define RIGHTBTN BTNMASK(3)
#define PRIMARYBTN LEFTBTN
#define PRIMARYBTN_CLICKED(tp) ((tp)->btns_sync & PRIMARYBTN & (tp)->btns)
#define PRIMARYBTN_RELEASED(tp) ((tp)->btns_sync & PRIMARYBTN & ~(tp)->btns)
#define IS_MT(tp) ((tp)->features & WSTPAD_MT)
#define DISABLE(tp) ((tp)->features & WSTPAD_DISABLE)
/*
* Ratios to the height or width of the touchpad surface, in
* [*.12] fixed-point format:
*/
#define V_EDGE_RATIO_DEFAULT 205
#define B_EDGE_RATIO_DEFAULT 410
#define T_EDGE_RATIO_DEFAULT 512
#define CENTER_RATIO_DEFAULT 512
#define TAP_MAXTIME_DEFAULT 180
#define TAP_CLICKTIME_DEFAULT 180
#define TAP_LOCKTIME_DEFAULT 0
#define TAP_BTNMAP_SIZE 3
#define CLICKDELAY_MS 20
#define FREEZE_MS 100
#define MATCHINTERVAL_MS 45
#define STOPINTERVAL_MS 55
#define MAG_LOW (10 << 12)
#define MAG_MEDIUM (18 << 12)
enum tpad_handlers {
SOFTBUTTON_HDLR,
TOPBUTTON_HDLR,
TAP_HDLR,
F2SCROLL_HDLR,
EDGESCROLL_HDLR,
CLICK_HDLR,
};
enum tap_state {
TAP_DETECT,
TAP_IGNORE,
TAP_LIFTED,
TAP_LOCKED,
TAP_LOCKED_DRAG,
};
enum tpad_cmd {
CLEAR_MOTION_DELTAS,
SOFTBUTTON_DOWN,
SOFTBUTTON_UP,
TAPBUTTON_SYNC,
TAPBUTTON_DOWN,
TAPBUTTON_UP,
VSCROLL,
HSCROLL,
};
/*
* tpad_touch.flags:
*/
#define L_EDGE (1 << 0)
#define R_EDGE (1 << 1)
#define T_EDGE (1 << 2)
#define B_EDGE (1 << 3)
#define THUMB (1 << 4)
#define EDGES (L_EDGE | R_EDGE | T_EDGE | B_EDGE)
/*
* A touch is "centered" if it does not start and remain at the top
* edge or one of the vertical edges. Two-finger scrolling and tapping
* require that at least one touch is centered.
*/
#define CENTERED(t) (((t)->flags & (L_EDGE | R_EDGE | T_EDGE)) == 0)
enum touchstates {
TOUCH_NONE,
TOUCH_BEGIN,
TOUCH_UPDATE,
TOUCH_END,
};
struct tpad_touch {
u_int flags;
enum touchstates state;
int x;
int y;
int dir;
struct timespec start;
struct timespec match;
struct position *pos;
struct {
int x;
int y;
struct timespec time;
} orig;
};
/*
* wstpad.features
*/
#define WSTPAD_SOFTBUTTONS (1 << 0)
#define WSTPAD_SOFTMBTN (1 << 1)
#define WSTPAD_TOPBUTTONS (1 << 2)
#define WSTPAD_TWOFINGERSCROLL (1 << 3)
#define WSTPAD_EDGESCROLL (1 << 4)
#define WSTPAD_HORIZSCROLL (1 << 5)
#define WSTPAD_SWAPSIDES (1 << 6)
#define WSTPAD_DISABLE (1 << 7)
#define WSTPAD_MT (1 << 31)
struct wstpad {
u_int features;
u_int handlers;
/*
* t always points into the tpad_touches array, which has at
* least one element. If there is more than one, t selects
* the pointer-controlling touch.
*/
struct tpad_touch *t;
struct tpad_touch *tpad_touches;
u_int mtcycle;
u_int ignore;
int contacts;
int prev_contacts;
u_int btns;
u_int btns_sync;
int ratio;
struct timespec time;
u_int freeze;
struct timespec freeze_ts;
/* edge coordinates */
struct {
int left;
int right;
int top;
int bottom;
int center;
int center_left;
int center_right;
int low;
} edge;
struct {
/* ratios to the surface width or height */
int left_edge;
int right_edge;
int top_edge;
int bottom_edge;
int center_width;
/* two-finger contacts */
int f2pressure;
int f2width;
} params;
/* handler state and configuration: */
u_int softbutton;
u_int sbtnswap;
struct {
enum tap_state state;
int contacts;
int valid;
u_int pending;
u_int button;
int masked;
int maxdist;
struct timeout to;
/* parameters: */
struct timespec maxtime;
int clicktime;
int locktime;
u_int btnmap[TAP_BTNMAP_SIZE];
} tap;
struct {
int dz;
int dw;
int hdist;
int vdist;
int mag;
} scroll;
};
static const struct timespec match_interval =
{ .tv_sec = 0, .tv_nsec = MATCHINTERVAL_MS * 1000000 };
static const struct timespec stop_interval =
{ .tv_sec = 0, .tv_nsec = STOPINTERVAL_MS * 1000000 };
/*
* Coordinates in the wstpad struct are "normalized" device coordinates,
* the orientation is left-to-right and upward.
*/
static inline int
normalize_abs(struct axis_filter *filter, int val)
{
return (filter->inv ? filter->inv - val : val);
}
static inline int
normalize_rel(struct axis_filter *filter, int val)
{
return (filter->inv ? -val : val);
}
/*
* Directions of motion are represented by numbers in the range 0 - 11,
* corresponding to clockwise counted circle sectors:
*
* 11 | 0
* 10 | 1
* 9 | 2
* -------+-------
* 8 | 3
* 7 | 4
* 6 | 5
*
*/
/* Tangent constants in [*.12] fixed-point format: */
#define TAN_DEG_60 7094
#define TAN_DEG_30 2365
#define NORTH(d) ((d) == 0 || (d) == 11)
#define SOUTH(d) ((d) == 5 || (d) == 6)
#define EAST(d) ((d) == 2 || (d) == 3)
#define WEST(d) ((d) == 8 || (d) == 9)
static inline int
direction(int dx, int dy, int ratio)
{
int rdy, dir = -1;
if (dx || dy) {
rdy = abs(dy) * ratio;
if (abs(dx) * TAN_DEG_60 < rdy)
dir = 0;
else if (abs(dx) * TAN_DEG_30 < rdy)
dir = 1;
else
dir = 2;
if ((dx < 0) != (dy < 0))
dir = 5 - dir;
if (dx < 0)
dir += 6;
}
return dir;
}
static inline int
dircmp(int dir1, int dir2)
{
int diff = abs(dir1 - dir2);
return (diff <= 6 ? diff : 12 - diff);
}
/*
* Update direction and timespec attributes for a touch. They are used to
* determine whether it is moving - or resting - stably.
*
* The callers pass touches from the current frame and the touches that are
* no longer present in the update cycle to this function. Even though this
* ensures that pairs of zero deltas do not result from stale coordinates,
* zero deltas do not reset the state immediately. A short time span - the
* "stop interval" - must pass before the state is cleared, which is
* necessary because some touchpads report intermediate stops when a touch
* is moving very slowly.
*/
void
wstpad_set_direction(struct wstpad *tp, struct tpad_touch *t, int dx, int dy)
{
int dir;
struct timespec ts;
if (t->state != TOUCH_UPDATE) {
t->dir = -1;
memcpy(&t->start, &tp->time, sizeof(struct timespec));
return;
}
dir = direction(dx, dy, tp->ratio);
if (dir >= 0) {
if (t->dir < 0 || dircmp(dir, t->dir) > 1) {
memcpy(&t->start, &tp->time, sizeof(struct timespec));
}
t->dir = dir;
memcpy(&t->match, &tp->time, sizeof(struct timespec));
} else if (t->dir >= 0) {
timespecsub(&tp->time, &t->match, &ts);
if (timespeccmp(&ts, &stop_interval, >=)) {
t->dir = -1;
memcpy(&t->start, &t->match, sizeof(struct timespec));
}
}
}
/*
* Make a rough, but quick estimation of the speed of a touch. Its
* distance to the previous position is scaled by factors derived
* from the average update rate and the deceleration parameter
* (filter.dclr). The unit of the result is:
* (filter.dclr / 100) device units per millisecond
*
* Magnitudes are returned in [*.12] fixed-point format. For purposes
* of filtering, they are divided into medium and high speeds
* (> MAG_MEDIUM), low speeds, and very low speeds (< MAG_LOW).
*
* The scale factors are not affected if deceleration is turned off.
*/
static inline int
magnitude(struct wsmouseinput *input, int dx, int dy)
{
int h, v;
h = abs(dx) * input->filter.h.mag_scale;
v = abs(dy) * input->filter.v.mag_scale;
/* Return an "alpha-max-plus-beta-min" approximation: */
return (h >= v ? h + 3 * v / 8 : v + 3 * h / 8);
}
/*
* Treat a touch as stable if it is moving at a medium or high speed,
* if it is moving continuously, or if it has stopped for a certain
* time span.
*/
int
wstpad_is_stable(struct wsmouseinput *input, struct tpad_touch *t)
{
struct timespec ts;
if (t->dir >= 0) {
if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_MEDIUM)
return (1);
timespecsub(&t->match, &t->start, &ts);
} else {
timespecsub(&input->tp->time, &t->start, &ts);
}
return (timespeccmp(&ts, &match_interval, >=));
}
/*
* If a touch starts in an edge area, pointer movement will be
* suppressed as long as it stays in that area.
*/
static inline u_int
edge_flags(struct wstpad *tp, int x, int y)
{
u_int flags = 0;
if (x < tp->edge.left)
flags |= L_EDGE;
else if (x >= tp->edge.right)
flags |= R_EDGE;
if (y < tp->edge.bottom)
flags |= B_EDGE;
else if (y >= tp->edge.top)
flags |= T_EDGE;
return (flags);
}
static inline struct tpad_touch *
get_2nd_touch(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
int slot;
if (IS_MT(tp)) {
slot = ffs(input->mt.touches & ~(input->mt.ptr | tp->ignore));
if (slot)
return &tp->tpad_touches[--slot];
}
return NULL;
}
/* Suppress pointer motion for a short period of time. */
static inline void
set_freeze_ts(struct wstpad *tp, int sec, int ms)
{
tp->freeze_ts.tv_sec = sec;
tp->freeze_ts.tv_nsec = ms * 1000000;
timespecadd(&tp->time, &tp->freeze_ts, &tp->freeze_ts);
}
/* Return TRUE if two-finger- or edge-scrolling would be valid. */
int
wstpad_scroll_coords(struct wsmouseinput *input, int *dx, int *dy)
{
struct wstpad *tp = input->tp;
if (tp->contacts != tp->prev_contacts || tp->btns || tp->btns_sync) {
tp->scroll.dz = 0;
tp->scroll.dw = 0;
return (0);
}
if ((input->motion.sync & SYNC_POSITION) == 0)
return (0);
/*
* Try to exclude accidental scroll events by checking whether the
* pointer-controlling touch is stable. The check, which may cause
* a short delay, is only applied initially, a touch that stops and
* resumes scrolling is not affected.
*/
if (tp->scroll.dz || tp->scroll.dw || wstpad_is_stable(input, tp->t)) {
*dx = normalize_rel(&input->filter.h, input->motion.pos.dx);
*dy = normalize_rel(&input->filter.v, input->motion.pos.dy);
return (*dx || *dy);
}
return (0);
}
void
wstpad_scroll(struct wstpad *tp, int dx, int dy, int mag, u_int *cmds)
{
int dz, dw, n = 1;
/*
* The function applies strong deceleration, but only to input with
* very low speeds. A higher threshold might make applications
* without support for precision scrolling appear unresponsive.
*/
mag = tp->scroll.mag = imin(MAG_MEDIUM,
(mag + 3 * tp->scroll.mag) / 4);
if (mag < MAG_LOW)
n = (MAG_LOW - mag) / 4096 + 1;
if (dy && tp->scroll.vdist) {
if (tp->scroll.dw) {
/*
* Before switching the axis, wstpad_scroll_coords()
* should check again whether the movement is stable.
*/
tp->scroll.dw = 0;
return;
}
dz = -dy * 4096 / (tp->scroll.vdist * n);
if (tp->scroll.dz) {
if ((dy < 0) != (tp->scroll.dz > 0))
tp->scroll.dz = -tp->scroll.dz;
dz = (dz + 3 * tp->scroll.dz) / 4;
}
if (dz) {
tp->scroll.dz = dz;
*cmds |= 1 << VSCROLL;
}
} else if (dx && tp->scroll.hdist) {
if (tp->scroll.dz) {
tp->scroll.dz = 0;
return;
}
dw = dx * 4096 / (tp->scroll.hdist * n);
if (tp->scroll.dw) {
if ((dx > 0) != (tp->scroll.dw > 0))
tp->scroll.dw = -tp->scroll.dw;
dw = (dw + 3 * tp->scroll.dw) / 4;
}
if (dw) {
tp->scroll.dw = dw;
*cmds |= 1 << HSCROLL;
}
}
}
void
wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t2;
int dir, dx, dy, centered;
if (tp->ignore == 0) {
if (tp->contacts != 2)
return;
} else if (tp->contacts != 3 || (tp->ignore == input->mt.ptr)) {
return;
}
if (!wstpad_scroll_coords(input, &dx, &dy))
return;
dir = tp->t->dir;
if (!(NORTH(dir) || SOUTH(dir)))
dy = 0;
if (!(EAST(dir) || WEST(dir)))
dx = 0;
if (dx || dy) {
centered = CENTERED(tp->t);
if (IS_MT(tp)) {
t2 = get_2nd_touch(input);
if (t2 == NULL)
return;
dir = t2->dir;
if ((dy > 0 && !NORTH(dir)) || (dy < 0 && !SOUTH(dir)))
return;
if ((dx > 0 && !EAST(dir)) || (dx < 0 && !WEST(dir)))
return;
if (!wstpad_is_stable(input, t2) &&
!(tp->scroll.dz || tp->scroll.dw))
return;
centered |= CENTERED(t2);
}
if (centered) {
wstpad_scroll(tp, dx, dy,
magnitude(input, dx, dy), cmds);
set_freeze_ts(tp, 0, FREEZE_MS);
}
}
}
void
wstpad_edgescroll(struct wsmouseinput *input, u_int *cmds)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t = tp->t;
u_int v_edge, b_edge;
int dx, dy;
if (!wstpad_scroll_coords(input, &dx, &dy) || tp->contacts != 1)
return;
v_edge = (tp->features & WSTPAD_SWAPSIDES) ? L_EDGE : R_EDGE;
b_edge = (tp->features & WSTPAD_HORIZSCROLL) ? B_EDGE : 0;
if ((t->flags & v_edge) == 0)
dy = 0;
if ((t->flags & b_edge) == 0)
dx = 0;
if (dx || dy)
wstpad_scroll(tp, dx, dy, magnitude(input, dx, dy), cmds);
}
static inline u_int
sbtn(struct wstpad *tp, int x, int y)
{
if (y >= tp->edge.bottom)
return (0);
if ((tp->features & WSTPAD_SOFTMBTN)
&& x >= tp->edge.center_left
&& x < tp->edge.center_right)
return (MIDDLEBTN);
return ((x < tp->edge.center ? LEFTBTN : RIGHTBTN) ^ tp->sbtnswap);
}
static inline u_int
top_sbtn(struct wstpad *tp, int x, int y)
{
if (y < tp->edge.top)
return (0);
if (x < tp->edge.center_left)
return (LEFTBTN ^ tp->sbtnswap);
return (x > tp->edge.center_right
? (RIGHTBTN ^ tp->sbtnswap) : MIDDLEBTN);
}
u_int
wstpad_get_sbtn(struct wsmouseinput *input, int top)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t = tp->t;
u_int btn;
btn = 0;
if (tp->contacts) {
btn = top ? top_sbtn(tp, t->x, t->y) : sbtn(tp, t->x, t->y);
/*
* If there is no middle-button area, but contacts in both
* halves of the edge zone, generate a middle-button event:
*/
if (btn && IS_MT(tp) && tp->contacts == 2
&& !top && !(tp->features & WSTPAD_SOFTMBTN)) {
if ((t = get_2nd_touch(input)) != NULL)
btn |= sbtn(tp, t->x, t->y);
if (btn == (LEFTBTN | RIGHTBTN))
btn = MIDDLEBTN;
}
}
return (btn != PRIMARYBTN ? btn : 0);
}
void
wstpad_softbuttons(struct wsmouseinput *input, u_int *cmds, int hdlr)
{
struct wstpad *tp = input->tp;
int top = (hdlr == TOPBUTTON_HDLR);
if (tp->softbutton && PRIMARYBTN_RELEASED(tp)) {
*cmds |= 1 << SOFTBUTTON_UP;
return;
}
if (tp->softbutton == 0 && PRIMARYBTN_CLICKED(tp)) {
tp->softbutton = wstpad_get_sbtn(input, top);
if (tp->softbutton)
*cmds |= 1 << SOFTBUTTON_DOWN;
}
}
/* Check whether the duration of t is within the tap limit. */
int
wstpad_is_tap(struct wstpad *tp, struct tpad_touch *t)
{
struct timespec ts;
timespecsub(&tp->time, &t->orig.time, &ts);
return (timespeccmp(&ts, &tp->tap.maxtime, <));
}
/*
* At least one MT touch must remain close to its origin and end
* in the main area. The same conditions apply to one-finger taps
* on single-touch devices.
*/
void
wstpad_tap_filter(struct wstpad *tp, struct tpad_touch *t)
{
int dx, dy, dist = 0;
if (IS_MT(tp) || tp->tap.contacts == 1) {
dx = abs(t->x - t->orig.x) << 12;
dy = abs(t->y - t->orig.y) * tp->ratio;
dist = (dx >= dy ? dx + 3 * dy / 8 : dy + 3 * dx / 8);
}
tp->tap.valid = (CENTERED(t) && dist <= (tp->tap.maxdist << 12));
}
/*
* Return the oldest touch in the TOUCH_END state, or NULL.
*/
struct tpad_touch *
wstpad_tap_touch(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
struct tpad_touch *s, *t = NULL;
u_int lifted;
int slot;
if (IS_MT(tp)) {
lifted = (input->mt.sync[MTS_TOUCH] & ~input->mt.touches);
FOREACHBIT(lifted, slot) {
s = &tp->tpad_touches[slot];
if (tp->tap.state == TAP_DETECT && !tp->tap.valid)
wstpad_tap_filter(tp, s);
if (t == NULL || timespeccmp(&t->orig.time,
&s->orig.time, >))
t = s;
}
} else {
if (tp->t->state == TOUCH_END) {
t = tp->t;
if (tp->tap.state == TAP_DETECT && !tp->tap.valid)
wstpad_tap_filter(tp, t);
}
}
return (t);
}
/* Determine the "tap button", keep track of whether a touch is masked. */
u_int
wstpad_tap_button(struct wstpad *tp)
{
int n = tp->tap.contacts - tp->contacts - 1;
tp->tap.masked = tp->contacts;
return (n >= 0 && n < TAP_BTNMAP_SIZE ? tp->tap.btnmap[n] : 0);
}
/*
* In the hold/drag state, do not mask touches if no masking was involved
* in the preceding tap gesture.
*/
static inline int
tap_unmask(struct wstpad *tp)
{
return ((tp->tap.button || tp->tap.pending) && tp->tap.masked == 0);
}
/*
* In the default configuration, this handler maps one-, two-, and
* three-finger taps to left-button, right-button, and middle-button
* events, respectively. Setting the LOCKTIME parameter enables
* "locked drags", which are finished by a timeout or a tap-to-end
* gesture.
*/
void
wstpad_tap(struct wsmouseinput *input, u_int *cmds)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t;
int contacts, is_tap, slot, err = 0;
/* Synchronize the button states, if necessary. */
if (input->btn.sync)
*cmds |= 1 << TAPBUTTON_SYNC;
/*
* It is possible to produce a click within the tap timeout.
* Wait for a new touch before generating new button events.
*/
if (PRIMARYBTN_RELEASED(tp))
tp->tap.contacts = 0;
/* Reset the detection state whenever a new touch starts. */
if (tp->contacts > tp->prev_contacts || (IS_MT(tp) &&
(input->mt.touches & input->mt.sync[MTS_TOUCH]))) {
tp->tap.contacts = tp->contacts;
tp->tap.valid = 0;
}
/*
* The filtered number of active touches excludes a masked
* touch if its duration exceeds the tap limit.
*/
contacts = tp->contacts;
if ((slot = ffs(input->mt.ptr_mask) - 1) >= 0
&& !wstpad_is_tap(tp, &tp->tpad_touches[slot])
&& !tap_unmask(tp)) {
contacts--;
}
switch (tp->tap.state) {
case TAP_DETECT:
/* Find the oldest touch in the TOUCH_END state. */
t = wstpad_tap_touch(input);
if (t) {
is_tap = wstpad_is_tap(tp, t);
if (is_tap && contacts == 0) {
if (tp->tap.button)
*cmds |= 1 << TAPBUTTON_UP;
tp->tap.pending = (tp->tap.valid
? wstpad_tap_button(tp) : 0);
if (tp->tap.pending) {
tp->tap.state = TAP_LIFTED;
err = !timeout_add_msec(&tp->tap.to,
CLICKDELAY_MS);
}
} else if (!is_tap && tp->tap.locktime == 0) {
if (contacts == 0 && tp->tap.button)
*cmds |= 1 << TAPBUTTON_UP;
else if (contacts)
tp->tap.state = TAP_IGNORE;
} else if (!is_tap && tp->tap.button) {
if (contacts == 0) {
tp->tap.state = TAP_LOCKED;
err = !timeout_add_msec(&tp->tap.to,
tp->tap.locktime);
} else {
tp->tap.state = TAP_LOCKED_DRAG;
}
}
}
break;
case TAP_IGNORE:
if (contacts == 0) {
tp->tap.state = TAP_DETECT;
if (tp->tap.button)
*cmds |= 1 << TAPBUTTON_UP;
}
break;
case TAP_LIFTED:
if (contacts) {
timeout_del(&tp->tap.to);
tp->tap.state = TAP_DETECT;
if (tp->tap.pending)
*cmds |= 1 << TAPBUTTON_DOWN;
}
break;
case TAP_LOCKED:
if (contacts) {
timeout_del(&tp->tap.to);
tp->tap.state = TAP_LOCKED_DRAG;
}
break;
case TAP_LOCKED_DRAG:
if (contacts == 0) {
t = wstpad_tap_touch(input);
if (t && wstpad_is_tap(tp, t)) {
/* "tap-to-end" */
*cmds |= 1 << TAPBUTTON_UP;
tp->tap.state = TAP_DETECT;
} else {
tp->tap.state = TAP_LOCKED;
err = !timeout_add_msec(&tp->tap.to,
tp->tap.locktime);
}
}
break;
}
if (err) { /* Did timeout_add fail? */
input->sbtn.buttons &= ~tp->tap.button;
input->sbtn.sync |= tp->tap.button;
tp->tap.pending = 0;
tp->tap.button = 0;
tp->tap.state = TAP_DETECT;
}
}
int
wstpad_tap_sync(struct wsmouseinput *input) {
struct wstpad *tp = input->tp;
return ((tp->tap.button & (input->btn.buttons | tp->softbutton)) == 0
|| (tp->tap.button == PRIMARYBTN && tp->softbutton));
}
void
wstpad_tap_timeout(void *p)
{
struct wsmouseinput *input = p;
struct wstpad *tp = input->tp;
struct evq_access evq;
u_int btn;
int s, ev;
s = spltty();
evq.evar = *input->evar;
if (evq.evar != NULL && tp != NULL) {
ev = 0;
if (tp->tap.pending) {
tp->tap.button = tp->tap.pending;
tp->tap.pending = 0;
input->sbtn.buttons |= tp->tap.button;
timeout_add_msec(&tp->tap.to, tp->tap.clicktime);
if (wstpad_tap_sync(input)) {
ev = BTN_DOWN_EV;
btn = ffs(tp->tap.button) - 1;
}
} else {
if (wstpad_tap_sync(input)) {
ev = BTN_UP_EV;
btn = ffs(tp->tap.button) - 1;
}
if (tp->tap.button != tp->softbutton)
input->sbtn.buttons &= ~tp->tap.button;
tp->tap.button = 0;
tp->tap.state = TAP_DETECT;
}
if (ev) {
evq.put = evq.evar->put;
evq.result = EVQ_RESULT_NONE;
getnanotime(&evq.ts);
wsmouse_evq_put(&evq, ev, btn);
wsmouse_evq_put(&evq, SYNC_EV, 0);
if (evq.result == EVQ_RESULT_SUCCESS) {
if (input->flags & LOG_EVENTS) {
wsmouse_log_events(input, &evq);
}
evq.evar->put = evq.put;
WSEVENT_WAKEUP(evq.evar);
} else {
input->sbtn.sync |= tp->tap.button;
}
}
}
splx(s);
}
/*
* Suppress accidental pointer movements after a click on a clickpad.
*/
void
wstpad_click(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
if (tp->contacts == 1 &&
(PRIMARYBTN_CLICKED(tp) || PRIMARYBTN_RELEASED(tp)))
set_freeze_ts(tp, 0, FREEZE_MS);
}
/* Translate the "command" bits into the sync-state of wsmouse. */
void
wstpad_cmds(struct wsmouseinput *input, u_int cmds)
{
struct wstpad *tp = input->tp;
int n;
FOREACHBIT(cmds, n) {
switch (n) {
case CLEAR_MOTION_DELTAS:
input->motion.dx = input->motion.dy = 0;
if (input->motion.dz == 0 && input->motion.dw == 0)
input->motion.sync &= ~SYNC_DELTAS;
continue;
case SOFTBUTTON_DOWN:
input->btn.sync &= ~PRIMARYBTN;
input->sbtn.buttons |= tp->softbutton;
if (tp->softbutton != tp->tap.button)
input->sbtn.sync |= tp->softbutton;
continue;
case SOFTBUTTON_UP:
input->btn.sync &= ~PRIMARYBTN;
if (tp->softbutton != tp->tap.button) {
input->sbtn.buttons &= ~tp->softbutton;
input->sbtn.sync |= tp->softbutton;
}
tp->softbutton = 0;
continue;
case TAPBUTTON_SYNC:
if (tp->tap.button)
input->btn.sync &= ~tp->tap.button;
continue;
case TAPBUTTON_DOWN:
tp->tap.button = tp->tap.pending;
tp->tap.pending = 0;
input->sbtn.buttons |= tp->tap.button;
if (wstpad_tap_sync(input))
input->sbtn.sync |= tp->tap.button;
continue;
case TAPBUTTON_UP:
if (tp->tap.button != tp->softbutton)
input->sbtn.buttons &= ~tp->tap.button;
if (wstpad_tap_sync(input))
input->sbtn.sync |= tp->tap.button;
tp->tap.button = 0;
continue;
case HSCROLL:
input->motion.dw = tp->scroll.dw;
input->motion.sync |= SYNC_DELTAS;
continue;
case VSCROLL:
input->motion.dz = tp->scroll.dz;
input->motion.sync |= SYNC_DELTAS;
continue;
default:
printf("[wstpad] invalid cmd %d\n", n);
break;
}
}
}
/*
* Set the state of touches that have ended. TOUCH_END is a transitional
* state and will be changed to TOUCH_NONE before process_input() returns.
*/
static inline void
clear_touchstates(struct wsmouseinput *input, enum touchstates state)
{
u_int touches;
int slot;
touches = input->mt.sync[MTS_TOUCH] & ~input->mt.touches;
FOREACHBIT(touches, slot)
input->tp->tpad_touches[slot].state = state;
}
void
wstpad_mt_inputs(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t;
int slot, dx, dy;
u_int touches, inactive;
/* TOUCH_BEGIN */
touches = input->mt.touches & input->mt.sync[MTS_TOUCH];
FOREACHBIT(touches, slot) {
t = &tp->tpad_touches[slot];
t->state = TOUCH_BEGIN;
t->x = normalize_abs(&input->filter.h, t->pos->x);
t->y = normalize_abs(&input->filter.v, t->pos->y);
t->orig.x = t->x;
t->orig.y = t->y;
memcpy(&t->orig.time, &tp->time, sizeof(struct timespec));
t->flags = edge_flags(tp, t->x, t->y);
wstpad_set_direction(tp, t, 0, 0);
}
/* TOUCH_UPDATE */
touches = input->mt.touches & input->mt.frame;
if (touches & tp->mtcycle) {
/*
* Slot data may be synchronized separately, in any order,
* or not at all if there is no delta. Identify the touches
* without deltas.
*/
inactive = input->mt.touches & ~tp->mtcycle;
tp->mtcycle = touches;
} else {
inactive = 0;
tp->mtcycle |= touches;
}
touches = input->mt.touches & ~input->mt.sync[MTS_TOUCH];
FOREACHBIT(touches, slot) {
t = &tp->tpad_touches[slot];
t->state = TOUCH_UPDATE;
if ((1 << slot) & input->mt.frame) {
dx = normalize_abs(&input->filter.h, t->pos->x) - t->x;
t->x += dx;
dy = normalize_abs(&input->filter.v, t->pos->y) - t->y;
t->y += dy;
t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
if (wsmouse_hysteresis(input, t->pos))
dx = dy = 0;
wstpad_set_direction(tp, t, dx, dy);
} else if ((1 << slot) & inactive) {
wstpad_set_direction(tp, t, 0, 0);
}
}
clear_touchstates(input, TOUCH_END);
}
/*
* Identify "thumb" contacts in the bottom area. The identification
* has three stages:
* 1. If exactly one of two or more touches is in the bottom area, it
* is masked, which means it does not receive pointer control as long
* as there are alternatives. Once set, the mask will only be cleared
* when the touch is released.
* Tap detection ignores a masked touch if it does not participate in
* a tap gesture.
* 2. If the pointer-controlling touch is moving stably while a masked
* touch in the bottom area is resting, or only moving minimally, the
* pointer mask is copied to tp->ignore. In this stage, the masked
* touch does not block pointer movement, and it is ignored by
* wstpad_f2scroll().
* Decisions are made more or less immediately, there may be errors
* in edge cases. If a fast or long upward movement is detected,
* tp->ignore is cleared. There is no other transition from stage 2
* to scrolling, or vice versa, for a pair of touches.
* 3. If tp->ignore is set and the touch is resting, it is marked as
* thumb, and it will be ignored until it ends.
*/
void
wstpad_mt_masks(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t;
struct position *pos;
u_int mask;
int slot;
tp->ignore &= input->mt.touches;
if (tp->contacts < 2)
return;
if (tp->ignore) {
slot = ffs(tp->ignore) - 1;
t = &tp->tpad_touches[slot];
if (t->flags & THUMB)
return;
if (t->dir < 0 && wstpad_is_stable(input, t)) {
t->flags |= THUMB;
return;
}
/* The edge.low area is a bit larger than the bottom area. */
if (t->y >= tp->edge.low || (NORTH(t->dir) &&
magnitude(input, t->pos->dx, t->pos->dy) >= MAG_MEDIUM))
tp->ignore = 0;
return;
}
if (input->mt.ptr_mask == 0) {
mask = ~0;
FOREACHBIT(input->mt.touches, slot) {
t = &tp->tpad_touches[slot];
if (t->flags & B_EDGE) {
mask &= (1 << slot);
input->mt.ptr_mask = mask;
}
}
}
if ((input->mt.ptr_mask & ~input->mt.ptr)
&& !(tp->scroll.dz || tp->scroll.dw)
&& tp->t->dir >= 0
&& wstpad_is_stable(input, tp->t)) {
slot = ffs(input->mt.ptr_mask) - 1;
t = &tp->tpad_touches[slot];
if (t->y >= tp->edge.low)
return;
if (!wstpad_is_stable(input, t))
return;
/* Default hysteresis limits are low. Make a strict check. */
pos = tp->t->pos;
if (abs(pos->acc_dx) < 3 * input->filter.h.hysteresis
&& abs(pos->acc_dy) < 3 * input->filter.v.hysteresis)
return;
if (t->dir >= 0) {
/* Treat t as thumb if it is slow while tp->t is fast. */
if (magnitude(input, t->pos->dx, t->pos->dy) > MAG_LOW
|| magnitude(input, pos->dx, pos->dy) < MAG_MEDIUM)
return;
}
tp->ignore = input->mt.ptr_mask;
}
}
void
wstpad_touch_inputs(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
struct tpad_touch *t;
int slot, x, y, dx, dy;
tp->btns = input->btn.buttons;
tp->btns_sync = input->btn.sync;
tp->prev_contacts = tp->contacts;
tp->contacts = input->touch.contacts;
if (tp->contacts == 1 &&
((tp->params.f2width &&
input->touch.width >= tp->params.f2width)
|| (tp->params.f2pressure &&
input->touch.pressure >= tp->params.f2pressure)))
tp->contacts = 2;
if (IS_MT(tp)) {
wstpad_mt_inputs(input);
if (input->mt.ptr) {
slot = ffs(input->mt.ptr) - 1;
tp->t = &tp->tpad_touches[slot];
}
wstpad_mt_masks(input);
} else {
t = tp->t;
if (tp->contacts)
t->state = (tp->prev_contacts ?
TOUCH_UPDATE : TOUCH_BEGIN);
else
t->state = (tp->prev_contacts ?
TOUCH_END : TOUCH_NONE);
dx = dy = 0;
x = normalize_abs(&input->filter.h, t->pos->x);
y = normalize_abs(&input->filter.v, t->pos->y);
if (t->state == TOUCH_BEGIN) {
t->x = t->orig.x = x;
t->y = t->orig.y = y;
memcpy(&t->orig.time, &tp->time,
sizeof(struct timespec));
t->flags = edge_flags(tp, x, y);
} else if (input->motion.sync & SYNC_POSITION) {
if (!wsmouse_hysteresis(input, t->pos)) {
dx = x - t->x;
dy = y - t->y;
}
t->x = x;
t->y = y;
t->flags &= (~EDGES | edge_flags(tp, x, y));
}
wstpad_set_direction(tp, t, dx, dy);
}
}
static inline int
t2_ignore(struct wsmouseinput *input)
{
/*
* If there are two touches, do not block pointer movement if they
* perform a click-and-drag action, or if the second touch is
* resting in the bottom area.
*/
return (input->tp->contacts == 2 && ((input->tp->btns & PRIMARYBTN)
|| (input->tp->ignore & ~input->mt.ptr)));
}
void
wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq)
{
struct wstpad *tp = input->tp;
u_int handlers, hdlr, cmds;
memcpy(&tp->time, &evq->ts, sizeof(struct timespec));
wstpad_touch_inputs(input);
cmds = 0;
handlers = tp->handlers;
if (DISABLE(tp))
handlers &= ((1 << TOPBUTTON_HDLR) | (1 << SOFTBUTTON_HDLR));
FOREACHBIT(handlers, hdlr) {
switch (hdlr) {
case SOFTBUTTON_HDLR:
case TOPBUTTON_HDLR:
wstpad_softbuttons(input, &cmds, hdlr);
continue;
case TAP_HDLR:
wstpad_tap(input, &cmds);
continue;
case F2SCROLL_HDLR:
wstpad_f2scroll(input, &cmds);
continue;
case EDGESCROLL_HDLR:
wstpad_edgescroll(input, &cmds);
continue;
case CLICK_HDLR:
wstpad_click(input);
continue;
}
}
/* Check whether pointer movement should be blocked. */
if (input->motion.dx || input->motion.dy) {
if (DISABLE(tp)
|| (tp->t->flags & tp->freeze)
|| timespeccmp(&tp->time, &tp->freeze_ts, <)
|| (tp->contacts > 1 && !t2_ignore(input))) {
cmds |= 1 << CLEAR_MOTION_DELTAS;
}
}
wstpad_cmds(input, cmds);
if (IS_MT(tp))
clear_touchstates(input, TOUCH_NONE);
}
/*
* Try to determine the average interval between two updates. Various
* conditions are checked in order to ensure that only valid samples enter
* into the calculation. Above all, it is restricted to motion events
* occurring when there is only one contact. MT devices may need more than
* one packet to transmit their state if there are multiple touches, and
* the update frequency may be higher in this case.
*/
void
wstpad_track_interval(struct wsmouseinput *input, struct timespec *time)
{
static const struct timespec limit = { 0, 30 * 1000000L };
struct timespec ts;
int samples;
if (input->motion.sync == 0
|| (input->touch.sync & SYNC_CONTACTS)
|| (input->touch.contacts > 1)) {
input->intv.track = 0;
return;
}
if (input->intv.track) {
timespecsub(time, &input->intv.ts, &ts);
if (timespeccmp(&ts, &limit, <)) {
/* The unit of the sum is 4096 nanoseconds. */
input->intv.sum += ts.tv_nsec >> 12;
samples = ++input->intv.samples;
/*
* Make the first calculation quickly and later
* a more reliable one:
*/
if (samples == 8) {
input->intv.avg = input->intv.sum << 9;
wstpad_init_deceleration(input);
} else if (samples == 128) {
input->intv.avg = input->intv.sum << 5;
wstpad_init_deceleration(input);
input->intv.samples = 0;
input->intv.sum = 0;
input->flags &= ~TRACK_INTERVAL;
}
}
}
memcpy(&input->intv.ts, time, sizeof(struct timespec));
input->intv.track = 1;
}
/*
* The default acceleration options of X don't work convincingly with
* touchpads (the synaptics driver installs its own "acceleration
* profile" and callback function). As a preliminary workaround, this
* filter applies a simple deceleration scheme to small deltas, based
* on the "magnitude" of the delta pair. A magnitude of 8 corresponds,
* roughly, to a speed of (filter.dclr / 12.5) device units per milli-
* second. If its magnitude is smaller than 7 a delta will be downscaled
* by the factor 2/8, deltas with magnitudes from 7 to 11 by factors
* ranging from 3/8 to 7/8.
*/
int
wstpad_decelerate(struct wsmouseinput *input, int *dx, int *dy)
{
int mag, n, h, v;
mag = magnitude(input, *dx, *dy);
/* Don't change deceleration levels abruptly. */
mag = (mag + 7 * input->filter.mag) / 8;
/* Don't use arbitrarily high values. */
input->filter.mag = imin(mag, 24 << 12);
n = imax((mag >> 12) - 4, 2);
if (n < 8) {
/* Scale by (n / 8). */
h = *dx * n + input->filter.h.dclr_rmdr;
v = *dy * n + input->filter.v.dclr_rmdr;
input->filter.h.dclr_rmdr = (h >= 0 ? h & 7 : -(-h & 7));
input->filter.v.dclr_rmdr = (v >= 0 ? v & 7 : -(-v & 7));
*dx = h / 8;
*dy = v / 8;
return (1);
}
return (0);
}
void
wstpad_filter(struct wsmouseinput *input)
{
struct axis_filter *h = &input->filter.h;
struct axis_filter *v = &input->filter.v;
struct position *pos = &input->motion.pos;
int strength = input->filter.mode & 7;
int dx, dy;
if (!(input->motion.sync & SYNC_POSITION)
|| (h->dmax && (abs(pos->dx) > h->dmax))
|| (v->dmax && (abs(pos->dy) > v->dmax))) {
dx = dy = 0;
} else {
dx = pos->dx;
dy = pos->dy;
}
if (wsmouse_hysteresis(input, pos))
dx = dy = 0;
if (input->filter.dclr && wstpad_decelerate(input, &dx, &dy))
/* Strong smoothing may hamper the precision at low speeds. */
strength = imin(strength, 2);
if (strength) {
if ((input->touch.sync & SYNC_CONTACTS)
|| input->mt.ptr != input->mt.prev_ptr) {
h->avg = v->avg = 0;
}
/* Use a weighted decaying average for smoothing. */
dx = dx * (8 - strength) + h->avg * strength + h->avg_rmdr;
dy = dy * (8 - strength) + v->avg * strength + v->avg_rmdr;
h->avg_rmdr = (dx >= 0 ? dx & 7 : -(-dx & 7));
v->avg_rmdr = (dy >= 0 ? dy & 7 : -(-dy & 7));
dx = h->avg = dx / 8;
dy = v->avg = dy / 8;
}
input->motion.dx = dx;
input->motion.dy = dy;
}
/*
* Compatibility-mode conversions. wstpad_filter transforms and filters
* the coordinate inputs, extended functionality is provided by
* wstpad_process_input.
*/
void
wstpad_compat_convert(struct wsmouseinput *input, struct evq_access *evq)
{
if (input->flags & TRACK_INTERVAL)
wstpad_track_interval(input, &evq->ts);
wstpad_filter(input);
if ((input->motion.dx || input->motion.dy)
&& !(input->motion.sync & SYNC_DELTAS)) {
input->motion.dz = input->motion.dw = 0;
input->motion.sync |= SYNC_DELTAS;
}
if (input->tp != NULL)
wstpad_process_input(input, evq);
input->motion.sync &= ~SYNC_POSITION;
input->touch.sync = 0;
}
int
wstpad_init(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
int i, slots;
if (tp != NULL)
return (0);
input->tp = tp = malloc(sizeof(struct wstpad),
M_DEVBUF, M_WAITOK | M_ZERO);
if (tp == NULL)
return (-1);
slots = imax(input->mt.num_slots, 1);
tp->tpad_touches = malloc(slots * sizeof(struct tpad_touch),
M_DEVBUF, M_WAITOK | M_ZERO);
if (tp->tpad_touches == NULL) {
free(tp, M_DEVBUF, sizeof(struct wstpad));
return (-1);
}
tp->t = &tp->tpad_touches[0];
if (input->mt.num_slots) {
tp->features |= WSTPAD_MT;
for (i = 0; i < input->mt.num_slots; i++)
tp->tpad_touches[i].pos = &input->mt.slots[i].pos;
} else {
tp->t->pos = &input->motion.pos;
}
timeout_set(&tp->tap.to, wstpad_tap_timeout, input);
tp->ratio = input->filter.ratio;
return (0);
}
/*
* Integer square root (Halleck's method)
*
* An adaption of code from John B. Halleck (from
* http://www.cc.utah.edu/~nahaj/factoring/code.html). This version is
* used and published under the OpenBSD license terms with his permission.
*
* Cf. also Martin Guy's "Square root by abacus" method.
*/
static inline u_int
isqrt(u_int n)
{
u_int root, sqbit;
root = 0;
sqbit = 1 << (sizeof(u_int) * 8 - 2);
while (sqbit) {
if (n >= (sqbit | root)) {
n -= (sqbit | root);
root = (root >> 1) | sqbit;
} else {
root >>= 1;
}
sqbit >>= 2;
}
return (root);
}
void
wstpad_init_deceleration(struct wsmouseinput *input)
{
int n, dclr;
if ((dclr = input->filter.dclr) == 0)
return;
dclr = imax(dclr, 4);
/*
* For a standard update rate of about 80Hz, (dclr) units
* will be mapped to a magnitude of 8. If the average rate
* is significantly higher or lower, adjust the coefficient
* accordingly:
*/
if (input->intv.avg == 0) {
n = 8;
} else {
n = 8 * 13000000 / input->intv.avg;
n = imax(imin(n, 32), 4);
}
input->filter.h.mag_scale = (n << 12) / dclr;
input->filter.v.mag_scale = (input->filter.ratio ?
n * input->filter.ratio : n << 12) / dclr;
input->filter.h.dclr_rmdr = 0;
input->filter.v.dclr_rmdr = 0;
input->flags |= TRACK_INTERVAL;
}
int
wstpad_configure(struct wsmouseinput *input)
{
struct wstpad *tp;
int width, height, diag, offset, h_res, v_res, h_unit, v_unit, i;
width = abs(input->hw.x_max - input->hw.x_min);
height = abs(input->hw.y_max - input->hw.y_min);
if (width == 0 || height == 0)
return (-1); /* We can't do anything. */
if (input->tp == NULL && wstpad_init(input))
return (-1);
tp = input->tp;
if (!(input->flags & CONFIGURED)) {
/*
* The filter parameters are derived from the length of the
* diagonal in device units, with some magic constants which
* are partly adapted from libinput or synaptics code, or are
* based on tests and guess work. The absolute resolution
* values might not be reliable, but if they are present the
* settings are adapted to the ratio.
*/
h_res = input->hw.h_res;
v_res = input->hw.v_res;
if (h_res == 0 || v_res == 0)
h_res = v_res = 1;
diag = isqrt(width * width + height * height);
input->filter.h.scale = (imin(920, diag) << 12) / diag;
input->filter.v.scale = input->filter.h.scale * h_res / v_res;
h_unit = imax(diag / 280, 3);
v_unit = imax((h_unit * v_res + h_res / 2) / h_res, 3);
input->filter.h.hysteresis = h_unit;
input->filter.v.hysteresis = v_unit;
input->filter.mode = FILTER_MODE_DEFAULT;
input->filter.dclr = h_unit - h_unit / 5;
wstpad_init_deceleration(input);
tp->features &= (WSTPAD_MT | WSTPAD_DISABLE);
if (input->hw.contacts_max != 1)
tp->features |= WSTPAD_TWOFINGERSCROLL;
else
tp->features |= WSTPAD_EDGESCROLL;
if (input->hw.hw_type == WSMOUSEHW_CLICKPAD) {
if (input->hw.type == WSMOUSE_TYPE_SYNAP_SBTN) {
tp->features |= WSTPAD_TOPBUTTONS;
} else {
tp->features |= WSTPAD_SOFTBUTTONS;
tp->features |= WSTPAD_SOFTMBTN;
}
}
tp->params.left_edge = V_EDGE_RATIO_DEFAULT;
tp->params.right_edge = V_EDGE_RATIO_DEFAULT;
tp->params.bottom_edge = ((tp->features & WSTPAD_SOFTBUTTONS)
? B_EDGE_RATIO_DEFAULT : 0);
tp->params.top_edge = ((tp->features & WSTPAD_TOPBUTTONS)
? T_EDGE_RATIO_DEFAULT : 0);
tp->params.center_width = CENTER_RATIO_DEFAULT;
tp->tap.maxtime.tv_nsec = TAP_MAXTIME_DEFAULT * 1000000;
tp->tap.clicktime = TAP_CLICKTIME_DEFAULT;
tp->tap.locktime = TAP_LOCKTIME_DEFAULT;
tp->scroll.hdist = 4 * h_unit;
tp->scroll.vdist = 4 * v_unit;
tp->tap.maxdist = 4 * h_unit;
}
/* A touch with a flag set in this mask does not move the pointer. */
tp->freeze = EDGES;
offset = width * tp->params.left_edge / 4096;
tp->edge.left = (offset ? input->hw.x_min + offset : INT_MIN);
offset = width * tp->params.right_edge / 4096;
tp->edge.right = (offset ? input->hw.x_max - offset : INT_MAX);
offset = height * tp->params.bottom_edge / 4096;
tp->edge.bottom = (offset ? input->hw.y_min + offset : INT_MIN);
tp->edge.low = tp->edge.bottom + offset / 2;
offset = height * tp->params.top_edge / 4096;
tp->edge.top = (offset ? input->hw.y_max - offset : INT_MAX);
offset = width * abs(tp->params.center_width) / 8192;
tp->edge.center = input->hw.x_min + width / 2;
tp->edge.center_left = tp->edge.center - offset;
tp->edge.center_right = tp->edge.center + offset;
tp->handlers = 0;
if (tp->features & WSTPAD_SOFTBUTTONS)
tp->handlers |= 1 << SOFTBUTTON_HDLR;
if (tp->features & WSTPAD_TOPBUTTONS)
tp->handlers |= 1 << TOPBUTTON_HDLR;
if (tp->features & WSTPAD_TWOFINGERSCROLL)
tp->handlers |= 1 << F2SCROLL_HDLR;
else if (tp->features & WSTPAD_EDGESCROLL)
tp->handlers |= 1 << EDGESCROLL_HDLR;
for (i = 0; i < TAP_BTNMAP_SIZE; i++) {
if (tp->tap.btnmap[i] == 0)
continue;
tp->tap.clicktime = imin(imax(tp->tap.clicktime, 80), 350);
if (tp->tap.locktime)
tp->tap.locktime =
imin(imax(tp->tap.locktime, 150), 5000);
tp->handlers |= 1 << TAP_HDLR;
break;
}
if (input->hw.hw_type == WSMOUSEHW_CLICKPAD)
tp->handlers |= 1 << CLICK_HDLR;
tp->sbtnswap = ((tp->features & WSTPAD_SWAPSIDES)
? (LEFTBTN | RIGHTBTN) : 0);
return (0);
}
void
wstpad_reset(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
if (tp != NULL) {
timeout_del(&tp->tap.to);
tp->tap.state = TAP_DETECT;
}
if (input->sbtn.buttons) {
input->sbtn.sync = input->sbtn.buttons;
input->sbtn.buttons = 0;
}
}
void
wstpad_cleanup(struct wsmouseinput *input)
{
struct wstpad *tp = input->tp;
int slots;
timeout_del(&tp->tap.to);
slots = imax(input->mt.num_slots, 1);
free(tp->tpad_touches, M_DEVBUF, slots * sizeof(struct tpad_touch));
free(tp, M_DEVBUF, sizeof(struct wstpad));
input->tp = NULL;
}
int
wstpad_set_param(struct wsmouseinput *input, int key, int val)
{
struct wstpad *tp = input->tp;
u_int flag;
if (tp == NULL)
return (EINVAL);
switch (key) {
case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_DISABLE:
switch (key) {
case WSMOUSECFG_SOFTBUTTONS:
flag = WSTPAD_SOFTBUTTONS;
break;
case WSMOUSECFG_SOFTMBTN:
flag = WSTPAD_SOFTMBTN;
break;
case WSMOUSECFG_TOPBUTTONS:
flag = WSTPAD_TOPBUTTONS;
break;
case WSMOUSECFG_TWOFINGERSCROLL:
flag = WSTPAD_TWOFINGERSCROLL;
break;
case WSMOUSECFG_EDGESCROLL:
flag = WSTPAD_EDGESCROLL;
break;
case WSMOUSECFG_HORIZSCROLL:
flag = WSTPAD_HORIZSCROLL;
break;
case WSMOUSECFG_SWAPSIDES:
flag = WSTPAD_SWAPSIDES;
break;
case WSMOUSECFG_DISABLE:
flag = WSTPAD_DISABLE;
break;
}
if (val)
tp->features |= flag;
else
tp->features &= ~flag;
break;
case WSMOUSECFG_LEFT_EDGE:
tp->params.left_edge = val;
break;
case WSMOUSECFG_RIGHT_EDGE:
tp->params.right_edge = val;
break;
case WSMOUSECFG_TOP_EDGE:
tp->params.top_edge = val;
break;
case WSMOUSECFG_BOTTOM_EDGE:
tp->params.bottom_edge = val;
break;
case WSMOUSECFG_CENTERWIDTH:
tp->params.center_width = val;
break;
case WSMOUSECFG_HORIZSCROLLDIST:
tp->scroll.hdist = val;
break;
case WSMOUSECFG_VERTSCROLLDIST:
tp->scroll.vdist = val;
break;
case WSMOUSECFG_F2WIDTH:
tp->params.f2width = val;
break;
case WSMOUSECFG_F2PRESSURE:
tp->params.f2pressure = val;
break;
case WSMOUSECFG_TAP_MAXTIME:
tp->tap.maxtime.tv_nsec = imin(val, 999) * 1000000;
break;
case WSMOUSECFG_TAP_CLICKTIME:
tp->tap.clicktime = val;
break;
case WSMOUSECFG_TAP_LOCKTIME:
tp->tap.locktime = val;
break;
case WSMOUSECFG_TAP_ONE_BTNMAP:
tp->tap.btnmap[0] = BTNMASK(val);
break;
case WSMOUSECFG_TAP_TWO_BTNMAP:
tp->tap.btnmap[1] = BTNMASK(val);
break;
case WSMOUSECFG_TAP_THREE_BTNMAP:
tp->tap.btnmap[2] = BTNMASK(val);
break;
default:
return (ENOTSUP);
}
return (0);
}
int
wstpad_get_param(struct wsmouseinput *input, int key, int *pval)
{
struct wstpad *tp = input->tp;
u_int flag;
if (tp == NULL)
return (EINVAL);
switch (key) {
case WSMOUSECFG_SOFTBUTTONS ... WSMOUSECFG_DISABLE:
switch (key) {
case WSMOUSECFG_SOFTBUTTONS:
flag = WSTPAD_SOFTBUTTONS;
break;
case WSMOUSECFG_SOFTMBTN:
flag = WSTPAD_SOFTMBTN;
break;
case WSMOUSECFG_TOPBUTTONS:
flag = WSTPAD_TOPBUTTONS;
break;
case WSMOUSECFG_TWOFINGERSCROLL:
flag = WSTPAD_TWOFINGERSCROLL;
break;
case WSMOUSECFG_EDGESCROLL:
flag = WSTPAD_EDGESCROLL;
break;
case WSMOUSECFG_HORIZSCROLL:
flag = WSTPAD_HORIZSCROLL;
break;
case WSMOUSECFG_SWAPSIDES:
flag = WSTPAD_SWAPSIDES;
break;
case WSMOUSECFG_DISABLE:
flag = WSTPAD_DISABLE;
break;
}
*pval = !!(tp->features & flag);
break;
case WSMOUSECFG_LEFT_EDGE:
*pval = tp->params.left_edge;
break;
case WSMOUSECFG_RIGHT_EDGE:
*pval = tp->params.right_edge;
break;
case WSMOUSECFG_TOP_EDGE:
*pval = tp->params.top_edge;
break;
case WSMOUSECFG_BOTTOM_EDGE:
*pval = tp->params.bottom_edge;
break;
case WSMOUSECFG_CENTERWIDTH:
*pval = tp->params.center_width;
break;
case WSMOUSECFG_HORIZSCROLLDIST:
*pval = tp->scroll.hdist;
break;
case WSMOUSECFG_VERTSCROLLDIST:
*pval = tp->scroll.vdist;
break;
case WSMOUSECFG_F2WIDTH:
*pval = tp->params.f2width;
break;
case WSMOUSECFG_F2PRESSURE:
*pval = tp->params.f2pressure;
break;
case WSMOUSECFG_TAP_MAXTIME:
*pval = tp->tap.maxtime.tv_nsec / 1000000;
break;
case WSMOUSECFG_TAP_CLICKTIME:
*pval = tp->tap.clicktime;
break;
case WSMOUSECFG_TAP_LOCKTIME:
*pval = tp->tap.locktime;
break;
case WSMOUSECFG_TAP_ONE_BTNMAP:
*pval = ffs(tp->tap.btnmap[0]);
break;
case WSMOUSECFG_TAP_TWO_BTNMAP:
*pval = ffs(tp->tap.btnmap[1]);
break;
case WSMOUSECFG_TAP_THREE_BTNMAP:
*pval = ffs(tp->tap.btnmap[2]);
break;
default:
return (ENOTSUP);
}
return (0);
}
39
38
3
39
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/* $OpenBSD: tcp_debug.c,v 1.30 2022/02/22 01:15:02 guenther Exp $ */
/* $NetBSD: tcp_debug.c,v 1.10 1996/02/13 23:43:36 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#ifdef TCPDEBUG
/* load symbolic names */
#define PRUREQUESTS
#define TCPSTATES
#define TCPTIMERS
#define TANAMES
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>
#include <netinet/tcp_fsm.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif /* INET6 */
#ifdef TCPDEBUG
int tcpconsdebug = 0;
#endif
struct tcp_debug tcp_debug[TCP_NDEBUG];
int tcp_debx;
/*
* Tcp debug routines
*/
void
tcp_trace(short act, short ostate, struct tcpcb *tp, struct tcpcb *otp,
caddr_t headers, int req, int len)
{
#ifdef TCPDEBUG
tcp_seq seq, ack;
int flags;
#endif
int pf = PF_UNSPEC;
struct tcp_debug *td = &tcp_debug[tcp_debx++];
struct tcpiphdr *ti = (struct tcpiphdr *)headers;
struct tcpipv6hdr *ti6 = (struct tcpipv6hdr *)headers;
struct tcphdr *th;
if (tcp_debx == TCP_NDEBUG)
tcp_debx = 0;
td->td_time = iptime();
td->td_act = act;
td->td_ostate = ostate;
td->td_tcb = (caddr_t)otp;
if (tp) {
pf = tp->pf;
td->td_cb = *tp;
} else
bzero((caddr_t)&td->td_cb, sizeof (*tp));
bzero(&td->td_ti6, sizeof(struct tcpipv6hdr));
bzero(&td->td_ti, sizeof(struct tcpiphdr));
if (headers) {
/* The address family may be in tcpcb or ip header. */
if (pf == PF_UNSPEC) {
switch (ti6->ti6_i.ip6_vfc & IPV6_VERSION_MASK) {
#ifdef INET6
case IPV6_VERSION:
pf = PF_INET6;
break;
#endif /* INET6 */
case IPVERSION:
pf = PF_INET;
break;
}
}
switch (pf) {
#ifdef INET6
case PF_INET6:
th = &ti6->ti6_t;
td->td_ti6 = *ti6;
td->td_ti6.ti6_plen = len;
break;
#endif /* INET6 */
case PF_INET:
th = &ti->ti_t;
td->td_ti = *ti;
td->td_ti.ti_len = len;
break;
default:
headers = NULL;
break;
}
}
td->td_req = req;
#ifdef TCPDEBUG
if (tcpconsdebug == 0)
return;
if (otp)
printf("%p %s:", otp, tcpstates[ostate]);
else
printf("???????? ");
printf("%s ", tanames[act]);
switch (act) {
case TA_INPUT:
case TA_OUTPUT:
case TA_DROP:
if (headers == NULL)
break;
seq = th->th_seq;
ack = th->th_ack;
if (act == TA_OUTPUT) {
seq = ntohl(seq);
ack = ntohl(ack);
}
if (len)
printf("[%x..%x)", seq, seq+len);
else
printf("%x", seq);
printf("@%x, urp=%x", ack, th->th_urp);
flags = th->th_flags;
if (flags) {
char *cp = "<";
#define pf(f) { if (th->th_flags&TH_##f) { printf("%s%s", cp, #f); cp = ","; } }
pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG);
printf(">");
}
break;
case TA_USER:
printf("%s", prurequests[req]);
break;
case TA_TIMER:
printf("%s", tcptimers[req]);
break;
}
if (tp)
printf(" -> %s", tcpstates[tp->t_state]);
/* print out internal state of tp !?! */
printf("\n");
if (tp == NULL)
return;
printf("\trcv_(nxt,wnd,up) (%x,%lx,%x) snd_(una,nxt,max) (%x,%x,%x)\n",
tp->rcv_nxt, tp->rcv_wnd, tp->rcv_up, tp->snd_una, tp->snd_nxt,
tp->snd_max);
printf("\tsnd_(wl1,wl2,wnd) (%x,%x,%lx)\n",
tp->snd_wl1, tp->snd_wl2, tp->snd_wnd);
#endif /* TCPDEBUG */
}
46
46
47
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/* $OpenBSD: strlcat.c,v 1.9 2019/01/25 00:19:26 millert Exp $ */
/*
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <lib/libkern/libkern.h>
/*
* Appends src to string dst of size dsize (unlike strncat, dsize is the
* full size of dst, not space left). At most dsize-1 characters
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t dsize)
{
const char *odst = dst;
const char *osrc = src;
size_t n = dsize;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end. */
while (n-- != 0 && *dst != '\0')
dst++;
dlen = dst - odst;
n = dsize - dlen;
if (n-- == 0)
return(dlen + strlen(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
src++;
}
*dst = '\0';
return(dlen + (src - osrc)); /* count does not include NUL */
}
open /syzkaller/managers/main/kernel/machine/pio.h: no such file or directory
124
124
19
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/* $OpenBSD: conf.c,v 1.76 2022/09/02 20:06:55 miod Exp $ */
/*
* Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/vnode.h>
#include <machine/conf.h>
#include "wd.h"
bdev_decl(wd);
#include "fd.h"
bdev_decl(fd);
#include "sd.h"
#include "st.h"
#include "cd.h"
#include "uk.h"
#include "vnd.h"
#include "rd.h"
struct bdevsw bdevsw[] =
{
bdev_disk_init(NWD,wd), /* 0: ST506/ESDI/IDE disk */
bdev_swap_init(1,sw), /* 1: swap pseudo-device */
bdev_disk_init(NFD,fd), /* 2: floppy diskette */
bdev_notdef(), /* 3 */
bdev_disk_init(NSD,sd), /* 4: SCSI disk */
bdev_notdef(), /* 5: was: SCSI tape */
bdev_disk_init(NCD,cd), /* 6: SCSI CD-ROM */
bdev_notdef(), /* 7 */
bdev_notdef(), /* 8 */
bdev_notdef(), /* 9 */
bdev_notdef(), /* 10 */
bdev_notdef(), /* 11 */
bdev_notdef(), /* 12 */
bdev_notdef(), /* 13 */
bdev_disk_init(NVND,vnd), /* 14: vnode disk driver */
bdev_notdef(), /* 15: was: Sony CD-ROM */
bdev_notdef(), /* 16: was: concatenated disk driver */
bdev_disk_init(NRD,rd), /* 17: ram disk driver */
bdev_notdef(), /* 18 */
bdev_notdef(), /* 19 was: RAIDframe disk driver */
};
int nblkdev = nitems(bdevsw);
/* open, close, ioctl */
#define cdev_ocis_init(c,n) { \
dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
(dev_type_write((*))) enodev, dev_init(c,n,ioctl), \
(dev_type_stop((*))) enodev, 0, \
(dev_type_mmap((*))) enodev, 0, 0, seltrue_kqfilter }
/* open, close, read */
#define cdev_nvram_init(c,n) { \
dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \
(dev_type_write((*))) enodev, (dev_type_ioctl((*))) enodev, \
(dev_type_stop((*))) enodev, 0, \
(dev_type_mmap((*))) enodev, 0, 0, seltrue_kqfilter }
/* open, close, ioctl */
#define cdev_vmm_init(c,n) { \
dev_init(c,n,open), dev_init(c,n,close), \
(dev_type_read((*))) enodev, \
(dev_type_write((*))) enodev, \
dev_init(c,n,ioctl), \
(dev_type_stop((*))) enodev, 0, \
(dev_type_mmap((*))) enodev, 0, 0, seltrue_kqfilter }
#define mmread mmrw
#define mmwrite mmrw
cdev_decl(mm);
cdev_decl(wd);
#include "bio.h"
#include "pty.h"
#include "com.h"
cdev_decl(com);
cdev_decl(fd);
#include "lpt.h"
cdev_decl(lpt);
#include "ch.h"
#include "bpfilter.h"
#if 0
#include "pcmcia.h"
cdev_decl(pcmcia);
#endif
#include "spkr.h"
cdev_decl(spkr);
#if 0 /* old (non-wsmouse) drivers */
#include "mms.h"
cdev_decl(mms);
#include "lms.h"
cdev_decl(lms);
#include "opms.h"
cdev_decl(pms);
#endif
#include "cy.h"
cdev_decl(cy);
#include "tun.h"
#include "audio.h"
#include "video.h"
#include "midi.h"
#include "acpi.h"
#include "pctr.h"
#include "bktr.h"
#include "ksyms.h"
#include "kstat.h"
#include "usb.h"
#include "uhid.h"
#include "fido.h"
#include "ujoy.h"
#include "ugen.h"
#include "ulpt.h"
#include "ucom.h"
#include "cz.h"
cdev_decl(cztty);
#include "radio.h"
#include "nvram.h"
cdev_decl(nvram);
#include "drm.h"
cdev_decl(drm);
#include "viocon.h"
cdev_decl(viocon);
#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmouse.h"
#include "wsmux.h"
#include "kcov.h"
#ifdef USER_PCICONF
#include "pci.h"
cdev_decl(pci);
#endif
#include "dt.h"
#include "pf.h"
#include "hotplug.h"
#include "gpio.h"
#include "vscsi.h"
#include "pppx.h"
#include "fuse.h"
#include "pvbus.h"
#include "ipmi.h"
struct cdevsw cdevsw[] =
{
cdev_cn_init(1,cn), /* 0: virtual console */
cdev_ctty_init(1,ctty), /* 1: controlling terminal */
cdev_mm_init(1,mm), /* 2: /dev/{null,mem,kmem,...} */
cdev_disk_init(NWD,wd), /* 3: ST506/ESDI/IDE disk */
cdev_notdef(), /* 4 was /dev/drum */
cdev_tty_init(NPTY,pts), /* 5: pseudo-tty slave */
cdev_ptc_init(NPTY,ptc), /* 6: pseudo-tty master */
cdev_log_init(1,log), /* 7: /dev/klog */
cdev_tty_init(NCOM,com), /* 8: serial port */
cdev_disk_init(NFD,fd), /* 9: floppy disk */
cdev_vmm_init(NVMM,vmm), /* 10 vmm */
cdev_notdef(), /* 11: Sony CD-ROM */
cdev_wsdisplay_init(NWSDISPLAY, /* 12: frame buffers, etc. */
wsdisplay),
cdev_disk_init(NSD,sd), /* 13: SCSI disk */
cdev_tape_init(NST,st), /* 14: SCSI tape */
cdev_disk_init(NCD,cd), /* 15: SCSI CD-ROM */
cdev_lpt_init(NLPT,lpt), /* 16: parallel printer */
cdev_ch_init(NCH,ch), /* 17: SCSI autochanger */
cdev_notdef(), /* 18: was: concatenated disk driver */
cdev_kcov_init(NKCOV,kcov), /* 19: kcov */
cdev_uk_init(NUK,uk), /* 20: unknown SCSI */
cdev_notdef(), /* 21 */
cdev_fd_init(1,filedesc), /* 22: file descriptor pseudo-device */
cdev_bpf_init(NBPFILTER,bpf), /* 23: Berkeley packet filter */
cdev_notdef(), /* 24 */
#if 0
cdev_ocis_init(NPCMCIA,pcmcia), /* 25: PCMCIA Bus */
#else
cdev_notdef(), /* 25 */
#endif
cdev_notdef(), /* 26 */
cdev_spkr_init(NSPKR,spkr), /* 27: PC speaker */
cdev_notdef(), /* 28 was LKM */
cdev_notdef(), /* 29 */
cdev_dt_init(NDT,dt), /* 30: dynamic tracer */
cdev_notdef(), /* 31 */
cdev_notdef(), /* 32 */
cdev_notdef(), /* 33 */
cdev_notdef(), /* 34 */
cdev_notdef(), /* 35: Microsoft mouse */
cdev_notdef(), /* 36: Logitech mouse */
cdev_notdef(), /* 37: Extended PS/2 mouse */
cdev_tty_init(NCY,cy), /* 38: Cyclom serial port */
cdev_notdef(), /* 39: Mitsumi CD-ROM */
cdev_tun_init(NTUN,tun), /* 40: network tunnel */
cdev_disk_init(NVND,vnd), /* 41: vnode disk driver */
cdev_audio_init(NAUDIO,audio), /* 42: generic audio I/O */
cdev_notdef(), /* 43 */
cdev_video_init(NVIDEO,video), /* 44: generic video I/O */
cdev_random_init(1,random), /* 45: random data source */
cdev_ocis_init(NPCTR,pctr), /* 46: performance counters */
cdev_disk_init(NRD,rd), /* 47: ram disk driver */
cdev_notdef(), /* 48 */
cdev_bktr_init(NBKTR,bktr), /* 49: Bt848 video capture device */
cdev_ksyms_init(NKSYMS,ksyms), /* 50: Kernel symbols device */
cdev_kstat_init(NKSTAT,kstat), /* 51: Kernel statistics */
cdev_midi_init(NMIDI,midi), /* 52: MIDI I/O */
cdev_notdef(), /* 53 was: sequencer I/O */
cdev_notdef(), /* 54 was: RAIDframe disk driver */
cdev_notdef(), /* 55: */
/* The following slots are reserved for isdn4bsd. */
cdev_notdef(), /* 56: i4b main device */
cdev_notdef(), /* 57: i4b control device */
cdev_notdef(), /* 58: i4b raw b-channel access */
cdev_notdef(), /* 59: i4b trace device */
cdev_notdef(), /* 60: i4b phone device */
/* End of reserved slots for isdn4bsd. */
cdev_usb_init(NUSB,usb), /* 61: USB controller */
cdev_usbdev_init(NUHID,uhid), /* 62: USB generic HID */
cdev_usbdev_init(NUGEN,ugen), /* 63: USB generic driver */
cdev_ulpt_init(NULPT,ulpt), /* 64: USB printers */
cdev_notdef(), /* 65: urio */
cdev_tty_init(NUCOM,ucom), /* 66: USB tty */
cdev_mouse_init(NWSKBD, wskbd), /* 67: keyboards */
cdev_mouse_init(NWSMOUSE, /* 68: mice */
wsmouse),
cdev_mouse_init(NWSMUX, wsmux), /* 69: ws multiplexor */
cdev_notdef(), /* 70: was: /dev/crypto */
cdev_tty_init(NCZ,cztty), /* 71: Cyclades-Z serial port */
#ifdef USER_PCICONF
cdev_pci_init(NPCI,pci), /* 72: PCI user */
#else
cdev_notdef(),
#endif
cdev_pf_init(NPF,pf), /* 73: packet filter */
cdev_notdef(), /* 74: ALTQ (deprecated) */
cdev_notdef(),
cdev_radio_init(NRADIO, radio), /* 76: generic radio I/O */
cdev_notdef(), /* 77: was USB scanners */
cdev_notdef(), /* 78 */
cdev_bio_init(NBIO,bio), /* 79: ioctl tunnel */
cdev_notdef(), /* 80 */
cdev_ptm_init(NPTY,ptm), /* 81: pseudo-tty ptm device */
cdev_hotplug_init(NHOTPLUG,hotplug), /* 82: devices hot plugging */
cdev_acpi_init(NACPI,acpi), /* 83: ACPI */
cdev_notdef(),
cdev_nvram_init(NNVRAM,nvram), /* 85: NVRAM interface */
cdev_notdef(), /* 86 */
cdev_drm_init(NDRM,drm), /* 87: drm */
cdev_gpio_init(NGPIO,gpio), /* 88: gpio */
cdev_vscsi_init(NVSCSI,vscsi), /* 89: vscsi */
cdev_disk_init(1,diskmap), /* 90: disk mapper */
cdev_pppx_init(NPPPX,pppx), /* 91: pppx */
cdev_fuse_init(NFUSE,fuse), /* 92: fuse */
cdev_tun_init(NTUN,tap), /* 93: Ethernet network tunnel */
cdev_tty_init(NVIOCON,viocon), /* 94: virtio console */
cdev_pvbus_init(NPVBUS,pvbus), /* 95: pvbus(4) control interface */
cdev_ipmi_init(NIPMI,ipmi), /* 96: ipmi */
cdev_notdef(), /* 97: was switch(4) */
cdev_fido_init(NFIDO,fido), /* 98: FIDO/U2F security keys */
cdev_pppx_init(NPPPX,pppac), /* 99: PPP Access Concentrator */
cdev_ujoy_init(NUJOY,ujoy), /* 100: USB joystick/gamecontroller */
};
int nchrdev = nitems(cdevsw);
int mem_no = 2; /* major device number of memory special file */
/*
* Swapdev is a fake device implemented
* in sw.c used only internally to get to swstrategy.
* It cannot be provided to the users, because the
* swstrategy routine munches the b_dev and b_blkno entries
* before calling the appropriate driver. This would horribly
* confuse, e.g. the hashing routines. Instead, /dev/drum is
* provided as a character (raw) device.
*/
dev_t swapdev = makedev(1, 0);
/*
* Returns true if dev is /dev/mem or /dev/kmem.
*/
int
iskmemdev(dev_t dev)
{
return (major(dev) == mem_no && (minor(dev) < 2 || minor(dev) == 14));
}
/*
* Returns true if dev is /dev/zero.
*/
int
iszerodev(dev_t dev)
{
return (major(dev) == mem_no && minor(dev) == 12);
}
dev_t
getnulldev(void)
{
return makedev(mem_no, 2);
}
const int chrtoblktbl[] = {
/*VCHR*/ /*VBLK*/
/* 0 */ NODEV,
/* 1 */ NODEV,
/* 2 */ NODEV,
/* 3 */ 0, /* wd */
/* 4 */ NODEV,
/* 5 */ NODEV,
/* 6 */ NODEV,
/* 7 */ NODEV,
/* 8 */ NODEV,
/* 9 */ 2, /* fd */
/* 10 */ NODEV,
/* 11 */ NODEV,
/* 12 */ NODEV,
/* 13 */ 4, /* sd */
/* 14 */ NODEV,
/* 15 */ 6, /* cd */
/* 16 */ NODEV,
/* 17 */ NODEV,
/* 18 */ NODEV,
/* 19 */ NODEV,
/* 20 */ NODEV,
/* 21 */ NODEV,
/* 22 */ NODEV,
/* 23 */ NODEV,
/* 24 */ NODEV,
/* 25 */ NODEV,
/* 26 */ NODEV,
/* 27 */ NODEV,
/* 28 */ NODEV,
/* 29 */ NODEV,
/* 30 */ NODEV,
/* 31 */ NODEV,
/* 32 */ NODEV,
/* 33 */ NODEV,
/* 34 */ NODEV,
/* 35 */ NODEV,
/* 36 */ NODEV,
/* 37 */ NODEV,
/* 38 */ NODEV,
/* 39 */ NODEV,
/* 40 */ NODEV,
/* 41 */ 14, /* vnd */
/* 42 */ NODEV,
/* 43 */ NODEV,
/* 44 */ NODEV,
/* 45 */ NODEV,
/* 46 */ NODEV,
/* 47 */ 17, /* rd */
};
const int nchrtoblktbl = nitems(chrtoblktbl);
/*
* In order to map BSD bdev numbers of disks to their BIOS equivalents
* we use several heuristics, one being using checksums of the first
* few blocks of a disk to get a signature we can match with /boot's
* computed signatures. To know where from to read, we must provide a
* disk driver name -> bdev major number table, which follows.
* Note: floppies are not included as those are differentiated by the BIOS.
*/
int findblkmajor(struct device *dv);
dev_t dev_rawpart(struct device *); /* XXX */
dev_t
dev_rawpart(struct device *dv)
{
int majdev;
majdev = findblkmajor(dv);
switch (majdev) {
/* add here any device you want to be checksummed on boot */
case 0: /* wd */
case 4: /* sd */
return (MAKEDISKDEV(majdev, dv->dv_unit, RAW_PART));
break;
default:
;
}
return (NODEV);
}
#include <dev/cons.h>
cons_decl(com);
cons_decl(ws);
struct consdev constab[] = {
#if NWSDISPLAY > 0
cons_init(ws),
#endif
#if NCOM > 0
cons_init(com),
#endif
{ 0 },
};
19
19
19
19
1
1
18
19
18
1
1
21
3
22
22
22
1
18
18
33
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
/* $OpenBSD: nd6_nbr.c,v 1.133 2022/08/29 07:51:45 bluhm Exp $ */
/* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/queue.h>
#include <sys/timeout.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
#include "carp.h"
#if NCARP > 0
#include <netinet/ip_carp.h>
#endif
TAILQ_HEAD(dadq_head, dadq);
struct dadq {
TAILQ_ENTRY(dadq) dad_list;
struct ifaddr *dad_ifa;
int dad_count; /* max NS to send */
int dad_ns_tcount; /* # of trials to send NS */
int dad_ns_ocount; /* NS sent so far */
int dad_ns_icount;
int dad_na_icount;
struct timeout dad_timer_ch;
};
struct dadq *nd6_dad_find(struct ifaddr *);
void nd6_dad_starttimer(struct dadq *, int);
void nd6_dad_stoptimer(struct dadq *);
void nd6_dad_timer(void *);
void nd6_dad_ns_output(struct dadq *, struct ifaddr *);
void nd6_dad_ns_input(struct ifaddr *);
void nd6_dad_duplicated(struct dadq *);
int nd6_isneighbor(const struct ifnet *, const struct in6_addr *);
static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */
/*
* Input an Neighbor Solicitation Message.
*
* Based on RFC 2461
* Based on RFC 2462 (duplicated address detection)
*/
void
nd6_ns_input(struct mbuf *m, int off, int icmp6len)
{
struct ifnet *ifp;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_neighbor_solicit *nd_ns;
struct in6_addr saddr6 = ip6->ip6_src;
struct in6_addr daddr6 = ip6->ip6_dst;
struct in6_addr taddr6;
struct in6_addr myaddr6;
char *lladdr = NULL;
struct ifaddr *ifa = NULL;
int lladdrlen = 0;
int anycast = 0, proxy = 0, tentative = 0;
int router = ip6_forwarding;
int tlladdr;
union nd_opts ndopts;
struct sockaddr_dl *proxydl = NULL;
char addr[INET6_ADDRSTRLEN], addr0[INET6_ADDRSTRLEN];
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL)
goto freeit;
IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
if (nd_ns == NULL) {
icmp6stat_inc(icp6s_tooshort);
if_put(ifp);
return;
}
ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
taddr6 = nd_ns->nd_ns_target;
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
"nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n",
ip6->ip6_hlim,
inet_ntop(AF_INET6, &ip6->ip6_src, addr, sizeof(addr)),
inet_ntop(AF_INET6, &ip6->ip6_dst, addr0, sizeof(addr0)),
ifp->if_xname));
goto bad;
}
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
/* dst has to be solicited node multicast address. */
/* don't check ifindex portion */
if (daddr6.s6_addr16[0] == __IPV6_ADDR_INT16_MLL &&
daddr6.s6_addr32[1] == 0 &&
daddr6.s6_addr32[2] == __IPV6_ADDR_INT32_ONE &&
daddr6.s6_addr8[12] == 0xff) {
; /*good*/
} else {
nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
"(wrong ip6 dst)\n"));
goto bad;
}
} else {
/*
* Make sure the source address is from a neighbor's address.
*/
if (!nd6_isneighbor(ifp, &saddr6)) {
nd6log((LOG_INFO, "nd6_ns_input: "
"NS packet from non-neighbor\n"));
goto bad;
}
}
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"));
goto bad;
}
if (IN6_IS_SCOPE_EMBED(&taddr6))
taddr6.s6_addr16[1] = htons(ifp->if_index);
icmp6len -= sizeof(*nd_ns);
nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
nd6log((LOG_INFO,
"nd6_ns_input: invalid ND option, ignored\n"));
/* nd6_options have incremented stats */
goto freeit;
}
if (ndopts.nd_opts_src_lladdr) {
lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
}
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
"(link-layer address option)\n"));
goto bad;
}
/*
* Attaching target link-layer address to the NA?
* (RFC 2461 7.2.4)
*
* NS IP dst is unicast/anycast MUST NOT add
* NS IP dst is solicited-node multicast MUST add
*
* In implementation, we add target link-layer address by default.
* We do not add one in MUST NOT cases.
*/
#if 0 /* too much! */
ifa = &in6ifa_ifpwithaddr(ifp, &daddr6)->ia_ifa;
if (ifa && (ifatoia6(ifa)->ia6_flags & IN6_IFF_ANYCAST))
tlladdr = 0;
else
#endif
if (!IN6_IS_ADDR_MULTICAST(&daddr6))
tlladdr = 0;
else
tlladdr = 1;
/*
* Target address (taddr6) must be either:
* (1) Valid unicast/anycast address for my receiving interface,
* (2) Unicast address for which I'm offering proxy service, or
* (3) "tentative" address on which DAD is being performed.
*/
/* (1) and (3) check. */
ifa = &in6ifa_ifpwithaddr(ifp, &taddr6)->ia_ifa;
#if NCARP > 0
if (ifp->if_type == IFT_CARP && ifa && !carp_iamatch(ifp))
ifa = NULL;
#endif
/* (2) check. */
if (!ifa) {
struct rtentry *rt;
struct sockaddr_in6 tsin6;
bzero(&tsin6, sizeof tsin6);
tsin6.sin6_len = sizeof(struct sockaddr_in6);
tsin6.sin6_family = AF_INET6;
tsin6.sin6_addr = taddr6;
rt = rtalloc(sin6tosa(&tsin6), 0, m->m_pkthdr.ph_rtableid);
if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
rt->rt_gateway->sa_family == AF_LINK) {
/*
* proxy NDP for single entry
*/
ifa = &in6ifa_ifpforlinklocal(ifp, IN6_IFF_TENTATIVE|
IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST)->ia_ifa;
if (ifa) {
proxy = 1;
proxydl = satosdl(rt->rt_gateway);
router = 0; /* XXX */
}
}
if (rt)
rtfree(rt);
}
if (!ifa) {
/*
* We've got an NS packet, and we don't have that address
* assigned for us. We MUST silently ignore it.
* See RFC2461 7.2.3.
*/
goto freeit;
}
myaddr6 = *IFA_IN6(ifa);
anycast = ifatoia6(ifa)->ia6_flags & IN6_IFF_ANYCAST;
tentative = ifatoia6(ifa)->ia6_flags & IN6_IFF_TENTATIVE;
if (ifatoia6(ifa)->ia6_flags & IN6_IFF_DUPLICATED)
goto freeit;
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s "
"(if %d, NS packet %d)\n",
inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr)),
ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n",
inet_ntop(AF_INET6, &saddr6, addr, sizeof(addr)));
goto freeit;
}
/*
* We have neighbor solicitation packet, with target address equals to
* one of my tentative address.
*
* src addr how to process?
* --- ---
* multicast of course, invalid (rejected in ip6_input)
* unicast somebody is doing address resolution -> ignore
* unspec dup address detection
*
* The processing is defined in RFC 2462.
*/
if (tentative) {
/*
* If source address is unspecified address, it is for
* duplicated address detection.
*
* If not, the packet is for address resolution;
* silently ignore it.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
nd6_dad_ns_input(ifa);
goto freeit;
}
/*
* If the source address is unspecified address, entries must not
* be created or updated.
* It looks that sender is performing DAD. Output NA toward
* all-node multicast address, to tell the sender that I'm using
* the address.
* S bit ("solicited") must be zero.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
saddr6 = in6addr_linklocal_allnodes;
saddr6.s6_addr16[1] = htons(ifp->if_index);
nd6_na_output(ifp, &saddr6, &taddr6,
((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
(router ? ND_NA_FLAG_ROUTER : 0),
tlladdr, sdltosa(proxydl));
goto freeit;
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0);
nd6_na_output(ifp, &saddr6, &taddr6,
((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
(router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
tlladdr, sdltosa(proxydl));
freeit:
m_freem(m);
if_put(ifp);
return;
bad:
nd6log((LOG_ERR, "nd6_ns_input: src=%s\n",
inet_ntop(AF_INET6, &saddr6, addr, sizeof(addr))));
nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n",
inet_ntop(AF_INET6, &daddr6, addr, sizeof(addr))));
nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n",
inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr))));
icmp6stat_inc(icp6s_badns);
m_freem(m);
if_put(ifp);
}
/*
* Output an Neighbor Solicitation Message. Caller specifies:
* - ICMP6 header source IP6 address
* - ND6 header target IP6 address
* - ND6 header source datalink address
*
* Based on RFC 2461
* Based on RFC 2462 (duplicated address detection)
*
* ln - for source address determination
* dad - duplicated address detection
*/
void
nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
const struct in6_addr *taddr6, const struct llinfo_nd6 *ln, int dad)
{
struct mbuf *m;
struct ip6_hdr *ip6;
struct nd_neighbor_solicit *nd_ns;
struct sockaddr_in6 src_sa, dst_sa;
struct ip6_moptions im6o;
int icmp6len;
int maxlen;
caddr_t mac;
if (IN6_IS_ADDR_MULTICAST(taddr6))
return;
/* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_ns);
maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
#ifdef DIAGNOSTIC
if (max_linkhdr + maxlen >= MCLBYTES) {
printf("%s: max_linkhdr + maxlen >= MCLBYTES "
"(%d + %d > %d)\n", __func__, max_linkhdr, maxlen, MCLBYTES);
panic("%s: insufficient MCLBYTES", __func__);
/* NOTREACHED */
}
#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && max_linkhdr + maxlen >= MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
m = NULL;
}
}
if (m == NULL)
return;
m->m_pkthdr.ph_ifidx = 0;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
im6o.im6o_ifidx = ifp->if_index;
im6o.im6o_hlim = 255;
im6o.im6o_loop = 0;
}
icmp6len = sizeof(*nd_ns);
m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len;
m_align(m, maxlen);
/* fill neighbor solicitation packet */
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
/* ip6->ip6_plen will be set later */
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
/* determine the source and destination addresses */
bzero(&src_sa, sizeof(src_sa));
bzero(&dst_sa, sizeof(dst_sa));
src_sa.sin6_family = dst_sa.sin6_family = AF_INET6;
src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6);
if (daddr6)
dst_sa.sin6_addr = *daddr6;
else {
dst_sa.sin6_addr.s6_addr16[0] = __IPV6_ADDR_INT16_MLL;
dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
dst_sa.sin6_addr.s6_addr32[1] = 0;
dst_sa.sin6_addr.s6_addr32[2] = __IPV6_ADDR_INT32_ONE;
dst_sa.sin6_addr.s6_addr32[3] = taddr6->s6_addr32[3];
dst_sa.sin6_addr.s6_addr8[12] = 0xff;
}
ip6->ip6_dst = dst_sa.sin6_addr;
if (!dad) {
/*
* RFC2461 7.2.2:
* "If the source address of the packet prompting the
* solicitation is the same as one of the addresses assigned
* to the outgoing interface, that address SHOULD be placed
* in the IP Source Address of the outgoing solicitation.
* Otherwise, any one of the addresses assigned to the
* interface should be used."
*
* We use the source address for the prompting packet
* (saddr6), if:
* - saddr6 is given from the caller (by giving "ln"), and
* - saddr6 belongs to the outgoing interface and
* - if taddr is link local saddr6 must be link local as well
* Otherwise, we perform the source address selection as usual.
*/
struct ip6_hdr *hip6; /* hold ip6 */
struct in6_addr *saddr6;
if (ln && ln->ln_hold) {
hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
if (sizeof(*hip6) <= ln->ln_hold->m_len) {
saddr6 = &hip6->ip6_src;
if (saddr6 && IN6_IS_ADDR_LINKLOCAL(taddr6) &&
!IN6_IS_ADDR_LINKLOCAL(saddr6))
saddr6 = NULL;
} else
saddr6 = NULL;
} else
saddr6 = NULL;
if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6))
src_sa.sin6_addr = *saddr6;
else {
struct rtentry *rt;
rt = rtalloc(sin6tosa(&dst_sa), RT_RESOLVE,
m->m_pkthdr.ph_rtableid);
if (!rtisvalid(rt)) {
char addr[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG,
"%s: source can't be determined: dst=%s\n",
__func__, inet_ntop(AF_INET6,
&dst_sa.sin6_addr, addr, sizeof(addr))));
rtfree(rt);
goto bad;
}
src_sa.sin6_addr =
ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr;
rtfree(rt);
}
} else {
/*
* Source address for DAD packet must always be IPv6
* unspecified address. (0::0)
* We actually don't have to 0-clear the address (we did it
* above), but we do so here explicitly to make the intention
* clearer.
*/
bzero(&src_sa.sin6_addr, sizeof(src_sa.sin6_addr));
}
ip6->ip6_src = src_sa.sin6_addr;
nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
nd_ns->nd_ns_code = 0;
nd_ns->nd_ns_reserved = 0;
nd_ns->nd_ns_target = *taddr6;
if (IN6_IS_SCOPE_EMBED(&nd_ns->nd_ns_target))
nd_ns->nd_ns_target.s6_addr16[1] = 0;
/*
* Add source link-layer address option.
*
* spec implementation
* --- ---
* DAD packet MUST NOT do not add the option
* there's no link layer address:
* impossible do not add the option
* there's link layer address:
* Multicast NS MUST add one add the option
* Unicast NS SHOULD add one add the option
*/
if (!dad && (mac = nd6_ifptomac(ifp))) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
/* 8 byte alignments... */
optlen = (optlen + 7) & ~7;
m->m_pkthdr.len += optlen;
m->m_len += optlen;
icmp6len += optlen;
bzero((caddr_t)nd_opt, optlen);
nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
nd_opt->nd_opt_len = optlen >> 3;
bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
}
ip6->ip6_plen = htons((u_short)icmp6len);
nd_ns->nd_ns_cksum = 0;
m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
ip6_output(m, NULL, NULL, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL);
icmp6stat_inc(icp6s_outhist + ND_NEIGHBOR_SOLICIT);
return;
bad:
m_freem(m);
}
/*
* Neighbor advertisement input handling.
*
* Based on RFC 2461
* Based on RFC 2462 (duplicated address detection)
*
* the following items are not implemented yet:
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*/
void
nd6_na_input(struct mbuf *m, int off, int icmp6len)
{
struct ifnet *ifp;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_neighbor_advert *nd_na;
struct in6_addr daddr6 = ip6->ip6_dst;
struct in6_addr taddr6;
int flags;
int is_router;
int is_solicited;
int is_override;
char *lladdr = NULL;
int lladdrlen = 0;
struct ifaddr *ifa;
struct in6_ifaddr *ifa6;
struct llinfo_nd6 *ln;
struct rtentry *rt = NULL;
struct sockaddr_dl *sdl;
union nd_opts ndopts;
char addr[INET6_ADDRSTRLEN], addr0[INET6_ADDRSTRLEN];
NET_ASSERT_LOCKED();
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL)
goto freeit;
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
"nd6_na_input: invalid hlim (%d) from %s to %s on %s\n",
ip6->ip6_hlim,
inet_ntop(AF_INET6, &ip6->ip6_src, addr, sizeof(addr)),
inet_ntop(AF_INET6, &ip6->ip6_dst, addr0, sizeof(addr0)),
ifp->if_xname));
goto bad;
}
IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);
if (nd_na == NULL) {
icmp6stat_inc(icp6s_tooshort);
if_put(ifp);
return;
}
taddr6 = nd_na->nd_na_target;
flags = nd_na->nd_na_flags_reserved;
is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
if (IN6_IS_SCOPE_EMBED(&taddr6))
taddr6.s6_addr16[1] = htons(ifp->if_index);
if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
nd6log((LOG_ERR,
"nd6_na_input: invalid target address %s\n",
inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr))));
goto bad;
}
if (is_solicited && IN6_IS_ADDR_MULTICAST(&daddr6)) {
nd6log((LOG_ERR,
"nd6_na_input: a solicited adv is multicasted\n"));
goto bad;
}
icmp6len -= sizeof(*nd_na);
nd6_option_init(nd_na + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
nd6log((LOG_INFO,
"nd6_na_input: invalid ND option, ignored\n"));
/* nd6_options have incremented stats */
goto freeit;
}
if (IN6_IS_ADDR_MULTICAST(&daddr6) && !ndopts.nd_opts_tgt_lladdr) {
nd6log((LOG_INFO,
"nd6_na_input: multicast adv without TLLA\n"));
goto bad;
}
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
}
ifa6 = in6ifa_ifpwithaddr(ifp, &taddr6);
ifa = ifa6 ? &ifa6->ia_ifa : NULL;
/*
* Target address matches one of my interface address.
*
* If my address is tentative, this means that there's somebody
* already using the same address as mine. This indicates DAD failure.
* This is defined in RFC 2462.
*
* Otherwise, process as defined in RFC 2461.
*/
if (ifa && (ifatoia6(ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
struct dadq *dp;
dp = nd6_dad_find(ifa);
if (dp) {
dp->dad_na_icount++;
/* remove the address. */
nd6_dad_duplicated(dp);
}
goto freeit;
}
if (ifa) {
#if NCARP > 0
/*
* Ignore NAs silently for carp addresses if we're not
* the CARP master.
*/
if (ifp->if_type == IFT_CARP && !carp_iamatch(ifp))
goto freeit;
#endif
log(LOG_ERR,
"nd6_na_input: duplicate IP6 address %s\n",
inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr)));
goto freeit;
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s "
"(if %d, NA packet %d)\n",
inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr)),
ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
/*
* If no neighbor cache entry is found, NA SHOULD silently be
* discarded.
*/
rt = nd6_lookup(&taddr6, 0, ifp, ifp->if_rdomain);
if ((rt == NULL) ||
((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
((sdl = satosdl(rt->rt_gateway)) == NULL))
goto freeit;
if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/*
* If the link-layer has address, and no lladdr option came,
* discard the packet.
*/
if (ifp->if_addrlen && !lladdr)
goto freeit;
/*
* Record link-layer address, and update the state.
*/
sdl->sdl_alen = ifp->if_addrlen;
bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
/* Notify userland that a new ND entry is reachable. */
rtm_send(rt, RTM_RESOLVE, 0, ifp->if_rdomain);
if (!ND6_LLINFO_PERMANENT(ln)) {
nd6_llinfo_settimer(ln,
ND_IFINFO(ifp)->reachable);
}
} else {
ln->ln_state = ND6_LLINFO_STALE;
nd6_llinfo_settimer(ln, nd6_gctimer);
}
if ((ln->ln_router = is_router) != 0) {
/*
* This means a router's state has changed from
* non-reachable to probably reachable, and might
* affect the status of associated prefixes..
*/
if ((rt->rt_flags & RTF_LLINFO) == 0)
goto freeit; /* ln is gone */
}
} else {
int llchange;
/*
* Check if the link-layer address has changed or not.
*/
if (!lladdr)
llchange = 0;
else {
if (sdl->sdl_alen) {
if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
llchange = 1;
else
llchange = 0;
} else
llchange = 1;
}
/*
* This is VERY complex. Look at it with care.
*
* override solicit lladdr llchange action
* (L: record lladdr)
*
* 0 0 n -- (2c)
* 0 0 y n (2b) L
* 0 0 y y (1) REACHABLE->STALE
* 0 1 n -- (2c) *->REACHABLE
* 0 1 y n (2b) L *->REACHABLE
* 0 1 y y (1) REACHABLE->STALE
* 1 0 n -- (2a)
* 1 0 y n (2a) L
* 1 0 y y (2a) L *->STALE
* 1 1 n -- (2a) *->REACHABLE
* 1 1 y n (2a) L *->REACHABLE
* 1 1 y y (2a) L *->REACHABLE
*/
if (!is_override && (lladdr && llchange)) { /* (1) */
/*
* If state is REACHABLE, make it STALE.
* no other updates should be done.
*/
if (ln->ln_state == ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
nd6_llinfo_settimer(ln, nd6_gctimer);
}
goto freeit;
} else if (is_override /* (2a) */
|| (!is_override && (lladdr && !llchange)) /* (2b) */
|| !lladdr) { /* (2c) */
/*
* Update link-local address, if any.
*/
if (llchange) {
log(LOG_INFO, "ndp info overwritten for %s "
"by %s on %s\n",
inet_ntop(AF_INET6, &taddr6,
addr, sizeof(addr)),
ether_sprintf(lladdr), ifp->if_xname);
}
if (lladdr) {
sdl->sdl_alen = ifp->if_addrlen;
bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
}
/*
* If solicited, make the state REACHABLE.
* If not solicited and the link-layer address was
* changed, make it STALE.
*/
if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (!ND6_LLINFO_PERMANENT(ln)) {
nd6_llinfo_settimer(ln,
ND_IFINFO(ifp)->reachable);
}
} else {
if (lladdr && llchange) {
ln->ln_state = ND6_LLINFO_STALE;
nd6_llinfo_settimer(ln, nd6_gctimer);
}
}
}
if (ln->ln_router && !is_router) {
if (!ip6_forwarding) {
/*
* The neighbor may be used
* as a next hop for some destinations
* (e.g. redirect case). So we must
* call rt6_flush explicitly.
*/
rt6_flush(&ip6->ip6_src, ifp);
}
}
ln->ln_router = is_router;
}
rt->rt_flags &= ~RTF_REJECT;
ln->ln_asked = 0;
if (ln->ln_hold) {
struct mbuf *n = ln->ln_hold;
ln->ln_hold = NULL;
/*
* we assume ifp is not a loopback here, so just set the 2nd
* argument as the 1st one.
*/
ifp->if_output(ifp, n, rt_key(rt), rt);
if (ln->ln_hold == n) {
/* n is back in ln_hold. Discard. */
m_freem(ln->ln_hold);
ln->ln_hold = NULL;
}
}
freeit:
rtfree(rt);
m_freem(m);
if_put(ifp);
return;
bad:
icmp6stat_inc(icp6s_badna);
m_freem(m);
if_put(ifp);
}
/*
* Neighbor advertisement output handling.
*
* Based on RFC 2461
*
* the following items are not implemented yet:
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*
* tlladdr - 1 if include target link-layer address
* sdl0 - sockaddr_dl (= proxy NA) or NULL
*/
void
nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6,
const struct in6_addr *taddr6, u_long flags, int tlladdr,
struct sockaddr *sdl0)
{
struct mbuf *m;
struct rtentry *rt = NULL;
struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na;
struct ip6_moptions im6o;
struct sockaddr_in6 dst_sa;
int icmp6len, maxlen;
caddr_t mac = NULL;
#if NCARP > 0
/* Do not send NAs for carp addresses if we're not the CARP master. */
if (ifp->if_type == IFT_CARP && !carp_iamatch(ifp))
return;
#endif
/* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_na);
maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
#ifdef DIAGNOSTIC
if (max_linkhdr + maxlen >= MCLBYTES) {
printf("%s: max_linkhdr + maxlen >= MCLBYTES "
"(%d + %d > %d)\n", __func__, max_linkhdr, maxlen, MCLBYTES);
panic("%s: insufficient MCLBYTES", __func__);
/* NOTREACHED */
}
#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && max_linkhdr + maxlen >= MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
m = NULL;
}
}
if (m == NULL)
return;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
if (IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
im6o.im6o_ifidx = ifp->if_index;
im6o.im6o_hlim = 255;
im6o.im6o_loop = 0;
}
icmp6len = sizeof(*nd_na);
m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
m_align(m, maxlen);
/* fill neighbor advertisement packet */
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
bzero(&dst_sa, sizeof(dst_sa));
dst_sa.sin6_len = sizeof(struct sockaddr_in6);
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_addr = *daddr6;
if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
/* reply to DAD */
dst_sa.sin6_addr.s6_addr16[0] = __IPV6_ADDR_INT16_MLL;
dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
dst_sa.sin6_addr.s6_addr32[1] = 0;
dst_sa.sin6_addr.s6_addr32[2] = 0;
dst_sa.sin6_addr.s6_addr32[3] = __IPV6_ADDR_INT32_ONE;
flags &= ~ND_NA_FLAG_SOLICITED;
}
ip6->ip6_dst = dst_sa.sin6_addr;
/*
* Select a source whose scope is the same as that of the dest.
*/
rt = rtalloc(sin6tosa(&dst_sa), RT_RESOLVE, ifp->if_rdomain);
if (!rtisvalid(rt)) {
char addr[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, "%s: source can't be determined: dst=%s\n",
__func__, inet_ntop(AF_INET6, &dst_sa.sin6_addr, addr,
sizeof(addr))));
rtfree(rt);
goto bad;
}
ip6->ip6_src = ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr;
rtfree(rt);
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
nd_na->nd_na_target = *taddr6;
if (IN6_IS_SCOPE_EMBED(&nd_na->nd_na_target))
nd_na->nd_na_target.s6_addr16[1] = 0;
/*
* "tlladdr" indicates NS's condition for adding tlladdr or not.
* see nd6_ns_input() for details.
* Basically, if NS packet is sent to unicast/anycast addr,
* target lladdr option SHOULD NOT be included.
*/
if (tlladdr) {
/*
* sdl0 != NULL indicates proxy NA. If we do proxy, use
* lladdr in sdl0. If we are not proxying (sending NA for
* my address) use lladdr configured for the interface.
*/
if (sdl0 == NULL) {
mac = nd6_ifptomac(ifp);
} else if (sdl0->sa_family == AF_LINK) {
struct sockaddr_dl *sdl;
sdl = satosdl(sdl0);
if (sdl->sdl_alen == ifp->if_addrlen)
mac = LLADDR(sdl);
}
}
if (tlladdr && mac) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
/* roundup to 8 bytes alignment! */
optlen = (optlen + 7) & ~7;
m->m_pkthdr.len += optlen;
m->m_len += optlen;
icmp6len += optlen;
bzero((caddr_t)nd_opt, optlen);
nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
nd_opt->nd_opt_len = optlen >> 3;
bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
} else
flags &= ~ND_NA_FLAG_OVERRIDE;
ip6->ip6_plen = htons((u_short)icmp6len);
nd_na->nd_na_flags_reserved = flags;
nd_na->nd_na_cksum = 0;
m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
ip6_output(m, NULL, NULL, 0, &im6o, NULL);
icmp6stat_inc(icp6s_outhist + ND_NEIGHBOR_ADVERT);
return;
bad:
m_freem(m);
}
caddr_t
nd6_ifptomac(struct ifnet *ifp)
{
switch (ifp->if_type) {
case IFT_ETHER:
case IFT_IEEE1394:
case IFT_PROPVIRTUAL:
case IFT_CARP:
case IFT_IEEE80211:
return ((caddr_t)(ifp + 1));
default:
return NULL;
}
}
static struct dadq_head dadq;
static int dad_init = 0;
struct dadq *
nd6_dad_find(struct ifaddr *ifa)
{
struct dadq *dp;
TAILQ_FOREACH(dp, &dadq, dad_list) {
if (dp->dad_ifa == ifa)
return dp;
}
return NULL;
}
void
nd6_dad_starttimer(struct dadq *dp, int msec)
{
timeout_set_proc(&dp->dad_timer_ch, nd6_dad_timer, dp->dad_ifa);
timeout_add_msec(&dp->dad_timer_ch, msec);
}
void
nd6_dad_stoptimer(struct dadq *dp)
{
timeout_del(&dp->dad_timer_ch);
}
/*
* Start Duplicated Address Detection (DAD) for specified interface address.
*/
void
nd6_dad_start(struct ifaddr *ifa)
{
struct in6_ifaddr *ia6 = ifatoia6(ifa);
struct dadq *dp;
char addr[INET6_ADDRSTRLEN];
NET_ASSERT_LOCKED();
if (!dad_init) {
TAILQ_INIT(&dadq);
dad_init++;
}
/*
* If we don't need DAD, don't do it.
* There are several cases:
* - DAD is disabled (ip6_dad_count == 0)
* - the interface address is anycast
*/
KASSERT(ia6->ia6_flags & IN6_IFF_TENTATIVE);
if ((ia6->ia6_flags & IN6_IFF_ANYCAST) || (!ip6_dad_count)) {
ia6->ia6_flags &= ~IN6_IFF_TENTATIVE;
rtm_addr(RTM_CHGADDRATTR, ifa);
return;
}
/* DAD already in progress */
if (nd6_dad_find(ifa) != NULL)
return;
dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT | M_ZERO);
if (dp == NULL) {
log(LOG_ERR, "%s: memory allocation failed for %s(%s)\n",
__func__, inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr,
addr, sizeof(addr)),
ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
return;
}
bzero(&dp->dad_timer_ch, sizeof(dp->dad_timer_ch));
TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
ip6_dad_pending++;
nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", ifa->ifa_ifp->if_xname,
inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr, addr, sizeof(addr))));
/*
* Send NS packet for DAD, ip6_dad_count times.
* Note that we must delay the first transmission, if this is the
* first packet to be sent from the interface after interface
* (re)initialization.
*/
dp->dad_ifa = ifaref(ifa);
dp->dad_count = ip6_dad_count;
dp->dad_ns_icount = dp->dad_na_icount = 0;
dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp, ND_IFINFO(ifa->ifa_ifp)->retrans);
}
/*
* terminate DAD unconditionally. used for address removals.
*/
void
nd6_dad_stop(struct ifaddr *ifa)
{
struct dadq *dp;
if (!dad_init)
return;
dp = nd6_dad_find(ifa);
if (!dp) {
/* DAD wasn't started yet */
return;
}
nd6_dad_stoptimer(dp);
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP, sizeof(*dp));
dp = NULL;
ifafree(ifa);
ip6_dad_pending--;
}
void
nd6_dad_timer(void *xifa)
{
struct ifaddr *ifa = xifa;
struct in6_ifaddr *ia6 = ifatoia6(ifa);
struct dadq *dp;
char addr[INET6_ADDRSTRLEN];
NET_LOCK();
/* Sanity check */
if (ia6 == NULL) {
log(LOG_ERR, "%s: called with null parameter\n", __func__);
goto done;
}
dp = nd6_dad_find(ifa);
if (dp == NULL) {
log(LOG_ERR, "%s: DAD structure not found\n", __func__);
goto done;
}
if (ia6->ia6_flags & IN6_IFF_DUPLICATED) {
log(LOG_ERR, "%s: called with duplicated address %s(%s)\n",
__func__, inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr,
addr, sizeof(addr)),
ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
goto done;
}
if ((ia6->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
log(LOG_ERR, "%s: called with non-tentative address %s(%s)\n",
__func__, inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr,
addr, sizeof(addr)),
ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
goto done;
}
/* timeouted with IFF_{RUNNING,UP} check */
if (dp->dad_ns_tcount > dad_maxtry) {
nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n",
ifa->ifa_ifp->if_xname));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP, sizeof(*dp));
dp = NULL;
ifafree(ifa);
ip6_dad_pending--;
goto done;
}
/* Need more checks? */
if (dp->dad_ns_ocount < dp->dad_count) {
/*
* We have more NS to go. Send NS packet for DAD.
*/
nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp, ND_IFINFO(ifa->ifa_ifp)->retrans);
} else {
/*
* We have transmitted sufficient number of DAD packets.
* See what we've got.
*/
int duplicate;
duplicate = 0;
if (dp->dad_na_icount) {
duplicate++;
}
if (dp->dad_ns_icount) {
/* We've seen NS, means DAD has failed. */
duplicate++;
}
if (duplicate) {
/* dp will be freed in nd6_dad_duplicated() */
nd6_dad_duplicated(dp);
} else {
/*
* We are done with DAD. No NA came, no NS came.
* duplicated address found.
*/
ia6->ia6_flags &= ~IN6_IFF_TENTATIVE;
rtm_addr(RTM_CHGADDRATTR, ifa);
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
ifa->ifa_ifp->if_xname,
inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr,
addr, sizeof(addr))));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP, sizeof(*dp));
dp = NULL;
ifafree(ifa);
ip6_dad_pending--;
}
}
done:
NET_UNLOCK();
}
void
nd6_dad_duplicated(struct dadq *dp)
{
struct in6_ifaddr *ia6 = ifatoia6(dp->dad_ifa);
char addr[INET6_ADDRSTRLEN];
log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
"NS in/out=%d/%d, NA in=%d\n",
ia6->ia_ifp->if_xname,
inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr, addr, sizeof(addr)),
dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
ia6->ia6_flags &= ~IN6_IFF_TENTATIVE;
ia6->ia6_flags |= IN6_IFF_DUPLICATED;
/* We are done with DAD, with duplicated address found. (failure) */
nd6_dad_stoptimer(dp);
log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
ia6->ia_ifp->if_xname,
inet_ntop(AF_INET6, &ia6->ia_addr.sin6_addr, addr, sizeof(addr)));
log(LOG_ERR, "%s: manual intervention required\n",
ia6->ia_ifp->if_xname);
TAILQ_REMOVE(&dadq, dp, dad_list);
rtm_addr(RTM_CHGADDRATTR, dp->dad_ifa);
ifafree(dp->dad_ifa);
free(dp, M_IP6NDP, sizeof(*dp));
ip6_dad_pending--;
}
void
nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa)
{
struct in6_ifaddr *ia6 = ifatoia6(ifa);
struct ifnet *ifp = ifa->ifa_ifp;
dp->dad_ns_tcount++;
if ((ifp->if_flags & IFF_UP) == 0) {
#if 0
printf("%s: interface down?\n", ifp->if_xname);
#endif
return;
}
if ((ifp->if_flags & IFF_RUNNING) == 0) {
#if 0
printf("%s: interface not running?\n", ifp->if_xname);
#endif
return;
}
dp->dad_ns_ocount++;
nd6_ns_output(ifp, NULL, &ia6->ia_addr.sin6_addr, NULL, 1);
}
void
nd6_dad_ns_input(struct ifaddr *ifa)
{
struct dadq *dp;
int duplicate;
if (!ifa)
panic("%s: ifa == NULL", __func__);
duplicate = 0;
dp = nd6_dad_find(ifa);
if (dp == NULL) {
log(LOG_ERR, "%s: DAD structure not found\n", __func__);
return;
}
/*
* if I'm yet to start DAD, someone else started using this address
* first. I have a duplicate and you win.
*/
if (dp->dad_ns_ocount == 0)
duplicate++;
/* XXX more checks for loopback situation - see nd6_dad_timer too */
if (duplicate) {
/* dp will be freed in nd6_dad_duplicated() */
nd6_dad_duplicated(dp);
} else {
/*
* not sure if I got a duplicate.
* increment ns count and see what happens.
*/
dp->dad_ns_icount++;
}
}
/*
* Check whether ``addr'' is a neighbor address connected to ``ifp''.
*/
int
nd6_isneighbor(const struct ifnet *ifp, const struct in6_addr *addr)
{
struct rtentry *rt;
struct sockaddr_in6 sin6;
unsigned int tableid = ifp->if_rdomain;
int rv = 0;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr;
rt = rtalloc(sin6tosa(&sin6), 0, tableid);
if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_CLONING|RTF_CLONED))
rv = if_isconnected(ifp, rt->rt_ifidx);
rtfree(rt);
return (rv);
}
98
98
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* $OpenBSD: strnlen.c,v 1.3 2019/01/25 00:19:26 millert Exp $ */
/*
* Copyright (c) 2010 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <lib/libkern/libkern.h>
size_t
strnlen(const char *str, size_t maxlen)
{
const char *cp;
for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
;
return (size_t)(cp - str);
}
7
7
7
7
7
7
7
179
175
7
178
175
7
12
3
2
9
12
2
3
4
3
3
5
4
3
12
12
12
12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
/* $OpenBSD: uvm_pager.c,v 1.89 2022/08/19 05:53:19 mpi Exp $ */
/* $NetBSD: uvm_pager.c,v 1.36 2000/11/27 18:26:41 chs Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* from: Id: uvm_pager.c,v 1.1.2.23 1998/02/02 20:38:06 chuck Exp
*/
/*
* uvm_pager.c: generic functions used to assist the pagers.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/buf.h>
#include <sys/atomic.h>
#include <uvm/uvm.h>
const struct uvm_pagerops *uvmpagerops[] = {
&aobj_pager,
&uvm_deviceops,
&uvm_vnodeops,
};
/*
* the pager map: provides KVA for I/O
*
* Each uvm_pseg has room for MAX_PAGERMAP_SEGS pager io space of
* MAXBSIZE bytes.
*
* The number of uvm_pseg instances is dynamic using an array segs.
* At most UVM_PSEG_COUNT instances can exist.
*
* psegs[0/1] always exist (so that the pager can always map in pages).
* psegs[0/1] element 0 are always reserved for the pagedaemon.
*
* Any other pseg is automatically created when no space is available
* and automatically destroyed when it is no longer in use.
*/
#define MAX_PAGER_SEGS 16
#define PSEG_NUMSEGS (PAGER_MAP_SIZE / MAX_PAGER_SEGS / MAXBSIZE)
struct uvm_pseg {
/* Start of virtual space; 0 if not inited. */
vaddr_t start;
/* Bitmap of the segments in use in this pseg. */
int use;
};
struct mutex uvm_pseg_lck;
struct uvm_pseg psegs[PSEG_NUMSEGS];
#define UVM_PSEG_FULL(pseg) ((pseg)->use == (1 << MAX_PAGER_SEGS) - 1)
#define UVM_PSEG_EMPTY(pseg) ((pseg)->use == 0)
#define UVM_PSEG_INUSE(pseg,id) (((pseg)->use & (1 << (id))) != 0)
void uvm_pseg_init(struct uvm_pseg *);
vaddr_t uvm_pseg_get(int);
void uvm_pseg_release(vaddr_t);
/*
* uvm_pager_init: init pagers (at boot time)
*/
void
uvm_pager_init(void)
{
int lcv;
/* init pager map */
uvm_pseg_init(&psegs[0]);
uvm_pseg_init(&psegs[1]);
mtx_init(&uvm_pseg_lck, IPL_VM);
/* init ASYNC I/O queue */
TAILQ_INIT(&uvm.aio_done);
/* call pager init functions */
for (lcv = 0 ; lcv < sizeof(uvmpagerops)/sizeof(struct uvm_pagerops *);
lcv++) {
if (uvmpagerops[lcv]->pgo_init)
uvmpagerops[lcv]->pgo_init();
}
}
/*
* Initialize a uvm_pseg.
*
* May fail, in which case seg->start == 0.
*
* Caller locks uvm_pseg_lck.
*/
void
uvm_pseg_init(struct uvm_pseg *pseg)
{
KASSERT(pseg->start == 0);
KASSERT(pseg->use == 0);
pseg->start = (vaddr_t)km_alloc(MAX_PAGER_SEGS * MAXBSIZE,
&kv_any, &kp_none, &kd_trylock);
}
/*
* Acquire a pager map segment.
*
* Returns a vaddr for paging. 0 on failure.
*
* Caller does not lock.
*/
vaddr_t
uvm_pseg_get(int flags)
{
int i;
struct uvm_pseg *pseg;
/*
* XXX Prevent lock ordering issue in uvm_unmap_detach(). A real
* fix would be to move the KERNEL_LOCK() out of uvm_unmap_detach().
*
* witness_checkorder() at witness_checkorder+0xba0
* __mp_lock() at __mp_lock+0x5f
* uvm_unmap_detach() at uvm_unmap_detach+0xc5
* uvm_map() at uvm_map+0x857
* uvm_km_valloc_try() at uvm_km_valloc_try+0x65
* uvm_pseg_get() at uvm_pseg_get+0x6f
* uvm_pagermapin() at uvm_pagermapin+0x45
* uvn_io() at uvn_io+0xcf
* uvn_get() at uvn_get+0x156
* uvm_fault_lower() at uvm_fault_lower+0x28a
* uvm_fault() at uvm_fault+0x1b3
* upageflttrap() at upageflttrap+0x62
*/
KERNEL_LOCK();
mtx_enter(&uvm_pseg_lck);
pager_seg_restart:
/* Find first pseg that has room. */
for (pseg = &psegs[0]; pseg != &psegs[PSEG_NUMSEGS]; pseg++) {
if (UVM_PSEG_FULL(pseg))
continue;
if (pseg->start == 0) {
/* Need initialization. */
uvm_pseg_init(pseg);
if (pseg->start == 0)
goto pager_seg_fail;
}
/* Keep indexes 0,1 reserved for pagedaemon. */
if ((pseg == &psegs[0] || pseg == &psegs[1]) &&
(curproc != uvm.pagedaemon_proc))
i = 2;
else
i = 0;
for (; i < MAX_PAGER_SEGS; i++) {
if (!UVM_PSEG_INUSE(pseg, i)) {
pseg->use |= 1 << i;
mtx_leave(&uvm_pseg_lck);
KERNEL_UNLOCK();
return pseg->start + i * MAXBSIZE;
}
}
}
pager_seg_fail:
if ((flags & UVMPAGER_MAPIN_WAITOK) != 0) {
msleep_nsec(&psegs, &uvm_pseg_lck, PVM, "pagerseg", INFSLP);
goto pager_seg_restart;
}
mtx_leave(&uvm_pseg_lck);
KERNEL_UNLOCK();
return 0;
}
/*
* Release a pager map segment.
*
* Caller does not lock.
*
* Deallocates pseg if it is no longer in use.
*/
void
uvm_pseg_release(vaddr_t segaddr)
{
int id;
struct uvm_pseg *pseg;
vaddr_t va = 0;
mtx_enter(&uvm_pseg_lck);
for (pseg = &psegs[0]; pseg != &psegs[PSEG_NUMSEGS]; pseg++) {
if (pseg->start <= segaddr &&
segaddr < pseg->start + MAX_PAGER_SEGS * MAXBSIZE)
break;
}
KASSERT(pseg != &psegs[PSEG_NUMSEGS]);
id = (segaddr - pseg->start) / MAXBSIZE;
KASSERT(id >= 0 && id < MAX_PAGER_SEGS);
/* test for no remainder */
KDASSERT(segaddr == pseg->start + id * MAXBSIZE);
KASSERT(UVM_PSEG_INUSE(pseg, id));
pseg->use &= ~(1 << id);
wakeup(&psegs);
if ((pseg != &psegs[0] && pseg != &psegs[1]) && UVM_PSEG_EMPTY(pseg)) {
va = pseg->start;
pseg->start = 0;
}
mtx_leave(&uvm_pseg_lck);
if (va) {
km_free((void *)va, MAX_PAGER_SEGS * MAXBSIZE,
&kv_any, &kp_none);
}
}
/*
* uvm_pagermapin: map pages into KVA for I/O that needs mappings
*
* We basically just km_valloc a blank map entry to reserve the space in the
* kernel map and then use pmap_enter() to put the mappings in by hand.
*/
vaddr_t
uvm_pagermapin(struct vm_page **pps, int npages, int flags)
{
vaddr_t kva, cva;
vm_prot_t prot;
vsize_t size;
struct vm_page *pp;
#if defined(__HAVE_PMAP_DIRECT)
/*
* Use direct mappings for single page, unless there is a risk
* of aliasing.
*/
if (npages == 1 && PMAP_PREFER_ALIGN() == 0) {
KASSERT(pps[0]);
KASSERT(pps[0]->pg_flags & PG_BUSY);
return pmap_map_direct(pps[0]);
}
#endif
prot = PROT_READ;
if (flags & UVMPAGER_MAPIN_READ)
prot |= PROT_WRITE;
size = ptoa(npages);
KASSERT(size <= MAXBSIZE);
kva = uvm_pseg_get(flags);
if (kva == 0)
return 0;
for (cva = kva ; size != 0 ; size -= PAGE_SIZE, cva += PAGE_SIZE) {
pp = *pps++;
KASSERT(pp);
KASSERT(pp->pg_flags & PG_BUSY);
/* Allow pmap_enter to fail. */
if (pmap_enter(pmap_kernel(), cva, VM_PAGE_TO_PHYS(pp),
prot, PMAP_WIRED | PMAP_CANFAIL | prot) != 0) {
pmap_remove(pmap_kernel(), kva, cva);
pmap_update(pmap_kernel());
uvm_pseg_release(kva);
return 0;
}
}
pmap_update(pmap_kernel());
return kva;
}
/*
* uvm_pagermapout: remove KVA mapping
*
* We remove our mappings by hand and then remove the mapping.
*/
void
uvm_pagermapout(vaddr_t kva, int npages)
{
#if defined(__HAVE_PMAP_DIRECT)
/*
* Use direct mappings for single page, unless there is a risk
* of aliasing.
*/
if (npages == 1 && PMAP_PREFER_ALIGN() == 0) {
pmap_unmap_direct(kva);
return;
}
#endif
pmap_remove(pmap_kernel(), kva, kva + ((vsize_t)npages << PAGE_SHIFT));
pmap_update(pmap_kernel());
uvm_pseg_release(kva);
}
/*
* uvm_mk_pcluster
*
* generic "make 'pager put' cluster" function. a pager can either
* [1] set pgo_mk_pcluster to NULL (never cluster), [2] set it to this
* generic function, or [3] set it to a pager specific function.
*
* => caller must lock object _and_ pagequeues (since we need to look
* at active vs. inactive bits, etc.)
* => caller must make center page busy and write-protect it
* => we mark all cluster pages busy for the caller
* => the caller must unbusy all pages (and check wanted/released
* status if it drops the object lock)
* => flags:
* PGO_ALLPAGES: all pages in object are valid targets
* !PGO_ALLPAGES: use "lo" and "hi" to limit range of cluster
* PGO_DOACTCLUST: include active pages in cluster.
* PGO_FREE: set the PG_RELEASED bits on the cluster so they'll be freed
* in async io (caller must clean on error).
* NOTE: the caller should clear PG_CLEANCHK bits if PGO_DOACTCLUST.
* PG_CLEANCHK is only a hint, but clearing will help reduce
* the number of calls we make to the pmap layer.
*/
struct vm_page **
uvm_mk_pcluster(struct uvm_object *uobj, struct vm_page **pps, int *npages,
struct vm_page *center, int flags, voff_t mlo, voff_t mhi)
{
struct vm_page **ppsp, *pclust;
voff_t lo, hi, curoff;
int center_idx, forward, incr;
/*
* center page should already be busy and write protected. XXX:
* suppose page is wired? if we lock, then a process could
* fault/block on it. if we don't lock, a process could write the
* pages in the middle of an I/O. (consider an msync()). let's
* lock it for now (better to delay than corrupt data?).
*/
/* get cluster boundaries, check sanity, and apply our limits as well.*/
uobj->pgops->pgo_cluster(uobj, center->offset, &lo, &hi);
if ((flags & PGO_ALLPAGES) == 0) {
if (lo < mlo)
lo = mlo;
if (hi > mhi)
hi = mhi;
}
if ((hi - lo) >> PAGE_SHIFT > *npages) { /* pps too small, bail out! */
pps[0] = center;
*npages = 1;
return pps;
}
/* now determine the center and attempt to cluster around the edges */
center_idx = (center->offset - lo) >> PAGE_SHIFT;
pps[center_idx] = center; /* plug in the center page */
ppsp = &pps[center_idx];
*npages = 1;
/*
* attempt to cluster around the left [backward], and then
* the right side [forward].
*
* note that for inactive pages (pages that have been deactivated)
* there are no valid mappings and PG_CLEAN should be up to date.
* [i.e. there is no need to query the pmap with pmap_is_modified
* since there are no mappings].
*/
for (forward = 0 ; forward <= 1 ; forward++) {
incr = forward ? PAGE_SIZE : -PAGE_SIZE;
curoff = center->offset + incr;
for ( ;(forward == 0 && curoff >= lo) ||
(forward && curoff < hi);
curoff += incr) {
pclust = uvm_pagelookup(uobj, curoff); /* lookup page */
if (pclust == NULL) {
break; /* no page */
}
/* handle active pages */
/* NOTE: inactive pages don't have pmap mappings */
if ((pclust->pg_flags & PQ_INACTIVE) == 0) {
if ((flags & PGO_DOACTCLUST) == 0) {
/* dont want mapped pages at all */
break;
}
/* make sure "clean" bit is sync'd */
if ((pclust->pg_flags & PG_CLEANCHK) == 0) {
if ((pclust->pg_flags & (PG_CLEAN|PG_BUSY))
== PG_CLEAN &&
pmap_is_modified(pclust))
atomic_clearbits_int(
&pclust->pg_flags,
PG_CLEAN);
/* now checked */
atomic_setbits_int(&pclust->pg_flags,
PG_CLEANCHK);
}
}
/* is page available for cleaning and does it need it */
if ((pclust->pg_flags & (PG_CLEAN|PG_BUSY)) != 0) {
break; /* page is already clean or is busy */
}
/* yes! enroll the page in our array */
atomic_setbits_int(&pclust->pg_flags, PG_BUSY);
UVM_PAGE_OWN(pclust, "uvm_mk_pcluster");
/*
* If we want to free after io is done, and we're
* async, set the released flag
*/
if ((flags & (PGO_FREE|PGO_SYNCIO)) == PGO_FREE)
atomic_setbits_int(&pclust->pg_flags,
PG_RELEASED);
/* XXX: protect wired page? see above comment. */
pmap_page_protect(pclust, PROT_READ);
if (!forward) {
ppsp--; /* back up one page */
*ppsp = pclust;
} else {
/* move forward one page */
ppsp[*npages] = pclust;
}
(*npages)++;
}
}
/*
* done! return the cluster array to the caller!!!
*/
return ppsp;
}
/*
* uvm_pager_put: high level pageout routine
*
* we want to pageout page "pg" to backing store, clustering if
* possible.
*
* => page queues must be locked by caller
* => if page is not swap-backed, then "uobj" points to the object
* backing it.
* => if page is swap-backed, then "uobj" should be NULL.
* => "pg" should be PG_BUSY (by caller), and !PG_CLEAN
* for swap-backed memory, "pg" can be NULL if there is no page
* of interest [sometimes the case for the pagedaemon]
* => "ppsp_ptr" should point to an array of npages vm_page pointers
* for possible cluster building
* => flags (first two for non-swap-backed pages)
* PGO_ALLPAGES: all pages in uobj are valid targets
* PGO_DOACTCLUST: include "PQ_ACTIVE" pages as valid targets
* PGO_SYNCIO: do SYNC I/O (no async)
* PGO_PDFREECLUST: pagedaemon: drop cluster on successful I/O
* PGO_FREE: tell the aio daemon to free pages in the async case.
* => start/stop: if (uobj && !PGO_ALLPAGES) limit targets to this range
* if (!uobj) start is the (daddr_t) of the starting swapblk
* => return state:
* 1. we return the VM_PAGER status code of the pageout
* 2. we return with the page queues unlocked
* 3. on errors we always drop the cluster. thus, if we return
* !PEND, !OK, then the caller only has to worry about
* un-busying the main page (not the cluster pages).
* 4. on success, if !PGO_PDFREECLUST, we return the cluster
* with all pages busy (caller must un-busy and check
* wanted/released flags).
*/
int
uvm_pager_put(struct uvm_object *uobj, struct vm_page *pg,
struct vm_page ***ppsp_ptr, int *npages, int flags,
voff_t start, voff_t stop)
{
int result;
daddr_t swblk;
struct vm_page **ppsp = *ppsp_ptr;
/*
* note that uobj is null if we are doing a swap-backed pageout.
* note that uobj is !null if we are doing normal object pageout.
* note that the page queues must be locked to cluster.
*/
if (uobj) { /* if !swap-backed */
/*
* attempt to build a cluster for pageout using its
* make-put-cluster function (if it has one).
*/
if (uobj->pgops->pgo_mk_pcluster) {
ppsp = uobj->pgops->pgo_mk_pcluster(uobj, ppsp,
npages, pg, flags, start, stop);
*ppsp_ptr = ppsp; /* update caller's pointer */
} else {
ppsp[0] = pg;
*npages = 1;
}
swblk = 0; /* XXX: keep gcc happy */
} else {
/*
* for swap-backed pageout, the caller (the pagedaemon) has
* already built the cluster for us. the starting swap
* block we are writing to has been passed in as "start."
* "pg" could be NULL if there is no page we are especially
* interested in (in which case the whole cluster gets dropped
* in the event of an error or a sync "done").
*/
swblk = start;
/* ppsp and npages should be ok */
}
/* now that we've clustered we can unlock the page queues */
uvm_unlock_pageq();
/*
* now attempt the I/O. if we have a failure and we are
* clustered, we will drop the cluster and try again.
*/
ReTry:
if (uobj) {
result = uobj->pgops->pgo_put(uobj, ppsp, *npages, flags);
} else {
/* XXX daddr_t -> int */
result = uvm_swap_put(swblk, ppsp, *npages, flags);
}
/*
* we have attempted the I/O.
*
* if the I/O was a success then:
* if !PGO_PDFREECLUST, we return the cluster to the
* caller (who must un-busy all pages)
* else we un-busy cluster pages for the pagedaemon
*
* if I/O is pending (async i/o) then we return the pending code.
* [in this case the async i/o done function must clean up when
* i/o is done...]
*/
if (result == VM_PAGER_PEND || result == VM_PAGER_OK) {
if (result == VM_PAGER_OK && (flags & PGO_PDFREECLUST)) {
/* drop cluster */
if (*npages > 1 || pg == NULL)
uvm_pager_dropcluster(uobj, pg, ppsp, npages,
PGO_PDFREECLUST);
}
return (result);
}
/*
* a pager error occurred (even after dropping the cluster, if there
* was one). give up! the caller only has one page ("pg")
* to worry about.
*/
if (*npages > 1 || pg == NULL) {
uvm_pager_dropcluster(uobj, pg, ppsp, npages, PGO_REALLOCSWAP);
/*
* for failed swap-backed pageouts with a "pg",
* we need to reset pg's swslot to either:
* "swblk" (for transient errors, so we can retry),
* or 0 (for hard errors).
*/
if (uobj == NULL && pg != NULL) {
/* XXX daddr_t -> int */
int nswblk = (result == VM_PAGER_AGAIN) ? swblk : 0;
if (pg->pg_flags & PQ_ANON) {
rw_enter(pg->uanon->an_lock, RW_WRITE);
pg->uanon->an_swslot = nswblk;
rw_exit(pg->uanon->an_lock);
} else {
rw_enter(pg->uobject->vmobjlock, RW_WRITE);
uao_set_swslot(pg->uobject,
pg->offset >> PAGE_SHIFT,
nswblk);
rw_exit(pg->uobject->vmobjlock);
}
}
if (result == VM_PAGER_AGAIN) {
/*
* for transient failures, free all the swslots that
* we're not going to retry with.
*/
if (uobj == NULL) {
if (pg) {
/* XXX daddr_t -> int */
uvm_swap_free(swblk + 1, *npages - 1);
} else {
/* XXX daddr_t -> int */
uvm_swap_free(swblk, *npages);
}
}
if (pg) {
ppsp[0] = pg;
*npages = 1;
goto ReTry;
}
} else if (uobj == NULL) {
/*
* for hard errors on swap-backed pageouts,
* mark the swslots as bad. note that we do not
* free swslots that we mark bad.
*/
/* XXX daddr_t -> int */
uvm_swap_markbad(swblk, *npages);
}
}
/*
* a pager error occurred (even after dropping the cluster, if there
* was one). give up! the caller only has one page ("pg")
* to worry about.
*/
return result;
}
/*
* uvm_pager_dropcluster: drop a cluster we have built (because we
* got an error, or, if PGO_PDFREECLUST we are un-busying the
* cluster pages on behalf of the pagedaemon).
*
* => uobj, if non-null, is a non-swap-backed object
* => page queues are not locked
* => pg is our page of interest (the one we clustered around, can be null)
* => ppsp/npages is our current cluster
* => flags: PGO_PDFREECLUST: pageout was a success: un-busy cluster
* pages on behalf of the pagedaemon.
* PGO_REALLOCSWAP: drop previously allocated swap slots for
* clustered swap-backed pages (except for "pg" if !NULL)
* "swblk" is the start of swap alloc (e.g. for ppsp[0])
* [only meaningful if swap-backed (uobj == NULL)]
*/
void
uvm_pager_dropcluster(struct uvm_object *uobj, struct vm_page *pg,
struct vm_page **ppsp, int *npages, int flags)
{
int lcv;
KASSERT(uobj == NULL || rw_write_held(uobj->vmobjlock));
/* drop all pages but "pg" */
for (lcv = 0 ; lcv < *npages ; lcv++) {
/* skip "pg" or empty slot */
if (ppsp[lcv] == pg || ppsp[lcv] == NULL)
continue;
/*
* Note that PQ_ANON bit can't change as long as we are holding
* the PG_BUSY bit (so there is no need to lock the page
* queues to test it).
*/
if (!uobj) {
if (ppsp[lcv]->pg_flags & PQ_ANON) {
rw_enter(ppsp[lcv]->uanon->an_lock, RW_WRITE);
if (flags & PGO_REALLOCSWAP)
/* zap swap block */
ppsp[lcv]->uanon->an_swslot = 0;
} else {
rw_enter(ppsp[lcv]->uobject->vmobjlock,
RW_WRITE);
if (flags & PGO_REALLOCSWAP)
uao_set_swslot(ppsp[lcv]->uobject,
ppsp[lcv]->offset >> PAGE_SHIFT, 0);
}
}
/* did someone want the page while we had it busy-locked? */
if (ppsp[lcv]->pg_flags & PG_WANTED) {
wakeup(ppsp[lcv]);
}
/* if page was released, release it. otherwise un-busy it */
if (ppsp[lcv]->pg_flags & PG_RELEASED &&
ppsp[lcv]->pg_flags & PQ_ANON) {
/* kills anon and frees pg */
uvm_anon_release(ppsp[lcv]->uanon);
continue;
} else {
/*
* if we were planning on async io then we would
* have PG_RELEASED set, clear that with the others.
*/
atomic_clearbits_int(&ppsp[lcv]->pg_flags,
PG_BUSY|PG_WANTED|PG_FAKE|PG_RELEASED);
UVM_PAGE_OWN(ppsp[lcv], NULL);
}
/*
* if we are operating on behalf of the pagedaemon and we
* had a successful pageout update the page!
*/
if (flags & PGO_PDFREECLUST) {
pmap_clear_reference(ppsp[lcv]);
pmap_clear_modify(ppsp[lcv]);
atomic_setbits_int(&ppsp[lcv]->pg_flags, PG_CLEAN);
}
/* if anonymous cluster, unlock object and move on */
if (!uobj) {
if (ppsp[lcv]->pg_flags & PQ_ANON)
rw_exit(ppsp[lcv]->uanon->an_lock);
else
rw_exit(ppsp[lcv]->uobject->vmobjlock);
}
}
}
/*
* interrupt-context iodone handler for single-buf i/os
* or the top-level buf of a nested-buf i/o.
*
* => must be at splbio().
*/
void
uvm_aio_biodone(struct buf *bp)
{
splassert(IPL_BIO);
/* reset b_iodone for when this is a single-buf i/o. */
bp->b_iodone = uvm_aio_aiodone;
mtx_enter(&uvm.aiodoned_lock);
TAILQ_INSERT_TAIL(&uvm.aio_done, bp, b_freelist);
wakeup(&uvm.aiodoned);
mtx_leave(&uvm.aiodoned_lock);
}
void
uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, boolean_t write,
int error)
{
struct vm_page *pg;
struct uvm_object *uobj;
boolean_t swap;
int i;
uobj = NULL;
for (i = 0; i < npages; i++) {
pg = pgs[i];
if (i == 0) {
swap = (pg->pg_flags & PQ_SWAPBACKED) != 0;
if (!swap) {
uobj = pg->uobject;
rw_enter(uobj->vmobjlock, RW_WRITE);
}
}
KASSERT(swap || pg->uobject == uobj);
/*
* if this is a read and we got an error, mark the pages
* PG_RELEASED so that uvm_page_unbusy() will free them.
*/
if (!write && error) {
atomic_setbits_int(&pg->pg_flags, PG_RELEASED);
continue;
}
KASSERT(!write || (pgs[i]->pg_flags & PG_FAKE) == 0);
/*
* if this is a read and the page is PG_FAKE,
* or this was a successful write,
* mark the page PG_CLEAN and not PG_FAKE.
*/
if ((pgs[i]->pg_flags & PG_FAKE) || (write && error != ENOMEM)) {
pmap_clear_reference(pgs[i]);
pmap_clear_modify(pgs[i]);
atomic_setbits_int(&pgs[i]->pg_flags, PG_CLEAN);
atomic_clearbits_int(&pgs[i]->pg_flags, PG_FAKE);
}
}
uvm_page_unbusy(pgs, npages);
if (!swap) {
rw_exit(uobj->vmobjlock);
}
}
/*
* uvm_aio_aiodone: do iodone processing for async i/os.
* this should be called in thread context, not interrupt context.
*/
void
uvm_aio_aiodone(struct buf *bp)
{
int npages = bp->b_bufsize >> PAGE_SHIFT;
struct vm_page *pgs[MAXPHYS >> PAGE_SHIFT];
int i, error;
boolean_t write;
KASSERT(npages <= MAXPHYS >> PAGE_SHIFT);
splassert(IPL_BIO);
error = (bp->b_flags & B_ERROR) ? (bp->b_error ? bp->b_error : EIO) : 0;
write = (bp->b_flags & B_READ) == 0;
for (i = 0; i < npages; i++)
pgs[i] = uvm_atopg((vaddr_t)bp->b_data +
((vsize_t)i << PAGE_SHIFT));
uvm_pagermapout((vaddr_t)bp->b_data, npages);
#ifdef UVM_SWAP_ENCRYPT
/*
* XXX - assumes that we only get ASYNC writes. used to be above.
*/
if (pgs[0]->pg_flags & PQ_ENCRYPT) {
uvm_swap_freepages(pgs, npages);
goto freed;
}
#endif /* UVM_SWAP_ENCRYPT */
uvm_aio_aiodone_pages(pgs, npages, write, error);
#ifdef UVM_SWAP_ENCRYPT
freed:
#endif
pool_put(&bufpool, bp);
}
145
169
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/* $OpenBSD: event.h,v 1.67 2022/03/31 01:41:22 millert Exp $ */
/*-
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/sys/event.h,v 1.11 2001/02/24 01:41:31 jlemon Exp $
*/
#ifndef _SYS_EVENT_H_
#define _SYS_EVENT_H_
#define EVFILT_READ (-1)
#define EVFILT_WRITE (-2)
#define EVFILT_AIO (-3) /* attached to aio requests */
#define EVFILT_VNODE (-4) /* attached to vnodes */
#define EVFILT_PROC (-5) /* attached to struct process */
#define EVFILT_SIGNAL (-6) /* attached to struct process */
#define EVFILT_TIMER (-7) /* timers */
#define EVFILT_DEVICE (-8) /* devices */
#define EVFILT_EXCEPT (-9) /* exceptional conditions */
#define EVFILT_SYSCOUNT 9
#define EV_SET(kevp, a, b, c, d, e, f) do { \
struct kevent *__kevp = (kevp); \
(__kevp)->ident = (a); \
(__kevp)->filter = (b); \
(__kevp)->flags = (c); \
(__kevp)->fflags = (d); \
(__kevp)->data = (e); \
(__kevp)->udata = (f); \
} while(0)
struct kevent {
__uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
unsigned short flags; /* action flags for kqueue */
unsigned int fflags; /* filter flag value */
__int64_t data; /* filter data value */
void *udata; /* opaque user data identifier */
};
/* actions */
#define EV_ADD 0x0001 /* add event to kq (implies enable) */
#define EV_DELETE 0x0002 /* delete event from kq */
#define EV_ENABLE 0x0004 /* enable event */
#define EV_DISABLE 0x0008 /* disable event (not reported) */
/* flags */
#define EV_ONESHOT 0x0010 /* only report one occurrence */
#define EV_CLEAR 0x0020 /* clear event state after reporting */
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
#define EV_DISPATCH 0x0080 /* disable event after reporting */
#define EV_SYSFLAGS 0xf800 /* reserved by system */
#define EV_FLAG1 0x2000 /* filter-specific flag */
/* returned values */
#define EV_EOF 0x8000 /* EOF detected */
#define EV_ERROR 0x4000 /* error, data contains errno */
/*
* data/hint flags for EVFILT_{READ|WRITE}, shared with userspace
*/
#define NOTE_LOWAT 0x0001 /* low water mark */
#define NOTE_EOF 0x0002 /* return on EOF */
/*
* data/hint flags for EVFILT_EXCEPT, shared with userspace and with
* EVFILT_{READ|WRITE}
*/
#define NOTE_OOB 0x0004 /* OOB data on a socket */
/*
* data/hint flags for EVFILT_VNODE, shared with userspace
*/
#define NOTE_DELETE 0x0001 /* vnode was removed */
#define NOTE_WRITE 0x0002 /* data contents changed */
#define NOTE_EXTEND 0x0004 /* size increased */
#define NOTE_ATTRIB 0x0008 /* attributes changed */
#define NOTE_LINK 0x0010 /* link count changed */
#define NOTE_RENAME 0x0020 /* vnode was renamed */
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */
#define NOTE_TRUNCATE 0x0080 /* vnode was truncated */
/*
* data/hint flags for EVFILT_PROC, shared with userspace
*/
#define NOTE_EXIT 0x80000000 /* process exited */
#define NOTE_FORK 0x40000000 /* process forked */
#define NOTE_EXEC 0x20000000 /* process exec'd */
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
#define NOTE_PDATAMASK 0x000fffff /* mask for pid */
/* additional flags for EVFILT_PROC */
#define NOTE_TRACK 0x00000001 /* follow across forks */
#define NOTE_TRACKERR 0x00000002 /* could not track child */
#define NOTE_CHILD 0x00000004 /* am a child process */
/* data/hint flags for EVFILT_DEVICE, shared with userspace */
#define NOTE_CHANGE 0x00000001 /* device change event */
/*
* This is currently visible to userland to work around broken
* programs which pull in <sys/proc.h> or <sys/selinfo.h>.
*/
#include <sys/queue.h>
struct klistops;
struct knote;
SLIST_HEAD(knlist, knote);
struct klist {
struct knlist kl_list;
const struct klistops *kl_ops;
void *kl_arg;
};
#ifdef _KERNEL
/* kernel-only flags */
#define __EV_SELECT 0x0800 /* match behavior of select */
#define __EV_POLL 0x1000 /* match behavior of poll */
#define __EV_HUP EV_FLAG1 /* device or socket disconnected */
#define EVFILT_MARKER 0xf /* placemarker for tailq */
/*
* hint flag for in-kernel use - must not equal any existing note
*/
#define NOTE_SUBMIT 0x01000000 /* initial knote submission */
#define KNOTE(list, hint) do { \
struct klist *__list = (list); \
if (!klist_empty(__list)) \
knote(__list, hint); \
} while (0)
#define KN_HASHSIZE 64 /* XXX should be tunable */
/*
* Flag indicating hint is a signal. Used by EVFILT_SIGNAL, and also
* shared by EVFILT_PROC (all knotes attached to p->p_klist)
*/
#define NOTE_SIGNAL 0x08000000
/*
* = Event filter interface
*
* == .f_flags
*
* Defines properties of the event filter:
*
* - FILTEROP_ISFD Each knote of this filter is associated
* with a file descriptor.
*
* - FILTEROP_MPSAFE The kqueue subsystem can invoke .f_attach(),
* .f_detach(), .f_modify() and .f_process() without
* the kernel lock.
*
* == .f_attach()
*
* Attaches the knote to the object.
*
* == .f_detach()
*
* Detaches the knote from the object. The object must not use this knote
* for delivering events after this callback has returned.
*
* == .f_event()
*
* Notifies the filter about an event. Called through knote().
*
* == .f_modify()
*
* Modifies the knote with new state from the user.
*
* Returns non-zero if the knote has become active.
*
* == .f_process()
*
* Checks if the event is active and returns non-zero if the event should be
* returned to the user.
*
* If kev is non-NULL and the event is active, the callback should store
* the event's state in kev for delivery to the user.
*
* == Concurrency control
*
* The kqueue subsystem serializes calls of .f_attach(), .f_detach(),
* .f_modify() and .f_process().
*/
#define FILTEROP_ISFD 0x00000001 /* ident == filedescriptor */
#define FILTEROP_MPSAFE 0x00000002 /* safe without kernel lock */
struct filterops {
int f_flags;
int (*f_attach)(struct knote *kn);
void (*f_detach)(struct knote *kn);
int (*f_event)(struct knote *kn, long hint);
int (*f_modify)(struct kevent *kev, struct knote *kn);
int (*f_process)(struct knote *kn, struct kevent *kev);
};
/*
* Locking:
* I immutable after creation
* o object lock
* q kn_kq->kq_lock
*/
struct knote {
SLIST_ENTRY(knote) kn_link; /* for fd */
SLIST_ENTRY(knote) kn_selnext; /* for struct selinfo */
TAILQ_ENTRY(knote) kn_tqe;
struct kqueue *kn_kq; /* [I] which queue we are on */
struct kevent kn_kevent;
int kn_status; /* [q] */
int kn_sfflags; /* [o] saved filter flags */
__int64_t kn_sdata; /* [o] saved data field */
union {
struct file *p_fp; /* file data pointer */
struct process *p_process; /* process pointer */
} kn_ptr;
const struct filterops *kn_fop;
void *kn_hook; /* [o] */
unsigned int kn_pollid; /* [I] */
#define KN_ACTIVE 0x0001 /* event has been triggered */
#define KN_QUEUED 0x0002 /* event is on queue */
#define KN_DISABLED 0x0004 /* event is disabled */
#define KN_DETACHED 0x0008 /* knote is detached */
#define KN_PROCESSING 0x0010 /* knote is being processed */
#define KN_WAITING 0x0020 /* waiting on processing */
#define kn_id kn_kevent.ident /* [I] */
#define kn_filter kn_kevent.filter /* [I] */
#define kn_flags kn_kevent.flags /* [o] */
#define kn_fflags kn_kevent.fflags /* [o] */
#define kn_data kn_kevent.data /* [o] */
#define kn_udata kn_kevent.udata /* [o] */
#define kn_fp kn_ptr.p_fp /* [o] */
};
struct klistops {
void (*klo_assertlk)(void *);
int (*klo_lock)(void *);
void (*klo_unlock)(void *, int);
};
struct kqueue_scan_state {
struct kqueue *kqs_kq; /* kqueue of this scan */
struct knote kqs_start; /* start marker */
struct knote kqs_end; /* end marker */
int kqs_nevent; /* number of events collected */
int kqs_queued; /* if set, end marker is
* in queue */
};
struct mutex;
struct proc;
struct rwlock;
struct timespec;
extern const struct filterops sig_filtops;
extern const struct filterops dead_filtops;
extern const struct klistops socket_klistops;
extern void kqpoll_init(unsigned int);
extern void kqpoll_done(unsigned int);
extern void kqpoll_exit(void);
extern void knote(struct klist *list, long hint);
extern void knote_fdclose(struct proc *p, int fd);
extern void knote_processexit(struct process *);
extern void knote_assign(const struct kevent *, struct knote *);
extern void knote_submit(struct knote *, struct kevent *);
extern void kqueue_init(void);
extern void kqueue_init_percpu(void);
extern int kqueue_register(struct kqueue *kq, struct kevent *kev,
unsigned int pollid, struct proc *p);
extern int kqueue_scan(struct kqueue_scan_state *, int, struct kevent *,
struct timespec *, struct proc *, int *);
extern void kqueue_scan_setup(struct kqueue_scan_state *, struct kqueue *);
extern void kqueue_scan_finish(struct kqueue_scan_state *);
extern int filt_seltrue(struct knote *kn, long hint);
extern int seltrue_kqfilter(dev_t, struct knote *);
extern void klist_init(struct klist *, const struct klistops *, void *);
extern void klist_init_mutex(struct klist *, struct mutex *);
extern void klist_init_rwlock(struct klist *, struct rwlock *);
extern void klist_free(struct klist *);
extern void klist_insert(struct klist *, struct knote *);
extern void klist_insert_locked(struct klist *, struct knote *);
extern void klist_remove(struct klist *, struct knote *);
extern void klist_remove_locked(struct klist *, struct knote *);
extern void klist_invalidate(struct klist *);
static inline int
knote_modify_fn(const struct kevent *kev, struct knote *kn,
int (*f_event)(struct knote *, long))
{
knote_assign(kev, kn);
return ((*f_event)(kn, 0));
}
static inline int
knote_modify(const struct kevent *kev, struct knote *kn)
{
return (knote_modify_fn(kev, kn, kn->kn_fop->f_event));
}
static inline int
knote_process_fn(struct knote *kn, struct kevent *kev,
int (*f_event)(struct knote *, long))
{
int active;
/*
* If called from kqueue_scan(), skip f_event
* when EV_ONESHOT is set, to preserve old behaviour.
*/
if (kev != NULL && (kn->kn_flags & EV_ONESHOT))
active = 1;
else
active = (*f_event)(kn, 0);
if (active)
knote_submit(kn, kev);
return (active);
}
static inline int
knote_process(struct knote *kn, struct kevent *kev)
{
return (knote_process_fn(kn, kev, kn->kn_fop->f_event));
}
static inline int
klist_empty(struct klist *klist)
{
return (SLIST_EMPTY(&klist->kl_list));
}
#else /* !_KERNEL */
#include <sys/cdefs.h>
struct timespec;
__BEGIN_DECLS
int kqueue(void);
int kevent(int kq, const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents,
const struct timespec *timeout);
__END_DECLS
#endif /* !_KERNEL */
#endif /* !_SYS_EVENT_H_ */
850
850
851
273
851
842
850
850
851
20
851
851
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/* $OpenBSD: siphash.c,v 1.5 2018/01/05 19:05:09 mikeb Exp $ */
/*-
* Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d
* are the number of compression rounds and the number of finalization rounds.
* A compression round is identical to a finalization round and this round
* function is called SipRound. Given a 128-bit key k and a (possibly empty)
* byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m).
*
* Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18,
* by Jean-Philippe Aumasson and Daniel J. Bernstein,
* Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa
* https://131002.net/siphash/siphash.pdf
* https://131002.net/siphash/
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <crypto/siphash.h>
static void SipHash_CRounds(SIPHASH_CTX *, int);
static void SipHash_Rounds(SIPHASH_CTX *, int);
void
SipHash_Init(SIPHASH_CTX *ctx, const SIPHASH_KEY *key)
{
uint64_t k0, k1;
k0 = lemtoh64(&key->k0);
k1 = lemtoh64(&key->k1);
ctx->v[0] = 0x736f6d6570736575ULL ^ k0;
ctx->v[1] = 0x646f72616e646f6dULL ^ k1;
ctx->v[2] = 0x6c7967656e657261ULL ^ k0;
ctx->v[3] = 0x7465646279746573ULL ^ k1;
memset(ctx->buf, 0, sizeof(ctx->buf));
ctx->bytes = 0;
}
void
SipHash_Update(SIPHASH_CTX *ctx, int rc, int rf, const void *src, size_t len)
{
const uint8_t *ptr = src;
size_t left, used;
if (len == 0)
return;
used = ctx->bytes % sizeof(ctx->buf);
ctx->bytes += len;
if (used > 0) {
left = sizeof(ctx->buf) - used;
if (len >= left) {
memcpy(&ctx->buf[used], ptr, left);
SipHash_CRounds(ctx, rc);
len -= left;
ptr += left;
} else {
memcpy(&ctx->buf[used], ptr, len);
return;
}
}
while (len >= sizeof(ctx->buf)) {
memcpy(ctx->buf, ptr, sizeof(ctx->buf));
SipHash_CRounds(ctx, rc);
len -= sizeof(ctx->buf);
ptr += sizeof(ctx->buf);
}
if (len > 0)
memcpy(ctx->buf, ptr, len);
}
void
SipHash_Final(void *dst, SIPHASH_CTX *ctx, int rc, int rf)
{
uint64_t r;
htolem64(&r, SipHash_End(ctx, rc, rf));
memcpy(dst, &r, sizeof r);
}
uint64_t
SipHash_End(SIPHASH_CTX *ctx, int rc, int rf)
{
uint64_t r;
size_t left, used;
used = ctx->bytes % sizeof(ctx->buf);
left = sizeof(ctx->buf) - used;
memset(&ctx->buf[used], 0, left - 1);
ctx->buf[7] = ctx->bytes;
SipHash_CRounds(ctx, rc);
ctx->v[2] ^= 0xff;
SipHash_Rounds(ctx, rf);
r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]);
explicit_bzero(ctx, sizeof(*ctx));
return (r);
}
uint64_t
SipHash(const SIPHASH_KEY *key, int rc, int rf, const void *src, size_t len)
{
SIPHASH_CTX ctx;
SipHash_Init(&ctx, key);
SipHash_Update(&ctx, rc, rf, src, len);
return (SipHash_End(&ctx, rc, rf));
}
#define SIP_ROTL(x, b) ((x) << (b)) | ( (x) >> (64 - (b)))
static void
SipHash_Rounds(SIPHASH_CTX *ctx, int rounds)
{
while (rounds--) {
ctx->v[0] += ctx->v[1];
ctx->v[2] += ctx->v[3];
ctx->v[1] = SIP_ROTL(ctx->v[1], 13);
ctx->v[3] = SIP_ROTL(ctx->v[3], 16);
ctx->v[1] ^= ctx->v[0];
ctx->v[3] ^= ctx->v[2];
ctx->v[0] = SIP_ROTL(ctx->v[0], 32);
ctx->v[2] += ctx->v[1];
ctx->v[0] += ctx->v[3];
ctx->v[1] = SIP_ROTL(ctx->v[1], 17);
ctx->v[3] = SIP_ROTL(ctx->v[3], 21);
ctx->v[1] ^= ctx->v[2];
ctx->v[3] ^= ctx->v[0];
ctx->v[2] = SIP_ROTL(ctx->v[2], 32);
}
}
static void
SipHash_CRounds(SIPHASH_CTX *ctx, int rounds)
{
uint64_t m = lemtoh64((uint64_t *)ctx->buf);
ctx->v[3] ^= m;
SipHash_Rounds(ctx, rounds);
ctx->v[0] ^= m;
}
8
17
16
1
10
16
8
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
/* $OpenBSD: if_vlan.c,v 1.210 2022/08/10 09:01:48 mvs Exp $ */
/*
* Copyright 1998 Massachusetts Institute of Technology
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that both the above copyright notice and this
* permission notice appear in all copies, that both the above
* copyright notice and this permission notice appear in all
* supporting documentation, and that the name of M.I.T. not be used
* in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission. M.I.T. makes
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
* SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/net/if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp $
*/
/*
* if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
* This is sort of sneaky in the implementation, since
* we need to pretend to be enough of an Ethernet implementation
* to make arp work. The way we do this is by telling everyone
* that we are an Ethernet, and then catch the packets that
* ether_output() left on our output queue when it calls
* if_start(), rewrite them for use by the real outgoing interface,
* and ask it to send them.
*
* Some devices support 802.1Q tag insertion in firmware. The
* vlan interface behavior changes when the IFCAP_VLAN_HWTAGGING
* capability is set on the parent. In this case, vlan_start()
* will not modify the ethernet header.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <sys/rwlock.h>
#include <sys/percpu.h>
#include <sys/refcnt.h>
#include <sys/smr.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_vlan_var.h>
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
struct vlan_mc_entry {
LIST_ENTRY(vlan_mc_entry) mc_entries;
union {
struct ether_multi *mcu_enm;
} mc_u;
#define mc_enm mc_u.mcu_enm
struct sockaddr_storage mc_addr;
};
struct vlan_softc {
struct arpcom sc_ac;
#define sc_if sc_ac.ac_if
unsigned int sc_dead;
unsigned int sc_ifidx0; /* parent interface */
int sc_txprio;
int sc_rxprio;
uint16_t sc_proto; /* encapsulation ethertype */
uint16_t sc_tag;
uint16_t sc_type; /* non-standard ethertype or 0x8100 */
LIST_HEAD(__vlan_mchead, vlan_mc_entry)
sc_mc_listhead;
SMR_SLIST_ENTRY(vlan_softc) sc_list;
int sc_flags;
struct refcnt sc_refcnt;
struct task sc_ltask;
struct task sc_dtask;
};
SMR_SLIST_HEAD(vlan_list, vlan_softc);
#define IFVF_PROMISC 0x01 /* the parent should be made promisc */
#define IFVF_LLADDR 0x02 /* don't inherit the parents mac */
#define TAG_HASH_BITS 5
#define TAG_HASH_SIZE (1 << TAG_HASH_BITS)
#define TAG_HASH_MASK (TAG_HASH_SIZE - 1)
#define TAG_HASH(tag) (tag & TAG_HASH_MASK)
struct vlan_list *vlan_tagh, *svlan_tagh;
struct rwlock vlan_tagh_lk = RWLOCK_INITIALIZER("vlantag");
void vlanattach(int count);
int vlan_clone_create(struct if_clone *, int);
int vlan_clone_destroy(struct ifnet *);
int vlan_enqueue(struct ifnet *, struct mbuf *);
void vlan_start(struct ifqueue *ifq);
int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
int vlan_up(struct vlan_softc *);
int vlan_down(struct vlan_softc *);
void vlan_ifdetach(void *);
void vlan_link_hook(void *);
void vlan_link_state(struct vlan_softc *, u_char, uint64_t);
int vlan_set_vnetid(struct vlan_softc *, uint16_t);
int vlan_set_parent(struct vlan_softc *, const char *);
int vlan_del_parent(struct vlan_softc *);
int vlan_inuse(uint16_t, unsigned int, uint16_t);
int vlan_inuse_locked(uint16_t, unsigned int, uint16_t);
int vlan_multi_add(struct vlan_softc *, struct ifreq *);
int vlan_multi_del(struct vlan_softc *, struct ifreq *);
void vlan_multi_apply(struct vlan_softc *, struct ifnet *, u_long);
void vlan_multi_free(struct vlan_softc *);
int vlan_media_get(struct vlan_softc *, struct ifreq *);
int vlan_iff(struct vlan_softc *);
int vlan_setlladdr(struct vlan_softc *, struct ifreq *);
int vlan_set_compat(struct ifnet *, struct ifreq *);
int vlan_get_compat(struct ifnet *, struct ifreq *);
struct if_clone vlan_cloner =
IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
struct if_clone svlan_cloner =
IF_CLONE_INITIALIZER("svlan", vlan_clone_create, vlan_clone_destroy);
void
vlanattach(int count)
{
unsigned int i;
/* Normal VLAN */
vlan_tagh = mallocarray(TAG_HASH_SIZE, sizeof(*vlan_tagh),
M_DEVBUF, M_NOWAIT);
if (vlan_tagh == NULL)
panic("vlanattach: hashinit");
/* Service-VLAN for QinQ/802.1ad provider bridges */
svlan_tagh = mallocarray(TAG_HASH_SIZE, sizeof(*svlan_tagh),
M_DEVBUF, M_NOWAIT);
if (svlan_tagh == NULL)
panic("vlanattach: hashinit");
for (i = 0; i < TAG_HASH_SIZE; i++) {
SMR_SLIST_INIT(&vlan_tagh[i]);
SMR_SLIST_INIT(&svlan_tagh[i]);
}
if_clone_attach(&vlan_cloner);
if_clone_attach(&svlan_cloner);
}
int
vlan_clone_create(struct if_clone *ifc, int unit)
{
struct vlan_softc *sc;
struct ifnet *ifp;
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
sc->sc_dead = 0;
LIST_INIT(&sc->sc_mc_listhead);
task_set(&sc->sc_ltask, vlan_link_hook, sc);
task_set(&sc->sc_dtask, vlan_ifdetach, sc);
ifp = &sc->sc_if;
ifp->if_softc = sc;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
unit);
/* NB: flags are not set here */
/* NB: mtu is not set here */
/* Special handling for the IEEE 802.1ad QinQ variant */
if (strcmp("svlan", ifc->ifc_name) == 0)
sc->sc_type = ETHERTYPE_QINQ;
else
sc->sc_type = ETHERTYPE_VLAN;
refcnt_init(&sc->sc_refcnt);
sc->sc_txprio = IF_HDRPRIO_PACKET;
sc->sc_rxprio = IF_HDRPRIO_OUTER;
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
ifp->if_xflags = IFXF_CLONED|IFXF_MPSAFE;
ifp->if_qstart = vlan_start;
ifp->if_enqueue = vlan_enqueue;
ifp->if_ioctl = vlan_ioctl;
ifp->if_hardmtu = 0xffff;
ifp->if_link_state = LINK_STATE_DOWN;
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
ifp->if_hdrlen = EVL_ENCAPLEN;
return (0);
}
int
vlan_clone_destroy(struct ifnet *ifp)
{
struct vlan_softc *sc = ifp->if_softc;
NET_LOCK();
sc->sc_dead = 1;
if (ISSET(ifp->if_flags, IFF_RUNNING))
vlan_down(sc);
NET_UNLOCK();
ether_ifdetach(ifp);
if_detach(ifp);
smr_barrier();
refcnt_finalize(&sc->sc_refcnt, "vlanrefs");
vlan_multi_free(sc);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
void
vlan_transmit(struct vlan_softc *sc, struct ifnet *ifp0, struct mbuf *m)
{
struct ifnet *ifp = &sc->sc_if;
int txprio = sc->sc_txprio;
uint8_t prio;
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif /* NBPFILTER > 0 */
prio = (txprio == IF_HDRPRIO_PACKET) ?
m->m_pkthdr.pf.prio : txprio;
/* IEEE 802.1p has prio 0 and 1 swapped */
if (prio <= 1)
prio = !prio;
/*
* If the underlying interface cannot do VLAN tag insertion
* itself, create an encapsulation header.
*/
if ((ifp0->if_capabilities & IFCAP_VLAN_HWTAGGING) &&
(sc->sc_type == ETHERTYPE_VLAN)) {
m->m_pkthdr.ether_vtag = sc->sc_tag +
(prio << EVL_PRIO_BITS);
m->m_flags |= M_VLANTAG;
} else {
m = vlan_inject(m, sc->sc_type, sc->sc_tag |
(prio << EVL_PRIO_BITS));
if (m == NULL) {
counters_inc(ifp->if_counters, ifc_oerrors);
return;
}
}
if (if_enqueue(ifp0, m))
counters_inc(ifp->if_counters, ifc_oerrors);
}
int
vlan_enqueue(struct ifnet *ifp, struct mbuf *m)
{
struct ifnet *ifp0;
struct vlan_softc *sc;
int error = 0;
if (!ifq_is_priq(&ifp->if_snd))
return (if_enqueue_ifq(ifp, m));
sc = ifp->if_softc;
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) {
m_freem(m);
error = ENETDOWN;
} else {
counters_pkt(ifp->if_counters,
ifc_opackets, ifc_obytes, m->m_pkthdr.len);
vlan_transmit(sc, ifp0, m);
}
if_put(ifp0);
return (error);
}
void
vlan_start(struct ifqueue *ifq)
{
struct ifnet *ifp = ifq->ifq_if;
struct vlan_softc *sc = ifp->if_softc;
struct ifnet *ifp0;
struct mbuf *m;
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) {
ifq_purge(ifq);
goto leave;
}
while ((m = ifq_dequeue(ifq)) != NULL)
vlan_transmit(sc, ifp0, m);
leave:
if_put(ifp0);
}
struct mbuf *
vlan_strip(struct mbuf *m)
{
if (ISSET(m->m_flags, M_VLANTAG)) {
CLR(m->m_flags, M_VLANTAG);
} else {
struct ether_vlan_header *evl;
evl = mtod(m, struct ether_vlan_header *);
memmove((caddr_t)evl + EVL_ENCAPLEN, evl,
offsetof(struct ether_vlan_header, evl_encap_proto));
m_adj(m, EVL_ENCAPLEN);
}
return (m);
}
struct mbuf *
vlan_inject(struct mbuf *m, uint16_t type, uint16_t tag)
{
struct ether_vlan_header evh;
m_copydata(m, 0, ETHER_HDR_LEN, &evh);
evh.evl_proto = evh.evl_encap_proto;
evh.evl_encap_proto = htons(type);
evh.evl_tag = htons(tag);
m_adj(m, ETHER_HDR_LEN);
M_PREPEND(m, sizeof(evh) + ETHER_ALIGN, M_DONTWAIT);
if (m == NULL)
return (NULL);
m_adj(m, ETHER_ALIGN);
m_copyback(m, 0, sizeof(evh), &evh, M_NOWAIT);
CLR(m->m_flags, M_VLANTAG);
return (m);
}
struct mbuf *
vlan_input(struct ifnet *ifp0, struct mbuf *m, unsigned int *sdelim)
{
struct vlan_softc *sc;
struct ifnet *ifp;
struct ether_vlan_header *evl;
struct vlan_list *tagh, *list;
uint16_t vtag, tag;
uint16_t etype;
int rxprio;
if (m->m_flags & M_VLANTAG) {
vtag = m->m_pkthdr.ether_vtag;
etype = ETHERTYPE_VLAN;
tagh = vlan_tagh;
} else {
if (m->m_len < sizeof(*evl)) {
m = m_pullup(m, sizeof(*evl));
if (m == NULL)
return (NULL);
}
evl = mtod(m, struct ether_vlan_header *);
vtag = bemtoh16(&evl->evl_tag);
etype = bemtoh16(&evl->evl_encap_proto);
switch (etype) {
case ETHERTYPE_VLAN:
tagh = vlan_tagh;
break;
case ETHERTYPE_QINQ:
tagh = svlan_tagh;
break;
default:
panic("%s: unexpected etype 0x%04x", __func__, etype);
/* NOTREACHED */
}
}
tag = EVL_VLANOFTAG(vtag);
list = &tagh[TAG_HASH(tag)];
smr_read_enter();
SMR_SLIST_FOREACH(sc, list, sc_list) {
if (ifp0->if_index == sc->sc_ifidx0 && tag == sc->sc_tag &&
etype == sc->sc_type) {
refcnt_take(&sc->sc_refcnt);
break;
}
}
smr_read_leave();
if (sc == NULL) {
/* VLAN 0 Priority Tagging */
if (tag == 0 && etype == ETHERTYPE_VLAN) {
struct ether_header *eh;
/* XXX we should actually use the prio value? */
m = vlan_strip(m);
eh = mtod(m, struct ether_header *);
if (eh->ether_type == htons(ETHERTYPE_VLAN) ||
eh->ether_type == htons(ETHERTYPE_QINQ)) {
m_freem(m);
return (NULL);
}
} else
*sdelim = 1;
return (m); /* decline */
}
ifp = &sc->sc_if;
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
m_freem(m);
goto leave;
}
/*
* Having found a valid vlan interface corresponding to
* the given source interface and vlan tag, remove the
* encapsulation.
*/
m = vlan_strip(m);
rxprio = sc->sc_rxprio;
switch (rxprio) {
case IF_HDRPRIO_PACKET:
break;
case IF_HDRPRIO_OUTER:
m->m_pkthdr.pf.prio = EVL_PRIOFTAG(m->m_pkthdr.ether_vtag);
/* IEEE 802.1p has prio 0 and 1 swapped */
if (m->m_pkthdr.pf.prio <= 1)
m->m_pkthdr.pf.prio = !m->m_pkthdr.pf.prio;
break;
default:
m->m_pkthdr.pf.prio = rxprio;
break;
}
if_vinput(ifp, m);
leave:
refcnt_rele_wake(&sc->sc_refcnt);
return (NULL);
}
int
vlan_up(struct vlan_softc *sc)
{
struct vlan_list *tagh, *list;
struct ifnet *ifp = &sc->sc_if;
struct ifnet *ifp0;
int error = 0;
unsigned int hardmtu;
KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
tagh = sc->sc_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
list = &tagh[TAG_HASH(sc->sc_tag)];
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 == NULL)
return (ENXIO);
/* check vlan will work on top of the parent */
if (ifp0->if_type != IFT_ETHER) {
error = EPROTONOSUPPORT;
goto put;
}
hardmtu = ifp0->if_hardmtu;
if (!ISSET(ifp0->if_capabilities, IFCAP_VLAN_MTU))
hardmtu -= EVL_ENCAPLEN;
if (ifp->if_mtu > hardmtu) {
error = ENOBUFS;
goto put;
}
/* parent is fine, let's prepare the sc to handle packets */
ifp->if_hardmtu = hardmtu;
SET(ifp->if_flags, ifp0->if_flags & IFF_SIMPLEX);
if (ISSET(sc->sc_flags, IFVF_PROMISC)) {
error = ifpromisc(ifp0, 1);
if (error != 0)
goto scrub;
}
/*
* Note: In cases like vio(4) and em(4) where the offsets of the
* csum can be freely defined, we could actually do csum offload
* for VLAN and QINQ packets.
*/
if (sc->sc_type != ETHERTYPE_VLAN) {
/*
* Hardware offload only works with the default VLAN
* ethernet type (0x8100).
*/
ifp->if_capabilities = 0;
} else if (ISSET(ifp0->if_capabilities, IFCAP_VLAN_HWTAGGING)) {
/*
* Chips that can do hardware-assisted VLAN encapsulation, can
* calculate the correct checksum for VLAN tagged packets.
*/
ifp->if_capabilities = ifp0->if_capabilities & IFCAP_CSUM_MASK;
}
/* commit the sc */
error = rw_enter(&vlan_tagh_lk, RW_WRITE | RW_INTR);
if (error != 0)
goto unpromisc;
error = vlan_inuse_locked(sc->sc_type, sc->sc_ifidx0, sc->sc_tag);
if (error != 0)
goto leave;
SMR_SLIST_INSERT_HEAD_LOCKED(list, sc, sc_list);
rw_exit(&vlan_tagh_lk);
/* Register callback for physical link state changes */
if_linkstatehook_add(ifp0, &sc->sc_ltask);
/* Register callback if parent wants to unregister */
if_detachhook_add(ifp0, &sc->sc_dtask);
/* configure the parent to handle packets for this vlan */
vlan_multi_apply(sc, ifp0, SIOCADDMULTI);
/* we're running now */
SET(ifp->if_flags, IFF_RUNNING);
vlan_link_state(sc, ifp0->if_link_state, ifp0->if_baudrate);
if_put(ifp0);
return (ENETRESET);
leave:
rw_exit(&vlan_tagh_lk);
unpromisc:
if (ISSET(sc->sc_flags, IFVF_PROMISC))
(void)ifpromisc(ifp0, 0); /* XXX */
scrub:
ifp->if_capabilities = 0;
CLR(ifp->if_flags, IFF_SIMPLEX);
ifp->if_hardmtu = 0xffff;
put:
if_put(ifp0);
return (error);
}
int
vlan_down(struct vlan_softc *sc)
{
struct vlan_list *tagh, *list;
struct ifnet *ifp = &sc->sc_if;
struct ifnet *ifp0;
tagh = sc->sc_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
list = &tagh[TAG_HASH(sc->sc_tag)];
KASSERT(ISSET(ifp->if_flags, IFF_RUNNING));
vlan_link_state(sc, LINK_STATE_DOWN, 0);
CLR(ifp->if_flags, IFF_RUNNING);
ifq_barrier(&ifp->if_snd);
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 != NULL) {
if (ISSET(sc->sc_flags, IFVF_PROMISC))
ifpromisc(ifp0, 0);
vlan_multi_apply(sc, ifp0, SIOCDELMULTI);
if_detachhook_del(ifp0, &sc->sc_dtask);
if_linkstatehook_del(ifp0, &sc->sc_ltask);
}
if_put(ifp0);
rw_enter_write(&vlan_tagh_lk);
SMR_SLIST_REMOVE_LOCKED(list, sc, vlan_softc, sc_list);
rw_exit_write(&vlan_tagh_lk);
ifp->if_capabilities = 0;
CLR(ifp->if_flags, IFF_SIMPLEX);
ifp->if_hardmtu = 0xffff;
return (0);
}
void
vlan_ifdetach(void *v)
{
struct vlan_softc *sc = v;
struct ifnet *ifp = &sc->sc_if;
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
vlan_down(sc);
CLR(ifp->if_flags, IFF_UP);
}
sc->sc_ifidx0 = 0;
}
void
vlan_link_hook(void *v)
{
struct vlan_softc *sc = v;
struct ifnet *ifp0;
u_char link = LINK_STATE_DOWN;
uint64_t baud = 0;
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 != NULL) {
link = ifp0->if_link_state;
baud = ifp0->if_baudrate;
}
if_put(ifp0);
vlan_link_state(sc, link, baud);
}
void
vlan_link_state(struct vlan_softc *sc, u_char link, uint64_t baud)
{
if (sc->sc_if.if_link_state == link)
return;
sc->sc_if.if_link_state = link;
sc->sc_if.if_baudrate = baud;
if_link_state_change(&sc->sc_if);
}
int
vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct vlan_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct if_parent *parent = (struct if_parent *)data;
struct ifnet *ifp0;
uint16_t tag;
int error = 0;
if (sc->sc_dead)
return (ENXIO);
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
/* FALLTHROUGH */
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = vlan_up(sc);
else
error = ENETRESET;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = vlan_down(sc);
}
break;
case SIOCSVNETID:
if (ifr->ifr_vnetid < EVL_VLID_MIN ||
ifr->ifr_vnetid > EVL_VLID_MAX) {
error = EINVAL;
break;
}
tag = ifr->ifr_vnetid;
if (tag == sc->sc_tag)
break;
error = vlan_set_vnetid(sc, tag);
break;
case SIOCGVNETID:
if (sc->sc_tag == EVL_VLID_NULL)
error = EADDRNOTAVAIL;
else
ifr->ifr_vnetid = (int64_t)sc->sc_tag;
break;
case SIOCDVNETID:
error = vlan_set_vnetid(sc, 0);
break;
case SIOCSIFPARENT:
error = vlan_set_parent(sc, parent->ifp_parent);
break;
case SIOCGIFPARENT:
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 == NULL)
error = EADDRNOTAVAIL;
else {
memcpy(parent->ifp_parent, ifp0->if_xname,
sizeof(parent->ifp_parent));
}
if_put(ifp0);
break;
case SIOCDIFPARENT:
error = vlan_del_parent(sc);
break;
case SIOCADDMULTI:
error = vlan_multi_add(sc, ifr);
break;
case SIOCDELMULTI:
error = vlan_multi_del(sc, ifr);
break;
case SIOCGIFMEDIA:
error = vlan_media_get(sc, ifr);
break;
case SIOCSIFMEDIA:
error = ENOTTY;
break;
case SIOCSIFLLADDR:
error = vlan_setlladdr(sc, ifr);
break;
case SIOCSETVLAN:
error = vlan_set_compat(ifp, ifr);
break;
case SIOCGETVLAN:
error = vlan_get_compat(ifp, ifr);
break;
case SIOCSTXHPRIO:
error = if_txhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_txprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_txprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_rxprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_rxprio;
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
break;
}
if (error == ENETRESET)
error = vlan_iff(sc);
return error;
}
int
vlan_iff(struct vlan_softc *sc)
{
struct ifnet *ifp0;
int promisc = 0;
int error = 0;
if (ISSET(sc->sc_if.if_flags, IFF_PROMISC) ||
ISSET(sc->sc_flags, IFVF_LLADDR))
promisc = IFVF_PROMISC;
if (ISSET(sc->sc_flags, IFVF_PROMISC) == promisc)
return (0);
if (ISSET(sc->sc_if.if_flags, IFF_RUNNING)) {
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 != NULL)
error = ifpromisc(ifp0, promisc);
if_put(ifp0);
}
if (error == 0) {
CLR(sc->sc_flags, IFVF_PROMISC);
SET(sc->sc_flags, promisc);
}
return (error);
}
int
vlan_setlladdr(struct vlan_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp = &sc->sc_if;
struct ifnet *ifp0;
uint8_t lladdr[ETHER_ADDR_LEN];
int flag;
memcpy(lladdr, ifr->ifr_addr.sa_data, sizeof(lladdr));
/* setting the mac addr to 00:00:00:00:00:00 means reset lladdr */
if (memcmp(lladdr, etheranyaddr, sizeof(lladdr)) == 0) {
ifp0 = if_get(sc->sc_ifidx0);
if (ifp0 != NULL)
memcpy(lladdr, LLADDR(ifp0->if_sadl), sizeof(lladdr));
if_put(ifp0);
flag = 0;
} else
flag = IFVF_LLADDR;
if (memcmp(lladdr, LLADDR(ifp->if_sadl), sizeof(lladdr)) == 0 &&
ISSET(sc->sc_flags, IFVF_LLADDR) == flag) {
/* nop */
return (0);
}
/* commit */
if_setlladdr(ifp, lladdr);
CLR(sc->sc_flags, IFVF_LLADDR);
SET(sc->sc_flags, flag);
return (ENETRESET);
}
int
vlan_set_vnetid(struct vlan_softc *sc, uint16_t tag)
{
struct ifnet *ifp = &sc->sc_if;
struct vlan_list *tagh, *list;
u_char link = ifp->if_link_state;
uint64_t baud = ifp->if_baudrate;
int error;
tagh = sc->sc_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
if (ISSET(ifp->if_flags, IFF_RUNNING) && LINK_STATE_IS_UP(link))
vlan_link_state(sc, LINK_STATE_DOWN, 0);
error = rw_enter(&vlan_tagh_lk, RW_WRITE);
if (error != 0)
return (error);
error = vlan_inuse_locked(sc->sc_type, sc->sc_ifidx0, tag);
if (error != 0)
goto unlock;
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
list = &tagh[TAG_HASH(sc->sc_tag)];
SMR_SLIST_REMOVE_LOCKED(list, sc, vlan_softc, sc_list);
sc->sc_tag = tag;
list = &tagh[TAG_HASH(sc->sc_tag)];
SMR_SLIST_INSERT_HEAD_LOCKED(list, sc, sc_list);
} else
sc->sc_tag = tag;
unlock:
rw_exit(&vlan_tagh_lk);
if (ISSET(ifp->if_flags, IFF_RUNNING) && LINK_STATE_IS_UP(link))
vlan_link_state(sc, link, baud);
return (error);
}
int
vlan_set_parent(struct vlan_softc *sc, const char *parent)
{
struct ifnet *ifp = &sc->sc_if;
struct ifnet *ifp0;
int error = 0;
ifp0 = if_unit(parent);
if (ifp0 == NULL)
return (EINVAL);
if (ifp0->if_type != IFT_ETHER) {
error = EPROTONOSUPPORT;
goto put;
}
if (sc->sc_ifidx0 == ifp0->if_index) {
/* nop */
goto put;
}
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
goto put;
}
error = vlan_inuse(sc->sc_type, ifp0->if_index, sc->sc_tag);
if (error != 0)
goto put;
/* commit */
sc->sc_ifidx0 = ifp0->if_index;
if (!ISSET(sc->sc_flags, IFVF_LLADDR))
if_setlladdr(ifp, LLADDR(ifp0->if_sadl));
put:
if_put(ifp0);
return (error);
}
int
vlan_del_parent(struct vlan_softc *sc)
{
struct ifnet *ifp = &sc->sc_if;
if (ISSET(ifp->if_flags, IFF_RUNNING))
return (EBUSY);
/* commit */
sc->sc_ifidx0 = 0;
if (!ISSET(sc->sc_flags, IFVF_LLADDR))
if_setlladdr(ifp, etheranyaddr);
return (0);
}
int
vlan_set_compat(struct ifnet *ifp, struct ifreq *ifr)
{
struct vlanreq vlr;
struct ifreq req;
struct if_parent parent;
int error;
error = suser(curproc);
if (error != 0)
return (error);
error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
if (error != 0)
return (error);
if (vlr.vlr_parent[0] == '\0')
return (vlan_ioctl(ifp, SIOCDIFPARENT, (caddr_t)ifr));
memset(&req, 0, sizeof(req));
memcpy(req.ifr_name, ifp->if_xname, sizeof(req.ifr_name));
req.ifr_vnetid = vlr.vlr_tag;
error = vlan_ioctl(ifp, SIOCSVNETID, (caddr_t)&req);
if (error != 0)
return (error);
memset(&parent, 0, sizeof(parent));
memcpy(parent.ifp_name, ifp->if_xname, sizeof(parent.ifp_name));
memcpy(parent.ifp_parent, vlr.vlr_parent, sizeof(parent.ifp_parent));
error = vlan_ioctl(ifp, SIOCSIFPARENT, (caddr_t)&parent);
if (error != 0)
return (error);
memset(&req, 0, sizeof(req));
memcpy(req.ifr_name, ifp->if_xname, sizeof(req.ifr_name));
SET(ifp->if_flags, IFF_UP);
return (vlan_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&req));
}
int
vlan_get_compat(struct ifnet *ifp, struct ifreq *ifr)
{
struct vlan_softc *sc = ifp->if_softc;
struct vlanreq vlr;
struct ifnet *p;
memset(&vlr, 0, sizeof(vlr));
p = if_get(sc->sc_ifidx0);
if (p != NULL)
memcpy(vlr.vlr_parent, p->if_xname, sizeof(vlr.vlr_parent));
if_put(p);
vlr.vlr_tag = sc->sc_tag;
return (copyout(&vlr, ifr->ifr_data, sizeof(vlr)));
}
/*
* do a quick check of up and running vlans for existing configurations.
*
* NOTE: this does allow the same config on down vlans, but vlan_up()
* will catch them.
*/
int
vlan_inuse(uint16_t type, unsigned int ifidx, uint16_t tag)
{
int error = 0;
error = rw_enter(&vlan_tagh_lk, RW_READ | RW_INTR);
if (error != 0)
return (error);
error = vlan_inuse_locked(type, ifidx, tag);
rw_exit(&vlan_tagh_lk);
return (error);
}
int
vlan_inuse_locked(uint16_t type, unsigned int ifidx, uint16_t tag)
{
struct vlan_list *tagh, *list;
struct vlan_softc *sc;
tagh = type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
list = &tagh[TAG_HASH(tag)];
SMR_SLIST_FOREACH_LOCKED(sc, list, sc_list) {
if (sc->sc_tag == tag &&
sc->sc_type == type && /* wat */
sc->sc_ifidx0 == ifidx)
return (EADDRINUSE);
}
return (0);
}
int
vlan_multi_add(struct vlan_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp0;
struct vlan_mc_entry *mc;
uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
int error;
error = ether_addmulti(ifr, &sc->sc_ac);
if (error != ENETRESET)
return (error);
/*
* This is new multicast address. We have to tell parent
* about it. Also, remember this multicast address so that
* we can delete them on unconfigure.
*/
if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
error = ENOMEM;
goto alloc_failed;
}
/*
* As ether_addmulti() returns ENETRESET, following two
* statement shouldn't fail.
*/
(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, mc->mc_enm);
memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
LIST_INSERT_HEAD(&sc->sc_mc_listhead, mc, mc_entries);
ifp0 = if_get(sc->sc_ifidx0);
error = (ifp0 == NULL) ? 0 :
(*ifp0->if_ioctl)(ifp0, SIOCADDMULTI, (caddr_t)ifr);
if_put(ifp0);
if (error != 0)
goto ioctl_failed;
return (error);
ioctl_failed:
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF, sizeof(*mc));
alloc_failed:
(void)ether_delmulti(ifr, &sc->sc_ac);
return (error);
}
int
vlan_multi_del(struct vlan_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp0;
struct ether_multi *enm;
struct vlan_mc_entry *mc;
uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
int error;
/*
* Find a key to lookup vlan_mc_entry. We have to do this
* before calling ether_delmulti for obvious reason.
*/
if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
return (error);
ETHER_LOOKUP_MULTI(addrlo, addrhi, &sc->sc_ac, enm);
if (enm == NULL)
return (EINVAL);
LIST_FOREACH(mc, &sc->sc_mc_listhead, mc_entries) {
if (mc->mc_enm == enm)
break;
}
/* We won't delete entries we didn't add */
if (mc == NULL)
return (EINVAL);
error = ether_delmulti(ifr, &sc->sc_ac);
if (error != ENETRESET)
return (error);
if (!ISSET(sc->sc_if.if_flags, IFF_RUNNING))
goto forget;
ifp0 = if_get(sc->sc_ifidx0);
error = (ifp0 == NULL) ? 0 :
(*ifp0->if_ioctl)(ifp0, SIOCDELMULTI, (caddr_t)ifr);
if_put(ifp0);
if (error != 0) {
(void)ether_addmulti(ifr, &sc->sc_ac);
return (error);
}
forget:
/* forget about this address */
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF, sizeof(*mc));
return (0);
}
int
vlan_media_get(struct vlan_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp0;
int error;
ifp0 = if_get(sc->sc_ifidx0);
error = (ifp0 == NULL) ? ENOTTY :
(*ifp0->if_ioctl)(ifp0, SIOCGIFMEDIA, (caddr_t)ifr);
if_put(ifp0);
return (error);
}
void
vlan_multi_apply(struct vlan_softc *sc, struct ifnet *ifp0, u_long cmd)
{
struct vlan_mc_entry *mc;
union {
struct ifreq ifreq;
struct {
char ifr_name[IFNAMSIZ];
struct sockaddr_storage ifr_ss;
} ifreq_storage;
} ifreq;
struct ifreq *ifr = &ifreq.ifreq;
memcpy(ifr->ifr_name, ifp0->if_xname, IFNAMSIZ);
LIST_FOREACH(mc, &sc->sc_mc_listhead, mc_entries) {
memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
(void)(*ifp0->if_ioctl)(ifp0, cmd, (caddr_t)ifr);
}
}
void
vlan_multi_free(struct vlan_softc *sc)
{
struct vlan_mc_entry *mc;
while ((mc = LIST_FIRST(&sc->sc_mc_listhead)) != NULL) {
LIST_REMOVE(mc, mc_entries);
free(mc, M_DEVBUF, sizeof(*mc));
}
}
2
2
2
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* $OpenBSD: idgen.c,v 1.8 2020/07/22 13:54:30 tobhe Exp $ */
/*
* Copyright (c) 2008 Damien Miller <djm@mindrot.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* IDGEN32: non-repeating ID generation covering an almost maximal 32-bit
* range.
*
* IDGEN32 is based on public domain SKIP32 by Greg Rose.
*/
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <crypto/idgen.h>
static const u_int8_t idgen32_ftable[256] = {
0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4,
0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e,
0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68,
0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19,
0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b,
0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0,
0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69,
0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20,
0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43,
0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa,
0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,
0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87,
0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b,
0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0,
0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1,
0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5,
0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3,
0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46
};
static u_int16_t
idgen32_g(u_int8_t *key, int k, u_int16_t w)
{
u_int8_t g1, g2, g3, g4, g5, g6;
u_int o = k * 4;
g1 = (w >> 8) & 0xff;
g2 = w & 0xff;
g3 = idgen32_ftable[g2 ^ key[o++ & (IDGEN32_KEYLEN - 1)]] ^ g1;
g4 = idgen32_ftable[g3 ^ key[o++ & (IDGEN32_KEYLEN - 1)]] ^ g2;
g5 = idgen32_ftable[g4 ^ key[o++ & (IDGEN32_KEYLEN - 1)]] ^ g3;
g6 = idgen32_ftable[g5 ^ key[o++ & (IDGEN32_KEYLEN - 1)]] ^ g4;
return (g5 << 8) | g6;
}
static u_int32_t
idgen32_permute(struct idgen32_ctx *ctx, u_int32_t in)
{
u_int i, r;
u_int16_t wl, wr;
wl = (in >> 16) & 0x7fff;
wr = in & 0xffff;
/* Doubled up rounds, with an odd round at the end to swap */
for (i = r = 0; i < IDGEN32_ROUNDS / 2; ++i) {
wr ^= (idgen32_g(ctx->id32_key, r, wl) ^ r);
r++;
wl ^= (idgen32_g(ctx->id32_key, r, wr) ^ r) & 0x7fff;
r++;
}
wr ^= (idgen32_g(ctx->id32_key, r, wl) ^ r);
return (wl << 16) | wr;
}
static void
idgen32_rekey(struct idgen32_ctx *ctx)
{
ctx->id32_counter = 0;
ctx->id32_hibit ^= 0x80000000;
ctx->id32_offset = arc4random();
arc4random_buf(ctx->id32_key, sizeof(ctx->id32_key));
ctx->id32_rekey_time = getuptime() + IDGEN32_REKEY_TIME;
}
void
idgen32_init(struct idgen32_ctx *ctx)
{
bzero(ctx, sizeof(*ctx));
ctx->id32_hibit = arc4random() & 0x80000000;
idgen32_rekey(ctx);
}
u_int32_t
idgen32(struct idgen32_ctx *ctx)
{
u_int32_t ret;
do {
/* Rekey a little early to avoid "card counting" attack */
if (ctx->id32_counter > IDGEN32_REKEY_LIMIT ||
ctx->id32_rekey_time < getuptime())
idgen32_rekey(ctx);
ret = ctx->id32_hibit | idgen32_permute(ctx,
(ctx->id32_offset + ctx->id32_counter++) & 0x7fffffff);
} while (ret == 0); /* Zero IDs are often special, so avoid */
return ret;
}
3
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
/* $OpenBSD: scsiconf.c,v 1.253 2022/04/06 17:39:13 krw Exp $ */
/* $NetBSD: scsiconf.c,v 1.57 1996/05/02 01:09:01 neil Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Originally written by Julian Elischer (julian@tfs.com)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
*
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
*
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
*/
#include "bio.h"
#include "mpath.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/atomic.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
int scsibusmatch(struct device *, void *, void *);
void scsibusattach(struct device *, struct device *, void *);
int scsibusactivate(struct device *, int);
int scsibusdetach(struct device *, int);
int scsibussubmatch(struct device *, void *, void *);
int scsibussubprint(void *, const char *);
#if NBIO > 0
#include <sys/ioctl.h>
#include <sys/scsiio.h>
#include <dev/biovar.h>
int scsibusbioctl(struct device *, u_long, caddr_t);
#endif /* NBIO > 0 */
void scsi_get_target_luns(struct scsibus_softc *, int,
struct scsi_lun_array *);
void scsi_add_link(struct scsi_link *);
void scsi_remove_link(struct scsi_link *);
void scsi_print_link(struct scsi_link *);
int scsi_probe_link(struct scsibus_softc *, int, int, int);
int scsi_activate_link(struct scsi_link *, int);
int scsi_detach_link(struct scsi_link *, int);
int scsi_detach_bus(struct scsibus_softc *, int);
void scsi_devid(struct scsi_link *);
int scsi_devid_pg80(struct scsi_link *);
int scsi_devid_pg83(struct scsi_link *);
int scsi_devid_wwn(struct scsi_link *);
int scsi_activate_bus(struct scsibus_softc *, int);
int scsi_activate_target(struct scsibus_softc *, int, int);
int scsi_activate_lun(struct scsibus_softc *, int, int, int);
int scsi_autoconf = SCSI_AUTOCONF;
const struct cfattach scsibus_ca = {
sizeof(struct scsibus_softc), scsibusmatch, scsibusattach,
scsibusdetach, scsibusactivate
};
struct cfdriver scsibus_cd = {
NULL, "scsibus", DV_DULL
};
struct scsi_quirk_inquiry_pattern {
struct scsi_inquiry_pattern pattern;
u_int16_t quirks;
};
const struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = {
{{T_CDROM, T_REMOV,
"PLEXTOR", "CD-ROM PX-40TS", "1.01"}, SDEV_NOSYNC},
{{T_DIRECT, T_FIXED,
"MICROP ", "1588-15MBSUN0669", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"DEC ", "RZ55 (C) DEC", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"EMULEX ", "MD21/S2 ESDI", "A00"}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"IBMRAID ", "0662S", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"IBM ", "0663H", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"IBM", "0664", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"IBM ", "H3171-S2", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"IBM ", "KZ-C", ""}, SDEV_AUTOSAVE},
/* Broken IBM disk */
{{T_DIRECT, T_FIXED,
"" , "DFRSS2F", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"QUANTUM ", "ELS85S ", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_REMOV,
"iomega", "jaz 1GB", ""}, SDEV_NOTAGS},
{{T_DIRECT, T_FIXED,
"MICROP", "4421-07", ""}, SDEV_NOTAGS},
{{T_DIRECT, T_FIXED,
"SEAGATE", "ST150176LW", "0002"}, SDEV_NOTAGS},
{{T_DIRECT, T_FIXED,
"HP", "C3725S", ""}, SDEV_NOTAGS},
{{T_DIRECT, T_FIXED,
"IBM", "DCAS", ""}, SDEV_NOTAGS},
{{T_SEQUENTIAL, T_REMOV,
"SONY ", "SDT-5000 ", "3."}, SDEV_NOSYNC|SDEV_NOWIDE},
{{T_SEQUENTIAL, T_REMOV,
"WangDAT ", "Model 1300 ", "02.4"}, SDEV_NOSYNC|SDEV_NOWIDE},
{{T_SEQUENTIAL, T_REMOV,
"WangDAT ", "Model 2600 ", "01.7"}, SDEV_NOSYNC|SDEV_NOWIDE},
{{T_SEQUENTIAL, T_REMOV,
"WangDAT ", "Model 3200 ", "02.2"}, SDEV_NOSYNC|SDEV_NOWIDE},
/* ATAPI device quirks */
{{T_CDROM, T_REMOV,
"CR-2801TE", "", "1.07"}, ADEV_NOSENSE},
{{T_CDROM, T_REMOV,
"CREATIVECD3630E", "", "AC101"}, ADEV_NOSENSE},
{{T_CDROM, T_REMOV,
"FX320S", "", "q01"}, ADEV_NOSENSE},
{{T_CDROM, T_REMOV,
"GCD-R580B", "", "1.00"}, ADEV_LITTLETOC},
{{T_CDROM, T_REMOV,
"MATSHITA CR-574", "", "1.02"}, ADEV_NOCAPACITY},
{{T_CDROM, T_REMOV,
"MATSHITA CR-574", "", "1.06"}, ADEV_NOCAPACITY},
{{T_CDROM, T_REMOV,
"Memorex CRW-2642", "", "1.0g"}, ADEV_NOSENSE},
{{T_CDROM, T_REMOV,
"SANYO CRD-256P", "", "1.02"}, ADEV_NOCAPACITY},
{{T_CDROM, T_REMOV,
"SANYO CRD-254P", "", "1.02"}, ADEV_NOCAPACITY},
{{T_CDROM, T_REMOV,
"SANYO CRD-S54P", "", "1.08"}, ADEV_NOCAPACITY},
{{T_CDROM, T_REMOV,
"CD-ROM CDR-S1", "", "1.70"}, ADEV_NOCAPACITY}, /* Sanyo */
{{T_CDROM, T_REMOV,
"CD-ROM CDR-N16", "", "1.25"}, ADEV_NOCAPACITY}, /* Sanyo */
{{T_CDROM, T_REMOV,
"UJDCD8730", "", "1.14"}, ADEV_NODOORLOCK}, /* Acer */
};
int
scsiprint(void *aux, const char *pnp)
{
/* Only "scsibus"es can attach to "scsi"s. */
if (pnp)
printf("scsibus at %s", pnp);
return UNCONF;
}
int
scsibusmatch(struct device *parent, void *match, void *aux)
{
return 1;
}
/*
* The routine called by the adapter boards to get all their
* devices configured in.
*/
void
scsibusattach(struct device *parent, struct device *self, void *aux)
{
struct scsibus_softc *sb = (struct scsibus_softc *)self;
struct scsibus_attach_args *saa = aux;
if (!cold)
scsi_autoconf = 0;
SLIST_INIT(&sb->sc_link_list);
sb->sb_adapter_softc = saa->saa_adapter_softc;
sb->sb_adapter = saa->saa_adapter;
sb->sb_pool = saa->saa_pool;
sb->sb_quirks = saa->saa_quirks;
sb->sb_flags = saa->saa_flags;
sb->sb_openings = saa->saa_openings;
sb->sb_adapter_buswidth = saa->saa_adapter_buswidth;
sb->sb_adapter_target = saa->saa_adapter_target;
sb->sb_luns = saa->saa_luns;
if (sb->sb_adapter_buswidth == 0)
sb->sb_adapter_buswidth = 8;
if (sb->sb_luns == 0)
sb->sb_luns = 8;
printf(": %d targets", sb->sb_adapter_buswidth);
if (sb->sb_adapter_target < sb->sb_adapter_buswidth)
printf(", initiator %d", sb->sb_adapter_target);
if (saa->saa_wwpn != 0x0 && saa->saa_wwnn != 0x0) {
printf(", WWPN %016llx, WWNN %016llx", saa->saa_wwpn,
saa->saa_wwnn);
}
printf("\n");
/* Initialize shared data. */
scsi_init();
SLIST_INIT(&sb->sc_link_list);
#if NBIO > 0
if (bio_register(&sb->sc_dev, scsibusbioctl) != 0)
printf("%s: unable to register bio\n", sb->sc_dev.dv_xname);
#endif /* NBIO > 0 */
scsi_probe_bus(sb);
}
int
scsibusactivate(struct device *dev, int act)
{
struct scsibus_softc *sb = (struct scsibus_softc *)dev;
return scsi_activate_bus(sb, act);
}
int
scsibusdetach(struct device *dev, int type)
{
struct scsibus_softc *sb = (struct scsibus_softc *)dev;
int error;
#if NBIO > 0
bio_unregister(&sb->sc_dev);
#endif /* NBIO > 0 */
error = scsi_detach_bus(sb, type);
if (error != 0)
return error;
KASSERT(SLIST_EMPTY(&sb->sc_link_list));
return 0;
}
int
scsibussubmatch(struct device *parent, void *match, void *aux)
{
struct cfdata *cf = match;
struct scsi_attach_args *sa = aux;
struct scsi_link *link = sa->sa_sc_link;
if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != link->target)
return 0;
if (cf->cf_loc[1] != -1 && cf->cf_loc[1] != link->lun)
return 0;
return (*cf->cf_attach->ca_match)(parent, match, aux);
}
/*
* Print out autoconfiguration information for a subdevice.
*
* This is a slight abuse of 'standard' autoconfiguration semantics,
* because 'print' functions don't normally print the colon and
* device information. However, in this case that's better than
* either printing redundant information before the attach message,
* or having the device driver call a special function to print out
* the standard device information.
*/
int
scsibussubprint(void *aux, const char *pnp)
{
struct scsi_attach_args *sa = aux;
if (pnp != NULL)
printf("%s", pnp);
scsi_print_link(sa->sa_sc_link);
return UNCONF;
}
#if NBIO > 0
int
scsibusbioctl(struct device *dev, u_long cmd, caddr_t addr)
{
struct scsibus_softc *sb = (struct scsibus_softc *)dev;
struct sbioc_device *sdev;
switch (cmd) {
case SBIOCPROBE:
sdev = (struct sbioc_device *)addr;
return scsi_probe(sb, sdev->sd_target, sdev->sd_lun);
case SBIOCDETACH:
sdev = (struct sbioc_device *)addr;
return scsi_detach(sb, sdev->sd_target, sdev->sd_lun, 0);
default:
return ENOTTY;
}
}
#endif /* NBIO > 0 */
int
scsi_activate(struct scsibus_softc *sb, int target, int lun, int act)
{
if (target == -1 && lun == -1)
return scsi_activate_bus(sb, act);
else if (lun == -1)
return scsi_activate_target(sb, target, act);
else
return scsi_activate_lun(sb, target, lun, act);
}
int
scsi_activate_bus(struct scsibus_softc *sb, int act)
{
struct scsi_link *link;
int r, rv = 0;
/* Activate all links on the bus. */
SLIST_FOREACH(link, &sb->sc_link_list, bus_list) {
r = scsi_activate_link(link, act);
if (r)
rv = r;
}
return rv;
}
int
scsi_activate_target(struct scsibus_softc *sb, int target, int act)
{
struct scsi_link *link;
int r, rv = 0;
/* Activate all links on the target. */
SLIST_FOREACH(link, &sb->sc_link_list, bus_list) {
if (link->target == target) {
r = scsi_activate_link(link, act);
if (r)
rv = r;
}
}
return rv;
}
int
scsi_activate_lun(struct scsibus_softc *sb, int target, int lun, int act)
{
struct scsi_link *link;
/* Activate the (target, lun) link.*/
link = scsi_get_link(sb, target, lun);
if (link == NULL)
return 0;
return scsi_activate_link(link, act);
}
int
scsi_activate_link(struct scsi_link *link, int act)
{
struct device *dev;
int rv = 0;
dev = link->device_softc;
switch (act) {
case DVACT_DEACTIVATE:
atomic_setbits_int(&link->state, SDEV_S_DYING);
config_deactivate(dev);
break;
default:
rv = config_suspend(dev, act);
break;
}
return rv;
}
int
scsi_probe(struct scsibus_softc *sb, int target, int lun)
{
if (target == -1 && lun == -1)
return scsi_probe_bus(sb);
else if (lun == -1)
return scsi_probe_target(sb, target);
else
return scsi_probe_lun(sb, target, lun);
}
int
scsi_probe_bus(struct scsibus_softc *sb)
{
int target, r, rv = 0;
/* Probe all possible targets on bus. */
for (target = 0; target < sb->sb_adapter_buswidth; target++) {
r = scsi_probe_target(sb, target);
if (r != 0 && r != EINVAL)
rv = r;
}
return rv;
}
int
scsi_probe_target(struct scsibus_softc *sb, int target)
{
struct scsi_lun_array lunarray;
int i, r, rv = 0;
if (target < 0 || target == sb->sb_adapter_target)
return EINVAL;
scsi_get_target_luns(sb, target, &lunarray);
if (lunarray.count == 0)
return EINVAL;
for (i = 0; i < lunarray.count; i++) {
r = scsi_probe_link(sb, target, lunarray.luns[i],
lunarray.dumbscan);
if (r == EINVAL && lunarray.dumbscan == 1)
return 0;
if (r != 0 && r != EINVAL)
rv = r;
}
return rv;
}
int
scsi_probe_lun(struct scsibus_softc *sb, int target, int lun)
{
if (target < 0 || target == sb->sb_adapter_target || lun < 0)
return EINVAL;
/* Probe lun on target. *NOT* a dumbscan! */
return scsi_probe_link(sb, target, lun, 0);
}
int
scsi_probe_link(struct scsibus_softc *sb, int target, int lun, int dumbscan)
{
struct scsi_attach_args sa;
const struct scsi_quirk_inquiry_pattern *finger;
struct scsi_inquiry_data *inqbuf, *usbinqbuf;
struct scsi_link *link, *link0;
struct cfdata *cf;
int inqbytes, priority, rslt = 0;
u_int16_t devquirks;
/* Skip this slot if it is already attached and try the next LUN. */
if (scsi_get_link(sb, target, lun) != NULL)
return 0;
link = malloc(sizeof(*link), M_DEVBUF, M_NOWAIT);
if (link == NULL) {
SC_DEBUG(link, SDEV_DB2, ("malloc(scsi_link) failed.\n"));
return EINVAL;
}
link->state = 0;
link->target = target;
link->lun = lun;
link->openings = sb->sb_openings;
link->node_wwn = link->port_wwn = 0;
link->flags = sb->sb_flags;
link->quirks = sb->sb_quirks;
link->interpret_sense = scsi_interpret_sense;
link->device_softc = NULL;
link->bus = sb;
memset(&link->inqdata, 0, sizeof(link->inqdata));
link->id = NULL;
TAILQ_INIT(&link->queue);
link->running = 0;
link->pending = 0;
link->pool = sb->sb_pool;
SC_DEBUG(link, SDEV_DB2, ("scsi_link created.\n"));
/* Ask the adapter if this will be a valid device. */
if (sb->sb_adapter->dev_probe != NULL &&
sb->sb_adapter->dev_probe(link) != 0) {
if (lun == 0) {
SC_DEBUG(link, SDEV_DB2, ("dev_probe(link) failed.\n"));
rslt = EINVAL;
}
free(link, M_DEVBUF, sizeof(*link));
return rslt;
}
/*
* If we havent been given an io pool by now then fall back to
* using link->openings.
*/
if (link->pool == NULL) {
link->pool = malloc(sizeof(*link->pool), M_DEVBUF, M_NOWAIT);
if (link->pool == NULL) {
SC_DEBUG(link, SDEV_DB2, ("malloc(pool) failed.\n"));
rslt = ENOMEM;
goto bad;
}
scsi_iopool_init(link->pool, link, scsi_default_get,
scsi_default_put);
SET(link->flags, SDEV_OWN_IOPL);
}
/*
* Tell drivers that are paying attention to avoid sync/wide/tags until
* INQUIRY data has been processed and the quirks information is
* complete. Some drivers set bits in quirks before we get here, so
* just add NOTAGS, NOWIDE and NOSYNC.
*/
devquirks = link->quirks;
SET(link->quirks, SDEV_NOSYNC | SDEV_NOWIDE | SDEV_NOTAGS);
/*
* Ask the device what it is.
*/
#ifdef SCSIDEBUG
if (((sb->sc_dev.dv_unit < 32) &&
((1U << sb->sc_dev.dv_unit) & scsidebug_buses)) &&
((target < 32) && ((1U << target) & scsidebug_targets)) &&
((lun < 32) && ((1U << lun) & scsidebug_luns)))
SET(link->flags, scsidebug_level);
#endif /* SCSIDEBUG */
if (lun == 0) {
/* Clear any outstanding errors. */
scsi_test_unit_ready(link, TEST_READY_RETRIES,
scsi_autoconf | SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE);
}
/* Now go ask the device all about itself. */
inqbuf = dma_alloc(sizeof(*inqbuf), PR_NOWAIT | PR_ZERO);
if (inqbuf == NULL) {
SC_DEBUG(link, SDEV_DB2, ("dma_alloc(inqbuf) failed.\n"));
rslt = ENOMEM;
goto bad;
}
rslt = scsi_inquire(link, inqbuf, scsi_autoconf | SCSI_SILENT);
if (rslt != 0) {
if (lun == 0)
rslt = EINVAL;
dma_free(inqbuf, sizeof(*inqbuf));
goto bad;
}
inqbytes = SID_SCSI2_HDRLEN + inqbuf->additional_length;
memcpy(&link->inqdata, inqbuf, inqbytes);
dma_free(inqbuf, sizeof(*inqbuf));
inqbuf = &link->inqdata;
if (inqbytes < offsetof(struct scsi_inquiry_data, vendor))
memset(inqbuf->vendor, ' ', sizeof(inqbuf->vendor));
if (inqbytes < offsetof(struct scsi_inquiry_data, product))
memset(inqbuf->product, ' ', sizeof(inqbuf->product));
if (inqbytes < offsetof(struct scsi_inquiry_data, revision))
memset(inqbuf->revision, ' ', sizeof(inqbuf->revision));
if (inqbytes < offsetof(struct scsi_inquiry_data, extra))
memset(inqbuf->extra, ' ', sizeof(inqbuf->extra));
switch (inqbuf->device & SID_QUAL) {
case SID_QUAL_RSVD:
case SID_QUAL_BAD_LU:
goto bad;
case SID_QUAL_LU_OFFLINE:
if (lun == 0 && (inqbuf->device & SID_TYPE) == T_NODEVICE)
break;
goto bad;
case SID_QUAL_LU_OK:
default:
if ((inqbuf->device & SID_TYPE) == T_NODEVICE)
goto bad;
break;
}
scsi_devid(link);
link0 = scsi_get_link(sb, target, 0);
if (lun == 0 || link0 == NULL)
;
else if (ISSET(link->flags, SDEV_UMASS))
;
else if (link->id != NULL && !DEVID_CMP(link0->id, link->id))
;
else if (dumbscan == 1 && memcmp(inqbuf, &link0->inqdata,
sizeof(*inqbuf)) == 0) {
/* The device doesn't distinguish between LUNs. */
SC_DEBUG(link, SDEV_DB1, ("IDENTIFY not supported.\n"));
rslt = EINVAL;
goto bad;
}
link->quirks = devquirks; /* Restore what the device wanted. */
finger = (const struct scsi_quirk_inquiry_pattern *)scsi_inqmatch(
inqbuf, scsi_quirk_patterns,
nitems(scsi_quirk_patterns),
sizeof(scsi_quirk_patterns[0]), &priority);
if (priority != 0)
SET(link->quirks, finger->quirks);
switch (SID_ANSII_REV(inqbuf)) {
case SCSI_REV_0:
case SCSI_REV_1:
SET(link->quirks, SDEV_NOTAGS | SDEV_NOSYNC | SDEV_NOWIDE |
SDEV_NOSYNCCACHE);
break;
case SCSI_REV_2:
case SCSI_REV_SPC:
case SCSI_REV_SPC2:
if (!ISSET(inqbuf->flags, SID_CmdQue))
SET(link->quirks, SDEV_NOTAGS);
if (!ISSET(inqbuf->flags, SID_Sync))
SET(link->quirks, SDEV_NOSYNC);
if (!ISSET(inqbuf->flags, SID_WBus16))
SET(link->quirks, SDEV_NOWIDE);
break;
case SCSI_REV_SPC3:
case SCSI_REV_SPC4:
case SCSI_REV_SPC5:
/* By this time SID_Sync and SID_WBus16 were obsolete. */
if (!ISSET(inqbuf->flags, SID_CmdQue))
SET(link->quirks, SDEV_NOTAGS);
break;
default:
break;
}
/*
* If the device can't use tags, >1 opening may confuse it.
*/
if (ISSET(link->quirks, SDEV_NOTAGS))
link->openings = 1;
/*
* note what BASIC type of device it is
*/
if (ISSET(inqbuf->dev_qual2, SID_REMOVABLE))
SET(link->flags, SDEV_REMOVABLE);
sa.sa_sc_link = link;
cf = config_search(scsibussubmatch, (struct device *)sb, &sa);
if (cf == NULL) {
scsibussubprint(&sa, sb->sc_dev.dv_xname);
printf(" not configured\n");
goto bad;
}
/*
* Braindead USB devices, especially some x-in-1 media readers, try to
* 'help' by pretending any LUN is actually LUN 0 until they see a
* different LUN used in a command. So do an INQUIRY on LUN 1 at this
* point to prevent such helpfulness before it causes confusion.
*/
if (lun == 0 && ISSET(link->flags, SDEV_UMASS) &&
scsi_get_link(sb, target, 1) == NULL && sb->sb_luns > 1 &&
(usbinqbuf = dma_alloc(sizeof(*usbinqbuf), M_NOWAIT)) != NULL) {
link->lun = 1;
scsi_inquire(link, usbinqbuf, scsi_autoconf | SCSI_SILENT);
link->lun = 0;
dma_free(usbinqbuf, sizeof(*usbinqbuf));
}
scsi_add_link(link);
/*
* Generate a TEST_UNIT_READY command. This gives drivers waiting for
* valid quirks data a chance to set wide/sync/tag options
* appropriately. It also clears any outstanding ACA conditions that
* INQUIRY may leave behind.
*
* Do this now so that any messages generated by config_attach() do not
* have negotiation messages inserted into their midst.
*/
scsi_test_unit_ready(link, TEST_READY_RETRIES,
scsi_autoconf | SCSI_IGNORE_ILLEGAL_REQUEST |
SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE);
config_attach((struct device *)sb, cf, &sa, scsibussubprint);
return 0;
bad:
scsi_detach_link(link, DETACH_FORCE);
return rslt;
}
int
scsi_detach(struct scsibus_softc *sb, int target, int lun, int flags)
{
if (target == -1 && lun == -1)
return scsi_detach_bus(sb, flags);
else if (lun == -1)
return scsi_detach_target(sb, target, flags);
else
return scsi_detach_lun(sb, target, lun, flags);
}
int
scsi_detach_bus(struct scsibus_softc *sb, int flags)
{
struct scsi_link *link, *tmp;
int r, rv = 0;
/* Detach all links from bus. */
SLIST_FOREACH_SAFE(link, &sb->sc_link_list, bus_list, tmp) {
r = scsi_detach_link(link, flags);
if (r != 0 && r != ENXIO)
rv = r;
}
return rv;
}
int
scsi_detach_target(struct scsibus_softc *sb, int target, int flags)
{
struct scsi_link *link, *tmp;
int r, rv = 0;
/* Detach all links from target. */
SLIST_FOREACH_SAFE(link, &sb->sc_link_list, bus_list, tmp) {
if (link->target == target) {
r = scsi_detach_link(link, flags);
if (r != 0 && r != ENXIO)
rv = r;
}
}
return rv;
}
int
scsi_detach_lun(struct scsibus_softc *sb, int target, int lun, int flags)
{
struct scsi_link *link;
/* Detach (target, lun) link. */
link = scsi_get_link(sb, target, lun);
if (link == NULL)
return EINVAL;
return scsi_detach_link(link, flags);
}
int
scsi_detach_link(struct scsi_link *link, int flags)
{
struct scsibus_softc *sb = link->bus;
int rv;
if (!ISSET(flags, DETACH_FORCE) && ISSET(link->flags, SDEV_OPEN))
return EBUSY;
/* Detaching a device from scsibus is a five step process. */
/* 1. Wake up processes sleeping for an xs. */
if (link->pool != NULL)
scsi_link_shutdown(link);
/* 2. Detach the device. */
if (link->device_softc != NULL) {
rv = config_detach(link->device_softc, flags);
if (rv != 0)
return rv;
}
/* 3. If it's using the openings io allocator, clean that up. */
if (link->pool != NULL && ISSET(link->flags, SDEV_OWN_IOPL)) {
scsi_iopool_destroy(link->pool);
free(link->pool, M_DEVBUF, sizeof(*link->pool));
}
/* 4. Free up its state in the adapter. */
if (sb->sb_adapter->dev_free != NULL)
sb->sb_adapter->dev_free(link);
/* 5. Free up its state in the midlayer. */
if (link->id != NULL)
devid_free(link->id);
scsi_remove_link(link);
free(link, M_DEVBUF, sizeof(*link));
return 0;
}
struct scsi_link *
scsi_get_link(struct scsibus_softc *sb, int target, int lun)
{
struct scsi_link *link;
SLIST_FOREACH(link, &sb->sc_link_list, bus_list) {
if (link->target == target && link->lun == lun)
return link;
}
return NULL;
}
void
scsi_add_link(struct scsi_link *link)
{
SLIST_INSERT_HEAD(&link->bus->sc_link_list, link, bus_list);
}
void
scsi_remove_link(struct scsi_link *link)
{
struct scsibus_softc *sb = link->bus;
struct scsi_link *elm, *prev = NULL;
SLIST_FOREACH(elm, &sb->sc_link_list, bus_list) {
if (elm == link) {
if (prev == NULL)
SLIST_REMOVE_HEAD(&sb->sc_link_list, bus_list);
else
SLIST_REMOVE_AFTER(prev, bus_list);
break;
}
prev = elm;
}
}
void
scsi_get_target_luns(struct scsibus_softc *sb, int target,
struct scsi_lun_array *lunarray)
{
struct scsi_report_luns_data *report;
struct scsi_link *link0;
int i, nluns, rv = 0;
/* LUN 0 *must* be present. */
scsi_probe_link(sb, target, 0, 0);
link0 = scsi_get_link(sb, target, 0);
if (link0 == NULL) {
lunarray->count = 0;
return;
}
/* Initialize dumbscan result. Just in case. */
report = NULL;
for (i = 0; i < link0->bus->sb_luns; i++)
lunarray->luns[i] = i;
lunarray->count = link0->bus->sb_luns;
lunarray->dumbscan = 1;
/*
* ATAPI, USB and pre-SPC (i.e. pre-SCSI-3) devices can't ask
* for a report of valid LUNs.
*/
if ((link0->flags & (SDEV_UMASS | SDEV_ATAPI)) != 0 ||
SID_ANSII_REV(&link0->inqdata) < SCSI_REV_SPC)
goto dumbscan;
report = dma_alloc(sizeof(*report), PR_WAITOK);
if (report == NULL)
goto dumbscan;
rv = scsi_report_luns(link0, REPORT_NORMAL, report,
sizeof(*report), scsi_autoconf | SCSI_SILENT |
SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY |
SCSI_IGNORE_MEDIA_CHANGE, 10000);
if (rv != 0)
goto dumbscan;
/*
* XXX In theory we should check if data is full, which
* would indicate it needs to be enlarged and REPORT
* LUNS tried again. Solaris tries up to 3 times with
* larger sizes for data.
*/
/* Return the reported Type-0 LUNs. Type-0 only! */
lunarray->count = 0;
lunarray->dumbscan = 0;
nluns = _4btol(report->length) / RPL_LUNDATA_SIZE;
for (i = 0; i < nluns; i++) {
if (report->luns[i].lundata[0] != 0)
continue;
lunarray->luns[lunarray->count++] =
report->luns[i].lundata[RPL_LUNDATA_T0LUN];
}
dumbscan:
if (report != NULL)
dma_free(report, sizeof(*report));
}
void
scsi_strvis(u_char *dst, u_char *src, int len)
{
u_char last;
/* Trim leading and trailing whitespace and NULs. */
while (len > 0 && (src[0] == ' ' || src[0] == '\t' || src[0] == '\n' ||
src[0] == '\0' || src[0] == 0xff))
++src, --len;
while (len > 0 && (src[len-1] == ' ' || src[len-1] == '\t' ||
src[len-1] == '\n' || src[len-1] == '\0' || src[len-1] == 0xff))
--len;
last = 0xff;
while (len > 0) {
switch (*src) {
case ' ':
case '\t':
case '\n':
case '\0':
case 0xff:
/* Collapse whitespace and NULs to a single space. */
if (last != ' ')
*dst++ = ' ';
last = ' ';
break;
case '\\':
/* Quote backslashes. */
*dst++ = '\\';
*dst++ = '\\';
last = '\\';
break;
default:
if (*src < 0x20 || *src >= 0x80) {
/* Non-printable characters to octal. */
*dst++ = '\\';
*dst++ = ((*src & 0300) >> 6) + '0';
*dst++ = ((*src & 0070) >> 3) + '0';
*dst++ = ((*src & 0007) >> 0) + '0';
} else {
/* Copy normal characters. */
*dst++ = *src;
}
last = *src;
break;
}
++src, --len;
}
*dst++ = 0;
}
void
scsi_print_link(struct scsi_link *link)
{
char visbuf[65];
struct scsi_inquiry_data *inqbuf;
u_int8_t *id;
int i;
printf(" targ %d lun %d: ", link->target, link->lun);
inqbuf = &link->inqdata;
scsi_strvis(visbuf, inqbuf->vendor, 8);
printf("<%s, ", visbuf);
scsi_strvis(visbuf, inqbuf->product, 16);
printf("%s, ", visbuf);
scsi_strvis(visbuf, inqbuf->revision, 4);
printf("%s>", visbuf);
#ifdef SCSIDEBUG
if (ISSET(link->flags, SDEV_ATAPI))
printf(" ATAPI");
else if (SID_ANSII_REV(inqbuf) < SCSI_REV_SPC)
printf(" SCSI/%d", SID_ANSII_REV(inqbuf));
else if (SID_ANSII_REV(inqbuf) == SCSI_REV_SPC)
printf(" SCSI/SPC");
else
printf(" SCSI/SPC-%d", SID_ANSII_REV(inqbuf) - 2);
#endif /* SCSIDEBUG */
if (ISSET(link->flags, SDEV_REMOVABLE))
printf(" removable");
if (link->id != NULL && link->id->d_type != DEVID_NONE) {
id = (u_int8_t *)(link->id + 1);
switch (link->id->d_type) {
case DEVID_NAA:
printf(" naa.");
break;
case DEVID_EUI:
printf(" eui.");
break;
case DEVID_T10:
printf(" t10.");
break;
case DEVID_SERIAL:
printf(" serial.");
break;
case DEVID_WWN:
printf(" wwn.");
break;
}
if (ISSET(link->id->d_flags, DEVID_F_PRINT)) {
for (i = 0; i < link->id->d_len; i++) {
if (id[i] == '\0' || id[i] == ' ') {
/* skip leading blanks */
/* collapse multiple blanks into one */
if (i > 0 && id[i-1] != id[i])
printf("_");
} else if (id[i] < 0x20 || id[i] >= 0x80) {
/* non-printable characters */
printf("~");
} else {
/* normal characters */
printf("%c", id[i]);
}
}
} else {
for (i = 0; i < link->id->d_len; i++)
printf("%02x", id[i]);
}
}
#ifdef SCSIDEBUG
printf("\n");
sc_print_addr(link);
printf("state %u, luns %u, openings %u\n",
link->state, link->bus->sb_luns, link->openings);
sc_print_addr(link);
printf("flags (0x%04x) ", link->flags);
scsi_show_flags(link->flags, flagnames);
printf("\n");
sc_print_addr(link);
printf("quirks (0x%04x) ", link->quirks);
scsi_show_flags(link->quirks, quirknames);
#endif /* SCSIDEBUG */
}
/*
* Return a priority based on how much of the inquiry data matches
* the patterns for the particular driver.
*/
const void *
scsi_inqmatch(struct scsi_inquiry_data *inqbuf, const void *_base,
int nmatches, int matchsize, int *bestpriority)
{
const unsigned char *base = (const unsigned char *)_base;
const void *bestmatch;
int removable;
/* Include the qualifier to catch vendor-unique types. */
removable = ISSET(inqbuf->dev_qual2, SID_REMOVABLE) ? T_REMOV : T_FIXED;
for (*bestpriority = 0, bestmatch = 0; nmatches--; base += matchsize) {
struct scsi_inquiry_pattern *match = (void *)base;
int priority, len;
if (inqbuf->device != match->type)
continue;
if (removable != match->removable)
continue;
priority = 2;
len = strlen(match->vendor);
if (bcmp(inqbuf->vendor, match->vendor, len))
continue;
priority += len;
len = strlen(match->product);
if (bcmp(inqbuf->product, match->product, len))
continue;
priority += len;
len = strlen(match->revision);
if (bcmp(inqbuf->revision, match->revision, len))
continue;
priority += len;
#ifdef SCSIDEBUG
printf("scsi_inqmatch: ");
if (_base == &scsi_quirk_patterns)
printf(" quirk ");
else
printf(" match ");
printf("priority %d. %s %s <\"%s\", \"%s\", \"%s\">", priority,
devicetypenames[(match->type & SID_TYPE)],
(match->removable == T_FIXED) ? "T_FIXED" : "T_REMOV",
match->vendor, match->product, match->revision);
if (_base == &scsi_quirk_patterns)
printf(" quirks: 0x%04x",
((struct scsi_quirk_inquiry_pattern *)match)->quirks
);
printf("\n");
#endif /* SCSIDEBUG */
if (priority > *bestpriority) {
*bestpriority = priority;
bestmatch = base;
}
}
return bestmatch;
}
void
scsi_devid(struct scsi_link *link)
{
struct {
struct scsi_vpd_hdr hdr;
u_int8_t list[32];
} __packed *pg;
size_t len;
int pg80 = 0, pg83 = 0, i;
if (link->id != NULL)
return;
pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
if (SID_ANSII_REV(&link->inqdata) >= SCSI_REV_2) {
if (scsi_inquire_vpd(link, pg, sizeof(*pg), SI_PG_SUPPORTED,
scsi_autoconf) != 0)
goto wwn;
len = MIN(sizeof(pg->list), _2btol(pg->hdr.page_length));
for (i = 0; i < len; i++) {
switch (pg->list[i]) {
case SI_PG_SERIAL:
pg80 = 1;
break;
case SI_PG_DEVID:
pg83 = 1;
break;
}
}
if (pg83 && scsi_devid_pg83(link) == 0)
goto done;
if (pg80 && scsi_devid_pg80(link) == 0)
goto done;
}
wwn:
scsi_devid_wwn(link);
done:
dma_free(pg, sizeof(*pg));
}
int
scsi_devid_pg83(struct scsi_link *link)
{
struct scsi_vpd_devid_hdr dhdr, chdr;
struct scsi_vpd_hdr *hdr = NULL;
u_int8_t *pg = NULL, *id;
int len, pos, rv, type;
int idtype = 0;
u_char idflags;
hdr = dma_alloc(sizeof(*hdr), PR_WAITOK | PR_ZERO);
rv = scsi_inquire_vpd(link, hdr, sizeof(*hdr), SI_PG_DEVID,
scsi_autoconf);
if (rv != 0)
goto done;
len = sizeof(*hdr) + _2btol(hdr->page_length);
pg = dma_alloc(len, PR_WAITOK | PR_ZERO);
rv = scsi_inquire_vpd(link, pg, len, SI_PG_DEVID, scsi_autoconf);
if (rv != 0)
goto done;
pos = sizeof(*hdr);
do {
if (len - pos < sizeof(dhdr)) {
rv = EIO;
goto done;
}
memcpy(&dhdr, &pg[pos], sizeof(dhdr));
pos += sizeof(dhdr);
if (len - pos < dhdr.len) {
rv = EIO;
goto done;
}
if (VPD_DEVID_ASSOC(dhdr.flags) == VPD_DEVID_ASSOC_LU) {
type = VPD_DEVID_TYPE(dhdr.flags);
switch (type) {
case VPD_DEVID_TYPE_NAA:
case VPD_DEVID_TYPE_EUI64:
case VPD_DEVID_TYPE_T10:
if (type >= idtype) {
idtype = type;
chdr = dhdr;
id = &pg[pos];
}
break;
default:
/* skip */
break;
}
}
pos += dhdr.len;
} while (idtype != VPD_DEVID_TYPE_NAA && len != pos);
if (idtype > 0) {
switch (VPD_DEVID_TYPE(chdr.flags)) {
case VPD_DEVID_TYPE_NAA:
idtype = DEVID_NAA;
break;
case VPD_DEVID_TYPE_EUI64:
idtype = DEVID_EUI;
break;
case VPD_DEVID_TYPE_T10:
idtype = DEVID_T10;
break;
}
switch (VPD_DEVID_CODE(chdr.pi_code)) {
case VPD_DEVID_CODE_ASCII:
case VPD_DEVID_CODE_UTF8:
idflags = DEVID_F_PRINT;
break;
default:
idflags = 0;
break;
}
link->id = devid_alloc(idtype, idflags, chdr.len, id);
} else
rv = ENODEV;
done:
if (pg)
dma_free(pg, len);
if (hdr)
dma_free(hdr, sizeof(*hdr));
return rv;
}
int
scsi_devid_pg80(struct scsi_link *link)
{
struct scsi_vpd_hdr *hdr = NULL;
u_int8_t *pg = NULL;
char *id;
size_t idlen;
int len, pglen, rv;
hdr = dma_alloc(sizeof(*hdr), PR_WAITOK | PR_ZERO);
rv = scsi_inquire_vpd(link, hdr, sizeof(*hdr), SI_PG_SERIAL,
scsi_autoconf);
if (rv != 0)
goto freehdr;
len = _2btol(hdr->page_length);
if (len == 0) {
rv = EINVAL;
goto freehdr;
}
pglen = sizeof(*hdr) + len;
pg = dma_alloc(pglen, PR_WAITOK | PR_ZERO);
rv = scsi_inquire_vpd(link, pg, pglen, SI_PG_SERIAL, scsi_autoconf);
if (rv != 0)
goto free;
idlen = sizeof(link->inqdata.vendor) +
sizeof(link->inqdata.product) + len;
id = malloc(idlen, M_TEMP, M_WAITOK);
memcpy(id, link->inqdata.vendor, sizeof(link->inqdata.vendor));
memcpy(id + sizeof(link->inqdata.vendor), link->inqdata.product,
sizeof(link->inqdata.product));
memcpy(id + sizeof(link->inqdata.vendor) +
sizeof(link->inqdata.product), pg + sizeof(*hdr), len);
link->id = devid_alloc(DEVID_SERIAL, DEVID_F_PRINT,
sizeof(link->inqdata.vendor) + sizeof(link->inqdata.product) + len,
id);
free(id, M_TEMP, idlen);
free:
dma_free(pg, pglen);
freehdr:
dma_free(hdr, sizeof(*hdr));
return rv;
}
int
scsi_devid_wwn(struct scsi_link *link)
{
u_int64_t wwnn;
if (link->lun != 0 || link->node_wwn == 0)
return EOPNOTSUPP;
wwnn = htobe64(link->node_wwn);
link->id = devid_alloc(DEVID_WWN, 0, sizeof(wwnn), (u_int8_t *)&wwnn);
return 0;
}
struct devid *
devid_alloc(u_int8_t type, u_int8_t flags, u_int8_t len, u_int8_t *id)
{
struct devid *d;
d = malloc(sizeof(*d) + len, M_DEVBUF, M_WAITOK|M_CANFAIL);
if (d == NULL)
return NULL;
d->d_type = type;
d->d_flags = flags;
d->d_len = len;
d->d_refcount = 1;
memcpy(d + 1, id, len);
return d;
}
struct devid *
devid_copy(struct devid *d)
{
d->d_refcount++;
return d;
}
void
devid_free(struct devid *d)
{
if (--d->d_refcount == 0)
free(d, M_DEVBUF, sizeof(*d) + d->d_len);
}
49
49
2
25
1
20
1
21
25
25
25
15
10
25
49
24
18
7
24
8
17
7
1
6
7
7
44
10
2
3
4
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
/* $OpenBSD: ip_icmp.c,v 1.191 2022/05/05 13:57:40 claudio Exp $ */
/* $NetBSD: ip_icmp.c,v 1.19 1996/02/13 23:42:22 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#include "carp.h"
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netinet/icmp_var.h>
#if NCARP > 0
#include <net/if_types.h>
#include <netinet/ip_carp.h>
#endif
#if NPF > 0
#include <net/pfvar.h>
#endif
/*
* ICMP routines: error generation, receive packet processing, and
* routines to turnaround packets back to the originator, and
* host table maintenance routines.
*/
#ifdef ICMPPRINTFS
int icmpprintfs = 0; /* Settable from ddb */
#endif
/* values controllable via sysctl */
int icmpmaskrepl = 0;
int icmpbmcastecho = 0;
int icmptstamprepl = 1;
int icmperrppslim = 100;
int icmp_rediraccept = 0;
int icmp_redirtimeout = 10 * 60;
static int icmperrpps_count = 0;
static struct timeval icmperrppslim_last;
struct rttimer_queue ip_mtudisc_timeout_q;
struct rttimer_queue icmp_redirect_timeout_q;
struct cpumem *icmpcounters;
const struct sysctl_bounded_args icmpctl_vars[] = {
{ ICMPCTL_MASKREPL, &icmpmaskrepl, 0, 1 },
{ ICMPCTL_BMCASTECHO, &icmpbmcastecho, 0, 1 },
{ ICMPCTL_ERRPPSLIMIT, &icmperrppslim, -1, INT_MAX },
{ ICMPCTL_REDIRACCEPT, &icmp_rediraccept, 0, 1 },
{ ICMPCTL_TSTAMPREPL, &icmptstamprepl, 0, 1 },
};
void icmp_mtudisc_timeout(struct rtentry *, u_int);
int icmp_ratelimit(const struct in_addr *, const int, const int);
int icmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
int icmp_sysctl_icmpstat(void *, size_t *, void *);
void
icmp_init(void)
{
rt_timer_queue_init(&ip_mtudisc_timeout_q, ip_mtudisc_timeout,
&icmp_mtudisc_timeout);
rt_timer_queue_init(&icmp_redirect_timeout_q, icmp_redirtimeout,
NULL);
icmpcounters = counters_alloc(icps_ncounters);
}
struct mbuf *
icmp_do_error(struct mbuf *n, int type, int code, u_int32_t dest, int destmtu)
{
struct ip *oip = mtod(n, struct ip *), *nip;
unsigned oiplen = oip->ip_hl << 2;
struct icmp *icp;
struct mbuf *m;
unsigned icmplen, mblen;
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("icmp_error(%x, %d, %d)\n", oip, type, code);
#endif
if (type != ICMP_REDIRECT)
icmpstat_inc(icps_error);
/*
* Don't send error if not the first fragment of message.
* Don't error if the old packet protocol was ICMP
* error message, only known informational types.
*/
if (oip->ip_off & htons(IP_OFFMASK))
goto freeit;
if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
n->m_len >= oiplen + ICMP_MINLEN &&
!ICMP_INFOTYPE(((struct icmp *)
((caddr_t)oip + oiplen))->icmp_type)) {
icmpstat_inc(icps_oldicmp);
goto freeit;
}
/* Don't send error in response to a multicast or broadcast packet */
if (n->m_flags & (M_BCAST|M_MCAST))
goto freeit;
/*
* First, do a rate limitation check.
*/
if (icmp_ratelimit(&oip->ip_src, type, code)) {
icmpstat_inc(icps_toofreq);
goto freeit;
}
/*
* Now, formulate icmp message
*/
icmplen = oiplen + min(8, ntohs(oip->ip_len));
/*
* Defend against mbuf chains shorter than oip->ip_len:
*/
mblen = 0;
for (m = n; m && (mblen < icmplen); m = m->m_next)
mblen += m->m_len;
icmplen = min(mblen, icmplen);
/*
* As we are not required to return everything we have,
* we return whatever we can return at ease.
*
* Note that ICMP datagrams longer than 576 octets are out of spec
* according to RFC1812;
*/
KASSERT(ICMP_MINLEN + sizeof (struct ip) <= MCLBYTES);
if (sizeof (struct ip) + icmplen + ICMP_MINLEN > MCLBYTES)
icmplen = MCLBYTES - ICMP_MINLEN - sizeof (struct ip);
m = m_gethdr(M_DONTWAIT, MT_HEADER);
if (m && ((sizeof (struct ip) + icmplen + ICMP_MINLEN +
sizeof(long) - 1) &~ (sizeof(long) - 1)) > MHLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
m = NULL;
}
}
if (m == NULL)
goto freeit;
/* keep in same rtable and preserve other pkthdr bits */
m->m_pkthdr.ph_rtableid = n->m_pkthdr.ph_rtableid;
m->m_pkthdr.ph_ifidx = n->m_pkthdr.ph_ifidx;
/* move PF_GENERATED to new packet, if existent XXX preserve more? */
if (n->m_pkthdr.pf.flags & PF_TAG_GENERATED)
m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
m->m_pkthdr.len = m->m_len = icmplen + ICMP_MINLEN;
m_align(m, m->m_len);
icp = mtod(m, struct icmp *);
if ((u_int)type > ICMP_MAXTYPE)
panic("icmp_error");
icmpstat_inc(icps_outhist + type);
icp->icmp_type = type;
if (type == ICMP_REDIRECT)
icp->icmp_gwaddr.s_addr = dest;
else {
icp->icmp_void = 0;
/*
* The following assignments assume an overlay with the
* zeroed icmp_void field.
*/
if (type == ICMP_PARAMPROB) {
icp->icmp_pptr = code;
code = 0;
} else if (type == ICMP_UNREACH &&
code == ICMP_UNREACH_NEEDFRAG && destmtu)
icp->icmp_nextmtu = htons(destmtu);
}
icp->icmp_code = code;
m_copydata(n, 0, icmplen, &icp->icmp_ip);
/*
* Now, copy old ip header (without options)
* in front of icmp message.
*/
m = m_prepend(m, sizeof(struct ip), M_DONTWAIT);
if (m == NULL)
goto freeit;
nip = mtod(m, struct ip *);
/* ip_v set in ip_output */
nip->ip_hl = sizeof(struct ip) >> 2;
nip->ip_tos = 0;
nip->ip_len = htons(m->m_len);
/* ip_id set in ip_output */
nip->ip_off = 0;
/* ip_ttl set in icmp_reflect */
nip->ip_p = IPPROTO_ICMP;
nip->ip_src = oip->ip_src;
nip->ip_dst = oip->ip_dst;
m_freem(n);
return (m);
freeit:
m_freem(n);
return (NULL);
}
/*
* Generate an error packet of type error
* in response to bad packet ip.
*
* The ip packet inside has ip_off and ip_len in host byte order.
*/
void
icmp_error(struct mbuf *n, int type, int code, u_int32_t dest, int destmtu)
{
struct mbuf *m;
m = icmp_do_error(n, type, code, dest, destmtu);
if (m != NULL)
if (!icmp_reflect(m, NULL, NULL))
icmp_send(m, NULL);
}
/*
* Process a received ICMP message.
*/
int
icmp_input(struct mbuf **mp, int *offp, int proto, int af)
{
struct ifnet *ifp;
ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
m_freemp(mp);
return IPPROTO_DONE;
}
proto = icmp_input_if(ifp, mp, offp, proto, af);
if_put(ifp);
return proto;
}
int
icmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
{
struct mbuf *m = *mp;
int hlen = *offp;
struct icmp *icp;
struct ip *ip = mtod(m, struct ip *);
struct sockaddr_in sin;
int icmplen, i, code;
struct in_ifaddr *ia;
void (*ctlfunc)(int, struct sockaddr *, u_int, void *);
struct mbuf *opts;
/*
* Locate icmp structure in mbuf, and check
* that not corrupted and of at least minimum length.
*/
icmplen = ntohs(ip->ip_len) - hlen;
#ifdef ICMPPRINTFS
if (icmpprintfs) {
char dst[INET_ADDRSTRLEN], src[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
printf("icmp_input from %s to %s, len %d\n", src, dst, icmplen);
}
#endif
if (icmplen < ICMP_MINLEN) {
icmpstat_inc(icps_tooshort);
goto freeit;
}
i = hlen + min(icmplen, ICMP_ADVLENMAX);
if ((m = *mp = m_pullup(m, i)) == NULL) {
icmpstat_inc(icps_tooshort);
return IPPROTO_DONE;
}
ip = mtod(m, struct ip *);
if (in4_cksum(m, 0, hlen, icmplen)) {
icmpstat_inc(icps_checksum);
goto freeit;
}
icp = (struct icmp *)(mtod(m, caddr_t) + hlen);
#ifdef ICMPPRINTFS
/*
* Message type specific processing.
*/
if (icmpprintfs)
printf("icmp_input, type %d code %d\n", icp->icmp_type,
icp->icmp_code);
#endif
if (icp->icmp_type > ICMP_MAXTYPE)
goto raw;
#if NPF > 0
if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
switch (icp->icmp_type) {
/*
* As pf_icmp_mapping() considers redirects belonging to a
* diverted connection, we must include it here.
*/
case ICMP_REDIRECT:
/* FALLTHROUGH */
/*
* These ICMP types map to other connections. They must be
* delivered to pr_ctlinput() also for diverted connections.
*/
case ICMP_UNREACH:
case ICMP_TIMXCEED:
case ICMP_PARAMPROB:
case ICMP_SOURCEQUENCH:
/*
* Do not use the divert-to property of the TCP or UDP
* rule when doing the PCB lookup for the raw socket.
*/
m->m_pkthdr.pf.flags &=~ PF_TAG_DIVERTED;
break;
default:
goto raw;
}
}
#endif /* NPF */
icmpstat_inc(icps_inhist + icp->icmp_type);
code = icp->icmp_code;
switch (icp->icmp_type) {
case ICMP_UNREACH:
switch (code) {
case ICMP_UNREACH_NET:
case ICMP_UNREACH_HOST:
case ICMP_UNREACH_PROTOCOL:
case ICMP_UNREACH_PORT:
case ICMP_UNREACH_SRCFAIL:
code += PRC_UNREACH_NET;
break;
case ICMP_UNREACH_NEEDFRAG:
code = PRC_MSGSIZE;
break;
case ICMP_UNREACH_NET_UNKNOWN:
case ICMP_UNREACH_NET_PROHIB:
case ICMP_UNREACH_TOSNET:
code = PRC_UNREACH_NET;
break;
case ICMP_UNREACH_HOST_UNKNOWN:
case ICMP_UNREACH_ISOLATED:
case ICMP_UNREACH_HOST_PROHIB:
case ICMP_UNREACH_TOSHOST:
case ICMP_UNREACH_FILTER_PROHIB:
case ICMP_UNREACH_HOST_PRECEDENCE:
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
code = PRC_UNREACH_HOST;
break;
default:
goto badcode;
}
goto deliver;
case ICMP_TIMXCEED:
if (code > 1)
goto badcode;
code += PRC_TIMXCEED_INTRANS;
goto deliver;
case ICMP_PARAMPROB:
if (code > 1)
goto badcode;
code = PRC_PARAMPROB;
goto deliver;
case ICMP_SOURCEQUENCH:
if (code)
goto badcode;
code = PRC_QUENCH;
deliver:
/*
* Problem with datagram; advise higher level routines.
*/
if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
icmpstat_inc(icps_badlen);
goto freeit;
}
if (IN_MULTICAST(icp->icmp_ip.ip_dst.s_addr))
goto badcode;
#ifdef INET6
/* Get more contiguous data for a v6 in v4 ICMP message. */
if (icp->icmp_ip.ip_p == IPPROTO_IPV6) {
if (icmplen < ICMP_V6ADVLENMIN ||
icmplen < ICMP_V6ADVLEN(icp)) {
icmpstat_inc(icps_badlen);
goto freeit;
}
}
#endif /* INET6 */
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
#endif
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_addr = icp->icmp_ip.ip_dst;
#if NCARP > 0
if (carp_lsdrop(ifp, m, AF_INET, &sin.sin_addr.s_addr,
&ip->ip_dst.s_addr, 1))
goto freeit;
#endif
/*
* XXX if the packet contains [IPv4 AH TCP], we can't make a
* notification to TCP layer.
*/
ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput;
if (ctlfunc)
(*ctlfunc)(code, sintosa(&sin), m->m_pkthdr.ph_rtableid,
&icp->icmp_ip);
break;
badcode:
icmpstat_inc(icps_badcode);
break;
case ICMP_ECHO:
if (!icmpbmcastecho &&
(m->m_flags & (M_MCAST | M_BCAST)) != 0) {
icmpstat_inc(icps_bmcastecho);
break;
}
icp->icmp_type = ICMP_ECHOREPLY;
goto reflect;
case ICMP_TSTAMP:
if (icmptstamprepl == 0)
break;
if (!icmpbmcastecho &&
(m->m_flags & (M_MCAST | M_BCAST)) != 0) {
icmpstat_inc(icps_bmcastecho);
break;
}
if (icmplen < ICMP_TSLEN) {
icmpstat_inc(icps_badlen);
break;
}
icp->icmp_type = ICMP_TSTAMPREPLY;
icp->icmp_rtime = iptime();
icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */
goto reflect;
case ICMP_MASKREQ:
if (icmpmaskrepl == 0)
break;
if (icmplen < ICMP_MASKLEN) {
icmpstat_inc(icps_badlen);
break;
}
/*
* We are not able to respond with all ones broadcast
* unless we receive it over a point-to-point interface.
*/
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
ip->ip_dst.s_addr == INADDR_ANY)
sin.sin_addr = ip->ip_src;
else
sin.sin_addr = ip->ip_dst;
if (ifp == NULL)
break;
ia = ifatoia(ifaof_ifpforaddr(sintosa(&sin), ifp));
if (ia == NULL)
break;
icp->icmp_type = ICMP_MASKREPLY;
icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
if (ip->ip_src.s_addr == 0) {
if (ifp->if_flags & IFF_BROADCAST) {
if (ia->ia_broadaddr.sin_addr.s_addr)
ip->ip_src = ia->ia_broadaddr.sin_addr;
else
ip->ip_src.s_addr = INADDR_BROADCAST;
}
else if (ifp->if_flags & IFF_POINTOPOINT)
ip->ip_src = ia->ia_dstaddr.sin_addr;
}
reflect:
#if NCARP > 0
if (carp_lsdrop(ifp, m, AF_INET, &ip->ip_src.s_addr,
&ip->ip_dst.s_addr, 1))
goto freeit;
#endif
icmpstat_inc(icps_reflect);
icmpstat_inc(icps_outhist + icp->icmp_type);
if (!icmp_reflect(m, &opts, NULL)) {
icmp_send(m, opts);
m_free(opts);
}
return IPPROTO_DONE;
case ICMP_REDIRECT:
{
struct sockaddr_in sdst;
struct sockaddr_in sgw;
struct sockaddr_in ssrc;
struct rtentry *newrt = NULL;
if (icmp_rediraccept == 0 || ipforwarding == 1)
goto freeit;
if (code > 3)
goto badcode;
if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
icmpstat_inc(icps_badlen);
break;
}
/*
* Short circuit routing redirects to force
* immediate change in the kernel's routing
* tables. The message is also handed to anyone
* listening on a raw socket (e.g. the routing
* daemon for use in updating its tables).
*/
memset(&sdst, 0, sizeof(sdst));
memset(&sgw, 0, sizeof(sgw));
memset(&ssrc, 0, sizeof(ssrc));
sdst.sin_family = sgw.sin_family = ssrc.sin_family = AF_INET;
sdst.sin_len = sgw.sin_len = ssrc.sin_len = sizeof(sdst);
memcpy(&sdst.sin_addr, &icp->icmp_ip.ip_dst,
sizeof(sdst.sin_addr));
memcpy(&sgw.sin_addr, &icp->icmp_gwaddr,
sizeof(sgw.sin_addr));
memcpy(&ssrc.sin_addr, &ip->ip_src,
sizeof(ssrc.sin_addr));
#ifdef ICMPPRINTFS
if (icmpprintfs) {
char gw[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &icp->icmp_gwaddr, gw, sizeof(gw));
inet_ntop(AF_INET, &icp->icmp_ip.ip_dst,
dst, sizeof(dst));
printf("redirect dst %s to %s\n", dst, gw);
}
#endif
#if NCARP > 0
if (carp_lsdrop(ifp, m, AF_INET, &sdst.sin_addr.s_addr,
&ip->ip_dst.s_addr, 1))
goto freeit;
#endif
rtredirect(sintosa(&sdst), sintosa(&sgw),
sintosa(&ssrc), &newrt, m->m_pkthdr.ph_rtableid);
if (newrt != NULL && icmp_redirtimeout > 0) {
rt_timer_add(newrt, &icmp_redirect_timeout_q,
m->m_pkthdr.ph_rtableid);
}
rtfree(newrt);
pfctlinput(PRC_REDIRECT_HOST, sintosa(&sdst));
break;
}
/*
* No kernel processing for the following;
* just fall through to send to raw listener.
*/
case ICMP_ECHOREPLY:
case ICMP_ROUTERADVERT:
case ICMP_ROUTERSOLICIT:
case ICMP_TSTAMPREPLY:
case ICMP_IREQREPLY:
case ICMP_MASKREPLY:
case ICMP_TRACEROUTE:
case ICMP_DATACONVERR:
case ICMP_MOBILE_REDIRECT:
case ICMP_IPV6_WHEREAREYOU:
case ICMP_IPV6_IAMHERE:
case ICMP_MOBILE_REGREQUEST:
case ICMP_MOBILE_REGREPLY:
case ICMP_PHOTURIS:
default:
break;
}
raw:
return rip_input(mp, offp, proto, af);
freeit:
m_freem(m);
return IPPROTO_DONE;
}
/*
* Reflect the ip packet back to the source
*/
int
icmp_reflect(struct mbuf *m, struct mbuf **op, struct in_ifaddr *ia)
{
struct ip *ip = mtod(m, struct ip *);
struct mbuf *opts = NULL;
struct sockaddr_in sin;
struct rtentry *rt = NULL;
int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
u_int rtableid;
u_int8_t pfflags;
if (!in_canforward(ip->ip_src) &&
((ip->ip_src.s_addr & IN_CLASSA_NET) !=
htonl(IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
m_freem(m); /* Bad return address */
return (EHOSTUNREACH);
}
if (m->m_pkthdr.ph_loopcnt++ >= M_MAXLOOP) {
m_freem(m);
return (ELOOP);
}
rtableid = m->m_pkthdr.ph_rtableid;
pfflags = m->m_pkthdr.pf.flags;
m_resethdr(m);
m->m_pkthdr.ph_rtableid = rtableid;
m->m_pkthdr.pf.flags = pfflags & PF_TAG_GENERATED;
/*
* If the incoming packet was addressed directly to us,
* use dst as the src for the reply. For broadcast, use
* the address which corresponds to the incoming interface.
*/
if (ia == NULL) {
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = ip->ip_dst;
rt = rtalloc(sintosa(&sin), 0, rtableid);
if (rtisvalid(rt) &&
ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST))
ia = ifatoia(rt->rt_ifa);
}
/*
* The following happens if the packet was not addressed to us.
* Use the new source address and do a route lookup. If it fails
* drop the packet as there is no path to the host.
*/
if (ia == NULL) {
rtfree(rt);
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = ip->ip_src;
/* keep packet in the original virtual instance */
rt = rtalloc(sintosa(&sin), RT_RESOLVE, rtableid);
if (rt == NULL) {
ipstat_inc(ips_noroute);
m_freem(m);
return (EHOSTUNREACH);
}
ia = ifatoia(rt->rt_ifa);
}
ip->ip_dst = ip->ip_src;
ip->ip_ttl = MAXTTL;
/* It is safe to dereference ``ia'' iff ``rt'' is valid. */
ip->ip_src = ia->ia_addr.sin_addr;
rtfree(rt);
if (optlen > 0) {
u_char *cp;
int opt, cnt;
u_int len;
/*
* Retrieve any source routing from the incoming packet;
* add on any record-route or timestamp options.
*/
cp = (u_char *) (ip + 1);
if (op && (opts = ip_srcroute(m)) == NULL &&
(opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
opts->m_len = sizeof(struct in_addr);
mtod(opts, struct in_addr *)->s_addr = 0;
}
if (op && opts) {
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("icmp_reflect optlen %d rt %d => ",
optlen, opts->m_len);
#endif
for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
len = 1;
else {
if (cnt < IPOPT_OLEN + sizeof(*cp))
break;
len = cp[IPOPT_OLEN];
if (len < IPOPT_OLEN + sizeof(*cp) ||
len > cnt)
break;
}
/*
* Should check for overflow, but it
* "can't happen"
*/
if (opt == IPOPT_RR || opt == IPOPT_TS ||
opt == IPOPT_SECURITY) {
memcpy(mtod(opts, caddr_t) +
opts->m_len, cp, len);
opts->m_len += len;
}
}
/* Terminate & pad, if necessary */
if ((cnt = opts->m_len % 4) != 0)
for (; cnt < 4; cnt++) {
*(mtod(opts, caddr_t) + opts->m_len) =
IPOPT_EOL;
opts->m_len++;
}
#ifdef ICMPPRINTFS
if (icmpprintfs)
printf("%d\n", opts->m_len);
#endif
}
ip_stripoptions(m);
}
m->m_flags &= ~(M_BCAST|M_MCAST);
if (op)
*op = opts;
return (0);
}
/*
* Send an icmp packet back to the ip level
*/
void
icmp_send(struct mbuf *m, struct mbuf *opts)
{
struct ip *ip = mtod(m, struct ip *);
int hlen;
struct icmp *icp;
hlen = ip->ip_hl << 2;
icp = (struct icmp *)(mtod(m, caddr_t) + hlen);
icp->icmp_cksum = 0;
m->m_pkthdr.csum_flags = M_ICMP_CSUM_OUT;
#ifdef ICMPPRINTFS
if (icmpprintfs) {
char dst[INET_ADDRSTRLEN], src[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
printf("icmp_send dst %s src %s\n", dst, src);
}
#endif
/*
* ip_send() cannot handle IP options properly. So in case we have
* options fill out the IP header here and use ip_send_raw() instead.
*/
if (opts != NULL) {
m = ip_insertoptions(m, opts, &hlen);
ip = mtod(m, struct ip *);
ip->ip_hl = (hlen >> 2);
ip->ip_v = IPVERSION;
ip->ip_off &= htons(IP_DF);
ip->ip_id = htons(ip_randomid());
ipstat_inc(ips_localout);
ip_send_raw(m);
} else
ip_send(m);
}
u_int32_t
iptime(void)
{
struct timeval atv;
u_long t;
microtime(&atv);
t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
return (htonl(t));
}
int
icmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int error;
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case ICMPCTL_REDIRTIMEOUT:
NET_LOCK();
error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
&icmp_redirtimeout, 0, INT_MAX);
rt_timer_queue_change(&icmp_redirect_timeout_q,
icmp_redirtimeout);
NET_UNLOCK();
break;
case ICMPCTL_STATS:
error = icmp_sysctl_icmpstat(oldp, oldlenp, newp);
break;
default:
NET_LOCK();
error = sysctl_bounded_arr(icmpctl_vars, nitems(icmpctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
NET_UNLOCK();
break;
}
return (error);
}
int
icmp_sysctl_icmpstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[icps_ncounters];
struct icmpstat icmpstat;
u_long *words = (u_long *)&icmpstat;
int i;
CTASSERT(sizeof(icmpstat) == (nitems(counters) * sizeof(u_long)));
memset(&icmpstat, 0, sizeof icmpstat);
counters_read(icmpcounters, counters, nitems(counters));
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&icmpstat, sizeof(icmpstat)));
}
struct rtentry *
icmp_mtudisc_clone(struct in_addr dst, u_int rtableid, int ipsec)
{
struct sockaddr_in sin;
struct rtentry *rt;
int error;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(sin);
sin.sin_addr = dst;
rt = rtalloc(sintosa(&sin), RT_RESOLVE, rtableid);
/* Check if the route is actually usable */
if (!rtisvalid(rt))
goto bad;
/* IPsec needs the route only for PMTU, it can use reject for that */
if (!ipsec && (rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)))
goto bad;
/*
* No PMTU for local routes and permanent neighbors,
* ARP and NDP use the same expire timer as the route.
*/
if (ISSET(rt->rt_flags, RTF_LOCAL) ||
(ISSET(rt->rt_flags, RTF_LLINFO) && rt->rt_expire == 0))
goto bad;
/* If we didn't get a host route, allocate one */
if ((rt->rt_flags & RTF_HOST) == 0) {
struct rtentry *nrt;
struct rt_addrinfo info;
struct sockaddr_rtlabel sa_rl;
memset(&info, 0, sizeof(info));
info.rti_ifa = rt->rt_ifa;
info.rti_flags = RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC;
info.rti_info[RTAX_DST] = sintosa(&sin);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_LABEL] =
rtlabel_id2sa(rt->rt_labelid, &sa_rl);
error = rtrequest(RTM_ADD, &info, rt->rt_priority, &nrt,
rtableid);
if (error)
goto bad;
nrt->rt_rmx = rt->rt_rmx;
rtfree(rt);
rt = nrt;
rtm_send(rt, RTM_ADD, 0, rtableid);
}
error = rt_timer_add(rt, &ip_mtudisc_timeout_q, rtableid);
if (error)
goto bad;
return (rt);
bad:
rtfree(rt);
return (NULL);
}
/* Table of common MTUs: */
static const u_short mtu_table[] = {
65535, 65280, 32000, 17914, 9180, 8166,
4352, 2002, 1492, 1006, 508, 296, 68, 0
};
void
icmp_mtudisc(struct icmp *icp, u_int rtableid)
{
struct rtentry *rt;
struct ifnet *ifp;
u_long mtu = ntohs(icp->icmp_nextmtu); /* Why a long? IPv6 */
rt = icmp_mtudisc_clone(icp->icmp_ip.ip_dst, rtableid, 0);
if (rt == NULL)
return;
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL) {
rtfree(rt);
return;
}
if (mtu == 0) {
int i = 0;
mtu = ntohs(icp->icmp_ip.ip_len);
/* Some 4.2BSD-based routers incorrectly adjust the ip_len */
if (mtu > rt->rt_mtu && rt->rt_mtu != 0)
mtu -= (icp->icmp_ip.ip_hl << 2);
/* If we still can't guess a value, try the route */
if (mtu == 0) {
mtu = rt->rt_mtu;
/* If no route mtu, default to the interface mtu */
if (mtu == 0)
mtu = ifp->if_mtu;
}
for (i = 0; i < nitems(mtu_table); i++)
if (mtu > mtu_table[i]) {
mtu = mtu_table[i];
break;
}
}
/*
* XXX: RTV_MTU is overloaded, since the admin can set it
* to turn off PMTU for a route, and the kernel can
* set it to indicate a serious problem with PMTU
* on a route. We should be using a separate flag
* for the kernel to indicate this.
*/
if ((rt->rt_locks & RTV_MTU) == 0) {
if (mtu < 296 || mtu > ifp->if_mtu)
rt->rt_locks |= RTV_MTU;
else if (rt->rt_mtu > mtu || rt->rt_mtu == 0)
rt->rt_mtu = mtu;
}
if_put(ifp);
rtfree(rt);
}
void
icmp_mtudisc_timeout(struct rtentry *rt, u_int rtableid)
{
struct ifnet *ifp;
NET_ASSERT_LOCKED();
ifp = if_get(rt->rt_ifidx);
if (ifp == NULL)
return;
if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
void (*ctlfunc)(int, struct sockaddr *, u_int, void *);
struct sockaddr_in sin;
sin = *satosin(rt_key(rt));
rtdeletemsg(rt, ifp, rtableid);
/* Notify TCP layer of increased Path MTU estimate */
ctlfunc = inetsw[ip_protox[IPPROTO_TCP]].pr_ctlinput;
if (ctlfunc)
(*ctlfunc)(PRC_MTUINC, sintosa(&sin),
rtableid, NULL);
} else {
if ((rt->rt_locks & RTV_MTU) == 0)
rt->rt_mtu = 0;
}
if_put(ifp);
}
/*
* Perform rate limit check.
* Returns 0 if it is okay to send the icmp packet.
* Returns 1 if the router SHOULD NOT send this icmp packet due to rate
* limitation.
*
* XXX per-destination/type check necessary?
*/
int
icmp_ratelimit(const struct in_addr *dst, const int type, const int code)
{
/* PPS limit */
if (!ppsratecheck(&icmperrppslim_last, &icmperrpps_count,
icmperrppslim))
return 1; /* The packet is subject to rate limit */
return 0; /* okay to send */
}
int
icmp_do_exthdr(struct mbuf *m, u_int16_t class, u_int8_t ctype, void *buf,
size_t len)
{
struct ip *ip = mtod(m, struct ip *);
int hlen, off;
struct mbuf *n;
struct icmp *icp;
struct icmp_ext_hdr *ieh;
struct {
struct icmp_ext_hdr ieh;
struct icmp_ext_obj_hdr ieo;
} hdr;
hlen = ip->ip_hl << 2;
icp = (struct icmp *)(mtod(m, caddr_t) + hlen);
if (icp->icmp_type != ICMP_TIMXCEED && icp->icmp_type != ICMP_UNREACH &&
icp->icmp_type != ICMP_PARAMPROB)
/* exthdr not supported */
return (0);
if (icp->icmp_length != 0)
/* exthdr already present, giving up */
return (0);
/* the actual offset starts after the common ICMP header */
hlen += ICMP_MINLEN;
/* exthdr must start on a word boundary */
off = roundup(ntohs(ip->ip_len) - hlen, sizeof(u_int32_t));
/* ... and at an offset of ICMP_EXT_OFFSET or bigger */
off = max(off, ICMP_EXT_OFFSET);
icp->icmp_length = off / sizeof(u_int32_t);
memset(&hdr, 0, sizeof(hdr));
hdr.ieh.ieh_version = ICMP_EXT_HDR_VERSION;
hdr.ieo.ieo_length = htons(sizeof(struct icmp_ext_obj_hdr) + len);
hdr.ieo.ieo_cnum = class;
hdr.ieo.ieo_ctype = ctype;
if (m_copyback(m, hlen + off, sizeof(hdr), &hdr, M_NOWAIT) ||
m_copyback(m, hlen + off + sizeof(hdr), len, buf, M_NOWAIT)) {
m_freem(m);
return (ENOBUFS);
}
/* calculate checksum */
n = m_getptr(m, hlen + off, &off);
if (n == NULL)
panic("icmp_do_exthdr: m_getptr failure");
ieh = (struct icmp_ext_hdr *)(mtod(n, caddr_t) + off);
ieh->ieh_cksum = in4_cksum(n, 0, off, sizeof(hdr) + len);
ip->ip_len = htons(m->m_pkthdr.len);
return (0);
}
2
2
2
1
2
8
9
9
8
8
2
7
7
8
5
2
2
3
2
2
4
4
3
2
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
/* $OpenBSD: pf_osfp.c,v 1.45 2020/12/15 15:23:48 sashan Exp $ */
/*
* Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <sys/param.h>
#include <sys/socket.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/pool.h>
#endif /* _KERNEL */
#include <sys/queue.h>
#include <sys/mbuf.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
#ifdef _KERNEL
typedef struct pool pool_t;
#else /* !_KERNEL */
/* Userland equivalents so we can lend code to tcpdump et al. */
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#define pool_t int
#define pool_get(pool, flags) malloc(*(pool))
#define pool_put(pool, item) free(item)
#define pool_init(pool, size, a, ao, f, m, p) (*(pool)) = (size)
#define NET_LOCK()
#define NET_UNLOCK()
#define PF_LOCK()
#define PF_UNLOCK()
#ifdef PFDEBUG
#include <sys/stdarg.h> /* for DPFPRINTF() */
#endif /* PFDEBUG */
#endif /* _KERNEL */
SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list;
pool_t pf_osfp_entry_pl;
pool_t pf_osfp_pl;
struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *,
struct pf_os_fingerprint *, u_int8_t);
struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *,
struct pf_os_fingerprint *);
void pf_osfp_insert(struct pf_osfp_list *,
struct pf_os_fingerprint *);
#ifdef _KERNEL
/*
* Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
* Returns the list of possible OSes.
*/
struct pf_osfp_enlist *
pf_osfp_fingerprint(struct pf_pdesc *pd)
{
struct tcphdr *th = &pd->hdr.tcp;
struct ip *ip = NULL;
struct ip6_hdr *ip6 = NULL;
char hdr[60];
if (pd->proto != IPPROTO_TCP)
return (NULL);
switch (pd->af) {
case AF_INET:
ip = mtod(pd->m, struct ip *);
break;
case AF_INET6:
ip6 = mtod(pd->m, struct ip6_hdr *);
break;
}
if (!pf_pull_hdr(pd->m, pd->off, hdr, th->th_off << 2, NULL, NULL,
pd->af))
return (NULL);
return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr));
}
#endif /* _KERNEL */
struct pf_osfp_enlist *
pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6,
const struct tcphdr *tcp)
{
struct pf_os_fingerprint fp, *fpresult;
int cnt, optlen = 0;
const u_int8_t *optp;
#ifdef _KERNEL
char srcname[128];
#else /* !_KERNEL */
char srcname[NI_MAXHOST];
#endif /* _KERNEL */
if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN)
return (NULL);
if (ip) {
if ((ip->ip_off & htons(IP_OFFMASK)) != 0)
return (NULL);
}
memset(&fp, 0, sizeof(fp));
if (ip) {
#ifndef _KERNEL
struct sockaddr_in sin;
#endif /* _KERNEL */
fp.fp_psize = ntohs(ip->ip_len);
fp.fp_ttl = ip->ip_ttl;
if (ip->ip_off & htons(IP_DF))
fp.fp_flags |= PF_OSFP_DF;
#ifdef _KERNEL
inet_ntop(AF_INET, &ip->ip_src, srcname, sizeof(srcname));
#else /* !_KERNEL */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_addr = ip->ip_src;
(void)getnameinfo((struct sockaddr *)&sin,
sizeof(struct sockaddr_in), srcname, sizeof(srcname),
NULL, 0, NI_NUMERICHOST);
#endif /* _KERNEL */
}
#ifdef INET6
else if (ip6) {
#ifndef _KERNEL
struct sockaddr_in6 sin6;
#endif /* !_KERNEL */
/* jumbo payload? */
fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
fp.fp_ttl = ip6->ip6_hlim;
fp.fp_flags |= PF_OSFP_DF;
fp.fp_flags |= PF_OSFP_INET6;
#ifdef _KERNEL
inet_ntop(AF_INET6, &ip6->ip6_src, srcname, sizeof(srcname));
#else /* !_KERNEL */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = ip6->ip6_src;
(void)getnameinfo((struct sockaddr *)&sin6,
sizeof(struct sockaddr_in6), srcname, sizeof(srcname),
NULL, 0, NI_NUMERICHOST);
#endif /* !_KERNEL */
}
#endif /* INET6 */
else
return (NULL);
fp.fp_wsize = ntohs(tcp->th_win);
cnt = (tcp->th_off << 2) - sizeof(*tcp);
optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
for (; cnt > 0; cnt -= optlen, optp += optlen) {
if (*optp == TCPOPT_EOL)
break;
fp.fp_optcnt++;
if (*optp == TCPOPT_NOP) {
fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
PF_OSFP_TCPOPT_NOP;
optlen = 1;
} else {
if (cnt < 2)
return (NULL);
optlen = optp[1];
if (optlen > cnt || optlen < 2)
return (NULL);
switch (*optp) {
case TCPOPT_MAXSEG:
if (optlen >= TCPOLEN_MAXSEG)
memcpy(&fp.fp_mss, &optp[2],
sizeof(fp.fp_mss));
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
fp.fp_mss = ntohs(fp.fp_mss);
break;
case TCPOPT_WINDOW:
if (optlen >= TCPOLEN_WINDOW)
memcpy(&fp.fp_wscale, &optp[2],
sizeof(fp.fp_wscale));
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) |
PF_OSFP_TCPOPT_WSCALE;
break;
case TCPOPT_SACK_PERMITTED:
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
break;
case TCPOPT_TIMESTAMP:
if (optlen >= TCPOLEN_TIMESTAMP) {
u_int32_t ts;
memcpy(&ts, &optp[2], sizeof(ts));
if (ts == 0)
fp.fp_flags |= PF_OSFP_TS0;
}
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
break;
default:
return (NULL);
}
}
optlen = MAX(optlen, 1); /* paranoia */
}
DPFPRINTF(LOG_INFO,
"fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) "
"(TS=%s,M=%s%d,W=%s%d)",
srcname, ntohs(tcp->th_sport),
fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
(fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
(fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
(fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
fp.fp_mss,
(fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
(fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
fp.fp_wscale);
if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
PF_OSFP_MAXTTL_OFFSET)))
return (&fpresult->fp_oses);
return (NULL);
}
/* Match a fingerprint ID against a list of OSes */
int
pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
{
struct pf_osfp_entry *entry;
int os_class, os_version, os_subtype;
int en_class, en_version, en_subtype;
if (os == PF_OSFP_ANY)
return (1);
if (list == NULL) {
DPFPRINTF(LOG_INFO, "osfp no match against %x", os);
return (os == PF_OSFP_UNKNOWN);
}
PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
SLIST_FOREACH(entry, list, fp_entry) {
PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
(os_version == PF_OSFP_ANY || en_version == os_version) &&
(os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
DPFPRINTF(LOG_INFO,
"osfp matched %s %s %s %x==%x",
entry->fp_class_nm, entry->fp_version_nm,
entry->fp_subtype_nm, os, entry->fp_os);
return (1);
}
}
DPFPRINTF(LOG_INFO, "fingerprint 0x%x didn't match", os);
return (0);
}
/* Initialize the OS fingerprint system */
void
pf_osfp_initialize(void)
{
pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0,
IPL_NONE, PR_WAITOK, "pfosfpen", NULL);
pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0,
IPL_NONE, PR_WAITOK, "pfosfp", NULL);
SLIST_INIT(&pf_osfp_list);
}
/* Flush the fingerprint list */
void
pf_osfp_flush(void)
{
struct pf_os_fingerprint *fp;
struct pf_osfp_entry *entry;
NET_LOCK();
PF_LOCK();
while ((fp = SLIST_FIRST(&pf_osfp_list))) {
SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
while ((entry = SLIST_FIRST(&fp->fp_oses))) {
SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
pool_put(&pf_osfp_entry_pl, entry);
}
pool_put(&pf_osfp_pl, fp);
}
PF_UNLOCK();
NET_UNLOCK();
}
/* Add a fingerprint */
int
pf_osfp_add(struct pf_osfp_ioctl *fpioc)
{
struct pf_os_fingerprint *fp, *fp_prealloc, fpadd;
struct pf_osfp_entry *entry;
memset(&fpadd, 0, sizeof(fpadd));
fpadd.fp_tcpopts = fpioc->fp_tcpopts;
fpadd.fp_wsize = fpioc->fp_wsize;
fpadd.fp_psize = fpioc->fp_psize;
fpadd.fp_mss = fpioc->fp_mss;
fpadd.fp_flags = fpioc->fp_flags;
fpadd.fp_optcnt = fpioc->fp_optcnt;
fpadd.fp_wscale = fpioc->fp_wscale;
fpadd.fp_ttl = fpioc->fp_ttl;
DPFPRINTF(LOG_DEBUG,
"adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
"(TS=%s,M=%s%d,W=%s%d) %x",
fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
fpioc->fp_os.fp_subtype_nm,
(fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
(fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
(fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
fpadd.fp_wsize,
fpadd.fp_ttl,
(fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
(fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
fpadd.fp_psize,
(long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
(fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
(fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
fpadd.fp_mss,
(fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
fpadd.fp_wscale,
fpioc->fp_os.fp_os);
entry = pool_get(&pf_osfp_entry_pl, PR_WAITOK|PR_LIMITFAIL);
if (entry == NULL)
return (ENOMEM);
fp_prealloc = pool_get(&pf_osfp_pl, PR_WAITOK|PR_ZERO|PR_LIMITFAIL);
if (fp_prealloc == NULL) {
pool_put(&pf_osfp_entry_pl, entry);
return (ENOMEM);
}
NET_LOCK();
PF_LOCK();
if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
struct pf_osfp_entry *tentry;
SLIST_FOREACH(tentry, &fp->fp_oses, fp_entry) {
if (PF_OSFP_ENTRY_EQ(tentry, &fpioc->fp_os)) {
NET_UNLOCK();
PF_UNLOCK();
pool_put(&pf_osfp_entry_pl, entry);
pool_put(&pf_osfp_pl, fp_prealloc);
return (EEXIST);
}
}
} else {
fp = fp_prealloc;
fp_prealloc = NULL;
fp->fp_tcpopts = fpioc->fp_tcpopts;
fp->fp_wsize = fpioc->fp_wsize;
fp->fp_psize = fpioc->fp_psize;
fp->fp_mss = fpioc->fp_mss;
fp->fp_flags = fpioc->fp_flags;
fp->fp_optcnt = fpioc->fp_optcnt;
fp->fp_wscale = fpioc->fp_wscale;
fp->fp_ttl = fpioc->fp_ttl;
SLIST_INIT(&fp->fp_oses);
pf_osfp_insert(&pf_osfp_list, fp);
}
memcpy(entry, &fpioc->fp_os, sizeof(*entry));
/* Make sure the strings are NUL terminated */
entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
PF_UNLOCK();
NET_UNLOCK();
#ifdef PFDEBUG
if ((fp = pf_osfp_validate()))
DPFPRINTF(LOG_NOTICE,
"Invalid fingerprint list");
#endif /* PFDEBUG */
if (fp_prealloc != NULL)
pool_put(&pf_osfp_pl, fp_prealloc);
return (0);
}
/* Find a fingerprint in the list */
struct pf_os_fingerprint *
pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
u_int8_t ttldiff)
{
struct pf_os_fingerprint *f;
#define MATCH_INT(_MOD, _DC, _field) \
if ((f->fp_flags & _DC) == 0) { \
if ((f->fp_flags & _MOD) == 0) { \
if (f->_field != find->_field) \
continue; \
} else { \
if (f->_field == 0 || find->_field % f->_field) \
continue; \
} \
}
SLIST_FOREACH(f, list, fp_next) {
if (f->fp_tcpopts != find->fp_tcpopts ||
f->fp_optcnt != find->fp_optcnt ||
f->fp_ttl < find->fp_ttl ||
f->fp_ttl - find->fp_ttl > ttldiff ||
(f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
(find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
continue;
MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
if (find->fp_mss == 0)
continue;
/* Some "smart" NAT devices and DSL routers will tweak the MSS size and
* will set it to whatever is suitable for the link type.
*/
#define SMART_MSS 1460
if ((find->fp_wsize % find->fp_mss ||
find->fp_wsize / find->fp_mss !=
f->fp_wsize) &&
(find->fp_wsize % SMART_MSS ||
find->fp_wsize / SMART_MSS !=
f->fp_wsize))
continue;
} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
if (find->fp_mss == 0)
continue;
#define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr))
#define SMART_MTU (SMART_MSS + MTUOFF)
if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
find->fp_wsize / (find->fp_mss + MTUOFF) !=
f->fp_wsize) &&
(find->fp_wsize % SMART_MTU ||
find->fp_wsize / SMART_MTU !=
f->fp_wsize))
continue;
} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
if (f->fp_wsize == 0 || find->fp_wsize %
f->fp_wsize)
continue;
} else {
if (f->fp_wsize != find->fp_wsize)
continue;
}
}
return (f);
}
return (NULL);
}
/* Find an exact fingerprint in the list */
struct pf_os_fingerprint *
pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
{
struct pf_os_fingerprint *f;
SLIST_FOREACH(f, list, fp_next) {
if (f->fp_tcpopts == find->fp_tcpopts &&
f->fp_wsize == find->fp_wsize &&
f->fp_psize == find->fp_psize &&
f->fp_mss == find->fp_mss &&
f->fp_flags == find->fp_flags &&
f->fp_optcnt == find->fp_optcnt &&
f->fp_wscale == find->fp_wscale &&
f->fp_ttl == find->fp_ttl)
return (f);
}
return (NULL);
}
/* Insert a fingerprint into the list */
void
pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
{
struct pf_os_fingerprint *f, *prev = NULL;
/* XXX need to go semi tree based. can key on tcp options */
SLIST_FOREACH(f, list, fp_next)
prev = f;
if (prev)
SLIST_INSERT_AFTER(prev, ins, fp_next);
else
SLIST_INSERT_HEAD(list, ins, fp_next);
}
/* Fill a fingerprint by its number (from an ioctl) */
int
pf_osfp_get(struct pf_osfp_ioctl *fpioc)
{
struct pf_os_fingerprint *fp;
struct pf_osfp_entry *entry;
int num = fpioc->fp_getnum;
int i = 0;
memset(fpioc, 0, sizeof(*fpioc));
NET_LOCK();
PF_LOCK();
SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
if (i++ == num) {
fpioc->fp_mss = fp->fp_mss;
fpioc->fp_wsize = fp->fp_wsize;
fpioc->fp_flags = fp->fp_flags;
fpioc->fp_psize = fp->fp_psize;
fpioc->fp_ttl = fp->fp_ttl;
fpioc->fp_wscale = fp->fp_wscale;
fpioc->fp_getnum = num;
memcpy(&fpioc->fp_os, entry,
sizeof(fpioc->fp_os));
PF_UNLOCK();
NET_UNLOCK();
return (0);
}
}
}
PF_UNLOCK();
NET_UNLOCK();
return (EBUSY);
}
/* Validate that each signature is reachable */
struct pf_os_fingerprint *
pf_osfp_validate(void)
{
struct pf_os_fingerprint *f, *f2, find;
SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
memcpy(&find, f, sizeof(find));
/* We do a few MSS/th_win percolations to make things unique */
if (find.fp_mss == 0)
find.fp_mss = 128;
if (f->fp_flags & PF_OSFP_WSIZE_MSS)
find.fp_wsize *= find.fp_mss;
else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
find.fp_wsize *= (find.fp_mss + 40);
else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
find.fp_wsize *= 2;
if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
if (f2)
DPFPRINTF(LOG_NOTICE,
"Found \"%s %s %s\" instead of "
"\"%s %s %s\"\n",
SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
SLIST_FIRST(&f->fp_oses)->fp_class_nm,
SLIST_FIRST(&f->fp_oses)->fp_version_nm,
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
else
DPFPRINTF(LOG_NOTICE,
"Couldn't find \"%s %s %s\"\n",
SLIST_FIRST(&f->fp_oses)->fp_class_nm,
SLIST_FIRST(&f->fp_oses)->fp_version_nm,
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
return (f);
}
}
return (NULL);
}
51
52
2
50
51
2
51
52
52
35
37
2
2
29
16
52
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* $OpenBSD: in4_cksum.c,v 1.11 2022/02/01 15:30:10 miod Exp $ */
/* $KAME: in4_cksum.c,v 1.10 2001/11/30 10:06:15 itojun Exp $ */
/* $NetBSD: in_cksum.c,v 1.13 1996/10/13 02:03:03 christos Exp $ */
/*
* Copyright (C) 1999 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1988, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
/*
* Checksum routine for Internet Protocol family headers (Portable Version).
* This is only for IPv4 pseudo header checksum.
* No need to clear non-pseudo-header fields in IPv4 header.
* len is for actual payload size, and does not include IPv4 header and
* skipped header chain (off + len should be equal to the whole packet).
*
* This routine is very heavily used in the network
* code and should be modified for each CPU to be as fast as possible.
*/
#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
int
in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len)
{
u_int16_t *w;
int sum = 0;
int mlen = 0;
int byte_swapped = 0;
union {
struct ipovly ipov;
u_int16_t w[10];
} u;
union {
u_int8_t c[2];
u_int16_t s;
} s_util;
union {
u_int16_t s[2];
u_int32_t l;
} l_util;
if (nxt != 0) {
/* pseudo header */
if (off < sizeof(struct ipovly))
panic("in4_cksum: offset too short");
if (m->m_len < sizeof(struct ip))
panic("in4_cksum: bad mbuf chain");
u.ipov.ih_x1[8] = 0;
u.ipov.ih_pr = nxt;
u.ipov.ih_len = htons(len);
u.ipov.ih_src = mtod(m, struct ip *)->ip_src;
u.ipov.ih_dst = mtod(m, struct ip *)->ip_dst;
w = u.w;
/* assumes sizeof(ipov) == 20 and first 8 bytes are zeroes */
sum += w[4]; sum += w[5]; sum += w[6];
sum += w[7]; sum += w[8]; sum += w[9];
}
/* skip unnecessary part */
while (m && off > 0) {
if (m->m_len > off)
break;
off -= m->m_len;
m = m->m_next;
}
for (;m && len; m = m->m_next) {
if (m->m_len == 0)
continue;
w = (u_int16_t *)(mtod(m, caddr_t) + off);
if (mlen == -1) {
/*
* The first byte of this mbuf is the continuation
* of a word spanning between this mbuf and the
* last mbuf.
*
* s_util.c[0] is already saved when scanning previous
* mbuf.
*/
s_util.c[1] = *(u_int8_t *)w;
sum += s_util.s;
w = (u_int16_t *)((u_int8_t *)w + 1);
mlen = m->m_len - off - 1;
len--;
} else
mlen = m->m_len - off;
off = 0;
if (len < mlen)
mlen = len;
len -= mlen;
/*
* Force to even boundary.
*/
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(u_int8_t *)w;
w = (u_int16_t *)((int8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(u_int8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(u_int8_t *)w;
}
if (len)
printf("cksum4: out of data\n");
if (mlen == -1) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}
104
1168
66
1825
5
66
1826
1845
5
308
1654
272
272
181
182
1275
3
3
653
1772
68
71
54
72
72
61
1709
336
1710
300
336
1367
19
19
19
48
43
5
5
5
76
1897
21
264
1722
265
1722
1632
1081
20
575
113
113
59
59
10
12
56
45
15
33
3
5
31
181
181
181
8
37
42
5
143
50
65
86
42
136
181
181
107
107
43
41
2
24
27
108
16
12
3
7
3
3
204
186
25
204
3
16
16
12
10
9
14
668
670
1720
1716
1707
38
1704
1718
1194
625
568
1274
626
1661
568
3
3
15
15
8
75
1217
312
222
5
100
3
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
/* $OpenBSD: pmap.c,v 1.153 2022/06/30 13:51:24 mlarkin Exp $ */
/* $NetBSD: pmap.c,v 1.3 2003/05/08 18:13:13 thorpej Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright 2001 (c) Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Frank van der Linden for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This is the i386 pmap modified and generalized to support x86-64
* as well. The idea is to hide the upper N levels of the page tables
* inside pmap_get_ptp, pmap_free_ptp and pmap_growkernel. The rest
* is mostly untouched, except that it uses some more generalized
* macros and interfaces.
*
* This pmap has been tested on the i386 as well, and it can be easily
* adapted to PAE.
*
* fvdl@wasabisystems.com 18-Jun-2001
*/
/*
* pmap.c: i386 pmap module rewrite
* Chuck Cranor <chuck@ccrc.wustl.edu>
* 11-Aug-97
*
* history of this pmap module: in addition to my own input, i used
* the following references for this rewrite of the i386 pmap:
*
* [1] the NetBSD i386 pmap. this pmap appears to be based on the
* BSD hp300 pmap done by Mike Hibler at University of Utah.
* it was then ported to the i386 by William Jolitz of UUNET
* Technologies, Inc. Then Charles M. Hannum of the NetBSD
* project fixed some bugs and provided some speed ups.
*
* [2] the FreeBSD i386 pmap. this pmap seems to be the
* Hibler/Jolitz pmap, as modified for FreeBSD by John S. Dyson
* and David Greenman.
*
* [3] the Mach pmap. this pmap, from CMU, seems to have migrated
* between several processors. the VAX version was done by
* Avadis Tevanian, Jr., and Michael Wayne Young. the i386
* version was done by Lance Berc, Mike Kupfer, Bob Baron,
* David Golub, and Richard Draves. the alpha version was
* done by Alessandro Forin (CMU/Mach) and Chris Demetriou
* (NetBSD/alpha).
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/user.h>
#include <sys/mutex.h>
#include <uvm/uvm.h>
#include <machine/cpu.h>
#ifdef MULTIPROCESSOR
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#endif
#include "vmm.h"
#if NVMM > 0
#include <machine/vmmvar.h>
#endif /* NVMM > 0 */
#include "acpi.h"
/* #define PMAP_DEBUG */
#ifdef PMAP_DEBUG
#define DPRINTF(x...) do { printf(x); } while(0)
#else
#define DPRINTF(x...)
#endif /* PMAP_DEBUG */
/*
* general info:
*
* - for an explanation of how the i386 MMU hardware works see
* the comments in <machine/pte.h>.
*
* - for an explanation of the general memory structure used by
* this pmap (including the recursive mapping), see the comments
* in <machine/pmap.h>.
*
* this file contains the code for the "pmap module." the module's
* job is to manage the hardware's virtual to physical address mappings.
* note that there are two levels of mapping in the VM system:
*
* [1] the upper layer of the VM system uses vm_map's and vm_map_entry's
* to map ranges of virtual address space to objects/files. for
* example, the vm_map may say: "map VA 0x1000 to 0x22000 read-only
* to the file /bin/ls starting at offset zero." note that
* the upper layer mapping is not concerned with how individual
* vm_pages are mapped.
*
* [2] the lower layer of the VM system (the pmap) maintains the mappings
* from virtual addresses. it is concerned with which vm_page is
* mapped where. for example, when you run /bin/ls and start
* at page 0x1000 the fault routine may lookup the correct page
* of the /bin/ls file and then ask the pmap layer to establish
* a mapping for it.
*
* note that information in the lower layer of the VM system can be
* thrown away since it can easily be reconstructed from the info
* in the upper layer.
*
* data structures we use include:
* - struct pmap: describes the address space of one process
* - struct pv_entry: describes one <PMAP,VA> mapping of a PA
* - struct pg_to_free: a list of virtual addresses whose mappings
* have been changed. used for TLB flushing.
*/
/*
* memory allocation
*
* - there are three data structures that we must dynamically allocate:
*
* [A] new process' page directory page (PDP)
* - plan 1: done at pmap_create() we use
* pool_get(&pmap_pmap_pool, PR_WAITOK) to do this allocation.
*
* if we are low in free physical memory then we sleep in
* pool_get() -- in this case this is ok since we are creating
* a new pmap and should not be holding any locks.
*
* XXX: the fork code currently has no way to return an "out of
* memory, try again" error code since uvm_fork [fka vm_fork]
* is a void function.
*
* [B] new page tables pages (PTP)
* call uvm_pagealloc()
* => success: zero page, add to pm_pdir
* => failure: we are out of free vm_pages, let pmap_enter()
* tell UVM about it.
*
* note: for kernel PTPs, we start with NKPTP of them. as we map
* kernel memory (at uvm_map time) we check to see if we've grown
* the kernel pmap. if so, we call the optional function
* pmap_growkernel() to grow the kernel PTPs in advance.
*
* [C] pv_entry structures
* - try to allocate one from the pool.
* If we fail, we simply let pmap_enter() tell UVM about it.
*/
long nkptp[] = NKPTP_INITIALIZER;
const vaddr_t ptp_masks[] = PTP_MASK_INITIALIZER;
const int ptp_shifts[] = PTP_SHIFT_INITIALIZER;
const long nkptpmax[] = NKPTPMAX_INITIALIZER;
const long nbpd[] = NBPD_INITIALIZER;
pd_entry_t *const normal_pdes[] = PDES_INITIALIZER;
#define pmap_pte_set(p, n) atomic_swap_64(p, n)
#define pmap_pte_clearbits(p, b) x86_atomic_clearbits_u64(p, b)
#define pmap_pte_setbits(p, b) x86_atomic_setbits_u64(p, b)
/*
* global data structures
*/
struct pmap kernel_pmap_store; /* the kernel's pmap (proc0) */
/*
* pmap_pg_wc: if our processor supports PAT then we set this
* to be the pte bits for Write Combining. Else we fall back to
* UC- so mtrrs can override the cacheability;
*/
int pmap_pg_wc = PG_UCMINUS;
/*
* pmap_use_pcid: nonzero if PCID use is enabled (currently we require INVPCID)
*
* The next three are zero unless and until PCID support is enabled so code
* can just 'or' them in as needed without tests.
* cr3_pcid: CR3_REUSE_PCID
* cr3_pcid_proc and cr3_pcid_temp: PCID_PROC and PCID_TEMP
*/
#if PCID_KERN != 0
# error "pmap.c assumes PCID_KERN is zero"
#endif
int pmap_use_pcid;
static u_int cr3_pcid_proc;
static u_int cr3_pcid_temp;
/* these two are accessed from locore.o */
paddr_t cr3_reuse_pcid;
paddr_t cr3_pcid_proc_intel;
/*
* other data structures
*/
pt_entry_t protection_codes[8]; /* maps MI prot to i386 prot code */
int pmap_initialized = 0; /* pmap_init done yet? */
/*
* pv management structures.
*/
struct pool pmap_pv_pool;
/*
* linked list of all non-kernel pmaps
*/
struct pmap_head pmaps;
struct mutex pmaps_lock = MUTEX_INITIALIZER(IPL_VM);
/*
* pool that pmap structures are allocated from
*/
struct pool pmap_pmap_pool;
/*
* When we're freeing a ptp, we need to delay the freeing until all
* tlb shootdown has been done. This is the list of the to-be-freed pages.
*/
TAILQ_HEAD(pg_to_free, vm_page);
/*
* pool that PDPs are allocated from
*/
struct pool pmap_pdp_pool;
void pmap_pdp_ctor(pd_entry_t *);
void pmap_pdp_ctor_intel(pd_entry_t *);
extern vaddr_t msgbuf_vaddr;
extern paddr_t msgbuf_paddr;
extern vaddr_t idt_vaddr; /* we allocate IDT early */
extern paddr_t idt_paddr;
extern vaddr_t lo32_vaddr;
extern vaddr_t lo32_paddr;
vaddr_t virtual_avail;
extern int end;
/*
* local prototypes
*/
void pmap_enter_pv(struct vm_page *, struct pv_entry *, struct pmap *,
vaddr_t, struct vm_page *);
struct vm_page *pmap_get_ptp(struct pmap *, vaddr_t);
struct vm_page *pmap_find_ptp(struct pmap *, vaddr_t, paddr_t, int);
int pmap_find_pte_direct(struct pmap *pm, vaddr_t va, pt_entry_t **pd, int *offs);
void pmap_free_ptp(struct pmap *, struct vm_page *,
vaddr_t, struct pg_to_free *);
void pmap_freepage(struct pmap *, struct vm_page *, int, struct pg_to_free *);
#ifdef MULTIPROCESSOR
static int pmap_is_active(struct pmap *, struct cpu_info *);
#endif
paddr_t pmap_map_ptes(struct pmap *);
struct pv_entry *pmap_remove_pv(struct vm_page *, struct pmap *, vaddr_t);
void pmap_do_remove(struct pmap *, vaddr_t, vaddr_t, int);
void pmap_remove_ept(struct pmap *, vaddr_t, vaddr_t);
void pmap_do_remove_ept(struct pmap *, vaddr_t);
int pmap_enter_ept(struct pmap *, vaddr_t, paddr_t, vm_prot_t);
int pmap_remove_pte(struct pmap *, struct vm_page *, pt_entry_t *,
vaddr_t, int, struct pv_entry **);
void pmap_remove_ptes(struct pmap *, struct vm_page *, vaddr_t,
vaddr_t, vaddr_t, int, struct pv_entry **);
#define PMAP_REMOVE_ALL 0 /* remove all mappings */
#define PMAP_REMOVE_SKIPWIRED 1 /* skip wired mappings */
void pmap_unmap_ptes(struct pmap *, paddr_t);
int pmap_get_physpage(vaddr_t, int, paddr_t *);
int pmap_pdes_valid(vaddr_t, pd_entry_t *);
void pmap_alloc_level(vaddr_t, int, long *);
static inline
void pmap_sync_flags_pte(struct vm_page *, u_long);
void pmap_tlb_shootpage(struct pmap *, vaddr_t, int);
void pmap_tlb_shootrange(struct pmap *, vaddr_t, vaddr_t, int);
void pmap_tlb_shoottlb(struct pmap *, int);
#ifdef MULTIPROCESSOR
void pmap_tlb_shootwait(void);
#else
#define pmap_tlb_shootwait() do { } while (0)
#endif
/*
* p m a p i n l i n e h e l p e r f u n c t i o n s
*/
/*
* pmap_is_curpmap: is this pmap the one currently loaded [in %cr3]?
* of course the kernel is always loaded
*/
static inline int
pmap_is_curpmap(struct pmap *pmap)
{
return((pmap == pmap_kernel()) ||
(pmap->pm_pdirpa == (rcr3() & CR3_PADDR)));
}
/*
* pmap_is_active: is this pmap loaded into the specified processor's %cr3?
*/
#ifdef MULTIPROCESSOR
static inline int
pmap_is_active(struct pmap *pmap, struct cpu_info *ci)
{
return pmap == pmap_kernel() || pmap == ci->ci_proc_pmap;
}
#endif
static inline u_int
pmap_pte2flags(u_long pte)
{
return (((pte & PG_U) ? PG_PMAP_REF : 0) |
((pte & PG_M) ? PG_PMAP_MOD : 0));
}
static inline void
pmap_sync_flags_pte(struct vm_page *pg, u_long pte)
{
if (pte & (PG_U|PG_M)) {
atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(pte));
}
}
/*
* pmap_map_ptes: map a pmap's PTEs into KVM
*
* This should not be done for EPT pmaps
*/
paddr_t
pmap_map_ptes(struct pmap *pmap)
{
paddr_t cr3;
KASSERT(pmap->pm_type != PMAP_TYPE_EPT);
/* the kernel's pmap is always accessible */
if (pmap == pmap_kernel())
return 0;
/*
* Lock the target map before switching to its page tables to
* guarantee other CPUs have finished changing the tables before
* we potentially start caching table and TLB entries.
*/
mtx_enter(&pmap->pm_mtx);
cr3 = rcr3();
KASSERT((cr3 & CR3_PCID) == PCID_KERN ||
(cr3 & CR3_PCID) == PCID_PROC);
if (pmap->pm_pdirpa == (cr3 & CR3_PADDR))
cr3 = 0;
else {
cr3 |= cr3_reuse_pcid;
lcr3(pmap->pm_pdirpa | cr3_pcid_temp);
}
return cr3;
}
void
pmap_unmap_ptes(struct pmap *pmap, paddr_t save_cr3)
{
if (pmap != pmap_kernel())
mtx_leave(&pmap->pm_mtx);
if (save_cr3 != 0)
lcr3(save_cr3);
}
int
pmap_find_pte_direct(struct pmap *pm, vaddr_t va, pt_entry_t **pd, int *offs)
{
u_long mask, shift;
pd_entry_t pde;
paddr_t pdpa;
int lev;
pdpa = pm->pm_pdirpa;
shift = L4_SHIFT;
mask = L4_MASK;
for (lev = PTP_LEVELS; lev > 0; lev--) {
*pd = (pd_entry_t *)PMAP_DIRECT_MAP(pdpa);
*offs = (VA_SIGN_POS(va) & mask) >> shift;
pde = (*pd)[*offs];
/* Large pages are different, break early if we run into one. */
if ((pde & (PG_PS|PG_V)) != PG_V)
return (lev - 1);
pdpa = ((*pd)[*offs] & PG_FRAME);
/* 4096/8 == 512 == 2^9 entries per level */
shift -= 9;
mask >>= 9;
}
return (0);
}
/*
* p m a p k e n t e r f u n c t i o n s
*
* functions to quickly enter/remove pages from the kernel address
* space. pmap_kremove is exported to MI kernel. we make use of
* the recursive PTE mappings.
*/
/*
* pmap_kenter_pa: enter a kernel mapping without R/M (pv_entry) tracking
*
* => no need to lock anything, assume va is already allocated
* => should be faster than normal pmap enter function
*/
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
pt_entry_t *pte, opte, npte;
pte = kvtopte(va);
npte = (pa & PMAP_PA_MASK) | ((prot & PROT_WRITE) ? PG_RW : PG_RO) |
((pa & PMAP_NOCACHE) ? PG_N : 0) |
((pa & PMAP_WC) ? pmap_pg_wc : 0) | PG_V;
/* special 1:1 mappings in the first 2MB must not be global */
if (va >= (vaddr_t)NBPD_L2)
npte |= pg_g_kern;
if (!(prot & PROT_EXEC))
npte |= pg_nx;
opte = pmap_pte_set(pte, npte);
#ifdef LARGEPAGES
/* XXX For now... */
if (opte & PG_PS)
panic("%s: PG_PS", __func__);
#endif
if (pmap_valid_entry(opte)) {
if (pa & PMAP_NOCACHE && (opte & PG_N) == 0)
wbinvd_on_all_cpus();
/* This shouldn't happen */
pmap_tlb_shootpage(pmap_kernel(), va, 1);
pmap_tlb_shootwait();
}
}
/*
* pmap_kremove: remove a kernel mapping(s) without R/M (pv_entry) tracking
*
* => no need to lock anything
* => caller must dispose of any vm_page mapped in the va range
* => note: not an inline function
* => we assume the va is page aligned and the len is a multiple of PAGE_SIZE
* => we assume kernel only unmaps valid addresses and thus don't bother
* checking the valid bit before doing TLB flushing
*/
void
pmap_kremove(vaddr_t sva, vsize_t len)
{
pt_entry_t *pte, opte;
vaddr_t va, eva;
eva = sva + len;
for (va = sva; va != eva; va += PAGE_SIZE) {
pte = kvtopte(va);
opte = pmap_pte_set(pte, 0);
#ifdef LARGEPAGES
KASSERT((opte & PG_PS) == 0);
#endif
KASSERT((opte & PG_PVLIST) == 0);
}
pmap_tlb_shootrange(pmap_kernel(), sva, eva, 1);
pmap_tlb_shootwait();
}
/*
* pmap_set_pml4_early
*
* Utility function to map 2GB of 2MB pages to 'pa'. The VA that is assigned
* is the pml4 entry for 'early mappings' (see pmap.h). This function is used
* by display drivers that need to map their framebuffers early, before the
* pmap is fully initialized (eg, to show panic messages).
*
* Users of this function must call pmap_clear_pml4_early to remove the
* mapping when finished.
*
* Parameters:
* pa: phys addr to map
*
* Return value:
* VA mapping to 'pa'. This mapping is 2GB in size and starts at the base
* of the 2MB region containing 'va'.
*/
vaddr_t
pmap_set_pml4_early(paddr_t pa)
{
extern paddr_t early_pte_pages;
pt_entry_t *pml4e, *pte;
int i, j, off;
paddr_t curpa;
vaddr_t va;
pml4e = (pt_entry_t *)(proc0.p_addr->u_pcb.pcb_cr3 + KERNBASE);
pml4e[PDIR_SLOT_EARLY] = (pd_entry_t)early_pte_pages | PG_V | PG_RW;
off = pa & PAGE_MASK_L2;
curpa = pa & L2_FRAME;
pte = (pt_entry_t *)PMAP_DIRECT_MAP(early_pte_pages);
memset(pte, 0, 3 * NBPG);
pte[0] = (early_pte_pages + NBPG) | PG_V | PG_RW;
pte[1] = (early_pte_pages + 2 * NBPG) | PG_V | PG_RW;
pte = (pt_entry_t *)PMAP_DIRECT_MAP(early_pte_pages + NBPG);
for (i = 0; i < 2; i++) {
/* 2 early pages of mappings */
for (j = 0; j < 512; j++) {
/* j[0..511] : 2MB mappings per page */
pte[(i * 512) + j] = curpa | PG_V | PG_RW | PG_PS;
curpa += (2 * 1024 * 1024);
}
}
va = (vaddr_t)((PDIR_SLOT_EARLY * 512ULL) << L3_SHIFT) + off;
return VA_SIGN_NEG(va);
}
/*
* pmap_clear_pml4_early
*
* Clears the mapping previously established with pmap_set_pml4_early.
*/
void
pmap_clear_pml4_early(void)
{
extern paddr_t early_pte_pages;
pt_entry_t *pml4e, *pte;
pte = (pt_entry_t *)PMAP_DIRECT_MAP(early_pte_pages);
memset(pte, 0, 3 * NBPG);
pml4e = (pd_entry_t *)pmap_kernel()->pm_pdir;
pml4e[PDIR_SLOT_EARLY] = 0;
tlbflush();
}
/*
* p m a p i n i t f u n c t i o n s
*
* pmap_bootstrap and pmap_init are called during system startup
* to init the pmap module. pmap_bootstrap() does a low level
* init just to get things rolling. pmap_init() finishes the job.
*/
/*
* pmap_bootstrap: get the system in a state where it can run with VM
* properly enabled (called before main()). the VM system is
* fully init'd later...
*/
paddr_t
pmap_bootstrap(paddr_t first_avail, paddr_t max_pa)
{
vaddr_t kva_start = VM_MIN_KERNEL_ADDRESS;
struct pmap *kpm;
int curslot, i, j, p;
long ndmpdp;
paddr_t dmpd, dmpdp, start_cur, cur_pa;
vaddr_t kva, kva_end;
pt_entry_t *pml3, *pml2;
/*
* define the boundaries of the managed kernel virtual address
* space.
*/
virtual_avail = kva_start; /* first free KVA */
/*
* set up protection_codes: we need to be able to convert from
* a MI protection code (some combo of VM_PROT...) to something
* we can jam into a i386 PTE.
*/
protection_codes[PROT_NONE] = pg_nx; /* --- */
protection_codes[PROT_EXEC] = PG_RO; /* --x */
protection_codes[PROT_READ] = PG_RO | pg_nx; /* -r- */
protection_codes[PROT_READ | PROT_EXEC] = PG_RO; /* -rx */
protection_codes[PROT_WRITE] = PG_RW | pg_nx; /* w-- */
protection_codes[PROT_WRITE | PROT_EXEC] = PG_RW; /* w-x */
protection_codes[PROT_WRITE | PROT_READ] = PG_RW | pg_nx; /* wr- */
protection_codes[PROT_READ | PROT_WRITE | PROT_EXEC] = PG_RW; /* wrx */
/*
* now we init the kernel's pmap
*
* the kernel pmap's pm_obj is not used for much. however, in
* user pmaps the pm_obj contains the list of active PTPs.
* the pm_obj currently does not have a pager.
*/
kpm = pmap_kernel();
for (i = 0; i < PTP_LEVELS - 1; i++) {
uvm_obj_init(&kpm->pm_obj[i], &pmap_pager, 1);
kpm->pm_ptphint[i] = NULL;
}
memset(&kpm->pm_list, 0, sizeof(kpm->pm_list)); /* pm_list not used */
kpm->pm_pdir = (pd_entry_t *)(proc0.p_addr->u_pcb.pcb_cr3 + KERNBASE);
kpm->pm_pdirpa = proc0.p_addr->u_pcb.pcb_cr3;
kpm->pm_stats.wired_count = kpm->pm_stats.resident_count =
atop(kva_start - VM_MIN_KERNEL_ADDRESS);
/*
* the above is just a rough estimate and not critical to the proper
* operation of the system.
*/
kpm->pm_type = PMAP_TYPE_NORMAL;
curpcb->pcb_pmap = kpm; /* proc0's pcb */
/*
* Configure and enable PCID use if supported.
* Currently we require INVPCID support.
*/
if ((cpu_ecxfeature & CPUIDECX_PCID) && cpuid_level >= 0x07) {
uint32_t ebx, dummy;
CPUID_LEAF(0x7, 0, dummy, ebx, dummy, dummy);
if (ebx & SEFF0EBX_INVPCID) {
pmap_use_pcid = 1;
/*
* We cannot use global mappings because
* invpcid function 0 does not invalidate global
* mappings. The hardware can cache kernel
* mappings based on PCID_KERN, i.e. there is no
* need for global mappings.
*/
pg_g_kern = 0;
lcr4( rcr4() | CR4_PCIDE );
cr3_pcid_proc = PCID_PROC;
cr3_pcid_temp = PCID_TEMP;
cr3_reuse_pcid = CR3_REUSE_PCID;
cr3_pcid_proc_intel = PCID_PROC_INTEL;
}
}
/*
* Add PG_G attribute to already mapped kernel pages. pg_g_kern
* is calculated in locore0.S and may be set to:
*
* 0 if this CPU does not safely support global pages in the kernel
* (Intel/Meltdown)
* PG_G if this CPU does safely support global pages in the kernel
* (AMD)
*/
#if KERNBASE == VM_MIN_KERNEL_ADDRESS
for (kva = VM_MIN_KERNEL_ADDRESS ; kva < virtual_avail ;
#else
kva_end = roundup((vaddr_t)&end, PAGE_SIZE);
for (kva = KERNBASE; kva < kva_end ;
#endif
kva += PAGE_SIZE) {
unsigned long p1i = pl1_i(kva);
if (pmap_valid_entry(PTE_BASE[p1i]))
PTE_BASE[p1i] |= pg_g_kern;
}
/*
* Map the direct map. The first 4GB were mapped in locore, here
* we map the rest if it exists. We actually use the direct map
* here to set up the page tables, we're assuming that we're still
* operating in the lower 4GB of memory.
*
* Map (up to) the first 512GB of physical memory first. This part
* is handled differently than physical memory > 512GB since we have
* already mapped part of this range in locore0.
*/
ndmpdp = (max_pa + NBPD_L3 - 1) >> L3_SHIFT;
if (ndmpdp < NDML2_ENTRIES)
ndmpdp = NDML2_ENTRIES; /* At least 4GB */
if (ndmpdp > 512)
ndmpdp = 512; /* At most 512GB */
dmpdp = kpm->pm_pdir[PDIR_SLOT_DIRECT] & PG_FRAME;
dmpd = first_avail; first_avail += ndmpdp * PAGE_SIZE;
for (i = NDML2_ENTRIES; i < NPDPG * ndmpdp; i++) {
paddr_t pdp;
vaddr_t va;
pdp = (paddr_t)&(((pd_entry_t *)dmpd)[i]);
va = PMAP_DIRECT_MAP(pdp);
*((pd_entry_t *)va) = ((paddr_t)i << L2_SHIFT);
*((pd_entry_t *)va) |= PG_RW | PG_V | PG_PS | pg_g_kern | PG_U |
PG_M | pg_nx;
}
for (i = NDML2_ENTRIES; i < ndmpdp; i++) {
paddr_t pdp;
vaddr_t va;
pdp = (paddr_t)&(((pd_entry_t *)dmpdp)[i]);
va = PMAP_DIRECT_MAP(pdp);
*((pd_entry_t *)va) = dmpd + (i << PAGE_SHIFT);
*((pd_entry_t *)va) |= PG_RW | PG_V | PG_U | PG_M | pg_nx;
}
kpm->pm_pdir[PDIR_SLOT_DIRECT] = dmpdp | PG_V | PG_KW | PG_U |
PG_M | pg_nx;
/* Map any remaining physical memory > 512GB */
for (curslot = 1 ; curslot < NUM_L4_SLOT_DIRECT ; curslot++) {
/*
* Start of current range starts at PA (curslot) * 512GB
*/
start_cur = (paddr_t)(curslot * NBPD_L4);
if (max_pa > start_cur) {
/* Next 512GB, new PML4e and L3(512GB) page */
dmpd = first_avail; first_avail += PAGE_SIZE;
pml3 = (pt_entry_t *)PMAP_DIRECT_MAP(dmpd);
kpm->pm_pdir[PDIR_SLOT_DIRECT + curslot] = dmpd |
PG_KW | PG_V | PG_U | PG_M | pg_nx;
/* Calculate full 1GB pages in this 512GB region */
p = ((max_pa - start_cur) >> L3_SHIFT);
/* Check if a partial (<1GB) page remains */
if (max_pa & L2_MASK)
p++;
/*
* Handle the case where this range is full and there
* is still more memory after (p would be > 512).
*/
if (p > NPDPG)
p = NPDPG;
/* Allocate 'p' L2(1GB) pages and populate */
for (i = 0; i < p; i++) {
dmpd = first_avail; first_avail += PAGE_SIZE;
pml2 = (pt_entry_t *)PMAP_DIRECT_MAP(dmpd);
pml3[i] = dmpd |
PG_RW | PG_V | PG_U | PG_M | pg_nx;
cur_pa = start_cur + (i << L3_SHIFT);
j = 0;
while (cur_pa < max_pa && j < NPDPG) {
pml2[j] = curslot * NBPD_L4 +
(uint64_t)i * NBPD_L3 +
(uint64_t)j * NBPD_L2;
pml2[j] |= PG_RW | PG_V | pg_g_kern |
PG_U | PG_M | pg_nx | PG_PS;
cur_pa += NBPD_L2;
j++;
}
}
}
}
tlbflush();
msgbuf_vaddr = virtual_avail;
virtual_avail += round_page(MSGBUFSIZE);
idt_vaddr = virtual_avail;
virtual_avail += 2 * PAGE_SIZE;
idt_paddr = first_avail; /* steal a page */
first_avail += 2 * PAGE_SIZE;
#if defined(MULTIPROCESSOR) || \
(NACPI > 0 && !defined(SMALL_KERNEL))
/*
* Grab a page below 4G for things that need it (i.e.
* having an initial %cr3 for the MP trampoline).
*/
lo32_vaddr = virtual_avail;
virtual_avail += PAGE_SIZE;
lo32_paddr = first_avail;
first_avail += PAGE_SIZE;
#endif
/*
* init the global lists.
*/
LIST_INIT(&pmaps);
/*
* initialize the pmap pools.
*/
pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, IPL_VM, 0,
"pmappl", NULL);
pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, IPL_VM, 0,
"pvpl", &pool_allocator_single);
pool_sethiwat(&pmap_pv_pool, 32 * 1024);
/*
* initialize the PDE pool.
*/
pool_init(&pmap_pdp_pool, PAGE_SIZE, 0, IPL_VM, 0,
"pdppl", &pool_allocator_single);
kpm->pm_pdir_intel = NULL;
kpm->pm_pdirpa_intel = 0;
/*
* ensure the TLB is sync'd with reality by flushing it...
*/
tlbflush();
return first_avail;
}
/*
* pmap_randomize
*
* Randomizes the location of the kernel pmap
*/
void
pmap_randomize(void)
{
pd_entry_t *pml4va, *oldpml4va;
paddr_t pml4pa;
int i;
pml4va = km_alloc(PAGE_SIZE, &kv_page, &kp_zero, &kd_nowait);
if (pml4va == NULL)
panic("%s: km_alloc failed", __func__);
/* Copy old PML4 page to new one */
oldpml4va = pmap_kernel()->pm_pdir;
memcpy(pml4va, oldpml4va, PAGE_SIZE);
/* Switch to new PML4 */
pmap_extract(pmap_kernel(), (vaddr_t)pml4va, &pml4pa);
lcr3(pml4pa);
/* Fixup pmap_kernel and proc0's %cr3 */
pmap_kernel()->pm_pdirpa = pml4pa;
pmap_kernel()->pm_pdir = pml4va;
proc0.p_addr->u_pcb.pcb_cr3 = pml4pa;
/* Fixup recursive PTE PML4E slot. We are only changing the PA */
pml4va[PDIR_SLOT_PTE] = pml4pa | (pml4va[PDIR_SLOT_PTE] & ~PG_FRAME);
for (i = 0; i < NPDPG; i++) {
/* PTE slot already handled earlier */
if (i == PDIR_SLOT_PTE)
continue;
if (pml4va[i] & PG_FRAME)
pmap_randomize_level(&pml4va[i], 3);
}
/* Wipe out bootstrap PML4 */
memset(oldpml4va, 0, PAGE_SIZE);
tlbflush();
}
void
pmap_randomize_level(pd_entry_t *pde, int level)
{
pd_entry_t *new_pd_va;
paddr_t old_pd_pa, new_pd_pa;
vaddr_t old_pd_va;
struct vm_page *pg;
int i;
if (level == 0)
return;
if (level < PTP_LEVELS - 1 && (*pde & PG_PS))
return;
new_pd_va = km_alloc(PAGE_SIZE, &kv_page, &kp_zero, &kd_nowait);
if (new_pd_va == NULL)
panic("%s: cannot allocate page for L%d page directory",
__func__, level);
old_pd_pa = *pde & PG_FRAME;
old_pd_va = PMAP_DIRECT_MAP(old_pd_pa);
pmap_extract(pmap_kernel(), (vaddr_t)new_pd_va, &new_pd_pa);
memcpy(new_pd_va, (void *)old_pd_va, PAGE_SIZE);
*pde = new_pd_pa | (*pde & ~PG_FRAME);
tlbflush();
memset((void *)old_pd_va, 0, PAGE_SIZE);
pg = PHYS_TO_VM_PAGE(old_pd_pa);
if (pg != NULL) {
pg->wire_count--;
pmap_kernel()->pm_stats.resident_count--;
if (pg->wire_count <= 1)
uvm_pagefree(pg);
}
for (i = 0; i < NPDPG; i++)
if (new_pd_va[i] & PG_FRAME)
pmap_randomize_level(&new_pd_va[i], level - 1);
}
/*
* Pre-allocate PTPs for low memory, so that 1:1 mappings for various
* trampoline code can be entered.
*/
paddr_t
pmap_prealloc_lowmem_ptps(paddr_t first_avail)
{
pd_entry_t *pdes;
int level;
paddr_t newp;
pdes = pmap_kernel()->pm_pdir;
level = PTP_LEVELS;
for (;;) {
newp = first_avail; first_avail += PAGE_SIZE;
memset((void *)PMAP_DIRECT_MAP(newp), 0, PAGE_SIZE);
pdes[pl_i(0, level)] = (newp & PG_FRAME) | PG_V | PG_RW;
level--;
if (level <= 1)
break;
pdes = normal_pdes[level - 2];
}
return first_avail;
}
/*
* pmap_init: no further initialization required on this platform
*/
void
pmap_init(void)
{
pmap_initialized = 1;
}
/*
* p v _ e n t r y f u n c t i o n s
*/
/*
* main pv_entry manipulation functions:
* pmap_enter_pv: enter a mapping onto a pv list
* pmap_remove_pv: remove a mapping from a pv list
*/
/*
* pmap_enter_pv: enter a mapping onto a pv list
*
* => caller should adjust ptp's wire_count before calling
*
* pve: preallocated pve for us to use
* ptp: PTP in pmap that maps this VA
*/
void
pmap_enter_pv(struct vm_page *pg, struct pv_entry *pve, struct pmap *pmap,
vaddr_t va, struct vm_page *ptp)
{
pve->pv_pmap = pmap;
pve->pv_va = va;
pve->pv_ptp = ptp; /* NULL for kernel pmap */
mtx_enter(&pg->mdpage.pv_mtx);
pve->pv_next = pg->mdpage.pv_list; /* add to ... */
pg->mdpage.pv_list = pve; /* ... list */
mtx_leave(&pg->mdpage.pv_mtx);
}
/*
* pmap_remove_pv: try to remove a mapping from a pv_list
*
* => caller should adjust ptp's wire_count and free PTP if needed
* => we return the removed pve
*/
struct pv_entry *
pmap_remove_pv(struct vm_page *pg, struct pmap *pmap, vaddr_t va)
{
struct pv_entry *pve, **prevptr;
mtx_enter(&pg->mdpage.pv_mtx);
prevptr = &pg->mdpage.pv_list;
while ((pve = *prevptr) != NULL) {
if (pve->pv_pmap == pmap && pve->pv_va == va) { /* match? */
*prevptr = pve->pv_next; /* remove it! */
break;
}
prevptr = &pve->pv_next; /* previous pointer */
}
mtx_leave(&pg->mdpage.pv_mtx);
return(pve); /* return removed pve */
}
/*
* p t p f u n c t i o n s
*/
struct vm_page *
pmap_find_ptp(struct pmap *pmap, vaddr_t va, paddr_t pa, int level)
{
int lidx = level - 1;
struct vm_page *pg;
if (pa != (paddr_t)-1 && pmap->pm_ptphint[lidx] &&
pa == VM_PAGE_TO_PHYS(pmap->pm_ptphint[lidx]))
return (pmap->pm_ptphint[lidx]);
pg = uvm_pagelookup(&pmap->pm_obj[lidx], ptp_va2o(va, level));
return pg;
}
void
pmap_freepage(struct pmap *pmap, struct vm_page *ptp, int level,
struct pg_to_free *pagelist)
{
int lidx;
struct uvm_object *obj;
lidx = level - 1;
obj = &pmap->pm_obj[lidx];
pmap->pm_stats.resident_count--;
if (pmap->pm_ptphint[lidx] == ptp)
pmap->pm_ptphint[lidx] = RBT_ROOT(uvm_objtree, &obj->memt);
ptp->wire_count = 0;
uvm_pagerealloc(ptp, NULL, 0);
TAILQ_INSERT_TAIL(pagelist, ptp, pageq);
}
void
pmap_free_ptp(struct pmap *pmap, struct vm_page *ptp, vaddr_t va,
struct pg_to_free *pagelist)
{
unsigned long index;
int level;
vaddr_t invaladdr;
level = 1;
do {
pmap_freepage(pmap, ptp, level, pagelist);
index = pl_i(va, level + 1);
pmap_pte_set(&normal_pdes[level - 1][index], 0);
if (level == PTP_LEVELS - 1 && pmap->pm_pdir_intel != NULL) {
/* Zap special meltdown PML4e */
pmap_pte_set(&pmap->pm_pdir_intel[index], 0);
DPRINTF("%s: cleared meltdown PML4e @ index %lu "
"(va range start 0x%llx)\n", __func__, index,
(uint64_t)(index << L4_SHIFT));
}
invaladdr = level == 1 ? (vaddr_t)PTE_BASE :
(vaddr_t)normal_pdes[level - 2];
pmap_tlb_shootpage(pmap, invaladdr + index * PAGE_SIZE,
pmap_is_curpmap(curpcb->pcb_pmap));
if (level < PTP_LEVELS - 1) {
ptp = pmap_find_ptp(pmap, va, (paddr_t)-1, level + 1);
ptp->wire_count--;
if (ptp->wire_count > 1)
break;
}
} while (++level < PTP_LEVELS);
}
/*
* pmap_get_ptp: get a PTP (if there isn't one, allocate a new one)
*
* => pmap should NOT be pmap_kernel()
*/
struct vm_page *
pmap_get_ptp(struct pmap *pmap, vaddr_t va)
{
struct vm_page *ptp, *pptp;
int i;
unsigned long index;
pd_entry_t *pva, *pva_intel;
paddr_t ppa, pa;
struct uvm_object *obj;
ptp = NULL;
pa = (paddr_t)-1;
/*
* Loop through all page table levels seeing if we need to
* add a new page to that level.
*/
for (i = PTP_LEVELS; i > 1; i--) {
/*
* Save values from previous round.
*/
pptp = ptp;
ppa = pa;
index = pl_i(va, i);
pva = normal_pdes[i - 2];
if (pmap_valid_entry(pva[index])) {
ppa = pva[index] & PG_FRAME;
ptp = NULL;
continue;
}
obj = &pmap->pm_obj[i-2];
ptp = uvm_pagealloc(obj, ptp_va2o(va, i - 1), NULL,
UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptp == NULL)
return NULL;
atomic_clearbits_int(&ptp->pg_flags, PG_BUSY);
ptp->wire_count = 1;
pmap->pm_ptphint[i - 2] = ptp;
pa = VM_PAGE_TO_PHYS(ptp);
pva[index] = (pd_entry_t) (pa | PG_u | PG_RW | PG_V);
/*
* Meltdown Special case - if we are adding a new PML4e for
* usermode addresses, just copy the PML4e to the U-K page
* table.
*/
if (pmap->pm_pdir_intel != NULL && i == PTP_LEVELS &&
va < VM_MAXUSER_ADDRESS) {
pva_intel = pmap->pm_pdir_intel;
pva_intel[index] = pva[index];
DPRINTF("%s: copying usermode PML4e (content=0x%llx) "
"from 0x%llx -> 0x%llx\n", __func__, pva[index],
(uint64_t)&pva[index], (uint64_t)&pva_intel[index]);
}
pmap->pm_stats.resident_count++;
/*
* If we're not in the top level, increase the
* wire count of the parent page.
*/
if (i < PTP_LEVELS) {
if (pptp == NULL)
pptp = pmap_find_ptp(pmap, va, ppa, i);
#ifdef DIAGNOSTIC
if (pptp == NULL)
panic("%s: pde page disappeared", __func__);
#endif
pptp->wire_count++;
}
}
/*
* ptp is not NULL if we just allocated a new ptp. If it's
* still NULL, we must look up the existing one.
*/
if (ptp == NULL) {
ptp = pmap_find_ptp(pmap, va, ppa, 1);
#ifdef DIAGNOSTIC
if (ptp == NULL) {
printf("va %lx ppa %lx\n", (unsigned long)va,
(unsigned long)ppa);
panic("%s: unmanaged user PTP", __func__);
}
#endif
}
pmap->pm_ptphint[0] = ptp;
return(ptp);
}
/*
* p m a p l i f e c y c l e f u n c t i o n s
*/
/*
* pmap_pdp_ctor: constructor for the PDP cache.
*/
void
pmap_pdp_ctor(pd_entry_t *pdir)
{
paddr_t pdirpa;
int npde, i;
struct pmap *kpm = pmap_kernel();
/* fetch the physical address of the page directory. */
(void) pmap_extract(kpm, (vaddr_t) pdir, &pdirpa);
/* zero init area */
memset(pdir, 0, PDIR_SLOT_PTE * sizeof(pd_entry_t));
/* put in recursive PDE to map the PTEs */
pdir[PDIR_SLOT_PTE] = pdirpa | PG_V | PG_KW | pg_nx;
npde = nkptp[PTP_LEVELS - 1];
/* put in kernel VM PDEs */
memcpy(&pdir[PDIR_SLOT_KERN], &PDP_BASE[PDIR_SLOT_KERN],
npde * sizeof(pd_entry_t));
/* zero the rest */
memset(&pdir[PDIR_SLOT_KERN + npde], 0,
(NTOPLEVEL_PDES - (PDIR_SLOT_KERN + npde)) * sizeof(pd_entry_t));
for (i = 0; i < NUM_L4_SLOT_DIRECT; i++)
pdir[PDIR_SLOT_DIRECT + i] = kpm->pm_pdir[PDIR_SLOT_DIRECT + i];
#if VM_MIN_KERNEL_ADDRESS != KERNBASE
pdir[pl4_pi(KERNBASE)] = PDP_BASE[pl4_pi(KERNBASE)];
#endif
}
void
pmap_pdp_ctor_intel(pd_entry_t *pdir)
{
struct pmap *kpm = pmap_kernel();
/* Copy PML4es from pmap_kernel's U-K view */
memcpy(pdir, kpm->pm_pdir_intel, PAGE_SIZE);
}
/*
* pmap_create: create a pmap
*
* => note: old pmap interface took a "size" args which allowed for
* the creation of "software only" pmaps (not in bsd).
*/
struct pmap *
pmap_create(void)
{
struct pmap *pmap;
int i;
pmap = pool_get(&pmap_pmap_pool, PR_WAITOK);
mtx_init(&pmap->pm_mtx, IPL_VM);
/* init uvm_object */
for (i = 0; i < PTP_LEVELS - 1; i++) {
uvm_obj_init(&pmap->pm_obj[i], &pmap_pager, 1);
pmap->pm_ptphint[i] = NULL;
}
pmap->pm_stats.wired_count = 0;
pmap->pm_stats.resident_count = 1; /* count the PDP allocd below */
pmap->pm_type = PMAP_TYPE_NORMAL;
pmap->eptp = 0;
/* allocate PDP */
/*
* note that there is no need to splvm to protect us from
* malloc since malloc allocates out of a submap and we should
* have already allocated kernel PTPs to cover the range...
*/
pmap->pm_pdir = pool_get(&pmap_pdp_pool, PR_WAITOK);
pmap_pdp_ctor(pmap->pm_pdir);
pmap->pm_pdirpa = pmap->pm_pdir[PDIR_SLOT_PTE] & PG_FRAME;
/*
* Intel CPUs need a special page table to be used during usermode
* execution, one that lacks all kernel mappings.
*/
if (cpu_meltdown) {
pmap->pm_pdir_intel = pool_get(&pmap_pdp_pool, PR_WAITOK);
pmap_pdp_ctor_intel(pmap->pm_pdir_intel);
pmap->pm_stats.resident_count++;
if (!pmap_extract(pmap_kernel(), (vaddr_t)pmap->pm_pdir_intel,
&pmap->pm_pdirpa_intel))
panic("%s: unknown PA mapping for meltdown PML4",
__func__);
} else {
pmap->pm_pdir_intel = NULL;
pmap->pm_pdirpa_intel = 0;
}
mtx_enter(&pmaps_lock);
LIST_INSERT_HEAD(&pmaps, pmap, pm_list);
mtx_leave(&pmaps_lock);
return (pmap);
}
/*
* pmap_destroy: drop reference count on pmap. free pmap if
* reference count goes to zero.
*/
void
pmap_destroy(struct pmap *pmap)
{
struct vm_page *pg;
int refs;
int i;
/*
* drop reference count
*/
refs = atomic_dec_int_nv(&pmap->pm_obj[0].uo_refs);
if (refs > 0) {
return;
}
/*
* remove it from global list of pmaps
*/
mtx_enter(&pmaps_lock);
LIST_REMOVE(pmap, pm_list);
mtx_leave(&pmaps_lock);
/*
* free any remaining PTPs
*/
for (i = 0; i < PTP_LEVELS - 1; i++) {
while ((pg = RBT_ROOT(uvm_objtree,
&pmap->pm_obj[i].memt)) != NULL) {
KASSERT((pg->pg_flags & PG_BUSY) == 0);
pg->wire_count = 0;
pmap->pm_stats.resident_count--;
uvm_pagefree(pg);
}
}
pool_put(&pmap_pdp_pool, pmap->pm_pdir);
if (pmap->pm_pdir_intel != NULL) {
pmap->pm_stats.resident_count--;
pool_put(&pmap_pdp_pool, pmap->pm_pdir_intel);
}
pool_put(&pmap_pmap_pool, pmap);
}
/*
* Add a reference to the specified pmap.
*/
void
pmap_reference(struct pmap *pmap)
{
atomic_inc_int(&pmap->pm_obj[0].uo_refs);
}
/*
* pmap_activate: activate a process' pmap (fill in %cr3)
*
* => called from cpu_fork() and when switching pmaps during exec
* => if p is the curproc, then load it into the MMU
*/
void
pmap_activate(struct proc *p)
{
struct pcb *pcb = &p->p_addr->u_pcb;
struct pmap *pmap = p->p_vmspace->vm_map.pmap;
pcb->pcb_pmap = pmap;
pcb->pcb_cr3 = pmap->pm_pdirpa;
pcb->pcb_cr3 |= (pmap != pmap_kernel()) ? cr3_pcid_proc :
(PCID_KERN | cr3_reuse_pcid);
if (p != curproc)
return;
if ((p->p_flag & P_SYSTEM) == 0) {
struct cpu_info *self = curcpu();
/* mark the pmap in use by this processor */
self->ci_proc_pmap = pmap;
/* in case we return to userspace without context switching */
if (cpu_meltdown) {
self->ci_kern_cr3 = pcb->pcb_cr3 | cr3_reuse_pcid;
self->ci_user_cr3 = pmap->pm_pdirpa_intel |
cr3_pcid_proc_intel;
}
}
lcr3(pcb->pcb_cr3);
}
/*
* pmap_deactivate: deactivate a process' pmap
*/
void
pmap_deactivate(struct proc *p)
{
if ((p->p_flag & P_SYSTEM) == 0) {
struct cpu_info *self = curcpu();
/*
* mark the pmap no longer in use by this processor.
*/
KASSERT(self->ci_proc_pmap == p->p_vmspace->vm_map.pmap);
self->ci_proc_pmap = NULL;
}
}
/*
* end of lifecycle functions
*/
/*
* some misc. functions
*/
int
pmap_pdes_valid(vaddr_t va, pd_entry_t *lastpde)
{
int i;
unsigned long index;
pd_entry_t pde;
for (i = PTP_LEVELS; i > 1; i--) {
index = pl_i(va, i);
pde = normal_pdes[i - 2][index];
if (!pmap_valid_entry(pde))
return 0;
}
if (lastpde != NULL)
*lastpde = pde;
return 1;
}
/*
* pmap_extract: extract a PA for the given VA
*/
int
pmap_extract(struct pmap *pmap, vaddr_t va, paddr_t *pap)
{
pt_entry_t *ptes, pte;
int level, offs;
if (pmap == pmap_kernel() && va >= PMAP_DIRECT_BASE &&
va < PMAP_DIRECT_END) {
*pap = va - PMAP_DIRECT_BASE;
return 1;
}
if (pmap != pmap_kernel())
mtx_enter(&pmap->pm_mtx);
level = pmap_find_pte_direct(pmap, va, &ptes, &offs);
pte = ptes[offs];
if (pmap != pmap_kernel())
mtx_leave(&pmap->pm_mtx);
if (__predict_true(level == 0 && pmap_valid_entry(pte))) {
if (pap != NULL)
*pap = (pte & PG_FRAME) | (va & PAGE_MASK);
return 1;
}
if (level == 1 && (pte & (PG_PS|PG_V)) == (PG_PS|PG_V)) {
if (pap != NULL)
*pap = (pte & PG_LGFRAME) | (va & PAGE_MASK_L2);
return 1;
}
return 0;
}
/*
* pmap_zero_page: zero a page
*/
void
pmap_zero_page(struct vm_page *pg)
{
pagezero(pmap_map_direct(pg));
}
/*
* pmap_flush_cache: flush the cache for a virtual address.
*/
void
pmap_flush_cache(vaddr_t addr, vsize_t len)
{
vaddr_t i;
if (curcpu()->ci_cflushsz == 0) {
wbinvd_on_all_cpus();
return;
}
/* all cpus that have clflush also have mfence. */
mfence();
for (i = addr; i < addr + len; i += curcpu()->ci_cflushsz)
clflush(i);
mfence();
}
/*
* pmap_copy_page: copy a page
*/
void
pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg)
{
vaddr_t srcva = pmap_map_direct(srcpg);
vaddr_t dstva = pmap_map_direct(dstpg);
memcpy((void *)dstva, (void *)srcva, PAGE_SIZE);
}
/*
* p m a p r e m o v e f u n c t i o n s
*
* functions that remove mappings
*/
/*
* pmap_remove_ptes: remove PTEs from a PTP
*
* => PTP must be mapped into KVA
* => PTP should be null if pmap == pmap_kernel()
*/
void
pmap_remove_ptes(struct pmap *pmap, struct vm_page *ptp, vaddr_t ptpva,
vaddr_t startva, vaddr_t endva, int flags, struct pv_entry **free_pvs)
{
struct pv_entry *pve;
pt_entry_t *pte = (pt_entry_t *) ptpva;
struct vm_page *pg;
pt_entry_t opte;
/*
* note that ptpva points to the PTE that maps startva. this may
* or may not be the first PTE in the PTP.
*
* we loop through the PTP while there are still PTEs to look at
* and the wire_count is greater than 1 (because we use the wire_count
* to keep track of the number of real PTEs in the PTP).
*/
for (/*null*/; startva < endva && (ptp == NULL || ptp->wire_count > 1)
; pte++, startva += PAGE_SIZE) {
if (!pmap_valid_entry(*pte))
continue; /* VA not mapped */
if ((flags & PMAP_REMOVE_SKIPWIRED) && (*pte & PG_W)) {
continue;
}
/* atomically save the old PTE and zap! it */
opte = pmap_pte_set(pte, 0);
if (opte & PG_W)
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
if (ptp != NULL)
ptp->wire_count--; /* dropping a PTE */
pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);
/*
* if we are not on a pv list we are done.
*/
if ((opte & PG_PVLIST) == 0) {
#ifdef DIAGNOSTIC
if (pg != NULL)
panic("%s: managed page without PG_PVLIST: "
"va 0x%lx, opte 0x%llx", __func__,
startva, opte);
#endif
continue;
}
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("%s: unmanaged page marked PG_PVLIST: "
"va 0x%lx, opte 0x%llx", __func__,
startva, opte);
#endif
/* sync R/M bits */
pmap_sync_flags_pte(pg, opte);
pve = pmap_remove_pv(pg, pmap, startva);
if (pve != NULL) {
pve->pv_next = *free_pvs;
*free_pvs = pve;
}
/* end of "for" loop: time for next pte */
}
}
/*
* pmap_remove_pte: remove a single PTE from a PTP
*
* => PTP must be mapped into KVA
* => PTP should be null if pmap == pmap_kernel()
* => returns true if we removed a mapping
*/
int
pmap_remove_pte(struct pmap *pmap, struct vm_page *ptp, pt_entry_t *pte,
vaddr_t va, int flags, struct pv_entry **free_pvs)
{
struct pv_entry *pve;
struct vm_page *pg;
pt_entry_t opte;
if (!pmap_valid_entry(*pte))
return 0; /* VA not mapped */
if ((flags & PMAP_REMOVE_SKIPWIRED) && (*pte & PG_W)) {
return 0;
}
/* atomically save the old PTE and zap! it */
opte = pmap_pte_set(pte, 0);
if (opte & PG_W)
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
if (ptp != NULL)
ptp->wire_count--; /* dropping a PTE */
pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);
/*
* if we are not on a pv list we are done.
*/
if ((opte & PG_PVLIST) == 0) {
#ifdef DIAGNOSTIC
if (pg != NULL)
panic("%s: managed page without PG_PVLIST: "
"va 0x%lx, opte 0x%llx", __func__, va, opte);
#endif
return 1;
}
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("%s: unmanaged page marked PG_PVLIST: "
"va 0x%lx, opte 0x%llx", __func__, va, opte);
#endif
/* sync R/M bits */
pmap_sync_flags_pte(pg, opte);
pve = pmap_remove_pv(pg, pmap, va);
if (pve != NULL) {
pve->pv_next = *free_pvs;
*free_pvs = pve;
}
return 1;
}
/*
* pmap_remove: top level mapping removal function
*
* => caller should not be holding any pmap locks
*/
void
pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
{
if (pmap->pm_type == PMAP_TYPE_EPT)
pmap_remove_ept(pmap, sva, eva);
else
pmap_do_remove(pmap, sva, eva, PMAP_REMOVE_ALL);
}
/*
* pmap_do_remove: mapping removal guts
*
* => caller should not be holding any pmap locks
*/
void
pmap_do_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int flags)
{
pd_entry_t pde;
int result;
paddr_t ptppa;
vaddr_t blkendva;
struct vm_page *ptp;
struct pv_entry *pve;
struct pv_entry *free_pvs = NULL;
vaddr_t va;
int shootall = 0, shootself;
struct pg_to_free empty_ptps;
paddr_t scr3;
TAILQ_INIT(&empty_ptps);
scr3 = pmap_map_ptes(pmap);
shootself = (scr3 == 0);
/*
* removing one page? take shortcut function.
*/
if (sva + PAGE_SIZE == eva) {
if (pmap_pdes_valid(sva, &pde)) {
/* PA of the PTP */
ptppa = pde & PG_FRAME;
/* get PTP if non-kernel mapping */
if (pmap == pmap_kernel()) {
/* we never free kernel PTPs */
ptp = NULL;
} else {
ptp = pmap_find_ptp(pmap, sva, ptppa, 1);
#ifdef DIAGNOSTIC
if (ptp == NULL)
panic("%s: unmanaged PTP detected "
"in shortcut path", __func__);
#endif
}
/* do it! */
result = pmap_remove_pte(pmap, ptp,
&PTE_BASE[pl1_i(sva)], sva, flags, &free_pvs);
/*
* if mapping removed and the PTP is no longer
* being used, free it!
*/
if (result && ptp && ptp->wire_count <= 1)
pmap_free_ptp(pmap, ptp, sva, &empty_ptps);
pmap_tlb_shootpage(pmap, sva, shootself);
pmap_unmap_ptes(pmap, scr3);
pmap_tlb_shootwait();
} else {
pmap_unmap_ptes(pmap, scr3);
}
goto cleanup;
}
if ((eva - sva > 32 * PAGE_SIZE) && sva < VM_MIN_KERNEL_ADDRESS)
shootall = 1;
for (va = sva; va < eva; va = blkendva) {
/* determine range of block */
blkendva = x86_round_pdr(va + 1);
if (blkendva > eva)
blkendva = eva;
/*
* XXXCDC: our PTE mappings should never be removed
* with pmap_remove! if we allow this (and why would
* we?) then we end up freeing the pmap's page
* directory page (PDP) before we are finished using
* it when we hit in in the recursive mapping. this
* is BAD.
*
* long term solution is to move the PTEs out of user
* address space. and into kernel address space (up
* with APTE). then we can set VM_MAXUSER_ADDRESS to
* be VM_MAX_ADDRESS.
*/
if (pl_i(va, PTP_LEVELS) == PDIR_SLOT_PTE)
/* XXXCDC: ugly hack to avoid freeing PDP here */
continue;
if (!pmap_pdes_valid(va, &pde))
continue;
/* PA of the PTP */
ptppa = pde & PG_FRAME;
/* get PTP if non-kernel mapping */
if (pmap == pmap_kernel()) {
/* we never free kernel PTPs */
ptp = NULL;
} else {
ptp = pmap_find_ptp(pmap, va, ptppa, 1);
#ifdef DIAGNOSTIC
if (ptp == NULL)
panic("%s: unmanaged PTP detected", __func__);
#endif
}
pmap_remove_ptes(pmap, ptp, (vaddr_t)&PTE_BASE[pl1_i(va)],
va, blkendva, flags, &free_pvs);
/* if PTP is no longer being used, free it! */
if (ptp && ptp->wire_count <= 1) {
pmap_free_ptp(pmap, ptp, va, &empty_ptps);
}
}
if (shootall)
pmap_tlb_shoottlb(pmap, shootself);
else
pmap_tlb_shootrange(pmap, sva, eva, shootself);
pmap_unmap_ptes(pmap, scr3);
pmap_tlb_shootwait();
cleanup:
while ((pve = free_pvs) != NULL) {
free_pvs = pve->pv_next;
pool_put(&pmap_pv_pool, pve);
}
while ((ptp = TAILQ_FIRST(&empty_ptps)) != NULL) {
TAILQ_REMOVE(&empty_ptps, ptp, pageq);
uvm_pagefree(ptp);
}
}
/*
* pmap_page_remove: remove a managed vm_page from all pmaps that map it
*
* => R/M bits are sync'd back to attrs
*/
void
pmap_page_remove(struct vm_page *pg)
{
struct pv_entry *pve;
struct pmap *pm;
pt_entry_t opte;
#ifdef DIAGNOSTIC
pd_entry_t pde;
#endif
struct pg_to_free empty_ptps;
struct vm_page *ptp;
paddr_t scr3;
int shootself;
TAILQ_INIT(&empty_ptps);
mtx_enter(&pg->mdpage.pv_mtx);
while ((pve = pg->mdpage.pv_list) != NULL) {
pmap_reference(pve->pv_pmap);
pm = pve->pv_pmap;
mtx_leave(&pg->mdpage.pv_mtx);
/* XXX use direct map? */
scr3 = pmap_map_ptes(pm); /* locks pmap */
shootself = (scr3 == 0);
/*
* We dropped the pvlist lock before grabbing the pmap
* lock to avoid lock ordering problems. This means
* we have to check the pvlist again since somebody
* else might have modified it. All we care about is
* that the pvlist entry matches the pmap we just
* locked. If it doesn't, unlock the pmap and try
* again.
*/
mtx_enter(&pg->mdpage.pv_mtx);
if ((pve = pg->mdpage.pv_list) == NULL ||
pve->pv_pmap != pm) {
mtx_leave(&pg->mdpage.pv_mtx);
pmap_unmap_ptes(pm, scr3); /* unlocks pmap */
pmap_destroy(pm);
mtx_enter(&pg->mdpage.pv_mtx);
continue;
}
pg->mdpage.pv_list = pve->pv_next;
mtx_leave(&pg->mdpage.pv_mtx);
#ifdef DIAGNOSTIC
if (pve->pv_ptp != NULL && pmap_pdes_valid(pve->pv_va, &pde) &&
(pde & PG_FRAME) != VM_PAGE_TO_PHYS(pve->pv_ptp)) {
printf("%s: pg=%p: va=%lx, pv_ptp=%p\n", __func__,
pg, pve->pv_va, pve->pv_ptp);
printf("%s: PTP's phys addr: "
"actual=%lx, recorded=%lx\n", __func__,
(unsigned long)(pde & PG_FRAME),
VM_PAGE_TO_PHYS(pve->pv_ptp));
panic("%s: mapped managed page has "
"invalid pv_ptp field", __func__);
}
#endif
/* atomically save the old PTE and zap it */
opte = pmap_pte_set(&PTE_BASE[pl1_i(pve->pv_va)], 0);
if (opte & PG_W)
pve->pv_pmap->pm_stats.wired_count--;
pve->pv_pmap->pm_stats.resident_count--;
pmap_tlb_shootpage(pve->pv_pmap, pve->pv_va, shootself);
pmap_sync_flags_pte(pg, opte);
/* update the PTP reference count. free if last reference. */
if (pve->pv_ptp != NULL) {
pve->pv_ptp->wire_count--;
if (pve->pv_ptp->wire_count <= 1) {
pmap_free_ptp(pve->pv_pmap, pve->pv_ptp,
pve->pv_va, &empty_ptps);
}
}
pmap_unmap_ptes(pve->pv_pmap, scr3); /* unlocks pmap */
pmap_destroy(pve->pv_pmap);
pool_put(&pmap_pv_pool, pve);
mtx_enter(&pg->mdpage.pv_mtx);
}
mtx_leave(&pg->mdpage.pv_mtx);
pmap_tlb_shootwait();
while ((ptp = TAILQ_FIRST(&empty_ptps)) != NULL) {
TAILQ_REMOVE(&empty_ptps, ptp, pageq);
uvm_pagefree(ptp);
}
}
/*
* p m a p a t t r i b u t e f u n c t i o n s
* functions that test/change managed page's attributes
* since a page can be mapped multiple times we must check each PTE that
* maps it by going down the pv lists.
*/
/*
* pmap_test_attrs: test a page's attributes
*/
int
pmap_test_attrs(struct vm_page *pg, unsigned int testbits)
{
struct pv_entry *pve;
pt_entry_t *ptes;
int level, offs;
u_long mybits, testflags;
testflags = pmap_pte2flags(testbits);
if (pg->pg_flags & testflags)
return 1;
mybits = 0;
mtx_enter(&pg->mdpage.pv_mtx);
for (pve = pg->mdpage.pv_list; pve != NULL && mybits == 0;
pve = pve->pv_next) {
level = pmap_find_pte_direct(pve->pv_pmap, pve->pv_va, &ptes,
&offs);
mybits |= (ptes[offs] & testbits);
}
mtx_leave(&pg->mdpage.pv_mtx);
if (mybits == 0)
return 0;
atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(mybits));
return 1;
}
/*
* pmap_clear_attrs: change a page's attributes
*
* => we return 1 if we cleared one of the bits we were asked to
*/
int
pmap_clear_attrs(struct vm_page *pg, unsigned long clearbits)
{
struct pv_entry *pve;
pt_entry_t *ptes, opte;
u_long clearflags;
int result, level, offs;
clearflags = pmap_pte2flags(clearbits);
result = pg->pg_flags & clearflags;
if (result)
atomic_clearbits_int(&pg->pg_flags, clearflags);
mtx_enter(&pg->mdpage.pv_mtx);
for (pve = pg->mdpage.pv_list; pve != NULL; pve = pve->pv_next) {
level = pmap_find_pte_direct(pve->pv_pmap, pve->pv_va, &ptes,
&offs);
opte = ptes[offs];
if (opte & clearbits) {
result = 1;
pmap_pte_clearbits(&ptes[offs], (opte & clearbits));
pmap_tlb_shootpage(pve->pv_pmap, pve->pv_va,
pmap_is_curpmap(pve->pv_pmap));
}
}
mtx_leave(&pg->mdpage.pv_mtx);
pmap_tlb_shootwait();
return (result != 0);
}
/*
* p m a p p r o t e c t i o n f u n c t i o n s
*/
/*
* pmap_page_protect: change the protection of all recorded mappings
* of a managed page
*
* => NOTE: this is an inline function in pmap.h
*/
/* see pmap.h */
/*
* pmap_protect: set the protection in of the pages in a pmap
*
* => NOTE: this is an inline function in pmap.h
*/
/* see pmap.h */
/*
* pmap_write_protect: write-protect pages in a pmap
*/
void
pmap_write_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
pt_entry_t nx, *spte, *epte;
vaddr_t blockend;
int shootall = 0, shootself;
vaddr_t va;
paddr_t scr3;
scr3 = pmap_map_ptes(pmap);
shootself = (scr3 == 0);
/* should be ok, but just in case ... */
sva &= PG_FRAME;
eva &= PG_FRAME;
nx = 0;
if (!(prot & PROT_EXEC))
nx = pg_nx;
if ((eva - sva > 32 * PAGE_SIZE) && sva < VM_MIN_KERNEL_ADDRESS)
shootall = 1;
for (va = sva; va < eva ; va = blockend) {
blockend = (va & L2_FRAME) + NBPD_L2;
if (blockend > eva)
blockend = eva;
/*
* XXXCDC: our PTE mappings should never be write-protected!
*
* long term solution is to move the PTEs out of user
* address space. and into kernel address space (up
* with APTE). then we can set VM_MAXUSER_ADDRESS to
* be VM_MAX_ADDRESS.
*/
/* XXXCDC: ugly hack to avoid freeing PDP here */
if (pl_i(va, PTP_LEVELS) == PDIR_SLOT_PTE)
continue;
/* empty block? */
if (!pmap_pdes_valid(va, NULL))
continue;
#ifdef DIAGNOSTIC
if (va >= VM_MAXUSER_ADDRESS && va < VM_MAX_ADDRESS)
panic("%s: PTE space", __func__);
#endif
spte = &PTE_BASE[pl1_i(va)];
epte = &PTE_BASE[pl1_i(blockend)];
for (/*null */; spte < epte ; spte++) {
if (!pmap_valid_entry(*spte))
continue;
pmap_pte_clearbits(spte, PG_RW);
pmap_pte_setbits(spte, nx);
}
}
if (shootall)
pmap_tlb_shoottlb(pmap, shootself);
else
pmap_tlb_shootrange(pmap, sva, eva, shootself);
pmap_unmap_ptes(pmap, scr3);
pmap_tlb_shootwait();
}
/*
* end of protection functions
*/
/*
* pmap_unwire: clear the wired bit in the PTE
*
* => mapping should already be in map
*/
void
pmap_unwire(struct pmap *pmap, vaddr_t va)
{
pt_entry_t *ptes;
int level, offs;
level = pmap_find_pte_direct(pmap, va, &ptes, &offs);
if (level == 0) {
#ifdef DIAGNOSTIC
if (!pmap_valid_entry(ptes[offs]))
panic("%s: invalid (unmapped) va 0x%lx", __func__, va);
#endif
if (__predict_true((ptes[offs] & PG_W) != 0)) {
pmap_pte_clearbits(&ptes[offs], PG_W);
pmap->pm_stats.wired_count--;
}
#ifdef DIAGNOSTIC
else {
printf("%s: wiring for pmap %p va 0x%lx "
"didn't change!\n", __func__, pmap, va);
}
#endif
}
#ifdef DIAGNOSTIC
else {
panic("%s: invalid PDE", __func__);
}
#endif
}
/*
* pmap_collect: free resources held by a pmap
*
* => optional function.
* => called when a process is swapped out to free memory.
*/
void
pmap_collect(struct pmap *pmap)
{
/*
* free all of the pt pages by removing the physical mappings
* for its entire address space.
*/
/* pmap_do_remove(pmap, VM_MIN_ADDRESS, VM_MAX_ADDRESS,
PMAP_REMOVE_SKIPWIRED);
*/
}
/*
* pmap_copy: copy mappings from one pmap to another
*
* => optional function
* void pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
*/
/*
* defined as macro in pmap.h
*/
void
pmap_enter_special(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
uint64_t l4idx, l3idx, l2idx, l1idx;
pd_entry_t *pd, *ptp;
paddr_t npa;
struct pmap *pmap = pmap_kernel();
pt_entry_t *ptes;
int level, offs;
/* If CPU is secure, no need to do anything */
if (!cpu_meltdown)
return;
/* Must be kernel VA */
if (va < VM_MIN_KERNEL_ADDRESS)
panic("%s: invalid special mapping va 0x%lx requested",
__func__, va);
if (pmap->pm_pdir_intel == NULL)
pmap->pm_pdir_intel = pool_get(&pmap_pdp_pool,
PR_WAITOK | PR_ZERO);
l4idx = (va & L4_MASK) >> L4_SHIFT; /* PML4E idx */
l3idx = (va & L3_MASK) >> L3_SHIFT; /* PDPTE idx */
l2idx = (va & L2_MASK) >> L2_SHIFT; /* PDE idx */
l1idx = (va & L1_MASK) >> L1_SHIFT; /* PTE idx */
DPRINTF("%s: va=0x%llx pa=0x%llx l4idx=%lld l3idx=%lld "
"l2idx=%lld l1idx=%lld\n", __func__, (uint64_t)va,
(uint64_t)pa, l4idx, l3idx, l2idx, l1idx);
/* Start at PML4 / top level */
pd = pmap->pm_pdir_intel;
if (pd == NULL)
panic("%s: PML4 not initialized for pmap @ %p", __func__,
pmap);
/* npa = physaddr of PDPT */
npa = pd[l4idx] & PMAP_PA_MASK;
/* Valid PML4e for the 512GB region containing va? */
if (!npa) {
/* No valid PML4E - allocate PDPT page and set PML4E */
ptp = pool_get(&pmap_pdp_pool, PR_WAITOK | PR_ZERO);
if (!pmap_extract(pmap, (vaddr_t)ptp, &npa))
panic("%s: can't locate PDPT page", __func__);
pd[l4idx] = (npa | PG_RW | PG_V);
DPRINTF("%s: allocated new PDPT page at phys 0x%llx, "
"setting PML4e[%lld] = 0x%llx\n", __func__,
(uint64_t)npa, l4idx, pd[l4idx]);
}
pd = (pd_entry_t *)PMAP_DIRECT_MAP(npa);
if (pd == NULL)
panic("%s: can't locate PDPT @ pa=0x%llx", __func__,
(uint64_t)npa);
/* npa = physaddr of PD page */
npa = pd[l3idx] & PMAP_PA_MASK;
/* Valid PDPTe for the 1GB region containing va? */
if (!npa) {
/* No valid PDPTe - allocate PD page and set PDPTe */
ptp = pool_get(&pmap_pdp_pool, PR_WAITOK | PR_ZERO);
if (!pmap_extract(pmap, (vaddr_t)ptp, &npa))
panic("%s: can't locate PD page", __func__);
pd[l3idx] = (npa | PG_RW | PG_V);
DPRINTF("%s: allocated new PD page at phys 0x%llx, "
"setting PDPTe[%lld] = 0x%llx\n", __func__,
(uint64_t)npa, l3idx, pd[l3idx]);
}
pd = (pd_entry_t *)PMAP_DIRECT_MAP(npa);
if (pd == NULL)
panic("%s: can't locate PD page @ pa=0x%llx", __func__,
(uint64_t)npa);
/* npa = physaddr of PT page */
npa = pd[l2idx] & PMAP_PA_MASK;
/* Valid PDE for the 2MB region containing va? */
if (!npa) {
/* No valid PDE - allocate PT page and set PDE */
ptp = pool_get(&pmap_pdp_pool, PR_WAITOK | PR_ZERO);
if (!pmap_extract(pmap, (vaddr_t)ptp, &npa))
panic("%s: can't locate PT page", __func__);
pd[l2idx] = (npa | PG_RW | PG_V);
DPRINTF("%s: allocated new PT page at phys 0x%llx, "
"setting PDE[%lld] = 0x%llx\n", __func__,
(uint64_t)npa, l2idx, pd[l2idx]);
}
pd = (pd_entry_t *)PMAP_DIRECT_MAP(npa);
if (pd == NULL)
panic("%s: can't locate PT page @ pa=0x%llx", __func__,
(uint64_t)npa);
DPRINTF("%s: setting PTE, PT page @ phys 0x%llx virt 0x%llx prot "
"0x%llx was 0x%llx\n", __func__, (uint64_t)npa, (uint64_t)pd,
(uint64_t)prot, (uint64_t)pd[l1idx]);
pd[l1idx] = pa | protection_codes[prot] | PG_V | PG_W;
/*
* Look up the corresponding U+K entry. If we're installing the
* same PA into the U-K map then set the PG_G bit on both and copy
* the cache-control bits from the U+K entry to the U-K entry.
*/
level = pmap_find_pte_direct(pmap, va, &ptes, &offs);
if (__predict_true(level == 0 && pmap_valid_entry(ptes[offs]))) {
if (((pd[l1idx] ^ ptes[offs]) & PG_FRAME) == 0) {
pd[l1idx] |= PG_G | (ptes[offs] & (PG_N | PG_WT));
ptes[offs] |= PG_G;
} else {
DPRINTF("%s: special diffing mapping at %llx\n",
__func__, (long long)va);
}
} else
DPRINTF("%s: no U+K mapping for special mapping?\n", __func__);
DPRINTF("%s: setting PTE[%lld] = 0x%llx\n", __func__, l1idx, pd[l1idx]);
}
void
pmap_remove_ept(struct pmap *pmap, vaddr_t sgpa, vaddr_t egpa)
{
vaddr_t v;
#if NVMM > 0
struct vmx_invept_descriptor vid;
#endif /* NVMM > 0 */
DPRINTF("%s: sgpa=0x%llx egpa=0x%llx\n", __func__, (uint64_t)sgpa,
(uint64_t)egpa);
for (v = sgpa; v < egpa + PAGE_SIZE; v += PAGE_SIZE)
pmap_do_remove_ept(pmap, v);
#if NVMM > 0
if (pmap->eptp != 0) {
memset(&vid, 0, sizeof(vid));
vid.vid_eptp = pmap->eptp;
DPRINTF("%s: flushing EPT TLB for EPTP 0x%llx\n", __func__,
vid.vid_eptp);
invept(IA32_VMX_INVEPT_SINGLE_CTX, &vid);
}
#endif /* NVMM > 0 */
}
void
pmap_do_remove_ept(struct pmap *pmap, paddr_t gpa)
{
uint64_t l4idx, l3idx, l2idx, l1idx;
struct vm_page *pg3, *pg2, *pg1;
paddr_t npa3, npa2, npa1;
pd_entry_t *pd4, *pd3, *pd2, *pd1;
pd_entry_t *pptes;
l4idx = (gpa & L4_MASK) >> L4_SHIFT; /* PML4E idx */
l3idx = (gpa & L3_MASK) >> L3_SHIFT; /* PDPTE idx */
l2idx = (gpa & L2_MASK) >> L2_SHIFT; /* PDE idx */
l1idx = (gpa & L1_MASK) >> L1_SHIFT; /* PTE idx */
/* Start at PML4 / top level */
pd4 = (pd_entry_t *)pmap->pm_pdir;
if (pd4 == NULL)
return;
/* npa3 = physaddr of PDPT */
npa3 = pd4[l4idx] & PMAP_PA_MASK;
if (!npa3)
return;
pd3 = (pd_entry_t *)PMAP_DIRECT_MAP(npa3);
pg3 = PHYS_TO_VM_PAGE(npa3);
/* npa2 = physaddr of PD page */
npa2 = pd3[l3idx] & PMAP_PA_MASK;
if (!npa2)
return;
pd2 = (pd_entry_t *)PMAP_DIRECT_MAP(npa2);
pg2 = PHYS_TO_VM_PAGE(npa2);
/* npa1 = physaddr of PT page */
npa1 = pd2[l2idx] & PMAP_PA_MASK;
if (!npa1)
return;
pd1 = (pd_entry_t *)PMAP_DIRECT_MAP(npa1);
pg1 = PHYS_TO_VM_PAGE(npa1);
if (pd1[l1idx] == 0)
return;
pd1[l1idx] = 0;
pg1->wire_count--;
pmap->pm_stats.resident_count--;
if (pg1->wire_count > 1)
return;
pg1->wire_count = 0;
pptes = (pd_entry_t *)PMAP_DIRECT_MAP(npa2);
pptes[l2idx] = 0;
uvm_pagefree(pg1);
pmap->pm_stats.resident_count--;
pg2->wire_count--;
if (pg2->wire_count > 1)
return;
pg2->wire_count = 0;
pptes = (pd_entry_t *)PMAP_DIRECT_MAP(npa3);
pptes[l3idx] = 0;
uvm_pagefree(pg2);
pmap->pm_stats.resident_count--;
pg3->wire_count--;
if (pg3->wire_count > 1)
return;
pg3->wire_count = 0;
pptes = pd4;
pptes[l4idx] = 0;
uvm_pagefree(pg3);
pmap->pm_stats.resident_count--;
}
int
pmap_enter_ept(struct pmap *pmap, paddr_t gpa, paddr_t hpa, vm_prot_t prot)
{
uint64_t l4idx, l3idx, l2idx, l1idx;
pd_entry_t *pd, npte;
struct vm_page *ptp, *pptp;
paddr_t npa;
struct uvm_object *obj;
if (gpa > MAXDSIZ)
return ENOMEM;
l4idx = (gpa & L4_MASK) >> L4_SHIFT; /* PML4E idx */
l3idx = (gpa & L3_MASK) >> L3_SHIFT; /* PDPTE idx */
l2idx = (gpa & L2_MASK) >> L2_SHIFT; /* PDE idx */
l1idx = (gpa & L1_MASK) >> L1_SHIFT; /* PTE idx */
/* Start at PML4 / top level */
pd = (pd_entry_t *)pmap->pm_pdir;
if (pd == NULL)
return ENOMEM;
/* npa = physaddr of PDPT */
npa = pd[l4idx] & PMAP_PA_MASK;
/* Valid PML4e for the 512GB region containing gpa? */
if (!npa) {
/* No valid PML4e - allocate PDPT page and set PML4e */
obj = &pmap->pm_obj[2]; /* PML4 UVM object */
ptp = uvm_pagealloc(obj, ptp_va2o(gpa, 3), NULL,
UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptp == NULL)
return ENOMEM;
/*
* New PDPT page - we are setting the first entry, so set
* the wired count to 1
*/
ptp->wire_count = 1;
/* Calculate phys address of this new PDPT page */
npa = VM_PAGE_TO_PHYS(ptp);
/*
* Higher levels get full perms; specific permissions are
* entered at the lowest level.
*/
pd[l4idx] = (npa | EPT_R | EPT_W | EPT_X);
pmap->pm_stats.resident_count++;
pptp = ptp;
} else {
/* Already allocated PML4e */
pptp = PHYS_TO_VM_PAGE(npa);
}
pd = (pd_entry_t *)PMAP_DIRECT_MAP(npa);
if (pd == NULL)
panic("%s: can't locate PDPT @ pa=0x%llx", __func__,
(uint64_t)npa);
/* npa = physaddr of PD page */
npa = pd[l3idx] & PMAP_PA_MASK;
/* Valid PDPTe for the 1GB region containing gpa? */
if (!npa) {
/* No valid PDPTe - allocate PD page and set PDPTe */
obj = &pmap->pm_obj[1]; /* PDPT UVM object */
ptp = uvm_pagealloc(obj, ptp_va2o(gpa, 2), NULL,
UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptp == NULL)
return ENOMEM;
/*
* New PD page - we are setting the first entry, so set
* the wired count to 1
*/
ptp->wire_count = 1;
pptp->wire_count++;
npa = VM_PAGE_TO_PHYS(ptp);
/*
* Higher levels get full perms; specific permissions are
* entered at the lowest level.
*/
pd[l3idx] = (npa | EPT_R | EPT_W | EPT_X);
pmap->pm_stats.resident_count++;
pptp = ptp;
} else {
/* Already allocated PDPTe */
pptp = PHYS_TO_VM_PAGE(npa);
}
pd = (pd_entry_t *)PMAP_DIRECT_MAP(npa);
if (pd == NULL)
panic("%s: can't locate PD page @ pa=0x%llx", __func__,
(uint64_t)npa);
/* npa = physaddr of PT page */
npa = pd[l2idx] & PMAP_PA_MASK;
/* Valid PDE for the 2MB region containing gpa? */
if (!npa) {
/* No valid PDE - allocate PT page and set PDE */
obj = &pmap->pm_obj[0]; /* PDE UVM object */
ptp = uvm_pagealloc(obj, ptp_va2o(gpa, 1), NULL,
UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptp == NULL)
return ENOMEM;
pptp->wire_count++;
npa = VM_PAGE_TO_PHYS(ptp);
/*
* Higher level get full perms; specific permissions are
* entered at the lowest level.
*/
pd[l2idx] = (npa | EPT_R | EPT_W | EPT_X);
pmap->pm_stats.resident_count++;
} else {
/* Find final ptp */
ptp = PHYS_TO_VM_PAGE(npa);
if (ptp == NULL)
panic("%s: ptp page vanished?", __func__);
}
pd = (pd_entry_t *)PMAP_DIRECT_MAP(npa);
if (pd == NULL)
panic("%s: can't locate PT page @ pa=0x%llx", __func__,
(uint64_t)npa);
npte = hpa | EPT_WB;
if (prot & PROT_READ)
npte |= EPT_R;
if (prot & PROT_WRITE)
npte |= EPT_W;
if (prot & PROT_EXEC)
npte |= EPT_X;
if (pd[l1idx] == 0) {
ptp->wire_count++;
pmap->pm_stats.resident_count++;
} else {
/* XXX flush ept */
}
pd[l1idx] = npte;
return 0;
}
/*
* pmap_enter: enter a mapping into a pmap
*
* => must be done "now" ... no lazy-evaluation
*/
int
pmap_enter(struct pmap *pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
{
pt_entry_t opte, npte;
struct vm_page *ptp, *pg = NULL;
struct pv_entry *pve, *opve = NULL;
int ptpdelta, wireddelta, resdelta;
int wired = (flags & PMAP_WIRED) != 0;
int nocache = (pa & PMAP_NOCACHE) != 0;
int wc = (pa & PMAP_WC) != 0;
int error, shootself;
paddr_t scr3;
if (pmap->pm_type == PMAP_TYPE_EPT)
return pmap_enter_ept(pmap, va, pa, prot);
KASSERT(!(wc && nocache));
pa &= PMAP_PA_MASK;
#ifdef DIAGNOSTIC
if (va == (vaddr_t) PDP_BASE)
panic("%s: trying to map over PDP!", __func__);
/* sanity check: kernel PTPs should already have been pre-allocated */
if (va >= VM_MIN_KERNEL_ADDRESS &&
!pmap_valid_entry(pmap->pm_pdir[pl_i(va, PTP_LEVELS)]))
panic("%s: missing kernel PTP for va %lx!", __func__, va);
#endif
pve = pool_get(&pmap_pv_pool, PR_NOWAIT);
if (pve == NULL) {
if (flags & PMAP_CANFAIL) {
error = ENOMEM;
goto out;
}
panic("%s: no pv entries available", __func__);
}
/*
* map in ptes and get a pointer to our PTP (unless we are the kernel)
*/
scr3 = pmap_map_ptes(pmap);
shootself = (scr3 == 0);
if (pmap == pmap_kernel()) {
ptp = NULL;
} else {
ptp = pmap_get_ptp(pmap, va);
if (ptp == NULL) {
if (flags & PMAP_CANFAIL) {
pmap_unmap_ptes(pmap, scr3);
error = ENOMEM;
goto out;
}
panic("%s: get ptp failed", __func__);
}
}
opte = PTE_BASE[pl1_i(va)]; /* old PTE */
/*
* is there currently a valid mapping at our VA?
*/
if (pmap_valid_entry(opte)) {
/*
* first, calculate pm_stats updates. resident count will not
* change since we are replacing/changing a valid mapping.
* wired count might change...
*/
resdelta = 0;
if (wired && (opte & PG_W) == 0)
wireddelta = 1;
else if (!wired && (opte & PG_W) != 0)
wireddelta = -1;
else
wireddelta = 0;
ptpdelta = 0;
/*
* is the currently mapped PA the same as the one we
* want to map?
*/
if ((opte & PG_FRAME) == pa) {
/* if this is on the PVLIST, sync R/M bit */
if (opte & PG_PVLIST) {
pg = PHYS_TO_VM_PAGE(pa);
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("%s: same pa, PG_PVLIST "
"mapping with unmanaged page: "
"va 0x%lx, opte 0x%llx, pa 0x%lx",
__func__, va, opte, pa);
#endif
pmap_sync_flags_pte(pg, opte);
} else {
#ifdef DIAGNOSTIC
if (PHYS_TO_VM_PAGE(pa) != NULL)
panic("%s: same pa, no PG_PVLIST "
"mapping with managed page: "
"va 0x%lx, opte 0x%llx, pa 0x%lx",
__func__, va, opte, pa);
#endif
}
goto enter_now;
}
/*
* changing PAs: we must remove the old one first
*/
/*
* if current mapping is on a pvlist,
* remove it (sync R/M bits)
*/
if (opte & PG_PVLIST) {
pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("%s: PG_PVLIST mapping with unmanaged "
"page: va 0x%lx, opte 0x%llx, pa 0x%lx",
__func__, va, opte, pa);
#endif
pmap_sync_flags_pte(pg, opte);
opve = pmap_remove_pv(pg, pmap, va);
pg = NULL; /* This is not the page we are looking for */
}
} else { /* opte not valid */
resdelta = 1;
if (wired)
wireddelta = 1;
else
wireddelta = 0;
if (ptp != NULL)
ptpdelta = 1;
else
ptpdelta = 0;
}
/*
* pve is either NULL or points to a now-free pv_entry structure
* (the latter case is if we called pmap_remove_pv above).
*
* if this entry is to be on a pvlist, enter it now.
*/
if (pmap_initialized)
pg = PHYS_TO_VM_PAGE(pa);
if (pg != NULL) {
pmap_enter_pv(pg, pve, pmap, va, ptp);
pve = NULL;
}
enter_now:
/*
* at this point pg is !NULL if we want the PG_PVLIST bit set
*/
pmap->pm_stats.resident_count += resdelta;
pmap->pm_stats.wired_count += wireddelta;
if (ptp != NULL)
ptp->wire_count += ptpdelta;
KASSERT(pg == PHYS_TO_VM_PAGE(pa));
npte = pa | protection_codes[prot] | PG_V;
if (pg != NULL) {
npte |= PG_PVLIST;
/*
* make sure that if the page is write combined all
* instances of pmap_enter make it so.
*/
if (pg->pg_flags & PG_PMAP_WC) {
KASSERT(nocache == 0);
wc = 1;
}
}
if (wc)
npte |= pmap_pg_wc;
if (wired)
npte |= PG_W;
if (nocache)
npte |= PG_N;
if (va < VM_MAXUSER_ADDRESS)
npte |= PG_u;
else if (va < VM_MAX_ADDRESS)
npte |= (PG_u | PG_RW); /* XXXCDC: no longer needed? */
if (pmap == pmap_kernel())
npte |= pg_g_kern;
/*
* If the old entry wasn't valid, we can just update it and
* go. If it was valid, and this isn't a read->write
* transition, then we can safely just update it and flush
* any old TLB entries.
*
* If it _was_ valid and this _is_ a read->write transition,
* then this could be a CoW resolution and we need to make
* sure no CPU can see the new writable mapping while another
* still has the old mapping in its TLB, so insert a correct
* but unwritable mapping, flush any old TLB entries, then
* make it writable.
*/
if (! pmap_valid_entry(opte)) {
PTE_BASE[pl1_i(va)] = npte;
} else if ((opte | (npte ^ PG_RW)) & PG_RW) {
/* previously writable or not making writable */
PTE_BASE[pl1_i(va)] = npte;
if (nocache && (opte & PG_N) == 0)
wbinvd_on_all_cpus();
pmap_tlb_shootpage(pmap, va, shootself);
} else {
PTE_BASE[pl1_i(va)] = npte ^ PG_RW;
if (nocache && (opte & PG_N) == 0) /* XXX impossible? */
wbinvd_on_all_cpus();
pmap_tlb_shootpage(pmap, va, shootself);
pmap_tlb_shootwait();
PTE_BASE[pl1_i(va)] = npte;
}
pmap_unmap_ptes(pmap, scr3);
pmap_tlb_shootwait();
error = 0;
out:
if (pve != NULL)
pool_put(&pmap_pv_pool, pve);
if (opve != NULL)
pool_put(&pmap_pv_pool, opve);
return error;
}
int
pmap_get_physpage(vaddr_t va, int level, paddr_t *paddrp)
{
struct vm_page *ptp;
struct pmap *kpm = pmap_kernel();
if (uvm.page_init_done == 0) {
vaddr_t va;
/*
* we're growing the kernel pmap early (from
* uvm_pageboot_alloc()). this case must be
* handled a little differently.
*/
va = pmap_steal_memory(PAGE_SIZE, NULL, NULL);
*paddrp = PMAP_DIRECT_UNMAP(va);
} else {
ptp = uvm_pagealloc(&kpm->pm_obj[level - 1],
ptp_va2o(va, level), NULL,
UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptp == NULL)
panic("%s: out of memory", __func__);
atomic_clearbits_int(&ptp->pg_flags, PG_BUSY);
ptp->wire_count = 1;
*paddrp = VM_PAGE_TO_PHYS(ptp);
}
kpm->pm_stats.resident_count++;
return 1;
}
/*
* Allocate the amount of specified ptps for a ptp level, and populate
* all levels below accordingly, mapping virtual addresses starting at
* kva.
*
* Used by pmap_growkernel.
*/
void
pmap_alloc_level(vaddr_t kva, int lvl, long *needed_ptps)
{
unsigned long i;
vaddr_t va;
paddr_t pa;
unsigned long index, endindex;
int level;
pd_entry_t *pdep;
for (level = lvl; level > 1; level--) {
if (level == PTP_LEVELS)
pdep = pmap_kernel()->pm_pdir;
else
pdep = normal_pdes[level - 2];
va = kva;
index = pl_i(kva, level);
endindex = index + needed_ptps[level - 1];
/*
* XXX special case for first time call.
*/
if (nkptp[level - 1] != 0)
index++;
else
endindex--;
for (i = index; i <= endindex; i++) {
pmap_get_physpage(va, level - 1, &pa);
pdep[i] = pa | PG_RW | PG_V | pg_nx;
nkptp[level - 1]++;
va += nbpd[level - 1];
}
}
}
/*
* pmap_growkernel: increase usage of KVM space
*
* => we allocate new PTPs for the kernel and install them in all
* the pmaps on the system.
*/
static vaddr_t pmap_maxkvaddr = VM_MIN_KERNEL_ADDRESS;
vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
struct pmap *kpm = pmap_kernel(), *pm;
int s, i;
unsigned newpdes;
long needed_kptp[PTP_LEVELS], target_nptp, old;
if (maxkvaddr <= pmap_maxkvaddr)
return pmap_maxkvaddr;
maxkvaddr = x86_round_pdr(maxkvaddr);
old = nkptp[PTP_LEVELS - 1];
/*
* This loop could be optimized more, but pmap_growkernel()
* is called infrequently.
*/
for (i = PTP_LEVELS - 1; i >= 1; i--) {
target_nptp = pl_i(maxkvaddr, i + 1) -
pl_i(VM_MIN_KERNEL_ADDRESS, i + 1);
/*
* XXX only need to check toplevel.
*/
if (target_nptp > nkptpmax[i])
panic("%s: out of KVA space", __func__);
needed_kptp[i] = target_nptp - nkptp[i] + 1;
}
s = splhigh(); /* to be safe */
pmap_alloc_level(pmap_maxkvaddr, PTP_LEVELS, needed_kptp);
/*
* If the number of top level entries changed, update all
* pmaps.
*/
if (needed_kptp[PTP_LEVELS - 1] != 0) {
newpdes = nkptp[PTP_LEVELS - 1] - old;
mtx_enter(&pmaps_lock);
LIST_FOREACH(pm, &pmaps, pm_list) {
memcpy(&pm->pm_pdir[PDIR_SLOT_KERN + old],
&kpm->pm_pdir[PDIR_SLOT_KERN + old],
newpdes * sizeof (pd_entry_t));
}
mtx_leave(&pmaps_lock);
}
pmap_maxkvaddr = maxkvaddr;
splx(s);
return maxkvaddr;
}
vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *start, vaddr_t *end)
{
int segno;
u_int npg;
vaddr_t va;
paddr_t pa;
struct vm_physseg *seg;
size = round_page(size);
npg = atop(size);
for (segno = 0, seg = vm_physmem; segno < vm_nphysseg; segno++, seg++) {
if (seg->avail_end - seg->avail_start < npg)
continue;
/*
* We can only steal at an ``unused'' segment boundary,
* i.e. either at the start or at the end.
*/
if (seg->avail_start == seg->start ||
seg->avail_end == seg->end)
break;
}
if (segno == vm_nphysseg) {
panic("%s: out of memory", __func__);
} else {
if (seg->avail_start == seg->start) {
pa = ptoa(seg->avail_start);
seg->avail_start += npg;
seg->start += npg;
} else {
pa = ptoa(seg->avail_end) - size;
seg->avail_end -= npg;
seg->end -= npg;
}
/*
* If all the segment has been consumed now, remove it.
* Note that the crash dump code still knows about it
* and will dump it correctly.
*/
if (seg->start == seg->end) {
if (vm_nphysseg-- == 1)
panic("%s: out of memory", __func__);
while (segno < vm_nphysseg) {
seg[0] = seg[1]; /* struct copy */
seg++;
segno++;
}
}
va = PMAP_DIRECT_MAP(pa);
memset((void *)va, 0, size);
}
if (start != NULL)
*start = virtual_avail;
if (end != NULL)
*end = VM_MAX_KERNEL_ADDRESS;
return (va);
}
void
pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
*vstartp = virtual_avail;
*vendp = VM_MAX_KERNEL_ADDRESS;
}
/*
* pmap_convert
*
* Converts 'pmap' to the new 'mode'.
*
* Parameters:
* pmap: the pmap to convert
* mode: the new mode (see pmap.h, PMAP_TYPE_xxx)
*/
void
pmap_convert(struct pmap *pmap, int mode)
{
pt_entry_t *pte;
pmap->pm_type = mode;
if (mode == PMAP_TYPE_EPT) {
/* Clear PML4 */
pte = (pt_entry_t *)pmap->pm_pdir;
memset(pte, 0, PAGE_SIZE);
/* Give back the meltdown pdir */
if (pmap->pm_pdir_intel != NULL) {
pool_put(&pmap_pdp_pool, pmap->pm_pdir_intel);
pmap->pm_pdir_intel = NULL;
}
}
}
#ifdef MULTIPROCESSOR
/*
* Locking for tlb shootdown.
*
* We lock by setting tlb_shoot_wait to the number of cpus that will
* receive our tlb shootdown. After sending the IPIs, we don't need to
* worry about locking order or interrupts spinning for the lock because
* the call that grabs the "lock" isn't the one that releases it. And
* there is nothing that can block the IPI that releases the lock.
*
* The functions are organized so that we first count the number of
* cpus we need to send the IPI to, then we grab the counter, then
* we send the IPIs, then we finally do our own shootdown.
*
* Our shootdown is last to make it parallel with the other cpus
* to shorten the spin time.
*
* Notice that we depend on failures to send IPIs only being able to
* happen during boot. If they happen later, the above assumption
* doesn't hold since we can end up in situations where noone will
* release the lock if we get an interrupt in a bad moment.
*/
#ifdef MP_LOCKDEBUG
#include <ddb/db_output.h>
extern int __mp_lock_spinout;
#endif
volatile long tlb_shoot_wait __attribute__((section(".kudata")));
volatile vaddr_t tlb_shoot_addr1 __attribute__((section(".kudata")));
volatile vaddr_t tlb_shoot_addr2 __attribute__((section(".kudata")));
volatile int tlb_shoot_first_pcid __attribute__((section(".kudata")));
/* Obtain the "lock" for TLB shooting */
static inline int
pmap_start_tlb_shoot(long wait, const char *func)
{
int s = splvm();
while (atomic_cas_ulong(&tlb_shoot_wait, 0, wait) != 0) {
#ifdef MP_LOCKDEBUG
int nticks = __mp_lock_spinout;
#endif
while (tlb_shoot_wait != 0) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spun out", func);
db_enter();
nticks = __mp_lock_spinout;
}
#endif
}
}
return s;
}
void
pmap_tlb_shootpage(struct pmap *pm, vaddr_t va, int shootself)
{
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
long wait = 0;
u_int64_t mask = 0;
int is_kva = va >= VM_MIN_KERNEL_ADDRESS;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !(ci->ci_flags & CPUF_RUNNING))
continue;
if (!is_kva && !pmap_is_active(pm, ci))
continue;
mask |= (1ULL << ci->ci_cpuid);
wait++;
}
if (wait > 0) {
int s = pmap_start_tlb_shoot(wait, __func__);
tlb_shoot_first_pcid = is_kva ? PCID_KERN : PCID_PROC;
tlb_shoot_addr1 = va;
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (x86_fast_ipi(ci, LAPIC_IPI_INVLPG) != 0)
panic("%s: ipi failed", __func__);
}
splx(s);
}
if (!pmap_use_pcid) {
if (shootself)
pmap_update_pg(va);
} else if (is_kva) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
invpcid(INVPCID_ADDR, PCID_KERN, va);
} else if (shootself) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
if (cpu_meltdown)
invpcid(INVPCID_ADDR, PCID_PROC_INTEL, va);
}
}
void
pmap_tlb_shootrange(struct pmap *pm, vaddr_t sva, vaddr_t eva, int shootself)
{
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
long wait = 0;
u_int64_t mask = 0;
int is_kva = sva >= VM_MIN_KERNEL_ADDRESS;
vaddr_t va;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !(ci->ci_flags & CPUF_RUNNING))
continue;
if (!is_kva && !pmap_is_active(pm, ci))
continue;
mask |= (1ULL << ci->ci_cpuid);
wait++;
}
if (wait > 0) {
int s = pmap_start_tlb_shoot(wait, __func__);
tlb_shoot_first_pcid = is_kva ? PCID_KERN : PCID_PROC;
tlb_shoot_addr1 = sva;
tlb_shoot_addr2 = eva;
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (x86_fast_ipi(ci, LAPIC_IPI_INVLRANGE) != 0)
panic("%s: ipi failed", __func__);
}
splx(s);
}
if (!pmap_use_pcid) {
if (shootself) {
for (va = sva; va < eva; va += PAGE_SIZE)
pmap_update_pg(va);
}
} else if (is_kva) {
for (va = sva; va < eva; va += PAGE_SIZE) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
invpcid(INVPCID_ADDR, PCID_KERN, va);
}
} else if (shootself) {
if (cpu_meltdown) {
for (va = sva; va < eva; va += PAGE_SIZE) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
invpcid(INVPCID_ADDR, PCID_PROC_INTEL, va);
}
} else {
for (va = sva; va < eva; va += PAGE_SIZE)
invpcid(INVPCID_ADDR, PCID_PROC, va);
}
}
}
void
pmap_tlb_shoottlb(struct pmap *pm, int shootself)
{
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
long wait = 0;
u_int64_t mask = 0;
KASSERT(pm != pmap_kernel());
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !pmap_is_active(pm, ci) ||
!(ci->ci_flags & CPUF_RUNNING))
continue;
mask |= (1ULL << ci->ci_cpuid);
wait++;
}
if (wait) {
int s = pmap_start_tlb_shoot(wait, __func__);
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (x86_fast_ipi(ci, LAPIC_IPI_INVLTLB) != 0)
panic("%s: ipi failed", __func__);
}
splx(s);
}
if (shootself) {
if (!pmap_use_pcid)
tlbflush();
else {
invpcid(INVPCID_PCID, PCID_PROC, 0);
if (cpu_meltdown)
invpcid(INVPCID_PCID, PCID_PROC_INTEL, 0);
}
}
}
void
pmap_tlb_shootwait(void)
{
#ifdef MP_LOCKDEBUG
int nticks = __mp_lock_spinout;
#endif
while (tlb_shoot_wait != 0) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spun out", __func__);
db_enter();
nticks = __mp_lock_spinout;
}
#endif
}
}
#else /* MULTIPROCESSOR */
void
pmap_tlb_shootpage(struct pmap *pm, vaddr_t va, int shootself)
{
if (!pmap_use_pcid) {
if (shootself)
pmap_update_pg(va);
} else if (va >= VM_MIN_KERNEL_ADDRESS) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
invpcid(INVPCID_ADDR, PCID_KERN, va);
} else if (shootself) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
if (cpu_meltdown)
invpcid(INVPCID_ADDR, PCID_PROC_INTEL, va);
}
}
void
pmap_tlb_shootrange(struct pmap *pm, vaddr_t sva, vaddr_t eva, int shootself)
{
vaddr_t va;
if (!pmap_use_pcid) {
if (shootself) {
for (va = sva; va < eva; va += PAGE_SIZE)
pmap_update_pg(va);
}
} else if (sva >= VM_MIN_KERNEL_ADDRESS) {
for (va = sva; va < eva; va += PAGE_SIZE) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
invpcid(INVPCID_ADDR, PCID_KERN, va);
}
} else if (shootself) {
if (cpu_meltdown) {
for (va = sva; va < eva; va += PAGE_SIZE) {
invpcid(INVPCID_ADDR, PCID_PROC, va);
invpcid(INVPCID_ADDR, PCID_PROC_INTEL, va);
}
} else {
for (va = sva; va < eva; va += PAGE_SIZE)
invpcid(INVPCID_ADDR, PCID_PROC, va);
}
}
}
void
pmap_tlb_shoottlb(struct pmap *pm, int shootself)
{
if (shootself) {
if (!pmap_use_pcid)
tlbflush();
else {
invpcid(INVPCID_PCID, PCID_PROC, 0);
if (cpu_meltdown)
invpcid(INVPCID_PCID, PCID_PROC_INTEL, 0);
}
}
}
#endif /* MULTIPROCESSOR */
3464
1959
541
632
489
286
743
795
43
286
617
300
136
677
669
48
182
1746
438
375
232
337
455
355
394
163
1173
655
680
320
615
315
423
547
498
302
427
190
321
678
467
760
254
87
603
533
744
218
1671
231
1568
1734
366
1730
1745
1744
1333
1104
502
2771
2678
2282
1654
3596
3590
2849
2513
795
2333
468
479
359
347
275
8456
8461
1229
1222
1230
14
14
14
8434
8178
540
553
530
531
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
/* $OpenBSD: subr_tree.c,v 1.10 2018/10/09 08:28:43 dlg Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/tree.h>
static inline struct rb_entry *
rb_n2e(const struct rb_type *t, void *node)
{
unsigned long addr = (unsigned long)node;
return ((struct rb_entry *)(addr + t->t_offset));
}
static inline void *
rb_e2n(const struct rb_type *t, struct rb_entry *rbe)
{
unsigned long addr = (unsigned long)rbe;
return ((void *)(addr - t->t_offset));
}
#define RBE_LEFT(_rbe) (_rbe)->rbt_left
#define RBE_RIGHT(_rbe) (_rbe)->rbt_right
#define RBE_PARENT(_rbe) (_rbe)->rbt_parent
#define RBE_COLOR(_rbe) (_rbe)->rbt_color
#define RBH_ROOT(_rbt) (_rbt)->rbt_root
static inline void
rbe_set(struct rb_entry *rbe, struct rb_entry *parent)
{
RBE_PARENT(rbe) = parent;
RBE_LEFT(rbe) = RBE_RIGHT(rbe) = NULL;
RBE_COLOR(rbe) = RB_RED;
}
static inline void
rbe_set_blackred(struct rb_entry *black, struct rb_entry *red)
{
RBE_COLOR(black) = RB_BLACK;
RBE_COLOR(red) = RB_RED;
}
static inline void
rbe_augment(const struct rb_type *t, struct rb_entry *rbe)
{
(*t->t_augment)(rb_e2n(t, rbe));
}
static inline void
rbe_if_augment(const struct rb_type *t, struct rb_entry *rbe)
{
if (t->t_augment != NULL)
rbe_augment(t, rbe);
}
static inline void
rbe_rotate_left(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *rbe)
{
struct rb_entry *parent;
struct rb_entry *tmp;
tmp = RBE_RIGHT(rbe);
RBE_RIGHT(rbe) = RBE_LEFT(tmp);
if (RBE_RIGHT(rbe) != NULL)
RBE_PARENT(RBE_LEFT(tmp)) = rbe;
parent = RBE_PARENT(rbe);
RBE_PARENT(tmp) = parent;
if (parent != NULL) {
if (rbe == RBE_LEFT(parent))
RBE_LEFT(parent) = tmp;
else
RBE_RIGHT(parent) = tmp;
} else
RBH_ROOT(rbt) = tmp;
RBE_LEFT(tmp) = rbe;
RBE_PARENT(rbe) = tmp;
if (t->t_augment != NULL) {
rbe_augment(t, rbe);
rbe_augment(t, tmp);
parent = RBE_PARENT(tmp);
if (parent != NULL)
rbe_augment(t, parent);
}
}
static inline void
rbe_rotate_right(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *rbe)
{
struct rb_entry *parent;
struct rb_entry *tmp;
tmp = RBE_LEFT(rbe);
RBE_LEFT(rbe) = RBE_RIGHT(tmp);
if (RBE_LEFT(rbe) != NULL)
RBE_PARENT(RBE_RIGHT(tmp)) = rbe;
parent = RBE_PARENT(rbe);
RBE_PARENT(tmp) = parent;
if (parent != NULL) {
if (rbe == RBE_LEFT(parent))
RBE_LEFT(parent) = tmp;
else
RBE_RIGHT(parent) = tmp;
} else
RBH_ROOT(rbt) = tmp;
RBE_RIGHT(tmp) = rbe;
RBE_PARENT(rbe) = tmp;
if (t->t_augment != NULL) {
rbe_augment(t, rbe);
rbe_augment(t, tmp);
parent = RBE_PARENT(tmp);
if (parent != NULL)
rbe_augment(t, parent);
}
}
static inline void
rbe_insert_color(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *rbe)
{
struct rb_entry *parent, *gparent, *tmp;
while ((parent = RBE_PARENT(rbe)) != NULL &&
RBE_COLOR(parent) == RB_RED) {
gparent = RBE_PARENT(parent);
if (parent == RBE_LEFT(gparent)) {
tmp = RBE_RIGHT(gparent);
if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) {
RBE_COLOR(tmp) = RB_BLACK;
rbe_set_blackred(parent, gparent);
rbe = gparent;
continue;
}
if (RBE_RIGHT(parent) == rbe) {
rbe_rotate_left(t, rbt, parent);
tmp = parent;
parent = rbe;
rbe = tmp;
}
rbe_set_blackred(parent, gparent);
rbe_rotate_right(t, rbt, gparent);
} else {
tmp = RBE_LEFT(gparent);
if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) {
RBE_COLOR(tmp) = RB_BLACK;
rbe_set_blackred(parent, gparent);
rbe = gparent;
continue;
}
if (RBE_LEFT(parent) == rbe) {
rbe_rotate_right(t, rbt, parent);
tmp = parent;
parent = rbe;
rbe = tmp;
}
rbe_set_blackred(parent, gparent);
rbe_rotate_left(t, rbt, gparent);
}
}
RBE_COLOR(RBH_ROOT(rbt)) = RB_BLACK;
}
static inline void
rbe_remove_color(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *parent, struct rb_entry *rbe)
{
struct rb_entry *tmp;
while ((rbe == NULL || RBE_COLOR(rbe) == RB_BLACK) &&
rbe != RBH_ROOT(rbt)) {
if (RBE_LEFT(parent) == rbe) {
tmp = RBE_RIGHT(parent);
if (RBE_COLOR(tmp) == RB_RED) {
rbe_set_blackred(tmp, parent);
rbe_rotate_left(t, rbt, parent);
tmp = RBE_RIGHT(parent);
}
if ((RBE_LEFT(tmp) == NULL ||
RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) &&
(RBE_RIGHT(tmp) == NULL ||
RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) {
RBE_COLOR(tmp) = RB_RED;
rbe = parent;
parent = RBE_PARENT(rbe);
} else {
if (RBE_RIGHT(tmp) == NULL ||
RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK) {
struct rb_entry *oleft;
oleft = RBE_LEFT(tmp);
if (oleft != NULL)
RBE_COLOR(oleft) = RB_BLACK;
RBE_COLOR(tmp) = RB_RED;
rbe_rotate_right(t, rbt, tmp);
tmp = RBE_RIGHT(parent);
}
RBE_COLOR(tmp) = RBE_COLOR(parent);
RBE_COLOR(parent) = RB_BLACK;
if (RBE_RIGHT(tmp))
RBE_COLOR(RBE_RIGHT(tmp)) = RB_BLACK;
rbe_rotate_left(t, rbt, parent);
rbe = RBH_ROOT(rbt);
break;
}
} else {
tmp = RBE_LEFT(parent);
if (RBE_COLOR(tmp) == RB_RED) {
rbe_set_blackred(tmp, parent);
rbe_rotate_right(t, rbt, parent);
tmp = RBE_LEFT(parent);
}
if ((RBE_LEFT(tmp) == NULL ||
RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) &&
(RBE_RIGHT(tmp) == NULL ||
RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) {
RBE_COLOR(tmp) = RB_RED;
rbe = parent;
parent = RBE_PARENT(rbe);
} else {
if (RBE_LEFT(tmp) == NULL ||
RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) {
struct rb_entry *oright;
oright = RBE_RIGHT(tmp);
if (oright != NULL)
RBE_COLOR(oright) = RB_BLACK;
RBE_COLOR(tmp) = RB_RED;
rbe_rotate_left(t, rbt, tmp);
tmp = RBE_LEFT(parent);
}
RBE_COLOR(tmp) = RBE_COLOR(parent);
RBE_COLOR(parent) = RB_BLACK;
if (RBE_LEFT(tmp) != NULL)
RBE_COLOR(RBE_LEFT(tmp)) = RB_BLACK;
rbe_rotate_right(t, rbt, parent);
rbe = RBH_ROOT(rbt);
break;
}
}
}
if (rbe != NULL)
RBE_COLOR(rbe) = RB_BLACK;
}
static inline struct rb_entry *
rbe_remove(const struct rb_type *t, struct rb_tree *rbt, struct rb_entry *rbe)
{
struct rb_entry *child, *parent, *old = rbe;
unsigned int color;
if (RBE_LEFT(rbe) == NULL)
child = RBE_RIGHT(rbe);
else if (RBE_RIGHT(rbe) == NULL)
child = RBE_LEFT(rbe);
else {
struct rb_entry *tmp;
rbe = RBE_RIGHT(rbe);
while ((tmp = RBE_LEFT(rbe)) != NULL)
rbe = tmp;
child = RBE_RIGHT(rbe);
parent = RBE_PARENT(rbe);
color = RBE_COLOR(rbe);
if (child != NULL)
RBE_PARENT(child) = parent;
if (parent != NULL) {
if (RBE_LEFT(parent) == rbe)
RBE_LEFT(parent) = child;
else
RBE_RIGHT(parent) = child;
rbe_if_augment(t, parent);
} else
RBH_ROOT(rbt) = child;
if (RBE_PARENT(rbe) == old)
parent = rbe;
*rbe = *old;
tmp = RBE_PARENT(old);
if (tmp != NULL) {
if (RBE_LEFT(tmp) == old)
RBE_LEFT(tmp) = rbe;
else
RBE_RIGHT(tmp) = rbe;
rbe_if_augment(t, tmp);
} else
RBH_ROOT(rbt) = rbe;
RBE_PARENT(RBE_LEFT(old)) = rbe;
if (RBE_RIGHT(old))
RBE_PARENT(RBE_RIGHT(old)) = rbe;
if (t->t_augment != NULL && parent != NULL) {
tmp = parent;
do {
rbe_augment(t, tmp);
tmp = RBE_PARENT(tmp);
} while (tmp != NULL);
}
goto color;
}
parent = RBE_PARENT(rbe);
color = RBE_COLOR(rbe);
if (child != NULL)
RBE_PARENT(child) = parent;
if (parent != NULL) {
if (RBE_LEFT(parent) == rbe)
RBE_LEFT(parent) = child;
else
RBE_RIGHT(parent) = child;
rbe_if_augment(t, parent);
} else
RBH_ROOT(rbt) = child;
color:
if (color == RB_BLACK)
rbe_remove_color(t, rbt, parent, child);
return (old);
}
void *
_rb_remove(const struct rb_type *t, struct rb_tree *rbt, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
struct rb_entry *old;
old = rbe_remove(t, rbt, rbe);
return (old == NULL ? NULL : rb_e2n(t, old));
}
void *
_rb_insert(const struct rb_type *t, struct rb_tree *rbt, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
struct rb_entry *tmp;
struct rb_entry *parent = NULL;
void *node;
int comp = 0;
tmp = RBH_ROOT(rbt);
while (tmp != NULL) {
parent = tmp;
node = rb_e2n(t, tmp);
comp = (*t->t_compare)(elm, node);
if (comp < 0)
tmp = RBE_LEFT(tmp);
else if (comp > 0)
tmp = RBE_RIGHT(tmp);
else
return (node);
}
rbe_set(rbe, parent);
if (parent != NULL) {
if (comp < 0)
RBE_LEFT(parent) = rbe;
else
RBE_RIGHT(parent) = rbe;
rbe_if_augment(t, parent);
} else
RBH_ROOT(rbt) = rbe;
rbe_insert_color(t, rbt, rbe);
return (NULL);
}
/* Finds the node with the same key as elm */
void *
_rb_find(const struct rb_type *t, struct rb_tree *rbt, const void *key)
{
struct rb_entry *tmp = RBH_ROOT(rbt);
void *node;
int comp;
while (tmp != NULL) {
node = rb_e2n(t, tmp);
comp = (*t->t_compare)(key, node);
if (comp < 0)
tmp = RBE_LEFT(tmp);
else if (comp > 0)
tmp = RBE_RIGHT(tmp);
else
return (node);
}
return (NULL);
}
/* Finds the first node greater than or equal to the search key */
void *
_rb_nfind(const struct rb_type *t, struct rb_tree *rbt, const void *key)
{
struct rb_entry *tmp = RBH_ROOT(rbt);
void *node;
void *res = NULL;
int comp;
while (tmp != NULL) {
node = rb_e2n(t, tmp);
comp = (*t->t_compare)(key, node);
if (comp < 0) {
res = node;
tmp = RBE_LEFT(tmp);
} else if (comp > 0)
tmp = RBE_RIGHT(tmp);
else
return (node);
}
return (res);
}
void *
_rb_next(const struct rb_type *t, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
if (RBE_RIGHT(rbe) != NULL) {
rbe = RBE_RIGHT(rbe);
while (RBE_LEFT(rbe) != NULL)
rbe = RBE_LEFT(rbe);
} else {
if (RBE_PARENT(rbe) &&
(rbe == RBE_LEFT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
else {
while (RBE_PARENT(rbe) &&
(rbe == RBE_RIGHT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
rbe = RBE_PARENT(rbe);
}
}
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_prev(const struct rb_type *t, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
if (RBE_LEFT(rbe)) {
rbe = RBE_LEFT(rbe);
while (RBE_RIGHT(rbe))
rbe = RBE_RIGHT(rbe);
} else {
if (RBE_PARENT(rbe) &&
(rbe == RBE_RIGHT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
else {
while (RBE_PARENT(rbe) &&
(rbe == RBE_LEFT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
rbe = RBE_PARENT(rbe);
}
}
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_root(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
return (rbe == NULL ? rbe : rb_e2n(t, rbe));
}
void *
_rb_min(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL;
while (rbe != NULL) {
parent = rbe;
rbe = RBE_LEFT(rbe);
}
return (parent == NULL ? NULL : rb_e2n(t, parent));
}
void *
_rb_max(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL;
while (rbe != NULL) {
parent = rbe;
rbe = RBE_RIGHT(rbe);
}
return (parent == NULL ? NULL : rb_e2n(t, parent));
}
void *
_rb_left(const struct rb_type *t, void *node)
{
struct rb_entry *rbe = rb_n2e(t, node);
rbe = RBE_LEFT(rbe);
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_right(const struct rb_type *t, void *node)
{
struct rb_entry *rbe = rb_n2e(t, node);
rbe = RBE_RIGHT(rbe);
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_parent(const struct rb_type *t, void *node)
{
struct rb_entry *rbe = rb_n2e(t, node);
rbe = RBE_PARENT(rbe);
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void
_rb_set_left(const struct rb_type *t, void *node, void *left)
{
struct rb_entry *rbe = rb_n2e(t, node);
struct rb_entry *rbl = (left == NULL) ? NULL : rb_n2e(t, left);
RBE_LEFT(rbe) = rbl;
}
void
_rb_set_right(const struct rb_type *t, void *node, void *right)
{
struct rb_entry *rbe = rb_n2e(t, node);
struct rb_entry *rbr = (right == NULL) ? NULL : rb_n2e(t, right);
RBE_RIGHT(rbe) = rbr;
}
void
_rb_set_parent(const struct rb_type *t, void *node, void *parent)
{
struct rb_entry *rbe = rb_n2e(t, node);
struct rb_entry *rbp = (parent == NULL) ? NULL : rb_n2e(t, parent);
RBE_PARENT(rbe) = rbp;
}
void
_rb_poison(const struct rb_type *t, void *node, unsigned long poison)
{
struct rb_entry *rbe = rb_n2e(t, node);
RBE_PARENT(rbe) = RBE_LEFT(rbe) = RBE_RIGHT(rbe) =
(struct rb_entry *)poison;
}
int
_rb_check(const struct rb_type *t, void *node, unsigned long poison)
{
struct rb_entry *rbe = rb_n2e(t, node);
return ((unsigned long)RBE_PARENT(rbe) == poison &&
(unsigned long)RBE_LEFT(rbe) == poison &&
(unsigned long)RBE_RIGHT(rbe) == poison);
}
3
2
1
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
/* $OpenBSD: if_bpe.c,v 1.19 2021/11/08 04:54:44 dlg Exp $ */
/*
* Copyright (c) 2018 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "bpfilter.h"
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/timeout.h>
#include <sys/pool.h>
#include <sys/tree.h>
#include <sys/smr.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/rtable.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
/* for bridge stuff */
#include <net/if_bridge.h>
#include <net/if_etherbridge.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <net/if_bpe.h>
#define PBB_ITAG_ISID 0x00ffffff
#define PBB_ITAG_ISID_MIN 0x00000000
#define PBB_ITAG_ISID_MAX 0x00ffffff
#define PBB_ITAG_RES2 0x03000000 /* must be zero on input */
#define PBB_ITAG_RES1 0x04000000 /* ignore on input */
#define PBB_ITAG_UCA 0x08000000
#define PBB_ITAG_DEI 0x10000000
#define PBB_ITAG_PCP_SHIFT 29
#define PBB_ITAG_PCP_MASK (0x7U << PBB_ITAG_PCP_SHIFT)
#define BPE_BRIDGE_AGE_TMO 100 /* seconds */
struct bpe_key {
int k_if;
uint32_t k_isid;
RBT_ENTRY(bpe_tunnel) k_entry;
};
RBT_HEAD(bpe_tree, bpe_key);
static inline int bpe_cmp(const struct bpe_key *, const struct bpe_key *);
RBT_PROTOTYPE(bpe_tree, bpe_key, k_entry, bpe_cmp);
RBT_GENERATE(bpe_tree, bpe_key, k_entry, bpe_cmp);
struct bpe_softc {
struct bpe_key sc_key; /* must be first */
struct arpcom sc_ac;
int sc_txhprio;
int sc_rxhprio;
struct ether_addr sc_group;
struct task sc_ltask;
struct task sc_dtask;
struct etherbridge sc_eb;
};
void bpeattach(int);
static int bpe_clone_create(struct if_clone *, int);
static int bpe_clone_destroy(struct ifnet *);
static void bpe_start(struct ifnet *);
static int bpe_ioctl(struct ifnet *, u_long, caddr_t);
static int bpe_media_get(struct bpe_softc *, struct ifreq *);
static int bpe_up(struct bpe_softc *);
static int bpe_down(struct bpe_softc *);
static int bpe_multi(struct bpe_softc *, struct ifnet *, u_long);
static int bpe_set_vnetid(struct bpe_softc *, const struct ifreq *);
static void bpe_set_group(struct bpe_softc *, uint32_t);
static int bpe_set_parent(struct bpe_softc *, const struct if_parent *);
static int bpe_get_parent(struct bpe_softc *, struct if_parent *);
static int bpe_del_parent(struct bpe_softc *);
static int bpe_add_addr(struct bpe_softc *, const struct ifbareq *);
static int bpe_del_addr(struct bpe_softc *, const struct ifbareq *);
static void bpe_link_hook(void *);
static void bpe_link_state(struct bpe_softc *, u_char, uint64_t);
static void bpe_detach_hook(void *);
static struct if_clone bpe_cloner =
IF_CLONE_INITIALIZER("bpe", bpe_clone_create, bpe_clone_destroy);
static int bpe_eb_port_eq(void *, void *, void *);
static void *bpe_eb_port_take(void *, void *);
static void bpe_eb_port_rele(void *, void *);
static size_t bpe_eb_port_ifname(void *, char *, size_t, void *);
static void bpe_eb_port_sa(void *, struct sockaddr_storage *, void *);
static const struct etherbridge_ops bpe_etherbridge_ops = {
bpe_eb_port_eq,
bpe_eb_port_take,
bpe_eb_port_rele,
bpe_eb_port_ifname,
bpe_eb_port_sa,
};
static struct bpe_tree bpe_interfaces = RBT_INITIALIZER();
static struct rwlock bpe_lock = RWLOCK_INITIALIZER("bpeifs");
static struct pool bpe_endpoint_pool;
void
bpeattach(int count)
{
if_clone_attach(&bpe_cloner);
}
static int
bpe_clone_create(struct if_clone *ifc, int unit)
{
struct bpe_softc *sc;
struct ifnet *ifp;
int error;
if (bpe_endpoint_pool.pr_size == 0) {
pool_init(&bpe_endpoint_pool, sizeof(struct ether_addr), 0,
IPL_NONE, 0, "bpepl", NULL);
}
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
ifp = &sc->sc_ac.ac_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
ifc->ifc_name, unit);
error = etherbridge_init(&sc->sc_eb, ifp->if_xname,
&bpe_etherbridge_ops, sc);
if (error == -1) {
free(sc, M_DEVBUF, sizeof(*sc));
return (error);
}
sc->sc_key.k_if = 0;
sc->sc_key.k_isid = 0;
bpe_set_group(sc, 0);
sc->sc_txhprio = IF_HDRPRIO_PACKET;
sc->sc_rxhprio = IF_HDRPRIO_OUTER;
task_set(&sc->sc_ltask, bpe_link_hook, sc);
task_set(&sc->sc_dtask, bpe_detach_hook, sc);
ifp->if_softc = sc;
ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
ifp->if_ioctl = bpe_ioctl;
ifp->if_start = bpe_start;
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
ifp->if_xflags = IFXF_CLONED;
ether_fakeaddr(ifp);
if_counters_alloc(ifp);
if_attach(ifp);
ether_ifattach(ifp);
return (0);
}
static int
bpe_clone_destroy(struct ifnet *ifp)
{
struct bpe_softc *sc = ifp->if_softc;
NET_LOCK();
if (ISSET(ifp->if_flags, IFF_RUNNING))
bpe_down(sc);
NET_UNLOCK();
ether_ifdetach(ifp);
if_detach(ifp);
etherbridge_destroy(&sc->sc_eb);
free(sc, M_DEVBUF, sizeof(*sc));
return (0);
}
static void
bpe_start(struct ifnet *ifp)
{
struct bpe_softc *sc = ifp->if_softc;
struct ifnet *ifp0;
struct mbuf *m0, *m;
struct ether_header *ceh;
struct ether_header *beh;
uint32_t itag, *itagp;
int hlen = sizeof(*beh) + sizeof(*itagp);
#if NBPFILTER > 0
caddr_t if_bpf;
#endif
int txprio;
uint8_t prio;
ifp0 = if_get(sc->sc_key.k_if);
if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) {
ifq_purge(&ifp->if_snd);
goto done;
}
txprio = sc->sc_txhprio;
while ((m0 = ifq_dequeue(&ifp->if_snd)) != NULL) {
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf)
bpf_mtap_ether(if_bpf, m0, BPF_DIRECTION_OUT);
#endif
ceh = mtod(m0, struct ether_header *);
/* force prepend of a whole mbuf because of alignment */
m = m_get(M_DONTWAIT, m0->m_type);
if (m == NULL) {
m_freem(m0);
continue;
}
M_MOVE_PKTHDR(m, m0);
m->m_next = m0;
m_align(m, 0);
m->m_len = 0;
m = m_prepend(m, hlen, M_DONTWAIT);
if (m == NULL)
continue;
beh = mtod(m, struct ether_header *);
if (ETHER_IS_BROADCAST(ceh->ether_dhost)) {
memcpy(beh->ether_dhost, &sc->sc_group,
sizeof(beh->ether_dhost));
} else {
struct ether_addr *endpoint;
smr_read_enter();
endpoint = etherbridge_resolve_ea(&sc->sc_eb,
(struct ether_addr *)ceh->ether_dhost);
if (endpoint == NULL) {
/* "flood" to unknown hosts */
endpoint = &sc->sc_group;
}
memcpy(beh->ether_dhost, endpoint,
sizeof(beh->ether_dhost));
smr_read_leave();
}
memcpy(beh->ether_shost, ((struct arpcom *)ifp0)->ac_enaddr,
sizeof(beh->ether_shost));
beh->ether_type = htons(ETHERTYPE_PBB);
prio = (txprio == IF_HDRPRIO_PACKET) ?
m->m_pkthdr.pf.prio : txprio;
itag = sc->sc_key.k_isid;
itag |= prio << PBB_ITAG_PCP_SHIFT;
itagp = (uint32_t *)(beh + 1);
htobem32(itagp, itag);
if_enqueue(ifp0, m);
}
done:
if_put(ifp0);
}
static int
bpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct bpe_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct ifbrparam *bparam = (struct ifbrparam *)data;
int error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
if (ISSET(ifp->if_flags, IFF_UP)) {
if (!ISSET(ifp->if_flags, IFF_RUNNING))
error = bpe_up(sc);
else
error = 0;
} else {
if (ISSET(ifp->if_flags, IFF_RUNNING))
error = bpe_down(sc);
}
break;
case SIOCSVNETID:
error = bpe_set_vnetid(sc, ifr);
break;
case SIOCGVNETID:
ifr->ifr_vnetid = sc->sc_key.k_isid;
break;
case SIOCSIFPARENT:
error = bpe_set_parent(sc, (struct if_parent *)data);
break;
case SIOCGIFPARENT:
error = bpe_get_parent(sc, (struct if_parent *)data);
break;
case SIOCDIFPARENT:
error = bpe_del_parent(sc);
break;
case SIOCSTXHPRIO:
error = if_txhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_txhprio = ifr->ifr_hdrprio;
break;
case SIOCGTXHPRIO:
ifr->ifr_hdrprio = sc->sc_txhprio;
break;
case SIOCSRXHPRIO:
error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
if (error != 0)
break;
sc->sc_rxhprio = ifr->ifr_hdrprio;
break;
case SIOCGRXHPRIO:
ifr->ifr_hdrprio = sc->sc_rxhprio;
break;
case SIOCGIFMEDIA:
error = bpe_media_get(sc, ifr);
break;
case SIOCBRDGSCACHE:
error = suser(curproc);
if (error != 0)
break;
error = etherbridge_set_max(&sc->sc_eb, bparam);
break;
case SIOCBRDGGCACHE:
error = etherbridge_get_max(&sc->sc_eb, bparam);
break;
case SIOCBRDGSTO:
error = suser(curproc);
if (error != 0)
break;
error = etherbridge_set_tmo(&sc->sc_eb, bparam);
break;
case SIOCBRDGGTO:
error = etherbridge_get_tmo(&sc->sc_eb, bparam);
break;
case SIOCBRDGRTS:
error = etherbridge_rtfind(&sc->sc_eb,
(struct ifbaconf *)data);
break;
case SIOCBRDGFLUSH:
error = suser(curproc);
if (error != 0)
break;
etherbridge_flush(&sc->sc_eb,
((struct ifbreq *)data)->ifbr_ifsflags);
break;
case SIOCBRDGSADDR:
error = bpe_add_addr(sc, (struct ifbareq *)data);
break;
case SIOCBRDGDADDR:
error = bpe_del_addr(sc, (struct ifbareq *)data);
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
break;
}
return (error);
}
static int
bpe_media_get(struct bpe_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp0;
int error;
ifp0 = if_get(sc->sc_key.k_if);
if (ifp0 != NULL)
error = (*ifp0->if_ioctl)(ifp0, SIOCGIFMEDIA, (caddr_t)ifr);
else
error = ENOTTY;
if_put(ifp0);
return (error);
}
static int
bpe_up(struct bpe_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct ifnet *ifp0;
struct bpe_softc *osc;
int error;
u_int hardmtu;
u_int hlen = sizeof(struct ether_header) + sizeof(uint32_t);
KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
NET_ASSERT_LOCKED();
error = etherbridge_up(&sc->sc_eb);
if (error != 0)
return (error);
ifp0 = if_get(sc->sc_key.k_if);
if (ifp0 == NULL) {
error = ENXIO;
goto down;
}
/* check again if bpe will work on top of the parent */
if (ifp0->if_type != IFT_ETHER) {
error = EPROTONOSUPPORT;
goto put;
}
hardmtu = ifp0->if_hardmtu;
if (hardmtu < hlen) {
error = ENOBUFS;
goto put;
}
hardmtu -= hlen;
if (ifp->if_mtu > hardmtu) {
error = ENOBUFS;
goto put;
}
/* parent is fine, let's prepare the bpe to handle packets */
ifp->if_hardmtu = hardmtu;
SET(ifp->if_flags, ifp0->if_flags & IFF_SIMPLEX);
/* commit the interface */
error = rw_enter(&bpe_lock, RW_WRITE | RW_INTR);
if (error != 0)
goto scrub;
osc = (struct bpe_softc *)RBT_INSERT(bpe_tree, &bpe_interfaces,
(struct bpe_key *)sc);
rw_exit(&bpe_lock);
if (osc != NULL) {
error = EADDRINUSE;
goto scrub;
}
if (bpe_multi(sc, ifp0, SIOCADDMULTI) != 0) {
error = ENOTCONN;
goto remove;
}
/* Register callback for physical link state changes */
if_linkstatehook_add(ifp0, &sc->sc_ltask);
/* Register callback if parent wants to unregister */
if_detachhook_add(ifp0, &sc->sc_dtask);
/* we're running now */
SET(ifp->if_flags, IFF_RUNNING);
bpe_link_state(sc, ifp0->if_link_state, ifp0->if_baudrate);
if_put(ifp0);
return (0);
remove:
rw_enter(&bpe_lock, RW_WRITE);
RBT_REMOVE(bpe_tree, &bpe_interfaces, (struct bpe_key *)sc);
rw_exit(&bpe_lock);
scrub:
CLR(ifp->if_flags, IFF_SIMPLEX);
ifp->if_hardmtu = 0xffff;
put:
if_put(ifp0);
down:
etherbridge_down(&sc->sc_eb);
return (error);
}
static int
bpe_down(struct bpe_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct ifnet *ifp0;
NET_ASSERT_LOCKED();
CLR(ifp->if_flags, IFF_RUNNING);
ifp0 = if_get(sc->sc_key.k_if);
if (ifp0 != NULL) {
if_detachhook_del(ifp0, &sc->sc_dtask);
if_linkstatehook_del(ifp0, &sc->sc_ltask);
bpe_multi(sc, ifp0, SIOCDELMULTI);
}
if_put(ifp0);
rw_enter(&bpe_lock, RW_WRITE);
RBT_REMOVE(bpe_tree, &bpe_interfaces, (struct bpe_key *)sc);
rw_exit(&bpe_lock);
CLR(ifp->if_flags, IFF_SIMPLEX);
ifp->if_hardmtu = 0xffff;
etherbridge_down(&sc->sc_eb);
return (0);
}
static int
bpe_multi(struct bpe_softc *sc, struct ifnet *ifp0, u_long cmd)
{
struct ifreq ifr;
struct sockaddr *sa;
/* make it convincing */
CTASSERT(sizeof(ifr.ifr_name) == sizeof(ifp0->if_xname));
memcpy(ifr.ifr_name, ifp0->if_xname, sizeof(ifr.ifr_name));
sa = &ifr.ifr_addr;
CTASSERT(sizeof(sa->sa_data) >= sizeof(sc->sc_group));
sa->sa_family = AF_UNSPEC;
memcpy(sa->sa_data, &sc->sc_group, sizeof(sc->sc_group));
return ((*ifp0->if_ioctl)(ifp0, cmd, (caddr_t)&ifr));
}
static void
bpe_set_group(struct bpe_softc *sc, uint32_t isid)
{
uint8_t *group = sc->sc_group.ether_addr_octet;
group[0] = 0x01;
group[1] = 0x1e;
group[2] = 0x83;
group[3] = isid >> 16;
group[4] = isid >> 8;
group[5] = isid >> 0;
}
static int
bpe_set_vnetid(struct bpe_softc *sc, const struct ifreq *ifr)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
uint32_t isid;
if (ifr->ifr_vnetid < PBB_ITAG_ISID_MIN ||
ifr->ifr_vnetid > PBB_ITAG_ISID_MAX)
return (EINVAL);
isid = ifr->ifr_vnetid;
if (isid == sc->sc_key.k_isid)
return (0);
if (ISSET(ifp->if_flags, IFF_RUNNING))
return (EBUSY);
/* commit */
sc->sc_key.k_isid = isid;
bpe_set_group(sc, isid);
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
return (0);
}
static int
bpe_set_parent(struct bpe_softc *sc, const struct if_parent *p)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct ifnet *ifp0;
int error = 0;
ifp0 = if_unit(p->ifp_parent);
if (ifp0 == NULL)
return (ENXIO);
if (ifp0->if_type != IFT_ETHER) {
error = ENXIO;
goto put;
}
if (ifp0->if_index == sc->sc_key.k_if)
goto put;
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
error = EBUSY;
goto put;
}
/* commit */
sc->sc_key.k_if = ifp0->if_index;
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
put:
if_put(ifp0);
return (error);
}
static int
bpe_get_parent(struct bpe_softc *sc, struct if_parent *p)
{
struct ifnet *ifp0;
int error = 0;
ifp0 = if_get(sc->sc_key.k_if);
if (ifp0 == NULL)
error = EADDRNOTAVAIL;
else
memcpy(p->ifp_parent, ifp0->if_xname, sizeof(p->ifp_parent));
if_put(ifp0);
return (error);
}
static int
bpe_del_parent(struct bpe_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
if (ISSET(ifp->if_flags, IFF_RUNNING))
return (EBUSY);
/* commit */
sc->sc_key.k_if = 0;
etherbridge_flush(&sc->sc_eb, IFBF_FLUSHALL);
return (0);
}
static int
bpe_add_addr(struct bpe_softc *sc, const struct ifbareq *ifba)
{
const struct sockaddr_dl *sdl;
const struct ether_addr *endpoint;
unsigned int type;
/* ignore ifba_ifsname */
if (ISSET(ifba->ifba_flags, ~IFBAF_TYPEMASK))
return (EINVAL);
switch (ifba->ifba_flags & IFBAF_TYPEMASK) {
case IFBAF_DYNAMIC:
type = EBE_DYNAMIC;
break;
case IFBAF_STATIC:
type = EBE_STATIC;
break;
default:
return (EINVAL);
}
if (ifba->ifba_dstsa.ss_family != AF_LINK)
return (EAFNOSUPPORT);
sdl = (struct sockaddr_dl *)&ifba->ifba_dstsa;
if (sdl->sdl_type != IFT_ETHER)
return (EAFNOSUPPORT);
if (sdl->sdl_alen != ETHER_ADDR_LEN)
return (EINVAL);
endpoint = (struct ether_addr *)LLADDR(sdl);
/* check endpoint for multicast or broadcast? */
return (etherbridge_add_addr(&sc->sc_eb, (void *)endpoint,
&ifba->ifba_dst, type));
}
static int
bpe_del_addr(struct bpe_softc *sc, const struct ifbareq *ifba)
{
return (etherbridge_del_addr(&sc->sc_eb, &ifba->ifba_dst));
}
static inline struct bpe_softc *
bpe_find(struct ifnet *ifp0, uint32_t isid)
{
struct bpe_key k = { .k_if = ifp0->if_index, .k_isid = isid };
struct bpe_softc *sc;
rw_enter_read(&bpe_lock);
sc = (struct bpe_softc *)RBT_FIND(bpe_tree, &bpe_interfaces, &k);
rw_exit_read(&bpe_lock);
return (sc);
}
void
bpe_input(struct ifnet *ifp0, struct mbuf *m)
{
struct bpe_softc *sc;
struct ifnet *ifp;
struct ether_header *beh, *ceh;
uint32_t *itagp, itag;
unsigned int hlen = sizeof(*beh) + sizeof(*itagp) + sizeof(*ceh);
struct mbuf *n;
int off;
int prio;
if (m->m_len < hlen) {
m = m_pullup(m, hlen);
if (m == NULL) {
/* pbb short ++ */
return;
}
}
beh = mtod(m, struct ether_header *);
itagp = (uint32_t *)(beh + 1);
itag = bemtoh32(itagp);
if (itag & PBB_ITAG_RES2) {
/* dropped by res2 ++ */
goto drop;
}
sc = bpe_find(ifp0, itag & PBB_ITAG_ISID);
if (sc == NULL) {
/* no interface found */
goto drop;
}
ceh = (struct ether_header *)(itagp + 1);
etherbridge_map_ea(&sc->sc_eb, ceh->ether_shost,
(struct ether_addr *)beh->ether_shost);
m_adj(m, sizeof(*beh) + sizeof(*itagp));
n = m_getptr(m, sizeof(*ceh), &off);
if (n == NULL) {
/* no data ++ */
goto drop;
}
if (!ALIGNED_POINTER(mtod(n, caddr_t) + off, uint32_t)) {
/* unaligned ++ */
n = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
m_freem(m);
if (n == NULL)
return;
m = n;
}
ifp = &sc->sc_ac.ac_if;
prio = sc->sc_rxhprio;
switch (prio) {
case IF_HDRPRIO_PACKET:
break;
case IF_HDRPRIO_OUTER:
m->m_pkthdr.pf.prio = (itag & PBB_ITAG_PCP_MASK) >>
PBB_ITAG_PCP_SHIFT;
break;
default:
m->m_pkthdr.pf.prio = prio;
break;
}
m->m_flags &= ~(M_BCAST|M_MCAST);
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
#if NPF > 0
pf_pkt_addr_changed(m);
#endif
if_vinput(ifp, m);
return;
drop:
m_freem(m);
}
void
bpe_detach_hook(void *arg)
{
struct bpe_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ac.ac_if;
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
bpe_down(sc);
CLR(ifp->if_flags, IFF_UP);
}
sc->sc_key.k_if = 0;
}
static void
bpe_link_hook(void *arg)
{
struct bpe_softc *sc = arg;
struct ifnet *ifp0;
u_char link = LINK_STATE_DOWN;
uint64_t baud = 0;
ifp0 = if_get(sc->sc_key.k_if);
if (ifp0 != NULL) {
link = ifp0->if_link_state;
baud = ifp0->if_baudrate;
}
if_put(ifp0);
bpe_link_state(sc, link, baud);
}
void
bpe_link_state(struct bpe_softc *sc, u_char link, uint64_t baud)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
if (ifp->if_link_state == link)
return;
ifp->if_link_state = link;
ifp->if_baudrate = baud;
if_link_state_change(ifp);
}
static inline int
bpe_cmp(const struct bpe_key *a, const struct bpe_key *b)
{
if (a->k_if > b->k_if)
return (1);
if (a->k_if < b->k_if)
return (-1);
if (a->k_isid > b->k_isid)
return (1);
if (a->k_isid < b->k_isid)
return (-1);
return (0);
}
static int
bpe_eb_port_eq(void *arg, void *a, void *b)
{
struct ether_addr *ea = a, *eb = b;
return (memcmp(ea, eb, sizeof(*ea)) == 0);
}
static void *
bpe_eb_port_take(void *arg, void *port)
{
struct ether_addr *ea = port;
struct ether_addr *endpoint;
endpoint = pool_get(&bpe_endpoint_pool, PR_NOWAIT);
if (endpoint == NULL)
return (NULL);
memcpy(endpoint, ea, sizeof(*endpoint));
return (endpoint);
}
static void
bpe_eb_port_rele(void *arg, void *port)
{
struct ether_addr *endpoint = port;
pool_put(&bpe_endpoint_pool, endpoint);
}
static size_t
bpe_eb_port_ifname(void *arg, char *dst, size_t len, void *port)
{
struct bpe_softc *sc = arg;
return (strlcpy(dst, sc->sc_ac.ac_if.if_xname, len));
}
static void
bpe_eb_port_sa(void *arg, struct sockaddr_storage *ss, void *port)
{
struct ether_addr *endpoint = port;
struct sockaddr_dl *sdl;
sdl = (struct sockaddr_dl *)ss;
sdl->sdl_len = sizeof(sdl);
sdl->sdl_family = AF_LINK;
sdl->sdl_index = 0;
sdl->sdl_type = IFT_ETHER;
sdl->sdl_nlen = 0;
sdl->sdl_alen = sizeof(*endpoint);
CTASSERT(sizeof(sdl->sdl_data) >= sizeof(*endpoint));
memcpy(sdl->sdl_data, endpoint, sizeof(*endpoint));
}
592
593
859
858
331
611
301
859
857
593
591
9007
1317
9007
9008
6027
3405
7591
1634
4029
5569
70
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
/* $OpenBSD: trap.c,v 1.90 2021/12/09 00:26:11 guenther Exp $ */
/* $NetBSD: trap.c,v 1.2 2003/05/04 23:51:56 fvdl Exp $ */
/*-
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the University of Utah, and William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)trap.c 7.4 (Berkeley) 5/13/91
*/
/*
* amd64 Trap and System call handling
*/
#undef TRAP_SIGDEBUG
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <sys/syscall_mi.h>
#include <sys/stdarg.h>
#include <uvm/uvm_extern.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/fpu.h>
#include <machine/psl.h>
#include <machine/trap.h>
#ifdef DDB
#include <ddb/db_output.h>
#include <machine/db_machdep.h>
#endif
#include "isa.h"
int upageflttrap(struct trapframe *, uint64_t);
int kpageflttrap(struct trapframe *, uint64_t);
void kerntrap(struct trapframe *);
void usertrap(struct trapframe *);
void ast(struct trapframe *);
void syscall(struct trapframe *);
const char * const trap_type[] = {
"privileged instruction fault", /* 0 T_PRIVINFLT */
"breakpoint trap", /* 1 T_BPTFLT */
"arithmetic trap", /* 2 T_ARITHTRAP */
"reserved trap", /* 3 T_RESERVED */
"protection fault", /* 4 T_PROTFLT */
"trace trap", /* 5 T_TRCTRAP */
"page fault", /* 6 T_PAGEFLT */
"alignment fault", /* 7 T_ALIGNFLT */
"integer divide fault", /* 8 T_DIVIDE */
"non-maskable interrupt", /* 9 T_NMI */
"overflow trap", /* 10 T_OFLOW */
"bounds check fault", /* 11 T_BOUND */
"FPU not available fault", /* 12 T_DNA */
"double fault", /* 13 T_DOUBLEFLT */
"FPU operand fetch fault", /* 14 T_FPOPFLT */
"invalid TSS fault", /* 15 T_TSSFLT */
"segment not present fault", /* 16 T_SEGNPFLT */
"stack fault", /* 17 T_STKFLT */
"machine check", /* 18 T_MCA */
"SSE FP exception", /* 19 T_XMM */
};
const int trap_types = nitems(trap_type);
#ifdef DEBUG
int trapdebug = 0;
#endif
static void trap_print(struct trapframe *, int _type);
static inline void frame_dump(struct trapframe *_tf, struct proc *_p,
const char *_sig, uint64_t _cr2);
static inline void verify_smap(const char *_func);
static inline void debug_trap(struct trapframe *_frame, struct proc *_p,
long _type);
static inline void
fault(const char *fmt, ...)
{
struct cpu_info *ci = curcpu();
va_list ap;
atomic_cas_ptr(&panicstr, NULL, ci->ci_panicbuf);
va_start(ap, fmt);
vsnprintf(ci->ci_panicbuf, sizeof(ci->ci_panicbuf), fmt, ap);
va_end(ap);
#ifdef DDB
db_printf("%s\n", ci->ci_panicbuf);
#else
printf("%s\n", ci->ci_panicbuf);
#endif
}
static inline int
pgex2access(int pgex)
{
if (pgex & PGEX_W)
return PROT_WRITE;
else if (pgex & PGEX_I)
return PROT_EXEC;
return PROT_READ;
}
/*
* upageflttrap(frame, usermode): page fault handler
* Returns non-zero if the fault was handled (possibly by generating
* a signal). Returns zero, possibly still holding the kernel lock,
* if something was so broken that we should panic.
*/
int
upageflttrap(struct trapframe *frame, uint64_t cr2)
{
struct proc *p = curproc;
vaddr_t va = trunc_page((vaddr_t)cr2);
vm_prot_t access_type = pgex2access(frame->tf_err);
union sigval sv;
int signal, sicode, error;
error = uvm_fault(&p->p_vmspace->vm_map, va, 0, access_type);
if (error == 0) {
uvm_grow(p, va);
return 1;
}
signal = SIGSEGV;
sicode = SEGV_MAPERR;
if (error == ENOMEM) {
printf("UVM: pid %d (%s), uid %d killed:"
" out of swap\n", p->p_p->ps_pid, p->p_p->ps_comm,
p->p_ucred ? (int)p->p_ucred->cr_uid : -1);
signal = SIGKILL;
} else {
if (error == EACCES)
sicode = SEGV_ACCERR;
else if (error == EIO) {
signal = SIGBUS;
sicode = BUS_OBJERR;
}
}
sv.sival_ptr = (void *)cr2;
trapsignal(p, signal, T_PAGEFLT, sicode, sv);
return 1;
}
/*
* kpageflttrap(frame, usermode): page fault handler
* Returns non-zero if the fault was handled (possibly by generating
* a signal). Returns zero, possibly still holding the kernel lock,
* if something was so broken that we should panic.
*/
int
kpageflttrap(struct trapframe *frame, uint64_t cr2)
{
struct proc *p = curproc;
struct pcb *pcb;
vaddr_t va = trunc_page((vaddr_t)cr2);
struct vm_map *map;
vm_prot_t access_type = pgex2access(frame->tf_err);
caddr_t onfault;
int error;
if (p == NULL || p->p_addr == NULL || p->p_vmspace == NULL)
return 0;
pcb = &p->p_addr->u_pcb;
/* This will only trigger if SMEP is enabled */
if (cr2 <= VM_MAXUSER_ADDRESS && frame->tf_err & PGEX_I) {
KERNEL_LOCK();
fault("attempt to execute user address %p "
"in supervisor mode", (void *)cr2);
/* retain kernel lock */
return 0;
}
/* This will only trigger if SMAP is enabled */
if (pcb->pcb_onfault == NULL && cr2 <= VM_MAXUSER_ADDRESS &&
frame->tf_err & PGEX_P) {
KERNEL_LOCK();
fault("attempt to access user address %p "
"in supervisor mode", (void *)cr2);
/* retain kernel lock */
return 0;
}
/*
* It is only a kernel address space fault iff:
* 1. when running in ring 0 and
* 2. pcb_onfault not set or
* 3. pcb_onfault set but supervisor space fault
* The last can occur during an exec() copyin where the
* argument space is lazy-allocated.
*/
map = &p->p_vmspace->vm_map;
if (va >= VM_MIN_KERNEL_ADDRESS)
map = kernel_map;
if (curcpu()->ci_inatomic == 0 || map == kernel_map) {
onfault = pcb->pcb_onfault;
pcb->pcb_onfault = NULL;
error = uvm_fault(map, va, 0, access_type);
pcb->pcb_onfault = onfault;
if (error == 0 && map != kernel_map)
uvm_grow(p, va);
} else
error = EFAULT;
if (error) {
if (pcb->pcb_onfault == NULL) {
/* bad memory access in the kernel */
KERNEL_LOCK();
fault("uvm_fault(%p, 0x%llx, 0, %d) -> %x",
map, cr2, access_type, error);
/* retain kernel lock */
return 0;
}
frame->tf_rip = (u_int64_t)pcb->pcb_onfault;
}
return 1;
}
/*
* kerntrap(frame):
* Exception, fault, and trap interface to BSD kernel. This
* common code is called from assembly language IDT gate entry
* routines that prepare a suitable stack frame, and restore this
* frame after the exception has been processed.
*/
void
kerntrap(struct trapframe *frame)
{
int type = (int)frame->tf_trapno;
uint64_t cr2 = rcr2();
verify_smap(__func__);
uvmexp.traps++;
debug_trap(frame, curproc, type);
switch (type) {
default:
we_re_toast:
#ifdef DDB
if (db_ktrap(type, 0, frame))
return;
#endif
trap_print(frame, type);
panic("trap type %d, code=%llx, pc=%llx",
type, frame->tf_err, frame->tf_rip);
/*NOTREACHED*/
case T_PAGEFLT: /* allow page faults in kernel mode */
if (kpageflttrap(frame, cr2))
return;
goto we_re_toast;
#if NISA > 0
case T_NMI:
#ifdef DDB
/* NMI can be hooked up to a pushbutton for debugging */
printf ("NMI ... going to debugger\n");
if (db_ktrap(type, 0, frame))
return;
#endif
/* machine/parity/power fail/"kitchen sink" faults */
if (x86_nmi() != 0)
goto we_re_toast;
else
return;
#endif /* NISA > 0 */
}
}
/*
* usertrap(frame): handler for exceptions, faults, and traps from userspace
* This is called from the assembly language IDT gate entries
* which prepare a suitable stack frame and restores the CPU state
* after the fault has been processed.
*/
void
usertrap(struct trapframe *frame)
{
struct proc *p = curproc;
int type = (int)frame->tf_trapno;
uint64_t cr2 = rcr2();
union sigval sv;
int sig, code;
verify_smap(__func__);
uvmexp.traps++;
debug_trap(frame, p, type);
p->p_md.md_regs = frame;
refreshcreds(p);
switch (type) {
case T_TSSFLT:
sig = SIGBUS;
code = BUS_OBJERR;
break;
case T_PROTFLT: /* protection fault */
case T_SEGNPFLT:
case T_STKFLT:
frame_dump(frame, p, "SEGV", 0);
sig = SIGSEGV;
code = SEGV_MAPERR;
break;
case T_ALIGNFLT:
sig = SIGBUS;
code = BUS_ADRALN;
break;
case T_PRIVINFLT: /* privileged instruction fault */
sig = SIGILL;
code = ILL_PRVOPC;
break;
case T_DIVIDE:
sig = SIGFPE;
code = FPE_INTDIV;
break;
case T_ARITHTRAP:
case T_XMM: /* real arithmetic exceptions */
sig = SIGFPE;
code = fputrap(type);
break;
case T_BPTFLT: /* bpt instruction fault */
case T_TRCTRAP: /* trace trap */
sig = SIGTRAP;
code = TRAP_BRKPT;
break;
case T_PAGEFLT: /* page fault */
if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p),
"[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n",
uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial))
goto out;
if (upageflttrap(frame, cr2))
goto out;
/* FALLTHROUGH */
default:
trap_print(frame, type);
panic("impossible trap");
}
sv.sival_ptr = (void *)frame->tf_rip;
trapsignal(p, sig, type, code, sv);
out:
userret(p);
}
static void
trap_print(struct trapframe *frame, int type)
{
if (type < trap_types)
printf("fatal %s", trap_type[type]);
else
printf("unknown trap %d", type);
printf(" in %s mode\n", KERNELMODE(frame->tf_cs, frame->tf_rflags) ?
"supervisor" : "user");
printf("trap type %d code %llx rip %llx cs %llx rflags %llx cr2 "
"%llx cpl %x rsp %llx\n",
type, frame->tf_err, frame->tf_rip, frame->tf_cs,
frame->tf_rflags, rcr2(), curcpu()->ci_ilevel, frame->tf_rsp);
printf("gsbase %p kgsbase %p\n",
(void *)rdmsr(MSR_GSBASE), (void *)rdmsr(MSR_KERNELGSBASE));
}
static inline void
frame_dump(struct trapframe *tf, struct proc *p, const char *sig, uint64_t cr2)
{
#ifdef TRAP_SIGDEBUG
printf("pid %d (%s): %s at rip %llx addr %llx\n",
p->p_p->ps_pid, p->p_p->ps_comm, sig, tf->tf_rip, cr2);
printf("rip %p cs 0x%x rfl %p rsp %p ss 0x%x\n",
(void *)tf->tf_rip, (unsigned)tf->tf_cs & 0xffff,
(void *)tf->tf_rflags,
(void *)tf->tf_rsp, (unsigned)tf->tf_ss & 0xffff);
printf("err 0x%llx trapno 0x%llx\n",
tf->tf_err, tf->tf_trapno);
printf("rdi %p rsi %p rdx %p\n",
(void *)tf->tf_rdi, (void *)tf->tf_rsi, (void *)tf->tf_rdx);
printf("rcx %p r8 %p r9 %p\n",
(void *)tf->tf_rcx, (void *)tf->tf_r8, (void *)tf->tf_r9);
printf("r10 %p r11 %p r12 %p\n",
(void *)tf->tf_r10, (void *)tf->tf_r11, (void *)tf->tf_r12);
printf("r13 %p r14 %p r15 %p\n",
(void *)tf->tf_r13, (void *)tf->tf_r14, (void *)tf->tf_r15);
printf("rbp %p rbx %p rax %p\n",
(void *)tf->tf_rbp, (void *)tf->tf_rbx, (void *)tf->tf_rax);
#endif
}
static inline void
verify_smap(const char *func)
{
#ifdef DIAGNOSTIC
if (curcpu()->ci_feature_sefflags_ebx & SEFF0EBX_SMAP) {
u_long rf = read_rflags();
if (rf & PSL_AC) {
write_rflags(rf & ~PSL_AC);
panic("%s: AC set on entry", func);
}
}
#endif
}
static inline void
debug_trap(struct trapframe *frame, struct proc *p, long type)
{
#ifdef DEBUG
if (trapdebug) {
printf("trap %ld code %llx rip %llx cs %llx rflags %llx "
"cr2 %llx cpl %x\n",
type, frame->tf_err, frame->tf_rip, frame->tf_cs,
frame->tf_rflags, rcr2(), curcpu()->ci_ilevel);
printf("curproc %p\n", (void *)p);
if (p != NULL)
printf("pid %d\n", p->p_p->ps_pid);
}
#endif
}
/*
* ast(frame):
* AST handler. This is called from assembly language stubs when
* returning to userspace after a syscall or interrupt.
*/
void
ast(struct trapframe *frame)
{
struct proc *p = curproc;
uvmexp.traps++;
KASSERT(!KERNELMODE(frame->tf_cs, frame->tf_rflags));
p->p_md.md_regs = frame;
refreshcreds(p);
uvmexp.softs++;
mi_ast(p, curcpu()->ci_want_resched);
userret(p);
}
/*
* syscall(frame):
* System call request from POSIX system call gate interface to kernel.
*/
void
syscall(struct trapframe *frame)
{
caddr_t params;
const struct sysent *callp;
struct proc *p;
int error;
size_t argsize, argoff;
register_t code, args[9], rval[2], *argp;
verify_smap(__func__);
uvmexp.syscalls++;
p = curproc;
code = frame->tf_rax;
argp = &args[0];
argoff = 0;
switch (code) {
case SYS_syscall:
case SYS___syscall:
/*
* Code is first argument, followed by actual args.
*/
code = frame->tf_rdi;
argp = &args[1];
argoff = 1;
break;
default:
break;
}
callp = sysent;
if (code < 0 || code >= SYS_MAXSYSCALL)
callp += SYS_syscall;
else
callp += code;
argsize = (callp->sy_argsize >> 3) + argoff;
if (argsize) {
switch (MIN(argsize, 6)) {
case 6:
args[5] = frame->tf_r9;
case 5:
args[4] = frame->tf_r8;
case 4:
args[3] = frame->tf_r10;
case 3:
args[2] = frame->tf_rdx;
case 2:
args[1] = frame->tf_rsi;
case 1:
args[0] = frame->tf_rdi;
break;
default:
panic("impossible syscall argsize");
}
if (argsize > 6) {
argsize -= 6;
params = (caddr_t)frame->tf_rsp + sizeof(register_t);
if ((error = copyin(params, &args[6], argsize << 3)))
goto bad;
}
}
rval[0] = 0;
rval[1] = frame->tf_rdx;
error = mi_syscall(p, code, callp, argp, rval);
switch (error) {
case 0:
frame->tf_rax = rval[0];
frame->tf_rdx = rval[1];
frame->tf_rflags &= ~PSL_C; /* carry bit */
break;
case ERESTART:
/* Back up over the syscall instruction (2 bytes) */
frame->tf_rip -= 2;
break;
case EJUSTRETURN:
/* nothing to do */
break;
default:
bad:
frame->tf_rax = error;
frame->tf_rflags |= PSL_C; /* carry bit */
break;
}
mi_syscall_return(p, code, error, rval);
}
void
child_return(void *arg)
{
struct proc *p = arg;
struct trapframe *tf = p->p_md.md_regs;
tf->tf_rax = 0;
tf->tf_rdx = 1;
tf->tf_rflags &= ~PSL_C;
KERNEL_UNLOCK();
mi_child_return(p);
}
1
1
1
1
1
1
3
3
3
2
2
2
1
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
/* $OpenBSD: kern_exit.c,v 1.204 2022/08/14 01:58:27 jsg Exp $ */
/* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)kern_exit.c 8.7 (Berkeley) 2/12/94
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/resourcevar.h>
#include <sys/ptrace.h>
#include <sys/acct.h>
#include <sys/filedesc.h>
#include <sys/signalvar.h>
#include <sys/sched.h>
#include <sys/ktrace.h>
#include <sys/pool.h>
#include <sys/mutex.h>
#ifdef SYSVSEM
#include <sys/sem.h>
#endif
#include <sys/witness.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <uvm/uvm_extern.h>
#include "kcov.h"
#if NKCOV > 0
#include <sys/kcov.h>
#endif
void proc_finish_wait(struct proc *, struct proc *);
void process_clear_orphan(struct process *);
void process_zap(struct process *);
void proc_free(struct proc *);
void unveil_destroy(struct process *ps);
/*
* exit --
* Death of process.
*/
int
sys_exit(struct proc *p, void *v, register_t *retval)
{
struct sys_exit_args /* {
syscallarg(int) rval;
} */ *uap = v;
exit1(p, SCARG(uap, rval), 0, EXIT_NORMAL);
/* NOTREACHED */
return (0);
}
int
sys___threxit(struct proc *p, void *v, register_t *retval)
{
struct sys___threxit_args /* {
syscallarg(pid_t *) notdead;
} */ *uap = v;
if (SCARG(uap, notdead) != NULL) {
pid_t zero = 0;
if (copyout(&zero, SCARG(uap, notdead), sizeof(zero)))
psignal(p, SIGSEGV);
}
exit1(p, 0, 0, EXIT_THREAD);
return (0);
}
/*
* Exit: deallocate address space and other resources, change proc state
* to zombie, and unlink proc from allproc and parent's lists. Save exit
* status and rusage for wait(). Check for child processes and orphan them.
*/
void
exit1(struct proc *p, int xexit, int xsig, int flags)
{
struct process *pr, *qr, *nqr;
struct rusage *rup;
int s;
atomic_setbits_int(&p->p_flag, P_WEXIT);
pr = p->p_p;
/* single-threaded? */
if (!P_HASSIBLING(p)) {
flags = EXIT_NORMAL;
} else {
/* nope, multi-threaded */
if (flags == EXIT_NORMAL)
single_thread_set(p, SINGLE_EXIT, 1);
else if (flags == EXIT_THREAD)
single_thread_check(p, 0);
}
if (flags == EXIT_NORMAL && !(pr->ps_flags & PS_EXITING)) {
if (pr->ps_pid == 1)
panic("init died (signal %d, exit %d)", xsig, xexit);
atomic_setbits_int(&pr->ps_flags, PS_EXITING);
pr->ps_xexit = xexit;
pr->ps_xsig = xsig;
/*
* If parent is waiting for us to exit or exec, PS_PPWAIT
* is set; we wake up the parent early to avoid deadlock.
*/
if (pr->ps_flags & PS_PPWAIT) {
atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
atomic_clearbits_int(&pr->ps_pptr->ps_flags,
PS_ISPWAIT);
wakeup(pr->ps_pptr);
}
}
/* unlink ourselves from the active threads */
SCHED_LOCK(s);
TAILQ_REMOVE(&pr->ps_threads, p, p_thr_link);
SCHED_UNLOCK(s);
if ((p->p_flag & P_THREAD) == 0) {
/* main thread gotta wait because it has the pid, et al */
while (pr->ps_refcnt > 1)
tsleep_nsec(&pr->ps_threads, PWAIT, "thrdeath", INFSLP);
if (pr->ps_flags & PS_PROFIL)
stopprofclock(pr);
}
rup = pr->ps_ru;
if (rup == NULL) {
rup = pool_get(&rusage_pool, PR_WAITOK | PR_ZERO);
if (pr->ps_ru == NULL) {
pr->ps_ru = rup;
} else {
pool_put(&rusage_pool, rup);
rup = pr->ps_ru;
}
}
p->p_siglist = 0;
if ((p->p_flag & P_THREAD) == 0)
pr->ps_siglist = 0;
kqpoll_exit();
#if NKCOV > 0
kcov_exit(p);
#endif
if ((p->p_flag & P_THREAD) == 0) {
sigio_freelist(&pr->ps_sigiolst);
/* close open files and release open-file table */
fdfree(p);
cancel_all_itimers();
timeout_del(&pr->ps_rucheck_to);
#ifdef SYSVSEM
semexit(pr);
#endif
killjobc(pr);
#ifdef ACCOUNTING
acct_process(p);
#endif
#ifdef KTRACE
/* release trace file */
if (pr->ps_tracevp)
ktrcleartrace(pr);
#endif
unveil_destroy(pr);
/*
* If parent has the SAS_NOCLDWAIT flag set, we're not
* going to become a zombie.
*/
if (pr->ps_pptr->ps_sigacts->ps_sigflags & SAS_NOCLDWAIT)
atomic_setbits_int(&pr->ps_flags, PS_NOZOMBIE);
}
p->p_fd = NULL; /* zap the thread's copy */
/*
* Remove proc from pidhash chain and allproc so looking
* it up won't work. We will put the proc on the
* deadproc list later (using the p_hash member), and
* wake up the reaper when we do. If this is the last
* thread of a process that isn't PS_NOZOMBIE, we'll put
* the process on the zombprocess list below.
*/
/*
* NOTE: WE ARE NO LONGER ALLOWED TO SLEEP!
*/
p->p_stat = SDEAD;
LIST_REMOVE(p, p_hash);
LIST_REMOVE(p, p_list);
if ((p->p_flag & P_THREAD) == 0) {
LIST_REMOVE(pr, ps_hash);
LIST_REMOVE(pr, ps_list);
if ((pr->ps_flags & PS_NOZOMBIE) == 0)
LIST_INSERT_HEAD(&zombprocess, pr, ps_list);
else {
/*
* Not going to be a zombie, so it's now off all
* the lists scanned by ispidtaken(), so block
* fast reuse of the pid now.
*/
freepid(pr->ps_pid);
}
/*
* Reparent children to their original parent, in case
* they were being traced, or to init(8).
*/
qr = LIST_FIRST(&pr->ps_children);
if (qr) /* only need this if any child is S_ZOMB */
wakeup(initprocess);
for (; qr != NULL; qr = nqr) {
nqr = LIST_NEXT(qr, ps_sibling);
/*
* Traced processes are killed since their
* existence means someone is screwing up.
*/
if (qr->ps_flags & PS_TRACED &&
!(qr->ps_flags & PS_EXITING)) {
process_untrace(qr);
/*
* If single threading is active,
* direct the signal to the active
* thread to avoid deadlock.
*/
if (qr->ps_single)
ptsignal(qr->ps_single, SIGKILL,
STHREAD);
else
prsignal(qr, SIGKILL);
} else {
process_reparent(qr, initprocess);
}
}
/*
* Make sure orphans won't remember the exiting process.
*/
while ((qr = LIST_FIRST(&pr->ps_orphans)) != NULL) {
KASSERT(qr->ps_oppid == pr->ps_pid);
qr->ps_oppid = 0;
process_clear_orphan(qr);
}
}
/* add thread's accumulated rusage into the process's total */
ruadd(rup, &p->p_ru);
tuagg(pr, p);
/*
* clear %cpu usage during swap
*/
p->p_pctcpu = 0;
if ((p->p_flag & P_THREAD) == 0) {
/*
* Final thread has died, so add on our children's rusage
* and calculate the total times
*/
calcru(&pr->ps_tu, &rup->ru_utime, &rup->ru_stime, NULL);
ruadd(rup, &pr->ps_cru);
/*
* Notify parent that we're gone. If we're not going to
* become a zombie, reparent to process 1 (init) so that
* we can wake our original parent to possibly unblock
* wait4() to return ECHILD.
*/
if (pr->ps_flags & PS_NOZOMBIE) {
struct process *ppr = pr->ps_pptr;
process_reparent(pr, initprocess);
wakeup(ppr);
}
}
/* just a thread? detach it from its process */
if (p->p_flag & P_THREAD) {
/* scheduler_wait_hook(pr->ps_mainproc, p); XXX */
if (--pr->ps_refcnt == 1)
wakeup(&pr->ps_threads);
KASSERT(pr->ps_refcnt > 0);
}
/* Release the thread's read reference of resource limit structure. */
if (p->p_limit != NULL) {
struct plimit *limit;
limit = p->p_limit;
p->p_limit = NULL;
lim_free(limit);
}
/*
* Other substructures are freed from reaper and wait().
*/
/*
* Finally, call machine-dependent code to switch to a new
* context (possibly the idle context). Once we are no longer
* using the dead process's vmspace and stack, exit2() will be
* called to schedule those resources to be released by the
* reaper thread.
*
* Note that cpu_exit() will end with a call equivalent to
* cpu_switch(), finishing our execution (pun intended).
*/
uvmexp.swtch++;
cpu_exit(p);
panic("cpu_exit returned");
}
/*
* Locking of this proclist is special; it's accessed in a
* critical section of process exit, and thus locking it can't
* modify interrupt state. We use a simple spin lock for this
* proclist. We use the p_hash member to linkup to deadproc.
*/
struct mutex deadproc_mutex =
MUTEX_INITIALIZER_FLAGS(IPL_NONE, "deadproc", MTX_NOWITNESS);
struct proclist deadproc = LIST_HEAD_INITIALIZER(deadproc);
/*
* We are called from cpu_exit() once it is safe to schedule the
* dead process's resources to be freed.
*
* NOTE: One must be careful with locking in this routine. It's
* called from a critical section in machine-dependent code, so
* we should refrain from changing any interrupt state.
*
* We lock the deadproc list, place the proc on that list (using
* the p_hash member), and wake up the reaper.
*/
void
exit2(struct proc *p)
{
mtx_enter(&deadproc_mutex);
LIST_INSERT_HEAD(&deadproc, p, p_hash);
mtx_leave(&deadproc_mutex);
wakeup(&deadproc);
}
void
proc_free(struct proc *p)
{
crfree(p->p_ucred);
pool_put(&proc_pool, p);
nthreads--;
}
/*
* Process reaper. This is run by a kernel thread to free the resources
* of a dead process. Once the resources are free, the process becomes
* a zombie, and the parent is allowed to read the undead's status.
*/
void
reaper(void *arg)
{
struct proc *p;
KERNEL_UNLOCK();
SCHED_ASSERT_UNLOCKED();
for (;;) {
mtx_enter(&deadproc_mutex);
while ((p = LIST_FIRST(&deadproc)) == NULL)
msleep_nsec(&deadproc, &deadproc_mutex, PVM, "reaper",
INFSLP);
/* Remove us from the deadproc list. */
LIST_REMOVE(p, p_hash);
mtx_leave(&deadproc_mutex);
WITNESS_THREAD_EXIT(p);
KERNEL_LOCK();
/*
* Free the VM resources we're still holding on to.
* We must do this from a valid thread because doing
* so may block.
*/
uvm_uarea_free(p);
p->p_vmspace = NULL; /* zap the thread's copy */
if (p->p_flag & P_THREAD) {
/* Just a thread */
proc_free(p);
} else {
struct process *pr = p->p_p;
/* Release the rest of the process's vmspace */
uvm_exit(pr);
if ((pr->ps_flags & PS_NOZOMBIE) == 0) {
/* Process is now a true zombie. */
atomic_setbits_int(&pr->ps_flags, PS_ZOMBIE);
}
/* Notify listeners of our demise and clean up. */
knote_processexit(pr);
if (pr->ps_flags & PS_ZOMBIE) {
/* Post SIGCHLD and wake up parent. */
prsignal(pr->ps_pptr, SIGCHLD);
wakeup(pr->ps_pptr);
} else {
/* No one will wait for us, just zap it. */
process_zap(pr);
}
}
KERNEL_UNLOCK();
}
}
int
sys_wait4(struct proc *q, void *v, register_t *retval)
{
struct sys_wait4_args /* {
syscallarg(pid_t) pid;
syscallarg(int *) status;
syscallarg(int) options;
syscallarg(struct rusage *) rusage;
} */ *uap = v;
struct rusage ru;
int status, error;
error = dowait4(q, SCARG(uap, pid),
SCARG(uap, status) ? &status : NULL,
SCARG(uap, options), SCARG(uap, rusage) ? &ru : NULL, retval);
if (error == 0 && retval[0] > 0 && SCARG(uap, status)) {
error = copyout(&status, SCARG(uap, status), sizeof(status));
}
if (error == 0 && retval[0] > 0 && SCARG(uap, rusage)) {
error = copyout(&ru, SCARG(uap, rusage), sizeof(ru));
#ifdef KTRACE
if (error == 0 && KTRPOINT(q, KTR_STRUCT))
ktrrusage(q, &ru);
#endif
}
return (error);
}
int
dowait4(struct proc *q, pid_t pid, int *statusp, int options,
struct rusage *rusage, register_t *retval)
{
int nfound;
struct process *pr;
struct proc *p;
int error;
if (pid == 0)
pid = -q->p_p->ps_pgid;
if (options &~ (WUNTRACED|WNOHANG|WCONTINUED))
return (EINVAL);
loop:
nfound = 0;
LIST_FOREACH(pr, &q->p_p->ps_children, ps_sibling) {
if ((pr->ps_flags & PS_NOZOMBIE) ||
(pid != WAIT_ANY &&
pr->ps_pid != pid &&
pr->ps_pgid != -pid))
continue;
p = pr->ps_mainproc;
nfound++;
if (pr->ps_flags & PS_ZOMBIE) {
retval[0] = pr->ps_pid;
if (statusp != NULL)
*statusp = W_EXITCODE(pr->ps_xexit,
pr->ps_xsig);
if (rusage != NULL)
memcpy(rusage, pr->ps_ru, sizeof(*rusage));
proc_finish_wait(q, p);
return (0);
}
if (pr->ps_flags & PS_TRACED &&
(pr->ps_flags & PS_WAITED) == 0 && pr->ps_single &&
pr->ps_single->p_stat == SSTOP &&
(pr->ps_single->p_flag & P_SUSPSINGLE) == 0) {
if (single_thread_wait(pr, 0))
goto loop;
atomic_setbits_int(&pr->ps_flags, PS_WAITED);
retval[0] = pr->ps_pid;
if (statusp != NULL)
*statusp = W_STOPCODE(pr->ps_xsig);
if (rusage != NULL)
memset(rusage, 0, sizeof(*rusage));
return (0);
}
if (p->p_stat == SSTOP &&
(pr->ps_flags & PS_WAITED) == 0 &&
(p->p_flag & P_SUSPSINGLE) == 0 &&
(pr->ps_flags & PS_TRACED ||
options & WUNTRACED)) {
atomic_setbits_int(&pr->ps_flags, PS_WAITED);
retval[0] = pr->ps_pid;
if (statusp != NULL)
*statusp = W_STOPCODE(pr->ps_xsig);
if (rusage != NULL)
memset(rusage, 0, sizeof(*rusage));
return (0);
}
if ((options & WCONTINUED) && (p->p_flag & P_CONTINUED)) {
atomic_clearbits_int(&p->p_flag, P_CONTINUED);
retval[0] = pr->ps_pid;
if (statusp != NULL)
*statusp = _WCONTINUED;
if (rusage != NULL)
memset(rusage, 0, sizeof(*rusage));
return (0);
}
}
/*
* Look in the orphans list too, to allow the parent to
* collect its child's exit status even if child is being
* debugged.
*
* Debugger detaches from the parent upon successful
* switch-over from parent to child. At this point due to
* re-parenting the parent loses the child to debugger and a
* wait4(2) call would report that it has no children to wait
* for. By maintaining a list of orphans we allow the parent
* to successfully wait until the child becomes a zombie.
*/
if (nfound == 0) {
LIST_FOREACH(pr, &q->p_p->ps_orphans, ps_orphan) {
if ((pr->ps_flags & PS_NOZOMBIE) ||
(pid != WAIT_ANY &&
pr->ps_pid != pid &&
pr->ps_pgid != -pid))
continue;
nfound++;
break;
}
}
if (nfound == 0)
return (ECHILD);
if (options & WNOHANG) {
retval[0] = 0;
return (0);
}
if ((error = tsleep_nsec(q->p_p, PWAIT | PCATCH, "wait", INFSLP)) != 0)
return (error);
goto loop;
}
void
proc_finish_wait(struct proc *waiter, struct proc *p)
{
struct process *pr, *tr;
struct rusage *rup;
/*
* If we got the child via a ptrace 'attach',
* we need to give it back to the old parent.
*/
pr = p->p_p;
if (pr->ps_oppid != 0 && (pr->ps_oppid != pr->ps_pptr->ps_pid) &&
(tr = prfind(pr->ps_oppid))) {
pr->ps_oppid = 0;
atomic_clearbits_int(&pr->ps_flags, PS_TRACED);
process_reparent(pr, tr);
prsignal(tr, SIGCHLD);
wakeup(tr);
} else {
scheduler_wait_hook(waiter, p);
rup = &waiter->p_p->ps_cru;
ruadd(rup, pr->ps_ru);
LIST_REMOVE(pr, ps_list); /* off zombprocess */
freepid(pr->ps_pid);
process_zap(pr);
}
}
/*
* give process back to original parent or init(8)
*/
void
process_untrace(struct process *pr)
{
struct process *ppr = NULL;
KASSERT(pr->ps_flags & PS_TRACED);
if (pr->ps_oppid != 0 &&
(pr->ps_oppid != pr->ps_pptr->ps_pid))
ppr = prfind(pr->ps_oppid);
/* not being traced any more */
pr->ps_oppid = 0;
atomic_clearbits_int(&pr->ps_flags, PS_TRACED);
process_reparent(pr, ppr ? ppr : initprocess);
}
void
process_clear_orphan(struct process *pr)
{
if (pr->ps_flags & PS_ORPHAN) {
LIST_REMOVE(pr, ps_orphan);
atomic_clearbits_int(&pr->ps_flags, PS_ORPHAN);
}
}
/*
* make process 'parent' the new parent of process 'child'.
*/
void
process_reparent(struct process *child, struct process *parent)
{
if (child->ps_pptr == parent)
return;
KASSERT(child->ps_oppid == 0 ||
child->ps_oppid == child->ps_pptr->ps_pid);
LIST_REMOVE(child, ps_sibling);
LIST_INSERT_HEAD(&parent->ps_children, child, ps_sibling);
process_clear_orphan(child);
if (child->ps_flags & PS_TRACED) {
atomic_setbits_int(&child->ps_flags, PS_ORPHAN);
LIST_INSERT_HEAD(&child->ps_pptr->ps_orphans, child, ps_orphan);
}
child->ps_pptr = parent;
child->ps_ppid = parent->ps_pid;
}
void
process_zap(struct process *pr)
{
struct vnode *otvp;
struct proc *p = pr->ps_mainproc;
/*
* Finally finished with old proc entry.
* Unlink it from its process group and free it.
*/
leavepgrp(pr);
LIST_REMOVE(pr, ps_sibling);
process_clear_orphan(pr);
/*
* Decrement the count of procs running with this uid.
*/
(void)chgproccnt(pr->ps_ucred->cr_ruid, -1);
/*
* Release reference to text vnode
*/
otvp = pr->ps_textvp;
pr->ps_textvp = NULL;
if (otvp)
vrele(otvp);
KASSERT(pr->ps_refcnt == 1);
if (pr->ps_ptstat != NULL)
free(pr->ps_ptstat, M_SUBPROC, sizeof(*pr->ps_ptstat));
pool_put(&rusage_pool, pr->ps_ru);
KASSERT(TAILQ_EMPTY(&pr->ps_threads));
sigactsfree(pr->ps_sigacts);
lim_free(pr->ps_limit);
crfree(pr->ps_ucred);
pool_put(&process_pool, pr);
nprocesses--;
proc_free(p);
}
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8020691f, &(0x7f0000000300))
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000000)='r\x00')
symlinkat(0x0, 0xffffffffffffffff, 0x0)
open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
r2 = kqueue()
r3 = open(&(0x7f0000000080)='./file0\x00', 0x80, 0x0)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x80, 0x0)
r5 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r5, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r5, 0x0, 0xd, &(0x7f0000000040), 0x0)
r6 = openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000140), 0x8, 0x0)
kevent(r2, &(0x7f0000000000)=[{{}, 0xfffffffffffffffa, 0x0, 0x1}, {{}, 0xfffffffffffffff9}, {{r2}, 0xfffffffffffffffe}], 0x7f, &(0x7f0000000280)=[{{r3}, 0x0, 0x80, 0x4, 0x2, 0x7fffffff}, {{r4}, 0xfffffffffffffff8, 0x1, 0x20, 0x0, 0x1ee277e1}, {{r5}, 0xfffffffffffffff9, 0x4, 0x0, 0x100000001, 0x205}, {{r6}, 0xfffffffffffffffa, 0xc, 0x0, 0x3f, 0x5}, {{r5}, 0xfffffffffffffffe, 0x28, 0x8, 0x8, 0x3}, {{}, 0x3, 0x0, 0x1, 0x4, 0x80000001}, {{}, 0xfffffffffffffffc, 0x18, 0x0, 0x40, 0x6}, {{}, 0xfffffffffffffffb, 0xa3, 0x4, 0x0, 0xc}], 0xe51e, 0x0)
setregid(0x0, r1)
r7 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r8=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
getsockopt$sock_cred(r8, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r7, 0xc1084425, &(0x7f0000000240))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r2}, 0xffffffffffffffff, 0x85}], 0x0, 0x0, 0x0, 0x0)
bind(r3, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r3, r2)
listen(r3, 0x0)
dup2(r3, r0)
connect$unix(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
r4 = kqueue()
kevent(r4, &(0x7f0000000080), 0xf8b4, 0x0, 0x10000fe, 0x0)
shutdown(r3, 0x0)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r0, 0x0, 0x6e, &(0x7f0000000040)="6713ed90", 0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
getsockopt(r0, 0x0, 0x9, 0x0, 0x0)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
r0 = socket(0x400000000018, 0x1, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x400000000018, 0x3, 0x3a)
write(0xffffffffffffffff, &(0x7f0000000100)="b4126295", 0x4)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x8}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000380)={0x3f, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0x11c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000200)=ANY=[@ANYBLOB="200000000000008d742c007b80309905f87c8a5b6701000000000000000000c41400000029"], 0x7f}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='x\x00')
link(&(0x7f0000000140)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000300)='./file0/file0\x00', 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
readv(r0, &(0x7f0000000380)=[{&(0x7f0000000280)=""/27, 0x1b}, {&(0x7f00000002c0)=""/22, 0x16}], 0x2)
unlink(&(0x7f0000000040)='./file0\x00')
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
select(0x62, 0x0, &(0x7f0000000000), &(0x7f0000001900), &(0x7f00000000c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x80206980, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000340)=[{0x74}, {0x50}, {0x6, 0x0, 0x0, 0x800000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="7cdc3fc0aa17dddf7830faa1aaab", 0xe)
poll(&(0x7f00000003c0)=[{0xffffffffffffff9c}], 0x1, 0x200)
execve(0x0, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206913, &(0x7f0000000300))
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x34}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x92})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x0, 0xffffffe9, 0x1000005, 0x6acb9789, "6ba0003639eaec08f501040000000006000000e5"})
writev(r0, &(0x7f0000001a40)=[{&(0x7f0000000a40)="9073b7551eb2b0ea62a28bd87a07099ddfc8bff500c541f961e70552f29712146b1fe63658bd2662280ec81b7583b8ae6b259fc252ce1f3b44ee1fcbdd7af7758fdf1137d1524c039e94669dd650bfdb0f81d922e81d54d76f074da1bbff654e9419c8a7a7f1257f6fc8e1bb278ba10162309e62d8ee26518a773ba0a4321d5268ec7197727ec61c5acde73e9c55a7a44a949a623881d55d345a443bee83e05eea475a3c506e662ae6868772a47a0b789c54dcaaa6409ccf20282cef54884c343fac3ecdb24cceb5fe4fc85776b90505418507ccb4cd223ed1e7ad0d69cc0f63d0b389266600bbc7769d47e6371acbb87a78e8960cf04be0d2a9b1e5d088e0e125080e49098b36b1056fcf9113293dc5a8985b4776786aa808d4a97ba109256a04", 0x121}], 0x1)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x97a8, 0x0, 0x0, 0x0, "5604007c0300e500000116008d1b82834700"})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x5, 0x408})
sysctl$kern(&(0x7f0000000000)={0x1, 0x52}, 0x2, &(0x7f0000000040)="b0c7a614", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x0)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfee6)
truncate(&(0x7f0000000000)='./file0\x00', 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/9, 0xfffffffffffffef5}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
close(r0)
r2 = socket(0x400000000018, 0x3, 0x0)
close(r2)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000100)="6f38653835302778d028bbc985e30a6c", 0x10}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffbc, 0x0, 0x0, 0x1, "6fc60900e4000000001200"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0x0, "00000000000100"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
read(r1, &(0x7f0000000080)=""/92, 0x5c)
dup2(r2, r1)
close(r2)
close(r1)
symlink(&(0x7f0000000080)='.\x00', &(0x7f0000000000)='./file0\x00')
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0)='r\x00')
unveil(&(0x7f0000000280)='./file0/\x00', &(0x7f00000004c0)='x\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f00000000c0)=[{0xc}, {0x4}, {0x6, 0x0, 0x0, 0xbafe}]})
pwrite(r0, &(0x7f0000000040)="e81af191c7a750ff6914f6317e37", 0x17, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x5}, 0x2, 0x0, 0x0, 0x0, 0x0)
poll(0x0, 0x0, 0xffffffff)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000180)=[{}, {0x54}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@random="f3ccfac6c1d7", @remote})
open$dir(&(0x7f0000001180)='./file0\x00', 0x20, 0x8)
truncate(&(0x7f0000000400)='./file0\x00', 0x7fffffff)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f0000000100)=[{}, {0x2}, {0x0, 0xffff}, {}, {}, {0x2}], 0x6)
r1 = semget$private(0x0, 0x0, 0x625)
r2 = semget$private(0x0, 0x1, 0x0)
semctl$GETVAL(r2, 0x3, 0x5, &(0x7f0000000400)=""/140)
semctl$SETALL(r2, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
semop(r1, &(0x7f0000000080)=[{0x0, 0x5, 0x1000}], 0x1)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
r3 = getegid()
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000002c0)={{0x8, 0x0, 0x0, 0x0, r3, 0x0, 0x2}, 0x1, 0x10002, 0x4})
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r4 = semget$private(0x0, 0x1, 0x0)
semctl$SETALL(r4, 0x0, 0x9, &(0x7f0000000180))
semget(0x2, 0x0, 0x400)
r5 = semget$private(0x0, 0x3, 0x18b)
semctl$IPC_SET(r5, 0x0, 0x1, &(0x7f0000000480)={{0x20000008, 0x0, 0x0, 0x0, 0x0, 0x100010088, 0x207}, 0x6, 0x7, 0x3})
semctl$GETALL(r5, 0x0, 0x6, &(0x7f0000000040)=""/49)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x1d, 0x0, 0x3}, {0x84, 0xff, 0x0, 0x1}, {0x4000006, 0x0, 0x0, 0x3ac}]})
openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x400, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x20}, {0x80}, {0x4000006, 0x0, 0x4, 0x3ac}]})
pwrite(r1, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={&(0x7f00000005c0)=ANY=[@ANYRESDEC, @ANYBLOB, @ANYRES32=r0, @ANYRESOCT, @ANYRESHEX, @ANYRESHEX, @ANYRESHEX], 0xa, 0x0, 0x0, &(0x7f0000000240)=ANY=[@ANYBLOB="14000000290000003e", @ANYRES32=r0, @ANYRESDEC=r0], 0x38}, 0x0)
r1 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r1, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="14000000290000003e"], 0x38}, 0x0)
poll(&(0x7f0000000140)=[{0xffffffffffffffff, 0x100}, {}, {0xffffffffffffffff, 0x2}, {r0, 0x10}, {r0, 0x2}, {r1, 0x40}], 0x6, 0xd66)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000100)={0x0, 0x1ff})
r2 = open(&(0x7f0000000280)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
execve(0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000380)=[{0x4, 0x1000, 0x3000}, {0x2, 0x6, 0x800}, {0x0, 0x0, 0x800}, {0x3, 0x3ff}, {0x1, 0x2e2}, {0x3, 0x7d11, 0x1800}, {0x2, 0xfffe, 0x800}], 0x7)
semctl$SETVAL(0x0, 0x4, 0x8, &(0x7f0000000080)=0x7)
semctl$GETZCNT(0x0, 0x2, 0x7, &(0x7f0000000140)=""/202)
semctl$GETZCNT(0x0, 0x0, 0x7, &(0x7f0000000040)=""/60)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000300)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f00000001c0)=0xc)
setreuid(0x0, r4)
r5 = getuid()
r6 = getgid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000180)={{0xfffffff7, r4, 0x0, r5, r6, 0xa0, 0x4}, 0x2, 0x7c42, 0xa23c})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000340)={&(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, 0x0, 0x0, 0x417}, 0x6)
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x20ad8, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x2, 0x0)
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x44}, {0x74}, {0x6, 0x0, 0x0, 0xfffffffe}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000100)="3c9ebbd555feff969613ba3e1fd0", 0xe)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746", 0xbb}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
utimes(0x0, &(0x7f0000000040)={{0x0, 0xffffffffffffffff}})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206916, &(0x7f0000000300))
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x21, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2080, 0x32721e0d)
openat(0xffffffffffffff9c, &(0x7f0000000200)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, &(0x7f0000000240)="d1201d9c07b00a16a86b937d24a7f2947be77f1cbe061ab59b4c433ac24bff05792c3a2d50e2f67857617740abb33121c0e531a8961cc8d241ef36017364c8e76f7c2692163b0acd5c1f91d0a93884476a2cdc21a85ca398e30ac0b9ca4b4c04698c5c47cbac42410af80255109331b974befe33c9e6aaf3", 0x78, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000680)={0x0, 0x0, &(0x7f00000005c0)=[{&(0x7f0000000040)=""/120, 0x78}], 0x1, 0x0}, 0x0)
mkdir(&(0x7f0000000100)='./file0/\x00', 0x0)
r2 = openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0, 0x0)
symlinkat(&(0x7f0000000040)='./file0/', r2, &(0x7f0000d06ff8)='./file0\x00')
open(&(0x7f0000001a80)='./file0//file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x24}, {0x5}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f0000000100)="0000000000424374cfb5f0b099b9", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000001c0)=[{0x2}, {0x1d}, {0x6, 0x0, 0x0, 0x3ff}]})
pwrite(r0, &(0x7f0000000140)="6ba9a481bbd5bc26d716c3ce78c7", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x2, &(0x7f0000000080)=[{}, {0x74}]})
r0 = socket(0x18, 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000140)={0x1, &(0x7f0000000100)=[{0xa8e5, 0x80, 0xff, 0x9}]})
close(r0)
r1 = socket(0x800000018, 0x3, 0x0)
r2 = socket(0x18, 0x1, 0x0)
pwrite(r2, &(0x7f0000000240)="3ab4f8b94eca6ef43f726d3ba6593585f6f08debed8e4230d8a1adceba2ccd42ddbe10ebd963cf29578dc67a1adbfcbe5f5f553a4a15855398111bf942d808cd6538a8d82f3b075ef5292a042f2dbaa00cb41860bfc8b1fce4638529540a895335d7fcd6cc1b9b2ccc08f524b2e5966cc8f89f344441370dc96fdcc1bd0b8f334e8a9d300bd82c069f8b5ffdd43961b251442133bed2df8fd6b7594a46ac382bd4b2ecbf8676b532555da5556b3b640efc6227baafc49897d0a188833622a324037a83", 0xc3, 0x7f)
dup2(r2, r0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x8, 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f0000000180)=0x1, 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
r4 = open(&(0x7f00000001c0)='./file0\x00', 0x10, 0x4)
setsockopt$sock_int(r4, 0xffff, 0x20, &(0x7f0000000340)=0x80000000, 0x4)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x15}, {0x2}, {0x6, 0x0, 0x0, 0x80000000}]})
pwrite(r3, &(0x7f00000000c0)='\x00'/14, 0xe, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000000)={0x4, 0x18, 0x3a, 0xf}, 0x4, &(0x7f0000000040)="1ab883d5", &(0x7f00000000c0)=0x4, &(0x7f0000000100)="3a9bebbb", 0x4)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="82022e"], 0x10)
r1 = socket(0x10000000002, 0x2, 0x0)
dup2(r0, r1)
getpeername$unix(r1, &(0x7f0000001280)=@file={0x0, ""/4100}, &(0x7f0000000000)=0x1006)
open(&(0x7f0000000080)='./file0\x00', 0x80000000000206, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f0000000480)="c2", 0x1}], 0x1, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
execve(0x0, &(0x7f0000000380)=[&(0x7f00000000c0)='^$+[&\x00', &(0x7f0000000100)='\x00'], 0x0)
r2 = dup2(r0, r1)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
fsync(r2)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{}, 0xfffffffffffffffb}], 0x4529, &(0x7f0000000040)=[{{}, 0xfffffffffffffff9, 0x9, 0x0, 0x9}, {{}, 0xfffffffffffffffd}, {{}, 0xfffffffffffffff9, 0x5, 0x0, 0x8000000000000001}], 0x7ab0, &(0x7f00000000c0))
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
r1 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
listen(r1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000100)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x2, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
r2 = dup2(r0, r1)
sendmsg$unix(r2, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x31, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000000)=0x2f)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x2c}, {0x20}, {0x4406}]})
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000080)=0x9)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_linger(r0, 0xffff, 0x1021, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000001}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
dup2(r1, r0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040)="0400cbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x87}, {0x6c}, {0x6, 0x0, 0x0, 0x2000000}]})
write(r0, &(0x7f0000000140)="7c0000fff7fbff00000000000000", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
munmap(&(0x7f0000800000/0x800000)=nil, 0x800000)
r1 = shmget$private(0x0, 0x4000, 0x0, &(0x7f0000ff9000/0x4000)=nil)
r2 = shmget$private(0x0, 0x1000, 0x10, &(0x7f0000b5d000/0x1000)=nil)
shmat(r2, &(0x7f0000c53000/0x4000)=nil, 0x3800)
r3 = shmget$private(0x0, 0x2000, 0x0, &(0x7f0000a52000/0x2000)=nil)
shmat(r3, &(0x7f0000a34000/0x1000)=nil, 0x0)
r4 = shmat(r1, &(0x7f00009a7000/0x3000)=nil, 0x0)
shmdt(r4)
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000100), 0x1287bd6910418d6d, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
pwrite(r0, &(0x7f0000000000)='T', 0x1, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x6, 0x8, &(0x7f0000000000)="9f6a17fc", 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x2ff, 0x0, "f81d08200b0f05000300000000000000d2cad800"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x400000000018, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r1, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
sendto(r0, &(0x7f0000000140)="ee6f3b3b389da65a", 0x14, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x16}, 0x2, &(0x7f0000000080)="74cf69d496e24ddd0d50bf4971bfd6894170c43b982d8be5a272ff82133be60d5dab7e094d67", &(0x7f0000001080)=0x26, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000000)={0x0, 0x0, 0xfffeffff, 0x0, "5fc513bd965045d3286bf112c0818a7235c9d56c"})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80185760, &(0x7f0000000000))
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
r1 = socket(0x18, 0x2, 0x0)
readv(r1, &(0x7f0000003980)=[{&(0x7f0000002680)=""/138, 0x8a}], 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff})
semop(0x0, &(0x7f0000000000)=[{}, {}, {0x3}], 0x3)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f0000000080)=0xc)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0xfd22)
shmctl$IPC_SET(0x0, 0x1, 0x0)
r0 = syz_open_pts()
ioctl$TIOCCBRK(r0, 0x2000747a)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x14}, {0x44}, {0x6, 0x0, 0x0, 0x3b00000}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
sysctl$net_inet_ip(&(0x7f0000000040)={0x7, 0x2, 0x2}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
syz_emit_ethernet(0x1b45, &(0x7f00000023c0)={@random="4383ab6b42cf", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "647ad4", 0x1abb, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, 'o8>', 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, [@dstopts={0x0, 0x33f, '\x00', [@generic={0xe8, 0xc0, "e2b0e071402ac5dd199302f0ee96b8ae704fc480850846cf60103a8e7449f84fde0f1791a8cb9a4f69fbbaeb46ea609399c6e8b2d523b940ab11fe17ec9ec4784df36a668e4e274c674358a16d7f3fc76cdc97a901f0e2755e28e002b0f22677c3b10f6f4b3c0606b87e43bae8ce82a4641521ac881ca341b946604769a88f6cff981d3a253578ad7408b1d061034f3fb8dee7e0124868fe621e214ffaf5d1181cb905be6d5f1e821a7426ca3679f5e449af9d498fe17281bd80e73f50aa18a8"}, @pad1, @generic={0x0, 0xfc0, "7eb3ab121d7ee35e1c5c8b9e49186175e43ff0c047e9b53dbeef9bdae60684c39e6b444b12e92100876ad9d4fe4d98fe5a8681c622c51eb380e938fffd71f4a00e858c1ef621b0f3e0963b0430c03782f532224f0ccae128904095a4bcbccbf04b7f5b6e538196e8aedb5599a18a16b65c049c6d56d77688d69c8f6ec56ecda0f6cce47d92d8b9fa79cb63909abbb19db5b8cfe81e5d8cd6efed41c7423c3fe9a8428af21d486c36805a49ea969b048a63a96b0d6a609ffc5f4d9faaf202da2a1389e2a12c0d3b6f1c38a00235dc0edee55cb6db546a36cd75f603f5e1cc389b7988cd5361c2dc96c62b5cb6139f807e64482d8dfd3f03e37fbf9e8e5deb1ced02214c4abf6b81fe038b07d3c5727aac8b8425abc9e580d69e3e9f27f1e9d82089ccd015af0ad54e79cd18d7ea011bf99481275ef77f23d14fa79213af097a977d1ee9ac229afdbc00fec8344ce10b9ae3e944ae44efd46ccfdb47bfb702a26a566ea5467547d8054819d10c255350652989192e07e2ae3c13996c46f7f81c3e7afa5f3adba6c7b95d433c6af422f07660d17aeb63d7989cab593da4ba5f98cb30b7a9bd7ed70bfc26b46dc7cf890c9ff9e3d0ff3c16984a4697ecf2b530ea6d851d95b2f7cbf338c3470074cdac42853b12a0cf40f0858962879e2f515c2876213efd245a9332a3a90ca005e6c44297c45a8b7e45873aa048d69ba7f31aa639fca802165c80e368f8a16b26aa002cd5336268b6f24ed7819621c52c093d20eeb53c5487af182539b4851fb26f59e73a3fab1bcb0a8122f1077e8b85360984356ebb4b5dd8ec636c80edf022f4228b3817645d39563a42c43f2ac065e9e295894d8efc612d284df1c95b7991d8187787aa462fec2f2b8597f4e4f3b812aa16efd06dd52c1728e335a6ec6e6b9390f677c5fbc08200f95947a2964339dbe9c2fffcedf0710d14ca7832e65e6d64f1e28dfd5206b5875f009b3dbb5829839a7a571f55522480d2fe46269c318e7ccce74771228beb8a6d53cad452b6c6e29a4c3852209033a7f9ebff54c61739ea03fadd2ef1261c81e66a2190ebd4e34225adebd94f4ca8c6b2bebafa92c13143f7a509e267b56d76fb7d42b6984b5e28852a405e150495c32ebef4b93623fa08171c064a6b18a8f2e5e69a2fb87b007ea7f461c30dfe7d8de718e91c63359f25b787e87d9b49499ddccfe32b1f7c4d0d11108d5963af1da4d839454f47fe85d8638f200e733ac547aeffeb3069826a7ab03900537a5bdff60479fe650dbbdeba2da3ab70f8cbccab3bf933dc39ff1e750e277495aea8e31cfdb65a466bcb8f1be783a6e8419b3460e1819a547aa6156f075c38fca3997cfd606548b50fc87e7cd400a3e0388459072e0f11e093e32886990a6141d15d67915127b10d11bcabe9d311ad0acd0bf30b40959759916ad29882ce2a95d8aea5f38ce390d5fdb2a36c4ec5a7cea94e88bf5ffc0552e78576622e642b26781aac386a34408ab60c180a6b0a6552ffe5448649b7217b58657557308e3d444890723c248890a7d0be62c4c03fb9bb4e8b4405c88761c0b80b28b9cbf0849180b2a5c98044c8849746264d3ae743f0c21aa16c7b5acf66157308c3f952445d62fae9de6ce7b4714f1bf1c462eb78ab6e45666019f5817df192f1e83e631166bc55a4b80ea87a20cd5bf44100463ed9f3e7ff1e5ec2f68506d1b472b071d135e5a2c85dadd34bf5cd0b7f2ee68e067f2b90c8a782f85a2ca9c7db2893c3d111b6649c81ea64b6fe4ffe76b87d10d05d02545d61eb99ca47514b9894acd20e9bfa3e1908d6b9c87db8fecfc47b10cc7d9c8d3605f328204c2e27363ee6a6e4fd21cdce5bbda523a2837501434976d64db263a41a8c340992407cefb357a35d86e2328c3e71480ce793915078510ebc2f6b651158a646197b7bb3008872ac180808f751f6c38382f381ea4e205fd4d3eb46ed29a146c3fa60cff56fab787a4659f557ded02b7013068a107c20dfff9db0750bedcfc407176ced509a99b9c3af85aaecd34e780312e4b390b7a97027e1adaf06f28088f1d4999d1a6805518da0a288118cd857a4123d848caa1802d07f4470abea765c9903b47738862f67e7e181db0b87cd21815d67e8878109cb3f34c9dd820fd77b1d40effd6d7385e0f1a67e06e5705b1845df3612e066801852bb4f362f9efa8a700d4224cd6ed720b40d7016188b976a3cdd3015ada40a3f8a871d817bd4fc6bcf86b685acfb9a4eaab7a8aa19f5f6423d421b4feb65c2c504ae2b92d63eccd308a29d52652ad2477ca394dd11589d3c371c172d1f1f4f4d197ac25691b7b0f3c9d56978f74c7d858afb4b06f32593d5874db16ce800dab6decd453e39de27874ceb6c6c0067adae1d44e640e747115854aee72b4f155bd9727ffe10eff19cd6dac3cbf0dc4a8399f4b8615f55d073c451766be0c368a3ed957e62bd2a7e0832260b27b18c4fa36b472de23f5989565e100b53d069fce91bdf9350c7426ba0c3a2ff4efada70bf6cdda5f11f813b1e7ff53e0e4bf1af74cb1eca99660603cf1d0ecbe607c65316f1e988a0f2ff8c4cced565db4ea3fb556627333900dfc00e8fc56986f7f0d538ce109b3d811dfdbf6121cdd5b5fc6479ab1e08bafc087ae08f0c849366e7801195d4a0a869abdf14859b865a9c2229a6e314f93f2213164cc55d834b3e645e06f532169c9024ed7456166b59df7a3b95f35bd7adafaa0eb1c8df198d2d5c9b577e1464b078ca774f7093347efebc79b9bb6b4caf4b5240ee5871d02a9a2bd78707f17a87f662e5b41f59cd44b53ffca9576de0ff4e88f61736bfb245614f6fe01e33ee73ba9bfaa978462d96372dad2fa208f7a3ed2c294e5e757df8a36b03b5accb653d3c9b90e4a3d587b2dfb283e1e74e8a55f71201c40f538edb29b4f700026cc152abf2de5b742f4c4f73b2df55e6712ef84cdd76e9e6d18adc14d5d3633c48a90e4487ace18fed2c510e78f9a17703da78c091f578dda110cb432881456a519c0ea06d6392266f429774cc6799645023d6d808a6698493deaa280af9495af1841058adab8189a38d98f8e36f9d907c4f981865215844dcccf106db158940ab0482c7a4a050ef0bfa6979931e615c2c3ff687b6974b58133187e4298fc0355f0029c0a4ef7c257488fe4aba7b25a182b4d119ad12d97ef5c3453c01bc813352b8e6f3731ccc9e84701f2e612a26bfeb792dac98c382f664b399969fdb5b4ce6ef388b353e81a30d5ca947b8c7dc282cc9762d2d3767be67a0778e2a351cd69fafeaaac885c61585bf6f9f9257e49580ce7e7d29fb11817f57fddcc2554a9c14536d52d634d4faf1255a4510beef43cf987151b0286f848f3c272b7f38667f1cf4f9f9fbaf756d1bb05cbcf29d4d6435eb0314a43c80a1f1554a5ec8e25983edf116d66455743fe96e2c4e245f60718d2a84ef18bb3a46f2298488ce1b3dae2bbad1b61624827912b181ed6b54c0a74965008a4284305d739f6050faa5b335fcd5a9a267ca1de7df6dcda90dcf73d52f9f223dd7f1b5d9a8bfc762a8c17057b81f44f6062269efea4fa2c07797afdd0b0068910225e5abb7129e7bcab9a93b239cf1f824e834162ae3b1550f5996c799963b8acb2397f37437f7d51f40cfcea2b4c0d812e257c08bab56b68bd38d89b69ffd73a0dfbc85c7cb21bf0f73751884f3881a78d27457ae168e2b89f2f85394b7409a7b24924978e9c8c648c66bbaa80dd50ce294cc450c421bdaae601fb40972ec0bfbc0bfc8fcf7d910b4f29fb9258796f6f3ffb0b0ee41c8fd3d1dc9cab1e53df34d4e3b46c9c5820121879ba81b9c6e0c9cfa4b28734338fd826f924188a47a63ab8e01248addf4aa6eb0bf124457066dedd4ab9fa00b142db8940a0459d3adb249d04eb0c58a95d8a35729645a40f7d0e9587b586741b8aad14551eda75138a9175e6745bcdb557dd501d85be18dc80c315214f0a6218c32992b16d67b1a23a88f70414748b2c7dae5d08ef5c7c357e07a48480b7c7281b25044e2518274cd6debec614c83d1e757730c07e0429d93718f6c7d40f5e6f983e936326091b1a021ddfac8d80a48ad2e06e53ede42cc4375d9fdc89fa62131818d25ef97841f3a0344495333e684d0450fcb36bf63f41cb491c1d545886b966b6798881f87a405c0f104813007eb1d8b5288badd1d1f38132721482c6b230b93bf10ca6b418ced1e340c7fda649b6fb735e9eeaf35829d761220fc1cd5e69ae747bb14f4fd486caaaa6e769cf5eb6adc0f1217f7f670a91207bd0f381ef1d8b93704c9b78ede300f8c239f6d82ebc986c60ff37da879074ecd9ad779f73ec8c0d296a921c0e2170a9162af8890077186d74d6112ab25bd8b73ad5720b5155cd5a958a6be95ea002ed12f22f0cb43d42973c19c9a71c66ad39054db4b9b26925fef23e912e1ab2f9596751835529461c33c927255fd83a9c5091111f3bcbc9ebc879dadd7757e570e9870f03cd79ef650c15cf3924b44f74de69b0adfde3c3a5c199d55ed5f6167bc934e469d10aa2aa6dedcb00c1210be7d93665b30469617a77e4adb4e955ebab338ba6bc11634ea535a2bca73791798cfb300518b1d80751d18559df821228d9836d1e9f914aef64ed9e775397c943f5fa95b1e64ddbee8a8fad8ddc9dd3363f7604304d8fda1f3f941d3ace453fdebcc0ad74c13b1bae84cf558507c2223dcb2ddcebf6256ece8377091a8ee79c64f05df3c015094a4342a1be251bae659d7065bc25d1cab4aed002cd3bc00ff8cbbea874c87893183eda56fc5af44eba96f8232f79a9827f1a46760c77ecaa0a9b760e7ab66bf56dec6fbef5268ed70212b39354a5959a58497838d1693749bcc5ed157e07c6cae58504695a3ae750619b2e23948e88db1c694ae68217d978af2d2f729118f3c840a2d14dc103d42beb3319c785c3e6678f3c41e8dbd291d1bd9d493b1015912e248b5777949c24f92b31add190633380feec685e84e1401ed3ed71f5a0c167265aa1a5ee76206fdde815430632b8b2650fb4a3a30b4b6fae915478b18a97070fc0834b30a0c3fed70411c1fd889b2b1ae20721e35e40886fec099efd2d9eb9448f0d330f38485a636e571fa7355d9f73bfe81443d33d9482a8f6bdfc46bfa7c0e83042504f58a4adf2f9c470d27ba0a74fb09b3b4634357bdf2f73d1c0a7092d6f0b176fd0ae93facb7b349c29733f6c6c8659d48506514132dac9fe68c6b0e6f015941db34d5285f9cfb2f007ad147b08382c308313e1f3b70ec4237159384b4432683d152335e43b987696c4146e1d0e6ce6aa74e663bc567783bae894ffb2216b04bbb3a959e9b72e86d04a13d30b0d99876d46cdb0e938a01017823e512744a4ae0223d3c736254fa6a6da1aa271c9051f83a986f190d9dfee797be5bd2bc68a2c8850b024c59a4c98045846a87a4e65c8eded580b8040811cf202a14cf32233e8b73d3dd2aa88c59d0c0bcd3ff433f3fad1768be013a75441b5592bab7b523a77b1d1afdb77b8e86cc22c944f81d017d46f9b0d9de48cafba61f5a3a6cc55d47ff0c05e0657a9aa5d40cdc6cc77fbf272208a6f86206fcbd9286c4e7c89bfaf380809a0b4b6dea34086243a916595d0185149734d4b41a86837c880303cb9d28f289b7752ac9abf21f3ad2adbb7558aa874b4f1dd308fb"}, @generic={0x0, 0x96c, "49a42d0978da2dcc3f397644d525d3c680863085ae291d252e377a819f974dcf5a5ed5d8a124136c1fe62bd6546224bdf8a826d987d4ca687facb3dcd72e38aa5d9d26101e659d838fa6a746e1b80ab3005dd72fd415e6ccc31a9f759339410152b58752389e4a2064ead42fd20be88c377b7de4158bde25148978aef771b5b853ef1ba68e4c2e4877f730e860bb23c61c3f30b45ae236dbd9caf8735dc0b79a5e82057b9ee21303506294aa369378fed551dd56cc0f331cc1e80f29ab6583ab26cfa891358eca8bbb44dd0748c9c76526352a0e22244f040891873af11551f7d8fb757236770fcc672944b91fc8be6f0cc4285f59a21d521489a44fdde565da008dc90435ff1881ed9fb865c2deae1400b34c7c70b3a095457be70093d148ea77cc426a1a4dc5900d9b3c1cbfd0472668d13242b3afb89d6e215f4ebc67d76e08d9ca6416fb371f63ea1a5486ff4ac9fb56d8bf7f37733a81773d66a1b3757255c8e2e332206e8a95446024e01ade275ee0935dd3f1a35331340afe44a23e0bcac2ee50c140346b7fe44a3482f7b5f5f7000783658f87a48c924cc3a21871466d985c4ab1115ec562a5ecc1b855ddb917a47cb6557d2adefff682eee5a258d646d45ee3363289cdc295f4ec9572e442d348d74f899794c7484ecba2ef1064c258a7b5c51a6e923a3810f3cae4076faef95e0f0da70b2c2952eaa6cdbd23945c98ff4de0d5836cc94433c377fd995abe8587d819220907139b5cb38b665f9f45f6be705cb9470f9136f301eee289500284b52e911a4ab8bfd44de9bd2aab5a484795ebb52395d6fe0aa86ac0005340e067badb6b39dd616c6b87661732b531936e29605099ac04b373f8bb138b7579f5f9864e910c50469dc210c5ffbb2ee9388634b93581e65ec2395d0a5e60696037b3f19c76258ca9b31732c12162bba213921e1026e90212164b34d82d7cadcb71a445c8306097dd22df021b7ffaf956306e9ff7b4549f9d8fc8d32654facd2f9051c4b9736e14589e563a44ec5d4b2b3732eeef659bb8df0cbe79372b5e12d78101885b0fa8504a6b043184508b9ba1cd31ffd9aef0bb0e7c8ce695dd60362269ae76551aef08a02db2545d6edc8044818d2d190da7ec94c2587963fc542dc4c67a21cd9381094c1822bd2ef204348129fdf928650929171fe918070985e91b670c6ff5445841e7eb87aff848a5662d416d31bf106a525e4e0b24ed29f5e0ca3247baa5d0043942492e5af84fe4d45ff1c13bf069ded2e05438ff2c84d003555109b8766ce43983cce498abc7c537d868107e55b2715cab0c9e31874821ad849e6a6bdd905d5d3c948e889a79e1f120213a8728fa51cf2273664026fc9ad921b51102fccaa6f7f2cfc3d2d16bf738d9fa5340147bb4a27fd783a06ec6e4134022383a7255f5235d6c9e0a3dd944d05845195dd30c200715ac11f1f23da56855b971adb52e2b1b990d730ed47b7fb80c693e0c598b4d78d562c3a640f3e1c1c218ca657ca3a53cb3dd11231c520db9ef1d6d30e6e70214438429168812fa73e37a870c07d0703553690a8fe6c52c1945b3665a4fcac8c40b2c1f4a26f6e10024b2c9464ae1197165faabbfaf53899526c0f0b8175f1814cb783d3049dbd935d6e3a6efcd0ab49791b5a221bc57302a4b9ce7f56e9a02bb378d754f58ac1b06183f23ebe98f90d2c917940548b9fe5240f2c7cabc773d6210b08c499c26ee5be719a78918354b986ce5842c40e22a8311a7e9ca377eb004ba2b2cce3132d4576f8debefb639075d56970a45c2d1828626d34149605b570839a8a07738b9870f42fc8ae18e34a4fe2af5ae2d71ed36fc99c45968620acec9aa62e19a62cf49af495e2df780c7a0a5172c9c07939d3e0cf8f506bacb185c85497157484f7a65e43af1a6c93f8f374c7f48b5ae6613ae9c40157a60bbf78b7eb2df4fa06c47fb4e83f429bc753ed268f1bd807cb902762682b2e0e253f2cdae33be740fbc04814e5ba1e1be4b9868642cee356b234d0f5a5ed3ddcf54d633cbb67aceaa2b42414b91580cdc39c6dbeaa7be3d6e97fe1248478570e3fb54e1ee21d53b812e3e9ac88488ad6d9bc0b17fac3f0b849981fa4e4ca95598f2f6072ceda4e4b1b110e57fd41a5f5f361cb833d19ec4d896f66aacbf800c41a22796b36af5c8918ee37e0624903e1fd9beb9949cc8313bf11aaba7307d83b3dcdf03c7729035d5290bc657b2aac755320902c0986e34b305590722aa9c9e4272346a207d9acd0bce77b07e2f312f790fc13bb9f1e87c9422944bbd29544e79d660974df8913aa8eb0c7c0356b2c3308fe712258c26ed241bec0008b8c1008247c2b7a8cab563c06f9ac4393e4d175d85f92f818c45e29062c9bd716bc9d1c8a54eacabe85604be7e1ec1f3d5e6e0e49fc50c8634c109b46f575c87fff7b25ca7018ae616c609636479ff36fb10cbbac415c547e8cc95ebd5b7d3c1887540c4ccb3d47df7e970546072b6d131f39823a2a68d99e18fc6a008e13a311cbb0fcdf939d7efc146ed1c84b1fd83ebe36bf6d032fa5fcbfefa104a8c36bff9ec5bfa961620c9452911cd61e07592c1d7069ce827786facf7bc75b8548fd7ff9b74ffd4e30ebcecc53c66e5d9b177b0968c547c7a92c1b788df8fdd50fed752707b8fd467c97561925fb54ea9a38dbab436ddae7a5fc142a7beac95210915c7af1b263a5db0bd876dc050f3649b4966e5b8814ae868cfd567bcf75eaea6bd2e4507e376e17f78f2e38d320bc7aef86211921157dd9a2cf49e046ad12cba02084f479cdc2b43be66836276fb6aa6df7b28b3279b796a7ca49742c266da9f81e8dcf31fac92c4f9055cdb51f057062f626a338c5d3417016b2d5593a26fcc15a40fc2edf9d6724414af39f1e24a2dcc8928984994d5237faefeb4851b96fa45085cc6674ab1211b340680284ee454ba48a659f6bb4795cc0950aeab4f5c2ec13b166ff992f054651490a86d859ed364f25efaab1b8c17df5bb09dd25d508571f7bcd72e7c122efcb680196d735b572367908f544e8dfc663d93cd51cff951d30a3d6812bdb141d251787de8b85c79bbbf8f054d2f3c1e482bbc20c5aafa8746e504282bb6b509e669a2738b6e0f2cd5ee5ebe16a4b29b70606e78afc65684d84cb18b6972ee2a92be2f47ef774d2f11b4cc7fd683ef51d0e554a87d62fc735ea07babfbea532234310857b25c2a14dece003a7919db13503ee7ed3d1baec871f93432d9da0ae000576febd3b16462a2fb85f362b3065da6061e64a8493ed12cc43b843f4b1fbc3a45b86219fe1acdf656d4168b7d0ff809e8caf256e022a5dd2f886a26aa8fc58db679a50e1d062d5fb5d43a12a0208a35f5db31b14fc0cff01f022cf3e4f37cd82648cd22845a"}, @ra]}], "842bed8b22414eb61038a180258e4bda91fe0a0aaa4e8e710b03a83e6e05f6ad4331645912ae2039e1b3a7e7a3e3bc01281b0a9551f882513e49c9d434e4e3f4878d9b2803a671bcb198ecd798045cd0552b716bbf1868aa024cfc12339f78c11a6dabccfa456aa4e2b0435c00dae67d20cc13df25bca1a817de90a4ec3cbf1b37586e"}}}}}}})
socketpair(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000100)="d8798372e0b787c67ef9c16ff72c993f718e6b26d3ef2587d3fc35d53f10908601125f23386c82ac514d7fac02e2858f6da512646a7be74011a8a984d1dd3d6eecc71650bf644826156b2a6e772e5d174022663c56c33170dc11ba2e9846be1a6a082aefde6996eef9200577dff7994346376eee1ffb42e96e3eaee16b716399eaccf533bc375c14ddbea3abe819809489", 0x91, 0xa, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000440)={&(0x7f0000000000)=@in, 0xc, &(0x7f00000003c0)=[{&(0x7f00000001c0)=""/229, 0xe5}, {0x0}, {0x0}], 0x3, &(0x7f0000000400)=""/48, 0x30}, 0x840)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xa, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x54}, {0x7}, {0x6, 0x0, 0x0, 0x7f}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
openat$diskmap(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x67, &(0x7f0000000200)={0x2}, 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x87}, {0x7}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
writev(r1, &(0x7f0000001380)=[{&(0x7f0000000040)="b79c75797734235fd849fd8f1bef9a8aeeeb95eab08598c2a2b3608a37b1e72a201060b04aa8f950a6d812005c5c3c78fc9eebb253667b045e2918990ee8010a6d0e195bf2f0f89227f4b47266febc685deaf2b22cec68585fd9ba563c93777d52eb6df23540f42cc44b9693e24a90521219bfe337cd6fb55d1d089b1f18bbb7ae535d57837e287524f8bf70efdaf833bca8ad5a5bfffc69e57f95c40b5aef0a19359fcc4e33b34d9024dac9736f6100ed48e75f91691805220c33284599904d097d", 0xc2}, {&(0x7f0000000140)="d410a0754c950130bbaaf4f25552827c3b3f958c0b1956e1bb3bae5c0666cc", 0x1f}, {&(0x7f0000000180)="c8f4c597faa17dc0cd31322ede2b10c2eb72fafc513eb25854c5a6c2add51602cc05626a54dda827b113ab2fe86e8a4eb9400b48f2f8a38f2d3bb0b5f28c91ff95a32b1c3f77f5628a7c2ab74c9b92bf3ce54c443643486d515ad0df78433acee4694034e7cbb4a792fc38701d858945611ce67fe1bf28c11d4643e0e4282d7debfec4e5bad2f0da9be9f2cbe11a14beb3daaa9b33223e8c76a1f06a36c059eecb5c68e59a4d45b268ad528702447018c03c8cbc2d4414c486018f8fa11e28c2743d35b91ae2b6e6bb3fe85a650b132ca8d7fdc4fb3e279978d602d81764fd7f6da40234504866737072a4bc312c50d669b53b4ed847a00965f4150ecd0e0e284aa1bf619fec83cf5a9ea49a5a6aab89828f7ef2bcbc5fcdbf5630946ecaa35caced2f00f89d61a970df49e6c2055da684687230ed0eff0853f64a624ba6a1190520000aa9ac2a52fe75ba6314c4faa5796c904f46c389cf745f80b2c49f53ed11f7ebf164f6c3a23826ea3f4e37fee21441b0b62a2483765f2f72b1e751a9e91eab19769bd159b3af08dc2d9169c0f01f9e5f4e1c6bb91afbab49a216891365658cecabf88f5eb789148da3bd9c3c5d4cf57401f3824f4f2077c6ad79f23d0240b5f1ff232c017c2271f341bb8d8680224ef88bf5ad5a3438a6addb8fa6003ff0f36e90a645ce79ac9195e50fe4d4b3dd8bad9d5468041d21c3cffc40af55c5fa6175ab3db240d4c60da2706acc0b780dc936836cf9dc8de9903e70e88d1df91aa66a47652c1e60bb083d9025d7e3a87eb8215745922dafb2eed373059615fe1bd1fe7361a8b9f3faa849ccdf87e537a5103a57ec65e8d8af05dc86f4bb97c29a3f038af1c4845700", 0x269}], 0x3)
execve(0x0, 0x0, 0x0)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d", 0x16c}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
symlink(&(0x7f0000000140)='./file1\x00', &(0x7f0000000100)='./file0/file0/../file0\x00')
rmdir(&(0x7f0000000080)='./file1\x00')
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x200, 0x0)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000500)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r1, &(0x7f0000000480)={0x0}, 0x10, 0x842, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000500)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
recvmmsg(r2, &(0x7f0000000240)={0x0}, 0xfcf3, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0xc0}, {0x50}, {0x4000006, 0x0, 0x0, 0x80000001}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
socket(0x400000000018, 0x3, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x0, 0x2}, {0x6}]})
socket(0x18, 0x3, 0x0)
r0 = socket(0x18, 0x400000002, 0x0)
setsockopt(r0, 0x1000000029, 0x31, &(0x7f0000000000)="eb", 0x1)
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0x68a, 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r0}, 0xffffffffffffffff, 0xffffffffffffffff}], 0x0, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f00000002c0), 0x3, 0x0)
r2 = socket$unix(0x1, 0x2, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000140)=[{{r2}, 0xfffffffffffffffe, 0x11}], 0x0, &(0x7f00000002c0)=[{{r0}, 0xffffffffffffffff, 0x48}], 0x0, 0x0)
kevent(r1, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000540)=0x8)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000580)={0x10, 0x2, 0x4, 0x0, [{&(0x7f0000e10000/0x2000)=nil, &(0x7f0000ee9000/0x2000)=nil}, {&(0x7f0000bea000/0x2000)=nil, &(0x7f0000cb2000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000bbd000/0x3000)=nil}, {&(0x7f0000f6c000/0x2000)=nil, &(0x7f0000cff000/0x3000)=nil}, {&(0x7f000087b000/0x2000)=nil, &(0x7f0000800000/0x800000)=nil}, {&(0x7f0000ee1000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000f8c000/0x3000)=nil}, {&(0x7f0000aef000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000914000/0x3000)=nil}, {&(0x7f0000f81000/0x1000)=nil, &(0x7f0000beb000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000c62000/0x1000)=nil}, {&(0x7f0000d71000/0x2000)=nil, &(0x7f0000da2000/0x7000)=nil}, {&(0x7f0000b26000/0x1000)=nil, &(0x7f0000825000/0x12000)=nil}, {&(0x7f0000d22000/0x1000)=nil, &(0x7f0000d15000/0x4000)=nil}, {&(0x7f0000a21000/0x1000)=nil, &(0x7f00008c4000/0x3000)=nil}, {&(0x7f0000bfd000/0x3000)=nil, &(0x7f0000c28000/0x1000)=nil}], ['./file1\x00', './file\x00', './file/file0\x00', './file0\x00'], './file/file0\x00', './file/file0/../file0\x00', './file\x00', ['./file', './file', './file', './file']})
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140)={0x8})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0x9, &(0x7f0000000000)="4044edf25b412189548c8766", 0xc)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107008, &(0x7f00000000c0)={{}, 0x0, 0x4})
openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x200, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x141a, r2)
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
write(r1, 0x0, 0x0)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000000)=[{r0, 0xf3}, {r0, 0x4}], 0x2, 0x0)
close(r0)
syz_emit_ethernet(0x36, &(0x7f00000004c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "873aa1", 0x0, 0x0, 0x0, @loopback={0x5}, @loopback={0x0, 0x2}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x2d}, {0x1d}, {0x6506}]})
syz_emit_ethernet(0x2a, &(0x7f0000000180)={@local, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @rand_addr, @empty, @rand_addr}}}})
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x37, &(0x7f00000000c0), 0x4)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x9, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x9}, 0x2, &(0x7f0000000080)="c80a539058151c621efc61eab9a24c58ac58e40faa6685187188a8d9530d34e20d5f78b0efc38c59e43a745240466082fecb054f065e89e9c40aaec719867842179a2b18e273b4976c4f8ad87691f69746b3a69946365210f72866f05a10ede6de380fa5ca6c8025670e49128efc44cc", &(0x7f0000000140)=0x70, &(0x7f0000000180), 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x54}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x4e, &(0x7f0000000140)=ANY=[])
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed19f4c8b2ca3ebb3557699a1f132e27ec0ed602000d7d026ba8af63ff37282921e4fd89720fd3872babfb8070c1abda58601a8bfee8aca4911faff575e8c881ff7cc53c894303b22f310b404f36a0069000fcfff80004002ec7299e340000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
link(&(0x7f0000000000)='./file0\x00', &(0x7f0000000040)='.\x00')
setuid(0xffffffffffffffff)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@local, @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast1, @multicast1, {[@timestamp={0x44, 0x4}]}}, @icmp=@mask_request={0x2e}}}}})
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x10016)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x4000000}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0xfffffffffffffffd)
sysctl$hw(&(0x7f0000000040)={0x6, 0x2}, 0x2, &(0x7f00000001c0)="ff9e219a32c712697e956a95d04fe3c7254188b8e605fd6a2db5fad2bacb1b768edb5f0ec9e85f8ede66fbda0fefb3759fa510ee80a590385435ca9cce42959ccbee70a6144d087242b6bfe452280e8f8cc8aaddce168e5a66f4e82df82c29343769dd159282f9d37f5f91c3844ad223e22c7f87fdb2a99520669d06875c9c3bcd645fc47bfb41980589f2f8fdf7a70dcc60a7c83203bf9bb72ca7f1586493", &(0x7f0000000280)=0x9f, &(0x7f00000002c0)="2ce1565b8498f04bfe800c9ad6e5dccf07049fad1c68e46cb1d29028ba8135a4d4858129f4ef512a1c301cdccbf460f75ae5e238698530ab0069e439aad7a5423d9833c0563e94e3959a4ec2f79616f1a9c8063b1ff86c240889cda3db1a16aafb669981770c965f233b06e312fb44d2a5a6eb0cb0002b18611c216bb0c477ed1d4d67acc49f3b102c0aa4ee57eef515f4cbdf52273f807f42f42df04a1d42415458d5d4f99dc68fe81df49048df42988edc418b2065b91f0f72395d8b48976a1850880cb3abc8fb0996e74ce3f525195f2042efbc533f60d23d4d092b10f8318e305c5795", 0xe5)
sysctl$hw(&(0x7f00000003c0)={0x6, 0xe}, 0x2, &(0x7f0000000400)="0160b2b56db50f5c746afb9f3d45a01d76ced3b35a65253761f585643843913bb14da29c8a94641d03e66135f1acae9465fcbafbab147df1fe7dd6", &(0x7f0000000440)=0x3b, &(0x7f00000005c0)="6659d205303b4968186bf84be6a24d5acabcf52c76816efaad17b0324f22a5c5a1c3bd023b9929c675b9017c4049ec9f7411d08710290d383b39c5b7840ee27bab2dc450c7f9ddc839ec7896759c182fe7cf7dc35f845c5a49a9e0f3bc7d5c2aa8e4690b44ac26e1dbdfd20f736dd68dc80bd38ac5ba0f18554cced5e564187910a3d946879a8f51db6be16d2988011c174b03", 0x93)
msgget$private(0x0, 0x2000000186)
r1 = semget$private(0x0, 0x8, 0x10286)
msgget$private(0x0, 0x1)
semop(0x0, &(0x7f00000001c0)=[{0x4, 0x4, 0x1800}, {0x0, 0x6, 0x1000}, {0x2, 0x7ff, 0x3400}, {0x1, 0x40}, {0x0, 0x2003}, {0x4, 0x5}, {0x2, 0x1fb, 0x1000}, {0x3, 0x33, 0x800}, {0x1, 0x4007, 0x800}, {0x3, 0x4, 0x1000}, {0x2, 0x9, 0x1800}], 0xb)
semctl$GETPID(r1, 0x0, 0x4, &(0x7f0000032500)=""/102380)
semop(r1, &(0x7f00000004c0)=[{0x2, 0xff00, 0x1800}, {0x4, 0x8, 0x1800}, {0x1, 0xfc00, 0x1800}, {0x4, 0x0, 0x800}], 0x3f)
semctl$GETPID(r1, 0x1, 0x4, &(0x7f00000008c0)=""/178)
r2 = semget$private(0x0, 0x3, 0x4)
sysctl$hw(&(0x7f0000000480)={0x6, 0xc}, 0x2, &(0x7f0000000680)="1eec6ced10abb19b291f90643780b0884fb4d04a73c5134c070e353e6f61a944664fad3560a0a5a662040a87e74549662a3eb256e69467f8c86eda176e72dce7890ae256f79ae9ad74071694c94206676da481b878baf00dd8962863a22ebf18dee7bc64ebe3befcdbfb21576e39a3c828c3df7113", &(0x7f0000000700)=0x75, &(0x7f0000000800)="dfb8f811f78981bd7cf3d6e52eecb307b969f3628ecef48424d1341121ba617eb592222da6bbfdcea42820eca2517ad49a9909dfd262398368649a9db817c43cdce60d9a396ef4ded0f5b73aa88c1f91e3e1b1d5821d1f416c111de2c5f9b868b6e59c1578c8f242b0ca1a6ed1b02e0a9e6c91d5434276bd", 0x78)
semctl$GETZCNT(r2, 0x2, 0x7, &(0x7f0000000740)=""/159)
msgsnd(r0, &(0x7f0000000280)=ANY=[], 0x0, 0x40000000000800)
msgrcv(r0, &(0x7f0000000500)={0x0, ""/139}, 0x93, 0xd6d0c418f59fe7d3, 0x1000)
msgsnd(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="00a9e57e430000004ca28dc72e914103e270e9161c3aaebb2c4669a42708c6a56c0300c4456c25285728db95b0fbfede7536e4bb422696f6254dd6c59891783b04dad9d11c7132619c16c8eac2e46dea29e7f6a735dc62eeb3bc7ab404ef5ca3a8548f0d9f3ae531b10260d235bb19076071360ea0770b2ba41036cf6196e4d9cf8f76886c7bf1857c6fdeebc34daec2d015f817f0207f28498affff68daead0d928a165b93b1a361892e3f71b51a5ca326e111c927433bf2b4698432ef4f23698f18c75087f791100"/212], 0xa, 0x800)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f0000000040)=[{0x21}], 0x1})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8278892ccb44425f846ac24b79057cbf079668334e99f13b3865e3f27d5e5a116e15cd349ef85cc3a94a5673c57a1c4646fc8fb34452c0ec33e50000000000000000000000000000000000000061"], 0x10)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x14)
sysctl$kern(&(0x7f0000000080)={0x1, 0x3f}, 0x8, &(0x7f0000000180)="8b9fd66879625b2b2c9137e56a21de5ad9dac99f1f40987e9fdbf1c15274947d365fc44f8a405198bcc50e4a3c586892dffa578abcf6715dc70471c400c3497099b38ca29dc2b0ef17a98d61ca490bc655344279b3d7cdc8dbb93ee9618589aa7f0ffbf40f1d765dde07d53d913874d063687657ec156f32f5a84edc01b5d002844a7057f22f2a398bee0a05086e22c2b13814b71a8388345d36c9a0879b84afb63372b92017fc86d512df6f71e7f2fa2bb25034b9a845aa7dd1e7407df8f4613d9ca102cbcd32bca626f2814a8a0d1e9d24f4fab854625673b0f01e8688efa12979abd491076ba58019e4ec35a017b129f1db972f63f400000000000000fc", &(0x7f00000000c0)=0xf1, &(0x7f0000000440)="dc4435a1a77a8e318920d50a0fc6d2c51a86ee514ec3fe82ac6be8daccef6262c71cda0a001cd40b916600000000000000005e82e6dce291368eae9d274f3c43cba0693a0a33966e18a8391a678a7e7765ae00e5b796467bdd1eca56ab2c859c8c6c7932e213c9cfd8a325cc78f2353a832379ac2c8ed54dec9f29c7cebc948e827d84f21053f44d9e74534ac31287040000000000000019abe0665e0b29e42e5db210f4098478ca4770882e0608815d257b1062874ee9e3d5095c1ed496779dffe14e315b01afe8c43201", 0xfffffffffffffd00)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc028445a, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000100)={&(0x7f0000000080)=[{}, {0x21}], 0x2})
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000140)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b407d2707e3d81f4dbbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89750fd3872babfbb770c1b98bccf5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500002002000000000000020208a371a3f80004000006000000000100000000000000000000008135d08bb2d7fd4f6c59", 0xb1, 0x400, 0x0, 0x5888adfd882f9c89)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000280), 0x59)
readv(r0, &(0x7f00000006c0)=[{&(0x7f0000000380)=""/189, 0xbd}], 0x1)
readv(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)=""/74, 0x4a}], 0x1)
close(r1)
writev(0xffffffffffffffff, 0x0, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1003, &(0x7f0000000000), 0x2)
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3300)
ktrace(&(0x7f0000000040)='./bus\x00', 0x1, 0x116, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x7c}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "c4b3c80300"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
write(r0, &(0x7f0000000100)="1c97e5f580acfc87f545dc686f16b7b01a3ac672559eec9f4bc44c4ba93775d8b554d8aae7a16b9719e5c68f0517eb723d1b3fda93e9a507d0502be0247f34704a21ee1c8e1744f2f589116a22e817d35a7b673ed78c10ed3ab5aad60cb0e8af3256732faaf7ba19069ccd6562821561035f74287e37b21515a6efa1271bd196a409f66097d85b2c68a17ef8bc353836dd066a055ba8e59e16953cc3cf4a749e75290ca7d2f3f51efb5d616f5856e7288ee2d9d388345c375086fac3cd0e608842f987e787bc59ec5619f66ad397983eb480aee6bcd42fe591cd42f87f14009db969c200cc663c0b1732b7a927e6dd4460a54fb8ddca1bd2429daed80b0ff880ab3f03ee6b31766fdd778ea99db08238f10f9ef096fd654710910a4c0c9f87129ca34e0aa4a26845dfd83a480b15fcea923bc2de5bbf8d004ed000c502cf45bee9f84ffeb20d5a0c47442b52b078c8e60b82a9d3e91b1a664a06e706b8cbd292be4286016ac1a09420894f81363a56dd19107e65a331c3f80ca21b63f00e9a16ec87b93903998d42548395418af148c79232284ff6f3b2a0399b4b82ea48fd1554678f2026f0233b7a67e19146b2b55b7922e3af9448042180f6bf587d0e95d3f1b811d80bd7aad26d7d8d2cdaf47afc528b462d9347636026d31aaae2f4bd2850ee378253d2c90ee93c8a7b501c17e5e03ab09c15ee04cadc34e5c112466acde068c27b5b01af9ddbcda5464285f073f13717a1e79e824e63647fa685effab84e7b69bce6c092ec89aff976a769821c26af31f68961f3ca79b27ecdfbae6ac82d275e9f483ce1e2ce58dcfed7903bf6fc5af0d6b10c15a41df5019b71e464e47df1772a80e5a30fac266f561d707cedcd84b5dca05761eb596dd4f8b8ac130c32f2ba65070fc9dcded331217bdb81d8312c624d23a8a05d927f64b9c0be6193e8814fb6673be6a8294b066e6a021b5890bca666ad37dd5df0253ded412f62e8b2ed528da64e610abe4d5b22db9270993366e09010bdedbf785c1eb447ab8b1f0549ebdd05ee73cc01a5cbd7e01a2a9a2fbaf3932b3871dfa3260059c6c5ba8d0eece183900a0dff283ea43d17f96ff9b0b93b4aafb839ff23d6917354a20d326c791f9e5d408bd8675ace533d1d2dae38fc61269015a623bacba7798aa8063b85e9affd5dc66c0afc315ca5b4d8f2ca6e7ddf3b6a640e24807c30e70ed010e635ff485c07277bf21cab9f9f4cf41d56f31ce20d28e52cb3a1b6727bf6a48218901b8f597f099d41c394b5df0db596a548f4d01625950f17acdf9933f1c09a6ad0812a145a7db4e8a2bdd2153289a56d15925438d181a230ae1051bf3a3d94da9a17bd11284eccfde63fa67427980d7bf93053f5bf6a476eacd9b14b3076a85c21ed9fe4299d0041420d9da8a590731d512d99e2d0b88fad674d27b61fdd2f7f01beda19834e73eb4685b7470c7b2134b0f29b75cb942c8df6e897214cadcd61b1c1924621598aa67cb1026b67cf9e60beb3a6b3e6c0a0b1ab8204b147fa457d9c707fb4f9592490604447f895a28de7753685c4ec6191493fe1f26eec1fa6d33231f83b3d1a01eaf125c2726dff43021e4ca02ffb8975df579c51248e46ac6bd15dd8c9cebad2b6639b2c19e578c1a9c7c11218ba72d316f34262b18e787dc4cca69419520777dd7088dd70c42cb796c5c157dc216f22d1dea45844fa966d225f8dbdd41e74914e561e676e2fb62a625e4ff35d688a3a256abb385f203ba43f6db4955b0489462541defa2c26b14eda88833ac28ccafb92c1a7c75e6707235b9ea1e4001db760b9f96dbc92b83b35bf3ba825885160ac64118472f9776e64d321027f42c42d9feadb510a72b4bc4701c79251a2040f1803029b654a17f3eea4a9a990aca25a8ca257775ae5cb729474389003aaec6a38c8b7609e0dbe502f9cd357ba16cff961b987e41f0a685b19578c6e7ba8f4761925c1839817147a32af7b3eebfd708662dd3926ef8e42bcf5e568328468de74de8c810a14b6c1ecb3479377bf6fdf277cd0484fe7178a8143ec41ace2ff7d612bf75aa170ec40d412ec960d1d5f039d623e7c7d43a53917ec613f429ae5ba4b283205c99a5d13b2cfbb7eeea9726706cbfeba209b4514b7e1ec343ecabbf014a0f204da12ecedc01c1e5bb398954041b29e4f94bdf9e70c7539edf3d9b582c0e70fc94a2df6d04d15d0d7f9b94ed61d5e93a3b8f99a9184ea5d0375f83eac0b9b4cc48c9d3ae85c1968ee67767b4004505b11a0430bda8a1140d2438c61643108f4ef7f2d79c3faf1963313aa87f960567f4d6ea8aa88df84df8e6fcd86aafc8757a11e056bb836bccb191fb59d017e1c96d6c4ee00e7b3dbf06321f1d1be11380e603ed7368888399256e7e77dc103053fd0c803aa594fd3287d193cabd17070bbb13bbed3373154761d39e820197f4c1d1d1d14ddeb83d73138373d18e9797db9dbcea2d1ebe583bebe170b699f8c018bce31e37fd6d45c977c900664e53a1dbe401dd12abe927bae93c8571dc268f2117be24bd618a457e1dfb28fb90562129042a8d8b790542fd5c6aac8f82a130e3e2d31b85cacfab07420ca5621d81043c94edb171ad535c3581299d0fcc8f231dd2abfb9c4eb3636a1ec282a03453c971203d8ffaa13fde5b0e1b643a918c5ea0162ab11e6b5f9ba2c3e599bfbe7a5c56803bf681f19edb2a6e10db191b34ae762a9ab91ba27ff37835d6bc1b9ac1a821f313c0e06f4d557849af3ce932c4bb38f85ba23a2a81dfd72225992d3e8facbbc9f6ed5c2fb88eba77f372f0ba4322af8a0d145262f5088e1e499e1dc9633a50da1b53428581d33526f2335ae9e4b56b7dfca5ce58de1cbebf14380eade7e198296e2e8a46b891bef0e3c00e68b610a650e28355bab017fbc5b76c8d47eee604cb2ebce8cab2b90eeeefc12935767a18f0e854d11ce7db66e176190824353451307c78fcc0f7cb62e62c93dbe28a40efde9ea212bd1b722f796dc54c11168e535b83caba6840855925584bd6a44f7dae6a6c0d2e6b78836d3a4bb6cc56900d5b44b8fa2e234cac99225f7a7721846386920fd5af9a3044ab61bd94c5e9d4f96abee46e67a16406825ebd4e602befd7693c07548e89005162ac7265532c4353fa30eb334e79d9ab33185cde18b343f7a182b48195ea17f6879164cbdd9082b89cc17a53cfe5ceb1ba1009937e35bb3ca7bd190b61bdc330e967460f5f3bfd759b6f5b701e56ec77321d3d8c041ede3bde94b9a8d41f43fb199084ae7d156fc1dbca0ccfb6c8cff789cf64396e254ca0c7d69c6c9ba0f825cc9252c15fb0dd4217c5db15639f7523b758523ca300e45de1445ea4765a06b1333bf692d22f845634e1bba14e0be4c65b6c0edf9172a037a60170312c44bdce93444e53ec3cd57881c8f7f77623be65efd7e6cb397d8783e03e504ec4fdce43fe95c7c197f825b0f87453717302f675c5d4febd61450ce2dc06ba70ad2b14de2554e15d82b3e560f7a6cbf205be676b6f7e00bd06ef8af36a24f209ae9e058c7f18a20c8c830faa77ed89367334b54254a87c7de7123ef8e3278a96064fe946f790cc153e3feddd3fcb8e096aaf134b8a88a42f3223f7366df711959aa026b39967b65aca93cb8a05d58205c88cadfe631d037568c0481f5bd8de6f32bbc15e05c36d8202d15f9d8ad339eef21d743d4cbed5adab347648b0047ba97e41bc23e2cb8050da8ae27c875f72d8fb3eebc5b216fbf4fb88402aa71775016c0e13d09c02c88c278058a502c4ce68fbdefdccad8315779962a6ee797a2dd9146dff155765f0eeef7ea1e8c46ca75d1fbcb9312a2b29a3a41239159f8726663e8e7515ef339a4ace87d3604e5fc164e8fda06df38da0c801c23183db53a3ea5d7e41d4a5ab34dd13a6ecd638231f102ceb4a9eaede93192e10f6963c77b328d0b37960a4124d4267bc1b99b7c795e2477a566b95a4ef2c412e17571937100c826af075e80cfe075b8577e177592a500f745b69cbe635cf1e1e9b6d1cba5727efd083359fc7d8e115eb88d4d0c63cae44f16d8b6e1f66dbc7be1df75a93de9bacb55e6da492ce34e998ba01b81b749a005ab8ee411057947fa8c6b11130f846f84f2a055636c5791a6b5a2bf4b3d72bca3c059daff03c36a17421108b9f216a09f404ca6f0044653866a4beb4549f91688991b66db99293ffc3dc932d13112da7167dec6d20bdfbd338686e075b3874721354c081fe133bbd7ebf7971ce80fda9a6bc43b241d18cf4d74ab35cc34b2a0e1be421ac8c667e4aab3add049871e40dfb3f392476a03b1153c1ef0a8ff9263bc7294f35a2cfbe9764aee7a06248331d5c488bc0870261d0268f0efdd52925bae9d66ff3f8ba04e6d678d2166952c8ee14f0ca727df2b2521db9035aa5e0da5ca522d7ee1c45c00344d46c0c6b6c32fe2e48552c3a24a51ba59b4dc2d89d393abff0d8f658b20058e0df2808c9b11f4a271e4255f77b12284b32cc969336c0d3a64d9d1269bc2e39527ab604d31309d79b8a1b610d724f180101abe3c58e23878e21417934bdefa9aeaf64c3300e0dd314bd7481fe1a6d2ed24053e29ffc1c5c82ec2ff36915b98413631f857c5ca47295e3e0c34a8a3abc0613b24e40278cdc977369c6d7c96affabb5b42d8a9c5f980d314b879d7b45fcfc83ff49bbe581a0b72c69545e1a444d5a69617620f627b4a0ed1872dfed9e466ad4fd3771c73120bdca4c09614f043401ba5a62a1bf86a3fcbe7c0b38c5ddabd3bcb10535898fb428a3af99158710c348a3e58b0ddec18dc605d6fa2b5ead8c1eee8c33925da3ad4499de0a0b5675c3230ca474e2d52fa43b35d8d5e4500d85c0c246712319c0839580ea388bb68920b3b85f86edbb26a156dbdd64fb5ac209b2375b7c4171680b4526f82ac7d94a0d26085c4fc2612ad8d5ebc5e44769978863b0578047ca0e588c02fe94d9829a15f5936184eae6f46b86984266b8d23014ee4f57c36ca826d0783383e10f812bbeed26af516faa3170d151d26a0b4cf1c1a45a7a18bd1c04e929b39ef52a316b19c98fb1126ae98ca81ea3998a1259fd8cb5c870bcae95684b09716f06860b9b0cf5e446bbc86f70e7dd018c9ddafe43db7ec5172dbddbcd92982e6b0861a71078d6a0c1498719877ae361d816394098749527be6574240a88a7154b79b9e9e6122af69efe8bd56b0ff5d05a650c6d36ac52320a899e0880198a9b39d19ff936e40ea2e8bfd3fc07d5845003582b4e08a6d9c702f567b817dbc43eb9f9aacb5e16ee2e33c6e8746f9643aa43225f7ce4ff9863cb24ecfb9bdad9aee21e9647c288578bb9114167042576ce58e61febaae548b229f4cbabba9aa923baac12e70b829f94442ae6768fc26a839481535e993cb0991a74df8321cfa222db1717faf69061192daa16caef29a6ae459d1f87604d6a1d72511e87461f844ea6f2afcbee10efc54ded194317fe2eea34b980c0c7a120b82286e3453c085b46c8546735edef9132f291ba1d73dac3d3b60883b7940d0cd5fa8d2f08ba4ea74cc636308d9df2ee069def5babe8ab65bf881b67582050e7283e00b5ccf628ca41d9cbb410a14856bc94ac3914ac8cd7f6a662976d951aa0cfb81e3953a0308923f928df4c11415abf4200c42945ede19b28e54858477d057a61d6148c21b42021a45e88c53017fc4cd8adfa1cafd710b284d67ea3dd934aae0cdc3b11eaa9b65106e49ca90c6e9d984917bff24d11768688a5f6d4f0cdb9fb1085ac5e71c4f962ae6c6", 0x1000)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f00000002c0)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
r2 = semget$private(0x0, 0x1, 0x0)
semctl$GETVAL(r2, 0x1, 0x5, &(0x7f0000000400)=""/140)
semctl$SETALL(r2, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
r3 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r3, 0x0)
r4 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r4, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r5=>0xffffffffffffffff})
getsockopt$sock_cred(r5, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000340)=0xc)
pwrite(0xffffffffffffffff, &(0x7f0000000300)="8def17a715da6adfdd91e45f51b6", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000280)={&(0x7f0000000140)=@abs={0x1, 0x0, 0x0}, 0x8, &(0x7f0000000240)=[{&(0x7f00000001c0)="19", 0x1}, {&(0x7f0000000200)="5dc1558155a5ee059f373e878a56efe4510b523c52b8c0bd46382a5cb8dd", 0x1e}], 0x2, 0x0, 0x0, 0x400}, 0x2)
setregid(0x0, r6)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000000)={{0x5, r3, 0x0, r4, r6, 0x180, 0x9}, 0x495f, 0xdd0})
setuid(r1)
chdir(&(0x7f0000000080)='./file0\x00')
recvmsg(0xffffffffffffffff, &(0x7f0000000140)={0x0, 0x1b, 0xffffffffffffffff, 0x1, 0x0}, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000001800), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f00000000c0)=0x200)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCLOCK(r0, 0x20004276)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x9}, 0x2, &(0x7f0000000100)="8cc49514", &(0x7f0000000200)=0x4, &(0x7f0000000240), 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x0, 0x0)
rmdir(&(0x7f0000000000)='.\x00')
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0x81)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
ioctl$FIOGETOWN(r0, 0x4004667b, &(0x7f0000000000))
pipe(&(0x7f0000000040)={<r0=>0xffffffffffffffff})
fsync(r0)
setitimer(0x1, &(0x7f0000000000)={{}, {0x0, 0x17}}, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffbc, 0x0, 0x0, 0x0, "6fc60900e4000000001200"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x1, 0x0, "d6e696d83fc4209e414d70000000d0c73d6500"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x2, 0x3, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000040)="0101f1c4f4ffffff03000000", 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8702dea3e6"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect(r0, &(0x7f0000000000), 0x10)
mlock(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
sysctl$net_inet_etherip(&(0x7f0000000000)={0x4, 0x2, 0xf0}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0x20004401, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x81}, {0x2d}, {0x6, 0x0, 0x0, 0x4000}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
mmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x1, 0x810, r0, 0x0)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x802069b4, &(0x7f0000000300))
select(0x0, 0x0, 0x0, 0x0, &(0x7f00000001c0)={0xfffffffffffffffa})
r0 = socket$inet(0x2, 0x3, 0x0)
sendmsg(r0, &(0x7f0000000000)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3}, 0x10, 0x0, 0x0, 0x0}, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206997, &(0x7f0000000300))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmsg(0xffffffffffffffff, &(0x7f0000000c80)={0x0, 0x0, &(0x7f0000000c00)=[{&(0x7f0000000b40)=""/188, 0xbc}], 0x1, 0x0}, 0x0)
sendmmsg(r0, &(0x7f0000000b00)={&(0x7f0000000ac0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x8}, 0x10, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x2000300000000})
fcntl$lock(r0, 0x8, &(0x7f00000000c0)={0x0, 0x0, 0x10000000000, 0x20002fffffffc})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000001700)=[{0x44}, {0x6c}, {0x6}]})
syz_emit_ethernet(0x46, &(0x7f00000016c0)={@random="6b2c64844967", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "7af2ec", 0x10, 0x0, 0x0, @rand_addr="65307c4128bf703441297693620af6b0", @mcast1, {[], @icmpv6=@ndisc_ra}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x5c}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
r1 = socket(0x11, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
getpeername$unix(r2, 0x0, &(0x7f0000002b00))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000080), 0x8)
syz_emit_ethernet(0x36, &(0x7f0000000280)={@random="5f250647e133", @random="e8ad5958bb99", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x1, 0x2, 0x0, @rand_addr, @rand_addr=0xe0000000}, @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b10005016000009005001b0007000000541c04fecea10500fef96ecfc73fd3357ae36caa0416fa4f376b36acd00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af63003728211d000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c50000200200000d230000020208a371a3f8343712051e1d89e000040000000000f71f000000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f00000000c0)={0x2, 0x1})
r0 = kqueue()
r1 = socket$unix(0x1, 0x1, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000180)={0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x7fffffffffffffff]}})
kevent(r0, &(0x7f0000000000), 0x3ff, 0x0, 0xffffffff, 0x0)
dup2(r1, r2)
sysctl$kern(&(0x7f0000000000)={0x1, 0x46}, 0x2, 0x0, 0x0, &(0x7f0000000300)="aaa2e82d", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f00000000c0)=[{0x3c}, {0x16}]})
syz_emit_ethernet(0x36, &(0x7f0000000040)={@broadcast, @random="0ba3ae0a44a8", [], {@ipv6={0x86dd, {0x0, 0x6, "f7b00e", 0x0, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @rand_addr}, @loopback}}}})
sysctl$net_pipex(&(0x7f0000000040)={0x6, 0xb, 0xc6000000}, 0x3, 0x0, 0x0, 0x0, 0x0)
shmat(0x0, &(0x7f0000fff000/0x1000)=nil, 0x0)
pledge(0x0, 0x0)
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf80fc7b457dbe24125f76e25dc0ced443"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)='x', 0xfffffefb}], 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "13555112d54728b89e22ce105b0a95bd9a4c8fc2"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "94bdce02189eea097b2e95f5736ef07acaa49f6c"})
syz_open_pts()
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{}, {0x7, 0x200}, {0x21}, {0x3}], 0x4})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x5c}, {0x15}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f0000000140)="ddfa874894e428de1f6a8fee203b", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000003c0)=[{0x87}, {0x60}, {0x6, 0x0, 0x0, 0x10008000}]})
pwrite(r0, &(0x7f0000000140)="f94c4c49dfd685fbaf8a8d1a029b", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0xb1}, {0x2}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x7}, {0x35}, {0x6, 0x0, 0x0, 0x400000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x200030000ffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x8, &(0x7f0000000180)={0x3, 0x0, 0x0, 0x100000000})
syz_emit_ethernet(0x3e, &(0x7f0000000100)={@local, @local, [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @empty, {[@ra={0x94, 0x6}, @timestamp={0x44, 0xc, 0x5, 0x1, 0x0, [{[@local={0xac, 0x14, 0x0}]}]}]}}, @udp={{0x2, 0x3, 0x8}}}}}})
r0 = socket$unix(0x1, 0x5, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000180)={&(0x7f0000000040)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f00000013c0)=[{&(0x7f0000000200)="5061b787d916a323118bf3c9dc03535270dfcc5214a99cf408ddf4b57ffdb3d7571d5cea5c2e211a5dd0e95db80f8fc59b7a87ab7317721be2b57c396f372c77e986db49c43ea5e9114a0787c51109a62d1faaa4b75ca826d8cbe9a0d2a367d568e467eb48f2ae2827d35e439106f5cb5c86d0051fe54765f9b2f68f7c6e6264b83a709e7ad8924bda34f94f7e390beff5f0e230fc69fc34a1e5864edf04a674ea1bb757b601f8dd83d549590d7a2e43eaf4bca1a7d85fb6ca16e857beadb1f6dc91ffcdcb6fead14b41c4a49d859b96ab3250339464c70a9294e5c970a0e7fb55d13c70fe00dc381378cf5bf422a3c7e672732d1f03f5fc953b4470e4d1fd035388b2becf24f94fcccaf4971ca49a89e18aab328c0704a22ad05dcddc7a910a1817a69db41c7e6b4ebbee15d87c477de02d3f9635e28d5a7d20b22a5bab854832bdb47ee4c1839d339b11dc1bcf3ad037802f16959eb313de2def07d654edc507683331e0be1791391c95cc5254ee7bde3f5573d450b1b7c2aa391c5cce581472e5fa91386fe6b0a136f5bbf2162008583ba02ff55bc57c397329aed2e2291c61fb0490efc4f2f0df235837861ee21be2200d07cbbbaa77757f80377002775d8b40a5fc0f0bec3565c2b7201ff40d1e0c1f96965c635b49e23646d53930e5f61fe8decd163e1c2e5d9167e7dbd9071d8918f42f27ac1e76d0799fe0bd82954ea1d895f1442c0d785718397dabd71eaffd8c22e853cbfdf04a5555a6b53d75ff9f1161372dbfde8a65761a3911e235e92887b9c7771078b4202aa92e6501103b17d0afe9ceffa764a808ff2e67fbce1819bc7d25b332ab5bb85135cecce89c8a32ef07082d38c1c59fd2f6cc0a29a65ea5d4bb48da7db3206e2ed114c9062c45983b2ab2a2f66b0e87ab08f12cec93f48ffa906ea9aa8bc8651d6f64f21f8e098e3484646d9b45d6c741faeebfdb89573c0ba0bced97ebcabbc8c42bdaedf44bff4a0b56fa79e3d24ad3c3123e66cdd4e45c45c4e25670e6e85b51348e8158c6597cbe116fefaf367edb85c50f441fdae70218da371373bd14e3e046ec824ef2394070aa301f8aa14b1ad2ad5bebc580425e7a3dcaa4e3d78f406ff82b5dcdfa41fc5aee573d9c49320efaf22c50d32131ef45f59cf4ca711f4a408adaf8dd8abb65f7b8c754498b2df0859e5109c9f6ae8bfeceecc3fb3da0ec79e4ff64ff21a1cb7b4d49ad827826ade8517267717d15de22941031daeb95fc9fd3469c34be754c726f6458e801dc5c43a99111830d73d7d260943050e92e39d6649db134c6b14378752f2c48be6376f98b342defc68abe4a1a0221ae2303b749d142ada191bf6f3c817a042924e751941680c969d38a55bee0a7ab30a67c84f01fe1967a40b9dfee7d34c806111cdd16444aaf8ab102f9abb46d346be8a9a78a57a8386181d6ec92079f290352e16c1a6665cf9e301246483b66db96ff21cc5243afd24951c0e75b8a48b6d9489a46608eec397bd91ad793b752b3877f7f78e433221147f30910bc5c33b822270286321b63e11667a3af941f65e6ca1a04ccd04220e7576bcf447c43fbfa3c5255da6a25bb1e14fcaec4b3ce18469dccce7c539e8fe866108f76164f53efbc3f62b036b1b57b855d462f13e980dc0d092c868ba8aba24d9bdd22c0a05f3c95f9f12e673d606bca4d832f692ba8db8800109945c673d0b722b692c67852c4e64df6fa8354fbe90b46667bb717fe2d259752b8665e1dac0f010eb50760d612bab85285761c0c748a0a3a4a4d9727bf916c2f8b420863d9b918e5865e194fb20b322aa4954a33318f9d8c76a87f9f8bf4be1aa598f554591a9d7a1d9c42b44aade1d3e4df91a97da72dae05ac3de0b99cc2a4eb7aef104500d556d8e902a601e9055a053df37842eb2ef522ebf46fa675360be3cc1e9eb1d34fa7135ac43eef871634d77a2b2ee6679aca4a8147e7dfd1cb92c4d29582891855f909b20b0cb590cfdbc86f7fee630a98ffd56bb2441e60db8aa23a10a9930641c23ee7db3dc9673af9b124ddb27499fb26dcf1ad645a7935e56b1367a25f8d6c18fe7cb2d92a53aeadf8ebb82de44ecc00d826d6a92db07c4a96cdcaf02fa88192d8ea1b70e72f01f49562f870a0b982d9e1159c9127fbe8f128e913cd403a3c4db2c07dc3707ef5e32917763552a043d630a0603bfdd60fea7375d8f8d4c24f0dc9f82284c8b0db10afbc18bfbe2ca5f338fd44eee4f02a66c304c02724bd600c9c83e006e6cbb9a0ba2fc59e9bf602ce018c573611ea93ace55de52bd9c4c21a872a8b1d72f856a3264931ee3ce2e595f04ec0a0d5eea9e2861a2afe7738278225d4cafec1286b12f353bfcffe62332d8cc005bf93341048bffb9290355613a9bc319f6a66456361bcbe9ef1bffaaaf1565d4cc108e36c07c9f445c40adf683fa320eca7a0adce0f4f90cf85b65b3b30cccd95f976f7212b01967aa194f5de4316277ac5666d122d80478345768deeb1a6c16af87df05b887f3e0d9abd3b97a59a8174d82620ed1759654a7e978dbd6bb6de68584c708e8e29ef8a47ed7556d59da8f325224acd096dc871c1031a28cac5e1f650aa600294760dd9c4cec03b5bfa99d356a89d42afdcdf1035b22ef7ab5a34aba09753fa44e2c3929f57b4db892c1bdb0941441537412f9017bb775139c307c916123113fc3b1c9e8b09da5d033232030b4db7c28ec217c4a7a5a5e62d2e63b4839a83cc578ce26405cfd3b80b168e6606a8988f1586b273d47c644b3695b0a045e09d51462edb5c50c50ea2bd4c1b9b3c491e174381f5232b2ca4a9b3c0ea07cc796b4093e2e513589e0b5444e3d8d94b8946d2e99a8085ddab2df5b4aec96b982642f7b46790f0164cb74f2c9882ff3360b7e90dac5df18133b961749648e5cf525b05582bc108a90b03a6bce869b793acfb1e877ae11d6f96ba752efb745220d59f9463d737b8a06985ad01b5e151de7fac5498f66e7d797e51991c70962bfb51918e6b99efad5170ee7caa6bd7f81b1e8d37245c59f9e53f9b8b0b2a8d56d9e0502e88ad192764da0b851a31e58c92c6ee41c2b953db5cb8aecd96017620e8fb771d8592833fe96506b283b10f6a9b4e542387865280fffbd1a1ba986dc478f03c4475736cde4d993f7fd36020ce123e3741789060ca9932aba4920c0dbb712bf2019e5eabf49b635426cf878ae8c9f4e57d33e6a1b511bbd138e005e8976d80998f91e52c11bb34bfd6b1e54d33d2dd89a4b4720582af11f6a9a689e5440716ccbfb302f121fb609cf8049483f0857d206b55e9fb46be054734728a7d632e8bce261f1ef483b26474fd8c56753ab88ee7cc15aa5b7d2e2d573654c21fd9cecedb34d4d704093bb515f6339ee56f511b3ea9e8d1e36e45de2e5a3388d42f448bad1c6013e6a1e75a957028b41db27824ad5a9c501fdb2b7f02d64afb283636f3c983b7d09218aa1cf30ac82fd5cdc9853e27fa65a3b8f2185db10cb75caf02ef4998064b19f1d8b4d75bced5747706bc057486a5048399c5ea7842da2a37e14451347d942fecd37390afd7c510180928ed81c3b623539a404a8254323c0e834dc13c0a154ba684e474dd4f6f6bef400510af154459018e1f7ff56c62869314702fd0d390cf5648beb3b4f5c125218e252e0f40a9261d4e9f0a4fb9ef9c4f69f1bdb44d3bfc53e4ca598c184a64c9ec755db8c09fa3d22edf5929ac55c10d60fb869b33a59a72cae67d84cfd7c2533986b8e9e131c8863cf416898d7c83b5c2ad2197d4aad98c7a4984eb429d3f67db3fa9349642315637a2d0c46c9d063e8718b91032d409bf44b12d038b2b4d06b5e2486279264d0c96fbb6107a5f5ad01f5a14aac0ea12f429e094071de3af77ce8f266df56044ff31733fe3855b2c4d5856eb8e22a0cc695da54a05a92867bdfdd08514edd1708ba4824126b14ee6f8148046731347f60cb9a1f3ce33649cd85dd77148c35d964aa256862e75bd34e3e07c4a85238222a69acd9e52568c3199d14bb1a760335a6888f36245c1027d974c8127cd6eec9dc181a3c5d9d744d865444f6ff19aeef00d0fb6023a9101e2d1498604ef9dedb15e06eeb60b0efa8894ebea9f3a0e39284a83c08f230eb41cca6a06456cecd353c3e6d8ec1a1d116c0932e3187680cff09792c27df775c0006df2aa65c9538ac5e3baaca05c1ae2d7bfcc6fe4cebb8d5137ed9ce881c6ab051bd591161d397e2f89c1acf2eaae60ceaceb5e03fd7ba45858d3573bc88ff113f3037bab8681dc022c5ac6e36898a20ea57105b187df417bf8e9607b9501d15e9db0c7533de480e91aa8988c0956861baf7d52a841d8b700e8241752ff858f139ec7a6707893dd4e6efe7ab15404e9343aa0e6f1d12a2705bc6c8445e12d80c413dd237f2f3c901f2ddc19a3390ad11b86c03a02eb8491403702cb251a0b14525bb2058c7a07e59575f2839352aafdf022d759351a33f6ee407e86e4226e958e9c051bba72cbdd8f7008367585e534d6aaf09916861512f0a263f128a60ab961d5fbef9765b27c23d561ec4f9641d98aece250622668f65df4c214d54cb1dcf037ba530a177455ff1c1d6b20b75989fe3fa28d7bf74f02046c7dd8358066321591590df5d8a75431d3a157427e65cf59ef32adb041c8b23239ce2a8b50059ee195bd2a16e35be901c0aa166406c14ed8147ff929cd63328c80165ea80768b0573a0a91fc692bebac29c9b91e70e33507e22eee1465d8d8dc7fe13e5e36b031378d918c751371f7916361b78aef09953ec2e362386c00ac1bc4af1e3f5488f1c92f0706ad16327a9dfad8f59a597fa8ecaf33234e4fd79d3037cc78b2080fdee1d19f21df589c569d890d1dd93aaf9f2e60e8b7c21ba6254618646a7c378863cb232b17668473b2d4714529caffeae21fa5256ae13153a28a338b6426e19724abd372e73fff131a5564e27920e4473a71dc225a8d19509c9f82dcad8e2f6c75536845e7fa67caa3c6eb446af35391d23c4774718376a798ecc3951f4eb83e653c4b99987603cc8e95ede66e86a633dc5dae05a1f9d170ad20ee5c92982bb1c769ada59616634b93bb4e2c08495767375d42980d8e780d84437f810481ae61f6290b5dd9759641db043646c5a6f6d9bed7a89342135554edf07ac4dc4d8bc16732ef8936a01cece9c7c45c3b2795daa3a04682b04885ce3262c2112bf520e89347b456a49ce22c260b2fb17dcdb7f2441982b790f30980a213090e53fe07bb73b30c8468e6feabc653223107a48708a3ea8b5d2e02ea15b4e9d63d6b44d6133d3ed2abb1e5e817e9187cb61fb6d1de8846822da80173f6025ad4eac69c8e79d5d776f7dccc58f6e00ca8b952327463431f43025b6e0f7abb4529dc6c10fd0df2a837370bd636f3cb6d4dafe5b69092c8511f9cfbbedffa29825802bcbb3882231417010fb96d2770c93e5a7d49f1b4b0060c3b208e66654fff38266188af897dc8b6508ee635cc3e57e5d7c382e67dbae65cb80e1a3e487415d361d216ab94bb5494d3a9266f3e55ad9a9028e30ed9997bb2e8b203044e796add230543904631deaad87f1973717a5c2c7b309bd855f07575a36b0a74bb08a5ed6d10b47ee829d1aa0e2da36500110575c19843079cf935966ebcd4e6abc78a0340705c2c78d2d2f2920a443465b3aec2cbd7c4c37effec72f6f900bc864820e4f56928c6afc059e5a40c019dca938b68f1c3014b2ea836c02dbaf8737e3df870123589bbd82e664b5a68e0e2c14e2d8b53a66cfb", 0x1000}, {&(0x7f0000000080)="cda2193d2ad97dfef9b2d6c64db43d9cc406dbfbd2fa4f8d64d222a00272f40036728f290a09c7f2a6ece9acb66607194cf4358859092b3b44a219b41982c5c53d28d4048dcdb801ed05267cc14bb9067ff42f11e7ccde61fb41e7784987f1695936cc7b961d5e440a2c285495d643472148ff164f970ac896e56c5caac7a37477b866f726f7b4fadd31fcdefdcf7ffbd34f18014c4fc03f", 0x98}, {&(0x7f0000000140)="068e3dbfd264ed2282f852f08ac9510e23046f", 0x13}, {&(0x7f0000001480)="cd0fd8c1d0728be63f680536d0191954a2da901267c875c0edc1f7ad56c6b4431692dc2de29277d262c3201a0ac9421176971582dd9d99bbf0cabcbfe1b4ba2813b9a439d2602978b7bd925ec2b9c37a5c53f696ab246bf6977c7f381dfe447f87015514f1fab51869a7a5d6fbbfca6d074ff1d45424782b6eb01ad84a6a90133ee820ea156facf81f5d0ff9864b1ba8624402144e5517b1b317c10c2bfcfd48ff0af8593b06b0d2bbb60e8a9dd9a282955270e6ffa7c8b8fc20fa6d9c09001b482cc0f0dd267c58cbe245b687a025edce8668cc999f8b70af6e5757757433f9e8a57ec816416173c1114f071f0fa46c5635a0b444324fbecad7f7aa14099f0bb34f7d8557265ed1f8801f055bb13b67b1c23468a6871aad2519150737b33e5b3a20a74256fcecf1e5f621fa790506e979c076c932ab6bb0eb39bd0bfe399f1438f8", 0x142}, {&(0x7f00000012c0)="3aa306e79d9f88761a0945dc4f8341d7657b9f9742eff76e1ce627905ac4069e9cc4654f9ad1a0b4e25bb7b5dbb4e1695649c06493a0bec9bf9a499154a50e1392c04e008b1cf2351cfd9eeb81d272d113273b3a4fafec82f84c68aec3f1d3bc4e018dc3f4f1f089994d9dcc3c57ae21079735887fe5e217410944dc74503e19fd169345525e32fef866b6f5772376fdfa646bc4d5a199d4e3356430b54ccebe91b8aa456df043ba5afb7f2590f43d7f43879d34d5f01e412f576530e064d66fa5198460bd153b2cb9dfc20e0a97b20bb2665de28681e282da363e5fd2a5848844065042daa8be1daef6eea5f99e1686", 0xf0}], 0x5, 0x0, 0x0, 0x1}, 0x401)
r1 = socket(0x800000018, 0x3, 0x102)
close(r1)
r2 = socket$inet(0x2, 0x2, 0x0)
getsockopt$SO_PEERCRED(r2, 0xffff, 0x1022, &(0x7f0000001440)={0x0, <r3=>0x0}, 0xc)
lchown(&(0x7f0000000000)='./file0\x00', r3, 0x0)
fcntl$dupfd(r0, 0x0, r2)
fchmod(r0, 0x4)
syz_emit_ethernet(0x3e, &(0x7f00000001c0)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "17e8dd", 0x8, 0x3a, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x3, 0x0, 0x8}}}}}}})
getgroups(0x7, &(0x7f0000000080)=[0x0, 0xffffffffffffffff, 0x0, 0x0, <r0=>0xffffffffffffffff, 0x0, 0x0])
setregid(r0, 0xffffffffffffffff)
setgid(0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x402, 0x0)
r1 = open(&(0x7f0000000200)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000380)="90c3fe67eb586898600425f2f573e0d1ac83c18d00c8e22066c0d389fe895a974c8d45aaf9a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3214ed85fb20e088c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e32082cec454327b6a1522c332ea628b8cb672e9e724780100000017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b28020000000000000000c1570f94a3e302", 0xcd}], 0x1, 0x0)
writev(r1, &(0x7f0000000180)=[{&(0x7f0000000240)="06", 0x1}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
sysctl$hw(&(0x7f0000000000)={0x7, 0x4}, 0x2, &(0x7f00000000c0), 0x0, 0x0, 0x0)
syz_emit_ethernet(0xe, &(0x7f0000000240)={@local, @broadcast, [], {@generic={0x8864}}})
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000000))
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000100)={0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x6c}, {0x44}, {0x4000006, 0x0, 0x0, 0x80}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
dup2(r1, r0)
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
listen(r0, 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r0)
close(r2)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
sysctl$vm(&(0x7f0000000000)={0x2, 0x7}, 0x2, &(0x7f0000000040)="77ba7e7d", &(0x7f00000000c0)=0x4, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
openat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x200, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000000)={0x0, 0x1fc})
r1 = open(&(0x7f0000000280)='./file0\x00', 0x0, 0x0)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
writev(r2, &(0x7f00000000c0)=[{&(0x7f00000002c0)="1c668feaec53927700830bf845932dbf", 0x10}], 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
preadv(0xffffffffffffffff, &(0x7f0000002680)=[{&(0x7f0000000180)=""/122, 0x7a}, {&(0x7f0000001680)=""/4086, 0xff6}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[0xffffffffffffffff])
sync()
unveil(0x0, &(0x7f0000000280)='x\x00')
madvise(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x4)
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0xc}, {0x60}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000140)=ANY=[])
open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x4, 0x18, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000200)=[{&(0x7f0000000240)="00ff030000000000", 0x4d}], 0x1, 0x0, 0x4f}, 0x0)
sendmmsg(r0, &(0x7f0000000180)={0x0}, 0x10, 0x0)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
r0 = shmget$private(0x0, 0x3000, 0x0, &(0x7f0000826000/0x3000)=nil)
shmat(r0, &(0x7f0000ffc000/0x4000)=nil, 0x0)
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
madvise(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
sendmmsg(0xffffffffffffffff, &(0x7f0000001880)={&(0x7f0000000280)={0x0, 0x0, 0x0, 0x0, 0x0, 0xffffff3d}}, 0x10, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r4, 0xc1084413, &(0x7f0000000240))
sysctl$hw(&(0x7f0000000000)={0x6, 0xf}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000300)="53ba96375f52c7ebd029feec3c81e6f9e853b3f4cbbdc93df44fa6d6fbe3e331fc7067fec4ec9d9a00364e02970b46491e2b7cb5b3af3b797ee37c4e4fde4406416b2363da94dbf346ef9ebb5e1284544ffa28f9e1818cb61a7ee71bee4a948362b5f5a5cc4ca0c0e82c45c0004a53bd502ba138920e026d031a78bfa742197f12de6e7aa96c2563316c3e54290189c95eedebd2186297b64cb62cf58cdc0af940d76e5f91050e9284f47d6cf17e2a388a8942828b0676b6e959e87abf5e9325a3f1b8b51c0094744022c715be7f752e16aaad5ebf5b8ebb1d688fef12f6f7f7ddf6bf9ffe3d3ba5923c717dfe0966fab0ac08a33ec3cf136a385f49a0c3ab1eb5780d9be41cae2fe6b69f513cbdb9f8cc3edf77ed1d4c5ff111ef5a81f83279bb9819ba745038322e", 0x129}, {&(0x7f00000000c0)='.0', 0x2}], 0x2)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xffffff80, 0xffffdf7f, "f163b56028000001ed0804000000284600"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be6c0045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dd31c0dc31b3c0e35ca7", 0xf2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000000)={0x1, 0x100000000})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{0x20, 0x0, 0x0, 0x200}, {0x6406}]})
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x800, 0x0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000140)={0x9, &(0x7f00000002c0)=[{0x101, 0x9, 0x3f, 0x5}, {0x81, 0x4, 0x9, 0x1}, {0x1, 0x7, 0x9, 0x1}, {0x2, 0x80, 0x17, 0x3721806c}, {0x3, 0x7, 0x3, 0x80000000}, {0x9, 0x9, 0xff}, {0x3f, 0x0, 0x9e, 0x9}, {0xca7, 0x3, 0x4, 0xf5a62b49}, {0xfd6, 0x0, 0x0, 0xd3}]})
syz_extract_tcp_res(&(0x7f0000000000)={0x41424344, <r2=>0x41424344}, 0x2, 0x8)
syz_extract_tcp_res(&(0x7f0000000040)={<r3=>0x41424344}, 0x7f, 0x2)
syz_emit_ethernet(0xca, &(0x7f0000000380)=ANY=[@ANYBLOB="acb8a73d733d0000f9ff000088a83800810039000800450500b4006407b906119078e0000001000000004e234e21ed5612ddbd5ef53990918d495d4008c85df2e2366a6cb768b63a357b399e087cd8b221af2e4f01d07231932d99673eaf306e9964ed04eb4b11b64f00a2a47f083a73d97888c4a679764cd784224901881d4af63508799799f2099dc847070229736ab2377cc1ff01db4fee18fc75164b93ee1112b738af7cba61939d6cb57e0ac0a9074a62fd6081e29c86c86cc6e8cfc4", @ANYRES32=r2, @ANYRES32=r3, @ANYBLOB="61040c31907800800100000020d58faab16bf78cb19c26d50ec08c34f0e72b21686f620541bf584ea4af62e2e7dfc4315d4eb7744ddb68de25473bf506ff3f87df8b881c8e68046bbabaa3f1b28da467d64e29fc0c11f68503004ac904fc84d06653024e7abe7937256f6a112025fd4ad67a36d174150fb715dedc6a10ff0bef0c571ef4cce6f9f788449bb5c9652aede2904d8e"])
syz_emit_ethernet(0x91, &(0x7f0000000200)=ANY=[@ANYBLOB="ffffffffffffaaaaaaaaaabb86dd67322cce005b000400000000000000000000000000000000ff020000000000000000000000000001330000c2000000004e214e2100089078c8e46b1bb92d0c39dce48af156c8115312b60ecb08ed13fc9e0d085619bc23e1b7c72dba4a3c87575cf0b461735ba7742f99e10851d71ca82c0a4fafc750e6f9a9db3bfa1ac86069f47832"])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x2, &(0x7f0000000000)=[{0x1d}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
socket(0x2, 0x2, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x50}, {0x5c, 0x0, 0x0, 0x3}, {0x6, 0x0, 0xfe, 0x8c9b}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001008e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000180)=0x8, 0x4)
writev(r0, &(0x7f0000000040)=[{&(0x7f00000002c0)="d527d249fb41dd05d82cdf60c11ad900cf0783aed719f88c3d7c7af659c1641f279bdfe534d2b114b8f269cba4b0b05d649ebe0d34de3c51e89d9943a0494a85a1dfd434504766234dd4fb51c22b19f00cc85ea1bbb2961e0e17fef9a35376f6921fe4232ea63a3fff851557758df2e9df64904cc1385010fabfdf119f1e5cf18416f271ca7019d3f5492274ef678d20c4", 0x91}], 0x1)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_COMPLEXBELL(r0, 0x80105702, &(0x7f0000000040))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [0x0, 0x0, 0x0, 0x202]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000080), 0x719f648c9530152e, 0x0)
r1 = dup(r0)
ioctl$SPKRTUNE(r1, 0x20005302, &(0x7f0000000440)={0x0, 0xfffffffb})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000200)=[{0x24}, {0x35}, {0x6, 0x0, 0x0, 0xbb4}]})
pwrite(r0, &(0x7f00000000c0)="d000"/14, 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000a, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x7]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {0x8001, 0x40}, {}, {}, {0x210}]})
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x1)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x2, &(0x7f00000000c0), 0x0)
r2 = dup2(r1, r0)
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f00000001c0)={0x1, &(0x7f0000000100)=[{0x3, 0xde, 0x6, 0x9}]})
syz_emit_ethernet(0x4a, &(0x7f0000000080)=ANY=[@ANYBLOB="aaaaaaaaaaaab9a9128b47bf86dd606e36f400140900000000000000000000000000000000bb00000000000000000000ff"])
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f0000000040)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000df2000/0x2000)=nil, &(0x7f0000e14000/0x3000)=nil, 0x8000000000000000}, {&(0x7f0000e69000/0x2000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000f57000/0x1000)=nil}, {&(0x7f0000800000/0x800000)=nil, &(0x7f0000b0d000/0x2000)=nil}, {&(0x7f0000815000/0x2000)=nil, &(0x7f00009a2000/0x1000)=nil}, {&(0x7f00009bc000/0x3000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000aab000/0x3000)=nil, &(0x7f0000b7b000/0x2000)=nil}, {&(0x7f0000955000/0x3000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000956000/0x1000)=nil, &(0x7f0000d4c000/0x3000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000cd5000/0x4000)=nil}, {&(0x7f00009bb000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ad1000/0x4000)=nil, &(0x7f0000f1b000/0x3000)=nil}, {&(0x7f0000af0000/0x4000)=nil, &(0x7f0000ecb000/0x1000)=nil}, {&(0x7f0000898000/0x4000)=nil, &(0x7f0000b52000/0x3000)=nil}, {&(0x7f0000bb8000/0x1000)=nil, &(0x7f0000a21000/0x2000)=nil}, {&(0x7f0000e9b000/0x4000)=nil, &(0x7f0000d03000/0x2000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
r0 = kqueue()
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000000)={0x0, 0x0, {[], [], [], [0x0, 0xfffffffffffffff7]}})
kevent(r0, &(0x7f00000000c0), 0x1f, 0x0, 0x200, 0x0)
sysctl$vm(&(0x7f0000000040)={0x2, 0xc}, 0x2, 0x0, 0x0, &(0x7f00000001c0)="bcad358631368c988339e6a524183c86", 0x10)
r0 = socket(0x18, 0x1, 0x0)
socket(0x0, 0x0, 0x0)
listen(0xffffffffffffffff, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000), 0x4)
sendto$unix(r0, &(0x7f0000000040)="d2", 0x1, 0x401, 0x0, 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000000)={0xffffffffffffffff}, 0x10)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
r0 = open(&(0x7f0000000340)='./file0/file0\x00', 0x301, 0x0)
poll(&(0x7f0000000300)=[{r0, 0x40}], 0x1, 0x0)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000100)='N', 0x1}], 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="06028d84fb"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000780)="0714edf38a03344ef95de162a9537b68fc57560000000025", 0x18)
write(r0, &(0x7f00000001c0)="80813a79bac1aacb3868e436d316fabcfef95e61894f35d9432a3f9d49850600020816d27f76cb369af318965c9d443d1962f1e733372b3f5dffabc0ab5486e9a06359035693c3cf0f8997c439a101deffd0a5d61bfd02c0308552117bf524b471d4bb2761a2e72c0acc0c11fd24847192ed25dac39435541630823ddc708bee498f6f0b9863a280cfa331789b02fc4d3a22d20ddc7c26545e6dab3242b2ac407e46172041f69255b63214975c7899672790ccc2e7fd26c9f48c6c292cca8bb0ab92740228802fbb9dbd7edb691686edb2eba067efb88df3d388122150b031f920e0242066498a39963708cf8f1cd46fc35045189f253b807826abaee4852a3b61dfb89e8ff00edc2bc98e6fece3c4f9d1848a0dc906c3b54c3f3a5c980f4ab65a3c945bce18dfc9d6a8964468cf8d5e96380f356a1c4681cc8648080000006387da5849ba2237b372ff0c6fd6b45359cefc3c6a58049bd5478b85938476085aecf0e11061d2229d49354c71faa51aec5aefeb87931bb18ee9591cd2ed55ae1f48d926c9ee530f7b368eb529e1d3942c35c4bf5bfccd8d090675b4ff020dad592a871874f41223321a8fefbea6560d19a4db8463e14edd4b3be4611e20c2bfad797e1eae6fdbc9262eaaf679505c62e488b37f075f14660a70eb928d030507fd4bd222793f9aab2db23b87b735b475da75cedeb41d30645181d506b38a04d33054e9ba195a745f63ddfeae3fa7c5cd95003fdc20d30671d1fdb4d6bd2d857d99cc40c2268ad0355e476ae3db67364755060b5ba9fe2860605c8ba3f4ae35208a865e54110979073606bcd4424afa3da55fc62c10f02987999ed3db0975f62c02cfd33e2bc259a97f3306534b516b6097ab66c788d296fe01a61cf471c16bfd5e514c8201f079703192c8823ac1590019936d7ae14e607d67bc348f8fc715efc09c9d932536978c5aca05465ad61e0ab0195b0d6a86700748e78c63abe607f8f545aea89074df7261bdcdd187f8e357f57990aea1eb", 0x784)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x4d}, {0x64}, {0x6, 0x0, 0x0, 0x7d}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
setreuid(0x0, 0x0)
sysctl$vfs_fuse(&(0x7f0000000000)={0xa, 0x3}, 0x3, 0x0, 0x0, 0x0, 0x0)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[<r1=>0x0])
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x84}, {0x3c}, {0x4406}]})
r3 = accept$inet6(0xffffffffffffffff, &(0x7f00000007c0), &(0x7f0000000800)=0x53)
r4 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r4, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETZCNT(r4, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r5 = geteuid()
r6 = getgid()
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000380)={{0x6, 0x0, 0x0, r5, r6, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0xbf5})
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000000900)={&(0x7f0000000140)=@abs={0x0, 0x0, 0x3}, 0x8, &(0x7f0000000740)=[{&(0x7f00000002c0)="d346ad672cb3452bd05f987c4f97e7afa28107012164cd981b7a", 0x1a}, {&(0x7f0000000940)="ba23b154f8ec54d89b6ba29eb1e9e38d19ae849c3760f1625af2732e80d2a65b68cd4bf270da6c573a79b7a91aa0527add4f350078bfcb88d3bbca0efee5cd3d10b08eae5a5e37eaf036381fbea6869385c10469eab17f5991ee5ad130eea3fa7baf897e3e4a1642874363a61f3f0750f718d4dc547eaf16746e673a71992536408f0198461f7f1700000000002098505010cfd9b35e25775fe2c2c2c031391134c6605a3a9d1aa12813297847fda89aa23d40befb4cea35639c54b0f337a2e6ce75dc4a1ac16300"/212, 0xd4}, {&(0x7f00000003c0)="19e1d9cb9a2df708ea7d2f44b5799d59c058639faae892c239a08ef24657d53feec627e03c1599b3eeba90f89918e89e3c39c0603de49619e00907c37a15674b047386de89f2f2dceb0d97fe177b53a857118d66a92d00b59c147d107e9381f9a89865c28038d422f533f30fbcae48266ba7b3ae2c6847d00014eee68f3917e03f5950d4339d785685e8", 0x8a}, {&(0x7f0000000480)="21ebe9bce6e917aef268730432c5798500691a740429bf34ea5a81938c143cc741dc14c52246ceecf4d048c0b9b2afa2ce787bc91d70beaada27f51c4770acd583041aae097ca1703c07e9b27ae8f6dca18d2f64114de28e9afb7c2204903705d325a8bcd71a91b28dce64161a4782497dc7", 0x72}, {&(0x7f0000000500)="40a12a969b5604d69bfbe7cf5d2134ebdeb1edad0eddb576205d1e1c53a2934b75e73ad5576a99bd2b8148b2e5ff6a3bab3d277e4ae6b917927e254d2da044c0e98c544ac60afffd82ed23fa36a110786c6815dd8daf214cdd39f2f79129fb9c6d50deef4468754c4003c0a2d7d9fbc9c0eac26c1177bc86cd", 0x79}, {&(0x7f0000000580)="17402bc8bea9c80b19784a0197e57abf015c3916575e33cfe7117a51d184dcb8cd28cf08bc29ae00d3fc7f4e3186aa43b736d210f878499f9aad2898a08ece17f36ffec72f42a1bb87218388c4b5ceafa260ba819b14257888b91f3ec1f054e6ac075435876cb1358b8ff16b4ae97951177d8170afd2aa13ae3e917ee2698ed4aa791f", 0x83}, {&(0x7f0000000640)="742ae6b85a4beb2bebf0af4da29408946f57cfe50389823dc724a39931c7ab307f301fe93faeac7146cefd2d1bb274fd42c9a7e5883cf087afd7cdb94617a3d8f06ca91b455194e1150dfdcf224cc10bf022e3e2858d09b52fd9855082ea4407960ce6782c519193b2905152825e3b183971b27e08033f865a96e2c59f23aafcff5b2000324d7f66b488215379ec29fb8d5f25f98152c412fae10b05d0c0f4e05126acd31f1250e8ff306125395c6f6556afb66f11fd5faf192c3d5fce38d379b01ec18fe11ca75c3c163d5e17b2765baae98c8835716e54eee58fe1ba0b56ceb19dce0da1a65834123fd068", 0xec}], 0x7, &(0x7f0000000b40)=ANY=[@ANYBLOB="2000000000000000ffff00000000000004805cf2ef899255f4d0f00f21e810c2cd9b4d8569c01803f8f4cc017b9381ef3022e058391137753cb4c8c91e04c8058bf01510932e707fede6f2ba691dd2f1e741e2b86c438acc41983352df03dc37b4b9683737817c96f9c860fbb3fa59c3326e047a8483b9fc9e12b07deeb4c1aabbbb68adb39456e93e4c40092aa08db13fae29f726b3a9b08258dfce245d0e0bb6c04085dced800c6d348304850ac8c0a2fffcc8a45e55e4", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=r2, @ANYBLOB="24dc0a1a2000f8ffffff00000020000000000000", @ANYRES32, @ANYRES32, @ANYRES32=0x0, @ANYRES16, @ANYRES32, @ANYRES32, @ANYRES32=r0, @ANYBLOB="000000002000000000000000ffff000001000000", @ANYRES32=r2, @ANYRES8, @ANYRES32=r3, @ANYRES32, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=r6, @ANYBLOB="3204009b"], 0xa0, 0x1}, 0x400)
getgroups(0x4, &(0x7f0000000000)=[r1, 0x0, <r7=>r1, r1])
r8 = getegid()
setgroups(0x7, &(0x7f0000000100)=[r6, r1, r1, r1, r7, r8, r1])
msync(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x5)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x1, 0x0, 0x80000000, {[0x200000, 0x9]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x3a, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xf0, 0x0, 0x0, 0x0, 0x0, 0xffdf]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000580)={0x0}, 0x10, 0x0, &(0x7f00000005c0)={0x0, 0x8})
mknod(&(0x7f0000000980)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
close(r0)
syz_emit_ethernet(0x4a, &(0x7f00000000c0)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd606cf6daaac91fcfbd5b0014000000000000000000000000000000000001"])
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4e)
setsockopt(r0, 0x0, 0x67, &(0x7f0000000040), 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x1, 0x0, 0x0, {[0x0, 0x4]}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
setuid(0xee01)
r1 = socket$unix(0x1, 0x2, 0x0)
sendmsg$unix(r1, &(0x7f0000001480)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x48}, {0x44}, {0x6, 0x0, 0x0, 0x3f}]})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
write(r0, &(0x7f0000000040)="1a296f059f3ab5e3", 0x8)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
minherit(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
mlock(&(0x7f0000849000/0x1000)=nil, 0x1000)
shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
setrlimit(0x4, &(0x7f0000000000)={0x0, 0x2})
pipe(&(0x7f0000001200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
fcntl$setown(r0, 0x6, 0x0)
r1 = shmget(0x3, 0x3000, 0x0, &(0x7f0000ff9000/0x3000)=nil)
shmctl$IPC_SET(r1, 0x1, &(0x7f0000000380))
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f0000000540)={<r2=>0x0, <r3=>0x0}, 0xc)
r4 = msgget$private(0x0, 0x658)
r5 = getpgrp()
msgctl$IPC_SET(r4, 0x1, &(0x7f00000006c0)={{0x384d, r3, 0x0, r3, 0x0, 0x100, 0x7fff}, 0x100, 0x55b0926b, r5, r2, 0x3f, 0x8, 0x2, 0x200})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001480)={0x0, 0x0, &(0x7f0000001400)=[{&(0x7f0000000240)="927bb8eb12ac6cb4452ee7c1991169007cef440527fe8c5fb99b6706e96165493001be6182351a32fcf4986e2e79e43028f1f68796e21b268ebdcbc09370e62fa4f2f6da5ec3bf8cc84705f372380a51d191f858d2eb251a8c426d267d90482e73767898ef0dd4ed87473b557b693ecbcf257920fb19449819e55f0c12b577daffb7f368fd51471e74f71530c669d5a0f00d1a7cfea33fd7d700c9f859168e554cccd993798691be080144658de8162bfb8d9b2331cedbf9d310316bb8cd35e593b8f9e43e08eb99aa426233206c370f2aaceb47ac2a5e08847631a4a719c0234ee785c6499c6f164e4c44c701c69ce38930fc2ce4f29267a3d60a050d0051add0c88a141aadf94d5778704f4c9d1deb8347e1c3350e6e9a08015aa2f8bb0f9a0184bee4878899894671bd706c4b4cca370c68be0b107cfe7a6c57e79e11b974d298c30015b6ab9fd74e10f3c5d36b3e33cb0584d7b80609ab8280a64e6f51a7e5c044b66a6deccefc55d4dbd63ada7f10b58f0174a512a870b753ab53661d4c99fe28304c900a3358ac574a581fa0ea4d7383cdd25e89e4c475db0e018be13dbca9c7f93d1c73f21365149f7adb853abda7cf643ffe25776b7938da3faae5bed930fd5da48683046f2c15f0969e2843c151097ab84d813d9e3d161c29e96a4d8a005716d1e4ded4d449bd233a263fdd069f8421d7b07e585c30ed3f59a171f6ad355c30f80e3e306a1827cd116830313d7622d9816ffce1795f086e7827d73d11236c2e6f6a960a447af11f821f522b08139d70e5e3d76791ce24233e9951445fc44dd9b8fdd5211af0f3a1aa3df4c481333b3faa1d8776db9408eb1e9043febaebb71731446c4b3892441ab7b86fcd9729c56cb4f7cda66a645bbd3d3b830b80ce1611378ed70e9c092581243796463e8f85bf1268b798f1236debba3e4f45dc444de19192ea512bc9684e470bdcb3cfb50199e56337ff35818c912fc209f8b13540bf7214ecacbfa7cfb4447e76b5e45250f876e91f9deace3827a50e7828fbb5b906cbff6bf534b36edd85fe8a5a5ba47d56ba11bbb5d59d411639c97b8caadf32fda8696e8759a1967b276fcc6c8448588dfed40711419b84df9b474ec3e9375a60e16c08a623dc9328dbaf3602eafd6b1959ce731b80751e98394343a1138edae60d55fa26b4d59526eadb7ddb745cf132cb4a6478f725f600a1fbeac58d0b4756eca46adeee4be355883b7cc6459bafec269fde754d92343cd6c7527152550e4df44d80131827d4c8098905bb17e98d2221b38d527af3f0c493deab69df018f9b75be078ee7011733bf257c8210e2c8259a979c96d4ac70b7be98b2c273136b6c07d1313354095d62cc0a421aad4036b46484bc5c631253221a61ca0fb30c839c89801d774a4218904746f8c69b7e828c6454e42f6a10c26f68663c4604229ebb732d04cf1be93cbcf1be033934794472fccde0da00927cd6260d2ea59d81a5fd1482d9763e568c1b7dc38a0f15f2b113ad62a9a7e43af8bc1146fdc00342133dfd37a248643e9858bf7921494097b0b1968bae5756a215eec75bd12a11d18ce941097bce911ef20869d12c283bafe5c8c5e886e43ef7ba5b7c60798deab2ca1e57bdaa22ef3eb2b75b28f35b84f0c5f430ccb512e23c4a12615bce647f65c2461ff6d4a8426d959b0187a32d9c1a403ab8fc34e322efed36c30224366ec8faf96907e3d42918c7286f2e664aec39801f63d5779c98b0c69b9d4f15c3e9c4516d433c9259668994785ab1a7099f39cc950541190e9bb72dd2cf968bd50be960141f4fa610f9d35e097a441a0bfb05bb831faa2c3951a77b6c7ffbb7b56e7438b686e879a081d046c0549cc212f8ed7a1be6331fc345767e3129f6ec85c4cff97d121958de5a52177dbb9ca158658198ce4fb9ed78e5a655d2213b05cb297395581a051208f9ec1d3d5e94c85673695f8bbe491bbc6a15fd8d4fb795165f950fc635ce8eaac0d120950bb262638a55cb24eccad82b980ed7ebfb9eca06ea5fd7d2492bf26ae7776234eedf3642c170978e90846fccce386e3696ec4d3039e3702ddf23db232f0645767140c969d21db42099477d30665e559defc29fb99ffa86756de3e98fc36c78cf24a30a2dad77e77321d18e0c4d574d2756061a860a2ed541540ccf0010b3ebedf84c199cb7d40efbacbbe67abd698a1da22d078d1f6771c35551de8143ae094777c11565f6a5299d3a4345ddb3d4f361211fab761f7b8a049c03ab6a065d07c57821bd8804475b5ed2f749c3ee1cc180e4f7e2a57a043c0bfc538908612262469025743a446ee59125fbdf139a75c2434a8d3a66d81867730d97c05913f6317b51d9c7aa0810a8bd29de7573e4b88e31dc087366a59e92c95b8f123d69316796651de80a9e9a116e5ef882490cfc662a90c00947144d5ad8980c5200eac141d708c31488e01eab351565071a1ae962c2d6eacdb31b9d3b0bdd446a6c6f1816b0c6e31e24a965eeb64223f6cbcc0e7fda2af2088a9270bd076af679d17ae2f24eaffa33b6a14b698d3e88f0f2f0fde9aeef1d838764b1ce77028f900f938491fa4b0877f3c6a087d499cced76310949a7978d85ff2947136cd9cc9cab66474c4d12c9284aafeb118f45b51247213430d65b04f7c971645c7bad780742974c263cc4ff03deab36875cb5298ddaafdd8fe24247dbe2ae7d246c7a9f9c61e3e13517df0d9f335353344ea72d5b05cf91285a723e5e8b7a028ebbd782bcd0c5fc3649cd7d61d9e73e6de2824907f8e3be0388d26be8127e33f22b7370007c352a3ac5bf59167db173d81821b3c8bd16e84e908f9ed9731eefe5e004a40fc8ec3a7f6d75394e5808e6c128d6b66f027b4704114f90e90884813738e2358ddd432e426db96135c9fb2afe4e8bdac2480ae819893afc2b6c99117848b119ab1ff835745beeb88655a519d267be7fc0f66de82281bcebc217ed4d15f4b8825cb0e3a9d96e4aa2ba327611bb343e67d9e902b46fa3532c503379852c659b15b522fb0d21292275a0ae9d7f5f083793f3c4ac216ac86acc8d23451cfadcad94aad089b92506a357acd188d71b5eafbf7d4f35d64257368b83bfaedd57d05153976163d656a07c2fc762d3d66060b22045abec877d1b41b022dbdd244a69350c51691b460593ae46a52d8063c5182753839bf1f86d8452f9bfef9518e6e6b0e10ddbb34764a0695649e284c7ad0fbf7917f4817a6c5b76e89ff4f79bb01cb7e9f4d933e2de692e389b87a395ef9630647e9adaa959d32ec3a849165503975adedaad094bc0214d4f850b8b45478eaeb933c5095af74a45bc6ba2f3e125b0ab29ec300f8398e864da0f6c7cdb035817ae4d28be62840361522c4da33318262ab73853cbf5021f9e87e3f17ee3126aee8c07a61fe4bb1146a6fc05606bf76298c7a386493e1cb5d1e2bf54c3ccf193a99a0adcdad360bc942956d6199b44f42b4c6be396822455215ed533aac06c9d86639ab99693adb2a474f838c72f510ed9ba63dcf812c3411a458ecd1264800093c3e60146ab2847db892953aba5d03fb7c3b4677c270f4d91f22d1342ca668e130be7dd6ddba2d6dfc8e1c3e5128284917c49dcf1b7c43ad8d7c18632647d3265bee2d898366ab0d4174b19bb96c0620630e1b3c4146f182d454059bdef6e244a239bf09558db4fc027c2a3fb70264b33bd811f0088c86d8493157489b409fb661b2b67781495e8794ee393edb414e58da58c905701d03d65971c2777a181a400ed00423ce57e3d1cc9f37c02f72bc48499abffdb943faacacdfca11e33d3b6e259be106e6bc0d8a093f9aca1d9c1aa15064aed85b341aa5e4e5ccde7d4d0825c05356e980626778171914fb8d72608b23c8695c6b7bdd027452396b8273888a1cc7f0ffe62db2e0ea868d4ea0c412d943d9370ba6bb2cc9f885ab12bc6037fff3412ea3ebf7f45d815b1dcf09a9bb8e1ab6d4ab4c37c1f06a62ab599457235a5d72736646ed305eafbb39faa7cceb33eb727fe1f9dca19f67a44cb", 0xb30}], 0x1}, 0x0)
ioctl$WSDISPLAYIO_WSMOUSED(r0, 0xcd604404, &(0x7f0000000080))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000680), 0x0, 0x0)
select(0x40, &(0x7f00000000c0)={0x9}, 0x0, &(0x7f0000000140)={0xffff}, 0x0)
dup2(r2, r1)
poll(&(0x7f0000000080)=[{r0}], 0x1, 0x0)
r0 = kqueue()
openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000)=[{}, {{}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x205, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1000300010005})
open$dir(&(0x7f0000000080)='./file0\x00', 0x8110, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x10, 0x0)
execve(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt(r0, 0x6, 0xffffffff, 0x0, 0x0)
socket(0x1, 0x0, 0x0)
r1 = dup(r0)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000080)=0x7, 0x4)
listen(r1, 0x0)
socket(0x0, 0x0, 0x0)
listen(0xffffffffffffffff, 0x0)
poll(0x0, 0x0, 0x0)
shutdown(0xffffffffffffffff, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
shutdown(0xffffffffffffffff, 0x0)
close(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)="919b5c5e5c069d14288be166", 0xb08f, 0x1, 0x0, 0x0)
r3 = accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r3, &(0x7f00000000c0)=""/166, 0xffffffc7, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x7}, {0x2d}, {0x6, 0x0, 0x0, 0xc000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="de2bcf583dccf1e393eecf1958d6", 0xe, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x35, &(0x7f00000000c0), 0x4)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x2, 0x812, 0xffffffffffffffff, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
r1 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r1)
socket(0x0, 0x4003, 0x0)
recvmsg(r0, &(0x7f0000000100)={&(0x7f0000000140)=@in6, 0xc, &(0x7f0000000200)=[{0x0}], 0x1, 0x0}, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0x9, &(0x7f00000001c0), 0x4)
syz_emit_ethernet(0x4e, &(0x7f0000000100)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "50a8b5", 0x18, 0x2b, 0x0, @local={0xfe, 0x42, '\x00', 0x0}, @rand_addr="b2170f45f3616d2b40404240bbe713e4", {[@routing={0x29}], @icmpv6=@ndisc_ra}}}}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3b}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2c}, {0x1}, {0x6, 0x0, 0x0, 0x996}]})
write(r0, &(0x7f0000000180)="5d526a9208ff69923a366b51f0be", 0xe)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x1504, r0)
poll(0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xffffffffffffff0a, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
read(r0, &(0x7f0000000100)=""/113, 0x71)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000002c0)={0x0, 0x0, 0x7, 0x0, "2e1d2678a602c25c86a2216d0cb0a096ea7bdb06"})
syz_open_pts()
sysctl$net_inet_tcp(&(0x7f0000000100)={0x4, 0x2, 0x6, 0x12}, 0x4, 0x0, 0x0, 0x0, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r1 = socket(0x800000018, 0x2, 0x0)
r2 = socket(0x18, 0x1, 0x0)
dup2(r2, r1)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000040)=0x3ee, 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x2, 0x3, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0xa)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt(r0, 0x0, 0xd, 0x0, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000240)='./file1\x00', &(0x7f0000000040)='r\x00')
mknod$loop(&(0x7f0000000100)='.\x00', 0x0, 0x1)
open(&(0x7f0000000080)='./file0/file1\x00', 0x0, 0x0)
setreuid(0x0, 0xee00)
r0 = socket(0x800000018, 0x1, 0x0)
setreuid(0xee00, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000340)=[{0x28}, {0x7c}, {0x4000006, 0x0, 0x0, 0xffffe000}]})
r2 = dup(r0)
pwrite(r2, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x1}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, &(0x7f00000000c0)="7343c9f13a1a65a5dc41e7000055b9e7c7e61e80a00111703c8ad427b38b2f8aa720381c97827991a34f07000000aa8428b796be7c3b0dec5cf124fe01853cbb22f62d6fc07aeaae1028cd4c8391f916efee16d809a6a0b0b5f077d55f10bf208af9f63d4fed291214c315c5459032ba78cf06e3e6dff86da9ac815ad539c221d0d8894615ff18fc678944d0f65ab486924ff505c7690ded14f9ca833b67bfd24a41b9c62e63182ec537052a6472066ce21532deaac00c6ee5dd8cca0bd9c132e66331f794", 0x0, 0x0, 0x0)
r0 = semget$private(0x0, 0x5, 0x288)
r1 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r1, &(0x7f00000001c0)=[{&(0x7f00000009c0)='L1', 0x2}, {&(0x7f0000000080)="8d", 0x1}], 0x2)
semop(r0, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r0, 0xffffffffffffffff, 0x4)
r2 = fcntl$dupfd(r1, 0xa, r1)
readv(r2, &(0x7f0000000280)=[{&(0x7f0000000300)=""/220, 0xdc}, {&(0x7f0000000240)=""/24, 0x18}, {&(0x7f0000000400)=""/143, 0x8f}, {&(0x7f00000004c0)=""/198, 0xc6}], 0x4)
pwritev(r1, &(0x7f0000001a00)=[{&(0x7f00000005c0)="b538190e70edee30687fb806269fa3832d4f6bebdf262e561efce804471c6dc50398859dbb4b0791629ec5a6058ddb5eb431aea2af82cc8b86135b89ab39d6a644948ff8c1487d77f1c735979413e795e674747f0478b90a810d0fc8b1bae2a78313fbf95074ee4fae13570a3d0463c0f2feb61972bad3ada76ad99801149fecae20f564638ce7d89bf4f54f3da57a0341f29b24f53ff53881cf4455aa7457d0a3e5847b66f8900112e0b49ea303d617c849a7f53122d53cf2cf9ef9c3a3f91a1d1c61caae19dd9a0450d092284ddb3540e990b9c776", 0xd6}, {&(0x7f00000006c0)="c99aec68e32dcc68755fc69ad4b744327ceeae873d1057c0a1446630f55c75bb2c10cc85e2f76d0f9cf96804e72ce04ec36bbf07407a5d0994a31b5a731857c0ad3a92dbbaa5043f9db91d88009e8c28e2311c262c0b8b29451ae6b9b8e469dcec08a975bc750f4e61f2b37d16a9981243fb12f4a8e382867b18453cf41e0ff807a39219d13e5874d6504d9e1ab1ded1b2af79901488721492b678641a90b144fb08754b4c4852d163255aac3721c5fae1c53172c14d7838c5971411180fb5760f744725be2e0c1d97b8b0a9edff10eba9b493120b69f2ba3e1a56b5a62930982e", 0xe1}, {&(0x7f00000007c0)="c5c47043ea73af88e1271e4ca2f3682df5bdc584f4771843f4252fb6f6f8daa8153801c5913c15cbca421db0d3973b84410a71a6f64394b99f2cc26e9c665bc6f9c91fd47369d452eac7cbfa369f20cb127372f7eef09eb582c3c55ca1e7c7c9003c5768a7409fbc6d4ea90dd7643ddb74c879641ade040ed14ac396cc0eacd56a6f71ecd5b248e33d9fa976eb58fc6c522d35e54fec65c898d3", 0x9a}, {&(0x7f0000000880)="a82552ce7e98bba08988332a24868fb10ec932bd556ca7dfc12f74cc70cfdabab6f795fdd98c0a9a2f71c46cacf24f0aa20e0226abfc837f06d4dc79c3a8d8556b706b4968920cc7be585c8f8f017daca6085a0d5314819216bc2962f8801efa", 0x60}, {&(0x7f0000000900)="f4b3ed05b5376acba3f6d743f03929cffe85b66efa04e6695b8ecf89263bf57ccde26ce57958dba52bccc023ac45357f221879f360b63255c002c4ecf52942afdc12ae562a72a98c4aa13ea3233a", 0x4e}, {&(0x7f0000000a00)="1091697c689af72727b99a992f63c3c42c918e59b89fb7921cc7152c330f14a4367d40f861071683c93073a8c4cc20094131eec4e179d8c5157c3f847a96e9e6c22f13313ce23e3fb25bd687b7dede63bf3cd2deb7f14df57922cd9820b066cd2c882c5fbce3fb4b81e533ca43ea84d8c1950961fc4a2f135998b20cd6dc23a08902d12580e82aa8d588de58b318cb7836f95d763384eb8512d5ebd36860184ecbef0e240dfa4c7a9e8809203fbfda9965c8ead1e0b4d7da40ca32cd74027ad767c77fbc2d9c855b553bd24c668576185057199454b01d8b2089b14e58adb7ca451b317d8d6af3b4e043cf98f36bf4ad2f3611ab115f2054d1ee6da0883c4d19ceccec70812750b57a3a6d8eefd5ce230d547cdd2bf07c1e46745c8e015c588089b1850571fc93dcc2d2be0e07db244ca5fb0e7d0e2dd258a079030be3480e2a7ae72732589a1905888418a5e0418fb30597a37c068ecc050bad9275a782f052d5b72bc27c6d7310bad5c3b45fe9b0a224942f6dfe78514af58fbd97071f5e4d5225afe8650a1b79d6ef64c0b8ac47921276d8836e6326ca524b733755206af8ae1b494adcfc1dbf8f3e3694f9bb2f97e7878f98ac402571012d3a17ea717c30050da8ac00548147aa1b84e9c409ff53647162ae2ef108324da545755a69ac2214d3d72068302ae033f9df1dbec3f71f5c51e3efd5eaf982b65def76a04e7848c2b0c827683ac118d4f0e9df06d7310006d48224db4ccb478dbd2ba7b7bfd354d97c4e81c3944c0bedcf9a97837e293bec6f2adee016285f9de67f034487fe3dc6995cc5af63eed51d8c6e2c4d932521d8d1129adce5e4ba432ba853af3fdda0456e130b17fdb6f4bfa546c2d96a461de36410e46233008fd8d002f6c4748ee61f658c907514fc1b7a693881642728a07e41e990613ed031f51674df57a29209c225a71466aca3d92628d32a1533832a3580fa013b2af196e15dd756445f895b1f13e64c7ca88ca75635f77d026e4a3dc50f6fa5f62f68dadf5c275bc6e8aa89563597e297599addba2cfdf98d25e1e21b724a0ba453b63cb61fcc477dd712c30f6b76f0ea2265980bbf7e2e357be7de11ac81a9dc3f1502e207be5de280af98c88591b0c061a72182600a912c2ed724abf310a331e11f6d949f1df3453a486f561eedb849d3cb09a36d7e736f39342d73d7446d24cfb8c0c38a2df7699bacf4edc44293c12d9a5ff5b1b997297d63eaf4c14b3efc931bfc681b34481e31ebd0cf686981e1f510c26296df09d2e9c133e12ee29bbe486d9d947e741cca7351a388a673412a7a4358b60ebc5520e65597a066a2ada232b1ae86b89e7aad068559e16143766faf3bab6ca4123640f502aef04e80e4c5cdf3813713ccff9d61bb9c6374f7d4b012a93eca441de0070d7bbdff2eb90546be24ed292e645219f81e8ffd65bc2707c213457bedb3c602ad6ef94a9f0322300f9366c645d668a0b2abedecb95982fe5d87bf1d66f08267e1d313092b34c0a2c893f5c6c7d564fbd850ebf8821378d45bc2536673bcdcb73c5aa5c0390dc2a34256816792e41fc1e781f7dd3bffaca8e5e88db0ad6a33cf9a9bb3c970e91de08d74271c20587156fdd13c6743430fd3499f09cb03b3b7c6486dd840f9474a980a55aea9e29a2390d02688cc3ef392feac1d8a2a03abc1afef942a759a404f260f3587835e616b95fe879a06b472b9aa59277054cd7a77e5f32469bdf246a5aa6f3f137c6e9bd6abcedb21137ddff6f8c2e96eea6f943126c6013add4c04581381439f553f2474829a173de3a6421fb1ea974a15fc5979869d225410f766f851f858f4a35243f8268b098e0d30eb7fcbbcb6e99cbec3d9d7eaf070fc5b5f4fb26638661c44c2b66b116b2949a10dcc1a3ca54d22c3c0628adcba6c5e428eb8cc8a98797233ba3ab4c90069103629f7e508db6140e5c1199aa8c692a45fe5dbecbde7ca946e0a4cb37e3264385a20a97e88aef68826b284a02d217d2f77e774f0f3b733a8e9f6f8f20eb7c4f62db2901d6413f81c2755095cc5fe996011ac1762b76ce11558fb145c5ce382efb72e017cc16e2b383df58ae9f8e97b5c52f36f4cc6558f8c881599a9d0c4f3d22774279bbb32a1aa5b786dc06c02bd50f5189d3e65607eec5f8b5ea4776989e0518e2fd357d9f9ad1194445bd832b8a48554e55263981e03b47606800cf604b6546ed5a88ca4c599b58984ed2839f9dd8e14b14dbe3eedfd68bd603a928753e4f3300f615ba503ab3924cac4e6cf6a5702a6548a3e29cf7c8616da5ba96cb8d11b6a957ae0d0136626f9bde723c34a0813595a06796fa1a7174d61cc4595b24cfde1e339737d043185f1712e87913c8f6d1bc271117e74be5d8bad5cf019190043f0bbd0b3461ea765c5ad354db378fd03f056d374e57a8b004616f1debcdbd55fbd54fd4a441c8e808bcaaef80facf2d8d50e8ff1e98b4b90ae4b88bb3450bf9f61c6b8eccf21933f40dbe216a904ed19710c31f169db83447f093ee6f4cffb4487174a5cef8a8197203b4d38a1df2420a1b53f09de5d570f7d2943e272386d163efe3e7ad4eceb7e2b5bfaa475b4f6fcf91945e7a02034358c3452c94a4c76bd840d73362c295c919ae3263bcc65a0e94c4490a5fafa43cfada764dc24b2119a22583e21893fcbd6009d777dfd7db03713bb8ed3d47366aa1e1f48f0b1d07bf4932517bc630b5f6af0244bbcb6920eab4c934972da9949f33b5c839ab1dadd9d3935ff175fa1387fc0168a175c775c8efbbd46692cb952629f797735b7c4af27fbd55eaed6265f4c1ae1bfb771603bba3baa12e514d88b99cc3f48e05c4807128153973de871b2fe154e79a93864c7a61452b9701f780a8a3732ea9ada9f319faa8f7145e98602d94edcce3c8054e0a85ceaceb63e05e27dab7d9731ba909652221e76ae34169a4d9a67222c5dcf8b60858bc5940961740017d52cbf9748dc6a2495d477884a7caca24ca055869a428e5bc372d3b41988e8f03e5b90c5acb62e19209b6a9ea70cd714087f9695c837668075bcd63f8cf441dd80a96d81b3d11f0dbb4833986e29ef2e0c4882ea8e5f90a5e1e356aa60741cca2ab7d3109a853fbbaeefee7685d26dbe0043530409dd84f1c37329e6dc274f591bbff7cf2d92c624769d177d92e04c1f879c3b80bfd80882014ece4f7d3eadade626556abd5e1d9e751d8fe9465d7d0bfbc6da1cecb522d4f36515b2a7cf3dac713f92c338a7b862e506008fc7ded0386d809e12d8a321f57d0457bf758649ef3fba8b1856b49efc593412d3cc856e475975a7dead696b61b10f603980a749982222567c1c6eed263e7794b59da0b5dbfaecd5c384614d7756863d530c8ffbb5e133952482dfcaa5a51f625e36b0a9233e77fd6feacd58e3592979c7f6b78a0157eaf2cf02de7664beef91c32b0a1f9dd8241f2278b0ca3852a2e854cca3b52481d592cf604ce49d1817108006f20e00f0668438b184d2834c53eae9b955801523dc670516a300dc30e5fc68b2b228ecd098bc748065a682b349728be33cfe6c68784e0cfbb46b2d99beb1daafe72fd2f9a3dce77969aab1e7e7f729333a99d98f9578b5fa89ef58d30bc5b6ea268f5336899b091fc7a375a84e32bae12a7d49afa23762c1009ef7542cda7d1fc1a02aaff72bec5311ef480f3e48eb5e6652ad903ad2efe6bfeabff8eff8d1eb7ebc9811cb5bbb44c3833fe0ae970dc027d8e7c594c775f5240cdde89b70c356e5ddd7841db4ea052a3bb4735b5d6840b35c26ba70328e053cef765a075d90624351f1238448c15a6263012953f9f2382b22f5ca97bc67fee2d4600cca2b32d0794601c5d75c2d11efa7a710ebcc2909efe24d5b802c31efdf9df21eb7f192f691698894b9712f3c7c2475990725895cf390a9963e1a70459f7361cae6e1e1b4d36b0ab4eb62dd6b8b594c11ef69114eccb25a8accb4776cb82558a0d65e7dfba1f8991de29a81927e7bb3ccfcf2a2de3a80ab122d58c99be6ec1912bfcbc59a2d4a05e6ea23f63aca82a045237b58bccd6f87510462e94a71af65669f288f49db714d746b57bea50894de250ea19fa1292a2ed7a38e9363fc256845e13e5eeb905f456864a79f19171989e1405be54a9fd51d9210350a9ea32794e8862e2aedaefbb69c614edff7719ea186ba40de38abdc73147a0065304789a179be099693ed071de1a05a40c94c5a2ae64a1d38c2cc2f27355bfbde0cdc944cc7734903dab21220c558ef16fe47c3f71c977ee8498193c1738689730eb2e1e2642acb50947c9168ceb561922110d7c504b23bae846af8eddf46bac3294f29716422706f035dcca2c00e0565fb3f3b71ab8ac017d27bb61b7684e7b125925bac98c865a45507265144cfa1d82f41689f63854baf029999b27d35f61eaaa6f81c5ff7fd2b40eba0682b2eebdb53f522974d5ec85bc9e2b501e3ec205148946167e9ebc48cc6307d61b773adae5b0771cdb6789c1f8969da0fe89fc669a0b70e6eb5bcbe7ba7ebca8514befc891c8eec37d331718d913f5c8bf06e51349fe13096907e8c2c5cf544ee3f6f364528b3876a5010ccc9be6bd5cdb1a8155064d52942c6570e731e9fa4b445e9450009479108a824db8ab736d9cc90f7e7f553eebe1dfe7a28e12c9a72b646bb47e3d671b40b365e1d037bb6715bff8a1d5f75f570e39030e50c28cb6cbdc43c74ccb2d0f6395d314c795fe6ab2823ed3ff44599325a289d1cb7a93c044ad5e8b4242e4908f4b3be52c298e5b0bd905e40e677a2a333864566ff826da44e9de01262aa04f3090cc8084bf7f2452a35e0a55894784dc26eabdbc4fcc17cd2e37e6eb3cfc4482d64db8da3c646dc908f103d48954720848d823d374dd36fc1fa0b9601332258549131147a4916731c8977a746faccf352d6fcc1bb69028dd1f6472df73d33c056a5311d2d5687241b92cd0399414d1bcd4c8e6fb8591eb13d3159472e677e5f0a82213c5813f4bd36c47af4e3a431dc7a8fd64fc939b2ed752edfd7de66f18f44d83f58608d080d87314620772b5f132dca3ff5b0f183d353fb5acae6abcf20572dad68a8d957f2a118bf1a81a8471dbdaca734272c7107708113033e3c09904ef1a78665383cfe77d15f5cc2d7102c6d1e7e0ee63e294d7e73f1f162f64e68a7e5c1f3eb697b5a849457a33cb1d376582c7c380ca9c49506383b45a197d0e9f94d2fc3ae3fb3ddf357ca3094462a71e0f6ca80e2aaed98206030e96c8afde6840ad8313fd0e785ecf6f12a41a82b3eb84b8e5c5566e97e9e4a7452fa3b1c6340b09e588a9d1e0471bd1799768b9e9ba94169bee35ed3e8db8f3f7c5ea0b31bd245e8aef822332d9addf7c2e7e676ec15c7419de343dd79d2f4c00410729068b0ac0164e008963edde4801476c097881fc2656cb555d1f64cf7678c492ab3ca643e9608b31cc65085fb10aa2ebbd512e6ae3b2086477d0c388b5ec024c83a4fc5e28832ec5d053e97676808562cfa268fc91a89b200abf34433184332348b1b62cb3c828a9cb0844c08905166586732f8d8cbce1cb6d5381fc00924550ca80dd48978f597f354ecb9f378308b3bc92a12976ae66ceb44fdb3db0bef5edd4e1f259e488c18c75aaa287ea117610e2e306dc970d8b4ad743523cc0853d5459b354a8054da5447ab13b47ef0cce77289dfd2eb8c62544c1af1e7f02cca683bd4ccff85cf0d842a3a6477eccc8811a6c138a955831ffbcb91f428a673f9f60a3fca7e47dbbbe6f9b6f902499c07fedc", 0x1000}, {&(0x7f0000000980)="ac6dbccde396673525127751a980ee2d51e092c23c79ed891afa00ce97468016f2ad4f61d1ce0481de433f12ac1be09508ea94347a05b4d0b27f416456a5", 0x3e}], 0x7, 0x7)
r3 = syz_open_pts()
dup2(r3, r3)
ioctl$TIOCSETAF(r3, 0x802c7416, &(0x7f0000001440)={0x201, 0x0, 0x7, 0x3f, "64e904fbefd2adabb1530300000000000000fc00"})
writev(r3, &(0x7f0000001b80)=[{&(0x7f0000001a80)="4eb1aeb06017ca5a90282da872318eb393bfc73b9c8c1dbce006424a1fbb35219843f004f59684cfbc5c093fd3b36de7b063ddb009901f3bafaa05c27538ada6308592f4b82db40de08c2c5fe6628b05c20c5c4896ec3d3c5fbd0505ae2c8b361be80c6b0a39dd9d9337999d9393d34d5c673dbb1c1f3d463d23af7b977e7ca444853804f5787d3eeab9f1901109d7471cc13449e9ce25f39d3877a361931eef0a79d8c7e8a7cf14e56dc6d817f8e4868f9ef72330f6324159d471741367c5ecd6b552d4e025f1bef1331a394c5afcb506198f7c9b8e0500e2d47663b2f95acbfeb204067d920f259157d303428e946bc5e3a101ffbf795893e8365f", 0xfc}], 0x1)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f00000004c0)={0x7, 0x0, 0x5})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x5, 0x408})
semop(0x0, &(0x7f0000000000)=[{}, {0x0, 0x1}], 0x2)
sysctl$hw(&(0x7f0000000000)={0x4, 0x1f}, 0x3, &(0x7f0000000080)="c66365257b939f5ed57f2ed3c50e7d3d3eef46057882b082ff964ae825aa66b0000000000000000694b08fed6450b91ee2f06ca1556de8933420", 0x0, 0x0, 0x0)
geteuid()
sysctl$hw(&(0x7f0000000040)={0x6, 0x3}, 0x2, &(0x7f00000000c0)="5bdb9d6ee88b1a7ca6dbba57f280e917a3dd53fe902d0f2b096b4e316b7ac6b2ea150e8e5b64c4d07898c70e3d75c67852f9731e0c239e741a5b7e0c930dfd99b4cd12aaec5ec98112fd03da316e9ab7798059788a22db3f6dc64dd1fdef08fa8b955b852f967930cbfcea7025ed10911257face3a8cd09af232af4f56c500286f04ae01871224d603c280bca087f6343aa49d9cb4acc948bf9d3cfe528da49016c079551b0c70424ab4fa9850f39c17685fd73591490091ca759261ac7ec8930f0ef8ef8642852365a6824522c661c9b48bfb57d4c2e3e95170a5b181c6fccb94d7893742f3ee27d18d0888174fa6a8", &(0x7f00000001c0)=0xf0, &(0x7f0000000200)="5eb1dbe596db121e637e8ffe65aa1214706d4062e61e3faf03f2bd21e2a33966ab251c865949a5ca2aed3e3865fcc5148d7ddb9c47f0835fa9a130d0dabb95f2a314e0178d73549b488b85849154a1af9c27b1269f1654ffd641b24578e56f278200c2fb55980f0bfc3410a7f43e8857857d2f4966ad1b", 0x77)
r0 = msgget$private(0x0, 0x18)
r1 = getuid()
setreuid(0xee00, r1)
r2 = getuid()
setreuid(0xee00, r2)
msgget$private(0x0, 0x146)
msgrcv(r0, &(0x7f0000000280)={0x0, ""/4096}, 0x1008, 0x0, 0x800)
msgget$private(0x0, 0x44)
setreuid(0x0, 0x0)
msgget$private(0x0, 0x2)
semget$private(0x0, 0x8, 0x0)
openat$klog(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x8001, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f0000000080)=[{}, {0x26}], 0x2})
socket$unix(0x1, 0x1, 0x0)
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, "7e000082ea095d792ffc47000000000200006ba3"})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r1=>0xffffffffffffffff})
r2 = dup2(r0, r1)
writev(r2, &(0x7f0000000480)=[{&(0x7f0000000140)="1517dfd954481b40cd7d422c4483cabd36de28bba76c4f71d6ff6979b7b9d9fd7bbbe7598be45c86a606b8540033e50ec12f2b3e68cab8caf018332e25dda84de8cdf48366974850bcb9a499", 0x4c}, {&(0x7f00000002c0)="7baa726584f534308e88f02dbce6d9302e5c8207f6b496a5d71819999aec4410bd79b4fa1030b0d2fd55b5b92569d3055ffb680eab51d896e3b94cc3d7afc7e28eda36ad4c7212216758d928c5830bcabea88334796c86cd74927a257babb1e998e977c89b5fe2eeddffa0e84f1cb44e6cd2f534f63bf208079fe631633df73d4711f8e2d7b2c81f9aa7ce2a71a3f77ee7a9f326db32a879216988bc7db9d722229a349467c8c56be7eba78358eaf451703042659c2f7c508384ac4313895005bb8657edf49c", 0xc6}, {&(0x7f00000003c0)="bda717428b9e81b0ff77ef26cdf65cfb8085f7ca9693a0d160de84", 0x1b}], 0x3)
ioctl$TIOCSTAT(r2, 0x20007465, 0x0)
setgroups(0x4000000000000010, &(0x7f0000000240))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000d40)="2abace18112030b738b1958303ac3fc1c384e67774d4bd49660d81fb4e2e9a19a972ea508fd17e3d0a932587a0980ddae58d2709a47c4986c8e74ed0d85edc5aca224a44bf11e4e1a51c0f", 0x4b}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x2d}, {}, {0xffe}]})
syz_emit_ethernet(0x4a, &(0x7f0000000100)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a4f7ff", 0x14, 0x0, 0x0, @loopback={0x0, 0x2}, @loopback, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @mcast1}}}}}})
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffbc, 0x0, 0x1, 0x0, "6fc60900e4000000001200288000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0x0, "00000000000100"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000005c0)=[{&(0x7f0000000140)="1fc19b860bdcc1efa1cf1e4a28975eceaf81f0f9b4d286b943ca297b942ee85291e79d30280a932ad1de517ad1d4d7fe6edc19e6394ab04b0dbf60e78931e9bca1810d3077fdae6abb5ee9c78218ff425aaf4ec39df6783a443ac5fbf62de3c7a77681d00f4cea37c63166ed833e324e12ec89c981d37d8c5402e0f2cc59f4d95cabc3fd86403de94f8d0a5623e2037652f1bf619837fbd370344779e5f26afbc9abb10e989234524c5d797c17604f07e3651f992feb4f3376d381", 0xbb}, {&(0x7f0000000200)="8475ed4c2c4bf81acf1209a687bbf3145ecfffa5f6a55e3ae5ff10afedfd975aac71916917899fda0f40a172079f31f6e494b48bd793f3249d74369ecccd48fd6569b333dde708c7b4", 0x49}], 0x2)
writev(r0, &(0x7f0000000800)=[{&(0x7f0000000640)="f5e13f2f73411096ea49f8d87832a8e114ec216f32a90f856b943bcd31fa0a5870dda5f5a10697c047ee0a3c86429191b4c8c2cdf33bd07981b09af79decb3fadc27c1a647a94c85ecb8a9ad3ec2b4b71edeebe2ba7d16269698d7d7409d7c7fc8b752e8cc3fbe51", 0x7a}, {&(0x7f0000000840)="c8828c88328192bc33388945b44e7630970aa7af20e73aea885fbb1bc71ecbc1ea82f2ada4bd29d5c41cac7d199ef013d0fcbc0e9d6bbe2e262613190b83f3ee78f59485274c328da29ad86340b7ccfa2ae7b4226e2264418183b07f7a854a570699c6f254a413085830739c30981341946cfbc07c1dfaf83f41ddf6d25546da165c22583b2b59916f6cb26732b74be32c80a6e538a6525f171d32136d", 0x9d}, {&(0x7f0000000900)="47742ac25c2ef6c569b9ef13420ee01c36efee7efd5b19ef20776fb3db342e22178e5fe6bd8e7612a64649b0032ef7bec40ea97de2319215f315ca09935baa48ac5a54b9e760b0db833894d49a8f02ba99337c2b5a6f212f260c52db1dd0d500f534b563531f0000000000008e3f0c271b5dd2dd5ead4c87103386c8ea167c74e0dae68f853b232d456be242f50e7c82f69ad8977901237ae7303ad468da40d4c72507007df9e5f8a879", 0xaa}, {&(0x7f00000006c0)="9964c9b4d7234f4c1c4e7802659369caf539421dc4571971da150f6ec1e4091badd6e7278588987ffe1df8bf", 0x2c}], 0x4)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x45}, {0x45}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{}, {0x34, 0x0, 0x0, 0x2}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000500)="d000ff1f0000000000001b000008", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file1\x00'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
syz_emit_ethernet(0xe, &(0x7f0000000000)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "25e0c3", 0x14, 0x0, 0x0, @rand_addr="2bde4f698b18e9dfe0e20fcf0fab9479", @rand_addr="1f16df1c5cee1e9d8df37dc0fce660ad", {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @empty}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x100000000c8], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x8, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x0, 0x6, 0x2, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002004, 0x0, 0x8, 0x46c, 0x6], [{0x4}, {}, {}, {}, {}, {}, {0x6}, {0x4, 0xf9, 0xe59}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
socket$inet6(0x18, 0x2, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)='D', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x2, 0x10, r1, 0x0)
r2 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1, 0x10, r2, 0x0)
sendmmsg(r0, &(0x7f0000000080)={0x0}, 0xfffffffffffffdaa, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@random="fbab00170840", @remote, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @empty, {[@timestamp={0x44, 0xc, 0xf9, 0x0, 0x0, [{[@remote={0xac, 0x14, 0x0}]}]}]}}}}}})
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
fcntl$setstatus(r0, 0x4, 0x0)
r0 = socket(0x10000000002, 0x2, 0x0)
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x80000000002)
recvmsg(r0, &(0x7f0000000100)={0x0, 0x1e, &(0x7f0000000140)=[{&(0x7f0000000180)=""/66, 0x42}], 0x1000000000000334, 0x0}, 0x0)
execve(0x0, 0x0, 0x0)
shutdown(r0, 0x0)
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000240))
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x4}], 0x1, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0xb, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x48}, {0x40}, {0x6, 0x0, 0x0, 0x20}]})
pwritev(r0, &(0x7f0000000200)=[{&(0x7f0000000300)="9ccbb0ea13c4fff8b82673000000", 0xe}], 0x1, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x8000, "00000100e20300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f00000005c0)={&(0x7f00000000c0)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000000180)}, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x7124a24c9c995e05, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
unveil(&(0x7f0000000340)='./file0/file0\x00', &(0x7f0000000140)='x\x00')
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000680)=[{&(0x7f0000000000)="276b7c593dd382e6855aa14a620c17eb72a393b187da72f5c79ac5ea0f208449df6d6e47", 0x24}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x12, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000140), 0x10, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x300000005})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
dup2(r1, r0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000005})
execve(0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x100000000000000, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
semop(0xffffffffffffffff, &(0x7f0000000000)=[{}, {0x0, 0x1}, {0x1}], 0x3)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000100)=0xfffffff9)
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x2}, 0xc)
r0 = socket(0x2, 0x1, 0x0)
connect(r0, 0x0, 0x91)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d0633635acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00e1c8b2ca3ebb355769", 0x4d, 0x0, 0x0, 0x0)
semop(0xffffffffffffffff, &(0x7f0000000040)=[{}, {0x0, 0x2}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x4, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000880)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "c1b314", 0x0, 0x0, 0x0, @loopback={0x5}, @ipv4={'\x00', '\xff\xff', @broadcast}}}}})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmsg(r0, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000400)=""/209, 0xd1}, 0x0)
sendmsg$unix(r1, &(0x7f00000008c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000880)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
r0 = socket(0x11, 0x3, 0x0)
shutdown(r0, 0x1)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000002680)=""/4101, 0x1005}], 0x1, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x2, 0x2011, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x45}, {0x44}, {0x4006}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)=ANY=[])
sysctl$hw(&(0x7f0000000140)={0x4, 0x11}, 0x7, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000280)=0x10000)
poll(&(0x7f00000002c0)=[{r2}], 0x1, 0x0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "2404000000080000800000000000000069f300"})
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x180, "e291ab2a541f561db0d2e90fe11ce40861a2b5f7"})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000080)=[{0x30}, {0x16}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0284459, &(0x7f0000000240))
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000001}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
dup2(r0, r1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443a, &(0x7f0000000240))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x10, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000200)={0x3, &(0x7f0000000000)=[{0x24}, {0x6c}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000140)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @multicast2}}}}})
readlink(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000180)=""/49, 0x31)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000200), 0x100, 0x0)
dup2(r0, r0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000440)={0x3, &(0x7f0000000400)=[{0x3d, 0x80, 0x0, 0x8}, {0x4, 0x0, 0x0, 0x2}, {0x6, 0x0, 0x0, 0x24000}]})
write(0xffffffffffffffff, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r2 = socket(0x11, 0x4003, 0x0)
sendto$unix(r2, &(0x7f00000000c0)="b100051660000000000008000701000000000000ceb1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257690000132e27dcb5d602000d7d026ba8af63ff37422902e4fdefe0952e89f21de070c1f5ab72c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002003c88c1cfc044101b5496fe00"/177, 0xb1, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000003c0)={<r3=>0xffffffffffffffff})
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
sendmsg(r4, &(0x7f0000000000)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
socket(0x18, 0x2, 0xff)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r5, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r6)
r7 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r8=>0xffffffffffffffff})
getsockopt$sock_cred(r8, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r7, 0xc1084425, &(0x7f0000000240))
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x2, &(0x7f0000000100))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000002c0)=""/187)
clock_gettime(0x2, &(0x7f0000000480))
clock_gettime(0x2, &(0x7f0000000440))
mknod(&(0x7f0000000180)='./bus\x00', 0x4, 0xe2df)
clock_gettime(0x4, &(0x7f0000000200))
clock_gettime(0x0, &(0x7f0000000240))
clock_gettime(0x4, &(0x7f0000000280))
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x20, 0xc2)
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r1 = open(&(0x7f0000000140)='./bus\x00', 0x8000, 0x8)
ioctl$TIOCFLUSH(0xffffffffffffffff, 0x80047410, &(0x7f0000000840)=0x2003ff)
sendto$unix(r1, &(0x7f00000007c0)="e76986a152684a2466cb21b3e94f96d3149e359b12511b39aa285abd441e0879c76688743459de42e52b54eb72e5c643ed60cbf56242db11540d2bb4d529e75cef05a861ce56895a5d9d38c810adb6ba04aecc6e67601cbb8928e92e29d8c2ac0c2a22942ac5423a3cf03617fd2d1ac29c0c9001a5f44e1ffa278afc", 0x7c, 0x2, &(0x7f00000003c0)=@abs={0x1, 0x0, 0x1}, 0x8)
clock_gettime(0x0, &(0x7f0000000400))
r2 = semget$private(0x0, 0x4, 0x1f6b4e471c181030)
semop(r2, &(0x7f0000000380)=[{0x1, 0x4, 0x1000}, {0x3, 0x0, 0x1000}, {0x3, 0x6, 0x1000}, {0x3, 0x4e25}, {0x0, 0x9, 0x1000}, {0x3, 0x1fd, 0x1400}, {0x0, 0xfff7, 0x1800}, {0x0, 0x47b2}, {0x0, 0x6, 0x1000}], 0x9)
semctl$SETVAL(0x0, 0x4, 0x8, &(0x7f00000001c0)=0x7f)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x10000432, 0xffffffe9, 0x1000007, 0x6acb978b, "0d43d3d3d33f0800eb0000000000000d00"})
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000280)="e5eba711d265cd4a6915a540d2739714f8531ecf959ba2a879feaef4f56a173f42da2cac363f4c2ba46c5bdf83e7ed9c1f6e7a5d5a603b7edac2dbcfa9ccb833e1a33409200425c2879a83a9ec873c095c11b570cba5c4404aef1831af650ab3aa9a698ec660b952e3c35e0baf6eee7557f778c576f122dbd860ba89ccc4098a010d", 0x82}], 0x1)
poll(&(0x7f0000000240)=[{}, {}, {}, {}, {}], 0x5, 0x7863bddf)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f00000002c0)={0x4, 0x18, 0x29, 0x36}, 0x4, 0x0, 0x0, &(0x7f00000003c0), 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000480)="eb2f02000000000000002c1f0febb3bb836986ef45b1e29628e7d1917a35eb7e1a0e41d78fe425aaf127570dc7d5302edfeeda533b7027e6fe269841547ecf7fb2787585d07f7e7dc1fda71aa5e4c6da75a75f2b73bb499e736e880961039bf9e095642fd4e96ca694cebd6428e051a59d30c3a32a38b370eb97635ffde485c6543235", 0x83}], 0x1)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "08179c7d9234997b52006000000700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000140)="4491125e055171c507d4e19557a30004856f276a74bb36778525298af8f7e8245df1113208474bb2185340f8a3116927e7776de444c14b4233243a47aa8c7779222189299257a1a295db6954f12079becbf00ec4ec9a0f2212ff07aa4bceae07b4e185e783a97e180dc9e7d921b6722788dca9", 0x73}], 0x1)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000000c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
setuid(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80286989, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000040)={0x1, 0x15}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
r0 = socket(0x2, 0x8001, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="89028600ff"], 0x10)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x84}, {0x5}, {0x6, 0x0, 0x0, 0x7ff}]})
pwrite(r1, &(0x7f0000000180)="0ba1ca0cb64c8023cb5de762d8ff", 0xe, 0x0)
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0xfffffffb, 0x0, "7e5a0f2bee0000b511900000000000000001ac3f"})
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x443, 0x0, 0xfffff901, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = kqueue()
kevent(r2, &(0x7f0000000000), 0x5849, 0x0, 0x800, 0x0)
r3 = kqueue()
kevent(r3, &(0x7f0000000000), 0x5849, 0x0, 0x800, 0x0)
r4 = kqueue()
r5 = dup(r4)
kevent(r5, &(0x7f0000000040), 0x80, 0x0, 0x419, 0x0)
dup2(r1, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000100)=[{0x28}, {0x74}, {0x6, 0x0, 0x0, 0xffffffff}]})
dup2(r1, r0)
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "d730c15bf4ff03ff2a7beffde4000000000200"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
pipe2(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
poll(&(0x7f0000000240)=[{r0, 0xec}, {r0, 0x4}], 0x2, 0x0)
close(r1)
pwritev(0xffffffffffffffff, &(0x7f00000003c0)=[{&(0x7f0000000140)='Hh', 0x2}], 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100)=0x1)
ktrace(0x0, 0x1, 0x40001e34, r1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9c00ffffff"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
ktrace(&(0x7f0000000000)='./bus\x00', 0x1, 0x40000c20, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x60}, {0x1d}, {0x4000006}]})
write(r0, &(0x7f0000000080)="23295428e2fa906bdf4ba040352d", 0xe)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x9, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000a00)=[{&(0x7f0000000e80)="26e97b62abb2071089d6404d58089c32d70b1e6ccac67da263aa3c9c41efb47324e8b414b17d01b4bcf117eaca252036f7659d2363fd333cb6d06601b86c7b263395794276b5e9d32cd816e2355b275e34e5c0170fb85ffcbe7c65b95b2765af2772ba254f4f7c7f578ccff3e06b25895d97f0a4c203d10e2b877a99a183c3bbd159789eed06ef672dea9eca5fd06f2d10b2b2dfaf6841806ce6c0d51f1f980a78f10281c408fa3211e97f6fd560581011c1dfbf4eca04478fb7737f82b027f66d9d31596770a5493a511c8f4e1d7d79083ce893bfed363fccd57100149b58018d5ba28bce796d1e00199d043633bfa21c9cb57f0cc5ab4ce99e41f3402524166ed69b4466750cda451ea267cd5f164b7f1779bf271508ad2e0ddb3877de8141d22f017ac8f0b43233b16e610ea9abe9df433ab0fc99615a05575e94d0448781cc9b8cd73bd5794fa0da70b154d39d82d9156647d1bb13f9bed8ef9f00e6895528f90eb455ad876dcb9f7f537be999bdf588cf76dd718d11d33eb1ac81c54ab20aedf2faa1fdd888bda9e2c5c313ab26e81c1f35d771cc74c46d41a6939ca5b27faf4a95001106bb5177af3577a9f756c3fbf19f2b0035b39ffca36d8d732e6d1a383ffbca358db25f49a4f9f879f44a20810fc04b7d63b2b882306c9daf386fc5206effcc5b7df64a0b82c457174b6fe9e1e914df66bc38f315fb8906b12b031664e591419f1d1a743d8999ddc30634e69c62758c1a8f05d715655e06247ad7052801e49001e88b0d37483cc5900cd28311ce041bb74690e2296b3a90f31fd1bab463b400974198df67636868b403a9b29c6d64fec37cd1e256177b356c98c46c557711f9d8cb9dd0ff2647a79d1960604666d97ad24feb17083bc042305cbda6a99d81f4de264a03ea297bd2a2fb4117a71056f7d5398e5823ab9a313e3adebb625f96ebc045f94dda507701bafbd99840c7594e600dc537b7300b5ddd591fc97d2141eea8808c406ff083d9b3fc8c050adc01", 0x2d4}], 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000040)={0x0, 0x3, 0x1, 0x8, "346e8440c0a6d7f28b1550c987e78e94756854a9"})
writev(r1, &(0x7f0000000380)=[{&(0x7f0000000080)="c1ce0b03bf3b1d1b67be56e2a2a068eb9a72998036d57567361ad904a4c647223c356d670b6c8f129a4744794aa5c3dd3695bfe32cee6b9c83164bde865a00b3adc58a9f9dac99b79e571d22d56a78d922cb6945872cd930db5477c7b9293c9c88f950de0e958086e4ec802939b35d9617c7464fed99269f2f73d5835bb7ea7fcfa8283b529f3653d135dcdd3c9c692d97ce582454cb8e63780d305f1c945754158ee4f7d5b533d7bb9753234ce0fece14ee2142630f14d6e02014a8b2de83332eb94e62a15377faf6", 0xc9}, {&(0x7f0000000180)="9b159cee4d9ca024f2f25f3b44918fe9d0397fa4ba7112723d026e13c6acd1bd3be2635fc3ac479b2b6328f31c97139dccabb36fb9d8e0ca8e4a322eb7bbf53cf66294128cdf5f40658111bea26d0029cd356000dd9fe6dc214bcc1edb02f4de2d707a93fc964cac7653d840f65e0d12270c8eedf766c909ffb181439964e51ce0ee6e76e94b3f2e8fad47d6eff1047755d5dad3cd994404135a34a6c9afd2f21fea16e3fec0daca19f62c5348924e2f1d7681cd7e56caf4a3a2f352233d79798b95844028f6170f26d4", 0xca}, {&(0x7f00000004c0)="ed2d8368f47ae9ff7c5225d807c7b4cca44bb7326d530269d897bb11da4494218fa8609369b730501c498b06ecabbcbcc80533cc994bdab45eeb248283ea1cb037cca023bce81701a81308894ac386785426edb95b003c2a5e4dadc284ebf4e3f3fb907ef81843446b19dc17cf9b4bee4dc860f9f3a27c62f990cc7749aa4d354c91559f83b967703a62278dec92bfa3ab9ff226244159fb5afe294aa242225e1e225132fad31cfdc27132555d91e3d33e760956f13c19b38d74807cf24910e66913336c17b73045f54efe6aadc8af9545d4a717bec1754566197bda89c16bc1a3b476b9660c52186b27e636a808c69a8d0b99d4e153b8623d39f7e30f3f76696e1b00d48c483105b295b5bcc08ce61deb0f03eee0ce9db568c2da51b238a209446b05e16e0adcfcbab7a65091602880a2b9e213f1ffcef7966c36252c8bda2f924b60eab6181a82dd9a3dc4b9d6bb528c15083c8dea7fe55acd67d418ae3c94a9b3438210af15ca0e552263f55ca68b371747707dc62c3265ad7deb2ba67b6b03f67a3e7c26d135b15b311fef43a16d8b162d6b3b720703105f551aed28d7503394702ba6c9fbca0dcc3c2cb8b608ce30db5d34955997c9455e9d957e51ad7ae7dc42b40960f6f2945802cbae2001ccdca509950029440fa435b4807b6b6b1c966fa1dc78be9f142c433bc6a6a79c6eb582e1e733f908e965189e3a0fe9fe24a89f33a563ce4b1e2b4738a96452d9da4f8bfcee4202327c44e1246203977bd7226264d9c8b49b36f96dd63586580152dc378aeff752089cd784d09c7ce5a9bacfd2d65895be7d25c18b0fd9187faf18a80cd7f66ff741119aecc1120c7fa4a980107dfdb776eafc387fa66e9bcc7ae0df61e6b1d51219ee62c967fda7897e363397abb027ad16497bf1b46a96e863447136c2148957da2f20d5deb5090bf4d83a2c3ae9535373af1619e8a0de74a12b77204dcb2ce3f35917bb8ec018ad99fccac05a564cd99e02e321756d31c940fb4347d5355786c3ef8360d04b75fb564d5e32458d4f679a7f72cbc3a0123b0f16bc8896c51ed1b9f913568dde09c806bb9007b4dbba50f6cb5b976dfbcbf2bc3d9ab1f525e27d82d893acc32bf3753942ed82f713a6724827cb434bd12bb32cc1abc97a239a3ef9025075475ff6f11aa40653f0f1fc4a6b74c4bdbb79a5427f36c568a7b8ac4d20bb5eb96e1e198fe91e33a9e0633ea0fcf4a650a8819ef29c8fb883fe5ecc542c53be13d158a63ed5bf4be54255bbb20ed3a6091af85ed00a20ef16ab9cb0f6a5adb5b216e1f57117f590d121792da4dab8ca5e99978cc55d81ee4369c7d4d338cd7d7ded014e1a084adebaa35d7a41ad02af3a1f841dbe38d1131231d71c790b03b0600da113ad3543f1bd85525c28bb744bb480bf8f1769e2648b9d8000576babdec7e6b0c9e98b43ab2db43c8f199cc7c55082469412973697ee3b4a3552193b2af82dec4d796ca319dd66ff85d97017319677714cf44115e7f718f73dea82ec5d7fc3c73d96d0c3314da6f2e1395326458b3bee079360cf26e73a24eb0434d08c62f8ca62a36728d5f90e0a87f76c4c029762d271f36a38a930972366711d41d5cab2ac181acabff21d5cc712688b26c0944858b1a891b1f6d6d53aa8c44fe55876a596dd078ad9f2c66e405a2d1b6638c58c2f60a589c00267c890706eb574a48b70a617306edd916c41203ebfcf704d23ec89cdc9fae8abc56b7970932f10cc1638c60e9bf46c69bfc3598532c287f564caa38976f1a67ab3d20fa93758dc4a29698acb84f0a30e9f75f1d3730681c4c768c4256a140263f8f8e0208273b48354119af32e0e62f6f1448823d354fd5d9f655eb68e74afebef13bd8549c9f37701e2ed6da2429f472d85685fd769f4b82120d6024dad8d4bc713c4c49a32667cd36944b60d4ff1d7dbc3f575e1d08f68fa5ec3bc1fe102d9cb1ef0870ad000f9c6c25548aa3557bba0433d96ae99e984219cde5f45dc76896c40f7050c6bdb9b08f43f4acb4c9704e6c7d185133913a313f04ad95d3d5041997e1a8e7be87a20a6e3a6be4ac3bd69e3cb07316d635bc44d0c0f99b4c0276190ca0d756a128e841f7b93c8a38845cdee1a272b7f67d519e60f969718bce482145b734f2adf783a0b45c015a16c81789c2dffb483ef223ae4c0f6502805dee6776749c50c4184ffb2c8b6de2db4f3bd07567322cd8523eea2d92e592cff227428c961012fd7055d6a41790bebd1656d71416fe641bd14a8f8c07104d684a72be5ad85bfca8347bc9ed2eabf8e9058ba0004a2de6062f8b7ff19fd574e44e6df1226f9478f1dfa8895e7ab3c29c3fbdf40cba4caf721019670380f4a89bf1d4fd4988127189bf76947e8da7530090dc3305e21305833b066d7f6fbd3aa9a737f3f0ab2d5bae4c6759bdd53ee4085124a50a30612fbc8eb128279549928836ced010632784fae382677f9252a6373b244bd58dd750191d322cc772c3b59557e1ca229c2ec7a64bf4cbf7cec9d22f1aeaaf155b6a06b91183dc63aa6659f616ee9402d9e6e69f894adaa8ad826be06964a2797a509947d8fa7ec3c957fd81560b82880c60752de51392669e7c94e9868e237814f93c3b7cc69280dcaf0fd924c194080eba63f1d073d12828bf5f4799347a0d781f9c64c480e2463f754d490f451b2e124245214e54dc89a0d2b65b03bd8249a49cd9c8c7a9aedf49d39b1d9aeeaf2fe50ed93ea9902785ea5b18d29074ad53cf859910d54188a1eea2722bd54d5d6f423bc63bfaf84d01cad09229b724f43347f94a6494cc0d35f9a72cf435fdd68402b10e2fbc1b1d5d8c1ffce803e0d313e9ce11138325ac355596b07dafc1e84b45deae1dc2cdc160bc1f787f15964e373aed6258d08d23d09564ada723348290be33189e6b1bf216cf9f0295dca51b4475f0bd1c5bde1bab9238ce7e6672715350d101c2201ae227f0438081ee24fd350ab456e28f83849bc00ff86321b7d3dabd179733cf1d8afe9236b5c7e0ae9463f346f966157ebfe08d2b4fbd3600aaeea9c8d3b4a4cfad6db412409c84efba82603f2aa75038726f6f3392b882563a1859c79a5a1e9e4db6700890b1647202c62e492aaa79291c3ea627b363554ecf85bfcc3dcd8c9f2eb74de78b77b7755d978d3831e5c76af9ecd73fb4d097b84753c7c0eb2a1d1e2a77f9cc435ca229fa80c0a2231e49131e9e4234bbfecd013a5b366b57c09a27a429dcb8974149d2c7da6e5b61d914304d211df6c92fb71996f964968baec03a4b090f897cfecbd83ec2f1edff974f8343975052173954acb58e7bfaeb90aec34dadfe487ff992a257ff3ab0ea2ef45cd254fba5e8d863a8578953c26aa2e74b7b5365a22f134dc0cd2f34c3c0b8c79b713564ec3cdcb684b16e5ae9e682a0f0275d62e91bb911d88a254f3300acaf238376c5b1a3a6a81777b6f2d55d0fcaf2af00ec041d1946a5de4f0195f30720b12f2a657fd1b583087a0180823afb2e5a5144edf57f5c97cf5c37a54c20f24e7ee56e2c8b209accc2a36fd9d259704aa3f49abf9d9ac88e1a01fbf0a189a2d3cbfd745b94d9de4b5159df9f99e0dcbe70a908e96b075c636a54f9e0cf8c2d0212c982dc5ed47c9e97421a0624876cd161320801a0107044ab4ceef2bf0b207add6034a02c8da2eca25c4173f01daba26cd0dfa75d4a1b2a37c0af5c8d1e7ec64af8a344beb61fcb8afdadddd8ceacfcc5bd9e6474250e7eb1ef9266b62b4b300b2c10b803495257d0618b525d523bd4b9a240d477820971d1d9276f07f64250bc9bb9963167723b933bdaf650f530b7814b73ba563391db954c727b59b7dc834f8e7f10e4674ed11a808b56a8d9c214bbbfb1612b47c264177c7d5889f85079de4ade703db2a56d42adf60088f0d5b1fbec99807d27b58a7745039ef489a9a8bd1353ce42461d1ec8226bb38afba84111827d7cec463e504e277818ca445a05caaf4f20acce5cab32f004c61e8d18cc30ca076a67d68bf1ec3edf52381a5ed7cb4e0937c680a8389c0f14afddd9184ea55569f0e3892fccd7232cf3971e56cec0fb1304634c072c5492f71aa45da8967a74d55bebffffe293655bf93fb1e2ed39f6db852da3dc2815bb4428f264eb757b042b9a24f2052a9693536941f4a1bf184102a2eba0e6a6375045225aa8a505ae6a99f2062afa248382669be07ff99d99d64719a32ea73a231e9044d60c9f280e92e4113ff7b50f9e39c54f2751916d95e114ad9df80e471585c7574f0c9940913bbdc78be6c48de767fd291135aff94562a24b2423cfccf23e47e8ec0444ac47cdff67eb6b8cc2ed0733f1b6921c59604be7ed4ea1c946ca5fa80a53c562b4570f9d44a009662bf77a9550e700db87a19ef6929b02935ea54c08a1abb854f2cc55fda6c28f1eb24a87ffc63980ace43c19a0fecac1c48116a6c2c6d473ccc767c9e47e1bcfaf71715e73b696d15fcb600d412430588e9734ad6b80809c83441f31f88ba5ade88764f4ae033412dfb8e5afb45817c7eea776d8075376fd970e9fe7f86fed06db1e66e6acc240c97ee00e51af48b825f6851231503f5a9d7cbf5b284abb3eb1ad6929b5755670113caa441f5ae75a925f8571299c12a153264b0fe6c58c2f9769d0104ce4cf5d816d55dc5ad1ef2f0e1267b9c26e4e2e41fd2144b92cf88eb5ff650974b991a0c897842ab6ded3df2a3c9753786ddcdb78b6c81921ad03351105e0b347b831f2a43f40ff258fcaf74566a8c9fd7b1e51ccc2c7644b1f73ca508d1aa3b8eb0d2b5561120610244fecefadd6e4a64b8ab1d1c1ce66a9868d45091212a89ecf8d0b3e799776a6fa914c686ad27186148243af9d90956777a8facc5a7ad603ffa39adf645e922879bf1c5de6849689947bec4c51851e4fa0a7e14794c5da93dbae71ddbc6809a2fc6babf4233fa16df1511ff96ff954d3bd8b38b81889ea6e2a9ff14d7aa6a636bd786f1ad9b3147dee77bb6b86a7be7fb97fe96fbce618d482ee1743124e9c1289a13d064da120656f3f7802971bb0765cd81a2922a5ea105e4ece17ef87078bc2c02725f02273a9b98f9e3fe096270ed81edb8439dad118b7953e6cd64cb92dcf4afb33582b667aaef73e9bf4d07caced0ba1cbc35e10c7268c43f1b6082a069643978a2d7f48eb91d7117ff1b8d005881fbb69b29aee18a7f21cde3b321a4e96d52f1e5fec62509923427de2c0a703eb9aaab284624d06d70b931f374f3f9c0a12ab9e1affc6eeb5ab6bffd230a49fcf3c4e2b00211c864fddfc89209d9ebf3e7b69b5d8bb7cad6aa0123ea52421ee21082b4b75a48e251498227fb4dff6a607edd3c75f4d3755febb3c749199d89eb2532647a24eb7b271128a9b147c5772c7d147c81733be202809ebae41a236847276b81824b487dd27b008e6e2956d5fbf050e00be44d74a5fb8dc3ad73abf52470181c48f19e7f90029a0caea24b571f622a12b19aaf46d3b68e864b7e6337eb9fee2651378685b4a0c88abe1d12b5e9c1c19c4238d207f1544de3122fac1470f150986ef36e4027d329fa8f42c7fbee95739f80106035bf5737ac3964453c741dbe634ff03341a829bd781bee1f626ced2e0015b5e5fb82538c865d69b8a4f0fc46338a8bea8a1ebd100cd01e840d8dfbc2bebf1f4d7580101afce578747154c73848bc8714a3c2a65de9bd61bcb2c8f7d60962de72aa2b76847c0005a377e5ef2ba2e3225413709899aa0c323c2425ddea11cdc38c85c3e5d6eec1032bc081ab918dc8df1d", 0x1000}, {&(0x7f0000000280)="9e798164b5522e01e7aeb47d612892e47a978e047792adae7bd169a98a301ea410646e17d616cdaa62c5b87a387c660cb853772a8947ef1df826e01327f875b42ac0aa34408bd2fe705200643ddc79ab20f1fb792478836a6353c7a1dad7ebde36cbdc82a868417263af9a962d0ad820b72704f1b3b47ddb4db73c2d21ecae123e051105f7223024d1f4e50658dd311c127cc523b8ee33270aeb36f7c257644692a8e7e00a21092a75e90a7f1aa3a78fd410275e1edd8e88b25ee76d00d7f5a2ddfeaf061d5edfa7b81d4783ad1e36f7cd3c15c7d1fb6b72a2ff4f87afe01be4a777f0f3fe641810a80c", 0xea}, {&(0x7f00000014c0)="3f6e265796cd177b3918e9bf9b14de8bf182a3ae69f89bee1ea73746ee187fa9f3239f0e88ccc3839762b0b69e08fd5fb372150c76e48d6fd7118bebf0c53c1fe91cd16c597e0ba1c23c000fdce9a3f5608e8fb66a9a3da1b6c4039a7a991cf52a78136f0edacd9c87b33e7ff29e49e110577a3a8f1a524f1f69d34b6e3815157fd379a1e33e8778cdd9598faca13815be0d732d65ca4f49d216a8eeff51eb74f5a23d988984743de2c11e4a222c6b8c0c7df9a6d09ad8d8a2aff4c44c2308feb3acb12aa4d893f57b1359c3e4dc79bf4176d18a6452ee5a0c720227326e48a3c02f2c4f1763f542927b9e0a080d7ac45647f36fe92a34293ee99973b8b535833133a86b05e1a998ef605f4333983c6aaffd7e3328b452250834dba77fba61530c019ca093e26d0d42e6df8002762d236c9a8188cbe989764b8e38ba197d7ba78a29f6910c7b880554c73c2ece1319509082b57b788bba30c69f2611dc9f7628b945657e786dc5fd226155916359b7959315178df0738f1fcb899aec336f007274f4d212c89392e4fdcee5a4b518c3463324e3de0ffa159e45783b1e4827dab57ccac5c6a48f549638f00fcecf1279e3e9a9ccfd43bec4000fe131714b87694805b89eeaa14abe515b0d13488cedfde0ea91e27c062781fc36ed77095e7a0a8398622ef98e5605a3d7e8c0b96460903551d26d31601c5a1773b64d93ef0e6657f6725a70340ce3225c65f385a9af778ce778c65052e5ebcba21ad5484eb2b2df113fd28e5b86d32fb391e7398fedbb3948afde9b4f9affcbece99ffdc785244c5a6367bd9e92bb84e1bd313809a02c5cc36fd16e82007aec1d656f4880aea359beb7da4c489006f349af5fc225ba2b67ab5cf3cf76135ce4a55aa744aae202aab932239f59b4f626817c4039f530ceb3e58a0048f6560369f588b9d569f138ad6205913d5c1b9bd7da45881808ec1c3a440c803060e2310a5b3c8a8a68eb07a45cfd19dbd8105a4cb34d60240f95687aa080f32d05f399deba2e598280a1037db46433c5bb8e9f4ed6907d23ab9690c8910b8f93c3de9fd8b980d6bf0b4fd585d78e6bf7b5d5593dd991006fbf5b9b72d5ef182b0b0de585d2512a40a8b1ae2b3998a36a28e6da67defbddb925c771f456e3c936e14ae0e0a2b5b919a8d8907c6875fef6fe45dd91478bf4a70a414a7a934ed251e7d3bcf96e7b30e9ee3e235e8f6bdad64f2b996a4d7da8a2e1bd1852563cea02019bb50f282457de7c870e386947fd87b1e4209ecb85daaabbd8066e443abbee89e22a41b5ba365ab671d859dddb1b0dc47f5b583031b788e88342c82f18800ac8164c6277005765b77e15d680a98af89e26df536cb1cdede320819bb9738a094e673026d64e3a94930f08dd6f7292bcc198d897ac9fd20e9b43e837a2984243c0d02ce5c711afc54b854c0c9256d134231775d8c8c94fa5d18533bf2fa48caeb57ad5691e0b034cf629a3a02b73daba75c95e4193e599b62da69299bd1b15aa92aae2115455053b342f4b095684dc588de6edb8c8626f1d7a380de14ed6a65f12595c5048941ee4b6709940eceebe075ecaf5195f6200da9866cb0bd756bb2dbc3bf63b8423c6f755939d2a9228cb4689d4ca69a10f5de7cb38c056e848e34c21b1af0d886a82a230e02460d2acf8598145670cffe6f33ffae3cd9bc8ae4a9d0e3e6c79bda07a38368cdc007f0a64c99acf0a9512d6c27d20e3e6c0337479010bfa0b85ae13f677d0e6ca3bcedd13e4be458eb2a216872fc562c3ed676fbb8912c401cd2066753c00d3f04a3c33d59e5f5ca8062b4f2ab7b267c09118138732165522d0145353b791d92990bc488064bf10c688b77e2f12e15fa344211ea0e0ae0fa8186fec671957312446fdf0e1d03832f8b9a1d279b0d2c4f7dc14e618a0372cbfa7389e227a6d4fbee470bacd5c346c9636e24904de3f51e36f300519ca154350c86a8aa2a98c479105a37ac0e41497a2d53761dcebbec688f30d5b5fac906ab46c1f5b2906bf5bb429cbe63ee292fe43f6987d128de47a5ab7aea5fddf2d3d3fd58730d0e17e1e425d06a38599c9a7a8b9f766425bf54edf333c6a0d656450df32b96ab43f042e1c500830fb2ae52c99d60a88beb14fd6550f7689cb9dc7e887446cd8713ea19ebba0c9837d7f096e1c32ffd7ef5ebf429c2062bd3d9eb4abbe200782b4e236c73e403ba20b2658f6a88e81e14137d71374e3044752cfcbef369cd7f113c322e93d12b7b4db627886a655b842fa035d4069c7de3f34a1a16417bfcb5098c4351fd08a33e61a3cb7ad0487718744f4ffa10eed89fd6983d70ad6b541e595fb07e3f20fe20abf8f947384e3ad0340d0501ee04e20b824538ef7a8f62b3d9d101a0ad0df78e9b4ec937e227dcf8e79a33ecc3d03ff7246513bee4f8b8bb761c8fd7dbb47a56dac5caa22569e38880a68474a2919b3c2d141d4c0e32ddcbd5d98ef9279715c08b1963a2fb9804217518078c5850498eb7d1ddc943922418c2bee92733727bb6ee34ce22ef5872a60176b226e39844f13218bbd75e8dad2791025ea9b421c2ac787d86dbf40d0f6c9ffd4c51d8771b4b4a41720637b56a27759111da24f43c1172b10145aa2d8b35f21d826e6d7a582723ded267a53c6bb2d7efbd64d8aa5fbb4dd93fd4a6a420ff5084a2cf6b0e89dbb183191da7d18dac53fd6cd2931ca36096128939d938d3471f8775aab28132d83eb6e9e3c8cf4cf151d98017465de3f01895c8587b04ac9a5ed6fd412e7e5c5908aeb1952265a1cf209e7f3fdb1adbdcdcf7bbdd22531d58f95a119e1eceb4cbf37c19ffbebc029da3296b0d94fcd9474a7e5d876961b8eb9a81c5f778686cdbbb44100ef9cd9dc6b1d11dc2dee50ead7cd0f1c208bd19f53190f69ec2be5ccb94e108a21047db7fa41968a64cc4028d4aceb33662fdf4015d50574cacf7f87372cec284df2fa3aab090403e16d8fa2b0de8943ba3a86df6129a9e872b1c5cc8cb4f2f99c9e0fb0249dc377e286a02800f1465c43ac539eab8c3e3d736eed633852923fbc6a71c5c3d73f116b36eb8ba9e171283a69fffa1cfc17e9420d361289d92d18ca3a6a7f47341b6198bddfc709e564f445914436ca187ad86a3c441d74a32bc19c477b9a6169238fa59c1acce0cd42981ed997e260e6088801ea3baa2651a685938866166a8a9c850bf0cff60c4ecb3524de98c469aa82b591287086b8c736dc816dc1582ccac3262f56fe7081a847c0226c6c68550f5cde1930eec3312809fe6bab1688011b178d2433d1527915f933e1c03c05c26389e0b47e7f03f91580ab17930b9d9dadc598ed3b2b8738cd661be2ce34128c2c82c988e5067b1fcebb1e7ddc7b120a4fbb1d1f9a6d33b0d99721ef23c96fc890c031b066d6b4498cafbc441d7eccaed077d9808f98b4eb11c052808c613bc5989d5c99dceb9f30c001d5a3900c77532d19b93885ce4e3d9aed87baa3eec9a1205b01383d405105f60cde470c4d72b5c6efc8631037f8fb08a1752bdf5670a3ac96193f8e39d5e4125e1c1637b45c2137b432bacc21440c4fe5441821bcd80ee2e547ad1f1e52e17aed07c218aeaee202acf71a6f687210154cbe8dcb3666459c9fd50d151c8ffa6dc299e67d885e713c02598fc7eebb93ace5dc239c81a61da335f97a1080908a3bb5f41ea2e91738591462308112f3abdfeef7413322c546ba0c3c1e7409e73b7ace55b8bcaaa0309d08afb7d9d0dd492eeadbaa4e7cce2c1ab9a84805662782c164b8266cdac4b05f1a25fdf18faa6035c016eb245e94a2b44541a90016d0e1d7390d447bc70d45c99b7ec9297f9c7eba04bc0f29706da1fc168c6bfe525b0230938926c953136764973d88d96540434c534f8a9babf8fff30462375074704a8d475ddf9da51a48e2be01502a1ae2b6563a99861631d0583820f73ff0deaa1d3967af7e82a0040d29437e058de72738456e3399074f79c65dda16335e8ddc153bd6e905086698d0692b447228e1fd5d3c06e0737e5fe9447ac50344cb72150d393179648ab3ec31ba2840e949068cdf5671d9d3b76652c1ba0ea58ba008cbbbe2bc7064038b4e8f09e03c931de48b8f833af1c2242400af8c7c21dfa59d0205522d5687c1d77eea44a7dedb1a58e03d7bf4457962719c273a40cfa47df588c0e1e0d7a9f7c1fc41674d337dd4874f9e48f31cdb788d5439c7922464d0a49e6a39d881998b3c7bbc5d1a3626d93203359a5182acef381ca3279648a45e8dafccf308986c09675ac976cd85f2cf055f810ae0c8e941d86ab3e53e57de3cca4aa3b82bdd2a928e85fe72f2597c0d3e1c440bc122526d4c4124682b7cbb46dfec1e4424b63fe679053fb6253d276b5788e4b6b002c95c79094466826ad3acfdc60fbc591d1728da8120f9ac7fd0660f7e64f27e4a9a2163c2ce1602d660089ce807cc5e33f2826548556db22915f31218e108a0493b9a69d1ea2fdfee51b0addff8521d216e4a82963cb1e50be9ce6130aeeb2f50f20867f5211f3d21ee5cabe2", 0xc91}], 0x5)
ioctl$TIOCSTOP(r0, 0x2000746f)
writev(r0, &(0x7f0000000480)=[{&(0x7f0000003600)="5ba38b79b82ae150f112194594abb8531e35fff706716e484be13f20ab60d4090ccd7ba2b43b2156dd5467a79b6babe5f66be3fc1190e30603ba36daca7dc43c0579abe2ab8b2e833a9cf61386a6e385262352a302d6467b0dbb65e3810772fbe9fafbb70eb6a062a1cfc607e7599e3c5019514187985ca6243f389a5a718ef74d78c91783491d23b7b7c79bde746e323cee20364b66da9dbc85895ec18ded20be009c8a25599c4e7c1088c995b981d433e6ae5974950f67af82ab74b2990daf4f5bec4cbe9ec5ea8cd2e148af0a", 0xce}], 0x1)
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f0000000040)={{}, 0x770})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x14)
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000000100)='^BC(', 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0xba)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc1206951, &(0x7f0000000300))
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x480002ff, 0x0, "003720ac390005005f000000000400"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0xa, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x2}, {0x60}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
symlinkat(&(0x7f0000000000)='\x00', 0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00')
open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="4afc93274e165f78a99515c17ebbdbb1211a54279f1595a96076d2909341313fbf81950986b8560e6b77686100b01dd62b48cdc3349aefeb01f7effb558bf0450f1ae0c75b595e77b3eec05a3b680ad230d0b6cc269430307c7a7534288e846327a5d71360312f7bd40feef263df73dc17dae115aefde9f0246646fc039b9d0d4409000000d7964e6dc289eee0ce1b9293", 0x91}], 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0xfe, 0x0, 0x2007, 0xffffffad, "919000"})
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000200)=0x8)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa1fabe934a1c1e400db556028db783e9f47ff98028e5eb8cbf5dc889b5ac6402a846918dc0e5097d2395a46fb114a16ea036db91fbb61a83ecd8f2c2ea7f4933fa25982129ace7b16a56cb12b1f238d8ba33c9856a0f0809394b8e5c242c2d9d5194e43f3bb1e6f81d5b8e7c3d5aa1e11f0d210cab776b5b7d1fdbcf1330114561137118a573d4e85cf86cc5488b8a844109050b475210e7df7a0d5f3f93f92cb55920c62fe88de5e006c16ccfb3bd743ce316a8baafe6acc43857db894d285d89375a807129d682358f0b93e83ae31f06d1d27b44c1d9476bdff4677c66fa24955a4273eb6bd918c5580730cfc1bf8520737cc0fb8814d963f3de72f8fdf9aba5a9feb1005c08d7b590f2fa75fa602967dd07b4603595e3b38efef0f26d4b2e943e1871b63479d4fe37c597e3fff39addea8b9ad4c7fe00d24d2adba9186a541bd69baa8d7062bd1f9b8fbac2f1fa00170c766592ed3e01912b24758ed0041f441bb7d3737ba080226c380ad7e766eba96399180b18f59bb475dfd1be417c50c0ac7934a4f55b9080ba4661680030f428eafa06dea84f1afcf60735f0cf7ce0939bda5433c36beddbe9b6e65e3a2f4216a530b24c41ca9745311beaacdfcd8c0c5d9d1d85f0cbb68c77ef1626a44bd209acd8f700a7adeecf94a5d30e3c8ae9c0134aa682b96275816b962bb390789b3253ec11c5485ecfd0b3387fb1cde62c22d6707093c53ac902e5cc0e4f363cc3dd810820064528765f21a8f928c85749fca1768571c685fbf3b99eb65eefdff1a232d95a3be6903584a45de52922972d345a12f0feafafd0fad3705c36ed4472b0637dab1df4f4c30e5fe2eaef09bc9c0ad59fb081a13d4bc6e72db22d12ba088f3d9bd0c96ef85fddeaf5eda74bea0f323ae15a0d1496293158d590f65fa01c87ac3e64facf894879737d20d0b1343b19b11c23a3c669b961b808c928399837145d2144634bcc95296a35db128bd780d6fad47c642339d7e2d3bd49455ec0dc893d6a3884e15c48c65837024637f9acf57ab57e6ad5900ae73c69ee6e88b0dbe2d51e81f8a39141ff8ecad5cfbccd3df4532a0c0a433e3ed6a22ff8236f42230f498f3b208a61fef15ac1455b6783b60b28017959dcd75df55ab591240be76e2bdf1cbb575b0df1cbf8210b2d4904755bf4a6c4b604ff558d9be52db6e8a7b790a11b1e89138913d4c946695a2088ea2cbf148e0716dcba54014a58e54f3314691289ffe61784e0cab5e8202f78414e36a7bf52153333f697a382d9ddaae6c7f9bdc5c611fb2568665c247ea950b3cbf32356bbd55d7c98d4093e4012cab15b378026af10e1c7fad4963d97993c48dc42dc30a4b013dbef0161651dc2797770e47002ef8569cefe5dc2a56b9ab1af917902620f3de081dcf31cb9af02772c1297ff819416b9f08cf0ee625abdeb795477cd29524554816d3bb675290ffefed3e33515665dc4da736fe29cccd7e0fb4e70ac57052bec30fe76d070e7f997499d62d42f0220f7cba6f29b3db3ddbee5b7816675af64b9d08f6d6213e49ba5e436c938067c1d04c4d018b60b96eb9d14ca9e1121aed3b3dc9c0990d7859f96f4667b24ef1ac32f8d2bdc0cfa73114c992fc5efa80bc67c0378aa596c370fff69f3811ff1eb38a578c8aaf598abfc74c81b120cbeb475c1dc103bd6b86b5bedd3742f70de64b0da208ae8c6f42d49df66cc30c740b8404e2d9ee202486aaa5334d819eeacfa03b873b9d9f74f0c69bc78b9abd5ca1ff42b82f0572353abcb3ade373db7676cdeb728fb40603d9a5046a245d3a413356a8063524509a79a9456128da1a9019cae11f9bac7718187b4d77fd5cec7a1798987accd471d3be22b98cd847bdad4ebf67824f746bde2b72fe3443f18185289e7b0f2a2d35c89ac4c8e8ad88c9ff86e43060bc00e2837e39158d3e899e34400bf4b33ab8248be2f062db014041d4c422945b2ddf6dae632fd235cff8e7cae38f7aa8d828681ae1e8f08aab4fed29aff66d46ddf60cb5499701464b5e837d4a7e0280af65480134f84a42c68bc2d2b37c7fed5b0d97240029be4d8002333fed58b8d84748c331fd96c7ce3dfdecac736cfc52ee844ca2267d2eba16b7fc930b23d47bf0b5d5cc12ca740747277719bdc0b1983e95c8ba1fcfc7ef6c15a94734cca4b400d048eb0505511a1d9d60a433417327b47f8729140c6dd326559d24db1a079f0fb1c3c8f53f12c586445d1a736d35885fa8ffc88c693cb2a3686edea02cc88451814005ce52e5c05ec2e99bc7943d83df39145b74797b5d30295b5f24dc2d4633776735147d2902cb71daf5991d599cea38e2f15861ff17c30c06b22683e5a3439be67c2dfca36b0bfd3bd8ecf30b341702db4457bcd74b02c2b74820fb93753db3184bff03f645b3dfdeea195400000000000070eec997eca79eddbfefba48adf41b107efa9804b4325125ec20518b9508186a63115f5acb9bf76c68da2d34dd941b0f132674cdf228b71a46967a60069f0e608cd917f5b968f2636019de739bfc014e6fdeadfe3263229092426ead638c3f8ac0a2fcbedd2a116b2937244c15d4a40dd3b940ba54c4406dc517a1433caf26b38f90079e6043a1fe1ab526658dd1de39853f161f267fdfe4ab8b9f5739588419abd2bd0a1d99041cf16f9a73a82fd48d04b9daa4384bb9742905fcc9f3ab9080b062f5275eb8ebf28e329725715c0ba01312edd85f81b954728e053518ae7911ee2c31e75c8d836377670336624ccb760820a584d3ccc13e2d505b507620379784149034ce52141ff633cf0508a7c7a9f2d2cacbd82e7c62ff544be7281aa27e40512f68fd9cfc35b466c829b3fe22f953539c0baab9c4a763d5210a2be94a64a7146dbf2c76a5029949172ac64fa14359d71cdd817c3aa6e0e0073981332102772e2c2d0165a30fe94b1ab5f526be3c21877139bcc9d685d7268a13d06177cb8b0273577250584c90999392f8127c387e03984fc5d75c5fbfcc4d64fa568bfe094c2f8334d0c614b050409f2782448a1ffdeddba77e3e375e64974ff22f58", 0xcf2}, {&(0x7f0000000140)="1ef646320e8298bfffa8b2a1bbfb4ebdb58a48f337ca9e47561be058fd4e0d4b4c76ea24861e96323ae4c7eac08c3708a683ab257cd184c9d35c4953613628ccbbeb6cfff60f99b159e6117f1919e7316d9f7cf6856c6da15f36bb7090f19fc8e0a171be2abf24c70c82a2bf06fbaf76f24b1c3b21ec10acf638740951afea8632359235a8b2cf43cb8511d89f53e0f616e7503c1205f08bfe5817eb8fe01f0d2823d95c7180946aa042f9dd36bbd52fedb9fac62b233beed1f686bb7ce3c7a8", 0xc0}], 0x3)
writev(r0, &(0x7f0000001700)=[{&(0x7f0000000440)="9a8fab2d8eb4a6ebdbd69775a443f51e35e2574e6291c1ff9732f9c18bceecbb4581154806307f3125f36c82a5d7ac0046ac9e2d55a4a97bb982134e50fd90d4038f94584eb617374dc40beaa963af0225b25d93dbf34d21cf8903414ca064fea5969aaa4c53fcb65b89b8e3f2ea6a3b9e3bcf5a56a8231d617050aec12c2a2ee4cc22957b4a80b7a24ec131fdb98d274f3d10d206f89430f4ada7045668f0e2d5f5565f0f346da98f886742c16d1203fb8c1d2c4abb2137148dbd8bc5dd1ec4365519b544cf39f6a5c4d600aebf18a18439c10aed2edc6c6acde29135aa468d6df763ad6753159c1c5a0c3ce3eea5dc07524ef1a0d7bfae4da0eb741194cd7694415557a7c82752fb76a497bc03f49a1d8efabd43135cd8a39bea327632760efed68b77c834bc70cac69fdd46d40314f6300969f9b552b0e313bb49a8a872c7b053b60d1f2df28c16a9981fca6155ed172c774d4647e996bd0bfbf384ddb84d005269ae1b9b583b5a", 0x169}], 0x1)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000240)="e3021d932704150e8dcadc3746e9ec1913fe510a15db410430a9145c5bc80bd0159a93b809c49f858152f439ba5d8141a9af01a1dca7e5e6b682fac7df93a5cfb2da0a794a378532dc42b5cec061f8b1b57de421b2914c197fda1578c2cf75ceef9bb2478865d1b2ff0135326d6c6967126e470b139a9d39cbba5f3002811ee288cc71d4afce2ca73b17cf2239f5da4447df12e67a7c017adcd271180e27f1b4df114b94be7cab08e50dd8a059d48ae6b887130967f791b9b5b57934a2a8cf076b403e20dc138e946f2e57d8ea74e79f337f", 0xd2}, {&(0x7f0000000080)="e89e00a77b9f491a19ffa382a103abf98bc714971db796497bd88138f30ee39ae93434fedde9696c463583a41394c67354f21a22", 0x34}], 0x2)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000040)=0x7)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
poll(&(0x7f0000000080)=[{r0, 0x1}], 0x1, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = socket(0x18, 0x3, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000140)=[{0xfff, 0x2, 0x7}, {0x0, 0x0, 0x3f, 0x7205}, {0x5, 0xab, 0x40, 0x5}]})
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x80206979, &(0x7f0000000300))
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x4, 0x1}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x1ff, 0x0, 0x0, 0x0, "0100"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
r2 = socket(0x18, 0x1, 0x0)
setsockopt(r2, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
dup2(r1, r0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000380)={&(0x7f0000000140)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x700, &(0x7f0000000300)=[@rights={0x10}], 0x10}, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x13, &(0x7f0000000000)="28c67cf7", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000540)=[{0xb1}, {0x2c}, {0x6, 0x0, 0x0, 0x200}]})
pwrite(r0, &(0x7f0000000080)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0xc0}, {0x4c}, {0x4000006, 0x0, 0x0, 0x80000001}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x1, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206931, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x3}, {0x60}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x1, 0x0)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0387200, &(0x7f0000000100)={0x0, 0x0, 0x0})
open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000100)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
select(0x0, 0x0, 0x0, 0x0, &(0x7f00000000c0)={0x0, 0x7fffffffffffffff})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f00000019c0)=[{&(0x7f0000001540)='t7', 0x2}], 0x1, 0x0)
r0 = kqueue()
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000040))
r0 = kqueue()
openat(0xffffffffffffffff, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x341, &(0x7f0000000280), 0xa55c, &(0x7f0000000140)={0x0, 0x3})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
getsockopt(r0, 0x0, 0x64, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x100001ff, 0x0, 0x0, 0x0, "00000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCGFILDROP(r0, 0x40044278, &(0x7f0000000200))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x4}, {0x1}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="2d00c784be3f585402424c33c16f", 0xe, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000000)={@broadcast, @local, [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}, {[@generic={0x7, 0x14}, @rr={0x7, 0xf, 0x0, [@broadcast, @broadcast, @remote={0xac, 0x14, 0x0}]}]}}, @tcp={{0x2, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x81}, {0x3}, {0x6}]})
syz_emit_ethernet(0x22, &(0x7f0000001080)={@random="7190e10f2801", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast}}}}})
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x2, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
setuid(r2)
ioctl$TIOCFLUSH(r0, 0x8020697a, &(0x7f00000001c0))
syz_emit_ethernet(0x7ff, &(0x7f0000000000)=ANY=[@ANYBLOB="955bac1b78b9ffffffffffff86dd6087712107c901400000fe80000000000000000000fffffffe956a"])
semop(0xffffffffffffffff, &(0x7f00000002c0)=[{}], 0x1)
semctl$SETALL(0xffffffffffffffff, 0x0, 0x9, 0x0)
r0 = openat$pf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0xc0504417, &(0x7f0000000240)=0x9)
ioctl$VT_ACTIVATE(r0, 0x20007605, &(0x7f0000000180)=0x6)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r2, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x20}, {0xc0}, {0x6}]})
ioctl$BIOCSRTIMEOUT(r2, 0x8010426d, &(0x7f0000000280)={0x0, 0x5})
semctl$SETVAL(0x0, 0x4, 0x8, &(0x7f0000000340)=0x3)
ioctl$BIOCSBLEN(r1, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
ioctl$BIOCSETF(r3, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x28}, {0x6406}]})
ioctl$BIOCGDIRFILT(r3, 0x4004427c, &(0x7f00000001c0))
syz_emit_ethernet(0x2a, &(0x7f0000000200)=ANY=[])
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
read(r1, &(0x7f0000000040)=""/32, 0x20)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x800, 0x0, 0xfbd, 0x0)
kevent(r0, &(0x7f00000000c0), 0xef8, 0x0, 0x800, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b1000501600000903f0000000700000000001306cfa10500fef96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b29fbe1aa5323edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af6300372a2102000000720fd38bfbb770c1f5a8aec881ea772ec5890400000000000000361b1257aea8c500002012000000042000"/177, 0xb1, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x11}, 0x2, &(0x7f0000000040)="05b01886e832063cd3c8a7e938748fd9a35360e697cf9a0db8c665f2c3b396b69fdcec251b4bea4b112a6ce28c", &(0x7f0000000080)=0x2d, &(0x7f00000000c0), 0x0)
r0 = msgget$private(0x0, 0x1c4)
msgsnd(r0, &(0x7f0000000000)={0x1, "4b243e882eabc69238ec6f77406ac270fd202a88c7efe7c91b327b19efecdc97d99fe7a499e36a37f54c3af92e7ac06303e6b69b049dec87ce351e09215b5e6d546433009c2c0e9ef248dab88353d2470d0e589f19405d689b25ecff8da3476d7d27eea55509f776b3b23e04d2717a106ceb76eb6e"}, 0x7d, 0x800)
msgsnd(r0, &(0x7f0000000080)={0x0, "7084d82abdce9835e6c9adfe86cfda1584136b497e397947a8b581c88948c8320d855e52a67375352efcdb3cbd7f"}, 0x36, 0x0)
r1 = msgget$private(0x0, 0x8)
msgsnd(r1, &(0x7f00000000c0)={0x3, "1fbdaa3aec134f566aea6e9c0ef94ef2413c7fea5b9764eb828b1f8e30a73fab8c8a15e29d5ff4cba6b1c3103a5a44c97a29a20fe392dbd7b6badcb13c2012c126e1f2ee9fa2006239caf0a24c79114683420bd6d71855221b01bbee1e3e3651c61acbb4ab57907b8218ebd5449d65c6f9778b184a40db"}, 0x7f, 0x800)
r2 = msgget$private(0x0, 0x1)
msgrcv(r2, &(0x7f0000000140)={0x0, ""/163}, 0xab, 0x2, 0x1000)
msgctl$IPC_STAT(r0, 0x2, &(0x7f0000000200)=""/175)
msgrcv(r2, &(0x7f00000002c0)={0x0, ""/52}, 0x3c, 0x3, 0x1000)
msgsnd(r0, &(0x7f0000000300)={0x1, "f724299a6049c4d92f09564427086fef8b662b184fa747812289826054a4948c62bfe87ec4113dd5a14348802c0a21e42bbab481b08edf59ba8fc087c8cd33c81e1aedcb7d5322810ef6745b537e6d4f603651f6a628c3161fcaf151638a0c1c3a5987a825dcea0eed70e46ce24bc3faaaaadea78c646c22997ef07402a266dd052bde9e3d2a2e5c56861c65b01c44c6edc8dbb31ad2e1e6bee53588350a209bd345f639b2462cf4dafd622af411a16fc5d6fb4e221821bd908005befaf2d92a275887e1db07a5224b0e3df5daa9c3"}, 0xd7, 0x0)
msgrcv(r1, &(0x7f0000000400)={0x0, ""/171}, 0xb3, 0x0, 0x1000)
msgsnd(r1, &(0x7f00000004c0)={0x0, "6efdd4dd3b91663bf4b55dd503d93664d295155e2d6090da459e2d4233bcbca8322e943d56e053170ee1d340e39ca3798e8f30e2217a9401fc2887abe70b6858ab53c832eed635029797def8b8532209f0c5a605abf59e481f519172b36b51ffceaee03e358ac402467a1e05e92c8cf17115a699cb67e79e7866b89300efcd926bd777b9ad9ef0d0224c501d54dcba61485e57fe32af5ad3a2471647c2f71b5f75c6ff7199d43b1fd7a265e8995d55bef53614501ff6d11819eac68632b89da2c358c580ed49824249ae7063866304b680b15b26"}, 0xdc, 0x0)
r3 = msgget(0x3, 0x312)
msgrcv(r3, &(0x7f00000005c0)={0x0, ""/4096}, 0x1008, 0x3, 0x1800)
msgrcv(r0, &(0x7f0000001600)={0x0, ""/47}, 0x37, 0x0, 0x1800)
msgrcv(r1, &(0x7f0000001640)={0x0, ""/174}, 0xb6, 0x1, 0x1000)
msgrcv(r0, &(0x7f0000001700)={0x0, ""/224}, 0xe8, 0x2, 0xc00)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x4, &(0x7f0000000000)=[{0x6}, {0xd8d9}, {0x4}, {0xc6}]})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
setsockopt(r1, 0x6, 0x4, &(0x7f0000000140)="aade08ed", 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000397ecf700100000000000000000200"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='o', 0x1}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7007, &(0x7f0000000080)={{0x0, 0x0, 0x2}})
sysctl$vfs_ffs(&(0x7f0000000040)={0x4, 0x1, 0x2}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x4, 0x18, 0x3a, 0x3}, 0x4, &(0x7f0000000c00), 0x0, 0x0, 0x0)
r0 = socket(0x1e, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="dd08f1ee02000000000000008452703f", 0x10, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
select(0x40, &(0x7f00000000c0)={0x9}, 0x0, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000080)={0x0, 0x1f, 0x7125, 0xffffffaa, "91903fffa80a00000000cafb00"})
writev(r0, &(0x7f00000015c0)=[{&(0x7f00000001c0)="cf6845a20652abc5a73e60b3b90700000098ba5f5b79eb69c8399073a888f64fb7952a26dc514957d9bbc90998c4a3907c13c15fdce356233aeaaaf9a3ca7d9a3661a07538e55e7a", 0x48}, {&(0x7f00000000c0)="ba10f1d170de844ca1bd60be085da963fa3d8f8aabfa99fbceadc881afe1ea84427fee40e9a124c08e9d111e2867bb7b1c4f7506e0f924c5ea5597dd9d3a06335f0d58b24838c6d96dfcf6a3b6e5ab4852fb59cdc5e8c806a06fceda0a4c20ae8731c8651cddda248d678c9a8803d26301d25cabadc4691495a6576ca05d07ee7dd9abeeeb70a746f299161ee6e4bd3120b6d4ed671505b9f03d598c58f28de25fb63cf8bf9e2998c7633693d476efe37451bc70f6f30f443a2741bc20c0bac88d8faad18b4f0b460b2b0ca8d8d956ebdc7be57c8547fb53c053b8a894e7cfd5aa13e97da0253679d9d012b6ce", 0xed}, {&(0x7f0000001680)="f1693eb617119e2811ce34ceb89d1b477726e5f5795c67000dcab18d6ce7c1c3a305e41b187e580f70e4c1bb63b99dd9207f39c9e346b87142a807135442f8e79335bd2d6df393367853d6e19a8ff50ffb2b0774c5113d75d1d63be60aa9bd24cafb48069bd2052e4ac6b370359fc766ffba97ec6d234d6824b5841f563783ebcd36d87160953bc30a3ba682faf71574f29f69db62a69944219c7bc78e3268ee4679c7dc4282f291df34f92aa2ee25d172a701fb4dfeae6c5f74086a8e341161d51f2d0948caa7f82e1a89c69d928da0593932c6dce0e2ecca7d5d5f9a1647062800c3a5472d3d4971376b8f59ee5c760596b242be3609a2ef228ecb1c209ac2c6b3fc6f8ce259f1292f3c155f7ed39836c7d5b0010272e4aaf6eedd97812f19610d89b17fdc6700915e8570165b48b7d6d3fa02e49f43d594db9ec1eea84cd0964e67f66f28b2d741aaeea919a7eebe8fd19571e673169262100d363c147b5ed735bc7b1d4d076707c3904cd7db1a9ba8179d06665adb63d8adc1f1ffbf4ac2fffa8845b4efd22f53a8f8040d9620250f8dc476837fbea37e8658f6f701d80e98b789bf723f43d44b54fb8920417ba0b41fe7c4d3e538d83c2787f58370b40d99784870ca2708fb8c0efb3932f451ef3bf9ac9595e353aacb921c5b23483f510a6dcfd81d49813ff4b9fb360a8901b8ae6ff35dbc66132106a25722644655727bbf87ef9c4a8ffc2fd181450714e038ec5e7b60a61fb6cfe10da38dd2cf74c6a87a02fcf1be23339f73837e88680c7f24a818b4bd27b540567d3b015592e090b329944ebb8c332ba56a86b1ab6c4872071027380925f7e0371ac758f3c337df27cd88ad83859c276197dec5d3585bf285bd2a35d8844d1431b851fc33999fbda18c33b584be6a4b47996924ab9d996b4139c46c8b6d6014d75bbb4d46067eb45acdba316916b536f65567f285ec10d33aa02c4ed0b0b515aa873a0532dd65459cfd258397d813839ef098bc944ad6b3c83065b59318902cb1102d5171b6df1e25146a0d2ad0a65ee67214175238bd32f17349317fb78ea2dc6f81896872784832764b35dfaef8f25600ccfa108917c71a7cbbd4ed4ab6ee70383c107a1976b5581a67c32a4d18e5ea6ad440205f70ce00da41deddd4abedb0fcd21e95d06f3580e84e60e1fb712678c20d94efb208e627656c5285c52e234d3435a2d2462e0e250480b41a62172110733026d7c65c6da6fd7e89aff56a471d4f7475470371bd000e6ea9123f84df73b72867587c5175919b8f1755c894fe27e55b58cdf76e30baa92f33953d45a2bec605d5f434c50d85ef3ea1a580a91746038ca306014be808f71b499cd717bcd83bd312e14d790d5d39038cdd723bec3060e1977cdf0cd6ae517619a56547e4d5a9143be892dbefabe98aee8ce97325e43e680b182a4756ea7a32bc71c28bd9f7d8f8a1cae3132d68193b1f1b1887341addf5506bc597b04062ed86fcf64643fd6c716f05cb09b1af05affda67df8e47706c5f78960b17a3ccfb35f285f149fa7ab33dda897881ea474450ac6f41d77c9cfe471da452484a6ae54089c14c15ef5cf793db7b27e1a6da16426d3d132f82eb30102f64c8d05e8a8fa7047d32809baa1cefdde5383956518c32cd1c780db5caeb87ef9390c049e25ea4ba489f33968ad9707c415697540e1e138588c1e7cbc861ef8992c13eddb0d9cc22642a80aae25c960fb61f3d0a5332df3cee9ec48752a3707ab810b1a9a91ef884a47be838699848c34154b4e0f72bd018705791f3f91f75b42674635bc651bf404411f6a7a4b30eef829fe81f716c9026c26d8356132de2b7e2f29af2a8e9cd2f72e9b2624b29a8a9b41785fe1a517809e8d22f7532bbee325e5e92ec51912dacd4cdcde568c1d182f4692a2ee85013316b602f00e28359c5020dfbb95f0aa24b9bee7737896caf5fbf10d3e01628342535303329631e0f3572d68d734d5648f73ecb71c6202f42162661a8de02987d744f3c1d9068dc063cd4e88047db479a52742eef89d6943a831022034690a786f10d3ac416e9e50694df3248f79c095fc9b70ad2eefedadf5bff599baea5a974cb79189c65a239b10636c1ff3df14dd3f3353c95fff9e919083fcf69cb62aa84450df46e569e1f15183a24605f73fa5523fcca738e54466eb8f0775cc25fb07c175ae59666d07ef37ef43110778126065da84e3b1703444c078f73d87e92e1a7a44331bcc86064a1669a71826b058bfbde5e671f8a16866a67600d4951850089ec34171c01cbf973643769d7c1d214172f6a18ff9c907c9ff80634d47d4d00fc53c40f301506ee635ba92a24da1eee880389997009e08a0e6bf60de17dce4d20b0d6cd02f2228f97d8114e1565d9389af3758926885396b68fa556c2d78c3be3f9a45bab8c881db1dee5687c6e2be131583d232a211e7db071ccc0e9130dee769cecd68dbe2f1f0a3414dfb8163e4cadf20e09a446728207a63e702850ca340ae07b32e260eb81d8bc9900194c2a0754dce4419ae9300e8b667ec1886e6d978fedf5e4864bf043dff17a3f450dac2bd2d031db8088b3667eb99e314b63aa6062f4fc6cc59dba83cf0418312d718d9b765c202b7a1650b927e2cb4f628996e5a46c25f06b18998c3a80dbc3590fb33450de0e2abec0b4dae905f41f89493e623ae99ced7f02ba732482faa1378d571a207f4d76618b3eaf8817733f284f3b96e870d5b91ea2c5cecc5cba98ea431eebea057e94a27bb404029c414b7cfdb5ec79f653949a191bad6bb89d910011d22e778fe81b8324e9277d60ffb958828660066688704c9d4f2db333584a34366946d5b5585e9cbddd712520a5cdbdbabeabc0ed2e1baa0d2288bedc8eceb8039854868f0f6abcc9051614f8de9c46a9f3a1085207a85874ce36a36a98b7c92477cd6136e8c8d38526cfafdf5b15edeaa720e75bf8de3122b2747cb57b8d6f4ca17e53a821826ccf86c38c0ec3720c801c11e084408e72083575c162172baee673ec199136a98ecf5f08453af83895a8b02f5babb3a4919f4fb859dd3704871a6f44cfe327d7f48257068ab406c31fca7f0ece63a0fa4ed1efd0162299059a2dcb376546e7f533353920362c69f3ea6f5398d82e2c9a967e75276849f6191d4f01dd1bd41f53212a95ab9561fd621e5d12ce724b8f28209a035c83168b248367611e63800ff88a3d174d89a93149b802739a07ac726b40cf4d7549467ecb2386f349af36665493fda3d90854334196343f8c5e2adaaae744e6b6f50a514b0a729b34f9457531d976f28eb8602b81dac702ee5a4e1d7686f2942103c4bf033c1c375512e5559d14b3a4ba1aab35a3de2970bc5ededb13e358515f70251edcce268760fec4c5eb77b216b0ec4023af9a1c7e03da0f75bcf57f033ef9cf7a4d3641c4830f8d87342d5505be61e80e8278317ad239bc5ffd65af16260a92e52444ad55e55aee9f2b109f8b07c2e171fb8f21136fa39b47430ddcc173b111efb2f3ada3a466e658db6b5b495e2d5c598f524bb14cc6a8b19e65ebadca3e900324ef7567333dcfc1f0a36f498b640645a373cefe599f49392eab400bd3aa34422b8cd6fe6d9f863114d6f4c350019a91ebc8e9736d4ff2caf07daf9b0f8195f543636862b70c46366ddfd06c5417f4b3a9cd90d2919b81b5c1cf1894a5b200ba6fb8441dd8a3b29cba246463c951b9826cbdfab7f8324100ffaa373c63969979edb7668e073b6e3997dce87e76585eb15586c1d78fc2f66d2e5045e8ca2b8507ecc411d9df4ab2d849422c4eb31e54e55827ed62bbeef85bb866dfc97273ed757dcf29afde8a5b4ebcccfc629224ce14f1a460bf46799b7d112346155c69ea1d10d1f31d2d00da34c6026cd516aeb94a856bb3cd0f501a8aea7aab03a33b6460e099c190713ffe80a610bce2287e355312ff6e7425a6eeba00c2934f6fbe0d0befd13ef65390774e7c63e470420004282c9ae07fd68bbc10c7834c2c3b9a07a3af2adac68f315f55fc2b035b30718f0813a07636e5029b6e66f7d32cff7aa32947013eaf4621ab8a300ee6661e5ef34a72623bde1575161ddd7971d9bd1fd074b5f3a134573ea89594fd5e0044c7d9eb1021f6f2dc22161920dcb3af7370a9a9cbd811f6d0d5483846a542c4a12a4706d2b3c700341d58395f4be65d5277cd5811646f4285a13495707c1b99d41da98143a74fd60ebd656f22151e5be2c733aae3183bd1ae0b4f76e4c34a07019c9d89a72d5ff6e72c86973a4515abdee618f680c73046dc8cd75ea83f7069658b39e6e92214a3ca3d8ffbf64526e2eccb6359a7c05553cf143609caa04d9b12412ff88b10f4ae99eed7ca5c4925582163efc72bda4e94d6bcd4978c22dca", 0xc29}], 0x3)
writev(r0, &(0x7f0000000880)=[{&(0x7f0000002700)="4f2f5e9a919effcea996eac2eac95d497017e4d734cc16f60e429ca990ae7b6e77d30ed14182158a2b055e53a61582ff8ccc240c28b7aa98f704fbd5e1afd55c7916ace84d794b838d7a39627f1d2369d3986340aca3c272670895dc007cbf5c87cc3d2a1baba20b14dd938d9980e1d3afecdf01e0ba486cde2af4261943129cea51450e6ed592097e0ffa75bd493ec4dfc4332daf7a5c3ba953042f24123ccd606d65db46e1caaf1b94df8274c37908bced2457a8c61b2301bf2104b02bee5dfd8f548e9d0c92f2c44e8387d98e02cc8473b21aaf7e1aa2e54df0d70a80670c0da69b1c7e2d69348e987b25a88cb0e53ef4f788cfc7ae5d5475b75661359f3338c816dce0b3d2e7d2071068f551527805cdf66e1eb5713aee25c37055c9b396175f10555c6f229141c22068decc344653238ec8c6663b8db79e06f6c6316c3f88d4d9947494d6619c8fc1c0b9be8a5e4d6759b6e9a731186d395043c779eb3d50c2eaddc8c7fb492f0009326276e3af899bc28f4b36082551a622545b50881ef9f337e43cd648cf7ca3518a3d6673e8b5a0722a66257426fa2dcc6f7e684f97268c3263d320521137e8b52cdb94947bef06a9a241821e65be60cbc07c6841e7b0c7dc839f300ee3335c1909a453408073a709b1c07074b98e718fb98eb954da3105faffafab983b1d3869e90600f46b080d823af9c168d339c73f012fab6e44dd91c847f7303bc569b6ca108d37dfa2252813a983272dec65a1901c3dbbaa27c87c86606845a7d7fdbf99df82567f02d210c8b90c54399c7ac79ef67c0728f0403acaa19178b4d500864e594c3646d5e6ec654f30d5b0c3ef318dc2458845d98dac02ea54e975dbae58fb3b0c25c25c5534991faadebe098ccba999e1dee45a83986d041261c0342ab0f9c869dd5015ea1dff609ce76f40d37879333a12c2ecfe6288bfa4b1a901a0348f4b8da2d1222f8acddf079593ba1accc5b5a99af11b482d30c44ee48e324251ed65405d3bf1d38fc9bd3de79ded0e2782324cb7ef2ad2b8b67787537af3c989e74c02cc7d349e6432aa790be7428e22678dfb43cd55d96ee9c91cc4718467194ab3c5a8b42b3202e9710e12533c858bba7a3b7c94dd317ab6b0b6151af23ae82969ab3f872f9a7b9635de82e78d25ee96a360e9638da96e2d46e67588c834f6ed47822cec66e537c2996940d6094ab8dcb08feb6a7f9dfdc9450202bd83ca0099821497014ba6831b219fc623630071c95105bfbcdabc6589f68ecd7000cbac3487661525387e1161336a4dee78d231a6c0fad51dd9eac4b273f572480bd0943932f1c470105458187267d5ca173bf17857c479df22e51a6896a602dfc2ba24ff282c8c497bb60abc7bfa5609dfd865a649b6c97c34e40b74059486b61a9160b2c098bf9fa5ca44e21c9cfb79752195f966b55163a4c1d0c806cacc1d1e2b434e42218fbebd5b82112a4c3a2930e433d9c61b068f65f20f8589a11df686999e945fce7cfc08843b2b77212acfa863c9a1b7230019b934947b46e177bd05c58ed79da354556b9a2ba827084f48c28f74f71cf2f071caf8ad7fb9c9a40640bf14c969e86f8d8fa8b0a64c6d2fee22245b1dd63ebebec9c82de38366479d212399514ba884c28eea841e964ac769eac769a2b08e8e06657248b2f2ac1f22eefe218bf3f641ab04c54f13e249a536383c5d577ebb379dfdbd56436d5bdcb0786f372c847d87d535567e391375650c6b8e63bde00611e7cc13240fcedcbb8a3490d22d0f2707df6751e36b11ec03c78a920b93ecd25bac9e07530386cd45fd1aa9b1e186088773932e8fafd09c5e8638ebf0f9f7dd13ab06ea8c465f0fda6d6c82d2981a2457f863f4bbccc3703e0a05ad1cd1e117bda6cec03ab2b2cb9c1e98324a16a483a035b81e673ff452c8b94c272d1bd3e070aa2ed983f4c4eb7ad3b0dbd927bcc5a71ca3cce11e8dcc1ce18bcc7a8f05e57d4cbf095e615c618722280b4ec5c816c6ed58455f63c2054dd4c310b93cb1350b126bfc17cb050cd38f9ee0b5b024dd7bf5ec9d34e21c3282296ca7dad11ade61c048018e9515e407280038d6e06b16bc824481d877e3337f40b0ea388831443961929ae074c7e0e565d817c3eb23f62c4965de7557791ccb335d65369228e545e0c92c04576184350e971fcb3e84b0436c00f61fc56b5c975d390c2108a6ffe8a958ad962556d37b21405cea182684268e6211a70841b518ba46d1281b4fe4a08bdc6e69c1604927589c6660dc6aa0b7627113a3574fae44113f2e2bb0cbf5e645f688e76d09c170e7e59076703d639f2e05b0eaae40ff14aab7e4b59f43c3540c0eb2516ac0aac3a37a2961da5d81e285d1fa35d68387313086f2ac05e8c82f8021edc0a5443ac3539a51e856df068a77ac97a53ed35455951f12d8d2a3154e45ed991ceeacc039c3e8807044b9896cf069f9a8da0245d32a4c5b55ad1e29d4864a71655cde2ad1a0634a3c8430b607c84c17b977bf8f49cf2d41ba737ab1fd91bcad916703993eb9317ef02a89699049a61840e6a006ba6d4bf4764af9bb4d4129573a8c21480d10cc7139a6862dd51d00d7e7a99bbcdfe7afa419cb1f376bf3a2862176e5fd404d4a18ff384356c308ddbfda7aae6311b2eba8f94cafc54c556ac4172a2495ac5fbbf00fb2b952697a738638b7096848a8d2d68a6e4d500ea61891f74abe1b6d802c8a7dd0c39abbb801ca7f14dbfeb5c85097fa42f11b27b1460cade8a11fa5ca63d108518989fde79e882cb5f70cf4107ac76dbb3750b0b850308b6a294d280671f93a94974b31f211cfb8aabac9a2c48f6210752a37d52cfac75b053c8a68fc359a201ec3e5162dcbaad428612bff94b11d86edb93cc872c463e2bf83921fe7b0e2f2df51f5a7e92ef090ad9a86c2ab610c500b09ea2ad94b34ad56cba82ebaa621d4e260b63e91c202cf1dbfa088befb04fa6bebbf8f67b8e14caf8a8021b7ff73903dc14b81d0d217dd9b92b0309db281aa271d88368c35e1163bfee372a8d1b6e67efcf2324d4c898a8286bcf2e66bdcf8b5b1a1fbc2270aac8f8a85898905bdeeb0f273df71b8c09a39ffd7d5b45155a5ccb4642597266b8cdf5e0357522eb07365e2a87d98c4e963fd97e631e4f88f82d44670cef47085f6367768f4d9a1f037ab1ed67eb6693bdfcc693ef7e0ea27987d58ec5740a4633a78547d6c1deef03d6df70cbca727309ede8c6ebbeeeee2a009e6f365381a8c4f1962257e99e6997465f444b3750a6aa2b513d8283ffa88b596f4a03b94824bde3c6ed34aab2cd2500a9576da93d3f30fb09b9fd27b16fccef30b343c8903275ead489141ec0837892b21f287dc7e71580b3ed3504ff9b9e70aefbfdd3d7e637fe134d06a621ca3ab3490aed1dca575c8b741b851bd9d4801e03a4a335ca48616abca93cdbe57a5837fe386bffc9a0fb4474b696c392e1f890ee85d6781f7d9367f3640241fcd508de822786d035701e53a4370b542fa28fd222b2ffde85ddb1388924345ecd408413b26b0ea417ba93d009ebf10c7af2b5f84fc2d7d974861859de7d7bf246a67f75c1813a4590b237815770a45bdb4e9332e881ebe02b3d5183ffd32391fbff7332992e4db04c1474147265636f9615454abb5a5fd04a695d4cc033cdf9a38341d4ce226924d633bf599051098da0d3c487a416ba2d55b2ef94c3b039b0716371a92d822620833d850c0d94725f2098e254267ea654c406030dc9e5ebb0031ec98380096a80c2e0e111a68f77ccb6e6caeb41ebb871d1153cb935f7073f7de722a619679dcf65e74a4d64c5351c8c351106e3f73ee77dde7be3ec86d216bacbe5c4b5a6a2e3999d574c82f67ddd6a07878aa715904a2e46e3ce41eb51987f9e0a7f9155d7c4a71e85e9a44852ab1c9cdbef2dc2273114c2739c885284815849edb51f8db9e3557e29a8574bd8b11791033abc03eabcbc49703d619ce5c746e284e15d606b783eb16d2af252f1632ae41cb7a9960d94c8ab77eb8af1a30d7ea99ff7382345c664085623e0bfa1cc9c1d0b27539396737b09eb34a16712d86cb4aaa3f87fd12ca06f90d86bbf6f189e9b74001853d5a7d9483f1b2e658e10c8c7ef41a861a606fb7e21644b5c79db689e7828dd93c2b2a570d4b7fedb2a2a89ebf76442af94f5b112103673332ec8af4ad663696d302df1dbd21ec678e3f5a004c7c3161c64f6ce935f2fd1218bc1a2493c94b0a37a7ac9d18b7a89999c9f97a4bac09584235aa18ac257e9d7072b6d0fac0e14348ba21d18894282e124481564c4ca2799b1f0d21006f8431f26177350eadc4af605d960e8e61d1c598dc586206f856242e4102cdaad24646cf10569c6b4a7945d4ee155fff9e65f36b1beb3aef7cf22a84a254f5c7b81c78c0e1df02491efba100cba4c8c6234a16ca1f8631b82cca89de93725b28b32b555c2fe16ffe79335e4600c59f1e390bb8f415b1801de0d2e660421cc4d5cb17091c524360e029a9c0841f06058baf19809461d5eaa63465b46b898e664da65190868cb0e4e63ab7afcdceaa890a", 0xca1}], 0x1)
write(r0, &(0x7f00000033c0)="6cf04ae7f7da7a3d732bc8ffa77050a986ee5a045ce6489140f82d75670ed64b99a731bf7880d484d9f714e6a51aedd60128090d23c89d1445d6f0db559aee8192a2bc49fe2a1f68b49c4c5bed8d34feb7b518ab4849f3034323478133f81f69910f603bfab2fd1bd15273d9d58638cd81a64ef8bbcbf331fc425e0810cfd897ea9b950cab78fe51dd82ee5f7198fbb3fa6f1a0ea6312ac0333523f7f2e9885ccabf1e0b6991e99ca2eec6df625977619be63058f98026dc04787120230ea0dc47e184d586df0c882218c350449e0b5766bb5337308a887a61c9ecb15073e93ae1074ae251063ed0f2cfdb778665829a6ba85670d6133f36d6216dcf8c31111c353ca35518d46071475841749be55de27bea4850ba3aa8f802551aac054e5affda68f77fdf4b7253a68dedde5664c0811251ea46e5c30d03418c7380b634894109fff6b1ba0460d1f9f46799f5d99d9bc64878a34439021b5884b68640795ff58d9da3262f3fc79882f5df62a022f67891fa3a585f3b039b77ca836a2672bc6c9d660566cbc213f9980cedd705f965efd7947a8af3a343c943c1acd12b15ecde76444df2bdd637865aabe08056a0e6380d5683c1e446bd7663797af41b6c9709a2cbae1216c092ad885acc3383c4c3a762b1b250dc6d9663b1d089d3fda9f00fa3f15c3138cf1a08b94f0e8a906f7f7599002a49283b2d63bfd360ebbb28e26abf01fe4c7773483be462e86d5ae4fda65c97619800ceee3f8fe2dbe4ef994c9d827b86ae7e63a119d4fceb025d3ce23bcc4c99f0768ff9615115219c6c165f9430c3cf4d5c3e9d4515b76b3e9b00cbf9efe8201766bc8e6644e0f32e96c034098b1030707ebe77754620845ef69022e6e3e7a2000c77d10890af6b4671766d9020c3b9b2d933c0dd938bb028851449bd94a674292e69d658d088e74271f37bf79193dd78b4130cf766901c995e5f14a3948ff0963cb0da7baaa22d1379760a77486d917e73e1618fc9d6534f7b533919b0f1c46ade0af18ef0e662dc025b5cad42f7bd5ec8dbbe73e69f2ffa6cf95cea7ebe00026263904e3edd46017318cc4a35b80c93db12d7cf5186b2c34532897e50f059f8cd9e504cb455830b7cfac04487e06de8780c56115853331f82fbf1b258d5b2b63906dd02e3c0ae3b754776c342172eebf7b55feb302d8da702efb0cb3c8633a5619b7574668287f76fad1d0135933d38ecc62dd5be86548f14f3a2aff3d461eaaa26b779cb61cd8aad508ffc4edb67814b7fcb78967e8ec2ab31f880e10a0a", 0x393)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
close(r0)
r0 = msgget$private(0x0, 0xfffffffffffffffd)
msgsnd(r0, &(0x7f0000000480)=ANY=[@ANYRES16=r0], 0xf1, 0x800)
msgrcv(r0, &(0x7f0000000040)={0x0, ""/41}, 0x31, 0x0, 0x0)
msgsnd(r0, &(0x7f0000000300)=ANY=[@ANYBLOB="0300000000000000a5fb4c8bf9e4c32542f35c9949f88bfeeb5f1e1ba76111115627e3a7048e6925c926ff0b65c9c14f3dd62c3943f4d25030452a91a2a6eab5507ec4a994efcc4276e4d629287ee85a348c9ca5c9b20ab9555134544e364e3cce490f2f8c7112cdc403412211d5cc1c9a9216e62358d526499e625307000000b21c951063c7493cc33922be61df395c4cc98a2aaa3b20793359264321e58a90951e33d2371fc3d4a01b57f79169022048e58896182db8241aac66e9b46cada3a4a0be9dcaf01960e8ad8e3ec8380964f233ed607175f0f183ee064b3ee9e362d0c1d44eab9bfab012d7da0ca316f6cc48d25d75dc60ddd77e834d36000000000000"], 0xf7, 0x0)
msgrcv(r0, &(0x7f0000000980)=ANY=[@ANYBLOB="0000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaa85761e67d5ba31fb0f010b152d3aeda074a8010e9fb03a97b92d05770c6365c9f51c18f59bae84173d3320a515d7f70e09d1b886c652f2cfbf58f04dbfaafe7962ff1aecd390d92a5789755bb46a36f509d0b62998a0eba1969cbe82df89206867680654dd5004ef52f3e99515121fb867b80a073a40fb390bd24"], 0x8f, 0x1, 0x1000)
msgrcv(0x0, &(0x7f00000000c0)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff7f00"/240], 0xf0, 0x2, 0x1000)
msgsnd(r0, &(0x7f0000000500)=ANY=[@ANYBLOB="0200000000000000c0365ee4c7978257eb12d01f0056b20f5a0e4a0368d08a60f511da252145ebe931b211f488cee4bfae117c044decdfdcdacb0f1b68137d885314c9759ff965c1c7525081389ce76a2c0d461b70"], 0x56, 0x0)
r1 = msgget$private(0x0, 0x400)
msgsnd(r1, &(0x7f0000000ac0)=ANY=[@ANYBLOB="0200000000000000a1c7391250f0bbc590d1a226a38d0e3d530aea92285fcaafdd5a40c49bfcb97c34675196fd5de6c99be9b53e028d0b3954c3a718bd5a1228f805fcff071755c046a79ddb5c75d976e5801c7354f78712eacad97d79d6817ff74ee7885260045b771a8683e347e928d063bc2f475377f0c5d19fc0acbd8f609af3ab750a7025d2c7422ff9cc225717b19b7c96f8d9cd4a15388c0b85f5d5f4fa0e8d0a959d7505c9b2e8062b95e92de290f28baba52218cfa90bcae2110e6f39b494386512aa229e01ef48276a527c29062540d0b2ac339244e9d8c87ee70d79dbbf99d016da2f2dc3b3031a42d9e6"], 0xa6, 0x800)
msgrcv(0x0, &(0x7f0000000040)={0x0, ""/41}, 0x31, 0x1, 0x0)
msgsnd(0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="0300000000000000a5fb4c8bf9e4c32542f35c9949f88bfeeb5f1e1ba761ff105627e3a7048e6925c926ff0b65c9c14f3dd62c3943f4d25030452a91a2a6eab5507ec4a994efcc4276e4d629287ee85a348c9ca5c9b20ab9555134544e364e3cce490f2f8c7112cdc403412211d5cc1c9a9216e62358d526499e625307000000b21c951063c7493cc33922be61df395c4cc98a2aaa3b20793359264321e58a90955e33d248971ecfa01b57f79169022048e58896182db8241aac67e9b46cada33ec8380964f233ed607175f0f183ee064b3ee9e362d0c1d44eab8c558020d7da0ca316f6cc48d25d75dc60ddd77e834d36000000000000"], 0xf7, 0x0)
msgsnd(0x0, &(0x7f0000000240)={0x2, "a6547612595540f9cd130e5414fbc229bf96e80fa7685b5cd9b011d6b26749ca7fff00693ebb6e796bed7c161b2e9e4bb45b046c535a7955f4c4db62c1eac3d11fa2ad268b83e0d599061652076c35e58250bdbe5abe4a68b05d768b66830669c5f21d4031d0"}, 0x6e, 0x800)
msgsnd(r0, &(0x7f0000000200)={0x1, "6521001d3f6bdf42aa2f11578884a0ce1c"}, 0x19, 0x800)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
dup2(r1, r0)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
listen(r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r2)
poll(&(0x7f00000000c0)=[{r0, 0x40}], 0x1, 0x0)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x2, &(0x7f00000000c0)=[{0x989}, {0x8d60, 0x0, 0x0, 0x2262}]})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd, 0x0}, 0x8)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x2011, r0, 0x0)
msync(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x4)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8002, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
fcntl$setown(r0, 0x6, 0x0)
sysctl$kern(&(0x7f0000000200)={0x1, 0x26}, 0x2, &(0x7f0000000240), 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000500)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3900000eea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cbb72e9e7247818f970e0174685", 0xbc}, {&(0x7f0000000200)="a5f81d1faee5aac038", 0x9}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r0, 0x0)
accept(r0, 0x0, 0x0)
r0 = socket(0x800000018, 0x3, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c100146600004c3bb2ff009e00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = semget$private(0x0, 0x7, 0x3e0)
semop(r0, &(0x7f0000000080)=[{0x3, 0x0, 0x1000}, {0x1, 0xff, 0xe17b725e3710487a}, {0x2, 0x8, 0x1800}, {0x3, 0xffff, 0x1000}, {0x1, 0x8, 0x1800}, {0x3, 0x1, 0x800}], 0x6)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x191}, 0x20, 0x9, 0x7fff})
semctl$GETNCNT(r0, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
sendmsg$unix(0xffffffffffffffff, &(0x7f0000002700)={&(0x7f0000000100)=@abs={0x1, 0x0, 0x2}, 0x8, &(0x7f00000023c0)=[{&(0x7f0000000340)="b1e5ce615fa8cd6a205cadf9fb6a604894438044506aebfe26977a37ef1ac857bcd225d3d9dcaa458d7abc027e46e4f0f5eb6f508f6e00dd6561af765bee3492908df7fc286b3bd5ae05db76228bc766ac1948906b5c2a0d2a55ce00ee1bd1457fcb05759f5d87288fdb70d08a3e47a1c4c2ecf999a10b19f3c7d19d69b0a06e7ae028e2a9659d8eca39566d8fa5d68983ac80658c3706ef15e422c2b239209cd90de84d2606c4b2f6471d6876b0606b4cacd592327c6bccc88aed95a7005ee127af870b11eb6efe7fd8525fc7badbb60e3a2989bc23680a6a4b3bfa4773f30338807d57378a1689b0c323fdb6dd49f84256dcbe185ac4ca2588e7340a8eee727c967138586499feacbb314f1d59d6888c6b81f93baf5039df82dce578f05414010cc9c7c9c705d1219bdd71fcbbb88d05efb82e53fe976b02e013f9f716c6fcdf5df5d40ae3cfc3f672e7f6914b46496114a50b5158d89152daa72653b2313b4783c4e631aa2a59ab81720bce522f5090feb30fa3497b73af4ae4e8bee1c6a066747c1ccc02b45df30f9a1e9b4a335974187371c70d17c2d22068acd8676db289c7863e3329a5a082e97ab297e02eb8dc88b8112232054cb2727ec613b4e0df6e1e3ec3acfd68e67fa51c41c8e428de68b3e74b77421dfa4d8cc0b6d7356f7a9359ccdd06db6a465a98d1169461c8c4e71419d2019083336e1b5e087a97c1698bdd3b6289eecf0678ef2e34751f28e6710314e351776250c56ee18f9f921b190065d41c0e0b3687e3958d7b53c14a39e886f39c0da22d8d3cc284ce007b2f4afbbb4618a6ab37f70aaf061725342241658e674833d425bc6f9d954ede4e40f72f307605b41ec3f9eeffd3a290b4fbc57fc8c715511da950f126b8612697a9eb23b5d18cec4d8e09ee6b4c632ea3d1bc8cd5c40c8bcde01abfa286f50cd0dcc4e76c149bdd03e3c5c7ad968b389fcd19ccd157f19b5d36d0f551e968cd6926ad83e1fd586fa95a9427234208055a56a4a15b17d865b7175bae504285f16952a2be909690b115d1e834a4fa505badbff22613be59150ccb7f2b7c5b8350b5fea2d2d06140d8c543797a8b84d4b9db020e1661706ddef5ba97906685d635d3014f96f90f25cebf76e12817cda84a7b4829532aee91407616a0d31f8fd5ee46c483b966dd2b26232cdd5160c03bab497372a0d8795e726b755d7ff1e410a614a69fd8d7c6c0acccd3d278a0ecb3a7b4d7cd62b029b1b40558a37bcbe54c93e5c8fcd1488f91dd57efcaeccf97b51dde24afdbdbe4258b84b991e901654479f5a0b11982aaceef5a91d78ca6981741521a3ed88c7865f4b594359f6fd4ea3d69db159d91b8899cc5b7e251685db8bbfdb9b6ab37f1a1a2eb1d0ade0b0c01eacbfbbc181e77e4da1a150a6b496fded025c2c7bb32025e9ec9d7a4089d51d3b7c96048b9ad52f282baac4e68a52ff2647f6eda2a8be18372dd11f6ecf952978999bb712d54251c6b47db3a3cada84eba5eb269037d458767ecbac36ee9cea4deb9f5db821f07f46dabbd7139a4362437b70bac97d338debca9569af9af693bd36db3fe4fdb3b034c4271ee4dae265930cefa18a3edba18380e1d9940592f8b45c2ced73237cd37bdeb8c3b390946390dde1eb182c5e49d330ae52b8d709d98314c29206a21f48bd0c27bc341f0188a789c37053f81cadda31517007421f477668149496e4e912c84b8ae83357311659e382f5add65a609a64616a90809ca845064e8dc6c7cc3ddef31f40b752b6e3ba4bc878792dd2b03b2ba34d4adf13e7baface9aa33cfcedf61828e9f529f28897284e22c1a119fed50b98ba19f520efc5b8a2cf2bb28e3512ccd7238192e0257f2df3e5ccc442c8ad482a38a8b7f329ab80c0070fa9a0ef79c39d5ee6868392a529eac1bef41781d00382be9ee952886da3559de7cf81311a4ff23d1257d842216d57cc960c0491af807d90155d3845c2c311e9011b21389d8aef046b10e0279068dec210220c80eaed48c605ac9df3480d261d81860f00bc15fe8368b4dac49e2a4bfc9f8e47f67f64620fab52501bf9f1e3c10dc064177aa76e78fa060d49c2b3e4facf997004631dc5c2a4e1bbfa54a673501a36df6846195100278a0bfe7627414c3b850a14e61a8c2de0cda6fe831446da1093cac1a5e4391c07b460980cec9eac236ab35c295f6b72e0e268a66f34e9980bed29a3bb3089bdc0afd0bcaf91541d729acc9853c49d75044252fec22ed9c577b442907a4e98d0ed3fa5d02bf7ad3bba3fdf8adc074dddd8cb93cf0f3b3e666c84134fbd8ad87948f1d878c06490f83695e7b86eeb6f50fca78140dbbd147a91d85dcb561d44115aefe223ebade9b220e57397b7693725e307fb3e032fb696f400a1536b96c45c60490ee16eb8b7ff8ab513fadb518fae1e6f4887fb695a30fa533e8cca967346da8db7e318d9937dffe961e7a86c70f27e9d0ec195ef91681e3c7cfbe32be1895349454cd836919b8c7880a298f46c19e731538f0348b947fe5acdb7211dd6c03ce2565e7856849ad22d41a4f2f754305e270bd700118125910c8d8b97b4479eda1208c299f23a877e565db4182c9b1a3e6d4815b2fe2f1ad54ed8ab8b631544c7cb360d4a8aa752f34a647dbc1d9be3b0012760cafa924078b71dd592f4e142f7db443ae75d6e00077672f85b5634f26e79d19e317323bcaab4db0a85360ef0f3ecdcf06edf92e13ee4a0fb138055fcd95e198da4abb4f95bc1d4ed6b44b82dac769aa057ede1758b0b927166fe555335b2d17c84b446afbf0bf62e3acd8e91f5e6a179f55335f03cfef4ee0d382698a402c38ab63ac2a48921120f728760ec036eb49f3a74b1b18a2abd57e92be1e725e13c9d49fe4413218b893cb89777d4ceb1f5e1a95f3558d636413d2b831d72128cfe40406c17926b3644bc5c61705e466b60ce9ae51c40cf0110f0a96d76fc80861c5d790d4244ce4dbe93dd693cb2944a6bce3d043a6aafdf63cec61780f6126eb9b9218154b7e6ce0c1a51f53993981099244d69a6b5bb3c0347250e77d2256081a36fd0f569ed212a3e28d50405923f36812f8580a647774d3ecf12bb133346db9f2f48a50aa571751b49bc61432c7f2e458541e76f292b4ca0e4a91290e94035a9eb96544e09ae5b4ffc3f679e550e3ba1d115bc7d45a6c46519aa2e8b4977378d2709cdc7c89aca4afb415981888626bf289fece389fcc8c978a9bd53e40e9cc7cee10ba5c22b7e28fead88423034d29b5601edded83576548e383586341749e15b03802fac14c30bc39ed9f80fda601729d3da177bdd2ded28dd63eefb452cf6be3c4d2a7d3c0cf41c9ae6da35c5d2e4940aa8cebd4ee05788498313e3e7c96f06f65c28b56d9684ec9cd7cde878cd321fb62c36d538630f11b63597ba93f79945ce9f37c248b7aafb7cad51e43e231b959a5863b03ae5325ac3ff4272ea44f97f5ea055e52012d891d46772de79541619c69b985519ef3138d590114453dff940279250263700efa5d5ec0a7c2d7b2d0749966bc48c96a6a63131c61836ac6d1504d2573210a1499b289b12681677a66cc9fafcac7985dddb513e22dfa203cb5add9948b56eda240e20c769da8ebef4b5b06686a154ace5174400bdf511b1575faca1aceade63b1121cda3b7ce5813f3480f3cd2e90f2c043fa277dfad3551641ec7863488ddc907489b9675dafe4577b6fedd7453a93e6200e821ccb72631ce2e82dca5a368ba8eef01e41d5e9d972be4c5f326ef69ab2180a0eea4ae6b90dae3748876d29e4bf6082047bf32b52828e520f6e48d339611872692dd7f09b9c28e7ef26a17edec2ab9776838cdea881fda9b9403548eed8e022da89e1983275a77e7e82566134412662792831ed782d62e6dd5d9c398e25fe2da8116afdc0eafdfcb3739ceecacf7baa36c4054eb7cbf295ab29de7f5f706c464bde5943a596ff01d4a06e2194df8a3c946aa20de7991d424cb264020fcedd10c1b0aa00289a8b19fe6024fca499247ee255cc20c3827d0c29e743af338d5b40a813b2b462b8490248cc57e8ebfe119d7bbff421828db62ed002600147454a31ed9d75ea77a3487705a002c3538293a850c2519e1dc91ec0b6f60fd09b19e1248642691fcb24864e7bfb4722026e50e1f21eb4bafdd2e1784ec4548b170d8cc4710fb1202200cecb8c8caa45d7560c0f4caef3aae52fa6c1b6ac7591c6570c2eab6a7b564dbf8859c26ebdcbddcb65b7e0c2f2aeca422bc3a543bac96045f75bfa33cc3976bdebf22eaf9653d479a29b7f9c5f9e096685566073be822125836afca9ae5a38271a92fb9049e157e154d3c91a47bd69cf5d9a9a39ba5d62ddb52a5dd8be720737b0afa262f964e1b594c92b3dde153d2fe41175c2fccb934ada5644fa8c689dd8975f635c07f4f8cea5e653103a3a6adf8243fef545edc6b74f5fb04b5207eced5b4f6785614a355576119bb81c24df2d53bf0da704afb86ad6945645d402f57d65c07d91c47381bd36bb600327fa4f0fa57a0b9a9c49f24658785ffce219d14fcf69eb8752312b8b97463bceafa5879d60aca2d1b73e3bf7648e1c081166c7c817d49c00e2f14ab5571a70714444c8fbfbe78c3165588de5c574a23ba9bbbc6879745b127daf88d0c80bd7fd5f1ecbf51feb506772dfb4fcfc0bc9479ceb2a224e647d11e28d44dcbd09fe898856ec35c0d3e320e928ba38142f6dbaa27a33014ddc2cf09aee9e89303e429fc6369f6a1e9dfd3e9c779aefd33cea962baf003b2ca79512b1b78b1ef5111a5a494434807e327931db57656514f1c4bc63dfc159d156e46d26dacd50f3e3ca525939910a2fa801db877109127637c235c844cf6a37521f32a3b938dce9b8fa1276978562898fb7c13107f0dcfddedf12b79aa29811ee466383884803e3c428441e6497876eaf6c4b98a97675cd84d3b1a91748c004765ca5951ac0cf9fb5406da63f2fe698cedaabd17e1b2be9dd5ce9811f795f4a67e2d35377b6ded7b78356e6979e9de0c838bfad84bd15a5490b991d812f8ac09c67fabd5a25b0fc38babcf2721ad0f3f500e2a3b8e4410b586469dfc606748f933290fcb6f42ff004e260697cbc6ed4f32fe7ca0b3485e53a620ec93cf48b5c4d3d938270f13d7b42d93a24350585918d1b5394d2dd0e79f1bac8a4716b5f2379d2b3232348ba38ee0c0c79cdaf2ef47b80aeca89a8727b51cc78d4506151a9c20b0b407926addb9165905b0574b38bbd251bfe46f413232eb3868b14702cc1ecebcfb639c6a6c340ff094d226a34dc00e38c415a4f8763a41f7a5c0b51f2d6d634dd95ddfeec87593cffe68844ac7f021d51454d859763f5fb24709bac85a70650af1f532b41e749e084d3d02f1ec956027f1d55ce1f5727b46560b7e76e07cc5f9ae0c910545be107ab3ea51e9027636e2f28e9418a4f1840dc13ca3a22a59ff5e3e3fe6e3b6b8bd9b8bea8c1f25c1bc497e98aacb99492309c1c15d535d330059d8742ec0c91bd37c12d95320476bca4b063377ded49449a66254e5753ea1e9f0dd0e9bf9a08787b431f90a72c23169abbebed5b6ce9a698be86acdeed0b117c2b9d6912e8a0cde182bf73ba8fefa6f0a782eb6909dac627914f5b30a15b10cfd6c38955f9243d78fccabfa4fe6f3b2eb7172b9eeb9897fd52e91e8c3b9bb07e0a670eab337b2e0678b3ed05819ad8b1cc4e9ee06524499f054", 0xfd8}, {&(0x7f0000000140)="1d38f7bc2da084f460d6417e834674eddd93f92b0c28775230684190339f9a8e13f8f85e70b33a0d2f1703bd761ff1a4818c8656ad827f80c6a3eef281fa059747127e92733bab3b5fe1befa312446881459d4655424d100290eea7620a98f86a25f3f19ce8402f4b48fb0142b6c53abaf27ce25b09161536cf55b79772d00d4b61dc06f23b430bb3d02af93626c1fef82312ccbacfa40f16cd2", 0x9a}, {&(0x7f0000000200)="87428e45cb4004bc654d0c22e667464a9a097dfeaef062d7e360ec86831ee51da6fd5d7c9a4a46720884d70b72c5f48940f8", 0x32}, {&(0x7f0000000240)="796eac9253ebf4395e40245753ccc0689e9fa0ab9597e3923ba33080e914d8e03e493963c22c012f12e8dcb7da7036a2fdfa9ca89cb4441e0ac0ae046ecbd45f309c7de2bfb162c3241adb63343c637163f794", 0x53}, {0x0}, {&(0x7f0000003f40)="a565e83b52e66444d66edae8f17822e2b29554b5aa49ef8fa8dbc6e07b57b0700bf533b3a81fb41a845a96a594fab6772b8c846c556f4cffb03099b7a67eb4c7bcdb4d3ad85feedfe904ca698680fc61b742f3b83328279e4efec7b2ed17aefbc7ffc80dddea6a0e3b65b7aa661d7fe916cdfa33843e11c02c551d77f933027f867622e61162f9a35eb27bb0081f4151e1b0c6f83e3bb8f0247c9f44d62c7185dcb58f7e56445172640e45b7d195e9c370dc8fbc782b031cc0d417b941ac9c6d9abcdcbc6670ac398dac4e194141e0bb036475ff449547ad3e836b30087561a83f275b978f1b5e2102b1a79143d1d2ab12b3bd35c2197869a50ba3c713e9b75549e6e2fb355637b597ebe90af2b44b12189fa6d9a724ba0c765db1682dbcb9269e17e361d891b035da43e443de2a481247ee551f392b3e99382a58f3222e44fdea34115b77c7354089f128ac20f9f822734e2f392f12bd2e0340579629ef1e9b0263d88b9bc663cacfcfe7db8ace55f8342ac2f40f0cfd53ebf34028e1091a7a85bf69ae78a348d16f30c4e94c59639ffc6935d348d486147fc1e7b8147a8339e57223053c90790ed53075b0124ad7b1ed083124fcec6646250de256d0f12abe572050fcd29ff7e3e49eb012c30405720b8b6d68758b4a194151d7da616716680ba972497600000000800000002fe263fdb14f88e5440a4c751400b1f4b0942cbb4e65444aeb59527bc7053de5ee0f3987c9ee70ea73b2bfbd2719d11648638ab717ba2d35df4294e81fc4c78abc2e5c8040f26f0fe567f589c0d78e48658c6a6e2944793d29591c3d2b8824ea095476ae2a2c65e499fbb40ba360abcb67669d885b89821f9456939ddd29490f4cd1d130561982d48b560ca695b063333a3e61ed67dd6a13c42781ae8298d422978145060a2bbe1ec5a3f80209e86a6701201f7a4aa30d3337b46f18492b6e3810dc3a7bd92b572e632fc80c9ce262c7c0459eb1b5160e0f67fba2abdcb849734fda418b5254d53ae46023380115a946c6838be82d717fca6e2ea9ccbd02dffaa2b8f719d2a2b0852e95aaa151381ccc1ab5bf188d30a6cd36b66f5bbacbd247de7405094b0bdd6f4d071927928046ea825ca9f6638655e097ad406c0c415b13f60366ee2c17ffc37d8d8018544fcd3dec656e84dcd7818b6d0769a631511b601b109e1e2c5f0692b693ff27e0f29c0da25f9d5d902661bc1b4f93a0f4ae526016797e3c2e3531a6d9a1ccf14e465706c97370e914b7f21385c0304f8ad98f8d654d864f43871795525e5fac0775bb0366946b7a1865a28654062ce92ab608645f24946eaa83d31ff065b7b06fe0555e57acde9e5364c71d922dfe98f4776c311db97ac973a9251b0cbbe695ec0499528dc96000f401cc7882b5cf53fdeb6203aa6580f41e01ba545082ae6ab475165a7ddb9d6a3dd7df41ff3d6bda16014a66a3cba2c749b22d92337cdc7c3e119cc3719ccc255947de7d6630ebb8d7a5444beff4673564cbfbe02bb0ee9143e6a2c2295f0f1061768da449a13f745ef3f8fc1f4f6320d365bcc695ba7c7ea7e246ccf448cced8370a9f2eb090ec62c1ed88a1ddbe6674b0110d56edb0dbabac5c49bc3a13195f65c6b11e86d8aebb516d0133425f3b0db83c6d77b7a4af1261822f41ae5bbb094bcc540ef6e9b8a342b60c458dd4396a67d4a3c820911d04ad6816fcafcacf0f82ca36876840395d9b9f0a12f29dd35b7d63852749ed4944857aba7707ea6e2a7ddbdea438d6a692ec02731d4d3d06a2bb320921b7bb8d93b67536323cae0ca982b35f108c09bf79622ae2736b857779594064270c74908bc79c80bac427c57dd58c1958dd628b8358885eb9eb3271f792f82c3dd8a69edce80eb0ace7add95a6e3966a0fd47dac9323e0447a2178e1b312fbbc39f34290ad7abf12b1de68f0507a32f37c041537497a292440fa5117f961e0f7f90530a04673085ae6c7d24aeea0e1a782af4819fc15d9bb9f2a905dadf61fe89e6986de3b8253bd79a3aacf885d0f34dfe5217c4242793d04b903e80f9a4e7bd4ee957b7dd0390223f63ad4bc073a658b15a9057e9c3798e94839db2529d37c0a427552db1fdb3f30bfb914a012f1eca9eabd13a4ed5e6dd7e526f4f6cee969be730f48a23a71dee9c48dc53f4de9d0ce3974984864e28f44ccb287b8388a4e6c4b2657bf700bd8eb8006d31556abdb5cb298706e603d7f29aae5c4260cf0010374cd50746b14bdcfdb12b5feb41c2146a70578addd67d83083966f4e6efb976137e7edbd3f3121a97e5e20188bb5b99253faf9a5ce5991f7e163de54a6b2a27e0c667059815348e53d9bc00d1fab5fd8de4728e5bd07468abef74f4efd3616e9810d5871408cff9ed7031aae3bf1d43a9a688adddee4d2337f730ddcc37e344523a5ee5b63c95e880e6d86d1a68759b98d3a84e2b4c29a555b57de0a5d86d5cddda94199cf64cb4337fb391ade3037913bbbea942e2b68ac44f4df0751ed75b38165385e96147790cb000cb0a662a62fff627ee32d6e9d3fae1c6a09d5f3661a0bbb7da918afb911b396af34a831b6f1d4c2aa5b554ac4c88cb02fb14a0e8114af26374403a9952bd11a636b09b3cdbeedb75bbadd99d5e500afe68669e0cf41d8c380308619cf2b94a7604f190727cc5340a788b5d554d0d0b417778f214abfd5c3c242ad8bd259038da65bac830a6413993c9812b7dbcdd1db4fe82c99dae29bbcd191d28b8832948d712de4a41aac5490e2d61dd4afe0e35224938fef03cff7d45ccc6d8c5f9246315175cc13f86738a0a90392ac4388ad4f68d7e8a0e93ab18640b483fb36a2b0421fb0544c26b775605889b2cc79a4ffd625ca788b265779d925707369aebb69bd978ad7d94b84e8604a5adad856f0ced0b91098b6fdf5aad351135faa3ed641d212a5dd92fba18097d027ae974bf0a99ddfe910ce5b896ede0420ad307b8382a05042a237b76bd2c2cb25a371807b51c6113255737240ce355a952bbc839972c6ee336f39ae08b6710be513165d9bde1b5f99da677373f2aa0018938dc0d4d6a4c96d236b7dfc58453222802cb57c3fc8b4b24bad867f31630ea8adcf86b6e087d903044de082e71469aff8abbc0cc804dce0287c055293414d21073f7d2e3f046bb049488157caf0792a25fe8340f62485f7276f9105cea8f92b57db9280236d5c87824878c881d96bfdbcc985f59aff901ce66a36a7c4cdd19815b26a51a71a8561bc4cc6c0c003b60455469d2d2e76995a080c1f6e29d6bbad233961645e54a15f97f2708a2f2fb606d6f9009a2e20dc702bb25fd84ac130a08b7adb83ebe2b7bd19485f96455973334b15dcbf7762a8a13018eb38d10b2161391a014c7b5d76b894fba272a34c198804c8fb32b6ddcfc427d653bac4341a2495cd2710318e5008ca915948359d05556357564d850655147ba47f2d238f8a35cb15588697a333bd66196c74378d1eeb4282b58665c70d42e3d48fb9456845efc8c7d750b32853f6e8504958923bca02f75b0f4ee2567bfbd19408571b8c3326b6f85887ce9c7f317b32d3a49f0a410be233a9635c725689215d2a1384536a3e8945fb1a820c2c75c000168a38a6a3ef6dc5924ea3ccad1450c01cbd951cd02490b4d9e35f80ec666e0173580fd3da4e5a66c10eb4c62b8aff812c221170e439010d9cb0dc4dd030bc526ad539a24ae26c051eda5a11ad3049409fd96f6bcae69df9d1467d0fec7f7b7b9dd3991c65fb3a8f0acf1d346d0f6f91f14c46f4607832439f3d05c883ad572377d451955cc9bce0bed0ae117760d6251bc8263abdb385dedbd8eed648f84b331052f0eeb42da64c91a94ca8d58831d9593ba9912349a7c61c274d54fb7d7c92dc462a514e58052563b56c13c22e5e5bc43b0392545e90b830e6f3acc49f674f91609c4dafb3536b75974f824fcbdf02b20508adb3efdb68ea8aea36765ad701df1127ed0363e94651c31e5fed55f1c652aacc86c8ab2febf3d2520f7df75dd117e7ae0e47f62314f4cf07e47814d60006410109ff322aece7e57c104eaed27749aefb7c6da003870237b06d6ab79af2fa2ea20a3b6094d756c6f4df72697d0f015fdc266f1f2d3a2700bd1769f48c9aeac6aa54ca88359cf46923bf940501df1c217bfcfa377232ddce5de138ae600100c2958135e8439764a9b366cf91a836bb6121996e08c9629e533a96787810376ce25d114fee47a8990c07c3616a84d34bfc87c21a2712a3f9d2051d192354d04ca17a4575e3262d369544bc1b8cbfb7a3fad909e7b7b5e605b6ed49c13f43e67cb4f027837bc9abf10c3b8240b5e44b76e9932699f57190ef5051f879592e67ba257a6cdccb2bc09605ec7255d5a63a1ee5e59788bf073f39d628ce0b4f8b66293273e134fdee7c9bc36c8716f446aaa5a54c4214e265d4504357d29a6f0cfc8bcd0166bbf7e6fea28543dde22bb8b17d32d546faa8252726fa92a2f4c22999cd967cad4affd4061d8681a97dd8f6fff2f99a84a324eff6aaa82f4f4994862d012d916036fb440fe3b91c2d12f500ca8c3564e063a2ed6272bfbb609618d2470f2bccec800b029aa81b622616f3f4ad0a05fec20d897d5277b64174d2d0fc752091f1e83b4c6bb2e0fe9b88ca7046cca49d36d70e31c4e4fde4a6bc1df2b2da80ca3596bc6d9691bef38c35410d7354396ca2a5542ac85a543eb5100b718f3d871f7bb67040bc8eb83da44c76f5ec0bade576c9c5e2f18e760319aea1b3316a7622773af5187837252d83ec6d729e1cc6c580239587696314ea45bb52f5f6f8b0ad530031f04b4f5e3b7ace2d7170eb0eb2ba05caa43d37c2c1e43033d2f10a5b49b89ef22a9319f97809109a529441807ba4c4a46e13c8d5256bf9be54d8fc41f3029ab6ce11d6db27b97dfd1c0cd0dbfdaafe0039834646932686e5ade7faf92e40a041f87a4328669f2dca1328d3a7ec513c0cad452a9b6633cc1ba28ede74b45ac84a422572bc9a75d57ad4b018969b8eeb1fad7f48803de0d3372b27438e3a1e6cbf52c9298eb04a754dc5286e3ba263a39f85d034e4f88a04ae5244df36967ef2939cd261675b01167a8f45da3a0fe295de82e4b561be5abd70cac638ac282aecaceb0f2823bac0343b606bd4644f2b5ff1abfe7ded1376442cb1581bd1b7ed049f5f325b704f6d867137416ce243d4ecf6a3813a9fb034f3275bd92b11891e97d0f565d43daf333a06d90d10f041e1415df969cdef2147145f25b9bd73f45dd8c192eaaf8b41ad81ee04fe7da28e36177c1ca2621a33fa53ed0919979ddd6365f6e73825cc897109b19d5e652dcf753b49661fa03dbb0994f0f4adf56924999c3be0b363b11c10b1a511a281d80f39feed3a38d01aebeabf690001b24527fcc4e763bd63029c6bf3c838f5b378f21a820facb43be84911e18d1f0d3799088ee4afa1207950471fa1f8a2004ee434588b7c10ef4a18a8d0d4e67604656c2cf71ee24935c4b3fc47242eb6976ad5e3ff391e71d00d0dc976dd27ed3beb9e9ae6e8cd883cbd717948b7519f059188745c412e67ba8799255f79f80075c88c5dfa442db232397744fdabe9136786108752e37ac284dbd7565dd4ee37448432677bd659d5308ffa730eb486fd427f83f418f3602bf6fe9ad1fdff77e4fdae2bf68e6808c76c019e940127c55ef5284958e22d0a331efae6b0baa4dda3a8baafe56d6765dc80181947cba3e785352d710ee83d0b4fa989891c16ce057d0ee375dba9efccff1014f8d469e7db75d0", 0x1003}], 0x6, &(0x7f0000001340)=ANY=[@ANYBLOB="2000000000000000ffff000001000000", @ANYBLOB="b5a3acb7c741e5e7a857a077ee9bf9a9724b458aca7a359897ee3831da653a958295c47f840e4049a32cf0a11c69aaff2db13c6befeb6bb8e0618e93abec77be34a6cac04059dbc3102d785b1fe606891ca3c2a1c88d3031d12570aaa627271cb8ab9decc83835fecf7caaef67d89c0e32241ee7d0a3cd83afc43111830844", @ANYRES64, @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRESOCT, @ANYBLOB="200000000000000000008d0b2fd48148c37fe65ae4795b13ee2c780f241fc669f40b42861922744dd84755", @ANYRES32, @ANYBLOB="a8056c9c596c0c76a319cec5d5b5d583c967724a17f76e1694d54b6501c3f1443d219ef97f61575c3d185ddef7fd89a77ee9135e8eb88c3a92886e8d7b07ceda0cba2a74ffea299d45607c6be3817bc1bfd009a1d87ebb4d9ef3e1b1beb02309cf3c1afd1a", @ANYRES32=0x0, @ANYRES64, @ANYRES32=0x0, @ANYRESHEX, @ANYRES32=0x0, @ANYBLOB="00e2000b", @ANYRESOCT, @ANYBLOB, @ANYRES32, @ANYRES32, @ANYBLOB="000021000000007fff000001040000004ccec7bd2b5a0843e88303ac0d001c73e080ddafec1735f2b825fd215247d7d028c8134aecfd4b7fc45590f4c1ebab1193fe9048da21eeeb94656fced3ac2fffa635d62f159feb966789d6638f30", @ANYRES64, @ANYRES32, @ANYRESHEX, @ANYRES64, @ANYRESHEX, @ANYRES64], 0x100}, 0x202)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040), 0xc)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0, 0x2)
getgroups(0x7, &(0x7f0000000080)=[0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff])
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080)={{0x3, 0x0, 0x0, 0x0, 0x0, 0x145, 0x9}, 0x1, 0x85, 0x3})
r1 = geteuid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000100)={{0x1f, 0x0, 0x0, r1, 0xffffffffffffffff, 0x10, 0x7fff}, 0x100, 0xfffffffffffffff8})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001840)={&(0x7f00000000c0)=@file={0x1, './bus\x00'}, 0x8, &(0x7f00000005c0)=[{&(0x7f0000000140)="f47081e79f9227118340cda6832f53c20db773fe854acec0e824c79552", 0x1d}, {&(0x7f0000000180)="c9ca56ad010144e824b0deb413c2215594dec89fa95aa326d6153af368a669b95427ab17a0be75b94aa6178224cfcf34b7267e121bebc5245113964eda0bbccb6e46b042f970685162ef3d8b4957bb1bb699d31fd1918b276b543a7f56e1aaaccc2b6001bc6fab89af79b42fa081c819a5fbc729d8a501b4fec0fd05013c8b2a037991aac89ad598e79e79", 0x8b}, {&(0x7f0000000240)="ff38d027edd506f1e3a7c2033cdcdbade08c232fafd60a0ee0c4e867ab64604cc10e4ce1b715d90f83a933c05fb781f35341f8349bb35649d85afc4eb647a5ae2560621405ab88ac5bb613f46736aa5929bb8cf92be535e1e99b9cce9bacd5dbd8f92c6b1f6e1b8759174da614cd3b8aeff3f3c0138d32c0e616e633667f1e973b0ba7f9c6678aa03ab8fe5036f39314baec4ec0", 0x94}, {&(0x7f0000000300)="2e9d3c7dfed083c35745bb29b834fbec69075c5e6ddda455fa76752610a64670708ce238263749e93f5e913ad773dd9ca245fe4247e10d8a2885eeaa9fc35bb4d8fd07d3fe7ab782712096712b03c3bc595dd025cea2da32af268f8f48fe996245b343d865d16516adbf1ae1f4dbbc3f003e2944d69049d50ed9d43fc02c211820489cd30515ca0627d475c7b63fb2a7140d79bf53185abf97", 0x99}, {&(0x7f00000003c0)}, {&(0x7f00000004c0)="c6a901e4ddb60e5b1984276905566fb4ab71d2782d619237d42da852a0188261daa091298f6e13e0feb2a348b3f10d856babb0d3a0a127b8fb21539ebdf37382b9db87b0937b8ea45c9972184b6749538e", 0x51}, {&(0x7f0000000540)="ab974f1a1db01c697b224594b691120556446da8d0338d75101d597d91e819ff81235f22aa471cb41019769e18e361de134523e9a8a9d6a8578d", 0x3a}, {&(0x7f0000000580)="2a76dbeec37ad85452f0cfed3c2894adfeaf1cce89a1b077c5bd6ee62b294163ec0a0c118a", 0x25}], 0x8, &(0x7f0000001780)=[@cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20}], 0x88, 0x401}, 0x402)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000180)={{0x1f, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x14, 0x72}, 0x200, 0x5, 0x1})
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000380))
semop(r0, &(0x7f0000000040)=[{0x2, 0x3dc}, {0x3, 0x0, 0x2000}], 0x2)
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r2, 0xc0107002, &(0x7f0000000100)={{}, 0xc6000000, 0x4})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e000f87622036b0001000000", 0xc)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000000)="e08bc6a1540e034001000000", 0xc)
r2 = socket(0x2, 0x3, 0x0)
dup2(r2, r1)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x2, &(0x7f0000000080)=[{}, {0x74}]})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x3, 0x0)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000000029, 0x4, &(0x7f0000000000)="06000000", 0x4)
r3 = dup2(r2, r0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x8, 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
writev(r3, &(0x7f0000000100)=[{0x0}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xf8, 0x0, 0x0, 0x0, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffc]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
syz_emit_ethernet(0x4f, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x41, 0x0, 0x4, 0x0, 0x0, 0x0, @broadcast, @multicast1}, @udp={{0x0, 0x3, 0x8}, {"6eaa3efdb1d5e972b531ce254730290bcd4c5b913503d3d8fbc47a6a66b62486eb843228cd"}}}}}})
open$dir(&(0x7f0000000100)='./file0\x00', 0xb80, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x702, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x100, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x18, 0x6, 0x9}, 0x4, &(0x7f00000000c0)="0118fff6360f9ea14fb359657f16666e9f97069815ca5835b6f65316127c991ab43afd5604c4aa10930ed14b1088b7d8414191ac6193bb01919a8a372208b127f29c66755d45d5ae11c6731aede78c4421cef62cac7d5ecb3a69b2e7910599897b40c8c7947c3c72d9ca5112b32966a5c0411d0be46ebb5981ea2de4000000000020a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66cd38f486f7e59a59a05bb689915b909800989d8d1fb9183ed44a85c22ad066d2bee08f7397cfe2cae6e966e98c4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac7340745175bd902a5f48e0a013a1dc24244ade0d510672dd77daac8ffff2ec00000000000900000047000000030000000000e7e3ad4ba11ca80000000000000000000000000000000009e39581885647e6b9ecd6bff6b37cd49c4287ed75b08a58f19f470bd87e5503007e0a33c733fc2181b57458e55df302e2d611ae3e030100a9edbd2d2d845256df19b563ef69e55e74120536a99d2a43575893f400c7c32ed7a1d4dfedd53dc24cb41b274925139f0ceb63553689a46145fc5d2c30c0d29de0815e8214f857ebd1f1e41bfb9a21624824a96d9619e00feb10a3ed95ef4c7d2798f9f87399347c7a958d5bb60a27d465014bd7652b7e5f4a46ca83eea6b48aeb60db0242a3044bc0955208edb0450200b34c238f90402598ad960ebf7502767ebb56", &(0x7f0000000040)=0x210, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
r3 = dup(r2)
write(r3, &(0x7f0000001380)="63c2a9def229dc3469a744fed203018344a6e7d4a5f5b0fae65ce9672b6f0c5bb851aa6955986f92d0ff88ceac30de35c1e6f929c6f6b29aa9ab33ba359387ecd89b8007fd18ba098cfdfa4933bb078bd3f0bd6b92963b921f257db30043674e70a92bf651d533decdf0749c55cb6614e8cce728cb31eb483c2ea7b990eb9b813cee8ded8357d50677e7bd61bff284c5702129e2f406ad9bc6a83a45070c81449bc78de48188171a755f08a86427f90cf5a98bf01317f2857b01f7b0d7b8239a32ae3e4a49192654bd9dfe24a7dc53d1ceb8cd33d74ddf1456bb8ad01a6284d64820b9f0109170038cb5e7ac0377950a1318799c9309788c9f23c4fa343701", 0xff)
writev(r0, &(0x7f0000000300)=[{&(0x7f0000000240)="e3", 0x1}], 0x1)
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000040)=""/157}, {&(0x7f0000000380)=""/4096}, {&(0x7f0000000100)=""/190}], 0x34)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
dup2(r1, r0)
listen(r1, 0x0)
close(r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r2)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r5=>0xffffffffffffffff, <r6=>0xffffffffffffffff})
r7 = dup2(r6, r5)
connect$unix(r7, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
close(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x74}, {0x24}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f0000000140)={@remote, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @broadcast, @empty, @multicast2}}}})
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
dup2(0xffffffffffffffff, 0xffffffffffffffff)
recvmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0, &(0x7f0000000580)={0x0, 0xffffffff00000000})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000c00), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0xfffffffffffffffd, 0x1000000000000, 0xffffffffffffffff})
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0xb, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$VMM_IOC_READREGS(r0, 0x8020560b, &(0x7f00000004c0))
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5901)
ktrace(&(0x7f0000000080)='./bus\x00', 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x1000, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$kern(&(0x7f0000000200)={0x1, 0x26}, 0x2, &(0x7f0000000240)="2fce5c27", &(0x7f0000000280)=0x4, 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000080)=ANY=[@ANYBLOB="e8"])
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400002ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0x20000002a, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
writev(r0, &(0x7f0000001380)=[{0x0}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x4c}, {0x44}, {0x6, 0x0, 0x0, 0x20007fff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = socket$unix(0x1e, 0x3, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x2000, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x2}, {0x2c}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000000)={@local, @random="3a6d1f6d3961", [], {@ipv6={0x86dd, {0x0, 0x6, "3835fc", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}})
mprotect(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x1)
sysctl$kern(&(0x7f0000000040)={0x1, 0x48}, 0x2, 0x0, 0x0, &(0x7f00000010c0)="64b2020a", 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc110445f, &(0x7f0000000240))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0x81946467, &(0x7f0000000240)={'./file0\x00'})
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0x80000001, 0xfffffffffffffff8, 0xffffffff, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f0000000200)=[{&(0x7f0000000fc0)="1596693388d4e8bb6df16e29f95a81fb895de567ab94e94f340ccf9b5295e0b5957b71442ecf83be94957b0862d96edb05ace734c4e80cbef22f5d8ad75885c09cc69c907ee5dc2f439aaf0f", 0x4c}, {&(0x7f00000014c0)="e503c61d80583de86ea2d148cca602eb0657023464e233112274b781731ade6ce55d6f7afec296ded73d238abef715fafd4878550b5ae5e0322e293ae4e5cd519280829bd61933159593bc331e33a52b5adadcf490f78b92ad6bbf96cf058bdf43ff7064a7585ffb497f84be4ee9bb8e49007e0dc1c79295cfd07e5a3c4505933bb40cf0456686eee9bb3309f16aaa5fdc16357f7633d6c343fc4234dbe18e1666caedac66c76d37692d1e8ce408f0e1e39e73797387106a9259ec87d601b009746350ca5a9ca0274690b50560de53220fc2815a78f45d277d5002d3f6d428a60fee7a15de1de6861685d366b18d5017d9d3e272705ac7b6d9b2a2035921ad860eedc636c7d1341a6c9c9cb2deb630b4dee1d54d3628914fcc0dc5bb93a1457844e5dd8808e4aeede385f70e080af31871a7ab1d9c9a78b1ed288ca2dcabd5fed2826f54265c8f75b487b3766f8fbc0527319298927930707dbba165bfa3c5d3432abd55fb9cdb41280eae16c186594d85a55842264a9b5421731538d9b392eec31ae109fd9813bc067086a8db7fb046ddd04daffb3ce94a4843621a33d5d90f99178b375e2848802e1b99b3b974d7c53192e469daa156e5667fef2ec5653c4c896a9280617a7de3e5c7f47c35342a81f72e4f9c42140017300b5bdc624f4e7c3be8c91e0fb87590d6e9ab1d547a8d41319e40837678d23715e0e5b0a4da9121eae6dcd6792574aef12d49ece93d6fc31d77214f0bb08da10bdbc6950c60b1ba11c44980e21f78c55c9ca817507356dbf3b62a0b588ba243835e8d59e877e5f8c459dc6f06a9596be14ab364fa616b71c56f12ed3a028cd3106396e2e2ef55a208ee4f12f1cf619f134e046d0cc28fcb2a465e22372eadfdb5e08f22309ff23eaaf5205705bc29729244fed42fefae8f363c70ed64e37845eb2faa6b8da8d143c75f6b23c92f527edd0576814ed90033ea760b1c2eeabaf5ebedb269b845541ac4a1f68bc13a9c9f65eb3d3e1781f4864c08775448f30f4cb4632131c23ac76636031f9503c902ead364154d460caf2b9371673f52e57bb98c181e86ffe291f30c1e4ba64be4b03b65336dcab894bc75bf5d5f505d8a8f0708ddb51fee9a47e3276e427becabd7d331c1e126746082c07c921ec1743f46f5165f6f9eec781a66ca6dcb08283f12b8c856c53fe7388ebecca7cb4dc9cd60321d834e0c85a8e8795f8ba6dabf6f1df96fad15749813a33483de3b853b821f2dacd90a970e513ad3b108cf6706e61c9182adef982c24b764e65513bcf17e6e169b1fe7da7f05ed8112a691964af4582ccc9ea61afa4aadf97459f8fe9df9a0d03a7e6b362514ac445da8cc06b8092be3049a0473ef1192dabf1ce41cd65d8fbe137b2317f2aa6fb8a12e1275ec5baf15bd3a0cfcb17280a53683b21bd2ec134b2d357f49231f1c49421ea8c6679a66a9d526cbd8fbd6a7c285f0882648a2ca16bb5561322ff5e046df51dec0cada6d75cefd5aea53191c3e12760a8490ecc308df8d9d4475c3ddbda945b12e7aeacf1cdfc27dc41979ba4c050bf04281fa762799ae06691a785979a5a97d45733e7c51606670384af46c06f180dab18a4c1028d5354222d54aceb5f8cd5f1da4b3487cc954f05a0b652d2d8adc1327f2a92aa0ef1006d70f121d7cbdc2c519654ae950604e83e23ceb77b2f02493cb99d7c7698f6281890950ecb6e7c46ce9cdff99b31e3eec77f8b4e0d78abcca03356606e0c394bc3513e24f3dd0e073e9d9ffcf210311505306a03aa6f4111b765865a19b4567a59aa582b54804a2a77b4a246af051a4b7cd22245206bf4d3ad6b022905197888f841e50f16b647c7c4a60e5412f01f99379b3247b2c4e57a627b4da2722d9f02e1c251b805242320c0e4e92d6966cf5dbe35124ee702e10850314f5a45c35fd497ca7ab6dc76a50b13c263136db5b0678bffdd7ed18d605f9dacf0346842f16865fd292d8937433fcc65c612042eab65bb783570b4b48178fd35deba95fdc39f3ddd325cbf79b56e53b660561f3f6b44fed109ed1e923890f8fe7c57d06d4ae5b098b2464d63e97fe533ab6d03616a5d7199cad8470952349a76ced2d458c28d6fac9338791df5a23d1ea52f7898db22ce959ca463592e39c656748156b41eaebcf58f4224c4c85a8656ab1916d3aeb82a0e4537b2aa1fda04575eaf4a56fbddadce2ce68b0ae8c53f3e0db0594ec9bfc7f95b02bb188489853a7efa354ab2510852422dc1d5a60cd928364f0e45ce5ecdf8fa6c0b6d6f44ddefa61de03cceb3eff026d6d308fd7d7ed9e7a9e7a8201d545524e6df3b8f376fadef047f130bd39a8b4de1dcd6ab5c6c4b53d7815828a01da78eed1eb097fb88cb21f6affd087fbd17a6014b491fa4609be2d07882259fe0d578e71d0028b986636dd2dfaa2fd1e6a6669177692a1ce82ce6317dd6d45e7a6fef72b56571848899f4bdfa66b59c8ac646a637c3df441ee5bb46af79a7d83e160f760126ef381167a14f2e79994893cfb774704f857fc02af519876bac4cc28bbcfa71271e051413d311a21d0bb748bf0d00a63ee0ee66e440e9e13e35b35d390c2a6f36d7890895ae7bcc828a9d10f61e4edfb91fc3272527711a665fb7817265ec9d02f76d625f824ff364b0280916144eec5597d70980265522fa783b9602355732d7dcaae870f2ecc36021285f39543bad16f343dd7df202049eac437eccd300b6c2510dd7a9fdc5f5105b0a726f6f841131d158d071f89b6d6f5ab6b4cf668d143c516fd5afe638a3d856bd5a5b8c455dc21b4c8be976cb988011bbc1c415382ee7b8c7c9d8515e847f3f4a4b939d68a9f8a54dea5ebbf8ec95cc7104b046fb6ac94ccc89ed9041446318d5b57072ceefb68532fe81bdb08e89ce9829adacd606a8d08e7e3c82c576c2516cd574c20a6e5a970338cf80d6730c7a8d5113974419f8638b6525c72542a3cb956fe061854854d91497adb52704a7b55922ad9a81a80de2d6199ba68e2539151cad00b48aa1555d7dc40d68a923a21cea364e5dd6f90c3fac3e582f70ac2ec825f9c2f8c0622bad1bd8e6314c3b9d2fe911aa4c1f4431a0788e4c78274cb6efb4a5b0fa94d65ccf2a360de8fc0bc37f28f36ccb7af17086bf48769fd8d82c233ab6eba6498601a9c039bd59ddd0c3e5bb028f425b70dd098aea7afdf5de9463ac370fa5f21eda4415031dafe2a6ad1f4ce1da80bc2931f43e091c555bf5e5058988aab182447a6dad586cce18c369f2f9ab2e579fdf18700ba65c548d6aae5bb53e803f143f30c5b8bb547df0ac5b10cbdcc4f7776fdfdf2f9d2d7d7b3d38a8f54397c9cf17e93c7261107c669eea7bf9bb7f5672ac379cfa7c38fcd3909df05918ea221ad12d8db3010ea52de9d175e7a85a0b35b1bf5a6191972f0ab63f5965a9435ef405e9ea96807f4200aed1e2155ef5ed580749b4a5a187520ddec62a30df3ff92bc863f4c79da5cd929dac6e71271b9d8797cdca3179b85dec50cbfb784f17bbb7c09957803f6ecbc2d34057c94cfc32e92162bc82413caa2d3e01fc44f466baff726420d708dc7cf2948ae7e2791a006cb46fd81bcf00d3a50301b781bc416e91edfa9ac4a001a7b2bb40abba48ef37c5ce95da335d3ea561f12a174d553773b561ea550bf51d17ed1d8c5a1d7953c86e0be8088f911ca3026feb956c5f6a2d9170051dc97e49265a0109a9adb59a93248864b5b6600161b0c9a7b0217a2e3e4639316a8b7c39f97b6d6f2619f6b5b028c3a1abfbef83d42bf03b3e5b6c6a64798058b29321127d921730bcaa89308778ec69655fcc479484b46a775205c08b35bce05e4c5366ae66b5841f5cee79c19139ab4a0e534433f2cba2c91b71dba64eece54b8e4ee261718e7da621c69ceea4b3f03585e85eef77e19b76518c45cca504ce7e85788288e5ca5deb0def30760f2112ecaf237103c9a9743f8d9363aee6c9739a1e19a7fcef68823aaaee87bc601274a9d34b3fea7fdeee8b5295ff38bdd7786613f0b71c7635d6d529cc9782f4926b754f76e312f6818138912d2f45d738400d0b9d40ff59bf8f388736e01656d3644326d5d3952c4f16833258771cd79ee6020e233e2a351abca16b5fc1be0d6eb93d00d7c0b76419c91464aa35d9ff69896a57506d84808711070b1053e1c4496f785fc0ae8dbadab036004", 0xb9b}], 0x2)
writev(r0, &(0x7f0000001480)=[{&(0x7f00000001c0)="668ebea2a6d10614da47d9ef461a1757967925bd7b3d418388532c830ee0dbbafb5b5a7f35093e", 0x27}, {&(0x7f0000000080)="b4b5d10a8b25157dcf64c002c70f02f42e10a46c2eb46f96e27cea8bb8ec40aaaf98d019f5c505db9be5859e3946ab7b3cf86b8b01761e1138566b56c52f330148c32a6647c1eca95fafadc8b7c6c68a45474853c677ae74a33489807d00ed6bc6d8ee02da08bece44226f570b87683c9501a8b7a6e64a42055f712b67b7e3b20a01dc0e092abce9a04b1674ee2b30151847de4d9d6d07af8ad206914dd74b58642e2aeedea7275649aae4555ddec5c4427ba11d46d7bba8bebe9940861c0af9cc2e73fea58a2d6f34414f6027473dee4a5fd65ff56f47bd4a20b1b9cb213ed7ce27d570348acfc5e397728cb7a841a9ea8ef36260e76d", 0xf7}, {&(0x7f0000000280)="ce9f97e1d72835785b27339046496cfab6fffe87c260269d86df477427f075cf16e705bb5e296889f2280748d943d3f9116a96474a52b1ba348f6da5a02b9f7f77888c61db813e69201407a94b9263ac23301581ed91dcc0fe5b731a3eec7062333d0722788ea17951cf3fb454595df2a242b40629ea8602fdbee0ac1d244dd492ebd97b9e416d0153be5814640db3c67607b3aeb10207e0a90c7c9adf0f7d6d7a9fcb344c7f2da91ef38d49caf467c29f994799c1731fcfc95be0df5b49678c6db9e77a873e5d48a1d186af187181e06c462cefa9264bf0b2349e280f3c80bb2d9b4a80804b6219c223ed03e4f714f2da770d097c", 0xf5}], 0x3)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{}, {}, {0x2}]})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x801169ab, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0206921, &(0x7f0000000300))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f00000001c0)={0x3})
sysctl$net_inet_ah(&(0x7f0000000040)={0x4, 0x22}, 0x3, &(0x7f00000004c0), 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x6007, 0xffffffaa, "919b914c6f58d3ad9f7b4acbd21500"})
ioctl$TIOCSTOP(r1, 0x2000746f)
writev(r0, &(0x7f0000000100)=[{&(0x7f00000004c0)="0d9cf1fdb6c86ddc13d68b", 0xb}, {&(0x7f0000001540)="60c1d1332329fa69773a7284d200f404eac27a733a627752ec4811c6a2a66976a4f095a9bdc225e17e76083021419578ef703951de5cd08b99ba5433ec4b402ed8a691c615bdffea683f1925685af1ad82f2b90aaf82666555eb1dad63c12d90b5a015907eea03ffd1f3e5a5641370565ab63b7d451473d4101673f36b80c33df3f5c0e9504e9f2a169a4c95ba3d4b9eb0dd805ec6b3b22cdda375ac19df130f9c652df637d1c482aacf0aca92bd0bc206ba26838114f93e595dc93087379479590183e667ebd58cc72d98042f5e5b22c17feaf4bc25fc41e555e18656c4f1bc70ba753c3c1e647c7615d9571e0be109d53684a15833e52436fcdb264a738632086c9c8fef5250236b3472e8836976c926c78abb8983bbbfa9ff95447edab504b8baddee210dc3d4f83fe741aefe9518c06129b6fced1cb90c04e634d72edfdcbf0569ef725ff80c9fc6713c58f3ee4f2d6aeeb96fd5ec487ec915439e8684f242ae199ff6896c9dfe0b30d5c9d00594b5aae58e5d6b7edf4a9a2b1a83819661c91c9522943320e82c4ca58995d5c7f73156c6a583f5b61863992591b3d7b2c75b1fe7f0d120105b8f03e9508cbe5ed27347ea5aedd58398fc8d00483f8af315d4f4d054fa3cd4398044e004e31f107e6f87637fe87edcd7f5f9018a5ddeb00c434a52ba177fbb625f4d5c651699394292d3b4e706722a0f57f840c909739482edecb1d0bcc99ae85db549a593cd17ed89e6b42b6c0f861e0125e9d9bca50381531ce1ae65823821ed2bc8ec0abb889cfa284bd12d321921b31e1a51850784f11f7ce2e0ef5f25db1a1ffebe99aa2fd141cdbfed97679b0414ba9cb9cecd564e10fe9d12e2693e6483f14de843938a6fb5bd2253016ddc58e60fd2d37b3347aa854b54a350834dfc5553ca4d5c18f986d2026e747f64bfb9a2817f91d4d603a09dc31cdb6282e01f577dbf8eeca58534b9152a977238866647acb04c12ba4e137289d76944f1d4548ce233645cafe04ecc107216686e11e44ff4384b84a65d04742f71b3cd87a9f371418ebbc8a87d61e9c4a8186ec9ab9905d4fa754529a8d6a7c0caecb9cbf5d4df61c3d28715369b3c136f32e5b608f34527e5d0dd284771ee0dcc1c16419c4d4c992a193817c8c5003e109dd361c41d77cfb11fb1fc78f956706487f092af90368c75385d67115927a31d3b13d5835bd8f54648311fdd373dde5caf3fe78b063986a2202245967c159aba26959e29e8641835ccc2f997422992ac4461c542d498cd5181d1114b070baf0151906c3c5a003c87a17b3bf5b88b3bf2e0d378715e8013bdd884c325300a8b7c8bb14fca5fe96dc15d147097c8e3d2b52020dfd5847177827a1d184f04be571735d5c2d01eefb8fdb6e5f211aa6b6232064a89563e31f57dcd204c9e8f5a3ad5f4a983a00c8c97d342937a70d981d1b579397e243b9cee258633a3fc1c1e921bb8c13c64f261dfaa025c7b2a0a8f0d676a6fb9f08937252bc9efeda6b6f9528cb49af31991474d2487f7e9240b0f53f8271d8fc4236bd434c8b8eb3e0bcc0b576bf91aa2078876567eacf1eafe45d974da05adbde111219bd916490c661df20ac8bfc5068d2c22605fc626c7ea99f7481fa9cec017132c0540874a95926b7463d9767ae7f5f3b31fd78855026b84ee8ff1b0b6485779fffa204caa0c37854b288a486e7c85191dcc8ca5db12352fa89d5ff5efb34e50375505f09ee04d850526b78dfc37bc94444afead64c2d55013fb67b923bf2671b90559cfdf79e3add9d6b26b0942f9bfb08652a324ed3c527716387789a3f78a4b25bfe718ae2aefab6828b1bfe46ce84d3b384182b76ea5f5fb5fa99591f0d4954407b23dc9f71b0124d0d9819e3f7d7071b0704cf21ae66b06593c44e68bd3d508ec30754a0dd0b86c67f1c928caf8654979f261c2e6cb97f71036eeee81694b48dd93e28d2612b50c59910ea10208f6f178015dbbfb74ea0e200a5b135227563b99778eebc828a9951c816eec38e7c54cb73d0043ec02cffc536c260b1d8269ec7d3e9aaf2a0fa22aa3c63312204dc0472c402ae24a15e2fb6772d475b50cbb3eefd13b5828d0eacf33fd1fd81767860317fe88e109df3149877dcfd1d089e6c51c0ff96ccd8451648bd4f67dc063f95b8194c498051ea0baaa2adc68c20c7d880b15c855f7c8cf02c78742e98c654ba839307ee092fca4cb8807874456e36ac75850918776c6bd2bd1227ab480a8d14b2afac3e834594e076e5930bfcf34fb3d92fd0fdb4d55bc60afdddc507322a7179b67a14fccde8532a6e51504c5b81dec7d1cee7de078e650b84c9ae15061134ea559beed1c347a207b138198d92834c0e166a814b54d009fa91424ec5a9f4bde33839b99fb1b21d1b2ac4692570c1b8d04a37c0b10f7593bf66d0e2d9394d9e7986f2998f503837c2f5183142341e8995d764d90003c20abba2d7862a1d76d1b04159825f11f190a7901be36eb446cc78b62931a4c7e430bc00264c5c483a8be4e711164f0bc134314411c4df7bf5e1aa82ba1fc1d50472a0318a76181e0d9092de627fd859f652ef8b73b9039c4bb5199d710cc6697235c59843f43d99a2d6875bc1266d789c124e4f4e2a1263aae2b1b0973a7950e2f0072789c297e80bb4de0b6ea4b2ec46bf4d73981d4dbbf4766feb415cb3e7747aee466ca81ed12f4552e00d45c23d67446f94edd8bb5e5a51b00e322b5081e594af9fdb2c1154999e5312d59c2fb20535adbedfbafc030a8380ea8cea86ab38888c27bd4108abd569580666562d6a8ef6971e45c9097dfddb77b7bfc4fabab0fdd0e3ef3861d1a33619bbb324805ea4a40b51541cb89f81307929200604e9bb4358ce7edf61db19024058ba67f1c1c7a9778e60d81482d6f25fde8213c7392e6b70a48533c9bb7078b5ad20deb12004cae9cd78e5c6e3d1ec9da693f4fce706f992c178f52f7c0c7d34f21b91aab98e1abd929550fcdbf6049776e29e0ea29ee4df8da72a2051f952fbfdfa178c535c9d92b4fe05f36fb08c663a90ce213fdc6337c19220e39276880977d95a7c8e343cfb56caa206557da4ca5bf874a23e101456974025e348731d015c4cb570f5626c8a429bc82f611872be24009a618b7800238b83e6fd16748449fb0f79a87f0e62f6ed8809ac9a7aad4b26ee445dabdf8a7b60cc5adf1ab2e82103a69188425cb8bf18fd6ccae51da14c843d0e795058af455da029c6bd5d6468a2d2cfc7c6f6c66e1d2794503393e95c78197b777765770058614a115211508952681163ff70f16088d5c777e84516402a7630d2832ab06dc14393034db58", 0x93a}], 0x2)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x10000000c, 0xffffffffffffffff})
flock(r0, 0x2)
flock(r0, 0x2)
syz_open_pts()
syz_open_pts()
mprotect(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0)
msync(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x4)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r1 = dup(r0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x9)
execve(0x0, 0x0, 0x0)
mkdirat(0xffffffffffffffff, &(0x7f00000000c0)='./file0/file0/fi\x00', 0x180)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
openat$klog(0xffffffffffffff9c, &(0x7f0000000300), 0x20000, 0x0)
fcntl$dupfd(r0, 0xa, r0)
fcntl$lock(r0, 0x0, &(0x7f0000000040)={0x1, 0x0, 0x80000000000004, 0x3})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1e}, 0x5, 0x0, 0x0, 0x0, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$WSDISPLAYIO_LDFONT(r1, 0x8058574d, &(0x7f0000000280)={'./file0/file0/fi\x00', 0x0, 0x592, 0x10000, 0x1, 0x0, 0x200, 0x0, 0x2, 0x0, 0xff, 0x4})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x5, &(0x7f0000000080)=[{0x1, 0x1, 0x1, 0x7ff}, {0x82, 0xff, 0x81, 0x8}, {0x6, 0x25, 0x6, 0x7}, {0x0, 0x7b, 0x3, 0x6}, {0x3, 0x1, 0x43, 0x40}]})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0x80009)
ioctl$TIOCSWINSZ(0xffffffffffffffff, 0x80087467, &(0x7f0000000180)={0x2, 0x0, 0x7f, 0x400})
r2 = syz_open_pts()
close(r2)
poll(&(0x7f0000000240)=[{r2, 0x4}], 0x1, 0x0)
chmod(&(0x7f0000000200)='./file0/file0/fi\x00', 0x188)
ioctl$FIOASYNC(r2, 0x8004667d, &(0x7f00000001c0)=0x7fff)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x80007, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x74}, {0x2}, {0x6, 0x0, 0x0, 0x7f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
accept$inet(r0, 0x0, &(0x7f0000000000))
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000340), &(0x7f0000000380)=0xc)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{0x0, 0x0, 0x20000000}, {0x0, 0x0, 0x0, 0x100000000}, {}, {0x0, 0x0, 0x0, 0x20000000}], {}, {0x7}}})
ioctl$WSMOUSEIO_GCALIBCOORDS(r0, 0x41205725, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
getegid()
pipe2(&(0x7f0000000540)={<r1=>0xffffffffffffffff}, 0x10000)
ftruncate(r1, 0x3f)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
setreuid(0xee00, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f7867bc, r0)
r2 = fcntl$getown(r1, 0x5)
setreuid(0xee00, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x8, r2)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt$sock_timeval(r0, 0xffff, 0x1006, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {0x80}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000080)={@empty, @empty, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @multicast2, @remote, @multicast1}}}})
setrlimit(0x8, &(0x7f0000000100)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
readv(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)=""/57, 0x39}], 0x1)
r2 = dup(r1)
writev(r2, &(0x7f00000004c0)=[{&(0x7f0000000180)="f8", 0x1}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000003c0)=[{0x1}, {0x54}, {0x4000006, 0x0, 0x0, 0x10000402}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0x1136, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x200, 0x4)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x2011, r0, 0x2)
r1 = msgget$private(0x0, 0x90)
msgctl$IPC_SET(r1, 0x1, &(0x7f0000000000)={{0x9, 0x0, 0x0, 0x0, 0x0, 0x140, 0x1}, 0x4, 0xe83a, 0xffffffffffffffff, 0x0, 0x2, 0x93c0272000000000, 0x100000000006, 0x3})
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x3, &(0x7f0000000100), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x612, 0x0)
writev(r1, &(0x7f00000000c0)=[{&(0x7f0000000340)="4d5b4b3898d5b15518685bc8fa1102a6", 0x10}], 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0284459, &(0x7f0000000000))
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x10000000003})
getgroups(0x4, &(0x7f0000000040)=[0x0, 0x0, 0x0, 0xffffffffffffffff])
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setitimer(0x3, 0x0, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x1})
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x4, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000003c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x44}, {0x30}, {0x86}]})
syz_emit_ethernet(0x36, &(0x7f0000000200)={@random="3575cbbaf45d", @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @loopback}, @tcp={{0x2, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x60}, {0x3d}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$FIONBIO(r0, 0x80047476, &(0x7f0000000080))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000900), 0x0)
syz_emit_ethernet(0x76, &(0x7f00000001c0)=ANY=[@ANYBLOB="ffffffffffffaaaaaaaaaabb86dd6000000400403a00680000000000000000000000f8ff0000fe8000000000000000000000000000bb030090780000000060f14bd300000000fe8000000000000000002400000000bbfe8000000000000000000000000000aa06"])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f00000002c0)=[{0x44}, {}, {0xfefe}]})
syz_emit_ethernet(0x4e, &(0x7f0000000300)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "cfe624", 0x18, 0x0, 0x0, @loopback, @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@mld={0x0, 0x0, 0x0, 0x0, 0x0, @mcast2}}}}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x41, &(0x7f0000000000), 0x0)
sysctl$net_inet_tcp(&(0x7f0000000100), 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x14, 0x0, 0x0)
socketpair$unix(0x1, 0x200000000000002, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000002d00)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002c40)=ANY=[@ANYBLOB="10000000ffff00000100000000000000"], 0x10}, 0x0)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
close(r1)
sysctl$net_inet6_ip6(&(0x7f00000000c0)={0x4, 0x1e, 0x2}, 0x4, &(0x7f0000000100), 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0xffffbffe, 0x0, {0x0, 0x20000000001}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
unlinkat(0xffffffffffffffff, 0x0, 0x55dbd912bc955e9e)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="903f79d03bf2f62a444528d671e3aebc37421fc53e2f4743c2dea19d5bdcd7cbaa3bcf29c1f563a7bae1183cccaf1abf1913bab8bc6d1ed2e545efa479e612bb6c81323d4773023044c67bc7e3035689ff4b53b1c130ffe3a0a17aecc658494dcbb3c063c642490aee30806f1c365f5a172ec8f5d6a4c99b78fb5861ab18beb73fa8bf6366597edea56b7f6be7ed26e82b14f3135bb080a43b134dbaab39c881bd23f07da9906a669b2dbe56fee74ae3eaa8bd0cf9bac69ac464225f28ebcce3e27da7d61c", 0xc5}], 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x2001000000009, &(0x7f0000000000)="3c380652", 0x4)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000340), 0x8)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000280)=[{0xc0}, {0x3}, {0x16}]})
write(r0, &(0x7f0000000300)="ccf06c9bbdc73dfb7c40df5037ca", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000002900)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x2c}, {0x35}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f00000001c0)={@broadcast, @random="204d0d81ff82", [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="7748fe6a7536", "", @random="669a031b835a", "63de99960bef38a3f076c3f9fb9b5db8"}}}})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x4, 0x0, 0x0, 0x0, @broadcast, @multicast1}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt(r0, 0x6, 0x1, &(0x7f0000000100)="f6b0aa32", 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
setsockopt(r1, 0x8, 0x401, &(0x7f0000000200)="c5d62ae5d042b64200162d908e7fab5fdc1d90e091bdf59cc8dc165761c95e6303f98d1b9ce33abb7fbcb9394fd0953f2f7a367aa023e0c9a4734366966be81be4fce4fa14650e3b479120e115e997d4307737a03adf7e13a486eb3f581db12e3d46592d0b25ccb3f6f93ed5fde2e6e6d3cf6fe983760c0e5a9cbb5c17876dd20f2f8b6db177e5c881bc89b5b5a0e5fe4e5766033d", 0x95)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000000029, 0x3e, &(0x7f0000000100)="2c16954c", 0x4)
r3 = socket(0x18, 0x3, 0x0)
close(r3)
sendmsg(r2, &(0x7f00000018c0)={&(0x7f00000002c0)=@in6={0x18, 0x0, 0x3, 0x40}, 0xc, &(0x7f0000001600)=[{0x0}, {&(0x7f0000000500)="d345042e731a5d77eb05c4cae53f0644ef8cf22c637f0c6624709d8911c7e7b20d48e15d7957cf6035fd05392477c22bbd73cfe6bc6a9beb976bf6d164e3ebbfef0a0f020f57c811ba973fbd15c5435ec74e180f9282995a", 0x58}, {&(0x7f0000000580)="8728f925462e03c8286b2de784eb6d90305e35abc2f5ef21170a24ca307fb32df18106632333d77edb5d7ab7b8dd9629f79fb18bf294", 0x36}, {&(0x7f00000005c0)="a3ce6a3afbf08b6294d500cb6dcffa9622cc29f5724709206a3fef699fe3bea2e0f6dd91ddbe396a633414fe182660f0b16d4efa32f1a8ee1a6f252ac8a760be8f689869a1db259eb66a2205141a34391a46da6f833c7a3dac7a80e25d885f00ffa343d69085b4bea425d155144301917d778835a1aadffdb6461a1fc0f657469177f7a8fdb47587ecf1ee138af96c14a4a77fca8426fae565828627a6df2b3cc213a8fbdbefd41250bbb230f50322f996631f89e65920171f7a70ed62a3e510e4ee15bcc79c7743f5ffa191ec0b44c1ff7fe29088ec99931a1fca6135db248e7c7cb386376e3f02e86a6b7141eeadeae6ffe2fba94d0fc9b106d207f54c2644f6382a73d3a0d840161fe0ba440996bf0fd06dae0cb9bb68c6d009092378f3315ad255817b7a07a564665967da8448d992d7c38a8406ccad0a8db60a8383195be27a456042cd2915aa11ac0cca2ee380b64467b29b1015a90b539fdf7ee98dca46693239a847564dc2f224b71012dae3b6f4bc8bdcf78d0f88223d778ddd002ce9e2c8ca786a526f1feb8fdf9e476591f1a829b2173080130020a47fba457452207c26ab8e8a4297e0190590028778430c6a9168957381de2b5d0eb44cfa72781cdd92113a98d929d5dcd3433367d1582b416501c28b665c82fb44901dde37a8400ce506002675ab5a02d2d7a917399de54cdce9843ca86379ab9529e511d46a5482eb7a67a01e74ac9dc5c211054e369cbe4ae50b125eed055a7da7095d182841a92bf9294341e362eb17370deba15e5e425640ee0bab65db0f294e57fc9c53fe5c08433db2f84eb46eb1e9e1015e77d59a3322f66bc5c9c63604415b5c7e874e207f62a22773a97f0b1f0caf797fb096705fa480b268c08db25cc49bceb4bada9486994a1561f0449c21559eedcdb19a37dcfabc20586cdf44165787fe06f4fa8a12cb8e3dce367e2325b899ee7e8e98fbeba9e808c7ef1261fb6318618877e6504021ee49312603fcb322f74e8ceff0a41233bc1a958f6feffb943a8aad62d023ea63b53712524e700fcf898af3db50c0d76a2dfb0bdb2ee12a78b6d553fbb18d48b7d8594ff81e2cdb8b2c070a6acd740bd2d388b83eef1c8a2865f7163324b17b1181835653eda7eb69b24d1f18a82439b4e5c2d65b032c4d4c1331791edb11c98eaf2e0e94cf47816b3d04e68352e158b1904d7baa548775a636084655af000e07c3ceacdce9a6f1494af11b8e57bcce8b1c5e8f397a88144284a2aada83ebf1da1f8858cc27e61a3648e3a846929e1e0aa017c36c7efaa6a4d9b849b02bdf981fc8e636037e8b50e0856a7fbc39613caddca3e7d98a1d02a4a127b9c5f8ca3594f13e568ae137180c75f3453e6ba2dc1c187b58b94840744100f77a76d1b86f8b9e0f89b47afece99db9f1c9f0bfad50193364b1273f2f0c914fedc1458776c65f57093ecf433712c002e195f6542f6dd2b59377a562ce028859e8027485f86466aacb18d1ac360bd8f374f41758ab33d9ce7e0d94e32370848eaf65f248e2a3ada5b6d315ed603f7634dcf5e6bb8fb4dca5791873f6570667c116e3b053e5bbf51afc128471a4987ac853ee314f9e51a1922bcb236674dfaa6631a5d3783b6f8cf5d42f12591c0222e2601081cda2ce0e6caed33dc50357132e437a6a39ae15bbd996578da30e388a6382f1e4a5f028e8a8db0fc3a82cdab3d03f1cdddb8c4dfd44f4d66e4a92ab11d21d84f35576fb54659ffe43c3dfbb3da29ae20d2f23c26327a6c9c566996122ecea914aeadb32cacf77c69f991555698e29417291dc5643f626cc93598ab89b8938d750f4321546ebd5fa54df0978a8e88a6ffb0a664e5275c7f3a77094fd876128d70cb5b924360003b207c16579a483d9a20645fed6975af3a2e4a5d02c1933cc64720fbf85836e6369b12884fbacdb64881ecb370d4a71a4fb0b31bf09c6d867c3baf10102f470aeeb045d243ae7ec2b863948f0b679d7075aa5922d10048c67bc28171379bbaa6b9451e72ded353a844dc1ae668db0ef45fbd7a5dbb727519688987fb4c72a83943baff4fd61a91ed8ddf1f5966c65d376999a32bcb179d5992498aac44e03d6488c7eef9ecae54c7fb5b3b27328e3d9e2526ca56744497827040bef0fcdd3ef5e6eb97fd0e70ca777ed5f446cc7b12e2fbfaf54a71dbc0179fcd7966cba107263bf6e7383c4db17158864044d71293c54741dc66fc0c7ed3c612a18cc27c608016c87beb4a50d0f1885f293be63152eab5b61f4bb0333a65dda61a3a61819fc0c2ad213a2716c06f501ebe8c60eec8c9d22bcf84b44fe8e616aec3e3909b2dde94a5dd9344763bedadc49b35577bdbf8970873a590d3e6ce042eed48cda74f7e49c4dd912a19ea3e25a050acac12f627d7171c216941202307af9416343332a07b9f23ebc43784e4546dc8e3ba9e226a618bd548d7839fd0d0a2d8047380e81ae9a000f125727bb8b6c9cc40a4133d7f56ad59ae1b1bd455d01835680718592bd1017600c7a629ce2913dba04cba76a46ec01b7b85b5a8fa96c934f7d55708c2df357cc2f6aedfb2e57c853d45a0f1548c20a011e95a6e89c18edeaffa1d6b93a9847ed819d7362cf49904deb3f5f7cd1d6c3112fe791e7905360ddc679228ce53ae4875a14915ce7c06c4df9ea0e30c89f846cdc4e0a94de56061dfe1cfab286f201b660f42d88fe5c65428c8b8f8ad190caf2d6bba88c95e79202e78b77b1719131fcb3fd81080ee698ddbb1425789ac9322df52bd56b085469b4904e2f82dc3ea9f6bdb36f869a4f46205eadb4d82502fba138731be001f037dd91202a8101aa65d18f7564658336532e46b11f05ec542c2d1f26d38f67a373b03dc5eb9f6c5742037826fc31932ebff9d43150db8e94c5f32723dc09158b10f2712f5f3ba5f6d10f29517a236d9b5229abe338e725c836048d9930771fc7028c91d8205791c7b198dbf3d226ae5fdb5103e38bc91b974daf21db159a66db28a01bbf1ebddb8b4694082c9f19c4efa7dda587a7523c848f9b9506261b49cf56c8f81a6c71e027030831a755102d425a439f27494f2694c8617728d169fa1875217a429380e7594f7fa7d3cab112888b5065fcba231ce395ecffe7a3eed2708a3ef5c50021937cecd4af6122da6ce44d2fc37cdaccb58926f53bbc41c0e1768d7f3a018b9e2272ec2fe8ac31f6cc5bb39d554eefb3f198e4bedebdd244a8621adff8cba8f3a22a2d3edebdcac5e97cbe4e716d286910ee2968979a393cac8c70133d175b689570c372369f72114fe2194cac5355f554534b6e87634e773d8b2ba951aa64d77125c7486ba544bbe3ac8b1cddd5b025c27762a35a89493f3831a65bd2997648e933074a299468083ba4952d6bb3196599ed42feff52bc3876582f15232603d5459450aef682f1447312132b23072fb533ea38119dd89ff1391b2d59f575e083d76e824a0eea9ba0a38a565060c9cf6557ba7a7f5739bb5b14735651d998899f6df16ae00181349baf4ebe8428f3f5c8c117d9b912aa06012063023b3cbe2f51bec73f97e95935302d46c909fab98c6d3eed75c2eb9138c7ee6e5e9b28dd0061452385a8bc6fc5e13b313fc5e42e3443cce458226ab418d32a43214e9ca0eff497bad4d72c0710c38be2bccec0e1ef706b81220bf5528f741187cfaa7cc6d457afba34cad3ebc8d1ad80b9626a60b0513bad52d66f4f34bc6f675cc8d2b9652c8d047aeaca56baec5de83b8a9d3364f4ddaf363e7df59d7d934c97065cbafecf5c7afc6c40698619c3d881caf85c1d399d7ba8de48370a011b759eb503435ec18cf58304157fff4e41047c313d713f0c2b151da4252484f28bcc2a720f7f779af0ae5e829be653803abc510bd980eac623fd8d0fe627ac8e5b0617a6ae9575e5ffd4100b5077ba23438ab69162860d55e67728b6b0cac32f589278435c9e4a73c68e6d1d96b6524b4069cec487d2f02d54eb107580604ea851b570359af44fc7c0a9b8f7838d9e75c76772f4cb676da58dceb000936dd7f24a0961e6d04c8001f357b11ab827595bdfc51514f96c7ac5e094790388b6307133c860a22e1364528b68dc21943a051e292da5de260b22860aa67d127503836778de0b6891e89bfa75a1217ccde006ed6fac993369a2b0432297d973cf9ee0b3a4ef81d3756d339e6f54c404839cf2fdbda6926c8ac3791148ef98ed8d6eaf278ff7604ecaa3ca183f225f37488d2cb0a710f9962518e51188339d5dd5482ed9e869d17421ed56305a74ac8392d76c1f907e313abdde4aa9d037f5bde3f078e34e60d4ac074319d4b468c137a15c6b66b9bef2d91d1bf3923e30fdc457520166cea6cb985d01726e1ebfcb1206c4fbfde71049993990bd10ef9a52db91899ebed8c9a2bfb2eac91c6cbd569969c8adf54903136ca05ca9beff791aacbbe0ff37ba3e504de209cdcff9663d35634ea76ba2739e54c052572136d38b36e5ad0d3f615a6c427f160fca30df8d991985c70f5a259cdc5a7169078ba7bd26cf5d6428653f47060a4e0a600d5762bd5f98609e445e41ec4425be6f3bb23b3353c85952551a6f6525ffeff625c3ca6334484b0d1fbb71fd823fa897c77550dd3ce60ab00d3e27565123e8318c74afedb76b61e2a26a34d9c03f16e3abf352e4ba9ce887953f23448e0c9fc79f633c0b2de33ec9f7ba92022d464f34f9d21ce65c264ee732d420f25894b6e4408cc389baaae24c131fa2951947c7965babcfa139a34356f40ec6e82eaf76bce4ffa5b3535bbcd2499f6b0545b030bf93956417542cf7f697c1567ffaead0604d5c86b1530e2c4fa178f2d810e22712ab19ebbd95b1e7092122a93949102a17caffdb1ec47422c496d44f27a9dccb8d88f0e010f6e1880118bc1c6d18da836c6f3cc2655e28a933ed8a4440728513eaf4766aeb738009a456a5006fffa87849c817e3df6d68e672700ae895705875fa9c92d63f66c7bfb3db2b1413e61e3a2e48f8e22ebd43c36a6c9671810901670ac955e86f8c981f16d2243daa36f3cd76487007d681ad8a962e27abbe07f72de8a73840c979dd0abeb158096ca5e75e8e575909657378e6f6083c7a561b3da63f27dddaa8d1d984caa5f483c4827f78220af82f5288a5b2d3c5353974b6795946e4daad89a3cc4efb331e3b72585f84f52864cd1563cf93a1a58a9a738bee8d0ea13b20ee135464fae66d115fe9835035d2762be27291afde196acbd2ca062109b22ddb6c9853230b9e4994021262f9283aef8a59e9a4e6d1143c28ccc9c29982d579d97e464d6063724dc8364a30550d4121bbde2a1afbac17f4f9d0c256d9bf1d435ec97c6a9847dcec4d67bb7012b6ca1a3bffc52dd709e977f76e954f25b5c5f61fb18c216c3899b3ac7bdf036164523c1347f7fd265365241081dff562f93883a35b9edb0fefa08d9cc1bde7ed53ac719b8511c4da2a7a33458b9c5e8f2603b826365b7e38f4242f92cb79f36f32a3c7cb920443cc2724eace5403995324156dece9ff712d4802f879c7815640f8aa20e5db6ce28636a5d52058b8a7848ec7455b193bb2b6d685d67cf30842d2dd9b74c4db9905a0880a99023a97b119abe1a2176595c5e8a3dcd052fa7230c305f967e12eb855357c9b094ed9b253229ccdaef6ee86d1ab7e927aac13b7aa32d6ef6c3b3c7c728d01ec6525e9286152f0272cef418011241cceb12e42e0ec7ea4bee862233d9f3f3f2c2c13f744770daba7ba3b32e15d3eefc5f5e5104928bb84f2986b6dda355559b2edf", 0x1000}, {&(0x7f00000015c0)="2ee65eabd1786c7f1918d0875841", 0xe}], 0x5, 0x0}, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
connect(0xffffffffffffffff, 0x0, 0x0)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001008e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000001c0), 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000180)=0x8, 0x4)
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000000)='\x00', 0x1}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{}, {0x20}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000180)={@random="27aacea59068", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
pwrite(r0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x11, 0x1, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x25}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000004c0)={0x3, &(0x7f0000000180)=[{0x54}, {0x81}, {0x4000006, 0x0, 0x0, 0x2d}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x2, 0x0)
dup2(r2, r3)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
write(r2, &(0x7f00000000c0)="43b09a0d0f2cafc8d482e82d036e85fceec8cf56", 0x14)
write(r3, 0x0, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
sendmsg(0xffffffffffffffff, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7afb51e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x8, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x400, 0x0)
ioctl$FIOGETOWN(r1, 0x4004667b, &(0x7f0000000340))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2], [], [], [], [{}, {}, {}, {0x5, 0x0, 0x1000000}, {0x0, 0x0, 0x0, 0x4}], {}, {0x0, 0x0, 0x406b810b, 0xffffffffffffffff}}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd60000000001433fe000000000000000000010000000017000003"])
sysctl$ddb(0x0, 0x0, &(0x7f0000000080)="00000000eb", 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000080), 0x14)
pipe(&(0x7f0000001c80)={<r0=>0xffffffffffffffff})
poll(&(0x7f0000000000)=[{r0, 0x65}], 0x1, 0x0)
readv(r0, &(0x7f0000002140)=[{0x0}], 0x1)
mlock(&(0x7f0000fec000/0x12000)=nil, 0x12000)
munmap(&(0x7f0000c00000/0x400000)=nil, 0x400000)
r0 = socket(0x11, 0x3, 0x0)
shutdown(r0, 0x2)
sysctl$net_inet_carp(&(0x7f00000000c0)={0x7}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0x7}, 0x2, 0x0, 0x0, &(0x7f0000000100), 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
pwritev(r0, &(0x7f0000000200)=[{&(0x7f0000001280)='n', 0x1}], 0x1, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x10000000, 0x100000004, 0xffffffffffffffff})
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x653, 0x2000300010000})
mkdir(&(0x7f0000000080)='./file0\x00', 0xa)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0)='c\x00')
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
writev(r0, &(0x7f0000000400)=[{0x0, 0x1000}], 0x1)
readv(0xffffffffffffffff, 0x0, 0x0)
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x0, &(0x7f00000007c0)={0x0, 0x7fffffffffffffff})
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xd})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xfffffff5, 0xffffff78, "5604007c0300e500000116008d1b82834700"})
writev(r0, &(0x7f0000001440)=[{&(0x7f0000000200)="7cc13e8afea5a9b2295bd9cf4fa2ea99a25bfda408baee9ddcee28eb955ba3bc99857091f32244393ae132fbd9fb1895ee6e5c21", 0x34}], 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0d", 0xc0}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0xfffffffd, 0x0, 0xfffffffffffffff8, 0xfffffffa, "0901a6d3137c00005da24700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000001080)="dc7bcbdc99249c1afb983a7c28501fda36c51cf1e922f1e9890824acd611ed84d03e9c8a53327c5bf7e5dcaeb5ff0b7dd5f5f96c82fcadf00f30e03e342b1db361fcd17ad971768487588e358c316df7815ca4cd332ca06e0df1e853b51cf2710beb8fda52ea74b0d3371ec3d8e81429287c8b515f4792043a28c017c36ed7054b680ca4f60d0b6e211d8918078794d48ad4b44e1c48faecec523ce61d7afb14ca065f2d7478bb497a4ca299e5d7ea4f92f40723494a5f5b9ee5bb742df6f9ac78b1cee891712cf579a09ce10d6cb8c770c909a377f5a40aee3bc9a20fb1a3f1237eb9c9e0db5df7d4796dbcdedf3b65204ea4c9ff5b3f7e58c138a102feab0f7e0a866953af52fb8c05a67b47129b4617ebae526b05b5ef09589b75c7575c2953565a0a60fa57343c0f271302a884808a6901aff414fb4aea99969f87d1a3bad786351990197b4a802e3c3c6091d48efe1b80e61baf107ff8fcca4399d48f219872424faece8ea6a2376af2d5e35499d8a71f1da264c3381f81e45f8da2f918a21d762ec778da1354792838b8a79b52cdaa3c6b316b0dfe915614dd004877423ecf6329ef3fcc9d2a8ee5f6cb5a858313be2bc4852ffa1667020305d8e348d1b341f5a69e6458d550da6967b222f112b9a9dfe528bf5edd7b0404e7e25307067d777b4abcf1864b40b44aa6bc041b021cd0d2e21e438a270e4970748725a27e9d905ee2f7347445d031d22ae62ba1214160bc2bace7f7c1c5b95bb3aede69c203532ee0f21af695cb9ef1411ceda039c63ca1610d0b2b0bfb5029bee2dd2c634607eca391610731b0dc0171eba458a842327922fb5ba075d12d979c418600b00acaf7190ab7c90c183279f294b9afc6e6b4642c72a0b817aec7e34004c36824d77431734f1fbfbb1dfb30995e991fce0e9b89f92a7569e94434b75d64d55bfd4f9e2416dca7f095561f8ba00f6e3ff89fe29d97558faeda8f12e29a12bdcb916fa2987daefae0ce4e85c6dceded0c714e3e9255ee306893fe10f12cd689283c7ac64ed6f3342d80fb051d0b6f762891db31c59792b6de739d7c9a6820b719e5087bfe465ad98d0473b1e2554b71743473a4b3a72d5690132a8ca6ff21dfd81ad20f484e9dd9dc7062629b692066a5d98b0cf7a6d59625e5d1944001f3bfdfc3b78006813d6b03ff85690d079d1e424cb1ead0ecfed0e060f3e97e037097b3d7b3c99bccd14e562c7a77a9dfaeddcc179cb91c631fc0a6d264b3a84149f1e2021d41bec426c9d5f526d6dae6e88943f38ed82426884a69a954b39bb76b0154f75544b51a13a5bfe918817117ee38645918cf8540c5ed26e892206325749f8fd86be2324c66e3365011f905b77e6756a3388fbf3be27232ff5032e4537ee705af8fc56d5acec844bd0c2649a9d471dbf5e6b7327f410133bed70fe21e54e6b966613f5bb4d75f540802827265c3a63a2ca3333f837655f0f40777b6bb3994432875694a520c852be0ed94f3582212f192c4b2f5710dfcb65875664a7ebc276d22500656b3985ce2659a1c3bfc060ec1fb94896224c5c7921249f6dd942c409ddfdc10cdc7207315b191c514642f12b9f4376ff547b3c9101ce6d62b57bd179fcf6bca990e7d1556727156f4c42eea2aa83fbfa2ccd051d5d0465094bdc314ec8afa7a7cb5888f7b1a865525875bc4aa9629ce66b175b31a9116af1db39eefe96e2310e890f78ff93e1b0b13f7de2365768ccf0ea4ec1efbe517f53a097ae68b2e1869020e5aa12d50fb80cc43ed9eb6144b18be97cf37b189f2c383ad7f2f6e2a940bcd0f27319689de63b0f18c94de5949ffd6b1448539e31be41c85228e38aacf3bb1ee7e56233f79cd53a4a84506916708bf398374192af1dec1c9e14a4f6c591288e9a63a7db25d9272b9f5e5b84854159ee96a76d07fe134855bb41ff3ca7792d0bcfe52cc06c42b4b2906a175108490b8fa1eb186403d38c53544072d8b0480bd92209039727e823f4bbe62f12adc62716fa5a3fb2806b257e7840dade1294540df38d4515a39535732db06619e75cb677b8668f06e030459951e0f9b5d5fc49b3800cce09509bf8fcb906b0685273f7307509e625ef1c44c5de8c02683b74b94346dc21322bbf2db2393bed91a063d8f8a59c44b81049638384541950b580033d13d8f20c90a63db72e5e38887f9194b92fd4e1a29aa428824911c6c4a3b7b4b3c947ad64bdd209c9aec793270398f779361b05674715e21d18badf59556d4628d01c83225503a88953bbc4035238b92caead9f954c140b0c6a0ae4b1a87afca7648be7ed20253994802ef4c96feb7164612ee47d37d7be684be49caea7acfe3c4804a9174d43190e7483699f13df9f2cb8fd7e8aed9c900fc722b844131331609cc8808954441af5ef54509426b8c6906a7cae69a5017e8847379e63dae429388ee9b198134186888a8d704d2cd550c4e0110e8c66c34174ce3303b7250c14ef11da710d3b4e6897b47136965425a18648542925a7250af9f388048d49583f2171ad5ded2d7291d94fcefd8bfdabfb1a8845721377928167a62ce51ab97e2a94321f78d3634be3a6648f693f919491f385b905fed40172a178177a74ada3c9ed1358e0051f53ea10e6f41c240ae156b32c5b25224e9d4e807c5a9e7d441f09ab9fc8163d8b772323d5a7f0895195d600efb82a29ab71fd8bcd63a991bd2afa6949c362dc1a1d11f8ecac5db31f3390866399974825395fa723297a2f203c6e399060c8a46404c82f5e12b74d857d01d807f9c07e9caca9d637de81081d43c49675973d0aac69c41ee26e8896ace673db13975dc57baf94ead8fd56308538ae72ddf3a836648ab80d5afd45b18d4c131f49419cf17850ccd0d89f5f593aa83395039d79dccadca838ba6991ebd9ffcfe78800a44e2d630bf3ca3974d378475f7b17564ed2af708f4a7e5685479ea0beafae184557ffb811b23276f7a4a45ae7e8dfa6fb21be01c3abed55b92ae876ac6f796801004e82e76e74f054d1b40e2e6a8c2e67c5d87c546ceeb4d30e1ed8c83980717aa0d1a4096c35ac6c94311659af588d1cd7fbb54bac53609abd3ab49430744f3aa9f1fc2a3ab3cbf5a320556c7092f6bbfc4bddb2b362f72cdc67d9690cd73ff614c86f11223a42d46fb882246663efe6f0a3d6412a052c97f0e38ea825b56a0464d25ad21ff37f0d30439b12bbdfccaeab0884ae196e328c6a10245567705c8e85d2f49a8f30034168d4bebcbfd7c9daf1359346ac2539237611f1489e01b1f71eee4954ef6048cd7cdff3384ed149a8c78fa3fb96714a76a1a8d90c4f8c25d2b595f0e55d7d721dba43a38fc08a1f1570a6584fc633690d4bca45d1303cf8dc9256c90bb723e1d2f27317a81e1cd7452e72e06c372d627c527daf28b6a00e31df5859d3fa89d3df9c8c397d0a58f954edecb54a6dfbaedbfe38d319df585e8bf0e97e66b3b3dde53850f491b78954544a0ddc1d013edff00094771898cbc818fa899a6e3f0df16460fb718508d4ed6d719beb82f6da2acc25f59e83042e8e19926291621e9dbebe551bdde7e1f918461e71f453bebc41322c63d42186457704d3d4682bc142e861bca67e5df5c848f0e29a9b23e891e740d6e5d686b4b99838779def798e8ab63140c9d687ce253ebca378a258475de8ef7a5343de4bd63f0f1467248ed483c77b0bea575bea33246c47305dfe304ac3239af656b2d91217009dd17329285592b237f3fdeae4cda6c5a76efef1c610b5ddbcf232ff302ec11562918ed43670a245f499cd1a400cfed0e15fd79452511f1b6e4d3ae91b129e4144960e777ce4e343804910bbe986438049f2ad1a44096cf8cdbefd641b1f8a54a2dce54a820183a1a40eb2d1a4b7238de176ddf74ce3d10f7349c3ab058a4f9e4f15a557c89336cf0d4c4f4deb914b661547ae3db57f286be909b01e5c3123c2381a9c2f178ab1f45271d054b47cf16350b1871b9eb87fc4c9960f8b2525ce6db941abbd5e3b765709591a4cfaa49088ec4ddf097eab22a714cf61fc3d2839a39f0e4ab24bb65b193315660fae0884da1799154dfd0710318b204fbecc36b1ba3f4bcadd2e338c3bf859b3169a92fd084621281f5e518918c4393c9bfca6c4c5b3e0c3ee621eee22bdb02a81c089c788a5069d155570f753eecda60c9675dfa476d2353511c46ec95f11dd0b633a381ef79cd5d4bad5e23f1dd62a50e8d262d5d7030e644e25ec6f6bd5671b70f264b8d783492c77f47de62f53376981b2f902e63b5ad88110c37d8bbcb6ce03fedf2d8b04600343677f34f7d0562e964a2d4c4f83ae51f1835460bcedab6797b0957dc01427ad5df78ab288c99a69108c85fbfceb1fc619802ce0b03b1b086012834ff9a4d4a2978907ac4ca7ec1e6b78be22fb7dafed972d6987b2f2b753f5556e48158362097e9cf8778d1736ec45151d68ae7fc4d680d568ccd8097e357715379444da6fb37f15845321dee067c032db6496259aa6aa0482c9d29ab10ac12b53783e56490b3c4b0e6886018e76680f88ebffd99561a91cfbf34e3619804fcad216e4c4c0757fe4e809cfb5d4a62d8a3ac5e06ac5fe2a4d982a231bb11f1e9fa80a44d604c4750403a951aed4a73a3217c74a66677f9d2a3a727fec6d77668093da52cddfad39a03c21bab6a5d1d0924013b3897bdcf836aaff8e188c3fb4d9ca674efcadf26b5e7c122fba41c6871215384ab55eb9aee1a93ec14bab4f20440a7762dae26cb9fa8fa02a73e62082afa0d2559efc9d35de4804e89be5c7bfb6eec37a99ab555ccf995291630113951917a652b1b2799430814af95d6bfb6946886e2a358b539ef09a2252e794a35b4572f0e5f097cdeef56c8dbc4c1bdb0c50cbb7a2753f0941ccaba7994496fe22facef276891894c19764317dec013c8efcd9907505178268b94e87238de4e5c6f1851152bfa1eea90c20e05372cc6f391a9280e7188a145fb3665c536fa7adb5e3655bfb08b466d36326b1598c20a52101cabcddd77bbcf0848e293025f903ed46350aac87f2f16198798b3387b2190c18a147b0995e3", 0xe0a}], 0x1)
read(r1, &(0x7f0000000040)=""/30, 0x1e)
sysctl$hw(&(0x7f0000000000), 0x2, 0x0, 0xffffffffffffffff, 0x0, 0x0)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
lseek(r0, 0x0, 0x40fff)
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)="b5", 0x1}], 0x1)
ktrace(&(0x7f0000000100)='./file0\x00', 0x0, 0x1000, 0x0)
truncate(&(0x7f00000001c0)='./file0\x00', 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f0000000040)=[{}, {0x21}, {}], 0x3})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000080), 0x0)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)="ea", 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="2902657f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x8001, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000040), 0x8)
connect$unix(r2, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
close(r2)
accept(r1, &(0x7f0000000080)=@un=@abs, &(0x7f00000000c0)=0x8)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000006c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
openat(0xffffffffffffff9c, &(0x7f0000000500)='./file/file0\x00', 0x0, 0x0)
sendmmsg(r0, &(0x7f0000000400)={0x0}, 0x10, 0x0)
sysctl$vm(&(0x7f00000001c0)={0x2, 0x8}, 0x2, &(0x7f0000000400)="060e980c", &(0x7f0000000440)=0x4, &(0x7f0000000480)="ecb69a86", 0x4)
r0 = socket$inet6(0x2, 0x2, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x0, &(0x7f0000001000)={0x18, 0x3}, 0xc)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
ioctl$VMM_IOC_INTR(0xffffffffffffff9c, 0x800c5606, &(0x7f0000000000)={0x9, 0x3})
r1 = socket$inet(0x2, 0x3, 0x0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffffff, 0x0, 0x0, 0x8, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9d, 0x0, 0x51fc], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8], [0x0, 0x200, 0x0, 0x4], [0x0, 0x0, 0x0, 0x0, 0x1000000000000000], [{0x0, 0x0, 0x0, 0x100000000}, {0x0, 0x0, 0x0, 0x9}, {0x0, 0x0, 0x80000000}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x1610, 0x0)
r0 = socket(0x18, 0x3, 0x0)
readv(r0, &(0x7f00000001c0)=[{&(0x7f0000000280)=""/136, 0x88}], 0x1)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r1, &(0x7f0000000040)=' ', 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
execve(0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
symlink(&(0x7f0000000000)='./file0/file0\x00', &(0x7f00000000c0)='./file0\x00')
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x5900)
r0 = kqueue()
r1 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000200)=[{{r1}, 0xffffffffffffffff, 0x25f12e15719981ed}], 0x4, 0x0, 0x0, 0x0)
dup2(r0, r1)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x8, 0x5)
select(0x0, 0x0, 0x0, &(0x7f00000001c0), &(0x7f0000000200))
r0 = syz_open_pts()
ioctl$TIOCOUTQ(r0, 0x40047473, &(0x7f0000000000))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000002})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3d}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc00c7007, &(0x7f0000000280)={{}, 0x0, 0x0})
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt$sock_linger(r0, 0xffff, 0x1021, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f00000001c0)=[{&(0x7f0000000140)="03047d872e66f93922be50e76b00da8092c6e3541dc3668fb7861cb3b60fea60c47fe1bc8ebaff742f8f5024da02c0ccee", 0x31}, {&(0x7f0000000240)="5f036d6e803146c7d00ee9434f6f71df4603322722a78b86bbedae52c5bf3144e8e1656769402f2a1d7fbe18bfc2ea6dc5901b9a47f37992586bf8c734ff5f245f21fd3d7c918178064e73ae395ea0ed14069b5a74eaeb9266c61efd7be70545798aed5a628a949128f263bef23fdb572229e24a8e10edefeeba495208", 0x7d}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f00000002c0)="c820a0be6ac42fe8e80553b47d91e7e61a7959915dbc5b1e5b1d8d37c36e92018e0741eecda2cc2836719a03d07fdc7e20de3bbd6ca9c84e3a27525a43de6c7cb4376042fff051103a5cc0c1e4ca6a25de8c2a34535f315a6751acde661a42576ceafff24e2f482fdde0f61ce5ad3f3b51392b8de61b69cf0cb7903731188bb9c483164e531813b08884d8120a603bdda21a083b7929b5f45f05bf0de4888de5b2a03fe60f9d374661b8584e", 0xac}, {&(0x7f0000000380)="362ea9", 0x3}], 0x2, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$machdep(&(0x7f0000000040)={0x7, 0xf}, 0x2, 0x0, 0x0, &(0x7f00000010c0), 0x0)
select(0x40, &(0x7f0000000000), &(0x7f00000000c0), 0xffffffffffffffff, 0x0)
setuid(0xee01)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x37, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x6b, 0x0, 0x0)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, 0x0, 0x0)
writev(r0, &(0x7f0000001740)=[{&(0x7f0000000040)}, {0x0}, {0x0}, {0x0}, {0x0}, {&(0x7f0000000480)="774d257256a3c186f3f632efd510fb20c0facfaf30790df82abbe5a38f99c4e4174db3a8d380fdbb0b504c6b59f7663029917d", 0x33}], 0x6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x4}, {0x50}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@remote, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @remote={0xac, 0x14, 0x0}}, @icmp=@time_exceeded={0xb, 0x0, 0x0, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @multicast1}}}}}})
mlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
mprotect(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0)
mprotect(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x2)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000000280)="3a8fdf4b0fb8d9b06eaf7696d200ea1c8fb9815c3e65d8df252b3821822741edb988b99acb8670cdf2f3fc5f915000c0851a60206abf5e3e9e2901bba9fa8c367d68b9adf5bd37ae91617a43c83860ce2e55075d7ae8438a0c6e8d00e1f8e6c39975f2f4cc74e675a4f41de06dc6d37a36ba5ffaa76bc204bf5f8df100000000002c43ecf4ab3b659f78429f", 0x8c}, {&(0x7f0000000200)="03000000d943e89274cf5d101c30ccbc23fdad92cb22f116c013d668b8034d22a68007c36ba18f1119d14204000709d87406129e17638e714e6a9325ea3ca9a91df584", 0x43}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x242, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r1 = socket$inet6(0x18, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="9a", 0x1, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
pwrite(r0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x14}, {0x50}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f0000000100)={@broadcast, @empty, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @empty, @local={0xac, 0x14, 0x0}}}}})
r0 = open$dir(&(0x7f0000000380)='./file0\x00', 0x200, 0x0)
munmap(&(0x7f0000ddb000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ddd000/0x3000)=nil, 0x3000, 0x0)
mlock(&(0x7f00007db000/0x3000)=nil, 0x3000)
mmap(&(0x7f0000ddc000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xd, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x7c}, {0xc0}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
setuid(0xee01)
chdir(&(0x7f00000001c0)='./file0\x00')
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="efbeadde0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a614815fff271957d3cd40a87052ffef6650d2e29d42adf810d0fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e", &(0x7f0000000680)=0x1b80, 0x0, 0xfffffffffffffe97)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x24}, {0x87}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
sysctl$net_inet_divert(&(0x7f0000000080)={0x4, 0x18}, 0x8, &(0x7f0000000100)="7850907807cebf3ba949a909c1c3b3b20b550c3dafdad5587ede6d375a2e58aabed3b12207e12ca2", &(0x7f0000000200)=0x28, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000280)={0x0, 0xa, &(0x7f0000000240)}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="14000000290000003e"], 0x38}, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x4, &(0x7f0000000900), 0x4)
mknod(&(0x7f0000000000)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x3, 0x0})
writev(0xffffffffffffffff, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
execve(0x0, 0x0, 0x0)
socket(0x1, 0x1, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000580)={0xfffffffe, 0x40, {[0x800000001, 0xf8, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x5, 0x9, 0x0, 0x1], [0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0xffffffffffffffff, 0x7], [0x9, 0x0, 0x7fff], [0x0, 0x1, 0xfffffffffffffffc, 0x17, 0x7f, 0x1], [{0xfffd, 0x7, 0x1}, {0x0, 0x0, 0x89d4}, {0x0, 0x0, 0x1}, {0x0, 0x0, 0x100000, 0xffffffffffff3cd6}, {0x0, 0x0, 0xfffffffc, 0xffffffff}, {0x0, 0x1, 0x0, 0xfffffffffffffff7}, {0x0, 0x1, 0x0, 0x200000}], {0x0, 0x0, 0x1, 0x474}, {0xaf8f, 0xffffffff, 0x0, 0x2}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r1 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000000)={0x1, 0x0, 0x2, 0x0, "106e91b8f920945b0c315a8625e15a16f99c7f31", 0xe82})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x5, &(0x7f0000000040)=[{0xb617, 0x5, 0x1, 0x400}, {0x8, 0x3, 0x1d, 0x80000000}, {0x400, 0xa5, 0x1, 0x102}, {0x49a, 0x25, 0x1, 0x101}, {0x3, 0x3, 0xfb, 0x13a}]})
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCPROMISC(r0, 0x20004269)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000240)=[{0x87}, {0x5}, {0x4406}]})
fcntl$dupfd(0xffffffffffffffff, 0xa, r2)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
fcntl$dupfd(r3, 0x0, 0xffffffffffffffff)
ioctl$TIOCFLUSH(r4, 0x80206979, &(0x7f0000000300))
socket$inet(0x2, 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
lseek(r0, 0x0, 0x40fff)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000100), 0x1000000000000161)
r2 = open$dir(&(0x7f0000001240)='./file0\x00', 0x0, 0x0)
r3 = dup2(r2, r2)
pread(r3, &(0x7f00000000c0)="bd", 0xffffff78, 0xa83)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x10016)
msync(&(0x7f0000d5c000/0x2000)=nil, 0xfffffffffffffef1, 0x6)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="110000002900000033"], 0x38}, 0x0)
select(0x40, &(0x7f00000000c0), 0xfffffffffffffffe, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x1, 0x300004004})
r1 = dup(r0)
flock(r1, 0x1)
flock(r1, 0x1)
fcntl$lock(r0, 0x9, &(0x7f00000000c0)={0x0, 0x0, 0x1, 0x100000000})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000080)='./file0\x00', 0x23b)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000240)=0xc)
chown(&(0x7f00000000c0)='./file0\x00', 0x0, r1)
chdir(&(0x7f00000001c0)='./file0\x00')
setuid(0xee01)
unveil(&(0x7f0000000280)='./file0\x00', &(0x7f00000002c0)='x\x00')
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000040)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000d91000/0x4000)=nil, &(0x7f0000623000/0x3000)=nil}, {&(0x7f00007ed000/0x2000)=nil, &(0x7f0000440000/0x2000)=nil}, {&(0x7f00004e6000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000fee000/0x12000)=nil, &(0x7f0000cf9000/0x2000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f00007ea000/0x2000)=nil}, {&(0x7f0000ff3000/0x1000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000762000/0x2000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f00002b0000/0x1000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ff2000/0x2000)=nil, &(0x7f0000fb6000/0x4000)=nil}, {&(0x7f0000aec000/0x4000)=nil, &(0x7f0000acd000/0x3000)=nil}, {&(0x7f0000c0e000/0x3000)=nil, &(0x7f0000693000/0x3000)=nil}, {&(0x7f0000e4c000/0x4000)=nil, &(0x7f0000dad000/0x4000)=nil}, {&(0x7f0000df3000/0x1000)=nil, &(0x7f0000ff0000/0x1000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000fe0000/0x1000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000e13000/0x1000)=nil}], ['./file1/file0\x00', './file0\x00', './file\x00', './file\x00'], './file1/file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
writev(0xffffffffffffffff, &(0x7f0000000640)=[{&(0x7f0000000140)='#', 0x1}], 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000705", 0xe, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x1f)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
sysctl$kern(&(0x7f0000000280)={0x1, 0x53}, 0x2, &(0x7f0000000000)="11ab71df0bee6452bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa35140b87a8ce59e083eba3ad337618b45d6088a9737e8b581e00"/114, &(0x7f0000000680)=0xfda6, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x3d, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x0}}, 0xaa)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffff9c, 0x80245753, &(0x7f0000000100)={0x3, './file0\x00', './bus\x00'})
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000000), 0x32, 0x0)
syz_emit_ethernet(0x4a, &(0x7f00000001c0)={@random="2da1ea475a70", @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "048f52", 0x14, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @broadcast}, @loopback={0xffffffff00000000}, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}}}}}}})
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x8, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x7c}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000080)=ANY=[])
r0 = socket(0x2, 0xc003, 0x0)
connect$unix(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="9a02"], 0x10)
r1 = dup2(r0, r0)
setsockopt(r1, 0x0, 0x2, &(0x7f0000000000)="62c5ef11", 0x4)
r2 = dup2(r0, r1)
write(r2, &(0x7f0000000140)="f96506e939746d59675f1b9eee7d7b13cb5915a2", 0x14)
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x800)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000080)=0x4)
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x4001, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206932, &(0x7f0000000300))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000400)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd6c115b266e6cac2cb93537c2b236b848dfe87156e5eecf5dfd60c15d0165ec940cbfc0b74c1c4494b999e4ac", 0x1d0}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51f4b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2af76824b990a8ce316d0e8cdd2b7420511bf6e2204f87eefbae7e9aa56103ddce0203eeed44da7b3c5a3a072ec8b1312e5c28319500820e262b6d4011a4b473b4d7694f462e7fa3aa201ddb7eab4af9a44166f245fbd59802445ca018b2032", 0xe0}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
symlink(&(0x7f0000000100)='./file0/file2\x00', &(0x7f0000000140)='./file0/file0/../file0\x00')
rmdir(&(0x7f0000000080)='./file1\x00')
open$dir(&(0x7f00000001c0)='./file0/file0/../file0/file0\x00', 0x10601, 0x0)
mkdir(&(0x7f0000000200)='./file0/file0/fi\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
r0 = syz_open_pts()
ioctl$TIOCGETA(r0, 0x402c7413, &(0x7f0000000040))
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x0)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
r0 = socket$unix(0x1, 0x5, 0x0)
shutdown(r0, 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x50}, {0x2}, {0x4406}]})
syz_emit_ethernet(0x4a, &(0x7f00000002c0)={@remote, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a3c692", 0x14, 0x0, 0x0, @rand_addr="8c3a02ced133a1650e378bbfe3a6765e", @ipv4={'\x00', '\xff\xff', @broadcast}, {[], @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000140), 0xc)
mknod(&(0x7f0000000000)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x3, 0x0})
rename(0x0, 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
open$dir(&(0x7f0000000180)='./file3\x00', 0x80, 0x4)
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
setgroups(0x0, 0x0)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x1003, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSTOP(r0, 0x2000746f)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000000640)={0x0, 0x0, &(0x7f00000004c0)=[{&(0x7f00000002c0)="612c586ce7fbaf3ba028d0d376c9d30f80d0d3b745822aba3b777412c47f4f2d9f7115b58ca37c1f173347abcf6eb609eeea3d2e242250862df85fe23f3e2a7201c77e3aa8819af19e2a9a4dddc71aab3ead1324538e42840472b58cb4415b1eda4c62dc8f36629d0b3cefd146a2b2159ec19edcb51097662ebb213f8c92fa51", 0x80}], 0x1}, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x80}, {}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
semop(0x0, &(0x7f0000000000)=[{0x0, 0x0, 0x800}, {0x2}, {0x4}], 0x3)
semop(0x0, &(0x7f00000011c0)=[{0x1}, {0x4, 0x1}], 0x2)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x2)
r0 = semget$private(0x0, 0x5, 0x2c4)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000010c0)=[0x1, 0x7])
semop(r0, &(0x7f0000000100)=[{0x0, 0x401e}, {0x0, 0x100, 0x1000}], 0x2)
semget$private(0x0, 0x3, 0x200)
semop(r0, 0xffffffffffffffff, 0x53)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000400)=""/30)
semctl$GETZCNT(r0, 0x0, 0x7, &(0x7f0000000440)=""/61)
semop(0xffffffffffffffff, &(0x7f0000000080), 0x0)
semctl$GETVAL(r0, 0x0, 0x5, &(0x7f0000000040)=""/4096)
r1 = semget$private(0x0, 0x0, 0x20)
semget$private(0x0, 0x3, 0x140)
r2 = geteuid()
semctl$SETVAL(0x0, 0x4, 0x8, &(0x7f0000001180)=0x2)
r3 = semget(0x1, 0x0, 0x42)
semop(r3, &(0x7f0000001140)=[{0x4, 0xd, 0x1000}, {0x4, 0x3ff, 0x800}, {0x4, 0x3f, 0xc00}], 0x3)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000001200)={{0x2, r2, 0x0, 0x0, 0x0, 0x66, 0xe}, 0x5, 0x6, 0x6})
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000001040)={{0x8, 0x0, 0xffffffffffffffff, r2, 0x0, 0x104, 0x3ff}, 0x8000000000082f1, 0x5, 0x100})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket$inet6(0x1e, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x1, 0x0)
shutdown(r0, 0x2)
poll(&(0x7f0000000100)=[{r0}], 0x1, 0x0)
select(0x40, &(0x7f0000000040), 0x0, &(0x7f00000000c0)={0x8}, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa66376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283018e4fd89720fd300000000000000f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020288a371a3f800040000000000000001000000000000ffff", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000029c0)={0x0, 0x0, &(0x7f0000002740)=[{&(0x7f0000000540)="71f2893e34ac2dcb05a97d510a133e33c3eaa7fcc1dcec634e413153557c4e90d571480c41dfa94b2c2321f44bc730d2ea3697d5d42017d0432e8322f05aab1d347ce3c830b3ed67b453fe6fcc19250dc4177ce0338d5cc4b39d86e87519836129779b105dcace09f591c919bd6c324cd9159b8b24f0e7658803e17bac383ac0f847553468618facbeee9965f90cc3161ae859c18f0d9b60728ed04e2842ea5bdb3db7c1423bc7931c405e11fb4bd42c6003f742a1f20de19394bcd45614c52a69b5e70b50d720ac1929f0a0814eb0497b521f98e68b116f10b74f7e4600aa4285f6ad54c51cf139206b6675f3003716147d0e5b18accf20e94aee8b6e82cc51d5248b8f4d518a4753f6a0f66f434525fdc2bdd8b0728b3cb1e52fceb4451a0a709a14b31cbfeed8d417d367668660c8558fe43fbfeabdc6ab029197856354e7373c6747ccf1404ec3a6f5a027929f7b0807628e69c45cf561bf2597265dfef594e65f27e695496ee62627bd2b85e91f4d96d74af2f0f621d8f7cfcf2992bd1c5ef4889801a8e334274289cefc5bcb6fba8cbe3807965d7d7f843a32127443455ff5bc7056777d9c7eba6dad1944476ed57cd5ed361e7eb281cf12b85ffb776ee82148ce4d01ec57aaa38c7fae730db7702519369242c0031d67a6b9e1a25122de3a91703ea516ec00cc53f15d7c5bf64d039b28da2b6bfb5f9a13f11a85d12fd76f78b87afddd749ca8685f49252dfc465589153b0b719f9f7b6548663e60bc3ba157c2ee3fcecedb9f4bfe71fb14984fb103cbb7622c8df9f117cdcce4f911bc578f6dff991cc85a1e5fe07e20119eb446c84869422d55bb135e64f4ee2b116d9245963a9790619fdf1130aa6590c1df3ce32004d3f897b0187a7c397915be9196a219299c3b2345faec1a965a64a752f5c95cb818782662aa657a29a878ffeb9d55eeebf4fd12d07df4074c3882085ddbd6effdb33b303a05a3e2c79da671e6718c32b7042774434be8d0259147508384ad6dbe3467755b8b1f39b7e83406fec659829821ee7ff2b6cda0c5a5b96b0a37fb217eda06897ad11b7b3448d8363304c4dd64a1bf883cbdac0e3deeb13132ceb21aab87de7009587dbf740e4f97b22e61daec5cb78bc88f1ce7961787db0e5b3a3d4effafde862b1d91fe1b3e998efc06c9e7a99c4df960660ace03fa0d8fcab2a81acfba051348a9cd634b71e7c23fd6159af389804c5c09cea8391429d32b6af608c6d34247ceadc675c56f015cf4efffe0f2acccae837fe0039a416b4d76d66103e762e725d691717eeb419be4ba3d954ef9673ccd584b8c0cc82381074e2cbfb348ea5fd1f5141b644a2d16eebc02bd854721225761c3ce68b0bdf407bebc7f22ae25006b121b1262e2ada6450c1dae15a2698eb7e4591b948358ee93bdfe6118f69e6d032de9df1508eb1861c6a75298fc778543e87f34bf7bb7aeb308f2fafce35340574e760b0ab55c99537a9dff1b97df891395d72b3382c6c0bdcb64f31cac5fd5f36e48fe8ae83a6616a0cc78e364ecb0abd91c7b0e59128c9d2a25e0f553a5fbe70c52818f9f32d27d08832ca5f74456ae7ca8f3f39148b83d4c5511210c9228fcb2a4700c09", 0x47e}], 0x1}, 0x0)
r1 = semget$private(0x0, 0x7, 0x3e0)
semop(r1, &(0x7f0000000080)=[{0x3, 0x0, 0x1000}, {0x1, 0xff, 0xe17b725e3710487a}, {0x2, 0x8, 0x1800}, {0x3, 0xffff, 0x1000}, {0x1, 0x8, 0x1800}, {0x3, 0x1, 0x800}], 0x6)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x80, 0x0)
r3 = socket$inet(0x2, 0x3, 0x0)
setsockopt(r3, 0x0, 0x17, &(0x7f0000000080), 0x0)
dup2(r2, r3)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x191}, 0x20, 0x9, 0x7fff})
semctl$GETNCNT(r1, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000380)=[0x95a, 0x0, 0x9ff, 0x96, 0x8009, 0x8, 0x402, 0x8101])
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r4=>0x0}, 0xc)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000040)={{0x3, r4, 0x0, 0x0, 0xffffffffffffffff, 0x4, 0x1188}, 0x8b6, 0x7, 0xc897})
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000140))
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r0, &(0x7f0000000280)=[{&(0x7f0000000100)="7f", 0x1}], 0x1)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000080)='E', 0x1}], 0x1)
fchmod(r0, 0x6a)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="4c45b861d0527960d120e55148c3a5f0ac10808cbfd0cdc72acd81f4b9a2cff796826d73aaa67c2ee8b91a6b6c1d17bfbad809d5bef8d18c34989e0451d6", 0x3e}], 0x1)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
open$dir(&(0x7f00000003c0)='./file0\x00', 0x2, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7ffffffe})
r0 = kqueue()
kevent(r0, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f0000000200), 0x3f, 0x0)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
mlock(&(0x7f0000ffa000/0x2000)=nil, 0x2000)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x81286947, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x134408c1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x5c}, {0x5}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82023e2f66"], 0x10)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x2e, 0x0, 0x0, &(0x7f0000001440)=[@rights={0x14, 0x7}, @cred={0xff0014ac}], 0x18}, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
getgroups(0x2, &(0x7f0000000040)=[0x0, 0xffffffffffffffff])
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f00000003c0)={0x0}, 0xa2c4a4fbccc5c5aa, 0x0)
r2 = socket(0x18, 0x1, 0x0)
dup2(r2, r1)
recvmsg(r0, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc0284459, &(0x7f0000000240))
open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x25)
setsockopt(r0, 0x0, 0x68, &(0x7f0000000080)="966605d9a6dd4219de05865ca5d2faf6c75a292610d042e5fd37c8f2664a4d7609ee0fb4eb57d86bddd0b397", 0x2c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000080)={0x7fffffffffffffff})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x74}, {0x80}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000100)="529ac7ecc87315600a6543100f07c95df3287c82518c0461a37e5f30cd2e89292a66bd2d3cf7c41e0dd946a42f4a85799cc9f02756fb514a89579f01f401ffdd16017207f2a3d3c0b4d541dc3ece7e475b8a400c8a312464d18b87911540374731c15f2cb4bbb1563c17541a9658a656d3e478001cf220ceb66aa96f17d5386adaff35", 0x83}, {&(0x7f00000001c0)="52474183a9a2d7b4647d191e127b82407d98c3cd48b6ddf718f7c054ba76a1b5d3623a4de03cf8663e6db1c16fcb78206de351296e08624f88b8cb8ac4fdc551d0f021fb", 0x44}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x4}, {0xc}, {0x4506}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)=ANY=[])
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
connect$unix(r0, &(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x807fe, 0x0, "0008000000000000008ddd0000000700"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r1 = socket(0x1, 0x1, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
close(r2)
socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80146953, &(0x7f0000000300))
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
ioctl$WSDISPLAYIO_LDFONT(r0, 0x4004667f, &(0x7f0000000180)={'./file0\x00'})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f0000000000)=[{}, {0x24}, {0x23}], 0x3})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x25}, {0x44, 0x0, 0x0, 0xfffffffd}, {0x16}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
munmap(&(0x7f00007b5000/0x4000)=nil, 0x4000)
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x80, 0x0)
lseek(r0, 0x0, 0x1)
getdents(r0, &(0x7f00000000c0)=""/4092, 0xffc)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f0000000040)='r\x00')
openat$zero(0xffffffffffffff9c, &(0x7f0000000100), 0x200, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
unveil(&(0x7f00000000c0)='./file\x00', &(0x7f0000000080)='r\x00')
unveil(&(0x7f00000005c0)='./file0/../file0\x00', &(0x7f0000000100)='c\x00')
execve(&(0x7f0000000040)='./file0/../file0\x00', 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCNXCL(r0, 0x2000740e)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = dup2(r0, r0)
sendmsg(r1, &(0x7f00000046c0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001500), 0x11, 0x0)
ioctl$WSDISPLAYIO_GETEMULTYPE(r0, 0x40047477, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000100)={0x4, 0x2, 0x6, 0x17}, 0x4, 0x0, 0x0, &(0x7f0000000200), 0x0)
syz_emit_ethernet(0x1fbf, &(0x7f00000002c0)={@remote, @random="d7597f0dc9d0", [], {@ipv6={0x86dd, {0x0, 0x6, "9bf9cd", 0x1f89, 0x0, 0x0, @mcast1, @ipv4={'\x00', '\xff\xff', @multicast2}, {[@hopopts={0x0, 0x3e2, '\x00', [@pad1, @jumbo, @padn={0x1, 0x2, [0x0, 0x0]}, @enc_lim, @enc_lim, @jumbo, @generic={0x0, 0x1000, "b653d3518ec342b91fab3c6711d03f832fbf7a464c025b9c6da1277fa6686af73b954ade168c2d64e5038d6c4d3b3205f69944ccb6dc2f971cdc4256e69a313216c72629e364d2e6256508ff34b0730fb0eb80dbd9ce739960a7d3502a78b3fd7391daf3c32df7487afa6a8c096f3e2addcb79a386bdce633485b609f0060bab65a6580a588e9338b58657b3aaa96d4ffb73bae9e152b9ca3e527ee27ba844ab2480bfdde489ee59077fc9644459da2680bbf13c73623b969dac499a4c97ed0c29a27941bc2a958b98556e8d9c980a016fa486902e2a96b939fe921c5fa30dda51910ac4f737a441ae8be1381e02b8927df62bbec5b928a09cdc8ff6841b1aa8d438f432b7c52481afa91263a6776c9f85dfb593d54548208aa121f31fe4648812d23987d626e4d0a68d2ac6ceeb3d8c319eb08cecf89fa565aec69716d74e2bb5ff66566d8c94bfccc637f64f893380889e03a1a486686d3fdce79f20477ae072b712f5d51f7d30187bcc5aad182b8a0f94c40b97f7add6a29435151c83eb8dba7e520db7c2b26ebf530b1d44e132b49fdaa215ee4aacc48354b30ba0c4d0825ce3ba7b0a8f1561e47b131b92f94e93bb640f17bdf3a11b15de634c29d915b81c6a03b566fbf606a82137bfe18a8b70e414b12d0816d17b4bcc4b8305d20fae530e53d73603f31a5f82e96261b2e84e27dcc06cb5cf90244f785c12c1925bd1a930a1d5537bc606ce6579329ab7d2f23ae04f6c478d7c8ffdc74cebffa045cbbb89fa46e1563915295ce07ba9c9e57aba7e0deb14c90019a9fea4ef0dbb46a0d0c24ed7151bef5218f7275fe08bfba0897f493719b350748f542b9fbedf73e9d45bef9164e74087187c43f87060caa99fecfd5ef731bbead140fa73c3171066205a73da97c55c92da300183514f88e126fe4b09e9e759cf4b01aaf2b77514487bc603857ac130d22a8b53e7bb13d4816ddcbe93497e287609b8cb3e9446f7c1249657d345b53b1b16806b6cbc6803f383950e8973355e2b8c5f2a27c6ec35f0173c1e09bb9b4f8601a37ff62d72210b220ef8342aefcd326ddd64640aa65d0dcee6b9a4f5a32997a318dca06c9bb8dc8c9b6c14388e1163b21700c5c987c3fb95b3210c97dc1814e46c55e88309a1b47041192871780f7213bb783f1fb6a3370bc4430422b90aaea48b37c3f2c54ec496fc8014913ebb8c2ea7eeac7afb33d0d87db101fd3d34ff1da2b8d1172a7ef8c889efc5d7ad08ca29397062ddd4e9dc7bd974d485dc016277fc06c85f01a44105d1e65fd3376ac141a1c7175a9f7a3ee18d54b8957ea6259582f8fa7bebf69ce67f67a80ce32e70ae112b742f35f346387fd194b3c6ae40f1d6137957bfd10656e9a1eecbf28bdeb04b9687f0a7f5be72d6a3c64b26a31e7e977c6f08f8a1d5cbb9520d546860d2dab5ae1fbbfdf57eeedc8902c33f51b2ad81f9f75e59391971defe779897bf3f76076506798254d5468f51d30f2e95b985e9d7e25054f8a71046e12a23695764e130f05abb93b8f5c3c319502c65936ef4fd1ddaf20704cb14cb31f8d4b3dd5926009df8554d96f7f438d1d48f58458af1a8da89634ded6eb128eaf88f79cc32c76a8eb7725cf63a81c2d0d11b10c2312609b5192bc24ea35b88c4889d86f5ab601ceb1384a5a1750453a9fc7bc28983b96800a8a2553f03bd2bc56e6be5f2367ac122b6d9eed4cbf8d611cc904c6a47ecf1099bd4f9de933336dec9c56fb08081ec49d6906726f160c9c439b42785660fb1cee7d678891cbcbddf8dada5f5ef1f298a34e41c411d9bcf3fcccf843a8b5e2e9d6a328f7f625aad70d281ebb55a6b79bf8d3e551b5eafc8b2432cd4b3d5bfe174216e7d76d86b5519c4086e592118b0119dcf85450fe42f509dc572ef590760261dcddc9fda03bbf84340fa97004c596128805014d3b1cd70279bfd4a817203a109930e292393614c0962a0bbf7a2f26ea31027498322d33581d4a88eb19f1c5311c11411667c9beb41492b83d5ed9f49d4cfcffa5a689e8de642a8349a3697dfddd9d26a387185683a3a374fb82ba8147ba3d62618816e2cc2aba0e50fd8329339e058e79c54e79be879981694805c54e76fbdd65a6ca70b64a6c7d1287c87b4a9d735a92bc3472997711fe9c607d6c9298651b76aaa1176750b54cb8b711723051fdbb9289c03774bb0fef5e99c89a2cca026d5b8e22c422499302ca7307ef2174d0f5b714851b77d35b8b26421566964c9643be8ae0e1b65eaac8cd8b9941f1a96705b11c9373ca4052645ca0fb680e21bc25b8db78dbebb8391ac45824551c082ba6986b2af4509bf5a3dc188d7091b8ce8e470b3e4345c38f85940e179d4bef809ce59c904fd58ea2c0c6fe18f770ea57909d52a95eb17a6dd9978d30b793bdcd49b0e269a96629920025b9ad48f352b70ca5e704359f79280c14060053178c89145d7416b66bc3c78d3ac7a66e90596d692685f24b9b7f93cca3d9e367680a9be6131e33030a9ea9b733144689b384f79aaad7f6c43a45afaa2a32e077f6c7fc65474966f80e25bfd8a454c94cdfdf4f3a6b1feadd007981b45e2dba62577c67eb908efc1f6079349b0ad937a55837e7401bc3b0c8a6ba5bb1ec116f03d40dd5281f8a58e9bc90be09bac0243c80336d78df7e009040c2940b1a3f9ac20b3719a25b53b286040a739eb18be41d38f159d19184f64f371dd7230fca3fcf9789802c04fc5cda83dca911a72f0f217a3443e19b38819a1c3cada8614d008ac44eb44530d9122d92c123edde6d13434fa8cf1a84cdc96c13260e4a1745f3cdd234354935dbfbd4cbc974e776432cfa9429f111468aa76ec3b64ca2972c8cd78aa19a83d0e044b6603e27c12b7dce80a4650a418c9d0701772511f36a9c92445750bb9b11c33e7a77edca9b3cd4b43822242a5ded5e2c3312b83400d6278dfceae55db90fb989a954aa0aeafc63f2f8d94349cd925b0fa269ccf65722d3548242cb9279fc40364a4fb8b8e812598df19c49294bd2b0a1c03a61dd66c0dc32649e8dff0ff29fb5855b40a1e294e2d9b79c0d9465208e27d4cf970d3e01c97b158219e710f616907e228298fd651530d61c2ddd4ea6e5de13293ea576ad306100cae13a04fe2eac5bea6e90fa595cb50eac80746d20a9a4fde5dbdbe835f2c22f4b6b5f5775bfc219f808d9da6294c68576a0b27c54741f41985b5f18f59de078da24b6e6bdd6ca7ced76870390b0140db1e24237d67a4f0630ce07cf4e5c35623e61bebb23f6367d3b192547bd17d585970e5d618225f8dde3afe9eea14d37cbc4bb3887d2041497f6da456ec87c961e56485e844f02052518f866aeb4dfcb5b0375396cebc56e0a310384297b795d18074a563d51e314609fff62d31b02d184b998ae609539a8afe5fa3a71cb8a191233edb86a1fed7cc5fedcf4f0004f453133f3f934f42c9d885e04321817497ec35d795a02d4a6ef47387e34751ed42904d3e155ecc90fdf8aa3d7485036c17cf134385a19730ecefd1e7e79bb39e42eb035b632dfc0d32fa908596a9d742a100b10a54ce0dc7bfef937df3bd9a492645e1f06ccb768a14ea31e416cf63804bf5aad2e16422f3f70d6b98b2ee850914ae84c76278300e8a7e1c754abe62c619f750afd8c3388fd62499339534c8da0d5b7c9dc57934d8dfac4a768876021de37cbe5c5dc7f127962d85e337cdc8b554410cfe0cc2f7483b8655340c9821c83506fb00dc6c5f9e2c6d53f5306eec60dbda468c2d577f0906d8c5e5a9aafb7df4b03a8e3e69bceb16ccb6819fb00a94986f4079196c8239986d16009c7e15403482e747dc19b9003cef745445b54eb7bcbc19eeff356531ae13f0fe15656deacc5c54dfd3d38ad7ac019fc714e093c79260b7f33385cb928841a3dc0610e9e13abbacdda2b635de8b7ff3005977c56ef4fa21507b633077cea118bc0cca0fed568708bfc4573ad46c477d1b3c81a36a9058a3be7231470db9b5b455a857845059ff768c0d3582ca5e68dcd934ce1e8fe80eae05a54f1f34b076a527d03b5e154365c587c48ac076ccf931ff6718cfe024711f0acac8a55dcf6505ed5471d9caf5306031218a2a9c389e9335b0017ee41e59ed16ec8eabc206ff1ee649ab3f69b9344b2b624679de96e412c38394becef9b0fcee17bb89fe35963a5ad78b38d3f67dd4c5615e5ca28be9fc9d6a88cc11f5e5ba3bddabd50fe1e000ad97dc6ce3883d30be52d5539678b812e72b47159b0218f5dfc6dc262462ab60426f31993559b69e9379d483a25ee82f732b0715de344dfbec71f2f8feb7105f3621dbeb5d5a29d2b7f0a9ad9e6c4128fc045eb7527da4c61d1e91e838cc1685f2a13fedba39a49c295f096133fc8a9d9cf05bf5c63c5d5bd9b90a43b5c4eeee72820b10c08f9a1802d4b316de2443f398e56b85845a255532523b15d460fede47cfcb981bd5cf43006eedbb9d6b3bfeb7dcd97aed787af8c2a95199b41a5414def313c798dc6f09c5da241e81e7a033b41a12864cc535d211a4971e598be15a9b3985b1a14ab80d59b608e3202bba797228a0418d15037ce18758a69829e11d08368b500218f1e696a73d4a027c34d5301b833d48becfc95e16d1de5a6f3d9dbc9126c4094b3da6334983c83df0182cfff379c531a5992fb800fb0e1b0f75e8cbf141474a611c95d8052af234ee2c65a4ec7093a8197318d1ec55da5cb15d403e61724983aed28499f0a86995396cde99d23407801bf197d0ae2b9ce975a067a3acda3d2f82e93bfd90eee9ce35b8ef48ea487a04bab1ea9466042be5803c000062f558db0c62e419898d10811bde191ac872352973798eb631b6fa75522090a60e6029d4ecef0606f82c1cf71df62e48ac11a2b0a68a75af15f959cef78dc704f07827c9b28bbc979b650deb52bce1c2b30866d61269c2d8215c65b38531a8cde3016a36be75349e6bb2f4df37bd16ce0ab4706e5c0608d225520050029b1b1edc5becf67d0afb2ca48fb0753e820021504ff5beba472cbdefd281fdd42a73048b2c4dbcaf795e90175c9e9a050c135253be533c2f1b577383da39d314eed9fc8159531ce549326aa640ec5214c4456b85760d4c9ba3b14a7617cccb409aae4dca6c6a6036ba8411a532cdfbd2a41393aac2b9717c7ed5acef88eda0df94ef1124d5a5d42e8d68db3d13a29727a939045e1a47d680d38002ab71c5dd98a0d0bb5ab62c6fcd10a3eec092e9f700f0bc81020427653247c9bf103be1e1e60c69b053bdd1324bbf8d49622cd87e12eb26ac6f75fd82ef07bd0fd3b0396ee93004b08d4c233ba6556a31c5ddf9d1ef83e884a65eefe3b1b638ddef05660f344b9c0ae1d14c4dde66cce2f743d4e7f0354f353163a8b22c4f1ba0c3c3e83b56991e3aa701f03f76c8a1e3c6d3f3adb84d9c9ba5cdb963bf2b5ae6e9bef5c87b231549dea3c16c2e15b2feb0cdddb608e91ff57fbea112ebaea7483c859a5c8fb280d6924a80598a42d7fbce3e55bde36d99a2c7909d0ffa6572840db34edc70ba6f05d4f20c1b8d1156dbd6e2bcdbcc4439db81b77dbf48465f5431177054f567e89a77a89df5a9046d70ab1407ff49763aa3f06136476065e052195c7c5f976794695b3f33aa4e6d4559aef90a1799d65453f9cdc28ce33654f34d8d1159ba13eb89a2c85109e4d321479de332554c034bcd23c37597b5340b9be7c0524cf17f29f6e5d033dcd3b7bc31090cfd84066293491c8482f3d75315e1dc5012cc74b7393fae582d5a3de9e6b705d1ae3b"}, @generic={0x0, 0xd8, "03a939a0c23f2c8778de611b809d0f0377d9f3c9c698dd9d2c8592f21ffe4f121e7236e8d751b01edf512308fc506cb51e1ce7da4cd8edfad8df90d4d02687083ba1b600c6cd503c29e8577125ce154c4d8858f68ae1ac4e54e9830dc3c4282f3c37e485d9b510b5d1caa641ba34d5d362097bb105941e6a85183ad36d1184f439985b4fe38877df4799f8c4050f5ac26d01e5778584e6584e03aafa3e2b15b0b028241a35e19b99e9ab88e779299fa2f01a4b119107f0253803f4184aa3e99fd1dd65d6ccb963c0caf61dbea5834f3243a75822cc2ed1b7"}, @generic={0x0, 0xe1a, "239d77cc6cebede3906f13e706eeb6cdb2659c770db5ce25b79b64a9b5abda1c61fbf8b9673f3f976bfce8ea1e66497e58146b669f38fd76fbc6e5c2de8df3b52d12d1b9cbffedd277229cd4f14a9cf1843e19df1a26980e14a159395ff81fe16e37879e0fc1295172fdca7bd217c77f034299c69ac1fdf47785f316f288780c78e33fdf48d774ca78fecdeb6685945d52e9e335a792c6bebbea9bc179d5e7be11b0c4ed8b2e7af938981165b791dd7e7c70561d21dd35c3b4339b8b95ad69bcd2746a8539d3c3c90973b0cf3138303ec27efff0179e77662ee5646930a75eeb3779a043a977441aa0eb8e8ff240df48d54ad681e1c062094030c1ae07ba9793a988aa3c28aa304fb9d0175c6b2eb785d369f626c6760287b9e5c30db5c87b99030758ea051f0f8938a224067207b5bff08243969c9a958b9ab6c90bad94fefeb364af5326f284eff625bdcb921169c66dea43dbc5c883f3350b6bb858733e73449a20f52f2cf20dc64ee107928d525dca54ef8d6fbdfc2189cfcf0d89aa1a71b8a404d8f22239dc5692059126f600520042aad8bce503fa5c6a1dcb3a79a407933ac035e5c57218e2c4e6eb2d2ef404a5ecfc9cdb860759890c6837edc0cd7f357c36246d5d071e1b26596aa0e2faf115c8fe4c318827ac4d163674f7607567234758b501cd6f410c6f9f983e030a6767e87f356113fd5c5573599f14e77a78a3242d5ef9c008fb020535d008c9289979210d2f2381f934fab30f43711cdfe0aaee9ed8c25f5fae0fa15eecac58b995e515985fda2a58127d51edc7f011d89224c74ffcf95d648ba9595c4d5be0d7a557d630dd24de44c19df652b8d8c9778d63e4b7afc6b3ac146349f271a70f41f1d9e1122dabfe51aaa82bf897b26ca9b458c30e56ccae55681d72b9ed73d2297285240aa411f09935a33c54e41e1aab2e64d2fc75d8a9b87b1cf93e0e16d76a2946ac19da5b7dd386ba3bfd46e44d0f623c2d8a0c5e04d77a5654883a4eb53fd1c1ce43d1e56b527ea30e3d4bf219ef73d555a84623a6529faa1a029dfa8cbdc750077dff48c88b2b2ba830186e26c21b182bd60a64db0d35f466d81131f166f6e8fa424f3c2f84bd2f9d90cb7e7186fb0ee4a64702e74a03a7aa82407ca25ba25fa9deb57739c68e93730a8e940a43f00e855842d71f45f8fe0be0df08cf59aeddfa4830bd5b66a5bc6a076308f374812e01125988552dfe91b552bde7904e4bf65a0b7d26c93a66f92756a73a22a845f831cc174dba878a4dcc326a9e682b99379b59aaf1623aef1ee77e668172d1dcd73577309796e08f5b6007e6f687e11818a7adc23e5c9ded8688b29782544bafc366a248064e8a9ad59b428fd337aad9b72bbafac95a266149256111e56826f875c6a736f31f48a810a89f6442f26befb67f2d642c27a1af0ac8e63d169e945101103c62c5bae06def12945e47622a680715c8b1906b3597415d9ec6d1fe719b731f1eb0ad434d9fe4880731ae78639bc40d31d8cf2a93971add1f7aa1f1097eeab75ee083e77bd3fd330c48929e71aad98dbab956d47b9a6b57abf4c78248fdd30a5942f1c55769aaf23b102d308deda23b3135da828928b2a1f8c4dac771f8399433cddc86774a0757140200f3b78e5a9091a641a5cc04db3a93227b76fa50618698520755ede4497a34d947591828b208129c0fdb577d85ad19e0b06d1545fdcf577475ea70013678fa59a6c3452490d5944d4658f832c5cd32f5fc7ceb039cc58287a91ec243d30213d5e30edbac1c7f6bb8268c54ebe4648dbd7eed20d0c1b2f5060ee5a2bea849d6fc1481c7dafe1aeb98e098419aa9ec5878ab56c56695922126b668e68cebc998ed7b10d0326ab6f0041c00c2f0ad21d3f94b9eec39f16c54547f22c93eb21c7a19065eb85a8667b7a7a80e12a77b2e4c7a6e54b98ad6af981f95630970014369dce62d7930f7155a9572758286063bbeb982b32b9bd24983331c258530d335e274663569d0a2184aaa06a3140bbd12e5a80d43e4c613b2d368c6879dca10bb1a9bf2d525d74aa9b755ae6efbaa4e51de3ccdbbda462ac5629bf13ddae933d86b0171feb90b9f084c7d3daf5069288d20347b4f093d555cede107cee245fff738d05e2816229ef56f73e9e30ee12b51699447b4ebb3e193b0024f061cb04cec4b83455f0638776f743362acd228465ae059515f308b40a062f2e66b331636ec7c2caf562f7c91508dea80dcc930b24cdb7e55a10945f911ede42364a9d2994d8615ecb68da2b81ed0817c20d1d407985a91b79bb0b96bc2ca7c61897c01b90f8c93dd99bcc19f9e61288bcbfb4bce9f0a3bdb57c8ec4f16c607d9bf15660e0d6d6c31e54fefa90321535db0bc9f0ef2af836570b4548801f8cd918276123ec95b77467feebd54ce6c293d42210822bcb0e11e3ad2f8e2a3eca81a05d7a59c89610b6c5aa97d825bfa3c1d7b658c8a2b9d46c8e2f7f2a0d3d52904b5f958c13c8f202833dd63cee7032e86d4d3bd80921ab347597877a9374877c8d7c7575b602bb1ed5fdc9ebbf330bfdea684a15e65ccfbf15aa40408cfe3e91dd3ad09a140b4861d9684c060d5eea94adf9ef8e469e279a99b59cb4114d7360943209d32c522be8d314d25c564ca1471784f8e37270b04030c864f54ad25df661edf7a1ddd956f127f75aed3565aab15a32425ee128838e9a2ff5254c0c417917157e615668c854cab8a32a3401ae2089bb0c288e207b80a1d1e405bfd57a07527ef6ebc880bb6e21ecb3abb3fa8a1dca429f1408b44c9ccff09c46deced018c46b3234c8b98970e976aca0e3d395e1e9d8cdee15a43e4bf98f67bc6058f1762a3ab38c16ca0047b32b39a0beb7ddccc18c1b76aa7e49c640df9f116a14fc9b6239ee3799a2e0c886d31c321608cf7e6c38116a93a9cf86cc8a507631e8f21fcb88fb87ac9673efaaa66440d554d20996e3dd596c2752fd526d4da1d4eb6f5772d249df01c3a40a1c0bdf8c1b187c11a17244bee9c5a697378e52eb97566191a8d289c7a83aa50640a45febec8e0878b384725d5fd37e6cf420ab64f3649411648ba5a126b3f378e191e91a2e1aa45f45aa10a33f0e5e22755f1834491253d46b16e4a100ef8f36f98bfd75f2c7ed3c147fbf6071719c35192d737fd85bc0a2a59a6e301cd1e216389c1a863ce1578cd879281c356c674eed784bae1d445121e33049aeb4ec775c90f879f7ee758edc71e43b92b71f0067530863b9b04b80f5e422bff81c51f2a1b53ca4dbf82ca8d0cd72bc09f2a7b4d475b6513670e09a7fcbe691ea00a460953164527625e5bac9c62451ae3180f2f20f29d0617e37e51d693c7fe1226e39e63b389a3c1217c3cc94aeaa5300ad082991a37662f0a295098e26f80ab1a4d48c84f16a022e93544c09f2f8602477f9cab090f4ecda2b2fe5a3941e9668984547d53036b8b3de09320b5419ec4f4e4e8d2e7e55fa67a18b38814f7d4d055ae7214747ab6e70fa596b59640a5711b13fc00f5e81484840ce4f2a0c118d49c862c42c740ab8d5830d2ad4e7f94f6890508c363db166827e6a6ea246ea7e850db121f790928fafbb64daecff68c7df206d293e84fe39dd688ce14b78d00423628d224406b427b44a595b488346b8bc58feb2a25183839ad8457d3bffb774c38c18ae5b2267451a06d2745899c4b78e3ee05a14a920c2df275bd4f489b0787841c8b62d38ca9f195c04b6a974b31a6f6c248ac5ca49f4411ea3260682e643f5f1759efcadd59c7bc010f141af818ba4813ba106d577abfb10b32fd5057aa5069857947124d1ca31ac34c6c980fd8abdcb95a7a512d21b9423258c0455332bbfe3486a890e0a6112d3b32cbad60e6702abe21eaf9a3332c4e28c024de33ba291c6c770646f4a7dd650c981d39c696160b82556e4d7d90e2d799471a67a9910035c19421e1f57ef8136dda008b2fa297cd33026710fc4eb92039fdff1c19069a75e6756b53c0af0f1caa4a1510ad94175cfa7964fc7868c8374ecfef5a6b3538a9e7f275ebcd7b074e2651df6540bece13acb061696c074896b38c5b492488994058b81429ea10f385df3ddc9302c678778a92bce90e28adaaa41e8e9597cdf7c521de9b76aa63220f9ef70a8c29ce8ebbac5eb555de4568d064fece7e389823005266d15695bad81a5ea794426a3bc25f99a66ce4196500226c99e0349621fe8f7b1c0d03f5ee42367ffca3abea843d8c80cda0e0f70fb03a7dd4143a2d0b01be728771019ac61d6292e95f53653502a8b818811694c2498f6467d15f222d1f9a5f00a872b909cdc26ab9fc5c066d85e41e947c06419bf910c7f6eff860fff7463f4777e2c7e45d5fb6ef943fc28a22ae71e66b536745341c019dead62981319d883d40a5a706c0f909fc1e6eee61bf3eb1aa56500236a3edf410764e0d290faa25cc296b926703ffe3615b51d5a17b9f12eba1640c87e77c54f4f4699344fda04aafd3272b8a6c524059a6c200229287b14c04b3afe411a66771b6060475064f459110dcc92fe6d5f7a9876bc9b07b1206f3b16362c2ac4dd7aef02f49f2c0dfebfb79f88e0bfac06dd6312b653c18c1d72779b1a33aff2d69df65d1102ae354e570977d3abecbb24a92ee0d53e854147983f5c97b597828949039b7feb59e8821ff2ace3337dc0cdcd1ffe137ce09d8f4d952d693724b001a2bff508ec16bb96ac7245cad53c8344393a34fded129b6672a401b16f332861fdacdc7faeba68e26782d5ffa44aa40b41ed6b16c3f8cb4f3f78abe3589dd5a412463c700ef9780d2d38c4bcffa4910c8f59ca5f0a4f642ac8ddd26ba4c873cfde37e7458701761157d3e61141ca392c66904a1feaaa1f71aafab4b90f4c1bb222fb312f839cc797a2638d12eb63237f747d7f7c5d288b300a7fb7de412c5996253f2abf362f0d4ca1b1d9b7456488b8bb341f2f6c2694b71a7e588213bb69fdffb6cc8f85d023aa0729e2315c5499d24746b8ea09ee590b39ac575c3a38e2fb0191026a576e9a67b4ada8a835473d6d029d2a3f3c015382e8ee44334335a49a6bc059603365d85f501dd47c4d5a9cafcbdd7e9d4cc4dec"}]}], @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, {[@mss={0x2, 0x4}, @sack={0x5, 0xe, [0x0, 0x0, 0x0]}]}}, {"f024e0ec35eff4f6bbd6eb9cfc997dc5980e60e46e5dfecf47370cd2e2f04626b1d0d3f72e37409342e56e97412b4ba5597ca82cc9cf0130a9a636b68a265e7e73"}}}}}}})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{}, {0x4, 0x0, 0x0, 0x800}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x2)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
writev(r0, &(0x7f0000000b80)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r0, &(0x7f0000000180)="09200909f1ffffffffff13804752cdce9c904451473f0300000029157cb4b0ff03000000000000705abf281e73d9b6338a02bf0a", 0x34)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x4d}, {0x48}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="2d00c784be3f585402424c33c16f", 0x49, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
recvmsg(r0, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000001440), 0x100000000000017d, 0x0}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_int(r0, 0xffff, 0x100, &(0x7f0000000140), &(0x7f0000000180)=0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f00000003c0)={0x0}, 0xa2c4a4fbccc5c5aa, 0x0)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup2(r2, r1)
recvmsg(r0, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x4d}, {0x1c}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000100)=ANY=[])
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
recvmsg(0xffffffffffffffff, 0x0, 0x0)
bind$unix(0xffffffffffffffff, 0x0, 0x0)
close(r0)
execve(0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x001'], 0x38}, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
r1 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r1, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[], 0x38}, 0x0)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x24, &(0x7f00000000c0), 0x4)
setrlimit(0x8, &(0x7f00000000c0)={0x8, 0xd})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97a8, 0x0, 0xfffffff5, 0xffffff78, "5604007c0300e500000116008d1b82834700"})
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000100)="69a76608ca565019deb14e286120985ed8471dcd6760f7ceeec7019a1d22bd5e4d97e2d5d1897330c0d68d58c2751b8b7e772e015d45", 0x36}, {&(0x7f0000002640)="a7cef3a63a716650986bd1cebde4cfca7317d501e364066e7551c8c5b2c244c1602ab76f23d57fdc653901e5ecd8d473b98c22d7ee5bcbbeadd7ebf2dad59dbecc2646645d8c7ec3032a16ef9798d0436f974d3fd75260429560af1188af270dd7b33e235fa62157033e4b58ab764418a1e9ab5fb3518e0e4e415de736cdeb93ceda14c563d17efbf8b7f13b53b2aac075265881de2a5371e08234ec8df6ecd834824762d6bcf755c00a7bf680499900db3c3ef24b28696dbc0cb8b52ad8e4f083eeb012090de42a731036a89fd5aa797cc578f75279ccadf8532a44f1f83fa90eb613631c5cbb27364c58558b49f35a87a656448866230dc67770ec66760a3b65d30be9d87e040f7cb72ad690e43693e0261771ebe6a1574fdeae4bbbb4ec08e65abba8e90a16c183f090f3b96e1f4a5dadad9b5781fe4852bb6a864c89d7b1031a51eb14c561fe18f347e733d739e22c5d205bbeae1d3e0fab909d3af83681933f43", 0x163}], 0x2)
writev(r0, &(0x7f0000000280)=[{&(0x7f0000001480)="c0db3159d2066fec2a33b40b66b725718575aef4af9f59c9e4c5fa0685fba0064ec2f5c1d8c7484822d758066448bdc3cb2d1b9d98bd6fdaacaa9efdbe3ac67dae14e8066e029ff3d5fbd66466fecf70c2ffe4faff897cc8dea355147986dfbff4743dc58ff136a4d8b6309dfb69cadcd3f675c400e60e3e7d50f4668afaa6dc7de4e80bcb7c6336e73da833a24dbf1a1f1d10e056ae940255f119579386dd0b2f600767c3b5e03bd379a4ee31c123c64a67396242d448155472f90f3e5b7b7a7929ae75dd40e1029552187310e441d7dceb42e9a10481c40973cc996c52bec87583c5eb86bd6e2b8dda2c5cf46548fb2c8e7f1bc12d99fc4983bbfe06da04a1ca588994f932945d40427ddf1494a5722fff63f49be8a97252e74fd59f8b4217229923e38536a84765baa589c4a91609689b3e2473958d067087ec1e8129ad5ea522379682b3ef5df5f5bb481aa0a9d428b600ac483ffb9d219db97e245e57b71b0a3f03d88597f88065068b2ef3cf9407e0557582e9431026f076d14f6c152c281c59f7ef663ac9ed32d5555b8389a9e86260af28df53cb7b70e8ada00661fba897a22fc8479a596e55694f246d9bd2f43507f99c0b002089107346da65738f1f067e0fb1e6cc13c5e6a7b882c3f5e3380e4a6de058bdda8318c5b91ab78d887b56c19111cb1d0a595a8f02bacabe883f854c1af4b8a9bb09d36e5030eebcda9ceb330ff5a4f96941d0bfbe6cda5de578efdab85ec5ae1e82196f2f0d0ba0a0efb5dac7dd3ea27a3020e56905f56b109d0a9aa514f41835b5f917179b1d1d1032a087e19a021e08cad5984a4f9547374ae2beb1e7c76b53727608d3f3e5256aaf590fd3b2b72b25d1d6a91f364ad0dd2dcafeeea2324f7cbd7b6c8b87cefa1e56fda22d3cca3165e87ff6da1a0c2fe05944baf1b45e41e6748b2f9490220138bed168cfb1c8d228c751d44fab707624fb8d91a2b9450f78fcc46ae3d6032d8f4731fa97ead3b0d9d6baae6fafa442478694ca7ef5c0254a64ba4c32b329a073c8201a16ea438ea4172b4b6707d9d1aca58c371840a5cd72fb73b2228997d6eee846ec0fd0d5f37c34adc29f1153b50642d54d76e33db94edc3be0fe1ae755d70f2665b8f93fc743709a8d7f134ee27d8c329f7ccc5f02acedfcb951bbc4bee09362f52062821fa50fe2560c7de319499eb2bb480f33fe678c85d49e2454361290d477b7e37b24ab96f0561c6798575469134eabbb5c2a7e0c20ab48ab7861f27750ad83244b79b536630431359dd763b9952116c3cad0a2edd59e7804c76ad50291db3ca5a4bb37dc2fd44a3539f629eebf7db10eac65f3c680d0f8ad0328922e3a906a28513d90de024f98d7a69735709ca67c802de4d6c94c8bfedbc4d16a64df8bbae5308a127c3a0fa8b53dd288d528cf43f53d0f0a1500d8ec6d69d47a4348bbff5fdaff3bc865c2d690256aca23f03f891bd0f67d8da8f39c0585e3d40dc0e4ff5cb0517f3583f362c20fca8ee55a944610b6194a5b3654a736a0d65ed34dc3b047e9695c067dff741b7a4dfd66e07dead80b3d29797e3286fb39beb535d1c2b0ec089d3f384c4340798139af026a8dd57d9299a7c5051f63280738470a44da2ce95ed65b9ff289b13e1688fffc11f6c2783d802fae4e7ba04e52ad0a044076bc18e1d29a78851fed0d4bfeb5b8c142c20410f72eade502c72f5aa4a583fe9230683ad136ca6f9f06437ee0555c2c29339c77cefb0c4a8ed767d2c5ee3313c9e346b193cc56c027a50a0ba52aaf1ed040182617fe9dacce9fcab675915a3df7810bc766d65be94f845ce48dd007d5ea75446e52fbff334899d9beb4b1fc1e872a5ccbaacc383b17a34147cc6969d83fdac081fbb53981b6a78b628392afff707f899f5b3176a79d5c81084d7bea0a650356145c65cfb8a0880cf268354453ef4dc0cc8872d63b1e44dab040018b8347a5eda656797a184b5e27cf5949d56822861545d50a42538532b10b84c47aa9c0a78bec0ac45759434575fcf71c62f231584498e39011045bf883689c4bbdbf8885f016485f1f4c244341445e8f8a3079263d28bc8e1e0301695f87ae0db8d1a94e8d93b9de5775b74a7ea57058a60ab04e7e162d055f107cc3743f367e8454298cad165380a1f4382b6d75e1a8bc8a5b75e5d45df42e72f603d9c1e06450d8ee062d71bb2c562ce2fd878477bb0b983f12c682ff30fe4fcb1f3f71f3254da39a075840a27319c43af36713b23149f493250a4a9378f0492a6be45eeb58bb7f4365bc99a970324e930bc5558291c4a60ac4de554f25cb928e30ad76bc8c2684dc0b1eb64a151acad823c9be854ec27083a3563f268bfd6e7b0d6ef25d3ea36aae333c2f90314096fbbcd3886958123d590791f548114257cd5fbfc3fa5dfddfdd0a6482c15c0e448b5632421dd21c7f6f758e8d5681ab638c9dc57bf360f91706f6b8d7d082933e662a7198cfb85193fd93ad64ee58d0e126f66007a46d699ccc288bff46db53dab0ca1622493abffa11416d80b0cb06c1e246776c2b9a69ff4d5f86dd020bd93039265e7e676362e2095740ffa8859bf532257c68dad4e734cbdc8a947e0b7b8d2632f2045f26381786e1b9ec0df834416945ff0d3d61501935ab4b22493124bfe9f5225aef4596b3004e2d226f055982d3f0f052268a66ae1b4f3331514e58132b68ee6228b9aaadae38d6c8838251326d1a6a9628ee27024a432551f55f0db315066ff1850c3e13e938d2976006d8def27cc4200453627741b01bed5a18c9e2e0d95128f83eb0ec2d9dd7ecf497972c8866067243a95564255d1ce407d19af6c1b36d1fc4c9c81c9d1b3c8ee112d519701835f65c4962123a887fb82846b114269cf25724065f9bf511c06e9058e0afd33432b36e07f948800157b6dfd0172a05248ad5fbb6cae3268a88426b200edb9a98ae0c8b1f350c3196ebf9dd00fb5340b1caecc080de97834af3b799dd1cf7519b712883e37b62a434f70d6dafed6c6dd7bae0b5faecab8f1a7fa91677a31bc2c2b603be3f1e85b92dc0e94fc5313b7f5b74670d3122a6c1e2d13362509e24336bd9e288765c44b50679793c9d478b25d52d1dc771e3ba27558db9c9e5a9371ffa655a4a408c8279a6d17c0793556beab8d247b2c8fa6d3c20056a1326aa57138e500eb41ec28df84c5422020e62705051248cc00519d8077b9d4270053f15032fa6ca5d6733764fda4accbf21a9d1d544a09e4e6e87bc19cb7dc7c96a9d7e74e65fecb521e63ca6599381cb9e9d6c05057750868a8669be0f8fae388b33a4c8ca9f7e35f5cdd0d9de6259c4ff5f75050b317dc5ddb3329fdefbd951aee12149a1b96e5a2a1cabc00e4a3993edb8cdc268bebb6243de8ca312b5a8d97370cfb2e543e67a1ff9390b15f882b4efa7dd4120401d66212227ddfa2c686feabb6460fcc44f3bf566ee824c764f3eddddc45726703808c18ee3256ef3867270deb0d124e26b7e178a19657378d7ebeea4922d415454a0e0f38cd0a6c940826115285cda0af8d1187715bc705156b96c3ad5c002f079c75ecc70c6cf15d7a55440cf8abbe84c7be707641c2dcd92a27db8551c73202bd79c7b1dae2271abd298ff9c4e9965a5f2b1e6c96c34b9d8df924d7e58c50f582a660e14da7d46881963cb25d20c77bbfadceeabc7dbf3967fd281f45835ea6448fe116bec166d436b9a18cf8d3a018dc727efeebb1e96232003c208be3cf9de75bac511bd807dda92749c8b02f51c2cc25c8b5f8dfbc99479f19d75f43dba3e9c45021fc4945863a3c3ca4276ef2dcc3d9b2d53bec8f30a686a3efc162ae884cf1a8de9397604102d5bd52f60c94ef41c832332171d7c60365fdd4c888c0598656bc31a33e51c8e4e8f096754e4441294bf66adb990901806b39c9584d142182af4be39f78da3b3a757906d4199e479ef24a381773f485274ece066a5b7d8afbd5fa22fbaaae697774bb5d84f81ce6a6c631ef4c37f9f793f2804e95420e0176dec1e23e9719fcdfe8aea600ca9d4c377edf306612ea048967bd31c77f69838c114b5f626641e639cbfe88524fe0047cc7b74604537e22d65156ced142ed850fc4ee409ba38e65f8ebe8136ef1d28aeff5bf76c6b510249792fe0a2917946a2b649c64a6e1da90120ac95654094bcf0336a16dcfe26ed1d620238cf4da76df3aaf1cecd31b9a245b84a8c879dcf9943afecc64df883b098fee52897971e85b137fb10c644ccc655d3a62f8f31cb7280b85ea4cf302b67d17df66c4b889502ab34b6e744a7e6f9ff03c217fd9148207e0d90e341856edde290841ce793b2d2ec3a7d07c8f06ac6bac3539db18b872257c6487808a23281d2861cdde21aa4d66f4b60f0ef9a8cc021db545160c45e19b22fe402db943ac859393860aa62fc229c22435db9d57706459dd74a5dd75f421420c978d124f4f6c0e1551404f94429d8e447982383b7e3b5c564b1ba8779d3cb01e317503d7311c6f1dc3cdafcfe7553140e70d1eab1d91bac4554b3ae0934407ea5f48e2562b329d4a1a1ed9a2af804ee45d502f36b6959b87ad2c6094f35341b9adf8aecc7ddc3487b524ac496ad0efd75fe84ce755d420c9b712a8db44a470dc35a9ec20d613f46701c23cdec0a0fc5bb7e6963888f0b954838e23bb6624047610aa50d9bf1626a41be2d277acdf0999b7a1cb88a7f0a3d66b47db0e2a6ef705db01fe66ccbfc19", 0xd17}], 0x1)
r0 = socket(0x18, 0x400000001002, 0x0)
connect$unix(r0, &(0x7f0000000040)=@file={0x0, './file0\x00'}, 0x6a)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x21}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d7300800001000000004000000000000bc00009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
fcntl$dupfd(r1, 0x0, r0)
r2 = socket(0x18, 0x3, 0x0)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206979, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x48}, {0x61}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="6d9ef757a944e00af2e26510358b", 0xe, 0x0)
semget(0x3, 0x0, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0x0, r0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x40}, {0x6, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
open(&(0x7f0000000000)='./file1/file0\x00', 0x8220, 0x0)
chmod(&(0x7f0000000100)='./file1\x00', 0x18a)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000180)='./file1/file0\x00', &(0x7f0000000280)='r\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000400)={0x3, &(0x7f0000000140)=[{0x1}, {0x3d}, {0x6, 0x0, 0x0, 0x101}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="e16914f6357e3a00000015000000", 0xe, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x17}, 0x4, 0x0, 0x0, &(0x7f0000000240), 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
socket(0x10, 0x0, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x80, 0x0, 0x0, 0x0, 0x2]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
lseek(r0, 0x0, 0x40fff)
r1 = dup2(r0, r0)
r2 = dup(r0)
mmap(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0, 0x812, r2, 0x0)
writev(r1, &(0x7f00000011c0)=[{&(0x7f0000000040)='/', 0x1}], 0x1)
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="6f18"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffff9c, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x7, 0x1}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0, 0x43}, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82023e"], 0x10)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x2e, 0x0, 0x0, &(0x7f0000001440)=[@rights={0x14, 0x7}, @cred={0xff0014ac}], 0x18}, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VNDIOCSET(r1, 0xc0387200, &(0x7f0000000300)={0x0, 0x0, 0x0})
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x4}], 0x1, 0x0)
mknodat(r0, &(0x7f0000000040)='./file1\x00', 0x2000, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
pwrite(r0, &(0x7f0000000000)="505738f2138d2e49a8947a9155fabcfdcb7c307855d43776b19edfb893d5f6e5916c36", 0x23, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x806, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @remote={0xac, 0x14, 0x0}}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, 0x0, 0x0)
poll(&(0x7f0000000180)=[{r0, 0xde}], 0x1, 0x0)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xc028445a, &(0x7f0000000240)=0x65)
r0 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000500)={0x0, 0x0, 0xe1be, 0x0, "b06d118c24f6f979cbd500d96cc0a5f09d638b0a"})
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x1, 0x1, 0xfffffff9, "6da43a51f374f70433b6aa91da8257f5f6620d7d"})
syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
poll(&(0x7f0000000280)=[{r0, 0x1}], 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
bind(r3, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r3, r2)
listen(r3, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x4}, {r0}, {r3, 0x1}], 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSDTR(r0, 0x20007479)
sysctl$vm(&(0x7f0000000000)={0x7, 0x4}, 0x3, 0x0, 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0xfffffffc, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@random="51d2e863332e", @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @empty, {[@ssrr={0x89, 0x3}]}}, @icmp=@echo_reply={0xf}}}}})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000180)={&(0x7f00000002c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000340)=[{0x80, 0x0, 0x0, "82f5b7c088168ea73b2a234f1d3b30e27d02fe88e821e10fd4a82dd08aacd6b9e3d837e13bc6de321558eb8ae354430438b02daa1ba87a4243d67e2b85e5f84e6f9cb53b63da3e6d384c4ef8cc083abf9f169567000000002aed347136bb7653af6bdd4b698f42ad43"}, {0x10}, {0x40, 0x0, 0x0, "c8922229e00e78df88a5b7c4161873b4759bfd22f62795f2829afef37a029152ffb8896f2424a96e4d"}], 0xd0}, 0x8}, 0x10, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x310, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1112, 0xffffffffffffffff)
clock_getres(0x4, &(0x7f0000000000))
utimensat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', &(0x7f0000000040)={{0x0, 0xffffffffffffffff}, {0x0, 0xfffffffffffffffe}}, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000080)='./file0\x00', 0x23b)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000001c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000200)=0xc)
lchown(&(0x7f0000000140)='./file0\x00', 0xffffffffffffffff, r1)
chdir(&(0x7f00000001c0)='./file0\x00')
setuid(0xee01)
open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
chmod(&(0x7f0000000240)='./file0\x00', 0x0)
setitimer(0x0, &(0x7f0000000000)={{}, {0x0, 0x8000000000000000}}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000540)=0x8)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000580)={0x10, 0x2, 0x4, 0x0, [{&(0x7f0000e10000/0x2000)=nil, &(0x7f0000ee9000/0x2000)=nil}, {&(0x7f0000bea000/0x2000)=nil, &(0x7f0000cb2000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000bbd000/0x3000)=nil}, {&(0x7f0000f6c000/0x2000)=nil, &(0x7f0000cff000/0x3000)=nil}, {&(0x7f000087b000/0x2000)=nil, &(0x7f0000800000/0x800000)=nil}, {&(0x7f0000ee1000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000f8c000/0x3000)=nil}, {&(0x7f0000aef000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000914000/0x3000)=nil}, {&(0x7f0000f81000/0x1000)=nil, &(0x7f0000ebe000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000c62000/0x1000)=nil}, {&(0x7f0000b47000/0x4000)=nil, &(0x7f0000da2000/0x7000)=nil}, {&(0x7f0000b26000/0x1000)=nil, &(0x7f0000825000/0x12000)=nil}, {&(0x7f0000d22000/0x1000)=nil, &(0x7f00009fc000/0x1000)=nil}, {&(0x7f0000a21000/0x1000)=nil, &(0x7f00008c4000/0x3000)=nil}, {&(0x7f0000bfd000/0x3000)=nil, &(0x7f0000c28000/0x1000)=nil}], ['./file1\x00', './file\x00', './file/file0\x00', './file0\x00'], './file/file0\x00', './file/file0/../file0\x00', './file\x00', ['./file', '\x00', './file', './file']})
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140)={0x2f})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
syz_emit_ethernet(0x4a, &(0x7f0000000000)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x14, 0x6, 0x0, @rand_addr="8ab32d78d67f3a344cbcf951919a5b00", @local={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5, 0x2b}}}}}}})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={&(0x7f0000000080)={0x0, 0x0, &(0x7f00000001c0), 0x0, 0x0}, 0x81}, 0x10, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff6000/0x1000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ff4000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ff6000/0x1000)=nil, &(0x7f0000ff4000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffa000/0x1000)=nil}, {&(0x7f0000ff4000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}], ['./file1\x00', './file0\x00', './file0\x00', './file1\x00'], './file\x00', './file0\x00', './file\x00', ['./file', './file', './file', './file']})
r0 = open(&(0x7f0000000000)='./file0\x00', 0xc02, 0x0)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
mkdir(&(0x7f00000000c0)='./file0\x00', 0x40)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='r\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$WSDISPLAYIO_ADDSCREEN(r1, 0xc0084427, &(0x7f00000008c0)={0xfffffffa, './file0/file0\x00', './file0/file0\x00'})
r0 = socket(0x40000000011, 0x3, 0x0)
r1 = dup2(r0, r0)
sendto$unix(r1, &(0x7f00000008c0)="50010504000000000000000113060000081010bc9100e76aab810f28db240f0d746bb1fecea11ea8fef96ecfc73fd3357ae26caa1016fa4f376336acf00b7804be781e4991f7c8df5f882b29c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfb6a000000000000002f310b404f36a00f900064e0629616b0a8e7000000020000000000000e1208a31fd3f80004ffff00b20000000000000000000000008539cda74d1467802813c67be2313927b913cebdbb7e563c7389e9f6b576837f11c34b0cceb2024db224dadc0640906fa45d709da9d158d945a2a5fc1233cf6e27749bbd97bb0d03fe2d04a1b9ff40195f1fde95d68896dc7ed7b187906698e04e34248daaf9231f0de030323b4125ce41203583671ac5e51a7a3dc1a0c21d548f2de6c031b7b303757e60642b45cbc5737b8e240575710000000000000000", 0x150, 0x0, 0x0, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000001680)={&(0x7f0000000140)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000080)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
madvise(&(0x7f0000ffc000/0x1000)=nil, 0x7f7ffdee7000, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c00000000", 0x8)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
setitimer(0x1, 0x0, &(0x7f0000000040))
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x2f00, 0x44e2)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
sysctl$hw(&(0x7f0000000080)={0x6, 0x14}, 0x2, &(0x7f0000000100)="3e080b53bed6b592", &(0x7f0000000040)=0x8, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x40}, {0x5}, {0x6, 0x0, 0x0, 0x850f}]})
pwrite(r0, &(0x7f0000000080)="fbaf76166d2b22c071d41e4eb71f", 0xe, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r1)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
semctl$GETALL(0x0, 0x0, 0x6, &(0x7f00000000c0)=""/5)
utimensat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', &(0x7f00000001c0), 0x0)
r0 = kqueue()
r1 = socket(0x800000018, 0x3, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000900)={&(0x7f0000002a80)=ANY=[@ANYBLOB="01002e2f66696c6531681695044a16b7a79a1664f88e31e1f01061d7a28af2a23128f689e40db03dfad5b14cf53d46f48d328889e51d46da66e06b136dafef44893a2a385079a29bc93a4faaf293d86cfc7d0b2b949e89f3a971d0e552e660c05dfd4143473e20529d8a9032d67a1ea995db99096330632b02ff32007259ed3842b41050b418b4a0f7c1451a89df79ab16b1c9a7e36852f1bf1a1e9210e8b74b3673b503a6e7ab964b60c83bc35f088dc741d337f9657b49f59beaca0b8a6c9298f60ff9ff34d9b1a7eeaa5e1b5648056a105db7bcb9a0f7cf80f921956674fe3a7679609fd31f01d5042cd73e56ef63b0bb435980b8"], 0xa, &(0x7f0000000780), 0x0, &(0x7f0000002b80)=ANY=[@ANYBLOB="1800000000000000ffff000001000000", @ANYRES32=r0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32=r1, @ANYRES32=r0, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32, @ANYBLOB="0000007d36f40900ce7201d8b7ca0020b16557de4888548c2593399c127711e502c15a533c30b0bd50060ae5be413766d12e64c5c168d0bc3d8faee501901ff448d4f10002f7e0b6ff75cbf6279606c684ae3e7858dd0387e14038f9e4268be04df5134443c859a82ab78ec1aa9d68d7a0f4c3b11a7269cdc8d4b10cd35030de6538f6ba007f0f34a41c8bfad5891d4eac9a035b52861fe95f1ebb2af75cd977a35a", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB='\x00\x00\x00\x00'], 0x90, 0x402}, 0x400)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x8)
r2 = socket$inet(0x2, 0x2, 0x0)
getsockopt$sock_timeval(r2, 0xffff, 0x1021, 0x0, 0x0)
r3 = socket$inet(0x2, 0x2, 0x0)
getsockopt$sock_timeval(r3, 0xffff, 0x1021, 0x0, 0x0)
r4 = accept$inet(0xffffffffffffffff, 0x0, &(0x7f00000003c0))
r5 = socket$inet6(0x18, 0x1, 0x2)
r6 = socket$inet(0x2, 0x2, 0x0)
getsockopt$sock_timeval(r6, 0xffff, 0x1021, 0x0, 0x0)
sendmsg$unix(r1, &(0x7f0000000480)={&(0x7f0000000500)=ANY=[@ANYBLOB="00002e2fed3ecb745a7030d4cf89c7a35d86f526766f95b6d0"], 0xa, &(0x7f00000001c0)=[{&(0x7f0000000100)="add60a9b0915009e", 0x8}, {&(0x7f0000000a80)="74cededd2072b1e664b28d481a4368b958f30788520617995402564577ff83e3a77849c7883e678b0b3be81fb49c34dfa050c6fbfca823ee519fa4d6ae57321b28533faa8a988b647c9e87d6ef6ddf4f1b517257b13d6274ec80609e3f9484590861055721cc582590f432f09bee28ee8456f9eeb8f5a8074f9e728bfb704f8f39415881eb2bc9c7bea65729f69c543c1245735ab4790f34904cf63787c9d611d4eb27deaa1cf9b804061612bb7bbf4247ab504ef3baf6389f60bbfab827e50f3879de90cdf0d8715283f4ecf0d3dd4f291b8c22fb1d76d14e1b03433db48cefdb78a52355581c7335a3fe587d8894f861ec154a22529688b71399b255f3c51d3df346b1a3db4ad92bbf8631cbfd4d7b680d16326e98963ff4e03f470fe42d36ec78ef852bed4d2bcc89575079ed490b854c189775fb4cf0a6216f4e8bb79341f6069616b47a253ed49b2725086588ae7204934b90d8175836eff116edfc075bf7e4bba93a7e1f5813047ac8c6e4d6f6f44f37a246604489507d454f30fb41c7674d836c58755106d8b27d001224bc35eb1db1f4311974c4cd90418324b4c4d6d9f8b8d208869ad4f4e6412e8ec5047d053b0097087cffaaeed1bd6e51a129b4cdbe83e624599b084e6ea28b56925dbe2f01acc63c8ba24e9e629219606bed792b7bf52c9c7cd530bc1106543988dea4c803490d3a49736af92498a239999db964c0dd6030ef3f70e31dc30c57b40a80d8f90dfe51ca8cf4547d4391ca1ac9b0f26604ae1385f97c34aba775694cff7dbdee1511b96ef5a0f4032dceaa7f9897647a0a02808d3caba71e171013c96c1515ce559fc38b84c147ea9bc66024bcfc7c51e1b4be42be7b0ac4370b5956b5604eff6c0daf5117735a6a4af2c749c77e6e3e17bfe1aeec4fc4399cf9ec65dae1182e4980147c64a3a67c892b57453fe4892ac73b8cbc7ef063556730f10303df274c5069106afa8755548481c2121f0cde0a92216461a2909e21c1585c798fae0ac6ad3cdccd60eaa6c0fa9efca52196ff888398f1065360f4f58b89b5b5a700be9fd5a9a5acb1657db3498d1d0f99b28fefe1f329340278711037531ef8dce464b40ed01a2728dfd09e177c343dc342fab68adbef2a3cde78496a3465a769f2cb7f8800cb8a56479837eee52af53cdaf562126b69d5e22a43f35cab573f78f9837ac52d541616f4bf41d16d03d8e7aa095b80f87a0e64b080ce1c67c3ae9f25099284504c5482b35c4a0b47f0878ca2d93e8498fbfc7de4fdb2bac3df11554ccb87d58cbb2e9f9270338828b17fa7d0e8ae44fefa3aba383fcea29291dff3b2e81d765713a314b5df546b542cf95ffbc4e3f58ae730c30d8bcf1d4a659909135b16add982a8ebf20de371f2549d41f7fc8e3306b12dd3399d23c00dc2b8db0cb4b07cb4b266cb20f13579af3d798b81f97772de304080a54cbfe1d752218c9eed9087ea7029dff1492e7e88f447adaaa3e89a3329b52f764d732a6e0e92fbf343a77cc7679178db410a62cb9f5321956ec1ab257da0ba13599f8b276d089a04de3e6f4db4dd40dc4fdb959544f4f8dcb960daf51c9487f911834d29210d59bfa31759d1c30603b92dc460e6818c5f928b0e4097d646d0b65f04bf7593ef044a90ef503e07f09d71e72b81654377a8bac6d689561af8d946b088439cf11d95bc9f3e4e08897ffb0223da9403b64ddb6f03cad13499d21f1f791660c5abe259aefa30f1a5dbffa41e78d5c1e5942c18785173d68e876ef7af2caadd5d572e7f5925450f478215d17ab6274e1852029c64377bec087030562e7558666ab34158b3cd49e082492ac7d5fad041a4ea712d6ba980b2eb4c12ba7d130b87db0184f6b871512476008aa0d536481226143028fa7bc63f6ecdbaf26dd0cc9318804754e034446a51d12e0638c7a22da93707bb29f4bf329982b755b1ed862fb38d21d95d91c731f41c4573589829ff8266e45053762df348fc00e57b0398f8c58eeb7f2a11140f0b212d8d69ee4de9ea6bd0cafaaca35ab914e91ae06443d6d9a5bc702c348b03de6d30ed998781f54775d9c79e171f36c817ee307cd03f4257b25572e502881b1e881c867b69855e504c378432ed7fc9eca259e3a1d13299a1f6ea78e4ff27ff92bc260068e3b4d28edcfeff680869430a813c4e104858b9e0f846b48582add300ab85e53582e1ad9363123c1c97c3d3c8b20d3853a5171ba63ef4858981565934933705c76184fddc79437bf247a457d6a4d4c43ef9f755d5dff03dddabf77bf1d498dbe3a34a7cd1910cc84d57a6229b9251d452f8a97a80cac3a6a9ba7caaa3c361a99fe06c0e23fb3bcb7d452bfb5ba94cbbc1cbc2970d1461d793faf3f55ada74db268666639819b081eeebbd398cacdc41e7087a7556f305d4a9381cf00f9654c669bcf9d81ace9e992a8d0e812072f429fe1cd5aa1655ce996bb671f1facf1ba85e403026c4e411e7fef4421ff16459d2e58029faf240518115213dbdd0dd3fcacbcc5297dd189fa43fba897d1a8a966db0f02751c4ab71ba9ea65671cc897d3e72ebdc5c6f2dd3b5b85053e16dd3caa55e4ce3a75875bdc0374d8868afc8562bb02867fcafd3566cd7fc57e5e1a28be731723ee1550af99ca13756ecd9ec764e7a65480fa7d77c54745897ffeb222bc6513c09b42f12eac0969638286cc65927ce23394d65f2f72e4f2a6c67f6b57ddfbcaa684710a8f1f43dcf6aa2fbe64a21f209dee9ac3e3d5c535a3a62c214cde2acf3197ee4e06d90b532560e0abbb315088a3523ac5eae2fc9686bb672c6ec05b3efebad663ca8fb2af1be039e65e54dc2281c5811fba5220f99a9baedfab497abf639f6b5b0633cf912034e411ff1c2c033691365c20b4b2eb87cf6d2baf7202f0fa22c3c9a89ccd9cbbd68ea4a772ff532e66fc69d7310d2bddd1f89bbbafa0fb12d340ec5a0b9b8406b71698959460ee4aa34f490f9f0aeb717750b08abf8fde3c208f3994c3b6cfefce9a107a2f3cae644ccbd530a61a40a7a606d163b8697d31e2807ee8c01d6212b97ab7107614933ea9c18414f0757596a01db79feaccce63edf8c597781b0a683a1fb2dba454040a844c15324e33be1264147cfaa881be95eb3c89c7b1a084ad52c375af0b185f66fea0ff1a9bae75c55f1a05c8f050db104b5ed700458e945120e7ad36f8be61ffe0fa8a208254156ec1aadea20a61984c02a6d73d3391d028e039462d5db1d1159d20f65315227b64b90fb1e60d9ca3ba9c0066b96b5844ba98b8d268061dd29e8bdfada33b359e74cdddadf74764ccaddfcc59536dec643aed7eea463183a3fd8f2181fad9fadfb605dd5c25b3fc638df9c7545120f12eb75e7524bd377c4507ba021923e8bf5f57c1674387553e4f23708782f0ea807fb7c47eaeb54ad72742f8e56dba345522a02996a751666ae2cda4312d6028755e0c119c932a5afd534e3edb3e3c54e7e088b64438614ecb77e21fd4fbaf0c51a91cd002dbe1d24ee63f9068b44f1ef739f4ac14e622141ed8eab9efdfab13f502085c2b8fa845e2847ff69a7e59f538256ad09c4f7b22b364e670fb7999645f4acf8e800f70c12b0f76e6c68aad0269457645018624482ce37903db27cf5e6847ec5564bae346863005e561eb59a601ef0011b517a2c3351c8d64ff9a2d65da518eaea43c02c369f6823eff86bb7467770fd9cd2a23b0f0e74368ea27c5ba6b2b149a27cc7851c73304c455f819040c88ee247a14a6c0286667cb83aaa5f1756b35d6661ec676a85ca61fea6eb3fe166e41a0503a4e5635f9fa83e54f7955b86bd9b6c22c4bc254a5861ced30cc6b13e6c56cd22742aa293983dd687ff1e324c7af12d6ce67adfd3fc4bdf51591c95cf95f23b166844b5f80c8c01f41746c8109f37f53355c36a75d82bc4e04d010998bb21603bb7a7514567278bc31e664ff906f7a8367f3df36cde922862db7cd1261bed40b7aa8027fc0faa3708e3c5d8716a839caa0813c9783e21a02e5b37dd43da7066b3dab3421968b54cc7220e83a531ff7befc10612534fd87ef101c818b3913298d5636746bb5f929062cca4d449057492fea87d5821684645f3d3ce835dfd4144c1dca050581cf052b0f636158b7119706d22ab492b6e2377936d5db8ffa3903846d563b33563c72793ddc9062415bc08aa66bc20a33792c15e01974356e71a712f7e04825d29f5d614e61bd89cb354a818f5eceef30e385e69c68b1428c8916665e1395ed10a04c30e600224904cdddc276a16b629289be2dd985d98f21338a26b49bb71974f1b3d9ae3d457ea5ad5ea93e1667d852855da6f886de457846b3a2f4b6bf5728edce1e352a09763f41b1bfed5cf6e8bf615357bfad23977bb2cd2fecb4a015823dc1f9ae09b88fec89c28613739dc8919d15d31667690d9523ac373a2ed3c60ba5f90178846bdc4ac7a338fe176173d2acbfa3b32f3392cae558cf5422a4c8fb1a5f9c2e9ba530222548cf2f6450450b7730f084a5654058d8c6aea9956fd1201bd1d1a69ca02d1ea8ed3f8730c021b5852bf924a23407622b18fabe576cb95dac6343dbaa849f4a0899739b59b77b3a96e5557a427e3667efaf6f384f8725570c67208c8dde2c7862ff252cd4bbf283308abaf4d416809ab52e0810cce7556d266efda72f4e3568e805b67276f06caf81eef49609413c34027f75fbd84fa15c2ffdcda8dbaba5f49b2d721a92b3ef1bd6e2d3bffe8702c552f3183438ac77b17f70a4dc2c2e3d8afb05f27c236bfd983e7bd6c9cea34431f28d8df08897e44b0a5957c98b66f97d0fa7142b095591bf3e3c16b94596dd8b1ec31dd0f097226e861d4dd000a64a4b04a0095080ba28ab151d455f9c7fd2e72880867a6385f6c06033a3216cdfb8504087b2cf0aba94dea80af1ee3817d94048263375fb9c861aee8b48c371f6c45eab5af37293e9733db20b9080c3e054735e54b74da05f394a3fc1f4b8eb9b35480ef68189fcd23e179e75adf6681dc8658be76c8b6808123ef5a9b887954667ec5b3fc463d2d72ade893c4f071b64bca71ffb0d96baacd30c0cf32d6653930c618e940ed4bac77084d2168a266a1e283f1b017b95a07fd49f6817c8d3e2378bc0a31579c9afab4ef16fad5a3aa5ab5f711521ba9e334cd0ff5c181a041f3d48e180abbb946860fa6998a141de97d0df4020b2abcaea607e8f7d5c7140c2bbc61f3cceb06ad7e94683c1ddcceb03a8220d08248cce00963c667d1ecb5f181c5c5cb9be940a083c1783e6335a1a8a7a74ec0c77b1e192d0c64ac7a973765a046290e371244ad76f7f22da823e77f704c1b9b77aaf26176ce49d9d5e2a3c26040279bc2d841b9de56aa07b30569a1e3be40e1ebd9016f9ce0259f9602c3a96952c6142cb6ead5b89cd975afece6abb9ca892f545fe2bd09c24c731dd17f98baaaec5408396575e25d779f1527d7fd48a0afc10edcc4e5be01e7cfbf293180496868b3e403e4051a303152a130cacea72c230feab2646b0f50865605d548978eb143776cb94319eaffc62de50be23454b714b60f6a793e409bd1a67665a1d8a4dd57a6d92cd3e8ed98c5562510c7fc7d58119125502e9174f9ac60466b9f91931232d030aa0b02a0e3e7ae243e889f25c4e5eea37af2c91fc7721dd3e0cd57183e7849310a2cb37248ff486ee62c90d279f7164014224d209ddf53018624f1e414fc659c0cb3fd0400809bae2bdc6ac08fb3b0a4297f79184a5cae42e51c379c69c3495d17caad18e9caa64eb5e54527fe56", 0x1000}, {&(0x7f0000000140)="16a49d194bc22a3b240518ffb53edf8c8821543c9e6c2d25debd9e01e0d19f7d136a68760174be60b590b09d209e29277b92099f7bcf299e90b25dcfc02729acb965863d208bee29aa1e3525f2d70b30f091ddbc391a1bf646fee3582243274624ede6", 0x63}, {&(0x7f0000001a80)="60bb701c651ad0fef6fcb625b549e8f20f18cb43bb27ce07be9387dbe5368f46d2b24eeb753a6243e8c1eb2946640fa735ea16fa468a2484ecda8e3fd0afd9a92fc47f7c73b3ddf0aa5dc178d8c660fd32f1c94b650c418473eeb8d3bb5b40d40f82f3584d346dc40147c1a0e289d9fd30d0365e7404e57fae68edbd61cb685287631c5d30865a4e9ae4ca023b9188c0e3e38dc4dfeb298ac57b30f7b97d95453d851b858b500c8d5bbb118d9b8f46f3e611bb9452339b2ce9d1edfeef9e15734fd1e83ffdc59da2a9e036f2c0137eb47a2702984ff07e0b782b9617722f3e12d1ed48f90524e3ecde138171025fb2cf78d4d563e9be554737eeeefa532a3056eb9a8f01a8cfac2b7c8cbb074b5dbad5bd37b8b9bc14dcdfdabbe274279d19a81c409914ac24dab20aa398a10c168930779d81f51799494cf64c2cfa7accba157c7e81a4ab8902913d2ff3783e97ddbeb412f9d3348869ec5a249b0214aa6cd762d3ca6d28bcb31bb147d6a56b80b165e5d67f70b05fc6921530d836998eea2068cf61c971a6d4152ab96b8da049798c90100241221675fec98cade9caadaf5ba0b13aa60357345b94f12fa340b9c6032c6a16eeff83674da27d09312a7ae2487a0a039fa94f9937399ccff2ff7039c39e2c3ca8ceb17641cc295a4f3c1dc1c09a965f9f021239dc2242d8af013dd58cc7b6336ff75c6d5a50f4b71f8b4d22dd0b9cdaee639efc34d3c00f4236f2975d7bca572d0389a04ba890f7247e4cd2783672c86956dcb30506564deb13a26c4347acf3f6e36d4c035fc3081ede2ca2775a21bbb61b0a71547de7de0ab7b3e6d50cc37c64fccdcd2ad7621dd993fd2d3d7a242e5eee048660e91049514bfc9c7bf02e178595f7c472ae3822bcdcfac7831734c2f5aa13a3af53005a1e9ba836200a7fcaa854952ac1f0b8f83c3c7af2a9510f8c76f40006205c8b0586af2baae14d6afc9fb344dca7494dc4ebfe808ed5861fc264226c486994641debbb04781318b06977a38fce47a0265931bd68c2c127ba896c2f29001025972c95e0c5dda4a3285a3e8a2b9dfe575339922d97821312e23cbec004eac816618253aed14008efc6312d22fecae606c1bfffe14c65e72e640955f3a02b4a2cde82b838fc014ca0380b185ad5e82b309d6ebf1a52b43e3190a6f91ac7592a17e1350a7e91fc1a4c3085a5747051a8436be13234e70ad2709bc42534a9f84ab7936a68374dd3ea7533ddeb73cae3611bcb8471ef65a5b9bc8942c3aa6faacf1546da2fd2652b3f219829da8ef36099fc80fbd4eb55beb29e8c237531562668f0e33136d41a39a6bb4be2c1fe5eec6044f88d19301b020db517d8255c675f8dee20f2306cafe573ac069073c4e9fe5e996d9bb67aa525e1e37a7adb1d77a5f9010cb3fa7b36f452d12afcac82a528ca9dd380f64390217e55dece5e96be8632f087f6794cf39aaf7d29d03b4227518c452093adcd911abb13f265db7cbb360383d5b4be6f1a1004136a0cd80769e1f873862ebed5f9bd9ce2ffd70d2a93b10b6d094d6ffdefdf7cf464ff9d558722aa1fcf00c058a16e07b7271ee84371a4256852ae1824cc45869dc7c9c7aed4f5a1daaeb8e8b509c6095d3d9c34c726d0eab768b798145ba8ed66af802630f7730a921e65b85b74dd8561dd4e44e4ff48de81da8ab287d22bca761af12043ff81feca0dbdc4340158b3cbeaa9d0c585bfd6718e4ad816cd97f23bcc9d6d1d20d90dc611dccf2a3333fda76efb3b866798525ef38c365fb0c53060ad22cf70473895b1f948f0746ddf46615ea0f0ac89c58455a3593022ee716ee8527e81a27265d07fb572f5fadac921ce3f8ef3a78a90e5f1630c4eb246a5326b7d69086a4b03d72add07712632db37bf9dd73c01077a078f9f1c0f84b39e61c43d3e6139c892095ea79c7b067f03f15f3123c0a24050390ca222af9299a95dcde95440e70e57aae6275190648ab374eaa61dbd68da1f227f5c66f0e5b75566426956f609b05200882628d2eac999da7d0ca99aed8a218bebc829e34a578f29b6e682c3b436a88b4a620dbf9a92a59b663b3f25c93c4938923476f41208eccde7a8d51d9ba8e6daa2f6ff393ebc86537c7e8f48ddc7efef54a040ae3455735496d1488a2fe70095d25ac8778c1d6364bc8422542597b02978e3301d0aae149b91441f9f4f99e9097de2bc8a4c25fb2685c178dae802ff53f072e5dea4e04e76285fb352dca5e82a528c1987f4d72788ade65887800f4404e3ddd782a81b84ad61bb4a5ae12ad118da10965f9fa531f43f939bd420bffa83905521e86ea12f801b306c65e8f3cd96599f4ff041ac54bd5033c4d0fc1e12db809ae6c1aa748ec6dff77f24274ad292a8ad1b3ceaa25338661a77818abbbbc00ad59625c2c9468e3e7daaff221034415aec888184b95f4750d5cfc534595b10def97a318be01842632f03edfabfdd23e64a1d2cfed812490b84dc9d436b939e435a8f1495e6a9ed03fca4c03f51d2820eb9599c2311dbd431e5162d8a77c582705c0d504ab8e8da7eaea46553a6967a3735a7651e6361df863b15e4250e705b97c7bbd357e31836f3222f96fbc20fc564cc411012d8ddf5e4239a39b975f7471edfc37938ba078835cfb5c9cfb9a5f6fd2ccd4cef8862e60c02acf6e3a6ae27bb3441b84d10131a46bd153e46858f7938024a1bc238c3fdc0a92314f078d9bef966ee0136e6debf3aba3b2f1bfff53cb8870c0df386ac4e63b634812ca1516232c5bca0ef88041876a47cb0491dc41a68a6519da5c5bba5bf623d4d3f724940e37d9c1a1a2e172c30a415b7d48c0442382f89dfbb3e5403e980ed6bc819e15c804d3b02e3543e54b2617be64d966c1bc56b2c79de2ec126faac96924a61dcbd4b379da3f4b33b91bbb4a8ca6332030fe47d1ba7e2ef534e15bed36ea19cdf28afae2ab96d0a3149875b58b08d0ce913519700f98fde3794b5836a0c5eebb16365a57b7665852c74c62dadc921ba0bcaa1e6574f979c1ec40c5723da607fe31dd92e49d8fa0424f32a2ba2fc90cdb02b0a78be8ede493799dbbf98fd4046d980ec1ee0effb10aeeb5d73fb00b9d9a32c4b13ede3d95e6f8ba59341bc3e6003d8198f09b52878340e0aad1115abfeb92c73c8255c45207c44ab11789c7043a05470898fc03fd445d6d52cc725400c08a3e0336dddc66fec675ff57512d5e66784dd1ef134544a0534fa68a514b59aac8a0a9c8926a5368ca46e2ae206a1095776f05b91ee4324bdffa4a4abb9a27fa236aad3b9225c53fb8b678a79dd24bf43e2085faea787fb32b77e18accd2d7fbfc573ccda28927f543dcd64f99c1b20aa2d6de0ac95e038cb3a905a43aa5d306ebf1dbf419f8841595e5adc5b3841e63c7b26ae37a1077f769c73b0bd7681f6fa1a538193673b5ce3aa2f4010c70b012847f3c64b1672f6802bbc0a5593c0500c3e4d2e2ef2c7d906930ff7adb476b120c20b0e8c259de1e6a45b5a5c1175e3560005e7dcf93d8b5e4011b6a4f4bc5a257561b581726c593ac301c050bf2100bdd856bba552f802d2057ff940b89e1aca0209cf34a4c4a52e26219b249111cb221f0ce92ce4bb019969eb2ee9d1b507fdde4fc73c79aea94e0249ebda2c236524bf1d42f836b3feca7d61e1f1ea2cecc086b446eed45073802b307c424e6928017b12923e6ae5dcfe1b386b8474e70fdc4013afcfbd7dd0d4069f484a4632883b7099995a8ac4a15fc5e727ec8e0b39edb9f7ba623553c3d3141203a7be6a45f620f8425619915bcd8b5b461f5f13611fa9c87e3b00726da56b0587f09ed3f4147adff3bfdbf4a98a35ae2717b37e72b472e3c8a49477f44ecdc1bdc0ce6f817bc2a9625d23c2bba6f2fc7e35bc905801302e4ebfd6fd617e896cb3b335c16c862fed1f435fbca4f216839abe9005fb80aaf737aaa163a7ee2f797bfafa9142471615f7b20e023330d4b9635763f114a43caf159f545cef0fa317fc4494cf6f3471a7742381992bedf914c1874a70c3b99449ac02370bf9d59fa9290732f6358fa0b92124b4433cdbe021a68e1834d1addf9a03c693b5eb11f983dd7f390f9399e6dc9dd574f309754330824eec508afe2ccb2a879dae81a1036a92e18f0dad70988cb1dcbf884e4f3a9f8ce0cb7ead27fe80a7eade14c3124ba407144cded2269276a3a7cd80c6aa72d0ff230cbf7e21825b8b88a013c0e41b8ff2d175e3ec32d13125682dd5d153a8aa1cf502b4abfda271e00465a3385819a3b9f5b7b38a7355b1c2b6fb32e10caec97c7abe37eab479f53835b5563cd0ae21d92d7ee01239cdd8252a727a693afd27263071acb2055224a21d50efe9d88c2f1575cd474715a9238b0fdce0bb302059bcf100eef5e4952629e790dad4efefba6481a94b2e9c104feefffb3addfe5599e9e2a64dc1379970eec6e9597263fd7b4637a0e357f0b9c9c6e781944e54fc286daf35400c72e9ae1816831e420cbf2e0bae715174491c6fc4613b9c78fe03e55c7d0d7cce3324de7a862d33087a80b7e2da4af480293991bf12d0bb13df4ab6287cfc5cba123a414560249e55cd49bb1b455694faeeba1bb5164d427386cb427c81dcafd1a6298c935abbf1a8e665ab51da5f47aaccf6e5c7fa95c070c4b460c2190818d2343cd3ecc32968d84c1235ba1b4eb34c65946cb5132a5ee0727ed6388cacf095841d967db9a267c937fbbae8f49ff32774aeb582c5df0c95dcd40a65d20526497744f8e3610c3e5f459cd30df2b26d7425d2b56989a435cdea728a79f587a4cbd89347fa8f62a2280d78d094e473e894d74d9023fcc91d8a471d60d3aa28b487949fce681b4471a3f16bf86c017ea5747e195ed29ab0af0c3c2080b6561e350b0992c971fb6c347218a82ded8f74420c6fed46032fca6a0ee99b2646fa0f1ba15fc43d5752c7bbcc0c632836628b8ff6a1eed817ba542d1499fecb090796e09327b9e5a353c4cfe477f05e230c8e8a1f6933db6953e30940e386f28b1d590d6fa94c9ecfa60e59d3bc69bb740ff7db3f65aadf7aba44ef124a1c1159c7daccd5cec01ff55627ea42fb0271a7ad70992366b542feaa8e73ba71e117afeaa94af0cae58fb9e7691765af7f8e001d85909a9e2806afdf94ed4b0d0f194ecb299d22ed1a0aea1edfe0057c0b4bb7f7d699de99b0b8716c0accdc25349e00059025eee7fcefae73ecb2f5167633d6a1f16fc930b72ade45fbffa1c44b27ea6df0738a0aa442466374849efcf785025fa14cd21b5880c86c364a33c7b2920f93dc367aa5dfdb0ded893dce0b64e25de6dd670b5a288b24d9e71510c835d0d76ba39801df5007ee9bd8973ad3c709797b590b2aaf977d46def03dcee75c4ddb31885cbe4448ae629686ec83c19a68cdd612d936a93a0cb0a06122391e739138716bb2ee7aa7f0409e5899bc5c053757af5bd5ba90e9763542999b700e3e7ef2845d423f4df2499dba9b375a201d33f20f325416e6b0ad894fa8d9dc190f41a7083558c5d67215c9d8486d3a5d42c9349c7e7502b65ad01e157dbf53845244d864e86b1dfb2f2e7213d4d303d8e01c939ff8b89a9c9c7eb58d80828fc921dc05dbc73be70e299b8ceb845a453e9858199cdb65985cbf0ecbea99426f1186707b89046d256563520f2a0e5e58ab984676827c700dc73602d8d60a1384eb6c388c869a65fbaa2991a6694a8b067bc1a1d2561018161b3e22a19e4ae5d35b547e6098b06414bc790879b3136256761449dc507395cbab8b4cdd2c0194d51fe7", 0x1000}, {&(0x7f0000000280)="f1b338fbe6925e775b2e2f9c46a9f3c3006154dc52beaa94cc6d32bb270f3fed90effd74758dcafa117d12616c628b810e7dac72285b9c80c4dc1ae113a9504b1ca1fd6385805497a97b9645d584519fe475bd6fb6edd00dc85bb7576e9b900e685443c2d6502746ffc0bb55f5f9f248fd0bd2d23e2ed801878f44e4f08f073ceae982802b56661ea5e5310e5722f98b6c62da1014d87b21d9cf685457e623433afd53b366aa2caacb9ebe7b5c3e685122e206d51ed5bf1af6eb848c49984c06a86a5709b6e476c489ff2afb1e42d97ef56495b3d507d6a269ec4f52", 0xdc}], 0x5, &(0x7f0000000400)=[@rights={0x30, 0xffff, 0x1, [r0, r2, r0, r0, r0, r1, r3, 0xffffffffffffffff]}, @rights={0x30, 0xffff, 0x1, [r0, r0, r4, r0, r5, r6, r0]}], 0x60}, 0x9)
kevent(r0, &(0x7f0000000000)=[{}, {{}, 0xfffffffffffffff9}], 0x890, &(0x7f0000000180), 0x80, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00), 0x0, 0x0)
getsockopt$sock_timeval(r6, 0xffff, 0x1005, &(0x7f0000000380), &(0x7f00000004c0)=0x10)
setuid(0x0)
r7 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r7, 0xcd60441a, &(0x7f0000000240)=0x4)
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0, 0xbc9}})
recvmmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0}, 0x10, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000040)={&(0x7f00000000c0)=[{0x22}], 0x1})
open$dir(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
nanosleep(&(0x7f0000000000)={0xfffffffffffffffd}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x25}, {0x48}, {0x16}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
sysctl$vm(&(0x7f0000000000)={0x7, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
utimes(0x0, &(0x7f0000000280)={{0x0, 0xfffffffffffffff7}})
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r1 = dup(r0)
pipe2(&(0x7f0000000000)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
writev(r2, &(0x7f0000000200)=[{&(0x7f00000004c0)="8abf3693cc15f7ab2ba5ba805b4509895ad1b3269cc071b540894f027b364d3eabfced49bd0089d2240e59467fa4fa32353f8582d82c7d6e39885bf558680469b40fb208821c77c054ec792acf01e5aab646020ffb8faf8a92d5f5f8a806c036eaa685a56f5b06ba66278745dc199d8735ab7598b239df909de4dbc214e115415a0ee9bfa82eecf850e404f2b9eff9738b900ce32c569dcbf53534a28152143ef08a79dd7de715d3d9136b5871639be0083f512b429c9627e3bd6d71e3274346beb5ffdeed1bb17396a79ca1c9523d770b7c92bf4420dfbc9bbf9fff2268abc8f6c7e5d645a686df4a81f8c18e6460a54316a1dd2e51fb99158301884265097321a1c91feb96d3dd178f8c6a43d4c4d2d5990dd29bca34bbb886ea37311ec20fdc56bef6875e2f602623712828f7be2b3e9b6785cc038d1c6fd6baf0535ab9c96fd0d7692a78c1b0ea086d9715c4c7518280a840db9dc13269ee13ea75a16490d3c8fa42bea488310368b453cd747451fe959af064b431d51033eb2b8e4898cd7afa49b7823b26d24fe2bc40138f3ea7b3b988d96099f875412798ce656857a665a9a729bc41cf70da58cbc076c349b282ace8d9f57217eec7549466b0b570d5b722de7c6315a5d8da5e2e7a431104650c7fc8de85c9957832a3c00b7f783f6a1e3179a361c6d827a9438672b3b2c0a03b4df226ef3f982725fdc216137bf8944bdecc6c286a2222b9555e8adc60692892a6c7a2fb8b5424838ae9545aace21e21e0cad9cd25bee2c53f21b885277777340e38f14553a63b0e988eceb12b0ce60920c8f531acfd0bd25f718ff8da2989cbdcfe36ac68f72b47c45f9dd872a45211b9137455d348ecd7246f6f2f7e2abf6186ee9703305ac9ba536a2f891bb21f4eecdf57f0ee8656e4697a6bd0897acd0cff7d21e300aa5f5aaad814a918f9f2b665c20bf5d75a4c61bb5167d04a120eec67b9a2b86bcb6146cf5074c636f9335908515834fb2d7e164e99707f4d3ca6812dbb90b543a643bcb620435c58525f73f63e700c71c2e65c379679fa79ebcbfa2b15a5f9fe91ce5ddde608c4c8f0b12bb42a700d0512713b9155347aae5b00d49c0a0f08be9205e026c642b120a628de9185b5beacfeeacc202cb2a69724ee9bb3a00efaace15aa2643e432131f06d3f19e47d97cb33f1bd065a1c3211d150fdabfdf7472c45128fd4e8b3f4024d4b2b94fbe9cc7f450ebbd32ea3729d4479ec1f8baf3e898e0172c25ba08df28e07151f7d76f6339180ec2c7b92264846f1bab571ab5b7e0bf62d9e59cec68a52ea9ee368a799a5b19467c742edd8716a7c725ea59da8b609c43b75a6c60834dbe5d7e7aa7746419ccfc9fa231ee83b88f8c9f3f9b60c66d13db2027a28b8181ab6a0cae43ecbdc15f62679f634d39a000518fbcf9e0a05e6040df505fce2521f5175a155d2e7c96f6b27289e5ccc0e7b735d138e20e325f3d9c6cff705e9ea831323b2196d25ae976d630ea10a53b685b3a7a66f0a7e5324318e8c2cab2ae6014320145602d5d2d48a9d29d7f2f6e1d12cba3140d4bd30f359ee801d6d972ba51c2fca182562694ffe68ed5ec5145e2bdff1f237f67521a3c899a428885055bc655ab6f858d4fd6c5340947ae1c80469c2154d10c2c99d6e9f197bfa2cbe332f501283aba2585889de559e2e51a8ef131ec69c1a140f90d1f958c7651a5f7cc37b3586d3fa71f1a3e1e8aae4d54533f33c6cc57fada8a00a57233429ad399b5ad6e4a84be9a493bcb6a62d6d4bb233e627b61debc83f753b70a1fe8da04ccc8cb76ada06c576645148bf5e5dd647f4610bb92e440e3db32ab63b779c6592c542d57cae6bc21c10a409285f3f765226900540de42cfb1ccf254238451fd63a615f9a9e6177c7c413a81692506262416b2fc6e35ba500166a0316883319bbbe793fce1e5a24ed9cfa22fea43b4981bf32b79b939da21e4f56e2f252e300fe5bb87097177a7f5a2740c6fe9c557cf2075435719fac5f205b5570fb87ed89b13b972a8dcf9cba88efd8e2ba573982c3e8f832dfb0f7bf2cf9e6b19b23e522254f63a3fb2c286e794549f8ccd876ad8b7a4f6af9ac1351284fff216a3444b71c8d33a095ce93c75e03921f7839fc750f67f8d158c5654a768b089ea5500fb33d5041e840b1ec4856df00aa337e3eb6611b649ff161078ae7c7f298dbd6525660a22da38f363a68a4fda83f4b05f9685a01bbe0bc875657729ed4d560ca287f88f6ddd30cf241d0eabc6c218cd1ab8249763ab2ef578852182634547766f21e8794367279ec407a11a70d2c75a07e0fdaa365af0106f058573cda968fee1499e829f064f2cd2a7ffcf7b62d23ec63984c66b6e2eb0e1822649c7a69afc157181b621b8636b70fd9ac4d940f4388f71ce7de4e52cda1ccd88d981567dabc3e02f58e3acb2f71335de5b1e9dd307100366817fd52c31e9b8fb08381d3830f610bb2d730a5066931a090ff15332aa1d261376329650576cac06134ed83a727ecc858325cf9f9837f8adfff18edf888352a423e822e5202533d4153ee9636b9dfe4be3d4327e85e90ae99f1f601137a23b6b99fe5e46fe72114e2b5a32a728da0ccf20faf1a2b4145f090ae5a4f48379ed392c7594934ebbec5c84e63663165aba05f1241a1609fb3efb86d3ab05a6d34568dddb4f2156e2d2455544270e3ff66c72c3ec0bd79f7b32219617d7d1c83e589bc413a4462e6fa158cbc1cf865790c39c2adc641815c75dfe6783480fc0033c9697681e279825c1fe01650ba9786a2b2cb510e1c6127d6aa7587bb140c15f5bfb23835d65ebd13419d3ca4ec2023720e821088847a3d9df5b6f15a707fd87c5303070ca234b2c861fbf67000cb182b939cce8db734923153a03a27a7ead0aab0a150753cbae1329c84b6b0b401ddba6a69a5d27d5a7a60cc729b5cab7ead676b5efd984cd40bbfb8fe9923504568214043ed408bb303ae8f1874d51b646f02a034fad107df809bbc7b994d0f9f47fadc33e4af99fb112405a4d8f8a6e4702c1cbbc3a24312682aaba7bb060696bcb3a508bb7144284fc6d2120d939d76ed67b0d8d81f8d44d05f35b0fe36ee3744f37e708a244736b4b7d4d1e8eda9ca00fb55b00d60a59919ae9e062adcbbf7fe1ecb6d9ff90bceba0c7db6fd42d65b15867d5621001f2361702de7402e4af210afede9b29d2893893d10495369c8d883bbbe6549037382825ff282216afa41c6e097c52e8d3369348c74af9005c399b2250361eb215b59017abeebc591f25418e3a03eec2b94bdee4f184e7e83e125120422725708a59bf5fa215ff47edfbadfa91a329fa29ea615ac40b803dc4e0227c968a36d5245f27dd41fb406088c3341275ae291f4ca8b4f1b6bbb94873597c4623d1ab61301ac5e5e7569837f2f50c454da4a0eeb9aa1a316b4ad9dcc1980ba737d34eeafe12b5954b997b528061b27ba59c087f24ade8ac0efe41b3d8f25d6289645c9d35922e57a8023455f1f1f75b15a6801bed1455cf9670b30eb37193c44ac9912920e69fbde61d2096e36d5c1dcb2ac26ee8a16d683898f64bd4acee79a5c3f13f7001f8daeb9dc665bcf31d4cd8e6c2b8a84c00862467ac6b2570a75804a1e570106c5c5446c8b005aad4a3c74e064700792732fbbe046a6d99e0859d8a5d874b27a51d13fec54e35ab414971096430aeb72d339ebb122aa74eeb83732bc113f41d7516e77a847281497562dbb5627311486e823d17449dbade36cedf1d8bb57cebfbbcdeb90226c488913422edbe2fd337d7c1d2ac100b63eeddcb82fb3d7e24bcf96515c055dc0fe0181b20ab75a857a52f4d9d87d2e6cc08c651dd7ef54918fc1cb6a473c89311accd710b25c8af563de7a56fb1f692bec339f3b827d1e1a44c14960089112014771e3dcd14d581a56ca5e20cdf06f6596e5143d82a59acbb4235a661c4487c652f48fc2660ae6536b3db4c76270a2ba0025d03d2a8d8bccd1239e7e9668615772aa452940edea1752dfc0c2330b69ddd6a5b2d0a9db11649f2af3f35d933064e1dbaea847b258759d56039ee1893fbebc3337d05575df468e7e05333c7de28da4c6a5a8def7daae72ab1bddd327484cebae598b312d41588c6d8862e2bd39dbde020c408e20b4d6fc7cadfae62a0eb65d6dab2764a122fb92df63763e8ac57f85f9cd85d252438a80f1d532a214192a1ef0294b1aae14bcdb6cd0c8e99c7336f903cd9c78ba71596c0b10e6d602c11076a291495dcd12adf7002cc8cc54a2d85b4d07e415dc7c09b4f6645014b63ede2ad83c574a163bf7fb8213b2ea65b5977bb20cce87432d8d0682be1b56006fb55578cab868abd4c9ac8e233f5795b5f1d92f9b8187928f4aae168087d5a1a94c8e8bd699c5e9f08c15cccb9f341504c804bc779f8355f20feea601878ced07fdafa0c3490476d65f75acd3b248bdef8b7dee18056eb1a169db5e766779a73494e4af4c25f2ed4eeba53860604a6e2792ea25eace7b959d48eb0f9d445329641379e65bd4e6669577515e5dddd73b1d696a71f4bbbda7bc1e7e66095f2761253a5e4e30f031e3919551d2504ceae93c2db1752dd4a141ef2933c380a23d2378212a55f1cf653412300917c8fc76f87f19a989f4c01530ba561b631d5789cd0ad1c628052797fdbec7d94b47c06572873cefac8a370ea49ba65ae05bfc2018d27613e0dfd4adebd4bd5ed1fd0557ef318a822c897d14e391389826e51e5eb0b1a235686b2dfea5bcd40be7768928e7a2f0ce7268a7e72df4575673fd999f937a9bbd28e9fa452e21a633491c1dcc04a35ae23f632943faca318d3a744632eb67ffa15e2b3282fa8e828d79cd1cdfeb0ea73f39c88d13ed04a1bb660aa37ba44c0e489d0694999d0a2c63def2fae7839e45ef289e93874edc32cd64ed3f5716f9c99ca2425a319cfe3a8c739935cd09eeda0103e70fe81c9204407da03364233617dc1af05f01e29a6b96cc26636f770f562326968fe02a1b1ebca712bf2685aedc7744a15971483c4d357ed95bd25f4c90cb83bdd789eb783fb9ae8de9d48e9c962d08f794e5235177132bb93588934fcb89e17d489f1f3540b87fc8c31828ffa83d1365d74a27918848c4efa162f1990cef3d4c710a7abf61ca70479b4f57c5ddc90ac9a2be2325ffd96a26d7ff452ae64db9e7713d72bb3838f154621e3bc9f8c3bcf7cbc518b476cfb069cbdbe8ed311f4cdfb2d512352c990e01a2ce79378d3315d39e6714691a6e87053eb8707c50bab03c99ea8b9cf8d265667fd7ef814de6a261a31a6d9e51efa11735fd469277b7a4a7b10bd2f6c98902eb12ecf19ec3894d29d9fd36f8474cb0edfaa19bb6204db6d96be49f2b756b3400899cc39a893c8f0b7ce685916c7d0a74e9ea33ff194e7bf06f012d56aab34ecdf0bd07999adefc73efcb2b70da0219fb78d310ac5e0b4aed98e6c44c953a93cde66900c46a117ba0e28ca15f3e58817c4fcb8732a92221e38b18048daceb661c797e7dd36f24f464e47eb93288bd0240bd0dc694a57a5331d85759d970e0356646e0c1cb4ed71d8e955d12200a1a25f6bc514fb64c255d2a0cbf56515c9da4ceb88c8d9b22a97a7ade5891e641141f972a8a616a05ffabcdbacc5b6bcf9e5c2094c2331c297994ba71b81c31c031ee7322748b4a738d49dba59dd49f2b8c63e74a21653643c5d2e73aab9a658433562fbb0deba8de0d51ca4dc6f1d8b3ad475459aeed9a0ebd444b6308fecb8f546e2ef81d3b0989a1cb1292cbc2b86ed16a4", 0x1000}, {&(0x7f0000000100)="3fd8bbb90c621fbff57e32e5c7", 0xfffffe85}, {&(0x7f0000000140)="bd19a9ec60c6c0c5d2fb157b25d6f9c092878690ba9ef8b713fe35161072d6d7856e780a80b910c97c01be56b3092c57a53b9edd38b54c34441b32bc4b1fec8bb0eaef2e2dbeedff6f8ec374afc253baa5cb1be61f826fba8c0ec9c997afeea208d452044661db49252d889d2f90ed550f7e36f9b34743440de9a218d4631ba158a8a5ff5fb75590803a0c15bbad682503f3300c95e8bedf24b4529b5241fe9fd2969e35105e7e463447", 0xc7}], 0x3)
ioctl$FIOASYNC(0xffffffffffffffff, 0xcd60441a, &(0x7f0000000240))
writev(0xffffffffffffffff, 0x0, 0x0)
write(r2, &(0x7f00000000c0)="95", 0xfffffe76)
dup2(r1, r2)
execve(0x0, 0x0, 0x0)
openat(0xffffffffffffffff, 0x0, 0x0, 0x0)
ioctl$WSKBDIO_GETENCODING(0xffffffffffffffff, 0x4004570f, 0x0)
setrlimit(0x8, &(0x7f0000001280)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x0, 0xf4b, 0x1, 0x1fc80d88, "00000400000d00e300000937ebffff0700008000"})
writev(r0, &(0x7f0000000140)=[{&(0x7f00000001c0)="cb3ddde35854e954bda4e38f2979374e932e2faba1684140f12eccee51e4cc2b7bccbad13010cd92b3a2e1458707892d9a78a715426cbeb3d50b1e9640277cb6a6e60ad994c817789468287aeaeb89dd4540f3ffc76516fa3f62b6210895d24c36b31dc124a06d02b5fcb884101b7905f036b2a2d5dc32d3365642416857ba1f8b4edfa0591046cbe548049073160000f5e30ab9fb6eee2d7c0446c337e1bf8148e60c1a2e8f180147ec2dcbfc6c6b89bf0d473a667384e0c7f2f248a3b4a36d49176b6ae95be17a2e1f5e68938e56f6d2b9b27031691c05feaef243225240601ea59fc135678272614f15fd3f3e1518c5e5bc7cba838c6e09", 0xf9}], 0x1)
setitimer(0x0, &(0x7f0000000040)={{0x0, 0xfffffffffffffffc}, {0xffffffff}}, 0x0)
mknod(&(0x7f0000000100)='./file0\x00', 0x1000, 0x0)
r0 = open$dir(&(0x7f00000000c0)='./file0\x00', 0x2, 0x0)
kevent(0xffffffffffffffff, &(0x7f00000003c0)=[{}, {}, {{r0}, 0xffffffffffffffff, 0xb3}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000400), 0x7, 0x0, 0x0, 0x0)
kevent(r1, 0x0, 0x0, &(0x7f00000001c0), 0x7, 0x0)
r2 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r2, &(0x7f0000000640)=[{&(0x7f0000000140)='#', 0x1}], 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x1000007, 0x4b2, "0d43d3d3d33f0800010000000000000d00"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)='\r', 0x1}], 0x1)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000000)=0x2f)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
writev(r1, &(0x7f0000000380)=[{0x0}], 0x1)
writev(0xffffffffffffffff, &(0x7f00000003c0)=[{&(0x7f0000000080)="5d526deb54a107166258db46566a6ccf9130ce4460f63c3cacca13e8fed3d5c5484f8b84fd67ecfbbf5b24fb53af33f8cb344e6f980361b7ac234c47c805589cc08a706a4669d0a4dfe62ef51ad60690209ccc46e281557b1658371f096e4322a3e9e5e7ab12405d17f0266800bb443fbda1b7aa575c8de05bb51b2413", 0x7d}], 0x1)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x123)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0xe5)
utimes(&(0x7f0000000040)='.\x00', &(0x7f0000000080))
mknod(&(0x7f0000000980)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x16}, {r1, 0x2f29a8c09c465320}], 0x2, 0x0)
sysctl$kern(&(0x7f0000000200)={0x1, 0x3a}, 0x2, &(0x7f0000000240)="2fce5c27", &(0x7f0000000280)=0x4, 0x0, 0x0)
setuid(0xffffffffffffffff)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000180), 0x14)
sysctl$vm(&(0x7f0000000040)={0x7, 0x2}, 0x2, 0x0, 0x0, 0x0, 0x0)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
minherit(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
msync(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x6)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fb182e0b3da63d9fb9"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x9, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffdf7)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x5}, {0x2d}, {0x6, 0x0, 0x0, 0xc000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="de2bcf583dccf1e393eecf1958d6", 0xe, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x34}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc028445a, &(0x7f0000000140)={0x4})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="6b18ec"], 0x1c, 0x0}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = dup2(r0, r0)
sendmsg$unix(r1, &(0x7f0000001700)={0x0, 0x0, 0x0, 0x3c}, 0x0)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x4)
openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x6, 0x8, &(0x7f0000000000), 0x0)
openat(0xffffffffffffffff, &(0x7f00000001c0)='./file0\x00', 0x200, 0x12e)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000200), 0x80, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000280)=0x4)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r1 = openat(0xffffffffffffffff, &(0x7f000060cff8)='/', 0x0, 0x0)
getdents(r1, &(0x7f00000020c0)=""/4096, 0x21f)
openat$pf(0xffffffffffffff9c, &(0x7f0000000700), 0x0, 0x0)
pwritev(r1, &(0x7f0000000740)=[{&(0x7f0000000180)="20774af690872d4d4f34347ab7b9f7250d07c955d5d718a7cf2b0144283b54582e7dde64a2e91bea1d4b196a28c8b711c57c6a2af9fab16e9e8a8de7fae391738d3ec8231a2a1b065dca8be899aa31d3cfed89c25b650f54054863da6bdb737f65122e0dcf6109d3884e677edefe6171849e87", 0x73}, {&(0x7f0000000300)="a6108e9e99d35ce85eed51b8d37269c587efe74b06f80dbe7f45d4dccf525a43ef59d2bfcf284cb849b09be8c90a291d433064a6fddb49252c54c8d0e67987ffbbb66dcc7679da67184fda7544067bff3a67a5f04719baf1e81874e3fad45f79c034ebb4770be239fd23765bdc0fb503944759d4648276a41753051e0cb698f7aa1436100dabd7dcc8ec4685c93226d2b16e722491ab538da26fdd3f78fa725fa15d9de645d583f0de2ed2d6f552ddd5b6651aafd61e4a6f186cbf6e4309e330e36036cd7db4ac7514dabb8e53e7b2f455778b56ac", 0xd5}, {&(0x7f0000000400)="a60d56ff39aeaa04c22db3d7468316a198ae3ceb97721145a5f59343e5aa739dddb3247626c66f866d2d5bf01c30362e7a87a41ecfe0585f8d4041258c0ee6bec80d00e779584c5d55bc3ce098234d0fa65a90e92f68b8393b8dd5b53fe9e344ed219b90b321d4d0c16def225eb6ddf10ef99e2715aeab96931acfe69018c791f1d3e24f3279315ad7d46d2209052df1d3b6d6bcbda21fba231c0fd92c4da47e1c9ea7288067eeabc1d3dc082da5c28fc14b2cc653d7371d02087a8e43b0052cc13b891665c0d9674bbdee", 0xcb}, {&(0x7f0000000040)="dc32edea977df441287042521cc17ebebcf4df30f357", 0x16}, {&(0x7f0000000240)="7b9eab4793d051e550cf3865278d4c627920746060d8e5da3bbc84e8e8fa", 0x1e}, {&(0x7f0000000500)="e1753425c48611173ec685a0f5c935d8511162e851fe3c0ec7a6b506416d492aec752195870ee5c8f60b3002dac9af8a718a78745a44f8ee403f030e49c8da09cd2bcbcf0d86f27832554cc90b94826863b386c8c931ce7ee375471aac8acec78e40d63890aac557c45333c99e8282dda34abb56a13cb43e63929825652e8452a622b969b9ed01267f438cb6c19c3eb849be1f731a16c3ad36a893a60f17e60f14f6dcdf027806ed2fb8867033aa8432db267ad9e376caefe48a1c123fdbd6f3d470d4f0071e181d", 0xc8}, {&(0x7f0000000600)="cd1de1c013cef1ad365a0507c54a62ce915254bae9a52e0794cd61f8b917568bd067d6f1b2c2f880ac1646f5fda4d55671de26430c8e57846e0cafc1a2280209b21a2070e3365399c6d01713b4861b05fe9b6829d635eff7a7bdd4ef1b3d997dab581f42ff8f0624f8b3d17efe35b57a730cbef993eba758f62906b90cc8fbcd56f6fbbc30815981775da352988589558578b5984496178acd4be0d3672c1f61df8cbdd074bd2283bce20348a20be8c9", 0xb0}, {&(0x7f00000006c0)="09ef0afe3ad886d76b2fc608b168", 0xe}, {&(0x7f0000000700)}], 0x9, 0x100000000)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x1, &(0x7f0000000080)=[{0x200, 0x0, 0xff, 0x87}]})
openat(0xffffffffffffffff, &(0x7f000060cff8)='/', 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f00000002c0)='./file0\x00', 0x0, 0x0)
faccessat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
setuid(r2)
readv(r0, &(0x7f00000016c0)=[{&(0x7f0000000180)=""/19, 0x13}], 0x1)
sysctl$hw(&(0x7f0000000000)={0x4, 0x1f}, 0x4, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x0, 0xfffffffd, 0x979, 0x0, "0804000103000008000100008d1b38b85200"})
writev(0xffffffffffffffff, 0x0, 0x0)
write(r1, &(0x7f0000000ec0)="a7917d01866f6348949969460d7a68d55b45945c8a7b2b03952672e15419558776ffe617f42fd48817be796fc431f0bb0aca18b348f49fca2aa10810f39a1a052fd33c1ab3eaba82465a2c5a4b0804", 0x4f)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000000)=0x2f)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8000000000003})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x75e, 0x0, 0x6a5, 0x1fc80d8b, "0400e5ecc7db00"})
writev(r0, &(0x7f0000001440)=[{&(0x7f00000012c0)="f7d98f052351db059ed5bed53088f23e16ec2b87f7bdf027d359cee7056ad3a0013b5653910e1e2d321b8a6692705ceaf6ba5c8d32ec0fce4de867be1584e22bd73d69d00c0500000000000000699f25f96ae29ffd5c14279599987c5adb2350ea1c62ee49d6a2e37a82d56fb68d35dfc685fc42613f9a34822c9d0d2de0c0497515000000000b48ca0da5b823ff6587c4ae6479ea910b7280c277fe577ff5f47325ea8c36ee46c4e841d2078bbd6478b3aec6d2d046116cfbb514f129c609c7", 0xc0}], 0x1)
r0 = socket(0x18, 0x1, 0x0)
listen(r0, 0x0)
setsockopt(r0, 0x29, 0x1b, &(0x7f00000000c0), 0x4)
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, '\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00B\x00'})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3f, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecf860080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504441, &(0x7f0000000240))
seteuid(0xffffffffffffffff)
setrlimit(0x8, &(0x7f0000000080)={0x0, 0x403})
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd6000000000143afe000000000000000000010000000017000003"])
sysctl$net_inet6_ip6(&(0x7f0000000040)={0x4, 0x18, 0x29, 0x35}, 0x4, &(0x7f00000005c0)="4f009952d47c707ffa7d24e95b32c887", &(0x7f00000001c0)=0x1000f, 0x0, 0xfffffffffffffca6)
r0 = msgget$private(0x0, 0x2)
msgsnd(r0, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2c9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r0, &(0x7f0000000a00)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da6461341bbefbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6ac1571170e268ce1d0b4bbd8bf45c5fd340a61305979b0bf685e45f57392649d8248976549ce08056e03c959080cbf5e012d6635b3b58174bd552e9c513f2acc71bb2c9788a895fc07478c415c6aa3db3cd6b47f2e874c2c9d63886179802e5606fb276950cca74cf527bf968ceba0e8125af4bd5000000000000000000000000000000c6bd6dd52135696ea9f082ad0938b93df3eca8aad08910b7e8ee4403738cb1dbb0c104f09ad91582087e0eef0e43c90e92357a3ef5407833d2a6ec79c459488669a3cd6a31aa014aac155a5bd37af6377d1ad2cac8b2e1796506fade88f319a97d9a40c749f3a53994efba60bcc277b0a17c6e5bee5a8698f73b55972dc1c52a0b69bbbf91228a81ebf27c3732ced1949ff053da397baae03e894a58defd824bac1ac4ea092b2d476d374f122423d47361c20a5575804aeaaf479fe73daebf5309cb45fa4eb8bfe4165315a5f49b632e69ac567667569d953be148"], 0x6d, 0x0)
msgrcv(r0, &(0x7f0000000580)={0x0, ""/18}, 0x1a, 0x1, 0x3000)
msgsnd(r0, &(0x7f00000007c0)=ANY=[@ANYBLOB="0300000000040000db4436958b193c67b6ce0c093bb0bbf3b3245230033f58052ebd418aa6e7161916fd47a14a3a74259ca57335615dd09efc3ddf520906bf778f89cf9aa9da8fbd888acf1d82015453ebaf5328755e3b05d90ba5ba0c00faff96c620f0866300000000"], 0xb, 0x800)
msgsnd(r0, &(0x7f0000000d00)=ANY=[@ANYBLOB="01000000000000004b3e4adaed60f9694f579a48a898b3e0a244a62aff3ff370299a40dd008d33ef2b68889815e00dd74ea11849789c849d6b72cbe90087da24c19071a704c5a74c3e906de00234767800b15445d036d92d6665637f4fd5d4f6eb7768ecc61e4001f2cea489a1672715d300f44b841a450c57ae4e7a21e973bba27d079143b4db6b7183e6409ec35b565a367c99d8baea7ba434dafcc2bdc7724c967c900f8a55005176c91fb4acd8b9e6dd99f981d8c3e2a76593fa78ab7b0925ebafc865fb4bc1fb48bc7ea6f403429197f6c8bd75610c3c957eec275eadd20c3f10542f0cc66afd043797f47a3860b99a1e6c5b630aec30a69cb84b2d70b23b75130a20722d15baf72b35e9c8bea0a92854bf1f261b8a06d1c63c762d660a5045675a052b0463175bd6533f0f7a7a8acf838670077986be8bd3cb42af4e25c6df538fa24030c50fb6762dfd0e69f0954b9dab550589f700c3294135518a84e1c51291364ada73255ec4915820cf6eeeb8c41f921cf6012d1e1bcd745b8cc7123d1cee4d8a1eff5922ecc1ec80daed1f4e893d5cdb0bebce665b0f0d0f7c137d6d62b8856c8ba951ab7302c7daa0d05c0a912707f3e4d1ba048be1bdd3cec3be1769f4339853ca31f761dc5fb15fff301388b53b4b14cdb22baf6a61"], 0x143, 0x0)
msgctl$IPC_STAT(r0, 0x2, &(0x7f0000000680)=""/9)
msgctl$IPC_RMID(r0, 0x0)
msgsnd(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="010000000000000041090384587c9d254f6c5592c8096d8744f9d60c49ecc20561ebb1339471f988560f29ddeeab75ac49d0d4c5a9669583bc072a62972d6fb516b20b8c904f4531746a55d37c5594651bb38bbf20c0db1d7485474daae5979836fc88f13b25bb2052de333689109c449365a777142bc9f580724dee0a7005c8660d96b3a3b8df4f30ba819c33ba92e352a346655e0859a7c935552ae4eda3cc91d0007f945281718e314f74e798acb9468d44bcfe6dbe9b4ad86c8be94f14363d7e47c2cd2c7548807130eec9da908f648725abd9a26d53e2f6710b7e0b7a6b8af3e86891b7f2b718af78c5ef0819b86c4449327b3e3c5d90c0efce2bcae5e8fcd2041cc365de2988938333b6af09509c"], 0x47, 0x0)
r1 = msgget$private(0x0, 0x2)
msgsnd(r1, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2c9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r1, &(0x7f0000000340)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da64540237056eb6a5f04f17cc993f074c6661341bbefbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e45e348cb20ebf83ab0ce789d687b03d617130b6ac1571170e268ce2f0b4bbd8bf45c5fd340a61305979b0bf685e45f57392649d8248976549ce08056e03c959080cbf5e012d6635b3b58174bd552e9c513f2acc71bb2c9788a895fc07478c2c9d63886179802e5606fb276950cca74cf527bf9cd03ba0e8125af4bd50000000000000000000000000000007fbd6d7a00000000000000ad0938b93df3eca8aad08910b7e8ee4403738cb1dbb0c104f09ad91582087e0eef0e43cb0e92357a3ef5407833d2a6ec79c459488669a3cd6a31aa014aac155a5bd37af6377d1ad2cac8b2e1796506fade88f319a97d9a40c749f3a53994efba60bcc277b0a17c6e5bee5a8698f73b55972dc1c52a0b69bbbf91228a81ebf27c3732ced1949ff053da397baae03e894a58defd824bac1ac4ea092b2d476d374f122423d47361c20a5575804aeaaf479fe73daebf5309cb45fa4eb8bfe4165315a5f49b632e69ac567667569d953be148"], 0x6d, 0x0)
msgrcv(r1, &(0x7f0000000580)={0x0, ""/18}, 0x1a, 0x1, 0x3000)
msgget$private(0x0, 0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0xa, &(0x7f0000000040), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
r2 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x10, 0x0)
pwritev(r2, &(0x7f0000000080)=[{&(0x7f0000000100)="74745082a6621f7a54d463239daea0ee0be328dc1800a38256b22c4ea6430ea5f3c781206a217d63e30cb59169e810d85c1bf5ff3fd85a6b1eb1c5dcaba6b63c14ea0505c23eefd5fcb6affb3a4168d794105a2d5f5821fb03fc035246ee7e7f08c344158743572aa8e4540b3186dec0e01d2f951d7903c17bc492ab765dbe4378bab4de638661bdfc2d6a46e37ed523dd0335853596cd770435e4d2d82c", 0x9e}, {&(0x7f0000000040)="1cf1c094764496f3e4f01385a1aab3acd845d351ff419b5973a5b76a38d7a5ccf75d31d2700de93a214e978f9e91", 0x2e}, {&(0x7f0000000340)="7f927f85b18550d80e52b71c13dd32deee9f69188efc3c9898f228b5a1ff2cf8b602d92ecc0748a1032ffce648777d10ac1232c3aa53359bcc5f4fa3ade9c40ba70253c65188a35354c84d35fd6a8df18f7fe33f8a8669e7de07779e0b6170aaff20e1699b2251336d64fcf95ca64573151a6823233b58f3c5b80780371f2a60fdc2f472573af905c1c5ee4817279cecbd32a3176660b76a73b3d9e67bfb8e80411c19953d5fc9509a976eb8b014366f451d75954196c4c9c272d2034348cac38b6d36f08230d2fe204c4761a5fd17e0e969145f4c9a65a58f5a4074c74f42bb40e990144c3e72e3819dff5b1d2b256f6873f1a95e3283d9162cbaff0ca78117b63700946e93b7c563d98d26543e0d37b148965f5c786717c952ebfc4c01e45fae2f42bab2d3c20b965ce1ebd8b601f76adc444a9cd98de0eaa30d2ab3871196dda144fd884accff3ccac65cc4ada9e0998e7fea0de0b670cf4d07d65348fcc5a428f524d36a5dbcd4882e84130ebd1fdc191cefc472c8b1aa0dc2893c36959652ef80accd2ca63fde426b721eac937be2a32413edab0f5d285e738f36230ac5c8d633479abc12e3b81af7e88c6ec20fa0ac3e96f9206c7fe104807e900d2f2c6f85f6cf0160488aa56e1888405832d0c94dac25c67ee782f95b8db038390e710e48c2355b509769f7d648bef3d01386d1c72ce83ef523c7eeb224dd1bfe49550586ecf46ad9f615848c897942846235f51abd62516ac1210c4657634a314243bde6df63d760ae4ceaa17f9c137a1ed62042a58750a6b8b21e1bfd8b9e858e46cbbcdb86e6f64d329855dce4d07f7f6ec7af1252e7c36c1a96cb6958e5b8d129721813306f5db9c5c3876a1ab38526ea640d4183211002f5aefd3ed0843f077321b548e8f70d14cbe0a6f30b00b94741a984b32bff02c8739e2ee6185be3b73b7e3669769c91ff6325769471fac4149a44ac16bdce03a4f1eb2fb90249fc19c2308115dab84a53d9e3defedd25d6814546d7a8190b59540d43adee4fa76bb82f92bdc00360bf83a847e8a4a5d7d526cba4b160289512ba0b1dd1bdc4ba825054a3ea5c69470845be5e5c6bd5f1516d8e737a1c2e8b2fa3b987578fcdc10d48eea22c0b40922468e16e53a8fc6161112badd1452cfe3d053da62a1547ec56355ebfb057f2c5c28939501e761bb7c4f5f7babd653ab869d0635e5254e88c519635a6c74480e69f89cff324f2bd97f3f0827109776fa9d7758218af83b61c98e5d51aa7c7f357e41b985190e81786853772506600e5c284eeff6eeed1a792124f24e3ed813c75cb02043e658aabc605c351c9fd2077565201cfb12d4ce8b9f789b8c33ad1e9282bc0797919b4cc11071bf5db4c71da4d81fcfee6a6673a13ba6dd334c7847546b1993350ae1f0dfb3be69bd3b7d0adaf6f1dac07bb77dbc8e29aefa2d7f9fbe5a8f6732514b949337c92f001e280558ab48727c2dfdbfce3217ddd13694a1e8fd3b948279fe0cf26f4de113c4e52d1be79a6623dfb04e0366fd320746f06a17c95a6fd13eee90917ffdd0625ec9896e7e98977ec75fc91190119fb7ea6b1cf14af7e55efe8cc976def878c50642995eba7c195164a883defd936ad69516c4fd057235f23974b97303a6e0518bf2f189a0f973ab34f1c879c15cc022e2cece6db0fb686544ad2a226118117d4f64069ffe2e598d0a7be7064972e3669dd2e90d40f18c4561b48ff6ee0e2eec604bdf3f92a206f76b8ea390b187a3dab243568e7614ddda5dab168f35bfc1658e8f283780e25386136c77bf0b37764d13d2d6c3eafd343229c2c729fdd7ddf9bb895c7fe8a99e1a3de1703454c3fc27216bc39a0de8e6b06c0bc00d26466c4b5bc835578e3e0fdf615ef883ade2470cffc52b2904222ee4be2a0dcb55c3b77d1d4989cb337a0c4ced0b3a38b675480f18255e09f2da7e8d4574aede3878f8d759bd53dffb86805ac7fb68ea5b5dd29b95ba4bcba54892fe9be47e99f0004e47eaf7d6b8cd695c98a39de37ee66982eb670b2f5db2349e8582a6ad44b920d86bf9366a450ab7315d9a9335ff4bd4727df1876260888b1d9bdc3655563541a9f40e14dd7674a913074dad1649853b5fc968ef815d715c372ff5fcf71be428acaa3ac09f7edd660bccb7781447b437fe004da24674f7a13639eece28d80b4054161f2b484bf407e84706f84e669289540196da2bdf2bf0313fb57aa290a60421a0fe3f109bb9cdc74126dfacfbb3f389b8b3932c907111631ab1622ebc4217af7289faa0222aeef29a9c3bd69be504bdd4ecacf7e44a9aac6d98819e6201d58556272d8e09527546ac7af542e0f6165a3f6bfb96410b9008fa58d4fdc8283c719780b2c633660b6c72bed9e7d58722079548550f90133d4a9b87342f636cb6dbd1a4e0799de659b355d54bc84d5658d8c2d71637b54ce0163fea540a04e7b4a988b09ae62ceca1dc7234501542e9a0283f1ad311e2f8ba9b5c7bf71029f0658cebcc5a692256f4f77c11ce66c868b45b8f78f93e406a45db5069e19a856fc5b54d6444323493513e40b5c5c4624aeeb6e1f8f828993d705d59fb96ef2c64b175c44d6609be8132e7acce591b641859ded3f444cd86b0879824fc4d6ee2ab85590837dbdaad72c4a78677291139f3c854b34550ce9b1f5f111c5d5355d463b2fe796e492ce08de8a4da4f08b5f335fe0ca6c1fac78bf23935a2a84b0269275251a6079153248f1b6f7a495168abba6f49616cbbebda6584e02a9cbc954da94e9f24dec210330a1f8a27a6c17cd5d8b4b5e40702fa6c7854d6f8da8429f0b65f2022702ae6333bcd4aee2b534e5562ba503f430c568b657f906866b2ea055337e81336e18bdf4c55434efa6f65f6937340f3e6eba82b56e57a8a12cf032fe9411054c5e34e3907ab9365e3afd0e659d233a6c4d6b4cacfb0ed0a771b1c09b3e4e2b506d67ead3250779af711e06457c47adea3cec38b1ac862ed99b564ff9cc50419a483c39afccc445f9d0085132aa98596aa8e50c25eb8fe8644a0011c7a47de73de8d9cc292f7961814abc9e86dc7c485438a2848e0bf185a54f1996c0757aefae5d2967159c98a5c825536c66cb10b05492421c435e22620dc781daab359d1c36c0cde06c9581f8c5673588a4257238bd1d6b66addb67d12a945237ebb6e0aed01850c4ce1bef151fe1d651007008e8c2e167bf1c344b7a3b3292b6691d451e0f2093fbc96d1e24f425e8871376acd306836819f1c86e0da7329c2d2ad6f54e5d450899e751a4b4fc5db0c5978b203e76b0d988b291ddb8dc9fda4c8ab036af431baf3916c9c33215a8c5df5a9dc23fd8818b97757ddbcf382173bd0d9e4b037290ce11a80023474ac007dddb3e8547d02d2d42dd0c81cbe313785e4e8dfe6f9eaecb2e4238cf9b44c19024bfb3139d77c0716da32f04191bef3c83ea9d38c01fb5d48b97bd85bd6b36065b1b5e63b934fb3695708e1ee76a9f147e24bc5b942d113d1a488ed78ad59edb20163081d17142a97fd65da747cdfac47bc3d30d081d4fec734438280cc1b6f38596cb804ccd7c9010e861761a99d00869c7268b6862d713afb0d22cf01051bc4e5d5db45beacede3953186fc8f0bd7ac21bfc69fabf288799bbe96be979be00fab8763535be8deccf1f35c1315d3efdbb2a898e484125130d44863fbf6e1085317730b32adf098366910fd0499d44491b13d91ef5aef13f805ccab1cb762f80ddf442ad89722d138480ffa8c4743bf20671ec43d2a50410bdbc9743e005e021defc72c76bbd09fa782a5009770cf6812b630ede630adeaa4493aba8b48b39f682ff314a22fd0bee85371eeb5f59c186ff286c9ffd017035e56135d317b872f14629bd16c63c121e6206d8763cd4d5b0a2c49418bfb94b0ed3af50bf28918a994a573f1c6241bfd47a7e36becd5cdb4db3fb11bb2faaf7e4a5ac80073f40ed3c6f34d8d6dffba4f4570ed33539b8912c6784fa79ea1cb8ae4aced510352f25461ee88d3527172b8e357423ba967dccf6ebb09925054d0f52bbeb0f4be0133390fe2c3372502aa8e42f4290017bf101c7ee4fbd4fc34b73054dd3b034c56f85cc5c92bfac2479170a3049cbd759fd135cabb3ea900bcf7ac7fad624b4a52224008a91922bba827f8cc24f9a50207fe02cab8e33210b866d431042ed821f20d2d6dcc8264eb7e595761fc16e7be1d5fff604214a35bdb3cc1c96855a66361f619e613eadb36f2b2eb312f2e300b62a31d13fd4272db4740841609bd573bbfd65fa7055d8318f7f2bb9b202b469da18ce0d84e790003b18154b6c3704f42903633769115ecaef2ae08c5434ca6771de546c612b906c04f038bf3ce5e55e0ad893b1429805cceb2cc53785a8761dde30f3a10af2f6965277049ed26d9c307c4d71528db731e5f382de1f3a1c612ae4b01b9d5824b79cd4283d5795026e8111cdae4f244b031839912aad586543782483e948e759fbb96c4c00b1c61ab5b45118fd0d1522e81f4f99bc4ae62f975611e41fe02b131fda6cdf968cd1498f5abe310412a4dc425d01150ad3d42012371f6369ede9b5e71cfca82415d2e9733af21056dceb46ced4a7ebc6509d962e1fda721167104e827f58743cec70aa36c306974c0fceb19c93eabe6f806bb1a54c7fb17f915e7e343c3b6e296f4c5eaa68f5130f510cb532b3fe46069b828a23634dea89625fd5b15ccc5f5fc6ed8a08e21fe4cf9b951e4256a9170f9bc5155c2133df893676d6b0c486373b99e7d5679eaf68ca3ef9490e58d7124b7f1d092b6a8d589a244d9e726090ea6684ccba24a06ad2ed8bd130fa9697791c95656937a83aa2f915d31fc5ed35f140757c2e7678a84ac90731758f23d197db3625e9d54b311d0ee9beae48ee6d5feb7735f67a776945741d0c5e86700414c3c6cf624a0a4cc0f8f261622b05a8751a57a78554d8a6a530c680a8610b9514b3555f9ec0cc37056d018fd5d7daa422b2c127c24ad341fe729bf9a2793c02d701fe3b0afb5642e7c4f89e01d2a71957027fc25934c46c52875b5a85cdeb21d0e44831bbda8c3df409137257ebef67ca639d20ddf05a82af89eaa10c882088fb1272b8d08bbf0c50b60e6fbd4bf6cbb15870379b88171f99daf98c4e074044b1f0ee9b9b3f41b67d754e258a4a4970ed5db8b621fd546df08f0336ac69618b7077bbb89622957fbf3e4eb6619dff87d039597b4bc74281ce543caed9ebb01afa29c51df24b54a99cecd3fa270be19ad635ada3c21a5299a26f9ba0f0943b704c7e259497205f2885e2cc41c4fa5b99426c59c83de35cd8fa0f1d3e657b6497d32273bceafd795b8fee17207b62ce5896c57970145212e53513822478f52673606c99f0be185cb761de9daf187ce51b9864d248ca64ba8846f969d4e05fd4bc232ad6a0d5905428d640711141a1c99346e6b731a5f5cc9c93d0f55a412c92db2d1b00344ffccbcc94169939238ce195c4e7831dee939157596084c00c5035b53a32c421dce808c99e2a3878c18a43ae6d213c4d46033454c6ce8204583d48907788e043da844b3ff753efd9e80935c1eb366445c179427a4e5bd46cf8b84616976cff6ed76dfb210b05ca125f9e72e85693119a8491fbbd73dae51d097fe2f4e65552b7ce2ca7aa33c7bd3a13512426d7d04be0b8388b321510b4fd3c5b92d5b5d8d2c200e61dfed110e23c0353b056d7489ea13363a6c42497208caf9f346533cceb63455f842ff31ed2316c6662855fd8a28f6b3582a12c7eebfc368981fa", 0x1000}], 0x3, 0x0)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = socket$inet(0x2, 0x2, 0x0)
r5 = fcntl$dupfd(r3, 0x0, r4)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000380)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x7, 0x408})
fsync(0xffffffffffffffff)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x2, 0x0)
dup2(r2, r3)
r4 = socket(0x2, 0x2, 0x0)
connect$unix(r4, &(0x7f0000000000)=ANY=[], 0x10)
write(r4, 0x0, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, 0x0, 0x0)
read(r3, 0x0, 0x0)
mlock(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
msync(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="20000000001a003e74679048073000000000000000c41000000018d4915bbb0f57"], 0x7f}, 0x0)
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f00000001c0)="e4c5a0b2f54f397d92861185c7db60fa582afea1d2b9af442b72eab841bfe6486a3f3f91d092f41c8ddc894eedf1f5cc98041d7233c82e26a87571541b5cf0aaad73b5f5e27c649152abb31516bedc465d43c8a432f6b3f8d98c720e28335c6f6acd2b6bbebbe31cee6a0409391c809c1ddd0a013bba5a64e805dceda1092b7f46ebcf2b624aba7ae2d86c88337b855f8d4f38", 0x93}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0x27, 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @empty, {[@timestamp={0x44, 0xc, 0x8, 0x1, 0x0, [{[@empty]}]}]}}}}}})
sysctl$net_inet_tcp(&(0x7f0000000000)={0xa}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000040)=0x80000240)
r0 = socket$inet6(0x18, 0x2, 0x0)
sendmsg(r0, &(0x7f0000000740)={&(0x7f0000000000)=@un=@abs={0x0, 0x0, 0x0}, 0x51, 0x0, 0x0, &(0x7f0000000040)=[{0x1b}, {0x10, 0x0, 0x0, "14c93019c3db7cbe19c7551253b4769b3d79ed0af3500d40b1c40f1f65cef3cb6192f959d415dd3e1f19d88d1027b93f322b2a358f411d0309dab0087185b8e057da9c3d6daa7e92eb75d43f9320ebb436777e42fccacb4e553f5b60f7077179824a145dae535e7a4d93435cd384c76684c9aa6f7f3e92f00e37af55e365f31fbd950a3b974d411ec5f934db4db8176da750a2883fb0a4576e88c85406741ae4f417e8121bf7e6656121b5cf67b0bb3fac2716509852743aff0d5c9c5675a79e460bdb699eae20aa8204cdb17735b6942c90a04e1cc99c37a865c94f40bd6a8e5097e80f8469fd08b910ef5ecd14000000206bb1412bee8a8c4c39b2306ed1c110f28ef9ebc9da8163982fb926e029f35e2fcabd4f1d6e7c14a94a089abf31d3c38b66ab125bf828926717e5b1724238c193a9d45ad772930000000000000000"}], 0x1b}, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097a8, 0x0, 0xffdffff6, 0xfffffffd, "080486179cf37daed67b522000000000008000"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000003c0)="4491125e055171c5077f329557a30004856f276a74bb36778525298af8f7e8245d661132a4474bb2185340f8a3116927e7776de444c14b4233243a47aa8c7779222189299257a1a295db6954f15fcbf00ec4ec9a0f2212a452aa4bceae373d8c2107b4e185e783a97e180dc9e7d921b6722798dca9", 0x75}], 0x1)
syz_emit_ethernet(0x52, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x44, 0x0, 0xfffc, 0x0, 0x0, 0x0, @rand_addr, @rand_addr}, @generic="a5221735de4315163c1f3719b503a0e08efe6d7cfe611c21a10832f6376c55b70718b569a35d9a9d26e1f37ebb899731"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000240)=[{0x60}, {0x5}, {0x6}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x4, &(0x7f0000000200)=[{0x7c}, {0x64}, {0x6, 0x0, 0x0, 0x10000}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000100))
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
close(r0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000002c0)=""/155)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x26}, 0x2, &(0x7f0000000640)="fedce72d5902e4ee37ab800f5a52f1d80ced6c299f6df57177b83c89b01141f52b7642eab8b84dee8bcfe0a33510fad07daa078ad9c5184a773c94829285434bf7bacb2057d10a2419e13b60cd30e58817621ad71f88075e833cb7c0db67a1b29d31ab7de5a4768fd91318e9312bfdf973b8ab87793aa041", &(0x7f0000000280)=0x78, &(0x7f00000005c0)="0c93c293fd27b56aa91ad595cbb7860520df708cb657e0d4232a42d86673c0b5ce4963e38eb9e7813c5c519147af6bee94029d9a5f5e92854f5bd91eb928d3b15b40030319de2b42a71e8a54ee74d64567df36916aac7f8f28d39b29aa62ebe14f1ffa350000000000000000", 0x6c)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r1 = semget$private(0x0, 0x4, 0x1da)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000100)={{0x5, 0x0, 0x0, 0x0, 0x0, 0x1da, 0x7ff7}, 0x7, 0x3fe, 0x80})
semop(r1, &(0x7f0000000300)=[{0x2, 0x9}, {0x3, 0xf98, 0x1800}, {0x2, 0x5, 0x400}, {0x3, 0x77ff, 0x800}, {0x2, 0xcd, 0x800}, {0x1, 0x1}], 0x6)
semop(r1, &(0x7f0000000240)=[{0x3, 0xffff, 0x2000}, {0x3, 0x1, 0x1800}, {0x0, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x0, 0x517, 0x800}, {0x2, 0x0, 0x1000}, {0x3, 0xffe}, {0x3, 0x9, 0x1800}, {0x3, 0xfe, 0xcf81320aaf5ac8b0}], 0x9)
semop(0x0, &(0x7f0000000140)=[{0x3, 0x101, 0x2a6c566928f6743}, {0x2, 0xfff8, 0x1000}, {0x1, 0x0, 0x2000}, {0x1, 0x2}, {0x0, 0x100, 0x800}], 0x5)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000180)=0x1)
ktrace(&(0x7f0000000380)='./bus\x00', 0x5, 0x18, 0xffffffffffffffff)
sysctl$kern(&(0x7f0000000480)={0x1, 0x17}, 0x2, &(0x7f0000000800)="103bb0a35d2771302a0e47ea4e96c803a0b684e7b6899bb7c0bcdcdb488ced4bfafbb5c291a3475540bd58b172cdb7e01a124bdb17a6772c9fa700adfa83728e45bd3a0766fda71c413c506f02aa799a540736e0df20813a64d133e25f88bc28418172139640f66cb338ccebb7da3369cdfdc6029eae49d0ff717484c99fe7ea94d484b23f7ca836ef0a8f4e70637514e369436c6f92906d139f66e807895651409df3a58b0c48d912049cb318b681af50a674dde53057e9694ef1c5c3a116d615a8e87d19615c82038023f0b00fd6e25f44f4b4645b92599d9b2071b1e6532f59d2ea0b5d60649a85c7a1bda4a3988068b9134f136d635594ab5055ab75a0b494101c7905e0c55c12877563c41fafaed9a7c83cd214ce9d692b825d802a6eabfff133ddc94d6f066f3de019d249399db92248035ac06fe69742052daa87e4e55a59f170e919f4265dff26a17b7593c7e838fdb39612dff7195d044f2e9c3241086d656a6f731e45057ce3711aaee86a5c80e152a3f1cd88474666df6d47fdb56d6100efe8577697f48a387d2d5cdf90e8c433c51cb736b31ff9ecd0845421f9ac178c20ccfa195c36376189cc5d84118741931efd67fc83944f0504381a0fcc69a534218f4bdc208d3f7672e4ca0108268e9bf97eb3fd58de5703c4b21e5d6f7783aa74831e85789485522574e7061b860e999f639db086b0d1b9ef45ec24cf3b30b41a6e7bae64a9ae919d46ccc93b80329481905c3a68a6986639ec3bc7e9761b42109dc3e01d0a902c9fdb0255c81df895538c24843d3e8da2a20a509d138e3213c88cd44397573c44f8266c0cd68179bed0972df0fc1e0d14b25e02f3a30298db5bda08430906614752e5f69fd2f88d551cf40b2bdf02e1572cfd08fae365124273f6eb470d469ff7d9ad252718c8b93c17956083163ff53832019feb254d22cb99c3c3d477ef7068ef7bfdee7500e3f86c404d347d2d691ac0118885c592fa55295ef4d4afbb603bbdde6c84ddc930a7d2cfd91cdb9201c43f12cbb596dcab41db7627e9ed636ea67428283340d8bbf01a06138c9918fa6e59aecb3846b979b425abac77c28a4c85d1a6c69b7576af0e994bc56418b29a74543e5ca3dee468b0aee2ecdf3de9ca881749c31e272e6ad0a66c2f1c7eb82b01de862f32af9a0703f3b4f1bf0a05df4a4745a2d106cda2812635f02ad842a891ef6ec620a0e79df2c4b05c8a03839470d8d20eb0cc4bcbd5ad70a9a5ac3589bec77d53a29fc32b6b1e6963129b1ce2fb2a7637a5b305ad159d3365d1de504ccca260920a8271b92a26308a86e0166696b4a89466d35d4bd562aca2bbedd5f4bf978bad3ef7e2664e67e05f933af911da356ffaf2e245734cf81b2b7d1bf52240a804dc21f23ec91f902a46701066cb7ff19cf15b9d5a0f715d92addb09e6503e2ac84a21e05865dcbc142070c2d079833ab632dcc0884c97b18da82f0b129051bdecb692041aabd09de527e6f257280d581f4065ca6c7f612f62e044bd95ec8f3b36a63395aad8de53c4f2a8b1030a5076c58c4bb8be34a66791647770dbbecf90346a720c3681bcefa2e454d386d345d8bc3701a0d27c1f8221ebc5309beace5d149af727e88ddb727150c6af3c62289acad31cbf97587ff743eea15028bf85067b134b33e51876a27177fc291a9444f20e6c688cfc2f8ac1542186a5ee2a18f4b69c38696c38a6b06befacc24e1c46a48d2b7f3784388242730b3f951c513dd83e6add58f488e642e46239c3bbedfbedce2cf4eaac7be1b4f0ad5db0069efd81908716a9bf26a934216625cb7a4f3fb927b6bf22c350d11c0a499d9d2beb3a95c23cf86ebadd75f6a66157eb6c4d4d2f2a71935c35c27da077010378f41b681f24d4d64175ad5ad8564ccda82b3b60b86369e70c519af2e567518bc1ab7164fe41fdd3043172595213082702bd61907656cfdfe9bc441b07b507fc076efa2884356016e508832a016f0029aefb2301172132c12591b83348a28ece7fda3e40aec9fd82b7c79b4c33e803f8a6307307caa94c1918d6673fac929c4f3ed1ac84b3b92e0d502aeff43b1dde2509395e03ea026769843542e61759119be24b0de4c4f830ca4363ef56eca685a75c78187cb310e41de20609261aa7b5408910878f5c80d9ae66a3d6a1123f778e74169c52a616f75b74f4948855d7dc54fabf3ae6ca6666ada28912b8b17c9d8397af0621aa90b51f3ee206d69021237d4d0dacf6510b4c63d1265452a8e7e8126a44730d051180723051e60bec853255b4f86a652802854a3f712d560705d7135c3a2fac1a301ebd5b51c824e6fa431439920e1f7c2ce9d4fa948de2002f9cf954307b5348c49a2a83656ab28de2b4bebe9b8b5e38be54c86bfe700bf83a4b3fd28a2bb4063b14b541eb6e02dd975f73d465ff033b341c594f999257e190e76fd612178440ebbbcfdfcc49721526e20fc0f307bc1784fa8e511110b4586720c0d84474a7af9715f0629be2e799db48d6d01bfd1ef8c65b1703fb7cac9891a3df5b1e547a9ec9e965ab962e98f633c51c7c9e2f9109424e61c8ad9b45f445849fa21606de73f5e69e32da0b542bbdbb329fe9ecdf5f146e0d13c6c848baf35be7383b099ac47e0036104d070843554598180941f49d8c27293156fb1b2c53aafdd00337238ddfc1a70e8f43496151a3753f4775c56f57288d7739c7becb48522e8086e03e49eebf4609bd122d59013315ed6f999b771c9adb9f30bf4209d9b05ce9fafb44c2a2a5611906f0567249c50cea43ee29f5030f19e6b345c116e6be613136f5765a0b14051594b59a0de88deeded13b9bbaa101a1d863f74be1154f910be4f51fdd4a451f2009455957e636a09b16852371ac1accb7fde816cf9dd4ef074323c7c744e073ed6b3a283e34e1e2b107a20834ccd639909a8a69b5d558ce56dba2199370abd3960a93b29e05e066eab0fbcad7a4c029012130f8fbb16518aab866a0f0433251131dede3ef7033e5f7c0ca651b9d2d66dae6ef1948bae4b9afb45ce70b95e79b1dc6df50f209a7a4bc0ae9028af48d63302048f5c6fed43b2446691f552476666f7e0f57e0eec59f3c2d7c324a9651916d5d10ba1541139e59607d9eacf88fcb2d863cacc83b2e64545460244647ece4d3465df85c4a5d214619f0085a14c16b6aafd82bbc59faf8f4405572a244df0f9aa277b5caa9e1e11e5924836054cc84064316842e95d30e2c96422ddd3212b11d90143e93a225082ac6ca8b5e86625bb82500c5ae2f10a95407465efce49e2f0d80c21cb926a4b8f5c88ce3f3ef9cb605d90c9f9098e419c25e97ba9e8efffce4a7bfeb19bfc5b8ab5f58ab61ec9422c5e408cad344883533a8d65e674b5b354c90ca8574934f8f034c3b682f0403d8fc087ee54d0dd76158de4565295af6af48b90bd39577a84abbc2e95df87092bf3e3c7573b2823174251025c933c3318a2d38c6c94b3cd6fdda45c840b04bcc62de746653105ada2ba79b551b2ad421c02128a169ff7238ff678c77ee0408eefa741690be4900bf9d69bcdf5f6082a139a4316ea3730e887b7ed57f9f60628be9ccacd0d0b7febfcf51b7f49ca0fa306cb5c6861f5ddef2bd0828db05d082932bb70ee6878759af104c18f0c08e6e9f490ffee716ab6d9142df4a7da16911e9b3c4c21bd0044615828930644684f5917419fd25eeb5df29f0fdb87e7404e4ae1f052a0016d1505f61cf3082c9587bb94b20adfad867a7338f78576f9ea347bf49a086b808ba56760097a5556f35053d9373877234baa6dc2e3cfaf8d9d6d52d8cbadc6baad876dd93fab78cc001b89a93e16f347025179adc2c8bb1160c661445a81a1bb6397f21cebea5abe3deddb1a749cd8eae0ef41489e0048260b0126f3f8701799472a717f509518e02e2916c1f1abc0b56bc1e3be3e3d3db3483d7e910b7b3894f9239aec4a9fa07c935b2f4f847dd67e512ca9c1830ce56a0942fc55df2f2734e5b8c424711694d214cbc676672140642d1e57bf62a3c214ad450d90af8e729a36fa665868234bc1fe5492183553b78e57ae62734dcaeb4c49b517998b72f81538488ee568740e65ba71796ccaf1a6893b9f758892899bbe150bad672712073a90778508896bfb56c824176cdca63a2712f5e0f6bc92389485fd2bc436d802702cd9fbf9f8dd3b59767d6d1113479c885699e2f88f25362eead95db5f78ea3a08b4ad4dce8403ea1861689b61d572a0904a890e241bf90edae8e2b6aeb0c7b3dbbce6566c672669acbaa7c2b8f893d40f0b1bf1d2b7f2af86737551aa5b8f2fbed9ed9261f3099c0ff8b56117487e7cd48b68368809723bb8140214d26cae307740edf644b485e98074b6f0e786935b73c1948baad38957512a3ab7db7c02d250429082e4b2099b81cd75897d3b678b9db1f722e35b01d41c827033ee30250b2fe339b578ecd04b54bfcbbd2f5e3d4fde317646f67e6245b6de3a434dda96a1f0ed6807a4a1195a4a4fef03e67058361e1799bb2fd213601f7e6cf43b90e533a7b844e0989c2d6a0e3df146d640936edce2f790bc570b48ba7b15f5d73c348f9ad5c900feb9751dba6a67e1fb0d73c2a2afe83cc525f4a18d5f21b23ce78e7b5ae7c08aa5ca706844e02576293523fc6e829814926660ff4082e7347c95643dad86e9afec8944b3b03f0d093219900055d674cabfeda51fa9750dfa0a326434b653ad37f52eb4551a95c6a95da0fb5d3f6c5476d74837e69993e4f677e8ff27ce1ad88a95fc628c39e3975ad19788598f6e7e7366b88d3e1f58b6864030c5b71dd660bccbde63cb09cf76687acb970b1efa8052e5464271e0848428af21d28cab6a74bd5860cb84917345a1b562e1d973d5685e31e1c3632e9a29f9fccd9afca6c57c1f3d648f3346699fef4a7609cbad8edf78bd4751f2055fb789fe06c1e6f364d04d17fd3e67efd112e1a5f1a4c848912e53289874576d7440d9531c801cca1a0070d385db6024cfd6ef7ca08a00aab8f62c53c953b8c8169c69e3a95c26ca0e4137a1661cf7671653f968f55de9a74bb572a13fec3361461b6d811d5498b50223424ec3d7bd8111106fd07cd2f47f3f62d252ee3c8f3dfc4b745a2554d71925c2c0defdb2e3a140559dee0abd0e33e90cab4288abe9ffaf3542b661c747327d01e0676f549c37d4bacecfbeac9943860de8c53d191be00ce349d529fc0f4c0cf1c4066b2fc3e697746cae437c454d40f61f91e1b71daecc2a201eb835311c03584c103b7c4430a423a635635837ae9ee7607a3f39acb5b79d8d20708239afe74357648d93a5b1d11ce6e83c56a01869abc7cb7942f5452a155fbc3b9394b5b199d2f541f2bea75d77274aac539f3363d53043e7e6fadf90c3118062d432b8ef998fc9813461f6b95d73a977183908c19efe8fadaed6737c0c2b359c85f8bf714bb42b175da69dc928177af4dff6489e3fe9684264651da2c34d565ff82cdb3fc9089cc257806f637275aa7d22c259b13a55273ffcf2668975d23253486a941c98f935873b5b290db8675c46621c1da647c0cd0c8efd1145cfc50033f362edf0bd02177a9333b1a6b641e9d1863b7ca182c80d3d52c956cb8deb55ae6b4b6026ca8b1bf36df1014626fcb7a611c4a09b77be059d799c9de72a64d6fa1a760e0e5ab33edd0a437b2b201758b87ca0332f39299ea6ff732936facb08bac8020b8d27032897fdd4688bcbcbc29e15066a1d0fcbb3b8a52159d05673f4e4a8f964b0821394d21a2d647e800e8789", &(0x7f00000004c0)=0x1000, &(0x7f0000000500)="6856d61a733ac0ad425b796b5e0b9ed90eb5fbcd149ea8855eb11cda65ce89edc4744da06eca1bdea27b33fa11aa10480e136e43a38934f39240eee15fa2e5d26c1cd1f07b1f970ccc561e07a3ecd19be9dcbb43366309b983ddc8903cf0b54a8e3a3921ba9da2447a3984b0df171e41e99684a5ca0f0f22b49f41d22872a250c20ff05309d130fd1b10663ea89f613f6261c6bf21f30a", 0x97)
r2 = semget$private(0x0, 0x4, 0x1b2)
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(r2, 0x3, 0x8, &(0x7f0000000280)=0x80008)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000080))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f00000000c0)=[{0x23}], 0x1})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000002c0), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0x20006473, &(0x7f0000000000)={0x0, 0x0, 0x0})
ioctl$TIOCSETA(0xffffffffffffffff, 0xc2d0422a, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "0400"})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=""/48, 0x30}, 0x840)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x28}, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000001600)=[{&(0x7f0000000100)="e8fb8707e53bf0682515500f5fc54eb14b0d0571906ad18216a273019c2e4598a0982e66829b304039e2cc2bfb10ecfb44d276810ff29d807b3d2fb0dc7d2bbd4dc9c925b5bdb4b1b1a5f83bb9d231617f4e982c05d2e1edfa2855a38e362553ca9bcf35a12e9bea210ac5cd53bee40ab0329825c87bddcef8a1cb1f3e4058a54f60716eefdf777b7bca266962af3f1744b17e0da13a91efacffdf4bea4ffa500f8b472c6fdc48f9189ab2c67e109104eb944539a12ad68f471c3e7ab5636c58e5a6cea018d55ede58999d82b99337", 0xcf}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3, 0x0, 0xffffffffffffffff})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x24, 0x0, 0x42)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x41, 0x0)
writev(r0, &(0x7f0000000440)=[{0x0}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x5, 0xfc], [], [], [], [], {}, {0x0, 0x0, 0x0, 0x5}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
utimensat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', &(0x7f0000000040)={{0x0, 0xfffffffffffffffe}}, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000140)=0x300)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffff9, {[0x1, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x3, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x2fcd, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfefffffffffffffb, 0xa6f, 0x2, 0x80000, 0x5314, 0x20000000ffffffff, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x6, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x7}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0xff, 0x1, 0x203}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x4, {0x8000000000000001, 0x3}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(0xffffffffffffffff, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000080))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0xd4d7, 0x0, 0x0, 0x1], [0x0, 0x3, 0x0, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000140)=0x1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000400)=[{0x45}, {0x20}, {0x6, 0x0, 0x0, 0xfffffffe}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
ktrace(&(0x7f0000000040)='./bus\x00', 0x1, 0x2, 0x0)
socket$inet(0x2, 0x0, 0x2)
pipe(&(0x7f00000002c0))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
seteuid(0x0)
r2 = semget$private(0x0, 0x5, 0x0)
semop(r2, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r2, 0xffffffffffffffff, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000180)=0xc)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000001c0)={{0x7, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, r3, 0x63, 0x3}, 0x100, 0x321f, 0xd090})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000000040)=@file={0x0, './file1/file0\x00'}, 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ffe4484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e721e40934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49bef3238d0b05c82c7ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae836cadd4c3ba30d79ee4c956121c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e6ac33a607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb033f52ff3063d96df138a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b895417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c07", 0x1000}], 0x1, &(0x7f00000013c0)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES64=r0, @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=r3, @ANYBLOB="00000000f0ef7c7319fbb788aa378d578c312540b6c5d3d1680abd701dc89b819f500cbb9ffadeece96494c8110f34397047b25a35dd300e7f71f546193d83dd77c1449cc8405c78487b244e08011e5e899198c36b8746b8ebbb65b85bd711e4837c65f6ffc3fb51ec6096925e31b5b75e09e9a2c48f6c7dc7fb503f9805bcda470b19e235d68abb446ffe6594c31707a247ff92066b720ba42c5d57c48c29893978ed6f8efa9f1ca1dd5c60aaf94c5dc271bca442ac596e2ff4fcb893688a50b9e65e4d9045448e57e8b30c6709ee47b057dfd590e2c3676f9c0b9348fd72cb2f63e7a47c31"], 0x58, 0xd}, 0x4)
open$dir(&(0x7f0000000080)='./bus/file0\x00', 0x8, 0x86)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./bus\x00', 0x0, r3, 0x4)
setsockopt(r1, 0x27, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000100), 0x10000, 0x0)
open(&(0x7f0000000640)='./bus\x00', 0x2, 0x80)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x6, &(0x7f0000000040), 0x0)
r0 = socket(0x2, 0xc003, 0x0)
connect$unix(r0, &(0x7f0000000680)=ANY=[@ANYBLOB="9a02c2f5ffffffff"], 0x10)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000400)="3476df6d0155dc2767ac8b9c77bad6f288b5a3d5145218a1f4e1068996dc4ba46aff1e02408a6ce27d81050e00accdd07d1cb96d92789f23e1f4dfca7c01969a9793447ac4642109360e13ce096c29e253b1ef4c0dce41a96410d26209ba371cf1db51a9e7d518ef9c48e0af40a685a9676e8ca84369f50c74d8ade53b19c4a65567e4f37ecceee99a7037381ae21a9232d841dfd5e4e317c3c84846198fd0b9b993ae00ca521d92b9c96ba0d00ca5d38dd46e3fb96e7361f933dd3c58f7ce3d218f01947622a95ddc01e719bda58d1527d5c303ecf0cd1ed63775ea16c19f2ed6b40b9105b55ad1d745f844fa526da1d3cae9afbb516340a37bb01df306", 0xfe}, {&(0x7f00000002c0)="1e844b8fdab40e6783a700a058712d", 0xf}, {&(0x7f0000002000)="17312fd8110886114e397f21177b720726d578207484d248d414664f4f82d018295b9d7497f12b1624b0a6af034423c6da29ce1af0c5797d2c8b6855a56cf39920467258af4c50c08e00fb72079d7d72d3141320eaa30801143236ef6d9628179f24a4253bf77d789ee40abd37ff47abf8549bf90692bb6ce650ca64d07c9a4f8ec129113d5d2580500c8bd81519b0f3c4573a5368c998c84b5cd275a48dacee1b2361b90c358978a23130cca7ba53a31901b28713df21fac0467567d60c5bb777f6181e98b6b02543e0d624a3160b94982eadfe5f8cb5095112e1a2c85652c17e3289af069b0ecf1a19d6065e93ffd73c5683e6b5e84c7b1d1c69471ae630672aeb42f774b5b5716fedb7c423f28b2e1cb483f4cd3569051376b52ca0e5de2440aa187554f8ad72560bad0fd3db2c06e51329edecc2c4c65ac10be14689d50204d94f9cb029bb92d6fb8766891b4c619a76239ac05bd7ec3849d7eefbbc405286252cb415ccaa9197f17edb5a6cb42a57299187a3bdfc8c794ab1da3b53b9b43d70f4bedbfa7185e8e7e765fb63c01513f3e233474b71f38abd366e2bc708fd009299e4b7799cfbd20593ee6680de55d6f13c5156541538b849b6d9341547fce682f7bffbeb8c0636d90b1cbd3ef14f3644e402b909ede83f25fb68577b57ea6a7c11c4c127d4fc22e0e29b5c5c04538cd1501b69cf06af9f26bea99333013e91aac9e4dfe8c689d415eaa769a9e8c5dd4c94a0d4759ccf2e147bcdecf8b58e1b0c81a1cb05ab531cd415e30355f17029f604ea4c9af0f26da9794ee39dc5dbc2367669906872e2b9614d1df0650e2091de8e5d0334d0bc322e6cb5d854836393ca4e34a3466d2626b76aee696dc46628ff46a139e8157fa6fd41ce5e2a85de86f49d2978ced3213c1ffab41f5e1993de0b8ae48354789d11513c8cd1effde15b864b917f980cfff4862f3e0ba939dc1114b38388bf7ab12e09f400858f977e7a7b177748d4ccb31dc307edd97bc03f50817cfd51b7577356013edb98578d7f1160552260de8a3df5cbfd1181076f1c41a5d07a8e5aef1f59f5568729084b2077ee8b5c51f348a9e0cff36664e7d61b404331dcc300e386cf497dc1b4c5615fbcf66b2d57849be0af05391ee3f78d4ebcc90ae02e810ffd65fe77de3a551670a2b3a60b855b690b0b43afd7e2e2740132451c2c52dde490fe22a8680421cc380cd4d60213b9a540aaeaf290a863829f1716bf1c6a0c4c819cdbe40bd44791051140749b9ae75a6c8a530c14aa8a316af1afe28aeb3d942de24701403ef4601d6d2ea73b0d3fe74e9998b7c6a81d5098577e200bed57844448cc742afa4bfcbf023ec9c94f335112a2ace44f6d3abaefa70c03bb34456045fecf9e42419230feb0444057f8c33c405b3e663a3f586ca173f3939d6dd1d215fb2c3929d88a046faeda8987f42a56c224ece4bd07085d2211b33fe2b1662b01f397a9287babe3bd9d37022dc850071a5fda653affa51497a4a0f82437446dda05aa6d909a3be1f8cace8ecc07037d32dd489ad33a3fa8a242f2cf0fda8c0b0932caed430be42106acfb2afc141866b9a6e8ca1212ece6aa85a175aa78af89d45525b8de885ef19dac98b88a21165f6856b2bfe761c6b8bbfb162bc1368bd9c81503a2f0b26f66f3a1681973f35d1f4e8e3064f1a64c15f2fcfb8cab6fdefb1a7c064e901a2b81e362a5b3c3525b7dabfcb3bbde98bfb224be48e002b164b2f70fe37a4379f21840e445357b8907679c65f883bda15b260f0a3b29c3ab70604e9197cb38293d39c827be85133579d0c69b2a6a45d311671684625d12476fdc84cb19ed694c9f86d89d292a87b67e1a49c5fa8582a33ceb333e1a3553e41fc35e1b9eb6fe918c2525f8f9c35803c18263a503bbef970958229d9b481c27f722d62734cfc3dc865f1f90ce4f20022236ebd92631522ae1ff6149ded8eefdf89f360223a97ed7734ca3a0f0cecc2f138f38d98e449bd516acae67b779e60b3113c566ce90f93d77365a6ea221b01da096e50bc5db85d227719839bd946b50d8ee1d7037996813738521c7d2fe9f65151fa6056f96f7f39dcb98fa21c21dcf9edd1a87e0bf8f252ac4c36533e47be90cd03d4b1cf724ceab5d0aefc6d021b91e3549e46639c2b20179933db276370293bffae7495e74567ad5914748ca736ababe199210556590cfd169554d2bad4598817e30af695ba3078e2ee5a1e45fcd16c41818633dc615d43b9b787f712e1621601e9c82d8f656fe2d98198b973129d5d77d90df2d947a9c7907fd27223c477db947c30bf1d6cc39a871d3cfb000872e242983824eadfd0d9e9d994b0ca13e0412ff11685da823eb0bf044981406119cee95e3461edfd7b70e777cf58380ea8963f10d01a2cb39e61a794d3db1fc3d28a9c9b0f909e769f42d1e9464b9a984b69194053a49ab55f91d0ac23c7c764eeee974ccbd4d1f55e53b7731b2ace4332f0734cc46c82055dee5c0da90f51a06c3d4e641ee0ca994349ffe72083ac37fd86cc47a4958df0ec8f4758ec95ee616fead3c0135d9799fd75e3837fa0c1ccee866bacd4deeee2443f390b13650dc12f88f9062abf3e24ebc4872741b1d6d6734b329119c21de223be346d6a2421d9f776ff2b668ba023390c655579f9eccaafa9b73a8fa2400a5e1b3fe695b3969ab23018461b0743e755312469afd57677ea177e9240805f1eae941ddb7ce9ff0c102632e9dc766ef35491104ea07f117c7930094999f36b6e82fd9ac12c511ab780c5f8a25dcc47e519905ab479c5eb41e731483efe14b33c8685676fc6d53b7a124ed94b7afb9201fa29dbefa9108ac8ade27c203105923350293a22eaa99b81547f56d025c4006988c385462fb4112ba9da8e0ac1c73dfd006cdd8bc07748a9b6df6ddfb13b26b5294e9bb488c69c4fc35a4b2ab982dcd166b70585af918dcbaac02ed9b8cdd1418e80758c396b13ae9e335dbad59d0c18eabc63839453cd68c9c7e97904a5ed4e09bcf21f94dda44b05e72563b14dc107c09210d9d2112098768142e83fe60b5cf764a094b94087f012da23234ca0c18450482f7e77e5c247e42ba904d61ce1ac62584ba5c29fcb512212000b175bebdee63e22b8f82381bec9f119073c633b1536236d7d4350259283154239516b391b8fa0ad87af89de5e2439d97053d8e49416706efc1080e92bcb423fefc1558dc0c2b93e1570f971a4979730a0922ef30da447a1097b22b83417838026ea42879ecb206869214f77e3b646659d9b504ddd3fe24613d75b0f9412d01fc1844bf0765709eacf8c503b7b2e5aa706ff9b782b352e2a10b06dd4c0bc2e0ce3363ad33f2fea9e1e9b8b0b7fdbdc976c9ba0365f52daa39417308bbaff960f8e1e92a6d9cd4115deb5734f0435e9a738c158e7ce7db425808aa3de1f1ed23e17c71ec84f8c306fc3f9c711b7791bd04dd27ad5ad58d76f61204ecc358326ccb2c6e40f156aa6c5b5d9f469b2d8213d6686b78f14db4b939ca2e4fe3158812ba8b97c7d8920760a0320d9fae7c2ab6b6d30a5429e4ca40a5d6d2601b865a2cc19f0b2f78328345bdbe60f9cf941fe3c0c663058d261e2f7de848275fad490d0caa40d9d860b4f18dd5a95ea390a1025ddf33237913f25d4e23f1c816f250e7553a24e9cc46297421cf902d511d2ec1b8abd165dfa9022e993e01718e95daa8ad81c91ded8547f4114ca5bff293002fe22b746a46c02b530bdd2acb6b55a69e76933d9ad46bc3066640b5fea428a6d75c0c93547d01869059ea541b30", 0xa84}], 0x3)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {}, {0x3, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400002ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getsockname$inet(r0, &(0x7f00000000c0), &(0x7f0000000000)=0xc)
r1 = socket(0x18, 0x1, 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000040), 0x30688, 0x0)
bind(r1, &(0x7f0000000080)=@in6={0x18, 0x3, 0x7, 0xfffffff7}, 0xc)
socket(0x2, 0x3, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
shutdown(r1, 0x2)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1}, {0x81}, {0x6406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000700)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "c24c93", 0x8, 0x0, 0x0, @loopback, @loopback, {[], @udp={{0x3, 0x2, 0x8}}}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f00000000c0)=0x5, 0x4)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f0000000140)='\x00', 0x1, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSPGRP(r0, 0x40047477, &(0x7f00000000c0))
clock_gettime(0x5, 0xfffffffffffffffe)
sysctl$kern(&(0x7f0000000200)={0x1, 0x41}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000480)={0x0, 0x0, 0x104, 0x2000300000000})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000200)={0x0, 0x0, 0xfffffffffffffffc, 0x20002fffffffd})
r0 = socket(0x1, 0x1, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc028756b, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r1 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x8080691a, &(0x7f0000000300))
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000140)={{0x0, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0xffffffffffffffff})
getrusage(0x0, &(0x7f0000000000))
r0 = socket$inet(0x2, 0x1, 0x0)
shutdown(r0, 0x1)
listen(r0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d0633635acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00e1c8b2ca3ebb355769", 0x4d, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x3}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000200)={0x1, 0x2}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x45}, {0x7}, {0x6, 0x0, 0x0, 0xbfff0003}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
r0 = syz_open_pts()
fcntl$setstatus(r0, 0x4, 0xcc)
readv(r0, &(0x7f00000005c0)=[{&(0x7f00000000c0)=""/145, 0x91}], 0x1)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
pwritev(r0, &(0x7f0000000200)=[{&(0x7f0000000280)="9e14984f02847847fbc0a7f32058512d4d73e88c56189d4996c57d8f47aa18518f5c94b8ee44d99fb0fcd6cfc7d4a662828c7a870d9be4654f08681fc79818b818389e29b61f987552a9e44df13a8b66eb7cff55bbb78ea097822794a2f53b0f355589fc46d63242f81ed77aa8d77bd27ee6ce77b56c5a4a975d982ed7213710121bc2c2f29341880319cee96e154b95472926fb2e55fb422597ef902f1f9f45726a89810dbd9ba4c0fc7954", 0xac}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x54}, {0x3}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
setuid(0xee01)
r0 = socket(0x800000018, 0x2, 0x0)
setuid(0x0)
r1 = semget$private(0x0, 0x2, 0x189)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
semop(r1, &(0x7f00000002c0), 0x0)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000480)={{0x20000008}, 0x0, 0x0, 0x2})
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000380)={{0x20010007, 0x0, r2, 0xffffffffffffffff, 0x0, 0x100010024, 0x7}, 0xc8a, 0x0, 0xfffbfffffffffffb})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r4)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r5=>0xffffffffffffffff})
getsockopt$sock_cred(r5, 0xffff, 0x1022, &(0x7f0000000400)={0x0, <r6=>0x0}, &(0x7f0000000440)=0xc)
setreuid(0x0, r6)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000000c0)={{0x6, 0xffffffffffffffff, r4, r6, 0x0, 0x46}, 0x9, 0x7, 0x70e})
r7 = socket(0x18, 0x1, 0x0)
setsockopt(r7, 0x29, 0xe, &(0x7f0000000000)="02000000", 0x4)
dup2(r7, r0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x18}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
ioctl$TIOCGWINSZ(0xffffffffffffffff, 0x40087468, &(0x7f00000001c0))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106978, &(0x7f00000000c0))
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000200)={&(0x7f0000000040)=[{0x25}, {0x22, 0x8f}, {}], 0x3})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x2, &(0x7f0000000000)=[{0x30, 0x0, 0x0, 0xd7f}, {0x6}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
ktrace(0x0, 0x1, 0x4, 0x0)
sysctl$net_inet_divert(&(0x7f0000000040)={0x4, 0x18, 0x102, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000100)="02", 0x1)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, r1)
setuid(0xee01)
r2 = open$dir(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
renameat(r2, &(0x7f00000002c0)='.\x00', 0xffffffffffffffff, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
r0 = socket(0x2, 0x400000001002, 0x0)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000100)=ANY=[@ANYBLOB="00020035"], 0x10, 0x0}, 0x0)
ioctl$WSDISPLAYIO_SBURNER(0xffffffffffffff9c, 0x800c5751, &(0x7f0000000000))
madvise(&(0x7f0000ffa000/0x2000)=nil, 0x2000, 0x0)
minherit(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x802069c5, &(0x7f0000000300))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMUXIO_LIST_DEVICES(r0, 0xc1045763, &(0x7f0000000200))
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0xc003, 0x0)
connect$unix(r1, &(0x7f00000000c0)=ANY=[@ANYBLOB="9a02"], 0x10)
r2 = dup2(r1, r1)
r3 = dup2(r1, r2)
dup2(r3, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
syz_emit_ethernet(0x4a, &(0x7f0000000000)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x14, 0x2c, 0x0, @rand_addr="8ab32d78d67f3a344cbcf951919a5b00", @local={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x40047477, 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0x13}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x34}, 0x4, &(0x7f00000000c0), 0x0, &(0x7f00000001c0), 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file1\x00', 0x2000, 0x3ff)
open$dir(&(0x7f0000000040)='./file1\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000400)=[{&(0x7f0000000100)="1bc22be2bf943941374a8e08bb3488a9b8f632138ee7ead398b570256a143675baca1f8ad623d161fec3ff828917bf9ca3a83bbed64f0e3f8872718e166fc807c8c4022f47c8b93cf56d084f30119f3d4b97f835c8012a5ee02acf115d8da51f15776849b579408e16c2cdb6b39e6e54590f096b8dff19e5a033f2de9afdd2fefa088dfad4bd2a2af79a08a5839bc2734baad2127b272329b81d1380ea767d56946dbed8fa679daa9a0752de3ab69d49187547ae22a1452a4954224a9bfba4ee4aaeed6902fabad11c", 0xc9}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106978, &(0x7f00000000c0))
sysctl$kern(&(0x7f0000000200)={0x1, 0x58}, 0x2, &(0x7f0000000240)="2fce5c27", &(0x7f0000000280)=0x4, 0x0, 0x0)
r0 = socket(0x18, 0x2, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffff9c, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210, 0x0, 0x0, 0x0, 0x4, 0x7fffffff]}, 0x3c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x54}, 0xb, &(0x7f0000000180)="dc77ff6439ed12054bab7533e3a2e41deef6abc1282a730eea5eae2cd914212fcc7293d4bf35f9c9852ba6663a736c7e8dd2a892087287df9ff6572d02b3f3ac346cffebfb1e3414cde68f88c7d4d9c3286c262b28d3254e5965a02b43b8156448af443b456fc02dddffb6ff0e7bf1f27a4e329a58c7dc75c819e3d190b2cd403d6340ddf95359a0502f09be5ecd88277cd7d1e544a55511bd6af6f4c1e5961004188ffe7ba208a2ad19cd216bd5ead79b8967ab27bf96", &(0x7f0000000240)=0xb7, &(0x7f0000000280)="2090b092abce285f3fdf6c33cbc94c62b020930dcb1deba191cf33a42c1bc6200b748b0e73c174e70ca8c8bca8ab5db75742f07580b6fd084a3cc469ff42f2cd6f9f98b72d0036a0bbeff0f25e324da72865f470a3371ef9dc811bec2ede988292edb5db3a6663c35c3e9d1b230622abd6800d10fbcae88c90dca724b0d3fc6838ada4f7d5f6e94d6742fcd5ed1a98e239d6277d760a2c4251b0f50a26536d5ac0f2490a7f01f3ef5af9da798c21fc94d9a6911be51d3f71a37d58d52ccd8bdb3a4e73b558d682d5cd0e0d2479751340d211900b9e7c2e20b79cab6d595a355a4380763c49520ebfc85b876183085cbbd926820c3ccb9e6cca27", 0xfa)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1b}, 0x4, &(0x7f0000000200), 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000600)=[{}, {}, {{}, 0x0, 0x0, 0x2}], 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
sysctl$kern(&(0x7f0000000040)={0x1, 0x37}, 0x9, 0x0, 0x0, &(0x7f0000000100)="987c000000e9c9b3faf040a765dceff38911c6bc5f6b8bc3d400af4f1148af971877b25a5bc70f2320ebc54589b0728a057cafb84bef54d21d25cdf68496d2073ce91bf3c2ff2244000000005e3ea58b0100", 0x52)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000340)=[{0x87}, {0x74}, {0x6, 0x0, 0x0, 0x11001}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
openat$klog(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x1020e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x1, 0x10, r0, 0x0)
renameat(0xffffffffffffffff, &(0x7f00000000c0)='./file0\x00', 0xffffffffffffffff, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r0, 0x0, 0x0)
sendmsg$unix(r0, &(0x7f0000002d00)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002c40)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
r2 = socket(0x2, 0xc003, 0x0)
dup2(r2, r1)
r0 = socket(0x2, 0x3, 0x1)
connect$unix(r0, &(0x7f0000000040)=ANY=[@ANYBLOB="7702052bac"], 0x10)
writev(r0, &(0x7f00000012c0)=[{&(0x7f0000000080)="42031255a875b688", 0x8}], 0x1)
symlinkat(&(0x7f0000000000)='\x00', 0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00')
open$dir(&(0x7f0000000080)='./file0/file0\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mmap(&(0x7f000000e000/0x4000)=nil, 0x4000, 0x0, 0xa010, r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="200000000000008d742c007b80309905f87c8a5b6701000000000000000000c434000000290000003d"], 0x7f}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000100)=[{0x80}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)=ANY=[])
sysctl$net_inet6_ip6(&(0x7f00000000c0)={0x4, 0x18, 0x29, 0x36}, 0x4, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r0, &(0x7f0000000400)="e01d99e6ff3fa45070b94d86fd8f7d95be8227408c032ddce68c15d33a078e6fed1c085017c0fdaebc628e747e172dd5f86e19fd110209bb0181729f56f8b833aa8003ebe9cbdcdb9322e61208b1f82a6c6f0b81f03dfb826cc7925c9c301db4c72f93b978081bdb524e02c4c81cb30ac6fec4863f80ca686133fcfc2a0a3f418f8da2330827ecc7f4a2851687759f1c6d671a3e000000000000e83f00000000000000ca85e6bac2fb4823e8950508cda4faa12488671f33d1aadc89b3a569d5d1af82f332f564f9c7763813615a12ef04f75d2a515a280bba11b542a3c13e10e67ade734d4ec2aeb401d6404955156c97d3b7ca6cfbce55241f22301d64e4cf4e2378b5973f68a805175da617ae412b504585659f0d90ae28a59aa4d1fdddf0320c93de4f23beea3f454093f4d7c90fcb4b9d4e9a1451fdb8ff9f5c9d2d0d7076d7aa4429715e734251790855f0e161eb72d1d849f13cc87c81e00871b99cb817a2dc0cd7b49160549cb33ec791c0dfad11d5283b5f2559ed60c314c8e49342d3cafafd626fbb2376714f4c00000000000000000000000000000000f540d84ac0be861b16849f75668dc0f89b54cebb2f55ca8f7de1fd6a4e6a2222ec28d726bedabc98e14e397531b17dd25b1d1c6562", 0x1d1, 0x0)
execve(0x0, 0x0, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000100)='>', 0x1}], 0x1)
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x6000, 0x63e)
open$dir(&(0x7f00000002c0)='./file0\x00', 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0/file0\x00', 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000000)="ea00005c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
sysctl$kern(&(0x7f0000000000)={0x1, 0x1}, 0x2, &(0x7f0000000040)="9e5225eb6eb368d3", &(0x7f0000000180)=0x8, 0x0, 0x0)
wait4(0x0, 0x0, 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
symlinkat(&(0x7f0000000000)='./file0\x00', 0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00')
fchmodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x0, 0x2)
socket(0x2, 0x1, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000080)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f00000000c0)="0102fff67f0000024fb359657f1666819f97069815ca5835b6f65332127c991ab43a427a6555560ad8a048bb094573b887c6f4a843429b99897b40c8c7f4766c3bd9cabaddbd0066a5c0411d0be46ebbd981ea2df4000000000020a63fde26f7ca6a157ad150ada66c878f486f7e59a59a05bb689915b9098089246fa85c22ad066d2bee03b0023a66bcc45356c7ba884245783d9a0df740ec3c9eff495beb70bd17437528909283d73f252d74b0bd01000000201c5030e790e6157c7354745175bd902a5f48e0a013a1dcfc71c34fba6de7abc4c70f0bdd24244ade0d510672dd77da2c90fff2ec0000000000090000526da4470000000300000000000000002c8900000000000009eb3881885647e6b9ecd6f6b37cb2be80e3d49c4287ed75b08a58f19f470bd87e5503c733fc217ed201005e8552cdd86bfa6da9edbd2d2d845b8e1f2e11180dc1a19bd5ff5256df19b563ef69e55e74120536a99d2a43575893f40002c32ed7a1d4dfedd53dc233b41bc709cc342d559212eafd7e6b977bff77c2d1a749256ddf3343a02bf274a46145fc7f5d3082000010815e821403000000000000009a21624806a96d9619e00feb108d5bb60a27d465014b6b918fcf2b273f708044ef458aeb604bc0955254edb0450200b270acdab70801fbc9960ebf7502767ebb569f48ec63072695e50000000000000000000000000000000000000000000000000000000069c69c7e6f", &(0x7f0000000040)=0x210, 0x0, 0x0)
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x100000001})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x26, &(0x7f00000000c0)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @multicast1, {[@lsrr={0x83, 0x3}]}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x5c}, {0x2}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
select(0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x4, &(0x7f0000000000)=[{0x10001, 0x0, 0x0, 0x800}, {0x2}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x66, &(0x7f0000000380)={@random="f03640e81b95", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "b4f230", 0x30, 0x0, 0x0, @mcast1, @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "66b256", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x3, 0x0, 0x0, 0x49}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffff9c, 0xa, 0xffffffffffffff9c)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f00000001c0), 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
write(r2, &(0x7f0000000040)="eaa2", 0x2)
socketpair(0x1e, 0x3, 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r2)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
recvmmsg(r3, &(0x7f0000001e40)={0x0}, 0x10, 0x0, 0x0)
ioctl$TIOCSETAW(0xffffffffffffff9c, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x3, 0x4a, "070000001d0500007800"})
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000740), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443a, &(0x7f0000000240))
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x4)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
ioctl$WSKBDIO_GETMAP(0xffffffffffffff9c, 0xc010570d, &(0x7f0000000100)={0x2, &(0x7f00000000c0)=[{}, {0x0, 0x0, 0x6}]})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106978, &(0x7f00000000c0))
socketpair(0x1e, 0x0, 0x1a, 0x0)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
mlock(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOGETOWN(r0, 0x80047476, &(0x7f0000000080))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = open$dir(&(0x7f0000000600)='./file0\x00', 0x100, 0x121)
r3 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000640), 0x8000, 0x0)
r4 = openat$pci(0xffffffffffffff9c, &(0x7f0000000680), 0x20000, 0x0)
r5 = kqueue()
r6 = fcntl$getown(r5, 0x3)
setpgid(0x0, r6)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000580)={<r7=>0x0}, 0xc)
r8 = getuid()
sendmsg$unix(r0, &(0x7f0000000840)={&(0x7f0000000040)=@abs={0x0, 0x0, 0x3}, 0x8, &(0x7f0000000540)=[{&(0x7f0000000400)}, {&(0x7f0000000440)="50c4c97dcd6ef5914b8c087818beffd347a0", 0x12}, {&(0x7f0000000480)="205a3e67048e", 0x6}, {&(0x7f00000004c0)="bd8324701f38a01cc87be0f87ce6dd33823275a67f64918600b6ffd74792cc61b4b920a2c62765a09ed707cb4d66ed52af0ffebad633c984b17d59756806bd4cd2aadc857e81a74133684715736855039bf7e85c9d9acc2c4c9dd06e177a0ffc85b8b4633f262c56ed1ae7840a8c4bb42935c12221fef7", 0x77}], 0x4, &(0x7f0000000880)=ANY=[@ANYBLOB="2000000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32=r0, @ANYRES32=r1, @ANYRES32, @ANYRES32=r1, @ANYRES32=r0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32=r2, @ANYRES32=r3, @ANYBLOB="2800000000000000ffff000001000000", @ANYRES32=r1, @ANYRES32=r1, @ANYRES32=r4, @ANYRES32, @ANYRES32=r0, @ANYBLOB="000000002000000000000000ffff000000000000c8c1ca79bfe8814d08494b434ec6bb1c55eba3db0103142aa6a6339ecfcf02715a95f0acacebd8641e225126fbd1b77a0cadf810d022f3c30af568ba2e8b9c11099ae0e0fde8716a978b013e1bf306f1fcb146d32f0369e11d088e6452813f372124b2076c80fb44ffd3557f683fe86d20db059b30c8bb25a2ff023df4a27be14f6547440e0916d839486af44f3e276cded40e38dd1314c02d119367d8c9534956d3cc78ce3d4ed5d1d2ce981fb45b", @ANYRES32=r6, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r7, @ANYRES32=r8, @ANYRES32=0x0, @ANYBLOB='\x00\x00\x00\x00'], 0x108, 0x403}, 0x406)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r9 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r9, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r10=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r10)
r11 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
ioctl$FIOASYNC(r11, 0xc1084425, &(0x7f0000000240))
mlock(&(0x7f0000ffd000/0x1000)=nil, 0x1000)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000440)=[{&(0x7f0000000100)="ea79607ce36b5da45a410d4f0ed4a5cbe6e94557b181d05efe3006c430cb9457f84e8c877c142554e3f3672d36bfbb50859c789cb7807e6fb56bf71ecce46d2a62a0f5fbdaaa0ade5c901e87de8d476feab7b26204e3ceddb0b95ff94a5aba117b0a50796f6ec4", 0x67}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x84}, {0x14}, {0x6, 0x0, 0x0, 0x80000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000535000/0x3000)=nil, 0x3000, 0x0)
chdir(&(0x7f0000739ffe)='..')
mlock(&(0x7f000071d000/0xe000)=nil, 0xe000)
msync(&(0x7f00006e0000/0x3000)=nil, 0x3000, 0x6)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)="2606ef67f743de166e61398c3e652587176ae49204dd1d002884e6c5", 0x1c)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
mprotect(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1)
sysctl$kern(&(0x7f0000000040)={0x1, 0xa}, 0x2, 0x0, 0x0, &(0x7f00000010c0)='\\', 0x1)
r0 = socket(0x2, 0xc003, 0x0)
connect$unix(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="9a02"], 0x10)
r1 = dup2(r0, r0)
setsockopt(r1, 0x0, 0x2, &(0x7f0000000000)="62c5ef11", 0x4)
r2 = dup2(r0, r1)
write(r2, &(0x7f0000000380)="4d000000003572222b76d50bba28abd12122c05b", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x6}, {0x81}, {0x6, 0x0, 0x0, 0x4000}]})
write(r0, &(0x7f00000002c0)="bfa80748c4b3ff8918915dc05e7e", 0xe)
sysctl$vfs_fuse(&(0x7f0000000040)={0xa, 0x12, 0x4}, 0x3, &(0x7f0000000080)="efc409b294f2695689ee49fdfcf34efa2685e5cb32971fb23ffe75aa0e438a20008956ebd93a2cb279dba062411eea9ba0d577f87e4860e61b9738b0388227aca70f1a23234153f650bd6cbdb6827b9b0718250290582f7ac4cfaf6eef7c74b745ed7f7e69262ecd30aeec608aecc40bc7deaf5cf33a2c7765da3410105c769bc2bf0c6889ee3ececbda6cade16935616e3c61495f6b7b31634381f59d7347095d5a960cb75c4158d3d0596cac8d13a31460b20b0e904640d23e37926ada99e01c0803f18d1408482fee7baf7c31217e454a1a1ce164df4c7aab118f47f03554f56cfd3b49e26218cc521a66a35d6bc1edad429036ac8e9a333141e6fa1fe9bb5095b7d200bbc936109cfe46d9a2b47905ac322079aa54e667df53abb611180d7a9ec15f527dace32569253191002e402ca0a56ddf0947897467382f1759d397bcbd923633bd6519422dd93f1903f2ab0279348904037abdd2382cd0eced0c64239be1d9ec91e2868280119bdd72fd6cfc765db68067b180bebf0eb040095d2c4beec7b4236214fa3e441d077a3a2f6b89d7dfcf9ca694a857fb5a1903cda21764f1c2b2e5e681e87646a54e1a1f4f78315b007fd8650e022d2b22aaa80476c3f04a070244de2f1165defe25bb17c0abba62747b3fceafb34a8e5fe3cf1165a82f6311f251f4d63dee0dfe08eeb5c87ff336bb306764cafc8d8019913ba62c9bb4b3676d069a11a5d32a8f32af96c59c5e0b820acf34c821306ae9201d7fcf489f91cdbd3aca15e5eb3c633f51d216ec4e2a6784b52d145d97605d8780d6bc3aba8a69d727cf5e43019f67aa1c07ae37aed8184b3223f605d34000ece75172e16f0b2cc97c6d5904ab1b3529c33be833e9096613710c432185f82a1fdf6893fef6536137f0ac1a2b5b5e2250ce2aa5fd01c3e646fd8349309177f45589a1e6d77aa3fa0211f09ce465c3b8109c7f69aa2f8af4520b156146b826b594cb8fd69566b1da69a607323eb11a8cac966c0f3ea29fa6fa3c705594eb9aee0e8232b2836ac6cafd697e9c7a8eb73c404c0573d3e590ac715dcc2539816f029b7c36bf26da8942738ca9bd9dcde7edc51e4e4941fb67bf75fe4cdae4e40abcfa0c4976e2a561fc0f1ed77df80fab1965513efd1493baaeaf221a168f90fdcbab3086270c594b65b0671745e0afb1c700e5eb97a9e7fd1a9f83b6699174428561c905c2ffcc66ab1807fc5906a1d287da8f9bca788ea12ba573d153c0e3ffc7eb3bdc4962d1ab69b8911df0698aaa29bc1f0da8c09e9bc0404038368e92948e39aaa2c161ba93f91a03639d8b5d6bb60e597b4de950ae2dff058cad6f1c9293a5fea8e38c451163e1e3ed9be4c6c9c29307c8fcd20cc4685ad15787f0fec0cbccb6f3985b2c6954c95d516b94d909a16949b19d3c5a4a9667a4fd26f376e8d03f0eee1cd7c5a7439158dfb59c8026be1dbacbaef082da091bd89aabebc9ef9195ca9412398c026a04542d5e40c2e3307711cdf4314419dc3bddcd8bc8866e8ee977b7e62c77a4acffa3f857049d8e06ba60f9051a42147ccd153114d361c84302a612874c5d4f6a2139945c3cbc037535f88fa2210a4e707b4dcf1d8a708bf2a4595ee46a0a5b5f666f89b63ffc473f18be52c8978c382f1ee6a57b87c9e832aa92dc47634a0773e8109e7b372df93eaa4159d9703ad2de2c3df96871cc5e579d5fcc5dbcf47c77a96c5f91598a52bc57e1e6dbcd96ab24d4ed270e20ddf1e9a1fb9ac6f2256efb5906eb83e34e20119af75bf1991e13b0524da682fd0bce5e2a0b308817df544da85741e28cb72ff86cbe78984c61bafa4ea7b9256dc235ba89ad906115a15c44af220ad95d06559c2701dad3b4d932fda775aac24adab1f2b2aac4505ff80f1745b97a48197fc3afdaefeb8bccbd24150acbc832d874a02982f01f20dfa485870876d3cee1ba4f8e6151f05482b41cc5696e1479414778fe827e6f714782065e8e52cac82d358af144e256727724b80b81808619473cc8f3709fe65e22e3f2e996362c862a3891e818962a862f6065a7f69f7930f079b35b07b00458a6d6e5ebe52997ff0a435439e6392944ec5e2022eb7ac01f7459513ef8332b48ad78ff64b5f2dd9099cb06ad09afa45a658de6af3b923c7a1577dd5abb3cb9bcab2c182db3b5ea32df68c1dcb8a65819d9c60d53533505b0ff1f4bd4f615a205de5dc49bde3b429eeb3784c8c252a9636dd909b61507aafbd71dd122521b9935ff1e2efc5af8e03e9c537c1a3b644800aba98f77bc43e06ebf025002bec90cc2507300b66de98064b2fe32bde44e9f10a2baaea060a222bf98d04ce458f6632ae0851e201996cc543b2477f00f5073a0b6615f423c5cde924f9cc47469fbc983ea1bd1b7ae7ad78ab6d542825369a501a1365fbfffc74affe7f27c938f2a1d76e37ecb86e08bfbb6ecf4efe85397532563f5466039b54721709636f893441d944e48bd86927430001cbc996afc4afcc3d5e6a81d4b5b6d645a81fdb040e5f607a46d01cebfda0d73a80eb6879b151629f67e0b456bad1e5cff3ebf5f3b8404140e553eb6c0b2c65f3a51dba3b6ddf16f9b89829989064a637bdd2025c45c199b89dcd463bd008a8e4af82e6c19ae0c335413ce8049017add4a6ab2a7256ced60e000f637cffd266d26eefbbcc1366158240b6e4f5d270eee27e8b9bc6a74afb4b2fffe5ec36b7c7fc5ed6d74e632c917041383c595c359162fec16299ad3a1d298c679a5fcb6a5f812124c9f63548796410f31db8377acb0fe57ffc473738985a626be6836d3962cba3681dde4807fa02a004334f4f5043dfd64f27267c3089ba86953d5b3047e7457fb887adbeab795b806533f07188d783ce4af0909e3a91ea6c7cf3e5477e1979deb6a15466a6b26680915f76d390afeed5b6abd389f6dbcffdc6af16f02ad299f5378a59965c8f0732e78293081d13ab88c8fd4d7e1269ee89aee378b6697e32e828b29d469b67e296f8b10f5fa7ba0062752eaa9d26bb8606d40823534345a450c466a4e7d86c626ed54496cd4ae258c71c3f2d2633b3fb6e9a64c8aa0c6fa25abb17b0e14d81d8ebcb614666d66539c970fbf8f8068c935320fdebe7602f56aac06528de2d34c7c283d35e8dd2564fa62140a686f16d70942c59ec404dc5eae3115e31312842c3f54c71f0b58922efd071bd9d447aa5f2649215dcbc0493f61755402c2db63bffc07ac8f329296cd111b6ff176a56da2b089bd01badea2444d7f670373afe44b550687cf069f77c88a197ff94064e14fa53490f68369eff6f3dd44be558bc5d0b1463da3a7b6231dbfd54b0beab6f9163830b50a9d6181edf751f78c1ed4a4fb8491b83992f8cbf1652b32cb0fcb705bec2a3d4aea036e23afbcafedffbffc62376bb2ab2f53903399046b82ee5292951c64a314fb3233e877681579cfc3d3d7c938823052d50341d845d175d827af61f9631f7315f1fcb14bcb03296d9cb3bcdc04b259e038534c5f1c532cbf7a43b34c735f0c330e4a4b53f51fd541cac83be3779123c6d3f2034768ee8058c2c7362c3eb38a8a87d7571c6a7b010c9e5fae72a90fb62125d7782c789fefd563aefc8576aca846d88aa9df5dbad245fd3bafb1e1644079668fa19b4d44da77fd348b72146fbda29ccb32ff0bf76379bf040c4fe96984eb88a6ed5be64fab815d266d3a051bf0a8252b7b18475027f5a24bb21d8b70bc222bd9bed1a65b0170c8f34bc354d00db1749069ddcf4179367af2d9650122ab83f5015b7e197ae78d6d7cfb3ea15c21aedd7d308611aa04e7b74205c3948dd496cd6dec75073cec5bf561bc71b8700d96e9b931c3636fa6de8fa6d9d08474146e909102d89eb6adfbd95413291d86e88e14939859a686a1a9464169a0a0bdcce1e134d6bd16e7f1397d0a7e2489c41cc38cce17aff8e3fc9430e056f2e25319f8f1db74ef2b2a0b82746894e08188cd2d241a412432cb2cd2a9af2a24c3ac95856b3d840bf46723425cd545f17780294555c53dd3b13057edfb2974df39337a1634cbc8391be5ccab241e2a557046354534c80863b0e81187e51c2374e6954e823ef23c968839c7cc6344fc3bdb8d2a9bc470273e38a9ceb0541348524b24d6ac283d1b835cc981dd18c9970ef5f7a61473ebf539c3d8cf73523e2e427b4504d8d2fa3c06d21d95bbe1cd7a36799e2b3618ba643d6c945bc000f1e64d2af2030662db1e5df62f7e714fea92030dcf5e17dcb340be888bb66cf73b8b7fdcc9b63c1f472d9dc42875f33cdb5dce44b5b8ff15caab3ed290b7b40c54d3b7a6b917f1c7cda65d806ebb244391d9ba1156680af4a22740f35598a73531449993db680680707a5e785e8ba7155dfc9e84cfdfa4cfb7907a2acaa8ed0a98b8bd829a46fd37590ca6c131881ab53155b393c4be02ee8ce733b51cf26a0ad391d46427685a1dadf480029fbe7535e1a29a218fdc260de8d299515b7fc50712f5dea01c06912a660c74821321cbe4df855dc209c673775b3c277bc04340e1241981777ae32eba6146db886f592c9ac079335d6ced29034d138457f913370793eb06a636c1570fd68c0b986d0423b34cb53b1e37a37797ccb9ee9f6f66f96429509426b853541938325a38701923e8f3792ddcfe7793b94fce3e48c90f2c35419ff25c184b866c0140b27bbeebad16c9c68c28b45001aaf9910acc264d4aca1c615e2e26c4f99cf77bc5f6a05071fc495b611f7b697c9919ca20e41908d1e87ffe9c5b50b1c1d8a674a6621b0325f789bc42819e5ed3cc4f080589bdc5b8fa0b243806acbcafdd637b135dc09e66d39fb3135d32548c745fd2264c2f7267255f066546b714486391f3c601749ddc9a4d036c5fca615c566839bda4da001cf559bfbf903d1ffb3f4094570c486c685f0c7a89c8acc017f6c2c4f6c39af63f49be355fc9acef2c1c40e49cbe1ec6767b04cfc693d1d185c83714c5b5671e5837fc9f005ff4e3ccb4b25016b8204c7ec3fc37468ccccdc73f0e71f53207e36c621635f573e0d6498ef656a15cdd85f1fab0781890884e6340d13fad47d67eec9efc16f07f2cbbb3ddc95687394b989d355d206f2e8d3da0acdcb50351ad0c48ac88c8372bd5b888c4c6b68f48590d45ac464d4251134fc68c84e546361acf107acb4162fc988f25a172ac897345e20949d4c5358e6fb7b15104a36e68f568d498173847eec538d5fdefbd4a85d7d687c521fd8a35265be4fff30c1e91aa61a774530a8607a6c898b5630efbee2df4b64e4b0d52834f3f0e8726744974ffb3763bf0e1dfb13007ddc5ea43c796810f2b20ec5856eea5d2fc14efd936f73de3ce8970e26fe29962fcecd4ab97d8d6320338bd59610a25685b28f1daccd6fc612ea3dc7dafc06ffa872886e9f9648c347e2cdb4a93626109da97a17aad9a6f158c84e5b61e4e51df5e3accdcd81b4bfa32385437352823a3528096b7f5f7a044fdc785f2462881e156d8df36ed736ad0f53c26b5a436a26871d89205ff58588543874b38a95b9d99bcc7cc37600522c7e5d9405769174d9a3a1f327d79afc190ac26ab33f45b526853a8cdca9a0eb966651ef37958c3", &(0x7f0000001080)=0xf81, 0x0, 0x0)
mlock(&(0x7f0000c00000/0x400000)=nil, 0x400000)
munlockall()
madvise(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x0)
madvise(&(0x7f0000400000/0xc00000)=nil, 0xc00000, 0x6)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000006c0), 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r2 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000540), 0x0, 0x0)
ioctl$DIOCMAP(r2, 0xc0106477, &(0x7f00000005c0)={&(0x7f0000000580)='./file0\x00', r0})
r3 = kqueue()
dup2(r0, r3)
dup2(r1, r0)
acct(&(0x7f0000000000)='./file0\x00')
r0 = socket(0x2, 0x2, 0x0)
socket(0x1, 0x5, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000080)=ANY=[@ANYBLOB="820204c452"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
connect$unix(r1, &(0x7f0000000000)=@abs={0x0, 0x0, 0x1}, 0x8)
listen(r1, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000002c0)={0x0, 0x0, {[], [], [], [], [{}, {}, {}, {0x0, 0x0, 0xfffffffd}]}})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x00.'], 0x38}, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='$'], 0x38}, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x36}, 0x4, &(0x7f0000000040)="1a038947c0300b50dc08abd81062ad11", &(0x7f0000001040)=0x10, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
setsockopt(r1, 0x6, 0x4, &(0x7f0000003740)="125637b3", 0x4)
connect$unix(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="82022e2f66"], 0x10)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f00000001c0)={0x9, 0x0, 0xfffffffffffffc8b})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x808c694e, &(0x7f0000000300))
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000501000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282921", 0x62, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503000000b30000000007", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xd0584fcd7f7abe72)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000000c0)=[{{}, 0xffffffffffffbffe, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getpeername$unix(r0, 0x0, &(0x7f0000000180))
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000000029, 0x200000033, 0x0, 0x170)
getsockopt(r0, 0x29, 0x3d, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x84}, {0x4d}, {0x6, 0x0, 0x0, 0x3da5651a}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x20}, {0x87}, {0x4000006, 0x0, 0x0, 0x3ac}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x2)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r1, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0x1c, 0x0})
r2 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
rename(&(0x7f00000002c0)='./file0\x00', &(0x7f0000000300)='./file0\x00')
r3 = accept(r0, &(0x7f0000000080)=@un=@abs, &(0x7f00000000c0)=0x8)
r4 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r4, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0x1c, 0x0})
kevent(r0, &(0x7f0000000100)=[{{r0}, 0xfffffffffffffffb, 0x2a, 0x2, 0xffffffffffffffff, 0x2}, {{r0}, 0xfffffffffffffff9, 0xd7, 0x20000000, 0x3, 0x48c4}, {{r1}, 0xffffffffffffffff, 0x32, 0x40, 0x80, 0x3}, {{r2}, 0xfffffffffffffff8, 0x80, 0x10, 0x8fb7, 0x7bf}, {{r0}, 0xfffffffffffffff8, 0x73, 0x8, 0xe4, 0xfffffffffffff000}, {{r0}, 0xfffffffffffffffe, 0x30, 0x20000000, 0x2, 0x1}, {{r3}, 0xfffffffffffffffd, 0x30, 0x80, 0x43f, 0x5}], 0x0, &(0x7f0000000200)=[{{r0}, 0xffffffffffffffff, 0xd6, 0x80000000, 0x4}, {{r4}, 0xfffffffffffffffe, 0x20, 0x1, 0x5fe, 0xe}], 0x7, &(0x7f0000000280)={0x9, 0x100000001})
r5 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r5, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0x1c, 0x0})
fcntl$dupfd(r4, 0x76fdbf4cc4b34238, r5)
getsockopt(r2, 0xfffe0000, 0xbf, &(0x7f0000000340)=""/164, &(0x7f0000000400)=0xa4)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{}, {}, {0x6}]})
sysctl$net_inet_gre(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_INFO(r0, 0xc0185603, &(0x7f00000010c0)={0x0, 0x0, 0x0})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x2, &(0x7f0000000040)=[{}, {0x3, 0x0, 0x0, 0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x4, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0x68a, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2c}, {0x4}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
pwrite(r0, &(0x7f0000000140)="676ee3450356e770f593c59adfc7", 0xe, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0xa, 0x6}, 0x4, 0x0, 0x0, 0x0, 0x0)
pipe2(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
dup2(r0, r1)
readv(r1, &(0x7f0000000000)=[{&(0x7f00000000c0)=""/196, 0xc4}], 0x1)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x1ff, 0x4)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0x0, 0x2c}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000200))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x61}, {0x3c}, {0x6}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000140)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae27caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebb4257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd007f720fd3873babfbb770a2f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100000000000000100000", 0xb1, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000100))
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x3}, 0xfffffffffffffddb)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
shutdown(r1, 0x2)
syz_emit_ethernet(0x4a, &(0x7f0000000240)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "182130", 0x14, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @local={0xac, 0x14, 0x0}}, @loopback, {[], @tcp={{0x1, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
syz_emit_ethernet(0x3a, &(0x7f0000000040)={@broadcast, @random="8e50955b45d4", [{}], {@ipv6={0x86dd, {0x0, 0x6, "102269", 0x0, 0x0, 0x0, @rand_addr="a48d1985c5ac4b7b966b39a56cc756af", @ipv4={'\x00', '\xff\xff', @rand_addr}}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffff, 0x0, 0x0, 0x0, 0x0, 0x5]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, 0xffffffffffffffff)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f0000000040)=0x4, 0x4)
dup2(r1, r0)
r3 = socket(0x1e, 0x4003, 0x0)
sendto$unix(r3, &(0x7f0000000000)="dd08f1ee0200040000000088a812703f", 0x10, 0x0, 0x0, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x8, &(0x7f0000000100)=0x401, 0x4)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
shutdown(r1, 0x1)
r0 = kqueue()
r1 = openat$null(0xffffffffffffff9c, &(0x7f00000008c0), 0x0, 0x0)
dup2(r0, r1)
select(0x40, &(0x7f0000000200)={0x7fffffffffffffff}, 0x0, 0x0, 0x0)
dup2(r1, r0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x2})
sysctl$kern(&(0x7f0000000140)={0x1, 0x33}, 0x3, 0x0, 0x0, &(0x7f0000000200)="dc1c2b0d7c9c7055e400d23b42d372ef8cae61118dd9b8656de9657b2a816d6c0a4cfea0b4843f0c36b1dfc1aa02bdc78bce3c24fb0a541b6d148a21b9ffad1089db0d9851c6a268e23dd2c97863335a0bfa0a5276fa9004adec01501d2939190a7d08a58db2ee24105bedd3983b56e6a3e4139145db598e3795f9f0b6b48bd0c947954960e1d158c46b1d", 0x8b)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000340)=[{}, {0x2}, {0x6, 0x0, 0x0, 0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x54}, {0x35}, {0x6, 0x0, 0x0, 0x101}]})
write(r0, &(0x7f0000000080)="7c0000ffffffff00001a00a5f3fc", 0xe)
r0 = socket(0x2, 0xc003, 0x0)
close(r0)
r1 = socket(0x2, 0xc003, 0x2f)
setsockopt(r1, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
connect$unix(r0, &(0x7f00000001c0)=ANY=[@ANYBLOB="8202a6917c"], 0x10)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000040)="bcdc803d9059f55d", 0x8}], 0x1)
mknod(&(0x7f0000000980)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
r1 = syz_open_pts()
fcntl$lock(r1, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x300004004})
r2 = dup(r1)
flock(r2, 0x1)
close(r0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x3, 0x9, "e1937b00000000000000000000021000"})
sysctl$kern(&(0x7f0000000000)={0x1, 0x31}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
unveil(&(0x7f0000000200)='./file0\x00', &(0x7f0000000240)='r\x00')
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='c\x00')
sendmmsg(0xffffffffffffffff, &(0x7f0000000180)={&(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000540)=ANY=[@ANYBLOB="d00000000000000001"], 0xe8}}, 0x10, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000040)={0x0}, 0x10, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f0000000140)=[{0x30}, {0xc0}, {0x440e}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000040)={@random="c028a1b52dee", @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="3efd8ec01613", "", @random="f4716580cdf1", "79d01739c9643097cfc3e546281f260d"}}}})
setrlimit(0x8, &(0x7f0000000000)={0xb, 0x92})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x7)
writev(r0, &(0x7f0000002680)=[{&(0x7f0000000080)="d9635ca058ce063d34cb3d0d9580eb231960cb24699b9a9bf381d4ef99f4eccdec965912106ea543a1f143f3ad2fb08434bbe2db504378136a02d62cb9b95f7dcafeba45ec2d6e05f5af0eada018ded94cc7c5b93f9eb775100dabe81d57230899486e973a03f2a3983433c4308a903793570b44f23c76ea3baec9f2275d2f91eedda9686af6f8501d71053e16323a7ab8debaae74ee5f185058612e045ef9c6f86c95df99bd15e68e0c12cedb85e31d67c7500781d7ab6ea27bc39801d06e42736a4e46cbd7fb5da0497f65a7cbc43e2a4f878a3e17c7166a73735b86801fe13574448213af297d23294eb835b8e5c17d6335b6e9e9237b816c1cb995251fecbd18c1c69d4cf07b0934004df4e5f9186130af1238dc29d93b27956f85c8aa8530294efcd0f1c71dce6ae0bc986485a5da998f4430bb53107bdea49d1f970a11955c419ea3087aff9a32e56ca726e95c4eba2ffda49291e3a23f45c5fbd34c468c246575b817891980dcde98989be6f79d6f2445bb6f58465ab8c4784320460fb33eb2e3669be5a1df6f0730e6ff397ee5566349b5408df86d3525c82e2f0c226b03690bed5600b463751e425df58b3dfa033fd415e5b3026186a4d32fc6d0ab1a7835e3e5c5b6650befd2fbd4b0ab72e03e5bddae9b99eaddc2662646f6b267bff8a3ff827bd2325b67fb34cb980b796306308f26d213ddab437683a1f7ea2828ce6e49bc06df76e0ab6e0a652acb822b1710661835c312ea2af685322580215efb6911c0a67931901d5bc17afbd7e4d9f0de959afd206f19ddf3eb29a221292a680ee763e797da0872090dce2468da8ac642a220aef6b5e414ab83ff5716dc5c18092908610032e9b1a97d799039e0e1c678487fd6843cf3b51b78d21a6d8a80ec893c10dc148169b400fc65698714f43e64c3e79f497636e558827132bc2cf33834d32e4770867a08000000773cddac3ae20257b41bbaf4012d94526aa0745a5126476d5d534b3c6478486b63f995dba4086c773cd1fb584766bd3682799d993d6eeccc93440155d3bab6441eb7603580a649c7138523f93a7dc2fff7879367f27768595d12ad0ab803aeb18c958357e6f8b757ddade1ed6384fc8652d9838409ddc12554267ca4e6c8e9c0a73b0f2d046fa7e5722bee6f326b5a10c0cb0e41a382c907700e53f0d95ee24dcf64c61c4ec3f296848e3b41226fafe62f5f3970bc523104278d345864d0410a2262e58ada6e11461902423bbf110b157418719da20b5d95d710379f1265197ccd6dcb2f25fd48599e1bfd7398a950800267ef19491168cac42a971c197bad683277c0d95328b96aa136efc38269d1a058bb6f3e353bd0e0c52ced519811ca756b02ddb699a6f429f08c8ca908481a57ecd3132273dac84e96a336bbf883f0a1b8a20c193c6f8af982e573c31ef4e8fd2174b81499f5c58b24081161f33dbc9614dc3b95c9791c3c8823e2592531389197524ce962d5cbb22b8e5854cda2e0da34ed726d61cb44e76b4ad9e464de6ce842230d41896b6bdcb00a3767d3389be93792deecede6f94da968f97b19a8cd2b09d5433dfdfa32fdfcae59be6c398016fe65234b5ae649b6364c0e5576603952d1c8dc161bf7c9d2c114d92715afefa1af7139e0b780fe49bf7a3261ca7b7301dfb505a63898075528c284d7fd5eecf3a0b15da5fe5dbcecc91c4173783284d7719378f3c304d236e4316fd0a549e1191f2e3fab32ea5748b3df9978d1849b068648175f1f4ea6f08e5a13a09e47acdd3e8b1c895858334bc0c96062ae30b8c3cfb91f33194a8aacc57500129393624bc515f922507059d6ea55d69072099a2359de8fa886bb45630404723086d12df164a8ec1d7735ca39a509d3244741aa3b88e9ae08f6ee0b11cca6c03274c33586fa2c2346ca6256128a9fa3598ee8d3337e61d40472e495379271cafad163563f575f5673b17fc75a5ac18d2c2a35b55110aade9bbd00000000ef81e92c281b0c9dddb2f1f41d5f20e18ee0f6e086c2dc3cfeaf830b926fc0af6cbeb3dc6381886e58126a0bbfacc94265a6d153692b2e8be46403a646ad441bba43a73a561e5261d44beb39266d2ee1624d553291cce2b790802f59eb7facdeeb01697452773d5c68a8440c8bcdab4aefc7af452ea4659bc123f77657e57add5c98ea3b90d965bb4dcdecfb05bda5ba42f92ea5c414c6aed97ae885cbe0409885bbcf07e129ca2dc657e2f0349626c04f2126791cf597220ad157377acb25eb0a7b3b35dbaa3bc3d48ff8fb44e4cfeff2d310896ccd1e6415376b611587f77d00c72a46ca0a97d95a00c65da2b04f9f495f184abfaf68e82d8371b6242adf6bb520465cfd3f5becc886bdc4ee1947ffcf0b836ab7823fcbcc4ae8965f4c9fbda8299f87c4420ea31ae05c47fd14bb8795edb921ca527b60960867e74eab847b536b130f81d9160ca7ef1571037443d6dc9e4569ba4575efdbf43e38d0eabea2c497c9883866e891f4c9b4b65d34a4c992e8a4c68c526b965ac8ab333b8e67803b1e22a994ee4089beef35f6c45e803ca8a5ecddc7100fe274e514c434efd26dbfc8eca48e4b1bf7b83109e48cda14590ebe2af0f5512b14864e0d", 0x741}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000300)={0x1, &(0x7f0000000080)=[{0x4d}]})
ioctl$BIOCGDLTLIST(r0, 0xc010427b, &(0x7f0000000080))
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
unlink(&(0x7f0000000140)='./bus\x00')
sysctl$machdep(&(0x7f0000000000)={0x7, 0x10}, 0x2, &(0x7f0000001280)="885238b598ff40ef", &(0x7f0000000040)=0x8, &(0x7f0000000180), 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$BIOCGETIF(r0, 0x4020426b, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x4}], 0x1, 0x0)
writev(r0, &(0x7f00000005c0)=[{&(0x7f0000000340)='\n', 0x1}], 0x12)
readv(r0, &(0x7f0000000740)=[{&(0x7f00000001c0)=""/197, 0xc5}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x40}, {0x34, 0x0, 0x0, 0x7}, {0x6, 0x0, 0x0, 0x100000}]})
pwrite(r0, &(0x7f00000000c0)="00000000000055000000009c9f00", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000280)=ANY=[@ANYBLOB="6b18"], 0x1c, 0x0}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x800000018, 0x3, 0x102)
r2 = dup2(r1, r0)
sendmsg$unix(r2, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x49}, 0xa, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f00000001c0)='./file0\x00', 0x2000, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$TIOCOUTQ(r0, 0x40047473, &(0x7f0000000000))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x1, 0x0, 0x0, {[0x200000, 0x1000000000004]}})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000040)={&(0x7f00000000c0)=[{}, {0x6}], 0x2})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8020697a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000300)=[{0x50}, {0x7}, {0x6, 0x0, 0x0, 0x80007ffb}]})
pwrite(r0, &(0x7f0000000040)="f94c4c4836eededf016fed8d1a02", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x4c}, {0x5}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000140)={@local})
semctl$GETALL(0x0, 0x0, 0x6, &(0x7f0000000000)=""/21)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
r1 = semget$private(0x0, 0x7, 0x3e0)
semop(r1, &(0x7f0000000080)=[{0x3, 0x2, 0x800}, {0x1, 0xff, 0xe17b725e3710487a}, {0x1, 0x8, 0x800}, {0x3, 0xffff, 0x1000}, {0x2, 0x1, 0x1800}, {0x3, 0x1, 0x1800}], 0x6)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000180)=[0x0, 0x7fff, 0xff, 0x0, 0x1])
r2 = geteuid()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f00000001c0)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000200)=0xc)
semctl$GETVAL(r1, 0x1, 0x5, &(0x7f0000000300)=""/213)
lchown(&(0x7f0000000140)='./file0\x00', 0xffffffffffffffff, r4)
r5 = getegid()
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000001c0)={{0x6, r2, r4, 0xffffffffffffffff, r5, 0xf9, 0x80}, 0x2, 0x9, 0x54e8e19f})
semop(r1, &(0x7f0000000080)=[{0x0, 0x401, 0x800}, {0x0, 0x5}], 0x2)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001440)=[{0x5c}, {0x74}, {0x6, 0x0, 0x0, 0x661}]})
write(r0, &(0x7f0000000180)="3338f1a4f1584c82cf26e26aff11", 0xe)
write(0xffffffffffffffff, &(0x7f0000000080), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x1d}, {0x44}, {0x4000006, 0x0, 0x0, 0x7fffffff}]})
pwrite(r0, &(0x7f0000000140)="56f33a873743d1b828b0c2d5fcb9", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000300)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x87}, {0x87}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000400)={0x3, &(0x7f0000000140)=[{0x4}, {0x3d}, {0x6, 0x0, 0x0, 0x102}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="e16914f6357e3a00000015000000", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000000)=[{{0xffffffffffffff9c}}, {{0xffffffffffffff9c}}, {{r0}, 0xffffffffffffffff, 0xb1}], 0x0, 0x0, 0x0, 0x0)
bind$unix(0xffffffffffffffff, &(0x7f0000000100)=@file={0x0, './file0\x00'}, 0xa)
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0xfffb, &(0x7f00000002c0)=[{}], 0x80000002, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
mknod(&(0x7f0000000140)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000001c0)={&(0x7f0000000100)='./file0\x00', 0x1f, 0x0})
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f00005a1000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f000055f000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000fee000/0x12000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f0000558000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff0000/0x1000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil, 0xfffffffffffffffe}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000ff6000/0xa000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./file0\x00', './bus/\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus\x00', './bus\x00', './file']})
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f00000002c0)=[{0x4d}, {0x1d}, {0xfefe}]})
syz_emit_ethernet(0xe, &(0x7f0000000140)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x64}, {0x48}, {0x6506}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@local, @random="9ba8ed10ab90"})
setitimer(0x0, &(0x7f0000000040)={{0xffffffff, 0x5}, {0xffffffff}}, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chroot(&(0x7f0000000180)='./file0\x00')
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
unveil(&(0x7f0000000080)='./file0/file0\x00', &(0x7f0000000100)='x\x00')
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000200)='c\x00')
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='x\x00')
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202000000000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{}, {0x3}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="01080513600000000000ab000004000000000004fecea11ea8fef96ecfc73fd3357ae2ecaa0416fa4f376336acf00b7804be781e2fc2caab610f53c2297be1aa5b23ed00f4c8b2ca3ebbc257698f1f132e27acb57ad602000d7d026ba8af63ff37281818e4fd89030000000000000070c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000005000000020208a371a3f800f10000000000000001000000000000", 0x801, 0x0, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="6b18ec"], 0x1c, 0x0}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = dup2(r0, r0)
connect$unix(r1, 0x0, 0x0)
sendmsg$unix(r1, &(0x7f0000001700)={0x0, 0x0, 0x0, 0x3c}, 0x0)
r0 = syz_open_pts()
ioctl$FIOSETOWN(r0, 0x80047476, &(0x7f0000000000))
syz_emit_ethernet(0x13, &(0x7f0000000080)={@local, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="72d30dce3e99", @rand_addr, @empty, @multicast2}}}})
msgsnd(0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="030000000000001ac5e9467476d48db0a9e660dbd3"], 0x6d, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x1e, 0x2}, 0x4000000000000006, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000180)='./bus\x00', 0x80002005, 0x514)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x10005, 0x0)
ioctl$TIOCEXCL(r0, 0x2000740d)
open(&(0x7f0000000080)='./bus\x00', 0x606, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x61}, {0x35}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000000)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = fcntl$dupfd(r1, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206982, &(0x7f0000000300))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x89, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
ioctl$VNDIOCCLR(r0, 0x80384601, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0x2, &(0x7f00000000c0)='./file0\x00', 0x8})
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a096524f374b7934364f461f145dd11e7997a2c9d4", 0x1df}], 0x1, 0x0)
writev(r1, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0x35a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x200000000000a, &(0x7f0000000000)='\x00', 0x1)
setsockopt$inet_opts(r2, 0x0, 0x200000000000d, &(0x7f0000000140), 0x0)
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f00000001c0), &(0x7f0000000180)=0x39)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000200)="6e3483", 0x3}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0206925, &(0x7f0000000300))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
rename(&(0x7f0000000280)='./file1\x00', &(0x7f0000000440)='./file0\x00')
setuid(0xee01)
r0 = open(&(0x7f0000000340)='./file0/file0\x00', 0x301, 0xb3)
chmod(&(0x7f00000002c0)='./file1\x00', 0x92d3d8db0df24e69)
chmod(&(0x7f0000000480)='./file0\x00', 0x3d2)
writev(r0, &(0x7f0000000100)=[{&(0x7f00000001c0)="5f70b1cdb62bafc401ba074c43be48b2d8aee9e6fd55ec5d759c3a01d8348c420091fd5cbca3908c694b304074556a26181f3982c6b2e4b6b22af431f8287695dc2d2a5d", 0x44}, {&(0x7f00000014c0)="1ef3a00870d5750595199d08aea187e5076e8cb3a821cf9a40b475a908366bce4c265378384b6ec3bc1cf4e75a6434765b475a5b6ebe459eeb425746b0b96b14cbcc6b7ba187824848b1d19a3e1781694e9338ba7b37a40a44be4968dde02fd2e307101763daf396a026a96124c49bd8d23f783665314c7f95b73a92bde4267106816a5f592908e7413b704437644c0bc3e78b39c9cf37bc9682cdb1459c1c9ba45034adb788dec42827a8841002445445feb491ce43656c950baa38086f60c9142032984d", 0xc5}, {&(0x7f00000015c0)="96043dbeff8dabf5cea3b62f1faa83782a4afce1c9d1b883fd9eb40a43e500b58bae10e9c98d3c4ce198d01750445c6655b0b6df04df0609bd63b8c9bb636d6d2df8ba2b0d8963bb4ff74a0fc6790b4466a6ba0ac078a71636024a6c76d8fe580670ddd060", 0x65}], 0x3)
rename(&(0x7f0000000080)='./file0/file0\x00', &(0x7f00000000c0)='./file0\x00')
r1 = semget$private(0x0, 0x1, 0xb)
semctl$GETVAL(r1, 0x0, 0x5, &(0x7f0000002880)=""/205)
r0 = syz_open_pts()
write(r0, &(0x7f0000000040), 0xfffffec2)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf80fc7b457dbe24125f76e25dc0ced443"})
writev(r0, &(0x7f0000000700)=[{&(0x7f00000000c0)="87", 0x1}], 0x1)
syz_open_pts()
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x40}, {0x4c}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwritev(r0, &(0x7f00000000c0)=[{&(0x7f00000001c0)="16e1f3eb254cbe285d574262", 0xc}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
clock_settime(0x100000000000000, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x34, 0x0, 0x0, 0x6}, {0x1}, {0x9e}]})
syz_emit_ethernet(0xe, &(0x7f00000001c0)=ANY=[])
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x6a5, 0x1fc80d8b, "040000000e020128f6a708000000bff090b0c7db"})
writev(r0, &(0x7f0000001540)=[{&(0x7f00000000c0)="4e010412", 0x4}], 0x1)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
sysctl$kern(&(0x7f0000000100)={0x1, 0x55}, 0x3, &(0x7f0000000180)="206bb8f92f2d62ef2869d90c4a2d9ec616be3313275ab3965fcdbd7b540d53471d2a8672b6d57f0a46f4318383dd99bd32adeec49488a9e7d72a7e09a86337e16b9a4293944fa6bd1770f952071fd5d04a87218f2af8096a56069c210395a6767cca5c4937e4e51e156cf76f763e266f6483f4f8731058da62c01d888efc6df9c0ce10f7e4c3741dadfeb083f8e56afd1cdb4e4eaf7238f63a5aa2ec853053dcf46feab1a818c2c08e08003cdd9949ea4fe45c258f0b6c274ef37e7a65ab640b4ad1", &(0x7f0000000480)=0xc2, &(0x7f00000004c0)="edbdc371fbd5d48f7e35c132e77905b5841db7eb8054a397b658d0bfbc4ce9349f53eed52294f49abd4ceefe37d709aeddcbf3542acd24", 0x37)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d733c4f6ff45e400"})
openat$tty(0xffffffffffffff9c, 0x0, 0x80, 0x0)
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)="1e6695d12f0f12e1253d2698bfab4e8058e4d72a94b5a55179705efac528870bbdd4cbd36922087edb155d8129988b147ac75de8cd2ce1453e7000a6e4505f1f39932d4d84a91e658e113bece7813cc2f4b79873559d6199e0ee816389c28eafc33d82f2ea0d6e0f4ab10852bd71ab5966580e6ad5b5aaa666f0f423fb3792562d52f020b013ce20df52ef21399a8ddb2e4e0f", 0x93}], 0x1)
write(r0, &(0x7f0000000180)='X', 0x1)
close(r0)
syz_open_pts()
fcntl$setstatus(0xffffffffffffff9c, 0x4, 0xcc)
write(0xffffffffffffffff, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
socket(0x0, 0x0, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xb, 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x5300)
r0 = kqueue()
r1 = open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000040)=[{{r1}, 0xffffffffffffffff, 0x11}], 0x800007, 0x0, 0x0, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f0000000380), 0x7, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f0000000080)=0xc)
ioctl$WSDISPLAYIO_SVIDEO(0xffffffffffffff9c, 0x80045745, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
openat(0xffffffffffffffff, 0x0, 0x0, 0x50)
openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x8000, 0x0)
getsockopt(0xffffffffffffffff, 0x2b, 0x81, &(0x7f0000000400)=""/210, 0x0)
openat$vmm(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ioctl$LIOCSFD(0xffffffffffffffff, 0x80046c7f, 0x0)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x84}, {0x28}, {0x6, 0x0, 0x0, 0x400}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r1 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r1})
r2 = msgget$private(0x0, 0x49)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x1436, r1)
msgget(0x2, 0x102)
r3 = msgget$private(0x0, 0xfffffffffffffffd)
msgsnd(r3, &(0x7f0000000140)={0x0, "7aa59e80105a6dcac48b402c590b73a58d10b00dd8bfd86e8d8323c1f2c364e5ba8363050598409e73f288d725e07f90e99fba521cde1767fcfda2bfc606a91874b26129ae6e450ec5ee01624f2d4c65c29b0f239d1152c8a53e09153dea1cf499002e4e8843b87e23d4967273d45995f2ff211ce9"}, 0x7d, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f00000001c0)="90c3fe67eb586898600425f2f58091bb8110b2ec73e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca04ab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f9", 0xbe}, {&(0x7f0000000500)="3e0406b7a02899c8e5d87432", 0xc}], 0x2, 0x0)
msgget$private(0x0, 0x4)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x200000)
r4 = msgget$private(0x0, 0x440)
r5 = msgget(0x1, 0x200)
msgrcv(r5, &(0x7f0000000280)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008100"/137], 0x89, 0x3, 0x1400)
msgctl$IPC_STAT(r2, 0x2, &(0x7f0000000340)=""/171)
msgsnd(r4, &(0x7f0000000100)={0x0, "1d49f9fe786824ebc2c905151624"}, 0x16, 0x0)
r6 = socket(0x2, 0x3, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$TIOCFLUSH(r7, 0x8020697a, &(0x7f00000000c0))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, &(0x7f00000000c0)}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000004c0)={&(0x7f00000000c0)=@abs={0x1, 0x0, 0x2}, 0x8, 0x0}, 0x0)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = socket(0x2, 0x3, 0x2)
setsockopt(r1, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r1, 0x0, 0x66, &(0x7f0000000000)="ff3b04b0ee2e0013daf9db18617c9c00", 0x10)
socket$unix(0x1, 0x1, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x80206982, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0x2}, 0x0, 0x0, 0xffffffffffffffff})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fa090000001e328a1811", 0xfffffffffffffcb5}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSTOP(r1, 0x2000746f)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000000)={0x0, 0x84b, 0x403, 0xd, "cf11050038050000000000ea3cc0a48000000800"})
write(r0, &(0x7f0000001200)='\r', 0x1)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000100)={0x0, 0x80000001}, 0x8)
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf81fc7b457abe24105f76e85dc0ced443"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "a05d1ead3e531ef43b59ff65be755bc2ca0c93c5"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)='x', 0xfffffefb}], 0x1)
write(r0, &(0x7f0000000140)='=', 0x1)
close(r0)
syz_open_pts()
syz_open_pts()
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x1000, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2, 0x0)
fcntl$setown(r0, 0x6, 0xffffffffffffffff)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000780)=[{&(0x7f0000000500)="dcd0700650c98e21a132bd63ddae2c5181c3b4ec2e87441c48a6b932c80a3bf03dacf1cc134222bc5caf6c8faffed8e720f0fa55d1ed82c49e822191b5d727f1339d0e97032b6248fc9812ac7a826c90d96ba2e63cae5e19b456e2cbc37575f1", 0x60}], 0x1)
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000340)='./bus\x00', 0x3a0914c44f7b202d, 0x505)
ktrace(&(0x7f0000000080)='./bus\x00', 0x5, 0x122, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
execve(0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xa, 0x0, 0x0)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfee6)
r2 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x64}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069dd, &(0x7f0000000300))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
setsockopt(r1, 0x0, 0x0, &(0x7f0000000500)='v', 0x1)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
r1 = getuid()
sysctl$kern(&(0x7f0000000000)={0x0}, 0x0, &(0x7f0000000080)="32fd191b9b14ccb94532657dc5bfc25894b87210865324a41ab4cc2e752587c03812949d9df4f38da7f7d505db3b7e479b6a6fcc5b9c3fafdd308f3c14cc5f5f5a9be99710bfe03ee6560b5622a289835810b8317f8e8d8c2e5a026de8365afc365b89dc8d4ff8bf82f84ce2bfa0f9915997d375c80d57f7198728079144afa831a73892a1d4e1a7bb85cf1f76d4b83a403fce9ac49720c34986fb4f695874a2eb86c4a53d20eb96f61ef17becf0cfdd35e44da4c99bad70284818338623565c8d7aa8ef6163cad43918511fc08673cb7a9dba2e8a1e86f4589cbfd87c025d61eaaf80133c15be105510d2", &(0x7f00000002c0)=0xeb, &(0x7f0000000740)="b873d1c4c5cf89fa65dce810f36e3e40ea96ccf5d6d8ac72460880e56e9366a36d1b57c7981d3458eaa8d750b68946310904b6a3867267854e4bf7fd96cefdf989c98971c478307c2cacf716d80f2904b07ecb887ff89340c986bfedbdc6cee08a16d8ded3f2216676a797e623bea6cefa32694f9cac4987a82368fedd50175cd35dab01bb91cbafb62715d25f4b8bd76ee320cea3dcde94dfe3015bc7eb1fbd82ff43332eaf7c474a524523ffe43b55900f7b28236d179451bfc12cb35a4ece86049c33bf9a31333268bca8825d75f0bdf8455535cfc934cb43355eae9a75a16e562cc74bdf5ff81abc84bc4449668c0e4e2fce5c9014f9273c9f6e56df979cb35e7aca31c5ed32ff0cae8032d581b4361e4fcb4b7c9ad79471553c873ad5d7517560b45c6f5816db7c84e47ae9dae23fbd2b49822bd57f3cd32ea73554c9928761409ba8cdabc77ffffad71bbe4ae247aa184abad0514e3edf21b18e212a0429d07438d5327da36929369837d0e1ba138730f379e7bedbaba80d15ace380fe62b2a579d5efd1eda25df0977302d8d794e6f1499eaf855857aefc1aad9acad373d952ec29401b8d7110723d70944b7f181ee8da43ed90bb2ea15d9b45a16ebeaea0c8ada7b2fba4c2abc22e14eab54b04e18d79c00f92f0b52bb59e682356064a55cbbb1d4227c14d526c3df0f34a1dd3158ebf6820c007b49f8ca3321802a0cc4d1cca93f4b0d742ae37efd8ae543a883a23200851b083281efe296d23a45eea1b6bf395e72a0b00dc8a88c2aa903164cb1d4f16c0392c50f4cda3a5015da65cdc775ad4a1b1c9d7d993062438b22a7d36e253d8be8cbb1d90d29072ea840255196fde1943cab62a27f0151fcd61159bf6c64a1082a5c3acea0ea42c9e744ed56b4ec811ae40a98483f9c49ff2148a0319562bec3c751f39995b876670fb61a11e57b6c1cf2cb1044fc4d52ad69f7a513e61d2e5bc80e41430fd30043f75d596f0fdbbcd65530663c5878159eb6f68f018ef6459f13aa4c82ba99f7e2b4789a8b0e3490b8cbf4918cbfd14eaf3fbc4ea6a236f97fcd606a44fae34280fb0cc1d1648bc6fe5680a5afac38b886d6d92b047764037ab88019703daeb49277dda3e256ee3ba3984e2ece5c78186e31362572f70a1c25e1bf54f353b3823384cff879193692e76b954e9d4b112ec0b8d6bf48babd3eae89ad9057748e9e6bbd56589f8011318c6f08cc8cb110465f39eba20a5e5bb5ab41b5f288063774ff5a4b38bfbab23ba6999814030ce898d0680b30c01b5d1f3e72d7d7db22a2f1969859beb44b4fff99704f41f33717645b052430cb9681286131d6a91260053f655dbc94bc67636046360ea0d4ac93501e6f9895dc6e476a38c41ffaf70db3465daead21b9692c5542adedc01b9c31da15702dc709af981f891e417f714e6aaafb5c66e9e3d574cafb28a4816178d1fc8748fc8eb05f18ca9ffa196abb795082cf72c1c3278f41ef333504bac5932eed6ff7282bccc9f2ff24d2a3a0ea1832fa4dce95db722ea8ee4e9206948bb722b2b6d5620141550fc89ee7d6ef47577e32aeeac5ab810553fc79ac0dc873d6620983af55d9fad190965c94a18abc7f57e270a5289d58c987aa284c01b3cb2082622e026097fa84ac1e4f495e9750ed3738aeaaddc90835b8a72a5b0055710b94e68cb9403d2ce988cbd85532185f2c4e361c9b3a85c7b97825651c94892823b1f21586a76c0121087b9a9270cd41ceef5d47534cff5ac8ef524e435c582a9357a267964eeeb71c17e02c94a17765c402a8d9cb8a3fac850bb502443e966bce910814f2f18acff0f3d0930969e791448915f3e4a25b9e8c1bef2fd06045c3339eff49c601c3422dd4e0f2dd7187756f9b1191d891a329f15aabbb19742050cb56e23a0a1d4c8da91818bbaf0c5b9f6ef9b3bdc3a73375dd2c0e2f1f9d768b701005198815ca12f518dea509c57e06128b047161434830d90d443e9ac36fe72487b613b6da9b8462d5bd9be790f5078eb2c5e6fdfae9b121179379e487fbd2620bf7f1855a1683b5745720c7eb67c9ffa9f0a3d311dfbd4e2b052ad111ced96f729d70083d34d4136f23671b1f1c8f5f98a14c60cf5716b36caf0522a683ca3a57bfc542941d1aa7e82dd5261b277add84ee4ab155033a071ba1506be6a4be7d385dbe0e0c45d62dad6ce6e8fd40f6610e0655c187bde233613a0691aa4e94349497759c7a7ec9d5f644a33fdb57537679ecadf6f7bb2c6e83279ee0ad9d23a739a081cf9f9b42c210e3b9fc88e56ecb66523ea0e3f111f274134150307dc4616b80fcba4257828ea7cbf8387a48ffec02ef77db4b4d5964d44c03a9b48b1124ffeb0b998d101223c82216d24bad782a2ed584b9924cca3d51145f928c1cac3a31699b58ff4c4eb0e9b68b50c3cf5350a4bae2b720eb2bd7a1fe033e906f3e3bf93da009bd0cb14de47799dc6bce3e2141f342989595fe6bcd876f68deda4d11c1b6b3bcf8b9ebc2d1abe863ca0760f163066a931b9c42c34f0288d57b8c331fc411a78aee81fcb2c2e3f6928435c10cb344e8e893c40f5e4312897b721574b863139d0d2d88eeeaaefbdc775eec1d7381dbeac29bd547b93844880f87f5faab28d182fce5aed7444eafcc305813612650137c0c0f3c204e772327f892f4a40fc37b45302ba3e215c3071cbb37c1b25d875f659f15e0e39e7dc7ac587e3d8c8eeb5ca579ae8d6b3e55c729853139faac3691b39d4cb7016ccf734b8af2ac68df0c58df91f61ff5e2560cc35bee1b8ba5edff3d428c65296abf18c618c74f4f6355be33088143d034a656f7c8ac9671c9407a9ee341ede5be8213026d3c42e2b5d765aa1d242a08d419597f006de658ffd7b8b4aea6b9daf3665b80705f9c697cf9649bb4b99f51c72a7fda744d457bed0067f4b96aabc017c7fdc518515d95339a235d262d77e76e71ba3d989801750d69bf745c64344c4b99b42e72fb29167533572e8edfa67d2fd70d6aa32702d37f996392d7f01fa5e2e5af1bb2e5169099740e117851e569cdbb5e1c25498955b216293cdcfe0de7434fc998c6f9cbf646e50cebe01ddfb6d9e3210777bfc1b3f67b8943ad34dafbee65e4a7d71254e1245cc86153389f891250897fd4706167df314af9b393180279f5975cf3c9b28cf726eaa97f9a1ef3c136b7414061c72b4242dd998e45dab8b480ba00bc53cdcb71a44c142bf36401bde0a8e55e0ab47d019b6c1f4c69ee5759e868ebfb2d0a109a697301a3eaf57ac4a9e1ac544fb2ed1e27ec1bbc9fd57703c53dae6ad38b81751aa061a1c61bf9a48f61ca30e69f8ee726ecbbe2bc1bd3a4f5d1230c72e52346c41d6c7d0650071df78217e87a6c1a897b9dfa3a1b2269cc226fe2e63f6a12979130ee8169fcc278d6ec419fe8a61343ee6cf6a53c5133ace044d98e4abe31b8c5a3366a65ecf8864f2c40f7a3074f8822dfdf77b56588ff2e6b208f6daa7cdcd7fe76394c1c1f4a9e22150fea838b3137c8c4708f58edf035787202c924713b4a768b1567852dc571125294c58c0014569c89bf608dad7198d31fcf8dfe46069ef847381bb1190e8baa3f31e64e79f21367d950e304c0d1312c33e80a7393796a6b0e155c6fd0bd66abc4da06b8b00e8996b58e19045039dd700a73938f1632dc6ae4ad00701a2d5abd065af15544c1efb993b39fbbca7bc791cf0683d955664cd0f9e01cb4effcfe64e180f2cbe4faff78bc1326e1536d6988027774e3d913ee0752a3d96cf9943bfcd4303bbb4722ff85fa492d2a049a769c0492fd8de9bd673b83141d23cfa5671ded719fcfbbab6849ffaaaea228923a7d9f4c10c97c72ef415ebf2e32856ffc891e5792412a06407ad67648f1ff04e9a9e4fa267b1cff4f59312655e926995e41adbb9c2ed68754875379234ed87d9f19d94c60cbd2e521328ca79f17679092904acf8131f4c19fe2958c1307af7ee323815196c772c6ca74bb11e06c27216aa100a4496242c4c4542e913d6ed9c654c3af9e8c83afe6f933165f9d1cd596e12eb07c3acec1591065e93a87b9249ad75b795a28bcc6e91e03e597179169ab24acefc9baf0eae0c26c1d63de741ed865ea500e8b03664e7df12ca70891a5fd5f15dc422a620200f009c3f2895070bed1ea67b7bbbd2051d36127b05526de83fc40cceb328c7510aa231e4f2ff0c8b5868a2a5066b3068fb8d4c705faa5c02014525619b83b52f810d98b69e1880eb7cfef7f4db1a4d8bb473f0c30631b7ad5e12b49dda049cefb6c35fe7d87899606ee37dfe879ddd7ac3a9ae1b9561bd51799e0018c53a053f5c4f0ba420d2816860bc31fb879fe1edcb0061be19822423da108336bd51208aa9f8ee54a6ade6c5ca9ecb3c9c8ba9d9d4b4f7986266b344ae8a758751a8ca7f68372e276dd4a8e6e69f0ee3e6a70c5605167ae04eb90b7a045169d6fa892beda50f764c8abe8729ad75894ea4d5e6a455bc08179c3af2185842ab466b74b11ffe5237f45bb68db837d69897fc140a4de7be4a6c462937dcb7bd845690cf08f101b7724b77c2f929615bb4750b487e2b1e194033b041fedc71a680b1d2c4bbe060874f7fde74e1329b826c9dafd914f310f638d64d6eb2c4aa4a41faaf908d7c8313188395a77a5e161f5ce475b8a072636ef4a1b91472efd24cbb97fb835794fe63a35905b8ebe0f85393c2255d023334713bfd8848288131f1a1ab1f9a4045f47868c64e397273bd7960269e6f21147f3c0b53b338167caae47a84ff8af41f2149eb89cd4fed8d354b1f3b930fd71c939002a819e44db10feb4c07f706f425542e6e3b10c8b1f9ad3c66834371b59795f1c094534c309ed4bd80cde5c80233ea97a61bee89d75764933b930ea67220b22fb831c5290ffbca23dd5b43e82a9b534651f2e7d873e4526e5326e25c3e99d23f10e3c58d5cac743058502419f68a52e0ef7d1af12f843a17a669620829a3bf7351bf09f9d4b723f9468e43d0f924f4ad36606b54799fe251c5bcb68296aab3968f1ed5038fb92323ed0f0c3d0aaa159553e7a1c69fca5774f2c2022ceb52224bd3030362bbed261eb42e45da8d8341cd28da160833a57f28e57dc56c26f4b8e53c6ff705e6d18672163a2b3a5fd7d7df24e3cc4d99856028e30122dc04b7c9d06481eb8aa1672b3664e575994e336c9f35227a9a4dad6d41e6d3a7d8f3bf26b6ec87be35eb8c0a317324d463fc5e90844666413a11fa3ca0fa1d598fa1d6e7e12f0881a1cd85e2d91a8d3cd926da58e15726056d0ff59bd807c403503f2fb1ce269facf3537836bc454daf0e4ae3a8488c9aa24db70a21fd7ffc250bbe4b475af88bb80c0987c7e55d12dd142727f5f767ad7596b10c8dbb9042a24d1de0e33f197c8aa231305c67a7558cbf74976125e206e0cf2cf115d798f9578a6829c455c80328a0a21170d825c486be0c4cd971cab277e594566f92bc64075ace9e7a7aa37648c9ccdef0c123950d53e9aa08d168d1167264fdf0e2568cf3cbadc246cb81def73e3c6bd11cbfde847971021a6c296a3ad66b9abbbef8894278cd83a5a79cbd6ba31b7e9e0fec066ee71de8388f7c3bc131a8a9056a571293f5787b9b9cd8f99f58b7bcdfb625f9a74eafc320d36bfd576f2e03d00115ae9c14f5b96b5b89710a531f1ee0e7dffecbf776935d200936685eed00b49d3f88be65a5dfd469dde0e7d3eb8cc7a2cb64c4273ea8e2ed75e6158f646d1a03796cb95035da57ad292dd685917b2f6b17d42bcce9712634320155fe", 0x1000)
chown(&(0x7f0000000180)='./file0\x00', r1, 0xffffffffffffffff)
r2 = semget$private(0x0, 0x3, 0x11b)
semctl$SETALL(r2, 0x0, 0x9, &(0x7f0000000040)=[0x14dc])
r3 = getuid()
setreuid(r1, r3)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000040)=[0x800])
semop(0x0, &(0x7f0000000240)=[{0x1, 0x20, 0x800}], 0x1)
setreuid(0xee00, r3)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040))
socket(0x2, 0x3, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000001c40)=[{&(0x7f0000001d80)="436becaf86e33828aaf23d59a5865266420deeb16abe4a1ea8bde98c5ed58b207c263bfaf6fd4da40de0e2b2d595c6ad0704038d149ee312f21f5dc7eada4f7d53394abe132f527485772e5a7eeba2f6d9f6bcccecfa250a4ab8c7f44afe36c6ed2fda0a03202e559e37e6578bb780958e0e6e1bc71da0af6ac8eae87ea5154acf7e40dee25698fcc8d687969972dc21739c7ab73c292954a5a204a3f640679d7a9f0b53be33e1b31fc4736328c26f056126db4fb9cbd1d5b021f5f7d6da1369da14d1433df4eb363be52e00000000a718bef3d975a4b078dd9fbac99f610e8e6ab05791fd1b4222cc0ca4ea290aa0dfd4a25cd589c6aaeec980862936324f2f8be673b85d92266cb5a5b3605d4b2f62aa8233ed48fe0f537fe022127edad594accedbdddf40ce5650151c1dbcac44d9ba79e39402fbb3c8325eea6a89a3939ba149a43c7a7572255c5eae2b976c4528be0b89ac6dc2d75f9fc16a896913b642e6ea224db1a7414fe42f21d1e4452964878dfe8ed2eb3db635c88faff86d3c5c36ae4f39f1f60bd674f5af469769ceb95e6a8a5c95d88150e9aacb9cf4dfe64323", 0x1a1}], 0x1)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x10097ad, 0xfffffffd, 0x979, 0xffffff59, "0804000103000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000ec0)="d8e22ca54c3866f2530d5f823da761504702940b47e3056c089a41b1f38f32b86e7bcc758e4ded15bd4c8540d8195c0c02fc888b761964c5c27b00b59c5243ab84bc800c4df348db0e197e3642cd5bc8f289e74918e56e794e852cf0e8d812b212068891e0ad61f6c1b04fabdae5b45572aaae3cca6f7b50d41f8f8e58311a50c80f96bc58d396723f4b05933bf2e21b01fbb830ade002da1dcaeb3e3f0b0d32b29b2d0ba07e9f59e2dc4cba52bba1c434db2b51c921895ff9e41b544dddb9471b5fc2a83768753db27cf04a4e60b1823b605b32562a0043b170823869d030f8ac2a82b31362a76938e20004eacdd3ceb4f44a5226b9d64837197229bdcbf5c38fcdd6411e624ab78a1adda84f8a3f7625b2877bfad50aeb149b270044b6d0ff3d65a2da9bc6dc5325681b668dab6a4331a291958136fdeccab7406c38ab267a1c1fabffbd723eac892c99a06504c3316e29ef3e074ad7c4a7ff4f3877075edcac7f18cb522a7c171fb123fc3566c6074ac8fd72892fcf2fff9f405c37a671848efc7315075b1894e5a329179e8fec5744dff335e5b83c7835106cbc1616083ca76b15fd7707f5c5fabb2246fa6460c0340a95c64abc949fd37543119026f9eecc698a57fdf835db0f571428130772cfd128a0ac660abb39304a149dee64a3216b430b7eef38baf101866a723e602bba533dba0ade4dfda529c4d6a94620710d88f175db3794c3a805f00c40e3f1f9b1dbd590f63d2296dd220c02205677bcbe110126bcd72a114107f06e038100bb771809427325cac69f3bbf7f4047a45cf1a4374642f7ecac3c45a316b330122a9a8c7a99a02dca91eef4ca3cbf41bdf99c149acf3c766d3c2ad207828ce0d858a6de408b60ae579684b16063178c671e3482173d8cc076458d7c84df197594b273811688e92e6c20da10e558c569e23a7114c5ec41282e944b92b446ae8a72defbc68fb42ee3e681444b26ac0a1e0c1773c6bc9b7674acb3555022201ef6447b6c987aa554c778480afa9ee56c5a2d583ade68e4f87ffd7cfb8435dd5090d080a51c2473b85ff212d7bbecc4aa7d4b93de5681485b897da7e082767f2865e7e5a5e8b3433ffdc0f3bd8d21455a1e9d3915109e7ec75fb36049591357518108f9f81f6d337de7d76dc2b18928d7d3b2f967a29bbd55d6cc34fe823cc36a0d1a7211e5ef8a6dfcce34e455b8ccd47e26d6b96763bcb80277821b08d2738b4a81331e4f9b16c912815951c6f907c643b0d7950b811b26278baabcedcdc7f1c2f7ab50d713acbfc3bd621045bcf018c59c0ecf922c16465c6d18aa06aa85beca2d14a99a0304f48b00f33b4be1edbdb6e84f8a7adf5b06595ffda0f56f23e171e414613cc42e24a544dcb3f5917e57c467c90a690b8fadb0df0d2ead24b584fc46859d1ad29dcab1bc75b20bf1f2f466980d27103222ebc054b80d93de33bf0fde0bc7e8ec4793181d1d7b55c5870f4f5ed5b10f730b4037329c0314a2495861906c0a3db14be4d11bec90936eb046b52429653b2aec23b6a1c96e481cfb6738ff6d4ca55ca59ba3d3725dc358e5098a7f28fb025bcac102bf773ad462f8b0c678d7b28ff75afada9955a4280bf3c9030640451f7657eef323c3614135fd89c6fe28f09f4df541f1ed2be67b6c0e0f951fe850881e56a4ab6ff9a8b35c6f212aef369a492bcc4310ed5c0d74436ac77bc3ca2844693f7a62c82ac543cd6ece960f8f2ecb053f0a5df0e4af015886b0498f1997787a50ee9bead15837d4abf4eb77db4a20f1f5be19d0b91d1305e91f6637b799620713c5f946080867529f7c674493d3b7c46005d1419803343fe851cb2651d2838462bb3fcb736d9e75e37d54cbcb6e2bb5c8d2b87f3c97a1f90fa386bacb4a7222aacae14d1671c671bf401736c94689afe34b15c979855b0dc779b2cbddc3cdedb82f194ab4f5d343814af06f2510af469ac782f67c045b19bc164dff8de8500dbfb9ad3be2afb87f5c74d1fcff5fb253f33fc561e575ea6a0cd13bbf66ca8c5ca3cb0e26ecad223ed71b2a0830cd3f128b5272e0534e608030ee4800a89efe06e4cfbb930864dfe7cfb448aee219c45b3251fcbb19f0bf4a93b17e6ea59aafe62c679508acd17cc94949a7f4dff0ab253913abfefb171528ed7090fa466d763738512c327bc0c899c4b44d7ae0529c66fb9ec5ec8253e9540f5665b6cf0db212cbb965cfe28e7357215a36e0878e96b2a908ff1b7e68ff1ccda8e51dd53479bb6278b2c5adeabb577e0bea0b57e51d0fed5c8757002262b86626d9245dd978ac9aa3a0cdd60d638eccd657be93c6b01580d04d36c609ba5b52d0a9c5213584ba86d1f91ef991a6a4711ea7b909cb999bf290d499cb751e68c51298fb623a899e76aae08fd02e91e6d145d0a90c0c2732f8469f8084af050847629b5203332909d48e12ea1a28b05930f7661fb238965800be6810373170d2b1d2bb88f4028f1c492674bf1d5b26bdd45ab0a75f44e8a4281e54c0ee273e8bd8df688f45680e01c9e483a5a6ae73854a175fc7959099437a493f883ff00332603f7717a325bbdce15e66f0b2c5205723a665f5e49a13f21c29a94a7fe3e7f9076ae950770008e6a144f3d173b63daa11b91fc068f6266f7a7c486ce3c1efa477e442a1a4baed30d5e6a28f5f68740135cd6a6cf8159dd8b26ce539d10a4c4ce098fca991174a13d1c09bc0a4647f9ed9a79b74334cea4c8d3e5136b59b8ab648f7d972047cee4366d4deeb0a92e7dda9dc486141ecea6ce98b735a152b3b585f8411ae6cdaf9df611191537658f2875338d2c163b279c3d157848eee76416d6f7df6f30703465511aef94ac0efb0cc4783cfbae7a655835b0f875a3507c5328e0f191771eba18c703644416d116516a603e7985d847b9a543cc5d7f3062ea4739218086593967e1235dc48ed1fd8d3e7a8ee59316781ee2ede1a80e17210b309a2150b9094813f59b131551cfcac3143ae5fc941222641fa4d42eee988de2a1aa2b3662ede2d5ec1a6afc42cac5ab358c45f23f34121e9cf9bd324e349bd84589b843a7aaa276991173cc7c744626da61778843727c9f31bd1db1a29a4814d47bec571a9ad084cc43f04ff2070f308c132033058218cdba0ee2bdf0d6c4a4d038301a63ab998851089584a6778ba082fb743a4741def7aafae48575d30b34557fcfef3f6e76dff95cb757e1f951bb38b173700bea19e38e5e648c5c0de5876dfdf7f31f57144cb2d3c9f5789c7ee96785fb82623991d99d20fb7c8f07319ad784255eacc868e50296626dfffa45f5e9bd9b1cb41fbeb973c7b4613795ef289a5a231e25bc183ca14a29e37db072fe40db949744f7838a95dae72207c5a878510ac3ea4315023f0267df2e99d5c0d29427a6949f6c822a8a7002feeb430debbf3d0dc0249af1d0347368c85e903a1e02bbe9169d48d9d44933b62bbabaf3e6ad89067ef853f0e6b370fa24fd0664e9206677f2d67cf1c82c90dace8c010156aed03d4a8eb455261c2427e8b47826e801b6eeff9bbd8924af1f5c22a54b4c59aa02c60d4bcbe30c13a4b6fedbbddcf8cedf3713140e034fb3ee6022b19aa4b63ab64893b7cace5050dc3d15257a7bc02532740c01edfde2e56a196b483c4204e1b890d25001c787fab64632d20e3fa6ce54a04b99d45495fa7adf515564011cfdc92cdb9ce8750fd4636c507032a2a390d745c1888", 0xa4f}], 0x1)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open$dir(&(0x7f00000001c0)='./bus\x00', 0x0, 0x0)
r1 = dup(r0)
ioctl$TIOCSETD(r1, 0x80184600, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x48}, {0x84}, {0x6, 0x0, 0x0, 0x20000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc028445a, &(0x7f0000000240))
socket(0x0, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x0)
unveil(&(0x7f00000001c0)='.\x00', &(0x7f0000000000)='r\x00')
r0 = open(&(0x7f00000000c0)='./file1\x00', 0x0, 0x0)
openat(r0, &(0x7f0000000100)='./file1\x00', 0x201, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt(r0, 0x0, 0x3, &(0x7f0000000040), 0x0)
mkdir(&(0x7f0000000200)='./file0\x00', 0x0)
mkdir(&(0x7f00000000c0)='./file0/file1\x00', 0x0)
mkdir(&(0x7f0000000040)='./file0/file1/file0\x00', 0x0)
chroot(&(0x7f0000000000)='./file0/file1/file0/../file0\x00')
rename(&(0x7f00000003c0)='./file0\x00', &(0x7f0000000340)='./file0/file1/file0/../file0\x00')
syz_emit_ethernet(0x46, &(0x7f0000000080)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "fb00", 0x10, 0x0, 0x0, @rand_addr="000000000000000000f5ff0000002000", @rand_addr="00000000000000000000899be21400dd", {[], @icmpv6=@ni}}}}})
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000000))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0xfffffffd, 0xffdffff6, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60acaca1f2844dd730ae78b0e94daa6340f332c6136fec572c091ec964f479d3f09601e05a4265ae5f4d3a70b8530f85f78b162764959532374a5f97ddac09069dc842ee5b6b470f0a2c13de93943f58c029f6bcf437d67a29db534b1221fd5256cba8ba8535737417084659d7f6ab02ab49954c6a070955682cc9dba2931d55a413df999446fbd9407aaab9e385a161f802553f02b1e823e10d785547a7acfe8c11b040de1a30bddc771c1adf40bfb83be91ade242f0a5330f89791d6166e038cbe7f5d719bf1c9d0d9482f436fe94ce6637c899cdb4027cdf11af7c3d3662695d53198228546d6800ac6a6d9106e682c0c96e510ed7cb32772008f345f19a39743eed6db45bca832b6164801181f213a793ebb0fdb2670fc204462dd4c3ef62ea40c007418ef216e5c8be4fc31a982fc12166b472e53b1b4171e7c4a4f96b2d0bc9d72e34b455e6a2833e7f4402db98af3d1c522715c1e26e23066014f1b80741f9b37194e9a74df15837b3bad80bf2cc2f30f14388c68e6d72c6dd465795672595b826b4c31f4162f128035517b0003696bf69d4bc74cffb0b74183ff539c4562d25ca0c6edf7286790e23e37a144f34615d42bd2be5ccbaf4856c11188e1d0564a18beba578e822b6bb1da3322ab17982f3e961251692c659d0685a01be2b0908b51991ce84ea5bbc07ee3989a049a1d51e727c6fd02cd9117d17f36bb4790fe8fa0ccad53430db28c2792e2d3be8dc96d404ea501671e78f0a222a89f540c4d776612efd9f3decc15316e39b3cb6efe90ead64d346d4d40c2eac3267bab8d88692399cddcbbf42acb543f97bae9e29a11836096802e367ad2606ae1ee8e5627a5d44960588cccaf9319a3abb26c73a6d5aea70595b68dfdeee86fb58171553ea34d4179d6ebb61157e4b0d3eb60d19880c018007e794e65b6e02c3e6818c85657b751e45ac169039f4ad5b34813b64c2fd684774117c28bb2a3143b9a0810ac009b09af56d5550209104d6c24242b75a2a41fb81fcfbb4c070e44f798d6bee721c37581fb91e3c71f708c370203e505df9e137130c95c1e110826909726264a6e20016884d979e9bbf7222eeef720bbdca3dfd700844b9554624e00f6607d0d16deec307ed23066a834121be585c778d714415307a7f4f9c3d904d45059a0a096b3701d13b47803e7ac0b319e97a8146d7a111671043df4b9ffb3593b522dd160ecd92d4ec2e45cc2e3787a0328f784e2a5af6fe154bd11027055e2a1e1008fb759c78637041319d2eb2a19487d837ba9fa7b953b9b02c49da2245796bc2d7f0b641b6d6ee891725dbb3049e7e31caa2b7a38adf271a3a969a099e133cc23418349151efc103d55f6fe50a4fac6cb03ab24c396183dbc816fdd75c6b2968d76f80de051bc35eecdcc3c5fcff7ac402addcf0f3cdeeb529b08936fefc35776c762805501686930ed005f4fc7e58a6c3c2e8a02bfb54078c2407d08ab10cf4d7ac48b6e375582bfa7573ea570ac1df89c3bace13d999b37dd6d2de42dfea62652ca602139a1ad53b568b771aee8ea670297be8a6d1fe1558fe566d2b6793f9627c53a2774697ec6ba2c4ce28717b91b9b11af91da845d3bd50ee59c17de0f667efaf5858963c010c24270aa19be10efa16a9d96fbf37454d5f4a61195b4c605fcaf43b848512fb5ce18cd39ef4179464f027bfa9b8ba55373e2b89d2ec83e1ab28e64ea913abf1146c31d5c5c4db77308e02a4ebad58a0e1c4e66a3d2164ff56e50ec9eaa71ce4f046620a16137943f0e47101e6ecafa9ba5adf86ac7e8cff297d23fa36a0e37eee1e5ee56b7f379a3969de70796193ae4f380248ae9cff6267cd7283959950a4271e28de92cdf3c34f01ed4337df700dec6a5ac263e476af52a5fa13edcdf7d62574df964e631e3abf5e0ede16202fdcb64cd8f5808c50f34c52299170f3699cb11771785c1bda9eb62cba5012808a5141b2c6a4de8e784c62269fde010acb4b3d41910f1719d95884d834c489d2158b9e49b5754fa636eb18654daaabab06b884c71ffd7a02d5cea6376b05a295c8f246fbdcc2665317f4ab72bdb99495e700216675b22a047cfc2fd683dd4dbe5e7c023b729000e4d5e373a62b7b075bee893e107687fec5fa9c", 0x7b0}], 0x1)
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000c80)="a26bdb3aac1ff5c615000f425586156414c4ab74191bf2136d34fcb9aaa7b7f60c21628407fbb46df93676b5d28fd8074b9028b52a7a811338a16d5801ef0b2e2bb9efe30f2ac3bbd689d9503b1f1042a5d827161e7be5f0e685b4c8a6b380", 0x5f}], 0x1)
msync(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000180)="53ba96375f52c7ebd029feec3c81e6f9e853b3f4cbbdc93df44fa6d6fbe3e331fc7067fec4ec9d9a00364e02970b46491e2b7cb504000000000000004fde4406416b2363da94dbf346ef9ebb5e1284544ffa28f9e1818cb61a7ee71bee4a948362b5f5a5cc4ca0c0e82c45c0004a53bd502ba138920e026d031a78bfa742197f12de6e7aa96c2563316c3e5a4d6c1c05fdec5b97b9e0d22254290189c95eedebd2186297b64cb62cf58cdc0af940d76e5f91050e9284f47d6cf17e2a388a8942828b0676b6e959e87abf5e9325a3f1b8b51c0094744022c715be7f752e16aaad5ebf5b8ebb1d688fef12f6f7f7ddf6bf9ffe3d3ba5923c717dfe0966fab0ac08a33ec3cf136a385fe8ae094a7dbcb92a9be41cae2fe6b69f513cbdb9f8cc3edf77ed1d4c5ff111ef5a81f83279bb9819ba74503832", 0x135}], 0x1)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setreuid(0xee00, 0x0)
r1 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r2 = getuid()
fchown(r1, r2, 0xffffffffffffffff)
setreuid(0xee00, r2)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
dup2(r1, r0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
r5 = dup2(r4, r3)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f00000000c0)="05", 0x1)
ioctl$TIOCSTOP(r1, 0x2000746f)
readv(r0, &(0x7f00000006c0)=[{&(0x7f0000000380)=""/189, 0xbd}], 0x1)
close(r1)
writev(r0, &(0x7f0000001d80)=[{&(0x7f0000001f00)="e581f9c0ae673edfcc51d5399a4323f00817d456c4350c5b19bcf1778802269551f105b9986383dcb0eeb97ec38d6093f4f1e4aeb9c21cff77642442afc9dd6d48d80a5ed84dc467997d82fdc12d5fa792e426baa6c8e9094d54a4bd15f7576077da343fe1722c6037e1609a1c49de7ae88f7e0221a1da4f74ef04582da840a3a47059033e415751d6f93b5267cceba3f82b5f6b6db9edba6718a4ab5db2f68c204e2a4585d378ac81fb7385abb84bd859a97aaa1c1a1305aa339855ce85f3ef8f59c5c907cb0f2d24", 0xc9}], 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$KDGKBMODE(0xffffffffffffff9c, 0x40044b06)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282906000000000000002babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000021000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001780), 0x100, 0x0)
r2 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000017c0), 0x20, 0x0)
r3 = socket(0x11, 0x4003, 0x0)
ioctl$VT_WAITACTIVE(0xffffffffffffffff, 0x20007606, &(0x7f0000000180)=0x2)
sendto$unix(r3, &(0x7f00000000c0)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282906000000000000002babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000021000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
writev(r3, &(0x7f0000001700)=[{&(0x7f00000001c0)="a81d386f466755594ae2ac305ecd94be0c23f5acadf35c964b25cdb95bf9af31bfac331b0a7d574b7ca5e6bd6d0592d5f96a07272c7de150c381cfd2aa06eb3e7aa66686257aa5f56245f91afb0ee6b9d41a393e9979a5659dd2a4f83eb236fad99c716dc26d1e24cd786b325d220c", 0x6f}, {&(0x7f0000000380)="7af1c625cf93da79d9837450a2d672be96e68e7e27c69c524adeeba590471c8202f5e36fda5bdee474507e57f86742dff30008fc1f5a2e3e3ec1dfa0d27de3abf25a7253b15e436c2fbe7268437519e2e978973dec620055f5ba7f510d6707f167958e416270838944b421d8fb1bf0b9ea1641b173ce40077c2cd080cc76353b8b3e7b44b74a2f019650e085a3e6981562bbb437625f7713143ace49615c0c071a", 0xa1}, {&(0x7f0000000440)="a5807ca5ffb0ad6dd86406c36982b5d203", 0x11}, {&(0x7f0000001480)="c913aa3fcbccb2a419dbd12ac3049bc1b8859fc1de284e5c8fc65ef8ca22ab922c422ac80ce5dbba38f5149ad784b74e609ff86fa38beb89618914ea09558901652816252b2700bbc7245e83a91b796c73334821c4c3f9669bdb1cd6405e6f57a1b68d6cba8681d36f466d54fc059df64b238c65be7acc1c4567e710ae56297d5896b8d3bbfed5225f68739b890b3e208837a08653caddd881ce6a40ef9fbe7d6e4662b77ad03e7cfec5d9f35a837672989bbacc71b3413762e3f1b76921aa3f38756705b1d2f90b831f460857f4e9376ef2b26ab0019e83139f0477c1e7b3", 0xdf}, {&(0x7f0000001580)="878144c6ff1da493e0dcda6e1873c88fff8131c9e6d99824003830e2af71a1b2de53a7085641cf87ffb684c701aa25221564efb30547bb5c5644e30a44ef3efe1e5026fab8b1f084ef4b4fd0487a9c3ec7cf18bf7afc2f6a0b7861260843db0a9a801b44b61fb53b1031b3311f396d52001e845941c33bab5a40e7c5c03a72276fba2c71130ee44c2bcae4acbe68c60d2e4dcdfb1c56dff9d4a851b80ef718e88ad445a2ce3c046fde1989b225d565c69c2621d05a2bf1afc30c1c60f753f28a589e7178", 0xc4}, {&(0x7f0000001680)="bdc1208670bddf9fbe", 0x9}, {&(0x7f00000016c0)="a268bb240df1f428e62cea257134723a18e3fbbbcddc98f119d60c4ff72f508807f968", 0x23}], 0x7)
pipe2(&(0x7f0000001940)={<r4=>0xffffffffffffffff}, 0x5f176e70d665fb56)
pipe(&(0x7f0000000080))
r5 = accept(0xffffffffffffffff, 0x0, &(0x7f0000001980))
kevent(r1, &(0x7f0000001840)=[{{}, 0xfffffffffffffff9, 0x2e, 0x0, 0x2, 0x7fffffff}, {{}, 0xfffffffffffffffe, 0x1, 0x20000000, 0xffffffffffffffff, 0xbe6c}, {{}, 0x2, 0x23, 0x2, 0x400, 0x80}, {{r0}, 0xfffffffffffffffa, 0xa0, 0xfffff, 0x6, 0x8}, {{}, 0xfffffffffffffffb, 0xa, 0x4, 0xf62c, 0x3e6}, {{}, 0xfffffffffffffffd, 0x0, 0x40000000, 0xfffffffffffffff6, 0x1}, {{r2}, 0xfffffffffffffffb, 0x4a, 0x40, 0x1, 0x8}, {{}, 0xffffffffffffffff, 0x0, 0x8, 0x0, 0x7}], 0x8, &(0x7f0000001a00)=[{{r4}, 0xfffffffffffffff8, 0x1d, 0x1, 0x2f, 0x44}, {{r5}, 0xfffffffffffffffc, 0x2f, 0x1, 0xfffffffffffffffd, 0x6}], 0x200, &(0x7f0000001a40)={0xfff, 0x3})
ioctl$FIOASYNC(r3, 0x8004667d, &(0x7f0000000000)=0x4)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x5)
r6 = dup2(r2, 0xffffffffffffffff)
fcntl$dupfd(0xffffffffffffffff, 0x0, r6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x60}, {0x7}, {0x6406}]})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
syz_emit_ethernet(0xe, &(0x7f00000002c0)={@random="c81b4ed21734"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d770c167e400009e00"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x1, "dee900000100"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000080)="ebaf0900ed0e25baff6e", 0xa, 0x0)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = socket$unix(0x1, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x20, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x44}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000180)={@remote, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="416494db2363", "", @empty, "f9be501d25e4172bd7c6287b90ec8616"}}}})
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x4f}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x99})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000140)=0x9)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000180)="0d5003d28a57c5b4b9f5df1a994868c8f1afb4a51912037d6d71d6562178519221167ad1345983aa503cdf7e0a", 0x2d}], 0x1)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000680)=0x2)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000180)=[{r0, 0x40}, {r1, 0x40}], 0x2, 0x0)
r2 = syz_open_pts()
readv(r2, &(0x7f00000001c0)=[{&(0x7f0000000100)=""/65, 0x41}], 0x1)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
dup2(r3, r0)
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0x3f, &(0x7f0000000000), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd604407, &(0x7f0000000240))
openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
syz_open_pts()
syz_open_pts()
syz_open_pts()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000002c0))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0))
select(0x40, &(0x7f0000000000), &(0x7f0000000200)={0xfffffffffffffffe}, 0x0, 0x0)
syz_open_pts()
select(0x40, &(0x7f0000000040), &(0x7f0000000080)={0x4f9}, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "dc4552e700000000000000000000000000002300", 0x0, 0x1000000})
pwritev(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x800)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000001700)='./file0\x00', 0x400000002, 0x0)
writev(r0, &(0x7f0000003f00)=[{&(0x7f0000003980)="fad4f4047873c5dfe75ffac6835fe8faf57eb3396ea406418907a7981e79ab279085935e85bd15b3851c05a744c9d99bf3a09879d4db67826b0d6c593b61b413db164850a0a3b4803ad32001f86e8afe3431f7af6c3a1e525d3eee2ca774658df572a3fd6bad358dfd10cb1e05ca98920c5ba1ec5c9f37eef6b995f26434c1247c42e2657e1a385de4258fc909da5ceb05eae619c0d80a1ea6c54d4280498a572de7aae65a26b407cdcec03b8d473c214137c112b86504f9c159aa76ec8cc5903e1000bf73f4104818f57c9a30a2683c67415c51a8607d6758e7ed3543fed503e57e0776afb78a5e04b4baec14eba5", 0xef}, {&(0x7f0000003a80)}, {&(0x7f0000003b00)}, {&(0x7f0000003b80)}, {0x0}, {0x0}, {&(0x7f0000003d00)="496d47e2f333085136803e63a7b571dcd3c07d69359a0fc608e56ea0b99ad5d5cff7e7929be420448c9179bc8756b58a6edb4fae9799d84427b26cfb70", 0x3d}, {0x0}, {&(0x7f0000003e00)="9f31ce8d6897da281ae02809a71cb5b625e5b974972bcef34de79f892fd466178dcc6d2580acdc62752dc6ff282557c734217d25a1af09", 0x37}], 0x9)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setrlimit(0x2, &(0x7f0000000040)={0x60000000, 0x60000000})
mprotect(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
openat(0xffffffffffffff9c, &(0x7f00000000c0)='.\x00', 0x200, 0x0)
r0 = socket$unix(0x1e, 0x3, 0x0)
getpeername$unix(r0, 0x0, &(0x7f0000000040))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x6b25}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000080)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x1b, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000b3060000000000000000ddff00"})
r0 = socket(0x800000018, 0x2, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x9ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r1 = socket(0x18, 0x1, 0x0)
r2 = dup2(r0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r2, &(0x7f0000002600)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002500)=[{0x10}], 0x10}, 0x0)
r0 = socket$inet(0x2, 0x8003, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000080), 0x10)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x300000003})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
fchmod(0xffffffffffffffff, 0x951b32dd7ed0c0cb)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000001700)='./file0\x00', 0x400000002, 0x0)
mmap(&(0x7f0000002000/0x3000)=nil, 0x3000, 0x0, 0x800000000009031, 0xffffffffffffffff, 0x0)
pwritev(r0, &(0x7f0000001500)=[{&(0x7f0000001740)="92cd2c038ac4fa1fcf8f9cbe66d831cbc7b0251bf01440e2c417ba543cca2204857a4ff64647bf4345b0b1a4d02562e802f227b4eb4d2afe97dc71623ab3f7b04c9bf079e29391eeeb58b4fc3ad77a748fbf103ee84c4b2db9f3c4d757ffb928fd97314a1a73c699e96c6ee6274d605650f5b3d0a70c460d94e2f7a9e1ae58f80f6f0b2ab29e87cdcd3fc0f50d6b165212d4da47a7f7c56e2decb761d73970e2afc6099f1ef0a5820db048205069cfa4c60c0e001a024fb8ff9f01eaf5dab68351d2e015cc63f6bcdd0dcbe66d9bb1302cabaf971f19bb76040f7e511906713b4074d63f237910bb2534c6523e990ed8d7035020642ebd632b9b1a4ce21c23d91c25caabeac75405e4b324b2072b989181108f3c1892f1ca1028e42a639bdb16051e563fdc50f6b47214a976a2d066d9fcb73640baa484986c43bef16bf053132e3adcf148037ce28e5d73db7d66d630368dfd8f1660ccc728787a676ac00e94ef03072e5325938ca0dd07bf0ba11ba89353699c2d990a16485a2cd342a524a7b3fe6437f94874ddf72377571247eba2d88d396c1332dffa5d8a729f5c9c713fedabbcff8e4816b384b6df95e9c5fce705b912db97fd76d043913ab492de997e3089f27232e24db2dff813eae16f8f6004dd3c63d479378fcd305ba00c35f1c707697117cfbe1650a34dc6278a9d3b2a0a0cb09d29f3625f4f26e5b7f2e64953e6229b6765cc0daa59c1fef29ce3a10b64f7c88aa5953e1e23603f38d620f7c012361e22e0ba57f7046c180ff0d8dda6ac128717cb54e11fce2084eb9f7e98ca04e03218d716f31a1a9a5908eb45616351a7478bc10525979f396fc370d46e69fe6290885411809c56c537e1409c8bd645141ef742940757a35b31438a2da5a123e249b0e4eddd4fc7f9fefdf8a58d269adab34f8524460cba6886b10a0af8383ec9061bffa420ca521186ac76d71100fefdeaa44cd975d0a7498e3695a3eef97f36032d4e5e97603cb2776101f235ea73e359997f79f0e295d7a1f49939689ecf0f07ef025b4605ce8e25680c2388049be1b1190f952c433c5aaf9e60fa80eaf013accf8ece76701098463521c28a0f60f423b56ab4840c495aabf3cd5366f2de440d95ee9c511c2cee47add38750246cc2ce3d3c9b315c0683be321562131e1a8352aa90db0f07539573c385ae5670cd5501974695791fb535e63c240e1e7d881c533152160b0a4bc3eb77ef0865f07a8e9ea91a45dc90c43aef9a7c1bb291f59b53fb4839497a6b4dd1d413f006a1585a2303556885499286ea69ed4cb8bc6176cbfb530106d4692d440317e13b2ec53c591d7ccc6c4943cd79f16d856b0004062b3dcadf6515aeae0999d8b14b814ce9d331c75875dfbb6985ba07269ae8e9d71b52e1f357e7050c2eb43c84dc412b097c1b21e155a38f412d81057e6375b058e13f612549b1f91bf098645de285a403ef44cd049fb9024a4dd814bb40ba9852665698b86764055c458c625db36ad01aa8e10e262b1f4c878f54e3c54e578eddd99dfc52ebd4a9ccaa49d0508c1e2bcb217b9df33b5f2d394d65a9ce013c9269bd6663543fd2e1b3288320c519837551a6f8c791342e6b8bfcc198e6c9772698b17f64723de1fddc49e0725ac15fa47cb21f4f2a58189bf0397153abbb0b828b72fb194882a0e4ec8ed6af4eb00963a8018d017899a60dd9f1b5d7864a399156c5ad2ffc68c20e7aaeb98df95bb01523fe6ed2d1370971dff193c67d526f649699b71ea4a83cd8ba667e5f954d34c39c27a077270011508bbd39bf34bd1a0a32247cc041aca8b516eea458167e61729afc2d18d6f6f0fa8752853c525661bca8313da26465db6c7aac4fcb49a6b49ef1ebe6d0889afc3ba651dec6cbfb827ad809ab3d542bfaad72ef9c776bcaea393ff7e8332995282f596b0b43503b7d31d79baf4a14644bf8736e6f2733de6131b61f271be861892d8da6f5d686b8f7c883e7959518ad601db239d048e80b3539f4d6ed1a95ce4364bc0f5f428679dce3f62743fa5e31acad0eb3bf1faa1c89618322b990936838d5cb28b3e5cdcc3925b6da53a30c83e56af951e0bd3d76c5c83000e0f20c8d2cfdfe152bd5408b514c681e47a71b4ba35742b40484cd1fabc98899ee20a4ee3bc2c32453ca1851aeab8f977aa27dfc6a3f958fd51bfa6383576350a0482c8af0762e075b29c41783689ed8c60c7eeca49e04202a3688d4a34a90daf89617442045f235b37565328f8cba44ccd92770a0096a5a8d63a20c278bf6f32a8d93f8bb3cdaf4f51adae17a421a7e580e7cda8f441ff1cf6826d6e081201c68b4e9ce6695454c832a68ee76b3980cb8df824d66e6731a095389f189127065f5bbc038f9e7ad061c96974099131392d23f066a78100f05dfafcfa5a4a70471c53736bd5fe5fb735c8e21e6aff31eea860dcf6549ebfd4e4879ff50428accafa1e4d83aee7066bae9c787b59d5d79af64522fed8a0947df8885979f2fcdfe886e710033e69391b407b040d8a2363b83b337a344d227519ad3a27a21b456ad1c99f33b240a43304a665b6634f4d91c10809cb1ef996a24e8945585752ca6660caa9844885d848f97e7a889f73939e448f89d3e541cdbab709449c29af62cb45abbf4da38c12bc6a31ac221bd165ab072b46c393cadd12a7f4c0b993c53143f47972f046dd51796a52e21bf889288b25ebecd1ac53ae1429417cdd01d8d689e7edc417a7f880c3751e0d44e0c6c611b1ba34ae7bbe56cd7497e88608179d80afcc6fcd52e99d27b1a492feb4cd67c42b3bcd99b81b6d2546a9a906c8217651246056f6e5893d692f325cf32f718c6716244ba923858a1a93603d2d2bcef913366c9fa2173514718a6220eff738225f54283cca3ace1f44750d3b1ccc9392559f414469fdc1a9d500480a06fb3476b3000c0a577208e03951108a4d12bfd25dafc038c29deec988050bdd4e0d6c195c5adb3c6e4e7dfa039931cb4c734c0c2d3d2699d8b7917e0ffff3bac9a704685c42ffc39f0447756bb9bbe1526a4147aab51bbe0f729b3dcd986a811231ca5f1ec0554161ba90bd938c1dbb6f663d22362abf4d6113b0a2c16f5097dff5fa947ad6b5803b21a88f367c5b582e07698625546a83742a2f7c060cd645969dac493", 0x8c1}], 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = socket$unix(0x1, 0x5, 0x0)
dup2(r2, r1)
bind(r1, &(0x7f0000000140)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
dup2(r2, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
r5 = dup2(r4, r3)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000080), 0x4)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f0000001300)={&(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100), 0xa0}, 0x2}, 0xffffff36, 0x0)
sendmmsg(r1, &(0x7f0000002380)={0x0}, 0x10, 0x0)
execve(0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000400)={0x0, 0x0, &(0x7f0000000300)=[{&(0x7f0000000080)=""/95, 0x5f}], 0x1, 0x0}, 0x0)
syz_emit_ethernet(0xc6, &(0x7f0000000000)={@random="fd658b242f9e", @random="ce033c48a196", [], {@ipv6={0x86dd, {0x0, 0x6, "a21f98", 0x90, 0x0, 0x0, @empty, @mcast2, {[@dstopts={0x0, 0x10, '\x00', [@generic={0x0, 0x80, "93b856e8f912e60655500fe120c37e09de951798c28f86bf31904af9a55996a3395f07ff27842a0a14ddd3e0d96d5b50a1b65b42603e36664bce34743ca089baf6da34aa8862a18659f8c30428640a15af0bccde5d382f2fa5fbb321ea7dc43554828fa37de80507235e5a0a8238459abac3f1ad167d41dd82a0bd5331bf669e"}]}]}}}}})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCSETAW(r1, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde06000000000000000000000400"})
write(r1, &(0x7f0000000040)="da075899deeb61be144a66cac42a2a26e0956dc4921daef310848a0828b7412810de0d285a8fd752f188df9c00d0643de402afef1d79823bcd24af7dad925cbb28d8e0eea45150be0e2fa55109e80a2f157b260e66158625ef9f9a0dd594cdf52361e817f51f2a5fb1", 0x69)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = fcntl$dupfd(r1, 0x0, r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
write(r2, &(0x7f0000000280)="c5049d8589d603c8db9cecaff1074e8e7f6efac8084b8f97cafe202f958a404e37078a94cf2f4d2eceddcdbb7cce5543c68ef25f55edae4feb430733c398c137a1171728ceaf7030af9c961b59dc43024aeacb0e5a86199ee7a210ba2b1ca1b056ab91f0fec8e55a44ea6606dfaa0a9d3c6c2df559dccdb923faf0bcf4f214e84a4bb1c4d88ade37d107b6b0158397b1e48dbab75f3687aeca4c7e34960b1df09de2b0e421cf7ca88d9d62a351099ecb7729f79e0a3be72dff3fbda04b503cf741f19e1e8a7e5d2de80d9fd5fd05eeb2e1ecbabc78360056c0262384", 0xdc)
recvfrom$unix(r0, &(0x7f0000000040)=""/219, 0xdb, 0x42, 0x0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f000052fff8)='./file0\x00', 0x0, 0x0)
r1 = getuid()
r2 = open$dir(&(0x7f0000000040)='./file0\x00', 0xc0, 0x20)
renameat(r0, &(0x7f0000000000)='./file0\x00', r2, &(0x7f0000000100)='./file0\x00')
lchown(&(0x7f0000000080)='./file0\x00', r1, 0x0)
r3 = getuid()
setreuid(0xffffffffffffffff, r3)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000380)={{0x6, 0x0, 0x0, 0x0, 0x0, 0x40, 0x7}, 0xffffffff, 0xffffffff80000001, 0x5})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000180)={{0x1, r3, 0x0, 0x0, 0x0, 0x1, 0x800}, 0x1ee9, 0x385})
fchdir(r0)
r4 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r4, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETVAL(0x0, 0x4fa596d1f056515a, 0x5, &(0x7f0000000400)=""/287)
semctl$GETZCNT(r4, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r5 = geteuid()
r6 = getgid()
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000380)={{0x6, 0x0, 0x0, r5, r6, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0xbf5})
setuid(r5)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc0107005, &(0x7f0000000180)={{}, 0x0, 0x0})
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{}, {{r0}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r2, 0xc0107002, &(0x7f0000000100)={{}, 0x7, 0x4})
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f00000002c0)={{0x3, 0x1, 0x8}, 0x6, 0x9, 0x9006})
openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107002, 0x0)
openat$klog(0xffffffffffffff9c, 0x0, 0x0, 0x0)
openat$pci(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107002, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, 0x0)
openat$klog(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
dup(0xffffffffffffffff)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x3, 0x7a2, 0x33c0000000, 0x20008, 0x1000000040, 0x8, 0xfffffffffb, 0x10002, 0x7ffffffc, 0x0, 0xffffffffffffffff, 0x4, 0x40, 0x7, 0x4000000004], [0x10ca71d1, 0x1008, 0x9, 0x0, 0x7, 0x10000000001, 0x10001, 0xa00000099, 0x4, 0x3], [0x0, 0x2000000009, 0x8, 0x102, 0x800000000007, 0x194], [0x1, 0x8000000400002001, 0xffffffff, 0x8, 0x46c, 0x4], [{0x0, 0x0, 0xfffffffc, 0x7}, {}, {0x0, 0x0, 0x2}, {}, {0x8}, {}, {0x6, 0x2, 0xfffffff8, 0x1135cbe8}, {0x7, 0xfa, 0x0, 0x9}], {}, {0x0, 0x0, 0x4}}}})
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x2)
kqueue()
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000000c0)=[{0x30}, {0x45}, {0x16}]})
pwrite(r0, &(0x7f0000000100)="92f27a58be70edbbd6bc0db0b748", 0xe, 0x0)
r0 = syz_open_pts()
syz_open_pts()
write(r0, 0x0, 0x0)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
open(&(0x7f0000000000)='./file1/file0\x00', 0x8220, 0x0)
chmod(&(0x7f0000000100)='./file1\x00', 0x19a)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000180)='./file1/file0\x00', &(0x7f0000000280)='r\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r1 = dup2(r0, r0)
ioctl$BIOCSHDRCMPLT(r1, 0x80044275, &(0x7f0000000240)=0x3)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x800000000001, &(0x7f0000000100)="026f9aec", 0x4)
sendto$unix(r0, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000001c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000280), &(0x7f0000000040)=0xc)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a614815fff271957d3cd40a87052ffef6650d2e29d42adf810d0fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e", &(0x7f0000000680)=0x1bbb, 0x0, 0xfffffffffffffe97)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x40)
setreuid(0xee00, 0x0)
r0 = getuid()
mkdir(&(0x7f0000000000)='./file0/../file0/file0\x00', 0x0)
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
unveil(&(0x7f0000000080)='./file0/../file0/file0\x00', &(0x7f00000001c0)='x\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000040)=0x7f)
preadv(0xffffffffffffffff, &(0x7f0000001540)=[{&(0x7f0000000080)=""/120, 0x78}], 0x1, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r1 = dup2(r0, r0)
mquery(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0, 0x0, r1, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000080)={0x4a00, 0x3f})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mmap(&(0x7f000067a000/0x1000)=nil, 0x1000, 0x1, 0x811, r0, 0x6)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc1206951, &(0x7f0000000300))
syz_emit_ethernet(0x536, &(0x7f0000000040)=ANY=[@ANYBLOB="ffffffffffff8b923a45ecd286dd61"])
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)="90", 0x1)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x7}, {0x3c}, {0x6}]})
syz_emit_ethernet(0x22, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @multicast1=0xe000ffff}}}}})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206916, &(0x7f0000000300))
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf80fc7b457dbe24125f76e25dc0ced443"})
poll(&(0x7f0000000180)=[{r0}], 0x1, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "94bdce02189eea097b2e95f5736ef07acaa49f6c"})
syz_open_pts()
r0 = socket(0x18, 0x3, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503000000000000000001", 0xd, 0x0, 0x0, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_linger(r1, 0xffff, 0x1021, &(0x7f0000000040)={0x5}, 0x8)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000440)={0x0, 0x0, 0x6a7, 0x1fc80d8b, "f4ffd25e025c46cf0f4e89000000ff64e300"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="b100050460000000000008", 0xb, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x55}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSKBDIO_SETDEFAULTKEYREPEAT(r0, 0x800c5709, &(0x7f0000000080))
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0xfffb, 0x0, 0x80000002, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002009, 0x3200)
r1 = open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
readv(r1, &(0x7f0000000380)=[{&(0x7f00000004c0)=""/230, 0xfffffe9f}], 0x1)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000300)=[{}, {0x4}, {0x0, 0x9, 0x81, 0x2}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, &(0x7f0000000100), 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f00000000c0)={0x6, 0x10}, 0x2, &(0x7f0000000480)="01ec4ba336167284039350a6ed86b2b92139b8c2bac894a21e2ff143ae331f07f2085446823b1e56f6324bcb7cf2d98cabb52db9d097090fc13bd074567d63ef66decf212479b57cb6e8962ec064c4d12bf29f935f3a84193d0d25483d62cd036f3bdc578f672f434e2342f3b6e91cafd0c9058be69c0c851deb090815638783f73265d28771a1c9727aa6f659388e19686fb21403b1286ddc89fe3406340b04000000301e97055c6f61429205eda8d35cbfc7539a98b563b15c", &(0x7f0000000280)=0xba, &(0x7f0000000740)="2b603845da0281eaae095026fa254e6c947303e0cc9c8794a1c951bdb50a3a984eae0a049c13b256ddbd64b38dacdd6a2c80a56f56c2bb48b47e86986c2d4d3be9820e85becebb42a2d295177cd14a71875c3845332589ea0906a93775c43d79296b83942a9702a58155d0430389a209cc65917c835a411e845d857e1003236dfdf90dd4aaf6bf34d5d4342261a06208e7f2adc09651c3b839a535f0cc973cc7ba548a38a27e6cdceeb24dafec46a4436400152070368acf239c9ad1ebe1bc42e406473501e3db8b712d28c1f6d23208c5f01b65fb8dda73208c73dab38b5dd210944bfbec84c1f4b68ef6466a4c914099447f0c53310002977f0faaad422c572f89934a1383d9fec318a7", 0x10b)
sysctl$hw(&(0x7f00000001c0)={0x6, 0xb}, 0x2, &(0x7f0000000340)="5084fa42d1fc32ae3b7f282df627035a6837c4cd97f980a33a7b259f6fff493f97bec3b400576d735600066223769dfbb7625d6aa35dc1e25e7b97f65c8c24a1517f4865fcf2e30afafc26fc5955cd9d70e8fdb0110b4c259551ea3f2d8c9f7a204b51cf06d31be41ccc0faf983149b15f6ae3b965a1853d824144f1529abe38581d", &(0x7f0000000200)=0x82, &(0x7f0000000240)="26e31679bd82b965a461278a95d25d2c4f4cbcf41683374f9d15b89ad30bc4eddca922a9ed5c1e7132c51cd0ba187b6d0f83", 0x32)
r0 = msgget$private(0x0, 0xfffffffffffffffd)
r1 = msgget$private(0x0, 0x0)
msgctl$IPC_STAT(r0, 0x2, &(0x7f0000000040)=""/28)
msgget$private(0x0, 0x0)
msgrcv(r1, 0x0, 0x0, 0x0, 0x0)
msgrcv(r0, &(0x7f0000000980)={0x0, ""/175}, 0xb7, 0x0, 0x1000)
msgctl$IPC_RMID(r1, 0x0)
msgsnd(r1, &(0x7f0000000140)={0x1, "30860ae3120e8fc6a353a9e28e9b4bb50cfc057680fb0c9fd0f66cf9d170e138078a21807f1bf2be27fb2eb07a3702cffa30ae6733ace0d1fcbef40d12e7a49b1bc5dff96f84b8fb5f82d4ef476203273596505d189fc08bbe9f"}, 0x62, 0x800)
msgsnd(r0, &(0x7f0000000280)=ANY=[], 0x0, 0x40000000000800)
msgsnd(r0, &(0x7f00000002c0)={0x1, "53221067059159b9a80ff87027a7c52ce1640083833f5afe064958b2100371b691"}, 0x29, 0x800)
msgrcv(r0, &(0x7f00000005c0)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d1ccec95f8d788c80000000000000000000000f800efff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8dd797306e9c277eaa0b84fef54f0cf75a1d81be79e712a1f816af8cd517fc97c3ca753e28413612d130ab0300ac1c2b308be4de3cdfe0b10dfcc1d1b5e230af9f5ae319fedef49f399c16e7f449ce280d946920ad9215dd85ae0c06920eac9829cce237935c82cb40cbbbed35c94b848340ad34be77f2bd188369ae2c5f59816299a246196ba0d627ee0d608db320bc3c1d3afda823dcdf57b98109a9c56724c47f76195084b8ced6befb63079000000000000000200"], 0x93, 0xd6d0c418f59fe7d3, 0x1000)
msgsnd(0x0, &(0x7f0000000a40)={0x1, "6b05267ac8834aa2f451006ba9c8258360c076e6389c3c1cfd305a7f0380e4232a85b3767372cce8da27e5ff9ca8bab3a54abca82bb3ae0412592e8713e665dd814cc154723b17c624328988f650489c087c23e8dbcd7b3fa00d95d402564899c785dfe4dcd54588453c77a9ce93c78f109dbf456d359fc713b0bf6fe9e506fafc7815123c34b5af41a31630a82defd986c53a9dc8c94a98e961145d517db0ff9db7260153aa66b8706965752d84c8ce9cc42f5cfe5ae044f7059648fcb3e9eb9e1851"}, 0xcb, 0xc97a76d66f3266b1)
msgrcv(0x0, &(0x7f0000000600)=ANY=[], 0x22, 0x1, 0x1800)
msgrcv(0x0, &(0x7f0000000400)={0x0, ""/29}, 0x25, 0x3, 0x1000)
msgsnd(r1, &(0x7f0000000b40)=ANY=[@ANYBLOB="020000000000000018ff1344b10e91a8029cfc558266bab590122ad7edee44d7b8cabd0ffc8d69b9d9989692bb9288be2c08e18a9b25e8d3623c57b05ce7226f9cd4281420a8473b82915c8a12159e34322aa9e4632526174e65019152d3711b975d76644148367922bc6cfc505c3a2c75b49e61b9a4e7d02ae85ce2d909cda5da619dba0b2467c2b51ceecde70370c97ed28973c68bcd40d075d3c0aad19bd2f4ebbfc204ca5a1c22f8c71f3a3ec1d7b75db944f63486147c030d7c3afbd9f626775758c19e378a0fe71436186932cd0c45aef504598ae123ac5ce6571641ccf8dd2f3a315fa06ae45c518032cb6c2d3551cea84aef8068ddd55d7d1879bad1836060ea0a64c613257e1c04dbe0c2cbed42a9961d2ae6fd5db36bf99020b0f8151289bd4464bda5524f35e2a1fa2b4996fcd2ce06149805c353e8cad825969e915897de9521ad46fe9da32bca"], 0xc4, 0x0)
msgsnd(r0, &(0x7f0000000100)=ANY=[@ANYRESOCT=r0], 0xa, 0x800)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f000081f000/0x400000)=nil, 0x400000, 0x6)
r0 = socket(0x11, 0x4003, 0x0)
r1 = socket(0x11, 0x4003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000100), 0x4)
sendto$unix(r1, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37281c18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
close(r0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x1, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000200)=[{r0, 0x4}], 0x1, 0x0)
select(0x40, &(0x7f0000000180), &(0x7f0000000080)={0x1}, 0x0, 0x0)
sysctl$net_inet_udp(&(0x7f0000001b80)={0x4, 0x2, 0x11, 0x2}, 0x4, 0x0, 0x0, &(0x7f0000000000)='i', 0x1)
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000100)="529ac7ecc87315600a6543100f07c95df3287c82518c0461a37e5f30cd2e89292a66bd2d3cf7c41e0dd946a42f4a85799cc9f02756fb514a89579f01f401ffdd16017207f2a3d3c0b4d541dc3ece7e475b8a400c8a312464d18b87911540374731c15f2cb4bbb1563c17541a9658a656d3e478001cf220ceb66aa96f17d5386adaff", 0x82}, {&(0x7f00000001c0)="52474183a9a2d7b4647d191e127b82407d98c3cd48b6", 0x16}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080), 0xe0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x47}, 0xa, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x3080002000, 0x1604)
open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={&(0x7f0000001640)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000001840)='b', 0x1}], 0x1, 0x0}, 0x81}, 0x10, 0x0)
poll(0x0, 0x32, 0x0)
r0 = open(&(0x7f0000000040)='./file3\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000007c0)=[{&(0x7f0000000240)="29cc4c3eb00b2a93a7f75e4c2cf3a9211b3476b39a0afb0d13b4daa2cb487c1117f2f63bdbf7bbe38bb02865c8e1c9455b824f83aa3d068796b4db934a15aefea000a0", 0x43}, {&(0x7f00000002c0)="16e7d43f55c9664c81e67707af1c41fdc9ee774dd52667245f9a8670315f7cd81853e0ce1ffe4140cfa640ad319f1c30fa682580b3e9a50d8aaab74de28625283c76e4674f3d27bb81ef21126cc3b718608eb42b8e305928b9dda07838721167461cdcb989ac2b29d8f52585e3a009371b00785d7d649e84fa1a7393af61878818c604e7d22d0675c22d2cc41c01", 0x8e}, {&(0x7f0000000380)="a050843298a9615d130b5d4baa787391cfa118801009752436bdf0865178d5757029b390cbcd45433cf924d9e3af3a146cd1dd740aecdeced49cfd2ed6c088e88709618430046b746e416daba1f63291135bcce3eac58f350751841d2328875548818173a0fb899f474283d7a900fbde3745e2ed8a017d8866eb4989ab4562e826ddc2e3da8e068f9fa9f5a86666d171cff42cc74c72c6e720cbd76f28a1da360cc6", 0xa2}, {&(0x7f00000001c0)="f141274fc85d42074ae3e8ec72910064", 0x10}, {&(0x7f0000001c40)="23d3e5780d465dcd3442097385bf4e18d356251d13d33940ac80e74850f08526dda5662470f5e378ec7c9d8e5f459754fa57acc13f21fe2a816ccddef18a422961389b967c4ea708a58a604b1a02c218f3623394ae6efc75a501a08ad7e4bd1bba06f7a4d4121767fdf3adf788edd7ae812bb349392c801ca073df8ee206321623b92193151b0c154984f8810b9697a4c8741f34ec13cc758d971dea7159691a7485c5acbb", 0xa5}], 0x5, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
mkdir(&(0x7f0000000100)='./file2\x00', 0x0)
mkdir(&(0x7f0000000200)='./file3\x00', 0x0)
rmdir(&(0x7f0000000080)='./file1\x00')
mkdir(&(0x7f0000000140)='./file2\x00', 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x242, 0x0)
write(0xffffffffffffffff, &(0x7f0000000240)="54a43e5f2e", 0x5)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240))
setrlimit(0x8, &(0x7f00000000c0)={0x9, 0x46})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x0, 0x45, 0x1, 0x6acb978d, "d5426d0d43d1d3d32a0000000300"})
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000200)="6e56918cf1083f513d9d510679c2ed5e2c05a6e3502f6d60ebcc83a7f5699f2228e9e8b23ddbc0dc11ac8e09ae2beeb062ecf2b830d74314037c9e0aaf149184ea1ba61e46d2664e5e9917c1b2712f4a6fb3ffd1", 0x54}], 0x1)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x8, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000080)=[{}, {0x101, 0x31e}], 0x2})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fa090000001e328a1811", 0xfffffffffffffcb5}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x23}, 0x4, 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "c99e0400"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001c00)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x2}, 0x8, 0x0, 0x0, 0x0, 0x58}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000000280)="3a8fdf4b0fb8d9b06eaf7696d200ea1c8fb9815c3e65d8df252b3821822741edb988b99acb8670cdf2f3fc5f915000c0851a60206abf5e3e9e2901bba9fa8c367d68b9adf5bd37ae91617a43c83860ce2e55075d7ae8438a0c6e8d00e1f8e6c39975f2f4cc74e675a4f41de06dc6d37a36ba5ffaa76bc204bf5f8df100000000002c43ecf4ab3b659f78429f", 0x8c}, {&(0x7f0000000200)="03000000d943e89274cf5d101c30ccbc23fdad92cb22f116c013d668b8034d22a68007c36ba18f1119d14204000709d87406129e17638e714e6a9325ea3ca9", 0x3f}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x2}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000080), 0x0)
sysctl$kern(&(0x7f0000000140)={0x1, 0x48}, 0x2, &(0x7f0000000380)="4707621f", &(0x7f0000000300)=0x4, &(0x7f0000000340)="33e08db4", 0x4)
getgroups(0x4, &(0x7f0000000040)=[0x0, 0x0, 0x0, 0xffffffffffffffff])
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
mknod(&(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "6baaf28dd9d2f7d54ca9619313aa3bdb51cb8a37"})
r0 = syz_open_pts()
ioctl$TIOCSWINSZ(r0, 0x80087467, &(0x7f0000000000))
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff})
dup(r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0104454, &(0x7f0000000240)=0xc6)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/237)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f00000001c0), 0x2aaaaaaaaaaaaced)
semop(r0, &(0x7f0000000200)=[{0x4, 0xb98}, {0x4, 0xef1f, 0x1000}, {0x0, 0xfe3, 0x1800}], 0x3)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000280)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x40}, 0x40, 0xfff, 0xd})
semop(0x0, &(0x7f0000000180)=[{0x1, 0x7f, 0x1000}, {0x319347b6e55e5096, 0x41, 0x1000}], 0x2)
semop(r0, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x4, 0x4, 0x1800}, {0x2, 0x96c}, {0x0, 0xe7}], 0x4)
semop(r0, &(0x7f0000000040), 0x0)
semop(r0, &(0x7f0000000040)=[{0x2, 0x0, 0xc00}, {0x1, 0x22, 0x1000}, {0x4, 0x4, 0x1800}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(0x0, &(0x7f00000001c0), 0x0)
semop(r0, &(0x7f0000000240)=[{0x2, 0x1c, 0x1000}, {0x4, 0x5, 0x1800}, {0x2, 0x6, 0x1800}, {0x0, 0x1, 0x400}, {0x0, 0x6, 0x1000}, {0x4, 0x6, 0xc00}, {0x4, 0x595, 0x1000}, {0x0, 0xdc6, 0x1800}, {0x1, 0x1, 0x1000}], 0x9)
semop(r0, &(0x7f0000000000)=[{0x3, 0x97}], 0x1)
semop(0x0, &(0x7f0000000180), 0x0)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b10005046000000000000847d7", 0xd, 0x0, 0x0, 0x0)
semop(r0, &(0x7f0000000080)=[{0x5, 0x1ff, 0x1800}, {0x38ceca30e305b873, 0x8, 0x800}, {0x0, 0x6, 0x800}, {0x1, 0x4, 0x1800}, {0x2, 0x71, 0x1800}], 0x5)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000140)=ANY=[@ANYBLOB="fb18"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x400000000018, 0x3, 0x3a)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
syz_emit_ethernet(0xe, &(0x7f00000000c0)={@local, @remote, [], {@generic={0x8863}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x6c}, {0x2c}, {0x6, 0x0, 0x0, 0x7ff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x4d}, 0x2, &(0x7f0000000000)="33cb1109", &(0x7f0000000080)=0x4, 0x0, 0x0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f00000009c0), 0x0, 0x0)
accept$unix(r0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f0000000000)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/190, 0xbe}], 0x2, 0x0}}, 0x10, 0x42, 0x0)
close(r0)
dup(r1)
sendmsg$unix(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x5}, {0x60}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0x2000444e, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff})
getsockname$unix(r0, 0xfffffffffffffffe, &(0x7f0000000080))
mknod(&(0x7f0000000100)='./file0\x00', 0x1000, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x5, 0x0)
syz_emit_ethernet(0x3a, &(0x7f0000000080)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @multicast2, {[@noop, @ssrr={0x89, 0x3}]}}, @icmp=@timestamp}}}})
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000000))
syz_emit_ethernet(0x14e, &(0x7f0000000040)=ANY=[@ANYBLOB="aaaaaaaaaaaa20090086000086dd6055ca21011800000000000000000000965368d41c0000000000000000000000000000000001"])
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000440)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getpeername(r0, 0x0, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x50}, {0x87}, {0x6, 0x0, 0x0, 0x5a}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xff, 0x0, "201f00011900000000558735f187e9170500"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_linger(r1, 0xffff, 0x1021, &(0x7f0000000040)={0x5}, 0x8)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
close(r0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
writev(r0, &(0x7f0000000300)=[{&(0x7f00000001c0)="023f4e3fb335279905623d488d5dcab97c15c15fa76f1b29de72fc82c526063969369780e5b9cb17457933041423d620ebde5b5fc5e3612a458588f1c9f10c25a39ba50826f5347bd6693b8cc8b902f59822aa6454cd8e4764ad3d051f2908e89e7423cfd40c770b78149b0603b1af6cd8fbdacd0900432b", 0x78}], 0x1)
execve(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b026254e1"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x2, 0x0)
dup2(r2, r3)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x48}, {0x2d}, {0x6, 0x0, 0x0, 0x3ff}]})
pwrite(r0, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000180), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x64}, {0x28}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f00000005c0)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{}, {0x5}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x2)
poll(0x0, 0x0, 0x0)
ioctl$TIOCFLUSH(0xffffffffffffffff, 0x8080691a, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x80000000, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffdf7)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = syz_open_pts()
ioctl$TIOCGETD(r0, 0x4004741a, &(0x7f0000000100))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x1}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f00000000c0)="7343c9f13a1a65a5dc41e7000055b9e7c7e61e80a00111703c8ad427b38b2f8aa720381c97827991a34f07000000aa8428b796be7c3b0dec5cf124fe01853cbb22f62d6fc07aeaae1028cd4c8391f916efee16d809a6a0b0b5f077d55f10bf208af9f63d4fed291214c315c5459032ba78cf06e3e6dff86da9ac815ad539c221d0d8894615ff18fc678944d0f65ab486924ff505c7690ded14f9ca833b67bfd24a41b9c62e63182ec537052a6472066ce21532deaac00c6ee5dd8cca0bd9c132e66331f794", 0x0, 0x0, 0x0)
pipe(&(0x7f0000000280)={<r0=>0xffffffffffffffff})
symlinkat(&(0x7f0000000240)='./file0\x00', r0, &(0x7f00000002c0)='./file0\x00')
sysctl$hw(&(0x7f0000000040)={0x6, 0x2}, 0x2, &(0x7f0000000080)="2e9dc3e9741c8c024e1e156b0e15c943a31085e82d5ec906a8c0b03a9c5131", &(0x7f0000000100)=0x1f, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x800, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000018c0), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000004c0)={0x1, &(0x7f0000000480)=[{0x20, 0x0, 0x0, 0xffffffff}]})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000740), 0x0, 0x0)
lseek(r0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0xb, &(0x7f0000000180), 0x0)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000001c0)=[{0x2}, {0x5}, {0x6, 0x0, 0x0, 0x8000000}]})
pwrite(r0, &(0x7f0000000000)="0aa8c8a186e0964033e34b8914aa", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000600)={0x3, &(0x7f0000000040)=[{0x28}, {0x1c}, {0x6, 0x0, 0x0, 0x10001}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x1000000005, 0x10010800000003})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(0xffffffffffffffff, &(0x7f00000008c0)={&(0x7f0000000880)={0x0, 0xffffffbb, &(0x7f0000000780)=[{0x0}, {&(0x7f00000000c0)=""/160, 0xa0}, {&(0x7f0000000280)=""/246, 0xf6}], 0x3, 0x0}}, 0x10, 0x0, &(0x7f0000000900))
sendmmsg(r0, &(0x7f0000000080)={0x0}, 0xffffffbc, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x100000000204f, 0x58fc)
open(&(0x7f0000000280)='./bus\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000008001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x2}, {0x7}, {0x6, 0x0, 0x0, 0xffffff7c}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)=[{}, {{}, 0x0, 0x0, 0x0, 0x0, 0x7}], 0x0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
bind(r2, &(0x7f00000000c0)=ANY=[@ANYBLOB="2d01c5"], 0x3)
r3 = dup2(r2, r1)
bind$unix(r3, &(0x7f0000000140)=@file={0x0, './file0\x00'}, 0xa)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f00000011c0), 0x0, 0x0)
ioctl$BIOCSETIF(r4, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r4, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3d}, {0x3d}, {0x2006}]})
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f00000011c0), 0x0, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r5, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3d}, {0x3d}, {0x2006}]})
r6 = openat$bpf(0xffffffffffffff9c, &(0x7f00000011c0), 0x0, 0x0)
ioctl$BIOCSETIF(r6, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r6, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3d}, {0x3d}, {0x2006}]})
kevent(r0, &(0x7f0000000040), 0xffff, &(0x7f0000000280)=[{{r6}, 0xfffffffffffffffe, 0x12, 0x1, 0x5, 0x2a1f}, {{r2}, 0xfffffffffffffff9, 0x95, 0x4, 0x0, 0x4}, {{r5}, 0xfffffffffffffffa, 0x0, 0x20, 0x4, 0x7ff}, {{r4}, 0xfffffffffffffff8, 0x0, 0x4, 0x20, 0x4}], 0x10001, 0x0)
r7 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r7, 0xcd604404, &(0x7f0000000240))
r0 = syz_open_pts()
ioctl$FIOGETOWN(r0, 0x4004667b, &(0x7f0000000080))
r0 = kqueue()
pipe(&(0x7f0000000180)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
kevent(r0, &(0x7f0000000100)=[{{r1}, 0xfffffffffffffffc}], 0x5, &(0x7f00000002c0), 0x7fff, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000180)={0x0, 0x0, 0xffffffffffffff0a, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
read(r0, &(0x7f0000000100)=""/113, 0x71)
syz_open_pts()
ioctl$TIOCSTART(0xffffffffffffffff, 0x2000746e)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, "bd6d00238dab87000000baef84e73e333248bd74", 0x80000000})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000001}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
ioctl$VMM_IOC_INTR(0xffffffffffffffff, 0xc1084425, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
dup2(r1, r0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000080)={0x0, 0xfffffff9})
sendsyslog(0x0, 0xfffffffffffffded, 0x0)
r0 = socket(0x2, 0xc003, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="9a02"], 0x10)
sendto$unix(r0, 0x0, 0x0, 0x0, &(0x7f0000000080)=@abs={0x0, 0x0, 0x2}, 0x8)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0xffffffff, "090000000000000fb3040000001000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100051360000000000008000602000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282912e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910d78e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d3539772d8c9edc157b0b06bdf84e589d58abd35d049253cbd6a91412dd29f", 0x70}], 0x1, 0x0}, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$FIOASYNC(r1, 0xc1084425, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = dup2(r0, r0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c000000e3", 0xc)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
minherit(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
mlock(&(0x7f0000849000/0x1000)=nil, 0x1000)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
write(0xffffffffffffffff, &(0x7f0000000640)="79b964a5543210dbb35271ab2f3a", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x1)
mkdir(&(0x7f0000000240)='./file0\x00', 0x0)
mkdir(&(0x7f0000000140)='./file0/file0\x00', 0x2d53b6584dddbb69)
chdir(&(0x7f0000000280)='./file0/file0\x00')
mkdir(&(0x7f0000000180)='./file1\x00', 0x0)
setuid(0xee01)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f0000000080)='c\x00')
unveil(&(0x7f0000000100)='./file2\x00', &(0x7f00000001c0)='x\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x28}, {0x24}, {0x6, 0x0, 0x0, 0x90001}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="2eaabad40041708e7847e1b7e237", 0xe, 0x0)
shmat(0xffffffffffffffff, &(0x7f0000ffd000/0x1000)=nil, 0x0)
sysctl$net_inet_udp(&(0x7f0000000240)={0x4, 0x2, 0x11, 0x6}, 0x4, &(0x7f0000000280), 0x0, &(0x7f0000000300), 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x12080, 0x5cf9)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
dup2(r1, r0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000002c0)={&(0x7f0000000240)='./file0\x00', 0xfe, 0x0})
r0 = socket$unix(0x1, 0x1, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1003, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000340)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x20, 0x408})
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc1126939, &(0x7f0000000300))
r0 = syz_open_pts()
r1 = dup(r0)
poll(&(0x7f0000000040)=[{r1, 0x4}], 0x1, 0x0)
r0 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0x8000)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf80fc7b457dbe24125f76e25dc0ced443"})
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "1da6816170c234122ec406a6dac8e01b2f26314d"})
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
pipe(&(0x7f0000000080)={<r1=>0xffffffffffffffff})
poll(&(0x7f00000000c0)=[{r1}, {r0, 0xd1}, {r0}, {r0, 0x89}, {r0, 0x8f}], 0x5, 0x20008)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
close(r2)
accept$unix(r1, 0x0, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f0000001740)=0x4, 0x4)
close(r2)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x3ffffffffffe})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x87}, {0x2d}, {0x6, 0x0, 0x0, 0xffff}]})
write(r0, &(0x7f0000000140)="7c0000fff7fbff00000000000000", 0xe)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x6}], 0x1})
open(&(0x7f00000002c0)='./file0\x00', 0x80000000000206, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r0, r1)
execve(0x0, 0x0, &(0x7f0000000400)=[&(0x7f0000000180)='$./[:\x00', &(0x7f00000001c0)='\x00'])
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
fsync(r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{}, {0x44, 0x0, 0x0, 0xfffffffd}, {0x16}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205602, &(0x7f0000000000)={0x3, 0x7, 0x0, 0x0, 0x0})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x6)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={0x0, 0x0, 0xfffffffffffffffe, 0x8}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt(r0, 0x0, 0x0, 0x0, 0x0)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000000)=0x6)
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x4})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x4, &(0x7f0000000140)=[{}, {0xffff}, {}, {0xfff9}]})
sysctl$net_inet_icmp(&(0x7f0000000100)={0x4, 0x1e, 0x2, 0x2}, 0x6, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000a80)={0x0, 0x0, {[0x4000010002, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x6], [0xfffffffffffffffc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4], [0x0, 0x1386], [0x0, 0x2], [{}, {}, {}, {}, {0x9}, {0x0, 0x4}, {0x3, 0x81}, {0x0, 0x0, 0x101}], {0x0, 0x0, 0x8, 0x1}, {0x0, 0x80, 0x20}}})
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r2, 0x0, 0x200000000000c, &(0x7f0000000100), 0x0)
setsockopt$sock_int(r0, 0xffff, 0x8, &(0x7f0000000000)=0xfff, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x2, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f00000004c0), 0x0, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f00000004c0), 0x0, 0x0)
ioctl$VMM_IOC_READREGS(r5, 0xc1e84415, &(0x7f0000001dc0))
openat$pf(0xffffffffffffff9c, &(0x7f00000004c0), 0x0, 0x0)
ioctl$VMM_IOC_READREGS(0xffffffffffffffff, 0xc1e84415, &(0x7f0000000800))
ioctl$VMM_IOC_READREGS(r5, 0xc1e84415, &(0x7f0000000580))
ioctl$VMM_IOC_INFO(r4, 0xc0185603, &(0x7f0000000040)={0xe7, 0x0, &(0x7f0000000100)=""/231})
r6 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="aad485c4", 0x4)
setrlimit(0x6, &(0x7f0000000000))
mlockall(0x1)
mlockall(0x1)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x28, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000003c0), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x1d}, {0xc}, {0x4000006, 0x0, 0x0, 0x3c}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
pwrite(r1, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
syz_emit_ethernet(0x86, &(0x7f0000000000)=ANY=[@ANYBLOB="aaaaaaaaaaaa7d4bfe795e7086dd6030a50500000000b690622539dc603d96bcafdab09d2b65ff020000000000000000000000000001000200000000000005020000c20498"])
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x1006, &(0x7f0000000240), 0x8)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000000)=0x1)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000140)='./file1\x00', 0x0)
unveil(&(0x7f0000000200)='./file2\x00', &(0x7f0000000240)='x\x00')
unveil(&(0x7f0000000080)='./file0/file0\x00', &(0x7f0000000100)='x\x00')
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f00000002c0)='c\x00')
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000040)=0x7)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x7)
r2 = syz_open_pts()
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000040)=0x7)
ioctl$WSDISPLAYIO_LDFONT(0xffffffffffffffff, 0x8058574d, &(0x7f0000001500)={'./file0\x00', 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x10000000000000})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
chflags(&(0x7f0000000100)='./file0\x00', 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2030, 0x1600)
open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000001380)={0x3, &(0x7f0000000000)=[{0x64}, {0x61}, {0x6, 0x0, 0x0, 0x51b}]})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
write(r0, 0x0, 0x0)
writev(r0, &(0x7f0000000400)=[{0x0}], 0x1)
write(r0, 0x0, 0x0)
readv(r1, &(0x7f0000000140)=[{&(0x7f0000000080)=""/12, 0xc}], 0x1)
readv(r1, &(0x7f00000000c0)=[{&(0x7f0000000440)=""/4096, 0x1000}], 0x1)
r0 = socket(0x11, 0x4003, 0x0)
r1 = dup(r0)
sendto$unix(r1, &(0x7f00000000c0)="b1000501600000900200200007000000000004fecea11ea8fef96ecfc73fd3357ae26caae1c8716e2f35ebb4f00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff3728211ee4fd89720fd3872babfbb770a9f5a872c881ff7cc53c895303b22f31a3fb9037a00f90fb6de01b00000000000200"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x81}, {0x2c}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = socket$inet6(0x18, 0x1, 0x0)
shutdown(r0, 0x2)
ioctl$WSDISPLAYIO_GETSCREEN(r0, 0xc028756b, &(0x7f0000000000)={0x0, './file0\x00', './file0\x00'})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
chdir(&(0x7f0000000040)='./file0\x00')
mkdir(&(0x7f0000000140)='./file1\x00', 0x0)
symlink(&(0x7f00000001c0)='./file1\x00', &(0x7f0000000200)='./file0/file0\x00')
rename(&(0x7f00000002c0)='./file1\x00', &(0x7f0000000080)='./file0\x00')
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000100)={0x2, &(0x7f0000000040)=[{}, {0x0, 0x0, 0x0, 0x97d}]})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x14)
open(&(0x7f0000000500)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000240)='./file0\x00', 0x1, 0x2, 0xffffffffffffffff)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x200, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000002000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000000040), 0xfeea)
open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x20, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0xd1e)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x9)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x40, 0x0)
r1 = getppid()
openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
getppid()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x54}, {0x84}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x10000001, 0x7fffffff, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe], [0x0, 0x0, 0x400000], [], [], [{0xfffc}, {}, {}, {}, {0x1}, {0x0, 0x0, 0x0, 0x1}], {0x0, 0x0, 0x2}, {0x2}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket$inet6(0x18, 0x2, 0x0)
getsockopt$SO_PEERCRED(r1, 0xffff, 0x1022, &(0x7f0000000140), 0xc)
setsockopt(r3, 0x1000000000029, 0x200000033, 0x0, 0x170)
ioctl$TIOCFLUSH(r2, 0x80047410, &(0x7f0000000000)=0x40)
getsockopt(r3, 0x29, 0x31, 0x0, 0x0)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r4)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
connect(r0, &(0x7f0000000040)=@un=@abs={0x1800, 0x0, 0x2}, 0x1c)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x35}, {0x3c}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x66, &(0x7f0000000240)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "979fd7", 0x30, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, "f14797", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, 0x0, 0x0, 0xa, 0x0, 0x0)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, &(0x7f0000000280)=[{&(0x7f0000000180)=""/225, 0xe1}], 0x1, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000000029, 0x4, &(0x7f0000000000)="06000000", 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = kqueue()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001440)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001340)=[@rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0xffffffffffffffff}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}], 0x70}, 0x0)
kevent(r0, &(0x7f00000000c0), 0x401, 0x0, 0xb200000, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x4})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x7, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x1e, 0x2, 0x2}, 0x6, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = semget$private(0x0, 0x4, 0x100)
semctl$GETZCNT(r0, 0x1, 0x7, &(0x7f0000000500)=""/166)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f000001ba00)=""/154)
r1 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r1, &(0x7f0000000180)=[{0x3, 0x2}, {0x2}, {0xe6df1e5b51714bf, 0x7, 0x800}], 0x3)
semop(r1, &(0x7f00000001c0)=[{0x0, 0x22f5, 0xc00}, {0x1, 0x0, 0x1000}, {0x1, 0x1f, 0x1400}], 0x3)
semctl$GETVAL(r1, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semop(r1, &(0x7f0000000280), 0x0)
semop(r1, &(0x7f0000000100)=[{0x0, 0x1, 0x1000}, {0x2, 0xc2f, 0x1800}, {0x3, 0x1000, 0x1800}, {0x0, 0x9, 0x1800}], 0x4)
semop(r1, &(0x7f0000000040), 0x0)
semop(r1, &(0x7f0000000080)=[{0x2, 0x1000, 0x800}, {0x2, 0x6, 0x1000}, {0x4, 0x5, 0x800}, {0x4, 0xfffb, 0x1800}, {0x1, 0x4, 0x1000}, {0x1, 0x3, 0x800}, {0x3, 0x5, 0x1000}, {0x0, 0x807, 0x800}, {0x2, 0x7, 0x800}, {0x2, 0x3e0}], 0xa)
semop(r1, &(0x7f00000000c0)=[{0x4, 0x3}, {0x4, 0x0, 0x1000}, {0x2, 0x973, 0x1000}, {0x2, 0x0, 0x1000}], 0x4)
semop(r1, &(0x7f00000000c0)=[{0x4, 0x0, 0x1000}, {0x4, 0x1401, 0x1000}, {0x1, 0x2009}, {0x3, 0xff}], 0x4)
r2 = semget$private(0x0, 0x2, 0x82)
semop(r2, &(0x7f0000000140)=[{0x3, 0x6}, {0x1, 0x5, 0x800}, {0x0, 0x5, 0x800}, {0x0, 0x7fff, 0x800}, {0x3, 0x2, 0x1c00}, {0x3, 0x1, 0x800}, {0x0, 0x8, 0x800}, {0x2, 0x3, 0x800}, {0x0, 0x2, 0x1000}, {0x2, 0x9, 0x800}], 0xa)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f00000000c0)=""/5)
semctl$SETVAL(r0, 0x2, 0x8, &(0x7f0000000080)=0x4)
semop(r0, &(0x7f0000000000)=[{0x2, 0x4, 0x800}, {0x6, 0x8, 0x800}, {0x4, 0x100, 0x1800}, {0x2, 0x2}, {0x4, 0x81, 0x2800}], 0x5)
unveil(&(0x7f00000016c0)='./file0\x00', &(0x7f0000000000)='r\x00')
open(&(0x7f0000000080)='./file0\x00', 0x202, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x55}, 0x3, &(0x7f00000002c0)="11ab71df", &(0x7f0000000000)=0x3, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000980)="b10005016000009005001b0007000000332a13fecea10500fef96ecfc73fd3357a4e7d7e723c33ec436b36acf00b7804be38854991f7c8cf5f8800297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af630037282102000011720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c500002002fbff0c2300008abfba0900000008e371a7f8343712051eeab71d89e00004079c830dfeda59ef3a", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSDLT(r0, 0x8004427a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x61}, {0x24}, {0x6}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000340)=[{0x60}, {0x7c}, {0x6, 0x0, 0x0, 0x1003}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, &(0x7f0000001680)=[{&(0x7f00000005c0)="f599cfcc6747e2399216086609af39a88f4c33c1189e0a0233072ccb9bc41521afe33e863d0ce9056fbda07a9722a24195888ed421650fc897c95e993ccccdb9f26237e22971ecc733a906c3dd7021d90512be99620ce9b8f899cd47eb3c8a4843902eb9d6938318cfb8874a71cd73bc03bfffb4d30722f7558a84ccc567959f0f8c55c04082be3e81d26e027e580a7b4a53972ad8331aef1baaba9813c7fb4a5000a866969acf01d520106857e60e1f31948653df9357eae6615aab8fdadba56fd33528398337ba4e392855337f845fa11806446c1041e0afd25bdf6bf77cf5ee3ad947c6774dcecce9a601df22c6d5f1a84cffcbca2147806c033f682b05df078234a97105a109b4a66b3c0507308f63d721bb270138be3f7007525773c3d6174e15b08b799b9e2db655799d5bb3ef7901c556acee1ae6ebb2080f17c1d5ffb7b455f77bb91b98163f85e4ffa1217f5dc10256eeb7bfee5ef6427ff73930846220a45c97f7e93831b5326f14308cd9d1e91158c15d192a3a9b0a9f87f0f98318976ae65745dde73f7ca5467f04c08461895a2237cfe4809a191cd8d840a043f20b9d06807098353c1caecd30e2d367f866d424a56f384df821eacd8109fca11d89423c22c0a48b1493b1204fb8743136e5572464e47893a251f3f62cc330574c0fb02c407cde1638ffca82e560ccee1cf2b9af7fc0a1bbf77076c3e5e6fcbfdf0e1d1ba5b37f03fad603afc2e0c5ccf8b367803dd16c5cd024b15da311432e6f394a3aef7e0512204d228bafe1f6ce59cbcc91516a919022d534736873c125732d9f8fd19f5359396f03ddcca16e268d11c410b95cac8361c1a77ea0a94617192d8e699dd0f15205665c4c2e31e1c0d095e1bcfaa2fa1c3f5e81ce342a513cbc10704e65aa6289e8da15a2c12a318e0ca76c8c4c670d74b09d16e1134dbeb3e7986c839424961829a1ddf948fa19a556b0a400136a2990ce0a639909bb2b16f72620abb728870805806b5c9af5c500601c3e154445b8f915985f7787e55783be8ba698a62323fa661053714e363e7babe439895f5b6cf9c1f298de49e0aa1deec45850494ec177dc07d9cda9f3052e546662fcaefc9d6664548192899d86349f2ddca004758bba95f97ae5dd2871e547767eb74b315eb5e57c2c8e832bd7e408321292099f395d79c9d713962e48b3cebf2d6b5af2e6d4c53daf6c09d18176a8c1ba35155a66602acdfeac85d139a9ee3ae8b56bd58e30c20cb8b4c0f207f90ee6466c36954127d394d71e29e719cb3e82661c3d80914e3bf0ea2d639629e943629ee84667ff24b43681168335cc1c74eaef5a3b5312f40e6b20d533797e7cc5fa0c3cf7fda25a46017978ca9695fc00087fe208d153c1a31020f34d9479de97cb03955b222cf776badfdf7c4869b80c18addd678ba3bd1482e92cb2ef7a318749ebbafaf890a8166fbfc9c01c47a284c3e8fdbf819cba98c7d45bd6a59522b796a384bd5287b1f91675d14bfdcfbc1af75241857d082e0b89efa6a90e7fbef4f7a2be34294897b5affe11ee4f19f3d6c1e29152aa5a30ceef8cbcf992ed28c2ecdad21ed9f6e0aac8ed724dce9d792a1042f80682b73785db2ba1bdc900a9e23975d83370e8af7de5986796b6c060c4da7897d7c82663fd07083f91eb0d8cf8de1137bb91d9cbbe3fcdb6ced2bdb7fa08725745f3ea61e65bb422c0c94ce64902538d2b2ca5175b6bb84f0615394f12a7b707dff11ae2b9c7cfada21bd2510c7157e58c6b5a3b9cb8b3e11e7e23858504584ffd6996dd958c9cf32d3afc5151799c4102fea4cd5ff17c3127cc94d1e22c49ff2423895fb37909c884eb333fc9a46ea026478aa23b04246f509ca5f63ff5abe8c9764c9d4a598efdb6a3818d47c090f864a8776e3f5ffa3ddde8892ba22e800219a02309894d8d48f2996d08b72e112be013213737a7bd36afffc0428e24e889cebb3f5a352c5b7610d3033da606b1466d923c4d3000c109c92fae472060f0c0099226cd2caa741b936d9bc993d76d823610259f2dd41b9e6eda9fdb4c25496c2aa2659f086dabd6874f496678b43d88b2ab54142fe4073af3f9723b2032b7bf63ba5d6824131f78420d0e48365913ee9d2018ed7790e5f994bce611d4738d94651eb9952b86e51778476867e723ec9fb6fd8d4b1316a21f28f957703b41d4d79514f3b2397fc6afaaa5495accef6940fcc77dcf602cf2d35e19e834474886767179082db25bf77103c97a3dc1428b070af66dc25729390a46205b8ea6f49ef78408668bf1288bad2c571911748ee97afaaa49397849164e653a3cf1cca02cd1ad7ccebafe0c0294db452278d832a9a6dc566d3971abfda774df901d35f94737c376c148d61fa76a1749eda33ec21f25ea512adb7d2bbc636bad60718a7569bf2d9140f38fb99015315c0261c727f7674521820f68f5dff9a425c87376bbac440b69e2d9f05a86c503f3a35574950aeb304b6ada4ebd7bb62e036ed323aff0eab6c38415f580d077e5a149dfefe6ab49253fe977d4f83cb4e935ff626f5a7b3292a15e73a6c83862e874dfa0728fceff033b2c4e1ef1cfccc4e4f55b0183f7483216aa369630d995ef316bf4a59f2e13e1ccb4a5e6653d16f0274f00421a928870a1d3034ff31c66454271ad074236a0f628d5c57d6d59b279c128fc3dbd8d0d9d8309fcb382129b3a987906953a415218371e09be237b5330813b626d841a721c28f3b793ffa8b44af56dd7ddc5f1ace03be500a7d15d8452a17590d2bec539c50d5bb3290b73aefcd8b618a09ca14ad34fc9789a4cf04015f29e9185b0b0c623857cb83cc46cefcda7636f792801", 0x7e3}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket(0x2, 0x400000001002, 0x0)
recvmsg(r0, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000280)=""/93, 0x5d}, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x6)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_cred(r0, 0xffff, 0x1022, 0xffffffffffffffff, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x7)
sysctl$net_inet_ip(&(0x7f00000011c0)={0x6, 0xb, 0x2, 0x11}, 0x5, &(0x7f0000000180)="613393f6e7c821f962d05d25aa800201670f73711545fa211601ed3092c7e6e58e883f7ade5e5ac659948c0b37536c0c9442d751d4ebd900d85617d70ee23a14d1b51a81a45c118d9f2bcb859a333d5e16a7b2da390a01c80aa8ebea088ac2e10100000069bba80100000000000000b3eea53d6ea1d205cd4c312eb5fe8523758226399d0c7e29d48a588ec59354669b3b0595c4f6759cdd4ed4abe07e760f9a897bf1eac5fd34746328350000000059ebdd7d38222b59ed7b50543390b22d05a06f15fed41ca9066efba1768e55ff967eeac9b7f7a0fddb2f24ac7ff3b1e8ccfe8364acadd0696186e075c81d2e915ce8b927da134a15d632952b10a15ed6316953c1921d39be50da1715e5009d69561a59b1bb8d375d9db756b240e772d8945559516d97ca2e94e1db0ecc9b1ff88f0561bd4924403cf62f064bad07148cbd22c04a7536e21f3d4382871153560abd4e47fb5e55f93e35f55186c8890b902de9737491178c", 0x0, 0x0, 0xeb3194968c3d41cb)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1d}, 0x4, 0x0, 0x0, 0x0, 0x0)
symlink(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
unveil(&(0x7f0000000180)='./file0\x00', &(0x7f00000001c0)='r\x00')
open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000200)={0xffffffff80000000})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x1})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x7, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x7)
sysctl$net_inet_ip(&(0x7f00000011c0)={0x6, 0xb, 0x2}, 0x3, &(0x7f0000000200)="611c3d799d7551683393f6e7c821f962d05d25aa800201670f73711545fa211601ed3092c7e6e58e883f7ade5e5ac659948c0b37536c0c9442d751d4ebd900d85617d70ee23a14d1b51a81a45c118d9f2bcb859a333d5e16a7b2da390a01c80aa8ebea088ac2e10100000069bba80100000000000000b3eea53d6ea1d205cd4c312eb5fe8523758226399d0c7e29d48c2e0635a1cad4f878c09fb9e98a588ec59354669b3b0595c4f6759cdd4ed4abe07e760f9a897bf1eac5fd34646328350000000059ebdd7d38222b59ed7b50543390b62d05a06f15fed41ca9066efba1768e55cedb7eeac9b7f7a0fddb2f24ac7ff3b1e8ccfe8375d0696186e075c81d2e915ce82b10a15ed6316953c1921d39be50da1715ae3f829e561a59b1bb8d375d9db756b240e772d8945559516d97dd2e94e1db0ecc9b1ff88f0561bd4924403cf62f064382871153560abd4e72fb5e55f93e35f55186c8890b902de9735a52ec0c878b2dae0c04b27acf46db06d58ca60fc89fcce97037724c9bc931bbe000000000bc22d276f3f5ba701cd9f31dbb2186c79500"/429, 0x0, 0x0, 0x0)
mknod$loop(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x2000, 0x1)
link(&(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000380)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')
rename(&(0x7f0000000780)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f00000008c0)='./file0aaaaaaaaa\x00')
open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000000)=[{}, {0x2}]})
sysctl$hw(&(0x7f0000000000)={0x4, 0x1f}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e000f87622036b0001000000", 0xc)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
r2 = socket(0x2, 0x3, 0x0)
dup2(r2, r1)
socketpair(0x18, 0x1, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000000)="8e", 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0x5)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x21a332d536087be, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x4}, {r0, 0x40}], 0x2, 0x0)
pwritev(r0, &(0x7f00000015c0)=[{&(0x7f0000001600)="d3", 0x1}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x1}, {0x44}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
mknod(&(0x7f0000000000)='./file0\x00', 0x1000, 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
r1 = open$dir(&(0x7f0000000280)='./file0\x00', 0x2, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f00000002c0)={&(0x7f0000000240)='./file0\x00', r1})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, 0x0)
sysctl$net_inet6_icmp6(&(0x7f00000006c0)={0x4, 0x18, 0x29, 0x3}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x3080002000, 0x3403)
open$dir(&(0x7f0000000940)='./bus\x00', 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x800090d6, 0x0, "d700060000000000005b00000000000000e74de4", 0x0, 0x4010000})
write(r0, &(0x7f0000000040), 0xfffffec2)
syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1021, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{}, {0x87}, {0x6, 0x0, 0x0, 0xfffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x49}, 0x4000000000000007, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
sysctl$net_inet_gre(&(0x7f0000000000)={0x7, 0x2, 0x2, 0x1}, 0x400000000000027e, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x31}, 0x8, &(0x7f00000000c0), 0x0, 0x0, 0x0)
r0 = semget$private(0x0, 0x8, 0x0)
semop(0x0, &(0x7f0000000380)=[{0x2, 0xc, 0x1000}, {0x0, 0x0, 0x1400}, {0x2, 0x0, 0x1000}, {0x1, 0xffff, 0x1000}, {0x1, 0x2}, {0x3, 0x2, 0x800}, {0x4, 0x1ff, 0x1800}, {0x2}, {0x7, 0x2, 0x1000}, {0x3, 0x9, 0x800}, {0x2, 0x3, 0x1800}, {0x3, 0xe99f, 0x1000}, {0x4, 0x3, 0x1000}, {0x0, 0x4, 0x2800}, {0x2, 0x0, 0x400}], 0xf)
semctl$GETPID(0x0, 0x1, 0x4, &(0x7f0000000640)=""/102381)
write(0xffffffffffffffff, &(0x7f000001c4c0)="792ce3a37136d7faa009633425dc0f9d6752fe12f9da1a559b6577870c05486493b781904b68e998ade3ca4d60e5da0559eb43f5da125cfaf1df2db99ec340e1b472ab11f490bfb150acc338590f3d25bad466e1704cb375a41a712d5c458800f8dddef7e4f3a06e29b73e6bb4d297e212c3e6d78ee21b2e6ec7e3f4d899699a60e356ced86e73f88a749b61322723b37a81f6d1ae129a3b701ae956c8c65abfe47e6c06838f74a44f281243350317fe3a95b450321fadf5c83ae62cbc4d430585a77630d9dbe195e30948a25c415b7d353be5039030b319ce36e998509c00c86d115bea9891f16e05300a13a22de0b73fd7b89f32bf1f787550e276e787f3d29803b7f83c2720ab2f63084ddb08ce859f130e4a079c39da689bb71da3c91bf8932164dae39403668b20c67e690cc420b239606a57984f34bd89fd11744cd7ee92ea04e6ff3c6948f83340b2f463604c4662fe9c732e41fc4d463519ac8f272d4736402d06a58828ad44f6ec7538922f4500fa56738b5e52dbf4d1fa497c6316eb44b49252a48c80ab374b15e887717582efe94ce4fc36be4eb170b53c3528c9b55d32978536b025e860493192e3155c324936dcee8694c2ecd8eda1cc8382e9c782e2221432ba7e3832dc22528886d3d753e8960648030460e4639596692e2e465fba50989f3e727f114d5178909f3123b9dead42f70753100b11478239672aa2d41bff3e1c70bcd77f14f8c5aaff78a8f5e1cc5c06628b6d58829b53c3ba4ad6001d9d1fc17e15d0aa8ce5352089ddf94dc27189c06373ebdf53b0e9f9eed45f4ff9adc3a40f1607701d564a2183fc902cd910a079694deb696a18ad946dc4806c3f0e39da81400f46214f6f47f59eb5533c8891995c09b8f8addf85231b5c7da5e6d55bcc77a247b6062df53a5b90cf8908f47407163791a0c80ac7393335addbfe26729fd09da0befe232ae05099f611a2ba987d138f1ccc53e99fbb9a53c8e71bcebf004a7a9a7bbb09d03a6676c260ec7c83f8888956a6a6d4adc624e706fe769c10873ed4b3737f9e05b05285f70db97a34160c45f60f86f1a1a0a2e26727d0dce8d8ab578630b9060fbd1f94e6f0182085e693a095b261a9dccff3a2d5e9505f0297ec661b9d0b9b6a89ad529a7201ad9af94e5a0de57f22cf9ae9f6ee54375a037a2f68a3f6f2f5ddcab02eac18ef2b6ce21c8566eae704de3df41b0048241637c12598b1f4b746207c1a0cd88fb647aa73b5e9dbc664c4198a3f88c01a2e1f1d9500e82a7e7616ec4943cd95de103d6daa00342b45c77a79cd28ed7bd4700830de4f0f6bd253bc6d27857a97ac787155b39c1ee4d6d8aab3520e97d0363fa86020a5c0d312b9c92232ebdf53c33d8e591e52266e6b760485335051bb76f33f210f7834c1a1bc8f5c1f3ca6d6ef4a5b9847468d862328bbd42e7a08e229a43fb0e53bf4a5404018d5b6fdd52c9e04bf637c9b982f73d1858992a7e716c9122c524064cbd272d5b33262a2caf5bd8057a7059b4c4b7e33a20498b8f4b255c3994dd96cce6b5b37f8b36ecc0cb0cc027a520bce4074b53d752e45f55302c7172e48dc9ef3047ae3acafddb59c11e214801d9a4c93f0338f0a09dac6f00df52cd6082989859368ac99e4a26838d5828cb8e08008ecc77e2f600fda1ea31d8f8bd3e17dc108a4489b119cfcc9b91a55e511819a2b3a79d5ea0051dbeb06e6d7bab24cd6f3ab64052647f2a8f0c4eb0cbfd142bd2a5eb880f13eec3e8e69853998985bd0c09adc75986c3fe0c420dbc46b674637470ad589ac83c88f54e982d416bdfb69e5e74c8447539c24c8701984e43cbcc47458c1f82ae0d46f09c5bc476cc3bfaef73514eb7da2bff008694702469624dbc9ba727946837a5649a8c86b5991dee6d94fc0ca1d3952571eaf016ccff8b0591e63ddf24dd9995651333481a9ea52fe6254702dbb4d324ed9dc431be79c14db7c34a66624bf7ba27f8a866241161809016731fccef994b62d60630ea2c7cf5783976b138a7b34ce99568c0ddd90330fe9ace2ba730c2bc008cabb0dbaf7b6fd0227f38b4d794f9018ca013e6b1dd2a732dc2e301b0b7c6be7c4959c1e652501bfcf72c9c93fd23131b0de1a2cdc6643a555dd2f97dd87300c2447957606435c04ecdfa4f0113968cd3ff54ccc9c459547b3dc284619ab4e0aec95fb466b709787c9bd242ffaa5abbc98a7592d445d6284486a7ac14c12c04f5d89fad6c5e212d466a08aef02d51041c4e03b4267a69d108b5c2e08a2a2b6fd1f9813c7f2d924735f72e986f167cd6000d9a5ab80ddeb4ec4a31d0a2290962b7dd138400d67ed9692c842b245dfda35f0a555483add78a74fa67c1a56221e6d744806b9e81cb4564de6649ed3924cabdfb526a7efa03c25eff2a20c3dea00db4a238e872f9b02505d61202fa25220130b91d33b02d454f107a2d738fd56042c29ac16bf88b81e3a2310be3b25e6425bf4b9c173268eaf8753371e63aee23a6a06336bd20c5d6f2f6dd30c8fa7b8a5d429b3d26857840464a9357bb68c0e0321e794da5d4e3671d0c088c72898f59754f0c350ad92416343eccee36c21a3966fa8a5662a6f0287c13dc95cb8ae8ba129d49622923296a2acb0db38f72fe299311c884ca2889feda7e51508b2d032f6a080f4aed0c25bbe0cbfe4ba3d23162f3d7a84bde1684452440fbccefedf929a5ce8c53dfc00bc5f22aa7d45e47235e8447df3ebeb6d16a7331ac9edff94ad6bc735e0a19797cff5031618039df12bb0fcac4b851e5749bf9f02e13f7ca5943b340fcccd79da2dfe56c08a1d6e62f12cfc5627797050de5189976ec697b3a0d68fb38cee2dd891ba866b1fdeb8cfdb13a3be025c88437d1246f266c29a977c7b91f61410fb061c461c12274619ba67c1708f376eca1e54872c583482f5051c26cd232600f72ff6694efff0a4d3a403dd5be98de1b5d3940132b5b597223f52d4474ec2d14391b2a7dc9e6d613a40df97c5706a0d28d68e9a8e8c54cc4a3097b0d8266855e8e81ee6a0840e5c225d1f3ce2367b3b1a4159e387c9e413b5e2ced3c8e5d3b1023ecac99aae897e1b109fe1f7e6e29c03de1290c5c3356d47b98377ad24b83553910a27328f49d18fa14baa8b6049a79470bc435576009ef504be8bb0d551f605fc4397834220ec610c3cbfb905470cdae0c7c4901f023d634e3c261ca1ab6fb44dc986a12f13881db9d20d4bd7b945d7498ce7076dd0beb24a1c86f258e5aa4b297fa302389573ff859857c6b4859b4097086ca47e3356e96cdb7acd016273719e73458ece2f6d804c0949507a39e689af4e308d5ebd2bc9b9e75fd115ab000844821b546ad419460579bcdb7ab995dea000e214afaafd08f2e6b3c17942d364acdbf343c3070a585834b8986d00c6a31f59eea6294c36f417f5732da68629ce5ca49ccf55ad503458793d7b6d4bfd2104c68b07640a422da6b2c8b68e0875ce9980bf41a1fb2c9c2a52b49436715bbdf37fb20d4898fba2c298ec2a800e1969ef2ec80239303dc10c34fc053593335e494e87a640111eccf3a699a87e43430c9740ea6f0e65e6b0993680a40f8d97ddcbf4fca06a1c131dca259a432b7951dda63f8ffe1f716db0ddfb19333dde040194e91ea5e478121994c0cc679282ff756e955f2c2f9ba1b9da98d7d0f47bd2c34ff0fcddfc95c621202299fddc6867bf1b69d59cac3d75fba5cc8a1ed47134438656c90ebb2c6fb0c2810bb3ef8bb59cfae71cf43bf6c4b76fb45643f8e70df2b933074b7846fd10757f02dcbf6df8f69989a8ed97cd1fdbf8f4505cba873bfc625dd75285edd08f872dc08b3e3e8f8ed4aca22a74c545820d3d7839219b304b5c340f4b966d455d33da2bdbfc8b5a44dcda20371ba4ba59c1ff7b22a6bc295ac3d3b592e74ce51f8fdffce1d8e796722912e93630bf2c2f6bd59e7f8a1a46221f2805ada57baca03310e3a55d45e129e8f2268ce93cacb56874b1fa8cd20dc19ae99405ffbe66726e69acef8534e300452b6c61c5d0d91e56537ad2af9f5de03d33267e2f6dffeff501b5797c642f1f8cd6912cf185b27ff32e6d70b6223fb04b0271426c6befc0182cbee3047fbc5dc2295801a42e2cf4491a47c0398bcc4ca4668c9a2792e03093e98158a56b58ab66ffc2d67ae5c64919bfcf7c15a6f7a744ca1606abc396d8c257c66608bb8f7f7f7eb141269e33d50944083a44d41d308fb6c052d571cd3868c0fef17cf4ffdd274f77d353a1c69d91ec7399dbc32f799eca8049835199d24e352d21a8cf8a85ff60a30b4037803e99cd5b692dd6089b39b51a4d4172ecd2e1348b2c8800e8dfa3de69a7a9d218d7f3e8a41a16b803d4a99aff6f25e5df6f4d559f3109a63cfdf063eccd363c7f72a66b6ee282b9922c4fccd2ac72e39ccdc5effbf815f2acf42028a335e3161d982676a6c5071441fa148976fc4904a27ca48d7198ea0800e5efdb0190b3da1bcdbc315ffcc108815146e0ce3c79b647c0b51681da4238003ae20393bb36c94cbac86aba76d3782fa89c63dcbf6b4106e76bb3bcace47c6512111a67c34421be3ba44d769bd73cff16d6488d6c6e954ff65614f7f7c6f9701717abaafc4899293decbf04f6d889e6b9112e9af01951b13b1d93c83207d4b9c80246b81817a22d91c79f92e7a9e4ccede2d7b228f810c7076b59589aa8bc65dc47dbdf8e1386f5fa10596dacc7b84c4513b22a0014ee2f29b21fc3a1a570fddfaec9d21b13e03ea16ec97746ed320100d1a6f5d3191b5c0f52a24c47c9192bbc3f3802026b9e1308a2636f86135b15cb1c25d648b190b751a80a1c030ae83d29ed3b166038f5b90bf15687fd6bf8ca9acf84e1004e1a4c8a366820e430a409c67db342930d53d49e132fe930799f183e6e0882c22835b942bc7de436da0398bb7e2d8fce441dc0fa15d82373cf242db015825cd42e33e2d66763307a1a9499c23f2f4a319a9a8999119ed431829246157ea346d28f29c9bc266e17a96231354cb8d40515f6e09acbfbec10ded8fe7cfd93184d63dbad7bf0e98d059aa339644b6c9319d9c72ce6d14b74949d6fc5ecf492082eb4ffe1be5eba0e4beaa1c79673948b857ea1da8cc13825b89f64d4ea9ba92f13566cacb28dd69beb201578b7006074b2350b9a59f5dcad0c7a1dd2b46571598643755870f43c2e90f787fc3bb8aa86cc7f1ac967aa394d2432bc0dbde7c4edaa3b4618640760918790a4a8f6bafdbb51173dce8df2f5a44ba39dc3d158934f60b477a9219a8e0f28fe2325943867113e66d946cddfb86243eb4a1cfeac45f52ad8d2e79a658887710e7bcf0a1d8b161310ae4a019e08c760f2839be5e0a30e6bc4e7e25a3faf9378176983cd2919115f7d9bdbe39127e0e4eb2291d9d1af95947056ddc7057eb899f45b3e108d18fadb91859ae9203d5f28ddd6a24f3b94f900876f16223ce6436b5a97e0dd04a03d04764d281eaa1dbf0524738780a8a22f7c9fcf5216a9540158c0731e9dede162482af99a3d792d68839405e1659ad8139caa46d3a2c54e3548b53682affb4051e89aca6b52814eaadb58b0d3414e6ad586e16e4a151acafdefc9a3b9ec6cb09708bd1b85b9d3d1d7d775c3a5a8c761f38e6b3299dedf9542ec14bcc98011919652cc528ab2c3fefd963b5a39f1b05e3d59eff6909776e9d5664dd48846ebe75c5663ce8cf28bbb0f85dd074569d9a16d17612afad4370728ac91968c626393e5a17818bc806e6e99c4f1b782c23b329b", 0xff5)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x23}, 0x2, &(0x7f000001a680)="46ce9ed91f62d0000410f3c1b19af02ccee722872a4524ac59df36cab95c0c27753afff5b007bdc163338fce23256ef17bf929a5005541ff105e2389ce4411a8dc73deb833e4123d644b32257b1e6843beeb65f38d2866e690290bd301f0ffffffffffff606e85185fd3ae1a81c1e46a75ef914868b1438738ba5044a84000a9026f38167346c8d3fd7fa1105cf04943c95f39b76902ae73a57a159020646ed750bb8ae5bfebe8c128dc181e5f7e5b8cf420b28841dd73e7001000000000000086cdc380889f6c3b02ddfa038c52c617e7ddb7a670b9c114404a1bff2a3911ffade823181ed78cf44ff44eb249fd169b752fd4fdb888c79df163b1e4b0c8cdf7f83c15d81bd46b6c44f99837c08883a7e67d25e9680faaf9559179bed9d9fc9519ea79c7b29fb892be4135fcb4d4b620d0f9acbff1f630cd211166813793d095c36e0e51c72385f3099d782537f37c918a276c7a4c050ddb8177d47b56895a15ca71f73b9104b68b8668fb340ffea8d770b254e4571cb357d53925b9fc40ed35780f355b338e0a54bce91769f9e98c651140b4d5a72fc49f7ef4c7de6e5611a8ac63c983a7aa4f836b94e6ef25e7000000000000000000000000003e966a6be9c3e72b0c89adfbf6644e7a9fb7020d3573f0c77b3001d8b09a18885b79e876462b9f8278f1d057f74b743c81bf8b47aaeb863a18547e76301394549687341f542184cb7f23a72939259b2cf7c3f8d2ba3d6f912c433c20df49a40fefab1b52566a3c41860390a92e1fbdeaa05db7cc0dbea0a1802d7813716a57d1a732c6059b866d606cc81928902e88852c07671b1660d24862d0b623f3273403a15824c743729bfcf8f3f5c5683e0517c40ad6f442df0da0ac6fc33d8a4b11248c60a65610cca382b92e6122", &(0x7f0000000280)=0x287, &(0x7f0000019640)="68a7e6cc16ca78004c847f7d8db118ce61cd322ce39705283158bed8dcd264bbddec7f41ffae4804612e88cf44ca9eb75d3926b5f76d8d69c9015bafe2d7af4840c4d2ce754ef5b3e0219d04a7f2bba1e1bf4f6cd2f09d3f88ae08981deb81380b2f8ba60e22500221c1e79bab14568c452b284a7c00348ee79082f07579d031d56facff7e695214c57529ab94d82f58b5d6ec120ef2dd3874bd860bd78f487fc5c01fb394d403dd679d2fe7033f7de2f174820e049ef74e9976ba161258b0ee4c57c5d865f9000cfcc98188d4f12de3c8645b5ff5b928b9df8214ed2e1bf0b750ec77f93f9d1afb838359f4e6c710d29f91a4e2c8667342f174e7d286705f2c357753e17090c1e35e89c3c7a154d4dc65548b821d776dd6e0cb9af87ff0345fc3aa7714b6e65a27d695b70ff22f8186a708b969462319a27419764dfe901a265d7b9c393ced1aee483312febe14fc8a7739989998f3176fd7ce84c3421cb944bd0acd96664a67482aef7ff097f1371113d78b24cc6c9901225e7a18b6a747f412c20cf13f574cb206574da79ab799792545d0f4a7312136080b854771581d97022e8299a66e87292568298f275143bb37ba4032917e692570f8047c2917dabaea2f009c0414a36af2e4093c64f7fe82dd9eb05c65ff364cfa366c5800335eba4a2811db2fb1d2920ab22e6439e7856c4cc07af7dda80db89748466b053f5d070e90339ca509e220cc4307b60f37e424767c46f1ba90f3923d019ab68953efc509333cae0a48bc918339abd0c2394c408739c633f250d7d621003a126432c51150b9f3773653712f8f02d81c9de51db4b6b076ba0c42d98173ef41f968016065325d05af6410f363f4564b11755aea1a11f036b02d6c7398a1e8c85cc3fcac15fd93188bd21471fa30aa2ebee2555ea42e3978045eccc015b36107307f4a00d93df71445429ce50258a5102d3a2df9ff56529a558944ae10d72e00477aa88a4feac2614f55e1808ae75893b0917fa41fe612797c9c39a28afa74d57eb63528d3f1899e2fc6b713fbc123e10131308c1490373a1fce0de9465091be06f381664a2d6c730ecbe0867456c72f4aaa82224f7c919e383a9c577b97875a1a0fbf26307b3b54acebba9cef5bbe896fa8b736b8296592316832019b149727d9ecb3f09d2cfe3306d00c4b5b5a7af421da740565a2f49cbbdeaa9563a476d45e8dfc11532c0991e64bcd25370b36b7e34e3dddc6babde02326d1e7d42f8e4fcb02a9baaec58805ed9688934a2d59f8ba8b4482541db7fc5faeecb7e514a6f72c4efd7cbb20ca72b5f179ba0849c55fc5447cd47812d691ac395a0cbcd0dc685249f371b70923e801a0f08dd900571f9838b2b99d2676e62dbceee3a3e961714a57d874b6e1f304b8d3e9eca8e2481c6f364fae4e18c27d4660e8ee1c07476b525a4fc78e4213d0e18e55360faf7a20e557373d9d056d9f058d3506f76c5a445861843c0c7e7c2da34cdd074a5fa0c4bf110d4203cf2c98baab21079625c8178aa2067281e03b41c2e23ac40b4e42fb60c071203c5da7f510aedec5e3a3a0e5f961387c13f5d59041c4c0cb62a7ab2de496712188d073fb4b773e606338a5ec41363d02e7f9f66cea6be2636b0eda5e11044fe941e78bbbf73e6e5fb44a686c5ced5055d20d112e33b2201b46e7ecc394116cb2326b96847ef7cafa2ef88abfc8c6a5e106d61885f6bd2dbd00131ceedead340e82a840d4166d13892ec2c8b4e294e5c7b6303f01b9422de9134d172054a0b57da2ef5af7628594fea009e9789cb93729ec452d08c0e346ea47c21774a57f544e1ee1dc695a253c660e3db0c19b8ee287286066bdf4e60dbfed4f000ad5d805a4231ff7f8cadcee6802bf341f7af43ae1338d6d5c5f71bf1dc9b906c949ba2e1a03b81a5a0fcfcc80149c0c49efe226bc4da8d1b5c0f035521baad3ea907c8387f9a4940ecbde1c3b62e902c63da4bca19dc4251fe606132dbb22c11594f96cf52c4d8c28d41c6c7e770235b44bc5f4ec163f1b918c048ac9b92f3f8a46d1f2b6f8c7a9cc0dde4883bfbdbc4b509123c0df3cfc7dcf4c1d020564b06d0d05a46b9cc4b63babc7a402ec8e62e8f08767e2d30bad2d6b9e015db787ba833f271739e56e2d9eafa5fa12fdba45446f511c719d6c620fe9dc3f9d476569aebc188eb03cc2d7d9411bef02bb242b6abe98f00b5741c9de094bb21666655d48fa4d477fae9e469479e2cab1a94fc14a62ad4cbb851623b0ccb3c8964a4adba456caa388f9f50bb4bd4860db25102459239882bd5c1789fbe96505db308fbdf2a12720c4c1bb48d643f5f72cbf76ee2b4c15b8900675d1375aa9e6669b02aec1d74be6c071ca5ddfca080da953833a4cb79b6beed6678e4f0b0ee178cdbafe9674bde5314abe342b406ac96d6d5a1539eb7f4e06acce58eb91cda9aa2b6beba1e1c2b3ab90523748f40eac813aab51f1a44b088c649330a0a78e2a1010c7ed74ed3f71a9ca5ec7a56cd9fdb6a820f31770907ce947f69c6051d0c040faea7eda56dc044fdd33f1e006b4d66d95d225317562fa94865448cd976fd8ef16d7120918f2a673b49816df4c1dd5e0d82f193fdddf14feb0eda0ffd7a7ccddfec16f15ea182aefabe00d863560b268a033c3775d8ef390ce27fdcb970332a89d3d85cad977e6a09e3980e42126cb1319e050588083af482428793cbd51b5e57cbf80c30451b9dbaffe6d9b144811e825cf2228b6122c1a4dde26bc518926d763594d8307e6b56fa7f1c29d1ed88ff615da03ee799b7933da1b74f51c75bde4184044a5b56f0747aa40dc23caae090c942d3128459e64516fb6711e4e083b68a3fcc0e5ea114035d9f1bc04bd130da212467b3839e22c88e9e6f054a9f6a3b7e5e7aa3ae9634675b1f1c3aa3e995380feb735cf6c799ce38bdab1c12d24b6dcd24540333719cd96583c90f4943c71c57e78c0029180d47a7e62c6817e4d7b8c6893b35966f419fa6cdd8c50a5d1bac9984305df66078ac85b0deff66275fdface09e28090ec3e3052a5003fe78606edccc9a84363ca833ee26817892fd33c327976284aa5c8035de76bbcf31f87025f343d09e371510d3ff2d3af394fdcdf5b33017378f3599a260f5892e404b1654be788b3711b89a3128630edac0224b4bf85d2cca416995f3bee22f2b7eb14a27c5a066b91ba6eff0dbd7c7836ccb015a40f42ee08732b5ed51bcc953c265cb28eecdd48788056ff179d6740c7139e8cda5c11dcf0f51f1377461d1131bf72631011fa2096b93869f24309dd5577e8437eebe95817911f252d36c222a6cfdaa5f0882337e47e3fa6322a89dd3783bf4c818d6bedad4202299fd94608325b7c0323ad5caa23eae3de61f1093edd206612b4b4161e5bbb6b5667fb4deef5aa39e393ccf43a57e28a80c4859b7297aebf761cc5ded2c035093d38d7f84863a705b4156293b6052abac6b34b876b939aeb650020a352f4fb9c40b81f0059e59a01457f8ff679d106e6e9684d0d242ae7375a03896d528ce80229424b07150ed2ce38cd9fd52b4d28c5e18463685eb008a1f91e3bdbca70e2734aaf0c2a188c7fdd993b74316ba51a9f45abb44289e6c64d3315e839614f5e36c5a6085f055fa0a394a1b460ff558b6f52ee92bb132ea77c577b5056a0d505c1801333202ca70481f4d8ac1c129db5f67194e4a8e071ae0b4248ec490921f5bf757f24e68e9a4b90625ec709a4268f6f8b1285c99ab128ff33e90a5896948731598f52b5b37d7673e6106ce9e58ad5584fe142ad0e1b196116b6d90c277171b021d901158f06d41f85f49586e163400a83bc9b9d7ac6b122911da815ed1dfa44b53391d7d7106554b5fd442683b9fb85649bc60ef6bb5a98fb1488717609bd45173c0d4e4d0bb0ed2526cc31b0b5576c410caa639144e20f01b244c820d0152b22c9ef1f5b7e370a002d5b45be3bc1f0b33c82bb6467695e39cc3ce7aa5effb5c5550347268e982b981b9e9caa7dd802484cd7f50c088354631974810516efebaa41118572fe4249f9b282bce75a4405ce3d90ab56f9581925031b9b2586c914cb8c6179e407ce609a2f2635c18a78f5d612e664aea138d76bbf70a4f99a46a3e6d23097fb59248c0f45e42bee50ccf0249d982f0a2b66baf2d1fac40077aafa8685a46553ddf6bf519ed910f1a5b117e24b62de4d12c620a113608233246669aa5c03a3cfd643b5463f795339d701d8a5a63ccfb4f7767709691462823e2452ed60e8b404a1f53e2bc808c3c958933e099cd87fe6fccb270aeb049ff1e6bf95a41804f3c7384997246445fbce4a8bdcde6d515ab3f6699d0b21d1ae39bad701a9d23cdba0fe69aeddc8c3b5b9b045e91e136327710dc6f22f2188f129937f02459453ec03ded6bc2103ef9fce2ddbd1700da96f357ba03b67348d4c2ccace8eb5db68a838d5d7f1549935864040250fbf80ceeb60ab5402768368888420c03e9bde71ebc34e45cc791f1f70fa850a9992798d43c5a2d67c08609b8401f4124c0457bdc5d70eac5a939eed0c62b66d1b8f6a2d84d601e939083ee8e232e66426960631f07998a2287f691c05a9c393f17c30269d55f7d5779f519710e7c0085a67f85bf04d2a6b5c887552faed579774332f0df7d01e020bf405d42f353a09b5180bb26786ba1c501b9f71e50331f1435140b57fba795bdbb4cc7a3218c88e400f6f45296b184a1e9b50485467353f3b9d6fe13397fb9664c2663939be77f55c262b61b13222fbeb19fb5339a780ffc10ef0f8e531e6edd86e66cb0f70e458fe9a5f2d05ee6d85a068c37a4c88eeba81ca3797cc10a13410be9181dfe53ff5eb65c3c7f579f9d6ed1d141f13055d23098a72d37ef347a2a8e6eda12586e971b4daa3ffff4208ebcf2e8a1c82df45fae18a940313bf2f28d15eae3a12922bca39a8f1e4128ba56283682e73a96daadb3b387d6bdf92b1700ee7ef0e159fef677ac3adc722f77a84e4bc837c8561b4b02cf70028661bb3e55e2d8239537297511df9d89460d4c6dd54aace16c6d5e651eb007aeaff794cdabd647c21a40a699b9ba055e38555fd2ea11a2e1fb30a1aa134e74b3ff921e18f5488afb3ca938c872c9b8623e5d58c3ac22ccb2a029252b6fff9feab47adfd4905d61d9b44206615a4da13e3bf1d7b7d123db461995afcc0376cebbae0fb570a91c23971b30660ef6354071338cb794f11d881463b902a2813fec9eb380240ec050fa60549a78560689ede5fd75cca04c4abd58d6f5261bae1c28413347be2435304bcd7484caed3afbcf23d3be996207d6b93357a52b83b8ab976dc44bde0a7d806d1b7f8ca2de87719aa692f2dd77ee92d075f099baa89f9f350f08dc4cef68c4457087d7a7301b583b5c6ff75c70e78fd1b8f9c0adfb3e088265d6b62be00e59392b048ac3a0de8f2326b63981c7559278081cb689d5b18ccc7f3397c2b75cdf2b23e07741274092c423c8d238c3d18cadaaaa0212a00bb5ba97df303d8982fc701fb5f3d9b07ce0bab653201c94e64a448039e7511aa67e58f939d38785731deb7e77a62e83ccfa7b11425f94676a6e10a8ed9e9469c8cd2353aaa3462de127812bb05b286874f3f607a1e5bfe2b050162e0acc9a6476e5896c390bd4aa3722a77e5fd53ab5f5541ef8a01a20083ab167e08603607309ee653be4ddc1da7de0eed731aba48a4a04e806c563d93cdc5ec26e4bc56ed3773eaed285d76d08065d7ce2ce5f20c5e649cf2503395b09e251761dae7c274548b4987c928fbb48219c195fa", 0x1008)
semctl$GETZCNT(r0, 0x1, 0x7, &(0x7f0000000500)=""/166)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f000001ba00)=""/154)
semop(r0, &(0x7f0000000300)=[{0x0, 0x2, 0x800}, {0x3, 0x1, 0x1800}, {0x2, 0x2}, {0x0, 0x9, 0x1800}, {0x3, 0x4, 0x1800}, {0x0, 0xffff, 0x1000}, {0x3, 0x0, 0x1000}, {0x2, 0x1000}, {0x0, 0x4, 0x1800}], 0x9)
r1 = semget$private(0x0, 0x0, 0x1b2)
semop(0x0, &(0x7f00000002c0)=[{0x4, 0x7ff, 0x1800}, {0x0, 0x6}, {0x0, 0x4, 0x1800}, {0x1, 0x7, 0x1400}], 0x4)
semctl$GETZCNT(r1, 0x2, 0x7, &(0x7f0000000580)=""/160)
r2 = semget(0x3, 0x4, 0x10)
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000140)=""/104)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=ANY=[@ANYRES32=0x0, @ANYRES32=r3, @ANYRESDEC, @ANYRES32=0x0, @ANYRES64, @ANYRES32=r2], 0x109, 0x403}, 0x406)
semctl$SETVAL(r1, 0x4, 0x8, &(0x7f0000000200)=0x1ff)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f00000000c0)=""/5)
execve(&(0x7f0000000240)='./file0\x00', &(0x7f0000000480), &(0x7f0000000280))
sysctl$hw(&(0x7f0000000000)={0x6, 0x8}, 0x2, &(0x7f0000000040)="e601d433d94810211110347414448e14fc0c05dbaf", &(0x7f0000001040)=0x15, &(0x7f0000001100), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x3}, {0x7}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000480)={@random="27aacea59068", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "804c3b", 0x8, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x0, 0x0, 0x8}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f00000000c0)=[{0x4d, 0x0, 0x2}, {}]})
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{}, {0x0, 0x0, 0x4}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
clock_gettime(0x0, &(0x7f0000000080))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000400), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000040)={&(0x7f0000000100)=[{}, {0x8, 0x2}, {0x2}, {0x2, 0x5}, {0x101, 0x81}, {0x101}, {}], 0x7})
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[0x0])
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, &(0x7f0000000280))
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x14}, {0x5}, {0x6, 0x0, 0x0, 0x1000}]})
write(r0, &(0x7f0000000100)="aac229f665e50e69ba24af8b103b", 0xe)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
write(0xffffffffffffffff, &(0x7f0000000040)="a4", 0x1)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x14)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0xf4b, 0xfffffffe, 0x1fc80d88, "24180400000800008000"})
writev(r1, &(0x7f0000001440)=[{&(0x7f00000001c0)="a66c2dcc193a8f951931f490fbce15d50243dc6a995fad241fcb780b903b75a020852e25924060a0a88c6457dce6a761ff1df5eddb61bcd5bb5944d6bf143612526ff110e926a7ee2cb24d6d211a91f49a36ae05e6432d31aa478f7f5ccab36b6c78f6f20908", 0x66}], 0x1)
mknod$loop(&(0x7f0000000000)='./file0\x00', 0x6000, 0x0)
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f0000000080)='r\x00')
rename(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000040)='r\x00')
mknod(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000240))
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000002c0)=[{{r0}, 0xffffffffffffffff, 0xc}], 0x0, 0x0)
kevent(r0, &(0x7f0000000000)=[{{r0}, 0xffffffffffffffff, 0x9}], 0x8244, 0x0, 0x0, 0x0)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{r1}, 0xfffffffffffffffe, 0x3}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000040), 0x80, 0x0, 0xfffffffa, 0x0)
pipe2(&(0x7f0000000040)={<r0=>0xffffffffffffffff}, 0x0)
fchownat(r0, &(0x7f0000000140)='./file1\x00', 0x0, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000140)=[{0x84}, {0x50}, {0x6, 0x0, 0x0, 0xcd5ef4e}]})
pwrite(r0, &(0x7f0000000180)="f781ea80e21bb6f576c45af84a28", 0xe, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000140)='#!', 0x2}, {&(0x7f0000000000)='w', 0x1}], 0x2)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="0909000000000000003b52cb261ec3744b80b0aba0ebe1c1251f015bffc2200a", 0x20)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
link(&(0x7f0000000100)='./file1\x00', 0x0)
sysctl$vfs_nfs(&(0x7f00000000c0)={0xa, 0x2, 0x1}, 0x3, &(0x7f0000000100), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000340)='./bus\x00', 0x3a0914c44f7b202d, 0x505)
ktrace(&(0x7f0000000080)='./bus\x00', 0x5, 0x122, 0x0)
openat$pf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x10005, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x6, 0x4, &(0x7f0000000100)="f6b0aa32", 0x4)
setsockopt(r1, 0x6, 0x8, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000040)=@abs={0x0, 0x0, 0x0}, 0x8)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
r2 = socket(0x18, 0x1, 0x0)
setsockopt(r2, 0x6, 0x4, &(0x7f0000000100)="f6b0aa32", 0x4)
setsockopt(r2, 0x6, 0x8, &(0x7f0000000140)="a0671d0b", 0x4)
writev(r2, &(0x7f0000000280)=[{&(0x7f0000000180)}, {&(0x7f0000000380)="b23a95d33a1e936d9c926e87cf0c78439e655ca47db701c56dda29eca84b1539b8f248bec2aa41b27146af9a21e39b664b990c9af23a3c819e6b9dc62fe1da7182c43dc6bcabc5851609bfc653ce79fc2a0720bfb0068e4fba9294707f8c7ceee849c57ac57eabab5ab347d00292c90622f1868a9013caaf67091b1190b4569ca1b9729821350be325e9086a9bd82b460237274f99902d500df092f573be47f9c3b92998a0501d20267e74b9a883c437b1676e209c9fd04968eed4114fac8415ffa83ce68992db9602a8835da3dddccc5db6", 0xd2}], 0x2)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000300)=[{&(0x7f0000000200)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797bd837846cfdbba859a657606a4c33fdc9a86a4433ef2b4f9eb720d5957f57e0e6fac6a38df38d42eda66c61f80a4d797389f725f530e00845df5b05887b64ea59a7d4b6e6911631c385ed9ea16ec8b6f5f7e68de03d39c36e154bd766a953733f58e15f0edae546db4c96c43b184ff9a529ef3ac73619f92097ec99ca7493545c99895bee4cb38c2ed35734", 0xc2}], 0x1, 0x0)
r1 = open(&(0x7f0000000140)='./file0\x00', 0x8, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000180)='W\x00')
r2 = socket(0x2, 0x1, 0x0)
bind(r2, &(0x7f0000000000)=ANY=[], 0x10)
listen(r2, 0x8)
setsockopt$sock_int(r2, 0xffff, 0x1001, &(0x7f0000000140), 0x4)
r3 = fcntl$dupfd(r2, 0x2, 0xffffffffffffffff)
close(r3)
setsockopt$sock_int(r3, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
r4 = accept$inet6(r3, &(0x7f00000001c0), &(0x7f0000000340)=0xc)
ioctl$FIOSETOWN(r4, 0x8004667c, &(0x7f0000000380)=0x7ff)
r5 = getuid()
getsockopt$SO_PEERCRED(r1, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r6=>0x0}, 0xfffffffffffffe88)
fchown(r3, r5, r6)
r7 = socket(0x2, 0x3, 0x0)
fcntl$dupfd(r7, 0x0, r7)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "0000009e0000000000000000000000000c00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
open(&(0x7f0000000040)='./file1\x00', 0x200, 0x0)
r0 = open$dir(&(0x7f0000000140)='./file1\x00', 0x0, 0x0)
mmap(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0, 0x8010, r0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0205609, &(0x7f0000000040)={0x1, 0x0, 0x2})
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000180)={&(0x7f00000000c0)=ANY=[@ANYBLOB="00002e2f66696c67300018456e774a326b4c6e557f72127df9e79934e03fa7b57d6a839ec9a4fdc2ae52"], 0xa, 0x0}, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x3)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
close(0xffffffffffffffff)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f0000001980), 0x1, 0x0)
kevent(r1, &(0x7f0000000040)=[{{r0}, 0xfffffffffffffffe, 0x35}], 0x203, 0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100)=0x1)
setpgid(r1, 0x0)
r0 = dup2(0xffffffffffffffff, 0xffffffffffffffff)
ioctl$WSDISPLAYIO_SETSCREEN(r0, 0x80045756, &(0x7f0000000440)=0x80)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r2)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000480)={<r4=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29f", 0x70}], 0x1, 0x0}, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r5, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r3, 0xc1084425, &(0x7f0000000240))
sysctl$kern(&(0x7f0000000000)={0x1, 0x1}, 0x2, 0x0, 0x0, 0x0, 0xfffffffffffffe9f)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x20, 0x0)
flock(r0, 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f0000000080)={0x0}, 0xa2c4a4fbccc5c5c3, 0x8)
r2 = socket(0x18, 0x1, 0x0)
dup2(r2, r1)
recvmsg(r0, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x2, 0x0, 0x0, 0x1}, {0xc, 0x0, 0x0, 0xffffffff}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0xb, &(0x7f0000000080), 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x0, 0xf4b, 0x3, 0x1bc80d88, "64da00001d56e7eb7cf5a5ff9a15978c326f00"})
writev(r0, &(0x7f0000001480)=[{&(0x7f0000000180)="0d5db8249d78cdc1bf8c", 0xa}], 0x1)
sendmsg$unix(0xffffffffffffff9c, &(0x7f00000014c0)={0x0, 0x0, &(0x7f0000001440)=[{&(0x7f0000000040)="2870edeabd783a746c4185925bec23245627155cd354d8bc73a9e3fcbc630235dde5636a1803299faa3c96fefca100ccf68d85056fb42f24176f61727827cc7adbcb98f43f917ab9f3a40ddb967034891163a4fd5e42c5a0f95269dfe092ca1344ab84058732042b9f76a8930521a2104ed7c1d73158af8d6ef8407e01186dec43de9c953ab91a2ed9ccf3cfbd3393316a7cc0041753da020cf58f1b8d350e07a7cc8d94a5fcfe389da93b652684a23989a07b94b705c5e5a455949d00f65cd201fd613a7a15032f2f6a9000bab81e3613c2a79dcd3e4b8093b2d7a4c32feca58437b3734fb8799069a8dbd464b8ff3e38938a4379735f4090c6303b56122b6c9bc6c7e1f272532db03866feede3e98a1247fa57ab74bd0ebbb35476b77cd216908cb9736466e8baba0bc4d563fd3e040cc02ed6e0cdfa82f8e34d29b800fdd43e06e2fa014eb24b76089ededbfa59e7d7421aa58d2f71bc5fe70dcca9c8b9880cdf11b4f74e59290a2e6fd53a726ceba71d2b94a327a53d806b45e61ca47ee49b975e377d3f5cb0f9702dc170c1022332e09ac6538b1c2d275f42f4c081d4f75ad8d0ddedf0cc17ca449f915c1def336457470da14bb647b8e9f6b8bf428320e7b14feca47cc8e96fff3f10710bf62d717266591347c14d490d26e8a38639bd07e1cf3c27abd814f151180018564f1a3d88949cf0f90145cc03b68186ba5077a9879e1da40e5896c5a3436dce8287042249b5bc132a14b10d4984ae4e7a00002e1c5e361592e74a7e81b0405395289373a138ad5ec3bc886c617c0be8474108fc1804d1025620cbc66ab853eaaf26791a3d5cde4ce56c84f4cd749654cf648258c42cd71dc7a4ddfee57968b8cced10b0223ce508d00acdd3ed2b2c383da749d00b874498ccc65079176942881d717472209cceebad64731f5ae2a6e83d3cdeefcbd4da57cd450fd2353ad965f40ae55affd62f1fb391c66933238024273034dee95cce40a57655bd617cfbd96152d2f8a8898045540a36e2b07e4129def73a0b0e07a1132d759b1a3ddde41c36b7a43ac7e6e78f01aee509806da636265df7e6ca1d3998ee319fef0869dd0529f4da17241b562269f3fcec594043642a548801838330d1165a431c9e4a3f666cb113c3d6ba2c7560137bfc5526746f3f3baccb27148866d75325314836e0c4e8867a9c875c7e446296b092f6a50e66161cc23fe21e4f50a0121dbd5acc617a3e5a77c9521cd74bdbd55f90535b31e48be37e9c6a95a14bf18bc5d998c38e297641b409ffe16811934fbe34c741224285f1af7a551b9d1d9df8db7cbd39bea14125132009716209c88aa5f6df01e4a11e7d17c56cb4cd7e79dd9bf5d9085ef29112d8510a7e00f5e5e3f6c1034799a05846ee6956df06c0a20503a70f72ef032c022a1aa6d55a2d2f5b9dfa5d329e65f02d127c0ba4d4d85b197829a812c9c3255ac24e2ed7bf844e36ea88e5c0281f894adb8b6711c0d2ea5263fc2c6ab14f5a58c6fb68dd97779364a4504387edc6a02ab8f59312e1d74e7504cd14563fcbbf5a78355fc30582671d2029e8c9bc9b14602f013ba4008c9cf95b54d5647491ed1b3e0bf59e327a6451206742091adc7a3f89932889316a332554e14193afc70c1e571fd517f464087af82655a3acd66a7f3ecc0ed0e4be315f1529ec1b2bd269fe0cecd73368838a22eca8f0548e8e05c47dd84de18f2aada6888a7047bab08f2f2e390251bd9e8847b089", 0x4d9}], 0x1}, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f0000000500)={0x0}, 0x10, 0x0, &(0x7f0000000540))
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "01000611aef6e4c611d53300"})
setsockopt(r1, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r4 = dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r4, &(0x7f0000000200)="7fdd1dc4e7d1995ec4ea6545c25c1a1d5a2c534b", 0x14)
sysctl$kern(&(0x7f0000000200)={0x1, 0x4f}, 0x5, 0x0, 0x0, &(0x7f0000000400), 0x64)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x35}, 0x4, &(0x7f00000000c0), 0x0, &(0x7f0000000180), 0x0)
r0 = syz_open_pts()
syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000000)=0x32a)
syz_open_pts()
unveil(&(0x7f0000000000)='./file0\x00', 0xfffffffffffffffe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504441, &(0x7f0000000240))
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfee6)
ktrace(&(0x7f0000000140)='./file0\x00', 0x0, 0x1002, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)=[{{}, 0x0, 0x0, 0x40000000}, {}, {{}, 0x0, 0x0, 0x0, 0x76}, {{}, 0x0, 0x0, 0x0, 0x0, 0x4}, {{}, 0x66d7894d57c55563}, {{}, 0x0, 0x0, 0x2, 0x4, 0x6}], 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000000))
r1 = syz_open_pts()
fcntl$lock(r1, 0x10000000000009, &(0x7f0000000200)={0x0, 0x0, 0xfffffffffffffffd, 0x20002fffffffd})
sysctl$kern(&(0x7f0000000000)={0x1, 0xb}, 0x2, &(0x7f0000001080)="62a85a94", &(0x7f00000010c0)=0x4, &(0x7f0000001100)="d99c3207", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x25}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @remote={0xac, 0x14, 0x0}}, @icmp=@mask_request}}}})
kevent(0xffffffffffffffff, &(0x7f0000000200)=[{}, {}, {{}, 0x0, 0x0, 0xf0000006}], 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x5)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000080)=0x3)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "f81d08200b0f05000300000000000000d2cad800"})
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x74}, {0x54}, {0x2006}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5b52f60f48f6ca230", 0x97}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000200)="a5781d1f", 0x4}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
sysctl$kern(&(0x7f0000000000)={0x1, 0x2}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r0, 0x0, 0x6e, &(0x7f0000000080), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffffff, 0xfffffffffffffffe]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x30, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000180)=0x2)
write(r0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000504600000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x0}, 0x0, [0x0, 0x210, 0x0, 0x0, 0x0, 0x6, 0x40004080]}, 0xa0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
setitimer(0x0, &(0x7f0000000080)={{0xc600000000000000}, {0xffffffff}}, 0x0)
r0 = socket(0x2, 0x3, 0x0)
connect$unix(r0, &(0x7f0000000140)=ANY=[@ANYBLOB="6c023f2fac1400ff"], 0x10)
writev(r0, &(0x7f0000001440)=[{0x0}, {&(0x7f0000000180)="0f2bc89e28860a7472ed72a6869a2193763e955ae892f4f8b3c2ce33e612", 0x1e}, {&(0x7f00000001c0)="6e696b3db1274ee148c0b839d373be9e6eda7d5c13a1064ea08d83e88c3d041e9eb1fccca5382ff3d5f5e252196109975512f80376a8bc6fd6ed98456fa897e8f094005d7d49cc307a4da80a85b06f4a79662c62de62be48f49956a0d7bc9e09e400c33679fc40782a24b37f401a3401fec66f725cc8e7b7d2568d15336b8002ceeedce25a0692f829c2f38e29ff0a0013f4f8eb983db3391f65721f31745fdc008f464bba00e0f2f969fb99504fbe7efbdd24c7881020b1fb3471b8450e8948bc1ccb93735131b0e61b3595d9a124193fa47a20a999fc76f770b0ec9af8ebaf3517169202c139105f7f990149d956279cd018610d915f03ebf7", 0xfa}, {&(0x7f00000002c0)="3f04a75b1f45c27296af454b490222c48fefd6baf6a80e79c5a13df3a718f71f9434665d4dd070786913198a77a1d0cef74b27b06c253074769bb359f6dac10ee5b27d4374ea543befa0901ed37b2ac8d052b30877262e846184da2c6ecf204030c7659b3c992dfd4bf98f5e3696ca", 0x6f}, {&(0x7f0000000340)="34a91375a20da084", 0x8}, {&(0x7f0000000380)="ba7664d50ebb4a75dc2152e5cea5132c1e4bbaadab8062265c5ecf5c0bc52f1987f66bd144b41209d700ee718dd151ccf52842b60a84344cf245b8e74ef40dcf507846f23537d7ee20b44852268e1962ec71ca3720dcbe97048bf3cbc86287a058ba0bc4cf57accfa0b41059bdfc6737e8264cbf96540e74155b712d38c0dbc8b61ead6edbf1b56cef841d37eef427fce41f3612c598fdb71c", 0x99}, {&(0x7f0000000440)="d93de2151ab9e28a8e93c4054ce624a82884b9a813b7b83b425eef050818a7f386a5da14a74f15ec0776ef4f95ab3ae9e88826ff7d75e3a25cf0129e5c7c4933a3635e52b98dbb66cbc11c73b5d856d242cd9ae5a8a294ffcf363f1998accf0499991ce11e2207dd0ea6e6a5df1adb3c935aa884ebd4d86d6d64ce437852c90e55a592ac718df452959c156283d9a79b8c8613d7240ca7da1cbd1a3e6cb563acba826ba86ccce5e1bfef85f8826b42f85f7919c14e90937af434fd47617d7d2f09f8e5a6781fdd4a3aefca9b795e9186c4f2257b2e82b915c4e34ca5bb38f1c392dd61da56199aac9c96e455c0cf5c521f3229d762066e6bedd6cfda3478943de9ea2998b52a2dc05b82b5c84b9020e6fbbf5818aaf45a06a4cd373e26f67f211e1ff46a94adb0a4eb787605970bd9e003e5247415f1286b14a44708b0a23d9aecd4809a7fecb5e749b70f5861cf12f879efad61747e1daa3d86b7c5812e9df7103675b2dcc836c9662c8410891116e21d2410beb2ec1a5305be130bd6549176f0ec508345faba8aebb0d9b898b3426a9e74780dd73a5e46059d143b91073a2c76b8f4e2f8653d6f3fb7fab36c6a015c3c18b8017245bdee166802fa9b900bc6ac0112d337b366a2363fa9da68b3634bd41d2503f233cc89ca9c1626f949ba9c4cf30bac729953fec9d7ed24a6aaaa7f95e4aa560299cfa531d1e3d69b78b6ab462d017a4fa9f9a3a2c4c5f08e7dcabacc024c150a7f614e9c1a1ab9533f5be9498260b1d2b5b92a5e519cd7f236503d1d8656dd91e16d46f8f9ec8c61093811a0dfaeda92c35f035dca11378cceda2b8f14d065bab5ff7b80476f2c281619ec365b04c71a5708ab3d018c04aba6d7c57bcade886a9503792755318d2830049c88f839b80dc7d921c9f40da8b7113d79ff16eccef9299c167bfd4f3535e1e6e5cdfdc7329b92a02a082211c914e7da7df9109d9c858c640da57fd193603c6f9bf658f9d7247a23cb95fd0ee21577cc9f20", 0x2d1}], 0x7)
write(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000380)={0x97ac, 0x3f, 0xffffff80, 0xffffdf7f, "0804240380b5602800", 0x5})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be6c0045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dd31c0dc31b3c0e35ca7", 0xf2)
writev(r1, &(0x7f0000001540)=[{&(0x7f0000000000)="503da72740ee668e9fc036cc0473c779de44cb983353bdc17c80f31e676b200e21ed10d651f8eea166d969c72269c6bd21a8a369ce967a555b557d3bd937736c552c6fd83313a4ec", 0x48}, {&(0x7f0000000140)="a93523a106a37a1ae96f97d309779d56e8f1631d14699d84105d01426f385b2b06ea73b9f9d22296b3f34da7699804361cf2bec4a1817ec1fc0f6243c9b18981c64b2a67965d0e4264b6a5f207efeb7a9d6e09469d344972320e1f52be99bfcbc2ed5d551c122d80fb4a7dea1a1eb720744785e33ca1f7f5e7e4e155a07dbf02884c9f1cae3e1c8f52cd0db6de43039c8efa4f897720cc5f20b372937b2220574c8c778bcf9988e0033bf99664b26fb6ec6c21a1c07f561a687be730e881d7d18dbcee51bc1ff3628d28fe85b08416217dd722685db24e9fd8cba137a819e450e388f3bf8d57", 0xe6}, {&(0x7f0000000080)="f7be5629ab564be7eafa9b46867485e295dad5027885c750ed8d7441ac6e2cfbff1fa1e82981e0adbb4f0fe8a0ff3da42aa2d45c54a27b381f87e454e6f36799e5f3d35762b39b0f0562637e4748cf5ba037b0457c514376c31642faed4093516b22a7228adb2c344df4d4019e3b83a7bddce1c011f5c345d7", 0x79}, {&(0x7f00000003c0)="2a500b08ce6fc8ab780a9c585b3aaff9b4898d04be7121f04a6bb4f3c1e87cc5f5823de5a5064b2decc0bd4d4e1f0b72938bafae10bcbce98652d26e7118519f1afdfd870a8b64d888f5504077c753be418ec467faee562cbba2383a545bc341ff79719fff52d3a8f2bfbef997e64a8563d778c9382b275af4cd40b259be434ef1be6295fa0b51249a14dab6056f3813", 0x90}, {&(0x7f0000000480)="4ad05d9c4d285ba595a3511db1016a8f0ebe8f3310504f553e3c07d6c47f53956de966ad40a9604de52083c8ed2c6b9870f8e76351dc48c0e3a43f960542f6c9b992d4943a31cc7bbd78cc208426eab29ef11b8c0c9bfa8d1dad368298265d18c664100e703771856257eefb787cb824f33e90e5585e68deb4a9f40a552eae61690a10de0006a31087cb1533811b4a3c805166dbb9f58088a7b28689fd43b77e4c47e9cffcb76d860978b6b66981d5ef655403c3abd500f04077b7b585e98d2f0ca65d4afd9853b73d9ecce365ecc1890ee4e40974e3dc177fbf37986538f560558054f25167f4470b99b39589a1f22a45d7ee0a6a5e905b3a01e236631a50de15078e867345f261672e913ed19cb8ef314466c14fbdde4812b645119d071bd20af5ef78b4e821dd112d8dc4f5b8e1e1be6b6e173257cadd250789e17a3ccb4363112f907d352d2b49d663cded5e3e6fc178e9fe40907cb365b81412a6f8accd4878c71e0491624a7aab21772f06535818e606f938f2181e41b166d69b77b95d4e9513ed3afadaa0fd3c8d49f0489548475cdd6841b4a2b216e92e947bcaeba5ddb702ac580b39f74bbdf481773dd709ea6fee0c59d8e3a2d3fe4cb754a4057cad4235c3d4a19846c7366348e0d6f496fe7b6e09c20735ac12774ec698ef79c5488005d554e098cfd9673bfd8de775da065ccb82de112542952ba526281451a13b0194d890bd30ffcd535382516018d7e9a16b670f699633d82e8150805f601fe9209a927df1f6199d6d7a86111678bad15cc9976a73a71b74f8771227cd30ab19ea92c90b00977c36e44bd7d2399e613e6e63542b9110e431b80f77a4d85489401f1437269c361157b6d6c5131088ba8cd8abd86e54e7a1868281e6f7829cc0427e9347a2983ffd3fc47a54030710295536f964c983babd443dd2938ce7124edb1bf1189a0d2f602be20ab3bb667d8997f8440e47539856a481408da7d5569d6efa7d5f43cb5e46f9c1f9c4330dc8f8ba3d278d213d7f4defbc40b8768e385da00f5a6f106618bc7728abcfe2e32cf917ae5bbfcbd4693f825ff139414566c221ef2bc8394c982fba4ce10ea7fd04b7f53ec53fad90ad71768509d86cde90257d46eb69ff6320d3d2a412e73d41e96e03ce290e765a0875d9d405dfbe0677ce3b264f1aff5fbbb3b5288d00b41af7768207f4fd289c5cd27e191aaef22217b369cdf5d69a1df37712c2f6e0135cc785cda6e063f405e548c79cc873cc2fe392af7b505fc19031f8cf1446769fd8645b2cb196c69f6c766505c9a5f590ce4a733b85c6769af7ae0494a80ac0f1cb56d3c6c41931a916c194183ff5b0ac6733c6015e16499faa9d1301c6f96f94fb2d6e8475f3b6515be99ff76098953cec6a178dce2b6288fbdd7c8a7c0631000bd77fb97e779b23b2192ea6a5bc269acd8909b1195ab4ca04cbb5fc77e06977184e503110fc85a01dee6c2e070335b2ac79b1f92a029ae146d674a7dacde9af60758fa1ec4fae70340f334b206babd6b70731ee09a47def0f2ae2dd15d293c734e17d2a06a82d2c25a151df5a4eb51506bb337817e084ba589b2925d19c6b0f0bfa0055d53404403050cba69313773f747cb1a667395e55ea1f82b64222d98a45eab4ef7812bbd368aba5f046276e43d0837d45be7ba1c636e8ac4951a1782e2f3089e29ed9f108aadbe0bca320ae99465497f2dafb40cee028184aee0509c13421bf9ead37859d864c249583adf0f231bb70ec18bc954e9159296472b59b9c4c16c04d8732f4e543b822ba5d109218135ab29aaa9711a952124411b159778c18572002f527a207635a70373c04883ecdb6bd4bae6f3f50fe64e82e27cb37437aa434ef21b823d76193c630e8924378e7be159efb5b5f1488f1865bf6f04d8e03b11fdf4ac86f52594eac41cf4e13d92df6026d56cee27ac5555b9dc168f5b21abdb0713d578d1567c912ae2ea9084aa0317747403e336860032270b87b50b928e37f7fd076b0b85e60f857bc21e1e0011425db3e93121a8e1c61cd7e47dc883b46068539bca33e230966c663ff939a46e39956d875730ffb22eea3b383340b60cc223ed33eef46b329f0b51362d138a4c7d03bef0cf815ce7f1b38dcf94cc9568ce82282de5b02c5f65ff20a67456ae7c0514514feccec94474ccbc41d015d6fe62176b4dc40286ae72a84066105dfc0de597f193ec74da4765c3731ac98836c3b3a323f65c09b3a9e5c49c6bc0824ad9132021a83411fb5dd1e423719934f4f67789416977f112eb7982c181d8a1b180d62383afe93731e0f4b232239c52c287e281369db6f0069318363a0f71ba12ccbb6db73d838d4ecdc71ec7b185969c02fd26496efae6877944f43db635107c05ad189234d66bfe2403e0d2950e915c17f3f2b846cc037bcdffbbb1b335de2436ebe1d65be77fcd94c2e3762cf0f9f9447386e8bddb5a460c7c882b62ac3a88712728a773293d96535df32d7edec172359b6494daadf61c1737062604b3d1c4dd4d1a79d24a592ea7140b0938295749a70a3ef1f060676c87404f0eb73b648b106d9fc71f4aa2051833119890f233900807c06b79edc4e99c90ce0e4219ec93fdd2852efd5b451f56303cefe6158553a6ddfb9350f6984d8d8987b53c931a6aa0201222b2840d22473c8498590aaa27239105860dd06f7ce8f468827ab7893e2e5cad782a92c6e65525faa2fd83a954661bbd79497af9968b20d597ebf904127d6cecec0f504375afef0453329f255b8df391c5192a0c00e8e810d5c3d29170cc4ec95fa67e6858c187cbc2410e6779ff4e0fc74f3d3dec6ecfb5de16b1ab0d00c8e78acbe4516a7833781fd06e3ec8b0791420028eaf1395d8c9c54b8cded2b4834f5dc60c56e7d2241eea93cd4796353590a634162c89c5138a4b0271ddc5833c77b6492160299b617b7731f7f1714d444ad3a613b5d4a4138457b5b48c700d1bb6b12afb4aaceb402e8d931ebf55627ffab314bd7794855550d3544cb40168cb3f3b801fcfecc25a35c985426a08dcb78b6ff6b50abb94ff7cfab8b6f6a81cbb92997272aaa6e2bc67734c02ee5ce1d18093c4345660cc19b266737fce7277864d65bd5b7657786f54473120fdfa89f6ae4653f0732eef22b0862be440a621b18741be505bfcf59b2a4472269519534d75671696750b70f7b1db33fb2134256cf54d4adc8bfba37d877753bd4dbe0a27124685b27bcaaf7c613e54f0ea4465d90b49067e8cd31776bb6ec3dce8f6c6a74b8619c97824d79de5d22061e17e72171bfc8da3d3c15ca899a74c0c6b22390a25259193676c96139cee137c80729865ebcdfcd3ba0932a74103267726c20b71a0916f210751e3e166234174d38c927ce9cd0d6dec2ea8806e890cbb966b0b97bd2fabe78c472d0158fd04f9cd8a767c7eaaba98ca22feb6881dfcec470ebfdd170c87375c3747a0bab99d38a6ec2f77007dff0953ac90f8f9a87c08d3655063299e6c45a903d0f01480da4b8b9e2aac2950ca5112ba5b73bb0990c60b6437273785121dcd34e16e1d94fc0b54b37600b5a009dd7cca217baaaf5edd5f975fe2d63eb84463072c8905736067dfbb1ae70311a6c9281bb9a3cf93cd7a4c7848079e2473127f65cb385c7f31654da5459006e1f4822249aca35026e097ebe0cc81ee6b39cb9032810286a058e7097bfb1094e119e2fdef901e236d5c524530ebbc95fe5cabe75fd1fc6c8c900559eb455acf769dc618e985f2dc09e0890b18c233232de339f7a14232cb610aea7119a7835e06510c1117b8a8789af4f67b1aceb15014ce607d6e36830e4dc2895c541a669e41f7d1e44777663e5dc84c9e096c1a2a152b769dd901f1dc5df5300a3c308faea9ae2c226b5e16c9a9d334546845a61c1005d51ca7a9d86e73c0877e182fc9c0b9f7bcc4aff158e169a7ea584694f6a479d4504e48e3e3d21e2f1d5fc71d9d48839a3e9beb6670c036946e6259b1b93d671219e8174e6a39fb781e206e29cf495c74df0f7500e57cde95c67ea0f2cb071d88798d93315e8e941201cc114db1cf2937b5c743b01879ea6d969513a30d571aa395bdad5bf15c885e3696ab09f7059c6cd97f0ed68d5b3059890c209290d0d94005eae0776bd50c7bd2078d2fee3fb0d1b6fb4459dbda077d760caa13ccb8efa9c868b72b45869c1775691380ba7f5cf87869a005879634c4f94b67c5cb080d730f33c838ed897fc2abd9207655e7278307a54669607ae9099bab15d3edbf5a912a635f7cb713ce93a0cecad4c450c6b8dff80b5e31e66b5007963df546d647f153e40c50ac027e924ec677bd95649373280cef41dd9c5e16f282b03a472bb4d933327cdb8b019a822ffc8d05db09f7b824d4439b0973dac18382679561546b6a4213e0d32e8a78d9fa89e4a1a86b00fb0543af0edc0ddd7171991bf423f0876daabce3ccc59bea523d51affab62f0dd3dbb167512b852a02aaaeeeb2a82b78aeec4f4a37330c8c95a72e51b09982d55b624f9793d792b59457b2c40f1e49697c61f98c46d08c32fe86e1f7caa8bdc2d297a808c55ea2d2fe5cb644c016a8cbf8efc84bac7204263a1ed490b2953195c38bf8b9cb04e779f8ddb49b63a973ca517aa8e77934d185c38aa5429921e40056b2422c07f55208b51ebb9d8c24e91f37364e3e9e6c8fb8e4ac6ba0948388de29d59b6b728ef9cacb20482291e7aa801a11010ed12e3b818e112eb33fd8f0d9d10bae8083a524d4dee688423311fb1fdcd4dc8e1c097d34524faba231561b5c183ef20539b488225332311eeb56224f51394e2782d0fa2cfad138b6b94cb78ab6099623648d0c3048b4970369281d3117bbf2ffd18b170d383308c8adfbf49057be51402fb40192531448211cb7a70b5ee8f580e6226bb4c76068f085de37811f33a5b9a4b0cae9d9939a950521b35158804503fa21e7011984f7c04bd5b315a577ee9ed8f7a26daf463f6721936e71abbe7c60259011634bf05ae479935a45815cbef2c3534c4efbc034c90f42118dc8cf697d619e91698e1e223c47fb71d187a0e653b6249144a22014317b9c810bdd5dadc458fc0ecc669450ce78890f2d97bf576a7f5c07451d2926f2a0ff339204446b1b479763c875ada16d1683e8b3efc4e02ac00264cd4be87d90f907ae0ca00c4b42a14c332a7129b9fea44ff9a5676e2890051f2f011f8d81630de4f180cde87671ae66278e5045319cee632fd555fc0f8e094555e880ba9a9e39f2676ee26baf347eaa2ceb8cdd08f9c987b8a2109287f46ce40fed26607c5d147655a53bdc27627f30ab1d273b43854b242f973d09f7963f8f4be9bbb873ba68674e61f1e4b215c1d5844804dd97fff0a468c9d3fb461037b855b5bafec469f2c2fcc6a78ae905d34f863ac5edc7ff71cd14e42570d82ecc14718fb63462f252e0c77ab15fec82c5ca4adb5376c0c6c6bd325487c60768d94075f79a2b3fc467fc4360a84096d09f50d1cacfc27bb02494ff9a444660630099a018d7313b3d91ffa32ffa3e037e974fbb5979a799022481d5c7e24b77bdc1ab06fe5ab95c4bd65ed003b6786cf9be30a0bd223e40c14bfebec29c6c790a16d34b0674e88774bed972fcaa227a1b877679b8283c6cdbf4455e7fadd460d8e8f6dc801dcdfbe44ffbc719eb5eca1ad55710fabe5f1735a9cc010bbec1ff8bff7b60e3c1be3eacbd685537432009c921469c841cb48111a66d07a6105334d1d9d377e76c5cfeb4a7a7bfc7a2d97c3c32a36d1d1613ff4a383a4", 0x1000}, {&(0x7f0000001480)="d348dc69e396fde6072cd11352e6d647a74d250a3258b14bdf4aec805f67ce29feeb58a7827fa1182ede25a253ede12f4363d08faa3b1f7e50fa957c231d4cd57335a23eb9fcd9b048b7b023622b859adb3c1f192032e37097d733913c12ac0d081b99328845d898dd904aeb58db315801fc0b9c0677d92ab87c4d4e58c77b31dd2ec45b575bde64930f1b07f80c316f787b0917aabb0a426559df8a79d62b19a5d3a2dfc4", 0xa5}, {&(0x7f0000000340)="5dd65e39251fe8be91", 0x9}], 0x7)
r2 = open(&(0x7f00000015c0)='./file0\x00', 0x400, 0xd0)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
r4 = socket(0x11, 0x3, 0x0)
setsockopt(r4, 0x11, 0x3, &(0x7f0000000000)="1a7b43da", 0x4)
poll(&(0x7f0000000000)=[{r4, 0x2}, {r3, 0x8}], 0x2, 0xf40)
ioctl$FIOASYNC(r3, 0xc110445d, &(0x7f0000000240))
r5 = openat(r2, &(0x7f0000001680)='./file0\x00', 0x400, 0x1c)
ioctl$TIOCSTOP(r5, 0x2000746f)
openat(r3, &(0x7f0000001640)='./file0\x00', 0x0, 0x80)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000001600)={0x80, 0xff, 0x7, 0x0, "9c05a3acde9203280937d927103c08ddf8fe831c", 0x3})
flock(0xffffffffffffff9c, 0x9)
setreuid(0x0, 0xee01)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1d, &(0x7f0000000040), 0x4)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0dc000"})
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "0360772ad951823f37001e00", 0xffffffff})
r0 = kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
kevent(r0, &(0x7f00000000c0)=[{{r1}, 0xfffffffffffffffe, 0x5, 0x1}], 0x24300000, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000040), 0x120, 0x0, 0x80000000, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x8f})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0xe970, 0x0, "7e00000000800000000000000000000200006ba3"})
writev(r0, &(0x7f0000000600)=[{&(0x7f0000000500)="18", 0x1}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0x0, 0x0, 0x7, 0xfffffffe, "000000000000000001000000000000f843d07e38"})
dup2(r2, r0)
select(0x40, &(0x7f0000000040)={0xff}, 0x0, 0x0, 0x0)
munmap(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
mquery(&(0x7f0000ffa000/0x1000)=nil, 0x1000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0xa}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
clock_gettime(0xaed4d5c8711ac004, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0x20006473, &(0x7f00000006c0)={'./file0\x00'})
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000180)='./file0/../file0\x00', 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{}, {}, {}]})
poll(&(0x7f0000000000), 0x32, 0x0)
rmdir(&(0x7f0000000140)='./file0\x00')
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000000c0)=[{{}, 0xffffffffffffbffe}], 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
pipe(0x0)
bind$unix(0xffffffffffffffff, &(0x7f0000000280)=@abs={0x0, 0x0, 0x3}, 0x8)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
open(&(0x7f0000000000)='./file0/file0\x00', 0x0, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{0x0, 0x0, 0x20000000}, {0x0, 0x0, 0x0, 0x100000000}, {}, {}, {0x0, 0x6}], {}, {0x7}}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
syz_emit_ethernet(0x4a, &(0x7f0000000000)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x14, 0x6, 0x0, @rand_addr="8ab32d78d67f3a344cbcf951919a5b00", @local={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5, 0x5}}}}}}})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30", 0x8e}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f00000001c0), 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f00000011c0)='\x00', 0x14a, 0x1, 0x0, 0xfffffd7e)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0)
close(0xffffffffffffffff)
socket(0x0, 0x0, 0x0)
close(r2)
r3 = accept$unix(r1, 0x0, &(0x7f0000000080))
dup(r3)
preadv(0xffffffffffffffff, 0x0, 0x0, 0x0)
accept(0xffffffffffffffff, 0x0, 0x0)
write(r2, &(0x7f0000000040)="4fa1", 0x2)
recvfrom(r3, &(0x7f0000000300)=""/157, 0x9d, 0x8c2, 0x0, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000000000)=[{&(0x7f0000000180)=""/244}, {&(0x7f0000000280)=""/4096}, {&(0x7f0000001280)=""/130}], 0x9, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x802069c7, &(0x7f0000000300))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000200)="a5781d1f", 0x4}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = socket(0x2, 0x1, 0x0)
close(r0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
preadv(r0, &(0x7f0000000140)=[{&(0x7f0000000040)=""/243, 0xf3}], 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
execve(0x0, 0x0, 0x0)
chdir(0x0)
setuid(0xee01)
r0 = getuid()
r1 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
ioctl$WSDISPLAYIO_LSFONT(r1, 0xc058574e, &(0x7f00000001c0))
r2 = socket(0x18, 0x2, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000380)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x80}})
setreuid(0xee00, 0x0)
setregid(0x0, 0xffffffffffffffff)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, &(0x7f0000000040))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000740)={&(0x7f0000000640)=ANY=[@ANYRES32], 0xa, &(0x7f0000000400)=[{&(0x7f0000000500)="851a4c02ec276d92a82db9f4b6272312fad04b325f81f3ac031f3c5312e5acf292b07a6e34456594bbed8266a32dd8d91d4938154824f7a31e48a231427f43c2cda68c1231963c650dccd46f63bad9bbf15bacbfc39661a8fbfedbfa4dacc188cd9346c0dc9731b8d75d2c9404654ccb5b01850c424b38f47e20342feb2c29fe4e7f7704c1528de4590daf2a8313d534a42642ae4458741418bf9c39bdc83982ea", 0xa1}, {&(0x7f00000002c0)="c992bf68850d464af619ab30293a6f70618244c91e77bcd6a7462e720b0445b41111a7820fe12ac28b35ad", 0x2b}], 0x2, &(0x7f0000001040)=ANY=[@ANYBLOB="2800000000000000ffff0000010000003bdf07a171d8d1cb4335a642a4baf57eee8667c01db838ab4ba926defa3d800303cbc5e9fa6557951e7608e945f1df4eb67b8d0b14294deb8508293bcaaf3ac98bf92efac097719084e6d673244c9ce68fb0c0ec413f02e0e25f207b8f8c1a37376f80e43d9d36", @ANYRES32=r2, @ANYRES64=r0, @ANYBLOB="39346f95535d5e24f119a8423a9b05741c27a2cfce63b5dbd581b396c910e6c5cc6486ed08d0b1f62354861ca3f60fdc6e1b52e7679873df10b60e65d6b7743266d8b642e60b61da2f1fb046b8d5a2d114f7ed2d2ae8752cc93422ff6bac7e0f13768228e2877833e9b19eca30e94d30a403313e0e2d8899", @ANYRES32, @ANYRES32=r2, @ANYRES32=r2, @ANYBLOB="300000000014004034966702000200006feafd611632127dd893bea01a85d9b6a56b1995a9c1b114be4a20d15ac372ca8690185ffda81ca911435065564452c813618169e95900"/86, @ANYRES32=r2, @ANYRESDEC=r2, @ANYRES32=r2, @ANYRES32=r2, @ANYRES32=r2, @ANYRES8, @ANYRES32, @ANYRES32=r2, @ANYRES8, @ANYRES32=r2, @ANYRES32=r2, @ANYRES32=r2, @ANYRES32=r0, @ANYRES32=r1, @ANYRES32=r2, @ANYRESDEC, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r2, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000c1db8ea36a005f157c3e136b4dc5826ddb746a508d7d891476582b6857621fae560e51eeb96d43c3ce8426515d7c34788dca9e146d80579781ab2bcb87bb4803613853080000000000000070c9e8c14e05eaf698dae7bc8e973b5f8eef931116250299d3ab377e690512249f1f4a8c39cbdae9af7ab31b864a35ad498d7acba771f351413767a5a8b476a9e83b3e", @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=r1, @ANYRESDEC=0x0, @ANYBLOB='\x00\x00\x00\x00\x00', @ANYRES8=r0, @ANYRES8=0x0, @ANYRES64, @ANYBLOB="00ed9937c04b1af73ca829be974100000000000000ffff000001000010", @ANYRESHEX, @ANYBLOB="1d9c65beb5e4e059d1a268ccb0bc4e3db72590a5a4f8e41af967f5654849ad0fa8158854d5ea4e60f73ad212776d10792b5706a9450100b0e47399246889ae3d4163a820e6414e0dd096af953fe208ae3adf5e5daa09000000cb4482c9cb7c0a0c1702ac7e312bb0351dd02c18143737f842b8a6929cba0f9dcf8723a30ec36fe2a7455b7f8b02476bb0ba74f4e9"], 0x128}, 0x8)
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000080)={{0x8000, r0, 0x0, 0x0, 0x0, 0x100, 0x401}, 0x7ff, 0x3f, 0x0, 0x0, 0x20009, 0x2, 0x1000})
chflagsat(r1, &(0x7f0000000100)='./file0\x00', 0x0, 0x0)
setsockopt(r2, 0x29, 0x31, &(0x7f0000000780)="9513f3948292ae269282d455abab56626a73422f637e189929b400aaf3fd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce215fa941a79b7d45722a806d166b1bc4553bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54b8ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7000000000000000000000023e8ce5164daed150ea603d66d9fe645a026172cab3de14037498d638c34254c44f3dcdca419d7d7f3f16d69bdaa499af212ce1a9dd7b91a4ff5ff1abaf76de322914c412e328f39647b748404e8fc4e87ba1532498bba96cccb669634c89c888c67b98537150635a2ac8ac78061facc19781678268455199c35cdc349092d2e5c2036a2e6a922957e2319ec1350f3796cef59f0e7e9d1f4210067cff4b43d41cbebff08143f41f0edc8c2c2d317da89c7be06c84f409f45ab5d490e7391f2e43ae0e3f5b1192a48d659dd3a36b19383e45a5308fbe7e2f6e075a6670a9bc40daeae366c7d5d718b21e124649eff84c6425997a04345a1", 0x1a3)
r3 = semget$private(0x0, 0x2, 0x189)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000140)={{0x20000008, 0x0, 0x0, 0x0, 0x0, 0x10001000d, 0x207}, 0xc66, 0x7, 0x4})
rmdir(&(0x7f0000000000)='./file0\x00')
dup(r1)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f00000005c0)={{0x80, 0x0, 0x0, 0x0, 0x0, 0x102, 0x3b2}, 0x7fffffff, 0x8, 0x1ff})
shmctl$SHM_LOCK(0x0, 0x3)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x2, 0xfffffffffffffffc, 0x100000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x2, 0xffffffffffffff01, 0x200000005})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0xd}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
ktrace(&(0x7f0000000180)='./file0\x00', 0x0, 0x0, 0x0)
setuid(r2)
ioctl$TIOCFLUSH(r0, 0x80206979, &(0x7f0000000100))
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
read(r0, &(0x7f0000000100)=""/113, 0x71)
syz_open_pts()
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3b, &(0x7f0000000000), 0x0)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
syz_emit_ethernet(0x56, &(0x7f0000001400)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "78ba0d", 0x20, 0x0, 0x0, @empty, @mcast2, {[@routing={0x0, 0x2, 0x0, 0x0, 0x0, [@loopback]}], @udp={{0x3, 0x0, 0x8}}}}}}})
setegid(0xffffffffffffffff)
setegid(0x0)
symlink(&(0x7f0000000180)='.\x00', &(0x7f0000000140)='./file0\x00')
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000200)='x\x00')
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x8020699d, &(0x7f0000000300))
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010c8e7d00000000ddf500"})
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000000)=0x83, 0x4)
sendto(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x74}, {0x25}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000080)={@remote})
getrusage(0x1, &(0x7f00000000c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x8060693d, &(0x7f0000000300))
kqueue()
select(0x40, &(0x7f00000000c0), &(0x7f0000000100)={0x7fd}, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
recvmsg(r0, &(0x7f0000002800)={0x0, 0xffffffffffffffe9, &(0x7f0000001740), 0x3, 0x0, 0x17}, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x4, &(0x7f0000000100)="1b000000", 0x4)
setsockopt(r0, 0x20000000000011, 0x800000000001, &(0x7f0000000000)="186fe65c", 0x4)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000040)=ANY=[@ANYBLOB])
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000320000/0x1000)=nil, 0x1000, 0x0)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
madvise(&(0x7f0000454000/0x1000)=nil, 0x1000, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000280)='9*', 0x2, 0x401, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
sendto$inet(r2, &(0x7f00000000c0)='\x00', 0x1, 0x0, 0x0, 0x0)
linkat(0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0)
pwritev(0xffffffffffffffff, 0x0, 0x0, 0x0)
socket(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r2, &(0x7f00000012c0)=""/4060, 0xfdc, 0x40, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
r2 = dup2(r1, r0)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
listen(r0, 0x0)
accept$unix(r2, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
r5 = dup2(r4, r3)
execve(0x0, 0x0, 0x0)
connect$unix(r5, &(0x7f00000002c0)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {0x81}, {0x6406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000700)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "c24c93", 0x8, 0x0, 0x0, @loopback, @loopback, {[], @udp={{0x3, 0x2, 0x8}}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="05ff00009b00000000000000", 0xc)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000504600000000000080007", 0xd, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000513", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
shutdown(r0, 0x1)
r0 = socket(0x2, 0x8001, 0x0)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="89028600ffffffff"], 0x10)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
sysctl$net_inet_tcp(&(0x7f0000000100)={0x7, 0x6}, 0x4, &(0x7f0000000340), 0x0, 0x0, 0x0)
r0 = socket$unix(0x1, 0x5, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x5}, {0x61}, {0x6, 0x0, 0x0, 0x81}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
utimes(&(0x7f0000001200)='.\x00', 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
rmdir(&(0x7f0000000100)='./file0\x00')
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
r2 = openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r3 = socket(0x18, 0x3, 0x102)
shutdown(r3, 0x2)
ioctl$BIOCSETF(r2, 0x80104267, &(0x7f0000000040)={0x4, &(0x7f0000000000)=[{0x1, 0x7, 0x4, 0x6}, {0x0, 0x4, 0x5, 0x10000}, {0xffff, 0xc7, 0x7f, 0x3}, {0xf800, 0x7, 0x5, 0x401}]})
r4 = socket(0x2, 0x3, 0x102)
shutdown(r4, 0x2)
shutdown(0xffffffffffffffff, 0x2)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x8000, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, r3)
socket(0x20, 0x8000, 0x80)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
poll(&(0x7f0000000140), 0x0, 0x90000000)
poll(&(0x7f0000000180)=[{r1, 0x190}, {r1, 0x4}, {r2, 0x18}, {r3, 0x20}, {r0, 0x20}, {r4, 0x10}, {r0, 0x1}, {0xffffffffffffffff, 0x80}], 0x8, 0x7f)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = fcntl$dupfd(r3, 0x0, r0)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000000)={0x6, 0x1}, 0x2, &(0x7f0000000080)="ed6b159f455a", &(0x7f0000000180)=0x6, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {}, {0x7, 0x0, 0x0, 0x81}]})
r0 = socket$inet6(0x18, 0x1, 0x0)
listen(r0, 0x0)
accept(r0, 0x0, 0x0)
shutdown(r0, 0x1)
shutdown(0xffffffffffffffff, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xf8]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = getpid()
r2 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
dup2(r2, r0)
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
r0 = socket(0x2, 0x400000001002, 0x0)
connect$unix(r0, &(0x7f0000000000)=@abs={0x0, 0x0, 0x0}, 0x8)
sysctl$kern(&(0x7f0000000100)={0x1, 0x9}, 0x2, &(0x7f0000001580)="0f513a92", &(0x7f00000000c0)=0x4, &(0x7f0000000000)='\a\x00\x00\x00', 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0xffffff80, 0x9ef, 0x6, 0x7, "9a7f85e4aae6f02a0b336f271d75b2f862ad3b9c", 0xfffffff7})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{}], 0x1})
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
msgget$private(0x0, 0x0)
fcntl$setown(r0, 0x6, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x15}, {0x3}, {0x6, 0x0, 0x0, 0xd920d164}]})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
mprotect(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1)
sysctl$vfs_ffs(&(0x7f0000000000)={0xa, 0x1, 0xc}, 0x3, 0x0, &(0x7f0000001040), 0x0, 0x51)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x80}, {0x3c}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@remote, @broadcast})
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x0)
rmdir(&(0x7f0000001240)='./file0\x00')
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f0000000040)={{}, 0x0, 0x0, 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x37}, 0x4, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x66, &(0x7f0000000200)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "f05dcb", 0x30, 0x0, 0x0, @loopback={0xfeffffff00000000}, @mcast2, {[], @icmpv6=@dest_unreach={0x1, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "a36b90", 0x0, 0x0, 0x0, @mcast1, @ipv4={'\x00', '\xff\xff', @loopback}}}}}}}})
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000002340)=[{&(0x7f0000001180)=""/4089, 0xff9}], 0x1, 0x0)
r0 = semget(0x0, 0x0, 0x0)
semctl$SETVAL(r0, 0xfbc8989ebaa37159, 0x8, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x28}, {0x61}, {0x6, 0x0, 0x0, 0x7fffffff}]})
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000200)={0x4, 0x22}, 0x3, &(0x7f0000000240), 0x0, &(0x7f0000000000)="f20c1002b9c92b1c5260f9a37dd400467eb653e49a11598f77585d50e764febd4ab02a32043d2c0044e110102deb512fa9c594917cc66dc769fe80984c7263c116bf45b2dd5a8dd293fa12958d79f2daca51fcc2d887340d23e74c9bcde0d2378f4b7f60faca74c25d2837e7fb2581f754f7374f711e5679a01d72ae25c5fda04479", 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x38, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000000)=[{0x81, 0x8}, {0x83, 0xff}, {0x21, 0x7fff}, {0x44, 0x1ff}, {0x89, 0xa000}, {0x6, 0x4}], 0x6})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0xc}, {0x40}, {0x6, 0x0, 0x0, 0x100004}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x40}, {0x15}, {0x6, 0x0, 0x0, 0x20000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x8, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{}, {0x81}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0xe, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000000)='r', 0x1)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sysctl$net_inet_esp(&(0x7f0000000000), 0x8, &(0x7f0000000040)="e3d5a4b3ab4aa41a02bcb860729b37b269e5801a4f04e81671cd0a8cbf20ac73c6cded87ddcc450fc45e2d2368d7a0dd5632f107bcfe8884b9ef89f07bc23500f035c0a486e6c1d7a726cf39d38c9510f357bbc4bf16509c2961a0e95e28ddec8357d365ad5d81892ff4e3791eb89e8e3ac434ef467d8ea143b48c02eb334e33efc740282a24be55eb982dd755faa467119b608bc525ea7c225d8278b2de50a0dbe915464feba909ceecb16def4d4e246859edbf6a03ce921cd8fe0d723b6343", &(0x7f0000000140)=0xc0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x64}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x4a, &(0x7f0000000080)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x14, 0x0, 0x0, @rand_addr="8ab32d78d67f3a344cbcf951919a5b00", @local={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
shmget$private(0x0, 0xc00000, 0x0, &(0x7f0000400000/0xc00000)=nil)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x31}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
r1 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
renameat(r1, &(0x7f00000001c0)='.\x00', 0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00')
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000002c0)={{0x18, 0x2, 0x80000001, 0x1f}, {0x18, 0x0}, 0x0, [0x5, 0xffff, 0x4, 0x0, 0x8, 0x5, 0x3fe00, 0x7]}, 0x3c)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x3}, {0x5}, {0x6, 0x0, 0x0, 0x80000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000280)="97f1621471610e72711fec54e378", 0xe, 0x0)
r2 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000240), 0x8000, 0x0)
r3 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
mkdirat(r3, &(0x7f0000000180)='./file0\x00', 0xc5)
mkdirat(r3, &(0x7f0000000180)='./file0\x00', 0xc5)
ioctl$BIOCSETF(r2, 0x80104267, &(0x7f0000000100)={0x9, &(0x7f0000000300)=[{0x2, 0xff, 0x9, 0xfffffe01}, {0x3, 0x6, 0x8, 0x9}, {0x81, 0x6, 0xa6}, {0xfffb, 0x4, 0x1a, 0x101}, {0x5, 0xee, 0x8, 0x4}, {0x8, 0x1, 0x0, 0x2}, {0xe01, 0x0, 0x0, 0x40}, {0x0, 0xce, 0xfa, 0x81}, {0x2, 0x8, 0x1}]})
dup2(r0, r0)
ioctl$WSDISPLAYIO_WSMOUSED(0xffffffffffffffff, 0x80185758, &(0x7f0000000000)={0x0, 0x0, {0x1}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0xa}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x81}, {0x4}, {0x6, 0x0, 0x0, 0x1ff}]})
write(r0, &(0x7f0000001700)="fb2fe7e986d81d7acfccfbcbd4a2", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x30}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
sysctl$kern(&(0x7f0000000140)={0x1, 0x41}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x100000000})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x36, &(0x7f00000001c0)='\x00\x00\x00\x00', 0x4)
sendto(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1)
open$dir(&(0x7f0000000000)='./file0\x00', 0x302, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
select(0x62, 0x0, &(0x7f0000000000), &(0x7f0000001900), &(0x7f00000000c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0884450, &(0x7f0000000240))
r0 = socket(0x2, 0x8001, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
setsockopt(r0, 0xffff, 0x1, &(0x7f0000000300)="9d059736", 0x4)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x1a3c, 0x0)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0/file0\x00'}, 0x10)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x29, 0x68, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040), 0x4)
open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r0)
r1 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
recvmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0, &(0x7f00000008c0))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0dc000"})
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "d4ce772ad9518d3fa0e9436c5483b98ba7e0a637"})
readv(r0, &(0x7f00000000c0)=[{&(0x7f0000000140)=""/99, 0x63}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000780)={0x3, &(0x7f0000000000)=[{0x48}, {0x3}, {0x6, 0x0, 0x0, 0xb13}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x3c, 0x0, 0x0)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f00000001c0)=[{0x0, 0x8, 0x1000}, {0x1, 0x4, 0x800}], 0x2)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000200)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x48, 0x40}, 0x3e, 0x10000000fff, 0x9})
semop(r0, &(0x7f00000000c0)=[{0x0, 0x8000, 0x1800}, {0x4, 0x400}, {0x1, 0x4, 0x1000}, {0x1, 0x26bc, 0x1000}, {0x0, 0xffff, 0x1000}, {0x0, 0x0, 0x800}, {0x4, 0x6, 0x1000}, {0x3, 0x7}, {0x1, 0x3e, 0x1000}], 0x9)
semop(r0, &(0x7f0000000100)=[{0x1, 0xffff}, {0x4, 0x7fff, 0x1000}, {0x4, 0x5, 0x800}, {0x0, 0x403f, 0x800}], 0x4)
semop(r0, &(0x7f0000000040)=[{0x3, 0x3, 0xb1dca765c7eab520}, {0x1, 0x937, 0x1000}], 0x2)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f00000000c0)=""/4096)
r1 = syz_open_pts()
flock(r1, 0x2)
fcntl$lock(r1, 0x7, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x2000300010000})
syz_emit_ethernet(0x3a, &(0x7f0000000000)=ANY=[])
r2 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x80, 0x0)
ioctl$TIOCSPGRP(r2, 0x40047477, &(0x7f0000000080))
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r3)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000000)={{0x5, 0x0, 0x0, 0x0, r3, 0x180, 0x9}, 0x495f, 0xdd0})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000010c0)={{0x3, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0x4, 0x9}, 0x9, 0x4, 0x20})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0xb, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x10, &(0x7f0000000000), 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1a}, 0x4, &(0x7f0000000040)='\a', &(0x7f0000000080)=0x1, &(0x7f0000000140)="505e3f62827ab105dd1bcc6817347199dd881f2f29e7e45c2e0f9d89e35e04ba1d8af150384ff2b17bfc783c0e281b0865ab9b42c32f62bb7cecafda4c44121721e8b5ecb09d92a368cca6b151df46479bc56a2ce68412f328f33ae329c63eaf0b0df43bf12ec25a62cb679dbd0a25682f8b2a8371bbddcb88523615501042ab44ab62ff4a0e2f8b8ba51dd93d713f8a07de3aa27a490660082d7992d279d26fb0b0ebe197ff08c3d963225eb4d84f55b3dd9f67759fa0deb2c0c73893314f12e4ae4d0bd95a97c8a4be35c921e6f55e1cee72ade57b658113f30a37c556c00780861563f4aac61c10487dccd745a5d76a9048fbdb12d1e04bc77bf1bf65542b3c967a", 0x103)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1e}, 0x4, 0x0, 0x0, 0x0, 0x0)
openat$wskbd(0xffffffffffffff9c, 0x0, 0xd00, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x3}, {0x2d}, {0x6}]})
syz_emit_ethernet(0x66, &(0x7f0000000080)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "7a8992", 0x30, 0x0, 0x0, @rand_addr="00000000000100", @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "7be033", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000140)=[{0xff, 0x2, 0x8, 0x974}, {0x3, 0x4, 0x0, 0x10001}, {0xb0, 0x4, 0x7, 0x1}]})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x28}, {0x25}, {0x6406}]})
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001000020000300"})
r1 = socket(0x18, 0x1, 0x0)
shutdown(r1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000240)=[{}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
r1 = dup2(r0, r0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r1, &(0x7f0000000100)="8188d772f123016b26e25db77a77", 0xe, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x40000400000002c1, 0x0)
setgroups(0x0, 0x0)
r0 = getpgrp()
r1 = getpgrp()
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x410, r1)
setgroups(0x0, 0x0)
open(&(0x7f0000000340)='./file0\x00', 0x20, 0x0)
ktrace(&(0x7f0000000200)='./file0\x00', 0x4, 0x1334, r0)
setreuid(0xffffffffffffffff, 0x0)
r2 = semget$private(0x0, 0x5, 0x28)
semop(r2, &(0x7f0000000100)=[{0x3, 0x5022, 0x1000}, {0x1, 0x0, 0x1000}], 0x2)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000280)=0xc)
r5 = getegid()
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000002c0)={{0xc, r3, r4, 0xffffffffffffffff, r5, 0xb0, 0x568c}, 0x3, 0x2, 0x7e})
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040), 0xc)
getgid()
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x2}, {0x3c}, {0x5}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000280)={@empty, @remote})
pipe(&(0x7f0000001c80)={<r0=>0xffffffffffffffff})
poll(&(0x7f0000000000)=[{r0, 0x65}], 0x1, 0x0)
clock_getres(0x4, &(0x7f0000000000))
clock_getres(0x5, &(0x7f0000000040))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f00000024c0)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="1060d208ee7b9d77b697559b7a243e5083d50429b9b19ad4cea01493fed6d003cfcc9e7c8cf7b7b825bfeeeaac6b840357325a0323a489745658f59c8c1b8750609bb8b644", 0x45}], 0x1, 0x0, 0x2150}, 0x0)
ioctl$FIOASYNC(r0, 0xc0884450, &(0x7f0000000240))
openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f0000000080))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000100)=ANY=[@ANYBLOB="fb182e8dfeffffffff"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000eefeffffff0300002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
connect$unix(r0, &(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
sysctl$hw(&(0x7f0000000040)={0x9, 0x6}, 0x2, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
sysctl$hw(&(0x7f0000000040)={0x6, 0x12}, 0x2, &(0x7f00000001c0), 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x36, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x6, 0x1, &(0x7f0000000000)='\x00\x00\x00\x00', 0x4)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0xca931229b419bd02, 0x0, 0x100000004, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0xff, 0x2000300010000})
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
sysctl$ddb(&(0x7f0000000000)={0x9, 0x2}, 0x2, &(0x7f0000000180)="7b7eaed9", &(0x7f0000000040)=0x4, 0xffffffffffffffff, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000240)=[{0x61}, {0x28}, {0x6, 0x0, 0x0, 0x80000000}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x10000436, 0x47, 0x81000005, 0x6acb9789, "000000000000c751bc0d7809c5d100"})
writev(r0, &(0x7f0000000400)=[{&(0x7f00000000c0)="42052536f0aa4c43c5daf91b6ac439662ce0376021b71d36501e31c49a23eb4d2465a70a1599e774ebb999393aa15ae4733c0dcbfb092a279acba21f9bc2c18409748a89b67927a9ab9b353ddbe949d80a6bc177f0a9060a99cc9b6b71cb70d5090e14f26631682624a6808ce412bd8524439020978dcaeee51ef70aeaa22c1d41b67b6a061da382d1259e8c8a7dd917668acb96a336208d8d", 0x99}], 0x1)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
read(r0, &(0x7f00000000c0)=""/144, 0x90)
mknod(&(0x7f0000000000)='./bus\x00', 0x280002006, 0x2065d)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x8004745f, &(0x7f0000000180))
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000300)={0x0, 0x0, 0x0, 0xfffffe01, "5bac51439f189f9fa3bbdb9ace8213e2f1dd284d"})
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x61}, {0x1}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
pwrite(r0, &(0x7f00000002c0)="f2d68c842ef478efef73674aedc5", 0xe, 0x0)
socket(0x0, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
ioctl$TIOCFLUSH(0xffffffffffffffff, 0x80206979, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0xb}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x4, 0x0, 0x0)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000000029, 0x9, &(0x7f0000000180)="01000000", 0x4)
sendmsg$unix(r0, &(0x7f0000001700)={&(0x7f0000000600)=ANY=[@ANYBLOB="fb18ffffffffffffff"], 0x1c, 0x0}, 0x0)
mknod(&(0x7f00000001c0)='./file0\x00', 0x2000, 0x200028bf)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2007, 0x40004004000828a6)
open(&(0x7f0000000400)='./bus\x00', 0x0, 0x0)
r1 = dup(r0)
ioctl$VMM_IOC_CREATE(r1, 0xc5005601, &(0x7f0000000440)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ffa000/0x1000)=nil, &(0x7f0000ff9000/0x4000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ff8000/0x3000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000400000/0xc00000)=nil}, {&(0x7f0000d3e000/0x2000)=nil, &(0x7f0000683000/0x3000)=nil}, {&(0x7f00004f1000/0x1000)=nil, &(0x7f0000887000/0x3000)=nil}, {&(0x7f0000c99000/0x1000)=nil, &(0x7f0000a99000/0x2000)=nil}, {&(0x7f0000df8000/0xf000)=nil, &(0x7f0000e58000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f00008a4000/0x1000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f000044a000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000d89000/0x2000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f000099c000/0x4000)=nil}], ['./bus\x00', './bus\x00', './file0\x00', './file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus\x00', './bus\x00', './bus\x00', './file']})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000001440)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000000))
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x1}], 0x0, 0x0)
semop(0x0, &(0x7f0000000080)=[{}, {}, {0x0, 0x7f}, {}, {0x2}], 0x5)
kevent(r0, &(0x7f0000000000), 0x5847, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
sysctl$net_inet_esp(&(0x7f0000000080)={0x4, 0x21}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f00000000c0)=[{0x50}, {0x16}]})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@broadcast, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @empty, "f9438477b7432b191466538a04047155"}}}})
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x80000000, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
setsockopt(r3, 0x29, 0x3e, &(0x7f0000000080), 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r4 = openat$zero(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
fcntl$lock(r4, 0x9, &(0x7f0000000040)={0x2, 0x0, 0x0, 0x1000100000000, 0xffffffffffffffff})
r5 = openat$zero(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
fcntl$lock(r5, 0x9, &(0x7f0000000040)={0x2, 0x0, 0x0, 0x1000100000000, 0xffffffffffffffff})
dup2(r4, r5)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}, 0xfffffffffffffffe, 0xfffffffdffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
openat$pf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000240)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000080)=0x2)
setpgid(0x0, r1)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x5}, 0x4, &(0x7f0000000100), 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@local, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x401, 0x0, 0x0, 0x0, @rand_addr, @multicast1}, @udp={{0x2, 0x0, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x4d}, {0x40}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='r\x00')
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
sendmmsg(0xffffffffffffffff, &(0x7f0000000180)={&(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000540)=ANY=[@ANYBLOB="d000f8ffffff000001"], 0xe8}}, 0x10, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000040)={0x0}, 0x10, 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x2000, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
select(0x40, &(0x7f0000000080), &(0x7f0000000140)={0x7}, &(0x7f0000000000)={0x8}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x100000000000000, 0x10000000000001}})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
close(r0)
r2 = socket(0x18, 0x2, 0x0)
close(r2)
dup(r1)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f0000000080)="0100", 0x2, 0x402, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
symlinkat(&(0x7f0000000040)='./file0\x00', r0, &(0x7f00000000c0)='./file0\x00')
rmdir(&(0x7f0000000100)='./file0\x00')
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0xd, &(0x7f0000000040)="669a3dfcca0ab6fdf0fc399a", 0xc)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f00000005c0), 0x0, 0x0)
close(r0)
getdents(r0, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000080)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x7, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast}, @tcp={{0x3, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
sysctl$kern(&(0x7f0000000200)={0x1, 0x2}, 0x2, &(0x7f0000000240)="2fce5c27", &(0x7f0000000280)=0x4, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0xbcd8, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000005c0)=[{&(0x7f0000000280)='r7', 0x2}], 0x1)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000140)=0x8d1b)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f37b38e3745967f3471b86feef9b22228bcecf079e5aaba03606336acf00b7804be781e4991f7c8df5f882b297be1aa5bfa495c2ec56724830eed6223ed00f4c8b2ca3ebbc257699a17122e27acbdd602000d7d026ba8af63ffc2fa63feebf3731b0fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c5000000", 0xb1, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f00000000c0)={0x6, 0xe}, 0x2, &(0x7f0000000300)="2b19d468ed84d1", &(0x7f0000000100)=0x7, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000040)="0101f1c4f4ffffff03000000", 0xc)
setsockopt(r0, 0x0, 0x9, &(0x7f00000001c0), 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000180)=[{r0, 0x4}], 0x1, 0x0)
sync()
getrusage(0x1, &(0x7f0000000340))
setitimer(0x0, &(0x7f0000000240)={{0xffffffff}, {0xffffffff}}, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000080)={@local, @random="3a6d1f6d3961", [], {@ipv6={0x86dd, {0x0, 0x6, "3835fc", 0x8, 0x0, 0x0, @rand_addr="e674d7e43964ddb01d19d62899579092", @local={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x2c, 0x0, 0x81}]}}}}})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000440)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
connect$unix(r2, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r4=>0xffffffffffffffff})
connect$unix(r4, &(0x7f0000000000)=ANY=[], 0xa)
dup2(r1, r0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
close(r1)
poll(&(0x7f00000002c0)=[{r0, 0x40}], 0x1, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x40}], 0x1, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r0, &(0x7f0000000280)="c6bd6b6acbdc0f81215ecd8ac1e3d2279f7a2257cdd387ab357f5667b30dd862277d97bb6f5dd376d54823bda1622171aca24e41a7e17849441696d24167b5181ecc27ad6513df96c07f00ef70fca41e7650f177ffb56c61a76c4e05c79ce63ab70b30dd95d403d8393efd295c3239cd0c223d33c5a90e41023d2a48be259df84781f4056982494dbbc946a9856ef6264b2659a085108aa0390749b8fec84b7cd7442d", 0xa3, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f00000000c0)='oL', 0x2}], 0x1, 0x0)
write(r0, &(0x7f0000001540)="7763baa4d010a03ec2bad2c4ba657dda73f644fdec0d30de6d0bb80aaeaf54c0622b", 0x22)
execve(0x0, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSKBDIO_GETBELL(r0, 0x40105704, &(0x7f0000000000))
r0 = socket$inet(0x2, 0x2, 0x0)
shutdown(r0, 0x7)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x4000001000000}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000002c0)=[{0x3d}, {0x6c}, {0x6, 0x0, 0x0, 0x20400}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x400004000011830a, 0x0)
write(r2, &(0x7f0000000780)="089267d3ff4f0b87969f", 0x100ad)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
write(r3, &(0x7f0000000040), 0xfeea)
open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
setitimer(0x0, 0x0, 0xfffffffffffffffe)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xd, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x2)
sysctl$hw(&(0x7f0000000080)={0x6, 0x18}, 0x2, 0x0, 0x0, &(0x7f00000012c0), 0x4)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xa, &(0x7f0000000500)="ebffcb48", 0x4)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x11, 0x2, &(0x7f0000000000)="1a7b43da", 0x4)
setitimer(0x0, &(0x7f0000000000)={{}, {0x0, 0x7}}, 0x0)
setitimer(0x0, 0x0, &(0x7f0000000100))
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f00000016c0)=[{&(0x7f0000000440)="23dc7034fe466be7f8", 0x9}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000000))
sysctl$hw(&(0x7f0000000000)={0x6, 0x6}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x28e, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
setreuid(0xee00, 0x0)
r1 = getuid()
seteuid(r1)
r2 = getpgrp()
ktrace(0x0, 0x93a96fddd37e5095, 0x40000008, r2)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x4000010002, 0xffffffffffffffff, 0xafa8, 0x0, 0x6], [0xfffffffffffffffc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4], [0x0, 0x1386], [0x0, 0x2], [{}, {}, {}, {}, {0x9}, {0x0, 0x4}, {0x3, 0xfffffffe}, {0x0, 0x0, 0x101, 0x80}], {}, {0x0, 0x80, 0x20}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3200)
r0 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
ioctl$TIOCMSET(r0, 0x8004746d, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x20}, {}, {0x6}]})
syz_emit_ethernet(0x4e, &(0x7f0000000140)={@remote, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "e97cac", 0x18, 0x0, 0x0, @loopback, @rand_addr="0e129952db8af047a44dbd7791cd6550", {[], @icmpv6=@mld={0x0, 0x0, 0x0, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @local={0xac, 0x14, 0x0}}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000180)=[{0x2d}, {0x48}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f00000002c0)={@broadcast, @random="7d9f3e96fda8"})
r0 = kqueue()
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{r1}, 0xfffffffffffffffe, 0xe5}], 0x0, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000080)=[{}, {0x0, 0x8, 0x1800}, {}, {}, {0x2}], 0x5)
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
r2 = socket(0x18, 0x3, 0x0)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r3, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
ioctl$TIOCSTAT(r3, 0x20007465, &(0x7f0000000180))
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r4, 0xc0104452, &(0x7f00000002c0)={0x5, 0x408, 0xfffd})
poll(&(0x7f0000000300), 0x0, 0xb86)
setsockopt(r2, 0x800000000029, 0x25, &(0x7f0000000040)="1f12fb36", 0x4)
r5 = semget(0x2, 0x1, 0x10)
r6 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x40, 0x0)
ioctl$TIOCSETD(r6, 0x8004741b, &(0x7f0000000280)=0x2)
semop(r5, &(0x7f00000000c0)=[{0x0, 0xbfe2}, {0x3, 0x1, 0x1800}, {0x1, 0x2f, 0x1800}, {0x4, 0xfffe, 0x1000}, {0x5, 0x7, 0x1000}], 0x5)
socket(0x18, 0x3, 0x0)
r7 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r7, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x2c}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x2a, &(0x7f0000000240)={@broadcast, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="08eb3445568c", @multicast1, @empty, @multicast2}}}})
mknod(&(0x7f0000000100)='./file0\x00', 0x1ffb, 0x0)
open(&(0x7f0000000280)='./file0\x00', 0x2, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1000200010005})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x5}, {0x40}, {0x6, 0x0, 0x0, 0x100}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="5df09d5792f307847f24cd378250", 0xe, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205603, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, 0x0})
setuid(0xffffffffffffffff)
socketpair(0x18, 0x3, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000080)=[{0x30}, {0x5c}, {0x16}]})
write(r0, &(0x7f0000000040)="3c9ebbd555feff969613ba3efed0", 0xe)
r0 = socket$inet(0x2, 0x1, 0x0)
shutdown(r0, 0x1)
r1 = dup2(r0, r0)
bind$inet(r1, &(0x7f0000000000)={0x2, 0x2}, 0xc)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/9, 0xfffffffffffffef5}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
r2 = kqueue()
r3 = fcntl$dupfd(r2, 0x2, 0xffffffffffffffff)
close(r3)
socket(0x400000000018, 0x3, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$dupfd(r0, 0x7, 0xffffffffffffff9c)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xfffffffffffffffd]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
ioctl$TIOCFLUSH(r0, 0x8028698c, &(0x7f0000000000))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x6d, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x38, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000240)=0x8)
poll(&(0x7f0000000180)=[{r0, 0x1}], 0x1, 0x0)
fcntl$setstatus(r0, 0x4, 0x40)
syz_emit_ethernet(0x22, &(0x7f0000001080)={@random="7190e10f2801", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast}}}}})
open$dir(&(0x7f0000000000)='./file0\x00', 0x762, 0x0)
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='c\x00')
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
munmap(&(0x7f000081d000/0x2000)=nil, 0x2000)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f000081d000/0x3000)=nil, 0x3000, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x20, &(0x7f0000000000), 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000022000210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020690c, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffdffff8, 0xffffff55, "0855c4012500fd07000100008d1b38b85200"})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60a", 0x1c1}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x1d}, {0x81}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000280)=ANY=[])
sysctl$net_inet_carp(&(0x7f0000000000)={0x4, 0x2, 0x70, 0x3}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
getgroups(0x400000000000010b, &(0x7f0000000000))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
r2 = dup2(r1, r0)
bind(r1, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
pipe2(&(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff}, 0x0)
dup2(r3, r1)
listen(r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r4=>0xffffffffffffffff, <r5=>0xffffffffffffffff})
dup2(r5, r4)
accept(r2, 0x0, 0x0)
close(r2)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f00000005c0)="0118ffac02010926feba70a7cf09f6360f9ea14f040000040200000097069815ca5835b6f653163972e537b4ea5084cec4aa10930ed148017d4c00414191ac61000000009a8a372208b127f29c66755d45d5ae11c6731aede78c4421cef62cac7d5ecbf929b2e7910599897b76cc0690cdbccbb33f66fe237bff60c8c7f4766c3bd9ca519f1c7612b32966a5c0001d0be46ebb59a63fde26f5ca6a157ad13900ffe6c35b55a191701155a29aec01000000000000007e59a59a05bb68baab1116ff9915b909800989d8d1fbe686246fa85c22ad066d2bee0180397cfe2cae6e966c98d4c45356c7ba884255d77f292d74b0bd01eaff00201c3f30e79006157cd0f6ac7354745175bd902a5f48090000000024244ade0d51faff2a39da008fffff003e000000000900000047000000001cd965075b49777b822443cd2340e953a800000000000000cfef0000003881885647e6b9ecf3341ad6bf4a36a36327d70d8275efa0fa183881b37cd49c4287ed75470bd87e5503c733fc217eb511ae3e030100a9ed00002e111835a6b76cd5ff5256df19b5634e2811d910faf269e502db1548a7df9ee943575893f400c7c38ffc4178351512cf13ff7c52329a2513b30cea635596050000000000007f2c30c0d29de0815e8214f8570900000000000000624824a96d9619e00feb7cc4108d57240fe1d465014bd7652b7e5f4a53283c73160df73959f4db28abef5f64c562d88d", &(0x7f0000000040)=0x210, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000011c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3d}, {0x3d}, {0x2006}]})
syz_emit_ethernet(0x36, &(0x7f00000002c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "08ec28", 0x0, 0x0, 0x0, @loopback, @empty}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x802069a1, &(0x7f0000000300))
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f00000000c0), 0x4)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x1, 0x897, {[0x0, 0x1, 0x0, 0xffffffffffff8000, 0xfffffffffffffffd, 0x0, 0x0, 0x13, 0x7, 0x7fff, 0x401, 0x0, 0x2000000000000, 0x2, 0x0, 0x2, 0x0, 0x20084], [0xe2f, 0x20, 0x1, 0x3, 0x4, 0x7, 0x6, 0x0, 0x4], [0x0, 0x3, 0xffffffffffffffff, 0x0, 0x0, 0x3, 0x6], [0x0, 0x0, 0x0, 0x3, 0x6, 0x1], [{0x0, 0x0, 0x0, 0x4}, {0x0, 0x2}, {0x0, 0x0, 0xfffffffc, 0x100000000789}, {0xfff8, 0x1, 0x6f, 0x100006}, {0x0, 0x0, 0x4}, {0x8001, 0x20, 0x7, 0x6}, {0x0, 0x80000200}, {0x0, 0x0, 0xfffffffd, 0xfffffffffff}], {0x1, 0x1003, 0x0, 0x3}, {0x6, 0x80000004, 0xfffffffd}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
socket(0x18, 0x2, 0x0)
r0 = socket(0x18, 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000100)={0x0, &(0x7f0000000500)})
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300))
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
ioctl$BIOCSETF(r2, 0x80104267, 0x0)
r3 = socket$inet6(0x18, 0x3, 0x1)
socket(0x1, 0x1, 0x1)
getsockopt(r3, 0x29, 0x66, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0))
chown(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8020691f, &(0x7f0000000300)=0x2000000)
getpeername(0xffffffffffffffff, &(0x7f00000000c0)=@in6, &(0x7f0000000080)=0xc)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x6, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt(r0, 0x6, 0xffffffff, 0x0, 0x0)
socket(0x18, 0x3, 0xf)
r1 = dup(r0)
setsockopt$sock_int(r1, 0xffff, 0x4, &(0x7f0000000080)=0x7, 0x4)
listen(r1, 0x0)
r2 = socket(0x800000018, 0x1, 0x0)
listen(r2, 0x0)
poll(&(0x7f0000000040)=[{r2, 0x1e11585f4e8fb0ef}], 0x1, 0x0)
shutdown(0xffffffffffffffff, 0x1)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
shutdown(r2, 0x1)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r3, &(0x7f0000000040)="919b5c5e5c069d14288be166", 0xb08f, 0x1, 0x0, 0x0)
r4 = accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r4, &(0x7f00000000c0)=""/166, 0xffffffc7, 0x0, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000140)="0100a56700009eff03000000", 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8202aa9cef"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r0, &(0x7f0000000400), 0x3c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0xc}, {0x24}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f00000001c0)={@empty, @remote})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x3, &(0x7f00000000c0)=[{0x2}, {0x25}, {0x6, 0x0, 0x0, 0xaf6}]})
pwrite(r0, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
pipe(&(0x7f0000000040)={<r0=>0xffffffffffffffff})
r1 = open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x1630, 0x0)
write(r0, &(0x7f0000000340), 0xd4e688a67930cd)
writev(r0, &(0x7f0000000400)=[{&(0x7f0000000100)='/', 0x1}], 0x1)
dup2(r1, r0)
execve(0x0, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r1 = dup2(r0, r0)
ioctl$WSMOUSEIO_SETPARAMS(r1, 0x80105727, &(0x7f0000000140)={&(0x7f0000000080)=[{0x1}, {0x25}], 0x2})
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x100001ff, 0x0, 0x0, 0x0, "00000000000200"})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000400)=[{0x60, 0x7e}, {0x1c, 0x0, 0x9}, {0x6, 0x0, 0x0, 0xfffff3b2}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
writev(r1, &(0x7f0000000200)=[{&(0x7f0000000040)="0fb0b212f159ef90c7f48dadba7fe738ea3a26bc5abe574168db262ccb8f015d3f1c4a7b1ffa83e3c1655c96e6c8bbe26658f11b2d24a38ac7b7312a144c306ce21ec22898b73aba2a8c780da7e4a78126b77509531fee4bdd94714b0433c29bd7470183dec8ddf5ed520a7cfd08856fadc87cf165281ffc8bb7ff298041b3705e9f3ba93eeeb390ca7ad4571a6e0d1cbe1ceebcb07ee4e3027edb9c6b0df43b37bc78b9f0deba4c50e0d7a349fb2bac6d826c5c18fb38a7fa66dff18a37898529d5a83f3e11545ef6e868bf2421a46353c0", 0xd2}, {&(0x7f0000000180)="2e4549fd00802b7f71d4f349f365da3c257f89b654e769a91031140c0543d5b259a754c7e5e5aecf1de38a12bc4fe3205bc41c22598fa0108a5719983114796b259a8f532ef950c1939b37c23a454429c40949fd65aecaa5b28a18dade94065c5adaddea535adbe965f5c3c2390832987bee", 0x72}], 0x2)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x1, 0x0)
setsockopt(r2, 0x80000000000029, 0xc, &(0x7f0000000000)="eb1bcc0600b9fd812eaa4e713048e69931929648", 0x14)
r3 = socket(0x6, 0x4000, 0x0)
setsockopt(r2, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
dup2(r3, r3)
getgid()
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206916, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000140)={0x0, 0xffff, 0x1000007, 0x6acb978b, "0d43d3d3d33f0800010000000000000d00"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)="0dcf67520990982c582dd6057e8d1069ce86b4854475cd2ad14516872e5fb8b07102f1f8b3c02aaeb9b32f1461a2fe9cafe8f3a3458b93e501ccc490dc2a968bc43140c8eb7b5882a12fa011c9518219d170f2aebe4c5aed28582ee61ecb44ebd70d", 0x62}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x64}, {0x3}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x60}, {0x5c}, {0x6, 0x0, 0x0, 0x40}]})
pwrite(r1, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
ioctl$BIOCGDIRFILT(r1, 0x4004427c, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000003c0), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x4c}, {0x4000006, 0x0, 0x0, 0x3c}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020690e, &(0x7f0000000300))
mknod(&(0x7f0000000040)='./bus\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x2, 0x0)
read(r0, 0x0, 0x0)
r1 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
close(r1)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)="ea", 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="2902657f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
close(r0)
r2 = socket(0x2, 0x8001, 0x0)
setsockopt$sock_linger(r2, 0xffff, 0x80, &(0x7f0000000040), 0x8)
connect$unix(r2, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
close(r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x15}, {0x48}, {0x6, 0x0, 0x0, 0x801fff7a}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = syz_open_pts()
syz_open_pts()
readv(r0, &(0x7f0000000000)=[{&(0x7f0000000040)=""/113, 0x71}], 0x1)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x23}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{}, {0xc515, 0x0, 0xa2}, {}]})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x4, &(0x7f0000000040)=[{0x10001, 0x0, 0x0, 0x35dd}, {0x2d}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@broadcast, @remote, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @random="272c59a1e933", "af2819d4c2e49bc6e965a2e406a9d1f0"}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x2, 0x0)
ioctl$VNDIOCSET(r0, 0xc0387200, &(0x7f0000000100)={0x0, 0x0, 0x0})
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b35c6"], 0x10)
r1 = socket(0x2, 0x1, 0x0)
connect$unix(r1, &(0x7f0000000240)=ANY=[@ANYBLOB="82028b7dbb"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
sysctl$kern(&(0x7f0000000140)={0x1, 0x2a}, 0x2, 0x0, 0x0, &(0x7f0000000200), 0x0)
semget(0x0, 0x0, 0x0)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f0000000000)=""/158)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x1}, {0x6c}, {0x6, 0x0, 0x0, 0xfffe}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x4)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x10000432, 0xffffffe9, 0x1000007, 0x6acb978b, "0d43d3ca00000d00000000000000005b0400"})
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000280)="e5eba711d265cd4a6915a540d2739714f8531ecf959ba2a879feaef4f56a173f42da2cac363f4c2ba46c5bdf83e7ed9c1f6e7a5d5a603b7edac2dbcfa9ccb833e1a33409200425c2879a83a9ec873c095c11b570cba5c4404aef1831af650ab3aa9a698ec660b952e3c35e0baf6eee7557f778c576f122dbd860ba89ccc4098a010d", 0x82}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x3c}, {0x6}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = socket$unix(0x1, 0x5, 0x0)
r1 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000001c0), 0x840, 0x0)
ioctl$BIOCSDLT(r1, 0x8004427a, &(0x7f0000000180)=0x6)
fcntl$getflags(r0, 0x1)
r2 = accept$unix(r0, &(0x7f0000000000)=@abs, &(0x7f0000000040)=0x8)
r3 = geteuid()
fchown(r0, r3, 0x0)
getgid()
r4 = openat$null(0xffffffffffffff9c, &(0x7f0000001440), 0x0, 0x0)
dup2(r4, 0xffffffffffffffff)
dup2(r4, r2)
lchown(&(0x7f0000000040)='./file0\x00', r3, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x18}, 0x4, &(0x7f0000000100), 0x0, &(0x7f0000000240), 0x0)
semget(0x3, 0x3, 0x68c)
geteuid()
r5 = shmget(0x0, 0x1000, 0x2a, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_RMID(r5, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
r6 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$DIOCMAP(0xffffffffffffff9c, 0xc0106477, &(0x7f0000000100)={&(0x7f0000000200)='./file0\x00', r6, 0x3})
select(0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000080)={0x2, 0x5, 0x2}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2c}, {0x40}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000ac0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000080)={0x0, 0x0, &(0x7f00000009c0)=[{&(0x7f00000001c0)="e5e6c163bdc8601aa15d3937aad7b2cc289d1efdc11c0244c696cbbf97cb3a10fee76d921a7adc82053fc302aac7e074bd54983f6fc7d16c10547931f2e1bb6ed9e06ff2c53abea564fb9b8a95c8a2926f318ba222a2345aab00bd493a960281e50c17e7584c1d528dad1a42e15b69bfa6cd96057f0eaa65fe67d014ad80c543f9a20679aaec97845ef6f0232c2aa61292d142664bb6bfb97114f254f9d9052cd7c2566df420d46b59d820ee8d332e18f4971ad570027f86f7457ea4c5bfc23b1553fd6532d56ab9c0979a58cbe00942252f3d923f81d4544447b9d8f68ffa0012ad07d5122b2f88caf89948619e7fb576fdb62d11cedbe44be1aa", 0xfb}, {&(0x7f00000002c0)="79a1633985ec2234fe52e6ee3400000000000000034ebf3a8bbd6bdfb31ad0a513c846df6131acecdf8a6be0f848f933a67f036e8970226f54bfd7c5bcbd58ccebcd51dbc484ebb9329acf7abd42f7b95ced131e092c75b2f53d2718281c0ac7d47f33585cbb54040439fe38b6440da2aa34b0b75410153f10840444720ae717e18aa7ec3cc6e9d807a36722ff0837a5abf412996e579e7ab4beaaa8ed2c648fef87d862", 0xa4}, {&(0x7f0000000b00)="c403e72f16fd8f8e17142e3b22e1b366c72e184ba3bffc7c3e20332e67407f3151efa2757b9bb56aa6a3190c5bf0d8d28bcaca13ddb234c01739d50add879529106fa8bbc1a5741d2492b36644db367459ffdc9fd52828a565c2ca00d1b8d3d95b155bf0c44abf95751631e242afc600e3f2d10d6006b59ff6a08792948c10de923e1d0e0132a3a37897a2bad6add8bbd3e59afdaab157a3eb31ac9866b3feac601f84223dce198641675b2103d98a224cabfc01e98014daf2b44253d582c7f5d6cd969b6050d3f3bc93fc1d52561dd85749cbcfcaf84e1048b2a91c1e496e8bf1c115c4c7471cd1c78b9edf402cac8dbb4517f7e054f1eaf31485d5828b34915c2019b9463282f407225eb96405e966271eafe7ec6038269199fa3a53093aff831abcc565e5a157e6fec700c46cbf8edcb6ed0a4590483630ea9cbb12d59bc46a1029c00401df5f662acba64d43d24be9cae1bcad169f4e23516a76bcb1cf5648c458b5c0c4b65bd37db6611ba628d658e10da16e680f7e58b12c84e0a7e19b6b9cb368a8b9d195a938c5b7cf26cb91b2e68365c7da28339d79a94fc2abbf03ea5bb09396f213f622a0a8aa4e94c5cc87446a66cd500ebd00e2cb94a7f0fbbf219f91eb36c0fb731a6f7270ba234201809439764f7b20780684d9db7f83020ab7e2b4abc712f282b6c8b617a58e6bdddfb1995180849fe9bd94dd7aa3c41865c0561491b5234bab476028a4dfaf01e6869a4731e2623d49c88656b7151f59ed6b601de89e306ddad1c549b18e641f7a0f081e01386fcdb83970a6b43d8a569795801e100a4dc7eee125fd35296973157928bad1ad30666f4fed032e6e1135964ed1f9f8ba3da4b762518d8c586f189685600ffb74bc40f57c06ac1ea19ad879c6fb56883c24d3b62271bd8775b97a6d6635b0901bdd79e8cf7e43a23e6550db015ba2450b3cf84dc551dfee2af81f1802c6fb85bbbe9879b8eca17962226d20e2e3352fb08ce683d431c50d446df0dfb1f2adf2f2eb60f113a20106fcca01d4409ea23ba9f21e980fa83eb089573ed6ef52df5f5d1fb5ddb363601dcaa33acbfc64d3d71f990be15018b6e0532ee76511bc57fd12cafe3d6089b4fe26a6a4d7b31d35a881b555f9ddc5a3a80d33f7b1363630e7e0ed41b58d7ae267fafdef113c25cce54854419443d84fe6e0e91812dcac77fe64fb38ba0367fa80c15dfbd03692e2000430256d91aea6673357785cb5feb9e558f36435505c8ce096926e9cb0412674e1151427f44307718b6128b530b3059b19855a04a1be2cd05d107ee57d8ff5b492fb3ddf139a2590e510b03aa6e217505bab8a1c9f2fc79b9ff94f68dd5a7bbb4a721a6bf76f4ebc208bf9c501f085a498c00b3e3400b6a6b526085b7b645b55abc381092ad959d12dc75cb4bbc4046915d225bc11b9f8005b485a28329fe575527116d87e5b0daeb19070c21067caa0282d570bc31cad5dd04eed0904a2a88dbe0d3d19add2f96e2268a8256bec948424a22d5c7db6a1232b103daf93ebe2f7fad36ff87d439287f666dbcfc2d5764d6ae83eff292152f3abf94ed868f13e2d22b0a77f1b2b0bd92fc244816a7dc4c4634179eef4e765bc878d45e3bdd3e65816c5cd35fb990e1b44b50c7ea4cf435ee4aecd4d979af845fce1f8be354ec40fe9e1733c299ef4563ad126f70d3861a3e4f86a8ccd16c994a21ba7ff0c9fdad28dff62ed6d2cbb1f0bb790d4c9ec56f0778b689672a38df057f804bfc05779c1c86b762daff2882c5f30936f912bf5773325ac50886d7c98367ea35d105d0910a50d1fea0df0a723f97b615def2d25a134eaeacec520d570d11eb053a646977b2d0eb12b3d8929f1cac12d9ffcb1959a791efe50bcaed8f539243de8c40d283d77433b3c04bc672a7a69e62d02724b4017b0b0235bde605d2b3132d83395ad96e775c98f17df02a522935e5f95bfef50f720ed0708cf293280d499f89fa026467ab83a56b3978f73f07e3e7c1aedbbc745577b5bb0dd01091833c649a371b48bb326431126e781d92e15f3d56aad4d280b219c76a3231fb5b435a15c89f03965d30a66fc3a747dfdeb1a785dfdc58533c5f235b249fa0c51b27a544116ef6137058cb25cda7a14185180ae3473c5f39247236f17ecdcc3e53cdd1511ff86e087727004bfebaefe62a9e6b8494027ad64bb3029bf1ad58c471d48e33eb76be870144f56e13e13dcf5fbcae3b1432beb2f3b384de62bf430df18d31da9035806405efa28292a168006bd95d8f9adc8773d0f9cff4cf5fdbcc373d59219cde8e89db12d882f7f688d11ed9bf34340fe36d5c43e818dabc53dd2f40f9c62023e4dae5e343a9bd9023e4b9b29fa05e533254be8a715b80d54bbbb832e2ed268fde5b59ab44ff99e4265f770f302881e19be1f73f1256c3278da2cd27ced5cc7a9da70d9b2a1b7a2affd4f78f9b70cda4eb59ca74df85ae3a031048a31fe3630e41d9bf99327304c58155007bff8f810594e45e6d2d949f7d3bf11fa4a3ee38337d5dd73ba5f99b6e033fdb663d5224a602ac580ddfdd2d5b542a07d50e6c4e81bfa3b641d1d97ebd5f598fead1cad2b0e5b60fb7ce6c985cc0434eeff8aa409bcedb49ab266fdd9b320900fab43aed5f9cf3a660a4bb196a6aeb78460f3e856b6d91d4b2f870d3d9c20b53ed4328146f28a1d879cfc4e4f54d95e2f9dee9a89d77a64e4756abe16d5b7089422ae991397d9c6cc5d85f64ab2f7776fd811be120d719c8e23e41a74757f35246a5b3144b33dfd654bd8abd7fe9f9029a2cc361ecbafa6943d803d4c341efdbf30dc831cb7fad291c7b9a987cbe17e2411f156ad00942f55e1a4abf273132183c9ad1823ebab62f375eed80e4d612e84cd089b69c5e5016c29e3003178acee26ee20a991d160e0ff0f3103bdd696e73357bf5f1383c6fdcfe7da384653b22ebbf6ade48a6e4ebf2d99cf6792f827a71252ef339c8c4560ecadd9c1449ae556b59c34fd6afc4e34d70aca52d99c985d6e24bd4235b58142aa6ac9375f9408579e3da93310041a944d75eea1e2cae33dfe2213eaec2f629e6cdd7d3b612c4ad429134e32c57406b69c315b96b347b4cfdbb1ccb2f11ad837c0f586af0ccab8b40d48d1bf878a4a81398e70da173038f20bb78329192d50ee2aee7a571465108d2c1de50c3555a173e515ca2c028907120c5c93dec269655806cb6293acc5177ca71e16d78ad24c7cd66b9ab100ad2e974fb29b151609a04517436e8c1b7445d121875627b528e458f3919616790ca47e6a144ae8536c413992a0c7d3b3ba250ee1ea6499adc8206e4ac741bed877573c5fb92cdec8a709475355ca1cbfa7492f670b4b008cead22c33c6f7d09404abdfa0f480b8343fdac2ef37a381330d9a4fc0434707a35f7b0220a83d9feca23586a78401924f0b407fb26b0cda77d762e2eb1acf3ff948905e1b52a60307666038d3b3b757eeb329a169032e3449d3d7c37b1e5fa1abc1543e420ca61c56ba813c150031287561a796b0c09bae7737e677c73e38ca1ba44bd88b273cc9acedc33f2a1bb76fe3afb985e94fd4e101b9919c35baf5a6769c2f32b58bc1ac45a43d8755b9dd29e4fc6fda9fbb850e267fe35302abe3e0c953060690efee468bbb046e74edbe75f365cf799574cee24b970964af4797669f4d3ed2004c4a4e9f9439491fd341119a41f7764fee733431ec99db060cd24fca730afdbc422ed96921d5cdfc6fa6b5c671ec3e5517c6294840f2d1cec19e1289941fd9d1203b6057c9f5cda67fa25a8b6a5c4ae9fae083f29ed0f07c55688c009dca6e1881d29ed39e7f7535db4d6ccb3ea87c0cf413eb9decbc16d508b121543de700ab335508d1e23145e7386194572c03ffa90b4305b15e121f79be34422d90f5f50b9f617e785b75e499db2825fa7e42b7b609629ef5f6c1ed52794d7de4e96655b645e68728b5896d4d016de7c14fd4f0273c3bce77fd594ad0277865b32f7da86910959c3f5f72ee0b86d4fc0e1a4231f40b4702ab9b48308c5afaf3ca94d9b58f60a0d16c53aa15c042265ad004a6459e63dff614b711e06836aacce3e6bdcee1eaa073e16083e98da222e8596bb922402bcf494daff3f75553975cdeabcae0d0eb2e3f35edbf5de518a9f17ecbceb27998f4ee1b7cd7d6b80b195022acd8a7112bb8b8fb4adb2faaa83430330ae51ec4e39e91461ccde68081dbda579e6bdc4304c4a8ef49cc5d0a56c1e5612effd6384fb588156b31f1882e4004f3d22e24a7e6aa2fa8e7e5b9a47b51834d6e0fa760187ad855db3d60ff691a216d8add5e0a5d0c6904562f95a10ad13c300d8acf58e47a2f5bff78048baca318a8168e16fccd599189f93a6e4b0250edc65bc52e6804be0cd426d6392c4350f0a159f2cd5931d4f3f06fdb734b43c9dda38319c2046441a18bfeefdfd0b6e99df6ff365cd0600f249a513e679946608fc853c15d8a67f8c7f3c5b485957083eb3c60714a112f725a04521e67e513c88ac1ad2460549fce79d73e516ea47d7f483fbd9ecffec3e7938355c6f7a9f91cb4911a232896855feaa49425e13c7df8fea43e07a260f2bd08b31ceaca552737a14b43ddc0f30a2d0de9e11b9a1ea8684b7a8007d1f773b00c510a39d8712b88cc6e501d181e876a9dce78d8610f0c07512018dc282932b64d77d551733b4dd6e5601607e9521e48aa56c06c059e522b2579170e3603aeec4b128c44603dec41fa45ac15382140d30af09b19b32d7f447e7ebc51d1cdca823767490a701455694b6fe311a9d6bbcfcae9b15c2eec99c0adc609d9d007eaeb6957146b3de03d9dabeccaf05e980c2e8721f39c0f8e12ac13be46fb2a6d1f03372766b4bf0c696d91067e92665559321ef64c82aac1df06921e9a62b4c0f4703acd815412b1cbd84f4de5023c32ddb9fb4b94352f2e53c6ff9a4fe9051237f9414a287eb2835e6651695d6048ce2f3b0ab7a6133f1eed83154d1523adac224a62faa83e01b3c89ae6164833816aeb61cc8929e0f1e38b7bab7946f82e43d74065e7a9a8820a443d3b5b0636a00cc7c4fd6945bbd2f517272d84cbf24aed17c637f4221e775b5f378c5479ee4686b83702b7447dcd8e7df135cd73d0bd1ecc6f7bf2d555bd2f7e0292ec8e63b1e947a2d812a5aa0ada2e77d7d54c421b06e325d3201947fe38bdd5f0bb51d932b4811a4668cf19f7ea835646c17be1ec2cf19db7805b61e811df00726f8474642e2c2bd85dfd9fe0035d5a0eebf94ceb972a4c228ed1bfceed8bbcaa16385c67180196fc38bd6124c99a68ce4f8441d29c4202544d8afbcf758ee28e535bac736c0affec07ffae1322064aa2b2dba87278408a3f5cd60ef852e77745c37176784c7b9c21a14b17e37f3211d9a3b5173245527fe42150208aeb003f0cc69c6e8eb4a3c1e4fa0c8be4000242e17963a89792c9cf7819530f11fa2cb97371618ce6541b24af190fbc3d20c3aab20d5c4be5b937c5890cf11deff8174a5f86ea8cef44896a3ef4ca3665ca37cf23aac9b78de4a0941aad6419e657b014d8eddd36943a4f8d7c948d1ee55064e34e08c988dce0a1cd2dd3ad2f56e42b7e9bf2d7b3032dfffc8d53990f8e67565d7e2fc055412eb1639222686db4366da8caf2fca223a767f436d4eaffac67ae3983b4ef086452036aee11a4fa4262674555993fe3dadfcbc92447ea998ae121a2a1d54e861a9370412652bbbeba7d909850214441fecbaa5f808bdf80d0d8597995df2625366dbdb569a3f55ec8ae57825f", 0x1000}, {&(0x7f0000000380)="4a10fe4eca79577f4e47becb2d48dfd97a375a3b0e04750cddb2ffd95cb0a180c6ade50c0d78be0f436654ac3fdaab77c6ced3871f2c922efe7e420688940e39089f7d1e5857117005bba9c1fb41ddb5d01b034fb2ede3007a74c2c3a67e19cfc685ee51e79c4c41a907722b2ae16ac355a99dfe5f69360efe7fdd99a3e02724ac0086829eb305794588f138c8f60f0e9517a38471517abed4d5614b0fc294d2d09c7bee63a8563e0c6ae84a0d93723f89d18a7537cc7247e3ad447e173418a906cd3a154f1be9386a3d9ff4d58d21bde56d4ad3bd2a9be5c5b8ad07d07a2c0fcb7c834980a9d2ab6ae294112649b2b546", 0xf1}, {&(0x7f0000001b00)="ed524bae6e5b95038fce3b8e3e3803b9ee27041a27631626d1e301788e8c12539b547526f4ddc041533509230e8bc599ec0bbb8be1158d080687e84c24b7500d305d2d25e51575c30578fe0d5ab4a5c01e6a703b90a217944d761373220850ff259fd377ce00b9808960150dcd73876ae95e72804c43ece9ec8ed278fd8c2522b6516e7a303bb7f24efa30c77c3fd0b09f915bf0253cef3afe9413dcc9455ba0b0758e4072d51e35e7369bb1d95b3ada6fb89a0d2076dc8e9313c42057df1eeb38d950b3caf8f885325b4dc952f89449cfa36e01399c4e8ca7be5d6a015fb39a1d80ed807f114454a0de837d590ec80ad6e28f373a01c761c3d27c8be167aa516a0891a7d22fbfa1917fec90d8401c4ea1d852f7161c9418b77ed6a0e042a3f2ca9222467d8fa96cc011c6db575955369a86eeff08167f6116a1bb24b67ecdebeca7ca760872aede2a4bc9e618ce17d1990fdefdfe3e8f0ba22b9e357a186a0e6314c938e9e7247654f3685d8321561b797c1f2154810093dd390195d37a589377611c81c3b3979829c73a493fcb60ac41e3e49d78ae7dbe7be3096ad01bbdef09f7a2cbf6e0e6b0354071c9ade449aa539d588fbbe698e2db664f6b785e498cc20b05dbb628e807b8757e8776007c1c4aa1105fb14ad5912912827275537f16e882a8e746c18d86549ece6cff1903370c6ffbac55557290636372554ef689b66d7e39ce44a4ea126e5744bb14b3f11f88377bd98b80733eb8c6979597a57b329ebdc18a6153bc89ee45a094854e8c76b0f42e070c353cb03c3b3ed5a7d4dd9fb643589969eaaa91875f54706a75c76c0fdf448c8de0113128987ba20e2f9c0423e4bd4c5893411ed9debadc6d7ed15936682ef19c000df659556bf6e99674f16feea213ff15c253c8e65d10209914459f33edfe9751bc9e1f11135e0190d8910a6e09c9b2ee7182367af0641f75dcdc5a7932336e0d75342648b394d1fde46e4a2614279074672242f0731cdeffc832ea358c521ea211d5958bee7b149b23336eb91c409a693d248c5adaffb246041ea84473cd2436fc8c1a677a97b15abab3ad9baf6e82748a5ac6fe154bb088d1f96f078b594421337d2632779c9a339e2af9d9eb45d18306a599068fea8d2bef4bd50cbffa26afdf64474bafa1f338e9aaf79d75ae185089dd8ec0b998e46fdeb381467b929b314b6e6304ea36d815c63274a08716bc4d82a2b1152853e32b7e3ddd75dcd369edacf99db07e9a1e10709cec58aeac80456ccd082031c3503fcc0f785eeeab297e76d8f5840a148eed8401ed69a9f670462fe6e9e8f72b2d196171a0e86b070841fa14aed5b06a7c8a8440f123b21c1a9383ca278fa637b527185dea694869a99107455b448647973793409407b74f05b63a3a9cd0d52d4675486417535695ef1727b80895c5aa1b6f360d164b1069ae0be632cc8c31277cfb050d51062b6a551a5666c015698daafa14940474e242822d29ebf2a4baed9d174fe9cb09389c90a61530b81c79053d0c42d8ce7df405bcb435330215b7afbbf770f619f24aca038ae1ed400115078f6e264c31ba14c0378543bac5a8bfffe9c9344f6aef7912972f8b3b6949aac0f95e5d1cfb9e003e2725769a3e3d4504e9ab8720901a5fbbe1bdf6a90847f6d8972dde8352d0f85896d410fe3849f57589565a94e131f637d78277587e658b065bc91e85565e35c49b4a60d4f368b11de94de3baa81f144e1d288adc1f93858fce2a19884c8d7c477e6be5d02bb854a7e0ab7c327382054fa0c1545a4cb02ed7be1f0652d2201d0983619a5e33f8f9e420f84c9086497e7f76243f0d7b8020022460694f34652ebcdfbd3d99573332488c732dfec990c701e49403be76e11fce89c04bbea2e84ad306cdcba18b921b2b4c412beb1c414b183bb840509b7cad690a75ab1e91c960cc3e8ee4a7eb554cc68f60679554", 0x571}], 0x5}, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
poll(&(0x7f0000000000)=[{r2, 0x4}], 0x1, 0x0)
recvmsg(r1, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
poll(&(0x7f00000000c0)=[{0xffffffffffffff9c}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3d, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000002c0)=[{0x5}, {0xc}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4b}, 0x2, &(0x7f0000000040)="61b1e8e2", &(0x7f0000000100)=0x4, 0x0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0x5}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f00000004c0)=ANY=[])
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/\x00', r0, &(0x7f0000d06ff8)='./file0\x00')
chown(&(0x7f0000000040)='./file0\x00', 0x0, 0xffffffffffffffff)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)="4fae", 0x2}], 0x1, 0x0)
semctl$SETVAL(0x0, 0x0, 0x8, &(0x7f00000000c0))
mlock(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000)
madvise(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086335)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f00000015c0)=[{&(0x7f0000000180)=""/148, 0x94}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000002c0)={<r0=>0xffffffffffffffff})
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000300)=0x5)
writev(r0, &(0x7f0000002680)=[{&(0x7f0000000340)="db081818f0b5d3e93fe1fd965b9d60f4033a9cea4929a518eb39ad2b40b52ee07364f38adac4502627f6f406a6ace8ce75a65565ca9cf86e1275c391afa738a5f1627c2d4a5cc1f68fe7f1faf662adc9fada8b2b4e1b122b751d3db7e6ae7504ea4339807345f28f832fde6658279d3eaeeeaeb8c18d5e81453bf7b370fbbe04e45c64bdb35c54aa632e48cdbe2856baa08c06672d80efbcb23fb7684361c1aaefcb0958b422e616adace3bd275a0e7df391cfc09d9a8099fdfcd211b212cdd1d26b1dcfcaf1c2a42be8cd48b1e8df9f03df794ebf86bc2c4bb8ef448b4c3e4a9a24b0b591e6fd", 0xe7}, {&(0x7f0000000440)="44fea0f63786740983b0fe408f3d7489cf3722ec0422233ed40224b337d71ebbdf8a873b0a15875fb642c80c710babc9facddeeecef69f1e2d96f4aebd37f7f451fec6081dcdf3d4fc4341090009971fd1f0914e96f45726f1f3033e0d37e19edbc447a97d65afa29a44da58ac0fbcbf4d447c813b64d849d67be780cf3affb6c7576f409673a43d8e49eff615cce3b29e7b2efbe42d15d8dad1be3e34871f5fe69f4b07ef6af370b1e32c2eb5928d4d725278c427e7123031f84976919e9643ef740609b066afc0848d0be8f0f57df9f4a3b1961d80cc9559599952e39fefc8bb9aee59e622b667cb1df11d1a7090ef82b47a1f823bac6b9435a10006e9b5a94395faadacb43ad35e87c4669aa62c5ff638ffe32e210156b3e92aaf3be4f373ebe06aca1a139f6b1aa06f258808192200e0031acd1de063147076777579eae23e5071f63f4c3d8e77b60a67534c570c8331c7f6bed434daeddbb3951dfea3d4f75580ff4b77500cc91781c6d0b010fb1b131b4e91586dc8ba8afaae42ebf1ba56a9e1dd763f9f743420a58d99ba73ace1070d9d210ad897c8690d401d3cc3d7b8442816ca59d36a3ea46db7ad47fce3f0964684ade0cd6f9efb40ee2545797f4768b5268d22c29d5c80135ca207bbd4fbd5227b0e9ba9e841f8d3fe7774245b5f4dca4ed4cb6f7a4e1ae7f8d428dbe77b2c96ad5dca7f1b247e7945c87e9fd8bd41ddd1d923c7b3d2c1b691bdb9296c1286f6bb579b7f432b12aaaa7d4753101c1e920e6121b2d0165183aa4938e0c5849df3389bed7ebd3be49132cab4f0e76bb2c9e2b9d1fd5ab2d3f732a4c131d926ca9cb041da70ee44099c079bde919c0a2328fe7c33deb88bec1c42d39288730b1a4185226eb6250fedcbb99dee6bef86a34f256318b7230b82976bbaedb5eb873498b0d3165dcc0b28ce62c590afc5aaa8102a6b14ded4db0a720e49b45c94c74993be0a2180c45e41e37bee8ee5d7a73740c4e95c8268b47854ecfde204c7c3c4eaf08b666b99194400e396a1bd822c6a16b9efb3d9383230eee8ca5a26c3d78d49ae7be9a4494c0d591b7ff487c186c7348518503b30ff51ad2fd855cb112b360605b8eb54f9ab1c38472f54fc18e01890a722065b04d137fb9aa0fb4048b3ceb4b08e2db70b2ab0a3664a38009c6073f6da5b525449da2c02aca25ba4739ce5d14c1d663c928f53d4fe1c596b746e207748647badcf76087037aec73e87d40a07561f7e8991a1b59b5726c605735476e45c98c1dad0c57e1353c1b7116bd08abd1c43437ccc9481fa0de5a4cff43cf92bd7fc11a5d582158f22af1d9c32f7729ecfffa0eeccb08e9f900ded8954ea4849df365b2e49dce007b32bfe8a01ebc285d11d56253cf3af2796d8ef9c5b70a4a1612f2adba0b9bfd1999e0a4e9f8a390837c3c84aa2a13f710c258d32df08ffa413084e54e2eebac406637cd77858cd4be166e5c0d92c3f40d27451d41379e05c94277b89b079bf78db4eeb29f7ee02949f42ccef9cfca435730ec556a0957d1c897cfda8062612a8788f25207bc9f62153a4da6d79c85472ca2a413362b579b6aa70e51a0f5345e591b745c57dfb65dccee4e4bfd9e220b3ed7e01d75aa666186877d14fa0e2255fe58f078c8a1047af8049177e15b557b1dd08b29e7c1d996c77ed409d2e9f8bb39ecf167df6bed8a96cb16d00e9de1f1d2b0040b9a7561f701b02dd5879b8cdd073229561956e2ccd6b4dec813d37a799a5a296ee11a43ae8e9ead57930bf8b1ae1848dda238f085575778df6e6c9882197294dd0f9479b64734ca29d26ac144ebbc18222fa299bbeaf348813b323d235b39548547fb09891343ee84e7552802c3eb4791998d764d1e7fa9315a2f479a20f7d0c6e556205bb83521c0421cdeb8d00ce35882e2dde2c97732047f4137f2d286c17e7a643fd7b39a4e00b6ccb75834a20f6525f98bb7e6f95c4dfc62b8c8d14e6b45ea5968c0cfed53fe89010ddba53e8bf498b285b17a7952fdd7950868ca91e90a4ecfd07210ad39e615842b7035338ad253a62eee58ade2ad22d827389d0f73a8bec1736210ee75f3c76f24e56cbf33fc88db25d7590998c6ffa2e5425f73a2708b50c5388662d28bd47bb4bfb5b5cb302be9b61572c2d66a10db2b998ff855481161cbbdf52871d6279fc59abd1b4c67fc76be66a63143a6e4db44ca45ba94ad60c42737af40a63d2ef932b8560b11f493f413669af8a642541247a17042d50d5893d79c50011d633cf61c6134019cbacf98e6a3d7f4c9dbea6613ff7b28e0015902dd1f134e6c80236aed88691a5448f3f748edfe11e64ba38ac0b48f89cf509cc40f44fa6eb1f755885a468dc000fab4ca82a2b4fcac4dce4354650c9193f60df0845e493a53e35038cf9fe1bcf3e0c6de21f5ac6e5401829acb4822d956c0fdab418995570f839267367054116bcb88752f30505f2a7381f96a53cd5cdca996ef4cac5ed62e82c52208e3ab4ffc6b219fdbbd3d27dab3d739e6d29030efa30205807d8456ccb95409965576816034a40efc62791bf870641981984ab116e6ed5e7db016d416db4271b85bfc7f00ae0453810dfc8e18ca009972dd027f7a030ee7a291f54840494e4cc3f5c9c910874649192ee3d8e08bcfd58835e3c8eeac4f494f2de8921e9c36bbaaffab834b0fb3e2cec8026bcf97b2608e48b1bd9f9c33fd706c31eca6dd90cb9d849fc72b58de13111db4a3900c169d228b3e5e86e640e5163ae7e6ccfe6aacfc2471fffa16d8dbd34dd58b3ed334883b9226b32b6e54f6b5e58fea7f1ade39f30ffa224e8cca79632882f1a4fbe65b3007cecf574624e135ff2da45db3749fd51f924941655c2c90b715b1c3f627dd9ac375845a3b3b0201fc6a670de04802ba1c062b03334920e3d92b6703ff818b423364fa2a1f0617611d40ff1fcfdf890eee577eadafc20335c6c9bf9b067e37c407cfaeff9c0ec98c1760cf09fd35c5592892cdbf95ecb4fd2554cbb2511469e653fe2f0131f912071e4765af181b856035413030eb12fdaff55f2c4903777b75f0ba4030abd3eb2a7e08b898ee90f528e443d3b2339047641b54e82f0aefd87d0dc764d473fca5af712ed206dd02330e669cdb09568f3798ee3fa1b2a7547e9c775d52014329b5faf69c320b623e220094bc7afc2f559de4b40776361295aefe7299c972eb50c9ce6d9744e8fa844091bdc7cfba1c10a793eaebd9f22bf6d904e1a1264f5f55e08ae9f17995142ee76d7c70796511196cf7be6ace78908b135adcf56cec00ee8aba89302afa9666c7fba644c0b5b2842557c17c7b62a1b949ecdc3b2428448f1ebf84ace8425028871b6f57cc0a113bea86c93f3b82e1271c97e9697270b1ab6d32818c2175166abacf6cd55e53a530f584703a3fd61331ad2dbaf668171e3980bdc6c976078b752fd7726fcafe350f64e916fc2172a98803c22b63cdd6c11b42e9595395217d57b3ce36c6e7fc2acd05c4835a0f5dd6a28a68e22f7b77589fce5571805e3de9530f7e480a9861b525e87e0d625bd9cdd2cc6ac2524c17202cd5fa2690ae43c6de23338e49b52240fe61e773012750ff87b6cbd00c5eb60af126d27bc1b8c85cc8f30d76d1ab4d183b393e5065204cd9ac6a89843fabd7ba17cf4277a8d9d956ca8e2354ff33f9b67a51b33d7ad526d0f196cfca659c06daa3ca148565ca2ee7318e598f158c9984711fd5d53660de6a2db34a90e3a1f745133dd6e1303d2bdaf3d128ae047cb5b9d28f465df880210691bcee34570f7d69cca202c57f4a0fb5ac408b75299407e9217f578786547c85c34927a7115f56fd0e9b7c49e6062ea86ed13c64f91363b38618784e2515f645e73e07bc55d819e54cc4241be8f927796c12bfba1260569a564fb3b4adc961a564d8f028983b444590405686c5fb4a6ab2fba0be96148fb77780e14f0ecfb923af4b30faf32e391ef69de3065518045322961d58ab15b535328a15122d84502ccb657a5ad8d2ee66de262fca9270928afbbcff360f2bcb3d47fc7359edeeec6f05b8f0bd1e721f4a7038bf5a8253cfab219344979a1fa8177237b5005f67f625b046600aa554dbff64fbf9c74edb850d519b3fcb0a48b300a20afc31afb8ddb6ee4ebe2e0af07da4448d187688e32687ce9d5332f51cb2019f24637f40d1ebb8ff891cc6a951e896f7d0f3f46fc8a4a50ceea87f632b7082e78554ab8568f745c4f6baa5d769c50f20c8a9f46faa2c60f0a6aaed1ab98d5adac1445e5bcf1c591699cda622398e902b14068d2ab554e6c53c4b6fe3867043aa896752a83894d27e8517047c6ce5ab0a50da44e4303d00e77d8207e2ef58f624bb213d15a2fea6ed9b784cd4227e4503980ba5705b6db32c39bd74f34a88145f4c3382266c4e6547567f5182165be8034223140821b5ba8239474a7240b31dbc67bbc071d3535761ed6350f6402aa94e54b376b927b4cf18cdcd31a5f27d0728935c6ef78bb53167eb2f6f66e202cadff482a50b791fa83511a3ad5204a6f7f607ca835bafc4182e9e57b8cf0bf755cbb3a9d9496fc92d475908325332d669b6cda63a653e32d268477f8c215a6083c0ce7694e8b143eb26ed94cf128d23a79e7726a27490b0ce2deeeb01ec881349fced96b3a85b84b899a20ef74a3a12a00f729b8cd1a73b182ffcd36cbf4a837edf26d34d68c6e103a3baea1027d0206bc3729f0f6aea86143eef39d7dffcd495ba48d71a263d2c6ac99d559aad2abcc484c74cb96ed200c29c2c6245a037802864a5c2f98d70d70160e805057611ddc368f48121d3b1555505c3343a68ab5fb9ab9f7e00d8762685ed7f461e76ab856d236376d10b195480249328960484594b3d586deedf8a06a0ee67212e9b321f5ac7434e6083e7e8155135deb21f715f2730a5524ce72b5af5bb7335e1d87723799d70b409313f858963b753bd972f223b65a084e515fa4b946da81289d4e45b129eccf0165db6683239d50bafbf90216834a6fdaf95ab187f5b7111d02eae89a27e505cd79594147431a678a84932c10a70f376fb55764c37f6c87c36982fdd6b0433c258b6501ca5fa6e50c179fcef41347d770ea9874b86cae9cc44815d47bf670ea722e9429632f4874377d43bcad7d89b44776ec3b8d60ee78c45dc208b3ab1cc087b322243558687fc81725bccd8144339656210b23cd287cb1729b1a17464dcdc83b3d72d042165cc1a30e4e088f53f08e73c68dc80c9e2081be09ca8a89770ea845b2dff5e076dc8b344e72a129cea67569239a27a5ea8704102d28f0b8efc65d4103a72bcd31ec42c32032061de40f77d30e5feae4730e909b004365adec21f7c70cb2bb37f8d17897bd7bf2b0711720b9f6d5ead43dd826da4568827700f3a495f69c3471db339e0dc26045556282996475eb15bdd49e8cbeb2fac77c8173dec398858b48f76449d80f8b3ae297ad8bb09fa4b43939f2d735e9da47db85b1ffb9f5a456965a5ebb8dca4a7449d0db25f7e6dfb3fe5038e8da1e6dbca5a6418162e99dbd8d2d2444b905f9f6a51851971422325f7007098246192b7904ab342945fe824f11c0c6f9b140e575bcd3be702c0d031e6a177007944fda9754041e877c93d3b68ad2c37b84370c50d5e6a6a6805222f60461c733ec512aa93071e1c3b07e79eb83d92e2e8fc528c3aaafd0d4e5a46c0603c293d4ad7b76053d55511f296857623316fd6fd9e095f73a34ca0ff06877c1a2a90bba2bb8b32767e5571f5f37782bfd3129f91eef38dc8ccd", 0x1000}, {&(0x7f0000001440)="4aa5f7eba43a51267ff9f91c61a215ad5dcd82503210", 0x16}, {&(0x7f0000001480)="d6b02285d0c43d6751cb65c92002971ec1b426b29912661722c7518af4eed038005d914a60aa596b7d9a0ea87860a5756571594f9e547e392e23ce65dabd7c072f15ccdc15f6d9c7ac1d2914f18dc7b6dca939e23edcfd16ed910556142022140e3ec0dc1b1fc2b9b6effdae850e0f86bbdbe6a746ed0456713367de4eed4a50fc5820084c9574b72133a50297203d8deab86c966d5ac8a4877b5e8329de58a91eaced640838e4bca14714ff8aa0caa9", 0xb0}, {&(0x7f0000001540)="77780b125875ae1d608337e2fc840558", 0x10}, {&(0x7f0000001580)="e6b73063005497bb79a36b1e6e48dd10c1d05d57377e343f099e5adcf60d5ac043fa5f1e168536392746e0f1cb522a66c5be767329d8e6fe71588617de2cd976240c48b1f65fc3ab784316839c447d97d1ef2388c43741b7c0121172253dd352083c01f70429b1cf3439ba775d84eff5a0f59fee764146862305bbbad3dce902ca6a4febf76cf1996f76cfac973fbad98f5e52f5158e5dca9d52f0bf41e66def7ca1ca26cbf82d4909fc5beac471dbd51467104d495c6a12d959fa04abc1cc3ed3e3096f83", 0xc5}, {&(0x7f0000001680)="0bb70ca1fe628aae7e1275ae61ca465025368390d0bd2e827cfb03f88368066ff884222357228e4d36670bb307daf93ce0af1f9a43cfe7a5191576dfc58d671786bd93d90a77b66059b96ebb35c3cf1cf286f3410415ef5d63e3c661b9feb1219134478b4ed8d10b3fbde5e2f744b3c6fe500a8ba09b270f0b31c47a35bcac9479e75dc46d2f4c03df7b6e7cc8f3cf9c8c0d3a50b767536eff48b0c532cff26d70733a79577d5d338a8168ff282d23cfbe6c1646448246064045ec8a92b1926c061dfed81c76f2966097eda01e08e3f1494526f2316704f840e02ab8be3691f5e85f061772daf8d4069f691cb1bfe7fe810959ed35241b344091021ff5c2bd0f873ecb545449164a770106d7d55e5c43313accd87e145964752b309fc252570d0702e0bc720f9a2b98698d094e9788288406c3e2ff73415007ed5609628ce908c39b8b054189bcecfb8a48debd147ab1688315b4e1560c2a187aec13bdcde40ca9dd3e60d8726fb801eb5e25e5e3dd0acaafe034f05610b6c7e005f9e282e47e6fdcc01a0319a6396780dfe1dab9f38f90feedb5f251f532ba10df6fd060ae4940f82ca9999577143e5d9fe843670efb0bcb4f3187b23ea12de99f7e845f674b169f5de1996ee90bc4b6b8156379af04c48c34dd04f6ec16a2a849e3ffe1b35c3460c9c336afffd5e76469eeef00ee5108fad12c55452c011107966d45628e1b383c81b7722419bb2ae07f78613b48e01543f99f313d4ce1b5c6ecfe43c7f7cbb901e6641839ede8c2c721452e201cf560f7c122a3e0fe17cc639058027be19224dff18753da4956b8183aa066cc9ec261bda037c70dc790cf04facc7d843e8669742513166c058b3d8e294ddd041d0a432d6e0f1e06f4ecb304e3e18deb53d58eecd338577ea4471ed3940cd4dbbec52d8f7603eaebbfc04fde60a46e42d17a4c24d7f82da8b24eeb28cb14329f7da1cf333930be89f2daf805be59e7f588a03052579d0959be7d5f13cc96c23825669c202bfe7cc17ef53c8831edd20f0d29ec2151a20d50f9cb92102412d6cf7e40696eb1812a8a1dff1d98299cbde16e5ec4437429a9b279b7827906bf9b69324d874e83c53ab82a5900166b1229f4a84efd141b151523d3ac78a2660a0627b84111e0f418cb1c312ec9ed80e07bbf160554660f33fc9cb404b6357f4217e97a639c7ecc26ab6e0f89e7ba38306223dc26002d12f481de573f91d5dccf2adac870c1364da3286fd07ab76d2dec9f3512250c24e9e11eb8318d56400e31791614d7dc51ced3ac98cfac3474dcf699bde48161b895b88da7d7ba4b98bd1db9e9f439fd61d8b94f39d3cbc8d83c89e217ac277f0e9411a32d41f3c5572aa33cc4ff534f118747f41a0f855d640dae18a7b695425c3a2e1823fae6761b6a0ba7c69e9d27d9d743f30282b4db7180f5728518be3896174a16a29f5c5cbd806c03ccdd7a2a1474fcdbe754a7a122f78b1e03b548ede52f1c3424c3ef10b92cce8bd8f86cdb32f1200c023a7654c28666ecba83eca1daa32ac7cbf2199233db6fe069101eff26936479299ec50c4ca3c4a4a604b29f1d353e4bc42a396a6733996caa04f16af9cc4432cdbf0d057ed31f9621a68271a89a66dd15589342d8b25c0cbc2b721dcd468dc11cd0fef0e69810ad5aa838b6ced822adf5ee87c56f1542c3387f463f02ef5cc55537a6bf45643ba2f76f5dae2275ef2554c51059f126198d0021ef54ed1db217971af5f8a939b78f8f9a8d402349dec698612e58f271e18ad76ce424b9602519d7e349c5d6c0d84e7840ebc1fed204eca6506edda786b294f027e8f171c6d6d4087d97611a1667628c6c0dcc81b17e559964b3ba6db9bac34369516818e3abdeb31aff7741a7ffdd9802e48e2cd4fe7006cecd8779d45c366afaed5b525bceb5e5cb10b1bbcf99514fc8d561744f3c17f2e0213f130b3f2ed7f38c0022e370f7761e2a50d5795c8d14f90105850fe167057a37af6c89da474f62b76265c066830f432280c2b82e1b23c17fd92c004229752086346f28b0f928bd45b683f58490bd7fd642fd9f77669f5f7f7352f7713b1d94c97c9b158e156b683194fbc69e5ff6ce7c7d87dec64ea0252d143ece3b6b4925703507dbb2cea36677df0ff6f677954596a235e80a9e6e59ce9cb971edccf0912fe282f479c242b13d4d9ab7213ec0711946193068ca4117a6f68583690416fc43ef8bbe532c61e0bc1e54c9a698651fc217c81cd1617ad8acada346fccfd899901be14d4ec57c3529be6570c800f3baa93e076e47e24f283283739d7171544127cd3228d3662fc9f2e9d6702a246b9a8143379e2943e31fe0712f3f1894ba652ee47ca305358581d534f6a51be4a27ccffa35d37594ca3b0392069088317528d6555c3299972c42c1a56c907a6cc73b2dd3d834cd56fdda5d63ecc24049baf6e93e75960b226a90c9783f96293efa2a9a4750bbacafbcedd9d92f61a3b3b789115530e3ea82791242fb5acb90583b471556ce17890d76db1d45b731a4eb661d0a05b878ac431db498d08c1637430497517546bd967132a05f5fc389dd7503d0ca0b6d32be68eb316c9414389b011e0bf5ec28434de2c6ee20e068338f3ffa9378b488a9a7a9ce5efd650ec860d3b3225e66dbba9bc60567f72790695c6e50629751c5ba033809371aa44a497179eeeba242e4998a60d33c96749b54c5c4881e55effcf1626dac3487f855db1dcbafbadd708b7b809d376a0f0108f41621b26f0348cf9209e08eb79656edd5456dfd85da9124f684e86001a335f4a9a76d4de87a40df1862a44a89da7f465db9b767e547beb8d223201c6c954f8821fe2fb832da06d12a02fb12734f3f9a95e5b2f7d90ad30d5eff0e43f9ec6efc9df50679c17b857f7f10b1cbbc92a13a3cc2616b3c5f07a52fde4ba29c27eac8fe5154d8cd13ed8ad4d202d963355b2561e8d3447f4e51e933815e8d1945ab6350a3e5268e41fcc7db11f0493ece30d65eaa25849036ae9d934815029dfbb7d7fc955165e204e05a557cde7842b492dfb1a71ddf6aa9b0812d844450b02ea028b44d877631da2e0137c6ec40905beb3d41ed2be34b361d0bce53dc4558e22a3f51abf5643fd5bfbdda41fdd361287572d0f40adc5cae0ab716745808c03b09f35f4699ae24530cbaf75db3b252eb7aa51fcd1207cf2cb1de095b46c42afb0abe5ef2741d96cac461bd07a9a1f1a08cf4a405a0ab872c0fda46becd37160555d022c75adacd2e6706a7f52c50049f59fe7609bd41a69554b94dd50e8b281f7f637b857d58547ad49eb15249db6c156781f13c4c7f721563a837848eefb33c64bdc815a05d55d57e35d4b5fb71d014a076f4a8de2c3ce50f1350e888cc668486617b4a11428f95ee2649d665ca9a6e88cd0e3f99e8b6574b9df1c7af7ed8a2781bbd26abeda97516e33f967076bc4542d8c3b56b936b1e03714e1a8dda6170c41ecd80e7be5c00bfc7b377223b42bef0f39f245e18c3a5b17959a14664cb89b8dfcf3e456c73ad73c4ba4f7e902265855265d93585516737a6445730f048c786cf6be456315cbe28a34076077b4b2350e6db29a12efa065640e25a3f13b0f3a9800bc5b710aed0fba2e3264c85cd6cf32b158fc4e0b45f535e8b8f552df4399402a6e356a6bce6746c1cf2209866a765dc1d33ec63a01332a6c1d16fb571bb9e887f21f57e5979e68eab3397023ff2d6faa27b0c7a6829e2dc34e2f9f55d3e103d6cfa21a0a8c1648f2401b3445e07bec223ddb54da1963b1e05957902f28536861f631767fca4e22070f3a112b333c7d903916a2b3bfb5e49659e2489bb67a9927ba8dcf9d467e6de833e63cba9914722c9f20577f50a9e520fc879bf36f996ec88f08a411da30007f6381a89195d686987ec1eaf15242ea148ff9f2fbec28d39a0ed4f67607f686f6c3098c04c3305c7bc96c8a26779caf7f61dbc605d7663a291d18ee004bac43081df2da03b26667a7515a213beb98812163c2f0dc0d7605271af3ee933c1194abb3f41a8196edfb4d0b9ea25d26ec47dc762b64b937066c4ce9150832d6475efae6b736056fdb0428d20cd7dab68532d4c786aad0895c3f9c1fd55daa8d0308ad26032fe95b63d87472a3bc835fc7bb265338ddc63de6b3a47a1360ac87635ed704e70e618590728c36ce28a7c604c1aa4ffe3544f18f190eea4efc000ba3132ff610ff49d5bde9d3b5ad519dfe37b36c70c0aca4f2ff2c26dda35940de330e7297593142dbfc7a52c22d941996841f1cea5b6f22b9178259032dac8242b086e38b8765b75019a51ef0b80ba8564f1b689f90bf9301052d348a35524a227407d6e1858fe1f4e6a03c7f0ea5186b5331ed2c744f829f30535b9ba8115c1327d426195cc5172462ac62ba6358f516b675ea2eb2f8a709f3d4a9826ea47e4fb1105f3cedd026ee35742ee6fc04b8983a4584aed7e2454ff7ebb0844307c4ea60a375499fd371afcc0d88986dd0a39fc2ebb19288e26372ab9e46a9ed1f8aeaa6897a485cbf383c9ba886d3404d61dfb1cf6bee004e2a2b6e1864ab8ea3b0463eef84d3c0222472612930bfb1e57e9e3966615f9566c33ae994b975dfb7eda4373a0387b5169d20ef116a02228d62b4901367ca377869a6445e7094389d5e57c88b1e570b86a26f36b7cb7fa5d16c383756d0c5c4231c31e9416f9cc0554226b4275f5ecc4b58a78331bc54d1b4a2e407a5046e8d88ff8b53af2f3513207f586bc2a56ec515d55925d8c21d76a963af0d3c64200c48a14ff7fcab37a0e960fb9dad48f3aac7bbe7567fae160c952c1aff20822af2f3b78d185e8137c50c5054eb42537788eba4aba79f3d0aa58ee35d6b9217383a6bd0f", 0xd7f}], 0x7)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{}, 0xfffffffffffffffa, 0x39}, {{r0}, 0xffffffffffffffff, 0x21}], 0x8, 0x0, 0x2, 0x0)
close(r0)
pledge(0x0, &(0x7f0000000100))
pledge(&(0x7f00000000c0)='vmm ', &(0x7f0000000100)='vmm ')
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x45}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
open$dir(&(0x7f00000001c0)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x110, 0x0)
r0 = getpgrp()
r1 = accept$unix(0xffffffffffffff9c, &(0x7f0000000240)=@file={0x0, ""/250}, &(0x7f0000000340)=0xfc)
fcntl$setown(r1, 0x6, r0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x40001a1a, r0)
execve(&(0x7f0000000040)='./file0\x00', &(0x7f0000000100)=[&(0x7f00000000c0)=']]#/&\\-%]-\'\x00'], &(0x7f0000000200)=[&(0x7f0000000140)='#\x15&)\x00', &(0x7f0000000180)='\xf5}\'\x00'])
r2 = semget$private(0x0, 0x4000000009, 0x0)
semop(r2, &(0x7f0000000300)=[{0x0, 0x8, 0xe5ce97ab354d96be}, {0x2, 0x202, 0x800}, {0x3, 0xff81, 0x1000}, {0x4, 0x400}], 0x4)
semop(r2, &(0x7f0000000240), 0x15)
semctl$GETZCNT(r2, 0x0, 0x7, &(0x7f0000000540)=""/233)
semop(r2, &(0x7f0000000080)=[{0x3, 0x6}, {0x2, 0x3, 0x800}, {0x4, 0x5, 0x1000}, {0x3, 0x9, 0xac61f474f541193d}, {0x3, 0x7}, {0x4, 0x5, 0x800}, {0x0, 0x22}, {0x2, 0x2, 0x800}, {0x0, 0xf1b3}, {0x0, 0x2}], 0xa)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000003c0)={{0x104, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x6d, 0x47}, 0x40008006, 0x5, 0x4})
semop(r2, &(0x7f00000000c0)=[{0x3, 0x5f6b, 0x1000}, {0x0, 0xfb5}, {0x2, 0x9, 0x1400}], 0x3)
getgroups(0x1, &(0x7f00000004c0)=[<r3=>0x0])
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000440)={{0x28, 0x0, r3, 0x0, 0xffffffffffffffff, 0x11, 0x1}, 0x6, 0x3, 0x7fff})
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000100)={{0x1, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x41, 0x4}, 0x0, 0x0, 0xfffffffffffffbff})
semop(r2, &(0x7f0000000180)=[{0x0, 0x9, 0x800}, {0x3, 0x80}, {0x3, 0x401, 0x1800}, {0x1, 0x8, 0x800}, {0x1, 0xf8b8, 0x800}], 0x5)
semctl$IPC_STAT(r2, 0x0, 0x2, &(0x7f0000000040)=""/31)
semop(r2, &(0x7f0000000380)=[{0x3, 0x8}, {0x4, 0x5}, {0x1, 0x9, 0x1000}], 0x3)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x5300)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x34, 0x0, 0x0, 0x8}, {0x45}, {0x6, 0x0, 0x0, 0x200802e9}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000280)={0x0, 0x4})
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000200)=ANY=[])
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
read(r0, &(0x7f0000000040)=""/32, 0x20)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x13, &(0x7f0000000000), 0x0)
openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = dup2(r0, r0)
ioctl$VNDIOCSET(0xffffffffffffffff, 0xc0384600, &(0x7f0000000200)={&(0x7f0000000000)='./file0\x00', 0x9224, 0x0})
writev(r1, &(0x7f0000000100), 0x1000000000000161)
writev(r1, &(0x7f0000000040), 0x1b)
r2 = open$dir(&(0x7f0000000300)='./file0\x00', 0x2, 0x0)
writev(r2, &(0x7f00000000c0)=[{&(0x7f0000000040)="0771f3d25e0505139d761f8af1ea0ba026ea7c304656ec4b", 0xfffffd0f}], 0x1)
nanosleep(&(0x7f00000000c0)={0x0, 0x1fffffff}, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
fcntl$dupfd(r0, 0x0, r1)
r2 = socket$inet6(0x18, 0x1, 0x0)
r3 = socket$inet6(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r2, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x6e)
r5 = socket(0x18, 0x3, 0x0)
fcntl$dupfd(r5, 0x0, r4)
r6 = socket$inet6(0x18, 0x1, 0x0)
r7 = socket$inet6(0x18, 0x3, 0x0)
r8 = fcntl$dupfd(r6, 0x0, r7)
ioctl$TIOCFLUSH(r8, 0xc0106924, &(0x7f00000000c0)=0x6e)
r9 = socket(0x18, 0x3, 0x0)
r10 = fcntl$dupfd(r9, 0x0, r8)
ioctl$TIOCFLUSH(r10, 0xc0106924, &(0x7f00000000c0)=0x101)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="b1020801"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0187009, &(0x7f0000000380)={{}, 0x0, 0xa00})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x25}, {0x7}, {0x6, 0x0, 0x0, 0x200003fd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000200)="34cf362b3c3dfd6039bc17b23704", 0xe)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x4, 0x0, {[0x6]}})
r0 = open$dir(&(0x7f00000000c0)='.\x00', 0x0, 0x0)
mknodat(r0, &(0x7f0000000040)='./file0\x00', 0x2124, 0x53b4)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000080)="b10005016000009005001b0007000000332a13fecea10500fef96ecfc73fd3357a4e7d97b90000ec436b36acf00b7804be38854991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257f0c942ad3ea7acb5d602000d7d026ba8af630037282102000000720fd38bfbb770c1f5a872c88100082ec5890400000000000000361b1257aea8c500002002fbff0c2300008abfba0900000008e371a3f8343712051eeab71d89e000040700003ff0d3fa1ef9", 0xb1, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x65, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000040)=[{0x3}, {0x22}, {}, {0x23}, {}], 0x5})
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1a}, 0x4, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000180))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
syz_emit_ethernet(0x22, &(0x7f0000000240)={@remote, @random="c36c1baf33bb", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @loopback}}}}})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r0 = syz_open_pts()
r1 = kqueue()
r2 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
kevent(r1, &(0x7f0000000380)=[{{r0}, 0xfffffffffffffff8, 0x2f73001db16cf8b9}, {{}, 0xfffffffffffffff9, 0x5, 0x0, 0x7fffffff}, {{}, 0xfffffffffffffffb}], 0x401, &(0x7f0000000400)=[{{r2}, 0xfffffffffffffffe}, {{}, 0xfffffffffffffffd}], 0x8000, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8020690e, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000040)=0x7)
writev(r0, &(0x7f00000013c0)=[{&(0x7f0000000200)="f3418e61cc8513044f7ebf66cca2df9686e480cd2af2255bde7a5deb244cab8dad9be2b9538dbb322c0a", 0x2a}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xc)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r1}, 0xc)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c000000008c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x71, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
dup(r2)
ioctl$TIOCSBRK(r1, 0x2000747b)
select(0x40, &(0x7f0000000040), 0x0, &(0x7f00000000c0)={0x1ff}, 0x0)
readv(r1, &(0x7f0000000480)=[{&(0x7f0000000140)=""/135, 0x87}], 0x1)
writev(r2, &(0x7f0000000100)=[{&(0x7f0000000080)="b4", 0x1}], 0x1)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x97ac, 0x0, 0xffffff80, 0xffffdf7f, "08049cfcb5602880007c686ea7ffef1100fe00"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be6c0045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dd31c0dc31b3c0e35ca7", 0xf2)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x1}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x8, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @rand_addr, {[@lsrr={0x83, 0x3}]}}, @icmp=@mask_reply={0x9}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
close(r0)
mknod(&(0x7f00000001c0)='./bus\x00', 0x2000, 0x4086334)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)=ANY=[@ANYRES32=r0, @ANYRES32], 0x30}, 0x0)
poll(&(0x7f0000000100)=[{}], 0xe6, 0x0)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getpeername(r0, 0x0, &(0x7f00000000c0)=0x1e)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x5, 0xfffffffffffffffe]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8020699d, &(0x7f0000000300))
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "f540b2ea0000000096b8911f121b3bd89a53e1bf", 0x0, 0x80})
setrlimit(0x8, &(0x7f0000000100)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup2(r0, r1)
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000040)=0x3)
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
poll(&(0x7f0000000200)=[{r0, 0x40}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443f, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, 0x0, 0x0)
syz_emit_ethernet(0x4e, &(0x7f0000000000)={@local, @random, [], {@ipv6={0x86dd, {0x0, 0x6, "50a8b5", 0x18, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@routing={0x3a, 0x2, 0x0, 0x0, 0x0, [@mcast1]}]}}}}})
ioctl$WSDISPLAYIO_WSMOUSED(0xffffffffffffffff, 0x80185758, &(0x7f0000000000)={0x0, 0x0, {0x1}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x1e, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x2d}, {0x40}, {0xffe}]})
syz_emit_ethernet(0x4a, &(0x7f0000000100)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a4f7ff", 0x14, 0x0, 0x0, @loopback={0x0, 0x2}, @loopback, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @mcast1}}}}}})
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000009000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804bedd45363a48fbfc781e4991f7c8df94da000086e1aa5b0100000001001f132e27acb5d602000d7d026ba8af63ffff072918", 0x62, 0x0, 0x0, 0xfffffffffffffe8c)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000502", 0x5f, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x15}, 0x4, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x42, &(0x7f0000000040)={@broadcast, @local, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @multicast1, {[@timestamp={0x44, 0xc, 0x6, 0x3, 0x0, [{[@multicast2]}]}]}}, @tcp={{0x1, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup(r1)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f00000000c0)={0x6662, 0x0, 0xff, 0xfdb, "2ff77f2b32f95c143d00"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
ioctl$TIOCSTOP(r0, 0x2000746f)
writev(r0, &(0x7f0000000580)=[{&(0x7f0000000080)="c7", 0x1}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$LIOCSFD(r0, 0xc008441e, &(0x7f0000000080))
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
r0 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
r1 = shmat(r0, &(0x7f0000001000/0x2000)=nil, 0x0)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
shmdt(r1)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020699f, &(0x7f0000000300))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b5", 0x5b}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
setitimer(0x0, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x4, &(0x7f0000000080)=[{0xb1}, {0xc}, {0x8}, {0x6}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x2}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
syz_emit_ethernet(0x62, &(0x7f0000000480)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "91c235", 0x2c, 0x0, 0x0, @rand_addr="00000000000000001d00", @local={0xfe, 0x80, '\x00', 0x0}, {[@hopopts={0x0, 0x2, '\x00', [@enc_lim, @ra, @pad1, @generic={0x5, 0x4, "c6dd0c7f"}]}], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
kqueue()
socket$unix(0x1, 0x1, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x60}, {0x84}, {0x6}]})
dup2(r0, 0xffffffffffffffff)
readv(0xffffffffffffffff, 0x0, 0x0)
r1 = socket(0x18, 0x400000001002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="08180035"], 0x1)
sendmsg$unix(r1, &(0x7f0000000300)={&(0x7f0000000000)=@abs={0x0, 0x0, 0x1}, 0xc9, &(0x7f0000000280)=[{&(0x7f0000000040)="b1bd36220bffd2e5ef703e086f74414179f43984db96817c738881a2f2ea100cccdfb3a8a3b83f457625f278736770aff3c787c13dc322a04502daaabe72fa80cc06ccc1ec1061abdec3cfa8df998b", 0x4f}, {&(0x7f00000000c0)="5b4383c13420188f29a134138e2554789916cbd24010dce2a11c21227ea4163a2821f18e23118b3102b68dd3bec3bd5dee724fae4fbb1fe3c8c15de6230c74666fb79a74562145037b100cf2eb634af4f42787af0ed5544c5bfeb4af93feeda358b9f380445d3c4c6ae5ebe4b3191c5f3d0bfc4cdbe5c139ee0c1a275b59dbcbcf7de6a38166add197a40c90822d77915d0b9d2a50d79b0ab069a423c261be0f0c3917918e884600c8ea8f7ef36bd54110492d89b7d8ca94c375dffc87323db4a5da6fef333a9092167b063d33fb4f2fcb692007b1bc413c85aee1c13a5e5e88e38ba9c6cd95809bf16ceb", 0xeb}, {&(0x7f00000002c0)="554006d9e141c02c4970cc88328102640f1723e3136fbae0308327ab36d305e8ca4a510606f7c3b154fae7ab0003bc5055384a9ce345321639789bc8497d4e23", 0x40}, {&(0x7f0000000200)="bfaec75433267e3aeb88a97024f8bccfb9f9ff274110e780c3bc61f97a63897d4b56e3de60b45cfd584c4b2ce7f4568734498480967e04553c415c7283c40312bd16f51fa0f317075b200b9ee12b1ce67fea612a362c1ec4991e42b5647b9f90349a0f620721a7b17d9da48daabb972ab9cc18d620aaf94d3a153fb56ab3", 0x7e}], 0x4, &(0x7f00000004c0)=[@cred, @rights={0x0, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x0, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x0, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}], 0x10}, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
chmod(&(0x7f0000000300)='./file0/file0\x00', 0x51)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000240)=0xc)
lchown(&(0x7f00000001c0)='./file0/file0\x00', 0x0, r3)
r4 = getuid()
seteuid(r4)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0xd4)
rename(&(0x7f00000004c0)='./file0/file1\x00', &(0x7f0000000500)='./file0/file0\x00')
sysctl$net_inet_udp(&(0x7f00000000c0)={0x4, 0x2, 0x11, 0x2}, 0x4, 0x0, 0x0, &(0x7f0000000200)="f82aa793d05ff9ae6ad7a5a9926dfc73e7b192894253974e01156ba359ff7e62940b49e23577c1641433da72564dd8867031f7d34a350eef53294b51bb6caed60fbd63e3e9a941a6abe40250527a31441a58712495e2def3b559c826ba23b2c5d28f352acaba14170eca0134b2f2443b02f2fe3256b2c023114d30253b3ff6dd27f329c25ac54dcd319185f89b19d163ff050ae14779bdc14f5d3c245ca117ae2a2c3a3afb62bbe67bba7edbbe7fd16db7e060fc23773cd596d562ac780b08c9b12f8a9d0961d64527a7b1b7b51a4a745b8204411f13061e917472f568d060d97817b8305940c0e648914f0263185cd1b8242ae0a55b6a0af8bc7b9b67e7f11859d022967266359842", 0xfffffffffffffffd)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x81}, {0xc}, {0x6, 0x0, 0x0, 0xfd}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt$sock_linger(r0, 0x11, 0x3, &(0x7f0000000000), 0x8)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
getrusage(0xffffffffffffffff, &(0x7f0000000140))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x5}, {0x4}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000180)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "17f37a", 0x8, 0x0, 0x0, @rand_addr="fe83c158edabae23ebfe9514b180b761", @ipv4={'\x00', '\xff\xff', @broadcast}, {[], @icmpv6=@echo_reply}}}}})
open(&(0x7f0000000080)='./file0\x00', 0x80000000000206, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f0000000480)="c2", 0x1}], 0x1, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
execve(0x0, &(0x7f0000000380)=[&(0x7f00000000c0)='^$+[&\x00', &(0x7f0000000100)='\x00'], 0x0)
dup2(r0, r1)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0x13}, 0x2, &(0x7f0000000080)="508204671fc7dc7c", &(0x7f00000000c0)=0x8, &(0x7f0000000100), 0x0)
r0 = semget(0x0, 0x0, 0x0)
semop(r0, &(0x7f00000000c0)=[{}, {}, {}, {}, {}, {}], 0x2aaaaaaaaaaaab52)
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x2000, 0x1e6e)
r0 = open(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
open(0x0, 0x0, 0x0)
ioctl$TIOCSPGRP(r0, 0x40047477, &(0x7f00000007c0))
r0 = kqueue()
close(r0)
fchdir(r0)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
chdir(&(0x7f0000739ffe)='..')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x200, 0x0)
ioctl$BIOCGETIF(r0, 0x4020426b, &(0x7f0000000200)={""/16, @ifru_dstaddr=@in})
dup2(0xffffffffffffffff, 0xffffffffffffff9c)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
bind(r1, &(0x7f0000000240)=ANY=[@ANYBLOB="01002e2f66696c653100e1222066464e4d8afbcced8f179558add1547c7fb9948ef40e2876d96ea41e69e648ff8ae9b6dc75ba8aee8739ec6aeaf5d090a3891f22dc5f7efd59db03677de28df379c4cac80c850796c601cccc7914afcb49892b1daf6237fa4b4ed37f8aff0b3473b011e2597b40a0d56b1a0694d264046773330e11b43ca4fa0e274c0493138e5478c47d1a6d5447532e3d89d34a8e0f2bdd2d6b1687bef9dd4a4922b957ae438151222e3616bbd880af0de5fdf8e00431fb9b7313fcd48dbe074b530bc4d82ea247f59068"], 0xa)
mkdir(&(0x7f0000000040)='./file0\x00', 0x12b)
msync(&(0x7f0000468000/0x400000)=nil, 0x400000, 0x5)
pipe(&(0x7f0000000080)={<r2=>0xffffffffffffffff})
link(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000100)='./file1\x00')
mmap(&(0x7f000062f000/0x4000)=nil, 0x4000, 0x4, 0x1011, r2, 0x1000)
r0 = syz_open_pts()
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000005c0)=[{&(0x7f0000000280)='r', 0x1}], 0x1)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000140)=0x8d1b)
execve(0x0, 0x0, 0x0)
sysctl$net_inet_carp(&(0x7f0000000040)={0x4, 0x2, 0x70, 0x1}, 0x4, 0x0, 0x0, &(0x7f0000000140)="543a27bc", 0x4)
accept$inet(0xffffffffffffffff, &(0x7f00000003c0), 0x0)
syz_emit_ethernet(0x72, &(0x7f0000000040)={@local, @random="b03f0040bd38", [], {@ipv6={0x86dd, {0x0, 0x6, "7fe166", 0x3c, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@dstopts={0x0, 0x3, '\x00', [@padn={0x1, 0x7, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, @enc_lim, @ra, @padn={0x1, 0x3, [0x0, 0x0, 0x0]}, @ra]}], @tcp={{0x0, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = socket(0x2, 0x2, 0x0)
sendmsg(r0, &(0x7f0000000040)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3, './file0/file0\x00'}, 0x10, 0x0, 0x0, 0x0}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
sysctl$kern(&(0x7f0000000000)={0x1, 0x2f}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000380)})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000))
write(0xffffffffffffffff, &(0x7f0000000380)="2886cc294fd5c8aefc05cfc10eb5a56acad88bf2f56f70d9b62e9538075fc7e02d13ab8f55ecea7c3abccf332a103e68f5b0bc8cf16c95af382f8a37bea429f5d488cbc66412e9812f9b09cbbe8b01eb6ffa82d27645fa6696cb338a256799d5884996d60bae7e3d4770c3f33dd5d354202dfe6377d88769cc2bece5be5e356fc269d189433def1a45", 0x89)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x1, 0x408})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000140)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
open(&(0x7f0000000080)='./file0\x00', 0x10000, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000100)={0x4, 0x18, 0x29, 0x6}, 0x4, &(0x7f0000003380)="3e395f5c2a47202d4821e8674b826306585e74f97b15ce6f72322a3a24e5ba11d0db1da4e5465a3c7b42d9c58324d5f66bba59ffe745e8ee35e8e88caf2b131827108ece633daa701f1d4c3ece6a5e2b0c17ae05bdfb43d20593dc41bac528bd460f09a5f7563f31d1e56ed5839f7d766378c67a37cfcd9a67e61a90bce05b81b04e514cffcfba6d38536760c2f330be180e668f1de6b687f03d906296ea01f926a72f984bc9bd2570926e29c6384f89b7338eac67dae4b876dc8ebc544f64683066e8b4be1f879fbb3adcf37dec5b601c86f8ea8f9f5e4e4fc083340745f38c6e6bbfc4a571619d7db5b08d994cd9522991380b3fe7a364b373137620d48b59e84e581eed9b490adbe3c38dadd729a2bef8fad2b34e54ed2661825e2ef6aa85a2a9a5a88954715fa4483b6dc55b35eacf298d345ed0529fc5a14f34e50b20b2ab458807a0fc05e5a6862ed4948f9f5e0a5f2eff191ba231ff543977e1029c3d58b1c1457dcd5769a35e6b00b23101f31b38db6d4eaada906013cac89075a7ff3bfd0f08cda4b6b1633dca703a3704d32991f31379fbcc786a5ee4af325026e27534f4024c84abe059e876d72bca656f7b864483dc3554df4545746e0687e2306246ef0b7d76ace030d4220f3fa06633cc5b4f5d706eb35089303ec6148bbdafd7e68beae4debc80e79237413d35de584dec2cfd9dd7c08efb499fd14a5a364583988cd5e128c8cc05d83fbaf2364ad2cc320f2df5d7d4584ef27770d4e9fc7749554d8498ed8d5479005914ac5d22c1649b31724b071aa4852db850711a0f1117ac095e6eeacd57ea75ba1794a67843b0521945ebe242920e5a2f5d13a290dd9769bb59473a49eb53e8bb84f94509f49bcdab3d5a4b3cc99bcd9890e56d42d2cf39dcc0b3a9e655d23ad94eed656c13ba95fdf74db693eb9a4cc97a9b4e502ce4b7605751236beb95b69db319917b32fa2339b1fd9056b0733629319d5487ac7f842b3bda1c6625c5b41af70e84fe45a829c126da4b1113c64a65dff09a2216821da84e2c2adbeca3f09001b9444ad6bd3e76a8d19b65acbd77f5ce2b3f2f05007d8578c65afc30206972650a99e3ee781145f4b0c5e03fa894959409579f7b38b4e7cd4d7c7790922be6f45d2f4918fd4240dd23f73bdd15fd3461599fe9902c235fd7e3aca4975936ba519d4a6c38a24fb74945124e81cafeecdaeb20ff6191097106e134ed1d75d1e8dd353c444fd5923c6fa79b27704dd546ea7aa4caab9e41750191ccabe112d73b5deb1b5812fb5c6d3b910648acf7226ebe5a0d181759439d9ef3539b3a71c8205e093aac5b8b13b77972f3546b3fcf2e7fb5b5ced4e4bb68b77e54f9fa0edf36dc629f48a3e55af27ae5b5acc7398f7daa6db6833a43b8318fcb91fc755091ba9f17f56d4ba87531214af8a3454b879fc251e027bf0bb240c22c7c7bbf95344f219891afaa8b440161ae1b3a427595d879ededc94635681dc36b5ca61113cba4d4346f8b6bd68811573c01cc00c0d0f1fa801bde21fe892d24bbe07722704c3e729738f8377d5921fdaea43ba0e0f90704abd390f0d12f2598720ddf54590ab8de1136f2cc93a13766deb58c3b10ec42bdc731b38ea42317d55c34f0a070a6eaf2479d662f47cfb4c03dab70300eca28ad78fb2c6fbe05badc255f5e3321b1390265d350c96a7288888135e5a994a12633009a1844c5476cfeb3bcdfa753fb5328c4bfaa6aa18ff8c303bb1e94c7a6701c4aa57117dcc21ba624cc0b7f0212de6a10d9c93ed041af7e455f7c96c1190b55ed6721520308cc8245b5e439fce83bb954cd1221e245633e52832deff5be66ed761f3d2f2fb77fdc7d154000f7908ce6fdec33726ff68ff80f7c706c5b79aa956f56860d887ed73e8218ed70f150d1ab3fcaf29c607431ffc0218d3599651a6ab2c63df9b402d2e858abd55c0a535e121e8ec209011724d65e524c88e9ef9ec136b0b12a807a6e85e355461f67f6e1a0ed114ab1d1e5003acd9711059da5084491302e9353a7a6976de243ca67c81cc5f8e534cfbf754f00ee74e028310e8c25226434e2d2db7969bd6e535b7b7d7f5c7044e9edd602b6139adb2c2d7d99e5cecd8e6b48ab16c48a8fae9c306b03045c37a2b76c7349fdadac6d7802a271d1387829f122816ddfa2d1e199ee5fe27815909ac3ac65bb813f76992024082b67f06f22a1f7dc4afce2924b7d3bce2c9bc98cbec2228a213265912add7a90743bfa9c8126762aa0855b890bf1c0fe1618c28adca22e82b9a02137255e38f89013238b6689fd4fa0375e7c3be1226ae986b2fc6271c93db663800393ee4bf1e2cc4cff68d5f7711eda8f32046460538ec3d331c11b87b392d2ab86b05506ef59f061bccc32493aff09bc1b45b98151dc189f7c8a67abed74a4553ca98223c36fcdc79988d506fee40ec10ecec93933eaec9d5b1531c7830a42571f4aaa6348be3b468d24d38d11edd812488a7b9cd4678b2c6b33bdb2185fd7013600dfaafa94b17d58661276722e25220c0ba09ab3db73360bc9f25b950af560d8b78b2e90861678afa8de88f4d1b935fb5541741f5c533baf198c4286d50dff9eb0e045e23f48e56efdd59ee4a5c756319ca9df8d6edf4cd365bff7b8da1fcbba05a2b2da096c0629ab2fa5364e3be5d71b5e5b20334a67f09e876b43b3de89e2a92bd7e1d1d6fdd23a91a081bd93e0964493e5db7ec8c93832da54ed0116e90ba371bfd9fe23322ab7cfa4068ad154c374204657c104302cae6893be6cfec9dea87a53e76c47cb65ba831cadbf5dfc0111058c7722816b565b91d61d22ba6c145b56b229d2ac234d0c3fb242f28f25ac656911592a5f2857d8d5a5721228a5a95a8f1942dcac1d5d9ac71059bff744e379cb46721759a9628d32d1ebc12f1497ec33d2a83f01969b3fabc5370f73b3cf2b1307ab0fe286bd9896b48aaae554c99e82bf0d052a20b91a6238594b609dad3ae14c49e79b7f87266a48e8e25274c4a99f4b505ea75776c5884164977cf1ac53f40295521efca0ec589460b88eb17ff73f66868824b98a919664b63f9571d55fe5f9037c9a62bb955d1bc2b322240a73bf23591778644a5fa905e5735c9ff5fd636fd6f2c0ae0fcce6515da7ab087921ac18d76ae49096b5acb733f5ca7b05c0cae53c6e1f442dd684c1d31040618fef5cf76e436f80d42b5203f5f8b2ed7178bbaa41eda5dde46994f344149d15d5026c586a4a8b1bf48a2110a47ccf43ea63dce1f65a7b28a7c545b485fe1533ed669d8fb540def231379331b5fa823de3602667bc95dc5cabcd072fd0796693fd798f5cc5e65570aa9026a7a262bc2ede8cc457279931965477cd7b97314d969d8ce4dce0fa64f3e39a9cdc770e045f7f65445227c495b22169211d4b5af855a938818489abcbcea47f754c661aed8907c16c5fc3d9b3d4d216d41d3ac43f3322b5a5c8394c610960903442c18a0a54c1b4524c9239634cca0a5661df043f16a598b03e7942f9ea5a3f721ca0998c8223d87ccd4b22d1d42f1e21ef6b127fac253097abb6136bf17ebf77057e66f1d6a523d53875370ca8b3ae7040040be2bd90e33895a86420d095b40d9b7d1568608af649c41a6f7ab8c95d1ddf7ca246adeb65b6c34d3e25e1e32be2f274e2fb1645f3f314b136ac354ec20eb1547067979839124f3969b82969eb7f49215f9f13f9668ae986240fac48803599baca0766886ea50733d3b68f301b87273a8f76311068d357f33aa3c680156b889da6239b4bb337ed91285b31f2cd2e582cbf0dc781bc3f2d936b3288673a34fff74c6ffeb18a817964edfa6dee354f2e689a61ed6e0302eeafd4cbe930cad91c2414ffcefe60ffa3ab3990d912524bbbf95cf6ec6a999b4591983e99d7b3d3d2d52e4b0b7a633f02556fdb5b66c582d72f0184540c0a2a9b2851762686dbdb3ab8a18bfc4965462e4fee9c57075db455b460abd7bbf4520e876913309c7b056a42291de282360e212efeff116d751551d5471e4b6eb64af3326d8253b867ecb503b4438a06a1ea625720b78061a0c503aa065dd80f54e3533cdf469d93fedec35dbf75741566bff533123f03366a91da15890df1c965509c61affc5e5576cf0c254fc36397adf78897d22cd5fe1447218220e406fc721af4826e8407fefafcffd293520a4110190414e9dcdd198de2d661b47a4d8a75e1a91396844fe4bcb0e75fb631fe7a53e0e2e983cca7502b29737e3342b3276741ecb24233709615f2b19f24eb1e24f15d66e3194cff3aedc3b852cde6c202dc0943ae723be2bc9b6e35a70ab2d363e78a5ad6b6ba1d9cc4c3f64db09cb1d867b33a66625ebf935af37750706b33cc5080536e6f0f98a604ceac41bbfe045a4300d32f2f8f65806454f4437c066201069d5e0112306d6b1cf58ae9aedbda5f9964c86b2fd2e1ca0f8cc4edbb47e98cb85cafdb51a0995cd9c520ff6b5f2ad964aeca0960534a0d7d5c37f95780289edb8790e08a376944ef206608578ac2a9bf4408", &(0x7f0000000000)=0xc88, &(0x7f00000010c0), 0x0)
r0 = msgget$private(0x0, 0x2)
msgsnd(r0, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2c9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r0, &(0x7f0000000840)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da6461341bbefbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6ac1571170e268ce1d0b4bbd8bf45c5fd340a61305979b0bf685e45f57392649d8248976549ce08056e03c959080cbf5e012d6635b3b58174bd552e9c513f2acc71bb2c9788a895fc07478c415c6aa3db3cd6b47f2e874c2c9d63886179802e5606fb276950cca74cf527bf968ceba0e8125af4bd5000000000000000000000000000000c6bd6dd52135696ea9f082ad0938b93df3eca8aad08910b7e8ee4403738cb1dbb0c104f09ad91582087e0eef0e43c90e92357a3ef5407833d2a6ec79c4594886691ad2cac8b2e1796506fade88f319a97d9a40c749f3a53994efba60bcc277b0a17c6e5bee5a8698f73b55972dc1c52a0b69bbbf91228a81ebf27c3732ced1949ff053da397baae03e894a58defd824bac1ac4ea092b2d476d374f122423d47361c20a5575804aeaaf479fe73daebf5309cb45fa4eb8bfe4165315a5f49b632e69ac567667569d953be148"], 0x6d, 0x0)
r1 = msgget$private(0x0, 0x100)
msgrcv(r1, &(0x7f0000000040)=ANY=[@ANYBLOB='\x00'/60], 0x3c, 0x0, 0x1800)
msgrcv(r0, &(0x7f0000000580)={0x0, ""/18}, 0x1a, 0x1, 0x3000)
msgsnd(r0, &(0x7f00000007c0)=ANY=[@ANYBLOB="0300000000040000db4436958b193c67b6ce0c093bb0bbf3b3245230033f58052ebd418aa6e7160101000000000000259ca57335615dd09efc3ddf520906bf778f89cf9aa9da8fbd888acf1d82015453ebaf5328755e3b05d90ba5ba0c00faff96c620f0866300000000"], 0xb, 0x800)
r2 = msgget$private(0x0, 0x46)
msgrcv(r2, &(0x7f0000000a00)=ANY=[], 0x8e, 0x1, 0x1000)
msgctl$IPC_RMID(r2, 0xa)
clock_settime(0x100000000000000, &(0x7f0000000140))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000001c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x80, &(0x7f0000000200), 0x4)
r0 = syz_open_pts()
syz_open_pts()
r1 = dup2(r0, r0)
syz_open_pts()
lseek(r1, 0x0, 0xcc7480c364d3b074)
socket$inet6(0x18, 0x0, 0x29)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000080)={0x3, 0x9})
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x801)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./bus\x00', 0x10000, 0x8)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0xfe, 0x0, 0x2007, 0xffffffad, "919000"})
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000040)=0x9)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa1fabe934a1c1e400db556028db783e9f47ff98028e5eb8cbf5dc889b5ac6402a846918dc0e5097d2395a46fb114a16ea036db91fbb61a83ecd8f2c2ea7f4933fa25982129ace7b16a56cb12b1f238d8ba33c9856a0f0809394b8e5c242c2d9d5194e43f3bb1e6f81d5b8e7c3d5aa1e11f0d210cab776b5b7d1fdbcf1330114561137118a573d4e85cf86cc5488b8a844109050b475210e7df7a0d5f3f93f92cb55920c62fe88de5e006c16ccfb3bd743ce316a8baafe6acc43857db894d285d89375a807129d682358f0b93e83ae31f06d1d27b44c1d9476bdff4677c66fa24955a4273eb6bd918c5580730cfc1bf8520737cc0fb8814d963f3de72f8fdf9aba5a9feb1005c08d7b590f2fa75fa602967dd07b4603595e3b38efef0f26d4b2e943e1871b63479d4fe37c597e3fff39addea8b9ad4c7fe00d24d2adba9186a541bd69baa8d7062bd1f9b8fbac2f1fa00170c766592ed3e01912b24758ed0041f441bb7d3737ba080226c380ad7e766eba96399180b18f59bb475dfd1be417c50c0ac7934a4f55b9080ba4661680030f428eafa06dea84f1afcf60735f0cf7ce0939bda5433c36beddbe9b6e65e3a2f4216a530b24c41ca9745311beaacdfcd8c0c5d9d1d85f0cbb68c77ef1626a44bd209acd8f700a7adeecf94a5d30e3c8ae9c0134aa682b96275816b962bb390789b3253ec11c5485ecfd0b3387fb1cde62c22d6707093c53ac902e5cc0e4f363cc3dd810820064528765f21a8f928c85749fca1768571c685fbf3b99eb65eefdff1a232d95a3be6903584a45de52922972d345a12f0feafafd0fad3705c36ed4472b0637dab1df4f4c30e5fe2eaef09bc9c0ad59fb081a13d4bc6e72db22d12ba088f3d9bd0c96ef85fddeaf5eda74bea0f323ae15a0d1496293158d590f65fa01c87ac3e64facf894879737d20d0b1343b19b11c23a3c669b961b808c928399837145d2144634bcc95296a35db128bd780d6fad47c642339d7e2d3bd49455ec0dc893d6a3884e15c48c65837024637f9acf57ab57e6ad5900ae73c69ee6e88b0dbe2d51e81f8a39141ff8ecad5cfbccd3df4532a0c0a433e3ed6a22ff8236f42230f498f3b208a61fef15ac1455b6783b60b28017959dcd75df55ab591240be76e2bdf1cbb575b0df1cbf8210b2d4904755bf4a6c4b604ff558d9be52db6e8a7b790a11b1e89138913d4c946695a2088ea2cbf148e0716dcba54014a58e54f3314691289ffe61784e0cab5e8202f78414e36a7bf52153333f697a382d9ddaae6c7f9bdc5c611fb2568665c247ea950b3cbf32356bbd55d7c98d4093e4012cab15b378026af10e1c7fad4963d97993c48dc42dc30a4b013dbef0161651dc2797770e47002ef8569cefe5dc2a56b9ab1af917902620f3de081dcf31cb9af02772c1297ff819416b9f08cf0ee625abdeb795477cd29524554816d3bb675290ffefed3e33515665dc4da736fe29cccd7e0fb4e70ac57052bec30fe76d070e7f997499d62d42f0220f7cba6f29b3db3ddbee5b7816675af64b9d08f6d6213e49ba5e436c938067c1d04c4d018b60b96eb9d14ca9e1121aed3b3dc9c0990d7859f96f4667b24ef1ac32f8d2bdc0cfa73114c992fc5efa80bc67c0378aa596c370fff69f3811ff1eb38a578c8aaf598abfc74c81b120cbeb475c1dc103bd6b86b5bedd3742f70de64b0da208ae8c6f42d49df66cc30c740b8404e2d9ee202486aaa5334d819eeacfa03b873b9d9f74f0c69bc78b9abd5ca1ff42b82f0572353abcb3ade373db7676cdeb728fb40603d9a5046a245d3a413356a8063524509a79a9456128da1a9019cae11f9bac7718187b4d77fd5cec7a1798987accd471d3be22b98cd847bdad4ebf67824f746bde2b72fe3443f18185289e7b0f2a2d35c89ac4c8e8ad88c9ff86e43060bc00e2837e39158d3e899e34400bf4b33ab8248be2f062db014041d4c422945b2ddf6dae632fd235cff8e7cae38f7aa8d828681ae1e8f08aab4fed29aff66d46ddf60cb5499701464b5e837d4a7e0280af65480134f84a42c68bc2d2b37c7fed5b0d97240029be4d8002333fed58b8d84748c331fd96c7ce3dfdecac736cfc52ee844ca2267d2eba16b7fc930b23d47bf0b5d5cc12ca740747277719bdc0b1983e95c8ba1fcfc7ef6c15a94734cca4b400d048eb0505511a1d9d60a433417327b47f8729140c6dd326559d24db1a079f0fb1c3c8f53f12c586445d1a736d35885fa8ffc88c693cb2a3686edea02cc88451814005ce52e5c05ec2e99bc7943d83df39145b74797b5d30295b5f24dc2d4633776735147d2902cb71daf5991d599cea38e2f15861ff17c30c06b22683e5a3439be67c2dfca36b0bfd3bd8ecf30b341702db4457bcd74b02c2b74820fb93753db3184bff03f645b3dfdeea195400000000000070eec997eca79eddbfefba48adf41b107efa9804b4325125ec20518b9508186a63115f5acb9bf76c68da2d34dd941b0f132674cdf228b71a46967a60069f0e608cd917f5b968f2636019de739bfc014e6fdeadfe3263229092426ead638c3f8ac0a2fcbedd2a116b2937244c15d4a40dd3b940ba54c4406dc517a1433caf26b38f90079e6043a1fe1ab526658dd1de39853f161f267fdfe4ab8b9f5739588419abd2bd0a1d99041cf16f9a73a82fd48d04b9daa4384bb9742905fcc9f3ab9080b062f5275eb8ebf28e329725715c0ba01312edd85f81b954728e053518ae7911ee2c31e75c8d836377670336624ccb760820a584d3ccc13e2d505b507620379784149034ce52141ff633cf0508a7c7a9f2d2cacbd82e7c62ff544be7281aa27e40512f68fd9cfc35b466c829b3fe22f953539c0baab9c4a763d5210a2be94a64a7146dbf2c76a5029949172ac64fa14359d71cdd817c3aa6e0e0073981332102772e2c2d0165a30fe94b1ab5f526be3c21877139bcc9d685d7268a13d06177cb8b0273577250584c90999392f8127c387e03984fc5d75c5fbfcc4d64fa568bfe094c2f8334d0c614b050409f2782448a1ffdeddba77e3e375e64974ff22f58", 0xcf2}, {&(0x7f0000000140)="1ef646320e8298bfffa8b2a1bbfb4ebdb58a48f337ca9e47561be058fd4e0d4b4c76ea24861e96323ae4c7eac08c3708a683ab257cd184c9d35c4953613628ccbbeb6cfff60f99b159e6117f1919e7316d9f7cf6856c6da15f36bb7090f19fc8e0a171be2abf24c70c82a2bf06fbaf76f24b1c3b21ec10acf638740951afea8632359235a8b2cf43cb8511d89f53e0f616e7503c1205f08bfe5817eb8fe01f0d2823d95c7180946aa042f9dd36bbd52fedb9fac62b233beed1f686bb7ce3c7a8", 0xc0}], 0x3)
writev(r0, &(0x7f0000001700)=[{&(0x7f0000000440)="9a8fab2d8eb4a6ebdbd69775a443f51e35e2574e6291c1ff9732f9c18bceecbb4581154806307f3125f36c82a5d7ac0046ac9e2d55a4a97bb982134e50fd90d4038f94584eb617374dc40beaa963af0225b25d93dbf34d21cf8903414ca064fea5969aaa4c53fcb65b89b8e3f2ea6a3b9e3bcf5a56a8231d617050aec12c2a2ee4cc22957b4a80b7a24ec131fdb98d274f3d10d206f89430f4ada7045668f0e2d5f5565f0f346da98f886742c16d1203fb8c1d2c4abb2137148dbd8bc5dd1ec4365519b544cf39f6a5c4d600aebf18a18439c10aed2edc6c6acde29135aa468d6df763ad6753159c1c5a0c3ce3eea5dc07524ef1a0d7bfae4da0eb741194cd7694415557a7c82752fb76a497bc03f49a1d8efabd43135cd8a39bea327632760efed68b77c834bc70cac69fdd46d40314f6300969f9b552b0e313bb49a8a872c7b053b60d1f2df28c16a9981fca6155ed172c774d4647e996bd0bfbf384ddb84d005269ae1b9b583b5a", 0x169}], 0x1)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000240)="e3021d932704150e8dcadc3746e9ec1913fe510a15db410430a9145c5bc80bd0159a93b809c49f858152f439ba5d8141a9af01a1dca7e5e6b682fac7df93a5cfb2da0a794a378532dc42b5cec061f8b1b57de421b2914c197fda1578c2cf75ceef9bb2478865d1b2ff0135326d6c6967126e470b139a9d39cbba5f3002811ee288cc71d4afce2ca73b17cf2239f5da4447df12e67a7c017adcd271180e27f1b4df114b94be7cab08e50dd8a059d48ae6b887130967f791b9b5b57934a2a8cf076b403e20dc138e946f2e57d8ea74e79f337f", 0xd2}, {&(0x7f0000000080)="e89e00a77b9f491a19ffa382a103abf98bc714971db796497bd88138f30ee39ae93434fedde9696c463583a41394c67354f21a22", 0x34}], 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x7c}, {0x7}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
openat$speaker(0xffffffffffffff9c, &(0x7f0000000200), 0x8240, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000018c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0xbe4)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f00000018c0), 0x0, 0x0)
dup2(r1, r0)
munmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000)
minherit(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}, 0x0, 0xfffffffdffffffff, 0x0, 0x0, 0x6, 0x0, 0x0, 0x7fffffffffffffff})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
sysctl$net_inet_tcp(&(0x7f0000000400)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f00000006c0)="0118fff60f9efcffff59657f16e66e9f97069846a2fa74080000005188f322a33afd5604c4aa10930ed14b10abb7d8414191ac619377bfd683086fc78a1dbb0991fe8a3722e3138bc29c66d273252d8a49f0b62ab57ed767755d45d5ae117a6d1bb9e7734421cef62cac0800000000000000059989c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebb2481ea2de4000000aa1e20a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66c878f486f7e59259a05bb689915b90980246fa85c22ad90d4e3776895066d2bee08f70300000000000000bd0100fb00001c3f30e790fd157cd0f6ac73547476b2a766825175bdbda013a1dc24244ac0ffffff3f000000000000000000000000000000000000000009ebffffffffffffff7f3fcbadf25485d5ca4287ed75b0db89c123fce0cbfb668a58f19f470b6569b58284c0cd71f0e8d87e5503c72b7d1b3db57458000000008e1f2e111835a6b788d5ff5256df13b59edcc163f269e55e741205360c9d2e43575809838bebf4e71b1393f42a216e6f1b67f8aaedd53dc24ceb12d50d3fb41b2732e741d0ea739f0ceb63553689a46145a280533ec0d29de081568214f857ebd1f1e41bfb9a21624840a96d9619e0eb108d5bb60a27d465014bd7742b7e5f4a46cb83eea6b48aeb60db0242eb2d2abfec6dc0e3b0450200b24c238f90402598ad961f9f7502767ebb569f49ec0000000000009948c6", &(0x7f0000000280)=0x210, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x4001, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x5300)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f00000002c0)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffa000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000400000/0xc00000)=nil, &(0x7f0000a41000/0x4000)=nil}, {&(0x7f0000800000/0x3000)=nil, &(0x7f0000fb8000/0x4000)=nil}, {&(0x7f0000924000/0x2000)=nil, &(0x7f0000cca000/0x2000)=nil}, {&(0x7f0000845000/0x2000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000b76000/0x2000)=nil, &(0x7f0000d3e000/0x1000)=nil}, {&(0x7f000048c000/0x1000)=nil, &(0x7f000087d000/0x2000)=nil}, {&(0x7f0000630000/0x4000)=nil, &(0x7f0000afa000/0x4000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f000067f000/0x1000)=nil}, {&(0x7f0000606000/0x1000)=nil, &(0x7f0000617000/0x2000)=nil}, {&(0x7f00006ee000/0x3000)=nil, &(0x7f00007bc000/0x4000)=nil}], ['./bus\x00', './bus\x00', './bus\x00', './bus\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus\x00', './bus\x00', './bus\x00', './bus\x00']})
openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSKBDIO_SETMAP(0xffffffffffffff9c, 0x8010570e, &(0x7f0000000000)={0x7, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {}, {0x0, 0x0, 0x0, 0x0, 0x2020}]})
pledge(0x0, &(0x7f00000000c0)='vmm ')
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, 0x0)
r2 = dup2(r0, r1)
ioctl$TIOCSTAT(r2, 0x20007465, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x200000000, 0x7})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x6, &(0x7f0000000040), 0x0, 0x0, 0x55)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x10000435, 0x47, 0x81000005, 0x6acb9789, "0d4300002c43568878a0083639eaec00000000c4"})
writev(r0, &(0x7f0000000400)=[{&(0x7f00000000c0)="42052536f0aa4c43c5daf91b6ac439662ce0376021b71d36501e31c49a23eb4d2465a70a1599e774ebb999393aa15ae4733c0dcbfb092a279acba21f9bc2c18409748a89b67927a9ab9b353ddbe949d80a6bc177f0a9060a99cc9b6b71cb70d5090e14f26631682624a6808ce412bd8524439020978dcaeee51ef70aeaa22c1d41b67b6a061da382d1259e8c8a7dd917668acb96a33620", 0x97}], 0x1)
writev(r2, &(0x7f0000002440)=[{&(0x7f0000000440)="e6f5307cee2e0872864aad79033c3db945b3a3d734685d1bb23e82790ffbbdf23dbf7ffd8cb5fa6e87ee8a2a282ec814863206c39d4b2eab85c1d1b3f4c177f74530f2e6cdded870cda75b0d626cef27cf0b05fc13c4828284d17b4fb3dc71b8827233b71b443c8d6a1b1a19abdd5cc8c29e7d1d2f3a79aff5e98eadbeab4b892fe32bbddbf406e5ea9de6e352aca3c9b156e2f5e492884cae0c75d1f8551d0ceeda877dd174cd8a1efd5305e6a7043f8a177034e33f63183ea85bdd170e1fb8216d6eec2e2bca13951443fdfb64071bcf4412310fb2edd60335df440d005fb98c8857fcce04f39320f063ebfbe76530454c8f80f12224e8c83a7b8bc6b5ba14b60013032d881c56e68dc15f2a0f5177dd0ed734691a6e7a0010ba86a5ddf557c00382a3d9b174043bcb2c6d1d6b665c117ccfc0d8a04efccc912a181c47562a6ed6686c1b03cec16a2b8c5dccbb2879ca086722d43fe3bd4d169a9867daa079eadbbee2349588fbe2c5d891da9c5d814934eee8ea8ba9362cf93e5f63fb70f1ce224b9a9152f660a8b3102f36f9677d2ee719ecedb97e7758723bf80776620af46a2ae2499d972ff93f6687101ecf0479dcfc828e4a3e371fad0451da38ee39e9558da3334b6ab2e957629839c9a4d7163e6a0dc648084f70f0e951b5cac19ea8d3b0a71c572b6a88c49a1ac0ba9dfe58c039f916de9b74ce34386aa7f0a1858994b3a57e044aa8f4cbdb7ad7d3d541634d72183f2ef542bb45171ed3f9d03f422a68b29cac1336290725d41d29b245973a6c5884720acc44763e64a0ecac440fbc0bfb91243f92a46c86b82aa63a01a132af6cf130851a5fd8cba120425d0f6efb855550ddb26a08fa909dd4a493e62d8db3f24b91b13a39643fe78c978e9a40c9317b9de5fa3125b724678d2c10f623d4e6e9e5bffd7b227a6704f1adf1acad20cde6cbc52a4b1fb47f9d64f69d3215fa673ea7cb1f1b01748f196381d75276ad9318c7cad7a95239c6af2e3600b0d86672bddeadfa609c780420448452d4ba41fb4502cd2ef2ec56d43bfe599372f58ac8943aa5a4d976fd959370ddc78cd9e03d0a6de469b97222391f11ab302b9cf9c997e5e11c895050a8f288f78cf6aed23aa04409418a57ff421282db14a084545b78658983166c094c33d47ce23fceee373a5d77052bbca7623a4c061df1909f3dfc1db60c07cd1ff74c6ee5bbf001b435dcf50e876ca82038f5b172b93b1d452fee70bf57b37ac281367bc9fc54eaaf318da4bf96ec0566d25c53f2f3a0170f89091bebe2c6c34fcf40aee4577fcc8b4101edb48168a6aed833c01a26bbb51443a226af837762b94ce7770570870ed3dc9bd32fc702e1d90569b5c002218665957da3710004a291992aab9e27ced32401040fd40ea314ada83b1aab0281bbd42de9ff80448764274fdbb2616387dd1f6c1132d2d59d19dc84a6b23fe24068d563cb54ab1b896776731357ffe519802a9dfce2509406e0b2e9e2bc2dcfa23518e000e94c59b27b4151f8dc437abc7334fc78d091f40cdb07240cee727b3bff07e84d19f9ecd35ff512092aee8ba6864345bb00685e01d3a89acf2043261e3e74cc740aea5142b1bf3f0bee82b2fb6586f8cb9b84f581bba0979e5f2d0f938e5608b6ffae8cda6344cb73490036035d220b1b2d5b7e86334908fae7e8e6f8b17bf8333272cee0417250f8751d2a95b63a6a9380d9198b72f356208b0beb9d15c1c6282d8469016621c19200445b43eab8e033639da226bbc6f82756bc954ba3b90ee6c40d4f301e7bb29ae90b58b0a289bbf9af29cc643be4c4b3d250947882445e14dd52eed612a8f6e828899159edfbed2a952c546ec32d26a24a58b0bd80f3e87baadcb0317ee5d22d660e018ae3305ee2ce173e4509507d4f526fb66f463cc9371e746a6a09719a94eab5ff551b3c12553283b35ef5981423b55b1928a04fda2a9c1df1ae527b9b22c7ac3ad5f17915107e4c75a841f4d2c1e703223650c1fe5d551f9b628b07d46c1b416cdd81c590662d04d94981c67e187a67f1c7a39a1f29d13a634ba62b9576dfa2d9e7ace658ee41b9de77017967016a2f5947dbe628db52d78c944ef329d1ea875fd64c464e6ae7c3716f67397d1484f4e4735374c1fc1f456465758a06a60bae3b61d7e5bbfb9a4575f6dff6ae72b410c60dc5e52be1a832a02e1d87cb3bb842d7a24c5fe03143a554cfb01811aba92baa18be8379919b9630f7ff5c57d240712784e181f8f74fb97fc4de2720ecd96890bd5e111a5d62129ea36d582c2fb1b5f7938fb34dfc09f31283478e174cd4ccf5672a7e0767014e0c1ad569ac6c239a749a533e4cbc7a3124d2613c5d574f974e6fedd17afe5dd8fef1e992edae0561bf966b4c3aceee492b7669af13c2de977d6be56913aa5f19d8393e94d0a906060e780ae85520912f3c36f440fab6ba06c1185c785b63d892fc9037418556831d2abfced3b2c46048594734e59289fff91bee2c55fc4e19739dd5b333acc714bbf841965e90ef589dcc98ae7e85ee8ff1a6ecddcf18ee6d6926b7682fee2490dea57d482f79acbac1bd132274a85746a4dfec6412870db6ada6bbf071aa12ef056e95d09fb6dd2f3ebcebbdf883ae8ed6bdca3def68bc059fea9d3f90c109f732183247190ac9872e76da87320cfe7f452e79d7edc72b4ef7c9252c3d773fd56525e36318a99d9356bdd047fb0bf6c4d357f58e3bb9f6992a62a3b73369c466512132a04cb348169b1e074166e23f1ebbd106d23584584359b434e70444fe77ddec98a8f8bf462d1278cdd152b88504202eaec7e512f23e82de8fb28f63c262f5dbb2917189daaf4d3b3e8509026c59a5537a81cc3107e3fbabe44e0852adcb6cd79cb4bdbb2a52f3bd1f9b324c262e06839bdcb587612abbe972c344f4659068d952d336bd59a84ce12830d0315953e9268063710bcc8347550b064c4593dfa67a2c33b37d6ed0281d0ef46e852f07d478c10a929cd62394a33276c49f8f5cca3894aff9eb03017a95c994ee02761abe40dd1873fbb3fb285d9462536ec54157e77bd8b71f0bd0a3c77a3d3ee9206a807de466dda94434c34dbd2c9fcd4c67eb583977863309b019ef41cad720432cf60f7f44baa9165ab53f8b5f6d9694f5164e60b91f318d4b55ddfcc3ff5018cb6d451717b05fc1953a2d4d377dbedb2079ba82ec13a18d2462553fcb10e6b359b164c9840b6fe6b2ad3e25d072b808ce193cbfc7bb9b20510e71159462cb304548d8bd06c8671b442a2cfbb36cdb522c3c7e8d8432d5189167f2ddb3befaaf6b528035cb2f9996eca4dd1869b526b4767ea9f45415356b15dd8e8af198d5458cfa95a682300196e26efdd142a03599ebff150e1f9a81f474ef2c51b0fbb22e8002b61663c8e7586c2732fef982129355bf3b880d911fb574b93b80080aaab46921444b8e60c5d6b9765eb7ebe850d926d8154d841dbad883f0af0ab5b04e5c2224b69629df4238a99ab62ed28ce11875452956994e833145d190c6bd6c6c15d9248f264e814b8085157a0a3a3a3e257ef151a92fc04df317520b7549427383af786e49bda60a3bb886050c7334d44d047ae3051538538682d0b4808cb3b1d7c39f1f0ef44b14de7a06435919d861430e31e080b5c60e952dbcf8fe4e6bf1e0735bea45739724139b70f6a46d16805da4470512a6550a48e327e262226b22805acaf82ac1861d1966c0c1dc924346a0b2649749643d5a538a9f8e99038ec763a06e9bf2c4a7fcacb05d62ef96a560439759558a4c212190a54d4f62ce2adc667c7c409ff9195ea2fccac541cc4e9d895be44d77ff0bcf96fed6855225701757ad9f991fe020f4dd6c262226cf543faa265ab8668f14e244b33e644a1dd402fbd7b52341c994017f59a366f2192c31ad6dc19f68de6d8c1b234c8fb848235ba9a6fcd6b886d80581b7650f3814178edb6e07b1e5cff1b0ba54a7c7d0e49478014314fa27c0bf4eae3cc15c14ed2247a6ed1200d82e26d95f345802075869f12cb05448f7301587a3d05b0a2f7c0c4711592eddd75915244876f0afc1b454c9da0e0911891d163bbcf5224cb3d5da74f0999689c0e65ca66f27d8d6563f5e748be88a219e11cb589e7eb69b4105dee0f49f1efc1f249abf8d13a23b15ae9154cc95188f21bae5b70e348266b918f4de53e4267094b52e85737f8b54b7daa1942a84f08bd0a700a2b204fba7c7f35b0a0256c3adb9ba621568cd9878d3228183c0addd701c2e776d4f6be53d5dcd2f6858bb133d30d3e84a763894236efcc4e09aceb10d2dc9a443469282037aa098fd8281bb4a5ad540b188726670c4e86273b000249ddbc5112959550bd5995cc6aded476d6a937643f09380cc881a80593821ca7d394f5a79a65e75ae623d42968f6f1e02fd78b87e089ff6dbd05472f090f689c7779b7e6350dccf7bfd23f689a1ce55f10f26c2709dbe8daaa2d86786b23890b8a47e0cfa3a21756b31ef2fa0a183bdc90bd7652061865b77b5e9adf6be544f8a636fb648ae5bfd0a46a82c2feef9139b2a3576c42d9b3517e488243b75391edd50321bd248eb344168e7fa2141468f1b3aef0d02542c519294aa55babeaa71f2b57260b60ec5c62f880e7883c41a190368bc3d5007129dbb480b87aa95e4d4fcb2364a318b4741fed6efbd9a4e3c7f1c290c39983a421c17d429ebb8f0aa211bdb2a7bc91e1dae958d3f19280d9a0bb98b2df0a855dd98b956f36e5fc284a26aaf342878c34a37e6bea246b106a5e26ae618bde795caaa0467aa619e40356bf4ba8351bd00c0040513e46b00cbfefe3bad468d2e1ea483974ce1b86e8be5e24f19cfe6101fd980eabe90fbae2d9f4b9191d3a4e6596e7977728a5920cd52d806c13bececc431e165a631f6f50818822d62bd84da91244cadcbfbfdf509fc032425484cb28123be82cab963992c0ecfa89d368fc24170c40a97a7456fac2ad0b0ba4bbb66a0d2e3edd4998936af5ec86d11d90c204322670144369fcd4f5a33a92422a2f55e3c940374610066fe7a0b44916765cfa50bd296b5b19151c5d22c0aecd00cdb4a3e3dc3dec885f6ba476dcfd5958c9032ebb62668afc09ce8bb777bd5b9a45ef8c1c3d5162323d7b77d97e09b831c72effbdf5a55bb67274cd557ef98afbc002d274cae7f07435e20c1f1b54e90c40245722775c9fc47deb1cc239b177358b68874af5931a8a8655bd2524ce38f13d7a49fa609043fea33cff81d84e0eb4c4a45cf1ddcb5b521e990eb54d57e79baf58cba0db1b9182186ec92e0f9c9e021d4b761a049d1c1849ec45fee134be7b9e9710ecd605b324015d1bcc67426aa30cae3b6dbf6fac648e7f36af3db881f6c1cedfb4998d68290488afee92cddc722ba7c412caeaa70668ec763f65dde9ae6d0909453798c999c759f1d575b77ac3aadbbd94c3a7db0d2d39754ee66fafad260089cd1d26e338344a12c392241364b9b56f81b3f35e13385b701bee038130aac70957d0be19eceb9bc8b7a4a17e5b5a8ba30f9b64a1256b563ccf180fbd147f642493f21d86bbf340841ddcde0ca5ae320f407b4b4e3245579bc85aec9081cae94202d054ca0076aeafe3d02810b08d621db8664ab4bf1f1d9b263049d9dd2e114f0d485871653611ffce00f59cf36bab591c021962519ed85eace8d40560f3f793af8ec2b892f1083a6ae07e231f7d8c3f1f88cf157e06eb51c4d5847ca908a395d2e4e8054e71639cb70b517740c314ecf61f0cacc7799600c", 0xffc}, {&(0x7f0000000180)="c8eff8a9a7666f8acb881ba62b4d67f34f171e32ef33c8836fc5f592066e7b1e7fe4e4e2c31f7ffd6705a9b2b5607333eb89a56cf76a32a26a994afc4d568fbdbdf95f76d5ae82e0eaabe302da0968e90599f26ce105cd33c7d00bb17cb45ab38cc6af67db631b8890d7dd5107ad9234800d1ee95d627a7b7510b2626c5a8ccfd71b7064757f0c10f1385a686e66147057f644296f77bfe881ded8859d7ef7d20035db2013723ec630a1fbb3599eff09619e548cca74732210b22e9f1ed17c2cdd06891dde04cab9a886e601980d97b429942a95d3ef8be2", 0xd8}, {&(0x7f0000000280)="fafdcbdb8ab066d3a12e449414d6595639e403a13d77018da1ee2fd26a98ec5b811b09eb53632c4006f52d0b3659157f12c0b92e462ace79ded4025f14be76c1af7250730504854ae4eb9fc2d05232fa75deab2baa13e9f1438dc81fd14e6f7d845f28fd40f4318c", 0x68}, {&(0x7f0000000040)="07d3d1ea6ba4d0dc6376e6d648897ee9ab9afd3c7e258c0956a5ea8a2247", 0x1e}, {&(0x7f0000001440)="3400c7445f39aefb6f77f7d1d72468b62e06527b526ca203a6b59851e8ae07b084ea16505197fa370ecfd1706e721ad7aaee62d7e3f828334a62cbf6be3a453828ccb680ee76c8d60ab7b09ccdfb4fe149498277bf4cfe7cb500dd92f9f902f2703491ba6d41337d873e574fee1d47a1b8e12fe0ca9c3ed98920d8749e9fcc2a77fecda648bdcc6462c9d73ecb167629c62edcc0fa56035c73e29dfbbe658c2029ac7d174123d8d84eface464b3c4ab0930e6b3e4ae93ec9d17d81c106fa37d7af6e4da1ca31e1ba21d2a22bb5304d09af6148f6329241e4df59a75a01d450201dd16d4e2c9f96a0015dd881d8b4d3e69cc1e2154b374cee416ff1eeb4cb509ee9145bb115bfd1da15af7fb44e266749ef18e5aa087fb8ac627420aa8638979ba1b7abbf73bc9e446d4000959f3f3ec46e7e4a8d26001adb22b9b3d44b77db03dc44e0cfb8e9c55bfa1a6d7d737294f9c5ee7b1d5b52915167e19502a47a8d846156676bd521c77af2c5b240fe5386d1c98ca4f52fec4e446ebf99c0bd5431b7f8ae265950425b265b68f85e121d979f2aaddaedb9bbfb929504c58a9869167f8a6582688b6eca266d25113c1990e2b9fd30369af645ee9280d4e1cfdf58d4ffd928380012397421a0c41019b70f97af40aa6c197c55e2488dc200e6bfc906a760e9454c986f0a43728048d62c6c6d3d7c9eed773a7f03c3e00752058c0f0de965ae819b4491621b672043f44dc989c7609987ac251ac45724f99a1f771f789a4364f790d5b392a29da602e8ca588607182617d3bcb3b0cfd104b712b4a71310afda221b6a8a7600b16a4331bdf611d9405b696dd80f8eb0e61d6225699ee2c9f98d33da570a4d01c85fb36c260d30bc8ab4d2b9d777256d2f8b179bef06040abbe0ddf68c2a235c1440300833d82cdca55b8609517975a688275eb6985856bf6be12dc8648291064a46a19286e84a5fc90ddc41b58aea7f264ab80a361c8253ed8feaa3dc29c275d0d824cfed26fa8de00908039d5012fe417939afb984390fae0e51b4d748b945126401ccfd35f3ccd53ca940a39bcc7e250f7b063cb98d97e9f9b62e9b135752beae4e9e42d7bfa1f7d1eccc84825b3399e9e149ef85a03935ae3a51dd06d5e529c7244f50c42827fc8b5b2923179771fb440104bd1a79a4d8867444735253b22da9858fc7888dafd191ef6e52b678b86fa359f8fc0e95faed9b0d7f2caae9a45f66d1b04bd1e7d1e1de383b5910b6b076e80939a5bea8d2743673457576bc92ae3020b6db2dcb7389066d03f6a7064516c7147b7f0edc0820862b3ef217d570c0f6a4d1292d511caee7ff891bf6d3f9e8df48460d146cacb63078b2637c05dd44d98bb65ebced4bdf92841a902b6e31346998e2241fddf23b06625945f9766d673ad74bcc502364bc88fc0b6fd3db4e49d7ef993514b57e3257a535ae6dc139a4364b95961e728ac71f94ac7389a5d8279f7e2df717d699c930d1ff05f9a4fcf7a90299588735e1e4a2c7b6a6c04f62a72aaee419576b891142d8f3992e5a09f2441b2e702a57f542c6feafc3771634b980af119ed471f35d532c34ffb4d1e0d8b298a7c186d5f59475483299f0010e9df98548cfc36b2a36d86f0eeeecb2545678d0d3922cc5c1f88b0a2ff926468d976642d55eee3e7a4dab21d78d6da7b7b059f8d17291e71c648af3044e81c7bb8d221b0e700bf64ba9e1c1579368f79b589b4c6e533dd0f76188ea502a89ea35685c747abc62c6a92cbc881145b38783716571cbbb50fb834ec110b3d2bb9fff94c5f06fa322108f45ea50608d01d92594eb69768180737e8b0e995bbf99ece8845c4081b119716019b1c271d636fe75b04b6e6c53f1a4b6a2d2346f4f18f7e6753058bb1d9296e3f036569fff600561c7a9f7ac1a0d2341c6bb827b8f66ac80186e4b57e7fdc141eff9f499c21bc9d4e3081ff2e85e7669c44a1fb1b75be86bc9bafc882d62c00ba3cec725396ed62bd5c249d50df8eb68b691029325855dae99587c8eee0bd09e486dd07d824dd873371df5095dec9d0f0b3d19748e0625175b0a51f5e0078cd5b8710920ae372526215ed367df57dcd72ae21c1b6749196e8361d5f22dfc10442ed4ce5fc6e71b9965891350a887d54faf99bf7fb84bf99d4a86e0f1da97591b09790002cba72b60f639971a9cf53ebe7645746b5661fac205dc31778aee2c2aaf04c1fdd7c57631c10f139e3d9f6451f39ecc539c456733df492e928b30e3e0bd042c7a68c6855e204c7c250b40f6536bec41c672daaf4f38b7422cc0cf8b55986cf030e8519f51b66404e2755a88886183a5672267f3f07362351609bef7409a964924ab5b57e014cb53540f6244a4ad6b856de688d17e5684a6622d7982fdaa8e859bc17f8f50f2b486edb846001364461d94f4988192f4e72f94decedffb623871192960e6fec9f3b1137cb5fd204b00a4a311b045c220a7a7a22b4b8112b09af00b071a9d6fe75435671ef2bd09d03804c8860d75b3ffa75e713fe60223fbd1714dec6003aaf643585283c2b4bda6fd7d4e849263b882124707259340e89f389c07d44efc525d524b85b45be58eb29954ec86c2fadf8d3689d0010449900d7bb0110723c8d9985115e9e387b332a2cdb8fe3507c51a2411bb1f481bd03c3dbc4dcec74b00433ba22dc3ad344e176432c7b0cad53923adf7e5b6fbe7baa23a24f47b9d21dda43ce2ce8807ae759913ce05993ca341cc116d8671e6a54534da040a85ceebe999274d30c07051ba48b82d0914016effa8be3e0b6deb7988ef0c59e0dfafbe6c659a13da18f25437a44681ea287411cbafb9c67bdf753cc0a0ef1226dd12e7dc1b846a7db59b17b337a4b22d159165c836c764e22472ee910576a1b687155f81add0f4b1bc5ddcf50d601445005312775eb44839385a0f986d2faf1e2c86a9b985175679265f026b6bcabc530351edbd80b432447881c5a0146281f91bd3a25b5f69a0863258d754f82acdfb695d00c1135f7943ef803ca2bdaba2d083a66168bc53bc2b581587d94681b228a430c10e06206c1ab8b1da5efe1c262f9f754106fa33c19623556038ae41067790c00555bced6a7559b9eb8e339d3f496d5fc20262188d9392b1ddf8b0211cca7c5f7d3b2250697af090fca652b5a85ef165a1c0b60cc22080c87302e973b1c00246a3aee0c7d029e4d7ea95d23a90a69584ccf1e07b0c1a8a41bae16e0c74800c66e366339f2bcdaa2e1d145ec6294eef9520b5bbeb4e519b6a7301f54c9335ab1f97f2759400898f8a3cd85846d12e6e6039114152665bb5ed572fe36dd4532fddcc6cda2dbff9a571e0141b05bac75b1eee5da4d5e99a626b3ece714c6749802848555a07c811c5185afa50ca9c1ae8c960abb857b7cbc6b235f949bb29f155160a1aebce9bc451ec556f797e1db9fcf32c146c586cf1ce13976811987012b5616f219430bd9197289ef1452af1187eb489deed0e025458e06d1d3000563d7f7525dd2773d4addbad8219366d6eeb92c860c076c57e2d2824de130b847c5928c69ec56dadf2781b6783163c0d672d5e242348ebc2955830924c83a8a14029e339c698024e017d013086eebb7216f94ef8507c8d80d0ec3dfbe866cc8bcf3e505e95c874f9f6bc3c15f2620d4e2c73e4698cfc8c896f187ce4c2e11fda30faa3802ce9ca07ce46d1f2aef71ff594b16517b9e7f144aa7594bc2518b9ce32d058c1e6252509b80e0c50fc44451202ee5edfaaf5264fb8846ea20d685b650f9d6dfb40bf600190f025d54aa229f8d95577fecf90314147c2235357c03a35d2210aeab0adc89f9ce47394b3accb92918ce557bccf8a34b7efe96f7d971b12255b767f769c3e6751f2d594832ac8d3d1d9e4e0dee131e39f2dc26287c89be895121b53d666e1e3df36df0282dc3032b41cce69cd99f82f4416ce7ba031574384f8ee6479e0355537ef111c207f21e7eaae89a63b28b18fe1932fdf28370fd73c9359cb3473cdf7fbc07c39a23b1b75e9c1a4f21e4bd5371a7684b017dbd5bf0f2fba88186cc35db253ef6679941caf80c67e6f710d64d62259878bd31c049375a118487dec871d1155a3b1d183dee21d22657976a211735a53787f52f290d947ae0ef6d11f8a26d8480eab7b66ae2d937efea70c8212c75c46dd7d052d10fd4b0932446ba9bdf13aed34046873c6f5fc2e3ff9475582b239fe50b16224f832db12b52ead16d652467d07446acd61b82d0d24d72a2ef3f63218a40a8ecce5f2c426efeefac9afcca8f01a18d20047b9a29105476742bf4dd5ae4a776b9474a044697d3e8a61c9b6c0dc3e6d6de65b495c02ad774367ef38aa8fcf7e39508077256924ae83629c863dbd41b1f95fa47ec2ce5bba8cab384c2423e55b4daec098cf118843b78cf9c7af35de07c754e93dec6e53ddd0032f59b5e3e53bea22355cd964ff4507745b665423890e7a8e8e1a8dc49a68c50b6d5709", 0xc6a}], 0x5)
write(r0, &(0x7f0000000300)="2d93e7c098550fbc52f1228452bb93d6abbfc2bbfb2423fc7be943c574dc298e1ddfd82e56ec6a83389ac9b0510d4fa973f07b6f814b2a4ee60d6e357ea629092c99be7b129807336aeaa67346ddd7f6d914b586b58d98a0ac60d32f21b7d38d1244e588c63352173de0333b38cdc36c27cc3378", 0x74)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x9, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffdf7)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
socket(0x18, 0x2, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000140)="b1000504b1000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b407d2707e3d81f4dbbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89750fd3872babfbb770c1b98bccf5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000006000000000100000000000000000000008135d08bb2d7fd4f6c59", 0xb1, 0x400, 0x0, 0x5888adfd882f9c5f)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @rand_addr=0x1, {[@noop]}}, @udp={{0x3, 0x3, 0x8}}}}}})
open$dir(&(0x7f0000000040)='./file0\x00', 0x7124a24c9c995e05, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
symlinkat(&(0x7f00000000c0)='/', 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='c\x00')
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x36}, 0x4, 0x0, 0x0, 0x0, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000080)={0x0, 0xa8})
openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
execve(0x0, 0x0, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000040)="643b18301229806232541b5fe05085bb777269a07260d9dfc78c50be4f5c0a7c2ff4e710dd7e5f31007b27d71b6a08b04ab954ae7146c846f9ac1c9dd9a2e2ef277e1300ae974a0d85fa", 0xfffffffffffffe62, 0x6d)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000000))
getitimer(0x2, 0xfffffffffffffffe)
mkdir(&(0x7f0000000000)='./file1\x00', 0x0)
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000100)='./file1/file0\x00', &(0x7f00000000c0)='c\x00')
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
r2 = getuid()
seteuid(r2)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0xd4)
rename(&(0x7f00000004c0)='./file0/file1\x00', &(0x7f0000000500)='./file0/file0\x00')
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000340)=[{&(0x7f0000000080)="34b1511e987487", 0x7}, {&(0x7f0000000100)="38d5c4b5490e2c4d25c079a30cf2bd7b418536a4046c6ba601a840f7e200636eb1df7b0ce77d9ec90fc3124e4b0d0310e2dc8370065bafa5d7c98cb255c3df95aa76b6ce71f45bd46dfc4671e7345acbd6423acf507f803012de1856579577a409b8efb44960c5007e337ca20a4cb6e7e90b5b6dc56916342134dd3533c0def5b9991f1e5b213e29aa16c2716b4e2378d13afca68cc6a2358f9f3ce5aa2c4f0933f2e4f8efe8a4372851818ec9866b736ac7b3b7217c140ad3fcb16fa269edd00687175e37", 0xc5}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000300)={0x0, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000240)=[{0x7}, {0x3}, {0xc76}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)=ANY=[])
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x8776dc0573891baf, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
ioctl$FIOSETOWN(r0, 0x40047307, &(0x7f00000000c0))
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000100))
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000b00)={&(0x7f00000001c0)=@file={0x1800, './file0\x00'}, 0xa, 0x0}, 0x0)
sysctl$kern(&(0x7f0000001100)={0x1, 0x16}, 0x2, 0x0, &(0x7f0000001180), &(0x7f00000011c0), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff})
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x8020690c, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x60}, {0xc}, {0x6, 0x0, 0x0, 0x4000000}]})
write(r0, &(0x7f0000000180)="5d526a9208ff69923a366b51f0be", 0xe)
r0 = socket(0x2, 0x8001, 0x0)
close(r0)
r1 = socket$inet(0x2, 0xc002, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000440)="ee08665d19ac14d5e51348771197a7728420aef61715f7b183d4b3830c921bf0817a0000000000006a89dbdf", 0x2c)
connect$unix(r0, &(0x7f0000000180)=ANY=[@ANYBLOB="8202a6ffff"], 0x10)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000480)="8115e179be9bb62a816ed03af63ed20ef10c89a5215e230c3fd2eefa0a5dda0e50fe36ba463075414ed3652d665e63c067c489d0b2a05b1983e90801e014da8580afd22318624edb99f031173bafb77f3bda57e210e98a21bb2b0469e817cb68e489629a1c272c84c215410891be343fd0d4a01985c7700811819747c505f67a34ef2c45af707e5cf31880f1dc74c7d2915e9ce76b01f701b77278b0e9f279e29a48aa203be8ea3b40acd8dcac3bdbe86561d24f39ad1c58fcb51ca6cf2788ab371ff547ebf251f50dd54689f07e522b923566c024d90b06e21070a709a9e8a02af91b13bc66939feba64a1f44782d7fd20afdf812fe07055ccdd40922068b5c4b3dd77d68185cefc31a314ae91294720711b4db5ed26f9e74470d4104f354cbc75f8aef48953ab0d1ba670f6548c90daecabd1a2ea0f17e425f17a0ebc047af055311f1aaaa3742c48c62140e943900fc25a0a623463ed7c42c68c99eec76957982518305c7459d6e5da59ba70a05b6e5ec42883a5e4f6dd69da0b5c127a6fa851b12689aa81cf8e97a56227a19ac0ca74583f516e46dc1784251838b354bae82d045b03f369a6063fa699163ba4ca5704ba7f112175b3d889e4f6f9146557b08b7698cf588d4a9b4080258b1b0f352a201379fc7fec785107b032bcbf0fd37eda4c6a4eed6fe24a689970239120d0e4dc65e6015640f2eb9ca502aec384d7588969bf7046f388aad88025cb7e68b8318df7eff6254cc1228ceb4d751c2a2d3181a66ef057404bb8d72fa863fb4b92ba6c117369f06c2808df23e5387c99ebd94096bf54e561437f8bfdff26ce6546bd097e4071ae01c26c388e2e27078b0bb16de9f5f42d971c04594665e0770fa68a2032873e128d485705c58b0f8811956b51ba788b8af9f9e413bca061424e80dbf0eebc508f6c19fe2f27c3636549e2cbc79699c5764bf07b2a95a8d3342b48688dab2559ac958558c686451a82e23302fd3e5c4193e9c1e52caa7e02ec586264fa9d0ef536691e62b4c4fc85ea72b2841cf46e610455bbb9d916ddfa093c38f95898b32a5e5d8b6971e3980f4b2f4a856f7e79948ff9f4fdb92a7fc66c821b2d65be5d4a521c73440ca2ed1834092c2a30e276af1b44ad9e86f57a555a4ed35087fdb8bc9117be6ccfe1a04302855e424c1d4d39d33a6ad26d71282cbb44970c792027ef3565c7005ccc901f8f743a0a8a8154c1f77cfe041f5d3cbf405ebb7cccabafe31b278e50753d66b525c3cc2c72df370081bd9a53fd86cbdf8c28fc7207236d629a92ee1fd58dcb6c75bd4ee61d000966f4039862c45078aca70559b515d208ce2963c83331a73f879aad82d08da5db71ddf67c6b331030b5cade5081d715b1f2269ac72bbf27f6908d5c00b4161116b976f5f7b61826ecae10ab4d26dc5c45dcb7e07d4fdef14f815ec42455fda39ca6940463bb100b09d3b405864f3fee9fab0a43c27ee2b023943bb43296940e7db5d7acb7c3d4223df82779234c4daadee69b83a5a55eb9a239653dbdbd9cf23ca62c8352170630053163e36a3ab8321ea37ac6260787601feba192ad107638a06ed2ce00af8072980e7847b5b8027a777a1ec8af8aff5cc248e97d903d233babf72366e055802d915200c88e283de649376cb9c66977d2bf487977f24d18c67842c3de126a1dc4a489d1cc6a58545d71b768c66fd0f0e2878037b2b6b7af0bd6366535781189a797b6974127b30314d3ee0f164d8230be1fc8d75039a24c4fa14cfc107edad86f9948ef1e2801a2c261a62580e416dddb9a51f8037714cd6611cc28b85b97e587c473bc972cb1f1130d494c196d2a4155158cc5e708738d8c303394cc28ff2950b30fec9e193d33a2f3a41ee61f6c98d0a8287ad9fd4030bc0be34c573d8ecc2cb3bb759e270646401c9b03a7ecd3faae4f4060be68d9b931c6ae9d7fc3ef2207368c6dccb002ced383028b56120d427d9acc07a461e6aa791992cebc57267069a22c87ccce182aebe83d0ed1d3a0373070a", 0x59c}, {&(0x7f0000000000)="8a", 0x1}], 0x2)
select(0x2, &(0x7f0000000140), &(0x7f0000000180)={0x2}, &(0x7f00000001c0)={0x4}, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc450444d, &(0x7f0000000000))
openat$tty(0xffffffffffffff9c, &(0x7f0000000100), 0x10000, 0x0)
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000140)={0x10001, 0xfffffdff})
r1 = socket(0x11, 0x4003, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050200000000000088000701000000000000cea1fea7fef96ecfc73fd3357af96caa0416e74f376336acf00a7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657ae00000002000000000000020208a371a3f800000000000000000000000000000000000d9b51220799d716f97b000000", 0xb1, 0x0, 0x0, 0xffffffffffffffbd)
madvise(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x8)
r0 = socket$inet(0x2, 0x2, 0x0)
sendmsg(r0, &(0x7f0000002740)={&(0x7f00000001c0)=@in6={0x18, 0x0}, 0xc, 0x0, 0x0, &(0x7f00000023c0)=[{0x10}, {0x14}], 0x20}, 0x0)
sysctl$vfs_ffs(&(0x7f0000000040)={0x4, 0x1, 0x7}, 0xa, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
dup2(r0, r1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x24}, {0x2}, {0x6, 0x0, 0x0, 0x1001}]})
pwrite(r1, &(0x7f0000000300)="977fffffff000000000000000000", 0xe, 0x0)
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@broadcast, @local, [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x4, @local, @multicast2, @random="02e12bfd8f36", @multicast1}}}})
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x1, 0x100000002})
flock(r0, 0x1)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000002})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284459, &(0x7f0000000240))
r0 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000500)={0x0, 0x0, 0xe1be, 0x0, "b06d118c24f6f979cbd500d96cc0a5f09d638b0a"})
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x1, 0xfffffff9, "6da43a51f374f70433b6aa91da8257f5f6620d7d"})
syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
utimensat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)={{0x0, 0xffffffffffffffff}, {0x0, 0xffffffffffffffff}}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
connect$unix(r0, &(0x7f0000000040)=@file={0x0, './file0/file0\x00'}, 0x10)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5b52f60f48f6ca2", 0x96}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000200)="a5781d1f", 0x4}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = socket$unix(0x1, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000001180)={&(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa, &(0x7f0000001100)=[{&(0x7f0000000080)="990b0531cfe81dd2d2b20dc9c34d2c17ac7f0965fa5990434e481b87b46948eae1a0cb45aa5586b39bc6838a9b2fd8b639d57b3518f0c52b61b644249da0bc90d66072bcca97e3b81fd1bdabf653314f3550e01b3439afc6479f6f89ac8021a1861e97", 0x63}, {&(0x7f0000000100)="dbeca4121c2cbd65f64bac52674f9953bedaf9e6f88a35734e90be8c6a708968935434346fc5c346319e208b8d0a7b9e76a2712880d7688403a295e4f8827e3c0511b66c7f48b00f7f32c464d1903c02c62ebea4ed819ca420e5003c0473ef43449263a475ed7390b568f639ea02182c40af0163fe284c48f459be25667a022a8a480106a2477fcee49a634f4b2b0cdae50a9cb05e594b3a6abe5063c7a7000a714ef9de46d57b06ee891561de4993cbd009d43961428563764959acb964afa4d47aacbb8c382eff9806ac6be08558cdb3e009ae74365d3f3da91bbb8d28ba979bb8147aeefbaa59b8d4a8516ddb6666682b5e65a1b0d341ff8a72a7195e041877eb3a336314713e9c8068f94b90e47ceecb1c7f5fb653c5136a362a22340a67dc073d38a72a5ecad59acc743599bb7efd1a61c2a968b0565ae9f20628b75c62dcfce56b2f9dfe38782934aeeaa16bf4dbf7625a7c5f40dbb6679b000586cd22a69cebdc89d74b743c39e0d0bb19e35fcb7470936a8536998a185a895cd5539ea15cb2cec4aa7ae95d533d3beff8eae5dbc525b8166bf26dbb39798b4a4c23b898a509ed5c34cda82b7d74454ac6e98a2bf2f5682c99c83bcbfeb01afaac25d81fd30ba4fdd082001dd4f74b3ccb7ceabc2ae829a3c14e8d15fbba1eaffdeb1c8cb8b868daaeee5f4178b346b048e369d8ba01fd9e310704bd52dfae833dc483393add6c2e65fe0058ac097c0c14b7945bc53456ba187dba297e20333ff5e66a308a919f0afae62c7b4d40b15f3b24cf933ebd815d45cb1a5babfb3f3000309e361deb1f6e9fbbe1a5903ad3225cf00b28249b55f5dc1e1fd9fe47ccba6ea447e17f0a7f873ac4362663ba024f9ed98bfd9f83e367bf4b9a05fbc64a40448177b4ffa8df582ecb6738aaa4e894d773ca41128532a62d2f7e8cf23735be6d34f0f79131a400c67b72a07f9e4a1c864ee9e6564af76ef9d8ee8e2cba79b93a092e1c8f976b55a02a7da4c2379b1d12d8658821370acc4a197659dcfa5dc63ff50633be318b7022903227784a0549bcd8d126296816c225e9581d92db7aeeb05255b8d19847d31a37f58dd2b4bef81608948c0d733eaeb3c97549b258c3e0bcb98c0f0881c08ee91715c1d5ea00b897c39a4a393805d5683a24d59608cdda1ad3c088a7b9ac7d089f84ee04fd691b966698c995828dd042e1bb6b18385331267567f76357ff6a671aba9f42f7935c9feb9212fdff6936daffd8a15c8ddd375a06b6933ad82b0ec5892035d16ffbe3fbc37f9116c25148088426512ab0cc9ff13a50c6277eb4cfa566a7a7c1a121d72a6210e859ecae0341a132da3b80a3d1eaa9f486d233d3cfba6d15276ffc75929c99d4a28d2301f08c39b496ff4c947071b179c23af0fcb5473376c99717e955b024d1cc587647f763904c6ca10494526c7570773016c54b90ce15d592d82640d73a7e47ad3ac5e09b5602f33eedae6a1def286898b2710f31b3e64a41fe958f5da775b6f7312482dd772c117bf399fea4b4505203a213b4c3c77b9c88ddb916be84ab7b89a557dd840c5cd7ceb280a6df1a9fd3a601b0f2cf423a6775954567e9bbd12d2bb91217e7214339c0763789420a1b1ca49c09631d067b25e10eec195c2999f450bc3671d9e0e4cc3011a0fb1499f99d7c3530f103ea2ab68bfdd3298ffc6374627d7391edc540a6ef8d5b907c71448ff2d973e9c878c895943ed042ab75a6940ab59a921a557210eeb93db914134ed44a6c7b2fbbf4715e3ae7e06be43bf38b476d962ae4c6c06900cdd6578aa01b62fdfc770d72d630cc0aeb201ba0565cc6c12785be247e5d41900b91604a06570dde1890650a19fac42ada0bc14383784aa0a37fbb28a70dc2449ec4d58563db34e883a247120f9fa55c813d56d675db7aba1a64399e1ce40e7803cc6968133c7d13d1787214799a2f8b8cab97af73a7ad47e5b064bb448becbf3976012075cbd9aeeb48fc2ffde31924c12fce4ddc555455a972719d41dc122eaee907ec1db141adfc393e105ecee8c9992c3662ad638f1f0a0673d2be5642eede2ff99ff7f4c6cc61e7daaca865829e83eafb7b0e83418f5b0f4ee8a119252271752a486108e06d36c3601c3b8a4480e99d387c94a464979cb584b4e1459c0f0786d1a634021a09fc3c3c908c6d810a22a400b99b89754fab00b9afa4b06b4caebc0ac495dd2efa95f63f2c39a6cc3d059d2dfa1fa8e430e0081d9fbeb0f910bf4a7c72bd45b33a9d58c7ef9e70a4da2ef475fa562f34aa2f7a06d75e7c40a9f0d3aacb46ec0b908065f407b3f277e69e43a4a4e65be9b8d9b114c8176dd91d3af7c887d8dc405fb8f4e118a28ecceb55c9d3a9962cc30ad26e25735c0e5f0dbebfd3829d2f58f6695e363819de64f0e8c4a78b98983be605dabe4b6d359d9b49400b5101800a90d0fcd5b50c25ccd8334618b570252080de78cffbf0e225f8d50c2c05972b27de98bf2c5564c7fa330f8ad253e156599ca3a79d400008bb5620e5b1d276312199c88db86ede98d199e23116ce09a864987d8e2a01d0325c0e66b32071fd2b7b4fc395c831773945a91cf167806a2ae8e4942711949f4a94857abfb14148f3f351ac10c411111deae1f02a2eb865498d20d0c73d8daf087171b0857f16", 0x75e}], 0x2, &(0x7f0000001140)=[@cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}], 0x40}, 0xc6)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
poll(&(0x7f0000000040)=[{r0}], 0x1, 0x0)
write(r1, &(0x7f0000335000), 0xfcb7)
writev(r1, &(0x7f0000000400)=[{&(0x7f00000000c0)="d4bc2c679ba78400621dae7f11648f462cc4180c8cf5259fedc1034c171735e5a6664451381f044e4a5da217b44626d668f1dbf609b8fb2197d8553472f873f34fd38a4cfb8a011073f5", 0x4a}, {&(0x7f0000000180)="2912c137a0725a76f108213b7ce3976a1f991881d4807a6725632953311916ad683d2fb35a243c3913a11bd27a6ef964553e9a22b7b6c48e8b7580d3ef116c4426a3e88eda716a99da82821e000e40df9b34df76443094945918fc225d693af70541a0d94fbb06c982038e5ed21525544df00bc9bb7e2b2c37112cc352384074a392a46115d582de9c1092133832760f293b31ac39b504e44bf3e14ae4", 0x9d}, {&(0x7f0000002740)="e46fa48fa79c5847c51f0f5a07422b72ef056a329a4d96aff53bf8bab373c7bee308e29078f92d237f76074d635183373a1a18fabd09a8ed27900b20a5138d9a871fd66de17b4651ad6a3f301df02a64e13b2d55dd95f0133a98be93d52fd6d5e23333e5f8d5673e0aa3cbc370c278bb634cc219cb7b77d1e65f6c61cfeb65d3d4f190fd8b123695153b853424b0c7eae48d8957833d7cf03e425b621eff9594d070817689192e15c2fc74261bb9fdf8f861b0dd6bc3378831346d296e2e25bb45a28ab9387be5d3089154ff6a8a311a0f0c6daef05c86a50e815f3c018777391e77fffa0e486ae6722364d525963d6eae750d1cf6b562d651e6c367d560e076cb789b5af63f635caf73a76a3568b0c3559cbac29b7aa671fd6ba1204425be795d3a19079266a012df07aa3490743f3f7bda28be00aa490df6ee17274d44a53ef98846073bb3137ee1683aa6e533c0f63a53ab059335f5819b64ccb81e450fa6f185f0d30db19e68228c20a5fab27aab920f9bcf8d7cd6c95cb923dd28ff08a47f7b594f0ff01b6592eaab847805d10b3a1943c91d8f6181f3b8533a7a4a872fdcbf84920f7f5f13c89376cd5a91f3e5685bbd7d008b2aa10b1c0775c25457260aba98514fc63928915270e90d059845555d245f3ab3e30a3631e4861981379a573ebfc8d32d959a472954daaafbeccd77b99ab7b1d3d6f65cf2f653e691db64119704e88d604e3d58d304c37516407cababeee08b8ad3bd75ab9505d46f5fb604f675097ea14429758bca73356a790b696904d485be93d8240ccafc6dd0e9e2d2203b3e948289e61d88025059c353cf792b3580bd3f985e71c2b21f43544ba565dee2", 0x263}], 0x3)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
sysctl$hw(&(0x7f00000002c0)={0x6, 0x8}, 0x2, &(0x7f0000000300), 0x0, 0x0, 0x0)
sysctl$net_inet_esp(&(0x7f0000001300)={0x4, 0x2, 0x33}, 0x8, 0x0, 0x0, 0x0, 0x0)
pledge(0xffffffffffffffff, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000), 0x3, 0x0, 0x0, &(0x7f0000000080)="01000000", 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="a4027b"], 0x1)
r0 = socket(0x2, 0x3, 0x102)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0xff})
sysctl$hw(&(0x7f0000000000)={0x4, 0x18}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x4e)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000000))
accept$unix(0xffffffffffffff9c, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000015c0), 0x0, 0x0)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000016c0), 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f0000001840)={&(0x7f0000001800)='./file0\x00', r0})
ioctl$TIOCMGET(0xffffffffffffffff, 0x4004746a, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000080)={@local, @local, [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @multicast2, {[@noop, @noop, @ra={0x94, 0x6}, @timestamp={0x44, 0x8, 0x0, 0x0, 0x0, [{}]}, @generic={0x0, 0x2}]}}, @icmp=@timestamp}}}})
connect$unix(0xffffffffffffffff, &(0x7f00000001c0)=@file={0x1, './file1\x00'}, 0xa)
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x107)
rename(&(0x7f0000000240)='./file1\x00', &(0x7f0000000280)='./file1\x00')
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f00000000c0)='c\x00')
connect$unix(0xffffffffffffff9c, &(0x7f0000000200)=@abs={0x1, 0x0, 0x2}, 0x8)
r0 = semget$private(0x0, 0x7, 0x10e)
semop(r0, &(0x7f0000000740)=[{0x0, 0xffff, 0x1000}, {0x4, 0x800, 0x400}, {0x4, 0x0, 0x800}, {0x3, 0x80, 0x1000}, {0x2, 0x29a9, 0x800}, {0x1, 0x2657, 0x800}, {0x2, 0x9, 0x1800}, {0x0, 0x3f, 0x1000}, {0x0, 0x3, 0xc00}], 0x9)
semctl$SETVAL(r0, 0x0, 0x8, &(0x7f0000000380))
getgid()
semctl$GETZCNT(r0, 0x1, 0x7, &(0x7f0000000580)=""/224)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080))
chroot(&(0x7f0000000180)='./file1\x00')
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000000c0)=[{{}, 0xffffffffffffbffe, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto$inet6(r0, 0x0, 0x0, 0x0, &(0x7f0000000080)={0x18, 0x3}, 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{}, {0x24}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000380)})
fcntl$lock(0xffffffffffffffff, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x1, 0x408})
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f000052fff8)='./file0\x00', 0x0, 0x0)
r1 = getuid()
setreuid(0xffffffffffffffff, r1)
fchdir(r0)
r2 = semget$private(0x0, 0x1, 0x315)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000040)=0xc)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000100)={{0x30a, r1, r3, r1, 0x0, 0x101, 0x92}, 0x9, 0x80000000, 0x21fe0805})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0, 0x2]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x7, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
r1 = socket(0x18, 0xc003, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028756b, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
munmap(&(0x7f0000001000/0x3000)=nil, 0x3000)
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
dup2(r3, r2)
connect$unix(r3, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
accept$unix(r1, &(0x7f0000001640)=@file={0x0, ""/58}, &(0x7f00000000c0)=0x249)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000000029, 0x9, &(0x7f0000000180)="01000000", 0x4)
sendmsg$unix(r0, &(0x7f00000001c0)={&(0x7f0000000600)=ANY=[@ANYBLOB="fb182e2b666902e3ff011ff3bba9a85e19f514365958431532"], 0x1c, 0x0}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000003c0)={<r0=>0xffffffffffffffff})
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r2)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r3, 0xc1084425, &(0x7f0000000240))
r0 = socket$unix(0x1, 0x1, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x4}], 0x1, 0x0)
shutdown(r0, 0x2)
poll(&(0x7f00000000c0)=[{r0, 0x4}], 0x1, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000100000100000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d0001e4eeb92a265411026ba8af63ff37281c18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500000002000000000000020208a371a3f8000400000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x26, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r2 = dup2(r1, r1)
dup2(r0, r1)
ioctl$DIOCMAP(r2, 0xc0106477, &(0x7f0000000040)={0x0, 0xffffffffffffff9c})
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
shutdown(r0, 0x1)
connect$unix(r1, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b35c7"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
getgroups(0x6, &(0x7f0000000000)=[0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0])
setgroups(0x0, 0x0)
setregid(0x0, 0x0)
mknod(0x0, 0x0, 0x0)
seteuid(0xffffffffffffffff)
unveil(0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x200000000002}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100000000000000003f00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{}, {{r0}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r2, 0xc0107002, &(0x7f0000000100)={{}, 0x7, 0x4})
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f00000002c0)={{0x3, 0x1, 0x8}, 0x6, 0x9, 0x9006})
r3 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r3, 0xc0107002, &(0x7f0000000300)={{}, 0x7, 0x8})
openat$klog(0xffffffffffffff9c, &(0x7f0000000080), 0x8, 0x0)
r4 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r4, 0xc0107002, &(0x7f0000000100)={{}, 0x7, 0x4})
openat$klog(0xffffffffffffff9c, &(0x7f0000000100), 0x20, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000004c0)={0x0, 0x0, 0x0, {[0x7, 0x17a3, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffa, 0x3, 0x1, 0x0, 0x0, 0x0, 0xffffffff00000000, 0x0, 0x1, 0x100, 0x7fffffff], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000000], [0x0, 0x0, 0x0, 0x8208076, 0x6], [{0x0, 0x0, 0x100}, {0x0, 0x4}, {0x0, 0x2, 0x8, 0x827}, {0x9, 0x1, 0x401}, {0x200}, {0x0, 0x7ff}, {0x0, 0x0, 0x2}, {0x0, 0x7}], {0xe9e, 0x0, 0xffff}, {0x4, 0x0, 0x8}}})
r6 = fcntl$dupfd(r5, 0x0, r5)
dup(0xffffffffffffffff)
ioctl$VMM_IOC_RUN(r5, 0xc0205602, &(0x7f00000003c0)={0x0, 0xfffffffe, 0x0, 0x3, &(0x7f0000000c40)={{}, {[0x3, 0x7a2, 0x33c0000000, 0x20008, 0x1000000040, 0x8, 0xfffffffffb, 0x10002, 0x7ffffffc, 0x0, 0xffffffffffffffff, 0x4, 0x40, 0x7, 0x4000000004, 0x0, 0x0, 0x7], [0x10ca71d1, 0x1008, 0x9, 0x0, 0x7, 0x10000000001, 0x9, 0xa00000099, 0x4, 0x5], [0x0, 0x2000000009, 0x8, 0x102, 0x800000000007, 0x194], [0x1, 0x8000000400002001, 0xffffffff, 0x8, 0x46c, 0x4], [{0x0, 0x0, 0x0, 0x7}, {}, {0x0, 0x0, 0x2}, {}, {0x8}, {}, {0x6, 0x2, 0xfffffff8, 0x1135cbe8}, {0x7, 0xf9, 0xe59, 0x9}]}}})
ioctl$FIOASYNC(r6, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x4}, {0x87}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000280)=[{0x3}, {0x35}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
msgsnd(0xffffffffffffffff, &(0x7f00000004c0)={0x3, "39a1de26a3e44646ee42604b07a253e149673c64d1ede6fa37973bddb0dbbdbcc40af083812175ea94710c3e9414e00a2f876d4246eab06938697fe237a76515a9f50b88c92910fd2f1203267a024a3a602a43fad8303ff73295eec601209e96a570e90f3bf02c3fca952010e1ff51f459b65d97f7d1b33fcffe4f6065e25f2013410fd761971de1be5a3f822dd6aec26c3d8cf423947b96886e97b90d0f97b646eab195e09d7c1bafa344c631bb339d8893924830d4ac20a7467351ec213fd8c6079677e4c6bfdee1c6d0c15b761ff347e6543b91ac751f978f9b9d95ea7013b013212bd64c40b5059186e729903607b2a12d1d9c08be2039709d6bc5ae08adbc28fcb90343ede903c2804b7daab8dfaeff1c74ba0a3b53f69f4d86e3ad0df7392db662ea78f0c98ffe05992512d7b3395e7f61fc0574472c4a2caf62292b77977fef879856751d764d686274f3baa86ac64c6576d212a1182196d6d485380911546d8a256bba5e59d6076fa568105b9970bbe6f64ba342f1c2fb2ab5fd535b33c1a0dd19825dade3535e4a4858239d06f235750c17701ec97a52ea0f097f74378372c41ac74f150faed556d82dfc438b0059ea532a56acf67619e29fe6725e4b878d3a86fc5c1823ae8c1e95c60e149d5db047b06cc0a4f4c415108b7e57fcdf5accb75798f4bf708935e2d9a05b8a50a4001a46d8164318d714ac4dd79a26292260dc0aec0e868b3f370ca5e740f42dad6e958343805aa055e10ecb78f19bd691b2b9bdc67648fda998c3e138cda668ef1de6072192d8e40602f957267db734e9d8303e722793b556664dccfba1a8a8bf6e5db9b866ce2d0d7f6eea69aef34a48edf04abd1801e4786c9e79276682714eb83e780b66f1f595b3b84b6224d4668af33d086b3538b4fd018f3f960882e2a104e6c7c9eb9516623595f2ad6ecc92117bff9f1c14a031cca0cced5a43df9e7a7d4af559bf81abfd74d67677c4a2b3751095da806920411786fbe2f0dbcc14a81f840b3a3c2b17720ef200f4e72255ac3411dfcd4d7ffe334643e73940c3396c4ff04bd22f7d2d074343f17157a1740239e0b8c2f4c5e75d9e4568817422babb6b91b0b70cb6ccff91242ee4bf06fbc424525b4321622070173c84eb6c39812bcc446532e60ed798472dae524c7eb98a1af43247d6976a6bee2c8a8e2d305862077539fc92610353c22bfcd5a95dd0a57871a0a6a16a90ae59fc19a3495df367bda7fa85100ea031ff4f4b871e9faf28b0a3b669d5f5f0fa2fd9ca22165e33e5ebf0427cb26182e0ac64b96a0da002d2289c3d3de762e37fc5aa89ac56d893df76f2d2b1be2cc1c8a37452266455340b05a2b6afb81857b26c1a3f86d25d126f5c06e07dc25ae33d413cf9354c0e8eb586390669e71f7e75bd574b2492ed158b22402850ec16f9511e564c67ad7846ac3169b79ae83e2c035d1c70eeb4e3a2cdec8d0a0e6fea3b00403d2cdadfc0aa47f5f28d0ccf2f64591d27ea3cf95c63ac0bd5be1be716263581a2badce74c944ee223dd5af256980d7b20cd09dc9c22b9a561cc498aef6f7f173b0324b55653b83e126147831ea13b6067133cc9d60a3f3afcb5210041bf72bc24740a3f1feea5207a6e71ce0eb838f5e088192d878fcd0eb8c44942bafbf2f486ff48d94b470c162a636a0f68b8e82e79605a13b65f702fe155a30b156f62edaf13379fa12180cfc1afeab2a3a52d661d9cb8a8d5a4ed4a7dae1ca1adae0b6ab77c635b39e706ad647215b67658e5cc81d301b82eb0929b4e02f2fffd82b40baf05b9f125ff908bc15d3df01ee2991e0bb190464cf3c4f726c3771c933ca1d9d49335906758b362c60e742eb002c2fc222e07a7f468cacb012cc938e6a2734b1e1cd15e4f0998beeba20b9ae6496d7968dcfba021b8e33e2df50cd8205ddc506c9124ab509e5c9a5d24c00e714c0ec47fbc78476f12bf49eec96dfe4f1d335f8e4cd243c59a2f91953fd78321b2d7ae4110e144c14abaea86ad5480a6a7ff65a332d497e711a9c141ff9ae1ac25e91e7f152d7932ad4f814f27f6cc13bc7a142b4e0ab8e45bd7e2d088f60d9cc3c85e06525c959208de28ca46c0f953d4a47db2a5bb5858fd559001e6831191a8dde535da93c28e1ed3f7efe025773857054985ebe1a1ba01d92ed88bea54e27d485e945b5c92840576825d731b6ab1e7860e703115e67b58f867aed57b8f7445a8bb185a6692a3d7b4ba4344d0ce11ed44fff5577b1535a9ef03d16b14e9b65c5fca9642aebf73da2bd8e73b8f21943767c4c6cf8974e7cee94cf6c8de4b0523bbf8c15a45084e1e8aeaa46b93ab4a728c4cf05a9c38f2b607b2d7fb989d62a83b5a49cb452fa672a60450b126edaa573379691ef82dd80f2ddec7e0a0aa19b3222033ad0e71baa1b70b8e8a8a89502b3d2522a4ca84cdebeb61e397aa146d4e1ce2276818e0c32395d397f18938cd0e80edd311927c83382859963c15cfb18bca8506cbd342e7250c0d544a293e0d849d64e57eb3de099a34fe5a5aef8d3021857eaae0ff97002a31bb837c97ffc4446af772ccfbfdb305d437324726be0fdf4da3ab29daea8a68a27515cb77293e80b060387cb69f7aaa84192c58fa4727f6bac5d2f70faac6c7453d06c36a126b6b670ed05033d3be0466ed36242bad042d5573755616d667abc6100f6de57187a6fc8f8cf6ebad60018051f8189944ad0d6fae7af80291a1bd8109dfa0337f3fd5f45a761b2a94383e3092a75c4c0d3718d09b5665be1ae5bb4e5ad4d4b9c008f5048fb158595f10173b142f4e99fcd65ff0bde29aeb39968d35d92b5095f932a1adab2efaabde7bf2b52a126f74aaeba4bdc1feb45553c63e22af07eabcbb76b1f16eb7db63ba5491e64e0cee2bd564f7a31c7912f8e68b591c842550260349aa36b1cdb498e3f2d80c01437a8933c38da3714ace618d05922d9f7ef2417993b45d0048668132e8e0f8a35c99dd08d11c65e63acb3e44bd4ea173f951d0339e2c4040b1b398caab9ff61ec8f0184d8fb3cafb6a2ce2b8d93487c360518edd948a3cc6a48a3d912e5f1274e68bfd59b7306215eaf2898f8415bbc395c9e7ab31f20c6406f50d373b90c05374f389eadb9ea08b9c24da9e6f641718cd367de0449fdc966bdfd9a5e8cf074523b220bee3c3edc5eaf760a87d9b02e07fc2fae36f63abe1187fd3deef9e400cd5c3eef8a26efbf777742a525022f705dc395d1f021e1d26e0f6e22d63f31cea90a794d56e99d1241cc64c1e5c0b9c74d897787c01a11cc5c04497c6d176baff1ccf649451668d5d19e6955c0787aba4f2ccb35641437b7783dc4ae5c52eecc3db93c1f2045e3963cb1ba86957f5ed4dee85d7ea8d6e804b64eea948dff24cfbcee9df005524aaeb21b04a99f5dc5a7a9a60f0dc92f9ab3cc7652996bdae6cb90b86109ebce22ee7ef7e75c8fad1d5283f5db43bd967909b92a0910339082ee81c90100ea2ecb8de5c16a0d4ff80cdc716df06f55542ad70dbd0f899a2d6514d3cf9e4820e9a1251ed81b64051aee017b30823a865e0537abbd5a5c1a9feecfbf91906a8f1de1225d91a10749b71a71fac08432dce97c87212a109629181b49f6128469537d2b9738b9e10c7e9b24ef0be49689ccb7229837c76255c35981fccc372934fdbf25fff60ac7d2d530650e550b1c99735fe12bb57c4c5114fbcb2be1467779efaff05792bd9301123e8ddf4ae984e38a3f6cdf13e2fd6fcfb3d43fa3c195b3be1308bfa5e5dd435a2fc77714bf5ec29ab8bd5789fba943c7102dd9ef52393b46a356bf60b29f22f29eb4deaac4c00a45219e2764d70b15ea8f37532c11c6685583f4eca67d1e54f51da7c283aedc3af3be32d6992f5e14b74f59f96b82f2c95a9203e179dbe89d2cd2999b19fcbd6780d4b60b7db2a13e59f6323c6a2b061dad8c7f63e6c44642ad665c282c4e1c5389afd43181be7514b045cc41c1336f21ecc6f226597ed3f79c7a89982d5df098b34136925536444f997f0f771a2df084b60b938137bdd1ea3f738789f8ebd5603b286d4777e348a6dcb941225a93eaa6c0531f6892f8117163346c28ff7728679bc1825e6700d06930540d26d9b436dbdd2751ae9b78b4d320897b21c91b4965dddfef54b3ef523a5ab5cfb9341b0893e8f38e8ef5e34169cc41da6d06ba979d5f7e5acefd3550eb85d15af297d6d3f04028c7dae92f6da0853954064b89907f3f4d88bb3b8111ac5429ed64b121aed18d4e6ce4f1adc8ed628ce7039f6a80cbe8f555c057cc65447803ae3a438af6a925a3b324f3651f20eb7758c622457ec3b42686388dffd489d33054ede12d5f0672552dc9f2fbcbc51ccd223dbfb192c486179e0fe369086e24f18f6d67bb9621825476557a60aa595a77708aa7ac6876d2cb433f4e8209ec8a1e43372b8956547e1c4acae918fa826de73b3bf2ded6c7903cda3082a38e4a897e8e624b83e07be4c768cb18480d5ef46543191b703df3930fcf8975fdde883cb2864758f250472503dfd56f8bc2277acfb424c450a48bf3ca2f440b42346655977dbbb37e24774d4fd5fd8c8036732a03b095dc1cca6e3fad07dbcde68f3d545978f7c7d13d5e82480d3e9108d46bcc1b90f2140ff4399a4a95e6e09ae1900d8056d655d8a5c76f4fb4b672f6ff57d32cd248ff13854bcac3db280c0e7af9a012ca74d7f8add447b9133ca5ac54d9a78b02e89cbd6852b4b53bc6e95100730476b68db0a27afd5689f917dbdbf62540ce8ca95a251326f6385ff4182baef44f9cb3eab5bdca1603522f39f394d4b752d6366f7537dce004425ddc895e3daad82a38fc7f6200b38e826323af56ab1ac50316abc04c1c93cd1b69be016c87a6de03dc6d470bf4f40f2680f73747d80e0bb44c7f232e529b6f824b47c8a822aee0bbf20ec8bf0764df0322b23c2cb3979ac1b06d32dbe5c048cfae3cfe6cf976c7373a9a5a12f89795815b934472b9bfb6df8b321311471b3e719cc12de3332f119bd4d50d2c93dd3d28c99bda31f4bde1d7fd5d80ed75f545ca1ea1ebd3e285393e1f6a2fd303d564829ec44769bf00230429060e7867ed21d0644ecdb748a8f384efb41a3b9ef59111aa2d64a23e72ffeb7a55f8416e63e2ac5a50e431106e28eef8d03450ce48daa96d5369274c9a6ba852e62024818e4c998abd29d81eb7e7b7c4278cd43e5f35221c50d845134a77fa57e8be122696cb2c82568ca7c4d00c44708e57666d9ee83370d42eabb5e21e5e9e8409af82c7bf4255cf894134522c4eeb67f88982d0b7ba5d2460b33a80862bf7e4ed9f7697a1b943b565ca773d1619240be11347ca87b18ac6850bcbeddc0a40461a98168ba7550ad191230ae6723d16d42473586c1f0e470c4ce00abdfed5b0a935a1a897a75c6cd4de1e985a455f93a0dbb564b3452bb79b2222471c98f9c3fb86ea3133827c5e5caad6a703699c0be0e74bc6fa252f0ef547921314c4948703b40e65393037088ab856424bf356c1d02e9e18dda39762cd47e398f39b0b27f7e3c99c990cb5c16961e410fcbd362d3adeddf8d9475fc620697ea1c632ea9050e4e526e31460f8f5a1e8c817c12e88189b390f8d20c4445e2282cd059e98fdcaa36a8d53c7483788ffdda0057ed2394441c3b58619ee8c59546df8873862ae609fd56987230596e6829e6e02babf286756fe53051a04ece31c00e53c403cc4f447fda704e5e17f9c231ca299f1b86787520f8c15a2385c4229793106aabeac4a02870ec7fe6a460fb47f8efb69e"}, 0x1008, 0x800)
r0 = msgget$private(0x0, 0x420)
msgrcv(r0, &(0x7f0000000000)=ANY=[@ANYBLOB='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00'/70], 0x46, 0x1, 0x1000)
msgrcv(r0, &(0x7f0000000080)={0x0, ""/123}, 0x83, 0x2, 0x2000)
r1 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r1, 0x29, 0x69, 0x0, 0x0)
msgget$private(0x0, 0x6d035d1a37d3b776)
msgrcv(r0, &(0x7f00000001c0)=ANY=[@ANYBLOB="0000000044000000084e9f7bb98d8f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032e7de0b2d18f22f00"/97], 0x5a, 0x1, 0x1000)
msgctl$IPC_RMID(r0, 0x0)
msgget$private(0x0, 0x0)
msgget$private(0x0, 0xffffffffffffffe6)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={0x0, 0x0, 0xfffffffffffffffe, 0x23}, 0x0)
syz_emit_ethernet(0xe, &(0x7f0000000100)={@broadcast, @local, [], {@generic={0x8863}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x48}, {0x7}, {0x16}]})
pwrite(r0, &(0x7f0000000240)="f94c4c49dfd7675ab985f9af880d", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x1, &(0x7f0000000000)=[{0x48}]})
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000100), 0x1000000000000161)
r2 = open(&(0x7f00000000c0)='./file0\x00', 0x692, 0x0)
write(r2, &(0x7f0000000180)="cb", 0x1)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000001280)="90", 0x1}], 0x1)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
setrlimit(0x8, &(0x7f0000000100)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000004c0)=[{&(0x7f0000000080)='\x00', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x25}, {0x1}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
shmget(0x3, 0x1000, 0x0, &(0x7f0000ffd000/0x1000)=nil)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x5}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x8, 0x0, 0x0, 0x0, 0x0)
setitimer(0x0, &(0x7f0000000000)={{0x0, 0x3}, {0x0, 0xf423d}}, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x59}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29f", 0x70}], 0x1, 0x0}, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r4, 0xc1084413, &(0x7f0000000240))
sendmsg(0xffffffffffffffff, &(0x7f0000000280)={0x0, 0x0, 0x0, 0x0, &(0x7f00000017c0)}, 0x0)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f00000006c0)=[{0x25}, {0x50}, {0x6}]})
write(r0, &(0x7f0000000040)="54ead1000000fcffffff00000000", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000280)=[{0x4}, {0x4}, {0x6, 0x0, 0x0, 0x100102}]})
write(r0, &(0x7f0000000140)="7cd23fc0aa17dddf7830faa1aab5", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x54}, {0x48}, {0x6, 0x0, 0x0, 0x4000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="2d00c784be3f585402424c33c16f", 0x49, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000180)={0x4, 0x2, 0x2}, 0x4, &(0x7f00000001c0), 0x0, 0x0, 0x0)
sysctl$net_inet_divert(&(0x7f0000000000)={0x4, 0x2, 0x102, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f00000001c0), 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f00000011c0)='\x00', 0x14a, 0x1, 0x0, 0xfffffd7e)
close(0xffffffffffffffff)
close(r2)
r3 = accept$unix(r1, 0x0, 0x0)
dup(0xffffffffffffffff)
write(r2, &(0x7f0000000040)='O', 0x1)
recvfrom(r3, &(0x7f0000000300)=""/159, 0x9f, 0xc0, 0x0, 0x0)
r0 = kqueue()
r1 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{}, {}, {}, {}, {}, {}, {}, {{r0}, 0xfffffffffffffff8}], 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x81}], 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x419, &(0x7f0000000100), 0xff, 0x0)
ioctl$VMM_IOC_READREGS(r1, 0xc2485607, &(0x7f0000000380))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000400)=[{0x0}], 0x1)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000000))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f00000003c0)={0x0}, 0xa2c4a4fbccc5c5aa, 0x0)
r2 = socket(0x11, 0x3, 0x0)
dup2(r2, r1)
recvmsg(r0, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000001680)={&(0x7f0000001640)={0x0, 0x0, &(0x7f00000015c0)=[{&(0x7f0000001840)="62a60483291de3543852", 0xa}], 0x1, 0x0}, 0x81}, 0x10, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0xc1206925, &(0x7f0000000300))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f00000003c0)={0xaaaaaaaaaaaac67, 0x0, 0x4, 0x0, [{&(0x7f0000ff3000/0x2000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ff1000/0x2000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffb000/0x1000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000ffb000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ff6000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000fef000/0x2000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ff8000/0x4000)=nil}, {&(0x7f0000ff0000/0x1000)=nil, &(0x7f0000ff5000/0x4000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000100)=[{}, {}]})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="00ffff40", 0x4)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, &(0x7f0000000040)="1d8648be", &(0x7f0000000080)=0x4, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000513ce0000000000000020000000000000feceb11ed70bfbfbacffffffff930835cfc7", 0x25, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210]}, 0xfffffdfa)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000340)=[{0x60}, {0x74}, {0x6, 0x0, 0x0, 0x1003}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x107)
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
chdir(&(0x7f0000000100)='./file1\x00')
mlock(&(0x7f0000c00000/0x400000)=nil, 0x400000)
munlockall()
madvise(&(0x7f0000400000/0xc00000)=nil, 0xc00000, 0x6)
munmap(&(0x7f0000c00000/0x400000)=nil, 0x400000)
r0 = kqueue()
kevent(r0, &(0x7f00000002c0)=[{}, {}, {{}, 0x0, 0x0, 0x0, 0x0, 0x3}], 0xce, &(0x7f0000000480), 0x99, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000100)="eb", 0x1, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0x0, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0x6b}, {0x0}, {0x0}, {0x0, 0xffffff6c}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x842)
shutdown(r1, 0x0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x612, 0x0)
dup2(r2, r1)
execve(0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0xa, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000100)=0x1ff)
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000001740))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
ioctl$BIOCGDIRFILT(r0, 0x4004427c, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x2}, {0x48}, {0x6, 0x0, 0x0, 0x801fff7a}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0x9, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000540)="4760d4acc155b5b21e5007bd441dbd6ae98db90dc4970b191a8d270853255d72b971c936428a4472b7aabad4421675903af8eb04cee21a9a0dc2509a1eacf07d34dae0ebb9a30c6eef187b56e990a0e54b2352346565371e6c4294d67263bddc025afa56e95b086996822fb6b21b62a3f4d8927e7151a7fe79e1577e0f849eadfb5ae1e3c5b7537324b812f2530d4acfee8176f74d1a", 0x96}], 0x1)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, 0x0)
pipe2(0x0, 0x0)
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, 0x0)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000100)=""/65, 0x41}], 0x1)
writev(r0, &(0x7f00000002c0)=[{&(0x7f00000007c0)="668ebea2a6cd9a14da21d9ef08370000000000027b3d418388532c830edadb820c619ad8762fc4374ad97443a184c14d3bf391b38db5e62db0ce686e19bf2747873599a64f93da5fe7fa6f58483c01ca27fdfb63e96b767983fc2ef8d54bff2d8f4ccec5771c3c19a6ebec93f575ac77381fefc6ad4ea5a0eacd9d8bbb0cfd9fa7e77e01ff228c372fbef07e9d13996f42313465aa0a", 0x96}], 0x1)
r0 = open$dir(&(0x7f00000000c0)='./file0\x00', 0xe3a, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000080)={0x0, 0x2, 0xfffffffffffffffe, 0x1000000000001})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@random="51d2e863332e", @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr}, @icmp=@echo_reply={0xf}}}}})
r0 = kqueue()
r1 = kqueue()
dup2(r0, r1)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
close(r0)
getpeername$inet6(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8000000000003})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x6a5, 0x1fc80d8b, "0400e5ecc7db00"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1d}, 0x4, 0x0, 0x0, &(0x7f0000000200), 0x0)
sysctl$kern(&(0x7f0000000040), 0xb, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r1 = kqueue()
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000005c0)={&(0x7f0000000040)='./file0\x00', 0xea4, 0x0})
dup2(r1, r0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0x4004426a, 0x0)
sendsyslog(0x0, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000080)=ANY=[@ANYBLOB="a2026f"], 0x10)
sendmsg(r0, &(0x7f0000000040)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3, './file0/file0\x00'}, 0x10, 0x0, 0x0, 0x0}, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000940)=ANY=[@ANYBLOB="88000000000000000000000000000000d10a6bad10eb62b197607e5c035035a49e30f52f178e9527be463fa71f0740b7d1d0f21691e6ddcce44e2254d032ac4c3b280a9f7b7962488aa656e4c44c4a23332624ca76b0ce44b7f0c64a8742a248d74a6a62f538bcf67ed03246d0fff4fcf561938471dc5c7b97e3a149fd0b3bb84800000000000000e0030000000000000000000000000000b6100747113e286117686891699782df7c354f9eb25942557a4dd2a29594ce5082c4ce9e6c3a68495bf447a60e37970fe499ceeb4a294080b5db44dedc93326040041536bf5922e83e7ebe1af8af1ccd1770b8e53bbfd7ffbf07e043bbf3d91216c8dfa7ad73c61ecfd99326383ebe4396250ef0a5fb5ac6cdf1877518fdf048f6d67dae997e67b2fed9ac9a2e602b1b8aeaf00e3b209af4d786ca92039643ea2d0ca84cae373d06f1ac062acbf3b467ef54850efc1196917ed0a3ecd9b62c0d350dbb636ae992a04d35888cde03a43bd7e98e3d56f3523b3aaaacc306e593dcb58117af5208"], 0x468}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000240))
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
r0 = open(&(0x7f0000000280)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040))
recvmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0, &(0x7f00000001c0)={0x0, 0x36bbe29b})
sysctl$hw(&(0x7f0000000000)={0x6, 0x9}, 0x2, &(0x7f0000000040)="9854c0e31ca724b5fbd80e88ebe1dd2f", &(0x7f0000000080)=0x10, &(0x7f0000000580)="8073488961c1e05b7e8df600613340f0bab581ff8864e14b318170c15617cb4e8517d4dfdd409156723e7b734d363d3f38a643f5eced5a31b9d9cec0c039522466db4f337882588ba765fce9a847bb1d746200100000000000", 0x59)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x1], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210], [0x0, 0x0, 0xa5, 0x3ffffffffffffd], [0x0, 0x0, 0x0, 0x200000000000], [{}, {}, {}, {0x0, 0x0, 0x0, 0x7ac}, {}, {0x0, 0x0, 0x400}, {}, {0x0, 0x0, 0x0, 0x1000}]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x1, 0x13e767ab03f556fb, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x410, 0x0)
shmctl$IPC_SET(0x0, 0x1, &(0x7f00000001c0)={{0x8000, 0x0, 0x0, 0x0, 0x0, 0x48, 0x9}, 0x100, 0x3, 0x0, 0x0, 0x6e, 0x5, 0x6d})
setsockopt(r0, 0x29, 0xe, &(0x7f00000004c0)="0b652aba822bd43b69b3fe462a9ea20ff230f3db5d9a56bccfb9fe46ca049c072af64ebeac62c0727d06d28ab2f9190fc533bf17198e0d031c37ec589746a0182715201a4322d6eead9cc590d247b425de70bba239fa07854f951cb8ed86933daee428c41b2bf28b5681ed025a7f077dc183af9ac90942aac68b851b454f008c2ec3e8db51ee074e6085790f", 0x8c)
openat$speaker(0xffffffffffffff9c, &(0x7f0000000180), 0x100, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r1, 0x29, 0xb, &(0x7f0000000040), 0x4)
socket(0x20, 0x8000, 0x7)
lstat(&(0x7f00000000c0)='./file1\x00', 0x0)
poll(0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206919, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
r2 = dup(r0)
ioctl$TIOCSTOP(r2, 0x2000746f)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000180)={0x0, 0x0, 0x5fff, 0x20009, "a5130c00000000000000000200"})
write(r0, &(0x7f00000001c0)="fe", 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f00000002c0)={0x0, 0x0, &(0x7f00000001c0)=[{0x0}, {&(0x7f0000000140)='C', 0x1}], 0x2}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x1}, {0x50}, {0x6}]})
syz_emit_ethernet(0x4a, &(0x7f0000000140)={@remote, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "010001", 0x14, 0x0, 0x0, @loopback={0x4}, @loopback={0x4}, {[], @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
mkdir(&(0x7f0000000040)='./file0\x00', 0x0)
chroot(&(0x7f0000000080)='./file0/../file0\x00')
open(&(0x7f00000000c0)='./file0/../file0\x00', 0x200, 0x0)
r0 = open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000400)=[{r0, 0x4}], 0x1, 0x0)
rmdir(&(0x7f0000000180)='./file0\x00')
sysctl$net_inet_etherip(&(0x7f0000000000)={0x7, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000000280)=[{&(0x7f0000000100)=""/57, 0x39}], 0x1, 0x0)
open(&(0x7f0000000000)='./file0\x00', 0x100, 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x25}, {0x20}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000200)="2e4cb129e1bf283037212ab1076a", 0xe, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000c00)=[{&(0x7f0000000240)="c820a0be6ac42fe8e80553b47d91e7e61a79aceaf559915dbc5b1e5b1d8d37c36e92018e0741eecda2cc2836719a03d07fdc7e20de3bbd6ca9c84e3a27525a43de6c7cb4376042fff051103a5cc0c1e4ca6a25de8c2a34535f315a6751acde661a42576ceafff24e2f482fdde0f61bad3f3b51390b8de61b69cf0cb7903731188bb9c483164e531813b08884d8120a603bdda21a083b7929b5f45f05bf0de4888de5b2a03fe60f9d3746", 0xaa}, {&(0x7f0000000180)='6.', 0x2}, {&(0x7f0000000200)='.', 0x1}], 0x3, 0x0)
execve(0x0, 0x0, 0x0)
syz_emit_ethernet(0x400e, &(0x7f0000000040)={@random="93c40982e062", @broadcast, [], {@generic={0x88e7}}})
setitimer(0x0, &(0x7f0000000040)={{}, {0x0, 0x9}}, 0x0)
getitimer(0x0, &(0x7f0000001680))
open$dir(&(0x7f0000000140)='./file0\x00', 0x24a, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
chown(&(0x7f0000000000)='./file0\x00', r0, 0x0)
r1 = getuid()
setreuid(0xee00, r1)
truncate(&(0x7f0000000000)='./file0\x00', 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000780)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000080)="ba", 0x1}], 0x1)
readv(r1, &(0x7f0000000200)=[{0x0, 0x5b}], 0x1)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
writev(r0, &(0x7f0000000140)=[{&(0x7f0000000040)="f7c471e60ffca038298c0b7433", 0xd}], 0x1)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
minherit(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
munlock(&(0x7f0000ff4000/0xc000)=nil, 0xc000)
mkdir(&(0x7f0000000440)='./file1\x00', 0x0)
unveil(&(0x7f0000000040)='.\x00', &(0x7f00000000c0)='c\x00')
unveil(&(0x7f00000001c0)='./file1/file0\x00', &(0x7f0000000240)='x\x00')
lstat(&(0x7f0000000000)='./file1\x00', 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000040)=ANY=[@ANYBLOB])
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000320000/0x1000)=nil, 0x1000, 0x0)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
madvise(&(0x7f0000454000/0x1000)=nil, 0x1000, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000280)='9*', 0x2, 0x401, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
sendto$inet(r2, &(0x7f00000000c0)='\x00', 0x1, 0x0, 0x0, 0x0)
linkat(0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0)
pwritev(0xffffffffffffffff, 0x0, 0x0, 0x0)
socket(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r2, &(0x7f00000012c0)=""/4060, 0xfdc, 0x42, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
shutdown(r0, 0x1)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x4}, {0x61}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000080)={@local, @broadcast, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @loopback, @broadcast, @multicast1}}}})
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x6}, {0x3d}, {0x6, 0x0, 0x0, 0x4000}]})
write(r0, &(0x7f00000002c0)="bfa80748c4b3ff8918915dc05e7e", 0xe)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x3a, 0x12, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000000)=ANY=[@ANYBLOB="fb182e28666902e3ff010da2227002d1a95f93e11523de7f036d"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x18, 0x8002, 0x0)
r1 = socket$inet6(0x18, 0x1, 0x0)
r2 = dup2(r1, r1)
dup2(r0, r2)
sendmsg$unix(r2, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
clock_gettime(0x2, 0xfffffffffffffffe)
ioctl$WSDISPLAYIO_WSMOUSED(0xffffffffffffffff, 0x80185758, &(0x7f0000000000)={0x0, 0x0, {0x2}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x33}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000100)={0x4, 0x18, 0x32}, 0x8, 0x0, 0x0, 0x0, 0x0)
madvise(&(0x7f0000000000/0x14000)=nil, 0x14000, 0x2)
sysctl$ddb(&(0x7f0000000080)={0x9, 0x2}, 0x2, &(0x7f0000000180)="1ca1e8e9", &(0x7f00000000c0)=0x4, &(0x7f0000001180), 0x4)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat(0xffffffffffffff9c, &(0x7f0000000100)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/\x00', r0, &(0x7f0000d06ff8)='./file0\x00')
chroot(&(0x7f0000000080)='./file0\x00')
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
recvmsg(r1, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000400)=""/201, 0xc9}, 0x0)
sendmsg$unix(r2, &(0x7f0000000340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000240)=ANY=[@ANYBLOB="28000000ffff00000100000008cd7f8603"], 0x28}, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSCTTY(r0, 0x20007461)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000680)=[{&(0x7f0000000800)="1cef7169a51e177fcb3d9126cdfcdde865488b104ba6c668297006725191953df365cfb46c817b0a39ac25d26136088860cdd18a6787e11ed1693bb0e8f935c3ac2ee35e21fe6ae3e48bba98c1cdbb1ba783aa51aa0a8f419f17cfac9699b93c9f5be6a8fa55cf782dac34f795d1ca36d6", 0x71}], 0x1)
pwritev(r0, &(0x7f0000000500)=[{&(0x7f0000000200)="584c5ec943e1148822a3734b508f204ab6", 0x11}, {&(0x7f0000000180)="5e4bb1c9f650ec29f0c438a0230bc140a174a40f6814cbcecaed31316553eb971391b75d9e7751eeed4ec0757a7f657d8662bbf883910e8d66b5e5f95adaffcccac6db27", 0x44}, {&(0x7f00000002c0)="0d3e3c3ebd81e00ebae06f5205f5e514a5ebc08fe8c60f75ff252e", 0x1b}], 0x3, 0x0)
writev(r0, &(0x7f0000000140)=[{&(0x7f0000000080)="b7bc923e77030c433daf280c2c27934137b9e366b725cbe81384bc31e66a3426bd8bc24242c4722cee8d05e1a94036643f49d8c3feadd0c94d81715d2794e817532abcc6ecb10de74716618bd63a2c84132e133cd456787019d163a82102b126b4309fd36631eb0c32bdcc813e6e2e", 0x6f}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
ioctl$WSMOUSEIO_SRES(r0, 0x80045721, &(0x7f0000000080))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="00ffff40", 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x2}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x66, &(0x7f0000000380)={@random="f03640e81b95", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "b4f230", 0x30, 0x0, 0x0, @mcast1, @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "66b256", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f00000002c0)={0x5, 0x0, 0x0, {[0x0, 0x0, 0x0, 0xfffffffffffffffd]}})
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xc, 0x0, 0x0)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000040), 0x0, &(0x7f00000000c0), 0x0)
mprotect(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0xc, 0x408})
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4e)
setsockopt(r0, 0x0, 0x67, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x6c}, {0x81}, {0x4000006, 0x0, 0x0, 0x80}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
select(0x3, &(0x7f0000000040)={0x190}, &(0x7f0000000080)={0x9}, 0x0, 0x0)
kqueue()
socket$unix(0x1, 0x5, 0x0)
openat$vnd(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
kqueue()
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000300), 0x0, 0x0)
pipe2(&(0x7f00000002c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
select(0x40, &(0x7f0000000080), &(0x7f0000000200)={0x415bfb14}, 0x0, 0x0)
dup2(r1, r0)
pipe2(&(0x7f00000002c0), 0x0)
select(0x40, &(0x7f0000000000), &(0x7f0000000040)={0xfff}, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x74}, {0x7c}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="5716115feb1bac1c604f32cfe6f2", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x801169ac, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt(r0, 0x0, 0xc, 0x0, 0x0)
chown(&(0x7f0000000000)='./file0\x00', 0xffffffffffffffff, 0xffffffffffffffff)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
mquery(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
r0 = semget(0x3, 0x1, 0x10)
semop(r0, &(0x7f0000000000)=[{0x6, 0x7, 0x2800}, {0x1, 0x4, 0x1000}], 0x2)
socket(0x2, 0x4000, 0x4)
r1 = semget$private(0x0, 0x4000000009, 0x82)
semop(r0, &(0x7f00000002c0), 0x0)
semop(r1, &(0x7f0000000540)=[{0x3, 0x3f}, {0x3, 0xfffc, 0x1800}, {0x4, 0x960b, 0x1800}, {0x7, 0x7, 0x800}, {0x1, 0x3f, 0x800}], 0x5)
semop(r1, &(0x7f0000000740)=[{0x3, 0x9, 0x800}, {0x2, 0x6, 0x1800}, {0x4, 0x68}, {0x0, 0x3, 0x1000}, {0x2, 0x2}], 0x5)
rename(&(0x7f0000000500)='./file0/file0\x00', 0x0)
open(&(0x7f0000000380)='./bus\x00', 0x0, 0x20)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000000000/0x4000)=nil, &(0x7f0000000000/0x1000)=nil}, {&(0x7f0000ff7000/0x2000)=nil, &(0x7f0000008000/0x1000)=nil}, {&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000003000/0x4000)=nil}, {&(0x7f0000ff6000/0x3000)=nil, &(0x7f0000ffe000/0x1000)=nil, 0x40}, {&(0x7f0000000000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffb000/0x2000)=nil, 0x104}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000000000/0x3000)=nil, 0x1}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000002000/0x1000)=nil}, {&(0x7f0000001000/0x4000)=nil, &(0x7f0000006000/0x2000)=nil, 0x6}, {&(0x7f0000008000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000001000/0x4000)=nil, &(0x7f0000000000/0x3000)=nil}, {&(0x7f0000004000/0x1000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000001000/0x1000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000ff2000/0xb000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil, 0x4000}], ['./file0\x00', './file\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0/file0\x00', ['./file', './file', './file', './file'], 0xfffffffd})
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000640)=""/248)
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
semop(r1, &(0x7f0000000380)=[{0x3, 0x2, 0x1400}, {0x4, 0x7, 0x1000}, {0x0, 0x81}, {0x2, 0x21ff, 0x1800}, {0x3, 0x2e5, 0x1000}, {0x0, 0x7d12, 0x1800}, {0x0, 0x7, 0x1000}], 0x7)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000580)=[0x3d3, 0x0, 0x0, 0x5, 0x8])
r2 = semget$private(0x0, 0x2, 0x390)
semctl$SETALL(r2, 0x0, 0x9, &(0x7f0000000600)=[0x100, 0xff, 0x0, 0x3])
socket(0x10, 0x3, 0x5)
mkdir(&(0x7f00000005c0)='./file0\x00', 0x12d)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x100000000100}]}})
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x32)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x200, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x0, 0x2011, r0, 0x0)
mmap(&(0x7f0000002000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000003000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000000040), 0xfeea)
munmap(&(0x7f0000000000/0x8000)=nil, 0x8000)
syz_emit_ethernet(0x36, &(0x7f0000000000)={@random="d7499279dd22", @local, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @empty, {[@timestamp={0x44, 0xc, 0x5, 0x3, 0x0, [{[@local={0xac, 0x14, 0x0}]}]}]}}, @icmp=@echo}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
sysctl$net_inet_ip(&(0x7f0000000000)={0x6, 0xb, 0x0, 0xa}, 0x5, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2040, 0x4f4b)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0xc2c04200, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "04713de0af28a2813d8209b8d9f39321849e3c99"})
r0 = msgget$private(0x0, 0x2)
msgsnd(r0, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2cc63a9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r0, &(0x7f0000000500)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3501000000341b1f80b87cb6f04b1aa1995045af2238befbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d6803d617130b6a00"], 0x6d, 0x0)
msgrcv(r0, &(0x7f0000000280)=ANY=[@ANYBLOB="00000000000000000000000400000000000000000000000000000000000700000000"], 0x22, 0x0, 0x1800)
msgrcv(r0, &(0x7f0000000200)={0x0, ""/233}, 0xf1, 0x3, 0x1000)
sysctl$vfs_nfs(&(0x7f0000000040)={0xa, 0x2, 0x2}, 0x3, 0x0, 0x0, &(0x7f00000010c0), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0014462, &(0x7f0000000080))
sysctl$net_inet_tcp(&(0x7f0000000200)={0x4, 0x2, 0x6, 0x19}, 0x4, 0x0, 0x0, &(0x7f0000001280), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x61}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000040)=[{}, {0xffff}]})
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80146940, &(0x7f0000000300))
mlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
mprotect(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000180), 0x8, 0x0, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x5}, 0x2, &(0x7f0000000040)="f19dd68f", &(0x7f00000000c0)=0x4, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x4d}, {0x4}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000040)=ANY=[@ANYBLOB="8faaaaaaaaaaaaaaaaaaaaaa86dd6004000000082b00fe804dc25ecf93ce1ef45ede2ec73e2b00000000000000000000000000aa"])
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
msync(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x6)
mlock(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
syz_emit_ethernet(0x4e, &(0x7f0000000040)={@random="1b329ff120e0", @random="c1826faf9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x18, 0x0, 0x0, @empty, @mcast2, {[@hopopts={0x2b, 0x1, '\x00', [@enc_lim, @jumbo={0xc2, 0x4, 0x33}, @generic={0x0, 0x1, "92"}]}]}}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000001400), 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205602, &(0x7f00000016c0)={0x1, 0x0, 0x0, 0x0, 0x0})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0104454, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x54}, {}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x45}, {0x5}, {0x6}]})
syz_emit_ethernet(0x1c, &(0x7f0000000000)={@broadcast, @empty, [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @rand_addr, @remote, @remote={0xac, 0x14, 0x0}}}}})
ioctl$TIOCOUTQ(0xffffffffffffff9c, 0x40047473, &(0x7f0000000000)=0x80000001)
ioctl$TIOCGFLAGS(0xffffffffffffffff, 0x4004745d, &(0x7f0000000040))
r0 = geteuid()
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000080)={<r1=>0x0, <r2=>0x0, <r3=>0x0}, &(0x7f00000000c0)=0xc)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000100)={0x0, <r4=>0x0, <r5=>0x0}, 0xc)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000140)={<r6=>0x0, 0x0, <r7=>0x0}, 0xc)
shmctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000180)={{0x3, r0, r3, r4, 0x0, 0x81, 0x9}, 0x7fffffff, 0x200, r6, 0x0, 0x7, 0x1, 0x9})
r8 = msgget$private(0x0, 0x4)
r9 = getpgrp()
msgctl$IPC_SET(r8, 0x1, &(0x7f0000000200)={{0x8000, 0x0, r5, r2, r7, 0x18c6eb496c9f6dde}, 0x7fff, 0x3fb, r1, r9, 0xffffffffffffff80, 0xffffffffffffff4d, 0x1, 0xfffffff800000000})
r10 = geteuid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000280)={{0x5, r0, r3, r10, r5, 0x83, 0x800}, 0x6, 0x2, 0x27})
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={<r11=>0x0}, &(0x7f0000000340)=0xc)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r12=>0x0}, 0xc)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f00000003c0)={r11, 0x0, r12}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000400), &(0x7f0000000440)=0xc)
ioctl$BIOCLOCK(0xffffffffffffffff, 0x20004276)
r13 = msgget$private(0x0, 0x400)
msgsnd(r13, &(0x7f0000000480)={0x2, "4f915c0f949687d3a1c48311d5ed997de1800a66d353629a9913668fbe35d01a673b27ca5a4d693586e622a6c8bc1da733a4352e4b1c567c9e402fef189b8318782bad90192bf9b8dfafb28fcd3d3634721dd809ca1e28c6797e"}, 0x62, 0x0)
getpeername(0xffffffffffffffff, &(0x7f0000001540)=@in, &(0x7f0000001580)=0xc)
clock_settime(0x100000000000000, &(0x7f0000000040)={0x8000000000000004})
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
sysctl$net_inet_tcp(&(0x7f00000000c0)={0x4, 0x2, 0x6, 0x12}, 0x4, 0x0, 0x0, &(0x7f0000001140), 0x4)
sysctl$vm_swapencrypt(0x0, 0x0, &(0x7f0000000280)="c9947b2e770394db012d665c237613b8fa65f823f1e7be5b76b2c6ac1562983d49f6d5034404425ebc525fa77c9489cc834652ac5c209ee62469f4a2b04e20849caa5ead93baf8c0069fc72da88b87f45171e859ad370efe02c9bd305db32859fb5f32f2371245d77e49a01408df74ef", 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000140)=[{}, {}, {}, {}, {}, {}, {0x0, 0x0, 0x1000}], 0x7)
r0 = kqueue()
kevent(r0, &(0x7f0000000140), 0x100, &(0x7f0000000300), 0x1ae, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x2007, 0xffffffad, "919000"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa1fabe934a1c1e400db556028db783e9f47ff98028e5eb8cbf5dc889b5ac6402a846918dc0e5097d2395a46fb114a16ea036db91fbb61a83ecd8f2c2ea7f4933fa25982129ace7b16a56cb12b1f238d8ba33c9856a0f0809394b8e5c242c2d9d5194e43f3bb1e6f81d5b8e7c3d5aa1e11f0d210cab776b5b7d1fdbcf1330114561137118a573d4e85cf86cc5488b8a844109050b475210e7df7a0d5f3f93f92cb55920c62fe88de5e006c16ccfb3bd743ce316a8baafe6acc43857db894d285d89375a807129d682358f0b93e83ae31f06d1d27b44c1d9476bdff4677c66fa24955a4273eb6bd918c5580730cfc1bf8520737cc0fb8814d963f3de72f8fdf9aba5a9feb1005c08d7b590f2fa75fa602967dd07b4603595e3b38efef0f26d4b2e943e1871b63479d4fe37c597e3fff39addea8b9ad4c7fe00d24d2adba9186a541bd69baa8d7062bd1f9b8fbac2f1fa00170c766592ed3e01912b24758ed0041f441bb7d3737ba080226c380ad7e766eba96399180b18f59bb475dfd1be417c50c0ac7934a4f55b9080ba4661680030f428eafa06dea84f1afcf60735f0cf7ce0939bda5433c36beddbe9b6e65e3a2f4216a530b24c41ca9745311beaacdfcd8c0c5d9d1d85f0cbb68c77ef1626a44bd209acd8f700a7adeecf94a5d30e3c8ae9c0134aa682b96275816b962bb390789b3253ec11c5485ecfd0b3387fb1cde62c22d6707093c53ac902e5cc0e4f363cc3dd810820064528765f21a8f928c85749fca1768571c685fbf3b99eb65eefdff1a232d95a3be6903584a45de52922972d345a12f0feafafd0fad3705c36ed4472b0637dab1df4f4c30e5fe2eaef09bc9c0ad59fb081a13d4bc6e72db22d12ba088f3d9bd0c96ef85fddeaf5eda74bea0f323ae15a0d1496293158d590f65fa01c87ac3e64facf894879737d20d0b1343b19b11c23a3c669b961b808c928399837145d2144634bcc95296a35db128bd780d6fad47c642339d7e2d3bd49455ec0dc893d6a3884e15c48c65837024637f9acf57ab57e6ad5900ae73c69ee6e88b0dbe2d51e81f8a39141ff8ecad5cfbccd3df4532a0c0a433e3ed6a22ff8236f42230f498f3b208a61fef15ac1455b6783b60b28017959dcd75df55ab591240be76e2bdf1cbb575b0df1cbf8210b2d4904755bf4a6c4b604ff558d9be52db6e8a7b790a11b1e89138913d4c946695a2088ea2cbf148e0716dcba54014a58e54f3314691289ffe61784e0cab5e8202f78414e36a7bf52153333f697a382d9ddaae6c7f9bdc5c611fb2568665c247ea950b3cbf32356bbd55d7c98d4093e4012cab15b378026af10e1c7fad4963d97993c48dc42dc30a4b013dbef0161651dc2797770e47002ef8569cefe5dc2a56b9ab1af917902620f3de081dcf31cb9af02772c1297ff819416b9f08cf0ee625abdeb795477cd29524554816d3bb675290ffefed3e33515665dc4da736fe29cccd7e0fb4e70ac57052bec30fe76d070e7f997499d62d42f0220f7cba6f29b3db3ddbee5b7816675af64b9d08f6d6213e49ba5e436c938067c1d04c4d018b60b96eb9d14ca9e1121aed3b3dc9c0990d7859f96f4667b24ef1ac32f8d2bdc0cfa73114c992fc5efa80bc67c0378aa596c370fff69f3811ff1eb38a578c8aaf598abfc74c81b120cbeb475c1dc103bd6b86b5bedd3742f70de64b0da208ae8c6f42d49df66cc30c740b8404e2d9ee202486aaa5334d819eeacfa03b873b9d9f74f0c69bc78b9abd5ca1ff42b82f0572353abcb3ade373db7676cdeb728fb40603d9a5046a245d3a413356a8063524509a79a9456128da1a9019cae11f9bac7718187b4d77fd5cec7a1798987accd471d3be22b98cd847bdad4ebf67824f746bde2b72fe3443f18185289e7b0f2a2d35c89ac4c8e8ad88c9ff86e43060bc00e2837e39158d3e899e34400bf4b33ab8248be2f062db014041d4c422945b2ddf6dae632fd235cff8e7cae38f7aa8d828681ae1e8f08aab4fed29aff66d46ddf60cb5499701464b5e837d4a7e0280af65480134f84a42c68bc2d2b37c7fed5b0d97240029be4d8002333fed58b8d84748c331fd96c7ce3dfdecac736cfc52ee844ca2267d2eba16b7fc930b23d47bf0b5d5cc12ca740747277719bdc0b1983e95c8ba1fcfc7ef6c15a94734cca4b400d048eb0505511a1d9d60a433417327b47f8729140c6dd326559d24db1a079f0fb1c3c8f53f12c586445d1a736d35885fa8ffc88c693cb2a3686edea02cc88451814005ce52e5c05ec2e99bc7943d83df39145b74797b5d30295b5f24dc2d4633776735147d2902cb71daf5991d599cea38e2f15861ff17c30c06b22683e5a3439be67c2dfca36b0bfd3bd8ecf30b341702db4457bcd74b02c2b74820fb93753db3184bff03f645b3dfdeea195400000000000070eec997eca79eddbfefba48adf41b107efa9804b4325125ec20518b9508186a63115f5acb9bf76c68da2d34dd941b0f132674cdf228b71a46967a60069f0e608cd917f5b968f2636019de739bfc014e6fdeadfe3263229092426ead638c3f8ac0a2fcbedd2a116b2937244c15d4a40dd3b940ba54c4406dc517a1433caf26b38f90079e6043a1fe1ab526658dd1de39853f161f267fdfe4ab8b9f5739588419abd2bd0a1d99041cf16f9a73a82fd48d04b9daa4384bb9742905fcc9f3ab9080b062f5275eb8ebf28e329725715c0ba01312edd85f81b954728e053518ae7911ee2c31e75c8d836377670336624ccb760820a584d3ccc13e2d505b507620379784149034ce52141ff633cf0508a7c7a9f2d2cacbd82e7c62ff544be7281aa27e40512f68fd9cfc35b466c829b3fe22f953539c0baab9c4a763d5210a2be94a64a7146dbf2c76a5029949172ac64fa14359d71cdd817c3aa6e0e0073981332102772e2c2d0165a30fe94b1ab5f526be3c21877139bcc9d685d7268a13d06177cb8b0273577250584c90999392f8127c387e03984fc5d75c5fbfcc4d64fa568bfe094c2f8334d0c614b050409f2782448a1ffdeddba77e3e375e64974ff22f58", 0xcf2}, {&(0x7f0000000140)="1ef646320e8298bfffa8b2a1bbfb4ebdb58a48f337ca9e47561be058fd4e0d4b4c76ea24861e96323ae4c7eac08c3708a683ab257cd184c9d35c4953613628ccbbeb6cfff60f99b159e6117f1919e7316d9f7cf685", 0x55}], 0x3)
writev(r0, &(0x7f0000001700)=[{&(0x7f0000000440)="9a8fab2d8eb4a6ebdbd69775a443f51e35e2574e6291c1ff9732f9c18bceecbb4581154806307f3125f36c82a5d7ac0046ac9e2d55a4a97bb982134e50fd90d4038f94584eb617374dc40beaa963af0225b25d93dbf34d21cf8903414ca064fea5969aaa4c53fcb65b89b8e3f2ea6a3b9e3bcf5a56a8231d617050aec12c2a2ee4cc22957b4a80b7a24ec131fdb98d274f3d10d206f89430f4ada7045668f0e2d5f5565f0f346da98f886742c16d1203fb8c1d2c4abb2137148dbd8bc5dd1ec4365519b544cf39f6a5c4d600aebf18a18439c10aed2edc6c6acde29135aa468d6df763ad6753159c1c5a0c3ce3eea5dc07524ef1a0d7bfae4da0eb741194cd7694415557a7c82752fb76a497bc03f49a1d8efabd43135cd8a39bea327632760efed68b77c834bc70cac69fdd46d40314f6300969f9b552b0e313bb49a8a872c7b053b60d1f2df28c16a9981fca6155ed172c774d4647e996bd0bfbf384ddb84d005269ae1b9b583b5a", 0x169}], 0x1)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000240)="e3021d932704150e8dcadc3746e9ec1913fe510a15db410430a9145c5bc80bd0159a93b809c49f858152f439ba5d8141a9af01a1dca7e5e6b682fac7df93a5cfb2da0a794a378532dc42b5cec061f8b1b57de421b2914c197fda1578c2cf75ceef9bb2478865d1b2ff0135326d6c6967126e470b139a9d39cbba5f3002811ee288cc71d4afce2ca73b17cf2239f5da4447df12e67a7c017adcd271180e27f1b4df114b94be7cab08e50dd8a059d48ae6b887130967f791b9b5b57934a2a8cf076b403e20dc138e946f2e57d8ea74e79f337f", 0xd2}, {&(0x7f0000000080)="e89e00a77b9f491a19ffa382a103abf98bc714971db796497bd88138f30ee39ae93434fedde9696c463583a41394c67354f21a22", 0x34}], 0x2)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0xf303, "000000000000000100002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000180)={&(0x7f0000000000)=[{0x4}], 0x1})
syz_emit_ethernet(0x4a, &(0x7f0000000080)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "6cf65b", 0x14, 0x0, 0x0, @loopback, @empty, {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
r1 = dup2(r0, r0)
setsockopt(r1, 0x29, 0xb, &(0x7f00000000c0), 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x24, &(0x7f0000000040), 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt(r0, 0x6, 0x1, &(0x7f0000000140)="6768b6dc", 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000), 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f0000000040)="d26a0000000a00", 0xfe26, 0x401, 0x0, 0xfffffffffffffe00)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="2902657f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
accept$inet6(r1, &(0x7f00000000c0), &(0x7f0000000100)=0xc)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f0000000080)='P', 0x1, 0x401, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x2d}, {0x60}, {0x6, 0x0, 0x0, 0x447f}]})
write(r0, &(0x7f0000000440)="3c9ed0f773672ee31265ac52c02e", 0xe)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
setuid(0xffffffffffffffff)
ioctl$TIOCSFLAGS(r0, 0x8004745c, &(0x7f0000000000))
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = fcntl$dupfd(r0, 0x3, 0xffffffffffffffff)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
r3 = fcntl$dupfd(r1, 0x0, r1)
write(r3, &(0x7f0000000100), 0xfffffe5d)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x1021, &(0x7f0000000040)={0x5}, 0x8)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000000), 0x4)
r0 = socket(0x400000000018, 0x3, 0x800000000000003a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x67, &(0x7f0000000000)={0x2421}, 0xc)
kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f00000000c0)=[{}, {0x9}], 0x2})
r0 = socket(0x2, 0x3, 0x0)
setsockopt(r0, 0x0, 0x1f, &(0x7f0000000000), 0x0)
mprotect(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x1)
r0 = socket(0x18, 0x2, 0x0)
select(0xf, &(0x7f0000000000)={0x8}, &(0x7f00000010c0), 0x0, 0x0)
mprotect(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2)
shutdown(r0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xf0, 0x0, 0x0, 0x0, 0x0, 0xffdf], [], [], [], [{}, {0x1}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000080)={0x6, 0x16}, 0x2, 0x0, 0x0, &(0x7f0000000200), 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x64, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2005, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xfffffffe, 0x0, "090000000000000fb3040000001000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0xfffffffb, 0x0, 0xfffffffffffffff8, 0xfffffffa, "0901a6fa597b2e005da24700001b000000001c00"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000001080)="dc7b", 0x2}], 0x1)
sysctl$vm_swapencrypt(&(0x7f0000000100), 0x3, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
poll(&(0x7f0000000200)=[{r1, 0x1}], 0x1, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x9, 0x0, "25a6c6d17013766d4f76c774e1c3ff94d19b5cca"})
writev(r0, &(0x7f0000001800)=[{&(0x7f0000000080)="fb4e", 0x2}], 0x1)
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100, "745541562d94962f9a0f8280acac41162e7192bf"})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80047476, &(0x7f0000000040)={0x0})
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf81fc7b457abe24105f76e85dc0ced443"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "a05d1ead3e531ef43b59ff65be755bc2ca0c93c5"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)='x', 0xfffffefb}], 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "7bf6f5bdf56bd1ffb989e5b9268aa74eba8ac5f6"})
syz_open_pts()
syz_open_pts()
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
poll(&(0x7f00000006c0)=[{r0}, {r0}], 0x2, 0x0)
close(r2)
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x450a)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
read(r0, &(0x7f00000001c0)=""/84, 0x54)
read(r0, &(0x7f0000000480)=""/4096, 0x1000)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000100))
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x4d}, {0x54}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106978, &(0x7f0000000080))
sysctl$net_inet_gre(&(0x7f0000000040), 0x8, 0x0, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x17}, 0x2, 0x0, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000003fc0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000bc0)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB='\x00@\x00\x00'], 0x80}, 0x0)
renameat(0xffffffffffffffff, &(0x7f0000000600)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0xffffffffffffffff, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
open(&(0x7f0000000000)='./file0\x00', 0x60a, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
fcntl$lock(r1, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1000200010005})
sysctl$net_inet_tcp(&(0x7f0000000040)={0x7, 0x2, 0x2, 0xa0000204}, 0x4, 0x0, 0x0, 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x40000400000002c1, 0x0)
ktrace(&(0x7f0000000200)='./file0\x00', 0x4, 0x1334, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff})
readv(r0, &(0x7f0000119ff0)=[{0x0}], 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd", 0x1a4}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51f4b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2af76824b990a8ce316d0e8cdd2b7420511bf6e2204f87eefbae7e9aa56103ddce0203eeed44da7b3c5a3a072ec8b1312e5c28319500820e262b6d4011a4b473b4d7694f462e7fa3aa201ddb7eab4af9a44166f245fbd59802445ca018b2032", 0xe0}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
symlink(&(0x7f0000000100)='./file0/file2\x00', &(0x7f0000000140)='./file0/file0/../file0\x00')
rmdir(&(0x7f0000000080)='./file1\x00')
open$dir(&(0x7f00000001c0)='./file0/file0/../file0/file0\x00', 0x10601, 0x0)
mkdir(&(0x7f0000000200)='./file0/file0/fi\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSKBDIO_GETDEFAULTKEYREPEAT(r0, 0x400c570a, &(0x7f0000000080))
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "f540b2ea0000000096b8911f121b3bd89a53e1bf"})
sysctl$net_inet_etherip(&(0x7f0000000080)={0x7, 0x2, 0x2, 0xa0000000}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x7}, {0x1c}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$VMM_IOC_INTR(r0, 0xc0104419, &(0x7f0000000140)={0x0, 0x40d})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000300)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x1000000})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0xfffffc8a}], 0x1, 0x0}, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000001a80)={0x0, 0x0, &(0x7f00000006c0)=[{&(0x7f0000000280)="ce46135fc72238eb2e1c85dba321fd55050f35f2a5214018a34ba9bb128cdaf7e8e4d10b871b7f", 0x27}], 0x1, 0x0, 0x1308}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000100))
r0 = socket(0x10000000011, 0x8000000003, 0x0)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280))
setsockopt$sock_int(r0, 0xffff, 0x2000, &(0x7f00000000c0)=0x2, 0x4)
sendto$unix(r0, &(0x7f0000001380)="60cafaabc00bbc6ff36232f9ad8c92f70491f57ddfd7c64a987cca05f283e2f29b0678c06277ff2ab267118d726599c3ea3c651c7050793766a95d2dbb0e1a67c207c0b0101e1810e225f36ec28b6366a37e9b30c68d4b681ce6650254a94ea854d86704c252dfe88e4b68bc617edebeff6de6ff86d2a3528a54798202e9a757d6587ce64a9d3e777cb77be11ea8fd5ef9", 0x91, 0x0, 0x0, 0x0)
write(r0, &(0x7f0000000100)="78f4752e3b11a2ad4b1c67fa438b5dc9dd4ab6fddfbf5e444de0581f3a0dabf3198beb122d39a9e989585af686650ecebe6611241456618137c517cc0ce6d25f00996188e326425ca4895ff9a18579923ca3d91c4d768ee3bd7ea4cd3b20e64d001d54bed2cc6839b6b2cf714d1f7ed08b085d0fe1afdca8bead7db1b5f7017bcc38904db24997ed1a2eb4e03a60775b4f", 0x91)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x20, "cf11050038050000000000000000008000000800"})
ioctl$WSKBDIO_SETMAP(0xffffffffffffffff, 0x8010570e, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x7c}, {0x3d}, {0x6}]})
syz_emit_ethernet(0x22, &(0x7f0000000040)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @multicast1}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x34, 0x0, 0x0, 0xe1b2}, {0x25}, {0x6, 0x0, 0x0, 0x10000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000400)=[{0x3}, {0x15}, {0x6, 0x0, 0x0, 0x88001}]})
pwrite(r0, &(0x7f0000000000)="dd3600ff7f00843d3d14cc87cabb", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
ioctl$TIOCFLUSH(r2, 0x802069a8, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000d80)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x4, &(0x7f00000000c0)=[{0x10001, 0x0, 0x0, 0x4}, {0x3c}, {0x15}, {0x4406}]})
syz_emit_ethernet(0x4a, &(0x7f0000000000)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "a80300", 0x14, 0x0, 0x0, @rand_addr="800000400000000000000500", @rand_addr="000000e21400dd00", {[], @tcp={{0x3, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
pipe(0x0)
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
mprotect(&(0x7f0000cdd000/0x2000)=nil, 0x2000, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x1d}, {0x6c}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
preadv(0xffffffffffffffff, &(0x7f00000004c0)=[{&(0x7f0000001640)=""/247, 0xffffffcc}], 0x10000221, 0x0)
r0 = open(&(0x7f00000002c0)='./file0\x00', 0x611, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0xb36000)=nil, 0xb36000, 0x0, 0x800000000009031, 0xffffffffffffffff, 0x0)
write(r0, &(0x7f0000000000)="ed", 0x1)
mkdir(&(0x7f0000000380)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x0)
chroot(&(0x7f0000000480)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000240)='r\x00')
unveil(&(0x7f0000000280)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000100)='r\x00')
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000280)=0x507)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xfffffffe, 0x1fc80d88, "24180400000800008000"})
writev(r1, &(0x7f0000001440)=[{&(0x7f00000001c0)="a66c2dcc193a8f951931f490fbce15d50243dc6a995fad24", 0x18}], 0x1)
ioctl$TIOCSETAW(0xffffffffffffff9c, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x5, 0x0, "23e7c8204832406f637b9a9f11c632de6aaac396"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$hw(&(0x7f0000000e80)={0x6, 0x19}, 0x2, &(0x7f0000000ec0)="95cc5cf0", &(0x7f0000000f00)=0x4, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f00000004c0)={0x3, 0x8, 0x2, {[0x4, 0x7, 0xfffffffffffff801, 0x1, 0x7, 0x0, 0xc3b, 0x400, 0xfff, 0x7fffffffffffffff, 0x3c2c0, 0x10000, 0x3, 0x0, 0x0, 0x8, 0xd82a, 0x5], [0x7dcc, 0xffffffff, 0x80, 0xffffffffffffe6ad, 0x46, 0x199, 0x1, 0x0, 0x4, 0xfc1], [0x4, 0xffffffffffffffff, 0x3d5, 0x9, 0x7, 0x8, 0xfffffffffffffe00], [0x3, 0x4, 0xc3, 0x10001, 0x8, 0xffffffff], [{0x8001, 0x7fff, 0x70f, 0xfffffffffffffffd}, {0x8, 0xfffffff7, 0x0, 0xddd}, {0x8, 0x8, 0x8000, 0x6}, {0x5, 0x5, 0x4, 0x6}, {0x3, 0x4, 0xfffffffd, 0x3}, {0x8, 0xffff0000, 0x75dc, 0x8080000000000000}, {0x18f4, 0x73a5, 0x7f, 0x9}, {0x5, 0x4, 0x2, 0xcaf}], {0x1, 0x800, 0x3, 0x9}, {0x7027, 0x5, 0xfff, 0xffffffffffff0001}}})
r2 = openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x20, 0xac)
ioctl$VMM_IOC_WRITEREGS(r2, 0x82485608, &(0x7f0000000080)={0x5, 0x4, 0x3, {[0x0, 0x7fffffff, 0x9, 0x800, 0x3, 0x100000000, 0x1, 0xe7b, 0x5, 0x8, 0x10001, 0x5, 0xffffffffffffc3ee, 0xffffffff, 0x1, 0x3, 0x6, 0x7], [0x6, 0x7f, 0x0, 0x10000, 0x4, 0x4, 0x1, 0x5, 0x4, 0x1], [0x3, 0x1, 0x1, 0x824e, 0xbb2e, 0x6, 0x4], [0x1, 0x0, 0xffff, 0x7, 0x2, 0xf8], [{0x3c9d, 0x1, 0x1, 0x1}, {0x3ff, 0x10001, 0x6, 0x101}, {0x9, 0x3, 0x99e7, 0x1}, {0x670, 0x60e, 0x6, 0x9}, {0x1, 0x80000000, 0x80, 0x8}, {0x6, 0x2, 0x1000, 0x3ff}, {0x2, 0x5, 0x6, 0x8000000000000001}, {0x6a23, 0x9, 0xfff, 0x5}], {0x20, 0xfffffffa, 0x0, 0x5}, {0xf60, 0x0, 0x2, 0x9}}})
dup2(r0, r1)
r3 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000300), 0x4, 0x0)
preadv(r3, &(0x7f0000000400)=[{&(0x7f0000000340)=""/174, 0xae}], 0x1, 0x8)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000701000000000000ceb1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257690000132e27acb5d602000d7d026ba8af63ff37422902e4fdefe095bebd108ae070c1f5ab72c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002003c88c1cf8044101b00fcffffffffff", 0xa1, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup2(r0, r0)
sendmsg$unix(r1, &(0x7f00000007c0)={&(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa, &(0x7f00000006c0)=[{0x0}, {0x0}, {&(0x7f0000000240)='S', 0x23c0}], 0x3, &(0x7f0000000780)=[@cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, @cred={0x14}], 0x40}, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecfa60080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r0, r2)
r3 = dup2(r2, r1)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r4 = fcntl$dupfd(r0, 0x0, r0)
write(r4, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x120d, 0x0, 0x20008009, 0xffffff03, "1f000000007e00"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d298b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5e", 0xcd)
sysctl$net_pipex(&(0x7f0000000240)={0x4, 0x23, 0x1}, 0x3, 0x0, 0x0, &(0x7f0000000380), 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x97af, 0x0, 0x401, 0x7fffff7b, "ff070000d6c57c00"})
write(r0, &(0x7f0000000280)="4de3a767cb3572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cc3392c70fb4d2ea468e980877377f1cfb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70ac323074dd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19a3dff7bde555c03c89296384aa8f8b1e7179729ea0bb9ac633eedb29996c3da18b7af5150085f27cd1a07a7e9b60d858b2f1e5800125da2f78713e82f66341bc07c02b83799f6c872f9fdc87b82c4f4cb92ef1292f1fc9abaf5d17d7804a678fd8601aab32be210c18e48e1287828d903f336f4ea7342c526849e1dfbfba385183f622d1e4e0e1498329fb523d2bde8182cb6bb71d0c7c7cff135c6348649f6e0e61785ce8cde972e9a411d8c951c3f45f83161b98f84c13775ca1", 0x170)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001000020000300"})
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
poll(&(0x7f0000001980)=[{r0, 0x40}], 0x1, 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x200, 0x0, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x53}, 0x2, &(0x7f0000000600), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x20}, {0x45}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
munmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000)
mprotect(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
mprotect(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x1)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0x3f, 0x0, 0x0)
munmap(&(0x7f0000fff000/0x1000)=nil, 0x1000)
msync(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x2, &(0x7f0000000100)=[{0x28, 0x0, 0x0, 0x400}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r0, &(0x7f0000d06ff8)='./file0\x00')
chroot(&(0x7f0000000000)='./file0\x00')
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = kqueue()
r4 = fcntl$dupfd(r3, 0x2, 0xffffffffffffffff)
close(r4)
openat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x0, 0x0)
recvmsg(r1, &(0x7f0000000700)={0x0, 0xffffffffffffff36, 0x0, 0x0, &(0x7f0000000400)=""/210, 0xffffffffffffff2b}, 0x0)
sendmsg$unix(r2, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
sysctl$fs(&(0x7f0000000040)={0x3, 0x1}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000300)=[{&(0x7f0000000040)=""/228, 0xe4}], 0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0))
execve(0x0, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050460000000000008000701000000000000ceb1fea7fef96ecfc73fd3357ae26caa0416fa4f376336adf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257690000132e27acb5d602000d7d026ba8af63ff37422902e4fdefe095bebd108ae070c1f5ab72c881ff7cc53c894303b22f0100404f36a00f90006ee01be657aea8c500000002003c88c1cf8044101bfcfffffef0ffffff00"/177, 0xb1, 0x0, 0x0, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, 0xfffffffffffffffe)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{}, {0x7c}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f0000000080)="23295428e2fa906bdf4ba040352d", 0xe)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000900)="6fe51238a77ce3d9274256063d40e475a7b18f080c3331a48a36f787b2a45f7cb1f05367c3665da25a71108f93d2f88625080f1bad33e6bf7b664036104d6b2d70c8410d46e0f9ef2a3e8fcd15d2a074501c0ebed92f988440d63f0c7f04b711dd936912b4fc21c424a6238108f4a6c1a8c896ffea77d566699b217d738e1d41c2e6e15841e927bfc511fba2d1ff6573db00b504efe494e6d7275327587e485a4ad40a25680b021c0ee8db992ff3d0559959144ad984531c1aeef76965dc1ed8285195a48ca29cadb73747d87c186e39b36e90ef0c6d9b", 0xd7}], 0x1)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20804000, 0x200000]}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x60}, {0x2d}, {0x6, 0x0, 0x0, 0x80}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2fb21004aa08e2d554ccf32c4", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000480)={0x0, 0x0, &(0x7f00000001c0)=[{0x0}], 0x1}, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
r3 = dup(r2)
write(r3, &(0x7f0000001380)='c', 0x1)
poll(&(0x7f0000001540)=[{r3, 0x46}], 0x1, 0x0)
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000040)=""/157}, {&(0x7f0000000380)=""/4096}, {&(0x7f0000000100)=""/190}], 0x34)
sysctl$kern(&(0x7f0000000100)={0x1, 0x15}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x5}, {0x3}, {0x6, 0x0, 0x0, 0xfffffff7}]})
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
r0 = socket(0x2, 0x2, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000080)=0x80000001)
poll(&(0x7f0000000100)=[{r0, 0x4}], 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0xfffffffffffffffd)
chdir(&(0x7f0000000040)='./file0\x00')
mkdir(&(0x7f0000000140)='./file1\x00', 0x192)
rename(&(0x7f00000002c0)='./file1\x00', &(0x7f0000000300)='./file0/file0\x00')
chmod(&(0x7f0000000100)='./file0/../file0\x00', 0x49)
rename(&(0x7f00000001c0)='./file0/../file0\x00', &(0x7f0000000200)='./file0/file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0xc}, {0x1}, {0x6, 0x0, 0x0, 0x100004}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x4}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x4, 0x0, 0x0, 0x0, 0xfffffffffffffe62)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1e6e)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000180)={&(0x7f00000000c0)='./file0\x00', 0xbfd2, 0x0})
execve(0x0, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x10002, 0x0)
fcntl$setstatus(r0, 0x4, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
socket$inet(0x2, 0x2, 0x0)
socket$inet(0x2, 0x2, 0x0)
poll(&(0x7f0000000080)=[{}, {r1, 0x147}], 0x2, 0x0)
writev(r0, &(0x7f0000000980)=[{&(0x7f0000000580)=':', 0x1}], 0x1)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0xe970, 0x20000003, "7e00000000000000000000000000000200006ba3"})
select(0x40, &(0x7f0000000040)={0xfb}, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x7, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205602, &(0x7f0000000040)={0x7fff, 0x10000, 0x3f, 0x7, &(0x7f0000000500)={{0x7f, 0x1, 0x5, 0x80, 0x40, 0x320}, {[0x2, 0x0, 0x8, 0x0, 0x3, 0x9, 0x3f, 0x4, 0x40b, 0x0, 0x3ff, 0x0, 0x0, 0x8000000000000001], [0x1, 0x7, 0x9, 0xfff, 0x6, 0x9, 0xff, 0x10001, 0x0, 0x1000], [0x8, 0x2, 0x1a16, 0x9e7f, 0x6, 0x0, 0x3], [0x4, 0x100000001, 0x806, 0x0, 0x1, 0x1ff], [{0x45, 0xfffffff7, 0xfffffffc, 0xffffffffffffffff}, {0x8, 0x0, 0xe1b9, 0xffffffff}, {0x7, 0xb23e, 0x200, 0x61}, {0x20, 0x0, 0x1, 0x40}, {0x5000, 0x800cb, 0xb8, 0x6}, {0x0, 0x1, 0xfff, 0xa51}, {0x1ff, 0xfffffffa, 0x1000003, 0x8000000000000004}, {0x3, 0x1, 0x0, 0xffffffffffffffff}], {0xff, 0x4000, 0x80, 0x6}, {0x8, 0x2, 0xfffff800, 0x4}}}, 0x3ff, 0xf8})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000180))
pwritev(r0, &(0x7f00000015c0)=[{0x0}], 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x1, &(0x7f00000000c0)=[{0x3, 0x80, 0x0, 0x3ff}]})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, 0x0)
r1 = dup2(0xffffffffffffff9c, r0)
r2 = fcntl$dupfd(0xffffffffffffffff, 0xa, r1)
symlink(&(0x7f00000003c0)='.\x00', &(0x7f0000000140)='./file0\x00')
mkdir(&(0x7f00000000c0)='./file0/file0/../../file0\x00', 0x0)
fcntl$dupfd(r1, 0xa, r2)
unveil(&(0x7f0000000000)='./file0/file0/..\x00', &(0x7f0000000040)='r\x00')
unveil(&(0x7f00000002c0)='./file0/file0/../../file0\x00', &(0x7f0000000300)='c\x00')
unveil(&(0x7f0000000280)='./file0\x00', &(0x7f0000000340)='W\x00')
ioctl$WSDISPLAYIO_LSFONT(0xffffffffffffffff, 0xc058574e, &(0x7f0000000180))
rmdir(&(0x7f0000000200)='./file0/file0/../../file0\x00')
unveil(&(0x7f0000000380)='./file0/file0/..\x00', &(0x7f0000000400)='W\x00')
r3 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
mmap(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x0, 0x10, r3, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x2}, {0x87}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{0x8da0, 0x3f, 0x49, 0xa9}, {0x8, 0x2, 0x3c, 0xf1}]})
r0 = socket(0x18, 0x1, 0x0)
getpeername(0xffffffffffffffff, &(0x7f0000000400)=@un=@file={0x0, ""/34}, &(0x7f0000000440)=0x24)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x1000)
r2 = socket(0x10000000002, 0x2, 0x0)
writev(r2, &(0x7f00000002c0)=[{&(0x7f0000000180)="4fad6f935c87e1af714248f8c13203a3dc6fcdc82c57821349285a53bb0ff2afb8dd81a7ee695f819c41fd76c235282e01871b3c62db60823ab16918d0", 0x3d}, {&(0x7f00000001c0)="87b144147f426c0f5cf9e2705bf5b4af2aadee060fb1546807b0048cf1c38503b7f1218bf5219042807a8694488aef70223f04b6dbc2a5f04968a41161a98c3789976dd636f456d586000b1b194548c3ad34bef34e308cb0e681ff0789890c4e01c3aabb52b2dd6ffc2235cd4a168758019249854c80c3a23653e2c06deee2685f534f25727323d057635eca0c3a194735e186c2", 0x94}, {&(0x7f0000000280)="69d8eebd55c3c523a592bd28", 0xc}, {&(0x7f0000000340)="a1ebf04dd1254d9a411d1428df2565470e0e083ff08c2c071b65c1a7f20776d6f14df71083b227fd3e979b099add1b5f43d23fe301a383eca88b673be477d8c6ae44f42ce85001da980de87e57a86e1fb84023a11d37d478b9c95ee4c6fc0e142c39c6f785fc0e0e5dffa78ecef6807d4a0db46f5241772fd5ef1667c19f49df085293f10e8e54cd79a3cb67e11be2f7e91c06fbef270b0ffcdeea464cb37311b91241", 0xa3}], 0x4)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000000), 0x12b)
r3 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x10000, 0x0)
ioctl$BIOCIMMEDIATE(r3, 0x80044270, &(0x7f0000000100)=0x5)
mmap(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x1, 0x8736fb0032e2f0f8, r2, 0x0)
r4 = socket(0x2000000000000002, 0x3, 0x102)
sendmsg$unix(r4, &(0x7f0000000540)={&(0x7f0000000000)=@abs={0xacf25922147002da}, 0x8, 0x0}, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x10, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x2, 0x0)
r6 = syz_open_pts()
r7 = dup(r6)
r8 = fcntl$dupfd(r5, 0xa, r7)
ioctl$TIOCFLUSH(r8, 0xc1206951, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x7)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x80}, {0x3}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x1, &(0x7f0000000040)=[{0x44}]})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
mlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x3bb0c000000, 0xffffffffffffffff})
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
flock(r0, 0x1)
r1 = dup2(0xffffffffffffffff, r0)
sendmsg(r1, &(0x7f00000046c0)={&(0x7f0000000340)=@in6={0x18, 0x0, 0x72, 0x3f}, 0xc, &(0x7f00000035c0)=[{0x0}], 0x1, 0x0}, 0x2)
socket$inet(0x2, 0x0, 0x0)
recvmsg(0xffffffffffffffff, 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x40}], 0x1, 0x0)
close(r0)
ioctl$TIOCSBRK(r0, 0x2000747b)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r1, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0xfe, 0x0})
execve(&(0x7f0000000000)='./file0\x00', &(0x7f00000001c0)=[&(0x7f00000000c0)='/dev/rvnd0c\x00', &(0x7f0000000140)='%&&^*/-*\x00', &(0x7f0000000180)='/dev/rvnd0c\x00'], &(0x7f0000000240)=[&(0x7f0000000200)='/dev/rvnd0c\x00'])
r2 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000040)=[{r2, 0x40}], 0x1, 0x0)
close(r2)
r3 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000040)=[{r3, 0x40}], 0x1, 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000300), 0x18, 0x0)
madvise(&(0x7f0000d7f000/0x1000)=nil, 0x7f7fffffc000, 0x4)
sysctl$kern(&(0x7f0000000040), 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_GETMAP(r0, 0xc010570d, &(0x7f0000000040)={0x0, &(0x7f0000001500)})
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc450443c, &(0x7f0000000240))
setsockopt$sock_timeval(r1, 0xffff, 0x1006, &(0x7f0000000080)={0x8, 0x2}, 0x10)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r2 = socket(0x18, 0x3, 0x3a)
setsockopt(r2, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r3 = dup2(r0, r2)
setsockopt$inet6_MRT6_ADD_MIF(r3, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r2, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
r4 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r4, &(0x7f0000000800)={&(0x7f00000002c0)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, 0x0)
r5 = socket(0x2, 0x400000001002, 0x0)
ioctl$FIOASYNC(r5, 0x8004667d, 0x0)
socket(0x2, 0x400000001002, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x8000000000000093})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup2(r0, r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x5b0, 0x0, 0x9, 0x7fffffff, "4d881953e46d1c268243a80c8bc9abf75524ccc3"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000000)="d96ded53ef76dcb3ac69f79e03767a2c9a14866294d68ed55964c703878076f5cd", 0x21}], 0x1)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000100)={0x0, 0x0, 0x5, 0x101, "cfe9691e70058333f34171bbea9b5144b04424a9"})
readv(r1, &(0x7f00000002c0)=[{&(0x7f000001a840)=""/102400, 0x19000}], 0x1)
sysctl$machdep(&(0x7f0000000000)={0x7, 0x6}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0x5c}, {0xc0}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000200)="e803f150ff8569142b71abf63143", 0xe, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x200b, 0x200)
open(&(0x7f0000000000)='./file0\x00', 0x81, 0x0)
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
ioctl$TIOCGFLAGS(r0, 0x4004745d, &(0x7f00000001c0))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x37, 0x0, 0x0, 0x0, "1f5012000000f5ff0100d9566d7d001200"})
writev(r0, &(0x7f0000000040)=[{0x0}], 0x1)
r1 = kqueue()
kevent(r1, &(0x7f0000000080)=[{{r0}, 0xfffffffffffffffe, 0x6b}], 0xff, 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000280), 0x14, &(0x7f0000000080), 0x7, &(0x7f0000000340)={0x0, 0x3})
kevent(r0, &(0x7f0000000000), 0x899, &(0x7f0000000400), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000001c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000280), &(0x7f0000000000)=0xc)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a614815fff271957d3cd40a87052ffef6650d2e29d42adf810d0fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e", &(0x7f0000000680)=0x1b80, 0x0, 0xfffffffffffffe97)
execve(0x0, &(0x7f0000000000)=[&(0x7f0000000100)='r//'], 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0406938, &(0x7f0000000300))
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000140)={0x8005}, 0x7)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xffffffffffffff0a, 0x568, "d79400e7bb6fc6e200000000e400"})
read(r0, &(0x7f0000000080)=""/105, 0x69)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000002c0)={0x0, 0x0, 0x0, 0x0, "1e080000745400000000000000e6cfc86700"})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000000)={0x2})
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x4, 0x0)
pipe(&(0x7f0000000480)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3200)
r0 = open$dir(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
close(r0)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000002680)=""/4101, 0x1005}], 0x1, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x2, 0x2011, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r1, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0xa44c139c98d03e8, 0x0)
preadv(r0, &(0x7f0000000480)=[{0x0}], 0x1, 0x0)
munmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
mmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0x9)
close(r0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
lstat(&(0x7f0000000080)='./bus\x00', &(0x7f0000000140))
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000100)=0x3)
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000140))
munmap(&(0x7f0000ff3000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ff9000/0x1000)=nil, 0x1000, 0x0)
madvise(&(0x7f0000ff6000/0x2000)=nil, 0x2000, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x7)
setreuid(0xee00, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f000052fff8)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000080)=0xc)
chown(&(0x7f0000000080)='./file0\x00', 0x0, r2)
r3 = getuid()
setreuid(0xffffffffffffffff, r3)
fchdir(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x81}, {0x5}, {0x4000006, 0x0, 0x0, 0x6b}]})
write(r0, &(0x7f0000001480)="23295428e2fa906bdf4ba040352d", 0xe)
syz_emit_ethernet(0x4a, &(0x7f0000000080)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x14, 0x6, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5, 0x10, 0x0, 0x0, 0x905}}}}}}})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x7, "01000611aef6e4c611d53300"})
setsockopt(r1, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
bind$unix(r1, &(0x7f0000000040)=@abs={0x0, 0x0, 0x1}, 0x8)
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
mmap(&(0x7f0000567000/0x4000)=nil, 0x4000, 0x0, 0x10, 0xffffffffffffffff, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r4 = dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r4, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400002ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x800000018, 0x2, 0x0)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getsockname$inet(r1, &(0x7f00000000c0), &(0x7f0000000040)=0xfffffffffffffd20)
r2 = socket(0x18, 0x1, 0x0)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = syz_open_pts()
pwrite(0xffffffffffffffff, &(0x7f0000000000)="f94c4c49df016fed", 0x8, 0x0)
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x2000300000000})
sysctl$kern(&(0x7f0000000040)={0x1, 0x4b}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0x8010570e, &(0x7f00000000c0)={0x0, 0x0})
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000600)={0x0, 0x0, 0x9, 0xd61, "f400"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
writev(0xffffffffffffffff, &(0x7f0000000ac0)=[{&(0x7f0000001c80)="373e40517e4c044a2422fd09c32aacda72525149a00deee9573bccf811049f65d4678e03f98bb6ad53fc15f6029ab9b69e136f2af35c7dffc9d5b2d0f2211f7021d47e4437652b8568c44f5319140c55cdbab73ba75c0609157001f7a53a6c1c8b05c09101e85fd33e6e783efbb4b737aabdb8417cfb7af848416735fbe8eb8c32b07ff6e4d2f8d9815f566d3a43409027c9b134d3d1fc6bc9333c4c8ffbe9760f1f75904b605de357cd229d5b4d2ab1903776cb9b0f43a8cb9bf4b725be02c7f2e0c6b5f6581748bff70fc427d720fa283cfda3fee11db543bbd998d8e293996c405072fc6edbbc3d", 0xe9}, {&(0x7f0000004000)="716a408a3b34f703ef42833bf6ad3c5f481196db1bbf67e41a75b82038541b65db38f535aebc773b629165daacb97b9948b9ae2388cf9b2f44d8ecc97861e6fb03a72b8cd8702724d72a6c1f1718e67379ecd18406b456b1ac4b1a9819f066e78f821081a96aeb86041b632d1099889ec0e4c022398eeb7f4bee2bcd4bb43f1665c0fb3ed0910d9b97193eab22b29a95c5a9788c6b3e6baba7ed36494c2c3fb68a36fa13e766b21a3eef62a2094efb9214accc0719a8aaf2cf227aa96a2e444628161b53a99617e6d481dccdf76b7eefbfd008557e4a85aa36c4cedb850ec3a8a73889a9221514071fca32eafa24c977c1c11df00a02aff2ec955ba8fcfabef12c1bbe6dfae712ed0c6af4b2906cbf75222eee1b46e4c5ac2b6f9df8ec5b21c9951379783cc33ce7f857f0ffa56e82a082a45d73451622c4f46ec32ac06cb7258fc6ab8ebaaf8fbf235ec31c424636b83d50f3e5805c87a0e5fc04fb5bc5f92193c2c13fcb08ab4be9ea8b6b1665f5b00bb2801a45d316c5f9576cadcd1d8771f5fcb0b8333d2e5153b37d2fc7d66bed6962d6f1416a4bc339aa89699f7e2f995f9b5b487e8d5ddccc7b6b7a81b157c51f4f854d2cfc7208e7785b7f2e0e0e8c94fd0c9f1d4164924bfb8c73e7d9a68241ddce53fe674f5963cffcd41639d3b4eba8bc94872fc97b8f09bca75de56530e2eb781ad0ebc748dbe9fe6e368a20b3edd0e1938056620cd644627d465847e9a2bc53343996f65696d705e86ab62ac23d140fb4d13c3dddf4ea67adbd133242c3be50771b3aa5599c3c13ca987e501639eee5ade64184fd80317c8d1ccf1d9cd37db34d794d8d753d00747bb352fca8227edb2f19776076e26937faa8982542322a9dddfe32ecc6c0a10bc3c032cbd0e800e82ed7bc0f78565f32186749b7d02aa41f3988cf6d722a407df691add26e9595eeaf61688f22eda6c9a08186f2a986311f62fcd53e0a27e714856be798e74730d12eb8188377234ba66c18b6adce70551f462e09c1b08177c9d82827ef0c433c1c9fe50b02e673a17b1f87b0cf995d633428d2f3da7169c55208d39c9cf066ddfe6080830506383d8c07bc321c3460bad8fb3b2a48322f50eecdac8a9e7000be10fcca517ccad872a4fd54e9a5ea9245698efe57e774a1c2c6edbb80bcda7a4d67728c2f42c423ce270dee62f512d857def0af95347193dcd97888dbc049b891eaad348c3b99df31b305e872c81ed7109a2113df238be67f622355ca62ba55aca2daa03a19e51db1a1b03142d24d7546732d45f98103e71a89f2851e6f4797d632dae8188f91d1ed5dbf3cc86510bf887a6f0be808751ff1a125667ba8866b4770ea6aa82027f4b3532aeb6098a49ac74a1602cfdd7ccf90f97389a027a3cb210d52845c5a1463ffa690ae9314596e1a2e5d0931ce3c91fc0a1a9eb64a2f35b9abb2b530cfa7edd53fd4ba2b62bb3feb2b2b5cf28bed7e94bdba16cabeb0316d5af53c9fbd942ebbc3d5a6530de04dce110e641aaae09ad547ba37289d90934fd9f1bdc8d3767d5e6923d321b09ec5f367375d54781dad213d5e970e15e3e2b9ea5e4611a20ab5f55a20d0cfc463060cc624432be3d170e51a1ebb13340532e7c56c5d51bc17764d2c1f37880c2578a624be144694b621307b3a2e82580b3b5bf35a6fc9f7013463a8a6c2a2bb42c44b23ac28768dcc92466910113870888e9ab793e66c5eebde56eb4abd8bbddab109246f1d566ee1c90077135cb63181a482f622d3114834db01a960bc41715247ea5a9622f1cf8454e0f5373e8002a9ee5dc98fc69854bbe25b9c8ca19060d160541ba888f33af78e015aabbb53df6aaf392400d570c3992e0550b388fefe4f3f0bf794ecfd965f511d696df61966011d8115f4cf6b06c2fbf3c777ace498393c3ccf673dc06f11177cc453e8bb635ab15b3a29f990b443769ebab426898f44d15aa3c306aa6f40c42216089175c0f5bc4415e26f0761fec74a9c75c4c345c55ed93c3be8a0f3638773df18b1ee570bb885493e699c24943062194ba2efa33a8add0edb308182ec73d4aff85739640f6cfd464b5183da57489829bccb730af7446b417a41141f46138754e3c5aa373c8c926ab0a9552f04a7c718af3c1b54152f4f65f42e7f2b8808287ac543afb183a071b1a031ffa490c13268ccd577ce0e73ad848003074917cb18d13e8651a81b2faae22c460b54f8ad7236271cb3215b393f32988a769867644f13a337e55912b33eb188428011efce6b4bc77727e8a3921a8c309286b635f7b49f7a2cdcb43f26117ca32269222a5103a7f0afda4750d94518f43fc34e6574e3f8b0f1c54623728d1e796a59b84f8e9333256dab20a090918cd01997a5f29116248d338c96bc42bc43600e0a5192287ada1776056774b9d9c2a7d578b20877ea4c7892b491aef080382eac0046e6f37f9ad33c821d36805217640765b8df8a2f5d951a1c30216e929860b2c92145beb9ac13f70b3fed5ebc0da4df2cae3a4002bb721f0513554a92f70bb6d13a6e4c534b27eeb93c7d15e7dbc2b169702c1f5beafcb341d6baecc999ce3e8fc424c8d43d415cddbf89e42f22f588fc3dd69d306858fb2efb55373ae89355cbe7eaaf4fa9aac529b4dd679333e9c7c382bbdd2b35da8d107ba486eeb719d123e687964bd048022016abce276c275f757ccc16631d8b348e44f68afd45ee64035d7d6ac59681aeef15862e6a210a7600b9096cf7012a3fd3ac3e22a5855b3c205aa0e5598ed072cbc6d3f8122480207d1c680fedcdc2d2da5f18d0f302bc3789bb28a7329f720221f8630894443ed91b400a0809882b49fd78d97a9535866c68637f2926a522595fe28ffbabcd8100a52ca8b89ed25d014f17f773a7fa764104c1df9d594e5d62c3409e33586e288d39bdae43fb4ca36152c8a97a5b322c458cc1b9c54917b570bd391b252e44dae35ed5d7ac1cb703eb93194b1b4fe0cb4bb801e49eb5e0a4744f432aa49c2e17138053ef132915ed5976ac554f193364e96c5443ed46f80a525c1eef510a6eba201b3ab2a0678a5799ab3c4656b053b232b46d2a3e4c8a3a3e55ea7e35bd96cf64fd6ac2cc7e69ce660802c9c4e0f3648a88ef8775b7bf785227773e58afd617e1f256515585d4bbad260aece574b646e943a86e6cb6de7d9d192a385de9e7428086ee2f0b9fe2903848446bf0260eae98916ace9e3c2ef6d76a9d086a06654f1e115bf4964eb4d8aef01b244b3f0bdc8b5138552393b5486268c4d0d0364fd0eef30d38cae9d2e012f471311f6b871b29d31fb12edecf89267e8949fb92bf3a92fe6b4be923df31d2a0dfa7adbd8ef6a304a39491582faf0f2a80780829f4c0f7faf241e65928ffaa87a58dbd9ca8e5e84def9f435f5103a696f4c13d2adbe5ce5a99749fd742ab4b28160bb9ead8ad49e53f0286dda8e6345835217578a5dcaf708b0a5aa7a5856839ab22b81e50eb9b4be7191176292ff71be5695cf7c9ba280d43ccb3aa914c88cecbfd2c0fc8a83bc779ec9d25cdd6dcc4d2d814529b16bc6610bed18941120be210b436a3d74c45dd0359f5a22191804ecf73d5113184ae519bd7c69f666dc983a8bfaef062a16f78b2404e251f27a374c4133770bf2c59a7ba4743f0568823c18194c3d3f2865aa91bb46a695cd4010b5779c563aa603769de28bab70a19869f5703de30192ca3f57e95728e453891b42fda527b3e8f9cd0ad5b3263b531ff2621f41f03a035c4b8da8f9b34b78bc2eb26d8e62bc4351ea6fc339b9002b2566f3f6f1d6f5721718f135ebdb78db26984433b39fdc5bc0b1ebceb1f3ee194794ea450705544f194b05429c03a4c19d5ac6350bfe735636cee3bfb43276383a6a3713582905bb2267d503112acb5e4a8a7bd1d079bc9e8c375791a54287c6682180a28886805ceca29914e0b5cc47f68f6604571cc733b49b09fd0d85e70889983179353c876bc0d9dd9118e8618b89540c2016e84074394c5ca07c622e0cc4dae61a988edb6a96f021526fe216df3a24dfade7daa16720d49d927544dfa951d467648a0d31142f2a1387c65e9ade18324a8c10a447f383dfeea15e32667f429acd97b195e57cae1d425d0efe7ac179ac3f6f22a6ed38e28d2f1a4796d15ca80331c45446e46ee093e572b116b1a2930e0b83a01ddaae91fdddafc829ce96053482cf1f9c8f369c1aeb24d434feef0bffac6ac7a65163ceb87f13b83e93af7a07a3a20ec67cc1db15b8e7e5a952a0a199a65ae605e762a0cbff1f0095558000cbeb37608e04813cd027800f7fd0eb1bfb73980a12e45b43e5634ebf6b0e22db813181963c2b70e8731cab146018d67a37fec748a1c839ec950698cbc20d2a187613eef11372c430b0eb2672d93f9bd911f8776fb240372107fc0e6b03f3479d6b202308254f75c5010a3cfc975de7be4ecb94c59d258c0938e78ff1ccb5777642ce456b16de6eeb1f03f383a024d0b69edc010ebd98ab1719c1c69c14570cfd91b57bac61df52e3844865c0ed64217d5d7ade1a8efcf7e57e76612e31b84a1c987e4976b1b094ddf9d734c8bc7d57a1482237f6d37588f396e0ae5b0f880ed17e1efe6b01a9adaf5c5168c703b75fde2c753214cb19debc3ae8e2a0f5d47817a5809821eb77fe8b9201eafa79607c18550faec73cd9c54328aee973fd99188006308aad5e51fda659380f52642c9b7d91c63d701992624fdc22c0ed8aa84330b15b56f05e6cfaa1b1a827c3971047b48447e458bdb1ff0b88528068faafa7bd59b4810df00f2ce9ca1cecb4274bf9c7036f874fdf590410cf0e07b2d39fd78fefb7f0c15cf8e655d6c9f07853484ecb8272d53dbb9b17ab47bf7055a1bcf9bb9ca9748fea183fab387c31a8b2f38131dd3fad565dc900277126bdaa9086c5f5d4c204db5ba31c21f9064cbfb91fd07c57149efdbea53474cc9b25673f2bcfe26eefe3dddc4013a645f3ff9cd5d101ab00ed3195484a9c3adee6a90fa8505f530fe0c978f939fa6291bd9a0952986786132930b5fc9a19ec5931da04c90f75b09b242f3b566365e02e5d831e62dc95083c6108210eb220b6156ce04df61396a31143d503fceb6d77f8b4086a303b59ece70c415cd7b10db690d0b8bd191711039fbba8626d3eeedf2308620039e5bb9320f1bea3e24b53124d08dc47b24a278af89505c37b4041772d7a1277dbba1a1f7eb700a29986e58b6c7f95b2758d50b4ef6163e8a08fcbc8866e2249954d860bb4141926c0113937a736e735abfa80ec520047c2a5c38e0131ae1354f8884504c17997a200e7bc5509890809753086233a9c8fb5cc2d904378b02ed59c0fe25ece7ffe916687573d680c8003f3d2ce93608c89a374ab2e108334278673c9cdbcddee0b5875f11f78e902dbcbc19ca500046cafe276917803c38e0a868210a0bde2c31af2a0df66fcc0dca892a671be13272acc19caac617d178e33e3f545e1b3184358901dcb97a850eb550e3eb852ff6d7f3e3cc4c63cafc88bcb222d952f76133e82e54c3282077e0c8c5f994265b1d3bbaf1cf5eff1ac4a5c6b30745dc7b94136051f55dd901d99fe57ad202efacb6ad5a75f1f8428973384332cc2bd31e50cc3ca119a9f8b542b16c72ce94cfaefc045120935fd0af35493fb1e8195b1945bd940172ba9d45d1f4960f6f4d01824ccc23af4aaf73d832f0a016b51c40c18961fca4b40d3c8e80", 0xfd5}, {&(0x7f00000007c0)="3656fb439d40b2c8c984fa90405f6b180a773406967d5c49355b0773f7745f6102000000000000007e45cea3b4c7d8d9d5269127a081ed7eb937b33758d1b11065da17e06a0e4871635da4f35cd318b8211c08b33a5fb3c7cecac6384360cca8b0a52493a49f87b48d0c26d483d535e28021c4cc25ab21832f95e72148f47b384a10213cfe80fe4608866aa9b0fed07426bd4d5dab20ea8a20e21f65dcbe9815a508c6c61c813ba469e2db64f0242d8d1a04c543728b1c9e0565e7d15dd74992e0460e516b", 0xc5}, {&(0x7f0000001d80)="5b6ebeff33593511d99197824fdfe6646234c9172c5a94d94e83cb120a75808ac485dfde181b8cf62990d8211c89076052fa456f59c8f4b9a4971f591d2b631e2b007f753f191249330f4f3d7e28e2bb4940794b6b3cacc1c2427ec4c35d784b9af3b35317fae848f15a21eb7041a69157f45d5a27146ff4891552240d9fcbc8a0a962f53d8223aae84b4b0ab56712aef214096d9a1186c2d3ba733d1e2b531aa73d7bcf219cd3bb865222b5aa4fa2ef6b5dae1629ae0c4cb1f45213cac0d4936a44597e339ac5147429b954dc0d556f513c12eaf8d8966ad85666dadfe27b1777386d34000000000000000000c1a5dd34f012f341a5e330650dacca59d5f1824798ba3028c142f0829bf290930050508c2f70bf684389c179c788def0fba76de3edbb241d521b169f365065314f1541cda0f5c8c0778b0ecbc5824126db46c6d977871a47e28f4e4dae00e2ffc068c974207173f81ebda1188f35607623fe1980f6eaaf64a9702fbcba01d4902e53f99dc1b21d469c7bfb2809bb5d3e76474c787f5b694abfb958ee212e55302fe38bb57703d9416670ff2100259dfa865b00", 0x1a0}, {&(0x7f0000000540)="70dda14d60338e1128994c25f29d429c2f684e4060fd1ebe1948b4e53bbb6f0dc6e521edc91e30dbcc5bd58b61373f3d23dca0c3bf62863e1ac4f015e78657043a8da44893b5cb63fa7ecdb0c2b26f9e47a1a70429", 0x55}], 0x5)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x4375, 0x9, {[0x8000010001, 0x3f7, 0xbfffffff, 0x6, 0x800000000000005, 0x7fffffffffffffff, 0x7fffffff, 0x9, 0x0, 0xc00020000000801a, 0x3ff, 0x8, 0x100000001, 0x1, 0x8, 0x0, 0x1000000000002], [0x80000001, 0x100000006, 0x8001, 0x800010000004001, 0x9, 0x4, 0xb7, 0x100000000000004, 0x4, 0x59f6], [0x40000000, 0xe77, 0x8000000000000005, 0x800000ab5, 0xf7fffffffffffffd, 0x800, 0x40000000], [0x2, 0x0, 0x40000fd7, 0xfff, 0x4e8], [{0x9, 0x5, 0x0, 0x6a17}, {0x0, 0xfffffffb, 0x0, 0xffffffffffffffff}, {0xcbc8, 0xfffffffb, 0x8, 0x9}, {0xd4df, 0x4, 0x3, 0xfffffffffffff8be}, {0x0, 0x5, 0x401, 0x409e}, {0x1ff, 0xd, 0x5}, {0x2, 0x3ff, 0x4, 0x2}, {0x808, 0x100, 0x6, 0x1ffffffffffc}], {0x0, 0x80000003, 0x2, 0x8}, {0x0, 0x2000000c, 0x4, 0x2020001}}})
sysctl$hw(&(0x7f0000000100)={0x6, 0x12}, 0x2, &(0x7f0000000b40)="5cd73027195802b41350e0485d9953385d15e05e41f3a45621eca11683343a3a7aa23c260a86d3d52b20cb00b999d96e1d43a83d863ed07d98374452252e5ca853f3b76c0d1dad4f8ecbfb72cbde7df445dad18070a78285cc86487af02e4076e1d59b20a7dffe780fa9f8cfad5ca84942a26841be2826a17fce121325074f451c89e1e37c77bd85027b20d74120b2b91cd04532384d25db6f1dc2476dc1a2d5fbac083242c610ea0bc97788eaaae119df56aec9bedadf14ba0d468494c16257cbfcb4120be9c2c41ac6c29eb23d7a29a549296bd947811cac8812bc1817c55df5821ca208bcf297ccc2af204fa0ffaf673d6648b085cced8b61ed5fbf7bda2868b173319080aa15c5", &(0x7f0000000040)=0x109, &(0x7f0000000c80)="239bf3aedd4131c766b871deffd6bf326389da9cf5e19469a50c20c9316dd0f891c5e70d91cb82e6c74bd8bc67421b5b0a2e3fecd898d0e0f7efb64c16547d68be7201e2e9d83ab2b6cc8ca5402193c30c7cf0631663e5a5ff0d838ba68822d261b222d2b92c8dcd99571748c9a62a64cb2b3d74a659745970db95f142315986bab12599eb4e864d62b02384eb1086a34d926ebd50c6d6c0fae5904fe1d3244d28f3383a4ec1483dd6b435cd5019377400f0f90e29df44707b23fe5db0f01d385db9b706d7adb4cd1045c9b7b78f60d3fd33585284cd11e57ff5840a2e6a376d6387b864bbe8d1ecdd27b6c0679c3f3379f66a313b60bd489af64d09200120f589d80e9bee41bc39542108e4e38aa1ee297c0de6ec8e7fbb9026da1d911446898849725ae66b52088d7561a5d53301015fd426c2d233dbba02790c8bd426c690781cbbe98491b29c0e96519522117f3855819aad477f06c9dfc6f8dd1c15486b80a2f656086173066f35d2356044e5c81427810be6da3cbc34bfbf8c56afd29dc3d1937cfc635a0cb5e2ec685408714b6d698c4208c4aff6c00454a4753f808000885217928cdd6cf3130d535dc6a1657b5cb8d9b22acf53689556c93255ff78e9865cfdd78a6c8d73e003e967412ea810262e125a251345013a8c9a6c7c9ca3b40803647c6df6994a517bc463c5d94cfb69576813fc69090af4c8b537a84119363dd1e2e4a57e7a27e18f52367f78a748c73f7c61764a09875112e7c77c9857b40b4502ec9a2b361c6cc89ec6251f2f5ac02cb5ec6d4b69bb741bb4cc94693952573b7d662cc76ff37aa4978f600bcc62826824e7e0432d126119c611ec1ca2d4482f77ee78476d8250a5f8b3dc47acf55b3eeb389ccb191390fe03a2303c768efb3413d669c104080d7c27cf710adccb03bd6c158ad60a8f78b52c67d2702fbd6e4b751a427666c34ca995c508038dfcf1ec687a7732dd79779fb4dc7f9902179d2f71053ff869ab80dd1f6f5cb9caa327fa03574be4dc6909d1461bc1c7e95935bf1961e17276f624bf78aaf31fe93522c5532a522ef07edba15c978813013f33a4a5295b3940be3fc118cf6e1c42092f4cfac22815f99d263e3f4253b7d561ffcd172916445b4f5108ca76284f968a0c5dc9d0fd7fab2ba176f609fe9383b61324e79dbe5a2890723483510cc7e22fbc8877de237562654084c498b05695a5711d5765a419984f1b46fd50b2db461fe22e1c056d33d8935d6e360dfa66d0f667b524578ba5588f7436d336a4319c4bb4bd0746b223c47e1499504d960282f987ed41fdc727e5a0e10ed73de60bc5b0581b93f5210c562dbb2ac6aa0e74842ff348f5c9ee7ea2124bacfff8c148ae2999a9154c42fc2d286b0c33ce42f97d9b37a3f667b1589abf5572264e8c0c3553e677c8de10d7327a4bc834b82b742072873a58e008bf21048c2fff2339ed8e5ef70d57d0f9afa2d51408f36ecee606c8662d23852eb689d9a110a68071fbc7fb8f12283c3d5b4696dcec754612cb049e8e70d936c696cafda665dabce24a8fed0e7b38e406c8641d59276b2b9225fd20009f6d1d5c9e9d43f493ba4310579e5ce5ee7fceb0ada1e962030e79e9098f5164e41bb631a34f445bfb345c2c29c190221ca292a3f460ccab290268a0dd84f3873224340d21173f22ab28b15862640562acd42241f43947baa71fc35989e00ee0cf0a0b3d8e8ea6d3b4dda3f2bb69df6edf809194079132114f4e2f3438d6a95eac807e0e47043aa492dca5d91ec58159a3b8813ebce7127481daf9bebbcaa39f5c920a55779a7f24e08d3cbcb971115ff0b5cf13e63899a3400b7fbc3b042c8d691296c8625b22b234784f28988a2dc36a856f690b1f93abe72b67485b55fdbba97309afb3526a5554976866fc012664e29b2c64b715a59e03fd62cfe389c292b3a5931d4686a3de71f6da48d42f2d6dfb629533553bc48233d50e0cfcf3f22c7936c97f949baf2eb41836dccff0bb520533efb0e0d4b40ab960eccc84d3012e7a0e2f820d417cfe1048aa708657c3e644d907ee07d623b912793cd943558db7f8d19ea0a9c23dfcd5773e1522eb150ac1d809c2b1d19593360b36ed960569e486f261d0838b6766f232a2d2c519d3f5e072a75c184c6e9ba17fa8235dc8204bbfc105846c22bf3252b416aba556a9ea8b9cb140e8645954b9a34db5f88445a239cdc76a659dd6a3b65493f68bc62fe603ba49a534c1d0b9d136c803ca7505d135b7747dae3c75848784e1e22e9d32890cc2dc866f8961bcc3a4eb86bba1d49e26364f5e4281d0e9849f43c01c9aacd2dfc4c14e9d965b9081904cbc2fddd429b44709c2e248b0523955f159a2d8df67738c02cd1d230c89647baf00d97656950ee77d7e9def50eceec02850e9ddfa6c5a0c342da30ee1e91b6ce647b37bea7e2a5431a64bedc4a287c326dec3a5599b31e1db2df10da658b2061b8e597de74a060001ca52d4ed3825daff97b9412e6c4d26d0e5c40abd8b9a0053ba12e7c5fca739d40c617af943bee374e26a17703f0f194ae5934cf2c93d8f43a552780b878d7d571a205743d9e64eabdfe809d394d26d059e14c34a5622c49b983030590b8cdcd90f278e65893b0ff3af163ef7e0cbf4733f214d98088edcfb7c62527ec0c6c87c4d97ce79168cd68a241f5ee2e01de371538bcfebe27f4c019ee3cfcf2422085d4b7c6000e000eb9b6c50dac50090fca35fc09ef2245ce90844c3f82d76d0d98803f48206a8b6c932c5a6787953f7decb07412fa3f5800c07aff4c84ff147a2e1635b1009437f4ec2220b36afc08c253bea4475eadc389acf23d1b5d2d2d2fb6070c9a13b5b71e9e3eb7f14d0ed7d569ee29e06dea79912a6846abb70a97b7ca9dd274009cdd34570da11d5ce09d5af27dce384330bbdfbbcb738f8cf6811adb9f3a7d148c8417ca465916782cc5f973f09ebb5ea7ae51c3c42e552ee5987333e89297b293c88c49761b104253a7e3438f15c962a0709e3335078368ab6a1d079df61e180eabca587a1d30b3c095d770964b2f02232bada8c83a865011bade54ed6f16173a13712428c6512b5d2df6cae920c4d1ad117c8ef66e156acfbb8057a519b35227aa83ecc36e1f6813939ad5ff78c553fc94ca250ee667b17a089463c0d8812182795c3cd244aa09a2fb508f17ef9b6a18bd3cb0bf1ffe05f630896e81d33f5c823312af372fcb01c455fd4c87e93d62aa0d621e9bd61d5a9ded4fad53cd2eff3cd68c1638f626528cab5afaa4ceb0829243ab98c2455fc54f95e075572caa727bbcee4d97f5d91930149ecdbaa014e29fbcf5d0bfee379b1e9ba616292ded9ee1054e4b4f58314be3b7f58190294e707c43ddc7fa9497600cd7352419a6b55d9e8ea7dc59d67e97699cfebb60d2b54aabe7aff4ada19de93ca87b59229bcf5ca088165b9758d7cf9d489b283639a3bb79566d3aa73bcbf1e42afb87bd52072d655e10a7805a4d93677f02b449b9f343eb156a4ba2e895fb64802b5fbf6c6056ccba0d7c6704d495b3483fb9ac7dd74f498749662b0f319c5bc3713536edffa96a1766f375bcbc5e0f060abf6a8bc2519240d3a6eb76a2927ed1fe8d7bd00997ee06a08e68f36da29a1bc9f01176b75e573d96f705757b1566f6d9713055df54ba873fcdc985a04bde01e9af19ae8b4835f569e07aa35306366fcaa5da54b3da99be4b2488b13b3662e4059440199bc808722fa691eb1f3bd39b8283540439db4db09adb7907f1880d67c9b2f241e9f6096e3a735fd953751874789df5dde741cec3b1f65dc419caaf362c7be7edfa670af25bda89fc77bd58107c73caddfc478405afd345c77e2f99c76e8a9d76b4a79e038155af13c2f90d7486a0ba67c201ffb56de458d39c66914c00b1aade7e93827a62c5a6fd54e64081ad2d28c2850152cabb623c7c3a9eba429e5a560bbf9ccae6a65d519b177c098f5dbfd91dede9fc262d97050adec63609a7ee063123f63cb82baf74d0a4d7bb3e8f9ad8a29849c7a37a693baf8f2e10c775e150dbbfe801f747eeddb78ba68c67707b82590b0d8b4894910035b5176c33ac528f07b76dd7fbdce70aa61bd923a29159516f08c8ae848858acb84d9be72e9627521486cc2ef9596d20c8dd311deded20ef9cb455a2f7abc27938e29399b742234e0ab0870eb4a461d4855f2866555c4599c57cdb6c494dab32bb386b755d920cad094e689b51dd9a77ed378af6defb782152c4533635efeb2c2dab2c860c2fe7736fa27de99faa1b85e6bcd90f0c95235d6a3b2f1b98d60a2e8eccf22edbb9e356cc8e0e57cff562ae087e1eb9b5ca4bbc6d2924fadb8e26a7af3e7efb1c43a769dc61c90f6661d07f9af643565159575a6697f0b03a2492a44966582feae83c08aff1e1d1011995787d4bdaf181fcbccf50752a0f68eacdab9a159aac9726daf744baad5e5eef7017b2d833e95ab1282288af9564888a45da6858a5e4eda2b2c018bd5ff6458b9dce27dda482b09fc68d217941535eb712e2c114bb43079d06df7ae4fa5b0cd82675cc4f2903545d1c87f1e55a4549e93d130fbbb21c6be99c5f259d854d9bffdf9641f70c0707e0f610d6e49c29363d46a2696534f2c081fdca1b48ddc170a94fc48fa2c3a10e9a25c56ab171419b85f7dfafb16c0f3b788d1157c1362177735ff6a64c2502ab5f74b143141203f7934b2a2c3807e6183067b27114c7288c312d4a6a983bf381bcfe0f3bde517e6d5abaa41bf1978f57130435d8fa1a6b130fdb90092fb6e9cd08010e9db144ac8c734611a30c061d8fec42c88eed3000a5c64c68c3161f1a0a46fff8e53eb6c38be5f47ae6890e3d9148b769568edc4d9027ff168f3003d464887b55686284f360aec74d4d55ee6c0891f0924d198b389b4d94da7e67a122c3731f5fda886042ffade86c3ae3ab5cafa6dc243f6fcd461230f3d32ed8df64adc53afb67ee3c19d5e4ac97d3edde3a9d3f431a97a3139572935e0f8f9a7322efe1719a7e60ebc57d432278a7233c7ae331d83085e1b035e60d6a8d3ec817b15b8102428e333d8f7f12a61b2f01b172a4b80c8e0fbbaf73d1920b3861a73ed6e19051ba86d44afd08775d5ae5972e482379306db1b9b7f12888e0a160f500a4148a706e610815b3311d2e5a5cfb5e8293a06edd8c9c0914a1ff115e8fc1b5bbe7db98bff1a3e81b9edc6deb9732112b6da1b0dc6abe3e2d256c659f7139df5f96e8931df1473f167232dc388ce75ffecca0912b474a33860cf627349928477058c56141177718f42358f92ee0091438499e6a541a77e6bf326fb59b346eccf61dd7d964d3c4c445d0581b5c3bd4cdcd17508553b8b7d471a573b2c46578609eca53bc6483d8f6624c85862f243aed8abfe6b0fc7f78cbf89da3b8eeb7d02680d80fb9da15d0270a1f23107a38f0e483aaf7e037e01380c97f5ea8f8022b08c9639ac8e362abd4d5a11834749d96a9ff68df5a6cf35495bba6583becfdb9382cd2493e9b1f230b538720688052ccf44c1ca19bc83fa7299d269103c496720e09e21861a1f1aea000a7fd293d8687b7ddec4f9e19c4b8cc744ccc26d873d33592e85bb0ecaacc2cab77d010719118b936729b1e7c181bb242164dc2d56833c12d06ddaf2edc9be6c0095317ca309f45d75cab9de03e4777ceba6fb1bc44ff231e7f49975f82c14b72a536eaac2442ac5e4ec5312fe3a012647ddf7e9a9c3fa9324592b5edb63dbb35206cc1001a43e47920caf5f97e1000000000000000000", 0x1000)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(0xffffffffffffff9c, 0x1029, 0x0, &(0x7f00000000c0)="03000000", 0x4)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x0, 0x0)
shmctl$IPC_SET(0x0, 0x1, &(0x7f00000005c0)={{0x8, 0x0, 0x0, 0x0, 0x0, 0x18e, 0x200}, 0x40000e6, 0x2, 0x0, 0x0, 0x2, 0x7fffffff})
setsockopt(r0, 0x29, 0xe, &(0x7f00000004c0)="0b652aba822bd43b69b3fe462a9ea20ff230f3db5d9a56bccfb9fe46ca049c072af64ebeac62c0727d06d28ab2f9190fc533bf17198e0d031c37ec589746a018271520", 0x43)
r1 = accept$inet(0xffffffffffffffff, &(0x7f0000000a00), &(0x7f0000000a40)=0xc)
setsockopt$sock_int(r1, 0xffff, 0x10, &(0x7f0000000a80)=0x6, 0x14)
sendto$inet(0xffffffffffffffff, &(0x7f0000000640)="21bdb4ba44bbf9c04c744a5ef9066b302690d1104db522da39b24d46cc344770f4c3b47fb53aa2f35086a413d8c17be3326ff12848868052a842fbc02ef124f6c3dc1f1ed6bf82f8bb4935f7986626849c93a1c777bb8b53af3ca1d33a7776225fe6b41321b62ea8b5498dd873cc514cfd4b611b0cc2d4492e70c26abcf57b874518afc8b874d59634694ea02424beed4052de1ec2c2bda8f8aac7ae24", 0x9d, 0x400, &(0x7f00000001c0)={0x2, 0x1}, 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000002380)=ANY=[@ANYBLOB="01002a7747658f65320002883a4041b84ef89cf48bd82a1ea309c8272fee7b29cc6ac50a04131f588eb8899e75fc800929ba60982a7c000000000000000500000000000000f3d800ef5d203052e2ba335dfca4e154784505a4d39028326ee2492ef9a93299a62f01d744991a8ed5089e3b3229dd941292dcac6bca7dd17dea13f1e4bce7602b6c96e471da5c38d2601942a8b483f89d23564cca8d549c1af895878ee102a67df68689261f0832e708eebd23f072d84359a372f5bbd6c5b2b1b6a56c1099e97c6173176a6410f0c6ac2bfcecdbd3086227d1ffece991f39e86f01f2b3059a08fe2e843659c47624209073e6337b0390b3ebd7240c379927f2b7efd09000000000000a45040c29e56db6036594faaf8ecc645b6cdacd8076b983fa5c1c89022bab7e940b446434973e9368f572f3b36f3fd55861bc216bdfe090000007ebb1916b2b8f2790a081ad00b55304f0589fa0548871416de6dc6b4f6cb19f32af08fe3c38b6a7b5c9be4c33adfc516a38229b42044a62e26f07b0f0afd2acc453a5f83aa23ebea5d0f207da075318e613f4622a724639caf7284a533e59d394c4e5a5a6e39694e6298d55c320f71a279a0f4954885eebd621d2557c28b167eb63edd163e6e34225a099256017a8d888d99a332c2a19ac30a8bb96c52d887c67e5b2abaa60152565ea5dcab361b7c09172266b5f800ab904e8c755fd11aa8fee63da52d7a49c147881e3618711cb3cb3979588dfe26b4a231f408b67032bf5a58ae00990e0914a0f955fefc515665572750dcca2423b08728aa3e7024f6f598efe0965bf3a824bdcb5158fb2570b1c9c65f3970a9ec9d0324c61c29393af91c3cbbfd802a78071ad5918ce134b9c75eba3ad9655116940d69b9666103855aac0aeed7132f3676789818bc34895400b16bbf3e42482b2ea8b67ae70d16706341d34f0a7a3ed068b33c9376f0ffd26f89d63334bef5e59a92c1fb3d6f3ee2c65c272f5813c0d0b773c7c676bd8b3ab3b4a18b2c4deba4a0eeeee20d6cd74eba0bbb96bf6ac08dd65ce525840615568f84afb27dd1294608e9e1d5b71c950f0c58251117dc5032d115f152f72dafa852d11fa1d4987820ea92f720a5fcbdc5af1877e76e57feba3dd2302b0ecf01cec2ab304f57cafb7ff71ed2d5460996a494e5a61a706a70048a4d858d73cd7324"], 0xa)
ioctl$SPKRTONE(0xffffffffffffffff, 0x80085301, &(0x7f0000000780)={0x8})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ktrace(&(0x7f0000000140)='./file0\x00', 0x5, 0x200, 0x0)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80286987, &(0x7f0000000300)=0x81)
ioctl$TIOCFLUSH(r3, 0x80047410, &(0x7f0000000180)=0x400)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="83028b35c7"], 0x10)
poll(&(0x7f00000000c0)=[{r0, 0x4}], 0x1, 0x0)
shutdown(r0, 0x1)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0x6, 0x10, 0x0, 0x0)
ioctl$WSDISPLAYIO_WSMOUSED(0xffffffffffffffff, 0x80185758, &(0x7f0000000000)={0x0, 0x0, {0x4}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000140)="0100a56700009eff03000000", 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8202aa9cef"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r0, &(0x7f0000000100)=[{0x0}], 0x1)
pipe(&(0x7f00000001c0)={<r0=>0xffffffffffffffff})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000200))
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecf860080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443e, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000040)="eaff125c", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0xb1}, {}, {0x6, 0x0, 0x0, 0x80}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x54}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000000)="f2afca000031fd9e3840ca8801e6", 0xe, 0x0)
shmget(0x2, 0x4000, 0x71d, &(0x7f0000ffc000/0x4000)=nil)
r0 = socket(0x1, 0x5, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x15, &(0x7f0000000040), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cff"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r0, &(0x7f0000001740)=[{0x0}], 0x1)
sysctl$hw(&(0x7f0000000000)={0x6, 0xa}, 0x2, 0x0, 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000114, r0)
pledge(0x0, &(0x7f00000000c0)='vmm ')
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
open$dir(&(0x7f00000003c0)='./file0\x00', 0x2, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f00000000c0))
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, 0x0, 0x0)
getsockopt(r0, 0x200000029, 0x2e, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x802069b5, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x26, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000040)={0x1, &(0x7f0000000000)=[{0x0, 0x0, 0x0, 0x7}]})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000000)={0x1, &(0x7f0000000040)=[{}]})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sysctl$kern(&(0x7f0000000000), 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x48a, 0x0)
ioctl$FIOASYNC(r0, 0xc4504447, &(0x7f0000000240))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x801169ab, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x4c}, {0x5c}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
setuid(0xffffffffffffffff)
utimes(&(0x7f0000000080)='.\x00', &(0x7f00000000c0))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{}, {0x7}, {0x21}, {0x3}, {}], 0x5})
syz_emit_ethernet(0x2a, &(0x7f0000000340)={@broadcast, @remote, [], {@ipv4={0x800, {{0x7, 0x4, 0x0, 0x0, 0x1c, 0x0, 0xc6f, 0x0, 0x0, 0x0, @empty, @multicast1, {[@ra={0x94, 0x6}, @noop, @noop]}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f0000000140)=[{0x28}, {0x1}, {0x440e}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000040)={@random="c028a1b52dee", @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="3efd8ec01613", "", @random="f4716580cdf1", "79d01739c9643097cfc3e546281f260d"}}}})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
getpeername$unix(r0, 0x0, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x6}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0xe5)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecfa60080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x64}, {0xc0}, {0x4406}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x46, &(0x7f00000002c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "32fda8", 0x10, 0x0, 0x0, @mcast1, @empty, {[], @icmpv6=@ndisc_ra}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x1c}, {}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@empty, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @remote, "54e2e9767c5f018a37ee7b48af7d655c"}}}})
sysctl$net_inet_esp(0x0, 0x0, 0x0, 0x0, &(0x7f0000000540)="1e85bad2a86e7cbb7f293e505a6f588f0c62179122756abfc92787145b6beed77c", 0x21)
r0 = socket$inet(0x2, 0x3, 0x0)
recvmmsg(r0, &(0x7f0000000200)={&(0x7f0000000340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)=""/9, 0x9}}, 0x10, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r2 = socket(0x2, 0x8001, 0x0)
close(r2)
r3 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r3, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
connect$unix(r2, &(0x7f0000000140)=ANY=[@ANYBLOB="8202f2540a0fbb241b10012b0e10e6d14afe7c6409805daad462303fb80268ddf7302bc996f72c21b9d6bc3417918118df567a870623b5845d5fd59711efacd4fe5b116ce0"], 0x10)
write(r3, 0x0, 0x0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x1, &(0x7f0000000080)=[{0x7ff, 0x6, 0x9c, 0x5}]})
openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000100)=0x5)
execve(0x0, 0x0, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
ktrace(&(0x7f00000002c0)='./bus\x00', 0x1, 0xe5339ac4c82f8770, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x24}, {0x1d}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000200)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "22b812", 0x8, 0x0, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @multicast2}, {[], @udp={{0x1, 0x3, 0x8}}}}}}})
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x26}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
r1 = dup(r0)
ioctl$BIOCGBLEN(r1, 0x40044266, &(0x7f0000000040))
socket(0x2, 0x1, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000240)=ANY=[@ANYBLOB="82028b7d9b"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1023, 0x0, 0x0)
socket(0x18, 0x1, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000400)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000440)="0118fff60f9efcffff59657f16e66e9f97069846a2fa74080000005188f322a33afd5604c4aa10930ed14b10abb7d8414191ac619377bfd683086fc78a1dbb0991fe8a3722e3138bc29c66d273252d8a49f0b62ab57ed767755d45d5ae117a6d1bb9e7734421cef62cac0800000000000000059989c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebb2481ea2de4000000aa1e20a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66c878f486f7e59259a05bb689915b90980246fa85c22ad90d4e3776895066d2bee08f70300000000000000bd0100fb00001c3f30e790fd157cd0f6ac73547476b2a766825175bdbda013a1dc24244a0600000000000000000000000000000000000000000000000009ebffffffffffffff7f3fcbadf25485d5ca4287ed75b0db89c123fce0cbfb668a58f19f470b6569b58284c0cd71f0e8d87e5503c72b7d1b3db57458000000008e1f2e111835a6b788d5ff5256df13b59edcc163f269e55e741205360c9d2e43575809838bebf4e71b1393f42a216e6f1b67f8aaedd53dc24ceb12d50d3fb41b2732e741d0ea739f0ceb63553689a46145a280533ec0d29de081568214f857ebd1f1e41bfb9a21624840a96d9619e0eb108d5bb60a27d465014bd7742b7e5f4a46cb83eea6b48aeb60db0242eb2d2abfec6dc0e3b0450200b24c238f90402598ad961f9f7502767ebb569f49ec0000000000009948c6", &(0x7f0000000280)=0x210, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x40004200000028ac)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x60}, {0x4}, {0x6, 0x0, 0x0, 0x43ff}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000680)={<r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000a40)={&(0x7f00000006c0)=@abs={0x0, 0x0, 0x0}, 0x8, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
getsockopt(r0, 0x6, 0x10, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80606941, &(0x7f0000000300))
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
mprotect(&(0x7f000081d000/0x1000)=nil, 0x1000, 0x0)
mquery(&(0x7f000081e000/0x4000)=nil, 0x4000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
ioctl$VNDIOCSET(0xffffffffffffffff, 0xc0384600, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x2, &(0x7f0000000480)="f7ff51a4a37b0df30b1d32d8f652ff164596567a155304fc3fc5e0d8ebbe983cd4a74684e785467ca901fcbe92b233fcd97455ad4827a12633575084c0d82e08794ba526de57aa7476686ca1b94b73f6fd34922c7e02529c494fa814b50e80b6d29c06a29c4a15954970bf801e612f4500fc4fff8698dfceafbac61b8d6f0813d2c63e8160f9cba477ecccd060ba44552ab93c0a002082cf5d57ef4541f0e086dac5389059bda2a98093775df9b83707c484cfa4cf40c1d7d9526963f876622e3c82169c8372abf13414b211886ce5fc51990a137f17bda63d52a983c4e6a85c036a43337b854d08c8f16b1d027d1cc3ed07a1a2ca8294c8f4c8d7a08e9a16292263859d2965f2685fc49a141ea9cfacdd3c65fff10bd34df033eefde4", &(0x7f00000001c0)=0x11d, 0x0, 0x0)
sysctl$kern(&(0x7f0000000240)={0x1, 0xa}, 0x2, &(0x7f00000009c0)="f4e469e1416d4c825772f597ebcea58037d338159133752d73e0570ba3ad247f73a33585cb52d522249048fb5b49c6eca1a86e96c9aa8cc48c7c21fed028bf2de528b9578b94179a9e907d9ddb4ffc04bcb05a5d09094c643d3538792d1d5cb4a5cd93643525f2b0f72ca224f2ce59d2713722f248899355c4e2e2f9c420eb19b06939f8d31849", &(0x7f0000000380)=0x87, &(0x7f0000000040)="931f00", 0x3)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{0xfffd}, {0x1, 0x0, 0x0, 0x11}]})
ioctl$BIOCGDIRFILT(0xffffffffffffffff, 0x4004427c, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
mmap(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x4, 0x1010, r0, 0x250)
r2 = semget$private(0x0, 0x4, 0x1b2)
ioctl$BIOCVERSION(0xffffffffffffffff, 0x40044271, &(0x7f0000000980))
sysctl$hw(&(0x7f0000000180)={0x6, 0x2}, 0x2, &(0x7f00000003c0)="52a3a4c88889664a74403781631ab46a2067e65589ec879a20f2579884c215956e3df006c07cbe5ec1a802d5a674ef8549affb1c30df216b458949216abc16b3cf6de9d523694fbfd1986708c68f71a8f5cc42fcd3cd51b17e888a7a521228eb3c58f768f6663b5485209cd8490821f6e3608a879c758269a075c4ea9f6d08fd2c4fa98dc5c49199d9303b5839b133896e3834f3d6af951e", &(0x7f0000000200)=0x98, &(0x7f00000006c0)="0bf5ddc2a7eef80ded45e840c5f080a8d736d540172d4aacc1d097a56279dbb794c3a4fa4447eee4b13a4e08fe9c7b6cb13d691dd75d6d13efd58f4484dd21ad4cff236c29049c2e986ee7ff35a07862f601a74bb067ddb271444e47a7bbfcc963ac", 0x62)
kevent(0xffffffffffffffff, &(0x7f00000008c0)=[{{}, 0xfffffffffffffffc, 0x42, 0xf0000000, 0x2500000000000, 0x3}, {{r0}, 0x0, 0x20, 0x2, 0x8, 0xe97}, {{}, 0xfffffffffffffffe, 0x0, 0x40, 0x5, 0x8}], 0x7, &(0x7f0000000ac0)=[{{}, 0xfffffffffffffff9, 0x0, 0x1, 0x7fffffffffffdfff, 0x400}, {{}, 0xfffffffffffffffc, 0x0, 0xf0000000, 0x8f0f, 0x4}, {{}, 0x7fa36c34f9ac6e15, 0x0, 0x0, 0x0, 0x400000000000000}, {{}, 0xffffffffffffffff, 0x74, 0x40, 0x3, 0xdb91}, {{}, 0x0, 0xa2, 0x1, 0x4, 0xffff}, {{}, 0xfffffffffffffffb, 0xd5, 0x2, 0x8, 0x1}], 0x9db, &(0x7f0000000bc0)={0x1, 0x100000000})
sysctl$kern(&(0x7f00000000c0)={0x1, 0x51}, 0x40000000000000f4, &(0x7f0000000100)="8e40a78dcfe50284d5d5c343dbd340e832dcae51442a30519354d977dbcde1e06c4057d0befd082cdc2e156aa7ccf6dcf9fc5ceb8c7de37a61e2d5bb1bcf9fd6a84c9f95a940da593e0a7852c1609dd4ba187ae907458655c70cbf331c45f80300"/106, &(0x7f00000005c0)=0x6a, &(0x7f0000000d00)="56464d8b23fb280b2313916e5570cd62af7fd2667f28a9fd028cb2afc4c6094ecab395705ae07412cf73fdc7de9dfcd636618ca9e658f3985a5012cea2ca6ab563fe8278f11dcce6125e5bf2a12c94327224da23c5259110346a4b73da70f4185fa0b8af37b0e07a38224969da4597d46afe76e9cdaa8ca4d5689fd1b0937eda8d61356bbad3b15ea0c58f61481eae11b0ce69e9b51601b10fba939bbf585dedc4d16654606dc1027a9347226537fb80fbc60a8ed630b9e7597af45de69291b72a304a3174c5099a4df909faab4b1bd8b6a99670110b7799308e26178829b7eaf4e26b79e33f5efbb9567191d42b7f2f112510408d5a0712d984d21786895987", 0xc2)
ioctl$BIOCGSTATS(r1, 0x4008426f, &(0x7f0000000340))
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000740)=""/196)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000940)={0x4, &(0x7f0000000880)=[{0x0, 0x2, 0x1, 0xa3}, {0xfa, 0x71, 0x81, 0x8}, {0x7, 0xfa, 0x40, 0x80}, {0x3, 0x3, 0x3f}]})
semctl$GETNCNT(r2, 0x0, 0x3, &(0x7f0000000600)=""/156)
r3 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x1, 0x11, r3, 0x0)
semctl$SETVAL(r2, 0x3, 0x8, &(0x7f0000000280)=0x80008)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x2}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, 0x0, &(0x7f0000000040))
r0 = shmget(0x1, 0x3000, 0x0, &(0x7f0000ffb000/0x3000)=nil)
r1 = shmget(0x2, 0x1000, 0x244, &(0x7f0000ffd000/0x1000)=nil)
shmctl$IPC_STAT(r0, 0x2, &(0x7f0000000240)=""/211)
r2 = msgget(0x1, 0x260)
shmctl$IPC_RMID(r1, 0x0)
r3 = openat$vmm(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r3, 0x82405605, &(0x7f0000000340)={0x224c00, 0x4, {[0x8, 0x6, 0x100000000, 0x1, 0x1, 0x2, 0x493, 0x2, 0x1, 0xffffffff, 0xd6, 0x2, 0x0, 0x8e3, 0xfffffffffffffff7, 0x10001, 0x5, 0x6], [0xbfd, 0x2, 0xfffffffffffffff7, 0x4, 0x0, 0x4, 0xb43, 0xffffffffffffffff, 0x400, 0x8], [0x78a1c8c4, 0x2, 0x0, 0x2, 0x10000, 0x4, 0x81], [0x40, 0x7fffffff, 0xfffffffffffffe00, 0x85, 0x1f, 0x8000000000000000], [{0x1, 0xfffffff9, 0x52961787}, {0x2, 0x6, 0x6, 0x800}, {0x2, 0x0, 0x80000000, 0x1}, {0x9, 0x6, 0x8000, 0xfc04}, {0x4, 0x9, 0x4, 0x7ff}, {0x3, 0x8, 0x20, 0x1000}, {0xcb4, 0x8001, 0x101, 0xffffffffffffffe0}, {0x401, 0x1, 0x3f, 0x8}], {0x6, 0x6e6, 0x1, 0x3}, {0x3ff, 0x3, 0x7fffffff, 0x9}}})
ioctl$VMM_IOC_CREATE(r3, 0xc5005601, 0x0)
msgsnd(r2, &(0x7f0000000080)={0x3, "f77e68f001a6c1be9fde3cb5f6bb58d94fe0aeea5834e1770d1fe612b0fdfebadf13ead33dae6faf39bbe09327488ceb0a7f928a73eb2dde12de66192a831153036a6c40c206c0a8c6fe90ab829ec416b9d01eb4d889daba3b7a5cccb6b8a40b2c941ca2fe2c09f3aaef11ef9f201e0170989ec54742668904b6ec086fdd953ee8cbdea2ba6f74bef3091179797d02ff56744e02d178e3ec1d4dadb0fe894436ce1e0a5323d5f6c753f7f259768be270500e73ef9b5edb0713ad8dfa1b7c5ba45452354cf177146a8964639885015acd20a8366aac4f0eed20a57dda7cd7756af2c601eb6ca9d83951cdaa22145dc12fd25835c778429935a1a9"}, 0x102, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000800)=[{&(0x7f0000000c40)="e56e0f3c4c437a23663442c7656484bb2b491e5b1f106d6154a6ddb91c4becf599cc0b8f3866600efeb88fd18ae3381690a2358f2b2db48a2e8e8e3ee2e5f1bfdb71511ff58f1c9a3a33e07eba902384e8aa5f2077c8d51a9cd574630219380efa79d78331bc3668da2147176e3428f8ded12f2462bd693deb760a81931ad6b7febd9b45733e607c873052c1a750cf70711124f9978f25961dd10a0a47ad8a469aa166220d42bdb509afd77c67b13f466bbd9d85ad019e7c847b630d6f0a343d9b0e568013b25c841e2b122fb1f7a3078b6249cc39fec76047dc2f6614a211b840f3b48a0a1543a18c9f193891dbb808399a3ebc28aec5c888dba6cacfd502d17cfa3e50ded0edcced8575193e93912908895deaf6086e76531d090e14dc2ab8686c2483d71866c2eafb5439bd2ea78d2820b8ca632d", 0x136}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x5}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x40}], 0x1, 0x0)
shutdown(r0, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x40}], 0x1, 0x0)
r0 = socket$inet(0x2, 0x4000000000000001, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f00000002c0)="ee08665d19ac14d5e51348771197a7728420aef61715f7b1c3d4b3830c921bf0817a0000000000006a89dbdf", 0x2c)
shutdown(r0, 0x1)
sysctl$hw(&(0x7f0000000040)={0x6, 0x6}, 0x2, &(0x7f00000002c0)="2c5c2ba4", &(0x7f0000000080)=0x4, &(0x7f0000000100), 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000000)=ANY=[@ANYBLOB="fb1800135408fc00ff02060000000000000003000600002002"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x18, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400004ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r1 = socket(0x18, 0x3, 0x0)
setsockopt(r1, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mknod(&(0x7f0000000180)='./bus\x00', 0x80002005, 0x514)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x10005, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000240)=0x7)
open(&(0x7f0000000080)='./bus\x00', 0x606, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
fchmodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x111, 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000180)='./file0/file0\x00', &(0x7f00000000c0)='c\x00')
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VNDIOCGET(r1, 0xc4104603, &(0x7f0000000140)={'./file0\x00', 0xffffffff})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='oL', 0x2}], 0x1, 0x0)
pwritev(r0, &(0x7f0000000440)=[{&(0x7f0000000380)="44dcc12f33a3f8e6988d3ac849f108c6168398b6a886540dcbf8c50eb277761493a1ded4c624ff53063782a2e7658d361ce1f6d2f8090e445feed5c5da9eed8327c0dfda1cbb2e4081a49aa7bec3e1cc243e4a3de6d1b4b4be6094ca75a9729290762b04604b4cd2b2d9a3d5bee6c44089a70fc2f5f0231f18bf43a31b8a96ae23e72ae80fa89300f601b3e65d274d0c6723", 0x92}], 0x1, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000140)="74e201887d347d495fd8ac509b692e", 0xf}], 0x1)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000340)="83eca8fe3800aac72d4750a3422e", 0xe}], 0x1)
execve(0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "d730c15b00f4ff0000747beffde400"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
close(r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x800000}, {0xc}, {0x6, 0x0, 0x0, 0xfd}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000240))
syz_emit_ethernet(0x32, &(0x7f0000000080)={@broadcast, @local, [], {@ipv4={0x800, {{0x7, 0x4, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}, {[@ra={0x94, 0x6}]}}, @udp={{0x0, 0x3, 0x8}}}}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$PCIOCREAD(r0, 0xc0107008, &(0x7f00000000c0))
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x6c, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x14}, {0x87}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x2, &(0x7f0000000140)=[{}, {}]})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206919, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x48}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x74}, {0x4}, {0x6, 0x0, 0x0, 0x7f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f00000002c0)=[{&(0x7f0000001580)="ceaa87a2610a44", 0x7}], 0x1)
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x5, 0xfffffffd, "cfe9691e70058333f34171bbea9b5144b04424a9"})
readv(r1, &(0x7f0000000100)=[{&(0x7f00000015c0)=""/102396, 0x18ffc}], 0x1)
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x5, 0x0, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
close(r0)
close(0xffffffffffffffff)
dup(r1)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000280)=[{0x40}, {}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
r0 = socket(0x2, 0x2, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x4}], 0x1, 0x0)
connect$unix(r0, &(0x7f0000000080)=ANY=[@ANYBLOB="7b022e"], 0x10)
r1 = socket(0x10000000002, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f00000000c0)=0x3, 0x4)
close(r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x6}, {0x2}, {0x6, 0x0, 0x0, 0x4000}]})
write(r0, &(0x7f00000002c0)="bfa80748c4b3ff8918915dc05e7e", 0xe)
r0 = kqueue()
pipe2(&(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
dup2(r0, r1)
kevent(r0, &(0x7f0000000080)=[{{r2}, 0xfffffffffffffffc, 0xb}], 0x5, 0x0, 0xfffffffc, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
writev(r0, &(0x7f0000000780)=[{&(0x7f0000000480)="fb7f", 0x2}], 0x1)
fcntl$lock(r0, 0x7, &(0x7f0000000040)={0x0, 0x0, 0xfffffffffffffffe, 0x1000300000002})
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r0, &(0x7f00000000c0)='5', 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="2009d75c20650a", 0x7)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000100)=[{0x0}, {&(0x7f0000000180)=""/173, 0xad}], 0x2, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x44}, {0x25}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000000)=ANY=[])
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
writev(r1, &(0x7f0000000240)=[{&(0x7f00000000c0)="05", 0x1}], 0x1)
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
pipe2(&(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
dup2(r2, r0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x2c}, 0x3, 0x0, 0x0, 0x0, 0xd)
r0 = socket(0x2, 0x2, 0x0)
shutdown(r0, 0x2)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x5c}, {0x45}, {0x6506}]})
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @random="9d8b1e9d2424", [], {@ipv6={0x86dd, {0x0, 0x6, "923149", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
sysctl$vm_swapencrypt(&(0x7f0000000000), 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1b}, 0x4, &(0x7f0000000080)="ccfcebbe", &(0x7f0000000140)=0x4, &(0x7f0000000180), 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x21, &(0x7f0000000040), 0x0)
setreuid(0x0, 0xee01)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0xffffffffffffffff})
shmctl$IPC_RMID(r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x1, &(0x7f0000000000)=[{0x69, 0x0, 0x0, 0x100}]})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
fcntl$setstatus(r1, 0x4, 0x4)
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
readv(r1, &(0x7f00000006c0)=[{&(0x7f0000000280)=""/145, 0x91}], 0x1)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000000), 0x4)
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt(r1, 0x6, 0x10, &(0x7f00000013c0)="6d66b176", 0x4)
write(r1, &(0x7f0000000140)="ce", 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
r1 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r2 = dup(r0)
ioctl$WSMOUSEIO_SETPARAMS(r2, 0x80105728, &(0x7f0000000280)={&(0x7f00000001c0)=[{0x87, 0x8000}], 0x1})
r3 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r2, 0x80105728, &(0x7f00000002c0)={&(0x7f0000000200)=[{0x8e, 0x651}, {0x8, 0x7ff}, {0x3, 0x4000}], 0x3})
ioctl$WSMOUSEIO_SETPARAMS(r3, 0x80105728, &(0x7f0000000080)={0xfffffffffffffffe})
ioctl$WSMOUSEIO_SETPARAMS(r3, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000040)=[{0x8, 0x7}, {0x8, 0x9}, {0x42, 0x2}, {0x21, 0xfec}, {0x26, 0xee64}, {0x89, 0x2}, {0x47, 0x889}], 0x7})
ioctl$WSMOUSEIO_SETPARAMS(r1, 0x80105728, &(0x7f0000000080)={0xfffffffffffffffe})
ioctl$WSMOUSEIO_SRES(r0, 0x80045721, &(0x7f0000000180)=0xffffa18e)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000000)=0x4)
ioctl$VMM_IOC_INTR(r0, 0x20004401, &(0x7f0000000140)={0x0, 0x408})
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80105727, &(0x7f0000000340)={&(0x7f0000000300)=[{0x82, 0x40}], 0x1})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x3}]}}})
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0x41946472, &(0x7f00000006c0)={'./file0\x00'})
getppid()
sysctl$vm_swapencrypt(&(0x7f0000000100), 0x3, 0x0, 0x0, &(0x7f0000000240), 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x35, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4f}, 0x4, &(0x7f0000000080)="1daf63b25970aebd36020d25e1b10cf3ece6b8be02", &(0x7f00000000c0)=0x15, &(0x7f0000000100)="c2d24c9d34fadd0eae82b8e3168fa740a37829960f8862294ace4e16e103938e10e9fef2d403c0954d8babb49abbd7757e100f97a12aa77f902737ae02664eb0c56d03bb914b2b2a4c4cffe51a6e41cf38d5db8c47370140fb18077b6f8e963e39233b5a0e053e6c86179c589f83b7fa0105135c0b110bd373f3850f556b04ee1ce94eb6e707ac4ea0df9499463f04a489a12e300431c0e42d390839498f20412301ba6400f392533db65e70b870ff4215a0c879ee965532ec5852910377cb30a7d9cf5b1184b39d9d8c7dadb9d96aca5c946e6f5c5c18b50a", 0xd9)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b10005016000009f05001b0007000000331c13fecea10500fef96ecfc72fd3357ae36caa0416fa4f376b36acf20b7804be38164991f7c8cf5f882b297be1aa5b23edeb51e2f0ac3ebbc257699a1f139b672f4d335c223e7d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c500002002fbff0c2300008abfba0900000008e371a3f8343712051eeab71d89e00004070000008004200000", 0xb1, 0x0, 0x0, 0x0)
execve(0x0, &(0x7f0000000000)=[&(0x7f0000000100)='r./'], 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
syz_open_pts()
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f00000000c0)=0x3)
ioctl$TIOCSWINSZ(r0, 0x80087467, &(0x7f0000000040)={0xa8, 0xfff7, 0x1ff, 0x2})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
syz_open_pts()
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000003c0)=[{0x20}, {0x74}, {0x6, 0x0, 0x0, 0x7fff}]})
pwrite(r1, &(0x7f0000000140)="f94c4c49dfd685fbaf8a8d1a029b", 0xe, 0x0)
r2 = open(&(0x7f0000000300)='./file0\x00', 0x0, 0x0)
r3 = getpid()
fcntl$lock(r2, 0xe, &(0x7f0000000040)={0x3, 0x0, 0xffffffffffffffff, 0x100000006, r3})
ktrace(&(0x7f0000000080)='./bus\x00', 0x0, 0x1302, r3)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r4, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r4, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000003c0)=[{0x1d}, {0x74, 0x4, 0x0, 0xffffffff}, {0x6, 0x6, 0x1, 0x7fff}]})
pwrite(r4, &(0x7f0000000140)="f94c4c49dfd685fbaf8a8d1a029b", 0xe, 0x0)
clock_gettime(0x5, &(0x7f0000000100))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvfrom$unix(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0xa)
shutdown(r0, 0x1)
kqueue()
fcntl$setown(0xffffffffffffffff, 0x6, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000ec0)="44000f3e265873b91d1195de6aae7fd0cc327233a9ff18e27ff8328f8251e84ab281bbadc0f1b0c9dd01a66c110fb46800f5fb1830aad6f149cd793c3e9703e27bc23ee4e7747097a4d928098f2d5a4f468527b58a5569e6cce319ea39307452d1f9fafe54024e3fb0ba0ae44677cc64d889534d97dab3a38dcd661d32c19ac82ab98e8b9c86930092eec37e297a0181389db8759d071488187d169caf2fd0b36667a99b4aacbf5c5d22db", 0xab}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
sysctl$vm(&(0x7f0000000000)={0x2, 0xc}, 0x2, &(0x7f0000000140)="ad", &(0x7f0000000040)=0x1, &(0x7f0000000080)="5d06da49fed08ee216613f26308ea2b0eedf64aba8eb020daad9c9da580b579d34041f255941b6b5369c69656e7bab42c32a5fc4ec090a62", 0x38)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecf0121329f7ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d02000000000000000018e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b5257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
pread(0xffffffffffffffff, &(0x7f0000000180)="4b9e86cd40d7ca01f62169813f26f2b8f3f8fd0d2cdd826cd26db00ccfec40d54fb73a734776d74bfa00bd1e615efc633e7e785f0fa8127857570541fbb418ae4b34c47f0da0df87242d93a2f33898b70fcd37ed2f43440195c19e5ef7217f2956c9f5dfe5a8b03d9d5aacc71aaf70042723c837fd8182e4036a6ecf4de9c3135889c8e3682f582f06a73dc97bd48e097f1d71e3ef989677ae4527d292bcd40d9dcf778c08d47ba7b087b07a135a4576fb365699dffa519131199a6be813e953954e7a0eeedeb814", 0xffffffffffffffda, 0x7a27)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206918, &(0x7f0000000300))
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f00000014c0)=[{&(0x7f00000000c0)='#!/', 0x3}], 0x1)
write(r0, &(0x7f0000000140)="0920120000feffffff04148027454a638a711de274fe266ce23205554752cd8439df6dfe4e0f84b94aaa41f680fa371c960a", 0x32)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
sysctl$net_pipex(&(0x7f0000000000)={0x4, 0x22}, 0x3, 0x0, 0x0, 0x0, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x35}, {0x44}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
sendmmsg(0xffffffffffffffff, &(0x7f0000001880)={&(0x7f0000000280)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[], 0x12d0}}, 0x10, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
r0 = msgget(0x1, 0x0)
msgsnd(r0, 0x0, 0x53, 0x0)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x3, 0x5}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f00000002c0)=[{0x5}, {0x4d}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000100)={@broadcast, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="33306350af88", @rand_addr, @remote}}}})
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x0, 0x2010, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
accept$inet(0xffffffffffffffff, 0x0, &(0x7f0000000140))
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x0)
r0 = semget(0x0, 0x0, 0x0)
semop(r0, &(0x7f00000000c0)=[{0x0, 0x2d0}], 0x5d)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x2001000000009, &(0x7f0000000000)="3c380652", 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x28}, {0x7}, {0x6, 0x0, 0x0, 0x90001}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="2eaabad40041708e7847e1b7e237", 0xe, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x80, &(0x7f0000000040), 0x4, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
dup2(r1, r0)
r2 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
writev(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f00000004c0)="927d447ff8ab2cf496da580000000000e900007cdd8e6a0a2226ae149c94dc04ab2fe3f2a3475b053e7cbfcc5895106dc01f5c0fc97b2ab1a0db9290dfc2e56735ad9de2f08e94142b92e1c1ea8b52add0473e1c06cf7717a64650e1e65ae20040bdc5ed1b42c2a0c67c9d03507f844f779a3be04aee93f14498da39a5f73da1e02d60bb51a75ceaee2b99147551f04b8bab2ddcda9b95fe641791dc608d7b785d3815552076a00dd71556bec9218a8f5ff1150fcdd1b8d27422e688ae7113bdf201cd417f8011430b6e5ee1bb4852ea6b55a3a80d26272def40a15a1635f77e9691e583"}, {&(0x7f0000000080)="9134aff4f5c4c4b3c3770515a942ec0d90ee52008a0f10e5d01b5c793dcd1aae2a58807ab3f237329df03a39e4220334ecab8ad197cf5d9bb2d06989305f1b4bb8c448a6260405143111cfeb56aa17daa8aac91e9bded0491464279f89ed3f82a9b29f5c189d1b2947bb0e421dca56afe6b829f0ff6ecb699735e105b9941bfab82eef4759dab9939afbdd2c2153316e131bb2ce901a9a284f2024e84028d8400a2da602722ce5a154cf148ab3a3fddaadfc3d3f2d1d55eaa295630a354e378abaa56cd39bf63cf3dea192169a12276a4cc95010445e09369d45d11553893687e65a7e35a2ee6b2a85d68f9ea8f1ae3aa894dacc4fcf8bfc92b81e62b2890b"}, {&(0x7f0000000180)="e8a6df860b01cdbadd65ddb3d0ed1bb87ad45d8c6405d2739a045733a29dde5e1a204862394d914fa1a1dd706f5cd583e6991db7cd0d146e1a2feecc00781f879ca551ed0f172ed0501c5182baa0fc32cc4dcfb4a939e426145f0eee4b9c84f3b497"}, {&(0x7f00000002c0)="95a9043cb0b41565363758dfca66e6d0cf316e4e429a3d68185c57f388fdbfda178f676d5ce3c5ec84bf48ed744b9608cce61587db0472d9c4849a29f6adcad960f4ecdf2c9a8908dcf31fbcda6790b64b51cbb1f4b820b193ef6c57ec148597eda79aa30fefd211c56ef4691f581186d93ce12bcc3001587ec6e5ffc4ddbb3ea5da8dba3dbbb66a893c21beaf407b95df8dd69df826405e314159"}, {&(0x7f0000000380)="e367ff79fa76e0ab67e8a2ee32a28582d97af47e56250d34a47cb1daa05b7d88251c59427c1452e224665b0a3415df88a4769764b57ce565e913398d02d91a03de454aac64e76dc48d9b6f657ffeb3037934bcf7323edce5edf58f814bd5ab27c21294686cb0e93cbda2fbfbdb8bd7f43fa7e0cf6bb0057e7a7ecfc4908fb8d293bd9e196dbf6ba732649e7155c3a7298a575a5a8de7f8f09ebcff10a44808ee147851aa39ddb5"}, {&(0x7f0000000200)="816e4eb9c4d57e14eec41c8c691ada0bb28e937bd47883b9fd07600ecfbe0ed0984c195c575ae0ef2f23899f5ca40accc9f45d3cc3f911a420d5190e4c1f61706c9979b390de77e48dc5ff75fb"}], 0x330a382ab4219e2d)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000000)=[{}, {0x5, 0x0, 0x0, 0x4}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="01080513600000000000ab000004000000000004fecea11ea8fef96ecfc73fd3357ae2ecaa0416fa4f376336acf00b7804be781e2fc2caab610f53c2297be1aa5b23ed00f4c8b2ca3ebbc257698f1f132e27acb57ad602000d7d026ba8af63ff37", 0x61, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100", 0x2, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
socketpair(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendto$unix(0xffffffffffffffff, &(0x7f0000000500)="09afc1b9bbe705ee008a340996e16895", 0x10, 0x0, 0x0, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f0000001680)={&(0x7f0000001640)={0x0, 0x0, &(0x7f00000004c0)=[{0x0}, {0x0}, {&(0x7f00000003c0)=""/209, 0xd1}], 0x3, 0x0}}, 0x10, 0x0, 0x0)
recvmmsg(r0, &(0x7f0000000380)={0x0}, 0x10, 0x0, 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f00000001c0)=[{&(0x7f0000000040)=""/41, 0x29}], 0x1, 0x0)
r0 = socket$inet(0x2, 0x3, 0x89)
getsockopt$inet_opts(r0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
pwrite(r0, &(0x7f0000000000)='G', 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x3}, {0x83}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x1e}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{0x3d}, {0x16}]})
pwrite(r0, &(0x7f00000000c0)="9dcbf72c8cd8787d68609f665eda", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
ioctl$FIONBIO(r0, 0x80047308, &(0x7f0000000040))
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x97ae, 0x0, 0x5, 0xfffffffd, "e400137c007788511276ee83833cc800"})
write(r0, &(0x7f0000000280)="4de3a767cb3572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cc3392c70fb4d2ea468e980877377f1cfb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70ac323074dd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19a3dff7bde555c03c89296384aa8f8b1e7179729ea0bb9ac633eedb29996c3da18b7af5150085f27cd1a07a7e9b60d858b2f1e5800125da2f78713e82f66341bc07c02b83799f6c872f9fdc87b82c4f4cb92ef1292f1fc9abaf5d17d7804a678fd8601aab32be210c18e48e1287828d903f336f4ea7342c526849e1dfbfba385183f622d1e4e0e1498329fb523d2bde8182cb6bb71d0c7c7cff135c6348649f6e0e61785ce8cde972e9a411d8c951c3f45f83161b98f84c13775ca1", 0x170)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000080)=0xc)
chmod(&(0x7f00000000c0)='./file0/file1\x00', 0x19)
chown(&(0x7f0000000040)='./file0\x00', 0x0, r1)
setuid(0xee01)
rename(&(0x7f00000002c0)='./file0/file1\x00', &(0x7f0000000100)='./file0/file0\x00')
setrlimit(0x8, &(0x7f00000000c0)={0x8, 0xd})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97a8, 0xffffffff, 0xfffffff5, 0xffffff78, "5604007c0300e500000116008d1b82834700"})
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000100)="69a76608ca565019deb14e286120985ed8471dcd6760f7ceeec7019a1d22bd5e4d97e2d5d1897330c0d68d58c2751b8b7e772e015d4549", 0x37}, {&(0x7f0000002640)="a7cef3a63a716650986bd1cebde4cfca7317d501e364066e7551c8c5b2c244c1602ab76f23d57fdc653901e5ecd8d473b98c22d7ee5bcbbeadd7ebf2dad59dbecc2646645d8c7ec3032a16ef9798d0436f974d3fd75260429560af1188af270dd7b33e235fa62157033e4b58ab764418a1e9ab5fb3518e0e4e415de736cdeb93ceda14c563d17efbf8b7f13b53b2aac075265881de2a5371e08234ec8df6ecd834824762d6bcf755c00a7bf680499900db3c3ef24b28696dbc0cb8b52ad8e4f083eeb012090de42a731036a89fd5aa797cc578f75279ccadf8532a44f1f83fa90eb613631c5cbb27364c58558b49f35a87a656448866230dc67770ec66760a3b65d30be9d87e040f7cb72ad690e43693e0261771ebe6a1574fdeae4bbbb4ec08e65abba8e90a16c183f090f3b96e1f4a5dadad9b5781fe4852bb6a864c89d7b1031a51eb14c561fe18f347e733d739e22c5d205bbeae1d3e0fab909d3af83681933f4327940543a2455e379ec5cf9c748365bc471eb0b42124dcd74b72f14dd65cf34931554363c70507479ba95d4ff88b09d49e93461a72d7df4287a84ca6b2adebba608bf2f0478028e015b0ed3d5108d63a60b647fe0e046373225d8914ef51f51aef41738989061be500d2f84045e752dc7714a32b07e8d25dcd8a74a633ecad864f85aa3b42c983d1a172f35585f3457f219680341c7b0ee735f8214e2326a6f21b06ffe3f9952fa695e5cd876540893ec26e869262c8286354250a7c231fc3d0952b824867b96d2f2c5da2e3692416f4eca2a9c8544dc8b66bfac414b956418b3162a7e3ed16ef5ba2237f6d52f085372647a5361f6b68db767470dde1b01f035acd25feb5aae0794d8109c2a9986841278b9b74b9f40d23ea610990fc9e3bd5df1c9e8c195b25c2c323476407627fcb388c2001109ef0f92271461c1b1fd16e322b11091e9ceecfda1f788f1549fb2b92b3eb3486a7303a69c5d8fa150446fa9907d89e014d50bc1f99a1a1b5d9fc54ce41d082d7cb64021ccdbd8c0f1ffc174049939e24bed90db18034351c59452e657ad81c1bc4fd6f5f985ecb8a073aa855dd8ed5f526f605677f9222296064983d436f6d9f054de4d06ec9bd0a53f19c47d052b10672fed0022ad083f9e997bb220ff82f40bae4b221259d4cf220377911485dd1ffa1a61e42b4ed1e9b5fac85149886b5aa6486dcc2182ebbd225fb6e0fa54bb4b3d6953e6b5baa04be07c790ee416f7d4e94592b35026d12a90454102a31a15cf7aa8500dd598054eeaa4cd62c601be7dfd4c6ab0e4dfaf96822b36bdffeed1e1904e5d82cf9ab2848793f2d2de0485018ca2c8b0ad356fa22558d22c097f78e7ae4dfceff38614c7d7bc08e1949bbfac2de249b2e10e777fda9222da911a49787df13ffac791db61719f49bab86640576040060b5fd26e6e30814a6b9576a209d44a513204cd5b0d361429b5f9ddb9dd9589a1962f09382731e7a54dab1ccf973baeb4db6c8af0ea55c3069b1ebfb5999d8d0cc5a77d10370a66fe640cd4a3af8c38c8624147b223f37a05810917d7a4c0e88db17640311444151c88b137b7f4ad90a1cc18f9f93fb5c64bedc9538ae7cdb3aea6476d7ce2382d85dbc718ad636bbc38589d7e6d736bff940b2d3f9f6a9c061f0faa16d39aa2f357d39d44408fdcf3068cf8ebb4de9ed59dab284350c8a82e05c4c9dd78cd9677c9afa52defdfaf9447a0d1395ff928a96b4ae5b0bb0828707ca3008c95debb950a0aab7d6d556e7a0e7be7575eeda7c745bbaca13b53578d83571f39538d5e98826c6bd9082a265d3db802e8c3c30de33a659a58cf0c7cd7e0ed8f847ebb41a5bdba3373bddea2a14bab20392485b6f724995aa33a4c5d7bd071b7454bf2ea27470b2448a201772e5814c8eaced5db5d4d46019e966fff632eb1e83e006297adff5369e6773a14313f05aa9862c72a9d98f0cfa811a8597bddb8a8b4daae7ecf9ac8e2e473e207bab3c1434f9d1b7bb3dfb6cb4a1ca901b6ee5ab2624d8f86e390a8910a5f8974fdea3a5e8bf089957b44a15c089ee6d84abf381fd498e72e55b697ffe39e8e320c14bb7f7a19694a0f86bdc6acf2d925324797c7b263f04e69bc0e550b8035a0888e423b05cb707378f7d01c69fb5aa1c93af35b96edd118de75359a7e92db91b50052f1c1bddb21516b5070e0ea94ccff1deaed628af91bd01f43e503406d5a69c7d463944c56675a874255f31febc0ec543ca9a7b8ff17e9d1c5b3a41ba84d9d76980382a6cd8223d20f6174a012418d39b7e73758ff5715b8e2ccb3987aba4095338f434b3676b163e92123c41cb3b5c75e1ed2bfa4becfdb440d9f4f4d470e474a33a99f3061756aa978a88e016f14806b4e5386a7726a27ec8b7ee9a8e1da193f92648641098c7499c068af206202af608a8fa0449eb203f5e903fd3c878a16d83219145ebd6a337ffb8acb5c31d0a26c18fc5c5186cfeb4dd664006e955d9ae5163525cfefd274d94df4661c3bbf8838691ea06e63db4c95146cf9c85db85623ef00657cb6b689ad88eb473af99789da206a9f5452eb01107e60c08a29049fef244d6bb3a2771af504a01b78edd71cc8510972f7eb715532cfc3488e65cf684b84089550c66d957e76eef76325c0f485c1671ff55604804dd8699ec66f8f454ca0bafa66e2918b533b8bc015429b3e14b80dfe8cec247b976cef0266be0f50f7973358769be36935e737ebacb9177ffe8f8f9c8957a8708ef22aaa053fc72959560d3951f4cfb0397b21eee91c108c016d92c72deef8efde804792b351a11849f8ed56cad237a2c3adfabc56b85532bf8d0cb7832e482316646f6cd316f5a1f5bbb617d8056686b687e31d121573a84d4753b4bcb3708b05e39d7deb1a15f9d6c4699835e9b88c989da343d2130ce0660877f793c27d4e8981176989371e53095449c3e963d998c7bd7139e5cc70ac6791883daa2d5e857ff084d5cbf16c2947de2c89f660e34171acbb2dfa5790c2b3e261e9ae3f4ba32ffd09ba89283623e6578f323156c164f12eb2fe6bb57d6a22af2ba41636f1d560668be91a7f1a7fa1b9045465e76376d18a8ccac353123c0a9f403270507da91cda02e0293a2fbf8381265d2ead29a7e1f3924ceb92698f1080a", 0x8bd}], 0x2)
writev(r0, &(0x7f0000000280)=[{&(0x7f0000001480)="c0db3159d2066fec2a33b40b66b725718575aef4af9f59c9e4c5fa0685fba0064ec2f5c1d8c7484822d758066448bdc3cb2d1b9d98bd6fdaacaa9efdbe3ac67dae14e8066e029ff3d5fbd66466fecf70c2ffe4faff897cc8dea355147986dfbff4743dc58ff136a4d8b6309dfb69cadcd3f675c400e60e3e7d50f4668afaa6dc7de4e80bcb7c6336e73da833a24dbf1a1f1d10e056ae940255f119579386dd0b2f600767c3b5e03bd379a4ee31c123c64a67396242d448155472f90f3e5b7b7a7929ae75dd40e1029552187310e441d7dceb42e9a10481c40973cc996c52bec87583c5eb86bd6e2b8dda2c5cf46548fb2c8e7f1bc12d99fc4983bbfe06da04a1ca588994f932945d40427ddf1494a5722fff63f49be8a97252e74fd59f8b4217229923e38536a84765baa589c4a91609689b3e2473958d067087ec1e8129ad5ea522379682b3ef5df5f5bb481aa0a9d428b600ac483ffb9d219db97e245e57b71b0a3f03d88597f88065068b2ef3cf9407e0557582e9431026f076d14f6c152c281c59f7ef663ac9ed32d5555b8389a9e86260af28df53cb7b70e8ada00661fba897a22fc8479a596e55694f246d9bd2f43507f99c0b002089107346da65738f1f067e0fb1e6cc13c5e6a7b882c3f5e3380e4a6de058bdda8318c5b91ab78d887b56c19111cb1d0a595a8f02bacabe883f854c1af4b8a9bb09d36e5030eebcda9ceb330ff5a4f96941d0bfbe6cda5de578efdab85ec5ae1e82196f2f0d0ba0a0efb5dac7dd3ea27a3020e56905f56b109d0a9aa514f41835b5f917179b1d1d1032a087e19a021e08cad5984a4f9547374ae2beb1e7c76b53727608d3f3e5256aaf590fd3b2b72b25d1d6a91f364ad0dd2dcafeeea2324f7cbd7b6c8b87cefa1e56fda22d3cca3165e87ff6da1a0c2fe05944baf1b45e41e6748b2f9490220138bed168cfb1c8d228c751d44fab707624fb8d91a2b9450f78fcc46ae3d6032d8f4731fa97ead3b0d9d6baae6fafa442478694ca7ef5c0254a64ba4c32b329a073c8201a16ea438ea4172b4b6707d9d1aca58c371840a5cd72fb73b2228997d6eee846ec0fd0d5f37c34adc29f1153b50642d54d76e33db94edc3be0fe1ae755d70f2665b8f93fc743709a8d7f134ee27d8c329f7ccc5f02acedfcb951bbc4bee09362f52062821fa50fe2560c7de319499eb2bb480f33fe678c85d49e2454361290d477b7e37b24ab96f0561c6798575469134eabbb5c2a7e0c20ab48ab7861f27750ad83244b79b536630431359dd763b9952116c3cad0a2edd59e7804c76ad50291db3ca5a4bb37dc2fd44a3539f629eebf7db10eac65f3c680d0f8ad0328922e3a906a28513d90de024f98d7a69735709ca67c802de4d6c94c8bfedbc4d16a64df8bbae5308a127c3a0fa8b53dd288d528cf43f53d0f0a1500d8ec6d69d47a4348bbff5fdaff3bc865c2d690256aca23f03f891bd0f67d8da8f39c0585e3d40dc0e4ff5cb0517f3583f362c20fca8ee55a944610b6194a5b3654a736a0d65ed34dc3b047e9695c067dff741b7a4dfd66e07dead80b3d29797e3286fb39beb535d1c2b0ec089d3f384c4340798139af026a8dd57d9299a7c5051f63280738470a44da2ce95ed65b9ff289b13e1688fffc11f6c2783d802fae4e7ba04e52ad0a044076bc18e1d29a78851fed0d4bfeb5b8c142c20410f72eade502c72f5aa4a583fe9230683ad136ca6f9f06437ee0555c2c29339c77cefb0c4a8ed767d2c5ee3313c9e346b193cc56c027a50a0ba52aaf1ed040182617fe9dacce9fcab675915a3df7810bc766d65be94f845ce48dd007d5ea75446e52fbff334899d9beb4b1fc1e872a5ccbaacc383b17a34147cc6969d83fdac081fbb53981b6a78b628392afff707f899f5b3176a79d5c81084d7bea0a650356145c65cfb8a0880cf268354453ef4dc0cc8872d63b1e44dab040018b8347a5eda656797a184b5e27cf5949d56822861545d50a42538532b10b84c47aa9c0a78bec0ac45759434575fcf71c62f231584498e39011045bf883689c4bbdbf8885f016485f1f4c244341445e8f8a3079263d28bc8e1e0301695f87ae0db8d1a94e8d93b9de5775b74a7ea57058a60ab04e7e162d055f107cc3743f367e8454298cad165380a1f4382b6d75e1a8bc8a5b75e5d45df42e72f603d9c1e06450d8ee062d71bb2c562ce2fd878477bb0b983f12c682ff30fe4fcb1f3f71f3254da39a075840a27319c43af36713b23149f493250a4a9378f0492a6be45eeb58bb7f4365bc99a970324e930bc5558291c4a60ac4de554f25cb928e30ad76bc8c2684dc0b1eb64a151acad823c9be854ec27083a3563f268bfd6e7b0d6ef25d3ea36aae333c2f90314096fbbcd3886958123d590791f548114257cd5fbfc3fa5dfddfdd0a6482c15c0e448b5632421dd21c7f6f758e8d5681ab638c9dc57bf360f91706f6b8d7d082933e662a7198cfb85193fd93ad64ee58d0e126f66007a46d699ccc288bff46db53dab0ca1622493abffa11416d80b0cb06c1e246776c2b9a69ff4d5f86dd020bd93039265e7e676362e2095740ffa8859bf532257c68dad4e734cbdc8a947e0b7b8d2632f2045f26381786e1b9ec0df834416945ff0d3d61501935ab4b22493124bfe9f5225aef4596b3004e2d226f055982d3f0f052268a66ae1b4f3331514e58132b68ee6228b9aaadae38d6c8838251326d1a6a9628ee27024a432551f55f0db315066ff1850c3e13e938d2976006d8def27cc4200453627741b01bed5a18c9e2e0d95128f83eb0ec2d9dd7ecf497972c8866067243a95564255d1ce407d19af6c1b36d1fc4c9c81c9d1b3c8ee112d519701835f65c4962123a887fb82846b114269cf25724065f9bf511c06e9058e0afd33432b36e07f948800157b6dfd0172a05248ad5fbb6cae3268a88426b200edb9a98ae0c8b1f350c3196ebf9dd00fb5340b1caecc080de97834af3b799dd1cf7519b712883e37b62a434f70d6dafed6c6dd7bae0b5faecab8f1a7fa91677a31bc2c2b603be3f1e85b92dc0e94fc5313b7f5b74670d3122a6c1e2d13362509e24336bd9e288765c44b50679793c9d478b25d52d1dc771e3ba27558db9c9e5a9371ffa655a4a408c8279a6d17c0793556beab8d247b2c8fa6d3c20056a1326aa57138e500eb41ec28df84c5422020e62705051248cc00519d8077b9d4270053f15032fa6ca5d6733764fda4accbf21a9d1d544a09e4e6e87bc19cb7dc7c96a9d7e74e65fecb521e63ca6599381cb9e9d6c05057750868a8669be0f8fae388b33a4c8ca9f7e35f5cdd0d9de6259c4ff5f75050b317dc5ddb3329fdefbd951aee12149a1b96e5a2a1cabc00e4a3993edb8cdc268bebb6243de8ca312b5a8d97370cfb2e543e67a1ff9390b15f882b4efa7dd4120401d66212227ddfa2c686feabb6460fcc44f3bf566ee824c764f3eddddc45726703808c18ee3256ef3867270deb0d124e26b7e178a19657378d7ebeea4922d415454a0e0f38cd0a6c940826115285cda0af8d1187715bc705156b96c3ad5c002f079c75ecc70c6cf15d7a55440cf8abbe84c7be707641c2dcd92a27db8551c73202bd79c7b1dae2271abd298ff9c4e9965a5f2b1e6c96c34b9d8df924d7e58c50f582a660e14da7d46881963cb25d20c77bbfadceeabc7dbf3967fd281f45835ea6448fe116bec166d436b9a18cf8d3a018dc727efeebb1e96232003c208be3cf9de75bac511bd807dda92749c8b02f51c2cc25c8b5f8dfbc99479f19d75f43dba3e9c45021fc4945863a3c3ca4276ef2dcc3d9b2d53bec8f30a686a3efc162ae884cf1a8de9397604102d5bd52f60c94ef41c832332171d7c60365fdd4c888c0598656bc31a33e51c8e4e8f096754e4441294bf66adb990901806b39c9584d142182af4be39f78da3b3a757906d4199e479ef24a381773f485274ece066a5b7d8afbd5fa22fbaaae697774bb5d84f81ce6a6c631ef4c37f9f793f2804e95420e0176dec1e23e9719fcdfe8aea600ca9d4c377edf306612ea048967bd31c77f69838c114b5f626641e639cbfe88524fe0047cc7b74604537e22d65156ced142ed850fc4ee409ba38e65f8ebe8136ef1d28aeff5bf76c6b510249792fe0a2917946a2b649c64a6e1da90120ac95654094bcf0336a16dcfe26ed1d620238cf4da76df3aaf1cecd31b9a245b84a8c879dcf9943afecc64df883b098fee52897971e85b137fb10c644ccc655d3a62f8f31cb7280b85ea4cf302b67d17df66c4b889502ab34b6e744a7e6f9ff03c217fd9148207e0d90e341856edde290841ce793b2d2ec3a7d07c8f06ac6bac3539db18b872257c6487808a23281d2861cdde21aa4d66f4b60f0ef9a8cc021db545160c45e19b22fe402db943ac859393860aa62fc229c22435db9d57706459dd74a5dd75f421420c978d124f4f6c0e1551404f94429d8e447982383b7e3b5c564b1ba8779d3cb01e317503d7311c6f1dc3cdafcfe7553140e70d1eab1d91bac4554b3ae0934407ea5f48e2562b329d4a1a1ed9a2af804ee45d502f36b6959b87ad2c6094f35341b9adf8aecc7ddc3487b524ac496ad0efd75fe84ce755d420c9b712a8db44a470dc35a9ec20d613f46701c23cdec0a0fc5bb7e6963888f0b954838e23bb6624047610aa50d9bf1626a41be2d277acdf0999b7a1cb88a7f0a3d66b47db0e2a6ef705db01fe66ccbfc19a0a0253d912087c28a62abedbfc0cf61908432c75eb76a2eb20daa802aa4c7d477e9d531077744a7ee1ef96837ed6fb1c9a6a144477612c1c5", 0xd50}], 0x1)
r0 = socket(0x2, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000002680)={&(0x7f0000000080)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f00000024c0)=[{&(0x7f00000000c0)="ee30192f645d144a9d68c7d5793bdf4288a1c44c237342a3127248c737d51bf0811a6500bf2741e8a7c669416b0825f0ca6ec638c396715edf49dfc42f3abec7e01c32f83faed74454db0b5e46be0685684e57d4c904d6406c43a9e7b05bbdf24e74f8beb78ec11001857c8a83297a2cb72356ae73c9da9ff4b73bc5eda921", 0x7f}, {0x0}, {&(0x7f0000000180)="02708d5fee391da0869a8d21105c6dd75f2448dd072814bf7c927a3e5a110a4e6f8cb12e06d90b063957c89ec06c9330c0a94df20e465f163953bdd6683a43cea0f281a1340a3430f67c1b97e8afc32164a61e0d52716e69ce6b2e00eeb04aa80735b87bb5c0bd03479abf1df9581e715b4eb1682f301ec06d6b6605847259249734043ad7b621", 0x87}, {&(0x7f0000001240)="22887132e9a49c1ba5c810435781891fb7a28be38388eb2e5026e8e287947f00c79eaec62199326f7102a89bede25f011d562dfd732c07dd2fc20dc8563f16cda6e13805fda7102fb376cb29b41c1ef04e7408d224338624259182bf72b8a51d99d34250ae157ae76fd0bfd08166f39ac617d2a824a2bed96e7350769f4a16d9", 0x80}, {&(0x7f00000012c0)="6beb6f4af140867768fb039d2ed4111733875e55064c276bad547803dbd923246b3613f93065def01eddc4457e97916f8d64241fe0420fa651754119c2073d7fb3804b892c81d90956ca446413a94107832e0ae49a17e9379fa4d4aa0cfed68a7287980983f5d39ec9dba1b60c3123bac5e3d39d7ab8ffe96f27771971445a45d9bef18ad5371c69e32baac5837f46c45720edfeb0d3500773af4b5dd6302cb93680c17a4124ac00d606b0b4cf296d4ab9c5865d1cc2aa4c5390d3ac75782ce2471dea91576b84d433e238a2968d514c45cd4a3adbb119a94b344a7bbda1cc0cd447eb5aa5a25c130275c551194bffade049987e34d7427efd9bc8b65a7ea9bfd5a95dfb7243cdc9c9a156e641aeaafbabf58eb9f0ce0b1a11832ae55b6cb1e9d575787e4446f151eec8f61ac7727ac2ad47338672a1f02d0d6be1bdda81e172474c816031944e3e14efc929dc4c096f5ce5ea967d9e6fed0d435a43185186f3ce4494aa3358356f5a59518e3f36e157aaefecdcf6e8f95e2e39892793d7d21215d000c228f0bcd345434eff210a01448bea75b39baff7f2c0fcc592f338d1385484ba756a19acc09b618562d385036a2fd3b88dac73f28096b24888dccd26bf5eee41257f3d453b80ec7513b6082b0e0cd40de70a306414bee3eeb9c41fff8335e96a66dfa4c25d00cdbe69335f2cb08f5c3219abcb8a3f9b97f5c618b8f9c5ce797c08ef3680de6bdffbc9d3b0aba63302b03b2a3451962008b8238d23e6b0f95206bd6c679a34cf2da21b34cf74840e08fb9ca3d5c8b0c4a66e13b39ab5ca70c2c11af64361c1ae745e7c84f1538997e90a5bcea4e4c5dd5f05a3bf573a755acb2fdbd60416d7e811e189172a69b97a2d9a9af75ac3e3c4d25c2d67625a4c8ebee81c41d5d9173cf27e07bf254760763af0e00538970e49865d11075f8ab5c5d773a13474b84ebc0461d93b353f55665f68558c53504b65700b9161aa0f75f2c8dd3c827f12bc5b376954b90088eca2318cbcde7f26b562f706e657114e05ccfe5fe21e269a473f69a971380ae357a423e81a3930ab360c4c2a949a5f19623051d7743d57cb61464b4bc4f08603b38a54d699cb9a82d36e3f8b7042ff93f3c31d9408a6af08847959ab4eb1de6f2b7e438ce16f8610e66919a95fbd00db49cbf963a13f00e9cb2689f8875eb24895140940f5f239c25e5c772ab9fa2db12932f5e57c9f9c6eae67334547516953e66ae88420aae21f275b13f63df5c9133f2ef2abdee1e8acb2abbaed24a0e1250793c1455bbe3202e7831db05988a0978facf89a21755476acfd12b0395d10cee69aaf6df140a461c6911747792bc1280e99fb0a9badc1c3aaa5b7fe4bc84202ba65dfa4d4247bde40f5e45ffc2c8b139ebda3f4b5ac9b526cf9b1f727f15956daef41b1fa9216185a4405fb9da90a4338db5f985233e6f1479792aefe1a9dca4836e9de83e830d862138ed32892164561a1cb7a56d610d503c0e35342796d0d9fc8d1059e390bc3c76867126f6eb062a515e8aa6c8532e411b42782ddbeeab16ba5fe120e6dc93f84621c5011405878160e9012302c72172e3d5740fe6aa6d4e42be3310fb1f0338eb71f57d26a371a4c3870a6f1884516ee55fd2e2a0d6a4d22482a8f4fb4aa9970591acea1bf55ac95ca55db3b9e1ff4b15be29a354bdc2a2e78b75469c55a1a530b03b56f91978629b1ca8b44f5567c4ad773a05fa3947ba23c349602f671a4877d01e565ad228f90794dfbe6a9fd931a3374ec1e2173eb89d8ad84faed44a99bf356c2fe877b0e1bb163e3e56e4b64d55fa5de36d2e95faa9843b424a6843256b7a3240fa82912bd1114256103faec34997de38c291a03e8932d8759d5fdc638fbc6caaa53c773da9e27b423c5d353033011d84b7a510706c9fd03e49d3168f05ddfbc1c3dd74ce1bd7d735066382a86cfaee14098430b036be429ddcaaf46866dc8c882fefd999802bfecd8beb975ace8626e08b1da639990c5521677982b7f07ff5dec2be9b3191babccce4f9d75c0fa51a12c5408ebc19f0a4bcd74607d5028859b9463fc055d235d33f30533098187514a06d8e57df01136eb3526157ed75dceb09529b7d4a995e81278eaa22450416a268547d22a2351bea9ba19ace41638e4ce75bfb902988272cefa4e64f0a7aea390200f5662a9cb1f54238ad011f6eca71f09f8faf8f9a5a2df62e6692ed70eb5eeec52abe4779cb5e849dde23be219f97a0f173cb8c08c88e234f42869bfeaf2eebc04a9a1631b4f22463ade2d98082cae8255be8ae365615037ca8e7800830cddd6ba072083cb9b015b813f9b3e14a2d77357dea5adcfe63affee9fb2076ac00a41f00b9d6169075e25e38280dd13fad2dcb8137f78823a32d7485ba9e0a78adc1bd6dc5459182eba39988f22d8dc4a63e285cc1ff951b13b77408c8ebba86984053a37e03040556de5e438c2c0a0de23b43ef331b660b827737b3d03d3d58cf9be0076d91cd42437c72284127d6a0f0be6dbf468f4782df947da0d5d40f8d2191951eae77b4fd6dc935468b7fcaa641222b8957f137457634a79061ae9698c06c29e739d342d3ca4d6d38af1acd9f31b9c0fe13b5dc5989c914770fd6f5605189b8303c6916d1082d0799ec45c16adf1763713c553c7fe3591929eb38c3de762816bb56822271a85f7bbfde52fa4a81b8600cdc53297f0fc04556d7335bbf82e008e5ac638db564010fdb4d8aecd346b5e0964e63b891d2b23f83785b1efeb454c2ec01a4976851fcf08b04c07bf63a719f323be12d68b7f2172c315f187bd8bfb2590cf8ade7a8f06278612fc8470ec8203d4cfe3f02d2d2c9e59e4aa220fe6d58ea18cc27bb2ce9e0ca7bd0bd2327825a7b390f5c7a0f3866498450dcf0c3c5feec6b607ffc3fa6ea8585e86c594677d1656470bd983cae1c75de93e4df46beb073586904a5f8d8a60f7de78b81a222f7a721c321c40b48f79f8db3316ff78636e39048d1b6f53b53d2c873553257ad755859b49043b0bd0346a4572ffa7cd01cc9abc91721b22bf61a4949f335cf15b7b63af0ec4067c6c61ead4f3e592946e31e44461e78039dae7904f8c2e3507463d471a53c0be8b34386674c2a0bcf07008aac7018aac64650126cf8edd9e6d9b0ab8a380aac7bdea5e81b1cf139c39612114a689add7e600600d012a935e31e2d9ca3d9e71197a7da6da8d7dbd8fb2dd8ea03639ea4e5210ae1f37f8a5f4eb9ee80e1a0ffdae8a16f9b6876596af4e4e02fc0ca82382d98c158671215733eec22c0dc007b93a3b3afb361307f0499aa2b4f78688604768af1a8a1e8a1c71a9a9de39ece74971d308c864cbb304b2d822a6548ef1565407d7148c75454322e55358d72d7dbb15b62dc39b6c80086d3ecbc13c01eb1fe0cf5f39ec66efd711ca85974bad30e8d6669bc67c8dfe2c3a1f90ce1898dc4a69dd50f65296ade6002fa34f6d420af98411814be4ffa69146d18a4a04d88864428e05d2fcc38b6f92322883cdfd8f00c03df603b4b20c76401fd95073dacb9a30be78e744b055782f23392ee6463e79aadc3b6ed342bd22c25fbf3da00219f5b12977327e885bb229edbb4174dbc05615714a4734d126e4ea41195f035790941c7d93c746bd46c83ec94271c00ee233a3a34b4f2b78f8dfe22e1452fb989e82484163c10e339878dccc9d7684da77b58d730708ff3440990346384a6bfb6899170fe0af86709ee67dd780d6374956914adb6c6f599945528082d4190ede2d40b5845f862122321570ea55220948da6eeac197bd232bb4e442fcd1bb1ddcf7907661c793a4f446184f6a9903eb4bb51cfef95648d186adac5f639ddd54fb5e794d394fadd31a53cc0c1e2610e64ddcc36f13c36f176fc2333c93ffc23f5f70aa227f9f9a9854e669679b9a3dff819fd2169c2063bb3795e70c9d541e73482ebeb384c3ccbe5f70f329099c0658f4c3d781e662dea7402918a299f5660975666cbb7a727016c2aec452864cd2038302585107dc147816d1e3029fea57184abcba19a3035851550c5711c508de909a09b8fcc6e73d4f8bf0272ab95a3eacbdc5c5079e222740f7d93585a22f46657af916242d099737eb064d35cbd97ce2df6dd86ad6f1f3e17bf1784042aee1da1598677cf07f959a28127caf129f4ca891a921d699dc21dcad48268decdb0ba7042569647e292b5e58f9f8f17210ed92dca20924d50de3be17c9cf92b2bc740ad1b674307bfcea1a5322c00ad8e06311d16dbc93cf4cafd60746d4fdc22a4a7070484611bd451c2896812189f76ab3d5dccc09af669373f9691e9616b0edc2607e42b2d035d0a7cc64f1b7c5b453a1dee058056586b85ea133b0bb4acb1cc364987626aab27e27232338efe32bd7bfadce1e3217caee20c5cd8e00073bced19e57c466965511d69a0f78ceb7d003a9e1a98fbbe274f965c6aa3d1914e6fbce1a6c13da3752e656f867dbc4769e01339ef50f1694b8f450557db34416783d9a8bc513107cb5251c6bda5ad3a75595aa797997e1a8e0b28ec932a0ef484c88232720463795dda2db551c474b6dc5374707109e3c79442eaf9bfc3e76d072484737e86dece49045667c0369495166ccca5337116cf88f011e0d4c6893ab60fae9acec3f577e2b8d6fdb8aeb89d1cf7d48266800bea7d851ea116eafd08b23fb56fe8af9cb0206ae65f9a6167dfab48d41bdbff88fec1fe3a3c22031fdd5ecc43aeebba4c71829f5d0ce6466ec4f498e5c22d44398a64a4c167b0a8da08b7e68d7414da034cf372daa575bb59b9d5c5a4219cd1678eb5a22cf360b02a2d854d75b9da48110a14453a0a6efc2e0b30a4d044480d9cf55aac2fd19a72c2e4ac27104b7a14e611d70cd6c662661b5e401cf56e4477f2cb47f0d777277316c99b0332544833194a8901e0119ba59cba8a28b35e0ece64dd6b704e3d32f51cced9d0c9cc20026c36a83db659ba14a7442a9ef2a27a4c1d8c2d68326e16d7caa8b0048a7dfec9f8e5d78259c4620c818a54aaca37b560e15f15a4c691d23b07976adda254c", 0xdfe}], 0x5}, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x3, 0x0, {[0x3, 0xfff]}})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x0, &(0x7f0000000140)})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000140)="9513", 0x2)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x3}, 0xfffffffffffffddb)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sysctl$net_inet6_ip6(&(0x7f00000000c0)={0x4, 0x18, 0x29, 0x36}, 0x4, &(0x7f0000000100)="00928e41b041d46d1174b6d964d729cb", &(0x7f00000001c0)=0x10, &(0x7f0000000200)="5a88cf0d64ca114e4eff874998599b1028", 0x11)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1d}, 0x4, 0x0, 0x0, &(0x7f0000000200)="089b0f1b229339380501a1d4d720ea8ed120dfb2", 0x14)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$WSMOUSEIO_SRES(r0, 0x80045721, &(0x7f0000000140))
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
chmod(&(0x7f0000000100)='./file1\x00', 0x13)
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f00000000c0)='c\x00')
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x5})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt(r1, 0x6, 0x10, &(0x7f00000013c0)="6d66b176", 0x4)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r2, 0x8, &(0x7f0000000000)={0x0, 0x1, 0x0, 0x1000300000000})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001600)={&(0x7f0000000000)=@file={0x1, './file0\x00'}, 0xa, &(0x7f00000013c0)=[{&(0x7f0000000140)="149625f958cc35382db1e633b56d281a112038", 0x13}, {&(0x7f0000000180)="93039be257d69585536386ed133a49cd9d273c40f54e79da38dc47ac9bb4ddc2cd1da4002d667854c8c0ba8d2d56f4b45891346c1df70806614b0d9a67f3078cd246285405e7583bc88b0ad70b24b13809a19958b4881a82cce26093a06d1890d32427a58d669cfa4342ea6b08ca987963315b249127e346f9ddaef6c0ac572a9cc6315f204975008c7bb804b859c330f683e4f672e6ffd1ca786a88fa8885d1b2bd039ddcf4f570f05e8516dc081c313c034e196843b7d6e929dd1cf32215852574eb888b6c1334e994048f2713e76243590db0daef4a91aa544679070ef61c5114113cdfdeaa90d205265bdfca46af859ca318b437732801ef66d664979b6d0051f0959add369f6610ac2e2ebc39d5a49c1c78d865337db4d579cebec2af048d416222ea68e3736b32e4ef8a85a950174c370952817023850988761709274cf597e622ee3b90d41c3c7c4dae92fb803542575ad4060321867699c6a23c4920e0d60061adf371d316320316cad8373181bcf9cbc327fe7ce9eb038019ff77c6bf3b56a27381078f708073620656e31f43903292feae974f0100b654141424c5ad76e14c96926121dad0f1fb561a5ec22d38952795588c979f3b0c4a50fcc9921df9ec066ad7f15b5508bff86077ef572748412913df489d001c0165d94ab865fc1adf623bb3c8295849329d95da2b84b8b450ab7afe0d00cdc2b24d6c2197de77697df34fce945c0a0fe78506e366ea42380ff48b0fc10d1685540ae1b1beeb05b1b9d7a797efcd9f24a7a3e3b75384734ecd45f8f6fef61ae14e18c63a53856f225d4ba30a1577379aa0ad7070d65d852348cd56a4a9b9ee2d5db5aba57f8f83d125af31698b56850d63ad01eb09e6471c17f6b027cdaaaf5ede9ecaa79b054f25bc26323b7cc73da37831d60db4d058f4a8739a7b8769da7d1bda560de2b5666ce5387957e960da0cd7c178c64d2e6647cf96d4b1152e043b6492686eab31e754de238d3253d0d98aa61415e8752dc4791e7e2d4e1dea3804ff7fa5b8a18b1e59a7627388dfd419b920babe9d7bafe9e01051483b8592a1c3d22d261bacde42bb512ca06f6efd5e52d88e558852d4e2c682e63707a57d86e82cdba71c698b5f2dc09a609f43aba11a147b1dc8140245136294c6f1289a22e69daeb1dad3a599858bac24a287d0fb403d2ee20ce9d921133a1183173317669e7104450bddd7ae75b8e2ab8c4310ca9b5c8b71e47a232ca04afd1465e7402bfedb2170b1f09728aa3e015f949a39dc944d682374f7b251862f0ea8a35f01d1c51a903bfe5aaa673e1697af2b50a4a556cbc96c518d18f8bdc6b93f7c7970d674d279fec1e62d0567a97d8945842c6007522e6b9fa02ceee020a24b8cae3cbbdd0abf148daf35691a1b91ff35dd8e6bac6605d588d8e51c51eba6100bd8e1d4cf0e35cfe3622926cb2ca7e4a95d606393be284884ded917d8e3e6852d060e396a9d288b1f1b70cdcecdedbe2962fb8545e08d52977390d3a057d47aa93d44c7a380eec6d62e101c24759d488f1a81e454833288dd5f8c59e93105ab31a25492ae8ea94f7aeb61439f078ae017743d264e2377e8abf61f10be6a44f26d995a961ea53e3d30d080d5e677f19404328764d3a2bfd9c43563e6f6ef37f85d8ab51a2bac14a377f967d4aed2d319be1a969c71728b01125bdfcdaf0b6c3b58e43ed2020c766eece011e91e6df823cad2fe7d3921d36507a82e491c5e5aa556ef348a3fa51cd507a5ef52aa0752ddc753ebe0340ee1cdc95d0729128db60c869eb80b8686bc13b83e3e51018a64fdfa6bb2edac9186fb1c12c970f5610159fad8619bc7262797f3b6c52df5fb7018d8fde099df7fb210004cdfe9eb4dfa3bdc139bff85c58645f42392e97b02ca5d597d57fde85d3e8b418518f12c76bed9cd21bc44b682a94e35bba148ab15d1751a2f01a76b3de331a24fce28c5863e918694fb265229ca935137554dd159cae435183b75ab9ac30752321593377a3de3bd9f556d01825399ad69cee871a213d97f2d86f557598f218d501b7a0fc055eb4cd580350d76b897dfe90a558592b7f87f68aed92e8e7e5be8100cb1331b815d0a0afefd595b9284559fd1d8881ebdee3f8fccc8d8a8c991cdb9f67682b9aff53dc925b1fbc54adf2773c5463faa3ce15c2a440ed64ff30da1d036fc18bbc9e1766a4126bf258f48e24917311a73ea47dd263868edf691c73820a4a64d9746fdc715b8bda49e9440bf35b6fa057c4c2f196a7e47668a9b0e1ed940e3d42212d5353a52260b1d2c22ee6aabe31ed45b68362e855003dfcb2e2d0ed2792b919bfe7c29686b4594c8024d131d9cf5b24467f4811a4007df7c6207e10bf5a4a5d3732279dfdff599609935624e3a09d5c3ccfd3e83aaf0340a31f839c3b71a774e457cd4bf358301a90a0cef93ba829aa16d5f8afbcf58855e648dfef9c8bfca852492d9fecd7f6dccae2c68e5f22825b08590916ed19f183456bab985c7c8bf161a79f4aaf775ec4b418b358ea54856d36d0fd465962386c75c3b06417552ace595c69bfc471b4dbee0eeb2ba546911df93b475e77a48938b97f11093348f7418413712b0770e0acc996c28734fad9f750d50b3f5edcc856b5e9dc75fc887bb89d3ced17505ce229e534c5c2b416d39200c41e828ffb792194934b8dba1451f2d554098c94c762b76bee4f23bc8749caa74fb5cc247ebf8a4c94f2cfc08eaf0ee207b8f819f068d25718599cb87f86f032df17977c73c3425a11e4e537bb0c8ae91eeba833a2c9a833fec99816ecdebec68747d7b2b7367334c5ada175941c12b27015bb04df92319b09be34ccb2189aeba3ccd4ea96742741ee142fc1d6d66086d24e5ff7053f3b1e1c088a2f66adb99954700ea0251050dd992a6b09e669e421f429009730ddc1da646455bc60809a565fd74752bd699a9a7f0fa64ee70eb6eea9eb27341d6db18e036c2e91b524c6358d103da891fd89b9a53639ef5095d95b3d77564f0b3176593d2630d9f73c49ce6fd12e04c55c2303f24835944fd361b9676820fe72547dbfb924ce9f51530660b7b7901b64308f2afde89998960bb997189b41a66e67f3864fe062602853620088afad6e72aa19b434ac57da0a90a453867449f83e56956130cca3171500b30e5bf730a8619f7c8b04ebf13ad0069ea6891dde9c1bf3b6a805f77770d0c852820a91c9cb72d75852091a7d9f170f4fdd0341cac4343c7dbaa812f7c06f3303306aca88fefa45e24aa1b8340bc6db920156907356e03fa0f1ac825613238070ba2aa3e6062f86b40aba53f0d1b908d2d373efc42f499a247d510d6ed8c132071ff7b0d8413590b31d29abc8ab1db6867dd4c7bfa50756b1927cfe108e74cdccba5873c0c88777a73a19a13d8604b58596281fa59fb9a13a21ef2ded8c355dccacf0c5efd577bb0decc0b2580786a2d719d1d2bf00fc10fa15fd752d84729d5c9ea247ea1e7ec52ddd62c5dceaebd0f09eed900ffa50ac8f5e8a07607ebd9c917862090435715feb28acce76c77907d60707d88c080b190b6ada28988884aaadde6c45425d5b96405b9ad5c7f61fdfba5a4af2bbca24552e70bba5d2ed1617321ef49abe4e9786869faf8d4ab2dcea2c121c02fe2bc8a279e11449df20ea85a219593e532a8d5364669f5df7fa2f3642f0dfd5b8c5985111cdc8ff10b2fa53f476a058da5a9370dc128170df45af31b8e96e47b7dd7060bedf15b3cb2f58c74fdaedd377d4c558344abcd7f806e858efc0e3591b019649520b9e6f6d9e069f0bd57d7f7262f9443bb921956cc48aec3c66881809fd3329391158ff441d2c5ce8542a0f7b410e85ca9f4c27648e1a5be89a060896f54fe51e6af393afc417259f50701ba4c54673a081a6bfaba0521f4608e082f77689ff1f945d6b5dd81a7f788dcea7e3f0389a43f95363c13e843e31c669adfba0c54f1c903bfd45e05981e032f9e8613e644e09531d138fdd7dca6ac8933e92fc953ce7c75c52946fc39ff878195cab6fcb452d7ecd1574af37792c2b3727da6f2e43fb7a6e06ced8c281c84d8299913b9cf0b32ad8368e37b23a20f7c86b2d1e87112b743589413e78b26213b3e746a21362bb508759bf920aead8a67b613a58046649d0dd12fb2b8769030a74c95f3a885a540c5f9f4116a6e9728fc531aebbfd2dc19cb6906e3e0691fc342760593f9df64662bd11aa51c3b2cacd2be9b472a7c018478d6ca5d94be706311dd6c11e6cf72f280c2119a489accbc77025ab6e7680959faf943bd0fe2ecc36dec0687f3187ebe056b572e48ae1995a507da8e00122a0316e70e833cbe0cce40e43cc6a65e32c7f5618e821b14a10cdd309e2b1daf3ac836a8d923d7a1f32dcd19774145c70559485d31a0d7799a700509554a688c6aff7a7659df949e5478870797525f56e9f9640260ceeb87cdc03404e53b580bb616ed0bb23944ef449259286a9c4951db0daf1804c1e28a4d47e26b2130f449d53df9a568aaa17ad0421d4de9bc7ab78dce92ed50d2a51da59e2d02d8ef78baa181cfb70423b6a9b5022e8af9c323cc940431d85d61d6fbf8b6a7bdf3dd9a04cc56c168840f329139db8a093be89cadc1ac51f85639e323bb7e99a28d86767bef2bd9769cf204395823946e288a4a2b239c7250c58c6cd3a85545ed8cdaba87aa934dfaac2af416345c359dbceb765dbb1fc58d2ff029fa47ad2ec95491c5f845e200305ed0f5750aa90e987cf29358b754343062de45cc018136fc91dfe036c14b30fe3ab348ce02765a834859d40ccc0036b689412378baba1366c2a3a7511fedb939606566098bfd5fba17cc7f39a1e8e725088a15ea9bcec6435734ef18cd50d3d95e00fbe111ceddac211ee44720c10f51f05b21333d83b59bf4dba1ba6f33afffdaddfac2a17ec7c71592be84ed4f05389a804659b93288e05be2353684d75dc6ec45edfc1d33f41b281414c5b9d4cb5d61a5bddf1ba0919daebc2e014bce7e81aa0faf745462ca2f96360c1bbcb425cd8707609c625577ae5ae85d2645ee057933a6f68c96ebcb4b8085b91fedd565679fc96520c2061ad1e5e66877e134f68feac3af995e588a9bdb3a6037ebc5a512cba76bc23282ab694e645a9cb1b14d78ed72c031906f6e3c8484f85ef4c6b6872d82616163a87e3512496bfe10e3e7443ac6ff2f46fb9f4f2d6802d918154f47bb7c7a0816692dfde6347bb902d890c148df2b091768e73d859cfcf57115ae057a357a955117d732f78aab4f479b38683085fa75acc48b4ded53318bb4d3350fcdc664c883d582ad026e9e749ada5ab771de6f8beb135af37ca433104333ef6188cecf3a44ff9e164c1d5ee903b37a3408021f8e2239ee7b5a2974bcd0debbcf53502d55128165a31e0ff0ae9d8b6b29f13eb0169e90df70727db5f12b76c763db09a1ef423ea9be291b8a8575af3dea76d25b9efb4d1056b982942924332c4dc9cde76a17f8fa172251b64f62c43b4205cfe0c3e5c6f1d4f3988f7d38240213055a466986d55cfa163f0ba2f242c063b2219996bd99463a2088a6271ed570163563c147dd74c5f3e0f26147bc1945ae18b1f184b12401aaf0798c320e3608f39f8dab2d8a4e65791d65d3bd40a214e8b8c2047c0bab9a208ccb97774f7d52c2aa53fdb430bf2a4df095a551796d0c967533d0f0c6e290997982a73f5c82ff634187c90b5ab156b2286cbd0061170d4e6183b0e10821acc03431657eab12046a8f29355c12d7d4e256fce4e3ef7fdd96ac4e", 0x1000}, {&(0x7f0000001180)="da497b36dd93ddfea3c306aa37976a4827703b52a7b339c8893cb3a8c806bf22365ad2d33de1435a400d9e8f34b2763f8debbe12e883e0f7a3b4d75c78413bcde227914d2e6d72048892c349656b42195c744bbd50bfd46c3e9d35b9fbe6dc555220dd80d05ef97858a9602d17425c9cd76b2b218800875c14655adae4fd1f9f6710a02e41294c0d612b1055837323065d8d4f0e4a153ee45baff6665fbf3b9cf88aeafd15c4c884f72a416b192368258ae1ad6af7caa75a37c28d88aa8344b0d00e57b6f36fbd66c8b4c812ef3be659f4e076", 0xd3}, {&(0x7f0000001280)="a917ed4348426d90e17442efdb4f24612e160f1c8784dc1802f8e054a34adf80ac1ae47b35f0899fe1219d24c236888798b7258fab5c111795a45687", 0x3c}, {&(0x7f00000012c0)="512db7595b35e41ac3837da31aeddf86b8f76db8eac58277034bbfc14da6ef3127e4f7d57415a1afc3d4b02e0845e887a554beb372082789cd54bd328646a03edd0afe70b28302f97c0ad3f9cde7c97242eb4b33a1793f5beb281afe104af80a55d3a8b542203388b63ea9ad2d25aeb6b225bb89612e28c24f14179a7f32f2e5918b1149a430e0364759eaf83281e89ec84e62e66fb450acc94611f9f3a12909b8b60a455adc6305b0e0026bc40135101fe87dcb4ef7f149f160b875c4ef4a173b2bd6ab77cd7a0374ed0b5a9ed1ef5b274ae020", 0xd4}], 0x5, &(0x7f0000001580)=ANY=[@ANYBLOB="2000000000000000f3ff000001000000", @ANYRES32, @ANYRES32=r2, @ANYRES32, @ANYRES32, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32, @ANYRES32], 0x78, 0x405}, 0x4)
r3 = accept$unix(r0, &(0x7f0000001480)=@file={0x0, ""/75}, &(0x7f0000001500)=0x4d)
sendto$unix(r3, &(0x7f0000001640)="554d65764d12342c6a5f617f229d1e4bf82cf65b8a06ec7d8356d2f9dcc120dadebde9b7de18c3e6d9a9f6899c77b90886d2dc4f9b67bb779f810f4948d77687a34a156c3d", 0x45, 0x0, &(0x7f0000001540)=@file={0x1, './file0\x00'}, 0xa)
connect$unix(r2, &(0x7f0000000000)=@file={0x1, './file0\x00'}, 0xa)
write(r1, &(0x7f0000000140)="ce", 0x1)
r4 = accept$inet6(r1, &(0x7f0000000040), &(0x7f0000000100)=0xc)
setsockopt$sock_int(r4, 0xffff, 0x10, &(0x7f0000001440)=0x8, 0x4)
sendto$unix(r1, &(0x7f0000000240)="05784c63ce246945bf596d8bb70608cb735a3da7f5dcb7729dff4454991b1baa88b295d44d4e447261335a3b7635d2222ec167f90604f235721ea605a5186b613553a1eea2328edb8f4f27225fec58f827d9bca1eec862f397e6f948697c7647e99674ff6412d46917abe35ae7b76c0a2ae57bb6202dcbb1fa460d9b7f8b798698ff671ee4c19f3ac638de78ae090cf592", 0x91, 0x0, 0x0, 0x0)
close(r1)
syz_emit_ethernet(0x536, 0x0)
r0 = socket(0x18, 0x3, 0x0)
bind(r0, &(0x7f0000000500)=@in={0x2, 0x3}, 0xc)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f00000001c0)=ANY=[@ANYBLOB="fb182e0b3d9a090000000000000043"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000080)=0x5, 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x4, 0x0, "0018c000ffffffff000000002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000003c0), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x20}, {0x4c}, {0x4000006, 0x0, 0x0, 0x3c}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x29}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0x0)
mknod$loop(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x2000, 0x1)
link(&(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000380)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')
rename(&(0x7f0000000780)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f00000008c0)='./file0aaaaaaaaa\x00')
link(&(0x7f0000000200)='./file0aaaaaaaaa\x00', &(0x7f0000000240)='./file1\x00')
r0 = socket(0x11, 0x3, 0x0)
r1 = socket(0x11, 0x4003, 0x0)
connect$inet6(r1, &(0x7f0000000040)={0x18, 0x3}, 0xc)
sendto$unix(r0, &(0x7f0000000180)="b100050460000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd38781ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c5000000020000000000000000010000000000000000000000000000000000000000000031d880e0611fb13ea9646dc01549210efe33fbf9f14a5b3a7a1487114b54c8b7e143d983598ca406b871db9eb5bf89cafb9316eb1d4e22015320bb82880751fc771f17de896a04f55ebb789f878917989f98c00daec1188ec802dc3394bb10b15f505807d16d20c7636fdb3aecb23dd794793dc1d42ee2f3fa8398aa007b6b9b270000000000000000f14c856cb8dd21bcbe10e92a6e6bb07adb33fe3879b6915965953970187c7bcd0d521666bc9b31b976066ccaf427c8e37d0672fce80faaf138eba41819775dc8a7e78e3f499d680267b22d2d721221ce7a7573a8ee223a32ab271f2cb39e2d6d3eba984717d2", 0xb1, 0x0, 0x0, 0x4af)
sysctl$net_inet6_ip6(&(0x7f0000000040)={0x4, 0x18, 0x29, 0x33}, 0x5, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x20}, {0x2c}, {0x6, 0x0, 0x0, 0x1100001}]})
write(r0, &(0x7f0000000200)="2329544ba06f91289c00100000f0", 0xe)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020697a, &(0x7f00000000c0))
sysctl$net_inet_tcp(&(0x7f00000012c0)={0x4, 0x2, 0x6, 0x12}, 0x4, 0x0, 0x0, &(0x7f0000001400), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f0000000000)=[{0xe32c}]})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
fcntl$setstatus(r0, 0x4, 0x80)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
unlink(&(0x7f0000001300)='./file0\x00')
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
fcntl$lock(r0, 0x8, &(0x7f00000001c0)={0x0, 0x0, 0x0, 0x100000001})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
r1 = dup(r0)
ioctl$WSKBDIO_SETMAP(r1, 0xc0105715, &(0x7f0000000000)={0x900, 0x0})
r0 = syz_open_pts()
ioctl$TIOCSCTTY(r0, 0x20007461)
open(&(0x7f0000000500)='./file0\x00', 0x200, 0x0)
openat$pci(0xffffffffffffff9c, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000000)=[{}, {0x0, 0x1}, {0x3}], 0x3)
r0 = semget(0x3, 0x1, 0x10)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f00000001c0)={{0xaf, 0x0, 0x0, 0x0, 0x0, 0x30, 0x40}, 0x40, 0x6838, 0x8000000005})
r1 = semget$private(0x0, 0x1, 0x361)
semop(r1, &(0x7f0000000480), 0xe)
semctl$GETVAL(r1, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r1, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r1, &(0x7f0000000280), 0x0)
semop(r1, &(0x7f0000000100)=[{0x1, 0xffff}, {0x4, 0x7fff, 0x1000}, {0x4, 0x5, 0x800}, {0x0, 0x3f, 0x800}], 0x4)
semop(r1, &(0x7f0000000180)=[{0x0, 0x0, 0x1000}], 0x1)
semop(r0, &(0x7f0000000280)=[{0x2, 0x481, 0x800}, {0x4, 0x20, 0x1000}, {0x3, 0x1}], 0x3)
semctl$GETVAL(r1, 0x1, 0x5, &(0x7f0000000080)=""/171)
semop(r1, &(0x7f0000000280)=[{0x4, 0xf61}, {0x1, 0x400, 0x800}, {0x2, 0x3}, {0x4, 0x97, 0x800}, {0x0, 0x667, 0x400}, {0x0, 0xdb1, 0x800}, {0x2, 0x40}, {0x2, 0x3ff, 0x1800}, {0x3, 0xc7, 0x1800}], 0x9)
semop(r0, &(0x7f0000000000)=[{0x6, 0x7, 0x2800}, {0x1, 0x4, 0x1000}], 0x2)
semctl$GETPID(r0, 0x3, 0x4, &(0x7f00000000c0)=""/214)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000580))
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000040)={{0xfffffff8, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x26, 0xfff}, 0x5, 0x6, 0x7})
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000001500)=[{&(0x7f0000000280)="4e5a39c8bb775007cc8c1d6852c8fdc1920b88fae2dc33da9c176c83f29f6e0c4110b5633f095296d7f86ec3c231dbb722010502d94090051b817668b5c1", 0x3e}], 0x1)
mknod(&(0x7f0000000040)='./bus\x00', 0x2040, 0x4f4b)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0xc2c04200, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "04713de0af28a2813d8209b8d9f39321849e3c99"})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
poll(&(0x7f00000000c0)=[{r0}], 0x1, 0x0)
dup2(r2, r1)
openat$zero(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
getpid()
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000280)={0x0, 0x0, 0x9a, {[0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x1], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0x1], [0x0, 0x0, 0x0, 0x0, 0x0, 0x401], [0x0, 0x0, 0x3], [{0x0, 0x0, 0x0, 0x3ff}, {0x0, 0x0, 0xfffffffd}, {}, {0x8af}, {}, {}, {0x0, 0x0, 0x8000}, {0x0, 0x21d7, 0xffffffff}]}})
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimensat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', &(0x7f00000001c0), 0x0)
kqueue()
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504440, &(0x7f0000000240))
semctl$SETVAL(0xffffffffffffffff, 0x0, 0x2, 0xffffffffffffffff)
mknod(&(0x7f00000001c0)='./bus\x00', 0x2000, 0x4086334)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=[{0xc0, 0x0, 0x0, "15aeb47dcfedc4cfd57680c22ae32cd729fc408aca995340491cc774f65ef86ba22c7d70fec0f232eedefdc488219395876455bad81c3753a7c1927877ce5a517906f6634af29b051542e83a439b30f0f428b752d9c50dc6ff47908cc281bb5fcc8ab9ec2532c4c0ef83c7195e30a8851cc0833aa384deed16035acea05b8908e5d95c2e93e75dc99fcebe7853b42ec82890a9f233116da2d9dc968f57071c385501bd71f8bf16a2d6"}, {0xf8, 0x0, 0x0, "f6be87a649f2080b47415716d4b1ce4f02d8482fe2beae41a0fec4bd7fc73accfcb4d312ba79ece7a0dd60d75a421a4770b244d1acf8b3916e3bf33e9e5898fd0cb0d321d04e6f5da6de6158fe6ac014ab9cd9c9786d41848c391cc469e1f7c7ff50978bb3fe5f42e4411303682102a6b64d238ae579c3682af39e1521fc901f09f4221a2a6f537d5c4370d30d3935492bea9311d20add0dbc5e7ce6c7239b945eb8bf3e7d58f1170a41b26c4c92a62080ae16002d4d707aea2b3cc4363602d0b6347b10b682de899741edd4c693d4d7015fb3dd6aa46a23099b64df2c66ed1499"}, {0x48, 0x0, 0x0, "6d5989e80a38a3dda3b66d6be1c8b53c6d74b1272f2aeb5de8a10ae1970dcdce468bb27baaeac9cc4d6b5b214603857b6d"}], 0x200}, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206913, &(0x7f0000000300))
mprotect(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000040), &(0x7f0000000080)=0x10)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x2}, 0x8)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r2, &(0x7f0000000180)="ab1fe080a6b1e651a393849717fac8983f744159fd80988a69504618da8bf881d8630f3f208bc01f58e8005fabe1c64a0591c8f24f47e5bc02be3983e941f0474d8e48c14d0a50e4f996308d01b4a40029e0eac78751b33da1ffce45c33aa43845780e798086d21355bf7c432bda7e339008ed7b578f3d4b95e09bd8161a5c63a52f44a88f19366cbd87d1a26a74a18652118d087dba76a880c25d85dc1b5c5167d33c7332ef9483784666628f40831c2322b2873e6d2aeef864349f4cfd7d02668960360d9a1b06223f53416a347e82a8bd1b20ac7d7696c07a193722af7ac058102a38f44203c8be5c4a6430ad99fbf6bc5b68f057eba9aa017183af47714165c57ff7048079c371d2a772719ca63edcca6b8ba0cd66022bf92743e47b2a95c9601eb99268dd276cb85150cd38002cbf6146577b80beec3dfffddcd0774770631a1c6ea32a25463f059d81f95482d7b0bbef99093d6e78d0c2f3a8372bc66618dcffb528ed5320e3196aa025bc2d94eb28836d90e6fa6c8e1488963e03b5099ce75692a9e20a31f4638c5a2642c3342d5eacc24ee00be8fff53aa67536700e724f853656a254c6612c8f07b4e718d3dc965cf488551376ff5e5c58fced0476aa9ce72e059b48b16e006abd9fda95d881e08c59ca71bc34fae9db1d55394211c6fa15e340c3aba6dbb729ac1e1baffdbc0a3744e4c05e2bb90bfa026dbc1210900bf1b2df1b89ac52deab410b7d477cbd11d523d22e9183557161f9b0b82628ac6a888e2e385dd715b56ac801a866fabebcba282812f6c097f101bb035551a0247937c6f3b4051db36b26f3260de99f52d9ac0ca228b3c9fca879dc4148302ef7913b964bef8e110b555a0ba3ffc305da5a35fc36c077bc702931442c1d3e05f8fa67a4f8510652558e743378044ebb9a64d9a6d98e9f0def2de6759b178a11c2751ff9cc9fc5057e2f833b5b19369aee5c160c880adff5c52f72534158205d4beb10dd7b5b6968367ba188337a20947f97173ebe42e7dd9109d881d7fc3d3b08c5891b7201dd7abbcb8c4591f6bf95235403939db66b2b68393c06c87a6c17d0f97d69337943d7721f6d8a43f0d8363243922f200a0a8211867ef83f897fae051d48e2beed35b3c2a9b3cc2d2f3acfe6c88b350ca5cf1e522fec8c787c69be0be8f4af6b2cdab51ce25631689d51485c7fe1281ddf5eb01e25efd63b974e4f65d1d1b08d1cec259ca31beb78e8860df8621cdb64b1f2ba0a33993e87cd948c66f913c86413e7e375c27aff7d2c49fd673e51e324b63d4451536b44aac074b1d6b6d37dd0ffd5de128daee0ca1eee3bccb20e29373a1896c872d8ba6721804f76ed113ffadb2ac20f2fd097106fec0b86cf58a0c475c7f87dbf11df8be984bc7cd52ac121c874db401d63ffc49b306a9ab487d8fc7ee9dd6ff672027b020531e4a98b5935f17ccc58f741f0eee04e49ae2ded06bf46f04235ce786978476783d2197197cb68bb78827b441dd2d562008e927613547a212f9d9dc60805704101cafcabccf8b74cabd57bdb0ba8277925c8422cd96fca5794c017e4c4ed7aade1534e32c143a78cb18fca66fb5382130c603bf5a3de1e9f4296fe9ccba39f10337880f14b14c288087074fb9be7edc8524820213bdb5bd37fd5767d7e54cecc6e769e0545982ad4f0af78469151945fc78b82c375a584c8d1868c69f028fb4ce5cc08495918c64c24eb227d7bba2a275aa9acea47ff71989bec4074fcb54e75e506ee7350f1cac71d86f2f28ae3d9a51e21557c034fc0d663082793ff164eadf3dfb9ae5477c283680df331cd1bb29df38dfbb5136315e742fcca4b83480e3e9a34bf0a686129af26bed657abe498dbe61bea582fdab3454d825bfd1c190f106ac618aee91431f5ddfb2ae358ffc5056555cd8f488264e3b4e51182a386143f491e46ac5f52b3a1cab32ea127b5c6b1df25e57d52cb4e299d370dc2a685d3301829a91db39114d261eb4acacafbde6ef7fc03b6fdf0e3e0da375c01731751acd33b3dcf8c67b610f0612ca8e51ba61729ed58c62224f6daf2159f4e8128a0e4c5995c32509192a96574a277e2d8af084448e8e3ad0b0b805657856104494fb78d53bc754f5ae6550d5d663ff128ff4bdc2357d198072f0e1970f2379d4a588b7bd5f4d34d03259ee8f1b7075d1d058a79a5eee2e58f90584bdae5ad98ee9b0114e19f3ab0ee8ed7f672cd625bf2351ac4698c5fcede28212de5d0b6e395685558bc45ac79f84e8e866be55ac1b84a7aaa1f4df0ab67fe0f65d46e2c5e5b04f7ea0ee127c53aff313bac26027084d45eee770c4c6ca643f6d7982b98196e1c4984f9967d48ba87c7ab187c2a947be7d3bfaba0bc722eb34470ffb252cf3b3cdf38f3a7cfaeb53d8eb5561368a99b9076751668173f57bf4ad10745f09660c8886a771f190acab0a769bd7c9aa1043bad45008fff116e31d44063116d1b82e9e280c53933f86f9413f0cb532049f211a200ad75f850aa2e652b84dbe594c0f46dc27b299d69c2db1fbe91b6838932a91f16fe986a78d1ccf625755ba830586aeb5681ee48fbacdbb4446e586168c315f6abc0cbe639228729c437e6c917aa671b20b2dddd5198a645a81fd85a9abc3a8777ad3848fc39ceb297adfee57010653d5964cea1686dea661c58063b4810f8f785e5c3f48ada21412d06f8a94b7fc26e92ad8175ad", 0x784)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0, 0x1010, 0xffffffffffffffff, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0xbcd8, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000200)="d8d18a66c710bc5b9b35744f070058846ca5b4b311db6d26d631d88ffce7e35aaa76", 0x22}], 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
write(r1, &(0x7f0000000140)=']', 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x7c}, {0x4}, {0x6, 0x0, 0x0, 0xfffffffc}]})
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
clock_getres(0x2, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0xfffffffd, 0xfffffffffffffff8, 0xffffffff, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000fc0)="1596693388d4e8bb6df16e29f95a81fb895de567ab94e94f340ccf9b5295e0b5957b71442ecf83be94957b0862d96edb05ace734c4e80cbef22f5d8ad75885c09cc69c907ee5dc2f439aaf0f", 0x4c}, {&(0x7f00000014c0)="e503c61d80583de86ea2d148cca602eb0657023464e233112274b781731ade6ce55d6f7afec296ded73d238abef715fafd4878550b5ae5e0322e293ae4e5cd519280829bd61933159593bc331e33a52b5adadcf490f78b92ad6bbf96cf058bdf43ff7064a7585ffb497f84be4ee9bb8e49007e0dc1c79295cfd07e5a3c4505933bb40cf0456686eee9bb3309f16aaa5fdc16357f7633d6c343fc4234dbe18e1666caedac66c76d37692d1e8ce408f0e1e39e73797387106a9259ec87d601b009746350ca5a9ca0274690b50560de53220fc2815a78f45d277d5002d3f6d428a60fee7a15de1de6861685d366b18d5017d9d3e272705ac7b6d9b2a2035921ad860eedc636c7d1341a6c9c9cb2deb630b4dee1d54d3628914fcc0dc5bb93a1457844e5dd8808e4aeede385f70e080af31871a7ab1d9c9a78b1ed288ca2dcabd5fed2826f54265c8f75b487b3766f8fbc0527319298927930707dbba165bfa3c5d3432abd55fb9cdb41280eae16c186594d85a55842264a9b5421731538d9b392eec31ae109fd9813bc067086a8db7fb046ddd04daffb3ce94a4843621a33d5d90f99178b375e2848802e1b99b3b974d7c53192e469daa156e5667fef2ec5653c4c896a9280617a7de3e5c7f47c35342a81f72e4f9c42140017300b5bdc624f4e7c3be8c91e0fb87590d6e9ab1d547a8d41319e40837678d23715e0e5b0a4da9121eae6dcd6792574aef12d49ece93d6fc31d77214f0bb08da10bdbc6950c60b1ba11c44980e21f78c55c9ca817507356dbf3b62a0b588ba243835e8d59e877e5f8c459dc6f06a9596be14ab364fa616b71c56f12ed3a028cd3106396e2e2ef55a208ee4f12f1cf619f134e046d0cc28fcb2a465e22372eadfdb5e08f22309ff23eaaf5205705bc29729244fed42fefae8f363c70ed64e37845eb2faa6b8da8d143c75f6b23c92f527edd0576814ed90033ea760b1c2eeabaf5ebedb269b845541ac4a1f68bc13a9c9f65eb3d3e1781f4864c08775448f30f4cb4632131c23ac76636031f9503c902ead364154d460caf2b9371673f52e57bb98c181e86ffe291f30c1e4ba64be4b03b65336dcab894bc75bf5d5f505d8a8f0708ddb51fee9a47e3276e427becabd7d331c1e126746082c07c921ec1743f46f5165f6f9eec781a66ca6dcb08283f12b8c856c53fe7388ebecca7cb4dc9cd60321d834e0c85a8e8795f8ba6dabf6f1df96fad15749813a33483de3b853b821f2dacd90a970e513ad3b108cf6706e61c9182adef982c24b7646e169b1fe7da7f05ed8112a691964af4582ccc9ea61afa4aadf97459f8fe9df9a0d03a7e6b362514ac445da8cc06b8092be3049a0473ef1192dabf1ce41cd65d8fbe137b2317f2aa6fb8a12e1275ec5baf15bd3a0cfcb17280a53683b21bd2ec134b2d357f49231f1c49421ea8c6679a66a9d526cbd8fbd6a7c285f0882648a2ca16bb5561322ff5e046df51dec0cada6d75cefd5aea53191c3e12760a8490ecc308df8d9d4475c3ddbda945b12e7aeacf1cdfc27dc41979ba4c050bf04281fa762799ae06691a785979a5a97d45733e7c51606670384af46c06f180dab18a4c1028d5354222d54aceb5f8cd5f1da4b3487cc954f05a0b652d2d8adc1327f2a92aa0ef1006d70f121d7cbdc2c519654ae950604e83e23ceb77b2f02493cb00d7c7698f6281890950ecb6e7c46ce9cdff99b31e3eec77f8b4e0d78abcca03356606e0c394bc3513e24f3dd0e073e9d9ffcf210311505306a03aa6f4111b765865a19b4567a59aa582b54804a2a77b4a246af051a4b7cd22245206bf4d3ad6b022905197888f841e50f16b647c7c4a60e5412f01f99379b3247b2c4e57a627b4da2722d9f02e1c251b805242320c0e4e92d6966cf5dbe35124ee702e10850314f5a45c35fd497ca7ab6dc76a50b13c263136db5b0678bffdd7ed18d605f9dacf0346842f16865fd292d8937433fcc65c612042eab65bb783570b4b48178fd35deba95fdc39f3ddd325cbf79b56e53b660561f3f6b44fed109ed1e923890f8fe7c57d06d4ae5b098b2464d63e97fe533ab6d03616a5d7199cad8470952349a76ced2d458c28d6fac9338791df5a23d1ea52f7898db22ce959ca463592e39c656748156b41eaebcf58f4224c4c85a8656ab1916d3aeb82a0e4537b2aa1fda04575eaf4a56fbddadce2ce68b0ae8c53f3e0db0594ec9bfc7f95b02bb188489853a7efa354ab2510852422dc1d5a60cd928364f0e45ce5ecdfafa6c0b6d6f44ddefa61de03cceb3eff026d6d308fd7d7ed9e7a9e7a8201d545524e6df3b8f376fadef047f130bd39a8b4de1dcd6ab5c6c4b53d7815828a01da78eed1eb097fb88cb21f6affd087fbd17a6014b491fa4609be2d07882259fe0d578e71d0028b986636dd2dfaa2fd1e6a6669177692a1ce82ce6317dd6d45e7a6fef72b56571848899f4bdfa66b59c8ac646a637c3df441ee5bb46af79a7d83e160f760126ef381167a14f2e79994893cfb774704f857fc02af519876bac4cc28bbcfa71271e051413d311a21d0bb748bf0d00a63ee0ee66e440e9e13e35b35d390c2a6f36d7890895ae7bcc828a9d10f61e4edfb91fc3272527711a665fb7817265ec9d02f76d625f824ff364b0280916144eec5597d70980265522fa783b9602355732d7dcaae870f2ecc36021285f39543bad16f343dd7df202049eac437eccd300b6c2510dd7a9fdc5f5105b0a726f6f841131d158d071f89b6d6f5ab6b4cf668d143c516fd5afe638a3d856bd5a5b8c455dc21b4c8be976cb988011bbc1c415382ee7b8c7c9d8515e847f3f4a4b939d68a9f8a54dea5ebbf8ec95cc7104b046fb6ac94ccc89ed9041446318d5b57072ceefb68532fe81bdb08e89ce9829adacd606a8d08e7e3c82c576c2516cd574c20a6e5a970338cf80d6730c7a8d5113974419f8638b6525c72542a3cb956fe061854854d91497adb52704a7b55922ad9a81a80de2d6199ba68e2", 0x851}], 0x2)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000100)=""/65, 0x41}], 0x1)
writev(r0, &(0x7f00000002c0)=[{&(0x7f00000007c0)="668ebea2a6cd9a14da21d9ef08370000000000027b3d418388532c830edadb820c619ad8762fc4374ad97443a184c14d3bf391b38db5e62db0ce686e19bf2747873599a64f93da5fe7fa6f58483c01ca27fdfb63e96b767983fc2ef8d54bff2d8f4ccec5771c3c19a6ebec93f575ac77381fefc6ad4ea5a0eacd9d8bbb0cfd9fa7e77e01ff228c372fbef07e9d13996f42313465aa0a6356cc0783bd12e5ce24992f964285a2061e365cf11a772ab1ace7e92d57500fce539c29ad0c36bf85c62699b4315cd8471a9cab28e1eedbff8a74b0e410c61cb0878748cf58dfcd72f928d30c6e6e6b5498ed32c54d070b7079464120cc647bc73dbc2479aa074cb563673a95fe6bc17b78c1fbdf1fb6e381bed7", 0xa3}, {&(0x7f0000000900)="b4b5d10a8b25157d4f64c002380f02f42e10a46c2eb46f96e206008ab8ec40aaaf98d019f5c505db9be5859e3946ab7b3cf86b8b01761e1138566b56c52f330148c32a6647c1eca95fafadc8b7c6c68a45474853c677ae74a33489807d00ed6bc6d8ee02da08bece44226f570b87683c9501a8b7a6e64a42055f712b67b7e3b20a01dc0e09aabce9a04b1674ee2b30151847de4d9d6d07af8ad206914dd74b58642e2aeedea7275649aae4555ddec5c5e17ba11d46d7bba8bebe9940861c0af9cc2e73fea58a2d090000006027e99455835fd65ff56f47bd4a20b1b9cb213ed7ce27d570348acfc5e397728cb7a84fa9ea8ef36260e76da220e05ec15ac0d653a6de3616a5449a5cf5c0efe3490e9a6afc56c9073f3b4f0a98452c806f49932195b608c60bb04e583d2c46e35e733628e3543e7b423a3c7e1562bff3a7e6827a2cb6ce340f029d6e6ada81de8cbc7e467e842e3bd8363ae81e633b9384fab8a8d474406e828b45001a9a260c0c774bed1143c7c0742727b00292cd928962a1790276155c7554eb2f51d1051af3c7e05cb5a008a86b72f14f634d7b", 0xff}, {&(0x7f0000000080)="ce9f97e1d72835785b27339046496cfab6fffe87c260269d86df477427f075cf16e705bb5e296889", 0x28}, {&(0x7f0000000200)="d2da04adec0765e533d5cfd0650938efcf24d899c76d9a30d37d527370db2e323fe3af70e625054c0174a1e34651982d961311d3cc81f971dbf0e6aecdb713b3a4ee42957843cf05e3b1e8bdf0f24c4aeb0414e567bf17d9bf72446745950c92a2891ac34c564051ff5ed44475c732f121c4b98df27d4acb599d23600aea5b599a0fd3387d05d7e3a4452dfc6ff38aafa22579a33af00b71dcf4fc1949914e42263094de4a3b776cfba92b5d45789e9b2542e0d7e9c58456e6c84346", 0xbc}, {&(0x7f0000000440)="842229ed2523a7b77b764056ea87338bb32117a2424b4fe37ab3de31b4b1a708bc665729bb5dfa3252607316aeb36e528dcf87764d961c447d67127666a0cef45bf527039463b47145b91089491dc4702ae6ab0b0af175abbcb02b7f7a64d53c9c28c9774b1807567e63477cecbbd4b14e1f81724c3cdbf2425cac711722413b98eb9c144d0f9b74fce8ddb6ab42a73399b4aeb0e2744b7a151b06baa34f73ea48b61d3d6d568a753f0bdbd157cacf4f158b1bf6e382d576a9e46fca9a4e2cae2df955a34847d207aa66a998369da2f07184b38c62bebbe543e712c4ad1de4ecd5cb10692fc2139764f8363f3f9f", 0xee}, {&(0x7f0000000340)="0fc725798913873378cdeb4f17d8bb2b04a0c25aa0a6a8dab2d490010d047d7ea853e98e898d19a8ef7b23f86ecc4286fed398846597d88a3da428630ee4f704d97bc05b618a874fa5a4ecc474db9535c62a8dfb79ea1279f9e6ae2e9a8659a1447901623aa3811b13268ebaac3ab9c81289ea5e9a5d3037384aa042562caf92885f6f79bb527a1e912c6dd94d7a46c96c838ea27ed03139f642f13a20bd06f5f42b3f0a1347141d2031a935209e630673b85b9de9da30adebebd720ad8763ba6c8eb604dbf1bb49bd5e4e4133f8028ef93a8bdc09", 0xd5}, {&(0x7f0000000640)="c0c9f5d0cfb59f6a2fd8dadff5d1efd9ec57d63d9e85d5ac3f0b7d2a8b72cfca6587924c12601649caddefbd2db58b67936170beee1c7893ecf97be4ab7876d2747fe169d980", 0x54}], 0x7)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f000081c000/0x2000)=nil, 0x2000, 0x0)
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x13}, 0x2)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)='\x00', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140), 0x0)
r2 = socket(0x18, 0x1, 0x0)
setsockopt(r2, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x66, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
getsockname$inet6(r0, 0x0, &(0x7f0000000200))
r0 = kqueue()
writev(0xffffffffffffffff, &(0x7f0000000380)=[{&(0x7f00000004c0)="287e75a480248a06195203939517d2478035039db1820693c5c24609e4fe1a8424ce277f052de98472", 0x29}], 0x1)
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0x4d}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x893, &(0x7f0000000100), 0x13c000, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
syz_emit_ethernet(0x46, &(0x7f0000000080)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "fb00", 0x10, 0x0, 0x0, @rand_addr="00000000000000e7fff4ff0000002000", @rand_addr="00000000000000000000899be21400dd", {[], @icmpv6=@ni}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="05ff00009b30df6d7c10f3c4", 0xc)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
ioctl$WSDISPLAYIO_USEFONT(0xffffffffffffffff, 0x80585750, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x1)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, 0x0, 0x0)
read(r1, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020690e, &(0x7f0000000300))
openat$zero(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimes(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0))
madvise(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
mlock(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
mlock(&(0x7f0000ff9000/0x3000)=nil, 0x3000)
madvise(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x6)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = dup2(r0, r0)
sendmsg(r1, &(0x7f00000046c0)={0x0, 0x0, &(0x7f00000035c0)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r1 = dup2(r0, r0)
ioctl$WSKBDIO_SETBACKLIGHT(r1, 0x80105728, &(0x7f0000000080)={0x0, 0x1, 0x9})
sysctl$vfs_nfs(&(0x7f0000000040)={0xa, 0x11}, 0x3, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x2c}, {0x48}, {0x6586}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@local, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="500bba10fa98", "", @random="0e66f0b33d2c", "ec88ab53347a6d702bf67fbeb296bb46"}}}})
r0 = socket(0x18, 0x1, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1024, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f00000003c0)={0x4, 0x2, 0x6, 0x19}, 0x4, &(0x7f0000001080), 0x0, &(0x7f0000000440), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x2d}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
minherit(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140))
r1 = semget$private(0x0, 0x4, 0x1f6b4e471c181030)
semop(r1, &(0x7f00000003c0)=[{0x1, 0x4, 0x1000}, {0x1}, {0x2, 0x7fff, 0x800}, {0x3, 0x8000, 0x800}, {0x2, 0x7f, 0x1800}], 0x5)
semop(r1, &(0x7f0000000000)=[{0x8a8b6f30f0aceaec, 0xff, 0x800}, {0x1, 0x4db, 0x1800}, {0x0, 0x5, 0x1000}, {0x0, 0x0, 0x1800}], 0x4)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x10000, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, &(0x7f0000000100)=0x1)
setuid(r3)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x40, 0x0)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x8000000000005200)
r0 = open(&(0x7f00000001c0)='./bus\x00', 0x100, 0x0)
open(&(0x7f00000002c0)='./bus\x00', 0x80, 0x101)
select(0x40, &(0x7f0000000000)={0x8, 0x1}, &(0x7f0000000040)={0xfffffffffffffffb, 0x8000, 0x0, 0x0, 0x0, 0x0, 0xc, 0xfffffffffffffffd}, 0x0, 0x0)
open(&(0x7f0000000080)='./bus\x00', 0x2, 0x89)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x10, 0x0)
dup2(r1, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x2}, {0x81}, {0x7b9e}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@local, @random="2a99058b47d7", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr}, @icmp=@mask_reply={0x9}}}}})
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x9, 0x0, 0x0, &(0x7f0000000100)="987c000000e9c9b3faf040a765dceff38911c6bc5f6b8bc3d400af4f1148af971877b25a5bc70f2320ebc54589b0728a057cafb84bef54d21d25cdf68496d2073ce91bf3c2ff2244000000005e3ea58b0100", 0x52)
syz_emit_ethernet(0x56, &(0x7f00000000c0)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "9fc5a6", 0x20, 0x0, 0x0, @rand_addr="a2018c2a9d85cd88e50db39001e38bf4", @mcast2, {[@routing={0x0, 0x2, 0x0, 0x0, 0x0, [@mcast1]}], @udp={{0x0, 0x2, 0x8}}}}}}})
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000000c0)={0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x6}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f00000003c0)=[{0x3}, {0x4c}, {0x6, 0x0, 0x0, 0x800000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000080)="7cdc3fc0aa17dddf7830faa1aaab", 0xe)
ioctl$FIOSETOWN(0xffffffffffffffff, 0x8004667c, &(0x7f0000000180)=0x1000)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x2, &(0x7f0000000000)=[{}, {0x0, 0x0, 0x0, 0xfff}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3f}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x22)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x7c}, {0x14}, {0x6, 0x0, 0x0, 0xff}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a54233f4d9d00000000", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0xc}, {0x81}, {0x6, 0x0, 0x0, 0x100004}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc0284414, &(0x7f0000000240)=0x2)
r0 = kqueue()
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={{}, {[], [], [], [0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffb]}}})
kevent(r0, &(0x7f0000000140), 0x7ff9, 0x0, 0x80000001, 0x0)
r1 = syz_open_pts()
dup2(r1, r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x81206919, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffff, 0x100000000000004]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0286988, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0xc0}, {0x4}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x74}, {0x7}, {0x6, 0x0, 0x0, 0x7f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1001, &(0x7f0000000000)={0x1000000}, 0x10)
syz_emit_ethernet(0x32, &(0x7f0000000040)={@random="b15b65dec012", @remote, [{[{}]}], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @empty, @broadcast, @empty, @loopback}}}})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000500)={0x0, 0x0, &(0x7f0000000100)=[{&(0x7f0000001100)="f96dfe6588d9910998b4aeea546c15208d882d2e0a0190fd2e6ad3dfc7cc27157eaca01752438c590a98ca4e416a23d4d68ef670ca82a6d3c6ec05f99c7e1424d7859962e59741727417a6d9a8e2e0979bc8ce13fbc40e9d0d048c56f8cfc66e36293646fb00137d5b2a98d6d40bbb97f9556b3223a0c2275c4975d76d080c8ce5", 0x81}, {&(0x7f00000000c0)="0bec11f98d2f1c254ccd9ce84a2215859debab0b48cd8c96b01cd2359cbf1af578a2e4adaf15ea3de4c528caf577d33c244dc8f352c482f62d", 0x39}], 0x2}, 0x0)
msync(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x5)
sysctl$vm(&(0x7f0000000000)={0x2, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000001180), 0x4)
r0 = getuid()
r1 = getgid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000240)={{0x10004, 0x0, 0x0, r0, r1, 0x91, 0x4}, 0x400000002, 0x7c42, 0xa23d})
r2 = semget$private(0x0, 0x4000000009, 0x282)
semop(r2, &(0x7f0000000380)=[{0x1, 0xffff, 0x1000}, {0x7, 0x2, 0x1800}, {0x2, 0x4, 0x1000}, {0x3, 0x42, 0x1800}], 0x4)
semop(r2, &(0x7f0000000380), 0x1d)
semop(r2, &(0x7f0000000380)=[{0x4, 0x1000, 0x3000}, {0x2, 0x6, 0x800}, {0x0, 0x0, 0x800}, {0x3, 0x3ff}, {0x1, 0x2e2}, {0x3, 0x7d11, 0x1800}, {0x2, 0xfffe, 0x800}], 0x7)
semctl$SETVAL(r2, 0x4, 0x8, &(0x7f0000000080)=0x7)
semctl$GETZCNT(r2, 0x2, 0x7, &(0x7f0000000140)=""/202)
semctl$GETZCNT(r2, 0x0, 0x7, &(0x7f0000000040)=""/60)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000300)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0, <r5=>0x0}, &(0x7f00000001c0)=0xc)
setreuid(0x0, r4)
r6 = getuid()
r7 = getgid()
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000180)={{0xfffffff7, r4, r5, r6, r7, 0xc2, 0x4}, 0x2, 0x7c42, 0xa23c})
setregid(0x0, r7)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000280)=0x10000)
poll(&(0x7f00000002c0)=[{r2}], 0x1, 0x0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xfffffffe, 0x0, "24180400000800008000"})
writev(r1, &(0x7f0000001440)=[{&(0x7f00000001c0)="a6", 0x1}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x3}, {0x6c}, {0x6, 0x0, 0x0, 0x4000}]})
write(r0, &(0x7f0000000080)="7c0000ffffffff00001a00a5f3fc", 0xe)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0x84}, {0x44}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@random="060000b00114", @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @empty, "69c0d33e60bc536b586388634e5f2dec"}}}})
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
sysctl$kern(&(0x7f0000000000)={0x1, 0xb}, 0x2, 0x0, 0x0, &(0x7f00000010c0)="dccad5fa", 0x4)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000002})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300000000})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0xfffffffffffffffc, 0x2000300000000})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x4, &(0x7f00000002c0)=[{0x4}, {0x1d}, {0x6, 0x0, 0x0, 0x3ff}, {0x6}]})
pwrite(r0, &(0x7f0000000140)="6ba9a481bbd5bc26d716c3ce78c7", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0x9, &(0x7f00000001c0), 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x80047476, &(0x7f00000000c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0104419, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x1d}, {0x45}, {0x6506}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@local, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="500bba10fa98", "", @random="0e66f0b33d2c", "ec88ab53347a6d702bf67fbeb296bb46"}}}})
clock_gettime(0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r2)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
sendmsg$unix(r0, &(0x7f0000003fc0)={0x0, 0x0, &(0x7f0000003b40)=[{&(0x7f0000001680)="695da0d554da0a57514427d7522ec71fa5c64e6bc68cf0ee7d5cd020daf6feaa1ee878d7247619c77966cd3f0fae9c22248dde7299b51e324227e67d82d51e79", 0x40}, {&(0x7f00000016c0)="c7552733409327d362e201b227ec367137710cd87e330ac3322f51195134b3a1e0e1148cf9902cfad9627583f7025c4fd6ce5773a3acd39a2ba8391492f1c8eb24fe9ac2dd092deb98f1f154fcdbc0a07c0b06cff1c8d15848760e48b10822009a9603e1b6f7cd0bd7bb9c08dc3ffab5eb4c65f9dc785e45338c32a7163b527387860118ddbbd5aedd2df19bda6c3b09efe33cae13961baa", 0x98}, {&(0x7f0000001780)="e7dbda059783da2bc2cea28b27fa53ca09acc06fac0457b70c746e4d3079a1bd9ba81f358ce2abc143c08059c3de2f4dfa0d1be170319e11bf1d7e31791a2666702dfe78e9093130e328165d5615afffcf8f6e90a71ab380f2282e47917ac1ac8b1510c3e65fce523023c911bea94a4e4a8b4df7a9a195688b28931939d264962eab0e4b647b2bc006132a0e295ce835e04d7ba6386b1e38ba9c", 0x9a}, {&(0x7f0000000300)="df3144a26c6f15678c539226956176374fd692fcbdef2de9cf928265e2f9a8695e1dc100c1ca500e56a5d2ea32487c71098de0d060c5073b006380ed01001b04e45904791600f56b097ab4621ccc", 0x4e}, {&(0x7f00000018c0)="3198603925b6512619275e428ec2db070749aa0d73aa697e46cac599979c6164f793560d6dfc76a880b99aa81875d7d80e63ac61e7d79a49a8c91d8ed422b732b3c0c7ed425f209c507205d514fb692436161634e69234b4cf68dcc7b1e398b65e989c0dc6c0bead6e97882a8499323af4b188c2c3976a6a3d271eefc38fb3e8eecdfd7ca0c0dc6cbbf08b012bb5d4715cb62b071bae8370fa2f0452e6dffe35f47e5de137ff3403ebd829b8faebc2fa2aacaf47498e499dcacfb5282c66c81d78f899ca9fafa98e60b575284888636ec10d0408956e60f492", 0xd9}, {&(0x7f00000019c0)="fc4bdd1c00fda2144dc3e13b507f345fb63f7fcd5863ae27d469b40e9a8e292615920ccccf7d0213d9ddfe99f512a65371c98af97aada46709167f237f629e6c8b40cafd07a1dfadef2aa136891d8661a173db5b19e2dfbc064a74d5edb446cc4f2fb12a4b96eaae79c4926ff786f60f29af8ef23ca2a4039b56f63b46fe8db5bcefd56fbbbaf141f7b534bfdbcabca6d15da3076263f0541a3a2e0cdee83845c62db867c6d65c49058418c32d1c576bead1ab9559206b", 0xb7}, {&(0x7f0000000000)="3e0ee6d516f27159b44c5a66e75ceb44c5e1c610df0322764e8fdc0e31ded4602e7b94659ad54410fb85e18a7a01786d585b5c731a0473b7b4c0c6198e0255c842c2c7321a0b56f497aed33c39c40fa9986f20efade79849c784556b1cdaceeae17a555bddfdfd238cb9e4eb8e0b2e4a4e066372090000007128406a522b1ca808caa819608b766a9394f2e511a5e5449815f49ee8bf0862aafefd8146097450cede127d683be943a5c4424ad756dbb547e853130a2ec5adabf058baa46bc8a8c880e9d390c2f85dd080fe6b2da90014cd9d238551d6e487853b21031f95a771dff3350c95cca4f08731ab40639e7f0ab0aaa8c7f34c2a8e31bae1593f264c09ab95b23784b748e07662f9e309b457930000000000", 0x115}, {&(0x7f0000001b40)="ce0c2459a3c3be6289bf8b9ddcc9baec7c0f63a4411b7798f6dcb865c865eb60337b8aa063534332c29b9bb9715d5090e2afc5789051bb9e05c7ca733743209fcdc18d4baa2c8da2045e55200dae6e5c598f80ecab2beddb7deb63719acdee1ff8944aa3d890e7ba2cac28791cf79e920e9d131c901f6e764aef36694c3d99e2d796837a63dd9426a0a38dc4962ec23235feb3533446c12a65f364ff435573931a939f214294c6967ebb02b34d54da03edcd537e13d8e8bf1eb7659432d89a78e818d5f816c77e1d81ea36aa3c8a21d1cfcb08a044f1912e17da6e907f0001f5e677d7589c40e4f2119fe6621cb06edf39d2c53ed3070e5ee0d5cbea11170f7850e5667f01cbd9f520e38830d886c7a17ec31fcbad148c495b046d2f04e5e0390ffb720211cc5b4295fe7e9813b78f8c96bc98100204c61e8edc7e7b7577a8c566d0630c45e0816cc8515db521b108b30fc547835ad4dc1c954001fa24a9046598a08d0da1681bc24a39a554b2918223dfd45e064aa9ee8bc28f1018ce3632cd9fec854c62574c27de11c692b352c1b1a504b2dae64eaf3fbbfac3f8d3d3eb00401bd4ca45a59f7577c05a4b5b2e9a3874e1a37e24640a15265bc9339222b4097e8232ee5458e8e344ff41df96ab45f2d3448ceffd8a7f33dd4704c5ac9152649497fe22ac9ae528dcea16a8548eaa9a650467853f348aab77670b023deb9a4f5a8e2da23bd02b9a17c5c15af540625d7e93d30297562e4b33315573a201c732f4a9e0c8d378bda6aae77ad8c399d8c54bb5e0b69a26c0fa2a5506e04212b411d3a030e9dfe0174387c9c6c12039b9f29dbcac18532d349577f7160b9fc497d6c0341eea", 0x264}], 0x8, &(0x7f0000000bc0)=[@cred={0x20}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff9c, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff}, @cred={0x20, 0xffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}], 0x138}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000003fc0)={0x0, 0x0, 0x0}, 0x0)
execve(0x0, 0x0, 0x0)
pipe2(&(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x4)
pipe(&(0x7f00000000c0)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
renameat(r1, &(0x7f0000000180)='./bus\x00', r3, &(0x7f00000001c0)='./bus\x00')
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x8000000000005200)
r4 = open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
syz_open_pts()
poll(&(0x7f0000000100)=[{r4, 0x1}, {r4, 0x1}], 0x2, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x40}, {r4, 0x80}, {r2, 0x4}], 0x3, 0x6)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x18, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc038694e, &(0x7f0000000300))
shmat(0x0, &(0x7f0000ffc000/0x1000)=nil, 0x0)
shmat(0xffffffffffffffff, &(0x7f0000ffc000/0x1000)=nil, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCGSTATS(r0, 0x4008426f, &(0x7f00000000c0))
open$dir(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
nanosleep(&(0x7f0000000000)={0x0, 0xffffffffffffffff}, 0x0)
munmap(&(0x7f0000200000/0x1000)=nil, 0x1000)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
r2 = shmat(r1, &(0x7f0000200000/0x2000)=nil, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
shmdt(r2)
shmctl$IPC_RMID(r1, 0x0)
getsockopt$sock_linger(0xffffffffffffffff, 0xffff, 0x80, &(0x7f0000000040), &(0x7f00000000c0)=0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x800000018, 0x1, 0x0)
getsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000040), &(0x7f00000000c0)=0x8)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107008, &(0x7f0000000080)={{}, 0x20, 0x4})
symlink(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='./file0\x00')
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x5, &(0x7f0000000000)=[{0x4}, {0x5}, {0x20}, {0x81}, {0x7b9e}]})
syz_emit_ethernet(0x2a, &(0x7f00000003c0)={@empty, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="44b8407aec29", @multicast1, @local, @multicast2}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
socket$inet6(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="4b027c7f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
socket(0x18, 0x2, 0xfc)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="1000"], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
linkat(0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0)
sendto$unix(r2, &(0x7f0000000100)='\a', 0x1, 0x0, 0x0, 0x0)
close(r2)
accept$unix(r0, &(0x7f0000000280), &(0x7f0000000040)=0x1002)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1, 0x0, 0x3}, 0x8)
write(r2, &(0x7f00000012c0)="d542853c7d630a991ff095ac454db8e699fd30f7e6f5552158c05a8fb9ea7945d1451109fba4992329afb3f6744fdcf3a52e84bfefd31bc9f99824f208c348da3454b814b98549ec57518fe8fddc2e9129f8ba995046144544a75ada3c7c40c15d6c95163bf35a24a1b68a0d99acbe700126eb0569e07c070712918426a6265147ec14cdb2c5c04dd0e249ac5902038a2b0881006f662fdf1c23f299eb1ea3d8190f398a572e3e14dba99bef4bd6ceb8f46c8fd81137155defe1ce48c654a2aab027565b1eb568656b810428d24d81fc82943aa87641fbe57e8278f06a8d4739ca38571e08bec98bc6870d4f4bf68705db1ae0beef1b5c9c0a0810ec7800855aabfe3527c92174a55a9c2246e80f2175f46c9cc4578e29cee26f8be639057c25952638413441f352683ffe0d986b2cd90869066b9fd7912572b8b6fc3ff245aaa4b47bfb897cc7a6312da6ddee9fbafa9edabfc3cefe919800fea64211768c7263613febe434c5c1a8c5af18415776085a2573ceb30f1de5cd18b079a71a927d459f840f0fa50e1eb8b77d56bd29fa24a108a05c47403dedf802a2e501f1e14790dc7ee90a55cbdcbd26fecf35fab07b5ce7fe5432eb07cfd6a73bf0642c3d83c07f6b718de42a705cda72940f3772e753f741b47121ed17ba850b630a2a4052389d1e6efb03704f124e24ac863f8fda22837cd2a1ca357096f836ee20ad2c7017a0a746ce5879d8e60a6679b428b508e507145782230d00019dc8c9d566759aaefb76b854af35cc6efd37e884ac1f4f4c72b5964ad9aa1c0f4114a2838dc7d6e019d62a523d668f8eb012bc3b307eea057d6c1ceffb24ed6c4806e4ed0185903d24163c26d898b18895aaec90c95d2570935825e039551d8202546903bb9c484d6b2f6e724d324794810e25c9830e37acca5e74e32a6f08b7ddaa861a06042f795a1c5ae1902d953f88eaa0c12f820b404b75cf2f1e9b108417013a4c75bb0ee7f1fd3a478c10a88660b5d49c239c3ea9ab85d4510aec3283ee5e993b3689b9d367c5f479326ebef9043264650eb56c5bba9d8dd52ae28d7304e810ca2a17a8ea5b0ed400dfde111931e781c4ef6620dda778b97ee3b669bab5532dce56d365303c63320ceb89798106c69a165762f82fe8a3ecfa5094b34ee6eda176cce5e987a391bd54146470a41c915f980a92a3f1c1d8c646aed971989f9d8bbdc18b29e83ba5353d63a07f898f8eeff40f767afcd9e96dcb50a77a21147be4f20deff24ca9d58d67d38cbe5d800fecd42f95a4b254d7cf460472a4ff4b7d4e180e2eb3ec27c6e7f0aad1a0365dba9825d501a309381deb5973282d93e5cbfb174ea2e66c80375904f4e043d533304291c37d15335bbfb1496571ddc136da5cef2fa0e2dd2eba947a6028a4e900451cc3365a2ac274c40c34b62067c09cea335647564549daa3a7e3a1069fb472d41d0a7aa6738fe8b040bf08afb4b855214cad017226a04918069da85412442c496265403e265d71f0d553fa0244fe8d69d1c457a99b954d579fef6aa0a702739374e006a6086d047632878a84fa68da0bb20dc57baa439807a71fc086c07cfe1951b372ca7031fc9227594a64a21c5158645569a9247672923d1d011d167858fc0090115c3b65013f1f6612f09b007123927a9c0676be897f290cac9301551108b97f96f933305843b5e315973de83d42a799aa653ec7207544c4f225b1c33dd25bc574741379c5a5189359126246299c33fe2d0bcb8162c932fe7cac065fcf5ca3e0fd674cac3ba39dbfe968df8293865f22c765d484decfa59c624b5ff0aa3dab62d8539691beaad2c67eb18ab6b5ac2092c6888e648d23f8cdcfb135156e2abe6f4a3f1cd2261e6a1089dbd701a3c68599eefa128e22656a07c2b8fc4830f2cfdf7ac3055532db2dc9d2f814abeb845e9ef8af91647cd87612669b9e2c505bcfa173e65951bc51858ad5294175cf2be7a886d8faac942f9f12e2c4e5a9e2eb6ebbb861d80d195e8c8eb2b56cfd65fa73cc75fbe3dff7bf9434cb8294b891d0bb9d0d40f737974fc5d7e61e2ca1f29cc67a0ac0e16ed58e8118849421cc772c37b610256118b18e40956b6d46c53f22c468b567c0bc4f530dec20176fe35616716394d2bd1f58320f367121ac51dc1d74b20fb053747223944890899e3d3c2cab35236a6e0da2ca2919c58fdf45bba654fa05bebf16c918aec7a7c286bee7f83ee310dbbce9243f6d5d3ead726cefcb19ed0f2c1d4a8dbb98ca392ec4d846bf89bbf418bd87522e9a20272a79993263481635c00cdff8d9bfced071ac867f088ed9446b3f7914bd977fc21b54f34e9c09d1149a87a26ac2ef2edfe76e6ebf329128e6ac356c9d69839a1526c37313b4b0c56553f62665b94ef72e59b7df90d9da79737e69e75529461eb8c6eb6ec344dd8119b784903f3e6e855ea79b2aba444f39447af1de01fde7cc7c27b17324db81a060359bd4aaeeab42b282e59ec23fb2d5c4f0ccc1fc6eba1fdcbb17e006e8910e6a0a90469f813ebcca7376be5a959d0bab42e0acb82706a9dfce72db80e4c6569b62328e82e68e1b3e77531d17166cc86e193885bce1ac08d90a08b05b1af76befdb5d1fb2563de90a440d42c992ab16545cd858ee2b7b823218b0585ea6e15020c89d9c56f8cbfc6c871a24d6dec62169cc51cb8acba03d7f5b286133a09b5562d2d0cc9d0c8a6730f730c31d0c0c5311306cadc0d4d37809b6e9113445cc7f23f83b57d107714552116ef366e2932a896aa82801928220ef4304a263ef77d22804e2e5bfcc3cadba56f33cca92e65168720941e1c4c843af9f1d6a56a2ecd6113e27850da10fc99062d0945f93973a023942b533377d5ef71e7f4a98350c28d1a8cfc38190a285a27173a9b1c2869e4e62412796645925dbf614695b2010a7cb8cb1b63a39408b3e019be907b79bd67bfc7ae31dc37bfa88853d923fdc7e212f2ed587d8cacdcc1f48ed5ec7cc7e8b3eacfbb551c28dae0bb796ea603d44bbc6a8d14b7b82730c0cd056ed850adb05bb8fd53f668011918c4bcdd5ca2ad8cce029e31be43892470006190f5a7183ac311ebbf40b0a6ac815a2641f8e36fa1b7f2e7bfa69599e0ca7ce8f2fd0be00c098e07f8d856c28986fcbaaa157aef9c8ea954aa102c06152929cc984abf2c8bc3e677ef7a3574bfec80f4a41d9d363faebef157403d0f3a82b1d0ddd9de7cad28346abc5ef903b24ef53d6e3481058fc8df960a86e822facb7aefe102473af7fa63b8f25edc7af5b4c8ddf37ed8f44a056d2f2806505eab7cd7621cfadab595422bc6bb3b32105bcb2ef75bfc58958fc0b17d125d2c8052e7c6d2300d2f1b24312fcf0d2f692e9aa883729f44758dbe895cff39b37907f58218175f180fd49f0fda6548972822b53da7eacd7d24b617668ee1e5825f59c4f08df68e5eb37f346e518aea0fc90406c5108b6752a51dc72888e3e6e4b49f3750d22e52db23a4c4f7858876926e4dbd1255826edd5b20e2188357cd686516995208af0817f1ecf63953c61d46da2fee1adbbfdfc2547472a8e0e341b042dbf7cf6f8d2674ee526752b589dd548d749fc5f142f42226967edcbbe9e7dcd60d68e365e473cc6db3be2673622613bf0a31eefbc5161ff9b46d78489214ff129756ab23fc9a9cad4e0ac135f13d4452e489957eae1787199816b934a427954c3eb961f8383d4535cc35a354ecc045c4f26fc4cfcb8bc35de1983ebe1bf716299188b1bfce1dbb6cef7294bd7f68a0090b65a4f36ebd01190a4147daf5d8fc3b20d6a557568b48e6e21dd73e2d13b1ba1ef24d44572a7c16a582274bba0876aca5d60b43e677e42a8dc1b1f84448f31d9f8fe2da40c456bf7f513f849a302eb99f0e8a27c46abf8b0b8cab15e74e25697e2f5e0ed5fbaab57ebaf8d7857450c00eaffe7c7cb576da8f378494a0340256ff6d41f5a18609e4901a6a3a752062fe7e3f1caee44ff002229ffcd4b4c24e9f5f9465ae1ff9febc0a42678b1d824229c4335bbb941fd54c758843f0280c4a73d273d79fc80a37d2ececb9c28d317a438d674b765c77e603c6c04b13e0f310990e7dbe9f566cbe5ac59515495f63a878bfcdeab4a0cdc30e55ebfbf7b07ac3912f56419fb33cf6afa6e4dfc3e585c8fea4883d07106987b6be5c7d0e86210b0c86fc059ca352d3ee66a2df44407a40421cf6c92f846449973ae6a1e658d971a5374e598de33afd71d9172ea131f7b20b0311a80a2968c42b4e8f3ba4cd2168517edcb664e5318f7592720b8cfb69ad7f2e7ebd549d34bf29f9dd0c83c1854d54d6c3b079e3cf5a94a1b7c0787b94e8919e504f60488cb9aeb9879fe2a1079ea0ab80849f1db19b20f44b2344a22d275528a1ad04a627ebebb233963d98645c7217ef1f1b8b566449cf2e793731e85f166b91d354c64cf88a3a3f92439fe1cefee353589e6a7e9e8c72938c24fbbdc92e01e1e3a31322da7208ec08776f7322b2aaaeb9bfcac0c7877bde249e0caf9d6ae7ca7a4e3a5fda34ba682ad10129e8bd7849f6eb1e97732fcd678111361b7d39c5e8f43dd9f88890241a07bc04964f42b1e1c684bb57b22b2f79a4cded6f03132df90e179b9e97dce47dd49e09d1f0cd60b80516b73a81de7c865a217db95c993814240cf03e18618369b028a0bd13cc717bc3147f194b64ac2128c69c3821f33ad217a6f1c7f9e85653b004b17eb27821812a80349f3c390fe90c3a12dfc13dbf35a84909dd3ca6ad9f510a92d786a58486b0830050ed6a96ab0b1bb1e9246b12065b9bc10230f38153c3196b344f4e73ad8784ead0318894946a077402d04d2dfff23cde6a70c12c6f7840f73a5d28c2778ff39d01ded4b4606725f9b1acf6e6065a4dcf900d4d0fcfce5c9a47136feebf6a42486222c711743aa8128b7d7624c3b04353049b49f0188b721446b47bb774885aaa98434e353953672fbd0eff009c9667527e6618072b615548b10022697f88b144824e4ec1b431ba23d2c0a545adbf15704ee1302ae994d05278c5e3853db0d76fc41c8f1462ae00a2e0ff30b33aab5778b8a0864b5e1781a00ded6941845ffad1ef39c30f74f4e20c6389b599c99b43f908ca8ab83b9dd96b7b21400a65aa9d8a9942df18b92c5c643056b52431db4db7cf7ad747a33ecca0ff4e0b2ed2803f6b2ba6e2ab41cc4ebe53d5dd9eb31dd001a48eeb1034770b1f02b38bcdb29f1ffe698b651c5fb42a6e2e9f85da6f8c261b88fd81184aadff8a5633c4d60511e5a433e9dc6b78f6f034f96d8f01c214309f30fc5cf6b3b7ca89d17a50c98d522ac1f1910ded7c4d873fed74c65d9b27ab7d750a5c3a3da0c2da426fb9adba5c61281a05d1e63bb97e04d62be7fce3cdb3012ec6f8b6ec8542e41c9048ef4c88ddbcf36ebea7bc0acafbcfb29bdc897b8acff86430a91c1eb2a6e233b11279cf56bf3ae3d00fe02bf4f0a3210b624f9bfb9969cc1e30e4130eb3fd30fa6d9ee0428e7ad73711026d0b82c80459643e729acb92fad638d8d1dd19880cc830e7465a57032416f87069db04d2e278ed467982075513fd465b467dbd60cc076eb943526f4ddd8ed41c6e99eae03062b1760b98ed3b1039047d5a56f1f4c4140c8ae28c7708e3843c23665fe8beb155cb2190e2f7d5276b202e9262347437d12c99470f00c0c990f160cb2bdd6a1ea840cbca208532c2cd7d55bd34c18d06373ded9eb4bd4c59cecc9163aa84873955bff8081c0135671591a48853b528460aa75726162533e5e87ce19497ce5", 0x1000)
recvfrom$unix(r2, &(0x7f0000000200)=""/89, 0x59, 0x42, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x2, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
flock(r0, 0x2)
flock(r0, 0x1)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x4, 0x100000000})
close(r0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x5})
sysctl$kern(&(0x7f0000000040)={0x1, 0x2c}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2688, 0x0, 0xfffffffffffffc68)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000040)=0x7)
poll(&(0x7f0000000080)=[{r0, 0x1}], 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x80206979, &(0x7f0000000300))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0x0, 0x0, {0x0, 0x1}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000080)='./file0/file0\x00')
renameat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0xffffffffffffff9c, &(0x7f0000000300)='./file0/file0/..\x00')
recvmsg(0xffffffffffffffff, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xffffffffffffff0a, 0x566, "d79400e7c40ec6e30000ff0500"})
read(r0, &(0x7f0000002540)=""/105, 0x69)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x3, 0x0, "1e077d0900000000000000930a3410490c000006"})
write(0xffffffffffffffff, &(0x7f0000000040)="eaaf23451b9390661d1ba4f12c904b710bddf697f98af346d273ba13c33314c918e2c06f0f747fedd0922a51e292382410d361605fa64778f478d5b3f7c6", 0x3e)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x4000010002, 0xffffffffffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
mmap(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
sysctl$ddb(&(0x7f0000000140)={0x9, 0x5}, 0x2, 0x0, 0x0, &(0x7f00000011c0), 0x4)
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@random="f3ccfac6c1d7", @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @remote={0xac, 0x14, 0x0}, @broadcast, @loopback}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0xc}, {0x6, 0x0, 0x0, 0x481}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x45}, {0x84}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r2, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x45}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r3, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
dup2(r2, r3)
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0x0, 0xfffffffffffffff8, 0xfffffffc, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000014c0)="e503c61d80583de86ea2d148cca602eb0657023464e233112274b781731ade6ce55d6f7afec296ded73d238abef715fafd4878550b5ae5e0322e293ae4e5cd519280829bd61933159593bc331e33a52b5adadcf490f78b92ad6bbf96cf058bdf43ff7064a7585ffb497f84be4ee9bb8e49007e0dc1c79295cfd07e5a3c4505933bb40cf0456686eee9bb3309f16aaa5fdc16357f7633d6c343fc4234dbe18e1666caedac66c76d37692d1e8ce408f0e1e39e73797387106a9259ec87d601b009746350ca5a9ca0274690b50560de53220fc2815a78f45d277d5002d3f6d428a60fee7a15de1de6861685d366b18d5017d9d3e272705ac7b6d9b2a2035921ad860eedc636c7d1341a6c9c9cb2deb630b4dee1d54d3628914fcc0dc5bb93a1457844e5dd8808e4aeede385f70e080af31871a7ab1d9c9a78b1ed288ca2dcabd5fed2826f54265c8f75b487b3766f8fbc0527319298927930707dbba165bfa3c5d3432abd55fb9cdb41280eae16c186594d85a55842264a9b5421731538d9b392eec31ae109fd9813bc067086a8db7fb046ddd04daffb3ce94a4843621a33d5d90f99178b375e2848802e1b99b3b974d7c53192e469daa156e5667fef2ec5653c4c896a9280617a7de3e5c7f47c35342a81f72e4f9c42140017300b5bdc624f4e7c3be8c91e0fb87590d6e9ab1d547a8d41319e40837678d23715e0e5b0a4da9121eae6dcd6792574aef12d49ece93d6fc31d77214f0bb08da10bdbc6950c60b1ba11c44980e21f78c55c9ca817507356dbf3b62a0b588ba243835e8d59e877e5f8c459dc6f06a9596be14ab364fa616b71c56f12ed3a028cd3106396e2e2ef55a208ee4f12f1cf619f134e046d0cc28fcb2a465e22372eadfdb5e08f22309ff23eaaf5205705bc29729244fed42fefae8f363c70ed64e37845eb2faa6b8da8d143c75f6b23c92f527edd0576814ed90033ea760b1c2eeabaf5ebedb269b845541ac4a1f68bc13a9c9f65eb3d3e1781f4864c08775448f30f4cb4632131c23ac76636031f9503c902ead364154d460caf2b9371673f52e57bb98c181e86ffe291f30c1e4ba64be4b03b65336dcab894bc75bf5d5f505d8a8f0708ddb51fee9a47e3276e427becabd7d331c1e126746082c07c921ec1743f46f5165f6f9eec781a66ca6dcb08283f12b8c856c53fe7388ebecca7cb4dc9cd60321d834e0c85a8e8795f8ba6dabf6f1df96fad15749813a33483de3b853b821f2dacd90a970e513ad3b108cf6706e61c9182adef982c24b764e65513bcf17e6e169b1fe7da7f05ed8112a691964af4582ccc9ea61afa4aadf97459f8fe9df9a0d03a7e6b362514ac445da8cc06b8092be3049a0473ef1192dabf1ce41cd65d8fbe137b2317f2aa6fb8a12e1275ec5baf15bd3a0cfcb17280a53683b21bd2ec134b2d357f49231f1c49421ea8c6679a66a9d526cbd8fbd6a7c285f0882648a2ca16bb5561322ff5e046df51dec0cada6d75cefd5aea53191c3e12760a8490ecc308df8d9d4475c3ddbda945b12e7aeacf1cdfc27dc41979ba4c050bf04281fa762799ae06691a785979a5a97d45733e7c51606670384af46c06f180dab18a4c1028d5354222d54aceb5f8cd5f1da4b3487cc954f05a0b652d2d8adc1327f2a92aa0ef1006d70f121d7cbdc2c519654ae950604e83e23ceb77b2f02493cb99d7c7698f6281890950ecb6e7c46ce9cdff99b31e3eec77f8b4e0d78abcca03356606e0c394bc3513e24f3dd0e073e9d9ffcf210311505306a03aa6f4111b765865a19b4567a59aa582b54804a2a77b4a246af051a4b7cd22245206bf4d3ad6b022905197888f841e50f16b647c7c4a60e5412f01f99379b3247b2c4e57a627b4da2722d9f02e1c251b805242320c0e4e92d6966cf5dbe35124ee702e10850314f5a45c35fd497ca7ab6dc76a50b13c263136db5b0678bffdd7ed18d605f9dacf0346842f16865fd292d8937433fcc65c612042eab65bb783570b4b48178fd35deba95fdc39f3ddd325cbf79b56e53b660561f3f6b44fed109ed1e923890f8fe7c57d06d4ae5b098b2464d63e97fe533ab6d03616a5d7199cad8470952349a76ced2d458c28d6fac9338791df5a23d1ea52f7898db22ce959ca463592e39c656748156b41eaebcf58f4224c4c85a8656ab1916d3aeb82a0e4537b2aa1fda04575eaf4a56fbddadce2ce68b0ae8c53f3e0db0594ec9bfc7f95b02bb188489853a7efa354ab2510852422dc1d5a60cd928364f0e45ce5ecdf8fa6c0b6d6f44ddefa61de03cceb3eff026d6d308fd7d7ed9e7a9e7a8201d545524e6df3b8f376fadef047f130bd39a8b4de1dcd6ab5c6c4b53d7815828a01da78eed1eb097fb88cb21f6affd087fbd17a6014b491fa4609be2d07882259fe0d578e71d0028b986636dd2dfaa2fd1e6a6669177692a1ce82ce6317dd6d45e7a6fef72b56571848899f4bdfa66b59c8ac646a637c3df441ee5bb46af79a7d83e160f760126ef381167a14f2e79994893cfb774704f857fc02af519876bac4cc28bbcfa71271e051413d311a21d0bb748bf0d00a63ee0ee66e440e9e13e35b35d390c2a6f36d7890895ae7bcc828a9d10f61e4edfb91fc3272527711a665fb7817265ec9d02f76d625f824ff364b0280916144eec5597d70980265522fa783b9602355732d7dcaae870f2ecc36021285f39543bad16f343dd7df202049eac437eccd300b6c2510dd7a9fdc5f5105b0a726f6f841131d158d071f89b6d6f5ab6b4cf668d143c516fd5afe638a3d856bd5a5b8c455dc21", 0x7c1}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
sysctl$net_inet_icmp(&(0x7f0000000080)={0x4, 0x23}, 0x4, &(0x7f00000000c0), 0x0, 0x0, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
getrusage(0x1, &(0x7f0000000140))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0x400, 0x0, 0xff, 0xfd8, "ae2048d132f95c143d000000000600"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000013c0)=[{&(0x7f0000000100)="2464d05a4be3194041357a8b49667e82c5444cddb8767f8fef8740befef754f86d974822", 0x24}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x20}, {0x5c}, {0x6, 0x0, 0x0, 0x200}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1008, 0x0, 0x0)
r0 = socket(0x10000000011, 0x8000000003, 0x0)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x2000, &(0x7f00000000c0)=0x2, 0x4)
write(r0, &(0x7f0000000040)="78f4752e3b11a2ad4b1c67fa438b5dc9dd4ab6fd", 0x2400)
readv(r1, &(0x7f00000025c0)=[{&(0x7f0000000380)=""/4089, 0xff9}, {&(0x7f0000000100)=""/197, 0xc5}, {&(0x7f00000015c0)=""/4096, 0x1000}], 0x3)
sysctl$net_inet6_icmp6(&(0x7f00000006c0)={0x4, 0x18, 0x29, 0x7}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x80}, {0x2c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x20}, {0x2d}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2fb21004aa08e2d554ccf32c4", 0xe, 0x0)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x28}, 0x2, &(0x7f0000000200), 0x0, 0x0, 0x0)
sysctl$machdep(&(0x7f0000000000)={0x7, 0xf}, 0x2, &(0x7f0000001280), 0x0, 0x0, 0x0)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getpeername$unix(r0, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000001480)={&(0x7f00000012c0)=@file={0x1800, './file0\x00'}, 0xa, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000000)=[{}, {0x1}, {0xffe}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @local={0xac, 0x14, 0x0}}, @icmp=@info_request}}}})
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, &(0x7f0000000280))
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
ktrace(&(0x7f0000000140)='./file0\x00', 0x0, 0x0, 0xffffffffffffffff)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x4)
pipe(&(0x7f0000000340)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
readv(r0, &(0x7f0000000080)=[{&(0x7f0000000140)=""/120, 0x78}], 0x1)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x60e, 0x0)
pwrite(r1, &(0x7f00000003c0)='\x00', 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
write(r1, &(0x7f0000000000)='*', 0x1)
execve(0x0, 0x0, 0x0)
setsockopt$sock_timeval(0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x0)
close(0xffffffffffffffff)
write(0xffffffffffffffff, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r1 = open(&(0x7f0000000200)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000380)="90c3fe67eb586898600425f2f573e0d1ac83c18d00c8e22066c0d389fe895a974c8d45aaf9a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3214ed85fb20e088c10b8d9f53fabdc099c74fbc4706c1d2e5741ed065c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e32082cec454327b6a1522c332ea628b8cb672e9e724780100000017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802", 0xcd}], 0x1, 0x0)
writev(r1, &(0x7f0000000180)=[{&(0x7f00000000c0)="11", 0x1}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
mknod(&(0x7f0000000100)='./file0\x00', 0x2000, 0x804)
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = socket(0x2, 0xc003, 0x0)
setsockopt(r0, 0x0, 0x20, &(0x7f0000000000)="caf5b415", 0x4)
r0 = socket(0x18, 0x2, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x18)
shmctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000300)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, 0xffffffffffffffff})
syz_emit_ethernet(0x48, &(0x7f0000000080)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "51ea17", 0x12, 0x3c, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x3c}, @routing], @generic="96ce"}}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$VMM_IOC_WRITEREGS(r1, 0x82485608, &(0x7f00000001c0)={0x1})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc02069a5, &(0x7f0000000300))
ioctl$TIOCEXCL(0xffffffffffffff9c, 0x2000740d)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
r2 = syz_open_pts()
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000040)=0x7)
ioctl$FIOASYNC(r2, 0x8004667d, &(0x7f0000000140)=0x100)
writev(r1, &(0x7f0000000000)=[{&(0x7f00000000c0)="7287f9d710c69ad27da12efac14ba8c05a0f46a306d64f5dbf131b6d94b97f78beb3a1d2191ecfa85cf10167f3a680b896bb78540a02686ae3f6c2d5afe15716a293603f6dee0ed666dcf850a164917e0f95b1936043a960b43d26ae9a0b3fd9153488a42edc1c", 0x67}, {&(0x7f00000003c0)="836a441e145c53fce1e13ecbd2b5c7175325cab2a0eb887f6abc592081224ddfe99d6ec7ed47b3cd616b2ea17d6f1f1bed3ef7baf1ecfdbe7993fa93e1e9676923f46d0d1806c9213cae4b986887da6eb237a45ed10f1450873bb85c33eeb735290a6d4bbae9af8568309fd4db36c5197fd0a2d637c8bad3aedb8dd9cfbabc0bb8a377daa23b5a99d13bc5da2fb63788e2bfd13fdd68e55bbccf5c4e40fe70cb7d03f4c5eca7e993ac3254595ee9bc3b253c850a2acca76025943e69d759aeb4f3cba3c2e4d460f6c7a43a300202cf4b2177d3c322be87bc91437a57bb2f3a7ee07dd0cbce80130a1bdf45f8e900c03ade47219d0a2781473b11f698f38103cb943d97d974cef8968142e732bfd981ba020121f8cb71b1adc1d1c5c235a2f949331ed772ca2c7327d3d013d3205db23cbacc299df08f5decbd64e6b9e8ea3954809ac2e7667cb76c213c984c1cdbdd0b6e70a385c4188ab1bf571459af74f8442c8b046e37d5bc895379d2f3a4b54eb4f9b532eee95783c156044792f4d721317c3eec8f0e4e7b8c9f19cc50dab61588156746f7e5c17cd798a4555eb33f39ac0b52cfa6427e4c7810aa0012dec25c3c28988981c13c486330199da0f108fd203f189d8a17a2223c3f17682b49c9a67ec65b0c3424e5433169303c3d84c9b7171b1b7f317f22c166f03edea272ecdcb32deec94119f65b342c5d678d84c73794a3c81151c71685a616e4d6301fa52591ed40beea640106da0546992bf4d74b706d4872a11f7a2213abc16e8849567b7b42d2ed68c208c793a2c018229092257b47f2f269252d1e5b99ba08113d62d59f6fbd9442f27229dfbfc2ffe509571a6075b28eb8e118146b93fd22e70f778b4c6af63da45abf542646ac995416f1953752687d1f369d24e52d84a6c50214f20f6f74f9dd7c0c0cf5209aadad39421f387d82755ffdafe2b886e43dbee68eaeebe3afb0a1b657cfd4b4c907330195f7864795e3436b71c2b193cfed79d7b6227cf0e2eed452d275d018cdf037e605d45e223d35ae6f7f7f156c2e0a6bf3bb5199d6ed5b5c9a657d7e354c8d1ea1e3b85ab622b4b6e8028a086680b7c67741035dd029d33c1e6f76ab3b544eae8ace59185c9002a2bfc4f7cd7e40769b2dc4236de7400c9560a736b35625192ab883d55cf8129ebbe8d613e372e8b9022250cd03c26990fd91a774596c9053fc92402d69dd06960811eb08aa22a7f5bebf8b111d6297aa5a2993c6d63c58c0588908c0bf6b426808d1cde728f65980bab1c82d4f047b066dae938ae149133d8eb03b6972a9325c605933782e32b8e6c989174d8da820083a778760a2109a3ded32ded3da77e81ee87772e1224d86bdabe94f4af13b7984cd9bee3218b5db62e250f8e79ebbdc72fc22ce5f15e9e41dcbedfb8eeac669351a70ec5617bac28dbeab2c675d", 0x400}], 0x2)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xb, &(0x7f00000000c0), 0x4)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x87}, {0x1}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r1, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [{0x0, 0xfffffffe, 0x80000}]}}})
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000040)='\x00', 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
pwrite(r0, &(0x7f00000001c0)="2387", 0x2, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000740)="055cc787171f20b652bb01607f577d50c2a851f0f1b007afff3c57d83668da3ab35628f24bbb2700b4d4aa7339d1532109a5c4de3c72dc546affae7126361ca65ef03e9209b1df284ccef6a599c354e6bddec51cd3fd27096f845eb24282b3d5129600267744979d92bc280f29ffab6917b41571a81c7604fbc76166308f2c729bf8059a5c5f569365e622c097fcf4be89def98f34473c10e6a303d41abffc0a0a4f0d069ad9de7528c7deb2d962025210cbe9f7e09ba93fc4270bdaaef38056f229e22ba255cfe3248d41ef2c40690ae656a72a1fd905cd93f7b60936f9e7455a40c03f2f36e05071f24f0909759fd7493ff552b125c3a2d3fc99d51b448d19d15d6862bc0cae0d35654a4c44823ea44919c03f1cf061696b8a905366c62eedac405693bb872962a8a3812768afa87059dfb57aba0717676de7e376fb59fc69f02164475f78d94ad671b424697e5cfe2c0f60988708f7be7208176d4682f8d39b7fb5460b8f8b0e8944634b5340d1d9369825b8d478e93618660490b77187cfcaad226c22c346fc8ba04c9c31168f2e9c7240fa37e2014e3e2f25f667abd10c7736d72b05400744c99cbff31ce40015a85a2cb94f65a795a2b1677d62eed83a77c1906b6d49b920f592ce2e8d76e51a9f48a7386dfceccb5c15f786aa792df6265bac8f365c6b925698937bad2dc1ac340ae40b275348e293c40aac2817bcf1a7576b8674f19de704fa246efa0d33fb4f94ce77bc74a036103736d42854453e67a4bc43418bf604223f832cf6bc43c86dd4e1fd26b06c7f7e74095218f6c3508ac62f507bfab2ffd5f6b417699a4623fd2365458a0fd420c9d0ec0ecd2484615af49778b7a8be497a746897c4c4d2ba7bed70a6f7", 0x275, 0x0)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0, 0x810, 0xffffffffffffff9c, 0x7fffffff)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f0000000080)={0x0, 0x0, 0x2, {[0x7, 0x0, 0xfffffffffffffffe, 0x4, 0xffffffff, 0x9, 0x2, 0x6, 0x8, 0x8, 0x7f, 0x1ff, 0x4000000000010001, 0x1, 0x6, 0x804, 0xf82, 0xfffffffffffffffc], [0x8000000000000000, 0x6f4, 0x8000000000000, 0x4, 0x6, 0x2, 0x6, 0x20, 0x5, 0x10001], [0x400, 0x10000, 0xffffffffffff0000, 0x6, 0x0, 0xd294, 0x10000000000000], [0x0, 0x8, 0x0, 0x0, 0x7fffffff, 0xffffffff], [{0x16ce, 0x7fff, 0x4, 0x4}, {0x97, 0x2, 0x6, 0x80}, {0xfff, 0x6, 0x1, 0x6}, {0x5, 0x0, 0x6, 0x100000000}, {0x0, 0x200, 0x400, 0x40}, {0x8000, 0x9, 0x6, 0x1}, {0x4, 0x3ff, 0x4, 0xb3}, {0xd0b6, 0x85, 0x5, 0x3}], {0x1, 0x2, 0x7f, 0x2}, {0x7f, 0x4, 0x0, 0xb43}}})
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000], [], [], [0x0, 0x0, 0x0, 0x0, 0x6]}})
ioctl$TIOCCHKVERAUTH(0xffffffffffffff9c, 0x2000741e)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x800, 0x0)
dup2(r0, r1)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
dup(r0)
r1 = kqueue()
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
syz_open_pts()
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x4, &(0x7f0000000080)="1daf63b25970aebd36020d25e1b10cf3ece6b8be02", &(0x7f00000000c0)=0x15, &(0x7f0000000100)="c2d24c9d34fadd0eae82b8e3168fa740a37829960f8862294ace4e16e103938e10e9fef2d403c0954d8babb49abbd7757e100f97a12aa77f902737ae02664eb0c56d03bb914b2b2a4c4cffe51a6e41cf38d5db8c47370140fb18077b6f8e963e39233b5a0e053e6c86179c589f83b7fa0105135c0b110bd373f3850f556b04ee1ce94eb6e707ac4ea0df9499463f04a489a12e300431c0e42d390839498f20412301ba6400f392533db65e70b870ff4215a0c879ee965532ec5852910377cb30a7d9cf5b1184b39d9d8c7dadb9d96aca5c946e6f5c5c18b50a", 0xd9)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x3d}, {0x87}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000340)={@broadcast})
sysctl$hw(&(0x7f0000000080)={0x6, 0x1}, 0x2, 0x0, 0x0, &(0x7f00000001c0), 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
fcntl$lock(r1, 0x8, &(0x7f0000000340)={0x0, 0x1, 0x0, 0x1000100000000})
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000006c0), 0x0, 0x0)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000540), 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000005c0)={&(0x7f0000000580)='./file0\x00', r0})
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
execve(0x0, 0x0, 0x0)
acct(&(0x7f0000000000)='./file0\x00')
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0xc02, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000000c0)={{0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}})
openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0xfd0}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0xa, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000040), 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x84}, {0x2d}, {0x6, 0x0, 0x0, 0xffff}]})
write(r0, &(0x7f0000000140)="7c0000fff7fbff00000000000000", 0xe)
sysctl$kern(&(0x7f0000000040)={0x1, 0x55}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r4 = dup(r3)
dup2(r4, r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x3)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x8, 0x0)
select(0x40, &(0x7f0000000140), 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x33, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
syz_emit_ethernet(0x4f, &(0x7f0000000000)={@random="83b6a410aadb", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "f7ef86", 0x19, 0x0, 0x0, @rand_addr="187ee3dfa9bb375c2b779fa1fb7b2747", @mcast2, {[], @udp={{0x3, 0x2, 0x8}, {"24d8f2f5a94842281bb9e2a8ee460e4067"}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x3}, {0x64}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r1, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
sendmsg$unix(r0, &(0x7f000062e000)={&(0x7f0000690ff6)=@abs, 0xea, 0x0, 0x0, 0x0, 0xffffffffffffff61}, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
writev(r0, &(0x7f0000000200)=[{&(0x7f0000000140)="6678b81d1749257e0c1daa36796cc1293fa187bcfffe6b847a8a62a9a9dcb6054454a40223ccf671d4ceb35ef133e6bb39d01183884416801899ec0c7fdec469169253548a036a828104de6cf9f55e54751adc716b6f1504", 0x58}], 0x1)
readv(r2, &(0x7f0000000680)=[{&(0x7f0000000240)=""/233, 0xe9}], 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000001c0)=[{&(0x7f0000000200)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8de7ae9fed58938eaeac68a0b0632688ca0fab3647175a23f70cbf22fea1200700000000000000295bf234505356095dbf9e50a4a5079723b5a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332cf92f38ec1dfdb4a91b59354736154ea628b8cb665000000004ac3d72160c9bb39af632ee98e00ec66b53797bab834d514bac90b3f2fa357b138589a44c17fa44177588cb987295b6b3110e71975a900b6f8af1ad1d95d5c40d4", 0xbf}, {&(0x7f0000000440)="a5781d1fe6aee59a92131f30", 0xc}, {&(0x7f00000000c0)='\x00'}], 0x3, 0x7)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
syz_emit_ethernet(0x3e, &(0x7f0000000000)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "f800", 0x8, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @multicast1}, @loopback={0xfeffffff00000000}, {[], @udp={{0x1, 0x2, 0x8}}}}}}})
mknod(&(0x7f0000000340)='./bus\x00', 0x3a0914c44f7b202d, 0x501)
open(&(0x7f0000000040)='./bus\x00', 0x10005, 0x0)
ktrace(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0, 0x0)
socket(0x0, 0x0, 0x0)
ioctl$TIOCNOTTY(0xffffffffffffffff, 0x20007471)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x6c}, {0x84}, {0x6, 0x0, 0x0, 0x81}]})
pwrite(r0, &(0x7f00000001c0)="895e3dffffffff9814aecd654f5c", 0xe, 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000040)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00?\x00', 0x10, 0x3a, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="4523b66c03a9c45479dfe38664457158", {[], @icmpv6=@ni}}}}})
sysctl$net_inet_carp(&(0x7f0000000000)={0x4, 0x2, 0x70, 0x4}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000080)={0x7, 0xa}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x3f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8020699d, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
socket(0x10, 0x8000, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecf0121329f7ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d02000000000000000002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b5257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0xa}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
pipe(&(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvfrom$inet(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x100, &(0x7f0000000000), 0x4)
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086333)
close(0xffffffffffffffff)
r0 = open(&(0x7f00000002c0)='./file0\x00', 0x611, 0x0)
pwritev(r0, &(0x7f0000000240)=[{0x0}], 0x1, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="82022ec1b6"], 0x10)
sendmsg(r0, &(0x7f00000008c0)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3, './file0/file0\x00'}, 0x10, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x2}, {0xc}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f00000001c0)={@broadcast, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @local, "f9438477b7432b191466538a04047155"}}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020697f, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000380)="f32c0248a241ca0aab6a17ae6587799d640e11362f980e911da1a8a18648825e8ef66c1ee20bd0ed2ec8988d", 0x2c)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_linger(r0, 0xffff, 0x80, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f00000000c0)={@random="831ee5406df0", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x3, 0x0, 0x28, 0x1, 0x7, 0x0, 0x0, 0x0, @broadcast, @broadcast}, @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000480)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504446, &(0x7f0000000000))
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
chroot(&(0x7f0000000180)='./file1\x00')
fchdir(r0)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f00000000c0)='r\x00')
open(&(0x7f0000000300)='./file0\x00', 0x200, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000300)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x9, 0x0, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffbc, 0x0, 0x0, 0x0, "6fc60900e4000000001200"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0x0, "d6e696d83fc4209e414d70000000d0c73d6500"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x48}, {}, {0x6, 0x0, 0x0, 0x3ff}]})
pwrite(r0, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x15}, {}, {0x6, 0x0, 0x0, 0xd920d164}]})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x3d, 0x0, 0x0)
sysctl$net_inet_carp(&(0x7f0000000500), 0x4, 0x0, &(0x7f0000000580), 0x0, 0x0)
minherit(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x0)
mprotect(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x1)
mlock(&(0x7f0000ffb000/0x3000)=nil, 0x3000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x4d}, {0x4d}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x3f, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
r2 = socket(0x1, 0x1, 0x0)
r3 = socket(0x18, 0x3, 0x102)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc050756a, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000e40)={<r0=>0xffffffffffffffff})
getpeername(r0, 0xfffffffffffffffe, &(0x7f00000002c0))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="10000000ffff000001"], 0x9}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x10}, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x28}, {0x35}, {0x6, 0x0, 0x0, 0xff}]})
r3 = fcntl$dupfd(r1, 0x0, r0)
ioctl$BIOCSETF(r3, 0x80104267, &(0x7f0000000100)={0x1, &(0x7f00000000c0)=[{0x2, 0x7f, 0xda}]})
r4 = socket(0x18, 0x1, 0x0)
setsockopt$sock_int(r4, 0xffff, 0x1, &(0x7f0000000000)=0x1d, 0x4)
connect$unix(r4, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r4)
sendmsg(r4, &(0x7f0000000500)={&(0x7f0000000140)=@in={0x2, 0x3}, 0xc, &(0x7f0000000840)=[{&(0x7f0000000440)="446dc17dc4937ff87fc23ecfa63335621d00ac9e5268657eae0bb8f8a9fec049078f0ef01c02661b79475a05aaf5b1b7c2b39b184eaa5903d98eb326c1b6d57d12ec1782b5c9c729387313610ec53c83fb36f91cb7d147211a0d667527b58cc495019e26e88c8ce645b062bcf6515d52dacef357ae17217a0267a11a663ae7f492ed09b81bee02561efe5fa2c8e63178eabfedad73968411d691dbd8d22e4f7932a7aef92c3366016ff5a8747ffc214921136632058928fe14b1", 0xba}, {&(0x7f0000000180)="69dce45c2420975ba121c810868fa71f3c18d019c0a6ad16a0", 0x19}, {&(0x7f0000000580)="1ce7aab2ecc3d43d70c9c491d25a4cc3fef900c008f800526795a469f9ae8574c8d9ae518f4f0faf4c53d8530ae9f645636c5214d5843f0996c9f5780c471ec7c85b718cfe", 0x45}, {&(0x7f0000000600)="e9f190935a29dc0bc41147ce196fb7a9da46a9334c25a32f81b1616203ea0d20b7fea195c9c692c6eb80adedf92bcf761bb6e484e01a6e4a5b17796710736c7a10a7dffefcd7fcd9a9627aacf593a22ab25dc34fbfe4162aabd3b45df4bea6291d6575a5bc1611169b35700a34282dce4d31fc7232ed5c120a0dbe774097737bfca85dec75fe56a63dc2cc707d582c1668dc5cb73344f2053db239a15e2991ab019f8ce4be7e57044cf5dfcd413fa750f6e3e177f8559e98fc54aad2f692592ac1c6c6c44391f4bc1cfdf828c84e631b39d48e60", 0xd4}, {&(0x7f00000001c0)="39b31659984a921cbe7baa0606341f79713ac785f46084", 0x17}, {&(0x7f0000000700)="5c0c157551778562a5884b9da95465c485c947229ed2182af156daac6e420c3c0caf98831241d0d77aef517ccf448d6e6344037a661c5592c3ff44827e46e53c894595c308bb1992e7c59f59b3703d37997b7704d99a30892abdad9c72916c27aef948637b850791a20b796c3f9b3239a27ac5b6da5d5d85b07daa0eac8059c435b420fec0f09fc9c2f627e0b23b1ae6cfe6c5395b", 0x95}, {&(0x7f00000003c0)="6b1c84b3c493a975185f2f339718630db2e982fd41ed2db48fc5e8431989a368f9a98bfeb004", 0x26}, {&(0x7f00000007c0)="4ea3063881bfa5665c1ab535d1955aaefed07a4f739df79f1c90036e7c18176f3a5d4855688598f4d0267842800ac93201c2c6cd370ce2289656ba94b32f25aeaf902e33342ba479e0efd145e1b6c82cb7ae363d62fe150a", 0x58}], 0x8, &(0x7f0000000900)=[{0xd0, 0xffff, 0x70, "40cad83bf74a7d21ab2313cd3eb100dae42e41148f93922d923915dd507eb94e1e85e4fc9a2399bc06072c1187be22a1638bfab6bc036a477a5eb842537140874a19d97024e924e87578e3d6c6eb590adf0fe61281941ccd385765cb7ee69186db1f77afef52af2856d0ca53c7dfa7a681e79fd22df81f3fa35155175c062f70c1c5885edf15a06c9f8b77df84cc5a4434771c569e49a4d922c514fd0aba174e1cf416709143e3041fc6562030bdd6617eaab6165d2877ea78c075"}, {0x98, 0xffff, 0x6, "3781218447212e7996d799aaa4af03d4d55e53bfdcba57bc9784dbd236e0fb2ff600ecf7fc6c91019ce487646169490b0988562471868a663b9d562a19015e2b7115abb4357513253ac2ee452a52a1f63fb0c40e30994378fe808cd0743326913bcb54e2d4632a467783860d51c666b3105ec5082bedf32eb257d8bde6512c08ae9da20e2a95"}, {0xd8, 0xffff, 0x1, "e41609a3c2ec60f04b4257a079d82faba54c6ea0898c66524a9d7ad88c370263d001cb1eb1d2a542053d955f2ab8c9e5f5c6654acafd9e577934fccaaab618deeb3d51447efdeaefad1c5a8ce3a687e9c73bca21bb215f9120a48f477302095731e6628e6cd20d79ad0e2d4e905d3a6d68a6c7a584abdf0d1d5599a69045dde77e9efc57000ef086977a1efe7771cdfd6f50e029d4d4aff3162f7f3563b58ef5bc1e74af120c6516892627368017ebb0ed7a2c1a3e96614b05ba48349357168e8ca3"}, {0xa0, 0x1, 0x5, "03f454bb88d29307068d0bf3293aae6d0b803a9ff82573e4d6f94d85248f44963bf3da370000766a15dad66aa5fc975107eddddeb79ebe282c6d3a1ea0ed7948d5c030fdbf7f39a25fc9f38c01d3007a41dd6429626250d9e703eb1e959e0fbb5fd651dcd4f5ebf006264b8818cc28ef6909c7b0f8765b811703550323f81b8d5086d68f8edb8f006dd5ceb54275fb02"}, {0x70, 0xffff, 0x1, "72b4a87cabbfb835c791b610fc03fa0c5d855832db8893c5d457c306f06b7fa670f40a2710b0f5ac3af3580fe2df5b1fd547a7c837f460f51c8314cd7958134d95254ec68b1d3109fdf25a0a9d1808fcf38b00be2de109f5d6077ae1f9785a"}, {0x88, 0x1, 0x5, "9ad5add28052cd76ad9355f83565624714984329509e8a69dd290935de640b6d329b64b67480ba81b1e68dd94f9fc44afea9878bdca6b3300084a75ac3c21c9891142db2e462ef5c8048f38f6f8786843e2e58c2b2a1e056c40035fa92b60d3146fec68d97506b8af96f5b04c2cbbe9b5a08c1e919a25dff"}], 0x3d8}, 0x4)
pwrite(r2, &(0x7f0000000240)="b94c6c25906fed8d1a0200000240", 0xe, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000180)=ANY=[@ANYBLOB="8a022ecf97"], 0x10)
r1 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
connect$unix(r0, &(0x7f0000000140)=ANY=[], 0xa)
shutdown(r1, 0x0)
r0 = shmget(0x2, 0x2000, 0x0, &(0x7f0000ffc000/0x2000)=nil)
shmctl$IPC_STAT(r0, 0x2, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x40, &(0x7f0000000180), 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3, 0x0, 0xffffffffffffffff})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070003000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2000, 0x0, 0xfffffffffffffdee)
recvmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0, &(0x7f0000002480)={0x0, 0x8000000000000000})
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x40}, {0x6, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@local, @random="2e8dd386ef21", [], {@ipv4={0x800, {{0x5, 0x4, 0x2, 0x0, 0x1c, 0x0, 0x7ff, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}}, @udp={{0x2, 0x0, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x64}, {0xc}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
setuid(0xffffffffffffffff)
r0 = semget$private(0x0, 0x3, 0x40)
r1 = semget$private(0x0, 0x4, 0x1b2)
semctl$GETALL(r1, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000280)=0x80008)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
semctl$SETVAL(r0, 0x4, 0x8, &(0x7f00000000c0)=0x2)
setregid(0x0, r2)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000000)={{0x5, 0x0, 0x0, 0x0, r2, 0x180, 0x9}, 0x495f, 0xdd0})
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000000)={{0x5, 0x0, 0x0, 0x0, r3, 0x180, 0xff93}, 0x495f, 0xdd0})
semget$private(0x0, 0x4, 0x1b2)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000001c0)={{0xa235, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, r3, 0x27, 0x8000}, 0xffffffff80000001, 0x3, 0x4})
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000040)={{0x2, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, r2, 0x10, 0x1}, 0x200, 0xfffffffffffff4d2, 0x4})
socket(0x18, 0x5, 0x56)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x1, 0x0)
setsockopt(r4, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r4, 0x29, 0x80000000000000d, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x0, 0x0, 0xff}, {0xb}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000340)=ANY=[@ANYBLOB="6f18"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x9441d8ae19a18991, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x4c}, {0x6c}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000900)={&(0x7f0000000080)={0x0, 0x0, 0x0, 0xfffffffffffffce6, 0x0, 0xffffff2d}, 0xfe}, 0xff8c, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="200000000000008d742c007b80309905f87c8a5b6701000000000000000000c414000000290000003d", @ANYRES64=0x0, @ANYRESDEC=0x0], 0x7f}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
mknod(&(0x7f0000000340)='./bus\x00', 0x3a0914c44f7b202d, 0x501)
open(&(0x7f0000000380)='./bus\x00', 0x200, 0x81)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x10005, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000200)=0x8)
open(&(0x7f0000000040)='./bus\x00', 0x10005, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000300), 0x0, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$FIOASYNC(r2, 0xc4504445, &(0x7f0000000240))
writev(r2, &(0x7f0000001780)=[{&(0x7f00000003c0)="a9ec9876f377b7f066ac0512b6c101694419d004d981d897801158b3f8035386ab7e676079c935ab1524219aefe6b5b2be69ded7c062c3730d1ff47f4a74685977f32387a82eb167077b46b0", 0x4c}, {&(0x7f0000000440)="e5b774746547d64e", 0x8}, {&(0x7f0000000480)="41d9c867310181d2ad53aa4779fba418d6b9c653fec55b2930dba51b7af9ea25a7698fde864e60151b7f75dc1d7eb6a431288e1bcf5e14ff14dff0d7017c8e1e312c43cf2773f649a046e6a7b4e3ce19a0b3c76728c52442ca800ca6e1a57c8e10dc82662579ad9e47339e29470de60c407c559af78030dabce76c5980ce2268b2a3cdebbbfa0d6a1d0edefa5a3f98751485bcad5c339b79f12db39c7e2be4ac27e93771fe30b92358e0961c3d795270d8da", 0xb2}, {&(0x7f0000000540)="c59fc791e3285b4769a23ffe998382a5cc3d806cff2d52b5a569bf1e5f26bf0e54bf14de15449cfb5c7476039ccfccdc7c575eff29093792ef5e3845f156b0144b29498d4a918f2eabed9d2b26b6487db612ee016080dce5f5c4e0ab444a04499590c8448a60942f787fd443a8347f0386f98ecd0bb4032b32e1888e53f6cd8af6e65d1d73b57ce2703d7b49e8018b6307c3acb9fc28f06dd4cffaafb2bb666cc49ac4cda28de745cf4f6edef2a6392f5e9ada4d491097b1811a60dc1d8eeb62d3af663d844c76384fa47f7016aa2efdf05eac5936f7098b264c8b50494ccaa6f8ff5bafb541e264d55caf9df77eb093114b5568825f221acc64bea6450d738c7f761d965a5068e29ff2b91e7f1601117a30efc3e7df45db36fa162e0509cf7b0620f9f89c85885584f300e0fe1188a44900372bdfa215bc39cf052c18101a0c063e512adc29aa302fe26d22a5cc3afcbd38c2bf81c2497f9558f3b0e1e8b8b2c2d0e3f2dae1587466061c93a3489d8af413f729bd54953f459ca16f338fe932c2efd8668d6e78763951d608d9c85f2601ea0ae4c6a167e3e9ec842b125f09701b7c381101d2770db58611d3771aed68ee97471878ea07e2e9e82627a445a4ff8b88cab5f3658b909ca7c1c96cfb1439c2e9cbe6b36069f4eef3d5a7fe317a8c11f0728b490ef98519457eb79318733e9d429fa2955cf5c633effd62655fa776b85416d6742941c7965a759a29ac33ec3a1dbd5135839876c8ac3335741a0d814254edd72f416013fe9eacb9fcf0c5ca88703d07d696e0c8edfa08859dac6ebb40c3f165df7c2041a0e5f6ee638a0f4bc6299732c1f96724839fba476e611fff9c85c9a5e7dc0ab322d4568c33648e85ef423ce2202c97fddd25247bba6256f24a09f10c6d5950f2b4bba03e3f006c668920ee0270c50167d57297c75f89dcab504b77557556cf12d18717abe25bddbf83097d9891ddb86dd19631561d7c6383f8b6dbd2780c197f62be2ccf5c24492af91a55d66af691bd72243515c03a860a1f1f063e9b3042da32693dbea989958250bda96fb4397bcd5514f43173fa8538d86110422f7ca2c415a2cdef089f8f051192a260e60bdac2e2f6379229140f079ba15a419dabbbe71a481c49e0a217fee0eead24ce38cc876089eff506c797bec9e420f248b9fbb5249c9700044d407f07952c74c40197eda55f8c3cfcd6168ec1e3c0f18229db06163cbe3a5d707399308c48b0852cb840ad38025c9602df94a5e83bd868a9821bf1de06238d4306a0f28134d1af821589c1b9e7b94e5a0c73a5db4bcb2c1af5304942953c9499869ae3120af817bed6646ac437f9939b0e9f8f66fb587aea511f73ed2ad49ebb065796efdf98260e4b5f1c042d5f5e45570b1ad180e245e36dcb0f482d0fde206088f52a04cde003132cc6b795501db2fab0fe88ceab422c46315466e86b4b616373cf1f79882c365f8bac3cc97235ad9e1a8ddb4896c5729c7669a4c0a48e3e4ed1f066870ea106a0aadbf0f01b7b5a2f28ee5379f632c8bee6ccde41830e790534006886b54f3d7f2ea521fffd5fabc3f8d41d6ba19d06c92d481bfab5e82fc351ab7e35f5751aa0a4b3497232546af1b5ee1d6378f1e53fb7de59f2ff4d5bcc6805e3f0116f48716f15f964a3cffbff4764e4e6144dcfc4b66eb7a74b435da5d39c3c0d0cc5933f78d0048a039eb399221fadb7f2d52272f839c1516b5d2120fee2cc491c8512e82f449769e437f5ad516088fc4cf27600ff1bbd426d5fcbace02c09d3f1a7464a433ea6108c98bacbb0d36c7f7a23b015e5b4dd6277acfb727eb97c216088fa9917c785bc05e7aee45b29aaf2f19446b6c24ad80b26977fd89734e2b2b38d56300e9b9fba80de14496ef1f7a5233ee4e0d21318ae99a510dc80e698c22d30ae9513fac010b47d93b2dd9610aab632f10295d07311089ceeb1d48a767e52ac78c9ac2480ad23caf7375ff206bfd2a13102baedcf9352d46c68d2a49c4232b1be6ca040ce4ff8c6e519954c25d538ca6afe960cadf969f10f7b07dc9059ab16e4bd6915faf4a4215aeb66b65c2e9036a09a55b58e3ee773114dcaae685e3d1f8d621f313c1ee76d3db6c6e71da4b1c5072dd90e4abbf6283e95a0d29559abcf08ffc1b9027691c76c3e51bb78d4ff0c9d779486d3e3a4261e55aec2dd649739f9157fa9ecd6f613ba9a02e4b5d49c4ada190c05f15a5bc1ee067b4b094319ffe0a5d3c678386a6cee8af269b5c5f896fb223f56c8ae98a03f9c3e98f6c485265211a789a869cc4ca0c1db08b543055a77dfe97ae22b5b16e00681fd1098d6ed927dafd19ae546b53a093fb0d20c43c449ee3351e5243c6119c927c57a1461d941dbdeabddeb0fad039557251d9374fa6fa4397e81515d72cb1a19fed3da25764a98a3b70baa5914b5084f254731f6b1e01205840fb0ddd4e5ab97e4eb52a3abd6454d9f27c0935165c73be811e150235f444179243b6b423aa6bba9d72f14f9377643d373ee8af22785f985b9a9a67b94be6e79614eb9ee6c40db8ec98599934b15479810d78157bf41755c7ad039b9abf24e41fa57341834b97dce2ed259ae330a5ea46ed691506f3f67034856358fffdfc09009e5983618cfb7fb3da66d3ee13e60e6675547536bf070aa1870e8353b37c584ec2015c7be1e7b572a15cc00f38fa66f36e8130afb3c2f2cef022d1ca354c788ddc07dc58dd23e226385999af5578ad20b650351aa87bb1f6b9d5245b914eff75977912089837befe84bc4228f829ff98f36cef124a8b12aa8890d1778df6916c9144647c68992c6a39ade6e7b8c378632337bd40b449e9ea4bef3d4ad91321d337a95ffba386bd82a43920e90ff850fc91c01515bd0f2e07f10fa7ef8cd83f0ccdcdf9a6e2e618ade65c08b38fed4686d056f13dad2f737bc59bb3a2ea1cbd6d7f84a92a589efddbab8efc61cca248a5fe1c472cefaa09b73df9760db0c18a95f65fecfe51012fc5cf3253d8fc198c3079aac05756a8a2fd03db20ab675c533c90b7b66a81ab34e0eafa9e24abbe0b15be333d062c103302bc660a71a009498c992aa01117688386f08020dc1771a3fc0482c6b50803bf4e75b1c94bc34ae5381a40643eee820b1e4f0c3a556609d976026c331e1648c3033eda63b0179fc8124eb4daff7a6a5c1c37f769f9428b103fc10ca8051053a65d3e65f285daede1cab6f94949e320919b2059734b08b3c0f0942279995115d896b9e3d01b2c97e0760268fcd6b75cec6baa48644179b858f9a25f194a2be53a9bcd417a09e3e83e3194ad954bf72b7e28a5f0492807c40f3966b49b059e07101be0adb4e0b5d4448c5cb24ff1889a377cb87d9ced71fdf8b97ea582655df95cca6036fbafcfe3afa11c088184550f853b46ad1fe20fd0ed60a4a633ba71d772e1e486954d498fddb0890fb6789e08f3102857493b178a4036e5ae219407879d08fa49802ed82082325597f453a087c789a559afdd31c1b15a8c8f772335e923821cc26ffdc1ce58ab876ed824f5cbbb74600ed99caa8e9336a37f548ec27d295c4f19c1af35cb08fc479d657528f289eeb6c815480e881e982aec03f1c595275b6054b51489f8396879a62b11c100145d52fc0cd8451c20d3e2f18b4761ca0774fc8bbff2480b24ea3e16db71e3584f6449b43922cd6c4b516941fe4985d105ba17d34b223b29a746d2b6b04e8c4b89b4451196c4d906d0d65c71c80cf0b692388c506d19c5b0fac687685c655b76a7331668fa8b8d066a8ff80d363eb3c88b4ad70a5009febd3e6ce55dd08c596fb31e60d0e851ff9235cbdcd02c9622a7b03119ae650a0ca2104696d2ac7295375d6c3b4e228c386450fbed15ef4afc5374cb64d6050739950e421242fa08de7aca1e8cbae71bbbdcd416dec465043528de77da6a707995f41b2c5143907be7460419dc6fe5e920e9c71d8f1e527d88e8310f907013d892e3b028e89f2ae273c0b36657aaaa2b2e637e3bc4acb4316a94135337b6ac24781d3e7f88bdf0f5b1cde63f7c6675027d584ae5f6d62bebd589a57dda3c3a1410af26f25ab28bd17a276c36138b2e53e60c2a3b695c50016464d243a8253cb35cc97ce34f59588f59ed124c70e091d6d21eaaad2590a2785a861264848072de63bb47ff2d9d593d6a21f1214b102257ce0de6a51180636e93797ba3cc25c1ae36341e928e2cc1a5206a2468beb69bd6f7f56b8eec2937fa7be1f1cd9336fde26fd8056d047eaa444a819ecb7874b804e816f5b2f8460d6be0415cbe9511f830ab899d5d16854d030d384906df23a59803e63af0da72e7a7f3844efe636c8ece09a32c1ca57361f2f54071e659404d55aa444e4cb85fb260cba752d028c7a87be9771ef59aacc1ba0d26188362014356a30b53e1feed4cb372db1a25493e05aacd765482835c5b54ee5f686c45ef74389c2f4751ee5ea451769182dc8ad89fb84c1f7493e1847e210178b2353fa50d71df7d9d5fd4ca90ba4951521ec830e2e9e1f6f8e8c4011285d099fa5c18cdb3304abf1ca1412be6ef0e6fec090496326d817544c96bd1ceffba1d3dc0b552eaaab5170a7b145629c25bb292a0c91bc05187f2cbe2f7489e9371132508369db27ae8da680d7a4aad6e1cc5dc65c5c87ea6c22683db00c8c256db936fbd9f8d73e7c2d1556a3d331b6fbacd6952b5343da23a243cd1d376f58cdf50e08a4c85e8c7225aaf092294be6e566a0bd6cc5e5776eaa896508fa26890db95b5df8b1fd35458bd8dedca0fb106e2eb9de2edd28ae95abe37a5258f5933f976f4f6853f1726948493d3a0d5c9d43d2f5f08dc69c8072c4c582fe1194128122a07ea7734f3ce2bca4b4940a5c5d8b524dbf9ebebd9859f1dfd261dd5b042782f90ce753d4350423cecffceeffce81f8617d48842d1c3ef71a16763dcfd51167a960f9432fa1879abb0ae25bbd323549c50d5f5e216376421fa49c46cd573af7e0f13916af2a27032aff18399b0e9198377b998a97c162620d9959ad37807900df72775f8204cb4b8cba56b743755d057373be3512bba1bd163d396fee84bd9df362809510b35a795a328fb1b71716ed11e83b13a6848af27b7a377dcd908efe535745b84633f9dd05b8ea68d1e48d238445af173407edde61aa4731712be3f2be26d16937a9ecf95bf02281d275cb83865585313fd20d9b091395702d8cf14bf15b84929bf624b6a89108fa37a1cdf64fa20a21fb0ed353acfe5e6025fd2fecda83e0b2f327c90e2ffec035a89232d9254ea0c7ae800d696cf604c1a34eb3777e26dc3fc8b7de336c39fc6182e73f2995720d823eb3627ee4f923293dce4ac16c1908b0363f5b4f181df0465272ed5018cc7547444e4a9791b63f2962f918313df90775bd9b6c532d21ab171b286777c16fbbdb865a1b1f98237772a63e1730e27fc9ecdfbd0c1265c8829b0c143fe6c7fff43d9a293386388aa6199aef28493d9452163a55123072531ee253af5cecc4b0b4cca3b355621bb6c4778e3e9ec41f5d3d8756b0350335047d79e86cd8ba88e97a15435a4b2fc6792892d9a06ada8b2b1de4bdb2f7b78b9c3ce05612f392085e20690a09a1ae1cb18b2e83455a295447530857409aac32889f5970932be9cb0961096189d3ac898256b5c8e82aa1288ea2923a17c956a7a604615fe720a9680bec78c03a7ca4b66b4a90740409dfa73de65dbe206f073278037f87c92097800ad7c5488f313a217afa4c4a7af4e75dff20497d487118668da53ef7e3f17aae", 0x1000}, {&(0x7f0000001540)="91fc92876aa45e7bed2d9011d8bab2ea7c0ef5d62a7a7e34a5db87dd3e41ddeb4f56002bb6010e50f3470b1fd70115a368c22d5d86", 0x35}, {&(0x7f0000001580)="272c8760c4d71ee9d89db81fc9cc005663ccaf3b5e3c0f36d06efee21c716e07edec431137dd925042f86e4c406e746bfda50acb1c097e8ac89dd4a661562adfa9f0", 0x42}, {&(0x7f0000001600)="c73ec46947a57332c31a0a70e51146ea4d16679d03e7a766071a5c131168de6e035f880f441efb567ab910855486e9109d7298b24e822f9cab03282a946cb1f6aebfe26447b3d36a3c1631ed51321204ed9d876fe5f12535be66823141fa5982b25fe16b92741c0591d9297db6b6fb1b60bdcbed119e6038e2afa3df7c531a36fdc7ceca8bb1de0f6b4f488cab5b6fa08ac546ec7030ffc16605e0622c572583e53df19edf8dd9d04da4bc02ef9df3f647f2142aaba61949a74761c2e19723ec4c485a", 0xc3}, {&(0x7f0000001700)="be14c579253b405ec55acc4cdc8545f1e64074e31c7eca4471ab1a179690ec8d960593e41446051c259100c9720e4f0ad314c309817f3d8bd2cb114619b4fc240d30086fc5cfd239cb1015dd791a7b7818c651a56ebacdd1d6", 0x59}], 0x8)
r3 = socket$inet(0x2, 0x1, 0x8)
mmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x2, 0x4010, r3, 0x100000000)
ioctl$FIOASYNC(r1, 0xc4504445, &(0x7f0000000240))
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f0000000240), 0xc)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000140), &(0x7f0000000180)=0xc)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x3}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000080)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "fb00", 0x10, 0x0, 0x0, @empty, @rand_addr="00000000000000000000899be21400dd", {[], @icmpv6=@ni}}}}})
setreuid(0xee00, 0x0)
syz_open_pts()
r0 = semget$private(0x0, 0x4, 0x1da)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000340)={{0x1000, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x1, 0x1}, 0x2, 0x6, 0x10001})
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000640)={{0x28d, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x177}, 0x8, 0x6, 0x66f})
semop(r0, &(0x7f0000000080)=[{0x1, 0x0, 0x800}, {0x0, 0x5, 0x800}, {0x3, 0x8, 0x1000}, {0x1, 0x80, 0x2800}, {0x2, 0x1ac}, {0x3, 0x2, 0x800}], 0x6)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000340)={{0x1000, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1}, 0x2, 0x6, 0x10001})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000640)={{0x28d, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x177}, 0x8, 0x6, 0x66f})
semop(0x0, &(0x7f0000000080)=[{0x2, 0x0, 0x800}, {0x0, 0x5, 0x1800}, {0x3, 0x8, 0x1000}, {0x1, 0x84, 0x2800}, {0x2, 0x1ac}, {0x3, 0x2}], 0x6)
semop(0x0, &(0x7f0000000240)=[{0x3, 0xffff}, {0x1, 0x6, 0x1800}, {0x2, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x3, 0x517, 0x1000}, {0x2, 0xffff, 0x1000}, {0x1, 0xfff}, {0x3, 0x9, 0x800}, {0x3, 0xfe, 0x800}], 0x9)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000180)=[0x6d, 0x3f, 0xff])
semop(r0, &(0x7f0000000240)=[{0x4, 0xffff}, {0x0, 0x6, 0x1800}, {0x2, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x3, 0x517, 0x1000}, {0x2, 0xffff, 0x1000}, {0x1, 0xffb}, {0x3, 0x9, 0x800}, {0x3, 0xfe}], 0x9)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000080)={0x0, <r1=>0x0}, &(0x7f00000000c0)=0xc)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000300)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000080)=0xc)
setegid(r3)
setregid(0x0, r3)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000100)={{0x80000001, r1, r3, 0x0, 0xffffffffffffffff, 0x84, 0x2d}, 0x0, 0x7, 0x4})
r4 = open(&(0x7f0000000000)='./file0\x00', 0x2, 0x1e0)
ioctl$TIOCSETAF(r4, 0x802c7416, &(0x7f0000000040)={0x80000001, 0x0, 0x6fffffff, 0x100007, "ed6312c14e009a6377b53cb0916ecded946e5a03", 0x0, 0x8})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080), 0x0)
r0 = syz_open_pts()
syz_open_pts()
r1 = dup(r0)
syz_open_pts()
getdents(r1, 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x3080002000, 0x1604)
open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000010206, 0x0)
writev(r0, &(0x7f00000003c0)=[{&(0x7f0000000000)="bc", 0x1}], 0x1)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)=[{}, {}, {}, {{}, 0x0, 0x0, 0x0, 0x0, 0x2}], 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000100)={&(0x7f0000000080)=[{0x5}], 0x1})
r0 = open(&(0x7f0000000480)='./file0\x00', 0xba854469e4540ac6, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x24, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x1, &(0x7f0000000180)=[{0x0, 0x0, 0x9}]})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000180), 0x14)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "0400000021bf04aa1500000000006e00"})
pledge(&(0x7f0000000000)='tap', 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028698a, &(0x7f0000000000))
syz_emit_ethernet(0x400e, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @remote={0xac, 0x14, 0x0}, {[@rr={0x7, 0x3}]}}, @udp={{0x0, 0x3, 0x8}}}}}})
syz_emit_ethernet(0x12e, &(0x7f0000000000)=ANY=[@ANYBLOB="ffffffffffffaaaaaaaaaabb86dd675c135a00f8061f00000000000008000000000000000000fd"])
syz_emit_ethernet(0xfa, &(0x7f0000000280)=ANY=[@ANYBLOB="3b1bfddfc55effffffffffff88a8020081001e0086dd6039789a00bc0800fe8000000000000000000000000000bbff020000000000000000", @ANYRES16])
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc088444f, &(0x7f0000000240))
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050300000000000000000301000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5fc82b297ba1ab5b23116730f144000000000000001f1306000000000000007d026ba8af63ff37282918", 0x62, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xd0584fcd7f7abe72)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x20}, {0x54}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@broadcast, @empty, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @multicast1, @broadcast, @local={0xac, 0x14, 0x0}}}}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000040)=ANY=[], 0x7f}, 0x0)
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc008441e, &(0x7f0000000240))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, &(0x7f00000001c0)=0x3, 0x4)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x100000000})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x36, &(0x7f00000001c0)='\x00\x00\x00\x00', 0x4)
sendto(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
select(0x40, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x6ead}, 0x0, 0x0, 0xffffffffffffffff)
syz_emit_ethernet(0x2a, &(0x7f0000000000)=ANY=[@ANYBLOB="ffffffffff001d06000000000000ffffffffffff"])
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
msync(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x5)
setitimer(0x0, &(0x7f0000000000), 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x4, &(0x7f00000000c0), 0x8000021, 0x0)
r1 = open$dir(&(0x7f0000000140)='.\x00', 0x0, 0x0)
r2 = openat(r1, &(0x7f0000000100)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r2, &(0x7f0000000200)='./file0\x00')
unveil(&(0x7f0000000300)='./file0\x00', &(0x7f0000000240)='c\x00')
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x67, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x2}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket$unix(0x1, 0x5, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{}, {}, {0x6}]})
ioctl$TIOCFLUSH(r1, 0xc028698d, &(0x7f00000000c0))
sysctl$kern(&(0x7f0000000300)={0x1, 0x2}, 0x2, 0x0, 0x0, &(0x7f0000000440), 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x400000000000001, 0x0, 0xffffffffffffffff})
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000180)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1abda71601a8bfee8aca4911faff5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be608a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
socket$inet6(0x18, 0x8000, 0xff)
getppid()
sysctl$kern(&(0x7f000001a100)={0x1, 0x11}, 0x2, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0xba, 0x0, 0x0)
openat$pci(0xffffffffffffff9c, &(0x7f00000000c0), 0x10, 0x0)
r1 = semget$private(0x0, 0x3, 0x1da)
semctl$GETNCNT(r1, 0x33677a76b2de9ef5, 0x3, &(0x7f0000000180)=""/170)
flock(r0, 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x28}, {0x6406}]})
flock(0xffffffffffffffff, 0x9)
r2 = openat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', 0x0, 0x0)
getdents(r2, &(0x7f0000000100)=""/102386, 0x18ff2)
fcntl$lock(r2, 0x8, &(0x7f000001a140)={0x2, 0x0, 0x6, 0xffffffffffffffff})
socket(0x0, 0x2, 0x1)
getdents(r2, &(0x7f0000019100)=""/4080, 0xff0)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00000004", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x84}, {0x4}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}}, @udp={{0x0, 0x1, 0x8}}}}}})
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000040)=0x7, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffffffffffffc, 0x0, 0x0, 0x0, "6fc6e23c5b00000000000000e74de400"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffffffffffffc})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa)
r0 = syz_open_pts()
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300004000})
flock(r0, 0x1)
flock(r0, 0x1)
flock(r0, 0x1)
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x2000300000000})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={&(0x7f0000000080)={0x0, 0x0, &(0x7f0000000140)=[{&(0x7f0000001840)="62a60483291de3543852372e2922a6b3"}, {&(0x7f00000000c0)="61907bd982ae893131281fe8b98b5428d6195905e75d316e5a5484b02f1205531b407725dd989a6d9012bc9ef5e009a56819916521cad95d0c830a704154d5b616b7af01c209350ef9c5a56d519bae4704418f45695d4edc74ec4c8ee65c58b693c3af91fa5dc37fc4b538f41362ae5c5802", 0x72}], 0x103, 0x0}, 0x81}, 0x10, 0x404)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8f", 0x10e}], 0x1)
ioctl$FIOASYNC(r0, 0xc0104419, &(0x7f0000000240))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
seteuid(0xffffffffffffffff)
chflags(&(0x7f0000000140)='./file0/file0\x00', 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x20}], 0x1})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_linger(r0, 0xffff, 0x80, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x9, 0x0, 0x0, "81080f00000000000000000000001000"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x1, &(0x7f0000000100)=[{0x23c}]})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000300)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x1000000})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0xfffffc8a}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgsnd(0x0, &(0x7f0000000200)=ANY=[@ANYBLOB="00000000000000004b3e4adaed60f9694f579a48a898b3e0a244a62aff3ff370299a40dd008d33ef2b68889815e0f3d64ea11849789c849d6b72cbe90087da24c19071a704c5a74c3e906de00234767800b15445d036d9eb7768ecc61e4001f2cea489a1672715d300f44b849a450c57ae4e7a21e973bba27a079143b4db6b"], 0xa7, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000400), 0x0, 0x0)
mmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
mkdirat(0xffffffffffffffff, &(0x7f00000000c0)='./file0/file0/fi\x00', 0x100)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x3})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1e}, 0x5, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x5, &(0x7f0000000080)=[{0x1, 0x1, 0x1, 0x7ff}, {0x82, 0xff, 0x81, 0x8}, {0x6, 0x25, 0x6, 0x7}, {0x0, 0x7b, 0x3, 0x6}, {0x3, 0x1, 0x43, 0x40}]})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0x80009)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
socket$inet(0x2, 0x4000, 0x99)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r2, r1)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000200), &(0x7f00000001c0)=0xc)
getuid()
pwritev(r0, &(0x7f0000000340)=[{&(0x7f0000000240)="3742d4cd92ea58d2eb76b1120080468f298528ac9e9beb1f03ceec207cfddb74efc2150461883a544e04ac6ac9558641efa95d0a84e75ae3c734d3bf044a43baa1953056abf56318807ae92376f26279bd81e2", 0x53}, {&(0x7f00000002c0)="095718f58c0a69716bce87c580b25c7f7f80fb4253c9b782fcfeffc35d2007394cba4640b752b986075dbb57da", 0x2d}, {&(0x7f0000000300)="bc471f0dfb50a7642d", 0x9}], 0x3, 0x1)
setreuid(0xffffffffffffffff, 0xffffffffffffffff)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000180)=0x6)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x80007, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r0 = socket(0x400000000018, 0x1, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x8}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000380)={0x3f, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0x11c)
mquery(&(0x7f0000ffe000/0x1000)=nil, 0xe4246aaa20a, 0x0, 0x0, 0xffffffffffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206980, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x40000001}, {0x1c}, {0x46}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = getpid()
openat$pf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r2)
r3 = fcntl$getown(r1, 0x5)
setreuid(0xee00, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x8, r3)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="70020000e0"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f00000000c0)=0x5, 0x4)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f0000000140)='\x00', 0xffffff99, 0x1, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206913, &(0x7f0000000300))
r0 = kqueue()
r1 = dup2(r0, r0)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f0000000080)={&(0x7f0000000000)='./file0\x00'})
kevent(r1, &(0x7f0000000000), 0xffe, &(0x7f0000000080), 0xfffffffb, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xb, &(0x7f0000000040), 0x0)
syz_emit_ethernet(0x32, &(0x7f0000000040)={@broadcast, @remote, [], {@arp={0x806, @generic={0x1, 0x800, 0x6, 0x0, 0x0, @broadcast, "", @random="38c7b2b47718", "196106ca33562ab1446bcd7e27dd9fe5"}}}})
socket$inet(0x2, 0x3, 0x103)
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
syz_open_pts()
fsync(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000100)=[{0x7}, {0x54}, {0x6, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
setsockopt$sock_linger(r1, 0xffff, 0x80, &(0x7f0000000040)={0x9, 0x81}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02", 0x35}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5b52f60f48f6c", 0x95}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000200)="a5", 0x1}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000006c0), 0x0, 0x0)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000540), 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000005c0)={&(0x7f0000000580)='./file0\x00', r0})
open(0x0, 0x0, 0x0)
r2 = socket(0x2, 0x3, 0x0)
execve(0x0, 0x0, 0x0)
dup2(r2, r0)
acct(&(0x7f0000000000)='./file0\x00')
r0 = socket(0x18, 0x1, 0x0)
sendto(0xffffffffffffffff, &(0x7f0000000080)="8ad0027876db76534e04507dbcab83aed651e94077d26c13f36636841e74d9b39e3194c679a6fafa13e80c68e1c51de665e03503eb566822822b1d60b555c1594dce1f88f7d135830bc8267ef1fe3cd0c3c24821e8e78b82304780915e93f54cbd508051bd0ce7f035759a209630b22ff8ba83c84774752cdf3f9256121613917c6999f5aa909a13cc209a8195203590bc9181521d5444335b00c4598995d0aa91c66a34801dae20a287bcd175bd8aa48dad59197fd948b931f9eb4beadcaeb5ff32", 0xc2, 0x0, 0x0, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000140), 0x14)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x41, 0x0, 0x0)
ioctl$WSDISPLAYIO_DELFONT(0xffffffffffffffff, 0x8058574f, &(0x7f00000000c0)={'./bus\x00', 0x0, 0x0, 0x281})
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0xfffffffffffffffe]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
setsockopt$sock_int(r3, 0xffff, 0x1003, &(0x7f0000000000)=0x6, 0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r5=>0xffffffffffffffff})
getsockopt$sock_cred(r5, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
mquery(&(0x7f000065d000/0x800000)=nil, 0x800000, 0x2, 0x10, r5, 0xfffffffffffffff8)
fcntl$dupfd(r1, 0x0, r5)
ioctl$FIOASYNC(r3, 0x8004667d, &(0x7f0000000040)=0x4)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2", 0xc}], 0x1, 0x0)
poll(&(0x7f0000000100)=[{}], 0x1, 0xa1)
msync(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x5)
recvmsg(0xffffffffffffffff, &(0x7f0000003540)={0x0, 0x0, 0x0, 0x62, &(0x7f0000000000)=""/125, 0x7d}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r1=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r1)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
setreuid(0xee00, 0x0)
r2 = getuid()
seteuid(r2)
connect$unix(r0, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socket(0x2, 0x1, 0x0)
pipe(&(0x7f0000001ac0))
r0 = kqueue()
socket$unix(0x1, 0x5, 0x0)
dup2(r0, 0xffffffffffffffff)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
fchown(0xffffffffffffffff, 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCLOCK(r1, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCGDLTLIST(r1, 0xc010427b, &(0x7f0000000140)={0x0, 0x0})
sysctl$kern(&(0x7f0000000140)={0x1, 0xa}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000001c0)={&(0x7f00000000c0)=[{}, {0x8, 0xffffdd2f}], 0x2})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x100000000})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
write(r1, &(0x7f0000000440)="cee7dd3135010000", 0x8)
seteuid(0xffffffffffffffff)
syz_open_pts()
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x20, 0x0)
setsockopt(r1, 0x3, 0x5, &(0x7f0000000100)="0924684835255fc6d8a164ec83ba685395ec91c27ada4850776d3b834f341498c18226f356bc243a92d870fd59405e67e578eb2fcf54c1f9b95612f0732af402e9327e15044c8952662135ddd235bcab230dfdef6b1699cd85b35058066b8061b9bd7458dbae5ef9473e8009c191907e6a8c74b74b65e0b3446701b8142415e4bf6f6dcd2addb83aa03cbb55d63c21f43136c1cf467c2ed5e985c6f41c7965de3519c4bb5a2bd19a8362b101d7195b6977a20cb2cd222e472140f5e7d6f18948fb31b2f568a896d51225fd644cd57effc4fe5e1ffbd68561046c6a6aabaea0b8b044a669b7", 0xe5)
r2 = dup(r0)
r3 = socket(0x2, 0x1, 0x0)
r4 = dup(r3)
r5 = syz_open_pts()
fcntl$dupfd(r5, 0x14, r2)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={<r6=>0xffffffffffffffff})
setsockopt(r2, 0x590, 0x7, &(0x7f0000000580)="3888ca3c398f73358b5f6696679255be84137f53e77114bc1991751d26f8c6cecead057caa61cf356334838c1bb4a5bc99c410adfe82f0b7710ff128970e0d43dc5f7bd7e2b4caeb14e282eef3d1fa6979ab319ff101870fbef0ac55b7b7a3839ee48d5d154333e57cd1b81eac43704755d6245b0abef0f05ef7a2e2ae1c2616ae61ddae9a13852e535f6327587f84676936712b1abc86fc2e14b64b0fc2b78416f4f860289aa52ca416f6ecc368e608e2fe3446a0d0dd46bb904e15bddc30170258205e16b878671111e9c5ec56b4537bf1f5715835ea33265b69d240a471c212210d9c9ddedb424454ec758bec87149f8ee58c10dc", 0xf6)
getsockopt$sock_cred(r6, 0xffff, 0x1022, 0xffffffffffffffff, &(0x7f0000000000)=0x4)
setsockopt(r6, 0x4, 0x46ba, &(0x7f0000000040)="816bc03a064487dd30118388efd82bfe77b8247985fd2606f83735c97b5aa6778a9de9c53bcb6c3de9faf9f29059b2136f549a5ce9035eb084ee5deb7494f582a2bf4dc9e2e3210c7f44b80fc359", 0x4e)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r7 = socket(0x18, 0x2, 0x0)
r8 = fcntl$dupfd(r7, 0x0, r2)
ioctl$TIOCFLUSH(r8, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x30}, {0x24, 0x0, 0x0, 0xfffffffe}, {0x16}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
socket$inet6(0x1e, 0x3, 0x0)
sysctl$kern(&(0x7f0000001180)={0x1, 0x4d}, 0x2, &(0x7f00000011c0)="61b8e55b", &(0x7f0000001280)=0x4, &(0x7f00000012c0)="6cc72a95", 0x4)
sysctl$kern(&(0x7f0000000140)={0x1, 0x38}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$vfs_ffs(&(0x7f00000001c0)={0x4, 0x1, 0x7}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = getpid()
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, r0})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x7, &(0x7f0000000040), 0x0, 0x0, 0x0)
sysctl$net_inet_divert(&(0x7f0000000000)={0x4, 0x18, 0x102, 0x3}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x4d}, {0x7c}, {0x8006}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0xfff, 0xf52, 0x9, "080404000c391200"})
writev(r0, &(0x7f0000000080)=[{&(0x7f00000004c0)="aa55e54742f735ce44c0f5842dfbd8bc57df79aec21a8b0057c8d8d857c0fe5202db277e0bc2692e2dd1ea5eca833249e8969afdd0175266853991c7f1281a97ff9ef63efcc31ba0c3e1bf9a5d4454ae9dc8c981e3a553e274414d7d700caa2e5697128a2f848bfdf55ba7adf3f404ab2ffd3a5153f569e15de36052d0a409343983eb03efa6948bab448962382aede76022eca2ffda2e2a65568004", 0x9c}], 0x1)
syz_emit_ethernet(0x2e, &(0x7f0000000100)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @multicast2, {[@ssrr={0x89, 0x3}]}}, @udp={{0x2, 0x3, 0x8}}}}}})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
dup(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000300)=[{0x4c}, {0x7}, {0x6, 0x0, 0x0, 0x80007ffe}]})
pwrite(r0, &(0x7f0000000040)="f94c4c4836eededf016fed8d1a02", 0xe, 0x0)
r0 = syz_open_pts()
poll(&(0x7f0000000100)=[{r0, 0x4}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x1, &(0x7f0000000000)=[{0x3}]})
socket(0x18, 0x4000, 0x20)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000002c0)=[{0x2}, {0x80}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000001c0)={&(0x7f0000000100)='./file0\x00', 0x1f, 0x0})
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f00005a1000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f000055f000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000fee000/0x12000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f0000558000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff0000/0x1000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000ff6000/0xa000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./file0\x00', './bus/\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus\x00', './bus\x00', './file']})
r1 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
renameat(r1, &(0x7f00000001c0)='.\x00', 0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00')
r2 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
renameat(r2, &(0x7f00000001c0)='.\x00', 0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00')
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3}, {0x40}, {0x6, 0x0, 0x0, 0x20000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
write(r0, &(0x7f0000000240)="34b438007037cbf413ff18f86789", 0xe)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206913, &(0x7f0000000300))
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1d}, 0x4, &(0x7f0000000100)='v', &(0x7f00000001c0)=0x1, &(0x7f0000000200), 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
ioctl$LIOCSFD(r0, 0x40047307, &(0x7f0000000000))
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
open(&(0x7f0000000480)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x802069af, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x4d}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000180)={@random="27aacea59068", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr}}}}})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f0000000080)="c820a0be6a55cb70e18c97b3f491e7e61a7959915dbc5b1e5b1d8d37c36e92018e0741eecda2cc2836719a03d07fdc7e20de3bbd6ca9c84e3a27525a43de6c7cb4376042fff051103a5cc0c1e4ca6a25de8c2a34535f315a6751acde661a42576ceafff24e2b482fdde0f61ce5ad3f3b51392b8de61b69cf0cb7903731188bb9c483164e531813b09884d8120a603bdda21a083b77e024ac7a709929e4888de5b2a03fe60f9d374661b8584e", 0xac}, {&(0x7f0000000000)='8', 0x1}], 0x2, 0x0)
execve(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0), 0x4)
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
r4 = accept$inet(r0, 0x0, 0x0)
accept$inet6(0xffffffffffffffff, 0x0, 0x0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0)
write(r3, &(0x7f0000000040)="04", 0xff9a)
sendto$inet(r4, &(0x7f00000004c0)="1daad5cd36195d6810b318270ef9a4e226c2d78a5413faf736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8ad376f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbaeff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e7068a0d0a3e839ee05e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e13faeebbccb92c28d329fb8635224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70dfd265fa833f7f7788b351b9a0abf03d9e21a38de65ed7352c75d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc3462aeff27e94fe6994ffe7086d8f0c631b96880096d82665ddc95c8ed5e187c85fc07bee3f65d4b91d59436540aac4eff6f5cea6e4233587318755e8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc780bd319a6235b800800000000000000f525897bfdd75d1f2d5a302b3c4eefe2f5ace97cade03c418d91b5edd3d278cedcdd1e7d4b95b3a63cd9079888381a65a8789264cd8048410f29befde650c4fa5dcb582718b324bf28706d04a602d395a0ff0aafed57c5dc7a2d17ef96202dc44790cc423511a5c2c44c1d6a2cffbad9e62418bc25d106ac22145de479fafe620614d07d935a3ae6cd295d9ccca947a6f483c71d2e04af4a8ffd6a534d183eb7d39dd34770f37290a93d68882c6a8d220e5feb6940b4ea75e0f94abe6c4bb9e905bc86656f854a277e6907c4478d74d2cf08a56c02d6c9586397ccbfbc41e3789060018a3c7047ea96afc10d610015f6861ce8e55f4bb8a90a29627ac549e3b", 0x2cb, 0x1, 0x0, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0x0, r0)
unveil(&(0x7f0000000040)='./file0/../file0\x00', &(0x7f0000000080)='W\x00')
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
fcntl$getown(r0, 0x5)
sysctl$kern(&(0x7f0000000000)={0x1, 0xa}, 0x2, &(0x7f0000000040)="1c", &(0x7f0000000100)=0x1, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000001440)={0x200, 0x0, 0x9, 0x100, "64eb04fb36c5c1132b0f32e4c46fffff013df7cd"})
writev(r1, &(0x7f00000013c0)=[{&(0x7f0000000100)="62405a8536b9578517006fd633621277b6cf7e462413b2e6457e216295a2064c9963fb397e169d844573d45e557948c6d9ccee19f84b725d5bd691d6c27edd7d3486bcd0ea40634a4e3e03f9faff4a41b6a695063c7e371cdd442efcff3be83abbb10c377fd6c7b85e31082b2578191e06bb5d9e256a94086307dd89d4013f01b627b98c2c3836a550f9aeec3555c4bf35574995fb1bf4b5dae0f691d29267f058eb721521e99c059d2f9fe0b18394ab63a2f537b3793fcd73093bbb3c17bf95dd4f4bcbc4f96ee1ab5b811efe4d3d85af17aba85b86a0ed844c89732a33d7c18c401038e7fa72dea7f92479b3d62024af8308235a56948c80c00f695933b51397431aa8d9846395be1b4a78d904849c2f968d739c99a1175e23ccfe8eb31672a598a6a728a7cd89146430fae0a6bf27a69a1da583a598ac76a0dd75498ac77ae00e938418aa32364034f572a3baed02b91ba0bf787ed8da0cc9a40c601ef71e4cb2d433d1a2b268550c6f6e4c459dcd2c85e84daa68a313c0af5dbe34e07cd2b4a73c4c13589e28c3ff97fa6e1a0f04ff9b895ad73a8c8699ff1f66b5a2b315bcf8c0ff8d982d0dbfd053189634e22c52d09472dcdadab1c737e54b3ac32f8b84d9dce04c0877e676b0000c057d87bff71c40a6074f6493eb959169e2aa61b45dca878f35d7cbd909644e9a0a7fb5dd97ffb236f0efb4e1f6ee1279b1ad714a9347766ed0a9307afd973edf9bb1d6759e2759fc841da4ef79bf8681705714758ada5530e65a3e519576f7249ecfbe103362a1fd7b2fbca220629e2c46b45a5e1291b2ccb1e395057e4acd74e5bc1cf15e56c27ddbc8cc3448031b7d629a5e4f4314a0d678d003f0da7ca0d86f7395aeedcaf8f9468388f8e6776f04115292363027be676348ede987950eb3377ca6569fa2c0359886b6f9ff55d55a784b98438bbb6c749b3a85dac49df81a66ffe46abb52fffd7dc4b40a51edcd52537f6e9edd5e071448a39339e2d0620ac454e23647e178014ae010daee5339d40d457e3ee7b1a96dde3ef4b58c9a81c6bfa76a960ca65f82aa298ca5ba00f5202d72700ec80a14d3208cf5521195cfc3fd4658d4ec887f29e71077a38339527af65b47b3c5eaf31df6f2ff4f4afade85ceadddc38d3582b1b68647c29e1eea225776904a51449a89f8ed0e649b45db520267d90ae11ab50c22456f16c5", 0x359}], 0x1)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000140), 0xbd18f2fe3d0dcdc, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
close(r0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000100)="90c3fe67eb586d98600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175aac66a72e20c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e6c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a77417d9aa33badde425628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000240)="257819cdffbb36", 0x7}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = socket(0x2, 0x400000001002, 0x0)
connect$unix(r0, &(0x7f0000000000)=@abs={0x0, 0x0, 0x0}, 0x10)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
getrusage(0x0, &(0x7f0000000080))
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "104ce467af60b403184700dd47c8e9d209670991"})
kevent(r0, &(0x7f0000000040), 0xffff, &(0x7f0000000140), 0x7fffffff, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{}, 0xfffffffffffffff9, 0x29}, {{}, 0xfffffffffffffff9}], 0x0, 0x0)
open(&(0x7f0000000180)='./file0/file0/fi/file0\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000140), 0x100, &(0x7f0000000300), 0x1ae, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000080)=[{0x61}, {0x87}, {0x6, 0x0, 0x0, 0x553f8734}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000200)={0x3, &(0x7f0000000040)=[{0x28}, {0x3d}, {0xffe}]})
syz_emit_ethernet(0xe, &(0x7f00000002c0)={@empty, @broadcast})
sysctl$kern(&(0x7f0000000280)={0x1, 0x54}, 0x3, &(0x7f00000002c0)="11b7f23b", &(0x7f0000000000)=0x2, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000340)=[{0x60}, {0x2}, {0x6, 0x0, 0x0, 0x1003}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
sysctl$net_inet6_ip6(0x0, 0x400000000000021c, &(0x7f0000000080)="4341230f373b14f615434811319190983f46c2d7f8d92d070ff4c1042666a7c62eecb5dffc504b8643d9b5ddd92388498115a9e0a065fe2e996d613e09cbc04d438ea2d17885fe9376f2312efc2f8166926f21c7c82861bb46548751c0d906343cafa06a9be93b6b9da61467e5ad7c105affc794ec1221f322ec828dec1a9231e4eeb7fb14dfbdd121586cd4e86f0be7dc041535f25b6e883f88452990ebbb0799899c2b98d91659b52ac887e61ceba44dc58732f1ad84ed60e37ec205762e574c768e477479577486141c18497c5bf5f244676b0f66f943276ae8588a82f7523b1a4bed12a4f55404de9202eeef982cc1f6cf14b8d96215dda441910fab658df9ecada31dfc6ce5ea0d9c09a7261009dee7484b317132a4714ba9e23eab53e00c23e22835e186b9d5d1f709a00fa3acd1927453e6291821c5b26d120ba7bc7cfbacf85003147d3094c8f3a5f955d51a91751c629ce9ffc687bdddccc6258bb671764bbd71d599a60556662051e72cd6908c93718f7a3b9ea5961ebfcb12794c8b65017de6a892d50751400667e34a3db59c6b0af84f301f67a21643fc7c141383407c3639dfe8ad89e25b2638a211ea242e52350d6a99e84adaf4cc3e7627d7a39751a7c9ff8dad7c0a930f50682d7f94e90a8d348b9925717e449621b1c83f34877b409ea16a1c814de61e5111d7b9ae0afdaeb3575bc56ceff606ea40b35f81abec4d7f04229d447d2055e4c99dfe7fb96b927a47015e8f6979c140d0f4f950f320ff6c8cde4fe4d2625f9a539b6f0b6fc90de488cdfe41604ede7974d96603cc8b3780f33c6c8a6ffb293f489c46a93f155599b9d20efe27b3138a3e3a820153a4b4e49367e7cccaff01dbfe609fac86b993cea3672a3e459c29f749c059dd9f815575b6e0e264d482d49fc762c4d3ce3eb75bc0cb1b1f8967e9e60849496c4f0acc49021bf06cab31b814750112a7f932ad65350f6a131c527ded01cb73a66a82da87cd44a01d87899537d9606242413d650eafad09bed60a5ba33d7a4f73b7d68d1a21a52b57556cfb923690851055e0e814861175035d52fb53446d4642ed9242d7434216cd06799231831ac9b936d5b5d566200d419242a887d57622ebdf9de95580be2fb6dbdf0709b54a4c6ed6073d18b6083944d716c9caa9b995b333dab2656f0bc5507c7b9acc8b18cfbb07ab1e3a6fe3d4994b66cb3df722ff5b87e457e6a864389bf71461b7a73aa667eb6141bd8bb978f1aab2474c0d6f92431ed263750d4fba0a4b687f522c2b47eb52f2687f8ed8a695d1e68d7b5c435e71cb1e42cbd8f160e3122e603fbb22dabe88b2a6b649c8a3ae4a77f8dea02b7289f2b7a483ffdb14c07a3ca46519d75bff3adc1254c26ce9363c4755a7b026ebc744be240da184b55bfedc723eee7b30ebdec1cedd4a400dce6d986878a64913c58d8b19c8fd349a585f848a9e652f927594239fad712c8df0c1d3b809585abe600ade9eb563149d18d5a19310b5550a0bf66dd174efa62faefa7101b8d1f57ed4e917a6de83d28a872bf568dba263689fb0e18d09c03477b5478365da7feb9a7aa8552b235d0c2752b9a14b94dbf5a1b9c6195e9ee807f4a115b95531aba18a9a87eacecd9cda9e4b53094c9ae87c7b010c2bb6eb420b901b5929e2f32c932e13778a735bbbfa621691fa26d618a2ed9d8720b8fc9de7ef879726b99e793e232e623c963a1e30956dad4b53f63e3fd8f943858648eb291a595da333ed2573b5fb818f52c934a5d0bdb810e61c0a74f9ba06fb19e4d9bca43ec3e8c37a8737e77ff6ab0bd0f83badb8ff26adde79b9f10df83bf14ed86747b7abdc718c69d8d5fdf86eaa6df2114a157a570a9169bb9dff9e6fec4e636dd273223045d18a04c8c2e18388f21a1bfd841af60437495606d3b4d22b2deff9d5e8d92749ac49e9a55632f3b9d14cd7017d2ed9b4e8f549a4d04f5e2a92446b4eb9956128200bfde18156b06fdf5c17bdcf811cc20b7bf70f61a2c40384d6d61f75621dd4f6547e30afdbbf9126f6085c0d96f3f66d0cfb4f36be95c4e33f9093ae7c5c381df917fe7481de", 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000740), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
unveil(&(0x7f0000000080)='./file0/file0\x00', &(0x7f0000000100)='x\x00')
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='x\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x64}, {0x81}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff6000/0x1000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ff4000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ff6000/0x1000)=nil, &(0x7f0000ff4000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffa000/0x1000)=nil}, {&(0x7f0000ff4000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}], ['./file1\x00', './file0\x00', './file0\x00', './file1\x00'], './file\x00', './file0\x00', './file\x00', ['./file', './file', './file', './file']})
r0 = open(&(0x7f0000000000)='./file0\x00', 0xc02, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
sysctl$net_inet_gre(&(0x7f0000000040)={0x4, 0x11}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
sysctl$net_inet_tcp(&(0x7f0000000140)={0x4, 0x2, 0x6, 0x16}, 0x4, &(0x7f0000000280)="b82cb26f5717e7429f0724c709823ce098d80a5b1aee84b681447fc724888f58043580a2a5f5916f622b8d090f60a2a2fece3ca805cf13f2fc653ff0677224db5afc10030e5a08083d5de99ca9aa06b95b3171ce71917e14bef18a9f10242a8337325f828e2bbd6959315f3ee20e59277c5e70c8ea0a57c391fe5a9f700b17ace625d5c3d3d78c2772a1ee260bc1221e53421344f7e12be15024db0d3e5d5104e8a290d9", &(0x7f0000000340)=0xa4, &(0x7f0000000640)="c5c2796f8ccd8983638b6ec542e343a4674fb72be7e9960962e2b3b4c3ddd90f121d0e23d1d91ea1e1f1375a45ee3c845ed27ebd9acea20c2ac3e40f1152dd7c9e4dd3b2b3ad97c60f839e5affefdf94eaa8bcb559a204cb4318dcdc2351e9581d3969357ca2d4f153ae85a47e1201b8ee61e80aa576db3c28dad16e27c0037305", 0x81)
r0 = msgget$private(0x0, 0x25)
msgsnd(r0, &(0x7f0000000500)=ANY=[@ANYBLOB="0200000000000000c5bd6690709e17e51e5cf856ce9c02695fcafa54da289d4bf132d1a9f8673ff140ec547a8da04ae5ea56e3cc8119b7b58050259d32fc3933b3dfa6b43dd9af5eca327f293b499c31c667d5009d603796959c0c6eaf0b8f351ec37a9da48bd3b2491b1378c79d8bd101e0e79ce38e6e26933846e823af313a2153fd995f6b2f3723b18d47d2027b15ff17961afb80f8d699cd665618e2a6c1949374c73160c631ea1128057be3d8161819bf8afd8aa255e83eb611a93b48d8e2055f0f38569f3210516c4d15c9d284b1c7789c69e318c6671fe69b9cb7fdc91400000000b3614eebb2694dcf03723d7b1590aa62d50258869b0000000000000000000000574e171a8585afacea0000"], 0x107, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0xe}, 0x4, &(0x7f0000000780)="b45d04f4618e8c6d4f678a1c219c487469dab5cca78a268198d2ef176faf6f269ea56b222bc0b3f3656fa534aac8fdffb496ea530e458862f2adf9eb8d0591492bb55b4fd6fb807e512a5517ae32e2d888d81cab35ce5b21e956a22178f87fc5c6bf104fdfc076c6908e33d25a0289ab5f52ddd1ccee594e000019648edebcb2303acf721c718896e76a396786336898a974de936beb53bee99f6b07f4e1fbd2125b0c996cfb71918d936c1b00595148bc0c820e0303a92767d1d66161b5e8918fda48491cfba26d143dba3d46719259109a7ded8595309ebe1e5085cec512b8d54ac631c794d64005f5850fa2ae20dd0daef1a44e02f6ae65828d5419068715197c2b0b27fa867151eacb5b7f005699d75fc413a8a9f192f71191c1b4d07aed32ea86172f786d16727c5a0ce4796e15f23af498e8e3cba3e611460ad70b095ecfda21e0230000000000000000", &(0x7f0000000180)=0x14d, &(0x7f0000000080)="c6fafc6ead0da8a4dda4aaf6dd6ddd67805f4fee5b856632bdb1a307d7507a825ab37724cc163d6c66c3e397815819cb1b7e3a71ed91b9c895c1c4321894a06d09446d69e24cb730361c12d2684a0333eccf4dacc18aae4c8fe668e4eaf3844a531c20324cb4a7356c41c7e3e56f6162b7541a53452ceaaa0fc37dbd81377339d458b2947f46d0738eda332e4a994aef7e7b95e47c96bada8d8ea1325cbb82fa0683", 0xa2)
msgctl$IPC_STAT(0xffffffffffffffff, 0x2, 0x0)
msgget$private(0x0, 0x0)
msgget(0x2, 0x100)
msgrcv(r0, &(0x7f0000000900)={0x0, ""/4096}, 0x1008, 0xffffffffffffffff, 0x2000)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r0, &(0x7f0000000000)="8b", 0x1)
write(r0, &(0x7f00000000c0)="0909020010fcffffffff51cb261ec3744b80b0aba0ebe1c1251f015bffc2200a", 0x20)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
openat$diskmap(0xffffffffffffff9c, 0x0, 0x0, 0x0)
openat$zero(0xffffffffffffff9c, 0x0, 0x0, 0x0)
fchmodat(0xffffffffffffffff, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
sync()
r0 = kqueue()
r1 = socket$inet6(0x18, 0x1, 0x0)
listen(r1, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}, {}, {{r1}, 0xffffffffffffffff, 0xc9}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x8, 0x0, 0x0, 0x0)
shutdown(r1, 0x0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000680)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000400)={{}, {[], [], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5], [0x7fffffffffffffff]}}})
syz_open_pts()
syz_open_pts()
r0 = kqueue()
kevent(r0, &(0x7f0000000140), 0x68, 0x0, 0x80000000, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f00000000c0), 0x2, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecf860080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443d, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x25}, {0x5}, {0x6, 0x0, 0x0, 0x200003fd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000200)="34cf362b3c3dfd6039bc17b23704", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0206983, &(0x7f0000000300))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000180)="53ba96375f52c7ebd029feec3c81e6f9e853b3f4cbbdc93df44fa6d6fbe3e331fc7067fec4ec9d9a00364e02970b46491e2b7cb504000000000000004fde4406416b2363da94dbf346ef9ebb5e1284544ffa28f9e1818cb61a7ee71bee4a948362b5f5a5cc4ca0c0e82c45c0004a53bd502ba138920e026d031a78bfa742197f12de6e7aa96c2563316c3e5a4d6c1c05fdec5b97b9e0d22254290189c95eedebd2186297b64cb62cf58cdc0af940d76e5f91050e9284f47d6cf17e2a388a8942828b0676b6e959e87abf5e9325a3f1b8b51c0094744022c715be7f752e16aaad5ebf5b8ebb1d688fef12f6f7f7ddf6bf9ffe3d3ba5923c717dfe0966fab0ac08a33ec3cf136a385fe8ae094a7dbcb92a9be41cae2fe6b69f513cbdb9f8cc3edf77ed1d4c5ff111ef5a81f83279bb9819ba745038322ee1", 0x137}], 0x1)
execve(0x0, 0x0, 0x0)
mknod(&(0x7f0000000100)='./file0\x00', 0x1ffb, 0x0)
pwrite(0xffffffffffffffff, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000280)='./file0\x00', 0x2, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106924, &(0x7f00000000c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f00000002c0)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd7f0000000000000096fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504446, &(0x7f0000000000))
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x67, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
seteuid(0xffffffffffffffff)
ioctl$WSKBDIO_SETDEFAULTBELL(r0, 0x80105705, &(0x7f0000000080))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x800000, "000000dbddb97b00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
close(r1)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f00000001c0), 0x921, 0x0)
ioctl$WSKBDIO_SETBACKLIGHT(r0, 0x80045713, &(0x7f00000000c0))
setrlimit(0x0, &(0x7f0000000000))
poll(0x0, 0x0, 0xffff)
setrlimit(0x0, &(0x7f0000000080))
poll(0x0, 0x0, 0xfe9)
execve(0x0, 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x44}, {0x2d}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000000)=ANY=[])
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0xa, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000280)="b2770fee66a10dbb591a8d0302f9", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
poll(&(0x7f00000000c0)=[{r1}, {r0, 0xd1}, {r0}, {r0, 0x89}, {r0, 0x8f}], 0x5, 0xffffffff)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000080)=0xfffeffff)
pipe(&(0x7f0000000200)={<r0=>0xffffffffffffffff})
listen(r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000240)=[{0x5c}, {0x28}, {0x6, 0x0, 0x0, 0xfffffffa}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{}, {0x35}, {0x6, 0x0, 0x0, 0x8000000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
syz_emit_ethernet(0x22, &(0x7f0000000040)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @multicast1}}}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x2, 0x2, 0x0)
dup2(r2, r1)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
renameat(0xffffffffffffffff, &(0x7f00000001c0)='.\x00', 0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00')
socketpair$unix(0x1, 0x0, 0x0, &(0x7f00000002c0)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getuid()
r4 = semget$private(0x0, 0x4, 0xe0)
semop(0x0, 0x0, 0x0)
semop(r4, &(0x7f0000000240)=[{0x2, 0xffff}, {0x4, 0x18, 0x800}, {0x2, 0xffd6, 0x1800}, {0x1, 0x37, 0x1800}, {0x2, 0x5}, {0x1, 0x0, 0x1000}], 0x6)
semctl$GETZCNT(r4, 0x0, 0x7, &(0x7f0000000540)=""/233)
semop(r4, &(0x7f0000000000)=[{0x0, 0x7fff, 0x1400}, {0x3, 0x6, 0x800}, {0x1, 0x23, 0x800}, {0x3, 0x22, 0x3c00}], 0x4)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r5=>0x0}, 0xc)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000740)={{0x101, 0x0, r5, 0x0, 0x0, 0xac, 0x45}, 0x401, 0x9, 0x4})
sendmsg$unix(r1, &(0x7f0000000700)={&(0x7f00000007c0)=ANY=[], 0xa, &(0x7f0000000a00)=[{&(0x7f0000000400)="6c55fc5ca0d3f9943cb873dde2c936c8dc70f358eda8cf9afb5299fb29da8ecbe7ccd18bb88f2839d75a43a3a7752f9706fa4aaccfeb629fe76ff5a636a0bca64fa2e6da1835da2c4bdd69e20690e82f4a8897b5a00c7325f8d3ce59bf8258eee4a66ba8e1212fb7f858d730f984313c95adbad0f137b2acee07733a6612fc", 0x7f}, {&(0x7f0000000a80)="71c544991e31bafee14d7e39c59cc247a119bcb972eebdd579e211c64a38856ce3951cd93c9f15ae516d95e13684bc11fb5ac93da8ff66edfbc69b10ea7d4388868fbb31d658690465f95a0e58c9aacad8677d8aaab441ecef315731f01de2105564976d6f27b31a99196c14f877ba3d406dd673e9cc81d6e777f99dcb986123d8e656314e2efbf211834810f670c90aca43d7c2c274e8fc7b97b18698c88d73384f2643e920b69959f9426b17c1a86a02eb7c89b0107aeeddadca97e98c80c2eae9bf5da8f0a6003b0e170115395d5ef43b06569346dae4ee72d5f9da99c14886b0511982ade2b6249945609873108975b6ffdecab7f401b3a19282", 0xfc}, {&(0x7f0000000500)="4e9be60a087212864b82221c8b221caed471e8a4fd7e2170c36242a7f93d22141135845fc556938a41131116e6647a37184444e53bc29d1935809cf1eae923d22a43d4ff41d773926eaf61fa9727f3c074ba6a48047ad5392fe5aa24905b7ce4f344aa7d6889f1e953179158f522eed995b813001f4efdab125511b5ead34dc453d3691c9e2babb6b3396893e46c4766c8cb6f37e0c7dd3e3aee79d69524a528ad0963c49dd6491132ba1161abb3c511bf6b95d302cf67c1cd68fe73ec88", 0xbe}, {&(0x7f0000000300)="e9610c9bad6c8cfd99f0cff7f1962170a76f27b41f0e439ba32bebe7f533ca42c0a9db7763499c7018418b80e998bf6301313248835c131b3b8347ea5baf0a69b85d398fcbdbec1dff1eeb73fd16359906769bd9eed3cf7b12846ad5a851ac098b64c7b955cb70e8ab8de43975b7de5522a543e4fa109bcc696ec6f5da1d1e10a0742608befe6d7ad6", 0x89}, {&(0x7f00000007c0)="7fae865be78ad87b61320af42b882c5d2be5f22027baaf02d51a819c5e847ac1eecae814e4e2754542402300"/53, 0x35}, {&(0x7f0000000840)="860659af783b6ed42743393cb828c0978e6cbdad26978790869438887f7abfbd7b6a7332a040e87d0561b03aab075d56d07503aa7ed7043a79aaa3b93645d6166e5d44ea3273b5c6c8631099793a90bd5ca769a5a7abf6f6130245bfaddf355510308893c3f7f56e53967bdf6806de0703d6ca1fe19d547a38e02ac4de9dc84f09d0559bf0c4e6b975edcdf8be381b4dd92b534d525fce7aa2ee3fb83ca19b28846f6c52b6a10d252a937b78f69f75698ab05cdedced8ba727e4769db981b86d23d871aa1f5746c08d286d7261a1a9deb71cb609098d", 0xd6}, {&(0x7f0000000940)="e09552d1a0a476fa2e161a85c875c4aa5bedd4aaf8b3b9fc1c2212fbf50fa13f975032d9334f18949a63a331afbb57d0385f500c7211a3efe5be652da66042e2631fb6afdd9fa4c94648875cf7b0c8b63cb28a1ccb225824c1d73fb56c656eaea8dc3842eed717fe3755cc6bec44936f5e9fd5dc8aa4682452ee96cb1a1af7484e26e15c7083964011650c83b37d550511ffcd8c82d506", 0x97}], 0x7, &(0x7f0000000c80)=ANY=[@ANYRES8=r4, @ANYRES32=r1, @ANYRES8=r2, @ANYBLOB="fa812e47aec7cd381b40b1cd4dd43a5ae612eec4300ab5b52272655bd438b2b117ecdfb8f49ffe571bd92178359783ee51f15b3d19aeb2eadb320bdd423f433b835626b59e2a4d6046caf46e166d02593241150b8c7d9461", @ANYRES32, @ANYRES32=r3, @ANYRES32, @ANYRES32=r3, @ANYRESOCT=r2, @ANYRES32, @ANYRES32=r5, @ANYBLOB="b400ea007c96e4e9bfc2b41c898ab2392225777d33133fec9d8c968d84386febd5f8ae9bbe3accbfe829b5c4662b6fff8f3db502d8b23178e1b91330f931c82707d8d2b89186797a44c8545e14fac7e7cd85b92bad6fdf28fddfeee1951e6cab4f7e3c04c5472af3a9fb"], 0x48, 0x8}, 0x4)
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000200)=0xc)
fchown(r0, 0x0, r6)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000640)={0xffffffffffffffff, <r7=>0xffffffffffffffff})
getsockopt$sock_cred(r7, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r8=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r8)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
execve(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0), &(0x7f0000000280)=[&(0x7f00000000c0)='\x00', &(0x7f0000000180)='(*\xe6\x00'])
r0 = socket(0x18, 0x1, 0x0)
r1 = dup2(r0, r0)
getsockopt(r1, 0x6, 0x2, 0x0, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
syz_open_pts()
syz_open_pts()
writev(r0, &(0x7f00000002c0)=[{&(0x7f0000000280)='#!', 0x2}], 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000140)="2020ff00650a", 0x6)
openat$bpf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000100)=[{0x44}, {0x30}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
preadv(0xffffffffffffffff, &(0x7f0000002600)=[{&(0x7f0000000080)=""/215, 0xd7}, {&(0x7f0000000480)=""/4096, 0x1000}, {&(0x7f0000001480)=""/4096, 0x1000}], 0x3, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000200)=[{0x0}], 0x1, 0x0)
msync(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x6)
setrlimit(0x8, &(0x7f0000000100)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
readv(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)=""/57, 0x39}], 0x1)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000200))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setreuid(0xee00, 0x0)
r1 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r2 = getuid()
fchown(r1, r2, 0xffffffffffffffff)
setreuid(0xee00, r2)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
r5 = dup2(r4, r3)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x50}, {0x4c}, {0x6, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f0000000300)="bfa8fb47c4b3ff7e18b15dc05e7e", 0xe)
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0xa, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x2, &(0x7f0000000200)=[{0x5c}, {0x66}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@local, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @remote={0xac, 0x14, 0x0}, @broadcast, @multicast2}}}})
sysctl$net_inet_divert(&(0x7f0000000000)={0x4, 0x2, 0x102, 0x3}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = dup(r0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000240))
pwritev(r2, &(0x7f0000000500)=[{&(0x7f0000000140)="ad80740490377706ed82a1bba52bb74afafd86958f27c50a5e25b9095e846e0fddc135ada439bedced5e452823ab37661a08b9f504000000926d41943a961bccd9879c98b10718fb2b000000006e1b58944dd16c2df783384b2988bdcaf4d4c25c08fd705875a7a57fc5d21e9f91748a121189c10ae64fbb421e53fc740ba0434504af2a3149018de4f1eea8b1d88978acd0fca90e5dcc44846684d050aa9d3e7ad51949cb8a0611509aff269b9ee4ee78cac32cd8e3ed2a94320008cc0131349e67358267540d051fa1cce9663d6e9745a349fdc929b7db94848351d96c7f15dcfa6e674c0e36a94db8df8fba61dcde98d7d48e7f5cd1aa275f70", 0x158}, {&(0x7f0000000080)="2b4f8f030000005485c9dbc244f420d66c8e063c016a99faebee87369037984bb7f5afd68241dfd7dc4e477551ac54b68a5df951b6", 0x1f}], 0x2, 0x2)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$SPKRTUNE(r1, 0x20005302, &(0x7f0000000100))
r0 = socket$inet6(0x18, 0x4001, 0x0)
shutdown(r0, 0x2)
connect$inet6(r0, &(0x7f0000000000)={0x18, 0x2}, 0xc)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000003, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020699b, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000002c0)=[{0x40}, {0x54}, {0x6, 0x0, 0x0, 0x2f}]})
write(r0, &(0x7f00000001c0)="9e2128e4ef5ad665a6215565e4ab", 0xe)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
writev(r1, &(0x7f0000000940)=[{&(0x7f00000013c0)="63871efe857f8ca8789a3079e5a6db6ce89e457d2cfa2b2cedfe40437f7afa88ec3e95afad110a8128254f2a887884bfe79983ba3403b7d348fbd88abdc992beb15c591b41fb935437a728624ddc420eb731b00701a328a8fe4e1b88aa9b54711c298413845c86505960140e85633ab9e64c26f4750c71f3f42f3edaeae5b6b40a9d330418bb651e0f57c14cb0e9dbd25e6631b892b786528b936af629b4ce785b165603d14aacdf1779b0ab43e179acf14316b84e688c7e389843edc85a9ed127db7b4d6a93e33812be953d6122b5f7ce651656119cb24d60027bcfd06a469cdb14e2e3e2a68fc555964db97226ba6fca82933e05a20c772ebed488a655e4030c85396108e03c2091939b41976ebbe2ec571aa2ad3dafb953439fb9f266f3b32a3ae2775ba6748d5e89fc4519fd7111c250994db07c0ed73c31e236d29520ee26cf6182e543ec4ca6109c72095e1f96db1170e8660870cfa9c05021429d4795d9c274b57cb1ef27ff29f5ce703510a347aedcd42eb9752561600576635bef29351e2d90302b5554446fd06e123a2bf4872db62669e6d3ea7fc6d9d4aba1c021ff8e1606fe6ce7c6c1e282cf78a3b7c71465462d19b1a3aac4db9c934536917f5b11146483661c94895cf6b11d7a0ef66ecc0c17b1ee3267d3de983334000d6fc8c5ea3e3ffac76a6fcdb73c02c3708dc38ec915dae79c025d1d90e4e29cb47df58a7d9e172eac01fb2a6cea3749cec54c4e91e47343eee4dfd0987b4cff6290348c1d01a1a0dac8b7a3fd804424283db06802f29cd82adf6748d9656303bcf5a5c7d5d83e093967af6cd48ace22db14e0a30a2f417e37239f1f7a65a3e1b1821df90ff568f3b1e969d93a36c81d0930331f40a0afa8dddc263f6c74496abdfce1dc5e89e5383a9d4ab08b8eeee71bd6c2b8d86193e7ff9d4f1cf98e38a4f9dc50e048f721fd3b0b92a72926b0edb07853b09618aa2714e514dc6b2c6acd14d3f09979ca8a9f02fd0bd8ea21f680f078cc81058a71f45911c4c837e196d32f1994a2d124d2170d42bf942a481d0fd36bd5f97e7cf6c54c52fddac3e0f0d13bcca319929a6087c6b37b0ec8d3c55c49a6c10c42cb06a3ade4993e183444d51bf6a1c89cfb77567c01a2740dfa9e02f730f54ae22e9e54ee3acb3273cdeb8ac00351934be5982b9e8075", 0x349}], 0x1)
writev(r1, &(0x7f0000001380)=[{&(0x7f0000000040)="b7", 0x1}], 0x1)
readv(r0, &(0x7f0000000080)=[{&(0x7f0000001740)=""/4096, 0x1000}], 0x1)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
mlock(&(0x7f0000ffd000/0x1000)=nil, 0x1000)
mlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
syz_emit_ethernet(0x32, &(0x7f00000000c0)={@broadcast, @empty, [], {@ipv4={0x800, {{0x7, 0x4, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @multicast1, {[@rr={0x7, 0x7, 0x4, [@multicast1]}]}}, @udp={{0x2, 0x0, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x80}, {0x60}, {0x6, 0x0, 0x0, 0x1f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
bind$unix(0xffffffffffffffff, &(0x7f0000000040)=ANY=[@ANYBLOB='\x00\x00\x00\x00['], 0xa)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000040), 0x14)
r0 = socket$inet6(0x18, 0x1, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000000)=[{{0xffffffffffffff9c}}, {{0xffffffffffffff9c}}, {{r0}, 0xffffffffffffffff, 0xb1}], 0x0, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001940)={0x0, 0x0, &(0x7f00000017c0)=[{&(0x7f0000000280)="4023689fc5df58df1723d83069b35deba0b8f4a2fa2e08c3f15f28eb9af1d8b00a345475874134b4d5cec286551d54fb", 0x30}, {&(0x7f0000000200)="a62c923b95fc47b33a77260de950a51fc9dadcb8889f6f8af4", 0x19}], 0x2}, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0xfffb, &(0x7f00000002c0), 0x80000002, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
setitimer(0x0, &(0x7f0000000000)={{0x100000000}}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xc088444f, &(0x7f0000000240))
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x35, 0x0, 0x0)
r0 = kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = dup2(r2, r1)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000240)=[{{r3}, 0xfffffffffffffffe, 0x1}], 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000200)=[{{r1}, 0xfffffffffffffffe, 0x1}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f00000001c0), 0x7, 0x0, 0x8001, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3c, &(0x7f00000000c0), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000001800)=[{&(0x7f0000000480)="ba68edd58cdaafeee9693ea7bd6a486b28901e36fee720c0ded58bfd2613ce8f864d80f162206e811056f7e9e3de7cb3b925eadb0aecaae69ed91cc978dffd532d1d655bc4473aebbab9f512d871c23589bca9e752729f37fd502b7fce04d47b4d253675e18fd589524996e9fbbfc09df73b765ee0efb9e82d4380b63e0b1e70e296513f6ab0925a640d8ecb3d0037ebf37c9e938fe6cfa4e224feef9300be317f6bcfa5021f25e8873e8ccc4ed28c80db0b50367178b6b7ec927d94ddb4ac4297093bb357acdd8098ceb2bb1f155465140ae4df7e8ef50cd4d8faeaa079bd10b40610431759fab4c39094b20585609721dbe4cd24d6d46505fbf8b13248e9c717e4e53e38c17632f84a0e995223afe997daaae4b4892b60cd3269abbd43feca5a36c14261cf8d8dc504f6ebcd343c9a8d3581624705ad2ce0a027e6ee0b4276125fb8dfffad1a2b3311c83b3a945e8ed80d0b0761f6793de605c53c0c9f3f2852b96988dacd47a3c7f5b8efdf6e63db48b4f04550ad35d435ae98bdea43f4a7047e5a83bf73554b2164bb02a1e11b2c69a1603bb77468556ee092f61a90d5648b680f7257c107ea233b885e48d33c5281cda15693c7a50cd5cc804f34085bb0d0668b678916194507f851ca203f492eace636b8afb102e8d2dc8154b2e537f2277ba78a48c642f2fb80179bc4cd2461d070f01e5537d286e919a32814946ed0357573d596ee710c8b3456dc6e77084fa581bfd5044229baad5691d7765d587adee7d1b9efe48858a087e2e7cac9525142bb07e1e578b3840cc4361e34b64864613fb89b157ff99fb38e5e56c864a70218e8a105542fee74b65dac1278d8c818f7c6e398e110e563a625ac83a7550a67b970ef676ffa8d267953f2df02aefee928c8a65266be2f452a2d977c9b67f6aecdee59646f6a5af3253e2e33e19e34484acefd2582f15f230f63f26b67dcc55e20345062e5f6f8043a4fba56631d4e8978b84383e66850f8556cb67c0ef4a553ec46c56c5642fe478888ae8fd53eb02bc4fd25a168410b66f660a7be55ead02b6ec60e3671186b2417f970ab9b83fea3cda015f23e7b1f59c476e5f1faa01449dc86abe44d4da63d9f09c933911bb9a059e0341781e1c2c2895f12b0eccbc36c84c2c75047e5f15d319b72836187da075852fe1f3ae02d7715a0773ca3944d6940450ddc72eae4ff9bab40919fb22bbee6fe26d9c6cb62e6bfc4f7fe3527c25ff3a2aa97220c71b3950fd16ac6a61f659d7bb25137fd88532a703ebf133f488c1faa2bee5713eb10e651137d87dede090d85cd59128a958d04ed7969c7402d0c5655d55f1bfc2f4628d5a0fe0aa605171789bc2a2f2d9ac0faf33e99c5c6f9963e69fd93301c3c86a6ee29a0bf039baad146ca84dfab8e20ab438a5071f06b19cd2538dc15d1967a478957c14f2a4e5c947d3965f778028f5f9f58b87ed8d680716ef7a8bae387f52645c519f95551e4af67ed34fb6693ad35f1d3e973da4965868f99e4c883ca949e0bb4c59524bdeae72c4c10178ad7eddd4836375085e9ab77487154570c661690b8c98bd0126583deb31aa3cbc3f77034f288ae08a702d140a5d42a1cef85103d509c6d5c8ea1a047c45b9a7caa65113b78f79b3418f86120a55c98caea829418618e73755ca3aacbd845406e51b39e9e44a7dce22ee0f7f1af8a228800ff4cebb000f141905924d339eeb770015fd66bd5584853150ebe8807f12c25bc531313ecd34048c4b0225fe5bb6452606166b43d6ac508b5b268992baa3aee3a9fb45eda776cefa539b5e7cb4abace74f4a6de39f4be9d15896f91e4df1e5cef7d3dde1af6bbd6cc614e38b4f1de110bdcdd62dcc1d40d9fa93d2b75d44477f9ad1b35e481a92823060e63ab300225d4eac0f545c90aaf66502cb1f662bd2882aeeee64df3d2ba4f29b64c8c3bafd077662b8c76a9c42be55b4109899867f172009779fd2519cac2475bb275bd550cdeb10f704f0189381ffa26620f14a37f6d0896acadfc58220f1f50b41fc59679f76297358f9e3208c3c5e3efd8b4b72b53504e4adfd6478f0dedc6f6178851eeebe6be11e2af361903f04cc67dd110c4e1b5f5e53c401eee737aac393b59282aae107e8351abe77f9050cfcc8bd413773d7048b8c8bbf2a3e6bec0ee6684bcc484c8a3a2d8443eac1cb2900bbd2a66b7fdbb1d7b7e38fe65a22ca0f94e0f0f4cf6af77311cb3bc82cf63a0b7364722fa4d9282cb8b7c80d8a9497a83f6f9b4f0051a595fc035f29a933cd3339b8845d4bc4e31494c071935fec3e4aaea5c5a362b110e8e3f0672aa90cf9a244335f593576c8c71528fa21a7cca0ba9f7be93ce841f36884de699f31327ceea4b5985d9d6734f1a241597aef60ae252af1f2c2e565ae5dfe3e44cdd0cba979825731ca3df0df34ffe1a6e446d46a497097c807a22c38440d28ddeee91df2d6cca42a54074fb240fad500cc4baa4371022174dbb2af3f06ddcd77537e6d59b5296d42313ac4228df12c52143233b1ca811cb42652adc0f8d60a49e2184ddaf6f78890cbcc3d1b354a2ca85cf054185c9f9759a6816d326f3d3c68c137461b5a8eb7a35cb9f09c364055dfb8032f1c385739a99825cb446c0355184368a11e2a52e30e8ee90ba7834d72c2497d449249e7d5656bac9622c6b3d0ba52ebe6e54c98a09b84ab469f02885fadc3e0cdf074e152246016f34cf6832269c41c3baf6807e3c358d7ba0551b09f60ebcf9228a791b4e7f59c3976895e42fc5fefe23bbd9b56d47aedc454133d3b59f9c06c20623d3d44393090a9a69f9c24654a0af502e2f716ef37ab93c8166eb58a036ec91a2291f4e83f2836b741037892cbfe1064625c696bbc3c0a09d6935fa84573689ba182c4d1a764478b77d44cde509062af55dc8f695b57826ee63a734ac0e4bd3c7f8ca77f8a381e29604d9e389bf5108039b0a397c1d0bb96333f2b5008e219bf00113a5e3dc1e8d1510ecdd8cd102bd4e5a8711d7f3a46d0e7b28f56bf54aff4929eaf46cb6335c517815d8404a6454e2c9e30d5475e6deaba33c71485b5f68a2d89955affa2b9a0cdb427caca2ad963e96381c9493f1ff5ae5b3fe458b3d3eec7a322d3ba7d5001550fb91a2f9cd55260261223ba8f5e0b59dfd64c4c1e3cda6360fa373c0ead4813a45602f6ba1e64a58c8679b00967bedf89bdcc21fab5847ecc23ff313b1418968ffdf6bc804d7ddfcaff825f7429d9089a8e58036a234b66551f4ed16a9a08382b612063feecfaf2e34cf2c28699477d2ec893a2cad9d3371dd4219583ab9f87c4196c58b3bb79d099d0aecf3c89190e7119d9e2df6612a1be8c5636f0d79963aa82a9cd5297331c42526522604fe9fda313000c7edaace199cec3bb30231cdb319497948a5ba78d336a7b83d6988ff1bfee4dd82cf2e4e57634b23289cd9b1723050e5a91007531d0e65f5b9bd8f9f6ad7c2af9d65330ad830ba7848dab135ac6aea89576ee36d1d6220bdf52009d742749365eb1e6cd37c581beb9ed5611ec1422b233316ac58b0ee5a57dcb0125a7b17846d0f73f0fc6c9fcad7d0d0ffae1395dd9f7d152cd683214fbb9368cc4b8076bcd88d537ef22c7b08bcbc60d881bf4ea6e4a3f27be923ad00d13258dc7339225219c94512a28472c8d937c3d38d39184129e97cdccda4dfd1bdd295bc1fe4d4e2cdb21ddee1bd5821fd6f5f3ca1c7703d5d7f343d87f1b2a388240fabe2cb352c81d25fe38b5d123f9d969ebc7be4a7173907f2e207961031a2b4daf09575e7c558c41c6cb2424b2e4b3a20008cd8fe87a32e8ad3c95890df692fe2ff0da42730df0644010967c3f1411446d39f8c476494b25ab38d91e907ad1db6e82d5da1d5e0d457b70c5712cfd6f0517181ed1ab5ddc93f0035b789ad91d27c80045b328a86829bb985f1069e548377bdee", 0xae9}], 0x1)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000000)=0x190, 0x4)
open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
r0 = getpid()
r1 = kqueue()
r2 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
kevent(r1, &(0x7f0000000240)=[{{r2}, 0xffffffffffffffff, 0x1}], 0x9, 0x0, 0x0, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x53e, r0)
kevent(r1, 0x0, 0x0, &(0x7f0000000340), 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f", 0x111}], 0x1)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000700)=0xc)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="b5027b1c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
listen(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0)=0x441, 0x4)
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
close(r1)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
recvmsg(r2, &(0x7f0000001d00)={0x0, 0x0, &(0x7f0000000600)=[{&(0x7f00000001c0)=""/228, 0xe4}, {&(0x7f00000002c0)=""/100, 0x64}, {&(0x7f0000000d00)=""/4096, 0x1000}], 0x3, 0x0}, 0x0)
r3 = accept$inet(r0, 0x0, 0x0)
sendto$inet(r3, &(0x7f00000007c0)="1daad5cd36195d6810b318271ef9a4e226c2d78af736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8adbe6f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbacff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e75e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e138a000000b92c28d39f6e23bc224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70df9265fa833f7f7788b351b9a0abf03d9e24db2448b2db5c1105d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc346d36d0fe7a2d0b32aeff27e94fe6994ffe7086d8f0c631b9688003f65d4b91d0000000000000003f5cea6e423358731875fe8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc7d8f9f2fe3d1d4cf4dc054a98f9013b515452b742f911f5aa5258ea5504ec5fd29ae3124f55cfdbea9c3969dc552e1d6f13d86e3043a8ed35e413ea2a8c43d6c462463b88ea0a3fa87742efce671a2f79f5b66a844fbb016ba0a0eafb7f26c47f58f25808cbfc6902b0f0133039066c1b0e4b133ab19283a8447b9412faf99b67243a3fab7392f29b6d3cae0b4fc6e528f7662267692e44540bfc23ae65598b6b621eed2daac98c6f59ee7a08fca4bd017e4b6b9be821d9af3bc2fadf96cd15857a54d7e879fa9d61bf34654841f961a030f3c81f9c750128b0ccb8fa9262c5d930c8f527d9bd5fd14ec06e29de", 0x295, 0x403, &(0x7f0000000040)={0x2, 0x2}, 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x5}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
munmap(&(0x7f0000400000/0xc00000)=nil, 0xc00000)
r0 = shmget(0x0, 0x4000, 0x232, &(0x7f0000ef4000/0x4000)=nil)
shmat(r0, &(0x7f000047c000/0x4000)=nil, 0x3000)
setgroups(0x3, &(0x7f0000000000)=[0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])
r1 = shmget(0x3, 0x2000, 0x700, &(0x7f0000ffd000/0x2000)=nil)
shmctl$IPC_STAT(r1, 0x2, &(0x7f0000000040)=""/53)
shmat(r1, &(0x7f0000bd0000/0x4000)=nil, 0x1000)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x80000000, 0x4, 0x447, [{&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000632000/0x4000)=nil, 0x1}, {&(0x7f0000ae9000/0x3000)=nil, &(0x7f0000a79000/0x2000)=nil, 0x8}, {&(0x7f0000ceb000/0x4000)=nil, &(0x7f000093d000/0xe000)=nil, 0x9}, {&(0x7f0000e0e000/0x2000)=nil, &(0x7f0000c9f000/0x3000)=nil, 0x7ff}, {&(0x7f0000456000/0x4000)=nil, &(0x7f0000cd8000/0x1000)=nil, 0x8000000000000000}, {&(0x7f0000c3d000/0x1000)=nil, &(0x7f0000c5d000/0x2000)=nil, 0x22a}, {&(0x7f0000408000/0x1000)=nil, &(0x7f0000d6a000/0x2000)=nil, 0xffffffffffffffe1}, {&(0x7f00007e1000/0x4000)=nil, &(0x7f0000912000/0x3000)=nil, 0x4}, {&(0x7f0000a6c000/0x4000)=nil, &(0x7f0000ffb000/0x2000)=nil, 0x8000000000000000}, {&(0x7f0000e35000/0x1000)=nil, &(0x7f0000b11000/0x1000)=nil, 0x3}, {&(0x7f0000a7d000/0x2000)=nil, &(0x7f0000ac8000/0x4000)=nil, 0x6}, {&(0x7f0000ca9000/0x1000)=nil, &(0x7f0000e38000/0x2000)=nil, 0x8}, {&(0x7f00007fe000/0x4000)=nil, &(0x7f00007b4000/0x2000)=nil, 0x100000001}, {&(0x7f0000bb1000/0x3000)=nil, &(0x7f0000fe9000/0xc000)=nil, 0x8}, {&(0x7f0000d07000/0x2000)=nil, &(0x7f00008d4000/0x4000)=nil, 0xa22}, {&(0x7f000050a000/0x1000)=nil, &(0x7f0000f49000/0x3000)=nil, 0x1000}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file'], 0x7})
shmat(r0, &(0x7f0000ffd000/0x2000)=nil, 0x2000)
ioctl$WSKBDIO_SETDEFAULTKEYREPEAT(0xffffffffffffffff, 0x800c5709, &(0x7f0000000580)={0x8, 0x5, 0x10000})
shmget$private(0x0, 0x3000, 0x6f2, &(0x7f0000ef9000/0x3000)=nil)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000600)={0x1, &(0x7f00000005c0)=[{}]})
getppid()
sysctl$hw(&(0x7f0000000000)={0x7, 0x6}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f0000000140)='\x00', 0xffffff99, 0x1, 0x0, 0x0)
setsockopt$sock_linger(r2, 0xffff, 0x80, &(0x7f0000000040)={0x5, 0x8}, 0x8)
close(r2)
execve(0x0, 0x0, 0x0)
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000100))
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
close(r0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000002c0)=""/155)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x26}, 0x2, &(0x7f0000000200)="fedc3ff77c4517dd50075c9bc1ed8fa8e72d48029f6df57177b83c89b01141f52b7642eab8b84dee8bcfe0a335078a10bdb4eb", &(0x7f0000000280)=0x33, &(0x7f00000003c0)="0c93c293fd27b56aa91ad595cbb7860520dfd4232a2dd86673c0b5ce4963e38eb9e7813c5c519147af6bee94029d9a5f5e923d69ad2e33fb854f5bd91eb928d3b15b409f1c030319de2b42a71e8a54ee74d64567df36916aac7f8f28d39b29aa62ebe14f1ffa35", 0x67)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r1 = semget$private(0x0, 0x4, 0x1da)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000100)={{0x5, 0x0, 0x0, 0x0, 0x0, 0x1da, 0x7ff7}, 0x7, 0x3fe, 0x80})
semop(r1, &(0x7f0000000300)=[{0x2, 0x9}, {0x3, 0xf98, 0x1800}, {0x2, 0x5, 0x400}, {0x3, 0x77ff, 0x800}, {0x2, 0xcd, 0x800}, {0x1, 0x1}], 0x6)
semop(r1, &(0x7f0000000240)=[{0x3, 0xffff, 0x2000}, {0x3, 0x7, 0x1800}, {0x0, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x0, 0x517, 0x800}, {0x2, 0x0, 0x1000}, {0x3, 0xffe}, {0x3, 0x9, 0x1000}, {0x3, 0xfe, 0xcf81320aaf5ac8b0}], 0x9)
semop(0x0, &(0x7f0000000140)=[{0x3, 0x101, 0x2a6c566928f6743}, {0x2, 0xfff8, 0x1000}, {0x1, 0x0, 0x2000}, {0x1, 0x2}, {0x0, 0x100, 0x800}], 0x5)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000180)=0x1)
ktrace(&(0x7f0000000380)='./bus\x00', 0x5, 0x18, 0xffffffffffffffff)
sysctl$kern(&(0x7f0000000480)={0x1, 0x17}, 0x2, &(0x7f0000000800)="103bb0a35d2771302a0e47ea4e96c803a0b684e7b6899bb7c0bcdcdb488ced4bfafbb5c291a3475540bd58b172cdb7e01a124bdb17a6772c9fa700adfa83728e45bd3a0766fda71c413c506f02aa799a540736e0df20813a64d133e25f88bc28418172139640f66cb338ccebb7da3369cdfdc6029eae49d0ff717484c99fe7ea94d484b23f7ca836ef0a8f4e70637514e369436c6f92906d139f66e807895651409df3a58b0c48d912049cb318b681af50a674dde53057e9694ef1c5c3a116d615a8e87d19615c82038023f0b00fd6e25f44f4b4645b92599d9b2071b1e6532f59d2ea0b5d60649a85c7a1bda4a3988068b9134f136d635594ab5055ab75a0b494101c7905e0c55c12877563c41fafaed9a7c83cd214ce9d692b825d802a6eabfff133ddc94d6f066f3de019d249399db92248035ac06fe69742052daa87e4e55a59f170e919f4265dff26a17b7593c7e838fdb39612dff7195d044f2e9c3241086d656a6f731e45057ce3711aaee86a5c80e152a3f1cd88474666df6d47fdb56d6100efe8577697f48a387d2d5cdf90e8c433c51cb736b31ff9ecd0845421f9ac178c20ccfa195c36376189cc5d84118741931efd67fc83944f0504381a0fcc69a534218f4bdc208d3f7672e4ca0108268e9bf97eb3fd58de5703c4b21e5d6f7783aa74831e85789485522574e7061b860e999f639db086b0d1b9ef45ec24cf3b30b41a6e7bae64a9ae919d46ccc93b80329481905c3a68a6986639ec3bc7e9761b42109dc3e01d0a902c9fdb0255c81df895538c24843d3e8da2a20a509d138e3213c88cd44397573c44f8266c0cd68179bed0972df0fc1e0d14b25e02f3a30298db5bda08430906614752e5f69fd2f88d551cf40b2bdf02e1572cfd08fae365124273f6eb470d469ff7d9ad252718c8b93c17956083163ff53832019feb254d22cb99c3c3d477ef7068ef7bfdee7500e3f86c404d347d2d691ac0118885c592fa55295ef4d4afbb603bbdde6c84ddc930a7d2cfd91cdb9201c43f12cbb596dcab41db7627e9ed636ea67428283340d8bbf01a06138c9918fa6e59aecb3846b979b425abac77c28a4c85d1a6c69b7576af0e994bc56418b29a74543e5ca3dee468b0aee2ecdf3de9ca881749c31e272e6ad0a66c2f1c7eb82b01de862f32af9a0703f3b4f1bf0a05df4a4745a2d106cda2812635f02ad842a891ef6ec620a0e79df2c4b05c8a03839470d8d20eb0cc4bcbd5ad70a9a5ac3589bec77d53a29fc32b6b1e6963129b1ce2fb2a7637a5b305ad159d3365d1de504ccca260920a8271b92a26308a86e0166696b4a89466d35d4bd562aca2bbedd5f4bf978bad3ef7e2664e67e05f933af911da356ffaf2e245734cf81b2b7d1bf52240a804dc21f23ec91f902a46701066cb7ff19cf15b9d5a0f715d92addb09e6503e2ac84a21e05865dcbc142070c2d079833ab632dcc0884c97b18da82f0b129051bdecb692041aabd09de527e6f257280d581f4065ca6c7f612f62e044bd95ec8f3b36a63395aad8de53c4f2a8b1030a5076c58c4bb8be34a66791647770dbbecf90346a720c3681bcefa2e454d386d345d8bc3701a0d27c1f8221ebc5309beace5d149af727e88ddb727150c6af3c62289acad31cbf97587ff743eea15028bf85067b134b33e51876a27177fc291a9444f20e6c688cfc2f8ac1542186a5ee2a18f4b69c38696c38a6b06befacc24e1c46a48d2b7f3784388242730b3f951c513dd83e6add58f488e642e46239c3bbedfbedce2cf4eaac7be1b4f0ad5db0069efd81908716a9bf26a934216625cb7a4f3fb927b6bf22c350d11c0a499d9d2beb3a95c23cf86ebadd75f6a66157eb6c4d4d2f2a71935c35c27da077010378f41b681f24d4d64175ad5ad8564ccda82b3b60b86369e70c519af2e567518bc1ab7164fe41fdd3043172595213082702bd61907656cfdfe9bc441b07b507fc076efa2884356016e508832a016f0029aefb2301172132c12591b83348a28ece7fda3e40aec9fd82b7c79b4c33e803f8a6307307caa94c1918d6673fac929c4f3ed1ac84b3b92e0d502aeff43b1dde2509395e03ea026769843542e61759119be24b0de4c4f830ca4363ef56eca685a75c78187cb310e41de20609261aa7b5408910878f5c80d9ae66a3d6a1123f778e74169c52a616f75b74f4948855d7dc54fabf3ae6ca6666ada28912b8b17c9d8397af0621aa90b51f3ee206d69021237d4d0dacf6510b4c63d1265452a8e7e8126a44730d051180723051e60bec853255b4f86a652802854a3f712d560705d7135c3a2fac1a301ebd5b51c824e6fa431439920e1f7c2ce9d4fa948de2002f9cf954307b5348c49a2a83656ab28de2b4bebe9b8b5e38be54c86bfe700bf83a4b3fd28a2bb4063b14b541eb6e02dd975f73d465ff033b341c594f999257e190e76fd612178440ebbbcfdfcc49721526e20fc0f307bc1784fa8e511110b4586720c0d84474a7af9715f0629be2e799db48d6d01bfd1ef8c65b1703fb7cac9891a3df5b1e547a9ec9e965ab962e98f633c51c7c9e2f9109424e61c8ad9b45f445849fa21606de73f5e69e32da0b542bbdbb329fe9ecdf5f146e0d13c6c848baf35be7383b099ac47e0036104d070843554598180941f49d8c27293156fb1b2c53aafdd00337238ddfc1a70e8f43496151a3753f4775c56f57288d7739c7becb48522e8086e03e49eebf4609bd122d59013315ed6f999b771c9adb9f30bf4209d9b05ce9fafb44c2a2a5611906f0567249c50cea43ee29f5030f19e6b345c116e6be613136f5765a0b14051594b59a0de88deeded13b9bbaa101a1d863f74be1154f910be4f51fdd4a451f2009455957e636a09b16852371ac1accb7fde816cf9dd4ef074323c7c744e073ed6b3a283e34e1e2b107a20834ccd639909a8a69b5d558ce56dba2199370abd3960a93b29e05e066eab0fbcad7a4c029012130f8fbb16518aab866a0f0433251131dede3ef7033e5f7c0ca651b9d2d66dae6ef1948bae4b9afb45ce70b95e79b1dc6df50f209a7a4bc0ae9028af48d63302048f5c6fed43b2446691f552476666f7e0f57e0eec59f3c2d7c324a9651916d5d10ba1541139e59607d9eacf88fcb2d863cacc83b2e64545460244647ece4d3465df85c4a5d214619f0085a14c16b6aafd82bbc59faf8f4405572a244df0f9aa277b5caa9e1e11e5924836054cc84064316842e95d30e2c96422ddd3212b11d90143e93a225082ac6ca8b5e86625bb82500c5ae2f10a95407465efce49e2f0d80c21cb926a4b8f5c88ce3f3ef9cb605d90c9f9098e419c25e97ba9e8efffce4a7bfeb19bfc5b8ab5f58ab61ec9422c5e408cad344883533a8d65e674b5b354c90ca8574934f8f034c3b682f0403d8fc087ee54d0dd76158de4565295af6af48b90bd39577a84abbc2e95df87092bf3e3c7573b2823174251025c933c3318a2d38c6c94b3cd6fdda45c840b04bcc62de746653105ada2ba79b551b2ad421c02128a169ff7238ff678c77ee0408eefa741690be4900bf9d69bcdf5f6082a139a4316ea3730e887b7ed57f9f60628be9ccacd0d0b7febfcf51b7f49ca0fa306cb5c6861f5ddef2bd0828db05d082932bb70ee6878759af104c18f0c08e6e9f490ffee716ab6d9142df4a7da16911e9b3c4c21bd0044615828930644684f5917419fd25eeb5df29f0fdb87e7404e4ae1f052a0016d1505f61cf3082c9587bb94b20adfad867a7338f78576f9ea347bf49a086b808ba56760097a5556f35053d9373877234baa6dc2e3cfaf8d9d6d52d8cbadc6baad876dd93fab78cc001b89a93e16f347025179adc2c8bb1160c661445a81a1bb6397f21cebea5abe3deddb1a749cd8eae0ef41489e0048260b0126f3f8701799472a717f509518e02e2916c1f1abc0b56bc1e3be3e3d3db3483d7e910b7b3894f9239aec4a9fa07c935b2f4f847dd67e512ca9c1830ce56a0942fc55df2f2734e5b8c424711694d214cbc676672140642d1e57bf62a3c214ad450d90af8e729a36fa665868234bc1fe5492183553b78e57ae62734dcaeb4c49b517998b72f81538488ee568740e65ba71796ccaf1a6893b9f758892899bbe150bad672712073a90778508896bfb56c824176cdca63a2712f5e0f6bc92389485fd2bc436d802702cd9fbf9f8dd3b59767d6d1113479c885699e2f88f25362eead95db5f78ea3a08b4ad4dce8403ea1861689b61d572a0904a890e241bf90edae8e2b6aeb0c7b3dbbce6566c672669acbaa7c2b8f893d40f0b1bf1d2b7f2af86737551aa5b8f2fbed9ed9261f3099c0ff8b56117487e7cd48b68368809723bb8140214d26cae307740edf644b485e98074b6f0e786935b73c1948baad38957512a3ab7db7c02d250429082e4b2099b81cd75897d3b678b9db1f722e35b01d41c827033ee30250b2fe339b578ecd04b54bfcbbd2f5e3d4fde317646f67e6245b6de3a434dda96a1f0ed6807a4a1195a4a4fef03e67058361e1799bb2fd213601f7e6cf43b90e533a7b844e0989c2d6a0e3df146d640936edce2f790bc570b48ba7b15f5d73c348f9ad5c900feb9751dba6a67e1fb0d73c2a2afe83cc525f4a18d5f21b23ce78e7b5ae7c08aa5ca706844e02576293523fc6e829814926660ff4082e7347c95643dad86e9afec8944b3b03f0d093219900055d674cabfeda51fa9750dfa0a326434b653ad37f52eb4551a95c6a95da0fb5d3f6c5476d74837e69993e4f677e8ff27ce1ad88a95fc628c39e3975ad19788598f6e7e7366b88d3e1f58b6864030c5b71dd660bccbde63cb09cf76687acb970b1efa8052e5464271e0848428af21d28cab6a74bd5860cb84917345a1b562e1d973d5685e31e1c3632e9a29f9fccd9afca6c57c1f3d648f3346699fef4a7609cbad8edf78bd4751f2055fb789fe06c1e6f364d04d17fd3e67efd112e1a5f1a4c848912e53289874576d7440d9531c801cca1a0070d385db6024cfd6ef7ca08a00aab8f62c53c953b8c8169c69e3a95c26ca0e4137a1661cf7671653f968f55de9a74bb572a13fec3361461b6d811d5498b50223424ec3d7bd8111106fd07cd2f47f3f62d252ee3c8f3dfc4b745a2554d71925c2c0defdb2e3a140559dee0abd0e33e90cab4288abe9ffaf3542b661c747327d01e0676f549c37d4bacecfbeac9943860de8c53d191be00ce349d529fc0f4c0cf1c4066b2fc3e697746cae437c454d40f61f91e1b71daecc2a201eb835311c03584c103b7c4430a423a635635837ae9ee7607a3f39acb5b79d8d20708239afe74357648d93a5b1d11ce6e83c56a01869abc7cb7942f5452a155fbc3b9394b5b199d2f541f2bea75d77274aac539f3363d53043e7e6fadf90c3118062d432b8ef998fc9813461f6b95d73a977183908c19efe8fadaed6737c0c2b359c85f8bf714bb42b175da69dc928177af4dff6489e3fe9684264651da2c34d565ff82cdb3fc9089cc257806f637275aa7d22c259b13a55273ffcf2668975d23253486a941c98f935873b5b290db8675c46621c1da647c0cd0c8efd1145cfc50033f362edf0bd02177a9333b1a6b641e9d1863b7ca182c80d3d52c956cb8deb55ae6b4b6026ca8b1bf36df1014626fcb7a611c4a09b77be059d799c9de72a64d6fa1a760e0e5ab33edd0a437b2b201758b87ca0332f39299ea6ff732936facb08bac8020b8d27032897fdd4688bcbcbc29e15066a1d0fcbb3b8a52159d05673f4e4a8f964b0821394d21a2d647e800e8789", &(0x7f00000004c0)=0x1000, &(0x7f0000000500)="6856d61a733ac0ad425b796b5e0b9ed90eb5fbcd149ea8855eb11cda65ce89edc4744da06eca1bdea27b33fa11aa10480e136e43a38934f39240eee15fa2e5d26c1cd1f07b1f970ccc561e07a3ecd19be9dcbb43366309b983ddc8903cf0b54a8e3a3921ba9da2447a3984b0df171e41e99684a5ca0f0f22b49f41d22872a250c20ff05309d130fd1b10663ea89f613f6261c6bf21f30a", 0x97)
r2 = semget$private(0x0, 0x4, 0x1b2)
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(r2, 0x3, 0x8, &(0x7f0000000280)=0x80008)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "01000611aef6e4c611d53300"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
pipe(&(0x7f0000000000))
r0 = openat$pci(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$PCIOCREAD(r0, 0xc0107002, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000400)=[{0x64}, {0x4}, {0x6, 0x0, 0x0, 0x80000000001100}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
mkdir(&(0x7f0000000040)='./file0\x00', 0x5b)
setreuid(0x0, 0xee01)
unveil(&(0x7f0000000000)='./file0/file0\x00', &(0x7f0000000100)='c\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{0x60}, {0x30}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000080)="c89f69db925aba20ff1757a81fc5", 0xe, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x40047477, &(0x7f0000000080)={0x0})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
write(0xffffffffffffffff, &(0x7f0000000040)="a4", 0x1)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x14)
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x23}, 0x2, &(0x7f0000000200)="d81828aed3f72b79947c2c08b17a0d8c796022553491f9824319d92d9bd54da1a6307d894175cfb10e92f2295bf850f83ea4f583ccf166c6beda881305ebe6de22f76fca2933d6a3be4d710ff74363077a226614e3ac4dc938ef4ec070f7203f6688c1b406f6ca2f5a746d6bcfba4877ef69b896a6566e1174e311636b28f8363e14f1b3b64dd39238e134f07e7b62f98183cd5d0865fc5841968a3a1e7cdafd2297c20aa93e6d407bc3509c6fe42adc707f5a5e408526494093674010e17ac4a8202e373d386792c22ea4b1148baeb4d30000", &(0x7f0000000300)=0xd3, &(0x7f0000000580)="93537391c2b31347fbccf755bc3bdef07e4a60f20b2f0d901fdff53dc21b4350f7bf639a35bc8b6cde688a7ebf2362fe0a2aca5e3e49ca39799a10b87ed7ba121205ad6a4e4e6d7eaf461b440df18d40afa33b16aacb3f99077ed595eda6f0ed025110c604abada9fffffffe000000007af52c22473c6e0043e1f96b92c0ac0307e6ef170ff8bec6e30f445fa912f023ff405cf91b10162d388899056141f26a4a937d3270a4d23a83a3e3be3c2a87c7bcd2eb4206b65415d9567e5aee9bc0b05adb430f139c35eae0d5287285b17c763467f039255c208d05fba3cec72384f9d0780e53884b", 0xe6)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
setuid(0xee01)
stat(&(0x7f0000000040)='./file0\x00', 0xfffffffffffffffe)
semctl$GETVAL(0x0, 0x1, 0x5, &(0x7f0000000400)=""/149)
r0 = semget$private(0x0, 0x5, 0x288)
bind$unix(0xffffffffffffff9c, &(0x7f0000000140)=@file={0x0, './file0\x00'}, 0xa)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000003c0)=[0x8000])
semop(r0, &(0x7f00000004c0)=[{0xe, 0x5022, 0x800}, {0x1, 0x7, 0x3000}, {0x0, 0x4, 0x1000}, {0x1, 0x8, 0x800}, {0x1, 0x3, 0x800}, {0x0, 0x0, 0x1000}], 0x6)
semop(r0, 0xffffffffffffffff, 0x4)
semctl$IPC_STAT(r0, 0x0, 0x2, &(0x7f0000000500)=""/12)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
lchown(&(0x7f0000000380)='./file0\x00', 0x0, 0x0)
getegid()
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000000c0)=[0x8000])
open$dir(&(0x7f00000001c0)='./file0\x00', 0x8000, 0x22)
getuid()
semctl$IPC_RMID(r0, 0x0, 0x0)
semget$private(0x0, 0x1, 0xb)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f00000004c0)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/205, 0xcd}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg$unix(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0}, 0x0)
sysctl$kern(&(0x7f0000001100)={0x1, 0x16}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x32}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='oL', 0x2}], 0x1, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000140)="74e201887d347d495fd8ac509b692e", 0xf}], 0x1)
pwritev(r0, &(0x7f0000000300)=[{&(0x7f0000000500)="cc4de153f2e6f87750862c1bf52c88", 0xf}], 0x1, 0x0)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000340)="83eca8fe3800aac72d4750a3422e", 0xe}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000018c0), 0x0, 0x0)
setuid(0xffffffffffffffff)
fchown(r0, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
dup2(r1, r0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="44911511a7508846142d"], 0x28}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={0x0}, 0x10, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r1, 0x0)
shutdown(r1, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r2)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0x2)
open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
readv(0xffffffffffffffff, &(0x7f00000002c0)=[{0x0}, {&(0x7f0000000140)=""/45, 0x2d}, {0x0}, {&(0x7f00000003c0)=""/4096, 0x1000}], 0x4)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x100, 0xffffffffffffffff)
sendmmsg(r0, &(0x7f0000000180)={0x0}, 0x10, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xb, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000002c0)=[{0x0}, {&(0x7f0000000300)="7a90bdeefb6c4765d4f4497552f6034d7ae2fd8d6b6177afc61c7a6ab86d67566d5d2dd022e81f190b04f2cdc14364a5c2d31999f6794a7378a4d47f6b01013d53d3967d3ffbf6884863ef275333fbb38b6edde7ad11e06cf40485baf0f743b1a30eac05db42579c0933211a8c156e92f3046359cf69359346b8fc925b3121350b443837", 0x84}], 0x2)
execve(0x0, 0x0, 0x0)
nanosleep(&(0x7f0000000000)={0xffffffffffffffff}, 0x0)
sysctl$hw(&(0x7f0000000080)={0x6, 0xb}, 0x3, &(0x7f0000000140)="42612f15", &(0x7f00000001c0)=0x74, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1e6e)
r0 = open(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000340)='./file0\x00', 0x0, 0x0)
close(r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000000))
munmap(&(0x7f0000000000/0x1000)=nil, 0x7f7ffdee8000)
setitimer(0x0, &(0x7f0000000000)={{}, {0xffffffff, 0x1}}, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x25}, 0x4, &(0x7f0000000040), 0x0, &(0x7f00000001c0), 0x0)
r0 = syz_open_pts()
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCSTAT(r1, 0x20007465, 0x0)
syz_open_pts()
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x26}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x483e)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x3fc000000})
setrlimit(0x0, &(0x7f0000000000)={0x1fe})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "a4c5c15b04e4ff00"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x1, &(0x7f0000000100), 0x10016)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x80000000, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
setsockopt(r3, 0x29, 0x3e, &(0x7f0000000080), 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r4 = openat$zero(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
fcntl$lock(r4, 0x9, &(0x7f0000000040)={0x2, 0x0, 0x0, 0x1000100000000, 0xffffffffffffffff})
r5 = openat$zero(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
fcntl$lock(r5, 0x9, &(0x7f0000000040)={0x2, 0x0, 0x0, 0x1000100000000, 0xffffffffffffffff})
dup2(r4, r5)
write(r1, 0x0, 0x0)
r0 = getpgrp()
setreuid(0xee00, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = getuid()
setpgid(0x0, 0x0)
setreuid(0xee00, r1)
setpgid(0x0, r0)
r2 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r2, 0x80047476, &(0x7f0000000040))
r3 = fcntl$getown(r2, 0x5)
ktrace(0x0, 0x1, 0x68, r3)
sysctl$net_inet_etherip(&(0x7f0000000180)={0x4, 0x2, 0xf0, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3b}, 0x2, &(0x7f00000001c0)="3a897ff7fdaa2cf46877a57480902955c7d1b6312e733c4aba1f410b4075587199f312481fc3f9e19d95cab1dcde776dc6f81fdf461b243909bccd0687cb7c9049ade6b7ba7959e07922fd1aad58889aa951d39e86b96579cf7e1d9d09a41ae581b83573cda4a6274178ebaa0b1f7e9741f28ac35484221539c85823d0b3d079d289b568f190ff97629338525d13dcd5e470fca24e52fa9e73e2bba9de24756cb1d18869c935d951da8d87545d16ff5b01efb55b5279cae833c42a9403005526e3c97be6cc09ca172216d771a46bac0fc2139c36d61e421e2d047d96e2b4da8fe70ca5785327daca4f49574aa521b6a0dd56b0555db54a26368bd0693f2aaee7de2364ace712a3294630caf0ee1c1f976017e58e0964973bdd7ef81e93175c3dee68bcf7c0c1ad78313eeb77a00254db873d84ba4279b8410cd4f6942b29e4af98f3d92eb6001a6d41f7d9bfb70145398cb823e222de6da5895edf722e336f71111d73111670d9eaad8c61df3a96eb481b5d75b850c67b759d4bf51ac828cdd5ff0a81921a6fe67d303caa6b60bb360f2c9d5ad97635dbdf764e8ec933f766bda5e043760853def5a7501fa50a93456f1aaa068b5f9f5984627fb19ea76c5e042b735d0fc10393292b19696c07c035e34dc80b9370f0fa5d77e2a8dfcdd4e8a96dc1dec2262a29af43828ad875c9deccecbfd34fb2bfffe68ba0368d468d59dcfb140694d058ef54431b1f0d0233ceccaf57d9d7ef0e2565", &(0x7f0000000040)=0x218, &(0x7f0000000080), 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000100)=[{{r0}, 0xfffffffffffffffa, 0xb1}, {{}, 0xfffffffffffffffb}], 0x20, 0x0, 0xb2c, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000a00)={@local, @random="0f867b50059f", [], {@ipv6={0x86dd, {0x0, 0x6, "de7cac", 0x0, 0x2b, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2}}}})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206910, &(0x7f0000000300))
r0 = socket(0x2, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
write(r1, &(0x7f0000000300)="251486", 0x3)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt(r0, 0x6, 0x8, &(0x7f0000000240)="93fa0384", 0x4)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0/file0\x00'}, 0x10)
shutdown(r0, 0x1)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0xc}, 0x4, &(0x7f0000000480)="b56eb668", &(0x7f0000000340)=0x4, &(0x7f0000001480), 0x4)
sysctl$hw(&(0x7f0000000000)={0x6, 0x5}, 0x2, &(0x7f00000016c0), 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@random='wj$\v)V', @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @broadcast, {[@lsrr={0x83, 0x3}]}}, @icmp=@info_request={0x11}}}}})
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0xbcd8, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000200)="d8", 0x1}], 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000140)={0x7d8, 0x0, 0x9, 0x0, "d01fb9aef2c22a89b855228367ff0ddba3297d8d"})
execve(0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000029c0)="41312531d04433fa74e4e0e359c25918df44b48dd405f1f0343e4f4c186cb4ea6fa639cd0eef5627eab0a942fbd905235c4979d4094ae6dae464053dcb03f68cf91b1ae54dca0200f755d5bc044db6e20ae3a2ca922f1e5ddffcb13c2e8065c4dfb2af4b2c14d47858bba46c0142a0056b154e6503d255347b4c1218c6e27c4670fa6edbb4512af328f5ed91716901c50dedeb05dc19d9e2a3376b5815a7adb6d8af947c2e9c79b52642bc9e33322bb2e1e4d1838c51462d3600", 0xba}], 0x1)
execve(0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000003}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
poll(&(0x7f0000000100)=[{r1, 0x1}], 0x1, 0x98a3)
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000001c0)="f89176d2829b093eb403caa8d01cf66722f5e90172fd9b69c9821106627301330a521ddcddd26de2068d1143117b3d177333b20a3ab634390dfa6f9fe3577791a9afd1646eecfbfc43f1e21474336d4a355ff4ae06f86e999603", 0x5a}], 0x1)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r2=>0xffffffffffffffff})
dup2(r2, r0)
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000100))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000002c0)=""/187)
clock_gettime(0x0, &(0x7f0000000280))
clock_gettime(0x2, &(0x7f0000000440))
mknod(&(0x7f0000000180)='./bus/file0\x00', 0x8, 0xe2df)
clock_gettime(0x4, &(0x7f0000000200))
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x42)
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
open(&(0x7f0000000140)='./bus\x00', 0x8000, 0x8)
ktrace(&(0x7f0000000380)='./bus\x00', 0x5, 0x18, 0xffffffffffffffff)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000780))
r1 = semget$private(0x0, 0x4, 0x1f6b4e471c181030)
semop(0x0, &(0x7f0000000580)=[{0x1, 0x7f, 0x1000}, {0x0, 0x400, 0x3800}, {0x2, 0x9}, {0x5, 0x1, 0x1000}, {0x4, 0x8001}, {0x1, 0x6, 0x800}, {0x3, 0x6, 0x800}, {0x2, 0x1, 0x800}], 0x8)
semop(r1, &(0x7f0000000400)=[{0x1, 0x4, 0x1000}, {0x3, 0x0, 0x1000}, {0x2, 0x683, 0x1000}, {0x3, 0x4e25, 0x1c00}, {0x3, 0x9, 0x1000}, {0x2, 0x200, 0x1400}, {0x3, 0x367b, 0x800}, {0x0, 0xfff7}, {0x0, 0x47b2}, {0x1, 0x6, 0x1000}], 0xa)
execve(&(0x7f00000001c0)='./bus\x00', &(0x7f0000000680)=[&(0x7f0000000480)='+@\x00', &(0x7f00000007c0)='\xc8\xbf\xb0g\x0f\xb0\x92\x1d\xda\\\xf0\xe8\xc7\xd9hf\x02\x00\x00\x00\x00\x00\x00\x00\xba\xd9\xa0G \xd4CLf\x1d\xa4\xb2\x91\xf9\xb14\x12N\xf4\xae\x14\xe5\xa9\x1bw\x19\x18\xe6hrf\xb2\xc1\x12\x1b\t\xa1!\xe6\x191}\x12&\xa2\x90\xf4m\xb6\x1d<\x1e\xdbt?f&\xa3\xbc\xce\x7fR\xe0YJ\x00\xc5\xd6\xb4\xff\x90\xe5T{\xcc\xdc\xef\x16\x03\xd1~\x04*\x18\x02\x18\x1c\x01\xb3\xbd\xc1\xc8\xa8Cd\x94=~\xeb\xecB\xc8\xa1\xde\xae\xcf\t\xe8\xf5a\xd96\x94h_\xb1\xce\xc5\xe0\x82 \xe9\xb8\x86Mn1\xf6\xdbZ\xc2:\xcd\xf7\x99\x134\xd2\b\xe8\xd4h\x8fdm\xf0\xc8\x1bL},\f\b\xdb\x1cjv\xd3\xfe\x85\xcf\b@\xfe\x1e\xc4R\xfb\xa0\xd95\xf3Q=\x02\xf9_z\xd3\x87\xe1\xac\xc8w\f\xfa!KV\xa5c2\xdd\x8e\xca\xdb\xa6\xd7\x87\xe6d\b\f\x1eJ]\x97\x97k\x93zV*\xd5\x85\xf8\xb3}\xa8\x17\xd0!\x14q\xb7\xd0\xcb\xdf\x96\xb4`\xdf\xcd\xbcp\xd7\xe0V\xf6\xdc-J;v\xbf\x9a,\x11\xa9\xfe\x00m\xe9\xba\xabt{\xfd\xb7\x9c\xa2\xe1\xe9\xdfi\xeeb\xcc\xd8]&\xbd\xef\x854B\x950.\x84\xe2\xcc{H[\xcf\xaf\xb1O\xcb\x1f\xf6~\x18\x11\xcdzsm\xd4n\xf7?\x8e\xe4G\x15)[u\f[\av,\x00:S{\x13-\t9\xd4\xbb<G\xc5\x82\xee\xcb8\x95|\xb4f\x14s\x11\xdaPiS\xc5VL\xfc\x83\x05\t%_\x92', &(0x7f0000000500)='[/]!%!\x00', &(0x7f0000000540)='\x00', &(0x7f0000000940)=']+&&%/B\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00&]!\x00\xab\x9bb\t\x1d?\x87bu\x86\xb2\x12\xc2#\x0fRo9n\x82\x0e2\x05\xd2*$a\xb6\xcf/\xc0\t!\x12;N\xfb\x86\x16S\x9a3\xd4\xf0\xc7\xca\x15~\x13`\xc6\xa0,Y\xdd\x89\x8dk\xcd\xb38\xeb\vUT|j\xa3\x80\x03\xad\xd2z\xc6\xd7\xd0\x92\x82^\xa3\xae\xcb*\r\b\xc3\x03\x0e\xa4\xbd7\xcc\xa6m\x1e\xd4\xe0/\x02[$\xd2-\xfe\xdc7J\xf2hdx\xba.\xe4\x1b\x84H8\xdf\x94n\x8b\xf2u\xa1T\x18)HA\f\t\xf1]\xa0\xc8G\xc9\xa1\x02\x8fL\xa7pu\xe7\x8cI\x00\xe6\xe3\x18\xc1\xa2#\xd1\x99\"\f\xe2\aG\xf2\x1a&tk\xb7\tI\xdb\x96Dc\xda\ar\xa0g\xa2\xe2\xc1\xb1\xa5-\xe5\xf3n\x1d\xfc]\xd3', &(0x7f00000004c0)='@+/]@\x00', &(0x7f0000000600)='\x00', &(0x7f0000000640)='\x00'], &(0x7f0000000740)=[&(0x7f00000006c0)='{\x00', &(0x7f0000000700)='+,\x00'])
semctl$SETVAL(0x0, 0x4, 0x8, &(0x7f0000000240)=0x1003fd)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f00000000c0)=[{0x1, 0x7}, {}, {}, {}, {}, {0x1}, {0x3, 0x3d4, 0x1800}], 0x7)
r1 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r1, &(0x7f0000000480), 0xe)
r2 = getgid()
setsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0xffffffffffffffff, 0x0, r2}, 0xc)
r3 = getuid()
seteuid(r3)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000002c0)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000180)=0xc)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000001c0)={{0x8000, 0xffffffffffffffff, r2, r3, r4, 0x91, 0x1020}, 0xdc8d, 0x4, 0x7})
semctl$GETVAL(r1, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r1, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r1, &(0x7f0000000280), 0x0)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40}, 0x40, 0xfff, 0x9})
semop(r1, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x2, 0xc2f, 0x1000}, {0x2, 0x96e, 0x1800}, {0x0, 0x7}], 0x4)
semop(r1, &(0x7f0000000040), 0x0)
semop(r1, &(0x7f0000000040)=[{0x2, 0x0, 0x400}, {0x1, 0x21, 0x1000}, {0x2, 0x4, 0x1800}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(r1, &(0x7f0000000080)=[{0x2, 0x1000, 0x800}, {0x2, 0x8, 0x1000}, {0x4, 0x5, 0x800}, {0x4, 0xfffb, 0x1800}, {0x1, 0x4, 0x1000}, {0x1, 0x3, 0x800}, {0x3, 0x5, 0x1000}, {0x0, 0x7, 0x800}, {0x2, 0x7, 0x1000}, {0x2, 0x3e0}], 0xa)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240)=0x9)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f0000000280), 0x5, 0x0)
kevent(r1, &(0x7f0000000080)=[{{r0}, 0xfffffffffffffffe, 0x5}, {{r1}, 0xffffffffffffffff, 0xc1}], 0x4, 0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x35}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file1\x00', 0x1000, 0x0)
acct(&(0x7f0000000300)='./file1\x00')
open$dir(&(0x7f0000000040)='./file1\x00', 0xfd5085ea1734f92d, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, "d700060000000000005b00000000000000e74de4"})
poll(&(0x7f0000000000)=[{r0, 0x1}], 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x3, 0x0, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5fc82b297ba1ab5b23116730f144000000000000001f1306000000000000007d026ba8af63ff37282902", 0x62, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000502", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xd0584fcd7f7abe72)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000010206, 0x0)
setuid(0xee01)
writev(r0, &(0x7f00000003c0)=[{&(0x7f0000000000)="bc", 0x1}], 0x1)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
pwrite(r0, &(0x7f0000000000)='<', 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020699b, &(0x7f0000000300))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
socket(0x0, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0xffffffff, "020000040000000000000000aa00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x401, 0x536, "a03da5c75c686c9dc5239288fe7d834a63750fa0"})
ioctl$TIOCSTOP(r0, 0x2000746f)
writev(r0, &(0x7f0000000480)=[{&(0x7f0000003600)="5ba38b79b82ae150f112194594abb8531e35fff706716e484be13f20ab60d4090ccd7ba2b43b2156dd5467a79b6babe5f66be3fc1190e30603ba36daca7dc43c0579abe2ab8b2e83", 0x48}], 0x1)
open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x4}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{}, {0x2d}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@broadcast, @remote, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @random="272c59a1e933", "af2819d4c2e49bc6e965a2e406a9d1f0"}}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
recvmmsg(r0, 0x0, 0x0, 0x0, &(0x7f0000000680))
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x8776dc0573891baf, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r1 = dup(r0)
pipe2(&(0x7f0000000000)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
writev(r2, &(0x7f0000000280)=[{&(0x7f0000000080)="1f", 0x4000}], 0x1)
poll(&(0x7f00000002c0)=[{r1}, {r1, 0x40}], 0x2, 0x0)
write(r2, &(0x7f00000000c0)="95", 0xfffffe76)
dup2(r1, r2)
execve(0x0, 0x0, 0x0)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
pledge(0x0, &(0x7f00000000c0)='vmm ')
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
getgroups(0x7, &(0x7f0000000080)=[<r1=>0x0, <r2=>0xffffffffffffffff, 0x0, <r3=>0xffffffffffffffff, <r4=>0x0, 0x0, 0xffffffffffffffff])
setgroups(0x0, 0x0)
chown(&(0x7f00000000c0)='./file0\x00', 0x0, r4)
setuid(0xee01)
r5 = socket$unix(0x1, 0x2, 0x0)
r6 = semget$private(0x0, 0x1, 0x440)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000015c0)={0xffffffffffffffff, <r7=>0xffffffffffffffff})
getsockopt$sock_cred(r7, 0xffff, 0x1022, &(0x7f0000000340)={0x0, 0x0, <r8=>0x0}, &(0x7f0000000380)=0x9)
setregid(r8, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000100)={{0x989, 0x0, 0x0, 0x0, 0x0, 0x152, 0x69}, 0x9, 0x1, 0x8})
getsockopt$sock_cred(r5, 0xffff, 0x1022, &(0x7f00000002c0)={0x0, <r9=>0x0}, &(0x7f0000000300)=0xc)
fchownat(0xffffffffffffffff, &(0x7f0000000000)='./bus\x00', r9, 0x0, 0x4)
getgroups(0x3, &(0x7f0000000280)=[r3, <r10=>r2, r1])
chown(&(0x7f0000000200)='./file0\x00', r9, r10)
semctl$IPC_SET(r6, 0x0, 0x1, &(0x7f0000000180)={{0x5, 0xffffffffffffffff, r1, 0xffffffffffffffff, r8, 0xc8, 0x9}, 0x7fffffff, 0xc5e, 0x5})
sendmsg$unix(r5, &(0x7f0000001480)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
sysctl$net_inet_divert(&(0x7f0000000000)={0x4, 0x2, 0x32}, 0x7, &(0x7f0000000540)="318bf81462a7699360c57e024c0d06af66b987709cdd8c7ff36837178889677d3163154849073a237e0ee24d12404120bc59e783f77a1a1ca6a8706b5b4b849d1cd0f0ae6c9b7d2a39417e0a8c4e4526070dc418174512eea4fbe7572bb684746df6409a8120c6822a3d82a9a4bf032ab1d65e69da6b6585b95bcce8a743e643b0d57b9a25b269ef4a7e7400000000000000000000009fc2a4c935154ae9d6a39f2b40d8a6d179c23529dc2b39c8895344969a38ac4e54aa0761f2ae73fba46966d04a03d0f5954ac08e22a169618c5e8f2579bde6d6a7c88faba4f01d7e4cbbb631bb1c917c76bc14380b7feede92826b1c3c145511d301e4aae81f9c23973d95b032f6aea63476947ded192903372245086deb566979b592dd0d58a31a50638163b8ea2875fb014a7437fe2013cafbb657e12c8d967ab7250533e31dcae98086b7a1022f1a099147889180e0ee74001c67a20629c89969d6438094e9922c60f0442726835327515d2bcaca5c3949b65a0608b6978db92f603dd6c8a0372a52f2ad154e0258aa4d2ecf94709eb3dfd3812c28394077f00dcc07361539cd4812797a38db78d6cc7c18930a0b1848b776a9f13701f34d710017003ca11b698ca6df27b70fa6affd934298ce34059aa030355459680078fccc0114d520980530d64bb31674295fb3172792110f47e8f7c0a3929ec0589b2d28584e87c80a482815ad375e3f157d9c1f01c2d21591b5f7027a0a6faa77ea3cdd78dd6c38e12b737b65fab50f2ddf9f2a090000000000000000000000000000c5490b34940a22980f7ad23ff5e4954a266e08b1220b0e016932188c5376d495ba8b82e4b2134e3c96b3bc114e9f6a9ea239b73a3a4b07908e56d3b36116816d2611ec8bf4db0da49278b2d8bed1b6635125be6e1447361da9d9d9bf7c6057874a303b68e2a2ae1208daee7b740f7a8afed57ad91ba8acbee9edd6b991f39e6ed60ef17d89684588e847d21875cdd484c07ae3323fb3d4fb2ab6a89ff0c164b1d73251a50a08d67bef1a9297866075", &(0x7f0000000080)=0x255, &(0x7f00000000c0)="d58daad30e65c51ada5e15d6ec74f5f252e409f95a1382b75a73d17835e0ac7de5570ea928f15da3052b8a5900025677e7e6fcde3cb9ae69785dae5749423a593da3149bfb7a1328ac0c9504368bfc277b6ff179d9b69afd38028f6b4d0c32eae4f1e6d05b1d901519db49b21355ba7619e8b4dae22e6b3f360b18388fdcd4573459f7d850804c47cf4747cec4bb8352779e62b553450a264e9a54439b7518878c59ed0fa3", 0x9a)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f0000000040)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ff5000/0x1000)=nil, 0x10000}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ff5000/0x1000)=nil, 0x8000000000000000}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ff6000/0x3000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ff4000/0x2000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ff8000/0x1000)=nil, &(0x7f0000ff5000/0x1000)=nil}, {&(0x7f0000ff8000/0x4000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ff7000/0x3000)=nil, &(0x7f0000ff8000/0x3000)=nil}, {&(0x7f0000ff4000/0x4000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ffa000/0x1000)=nil, &(0x7f0000ff5000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000fff000/0x1000)=nil}], ['.\x00', './file0\x00', '\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSKBDIO_GETENCODING(r0, 0x4004570f, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x18000, 0x0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x3}]}}})
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
writev(0xffffffffffffffff, &(0x7f00000006c0)=[{&(0x7f00000005c0)="7a13424e09da91716845e5ff937aa6d82e2e2e9204df3b1b68711113358dc6b07bd29278c3a3510945e2034d5a7436059dc32db511936a4395f5f7c5126a4455d1c51b63b39d3ec91f32bb6c709de19a6d28484b0cc948d38faf20d4e431b0b781ab4d6e4efcecb2435b53fdc709140afa7cc931cedd01c68a01fb943151d532aa954a9eca94c9565e33fc17d8944ab70a2b6bbb2b45e65defaeeb118faa9c4461a95ceb6fe7e85ccd207b3a57cd95f6a0d81893b6a911b48088319a49f927e68ab8803655f07d6e96c462ac", 0xcc}], 0x1)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000040)={&(0x7f0000000000)=[{0x2}], 0x1})
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000240)=0xc)
lchown(&(0x7f00000001c0)='./file0/file0\x00', 0x0, r3)
r4 = getuid()
seteuid(r4)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0xd4)
rename(&(0x7f00000004c0)='./file0/file1\x00', &(0x7f0000000500)='./file0/file0\x00')
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
r1 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0, 0x0)
open$dir(0x0, 0x0, 0x8)
r2 = socket(0x18, 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000002f00)={&(0x7f0000002740)=@file={0x1, './file0\x00'}, 0xa, &(0x7f0000002780)=[{&(0x7f0000002f40)="6c5f46dd54860e09e012ba71ab4b271056885c1aedf5777d46d1bd46af02b59f53cb678db74b0f77cb8018f75b7ce09e5eefb061581eb6dd0329aad347863be852ba7750bb06ab252bd7109db4a0e5c586e23c3d61889d2cc8bfd9f8e4f7cd2bacb64daf0e7edaa87023b110671f605343d2eb00001c66b5696647cb266498aabb0d9d7255240c42d4febd162358e6cd9e917ee06b87ed34fec67b6f9e534fe1cb109b5840ff4d9802e329cfc146d2b094827f49b6fa7c0c1c9994f6b59b6fb07f206b7bde29ff156d52", 0xca}, {&(0x7f0000002880)="49ad68dd56816db58872c519afb801968ce91599311af6940d953073f5226b10f3431519195fa6aec6014fb1e31996fa6c1690d0dad7c772cd4e7730414e86829aceb3d732b9eba10a1b0e8324e688547d2ec2dc91d2f5d18108df9a0f316a519a1393376e2fd15c5fe989efe7e7a8378fe1d400f7b37783c2bdcf4a30543e5cecb43d8cbcd320d4257c9a62dbb96a7c6375da45d4b6f2d9c7025ce8c687291165a11b1d63de9b622b747bba6eaa240e", 0xb0}, {&(0x7f0000002940)="3fc6af22d258133e0478188471cd482c0db69eaf6751442883fa91c533cb88f1af8699d8432355c0b06a84d8ea1062ec48a73e57d9c2dbb848d4a0b2c4e93ffa44d82fc537d84ac6a24c7f1ac81b28984232e54b290c3a0beed2de6c449ffce13f067a5756a7916fb4a880768366f03d5b1d", 0x72}, {0x0}, {&(0x7f0000002800)="6c334e41cd1e935bfda7c40099bd2974b9baae64", 0x14}, {&(0x7f0000002c80)="7928ca1fb9c3adf31695329144e2b44c6687e41d89b695226a9f246380dc1a15a566c3361343a95e4de893ac450b3c54a0aeb97385143b86685e9cdbf8cb87971b4ee46bbdd867fe07baefd94afaa467ac9658204b61b0fa58403d5668d96ef3d99de3bdb91dbe876f47e697b6cc2af7171965d03b1e5a36d2683fcf02f87c865a72fb136c1ed59391be81ca93b06e1bacc6c8e68375d27147def193f27cf5219760d5eeec4ea6406fd09475", 0xac}], 0x6, &(0x7f0000002ec0)=ANY=[@ANYBLOB, @ANYRES32=r2, @ANYRES32, @ANYRES32, @ANYRES32], 0x28, 0x408}, 0x6)
socket(0x11, 0x4003, 0x0)
ktrace(&(0x7f0000000100)='./file0\x00', 0x4, 0x100, r1)
recvmsg(r0, &(0x7f0000000000)={&(0x7f0000000580)=@in6, 0x0, &(0x7f0000000500)=[{&(0x7f00000001c0)=""/197}, {&(0x7f0000000680)=""/4096}, {&(0x7f00000002c0)=""/195}, {&(0x7f0000001680)=""/4096}, {&(0x7f00000003c0)=""/243}, {&(0x7f00000004c0)=""/52}], 0x10000000000000f2, 0x0, 0xfffffffffffffe3e}, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000000c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
munmap(&(0x7f0000001000/0x3000)=nil, 0x3000)
sysctl$net_inet_tcp(&(0x7f00000001c0)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, &(0x7f0000001400), 0x210)
sysctl$kern(&(0x7f0000000080)={0x1, 0x42}, 0x4000000000000003, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
r1 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000810, r1)
r2 = socket(0x11, 0x0, 0x0)
sendto$unix(r2, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37281002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000000)={&(0x7f0000000140)=@in6, 0x0, &(0x7f0000000180), 0x1000000000000264, 0x0, 0x26a}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x30}, {0x61}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9e9bd555d00000000000008000", 0xe)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x100, &(0x7f0000000000)=0x4, 0x4)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x1021, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000002440)=[{&(0x7f0000000440)="96e77608475e7e9413", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc450444d, &(0x7f0000000000))
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x55}, 0x3, 0x0, 0x0, 0x0, 0xd)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000000)="ea00005c00000700", 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000300)=[{0x2c}, {0x7}, {0x6, 0x0, 0x0, 0xfc}]})
pwrite(r0, &(0x7f0000000040)="e6d9a21f0478818bcfbbe1ad54dd", 0xe, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x123)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0187009, &(0x7f0000000380)={{}, 0x0, 0xc6000000})
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000001080), 0x0, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f00000020c0)={&(0x7f00000010c0)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00'})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x7})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3d}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
openat$null(0xffffffffffffff9c, &(0x7f00000009c0), 0x0, 0x0)
clock_gettime(0x5, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x81}, {0x25}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x4}, {0x20}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000280)={@local, @random="090e81537f08", [], {@ipv6={0x86dd, {0x0, 0x6, "958286", 0x10, 0x0, 0x0, @loopback={0x4}, @local={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x2c, 0x0, 0x5}, @dstopts]}}}}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x4000000000000004, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070003000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x3414, 0x0, 0xffffff3f)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x80], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000], [0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff], [], [{}, {}, {}, {}, {}, {}, {0x0, 0x0, 0x0, 0x1}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x18, 0x4001, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100a56700009eff03000000", 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cffffff"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a", 0x8b}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818", 0xb6}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = open$dir(&(0x7f00000006c0)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f00000001c0)='./file1\x00', 0x18278, 0x0)
select(0x40, &(0x7f00000000c0), &(0x7f0000000100)={0x3ff}, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x80000000000206, 0x0)
rename(&(0x7f0000000040)='./file1\x00', &(0x7f0000000140)='./file0\x00')
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000640)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0dee", 0xc1}, {&(0x7f0000000080)="a5781d1feeaee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0084463, &(0x7f0000000140))
socket(0x1, 0x0, 0x1)
mlock(&(0x7f0000fee000/0x12000)=nil, 0x12000)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
chmod(&(0x7f0000000180)='./file0\x00', 0x0)
unveil(&(0x7f0000000280)='./file0/file0\x00', &(0x7f00000002c0)='c\x00')
r0 = syz_open_pts()
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000040)={0x20, 0xfffffffe})
sysctl$machdep(&(0x7f0000000040)={0x7, 0x1}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x8001, 0x0)
close(r0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
connect$unix(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="9502640d2a"], 0x10)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000080)="0c52acb468f0b91b", 0x8}], 0x1)
open$dir(&(0x7f00000006c0)='./file0\x00', 0x200, 0x0)
chroot(&(0x7f0000000e80)='./file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x25}, {0xc0}, {0x6, 0x0, 0x0, 0xfb}]})
pwrite(r0, &(0x7f0000000300)="e92db3b5880200ac840353a10700", 0xe, 0x0)
r0 = syz_open_pts()
syz_open_pts()
pipe(&(0x7f0000000000))
syz_open_pts()
select(0x40, &(0x7f0000000080)={0xfffffffffffffffe}, 0x0, 0x0, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202c17e7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000001480), 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet6(r2, &(0x7f0000000300)="94", 0x1, 0x0, 0x0, 0x0)
r3 = accept$unix(r1, 0x0, 0x0)
write(r3, &(0x7f0000000240)="ddee43ab8734abe72e8289e48f848dcae6855abde176689fd16e6ad78a4f273bc1d2e3f3f911c89a2472d11399504870b9fb0e36d8892e80897800000000d20a2c82125431700f50a6b8d22db0ec0ff3a1800280b535084f92a7499cd07afbd1d0b82c439984f338ffbf66decf2ba302619dc2dcef33b15aa3d89beb2b70ddbd84311f09a2639749e5f4c3669169eb4be5a922395a28149f4d2ec0588916c297ac43adfbb5775ab949938e2baef7e6fa3703487dc7cd0255926c0ec9", 0xfffffea5)
execve(0x0, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000080)='./file0/file0\x00')
renameat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0/file0/..\x00', 0xffffffffffffff9c, &(0x7f0000000300)='./file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x35}, {0x81}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x25}, 0x4, 0x0, 0x0, &(0x7f00000001c0), 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
connect$inet(r0, &(0x7f0000000000)={0x2, 0x3}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f0000000080)=0xc)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
select(0x40, &(0x7f0000000380), 0x0, &(0x7f0000000400)={0x3ff}, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "238d9cf565a85eaf0e50d1fc73fe6a219b4c1a04"})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000180), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206916, &(0x7f0000000300))
write(0xffffffffffffffff, &(0x7f0000000100), 0x0)
socket(0x0, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="92022e2fac14"], 0x10)
writev(r0, &(0x7f0000000040)=[{0x0}], 0x1)
listen(r0, 0x570)
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)="6b57530e1cd9ff3da25e9a26bd64c21f4c16cae606db08c50023cd08711b8b5a9187450023f7792369f3197548b8ac91a99e9121a1d42a814135edcbea5237f892c289668b1fa05bc0", 0x49}, {&(0x7f0000000180)="b84cc5e5bd380cbbbd18f93b5d25995bc64a33b246d0c1f53cfb5fa230940ec66f1d5ff21079335719bd3dee5046b8af2a8a7481ddbf8177565967692fb17668e016687e6b0ea921050ec5de16844120375c1a250cfc44c8e7b8e43bf48950d8a4dbb0b50fc0d6369cf32b4dd6a4aa44b1be2707fb251de59c813cc61b344ce2e6abfc4a28811f8ac382eee1a13f313676cd5891af5b6c4e63cb49b439c5e09ad5bdd25c", 0xa4}, {&(0x7f00000006c0)="9bdbb1612aa65955ed697d1eee4bfdfd0652c604ccf632978c0bdd9b23d4f7cb725fd749c7ca91d4429152a2b4086dd3c676919a771884bda610515957c168116613531421c884583ae32d60df0e6b58a41a0a8ba1cfd66c54032f76065e65dccf4d82373f2444dfe875f7dbe866a184c8f8b9636ba53e3ecacc5ce6a105ae5f1e75939a1ee84df905449f78349bcfa3c2d72edc1a1bc5a7c8123abd228906216af11066d06d84d113b41ade9957f58d12fe26d505412aa740d50cfda757fcbd5bcfc72b89e7cbe021b67d440ecb1e8b2e1caf97825cac1a2ec7f9bf26f410f4332bdc94edb6d17b48a5acdc4c16570f987ed5c0c3662f61cb4f56c6709790240ca3ee07aed398bca3fecd365cddf6bf7fc96789b629bbf9ce460f58857941af1bc9871c7c0b2580d5ae79a53e2ac144d33d5e5a80f1bec00b90f6a53c2ca7aa9e732c88a8e280edc4d233247b5bec1b21c505b1c2ca23ee651d4f1c2e99fcf3e2f78562deafecaf6c91a58d112d1cca9eef999802252b1fe8aeed1aa8f0d8bc0f238941a96bc936b7ed365000164e660baf1f312b408921238685d466ff8521dabb69f06680a6e54811d28fd5fca20682193210d9327d288a5f79ff9cd164b04a2af762322adb866d8b08be437818f1fa572f695c1df692503f16705588fe76a4ec599ee7c8155b44b83c7a9de576884b8d9345c49f2d395c5f0d2b475bbf7815a744b3d747a85098276a7cd26cdb590cc48729fc372471607eaf82461db32c5c72f5b5605bd300ab8d6d1753ab96274b9d054e38b158076680abc3cec0494b08562b40fcbf22f537d8e888549799f60e69bce0c26dc31ac281a48f25d95288f7d2262f571c1f1158e23c3e682c960346a9fc5258104679bb4a2dc96108f98e2a7639616393f96eb03b50a12316c49e33a0bd01592e28704708478d126828b47a5fc440f9af99c40550e765e2d2572a3be5460ac03cedfae87788d9ee3bd97153045171351d11659e813a61663d562ca8038e63077168a552ca72aac4f1d837678df14e52aa16fcd6efb84a0b242bc14dd2c973b9ac70dd6d7f765e04a306e07a5bf463b2487040ed763da7823ce8d2773b7c49f501efe925a42411d3cf2a3729d3f01111c7184628dec3be4d64a8e1480b0a77110c7502c8a54c930c649fca532731f3040a2ca0ad135cf8d925d0e2cadd16efa72c21ed458d9f581fe5bca599fc716bb45b2967188fb8016c1b99d1525e5cdd591315ee58683bc1b7a9dbb2cccec6795d7d901c2734ef6cf4224ccccb89d1ba52225305fea6e08ccecac062125f2966e3d91bed427a2dc2e8fe84497a8010870b1d9efee1546d15fedfc53a21c14724b9adec598c8e0499060a057761ed9d5397603cfa138b9ce3523ff96550324537cfd1a82092881a72c01258e4e687bf1d8edbcc3a835bc31f4a9145067cb6c32ff45f92b8ef400f4b5ba6a989faac6cce952f71fb9f6776d979e6ed32fc6f9eaf74d0885c742e63688aebc1248e583c4d2e085969863f4e18474bdb4ccda1aaf1402768babb0420e97982edeb58e66df692314389ffba3ea453999ee5890f90ee364f7cb915af0972c5fb3ec86921266da3d9cd8ae6c812fe1e2047f5", 0x480}, {&(0x7f0000001240)="4855bcc1e3728613240267aab3924e28e3d8cb39ba0c5fe14d76879d4317023d869acdb967c60463c309a31a7547e65d057ed0a8ec534823279b9979a4f80e00e982a07cd83a00873d3d2e7e8ff0c3abf2a8eb52", 0x54}], 0x4)
sysctl$net_inet_tcp(&(0x7f0000000080)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f00000000c0)="b7fadcaf6c38a7e3a81a6aa067ff707f09b7c968009963c8592502f8e088abca4e14b1789b646ecb7c1069abffeb8d10967b9cd4b57da064dc51ecd7279e8ddc73e44e27effbb34709f3d7accffe7c1b32fc6ca40b97d968b5b39b93ccee20cca5c7878e5dc1def5ff7389bb84b630e77ea49f5a5d32c15edec86a508e39792730884385441da30ee1f950ec17f53bccff61272c748a85e0fdd71cbbd294ea7fc40cdddd4797bf047ecdf00c40cd3262e643b189d71b705e6b59b47ea77a8f8e29c1d61b78c7ae3748029d70fcee639ae1da93a1efc7d72f5f3b394caeeb3d96f28cde2085a55e47697ca9986c9504362a0499e6df26fd25c97a37b35970a6c085ead8d16e27fdfd41a48524f4e8f28a9ae7af54538dc5b51b5a96db2e5d1b6a9b003d7aaf5cea4d17871752712df882fa79a7e1b29ead906d477c3606807070a8d03509438e5337a2080dde5105fdd4ec58ff4dc39d850dbc2f602a4f9bcda6ba551dd5963951a8ccc19ab6cbf667a31775dc1f52aaf838d6b79a373176e007c27a0c3ac81308293ae3212bf8d5993df3b70acb908b1ca66c0f0d591af08f0b871a9ec0af2e0785f8ab3ffbd3b27cab844dcddce06b33d3ce4751df60c329bfbd5a8ae0a183196937a013ce93d52f6a3d01cd4d5d02bd32dc1538d01329ace45a370a772fa428ab4a0118049ebc61602876b093166826985d196e6d31bbe8e5da47c1ce0ef8b5b739f15c7c8c0fcd05", &(0x7f00000010c0)=0x210, 0x0, 0x0)
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x450a)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
poll(&(0x7f0000000440)=[{r0, 0x40}], 0x1, 0x0)
read(r0, &(0x7f0000000480)=""/4096, 0x1000)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
r0 = socket$inet(0x2, 0x1, 0x0)
shutdown(r0, 0x1)
setsockopt$sock_int(r0, 0xffff, 0x2000, &(0x7f0000000000), 0x4)
mknod(&(0x7f0000000880)='./file0\x00', 0x2f00, 0x0)
lstat(&(0x7f0000000000)='./file0\x00', 0xffffffffffffffff)
r0 = accept$inet6(0xffffffffffffffff, &(0x7f0000000200), &(0x7f0000000140)=0xc)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000080)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000000)=0xc)
r2 = getuid()
r3 = semget$private(0x0, 0x4000000009, 0x82)
semop(r3, &(0x7f00000000c0)=[{0x0, 0x1, 0x800}, {0x1, 0x1, 0x1000}], 0x2)
semctl$SETALL(r3, 0x0, 0x9, &(0x7f0000000100)=[0x6, 0x2000])
semop(0x0, &(0x7f00000000c0), 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000300)={{0x9, 0x0, r1, r2, 0xffffffffffffffff, 0x1c8, 0x5000}, 0x8f800000000003, 0x8, 0x8})
r4 = getuid()
r5 = semget$private(0x0, 0x1, 0x82)
semop(r5, &(0x7f00000000c0)=[{0x0, 0x1, 0x800}, {0x1, 0x1, 0x1000}], 0x2)
semctl$SETALL(r5, 0x0, 0x9, &(0x7f0000000100)=[0x6, 0x2000])
semop(r5, &(0x7f00000000c0), 0x0)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000300)={{0x101, 0x0, 0x0, r4, 0x0, 0x58, 0x5000}, 0x7ffe, 0x1000000, 0x8})
semctl$GETNCNT(r5, 0x4, 0x3, &(0x7f0000000380)=""/197)
setreuid(r2, 0x0)
r6 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(r6, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x40}, {0x61}, {0x6, 0x0, 0x0, 0xdfffffff}]})
ioctl$BIOCSETIF(r6, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r6, &(0x7f0000000140)="6d9ef757a944e00af2e26510358b", 0xe, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
dup2(r0, r3)
ioctl$TIOCFLUSH(r3, 0x80206979, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000480)=ANY=[@ANYBLOB="2c012e2f66696c6530"], 0xa)
dup2(r1, r0)
r2 = socket(0x800000018, 0x1, 0x0)
listen(r2, 0x0)
r3 = dup2(r2, r2)
accept$inet6(r3, 0x0, 0x0)
dup2(r0, r3)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r4=>0xffffffffffffffff, <r5=>0xffffffffffffffff})
dup2(r5, r4)
connect$unix(r5, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
execve(0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000002340)=[{&(0x7f0000000180)=""/4096, 0xfd9}, {&(0x7f0000000040)=""/11, 0xb}, {&(0x7f0000000100)=""/41, 0x29}, {&(0x7f0000001180)=""/149, 0x214}, {&(0x7f0000001240)=""/4096, 0x1000}, {&(0x7f00000023c0)=""/211, 0xfde3}], 0x6, 0x0)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0x0, r1)
r2 = openat(0xffffffffffffff9c, &(0x7f0000000040)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/\x00', r2, &(0x7f0000d06ff8)='./file0\x00')
chflags(&(0x7f0000000000)='./file0\x00', 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000240)="8644b784a3f9e8fcc6c66322ebf410c1ff81de0b0cd9b1c6b61382f5b94c2147b56f7a0c164229b0b9a3ca4cfeaf132b561cbc0419", 0x35)
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x5, 0xfffffffd, "cfe9691e70058333f34171bbea9b5144b04424a9"})
readv(r1, &(0x7f0000000100)=[{&(0x7f00000015c0)=""/102396, 0x18ffc}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000400)=[{0x0}], 0x1)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000000))
mprotect(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x1)
shmat(0x0, &(0x7f0000ffc000/0x4000)=nil, 0x6000)
minherit(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x1)
r0 = shmget$private(0x0, 0x3000, 0x0, &(0x7f0000ffa000/0x3000)=nil)
clock_getres(0x4, &(0x7f0000000000))
mmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x0, 0x812, 0xffffffffffffffff, 0x2)
shmctl$SHM_UNLOCK(r0, 0x4)
r1 = getgid()
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={<r2=>0x0}, 0xc)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000080)={<r3=>0x0, <r4=>0x0}, 0xc)
shmctl$IPC_SET(r0, 0x1, &(0x7f00000000c0)={{0xfffffffe, 0xffffffffffffffff, 0x0, 0x0, r1, 0x186, 0xe0}, 0xfffffffc, 0x4, r2, r3, 0x10000, 0x0, 0x9})
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r5=>0x0}, 0xc)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000180)={0x0, <r6=>0x0, <r7=>0x0}, 0xc)
shmctl$IPC_SET(r0, 0x1, &(0x7f00000001c0)={{0x8001, r4, r5, r6, r1, 0x9f, 0x9}, 0xfffffff8, 0x3f, r3, r2, 0x9, 0x3, 0x4ec})
fchownat(0xffffffffffffffff, &(0x7f0000000240)='./file0\x00', r6, r7, 0xe)
openat$pci(0xffffffffffffff9c, &(0x7f0000000280), 0x2, 0x0)
sysctl$vm_swapencrypt(&(0x7f00000011c0)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x18}, 0x2, &(0x7f0000000040)="d8619de9", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x4)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x200, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000002000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000003000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000000040), 0xfeea)
munmap(&(0x7f0000000000/0x8000)=nil, 0x8000)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001640)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001780)=ANY=[@ANYBLOB="100000004000000000000000000000008b1fee47223a4eecc337db1731a13f768ea819cc619344214f3fbaf0e765e878"], 0x10}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000480)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000001500)={0x0}, 0x10, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
getsockopt(r2, 0x0, 0x9, 0x0, 0x0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x1, 0x0, 0x0, 0x80}, {0x4c}, {0x16}]})
pwrite(r1, &(0x7f0000000240)="e16914f6357e3a00f40015000000", 0xe, 0x0)
fcntl$getown(r1, 0x5)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x4, &(0x7f00000000c0)=[{0x2c, 0x0, 0x8}, {0x2c}, {0x6, 0x81, 0x0, 0xfffffffe}, {0x96}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSHDRCMPLT(r1, 0x80044275, &(0x7f0000000180)=0x5)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x1, 0x0, 0x0, 0x80}, {0x4c}, {0x16}]})
pwrite(r3, &(0x7f0000000240)="e16914f6357e3a00f40015000000", 0xe, 0x0)
r0 = shmget$private(0x0, 0x2000, 0x0, &(0x7f0000ffc000/0x2000)=nil)
setreuid(0xee00, 0x0)
r1 = getuid()
setreuid(0x0, r1)
shmctl$IPC_STAT(r0, 0x2, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc110445f, &(0x7f0000000240)=0x1fd8a)
r0 = kqueue()
kevent(r0, &(0x7f0000000280)=[{}, {}, {}, {}, {{}, 0x0, 0x0, 0x2}], 0x14, &(0x7f00000001c0), 0x7, 0x0)
kevent(r0, &(0x7f0000000000), 0x899, &(0x7f0000000400), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x84}, {0x2}, {0x6, 0x0, 0x0, 0x7ff}]})
pwrite(r1, &(0x7f0000000180)="0ba1ca0cb64c8023cb5de762d8ff", 0xe, 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0xb}, 0x3, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@random='wj$\v)V', @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @broadcast, {[@lsrr={0x83, 0x3}]}}, @icmp=@info_request={0xe}}}}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0xff})
sysctl$hw(&(0x7f0000000000)={0x4, 0x18}, 0x8, &(0x7f0000000040), 0x0, 0x0, 0x4e)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="110000002900000031"], 0x38}, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x2e, 0x0, 0x0)
getitimer(0x0, &(0x7f0000000340))
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x4c}, {0x60}, {0x6, 0x0, 0x0, 0x806b}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000000029, 0x200000033, 0x0, 0x170)
getsockopt(r0, 0x29, 0x23, 0x0, 0x0)
open$dir(&(0x7f0000000140)='./file0\x00', 0x200, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000ff0000/0xf000)=nil, 0xf000, 0x0, 0x811, r0, 0x0)
unlink(&(0x7f0000000040)='./file0\x00')
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
sendmsg$unix(r0, &(0x7f00000006c0)={0x0, 0x0, 0x0}, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
fcntl$lock(r0, 0x8, &(0x7f0000000000)={0x0, 0x2, 0x8f59, 0x2000300000000, 0xffffffffffffffff})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x40000000000000, 0x100000001})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x1001, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0xa, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
pipe2(&(0x7f0000000680)={0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x10004)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000700)={0x1, &(0x7f00000006c0)=[{0x9, 0x1, 0x53, 0xff}]})
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r2 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
execve(&(0x7f0000000100)='./bus/file0\x00', &(0x7f00000004c0)=[&(0x7f00000001c0)='[\"\x00', &(0x7f00000002c0)='/%[\x00', &(0x7f0000000300)=',,{$+--{^\x00', &(0x7f0000000340)='\x00', &(0x7f0000000380)='$%\x00', &(0x7f00000003c0)='/dev/bpf\x00', &(0x7f0000000400)=':\xae\x00', &(0x7f0000000440)='\\\x00', &(0x7f0000000480)='\x00'], &(0x7f0000000640)=[&(0x7f0000000540)='[&\x00', &(0x7f0000000580)='#&@*+\x00', &(0x7f00000005c0)='/dev/bpf\x00', &(0x7f0000000600)=']-]\x00'])
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "00000400007be167000000683009459aec4ae201", 0x201})
write(r0, &(0x7f0000000240)="b1fb4ebfeb41969690fa0847471b197fa4bad25ae3c2e345a96c91bc9c75f916f951cace60bd88cfd9fb749b321b4273f77c751ec1dd01b4161335def548672ad8b1764c175f6c4c11a0fa7c13718bcfc98795c98f971625cc516e703388", 0x5e)
ioctl$TIOCFLUSH(r2, 0x82907003, &(0x7f0000000040)=0x2)
dup2(r0, r2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2}, {0x24}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)={@random="87d5772f329b", @broadcast})
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "b52ffdd83aaf31e348555dce91aa1159003dc2a3", 0x0, 0xfffffffc})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
close(r0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffff9c, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210, 0x0, 0x0, 0x0, 0x4]}, 0x3c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x2, 0x0, 0x0, 0x4}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x12}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x3, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000580)={'tap', 0x0})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000340)=[{&(0x7f0000000140)="2d80740490377706ed82a1bba52bb74afafd86958f27c50a5e25b9095e846e0fddc135ada439bedced5e452823ab37661a08b9f504000000926d41943a961bccd9879c98b10718fb2b000000006e1b58944dd16c2df783384b2988bdcaf4d4c25c08fd705875a7a57fc5d21e9f91748a121189c10ae64fbb421e53fc740ba0434504af2a3149018de4f1eea8b1d882ddc206d3280c6552da69573cea2f8978acd0fca90e5dcc44846684d050aa9d3e7ad51949cb8a0611509aff269b9ee4ee78cac32cd8e3ed2a9436", 0xc9}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x1, 0x10, r1, 0x0)
r2 = dup(r0)
r3 = dup2(r2, r2)
ioctl$BIOCGDLTLIST(r3, 0xc010427b, &(0x7f00000000c0)={0x0, 0x0})
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x27, &(0x7f00000000c0), 0x4)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000500)=[{&(0x7f0000000100)="f8d40ff5a3ab70f2a2944b33c50cc45caabacf5f51a44f87a96a27d8905ae08495f7b34de0380e67b3d26a9406cec5257337e8166fb13a0e67ee75bb4730558e3324e4af647dc3cecbd0c3ef26f342c532aa8f3541c683d4201ae3e9283c90b04767753b596bc6c4124e55650047bc1d1a5937101fac4c44d1d182004b18fe2340a5162a5e689c5ec8e8c534e80a4b81c694fd1df0a69fd73897add4a9c58beebfa579894c5b16290033", 0xaa}, {&(0x7f0000000340)="2ee8133db6895a73feca84d63e2e4fc929ae7923c178dbb01f11531123f8e228e17d0d33", 0x24}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000080)=[{0x101, 0x31e}, {}], 0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x54}, {0x5}, {0x6, 0x0, 0x0, 0x7f}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010c8e7d00000000ddf500"})
r1 = socket(0x18, 0x1, 0x0)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
listen(r2, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x30}, {0x50}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
mkdir(&(0x7f0000000040)='./file0\x00', 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x0, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000280)={0x0, 0x2, 0x7fffffffffffffff, 0x100000000, 0xffffffffffffffff})
nanosleep(&(0x7f0000000000)={0x8}, &(0x7f0000000040))
execve(0x0, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000000})
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
close(r2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504441, &(0x7f0000000240))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc450443c, &(0x7f0000000740)={0x0, 0x0, 0x0, 0x5})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x5}, {0x74}, {0x6, 0x0, 0x0, 0x80000001}]})
pwrite(r0, &(0x7f0000000080)="d98d651030b36222441b58ae835d", 0xe, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504446, &(0x7f0000000000))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0x2]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x1, 0x2, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0x6)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000300)="0102fff67f0000024fb359657f1666819f97069815ca5835b6f65332127c991ab43a427a6555560ad8a048bb094573b887c6f49f43429b99897b40c8c7f4766c3bd9cabaddbd0066a5c0411d0be46ef42bec53dc089c5000000020a63fde260bcb6a157ad150ada66c878f486f7e59a59a05bb689915b9098089246fa85c22ad066d2bee0306023a66bcc45356c7ba884245783d9a0df740ec3c9eff495beb70bd17437528909283d73f252d74b0bd01000000201c5030e790e615745175bd902a5f48e0a013a1dcfc71c34fba6de7abc4c70f0bdd24244ade0d510672dd77da2c90fff2ec0000000000090000526da42f7f85b8e74bc972134ef6360047000000030000000000000000000000000000000009eb3881885647e6b9ecd6f6b37cb2be80e3d49c4287ed75b08a58f19f470bd87e5503c733fc217ed201005e8552cdd86bfa6da9edbd2d2d845b8e1f2e11180dc1a19bd5ff5256df19b563ef69e55e74120536a99d2a43575893f40002c34ad7a1d4dfedd535c233b41bc777c2d12749256ddf3343a02bf274a46145fc7f5d3082000000815e8214f857ebd1f1e41bfb9a21624806a96d9619e00feb108d5bb60a27d465014b6b918fcf5f4a46cb83eea6b48aeb604bc0955254edb0450200b270acdab70801fbc9960ebf7502767ebb569f48ec63072695e500000000000000d73700000000000000000000000000000000010000000069c69c7e6f68a6", &(0x7f0000000080)=0x210, 0x0, 0x0)
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
r0 = kqueue()
poll(&(0x7f0000000180)=[{r0, 0x1}], 0x1, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x2, 0x0, {[0x8000000000000001, 0xffffffffffff4ee6]}})
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000100)="529ac7ecc87315600a6543100f07c95df3287c82518c0461a37e5f30cd2e89292a66bd2d3cf7c41e0dd946a42f4a85799cc9f02756fb514a89579f01f401ffdd16017207f2a3d3c0b4d541dc3ece7e475b8a400c8a312464d18b87911540374731c15f2cb4bbb1563c17541a9658a656d3e478001cf220ceb66aa96f17d5386adaff35", 0x83}, {&(0x7f00000001c0)="52474183a9a2d7b4647d191e127b82407d98c3cd48b6ddf7", 0x18}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080), 0xe0)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80286989, &(0x7f0000000300))
syz_emit_ethernet(0x66, &(0x7f0000000580)={@random="a36c26d03df4", @random="f1586c5b2213", [], {@ipv6={0x86dd, {0x0, 0x6, "b42c08", 0x30, 0x2b, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@dest_unreach={0x1, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "96530d", 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @loopback}}}}}}})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f00000001c0)="0002f350d37c45941a3c212047eac30700b5433b483534a185cd98917be6019ba00efd4e3e363ba6ee0a2305c66e936573041a0d6d3a0f63dd6d879130a347b4f3", 0x41}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5", 0x8f}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080))
pipe(&(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
poll(&(0x7f0000000040)=[{r1}], 0x1, 0x0)
readv(r0, &(0x7f0000000440)=[{&(0x7f0000000180)=""/166, 0xa6}], 0x1)
close(r1)
pipe(&(0x7f0000000000)={<r2=>0xffffffffffffffff})
poll(&(0x7f00000000c0)=[{r2}], 0x1, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000002000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x4}, {0x48}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="2d00c784be3f585402424c33c16f", 0x49, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x2, &(0x7f0000000000)=[{0x34, 0x0, 0x0, 0x200}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000001080)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240))
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
unveil(&(0x7f0000001240)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000000)='r\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2}, {}, {0x6, 0x0, 0x0, 0x20403}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
pipe(&(0x7f0000000140))
ioctl$PCIOCGETROM(r0, 0xc0107005, &(0x7f0000000140)={{}, 0x0, 0x0})
setreuid(0xee00, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0x0)
r0 = getuid()
lchown(&(0x7f0000000040)='./file0\x00', r0, 0x0)
setuid(r0)
faccessat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
fcntl$setstatus(r0, 0x4, 0x40)
r2 = kqueue()
r3 = dup2(r2, r2)
kevent(r2, &(0x7f0000000140)=[{{r3}, 0xfffffffffffffffa, 0x8b}], 0x7, 0x0, 0x0, 0x0)
r4 = getpid()
fcntl$setown(r0, 0x6, r4)
r5 = kqueue()
kevent(r5, &(0x7f00000000c0), 0x40, 0x0, 0x802, 0x0)
sendmsg$unix(r1, &(0x7f0000000040)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x2, &(0x7f00000000c0)=[{0x60}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
setitimer(0x0, &(0x7f0000000100)={{0xffffffff}}, 0x0)
ioctl$VMM_IOC_TERM(0xffffffffffffff9c, 0x80045604, &(0x7f0000000040))
getitimer(0x0, &(0x7f00000000c0))
mlock(&(0x7f0000ffa000/0x1000)=nil, 0x1000)
munlock(&(0x7f0000fed000/0x13000)=nil, 0x13000)
msync(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)={{0x0, 0x0, 0x0, 0x5}}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xa, &(0x7f0000000180), 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x4}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x55)
socketpair(0x2, 0x3, 0x0, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
symlink(&(0x7f0000000000)='./file0/file0\x00', &(0x7f00000000c0)='./file0\x00')
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x20000, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000006c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000040)=0xc)
getegid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setregid(r4, 0x0)
setegid(r2)
r5 = getuid()
fchown(r0, r5, 0x0)
setreuid(0xee00, r5)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, 0x0)
setregid(0x0, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x84)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x4, 0x104, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000040), 0x10, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000240))
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000002c0)={&(0x7f00000000c0)=[{0x8b, 0xf1}, {0xa7, 0x7fffffff}, {0x20, 0xacac}, {0x85, 0x6}, {0x100, 0x1ff}], 0x5})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = socket$unix(0x1, 0x5, 0x0)
dup2(r2, r1)
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
getsockname$unix(r2, 0x0, &(0x7f0000000140))
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x400, 0x0)
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x4c}, {0x25}, {0x6, 0x0, 0x0, 0xbb4}]})
pwrite(r3, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
listen(r1, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
shutdown(r1, 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0xf}, 0x2, &(0x7f0000000080)="65b0c1f50068a00158ee9d16a0fb9c7ce9cdf247037f", &(0x7f0000000100)=0x16, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="b3022e2fac14"], 0x10)
writev(r0, &(0x7f0000000040)=[{0x0}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xc028445a, &(0x7f0000000240)=0x3)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x4, &(0x7f0000000080), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
listen(r1, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x23, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{}, {0x60}, {0x6, 0x0, 0x0, 0x1f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000002c0)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)={{}, {[], [0xc8, 0xffffffff]}}})
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x36, 0x0, 0x300, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
r2 = kqueue()
kevent(r2, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
pwrite(r1, &(0x7f00000001c0)="47c6966555a40bc31f3e0ffc01d3", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup(r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f00000001c0)={0x0, 0x0, 0xffffffff, 0x7fff, "f79ebc0800000100"})
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000740)="a56e05017df6e34ef307911c47f1660521d5a9340bbc450276a125cf519af0541cea0e27218f6ead73325e2903000000b0635b0533416f828c78414cebd268d5dc4a9abc0edcf6fec0a09de15300b7661644fbe3096988cb8e41442b8b8a601a87db6d08ed7a26b00aff14492f76c4a826f39545771908bb1043ea05ee4b667eca95bac3c3f8786863126dd9913395291b90f9fc82fad9cae09f5eefa6f804a47d08fe788f9a24536c794cf4b3", 0xfffffd05}], 0x1)
write(r2, &(0x7f0000000080)="4716ec839301", 0x6)
writev(r2, &(0x7f0000000640)=[{&(0x7f0000000200)="35b6870908", 0x5}], 0x1)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000680), 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f00000000c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = socket(0x11, 0x4003, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
r3 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r3, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
lstat(&(0x7f0000000040)='./file0\x00', &(0x7f0000000100))
setsockopt(r3, 0x0, 0x800000000000c, &(0x7f0000000000)="36fd22eaeaffffff", 0x8)
ftruncate(r3, 0x0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0xee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = socket(0x18, 0x2, 0x0)
r6 = fcntl$dupfd(r4, 0x0, r5)
ioctl$TIOCFLUSH(r6, 0xc1206949, &(0x7f0000000300))
r7 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r7, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000100)=[{0x21}, {0x1}, {}], 0x3})
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0xe0)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
unveil(&(0x7f0000000080)='./file0/../file0/file0\x00', &(0x7f00000001c0)='x\x00')
execve(0x0, &(0x7f0000000000)=[&(0x7f0000000100)='//'], 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
syz_emit_ethernet(0x67, &(0x7f0000000000)={@local, @random="3a6d1f6d3961", [], {@ipv6={0x86dd, {0x0, 0x6, "3835fc", 0x0, 0x0, 0x0, @rand_addr="e674d7e4395d1bb00319d6289957902d", @local={0xfe, 0x80, '\x00', 0x0}}}}})
mkdir(&(0x7f00000000c0)='./file0\x00', 0x103)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='r\x00')
sysctl$kern(&(0x7f0000000000)={0x1, 0x16}, 0x2, 0x0, 0x0, &(0x7f00000010c0)="d21fad59bd795361b1bec8c8da1db91ec189e0bef92b8801c0748c17b29fba0963ad223f2e00a42992135938313565c9b5c43eefc605cebff487ec2e35762aacf06a24e0a982db74772a444878ebf354deb2b29411f9537a23c5b8b43d7593b79044f00a04042eeaaaf486579143efee3f2079a7e1bf3df213a1eddad1b5a077a116fbbcc34ae6d1835f853c2cb12d133fd91fa8f5c97b7de0714a0bd17c7842b2059820c860497efa1ab2093b2dec67557910189f336253e43a18787d7926a2b3cd73f036763db6b57ad77e4dd274882ecdf9de218d594e13d3e4b273d3a3c5baaaa2272fc0e8345aecf09d878d2fbe2ac340a6e931e9a7b1d58696ab4ef191", 0x100)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x800000000029, 0x25, &(0x7f0000000040)="1f12fb36", 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = socket(0x2, 0x1, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x8000000000005200)
r0 = open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
r1 = syz_open_pts()
poll(&(0x7f0000000100)=[{r0, 0x1}, {r0, 0x1}], 0x2, 0x0)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000000)=0x7)
setgroups(0x63, &(0x7f0000000000))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x1000100000001})
fcntl$lock(r0, 0x7, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
sysctl$ddb(&(0x7f0000000080)={0x9, 0x6}, 0x2, 0x0, 0x0, &(0x7f0000001140), 0x4)
r0 = socket$inet(0x2, 0x1, 0x0)
dup(r0)
select(0x40, &(0x7f0000000000), &(0x7f0000000040)={0xffffffffffffffff}, 0x0, 0x0)
shutdown(r0, 0x2)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000000c0)={0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9], [], [], [], [{0x400}, {}, {0x0, 0x0, 0x6}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
r1 = socket$inet6(0x18, 0xd80f59b5a9ecd02b, 0x5)
dup2(r0, r1)
bind$unix(r0, &(0x7f0000000040)=ANY=[@ANYBLOB="26e02d2f66696c653000"], 0xa)
r2 = socket(0x18, 0x2, 0x0)
r3 = socket$inet6(0x18, 0x1, 0x0)
setsockopt$sock_timeval(r3, 0xffff, 0x1023, 0x0, 0x0)
r4 = socket$inet6(0x18, 0x1, 0x0)
setsockopt$sock_timeval(r4, 0xffff, 0x1023, 0x0, 0x0)
fcntl$dupfd(r3, 0x0, r4)
sendmsg$unix(r2, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="10000000290000003e"], 0x38}, 0x0)
r5 = socket(0x18, 0x1, 0x0)
setsockopt(r5, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r5, 0x29, 0xc, &(0x7f0000000040), 0x14)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
unveil(&(0x7f0000000200)='./file0\x00', &(0x7f0000000240)='x\x00')
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0xc}], 0x1)
fchmod(r0, 0xe3)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000180)="e92243f80fde4d7f8f29daa64a599ac309006eead5e929394fcc880e89669697f5d9d98e3dd215d7d367a8a705093c0a", 0x30}], 0x1)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, &(0x7f0000000100)=0xffffffff, 0x4)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x9)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0xe5)
r0 = open$dir(&(0x7f0000000180)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x1c1a, 0xffffffffffffffff)
fsync(r0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000ec0)="44000f3e265873b91d1195de6aae7fd0cc327233a9ff18e27ff8328f8251e84ab281bbadc0f1b0c9dd01a66c110fb46800f5fb1830aad6f149cd793c3e9703e27bc23ee4e7747097a4d928098f2d5a4f468527b58a5569e6cce319ea39307452d1f9fafe54024e3fb0ba0ae44677cc64d889534d97dab3a38dcd661d32c19ac82ab98e8b9c86930092eec37e297a0181389db8759d071488187d169caf2fd0b36667a99b4aacbf5c5d22db", 0xab}], 0x1)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x10}], 0x1, 0x800)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
fcntl$dupfd(r1, 0x0, r1)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x2}, {0x74}, {0x6, 0x0, 0x0, 0x80000001}]})
pwrite(r2, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0xcd604404, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xc4504448, &(0x7f0000000000))
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [], {0x7}}}})
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000140))
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
sendmsg$unix(r0, &(0x7f0000000780)={&(0x7f00000000c0)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000000740)=[@cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff}], 0x20}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000380)={&(0x7f0000000140)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x90, &(0x7f0000000300)=[@rights={0x10}], 0x10}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x40, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x80}, {0x50}, {0x6, 0x0, 0x0, 0x103}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0x3a, 0x0)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0x80000000000000b, &(0x7f0000000040)="90000000", 0x4)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1c}, 0x4, 0x0, 0x0, &(0x7f0000000040)="affc16ad", 0x4)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000580)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000080)={0x0, 0x0, <r1=>0x0}, &(0x7f00000000c0)=0xc)
setregid(r1, 0x0)
seteuid(0xffffffffffffffff)
setgid(r1)
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x4, 0x18, 0x3a, 0x3}, 0x4, &(0x7f0000000c00)="a20b912e80f35e6f99a409b6bd554626091eb5adb7f200188a03163107091f71eea8259b40cb605ae998b44abcf2e62cc94f1732150c4302300c81f815a72940d22c679c94022bd9dc22086b89e2b3c80ccc817f5bf648d966336c13e9479586d102902f31fbf8fc8e68c4dcf8ce2c828580acc054947791358de836288ee8179466ee537e2739e785a73116c1896a0618d7214c07d79a01e86e06a1265bbc8a499355fda2ab7fbea0d2ca641c86bee4e1ff6a3426cf06617ad9cb007d21eb492ea0b529d9e3b2bf1e5a21007fa398ed6356aa8c0f9a8fb5850c2d9ff388eb4a640bb0c193fcc9e77ec515fd43176ada0613dc3a7ef780b718a0f22869e60ef74e83bba1a97b4d3ea10b80f58e5ce051654e25f4bca09ad97fe905bbc05f494b729da5d35a458cb00724dd82893d8b1cec748b45db2b4aa992ae63e44cf07ccc2f4cdd708d1b8fe86d14aadf3d42c5b444e16c1e5308aa22d3d2dba22b52159e4818235231b81ffc39e172776a4fb3908db6b60ff04959c00a7fa5910428145c0650fd3eba26e815c1dfcb98c73a173bfb5889519f8f2dcf4f7fd8e0b98c92e859fee347b522bf49fc0fe9f49d154c2c64c789b906da15661b8def837eec9592f0c2cd5c40ba7019b9bdd3566768007840d3d2317f1f42b96480eb3639a87f1a3e732838005809af18713ec337ba555c2d06b3baec1bca8d7d23825efcf7d65b8e90b2bfdb18295e0fb0b247e54e39d560fab36210bc86bcee9ef9fe822eb60b83560311a558a5e0363a83052964dec650258032818dcaf33ee572848fc5bfe7ddd5ff397af516017660eedb706876eff1561d81950dc5ff0aa13d8ebebc1d68c0d510ed11b27743c7dc8899f871781fbcfec5da4432d0a9ff4f137a808ac1a138b351f7da51a44259bec96183ea54deac1d83124bfe28182143d8e13ff17bea6762f51e53ca690af257eb61c26c549eb4db7e3e3efdfc267d6822d23c329b1d2ff4f7bab5ffd6a4cfdf6ae852ab6f1a129d8322f0b74ca6a6a1f4fce5508095de2f265159b907c8619597adbfa4ae97f15fc030109a59f21e1f12651ef3459ef0820455656bd96b5f47879ee4bbdbb75af8c4d88ee179a437dab12f68d197677516238d46c27ca4160f9f79a11b1c1a612ee313ab350d2aed64da768d14ae43d201e6142e4b7548b853e2b69cfa9fc5fab9107e31faad2ea2688958c27e5fc573adf8c7fce24b660b8b97cceb9f0d82b6e5cacd8eab8bf72dcc9849f942bdebbc62d12020d1e2d8cddcd29cb80ba6ad16c3f92d2225fa36f1bda3165100b7832b898d102ddbe6962dcc22162304a5bfcbd4804927fded3cdccefa16b4d80b9db6d72eaff75df93508f0df8d21efc329d4e503e8040ec0e12375716e106707756a10dc3e48d9cc0e857239bb36fc8b7bee6880c5e0868856483a755039528c0895", &(0x7f0000000140)=0x401, 0x0, 0x0)
r0 = syz_open_pts()
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100004000})
flock(r0, 0x5)
fcntl$lock(r0, 0x7, &(0x7f0000000080))
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000003c0), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000001c0)={&(0x7f0000000100)='./file0\x00', 0x1, 0x0})
open(&(0x7f00000002c0)='./file0\x00', 0x200, 0x0)
recvfrom$unix(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r2, r1)
sendmsg$unix(r1, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
sendmsg$unix(r1, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000280)={0x6, 0xb}, 0x5, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
openat$pf(0xffffffffffffff9c, 0x0, 0x2, 0x0)
kevent(r0, &(0x7f0000000180)=[{{}, 0x0, 0x0, 0x80000000}], 0x9889, &(0x7f0000000280), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$FIOASYNC(r3, 0xcd60441a, &(0x7f0000000240)=0x2)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x1, &(0x7f0000000140)=[{0x0, 0x5}]})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000140)="9513", 0x2)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x3}, 0xfffffffffffffddb)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket(0x18, 0x1, 0x0)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000040), 0x10, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1004, &(0x7f0000000080)=0x1, 0x4)
setsockopt(r0, 0x29, 0x27, &(0x7f00000000c0), 0x4)
syz_emit_ethernet(0x3e, &(0x7f0000000000)={@local, @random="386f1e06008f", [], {@ipv6={0x86dd, {0x0, 0x6, "299e41", 0x8, 0x2b, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2={0x0}, {[@dstopts={0x33}]}}}}})
ioctl$TIOCSETAW(0xffffffffffffff9c, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x3, 0xba, "c0ffffffffffffff00000000008b0efb00"})
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
getsockopt(r0, 0x0, 0x5, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
fcntl$getown(r0, 0x5)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x6}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x37}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x81946466, &(0x7f0000000240)={0x0, 0x0, 0x0})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f00000005c0)={0x0}, 0x10, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
write(r0, 0x0, 0x0)
mlock(&(0x7f0000ffb000/0x2000)=nil, 0x2000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000000))
r0 = socket(0x1, 0x5, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="92029f9cffffffff"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x2, 0x2, 0x0)
bind(r1, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
r2 = socket(0x2, 0x2, 0x0)
dup2(r1, r2)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
r3 = socket(0x2, 0x2, 0x0)
bind(r3, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x61}, {}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000380)={@broadcast, @random="069e4d509b40"})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4d}, {0x7}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000180)={@random="27aacea59068", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr}}}}})
r0 = open$dir(&(0x7f00000000c0)='.\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
bind(r2, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
setreuid(0xee00, 0x0)
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000180)=0xc)
lchown(&(0x7f0000000100)='./file0\x00', 0x0, r3)
faccessat(r0, &(0x7f0000000080)='./file0\x00', 0x4, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x4, &(0x7f0000000040)=[{0x10001, 0x0, 0x0, 0x3}, {0x3c}, {0x1}, {0xffe}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)={@remote, @remote})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
setreuid(0xee00, 0xffffffffffffffff)
r1 = getuid()
setuid(r1)
r2 = socket(0x2, 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
pipe2(&(0x7f00000002c0)={0xffffffffffffffff, <r3=>0xffffffffffffffff}, 0x0)
r4 = fcntl$dupfd(r2, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r5, 0x80206919, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000000)={0x6, 0x14}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000000)="8e", 0x1)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000080)={0x1})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x800000018, 0x8003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x4000009, "000006000000002000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x5}})
r2 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r2)
setsockopt(r3, 0x1000000029, 0x2e, 0x0, 0x0)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f00000000c0)='x\x00')
unveil(&(0x7f0000000180)='./file0/file0\x00', &(0x7f00000001c0)='x\x00')
chmod(&(0x7f0000000140)='./file0\x00', 0x0)
sysctl$ddb(0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
seteuid(0xffffffffffffffff)
acct(0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x4}, {0x6c}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x3d}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000340)={@broadcast})
mknod(&(0x7f00000000c0)='./bus\x00', 0x80002007, 0xffffffffffff2822)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x8000, 0x0)
r2 = syz_open_pts()
open(&(0x7f0000000d00)='./bus\x00', 0x40, 0x88bfb5ba64bd751f)
close(r2)
writev(r2, &(0x7f0000001680)=[{&(0x7f0000000680)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae432b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2d8d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46d", 0x5c9}], 0x1)
writev(r2, &(0x7f0000001700)=[{&(0x7f0000000440)="9a8fab2d8eb4a6ebdbd69775a443f51e35e2574e6291c1ff9732f9c18bceecbb4581154806307f3125f36c82a5d7ac0046ac9e2d55a4a97bb982134e50fd90d4038f94584eb617374dc40beaa963af0225b25d93dbf34d21cf8903414ca064fea5969aaa4c53fcb65b89b8e3f2ea6a3b9e3bcf5a56a8231d617050aec12c2a2ee4cc22957b4a80b7a24ec131fdb98d274f3d10d206f89430f4ada7045668f0e2d5f5565f0f346da98f886742c16d1203fb8c1d2c4abb2137148dbd8bc5dd1ec4365519b544cf39f6a5c4d600aebf18a18439c10aed2edc6c6acde29135aa468d6df763ad6753159c1c5a0c3ce3eea5dc07524ef1a0d7bfae4da0eb741194cd7694415557a7c82752fb76a497bc03f49a1d8efabd43135cd8a39bea327632760efed68b77c834bc70cac69fdd46d40314f6300969f9b552b0e313bb49a8a872c7b053b60d1f2df28c16a9981fca6155ed172c774d4647e996bd0bfbf384", 0x15d}], 0x1)
write(r2, &(0x7f0000000100)="e88e366cea37fb7decfc76d958eeabca06bc3444b948e5e189ee7a01b59c73182270ac339de3a89a4bc72894b3fa23d5619189389111d3f6978610a3a726807afbd89f0d4a78145ecf873e8f1c434dc392277656c13c286183a37a15ff6e9a47eaa8d09e79ed", 0x66)
syz_open_pts()
writev(r0, &(0x7f00000010c0)=[{&(0x7f0000000080)="4b5a0ca7ee503803cd62f2f6e13781ad0160adcd21abf1a8f0c25cff71a97e98eb6fa334bc343187b3", 0x29}, {&(0x7f0000000f00)="c1c276000000000000000726f0f472ee4d8597ca7b1e114416f7b0767a000000007bece4f63b8e474c7717dd8d1b372794f0464a63d304505fd5818a2c5d5438e948d6340d418da635f986c4062df24889fe7c04815fedac4f89d69a464d34e2e394d916ffc91ffc965db1e1f24d0044440f1576f5df5442381cca25d10e70dc0a3873dd30267e404b7f69f684c19d53c7eb91a259a06631b85b5285cef9a32d6859fa75fabed363c3033af235b608fbd6d0d0ad2fa2af371f7bebb66b162bf0372124fb1bd296dbdb6ef8cd3e16418a7b91c26d44d77b61", 0xd8}, {&(0x7f0000001240)="3ba471583ad241dec80ea52c4d4676ce0ee94793e1bf67e7d80fe9a9056e3739e78fd5e3b5a4ed143ee9d520b4235592275dc67c90cb763527b50433fea01b4a5c9a8cb78a016b06ea5fc218d0914c6cc2042c24dc68b1dce69b4a4684cb2209bc44255c0f03bdab1fd608e954be1ef51c0ab6954db03f439e5a5ce9d88b0ac766f011ec6d855512ee8a47a5b342c197eeaee2fa6f82d4f9d9d323817308db02840f426d870598b6f830abd09f6d63bd35eb30d752fd1869de7f5171e00f78c45b42a3fa9fc26de5921a42ebc083a02e4d", 0xd1}, {&(0x7f0000000180)="2db67a811e5aa9b4c28deccae480080071ac7c41d9a4c5918be22065a8c10915b3987dcc608922fbd301b804", 0x2c}, {&(0x7f00000001c0)="a26a6bfa", 0x4}], 0x5)
r3 = fcntl$dupfd(r0, 0x0, r1)
fchdir(r3)
syz_open_pts()
ioctl$TIOCFLUSH(r3, 0x8004745d, &(0x7f0000000100)=0xdffffffd)
sendmsg(0xffffffffffffffff, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7afb51e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x8, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOGETOWN(r1, 0x4004667b, &(0x7f0000000340))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2], [], [], [], [{}, {}, {}, {0x5, 0x0, 0x1000000}, {0x0, 0x0, 0x0, 0x4}], {}, {0x0, 0x0, 0x406b810b, 0xffffffffffffffff}}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000000)=[{0x2, 0xfe2}], 0x1})
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x11, 0x6}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x5}, 0x4, &(0x7f0000000100)="e7868f76", &(0x7f0000000040)=0x4, 0x0, 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x20000, 0xa0)
readv(r0, &(0x7f00000011c0)=[{&(0x7f0000000100)=""/145, 0xfffffeb9}, {&(0x7f00000017c0)=""/4078, 0xfee}], 0x2)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x64}, {0x3d}, {0x4406}]})
write(r1, &(0x7f00000003c0)="2932a36a848f180091ccccb2d9f707982647899375f536588c20ad4b46e514a5d2164f406f9c04aff6560cb17a4fb28270ebfa9f60d263b9d668637ca2e2fd1a3553d395311ba86259a5739705512e81618e23411e02c9f54b07d4ec6bd34828ec06b6ea3401e9013f3595ab56290242fbcd0a6359fbc26ecb216041ed304e7afd469b2876a912521c6ed6eec36268970199b500380d661a6d837eda76b80f0f96f84887ff8b4f9477dbf1abac18773e7400921491191973c04a66caebddeafc21e8a36b6c870d0a5f142a65694136bb22", 0xd1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x6, &(0x7f0000000040)=[{0x6, 0x40, 0x1, 0xa49}, {0x100, 0x2, 0x20, 0x7}, {0xbe, 0x1f, 0x4, 0xbb7}, {0x5, 0x4, 0x5, 0x4}, {0xcd22, 0x2, 0x6, 0x8}, {0x5, 0x40, 0x5, 0x1ff}]})
readv(r1, &(0x7f0000000080)=[{&(0x7f00000001c0)=""/229, 0xe5}, {&(0x7f00000002c0)=""/180, 0xb4}], 0x2)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r3 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r2, r3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
fcntl$lock(r0, 0x8, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x48}, 0x2, &(0x7f0000000040)="24ba8ab1", &(0x7f0000000140)=0x4, &(0x7f0000000180), 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@local, @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast1, @multicast1, {[@timestamp={0x44, 0x4}]}}, @icmp=@mask_request={0xe}}}}})
r0 = kqueue()
r1 = socket(0x18, 0x1, 0x0)
getsockopt(r1, 0x29, 0x29, 0x0, 0x0)
r2 = socket(0x18, 0x1, 0x0)
getsockopt(r2, 0x29, 0x2a, 0x0, 0x0)
r3 = accept$unix(0xffffffffffffffff, &(0x7f0000000040)=@abs, &(0x7f0000000080)=0x8)
getsockopt(0xffffffffffffffff, 0x29, 0x2a, 0x0, 0x0)
kevent(r0, &(0x7f00000003c0)=[{{r1}, 0xfffffffffffffffc, 0x40, 0x1, 0x154, 0xffffffff}], 0xffff40f1, &(0x7f0000000280)=[{{r0}, 0xfffffffffffffffa, 0xdd, 0x10, 0xfffffffffffffff7, 0x2}, {{r2}, 0xfffffffffffffffa, 0x84, 0x10, 0x100, 0x8000}, {{r3}, 0xfffffffffffffffe, 0x20, 0x40000000, 0x3, 0x100}, {{}, 0xfffffffffffffffe, 0x0, 0x1, 0x6, 0x1d}], 0x8001, &(0x7f0000000100)={0x9, 0x9})
kevent(r0, &(0x7f0000000140)=[{}, {}, {{}, 0x0, 0x0, 0x1}], 0x3, &(0x7f00000000c0), 0x51a, 0x0)
r4 = socket(0x800000018, 0x3, 0x102)
sendmsg$unix(r4, &(0x7f0000000200)={&(0x7f0000000280)=@abs={0x0, 0x0, 0x1}, 0x8, 0x0}, 0x1)
connect$unix(r4, &(0x7f0000000200)=@file={0x0, './file0\x00'}, 0xa)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000300), &(0x7f0000000340)=0xc)
bind$unix(0xffffffffffffffff, &(0x7f0000000400)=@abs={0x1, 0x0, 0x1}, 0x8)
setsockopt(0xffffffffffffffff, 0x0, 0x1f, &(0x7f0000000000)="caf5b415", 0x4)
bind(0xffffffffffffffff, &(0x7f0000000380)=@un=@abs={0x0, 0x0, 0x2}, 0x8)
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r5, 0xcd604404, &(0x7f0000000240))
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107002, &(0x7f00000000c0)={{}, 0x0, 0x4})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x2, 0x0)
r4 = dup2(r2, r3)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
write(r4, 0x0, 0x0)
write(r4, 0x0, 0x0)
write(r2, 0x0, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
r0 = kqueue()
r1 = socket$unix(0x1, 0x5, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{}, {}, {}, {}, {{r1}, 0xffffffffffffffff, 0x9f, 0xc5}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x10000, &(0x7f0000000080)=[{{r1}, 0xffffffffffffffff, 0x2aef0f8a3615ad57}], 0x80000001, 0x0)
sysctl$net_inet_ip(&(0x7f0000000140)={0x4, 0x2, 0x0, 0xd}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = open$dir(&(0x7f00000006c0)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f00000001c0)='./file1\x00', 0x18278, 0x0)
select(0x40, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0xfffffffffffffffc, 0x68}, &(0x7f0000000100)={0x3ff, 0x0, 0x0, 0x1}, 0x0, 0x0)
close(r0)
link(&(0x7f0000000000)='./file1\x00', &(0x7f0000000080)='./file0\x00')
r1 = socket(0x11, 0x4003, 0x0)
sendto$unix(r1, &(0x7f0000000280)="b100050460000000000008010010000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca05000000000000002e27ecb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1abda71601a8bfee8aca4911faff5a872c881ff7cc53c894303b22f310b404f36a00f9000fcffffffe608a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
r2 = semget$private(0x0, 0x4, 0x5b4)
fchdir(0xffffffffffffffff)
semop(r2, &(0x7f0000000040)=[{0x2, 0x1, 0x2400}, {0x3, 0x64, 0x1800}, {0x2, 0x402, 0x7fc}, {0x1, 0x3}, {0x0, 0x2, 0x1800}, {0x1, 0xfffd, 0x800}, {0x2, 0xfffffffffffffff8}, {0x1, 0x800, 0x1800}, {0x2, 0x2, 0x1800}], 0x9)
r3 = semget$private(0x0, 0x4, 0x34)
semctl$GETNCNT(r3, 0x0, 0x3, &(0x7f0000000140)=""/181)
semctl$SETVAL(r3, 0x0, 0x8, &(0x7f0000000200)=0x4000000072)
r4 = semget(0x2, 0x4, 0x402)
semctl$SETVAL(r4, 0x4, 0x8, &(0x7f0000000080))
semop(r2, &(0x7f0000000000)=[{0x3, 0x17, 0x1000}, {0x0, 0x8b45, 0x800}, {0x4, 0x1, 0x1000}], 0x3)
r5 = getgid()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r6=>0xffffffffffffffff})
getsockopt$sock_cred(r6, 0xffff, 0x1022, &(0x7f0000000400), &(0x7f0000000440)=0xc)
setregid(r5, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0x9}, 0x2, &(0x7f00000000c0)='}F\r.', &(0x7f0000000140)=0x4, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
ioctl$WSMOUSEIO_GTYPE(r0, 0x40045720, &(0x7f0000000000))
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETPID(r0, 0xffffffffffffffff, 0x4, &(0x7f0000000180)=""/145)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000006c0)=""/264)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f0000000280), 0x0)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40}, 0x40, 0xfff, 0x9})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r2)
r3 = getegid()
r4 = getuid()
setregid(0x0, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0)={{0xd2, 0x0, 0x0, 0x0, 0x0, 0x3e, 0xfffc}, 0x7, 0x27ffffffffff, 0x6})
r5 = geteuid()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000100)={{0x0, 0x0, 0x0, r5, 0xffffffffffffffff, 0x161, 0x3}, 0x1, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffffffc, 0x1, 0x2, 0x9})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, r3, r2, 0x0, 0x10, 0x7}, 0x160, 0x80000000})
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r6=>0x0}, 0xc)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000040)={{0x2, r2, r3, r4, r6, 0x1a4, 0xff}, 0x7, 0x1, 0x5})
sysctl$kern(&(0x7f0000000140)={0x1, 0x32}, 0x2, &(0x7f0000000440)="851b7e2b14c6625b9269680988a3ce5739681edc9cb64e6b9b32751b66c5dd368f6339b00269d91ba1c3d1a1a47081c426940ee5cda6f2e07de8f8041eab2def208568905a1a04336fa5fab7e7dd26238c8351e760dae2fc16c98029a116402bb5460275ff932c3a18027e46e4e7d85123b125a4cdcfb31994a77b566ae032b0ccf623c89a122a29868f595c2a1d4ac67055ba259bb281a1dff3a41042f1aadef83fca8ee885371ade0ae2074e6ce8a5460640a9e35cf8fb75b165b2950113d305cd784f50c84938cfccf5375e86f942fbd4d14328b3572e2a76b2153ab2a68921a6d6c493eca95f1efa13424edbce1cd300f03ec092d28e2342c5424165ceaeb5", &(0x7f0000000180)=0x76, 0xfffffffffffffffe, 0x4)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000040)="d73160174caf1fdb37b55715656e4718635a8c9994734f2d218868ac2881f1d6c2fda7ca6e6de98059ba14f943f52c65f327f660df5da2a156184864128ec5ae45372aab309d4db3ea499c41662d60a6835c4209f45a86dfd0b571ee93a26215006b35a3c4ca46a4afcc55d5d4f613b33240bc8c9d7beb68f43bf760fd171733787444b15b698c9c75c5b3e41882", 0x8e}], 0x1, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b10005016000009005001b0007000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff3728211ee4fd89720fd3872babfbb770a9f5a872c881ff7cc53c895303b22f310b404f36a00f90006ee01b0000000000020000000000", 0x94, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080)=0x6)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
unveil(&(0x7f00000000c0)='.\x00', &(0x7f0000000140)='c\x00')
mknod(&(0x7f0000000000)='./file0/file0\x00', 0x6000, 0x0)
open(&(0x7f0000000100)='./file0/file0\x00', 0x200, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSFILDROP(r0, 0x80044279, &(0x7f0000000200))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0xc1206951, &(0x7f0000000300))
utimensat(0xffffffffffffff9c, 0x0, &(0x7f0000000080)={{}, {0x0, 0xfffffffffffffff9}}, 0x0)
socket$unix(0x1, 0xbb468c90e8427fa6, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x33, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='c\x00')
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff115c00000000", 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x3d}, {0x1}, {0x6, 0x0, 0x0, 0xc1e}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000140)="0000000000000008cfccfbcbd4a2", 0xe)
r0 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "8ef78233a05e87c40193ff68b3678f2060cef07d", 0x0, 0xffffffe0})
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000740)=[{&(0x7f00000000c0)="d387201be0e779644f47f841aa7e4a5fc21269fcfe161575d616d4c184eafa2fcc6ad47815192d71c0b24cf9b76093d8af4b5d58ebe2c1458fa16e4e8c9389f57bb7cb5c719b176bae09f74075fe2ffb456d8aee4b9ce10de62b499032da6a1f184c347cdf08287690bf3e4785585bc7130599c712c6074952a5cb86d8e7ac7f2cf94fe6", 0x84}], 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100)=0x1)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x28, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r1 = getppid()
r2 = getppid()
fcntl$setown(r0, 0x6, r2)
fcntl$setown(r0, 0x6, r1)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001008e7d00000000ddf500"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000240)=ANY=[@ANYBLOB="200000000000008d742c219880309905f87c8a5b6701000000000000000000c414000000290000002f"], 0x38}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0xfffffffffffffdcc, 0x0}, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMUXIO_REMOVE_DEVICE(r0, 0x80085762, &(0x7f0000000000))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
dup2(r2, r1)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r1 = socket(0x18, 0x1, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0206921, &(0x7f0000000300))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000480)={0x0, 0x0, &(0x7f00000001c0)=[{0x0}], 0x1}, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
r3 = dup(r2)
write(r3, &(0x7f0000001380)='c', 0x1)
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000040)=""/157}, {&(0x7f0000000380)=""/4096}, {&(0x7f0000000100)=""/190}], 0x34)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x1c}, {0x20}, {0x6, 0x0, 0x0, 0x7ffffffc}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x3}, {0x44}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000100)={@remote, @local})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x61}, {0x7}, {0x6, 0x0, 0x0, 0x80000000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000013c0)={0x0, 0xa, &(0x7f0000001300)=[{&(0x7f0000000280)="581495f552b4d631af7294ee08ab94a721541560edeeb1c22736fa51dfbddfd7ba14ecd45ec89656af7f18d8cc99b8e64457c5a97a57cca1c6e7c267cd8543e82c66dfbf1496000b1f88ed456c3d63d1", 0x50}], 0x1}, 0x0)
socketpair(0x1, 0x1, 0x0, &(0x7f0000001400)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(0xffffffffffffffff, &(0x7f00000000c0)={&(0x7f00000016c0)={&(0x7f00000002c0)=ANY=[], 0xa, 0x0, 0x0, 0x0, 0x50}}, 0x10, 0x0)
recvmmsg(r0, &(0x7f0000001640)={0x0}, 0x10, 0x0, 0x0)
mprotect(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000003c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x7c}, {0x30}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@remote, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @empty, "", @remote, "02966cd200a17a203bd4b3e7e1f57a09"}}}})
sysctl$hw(&(0x7f0000000000)={0x6, 0x19}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
seteuid(0xffffffffffffffff)
setuid(0xffffffffffffffff)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x1c14, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
recvmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
mknod$loop(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x2000, 0x1)
link(&(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000380)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')
rename(&(0x7f0000000780)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f00000008c0)='./file0aaaaaaaaa\x00')
rename(&(0x7f0000000200)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000300)='./file0\x00')
munmap(&(0x7f0000000000/0x1000)=nil, 0x1000)
recvmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0, &(0x7f00000005c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd604407, &(0x7f0000000240)=0x2)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000000)=[{0x24}], 0x1})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0x8010570e, &(0x7f0000000400)={0xe900, &(0x7f00000003c0)=[{}]})
r1 = openat$wskbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r1, 0x80045710, &(0x7f0000000000))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000140)={&(0x7f0000000240)=[{}, {0x101}], 0x2})
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x300000003})
flock(r0, 0x1)
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x2fffffffe})
setitimer(0x0, &(0x7f0000000000)={{}, {0x5}}, 0x0)
setitimer(0x0, &(0x7f0000000080)={{}, {0x0, 0x2}}, &(0x7f00000000c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0286988, &(0x7f0000000300))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "011daa1e7d7141b432ffffff7f0400"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setuid(0xffffffffffffffff)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x28, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000002}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
semop(0x0, &(0x7f0000000000)=[{}, {}, {0x3}], 0x3)
semop(0xffffffffffffffff, &(0x7f0000000000)=[{}, {0x0, 0x1}], 0x2)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x2)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x3d, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0x4004570c, &(0x7f0000000400)={0x0, 0x0})
syz_emit_ethernet(0x4a, &(0x7f0000000180)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "052613", 0x14, 0x0, 0x0, @empty, @loopback={0x0, 0x2}, {[], @tcp={{0x2, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000140)="e800f876223f800016000000", 0xc)
r0 = socket(0x11, 0x4003, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000300)='./file0\x00', 0x0, 0xfcfc96ac1f78739e, r1)
getpeername$inet(r0, 0x0, &(0x7f00000001c0))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x26, &(0x7f00000000c0), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x402, 0x0)
r1 = open(&(0x7f0000000200)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000380)="90c3fe67eb586898600425f2f573e0d1ac83c18d00c8e22066c0d389fe895a974c8d45aaf9a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e66e1f5d347d5b668a3214ed85fb20e088c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e32082cec454327b6a1522c332ea628b8cb672e9e724780100000017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b28020000000d00000000c1570f94a3e302", 0xcd}], 0x1, 0x0)
write(r1, &(0x7f0000000040)="ce", 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
pipe(&(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
poll(&(0x7f0000000040)=[{r1}], 0x1, 0x0)
readv(r0, &(0x7f0000000440)=[{&(0x7f0000000180)=""/166, 0xa6}], 0x1)
close(r1)
r2 = socket(0x18, 0x1, 0x0)
close(r2)
sysctl$kern(&(0x7f0000000200)={0x1, 0x27}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x45}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@empty, @random="fc00400000ca", [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @rand_addr, @remote, @remote={0xac, 0x14, 0x0}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {0x14}, {0x6, 0x0, 0x0, 0xdd7}]})
write(r0, &(0x7f0000000200)="3c9ebbd755feff969613ba3e1fd0", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000080)=[{0x20}, {0x60}, {0x4000006, 0x0, 0x0, 0xfffffff7}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0xc, 0x0, 0x0)
r0 = socket(0x1e, 0x3, 0x0)
socket(0x1e, 0x3, 0x0)
socket$unix(0x1e, 0x3, 0x0)
r1 = syz_open_pts()
dup2(r1, r0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x9, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x403, 0xfffffef9, 0xfffffffa, "0804000003090000008100008d1b8283470400"})
write(r0, &(0x7f0000000040)="4d17fb388d00007c073572222bccadd9cc28abd12122c05b98aa8c3def8deca40ae7010000000000000091e53eb766834891f945d91f8d97d5d45bf0998a390c108f18b878325411bf91a02e155c09", 0x4f)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
r2 = getuid()
seteuid(r2)
rename(&(0x7f00000002c0)='./file0/file0\x00', &(0x7f0000000100)='./file0\x00')
clock_gettime(0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000001240)=[{0x0, 0xfffffeba}, {&(0x7f0000000100)=""/27, 0x1b}, {&(0x7f0000000000)=""/50, 0x32}, {&(0x7f0000001180)=""/149, 0x95}, {&(0x7f0000002240)=""/193, 0xc1}, {&(0x7f00000023c0)=""/206, 0xce}, {&(0x7f00000024c0)=""/193, 0xc1}, {&(0x7f00000025c0)=""/213, 0xd5}, {&(0x7f00000026c0)=""/235, 0xeb}, {&(0x7f00000027c0)=""/142, 0x8e}], 0xa, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0x0, 0x0)
munmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
mquery(&(0x7f0000ffb000/0x1000)=nil, 0x7f7fffffc000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
munmap(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
mmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x8d5, 0x0, "000100000000000000002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080))
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3b}, 0x2, &(0x7f00000000c0)="5617b270102aa62a179d672c904e4f561e49f7e54f36903477b4ae744204dbc48be3586a638a7dc725d059844d385f6e61cbfd16455ad7a00d207c27c50c70e2abdfd07602e4f9e5481008687180d5ce584d5e64f7686883db3dd52060ff510d2c6952423cb636add25ea672dfeb8450c17bd1e413fc8d539fef1fea6c1fd4bc5debd2f5077e7d351ebb69e42e98c6ea92ff2b8a65bf2d9283086dcd7643990491d80cde6c63103b875775edff692934182d697186af4f24e2a68347a3faf4bcbb326e19f5de9a60341b1575b363e15ac33bcef689cf2a348252f50a8264e3d6bb6e1915e421e532aa411380368418adb9ff7968085361ba0027a6a7e1eca82921997eac100b531d95d54c8fd63d9260bbce0674745bfb165e43b7f2507a21f28b45e22dd4feaa86d8191d65113d0b9add781b83d19c39fba93cba66c5f8c1a0388f5365f898d82c8db58fe9de47859d70634ee4faf6779113be588aafe61c30439e31a397df6bfd06d255876dec49026cbed3301cf5851453e8174eb5113d0d3f2e68f7ec9a0f0f214afe51397e3f5f86fb8ce02ff5ba7c5308524caea7322a9ad3c7b446a1db1e7716f40847cf3686eecbf1b5e13f3ed132937da9567745aeb58121bfb26c64d113f5f0d4828f320becff83e3326c82799e9466e3886209e37a06e581d52a3d3dc5af90b45081f471253b5033bdd3be1ef68b42090437a816cadb6db0c3d190b4f13c5d83cf81182701a0f974e17750be", &(0x7f00000010c0)=0x218, 0x0, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
chroot(&(0x7f0000000180)='./file1\x00')
fchdir(r0)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f00000000c0)='r\x00')
open(&(0x7f0000000300)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000001340)={0x3, &(0x7f0000000080)=[{0x1d}, {0x7}, {0x4000006, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000140)='./file1/file0\x00', &(0x7f0000000240)='c\x00')
fchdir(r0)
chdir(&(0x7f0000739ffe)='..\x00')
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)="392a22b867b5c301b6737b00a51141cc890ed1a5571cfb6b5d6b4a0bbb", 0xfe57, 0x401, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f0000001740)=0x4, 0x4)
recvfrom$unix(r2, 0x0, 0x0, 0x1, 0x0, 0x0)
shmget$private(0x0, 0x3000, 0x0, &(0x7f0000ffa000/0x3000)=nil)
syz_emit_ethernet(0x40, &(0x7f00000000c0)={@local, @random="386f1e06008f", [], {@ipv6={0x86dd, {0x0, 0x6, "299e41", 0xa, 0x2b, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2={0x0}, {[@dstopts={0x33}], @generic="98bc"}}}}})
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
acct(&(0x7f0000000280)='./file0\x00')
acct(0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000080)=[{0x80}, {0x87}, {0x16}]})
pwrite(r0, &(0x7f0000000100)="5df09d5792f307847f24cd378250", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x54}, {0x25}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000040)={@empty, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @empty, "fd504cfeed60403be742a1831c583086"}}}})
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
pipe(&(0x7f0000002840))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000005cc0), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f0000001400)={'./file0\x00', 0x65dd})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwrite(r0, &(0x7f00000000c0)='mn', 0x2, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0x401, 0x7fffff81, "09003019e3230600fc4c399f3bd6c57fbbe77c0c"})
write(r0, &(0x7f0000000280)="4de3a767cb3572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cc3392c70fb4d2ea468e980877377f1cfb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70ac323074dd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19a3dff7bde555c03c89296384aa8f8b1e7179729ea0bb9ac633eedb29996c3da18b7af5150085f27cd1a07a7e9b60d858b2f1e5800125da2f78713e82f66341bc07c02b83799f6c872f9fdc87b82c4f4cb92ef1292f1fc9abaf5d17d7804a678fd8601aab32be210c18e48e1287828d903f336f4ea7342c526849e1dfbfba385183f622d1e4e0e1498329fb523d2bde8182cb6bb71d0c7c7cff135c6348649f6e0e61785ce8cde972e9a411d8c951c3f45f83161b98f84c13775ca1", 0x170)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000001440)=[{0x0}], 0x1, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0xb}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETVAL(r0, 0x4, 0x5, &(0x7f0000000600)=""/244)
semctl$GETZCNT(0x0, 0x4, 0x7, &(0x7f0000000500)=""/67)
semop(r0, &(0x7f00000001c0)=[{0x0, 0x8, 0x1000}, {0x1, 0x4, 0x800}], 0x2)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000003c0)={{0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5}, 0x10000, 0x1, 0x7})
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000200)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x48, 0x40}, 0x3e, 0x10000000fff, 0x9})
semop(r0, &(0x7f00000000c0)=[{0x0, 0x8000, 0x1800}, {0x4, 0x400, 0x1000}, {0x2, 0x4, 0x800}, {0x1, 0x26ba, 0x1000}, {0x0, 0xffff, 0x1000}, {0x0, 0x0, 0xed809031737a0d4e}, {0x4, 0x6, 0x1000}, {0x2, 0x7}, {0x2, 0x3f, 0x800}], 0x9)
semop(r0, &(0x7f0000000040)=[{0x3, 0x3, 0xb1dca765c7eab520}, {0x1, 0x937, 0x1000}], 0x2)
semget$private(0x0, 0x2, 0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000480)={{0x621, 0x0, 0x0, 0x0, 0x0, 0x5, 0x2}, 0x2, 0xfffffffffffffffc, 0x3f})
r1 = semget$private(0x0, 0x7, 0x3e0)
semop(r1, &(0x7f0000000080)=[{0x3, 0x2, 0x800}, {0x1, 0xff, 0xe17b725e3710487a}, {0x1, 0x8, 0x800}, {0x3, 0xffff, 0x1000}, {0x2, 0x1, 0x1800}, {0x3, 0x1, 0x1800}], 0x6)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000580)={{0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24}, 0x8, 0x6b, 0x40000000007fff})
semctl$SETALL(r1, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
semctl$GETZCNT(0x0, 0x2, 0x7, &(0x7f0000000300)=""/124)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000180)=[0x0, 0x7fff, 0xff, 0x0, 0x1])
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = dup(r0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
pwritev(r2, &(0x7f0000000400)=[{&(0x7f0000000280)="81848a36ee777544002567fdc9b789e210bcd0a97d0b7fcad4d24e3f52b121d64a45bd5d3aab9bf26cd687e6c341caa90e4d856f65817fc10222412a60d07502f55251f0e19d0252065aaf5969b74804b7fbec7a72c12422a8973da4ca1e13c1", 0x60}, {&(0x7f0000000300)="434cd8b597b6b2e44ad6503fee2d558cbab7fca1788805d5f5b6de774fc5dd246907fa8d29f79dd9a1e698bf991d81088d7e0d58d4317d4da16d3e3d85", 0x3d}, {&(0x7f0000000540)="dcb890a54408522e0a553741d4d684cd1a0d5be9aaf99ea958f5d3c127e1e84057ce7995f7eab55685a5c983d9f4db7be683d39f6d99843b6b00cb6501d4294b4523399d17bdaf52707faecd8f2cc4c543e86fc53f92cb9387e673788a49ab63698bdc1ead95e45d74b79d", 0x6b}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$SPKRTUNE(r1, 0x20005302, &(0x7f0000000100))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
r1 = msgget$private(0x0, 0x2)
msgsnd(r1, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2c9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r1, &(0x7f00000002c0)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da6461341bbefbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6ac1571170e268ce1d0b4bbd8bf45c5fd340a61305979b0bf685e45f57392649d8248976549ce08056e03c959080cbf50312d6635b3b58174bd552e9c513f2acc71bb2c97c8a895fc07478c415c6aa3db3cd6b47f2e874c2c9c03886179802e5606fb276950cca74cf527bf968ceba0e8125af4bd5000000000000000000000000000000c6bd6dd52135696ea9f082ad0938b93df3eca8aad08910b7e8ee4403738cb1dba0c104f09ad91582087e0eef0e43c90e92359f8ede8b87f64c5eec79c4594886691ad2cac8b2e1796506fade88f319a97d9a40c749f3a53994efba60bcc277b0a17c6e5bee5a8698f73b55972dc1c52a0b69bbbf91228a81ebf27c3732ced1949ff053da397baae03e894a58defd824bac1ac4ea092b2d476d374f122423d47361c20a5575804aeaaf479fe73daebf5309cb45fa4eb8bfe4165315a5f49b632e69ac567667569d953be14879773cc3c78f2fdfec298216a3db9e99db5fba149b69d0a649ac39c238a4fd3088e1913b38a6c55b9ae4830b12d87c0377e0adaf3cf10b93e21c02ad1f444a48b3244fd5fb92e8d7c30659d340dc22b277dabe7d9e656b87da46b865b2ca5077fabb5581713793bdd72e1423112ffdd863a6ab56"], 0x6d, 0x0)
msgrcv(r1, &(0x7f0000000580)={0x0, ""/18}, 0x1a, 0x1, 0x3000)
msgsnd(r1, &(0x7f00000007c0)=ANY=[@ANYBLOB="0300000000040000db4436958b193c67b6ce0c093bb0bbf3b3245230033f58052ebd418aa6e7161916fd47a14a3a74259ca57335615dd09efc3ddf520906bf778f89cf9aa9da8fbd888acf1d82015453ebaf5328755e3b05d90ba5ba0c00faff96c620f0866300000000"], 0xb, 0x800)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000500)=""/6)
msgrcv(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000800000000000000000000002000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600"/168], 0xa8, 0x2, 0x1000)
msgsnd(r1, &(0x7f00000000c0)=ANY=[@ANYBLOB="0100000000000000bed026e4a61a5a31474bcb3caaae961351edc3de9f0f51f11ee4b78001b2755bbc86636ce1323c266151b7cedff6ab0d4575ceb3868bcb851e5bb5bd9870720be6fdb977fe6ab5d8a3574d7e584505de9e3b5292f2e5bb3588b2c1930f164befb0e8bfdba2df86bc557b60b6a18873a8ff91636435a38f1b5f51f8a565f36cf892768f23258eb980351c642e08dcf51c16e11aa425ea4071da4352de439c29a0d65d9fa8a886843bd190954409f1cc91695e665a361a8b1ba17663703ebb8c510ccfdfabb2eafa5ee96ae18e70ec4074687c40b2b126f7c0c39ac9a3b12f00c0a4e010dd80e6b7469e8b222e1f0cdbee757bb309724f27dd203456b463a24234856f7a4e44d7d63ec103a3a210030c66a8a2b17678d465ec97d9106691f2e56dcd627ebb75503e62eebed6e4"], 0xa, 0x0)
r2 = getpgrp()
msgctl$IPC_SET(r1, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x2e}, 0x0, 0x0, r2, 0x0, 0x0, 0x0, 0x0, 0x1f})
msgctl$IPC_RMID(0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000c40)=[{&(0x7f0000000cc0)="68ca069bf46eea9c5108d9185ae912485543bf07d872034e7cca08638acd50162eb8c96c924eacf0b5a518d5a440d097eb8860e51cde80047ffd9b6abf5cf5ad50b8244bd397cf7e370bd102052846f3c2dad14e2be517dce9615da0", 0x5c}, {&(0x7f00000008c0)="7f7f115c868423bd21599d7d57d76f9574f0f9a8ec66af83ec89bb36c37a43ef66ee52aae8e62aef7683fe7e054c37a2ace6492f8b03c9e5bea8d23e61a923a640d831604e7a359e851ac0113bce11cff54c8789aeee8a4a1711afdcedbe6dc98e29eb4751799520947eb784f56ac6bb4a61c5b7890f471c23cb2be1acf6de9702fce61952e391dbb7d2354d47acf28b7b1c099b151f4179e85119b255a38d87440aa59a8580bc1dbb70f6e4bf67ef2e844c09b6817353f82ecd535c9dadb26e67f2c78f868fb5893e794247cb1178029ccc", 0xd2}, {&(0x7f00000009c0)="6e187337644fc6502e936234be347bc1de77542283c713bfbdefa0f2ec107b2611016b82e898cc1c7b5cca8e80b9a6a10f1f8a0d91a771db853544fb0a0417036925e025ed4c51a19f2d58f97c04b5bf5bb078404280a88a72ceb1baf5227dc65da9ebe89c25fd2bf45f47a35e0351237a0b4b46ce089a2ba72d9f43894ea543d6bb2c625d8342ed84278b46320f4f18ddcd77749a2753b78b574929483d86b12220dfccce41f029f29b4e9ddc9dfe5e4e6ae201a6d63eba29f95577afac220a5e7673f8b980559192f4a5b0caf1042efef6058041245a8e7756046103884400f007966474d5a9ae0fdeeaead9cd7aed15302a2642f0648c9b8615141248", 0xfe}, {&(0x7f0000000ac0)="8a6ee19e478f49755a52079e6daf6d823b9ce3cdb99d1778ce736dc86fa577a9f6b3258e577b1a84ee4c09bcf3ab6f95fc9dbc8bc2b027058ea420d140c17e13126e06d79a0afba9d64c72b3d371a952151695ca0622f5e16fc54481dcead29edbb4f73edeeb68e6687b0273196d515e2d2722f36d2803543a53097e30517c0892e734a72ca84bad79ac1f54e6d42a6793dd8500dbda195c199b7b40dafcb5c861ba002fd1eacd", 0xa7}, {&(0x7f0000000d40)="377bf3256cdb119662cb4d463c61954735e70c29012833288bd3e538e7decf392da910fb9d3ffdfb8eb8cbbb23a361eaf237549ff2b8480e162f1d1218fb41e42d4c4ad9bd95109c151bf9ca1474cb294bb091578a353253f652249c5b6a328912fb2b9bbed283414017eb8f3c3b01d56f103983ceaffb24edfc7f0e41a5ff137e1363f42534c7b1231c7216e209b373981273751fc38d2a6b10e75f5eda73f596e760c650349c50e8491e661c73ab7219ad25d1dfc2c54c7ae591565089", 0xbe}], 0x5)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x100, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f00000005c0), 0x40, 0x0)
open$dir(&(0x7f0000000600)='./file0\x00', 0x40, 0x102)
openat$null(0xffffffffffffff9c, &(0x7f0000000640), 0x400, 0x0)
r3 = socket(0x18, 0x1, 0x0)
getsockopt(r3, 0x29, 0x37, 0x0, 0x0)
syz_emit_ethernet(0x76, &(0x7f00000014c0)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "fafc8d", 0x40, 0x0, 0x0, @rand_addr="f398ca796e8b2e07296234395c8de931", @local={0xfe, 0x80, '\x00', 0x0}, {[@hopopts={0x0, 0x6, '\x00', [@generic={0x8, 0x2d, "ce22d19bae02919cea1be7d7adc8554f9e527a6931674e1f64b2cd8f04429d5b843f6f6631a0ca969a992b980a"}, @enc_lim]}]}}}}})
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000100)="5c72ee1e803f9e494717baafaeba12b26ba66419063b9150650865881ce41fb49da494f8270b1f2ec26b0261600546132a6ad3537f65ff4ac47b6421e3ed853936893c92552d2c7cd2c115046cdf1ff2ca8081d89f7b780046ad991bd56a722b8da8fe1f9bb655a78a1bb6df9196ec42089f2a2c2878a66c5d12e193a2b34169d3180e1c91b3761ca2aa5a25c0cedfb2", 0x90}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080), 0xe0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f00000009c0)='./file0\x00', 0x4, 0x1238, 0x0)
symlinkat(&(0x7f00000000c0)='/', 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000280)="b2770fee66a10dbb591a8d0302f9", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240)=0x9)
renameat(0xffffffffffffff9c, &(0x7f0000000a80)='./file0\x00', 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x4}, {0x34, 0x0, 0x0, 0x7}, {0x6, 0x0, 0x0, 0x101}]})
write(r0, &(0x7f0000000080)="7c0000ffffffff00001a00a5f3fc", 0xe)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1001, 0x0, 0x0)
sysctl$hw(&(0x7f0000000280), 0x2, 0x0, 0x0, 0x0, 0x0)
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
select(0x17, 0x0, 0x0, 0x0, &(0x7f0000000000))
r0 = socket(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000040)="8904e4ff437a9068e11000"/20, 0x14)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x12}, 0x4, &(0x7f0000000040)="fcfbbdf3", &(0x7f0000000140)=0x4, &(0x7f0000000180), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000400), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x1, &(0x7f0000000200)=[{0x958, 0x0, 0x0, 0xffffffff}]})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0), 0x4)
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
r4 = accept$inet(r0, 0x0, 0x0)
write(r3, &(0x7f0000000040)="04", 0xff9a)
sendto$inet(r4, &(0x7f00000001c0)="1daad5cd36195d6810b318270ef9a4e226c2d78a5413faf736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8ad376f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbaeff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e7068a0d0a3e839ee05e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e13faeebbccb92c28d329fb8635224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70dfd265fa833f7f7788b351b9a0abf03d9e21a38de65ed7352c75d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc3462aeff27e94fe6994ffe7086d8f0c631b96880096d82665ddc95c8ed5e187c85fc07bee3f65d4b91d59436540aac4eff6f5cea6e4233587318755e8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc780bd319a6235b800800000000000000f525897bfdd75d1f2d5a302b3c4eefe2f5ace97cade03c418d91b5edd3d278cedcdd1e7d4b95b3a63cd9079888381a65a8789264cd8048410f29befde650c4fa5dcb582718b324bf28706d04a602d395a0ff0aafed57c5dc7a2d17ef96202dc44790cc423511a5c2c44c1d6a2cffbad9e62418bc25d106ac22145de479fafe620614d07d935a3ae6cd295d9ccca947a6f483c71d2e04af4a8ffd6a534d183eb7d39dd34770f37290a93d68882c6a8d220e5feb6940b4ea75e0f94abe6c4bb9e905bc86656f854a277e6907c4478d74d2cf08a56c02d6c9586397ccbfbc41e3789060018a3c7047ea96afc10d610015f6861ce8e55f4bb8a90a29627ac549e3b", 0xfdf4, 0x405, 0x0, 0xfffffffffffffd51)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x800000000005, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x1]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
utimensat(0xffffffffffffffff, 0x0, &(0x7f0000000040)={{}, {0x0, 0x3f}}, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000100), 0x1000000000000161)
writev(r1, &(0x7f0000000040), 0x1b)
r2 = open$dir(&(0x7f0000001240)='./file0\x00', 0x0, 0x0)
r3 = dup2(r2, r2)
pread(r3, &(0x7f00000000c0)='\x00', 0xffffff78, 0xa83)
r0 = socket(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, 0x0, 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000400), 0x0, 0x0)
r2 = dup2(r1, r0)
ioctl$TIOCSPGRP(r2, 0x40047477, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000180)=[{0x3d}, {0x84}, {0x66}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @broadcast, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @remote={0xac, 0x14, 0x0}, @remote, @rand_addr}}}})
seteuid(0xffffffffffffffff)
r0 = semget$private(0x0, 0x4, 0x426)
semop(r0, &(0x7f0000000080)=[{0x3, 0x0, 0x800}], 0x1)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, <r1=>0x0}, &(0x7f0000000080)=0xc)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r2=>0x0}, 0xc)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000100)={{0x24000000, r1, 0x0, 0x0, r2, 0x1, 0x7}, 0x8001, 0xca5, 0x5a})
r3 = syz_open_pts()
ioctl$TIOCSETD(r3, 0x8004741b, &(0x7f0000000680)=0x8)
syz_open_pts()
r4 = socket(0x2, 0x1, 0x0)
bind(r4, &(0x7f0000000280)=ANY=[@ANYBLOB="e72550a6218e1d4e8a09a5466b34b6d49a66a468a3a89ac69f4057fdd6649530bd2ff01fd551e06a10d5f1b487e5361fcef684e200ed9a2c8798158ae5d043ef4e959413fe248153d1fd0a215d1d62bae5eedbf56c49dc79ce04623747220557ac622d0767d05c21b156ef9f6c12647fc2c04f04382339d283f85de7d1f568252b1516534a4e3a3d5288e461ff3e3bfb708d44d317c94b6768efe2e40860445ed46002ab5d5f71420d80ba74cc5a5026a1978517cda90ea1d92534192f4c2b2234e55f72dc3f90c03d902ea3986229eb5ecf3879aea36cda41ec1bed5728e6b4f61f74a45877fdcaf8436d200a535b6801be4ce6099713afb834400407a7a142ff102b2278bebc4ce4314f7defd40eb11ff7c00055d1ba730d458b095dd60078d088feb2b95828475dac0406c7a833"], 0x10)
r5 = dup(r4)
listen(r5, 0x0)
r6 = socket(0x2, 0x3, 0x0)
connect$unix(r6, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r6, &(0x7f0000000180), 0x46, 0x5, 0x0, 0x0)
close(r6)
write(r6, &(0x7f00000012c0)="d542853c7d630a991ff095ac454db8e699fd30f7e6f5552158c05a8fb9ea7945d1451109fba4992329afb3f6744fdcf3a52e84bfefd31bc9f99824f208c348da3454b814b98549ec57518fe8fddc2e9129f8ba995046144544a75ada3c7c40c15d6c95163bf35a24a1b68a0d99acbe700126eb0569e07c070712918426a6265147ec14cdb2c5c04dd0e249ac5902038a2b0881006f662fdf1c23f299eb1ea3d8190f398a572e3e14dba99bef4bd6ceb8f46c8fd81137155defe1ce48c654a2aab027565b1eb568656b810428d24d81fc82943aa87641fbe57e8278f06a8d4739ca38571e08bec98bc6870d4f4bf68705db1ae0beef1b5c9c0a0810ec7800855aabfe3527c92174a55a9c2246e80f2175f46c9cc4578e29cee26f8be639057c25952638413441f352683ffe0d986b2cd90869066b9fd7912572b8b6fc3ff245aaa4b47bfb897cc7a6312da6ddee9fbafa9edabfc3cefe919800fea64211768c7263613febe434c5c1a8c5af18415776085a2573ceb30f1de5cd18b079a71a927d459f840f0fa50e1eb8b77d56bd29fa24a108a05c47403dedf802a2e501f1e14790dc7ee90a55cbdcbd26fecf35fab07b5ce7fe5432eb07cfd6a73bf0642c3d83c07f6b718de42a705cda72940f3772e753f741b47121ed17ba850b630a2a4052389d1e6efb03704f124e24ac863f8fda22837cd2a1ca357096f836ee20ad2c7017a0a746ce5879d8e60a6679b428b508e507145782230d00019dc8c9d566759aaefb76b854af35cc6efd37e884ac1f4f4c72b5964ad9aa1c0f4114a2838dc7d6e019d62a523d668f8eb012bc3b307eea057d6c1ceffb24ed6c4806e4ed0185903d24163c26d898b18895aaec90c95d2570935825e039551d8202546903bb9c484d6b2f6e724d324794810e25c9830e37acca5e74e32a6f08b7ddaa861a06042f795a1c5ae1902d953f88eaa0c12f820b404b75cf2f1e9b108417013a4c75bb0ee7f1fd3a478c10a88660b5d49c239c3ea9ab85d4510aec3283ee5e993b3689b9d367c5f479326ebef9043264650eb56c5bba9d8dd52ae28d7304e810ca2a17a8ea5b0ed400dfde111931e781c4ef6620dda778b97ee3b669bab5532dce56d365303c63320ceb89798106c69a165762f82fe8a3ecfa5094b34ee6eda176cce5e987a391bd54146470a41c915f980a92a3f1c1d8c646aed971989f9d8bbdc18b29e83ba5353d63a07f898f8eeff40f767afcd9e96dcb50a77a21147be4f20deff24ca9d58d67d38cbe5d800fecd42f95a4b254d7cf460472a4ff4b7d4e180e2eb3ec27c6e7f0aad1a0365dba9825d501a309381deb5973282d93e5cbfb174ea2e66c80375904f4e043d533304291c37d15335bbfb1496571ddc136da5cef2fa0e2dd2eba947a6028a4e900451cc3365a2ac274c40c34b62067c09cea335647564549daa3a7e3a1069fb472d41d0a7aa6738fe8b040bf08afb4b855214cad017226a04918069da85412442c496265403e265d71f0d553fa0244fe8d69d1c457a99b954d579fef6aa0a702739374e006a6086d047632878a84fa68da0bb20dc57baa439807a71fc086c07cfe1951b372ca7031fc9227594a64a21c5158645569a9247672923d1d011d167858fc0090115c3b65013f1f6612f09b007123927a9c0676be897f290cac9301551108b97f96f933305843b5e315973de83d42a799aa653ec7207544c4f225b1c33dd25bc574741379c5a5189359126246299c33fe2d0bcb8162c932fe7cac065fcf5ca3e0fd674cac3ba39dbfe968df8293865f22c765d484decfa59c624b5ff0aa3dab62d8539691beaad2c67eb18ab6b5ac2092c6888e648d23f8cdcfb135156e2abe6f4a3f1cd2261e6a1089dbd701a3c68599eefa128e22656a07c2b8fc4830f2cfdf7ac3055532db2dc9d2f814abeb845e9ef8af91647cd87612669b9e2c505bcfa173e65951bc51858ad5294175cf2be7a886d8faac942f9f12e2c4e5a9e2eb6ebbb861d80d195e8c8eb2b56cfd65fa73cc75fbe3dff7bf9434cb8294b891d0bb9d0d40f737974fc5d7e61e2ca1f29cc67a0ac0e16ed58e8118849421cc772c37b610256118b18e40956b6d46c53f22c468b567c0bc4f530dec20176fe35616716394d2bd1f58320f367121ac51dc1d74b20fb053747223944890899e3d3c2cab35236a6e0da2ca2919c58fdf45bba654fa05bebf16c918aec7a7c286bee7f83ee310dbbce9243f6d5d3ead726cefcb19ed0f2c1d4a8dbb98ca392ec4d846bf89bbf418bd87522e9a20272a79993263481635c00cdff8d9bfced071ac867f088ed9446b3f7914bd977fc21b54f34e9c09d1149a87a26ac2ef2edfe76e6ebf329128e6ac356c9d69839a1526c37313b4b0c56553f62665b94ef72e59b7df90d9da79737e69e75529461eb8c6eb6ec344dd8119b784903f3e6e855ea79b2aba444f39447af1de01fde7cc7c27b17324db81a060359bd4aaeeab42b282e59ec23fb2d5c4f0ccc1fc6eba1fdcbb17e006e8910e6a0a90469f813ebcca7376be5a959d0bab42e0acb82706a9dfce72db80e4c6569b62328e82e68e1b3e77531d17166cc86e193885bce1ac08d90a08b05b1af76befdb5d1fb2563de90a440d42c992ab16545cd858ee2b7b823218b0585ea6e15020c89d9c56f8cbfc6c871a24d6dec62169cc51cb8acba03d7f5b286133a09b5562d2d0cc9d0c8a6730f730c31d0c0c5311306cadc0d4d37809b6e9113445cc7f23f83b57d107714552116ef366e2932a896aa82801928220ef4304a263ef77d22804e2e5bfcc3cadba56f33cca92e65168720941e1c4c843af9f1d6a56a2ecd6113e27850da10fc99062d0945f93973a023942b533377d5ef71e7f4a98350c28d1a8cfc38190a285a27173a9b1c2869e4e62412796645925dbf614695b2010a7cb8cb1b63a39408b3e019be907b79bd67bfc7ae31dc37bfa88853d923fdc7e212f2ed587d8cacdcc1f48ed5ec7cc7e8b3eacfbb551c28dae0bb796ea603d44bbc6a8d14b7b82730c0cd056ed850adb05bb8fd53f668011918c4bcdd5ca2ad8cce029e31be43892470006190f5a7183ac311ebbf40b0a6ac815a2641f8e36fa1b7f2e7bfa69599e0ca7ce8f2fd0be00c098e07f8d856c28986fcbaaa157aef9c8ea954aa102c06152929cc984abf2c8bc3e677ef7a3574bfec80f4a41d9d363faebef157403d0f3a82b1d0ddd9de7cad28346abc5ef903b24ef53d6e3481058fc8df960a86e822facb7aefe102473af7fa63b8f25edc7af5b4c8ddf37ed8f44a056d2f2806505eab7cd7621cfadab595422bc6bb3b32105bcb2ef75bfc58958fc0b17d125d2c8052e7c6d2300d2f1b24312fcf0d2f692e9aa883729f44758dbe895cff39b37907f58218175f180fd49f0fda6548972822b53da7eacd7d24b617668ee1e5825f59c4f08df68e5eb37f346e518aea0fc90406c5108b6752a51dc72888e3e6e4b49f3750d22e52db23a4c4f7858876926e4dbd1255826edd5b20e2188357cd686516995208af0817f1ecf63953c61d46da2fee1adbbfdfc2547472a8e0e341b042dbf7cf6f8d2674ee526752b589dd548d749fc5f142f42226967edcbbe9e7dcd60d68e365e473cc6db3be2673622613bf0a31eefbc5161ff9b46d78489214ff129756ab23fc9a9cad4e0ac135f13d4452e489957eae1787199816b934a427954c3eb961f8383d4535cc35a354ecc045c4f26fc4cfcb8bc35de1983ebe1bf716299188b1bfce1dbb6cef7294bd7f68a0090b65a4f36ebd01190a4147daf5d8fc3b20d6a557568b48e6e21dd73e2d13b1ba1ef24d44572a7c16a582274bba0876aca5d60b43e677e42a8dc1b1f84448f31d9f8fe2da40c456bf7f513f849a302eb99f0e8a27c46abf8b0b8cab15e74e25697e2f5e0ed5fbaab57ebaf8d7857450c00eaffe7c7cb576da8f378494a0340256ff6d41f5a18609e4901a6a3a752062fe7e3f1caee44ff002229ffcd4b4c24e9f5f9465ae1ff9febc0a42678b1d824229c4335bbb941fd54c758843f0280c4a73d273d79fc80a37d2ececb9c28d317a438d674b765c77e603c6c04b13e0f310990e7dbe9f566cbe5ac59515495f63a878bfcdeab4a0cdc30e55ebfbf7b07ac3912f56419fb33cf6afa6e4dfc3e585c8fea4883d07106987b6be5c7d0e86210b0c86fc059ca352d3ee66a2df44407a40421cf6c92f846449973ae6a1e658d971a5374e598de33afd71d9172ea131f7b20b0311a80a2968c42b4e8f3ba4cd2168517edcb664e5318f7592720b8cfb69ad7f2e7ebd549d34bf29f9dd0c83c1854d54d6c3b079e3cf5a94a1b7c0787b94e8919e504f60488cb9aeb9879fe2a1079ea0ab80849f1db19b20f44b2344a22d275528a1ad04a627ebebb233963d98645c7217ef1f1b8b566449cf2e793731e85f166b91d354c64cf88a3a3f92439fe1cefee353589e6a7e9e8c72938c24fbbdc92e01e1e3a31322da7208ec08776f7322b2aaaeb9bfcac0c7877bde249e0caf9d6ae7ca7a4e3a5fda34ba682ad10129e8bd7849f6eb1e97732fcd678111361b7d39c5e8f43dd9f88890241a07bc04964f42b1e1c684bb57b22b2f79a4cded6f03132df90e179b9e97dce47dd49e09d1f0cd60b80516b73a81de7c865a217db95c993814240cf03e18618369b028a0bd13cc717bc3147f194b64ac2128c69c3821f33ad217a6f1c7f9e85653b004b17eb27821812a80349f3c390fe90c3a12dfc13dbf35a84909dd3ca6ad9f510a92d786a58486b0830050ed6a96ab0b1bb1e9246b12065b9bc10230f38153c3196b344f4e73ad8784ead0318894946a077402d04d2dfff23cde6a70c12c6f7840f73a5d28c2778ff39d01ded4b4606725f9b1acf6e6065a4dcf900d4d0fcfce5c9a47136feebf6a42486222c711743aa8128b7d7624c3b04353049b49f0188b721446b47bb774885aaa98434e353953672fbd0eff009c9667527e6618072b615548b10022697f88b144824e4ec1b431ba23d2c0a545adbf15704ee1302ae994d05278c5e3853db0d76fc41c8f1462ae00a2e0ff30b33aab5778b8a0864b5e1781a00ded6941845ffad1ef39c30f74f4e20c6389b599c99b43f908ca8ab83b9dd96b7b21400a65aa9d8a9942df18b92c5c643056b52431db4db7cf7ad747a33ecca0ff4e0b2ed2803f6b2ba6e2ab41cc4ebe53d5dd9eb31dd001a48eeb1034770b1f02b38bcdb29f1ffe698b651c5fb42a6e2e9f85da6f8c261b88fd81184aadff8a5633c4d60511e5a433e9dc6b78f6f034f96d8f01c214309f30fc5cf6b3b7ca89d17a50c98d522ac1f1910ded7c4d873fed74c65d9b27ab7d750a5c3a3da0c2da426fb9adba5c61281a05d1e63bb97e04d62be7fce3cdb3012ec6f8b6ec8542e41c9048ef4c88ddbcf36ebea7bc0acafbcfb29bdc897b8acff86430a91c1eb2a6e233b11279cf56bf3ae3d00fe02bf4f0a3210b624f9bfb9969cc1e30e4130eb3fd30fa6d9ee0428e7ad73711026d0b82c80459643e729acb92fad638d8d1dd19880cc830e7465a57032416f87069db04d2e278ed467982075513fd465b467dbd60cc076eb943526f4ddd8ed41c6e99eae03062b1760b98ed3b1039047d5a56f1f4c4140c8ae28c7708e3843c23665fe8beb155cb2190e2f7d5276b202e9262347437d12c99470f00c0c990f160cb2bdd6a1ea840cbca208532c2cd7d55bd34c18d06373ded9eb4bd4c59cecc9163aa84873955bff8081c0135671591a48853b528460aa75726162533e5e87ce19497ce5", 0x1000)
poll(&(0x7f0000000200)=[{r5, 0x8}, {r6, 0x100}, {0xffffffffffffffff, 0x1}, {r6, 0x80}, {0xffffffffffffffff, 0x2}, {0xffffffffffffffff, 0x100}, {r6, 0x4}, {r5, 0x3}, {r5, 0x4}], 0x9, 0x6b46)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff})
sendto$unix(r1, &(0x7f00000001c0)="bf4ca2e465b8cebc79a52ad18f95355965ce6a29e7985c1b99f8980d00512d9278eaddd245c0473b92257eabb48f28f4ac06a8db164277e5763370ee106c3674ddd9a8caf81000"/85, 0xfe5c, 0x400, 0x0, 0x12)
close(r1)
execve(0x0, 0x0, 0x0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
r0 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
r1 = shmget$private(0x0, 0x14000, 0x6c8, &(0x7f0000000000/0x14000)=nil)
shmat(r0, &(0x7f0000001000/0x2000)=nil, 0x0)
mlock(&(0x7f0000002000/0x3000)=nil, 0x3000)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000003})
madvise(&(0x7f0000010000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f000000a000/0x3000)=nil, 0x3000)
shmat(r1, &(0x7f0000007000/0x3000)=nil, 0x0)
shmget$private(0x0, 0x3000, 0x112, &(0x7f000000e000/0x3000)=nil)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f0000000040)=0xc)
r0 = socket$inet(0x2, 0x4001, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x4, &(0x7f0000000040), 0x4)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x2005200)
open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000000)='\x00\x00\x00\x00', 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cff"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r1, &(0x7f0000001080)=[{&(0x7f0000000040)="6f8678ae0af456c40c63d514fe494d269e0d5c54251c4198095b039b7c2d9d42749b933c91bc7896f0b31d5c789d6c3428863dbcae931862d8f961a38ba5e018201c9ddd8c9026e55332ee51f8e1d3e7011c084a14cdf9e42ac33d8f8d229669176b6b16848b74b1ed9f0e611ae9409afee3fb7789a415adc93f946f2a46ec386dfb06c4ac24da8bdefce43534de9e9bfffe99a344e3cae8b0df79415fa416aa72d1c1e5f6cde05e2fcac013b0bc8006362245c5e2702a01455eb5c4b5ab860a2567739b5c1a8e6c0d5c4a5493f969d8c6739eadfe32725de5b9dc37d590462550bb7c47d451f67c0658602021fc95a13d008bd78dd92312a04a9d9f96fe71d76c39c860e5ee6069484c8840ae35eafdab9d027b1c286948948c2fa55f32ed7f7a615b8a41fcf6109d42449adedc90a2dfd381d5b242b0527e77003419ed355f9c10d53c8ce75f9878673c80be42ffd6e7b846f5117bc2f62aefa9a28c3e46b1c9b6de7357efdc5adaee0c937c823969f4", 0x784}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000500), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240)=0x1)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
writev(r0, &(0x7f00000003c0)=[{&(0x7f0000000040)="fa8abf550fe870888677e0aa2944c0b9deadc6f546acbd863accf17b6fa2e5797935f7e6103db30c9a35f58e88eb35b18191b3f4c78237999c7b8a7c6ddda6ac088cdfc8c5bde3e2cab774e06842264ef0035650dd33e342efc2ef0862f6c4037fde5ca42fa249b15f6d516b332b1a5496e0f8300345bd675049467b7d92cacd28c4ba0d4d045944ec836d44c9d5acdc23f79d220027bc5b2b4c11cacd3d4b7512b2de46ce7ab54d98b237fc06016a1f83f922ce9f3b80def41684fbf676e63089a446bd898d565cd75cd5ab0c2706bf8ce1b05edb665ead6efa35789a926614bc7689", 0xe3}, {&(0x7f0000000180)="85305e66e0b41840f32e10e25ed36304899914e4c7c29d96855a2b3fcfd1db494936a995dfb8b2f9f1215e8fd6247c3a3910e0106ae2b743bebf8b2065d0eee991064d", 0x43}, {&(0x7f0000000200)="2e3547538f0466b92332765f04cac5be5f351b6931cca5f2bf4644f3a4761f2d5695786f06c86611f5ea5068e9daabdf00d25291f6fbd835a819703437817496f28e112cac6d420302bafabfe3f93c3139eea6773174ca884f05723ea509f4f53d2f9417a438af2d4f5a47dbaab7ae6db635ef0e088f3965682620c471fedc6b36ffe771951a741202ef8b978f2838b451022c0ddb30126203dfec597143b1a0949f8fb41e1d4f0fb894fd6e9e68fa3f4ca467ec693b55e7137679", 0xbb}, {&(0x7f00000002c0)="da541a6855d0a2465dfc076fb507422a85f87c6071ad6a310a6ce3fa24b5d0d7d819879ec6c493a30e47e3eb5e012cdd76c79aa22ef59dbf8dbc5eca3307e02ff43d93b5af0417951ad3d836334e762111ce875c779baa44c9013054eb3a9f55563405edb178dd5624d35e20da3b7c67491b39d27973fac61d76fa6672d6e0c984d524c18b303fbdf01fe266a0aa0029fde339d7088a62a3545341b6c695f5d987c51d6fa02809e569910a869e0fba308661d3e3dc9ca3840074eb323d1192", 0xbf}], 0x4)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
pread(r0, 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x16, &(0x7f0000000040), 0x0)
mknod$loop(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x2000, 0x1)
link(&(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000380)='./file0\x00')
rename(0x0, 0x0)
rename(&(0x7f0000000200)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000900)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')
unveil(&(0x7f00000004c0)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000340)='c\x00')
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x100, "d730c19e79000000000000fee40000ffde00"})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
socket(0x6, 0x1, 0xff)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
ioctl$BIOCLOCK(0xffffffffffffff9c, 0x20004276)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000300), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f0000000340)={0x1, 0x0, 0x4, 0x0, [{&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff9000/0x4000)=nil, 0x9000000}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000c9c000/0x3000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ff9000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000e7e000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ff2000/0xb000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000c00000/0x400000)=nil}], ['./file\x00', './file0/file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file\x00', './file0/file0\x00', ['./file', './file', './file', './file']})
mlock(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000029c0)="41312531d04433fa74e4e0e359c25918df44b48dd405f1f0343e4f4c186cb4ea6fa639cd0eef5627eab0a9ff7191aa235c4939d4094ae6dae464053dcb03f68cf91b1ae54dcac269f755d53f990c80b76fbc044db6e20ae3a2ca922f1e5ddffcb13c2e8065c4dfb2af4b2c14d47858bba46c0142a0056b154e6503d255347b4c1218c6e27c4670fa6edbb4512af320f5ed91716901c50dedeb05dc19d9e2a3376b5815a7adb6d8af947c2e9c79b52642bc9e33322bb2e1e4d1838c51462d36", 0xbf}], 0x1)
execve(0x0, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010000ded559cb00"})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = socket(0x18, 0x3, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
dup2(r1, r0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000100)=0x43cb9, 0x4)
writev(r0, &(0x7f0000001700)=[{&(0x7f00000002c0)="eb63cec15fec436b2c446d9535e3d38ce55d4e8082b729a47b93e2e1b0be39607b2d6a33cdcd5cd3e9f59f85dddc33987a2a13c15c04c82b87e0b14cdfb2722ef23738f19bd65da545c570e819fd8f1218469cac0324605613505e9f6894a8c0cb6ca69e198b00fc878f34d5df52d571032f1f4ae47f99cc85915d1029916622e3751623e043e3f04cfd12b60cc11a11", 0x90}, {&(0x7f0000000540)="12b8bbe6cff2adb66003052f6586ebe3ee40d2d213f592a71cc16c8277487cc4d1df62d5777ea7e8608c76a4ea3e0c45a69acf3e0cb0213303f509ce37561f247cdd98a0e5fe2ae574437ce8022a8d5149380b6c3e39480c914e32bfb93f86cda8c0cc", 0x93}, {&(0x7f0000001a00)="b100691c7ce79731efb263c35897b09207b7be586af75d2e40b7b30e7e50ab70d04b9e77e2bfd9f5ec32b094670599470010000000000000551f52b61205a088ee0f0765019a796c4941447ce62fd948d23e01a2c7aaf01029ecb4a7d9dfaaa53c5cb552935d20d9a113a26b0efbd7039501213f63355dd56bd538efd4229156bb5484fee1d5d8b4c10d84fcf17d5e479fcd83c02981cf98712bf6ae46d2b9a08daabb9fb69ac1f6e36ec413be309c000000000000000000000000bbaf0d4da3e6536e1ab5222270cd457af82da702c3796be0cd6d3dcfff7071db68", 0xfe6a}, {&(0x7f0000000480)="4fdedd0a22537467a0db20a3498134a7e8dd74c1781590acbcb7e6eaa2cceec00966e2125f097def3086a1572c669e7aff1139204f3eb28c7b8b2b9ad42755623e7248e7d291d1d9fb", 0x49}, {&(0x7f0000000500)="73b9a3440b257ca0c50bf0d14d02582e63ab6424a48e361a63981c4d6a1225b0000893ccc0afd80a47c31247eedfeb6df13706e4f1b5dd", 0x37}, {&(0x7f0000001940)="2dae27de7f66d9623283227b325039985678ef18d7f7f2cf2cf917f1aebcd2e72d1e17b80c89e1d98edc6abcea3f028495f124ac648668b11e9c696c97fa60ee9394c4dc0923d2d1d9a7dbf281bb2f4af92faee84262ea13a156cdbd910fe850c742d6361c37387cced92a037f94ad14050660d982d3b616ee0d2d73c95a1554d9c41f824d1facca780b161c3bb1f418f2f9457d0eb81d7395f503eda69f4958c6dd8c6e2914ddd905df", 0xaa}, {&(0x7f0000000600)="8757247a66082e16b36f72034a7d237190339f9d52f40f33ec6641cef47bca73456e61c5a6b84d02dc3ba2d8ed288bbe4925566901fadf697b12d9c18804e871266661aad2592b2c37a77819bd138cb223c13a222b5aa79ad13cc39a6fd6bc67de547227154559cd32ca9cbbc934b583e90e91024ed61d598eab014e7ca487abf95b4ac9edf70ca53e3a76409375b146647877230fc6aa0284d81ec3233cf15aa701c6f02d0e30264a684fb7098024c4bfbcd1777b35b0020cd0b9719c386c639b86affab753ff22d0de0d5c787f675f34e7417225e001ed253fc412c611c375ea9a7995db95029dad24f80e7bfe5452988660ee2be91a9f9f0fd8c819f4e5c37e540a664bda32bb4215f5a41c1b8661941fc84f70144ffecf54fc1dbeea34be3287160e6166b340d0fa3e8a1dd359d59598e2c86682d8f14b5d7049c1cb9c4c375eb60325b8e1c90b48009acbeee6fad030e450da59fb83589e46f219ce4cad016274c69db786416ff99226d9e21fa3396f254b2241b09140eb6ee0c6eeb05548f4ff634f512f4cbc3a1025f20adba20e91779734ff1dbda547cd4201424ebb8e288abe11eeb218f3bbdf03b5d91adcb0522f74180c8e97ffad9ad2da84b274fd57134a67930ca8f5dcfdb3898de7293682b3cd12705f8904a96ceac63469a662b87d3296d2458755d65d617b33a8bdd5d92d0e59b89e256f7efea0ea6d800f81e27f8f9c132c7c8ab3b02f96d785f4abcf1c4d3c81f669f4bfe3cfce1f9a1b0ea825cfaf4a9623b4e6420a4a31b38fd48660db2ee4b4d3ec9a7349a1390e0bc9ae8adb0ee390910ebff2654daf713c969025938f93e2f6a6e54de0f6abb36e52f5aa4a59086faff3cba291703d40dde1a1ffc7baadea72a7b0c787a2a45564119a35cc0470438ccce3c45c0851adf97b9e5d015e2ebaaa5f96fb9829db7c0209703c4af0602c55651e618b558ef4717dc84ab619b8898455b7554fda7140f930237944ba71d9c0619b68640690079b838f5a612085ed9b53f72a7a581138e085804ca1c51e159ec37aea005f787bce1edadc8cf941d4c960749a819e6c8e4fbe9cca5b1c8adee98ebf3607200f3240cc535afbc54d16384d43244d67033a207b5bcad4a03ca3c748021b5c4e357fcf024f1a4bb0b137bc6379f8081b0ea69e294d1c3b70308161f7e5a9ee48f58ca691299cb5c5288330a1e7880b593272f091a1f6706ec24f240c9c8969fc7c0c478e1b451b5fa9073f03efe9617ba5fb7264f8b0612d6eeebb6de53ae45bfc79ebfcf4639b4edc628fea3c2d8527a59ee55831a283b93ff8c5f35f31c78610d64c32eec016a3ece385f3ab117a5aa23bf6d3f28ee76d9ff9f87de8dd2f31f9157a68566e9c856a031aacd07b2f53b7e6509083f2aab34fd2aad5631c1d8533b34772fc1780a0e2dbbbe591bdeb42ad340af6a998ed7bac59c8843f774f5ab8493ea2ed10e267ab22dd962892108bd3843097df9dc18fd2f60b144bf7aedb4e2d70a32f28dc44ffc5a771b5ece291b8dce1887f82b11ef5398d92c0da733cc4cb7c78575b76f40f56f1dff685945cc8040d8911b939c8f1a50de451171fe3df271dbfadee32f1642c43e3ef6b588897981af0b3eaf566f6cdbc2545fa34922f34cff9c4d3de45e141da55e02fdc1e6907439986d0717fdf6e704fdca27a3499e234757dc531179b369fab404027723a7e2942dd381a1f129bd651953c82b79a801d97d3b949955812a1a8456f80d3865d27149e22720dc9f7d0fe702c848001b161efee8450d96dfa3140d1fc46f90136bdb8ae4608e1cd3740d9471c127a371fb2d34060b2a60430af7fda8e51c19bb279611b31ca54416b8379fe8d99a1936f07bff241bd467c6d4a023fc650bf142fb2f0efd0eefa5a9d88225cb54c5a0d86fbbc640c7cf2f2bf99012c4278e0ca14891942b27800d3f8e4b016fda6a38e08d060e30d7e87de2af351099f7f231dffa37889828f6e952c7445ff4cba8e08669a6a0bfe9aa69eb230351e4e164239ce5e1d1600440fc64239c0ffcf77d1ab10996c353c959a02100f58d8e8fcada78a73d56bee33f466c0423b35a93ca47e810c4487f997c326519ec63af6b827b30de4154734e7b883138087e329ca7c4c715a1b783a714f6f59e10c2bf6542a1fe6099291713e90d133ed9171988356cf4e69ab78c5eaf4c5522bebebc176b5b52df3a46228cd52a0fdae975d09bf4bd6151acfd0b7b735585c1754f52de05087e3008bb19cf40d182643ae1f5c4b84725ba2af077071d0d6158319df48f8fa8f187da5545187284bbb44d8797c9c312908b67729251a2322b08e0d4227897a27bea0b0fda49d507dea3e3e49b77acd508ea3d2a165c78e343d9d2a32e2dd726be9ee1aecbc1c90df2b44f010918bda0db646b5798cf55365b8222f45932b3fd6b899ab266f05873f0ff4616ff7cbdbd5c54921c49edf9720aaa432a78e32d33c9971c02e7d5f8e90e277e4c16e89e75c8206a9e92579c9f95e6961b6bf4834838f0be0eceab950bee0a8bdafa62bed0c545b8758c388dac670de2ed1990bbdd4f89146e393e4fc8f9ca3c1286d791135d2c5c65eacbe256541a5f307dddd46a8fe2ab9f243c5802c285a888079aee102e8d2ee10027562074a6715aa65554cba301350f60f523e0816efca36f2c2301941ebbe0506f78c59fb3a1e67cb3d37e3a36ad2a9f47a5c8fa62a8d9adbc81de92f7e92f1bbeb1abec7ec7d0499768efd037e93a29060212cbc5ffae07e3debf966a5522768ae0f4f792c7132897255ee97cf8968f18cc84c4a68382dec36b948a7187db8a5f6e3b13261fd00ee1be9db783ed3a3530d75c495f0a37e621c3a3ad7037cbfc823fd40c0189533d7b0c3b5baa01daa2d34400c321e68302d91f3441dfc411d8284bc60cd3980a6ab54d277802ac7768bb7ef9a09ac13d80e3e893ee13c19407b570b4e2e63ddb8e0952c1e1ae80dfa46644239e5c9891dd15a29b8021aa2f3639334da0d35857ed4a4cdc70c7b64604e924795f5dbd9ae2dbb259b23db8c16e00317903402da8caca8785e0ce64de7d2589789079297282716144dfe733b8a6029d2b8a6fa1fbb7a2c4f00e9ba302fe6c8044002750cda501de9ea9fb2860313613637b0d53f42d978cc03770bfaa4ae91faca9352a6edcfbb9c5ea50ef9af8214af78da57ab5fb6665e78e957162156f673cef071aab47bbc6b777e4adf396be241b262c022865cd4882428f2f038dc58032885b12927beb2be7afc6f19a99f819af82e6264fc5b6d68acdec823b02209c3cc552fa87872872997c1a1ee655523d8993e3c54a20ae27b23b0a26b96a009e8edc513244c41c8f2af25c4d133740f5da0a2467f496606deb9c3e3068dadb3511482cbe42ca9c2b0222394d327e82859b87423ac72b9ea901d2c6abf1ffadd93b334f52a4805bede4b23d344f9a6f8bdd92ffa9975066dbf1277b7657f4fe156fb132f7b99695c2f37d3d8ca32a448d04ea8171f712f9dd719fef0a9955552694d267b0b320509920dee3511019b3aeaf505b1b9712a079f3fc977b7ee2adfa355a368f28727d44f8df77c49d20d70b43bbb6e75432b09ba6ef511ed6f998c0ffd125ca0a1c347ceeeca7a1959e7eba87d55f6e4b29a14a1554a86d80a3667cde561678f503315b510c55c81c8a706baf52d523097935b62a2d594454272cbe76d8ae2bab6be451c52262d6133ba9e4aa9065b47fcfc057dac4f426efe68b51c90bf0b76adda5a4bfbb60501881169141f4e037190807c3a314cea0a5fdfd8c3636488fbbbb04229725a45aa3a77062bc21611ad0bdd32dcf83331ce8b5ee832a3a3b8176bcf3026c22d1aabc91283bb633099ad4f3d37233bb3373819c5ecddc52981c54c2c5538fbf48a0689996de29d500007b8da7d679ef33c1de45a33c4f1879cdae0883f559db304011718fc984a50c2246db477d4394827ab309a11b1b7b0c748c66114f45a217b1501d0d0c3bfd3529f7a198f4b46fac51fc8eae1b63bcffbe3b46e3672466344355b1fe5e0a83d0cc746b7918e9e68db7dd7be1ad4ab66d707ab54ffd2e1edb5cdb024b37d492dac68178b466a454516653d8cbdc7cf5b2f8206aeba807a1c69eb0e8002ad4bf22407d21b43062ac3d9b52391d4356a4ef67ce317aa14fcd23b506082b771029fb92a069d2b65d914c5db4b445ff3f1d63a5eefd3cfe102bd0464429bbb88d5e923017d72aee9e9a633a7587d63983e2f14c55712ee5a47cf875e21421ea451e8d6f37e36bd736466b3aa01662d2016937b490d77b8c88b713836c3dc180df3eb2112e04e4752cc55188351f96ef8ab7acb5b8ec9e3aea812135a50786a6059cecf387863392ef24aca116b533a1eea3bdf84a483ab9c397c0bc7bb03d4d6bbed896d0ebc0d763bbe1a325f3ac33b83112f94ef1a071f851ab1ae8b4773569019dc1a32b50e6ec8f3da25673d34b190a6ce4d2b5e5a3535c05e6f4c29343b1cc63fe21c1a500fca00f47322754de80eb282753be8d153cb8644a25c2a4cca62bc7e8e0f3f7a4ce990187dc22b6a24fbe5596fe84cba7e8c0676f995e98f08fa4c1e375af5517c31442b03ccf7b74b48e060d0dcd7bda9a0efa7bcb8c19182e641a1a446d34c1fa631d9e63ef9058109dae491a4c15f30cda910ee220792f036284086263f53b1fcbc40c394ab0fbbb0ac038de210740a7fe701e196d4b4d22845271b98c5ed1e62df4a2c1f3c56308ada941c65cd9b34237d5154abd836d86eeb42985e22ea4a8f7699ef21eec7c65e58c76af54d11ab2940c0324a5cd20e8c46359c4866ef4067adc6e2489df82a065761a37b47a9688f4409dcf63801f485e2464867baa9a030fadf275a2ee0700a18a9f450538c97730f364f930756f6251b79c82ccc7670bba7041c6177fff7313ac429e20e115b399f5ec9a276a9b352a84d71fb2fb6ffc3a36cb48653b05f6329cf24e51575cb999a303984cfa5023e6ce833ec20dde76d7aa4cb4e8df7fbd1ddbd85bcf0e748f2e2823a68f56b0821c204ea2fd027394593855c3381fb83c671862f59f621556e0120825e5034e4a5d3ae81ff90fa5eb0b28e595ff96df3013d0a7e2d862034eee93e6c6e3ee0a81960ce13532340009ad72853bbd8f94d6be934500f17eb75bcc7ad5dcc39fcbbb2806aa8b83d9fde0041c3112bb9547e8857a1c3662b1597537a07165bb3fd51f9d68fda06b5759cb35a5575303056eb2f6566ebcc1257e31c3cc19a2a2e049c5d9486b7b05dc9c8558d41958a5643da0755af68e233ebfc8b6d558f63d4cd8f110e55f7dff17cda4f14c4c607d628395a21f679cf5565ecbf2213b9debb8ecd25a30cec3e352540cc8d0e52afd1b7b4b4c0e1231bbd73a96db21c01f59a585ab84f6ccdc4363dd1d90a5f4565d21a67ed7b32ec36e94fb43fbb8c1a45c28af6d0e4a8e125ab73539a9a05ec7a169c7cc506a48b58bd3a9b4d586f471bf07c5434a2e151527aa6c666c8cf7c6574c0ea82fdf273d4b3530ea9abc54a5a4dc83075ab705b9063c9e832fc8b4208dbf5ad68b50d4db92d471ec5012359e6e08b7cc3af733a89534a77779da72753b1e4872c8df2f5a8a2e9991904ae004e6609c4d672fdc50db75de75ab52ce388f2f462f525fe7b05c108f20815c08cf15c64cf203cf1f7ced17453a9514f9e2c5bffd4e672b1d13f885a0c1e58f1bafe08cbad1ca06a5e3479f595e79e4a6de7bf2b99e5cf985c3fcbcc994f02dda25a05c8dccf2e880ef0912d2e5172ce", 0x1000}, {&(0x7f0000001600)="9289947d784edcd692767038d402bb0e866c103bec2d1cede81258ac70126ef522ee20a0098ef79a760ceecad489bb2018dae9ad3990ab36a5a715f223bdd513ec4d32f2279e413ac398a8e8023772d17de1ac86e82243fcec98a12f07f9cb56d18fa1fcdbc952b2b937e9527fea923dc09a8017360dddb48cf7d1545b825353d38cbace2ea0c1a9883960b50a1665345afde8fba04618e9a2847041576ad947b374ae0bc20dddf92c19af0712f81d85aea7e260cdb7b9a4a00934cc26eb4546dbed26c1ff5823e469c7ceb168aa522aadf8aa764db786b5250d6773cd21eb54f4fa13838c36708b5d11d1a938749ceb5b882d6b", 0xf4}], 0x8)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
writev(0xffffffffffffffff, &(0x7f0000001380)=[{&(0x7f0000000280)="f25adeae43ae2b4e180da5c7df2d3c3bbeca684b33295427a7ab10576e1ef73ae8e4e59d220a72c6d6fc33de4dbde3c4abdc9b8b142da592e957ee25efff2d14da28a22e0e2d059265677648c38d78b106eb4059dd82f6a82cd8a5ea31fc97a347df65c075d53b700bf0902e071ba247ec5e9c254046fb12f6136cc8c3d9c01a4a16d2198a9db157b39132ed7ac40997a069e9a8fa13ba40ba99c4a5dde48ca7978bfc5e21c1790c55954c8e3d4edd0ccb8a8845161334b96137e11afee89cf2695a4fe2550ebbda9507ee714205b8eabb79618cbd2061f6b17f25ab05fe04cf9d9853963cac4fe5ff33e1b0ce65472d416545e3575cb0e03599e6aaa95dc132f754ef7d26f59ef41bd512ea825efcdce465ecae6d49634153106e9aa662bffb64984f386f26dea470e21df24c710ac6681de105a8b498b6bc55cd04ffa35886f7936841af8f6ebfc7790a6595b94eae16869809b946f7b11ab42d59007ade2bd5d1ab4bdba71649badbd706673ac0bff692bed4776cc8e002e7eba62d543e72f0390fb1e618de1226de64cfe23efba63304d98633632ef9de72814db39b37b4d2328c2fc450c0e92711c3e4f584ee486afe625a733c95a7a549e5643bbbaeff077024aaa53c606db6f649efed78bd4cfc22df8c537339dfc6f236e165592778ecbb7d03a9f1c81bf25fa88f19f2a4ba67162430ed875bf76bec7b1ef53e0499341025e3ead0e36c6cd0eba998e9c755bfdc28692b3ddddf8f7bba7e0b8edf692570592f812ee30635635364625836284b5391854d6e4123892a73026142cca2ddfe28069391562bb11d7e21a12fed70922dc3975b68e2589d83fc241e274c4108f535dcb8e4950fbebf7b0d873db59df53e1dde742fcfbdf5cb3345255e75a8a4db2fe0dfe9329fdd36c639641019e94f8bc524de8caac869150453ce0f53d6ced533e7b339f6936154ba44da2c4d3d9f138c69b2baf9edc2ccfcf5e08c273528d0bed830aeada0983578ed80dd029b5ea39664f1a04e578022bf0cc8ad930863ce3b692904caec70f9e0b1a56ef1c7e2d6637816643283b4b6048573a59a3c46637545f16794b08db77bb86cd34acf23bd563cf71e8944a34eeae99235dc31b00fbf7d78e61873e67decfd84c4799be287fd1750e32595865a0e59636628d4c19feace319d5441576bdb4268a96f0e0823e1806244728bc498c8287e6362e5c581aae98df95dfe2eb06e7e831b0430a6f5a3a3261b26e8202c5a22704638193545768d0553d4409f12bd7fb72f25ddc685e1d895779feff4ab3ac5dcbc97107db8cd74b34d4b7241747b9a0d1cbd695c67dfed2dabea10488fadbbbfb957230f4b5e6711c804b3c999d3642fa04c1ec7e6a53644f88fb08884dba541be50f2d46e547c7dcfb37435e5f1a14bc453c60c3c556ecd58fea85ef146934cdd23c3fa6e0627c9d74d5995789bfa0a61ee053aa9bee882860e39fa721cb6731bd351404867fc78578c403bec3dd8624f81d853909c12f1b1c6de651d5f5256ab3d6556608aa70918fe17f42ee9284c07218febe5cfd8568c95bf2dd389d1816c42a41f2d06cb5045b0df5cb041611146b922c1edebd6276bd9ec7ae92f", 0x47b}], 0x1)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpgrp()
setreuid(0xee00, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r2})
r3 = socket$unix(0x1, 0x1, 0x0)
r4 = getuid()
setpgid(0x0, 0x0)
setreuid(0x0, r4)
setpgid(0x0, r2)
ioctl$TIOCSPGRP(r3, 0x80047476, &(0x7f0000000040))
r5 = fcntl$getown(r3, 0x5)
fcntl$setown(r0, 0x6, r5)
setreuid(0xee00, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0xad)
write(r1, &(0x7f0000000080)="e5", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x3, &(0x7f00000000c0)=[{0x80}, {0x24}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r0, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}], 0x1, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
mmap(&(0x7f0000002000/0x4000)=nil, 0x4000, 0x0, 0x10, r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
sendmmsg(r2, &(0x7f00000003c0)={0x0}, 0xa2c4a4fbccc5c5aa, 0x0)
socket$inet6(0x18, 0x1, 0x0)
r0 = socket$unix(0x1, 0x5, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000000)=[{{0xffffffffffffff9c}}, {{0xffffffffffffff9c}}, {{r0}}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0xfffb, &(0x7f00000002c0), 0x80000002, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x0, 0x0)
bind$unix(0xffffffffffffffff, 0x0, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = open(&(0x7f00000000c0)='./bus\x00', 0x20, 0xc2)
ioctl$TIOCSTAT(r2, 0x20007465, 0x0)
setsockopt(r2, 0xffff9476, 0x4, &(0x7f0000000140)="abaa80c85b96545ce3c2ea5f4056ede74eb6d74e0c5f0d3f56f02fcb38c2f39b4264ee8b4ba854d5b1e7b9db2393ed41fcf8fd7fa8a48428", 0x38)
r3 = open(&(0x7f00000000c0)='./bus\x00', 0x20, 0xc2)
ioctl$TIOCSTAT(r3, 0x20007465, 0x0)
sendto$inet6(r3, &(0x7f0000000000)="24174bdb3dda68fbbcbf86896fd2a59ac9a6df2377ad81d5ab57c0d02569091fed4d1cb78cfca1dbd2ffaf50e3191a46bcb9395eeb2abfc04dc8992216479ef186ccf62a94e327440f83cf62e0df1820eb0ba7daf2215ed07ccff0bb543616599093b6736ff675bbf39bcc893f290d8d07086d3f7224e41d716cbb831c5a660912", 0x81, 0x0, &(0x7f0000000100)={0x18, 0x0, 0x3, 0x5}, 0xc)
r4 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x5, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6], [], [], [{}, {}, {}, {}, {0x0, 0x0, 0x800}, {0x0, 0x0, 0x0, 0x200000000000}], {0x0, 0x0, 0x4000}, {0x0, 0x0, 0x0, 0x5}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r4)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = openat$pci(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107003, &(0x7f0000000100))
sysctl$hw(&(0x7f0000000000)={0x2}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = kqueue()
fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x1013, r0, 0x0)
r1 = semget$private(0x0, 0x3, 0x410)
semop(r1, &(0x7f0000000000), 0x0)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r2, 0x29, 0xa, &(0x7f00000000c0), 0x14)
fcntl$dupfd(r2, 0x0, r0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x9fa)
open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
openat$pci(0xffffffffffffff9c, &(0x7f0000001a40), 0x300, 0x0)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x8001, 0x0)
setsockopt(r1, 0x0, 0x13, &(0x7f0000000080)="02000000", 0x4)
r2 = dup2(r1, r0)
listen(r2, 0x0)
sysctl$hw(&(0x7f0000000080)={0x6, 0x16}, 0x2, &(0x7f00000000c0)="1f31b495", &(0x7f00000001c0)=0x4, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0xc1286944, &(0x7f0000000300))
sysctl$fs(&(0x7f0000000240)={0x3, 0x1}, 0x3, &(0x7f0000000500)="c774c0e509b1ee7c56050daf8703c84331f71ec4dea94afb42ac06d34446426bddcbf2a238d0edcd0e8f3aa48215ed2c3335e20a0db7f2c6936db56da513fffff21d5a79c1d55a6930c43d625b5388c57a8ce77906e27ee81c1c0100000000000000b2eff06050d1a2fb460ad85a48ee2b000000000000000000000000b2f9ff6f2f3f82812230f95362bb40f8a8494804fbd2ac77974242fb86e68b0ef96b96a34f1dceb31fd729a60280f0fbde8a89506367b775b60545cb2e40f928a36baa421cb05b8bde67c4ccc8809b767e8cb9a5e970349439885effc4f5c05b72f73a50f8d033166153b265b2f7642a87fccf67b3d92ae814de0e2b1a7ccfc47d4cd979d59d984887ca0058e9a1ddb559e350b1", 0x0, 0x0, 0xffc3)
sysctl$net_inet_etherip(&(0x7f0000000000)={0x4, 0x2, 0xf0}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x81}, {0x34, 0x0, 0x0, 0x7}, {0x6, 0x0, 0x0, 0x100000}]})
pwrite(r0, &(0x7f0000000040)='\x00'/14, 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x2, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x808c694d, &(0x7f0000000300))
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
sendto$unix(r1, &(0x7f00000001c0)="bf4ca2e465b8cebc79a52ad18f95355965ce6a29e7985c1b99f8980d00512d9278eaddd245c0473b92257eabb48f28f4ac06a8db164277e5763370ee106c3674ddd9a8caf81000"/85, 0xfe5c, 0x400, 0x0, 0x12)
r3 = socket(0x11, 0x4003, 0x0)
dup2(r3, r2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x64}, {0x6c}, {0x6, 0x0, 0x0, 0x800}]})
pwrite(r0, &(0x7f0000000240)="16d44ffbc1cf5ba084b9e56efda5", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x48}, {0x1}, {0x16}]})
pwrite(r0, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x60}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x2, &(0x7f0000000080)=[{0x2d}, {0x16}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f00000002c0)={0x0, 0x0, 0xed5, 0x0, "4a6befb423dea1e06584766b93516d9ea8010981"})
write(r0, &(0x7f00000000c0)='h', 0x1)
readv(r1, &(0x7f0000001640)=[{&(0x7f0000000040)=""/123, 0x7b}], 0x1)
openat$zero(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimensat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', &(0x7f00000001c0), 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000400)={@random="abd15bdc0870", @local, [], {@ipv6={0x86dd, {0x0, 0x6, "30c0dc", 0x8, 0x3c, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@ndisc_rs}}}}})
mmap(&(0x7f0000ffc000/0x4000)=nil, 0xfffffffffffff002, 0x0, 0x10, 0xffffffffffffffff, 0x0)
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000ffffffff5800000000000000003567b54500"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000000)=0x100)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0xa}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000000)="ea00005c00000000", 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[0x0])
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
getgroups(0x1, &(0x7f0000000300)=[0xffffffffffffffff])
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
close(r0)
accept(r0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
recvmsg(r0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x842)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f0000000000)=[{0x100}], 0x1})
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/9, 0xfffffffffffffef5}, 0x0)
fcntl$dupfd(r1, 0x0, r0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0206921, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x4}, {0x7c}, {0x6, 0x0, 0x0, 0x240007f6}]})
pwrite(r0, &(0x7f0000000040)="dd00000000000100000a3325937c", 0xe, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x18, 0x0, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
fchdir(r0)
unlink(&(0x7f00000001c0)='.\x00')
nanosleep(&(0x7f0000000000)={0x0, 0x2b59b5c0}, 0xfffffffffffffffe)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000340)=[{0x74}, {0x74}, {0x6, 0x0, 0x0, 0x101}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
r0 = socket$unix(0x1, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1004, &(0x7f0000001040), &(0x7f00000003c0)=0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x25}, {0x60}, {0x6, 0x0, 0x0, 0x80000001}]})
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x6010, 0x20e02)
open(&(0x7f0000000380)='./bus\x00', 0x0, 0x0)
openat$vnd(0xffffffffffffff9c, &(0x7f0000000100), 0x201, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x7ff, 0x0, 0x5, 0x1fc80d8b, "04000000010000a9b3c742000000d35ed900"})
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000002c0)="f337d7da07c968c1b4f746f37349a554b572556c3cf619c18ae330140d1102000000fafe922a9203000000000000009585dd74a6f000172364e233f0c27e6d5351d7baf0ab50e6daaec3d19dc9512f01c00001", 0x53}], 0x1)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
chflags(&(0x7f0000000200)='./file0\x00', 0x0)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
sysctl$kern(&(0x7f0000000200)={0x1, 0xb}, 0x2, &(0x7f0000000240)="2fce5c27", &(0x7f0000000280)=0x4, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
setsockopt(r1, 0x29, 0x3e, &(0x7f00000000c0), 0x4)
dup2(r1, r0)
setsockopt(r0, 0x29, 0x3e, &(0x7f00000000c0), 0x4)
r0 = socket$unix(0x1, 0x5, 0x0)
sendmmsg(r0, 0xffffffffffffffff, 0x94, 0x0)
syz_emit_ethernet(0x3a, &(0x7f0000000040)={@local, @random="0000ebff00", [], {@ipv6={0x86dd, {0x0, 0x6, "50a8b5", 0x4, 0x2b, 0x0, @empty, @rand_addr="b118eb192dc0a276a5e1d83e162cf4fc", {[], @generic="2bc6f1a4"}}}}})
r0 = socket(0x18, 0x1, 0x0)
r1 = dup(r0)
ioctl$LIOCSFD(r1, 0x80047476, &(0x7f0000000000))
syz_emit_ethernet(0x36, &(0x7f0000000100)={@local, @local, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}, {[@timestamp={0x44, 0xc, 0x0, 0x0, 0x0, [{[@rand_addr]}]}]}}, @icmp=@mask_request}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc0084427, &(0x7f0000000840)={'./file1\x00'})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f0000000240)=[{}, {}, {}, {}, {}, {0x2}], 0x6)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0xffffffffffffffff})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
sysctl$net_inet_esp(&(0x7f0000000080)={0x32}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x400000001002, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
connect$unix(r2, &(0x7f0000000100)=@file={0x0, './file0\x00'}, 0xa)
readv(0xffffffffffffffff, &(0x7f0000000480)=[{&(0x7f0000000380)=""/202, 0xca}], 0xffffffffffffca8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000001700)=[{0x14}, {0x6c}, {0x6}]})
syz_emit_ethernet(0x46, &(0x7f00000016c0)={@random="6b2c64844967", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "7af2ec", 0x10, 0x0, 0x0, @rand_addr="65307c4128bf703441297693620af6b0", @mcast1, {[], @icmpv6=@ndisc_ra}}}}})
openat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x0, 0x0)
ktrace(&(0x7f0000000200)='./file1\x00', 0x0, 0x0, 0x0)
renameat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00')
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000340)='./file0\x00', 0x300, 0x0)
flock(r0, 0x1)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
fcntl$lock(r1, 0x8, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x100000001})
r2 = open(&(0x7f00000000c0)='./file0\x00', 0x201, 0x0)
r3 = open(&(0x7f0000000300)='./file0\x00', 0x0, 0x0)
r4 = getpid()
fcntl$lock(r3, 0xe, &(0x7f0000000040)={0x3, 0x0, 0xffffffffffffffff, 0x100000006, r4})
fcntl$lock(r2, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1000300010005})
flock(r0, 0x8)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
r2 = dup2(r1, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
dup2(r4, r3)
r5 = accept$inet(r2, &(0x7f0000000200), &(0x7f0000000280)=0xc)
setsockopt$sock_int(r5, 0xffff, 0x1008, &(0x7f00000001c0)=0x9, 0x4)
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000080), &(0x7f0000000100)=0xc)
socketpair(0x9502b12f7077c698, 0x4000, 0xff, &(0x7f0000000140))
r6 = openat$speaker(0xffffffffffffff9c, &(0x7f00000002c0), 0x10000, 0x0)
fcntl$getown(r6, 0x5)
connect$unix(r4, &(0x7f0000000180)=@abs={0x1, 0x0, 0x0}, 0xfdc3)
connect$unix(r4, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
accept$unix(r1, &(0x7f0000001640)=@file={0x0, ""/58}, &(0x7f00000000c0)=0x249)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
readlink(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
poll(&(0x7f0000000200)=[{r1, 0x1}], 0x1, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "25a6c6d17013766d4f76c774e1c3ff94d19b5cca"})
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100, "745541562d94962f9a0f8280acac41162e7192bf"})
ioctl$FIOASYNC(0xffffffffffffffff, 0xc0104419, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x30a, 0xffffffffffffffff)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x400004000011830a, 0x0)
r3 = dup(r2)
write(r2, &(0x7f0000000780)="089267d3ff4f0b87969f", 0x100ad)
preadv(r3, &(0x7f0000000100)=[{&(0x7f00000007c0)=""/4096, 0x1000}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x1d}, {0x5}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
connect$unix(r1, &(0x7f0000000040)=@abs={0x0, 0x0, 0x1}, 0x8)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000513600000000000000004", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x211]}, 0x3c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
sysctl$net_inet_esp(&(0x7f00000001c0)={0x4, 0x2, 0x32, 0x2}, 0x4, &(0x7f0000000200)="c1e288bd", &(0x7f0000000300)=0x4, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x4, 0x4)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x68e, 0x0)
write(r0, &(0x7f0000000100)='>', 0x1)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1, 0x10, r0, 0x0)
sysctl$net_inet_tcp(&(0x7f00000000c0)={0x4, 0x2, 0x6, 0x14}, 0x4, 0x0, 0x0, &(0x7f0000001140), 0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
r2 = socket(0x2, 0x3, 0x0)
dup2(r2, r1)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="eb", 0x1)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt(r0, 0x0, 0x7, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x00.'], 0x38}, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[], 0x38}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x4, &(0x7f0000000000)=[{0x10001, 0x0, 0x0, 0x800}, {}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)={@remote, @remote})
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x7b)
r1 = dup2(r0, r0)
ioctl$PCIOCREAD(r1, 0xc0287533, &(0x7f0000000080))
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r1 = dup2(r0, r0)
ioctl$WSMOUSEIO_SETPARAMS(r1, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000080)=[{}, {0x21}], 0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x6c}, {0x5}, {0x6, 0x0, 0x0, 0xffffe080}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x4000000000000000})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x8010570e, &(0x7f00000000c0)=0x8)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmsg(r1, &(0x7f0000000740)={0x0, 0x0, &(0x7f0000000140)=[{&(0x7f0000000180)=""/102, 0x66}], 0x1, 0x0}, 0x0)
readv(0xffffffffffffffff, &(0x7f0000000480)=[{0x0}, {0x0}, {&(0x7f0000000440)=""/22, 0x16}], 0x3)
sendmmsg(r0, &(0x7f0000000340)={0x0}, 0x10, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0xc5005601, &(0x7f0000000080)={0x9, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x9000000]}})
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x4e8b, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0xc7, 0x0, 0xfffffffffffffc}], 0x200007, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000340)=[{0x74}, {0x64}, {0x6, 0x0, 0x0, 0xfffffffd}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{}, {0xc}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)={@remote, @remote})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_GETBACKLIGHT(r0, 0x400c5711, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4d}, {0x24}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xffff, 0x1fc80d8b, "04000000010000a9b3c742000000d35ed900"})
write(r0, &(0x7f0000000180)="28f7c720e242b97c85cbc1f98f3775fa7295b4a2274d0260311befc6e5321ea043053607a5a6b2b4f3b519d9d31f8c9b55cbf07c4e7196cda90893418a02db070f59d17887f063a70edf3b70d3d9729d52aaa3def08a18907984e09a543019873f971b488c5e643c36e9f4606899043000417bade7b28820bfecdeb23168865db0ac2a791d30200a6283ab5551c6d9ec9f42067c20e06fad31741fef2bdcbe243e72694e2a64a1448058ae3d24a1fe0af025638699b74bb09ff62edc4013cc3ff610567c63043a0659e1497b33d61c9734f2c681cca12c9579b6da9cf5b373b161e0906f0e3f4f118e5e0df83712016009", 0xf1)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r1, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
r0 = kqueue()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000100)=[{{r1}, 0xfffffffffffffffe, 0x43, 0x1e0000005, 0x8000}], 0x0, 0x0)
kevent(r0, &(0x7f0000000100), 0x81, 0x0, 0x0, 0x0)
sendmsg$unix(r1, &(0x7f0000000340)={0x0, 0x0, 0x0}, 0x0)
recvfrom(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f00000002c0)={0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x81}, {0x64}, {0x6, 0x0, 0x0, 0xfffffffb}]})
write(r0, &(0x7f0000000280)="731f5c52643f0e37bf54dd2a1b32", 0xe)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x20, &(0x7f0000000000)=""/4096, &(0x7f0000001000)=0x1000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x2}, {0x45}, {0x6, 0x0, 0x0, 0x2cb0}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x20000003, "0000001000000000002000"})
ioctl$FIONREAD(r1, 0x4004667f, &(0x7f0000000080))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="7002020be0000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x2, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
fcntl$setstatus(r1, 0x4, 0x4)
write(r3, 0x0, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, 0x0, 0x0)
read(r1, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x6000, 0x0)
open$dir(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x7, 0x4, 0x0, 0x0, 0x0, @broadcast, @multicast1}, @udp={{0x0, 0x3, 0x8}}}}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x40000, "0000eefeffb2ff03000020000200237e2000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
sysctl$net_inet_carp(&(0x7f0000000140), 0x4, &(0x7f0000000180), &(0x7f0000001180)=0xa000000, 0x0, 0x0)
munlock(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
setuid(0xffffffffffffffff)
socketpair(0x1e, 0x3, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x50}, 0x9, 0x0, 0x0, &(0x7f0000000100)="987c000000e9c9b3faf040a765dceff38911c6bc5f6b8bc3d400af4f1148af971877b25a5bc70f2320ebc54589b0728a057cafb84bef54d21d25cdf68496d2073ce91bf3c2ff2244000000005e3ea58b0100", 0x52)
syz_emit_ethernet(0x2a, &(0x7f0000000080)={@broadcast, @broadcast, [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x0, 0x4, 0x0, @remote, @rand_addr, @remote, @remote={0xac, 0x14, 0x0}}}}})
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000140)='./file1/file0\x00', &(0x7f0000000240)='c\x00')
unveil(&(0x7f0000000040)='.\x00', &(0x7f00000000c0)='c\x00')
mkdir(&(0x7f0000000000)='./file1/file0\x00', 0x0)
symlink(&(0x7f0000000080)='./file1\x00', &(0x7f0000000180)='./file1/file0/file0\x00')
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000640)=[{&(0x7f0000000080)="543598", 0x3}], 0x1, 0x0)
openat$bpf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
syz_open_pts()
flock(0xffffffffffffffff, 0x0)
openat$zero(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
writev(r0, &(0x7f0000000240)=[{&(0x7f00000000c0)="fbac", 0x2}], 0x1)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x1, 0x0, 0xfffffffffffffffe, 0x1000100000002})
openat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000a40)=ANY=[@ANYBLOB="8202d5642e"], 0x10)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r1)
getgid()
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r3 = getuid()
setreuid(0xffffffffffffffff, r3)
fchdir(0xffffffffffffffff)
r4 = semget$private(0x0, 0x1, 0x315)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r5=>0x0}, &(0x7f0000000040)=0xc)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000100)={{0x30a, r3, r5, r3, 0x0, 0x101, 0x92}, 0x9, 0x80000000, 0x21fe0805})
r6 = getgid()
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000180)={{0x1f, 0x0, 0x0, r3, r6, 0x152, 0x6}, 0xfffffffffffffff9, 0x33, 0x0, 0x0, 0x7f, 0xaf0c, 0x2997, 0x7fffffffffffffff})
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x81}, {0x35}, {0x6}]})
pwrite(r2, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
dup2(r0, r0)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000640)=[@rights={0x10, 0x8}, @cred={0x20}], 0x30}, 0x0)
r0 = socket(0x11, 0x10000000000003, 0x0)
sendto$unix(r0, &(0x7f0000000280)="b100050400000000000008000110000000000000cea1fea7fef96ecfc73fd3357ae27caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebb4257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd007f720fd387fc7a9adec45a2dba048ec120822babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002020208a3716ef800040000000000000001000000", 0xb1, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvfrom(r0, &(0x7f0000000080)=""/3, 0x3, 0x0, 0x0, 0x0)
setreuid(0xee00, 0x0)
execve(0x0, 0x0, 0x0)
setreuid(0x0, 0x0)
execve(0x0, 0x0, 0x0)
sendmsg$unix(r1, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, 0x0, 0x3d}, 0x0)
sync()
syz_emit_ethernet(0x7ff, &(0x7f0000000000)={@random="83b6a410aadb", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "f7ef86", 0x7c9, 0x0, 0x0, @rand_addr="187ee3dfa9bb375c2b779fa1fb7b2747", @mcast2, {[], @udp={{0x3, 0x2, 0x8}, {"24d8f2f5a94842281bb9e2a8ee460e4067562b9dde0e5da415d176c99567bba8b0052975b210933002005c2aefd8e2243912cdda738ef1abf24e4e1802b3dd919df2250389f4e14b302497bd6a6feed852b2ff786094a533d0e15282b360ff527430d0ecd8a27d6d4c168b1b0f2dea3c33e94efdcb919af824c9326529273f5ec2735161da20339c399461e3304db938797af8016fe398422d5483525639184274438aa81c4f0389a206b75a648da6ab1179670d04a8fe82e0ae32926550928f0d7f36c1547c8cc7835dd1c67adc577d90609dee9510d4c6e3d74ca4977e7e38b0d34270303b0363a2a1ec552f7e081a4fb4965ee552ec089cb5e4ab8a0c2b3d1aff98009064231bc936b10628d32a51274970009d7cebb9dd1a2b63902882a9609effef7ab713f540d40fb296c7088bbd933547271dbf861f3efc41b5dc89db9506376d479aa8e9cba1c69a0ac29e591e2b75eeb9f14e92ffde7e57bf86a92ec71d78ade29fcac5fa19fb53fc22366936449012c1b2a163102bc1b5cd4de95f9ffec602ad597f9d766fde5e3d10f5285bfce43de7ec307aad398d666e2ecdda6c8ccf6ebe15293dea6cee8426cee3f753a3627e423cdd272af7e3638c385aaadea336c0f6de84d8bce027bda4bab827b8f22d3dec29c0a1490ee8d8cd8a281e44be70ab1246fe8054b5aec774d65e13d7ced9f30d87efe0c60d41e3e243b87c89fd8b9aa36e149cb1cc671b05745e605657500c271f68a7d26d465be151755bb0ff235664644f193e18cbc37ce24836b7b028a345bf0494ec305aef5826cbf912703685f71646d804031ea6fced38b55335a833e86ee16ff61914ddd44173c33111201e36fc1a694be2876c9dcdc61085f3bb56bb4044df9671e290bab8cae76f5fb9422355e3a3636c56b18daf7123716dd83a909f0472ecc488e0121ba1931b81006d32c937609f45f7f8d65c07208c6b0430026348fa1e3cefa1e52620572824903a20bfaa2a3db613f29176758d942ca1015290d6a9a2ef4650e4e3197b9b0954085e20c6fa35aaf2182586c2a2983cc0e08eab892bd5d70705018d18d2566276f643111092e77d67a67188efcaf5f00a8f42a1f1f44ae45fcc6c97036be08c19a488b3e7eb05a334c13839ce05bc4eedb966382a32f18abd6342fb1bf809f92058de4823a636d985896d61411bada8cf1bd8e31ee28de9afa8b3272a53b4ec3539980c8aaaa6a71f3cb1ff929a0da211be9b21b5f883764617951f7726af85e2350ecf3584b3a6247858c343877aa4a6e060e3db02e160ad0c8d178b0629a9db20d43988cf13b25f93a3b074c0be906fba5e805b373f1e9cb77f7f8bce3d0519e7cfcc2943131a90ebc31b8f569bb83651edc122ba56ff6be03f7bd5148b5b2e4f5ad12e4c19814e8461da0b74777f463bf58e9a7434c3116047f3612f2e67aa7ff5b8e87497c35c2ed3e4846c75b9e0b83321383b8acbe1f4decb32cd6d2bb1006dc77aadd572a1048725bfbc5e0eca05ce495430faed7c014b605713c42df9000064b97fe79f523139a00ff2c6e3ed2ddfce29eab378bd67cd26ddca1838437289ed80d0aff404b51d118881ab04f85c5800b45c18ec03018d275a429930f97522ea3bf3104e3ac2f9dd552ed6d72827f4126322b50e957d9f24bc55f7b02a9df7a6bbe0060006aaf9c5ccde3bd1dcc38fb6a97fe8279033d41774a6d1dfafa5b678e9a619c68b3a85459010c1f85cd42714acc0b4f6ab4aa48e02feb654fb5aa6d0648627acb0f890b19e47826496bfee5e431a11a26260a0026d26f577ec78042cef39b92cbce8bd4e13ee9b3398ade33eb207b086654254f33bad9006f40078dd4b75f19dbe736a5358c6f90f2c039143711d9d1cc386e7a0a05ac5b217282694438adbec06a1880e03ef4f850e88ac89908e56efa453bf517365543dd9303ff06111681b1e2eefbcf0d3bb23c886147be2cbb50ec1714d217c690ec4a880103fecb9004cae3b3a5c2d179087ea60ae4c1d2c7eceb17dbe14d65f28f72f077017b99afb3a9703dee14069fec6cc0381158849c9b5a8bad2241980cb710a74d019604f260863413267b18148e39d4ac76b706c1d5fde205afc6b3f95a42d6ba35d986066d86e030de7157da35c355b3d82d346e7d5b83d101cb7c8f05d846bf9772d56cc7035051014c32cb764324bf860642f5856e33b68481c51f4b41b67790d82c6214172285de44ad766c757e1c29b97a2745c7efeee4068e421285e87d4bf908a9eeba12460f31418da0cee0cb7c008784eed4d4d95ea3d3257fa04034094fd2752b31d783744bde91f35f60c51772becdc4a0ffcf922895efe6c23364d8e29b83d561248960cddda78d8ca76c96570e6a77c4df99c926ff1458a88eac568cb05ccd000ea1ecd079d4216240fd59a026c12b8a26529bbab58a2413956f001e6373ece6ac5cb5ba5c485c3ce63758476783b2a0af1688da94bb24258bf34035f81bb5822d7feb02b4217bdcc227e077f4e6efc550e28ca3d0d2ecdfa2c01f962d946ee7fb103dcfbf39acd211321a2f6167e5ca4091d84bb950b660ae8c4e2077ef6e3906981f859de4a5089437b26cd36bccc8b16b760e3002c6122feb22622bf4e25550faf8c88ea48b7e84ce517d9a22d004b4127cd1a0a90c685f339a7fac2a40bd50a9ca9d8d965efa7e842f39caeaeb7f5716b73f14eef13356fe99ded1c8a9bbd7a5052e8836d2b6fb47abfabc6d401ddfae83767a28d8f00f00b0def4bd3ae1ed6089f82bf775312347455f722f47123bf3c91335cda"}}}}}}})
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x26, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000400)=[{0x4c}, {0xc0}, {0x6, 0x0, 0x0, 0x80000000001100}]})
pwrite(r0, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000100e20300000091a0ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x84}, {0x1c}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x4, 0x18, 0x3a, 0x8}, 0x4, &(0x7f0000000c00)="a20b912e80f35e6f99a409b6bd554626091eb5adb7f200188a03163107091f71eea8259b40cb605ae998b44abcf2e62cc94f1732150c4302300c81f815a72940d22c679c94022bd9dc22086b89e2b3c80ccc817f5bf648d966336c13e9479586d102902f31fbf8fc8e68c4dcf8ce2c828580acc054947791358de836288ee8179466ee537e2739e785a73116c1896a0618d7214c07d79a01e86e06a1265bbc8a499355fda2ab7fbea0d2ca641c86bee4e1ff6a3426cf06617ad9cb007d21eb492ea0b529d9e3b2bf1e5a21007fa398ed6356aa8c0f9a8fb5850c2d9ff388eb4a640bb0c193fcc9e77ec515fd43176ada0613dc3a7ef780b718a0f22869e60ef74e83bba1a97b4d3ea10b80f58e5ce051654e25f4bca09ad97fe905bbc05f494b729da5d35a458cb00724dd82893d8b1cec748b45db2b4aa992ae63e44cf07ccc2f4cdd708d1b8fe86d14aadf3d42c5b444e16c1e5308aa22d3d2dba22b52159e4818235231b81ffc39e172776a4fb3908db6b60ff04959c00a7fa5910428145c0650fd3eba26e815c1dfcb98c73a173bfb5889519f8f2dcf4f7fd8e0b98c92e859fee347b522bf49fc0fe9f49d154c2c64c789b906da15661b8def837eec9592f0c2cd5c40ba7019b9bdd3566768007840d3d2317f1f42b96480eb3639a87f1a3e732838005809af18713ec337ba555c2d06b3baec1bca8d7d23825efcf7d65b8e90b2bfdb18295e0fb0b247e54e39d560fab36210bc86bcee9ef9fe822eb60b83560311a558a5e0363a83052964dec650258032818dcaf33ee572848fc5bfe7ddd5ff397af516017660eedb706876eff1561d81950dc5ff0aa13d8ebebc1d68c0d510ed11b27743c7dc8899f871781fbcfec5da4432d0a9ff4f137a808ac1a138b351f7da51a44259bec96183ea54deac1d83124bfe28182143d8e13ff17bea6762f51e53ca690af257eb61c26c549eb4db7e3e3efdfc267d6822d23c329b1d2ff4f7bab5ffd6a4cfdf6ae852ab6f1a129d8322f0b74ca6a6a1f4fce5508095de2f265159b907c8619597adbfa4ae97f15fc030109a59f21e1f12651ef3459ef0820455656bd96b5f47879ee4bbdbb75af8c4d88ee179a437dab12f68d197677516238d46c27ca4160f9f79a11b1c1a612ee313ab350d2aed64da768d14ae43d201e6142e4b7548b853e2b69cfa9fc5fab9107e31faad2ea2688958c27e5fc573adf8c7fce24b660b8b97cceb9f0d82b6e5cacd8eab8bf72dcc9849f942bdebbc62d12020d1e2d8cddcd29cb80ba6ad16c3f92d2225fa36f1bda3165100b7832b898d102ddbe6962dcc22162304a5bfcbd4804927fded3cdccefa16b4d80b9db6d72eaff75df93508f0df8d21efc329d4e503e8040ec0e12375716e106707756a10dc3e48d9cc0e857239bb36fc8b7bee6880c5e0868856483a755039528c0895", &(0x7f0000000140)=0x401, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x89)
setsockopt$inet_opts(r0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = dup(r0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f0000000500)=[{&(0x7f0000000300)="ad80740490377706ed82a1bba52bb74afafd86958f27c50a5e25b9095e846e0fddc135ada439bedced5e452823ab37661a08b9f504000000926d41943a961bccd9879c98b10718fb2b000000006e1b58944dd16c2df783384b2988bdcaf4d4c25c08fd705875a7a57fc5d21e9f91748a121189c10ae64fbb421e53fc740ba0434504af2a3149018de4f1eea8b1d88978acd0fca90e5dcc44846684d050aa9d3e7ad51949cb8a0611509aff269b9ee4ee78cac32cd8e3ed2a94320008cc0131349e67358267540d051fa1cce9663d6e9745a349fdc929b7db94848351d96c7f15dcfa6e674c0e36a94db8df8fba61dcde98d7d48e7f5cd1aa275f7030433ceaf0db994391", 0x104}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$SPKRTUNE(r1, 0x20005302, &(0x7f0000000100))
mprotect(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x48}, 0x2, 0x0, 0x0, &(0x7f00000010c0)="64b2020a", 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSDISPLAYIO_WSMOUSED(r0, 0xc008441d, &(0x7f0000000080)={0xf})
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x2, 0x4}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02", 0x35}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5", 0x8f}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f0000000340)={0x1, 0xffffffff})
seteuid(0xffffffffffffffff)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x81206919, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
close(r0)
socket(0x2, 0x400000001002, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000000)={&(0x7f00000000c0)=ANY=[@ANYBLOB="00020035"], 0x10, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x10)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000500)={0x0, 0x9, &(0x7f0000000b00)=[{&(0x7f0000001100)="f96dfe6588d9910998b4aeea546c15208d882d2e0a0190fd2e6ad3dfc7cc27157eaca01752438c590a98ca4e416a23d4d68ef670ca82a6d3c6ec05f99c7e1424d7859962e59741727417a6d9a8e2e0979bc8ce13fbc40e9d0d048c56f8cfc66e36293646fb00137d5b2a98d6d40bbb97f9556b3223a0c2275c4975d76d080c8ce5", 0x81}], 0x1}, 0x0)
msync(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x5)
sysctl$vm(&(0x7f0000000000)={0x2, 0x7}, 0x2, 0x0, 0x0, &(0x7f0000001180), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000000))
sysctl$vm_swapencrypt(&(0x7f00000002c0), 0x3, &(0x7f0000000300), 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
r1 = socket(0x2, 0x3, 0x0)
connect$unix(r1, &(0x7f00000001c0)=ANY=[@ANYBLOB="c202"], 0x10)
dup2(r1, r0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000)=0x43cbc, 0x4)
r2 = dup(r0)
sendto$inet6(r2, &(0x7f0000000040), 0x43000, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r0}, 0xffffffffffffffff, 0xffffffffffffffff}], 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
close(r1)
mknod(&(0x7f0000000000)='./file0\x00', 0x2d48ad3a39c51b76, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x2, 0x0)
fcntl$setstatus(r0, 0x4, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
fchown(r0, 0xffffffffffffffff, 0xffffffffffffffff)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000007c0)=[{&(0x7f0000000700)="8fe502bcc908fb1ec64691b9fcb0a8b374f51acb6223", 0x16}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000500), 0x0, 0x0)
readv(r0, &(0x7f0000000400)=[{&(0x7f0000000000)=""/63, 0x3f}], 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x30a, 0xffffffffffffffff)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000740)={&(0x7f0000000080)=@file={0x0, './file0\x00'}, 0xa, &(0x7f00000037c0)=[{&(0x7f00000017c0)="1a", 0x1}], 0x1}, 0x0)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x400004000011830a, 0x0)
r3 = dup(r2)
write(r2, &(0x7f0000000780)="089267d3ff4f0b87969f", 0x100ad)
preadv(r3, &(0x7f0000000100)=[{&(0x7f0000000180)=""/110, 0x6e}], 0x1, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000200)='./file0\x00', 0x4, 0x121c, 0x0)
ktrace(&(0x7f0000000140)='./file0\x00', 0x4, 0x500, 0x0)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmat(r0, &(0x7f0000200000/0x2000)=nil, 0x0)
r1 = semget$private(0x0, 0x5, 0x28)
semop(r1, &(0x7f0000000100)=[{0x3, 0x5022, 0x1000}, {0x1, 0x0, 0x1000}], 0x2)
seteuid(0x0)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000440)={{0x1ff, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x1a, 0x6}, 0x5, 0x6, 0x400})
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000280)=0xc)
r4 = shmget(0x2, 0x1000, 0x4, &(0x7f0000ffe000/0x1000)=nil)
shmat(r4, &(0x7f0000200000/0x1000)=nil, 0x0)
r5 = getegid()
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000002c0)={{0xc, r2, r3, 0xffffffffffffffff, r5, 0xb0, 0x568c}, 0xffffffffffffffff, 0x2, 0x7})
semctl$GETPID(r1, 0x4, 0x4, &(0x7f0000000340)=""/217)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f0000000040)=0xc)
r6 = open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
getpid()
fcntl$setown(r6, 0x6, 0x0)
seteuid(0xffffffffffffffff)
r0 = msgget(0x2, 0x0)
msgctl$IPC_RMID(r0, 0x0)
msgctl$IPC_SET(r0, 0x1, &(0x7f00000002c0))
r1 = msgget(0x2, 0x1e)
msgsnd(r1, &(0x7f0000000000)={0x2, "51b234c270fa013dac7726908efe4b1c41748ae586d84a1776805c55e239d91408d1d5bd46b4f6f88bf0197a30866d0f12502ef97c8b931e517b27368154de99e205ca46d5efad980c187ff1eea81db019f545f1b917c836cb994716ee034b48fb18c299400bd0261515f8966dea0d5e226fa5f37adec042300f120906e79eac41bc90780a6f756f28535dfbc2b2a84d0610601c20a20daa24ac3dc3323927182832e22ecddadfb2a84d327dbccd7dc710697f9c3dcb02b0dcbb2c64804e1fbeeb996ae1aa9d8f7fa4a44b5b85718f10fb8108df1163938b168fa9e964bb8e009e9a9f32c84a"}, 0xee, 0x800)
syz_open_pts()
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
writev(r0, &(0x7f0000000b80)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r0, &(0x7f0000000000)="e6f509", 0x3)
write(r0, &(0x7f0000000180)="09200909f1ffffffffff13804752cdce9c904451473f0300000029157cb4b0ff03000000000000705abf281e73d9b6338a02bf0a", 0x34)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x30}, 0x2, 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)=[{}, {}, {}, {{}, 0x0, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x1, 0x0, 0x4, {[0x0, 0x4]}})
open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x100000001})
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x40, 0x1000200010005})
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x40, 0x0)
kevent(r0, &(0x7f0000000040)=[{{}, 0xfffffffffffffff9, 0x1}], 0x1, &(0x7f00000000c0), 0x5, 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
setreuid(0xee00, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r2)
r3 = fcntl$getown(r1, 0x5)
setreuid(0xee00, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x8, r3)
mknod(&(0x7f0000000100)='./file0\x00', 0x1ffb, 0x0)
r0 = open(&(0x7f0000000280)='./file0\x00', 0x2, 0x0)
write(r0, &(0x7f0000000140)="a4", 0x1)
readv(r0, &(0x7f0000000700)=[{&(0x7f0000000300)=""/140, 0x8c}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff})
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f0000000140), 0xffffffffffffffe7)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8014695a, &(0x7f0000000300))
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f0000000100)={&(0x7f0000000040)='./file0\x00'})
writev(r0, &(0x7f0000000000)=[{&(0x7f00000000c0)='#!', 0x2}], 0x10000000000000dd)
fchmod(r0, 0x8e3)
execve(&(0x7f0000000280)='./file0\x00', 0x0, 0x0)
mprotect(&(0x7f00004d9000/0x3000)=nil, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000080)=[{0x5}, {0x87}, {0x6, 0x0, 0x0, 0x553f8734}]})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
sysctl$kern(&(0x7f0000001200)={0x1, 0x57}, 0x2, &(0x7f0000001240)="3bf225435acff8c8a0457f2bd1aeeb02be95d90aaa2b6b8823adebcd9c263b41d9d0a13535b87177f7f486559273af1e03a2ec219eb86aef44ee06fd5e4b5328697d0a664c44c8a36c5af0ab1d37ee2f83b6c7503c7930a14de1ac361bcb0771", &(0x7f00000012c0)=0x60, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000001440)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}, {&(0x7f0000000440)="152887904db9a3dcf1", 0x9}], 0x2, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x2c}, {0x2}, {0x6, 0x0, 0x0, 0x58a}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
sysctl$net_inet_etherip(&(0x7f00000001c0)={0x4, 0x2, 0x6c}, 0x8, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x14}, 0x4, &(0x7f0000000040)="342a19e8", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x0)
setitimer(0x0, &(0x7f0000000080)={{0xffffffffffffff7f}}, 0x0)
mprotect(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1)
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000640))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{}, {0x45}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @random="9d8b1e9d2424", [], {@ipv6={0x86dd, {0x0, 0x6, "923149", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
sysctl$kern(&(0x7f00000000c0)={0x1, 0x4d}, 0x2, 0x0, 0x0, &(0x7f0000000400), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000002c0)={0x3, &(0x7f0000000080)=[{0xc}, {0x50}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@random="eb0420e10a89", @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @rand_addr}, @icmp=@timestamp={0x12}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x2, &(0x7f0000000100)=[{0x28}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
getsockopt(r0, 0x11, 0x4, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f0000000240)=[{&(0x7f0000000140)="baf92d151c3011fad28b6c83072523da54a57616c3141020ff", 0x19}, {&(0x7f0000000180)="9306c73832db3044ea31a8537274fdd4e7319488f35d94581fe06eeb140a5a5740e51c13a88affcabf30808984c3f36374a32a34f10171ebd4ede91789fbf29f6b2e0274", 0x44}, {&(0x7f0000000300)="749b502abf7032369be025ba65238e595477ce839c0792472349997c53b5af603fa3a96ccb401bdcb12aa773d6626e3cdfa200898aa27dfaa3f0988561133f7982304242cdd4add2010aef10082bd58e48e0019d40d3f054b2f7a50c613efdfa6859a002a47ec1f0ad8d3a3797cb3832707403ccdbb14158f5178c76fdb529d17b6acfb7e4981bd2ac12a95824f09807b58edd88bc9dc808a9dda1974bcb9da8a8ef67b4", 0xa4}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r2, 0x0)
r3 = dup2(r0, r0)
ioctl$TIOCCONS(r1, 0x80047460, &(0x7f0000000100))
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "e79081c5d4a5430b89999f321fe4d9824d8e2921"})
write(r3, &(0x7f00000002c0)='0', 0x1)
syz_emit_ethernet(0x4a, &(0x7f0000000000)={@local, @random='\'\x00', [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @remote={0xac, 0x14, 0x0}, {[@ra={0x94, 0x6}, @generic={0x44, 0xc, "076bfea25599c90e99f1"}]}}, @tcp={{0x3, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
syz_extract_tcp_res(&(0x7f0000000000), 0x2, 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000100)=[{0x45}, {0x30}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x2}, {0x64}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000080)=ANY=[])
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x30a, 0xffffffffffffffff)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x400004000011830a, 0x0)
r3 = dup(r2)
write(r2, &(0x7f0000000780)="089267d3ff4f0b87969f", 0x100ad)
preadv(r3, &(0x7f0000000100)=[{&(0x7f0000000180)=""/110, 0x6e}, {&(0x7f00000007c0)=""/4096, 0x1000}], 0x2, 0x0)
sysctl$net_inet_ipip(&(0x7f00000000c0)={0x4, 0x2, 0x2}, 0x6, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x22, &(0x7f0000000040), 0x0)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
sendmsg$unix(r0, &(0x7f0000001f40)={&(0x7f0000000640)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000001ec0)=[@cred={0x20}], 0x20}, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
chmod(&(0x7f0000000140)='./file0\x00', 0x3f)
setuid(0xee01)
ktrace(&(0x7f0000000000)='./file0\x00', 0x2, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x5847, &(0x7f0000000140), 0x800, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x5, 0x408})
semop(0x0, &(0x7f0000000080)=[{}, {0x0, 0x0, 0x1c00}], 0x2)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c000000e3", 0xc)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
r3 = socket(0x18, 0x3, 0x0)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000080)='./file0\x00', 0x23b)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000240)=0xc)
chown(&(0x7f00000000c0)='./file0\x00', 0x0, r1)
chdir(&(0x7f00000001c0)='./file0\x00')
setuid(0xee01)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
bind(r2, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
rename(&(0x7f0000000100)='./file0\x00', &(0x7f0000000140)='.\x00')
mlock(&(0x7f0000fec000/0x13000)=nil, 0x13000)
openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
munmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000)
mmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0, 0x1012, 0xffffffffffffffff, 0x0)
sysctl$net_inet_ip(&(0x7f0000000080)={0x4, 0x2, 0x0, 0x23}, 0x4, 0x0, 0x0, 0x0, 0x0)
clock_gettime(0x3, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000002900)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x2d}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000040)={@empty, @remote})
syz_emit_ethernet(0x4e, &(0x7f0000000100)={@local, @local, [], {@ipv4={0x800, {{0xe, 0x4, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}, {[@timestamp={0x44, 0x24, 0x8, 0x1, 0x0, [{[@rand_addr]}, {}, {}, {[@loopback]}, {[@local={0xac, 0x14, 0x0}]}]}]}}, @icmp=@mask_request}}}})
mlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000040)=ANY=[@ANYBLOB="82021bdf03"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
close(r0)
sysctl$net_inet_divert(&(0x7f0000000140)={0x4, 0x2, 0xf0}, 0x7, &(0x7f0000000240)="dabf133c0935e8ff7963e97c26952d2d255a41ad059de41fd5f299f5a4ad2a8d3b781b514dff23c2d63c719120000000090000000000d21406000000000006d6e3dd386e63dd9117fac2da6cfeabc5da72be223dcb17eb8fa8d1c2bf097d188d8b01d8cebee5d495445fcd34a16d082a672910ec24c63b2a2344f7dd00240d44ec71887818510cbf88acf5b96568dc666070f75a091ac18009d27708e02ce4abd144dff4c2a9263e4b44f545e6bb01030c1b19f6776c27fe55892018ab040c6fab0c659ad70ce0fc1c60238bd6dd84d0165d5c7d49e4929e6f0fd6f6b0727d3f1332e49ceed9f2904b8561cc429726760209565454031a8d6683fa64d963e8011be52a111475fb7e39a62f80b0ef67e6d05547065dbf", &(0x7f0000000180)=0x116, &(0x7f00000001c0)="8cb5078ff9773a6022faa35cb209e6fc66baf9c03700694d38925cd1f1f89743e1f02ff76dbd10bc1dac7798ae6ad1dde18ee24511281857c886bace0815c5230b332bf12f", 0x45)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0xc0284457, &(0x7f0000000080))
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040))
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000110, r0)
r1 = socket$unix(0x1, 0x2, 0x0)
recvmmsg(r1, &(0x7f00000002c0)={0x0}, 0x10, 0x0, &(0x7f0000000300))
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
r3 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r3, &(0x7f0000000800)={&(0x7f00000002c0)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f0000000080)={&(0x7f0000000040)='./file0\x00'})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="d5e8aa17391829b02af464ece47a36e121a7121c6b5700294093ff8298d91a42dfc10a9ac33a946de3e153c751ac93c979a740c36875e4df0656b612cc7a8f787b8fc366633118c33fe878a34bbd9fe6744f15f3824ac7d042a010635987ac880b9280ce4845f3a6aaa2ce2c5448a90d54dda0a56bd0a3515d1d5c64f3ec5d5d017d", 0x82)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000080)={0x0})
setreuid(0x0, 0xee01)
r0 = shmget$private(0x0, 0x4000, 0x0, &(0x7f0000ffb000/0x4000)=nil)
shmat(r0, &(0x7f0000ffd000/0x1000)=nil, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0206981, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={{0x1}}, 0x0)
setsockopt(r0, 0x29, 0xe, &(0x7f00000000c0), 0x4)
syz_emit_ethernet(0x26, &(0x7f00000001c0)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x18, 0x0, 0x4, 0x0, 0x0, 0x0, @empty, @remote={0xac, 0x14, 0x0}, {[@lsrr={0x83, 0x3}]}}}}}})
seteuid(0xffffffffffffffff)
pipe2(&(0x7f0000000200)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x30004)
ioctl$VMM_IOC_INTR(r0, 0x800c5606, &(0x7f0000000240)={0xa30, 0xffffffff, 0x4})
r1 = msgget$private(0x0, 0xa0)
msgsnd(r1, &(0x7f0000000c00)=ANY=[@ANYBLOB="843bff6fa757bc51a47748a031c4b055f40c6e5464b181c273c3d3f5cdb435191ef06fc0df06a5f885e9ea5181e6c386e9f43fac4ae1bce6e3ba54d4f1e94be4702826748b3da82d5bebd6eae7ff54cc65a1a149619c89b4b74b3f177dcfb1d362dcd501fc819c2372c9208ced42299578c3974d99bb6639be1b7092428532b4cdd07296c5e407d66ee18a837090039168542dac600331652d807aa700bd65399d6e197fc5805eef7661b1df5f54282d077da010458ae5e10fe3ae180fe783e863bd67019c6c8e80014abb174814998de8512dcdb54b84562ac24c5c489062a665a6076853450e0c05ff3cabf523c142ce0baa83554c1a1c42f00766e43c59b20fa329a34f98fba8d7fcf305798576c3225cc228561c9540ae54faa56438ea75b168755db1bbe1d18be87756c568a1b5b91fc909c5b3c541070c949794b5eed5a3bd247271598f614bd80ff93c2fc3c8d6ef07f50b9a1d4747f82108a0e1671d38b519275cf2dd4cf5ce6938b318e347ace7d6a6b5a87d01a837a7833f9398a6530c3e37179d0a041312515122970a8aa847418b1c759a9951c2636176bd833587168ee71eff337300", @ANYRESOCT=r0], 0x44b, 0x800)
msgrcv(r1, &(0x7f0000000000)={0x0, ""/43}, 0x33, 0x2, 0x800)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x5, "000000b3060000000000000000ddff00"})
r0 = socket(0x800000018, 0x3, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x9ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r1 = socket(0x18, 0x1, 0x0)
r2 = dup2(r0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r2, &(0x7f0000002600)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002500)=[{0x10}], 0x10}, 0x0)
sysctl$hw(&(0x7f0000000000)={0x7, 0x5}, 0x3, &(0x7f0000000100), 0x0, 0x0, 0x0)
shmget$private(0x0, 0x3000, 0x0, &(0x7f0000ffd000/0x3000)=nil)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000040)={0xa, &(0x7f0000000000)=[{0x0, 0x0, 0x0, 0x6}]})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000000)={0x1, &(0x7f0000000040)=[{}]})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sysctl$kern(&(0x7f0000000000), 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000001640)='/proc/self/exe\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1, 0x10, r0, 0x0)
preadv(r0, &(0x7f0000001580)=[{0x0}], 0x1, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x4, &(0x7f0000000080)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {0x44}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@local, @broadcast})
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}, {[@rr={0x7, 0x3, 0xfe}]}}, @udp={{0x3, 0x3, 0x8}}}}}})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000380)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x7, 0x408})
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0)
setitimer(0x0, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f00000006c0)=[{0x28}, {0x50}, {0x6, 0x0, 0x0, 0x5ffd}]})
write(r0, &(0x7f0000000040)="54ead1000000fcffffff00000000", 0xe)
mmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0, 0x61fb1adde94a5a52, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x7}, {0x7c}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f0000000080)="23295428e2fa906bdf4ba040352d", 0xe)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r0}, 0xffffffffffffffff, 0xffffffffffffffff}], 0x0, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f00000002c0), 0x3, 0x0)
kevent(r1, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x39, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000002c0)=[{0x15}, {0x40}, {0xdffe}]})
syz_emit_ethernet(0x22, &(0x7f0000000080)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @multicast1}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x7c}, {0x4d}, {0x6, 0x0, 0x0, 0x800000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="7cdc3fc0aa17dddf7830faa1aaab", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x87}, {}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000040)={@random="e3bdf1bb2c7b"})
setreuid(0x0, 0xee01)
munmap(&(0x7f0000ffa000/0x2000)=nil, 0x2000)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000140)={{0x0, 0x0, 0x0, 0x0, 0x140}})
shmat(r0, &(0x7f0000ffb000/0x3000)=nil, 0x3000)
shmat(r0, &(0x7f0000ffa000/0x4000)=nil, 0x1000)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r0, 0xe, 0x0)
msgsnd(0x0, &(0x7f00000006c0)=ANY=[@ANYBLOB="0000000000000000250cc96b72bd2a4c1733c65206bee968bba4e13e2334c0aab69e97b163bde38cfcc1769c170dafb860e19d042654ab4829d40b22b9644a0000000000000001fc5d8bc38f6bfbf84b024f4087d47405c5953ff98c2316092a2b7e08314a3fd54284d38ab4a5c2b6e1288a7abe5736d21c0e3b52e734f4dfad75d1d5ad6a6d647013377b36d9581fb244a5532503e88925d48000000000ffbce9b83ec8bcae8fbc6c9015ed96d18a0dc47b26300942948de78a0053abab89f20820f607e686104f67aeca093e72f5f43c967f05a410086c030dba59b3f5a9a8d6abc858822482328beca0d6003eddd76eb0198863410ef4767fc743989314605688ca0041b5862893d720dfb6b2f34e01c1d4cda3f220014da8035191456032721ef22d02217717c4da64597d141708092ddeec740cd7ea1e06ad3d9c962f0f49c329d5020000007cbac5e6be71f9632bfda06f9370e423f1d1bd0cbf48640bab69fefa774ad3229f9dc0ac3e7a3ad2c52cfd3dd9f1dbf53193a4e414510c58f86b060c1049e6c5b8b1415939ea90b7aca166bf4eba92804cb5782bc6392721982ab1106799e30f4c0b43f7f42dd309254a9f4d3f68a606748d39920ab863b5dfdf5c10d4c40b96184b659bedd599b019f977b1fd12ef9e9efad6ca54410611e89475561d713d215e150eb5b943a1e8f243bd134e263ea772f5118297b187a10fa29855c0182900f52468a49c123ef86d673fd79ab5cd77e86e3b979ff491df8244b0814885a1dbc047fdb3e73fc1c78dd663e10ef2263ffd2559889f7266c08bab87c4a4765e38bc9ee734cf604874abeb8abc0918c8a8721ebee25e353b48424d83cf"], 0x103, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x21, &(0x7f0000000040), 0x4)
r1 = msgget$private(0x0, 0x40)
setsockopt$sock_timeval(0xffffffffffffffff, 0xffff, 0x1005, &(0x7f0000000100)={0x4, 0x7}, 0x10)
msgsnd(r1, &(0x7f0000000980)=ANY=[@ANYBLOB="000000000000000055bc33060cbc152723cb53137ca0194bf4d398a0b16674db510b226fa46cc51f283e1bc2f6158637277817c5c75d13e56cf464d0b831c3bc57e96c85b2e53405198b45b156a61021dd54a53d6c9614639cea807bd6d56194b9c285c23b6700005126aaf4e20c3bd28abef39ec7e53130568c17c8859ddec7e1272187faf184fb93187cdbd092a79a10c71e12e2b89be17c6a02820c2e9dc4e96a30f6b6394a9f3bcdbc8f53323d5d050000005eecc7e2e2ba427e9b646ce95a895fdec85ac919f74fb5b3adb2339952c67118089c125db77f69de36f3d05bc4b803cfb14bdf42a79c0b44c6ad895d8d6d110302d3b6013f0b212b83c231241864c65071656b656df445b2c1666a3453689637a367d5aaad835c568f7d0d4af97a82cb2d8feca391f6105f359f44fc036a8f018fe7a44f096a0a573070566880d1989b540cf48571ff02ed11667cb3c46b90430b5d65968073a4943f9f6fa87a16798ec470e28db32173f7b7ec90f1cff0866807114c1124a8fbeec954a9f4f889105862296c6e80545ae74b9011bfe712315553ac648f8b69e3e234158a960ce9c45ff83857041bdb398d3e0f0c771a3a1cb6526ad8ee689ea8e5994a218f9a5901afb228f484762caf8abf4b4d704370e1355fabb0d357524a1835aaae6e9098991d607ab49f9d660da712e97a5a4d751ab01ad097729b87a18c9b1cdd9abed03a12f5fae25711a6874445f9d7c6cec95e99a2ff1225cfa8c30a813fc63df316a046b31c57fc8738e63735e609666590ab197ab620e0c14d85eef67fdcf68026328cfb9e3d5a5ef234513bcb45179b10e70ca85c78657b3ca8cd6216e768"], 0xed, 0x800)
r2 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000340), 0x1, 0x0)
msgrcv(0x0, &(0x7f0000000040)=ANY=[], 0x8c, 0x2, 0x800)
ioctl$WSMOUSEIO_SETPARAMS(r2, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000000)=[{}, {0x6, 0x10001}, {0x7, 0x8}, {0x20}], 0x4})
pipe2(&(0x7f0000000200)={0xffffffffffffffff, <r3=>0xffffffffffffffff}, 0x30004)
ioctl$VMM_IOC_INTR(r3, 0x800c5606, &(0x7f0000000240)={0xa30, 0xffffffff, 0x4})
r4 = socket(0x18, 0x1, 0x0)
ktrace(0x0, 0x2, 0x40000522, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f00000002c0)={{0x749, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x28, 0x8}, 0x5, 0x1f, 0x0, 0xffffffffffffffff, 0x8, 0x4, 0x8, 0x7fff})
r5 = msgget$private(0x0, 0x230)
msgctl$IPC_RMID(r5, 0x0)
setsockopt(r4, 0x29, 0x32, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff10", 0x78)
msgrcv(0x0, &(0x7f0000000380)={0x0, ""/225}, 0xe9, 0x0, 0x1000)
r6 = msgget$private(0x0, 0xa0)
msgsnd(r6, &(0x7f0000000c00)=ANY=[@ANYBLOB="843bff6fa757bc51a47748a031c4b055f40c6e5464b181c273c3d3f5cdb435191ef06fc0df06a5f885e9ea5181e6c386e9f43fac4ae1bce6e3ba54d4f1e94be4702826748b3da82d5bebd6eae7ff54cc65a1a149619c89b4b74b3f177dcfb1d362dcd501fc819c2372c9208ced42299578c3974d99bb6639be1b7092428532b4cdd07296c5e407d66ee18a837090039168542dac600331652d807aa700bd65399d6e197fc5805eef7661b1df5f54282d077da010458ae5e10fe3ae180fe783e863bd67019c6c8e80014abb174814998de8512dcdb54b84562ac24c5c489062a665a6076853450e0c05ff3cabf523c142ce0baa83554c1a1c42f00766e43c59b20fa329a34f98fba8d7fcf305798576c3225cc228561c9540ae54faa56438ea75b168755db1bbe1d18be87756c568a1b5b91fc909c5b3c541070c949794b5eed5a3bd247271598f614bd80ff93c2fc3c8d6ef07f50b9a1d4747f82108a0e1671d38b519275cf2dd4cf5ce6938b318e347ace7d6a6b5a87d01a837a7833f9398a6530c3e37179d0a041312515122970a8aa847418b1c759a9951c2636176bd833587168ee71eff337300", @ANYRESOCT=r3], 0x44b, 0x800)
sysctl$hw(&(0x7f0000000080)={0x6, 0x6}, 0x2, 0x0, 0x0, &(0x7f00000002c0), 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="4b02c87f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_int(r2, 0xffff, 0x1001, &(0x7f0000000080), 0x4)
sendto$unix(r2, &(0x7f0000000100)='\a', 0x1, 0x0, 0x0, 0x0)
close(r2)
sysctl$vm(&(0x7f0000000380)={0x2, 0xa}, 0x2, 0x0, 0x0, &(0x7f00000004c0), 0x0)
sysctl$vm(&(0x7f0000000080)={0x2, 0x7}, 0x2, &(0x7f0000000180), 0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0xe}, 0x4, 0x0, 0x0, 0x0, 0x0)
select(0xffffffffffffff7f, 0x0, 0x0, 0x0, &(0x7f0000001280))
sysctl$machdep(&(0x7f0000000000)={0x4, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000040)=ANY=[@ANYRESHEX], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0, 0xffffffffffffffff)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
faccessat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r0, 0x0)
select(0x40, &(0x7f0000001000), 0x0, &(0x7f0000000140), 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="d5ff9668", 0x4)
getsockopt(r0, 0x29, 0xb, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020697f, &(0x7f0000000300))
r0 = kqueue()
r1 = syz_open_pts()
kevent(r0, &(0x7f00000000c0), 0x401, &(0x7f00000000c0)=[{{r1}, 0xfffffffffffffff8, 0x3}], 0xb200000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x44}, {0x2c}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000000)={@local, @random="3a6d1f6d3961", [], {@ipv6={0x86dd, {0x0, 0x6, "3835fc", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x80, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000100)=ANY=[@ANYBLOB="fb182e0b3d9a090000000000000043"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x40}, {0x60}, {0x6, 0x0, 0x0, 0x4fe}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0xfffffffffffffffd)
chdir(&(0x7f0000000040)='./file0\x00')
mkdir(&(0x7f0000000140)='./file1\x00', 0x192)
rename(&(0x7f00000002c0)='./file1\x00', &(0x7f0000000080)='./file0\x00')
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x2}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000d80)={{}, {[], [], [], [], [{}, {}, {}, {}, {0x0, 0x0, 0x0, 0xfffffffffffffffe}, {0x0, 0x0, 0x2}]}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000180), 0xa, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x2)
poll(&(0x7f0000000240)=[{}, {0xffffffffffffffff, 0x1}], 0x2, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xcd604406, &(0x7f0000000240))
r0 = openat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x21a332d536087be, 0x0)
poll(&(0x7f0000000040)=[{}, {r0}], 0x2, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0x2f3, 0x0, 0x0)
sysctl$hw(&(0x7f00000001c0)={0x6, 0x8}, 0x2, 0x0, 0x0, &(0x7f0000000280), 0x0)
r0 = socket(0x800000018, 0x2, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x9ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r1 = socket(0x18, 0x1, 0x0)
r2 = dup2(r0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r2, &(0x7f0000002600)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002500)=[{0x10}], 0x10}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000080)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="10000000290000002a"], 0x38}, 0x0)
sysctl$vfs_ffs(&(0x7f0000000040)={0x4, 0x1, 0x5}, 0x3, 0x0, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100051660000000000008000701000000000000ceb1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257690000132e27dcb5d602000d7d026ba8af63ff37422902e4fdefe0952e89f21de070c1f5ab72c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002003c88c1cfc044101b5496fe", 0x9d, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000001c0)={0x0, 0x0, 0x0, 0x0, "93044e8000"})
readv(r0, &(0x7f0000000600)=[{&(0x7f0000000200)=""/134, 0x86}], 0x1)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xffffffffffffff0a, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x8000, 0x0, "dbad8c37dd71a52a0d3a737af300"})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0x64}, {0x4c}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r0, 0x0, 0x6e, 0x0, 0x0)
msgsnd(0x0, 0x0, 0x0, 0x0)
openat(0xffffffffffffff9c, &(0x7f0000000540)='./file0\x00', 0x0, 0x0)
getrlimit(0x0, &(0x7f0000000500))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x100})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000000029, 0x200000033, 0x0, 0x170)
getsockopt(r0, 0x29, 0x3e, 0x0, 0x0)
getrusage(0x0, 0xfffffffffffffffe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x2, &(0x7f00000000c0)=[{}, {0x7fd}]})
r0 = getpid()
r1 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r2 = getpgrp()
fcntl$setown(r1, 0x6, r2)
fcntl$setown(r1, 0x6, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f00000001c0)=[{0x44}, {0xc0}, {0x6, 0x0, 0x0, 0x7b96}]})
pwrite(r0, &(0x7f0000000300)="c8dcafccd1a6f48da693157343cb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000040)=[{0x40, 0x0, 0x0, 0x800}, {0x6}]})
pwrite(r0, &(0x7f0000000340)="0900000013eb0913587ca9ec25db", 0xe, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
ioctl$TIOCSETA(r0, 0x80047460, &(0x7f00000004c0)={0x0, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c651536a0073729e958b7ec0"})
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = getpgid(0x0)
fcntl$setown(r0, 0x6, r1)
r0 = kqueue()
close(r0)
lseek(r0, 0x0, 0x0)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000535000/0x3000)=nil, 0x3000, 0x0)
chdir(&(0x7f0000739ffe)='..')
mlock(&(0x7f000071d000/0xe000)=nil, 0xe000)
munmap(&(0x7f00006b5000/0x4000)=nil, 0x4000)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
writev(r0, &(0x7f0000000c00)=[{&(0x7f0000000100)="4232c104f1c85b15a3ca06baa9754d92f7d99ba3667c9f0aa76bdb36878d7b89ca7a963dd78078f7fb9d060285b2361e1b301027c30900632d8ace9af05c8283279600e7fc35af552dca413ff503ff3288d87ffaefec31de2c83d1dcbe1f54582dd74cf8f4b86f5d9d269b4648cf1abac2cef4a1318e344a25dfa1e2e503853d59cb90926edd2b7897c5834da91d3a1abd68b458349618cdd2e0f21331800db2b9bb2431e90ad42cdbc6a4a28d37327fa7595eddb6c8e0f27e", 0xb9}, {&(0x7f0000000f80)="d9629a9a6f925bfef4ee5668162069cdce698a606c8f4888ce81711cca7d8835edf456ece69fe870b2c7dc1d3b04f4fb57c4633408ca4a47b4f02a62e1af90cd20bb88d5b3d9153de3278dbf9acbe70ddb0bbab958e5731720602288c1d11669cd87aef35f3c81defe5011adca3c070600fbd28b7532f3dd22362682f44938fad932b61c6544f27ecbb852d247700cf82c6f94f127b46fb020c66602d50601cd358997c5b51e48768ff72f78c6d94d4d2cb6083f8129139dce351a657c2b3ba00ff39547000000001bb51113568949d7872785346e1982505277ccfec8d1c2d64bd801a4843a752f672395c44c994d578a30218b19d42f2744827e07e2eb225e32ead90c533b00000000000000000000000000007a14ff497d812f4b3d9fbbd079a354a90ced2f93dfbdf301e2be6a26936b80c2f2d7b4be4dadc088810c8aee82a0661f461e94da43e8b5ab8c5938537d0e86a614e803dea7b63f67645482f09cb68f6bb3394bc92c6046e16b2043d545d58f07ae83aaed9dd81dde5587577d288a762c29ecef2122abc6c5e9168d2345fff490138284a25fd71909c210b76a3dae2c39cba1ea94e41d9c28c25b8b1e509fde847cca0c2f6adf74d9ff4e4235d7f78b0ad30f1d3d0c933961de360d203acd94091c8eeb7584799675f1606b4730a67aab67a1ef5d00abf9457aa99d43d33ce7bd21a35b115db862c9b9a7e744dee55afad66d8b10124d67dcb0759f9163c917c840e370c486fa06152266b6c79e82609dc5ea682f1f41bf370613818428240e9e33eea6", 0x237}, {&(0x7f00000002c0)="196394c52ce9b96b23cdb04e3f778d558139c966d23eb35b7069bc7c7f0dc8f761b34727504b2df7f0b671e74a9fa3f1192632c5e3b80111e20aabe4d8a15432b2a4361ac55588ce326671573e3325a340a95779", 0x54}, {&(0x7f0000002400)="f7e9ac12e116a0e7496951ee7b60b2130ec49be87b2a89efdaa77b44e69331e21be9974ad61b52bede47c7437d2a0e5e221f024a7231c0ccfcf24dded3dcc90f7e05a2fbd6809ffc2c5130ceeb0c56e4804a9f88aaebf98f80a1ce25fdfcd6b75c1f09cd1e3de2e3ca94c9e7c914400771501c6c338e5f636cfdc1e9e8abc1fe7956c410cc229bf7754f9b9dd629bca7baebdc8c96392032b484d4b932910afbbf50744eae126019323e26343544330108088918d12e5fea5aee0abf174854809b521f025fa46d42ce95763dc9a65a1733565f55ed5bab919ad320d29c7f7d2279efe56fe145e8c53f3861f64d7fa687f6b31398e02ffd80f51597a8b4571e2118609d439dcfd5f8a8f1e9c692933d8bbd501e432aaee880da5c9ad2fb2e196d373faec3bca7064f937d35b277663a8a41f89f5f2bc899cb53aa212add189aa1b92013f1a0da48e036cac1b6683448595807d3236cf24a1d84613f2425e66b71053db7741fdf924687cac3ba0b9ce4466924fb26092331de3b8ef8fdef935e87bb617a7e4de31e2d832886979cc2c1fee2a1e483ab970aa8d2810954f6bc9cd42f7b091e6ee22415a59ac0569830e11ba98a8d7b55920e33a580ef091c241259dcfd52c113ab601506c033fc3ee300a653e3c32cfa8c130234f61e0e027fc5a28dc24d8ecdb434f8dec68a28121ea991bf4825ffd45e712466c137870e1d78774c9266dfca40bb529a59aa5cd49e118a44e511ec83145d2b7ae2bb6098d14799fb260c9ac23877dfe18ea4c223475b105eedb1950e5b4bf769baaffb962f684c6c4ba38438cc1dcc7df7e13bef33a068d41f926b30d4ffeb20df223b368b3d3f43041ab4390cda9486d64fbb84d7f97a36a025329ea9ce6dee020faa4e3c7fff30016ed2415ce1102602d27528fef180a7beacecb00db01b20d0065a7366d14003849cba044b9436f9024ff4614ca01d1fcf04b30a6771a4b673bfbe526fe83e3bb357cbb83875d8c86e67b81d5d2b84a9f96a8650720fcd13c8a91ba800e27afd17f4904e95838851cf93f537bf26808e6a4ee4c8eee6e97db380bcba1c1a69e23497d3123577a3628df66dcc421e817fd6aa380d2a2a1641a418c1a5291eaddd64e87091f0572bc1f11f8507f3e87bf05b7cfaf66dc912b302614042a06ad500435d92dbf6218b30cbb1fc7b3cb65e0080e484b4364d12e1149f95930e54b8c971632f3b06cca8dd0aa6b5ce7416fa8f61484463228cb5f021a84be99e4530b9e464e2daa4f962eb23b5871188c7c0f8d060999bdb2734ba472caf8ec39faba1bab04dc7f8eee6e6e1876f8ebbbf2947d7a808ad4f681897392775dacdbda65ad64324929caef96f7a2eca5a36b6516337da61cfea16b067c06c574b7be4e7e62477b7696ea092f6995f46de69fec41be19b9f27593e55095a248ab1d49355fb9789bcc6ec038c843e0919ed9c38c3f36fafed6620681d2351961391df9bfa7ed298bde46e3f31d675a157a5a6d07290a090e3fda4f38c21dfff2104cc96763d3eea49ae2b66a8d83d2b735eeb322a1d254331f04c1a884d6853bf1af3b303f0665fafc46f88e76c8c717433a499989eda5ee6449832478b986a3f86160d12219cfc0a7ddb026e4395e3cb9344aea31e6b7a723e259c621c9b1e4bb679d5340c9b549866b8a7a42c9b831ce22e2b73cb3426e021c7e40fc5081cbefacbfb032e628be21588e3efefe7f4ac4cd447f4468008e2cd610a9cfb7f247c07d936e653d20a1bbdb0fa3cb3393694563dfc8ba5921128c5198ac063fdf3d3adb0a90249b89c9feabc8f3858594f86d652f1b224004b93243891b9ff43fc5c88d5db74764b1b6825dcf8308c8b0ab370bd6345f4e958e830f33c52f980904a578720cd8a8531df79802d445cb5d537e02a4c25e18b22b2379514e35c59fae6907954145eb5f8556768ef7c1543d51f02387533d99a9f6dd9ef86ca8c844e169d525276fd864befd79bc4f338aa1e72326f91fd6e9d96f27847edb571a4d6256cfd1aaee67bfd556960b1cc2dcd9899886dd7d5b5f12008ceb3cb89b9cc30162b37e6f5e52e6461e6ed70c1196bbeeb3211c68b6576a7ef04a13dd4109732de4a920401d8ab62e287c6c3f5bfb1063505cf9a2eb6b36ba5f7e5e5b584e0e8c457f6e99e4656dfd821b40070a2eb6c828e6ddaf1d9f2c04aef53d4277180cf2a5394f84c886824dd30d23100332e0e56923e273eab50acea533c8c60f1f9e3552f54a449696ba0c256d08632ec59c76fb3564c3caf26245938b7d6379f803f860b748048bd6a48e89f3b2199226672e882c93c47840ea25f76d08613ceab5667fe9d33f405715884db19205c3f2edcf3bd6dc4e3178d694894cb28e6d338cc223ec9e6cf3b58e7451edeefc6f39c1835109f2737d429adeea183e8fe34808db28d3b71c83c15a1e478579a37152a4064bbc4d084cf4415a8fa1835fb5a84aa44023f24e827618124ca06108069f363c523f935226c531da1082328e090defcbcafda03a6ca0b4cd15e9e89115007fccbf28369b4d26c3600a006c3c579bea9674d8a70085365d4d6d2d242c40e662d1cfe9b883fcd914af23e3a6f2c7f58aa1530c9257a10b56c6a9a4d794099278d3a932ce04adc7b9466dce190ea2fdf2c741f5d5996f44cc60f15c8ef2f91ef0716d9fd26a54908fd6d2130f421dc1ec2fbbd5d3db393a077c83b6f01129fd85fc62461e327dd5e5a8877186c19ddd078347bde3b4f68486ce2c9c10f849ca86fad4d8befc75a5e0a1fda33c24c1fed82a9ae65b485edcd7f14569100fe410a9f8fea026f4a63db827bd4fe2127bc6441deb60d3bcd0a1b33ed44be0c6ea678f6ddb4943d9285be54f96bb7bc81253f18d0c23da81c9e6aa88601b9f056ae2878852a87afb998ea131b852a2743dd811d2803d9784fe336ce1dee10c7dd3137aadb781415d57ffa54d9770fa6525b414477ae039c61ea3073d2b802302ede868db0e5caf8db2e7c5b634f8003fd2082ece5307ccb27a0bb54b61bdc5133d59c62bbe1f94e3388b596b563eaef61af60195aabd14aaf0d6ee52d3f5231284feb755539e59ce8043f8aca04c8cff20b822e8f1bc8ab4d937e20b059fd4a5873a53f2b8ac71aeb758da1a5999ff8ae05dc2f7b21cdd4ac31a346a95afbcce4b291d50572eaad3894902cf5e4dca4ceaa98719e2d1ef674bcbb5f5e94f45b61559957fe4c7bf0525b7463c7b86ca4dfa6729b35a79a6379eb6d0472d85b7daf1e3e74296fea757c464b584a0aeab825d63cdb06c09b4fecce39cf8c7320623d6d1ee5d9edd35e4893f9dfc836230c2262dc7cac1b9dfc3d37001f1fe088900f46bf076871e42609b408d17bf5e1e2c10dda433d8ae871c2e67b4eb902d99154041d4ede699a319d94d6bcf4bcdfa2cbf27e03af455201a2985c8b6887a5aefec7dc7c18f63311174058602468ae7fb180fa00a51e5e5fac9e71aa25187d08681a99bdc2bcfc1387c828e4fc92eae87a38a01a5e89e35d0f373b9db66d89ce8a9e0cf2087847721eb42f1068da4f1da803748c9363b09e40d80b586765e5ef43a7095e96001145d3d31744d776000e3931cecdd3dc88ca599bc8562c89c8f04087161fe9c19661bdd7f68cdea59aa79d9da1e7228f236f624ac560aedb0be8fa3076cbc5082fe6b3c850b4a38b95471fa1affc9ce628a06963c7e623874291b29b59ba3041593082fb8cfa8ad79c09b54f5f8fcaa51900c4735d8fd335e63791ea86606dc43aa209adfa96d3e42a1d6ed5224551b8e2e914d463f4ab0584bcd3029c5a7cc45b28819ded3503782ef80a3f0c542a1b9295fb47b32a192698dd5d370eaae670173a8e28f9d5faa51b7a28ea077fed3e8724b3431cde00d9e31dbd169a5bfb57d816e6c4cdf100a05cea7eeab7679ddac3f7808c9a5d50ba0b776c9378ca0900ff99a37f63d55c2f34d8fbc6d6997a5fab5ed951f3c79ed5c15981de70fa579bf7733fe63c6df0cc8277f61db79140b7964293611ea0e7da717f5a2e73a65a0d3c5c03f276bb448b80d99292a214f9e4ffa4a36f28af4898e6f69db8f9ae4917d77bfbddc78f9f902c223825378f61cba4d21beffffed2e6544c27f3c388feb41a964610cba799bdee0c0ecdc58c2009132770427d860af346678b8dbbfb3324162a7d0db42b2ace11a61fb07f497619c1985f82bc0d158b083e3ec94674c50e419c4a04192f20fb095aeb387a4b35cee1d9200977b3d7d3366f7e4639e5e8876f25b88a00119adf7baf0f3c194aa14e597aaf42503678a64e5ab25b66d05dd1ebda37d9edbfc5ff39bcf866941ee925d8ac37142a88eed9389df395839855767233270136f05a88bd3b9121f8cf833bf906aa7b84f3da799b0c15020fc00a882ab5f7c371867a36f71a82cebb070a0bab7c340f4bde202a7ac3819cf138240a29d377bab3ff2649a61f286b85cc8bd1e13116c334f31968816daa4e442051f82d62a9117cdea46c8df2e9c8dbe51822247de00c707a83a4a6e83c85998b9550efc7efc1f721119f3b65083a8958e25cc57f8e45f4d8b36d60c441786ab08acf494393eb7c47e4b295be8d9d58101adf0f060fa893ec6507339a447198e80f7071eb871ba04a9701f1a545a08c8a9eca7184fa1b7eab161e9c4eb1ac96950ffc76da189d7854aea79e599abfaf91ebf067e722ea0307e65edd96f406117a8e005f66d531ea242e09546087fa1471d6dc9a257643df37c93da67b522c7df4265b52dfcb786c8594376419f6cd69ad9acd7faa429120c24086ae64aefb98827f0aa44ffc97e4a70d5503768a57366b97c06903256b8251d5e7a327fe9865dd6782442118533124c09bdf778a61e085d7e73ac0c3c8b163b816ebe7215d6f36f2547e287057c8b505037e384ebb0f7760ef9ab03617703fb9f896d91e02e8d26fbad7c05c55d59468df8f98e97908cc7b2ea1643393ea4e2d39febcb1502657c435765616db502daa5313426709feb9f12397e12d04a1aa3ede697c32019cbfaeb4f5ef92ae47a87927c5bbff31f60cafd094d65dd5aa8ee0b6e5dd22e13475a02d8cb1eb3c1a2303efb8c8cf6f52474970da806f4ce4c2f86579d3a7c0e0fdf8460253af76547a1644ecb5b76c761e4280d602221caee52be39216745d9dc77f86a8b7832f51e6c17a43a6cb431981a757b6db632620d38145f2f60b0eef8be20af390045b5bdcda92223549f767e9196089ee9d9c470b9d5ba8f4799921ea0e019d3284354e2c99b0306eb6dc647a419ed1b337bb35e9df80c700d56c17af9d99686d4ce91797895f18be45a3ce74a350ebee849049eefcea4c39e62e9959c4a8904b76af718ebabb2be8858d363e7ae9b257628655d3d2503b4fc700949309e0ea86a32425b34a7a4472b6bc597e1b733383348686fa16d9ec073369e180dcf81428c1d91c8f417574727a7bf14e55af339b597e297f2b378afd3a675761b7bb5b619b0f967cc4e90bbe74334b7ec86acbfc6e2d226a99b2939e624e02851bb95aee9f9a715c8dca3c2cd524ee89b7e66dcba1873737449e1137acdcbea88207be8b0c4e4b4edcd6a67af0207fc6687c2f6ad3e492251a800e7561c7eb8e446c3f55660921db4302749ec1e347bbb88eacdfc4a4f78fb24714df694f5fe484e5edce4b1c62433ee3f651580bd8ae6f069c8dd9cf3bec7e2c871657ab8135f7e8720342b5b50989db61d69ac6b37041ff69541328122aa4a6a34c45f5c42992f81122ae2b3b5ad75189b72d61930328ff73209e61ff9a9961e89e2a418f2b0e01f310234c2198ffadec7d0e15723396b770a6dc7f5b7fb84eb2cefe94b2598db9ba064bb054e10e23fe551963f2928b605756cf8b9bde83b3c8a7d0c445811e34da472aa648", 0x1052}, {&(0x7f0000000dc0)="ace05362749f8fc838f33490d80a1befd9a572d5803c355c3a067722000800001227c2af7d5d47bcac274ac83f3226c3a0d3b683cc054977aaacd3d33e79f106382048854f9c89663dbad55b2d653dbf10f2fa33bd595daab6d69b925e6d92cf585ab156b08fb7c44a29511372a75e0d099a8fb743985807bcab3b26fd67e0a6fe2308185c872dd8f11043b355a94d3bd11d770d6acfdfe1285ea9690736c5e1d8e52e0e1a6e617215ef4dbed518e9fc5faac6416045300e158ab909f5eddf2383d1073c3ac151f46befe48831d9015f4797bab96396a0e934ab167fa162d10ebf949b9681d70fa08951adb8c8088e6f6a64e1f49856c391e8d3e7d535900aa93af209b5dd41b84937b6669979294d81f23bdc46cd24edec8c929ec5954cc3d42ba271ef917c9bc0c21a6ea5cbb420c42527f192f042bf8ccdbf46e82a22feef8fbe18a8b3c5fe95cfa20e87076359f82bb7a8798d01c8f9e93ec40a5966c62cac0275365c3e1fbdca13f60da8efa66cdad05f421ac3c817a16c85b02d1140c9d144773626d2c3db4fdbdef5bcfc63ac6629", 0x192}, {&(0x7f0000001440)="31d0db70bcb1c272de31ed896254acdbd5155a29a96f05c864ab48dee3141bdb9e515f244410309c906cc00ab5abee3ec49f99064213331924e2b92122f817d925df59031995fa0440c8d4f62d3ae206f327b7b4fadf4d9f362368d0cc7d7e240f8d40f2", 0x64}, {&(0x7f0000000bc0)="2970d540ae8cf1044f9f9f1bfa198f0e6e67418ee431b0ed757e65a1fcb1d936aa6ad85191e0d96ac688", 0x2a}, {&(0x7f0000001500)="ff58f03eeadff2d7313cd672b0faa148fe60dbc374bd233d6f6ed42cbe141d81864fe6e1d8673e0d0393f717fa02ee0eebe861acaf2be7b7464ccf581b2d9abce12b530efac6ce9f40baccba2fabca22de0eff37e9ce6a847c5ac5968d16c398f94dd807d6d73e19c2640455d9d092bfb77769501ebdf59181f106f3f0b57faa888ef3ebb632f665412653fed1efa96afcf38036391e458f46a1291bc5dbf64487780b1b8b28246ed62b82f33afb7a1fb78ad6f96e94dcfc0958df89aa3f6cac78237246b159d2940b27", 0xca}, {&(0x7f0000000040)="c6bba2859ff4299c917454136b9de5a30214bb89e2c801a2f71998ff200093e854e5dd320d89b407b294fdb82ba1aaf8b9a9c2adb6dcbc4434569c83040f14eba201da6847e62a60fd2461f744ca0291fcd42e738082a33b3511f4e49f2655c99075f06e94b4b17721ce58263486efbf5910f8a97a9fa5440733a3628172ccc0fbd499e0ea44b096279fc0b0836e22637de69b3f561b6a07c8af92e325c4cccd53f79c7e285cf3753849530910923b571213ef2a6071", 0xb6}, {&(0x7f0000003b40)="07276208fbe3059e639f3dd7055eb55425af85d8d34ff102b7ade3ccd9087a3b5c21cb7d96ab1d0ff3d8af0e607e45cf0f2b18e9b5d8220f8cf29b36d179fcb42a5b1e3e50b534b59e0469161c7eebd6f1fa37b5a92702f2210cea00b3b63c85965d9b29ff030000003c0856cfbcfbece35b193c9017f9af724157fe722c12c97ddba9c2389b3295d1af64a3a2fa5612dfad04231902291ea22c959ca693705600a0acb152fc9469a1adf7edbd81eee9e5c91943a5d1bd3cbb6b8c26ca", 0xbd}, {&(0x7f0000001980)="89e4b2ae3290fa0ed5f133105099", 0xe}], 0xb)
sendto$unix(r0, &(0x7f0000004280)="92881d7ecf832bad6d9337572d2b3e7567733c764d415aaec58f2bdbd4a988d53a47505c9e2c3338e6917b6fb408098ec208202bfda421fe3dbcdad6e26f2dcf2f731d176a51ce43ee3415c362c2fb0c6ecbb01054651b1e51cc76db66eac07b34b7acb4969424523929d875ebd30d2827f3b47e644328063be6c9f372e23840c9dd892e92dcfe6c03a14f10e25fed9b559ed07bf7a5fefaa232eb98329951029758b0f2d1f971f203110e9733bf85474765e27cc1fb8137ae5081c77ac4def3972e5a850484560c85463f8a7ef09a754532e1615186b31b34f1c638b15782fea8f6b0f48d0857523c5b6615de15262c43583be8bf42181a454079268fe39004009b402e4a55b2e734748adac4105aac07641f1641599d872a2a9a8e34b685a91fc81d634c3a33ed14f11019c0b735cfdc5844249810ccf102440aa5cc04f70a5853dbb542b59443300e8411bb3060f7ae1704b5dfd5068a0b4c56e45b37c1de4272934a03262b548701210a6f47a2a16f44e21582b626a8ed4f435e72054214b3a613a0d41029728c5f837c1f695066be336de8407ac7c03823e1f0b2b65206dbaa86b934531d8f9633af7c2b07f26529b2e7775ca63ed5a0a787c98080399427d7c496c43edf7d3b79f15dd2d00387056761f09fc8fd74c8a321e13a7f40d0536aac1c23a0cfcaa05535c0ab0830be59689b04e9049616b1504d798ca52cb3170168c58be9dffc59d610031c01e82a06c23525a7a48e30ba2926166f017e1250bad173852a377f500f78744258bf71a7fffa838042fcbd1f0eb4e59dcd63b4e547789488af64d6b7186fa22be0b47f4d0a574b7151ac95a997bda2305861f37fcc95d3380b83741e43a4840f9aad28517ee8e08c5dd4f1e3b3dcf5b3f71ddce8bfa08141b6ee0d3a69bef7667b93041cd4d21044f50e662e663666eb4d387fdadc4ad73a0ebeb6f54de744b2aec3c0d28cbadb1e4f726e5b0d0e1667ec82271dd6677d0c80f978ff791d729d8da96187dc5529d9ec451a64046c64400b11cd658e96f4cd9e35001517b40441281b956e23e6a4c1219b71fc8bed6859fc0bc816095a6cfd5af69a28c0e8813811ec7319f97e2ac00c218d4ddb9e8ac8fbf57f58614739a9fdb11376f9b3a4060a77c445311c23fa39456b70164729ccc456676858f3888cd8953919852392fbc3f9a60c212f37d7a0c9a916303ffe85bce5b51a8e39a2a02339e109337182b28a0469579ee3d62dd692ad9eb8d1d83b9543cdf06fe2351bc04ae8e2f4cc9cade8c7fa352ce438910ea7f152440e8bfb5d86ec57f78a14c6d9393e75b378f2964c44e796de912aad4099ca024c32437b738c053265d7bd014aa4f96f7d177d5e098df26bffebe9343229e6c742d78f77dfa25d079a7c2a39035fd17ae7808ccd9071d000d276d76e955f6d24f60766bb942ff640f5dd1bf7dfb131a4ac08196f4b62357bfe81b6133c309741af89680271a5144ade904269a0895c073e38705bcc83fbe7ba51e702a7717ab798dbee2446daca2ff24e3fc4c903e5c1a6d6f332fd82afd0035cdc8c22ef468966feaf37287710a775e7ba21b69d238026345c006ee6ec774fb3e0472f53f30a3049e184319d5523291268016455e3bc162c1330fa07ebccebf26277187d69f697da696a109cad41adc16ce8f6e45e09c1e73a6cb0dcd3f6af43fb65726ee2d53256a94af83fc4b32cb1ca3530b4b70da6d142dd289cde841b46641bd63b88ad4c73cb757ce675b20b812d9f233f644f863a3e0b0be3be31d9abc11f48698d38b14e6cd905104bac4baab0e62d4b55bb0b3aba157e550183fad0fc6a6a4d0d3eb2c26f06f2727f0be27c8d370cb42f105e2226f6acd32cf0a008692eaebaed6173a2787b6c99e98a7dae1f8537a786916b9c1d163fdc49ee159610615f3a0071ae0f2ff03ec599b92a6f5a3783bd170672e836c0249b900a25a0a8c64244eafe1eb1fd57f6ed3f4dbf895ddef943caedbed44fecfbf5e2863f4c0ca246d0c6fa0340f8d887626f43c0747f3a0d5196ef1a29d52c63b6a9ce71e5980db2a0fa8f48c06df75265f7613052e31e9f54771547b508344f6197639602dea1c8dd526fde7f0cd721f68d4e6650612465be1e7c79e920b43d761ba29644123b461492a95e12359b91f73bb6bbee719a5886de41b1a3e5b81eeda6293e7557f17ff84ea1ed4eb52ea28388675ab8d2460f63bc8b5630207993e4dd7ff7cbb0bf61f27c72e8ee9a059809dac76dca416c8d7f35d5e1006f08ec3c9abb00541cb4e5a4cb7d99d257b145361c3fcc318def1390dfac7ac87f7b527341983ae18396681bffc790dbeaa86538e66f9a670948509f4b29378f0cc9dbf2d4f99fa68b045073fb62c9acf996c7c624d4d57f11d6a52c916cd0f772f55a373273a09d01a22a53dbfdeec961fda3745d1fb62b064faad52121c4a5c1dbdd3edd471510991372935ea75e29393ad1a485c175ff0443f6ac9a699bf2b007df0dc27941c8c879a2cfb5b556e2533151eab8c963032c091fa51e5390997fe67865eeebf9b653e4093a5c11975aeeddf8e2a1d3db08ab88a53605f5732f2cdeca8108a3adbfd3a882971108fe21a69c5f0996e900246ad3ffc54203ac26f9a6e9573ab4732eea8ad61bb8e6234eda751838e9e566b13a4aff3a5fd7b753e7d60747b91710c89cf73c21cf926eeace71edd27e2314c7198f4b982c5eeab6d1967b922987770495b7033e01bfccaf0a7963c47c52a7be2cc2acd60467fee6c2387310b43a6ae5e07010a69e407b00e7b0bbf4f15de3fad5845f425a4de525898599347f82bf5ef2499395d43fd2c1fdb8c91c8c9cd172ef92573c801c7cae15352fb42bc40e82a24a13bda69ea994ca38694f51", 0x800, 0x0, &(0x7f00000001c0)=@abs={0x0, 0x0, 0x1}, 0x8)
close(r0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200))
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x48}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
r0 = socket(0x2, 0x2, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000200)="529ac7ecc87315600a6543100f07c95df3287c82518c0461a37e5f30cd2e89292a66bd2d3cf7c41e0dd946a42f4a85799cc9f02756fb514a89579f01f401ffdd16017207f2a3d3c0b4d541dc3ece7e475b8a400c8a312464d18b87911540374731c15f2cb4bbb1563c17541a9658a656d3e478001cf220ceb66aa96f17d5386adaffe54710ac412cbe245425cc359ee5", 0x90}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080), 0xe0)
r0 = socket(0x2, 0x2, 0x0)
socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100))
pipe(&(0x7f0000000000))
select(0x40, &(0x7f0000000300), &(0x7f0000000340)={0x20}, &(0x7f0000000380)={0x1ff}, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000940)=ANY=[@ANYBLOB="88000000000000000000000000000000d10a6bad10eb62b197607e5c035035a49e30f52f178e9527be463fa71f0740b7d1d0f21691e6ddcce44e2254d032ac4c3b280a9f7b7962488aa656e4c44c4a23332624ca76b0ce44b7f0c64a8742a248d74a6a62f538bcf67ed03246d0fff4fcf561938471dc5c7b97e3a149fd0b3bb84800000000000000e0030000000000000000000000000000b6100747113e286117686891699782df7c354f9eb25942557a4dd2a29594ce5082c4ce9e6c3a68495bf447a60e37970fe499ceeb4a294080b5db44dedc9332604004a3c2224af6e2fd2be6c16bd81536bf"], 0x468}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000000))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000540)={0x0, 0x0, &(0x7f00000004c0)=[{&(0x7f0000000140)="31f378c6384e8a6f01", 0x9}], 0x1}, 0x0)
recvmmsg(r1, &(0x7f0000000440)={0x0}, 0x10, 0x802, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000000400)="3432dd3f401dd0b6cdba07a552c26fd6c68ef6eff42ff4ad5c58f070d9e83bac7880c4c54f0cc05666b1cfa666b04b54d456d148a1844b4b52952b9723c6883a35ca8321a578ccb562f69fc5861a75890f1834c2aac9c965ff3125f12342fda98fc4e9e2ad3093c178a852080000000000000069404d2d9a5007ffc10c0a99ca3e5775b409e4e4c070", 0x89}, {&(0x7f0000000100)="d53dfd28b640b928648d15cbcd350000000000000095f5630d83c5b845e55ff0becad72cf761569e01009dbb3c2f0000000000700c44d7c2174f02fe59b89130", 0x40}, {&(0x7f0000000140)='c', 0x1}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket$unix(0x1, 0x5, 0x0)
r2 = socket$unix(0x1, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8020697a, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x2d}, {0x45}, {0x6, 0x0, 0x0, 0xfffffff7}]})
pwrite(r0, &(0x7f00000001c0)="c6248e4e0c11b100010000000000", 0xe, 0x0)
sysctl$net_inet_ah(&(0x7f0000000080), 0x4, &(0x7f00000000c0)="ae72c037a6bbc9", &(0x7f0000000100)=0x7, &(0x7f0000000140)="d0016e28680e1b9b637fb4a28e5825a55d77cb18cfe56b7c6bc3aee6b51fba8227", 0x21)
sysctl$net_inet_ah(&(0x7f0000000500), 0x4, &(0x7f0000000680)="9e089fa338f1a969e01276bce22feeb7eb60970a6b4be31b385e97934022f33242cd8ff74e562400a0d264c926595ddf71f4c326e4ae4de55e8e79ada4eb2441d465a11616a031b19b0440e8979e36b486489e57647a5689f7f75e2bc0d9f730edecec99b0d20b3bd6b5221c8032ecc0ddefbbdabe7dbb72b27d5ccd1735ed18db0277f005d0ec7bc488eaf68cc1a98321ca89ec6f607967e2d0313940576525deacb9a4b5245c6671ed667f4fc090e7d2", &(0x7f0000000540)=0xb1, &(0x7f0000000780)="51cf4f01b4bfb00dd792", 0xa)
sysctl$net_inet_ah(&(0x7f0000000380), 0x4, &(0x7f00000003c0)="8151789b6bd3a60569f0d830bbce678b3bbca19b1ab9969c144b649bfcb1791b586f97f4ba7e648f1fc6b8268b4a06acfcf6ef220dbc012a9bb22d0f4cd717dfe8a54a4ba78744c88a4126ac11fec3a7652453a3f84908209a49fcc7293d2cf1be6688e263141da7e0cec69e542ae6519d7e88e3a7be38f0364626ee3ff0a66fe9cb060e6738b9f416e3bf146c3170043aff106f7cf0a2679776f1602d47d2f666055d23f287c286225c756bd0a65150bc4f6578679862c32d5ccc32", &(0x7f0000000480)=0xbc, &(0x7f00000004c0)="bcc9e03780a905964bc282ead47907c76e6a25af4b6b900371507f5f735381725be4be84bba287e3", 0x28)
r0 = socket(0x11, 0x3, 0x0)
getsockopt(r0, 0x7, 0x1000008, 0x0, 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000980)=ANY=[@ANYBLOB="f5c6932c804741d998e77f769fe90e4e051149f37ba82bcac76b12e3a7b07797f0e08a0a302af7dfed11a09c08494e7c5eb9a9750feca0742bfaaccaf5051531118e3507de10416b3a27af114ca7a90ec9515d1ef284f31a5374692a2bdd107782613c7539171d9411a4d3904b7840c916681a875045b8df59c76b0000be6c3e3296f59236e3d615bc9209a2a899240a717858544ffca13886b14e31c71d9dfad91d2c8fc5c46d396faec19eaec0b24cf531e9b88750ab97fe8f881beffad4370672757e69f1ab31832830e6d3a382b5d1d312f2a3cdc53885f7f9fbf9e8451279241a807de59fa2f86013dc74ce1380fb1b56e44341b16412c9d63edfffce36e09b8ff3c2e71718f894f201100ead9be78aaa5b305fe8d090c46233656638ad969037dbc1cefdf00497e39160138e3d9d2008a6a004b10c2ee3419e800823f1ed5fad955b8b747431a487b7", @ANYRES32=r0])
sysctl$net_inet_ah(&(0x7f0000000180)={0x4, 0x2, 0x33, 0x3}, 0x4, &(0x7f00000001c0)="f553860d961ebecbdb3ef311d509b1065282c1aaed0701807f926c551bce452d219bf4ab4fce97517128e2010996e2ca26e40f6e87f405007b3e591bb6c7934cf29cfddcb1dc7533887848e967849d8a4d3d3724ef04bea8f70fa2bec8313b01bad37be7437c8eed103bebb637636f7e2cebfb5d979a", &(0x7f00000002c0)=0x76, &(0x7f0000000900)="503803f8876be03f614319c9dc9cdfe574b29ad54168662517c953eb3f551ec08752756df3812e085fd90428235a74ce38158713cbdccd89a60e1937598099f4f6e8bec3742e9fca397adb242c54322b8cd13befb42a1150133009eb50e8b9", 0x5f)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebb3557699a", 0x4d, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000040)=[{0x2}, {0x0, 0x3}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
r1 = semget$private(0x0, 0x7, 0x30e)
semop(r1, &(0x7f0000000740)=[{0x0, 0xffff, 0x1000}, {0x4, 0x800, 0x400}, {0x0, 0x8, 0x800}, {0x3, 0x80, 0x1000}, {0x0, 0x29a9, 0x1800}, {0x3, 0x3657, 0x800}, {0x1, 0x5, 0x1800}, {0x4, 0x3f, 0x1000}, {0x0, 0x3, 0xc00}], 0x9)
semctl$SETVAL(r1, 0x0, 0x8, &(0x7f0000000380))
semctl$GETZCNT(r1, 0x1, 0x7, &(0x7f0000000580)=""/224)
sendto$unix(r0, &(0x7f0000000c00)="28d36e268d5837acd630ff647755c5c4df37e2a62c25a8e53e13b65fc7a8d55a8455cc39f8195e67d4316f95cd5a1c4514584a778db89bf5da2f79060ce36195535fb71c43d7c089111c2e7413944292378e0288637aa00103169a7b2f9b47f0e1143247587a593bf7b70abb5a97ee31d6039ec7c589d4e259978ee7698053ae2648f019dce2e6fbdc2ad34ef22aaf3a4093ffaa0c5729e76e91ba3e30433d816bf2e857916fb7dd18b647129562698f88da6a8c9f29a543a9b254c04d84dad71e1f7516f23405daad766220cf3613f6493278eda1de825ad749a6fa93bac95b58ba634171d3f4a4585e8d8aa2926c4611367d476a5f10e8ff801020a0dbfb9c105ef40b311284ae3f914e5cc9c5a099167d239c470188f48cc542c3156d5f553f541296f9e6f163f028127b2d186fe396dc27c404cbb1bc0fa83380a4943ffdfe5612e6c87a8c41a63b72f38750925d402eff390d544cc40334ecb546c1ec49835f1b70fa8eb75b66877b6e012101c3ab4608153c67eb1f1d7907bc12a42c9c4d8da7faca149753", 0x188, 0x402, &(0x7f0000000280)=@file={0x1, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
semop(0x0, &(0x7f0000000240)=[{0x3, 0x4, 0x800}, {0x3, 0x4}, {0x3, 0xfff8, 0x3000}], 0x3)
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r3)
semop(r1, &(0x7f0000000540), 0x0)
socketpair(0x21, 0x0, 0x0, 0x0)
nanosleep(&(0x7f0000000140), &(0x7f0000000180))
r0 = socket(0x2, 0x2, 0x0)
shutdown(r0, 0x1)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x2, 0x0)
poll(&(0x7f0000000240)=[{r1, 0x4}], 0x1, 0x0)
connect$unix(r1, &(0x7f0000000140)=ANY=[@ANYBLOB="82022e"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
writev(0xffffffffffffffff, &(0x7f0000000380)=[{&(0x7f0000000280)="e5eba711d265cd4a69", 0x9}], 0x1)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="efbeadde0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a614815fff271957d3cd40a87052ffef6650d2e29d42adf810d0fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e", &(0x7f0000000680)=0x1b80, 0x0, 0xfffffffffffffe97)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f00000004c0)="90c3fe67eb586898600425f2f573e0d1ac83e58d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d90ca0e27564c42ec8a60444d6f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bbc454327b6a1522c332ea628b8cb672e9e7247818f970e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c46106672cda99d1c3471259d08198e13683ee6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c8296827600d2c4d3054126e21463", 0x181}], 0x1, 0x0)
write(r0, &(0x7f00000001c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
unveil(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000180)='r\x00')
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
close(0xffffffffffffffff)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000080)={0x0, 0x100009, 0x6007, 0xffffffad, "c09b001000004f5249e3040000ff0004533fc8a8"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04", 0x70}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000180)=[{0x3d}, {0x81}, {0x66}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @empty, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @remote={0xac, 0x14, 0x0}, @remote, @rand_addr}}}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x7})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x7, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4}, 0x2, &(0x7f0000000100)="ca448f42075961759acd0ea3c9c9ea959c00c6c6092769a7a3c8b0a33fc07754a5fb3d6c3b8ec862f75bd9f61e57d578e3b30be010a14ecc1eafa7e56563a8b206a236a072a029619bee091d52404d7a4771e3c7d4aca038fb1bdf9eddd407bf33ec39dbcfabda16f04c52c1c2abd1b095ca575654917916e5a581fa8c0677884d352c9d3a06175b3dc5a77a3ea17302b6a85fe20de8db4de652111a0219ccd531a4ebd355860b4e3d", &(0x7f0000000200)=0xa9, 0x0, 0x0)
syz_emit_ethernet(0x6e, &(0x7f0000000000)={@random="e7c638c5cbbc", @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x38, 0x0, 0x0, @loopback={0x4}, @local={0xfe, 0x80, '\x00', 0x0}, {[@dstopts={0x3a, 0x3, '\x00', [@generic={0x81, 0x16, "29fcfa3d0f4403be188985d439dd0985bffae70c2a9c"}]}], @icmpv6=@mld={0x83, 0x0, 0x0, 0x0, 0x0, @mcast2}}}}}})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x3}], 0x1, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
recvmmsg(r0, &(0x7f00000003c0)={0x0}, 0x10, 0x0, &(0x7f0000000440)={0x0, 0x3c2})
setrlimit(0x3, &(0x7f0000000000)={0x100000, 0x100000})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
rename(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='./file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x28}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@empty, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="cf9fbb8ea1ee", "", @remote, "54e2e9767c5f018a37ee7b48af7d655c"}}}})
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x29, 0x67, 0x0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x40)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
unveil(&(0x7f0000000080)='./file0/../file0/file0\x00', &(0x7f00000001c0)='x\x00')
socket$unix(0x1e, 0x0, 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x59}, 0x4, &(0x7f0000000080)="1daf63b25970aebd36020d25e1b10cf3ece6b8be02", &(0x7f00000000c0)=0x15, &(0x7f0000000100)="c2d24c9d34fadd0eae82b8e3168fa740a37829960f8862294ace4e16e103938e10e9fef2d403c0954d8babb49abbd7757e100f97a12aa77f902737ae02664eb0c56d03bb914b2b2a4c4cffe51a6e41cf38d5db8c47370140fb18077b6f8e963e39233b5a0e053e6c86179c589f83b7fa0105135c0b110bd373f3850f556b04ee1ce94eb6e707ac4ea0df9499463f04a489a12e300431c0e42d390839498f20412301ba6400f392533db65e70b870ff4215a0c879ee965532ec5852910377cb30a7d9cf5b1184b39d9d8c7dadb9d96aca5c946e6f5c5c18b50a", 0xd9)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000040)="3c82910d992e", &(0x7f00000000c0)=0x18, &(0x7f0000000080), 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
chdir(&(0x7f0000000000)='./bus\x00')
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000100)='./bus\x00', 0x2, 0x2)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x10000436, 0x47, 0x81000005, 0x6acb9789, "0d000000000000000540bbf3c8b8ac4900"})
writev(r0, &(0x7f0000000400)=[{&(0x7f00000000c0)="42052536f0aa4c43c5daf91b6ac439662ce0376021b71d36501e31c49a23eb4d2465a70a1599e774ebb999393aa15ae4733c0dcbfb092a279acba21f9bc2c18409748a89b67927a9ab9b353ddbe949d80a6bc177f0a9060a99cc9b6b71cb70d5090e14f26631682624a6808ce412bd8524439020978dcaeee51ef70aeaa22c1d41b67b6a061da382d1259e8c8a7dd917668acb96a336208d8d", 0x99}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x87}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000140)=[{0x3}, {0x1c}, {0x6, 0x0, 0x0, 0x8001}]})
pwrite(r0, &(0x7f0000000540)="6ba9a481bbd5bc26d716c3ce78c7", 0xe, 0x0)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = dup2(r0, r0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000300)={0x2, &(0x7f0000000080)=[{}, {0x14}]})
dup2(r2, r0)
writev(r1, &(0x7f0000000040), 0x1b)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
fcntl$setstatus(r0, 0x4, 0x0)
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x807ff, 0x0, "0008000000000000008ddd0000000700"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3f, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, &(0x7f0000000100)="c5ca88b2448271d6", 0x8)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x3ff, 0x0, 0x0, 0x0, 0x100]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r0, &(0x7f0000000280)=[{&(0x7f0000000340)='#!\n', 0x3}], 0x1)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
execve(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000700), 0x0, 0x0)
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80105727, &(0x7f0000000740)={0x0})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000000))
sysctl$net_inet_etherip(&(0x7f0000000180)={0x4, 0x2, 0x6c, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x4, 0x0, 0x0, 0x0, @broadcast, @multicast1=0xe0000600}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = dup2(r0, r0)
pwritev(r1, &(0x7f00000002c0)=[{&(0x7f0000000540)='i8A', 0x3}], 0x1, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
dup2(r2, r1)
execve(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000280)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}, {&(0x7f0000000440)="3c06edc45dae58b053", 0x9}], 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000000))
r0 = socket$inet6(0x18, 0x1, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000000c0)='N', 0x1}], 0x1)
r0 = openat$null(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
pipe(&(0x7f0000000000)={<r1=>0xffffffffffffffff})
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCMBIC(r2, 0x80047308, &(0x7f00000002c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000280), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x2, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{0x0, 0x0, 0x2, 0x0, 0x0, 0x1000}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x8, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004, 0xfffffffffffffffe], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x4, 0x8, 0x102, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x65], [{0x4, 0x10001, 0x8, 0x7}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0x2, 0x1e, 0x2, 0x1000007}, {0x7fff, 0xffffff7c, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0xf9, 0xe59, 0x10000000}], {0x6, 0x2000002, 0x0, 0xfff}, {0x0, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4000000004]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup2(r1, r1)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x97af, 0x0, 0x401, 0x7fffff7f, "09003019e3230600fc4c399f3bd6c57fbbe77c0c"})
write(r0, &(0x7f0000000280)="4de3a767cb3572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cc3392c70fb4d2ea468e980877377f1cfb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70ac323074dd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19a3dff7bde555c03c89296384aa8f8b1e7179729ea0bb9ac633eedb29996c3da18b7af5150085f27cd1a07a7e9b60d858b2f1e5800125da2f78713e82f66341bc07c02b83799f6c872f9fdc87b82c4f4cb92ef1292f1fc9abaf5d17d7804a678fd8601aab32be210c18e48e1287828d903f336f4ea7342c526849e1dfbfba385183f622d1e4e0e1498329fb523d2bde8182cb6bb71d0c7c7cff135c6348649f6e0e61785ce8cde972e9a411d8c951c3f45f83161b98f84c13775ca1", 0x170)
shmget(0x1, 0x2000, 0x7cc, &(0x7f0000ffe000/0x2000)=nil)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000000029, 0x200000033, 0x0, 0x170)
getsockopt(r0, 0x29, 0x33, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x4}, {0x44}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000000)={@local, @random="3a6d1f6d3961", [], {@ipv6={0x86dd, {0x0, 0x6, "3835fc", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000002c0)=[{0xc}, {0x7c}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000180)={<r1=>0xffffffffffffffff})
ktrace(&(0x7f0000000000)='./file0\x00', 0x7, 0x100, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="10000000ffff000001"], 0x9}, 0x0)
sendmsg(r1, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x10}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0x6c)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
mlockall(0x1)
syz_emit_ethernet(0x3e, &(0x7f00000003c0)={@local, @empty, [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @empty, {[@ra={0x94, 0x6}, @timestamp={0x44, 0xc, 0x7, 0x1, 0x0, [{[@broadcast]}]}]}}, @icmp=@echo_reply}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4]}})
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f00000001c0)="f4877b15266974b938726266b25281efe44a46032d70c0956c7ae18240f6a93495c9f7594811e6b81e3efe2164322b305eb4eaaab005b60edaf5196dc9efcaef6d4faac3c9cdcce4034659d220713acdd088320cbef319781b6bde57bea1139ff93492fab00ac4879f75cdb25698e6f60f80168afc8f2453b6d59fad93ae6b2cc7731aebe4668077e85d7c2ea0962de8", 0x90)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x80, 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000080), 0x8000, 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000100), 0x10000, 0x0)
r4 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r4, 0x0, 0x1f, &(0x7f0000000000)="caf5b415", 0x4)
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000140), 0x10, 0x0)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
syz_emit_ethernet(0x66, &(0x7f00000003c0)={@local, @random="bc7e3e9f62a8", [], {@ipv6={0x86dd, {0x0, 0x6, "34c61a", 0x30, 0x0, 0x0, @rand_addr="feffffff00000000000000000100", @mcast2={0xff, 0x13}, {[], @icmpv6=@param_prob={0x4, 0x0, 0x0, 0x0, {0x0, 0x6, "cad7bb", 0x0, 0x0, 0x0, @mcast2, @mcast2}}}}}}})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x0, 0xf4b, 0xfffffffe, 0x1fc80d8c, "24180400000800008000"})
writev(r1, &(0x7f0000001440)=[{&(0x7f00000001c0)="a66c2dcc193a8f951931f490fbce15d50243dc6a995fad241fcb780b903b75a020852e25924060a0a88c6457dce6a761ff1df5eddb61bcd5bb5944d6bf143612526ff110e926a7ee2cb24d6d211a91f49a36ae05e6432d31aa478f7f5ccab36b6c78f6f20908", 0x66}], 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000180)="f7c5", 0x2)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x20000003, "7e00000000000000000000000000000200006ba3"})
ioctl$FIONREAD(r1, 0x4004667f, &(0x7f0000000280))
syz_emit_ethernet(0x56, &(0x7f0000000040)=ANY=[@ANYBLOB="aaaaaaaaaaaab636f6a7d56686dd607de95600200000fe8000000000000000000000000000aafe8000000000000000006011004000aa2b"])
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010000ded559cb00"})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = socket(0x18, 0x2, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
dup2(r1, r0)
writev(r0, &(0x7f0000000240)=[{&(0x7f00000002c0)="eb63ad8ef082436b2c446d9535e3d38ce501008082b729a47b93e2e1b0be39607b2d6a33cdcd5cd3e9f59f85dddc33987a2a13c15c04c82b87e0b14cdfb2722ef23738f19bd65da545c570e819fd8f1218469cac0324605613505e9f6894a8c0cb6ca69e198b00fc878f34d5df52d571032f1f4ae47f99cc85915d1029916622e3751623e043e3f04cfd12b60cc11a11", 0x90}, {&(0x7f0000000040)="12b8bbe6cff2adb66003052f6586ebe3ee40d2d213f592a71cc16c8277487cc4d1df62d5777ea7e8608c76a4ea3e0c45a69acf3e0cb0213303f509ce37561f247cdd98a0e5fe2ae574437ce8022a8d5149380b6c3e39480c914e32bfb93f86cda8c0cc", 0x63}, {&(0x7f0000000380)="b100691c7ce79731efb263c35897b09207b7be586af75d2e40b7b30e7e50ab70ba4b9e77e2bfd9f5ec32b094670599470010000000000000551f52b61205a088ee0f0765019a796c4941447ce62fd948d23e01a2c7aaf01029ecb4a7d5dfaaa53c5cb552935d20d9a113a26b0efbd7039501213f63355dd56bd538efd4229156bb5484fee1d5d8b4c10d84fcf17d5e479fcd83c02981cf98712bf6ae46d2b9a08daabb9fb69ac1f6e36ec413be309c000000000000000000000000bbaf0d4da3e6536e1ab5222270cd457af82da702c3796be0cd6d3dcfff7071db68", 0xdc}, {&(0x7f0000000480)="4fdedd0a22537467a0db20a3498134a7e8dd74c1781590acbcb7e6eaa2cceec00966e2125f097def3086a1572c669e7aff1139204f3eb28c7b8b2b9ad42755623e7248e7d291d1d9fb", 0x49}, {&(0x7f0000000500)="73b98f7f3d4ef282d0fff0d14d02582e63ab6424a48e361a63981c4d6a1225b0000893ccc0afd83e3d0a47c31247eedfeb6df13706e4f16586b05505b2c643bfebf16b5d0225a7d5b71dc9663a10f2187271b553ce75cca96131d412f8216ffa2273c3b17784b74e2b39be48e69269c17c74640e2f9379d2cb79953bca78e4a4f53eaffdbefbad86c8e68b5322314b207812defd8862d92c61ebbf6c852b0feab73db26139db8111c1efa5704557c45e091240208fd46328e7b9c9e159a3322746f637f189fde42d4f8d9674fcae61d451", 0xd1}, {&(0x7f0000000140)="2dae27de7f66aebcd2e72d1ef124ac648668b11e9c696c97fa60ee9394c4dc0923d2d1d9a7dbf281bb2f4af92faee84262ea17a156cdbd910fe850c742d6361c37387cced92a037f94ad14050660d982d3b616ee0d2d73c9ffff54d9c41f824d1facca780b161c3bb1f418f2f945880eb81d2055fa00eda69f4958c6dd8c6e2914ddd905dfa0773dcd060b989bd190ecd2d29835230b8dcd7ecd1b0ccdca10336e68a479ce2fb42c0a3bd21ba9a76a0000000000000000020000000000", 0xbd}, {&(0x7f0000002c00)="8757247a66082e16b36f72034a7d237190339f9d52f40f33ec6641cef47bca73456e61c5a6b84d02dc3ba2d8ed288bbe4925566901fadf697b12d9c18804e871266661aad2592b2c37a77819bd138cb223c13a222b5aa79ad13cc39a6fd6bc67de547227154559cd32ca9cbbc934b583e90e91024ed61d598eab014e7ca487abf95b4ac9edf70ca53e3a76409375b146647877230fc6aa0284d81ec3233cf15aa701c6f02d0e30264a684fb7098024c4bfbcd1777b35b0020cd0b9719c386c639b86affab753ff22d0de0d5c787f675f34e7417225e001ed253fc412c611c375ea9a7995db95029dad24f80e7bfe5452988660ee2be91a9f9f0fd8c819f4e5c37e540a664bda32bb4215f5a41c1b8661941fc84f70144ffecf54fc1dbeea34be3287160e6166b340d0fa3e", 0x12b}], 0x7)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0xfffffffd, 0xfffffffffffffff8, 0xffffffff, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000fc0)="1596693388d4e8bb6df16e29f95a81fb895de567ab94e94f340ccf9b5295e0b5957b71442ecf83be94957b0862d96edb05ace734c4e80cbef22f5d8ad75885c09cc69c907ee5dc2f439aaf0f", 0x4c}, {&(0x7f00000014c0)="e503c61d80583de86ea2d148cca602eb0657023464e233112274b781731ade6ce55d6f7afec296ded73d238abef715fafd4878550b5ae5e0322e293ae4e5cd519280829bd61933159593bc331e33a52b5adadcf490f78b92ad6bbf96cf058bdf43ff7064a7585ffb497f84be4ee9bb8e49007e0dc1c79295cfd07e5a3c4505933bb40cf0456686eee9bb3309f16aaa5fdc16357f7633d6c343fc4234dbe18e1666caedac66c76d37692d1e8ce408f0e1e39e73797387106a9259ec87d601b009746350ca5a9ca0274690b50560de53220fc2815a78f45d277d5002d3f6d428a60fee7a15de1de6861685d366b18d5017d9d3e272705ac7b6d9b2a2035921ad860eedc636c7d1341a6c9c9cb2deb630b4dee1d54d3628914fcc0dc5bb93a1457844e5dd8808e4aeede385f70e080af31871a7ab1d9c9a78b1ed288ca2dcabd5fed2826f54265c8f75b487b3766f8fbc0527319298927930707dbba165bfa3c5d3432abd55fb9cdb41280eae16c186594d85a55842264a9b5421731538d9b392eec31ae109fd9813bc067086a8db7fb046ddd04daffb3ce94a4843621a33d5d90f99178b375e2848802e1b99b3b974d7c53192e469daa156e5667fef2ec5653c4c896a9280617a7de3e5c7f47c35342a81f72e4f9c42140017300b5bdc624f4e7c3be8c91e0fb87590d6e9ab1d547a8d41319e40837678d23715e0e5b0a4da9121eae6dcd6792574aef12d49ece93d6fc31d77214f0bb08da10bdbc6950c60b1ba11c44980e21f78c55c9ca817507356dbf3b62a0b588ba243835e8d59e877e5f8c459dc6f06a9596be14ab364fa616b71c56f12ed3a028cd3106396e2e2ef55a208ee4f12f1cf619f134e046d0cc28fcb2a465e22372eadfdb5e08f22309ff23eaaf5205705bc29729244fed42fefae8f363c70ed64e37845eb2faa6b8da8d143c75f6b23c92f527edd0576814ed90033ea760b1c2eeabaf5ebedb269b845541ac4a1f68bc13a9c9f65eb3d3e1781f4864c08775448f30f4cb4632131c23ac76636031f9503c902ead364154d460caf2b9371673f52e57bb98c181e86ffe291f30c1e4ba64be4b03b65336dcab894bc75bf5d5f505d8a8f0708ddb51fee9a47e3276e427becabd7d331c1e126746082c07c921ec1743f46f5165f6f9eec781a66ca6dcb08283f12b8c856c53fe7388ebecca7cb4dc9cd60321d834e0c85a8e8795f8ba6dabf6f1df96fad15749813a33483de3b853b821f2dacd90a970e513ad3b108cf6706e61c9182adef982c24b764e65513bcf17e6e169b1fe7da7f05ed8112a691964af4582ccc9ea61afa4aadf97459f8fe9df9a0d03a7e6b362514ac445da8cc06b8092be3049a0473ef1192dabf1ce41cd65d8fbe137b2317f2aa6fb8a12e1275ec5baf15bd3a0cfcb17280a53683b21bd2ec134b2d357f49231f1c49421ea8c6679a66a9d526cbd8fbd6a7c285f0882648a2ca16bb5561322ff5e046df51dec0cada6d75cefd5aea53191c3e12760a8490ecc308df8d9d4475c3ddbda945b12e7aeacf1cdfc27dc41979ba4c050bf04281fa762799ae06691a785979a5a97d45733e7c51606670384af46c06f180dab18a4c1028d5354222d54aceb5f8cd5f1da4b3487cc954f05a0b652d2d8adc1327f2a92aa0ef1006d70f121d7cbdc2c519654ae950604e83e23ceb77b2f02493cb99d7c7698f6281890950ecb6e7c46ce9cdff99b31e3eec77f8b4e0d78abcca03356606e0c394bc3513e24f3dd0e073e9d9ffcf210311505306a03aa6f4111b765865a19b4567a59aa582b54804a2a77b4a246af051a4b7cd22245206bf4d3ad6b022905197888f841e50f16b647c7c4a60e5412f01f99379b3247b2c4e57a627b4da2722d9f02e1c251b805242320c0e4e92d6966cf5dbe35124ee702e10850314f5a45c35fd497ca7ab6dc76a50b13c263136db5b0678bffdd7ed18d605f9dacf0346842f16865fd292d8937433fcc65c612042eab65bb783570b4b48178fd35deba95fdc39f3ddd325cbf79b56e53b660561f3f6b44fed109ed1e923890f8fe7c57d06d4ae5b098b2464d63e97fe533ab6d03616a5d7199cad8470952349a76ced2d458c28d6fac9338791df5a23d1ea52f7898db22ce959ca463592e39c656748156b41eaebcf58f4224c4c85a8656ab1916d3aeb82a0e4537b2aa1fda04575eaf4a56fbddadce2ce68b0ae8c53f3e0db0594ec9bfc7f95b02bb188489853a7efa354ab2510852422dc1d5a60cd928364f0e45ce5ecdf8fa6c0b6d6f44ddefa61de03cceb3eff026d6d308fd7d7ed9e7a9e7a8201d545524e6df3b8f376fadef047f130bd39a8b4de1dcd6ab5c6c4b53d7815828a01da78eed1eb097fb88cb21f6affd087fbd17a6014b491fa4609be2d07882259fe0d578e71d0028b986636dd2dfaa2fd1e6a6669177692a1ce82ce6317dd6d45e7a6fef72b56571848899f4bdfa66b59c8ac646a637c3df441ee5bb46af79a7d83e160f760126ef381167a14f2e79994893cfb774704f857fc02af519876bac4cc28bbcfa71271e051413d311a21d0bb748bf0d00a63ee0ee66e440e9e13e35b35d390c2a6f36d7890895ae7bcc828a9d10f61e4edfb91fc3272527711a665fb7817265ec9d02f76d625f824ff364b0280916144eec5597d70980265522fa783b9602355732d7dcaae870f2ecc36021285f39543bad16f343dd7df202049eac437eccd300b6c2510dd7a9fdc5f5105b0a726f6f841131d158d071f89b6d6f5ab6b4cf668d143c516fd5afe638a3d856bd5a5b8c455dc21b4c8be976cb988011bbc1c415382ee7b8c7c9d8515e847f3f4a4b939d68a9f8a54dea5ebbf8ec95cc7104b046fb6ac94ccc89ed9041446318d5b57072ceefb68532fe81bdb08e89ce9829adacd606a8d08e7e3c82c576c2516cd574c20a6e5a970338cf80d6730c7a8d5113974419f8638b6525c72542a3cb956fe061854854d91497adb52704a7b55922ad9a81a80de2d6199ba68e2539151cad00b48aa1555d7dc40d68a923a21cea364e5dd6f90c3fac3e582f70ac2ec825f9c2f8c0622bad1bd8e6314c3b9d2fe911aa4c1f4431a0788e4c78274cb6efb4a5b0fa94d65ccf2a360de8fc0bc37f28f36ccb7af17086bf48769fd8d82c233ab6eba6498601a9c039bd59ddd0c3e5bb028f425b70dd098aea7afdf5de9463ac370fa5f21eda4415031dafe2a6ad1f4ce1da80bc2931f43e091c555bf5e5058988aab182447a6dad586cce18c369f2f9ab2e579fdf18700ba65c548d6aae5bb53e803f143f30c5b8bb547df0ac5b10cbdcc4f7776fdfdf2f9d2d7d7b3d38a8f54397c9cf17e93c7261107c669eea7bf9bb7f5672ac379cfa7c38fcd3909df05918ea221ad12d8db3010ea52de9d175e7a85a0b35b1bf5a6191972f0ab63f5965a9435ef405e9ea96807f4200aed1e2155ef5ed580749b4a5a187520ddec62a30df3ff92bc863f4c79da5cd929dac6e71271b9d8797cdca3179b85dec50cbfb784f17bbb7c09957803f6ecbc2d34057c94cfc32e92162bc82413caa2d3e01fc44f466baff726420d708dc7cf2948ae7e2791a006cb46fd81bcf00d3a50301b781bc416e91edfa9ac4a001a7b2bb40abba48ef37c5ce95da335d3ea561f12a174d553773b561ea550bf51d17ed1d8c5a1d7953c86e0be8088f911ca3026feb956c5f6a2d9170051dc97e49265a0109a9adb59a93248864b5b6600161b0c9a7b0217a2e3e4639316a8b7c39f97b6", 0xa67}], 0x2)
writev(r0, &(0x7f0000000900)=[{&(0x7f00000007c0)="56605a7506d2edd392888eb8b666283e339b4f73e3e91d46f9ae977ffa8a7d5c8aa852fa3c35f8b14baa3ec8688dfe3628e470b586f1d1b9ccab5174c1ecea7ad0b4cd9b21f731a86765a90904757f65dcdb8f", 0x53}, {&(0x7f0000000340)="b59e317a1bad5f08c34ad75d8bb0ff178d8bdea126ebf178d51421962acfabcb5e2ce6e181ca93383dcb6725e490b5e620760c32707ee592", 0x38}, {&(0x7f0000000840)="253283d3001ba56b086acb7eabe5ed5905eea632bb6f9c7d6ed0b3babd5c3dc531f230e1d381682785c2bfa238b6cdfc6c698ca5585c1727612a78d652f66bd9b8119265467335d5a082bfd7d1e8911adb5f8da0a35afa1851207940f8bf2108eaf17391fc66b74af1cb40e13ea8672e70d9a994e28f63c7112e56c4bcdca22c37d342a2ba85a6c5be39ae639278a72a2f63ae7e8c9c74aeffaaba9eb0e22d9439ae341323a8f0926025e5ab78ce7967001db426d5874b", 0xb7}], 0x3)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000380)="668ebea2a6cd9a14da47d9ef08370000000000027b3d418388532c830edadbbafb5b5a7f35093e374ad97443a184c14d3bf391b38db5e62db0ce686e19bf2747873599a64f93da5fe7fa6f58483c01ca27fdfb63e96b767983fc2ef8d54bff2d8f4ccec5771c3c19a6ebec93f575ac77381fefc6ad4ea5a0ea", 0x79}, {&(0x7f0000000080)="b4b5d10a8b25157dcf64c002c70f02f42e10a46c2eb46f96e27c", 0x1a}], 0x2)
r0 = kqueue()
r1 = syz_open_pts()
syz_open_pts()
syz_open_pts()
kevent(r0, &(0x7f0000000000)=[{{r1}, 0xffffffffffffffff, 0xd9}], 0x6, 0x0, 0x0, 0x0)
close(r1)
setitimer(0x0, &(0x7f0000000000)={{}, {0x0, 0xfd6}}, 0x0)
getitimer(0x0, 0xfffffffffffffffe)
sysctl$vm_swapencrypt(0x0, 0x0, &(0x7f0000000280)="c9947b2e770394db012d665c237613b8fa65f823f1e7be5b76b2c6ac1562983d49f6d5034404425ebc525fa77c9489cc834652ac5c209ee62469f4a2b04e20849caa5ead93baf8c0069fc72da88b87f45171e859ad370efe02c9bd305db32859fb5f32f2371245d77e49a01408df74ef", 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000001c0)=[{}, {{}, 0x0, 0x0, 0x0, 0x0, 0x80000000003}], 0x0, 0x0)
kevent(r0, &(0x7f0000000140), 0x100, &(0x7f0000000300), 0x1ae, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x26f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0xa, 0xfefffffffffedfff], [0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x2, 0xffffffffffffffff], [0x10000, 0xfffffffffffffffe, 0x0, 0x0, 0x0, 0x4], [0x0, 0x0, 0x2, 0xffffffffffffffff, 0x58e], [{0x0, 0x0, 0x800, 0x10000000}, {}, {}, {0x0, 0x0, 0x5c}], {}, {0x0, 0x5, 0x2, 0x40000000000000}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{}, {0x20}, {0x6, 0x0, 0x0, 0xcc}]})
pwrite(r0, &(0x7f0000000200)="b269f66b39b9de6923518e4027fe", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000380)={0x2, &(0x7f0000000000)=[{}, {0x3}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f0000000100)=0xfe30, &(0x7f0000000040)="d6efcc66cc62c54742823fee3eb5ecb3e535e529c2436f", 0xffffffffffffffca)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x41, 0x0)
open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
open$dir(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
write(r0, 0x0, 0xffec)
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x2}, 0x4, &(0x7f0000000c00), 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
listen(r0, 0x0)
fcntl$dupfd(r0, 0xa, 0xffffffffffffff9c)
r1 = socket(0x1, 0x1, 0x0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x800000018, 0x3, 0x102)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x66, &(0x7f0000000000), 0x0)
nanosleep(&(0x7f0000000000), 0xfffffffffffffffe)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0x0, 0x0)
select(0x4, &(0x7f0000000000)={0x5}, 0x0, &(0x7f0000000140)={0x9}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc008441d, &(0x7f0000000240)=0xc6)
setitimer(0x0, &(0x7f0000000040)={{0x0, 0x2}, {0xffffffff}}, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x40, &(0x7f0000000100), 0x4)
sendto$unix(r0, &(0x7f0000000140)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae27caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebb4257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd007f720fd3873babfbb770a2f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100000000000000100000", 0xb1, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x8f})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0xe970, 0x0, "7e00000000800000000000000000000200006ba3"})
writev(r0, &(0x7f0000000600)=[{&(0x7f00000002c0)="1854410995479d1df284fb80510bfaa1cf44d706e1ce3eb8f2fc3b3ea16923550e5f2589853a5feeeea53b167e033adf0ec86da315b32338c0a6b4229e0594f132c94ea710a300ecc099b0e362d57df8f8", 0x51}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0x0, 0x0, 0x7, 0xfffffffe, "000000000000000001000000000000f843d07e38"})
dup2(r2, r0)
select(0x40, &(0x7f0000000040)={0xff}, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)=[{}, {}, {}, {}, {}, {{}, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
write(r0, 0x0, 0x0)
writev(r0, &(0x7f0000000400)=[{0x0}], 0x1)
execve(0x0, 0x0, 0x0)
readv(r1, &(0x7f0000000140)=[{&(0x7f0000000080)=""/12, 0xc}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000080)={0x4, &(0x7f0000000140)=[{0x10001, 0x0, 0x0, 0x3}, {0x4}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000180)=ANY=[])
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
ioctl$WSKBDIO_SETKEYREPEAT(r0, 0x800c5707, &(0x7f0000000000))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1e84415, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x35}, {0x2}, {0x6, 0x0, 0x0, 0xb1}]})
pwrite(r0, &(0x7f00000003c0)="7ece617ee9b1d7ddf6cbcb060651", 0xe, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205602, &(0x7f0000000000)={0x3, 0x0, 0x0, 0x0, 0x0})
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r0}, 0xffffffffffffffff, 0xffffffffffffffff}], 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
dup2(r1, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x3}, {0x1d}, {0x6, 0x0, 0x0, 0x1ff}]})
pwrite(r0, &(0x7f0000000040)="dd9681090000000000000025b37e", 0xe, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x0, 0x2011, r0, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x0, 0x2011, r1, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x4000000000000}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0))
ioctl$FIOASYNC(r1, 0xc1084425, &(0x7f0000000240))
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000040)=0x210)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000640)=[{&(0x7f0000000440)="9900321a47fc5f744d", 0x9}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000000))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xc0084461, &(0x7f0000000240))
syz_emit_ethernet(0xe, &(0x7f0000000000)={@local, @remote, [], {@generic={0x8847}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000000c0)=[{0x2c}, {0x1c}, {0x6, 0x0, 0x0, 0xffff}]})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
preadv(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000040), 0x1b)
open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f00000002c0)="eaa2", 0x2)
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x6007, 0xffffffad, "1500000000000400"})
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000100)=""/180, 0xb4}], 0x1)
writev(r0, &(0x7f0000001700)=[{&(0x7f0000000440)="9a", 0x1}], 0x1)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x31, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8003, 0xe0]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x43}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x65, 0x0, 0x0)
clock_getres(0x2c442bccf316f004, 0x0)
mknod(&(0x7f0000000040)='./file0\x00', 0x6000, 0x800)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
close(r0)
ktrace(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0, 0x0)
open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000000c0)=[{{}, 0xffffffffffffbffe, 0x0, 0x0, 0x0, 0xf036}], 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x8, 0x5)
select(0x40, &(0x7f0000000140), &(0x7f0000000180), 0x0, &(0x7f0000000200))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x214]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206916, &(0x7f0000000300))
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff})
ioctl$TIOCSPGRP(r0, 0x40047477, &(0x7f0000000040))
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x0, 0xffffffffffffffff, 0x700)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x7c}, {0x15}, {0x6, 0x0, 0x0, 0x369}]})
write(r0, &(0x7f0000001700)="fb2fe7e986d81d7acfccfbcbd4a2", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x3, &(0x7f00000000c0)=[{0x15}, {0x25}, {0x6, 0x0, 0x0, 0xaf6}]})
pwrite(r0, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000048c0)={0x0, 0x0, &(0x7f0000004700)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x21, &(0x7f0000000040)="b1f5d915", 0x4)
r2 = dup2(r1, r0)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/28, 0x1c}, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x800, &(0x7f00000001c0)=0x80010000, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="de811fb86c59abc2"], 0x10)
r3 = socket(0x2, 0x8002, 0x0)
setsockopt$sock_int(0xffffffffffffff9c, 0xffff, 0x800, &(0x7f0000000100)=0x3f, 0x4)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
r4 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r4, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r4, 0x0)
write(r3, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file3\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a0", 0x1cb}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
rmdir(&(0x7f0000000080)='./file1\x00')
mkdir(&(0x7f00000001c0)='./file0\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000200)=[{0x60}, {0x54}, {0x4000006, 0x0, 0x0, 0x10000401}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt(r0, 0x0, 0xb, &(0x7f0000000200), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x60}, {0x6c}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f0000000240)="16d44ffbc1cf5ba084b9e56efda5", 0xe, 0x0)
r0 = socket(0x18, 0x8001, 0x0)
close(r0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
connect$unix(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="9502"], 0x10)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000080)="0c52acb468f0b91b", 0x8}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000000c0)=[{0x4c}, {0x81}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000080)="e81f0000000000000014b6317e37", 0xe, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x9}, 0x2, &(0x7f00000000c0)="0fbf86e7", &(0x7f0000000180)=0x4, &(0x7f00000001c0)="4e2c548c", 0x4)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
fcntl$lock(r0, 0x6, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000080), 0x0)
clock_getres(0x4, 0xfffffffffffffffe)
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x69, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x4, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000140)=[{0x2c}, {0x6c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f00000001c0)={@broadcast, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @remote, "f4eb686fd6c20b379cff079f164f580b"}}}})
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r1=>0x0}, &(0x7f0000000100)=0x1)
setuid(r1)
r2 = socket(0x2, 0x2, 0x0)
r3 = socket(0x2, 0x2, 0x0)
setsockopt(r3, 0x0, 0x13, &(0x7f0000000080)="02000000", 0x4)
dup2(r3, r2)
connect$unix(r2, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
munmap(&(0x7f0000ffb000/0x3000)=nil, 0xffffffffffffffff)
select(0x190, 0x0, 0x0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000}, &(0x7f00000003c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000180)=[{0x64}, {0x45}, {0x6, 0x0, 0x0, 0x4002a}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
ioctl$TIOCSTAT(0xffffffffffffff9c, 0x20007465, &(0x7f0000000040))
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
semop(0x0, &(0x7f0000001280)=[{0x0, 0x3ff, 0xc00}, {0x1, 0x4, 0x800}, {0x4, 0xd3b}, {0x0, 0x727e, 0x800}, {}], 0x5)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000012c0)=0xd2b)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x5)
r2 = socket(0x18, 0x1, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, r2)
r3 = socket$inet(0x2, 0x2, 0x0)
r4 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r4, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r4, 0x0, 0xa, &(0x7f0000000080), 0x0)
setsockopt(r3, 0x800, 0x8, &(0x7f0000000140)="bf093350c306c03d5f196b75b9945ecda3f0d8bd7946f8b83d84c07b92d67677bff38296aa05a2875b97e24fff057991a04ee1d4c69ce1f939d9351370189931aaae6fa0eaab32b8970faf6a0100804931de3ec99824c30b7f6a100b852c715b4e19f075f0876ffc0f9640256f25b534120742b3150978ebfc1faa32fafdcbc146916d7b049b62d1821db02ca9b9d4bda40e2f3e8d9efe1a80ce5953efe357c523537fdfce50adc04e902f71b2bd1ad1597c5411dbd1dc19ce6b365552233d1c82ab75cc7965b462df26", 0xca)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000001300)=0x3)
setsockopt(r2, 0x29, 0x1b, &(0x7f00000000c0), 0x4)
r5 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
ioctl$FIOASYNC(r5, 0x8004667d, &(0x7f0000000100)=0x9)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)="77410d09b965e7e8fc51618c6a81d10cd624929771a2daa7ad9ddd64ae83b7a03588fe67f2ef5eab0674bcd00ed0c6b03da350ee46e1f016d67e890f622db0e86b7bd7643a646c132406887b45a2313c8f5cf650991573068f637c8244e4a04abe40fe1819c0f5c929e6fb574502003a5ed5dd6e46dfecf1896c11aa093a90a234243f90c97973ac2b331725f392bf320ae632aa5d3632ae3ed3ff8abab2026b6097c477df1153f264bb96e9ce830595eeda76766c0b5f4841c5c1e8403f70823829c0039a849ba553ef06b43d6b36ef8d0e17ce63d25e0fd9ad37291dd902e1e11ffe680507583915024e14d50081ad036986d4defeb63f2fb3e45d6990a3e60e5310e357923b8be0011eee663ffca16f3ede3a4cfca7aac0e3a84095bf13013065564fc359862e8ff8b532321f93bda6d65235c85069b326e15a6015774c8e932f16217644f1ea940845a55c3e52958c55d402474f7a6d1d1c1d04d6608401a09d82644d75407a040039b5e94ce3194af70e04e950fc03b5ecab22e3223713a0bcda0d0c86528d3c68c025362fb2b2d32edd176f24e24e2e314810177d6c174706b49efed26bbfd64a567fc1987583b0df51e913a1ae007bd101ca93bed06e652733bec0c1c35dbfc5ef1a30592b16276833b4ec02f23151d49aa95b5f2733e47bad17c83ff914b883be2374134c97362f6653bb15746aee7cdb49923d32e932c68bb3b52f892fd5e72297118bdea862b0e7e115a20692f4259b94b4db469785ed71d289a4a515f7d1f690bf41cb81290b3dd02c599f5b24e634a065925c4244d132cd97a9c1bb437df296796813541e65d879b4fd4d4fc3d734c6b50b4bc9d79a5e8d682f3776bf7ea88785a86d5b263234f306271ced18e265e839a1ed19a2495f00f447fc027871bcb9f2aad274b6f0ab21eefc9d280cc5782353e7181e2dab92177cdb370654b7ad8db349bafb743e574eb372d9fb2252cc9b6462e233c4aff76ba9096ad7e6762947cc94f92e4342967a9dadb24ccad170f955c1fb5f3a67f8fc2d60ae1e6469f98d3c1b91311a17250b4efaed38e7b80e78d5777ead2ffba645640e08d8268566a9cc98d321c4a93cf7bf1477cbf497d063d6820d17d122b48265b1475982fadad9ffebcbd3a69cbbd4a5e8b47ec32efa136ac223ada1277443d78492b45e72fa09a1e6405faccaa510cc4ee9f16e28da5b271d4e21a72e482f0bf0882e51e1c69b05b1adbb08b5d4ab890fd9f85a67bf3e4699aa9dc07c81f74c8c56ace6cd149708cc5f93eff47e27515dfa22e4d2d2d1313e3865a3cc122165e0e63231516ad562a57da47301aeda67f748552372bc84ee374b5a99f6a3a4c3f4edddfb5c07b6e7477b2d2bd2b4e0bca60d1fc491980c6904a79aeb1e4fa201f03aeeb58af6a7290773cf8e187e672010aedd4fac79110ba932d938cfb523ddb9bac3823e38cf104afb2f8c81e4bae6120e83aa284ff9689d063849f768fcb982723691e4b201bc0298358004d7a18458e2982e1ca33638261fb04e54b384139ffd6c0281526f26b75a5add077a2f584cef813139bc8d47d4d07325387e5a3f567dbfd991bfece606c5b0d28f105ed112837c53ec757a774c4932a979885d4f2f24d0c950f87f7f121bd3a9fd4dae51ebe87b921a6e792a640fa36421c216d113193ea0d6694b39449357cb7c45c71eb32fa90965171fe1ae6e543ed28f16099e6e07d4ccf9422c5b92718ccee9aacbe0e205bf60444057d5601e768ad3a928b8a98591804b8bb73c785d8b96403e9f9f1846b42eae498e4126a17a202ffa4358160394fc3a2c5ac6a5a718901d6995b7b4dc164ed99c31f37ebfde83ed8ec351eaf5fad36f1c8e62cb497bdf7205aa6ccf3c0802dd07c2b762d39c780105013377460127a9e051c95839c7735f017f01d7e52510f928e7a4648955844ddf3e6024150697e389251bf5941c62303d6fb7217157a9f5966ef31d97d38712760f196c05762ec57466431ff0cf654d2e4504ade7ffc3646241a3bfbcfa1d412532e3a047ed8d2410606ac4bc6e09328e65bb0488667a31916987663b8613ab51b7a7e1818bd0011dd18e6a7609127d1925e396c86f930a40dbd6f952ee0ec53637228cc8d3d4f84c0db6685013f5b2250622db8d5863527ccaf0a11c2a9db8a51e85d149971f0310ef0be699a229fb96f18bae3a7551812863a5203e9dbb34e05e8295b0391e05c1e4ad20aa929364a934d071af8693e0d92035fdb768cb8273f6e45b84c7ffa9060f3686f0eb1be02115b010c6f37b38b58984a391087d973dbb39c55a235de00a72e2d4f00b393e33f14ce4ce3d56914c262b543e8c845fe9c0e1eeb35509aab19807299e9caa2897cef2b29a211a64fd9b85f36e4483e961cd4270b06951328a199cfd9cc4754d99712164d280aa1a6ce430ddeaf5ec6472e4e27bdd1a256718972f3739cb96727d643a23346d004a78691bc113f09170cd484ab3601967128a52f6aaa2503bbc1721209d1fe3414e22b11d59cd72d50b843820607b64c1af8cc15e476c987064d7584e2fdbafaf3fe4be5e5f246c323f0535609382066f93ad84f616ad0c866600c1408754117d5f5c9645cc9634663c8c62dd550330d21f97159993884ea80921120e2b0fb27549374835d701111b1623792cb4a5c32ea7937b2f2bbb2125e72df47016dba8fdfa12fdf1a2b274f8c80185dc41dfa2ad7320aff175cf1efe8a09568f1ef6b533106c69fbdbc5169daf2aca4539b4c2cbfbb5c7f4bfbbe1a4856962a52796c062791a2f7e25522011548bfa95366b6a63334087d9c4fa4c252ad4290653d2870fbbd60bb5c60bc1dd0c8297d0409a633275b1bb890b91793dc0b5d1f632b2247dc9542d12b3a5bf9160effbc2678bac0852ae8d76dac7d7a56471403da8c7f6965219142899ce8044c21c7e3acfbbabb0cd107328d46937ea4b65e8cc28021610af6e847d4805bcd2bc9d224d085a2909ca6d13ff95df8be78b43af3d6bad66824ee988b6b5be25064d28a7e358c32eb68538432ecf7a2a0cb208af286629f645560b389c6dc877550645b089204ff9adbedebf1d9b3546d8a1fdd2f68bc87282869355aca9dd9aa07180edb114e5f55e391fa3f0ebc7e0f7bec5dadb0c2c2ffb81843a6b85c73979e240a227ff4bf3a06decfc036e318c5f4446d7df6474c1fbaf596cdb18507fac0febb617baa9dc31c054de355362b5e974db74496a61b5c7301136aff75014639e8df842a69b045708e3f02aaf771a0c2bd1105f94e735c77436de6b60cfb43b174953bb1bc2ec5daeea484b616465f3c22656a26f3cbbf529362c353cc4fba7b894b09c2c4169299a02652e1f994079e9debdbf6dec9c772c7c8a6dcc08b5976cc36310b8d06f160f1047d0234e2f8adda676d7be07726d9a4496a3523bac6a11e86842aa6c9989f2da014f497a4fdd56c78537de31cd590a54248bb3d09b826504da28f9bb9b911a9b2de934f785a3c2848f19c0c75a3e9681547af1fde3f71b38f18e80d05ad68efcb46526a6ed20a6d6b0b803a848eeb26add9fb6e3295461ca2319be0721341b5e4be1ab364f09b5657ad40e6fdcec9e2a50b9fa6aff65143f21adfd2d2db07df705f56bd9f59acc01331fd866fe7d713861167dd0886f98256747cbfe4b6908fc10a365adf4df104150a78ea2d2a834ba9f0e572241717559f6963cd26f31a7e5bf38c6b502bb5d8a8ae24e51bf337ec81418d3df2aa168e4a0244b1af4ee25baf26435a3ad1a08ac010cd7af66c22f2339e19ea492f1a65be145926ab83f7e0e6d6b0c8a25f8d0fab49044c17c48f877574954064c0c7b79158e92073b98c61e54ba6a3fb726c6154146d6603eea7ba352e2a75afdd3cc6968e7dcef7de077d9b024f41c0996ffca6057b87715b1e2c798212e9efcf4655660d108fd5606cd728879d4fdbccb5133c0afe23189a8c8710bf5bf45d7c18aa3168fb903ce160c411a3b80db62be2bef1a2eda2c28ae35b7638bbda452d7da6e28336acc076b6a6d3ccf470a2fcdb56f05ccbb8cfafac274d828f4e55903cac6590b516ad492fd12eff3710d0cb64deaa261f8f2999f43dd95b689db18a14955cc317d53a8c7166ef3c390eeec55737844350b80e1682504255333e9dfc5635b681ccfc40c14ac786f9a4503975417fdfd50824e60e276aa43a9300732de8d8096e1d2aeea174b5ea677e3957f36eb3839827a2f7f6714508b20c220d623ea2925fa1645774dfac3737530d6e4da7a424127ef4e5d18250d7d035fa1871b7e670d0bc45101088be317e29b7e7c53bceb8e3f02d556615bf1fe3c4a6f8021cf2c5bba6b7dc3a443f55b40ec45e99a4519bcd7561442659861bc0b3963d27df71c7d9bff3503b7aae91d8a32abc4f8ae8b272bdcb3babad6480cf3b3705f9097d989c3d3ddc116590e56835f43ee0f69449a677a2d5772bc4dcbea5d717221f56f989f8e25a2169749d3008f7e75e4a27c75a5987bd0190077857193185d847bdac00f3356e9e96535a88b0028c9ef07b403f6bfa16c41ce4c86c6bd87750dad0e943a22f17f07c736e30a5f32c38f681762e53292e168e05f777a560d37e81a8b5c849c6f1ea52106b5c0c46f3adfd0eb35fda97c07a77133a4121ac2c88b365b88d391a78edb279278898d8599571533f752cc419afa80e32d6ce9e88af7ccd27a21473d6d1c7fafab697b029646361073321aa9673e4895d8029aea18b504ad0198c6d4f5b93f4885b02843f9d1485874361215e7e57e430b4b282ea02a4f0e5c2f2832b9872c5ec1e1f06f3cb1c458886cf55d858a87650315c9360579b27fd4546b494463b4d7894cc5bf309d10109e73ccb7d3d8cefad491a4e2565053cbb0e0719fe73a873c47cc7bc3e513f481a77b9c2d51ac5e3d5d9d705a2c93b560bf546193336953bd3ff8d3ca3dc3f84f708c2cbb91c3e55cec6ce678d40ce53549e038da5af7d7d32307684263aa615fc068057ed743aa4759e9aaa3359e3b380805c1b691b41fe0cee916b217df8834ab0a21f18a05866a3ca991bf6326d7ac14ca00b758e558265a1001681e76300554f550c6a4826d0ad06d895226806f5c0a8a7792f76201bb0b08d97fc593f845d4f0e723ec1ac7bc4be451b05e783955643888141e73c99526a6caa5815c15de4d5f057f9315782b9a0c5070900bde2012f3eab802c87fe3a5f05ab3d04a926f572a69aeaeddc470e3c2b0983d27ba8dab9da9d06e7ec1eb84063d68db04dc7af78c932c5abd82932ffa3dda547dec7e457a585e44abbab4c1bce15f6a95d84fc83a82b806f0b71fa79c6946eb333290effeb2cfd2290cb95d7cea5c5c76895adbe15b09f45a6f5c44b720c6c6de85216ff4bfee683d7853d25a1273923c523e2fc7a62df75f00a6a68c708bbfc4ee1c6ecefca1f8523b69759bdc544fcc3e3a91475de1203945cfa0b01d385039018c8af1646e6cc68823cb94ddc331f66ca559b4067f396896a5cd25cba10afbef8018669876ff14ad4046d96aeb0774ab31a73079230e9e56f6010f8f559c4e2f90ff949c739b13c52494160ee6b6c71c51522c9d52f74a5e58e53fda7368d5f893081efe5f68ee264a210b05aa6a85802fc4beb390b6ecc2cd37ad34dd932a1339a920be4db4871b44742069798b18d574ea3ad8dde8cc10beca8af3e6d81d15aca43384c71bbbc7406c6d5c99e4fb43d004bbb585a7a123e04a015f41acb042393b4d8042447b97b729c07a05d9ba5f2c9c0c22119bc67ad6f043b53e", 0x1000)
r0 = socket(0x800000018, 0x3, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0x0, 0x0, 0x0, 0x100000000000000}], 0x0, 0x0, 0x0, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
socket$inet6(0x18, 0x1, 0x3f)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x4, &(0x7f0000000000)=[{0x10001, 0x0, 0x0, 0x3}, {0x48}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f00000002c0)={@empty, @remote})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xc0014462, &(0x7f0000000240)=0x401)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0xc020699e, &(0x7f0000000300))
mlock(&(0x7f0000ffb000/0x1000)=nil, 0xffffffffdf004fff)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020699d, &(0x7f0000000300))
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0xffffffffffffffff)
setreuid(0xee00, r1)
fchdir(r0)
sysctl$net_inet6_icmp6(&(0x7f00000006c0)={0x4, 0x18, 0x29}, 0x8, 0x0, 0x0, &(0x7f0000000840), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000040)=[{0x80}, {0x40}, {0x16}]})
pwrite(r0, &(0x7f0000000100)="5df09d5792f307847f24cd378250", 0xe, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x101}], 0x1})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
write(0xffffffffffffffff, &(0x7f0000000800)="281e8673db0c37a654c1b7ef89093d1e0c3e74eccd251ac081450eb08247dcb6734e7c73f248ab296f6e88fbbaaa8ef3b0db38a30f7777c1b05ffa1945080a2ece4c153a849a97d18b957a90db714905f3390d670dabb75884ac1adb4a1b06abaf7f074d2ac31c13995ede94e5f7d5bf027443cce562a820ae0fef977e48886452f62f04d3424abf4beefba7b0c41b3d3c3741d1d924d8ba22a2c596d30caf12484ecd77c83cedcfbcd2722a6abddc737005e6e64cc293186513e949d7b52c62505c6082e00a4656ecedcd3676a5853c4ccf95f59d06bb2e6a6e1ab0175a1b5d639cc0ab9e8a9d975ec5e88977211ff605d4be0fdf4f77c09fa0b68c2cdf3d6212ada34f57f4da0b078f3f241ff5940a52b407386b342067f19929408d7d6cc1e6e372484ed4c6bad02e4117107b6fd37cbbedbde3f5ba9ce76dd93cc7f58f020380dfcd653c99c1d9f234e13de1a6b247ff58edf68e6d7aeb047cb507775b6fe9db6848ab8596f5fddbbef74740f1480246d2dd6667beb709ed551e7d837a0853cdc45da147ba53f4356eac52e865e982fbccb523acbfd97c6b7d2fa2ea23cdb553bf52305aca010c95abb57752e0b9463853b501d7e3eb6878d0335fdde8ed1e08d3ec07cc86d6523dca73af5aedd45da415eff27bb710d1fdbef8a9311d", 0x1df)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x64}, {0x64}, {0x6, 0x0, 0x0, 0x20}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0xc1206949, &(0x7f0000000300))
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x40004200000028ac)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000a00))
sysctl$vfs_nfs(&(0x7f0000000080)={0xa, 0x2, 0x2}, 0x3, &(0x7f00000000c0)="fb735966", &(0x7f0000000140)=0x4, &(0x7f0000000180), 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000003, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40]}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000380))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000180)='./bus\x00', 0x2000, 0x4401)
ktrace(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x24, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000240)="8e", 0x1, 0x0, 0x0, 0x0)
fcntl$setstatus(r1, 0x4, 0x4)
recvmsg(r1, &(0x7f00000002c0)={0x0, 0x0, &(0x7f0000000000)=[{&(0x7f0000000400)=""/161, 0xa1}], 0x1}, 0x40)
shutdown(r0, 0x2)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
setuid(0xffffffffffffffff)
faccessat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0xfffffffc, 0x0, 0xb04b, 0x0, "2900000000010000000057000000000000000004"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
write(r2, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt(r0, 0x0, 0x4, &(0x7f0000000000), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_linger(r2, 0xffff, 0x80, &(0x7f0000000040)={0x5}, 0x8)
close(r2)
execve(0x0, 0x0, 0x0)
r3 = dup(r0)
accept(r3, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1a}, 0x4, &(0x7f0000000040)='\a', &(0x7f0000000080)=0x1, &(0x7f0000000140), 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000180), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x4c}, {0x48}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="2d00c784be3f585402424c33c16f", 0x49, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010000ded559cb00"})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = socket(0x18, 0x2, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
dup2(r1, r0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000100)=0x43cb9, 0x4)
writev(r0, &(0x7f0000001700)=[{&(0x7f00000002c0)="eb63cec15fec436b2c446d9535e3d38ce55d4e8082b729a47b93e2e1b0be39607b2d6a33cdcd5cd3e9f59f85dddc33987a2a13c15c04c82b87e0b14cdfb2722ef23738f19bd65da545c570e819fd8f1218469cac0324605613505e9f6894a8c0cb6ca69e198b00fc878f34d5df52d571032f1f4ae47f99cc85915d1029916622e3751623e043e3f04cfd12b60cc11a11", 0x90}, {&(0x7f0000000540)="12b8bbe6cff2adb66003052f6586ebe3ee40d2d213f592a71cc16c8277487cc4d1df62d5777ea7e8608c76a4ea3e0c45a69acf3e0cb0213303f509ce37561f247cdd98a0e5fe2ae574437ce8022a8d5149380b6c3e39480c914e32bfb93f86cda8c0cc", 0x1e}, {&(0x7f0000001a00)="b100691c7ce79731efb263c35897b09207b7be586af75d2e40b7b30e7e50ab70ba4b9e77e2bfd9f5ec32b094670599470010000000000000551f52b61205a088ee0f0765019a796c4941447ce62fd948d23e01a2c7aaf01029ecb4a7d5dfaaa53c5cb552935d20d9a113a26b0efbd7039501213f63355dd56bd538efd4229156bb5484fee1d5d8b4c10d84fcf17d5e479fcd83c02981cf98712bf6ae46d2b9a08daabb9fb69ac1f6e36ec413be309c000000000000000000000000bbaf0d4da3e6536e1ab5222270cd457af82da702c3796be0cd6d3dcfff7071db68", 0xfe6a}, {&(0x7f0000000480)="4fdedd0a22537467a0db20a3498134a7e8dd74c1781590acbcb7e6eaa2cceec00966e2125f097def3086a1572c669e7aff1139204f3eb28c7b8b2b9ad42755623e7248e7d291d1d9fb", 0x49}, {&(0x7f0000000380)="73b98f7f3d4ef282d0fff0d14d02582e63ab6424a48e361a63981c4d6a1225b0000893ccc0afd83e3d0a47c31247eedfeb6df13706e4f16586b05505b2c643bfebf188b9d4b4a7d5b71dc9663a10f2187271b553ce75cca96131d412f8216ffa2273c3b17784b74e2b39be48e69269c17c74640e2f9379d2cb79953bca78e4a4f53eaffdbefbad86c8e68b5322314b207812defd8862d92c61ebbf6c852b0feab73db26139db8111c1efa5704557c45e091240208fd46328e7b9c9e159a3322746f637f189fde42d4f8d9674fcae61d451", 0x37}, {&(0x7f0000000140)="2dae27de7f66d9623283227b325039985678ef18d5f7f2cf2cf917f1aebcd2e72d1e17b80c89e1d98edc6abcea3f028495f124ac648668b11e9c696c97fa60ee9394c4dc0923d2d1d9a7dbf281bb2f4af92faee84262ea13a156cdbd910fe850c742d6361c37387cced92a037f94ad14050660d982d3b616ee0d2d73c95a1554d9c41f824d1facca780b161c3bb1f418f2f945880eb81d7395f503eda69f4958c6dd8c6e2914ddd905dfa0773dcd060b989bd190ecd2d29835230b8dcd7ecd1b0ccdca10336e68a479ce2fb42c0a3bd21ba9a76a000000", 0xaa}, {&(0x7f0000001b00)="8757247a66082e16b36f72034a7d237190339f9d52f40f33ec6641cef47bca73456e61c5a6b84d02dc3ba2d8ed288bbe4925566901fadf697b12d9c18804e871266661aad2592b2c37a77819bd138cb223c13a222b5aa79ad13cc39a6fd6bc67de547227154559cd32ca9cbbc934b583e90e91024ed61d598eab014e7ca487abf95b4ac9edf70ca53e3a76409375b146647877230fc6aa0284d81ec3233cf15aa701c6f02d0e30264a684fb7098024c4bfbcd1777b35b0020cd0b9719c386c639b86affab753ff22d0de0d5c787f675f34e7417225e001ed253fc412c611c375ea9a7995db95029dad24f80e7bfe5452988660ee2be91a9f9f0fd8c819f4e5c37e540a664bda32bb4215f5a41c1b8661941fc84f70144ffecf54fc1dbeea34be3287160e6166b340d0fa3e8a1dd359d59598e2c86682d8f14b5d7049c1cb9c4c375eb60325b8e1c90b48009acbeee6fad030e450da59fb83589e46f219ce4cad016274c69db786416ff99226d9e21fa3396f254b2241b09140eb6ee0c6eeb05548f4ff634f512f4cbc3a1025f20adba20e91779734ff1dbda547cd4201424ebb8e288abe11eeb218f3bbdf03b5d91adcb0522f74180c8e97ffad9ad2da84b274fd57134a67930ca8f5dcfdb3898de7293682b3cd12705f8904a96ceac63469a662b87d3296d2458755d65d617b33a8bdd5d92d0e59b89e256f7efea0ea6d800f81e27f8f9c132c7c8ab3b02f96d785f4abcf1c4d3c81f669f4bfe3cfce1f9a1b0ea825cfaf4a9623b4e6420a4a31b38fd48660db2ee4b4d3ec9a7349a1390e0bc9ae8adb0ee390910ebff2654daf713c969025938f93e2f6a6e54de0f6abb36e52f5aa4a59086faff3cba291703d40dde1a1ffc7baadea72a7b0c787a2a45564119a35cc0470438ccce3c45c0851adf97b9e5d015e2ebaaa5f96fb9829db7c0209703c4af0602c55651e618b558ef4717dc84ab619b8898455b7554fda7140f930237944ba71d9c0619b68640690079b838f5a612085ed9b53f72a7a581138e085804ca1c51e159ec37aea005f787bce1edadc8cf941d4c960749a819e6c8e4fbe9cca5b1c8adee98ebf3607200f3240cc535afbc54d16384d43244d67033a207b5bcad4a03ca3c748021b5c4e357fcf024f1a4bb0b137bcae91789d43859e678ce0286379f8081b0ea69e294d1c3b70308161f7e5a9ee48f58ca691299cb5c5288330a1e7880b593272f091a1f6706ec24f240c9c8969fc7c0c478e1b451b5fa9073f03efe9617ba5fb7264f8b0612d6eeebb6de53ae45bfc79ebfcf4639b4edc628fea3c2d8527a59ee55831a283b93ff8c5f35f31c78610d64c32eec016a3ece385f3ab117a5aa23bf6d3f28ee76d9ff9f87de8dd2f31f9157a68566e9c856a031aacd07b2f53b7e6509083f2aab34fd2aad5631c1d8533b34772fc1780a0e2dbbbe591bdeb42ad340af6a998ed7bac59c8843f06774f5ab8493ea2ed10e267ab22dd962892108bd3843097df9dc18fd2f60b144bf7aedb4e2d70a32f28dc44ffc5a771b5ece291b8dce1887f82b11ef5398d92c0da733cc4cb7c78575b76f40f56f1dff685945cc8040d8911b939c8f1a50de451171fe3df271dbfadee32f1642c43e3ef6b588897981af0b3eaf566f6cdbc2545fa34922f34cff9c4d3de45e141da55e02fdc1e6907439986d0717fdf6e704fdca27a3499e234757dc531179b369fab404027723a7e2942dd381a1f129bd651953c82b79a801d97d3b949955812a1a8456f80d3865d27149e22720dc9f7d0fe702c848001b161efee8450d96dfa3140d1fc46f90136bdb8ae4608e1cd3740d9471c127a371fb2d34060b2a60430af7fda8e51c19bb279611b31ca54416b8379fe8d99a1936f07bff241bd467c6d4a023fc650bf142fb2f0efd0eefa5a9d88225cb54c5a0d86fbbc640c7cf2f2bf99012c4278e0ca14891942b27800d3f8e4b016fda6a38e08d060e30d7e87de2af351099f7f231dffa37889828f6e952c7445ff4cba8e08669a6a0bfe9aa69eb230351e4e164239ce5e1d1600440fc64239c0ffcf77d1ab10996c353c959a02100f58d8e8fcada78a73d56bee33f466c0423b35a93ca47e810c4487f997c326519ec63af6b827b30de4154734e7b883138087e329ca7c4c715a1b783a714f6f59e10c2bf6542a1fe6099291713e90d133ed9171988356cf4e69ab78c5eaf4c5522bebebc176b5b52df3a46228cd52a0fdae975d09bf4bd6151acfd0b7b735585c1754f52de05087e3008bb19cf40d182643ae1f5c4b84725ba2af077071d0d6158319df48f8fa8f187da5545187284bbb44d8797c9c312908b67729251a2322b08e0d4227897a27bea0b0fda49d507dea3e3e49b77acd508ea3d2a165c78e343d9d2a32e2dd726be9ee1aecbc1c90df2b44f010918bda0db646b5798cf55365b8222f45932b3fd6b899ab266f05873f0ff4616ff7cbdbd5c54921c49edf9720aaa432a78e32d33c9971c02e7d5f8e90e277e4c16e89e75c8206a9e92579c9f95e6961b6bf4834838f0be0eceab950bee0a8bdafa62bed0c545b8758c388dac670de2ed1990bbdd4f89146e393e4fc8f9ca3c1286d791135d2c5c65eacbe256541a5f307dddd46a8fe2ab9f243c5802c285a888079aee102e8d2ee10027562074a6715aa65554cba301350f60f523e0816efca36f2c2301941ebbe0506f78c59fb3a1e67cb3d37e3a36ad2a9f47a5c8fa62a8d9adbc81de92f7e92f1bbeb1abec7ec7d0499768efd037e93a29060212cbc5ffae07e3debf966a5522768ae0f4f792c7132897255ee97cf8968f18cc84c4a68382dec36b948a7187db8a5f6e3b13261fd00ee1be9db783ed3a3530d75c495f0a37e621c3a3ad7037cbfc823fd40c0189533d7b0c3b5baa01daa2d34400c321e68302d91f3441dfc411d8284bc60cd3980a6ab54d277800002768bb7ef9a09ac13d80e3e893ee13c19407b570b4e2e63ddb8e0952c1e1ae80dfa46644239e5c9891dd15a29b8021aa2f3639334da0d35857ed4a4cdc70c7b64604e924795f5dbd9ae2dbb259b23db8c16e00317903402da8caca8785e0ce64de7d2589789079297282716144dfe733b8a6029d2b8a6fa1fbb7a2c4f00e9ba302fe6c8044002750cda501de9ea9fb2860313613637b0d53f42d978cc03770bfaa4ae91faca9352a6edcfbb9c5ea50ef9af8214af78da57ab5fb6665e78e957162156f673cef071aab47bbc6b777e4adf396be241b262c022865cd4882428f2f038dc58032885b12927beb2be7afc6f19a99f819af82e6264fc5b6d68acdec823b02209c3cc552fa87872872997c1a1ee655523d8993e3c54a20ae27b23b0a26b96a009e8edc513244c41c8f2af25c4d133740f5da0a2467f496606deb9c3e3068dadb3511482cbe42ca9c2b0222394d327e82859b87423ac72b9ea901d2c6abf1ffadd93b334f52a4805bede4b23d344f9a6f8bdd92ffa9975066dbf1277b7657f4fe156fb132f7b99695c2f37d3d8ca32a448d04ea8171f712f9dd719fef0a9955552694d267b0b320509920dee3511019b3aeaf505b1b9712a079f3fc977b7ee2adfa355a368f28727d44f8df77c49d20d70b43bbb6e75432b09ba6ef511ed6f998c0ffd125ca0a1c347ceeeca7a1959e7eba87d55f6e4b29a14a1554a86d80a3667cde561678f503315b510c55c81c8a706baf52d523097935b62a2d594454272cbe76d8ae2bab6be451c52262d6133ba9e4aa9065b47fcfc057dac4f426efe68b51c90bf0b76adda5a4bfbb60501881169141f4e037190807c3a314cea0a5fdfd8c3636488fbbbb04229725a45aa3a77062bc21611ad0bdd32dcf83331ce8b5ee832a3a3b8176bcf3026c22d1aabc91283bb633099ad4f3d37233bb3373819c5ecddc52981c54c2c5538fbf48a0689996de29d500007b8da7d679ef33c1de45a33c4f1879cdae0883f559db304011718fc984a50c2246db477d4394827ab309a11b1b7b0c748c66114f45a217b1501d0d0c3bfd3529f7a198f4b46fac51fc8eae1b63bcffbe3b46e3672466344355b1fe5e0a83d0cc746b7918e9e68db7dd7be1ad4ab66d707ab54ffd2e1edb5cdb024b37d492dac68178b466a454516653d8cbdc7cf5b2f8206aeba807a1c69eb0e8002ad4bf22407d21b43062ac3d9b52391d4356a4ef67ce317aa14fcd23b506082b771029fb92a069d0b65d914c5db4b445ff3f1d63a5eefd3cfe102bd0464429bbb88d5e923017d72aee9e9a633a7587d63983e2f14c55712ee5a47cf875e21421ea451e8d6f37e36bd736466b3aa01662d2016937b490d77b8c88b713836c3dc180df3eb2112e04e4752cc55188351f96ef8ab7acb5b8ec9e3aea812135a50786a6059cecf387863392ef24aca116b533a1eea3bdf84a483ab9c397c0bc7bb03d4d6bbed896d0ebc0d763bbe1a325f3ac33b83112f94ef1a071f851ab1ae8b4773569019dc1a32b50e6ec8f3da25673d34b190a6ce4d2b5e5a3535c05e6f4c29343b1cc63fe21c1a500fca00f47322754de80eb282753be8d153cb8644a25c2a4cca62bc7e8e0f3f7a4ce990187dc22b6a24fbe5596fe84cba7e8c0676f995e98f08fa4c1e375af5517c31442b03ccf7b74b48e060d0dcd7bda9a0efa7bcb8c19182e641a1a446d34c1fa631d9e63ef9058109dae491a4c15f30cda910ee220792f036284086263f53b1fcbc40c394ab0fbbb0ac038de210740a7fe701e196d4b4d22845271b98c5ed1e62df4a2c1f3c56308ada941c65cd9b34237d5154abd836d86eeb42985e22ea4a8f7699ef21eec7c65e58c76af54d11ab2940c0324a5cd20e8c46359c4866ef4067adc6e2489df82a065761a37b47a9688f4409dcf63801f485e2464867baa9a030fadf275a2ee0700a18a9f450538c97730f364f930756f6251b79c82ccc7670bba7041c6177fff7313ac429e20e115b399f5ec9a276a9b352a84d71fb2fb6ffc3a36cb48653b05f6329cf24e51575cb999a303984cfa5023e6ce833ec20dde76d7aa4cb4e8df7fbd1ddbd85bcf0e748f2e2823a68f56b0821c204ea2fd027394593855c3381fb83c671862f59f621556e0120825e5034e4a5d3ae81ff90fa5eb0b28e595ff96df3013d0a7e2d862034eee93e6c6e3ee0a81960ce13532340009ad72853bbd8f94d6be934500f17eb75bcc7ad5dcc39fcbbb2806aa8b83d9fde0041c3112bb9547e8857a1c3662b1597537a07165bb3fd51f9d68fda06b5759cb35a5575303056eb2f6566ebcc1257e31c3cc19a2a2e049c5d9486b7b05dc9c8558d41958a5643da0755af68e233ebfc8b6d558f63d4cd8f110e55f7dff17cda4f14c4c607d628395a21f679cf5565ecbf2213b9debb8ecd25a30cec3e352540cc8d0e52afd1b7b4b4c0e1231bbd73a96db21c01f59a585ab84f6ccdc4363dd1d90a5f4565d21a67ed7b32ec36e94fb43fbb8c1a45c28af6d0e4a8e125ab73539a9a05ec7a169c7cc506a48b58bd3a9b4d586f471bf07c5434a2e151527aa6c666c8cf7c6574c0ea82fdf273d4b3530ea9abc54a5a4dc83075ab705b9063c9e832fc8b4208dbf5ad68b50d4db92d471ec5012359e6e08b7cc3af733a89534a77779da72753b1e4872c8df2f5a8a2e9991904ae004e6609c4d672fdc50db75de75ab52ce388f2f462f525fe7b05c108f20815c08cf15c64cf203cf1f7ced17453a9514f9e2c5bffd4e672b1d13f885a0c1e58f1bafe08cbad1ca06a5e3479f595e79e4a6de7bf2b99e5cf985c3fcbcc994f02dda25a05c8dccf2e880ef0912d2e5172933ee9c262ef4cad6e8d331b473cdd2c70a741e8e3c924aa68d9ec88fa7f415115e0b6527bf67aab4068f6fbd6a83a2a2144adf80f364697f42adc63f439bfdc02589bbd74d914d93d086fe4d06322c098bf8e21d92558a0c17bfd048ac46a8c572091487acb550f36915dd7caf17396e8f6e74c6ef8cc6a4e0bc15268dfdf6f46bc01cc928331425f889a7d6077b8956059d312e4be0abc77c940b22dacd7ce965e418a325554e622dee5a7ec0946b433dabfbdaa0806b855c5764f23bd75c645c32d1d3a4118debb1a718dacf5c91fa61956f118d9", 0x365}, {&(0x7f0000001600)="9289947d784edcd692767038d402bb0e866c103bec2d1cede81258ac70126ef522ee20a0098ef79a760ceecad489bb2018dae9ad3990ab36a5a715f223bdd513ec4d32f2279e413ac398a8e8023772d17de1ac86e82243fcec98a12f07f9cb56d18fa1fcdbc952b2b937e9527fea923dc09a8017360dddb48cf7d1545b825353d38cbace2ea0c1a9883960b50a1665345afde8fba04618e9a2847041576ad947b374ae0bc20dddf92c19af0712f81d85aea7e260cdb7b9a4a00934cc26eb4546dbed26c1ff5823e469c7ceb168aa522aadf8aa764db786b5250d6773cd21eb54f4fa13838c36708b5d11d1a938749ceb5b882d6b", 0xf4}], 0x8)
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
writev(r1, &(0x7f00000002c0)=[{&(0x7f0000000a00)='B', 0x1}, {&(0x7f0000000240)="fb9db645cae7a6e4742943b7205f70ab735cf51bdfc10b9b9c46309a4a12a5826ee036d3c6e297254661724e938aa94b86", 0x31}, {&(0x7f0000000380)="aac4c240464d5e34dc096a30b52fdb22c5669546dba69aba828992d7724fbc1a6f72c634f3b33887d1dad69e629f57aec5116e473c8f031ba82b09db34fda0ce4f2034dc055a3048524650eed0c70e87ad0ade529201d7b5f26ec4f33576c6627f96c2df1989505c23200f4850c0e03a795a33d5f578233d2dd93b6532e3312e8a2723058fe9055c9afda8f799167bf98ca498a7a39fc2f92e1f96f96439a9613be81f4103a6aaa72e70434301a59a724d7bffbaec1fe732722b47c51d29ba60c39d64644d8434681abb3581d1175a3877e3066b8d", 0xd5}, {&(0x7f0000000540)="6803f1b645517aae7e4001ddd56ff9c04ac8cb3189927d817a5d2ca07d2739d1ad21274ad6c50ff5acdb2d3e3e7f05ad76b21505a3fd8c3c29843d8798c614e195", 0x41}, {&(0x7f0000000700)="fc30f6f5c96255d7425a4a571e3bc6954e981dfdd3f931052b7ffaf1835384bb13f55e1c9eb5d550c0000545c6964f0c6c9b6e1a7889501b87ab2b829d15ba19a9a0ccf11e794d36a653468e90d2d8b5353d445c6b941680b000888c8c9ed7efb6584ca173bac7eaa684c0a816bc81685a9412aff4806a62c4ab", 0x7a}, {&(0x7f0000000780)="c52a6226151c2d4e6bbfc2b79df7f58b76aa77b0b5169ec9a1b8e6eed0629bbd6ec0f8b0723c1be59898a8d56bf2b1d6f40e2c51fd95ef646851d343753f9ecb5a22a92d91ebfc038500278563cdec884fb13511525fd49c8ef391cdd41e6d6e0ad8b086dcb593dc8b23bb912e611f3853ba7b48e5fd68247c70cd18c42b8add332cc036f5cd7aacb38f6f83357c59ac7ca95aee2573ae1529d2014a9f506537df46613195c8b847e59fb3740cb6ceb765eaaa017568acaf285f21d6662adbef8b598f53baf9a3f682f481d0b7874e4803aa00f4503a8773a70b25a8f60f61fd03990ca9e976cc2d466ab5e34f5837529d0d1f2c465b67499fc3a21ea3", 0xfd}], 0x6)
write(r0, &(0x7f0000000340), 0xd4e688a67930cd)
write(r1, &(0x7f0000000040), 0xfeea)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000280)=0x1f)
close(r0)
readv(0xffffffffffffffff, &(0x7f0000000200)=[{&(0x7f0000000100)=""/222, 0xde}, {&(0x7f0000000a40)=""/4096, 0x1000}], 0x2)
msgrcv(0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100"/227], 0xe3, 0x6, 0x1000)
r2 = msgget$private(0x0, 0x200)
msgctl$IPC_STAT(r2, 0x2, &(0x7f0000000100)=""/133)
msgrcv(r2, &(0x7f00000001c0)={0x0, ""/46}, 0x36, 0x0, 0x1000)
msgsnd(r2, &(0x7f00000005c0)={0x0, "ad41462fafa8ba9416524e0305ffffffffffffffff14f3c25ef69de2543b464d85cd1546809376ce8f7db5056171ad80b6b4caa048f7ffffffffffffff73136a599973afb37a8f325717c4157a8f35fe45d2f922fc9c3295001e5a24f1a8c56fa3c1ac5a68ec17e34e1c796ca22accfbb80e9dedc4046d07287ccd1842043b5ec8e2c813d653f33ef5297de8c0f8f4429b6e783afd3da6272e161967c2152d2814bc6d195bf2e1d6700d8f8a6fe2bffa37d3f95c9cdd064370bc61b6f83c798b25b20a8f5122bdcff47d3b94ac8da1f32a74f0d8e7035c2eac2dce8e81c070ab059b8be0c00faee3af41840f28303a48e2aba85343375a25a42d0bdd8992df9e960dcb9be45795bd5fb09fd9b88e73ba9a"}, 0x119, 0x800)
msgget(0x1, 0x204)
msgctl$IPC_RMID(0x0, 0x0)
msgget$private(0x0, 0x47)
poll(&(0x7f0000000000)=[{r1, 0x4}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x74}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000100)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @multicast2}, @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x47}, 0x3, 0x0, 0x0, 0x0, 0x4b)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000040)={&(0x7f0000000000)=[{0x2, 0x70}, {0x47, 0x7fffffff}, {0x8f, 0x7}, {0x87, 0x80}], 0x4})
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000440)={&(0x7f0000000480)=[{0x3}], 0x1})
syz_emit_ethernet(0x2e, &(0x7f0000000180)={@local, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @empty, @empty, {[@ssrr={0x89, 0x3}]}}, @icmp=@echo_reply}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000240)=[{0x87}, {0x4d}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x60}, {0x20}, {0x6, 0x0, 0x0, 0x4000000}]})
write(r0, &(0x7f0000000180)="5d526a9208ff69923a366b51f0be", 0xe)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x35}, 0x4, &(0x7f00000000c0), 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, &(0x7f00000001c0)="0032568c952143b05817620b7ae7a9808adf9c9942c96e092d13b51ecc75ea5ad4ae0b1e9539fd2fdd55ab68a49b016ce6def2bcdf396195100bcc62e8e3250ba90d209f4d123b7f718fe88f90d8e10841a85512c25ff121d98ba6f16db95ab0bdb375d627800de0359f439b9a017d9af7229914c0e305b36cfd81c6fe307fcaa598ed0b238d490c7501756425219a3a560a9b5a80c6c34aecaddd1feb8df45570029164634b6e70fd922f194d05d1ffcd10de76650b54a49ed67e610abbf6072885c5960aed7ad71910edb61a27a9235d7cefb5775fcde1769574aaecc1f17e2cb3e753702d72901deaf8650ec0d543f23812d6caab9c76a67d3c20d578c109a6c10f7ab40538890383908281bf81f072ca", 0x112)
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x4, 0x1})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffbc, 0x0, 0x0, 0x4, "6fc600e400000000120000000000000000e9ff00"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x3, 0x0, "00000000000100"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setrlimit(0x8, &(0x7f0000000280)={0x4, 0x9})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
fcntl$dupfd(r0, 0x0, r0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)="ee", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{}, {0x7}, {0x6, 0x0, 0x0, 0xbfff0003}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r1=>0x0}, &(0x7f0000000100)=0x1)
setuid(r1)
r2 = socket$unix(0x1, 0x2, 0x0)
r3 = socket$inet(0x2, 0x2, 0x0)
r4 = fcntl$dupfd(r2, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8028698c, &(0x7f0000000000))
munmap(&(0x7f0000202000/0x2000)=nil, 0x2000)
r0 = msgget(0x2, 0x600)
msgsnd(0x0, &(0x7f0000000540)=ANY=[], 0xf1, 0x800)
r1 = msgget$private(0x0, 0x202)
msgrcv(r0, &(0x7f00000002c0)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000004ab3000000000000000000000000000000df64b44af0a2c2c7090b4b132deb65b30c058ebeda5bf39bcb13e4ae40ef10678a1311b888e606d0c6236a1e5feddb52b341fb6b4e25f32aa752da074af0a8eea914721d23a6b20424fbad2a65f07e4319669e67bce8e959c10e6be19a7892cff20bdb470d43b04a06da94633356ada36b2d759633899ad3105cf00842e59dff35c3244d7deac29364836f7f54a424dd7263d32bd5bf64882db53607d4057f4baf72f650658c95dfba69d0735d9598af21b23c0ef89bf96a51785d1c6484c1cfd38533dce5d6e68f7e16c33848a952a0fe70d3f3"], 0x2b, 0x2, 0x1800)
msgsnd(r1, &(0x7f0000001440)=ANY=[@ANYRES8=r1, @ANYRES8, @ANYRES64=r1, @ANYRES32, @ANYBLOB="5bb2725268ba86c5a38c45c1e3dd6a9cae4ff392283bb0af012cf2f2479af2f57079bb2a0b21d28e523719b3157ea7c201850083b8ed7e9f1aa67dcb1127765739cb215c9744221b71e18d026445cf8294af3f499f089f8c34ce2666606bbd6a2a02c5cebb8a3053a95e0212b3ccb2619204c6a15089ce42699d626e64bf01e7fd96ff43dc2700e8a6a776d34d3fe84d33edde288138a4217e8bff3190b750ec37f3b61c8e5f130b5a77eb6699a4f4ee11f3cfe5d920"], 0x0, 0x0)
msgsnd(0x0, &(0x7f00000002c0)=ANY=[], 0x61, 0x0)
r2 = msgget$private(0x0, 0xfffffffffffffffd)
msgctl$IPC_SET(r2, 0x1, 0xfffffffffffffffe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4}, {0x24}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@remote, @random="cdac86522d4a", [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="676d702557e9", @multicast2, @broadcast, @loopback}}}})
pledge(&(0x7f0000000140)='vmm \xc1\xf7\xb3\x11\xcaj\x9dr\xf1r9h\xe1\xaaz\x16\xa8\xc9\xe2\xbce\xcd\xb2y\x82>\x85!q\xe7\x0f\x8e\x1aC\xb8\xbc\xe2@\xb0i\x97\xac[\x8cTU\xe1h6Q5\xf1\xfa\xfe\xbb\xf4\xc49\xda\x9f\xe6\x01\x9cw\xec\x13I\xcd\x88\x05@\xd5\x9f6\xce)\xde\t\xcc\xf1\x97\xab]4\xc6\x9e\xb6\xbfH\xc2\xe2\x94\xda\x8b\x89\xd7\xc5K4\xf4\xd3\x8bG\x19\xe6\xbbU1\xab6m\xf0\xaf\xb7(\xf1\xee\xda\x84F\xb8\xe1\xd42\xa8\'\x14sr\x84\xd1{2\"\xc4W\xc8U\xdc\xa4X\xa6\xfc\xac\xb90\x06\x98/e\x14Ep\x9a\xf1/\xf0\xf2\x9e\xf5\xe5\xde\xca\x92\xf9\xb1\x80z\x1e\v] \x00\xc5CJ\xe1e0\x8d\xe9\xb7\x0fm\xec\x86[\xc55`\x84\xcf\xd6Z\xf9\xeaq\x93Y\xb1\xd7\xaa\xc1DX@|<`\xb9[\xeb\x06\xef\xf8\x85r\r\x1f\a\x8eh\xc6\xfc\xfa\x82s\x95\x89\xdc\x0585\x16\xd8O]\x1aw\xee\x92', 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1204460, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x18, 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0xe917009502a520f1, 0x801)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000140)=0x7)
close(r0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x14)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be789e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ec6e01be657aea8c500000002000000000000020208a371a3f80004000000000000000100000000000000", 0xb1, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r1 = socket$inet(0x2, 0x3, 0x0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
socket(0x38, 0x4, 0x20)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$WSDISPLAYIO_ADDSCREEN(r2, 0x80245753, &(0x7f0000000200)={0x10000, './file0\x00', './file0\x00'})
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r5 = openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000240), 0x100, 0x0)
mmap(&(0x7f0000050000/0x3000)=nil, 0x3000, 0x1, 0x12, r5, 0x6)
r6 = socket(0x11, 0x4003, 0x0)
r7 = dup(r6)
sendto$unix(r7, &(0x7f00000000c0)="b1000501600000900200200007000000000004fecea11ea8fef96ecfc73fd3357ae26caae1c8716e2f35ebb4f00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff3728211ee4fd89720fd3872babfbb770a9f5a872c881ff7cc53c895303b22f31a3fb9037a00f90fb6de01b00000000000200"/177, 0xb1, 0x0, 0x0, 0x0)
ioctl$TIOCSTOP(r7, 0x2000746f)
read(r4, &(0x7f0000000140)=""/181, 0xb5)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f0000000480)={'./file0\x00', 0xffffffff})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000001c0)=[{&(0x7f00000005c0)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8de7ae9fed58938eaeac68a0b0632688ca0fab3647175a23f70cbf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332cf92f38ec1dfdb4a91b59354736154ea628b8cb665", 0xbf}, {&(0x7f0000000440)="a5781d1fe6aee59a92131f30", 0xc}, {&(0x7f00000000c0)='j', 0x1}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000040)=[{0x2}, {0x4c}, {0x6, 0x0, 0x0, 0x800000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000080)="7cdc3fc0aa17dddf7830faa1aaab", 0xe)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001008e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getpeername$unix(r0, 0x0, &(0x7f0000000000))
msync(&(0x7f0000ffc000/0x1000)=nil, 0xffffffffdf003fff, 0x6)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x4, 0x9, "e1937b00000000000000000000021000"})
sysctl$kern(&(0x7f0000000000)={0x1, 0x31}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
shutdown(r1, 0x1)
recvmmsg(0xffffffffffffff9c, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b31f8"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
poll(&(0x7f0000000240)=[{}, {r0, 0x1}], 0x2, 0x0)
poll(&(0x7f0000000040)=[{}, {r0, 0x1}], 0x2, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000080), 0x363f013e73f451be, 0x0)
pwrite(r0, &(0x7f00000000c0)='~4', 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x6, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x4e}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x1, &(0x7f0000000100), 0x10016)
mprotect(&(0x7f0000fff000/0x1000)=nil, 0xb85a4463000, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x2}, {0xc0}, {0x6, 0x0, 0x0, 0x7f}]})
write(r0, &(0x7f00000002c0)="7cdce463e33fc0aadddf7854ead1", 0xe)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xb04b, 0x103, "0000a1be000000000700000000000000e3363b5c"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000280)="c7a896e8aaf53e86f006abf110edfdf0f4a6ae75bee6a557310de500000000000087", 0x22}], 0x1)
write(r2, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x5900)
r0 = kqueue()
r1 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
kevent(r0, &(0x7f00000001c0)=[{{r1}, 0xffffffffffffffff, 0x25f12e15719981ed}], 0x4, 0x0, 0x0, 0x0)
dup2(r1, r0)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x3}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x2, 0x11}, 0x2, 0x0, 0x0, 0x0, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)="01", 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x2, &(0x7f0000000080)=[{}, {0x5c}]})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001740)={0x0, 0x0, &(0x7f0000001600)=[{&(0x7f0000000280)="2c2acc91e5fc10d7a070d2af0261486333a4a21f0f81462cd4bde5132d66a351e63e41928259d977f880b1432e1082b389ee422c724742c919e8c56db2beb2714829cb3b78be12532aa488aac0738a099f1cae7ba0db9d9d340a20b9718044ee67b938f06e3e27da6679bc", 0x6b}], 0x1}, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x3)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400002ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getsockname$inet(r0, &(0x7f00000000c0), &(0x7f0000000000)=0xc)
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
shutdown(r1, 0x2)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000006c0), 0x0, 0x0)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000540), 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000005c0)={&(0x7f0000000580)='./file0\x00', r0})
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r2 = socket(0x2, 0x3, 0x0)
dup2(r2, r0)
acct(&(0x7f0000000000)='./file0\x00')
sysctl$hw(&(0x7f0000000000)={0x7, 0x4}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x40}, {0x87}, {0x4406}]})
syz_emit_ethernet(0x6e, &(0x7f0000000400)=ANY=[])
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x2c}, {0x60}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@empty, @random="cc812211ca64"})
setreuid(0x0, 0xee01)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x140}})
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000100)={{}, 0x0, 0x0, 0xffffffffffffffff})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc450443c, &(0x7f0000000000)={0x0, 0x0, 0x0})
syz_emit_ethernet(0x3a, &(0x7f0000000080)={@random="eb0420e10a89", @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @rand_addr, {[@lsrr={0x83, 0x3}]}}, @icmp=@timestamp}}}})
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x1, 0x0, 0x0, 0x300000004})
r1 = dup(r0)
flock(r1, 0x2)
fcntl$lock(r0, 0x8, &(0x7f00000000c0)={0x1, 0x0, 0x3, 0x2fffffffe})
sysctl$kern(&(0x7f0000000180)={0x1, 0x3a}, 0x2, &(0x7f00000001c0), 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
chmod(&(0x7f0000000000)='./file0\x00', 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc028698a, &(0x7f0000000300)=0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000540)={&(0x7f0000000040)=@abs={0x0, 0x0, 0x3}, 0x8, &(0x7f0000000440)=[{&(0x7f0000000140)="f336a9a509a89ffdda908ce629536175c1362e8499eeced2cfcbac8636f2bf6417a827b676ab582ba1c37205721f234715ce8b0163626f67db2d216e8d260bb94487e9e498935aa30d54925e32267a1419bbd66db7dc325b6037786e3d6f0a110084ca91c9aa2f495b784d90c2675714dbabfd85d483752dcdc8a761ce41a8388e174bca471ba2e0fbe7a992615515ecfd4c", 0x92}, {&(0x7f0000000580)="6ca7ee14f12cbde37d92374d71787cb6d33ad7e7950d7213fb2c18a02910e7698c2fd05c7d8faabb58a685001c837af01c7aa881a72bf300ed88fc274e7b908b4d46e572bfb9afc0766dd1b13591a26a5316c49197da3cd2404658ba1432394514418d8dd058b03963d9830986b6d2e03f3bbec0cd9a12069b2725765c9e0a448d0b187ec9bb885eb8727fabb657b3ea512f2280852012df2084e3", 0x9b}, {&(0x7f0000000200)="86517eb96775211a9d7ab9e45089dd1ff42947dbdaa2ea012ae29c81c44b69ebaa2779925e006f3fd9c0891a6cbcbe75e5f4fbb4980c9a368f4555ec962ff8c52b83789a", 0x44}], 0x3, &(0x7f0000000500)=[@cred={0x20, 0xffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}], 0x20}, 0x7)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80085761, &(0x7f0000001180)={0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x84}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
ioctl$SPKRTUNE(0xffffffffffffff9c, 0x20005302, &(0x7f0000000000)={0x0, 0x401})
semget(0x0, 0x3, 0x698)
r0 = semget(0x0, 0x4, 0x4)
semctl$SETVAL(r0, 0x4, 0x8, &(0x7f0000000040)=0xa0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
r2 = dup(r1)
fcntl$setown(r2, 0x6, 0x0)
openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0xc0e99db6de761f86, 0x0)
r0 = open$dir(&(0x7f00000003c0)='./file0\x00', 0x2, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r0}, 0xfffffffffffffff7, 0xd}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f00000000c0)='r\x00')
open$dir(&(0x7f0000000300)='./file1\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x64}, {0x60}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)=ANY=[])
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(0xffffffffffffffff, &(0x7f00000008c0)={&(0x7f0000000880)={0x0, 0x0, &(0x7f0000000780)=[{0x0}, {&(0x7f00000001c0)=""/160, 0xa0}, {&(0x7f0000000280)=""/246, 0xf6}], 0x3, 0x0}}, 0x10, 0x0, &(0x7f0000000900)={0x0, 0x9})
sendmmsg(r0, &(0x7f0000000080)={0x0}, 0xffffffbc, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x1d}, {0x3}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r0, &(0x7f0000000080)="d000"/14, 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffdffff9, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60acaca1f2844dd730ae78b0e94daa6340f332c6136fec572c091ec964f479d3f09601e05a4265ae5f4d3a70b8530f85f78b162764959532374a5f97ddac09069dc842ee5b6b470f0a2c13de93943f58c029f6bcf437d67a29db534b1221fd5256cba8ba8535737417084659d7f6ab02ab49954c6a070955682cc9dba2931d55a413df999446fbd9407aaab9e385a161f802553f02b1e823e10d785547a7acfe8c11b040de1a30bddc771c1adf40bfb83be91ade242f0a5330f89791d6166e038cbe7f5d719bf1c9d0d9482f436fe94ce6637c899cdb4027cdf11af7c3d3662695d53198228546d6800ac6a6d9106e682c0c96e510ed7cb32772008f345f19a39743eed6db45bca832b6164801181f213a793ebb0fdb2670fc204462dd4c3ef62ea40c007418ef216e5c8be4fc31a982fc12166b472e53b1b4171e7c4a4f96b2d0bc9d72e34b455e6a2833e7f4402db98af3d1c522715c1e26e23066014f1b80741f9b37194e9a74df15837b3bad80bf2cc2f30f14388c68e6d72c6dd465795672595b826b4c31f4162f128035517b0003696bf69d4bc74cffb0b74183ff539c4562d25ca0c6edf7286790e23e37a144f34615d42bd2be5ccbaf4856c11188e1d0564a18beba578e822b6bb1da3322ab17982f3e961251692c659d0685a01be2b0908b51991ce84ea5bbc07ee3989a049a1d51e727c6fd02cd9117d17f36bb4790fe8fa0ccad53430db28c2792e2d3be8dc96d404ea501671e78f0a222a89f540c4d776612efd9f3decc15316e39b3cb6efe90ead64d346d4d40c", 0xfffffd87}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x2, &(0x7f0000000000)=[{0xc0}, {0x16}]})
syz_emit_ethernet(0x26, &(0x7f0000000140)={@random="79ea6ef4433e", @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr}, @icmp}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069b2, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000400)=[{0x64}, {0x1d}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000000)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "08179c7d9234997b52006000000700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000140)="4491125e055171c507d4e19557a30004856f276a74bb36778525298af8", 0x1d}], 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, "f800a48670e8e71f19893204418be661f9d191b2"})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000003c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000940)=[{0x2d}, {0x40}, {0x6, 0x0, 0x0, 0x81}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r0)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
r3 = socket(0x18, 0x400000001002, 0x0)
connect$unix(r3, &(0x7f0000000100)=ANY=[@ANYBLOB="18180035"], 0x1c)
munmap(&(0x7f0000ffc000/0x1000)=nil, 0xffffffffdf003fff)
r0 = socket$inet(0x2, 0x1, 0x0)
shutdown(r0, 0x0)
listen(r0, 0x0)
select(0x40, &(0x7f0000000000)={0xfffffffffffffffb}, 0x0, 0x0, 0x0)
select(0x40, &(0x7f00000001c0)={0x9}, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = dup2(r1, r1)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x3d}, {0x5}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
setrlimit(0x0, &(0x7f0000000000)={0x7fffffffffffffff, 0xffffffffffffffc1})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x9, 0xffffffffffffffff)
r0 = socket(0x18, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x800, 0x0, 0x0)
syz_emit_ethernet(0xe, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @remote={0xac, 0x14, 0x0}, {[@rr={0x7, 0x3}]}}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000001180)=[{&(0x7f0000000180)="ffe959da28a244585b6ee09b4ed071b31f577428f704340d2b73305168721e95b0d3dcf156167f41f21f84c47572cfc23d77c20ff473ba3e3084746eb494f22752afcd3cdcbb08f73d0cb1b845b923972016f74b354c8554cae48a2f1760ff8bf18f1e88585aabd7d37d67c828f33927965308c58b6638e0a046f8524d9adfc4aa72bb73d8791540f3f37d29e6943ac6283608d5653cf95caafc8373a5c737c14b1c3f3e96db41873dae3eef2e1a56346c23129eff537a7e4d6537637cce08505473ba2d0190a2ef1f72401f10a1ac82b2027ff670c0234c569846dcc45ee56554f3e715f5944775470849129b63a5546046d2a108e9f15ad5695149fd6169e06a21ac2adde0c5f89be7a334cbd1a94c49e50afecff070386a43c9f6a3a8c9a0785b594f1d33868fdabbf67ed90a338f825f48fed0f3dca3851ea7e67036d6e86def76d7f05ff5e28be8deafe49a62d5a17014dcd454a8b251d000e5b52195c5a4d6731a36d4177c586c7747d382b95e9c2dcf68c54ab5127aa612d6407747ec594b7ec68ba8f3b2729d7f8845a79070de317163927d884898f409b8c744a03a46fae6672e5b92ff1f0c033dd794f7139ca509003dc5ca5ddeb7ac914b1a414df1ecc4a5bc8fe443a51b599daaf63f17a7efeb484c076ddfd540878ba9b6080508e0d2f16e912e31378aa20d49c178cc1d78116dbcb91d7b42cdde7d464508d36ce70f6870c089fa0b51f8a17a993e5f6556e03d96d537bb47986abf7340e6190c4e8de38148f4cc83013bae4c365fc91ecaa9496a289ba0d1744d463ee3194d3c8d87c3fe524820e5dc3d3cccc76ee5fdd3a971cb9a844cc9cc4e189bccab62befb4a3d7f19ad0421316c4eb526d4b41d18fe6a1f6be143ef89bde0baf1186a32791e257f448ca731fe6b87490f3985641c5a723945e72336e60b341d38cfe31fab6e4d1e9a74c55449ad4e71eadcc1513e451cb4b4af5ce4aa3c1a9dd720e9a3ba2e615be89c9edb467fd4858442d8a1fab24f0bf57084899e6ea63789baea8a6262c3c2624760d252696e20e37d303c11b5382e67edd92dbbfd48d58182c4e95db6a848fd405b2980bd0c979d7a74f77e860fbcea3ed76043b5747082ea93d32083e2ce39c4562e87b87b611fc2981b8c44fda4a4954a65c9de000d319cb3bc05df9fddb29de3a81b87e9a9e33779940e2f8fadefad1e8a36263b9e35a7d906a9fe8751919220336d0019ea4418f448c9f9192df0511747eb2810f67fc64d1077671c9ea5fbbcfee60797ebed819bca7b77ef195ce711c6d9e4dcbe1ff7a674dbf34bd22f27730c1595f0ce7f0e7053338743a2c64dc596542f627007b08097c4597bf0cb3008c95cf10f9d5736bd167323f3ac10fb1645ed1dc04bcd709b2320c13df8fea7e2210927b44ec7fa991cfcc6f5414aaae055dc0b30b2c01ec42b2278e493c0670de531f8d5dccc941f69f35df36d135b1f12d6120827d9ea6c60bb23429ea02ee4f3b9e05f6bb09a9283be69d0e93b189a97b51261316925ca79ee7eb28fd2d5ccd507d727c185feb92bce729b58fb2ddfb08799cd2eb1b7a461a4779f4f37eab97083d8a0d5dd950d2fe6e7945c894cdab89e2d7359d05e2442d5ac7a6221bcd419839974475548e417b9c0d2443946cd314c2d457a043bbe28513f7642627dcdee0416b1a32d05f57a1e4cbee6567bf13f5a2d30bc79e2cc6b9e70065c50cb6ef702a3e48845bccd98952f78e7d20e2091873cab01b6c169363598a33838b82c14b07566cf8552d5b87e93fb83df22dec82d3d44ec1dab19448a49e490b510fe29022b7c1b8f816f02703e2d98431d4bb7214b96adfcb538d99754b8db0ab1376592aa978c69b18c66130b2e380e25b882616017e9259a24f5e6822b32319cebec00233245ae9a61db2f26ac1a7cab0387d250aca94449e817e176bb99985657606d94316ba825af23e7b3348a8e0e4bfc26f84822bf71692db2c8b5ce21e07b6170198e931c697ab187d3501ff76c219ef783a6a6e30904eaf7589e84e21cefe8b835d629b27ed0cd57e1ec66f0810703153fb4e64b060b9e78a59e4f7976af5e3f731875d56e2a9664b89ef6ad681259201399634f92cc4400b2cd1ee28e0d07981e279982518ce4ae25b2ff204a7e06f98d3486c72bc7b0abca652aaa522ff35d1d0cc4fa8efe4dcbcdecabdd7ba65358649b84cc0f19335bec2ff8c94be7a7a931e9ede3bacbabfc8b01c9d621f6248353f664fd05d5ac2850032ddff046c1aa16aedfe3beb620bbdcc376dd11f1c7f831d6f36373c5845413ba6d8289913480a82c783d64b3db74d4e4cff5b7692d7375bb0b5515866b472a52d5b8720205dd8563f9bc52f5279683b2f0e7bb773074d24af8b8e7e2692f3a6222f4c44129751f452ca59d34d35d92b199b2c1cd7682866488e057e07444f7312351a9f18bdcb515927c5835f2860d02279c532f4d8bb3cde9d4fab00043df1abfb3828195f3955aefab34aa8605d3f60664afbf5a34de992cd4417add232467157ced39831cab30618bbca5e5fd6ab0bca59b9189218eda86a6c7fa9dbed2293d764b73a9f63dede04099a8ae63d4d30107eadc6ee455329faf4cf00144a0c339810336a79aea227bb7d2bd543f23d3e250beb973ffee02cc8bfd03406a9f6c7986dc3bf3f2754c6574d27536c381acfd8acd41b2801655292e763d4a302cb0ef802c1dfcd80a0225bde27b9a76756361fa70f56365f56f8993e147dbaac1b5e38cd3a87754a717f99219c2128fdb55dfcef84b137922c88a768db41649192c49f00a223b0ab4f10dae907ac8287981bf329ca6178325d8d503e585e8bb3bdb230717b5a0661a46ea524eab38d393fe122bd42178199a3b3ef8b01ea1dfa358d923539878900b074baba40ed1be0f8b579684f04ec3d1917b68975b1e8a9cba293354fad1fd3a04aff4d15e5f555cc9d7a588a593bc90a13d730ddea96130f2b69015f1758c0ceeb5f13b319e26f79fe384", 0x856}], 0x1)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000100)=[{0x25}, {0x4}, {0x6, 0x0, 0x0, 0xfff}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x69, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0xe5)
fcntl$lock(0xffffffffffffffff, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206916, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)="01", 0x1)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{r0}, 0xfffffffffffffffa, 0x9}, {}, {{}, 0xfffffffffffffff9, 0x49, 0x0, 0x1000}], 0x6, 0x0, 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x74}, {0x45}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x5, 0x7, 0xc0, 0x8}, {0x3, 0x3, 0x2, 0x4}, {0x1f, 0x4, 0x0, 0x8001}]})
r2 = syz_open_pts()
fcntl$lock(r2, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000002})
fcntl$lock(r2, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300000000})
flock(r2, 0x1)
flock(r2, 0x1)
fcntl$lock(r2, 0x10000000000009, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x2000300000000})
socket(0x18, 0x1, 0x3)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
poll(&(0x7f0000000100)=[{r2, 0x20}, {r3, 0x4}, {r0, 0x2}], 0x3, 0xd1)
r4 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r5)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
pipe(&(0x7f0000000200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind$unix(r0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x25}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7afb51e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {}, {}, {0x0, 0x20000}]}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff})
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000680))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5b52f60f48f", 0x94}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000200)="a5781d1f", 0x4}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000480)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f00000002c0)={0x0}, 0x10, 0x0)
r0 = shmget$private(0x0, 0x2000, 0x0, &(0x7f0000ffc000/0x2000)=nil)
setreuid(0xee00, 0x0)
r1 = getuid()
setreuid(0x0, r1)
shmctl$IPC_RMID(r0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105728, &(0x7f0000000040)={&(0x7f0000000000)=[{}, {}], 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x10, 0x0, 0x42)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x50}, {0x44}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='x\x00')
open$dir(&(0x7f0000000140)='./file0\x00', 0x200, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x54}, {0x4c}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x6a7, 0xd5d, "f4ffd25e025c46cf0f4e89000000ff64e300"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x2, &(0x7f0000000000)=[{}, {0x3, 0x0, 0x0, 0xfff}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3f}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x22)
r0 = kqueue()
r1 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{}, {}, {}, {}, {}, {}, {}, {}, {{r1}, 0xfffffffffffffffd}], 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x3}, {{}, 0xfffffffffffffff9}], 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x419, &(0x7f0000000100), 0xff, 0x0)
ioctl$VMM_IOC_READREGS(r1, 0xc2485607, &(0x7f0000000380))
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1d, 0x0, 0x0)
r0 = semget$private(0x0, 0x4, 0x10000015c)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETPID(r0, 0xffffffffffffffff, 0x4, &(0x7f0000000180)=""/145)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f00000002c0)=[{0x3, 0xa5, 0x800}, {0x7, 0xfe01, 0x1000}, {0x3, 0x7f, 0x1800}], 0x3)
semop(r0, &(0x7f0000000280)=[{0x4, 0x6f41, 0x1000}], 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000080)=0xc)
chown(&(0x7f0000000080)='./file0\x00', 0x0, r2)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000004c0)={{0x1, 0x0, 0x0, 0xffffffffffffffff, r2, 0x0, 0x5}, 0x2000007, 0x7ff, 0x1000})
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40}, 0x40, 0xfff, 0x9})
semop(r0, &(0x7f0000000040)=[{0x3, 0x0, 0x1800}, {0x2, 0x7c42, 0x1800}, {0x4, 0x3ff, 0x1800}, {0x0, 0x101, 0x3000}, {0x3, 0x9, 0x1000}, {0x1, 0x1002, 0x1000}, {0x1, 0x2}, {0x3, 0x5, 0x800}, {0x1, 0x9, 0x1800}, {0x1, 0x3, 0x1c00}], 0xa)
r3 = socket(0x18, 0x1, 0x0)
semctl$GETVAL(r0, 0x3, 0x5, &(0x7f0000000380)=""/134)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, <r4=>0x0}, &(0x7f0000000100)=0xc)
semget$private(0x0, 0x0, 0x80)
semctl$SETALL(0xffffffffffffffff, 0x0, 0x9, &(0x7f0000000240)=[0x5, 0x803, 0x1, 0x8001, 0x4])
setreuid(0xffffffffffffffff, r4)
setsockopt(r3, 0x29, 0x32, &(0x7f0000000000)="eb", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x81}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000280)={@empty, @remote})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x4c}, {0x2d}, {0x6, 0x0, 0x0, 0x1100000}]})
write(r0, &(0x7f0000000200)="2329544ba06f91289c00100000f0", 0xe)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = socket$inet6(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r1, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
r4 = getppid()
geteuid()
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r5, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x5}, {0x3d, 0x0, 0x3}, {0x6, 0x0, 0x2}]})
close(r3)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x400}, 0x0, 0x1, 0x0, r4, 0x0, 0x0, 0x0, 0x4})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3d}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2000, 0x0, 0x42)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {0x24}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @local={0xac, 0x14, 0x0}}, @udp={{0x0, 0x1, 0x8}}}}}})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
readv(r0, &(0x7f0000000380)=[{&(0x7f0000000000)=""/28, 0xfffffd06}, {&(0x7f0000000040)=""/22, 0x16}], 0x2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000001c80), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{{}, 0x0, 0x41, 0x0, 0x0, 0x4000}], 0x0, 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1806", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000006c0), 0x0, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup(r0)
r2 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$DIOCMAP(r2, 0xc0106477, &(0x7f00000005c0)={&(0x7f0000000580)='./file0\x00', r0})
dup2(r1, r0)
acct(&(0x7f0000000000)='./file0\x00')
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r0, 0x0, 0x6e, &(0x7f0000000040)="6713ed90", 0x4)
r1 = socket(0x2, 0xc003, 0x2)
setsockopt(r1, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setrlimit(0x8, &(0x7f0000000500))
dup(r0)
sysctl$hw(&(0x7f0000000000)={0x6, 0xe}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80606942, &(0x7f0000000300))
sendmsg(0xffffffffffffffff, 0x0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecfa60080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240)=0xdead4110)
ioctl$WSMOUSEIO_SCALIBCOORDS(0xffffffffffffffff, 0x81205724, &(0x7f0000000480)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {0xc8, 0x200005}]})
poll(&(0x7f0000000100)=[{}], 0xe6, 0x0)
sysctl$net_inet_ip(&(0x7f0000000200)={0x4, 0x2, 0x0, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x7, 0x2}, 0x3, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x3, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f00000000c0)=ANY=[@ANYBLOB="2d01c5"], 0x3)
r2 = dup2(r1, r0)
writev(r2, &(0x7f0000002580)=[{0x0}], 0x1)
setitimer(0x0, &(0x7f0000000080)={{}, {0x0, 0xe709}}, 0x0)
setitimer(0x0, &(0x7f0000000000)={{0x0, 0x8}, {0x0, 0x5}}, 0x0)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r1=>0x0}, &(0x7f0000000100)=0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r3)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000400), &(0x7f0000000440)=0xc)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r5=>0xffffffffffffffff})
getsockopt$sock_cred(r5, 0xffff, 0x1022, &(0x7f0000000400), &(0x7f0000000440)=0xc)
setgroups(0x0, 0x0)
setuid(r1)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x800, 0x0, 0xfbd, 0x0)
munmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
mprotect(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x7}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$hw(&(0x7f0000000100)={0x2, 0x2}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x12}, 0x4, &(0x7f0000000040)="f7df798e", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x4)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
sysctl$kern(0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)="d57e2db550579655610422b88a54e2ba2ae9305a5480090e41c2de54a93fd5b96662362db7b66b4664c03c235604ac538d787457313070d60b74d95b687a4ab04830bb591c372c610705a8291b8855934474ee5285061dc2da244c35c62d04a7b079446409c5cb5c82f51167844d6786b48ee94e6d2e7c9cd73b13", 0x7b)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {}, {}, {}, {0x0, 0x0, 0x0, 0x1000000000400}]}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000240)={0x3, &(0x7f0000000080)=[{0x7c}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)=ANY=[])
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
setsockopt(r1, 0x6, 0x4, &(0x7f0000003740)="125637b3", 0x4)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
sysctl$hw(&(0x7f00000002c0)={0x6, 0x11}, 0x2, &(0x7f0000000300)="6513b3fff9ded1a7f5b5ff32044217dc8abe71810950e71c89c661236ae785fdd6587c7064521de16d3859afe9", &(0x7f0000000400)=0x2d, 0x0, 0x0)
getrusage(0x0, 0x0)
r0 = socket$inet(0x2, 0x9001, 0x0)
sendto$inet(r0, 0x0, 0x0, 0x0, &(0x7f0000000100)={0x2, 0x3}, 0xc)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000200)=ANY=[@ANYBLOB="f4181a72e4edc745ff"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendmsg$unix(r1, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
pipe(&(0x7f00000000c0)={<r0=>0xffffffffffffffff})
close(r0)
openat(r0, &(0x7f0000000200)='./file0\x00', 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0xcd604407, &(0x7f0000000080)=0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x2b, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
dup2(r0, r0)
openat(0xffffffffffffffff, &(0x7f0000000200)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x5c}, {0x2d}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@remote, @broadcast})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x5c}, {0x1}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
pipe(&(0x7f0000000180)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = getpgid(0x0)
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000004c0)={0x3, &(0x7f0000000180)=[{0x74}, {0x81, 0x0, 0x1, 0x80000000}, {0x4000006, 0x3, 0x0, 0x2b}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x800, &(0x7f0000000080), 0xfbd, 0x0)
mquery(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
sysctl$vm(&(0x7f0000000240)={0x7, 0x2}, 0x2, &(0x7f0000000280), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x44}, {0x7}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000340)=[{0x7c}, {0x2}, {0x6, 0x0, 0x0, 0x1003}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
setreuid(0xee00, 0xffffffffffffffff)
r0 = getuid()
setuid(r0)
r1 = socket(0x2, 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
pipe2(&(0x7f00000002c0)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
r3 = fcntl$dupfd(r1, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x80206919, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x1, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000000)=[{{0xffffffffffffff9c}}, {{0xffffffffffffff9c}}, {{r0}}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0xfffb, &(0x7f00000002c0), 0x80000002, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x3c, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="a4027b3c7f000001"], 0x1)
r1 = socket(0x2, 0x1, 0x0)
bind(r1, &(0x7f0000000000)=ANY=[], 0x10)
listen(r1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f00000000c0)=0x200000, 0x4)
r2 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000200)=0x4, 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[], 0x10)
dup2(r2, r1)
r0 = socket(0x1, 0x1, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc050756a, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0x1ff]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
syz_emit_ethernet(0x52, &(0x7f0000000280)=ANY=[@ANYBLOB="ffffffffffffaaaaaaaaaabb86dd605e106b001c0000fe80925f64750600000000000000000000000000bb"])
r0 = syz_open_pts()
syz_open_pts()
poll(&(0x7f0000000040)=[{r0}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f00000010c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x9, &(0x7f0000000140)=[{0xdd, 0x17, 0x54}, {0x8000, 0x0, 0x1, 0x40}, {0x9, 0x9, 0x20, 0x16}, {0x2, 0x2, 0xfc, 0x1}, {0x3, 0x3, 0xb5, 0x81}, {0x8, 0xa2, 0x0, 0x200}, {0x9400, 0x9, 0xff, 0x1}, {0xffc0, 0x2, 0x9, 0x3ff}, {0x8001, 0x3, 0xf9, 0xa5a}]})
syz_emit_ethernet(0x66, &(0x7f0000000440)={@broadcast, @random="41bab27ae709", [], {@ipv6={0x86dd, {0x0, 0x6, "7a8992", 0x30, 0x0, 0x0, @rand_addr="000040000001000009003f00", @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "7be033", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
syz_emit_ethernet(0x26, &(0x7f00000001c0)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @remote={0xac, 0x14, 0x0}, {[@lsrr={0x83, 0x3}]}}}}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0xffffffff})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x78e, 0x0)
write(r0, &(0x7f0000000080)='\x00', 0x1)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1, 0x10, r0, 0x0)
accept$unix(0xffffffffffffffff, &(0x7f0000000180)=@file={0x0, ""/4093}, &(0x7f0000001180)=0xfff)
sysctl$hw(&(0x7f0000000040)={0x6, 0xc}, 0x2, 0x0, 0x0, 0x0, 0x0)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
socketpair(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000340)={0x0, 0x0, 0x0}, 0x0)
dup2(r0, r1)
read(r0, 0x0, 0x41)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x801)
open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
r0 = socket(0x11, 0x0, 0x0)
ioctl$VMM_IOC_INTR(0xffffffffffffffff, 0xc0104451, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000240)=0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000500), 0x0)
r2 = socket(0x11, 0x3, 0x0)
getsockname(r2, 0x0, &(0x7f0000000040))
r3 = socket(0x11, 0x3, 0x0)
getsockname(r3, 0x0, 0x0)
kqueue()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000700)={&(0x7f0000001640)=@abs={0x1, 0x0, 0x2}, 0x8, &(0x7f0000001980)=[{0x0}, {0x0}, {&(0x7f0000001800)="26e860cebbae92c2af33bb3894fb4037d19f37f4fdef92e55c16bdf650dfc327e67729c2a46fbbf9c314b0b8", 0x2c}, {&(0x7f0000001880)="8af845c53124083086907a55e537b3f6cbb74677f790722567eabee6c3d9d9bcf4ab407c1de6c398aaddfe9283911db561ac45958e1527019699970d7bcddead9e8ba3050cd6d53a371127929490eefb82ea3e2262427b20a42eec4ac870236276a41516acb356a90faee8dfd22be3d4db7a526d486d0475d815dbc416535f85c900b137e0d8aa4354137b9672365ca1684a383b79083b5beb6eb6f12639fbf4b809a288413b851d75dbc4a03c17a166d38cb506527c6105efb97d349cade43974881fe571ff51c5e8745484a8a8b0c98c4048ce7f5a4366b51d52c554d5", 0xde}], 0x4, 0x0, 0x50}, 0x9)
fchown(r0, 0x0, r1)
sysctl$net_inet_tcp(&(0x7f00000002c0)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000040)="0102ffac02000926feba70a7cf09f6360f9ea14f040200040000000097069815ca5835b6f65316127c991ab43afd5604c4aa10930ed14b01017f00414191ac6193bb09919a8a372208b127f29c66755d45d5ae11c6731aede78c4421cef62cac7d5ecbf98930edfc29b2e7910599897b40c8c7f4766c3bd966a5c0001d0be46ebb59a63fde26f5ca6a157ad13900ffe6c35b55a191701155a29aec01000000000000007e59a59a05bb68baab118ea421c7324f8145857673b4a15e8909d64f925211d0cc21b9164c249a44e821fa95d881a9bbfb910093d1fbe686246fa85c22ad066d2bee08f7397cfe2cae6e966e98d4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac7354745175bd902a5f48e0a013a1dc24244ade0d510672f477da2c8fffff0000000000000900000047000000001cd900cdab5ba5718bb8720265075b49777b822443cd2740e953a80000000000000000000000000000000009eb3881885647e6acb546c745052eec5503c733fc217eb57458e55df302e2d611ae3e030100a9edbd2d2d845b8e1f2e111835a6b76cd5ff5256df19b5634e2811d910faf269e55e7412e235a9072a43575893f400c7c32ed7a1d4df0000000000000292ebe97f33016e63553689b1e8a46145fc7f2c30c0d29de0ef7c54ed5b402b93815e8214f857ebd1f1e41bfb9a21624824a96d9619e00feb108d5bb62a27d465014bd7652b", &(0x7f0000000000)=0x210, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000280)={0x4, 0x2, 0x6, 0x2}, 0x4, &(0x7f0000000300)="64b5d5aacf3fbf0876f4639868863d78cae72ab15edd6fb1e92ecfce775f8d42e5ddd49ee8f11c8d76eba353842513f33dbce1b35fee730f180c96ae071161e50433d42c1638ca0ec997faf59efddf9a13c254d004f73c877943540d324066773fef20d24d882827e736f52ca09389", 0x0, &(0x7f00000003c0), 0x0)
mkdirat(0xffffffffffffffff, 0x0, 0x0)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
setreuid(0xee00, 0x0)
r1 = getuid()
setreuid(0xee00, r1)
shmat(r0, &(0x7f0000201000/0x2000)=nil, 0x1000)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x25}, {0x24}, {0x6, 0x0, 0x0, 0x1f}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x5, 0x0, 0x0)
bind(0xffffffffffffffff, 0x0, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x2000, 0x1300)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
mmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
writev(r1, &(0x7f0000000400)=[{&(0x7f00000000c0)="d4bc2c679ba78400621dae7f11648f462cc4180c8cf5259fedc1034c171735e5a6664451381f044e4a5da217b44626d668f1dbf609b8fb2197d8553472f873f34fd38a4cfb8a011073f5", 0x4a}, {&(0x7f0000000180)="2912c137a0725a76f108213b7ce3976a1f991881d4807a6725632953311916ad683d2fb35a243c3913a11bd27a6ef964553e9a22b7b6c48e8b7580d3ef116c4426a3e88eda716a99da82821e000e40df9b34df76443094945918fc225d693af70541a0d94fbb06c982038e5ed21525544df00bc9bb7e2b2c37112cc352384074a392a46115d582de9c1092133832760f293b31ac39b504e44bf3e14ae4", 0x9d}, {&(0x7f0000002740)="e46fa48fa79c5847c51f0f5a07422b72ef056a329a4d96aff53bf8bab373c7bee308e29078f92d237f76074d635183373a1a18fabd09a8ed27900b20a5138d9a871fd66de17b4651ad6a3f301df02a64e13b2d55dd95f0133a98be93d52fd6d5e23333e5f8d5673e0aa3cbc370c278bb634cc219cb7b77d1e65f6c61cfeb65d3d4f190fd8b123695153b853424b0c7eae48d8957833d7cf03e425b621eff9594d070817689192e15c2fc74261bb9fdf8f861b0dd6bc3378831346d296e2e25bb45a28ab9387be5d3089154ff6a8a311a0f0c6daef05c86a50e815f3c018777391e77fffa0e486ae6722364d525963d6eae750d1cf6b562d651e6c367d560e076cb789b5af63f635caf73a76a3568b0c3559cbac29b7aa671fd6ba1204425be795d3a19079266a012df07aa3490743f3f7bda28be00aa490df6ee17274d44a53ef98846073bb3137ee1683aa6e533c0f63a53ab059335f5819b64ccb81e450fa6f185f0d30db19e68228c20a5fab27aab920f9bcf8d7cd6c95cb923dd28ff08a47f7b594f0ff01b6592eaab847805d10b3a1943c91d8f6181f3b8533a7a4a872fdcbf84920f7f5f13c89376cd5a91f3e5685bbd7d008b2aa10b1c0775c25457260aba98514fc63928915270e90d059845555d245f3ab3e30a3631e4861981379a573ebfc8d32d959a472954daaafbeccd77b99ab7b1d3d6f65cf2f653e691db64119704e88d604e3d58d304c37516407cababeee08b8ad3bd75ab9505d46f5fb604f675097ea14429758bca73356a790b696904d485be93d8240ccafc6dd0e9e2d2203b3e948289e61d88025059c353cf792b3580bd3f985e71c2b21f43544ba565dee2", 0x263}], 0x3)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
readv(r0, &(0x7f0000000640)=[{&(0x7f0000000080)=""/19, 0x13}, {&(0x7f0000000140)=""/26, 0x1a}, {&(0x7f0000000240)=""/230, 0xe6}, {&(0x7f0000000340)=""/183, 0xb7}, {&(0x7f0000000480)=""/86, 0x56}], 0x5)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000001640)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x2, 0x11, r0, 0x0)
pread(0xffffffffffffffff, &(0x7f0000000180)="b3", 0x1, 0x0)
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000001240))
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$TIOCSWINSZ(r1, 0x80087467, &(0x7f0000000000))
nanosleep(&(0x7f0000000000)={0x9}, &(0x7f0000000240))
r0 = open(&(0x7f0000000100)='./file0\x00', 0x612, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x1b}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x24}, {0x4}, {0x4000006, 0x0, 0x0, 0x400a71}]})
pwrite(r0, &(0x7f0000000100)="0000000000424374cfb5f0b099b9", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x10, &(0x7f0000000000), 0x4)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt(r0, 0x6, 0x1, &(0x7f0000000100)="f6b0aa32", 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001008e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000180)=0x8, 0x4)
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000140)="ed289fba1cf5c8c8d82cdf60c11ad900", 0x10}], 0x1)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff6000/0x1000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ff4000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil, 0x100000000}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ff6000/0x1000)=nil, &(0x7f0000ff4000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffa000/0x1000)=nil}, {&(0x7f0000ff4000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}], ['./file1\x00', './file0\x00', './file0\x00', './file1\x00'], './file\x00', './file0\x00', './file\x00', ['./file', './file', './file', './file']})
r0 = open(&(0x7f0000000000)='./file0\x00', 0xc02, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000000)={0x3})
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
openat$null(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ioctl$TIOCMBIS(r0, 0x8004746c, &(0x7f0000000100))
sysctl$kern(&(0x7f0000000200)={0x1, 0x1b}, 0x2, &(0x7f0000000240), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "866740009382fd0109b8de0fdfad0000010100f5", 0xfffffffc})
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x2)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x3, 0xfffffeff})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x80146950, &(0x7f0000000300))
sysctl$net_inet_carp(&(0x7f0000001040)={0x2, 0x5, 0x11}, 0x4, 0x0, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000000040)='./file0\x00', 0x0)
chroot(&(0x7f0000000080)='./file0/../file0\x00')
openat$pf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
open(&(0x7f0000000000)='./file0/../file0\x00', 0x0, 0x0)
chroot(&(0x7f0000000100)='./file0\x00')
mkdir(&(0x7f00000001c0)='./file0\x00', 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @random="feffffffff00", [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x2, @remote, @rand_addr, @broadcast, @rand_addr}}}})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x25}, {}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000000))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0xa, 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x8100800080002002, 0x2e00)
ktrace(&(0x7f0000000080)='./bus\x00', 0x1, 0x1000, 0x0)
r0 = socket$inet6(0x1e, 0x3, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105728, &(0x7f0000000080)={&(0x7f0000000000)=[{}, {}], 0x2})
sysctl$vm(&(0x7f0000000080)={0xa}, 0x3, &(0x7f0000000340)="ca06b16a735a7756794c17536eb5d082e8ff5d2e29f0032ea51cb5ca7acfef6640dbd4ce4369988888e13f12765e9048b3155415251afc297be1d18a09525ca213db5f70781bd188c955dfccdbee068895e7efc2937ed72dccec418bf7f1a6e78a1e9f9615a699646f4b7db3c195a24465cf3e7b155b98474299ba23d6e6a40364f26e89abf68e22f298075d4d0e027e1ee9096b6dbe48f92aa7917d71dc6b5a704c9260d3928031d212ff035eb9733037fc2ee18afbe29be3fc71bd74742e2816d573724fddaff41069379d18b2d9850e3366deb40dc853fae24cf93fb045cb513f03f5a638d7de6c1068daec99568b67a4a26e5cff52a1646e866c067f638ba4f454e1bbb8e94537abece107b5c85de4087219b0683390bcc11ff3020b9b03f3a723aa772333bca8bc8ac817989a79c9c52bd99ace3f74658843b334e88b7e20fe719a6ce58e92f3d84a77dcd09b3b40e9018010b5cb8f4eeab226c8f964839a8bb64596b1b78427eaca9750855dea5dfc5eb84ad97eaaee624164d4ecd83bdbf7104dc25725287c45da67c54d899bed13676892236058628b0def3741d76d1d34b8592af2fae8928917b37c3e411bc786d22e2dce1ab9bc3de2348b3c6145ac329ec59d1c498ced8e89e4333e9ebfa43a5d4bb308172d7e5f1fb48de5c6fe376192bd4dd335377aa85196b55f5b738cf80354a514cddd8620eac9cdc35595ecf4a09fe2098b04710988b3bae5a926f145a783cebc1eec15a1e0426e501a9d70d2b53d64b2679f6688457e70b48485571734c7deb61254f76ff0391406c96ce1c094105964f949b94b3d2271a3e119f1d1e20acc4270b76e2acc918e7ab66641f15475db880c44db663d20dc997c4c83f8bae03699df5ca0ddb5456740eb9ec532caa222e261068b2e75b8c365358c512f0d3568be81a1094dd0e4ba21ef9c774c83e08363ac2aad4258e8de3eca6c540df74cd8021fc7d4d4150fa42325cb39671ce7d5c7328a0439f0a3c92d81c714ebadb42136638c26a8b25fcbd84203b213c40942412a0ac585bd6dc38f63e5e740cd007b95cc5fdf0c26c27320bacbc01f59d7e725459ff08624498241f48d911bd52b9bc4a56e01e3c6fc28e90dc0fc64b7f6617eb12f554d3ef8f448d529d1d6b16a4a2d056182b282842fb680d6c55b0cdb00b6947f655f4addd26b913011842ff00fab065eb73fb843a1d42f850d45b39dc3b588a8635b0cd34756d79a6fb40a04dd0e19c805fa593f2c779c70f4a7a23203695587192aa6ccde01b9b563f10b65706157c78fcfe1a9a04ee3558ac2ca791bf651747954c4acb4565d2f8678cd94964ef3fa14606820e243c165f80b528dfd8f8647b19f6b6594010d83c7982066c1537221ec469ec590a2685dbe2438700b475edfd1010f77ac54742f16197b562ca29a8d96d275f4ae9afc8119a09af8f2767d5fe8f6bfa201b43c8251924d86a602df01ddd2baaf85e486a0bf30e07247e45193fd8ac7fa3bc6a8b1fb5ab06aff79ab2f8e7402ccddbb460d8b6394fe3e7f64800ad34937e48a290ccfc9b0b2efce931b9f4f2918a128017e24b22411fa9baf77683ff372284bfb20cbb4c9815ae1a19e49439d7b2461f4476d24f16eb744ae7f514b79caf4b1fcf487296f81e815bd4df70964bbd2a76d9a75f80264ca6aaa1113ccf1524d8167d5276dcd97bc8baec09f46e553276abc9e20daeea81da292ce61518144ba5e90455db2eb8826ddec272466e4d2a580a0d3b5923a4249225afe4f62ff612bc52eba8dcb5aa02f8de8682ca2e5c9999bf1eb7c9afafebad8cc48cfeebb53327807518e10ed72fa123d5234fe0664c5278076e96d7a224ed88ce43bce7684dd07ab9b8b6de402a9ab71783cafe993d27dca5ac4bf63f2ac298471b44be1c55b41a02e4d586410930adf448d3096bc589e55ea2c28a097d8356e83a2c498ab5a95b69399f9ddbfd6a009b05a2f3c1b5c350bf88482ab2b000b11f7eb883c91c3f98bc51e1e8f3b9cbda63031d4d0a8807487c24f94046f387d9423ac42e7372664d77f1fa4e81210d0c86250cb1093277c07741bda0a904c4433111bab192ca9fe052b532adfcbb7366b98c9ba91adbc56ce84ec00e4eef17c640552ca32c3d45f3503c5c1f7aaa4a1eae9cd300c9fb00e1ab97d58457597810162bcf14f9a20f6ca77644a2e904cadd3e0a8370f9d97f4ddff5de09dea02815aeff19f639b66d3ab480e09d43c76c0f9792ada94062646646ac0c3ddf049078a43794b2795e746ade0743f15909447b61a442940905f58c2bacfe8505686116ac83b4cd298719420a77c79af469ab7364c565d741e90e03576c47a6c25a42834063b222977e204e7065235edaaa4ab3b9f6bed541a4c0a071628b31dcdbb003eb0047b0bb433aa81568bff46f43dcc993472d55b885f28e3fc3143b6e3adc04831a1443e39de239cd229e45c6472eee27d55b8273d8e9e4673504499961066b5bce34385865fb84a56ae5b82cd04afc95c0fb686056a41f9abfbc825c1154fb2fdb6bf83592118e342264aef01014541a1a8a2457604dc95eb5152f24b32df7c34322a7cc9fc27393bb24869c4c8efa97f55d672081b06a760da484c1e5fd0be48dc2a3cbfcb5500f1351b630d7d3270b7e55e15c131d832297a6f3dd068618f8509ff7d730967a951cabc5a4a69c4639ec641b198bff10b0a15f79cfd1f279b721acce151263b7cc1e3a87d15d015a070b624b5fcf4ac5fabc6b62799d3291df0eb4db3b05bed39556e8dc3b1d89a44cbe09a3763538994ad7e2b971e2aac503bf90b81b51d16bb7cf50e028d83b771a260dd5f9a6ee91b2155d4105c6f83e5ce059249ea371a636592df803553b696e791b3ec06fae67b9aebb2cbba4b99d2769ff6eeac6a42c01950e4da3691ddb103c9c752aad5288dfe1c31da3b4534575c4dc2cfee750a07e815301daa829e7846efc4d83820cbac253b96375b412df4fb4a88994667510a6d21d1f3f0c049ba8c79e8c9eccde60f7d497a90fb8e7f952ca934f835b8fb509673a9ff8a7d3c4238bd199d1467e667063268a75dccc3b3543e29dc176c6a391914e93312be18a0c64dafd208988aa53c84c199c61d5b581abbddba9d43a18dd4eadc47bce18a43611a2cde13081ea7083dbfe44ac243638018b332a9f8743011272d7231d660f8a6996d2a1195538c7ff76234a6b6b8bdacc58955429f55f06e56427eb408ab2350bc5aac96f53e308c18afad3b3692679b629c681fb17c3d069931a70886e4ce454cfa7f4dd54d3ef15c07c888f6da0e5247080030ce72fab6e378954c2928b1ad1574ff5e8dbb7d572dafc72e10375a5b89744a37f16b225a80bd643c0a0a8904b2d08e038861fd06354793083f7247704cfd42ea04b12836c7387c9241cf213dcfeeaafd124de6a1e866f5326afa70756239e29a3ca022194f9b2d6f4c7543092bb032f81d14cb1462600892e37d617cf0896f9e5d987cdb57e8ce16f98d133d78d19eeb7675cb084328acb01d70104d61979fe77ab26f71c4980a4d0e26e129c1c94bf29a037f4609c42b915972d313727bbf504d11146f28747705bb572873d07a8649eeff01b7257ddc52de259b0f9e6f0a08842e949f19dc6185d67b5d47369a00bca9452abb313150d1b5d620c751cdaf9c6391ca34005e944c3f677c6f30a7a36f1ae9934f8af3d505d00ef7b73f5f5f3778f0210f37ade51be4a71fec0f757a451d6c050d0d4c78afd5dd8028fba07df946497a4dea107d25e6f07611379fa7171291b04467a274c2c6982c6eff9edd1f081936fcd2061178d0e2405f393a720365e8c65db086f66e9c41718d92c4f69a4a2781ece55650c8dc9d09db65ba753d846d16c656e8e45d968eb9bc72710756e7c60c3fb243b32aec5fa9cd66feac06cee1b8a2da6cf5d611c27b1f539ee0b85dd41d93303dd6eaaaf93920c2458357dbf34e9930c13b95601471e4518b18c9eb7cd3ec936c3abddc2187b53b7c8312262b36d15ffd2623c839fb75fb6943f830da5751e9a4632732ad7cb9357cdf45f0f011c29635054b04fc1342f985e942c50b58e99209a8ddb0f35fb411ecf65476a97c3abfe4e91cad4c43828878952bc15a729f3eb555fe93a323cd237aee5247843ad655b29874fff543bd1937cf76890d4893bdfcef9dea784642e3fffd6adb867164ac993522af2b3ec9d48372636e8742471b14c201792645235d20c87efc472db634795f8f3b240d56d8a7a47aeeaca3defed27a252cb8cc89ab1c6f19a4cd68f5a5a8751d245dd40b074998e8a7f0fa083d07db424c54cba6d08696fecc28e880f438d0b9670c7629a931e3e1f057cfa582ffd834227351e9b5f0768e3fb78417143f1c572eed6fe84cda64fdc4969dfb69694a354aaa63bd3f475bdefcb02061151535bc9906c0ef34f4cc2d57045f3a370e3ac0a22c70e4bd7fcfd842c6c27d676e5b86034b03fe231bf47a820a489616206ac0980a93791c89343e71eb16a4d18f860ad5b36a759ad21752e312b93b35eafed7db7c647464bda7ea5f1d5dd2879202bface23085a14236755bfc95a91192b41acacf174e3f4ec98fcf74945093", &(0x7f00000000c0)=0xcc1, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
close(r0)
dup(r1)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, &(0x7f0000000100)="78b82ffc5161544792eb60810992249137ceaa4398b05397cfccbc37abef07c2ec598c7f7e1416a6ebdf417927b009b5a2e9f504409354ff9f2b7b86f00da9b6d32fc769f8b951a5eed694fcd07ad08a57ba6208bf801abd7714bbcb90df0c092632af1c93d811b4360dc56bf6869afb41bc64f26aff59331d5f1a630181e2f006f5aee6964977b7edce0ab4234975e75fed7f4254ebdbee86299977c80efd3d157ed7e3c3275204498955a337b463dc766b27de26e152795c91262f0d515b1d7094b810f4ba08955f241ca5b51329a923130ab5af02c331efb7c7f090400c25b30e08d99e95b8a48483222805e29489297c6adc4cda39e4d2eae947d57d98b462e513aea13af30928b9465d8070db75801c72cdf79cc302cceb741752b7332ced3681474a76fade364cf35b8ea5bad2d2a989a098eecbcb35eeb96e8d96656ab011abcc4f2ee318b847ea66e7f4d26ae760d148e16cc1ab23cf07f179aa0bfc7dde8765502dad2649e955bb7ac6458e9e135cedc09d78aa15ebc924342cbc46f43b15bee83c8ddbef0ee84a2c532eead8a34647bfb724dc6a86e9e51686759cb994137d80af8f286e49a2df9d671e8ca21972d52b72cd4c39a93a0609dca301897265b3a95a2dc32388317654f196cc252eae48e1a4cc46cb685b22c61e3ecb2682b938f375b3cf5fd1a4a019b2afea1e289e01f1cc88be4f576efe5152e6cd92df9aa3cd5bb056b077a048ce2019f768ac027ee8b58f96b238554cc956c28e1ebdf03df6bb1b06b08ea24b8006a1540ede36c57ac3d61ca0dddcdd7bdb6a87cb2db964168f0442d4bae96ad2bb1c421a8175568cabc4ebc0ef14173693f04a23165d23a936b45397835111849e3c43cb27221f15a4c57cf2062000ab828932e72b92da01d644638a5664f1d43de15e2fe94056523d3c2d066a94d957d50a0219a00d3f00115d13b3ff82b95fe611cd571b9cd3fd9b6d063d291433bfa97144bf523505ae3d26045afe15308ca4d7e5bded64cfb2028e4b233eec33f837e29dc1d3144592585cb914d61c7e6c3c069eb8a6c50e24ce643af8f8496dae17f0c568f60c612843d1d148a5faa5b2b42e5d39babec0841fd40ce7d7a7ae9a293e898b20bcf639f14aeebbcfdd0a5ff300e6a22873a03fb9ec0bbd777e338b8c2c546316b7d45b33685ba0c305e2c0106494043609063e5073d62dbe01bee86b6f7f272e3017917fb4eca5064d94871109706795e355bf6c38803f0c9ba4c8b8cb29794d6e6d951083fbc8e661afa666545bdc70cc2ac9cca2795243c5f2873aa8926fdbb68f853656f475f1d17cce2940f163eae8efacc1bd80e3baf1fc646be38facdf80ed00abc1b7ef92e58ffa053710581b3ff6a326e83cef2423495d49eacc20d3ac4d0b40de7d590c8818872721ba40081ad4fcb735c63ffebc3305e1d8c032cc2445beb3f7d2839ad8e6d5cca492dfdacb37d3f5900a7b82ac048256b6b7cf9e6d681bb2e3f895194dfc9f30f664b73a699e9b0538165716f1a4299dbe26685c616616f3884cdd621798e53b764b7d0117a9a0e3f2b8b20ec15634a6def7749ed9d761efa6244ecd07cfdf0162d72b2aac18cc20156725fa5216ea4aa0282e57d3d910da73e90ee1571baa6a0dd907af12e9306c2d23d8a5c4dd8a11ee208895d5e2bacd0f76333bae97ada47cc213e531423b492216e389080710be746308c10e92256015540334cb759aa0aeafb7cefe9691c628b4bd", 0x4d9)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x0, 0xffffffffffffffff)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
setuid(r2)
ioctl$TIOCFLUSH(r0, 0x8028698c, &(0x7f0000000000))
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
close(r0)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x628b)
open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
openat$vmm(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
pipe(&(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f00000001c0)={0x0, 0x0, 0x0, {[0x3, 0x100000000, 0x0, 0x1000, 0x0, 0x4, 0x0, 0x0, 0x4, 0x486, 0x0, 0x5, 0x8000, 0x0, 0x0, 0x1], [0x5, 0x8, 0x0, 0x0, 0x0, 0x0, 0xffff, 0x0, 0x8, 0x9], [], [0x0, 0x0, 0x0, 0x19f], [{0xfff8}, {}, {}, {0xcd}, {0x0, 0x0, 0x0, 0x9}, {0xffff, 0x2, 0x0, 0xdb86}, {0x0, 0x0, 0x4, 0x4}]}})
syz_open_pts()
readv(0xffffffffffffffff, &(0x7f0000000480)=[{0x0}], 0x1)
poll(0x0, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000380)=[{{r0}, 0xfffffffffffffffa, 0x3}, {{}, 0xfffffffffffffffe}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x893, &(0x7f0000000400), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000001140)='./file0\x00', 0x0, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x0, 0x0)
unlink(0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
ktrace(&(0x7f0000000040)='./bus\x00', 0x1, 0x2, 0x0)
socket$inet(0x2, 0x0, 0x2)
pipe(&(0x7f00000002c0)={<r0=>0xffffffffffffffff})
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r2 = socket(0x18, 0x1, 0x0)
seteuid(0x0)
r3 = semget$private(0x0, 0x5, 0x288)
semop(r3, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r3, 0xffffffffffffffff, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000180)=0xc)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f00000001c0)={{0x7, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, r4, 0x63, 0x3}, 0x100, 0x321f, 0xd090})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000000040)=@file={0x0, './file1/file0\x00'}, 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ffe4484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e721e40934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49bef3238d0b05c82c7ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae836cadd4c3ba30d79ee4c956121c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e6ac33a607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb033f52ff3063d96df138a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b895417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c07", 0x1000}], 0x1, &(0x7f0000001440)=[@cred={0x20}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, r4}], 0x58, 0xd}, 0x4)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./bus\x00', 0x0, r4, 0x4)
setsockopt(r2, 0x29, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r5 = openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000600), 0x100, 0x0)
r6 = open(&(0x7f0000000640)='./bus\x00', 0x2, 0x80)
poll(&(0x7f0000000680)=[{r1, 0x8}, {r0, 0x40}, {r2, 0x4}, {r5, 0x80}, {r0, 0x20}, {r6, 0x10}, {0xffffffffffffffff, 0x2}], 0x7, 0x7)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0xc}, {0x4c}, {0x6}]})
syz_emit_ethernet(0x3e, &(0x7f0000000080)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "a0f54f", 0x8, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="588516067a6bd66376a4124c4519ecc2", {[], @udp={{0x3, 0x1, 0x8}}}}}}})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000002700)={0x0, 0x0, &(0x7f00000023c0)=[{&(0x7f0000000340)="b1e5ce615fa8cd6a205cadf9fb6a604894438044506aebfe26977a37ef1ac857bcd225d3d9dcaa458d7abc027e46e4f0f5eb6f508f6e00dd6561af765bee3492908df7fc286b3bd5ae05db76228bc766ac1948906b5c2a0d2a55ce00ee1bd1457fcb05759f5d87288fdb70d08a3e47a1c4c2ecf999a10b19f3c7d19d69b0a06e7ae028e2a9659d8eca39566d8fa5d68983ac80658c3706ef15e422c2b239209cd90de84d2606c4b2f6471d6876b0606b4cacd592327c6bccc88aed95a7005ee127af870b11eb6efe7fd8525fc7badbb60e3a2989bc23680a6a4b3bfa4773f30338807d57378a1689b0c323fdb6dd49f84256dcbe185ac4ca2588e7340a8eee727c967138586499feacbb314f1d59d6888c6b81f93baf5039df82dce578f05414010cc9c7c9c705d1219bdd71fcbbb88d05efb82e53fe976b02e013f9f716c6fcdf5df5d40ae3cfc3f672e7f6914b46496114a50b5158d89152daa72653b2313b4783c4e631aa2a59ab81720bce522f5090feb30fa3497b73af4ae4e8bee1c6a066747c1ccc02b45df30f9a1e9b4a335974187371c70d17c2d22068acd8676db289c7863e3329a5a082e97ab297e02eb8dc88b8112232054cb2727ec613b4e0df6e1e3ec3acfd68e67fa51c41c8e428de68b3e74b77421dfa4d8cc0b6d7356f7a9359ccdd06db6a465a98d1169461c8c4e71419d2019083336e1b5e087a97c1698bdd3b6289eecf0678ef2e34751f28e6710314e351776250c56ee18f9f921b190065d41c0e0b3687e3958d7b53c14a39e886f39c0da22d8d3cc284ce007b2f4afbbb4618a6ab37f70aaf061725342241658e674833d425bc6f9d954ede4e40f72f307605b41ec3f9eeffd3a290b4fbc57fc8c715511da950f126b8612697a9eb23b5d18cec4d8e09ee6b4c632ea3d1bc8cd5c40c8bcde01abfa286f50cd0dcc4e76c149bdd03e3c5c7ad968b389fcd19ccd157f19b5d36d0f551e968cd6926ad83e1fd586fa95a9427234208055a56a4a15b17d865b7175bae504285f16952a2be909690b115d1e834a4fa505badbff22613be59150ccb7f2b7c5b8350b5fea2d2d06140d8c543797a8b84d4b9db020e1661706ddef5ba97906685d635d3014f96f90f25cebf76e12817cda84a7b4829532aee91407616a0d31f8fd5ee46c483b966dd2b26232cdd5160c03bab497372a0d8795e726b755d7ff1e410a614a69fd8d7c6c0acccd3d278a0ecb3a7b4d7cd62b029b1b40558a37bcbe54c93e5c8fcd1488f91dd57efcaeccf97b51dde24afdbdbe4258b84b991e901654479f5a0b11982aaceef5a91d78ca6981741521a3ed88c7865f4b594359f6fd4ea3d69db159d91b8899cc5b7e251685db8bbfdb9b6ab37f1a1a2eb1d0ade0b0c01eacbfbbc181e77e4da1a150a6b496fded025c2c7bb32025e9ec9d7a4089d51d3b7c96048b9ad52f282baac4e68a52ff2647f6eda2a8be18372dd11f6ecf952978999bb712d54251c6b47db3a3cada84eba5eb269037d458767ecbac36ee9cea4deb9f5db821f07f46dabbd7139a4362437b70bac97d338debca9569af9af693bd36db3fe4fdb3b034c4271ee4dae265930cefa18a3edba18380e1d9940592f8b45c2ced73237cd37bdeb8c3b390946390dde1eb182c5e49d330ae52b8d709d98314c29206a21f48bd0c27bc341f0188a789c37053f81cadda31517007421f477668149496e4e912c84b8ae83357311659e382f5add65a609a64616a90809", 0x4da}], 0x1, 0x0, 0x100}, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f0000000800)={0x0}, 0x10, 0x0, &(0x7f00000001c0)={0x0, 0x38c6989c})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x64}, {0x54}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x10016)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{}, {0x14}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f00000001c0)={@broadcast, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @remote, "f4eb686fd6c20b379cff079f164f580b"}}}})
pipe2(&(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x4}], 0x1, 0x0)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000000)=0x8)
pipe2(&(0x7f0000000240)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
dup2(r2, r1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x821869de, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0x800000000000009, &(0x7f00000000c0), 0x4)
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@broadcast, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @rand_addr, {[@lsrr]}}, @icmp=@mask_reply={0x8}}}}})
sysctl$net_inet_ip(&(0x7f0000000240)={0x4, 0x2, 0x0, 0x24}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_etherip(&(0x7f0000000080)={0x4, 0x2, 0x2, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000000c0)=0x6)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x0, 0x0, 0x0, 0x8], [0x0, 0x1008], [], [], [{}, {}, {0x0, 0x0, 0x3}]}}})
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$VMM_IOC_INTR(r2, 0x800c5606, &(0x7f0000000040)={0x800, 0x0, 0x3})
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, 0xffffffffffffffff, 0x0)
r3 = openat$klog(0xffffffffffffff9c, &(0x7f0000000100), 0x800, 0x0)
r4 = socket(0x11, 0x3, 0x0)
setsockopt(r4, 0x11, 0x2, &(0x7f0000000000), 0x0)
r5 = socket(0x11, 0x3, 0x0)
setsockopt(r5, 0x11, 0x2, &(0x7f0000000000), 0x0)
r6 = socket(0x11, 0x3, 0x0)
setsockopt(r6, 0x11, 0x2, &(0x7f0000000000), 0x0)
r7 = socket(0x11, 0x3, 0x0)
setsockopt(r7, 0x11, 0x2, &(0x7f0000000000), 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000140)=[{{r3}, 0xfffffffffffffffe, 0x90, 0x20, 0x8, 0x9}, {{r4}, 0xfffffffffffffff8, 0x40, 0x2, 0x7fff, 0x8000000000000000}, {{r0}, 0xffffffffffffffff, 0x22, 0x1, 0xa1ff, 0x856}], 0xffffffff, &(0x7f0000000280)=[{{r5}, 0xfffffffffffffffe, 0x24, 0x2, 0x4, 0x8001}, {{r6}, 0xfffffffffffffff8, 0x1, 0x80000000, 0x8001, 0x6}, {{r7}, 0xfffffffffffffffd, 0x4, 0x2, 0x8000, 0x6}, {{r0}, 0x0, 0x80, 0x20, 0x5, 0x2}, {{r2}, 0xfffffffffffffffc, 0x0, 0x2, 0xfffffffffffffffc, 0x3}, {{0xffffffffffffff9c}, 0xfffffffffffffffb, 0x6, 0x1, 0x4, 0x4}], 0x9, &(0x7f00000001c0)={0x7, 0xfffffffffffffff8})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @local={0xac, 0x14, 0x0}}, @udp={{0x0, 0x1, 0x8}}}}}})
sysctl$machdep(&(0x7f0000000000)={0x7, 0x6}, 0x2, &(0x7f0000001280)="885238b598ff40ef8d2ed2fb83", &(0x7f0000000040)=0xd, &(0x7f0000000180), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000300)=[{0x7}, {0x61}, {0x6, 0x0, 0x0, 0xffbffffe}]})
pwrite(r0, &(0x7f00000001c0)="7548c77f8c44bd39bfdffad9f7ca", 0xe, 0x0)
semget(0x0, 0x3, 0x312)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "01b4328ded000200000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086335)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000540)='./bus\x00', 0x0, 0x0)
r1 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
dup2(r1, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x81}, {0x20}, {0x6, 0x0, 0x0, 0x100}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x1c}, {0x1d}, {0x6, 0x0, 0x0, 0xfffffffb}]})
pwrite(r0, &(0x7f00000001c0)="d0000000000022b3a27e24b3a566", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x14, &(0x7f0000000040), 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
write(r0, 0x0, 0x0)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc8)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f0000001980), 0x1, 0x0)
kevent(r1, &(0x7f0000000200)=[{{r0}, 0xfffffffffffffffe, 0x35}], 0x203, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "5fc513bd965045d3286bf112c0818a7235c9d56c"})
execve(0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
setuid(r2)
ioctl$TIOCFLUSH(r0, 0x8020697a, &(0x7f00000001c0))
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0x4d}], 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000600)=[{}, {}, {}, {}, {}, {}, {}, {{}, 0x0, 0x0, 0x0, 0x0, 0x4}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x893, &(0x7f0000000100), 0x13c000, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
syz_emit_ethernet(0x12, &(0x7f0000000080)={@broadcast, @empty, [{}], {@generic={0x8847}}})
open(&(0x7f00000000c0)='./file0\x00', 0x70e, 0x40)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000040)="2321d74c3fd78d24c3f93020cb", 0xd}], 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="2020e66320650a", 0xa086)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x4d}, {0x4c}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x1, 0x300000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x2, 0x0, 0x100000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x2, 0xffffffffffffff01, 0x200000005})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x28}, {0x20}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000040)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}}}}}})
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
utimensat(0xffffffffffffff9c, 0x0, &(0x7f0000000040), 0x0)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
madvise(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x1)
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
msync(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x2)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x100, 0xfffffffd}], 0x1})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x25}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000100)=ANY=[])
sysctl$kern(&(0x7f0000000080)={0x1, 0x52}, 0x2, &(0x7f00000000c0)="e23691fe", &(0x7f0000000140)=0x4, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x30}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x8000, 0x4)
getsockname$inet(r0, 0x0, &(0x7f0000000100))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$LIOCSFD(r0, 0x274408c0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000004c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmsg(r1, &(0x7f0000001880)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001840)=""/37, 0x25}, 0x0)
r2 = dup(r0)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000480)=0x5)
sendto(r2, &(0x7f0000000540)="ce", 0x1, 0x0, 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x40000400000002c1, 0x0)
ktrace(&(0x7f0000000200)='./file0\x00', 0x4, 0x1334, 0x0)
lstat(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000380))
sysctl$vfs_nfs(&(0x7f0000000040)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000080), 0x0, 0x0, 0x0)
select(0x0, 0x0, &(0x7f0000000040), &(0x7f0000000080), &(0x7f00000000c0)={0x0, 0x202})
setreuid(0xee00, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = getuid()
setreuid(0xee00, r1)
r2 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r2, 0x80047476, &(0x7f0000000040))
r3 = fcntl$getown(r2, 0x5)
ktrace(0x0, 0x1, 0x40000c02, r3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x15}, {0x4d}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x4a, &(0x7f0000000180)={@local, @random="0000ec9b0099", [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x14, 0x0, 0x0, @local={0xfe, 0x42, '\x00', 0x0}, @mcast2, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @loopback}}}}}})
sendmsg(0xffffffffffffff9c, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="07373a7919b09e885e5c9106b1d8744eb47164459fb320d4c840248dc973ff3fc0865f223445b9741d2c49cddd6a0d509b7280c72cf486c9f4f73274aff60f86ccbf259eb7b200e812068fcc4d56ac1257d3c196bc4542804f930bf23f7f6ef9c391d1fce1f76cd945afd386b40618bf9b07ea814ea56acdba4c958640e28084a957cd5fae8feb5660b3ff99ecd5e054e182", 0xde}], 0x1, 0x0}, 0x0)
pwritev(0xffffffffffffffff, &(0x7f00000000c0)=[{&(0x7f0000000300)="18cfddcb60f8294666046fe16731fffffe", 0x11}], 0x1, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x400000000002, 0x0)
r1 = dup2(r0, r0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc038694e, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x400000000094})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0xf578, 0x0, 0x2948, 0xffffffab, "9190000100120000000092ff0000ebffffff00"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0d", 0x9f}], 0x1)
r2 = socket(0x1, 0x5, 0x0)
close(r2)
semop(0x0, &(0x7f0000000000)=[{}, {0x0, 0x1}, {0x4}], 0x3)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "01000611aef6e4c611d53300"})
setsockopt(r1, 0x29, 0x23, 0x0, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001000020000000000000082000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x42, &(0x7f00000000c0)={@local, @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @rand_addr, {[@timestamp={0x44, 0x4}]}}, @icmp=@source_quench={0x11, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @multicast1}}}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f0000000240)={0x3, &(0x7f0000000200)=[{0x8000, 0x7, 0xbc, 0x7fffffff}, {0x0, 0x8, 0x5, 0xfffffffe}, {0x5, 0x2, 0x9, 0x9}]})
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$TIOCFLUSH(r2, 0x80047410, &(0x7f0000000040)=0x20)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x81206919, &(0x7f0000000300))
clock_settime(0x100000000000000, &(0x7f0000000140)={0xc000000})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000005c0), 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0x80085762, &(0x7f0000000880)={0x0, 0x0, 0x0, 0x0, 0x0})
r0 = syz_open_pts()
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000000)=0xe86e)
r0 = socket(0x1, 0x5, 0x0)
bind$inet(r0, &(0x7f0000000000)={0x2, 0x1}, 0xc)
r0 = open$dir(&(0x7f0000000140)='./file1\x00', 0x8a80, 0x0)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
msync(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x6c}, {0x2}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
setrlimit(0x8, &(0x7f00000000c0)={0x9, 0x46})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0x7fffffff, 0x1, 0x6acb9789, "d5426d0d43d1d3d32a0000000300"})
writev(r0, &(0x7f0000000240)=[{&(0x7f0000001200)="1b7950dac6e16fdf262f79589176730284fff1e18e73e60a017c25edb1e4be06019f623a07bd923d209d7f2656df64f4593a3207d460a281f384639f6282b552153b953d89ee94b646bf6fea47a610b9e6f0026282bf24da45acc1276ac6652fe83ddb8ff0b078b5c3fea699107944b40dc0afd7f63f9c9b2d8636cb0cc2969ff3cea3cb8c3713ccb2ff4efd7fcfebcf7b731b900f0dd520aedcd156f7248332c0b6152c5da87c81d33f43", 0xab}], 0x1)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x3ffffffffffe})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
write(r1, &(0x7f0000000140)="d4e822d5f9b912d27899e268e33d383d1bb80cfb", 0x14)
pwritev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000300)="a449e47fe75fdf916bca74e767610ff81a288c4637e529bdfc84467944dca7b3b2d7158d78e3b2e7dbcf18a6e618", 0x2e}], 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x5, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0xc0105715, &(0x7f0000000100)={0x1, &(0x7f00000000c0)=[{}]})
syz_emit_ethernet(0x4a, &(0x7f0000000140)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "6cf65b", 0x14, 0x0, 0x0, @loopback, @rand_addr="acfcd334884942bbc8fd81c328e988ee", {[], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x2, 0x0, 0x0, 0xffff24f1}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x5c}, {0x25}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x54}, {0x44}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
mprotect(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0xa}, 0x2, 0x0, 0x0, &(0x7f00000010c0)='\\', 0x1)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
truncate(&(0x7f00000000c0)='.\x00', 0x0)
r0 = kqueue()
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
kevent(r0, &(0x7f00000000c0)=[{{r1}, 0xffffffffffffffff, 0x5, 0x2}], 0x8001, 0x0, 0x0, 0x0)
chown(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f00000046c0)={0x0, 0x0, &(0x7f00000035c0)=[{0x0}], 0x1, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x4c}, {0x64}, {0x206}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
sysctl$net_inet_esp(0x0, 0x0, 0x0, 0x0, &(0x7f0000000540)="1e85bad2a86e7cbb7f293e505a6f588f0c62179122756abfc92787145b6beed77c4f", 0x22)
r0 = socket$inet(0x2, 0x3, 0x0)
recvmmsg(r0, &(0x7f0000000200)={&(0x7f0000000340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)=""/9, 0x9}}, 0x10, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETBACKLIGHT(r0, 0x800c5712, &(0x7f0000000040))
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000001680), 0x0, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$inet_opts(r0, 0x0, 0x0, &(0x7f00000000c0)=""/181, &(0x7f0000000180)=0xb5)
syz_emit_ethernet(0x66, &(0x7f0000000140)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "000004", 0x30, 0x3a, 0x0, @rand_addr="680000000000000000000000f8ff00", @remote={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "f14bd3", 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
r0 = socket(0x18, 0x3, 0x0)
shutdown(r0, 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{}, {0x4}, {0x6, 0x0, 0x0, 0x800}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
fcntl$setstatus(r0, 0x4, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x1})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000), 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x2, 0x0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
fcntl$dupfd(r0, 0x0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r1, &(0x7f0000001340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001240)=ANY=[@ANYBLOB="100000002900000031"], 0x10}, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000140)='./file0/file0\x00', &(0x7f0000000100)='c\x00')
unveil(&(0x7f0000000040)='.\x00', &(0x7f0000000080)='r\x00')
mkdir(&(0x7f0000000280)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000380)='./file0/file0\x00')
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0xd, &(0x7f00000001c0), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000400)=[{0x4}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000000)={@broadcast, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @remote, "77faa3b4617eacbdeab1702effe4b282"}}}})
syz_emit_ethernet(0xe, &(0x7f0000000000)={@local, @remote, [], {@generic={0x8848}}})
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x2011, r0, 0x0)
madvise(&(0x7f0000011000/0x3000)=nil, 0x3000, 0x6)
ioctl$VMM_IOC_CREATE(0xffffffffffffff9c, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000fff000/0x1000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
mprotect(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x0)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2005)
mprotect(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0)
mprotect(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x1)
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080), 0xc)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
bind(0xffffffffffffffff, 0x0, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
socket$inet6(0x18, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
close(0xffffffffffffffff)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
sendto$unix(r2, &(0x7f0000000100)="28cd", 0x2, 0x5, 0x0, 0x0)
close(r2)
r3 = accept$unix(r1, 0x0, &(0x7f0000000080))
poll(&(0x7f0000000140)=[{r1, 0x80}, {r2, 0x1}, {0xffffffffffffffff, 0x8}, {r2, 0x80}, {0xffffffffffffffff, 0x2}, {0xffffffffffffffff, 0x100}, {r2, 0x20}, {r1, 0x4}, {r1, 0x104}, {r0, 0x100}, {0xffffffffffffffff, 0x20}, {0xffffffffffffffff, 0x100}, {r0, 0x4}], 0xd, 0x6b46)
sendto$unix(r3, &(0x7f0000000280)="d2651271e0b92dd527f70a7d7ff0e45142385065b3b20f26ee40482ee0e7dd4367a14ee619f2fbad7b884a2445e37090d8e50ce03e898b6978e4f6c213b0d32f8182bdada2174c9581032b170f17ffd868f54c2e4d3f85c0e6a96b55abb2fbee93b36ce63ca59cea3e5d7a348a749579be5d5ea9429e522086e52c34a6e2bb29b198ccb5e5f3a0c906fa4b054fc42b16d1ea73d9b1a489a925192b31ffc046939861864df52471bca86490f0e8b775f0b38da5c79919c684844fcd67e3774b16960b52d8ce00d930eee860a9d7008cc8d8b86e2ed15050e906423f6403d5912f8abb2e902fb6e0285d65178f3cec67af684881eb86fc61ba29946b656261e304714e8363869ab194f7b2baeef186fa96dccf9ac0f597fdebf7548dc7c7e26a076a9ffe8574a7c82515907a1663297f14ff284253315aeeb5e5bde559026a7817e5da4ddc832380c806f3fbab935799bc8e8ae2f445fd855c2e02b34a8cd04cef714439d4264a433b5c8aeb41abf7fb1a78cbbd730a3ee823525da6f54de9bef2031b28f2c18023241f2a8bbc6b27a2017f0413934dcfb3357ad13f1f6a3c35d5e7ba7727822834cea2d58a0cb598633e2b6ec1f37cd4292a96c8896375224c33c97819079d3e316ef874fb3ea150dcb8f266211260695c5d54049c1d841d69fa2af952cdca57abb4de29be0f1a8b652aa9e4198b86b6d99fdba910fa64be918deaa37473a1562a069f8a45e389402ff7ba9ad1657a4ee5333729e16fe759151f52e8b578cde92261decd69c1ee820cb554cb4bf713983da3c79990affb5e15e94f8b3ccf2d95f9175d72df9453f606a6a7e869449739486c121324dff471046c22a1fcc3ae73d2ac54387f333a70218f28c1aaed863670327ba5b9cb29893b63bbc7b2d76505ec63ac9c82ebd9bd7996d70ffb677c4ce99b0815ed9e6d3e5623843db2b6aa99643438a6e84f3669159eca33e8c56707e91b780e2a36beb087f7f19854fe9818bf678810fd14d70a9b0a9127225ba5ef5fb85424a9a1215c7a8d50be1c070601326357a00aece2b0333be49ed303715300d0ce095bbdcf611efd8c30310eb0f77281046b9af8b2d9ae451b906a0d8d67dbae556cf2295ad556c5030c729f66537ed41c98d3b9707421eed8c01686c81dd329a31d326c967fb29771835c7baef4c09b08df28cc9b620d7d31d177c3cb8e5023acc9d842f637fa77f52a4bf0df48ce401ecc021666ee07e13bbc62f15bab5eca00e1ff4715a4788c2e26471e944e5c86c2f6bc1cf5a89e3b8bfd1ae33254bbd12092f6845652d55f7125eb7f8bfc15f08f04c181f606fae4ad6732f7b42ff69236ecf0741f825886e203080f0fab4245a5634fd62db9cbffc2ae0c5e3c803c11c52f064afe95fd9f26ba1ae3dcc9137fe3dfe115c8c45efa92645269d6b936ad3497bf742817851af55cc6552ca622a3a559161056339f4a6b4d23ade2da22084a8dce101afe0d78bd3498074cbfd5d42351ab9379368d482c251fd308c3b1d3d8b8b558d6da6e4ce82b8ee0de73d1b153622d4df8fba95b7b8a0ee23187949b4418d98c8a2df0f820a545181af594efaef1ac6182252fb512c790c63ec47c0669ff3ce96f7b0e59c9f0342fda7d0c3d986aa2edc9225acc587a1e5ab7d2f1241f52a5b77b0b91eec6c7cc47782b6c184300264a7a5cf97d47da6982b8d3682804b4e3b6f2fb721c86a92ae06a86db4acab61f2ae5411280478df18e4680debeeb5f2f795f691c86c67485726429e3e9ae471ae008782cd4ae21219630c6651c95d17e4e531fe7e237441bfc033bee6e541de6f6dd3db54f4ede212e5d15c19bb1d08fd5c1dd2a90e2bf4cbbc6a142ab1948a2a61bb6099ea0e08f3ef7ac782af8cdf3cdeff4c4de06492c26d43b270974e3a497080a1653ebbb2fff34df44a0baae1ffdae16f9358cad99bfdaa3099807df118c6639720b898bb3d37ff5a4ea5ca01a0636d9eb33c28466f71abd98989e3cb9ad5d916ae61ae077fd4dada7b05acbd6396d7cb9cb50b4f6cc4f776df903471f411a8888c2c03d8b98d5c94e18e11b431c8175d4eea893ca48cb871387f506cb49a5f9dcaed9e89b858bc951dc2bcafd35cf5ecd41a78fac85dca207a0b4a77b2521fea4a884e158b7c61ffa047ba4cc7132ddd79dfa9aa1ad9346b6f520565275554d04954a693ccf2cfacd255a4492067836c4985356ee31daa05b67ff01a6bd8f8cdded3fee1e893c02b308da2093a13b18552f59bac46b070b2b5c09dae2bc914dee564359b203df0ae95ba03457cdae8d250cafb2115ba65782e9f9d1be75304e606483af5b7b99fcac5326d6ef2c8b73b130402193009255c9c73c0733943cc4211758e1586fbbcb90715154959087ccdd5212a8c156711f5efa128818ce3517d835565e962853e413b212ddd4aa6e0b81a9680556a58e6ab8fc098e7dc642b12be6b53959090d488d7d8731f03ac5b353a69f0d1876039806e946ceb8c657292b25bcc6e96357cec626f3595ff730b3ed6a705028d1e332373e05a2ee46ce2b5b1f78641d5981fc6ebba9040d4570c86a8a3f744aa656074498db7a6e78478bb70c2544e434b2b85e46c62e7a0626ac12a461c56082b220ec6150f5009b1967ed04517a51f4316c27c02d882935db7930269b5f55b63ece793d79872a340b64dd73ead0e06e7168f234ba88a67f535bdf4324cf6b211ddca8c69cc97c0ae295125211cf5f8aac0b80c2942fd4238901a3286085082cabe2d40554f6eb9d9a9e1fc90d41a8f535b5bf19a83285c7e52e4d64e2ef1e71cb5c7c1b9124693ae3e1170a341bd8d69a3255149d2494b39f2de4ed6b1c3074c6a5e12cba40df4ecb2f9a6d86160b6523ab4861b41d07264311f57bd9b29239dbc5c8ca6e139a7a80b976ada8fbe9fe9a324a195a48be70bca21a03b243ade8e1958363c2744db23e4d764372dafad670a8e47063d8af3dd614cc5e9bd1cac1d7b95812ee42549c04ca549685ba7d26a1ac2467051c663379f105e79ef228e53be86248177ffd7e83df0e7a90d72e81b00cf3d3810b8da36acffc31cad801ba0ecccff1356046350c31491f314358a824d0e482e62f6ab82238c46b0cd53b6795d767899c77ec66717857e6811389e837943e7539abc39a2bbfaace7b1e9c4e4cb79fb8ea67a8b6164693d0143e6bb86d78b0a3f18150f4ae9512572cc71b6b2c64fa911cd1b9d178c0443dcfc38ceb3f571732804fd0671aa981ac16476c7c8482a55be9cdeb3c3d1b464bda70dae61def77daa743bd09850162deb2fea07769522f54a7d70f0022e889cf3c0c4cba306f120a95b2af136692f415518a50fe6e76912a3e1b9f0767013a88258534228f9ddf4c6c4d39844ff235c4e7ae8b6059d9eb2a09f463345ed4107e4bbb4ca9e1a9a40b4c24c56e47ee694defda7e8589f5a4ce4ef084d52e21db2176af0db4726d139620b0676ce21fd5aae491c50bc810313477e7cb8420103c16e65eca6dd4bd989412c6acc7dfa19ef346d6cac7b6b0d2b7fc63e0cfff0833949f5b8d0eb63feb594191e7fb11f589b8a080608894163f4c6c6e8b3389c0e723278c52c776ac268fc1f1f442d21be8a5c2d7f037cd145c6b296cec6a0539728fedbe3da231439c4e8d6f95d16b8a7f293af8e1a6439a0a09ff107da1ea9d0c97f98bd43baabe67cc793c8bae2e06d6f4ec6d801360119bcd4a74007b4201007b2c5cf074a9b7d846474541e2185c7d133ab8be10e757674e5f07c8b93dc59fdebfd0ad2592a3f24573c8651fd778d8c3c1f8a42e3e640d66809ad604bcbd31dc28eab0ee2d6d6507a7fd2e5ae9fa8076f34071e46dcb7f02d8744c6c3736b65b6561b867b7ed783750de1a447525f7db090e4305563605af14104530a7ee1c2ecb74587bd3a98454c38a064b9efb3afe6da0169bda863121b987c7f32d5b14bafbf92884597898267cd726ae6c4e2bf4f863f3e89fde35c306ad5765ba2a026ab94bbf27cc0b899517ec06182aaf08edae8a0c8a5ad0fc87b53525ab17fe5b9367a6b04f1ef33efb8a4f052b37fcf5385f15f7019fa450839d320d036f7244a5b87858c06b77441b1c1bdc1a25c4ae16c399d874f74f12c21433827ffd5ea75784159edcff5f320e911d104dbba0a452dd60481678c1f89a1c82c98708604814cf0eb3d49d516fa3182fad3d96b0d2826779e81f65bf97a3234604965c2482a6fc276200824d18ec4f11f99c5e6dfb9d0af0c21140a217822cb312781985a4439655b825affa4252d3d97e8c1e113b0b0949fae2707878d245475f356d7a1db0ad7b558909ee1c51d121f4a9120205f94bc104f0958bad100d475b46bfa9651253c2d0c8a638734889b3753707591e4840831376d89ec386f9f7bb05215d44a7493c98c3ca46293d189c3137a0a1b6fc13c72f45655e40ab6a16838fec1f1f1cc4b1dd6eadc63312749274e01e5818067da8af26d4c8e26d870986e15497549cd4003b0ab8939d9f31a904ed097d88e25065bbe1770968311f0fbefbebc7379ddeb29bf2d5df8e71d6a731f78ede553481502213e6381eba1819abf03e962bffdf57ed92d63d05dfe964291cf2da37bd5eba384958ea9b78f317cb3762fd4cbb6eb71554b433e88b4a91d3eaf1f149653c3b83f689bc60c2ecaade3b494fed64f5ba38274fd6801fd074a2492feef110f01f5fc25588f0f4c51e8d2ce0c85e6a5a0428bcdd0dc3fdee278cd16cb6846e2096c727feabe78758cee31c252488e9bd99fee7205cf38610ff435a015b5cb24860d8734afb67e3c5df8f5e92b55bb2f53c4c88bdfd84bd8290c5efdd18141880b89a7d4ce38a39c0ccf82f0b20c2d1523dd07b325e5ef95a8e22b0905c850326e18b95dbb41a52d456541ceb6510f08801b4dad5b505f381243af6f8e1ff96b3d5b33a9626632ca3e4aba16ad2840dadda108d8a2b533bd5f434d0b4ecadee98b8cce4e2e8001a126be4267b9a31bcd2fc9737a98af92d95046586d7064f291bb7ce8e16adcd040e1318db4a3a061874ef102048f96079f299e774eb1cd255ddded99de7e77aa366d40afe6db294ed38531fe20f6d754006bfbaeb9adb66695b1d15695ff58fefb101a0a65710fa1bc4d9bb772240feba61d75a012e612f9133b77b6f5f982f38cd6f35b0d41e0b0a2d9de9e162664c3905ba89a26198aea774f92ce1146a0e9105ded68498432a67da518040b5bf2b13ed350300882969035566b4a91af5e457f11694430ed504cf3fbfa555db04f232ef058ef5bea26124b7b7461a3815606f0a05b3271517fec44890436000ca38d8be18c5c9fa83d901b65c00e5556d99db455bd58868098981efd3c284787f8731c31759eda3047c81218dde0e4111bc183c54ff514e0ea32ddbb6a9a36da51ea4ac3f0ba7b99f6e88699602f65966b378929d89260b161bb2adcb71afdbedc763088ff1fcd8a56ff2a8709d78ccabe66858d2f61ce8e697d5217172ca9c4780d98085eb9298a076365648cf45348203ea654012a4511cc5254c6bdfcb45dfb7ffac6fb9649ec62ffc298d5f3e6a525b125792e6696abf0309ab9db60aeed768fcb991af433b1db14bfea2787fec9c4784e2d05228241113269d534dd4050d7bf4573471959e6a874c2f1733ef5043e8b7127e6420aa1955566004400317bcd3e3ffd0f4913f292f7e7ce20d90438a04f8a4b648d0eabd542c3f87d83243b367f369670c66e5ed0caf637a699d3892d25c8818344552b4f40582d668b3c346053d3d774bf7324ea557", 0x1000, 0x1, &(0x7f0000001280)=ANY=[@ANYBLOB], 0xa)
shutdown(r2, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x5}, {0x6c}, {0x6, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={{0x3}, {0x0, 0x4}}, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = syz_open_pts()
syz_open_pts()
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000000)=0x32a)
r2 = dup2(r0, r0)
writev(r2, &(0x7f0000000100), 0x1000000000000161)
writev(r2, &(0x7f0000000040), 0x1000000000000059)
write(r1, &(0x7f0000000100)="4ab959368ee62b184e3422414b6f405941a261c2883d1de1519e91670342a1f1b7c0b27ef3aa620b38917bac29895a0fd6388574ef4266222d6e2b7460a60fae139a3f398c039858af8a10e02e339131b652cd51707cbc875f22fc6c808c94e183bbcbfb8f5144f562b40328726d02a8cc1637be0bb3fd6f7b6ebdccde3b077100f00687b211dad9a687292db4ff2b", 0x8f)
r3 = open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r4 = dup2(r3, r3)
pread(r4, &(0x7f00000000c0)="bd", 0xffffff78, 0xa83)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
minherit(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0)
madvise(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
close(r0)
pwritev(r1, &(0x7f0000000100)=[{&(0x7f0000000080)="4402", 0x2}], 0x1, 0x0)
socket(0x2, 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect(r0, &(0x7f0000000000)=@un=@file={0x0, './file0\x00'}, 0xa)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000140)="b10005166000000000000000040000f9786275a33359d20416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ef3cbd2bf55ce27dc27acb5d602000d7d026ba8af63ff37281c18e44489720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000011300"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x24, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000240)=[{0x5c}, {0x87}, {0x6, 0x0, 0x0, 0x553f8734}]})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
setrlimit(0x8, &(0x7f00000000c0)={0x9, 0x46})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x0, 0x45, 0x1, 0x6acb978d, "d5426d0943d1d3d32a0000000300"})
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000200)="20a30b603f84a481d09525888624fc3a633227733075c57935d76fe6c2754218943c8850a13315a25fe3e8155aeca26e3b752148f0a5ec6fa3ddb1d77cf08f0b5475f35a795661d50c2aa4b82d59a1402e2a1ba72429e401515dcad78d88e7910a4024ce4694aa2cc2f1f3793c1428f464251d9244eb4969e6db0ca558a7d488ec5465bd903662f0a968631196bf0e8b70b4dbf1066fb3494f61a6ba8d1c119821594ee9cba55b5e8527d3cb332d9406cf48e049d74c4bb462fc1b8bab489c750b6b5bed067da9485d992a729d9f15a40be596668a45c8794385a044b898f024c63e8e3f45ae248ef634113a27e292c58af5eb8f8b8ebfca812d0f2b7a40644d1a513f48bdf58f7d3e5492b5655c43a99c7fa932fa590b10e4f439b9c48dc2ad07dc817a82331d679fb3c507665476aaeff896d7fe0433d82a0a4d860da96ea9097b42979039b556b039018f33e4aeea0d6f7041c03002904eb07e957508b231989007eea3f24f3ad66dd0f557c283a7cc1691befa30fbea13aa88ca6f4fefd584efce879c35d61fd5380addf97e8adb27b1fce2ea2bad11be78e40ba6fe4f9349946e9480a2bc00f92dd46ecb4c5817f0e9290f3b9c4ddd2780c9c3cd698c2d5073ea1d3193aac0c311e4bd2774ca840ac59e615d5967d4cb7f02b5ea14b6305fcafe58ef87aed5c28d01e1bf36a3ef458257ebb066e1d815149d13f9432ef439a75ec901e91a70c0bf8878b5c69f194996ee438e89fefd9995fb6760930d217fc9dea14eacf7151bb7133f6647fe7da3aa4c1af29fabf7049517bddfe7d5be11ac99bbe6b075ebb6a2d387aba6bfb0cc4e2c3984e84618d29fed0344e0face8d19cfb0c664b2d326733805b971421564c0033863686b84114553926a99db18e3556732f10fd25bcdfbbfe3b611be2b4ba537be2c88a579fd871087ce8889df501b0c76394b00ba3c63311164473e7dbe653b92bd64a40d5dc30461f6b3dd635815b76fa4e6c810cf1af905d7f55df0526e062f087afc919bcfb73c64fbfdb7d96f822e97bf4a113c2e919874f89f27e53bbb04c8a3d0fd3768488ffed1", 0x2fe}], 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c5", 0x8e, 0x0, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x8010570e, &(0x7f0000000080))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000600)=[{&(0x7f0000000140)='F', 0x1}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x2, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
flock(r0, 0x2)
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x4, 0x100000000})
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
chdir(&(0x7f0000739ffe)='..')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x200, 0x0)
ioctl$BIOCGETIF(r0, 0x4020426b, &(0x7f0000000540)=ANY=[@ANYBLOB="a5c8e714c59f9fd8245d0000000004b35f5ad4b4fadf997b0000fd9be6e04a62a5bf9c2a7f07fae3633a6318bf6da53077625e4959605bf26146b32924bcf26e20ce84c3635f7751324e35dc6f38c6fc1befc6f2acd4095294cf1d37d223aaa4ea08b9a67a94a053080afe8e98890ab2b1ae684844f386dac161c71c1e86da27f8c472a8ed13dd5cd0460452e949890523d1bd1342bc4cbe24de634aced465219bb252a53a6c799902ba96a7d1044531f47365e8b92509544a844d7f712f6d7577456c367314e96d01b6335ae4d0e6354ed804c117eea74ddd0c21cab0005d920592ce73609132b3bbbc8addbbd7b04f0839770d36b6f54c865ab737bbb27cbf1d1a66b67768319485500bb5aeaceef45ec018c28d24c8054aeb5700"/295])
dup2(0xffffffffffffffff, 0xffffffffffffff9c)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240)={<r3=>0x0, <r4=>0x0}, &(0x7f0000000140)=0xc)
bind(r1, &(0x7f0000000240)=ANY=[@ANYBLOB="01002e2f66696c653100e1222066464e4d8afbcced8f179558add1547c7fb9948ef40e2876d96ea41e69e648ff8ae9b6dc75ba8aee8739ec6aeaf5d090a3891f22dc5f7efd59db03677de28df379c4cac80c850796c601cccc7914afcb49892b1daf6237fa4b4ed37f8aff0b3473b011e2597b40a0d56b1a0694d264046773c9b99c032727e00a09e333f9247b33330e11b43ca4fa0e274c0493138e5478c47d1a6d5447532e3d89d34a8e0f2bdd2d6b1687bef9dd4a4922b957ae438151222e3616bbd880af0de5fdf8e00431fb9b7313fc"], 0xa)
mkdir(&(0x7f0000000040)='./file0\x00', 0x12b)
msync(&(0x7f0000468000/0x400000)=nil, 0x400000, 0x5)
pipe(&(0x7f0000000080)={<r5=>0xffffffffffffffff})
link(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000100)='./file1\x00')
mmap(&(0x7f000062f000/0x4000)=nil, 0x4000, 0x4, 0x1011, r5, 0x1000)
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000180)={0x0, 0x0, <r6=>0x0}, &(0x7f00000001c0)=0xc)
ioctl$WSDISPLAYIO_LSFONT(r0, 0xc058574e, &(0x7f0000000340))
getsockopt$SO_PEERCRED(r5, 0xffff, 0x1022, &(0x7f00000003c0)={0x0, 0x0, <r7=>0x0}, 0xc)
r8 = getuid()
r9 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x118, r9)
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000400)={{0x7f, r4, r7, r8, r6, 0x40, 0x3f}, 0x0, 0x40, r3, r9, 0x7ff, 0xfffffffffffffffa, 0xeae2, 0x1})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chmod(&(0x7f0000000140)='./file0\x00', 0x2f4)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
rename(&(0x7f0000000080)='./file0/file0\x00', &(0x7f0000000100)='./file0/file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x4, &(0x7f0000000080)=[{0x10001, 0x0, 0x0, 0x6}, {0x3c}, {0x4}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f00000002c0)={@local})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080)={{0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0xfffffffffffffffb})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3c, &(0x7f00000000c0), 0x4)
sysctl$net_inet_ah(&(0x7f0000000000)={0x4, 0x2, 0x33, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x80, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000500)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000100)=[{0x7}, {0x1}, {0x6, 0x0, 0x0, 0x100}]})
pwrite(r0, &(0x7f0000000180)="bbc86460e2440102f2c74cd58cd7", 0xe, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x0, &(0x7f0000000040)})
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x3)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x2})
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x4, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0x9, &(0x7f00000001c0), 0x4)
socketpair$unix(0x1, 0x1, 0x0, 0xfffffffffffffffe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x1d}, {}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0xfffffffffffffffe]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x25)
setsockopt(r0, 0x0, 0x68, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x1000)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x5301)
ktrace(&(0x7f0000000040)='./bus\x00', 0x0, 0x0, 0xffffffffffffffff)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206910, &(0x7f0000000300))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chroot(&(0x7f0000000040)='./file0\x00')
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x4e}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
open$dir(&(0x7f0000000040)='./file0\x00', 0x28e, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
clock_gettime(0x2, &(0x7f0000000200))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x4000010002, 0xffffffffffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x6]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000080)={&(0x7f00000001c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=""/160, 0x8}}, 0x10, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f00000002c0)={0x4, 0x2, 0x6, 0xc}, 0x4, 0x0, 0x0, &(0x7f0000000400)="32f0f610", 0x4)
mknod(&(0x7f0000000180)='./file0\x00', 0x61c8, 0x202)
mknod(&(0x7f0000000080)='./bus\x00', 0x6000, 0x202)
link(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000100)='./bus\x00')
open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
pipe(0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000516600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283018e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x6c}, {0x61}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f0000000040)=[{0x74}]})
open(&(0x7f0000000100)='./file0\x00', 0x461e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r1, 0x0)
sysctl$hw(&(0x7f00000000c0), 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_ah(&(0x7f0000000080), 0x4, &(0x7f00000000c0)="ae72c037a6bbc9", &(0x7f0000000100)=0x7, &(0x7f0000000140)="d0016e28680e1b9b637fb4a28e5825a55d77cb18cfe56b7c6bc3aee6b51fba8227", 0x21)
sysctl$net_inet_ah(&(0x7f0000000500), 0x4, &(0x7f0000000680)="9e089fa338f1a969e01276bce22feeb7eb60970a6b4be31b385e97934022f33242cd8ff74e562400a0d264c926595ddf71f4c326e4ae4de55e8e79ada4eb2441d465a11616a031b19b0440e8979e36b486489e57647a5689f7f75e2bc0d9f730edecec99b0d20b3bd6b5221c8032ecc0ddefbbdabe7dbb72b27d5ccd1735ed18db0277f005d0ec7bc488eaf68cc1a98321ca89ec6f607967e2d0313940576525deacb9a4b5245c6671ed667f4fc090e7d2", &(0x7f0000000540)=0xb1, &(0x7f0000000780)="51cf4f01b4bfb00dd792", 0xa)
sysctl$net_inet_ah(&(0x7f0000000900), 0x4, &(0x7f0000000b40)="8151789b6bd3a60569f0d830bbce678b3bbca19b1ab9969c144b649bfcb1791b586f97f4ba7e648f1fc6b8268b4a06acfcf6ef220dbc090000000000000017dfe8a54a4ba78744c88a4126ac11fec3a7652453a3f84908209a49fcc7293d2cf1be6688e2880fcc63141da7e0cec69e542ae6519d7e88e3a7be38f0364626e63ff0a66fe9cb060e6738b9f416e3bf146c31700800ff106f7cf0a2679776f1602d47d2f666055d000087c286225c756bd0a65150634f6579679c62c32d5ccc32c1decc878d37201cbdec206b192811d6a9640bde04463ae352ca8451c40eba1687f6b5aade6e9c4ed7b0cb32d9864bb93c3ab7ab3ac56c8ba2a4d4e77dc9acc2b2cbbb85432cf02739d93071d87f9f5579e42af595864317e6ca982b6765bd9f9fd4d7c58eac410e438568338ec2af263a54e70fa1fad6f7c8e93e513a4056c31113e0495335a39760c7c598720e3d6384a347f6adb48c0066ce607aa36d6b934485d85c6dc98b56a21f9c301cf28703efd3a1b9224f5b7a43f86a1bac7b286de51d9b9f2d105e7781000000fcacf2043a3dc29312bee3b6478f6d34debaa2221a296f8671f5e95677420716566af3211d18cc2bca4bb53900a24e1301fa55a6ed6e687c150a4870263915dc4991825fa3f667", &(0x7f0000000480)=0x1d2, &(0x7f00000003c0)="bcc9ead47907c76e0400af4b6b900371507f5f735381725be4be84bba287e3", 0x1f)
r0 = socket(0x11, 0x3, 0x0)
getsockopt(r0, 0x8000, 0x1000004, 0x0, 0x0)
syz_emit_ethernet(0x46, &(0x7f00000004c0)=ANY=[])
sysctl$net_inet_ah(&(0x7f0000000180)={0x4, 0x2, 0x33, 0x3}, 0x4, &(0x7f00000001c0)="f553860d961ebecbdb3ef311d509b1065282c1aaed0701807f926c551bce452d219bf4ab4fce97517128e2010996e2ca26e40f6e87f405007b3e591bb6c7934cf29cfddcb1dc7533887848e967849d8a4d3d3724ef04bea8f70fa2bec8313b01bad37be7437c8eed103bebb637636f7e2cebfb5d979aa644d11077bec95bd0b3ee776286ab7baffd4c80f076811e2799b85304813d793ff5f208d3c42db9b8981e59c5c7e31e7894b2c7e4ba5da1fc85f1d1f17fb4e8f5357d4aef64a149f29124cf3f3182ab6d832650c748c80441134935b5a5a1019e0b8d23b19d32f386e5052a782f08a6fc5178ea1d74", &(0x7f00000002c0)=0xec, &(0x7f0000000300)="503803f8876be03f614319c9dc9cdfe574b29ad54168662517c953eb3f551ec08752756df3812e085fd90428235a74ce38158713cbdccd89a60e1937598099f4f6e8bec3742e9fca397adb242c54322b8cd13befb42a1150133009eb50e8b9", 0x5f)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000504600000f9000008000501000000000000cea1fea7fef96eefc73f0000005314fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebb3557699a000000", 0x4d, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000740)=[{0x4, 0x100}, {0x4, 0xffff}, {0x2, 0x1ff, 0x1000}, {0x2, 0x5, 0x800}, {0x3, 0x9, 0x1800}, {0x1, 0xfff, 0x800}, {0x3, 0x0, 0x800}], 0x7)
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
r1 = semget$private(0x0, 0x7, 0x30e)
syz_extract_tcp_res$synack(&(0x7f0000000400), 0x1, 0x0)
semop(r1, &(0x7f0000000740), 0x39)
semctl$SETVAL(r1, 0x0, 0x8, &(0x7f0000000380))
semctl$GETZCNT(r1, 0x0, 0x7, &(0x7f0000000580)=""/224)
sendto$unix(r0, &(0x7f00000007c0)="28d36e268d5837acd630ff647755c5c4df37e2a62c25a8e53e13b65fc7a8d55a8455cc39f8195e67d4316f95cd5a1c4514584a778db89bf5da2f79060ce36195535fb71c43d7c089111c2e7413944292378e0288637aa00103169a7b2f9b47f0e1143247587a593bf7b70abb5a97ee31d6039ec7c589d4e259978ee7698053ae2648f019dce2e6fbdc2ad34ef22aaf3a4093ff920c5729e76e91ba3e30433d816bf2e857916fb7dd18b647129562698f88da6a8c9f29a543a9b254c04d84dad71e1f7516f23405daad766220cf3613f6493278eda1de825ad749a6fa93bac95b58ba634171d3f4a4585e8d8aa2", 0xed, 0x402, &(0x7f00000008c0)=@abs={0x0, 0x0, 0x1}, 0x8)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000440)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140), &(0x7f0000000100)=0x1)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000002})
fcntl$lock(r0, 0x9, &(0x7f0000000080)={0x3, 0x1, 0x0, 0x2000300000001})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x1, 0x8, 0x20002fffff7ff})
semop(0xffffffffffffffff, &(0x7f0000000000)=[{}, {0x0, 0x1}, {0x2}], 0x3)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xc)
writev(r0, &(0x7f0000000280)=[{&(0x7f0000000100)="7f", 0x1}], 0x1)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000080)="832977f427b4050f11d104b604357594c804", 0x12}, {&(0x7f00000000c0)="62f6256f085791d628795283ebf8871d46d9", 0x12}, {&(0x7f0000000180)="9bf8e2a49ff9bbc42c3c71741a15685f4b1727f7fcd1ce33a982ca", 0x1b}], 0x3)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x61}, {0x3d}, {0x6, 0x0, 0x0, 0xfffffffd}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r1, 0xc0104451, &(0x7f0000000140)={0x80007, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r2 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x40, 0x0)
ioctl$VMM_IOC_INTR(r2, 0x800c5606, &(0x7f0000000100)={0x0, 0xe27, 0x1})
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r3, 0x800c5606, &(0x7f00000001c0)={0x5, 0x558, 0x8000})
ioctl$FIOASYNC(r3, 0xc450443c, &(0x7f0000000240))
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086331)
r0 = open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x4}], 0x1, 0x0)
poll(&(0x7f0000000080)=[{}, {r0, 0x4}], 0x2, 0x0)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000240), 0x20, 0x0)
utimes(0x0, &(0x7f0000001240)={{}, {0x0, 0xfffffffffffffffe}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, &(0x7f0000000040)={0x2, 0x4})
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
open(&(0x7f0000000000)='./file0\x00', 0x60a, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r0, r1)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$setown(r0, 0x6, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x2d}, {0x87}, {0x6, 0x0, 0x0, 0x408}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000580)=[{&(0x7f0000000100)="8336529ae69b6d8b740e5690fa090df16d069d4cb90b6121d1e35cc5fa73b07d47dc240fef6a8387d4711c9bca81489cb872dead686cdf468d307fbe2b5b1bcdbec7657a701c034af43d0e095af945c29a674497047449e0432f2151e6d8f3f643afd2ef089934bf35bc071ccd1f3939f4c1640e3f7780c3a8276060b26dc5dcca050f2bfa33414e8abd33871f09cfc06dd93ace3a7f338e4e636b1c8363afc83f0bfe19262816a8f29c2f3388a6aa2d12f33d0d4c9b10eea7b07e6c", 0xbc}, {&(0x7f00000001c0)='276', 0x3}], 0x2, 0x0)
execve(0x0, 0x0, 0x0)
nanosleep(&(0x7f00000000c0)={0x0, 0x1fffffff}, 0x0)
openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
sysctl$hw(&(0x7f00000002c0)={0x6, 0x5}, 0x2, &(0x7f0000000300)="05b8bd8c", &(0x7f0000000340)=0x4, &(0x7f0000000380), 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x20, 0x0)
fcntl$lock(r0, 0x7, &(0x7f00000000c0))
linkat(0xffffffffffffff9c, 0x0, 0xffffffffffffffff, 0x0, 0x8598a6941819896f)
r0 = socket(0x2, 0x2, 0x0)
ioctl$SPKRTONE(0xffffffffffffffff, 0x80085301, &(0x7f0000000080)={0x1})
setsockopt$inet_opts(r0, 0x0, 0x14, &(0x7f0000000080), 0x4)
preadv(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000040), 0x1b)
open(&(0x7f0000000000)='./file0/file0\x00', 0x0, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
r0 = socket(0x800000018, 0x3, 0x102)
r1 = getpgrp()
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x3, 0x1, 0x1f, 0x8, r1})
r2 = fcntl$getown(r0, 0x5)
r3 = msgget$private(0x0, 0x0)
msgrcv(r3, &(0x7f00000010c0)={0x0, ""/4089}, 0x1001, 0x3, 0x0)
msgsnd(r3, &(0x7f00000004c0)=ANY=[@ANYBLOB="0200000000000000c1873c269a5bfb1783f837a2f38ccb69f4c5dc9497985df45613b863bb462b6a724983da5c5e5b280af022c7430c1c2471a696fd3101e359157bfbc051705e009ecbbd28d95768c44d842099d011ab2cdf82013f892e4ae799bd5988eea138fe9f8243b79a3aa2c1a0f929d37b3e1f6a6495b72684f0adefdbc09a43525733f4febafa1acf7e0fd71e936ca9fd3b66301049a65f"], 0x1e, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r4=>0x0}, &(0x7f0000000040)=0xc)
r5 = getuid()
r6 = getegid()
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000080)={<r7=>0x0}, 0xc)
msgctl$IPC_SET(r3, 0x1, &(0x7f00000000c0)={{0x4, r4, 0x0, r5, r6, 0xed, 0x4}, 0x6f81, 0x3, r7, 0x0, 0xfe, 0xc95e, 0xffffffffffffffff, 0xd672})
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x100, r7)
r8 = socket(0x18, 0x3, 0x0)
connect$unix(r8, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x13}, 0x1c)
fcntl$lock(r8, 0xf, &(0x7f0000000140)={0x2, 0x2, 0x16836135, 0x3fa7, r2})
shutdown(r0, 0x1)
open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
select(0x62, 0x0, &(0x7f0000000000), &(0x7f0000001900), &(0x7f00000000c0))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001780), 0x8461, 0x0)
fcntl$dupfd(r0, 0xa, 0xffffffffffffffff)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000240)={0x0, 0x2, 0xfffffffffffffffe})
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000080)={0x0, 0x1f, 0x7125, 0xffffffab, "91903fffa80a00000000cafb00"})
writev(r0, &(0x7f00000015c0)=[{&(0x7f00000001c0)="cf6845a20652abc5a73e60b3b90700000098ba5f5b79eb69c8399073a888f64fb7952a26dc514957d9bbc90998c4a3907c13c15fdce356233aeaaaf9a3ca7d9a3661a07538e55e7a", 0x48}, {&(0x7f00000000c0)="ba10f1d170de844ca1bd60be085da963fa3d8f8aabfa99fbceadc881afe1ea84427fee40e9a124c08e9d111e2867bb7b1c4f7506e0f924c5ea5597dd9d3a06335f0d58b24838c6d96dfcf6a3b6e5ab4852fb59cdc5e8c806a06fceda0a4c20ae8731c8651cddda248d678c9a8803d26301d25cabadc4691495a6576ca05d07ee7dd9abeeeb70a746f299161ee6e4bd3120b6d4ed671505b9f03d598c58f28de25fb63cf8bf9e2998c7633693d476efe37451bc70f6f30f443a2741bc20c0bac88d8faad18b4f0b460b2b0ca8d8d956ebdc7be57c8547fb53c053b8a894e7cfd5aa13e97da02536", 0xe7}, {&(0x7f0000001680)="f1693eb617119e2811ce34ceb89d1b477726e5f5795c67000dcab18d6ce7c1c3a305e41b187e580f70e4c1bb63b99dd9207f39c9e346b87142a807135442f8e79335bd2d6df393367853d6e19a8ff50ffb2b0774c5113d75d1d63be60aa9bd24cafb48069bd2052e4ac6b370359fc766ffba97ec6d234d6824b5841f563783ebcd36d87160953bc30a3ba682faf71574f29f69db62a69944219c7bc78e3268ee4679c7dc4282f291df34f92aa2ee25d172a701fb4dfeae6c5f74086a8e341161d51f2d0948caa7f82e1a89c69d928da0593932c6dce0e2ecca7d5d5f9a1647062800c3a5472d3d4971376b8f59ee5c760596b242be3609a2ef228ecb1c209ac2c6b3fc6f8ce259f1292f3c155f7ed39836c7d5b0010272e4aaf6eedd97812f19610d89b17fdc6700915e8570165b48b7d6d3fa02e49f43d594db9ec1eea84cd0964e67f66f28b2d741aaeea919a7eebe8fd19571e673169262100d363c147b5ed735bc7b1d4d076707c3904cd7db1a9ba8179d06665adb63d8adc1f1ffbf4ac2fffa8845b4efd22f53a8f8040d9620250f8dc476837fbea37e8658f6f701d80e98b789bf723f43d44b54fb8920417ba0b41fe7c4d3e538d83c2787f58370b40d99784870ca2708fb8c0efb3932f451ef3bf9ac9595e353aacb921c5b23483f510a6dcfd81d49813ff4b9fb360a8901b8ae6ff35dbc66132106a25722644655727bbf87ef9c4a8ffc2fd181450714e038ec5e7b60a61fb6cfe10da38dd2cf74c6a87a02fcf1be23339f73837e88680c7f24a818b4bd27b540567d3b015592e090b329944ebb8c332ba56a86b1ab6c4872071027380925f7e0371ac758f3c337df27cd88ad83859c276197dec5d3585bf285bd2a35d8844d1431b851fc33999fbda18c33b584be6a4b47996924ab9d996b4139c46c8b6d6014d75bbb4d46067eb45acdba316916b536f65567f285ec10d33aa02c4ed0b0b515aa873a0532dd65459cfd258397d813839ef098bc944ad6b3c83065b59318902cb1102d5171b6df1e25146a0d2ad0a65ee67214175238bd32f17349317fb78ea2dc6f81896872784832764b35dfaef8f25600ccfa108917c71a7cbbd4ed4ab6ee70383c107a1976b5581a67c32a4d18e5ea6ad440205f70ce00da41deddd4abedb0fcd21e95d06f3580e84e60e1fb712678c20d94efb208e627656c5285c52e234d3435a2d2462e0e250480b41a62172110733026d7c65c6da6fd7e89aff56a471d4f7475470371bd000e6ea9123f84df73b72867587c5175919b8f1755c894fe27e55b58cdf76e30baa92f33953d45a2bec605d5f434c50d85ef3ea1a580a91746038ca306014be808f71b499cd717bcd83bd312e14d790d5d39038cdd723bec3060e1977cdf0cd6ae517619a56547e4d5a9143be892dbefabe98aee8ce97325e43e680b182a4756ea7a32bc71c28bd9f7d8f8a1cae3132d68193b1f1b1887341addf5506bc597b04062ed86fcf64643fd6c716f05cb09b1af05affda67df8e47706c5f78960b17a3ccfb35f285f149fa7ab33dda897881ea474450ac6f41d77c9cfe471da452484a6ae54089c14c15ef5cf793db7b27e1a6da16426d3d132f82eb30102f64c8d05e8a8fa7047d32809baa1cefdde5383956518c32cd1c780db5caeb87ef9390c049e25ea4ba489f33968ad9707c415697540e1e138588c1e7cbc861ef8992c13eddb0d9cc22642a80aae25c960fb61f3d0a5332df3cee9ec48752a3707ab810b1a9a91ef884a47be838699848c34154b4e0f72bd018705791f3f91f75b42674635bc651bf404411f6a7a4b30eef829fe81f716c9026c26d8356132de2b7e2f29af2a8e9cd2f72e9b2624b29a8a9b41785fe1a517809e8d22f7532bbee325e5e92ec51912dacd4cdcde568c1d182f4692a2ee85013316b602f00e28359c5020dfbb95f0aa24b9bee7737896caf5fbf10d3e01628342535303329631e0f3572d68d734d5648f73ecb71c6202f42162661a8de02987d744f3c1d9068dc063cd4e88047db479a52742eef89d6943a831022034690a786f10d3ac416e9e50694df3248f79c095fc9b70ad2eefedadf5bff599baea5a974cb79189c65a239b10636c1ff3df14dd3f3353c95fff9e919083fcf69cb62aa84450df46e569e1f15183a24605f73fa5523fcca738e54466eb8f0775cc25fb07c175ae59666d07ef37ef43110778126065da84e3b1703444c078f73d87e92e1a7a44331bcc86064a1669a71826b058bfbde5e671f8a16866a67600d4951850089ec34171c01cbf973643769d7c1d214172f6a18ff9c907c9ff80634d47d4d00fc53c40f301506ee635ba92a24da1eee880389997009e08a0e6bf60de17dce4d20b0d6cd02f2228f97d8114e1565d9389af3758926885396b68fa556c2d78c3be3f9a45bab8c881db1dee5687c6e2be131583d232a211e7db071ccc0e9130dee769cecd68dbe2f1f0a3414dfb8163e4cadf20e09a446728207a63e702850ca340ae07b32e260eb81d8bc9900194c2a0754dce4419ae9300e8b667ec1886e6d978fedf5e4864bf043dff17a3f450dac2bd2d031db8088b3667eb99e314b63aa6062f4fc6cc59dba83cf0418312d718d9b765c202b7a1650b927e2cb4f628996e5a46c25f06b18998c3a80dbc3590fb33450de0e2abec0b4dae905f41f89493e623ae99ced7f02ba732482faa1378d571a207f4d76618b3eaf8817733f284f3b96e870d5b91ea2c5cecc5cba98ea431eebea057e94a27bb404029c414b7cfdb5ec79f653949a191bad6bb89d910011d22e778fe81b8324e9277d60ffb958828660066688704c9d4f2db333584a34366946d5b5585e9cbddd712520a5cdbdbabeabc0ed2e1baa0d2288bedc8eceb8039854868f0f6abcc9051614f8de9c46a9f3a1085207a85874ce36a36a98b7c92477cd6136e8c8d38526cfafdf5b15edeaa720e75bf8de3122b2747cb57b8d6f4ca17e53a821826ccf86c38c0ec3720c801c11e084408e72083575c162172baee673ec199136a98ecf5f08453af83895a8b02f5babb3a4919f4fb859dd3704871a6f44cfe327d7f48257068ab406c31fca7f0ece63a0fa4ed1efd0162299059a2dcb376546e7f533353920362c69f3ea6f5398d82e2c9a967e75276849f6191d4f01dd1bd41f53212a95ab9561fd621e5d12ce724b8f28209a035c83168b248367611e63800ff88a3d174d89a93149b802739a07ac726b40cf4d7549467ecb2386f349af36665493fda3d90854334196343f8c5e2adaaae744e6b6f50a514b0a729b34f9457531d976f28eb8602b81dac702ee5a4e1d7686f2942103c4bf033c1c375512e5559d14b3a4ba1aab35a3de2970bc5ededb13e358515f70251edcce268760fec4c5eb77b216b0ec4023af9a1c7e03da0f75bcf57f033ef9cf7a4d3641c4830f8d87342d5505be61e80e8278317ad239bc5ffd65af16260a92e52444ad55e55aee9f2b109f8b07c2e171fb8f21136fa39b47430ddcc173b111efb2f3ada3a466e658db6b5b495e2d5c598f524bb14cc6a8b19e65ebadca3e900324ef7567333dcfc1f0a36f498b640645a373cefe599f49392eab400bd3aa34422b8cd6fe6d9f863114d6f4c350019a91ebc8e9736d4ff2caf07daf9b0f8195f543636862b70c46366ddfd06c5417f4b3a9cd90d2919b81b5c1cf1894a5b200ba6fb8441dd8a3b29cba246463c951b9826cbdfab7f8324100ffaa373c63969979edb7668e073b6e3997dce87e76585eb15586c1d78fc2f66d2e5045e8ca2b8507ecc411d9df4ab2d849422c4eb31e54e55827ed62bbeef85bb866dfc97273ed757dcf29afde8a5b4ebcccfc629224ce14f1a460bf46799b7d112346155c69ea1d10d1f31d2d00da34c6026cd516aeb94a856bb3cd0f501a8aea7aab03a33b6460e099c190713ffe80a610bce2287e355312ff6e7425a6eeba00c2934f6fbe0d0befd13ef65390774e7c63e470420004282c9ae07fd68bbc10c7834c2c3b9a07a3af2adac68f315f55fc2b035b30718f0813a07636e5029b6e66f7d32cff7aa32947013eaf4621ab8a300ee6661e5ef34a72623bde1575161ddd7971d9bd1fd074b5f3a134573ea89594fd5e0044c7d9eb1021f6f2dc22161920dcb3af7370a9a9cbd811f6d0d5483846a542c4a12a4706d2b3c700341d58395f4be65d5277cd5811646f4285a13495707c1b99d41da98143a74fd60ebd656f22151e5be2c733aae3183bd1ae0b4f76e4c34a07019c9d89a72d5ff6e72c86973a4515abdee618f680c73046dc8cd75ea83f7069658b39e6e92214a3ca3d8ffbf64526e2eccb6", 0xbfc}], 0x3)
writev(r0, &(0x7f0000000880)=[{&(0x7f0000002700)="4f2f5e9a919effcea996eac2eac95d497017e4d734cc16f60e429ca990ae7b6e77d30ed14182158a2b055e53a61582ff8ccc240c28b7aa98f704fbd5e1afd55c7916ace84d794b838d7a39627f1d2369d3986340aca3c272670895dc007cbf5c87cc3d2a1baba20b14dd938d9980e1d3afecdf01e0ba486cde2af4261943129cea51450e6ed592097e0ffa75bd493ec4dfc4332daf7a5c3ba953042f24123ccd606d65db46e1caaf1b94df8274c37908bced2457a8c61b2301bf2104b02bee5dfd8f548e9d0c92f2c44e8387d98e02cc8473b21aaf7e1aa2e54df0d70a80670c0da69b1c7e2d69348e987b25a88cb0e53ef4f788cfc7ae5d5475b75661359f3338c816dce0b3d2e7d2071068f551527805cdf66e1eb5713aee25c37055c9b396175f10555c6f229141c22068decc344653238ec8c6663b8db79e06f6c6316c3f88d4d9947494d6619c8fc1c0b9be8a5e4d6759b6e9a731186d395043c779eb3d50c2eaddc8c7fb492f0009326276e3af899bc28f4b36082551a622545b50881ef9f337e43cd648cf7ca3518a3d6673e8b5a0722a66257426fa2dcc6f7e684f97268c3263d320521137e8b52cdb94947bef06a9a241821e65be60cbc07c6841e7b0c7dc839f300ee3335c1909a453408073a709b1c07074b98e718fb98eb954da3105faffafab983b1d3869e90600f46b080d823af9c168d339c73f012fab6e44dd91c847f7303bc569b6ca108d37dfa2252813a983272dec65a1901c3dbbaa27c87c86606845a7d7fdbf99df82567f02d210c8b90c54399c7ac79ef67c0728f0403acaa19178b4d500864e594c3646d5e6ec654f30d5b0c3ef318dc2458845d98dac02ea54e975dbae58fb3b0c25c25c5534991faadebe098ccba999e1dee45a83986d041261c0342ab0f9c869dd5015ea1dff609ce76f40d37879333a12c2ecfe6288bfa4b1a901a0348f4b8da2d1222f8acddf079593ba1accc5b5a99af11b482d30c44ee48e324251ed65405d3bf1d38fc9bd3de79ded0e2782324cb7ef2ad2b8b67787537af3c989e74c02cc7d349e6432aa790be7428e22678dfb43cd55d96ee9c91cc4718467194ab3c5a8b42b3202e9710e12533c858bba7a3b7c94dd317ab6b0b6151af23ae82969ab3f872f9a7b9635de82e78d25ee96a360e9638da96e2d46e67588c834f6ed47822cec66e537c2996940d6094ab8dcb08feb6a7f9dfdc9450202bd83ca0099821497014ba6831b219fc623630071c95105bfbcdabc6589f68ecd7000cbac3487661525387e1161336a4dee78d231a6c0fad51dd9eac4b273f572480bd0943932f1c470105458187267d5ca173bf17857c479df22e51a6896a602dfc2ba24ff282c8c497bb60abc7bfa5609dfd865a649b6c97c34e40b74059486b61a9160b2c098bf9fa5ca44e21c9cfb79752195f966b55163a4c1d0c806cacc1d1e2b434e42218fbebd5b82112a4c3a2930e433d9c61b068f65f20f8589a11df686999e945fce7cfc08843b2b77212acfa863c9a1b7230019b934947b46e177bd05c58ed79da354556b9a2ba827084f48c28f74f71cf2f071caf8ad7fb9c9a40640bf14c969e86f8d8fa8b0a64c6d2fee22245b1dd63ebebec9c82de38366479d212399514ba884c28eea841e964ac769eac769a2b08e8e06657248b2f2ac1f22eefe218bf3f641ab04c54f13e249a536383c5d577ebb379dfdbd56436d5bdcb0786f372c847d87d535567e391375650c6b8e63bde00611e7cc13240fcedcbb8a3490d22d0f2707df6751e36b11ec03c78a920b93ecd25bac9e07530386cd45fd1aa9b1e186088773932e8fafd09c5e8638ebf0f9f7dd13ab06ea8c465f0fda6d6c82d2981a2457f863f4bbccc3703e0a05ad1cd1e117bda6cec03ab2b2cb9c1e98324a16a483a035b81e673ff452c8b94c272d1bd3e070aa2ed983f4c4eb7ad3b0dbd927bcc5a71ca3cce11e8dcc1ce18bcc7a8f05e57d4cbf095e615c618722280b4ec5c816c6ed58455f63c2054dd4c310b93cb1350b126bfc17cb050cd38f9ee0b5b024dd7bf5ec9d34e21c3282296ca7dad11ade61c048018e9515e407280038d6e06b16bc824481d877e3337f40b0ea388831443961929ae074c7e0e565d817c3eb23f62c4965de7557791ccb335d65369228e545e0c92c04576184350e971fcb3e84b0436c00f61fc56b5c975d390c2108a6ffe8a958ad962556d37b21405cea182684268e6211a70841b518ba46d1281b4fe4a08bdc6e69c1604927589c6660dc6aa0b7627113a3574fae44113f2e2bb0cbf5e645f688e76d09c170e7e59076703d639f2e05b0eaae40ff14aab7e4b59f43c3540c0eb2516ac0aac3a37a2961da5d81e285d1fa35d68387313086f2ac05e8c82f8021edc0a5443ac3539a51e856df068a77ac97a53ed35455951f12d8d2a3154e45ed991ceeacc039c3e8807044b9896cf069f9a8da0245d32a4c5b55ad1e29d4864a71655cde2ad1a0634a3c8430b607c84c17b977bf8f49cf2d41ba737ab1fd91bcad916703993eb9317ef02a89699049a61840e6a006ba6d4bf4764af9bb4d4129573a8c21480d10cc7139a6862dd51d00d7e7a99bbcdfe7afa419cb1f376bf3a2862176e5fd404d4a18ff384356c308ddbfda7aae6311b2eba8f94cafc54c556ac4172a2495ac5fbbf00fb2b952697a738638b7096848a8d2d68a6e4d500ea61891f74abe1b6d802c8a7dd0c39abbb801ca7f14dbfeb5c85097fa42f11b27b1460cade8a11fa5ca63d108518989fde79e882cb5f70cf4107ac76dbb3750b0b850308b6a294d280671f93a94974b31f211cfb8aabac9a2c48f6210752a37d52cfac75b053c8a68fc359a201ec3e5162dcbaad428612bff94b11d86edb93cc872c463e2bf83921fe7b0e2f2df51f5a7e92ef090ad9a86c2ab610c500b09ea2ad94b34ad56cba82ebaa621d4e260b63e91c202cf1dbfa088befb04fa6bebbf8f67b8e14caf8a8021b7ff73903dc14b81d0d217dd9b92b0309db281aa271d88368c35e1163bfee372a8d1b6e67efcf2324d4c898a8286bcf2e66bdcf8b5b1a1fbc2270aac8f8a85898905bdeeb0f273df71b8c09a39ffd7d5b45155a5ccb4642597266b8cdf5e0357522eb07365e2a87d98c4e963fd97e631e4f88f82d44670cef47085f6367768f4d9a1f037ab1ed67eb6693bdfcc693ef7e0ea27987d58ec5740a4633a78547d6c1deef03d6df70cbca727309ede8c6ebbeeeee2a009e6f365381a8c4f1962257e99e6997465f444b3750a6aa2b513d8283ffa88b596f4a03b94824bde3c6ed34aab2cd2500a9576da93d3f30fb09b9fd27b16fccef30b343c8903275ead489141ec0837892b21f287dc7e71580b3ed3504ff9b9e70aefbfdd3d7e637fe134d06a621ca3ab3490aed1dca575c8b741b851bd9d4801e03a4a335ca48616abca93cdbe57a5837fe386bffc9a0fb4474b696c392e1f890ee85d6781f7d9367f3640241fcd508de822786d035701e53a4370b542fa28fd222b2ffde85ddb1388924345ecd408413b26b0ea417ba93d009ebf10c7af2b5f84fc2d7d974861859de7d7bf246a67f75c1813a4590b237815770a45bdb4e9332e881ebe02b3d5183ffd32391fbff7332992e4db04c1474147265636f9615454abb5a5fd04a695d4cc033cdf9a38341d4ce226924d633bf599051098da0d3c487a416ba2d55b2ef94c3b039b0716371a92d822620833d850c0d94725f2098e254267ea654c406030dc9e5ebb0031ec98380096a80c2e0e111a68f77ccb6e6caeb41ebb871d1153cb935f7073f7de722a619679dcf65e74a4d64c5351c8c351106e3f73ee77dde7be3ec86d216bacbe5c4b5a6a2e3999d574c82f67ddd6a07878aa715904a2e46e3ce41eb51987f9e0a7f9155d7c4a71e85e9a44852ab1c9cdbef2dc2273114c2739c885284815849edb51f8db9e3557e29a8574bd8b11791033abc03eabcbc49703d619ce5c746e284e15d606b783eb16d2af252f1632ae41cb7a9960d94c8ab77eb8af1a30d7ea99ff7382345c664085623e0bfa1cc9c1d0b27539396737b09eb34a16712d86cb4aaa3f87fd12ca06f90d86bbf6f189e9b74001853d5a7d9483f1b2e658e10c8c7ef41a861a606fb7e21644b5c79db689e7828dd93c2b2a570d4b7fedb2a2a89ebf76442af94f5b112103673332ec8af4ad663696d302df1dbd21ec678e3f5a004c7c3161c64f6ce935f2fd1218bc1a2493c94b0a37a7ac9d18b7a89999c9f97a4bac09584235aa18ac257e9d7072b6d0fac0e14348ba21d18894282e124481564c4ca2799b1f0d21006f8431f26177350eadc4af605d960e8e61d1c598dc586206f856242e4102cdaad24646cf10569c6b4a7945d4ee155fff9e65f36b1beb3aef7cf22a84a254f5c7b81c78c0e1df02491efba100cba4c8c6234a16ca1f8631b82cca89de93725b28b32b555c2fe16ffe79335e4600c59f1e390bb8f415b1801de0d2e660421cc4d5cb17091c524360e029a9c0841f06058baf19809461d5eaa63465b46b898e664da65190868cb0e4e63ab7afcdceaa890a", 0xca1}], 0x1)
write(r0, &(0x7f00000033c0)="6cf04ae7f7da7a3d732bc8ffa77050a986ee5a045ce6489140f82d75670ed64b99a731bf7880d484d9f714e6a51aedd60128090d23c89d1445d6f0db559aee8192a2bc49fe2a1f68b49c4c5bed8d34feb7b518ab4849f3034323478133f81f69910f603bfab2fd1bd15273d9d58638cd81a64ef8bbcbf331fc425e0810cfd897ea9b950cab78fe51dd82ee5f7198fbb3fa6f1a0ea6312ac0333523f7f2e9885ccabf1e0b6991e99ca2eec6df625977619be63058f98026dc04787120230ea0dc47e184d586df0c882218c350449e0b5766bb5337308a887a61c9ecb15073e93ae1074ae251063ed0f2cfdb778665829a6ba85670d6133f36d6216dcf8c31111c353ca35518d46071475841749be55de27bea4850ba3aa8f802551aac054e5affda68f77fdf4b7253a68dedde5664c0811251ea46e5c30d03418c7380b634894109fff6b1ba0460d1f9f46799f5d99d9bc64878a34439021b5884b68640795ff58d9da3262f3fc79882f5df62a022f67891fa3a585f3b039b77ca836a2672bc6c9d660566cbc213f9980cedd705f965efd7947a8af3a343c943c1acd12b15ecde76444df2bdd637865aabe08056a0e6380d5683c1e446bd7663797af41b6c9709a2cbae1216c092ad885acc3383c4c3a762b1b250dc6d9663b1d089d3fda9f00fa3f15c3138cf1a08b94f0e8a906f7f7599002a49283b2d63bfd360ebbb28e26abf01fe4c7773483be462e86d5ae4fda65c97619800ceee3f8fe2dbe4ef994c9d827b86ae7e63a119d4fceb025d3ce23bcc4c99f0768ff9615115219c6c165f9430c3cf4d5c3e9d4515b76b3e9b00cbf9efe8201766bc8e6644e0f32e96c034098b1030707ebe77754620845ef69022e6e3e7a2000c77d10890af6b4671766d9020c3b9b2d933c0dd938bb028851449bd94a674292e69d658d088e74271f37bf79193dd78b4130cf766901c995e5f14a3948ff0963cb0da7baaa22d1379760a77486d917e73e1618fc9d6534f7b533919b0f1c46ade0af18ef0e662dc025b5cad42f7bd5ec8dbbe73e69f2ffa6cf95cea7ebe00026263904e3edd46017318cc4a35b80c93db12d7cf5186b2c34532897e50f059f8cd9e504cb455830b7cfac04487e06de8780c56115853331f82fbf1b258d5b2b63906dd02e3c0ae3b754776c342172eebf7b55feb302d8da702efb0cb3c8633a5619b7574668287f76fad1d0135933d38ecc62dd5be86548f14f3a2aff3d461eaaa26b779cb61cd8aad508ffc4edb67814b7fcb78967e8ec2ab31f880e10a0a", 0x393)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x48}, {0x1c}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000100)=ANY=[])
sysctl$vm(&(0x7f0000000080)={0x6, 0x1}, 0x2, 0x0, 0x0, 0x0, 0x0)
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff})
getdents(r0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x4, 0xb3, "00730988a9602e959e5c5bfe008000"})
sysctl$kern(&(0x7f0000000000)={0x1, 0x27}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d770c167e400009e00"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "dee900000100"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x23}, 0x2, &(0x7f0000000200)="d81828aed3f72b79947c2c08b17a0d8c796022553491f9824319d92d9bd54da1a6307d894175cfb10e92f2295bf850f83ea4f583ccf166c6beda881305ebe6de22f76fca2933d6a3be4d710ff74363077a226614e3ac4dc938ef4ec070f7203f6688c1b406f6ca2f5a746d6bcfba4877ef69b896a6566e1174e311636b28f8363e14f1b3b64dd39238e134f07e7b62f98183cd5d0865fc5841968a3a1e7cdafd2297c20aa93e6d407bc3509c6fe42adc707f5a5e408526494093674010e17ac4a8202e373d386792c22ea4b1148baeb4d30000", &(0x7f0000000300)=0xd3, &(0x7f0000000580)="93537391c2b31347fbccf755bc3bdef07e4a60f20b2f0d901fdff53dc21b4350f7bf639a35bc8b6cde688a7ebf2362fe0a2aca5e3e49ca39799a10b87ed7ba121205ad6a4e4e6d7eaf461b440df18d40afa33b16aacb3f99077ed595eda6f0ed025110c604abada9fffffffe000000007af52c22473c6e0043e1f96b92c0ac0307e6ef170ff8bec6e30f445fa912f023ff405cf91b10162d388899056141f26a4a937d3270a4d23a83a3e3be3c2a87c7bcd2eb4206b65415d9567e5aee9bc0b05adb430f139c35eae0d5287285b17c763467f039255c208d05fba3cec72384f9d0780e53884b", 0xe6)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
setuid(0xee01)
stat(&(0x7f0000000040)='./file0\x00', 0xfffffffffffffffe)
semctl$GETVAL(0x0, 0x1, 0x5, &(0x7f0000000400)=""/149)
r0 = semget$private(0x0, 0x5, 0x288)
bind$unix(0xffffffffffffff9c, &(0x7f0000000140)=@file={0x0, './file0\x00'}, 0xa)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000003c0)=[0x8000])
semop(r0, &(0x7f00000004c0)=[{0xe, 0x5022, 0x800}, {0x1, 0x7, 0x3000}, {0x0, 0x4, 0x1000}, {0x1, 0x8, 0x800}, {0x1, 0x3, 0x800}, {0x0, 0x0, 0x1000}], 0x6)
semop(r0, 0xffffffffffffffff, 0x4)
semctl$IPC_STAT(r0, 0x0, 0x2, &(0x7f0000000500)=""/12)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
lchown(&(0x7f0000000380)='./file0\x00', 0x0, 0x0)
getegid()
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000000c0)=[0x8000])
open$dir(&(0x7f00000001c0)='./file0\x00', 0x8000, 0x22)
getuid()
semctl$IPC_RMID(0x0, 0x0, 0x0)
semget$private(0x0, 0x1, 0xb)
msgget(0x3, 0x0)
syz_emit_ethernet(0x38, &(0x7f00000000c0)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "51dc5d", 0x2, 0x0, 0x0, @rand_addr="b6678ae985a2648ba21a9acfff376718", @remote={0xfe, 0x80, '\x00', 0x0}, {[], @generic='3@'}}}}})
r0 = syz_open_pts()
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x3, 0x0, 0x100004000})
flock(r0, 0x5)
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000200))
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt(r0, 0x6, 0x1, &(0x7f0000000140)="6768b6dc", 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f0000000040)="d26a0000000a00", 0xfe26, 0x401, 0x0, 0xfffffffffffffe00)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x20e)
open(&(0x7f0000000400)='./file0\x00', 0x1, 0x0)
sysctl$hw(&(0x7f0000000100)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x34259b81af16d0c5)
sysctl$hw(&(0x7f0000000140)={0x6, 0xc}, 0x1, &(0x7f0000000040)="0928c56c9c5522a08e5036de8fd50151847c65b1404a37fc93be6736487fcc7e7955bea82c", &(0x7f0000000080)=0x25, &(0x7f0000000200)="b336a93f4e5cb63b21893889312a6a1a80565780ca283fd57f8be4088bfccdda57f1c9a2dcae11e4c0", 0x29)
sysctl$hw(&(0x7f0000000680)={0x6, 0xc}, 0x2, &(0x7f0000000640)="5c00000000000000e70b6576642ad2f8267edd32cdc1ca17828531b314000000a09e40d27289b471481e358044073e30c8774b2f99a66855296ce0", &(0x7f00000001c0)=0x3b, &(0x7f0000000880)="0fae2caeb8f1d261160d6df681e372f5d8475db4810b06b9fe7619b6ad1ce27b07d2eafba1b65486d4bc14e842ab8aea8062482399f350b453c65cfb4098364d9a4665518176e495b3b3fc14543fbf814e05fc504c56ac214a95043ec04e854a990401000000b1b536aefce062904c3a54f1b9645baade9763ac2b692921f8693d55f476d5b2402433e5146ab2139e8e889861247531f21291c052f5c223e15c8d1417dc5f1a6337ec41cd30e196c6a0e4a440", 0xb3)
sysctl$hw(&(0x7f00000002c0)={0x6, 0x18}, 0x2, &(0x7f0000000740)="9b5766f6f47c76fbdd1ad855e63b79673cd46cad7fa0b8f911b36fc24abea3c5d900a3247f20fbf7862f7032dd0799407f21c8def60aa0a3f1cb24f29c63514aac3b0dc0bfd7be3dfdd73eb43db59e873d0226ca55bff12b9da69787f81eebb44fbd649741c60f87202bbf3b9b54b3305952af16aa24ac8849f6", &(0x7f0000000380)=0x7a, &(0x7f00000003c0)="06290484556fd81826f39131a4b6fec49fe3aacc8a02c61503c3a335408164e060feee61f2e8b99753a1d34b53367551c6ad727632b57fc5321f41a8f19f9fdd55e6c64490ae2b9e4912124601f8", 0x4e)
sysctl$hw(&(0x7f00000004c0)={0x6, 0xf}, 0x2, &(0x7f0000000500)="fb85534335e54620249c8aedb467509c4de67009d0735852108771e5032a4ab6a96aaec8f39e49ef0c9cd166d996a955dbed24d4798f8181fa77720af305ab2ae7c718c6f247b123f5", &(0x7f0000000580)=0x49, &(0x7f00000005c0)="79a7e4411dd5ad9ed7f6d5eab51c13d6a39d72a418b6eaf7e446862aa48c096db7caebcc5f798592296784349009e3db98cf7f4c65981ab1943ab846a6edd72dedc9a0f74d085837b88b81fc041c4175229dc8d66891bb6b0823e9c41a84d973", 0x60)
sysctl$hw(&(0x7f0000000000)={0x6, 0x10}, 0x2, &(0x7f00000006c0)="e8edf848de0217660590710c289d89320e7cecd195fc290f5e662b99aca6f02db40c55ca2181cca355baa86056ce0ba83fa51c403c3f0f", &(0x7f0000000180)=0x37, &(0x7f00000007c0)="04007a21f435398e63c51f41006a95f5faf11cf67f12547978b656fbccb1b6cbd4ca4b907bba3854c913ba68554e79c83e4dd4375a0393dde30bcb32ff8a3f8a8f37af1267315a56371d46098deef004c873791ee631bb7b42", 0x59)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000840), 0x100, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x2000, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x1, 0x800, 0x0, 0x40000000000, 0x0, 0x6, 0x8ddb, 0x800000000004, 0x0, 0x0, 0x0, 0x8]}})
ioctl$WSDISPLAYIO_GETSCREEN(0xffffffffffffffff, 0xc0245755, 0x0)
socket(0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
select(0x0, 0x0, 0x0, 0x0, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x14)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0205609, &(0x7f0000000000)={0x1, 0x0, 0x2b})
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x5300)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x101}], 0x1, 0x0)
close(r0)
getgroups(0x7, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000200)=[{}, {}, {{}, 0x0, 0x0, 0xf0000006}], 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x5)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000240)=[{0x48}, {0x5}, {0x6, 0x0, 0x0, 0x20b}]})
pwrite(r0, &(0x7f0000000040)="e81af191c7a750ff690700317e37", 0x60, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$VMM_IOC_WRITEREGS(r1, 0x82485608, &(0x7f00000001c0))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0x0, 0x7fffffff, 0x2948, 0xffffffad, "f40000f20000010021810a00f3d7000000002000"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000400)="c636fdd7152565d82d85addf569a0ee913fd180e4e7e3a23650aaa0919cd875e0ec9d64353c94625a83d62d2f17251ea623c94b352476bc0141d4d6ee15aa1c62ba9fa1289396338e20d9e69ff4ec8b6632341ce8f723c09fd97fe763a987734c012973ce34be4771eef9f17d27bd440059055ce1f1ca14c66cad8214e10b429cf6fbf0d718d7a5bb2bdf673f0151f9ebba1ae457f1e07c769097001ece7c8bb09003555fcd4388d8f5d89f60b0e8f47d31e595a8f37385efaa482d507537e55cff48b6889cc055bbe779c2c6d0ebf956affd64eaa379802cf27b5a72f2b47cc49776df5f12e3e3765a9d3b9ff0fb087fe4dd549c4670df2dfa67b07edcb5f6da26332c6e734ecd93ced31aa8bb2ff2e73a5a95670bab23c3930e26de7a6a4b15218288722066610d6bedfed1beffe4d414626a425f64a5f43faa3157e0d0a2f98d315f8812f5e53f7513b9cf56b02824e1ec34e6e3ddb8a368d2985d1e7c66b4275556cdb08b9a21033e58fcdcaf0cb49cc260cb005b91885ce8ba4e6765ac1189f44610ab926fde7c45fea2123844a3d4caa9bc30f171372247e5ab161dae0b5f090ad84328e1970b93a21f7f4178eba8fe0e1f491c682306dd55e6dbe642a4f03750d46f46849fde58a4201502d360bb1f4fae0c8de68a0b0f89130bcebb87af44572048cb7c6c3465997b0800f4cf9ed1a1778c7416e0b05e0b944ee0f663a64902729c9cf56dd12f01a249a8be9ec84b8f93f56ad17d7cb0ad33304075833bb5f5fc2b508ac9dbe40b813d6b09bc6d69c400fed24ed68b3740e9e12604d6ca73d05b50ae4229d4cc8567a77affae49b05d2811852de045fef2e7ba1a2c8b94cf5d874d316080fe36d7f20e0d1d9695a83a3eb152b1eadba3dff5d5428ff951f0f79d5f62a6155e8a17c173761bdfead5396a8767a3b1b35324044c3e9aff4385f34d42748fa21108d7da87a173e04f7bf201fc13b8bb06f2f74b96f29d8a052ccc42dd908e471cf84a8d941ff6abaa5d19529e741ce14d607d953b67afda5cbbe66885b2391259724e8a64f5aa75a2939c4900c5421d03447467f793b184c2f4316ecb155d0cc95fcecc8d335e38494bf205f6227cafb60e7f86da2f05bde78ed42c644b16b8f74f778e1f39a7257090a0a975ab7d7d352656fe35aa3fa1233cb8d6f4e258a963c6c731e4f9a5e9a41f0026cc3fcc4c91954b521dae4515d1661ec066df18fcc350a7be17bd0166d62a3669327104f39f1515426c49124f291e2a6aa512501dd8ca758ef5ba26a46bbc334701f566e5bf9fd208a2167f99013a6d4c607c9718c57d228ac464538ceb786cd72afb17e874a965f656fe55ab459639a28d8644bbf96fffd3eaa3b72a4c65e7c", 0x3d4}], 0x1)
writev(r0, &(0x7f0000000140)=[{&(0x7f0000002900)="581cf9deba9cef7c62447513f119110666719fe76d11e85d603d0c3ab639eadcbe7e8ce99c0e7b2cbc4d462468fd09a69df80142f21436e5178a5bdaa362b7ea79c897d26e46ff964d65949507fee3e07ff759f13bf9c0f3b285ff8cbf5bdaceed6e53f52048223ab55477ca23ddf25b304cf4b5a76ace52cb87c6f1439a3836444953a086c99e2409f0035b92244aaefc953b0852bf92a1a2188005c44b0608079f6dd86cd30e3d01e5c9d633c3e4d08e01e89f801d964543d52257de21dc1c4163849428870135969114621f3cfff6ce1c099878e1242f1dffe9d72190cd42fc892b163bd585c3838ab7160a", 0xed}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x81}, {0x6c}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
sysctl$hw(&(0x7f0000000000)={0x7, 0x10}, 0x2, &(0x7f00000000c0), 0x0, 0x0, 0x0)
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
mquery(&(0x7f0000ff4000/0xc000)=nil, 0xc000, 0x0, 0x0, 0xffffffffffffff9c, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x87}, {0x44}, {0xeffe}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)=ANY=[])
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000140)={0x0, 0x0, 0xfffffffe, 0x0, "f5016fca98135a03066e183e449aea14000000ee"})
symlink(&(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000000)='./file0\x00')
readlink(&(0x7f0000000040)='./file0\x00', &(0x7f0000000380)=""/139, 0x8b)
shmctl$IPC_STAT(0xffffffffffffffff, 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
writev(0xffffffffffffffff, &(0x7f00000001c0)=[{&(0x7f0000000200)="1689395293c2f848615ffde0cad2561dbc4e355e78111ab9359a91db1accb1577aec10193b5de37c569f16b39eea9087882ac27e3f076c8f970dd01ccfa3cde38cbfa3a292a4acce535bfd5dd02e5cc544f8642d14acda0f9877cc9291118dddda4d7bfa84123c3608a663c104560ab4334b2e743f91da8477ddd54ecc9cc1a37d1f97e626613910cf2b34b4f113a8852fc5f4e211ae637e13dca5bb8069065f9b5850ef3a4824d8c599a2438021fbd92999204b1f8fe49e1c00f9a975f1b9807536494f26edf29fce29d849cb0766786496ae9e84b9df403699409ad189b26591e1e44e5e77dd1bb0dfd6d6d99c1f03a772a3df395263fef51141135dd16312c1cdd778903c0166eae7e66a0f4acd1c07ed3e8324faca82574008e2552834df3a20abd39c8f6d66c94254b6582e96f4f15eca9fb3", 0x135}], 0x1)
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x4001, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8040691a, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000040)={0x1, 0x53}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc450443c, &(0x7f0000000740)={0x0, 0x0, 0x0})
sysctl$vm(&(0x7f0000000000)={0x7, 0x5}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000001c80), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{{}, 0x0, 0x0, 0x4, 0x1}], 0x0, 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1806", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
sysctl$vfs_fuse(&(0x7f0000000000)={0xa, 0x12, 0x1}, 0x3, 0x0, 0x0, &(0x7f0000000180), 0x0)
semop(0x0, &(0x7f0000000000)=[{}, {}, {0x2}], 0x3)
ioctl$TIOCSETAF(0xffffffffffffff9c, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000100)={0x1, &(0x7f0000000000)=[{}]})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x400000000018, 0x3, 0x0)
setsockopt(r0, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffff9, {[0x1, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x3, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x2fcd, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfefffffffffffffb, 0xa6f, 0x2, 0x80000, 0x5314, 0x20000000ffffffff, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x6, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x7}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0xff, 0x1, 0x203}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x4, {0x8000000000000001, 0x3}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0x3, 0x0, 0x0, 0x4], [0x0, 0x0, 0x0, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000140)=0x1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000000)={0x1, 0x49}, 0x5, 0x0, 0x0, 0x0, 0x0)
munmap(&(0x7f0000200000/0x1000)=nil, 0x1000)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r1, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x1}, {0x87}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000300)="aa420059027c71fc6a430929c088", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x4}]}}})
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0xd26462bf0b56ad23}], 0x1, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='x\x00')
symlinkat(&(0x7f0000000100)='./file0\x00', 0xffffffffffffff9c, &(0x7f0000000140)='./file1\x00')
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000002c0)={0x0, 0x4543, 0x0, 0xfffffffe, "dbe7575058482380d177cbeee17312636101659b"})
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
seteuid(0xffffffffffffffff)
setreuid(0x0, 0xffffffffffffffff)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r1=>0x0}, &(0x7f0000000100)=0x1)
setuid(r1)
r2 = socket(0x2, 0x2, 0x0)
r3 = socket(0x2, 0x1, 0x0)
setsockopt(r3, 0x0, 0x13, &(0x7f0000000080)="02000000", 0x4)
dup2(r3, r2)
connect$unix(r2, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setitimer(0x0, &(0x7f00000000c0)={{}, {0x0, 0x7fff}}, 0x0)
getitimer(0x0, &(0x7f0000000140))
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r1=>0x0}, &(0x7f0000000100)=0x1)
setuid(r1)
r2 = socket(0x2, 0x2, 0x0)
r3 = socket(0x2, 0x8001, 0x0)
setsockopt(r3, 0x0, 0x13, &(0x7f0000000080)="02000000", 0x4)
r4 = dup2(r3, r2)
listen(r4, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, 0xffffffffffffffff})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fa090000001e328a1811", 0xfffffffffffffcb5}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000540), 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
pipe(&(0x7f00000002c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000100)={&(0x7f00000000c0)='./file0\x00', r1})
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000501000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282921", 0x62, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503000000000000000001", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xd0584fcd7f7abe72)
accept(0xffffffffffffff9c, &(0x7f0000000000)=@in, &(0x7f0000000040)=0xc)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140)={0x2f})
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f00000000c0)={'./file0\x00', 0xd2, 0x8, 0x1})
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt(r1, 0x0, 0x9, &(0x7f0000000040), 0x0)
fcntl$lock(0xffffffffffffffff, 0x8, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x8000000000000001})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x1}, {0x50}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000180)="201d8eb323295428e2fa906bdf4b", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x4c}, {0x87}, {0x6, 0x0, 0x0, 0xfffffffe}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000280)="270653bdadf26ad4e1861c60ed16", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0xa, &(0x7f00000001c0), 0x0)
r0 = socket(0x2, 0x3, 0x0)
r1 = socket(0x1, 0x5, 0x0)
close(r1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cffffffff"], 0x1)
socket(0x2, 0x3, 0x0)
connect(r1, &(0x7f0000000000)=ANY=[], 0x10)
writev(r1, &(0x7f0000001740)=[{0x0}], 0x1)
shutdown(r0, 0x2)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x2, 0x0, 0xfffffffffffffffe, 0x200030000ffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x8, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206982, &(0x7f0000000300))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000180)={&(0x7f0000000000)=[{0x24}, {0x8, 0x5}, {}], 0x3})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x45}, {0xc}, {0x6, 0x0, 0x0, 0x20000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000003, 0xffffffffffffffff, 0xfeffffff, 0x0, 0x0, 0x0, 0x40]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x22}], 0x1})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0xc0}, {0x3d}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
mmap(&(0x7f0000ff9000/0x4000)=nil, 0x1b10320000, 0x0, 0x11, r0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000002c0)='.\x00', 0x0, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
symlinkat(&(0x7f0000000080)='/\x00', r1, &(0x7f00000000c0)='./file0\x00')
rmdir(&(0x7f0000000140)='./file0/file0\x00')
mkdir(&(0x7f0000000040)='./file0/file0\x00', 0x63)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7006, &(0x7f0000000040)={{0x0, 0xc6}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc080445b, &(0x7f0000000740)={0x0, 0x0, 0x0, 0x5})
r0 = semget$private(0x0, 0x7, 0x10e)
semop(r0, &(0x7f0000000740)=[{0x0, 0xffff, 0x1000}, {0x3, 0x800, 0x400}, {0x4, 0x2, 0x800}, {0x3, 0x7d}, {0x2, 0x29a9, 0x800}, {0x1, 0x2657, 0x800}, {0x2, 0x9, 0x1800}, {0x0, 0x3f, 0x800}, {0x4, 0x2, 0xc00}], 0x9)
semctl$SETVAL(r0, 0x0, 0x8, &(0x7f0000000380))
semctl$GETZCNT(r0, 0x1, 0x7, &(0x7f0000000580)=""/224)
semop(r0, &(0x7f0000000000)=[{0x1, 0x1000, 0x1000}, {0x2, 0x8, 0x1000}, {0x2, 0x3b, 0x800}, {0x3, 0x7f, 0x1000}, {0x2, 0x800}, {0x3, 0x0, 0x800}], 0x6)
r1 = shmget$private(0x0, 0x4000, 0x8, &(0x7f000035b000/0x4000)=nil)
r2 = semget$private(0x0, 0x1, 0xe)
semop(r2, &(0x7f0000000740)=[{0x0, 0xffff, 0x1000}, {0x4, 0x800, 0x400}, {0x4, 0x0, 0x800}, {0x3, 0x80, 0x1000}, {0x2, 0x29a9, 0x800}, {0x1, 0x2657, 0x800}, {0x2, 0x9, 0x1800}, {0x0, 0x3f, 0x1000}, {0x0, 0x3, 0xc00}], 0x9)
semctl$SETVAL(r2, 0x0, 0x8, &(0x7f0000000380))
semctl$GETZCNT(0x0, 0x1, 0x7, &(0x7f0000000580)=""/224)
semctl$IPC_STAT(r2, 0x0, 0x2, &(0x7f0000000780)=""/4096)
shmat(r1, &(0x7f0000ffc000/0x3000)=nil, 0x3000)
r3 = getgid()
setregid(r3, 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, <r4=>0x0}, 0xc)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000080), 0xc)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000000c0)={{0xffffc792, 0xffffffffffffffff, 0x0, r4, 0x0, 0xc9, 0xc9e3}, 0x45e6b0d5, 0x5, 0x805})
r5 = shmget$private(0x0, 0x4000, 0x8, &(0x7f000035b000/0x4000)=nil)
shmat(r5, &(0x7f0000ffc000/0x3000)=nil, 0x3000)
shmctl$IPC_RMID(r1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xcd604406, &(0x7f0000000140))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
pipe2(&(0x7f0000001840)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
r3 = dup2(r0, r1)
dup2(r3, r2)
r0 = syz_open_pts()
syz_open_pts()
pipe(&(0x7f0000000000))
syz_open_pts()
select(0x40, &(0x7f0000000080)={0xfffffffffffffffe}, 0x0, 0x0, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = dup(r1)
listen(r2, 0x0)
accept$unix(r2, 0x0, 0x0)
close(r0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x1}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x00.'], 0x38}, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='$'], 0x38}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000300)=[{0x4}, {0x15}, {0x6, 0x0, 0x0, 0x20}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
pipe2(&(0x7f0000000880)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x0)
close(r0)
select(0x40, &(0x7f0000000340), &(0x7f0000000380)={0x9}, &(0x7f00000003c0)={0x8}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {0x30}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f0000000100)={@remote, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @loopback}, @udp={{0x3, 0x1, 0x8}}}}}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f0000000180)=0xc)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r1, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x14)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xc450443c, &(0x7f0000000240))
r0 = kqueue()
ioctl$WSKBDIO_SETDEFAULTKEYREPEAT(0xffffffffffffff9c, 0x800c5709, &(0x7f0000000080)={0x0, 0x0, 0xfffffff9})
kevent(r0, &(0x7f00000002c0)=[{{}, 0xfffffffffffffff9, 0x5}], 0x1, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x200, 0x0, 0x0, 0x0)
poll(&(0x7f0000000080)=[{0xffffffffffffffff, 0x1}], 0x1, 0x0)
kevent(r0, &(0x7f0000000040), 0x6, 0x0, 0xd8, 0x0)
kevent(r0, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0x22}], 0x3, 0x0, 0xffffb3a7, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x1b}, 0x2, 0x0, 0x0, &(0x7f0000000380), 0x0)
r0 = socket(0x2, 0x3, 0x89)
setsockopt$sock_linger(r0, 0xffff, 0x1021, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f0000000040)})
r0 = socket(0x2, 0x8001, 0x0)
close(r0)
r1 = socket$inet(0x2, 0xc002, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000440)="ee08665d19ac14d5e51348771197a7728420aef61715f7b183d4b3830c921bf0817a0000000000006a89dbdf", 0x2c)
connect$unix(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="82028600e2"], 0x10)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
setsockopt$inet_opts(r1, 0x0, 0x1, 0x0, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x4d}, {0x3}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000180)={@random="27aacea59068", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr}}}}})
r0 = socket(0x18, 0x1, 0x0)
shutdown(r0, 0x1)
getsockopt$sock_int(r0, 0xffff, 0x1021, 0x0, 0x0)
r0 = kqueue()
r1 = socket$inet6(0x18, 0x1, 0x0)
listen(r1, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}, {}, {{r1}, 0xffffffffffffffff, 0xc9}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x8, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f00000001c0), 0x8, &(0x7f0000000280)=[{{r1}, 0xffffffffffffffff, 0x8f}], 0x8, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
open$dir(&(0x7f0000000280)='./file0/file0\x00', 0x200, 0x0)
utimes(&(0x7f0000000080)='./file0/file0/file0\x00', 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc0284457, &(0x7f0000000240)=0x7)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
mknod(&(0x7f0000000140)='./bus/\x00', 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="200000000000008d742c007b80309905f87c8a5b6701000000000000000000c414"], 0x38}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
mknod(&(0x7f00000001c0)='./bus\x00', 0x2000, 0x4086334)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x2)
sysctl$kern(&(0x7f0000000140)={0x1, 0x32}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000501000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be608a371a3f800040000000000000001000000000000100000000000000000000001000000000000", 0xb1, 0x0, 0x0, 0x0)
pipe(&(0x7f0000000200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = getpgid(0x0)
fcntl$setown(r0, 0x6, r1)
pipe(&(0x7f0000000200)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = getpgid(0x0)
fcntl$setown(r2, 0x6, r3)
fcntl$setown(r2, 0x6, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@local, @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @multicast1, {[@timestamp={0x44, 0x4}]}}, @icmp=@mask_request={0xe}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x48}, {0x50}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000040)={@local, @remote, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @empty, "", @broadcast, "bda4613758a40710179c3269982de1cb"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x24}, {0x7c}, {0x6, 0x0, 0x0, 0x80000000}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x69, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5"], 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=ANY=[], 0x10)
dup2(r1, r0)
r2 = dup(r0)
connect$unix(r2, &(0x7f0000000580)=@abs={0x0, 0x0, 0x0}, 0x8)
sysctl$vm(&(0x7f0000000080)={0x2, 0x7}, 0x2, &(0x7f00000000c0)="0ee1a854", &(0x7f0000000140)=0x4, &(0x7f0000000180), 0x4)
sysctl$net_inet_carp(&(0x7f0000000180)={0x4, 0x1e, 0x2}, 0x4, 0x0, 0x0, &(0x7f0000000380), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000240)=[{0x6c}, {0x3}, {0x6, 0x0, 0x0, 0x80000000}]})
pwrite(r0, &(0x7f0000000040)="e81af191c7a750ff690700317e37", 0x60, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="820201"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setreuid(0x0, 0xee01)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
sysctl$vfs_nfs(&(0x7f0000000140), 0x7, &(0x7f0000000180)="89da91201b38ca8418eaea1dde867adef75aa4e91c0398f25fcc725b56f2c6581dd4cdcddfd4317ccae521ff54037eafc45b42901b2ab88d309a6600dbbdd7505474fe12387ab901dc5668800073081bd1b6bc8b4ba7a966e4103221174294c6191a8d99d36e86893c7c1f92219d3a5c52ddc0dc1b0faa04d7403ec0cb63403d017f43ce5ddbcdba9ef7f2d9574321cfcdead59840eb9d7afb9bff2f24d4dd743bf45b5dff9f65ba8b9a978e6676b9ecb5c8326734dbba117a7bd9967efe90d7fd70329fd3a10dc37cd4cf95c6186ee445ace3c9023ae892546df58f618f82b8e7d3c1cd58a02799e53ecf82018443acbb1553c18ee4f60bf9d9233c4d66edde2aaad95a9e7b4c740742d030d027565cd3b5875913f71c07b75062dda53eed2b1765281035e858e5fc8607527b278f3328816acf054504b92149f1718548cd0bdc860955c4b0f8e78485a3e912d05109fca4bee845062603c335f88196694cdc30bfce79e504eaecae391a6b005f716cd7499df64e141c7bbc3e11f2a0067c861f21508d4dd2d9a4a28cb33e05bae46f3e097e655524d71357960b45879e5c2bdd41b95502a5030a17f78b9179791916fe3ff05f514df9449bc258a3dd2339e6da2e7ecfaf0bc12e8d737631219f6be8248a58d505fc7046f0cf5be79343b1dd14069ceff36e1633010e7f801dd2d134fa13b9faf3a350405864cfa26a9820ff1c7577aeac32fd02a30dc4eb85c784248278de1faf6590c43519ad1a096986a6d40b15e6a033b6cf87fffce9fcd24a9a49ed6b6a2f64e26ef1968ebd77be47681f5e68d546ec271b017b7ddce8d59171b35dd1322ed7001048782adcc580e5dae38dc4da20108b3fedad52d608104843", &(0x7f0000001180)=0x270, 0x0, 0x0)
mquery(&(0x7f0000ffb000/0x1000)=nil, 0x7f7fffffc000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x48}, 0x2, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='oL', 0x2}], 0x1, 0x0)
writev(r0, &(0x7f00000015c0)=[{&(0x7f0000000200)="aec68839b8e74717d611e77b404193d0e5f71992f93061d0905913c6fa1c63dfe7dfb18d669394984fc55f23a91a65e151fe7886ff3dc2e1af571d7c16677e1b0c1d2d8b39df57f0680c3ed7bb55a03147d99b4f53132fa664f13f5aaca4562b0ed56a9edc2b34518403966512f605ef094ce68ff06ee07b1caa4a392b", 0x7d}, {&(0x7f0000000500)="0dcc2bcd74e7f424f0269ce9878ae6923178834002f113a9b18730669fc6328e95bb5358c68f219995926e81a0ce815d5fadd5f664df0e36510b573c8faf789a3c79e37614caed2271cc30c37831fce8c27501cb464ab2c0d514e29b38de655237c3423ca8565ad7500e6ae583b5836b999f4b81f824ac640638be4e40bbb712e7d547ed8ce48fca3f9b54b230dea5bf0300ab17143a3e2d1b263565a4c7a82300f69c7aba038f78854c36e16472b37ee3eddb78b00bbdd1ddde3c23f33693d25a9354b58d16025b441cdcae1547ebb55b7e3c944980b3685c1f5ce69d3f", 0xde}, {0x0}], 0x3)
writev(r0, &(0x7f0000002980)=[{&(0x7f0000001980)="bb3be4cf1d558bf886dd64d5effdb85b6f76fd6985bbf4737b6e115d0ffd8a130a151d341f3189370c31a39e5e36443d2a29cdf80e5d3ab5d35c78515da9b361fcf53397fa555cb5fe2a8fc194f8c05f499ba2b6d053eef32d4e164bec653ea466aae2883a80d4c12f47e4185339d106653b81ea7ab9b9d0a44da916f007eba69108ea80ee15efa9b12d5e19274b66ee7926d9588b7afeb025e2b133057d916c26f776c28d1cba313c31cd724b798244d77ec1b2faeb2f4cc3b98832b209209810f1328ad6fd9fa75dcd59bd067601adf62d7d267f89f62583c0d0f44ee04bb0205f00f5d83d4808c20815609d334df8092081be54c8754305b78b2c3f0bee4197a60aa4571ef4c2399a8988a1dec7105bca3c820399901544fe5bfdd6d66cff52cbf12d72e9af0add7410fbde1aa15127d4f7e916b096c01331d00d231ee1a3558e10df4c2938bfc8c655840e72b23861a414da26f3e4822131e43e31342f2d2eeda699d0020b98858aaa436986de9297a676f1a6b5200907d784b212ece8a0cb047e84ff1980c6d5f18e1e22872eda58f0ba2787a183805f0d8dc89415723bfee45604d994d71c25b051e8ccd413b24c787612f6c6685a40720186cb18f43dae80bf88", 0x1c4}], 0x1)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000140)="74e201887d347d495fd8ac509b692e", 0xf}], 0x1)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000340)="83eca8fe3800aac72d4750a3422e", 0xe}], 0x1)
close(0xffffffffffffffff)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0xfffffffe, 0x0, 0x8009, 0xffffff01, "00000400007b0000000000001f001e0000000001"})
write(r0, &(0x7f0000000440)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713240c00009c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b758b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5ed0d85fc397bb1e7a51af9a9b0eb8230c4f92e2b8523872aebe96544d539e7cb063926132dba7fc0144863d7221640bbf251e82226d5ef65610ed4dbdbd20e70352c53f3e5d018ee845a4bb59ec0d78755f5666dc28", 0x116)
sysctl$vm(0xffffffffffffffff, 0x2, 0x0, 0x0, 0x0, 0xfffffffffffffef0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x44}, {r0, 0x25}], 0x2, 0x0)
close(r0)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
poll(&(0x7f0000000440)=[{r0, 0x40}], 0x1, 0xfff)
read(r0, &(0x7f0000000480)=""/4096, 0x1000)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
close(r0)
ioctl$DIOCMAP(0xffffffffffffff9c, 0xc0106477, &(0x7f00000000c0)={&(0x7f0000000080)='./file0\x00', r0, 0x1})
sysctl$net_inet6_ip6(&(0x7f0000000100)={0x7, 0x2, 0x2}, 0x8, &(0x7f0000000040), 0x0, 0x0, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f0000000040)=0xc)
r1 = msgget$private(0x0, 0x0)
msgrcv(r1, &(0x7f00000005c0)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000000000b30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000f9ffffff000000000900"/164], 0xa4, 0x1, 0x1000)
msgget$private(0x0, 0x14)
msgsnd(r1, &(0x7f0000000140)={0x0, "745537e95a3d15c68210fc0103bbe6b26b9efdab9284ed9c61c35a8f035b370269e384739bb71b05dee418dc0b5fa14aebb281e2965cba7445d6c7832da74f44366962bdac87a1faf0afa2793c64cc41e2de672891f6a605ac08b2293791dff2a78f88c020bb1c9460ff96089b0d88ed9bbf063d3c19848fc9753518025dfe70728649f0c7af586e0d14da268f832d0483aed0ce231224586bdfa4e73628524a3d7216c24becaa2f1d8107cf0f5963054dd1ada92f3f467daf82888171dfcccb16dab78f04290bb9735cd79f03c26b1abe6d8a4d9dcc52bb4a51493f231a"}, 0xe6, 0x0)
setreuid(0xee00, 0x0)
r2 = getuid()
setreuid(0xee00, r2)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000140))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f00000000c0)={0x0, 0xfffffff9, 0x6007, 0xffffffaa, "919b914c6f58d3ad9f7b4acbd21500"})
ioctl$TIOCSTOP(r1, 0x2000746f)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c", 0x83}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8020690c, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x100000000c8, 0x0, 0x0, 0x0, 0x8000000000000000], [0x4, 0x1008, 0x7, 0x1000000000, 0x6, 0x4b6, 0x8, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0xfffffffffffffffd, 0x6, 0x2, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002004, 0x0, 0x8, 0x46c, 0x6], [{0x4}, {}, {}, {}, {}, {0x0, 0x400, 0x20000000}, {0x6, 0x0, 0x0, 0x2}, {0x4, 0xf9, 0xe59}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
rmdir(&(0x7f0000000100)='./file0\x00')
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0x2, 0x0, 0x0, &(0x7f00000005c0)=ANY=[@ANYBLOB="380000002900000033000000010000005d04"], 0x7c}, 0x0)
syz_emit_ethernet(0x4e, &(0x7f0000000280)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "fd33d6", 0x18, 0x0, 0x0, @rand_addr="140d54eccbeeff70b96cc3c322e638b8", @mcast2, {[@dstopts={0x0, 0x2, '\x00', [@enc_lim, @pad1, @jumbo={0xc2, 0x2}, @ra]}]}}}}})
msgget$private(0x0, 0x401)
r0 = msgget(0x3, 0x0)
msgsnd(r0, &(0x7f0000000040)=ANY=[@ANYRES64=r0], 0x78, 0x0)
msgsnd(0xffffffffffffffff, 0x0, 0x0, 0x0)
msgrcv(0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008585be2575b24000"/82], 0x4b, 0x3, 0x800)
msgsnd(0x0, &(0x7f0000002540)=ANY=[@ANYRESHEX=0x0], 0x101f, 0x800)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x5, 0x9, "00730d88a95c5bee392f0d1f984cb9672ae400"})
sysctl$kern(&(0x7f0000000000)={0x1, 0x31}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
semget$private(0x0, 0x1, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000180)={@local, @random="a85f8d3f4d01", [], {@generic={0x86dd, "df19156ae230c6949186ca473dfc70326202788b02956271b29aa8c3de6ac06f8c96418aa9407ae3"}}})
r0 = semget$private(0x0, 0x4000000009, 0x0)
semop(r0, &(0x7f0000000380)=[{0x0, 0x2, 0x1400}, {0x4, 0x7, 0x1000}, {0x3, 0x81, 0x1000}, {0x2, 0x21ff, 0x1800}, {0x3, 0x2e5, 0x1000}, {0x4, 0x7d12, 0x1800}, {0x2, 0x7, 0x1000}], 0x7)
semctl$IPC_RMID(r0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000980)=ANY=[@ANYBLOB="2d0102"], 0xa)
connect$unix(r1, &(0x7f0000000180)=@abs={0x0, 0x0, 0x0}, 0x8)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x100000000100}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x2)
open(&(0x7f0000000200)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f00000006c0)='./file0\x00', 0x2, 0x0, 0x0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
sysctl$vm(&(0x7f0000000140)={0x2, 0x9}, 0x2, 0x0, 0x0, &(0x7f00000012c0)="02000000", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x74}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
mknod(&(0x7f0000000340)='./bus\x00', 0x3a0914c44f7b202d, 0x501)
open(&(0x7f0000000040)='./bus\x00', 0x10005, 0x0)
socket$inet(0x2, 0x1, 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240), 0x8)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f00000002c0), 0x80, 0x0)
write(r0, &(0x7f0000002900), 0x0)
syz_open_pts()
syz_emit_ethernet(0x22, &(0x7f0000000040)={@local, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x6, 0x0, @empty, @remote={0xac, 0x14, 0x0}}}}}})
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
close(r0)
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f0000000240)=[{&(0x7f0000000140)="baf92d151c3011fad28b6c83072523da54a57616c3141020ff", 0x19}, {&(0x7f0000000180)="9306c73832db3044ea31a8537274fdd4e7319488f35d94581fe06eeb140a5a5740e51c13a88affcabf30808984c3f36374a32a34f10171ebd4ede91789fbf29f6b2e0274", 0x44}, {&(0x7f0000000300)="749b502abf7032369be025ba65238e595477ce839c0792472349997c53b5af603fa3a96ccb401bdcb12aa773d6626e3cdfa200898aa27dfaa3f0988561133f7982304242cdd4add2010aef10082bd58e48e0019d40d3f054b2f7a50c613efdfa6859a002a47ec1f0ad8d3a3797cb3832707403ccdbb14158f5178c76fdb529d17b6acfb7e4981bd2ac12a95824f09807b58edd88bc9dc808a9dda1974bcb9da8a8ef67b4", 0xa4}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r2, 0x0)
r3 = dup2(r0, r0)
ioctl$TIOCCONS(r1, 0x80047460, &(0x7f0000000100))
write(r3, &(0x7f00000002c0)='0', 0x1)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x0, &(0x7f0000000080)})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x3, 0x0)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000000029, 0x4, &(0x7f0000000000)="06000000", 0x4)
r3 = socket$inet6(0x18, 0x1, 0x0)
r4 = socket$inet6(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r3, 0x0, r4)
ioctl$TIOCFLUSH(r5, 0xc0106924, &(0x7f00000000c0)=0x6e)
dup2(r2, r0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000040)=0xb, 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = semget(0x0, 0x0, 0x0)
semctl$SETVAL(r0, 0x4, 0x8, &(0x7f0000000040))
r0 = socket(0x11, 0x3, 0x0)
setsockopt$sock_int(r0, 0x11, 0x4, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = dup(r0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f0000000500)=[{&(0x7f0000000140)="ad", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$SPKRTUNE(r1, 0x20005302, &(0x7f0000000100))
r0 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0xff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x44}, {0x3}, {0x6, 0x0, 0x0, 0x5b0d}]})
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001480)={0x0, 0x0, &(0x7f0000001400)=[{&(0x7f0000000400)="cee0f03a2bffb5a470509ea859b9272318aa7c5b58796be9646fc79951eab894a9bfb681bd75df52c88092bbcfc5fc5f70d99ce3e1f5727c3b96596593c6be43fb0670af482057ded243ffde021f6a97d812456636a26f727a6c6999208e2cba3dd052cdf210c98338e427e54cc967003183318222c6153bcbf958da12e7d3dad607b50bb31beb8386bf8b06e7a1f32af1832a2c1a5a27a89b7ad11639e1e9f612496b60380f4eda44635623bcd15d918c29a0323976512647bfc84c5bf8c56047c1d224d9ac815f942f8b49b462773bc12e39fb388cfab9150a1f5d489b94a84abc0f488c6ad5a68750fd11c26f38658d3321d46f7520afc5329900d5bbc86f2c80da594f49e248f1a7281d9d1b3ad187b91a2aa5b2e8724b16f17b404f1f10e365e18e772d37c8a3527129d4a9e15aa87870f9733cfdcfc2b96a174f1051bdc82babd2f08a77bb0040db49616ec9eb4ff32bf52e965225cbbf7b6672e14f33f0aeccfb7f250eb7595c71fca8667c19c8fee558dfcd922780b932ef8dd9a40955a82121243fa6e531dcead70c5357a413cab5fda46003fc61ad2605b404a4601c2509e27a8230d234ff57bc809aee94021688e05ede8debe98410eafed581b5da50738f24d85001e2ad0da14d12f85f4ea8189ddd10ef27623688be21ce884813dd16f3b084093e4fe67358f9a7537b657d21567a1f6a6d920f80bdb940dfae342012e454436173d186319a91c41c152e2977717c472f305bdc1c7df0ee17d40fc57d3f804678ed4ffeb4bb9ebe1a30e78dc7787de8279ab63fb20db51621a408c2cc89b0441d7acc3504f874dd16bc7750ab980a9cdb7071f1f658de9d2098cb3f2e56f4e33ac2766967d51f9a0567de6c092575eb0e732720f6c2e661fa26d97e80a2cdbe1f5ba8137501b58a3914e3f28b021796bbe17b8a417d9ecb9652ab847afd1dfb3fd53f618b29241f9c7c73628b3f85e9f4ab3e9d98774bad552c6454e47af12762c5d0106a5cb465d6b2390bd3965639131cfb3b88208920eccf6704b3ccb9c99476e7ee3d6f6014f979cb3e09314ed82ca5b920fcad3ae39e65c5d0de5a66c9f9afd0e9333c8e17d16bd8d4524c5ba56aa8329ad28c92572463d6014e501efea7c360e726d1df6729d80248c1b5cc7a008ef76e5cfd57326764edf7d553e2538faeeab350757cee7caed829973bffd1f3e3ad6636d9b33d4c423c52aafae2ece5cd17990e9373186ea751e56ad309dd5586d868e4a8243fabe9cdc0fb8152adb3b1c69849ca6cc8bca39e4977f3deca8ee214132ac4120647013f8ec47c8d8e51bc2157d4a3ec68b6e646d679a8936f210a94bbc8fe0a73538c2a3f5d0a6594a14e07b09a40dec8c71d2b49adc347844fe27f02fff736586f2c5b5a2df8d6fc1f45f8e6129473e0c5009a1c01582cbdc5f6230df3f2740474159fd52084b37faa60a411dd7d482877a07c4db13c38f3b4e233e1d3311110cdacbc4c647724aba74c55403c1f9f8bb6730bef9f2b600d2509fae8a6479f68b03db5193c7139d5b950756bc756a4abadcce20e4d4f734dc021be6a7009b55b9cb1ceea0eeb7e6044c569de00c89ed0527b6cb4c32742111d41a93416ff828aed7d6dce24ea1a81cba5848c2482663e70927694397869794769f178a6ffe18d6cc4666ad22838fc455a18180c9fa129c3bf52eb27cf1868fec2ad17b0b2400d008125742d6e8cce319873fc90e7990fdb32f93464d3f57307c86efd7280a2d9eea09babec7b8267dff8f5f404b4826f6d521ee65c448e91dd9bcf3a64b16009337084141a1e536b920557ae3618111080e8772a15a66ea82ae9fe3c88282a9d54dcf561c7a3649ab984f29e3e8797e8c206b88fe3bab038c8a387dd60c53f8b5b1e77bfbb58f4f5830cf73757065c99e27b7bbf4a8a4a657a7271721901394210739dc068b3196cfc910af1a7fe2187c7758621366991e80877603c6b921fc333287cba04e125e9d57d29cf06f666ac62efc62e84f1651b10c214e18a7bb5cda35d894be81ccbf828311c41ed22ed5e3c44eefda5d2f92ce0f2c04ff2c0dced6b0011fde609bcfe35165cbcab23cbafb452679047374be2540a0058a4b52744f8a8f2f4b5b680a925cd61b7c8df495e7c8ce9139fcb46104af30b20b816bbca70ade1e362ead2e04efff2a4dd501917ef7946febdae8d757527c01fad418c649dab27d5357a3a0ff63299987d67f0f1ba8026a2240f27b47d3887f29674df06141d60e588b9b6d07dc1899a72d3a5048cf53a7917e815baeba71aeb29cc0bd1ae81519c44fe09f4c51f834eef622ab1e28dc14ffda1a7cd4f3072de22b80fee3383c2ea985b1539355eb79c5b0a1ed6363f181be665ef4bf3cf65cf17a65f16ea180902a0bdc61e6b852edb372e26639a856bd79b5b698b1ae69b8d4ba37e8a0f0b2ffde924323ef14ad1c01790902e69d8b4d524281bd93fdf4a73bb2eaeb82d22208c7b62b91687d99625db0af397d76f3d2c6d1f30c713164607b7a96db523e3f7bc3bd8c78b6b620c17c05a3f7e89c39809b27b52d1a3fc16a3571d5d1a9915ed746c497cbf0de92351074650679696e08ba82aee42d8642f0c192040de92562b121da77606ac732d8ba718e34d593204bd2a164b946b4dfbed4cd3ddd874100954976b53a8bf425651033adc8cb926ab6643b2a634dec45c0c761aff7e8ed8ee88f2623c605242ceb065b9a1d1f4eb13a5a44994dedcbc3c18619a9679f4aeffd374db5a7358ea07bdd6733f419266d8548dea234190dcf747dc717137d63846d48bb4c8651d25d8428c6c2b4500952d66677b0d3bb652863d533653c457e593b9b74912f05769ae516922da10a8f351a57de07dc9d99bbb20a090d896f460ba2648381758f83058342aa407aafa12e19d8e3504a77e2cb2fed79c47ab7f9bc07e136f7988e7ffb9c2c623cd126c00a535abb82bd1c90e42c7062e8a4910898b964228d4140f391cf8ec627ebd6b7b6acbdd64b7c0fab894e6a163e254fa3b1667a7fcf699935ef2d93d65eacc52bb0ec70325e0c9d080620392909f7937e0fbe0e07dc29c66785cda73670cbaffb47a13981917b999d935642545c0b5ef55d19b5e5b1a12a06557aeec079f226e2e2717bc8a95461a72d63089e244eec2e4021d5297dfe961142be6a9062354c597874375338218969003fbfb9b5b5fc8c27fc6c27db442996eeee147720d8a036f01791ea69405c0bd39c5f7c939c529b2f6f26244ffe94e0877f24fc7f3731c5f385682b2c4323f94bda3d8a82f16b52a10cac2c173f6f150edc98f68cbb1833371e4a62e48b967327274f074dcfa358c8c219864cfb3ab60331ed2fac661b84eb5f8b572efe82b5640d3a15918a2a06337a5a4a5ef222307cf5440e1e26c189244c9e2face9995a9014af79b7f83e2402f7a1080e5edb3f3d26a49e2f83610176ba30bcebf9a01968476fab4469e112ac798ba0e20c32dc665dc7d099219e7f313fbb1a9ef85609b53a382c9df13337883bf820f33558870c3da6b666efe146d4a45cff8cbbeadbd20a101a985b12cb32079a6b293d45a644169b49c6d504adb67240f565c91ffd5864f7cdeeb0138034376557919ecf0c77b1039370acc2b020683dc86868fc5454f5464c0fecaab6234c40b4a18e15717d64a1003ee2face2907745cffd2948788d4000e22c229717ec282eb81fa0231584dc97cb8584ee86320a02152da84f6681d4aeab91bac27374fe30cdcbe818838b", 0xa69}], 0x1}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xcd604404, &(0x7f0000000140))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
sendto$inet6(0xffffffffffffffff, &(0x7f0000000bc0)="451c2f2c49a1d9fbb3306ed7891fb52f9ec7062b3f9be97564127fd829f86f3af5a426a64398b02ab2e22e15e5375cdb51207e6ba247dd14f9ff54d7a5728382a25f7d281d68a42e7aaa85bdd9a3cf2e168226ca67c7bd0800942f0636d55bb7d3ac5bd03ed94a354e7fe3eaa914c9bfee8da99118ed3301f531567e8c019222c7d1da842b849e916328184ec78317f107717cb6926c188b4536be3900885ac1beb63377f6e63117887a001a0f6cc29c2fb2b00153dc5852cc4180826d7368a69c6e1b527c536f1716b2f061c30f0094d94044ce4026cee4c95a3ac02417962c4e46a74ae512c73a882034442dfd995bc7aad7ccc686eb2f5e8c9811fd0a9a2cb9919bd7f11d6353d695d1591b19203019596eda45bb1c751be56e3e8ecd48647c8fc5838688abe087cc767cc5d3fe220d93e65831db9c9dcb3677b8930deffad50c60dc6ec53127d4e30755dd9d5be36aa49afeb837705738e33b6cf5315adfab849d8067ce9ed9c33a8c1263d9f47de5cb4027067894a1c339f0557185461f78d520f7a5e9eb23f1c96a8b15b575f6e65aeb683252f6bc43e55586b2f0041c3bb94069e4532a6e45439ae55e8b7123f2f02b1dba3592b5f492f87267865c2830250bc89a5e15d765e657aae9b0bf42c093624511642be124926953f270448c2faada2dcf6128253e32b11f759d8d0380ecd34c049efbeb76def99032669aad8d5ad424a27ad28fe0ec1edba7d0dcb0cfee52a1fd052d2066e482608ca4d95973b6bee54cda16bbe43ed35a680b88ad333062ae868387fb79b318aa7854616fbeca3d96123457a7dc5113f2574f7ca842f9f3869d2f326b6a4c40adea9e6a4ba75a26b30d66b4b98c99b748f0dd6fa0fbdab0e62140c951093111163218099245ecf67fc3efedf8b443121a4de1c03cc49e7b648f55139d061fdf4fc27290c71764c5af0f67f0bb97bce3f93572a77bb9e25dd28881a419cb0074892eb987a630bb93e47729284a23982f47c33d3fd58cf54aa1e4936457489dd54787a7d5bada247b33446521127a10f55584930b5d0a2f06b26ef28c4d568179a28c8e9574fda266b91d2941213517fa188d275964c1d1cb0bd3915a0ae0072e476a6c0b6380efa57d5657fa62e766f0d38c39b399b785fe0b1abedf161efef872fe38d6f3d97b47c14dbd101badf846a811b0281a9685c3cbb45705585d6210fdf17c363addd1e5a4008309003ba65bc70ace83a516356b6e29dcd09ffbd745163842eb2fc15e376fcc20510f7faed6e9303a17262c44285b608db82f2bfba5273ddc9648247aea2ac17ce7800d75bded1e56fb6cb2637dc520679fd3f603b21454141e2c19060000006144dfa5de644e7dac9f52e658c10c7919ed623a790ab58ee2b515f6839b338c08852ec1d4e7b634aa5608bcd257ce2900a9065001c3e65aa1cec59d741359dcdc37d0e83ce00413b080a82e3e587bf6719736851a5c72f7972bc4b82052fe5a5dbedd80ccfcb25b1663de3585372440553da6fdfad08be27a96e3159f2ba08cb8a7aedf781c2ad1fe59b43b7764ab8af1ab03f5cfe87eaff7457ad7b3a40842f3dab12d278cc55329de2c514109e7d400c6e0be4856d6394fc72e2e16caf6115ba426a13fe3d692d9745eb7310bb0ba624f7d63bec8efdbc40cd73c45da3a494a5593a0e5088afce0c0ca841ff97141cc5c2e37fa222230", 0x4bd, 0x6, &(0x7f0000000040)={0x18, 0x2, 0x6391, 0x7fffffff}, 0xc)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r2, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
accept$unix(r2, &(0x7f0000000440)=ANY=[@ANYBLOB="0000000000000000000080000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000c15ab3fd8a2ad03c3c0d3f0883eb6dcf415644b70e2af3b2a6ccddbbc60c8901eeb622b6f7c941e51ec4e8587341ccec5b043c7d9d6337c044efe5c7b8eb2e92105e28ae481efb383d3c7b16848f379d4133d40187b70a972eddb439c34d0e27b9405872e74def74fb1fa380b2a883c39549461048c436ced7d6297859c2792f69cffea55844736a0e8b8178bf79ef98672ca5b9fa712112dfa247475ab4bb6f46de9e99ca91e5a15c36ac44ba3e206886a2492650a13cd97fd6727d5c0885dc3aed2fd07c26911cb9590fc0aed3228b9f19df08eea0ee65a1045d4bc15de1ef40ddc7456348c4d1fb1b11e27132bc01051db53327c46c35e8032a4edbee4a320ff5c8f877dfcf4f497dd8f53f28b5dd2d3172d90acfc0197ef47dbc85c517887313dcb191abe862869fd8cb92f69e3dcc511de8da0b185b591f9c84a25863392fc484ced0859201bb1b4bccd27994b9a2fe858067a7d5743effdccb09425b0936db23ce3d9dbeceb612c026ffe3ff9c"], &(0x7f0000000200)=0x4b)
r3 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
r4 = socket(0x11, 0x3, 0x0)
setsockopt(r4, 0x20000000000011, 0x800000000001, &(0x7f0000000100)="34aef885", 0x4)
r5 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$SPKRTONE(r5, 0x80085301, &(0x7f0000000080)={0x0, 0x800})
ioctl$FIOASYNC(r5, 0x8004667d, &(0x7f0000000180)=0x45ce)
sendto$unix(r4, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
dup(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r6 = socket(0x18, 0x3, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$TIOCFLUSH(r7, 0x8080691a, &(0x7f0000000300))
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000180)=[{}, {{}, 0xfffffffffffffff9, 0x91}, {{}, 0x0, 0x0, 0x0, 0x0, 0x4}], 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x2, 0x12d)
nanosleep(&(0x7f0000000000)={0x7, 0x40}, &(0x7f0000000080))
r1 = openat$pf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0x0, 0x0, 0x0, &(0x7f00000009c0)=ANY=[@ANYRES8=r1, @ANYRES8, @ANYRESOCT, @ANYRES64, @ANYBLOB="31e28fe2b1b76deb1f05e8f1a1b93569b14e1f7335168c4ca26d826d74540c455b2d5d348287c076f54d66d51f1dcd175b69ab070df300b44bba458469df3a022bd90f5b39ff2519304dc0a8824c060970426e85324b0063694f2cc9e9f312f54502e80f5e1ffc87bf0e3ce2e64df79d5b3c5bcbe6fd1715efd663ce89a27b3e8864f045333bfaf6f00710c021890e9608002499388ad765def459cce093508265527b393b80", @ANYRES32=r0, @ANYRES16], 0x38}, 0x0)
kevent(r0, &(0x7f0000000000), 0x7c, &(0x7f0000000300), 0xe51e, 0x0)
r2 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000100), 0x20, 0x0)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000140), 0x12, 0x0)
r4 = socket(0x800000018, 0x3, 0x0)
r5 = socket(0x800000018, 0x3, 0x102)
sendmsg$unix(r5, &(0x7f00000012c0)={&(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000001200)=[@cred={0x20}], 0x20}, 0x0)
r6 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
poll(&(0x7f00000000c0)=[{0xffffffffffffffff, 0x11}, {0xffffffffffffffff, 0x100}, {r5, 0x8}, {r6, 0x10}, {0xffffffffffffffff, 0x80}, {r4, 0x10}], 0x6, 0x80)
ioctl$TIOCSETAF(r6, 0x802c7416, &(0x7f0000000140)={0x3, 0x6, 0x1ff, 0x40, "80733efedf0528408378216de5b488419bba46cc", 0x0, 0x7fff})
kevent(0xffffffffffffffff, &(0x7f0000000280)=[{{r2}, 0xfffffffffffffffe, 0x2, 0x2, 0x100, 0x6}, {{}, 0xfffffffffffffff9, 0x0, 0x1}, {{r3}, 0xfffffffffffffff9, 0x80, 0x1, 0xfffffffffffff801, 0x9733}, {{}, 0xfffffffffffffffa, 0x8, 0x2, 0x5}, {{r6}, 0xfffffffffffffffc, 0x0, 0x20, 0x3faeb886, 0x8}, {{}, 0xffffffffffffffff, 0x80, 0x80000080, 0x4, 0x1}], 0x49, &(0x7f0000000200)=[{{}, 0xfffffffffffffff9, 0x2, 0x2, 0x40, 0x4}, {{r1}, 0xfffffffffffffffc, 0x0, 0x20000020, 0x9, 0x10000}], 0x3, &(0x7f0000000340)={0xffff, 0xce0})
fcntl$dupfd(r0, 0xa, r1)
r7 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r7, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r7, 0xcd60441a, &(0x7f0000000240)=0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
close(r2)
bind(r3, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
listen(r3, 0x0)
r4 = dup2(r3, r0)
connect$unix(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
close(r1)
accept(r4, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00001000000000010000200800"})
r0 = socket(0x18, 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001400)={&(0x7f00000000c0)=@abs={0x1800, 0x0, 0x1}, 0x8, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0xd, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x4}, {0x4d}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @random="9d8b1e9d2424", [], {@ipv6={0x86dd, {0x0, 0x6, "923149", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
syz_emit_ethernet(0x36, &(0x7f0000000000)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x1, 0x0, @local={0xac, 0x14, 0x0}, @broadcast=0xffffff}, @tcp={{0x3, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
fchmodat(0xffffffffffffff9c, 0x0, 0xddb4e0637a361028, 0x0)
poll(&(0x7f0000000000)=[{}], 0x1, 0x7fffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x2)
execve(0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
poll(&(0x7f0000000180)=[{r0, 0xde}], 0x1, 0x0)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
shutdown(r0, 0x2)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setreuid(0xee00, 0x0)
r1 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r2 = getuid()
fchown(r1, r2, 0xffffffffffffffff)
setreuid(0xee00, r2)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
fcntl$dupfd(r3, 0x0, r4)
r5 = dup2(r4, r3)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001000020000300"})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x100000000000000}})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
dup(0xffffffffffffffff)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
r1 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
recvfrom(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
open(&(0x7f0000000340)='./file0/file0\x00', 0x301, 0x0)
chmod(&(0x7f0000000240)='./file0/file0\x00', 0x3d2)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000040)=[{0x5}], 0x2})
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{{r0}, 0xffffffffffffffff, 0x51}], 0x0, 0x0, 0x0, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{r1}, 0xffffffffffffffff, 0xed, 0xc0000003}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000040), 0xef, 0x0, 0x208e8c28, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
dup2(r2, r0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f00000001c0)='./file0/file0\x00', &(0x7f0000000100)='c\x00')
unveil(&(0x7f0000000040)='.\x00', &(0x7f0000000080)='r\x00')
mkdir(&(0x7f0000000280)='./file0/file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0/file0/file0\x00', &(0x7f0000000180)='r\x00')
chroot(&(0x7f0000000380)='./file0/file0\x00')
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000200)=ANY=[@ANYBLOB="82022e"], 0x10)
r2 = socket(0x10000000002, 0x2, 0x0)
dup2(r1, r2)
getpeername$unix(r2, &(0x7f0000001280)=@file={0x0, ""/4100}, &(0x7f0000000000)=0x1006)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{r0}}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x899, &(0x7f0000000400), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ae, 0x0, 0x1, 0xffffff01, "080486179c7d9234997b52006000000700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000003c0)="4491125e055171c5077f329557a30004856f276a74bb36778525298af8f7e8245d661132a4474bb2185340f8a3116927e7776de444c14b4233243a47aa8c7779222189299257a1a295db6954f15fcbf00ec4ec9a0f2212a452aa4bceae373d8c2107b4e185e783a97e180dc9e7d921b6722798dca9", 0x75}], 0x1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac43]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
r3 = socket(0x18, 0x3, 0x0)
r4 = socket(0x18, 0x3, 0x0)
fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$FIONBIO(r1, 0x8004667e, &(0x7f00000000c0))
openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x20, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
r2 = dup2(r1, r0)
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
listen(r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
poll(&(0x7f0000000080)=[{r1, 0x1}], 0x1, 0x0)
dup2(r4, r3)
connect$unix(r4, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
accept$inet6(r2, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000ffffffff5800000000000000003567b54500"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r0)
socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x1}, {0x84}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
mknod(&(0x7f0000000000)='./file0\x00', 0x2000, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCCDTR(r0, 0x20007478)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0x20004455, 0x0)
munmap(&(0x7f0000800000/0x800000)=nil, 0x800000)
r0 = shmget$private(0x0, 0x4000, 0x0, &(0x7f0000ff9000/0x4000)=nil)
r1 = shmat(r0, &(0x7f00009a7000/0x3000)=nil, 0x0)
r2 = shmat(r0, &(0x7f0000c0e000/0x3000)=nil, 0x0)
shmdt(r1)
shmdt(r2)
r0 = syz_open_pts()
dup2(0xffffffffffffffff, 0xffffffffffffffff)
ioctl$TIOCCONS(r0, 0x80047460, &(0x7f0000000100))
setreuid(0x0, 0xee01)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x16, &(0x7f0000000040), 0x4)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000180)="fceab6cf3cba10d395465932249062df6688e8ce1a70561d7adda969f009b638f6c98a5316c30765e079504ef07bd41034ca66b745bd79beec2262a5118b0d9a27323d934bc77b4cbf2728f5e1a2e2bd688d47c97d542378308007", 0x5b)
socket(0x2, 0x2, 0x2)
setsockopt$inet_opts(r0, 0x0, 0x0, &(0x7f0000000000)="aa6b8f295f8d697e21d2d8b43e12bce9a502e961087d216d9715a4c708c9bbb2da5e24b55867a4", 0x27)
r1 = socket$inet(0x2, 0x4000, 0x49)
pipe2(&(0x7f0000000840)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
close(r2)
getsockopt$SO_PEERCRED(r2, 0xffff, 0x1022, 0x0, 0x0)
r3 = getpid()
ktrace(0x0, 0x5, 0x800, r3)
fcntl$setown(r2, 0x6, r3)
setsockopt(r1, 0x0, 0x4, &(0x7f0000000040)="01000000", 0x4)
r4 = msgget$private(0x0, 0x2)
msgsnd(r4, &(0x7f0000000500)={0x3, "c5e94674dbd3604cde5c3557da6461341b1f80b87c9e9c0a41a1995045af2238befbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6a"}, 0x6d, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x1, 0x5, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206916, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000bc0)="adf7480771c43ad526722303821fe14810de7e384b89084fc9827c8306344953b338ac132303e25186cb4538d78c5a8bafd4a6671cba335f4f625790b7733fe58732c1225aa4be23395ea68aa11025abd4344f68f6218c01f904bb77c1d989f1a4a494af047ee0a1920c0d62453ee9f69f8ed0bc52fcd099995af26b12b1b0411cc2d6838e167868c526e80fbb532b621c19b69d237671b7445ea1d973b260ae08dcfe7dc86b3c6bc67c5a173ba8697938ada022c6030a5f49cefcda828231dd2f360683fc8c1f380a", 0xc9}], 0x1)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a55180", 0x22}], 0x1)
open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0xf14, 0x0)
select(0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)={0xfffffffffffffffc})
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1004, 0x0, 0x0)
syz_emit_ethernet(0xff86, &(0x7f0000000000)={@empty, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2}, @generic="ee5055327896bdc63974ac0a937b285807cbc0935fa29485ebfd6334b984c8fa9a6ac79b4ddbe630e976a61f2e"}}}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
getpeername$unix(r0, 0x0, 0xffffffffffffffff)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
socket(0x2, 0x2, 0x0)
socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x5308)
open$dir(&(0x7f0000000040)='./bus\x00', 0x1, 0x0)
ktrace(&(0x7f0000000000)='./bus\x00', 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt$sock_timeval(r0, 0x29, 0x4, &(0x7f0000000040), 0x10)
setrlimit(0x0, 0xffffffffffffffff)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100a56700009eff03000000", 0xc)
getsockopt(r0, 0x0, 0xb, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000580)={<r0=>0xffffffffffffffff})
fcntl$getflags(r0, 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x4c}, {0x7c}, {0x8006}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f00000003c0)={0x0, 0x0, 0x7fff, 0x0, "48072ecebdb4c9042f2c9950f4a8a9fefb7b4dd0"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
r2 = kqueue()
kevent(r2, &(0x7f0000000280), 0x7, &(0x7f0000000340)=[{{r0}, 0xfffffffffffffffe, 0x1}], 0x10001, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000080)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "09901e", 0x8, 0x0, 0x0, @loopback={0x4}, @loopback, {[], @udp={{0x0, 0x1, 0x8}}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$PCIOCREAD(r0, 0xc0284457, &(0x7f0000000000))
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004})
flock(r0, 0x2)
close(r0)
fchmod(r0, 0x18)
acct(0x0)
recvfrom(0xffffffffffffffff, &(0x7f0000000080)=""/24, 0x18, 0x800, 0x0, 0x0)
getpeername(0xffffffffffffff9c, &(0x7f00000000c0)=@in6, &(0x7f0000000100)=0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f0000000080)=0xc)
openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = socket(0x2, 0xc003, 0x2)
setsockopt(r1, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r1, 0x0, 0x66, &(0x7f0000000000), 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x20}, {0x30}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000001080)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast}}}}})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x130d, 0x0, 0x20008007, 0xffffff03, "1f000000007e00"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d298b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5e", 0xcd)
sysctl$net_inet_tcp(&(0x7f0000000140)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f00000003c0)="0118fff6360f9ea14fb359657f16666e9f97069815ca5835b6f65316127c991ab43afd5604c4aa10930ed14b1088b7d8414191ac6193bb09919a8a372208b127f29c66755d45d5ae11c6731aede78c4421cef62cac7d5ecb3a69b3e7910599897b40c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebb5981ea2de4000000000020a63fde26f5ca6a157ad15000ff78e45b55a191701155a29aabecbdada66c878f486f7e59a59a05bb689915b909800989d8d1fbe686246fa85c22ad066d2bee08f7397cfe2cae6e966e98d4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac7354745175bd902a5f48e0a013a1dc24244ade0d510672dd77da2c8ffff2ec00000000000900000047000000a4fb0c0d186cd2eaadc9f94ea40000000000000000000000000000000009eb3881885647e6b9ecd6bff6b37cd49c4287ed75b08a58f19f470bd87e5503c733fc217eb57458e55df302e2d611ae3e030100a9edbd2d2d845b8e1f2e111835a6b788d5ff5256df19b563ef69e55e74120536a99d2a43575893f400c7c32ed7a1d4dfedd53dc24cb41b274925139f0ceb63553689a46145fc7f2c30c0d29de0815e8214f857ebd1f1e41bfb9a21624824a96d9619e00feb108d5bb60a27d465014bd7652b7e5f4a46cb83eea6b48aeb60db0242a3044bc0955254edb0450200b24c238f90402598ad960ebf7502767ebb569f48ec63dafb4bcc", &(0x7f0000000040)=0x210, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, <r1=>0x0, 0x0, <r2=>0x0])
setegid(r0)
setgroups(0x0, 0x0)
r3 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r3, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETZCNT(r3, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r4 = geteuid()
r5 = getgid()
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000380)={{0x6, 0x0, 0x0, r4, r5, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0xbf5})
setgroups(0x3, &(0x7f0000000000)=[r2, r1, r5])
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
semctl$SETVAL(r3, 0x0, 0x8, &(0x7f0000000080)=0x3ff)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
chmod(&(0x7f0000000300)='./file0/file1\x00', 0xd7)
setuid(0xee01)
mkdir(&(0x7f0000000240)='./file0/file0\x00', 0x1fa)
rename(&(0x7f00000002c0)='./file0/file0\x00', &(0x7f0000000100)='./file0/file1\x00')
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x7, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x20004000, 0x200000, 0x0, 0x9]}})
msgctl$IPC_SET(0x0, 0x1, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x80047476, &(0x7f0000000080)=0xfffffffd)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
lseek(r0, 0x0, 0xd35a57837e548380)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000040)=[{0x7}, {0x2}], 0x2})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0206937, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000280)={0x0, 0x4})
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000200)=0x6)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
read(r0, &(0x7f0000000040)=""/32, 0x20)
openat$speaker(0xffffffffffffff9c, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x10095bc, 0x0, 0x3, 0xffffff5f, "084c287530910a54f562f619afb1058109000200"})
writev(r0, &(0x7f0000000740)=[{&(0x7f0000000680)="5c8a", 0x2}], 0x1)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x12, 0x0)
mknod(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000140), 0x10, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x300000005})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000005})
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000280)='\x00', 0x1, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0x0, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0xb5}, {0x0}, {0x0}, {0x0, 0xffffff6c}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x842)
recvmsg(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
dup2(r2, r0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1b}, 0x4, 0x0, 0x0, &(0x7f0000000140), 0x0)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
ioctl$TIOCSETVERAUTH(r0, 0x8004741c, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x64}, {0x2}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
setsockopt$sock_int(r1, 0xffff, 0x1004, &(0x7f0000000100)=0x10001, 0x4)
recvmsg(r1, &(0x7f00000014c0)={0x0, 0x0, &(0x7f0000001340)=[{&(0x7f0000000000)=""/73, 0x49}], 0x100000000000022c, 0x0}, 0x0)
getsockname(0xffffffffffffffff, &(0x7f00000007c0)=@in6, &(0x7f0000000800)=0xc)
recvmmsg(r1, &(0x7f0000000740)={0x0}, 0x10, 0x0, 0x0)
write(r0, &(0x7f0000000240)='g', 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0x81}, {0x20}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000440)={@broadcast, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @empty, "", @empty, "b1df701410a03d88ded02e33fc28930c"}}}})
socket(0x18, 0x3, 0x29)
sysctl$net_inet6_ip6(&(0x7f0000000140)={0x2, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0x240)
r1 = msgget$private(0x0, 0x200)
msgrcv(r0, &(0x7f0000000200)={0x0, ""/104}, 0x70, 0x1, 0x800)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000100)=""/133)
seteuid(0xffffffffffffffff)
ioctl$TIOCSETD(0xffffffffffffffff, 0x8004741b, &(0x7f0000000000)=0x5)
r2 = getegid()
getgroups(0x6, &(0x7f00000000c0)=[r2, r2, r2, r2, <r3=>r2, r2])
r4 = semget$private(0x0, 0x1, 0x202)
r5 = msgget$private(0x0, 0x200)
msgctl$IPC_STAT(r5, 0x2, &(0x7f0000000100)=""/133)
msgrcv(r5, &(0x7f00000001c0)={0x0, ""/46}, 0x36, 0x0, 0x1000)
msgsnd(r5, &(0x7f00000005c0)={0x0, "ad41462fafa8ba9416524e0305ffffffffffffffff14f3c25ef69de2543b464d85cd1546809376ce8f7db5056171ad80b6b4caa048f7ffffffffffffff73136a599973afb37a8f325717c4157a8f35fe45d2f922fc9c3295001e5a24f1a8c56fa3c1ac5a68ec17e34e1c796ca22accfbb80e9dedc4046d07287ccd1842043b5ec8e2c813d653f33ef5297de8c0f8f4429b6e783afd3da6272e161967c2152d2814bc6d195bf2e1d6700d8f8a6fe2bffa37d3f95c9cdd064370bc61b6f83c798b25b20a8f5122bdcff47d3b94ac8da1f32a74f0d8e7035c2eac2dce8e81c070ab059b8be0c00faee3af41840f28303a48e2aba85343375a25a42d0bdd8992df9e960dcb9be45795bd5fb09fd9b88e73ba9a"}, 0x119, 0x800)
msgsnd(r5, &(0x7f0000000280)={0x3, "b5eee2e755de90a709b25b1107caa9d2294361a983aa724be9890f5943bf9290acda71acaa313aff25fec1fbef63cd90c480a59671fd02a4194e3ae426e6838ec0501f19d4695622552d4dd48475b4872a8d29ebdd827e8aadf8f9f9d7b0c3ac"}, 0x68, 0x800)
semctl$SETALL(r4, 0x0, 0x9, &(0x7f0000000040)=[0x0])
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000100)={{0x1, 0xffffffffffffffff, r2, 0x0, r3, 0x100, 0x1}, 0x1000, 0x5, 0x3})
msgget$private(0x0, 0x581)
msgrcv(r1, &(0x7f0000000000)={0x0, ""/151}, 0x9f, 0x0, 0x1800)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
lstat(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000140))
openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0x1a, &(0x7f0000000040), 0x4)
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x100450a)
open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000040), 0x20280, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
chmod(&(0x7f0000000300)='./file0/file0\x00', 0x51)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000240)=0xc)
lchown(&(0x7f00000001c0)='./file0/file0\x00', 0x0, r3)
r4 = getuid()
seteuid(r4)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0xd4)
rename(&(0x7f00000004c0)='./file0/file0\x00', &(0x7f0000000500)='./file0/file1\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443d, &(0x7f0000000240))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffffd, {[0x3d83, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x2000000000003, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x0, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfffffffffffffffb, 0xa6f, 0xfa5, 0x80000, 0x5314, 0x5, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x3, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x2004, {0xffffffffffffbdd1, 0x40}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x7], [0x1, 0x6cf11d8a0000, 0x0, 0x0, 0x0, 0x0, 0x4000020000210, 0x0, 0x0, 0xfffffffffffffffc], [0x3, 0x0, 0x0, 0x4], [0x0, 0x0, 0x40000000, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x4}, {0xffff, 0x1000, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc, 0x0, 0x800000000000000}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000080)=0x5)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0205609, &(0x7f0000000040)={0x1, 0x0, 0x1})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x78e, 0x0)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
sysctl$net_inet_tcp(&(0x7f00000000c0)={0x4, 0x2, 0x6, 0x14}, 0x4, 0x0, 0x0, &(0x7f0000001140), 0x4)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
writev(r0, &(0x7f0000000780)=[{&(0x7f0000000480)="fb7f", 0x2}], 0x1)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0xfffffffffffffffe, 0x1000300000002})
ioctl$TIOCCBRK(r0, 0x2000747a)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
writev(r1, &(0x7f0000000780)=[{&(0x7f0000000480)="fb7f", 0x2}], 0x1)
fcntl$lock(r1, 0x9, &(0x7f0000000040)={0x0, 0x0, 0xfffffffffffffffe, 0x1000300000002})
ioctl$TIOCNOTTY(r1, 0x20007471)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x400, 0x0)
close(r2)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r3, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000200)=[{0x40}, {0x81}, {0x66}]})
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r4 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$BIOCSETWF(r4, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0xce, 0x6f, 0x7, 0x3}, {0x20, 0x1f, 0x20, 0x5}, {0x40, 0x6, 0x8, 0x7}]})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@local, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @remote={0xac, 0x14, 0x0}, @broadcast, @multicast2}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='\x00', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00b7dd0d261ed1b9", 0xc)
sendto$unix(0xffffffffffffffff, &(0x7f0000000040)="429da7ed3556a6b73e1e72b7d00e5c08442e1adf159109327a0bf21d61b26830a31a0094a62210228b42405b2b27", 0x2e, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x3a, &(0x7f0000000040)={@random="831ee5407df0", @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @multicast1, {[@rr={0x7, 0x3, 0x31}]}}, @tcp={{0x1, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="b5027b1c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
listen(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0)=0x441, 0x4)
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x1, 0x0)
openat$tty(0xffffffffffffff9c, 0x0, 0x0, 0x0)
socket$unix(0x1, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
r3 = accept$inet(r0, 0x0, 0x0)
sendto$inet(r3, &(0x7f00000007c0)="1daad5cd36195d6810b318271ef9a4e226c2d78af736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8adbe6f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbacff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e75e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e138a000000b92c28d39f6e23bc224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70df9265fa833f7f7788b351b9a0abf03d9e24db2448b2db5c1105d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc346d36d0fe7a2d0b32aeff27e94fe6994ffe7086d8f0c631b9688003f65d4b91d0000000000000003f5cea6e423358731875fe8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc7d8f9f2fe3d1d4cf4dc054a98f9013b515452b742f911f5aa5258ea5504ec5fd29ae3124f55cfdbea9c3969dc552e1d6f13d86e3043a8ed35e413ea2a8c43d6c462463b88ea0a3fa87742efce671a2f79f5b66a844fbb016ba0a0eafb7f26c47f58f25808cbfc6902b0f0133039066c1b0e4b133ab19283a8447b9412faf99b67243a3fab7392f29b6d3cae0b4fc6e528f7662267692e44540bfc23ae65598b6b621eed2daac98c6f59ee7a08fca4bd017e4b6b9be821d9af3bc2fadf96cd15857a54d7e879fa9d61bf34654841f961a030f3c81f9c750128b0ccb8fa9262c5d930c8f527d9bd5fd14ec06e29de61866bb4fb0e405b324db46fa449242b5ff600fa6bbaa50d8736d7be3e9728bcebe3f703aa3d99b63d390759a13c2251c3ef8a3e0bf42c132db4317736a807c778c6b5e72a12330acd4552f2314b02eb9ceacde4bec3b552298a1bd623c1af3f4ed230056a735d0c372b6bc10000000000020000000000000026f184ac625c20f47abf53a298ba0d4e62943a57fafd57a5569c84b5517e0a92ae7580a16e6ca625dc04bb1fe6593f8e75218d1514bfe0a49c3483da21340c35377bb720d545fdf1c604dee2f5f126aca257e273af57b1341269319ddcff0281f060d65ffac74766ce2b0d3ae6074861220f542a28f4f67c464c01e27add18f942dba7e76fcbe894b1a439eebab9a9e9269bcb698aa699784c79c43ef1b6018a0432b2de4f299034e8ba0000005420abec3b55c819f4bd4a3ebe69ff68da7d7334a3c390a00b6373cc180cd7ae1c716b1fe026396179a3bb3f5fef9fdb52c0203c0e9b9965a1aa61d6011c98c2df81535ebaa69a3525fe1f924804e7e729daac801843f856f8599313b72fcbc41d1543c4a9902329d246edf766b227e1f66fc8ce920d102f4cd265857a3dcc54866a2b67a93b1d77a1c10268d3ce409bf692a7bc48afe4290cfe89fcab206057fd34cd39fe7b36c9ddf7759af0448c59a8fb3f8c61fa395d57666d076f312f1abe091c3977861b619e7c511337b9ed63c58da22ac9467f8884820e2d4861baa5e3289275b01fdf62b3861b9338939441cefe5603ff7a2f64fae5a93667d6eb2147018a60fb40bf7d89fa16c82ae55eddfc3e66baf4eb9c091dfd2b634574ed912858e3df00c20e640a0cc7c70c5ae73f999a3d86de45b960e5b5b970010beafb71c01492c9f9d5d762bccd5f03fb22f862cf6b67eebe8c910259b46f53718fb9378e9d60000928a6101ae308a387", 0xfffffdd0, 0x403, &(0x7f0000000040), 0xc)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
sendto$inet(r2, &(0x7f0000000100)="eedb2e9400860c868983dfe4391251820cf291db7c3dc7ea3e0b8cb3b395d69b549692141e104cab7ed8a428628623818344f6853b0197", 0x37, 0x401, &(0x7f0000000180)={0x2, 0x0}, 0xc)
r0 = socket$unix(0x1, 0x5, 0x0)
bind$unix(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
readv(r1, &(0x7f00000005c0)=[{&(0x7f0000000300)=""/48, 0x30}], 0x1)
ioctl$TIOCSBRK(r1, 0x2000747b)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000240)=[{0x1, 0x0, 0xfe}, {0x28}, {0x6, 0x0, 0x0, 0xfffffff7}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt(r1, 0x0, 0x9, 0x0, 0x0)
dup2(r0, r1)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r2, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f00000000c0)=[{0x40}, {0x40}, {0xffe}]})
mmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0, 0x3010, 0xffffffffffffffff, 0x0)
r0 = kqueue()
r1 = syz_open_pts()
writev(r0, &(0x7f00000006c0)=[{&(0x7f0000000100)="f0122c24afb882f80502e75883d377430095aaad5d9951194e379d5816be89789bd3386ddfda0f7b6f5edf54372bd7bf0986884c059d259b27db92fea9c663ad757117b3a13f6ca60863406e886d7a157728afa35acf06d0960f2de0b88cf45677a32b011e53d27151a2f02aa239b898df45c9a0b1e952e0679ed62bbb41847caf5420fdac6c5710ae", 0x89}, {&(0x7f00000001c0)="692588fb8deab030d8efd5e5103aaaea8dbc099a877dcfec8a493871b17df59defd2d73eb19cd4b978b2e0eeb96de664c6228b91aa09d59360a411edec4789d2fcc5f22754", 0x45}, {&(0x7f0000000280)="d421d947df213bc396d3b4949e41adab4a4aa1e8adf0b97f6a1f844cfac80ef0a7b2aa829abb8cf441dd6d438da756c4b4109ed58c7521746df777241b7dc7274d9948884b784d06aa63d83e86eed3197581652ec942a230fcd5837f950f1052a9bb15f5409eb34f21b6393496611723d43a419e45ce109cb51fe9ef51d8e88b2b391539bd088bade73957441eb5d7f85db65073abf4998a7c0784c5b41855462529bfde0455a66899fe712221f8c962c77d6b4e603a76701efce93d199e8255190d8561902b1a8bbfb5c63ecf7f61bbca8e8f5855b2b941defb73586ad547da7218a46c9431be370d3d1dc769e7cbd8f4d59246148efa3eefba7d54", 0xfc}, {&(0x7f0000000000)="4cbb1750af63c36267a3", 0xa}, {&(0x7f0000000380)="5d23d735104cdc51479e07a3c148e933697aaa0ccf8d12c947a40576084999d6770e798255958f5e9a94177c8d26c7118a912debb7daebc471352a951d42ca15bca020b40b8dd1a6effbfe419e30f906ad8b29002e1804f9f94016efbc25e93c767abf146857d51c5ce8fab88178df4fd147c3bd9f50940df4b1a38e8713a8d9017efc9843ad311b3ba5610e815e7c58d9c8633491aed09eab484daf4805802099ec86352034a52e3ce4aade9371bd7ba9aa45ceffed19932af2381f3782aa5f", 0xc0}, {&(0x7f0000000440)="249586b38dc5d099a2f939ca3e677e23bb8bd15f2567829372cdd351eafb95d7662d4589f50f845c6170c5d6fd0d32887cc146b6fae5eab9b58074372ca8d537b7b81228fe464f09cb8c2bf52caa0d53b025", 0x52}, {&(0x7f00000004c0)="8cf188955fcae370b03867345507fec4473c42f449e0884a724cc7f134368449cc81f8dd9e18b8721a5f248e8fc8c7d80bfb07265b0f6a1fd7ff9df4d6e5d5b1c72abf941552c598475a37734bd001b2eab2c0800e4072e305856a66c712d96fd7e9dd3c96eebb6df7280b77ce3d25208215261085ff37c356c9a64a328c357d9488ab0c71599d0347ace2d8b5f7dd99e14e794a85cc85152faa3f30f13529e5ccf033cb94cda553dcc1cd1a09408b5e613b8fee3e2072fb3798bc7d5dabbf7096507d81db517ee9f43e778172db440a1a000c0f7a5299050351dbfc90ec238e4bb5edf560e2e8f7f5d6944ffa", 0xed}, {&(0x7f00000005c0)="355b5bd8b269b251ca2b2b50c53e74b82960dbc88153", 0x16}, {&(0x7f0000000600)="0cc91e3fb43e87a045326790d2234d46f812da4cfa7492f506744f3266a8f1323e24bbc7c77c99d7c7bfe8981b6d6dac25acdfa6df782b9b6d62ab20f9cfc842af35ae41b3371c4fcfce80ddfe852e12f26706005e9a4957ef29c9caa816e9f27e07c0fc56717304ef656a625d0046ff2513c252b9019755acaa43535015f464767eb1d80332257993ca493c6b604a8f70ad10f1baf1d6daf83d356f6e08fb8feebea787c65186cdc7a8b544", 0xac}], 0x9)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{r1}, 0x0, 0xa8, 0xf0000000, 0x0, 0x8}, {{}, 0xffffffffffffffff, 0x8, 0x0, 0x4}, {{}, 0xfffffffffffffff2, 0x0, 0x2, 0x5}], 0x0, 0x0, 0x3, 0x0)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
poll(&(0x7f0000000100)=[{r2, 0x4}], 0x1, 0x0)
r3 = open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
poll(&(0x7f0000000100)=[{r3, 0x4}], 0x1, 0x0)
kevent(r0, &(0x7f0000000000), 0x890, &(0x7f0000000780)=[{{r0}, 0xffffffffffffffff, 0x9c, 0x80, 0x1000, 0x1}, {{r2}, 0xfffffffffffffff8, 0x26, 0xf0000000, 0x3, 0x200}, {{0xffffffffffffff9c}, 0xfffffffffffffff9, 0x40, 0xf0000000, 0x7, 0x6}, {{r1}, 0xfffffffffffffffc, 0x4, 0x40, 0x4, 0x1}, {{r3}, 0xfffffffffffffff8, 0x2, 0x1, 0x1, 0x4}], 0x80, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x4)
r5 = socket$unix(0x1, 0x2, 0x0)
r6 = open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
poll(&(0x7f0000000100)=[{r6, 0x4}], 0x1, 0x0)
r7 = openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000940), 0x80, 0x0)
r8 = open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
poll(&(0x7f0000000100)=[{r8, 0x4}], 0x1, 0x0)
kevent(r4, &(0x7f0000000880)=[{{}, 0xfffffffffffffff9, 0x4, 0x20000000, 0x7fff, 0x8}, {{}, 0xfffffffffffffffc, 0x2, 0x8, 0xe693, 0x811}, {{r5}, 0xfffffffffffffff9, 0x20, 0x4, 0x380a7cc4, 0x2}, {{r6}, 0xfffffffffffffff8, 0x86, 0x4, 0x6, 0x2}, {{r4}, 0xfffffffffffffffa, 0xc0, 0x10, 0x8}, {{r1}, 0xfffffffffffffffe, 0x40, 0x20000000, 0x0, 0x5}], 0x537dc69a, &(0x7f0000000980)=[{{r7}, 0xfffffffffffffff8, 0x2, 0x80000000, 0x8000, 0x1}, {{r3}, 0xfffffffffffffff8, 0xa5, 0x2, 0x1c2e800000000000, 0x2}, {{r8}, 0xfffffffffffffffc, 0x11, 0x1, 0x400, 0x1}, {{r3}, 0xfffffffffffffff8, 0xb4, 0x1, 0x787bfa22}, {{r2}, 0xfffffffffffffffa, 0x89, 0x40000000, 0x2, 0x8}], 0x5, &(0x7f0000000a40)={0xd3, 0x4})
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000840), 0x1, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
r1 = open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000100)='c\x00')
mkdirat(r1, &(0x7f0000000000)='./file0\x00', 0x0)
openat(r0, &(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
r0 = kqueue()
r1 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000240)=[{{r1}, 0xffffffffffffffff, 0x1}], 0x9, 0x0, 0x0, 0x0)
r2 = kqueue()
kevent(r2, &(0x7f0000000000), 0x38, 0x0, 0x302, 0x0)
r3 = kqueue()
kevent(r3, &(0x7f0000000000), 0x38, 0x0, 0x302, 0x0)
r4 = kqueue()
kevent(r4, &(0x7f0000000000), 0x38, 0x0, 0x302, 0x0)
dup2(r1, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000500)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(0xffffffffffffffff, &(0x7f0000000000)={&(0x7f0000000480)={0x0, 0x0, &(0x7f0000000580), 0x1000000000000131, 0x0}}, 0xffffffffffffffed, 0x0, 0x0)
recvmmsg(r0, &(0x7f0000000480)={0x0}, 0x10, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r1}, 0xc)
setreuid(0xee00, 0x0)
r2 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r3 = getuid()
fchown(r2, r3, 0x0)
setreuid(0xee00, r3)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
bind(0xffffffffffffffff, 0x0, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
socket$inet6(0x18, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
close(0xffffffffffffffff)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
sendto$unix(r2, &(0x7f0000000100)='(', 0x1, 0x5, 0x0, 0x0)
close(r2)
r3 = accept$unix(r1, 0x0, &(0x7f0000000080))
poll(&(0x7f0000000140)=[{r1, 0x80}, {r2, 0x1}, {0xffffffffffffffff, 0x8}, {r2, 0x80}, {0xffffffffffffffff, 0x2}, {0xffffffffffffffff, 0x100}, {r2, 0x20}, {r1, 0x4}, {r1, 0x104}, {r0, 0x100}, {0xffffffffffffffff, 0x20}, {0xffffffffffffffff, 0x100}, {r0, 0x4}], 0xd, 0x6b46)
sendto$unix(r3, &(0x7f0000000280)="d2651271e0b92dd527f70a7d7ff0e45142385065b3b20f26ee40482ee0e7dd4367a14ee619f2fbad7b884a2445e37090d8e50ce03e898b6978e4f6c213b0d32f8182bdada2174c9581032b170f17ffd868f54c2e4d3f85c0e6a96b55abb2fbee93b36ce63ca59cea3e5d7a348a749579be5d5ea9429e522086e52c34a6e2bb29b198ccb5e5f3a0c906fa4b054fc42b16d1ea73d9b1a489a925192b31ffc046939861864df52471bca86490f0e8b775f0b38da5c79919c684844fcd67e3774b16960b52d8ce00d930eee860a9d7008cc8d8b86e2ed15050e906423f6403d5912f8abb2e902fb6e0285d65178f3cec67af684881eb86fc61ba29946b656261e304714e8363869ab194f7b2baeef186fa96dccf9ac0f597fdebf7548dc7c7e26a076a9ffe8574a7c82515907a1663297f14ff284253315aeeb5e5bde559026a7817e5da4ddc832380c806f3fbab935799bc8e8ae2f445fd855c2e02b34a8cd04cef714439d4264a433b5c8aeb41abf7fb1a78cbbd730a3ee823525da6f54de9bef2031b28f2c18023241f2a8bbc6b27a2017f0413934dcfb3357ad13f1f6a3c35d5e7ba7727822834cea2d58a0cb598633e2b6ec1f37cd4292a96c8896375224c33c97819079d3e316ef874fb3ea150dcb8f266211260695c5d54049c1d841d69fa2af952cdca57abb4de29be0f1a8b652aa9e4198b86b6d99fdba910fa64be918deaa37473a1562a069f8a45e389402ff7ba9ad1657a4ee5333729e16fe759151f52e8b578cde92261decd69c1ee820cb554cb4bf713983da3c79990affb5e15e94f8b3ccf2d95f9175d72df9453f606a6a7e869449739486c121324dff471046c22a1fcc3ae73d2ac54387f333a70218f28c1aaed863670327ba5b9cb29893b63bbc7b2d76505ec63ac9c82ebd9bd7996d70ffb677c4ce99b0815ed9e6d3e5623843db2b6aa99643438a6e84f3669159eca33e8c56707e91b780e2a36beb087f7f19854fe9818bf678810fd14d70a9b0a9127225ba5ef5fb85424a9a1215c7a8d50be1c070601326357a00aece2b0333be49ed303715300d0ce095bbdcf611efd8c30310eb0f77281046b9af8b2d9ae451b906a0d8d67dbae556cf2295ad556c5030c729f66537ed41c98d3b9707421eed8c01686c81dd329a31d326c967fb29771835c7baef4c09b08df28cc9b620d7d31d177c3cb8e5023acc9d842f637fa77f52a4bf0df48ce401ecc021666ee07e13bbc62f15bab5eca00e1ff4715a4788c2e26471e944e5c86c2f6bc1cf5a89e3b8bfd1ae33254bbd12092f6845652d55f7125eb7f8bfc15f08f04c181f606fae4ad6732f7b42ff69236ecf0741f825886e203080f0fab4245a5634fd62db9cbffc2ae0c5e3c803c11c52f064afe95fd9f26ba1ae3dcc9137fe3dfe115c8c45efa92645269d6b936ad3497bf742817851af55cc6552ca622a3a559161056339f4a6b4d23ade2da22084a8dce101afe0d78bd3498074cbfd5d42351ab9379368d482c251fd308c3b1d3d8b8b558d6da6e4ce82b8ee0de73d1b153622d4df8fba95b7b8a0ee23187949b4418d98c8a2df0f820a545181af594efaef1ac6182252fb512c790c63ec47c0669ff3ce96f7b0e59c9f0342fda7d0c3d986aa2edc9225acc587a1e5ab7d2f1241f52a5b77b0b91eec6c7cc47782b6c184300264a7a5cf97d47da6982b8d3682804b4e3b6f2fb721c86a92ae06a86db4acab61f2ae5411280478df18e4680debeeb5f2f795f691c86c67485726429e3e9ae471ae008782cd4ae21219630c6651c95d17e4e531fe7e237441bfc033bee6e541de6f6dd3db54f4ede212e5d15c19bb1d08fd5c1dd2a90e2bf4cbbc6a142ab1948a2a61bb6099ea0e08f3ef7ac782af8cdf3cdeff4c4de06492c26d43b270974e3a497080a1653ebbb2fff34df44a0baae1ffdae16f9358cad99bfdaa3099807df118c6639720b898bb3d37ff5a4ea5ca01a0636d9eb33c28466f71abd98989e3cb9ad5d916ae61ae077fd4dada7b05acbd6396d7cb9cb50b4f6cc4f776df903471f411a8888c2c03d8b98d5c94e18e11b431c8175d4eea893ca48cb871387f506cb49a5f9dcaed9e89b858bc951dc2bcafd35cf5ecd41a78fac85dca207a0b4a77b2521fea4a884e158b7c61ffa047ba4cc7132ddd79dfa9aa1ad9346b6f520565275554d04954a693ccf2cfacd255a4492067836c4985356ee31daa05b67ff01a6bd8f8cdded3fee1e893c02b308da2093a13b18552f59bac46b070b2b5c09dae2bc914dee564359b203df0ae95ba03457cdae8d250cafb2115ba65782e9f9d1be75304e606483af5b7b99fcac5326d6ef2c8b73b130402193009255c9c73c0733943cc4211758e1586fbbcb90715154959087ccdd5212a8c156711f5efa128818ce3517d835565e962853e413b212ddd4aa6e0b81a9680556a58e6ab8fc098e7dc642b12be6b53959090d488d7d8731f03ac5b353a69f0d1876039806e946ceb8c657292b25bcc6e96357cec626f3595ff730b3ed6a705028d1e332373e05a2ee46ce2b5b1f78641d5981fc6ebba9040d4570c86a8a3f744aa656074498db7a6e78478bb70c2544e434b2b85e46c62e7a0626ac12a461c56082b220ec6150f5009b1967ed04517a51f4316c27c02d882935db7930269b5f55b63ece793d79872a340b64dd73ead0e06e7168f234ba88a67f535bdf4324cf6b211ddca8c69cc97c0ae295125211cf5f8aac0b80c2942fd4238901a3286085082cabe2d40554f6eb9d9a9e1fc90d41a8f535b5bf19a83285c7e52e4d64e2ef1e71cb5c7c1b9124693ae3e1170a341bd8d69a3255149d2494b39f2de4ed6b1c3074c6a5e12cba40df4ecb2f9a6d86160b6523ab4861b41d07264311f57bd9b29239dbc5c8ca6e139a7a80b976ada8fbe9fe9a324a195a48be70bca21a03b243ade8e1958363c2744db23e4d764372dafad670a8e47063d8af3dd614cc5e9bd1cac1d7b95812ee42549c04ca549685ba7d26a1ac2467051c663379f105e79ef228e53be86248177ffd7e83df0e7a90d72e81b00cf3d3810b8da36acffc31cad801ba0ecccff1356046350c31491f314358a824d0e482e62f6ab82238c46b0cd53b6795d767899c77ec66717857e6811389e837943e7539abc39a2bbfaace7b1e9c4e4cb79fb8ea67a8b6164693d0143e6bb86d78b0a3f18150f4ae9512572cc71b6b2c64fa911cd1b9d178c0443dcfc38ceb3f571732804fd0671aa981ac16476c7c8482a55be9cdeb3c3d1b464bda70dae61def77daa743bd09850162deb2fea07769522f54a7d70f0022e889cf3c0c4cba306f120a95b2af136692f415518a50fe6e76912a3e1b9f0767013a88258534228f9ddf4c6c4d39844ff235c4e7ae8b6059d9eb2a09f463345ed4107e4bbb4ca9e1a9a40b4c24c56e47ee694defda7e8589f5a4ce4ef084d52e21db2176af0db4726d139620b0676ce21fd5aae491c50bc810313477e7cb8420103c16e65eca6dd4bd989412c6acc7dfa19ef346d6cac7b6b0d2b7fc63e0cfff0833949f5b8d0eb63feb594191e7fb11f589b8a080608894163f4c6c6e8b3389c0e723278c52c776ac268fc1f1f442d21be8a5c2d7f037cd145c6b296cec6a0539728fedbe3da231439c4e8d6f95d16b8a7f293af8e1a6439a0a09ff107da1ea9d0c97f98bd43baabe67cc793c8bae2e06d6f4ec6d801360119bcd4a74007b4201007b2c5cf074a9b7d846474541e2185c7d133ab8be10e757674e5f07c8b93dc59fdebfd0ad2592a3f24573c8651fd778d8c3c1f8a42e3e640d66809ad604bcbd31dc28eab0ee2d6d6507a7fd2e5ae9fa8076f34071e46dcb7f02d8744c6c3736b65b6561b867b7ed783750de1a447525f7db090e4305563605af14104530a7ee1c2ecb74587bd3a98454c38a064b9efb3afe6da0169bda863121b987c7f32d5b14bafbf92884597898267cd726ae6c4e2bf4f863f3e89fde35c306ad5765ba2a026ab94bbf27cc0b899517ec06182aaf08edae8a0c8a5ad0fc87b53525ab17fe5b9367a6b04f1ef33efb8a4f052b37fcf5385f15f7019fa450839d320d036f7244a5b87858c06b77441b1c1bdc1a25c4ae16c399d874f74f12c21433827ffd5ea75784159edcff5f320e911d104dbba0a452dd60481678c1f89a1c82c98708604814cf0eb3d49d516fa3182fad3d96b0d2826779e81f65bf97a3234604965c2482a6fc276200824d18ec4f11f99c5e6dfb9d0af0c21140a217822cb312781985a4439655b825affa4252d3d97e8c1e113b0b0949fae2707878d245475f356d7a1db0ad7b558909ee1c51d121f4a9120205f94bc104f0958bad100d475b46bfa9651253c2d0c8a638734889b3753707591e4840831376d89ec386f9f7bb05215d44a7493c98c3ca46293d189c3137a0a1b6fc13c72f45655e40ab6a16838fec1f1f1cc4b1dd6eadc63312749274e01e5818067da8af26d4c8e26d870986e15497549cd4003b0ab8939d9f31a904ed097d88e25065bbe1770968311f0fbefbebc7379ddeb29bf2d5df8e71d6a731f78ede553481502213e6381eba1819abf03e962bffdf57ed92d63d05dfe964291cf2da37bd5eba384958ea9b78f317cb3762fd4cbb6eb71554b433e88b4a91d3eaf1f149653c3b83f689bc60c2ecaade3b494fed64f5ba38274fd6801fd074a2492feef110f01f5fc25588f0f4c51e8d2ce0c85e6a5a0428bcdd0dc3fdee278cd16cb6846e2096c727feabe78758cee31c252488e9bd99fee7205cf38610ff435a015b5cb24860d8734afb67e3c5df8f5e92b55bb2f53c4c88bdfd84bd8290c5efdd18141880b89a7d4ce38a39c0ccf82f0b20c2d1523dd07b325e5ef95a8e22b0905c850326e18b95dbb41a52d456541ceb6510f08801b4dad5b505f381243af6f8e1ff96b3d5b33a9626632ca3e4aba16ad2840dadda108d8a2b533bd5f434d0b4ecadee98b8cce4e2e8001a126be4267b9a31bcd2fc9737a98af92d95046586d7064f291bb7ce8e16adcd040e1318db4a3a061874ef102048f96079f299e774eb1cd255ddded99de7e77aa366d40afe6db294ed38531fe20f6d754006bfbaeb9adb66695b1d15695ff58fefb101a0a65710fa1bc4d9bb772240feba61d75a012e612f9133b77b6f5f982f38cd6f35b0d41e0b0a2d9de9e162664c3905ba89a26198aea774f92ce1146a0e9105ded68498432a67da518040b5bf2b13ed350300882969035566b4a91af5e457f11694430ed504cf3fbfa555db04f232ef058ef5bea26124b7b7461a3815606f0a05b3271517fec44890436000ca38d8be18c5c9fa83d901b65c00e5556d99db455bd58868098981efd3c284787f8731c31759eda3047c81218dde0e4111bc183c54ff514e0ea32ddbb6a9a36da51ea4ac3f0ba7b99f6e88699602f65966b378929d89260b161bb2adcb71afdbedc763088ff1fcd8a56ff2a8709d78ccabe66858d2f61ce8e697d5217172ca9c4780d98085eb9298a076365648cf45348203ea654012a4511cc5254c6bdfcb45dfb7ffac6fb9649ec62ffc298d5f3e6a525b125792e6696abf0309ab9db60aeed768fcb991af433b1db14bfea2787fec9c4784e2d05228241113269d534dd4050d7bf4573471959e6a874c2f1733ef5043e8b7127e6420aa1955566004400317bcd3e3ffd0f4913f292f7e7ce20d90438a04f8a4b648d0eabd542c3f87d83243b367f369670c66e5ed0caf637a699d3892d25c8818344552b4f40582d668b3c346053d3d774bf7324ea557", 0x1000, 0x1, &(0x7f0000001280)=ANY=[@ANYBLOB="b75c25b07ebadb629c2a5b6e09f36f39ca890641a157ccb4da3c9d409d92e1daf90481a078f48b479319119057836a858ca1718e889e16f1a7015fe85df37f3d1830c17f0ab0794e837fa89f7c3aed12cfaa09c1597ea90c801a78695483d7e108c36843410c99251ff4ee7f84d11fa6e96c51cf7c14f5ec8e4c8daa6dce79cde12f0be0d139709d70c663139a6beedd693c124d5dc8e61eacb898ea85f2415e96e7196ed7c6693682659a77eb1c5ac26a72002983e9d45f6e31c6433f8471f752e3b746a4138f90cb70f01d305a1144e130a438965ca12f4f1e49de77b46f6c61187cb4202e4e09aeb60523497219540ab5c47d6e75ab19cd05ebe4c4590a6c401d36bf686a84c8eb70b32b01eb8f390ecaf80c4f568e535449f40d1e472375f9a519b4"], 0xa)
shutdown(r2, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xd, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xd})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xfffffff2, 0xffffff78, "5617f17b0300e502000000b397a482834700"})
writev(r0, &(0x7f0000000140)=[{&(0x7f0000000600)="83b4ed195b417ab5cb4b504f6bf469ac59f4d772de193cc11194438d0418b22627d85e876f970f583d6553116e6b84444abb7ad3302dc529d3dd0a3b9709aea06213c5af92b5fc0487971528da6ad0f71997052d7b71ec0d3f19bc7bb5bd2e4d8a30a58ea4451cba31d696eff81e307b2845feeace270beac8db000b7ca71a960cb64a65b0c46239b92139e5bff159d8258ca84becc724ff6b84e34f17309953c314e3f2e81ff88d79d0b0a06baf5c021062640b2953fe5eef088351e68c1ec1920748d2bfe0b65d802f8ebe0423d28bdc47d40f21910c6293543eef647a179902878a8c69d714a09b984573a24cbfbcf7939953a37272ec12e6a73bfebf8e205fcd7ae48ae6737dd76ec338ec3831d1c2acd2106677ce2ee0b477b3028674749fd3997e3837ad3bc37c4514d4d5daecc4139b2a7b3b774a2ebc85be495e62a9d5ad5a2a22bb0c51a4c6a0649dad41dd7b05480287f1774ae7779a3943296056da70f2ebda16864325b63166bef56e24b3859ebea4d56befe6757f29fbc4aa375271859e71a53b812695634caa5112a2e1ac4576f9f1dfeb91831614a546128226089349b3b716e359bf3fa5bc862399f63dffec0ee330b3a97c802784987c2d0d1f22a9247e2e73735565993965be15cc7399a9117a186eae62cd0f293873ab4cdc9ca6d5e26094dfac26f2c89547ff492392ccda8aa7feba9737c8527111da00c484cd6f3a535e7cd1b6600a42efa2b371ae6772cdfe5df703fee6dc28", 0x21e}], 0x1)
ioctl$VNDIOCSET(0xffffffffffffff9c, 0xc0384600, 0x0)
symlink(&(0x7f00000002c0)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f00000004c0)='./file0\x00')
acct(&(0x7f00000006c0)='./file0\x00')
unlink(&(0x7f0000000700)='./file0\x00')
mknod(&(0x7f00000000c0)='./file0\x00', 0x2000, 0x5923)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket$inet6(0x18, 0x1, 0x0)
r2 = socket$inet6(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r1, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
r4 = openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
r5 = accept$unix(r0, &(0x7f0000000140)=@abs, &(0x7f00000001c0)=0x8)
r6 = socket(0x18, 0x3, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$TIOCFLUSH(r7, 0x8080691a, &(0x7f0000000300))
r8 = accept$unix(0xffffffffffffffff, &(0x7f0000000380)=@file={0x0, ""/108}, &(0x7f0000000400)=0x6e)
r9 = socket(0x18, 0x1, 0x0)
setsockopt(r9, 0x29, 0x3e, &(0x7f00000000c0), 0x4)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{{}, 0xfffffffffffffffa, 0x9, 0x626970ee3e745633, 0xff, 0x80000000000000}, {{}, 0xfffffffffffffffd, 0xa3, 0x80000012, 0x8000, 0xffffffff}, {{r3}, 0xfffffffffffffffb, 0x84, 0x40000000, 0x81, 0x80000001}, {{r0}, 0xfffffffffffffffe, 0x8, 0x1, 0xbc45, 0x4b30}, {{r0}, 0xfffffffffffffffd, 0x2, 0x2, 0x7, 0xffffffffffffffff}, {{0xffffffffffffff9c}, 0xfffffffffffffffa, 0x0, 0x20000000, 0x3, 0x1}, {{r4}, 0xfffffffffffffffe, 0x50, 0x80000017, 0x0, 0x4}, {{r5}, 0xfffffffffffffffe, 0x82, 0x2, 0x0, 0x7}, {{r7}, 0xfffffffffffffffa, 0x91, 0x1, 0x9ac, 0xfff}, {{r0}, 0xfffffffffffffffb, 0x33, 0x20, 0x1000, 0x7}], 0x0, &(0x7f0000000440)=[{{r8}, 0xfffffffffffffffa, 0xa0, 0x10, 0x4, 0x5}, {{r0}, 0xfffffffffffffffc, 0x9b, 0x1, 0x5, 0x3f}, {{r9}, 0xfffffffffffffffa, 0x14, 0x40000000, 0x1, 0x6}, {{r0}, 0xfffffffffffffffa, 0x58, 0x8, 0x401, 0x3}, {{r0}, 0xfffffffffffffff8, 0x80, 0x20000000, 0x200, 0x101}], 0x3, &(0x7f0000000500)={0x10001, 0x5})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x5}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x0}}, 0xaa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x7}, {0x60}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000380)={@empty, @random="cc812211ca64"})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000001c0)={&(0x7f00000000c0)=[{0x8, 0xffffdd2f}, {}], 0x2})
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1ee0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000300)=[{&(0x7f0000000040)=""/216, 0xd8}], 0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0))
execve(0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x26}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
bind(r3, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r3, r2)
listen(r3, 0x0)
dup2(r3, r0)
connect$unix(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
listen(r2, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004, 0xffffffffffffffff})
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x653, 0x2000300010000})
fcntl$lock(r0, 0x9, &(0x7f00000000c0)={0x2, 0x0, 0x3, 0x100000000})
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r1, &(0x7f0000000100)="c8", 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x2011, r0, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
readv(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000200)=""/223}], 0x10000000000003f6)
pwritev(r1, &(0x7f00000000c0)=[{0x0}], 0x1, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0xffffffff, 0x0, 0x20008009, 0xffffff03, "1f00007e2000"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d298b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5e", 0xcd)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x4})
sysctl$kern(&(0x7f0000000040)={0x1, 0x2c}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x6a5, 0x1fc80d8b, "0400e5ec00021ba9b3c70800000027f090b0c7db"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x3}, {0x3d}, {0x4406}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@broadcast, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @remote, "f4eb686fd6c20b379cff079f164f580b"}}}})
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfee6)
ktrace(&(0x7f0000000100)='./file0\x00', 0x2, 0x0, 0x0)
clock_gettime(0x0, &(0x7f0000000240))
clock_getres(0x2, &(0x7f0000000280))
ioctl$TIOCSWINSZ(0xffffffffffffff9c, 0x80087467, 0x0)
symlink(0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
write(r0, &(0x7f0000000240)='M', 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x80}, {0x1}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000000)="0c183256e5d425cb6e96d99d0279", 0xe)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0xc, &(0x7f0000000080)="99511c2b75f3b4c6fd90ff3306e44becd5c438b6618fbeee8bb283297ba656a237983783bb2f59ab6e3c07f6f943518078bec397ddc86fea16f84c76be11455efdbd8afc5920008df505e5bf079b75c09e35899dc239254aa4e2c9481490e49091fdeccc5d68e7f1f84d1f57a223eeace476eff75ad5fc7dd5a956e8fd49ae5fe283cfad7a4085f46fe664af7f91e9f76dd39b858ff5eb226cece107f54c9bff3674959795b2f8b4f05347e4a28e140d257a426d52727c794474a7dd238febc2f1d254391d08", 0x0, 0x0, 0x0)
recvmmsg(0xffffffffffffffff, 0x0, 0xfffffffffffffddf, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc020691b, &(0x7f0000000300))
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x8004667e, &(0x7f00000000c0))
mknod(&(0x7f0000000080)='./bus\x00', 0x800080002002, 0x3d00)
open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
sysctl$vm(&(0x7f0000001100)={0x2, 0x1}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setuid(0xee01)
sendto$unix(r0, &(0x7f00000000c0)="b10005016000010000001b00070000000300000000000000fef96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400002100000000361b1257d0a8c5001d200200000d2300008abfba5c00020208a371a3f8343712051eeab71d89e00004000041000000042000", 0xb1, 0x0, 0x0, 0xffffffffffffff04)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797b", 0x37}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30", 0x8e}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000003c0), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x7}, {0x84}, {0x4000006, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f0000001400)="000000d68978723219c2ff000000", 0xe, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
poll(&(0x7f0000000000)=[{r0, 0x27}], 0x1, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x4}], 0x1, 0x0)
sysctl$net_inet6_icmp6(&(0x7f00000006c0)={0x4, 0x18, 0x11, 0x3}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x34, 0x0, 0x0, 0x6}, {0x5}, {0x9e}]})
syz_emit_ethernet(0xe, &(0x7f00000001c0)=ANY=[])
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f00000000c0)='D', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x2, 0x10, r0, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x11, r1, 0x0)
sysctl$hw(&(0x7f0000000000)={0x2, 0x9}, 0x2, 0x0, 0x0, &(0x7f00000010c0), 0x4)
madvise(&(0x7f0000002000/0x3000)=nil, 0x3000, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r1, 0x0)
nanosleep(0x0, 0x0)
r0 = syz_open_pts()
syz_open_pts()
r1 = fcntl$dupfd(r0, 0x0, r0)
fcntl$getown(r1, 0x5)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r0, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
sysctl$net_inet_tcp(&(0x7f0000000000), 0x4, &(0x7f0000000040)="1d8648c76cf55b77762cb6cfeb8f0902902b13be", &(0x7f0000000080)=0xfffffffffffffebd, 0x0, 0x17)
r0 = getpid()
getpgid(r0)
mquery(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x7f366b383c9a27a9, 0x0, 0xffffffffffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
socket$inet6(0x18, 0x3, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x200, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$VNDIOCSET(r1, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000040)='./file0\x00', 0x101, &(0x7f0000000080)='./file0\x00', 0x8})
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
munlock(&(0x7f0000510000/0x2000)=nil, 0x2000)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
fcntl$setstatus(r0, 0x4, 0x40)
r2 = getpid()
fcntl$setown(r0, 0x6, r2)
r3 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000002c0)=[{{r0}, 0xffffffffffffffff, 0x1}], 0x0, 0x0)
kevent(r3, &(0x7f00000000c0), 0x40, 0x0, 0x802, 0x0)
sendmsg$unix(r1, &(0x7f0000000040)={0x0, 0x0, 0x0}, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCSETAW(r2, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde06000000000000000000000400", 0x0, 0x5})
readv(r1, &(0x7f00000002c0), 0x1000000000000321)
write(r0, &(0x7f0000000180)="b7", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0x1c}, {0x7}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@empty, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @remote, "54e2e9767c5f018a37ee7b48af7d655c"}}}})
r0 = kqueue()
syz_emit_ethernet(0x2e, &(0x7f0000000080)=ANY=[@ANYBLOB="efffffff170ce132d3ffffaaaaaaaaf8386d4b73e45132466b7e815596b86f33e863902eb101e954911763bd47a43ac068429309830bb759aa6386969701"])
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, 0x0, 0x0)
sysctl$vm(&(0x7f0000000080)={0x2, 0x8}, 0x2, &(0x7f00000000c0), 0x0, 0x0, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x6})
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x4, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
sysctl$net_inet_tcp(&(0x7f0000000400)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000440)="0118fff60f9efcffff59657f16e66e9f97069846a2fa74080000005188f322a33afd5604c4aa10930ed14b10abb7d8414191ac619377bfd683086fc78a1dbb0991fe8a3722e3138bc29c66d273252d8a49f0b62ab57ed767755d45d5ae117a6d1bb9e7734421cef62cac0800000000000000059989c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebb2481ea2de4000000aa1e20a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66c878f486f7e59259a05bb689915b90980246fa85c22ad90d4e3776895066d2bee08f70300000000000000bd0100fb00001c3f30e790fd157cd0f6ac73547476b2a766825175bdbda013a1dc24244a0600000000000000000000000000000000000000000000000009ebffffffffffffff7f3fcbadf25485d5ca4287ed75b0db89c123fce0cbfb668a58f19f470b6569b58284c0cd71f0e8d87e5503c72b7d1b3db57458000000008e1f2e111835a6b788d5ff5256df13b59edcc163f269e55e741205360c9d2e43575809838bebf4e71b1393f42a216e6f1b67f8aaedd53dc24ceb12d50d3fb41b2732e741d0ea739f0ceb63553689a46145a280533ec0d29de081568214f857ebd1f1e41bfb9a21624840a96d9619e0eb108d5bb60a27d465014bd7742b7e5f4a46cb83eea6b48aeb60db0242eb2d2abfec6dc0e3b0450200b24c238f90402598ad961f9f7502767ebb569f49ec0000000000009948c6", &(0x7f0000000280)=0x210, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x8f})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0x400e970, 0x0, "7e00000000800000000000000000000200006ba3"})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "000000000000000001000000000000f843d07e38"})
readv(r1, &(0x7f0000001840)=[{&(0x7f0000000280)=""/17, 0x11}], 0x1)
dup2(r2, r0)
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "f45cafb367639c323fa5184b5ab5b043f9e32331"})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x7}], 0x1})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1}, {0x5}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000280)={@empty, @remote})
syz_emit_ethernet(0x3e, &(0x7f0000000140)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a4f7ff", 0x8, 0x0, 0x0, @loopback={0x0, 0x2}, @mcast2, {[], @udp={{0x0, 0x3, 0x8}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000240)=[{0x6c}, {0x1c}, {0x6, 0x0, 0x0, 0xdd7}]})
write(r0, &(0x7f0000000200)="3c9ebbd755feff969613ba3e1fd0", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x800003}, {}, {0x6, 0x0, 0x0, 0xfc}]})
write(r0, &(0x7f0000000000)="3c9ebbd555feff969613ba3e1fd0", 0xe)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080), 0x1)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
connect$unix(r0, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
connect$unix(r1, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r2, r0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
r0 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
shmat(r0, &(0x7f0000001000/0x2000)=nil, 0x0)
mlock(&(0x7f0000000000/0x3000)=nil, 0x3000)
msync(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x4)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xb, 0x0, 0x0)
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000001700)={&(0x7f00000006c0)=ANY=[@ANYBLOB="721897e624158600"/18, @ANYRES64], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x8002, 0x0)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0xfffffffffffffeac, 0x0}, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffdffff9, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
ioctl$TIOCSETAF(r1, 0x802c7416, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60acaca1f2844dd730ae78b0e94daa6340f332c6136fec572c091ec964f479d3f09601e05a4265ae5f4d3a70b8530f85f78b162764959532374a5f97ddac09069dc842ee5b6b470f0a2c13de93943f58c029f6bcf437d67a29db534b1221fd5256cba8ba8535737417084659d7f6ab02ab49954c6a070955682cc9dba2931d55a413df999446fbd9407aaab9e385a161f802553f02b1e823e10d785547a7acfe8c11b040de1a30bddc771c1adf40bfb83be91ade242f0a5330f89791d6166e038cbe7f5d719bf1c9d0d9482f436fe94ce6637c899cdb4027cdf11af7c3d3662695d53198228546d6800ac6a6d9106e682c0c96e510ed7cb32772008f345f19a39743eed6db45bca832b6164801181f213a793ebb0fdb2670fc204462dd4c3ef62ea40c007418ef216e5c8be4fc31a982fc12166b472e53b1b4171e7c4a4f96b2d0bc9d72e34b455e6a2833e7f4402db98af3d1c522715c1e26e23066014f1b80741f9b37194e9a74df15837b3bad80bf2cc2f30f14388c68e6d72c6dd465795672595b826b4c31f4162f128035517b0003696bf69d4bc74cffb0b74183ff539c4562d25ca0c6edf7286790e23e37a144f34615d42bd2be5ccbaf4856c11188e1d0564a18beba578e822b6bb1da3322ab17982f3e961251692c659d0685a01be2b0908b51991ce84ea5bbc07ee3989a049a1d51e727c6fd02cd9117d17f36bb4790fe8fa0ccad53430db28c2792e2d3be8dc96d404ea501671e78f0a222a89f540c4d776612efd9f3decc15316e39b3cb6efe90ead64d346d4d40c", 0xfffffd87}], 0x1)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0884450, &(0x7f0000000240))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x5, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x2, 0x0, 0x0, 0xfff}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3f}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x22)
pipe2(&(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x0)
dup2(r0, r0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0xfffffffdffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
mknod(&(0x7f0000000000)='./file0\x00', 0x1000, 0x0)
acct(&(0x7f0000000100)='./file0\x00')
acct(&(0x7f0000000040)='./file0\x00')
unlink(&(0x7f0000001580)='./file0\x00')
open$dir(&(0x7f00000000c0)='./file0\x00', 0x20380, 0x0)
execve(0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f00000000c0)=0xfffffffb)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x3}, {0x3}, {0x6, 0x0, 0x0, 0x7b98}]})
pwrite(r0, &(0x7f0000000300)="c8dcafccd1a6f48da693157343cb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSDLT(r0, 0x8004427a, &(0x7f00000000c0))
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0xd, 0x0, 0x0)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = fcntl$dupfd(r0, 0x3, 0xffffffffffffffff)
r2 = fcntl$dupfd(r1, 0x0, r1)
write(r2, &(0x7f0000000100), 0xfffffe5d)
madvise(&(0x7f0000725000/0x1000)=nil, 0x1000, 0x6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x4, &(0x7f00000000c0)=[{0x10001, 0x0, 0x0, 0x1}, {0x3}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000180)={@random="27aacea59068", @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0xfffffef8, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}}}}}})
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1002, &(0x7f0000000000)={0x1000000}, 0x10)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffb4, 0x0, 0x0, 0xfffffffd, "6fc60900e4000000001200"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0x0, "d6e696d83fc4209e414d70000000d0c73d6500"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050200000000000088000701000000000000cea1fea7fef96ecfc73fd3357af96caa0416e74f376336acf00a7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657ae00000002000000000000020208a371a3f800000000000000000000000000000000000d9b51220799d716f97b000000", 0xb1, 0x0, 0x0, 0xffffffffffffffbd)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x402, 0x0)
r1 = open(&(0x7f0000000200)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000480)="90c3fe67eb586898600425f2f573e0d1ac83c18d00c8e22066c0d389fe895a974c8d45aaf9a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3214ed85fb20e088c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e32082cec454327b6a1522c332ea628b8cb672e9e724780100000017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b28020000000000000000c1570f94a3e3020a", 0xce}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8002, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
r2 = semget$private(0x0, 0x4, 0x5b4)
semop(r2, &(0x7f0000000040)=[{0x2, 0x1, 0x2400}, {0x2, 0x64, 0x1800}, {0x2, 0x402, 0x7fc}, {0x1, 0x3}, {0x0, 0x2, 0x1800}, {0x1, 0x1, 0x800}, {0x2, 0xfffffffffffffff8}, {0x1, 0x800, 0x1800}, {0x2, 0x2}], 0x9)
semop(r2, &(0x7f0000000000)=[{0x3, 0x17, 0x1000}, {0x0, 0x8b45, 0x800}, {0x4, 0x1, 0x1000}], 0x3)
r3 = getgid()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r5=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r5)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r6=>0xffffffffffffffff})
getsockopt$sock_cred(r6, 0xffff, 0x1022, &(0x7f0000000400)={0x0, <r7=>0x0}, &(0x7f0000000440)=0xc)
setreuid(0x0, r7)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000000c0)={{0x80000000, 0xffffffffffffffff, r5, r7, r3, 0x2a, 0x7f}, 0x5, 0x7, 0xcb})
fchownat(r0, &(0x7f0000000040)='./file0\x00', 0xffffffffffffffff, r5, 0x4)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, &(0x7f0000000080)={{0x7f, 0x0, 0xffffffffffffffff, 0x0, r5, 0x192, 0x8}, 0x5, 0xa086})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000002})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
close(0xffffffffffffff9c)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
ioctl$BIOCSRTIMEOUT(0xffffffffffffffff, 0x8010426d, &(0x7f0000000040)={0x100})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc00c7006, &(0x7f0000000040)={{}, 0x0, 0x0})
munmap(&(0x7f0000ff3000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000ff5000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x81}, {0x4c}, {0x6, 0x0, 0x0, 0x800000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="7cdc3fc0aa17dddf7830faa1aaab", 0xe)
r0 = syz_open_pts()
pipe2(&(0x7f0000000000), 0x0)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff})
r2 = socket(0x2, 0x2, 0x0)
connect$unix(r2, &(0x7f0000000180)=ANY=[@ANYBLOB="0302a195c3"], 0x10)
dup2(r2, r0)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000080)=0x43cc0, 0x4)
r3 = dup(r2)
sendto$inet6(r3, &(0x7f0000000040), 0xfd4d, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f0000000040)=0xc)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000080), &(0x7f00000000c0)=0xc)
r1 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r1, 0x6, 0x2, 0x0, 0x0)
setreuid(0xee00, 0x0)
r2 = getuid()
setreuid(0xee00, r2)
ktrace(0x0, 0xf4f9d909a797d62d, 0x40000408, 0xffffffffffffffff)
preadv(0xffffffffffffffff, &(0x7f0000000040), 0x1132, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000200)=0xc)
fchown(r0, 0x0, r2)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000640)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
truncate(&(0x7f0000000240)='./file0\x00', 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x3080002000, 0x1403)
open$dir(&(0x7f0000000940)='./bus\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x1c}, {0x87}, {0x4000006, 0x0, 0x0, 0x3ac}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x15, 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x3, 0x0, 0x1c, 0x0, 0x7fff, 0x0, 0x0, 0x0, @rand_addr, @multicast1}, @udp={{0x3, 0x0, 0x8}}}}}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setreuid(0xee00, 0x0)
r2 = getuid()
setreuid(0xee00, r2)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000640)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x86, 0x3}, 0x9})
recvmmsg(r0, &(0x7f0000000500)={0x0}, 0x10, 0x0, 0x0)
r2 = syz_open_pts()
dup2(r2, r0)
sendmsg$unix(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x61}, {0x5}, {0x6506}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)={@random="6aef0376194e", @local})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x5c}, {0x2c}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@remote, @broadcast})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f0000000980)=[{&(0x7f0000000580)=':', 0x1}], 0x1)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x20000003, "7e00000000000000000000000000000200006ba3"})
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "f722aa4b0e832c10184700dd47c8e9d2be670991"})
ioctl$FIONREAD(r1, 0x4004667f, &(0x7f0000000280))
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
writev(r0, &(0x7f0000000680)=[{&(0x7f0000000400)="d0eec5d16c31d27f7e223807715af087ff7f0d", 0x13}], 0x1)
poll(&(0x7f0000000180)=[{r0, 0x4}], 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80606954, &(0x7f0000000300))
mmap(&(0x7f0000d0c000/0x3000)=nil, 0xdf2f3fff, 0x0, 0x1012, 0xffffffffffffffff, 0x0)
mlockall(0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x80, 0x0, 0x3, 0x0, 0x2, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe], [0x9, 0x7, 0x0, 0x0, 0x0, 0x4], [0x25b, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe], [{}, {0x5d5}, {}, {}, {0x0, 0x0, 0x0, 0xb0e}, {}, {0x0, 0x0, 0x0, 0x4}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
r3 = getppid()
r4 = geteuid()
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r5, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x5}, {0x3d, 0x0, 0x3}, {0x6, 0x0, 0x2}]})
close(r2)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pipe(&(0x7f0000000080)={<r6=>0xffffffffffffffff})
r7 = openat$klog(0xffffffffffffff9c, &(0x7f00000001c0), 0x40, 0x0)
sendmsg$unix(r2, &(0x7f0000000240)={&(0x7f0000000000)=@abs={0x1, 0x0, 0x0}, 0x8, &(0x7f0000000040)=[{&(0x7f0000000100)="1fd09487ade94a23c6300a12e37d2a295a721e14bb60d9e402fbb8b22da2fb097b91576e9824e2adbd661eefdf1dd86e179fbbef308d19fd073b237486f63bcfea1a7cf333c0a93ed4d5bbfc58cdbb15c6da42812440b2ae68f40bb36fc398024dcbf8e3625f2ed28ba863878ef9bfd8a1bdbe70d8f6134b45c4b4018d7d2bcb3b9a4887eb1f20e7e46ea0a85a3c257d775f6b81daf4b30b3115561808ce182735cbebc6bf89c3", 0xa7}], 0x1, &(0x7f0000000200)=[@cred={0x20, 0xffff, 0x0, r3, r4}, @rights={0x20, 0xffff, 0x1, [r1, r5, r6, r7]}], 0x40}, 0x8)
poll(&(0x7f0000000200), 0x3a, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r8 = socket(0x18, 0x3, 0x0)
r9 = fcntl$dupfd(r8, 0x0, r1)
ioctl$TIOCFLUSH(r9, 0x8080691a, &(0x7f0000000300))
open$dir(&(0x7f0000000000)='./file1\x00', 0x7fe122c4bf6597bc, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
rename(&(0x7f0000000200)='./file0\x00', &(0x7f00000000c0)='./file1\x00')
unveil(&(0x7f00000001c0)='./file1\x00', &(0x7f0000000340)='x\x00')
unveil(&(0x7f0000000100)='./file1\x00', &(0x7f0000000180)='r\x00')
symlinkat(&(0x7f00000000c0)='/', 0xffffffffffffffff, 0x0)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000000)={0x0, 0x84b, 0x403, 0xd, "cf11050038050000000000ea3cc0a48000000800"})
write(r0, &(0x7f0000001200)='\r', 0x1)
sysctl$vfs_nfs(&(0x7f0000000100)={0xa, 0x2, 0x1}, 0x3, 0x0, 0x0, &(0x7f00000001c0), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x44}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000180)={@remote, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="416494db2363", "", @empty, "f9be501d25e4172bd7c6287b90ec8616"}}}})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000100), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000540)=0x8)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000580)={0x10, 0x2, 0x4, 0x0, [{&(0x7f0000e10000/0x2000)=nil, &(0x7f0000ee9000/0x2000)=nil}, {&(0x7f0000bea000/0x2000)=nil, &(0x7f0000cb2000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000bbd000/0x3000)=nil}, {&(0x7f0000f6c000/0x2000)=nil, &(0x7f0000cff000/0x3000)=nil}, {&(0x7f000087b000/0x2000)=nil, &(0x7f0000800000/0x800000)=nil}, {&(0x7f0000ee1000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000f8c000/0x3000)=nil}, {&(0x7f0000aef000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000914000/0x3000)=nil}, {&(0x7f0000f81000/0x1000)=nil, &(0x7f0000ebe000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000c62000/0x1000)=nil}, {&(0x7f0000a17000/0x3000)=nil, &(0x7f0000da2000/0x7000)=nil}, {&(0x7f0000b26000/0x1000)=nil, &(0x7f0000825000/0x12000)=nil}, {&(0x7f0000d22000/0x1000)=nil, &(0x7f00009fc000/0x1000)=nil}, {&(0x7f0000a21000/0x1000)=nil, &(0x7f00008c4000/0x3000)=nil}, {&(0x7f0000bfd000/0x3000)=nil, &(0x7f0000c28000/0x1000)=nil}], ['./file1\x00', './file1\x00', './file/file0\x00', './file0\x00'], './file/file0\x00', './file/file0/../file0\x00', './file1\x00', ['./file', './file', './file', './file']})
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140))
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000080), 0xcaa, 0x0)
pwrite(r0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
dup2(r1, r0)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
listen(r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r2)
poll(&(0x7f00000000c0)=[{r2, 0xb222a6c9fa1b3fec}], 0x1, 0x0)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x2c}, {0x5}, {0x6, 0x0, 0x0, 0x58a}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3a}, 0x2, &(0x7f0000000080)="37b9f0a7", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x0)
chflagsat(0xffffffffffffffff, 0x0, 0x0, 0xfec16a96f6367e7f)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x2, &(0x7f00000000c0)=[{0xb1}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0xc0e99db6de761f86, 0x0)
r0 = open$dir(&(0x7f00000003c0)='./file0\x00', 0x2, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r0}, 0xfffffffffffffff8, 0x25}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x30}, {0x4}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
pwrite(r0, &(0x7f0000000140)="676ee3450356e770f593c59adfc7", 0xe, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
chmod(&(0x7f0000000300)='./file0/file1\x00', 0xd7)
setuid(0xee01)
mkdir(&(0x7f0000000240)='./file0/file0\x00', 0x0)
rename(&(0x7f00000002c0)='./file0/file1\x00', &(0x7f0000000100)='./file0/file0\x00')
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
r2 = getuid()
seteuid(r2)
rename(&(0x7f00000004c0)='./file0/file0\x00', &(0x7f0000000500)='./file0/file1\x00')
r0 = socket$inet(0x1e, 0x3, 0x0)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x7}, {0x25}, {0x6, 0x0, 0x0, 0x14d}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x39af}], 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000040), 0x14)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069a6, &(0x7f0000000300))
syz_emit_ethernet(0x32, &(0x7f0000000000)={@local, @local, [], {@ipv4={0x800, {{0x9, 0x4, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @broadcast, {[@noop, @timestamp={0x44, 0xc, 0x8, 0x3, 0x0, [{}, {}]}]}}}}}})
sysctl$kern(&(0x7f0000000080)={0x1, 0x3f}, 0x8, 0x0, 0x0, 0x0, 0x34)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x10020804000, 0x200000]}})
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x12}, 0x4, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x7, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x20004000, 0x200000, 0x0, 0x0, 0x80000000]}})
ioctl$TIOCSBRK(0xffffffffffffffff, 0x2000747b)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000340)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
r1 = openat(r0, &(0x7f0000000040)='./file0/file0/fi\x00', 0x40, 0x4)
r2 = fcntl$dupfd(r1, 0xa, r1)
ioctl$VMM_IOC_READREGS(r2, 0xc2485607, &(0x7f0000000180))
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r3, 0xc0104451, &(0x7f0000000140)={0x80, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x20, 0x408})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt(r0, 0x6, 0x4, &(0x7f0000000180), 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)="919b5c5e5c069d14288be166", 0xb08f, 0x1, 0x0, 0x0)
r3 = accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r3, &(0x7f00000000c0)=""/166, 0xa6, 0x802, 0x0, 0x0)
mmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x4f18131414a30435, 0x10, 0xffffffffffffffff, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffd000/0x1000)=nil, 0x1000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x80}, {0x45}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r0, &(0x7f0000000000)="8b", 0x1)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x80}, {0x64}, {0x6, 0x0, 0x0, 0xffffff00}]})
write(r0, &(0x7f00000000c0)="2cd1ecf725b86a7c0b4fc7aeb545", 0xe)
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x4}], 0x1, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "f540b2ea0000000096b8911f121b3bd89a53e1bf"})
semop(0x0, &(0x7f0000000000)=[{}, {}, {0x0, 0x0, 0x1000}], 0x3)
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xc0284459, &(0x7f0000000240)=0x3)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = dup2(r2, r1)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r4=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r4)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
setreuid(0xee00, 0x0)
dup2(r2, r0)
r5 = getuid()
seteuid(r5)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x28, &(0x7f00000000c0), 0x4)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
bind(r2, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
dup2(r2, r4)
r5 = dup2(r4, r3)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
mlock(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
minherit(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
dup2(r0, r1)
writev(r1, &(0x7f00000011c0)=[{&(0x7f0000002200)='e', 0x1}], 0x1)
readv(r0, &(0x7f00000034c0)=[{&(0x7f0000000180)=""/4096, 0x1000}, {&(0x7f0000001200)=""/4096, 0x1000}], 0x2)
r0 = semget(0x0, 0x0, 0x0)
semop(r0, &(0x7f0000000040)=[{0x4}], 0x1)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, 0x0, 0x0, &(0x7f0000000100)="2a5b8bb8", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x14}, {0x5c}, {0x6, 0x0, 0x0, 0x20}]})
pwrite(r0, &(0x7f00000000c0)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
sysctl$ddb(0x0, 0x0, &(0x7f0000000140)="a0", 0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0x9}, 0x2, &(0x7f0000000080)="92fe2438", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x4)
mkdir(&(0x7f0000000040)='./file0\x00', 0x0)
openat(0xffffffffffffff9c, &(0x7f0000000000)='./file1\x00', 0x200, 0x0)
rename(&(0x7f0000000080)='./file1\x00', &(0x7f00000000c0)='./file0\x00')
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004, 0xffffffffffffffff})
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x1, 0x0, 0x0, 0x2000300010000})
flock(r0, 0x2)
r1 = getpgrp()
getpgid(r1)
r2 = getpid()
ktrace(&(0x7f0000000240)='./file0\x00', 0x0, 0xfcfc96ac5f786194, r2)
fcntl$lock(r0, 0x9, &(0x7f0000000080)={0x3, 0x0, 0x6, 0x0, r2})
flock(r0, 0x3)
flock(r0, 0x2)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
fcntl$setown(r0, 0x6, r2)
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x44}, {0x87}, {0x6, 0x0, 0x0, 0xfffffffe}]})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f00000000c0), &(0x7f0000000140)=0xc)
write(r3, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
flock(r3, 0x0)
fcntl$lock(r0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x2}, {0x3c}, {0x2c}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@local, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast}, @udp={{0x1, 0x3, 0x8}}}}}})
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
chdir(&(0x7f0000739ffe)='..')
mlock(&(0x7f00005f3000/0x2000)=nil, 0x2000)
mlock(&(0x7f000071d000/0xe000)=nil, 0xe000)
sysctl$net_inet_tcp(&(0x7f0000000180)={0x4, 0x2, 0x6, 0x14}, 0x4, 0x0, 0x0, &(0x7f0000000280), 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, "00000000000000000000160000eb00", 0x0, 0x4010000})
write(r0, &(0x7f0000000040), 0xfffffec2)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x20003f, 0xfffffff9, 0x0, "c706b288ad185e914d9415254dd09834302dbb87"})
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
r1 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x4)
shutdown(r0, 0x1)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r1 = socket(0x18, 0x4001, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000000)=0x1, 0x4)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000040), 0x4)
sendto(r1, &(0x7f0000000100)="a6", 0x1, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0\x00'})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x9, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x2, 0x408})
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x2, &(0x7f0000000100), 0x4)
r0 = syz_open_pts()
ioctl$TIOCSBRK(r0, 0x2000747b)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{}, {0x15}, {0x4406}]})
syz_emit_ethernet(0x4a, &(0x7f0000000200)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "2bd4ff", 0x14, 0x0, 0x0, @empty, @mcast2={0xff, 0x3}, {[], @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = kqueue()
fcntl$setstatus(r0, 0x4, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000100)=0x4, 0x4)
listen(r0, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs, 0x1c)
clock_gettime(0x3, 0xffffffffffffffff)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
r1 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r1, 0x1, &(0x7f0000000140)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x140}})
shmctl$IPC_STAT(r1, 0x2, &(0x7f0000001300)=""/4096)
sysctl$kern(&(0x7f0000000080)={0x1, 0x4d}, 0x2, &(0x7f00000000c0)="0fbf86e7", &(0x7f0000000180)=0x4, &(0x7f00000001c0)="4e2c548c", 0x4)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
bind(r1, &(0x7f0000000980)=ANY=[@ANYBLOB="2d0102"], 0xa)
dup2(r2, r1)
writev(0xffffffffffffffff, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x62a, 0x0)
write(r1, &(0x7f0000000180)="b0a2e042310eee2586d2344eed7d3e9f655b68928d32156383de03f8f52d5121069f71640b861992e1c8dd3b41d8ec053835fa5df90254511a4fdaad58d5d9f49f45d6fe57bd3ba6762b20699352954f17f0a060a97f8502dc4eef2b8ef96da7029fe5576ece4f6acf465d683d8d19acc05bfe874af87b1fb932398ed0ae86b036e29a10dadc6896d2320658753318ace8adb7aa1136ef8c2de5d24b63ee136631f7c0c0293f876d8264c92edf052d6f680096651e7c3804d3586774d8f36aff057cb9721e653719fff7c6ab28fde8b172e5819beb016dd4badd773f5fc0415f10edf1c5147589811e58f5b0304e2b6794ca138cd66a4f337ff79d41f48f9f79e21ee2d5e44aefbc558e52d3f38e5b71fcd53d3953bbfca6a42bad8bed5a2ad0fb8bcb3326a1585c8b6108cde279c0251f03ed7dacdec21be75708c1d5a2147dd300366fc1fb6ca8f154456ad83a6dd1f04b550c8ee4cb7fe285b91dbd02feebbd76352e98efd3fffe248cef5e644b68dc", 0x171)
write(r1, &(0x7f0000000140), 0xfe8f)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
write(r0, &(0x7f0000000280)='M', 0x1)
mkdir(&(0x7f0000000240)='./file0\x00', 0x0)
mkdir(&(0x7f0000000140)='./file0/file0\x00', 0x2d53b6584dddbb69)
mkdir(&(0x7f0000000180)='./file1\x00', 0x0)
unveil(&(0x7f0000000000)='./file0/file0\x00', &(0x7f00000000c0)='c\x00')
setuid(0xee01)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f0000000080)='c\x00')
unveil(&(0x7f0000000100)='./file2\x00', &(0x7f00000001c0)='x\x00')
sysctl$kern(&(0x7f0000000240)={0x1, 0x2a}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000280)=[{0x6c}, {0x5c}, {0x6, 0x0, 0x0, 0x7ff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
syz_emit_ethernet(0x4f, &(0x7f0000000080)={@broadcast, @random="3f5fde55476a", [], {@generic={0x8848, "7bf39a8be8d987bba91de78460238ebb7013c4cad1cacd9d53adbea898bec6370ce83e1ac59ad6d10b580620bf43757ab75155e26c4dfed286c96c942284c99e62"}}})
syz_emit_ethernet(0x3e, &(0x7f0000000480)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "5a3af6", 0x8, 0x0, 0x0, @loopback, @loopback={0x4}, {[], @udp={{0x2, 0x2, 0x8}}}}}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
setreuid(0xee00, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x3080002000, 0x1600)
execve(&(0x7f0000000040)='./bus\x00', &(0x7f0000000140)=[&(0x7f00000000c0)='!(\x00', &(0x7f0000000100)='\x00'], &(0x7f00000003c0)=[&(0x7f0000000380)='\xd1Q4}^\x11\x9c\xd2\xda\xac\xd6\xc5b\xeb', &(0x7f00000001c0)='(\\+\'+\v$,\\\x00', &(0x7f0000000200)='\b\x81P\x9c!/\x00', &(0x7f0000000240)='\x00', &(0x7f0000000280)='\xfd$\x8b\'[.#%.*{-', &(0x7f00000002c0)='\'\x00', &(0x7f0000000180)='{k@\x1d}\x7f!\\/-[\x0f\x00', &(0x7f0000000340)='(%\x00', &(0x7f0000000600)='\x00'])
lchown(&(0x7f0000000440)='./bus\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x90)
r1 = semget$private(0x0, 0x3, 0x80)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000480)={0x0, <r2=>0x0, <r3=>0x0}, &(0x7f00000004c0)=0xc)
r4 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r4, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETZCNT(r4, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r5 = geteuid()
r6 = geteuid()
semop(0x0, &(0x7f0000000a80)=[{0x3, 0x7, 0x1000}], 0x1)
r7 = getgid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000a00)={{0x12a1b8b, 0x0, 0x0, r6, r7, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0x2})
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000580)={{0x3, r6, r3, 0x0, r7, 0x1, 0x309}, 0x4, 0x5, 0xfffffffffffffffd})
semop(r4, &(0x7f0000000a80)=[{0x3, 0x7, 0x1000}], 0x1)
r8 = getgid()
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000a00)={{0x12a1b8b, 0x0, 0x0, r5, r8, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0x2})
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000640)={{0xa629, r2, 0x0, r2, r8, 0x83, 0x200}, 0x2, 0x2, 0x10000})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f00000000c0)=[{}, {0x16}]})
syz_emit_ethernet(0x36, &(0x7f00000001c0)={@local, @random="0ba3ae0a44a8", [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @loopback}}}})
sysctl$kern(&(0x7f0000000040)={0x1, 0x29}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = syz_open_pts()
r1 = dup(r0)
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x229, 0x0, 0x0, 0x0, "e3eba6b7a9c36c920995dbdd4e13b5abb733a689"})
ioctl$TIOCOUTQ(r0, 0x40047473, &(0x7f00000001c0))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000100)="a10451f61edccc00010000329446ab8d04a7a899214310590100520670003ba8fc499277210000008000000000825ef568df068fb95dcf6eff2d02e8d10000000000000008b6fddf1b863d5165ef398f414f502c21dd34e5d83a19e0c9ef1004ee4bbe6e3637", 0x66}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc110445e, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x4}, {}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x6})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000100)="0000e1ffff9b234243", 0x9, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000100)=0x3)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "fe17754d047c518155e501284a18461fc0a36bfb", 0x0, 0xfffffffc})
openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x10, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup2(r0, r1)
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f00000001c0)=0x3)
read(r1, &(0x7f0000000100)=""/143, 0x8f)
r0 = socket(0x18, 0x3, 0x62)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xfffffff6, 0x0, "01000611aef6e4c611d53300"})
setsockopt(r1, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
sendto$unix(r4, &(0x7f0000000200)="0eced7498e2c939fb798aef4b292c9c18e23ba1a08cb3a0cd88de19944b5c18e594dc555053981792118b32eead58ef5a0f2479fbca0e94a2ef1ab0d921efe9751b7ff8d464d56b159462b085dbfe6c71df591818f36c8ec161a6211b164d18c8755fcfe3fb48f71116a822d3e0d573060f6404a3e7c68b856272b6eff7363c0394b33ebf75a238b0500da9a2d89b6602fa00a781ee44dad0247a5ff26d972cbcc88e0ab4141a6ba31828d7691d35694e91e533acf4b0b30d0817d899ead960e2f09bbd2bb44d5415c6b32e314ccb44ef6465f7f8f3e89ad9ed01b41cc1ae7e709b32bdb618ca159925a3fceafc777cd56f05ef29675687fe068c90f74", 0xfd, 0x8, &(0x7f0000000100)=@abs={0x0, 0x0, 0x2}, 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x20, 0x0)
fcntl$lock(r0, 0x8, &(0x7f00000003c0)={0x0, 0x0, 0x800000, 0x100000000})
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f0000000240)=[{&(0x7f0000000140)="baf92d151c3011fad28b6c83072523da54a57616c3141020ff", 0x19}, {&(0x7f0000000180)="9306c73832db3044ea31a8537274fdd4e7319488f35d94581fe06eeb140a5a5740e51c13a88affcabf30808984c3f36374a32a34f10171ebd4ede91789fbf29f6b2e0274", 0x44}, {&(0x7f00000003c0)="749b502a047032369be025ba65238e595477ce839c0792472349997c53b5af603fa3a96ccb401bdc0500000000000000dfa200898aa27dfaa3f0988561133f7982304242cdd4add2010aef105783861448e001b340d3f054b2f7a50c613e00fa6859a002", 0x64}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r2, 0x0)
r3 = dup2(r0, r0)
ioctl$TIOCSETA(r3, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "12891c123cab9b0b90787156b1d4476cc129d5ec"})
ioctl$TIOCCONS(r1, 0x80047460, &(0x7f00000000c0))
write(r3, &(0x7f00000002c0)='0', 0x1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210], [0xfffffc0000000002], [0xfffffffffffffffe, 0x0, 0x3, 0x9, 0xffffffffffffffff], [{0x45f2, 0x200000, 0x4003, 0xfffffffffffffffe}, {0xff, 0xfffffffe, 0x9, 0xfefffffffffffffa}, {0xcfc5, 0xfffffffb, 0x0, 0x9}, {0x5, 0x204, 0x2, 0x5}, {0x100, 0x92b, 0x4, 0xfffffffffffffffe}, {0x1, 0x0, 0x400}, {0x0, 0x0, 0x1}, {0x3, 0x1ff, 0x8000, 0x2}], {0x6, 0x3, 0x4}, {0x7, 0x11, 0x802, 0x1}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8040691a, &(0x7f0000000300)=0x81)
pipe(&(0x7f00005dcff8))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="00ffff40", 0x4)
ioctl$BIOCSDIRFILT(0xffffffffffffff9c, 0x8004427d, &(0x7f0000000040))
clock_gettime(0x6, &(0x7f0000000000))
open$dir(&(0x7f0000001340)='./file0\x00', 0x383e0, 0x0)
ktrace(&(0x7f0000001dc0)='./file0\x00', 0x2, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{}, {0x50}, {0x6, 0x0, 0x0, 0x2f}]})
write(r0, &(0x7f00000001c0)="9e2128e4ef5ad665a6215565e4ab", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x4d}, {0x87}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
sysctl$net_inet6_ip6(&(0x7f0000001280)={0x4, 0x18, 0x29, 0x36}, 0x4, &(0x7f00000012c0)="a7740fc485daed7eba4844693bddadf8", &(0x7f0000001380)=0x10, &(0x7f00000013c0), 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="620201"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r0 = socket(0x2, 0x2, 0x0)
getsockopt(r0, 0x0, 0x9, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d733c4f6ff45e400"})
write(r0, &(0x7f0000000180)='X', 0x1)
close(r0)
syz_open_pts()
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f00000001c0)={0x0, 0x0, 0xffffffc0, 0x0, "9b4f0e92fa5a2069641288a0606db0ccce620da4"})
fcntl$setstatus(r1, 0x4, 0xcc)
readv(r1, &(0x7f00000005c0)=[{&(0x7f00000000c0)=""/145, 0x91}], 0x1)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f0000000180)={0x1, 0x0, 0x4, 0x0, [{&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil, 0x10000}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffa000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000fff000/0x1000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[0x0])
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
ktrace(&(0x7f0000000140)='./file0\x00', 0x0, 0x0, 0xffffffffffffffff)
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
pwrite(r0, &(0x7f0000001680)="83d82a329c0a755d65048002d942dddd1034888165d08db8869446adb290ca2668772534d04db30ce997b1f4d1e7377aae8bc32f95a10baf493954e773b4dfa8eefb67c65dd8afd894cdf641e9849dfa7fe313f6f0f4deb3e4498c304196ca5eecbc0537c51901e6ca4c3a173591aca7ea46c8f235c6ad4adf520265d3189b8c8d72d730854acd173cdc6bb4da195b2d52cd0b9bfbe8341420efddea7febbd80deaec6ae74c2c1f1fab0ec494a49487a9a50356921600aad75633554866d85b1232b9793506748d897cfec11d0e39ac90e4b55dc150511ab2186789834a09d36ac9dbf546e00f933a5c9ba3c6c7bc73f3a24745545c423de87f301d07189d5884b47767998df9f71a66f25efa01cc65696dcf71f31b2b5991845914366a534119b31a2d9b3ba6d98d27b8ee348649bee92b89a8b8a41dfe701c0faa2bd65a89da593dd5d113b635788042fe0ad39b3ac215e795b94eafe8a533841c44783ed3cb081edc40e3e7c21ba3aa431b9ba12e2ee2d8fad25953a943babd5bbf8396f8d3bbd2a257d624856ea23872db3ab97a15957aceaec624d14f0b6d1985e559bef1a3e4d7721c47cfca5c5b63c0dfa1829408f14efbf12e6b608bcab354a74146e003d96822efd494549550b69d71eeeaf798f111ae676c7b490fb6ee76e55aca3066f2881fcf87895bd856f357a55fe71f7e35e17ad2e422dd37b4f7936f212621d96fa11d4f859b8c46027028489dd767e84acc618d58b65669a0e1c37366c86fcd9890037c956ae707ff2b431933e6d02cf948f737bd4c1eb0bab63f83117dd58e1f8c004660dd8c25e3d7f114c82ff2090159d0a3ba960a9c93ec62941ee37844b9015660947648fd43b1a062560fe71390252949bf602b600e757c283e17b5a136160fbd8a1242beca0a7a22fb31d19ffedbfc78912918f3e0afbe29353869cc01e09c29c3be1e2e7f8ba1cfb501bd32d343e6cff78604631f6f8d3d75804e646c6d909c1f9b10342d3e1c6f71bfeb5c930cb48dc4728984bbbdf8172aa7c2250bf9665b48e0390fcb04900ca83ff70110b2369310d98849b6fef2037d5dd20bd91d3d96f1f3dddbfc49ac37f6cacb253001b71dea5cf1bfab5ae6f093a01a21517fc2d61ae233453acde00d2007ad251b59f0548757834e8182755cfc9ea0bc223038f6dae798ba54717debf715102cc373d84284595ccc42fae09e6d4ce46960aae7ca9ac50d0ceb8dfe2ef9784137c6bc1c81ba360a92018a1c03b36fc301313b60d0234c97e45e330bc5d717334f49eeba1dac0a2e5268f5d0c4bcc58b1f65cfffa2ae19b96bf1fb834963f75e4161cb2f4c1278e1a23ab3f3e0938019cc0f2437ce921329beb63afb3e6ba473275f4ec6e4455345cd9fecb1a368557bbdfefae2e9132324c20796f8a191304d4ce3babf9b2c97eb961940e9e9cba9e3dbe892ce8e62db25d3bd77cea3961166c34ad51e5c02528f988f50e0fd7ed6dea48d52e19bb23ef2526638ccd5eb9b390b330fe72f4153f1ea33b1b33ad7507eafad2b1977ddd32fda41d927c5440c0ce6ad9d032d29a81f4db1f7e0af0218ee8fcdd0b36dee00af6f4c7b338e65d5353f25c2383080f13ebdeebf0164e76945d9d7ca38b1fc914e09cd44935fea839cbc4a38732e1c31fd42aa8dc62d33bca542cb5a352afb3820022a5a0aa8f4aece9c694673aa48a8a64d58548f923cdc3c0742c82bc3c26b663553eb2649f08a93a9f1386df72ecd832834225339461155a93d5efc510688121fc80a0facd539e56a99fb6384123c171561b5c02de9fb0c4f2d8fe483493ab66b11cb10aefb78dcb72fcf52c604773e45333aa54423e475d330eb613df71286bcc6abb062ad5a2371b141e0b61e3bc3ae006e5fe78dadc044f4a95b200f8a63bcfec3ce5b6ace618f51e3ce2a51562cd6d933ec95231617e603db33f520f7741b875e2fd779dd4e2e8184fe9dcadfb04aba237bd73e800686aef8a84312b436697bcebc316c948e5133c82ec0098fefcf9c32a18ffe9b4fbeec833f4e05a4fc51c42879bd3253d968b6a3acc97447f472fd1af877f5e9183f4741011e6f45b591db4683891a8e13f2c36f59d609dd0d77bf7feed9b9b38bd4e562189133c6a1d16b3fe83a5079509f97f77ca85b24da7b05a8decc7260494b3a89ae119dcb981900f50f2415384c7e0a5cdc06fc3adcf12f15abb42f0544ebeb679de4e93e25f4703e6900f8970bc511f7dfa6914cd01699784f18b82918226c943b03d932fdcb34f90fe127cafab40fb43477c7fb107d0dd8bd89f54eaa4a22207221058739ea5ea7d782b689d962bb22373657a080036c784b9ebe5881db817310a0dcbd22267376f95772778b39868627f9a7149e7a1b2f098391db443b8c4d4ffc93296a21a8b407e59bfb71ed4bffa058f624f2ae59a4c78bd620e3a7497a118aca13a730ee30a96e4d2b184e017f81820fadf39440ecab7ae25067ebcd0c11467757d6cacc52cfd96d466fd61d588024c331095c6f216d2be6f5cc1bf9dd1267d08b3704d51041c37b30fca291ae23e6ca3418e07d90873bf0514f9f462620ae62626030dc455f9c3ec2af3624495eccb26c565a16c377bf124e4049241e48d63eb36c9820a92514529d93a21dd0184d3ff0486f70f526e9ec340a1833e4378db28e6632482e87a902d205ca1984f686a843cc6d2503ec0b672c5d1a4e1c502e48ee6d46af2cdaf8f8d1ca332d62293eec906b3284d35e870532cc8fe2d1b627740ac19d3dd97a7452036aa62f5a779152c000336595592afbb3e175b87ff4c1506a407a0c898c4169245f753c4807dc6a8a6e51645a17d7ee53a1c746b9c2a43646076f52fd2e080f70b19ba3fbcc1ac65717630d6dd2be74ad055c594ca695d9cf92dd74e8eeaf7fc05e82cf969696bfd4fe342fcd8837bc837033e392cc933c7fc5f426f119a1ba145412fe900c3a7b6fda1fa0cb2f195fba9794a83cea9283544dc19a0efdc83872edd84090c6940b192e6230ff659784abe8de2bbbf1b608d809595470293c2228a214751feb33b89f7f8547f3d2a6ae47bde121a779dfb4de383cab8e14d4a272f17f021dc2e17b18f4f6ceee45832200f436d758760261da6b902b94b47641fff6cc3427598e229487df75fb637fc2fc4d8cc6918b8d288caa6edb3d180fb4c5169ca7efa64c71d07dbcf258183a99eaffd0e85cf8e4998cb0e1c86db18a4f799d385d6ea379b8d4b0229e5c80bfe343ac4de8f3f86f442088c02705437eb9e68a415c09a5acbacc6dec824c64c4d0636797fcd12160ba5715ec74c93bd562d4d181b37524cc31200406d0d26bfbf211f0211f1fd68382523a966260949c5fcf80b4fd2861bdc39cb0bcf3072ae4cdf49ed7b552f8a72f421e037282b2d0d8714e7a8c5f92bbea21c876e9ebbc199d61b7e84a95be4d9618c05bda979001695d8dd23a80642c4c1b285f00134d3722e2cc259d5af40194d7b115aff4029820b5a31216ff95a5f2ba39dc95428e90aa4c183aa07ba50c0c88c6dffdfd946641c8c66711bd2823c0e4f9cf72c7488f3c11ef54b8f81dff2adf271ce9dd41111c642e1d43118bd584f4bf45b526e4b6cd0bf9701a139627748a71fe0c08c0d2c34de9db9860954b2f5463ee6580303f329b6c42c140cb06170b466a660e9ddde92fd8a3e004bd8beb18b87a468ca11a29149c183b76196b12489f67040eda7b6cb3bda41cdb3aaceaa9c53804452f0949552be9f1a3ce3b7d566a2595e1a6aaa52c875e7e4bf9b87c2d764dd10aca7f6458b5321d48f2cf3bb015ef77f38d662de174b67856c039bd7038611709a0efcc00f2261f1512f78099dcc25e0deb75aa00cb0cb653eeff1ac349f215159b87340983dece08a5ae8e0c138e5cbe8eb0c95c608d4a9baf6d9b7c1e0ec3d0a442be0fb9f81e8b9cecf20911a55d87abeb023d80e1727be999182fd5f60b2f60731210ab0565b913fdafe094eada69ad26b6c3d83d976391b8bc0c59e7e97f8883df10dab07d13edbdd7d9c33f2b7d8fe50449280432779d4ce684b848c28e4927c8d8f0b0f0d8e947189bfa70d80816e083b64cf986eeefc24fd27b2dc384cecdcf18f19698053a7edd7379fc4d47c3d8ffd3a677e3a7407907db42ec720aa6770e1cc4743f9faca40f8209fb070cd02d4445da68a7320533ff3fc6f6c1d3ff8f882e1283c65eda528bf8069ad1bb9600bf5cb5ef5e70d2e6843146dfc978e2a96a38d8806fbefb0e3138d6cc74c7e664088274003e60c49fc04682fd7a08d82c127144ab2308e945865bc7e8a663d20da03bd1f48882a7e8f82a5821b89abeceddde531e3c668d8e60c94585fa1dc78128c522b1f50b6f16a7d9cd2a3d690a4772cefe78a94c7f9e4172dc57d58de5ca5ca577ec7d9537be92d1a93606640ccc06e3ade10c5066c82c1da19216139f559bf6f633d28cae6e4bd7e1afa7ccbee3fc8d36670d1d06644cb1c077369467821bb8e489f72d5069d33151df7c8d76f6746c1677b8bebd57845e45a8fb0fd88f96d6b0a488ed1b97bcb80c5087b826f931e06e05dec0ad278c9ea6db05ac5f6c3335d22f6ef0fbc4fc51300aeff0c4df65db36b7831cbc404cfe56dcb1602387d5f18746cc6967673bb776a7c73f475accbdc48149313ff04fe635faf0f77f061d9737bcf1f33296b5beccdee25753ae1e9261e1199b7a2872d4eca62fa41068cfa8eb19722b659851babb5a9b85a50b8abca3f0ca9aa9ed12bb6829e8cc19c3a3d34c391458aa0b0a5f81a02879c093ca169d86780a7239b7b476f6bda25f1f0997d5105ce5e97ee80203acc17686a16636fc6bece67a4e5ed0c2cc1944e365a7c4bacdca9ec5b260a6a9b9e6cf184692e66951add4eb284396708d5d0c728b3f9c7048ff560b93dc3c970827343791688797b17d72343433b738d458ae98baaf8ed9e4e4ce82ecf9f7cc6a5fc46a1e8534dc7a34a97fc938764d7d52a3c9b4a31c432a6ed395b42b8fdad8d757d721a49a75c6e6790d76f935b28db4976c8ba097167bc35e753f3016ecf7def0770724202afe1bdc45ce8ecc599e8a885802fac471b061ec81fe69b9a4a62f025d8a91a8294ac86d39a6d12877302281f9ad9e0ebcd2518f2a7a2395b8d222b0e83c44e87e6f41fdae2066d24828040a8760dfac1b16092ff36a540fd63f917c713ced5cb60186ea645a483d87fdca8907af7688f8e1674c0d12281c0af025139c6624964d541a3a2d07353d0e8f37cc8974ec0ad2d6f6d5937e09eccc76f159c8796eed7d7ca9661e27a18ddebd2bc689c3886ebb7a3e1bd0800c1566e09117829537d5e5d6f1fb22cfaeca7f02fec74675c769cec3978503530a9a45ba0ad9aa3e20c9940be91c2b1a026128d5d8bfe77e08553efea3f200ca3ef5963a35db909e6c0eacf53c6085f1cdd6e1e2942e5cba3cf65f99ff0791422fbed7398f5f57cdde318c04dd1e3cceecc0965616429a237c28fef5a43fd4655e57d04e7f69dcb66b52900613918f7a605990a9d9a1b6f5c37ca57052849453ef23b13a7f57b895f3319d34b17358664f30b605d3a1d530e5c876283cec82b605039813736f94925c1a79c38c11391fdd9035f3cc4ee7303970df51bcb4453766b6dd36f90860539b89915ae1690f4b2270e38a295078c201440dda6282e50629a53a9033a267e00a84caa860a86a3f50ced17025847d3a59e2a3b065c7ad9366487465c2170de96c30cb6ca882bd46e98b1792b67d54e9daa1028351e4a00756a979501a00414005a01", 0x1000, 0x9)
r1 = getpid()
ktrace(&(0x7f0000000300)='./file0\x00', 0x0, 0xfcfc96ac1f78739e, r1)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000100)=0x5)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x40001a0c, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000280)=[{0x1c}, {0x30}, {0x6, 0x0, 0x0, 0xffffeffc}]})
pwrite(r0, &(0x7f0000000040)="f94c9f64dd0303fb27447669ee2e", 0xe, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
bind$inet(r0, &(0x7f0000000040)={0x2, 0x1}, 0xc)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x3eae)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x5}, {0x5c}, {0x6, 0x0, 0x0, 0x800}]})
write(r0, &(0x7f0000000100)="2cd1ecf725b86a7c0b4fc7aeb545", 0xe)
r0 = kqueue()
kevent(r0, &(0x7f0000000400)=[{}, {{}, 0xfffffffffffffff9}, {{}, 0x0, 0x0, 0x0, 0x0, 0x2}], 0x9889, &(0x7f0000000500), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$FIOASYNC(r3, 0xcd60441a, &(0x7f0000000240)=0x2)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x5})
sysctl$kern(&(0x7f0000000040)={0x1, 0x4f}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2688, 0x0, 0xfffffffffffffe07)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x3, 0x0, 0x0, 0x49}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x36}, 0x2, &(0x7f00000001c0)="7cd2f7d83518753e9b53aef5572c800883f81f2a3af2103dbe5ae34ccba5b08328e8fb1201533f996e8beb0d7d5e61c2c23a5f33c228356481351a9391d7a23cc7d4459781ce49483d1215791113a7f51a4785f2c1732674394deb00d4688d178da20b00dbd11bc7cfab43c03e62c8417ea230965fd725005b893ac806", &(0x7f0000000100)=0x7d, &(0x7f0000000280), 0x0)
fcntl$dupfd(0xffffffffffffff9c, 0x0, 0xffffffffffffff9c)
sysctl$kern(0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fff6f1f41e6e7ff0002800015e3000000000008"})
readv(r1, &(0x7f00000002c0), 0x1000000000000321)
writev(r0, &(0x7f00000012c0)=[{&(0x7f00000001c0)="a68e935e666e141d508e876ef031580fa78d1981291dfcf32f1cf14e00a7722c6a32bf17cb958977b94cc3341006d070ac1168023bf60eaa0d3b850df74e62788cb18fcb854bde59191d31c9a423b92d0e6be71208125c1fb1b0af097886d0a99574467061e45e258a01dd0af9feacee4dfdef451cd754d65f0570b7ce5acc943b07ef75fb05bffa4a4f3caa4472d04f53781f9d84b4637cb66084710d41c9214e3d1bb9094332cc12e3879d16af8b44af16ac613d7e251dfa6cda957f87650529d59107b6b193cad22a5fbdab9a60251b7b4f8961b0349f58453d36f4e90aa2d503350ca574d7e6f1616843cf9b3058040493af8b6d927fc4df06c7d23599287cb7531c0123f1bb08f2686996fe76e4ea295da7f8ac0cfff694e513967448dc6899ff618f49bf6026c230b3e7a091881de5993113acb52a99409ee873dce61bd8622d8a79936bc43b3dad34115f6f5a7125a09d8382bf2e0adffa2abf399aac0479c31c2fb1431f1845f065d7c1ca06602ea0f35489116d8a8258c2947c71195b6774107a5834fcc98e494d0f065bef2c2727b89faf45b6ecdf9d837b93cb2c233be012ecdbcc45462434d14e53dd036bb603f70396a41cf26cd4d8090f2e0fb3c99f1acff72838f1f36268136f9e00001b407c143ff20109d06d8a3ff237a10d415b283f8d70e4a415c7ece8e5606d1b9e57cc4efc3f32bdbd64c2bf2443d7f35de8867b5e77822accdcfdb912bf4b4374081e277e69faa813569edc1e06a74df30d85a8421115936d0863ec971a6ccb17288f3de6d1d6fc511c808f3bab4c59304c978fee340391acfbcbd8ee98deea42d65b1bf9f1ea53adcfe8bf4f4215e80583070b989f10bc914eea5c1749423a1dfaed392ca4723c0993f10c49ee35381f466c88dd00cbb94db769c965ed1e2ac369af07880242384e9c409c717fe8a0bcf3ac037e095fd31da942a734c0d9980a4de92548fa7ef5f62dac581ffd7377b12765db62bc2f1ed076cdd7b6fe7c0da20ebcf123cd873e61aa31c3e004d907f0913c81d4393d50457bb2fd0f418a192ddfdfdca30a27442986d42db106afaa3658601081c85216445a7b724b68f2ee9fbe2605a17c71ef04615c851f18e921d47bbed0bb493b2407d002393c4321239d7d6f36dde26a5ad1a4fc4cf739f0700023cd0415ccc2386595ec1cc094af50ede0d805216ef8e39774ce01711714963d5fd77f0db4f9dcbb1666e635342c3e46ac7cb4189796a4a2a5e55fa4f4b2dc397e8a36f88ccb356cbd7a929ed20b1bd2505a183d089a0b58362a46b080c680584de343724d3e7fe683ad9f0cccf00d4159762fc3ad133fce48697c42e1c4525b277c9a686812c3e65b046e22265da118a47ceeeccf46b177930b4949cf59e382b389142520971687afba371fcf30012ea830ecc78b251deec9f645c83c64c6fa3fcbbaa9e8504b78fad5280bb57165ad2e5cf7b49ebbbfea1c648aa05a1cbb8eba8d1d2dd96d65516d75cc231f1805615190fec2bd520c929210f470de50637ec5bcfca7c1867f21bd8ce1ad64558ec8cd71ca43478a809c2854105f97880d39745b377ca498c4c0942de1c1bd5a899f5c8755e8075c0c3359f284eddd81204ccb55bfc62d2b04ac06917c7a11bfc904c9bc7007cf891c51ec4cb63deb2b3c676adf484d419a4707240e19de8f6b0856b369dc345ca0eb4fcf42fde121b5f59148650eced36aa58db9a580c5841ce24d4961a93fdba41c56d8684e5c585e7de8842d5aeeb7899468e05d703767fa1c4d32ba12176859e5b2c74c2bb6076fedf5dbabaff6265f43c730702b05a1d46f13e36514b527de46792553f4257e2c238d1209beb3e3bd8b36b7f41b881d68beca4aabb1ebb417cf10823156d0fc2709969ed36437000d9887448f1f36fbf77d425ea26006ce05b006f6eb3de75c0f8be729ddf392965dcedb179a9f79518528cb5f79fcb3ee939d261d194df9aade2f055e2c439f232c64dd35922fc80f001f9dbb0232a23088fca8a95288b66b81c0edd2cdd13dca07efb13d080c4bed3b181c3f6f01b6452532bbd6258030f225a8682d3277912e385c02b6b577310e60fba20efc7da665780d4a7318497578615561d77f5a350b1011d9d16a0720687331ce429d6b6289c2e2ca93260df1fde7526a31752c5f55e73f68606758f8a615b1466c0a6e5c09652d8c30acb20ccaf43097907ecd7cc7eac5a27e93761a832045c80901f68dfa95341f42003eebe201ea71535fd1d282cb9e7fcaba502601fe56b1c78b9af05d1efb617e89575b9bc2f2bd0d194b49571a7eabdb1ef6f259c2bcb9432092914e5547eb40a23bfa950c13e2597da6d52053e40b9be93d6d12b04080965cef00f130bef6ed2a030d18727fe45ab8a9f429b0aaba348a00ed0bf94a8d5f9c1118c74ba317064d67a3d3fa758c7777eca54f09e5c6e9b7e63d8b7cc404ef831a7fee3b6bb6f53aa355b32facc7f7cdc2abd2692513dcef935b3441651e1d488729be2762fd2588a8f24ba4da9cad8aeaacfdd0ca06d9ad049c5c063e06d40da9880041370eaa5e497aad9ca6f9ac7bd942c7aef3e631b1723728ac4279f83abea821e3419d0bccf0ce790008c9f563efad178ffe147cb238a5e5ff4e1c2c6576392b687e66cbf6705896eef3315d7681c14fa1e52cd1a98ff9300bd6d62222ce72279e94a3fecf44741f7d4fcfc301d4764fda8bfee05b2b586353d75e8d0b685bedf064673cf2ba3a951b46de53fb94ef0d89b802860d60ff0596b4de87babe78f9d9b6a1a571725cfc97e0736b522fa7759627bbb1a6c7b929fbc6d505742ada38ed2f02e036b1911bba81233032223eab5218a1492402b255d3cc68c92a70f334cf11ce6665a5feddf52e38ecee865230add99f0631d3952e64c01a0baf412fd12f8ba02b59ddf7c2056e6b384b9fd8c15c44edc4229057dba83eb5f52a213ebad4f966ff7b26ea987e0d1af9526fc9b932bd120712ed5841abce4b6661c67970fa5a913b8aa4358d3869809258c5cc216bd0dfcd23ee176d47355f3cccd38a267c2d15d984b55aee60a26754f09e965cec40123774eac795644558d0b98fcf0b83f9ceff1e470d5a4dc52abd5fddeb268c6f2e228c18bb38ebcd0d69eeebf10d09eb368cdd69fcc1f754c2f7d2031692788491c09b64cc1eb9502493fee47efdbd81675eeca15cc63ddd1f52ac4a4b2e1df8cbec1ea4c3415b3b177f1f32136f694906cb4d321b24d4b82720721e99a3de8945596652b1f827838dd5fd064e9dd4c5dfed6ed5a68fa3f13891f7559139b7f674571296697387095def53ca8d132d1339286777438788703583b8d5f4a7dfe7d65692d65b2cfe9d2a63807c2ef11b30e29ab00438fd1c477cd6500dc3493a00333e3f26013ccb3e3e8e3be1c50e587a7b3d169d9bb41440e1db150f9a391a635fc64bbbde0730d99aa11399c07a9d8a4bde284264c79d2d3bbbc066a65e0997758037c32c0ca86af19034990953a4f23a2592e4aeaa2c1d2b84eaaf3a92f336bcdf1cd7990798ce77e4c6452bb4261c82610410834a6d8914256874335889bb61c3894504af9d9b6b2bbb07373b938534bda65839df87398f12130f409d276ccf132c4c37b2d9b23fe2dfea18725a802fb6cbc24f48d99aeee004a47ce4c4907686b427fea25678f6acbcd28d7f68bad7ab09a3b123f5aa3a8f119276df2cce5c5d2b1acd1753f03960f3ab0ac3b8ae4d3bfc90b3505b368919e9d2addccabb36146e45329dbd37522b1780dff3d5db2c8d1c37e906aa3777892d14a22252463e9ecd6e7944f2c9360bfc53c96e338163daed36535d144ad2ed10dc52f237b0925ecb81840f96e8949c823ad7a0b89ad300256d51657628fc495501ba742483c05e13aa52f5df8c5af85875d6b7c0090f419576c5faaaff2367835da118f53953f64ccca5e78fc55acdb4377fb65157bb7f0a979f5275c859ca09a62ec3abc1a7c1d6629025ad95a37dfe051d4a885df9ad7d290e2f079caa11e6f57d90f7eb40cb1a98e10899fd2decc483a239662cee632710ed3c8075cba3f60701ab7834a7f550cde40d5f0c295ac22501df293299a990463fa2cf7b10ac7e8c4fe94dfd07b38dad88dad3e80b8620387082a1aaa8a4e915d5d68ef5c94a80a9b997e94a323e2385aa70100826ca54ba27cf358b6ea61511f601b4284cefe1f1fba95609d7f5b1486352fb3fae536a3fce6e221a4b4262047ea05612bee4aa468312a1ecd9122726a91ca3b5b6382b1b0f13ee9fc1d7fcaf5e6636c1676f07f3aff01b20d05afc747df5d444a0360143d73fa241187c39b0934315cb811b345b96884ec7d8f34337a448bc66588e6312fad2b6e5e73aa6850cbd43d81a8e2c38971080453c15841630a6fd7e80715611aaa4ac794d44db9ee24e92ad229661f8db2bf5c8ef78a82cc08bd8eca186aa2d439431adf24d4d8aac6b47ee1f3d6a03c3284cf946c6f6c7f918550e5df09796ffa9697db4d276ddf66175c2c9102b9c1c351218315dcbe8b72b4f85a2851c0e292151c0f63be1d5a99f1b4b98385dca9a539e92e999c1e22849f1ee883f9aebb5b357a344d4866598be8884599726171b9929592eb98895a23cd8a93dd664893958edcd79f649102c54541b3fe44ccd3e61592d3d8e4675f4512f6dcd64c3d062e2717efabd9150d19240a632b11353360ff0349fe4fdbdbf292ca96704550bfc4f2321c7fd61949c1b71b9b0ae8f1e871e567744fae4f87bec4a9dd0720402f0581a8f87d28c4910daa620e604cef82a9fdd0070991081857214ec7ad7cde0ae2c82b148fa10487e355332b7aa2dd8f1ee56d96ec8b0deaf5a01ea6b1e42874d4c8646a6c3ef05f701b4ff2043e61426f695a948cf6b57e68f5fa3af8d93cdd68362017b586eb92438a11a28988d1dbaa156f8b54609c5e208ac44a55a5d5eaac485373b79e00084f7c5029a07ccead403b15e57d2820f406ca7940b79d09799bd43d2fd17ce5d6b3032a1f3be801e2022a3200c9d7cf4813d5c7be5939e749d73ecc8d453a03c76a9797c5cae56bf7ab8eb4973ac05f4a2ccd2eefa9cf0e1c259293b7702e97b5e163ceb8d114ad91d9d3bfd34ee230ab140d028f2dbc99e3176bd174e40aa435eb1aeb10dd866b3f30b78eaea83460a3fe41f4657e748a538cce0f57caa49a4c59a3d9608ae326469fdf45aa48b31b4903a9bacc01abd27aba8d43d13154dbd9bf3331b799756da98cd92c35b2ad91fabf57f3570775590b9d370e2ed236d4da6783d813910856ff8cc4b527a24842e45fc2443a1d3c6d1b2eb4e484602d0b53129f6cc810f5d2087f7bdccfd3141da19d90622f2319dcb943e806631ceaf7871b52f70a53616edcdacd6742fc9008589bab6d42710cda94072e2995cee7225949c88f4b3435e39c73b05d5629536c886679702bcf79a0b72d15c301dfa129eb22b512b8620b40c7aebb257f4ff978bb836bf7ac1f29b83310a6bb4f37eff505e065b0f0c3ea907ff8ba4fba695b59f948e1e7b16d0a8c895648973a77bd4189d7f8b51f76a3b7eb428017fd9796709cef143a49a5dba2eb38d1fbc580520cdcf02f44b5a4f394545021f1838f80dc9aae690048e7d9a4732b25c1995e873eafa521666393f707f2f96d3e17cfea5f3bfba65b85907f1349c7b7d934c8a6d0264992edbedceacb8c008c4bd11f6e18010331461eac397aa6d0e3d638fed3638dbced92666d846c4c22ae7c8359c259608d058dffe3803ed702fae52cd9d8324be2bd5", 0x1000}, {&(0x7f00000017c0)="2e884a966a97df65b3552e41e12e4f11a208fd4d73414f4395bbc04b6a310989e16f0e242fb3029f09520b5ac05ca557b84dcabebdb06fc25d03bcead8b2384210bca5d42a78527073808dba8141a3abb2467bec54784e46d12b480e0b7a2a6c8f238081aefd20ac7077e56f6e86eee84bfdd8dd647882c2f5c61f0adcd1b6e436d7ccee8b54aa7b10d20d9f2ae62724ee6bbb03196e59e33b6c7a58979ddcf4308d52ab9c469cdcf2b2cf1a986d4d82e4a84c0f8c508bc5a26a8efd506d678b6b5a3290d5221f7d3efe3d0045eff6abf658609445d8d25fb47d73434e8df9419d2f78ffd1998d690ce22e5b034a9dbb69ff98320dd5eca3eacc0e455291ef83a8fcb9bc81287652ccc821e26e76b23385e07399deb19ab01ae57f240d623dac312affba197173901daf95c66e5c63a75476ad8e8a6181b2187a083aca6592bb4485faa033ed796f48e7be72beffc306eb502f886e8666c495071c16af348401e47a8281c2b60d840f5893e011c772c47702f4dcb1cbf7324b22a2369bf5a7a2f4c6e88a3558781a4eadfdde849ec4526868e238a37fd2a9cc6fd868af3625d02ceef760febf030e82d0fb99d4ae6e85bcc6f2dc1e44c271f48f325b6c90d8890099d14d8f66caf2cf715bd2a2ebca21ba1ee8514c2d28404b0e00324ca03e2ff03c5a7fad7ed0e0285e630347dd0a9488d0e2d41867810154073545c13d890d3b98966b1389d39196aa8674feab99e1b844783750cdb3e45530614fbd5c455feb45e83856f0a77b302b189386f826d5579e383c1da95eaac9f87d6eecd435a96c17a09557bffea748204a6019c99e96a8b4b4a8f2008afd94061293512bb4f18e36e799204deb1a1ac448fd44fe452609e1c8f6c211f9a33fcfa3fd121b50533486272490b46b0184567d6392404d5ff0be84a63ba41ad51d0a98d591bde0d50f0d3d0db0f5ef46295a2a5aaa52e1d817a8da4bd84d06d43acadf0714fe1afea83d47a0d93ef132e8d9ffa1e15a782486d87735c61129c0b8ee7b38a458d001f9870d73a5f7a9381259ae2a1873c42d9aac3f73ed07610c4da3b6fba123d23a22db7b7f1bb54374b42321cdb30fbf6eb688a822bc9e4f76fc25a8d7e16537043cead44d59704bf2e29230a73fb4073cb74cda2e11284f5cf9a36e67208039c404cf9e0ad6ab126b8cf75381b98a909ea80449ad6a4363e97357669cf2094d4693627136d1695257c6b5e8800a045f39d202cfc64bbafd060099e9566082ab7750099be16835a11752d496115a75c3a5cae215b1861267837b8c9148c825fc91e4ea5fb793b41463bcdc7c56d554c4cc263e96ab9416ba854e4670d98f2e53c6529a299d02e6d2152cceabf0cf86c392019c19f96cd1713c558b0f9a9788fd6cea6f63ac8e3e2b776db5cc00d668ab3869dcbfb1f278a076a7ebc25d613d43f11b910c3e33f016e3dd483c8aa90fa98f0015b79ac3dd263704e67f8e5d8b98c00788d6388986911ee758f74ac3ac86f123243c207b81b09600e2c47c477ee3f77812cb88ace10977ba5ca7b6452b15c294e7a4b67258ac6673c13c15be4298cc373e705d363ed5fa93facb64488beb058a0b99c103844da8872805a6dc50634b7fba3b26b6f3362e32182c6f4e875702168946b98ac7df6b0fcf4383e68f1e2fbd6a776d7184b9b0a4c6edab1167bca546989351b81eb2a869ce6fce82894652020dd8b6f88031d4499743b4d7d5555da71b90ecaefc8a12e5d0ccbd0b20daf71928c1a9aed8d2f693d2eea2dbe4e7ed2b6f159e51a9144982068b5dbeb73dba46a6e970a7e2bfcad0d1466cb043155a99f6dde58c39c550aebc505189c40d51cc48b591d9b50f0e11048861e8d5af1f997ebe9c4ab2464a0c63909ba393ad7f793198e21825bac3efaf0dddef0cab7ea4b886b98f0dee561a705748da1766379d1066c06bb5cc608a958ff0247eb04e54e0612cfc86b5d11f3cd2acc97f9027f05e59f3314f772f3bb8cf445ae48c1b975ae84c4e054f2fd1a0804df0884b5cd2b4941c649d336c636350fc0e762ec7340617e2b0d46d2d070a63e0b45ead5007b7cf65ba0a75785d514f7ad3e1c21855d2d32371f687316e32eda436834ca66643fcf9225013dde77ce22632cec5a4da2fd947bb155c4e83241ab7c8d775ccd88a26a953650cd125fe5c60b2828364f8dc527e8762670d1140abd59a030968f4c294e0cc5e4ff21f59ac1fd74c570feee64222624af2659a219f20e9e6429a8efd2b4cdb762ab400d86467044ecb165b39e38b5bb99b62afb10c6e1a33fdc710b0f7b9aaa5f0cb9f57fb015883a6674c55aa2a9e91edd1470a23ed7bbd3fb7d62a18eab38da49d29168695584f70b44c2c7fd575e1e57711ff3516c23ddb052d63e2952e587c098c7957c679064be043776b06741104a62ea0790f3fa6436505a5644e2086ce0128d36fab6b9f64375ca178d2ea08b3fda062f72cca362d42d390f88916a824b94a9ba23c2b8d86632fadfd87c002bb23ad2affc8b650fe820e519c4bcb75c6001470cf2c1e857cbb656cdcdec7060d75a095cddee9f9d58571d1924afeb837b0a455f582aaef60b08385ae43dc567035c3ec16b83fb2447037af8ff0fb3540853788c37a04d1d36e09a49c7cf6f0bfacba8b36231e420912bd45a98a6d1a836c33f51ebcb1308d96beee9683f36ff69f87efa884c566d472af02e684dfada0ffd5cf6b0cc32bdcd83695c7629df9d8b000bab2292906cfeae5e40f98fb2fa5d5271edd25236dbc80bdb31c71a21dae977ae3c231dddf3ee8f1aa37d6bc41718e3cbe909e8eeff5974667d260d4d557ee3dfa80382afcc75496b594a9216137eeb66fa652105752c49cbbdd478f65d7f65acae993c576e95818c4ca6125f36f1e87fe270d3fe518338926334d2039a3cd0b7750848acbd5847334d197e93d7e6bc7ef10a0c0ac112b5845f4034186e9a5e292f92b06f796c7f4807ed193ef3457b1a512488604561ea011f64b065783c3aa52408c527a9eacf9304ef0e102c1e5ab17ece9a6a1a602ee0fc331edd42795acdecab8c0ba99825852ce62e1436216f8340e80d11c75c258f12549ced1133a2074ef55f205c56bd661e1a3d9c7aed8896521b678f51c2a23c86f6a5197915487348df8582035cc30b46dc71793e84dee75e41ab484468a3e9bd4d567a913b8985fab0b5bcda7cbc25e5185ee78728104c390d9889bb01381168b0a8b07d949eeeb86698fec5d80287e510c9f0c6f646fb191629910572dd083ca3f72299a9ebf000d66b4824b1dd8ee7453d3ee3bcca17a23bc24ef033125eb07bb9f9c3a1ecebf1cdbd7edefe4bc50cf0bab653f344084ce24b98c5bc675f1083fd34f13e182b06ec999858765703c5cc2c1cc5797347a7e5e11cf360318d50f15f6ef77617b78f5d6e27b2718d3805ac2ea2484cbeaa16175a8216b1c070795aab9527825020eec4d32168dac72e8e2207e0f574ac3e4eff427c674cf1072ddd79398736864c0883ff88107da70cd53dd84d403c549a5f13e28e6b6f3bac73861370d32703d97faaa8189f7f68d2520b48308a8e888036c6e3638a6de6189c1ef7fc729733bb7edb9df578596f791ae863a30dd0068cb11a9d64c6c1b3445f0f3294da3e94731e26e41d7f948d708598680e4b7d65c4f9e9b56a91a9185aa4f49292adc78c721b34eb8eb439ae7ba95281cd78af08e75112d332ab28ba099975e71835c99ff2917949ba3e5738a3f532d33426e9adfa9beadaeb1b8118ca3897cfb659a630f85fe092ae653a937e96cf852745b3f518b19b683f4a67f5dc7849b48d4c9675ee4cd676c3ee95f9d86d9299aedaa29cb1457735289342dbe6934e87702d2019d0370416ec5736dff5e62bfda557e1e7f50aa2fe19f83f5e933fe40b1ac6aafa4145eb1f9c2690f45882a19a499232ba44b17ccc7f64c50e7b26eee869642c95d78595f74fca37c3f422d9ab62d9fb3d95d6d65aefc82a95e67fbff1dcb856434f5c36ac90b199dd0a10c0787129b925f2c4c97f3886237e4abddc130939d5036085afa69fd75dcefd7303dc19cace13c57fa3be969ffc5b172c42130f52078dc15dea11a90aadb0af1a6366ae0e7cb4b908cb1a4f3d0b5f7547ec656cee02d9bed107230f9cf90ff01f75c6b37313bdd334123f82e1879953ea50c958d9329fe6e66f0a472d862cf2cd2300187080aec2c0886f4c8045e51a5c6ac78adcf2cda39def0f8daefa56a1a2bf6b4cf9a077b1c628111a5c88b420e7358af52b7f99775278caa08b7577643f5e96436785451058907d364df7017db33e36c1cbf253991994ae255a3be38cad3edfddde106ad4d6d8026e9cbb3ee1d1ac0332a950473629c66e7b6d6f376b24480d42091ce0a2534ec5180ffd36508024315b982d9295ad7cd20ea7eef5c046061c9d97c2654a37691320fdcd666b1c6af04eb0fe7992a11da936ffff65660f9fe510df58944edbeb5ed86abde20fae5cf840dca2ffd2dffce889e3ba7bd51dbffaa0ea43d7c3a88661b4a8a543b4e4a218748aff85d9181fa0f3cc9ff332a67c1e3207d229e0f9b9a1a57fd79fc668a44b45d21073404c1a42ad93b8f9da4d3f9efe748aed698e78ca2b0eb7b48f397275b4f32aa8c7a268c68644e0075af07c6cd0a910985b02caa805187141efbdf3dbd2e9ed6381e6df645bcc37cf7f004cdd116458b3a9104ebd3178361276db24befd0df7f0a1e8edc459956cb1150ddf416b14815551e917e056aabbf2675f2f62d3ed035e268d72daeba90445c8f21a7dd39e5272440269ec46c3a66a2cbf3e6f80ca2da9ead02eb8ed58b1b18934c7cdca49ee944068fc0d616185e5e526017cad6722a7f075888c106247b802c026ce2209f295c49c5f256ead51f539b120b92485fdb45d734f26490267a2b6c8c0bbd332aec4f58cc6a630e027a72f84b97b7039959377db0b52ac532086aa29df5249c296c97c0b56597fa8e33e31b6a2ac8cf15e39be70b17857141eb7c404f025e8c8a10ddcd9d1ebb80dee33e1abb9d5360348f77eca1d01c59b45641f12be0c5c5388a0341a561be4a1a1522be9d7d7aa0431fc41882fdc5c3bced7c5363671a89d85a9b6ca09f810d2599d985a98c47cdabe08a7caa3c86bcc222031c5e572da593223b6b70aa918e2ba7208f728dc39c5f1eb9bd8b08f3672b733076b8f0c4da1350270c2d28d45989793933537fa772a8124be16d7676ac098f10a2d235385ca57c4fb3f4a909c0da8983836f182664f9c9833317e5dcd5da3224b3fddce40f6eb37ebb69670140668c2cf21b91f6cc1ac316613260da620558db6bf79da5ad8ddd47f6329d3cd5a3530250722202eecb269a968e3d03d3a24c84be1a15396c0c921e244d266110c67d3ac661d74b0e85e8a15fc8ad5b8480120ea6dc9ebdd182d29e1386bc281ad2b1bdcc35e03ee7cd6c8ec4e6cf3bf9d9b8b9f6b78c64c24f19b8aba524aeb13e05e0147bbeb64c3464d5c1f90804d00e9a48e14c7697d8d73090b6262b8d0dd1e01de25a1c86cb7ccb41a961b566550a2298245d44a4092799b0bea95f2077c953e0e3c1a8c1a3591d6eb6e873c22b249aca16286a31ef87fd30dc5f73e1216c30040399ff73089288e527e1a36399053618a52a4d510b8c1e0aaad2db52163c7cfd179af5e987ea0d6181a7f73d8c5c7f1f786ff83594b65fcaa6210977f2c163aa3b82c17417d70ce5a07ff3a46151247f05bd5442a21827705f89e528d9f3f6e797caad5d984852695216b2b7888b", 0xfff}], 0x2)
setuid(0xffffffffffffffff)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
getuid()
fcntl$dupfd(r2, 0x0, r2)
getgid()
pwritev(r1, &(0x7f0000000640)=[{0x0}], 0x1, 0x0)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x81a, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000080)={0x1, 0x40}, 0x4000000000000007, &(0x7f0000000300)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fd47b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43f2baa813fa0c133f1878e216dcc1d20dc648b1a87e551016109eced2a39d5c5b0671d395a072e107082054f8db08a0b58b22867e5562e015f19e9b5121c394b7b1473f9ef8e472501ebbad47c17050ae72aa49a274bad1eb835b723d0a36749bcc52e7dc658d5be797c2c96a4c2100e729add810bd69c594cd19829e6223ea", &(0x7f0000000000)=0x20f, 0x0, 0xec)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x25}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
mmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0, 0x11, r0, 0x0)
msync(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x6)
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x40004200000028ac)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x2, 0x0)
writev(r0, &(0x7f00000015c0)=[{&(0x7f0000000340)="89d722ff", 0x4}], 0x1)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000100000100000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37281c18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x21, &(0x7f0000000040)="b1f5d915", 0x4)
r2 = dup2(r1, r0)
readv(r2, &(0x7f0000000140)=[{&(0x7f0000000200)=""/187, 0xbb}], 0x1)
setsockopt$sock_int(r2, 0xffff, 0x800, &(0x7f00000001c0)=0xffffffff, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x8002, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
write(r3, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{}, {0x2}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
setrlimit(0x0, &(0x7f0000000000))
poll(0x0, 0x0, 0xffff)
setrlimit(0x0, &(0x7f0000000080))
poll(0x0, 0x0, 0xfe9)
execve(0x0, 0x0, 0x0)
setrlimit(0x0, &(0x7f0000000040))
socket(0x0, 0x0, 0x8)
syz_emit_ethernet(0x2a, &(0x7f00000005c0)={@local, @random="3e94e1b1da6d", [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @local={0xac, 0x14, 0x0}, @empty, @local={0xac, 0x14, 0x0}}}}})
mknod(&(0x7f0000000180)='./file0\x00', 0x1ffb, 0x0)
r0 = open$dir(&(0x7f0000000280)='./file0\x00', 0x1, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$FIOSETOWN(r0, 0x80047476, &(0x7f0000000240)=0xfffffff8)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="cb5d8bf0b6d8836273667c6836ca900f6f3934", 0x13}], 0x1, 0x60e2)
execve(0x0, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1003)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
setuid(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setuid(0xee01)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000c, &(0x7f0000000180), 0x14)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x36, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0x3)
sysctl$net_mpls(&(0x7f0000000040)={0x4, 0x12}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0xc003, 0x2f)
setsockopt(r0, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
sendmsg$unix(r0, &(0x7f0000000540)={&(0x7f00000001c0)=ANY=[@ANYBLOB="9902"], 0xa, 0x0}, 0x0)
minherit(&(0x7f0000ffd000/0x1000)=nil, 0xffffffffdf002fff, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x6}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x29, 0x1b, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x14}, 0x4, &(0x7f0000000040)="67c2a01f", &(0x7f0000000180)=0x4, &(0x7f00000001c0), 0x4)
getgroups(0x2, &(0x7f0000000040)=[0x0, 0xffffffffffffffff])
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$kern(&(0x7f0000000080)={0x1, 0x15}, 0x2, &(0x7f0000000100)="23e5ae37e6408034aa8e4814cc63259e", &(0x7f0000000200)=0x10, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x10003, 0x1dc80d8f, "5017a1be57234d68b3c742c66a02fe0000cc6b00"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
write(r2, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r1, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20804000, 0x200000]}})
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x19}, 0x4, 0x0, 0x0, &(0x7f00000001c0), 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010c8e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f0000000180)='\b', 0x1, 0x0, 0x0, 0x0)
sendto(r0, &(0x7f0000000100)='A', 0xffffff42, 0x0, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x3c}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000080)=ANY=[])
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x8000000000000093})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup2(r0, r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x5b0, 0x0, 0x9, 0x7fffffff, "4d881953e46d1c268243a80c8bc9abf75524ccc3"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000003c0)="d96ded53ef76dcb3ac69f79e03767a2c9a14866294d68ed55964c703878076f5cd7c", 0x22}], 0x1)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x0, 0xfffffffe, "cfe9691e70058333f34171bbea9b5144b04424a9"})
readv(r1, &(0x7f00000002c0)=[{&(0x7f000001a840)=""/102400, 0x19000}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x7c}, {0x84}, {0x6, 0x0, 0x0, 0x81}]})
pwrite(r0, &(0x7f0000000180)="14ae0100008000000000f7500c8e", 0xe, 0x0)
r0 = kqueue()
ioctl$FIONBIO(r0, 0x8004667d, &(0x7f0000000000)=0x401)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000001340)={0x3, &(0x7f0000000080)=[{0x5c}, {0x5c}, {0x4000006, 0x0, 0x0, 0x40000}]})
pwrite(r0, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f00000001c0)=[{0x48}, {0xc0}, {0x6, 0x0, 0x0, 0x7b96}]})
pwrite(r0, &(0x7f0000000300)="c8dcafccd1a6f48da693157343cb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x30}, {0x7}, {0x6, 0x0, 0x0, 0xb9f5}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086333)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
open(&(0x7f0000000040)='./file0\x00', 0x1, 0x0)
open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x20, 0x10)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={<r1=>0xffffffffffffffff})
r2 = dup(r1)
sendto(0xffffffffffffffff, &(0x7f0000001180)="006b6bed3f0471170b607e2b6858164bf0817181e010963bf0fece", 0x1b, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000040)="7f66d351849cf08855afeeaf9a68dc9241d83118ecc071ee053875a04fdc979fd0b72b79cb3b7d4e925a016a43f6200e61055283b9163ce87c7a83cb528577522c43dc682cfa55a86a3fc7ed88901d0aa3f123d378781ee7caf1a79a62d9f645f184c848c00097b2c0022da534c041d8904d87d897c90fc784c390fde374e2c9ff862b5e888f0b69a99a80387f6e8eeeab", 0x91, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000040)="7f66d351849cf08855afeeaf9a68dc9241d83118ecc071ee053875a04fdc979fd0b72b79cb3b7d4e925a016a43f6200e61055283b9163ce87c7a83cb528577522c43dc682cfa55a86a3fc7ed88901d0aa3f123d378781ee7caf1a79a62d9f645f184c848c00097b2c0022da534c041d8904d87d897c90fc784c390fde374e2c9ff862b5e888f0b69a99a80387f6e8eeeab", 0x91, 0x0, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2c)
ioctl$TIOCFLUSH(r2, 0x8020690c, &(0x7f0000000300))
ioctl$VNDIOCCLR(r2, 0x80384601, &(0x7f00000001c0)={&(0x7f0000000140)='./file0\x00', 0x200, &(0x7f0000000180)='./bus\x00', 0x6})
sendto(r0, &(0x7f0000001180)="006b6bed3f0471170b607e2b6858164bf0817181e010963bf0fece", 0x1b, 0x0, 0x0, 0x0)
sendto$unix(r0, &(0x7f0000000040)="7f66d351849cf08855afeeaf9a68dc9241d83118ecc071ee053875a04fdc979fd0b72b79cb3b7d4e925a016a43f6200e61055283b9163ce87c7a83cb528577522c43dc682cfa55a86a3fc7ed88901d0aa3f123d378781ee7caf1a79a62d9f645f184c848c00097b2c0022da534c041d8904d87d897c90fc784c390fde374e2c9ff862b5e888f0b69a99a80387f6e8eeeab", 0x91, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000000c0)=[{{}, 0xffffffffffffbffe, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x400000000018, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r1, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
sendto(r0, &(0x7f0000000240)='-!\x00\x00\x00\x00\x00\x00', 0x14, 0x0, 0x0, 0x0)
open$dir(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
nanosleep(&(0x7f0000000000)={0x8000000000000}, &(0x7f0000001040))
r1 = kqueue()
kevent(r1, &(0x7f0000000080), 0x7fc, 0x0, 0x407c, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r0, &(0x7f0000000080)="2ab54bb3a1ccaf3ea2c6e7580adb34f9d76e405175942952e6a9340b312d759cb9227fd043fec509b5eb2a4515685aa2fd37de29545d7f97d5afd52ed3d94dee200d617eb5ff07000000000000d6ed2ff9c17a1b1af5065932130caa55a86a729de703489525e1b41f8f3293d1f8c434265fc4433722dcc15f8fe3e6a361af67356649b8e2ea9d3e7753dda1fadaaa7f9ac1036d545cbecd78cbd83946f798d92eefa56587dfb14e6f332d92d7c93e0b5d95f5d47123d6f85150367a2bbb85fe71576ae1cfd490baf24672f02e7514a6a532898bf1e7ed4a4a563ebebae3f35ad4c5cef01df83b821ac7539d29ce2f600c3db8ede11a4ef73b4e1c637cd3f3bf946e0f3a30e7c20cae6c5246bf7ef30cf9473133c82127cd0d2ebe81c0e8ea80c42d77f9c31c50cef212a198110c5c46f8d560615f654c07fbd14517bca388c23d1bda1507d705d3d423c3751991efee1663381cf2d0be21aba60129dc5f721b119994a27e8d816af60e0e42737e2e", 0x16f, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x5}, {0x24}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1004, &(0x7f0000000040), 0x1)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{}, {}, {0x8018}]})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x801169ab, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x7c}, {0x6, 0x0, 0x0, 0x8007fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x1}, {0x64}, {0x6, 0x0, 0x0, 0xffff5b89}]})
write(r0, &(0x7f00000002c0)="03e09a90b3911b4299937474390d", 0xe)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
dup2(r1, r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r2 = socket(0x2, 0x8002, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
write(r2, 0x0, 0x0)
shutdown(r1, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x60}, {0x35}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000000)=ANY=[])
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x5, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069db, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000)=0x43cb9, 0x37f)
r1 = dup(r0)
sendto$inet6(r1, &(0x7f0000000040), 0x43000, 0x8, 0x0, 0xffffffffffffff29)
sysctl$hw(&(0x7f0000000040)={0x6, 0x7}, 0x2, 0x0, 0x0, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x0)
r0 = open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
rmdir(&(0x7f0000000080)='./file0\x00')
mknodat(r0, &(0x7f0000000180)='.\x00', 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="b5027b1c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
listen(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000140), 0x4)
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x1, 0x0)
openat$tty(0xffffffffffffff9c, 0x0, 0x0, 0x0)
socket$unix(0x1, 0x0, 0x0)
ioctl$TIOCSETD(0xffffffffffffffff, 0x8004741b, 0x0)
socket$inet(0x2, 0x0, 0x0)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
fcntl$getown(0xffffffffffffffff, 0x5)
mquery(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
r3 = accept$inet(r0, 0x0, 0x0)
sendto$inet(r3, &(0x7f00000007c0)="1daad5cd36195d6810b318271ef9a4e226c2d78af736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8adbe6f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbacff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e75e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e138a000000b92c28d39f6e23bc224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70df9265fa833f7f7788b351b9a0abf03d9e24db2448b2db5c1105d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc346d36d0fe7a2d0b32aeff27e94fe6994ffe7086d8f0c631b9688003f65d4b91d0000000000000003f5cea6e423358731875fe8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc7d8f9f2fe3d1d4cf4dc054a98f9013b515452b742f911f5aa5258ea5504ec5fd29ae3124f55cfdbea9c3969dc552e1d6f13d86e3043a8ed35e413ea2a8c43d6c462463b88ea0a3fa87742efce671a2f79f5b66a844fbb016ba0a0eafb7f26c47f58f25808cbfc6902b0f0133039066c1b0e4b133ab19283a8447b9412faf99b67243a3fab7392f29b6d3cae0b4fc6e528f7662267692e44540bfc23ae65598b6b621eed2daac98c6f59ee7a08fca4bd017e4b6b9be821d9af3bc2fadf96cd15857a54d7e879fa9d61bf34654841f961a030f3c81f9c750128b0ccb8fa9262c5d930c8f527d9bd5fd14ec06e29de61866bb4fb0e405b324db46fa449242b5ff600fa6bbaa50d8736d7be3e9728bcebe3f703aa3d99b63d390759a13c2251c3ef8a3e0bf42c132db4317736a807c778c6b5e72a12330acd4552f2314b02eb9ceacde4bec3b552298a1bd623c1af3f4ed230056a735d0c372b6bc10000000000020000000000000026f184ac625c20f47abf53a298ba0d4e62943a57fafd57a5569c84b5517e0a92ae7580a16e6ca625dc04bb1fe6593f8e75218d1514bfe0a49c3483da21340c35377bb720d545fdf1c604dee2f5f126aca257e273af57b1341269319ddcff0281f060d65ffac74766ce2b0d3ae6074861220f542a28f4f67c464c01e27add18f942dba7e76fcbe894b1a439eebab9a9e9269bcb698aa699784c79c43ef1b6018a0432b2de4f299034e8ba0000005420abec3b55c819f4bd4a3ebe69ff68da7d7334a3c390a00b6373cc180cd7ae1c716b1fe026396179a3bb3f5fef9fdb52c0203c0e9b9965a1aa61d6011c98c2df81535ebaa69a3525fe1f924804e7e729daac801843f856f8599313b72fcbc41d1543c4a9902329d246edf766b227e1f66fc8ce920d102f4cd265857a3dcc54866a2b67a93b1d77a1c10268d3ce409bf692a7bc48afe4290cfe89fcab206057fd34cd39fe7b36c9ddf7759af0448c59a8fb3f8c61fa395d57666d076f312f1abe091c3977861b619e7c511337b9ed63c58da22ac9467f8884820e2d4861baa5e3289275b01fdf62b3861b9338939441cefe5603ff7a2f64fae5a93667d6eb2147018a60fb40bf7d89fa16c82ae55eddfc3e66baf4eb9c091dfd2b634574ed912858e3df00c20e640a0cc7c70c5ae73f999a3d86de45b960e5b5b970010beafb71c01492c9f9d5d762bccd5f03fb22f862cf6b67eebe8c910259b46f53718fb9378e9d60000928a6101ae308a387", 0xadbde36e, 0x1, &(0x7f0000000040), 0xc)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
r0 = socket$inet(0x11, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1021, 0x0, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f000081e000/0x1000)=nil, 0x1000, 0x4)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000090000080007", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x0}, {0x18, 0x0}, 0x0, [0x0, 0x210, 0x0, 0x0, 0x0, 0x3, 0x0, 0x4]}, 0x3c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffffd, {[0x4, 0xffffffffffffffff, 0x0, 0x0, 0x100000007ff, 0xfffffffffffffff9, 0x1, 0x3, 0x0, 0x0, 0x800a1, 0xfffffffffffffffd, 0x5, 0x100000004, 0xfffffffffffffffe, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x2fcd, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x6], [0x0, 0xa6f, 0x2, 0x80000, 0x5314, 0xffffffff], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0xfffffffffffffffc, 0x1000], [{0x1, 0xfffffffd, 0xfffffffd, 0x20}, {0x3, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x400009}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x4100, 0x39, 0x0, 0x9}], {0x10, 0x0, 0x203, 0xffffffffffffffff}, {0xe3, 0x1, 0x6000}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(r2, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x1ff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x80000001, 0x0, 0xb950], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0x3, 0x0, 0x0, 0x4], [0x0, 0x0, 0x0, 0x4, 0x4, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x0, 0x0, 0xffffffff}], {0x0, 0x3}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000004c0)={0x80000000, 0x1, 0xdfa, {[0xfffe, 0x1, 0x7, 0x7, 0x8, 0x7, 0x8, 0x0, 0x7, 0xffffffff, 0x400, 0x1, 0xfff, 0x0, 0x800, 0x7, 0x0, 0x20], [0x8, 0xfff, 0x81, 0xce0, 0x5307, 0x5, 0x44b, 0x1, 0xa73a, 0x8], [0x100000001, 0xc78, 0x10001, 0x3, 0x9, 0x0, 0x6b3d], [0x6, 0x7, 0x31ae, 0x6, 0xd92, 0x5], [{0xe3, 0x0, 0x49, 0x100000001}, {0x7fff, 0x8, 0x1, 0x1ff}, {0x3, 0x4000, 0x1ff, 0x3}, {0x483, 0x10001, 0x0, 0xb34c}, {0x3, 0x8, 0x7f, 0x7}, {0x101, 0xaee7, 0x7f}, {0x7, 0x2, 0x7, 0x149}, {0xf800, 0x10000, 0xffffffff, 0x6}], {0x1, 0xb047, 0x9, 0x520}, {0xcc1, 0xfffff001, 0x100, 0x9}}})
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000140)=0x1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
openat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x200, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000100)={0x0, 0x0, <r1=>0x0}, &(0x7f00000002c0)=0xc)
setegid(r1)
r2 = getpid()
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x4000040e, r2)
setreuid(0xee00, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x40000818, r2)
sysctl$kern(&(0x7f0000000100)={0x1, 0x48}, 0x2, &(0x7f0000000080)="7442f570", &(0x7f00000000c0)=0x4, &(0x7f0000000000)='\a\x00\x00\x00', 0x4)
sysctl$kern(&(0x7f00000013c0)={0x1, 0x16}, 0x2, &(0x7f0000000180)="a4e6a256794ed55a00dd34019169619795a613bc359658f0de67db413c2083f9def93d77ed19b5a2f39a16", &(0x7f0000001400)=0x20, &(0x7f00000001c0)="05268151ab3f044afc32785b3bf19b9671f0bc340f121c935559a9d32369e4fff9b69c689720e9b8a6e889f0421ccfe546750b3848c044ea839c4977c1c013c3883af92afee3e9e2afb91a66eb05bd82fce036eb4564edfb537394ee701c75164984f586cb6d44f0d3502ba771351ea1b99a34ece6a6f08f2fad130b4ae9e31352e0c698bb8e292c22b0b27e4cf5e091790815c1b345b3da70cceaa790cc32f0332f41e858c0c21a1cc9db965f87788933688338989c257dc6e4d40060d2d19ee220d031901784fb8943870d6f895defd68ff22ae34b0191c827a2dbe953d25cfae7538292fa8223e96ff15c17219dc780aad6b957d93a727ee83b193d471b0f1cb067c9370462981dfdd5ac80ad4d51acd76130154daf0a69b6293e295de573aef5d21ceba40bb8042c1c9cbc983808043dd5411ad0a6ffc310445f151c459cbc0b2ee2878dacc16451fdb5ae58b6855197a5fadca9c71398b30e85cb7d273436afa5e5fdd821069525767fb224a4290da7aad6c1f7eb93b914f483085721fa116c8c6fc30a560163463bceae6b894fdd75e5db5c76d8caf3bdcedab2eef7b159894f4f4f0409e283599538b6846a2d282dde3594881eb8ca7a57542e59bc4223ea89a42e5a8ad703654b7205cf6116880a1dfa36c1b4fa2c08f98aacbd498fcd23f46bf9e43adae7a35456bc5c2e3519de0693db05d8ff688e6527afe3ecc90ec3567175721d27ad92c8aeac777baea950a93c3a049c56c3afbb45a3642adedb32c53d42a4a1c2957bb595c574a0e70fe5e445ea815d71cc099df5a3f31c4922082567e7145ea0fb8bbc9dc54e2bc51082d1d73150abaf505f7884d0ddacbbabd2e9503ae1f4c346100f4a7667149824f3e3b5f81a6c77603ef89ae897249edfaa0f42f7abb0927b4204379fd83c68701da5d42f07f4d99dadbefc8ec22f1189d6a0e4be465887840d24badf6f70f491907494b33cff5c56184706552c9bb4b11db3a9a3d978f108f521c88a4dfb76dc57d66f4a1ee9ba841671965ce8e2b3f74fa3d8885fedd7abde8746808c4ec298e2e4a8b0acbeaaa7d14ae422ee4d61645bc65e549d20f4c84425c5af286d203191fbef0e9af0e40c829228817d165ecf79823a53beff25994bbd2ace0154016ecd5ca88844366ae937579fe998d19acf6268763f6da446e0be66b51641ba9df3f9c27d402186dc8937a4fae5e15d4776774c09db93e9c8ad260cf8321d2f58930d03ff9ee9eb5d7a704f38a75cdac28ef710ce5a03d370a37cdbb53321a028dcb5eb29139dab73a2c0a21953e474f9749eff2c61dcc9582a9a626c55454780a92fd07d8e865af13b9393f03707a4a7a33776726dd5439481cd97fa2afa56a95b5cf474a08e6c3206fc792a1fac480ce369c69a79a1204c01c0e51f02e537c029dd2a004f612ae1957ab93934773da84be73234acb98f42033c0a00698d1b758e42915ae77356ea6f2099531f6b6a900abb362a499872f34957f597f8e2949e69ec73b8fb08b92c2e93d569e8179021246239558cc31f1e0d3e8d13fc17cdea79d5f6437c186c0dd546e51598030ac9604a6b37f05e49346e52e651726190079820b3860d77eaac77686baf5d5cfc0b8a584a46a33fac80d92f0514ac4844e9a8733cfb50b775a07a41717fa8506ac230021cd33e713ea6fe5f5d68687faa847c4675307d0d8b19b8a31f324a0100f35392800a5e8249a0024add0a574ba0c89a87ed6e8dda4ca3d37a6880924f9935536b9eec205120e8740a3418025c15b82a883df6a26d7abef9bfbd73e231f7c196cac1e946a8e9aeefb93499a8a56ccf38bf7ce670ac16e55a4b42602d0441cd9342ec34335f645ee091e823a11ae147e8e02b81493253f60abaee517fdb968bee0cc5fc18e9e0650bb6d955f536da2804090ae9024b6dc6ef5722af02497932453f334be19c0c3552fa0b943d05243c5b5c3979d2e04255b7017b67ce88bf5f801b5a06450d9d83f835923ab0b25e6ad9e700d38adaa996b830cfeeac7de2134c10e97c68f5fe565ee0f688c163ed5dbcf37f5df4c44dc8dea3594349c6c621fbc94f51c466d53840c49c5150795a930156921ef34adfc41151828566310e2ec0ab554772604bf020af2f9ba1327875eb3f95b189cbc4c3a8172487fd7756895729b59fd26bc6a5595c2b12b82ab1a1f066474f6fa199244283aa6ba04d4c0f07af31eabbb34198b3f1466d5a83ad9d2988b78dc2a0926b5b59bc08cfe927c4b0ae7ba422697f54b7efdaf037b79bc068190d145d42cb75dfc4ef0dcd33df2510363db6a3918d7102fa588b3d1ebf0774d69a1abe37877c55eb4e2affa8962ef79286821244d38badac98734a6327284bf4d4560fff39cc16cd9e7b5241d482cd123d8d96a9f8b6d44544cb067a685be943546694d502d39c43f68f8ccc2c4ea7272a6fbc3ef9bc1ac4f4cba4dacd5f41ae4f5dc9ae134da0ab74f9ae4d84e4ec4c9c365da0cbb8f416662fcdab1bb6a2fda6a6fee700c632922fd8eb0cf5a9770812d323c872c322067f8d06a8fbd2cfcd5a32ac5d847f48788f7514b91e05347071a6d1b9429c6e208d01b74116e796f41dbec5085715131b71d8bcf3b90009c874d7dee38807768f72be64e4a2514deea51d129d2779147563bc212f79328a290254bd46f1bb2f145e98b3390318e58d1cad63b976e7bd445a85907fec534a610bce7a2396680d3e0eef45e1ef7e0f2213cfb5663bde469cfe184fe0ee153511f0ec40ef644482f02394b22c6ad88bd7fb3fd8046fe80ef6f547157facae9b1d8e3502a10b55cda8d83495b10888abe1a1fc50fe94b0bd952bc35464394e7a9bb5956a66320b8233e525ac0cb75eccec1f2710073683e9c30513acafbf67c78c790f9bfc3d5fa6291af03cd96ab19cf6650798709050667b49c953cdc9f79b1f1e1e988196c444e4cadfef6e4d1f5cc68f65f0b17d53edf0a5f433c670d692ec3b8f1f10f877c443b7130b3468a43f9e7afea2a2a6301e9a176f36ba6ef97a028d7788d94381d53f881be6d3d16a99d17972f5566b343ed980ffe5ff480ac71778d861a4522ad804b1b4093d1c16db01eecd6e0d8ef2c2ca46ded96ca6a02d8ac7363934bbf2cd208c36d0eba0562e43264e2f0b64430971fdf0c791527b0fc98e9ed0667571e267823366d587218536384d6d8a1e60401bd3d8dc2bdf001a3263b440238a415c560975267b8d85424ef2c594a33249d920af3766808f84700a7ab5d009be0ac92e8f0b8d150ba8aa1bf20ee49ed0daa39db8a7bc682812d9c6662145912dab155b47eea3212d0aa6eb7ce7ccaccf3770b8de782c7453396bf9fced05bb6a7169541f729f4e7c12d3399239771fd62b09f31ba31f00dddfac52729dc61466c9379e281e18318d3ec6d4343ad50d588cf964b59cd504d805da0a774a2b37f3d954cbda3740fbe7dffee5d53de0b9b966464adc6ea8c23e25de50d8ccb1f71404813489abc37bbdf38ed2a8dad461adcf6730ddad66491d2b803197e39521c79357670ab02acaa937082b77f158474d33b63ced12b25228d1829005b37003f22c9f4f0b18b9cca9ef232e02191a7b02d1dd5b6f58561491c296333b32b4f8e8b539fdb46164d1581a5c6b4a53ba3ff1cd00bf75a58a3ac3a7c63851f5afeff39d837bd14d58c4f1d4f37bc3b620da41ea40401e9c53dd70a8a65c80e64effb8516b56dd8a84e4d21f268b200df43b3f54356f67cc225eb6449e4c482d36b57f7d5424e3577d89b279d612590a0e5765cc4580677bebe129285de4f2cdbb6cd29c71c344a50a232434cc696f68fca98ae1caf9bf0dfdb0d27acb3975f54ed9307ea9fd5de7a310677ec4584e7a6f5828339f99a4bf66317437f61c4a30b417e2820eef4cc48137b3b6fdf9a38d3a0055056cd766f0b072d4714e8104d996aa72263768f1f64fce6804d2715e73c0fff5b5f6e52ee484606dd67aea643b4dd56ecff16a913f44f76bc006bd816a3e193cf22737ed7cf2b64c68e4ea0a1bb92fb1d33483f2db7a115c49deff86be7fcd5034b95653ae7838c50b06572f04daab1c9e414ab64418e062b2f79821158a814891721a8e440b1469422aa041f016b3450882c47bf4e22e076060915a2ccb068f221b0dcba0c0a59b8b403d349f9b3b027a288c6f0b9d19ebcf14daccbf7a8aa44255503c71195d5c8f2ec8567523661af435ea6c5e5c7ac3a5b35d73ec1525043a71c5a07ce880f013d036f6158fd5215fd1c7b7bcf1f3703a0c2b56203d0da4dbe7c48c21c9593d8ec07444cec42ddaa9d0470fa7ec9851d35624bfe4924f10b87113d699b98ef44754aa3d0f6bd50477e97f91f15f4153fca922c5389cfba4d6ffcf18f7896122ecf19da0168b05ac945931e817479ced55935bdc6263076368102295a42ea3e3ded9e3b2f7c172f9ef83c56e0d7a2228e83909704a80dc7cf9f9b0a30c59a9a3f9e632cbd50d7336828e277bf215642ec6ae74f4fb30078046c8f76a052fa6d6cd99b1860a1cbfa4139dec5a2465b27a8e0704f35479dd2643bb4729c2e80dc1922b95c11bbe32dc0f9832dbc055eb7d831b4ed54bb063ba50649cb78ebd12bf61f2fc8ba9a194a3595e551d1d059afac7262a41046cf4a266df3943a39e1a1ccd1ffef8128c0faa159f2b699644ed8e09f830dc40784f0ea5a390550d58be131d0ccb190dbec3cebab5cefb79aa200db9a0a225ad689308a4108e3ef723d4794918f35279507f7eb3c64c93992e3d171fe6942bda17e00a196cc022998a1c0d3cd69834720dd52e9ad00118e57397a7b550375773f6b627efd5b1a9f36de7f9a2d39e44be20eeac6e648aefe704c569a9b6689d09ee49655db97ca36da861cc12b0b88fae2ae2dbb5c9bad3e5178befb4cc1f6f06d76cf35e69f19ad35d2abd81e53708ceff957b678d718c41a24bc0b82a4caa4b9c07c44e292677e6d4bf2ea8245c3eecfef66b3c15df919abdc4721ff24d2956b031b6be662af5dec3a1263b055acadc3f6f6fcaf34f44e1bc89ea30a8ecbe739d3052ff65db9c82f4a17dbcd24fa600a79344448f0fefb2871b64c77a7fb82e33c3fd3f7f564be3fa9a33deef96b7b5828af2d96bc74b2feb930f85756f149086f45b6578526d5cf36cccd8942a63f008f18adb617b670e31dcef6360957dc6c21e8da0aea69ad27367defb608e47c248fb2e116f66a20934c1e3a2c598d7e1b9c5c5ac3d7c82794826b15b47567f280f9bdebf8fa194eb0996b7606e8d1af2b68d4aebd75f5f0188fa6ac8987f163078d15c7623d8ff15f68512f78a568289ab7b8f7517317ee5ea4544a7eaf522b7bfd3895375a5198c4a0385dc1c18576ff47e3d34347fe5c04093910cd079574516544553aac14f5b903d2f2d40f8af44ba66b22a111c112def7d1b15c569077da57940eb32409cf26e7224980ba89a566362e3a8e0479eacc4467d56c305295ac2d61c5c959d73517df5625a08319b24aa24f0d212af6ae94907612e846e82a1a0300afa91e1e9b24a8275c409716bdb3f17962d6cb688bfcd45f9fc2f79a33b8990d8436fa92044f280217e9e863baf55509f3ea0ad5ec6ddad0bfc961811276038788bf8d115b0b6604697f0723196091ab54a7fb23a1e0817d28df8a2ff27c8e4281aedf044d2982fb0ba00eff20de7a0c13128cdcb6dfcef27e595ce14a27857eaf400071882aa54effb0f88fe1001243142cf1913c2ee16163694acc68251cf3307ee0764fc4b2f485e2c3f7f2fb209b3ae454595ac854ebe15", 0x1000)
sysctl$kern(&(0x7f00000011c0)={0x1, 0x28}, 0x2, &(0x7f0000001200)="46626fb5a48469c86a12a92771b19c1b48f44994994dcf9ff88772e15b70376570a3ec5cd98c789483868b7b37f2a33167b4d20eda", &(0x7f0000001240)=0x35, &(0x7f0000001280)="21579027733338091b42e11fd1f2306fd62d64de54c73e4814af72136f845b263c5b6910db310c747be8a8d6fe87d05899ba8d1bdf265e1115563bbd3947290b06ebdc4959ec33806baaa8981cbc9df1de662d091d8339dbc25362398894f516c5e7c06cfbf87b33237568c2455585d6c187c2b83a772b114848b13a0815b13097cbd6002fe092ec7e6db0cd673d032770828061a95cb036518eb10eacdfe3c0e464051d58a57685c3af9f7c0f9bbb0b664e09ecf7b5f8e0dd4270f8010e80d787455a8c319846486be7d2722a69abe5711a8348f9bd57203b852774770dbd3d0f2b3671", 0xe4)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(0x0, 0x4, 0x7, &(0x7f00000014c0)=""/55)
semop(r0, &(0x7f0000000280), 0x0)
r1 = semget(0x2, 0x0, 0x400)
r2 = semget$private(0x0, 0x4, 0x18b)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000480)={{0x20000008, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x100010088, 0x207}, 0x20006, 0x7, 0x3})
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000040)=""/49)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000200)=0xc)
r5 = geteuid()
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000040)={{0x9, r5, 0xffffffffffffffff, 0x0, r4, 0x60}, 0x4, 0x400004, 0x8})
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000300)={{0x2, r5, 0x0, r5, r4, 0x80, 0x440}, 0x40, 0xfff, 0x8f})
semop(r0, &(0x7f0000001440)=[{0x1, 0xffff}, {0x4, 0x7fff, 0x1000}, {0x4, 0x5, 0x800}, {0x3, 0x3f, 0x1000}, {0x3, 0x1, 0x1c00}], 0x5)
semop(r0, &(0x7f0000000040)=[{0x3, 0x3, 0xb1dca765c7eab520}, {0x1, 0x937, 0x1000}], 0x2)
semop(r0, &(0x7f0000001380)=[{0x0, 0x401, 0x800}, {0x4, 0xff, 0x800}, {0x1, 0x8, 0x800}, {0x2, 0x4dc1, 0x800}, {0x3, 0x7fff, 0x1000}, {0x1, 0x40, 0x1000}, {0x1, 0x3a83, 0x1800}, {0x4, 0xf799, 0x1800}], 0x8)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x3b, &(0x7f0000000100), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000280), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x2, 0x9, 0x0, 0x0, &(0x7f0000000c40)={{0x0, 0x0, 0x2, 0x0, 0x0, 0x1000}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x401, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004, 0xfffffffffffffffe], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x4, 0x8, 0x2, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x65], [{0x4, 0x10001, 0x8, 0x7}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0x2, 0x1e, 0x2, 0x1000007}, {0x7fff, 0xffffff7c, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0xf9, 0xe59, 0x10000000}], {0x6, 0x2000002, 0x0, 0xfff}, {0x0, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
getpgid(0xffffffffffffffff)
sysctl$kern(&(0x7f0000000200)={0x1, 0x34}, 0x2, 0x0, 0x0, 0x0, 0x0)
sendmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000080)="01", 0x1)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x24, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004, 0xffffffffffffffff})
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x1, 0x0, 0x0, 0x2000300010000})
flock(r0, 0x1)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x120001ff, 0x0, "00000000e74de40000000000000000000600"})
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000000)=0x8, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
sysctl$hw(&(0x7f0000000000)={0x6, 0x9}, 0x2, &(0x7f0000000040)="9854c0e31ca724b5fbd80e88ebe1dd2f", &(0x7f0000000080)=0x10, &(0x7f0000000580)="8073488961c1e05b7e8df600613340f0bab581ff8864e14b318170c15617cb4e8517d4dfdd409156723e7b734d363d3f38a643f5eced5a31b9d9cec0c039522466db4f337882588ba765fce9a847bb1d746200100000000000", 0x59)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x2, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x1], [0x0, 0x0, 0x6, 0x0, 0x5, 0x0, 0x210], [0x0, 0x0, 0xa5, 0x3ffffffffffffd], [0x0, 0x3, 0x800, 0x200000000000, 0x0, 0x2], [{}, {0x0, 0x40000000}, {0x3}, {0x0, 0x0, 0x0, 0x7ac}, {}, {0x0, 0x0, 0x400}, {}, {0x0, 0x0, 0x4, 0x1000}]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206919, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f0000000240)=[{}, {}, {}, {}, {}, {0x2}], 0x6)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240)=0x9)
r0 = socket(0x2, 0x1, 0x0)
shutdown(r0, 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0207534, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f00000006c0)={0x0, 0x0, &(0x7f0000000680)=[{&(0x7f0000000400)="04", 0x1}, {0x0}, {&(0x7f0000000500)="f5", 0x1}], 0x3}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r1 = open(&(0x7f0000000000)='./bus\x00', 0x100, 0x110)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x4, 0x0, "00000400007be167000000683009459aec4ae201", 0xb6})
ioctl$TIOCFLUSH(r1, 0x82907003, &(0x7f0000000040)=0x2)
dup2(r0, r1)
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000200)='c\x00')
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000)=0x9)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x28}, {0x2}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @remote={0xac, 0x14, 0x0}}, @icmp=@mask_request}}}})
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
r1 = socket(0x2, 0x1, 0x0)
dup2(r1, r0)
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000100))
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
close(r0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000002c0)=""/155)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x26}, 0x2, &(0x7f0000000200)="fedc3ff77c4517dd50075c9bc1ed8fa8e72d48029f6df57177b83c89b01141f52b7642eab8b84dee8bcfe0a335078a10bdb4eb", &(0x7f0000000280)=0x33, &(0x7f00000003c0)="0c93c293fd27b56aa91ad595cbb7860520dfd4232a2dd86673c0b5ce4963e38eb9e7813c5c519147af6bee94029d9a5f5e923d69ad2e33fb854f5bd91eb928d3b15b409f1c030319de2b42a71e8a54ee74d64567df36916aac7f8f28d39b29aa62ebe14f1ffa35", 0x67)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r1 = semget$private(0x0, 0x4, 0x1da)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000100)={{0x5, 0x0, 0x0, 0x0, 0x0, 0x1da, 0x7ff7}, 0x7, 0x3fe, 0x80})
semop(r1, &(0x7f0000000300)=[{0x2, 0x9}, {0x3, 0xf98, 0x1000}, {0x2, 0x5, 0x400}, {0x3, 0x77ff, 0x800}, {0x2, 0xcd, 0x1800}, {0x1, 0x1}], 0x6)
semop(r1, &(0x7f0000000240)=[{0x3, 0xffff, 0x2000}, {0x3, 0x6, 0x1800}, {0x0, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x0, 0x517, 0x800}, {0x2, 0x0, 0x1000}, {0x3, 0xffe}, {0x3, 0x9, 0x1000}, {0x3, 0xfe, 0xcf81320aaf5ac8b0}], 0x9)
semop(0x0, &(0x7f0000000140)=[{0x3, 0x101, 0x2a6c566928f6743}, {0x2, 0xfff8, 0x1000}, {0x1, 0x0, 0x2000}, {0x1, 0x2}, {0x0, 0x100, 0x800}], 0x5)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000180)=0x5)
ktrace(&(0x7f0000000380)='./bus\x00', 0x5, 0x18, 0xffffffffffffffff)
sysctl$kern(&(0x7f0000000480)={0x1, 0x17}, 0x2, &(0x7f0000000800)="103bb0a35d2771302a0e47ea4e96c803a0b684e7b6899bb7c0bcdcdb488ced4bfafbb5c291a3475540bd58b172cdb7e01a124bdb17a6772c9fa700adfa83728e45bd3a0766fda71c413c506f02aa799a540736e0df20813a64d133e25f88bc28418172139640f66cb338ccebb7da3369cdfdc6029eae49d0ff717484c99fe7ea94d484b23f7ca836ef0a8f4e70637514e369436c6f92906d139f66e807895651409df3a58b0c48d912049cb318b681af50a674dde53057e9694ef1c5c3a116d615a8e87d19615c82038023f0b00fd6e25f44f4b4645b92599d9b2071b1e6532f59d2ea0b5d60649a85c7a1bda4a3988068b9134f136d635594ab5055ab75a0b494101c7905e0c55c12877563c41fafaed9a7c83cd214ce9d692b825d802a6eabfff133ddc94d6f066f3de019d249399db92248035ac06fe69742052daa87e4e55a59f170e919f4265dff26a17b7593c7e838fdb39612dff7195d044f2e9c3241086d656a6f731e45057ce3711aaee86a5c80e152a3f1cd88474666df6d47fdb56d6100efe8577697f48a387d2d5cdf90e8c433c51cb736b31ff9ecd0845421f9ac178c20ccfa195c36376189cc5d84118741931efd67fc83944f0504381a0fcc69a534218f4bdc208d3f7672e4ca0108268e9bf97eb3fd58de5703c4b21e5d6f7783aa74831e85789485522574e7061b860e999f639db086b0d1b9ef45ec24cf3b30b41a6e7bae64a9ae919d46ccc93b80329481905c3a68a6986639ec3bc7e9761b42109dc3e01d0a902c9fdb0255c81df895538c24843d3e8da2a20a509d138e3213c88cd44397573c44f8266c0cd68179bed0972df0fc1e0d14b25e02f3a30298db5bda08430906614752e5f69fd2f88d551cf40b2bdf02e1572cfd08fae365124273f6eb470d469ff7d9ad252718c8b93c17956083163ff53832019feb254d22cb99c3c3d477ef7068ef7bfdee7500e3f86c404d347d2d691ac0118885c592fa55295ef4d4afbb603bbdde6c84ddc930a7d2cfd91cdb9201c43f12cbb596dcab41db7627e9ed636ea67428283340d8bbf01a06138c9918fa6e59aecb3846b979b425abac77c28a4c85d1a6c69b7576af0e994bc56418b29a74543e5ca3dee468b0aee2ecdf3de9ca881749c31e272e6ad0a66c2f1c7eb82b01de862f32af9a0703f3b4f1bf0a05df4a4745a2d106cda2812635f02ad842a891ef6ec620a0e79df2c4b05c8a03839470d8d20eb0cc4bcbd5ad70a9a5ac3589bec77d53a29fc32b6b1e6963129b1ce2fb2a7637a5b305ad159d3365d1de504ccca260920a8271b92a26308a86e0166696b4a89466d35d4bd562aca2bbedd5f4bf978bad3ef7e2664e67e05f933af911da356ffaf2e245734cf81b2b7d1bf52240a804dc21f23ec91f902a46701066cb7ff19cf15b9d5a0f715d92addb09e6503e2ac84a21e05865dcbc142070c2d079833ab632dcc0884c97b18da82f0b129051bdecb692041aabd09de527e6f257280d581f4065ca6c7f612f62e044bd95ec8f3b36a63395aad8de53c4f2a8b1030a5076c58c4bb8be34a66791647770dbbecf90346a720c3681bcefa2e454d386d345d8bc3701a0d27c1f8221ebc5309beace5d149af727e88ddb727150c6af3c62289acad31cbf97587ff743eea15028bf85067b134b33e51876a27177fc291a9444f20e6c688cfc2f8ac1542186a5ee2a18f4b69c38696c38a6b06befacc24e1c46a48d2b7f3784388242730b3f951c513dd83e6add58f488e642e46239c3bbedfbedce2cf4eaac7be1b4f0ad5db0069efd81908716a9bf26a934216625cb7a4f3fb927b6bf22c350d11c0a499d9d2beb3a95c23cf86ebadd75f6a66157eb6c4d4d2f2a71935c35c27da077010378f41b681f24d4d64175ad5ad8564ccda82b3b60b86369e70c519af2e567518bc1ab7164fe41fdd3043172595213082702bd61907656cfdfe9bc441b07b507fc076efa2884356016e508832a016f0029aefb2301172132c12591b83348a28ece7fda3e40aec9fd82b7c79b4c33e803f8a6307307caa94c1918d6673fac929c4f3ed1ac84b3b92e0d502aeff43b1dde2509395e03ea026769843542e61759119be24b0de4c4f830ca4363ef56eca685a75c78187cb310e41de20609261aa7b5408910878f5c80d9ae66a3d6a1123f778e74169c52a616f75b74f4948855d7dc54fabf3ae6ca6666ada28912b8b17c9d8397af0621aa90b51f3ee206d69021237d4d0dacf6510b4c63d1265452a8e7e8126a44730d051180723051e60bec853255b4f86a652802854a3f712d560705d7135c3a2fac1a301ebd5b51c824e6fa431439920e1f7c2ce9d4fa948de2002f9cf954307b5348c49a2a83656ab28de2b4bebe9b8b5e38be54c86bfe700bf83a4b3fd28a2bb4063b14b541eb6e02dd975f73d465ff033b341c594f999257e190e76fd612178440ebbbcfdfcc49721526e20fc0f307bc1784fa8e511110b4586720c0d84474a7af9715f0629be2e799db48d6d01bfd1ef8c65b1703fb7cac9891a3df5b1e547a9ec9e965ab962e98f633c51c7c9e2f9109424e61c8ad9b45f445849fa21606de73f5e69e32da0b542bbdbb329fe9ecdf5f146e0d13c6c848baf35be7383b099ac47e0036104d070843554598180941f49d8c27293156fb1b2c53aafdd00337238ddfc1a70e8f43496151a3753f4775c56f57288d7739c7becb48522e8086e03e49eebf4609bd122d59013315ed6f999b771c9adb9f30bf4209d9b05ce9fafb44c2a2a5611906f0567249c50cea43ee29f5030f19e6b345c116e6be613136f5765a0b14051594b59a0de88deeded13b9bbaa101a1d863f74be1154f910be4f51fdd4a451f2009455957e636a09b16852371ac1accb7fde816cf9dd4ef074323c7c744e073ed6b3a283e34e1e2b107a20834ccd639909a8a69b5d558ce56dba2199370abd3960a93b29e05e066eab0fbcad7a4c029012130f8fbb16518aab866a0f0433251131dede3ef7033e5f7c0ca651b9d2d66dae6ef1948bae4b9afb45ce70b95e79b1dc6df50f209a7a4bc0ae9028af48d63302048f5c6fed43b2446691f552476666f7e0f57e0eec59f3c2d7c324a9651916d5d10ba1541139e59607d9eacf88fcb2d863cacc83b2e64545460244647ece4d3465df85c4a5d214619f0085a14c16b6aafd82bbc59faf8f4405572a244df0f9aa277b5caa9e1e11e5924836054cc84064316842e95d30e2c96422ddd3212b11d90143e93a225082ac6ca8b5e86625bb82500c5ae2f10a95407465efce49e2f0d80c21cb926a4b8f5c88ce3f3ef9cb605d90c9f9098e419c25e97ba9e8efffce4a7bfeb19bfc5b8ab5f58ab61ec9422c5e408cad344883533a8d65e674b5b354c90ca8574934f8f034c3b682f0403d8fc087ee54d0dd76158de4565295af6af48b90bd39577a84abbc2e95df87092bf3e3c7573b2823174251025c933c3318a2d38c6c94b3cd6fdda45c840b04bcc62de746653105ada2ba79b551b2ad421c02128a169ff7238ff678c77ee0408eefa741690be4900bf9d69bcdf5f6082a139a4316ea3730e887b7ed57f9f60628be9ccacd0d0b7febfcf51b7f49ca0fa306cb5c6861f5ddef2bd0828db05d082932bb70ee6878759af104c18f0c08e6e9f490ffee716ab6d9142df4a7da16911e9b3c4c21bd0044615828930644684f5917419fd25eeb5df29f0fdb87e7404e4ae1f052a0016d1505f61cf3082c9587bb94b20adfad867a7338f78576f9ea347bf49a086b808ba56760097a5556f35053d9373877234baa6dc2e3cfaf8d9d6d52d8cbadc6baad876dd93fab78cc001b89a93e16f347025179adc2c8bb1160c661445a81a1bb6397f21cebea5abe3deddb1a749cd8eae0ef41489e0048260b0126f3f8701799472a717f509518e02e2916c1f1abc0b56bc1e3be3e3d3db3483d7e910b7b3894f9239aec4a9fa07c935b2f4f847dd67e512ca9c1830ce56a0942fc55df2f2734e5b8c424711694d214cbc676672140642d1e57bf62a3c214ad450d90af8e729a36fa665868234bc1fe5492183553b78e57ae62734dcaeb4c49b517998b72f81538488ee568740e65ba71796ccaf1a6893b9f758892899bbe150bad672712073a90778508896bfb56c824176cdca63a2712f5e0f6bc92389485fd2bc436d802702cd9fbf9f8dd3b59767d6d1113479c885699e2f88f25362eead95db5f78ea3a08b4ad4dce8403ea1861689b61d572a0904a890e241bf90edae8e2b6aeb0c7b3dbbce6566c672669acbaa7c2b8f893d40f0b1bf1d2b7f2af86737551aa5b8f2fbed9ed9261f3099c0ff8b56117487e7cd48b68368809723bb8140214d26cae307740edf644b485e98074b6f0e786935b73c1948baad38957512a3ab7db7c02d250429082e4b2099b81cd75897d3b678b9db1f722e35b01d41c827033ee30250b2fe339b578ecd04b54bfcbbd2f5e3d4fde317646f67e6245b6de3a434dda96a1f0ed6807a4a1195a4a4fef03e67058361e1799bb2fd213601f7e6cf43b90e533a7b844e0989c2d6a0e3df146d640936edce2f790bc570b48ba7b15f5d73c348f9ad5c900feb9751dba6a67e1fb0d73c2a2afe83cc525f4a18d5f21b23ce78e7b5ae7c08aa5ca706844e02576293523fc6e829814926660ff4082e7347c95643dad86e9afec8944b3b03f0d093219900055d674cabfeda51fa9750dfa0a326434b653ad37f52eb4551a95c6a95da0fb5d3f6c5476d74837e69993e4f677e8ff27ce1ad88a95fc628c39e3975ad19788598f6e7e7366b88d3e1f58b6864030c5b71dd660bccbde63cb09cf76687acb970b1efa8052e5464271e0848428af21d28cab6a74bd5860cb84917345a1b562e1d973d5685e31e1c3632e9a29f9fccd9afca6c57c1f3d648f3346699fef4a7609cbad8edf78bd4751f2055fb789fe06c1e6f364d04d17fd3e67efd112e1a5f1a4c848912e53289874576d7440d9531c801cca1a0070d385db6024cfd6ef7ca08a00aab8f62c53c953b8c8169c69e3a95c26ca0e4137a1661cf7671653f968f55de9a74bb572a13fec3361461b6d811d5498b50223424ec3d7bd8111106fd07cd2f47f3f62d252ee3c8f3dfc4b745a2554d71925c2c0defdb2e3a140559dee0abd0e33e90cab4288abe9ffaf3542b661c747327d01e0676f549c37d4bacecfbeac9943860de8c53d191be00ce349d529fc0f4c0cf1c4066b2fc3e697746cae437c454d40f61f91e1b71daecc2a201eb835311c03584c103b7c4430a423a635635837ae9ee7607a3f39acb5b79d8d20708239afe74357648d93a5b1d11ce6e83c56a01869abc7cb7942f5452a155fbc3b9394b5b199d2f541f2bea75d77274aac539f3363d53043e7e6fadf90c3118062d432b8ef998fc9813461f6b95d73a977183908c19efe8fadaed6737c0c2b359c85f8bf714bb42b175da69dc928177af4dff6489e3fe9684264651da2c34d565ff82cdb3fc9089cc257806f637275aa7d22c259b13a55273ffcf2668975d23253486a941c98f935873b5b290db8675c46621c1da647c0cd0c8efd1145cfc50033f362edf0bd02177a9333b1a6b641e9d1863b7ca182c80d3d52c956cb8deb55ae6b4b6026ca8b1bf36df1014626fcb7a611c4a09b77be059d799c9de72a64d6fa1a760e0e5ab33edd0a437b2b201758b87ca0332f39299ea6ff732936facb08bac8020b8d27032897fdd4688bcbcbc29e15066a1d0fcbb3b8a52159d05673f4e4a8f964b0821394d21a2d647e800e8789", &(0x7f00000004c0)=0x1000, &(0x7f0000000500)="6856d61a733ac0ad425b796b5e0b9ed90eb5fbcd149ea8855eb11cda65ce89edc4744da06eca1bdea27b33fa11aa10480e136e43a38934f39240eee15fa2e5d26c1cd1f07b1f970ccc561e07a3ecd19be9dcbb43366309b983ddc8903cf0b54a8e3a3921ba9da2447a3984b0df171e41e99684a5ca0f0f22b49f41d22872a250c20ff05309d130fd1b10663ea89f613f6261c6bf21f30a", 0x97)
r2 = semget$private(0x0, 0x4, 0x1b2)
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(r2, 0x3, 0x8, &(0x7f0000000280)=0x80008)
r0 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r0, 0x40047477, &(0x7f0000000000))
pipe(&(0x7f00000027c0))
mmap(&(0x7f0000ffd000/0x2000)=nil, 0x2002, 0xf0ff1f, 0x138872, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000300)=[{0x50}, {0x3d}, {0x6, 0x0, 0x0, 0x80007ffb}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2d}, {0x50}, {0x6, 0x0, 0x0, 0xff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x18}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0x4e0)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000), 0x8, 0x0, 0x0, 0x0, 0x0)
symlink(&(0x7f0000000080)='./file0\x00', &(0x7f00000002c0)='./file0\x00')
readlinkat(0xffffffffffffff9c, &(0x7f0000000300)='./file0\x00', 0x0, 0x41)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
poll(&(0x7f0000000440)=[{r0, 0x40}], 0x1, 0xfff)
read(r0, &(0x7f0000000480)=""/4096, 0x1000)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
close(r0)
ioctl$DIOCMAP(0xffffffffffffff9c, 0xc0106477, &(0x7f00000000c0)={&(0x7f0000000080)='./file0\x00', r0, 0x1})
sysctl$net_inet6_ip6(&(0x7f0000000100)={0x7, 0x2, 0x2}, 0x8, &(0x7f0000000040), 0x0, 0x0, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f0000000040)=0xc)
r1 = msgget$private(0x0, 0x0)
msgrcv(r1, &(0x7f0000000300)=ANY=[@ANYBLOB="00000000000000000000fe7f00000000000000000000000000000000b300000000000000000000000000000000000000000009ee0c280000000000000100000000000000000000000000000000004ef3b078000000000000000000000000000000000000f9ffffff00000000090000000000000001000000000000000000000000000061d29fd500000000000000000000000000000000000000000000000000000000005d1886c835f40e5a1d2171f80997752b8c841fd658"], 0xa4, 0x1, 0x1000)
msgget$private(0x0, 0x14)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
writev(r2, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r2, 0x0)
ioctl$FIONREAD(r2, 0x4004667f, &(0x7f0000000240))
msgsnd(r1, &(0x7f0000000140)={0x0, "745537e95a3d15c68210fc0103bbe6b26b9efdab9284ed9c61c35a8f035b370269e384739bb71b05dee418dc0b5fa14aebb281e2965cba7445d6c7832da74f44366962bdac87a1faf0afa2793c64cc41e2de672891f6a605ac08b2293791dff2a78f88c020bb1c9460ff96089b0d88ed9bbf063d3c19848fc9753518025dfe70728649f0c7af586e0d14da268f832d0483aed0ce231224586bdfa4e73628524a3d7216c24becaa2f1d8107cf0f5963054dd1ada92f3f467daf82888171dfcccb16dab78f04290bb9735cd79f03c26b1abe6d8a4d9dcc52bb4a51493f231a"}, 0xe6, 0x0)
setreuid(0xee00, 0x0)
r3 = getuid()
setreuid(0xee00, r3)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000140))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xfffffffe, "080486179c7d9234997b52006000000700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000003c0)="4491125e055171c5077f329557a30004856f276a74bb36778525298af8f7e8245d661132a4474bb2185340f8a3116927e7776de444c14b4233243a47aa8c7779222189299257a1a295db6954f15fcbf00ec4ec9a0f2212a452aa4bceae373d8c2107b4e185e783a97e180dc9e7d921b6722798dca9", 0x75}], 0x1)
clock_gettime(0x2, &(0x7f00000015c0))
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x15}, {0x7}, {0x6, 0x0, 0x0, 0x369}]})
write(r0, &(0x7f0000001700)="fb2fe7e986d81d7acfccfbcbd4a2", 0xe)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80105727, &(0x7f0000000280)={&(0x7f0000000240)})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x44}, {0x1d}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
syz_emit_ethernet(0x10a6, &(0x7f00000005c0)=ANY=[@ANYBLOB="aaaaaaaaaaaaffffffffffff86dd61bd131c10702b04fe8000000000000000000000000000bbfe80"])
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f00000002c0)=[{&(0x7f0000000280)='#!', 0x2}, {&(0x7f00000000c0)="57dc10a770d9e890cdcccad84e73728726dd6632032e7dba9ce94b6fbd107ec5f1c367a2e89cc4c6fbfd0cd0a83309", 0x2f}], 0x2)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)='E\n', 0x2)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
setitimer(0x0, &(0x7f0000000000)={{}, {0x800000000059}}, 0x0)
open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000200)=[{&(0x7f0000000240)='\x00', 0x1}], 0x1}, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x100, 0xffffffffffffffff)
sendmmsg(r0, &(0x7f0000000180)={0x0}, 0x10, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3b}, 0x2, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8000000000003})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x1, 0x6a5, 0x1fc80d8b, "0400e5ecc7db00"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
writev(r0, &(0x7f0000001440)=[{&(0x7f00000000c0)="6d3cf8e6f69d7c9f5524c3710953ffd7a37a423d3c0abb51267e940f86f27c2cf0107e1945c4fd66c08e677c4db3fb", 0x2f}, {&(0x7f0000000140)="f7d98f052351db059ed5bed53088f23e16ec2b87f7bdf027d359cee7056ad3a0013b5653910e1e2d321b8a6692705ceaf6ba5c8d32ec0fce4de867be1584e29f25f96ae29ffd5c1427", 0x49}, {&(0x7f00000001c0)="54b5046fc20ea7cdaddbc9cf4a1c6e4784fa3e3f9aad1afae156d66fbea63d21517401cb1ad2cc3a6cfcf766fa12", 0x2e}, {&(0x7f0000000200)="edd69c429a6e2314517f44317bae5eda038b9453c4d6a423cea1515c1d2b06f97a21e16669e6a25d57bcef682f6f819835d8848c606de9b6125a0ebc6ba2ef9b51fd1dd2093c98bf4b3fb8ef6d38d53892b553e10ab2da8963b282935f9ed1d12079c28e4f9168f66e31bc7dfd8eb0cda216f0459e46ad458585c0377387832f676f0b08374c1b924d352117de79ca3cc80bb6b8882908418e110c8acc8799d9b25a3301bab1f183513f8d4f612857a3f29500", 0xb3}, {&(0x7f00000002c0)="fe156281ded32267e634d27840a0f3b88fa4bcad6673db20f41f497206edb05d810978a258982f7bb4bc263ddd9ec289d5c94eb3fcd7600bd3a87566f6c656245691541763e815cb1c0cb4814f5063d0b76f9735e955ae2e80d4253e35a8b029f9377092482187f946793c2c69fcaae0bf0c6c4e470bac2d96e362e31faa367fe8a0be889b7e451cbd2f08e470d614b563df369788ab4dc4f15b475ce86fa26ef31a6cc5fd708f8ceb2ab1090eeb7e155a327b4a11bbbc0cca1497aa145fca1f3cc20f9be9cb82f8a152bdb82d07dce0c6acbd2a02e8ecf2f5c7018082d451", 0xdf}, {&(0x7f00000003c0)="031ebd7ea4d8a19f6e31ec5a5a526a8b4ed600771b2f70c847ef531af7572d90d97503fe58b4272e476dd83eea969a4f3ce116ee5914e31225e2f65ae6b8b6f5654b15e3c08e1c387b9269e07db681d2e47d412d5604ab3dae6454c9c94a8092ba768755a647103d7c1668321ca9b564b0824a7e737c5e8d699e7809adf002a47e6683d2bc6c4305b6c4642f1393c8f69485ba8020b64dfc7dacef77a4778e9bd63c98637af3b29121a17dccc431613f762d4641bc676d3231d3ff42a24f215fd69c919ac51777df31e49e83dec583449ab0f6781fed67eeb5465dd3c1233828d3cda62ee7c305453578117e456c7d937eff03183b0390d72a42458e74c596d807cce76e8bdc8d46c51f528c11e9eb1b6abc0e2ab779113455c213f60d34dccd3ed1695f16ed019042097da2ac604e9bc295f3008c0981ca4537b3768498db1fd5fc198975dd444b8131ea2ea6014a032b8662f45c8bcbe444324af10986f70fe3f1080db4f0de5e98e2660e7abbced1d247c5e87dfdeb03d37cc5f4111bbf18e68f556de659b13006a93f6bc13c95bf51e5d95ef2b18c8c77bcb2148d949d4099ec1de9fcd473733a5a4bb1007941dc4ed313f96e4d9c27f81868d4f01bc2b656d9165c0dd97421b48024621259beb995c0f240c845ec9d1d678d4facd7fdf4d7e3d27e726567a41b1e4307b3812fa5761bd9525c7ea66728542347fd2e0d3b1b1a7e4934f93399bc9b185a43d8a4cd6f8f7734327c37d5a3ae8ef5fa040457e0672d9db7a2a4c33e64ad910b676758184f8d266be001983b566947c2e8225d37ac6176f92e5c3f83733e8f564b45984ad7e4067c6ba545cb03053c5b762c4e22e90507ccfe2b3a16308ca444741146e6e469233c25b9254debfc5e65a84ca747d6461e9ca52b3271abbaaf8aab6afcfafc7daf82ca6bd712fd0425237732fa0a66c89aed0ee9081ee19c30ca0e6782beda1a74eda81221d88e2315fc810cfb52d628ba417f8b183e614a847da67f7338a15889d96c66ddbf2c8ad15315fd3d1f1993afad0b4aa2618197a0e375ce91799164f234bf61e5500a96e77f411c06e10f7e53460e10c808a521e8785f6e8368625b3ab0a22b646dbbddbaa965387b69012a3d4caa5b69b942316dbb7e499f08d6e04c8b1f915a099b376fb5438f3a25bf7e74e9702537114cd4afb43f7ba15c3ac59c7989058781c801b354ed234c65314670e309b16a651e9017254691970a3014bc81dd3380fc2a269ad9214150174f437f103eb5fb2fcb8ea8beb3440e3d3ad7d3281e6f716924223f4865cb40f81486f38cc616ed08fd2baa266d7b5e816d5a2525b8f32e3a69d3a6b6c21e4b73c6e8cf64cb06aa043e9d9ed0cb53df4318bea6b72c835e9503555c9f3dbf65028d0e3fe28b9c6c87ff974c0ee8d44f22f87785603998c25e1496243580b5677e977a60c7a7339b409cbc9761c01e6bd9f509eda1e23609e1707912033b16c55d7fbf691424c3dfe5921ef014681ece3b5c42f282384779d14691de45b8722082da5452b1f3c3ac794ca3eaf0e5e90655fe11daf390d84e8c3494d2dec17b97bbda83fa95ad23c1229923448cf9ba93dcdd6579ec1ade5419f4148b1b2a9d71ff38cecc5f25a071e4a9bc32014079599fa476af6012d6f05298125091e1117bba9fb1d001845df2e4a8b3788a8dfe1517dd1b025e310ce2f10e39862153bc9d478674272450f20a5ec70d181ac808042fdde925f41ef15496009c02471c739ab589365a4ce35ff40a50b3e0070a39ba627f3d91094e62af698b24bac440e3dfb5ea9364944a41b7a4fb4461329d037342990c6808d9c90c2a64b1b9186ebb714102af4a6d2cfb746a2cf5f7b882af4e66b60899d57a02830b6f63710511a53ee7ce0beffecddba9fb68e77553134aecb1e6cd891213d814b70f34232c0debcb16d45258e80359e0f4ad1d0c324c45e4fa1715349cc111917d0054cdaac4742495061ab223488398964b61cf0964caa548df8459ebf080aa0d1c197175907b9f181d186020864ae73d2648c7900c826eb67f3b2b5a070cdebbe55b0866aeda64475e4dfe0c0f9c710081265fcdb2ebc7cd5b42f50457d1ee348562e6395c181f54735cfa63a919201f24a95ab68b2bb1d58d5d5e79bb54c9192c90fb45d34a32cee873c957203b6bad95364b283f9e644c0f0fb81d87787c2077a1fa2351e877392b6871340f84cd4ac70381846b4dff8900a86777f08b9077bf88953578e8b1dc75f6eb2472e78982a3bb725ada60c7375e546b29979039bd928c1cf5097bc9842c47943146af18b073a696e34a78c5c01fc2f33b1f0c3b516dbf20484811a363b98129595fa1f7239bf16835c893b080d7f89ceb6bc254df282aa6887c854d809d31354daf9df9f7b10609565f97d19e2af80bf1f8d4d71cd1f926880ba33d493979d1d87dc92f78df2ae8012041b641add73a18ea64b565140c418a6b24eaff9b28a11aa5c9b698f0b76327b3b6b7c4ca22bd0d3a9a6bb931753a3bc707e550865d5ee3d6ae3871cda51a8944e96210e9ead1fb63022c7ae61c579ee5dce6eac03733090f92d6442d8fbcc22c55185423505c8f25631acee7ca4a9ef7060389bf5bf44ad7e595e02a9f90a06f324a311906003987225114a65e7fb23db916b2cac3e9b56b4e343401f3bfb9f97dbe629095506efb52d97d612cbf049fbbbac7706a2f2036937667466578a3f796785a6c5357a45be3ac66870cf77adb077a7f8bc7f76ac614bdbdc69d7dbad26837344d26444925c354092bd43316422aada5f183e10f4ad9c88439de2b73ceef9e555e6e1592ea0f4630cdacd4ac7645d7bc5b5ffb839c83fb01c7e416a654116dca83f513929b50d7d2b1612cf4a252ced5dc2b42435821d79202f80647be36bd658bb2b766efb4691dcdd50bc963a830c15ee8f7e197d503aea046ea0a8aaca9422db53b8bdafec65ba614c918db4caa8bb11eb406a846823f784181d6c9a8dc90e61ed4b09fa0ffc27026e563eba10133a4e35e2075f3cfe74bf186be51a20526c282e0712de86eacd6fc1710a270b7e9ca01c95ff2c99ca180e3b12b529e44bb70d7e220abc70b36efdc9bf08ba973ee505777c98be4655022f966d1daa33ed9d3f7f08efaffd1504ae29387369cec637be2588af83e74c8afc2b87e8f07eacd68e4be001b862a36445a267385725357d688c77533e132e22135e19d4026c5aeb5f74a6645f162242b6d6c922ba2910afbb942c32f944789d9bbb88c8b73f0f9cee81c3b172f3ea98c288112ebfc7ff7df5d8a35301f2d4848b5d72c0b1ae9dd989800e1454d3a18bae2d1552f74ac456acfd0e1549097b6120a3c7cce931075ae6e2ceea01400b3b4b27d49ddaff863ed4501aa5ed811cc678bb02a4c4eb2eb1e75dec122a26eb604a2906b9b388dc65df68cf003042d9c3c92bf4466bac4db3a2790efc20c97846cca81a831552704166522b90641a836d67f7c0a23d6da8cf55091ffecddf90caea2aca48bf055089dbdac5cdd2f34d3430a9c8c04f4fe7cf8cc5053dcd2bcd6f56fc757394ace5b389705f59392ed719cbdfd1e0afdccbfaa2d2c2358e048baee19afbda4baa55175ac62107a03ae28c2a9273ffb62d6df3140026886849d735d0cd2fab63919e17caff86714e0b47887b89c50028951de66f5ef7444764ffaf51c624d71e2d6443c931c9d65414f02fcd08826669c3f303b7e616b312c6349173ffedecf9d68eda0f1b2851078a702acbbfa3b7f9b148415a5abf15fc8f6dea2735514d27c0f10d31557769725500ffdeead1b18457aba7c988167eec960d3b106ab9a90104675f9ee2d3935444b0fc6eda04b510ee60d22eb15c25a0503185cbbe267ac24097f70537007f3bcc444fe3560fe479117178b7d2eda444cdb0bcf5b1a3efdbbdec97ec22b431c79733cbaa69a19f67e053e321ab8b0bc9ec8dd4d37ae89c77dec1c1722f7b808be306ab355eb8a60e2947948663b5c104c53f80b56305ddbe8c651c189cafaa7fb4c4d35168a6101a10781c63baf13ed3e16f9d2ad2b21c255e128662d6a235577597130d7baa27437908b3c87fa618ec7657a6f6863b4c8f8a63be97e41900078b53c04d0cb8a51bf9736d6bc2ccab44f21332b2ecde538fe93f4e19fdbdb108d113e9479a0602ebc59a8a9d4f79cc4d23d0bf1ce43dc90c58ae5894d7ad6f7867f73dded82792da086f4e426ddce4e36f800c243367efa4a71d4532395749a6bd16ba40de52fb55e0f27f54e1592a96172c9e78fa1fc36d457740e176e31df72e9189e8e9f81f8c19619f10eb1ed50b4fddf063b4fcc6f1e821ca355d4bef264d59f5cc942d160a20ae16da822e49602cbdf0c5ffa767bb6eb3137f5c2198c451f9702a7e33552da65bf1d12dda6cc63d141bcfc6dc4acd0fc3f19cc3143a6daafb2b6d0cc361f8f148c559251c3039d32478adf945e5d1a8806389e9775653cf0272397a85e169ed7d87bece7c8149deb1348d6ca82c36ba114e6ce2340aa69c7a8ebc77ecf6f89dcf9e9d1141f526087065bf6c712430401963bc5715cde13d43adc7af4aa78cff5c663ebb9ab025bcf97b3dccc5116968d26e696abed47f538a4bbbd7fb61a0fcf8779fe15f53d904a73dff921b8acc56fe33c4cc5acb8fa7070b795aab941b18fd40d8f7e5cfb3f459a1e492930923e3790081e47e6329cc4c3cda787a0da552ca507b9172ec3f57d325b3f31eef669217b34ddb015a05a47c9146d5e420792cd76af65d007656850eff31de5e0f131a4e3814956fa20409f8be857156442b10f2f5ad17dede26fb7fcea645a94b40bac2baf4d4b59823408e59d756af441f194d16c74fba8c1b835fb7d54f58346ddf0986145e78ec360eb2129d338cc23cbec684f95e1117fc1629a5617e8412450b1432654f56997abd1b7dd15993b4ad4903d1e3627ff23448e43a3402ebda21f0adf44a47bb924b797d4687f661e11aecd63e21260603c682963d289a2b7b5ad03b02654da25824ecedfed7f5a4f74066d18b733e98a7ef606623efb713a7ecc961921876576e37ab32388a1c257d018331bb7e5ed961d2a9c8707042682eb4c297fc99d2db19a9461223f406fe64d069116ca4970597612242f59b56994175f06dc8e1d97af794e80ee7e929fdea1874a95f13236a6ae1e20a6c737238582c5ceb2842e8f6f52b70e14c62c36ba36bfec19d4d131e15b4d14144598ece5aa2c22cbc62f8d88ede04f38a26089485eab9c0eab3fecb4f295a2a81e9797e49d1f0a053480d888eb2e73196b234e07d039dc07d57817f907c9f35b977173f655d3dac9dcb6ab8c3c146bb1db4b9e455b79f785cb2b6c905710cae0aa3201", 0xedc}], 0x6)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0xc2)
mmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x0, 0x2011, r0, 0x0)
mprotect(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x1)
mlock(&(0x7f0000e72000/0x3000)=nil, 0x3000)
open(&(0x7f00000000c0)='./file0/../file0\x00', 0x80, 0x1)
r1 = socket(0x2, 0xc003, 0x0)
setsockopt(r1, 0x0, 0x6, &(0x7f0000000000), 0x4)
r2 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r2)
syz_emit_ethernet(0x4c, &(0x7f00000005c0)=ANY=[@ANYBLOB="0f09000533539382f251647566ca9200fe0c6e367231b9eff0cf347034e02c67d3c9b9584e1114f3f42e8c5f0c687fb68a646008000000d20003cb6c28607961dce717f60fa81213e8587f0ec9039eb974cdfb40bb6e9adb83c8db7799bcee96debdb88239790bf1c4107f276e536acb8d2b2ec54133b3b1394aa63edbd3042b743a0296140e7702594372d8402040f512d281df4035c3000000000000000085420294627d70d547fb243ec2ba3a7a0bbc51d0949d7850938e33b296cc306f733f225c338d29e6216c62debbbe6cd7423fd57ebb680b11ce533cbb33a8a460cc4b517b8f084179ffcfd3cdd3536410ff557a63edc0770bf00aed700f6f3f7a8baa6dbd904670e02ac7144f52c357376b3f5dc05c91f31e9f67edba72d936ee8499e0e527037f796fa41d1d8e21a014303883154ad6bf5e22c73b8c157f82620ac28e30752bdb6eec78e083ac"])
setsockopt$sock_timeval(0xffffffffffffffff, 0xffff, 0x1005, &(0x7f0000000000)={0x9, 0x3}, 0x10)
ktrace(&(0x7f0000000100)='./file0\x00', 0x4, 0x100, r2)
mmap(&(0x7f0000d62000/0x3000)=nil, 0x3000, 0x0, 0x1012, 0xffffffffffffffff, 0x0)
pipe2(&(0x7f0000000240), 0x4)
socket(0x10, 0xc003, 0x6)
r3 = socket(0x2, 0xc003, 0x0)
setsockopt(r3, 0x0, 0x6, &(0x7f0000000000), 0x4)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r4 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r5 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r6 = fcntl$dupfd(r4, 0x0, r5)
ioctl$WSMUXIO_INJECTEVENT(r6, 0x80185760, &(0x7f0000000080))
ioctl$WSMUXIO_ADD_DEVICE(r6, 0x80085761, &(0x7f00000002c0)={0x3, 0x8})
r7 = fcntl$dupfd(r3, 0xa, 0xffffffffffffffff)
ioctl$VT_ACTIVATE(r7, 0x20007605, &(0x7f0000000280))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x61}, {0xc0}, {0x4506}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@random="4882cde87249", @broadcast})
mknod(&(0x7f0000000040)='./bus\x00', 0x8100800080002002, 0x2e00)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000700)={'tap', 0x0})
mkdir(&(0x7f0000000140)='./file0\x00', 0x0)
unveil(&(0x7f0000000180)='./file0/file0\x00', &(0x7f00000001c0)='x\x00')
unveil(&(0x7f00000000c0)='.\x00', &(0x7f0000000080)='c\x00')
rmdir(&(0x7f0000000200)='./file0\x00')
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0xc2a5bf9)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000080)="179653e485eeccdabbd274229b5b41b4ca5252bca3e4f29c12ad7c47ef10c546a344c13ab2abad207618", 0x2a, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x1)
syz_emit_ethernet(0x170, &(0x7f0000000040)=ANY=[@ANYBLOB="aaaaaaaaaaaaffffffffffff86dd6912441a01000000000000000000010080911938115cdfaddf"])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f0000000140)=[{0x81}, {0x1}, {0x440e}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000040)={@random="c028a1b52dee", @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="3efd8ec01613", "", @random="f4716580cdf1", "79d01739c9643097cfc3e546281f260d"}}}})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x80000000000206, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000380)=[{{r0}, 0xfffffffffffffffc, 0x51}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x36, 0x0, 0x300, 0x0)
link(&(0x7f0000000000)='./file0\x00', &(0x7f00000000c0)='./bus\x00')
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000001700)={&(0x7f00000006c0)=ANY=[@ANYBLOB="721897e624158600"/18, @ANYRES64], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0xfffffffffffffeac, 0x0}, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000180)=[{{r0}, 0xfffffffffffffffb, 0x11}, {{}, 0xfffffffffffffffb, 0x73}], 0x401, 0x0, 0xb200000, 0x0)
r0 = socket(0x2, 0x400000001002, 0x0)
accept$inet6(r0, 0x0, 0x0)
sysctl$net_inet_ipip(&(0x7f0000000000)={0xa, 0xe}, 0x6, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000040)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27ecb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1abda71601a8bfee8aca4911faff5a872c881ff7cc53c894338796362f1a9f2428b1062851bfc03b22f310b404f36a00f9000fcffffffe608a371a3f8000400000000", 0xb1, 0x0, 0x0, 0x11d)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x10016)
sysctl$vm(&(0x7f0000000000)={0x2, 0x8}, 0x2, &(0x7f00000000c0)="c03544a6", &(0x7f0000000140)=0x4, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x32, &(0x7f0000000040), 0x8)
dup2(r1, r0)
setsockopt(r0, 0x29, 0x32, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x2000300010000})
syz_emit_ethernet(0x36, &(0x7f0000000000)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @broadcast=0xffffff}, @tcp={{0x3, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
socketpair(0x1, 0x5, 0x1, &(0x7f0000000000))
r0 = socket$unix(0x1, 0x1, 0x0)
ioctl$LIOCSFD(r0, 0x40047309, &(0x7f0000000000))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e56f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000100)="a30000", 0xfd05, 0x7, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r2, 0x0, 0x0, 0x803, 0x0, 0x0)
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x3}, 0xc)
r1 = socket(0x18, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028756b, &(0x7f0000000300))
syz_emit_ethernet(0x36, &(0x7f0000000100)={@local, @random="016fe9901ee9", [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x0, 0x2b, 0x0, @rand_addr="8ab32d78d67f3a344cbcf951919a5b00", @local={0xfe, 0x80, '\x00', 0x0}}}}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x32}, 0x2, &(0x7f0000000080)="cab48953", &(0x7f0000000100)=0x4, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f0000000100)=[{}, {0x2}, {0x0, 0xffff}, {}, {}, {0x2}], 0x6)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x4})
r1 = semget$private(0x0, 0x0, 0x625)
r2 = semget$private(0x0, 0x4, 0x0)
semctl$GETVAL(r2, 0x3, 0x5, &(0x7f0000000400)=""/140)
semctl$SETALL(r2, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
r3 = semget$private(0x0, 0x4, 0xe2b)
semop(r3, &(0x7f0000000240), 0x0)
semctl$GETVAL(r3, 0x1, 0x5, &(0x7f00000002c0)=""/236)
semop(r1, &(0x7f0000000200)=[{0x3, 0xfffc, 0x800}, {0x4, 0x9, 0x1000}, {0x1, 0x4004}, {0x3, 0xaca}, {0x3, 0x8, 0x1000}, {0x0, 0x8360, 0x1800}, {0x1, 0x8, 0x1000}, {0x3, 0x1, 0x800}], 0x8)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r4 = semget$private(0x0, 0x1, 0x0)
semctl$GETVAL(r4, 0x1, 0x5, &(0x7f0000000400)=""/140)
semctl$SETALL(r4, 0x0, 0x9, &(0x7f0000000180)=[0x0, 0x0])
semctl$GETZCNT(0x0, 0x1, 0x7, &(0x7f0000000140)=""/15)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
semop(0x0, &(0x7f00000001c0)=[{0x4, 0xa52, 0x800}, {0x2, 0x1, 0x1800}, {0x2, 0x8, 0x1000}, {0x0, 0xfff, 0x1800}, {0x0, 0x6, 0x800}, {0x3, 0x1f, 0x1000}], 0x6)
fcntl$dupfd(0xffffffffffffffff, 0x3, 0xffffffffffffffff)
setgid(0x0)
sysctl$net_inet6_ip6(&(0x7f0000000140)={0x2, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0x240)
r1 = msgget$private(0x0, 0x200)
msgrcv(r0, &(0x7f0000000200)={0x0, ""/104}, 0x70, 0x1, 0x800)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000100)=""/133)
seteuid(0xffffffffffffffff)
r2 = syz_open_pts()
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000000)=0x5)
r3 = getegid()
getgroups(0x6, &(0x7f00000000c0)=[r3, r3, r3, r3, <r4=>r3, r3])
r5 = semget$private(0x0, 0x1, 0x202)
semctl$SETALL(r5, 0x0, 0x9, &(0x7f0000000040)=[0x0])
semctl$IPC_SET(r5, 0x0, 0x1, &(0x7f0000000100)={{0x1, 0xffffffffffffffff, r3, 0x0, r4, 0x100, 0x1}, 0x1000, 0x5, 0x3})
msgget$private(0x0, 0x581)
msgrcv(r1, &(0x7f0000000000)={0x0, ""/151}, 0x9f, 0x0, 0x1800)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
writev(r0, &(0x7f0000000240)=[{&(0x7f00000000c0)="fbac", 0x2}], 0x1)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0xfffffffffffffffe, 0x1000100000002})
mlock(&(0x7f0000fec000/0x13000)=nil, 0x13000)
madvise(&(0x7f0000ff8000/0x2000)=nil, 0x2000, 0x6)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0xc02, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000440)=0xc)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000000c0)={{0x0, 0xffffffffffffffff, r2}})
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
syz_emit_ethernet(0xfa, &(0x7f0000000280)=ANY=[@ANYBLOB="3b1bfddfc55effffffffffff88a8020081001e0086dd6039789a00bc0800fe8000000000000000000000000000bbff020000000000000000000000000001", @ANYRES16])
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc088444f, &(0x7f0000000240))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0xc600000000000000, 0x100000001})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
mmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x2, 0x11, r0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000000)={0x224f, &(0x7f0000000040)=[{}, {0x0, 0x0, 0x0, 0x9}]})
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x4}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3f}, 0x4, 0x0, 0x0, 0x0, 0xfffffffffffffe62)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000003c0)="dfb43baac0d2520dd107f9d8367aad32b0b12ce8cb5d778117532f2c8d4f", 0x1e}], 0x1)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000100)='<', 0x1}], 0x1)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x0)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x3f, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
r2 = socket(0x18, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc050756a, &(0x7f0000000300))
clock_getres(0x0, 0xffffffffffffffff)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x80}, {0x15}, {0x6, 0x0, 0x0, 0x80000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x28)
shmget(0x1, 0x3000, 0x8e0, &(0x7f0000ffa000/0x3000)=nil)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x7b)
r1 = dup2(r0, r0)
ioctl$PCIOCREAD(r1, 0xc0207534, &(0x7f0000000080))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000080)=0x5)
preadv(0xffffffffffffffff, 0x0, 0x300, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x1}, {0x15}, {0x4406}]})
syz_emit_ethernet(0x4e, &(0x7f0000000200)={@random="670fb9280734", @random="1c1ddb53ac8f", [], {@ipv6={0x86dd, {0x0, 0x6, "24f012", 0x18, 0x0, 0x0, @loopback, @loopback, {[], @icmpv6=@ndisc_na={0x88, 0x0, 0x0, 0x0, '\x00', @mcast1}}}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x18, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0206923, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x48}, {0x87}, {0x6, 0x0, 0x0, 0x40000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0x6c}, {0x7}, {0x6, 0x0, 0x0, 0x8000000}]})
pwrite(r0, &(0x7f0000000000)="0aa8c8a186e0964033e34b8914aa", 0xe, 0x0)
syz_open_pts()
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
setreuid(0x0, 0xee01)
stat(&(0x7f0000000180)='./file0\x00', &(0x7f00000016c0))
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000180)=0x101)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x2, 0x0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
fcntl$dupfd(r0, 0x0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r1, &(0x7f0000001340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001240)=ANY=[@ANYBLOB="100000002900000033"], 0x10}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000240)=[{0x5c}, {0x20}, {0x6, 0x0, 0x0, 0xffffff01}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
mmap(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0, 0x4010, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000280)={0x0, 0x4})
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
read(r0, &(0x7f0000000040)=""/32, 0x20)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="7002020be0000001"], 0x1)
r0 = socket(0x2, 0xc003, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r0, &(0x7f0000000740)=[{&(0x7f0000000040)="f81ce5d76744f722b30306b8bde76973e2b3c19a28794aae0cf6ffbb9bbe0c4b447491e92d0088177f378c94abce2e03c8e482f86ac1d7a7114df046f810851c0ef5e38c687dbf9121e78083382f31d8532c122a6c95755d222a7bbc2b33c0108d7cb439ec116cfeb8542a1ea6766edf5394377af2649bce95f0984c2369fa439658316434d6237877729526d67d0250a6", 0x91}], 0x1)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x41, 0x0)
open$dir(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
pwrite(r0, &(0x7f0000000140)="1b222295b35039cc1954f17eeed1aab3f78e023024554270834053e4f1da32cc8a3cdd5a5fc80077e8e04e051d27f20796b453f219da9fb0dd3686e364858ba205cf1c91cb783614f5a35dddc35dcdb25bcc9bbb7ff66795c30b8b00ca1270571003f881f3b3cabbb535946b5f9e05a49252ae5a7028c45a336e2fb516376a3749037fde307a0a1bb5368e87fbaf048d138338656cc5c41429333f730bc84cbcef453a0c1a616213c419b017b3fc776b76a2d07140220a1a55a3afa8cf8a7a02", 0xc0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000180), 0x0)
r0 = socket(0x11, 0x3, 0x0)
socket(0x11, 0x4003, 0x0)
socket(0x10, 0x8000, 0x19)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000000)=0x1ff, 0x47)
sendto$unix(r0, &(0x7f0000000180)="b100050460000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd38781ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c5000000020000000000000000010000000000000000000000000000000000000000000031d880e0611fb13ea9646dc01549210efe33fbf9f14a5b3a7a1487114b54c8b7e143d983598ca406b871db9eb5bf89cafb9316eb1d4e22015320bb82880751fc771f17de896a04f55ebb789f878917989f98c00daec1188ec802dc3394bb10b15f505807d16d20c7636fdb3aecb23dd794793dc1d42ee2f3fa8398aa007b6b9b270000000000000000f14c856cb8dd21bcbe10e92a6e6bb07adb33fe3879b6915965953970187c7bcd0d521666bc9b31b976066ccaf427c8e37d0672fce80faaf138eba41819775dc8a7e78e3f499d680267b22d2d721221ce7a7573a8ee223a32ab271f2cb39e2d6d3eba984717d2", 0xb1, 0x0, 0x0, 0x4af)
read(r0, 0x0, 0x0)
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x200000005})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
recvmsg(0xffffffffffffffff, &(0x7f00000002c0)={0x0, 0x0, &(0x7f00000004c0)=[{0x0}], 0x1000000000000345, 0x0}, 0x0)
recvmmsg(r0, &(0x7f0000000100)={0x0}, 0x10, 0x0, 0x0)
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3300)
r0 = openat(0xffffffffffffffff, &(0x7f00000005c0)='./bus\x00', 0x200, 0x18)
close(r0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000680)={&(0x7f0000000600)='./bus\x00', 0x7, &(0x7f0000000640)='./file0\x00', 0x8})
r1 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
execve(&(0x7f00000006c0)='./file0\x00', &(0x7f0000000880)=[&(0x7f0000000700)='*\x00', &(0x7f0000000740)='\x00', &(0x7f0000000780)='&(/[*\x00', &(0x7f00000007c0)='-:&%\x00', &(0x7f0000000800)='\\\xf4\x00', &(0x7f0000000840)='}(/\x00'], &(0x7f0000000980)=[&(0x7f00000008c0)='\x00', &(0x7f0000000900)='\x00', &(0x7f0000000940)='.\\/+,@]\xa1\x00'])
ioctl$TIOCFLUSH(r1, 0x80047410, &(0x7f0000000200))
sysctl$kern(&(0x7f0000000040)={0x1, 0x4d}, 0x2, &(0x7f0000000080)="0e9c0a31", &(0x7f00000000c0)=0x4, &(0x7f0000000100)="c9ebea4f", 0x4)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4b}, 0x2, &(0x7f0000000140)="09465634cd95edd294ae", &(0x7f00000002c0)=0xa, &(0x7f00000001c0)="c21ae9d4d2d514c1ae8d25d1b6d827caa97a57bb528d4a248a03eb37d986a764f78b62b37ab0ea6fcb9372862e319c12254180be698cbe4bd1330c5a0fc33756019ffb441227d0ff43aa41317c47baddddd46b066e0a35bd0b6a44ea49945ff36c6d723809fe6d6bed10eed99819ee9a45ba28b9232a574c087881834c8221e55fb7a632897f981a46379c7ef10d2f4c3c00f793ed593171737073654de9570e142f197090de7d74789ce9d6ba5dd5cf4bd22b5024f41307b8e3247c6fde9f90a6f495b7761d3b5e56380f4b43654a04ffb8807d4c447f83ed1a6a46a6ffcb9d69e6b46d", 0xe4)
sysctl$kern(&(0x7f0000000180)={0x1, 0x18}, 0x2, &(0x7f0000000300)="5c143264fee47b4ea2de774c561d35caeb3bb7bcfb8ac1f0aa2eee843e6fa89d578826b0a8f7e04af15130a71c041495fb1292d0c0aed5a69be1e90b37982ea8018eeed97fddb1f5defb694d0ed368e9abd72aa33b489cfed63dedc2168d47fdc9a4a7be1a73926ae5415f2815cd877bba3c88ed69b6eb486a4d8f61143d55fe02eb96f0d7f5618a06bec670fdd64a1ad2a92afb54f5c7cfde41a5795432c27d4ba9379dce837188decb0889698ff294563ba0905194b376ee1367507feea208a9", &(0x7f0000000400)=0xc1, &(0x7f0000000440), 0x0)
r2 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r2, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETZCNT(r2, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r3 = geteuid()
semop(r2, &(0x7f0000000a80)=[{0x3, 0x7, 0x1000}], 0x1)
r4 = getgid()
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000a00)={{0x12a1b8b, 0x0, 0x0, r3, r4, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0x2})
semctl$SETVAL(r2, 0x0, 0x8, &(0x7f00000009c0)=0x7)
sysctl$kern(&(0x7f0000000440)={0x1, 0x2f}, 0x2, &(0x7f0000000480)="c403bf5fbb6be1573784cf9130b6adaf55561028df4d30b4717c2e077fc3d83d4e8d256432030fa957e8fdaee4ee9d3e90cb122b36c00362197375bf509a6cc59f3195c8eb4c23151d09bf4db099650fade94a091e", &(0x7f0000000500)=0x55, &(0x7f0000000540)="ad84080b29a9d7948982763a2f9f54776f1a3296382ac6982ddab025283f3f052d1b7a963c053008c17310c8cf3b2222951677d1a8e17c621345782df9404f60b1d101b4db8144c2d789f548f5d9d9ff3c520f2cc3b61e05c91089ea0cf9eed28826320f45a6b40f", 0x68)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x92})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x10000435, 0xffffffe9, 0x1000005, 0x6acb9789, "0d43000000000d516ba0003639eaec00"})
writev(r0, &(0x7f0000001a40)=[{&(0x7f0000000a40)="9073b7551eb2b0ea62a28bd87a07099ddfc8bff500c541f961e70552f29712146b1fe63658bd2662280ec81b7583b8ae6b259fc252ce1f3b44ee1fcbdd7af7758fdf1137d1524c039e94669dd650bfdb0f81d922e81d54d76f074da1bbff654e9419c8a7a7f1257f6fc8e1bb278ba10162309e62d8ee26518a773ba0a4321d5268ec7197727ec61c5acde73e9c55a7a44a949a623881d55d345a443bee83e05eea475a3c506e662ae6868772a47a0b789c54dcaaa6409ccf20282cef54884c343fac3ecdb24cceb5fe4fc85776b90505418507ccb4cd223ed1e7ad0d69cc0f63d0b389266600bbc7769d47e6371acbb87a78e8960cf04be0d2a9b1e5d088e0e125080e49098b36b1056fcf9113293dc5a8985b4776786aa808d4a97ba109256a040c685918d1618206c8517d995caf154a2f94717c042453b68bd56ba62c9a7e5db9180287337a611a6b4b757bc67de2c91dee84b115b5a737d8eb00", 0x15c}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000240)=[{0x3}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = syz_open_pts()
syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
sysctl$ddb(&(0x7f00000001c0)={0x9, 0x6}, 0x2, 0x0, 0x0, &(0x7f0000000280), 0x0)
r0 = syz_open_pts()
syz_open_pts()
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r0}, 0xfffffffffffffff7, 0xd}], 0x0, 0x0, 0x0, 0x0)
syz_open_pts()
r1 = kqueue()
kevent(r1, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$unix(0x1, 0x1, 0x0)
recvmsg(r0, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, &(0x7f00000000c0)=""/156, 0x9c}, 0x1041)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x300000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x1, 0x2, 0xfffffffffffffffc, 0x100000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x1, 0x2, 0xffffffffffffff01, 0x200000005})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
r2 = socket(0x800000018, 0x1, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{r2}, 0x0, 0x0, 0x0, 0x100000000000000}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
msgctl$IPC_SET(0x0, 0x1, &(0x7f00000000c0)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x2})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r3 = socket(0x800000018, 0x1, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000040)={{0x0, 0x0, 0x0, 0xffffffffffffffff}})
setsockopt$sock_int(r3, 0xffff, 0x200, &(0x7f0000000000)=0x6, 0x4)
bind$unix(r3, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f00000003c0)=[{&(0x7f0000000200)='\x00', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r2, 0x0)
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
dup2(r1, r0)
write(r1, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000000)={&(0x7f00000000c0)=[{0x1}, {0x5}, {0x100, 0x1}, {0x1}], 0x4})
getrusage(0xffffffffffffffff, 0xfffffffffffffffe)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000001})
pipe2(&(0x7f0000000880)={0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
close(r1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$BIOCIMMEDIATE(0xffffffffffffffff, 0x80044270, &(0x7f0000000000)=0xc2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x3ff, 0x0, 0x0, 0x0, 0x100], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x1}, {0x45}, {0xf9e}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a54233f4d9d00000000", 0xe, 0x0)
sysctl$net_inet_udp(&(0x7f0000000100)={0x4, 0x2, 0x11, 0x6}, 0x4, 0x0, 0x0, &(0x7f0000001180), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x40}, {0x2c}, {0x6, 0x0, 0x0, 0xffff0001}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb27c0099e0e524044", 0xe, 0x0)
execve(0x0, &(0x7f0000000040)=[&(0x7f0000000100)='r6/'], 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000300)='./file0\x00', 0x0, 0xfcfc96ac1f78739e, r0)
r1 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f00000001c0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2}, {0x1}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000001200)={@random="1b329ff120e0", @random="c182c3cc9746"})
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x4, 0x1}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x1ff, 0x0, 0x0, 0x0, "0100"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r2 = socket(0x18, 0x1, 0x0)
r3 = socket(0x18, 0x1, 0x0)
setsockopt(r3, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
dup2(r2, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x3d}, {0x4c}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x1, 0x0)
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000100)=0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x3, &(0x7f00000000c0)=[{0x15}, {0x24}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r0, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000180)='./file0\x00', 0x0, 0x40000010, r1)
r2 = socket(0x11, 0x4003, 0x0)
sendto$unix(r2, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37281002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f00000001c0)={0x10, 0x0, 0x4, 0xff, [{&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffb000/0x1000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ff8000/0x4000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ff9000/0x4000)=nil}, {&(0x7f0000ff6000/0x3000)=nil, &(0x7f0000ffa000/0x1000)=nil}, {&(0x7f0000ff7000/0x1000)=nil, &(0x7f0000ff7000/0x1000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
recvmsg(r0, &(0x7f0000000000)={&(0x7f0000000140)=@in6, 0x0, &(0x7f0000000180), 0x1000000000000264, 0x0, 0x26a}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000400)=[{0x5c}, {0x34, 0x0, 0x0, 0xfffffffc}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
open$dir(&(0x7f00000002c0)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x1030, r0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff})
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000000)=0x43cb9, 0x37f)
r2 = dup(r1)
sendto$inet6(r2, &(0x7f0000000100)="a7720000000056c0d9316f7a8249ba56be1c05efa2bff0c1bc5f5ad12e7ec486823c4d25012403362fc8fb96bf7fdb36aaba00"/66, 0x43000, 0x0, 0x0, 0x32c6b15a89e3dfc8)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5037f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x8001, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt(r2, 0x6, 0x4, &(0x7f0000000080)="52b6f7e8", 0x4)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x4e}, 0x3, 0x0, 0x0, &(0x7f0000000280)="837bfc0a14df504a6b218982e24e3cf4dee6e52a8a014a6c16633bee1ac055430e01054a90d1d410588c8a66ccb53af62ebe8531ad235e2b2c98cc48d394e64b989c03741d5f30d94b49c6a08d1f7192bfad2ca4dc516de97544ac22388ac77dc312a83defdffc079d44c237b78e3f877a45863355108d49b76f4a9d501277747c3a092287b6319eb1a799925a5ba87c7ff9c3e4e2989fb25a39066137d82d1655c30bf8615ba2bc32fc5c26191caf9abe75195d5cc6955878b18dbf8279e5998aa5e9fbbfd91752bcd88a11d8454b25ba4907d5676b6fe9cb54453a6f61e46492892581d21518a1c0cb708d248b247261957bdd49a60abc9db96c1b93eebf9f9494afab78349c765405e17cdc0992d9111e0bd650da", 0xf)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
poll(&(0x7f0000000040)=[{r1}], 0x1, 0x0)
write(r0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='\x00', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
stat(&(0x7f0000000000)='.\x00', 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmsg(r0, &(0x7f0000000700)={0x0, 0x0, &(0x7f00000004c0)=[{0x0}, {0x0}, {&(0x7f00000001c0)='B', 0x1}], 0x3, 0x0}, 0x0)
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x1e}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x7, 0x408})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000780)=[{&(0x7f0000000d00)='nm', 0x2}], 0x1)
execve(0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040), 0x10007, 0x0, 0x80, 0x0)
shmget$private(0x0, 0x4000, 0xc1, &(0x7f00006df000/0x4000)=nil)
socket(0x18, 0x1, 0x0)
r0 = shmget$private(0x0, 0x4000, 0x8, &(0x7f000035b000/0x4000)=nil)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r2)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r5=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r5)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
setreuid(0xee00, 0x0)
r6 = getuid()
seteuid(r6)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
shmat(r0, &(0x7f0000ffc000/0x3000)=nil, 0x3000)
sysctl$kern(&(0x7f0000000080)={0x1, 0x2c}, 0x3, 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000009000/0x13000)=nil, 0x13000, 0x1)
select(0x40, &(0x7f0000000000), &(0x7f0000019280), &(0x7f0000000080), &(0x7f00000000c0))
r0 = kqueue()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000100)=[{{r1}, 0xfffffffffffffffe, 0x43, 0x1e0000005}], 0x0, 0x0)
kevent(r0, &(0x7f0000000100), 0x81, 0x0, 0x0, 0x0)
sendmsg$unix(r1, &(0x7f0000000340)={0x0, 0x0, 0x0}, 0x0)
recvfrom(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
write(0xffffffffffffffff, &(0x7f00000001c0)="0032568c952143b05817620b7ae7a9808adf9c9942c96e092d13b51ecc75ea5ad4ae0b1e9539fd2fdd55ab68a49b016ce6def2bcdf396195100bcc62e8e3250ba90d209f4d123b7f718fe88f90d8e10841a85512c25ff121d98ba6f16db95ab0bdb375d627800de0359f439b9a017d9af7229914c0e305b36cfd81c6fe307f9e7ee4510b238d490c7501756425219a3a560a9b5a80c6c34aecaddd1feb8df45570029164634b6e70fd922f194d05d1ffcd10de76650b54a49ed67e610abbf6072885c5960aed7ad71910edb61a27a9235d7cefb5775fcde1769574aaecc1f17e2cb3e753702d72901deaf8650ec0d543f23812d6caab9c76a67d3c20d578c109a6c10f7ab40538890383908281bf81e772ca4929760d2e851aa52029817881bc407549d7715d72e95786c108c72ab37a38945f77607cf2803186cd1170accaa88a42b9ecee4e35", 0x147)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300))
sysctl$machdep(&(0x7f0000000000)={0x7, 0x1}, 0x3, &(0x7f0000000040), 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000280)={0x2, &(0x7f0000000000)=[{}, {0x4}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff})
sendto$unix(r0, &(0x7f0000000500)="09afc1b9bbe705ee008a340996e168950e2b9c4ed4419e3defe0ef401c2f29bdb3f58af70f7a046a515490470b696e6d3c90af1c73253bed3c7406afdf018607b64b719f09c3102246a71adf5e69f2aa7c95da81f0545b418e39f4982b35a282813de2924f8d9a719109e3117ba117debc08708a4cf3f76487a572e441c62cab561ab37df27af1ca2e352f57a2057e9e01342e8d7f52bbb940da8feb9a183c0de3d933e3579f89e421f6c5bfc9f634db7ab4a2e9252df462c49a505c2cd08a8fd4fda316579b59be7860471ce4f277138572e3468304f84f20c61a7a9b08c7edb1601436e4666232ed75b06cc66df1fd566f7f6f09801714047c475bf52686cc47b3cbf5cb974aad9028f92f2e2390cab7ad50ecf76e7e875c3c120edbedc5150a8fe8aa907ce4985bb388a44d814dc3d3dee72b99b408ed5c96abe70c801be2b96f7ea4d2c8b94217c46c53456afc397e25541f7ae53489a1b1b14adbdecc8b5867eb8f4ff0ba6bba5e19dbc5fcfa063b12fce4ac5cc19f7cdbcafbf043aba6d548fc5a5cc3e25b9b7aebbc1e163fd28ae3675ee2496944971d29abe646cced441b1a32629c00a1b166952eb15b6554034f04cd27bc0522d8c3294f639aa0c43b39c0dee905dd9c926890de67662d647c3f77eb95dacf90c9dc44d6499b820d5b5a50f6541d3ad9222768faa118216d19c3e0214fdc5b42dfddeba35088b920ae954047829e7ef6d88a37496a298d2f82860a72acbe17c2744aaed80ac8034a3fd90cae2d92faa8da1024b62de0746651942c4ddb20257b03721aaf0436cd84aeb39d3cc2af97acd07c3e407904dc431fd7fadcc9848a0b69c66cc65fc36006e940de0f067fd33413d6ba472c4474dbb7f4b5bfd0ed2da94ba99e350befb9aefcc50a0c5eb1c712f40c23d6645fb0e8368465708098b11fd0a129cabda51df51efb837121f26cb0ecb1fc8815d8f049831e9c0a87bdde8fdba6ffc16353dee691e5e1545bc22e972bbcd5a5f17a785dcdacc30dd1e1696542054cd1a59311847f2fbc696686a5fdc5f834e7dcc73983418ae7d04810d77ab4b2120312dd2605474da738c1d8c07c39a49db49a856433d5517f66747600429029e76319f4b9622d269c610d057d9023bb6693cb82db88bc222090b225e21b6ff48bbfbef5cd25c766ca11b33e5213f0b3dee01cf53424f32d22a7bcdac39c2818d83366ffd123f7eaccc57313321b3462f69be9f08d17b43a2cd67d85062f7126e49534f1597f6aa09f463bb586885dd44eebb658b38d8de3f0a4c0669e24a9a0f42f567f416a57b52838393adbc9e43cf56f35badf9c1b721bfd56567c0e629dfcd867e16448b06eaeecfb412899edf482c5bd5e5a19f98dc98eb11c62aa73f6b7c5e89c204da6c9061fd0b43c05df2dc16be7e4669c67e78e9eb3d0ca3c1befa41a2da8b47d9f5cbd496c49b188e14ae0c35e783229544c1bcef4c7fb984063f3004f3b4aa06c9630686c511392ab20074eca475935a1a412313cdc96476d6892af2e4d8f37e30c1975adedff0442d7ef2cd8e063e7e9a78719ed0e8132f1dc572f69d516e2ba3475a5453bd09b265f61435df42e53aa015c79848bd6a765e33b0163ed3accf1c1e95968782de14095dc1275a857ab1497ed7b6661308afb7883522184dc61402a20b35358126fa1e8e06d3433c260415191f1bcc44544905003543295c0c8dcfa82227c298b90e51ce5281cdbbd1b3d652e7ed929eb74428b298db4760c56de0399283f792f56b796cc1b24998557d20b3987b882d30da708f87f1005d98597ea58b0912081b7ee1878b9f31ef1389c3b6744074bd88c2fe10b44173d5ec30f36a07691235e26541508e57bc798174c2fa3014ad6b65598f7f2ace2dccbd9655d0e78b8e1da5c3fa1506e7768d0d1c2cadd61858467b3b754db020affe6aa3490df48f2ad8bbbd68e91f2b4ce08ec34399ae91c457d60c50ec37b3717565b7a47636a7f021cfa62cc7606833e76f6ef12519c7f4a949cd6cc559b0742ce1f11dea8a1b9b874213eda58a04855a8d08f63d1dc8d16f43a88791c466c0557b94b0e170505eedf7aa686ebec44c0ad750308d000a78359fcb6cab21a19755b27bcdcac6a3360b248499177e0e568049a3acfa75a8edd9dd9f6abc5239b6604dc0cfa4473c1934cb7499c782d39efa2f5f8b3e7ae060d2fe355eaf0df9b5767a2a64a27628630e4c581b1a6c8a272565851d9c45293f3cdd11d0cdbf1819c153fe0534f74b687db95835963da90497832c0f3fcc148831df54ce4bc7d172ce6702d7f144aaa360d48fd97183a7a3c283af64f5fd40fed46cb1eca8fe02dfe6e758264a2063b2fd82881cc5c8dc2914df7008c799f4615498962f57ba553137659030887fe897e4fd674c5f92d7c6a2ab7c698f895af3aeb66fd9509c1755e386305040816175db21b9ae9bd7a3a9b5801bba8889c91dc96455ee2884fda7e8c9f36ec613978d5649a9293dc27c371216fbfa95812cb64eaadc0cde49964c2c944c45a82fbb3a14c3ec1d719f9565ec7462e9eec003707d7dc084df1abc3fab87fef7ad563d7929e14ff3a120cbb5873a408d4c9018b71a42751aa3e5b3fafc8355beb0103cbc24a8567e927008b06f3e74b1adb07f78994829e7bd873df6ace101c18a38de2ca3a0c0ec49eaf9c096482ab95c41b9583c33c2a08dc3077d900387197c5f0f76e149ae027af0aa7c615bba52566ecddf285702073793f698d6ab2540e0431802072892bb264f20e24e53824e06a0c83f031062b50c8032c62155e48a5e1eb433af5c1f48e87be8f7e4a6c9d9fee245c95be77295d64f49191f7d2481e12079eaed6f811d4e80c8e51757cb18788e1840c723a66e72ee46a9290324e", 0x801, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000000c0)=[{{}, 0xffffffffffffbffe, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x400000000018, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r1, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
sendto(r0, &(0x7f0000000240)='-!\x00\x00\x00\x00\x00\x00', 0x8, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0044418, &(0x7f0000000140))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x42}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30", 0x8e}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
renameat(0xffffffffffffffff, &(0x7f0000000000)='./file0\x00', 0xffffffffffffffff, 0x0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x100000000, 0xffffffffffffffff})
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f00000000c0), 0x10, 0x0)
close(r1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f00000001c0)="00fdf250d37c45941a3c212047eac30700b5433b483534a185cd98917be6019ba00efd4e3e363ba6ee0a2305c679060000001a0d6d3a0f63dd6d879130a347b4f3", 0x41}, {&(0x7f0000000100)="fe0232df69a0613826595070899e67b312e9829b05ef44e5d65d1d1c57fb70d9b48f708800f5620c195564c17a952089bb492024c8647520d6ceaca961a3525ace928f31363783402d9fae3b280409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad891289b43be72b714e7f98a895e30b5", 0x84}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fd181a"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x400000002, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000280)={0x0, 0x4})
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(&(0x7f00000001c0), 0x1, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000200)=0x6)
read(r0, &(0x7f0000000040)=""/32, 0x20)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069c3, &(0x7f0000000300))
r0 = socket(0x2, 0x3, 0x102)
recvfrom(r0, &(0x7f0000002200)=""/146, 0x92, 0x0, 0x0, 0x0)
shutdown(r0, 0x0)
pwritev(0xffffffffffffff9c, &(0x7f0000000040)=[{&(0x7f0000000300)="024e118b0b6b038d2666046fe16731ffff", 0x11}], 0x1, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc038694e, &(0x7f0000000300))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x80384601, &(0x7f00000000c0)={0x0, 0x0, 0x0})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000001300)=[{&(0x7f0000001480)="a245d771abf6c17cb17c6787ab746f243b220afe3770fff15bc7ee34d0f8d3d2cbc3f1c3edc07719cdbdfc6dbfa5aa1a384d600b86b74c9b45d9930065583a897c8d03a6457413c0c74ffbd83e3ce86b3d0e63a73f1e7d86f7d56ef6dd0a3d1eaca268ce4f9edc58bd608fe34c2b3514f07ec0f1c2ca119754d82ce1303c699d02fd064365488f678e96610229bd0e96d8315f5ef43f05f18a71085d509fdf908edcaeca315deef6b6899430b4c6c36600000000000003ffdbc47eabd559864c98d98a79686f9807ed42d48d25c4ef40867d26bb07acaa2540293e7508bc5f15bdf9aff732505b82c43fb4c860006a", 0xef}, {&(0x7f0000000100)="e0db7e7b3390a6c8ee2b230d4b62fe777d1d738cd1b20d0fe3bebbf25bec74ad7981773ccde66d2825677ecb8d90cad0739ef80efc17927e367cf89a26caeacd561962f535a9a3051ed513567d37d2547fb5aca061e3325a3dfe8181a598180e52497edcc952bf1bebe751a1b561895668b848c4ae34d05d4fc70475b6c40a32afded737d29bf2d328211bf4d17e0d32fa7325ffa97a3dc889e37517e6e3425d092208729a9d8cbff6896a1bed5d5bc8554afd0bba26c0d799a44184ec86bba4b903b428f3d41ebd7f4a5d3e01ef4b94a3", 0xd1}, {&(0x7f0000001d40)="1ad20be03a33ef1a350ad33f89a054b6fa4b7d7f7bdd749356479b83b7de47ef7a2265c494a7586c36b2242253d282ba173d5ca946d9aea34b4ffc6527335b53f0cdb6595dcad976cebba8fdf4d2dc3023f78c12bfb8e9aaa0fc33e817baf8644d688049306aae157a04f59eb5b463c0a45ddbb3b7d78942f0584272beb6bc9c3b2a2b3bb392697df30b789d4f7e07ecf314a064038efeaccc1d15fabf58b1879cf2a54bbcecb5cb8a2ffb4bb640918224a3180ad07831ebe98ea82e60e5b6abd18162eb98f6e633f4e90f3c198e43c300106e455a9cde2caa7c2393ecfe0442d5f032d28dbbc62d80740cba535bea7ae233113d1287fa0ddd42982acea9754076e0f1c28be77328de18f751f4607ae2a0591d7626f1e22803d770d9bc5f151fa4e6bb4947709d864f917688196b45bbcad0d37832a9f8fe3c8d7612a48e3054a9501a5f2291852e11f09b0a09e82f58ab44e067e84fdf56e544817b788769db6405cc013841268ad68895556f7c0bcb46e6116aebb2ae7b8d3f90d75902a67153bf80d7d18b9cd69ca0de6994ce8bf6b7a58e3248214f22caf392c53a7ac2d893baa353c4f0927e0f491d2414b164d5251902bde6e2233034c0e77827ab686953a6789cd4056a9d7df9bdc99ed7e44b69a2920704f57130b59d4c0c99c3c0d28bf029a81dda833a72e6ca3653f5efbe9822fe48feeb3ac0179f7b1281124904203077133b45c5606882baef96d357cf4550f69f151f795608d810834ce699c3d2cd1b1e89ca607533c065225fc16cd379e8891833789a2b9842c1fc92dc63399ae7f1e0f18aec1514fd8f8d19fa4a481f6fe7a84c94b405405dac037fcb6426146791a6beb5236f614e6b4771afb274c352fdd03fa15e9319ceadea1c21c1d4beb6814b1add78c67e87af0d029d6a03052635dcee71ebea43ae1e49db11b75bc82579da50a2cb6971bf2afdd811c9857ac5e1a9bf9401da10bdb0c4551db4e9a68a1b29431657b432afe884deb2182dd51c92c6b5cafd73c24a848d4cee6fdf556e5848dfe62c067e9766fe42a6260e2e0eee59afe61b13b27678d1a7551d08313de8fb77d9a6e33e1392818e5fbc0ea3beb769584d6787fcc487062a5d01162320892b34aa64c28b667ff8d6d559c81bfccb638a44dc36a59940b2fd9f57ad078cfb32eb20f8bda39cebfada5f8bca19fbc59bb81993ab4b3a84f9593e91fd4a79d31ea06c69f904402764da4df7c157daef1361183037c01e0be97cfcd435d759e2a82854e6520a3125d4a9d26fccf0081b1991bae1d1f6715b4e275183c7ff01e5037f06d5700e13919a88fe4b1f9da0acf3c7bb498b9b0b3fb6e6ef1a0ad79f055faa2963668c63aa8289d0fbe38630228e703312152e30ee4e681270a973dce95a43c15ff67913de815e9543fa216d0ba47b5cd5ac5efe90e2f99a9c11aeee87621e7573f2ce920a85bb507dc08687dc2291b40b8e09f27581edba451c586a248c78d46c0015ede38b76f26f2757568b39381d92371c82230f04a94cdb463b5b63f5ed", 0x446}, {&(0x7f0000003000)="69fb0d0a5adef719c11feaef588d2ccbb6c9aac769663fbdd3498cbfc9bee28461793279d4d531c1b7a31e02d8779c82104b450d18c6e4e5e2407d810bd51634d04c661ba03d73b4307456935a48ff465bb7c5542e5bb053ca14929ded8485f3706dcb7e11808cc7ccce5b861159d4d3be5e9992770da5c41675cca937357692801ff439f9ff1e45b4ba77d0769797733b7b4da70f0b29912e7e445e5bfdc33ea1628b23a0378667f4aef49a2ecef061e5e5e18620f171244b9cfa313ecf5e8e1a2a234d3d32ef2de2f31967d69fb5e0f183f6bbc30ad99f00248323bf815a9841cb8e7c36947f6d091acab33ab7435c54ff6feaaf4e8e9ab6224e86c91258b6d35cff47c3daec54758680c0ac4cb8e337be7bc3b6fb3fdb4b1c5ce971d932c5e4c51cf445452f927cef3573106a73440eb8e1eac29884659215f2e6064d2d618d43fa24e44f987fd8b5699f9d00a695410347da40a0e7eef2f8fe3a08712ee289b9c1e85ca1f54de08a0a1c070871e24f39c2157b1d3ab387a467ffc896465b8f62179c47a00179e3d3f41606763765dd15c2401ac01af15095f4aac091cda1467a04eefcc605740eaefa21d3ee540bda2519696a9092a9cdb8439cf29a05a3850838b309db09af80f5e3d4dfe21aad8b7f5157a89023bce43b50e3414d6e7088ef77c997f32dd02c13e33c0c6a2156cc2ae249759ae1e8ed534a346250324851f3b5244d6ff3eb766ada26100b02b0f9461bb6036016167fb039132a2a40c35cd50df9caa3ee547bb01febe71da550dadf8474a5cce22137c8ab2129dcf4a340596e227c45bc287e9ce56a17f236bc09bba4dfda902119e2c44e60da4a6510914e0da2b838b9a80ddce856c7353ce5ac33802a4e9c5ec9976c49d3daea8cd35b81b296ebacdff257a6a63f77641857a6d46f2f622c85e3ba615500ec1daa07b24430637e8d744a66a7294ef3bfd729580ccf3349ac1ad089a14ce7a658e1c478a5a32beaf7575f77293e55f5c76ec3011f9bafc1aea01f244d9f1b0956749bcc7fb94b57c7d5d8765b0cc77bd8c0a9ebd69687d4c1855c4f70723e39d0a8c2b9574d8380fb02d596e2ed790353b1172d3379d615bf933e88719b18f56e9a1dda1c9358e9f38df80a991810b3df0a31465290d104a343ee78ffd3de6c41b6e71b695bbb092539ec3b017ce6d86d3849a3b0441d700be0734acc23ae7d6fa80a19090368bac7e596a06285fbac922e9dce0d11c49df81305c469c55a2ffafa21aac927c5707e2edbb8cce05a3d4e2f9b8b3d6c742f6f127107498dac8ca5c5a59aa1f8547ffbac5e992401f30092b2eb0afa1e7103657b664364e131bf22a260e219f42cd43ffafa5b93bd53ef173a31fd3bba1f4825aadf03df2907eb3b23fd2519bbe24fc22883f31864a59140bb52b08fc0d30b71b9266d6a06cc5849790724f0c4934eda36940a4992452c8edb82e8d3c1b81413a5d08eda7ee01409f6372065e683b11f6e13346b467bfa101b259d2c733ce90daf71335a7d5a5d214f9320be1b28c431746f025b3be0498d988212feb2d056ea0e273f8636de360ab58a3c58d4918d63f656de25f333ba7f20a15034c007c8d0dd6547da75af25ec568a72dc07dc688c4c8fb6e454ad78d54ad5002f3ecd91539f65a464ed0a3732f16ff4f95fe8baec97900eedb4a49ec361cf613e81d3e4a0158e22e784db3d66a2443a189cd7a9bd0d89432cbaa967fc084f94624443c833089167a467973e7ceb96b2b0d2a5cc27170286a91ea06c0492c9d295491e0fd5a5e3e5491950c23c486e97e0f33e617fbc1fcd96b9fa8c3072d213f4230e3f68aabf355630389e0e5254288bfeabaf6b721cf9cd2f74a782cdf756bb15a832513e9261d3874fd16fabe8a45677cb04bee526cd3f22f8321eca3b69b746efea4a8ec3e0749975b1f15051da5f8133cc58c0a5092c003931e822e2bdb62fea5020a27ec8793ee8a2549dfbbee48ca9fa55c8e7fe5fea288f301e9cdd03ad0b7fab7bc48f8ac3c1ef2fbd663e5bea47f2cb3fdb67204bc677707c941f3817d0f49a60e25d319c311447cd7bd965a63b9aca37b5f0a7e3316e6b0471514cdc3acfbdc2d2f20986eaa4621db318bb27b5c1f70ec15d48ffa5e56d1777d41e240114de991f4b1356ab4532aa2d6bcbe9c81abb343f07e63c1ae275275368b551f7a3dde60084afc8791d2f9154ec0820f74d3a620552f3aa2050d93f5090e2aa46d05a4b75fc22d41bb0ca80f211129e656a5bd531abc57919908cc25b786529823dbee55291d40b7a42ac26ef2b2e4a94ca20de980d72bac6048d7438249b17954672284e7f880d1443281f4348a43ae009bf34d7ea0ef538f9bd793a28322175b6a9c7fc427bb94849dfc33f5bf82c723df31a7d1fcb6a0c4e87b2cf64f12c403200f37e629dfe82d12dfab8edf4caa434eaf03e07506556b135e91eaf996c6f0c782f7e906657081ec4f6764d582844c61b4d79b647b6ffc0ef371ee25788b908615cb3264b5336d7d7dd0c713307dfeeac6fa70d00b8f56ea7020c6ced9fce692f4956278cc17a8085b9626b058cfbb4b166292c3f583075257e25b7304ce92a56cccaaab98ae99c3b6ff2006288766ee85cd11e1ac2401f439453885a31ad3b103eba7ac4153b55d9fb593c6ba38a02f9deb3e94ace98abb47f3ae34f3d8afc74df14ef3eaa58d556e19cc32347c701b98f221baffcb497101f76ff5e4c6c32475195ab725ec6a8a5a61e909ffd26349bd0953b6474ec67141a2df7a2e816bccbb7faf1b9411cfd9fd98c2fe525f999f2a6fdab62d7306af410b76f1cde1bc5d669b9b8453907aa4e31a7023b4367c5d5602dde7b8fe6383d61d37afb64a8ef41dfdc037908a62f7d55767acc9e7b85473dc3d37111cba6536cfd458804fb794e8eccd9a932cc3c3c22db7113283a98a0e53afe0abe2fe684f70aae483f5cb877835833b32afc9293d897e0dfe60ee039182a13aa5e7057b4b2431025649d550807a8e474c50772ca70001e71234b696f4f46a0b7def8b07d2d630f8e624767e8f96262b2c3ebc73c096b4303468bc55f7a32079a5efa3114a6727f6e6a18214c2e8b44f0caad7f170331889ec3b7c70779fc5b248bef536a001e83ba223ae419ee616947dfb6998d03465c4136b2369b64c46dab8045a972d8fe28477b6c6bc1412585b8771a588a37585ca9fde7dafaae54c7ad7e33c5248859d924b1f9108dd1a3a997c4d9c5fc7cbccca66da29b0abfffa76e85572de14db968a60cd07c79c6a061d2f3e3f8a10c59f10f2d38e66a8c951d3e00e402265ad175de8235b2c71f76a15612dcf3ddfb4563d68c067705f139a0eab3eaebcab1baee6999ef703dec3e7566e0364834c0247718045249c4b015a99c6a34ae75d6ddedbd408c7bb3c676d262325f7d69df98689c8917f8cd1226a1bbe239104562c09c8fed71e1912a25ec9283fa36a007a7d507e5d8007e1e4da50631eaf0146b8b8bba4bfec805c2f4c1705fe1c84ec43d8a27442434c2ccd8ac37df8a6a18c8e79599cc332716af53021704559b8b0e9c21700e7c25fae35dd4826d0e2a780037b6a8162f2e2c9059c6267326d86b4219f8d74ae595e1220d081d842beee029735eb4bc2a6d5305b0b9e98a82f6066e7c9f8ea0a5c32276720338025ed708597c76cf2bf20f4f2344da5746ab3233cd5d0db845480a13f5fdf1303c4b1955b07540c77e7c4857e2f4b2a890e26841412b878de51f36ad01bfaf8731878ab43105ac7b147da0d7918abedb69a670d05047f016f8d92d60eec481919218e475748ee0a8f8428f7c2645d9cbb875cde9dd43a0829ecdaafabb3dcf4b03daaee9e417cc39a43ae1082602c26b0ea4b4601489fdae05b56e184c63ab9f68105ef5b2d3cbed6eac0c2e252f8a6f23a7c774830bebdb249193a249b6ee6111356a42fd8d0a8b8830c95486284d9f2fed262da989d57a60dd37a828eb7d5115c691d98ac5d31b122eff84428b54e7d31db9f12686b59567f3c5d118725c392e34f4ce0497c31bfa0c670448fb2a7a525be80f61b52573b705ecbda3ebf1588a0bcfbb8da781beb256d5dbb69e95b50a024796127c1cae691a8707568a623700bec3b6fba9d2935d6198fd4d6292a36a2929960e2314d9b4fae82d6cafc9bfbf6ccccba2c526734baaa04cd78f1a936d86f5dfbad7656daf6f4780b5c7308db6a43f7ea4ccac39710e0b963830a3b0824b0f6f0d5c30d08cedff921dcf9f2ca0bd198a3d02473d3b8068348f1fc5deffe6793e8e1b6f5404677a1f54c7ea6da934ec2935209ad84d0622bdc73648715b47d671e9d295ef6488aee7a12b2f7e8a1f0191b83443217928e3f99cea5bdd33de15561614ec183f46f1cc5ceec334723e3681d9bb1f82c98f0a74251b8a608ecabf036a2507d29820074b611d7e98897cd3e947185e3ba2e6317d166911657f637711d5857fcca44837f7e9bebc1ae1f8e271097131d9d8d76e8db81073edbaf7c4313270ae3d3038a58ab2509a95014232821284f240ccc3490b7147060f6af29f9a28af7d6bbb56be964ac8c08eb0093fa655fa9fe4eaf7c62a746870c06f1fe580aa2b599258a8bc0c56e8b8d4380c004074bad8444e2dc5901cdcab09d3108e69d22e193f1669e156f810b057f6e35beecc6fc3679794e4a1642e92a2e2761dca6cd5a238a6f700defa97814b4c0a31bb89b60c3a2a4591d5ea64973d223e80161bb6e349d6e7402f5c4889c4ed91d4c72ae2015bc44c7416fbe75ee6db39b3b98dc49743135c31532a137c9b96f90dbd9175b18f0261748d73aebdad85b0a97c721c93be19e08e5a6894bb262c113c01d7a8a191fba4cc79833da690379bea09f7bf0699cd9da986001449a121afc6c9c9f6b6327fa025cc0a9acbedf0e952943738f8aa45b980edf763a0911b3aae2c41aa6d30c190327535df7eca5a271ded2371a909fe71fe1db4d7a0707e32d5e02a7dbeb7f4f0a60a6fe98b23fe61a30f6489938c3e365a3140e59b6b759fdc302ff8a165eab7b6b9e513a31eddecc316c532dd72d7df149a6895bb494621f5c19619b7181d5a3ce410b19ec1ae6a49359d5e2371514ab0dccb59a088c9ac7885cb5f569395cbe16b101eb3db1990a9163515fa", 0xe41}, {&(0x7f0000000300)="640a3ca42e87ecf3cdf36e5aaeed62ad76756fdd1fe3c5cc7caa61f6c1dfab4576736271a10ed77508c24c69f8aad62e10d35d2fecf184c08a0a6c2f536828cede94ee3e95ef6fda1ed237754ed7197b5dbdc80377caed88283289b32dfcfaaa50abbaa4de77d8b9966e473fe8c9729cd9b5becd063c2c1eac1e345b359ba728fa07d6677cce69029b7bddd3fa28dcc7d3505d0146e8067dc40b3b4fe10dcdf57eba2cced2b83171c0c51fca13027ce84d70b8aee85c7ed5dea555888ac4008337bc76a1bb9535221885112e665a711f6cbbc2ea2d4080e529302ec96d0b0da3dd0f7d0533e63a1bd12497424e1813d7a8864827b261d1f081775acc8bd7a71b9dc92d31cf9872c157d2654df55aec317b8bc64450d8ad2c1c3482826aa927dea55dfac75825c50e1127ee579c4bdb70e12f8cf51cec485b6f6201dd674224a94cb040c628f131ac24655863f1ab4317e23fac1e76560e7ac49ace0baa2630fc248612af5581fc135dd104f97f1d0415c3e2669c76d4e83407bdc0cb06e7cb57a058bb933d144fbeb8341d7f7ad23c541bc1c320f218d9a9b8816bf7ded1c8e2267e7ac068d24bb0004ddb3733673df083efee3fe401e81fc028d8907486df6542913217a4616f5d563c3b9882ab320c28b3184b4d1a93c505121fae401549f9818a3036dcfbd38b6ae581ab89f493eac56ec389943dd8b09dbfdedc5ea6833764215ab427c74eb57bf86ac9f4e22d3254d5e7c068eb1b95d15c7cb1293c954894717901d3ab90d5fd9502aeff7faa87917933f20bbd6d55759faf43049c9efcaf5737485531a9410a66945238e907635d15104031fc052f844d96c40f9ca9a8211fe3daa6624adb6e32f47cf80dd3a671936432931b4e78bebfc8c5d82a637b89265307aa4d49f0f65427b875dc16db2bfd87a884c2d5dac9246471e8a3a11ae80a00591061f2f87beff6bacef84d259fef81f18b565b74e2890008332e118d9c3d94ccb6bdba7e8d0868c159e623b81a015e3853d0801c7b8b9870342ad42c13fe1a941ab71c2f41e2e61ad71cdc1c4ade8c761ee2789796a63462b857e911a4f23c9641c5af9976bd8af1dfdb8ed0a81ccf4311a59c2d5bd081f48ef56664eecb77a2559d0e331be28be30b9b5b0203565ba6415491b9062b5f6fb90d2287970e7f66d846d93f790ce1fd5c18744782f87ee9aa26222bb965332029df04a42c8b46e0d9a3be5d02ea9b8b1efccd2534e86433b3538e6710f0b45f2620eb78046c5bbc5758295f9fac166411b49b44aa161b70f4f2a9b852f8318efb9986628c6cc4b099b66efa3f2188561c2bba8459942976b01edcb69349fba0e0fff471acb29ed673876ab946e7827f263843d56f02d90aa09ff1c23c4dd36ea9253ec411d6959c4da38f3850990b1343707ef0eed61f0dadf360d4b0acdac4ec9ffe039bc59ff549699378e471dd68caf6476c019231219729e570c24e4dfe78bf40d161da3473a48103f36a976903a0c0018e170717c5bce96214463a91cbf85494dc8d338c37a214dffb9e88d7e9634cf8e8f55a9713a4b0f676ff58e840a3df50d868ddada5b1f9dc9d24232f28035612597a134baeccf5398fba7e9bc4c7b4446296ade938df43298cfc129eb4dfd0a77df2375e667ac5ad9efe4ef01b01b0b14b9be25f71a29e34b41fe74a6a7802fce4789dadf5ac9fe5f247d5e597f2c6d4dca43d6fcfd8fafa4ae987c5f4adb4a5660410e431f2c1ff28780ead5baa89b4cdfbc3098358e98fe7176406494bbf0069c1987f1a5e227d585db50af3b6e58b67fbee2f306e9fecdcc3a666a9bc39c59ef31cb72b4ba5dd7502e47a1196dc84e3aa955ee8de52a873782a0fa6e49c785bd2c0138844259d7e7e02089e2e75ed148d319c317cd818a3f72c5569e8d571dfbb19aaab1a1bab75a2d3eb90404c5e9ddb32f0d8d9534a6c33894ac81c32aff8f53720be4b4d26386da518bd1d69bb0124f2b6a14cdb2954724562a76023b748b9f2542a8c70fc30b85b8158c202583e8b719e36ac530527abe04c4c3375f7334319cc0997c4caed1a5208744a7ca7296fbe7d86996bfde7dde3610494c0eb08c40970222367610a3b9ee7ec14992da9d25fcd3c8a1b219fd58e72be0f45ce5fe31734fe7602a2e2e049f301e3d1228dd5864819d0cc1cbd79e50b271f706ae5fc2465df4e4b557f25be4747a8b1aa715192f32ab2b55f235d8464a049dab1f3e583a0ff87d015e5c04c99b9cc23608932685c602bd35dee1889154c0ead66dbeaac3e4925ddadc950d1f04108e7d90da45b0ff414ad9390e77f3bb58688b95f2f0d548802bec30dbb8087c3875c0311d329abc5404e4090cfd7a9b97e1ef8d2083bb7728aafef6f8f04105bfcf5ff447b462af63f6c6c9bb88f44e5a5baea4e99238442e7a3ca047f9421ef2b844e5f10f0bf7d037462ee10497b0bcb5dd6223cbb6d497bd2fab76678a829b9d92282b6ae4150664df45e1cd952c7bdbb1720e131c6480138ba1feec5aa746ec09169414388d69caab7c1ca2ab36b04e0b59a2a837bf5b9b5bf64935f7dfeeef34c0e226bf43ed949953f07ebb2f5fc4e17af8236c4e6ecb2ded7320ae00e2afea7adb7ef07f8e917b82c49c795e478cc8c2221b182bb83a92cbba4237dcb319a1461ca1adcade811b9706fb77aad7ace85cc2a5b865b99f22396746190d2910a6b63f58ef533daaa313b37cb276c5fffb8d6d1e7b1f0eaddcdbf93d775d72e6b338504a46d334ba353e69be46df8306ed4bb5e25f14411eae20e6fa31f85310357c5818afc5e963210fd8eca5c3d22db7c439731af0bcb29acd09428079875865efb14f25cba24a4276dfcc820084c7021dc740bd8cec0627dacb99e3215c915faa2918067739b075f65003a12deea65f4d4830e4d209b6697d114d29ffee8ab092559a7091560b69d9c47f46412bed9fb1018938e18a53b53d0ddefd7c68f2af7ea0f50724e010d52b366a5c9ef54f85d8bc5ebed7dfa8bfa98753dc978b69a5962a869ca5288a066a6a1b04b9c3b9b7f10862ff92bf83e91de2fb7fe235e2540d75f1d863811f916801ad802daac43fe51e6958e7d03ef90f288d184f38fbf88ea3e2e48ddc448cce649a823848494471e3243ed9211f86ca3b88c2676df0a7b79166d1906f1a57ac98dab58a60deb6387d2d612fe1c593fcb7e168de39a25ec506c42f6ef5a0cba7ca73de285b76d596737cb65e7691414b7710d231dfd4cb49d229e01173a2cfe353cfe83d7cfb36494c616c24f17650bdff9212a3d6238c505c5438c5095fe55a71a90de6a5bbfce46e0c7f185e75b6e0315f01d1debe15cb2be59e3f4659a9970aa2fb69482a64ad91bd6987487bb2d4179101938b0e5619fcf4ec18a27b14dc5c329a9ce596c3484e5245e83084805fa051fae398f88aaa17c7b6dd3748e7d98cae2adfa7176d5c30d230372ebefd0498bfae72cd854f0b541ee8d7795bb1cd9ba1db5fcc01ce2a99cc0e9cb86f0037009c673551e98f612380ff1d6a4ca69dc5a3339a14465d7a5d49cc8b3f8153bef2a1d7deab1794e5816c809eda94e957796a682ece252fcbe371e33e0cd28cb4d531c1410f1ebf35d0ca4ffb18abcfbaf9cde05548bd20712c213e313021c3a6dd85b84b5db2779e61fc2745981276399a9555e4c19df7b5c36744f577942cee9d0212954f0babae2393c81152a0fc86acc36f8698e0dbdd0bd9727a357e246530fdc457faa7d49db59834a347ce7212d7b8bef039ea5b9e5ad1c6f5df1be8362976227917962918e2bef39bf3aa5e54dee6a316d128b99c3340a93130fc85f5a87a1a93c246960e513807205803da336ee55ea3b2435e0b6a02f6b88039053911783966c961a7f8a136613726ced96a5205562aff19da97d42323aea1435a99993595698c6853c36c35bd5d9ac0f9656c82cf8292dafcb35eda5450ddd3a401d481a9a12ac6b3d3bed7ffbf729990f14f577a099a5380925a1fa3710a9f660b33bc254e55e064b1d80dc1ad4b97de5c718310292a13eaf4cbc32c191b3c1f2e2b03639f9720630e89f2d64a96334be7527b2a7e0c14921f1ae9e23ea2a3ec73978ef53f6b542aae06fbfaf62cf5518835293b414330f1210f11cd2619987c61a4ea6c9513c63aa7f283a6c045b46669df276cc835148a971551d75211aa8a9fb3d0c871d4e6f3d581c18fef9aea839098a3e362ce36f427bbbacf5f9c1ad7e1d4708", 0xbba}], 0x5}, 0x80)
sysctl$hw(&(0x7f0000000000)={0x2, 0x7}, 0x2, 0x0, 0x0, 0xffffffffffffffff, 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "f81d08200b0f05000300000000000000d2cad800"})
r0 = socket(0x18, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r0, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000780)=[{0x10}], 0x10}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x81}, {0x15}, {0x6, 0x0, 0x0, 0x100800}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x3}, {0x50}, {0xc76}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @random="d2ad2c719745", [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @remote, "3f034d5dcc6595da6c7d976af8cd6e61"}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0185603, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000040)=[{0x1}, {0x40}, {0x16}]})
pwrite(r0, &(0x7f0000000100)="5df09d5792f307847f24cd378250", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0xe}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000010206, 0x0)
writev(r0, &(0x7f00000003c0)=[{&(0x7f0000000000)="bc", 0x1}], 0x1)
r1 = socket(0x18, 0x1, 0x0)
dup2(r1, r0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x2d}, {0x7}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000040)="e81af191c7a750ff", 0x8, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x200000005})
fchown(0xffffffffffffff9c, 0xffffffffffffffff, 0xffffffffffffffff)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000640)=[{&(0x7f0000000180)=""/191, 0xbf}], 0x1)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000140)='./file1/file0\x00', &(0x7f0000000240)='c\x00')
unveil(&(0x7f0000000040)='.\x00', &(0x7f00000000c0)='c\x00')
fchdir(r0)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f0000000180)='x\x00')
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
shutdown(r0, 0x2)
shutdown(r0, 0x2)
setreuid(0x0, 0xee01)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x37, &(0x7f00000000c0), 0x4)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000340)=ANY=[@ANYBLOB="6f18"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x9441d8ae19a18991, 0x0}, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecf860080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240)=0x2f)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x1, 0x0)
write(r0, 0x0, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
chroot(&(0x7f00000001c0)='./file1\x00')
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000140)='./file1/file0\x00', &(0x7f0000000240)='c\x00')
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8002, 0x0)
r2 = getpgrp()
fcntl$setown(r1, 0x6, r2)
fcntl$setown(r1, 0x6, r2)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
r5 = semget$private(0x0, 0x5, 0x288)
semop(r5, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r5, 0xffffffffffffffff, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000180)=0xc)
semctl$IPC_SET(r5, 0x0, 0x1, &(0x7f00000001c0)={{0x7, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, r6, 0x63, 0x3}, 0x100, 0x321f, 0xd090})
sendmsg$unix(r0, &(0x7f0000001380)={&(0x7f0000000040)=@file={0x0, './file1/file0\x00'}, 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ffe4484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e721e40934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49bef3238d0b05c82c7ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae836cadd4c3ba30d79ee4c956121c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e6ac33a607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb033f52ff3063d96df138a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b895417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c07", 0x1000}], 0x1, &(0x7f0000001440)=[@cred={0x20}, @rights={0x18, 0xffff, 0x1, [r0, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, r2, r4, r6}], 0x58, 0xd}, 0x4)
unveil(&(0x7f00000012c0)='.\x00', &(0x7f00000000c0)='r\x00')
fchdir(r0)
syz_emit_ethernet(0x36, &(0x7f0000000380)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "5ba74c", 0x0, 0x0, 0x0, @mcast2, @ipv4}}}})
syz_emit_ethernet(0x36, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fd181ae9696810e3ff"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendmsg$unix(r1, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x81}, {0x3c}, {0x6, 0x0, 0x0, 0xffff}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x45}, 0x4, &(0x7f0000000140)="67d11525", &(0x7f0000000080)=0x4, &(0x7f0000000180)="0d93226d2c620847d4ef96a3010001005e36ca6efa3ca1cba440efe993fd3329f613ad87fa3659f8eeb15acb867340e90e7c9183f279286d791cd8a16f2305bb43589109f39067ece9cb256c719cf7eb907ee53b4c9f198648837a292140175467b088151e212d0ab5c08051caa2d75bc7f7136ea4646b9cab2a243c70ae6b00b0298eba9ed5a31e660671e9237b963612be96d14bcee6227bad5b1f1fb4f892bcd8a7e2ea14f8892f0081987848f95cb0b7c67c17852e7ede6f3c24c3f3523dcc3d875248f6952a15f87f36915a14c8159bc24a90601f9ab15b8c6a3c28ad23c45343d61f074496550b0d0a672d03e3933d66b443a1b6ed", 0x81)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0x0, 0x0)
shmget$private(0x0, 0x4000, 0x318, &(0x7f0000ffb000/0x4000)=nil)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x2, 0x11, r0, 0x80000001)
munmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
setsockopt$sock_timeval(r0, 0xffff, 0x1006, &(0x7f0000000000)={0x0, 0x7fff}, 0x10)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x1412, 0x0)
r0 = open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x1, 0x10, r0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x32}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069ad, &(0x7f0000000300))
semop(0x0, &(0x7f0000000000)=[{}, {}, {0x4}], 0x3)
semop(0xffffffffffffffff, &(0x7f0000000000)=[{}, {0x0, 0x1}], 0x2)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x2)
recvfrom$inet(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000100)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @broadcast=0xffffff}, @tcp={{0x3, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000003})
sysctl$kern(&(0x7f0000000040)={0x1, 0x2c}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
ioctl$TIOCDRAIN(r0, 0x2000745e)
execve(0x0, 0x0, 0x0)
recvmmsg(0xffffffffffffffff, 0x0, 0x0, 0x0, 0xfffffffffffffffe)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x5300)
ktrace(&(0x7f0000000040)='./bus\x00', 0x0, 0x0, 0xffffffffffffffff)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
poll(&(0x7f00000000c0)=[{r1, 0x1}, {r1, 0x80}], 0x2, 0x0)
mkdir(&(0x7f0000000240)='./file0\x00', 0x0)
r0 = getuid()
setreuid(0xee00, r0)
setreuid(0xee00, r0)
r1 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r1, 0x0)
r2 = getuid()
setuid(r2)
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='r\x00')
r3 = semget$private(0x0, 0x4, 0x200)
getuid()
getgroups(0x1, &(0x7f0000000000)=[<r4=>0xffffffffffffffff])
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r5=>0x0}, &(0x7f0000000080)=0xc)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f00000001c0)={{0x6, 0x0, r4, r1, r5, 0x102, 0x955}, 0x100, 0xc2, 0xfffffffffffffffe})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2c}, {0x4d}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
pwrite(r0, &(0x7f0000000140)="676ee3450356e770f593c59adfc7", 0xe, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
openat$speaker(0xffffffffffffff9c, 0x0, 0x800, 0x0)
dup(0xffffffffffffffff)
pwritev(r0, &(0x7f00000001c0)=[{&(0x7f0000000240)="c542431910702b6eb640445d8941ec62ac83eef8346c6235a861856cbaddc3e349bc214365ebadf2c91a2fec0045dfbd5236a95ffdfd90fcd0c7717e67f32aa1391a982a4522975882b9a0819662dd5d2a2495a894b5ec664e702712f8d635ddb841b7de8228175dce547c330500a93e17ec2f8c5714ee792559741f7cbe4a13eb6e47c07ac78edad81781627b4143c73a8263eb35befdf0429f289eb971e3cf6bd87033bf2a68feca427f95a7aeba5ca95471d511833afde916d8e1a4dfb304c6ca4d2799fd0265183f252a2972491f0ec934ce774f4e76", 0xd8}], 0x1, 0x0)
ioctl$SPKRTUNE(0xffffffffffffffff, 0x20005302, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@random="1b329ff120e0", @random="c182c3cc9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x8d5, 0x0, "000100000000000000002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
sysctl$vm(&(0x7f0000000000)={0x2, 0x8}, 0x2, 0x0, 0x0, &(0x7f0000001140), 0x0)
r0 = socket(0x2, 0x400000001002, 0x0)
r1 = socket$inet6(0x18, 0x1, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
sendto$unix(r2, &(0x7f0000000340)="4d31bf8a93dbb226c9f2ddd97e6daa1028cb29d37cea390476d28539efc5dcdbec6ecd6f505b4b1f6b5fca9b3135ab676636356a78f3c72bbf151964e34fa2b20e681d078d6d44e542ce149151081b0d3e32b910203e4b33fd60a26556217ec529d7e8f6fd1cd48a68d536dc783c01f5f8696b83be55561061db330ee47750c1240272bbaa350e8d81d17c3f03ff09b64b37c042efe9fe16312842c3f2f65a1a76913d16599add25a1130b2072805b4bd4201ed712f7ac78f38cfdbe2b358dd9d279f9cf5740fc01d7f8b2e127563c430a6a285925de5992beff47435ba0f2bfb115a2ce90dd8e2ee4c7ee887ae522e43f4029357746866e37c4a8ecfce927a190dff6288e99fcc3537aaf484893312f6cbabcf23c0862b850af7bc6c70b7438ae53e377e90e7fe86802530b98130590c604b3f0c860a1ee4f14fd1079d1a0b043cea5be2076cb33947c9000a2d3a4b8d73d91056446324e76406e9c08c82249a3d018c3c4411ab4de110a2f00b6de", 0x5b, 0x1, &(0x7f0000000040)=@abs={0x0, 0x0, 0x3}, 0x30)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x81], [], [], [0x0, 0x1], [{}, {}, {}, {0x4000}, {}, {0x0, 0x0, 0x0, 0x4}, {}, {0x0, 0x0, 0xb7}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000002880)=[{&(0x7f0000000180)=""/4096, 0x1000}, {&(0x7f0000000040)=""/11, 0xb}, {&(0x7f0000000100)=""/41, 0x29}, {&(0x7f0000001180)=""/149, 0x95}, {&(0x7f0000002240)=""/203, 0xcb}, {&(0x7f00000023c0)=""/206, 0xce}, {&(0x7f00000024c0)=""/193, 0xc1}, {&(0x7f00000025c0)=""/213, 0xd5}, {&(0x7f00000026c0)=""/235, 0xeb}, {&(0x7f00000027c0)=""/158, 0x9e}], 0xa, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000003})
sysctl$kern(&(0x7f0000000040)={0x1, 0x45}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000040)={0x0, 0x3, 0x1, 0x8, "346e8440c0a6d7f28b1550c987e78e94756854a9"})
writev(r1, &(0x7f0000000380)=[{&(0x7f0000000080)="c1ce0b03bf3b1d1b67be56e2a2a068eb9a72998036d57567361ad904a4c647223c356d670b6c8f129a4744794aa5c3dd3695bfe32cee6b9c83164bde865a00b3adc58a9f9dac99b79e571d22d56a78d922cb6945872cd930db5477c7b9293c9c88f950de0e958086e4ec802939b35d9617c7464fed99269f2f73d5835bb7ea7fcfa8283b529f3653d135dcdd3c9c692d97ce582454cb8e63780d305f1c945754158ee4f7d5b533d7bb9753234ce0fece14ee2142630f14d6e02014a8b2de83332eb94e62a15377faf6", 0xc9}, {&(0x7f0000000180)="9b159cee4d9ca024f2f25f3b44918fe9d0397fa4ba7112723d026e13c6acd1bd3be2635fc3ac479b2b6328f31c97139dccabb36fb9d8e0ca8e4a322eb7bbf53cf66294128cdf5f40658111bea26d0029cd356000dd9fe6dc214bcc1edb02f4de2d707a93fc964cac7653d840f65e0d12270c8eedf766c909ffb181439964e51ce0ee6e76e94b3f2e8fad47d6eff1047755d5dad3cd994404135a34a6c9afd2f21fea16e3fec0daca19f62c5348924e2f1d7681cd7e56caf4a3a2f352233d79798b95844028f6170f26d4", 0xca}, {&(0x7f00000004c0)="ed2d8368f47ae9ff7c5225d807c7b4cca44bb7326d530269d897bb11da4494218fa8609369b730501c498b06ecabbcbcc80533cc994bdab45eeb248283ea1cb037cca023bce81701a81308894ac386785426edb95b003c2a5e4dadc284ebf4e3f3fb907ef81843446b19dc17cf9b4bee4dc860f9f3a27c62f990cc7749aa4d354c91559f83b967703a62278dec92bfa3ab9ff226244159fb5afe294aa242225e1e225132fad31cfdc27132555d91e3d33e760956f13c19b38d74807cf24910e66913336c17b73045f54efe6aadc8af9545d4a717bec1754566197bda89c16bc1a3b476b9660c52186b27e636a808c69a8d0b99d4e153b8623d39f7e30f3f76696e1b00d48c483105b295b5bcc08ce61deb0f03eee0ce9db568c2da51b238a209446b05e16e0adcfcbab7a65091602880a2b9e213f1ffcef7966c36252c8bda2f924b60eab6181a82dd9a3dc4b9d6bb528c15083c8dea7fe55acd67d418ae3c94a9b3438210af15ca0e552263f55ca68b371747707dc62c3265ad7deb2ba67b6b03f67a3e7c26d135b15b311fef43a16d8b162d6b3b720703105f551aed28d7503394702ba6c9fbca0dcc3c2cb8b608ce30db5d34955997c9455e9d957e51ad7ae7dc42b40960f6f2945802cbae2001ccdca509950029440fa435b4807b6b6b1c966fa1dc78be9f142c433bc6a6a79c6eb582e1e733f908e965189e3a0fe9fe24a89f33a563ce4b1e2b4738a96452d9da4f8bfcee4202327c44e1246203977bd7226264d9c8b49b36f96dd63586580152dc378aeff752089cd784d09c7ce5a9bacfd2d65895be7d25c18b0fd9187faf18a80cd7f66ff741119aecc1120c7fa4a980107dfdb776eafc387fa66e9bcc7ae0df61e6b1d51219ee62c967fda7897e363397abb027ad16497bf1b46a96e863447136c2148957da2f20d5deb5090bf4d83a2c3ae9535373af1619e8a0de74a12b77204dcb2ce3f35917bb8ec018ad99fccac05a564cd99e02e321756d31c940fb4347d5355786c3ef8360d04b75fb564d5e32458d4f679a7f72cbc3a0123b0f16bc8896c51ed1b9f913568dde09c806bb9007b4dbba50f6cb5b976dfbcbf2bc3d9ab1f525e27d82d893acc32bf3753942ed82f713a6724827cb434bd12bb32cc1abc97a239a3ef9025075475ff6f11aa40653f0f1fc4a6b74c4bdbb79a5427f36c568a7b8ac4d20bb5eb96e1e198fe91e33a9e0633ea0fcf4a650a8819ef29c8fb883fe5ecc542c53be13d158a63ed5bf4be54255bbb20ed3a6091af85ed00a20ef16ab9cb0f6a5adb5b216e1f57117f590d121792da4dab8ca5e99978cc55d81ee4369c7d4d338cd7d7ded014e1a084adebaa35d7a41ad02af3a1f841dbe38d1131231d71c790b03b0600da113ad3543f1bd85525c28bb744bb480bf8f1769e2648b9d8000576babdec7e6b0c9e98b43ab2db43c8f199cc7c55082469412973697ee3b4a3552193b2af82dec4d796ca319dd66ff85d97017319677714cf44115e7f718f73dea82ec5d7fc3c73d96d0c3314da6f2e1395326458b3bee079360cf26e73a24eb0434d08c62f8ca62a36728d5f90e0a87f76c4c029762d271f36a38a930972366711d41d5cab2ac181acabff21d5cc712688b26c0944858b1a891b1f6d6d53aa8c44fe55876a596dd078ad9f2c66e405a2d1b6638c58c2f60a589c00267c890706eb574a48b70a617306edd916c41203ebfcf704d23ec89cdc9fae8abc56b7970932f10cc1638c60e9bf46c69bfc3598532c287f564caa38976f1a67ab3d20fa93758dc4a29698acb84f0a30e9f75f1d3730681c4c768c4256a140263f8f8e0208273b48354119af32e0e62f6f1448823d354fd5d9f655eb68e74afebef13bd8549c9f37701e2ed6da2429f472d85685fd769f4b82120d6024dad8d4bc713c4c49a32667cd36944b60d4ff1d7dbc3f575e1d08f68fa5ec3bc1fe102d9cb1ef0870ad000f9c6c25548aa3557bba0433d96ae99e984219cde5f45dc76896c40f7050c6bdb9b08f43f4acb4c9704e6c7d185133913a313f04ad95d3d5041997e1a8e7be87a20a6e3a6be4ac3bd69e3cb07316d635bc44d0c0f99b4c0276190ca0d756a128e841f7b93c8a38845cdee1a272b7f67d519e60f969718bce482145b734f2adf783a0b45c015a16c81789c2dffb483ef223ae4c0f6502805dee6776749c50c4184ffb2c8b6de2db4f3bd07567322cd8523eea2d92e592cff227428c961012fd7055d6a41790bebd1656d71416fe641bd14a8f8c07104d684a72be5ad85bfca8347bc9ed2eabf8e9058ba0004a2de6062f8b7ff19fd574e44e6df1226f9478f1dfa8895e7ab3c29c3fbdf40cba4caf721019670380f4a89bf1d4fd4988127189bf76947e8da7530090dc3305e21305833b066d7f6fbd3aa9a737f3f0ab2d5bae4c6759bdd53ee4085124a50a30612fbc8eb128279549928836ced010632784fae382677f9252a6373b244bd58dd750191d322cc772c3b59557e1ca229c2ec7a64bf4cbf7cec9d22f1aeaaf155b6a06b91183dc63aa6659f616ee9402d9e6e69f894adaa8ad826be06964a2797a509947d8fa7ec3c957fd81560b82880c60752de51392669e7c94e9868e237814f93c3b7cc69280dcaf0fd924c194080eba63f1d073d12828bf5f4799347a0d781f9c64c480e2463f754d490f451b2e124245214e54dc89a0d2b65b03bd8249a49cd9c8c7a9aedf49d39b1d9aeeaf2fe50ed93ea9902785ea5b18d29074ad53cf859910d54188a1eea2722bd54d5d6f423bc63bfaf84d01cad09229b724f43347f94a6494cc0d35f9a72cf435fdd68402b10e2fbc1b1d5d8c1ffce803e0d313e9ce11138325ac355596b07dafc1e84b45deae1dc2cdc160bc1f787f15964e373aed6258d08d23d09564ada723348290be33189e6b1bf216cf9f0295dca51b4475f0bd1c5bde1bab9238ce7e6672715350d101c2201ae227f0438081ee24fd350ab456e28f83849bc00ff86321b7d3dabd179733cf1d8afe9236b5c7e0ae9463f346f966157ebfe08d2b4fbd3600aaeea9c8d3b4a4cfad6db412409c84efba82603f2aa75038726f6f3392b882563a1859c79a5a1e9e4db6700890b1647202c62e492aaa79291c3ea627b363554ecf85bfcc3dcd8c9f2eb74de78b77b7755d978d3831e5c76af9ecd73fb4d097b84753c7c0eb2a1d1e2a77f9cc435ca229fa80c0a2231e49131e9e4234bbfecd013a5b366b57c09a27a429dcb8974149d2c7da6e5b61d914304d211df6c92fb71996f964968baec03a4b090f897cfecbd83ec2f1edff974f8343975052173954acb58e7bfaeb90aec34dadfe487ff992a257ff3ab0ea2ef45cd254fba5e8d863a8578953c26aa2e74b7b5365a22f134dc0cd2f34c3c0b8c79b713564ec3cdcb684b16e5ae9e682a0f0275d62e91bb911d88a254f3300acaf238376c5b1a3a6a81777b6f2d55d0fcaf2af00ec041d1946a5de4f0195f30720b12f2a657fd1b583087a0180823afb2e5a5144edf57f5c97cf5c37a54c20f24e7ee56e2c8b209accc2a36fd9d259704aa3f49abf9d9ac88e1a01fbf0a189a2d3cbfd745b94d9de4b5159df9f99e0dcbe70a908e96b075c636a54f9e0cf8c2d0212c982dc5ed47c9e97421a0624876cd161320801a0107044ab4ceef2bf0b207add6034a02c8da2eca25c4173f01daba26cd0dfa75d4a1b2a37c0af5c8d1e7ec64af8a344beb61fcb8afdadddd8ceacfcc5bd9e6474250e7eb1ef9266b62b4b300b2c10b803495257d0618b525d523bd4b9a240d477820971d1d9276f07f64250bc9bb9963167723b933bdaf650f530b7814b73ba563391db954c727b59b7dc834f8e7f10e4674ed11a808b56a8d9c214bbbfb1612b47c264177c7d5889f85079de4ade703db2a56d42adf60088f0d5b1fbec99807d27b58a7745039ef489a9a8bd1353ce42461d1ec8226bb38afba84111827d7cec463e504e277818ca445a05caaf4f20acce5cab32f004c61e8d18cc30ca076a67d68bf1ec3edf52381a5ed7cb4e0937c680a8389c0f14afddd9184ea55569f0e3892fccd7232cf3971e56cec0fb1304634c072c5492f71aa45da8967a74d55bebffffe293655bf93fb1e2ed39f6db852da3dc2815bb4428f264eb757b042b9a24f2052a9693536941f4a1bf184102a2eba0e6a6375045225aa8a505ae6a99f2062afa248382669be07ff99d99d64719a32ea73a231e9044d60c9f280e92e4113ff7b50f9e39c54f2751916d95e114ad9df80e471585c7574f0c9940913bbdc78be6c48de767fd291135aff94562a24b2423cfccf23e47e8ec0444ac47cdff67eb6b8cc2ed0733f1b6921c59604be7ed4ea1c946ca5fa80a53c562b4570f9d44a009662bf77a9550e700db87a19ef6929b02935ea54c08a1abb854f2cc55fda6c28f1eb24a87ffc63980ace43c19a0fecac1c48116a6c2c6d473ccc767c9e47e1bcfaf71715e73b696d15fcb600d412430588e9734ad6b80809c83441f31f88ba5ade88764f4ae033412dfb8e5afb45817c7eea776d8075376fd970e9fe7f86fed06db1e66e6acc240c97ee00e51af48b825f6851231503f5a9d7cbf5b284abb3eb1ad6929b5755670113caa441f5ae75a925f8571299c12a153264b0fe6c58c2f9769d0104ce4cf5d816d55dc5ad1ef2f0e1267b9c26e4e2e41fd2144b92cf88eb5ff650974b991a0c897842ab6ded3df2a3c9753786ddcdb78b6c81921ad03351105e0b347b831f2a43f40ff258fcaf74566a8c9fd7b1e51ccc2c7644b1f73ca508d1aa3b8eb0d2b5561120610244fecefadd6e4a64b8ab1d1c1ce66a9868d45091212a89ecf8d0b3e799776a6fa914c686ad27186148243af9d90956777a8facc5a7ad603ffa39adf645e922879bf1c5de6849689947bec4c51851e4fa0a7e14794c5da93dbae71ddbc6809a2fc6babf4233fa16df1511ff96ff954d3bd8b38b81889ea6e2a9ff14d7aa6a636bd786f1ad9b3147dee77bb6b86a7be7fb97fe96fbce618d482ee1743124e9c1289a13d064da120656f3f7802971bb0765cd81a2922a5ea105e4ece17ef87078bc2c02725f02273a9b98f9e3fe096270ed81edb8439dad118b7953e6cd64cb92dcf4afb33582b667aaef73e9bf4d07caced0ba1cbc35e10c7268c43f1b6082a069643978a2d7f48eb91d7117ff1b8d005881fbb69b29aee18a7f21cde3b321a4e96d52f1e5fec62509923427de2c0a703eb9aaab284624d06d70b931f374f3f9c0a12ab9e1affc6eeb5ab6bffd230a49fcf3c4e2b00211c864fddfc89209d9ebf3e7b69b5d8bb7cad6aa0123ea52421ee21082b4b75a48e251498227fb4dff6a607edd3c75f4d3755febb3c749199d89eb2532647a24eb7b271128a9b147c5772c7d147c81733be202809ebae41a236847276b81824b487dd27b008e6e2956d5fbf050e00be44d74a5fb8dc3ad73abf52470181c48f19e7f90029a0caea24b571f622a12b19aaf46d3b68e864b7e6337eb9fee2651378685b4a0c88abe1d12b5e9c1c19c4238d207f1544de3122fac1470f150986ef36e4027d329fa8f42c7fbee95739f80106035bf5737ac3964453c741dbe634ff03341a829bd781bee1f626ced2e0015b5e5fb82538c865d69b8a4f0fc46338a8bea8a1ebd100cd01e840d8dfbc2bebf1f4d7580101afce578747154c73848bc8714a3c2a65de9bd61bcb2c8f7d60962de72aa2b76847c0005a377e5ef2ba2e3225413709899aa0c323c2425ddea11cdc38c85c3e5d6eec1032bc081ab918dc8df1d", 0x1000}, {&(0x7f0000000280)="9e798164b5522e01e7aeb47d612892e47a978e047792adae7bd169a98a301ea410646e17d616cdaa62c5b87a387c660cb853772a8947ef1df826e01327f875b42ac0aa34408bd2fe705200643ddc79ab20f1fb792478836a6353c7a1dad7ebde36cbdc82a868417263af9a962d0ad820b72704f1b3b47ddb4db73c2d21ecae123e051105f7223024d1f4e50658dd311c127cc523b8ee33270aeb36f7c257644692a8e7e00a21092a75e90a7f1aa3a78fd410275e1edd8e88b25ee76d00d7f5a2ddfeaf061d5edfa7b81d4783ad1e36f7cd3c15c7d1fb6b72a2ff4f87afe01be4a777f0f3fe641810a80c", 0xea}, {&(0x7f00000014c0)="3f6e265796cd177b3918e9bf9b14de8bf182a3ae69f89bee1ea73746ee187fa9f3239f0e88ccc3839762b0b69e08fd5fb372150c76e48d6fd7118bebf0c53c1fe91cd16c597e0ba1c23c000fdce9a3f5608e8fb66a9a3da1b6c4039a7a991cf52a78136f0edacd9c87b33e7ff29e49e110577a3a8f1a524f1f69d34b6e3815157fd379a1e33e8778cdd9598faca13815be0d732d65ca4f49d216a8eeff51eb74f5a23d988984743de2c11e4a222c6b8c0c7df9a6d09ad8d8a2aff4c44c2308feb3acb12aa4d893f57b1359c3e4dc79bf4176d18a6452ee5a0c720227326e48a3c02f2c4f1763f542927b9e0a080d7ac45647f36fe92a34293ee99973b8b535833133a86b05e1a998ef605f4333983c6aaffd7e3328b452250834dba77fba61530c019ca093e26d0d42e6df8002762d236c9a8188cbe989764b8e38ba197d7ba78a29f6910c7b880554c73c2ece1319509082b57b788bba30c69f2611dc9f7628b945657e786dc5fd226155916359b7959315178df0738f1fcb899aec336f007274f4d212c89392e4fdcee5a4b518c3463324e3de0ffa159e45783b1e4827dab57ccac5c6a48f549638f00fcecf1279e3e9a9ccfd43bec4000fe131714b87694805b89eeaa14abe515b0d13488cedfde0ea91e27c062781fc36ed77095e7a0a8398622ef98e5605a3d7e8c0b96460903551d26d31601c5a1773b64d93ef0e6657f6725a70340ce3225c65f385a9af778ce778c65052e5ebcba21ad5484eb2b2df113fd28e5b86d32fb391e7398fedbb3948afde9b4f9affcbece99ffdc785244c5a6367bd9e92bb84e1bd313809a02c5cc36fd16e82007aec1d656f4880aea359beb7da4c489006f349af5fc225ba2b67ab5cf3cf76135ce4a55aa744aae202aab932239f59b4f626817c4039f530ceb3e58a0048f6560369f588b9d569f138ad6205913d5c1b9bd7da45881808ec1c3a440c803060e2310a5b3c8a8a68eb07a45cfd19dbd8105a4cb34d60240f95687aa080f32d05f399deba2e598280a1037db46433c5bb8e9f4ed6907d23ab9690c8910b8f93c3de9fd8b980d6bf0b4fd585d78e6bf7b5d5593dd991006fbf5b9b72d5ef182b0b0de585d2512a40a8b1ae2b3998a36a28e6da67defbddb925c771f456e3c936e14ae0e0a2b5b919a8d8907c6875fef6fe45dd91478bf4a70a414a7a934ed251e7d3bcf96e7b30e9ee3e235e8f6bdad64f2b996a4d7da8a2e1bd1852563cea02019bb50f282457de7c870e386947fd87b1e4209ecb85daaabbd8066e443abbee89e22a41b5ba365ab671d859dddb1b0dc47f5b583031b788e88342c82f18800ac8164c6277005765b77e15d680a98af89e26df536cb1cdede320819bb9738a094e673026d64e3a94930f08dd6f7292bcc198d897ac9fd20e9b43e837a2984243c0d02ce5c711afc54b854c0c9256d134231775d8c8c94fa5d18533bf2fa48caeb57ad5691e0b034cf629a3a02b73daba75c95e4193e599b62da69299bd1b15aa92aae2115455053b342f4b095684dc588de6edb8c8626f1d7a380de14ed6a65f12595c5048941ee4b6709940eceebe075ecaf5195f6200da9866cb0bd756bb2dbc3bf63b8423c6f755939d2a9228cb4689d4ca69a10f5de7cb38c056e848e34c21b1af0d886a82a230e02460d2acf8598145670cffe6f33ffae3cd9bc8ae4a9d0e3e6c79bda07a38368cdc007f0a64c99acf0a9512d6c27d20e3e6c0337479010bfa0b85ae13f677d0e6ca3bcedd13e4be458eb2a216872fc562c3ed676fbb8912c401cd2066753c00d3f04a3c33d59e5f5ca8062b4f2ab7b267c09118138732165522d0145353b791d92990bc488064bf10c688b77e2f12e15fa344211ea0e0ae0fa8186fec671957312446fdf0e1d03832f8b9a1d279b0d2c4f7dc14e618a0372cbfa7389e227a6d4fbee470bacd5c346c9636e24904de3f51e36f300519ca154350c86a8aa2a98c479105a37ac0e41497a2d53761dcebbec688f30d5b5fac906ab46c1f5b2906bf5bb429cbe63ee292fe43f6987d128de47a5ab7aea5fddf2d3d3fd58730d0e17e1e425d06a38599c9a7a8b9f766425bf54edf333c6a0d656450df32b96ab43f042e1c500830fb2ae52c99d60a88beb14fd6550f7689cb9dc7e887446cd8713ea19ebba0c9837d7f096e1c32ffd7ef5ebf429c2062bd3d9eb4abbe200782b4e236c73e403ba20b2658f6a88e81e14137d71374e3044752cfcbef369cd7f113c322e93d12b7b4db627886a655b842fa035d4069c7de3f34a1a16417bfcb5098c4351fd08a33e61a3cb7ad0487718744f4ffa10eed89fd6983d70ad6b541e595fb07e3f20fe20abf8f947384e3ad0340d0501ee04e20b824538ef7a8f62b3d9d101a0ad0df78e9b4ec937e227dcf8e79a33ecc3d03ff7246513bee4f8b8bb761c8fd7dbb47a56dac5caa22569e38880a68474a2919b3c2d141d4c0e32ddcbd5d98ef9279715c08b1963a2fb9804217518078c5850498eb7d1ddc943922418c2bee92733727bb6ee34ce22ef5872a60176b226e39844f13218bbd75e8dad2791025ea9b421c2ac787d86dbf40d0f6c9ffd4c51d8771b4b4a41720637b56a27759111da24f43c1172b10145aa2d8b35f21d826e6d7a582723ded267a53c6bb2d7efbd64d8aa5fbb4dd93fd4a6a420ff5084a2cf6b0e89dbb183191da7d18dac53fd6cd2931ca36096128939d938d3471f8775aab28132d83eb6e9e3c8cf4cf151d98017465de3f01895c8587b04ac9a5ed6fd412e7e5c5908aeb1952265a1cf209e7f3fdb1adbdcdcf7bbdd22531d58f95a119e1eceb4cbf37c19ffbebc029da3296b0d94fcd9474a7e5d876961b8eb9a81c5f778686cdbbb44100ef9cd9dc6b1d11dc2dee50ead7cd0f1c208bd19f53190f69ec2be5ccb94e108a21047db7fa41968a64cc4028d4aceb33662fdf4015d50574cacf7f87372cec284df2fa3aab090403e16d8fa2b0de8943ba3a86df6129a9e872b1c5cc8cb4f2f99c9e0fb0249dc377e286a02800f1465c43ac539eab8c3e3d736eed633852923fbc6a71c5c3d73f116b36eb8ba9e171283a69fffa1cfc17e9420d361289d92d18ca3a6a7f47341b6198bddfc709e564f445914436ca187ad86a3c441d74a32bc19c477b9a6169238fa59c1acce0cd42981ed997e260e6088801ea3baa2651a685938866166a8a9c850bf0cff60c4ecb3524de98c469aa82b591287086b8c736dc816dc1582ccac3262f56fe7081a847c0226c6c68550f5cde1930eec3312809fe6bab1688011b178d2433d1527915f933e1c03c05c26389e0b47e7f03f91580ab17930b9d9dadc598ed3b2b8738cd661be2ce34128c2c82c988e5067b1fcebb1e7ddc7b120a4fbb1d1f9a6d33b0d99721ef23c96fc890c031b066d6b4498cafbc441d7eccaed077d9808f98b4eb11c052808c613bc5989d5c99dceb9f30c001d5a3900c77532d19b93885ce4e3d9aed87baa3eec9a1205b01383d405105f60cde470c4d72b5c6efc8631037f8fb08a1752bdf5670a3ac96193f8e39d5e4125e1c1637b45c2137b432bacc21440c4fe5441821bcd80ee2e547ad1f1e52e17aed07c218aeaee202acf71a6f687210154cbe8dcb3666459c9fd50d151c8ffa6dc299e67d885e713c02598fc7eebb93ace5dc239c81a61da335f97a1080908a3bb5f41ea2e91738591462308112f3abdfeef7413322c546ba0c3c1e7409e73b7ace55b8bcaaa0309d08afb7d9d0dd492eeadbaa4e7cce2c1ab9a84805662782c164b8266cdac4b05f1a25fdf18faa6035c016eb245e94a2b44541a90016d0e1d7390d447bc70d45c99b7ec9297f9c7eba04bc0f29706da1fc168c6bfe525b0230938926c953136764973d88d96540434c534f8a9babf8fff30462375074704a8d475ddf9da51a48e2be01502a1ae2b6563a99861631d0583820f73ff0deaa1d3967af7e82a0040d29437e058de72738456e3399074f79c65dda16335e8ddc153bd6e905086698d0692b447228e1fd5d3c06e0737e5fe9447ac50344cb72150d393179648ab3ec31ba2840e949068cdf5671d9d3b76652c1ba0ea58ba008cbbbe2bc7064038b4e8f09e03c931de48b8f833af1c2242400af8c7c21dfa59d0205522d5687c1d77eea44a7dedb1a58e03d7bf4457962719c273a40cfa47df588c0e1e0d7a9f7c1fc41674d337dd4874f9e48f31cdb788d5439c7922464d0a49e6a39d881998b3c7bbc5d1a3626d93203359a5182acef381ca3279648a45e8dafccf308986c09675ac976cd85f2cf055f810ae0c8e941d86ab3e53e57de3cca4aa3b82bdd2a928e85fe72f2597c0d3e1c440bc122526d4c4124682b7cbb46dfec1e4424b63fe679053fb6253d276b5788e4b6b002c95c79094466826ad3acfdc60fbc591d1728da8120f9ac7fd0660f7e64f27e4a9a2163c2ce1602d660089ce807cc5e33f2826548556db22915f31218e108a0493b9a69d1ea2fdfee51b0addff8521d216e4a82963cb1e50be9ce6130aeeb2f50f20867f5211f3d21ee5cabe2", 0xc91}], 0x5)
writev(r0, &(0x7f0000000480)=[{&(0x7f0000003600)="5ba38b79b82ae150f112194594abb8531e35fff706716e484be13f20ab60d4090ccd7ba2b43b2156dd5467a79b6babe5f66be3fc1190e30603ba36daca7dc43c0579abe2ab8b2e833a9cf61386a6e385262352a302d6467b0dbb65e3810772fbe9fafbb70eb6a062a1cfc607e7599e3c5019514187985ca6243f389a5a718ef74d78c91783491d23b7b7c79bde746e323cee20364b66da9dbc85895ec18ded20be009c8a25599c4e7c1088c995b981d433e6ae5974950f67af82ab74b2990daf4f5bec4cbe9ec5ea8cd2e148af0a", 0xce}], 0x1)
mprotect(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x882)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
writev(r1, &(0x7f0000000380)=[{&(0x7f00000000c0)="496fc0a5c40a33e83e79117a6ccbd4fd3011a1cf1a20527def72b0d509f4d6ca007abe5e4f14843102849b66bd7c9029a8bc39a5a9ce52ae88cf7e0b28ca203b415cf201966e7cf40267131b332c729a7e23b1e9878029429d94c3427979e77c32", 0x61}, {&(0x7f0000000140)="7287f9d710c69ad27da12efac14ba8c05a0f46a306d64f5dbf131b6d94b97f78beb3a1d2191ecfa85cf10167f3a680b896bb78540a02686ae3f6c2d5afe15716a293603f6dee0ed666dcf850a164917e0f95b1936043a960b445ed1d14206f6c5ce33d26ae9a0b3fd9153488a42edc1ce6c5c2dfa9711e31675909fedeeddb8f45b84dabbcad154ae3809d5c1c15c879b3345f40179b2c33f49a395e98d3f7a7c7918a27392b006470ec84a237299f51bc040594d1da62abd48526cbc0d1bc1c4f94dccf41d428c707b8a5c87c5bc45dcfed09bd85b22ecdb420c52e4624fa13e556c47c87af503e98d358a0299c95603b072fc12d4c", 0xf6}, {&(0x7f0000000240)="88e28d48acf91e48ec2b053c3b155757f16e91b1526936c0129ba4c018f509b05b945da8c45e4f75ded2ca1930aef2725208e12c7f8f9142b7c958375c4c78e82903a58cfe1469db9b619ae423a596003f449d6dd6f448d4fab2d81aecb818b1e6703594bf0b32a268463efa17326a09ce45e325c1672bcb15d352b6574a1207675236e6e6e8e3b8c68c9bbf0d9fd0b487e27444229593601517", 0x9a}, {&(0x7f0000000300)="e27de3a1be1c1fb6b58843a95b46a4a7423d085c6691c0c0525f1dec0f84e81b48c461c0c26047a1afe9c702602f711583adddefa426bd350047a7c4f3a24eb57ec13e523a4ed399f79c21233d8b80f02ea9be", 0x53}], 0x4)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0xc2d0422a, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "0400", 0x4})
socket(0x2, 0x2, 0x0)
socket$inet(0x2, 0x2, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=""/48, 0x30}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x28}, 0x0)
sysctl$hw(&(0x7f0000000140)={0x4, 0x11}, 0x5, 0x0, 0x0, &(0x7f0000000000)="eebfc15ba5f483b555e16005d964391b28052c7637e7a15c165ad1f316fce006a916cf0dca6739ffd98c9e4e1784a969981c10ee06da23f9f43234be8257d31715b48aba26353b8f75a056dc8f09", 0x31)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f00000000c0)=[{0xc}, {0x80}, {0x6, 0x0, 0x0, 0xbafe}]})
pwrite(r0, &(0x7f0000000040)="e81af191c7a750ff6914f6317e37", 0x17, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000001c0)=[{0x7}, {0xc}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
mlock(&(0x7f0000ff9000/0x5000)=nil, 0x5000)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
munlock(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
sysctl$net_inet_tcp(&(0x7f0000000240)={0x6, 0xb}, 0x3, &(0x7f00000000c0)="7c64710100406b744ab08f2a224ce7719246201df68da0331977650efb7153ccb18861f0666e840900dd12d926ac2f7662a19bb2bc1b581a7e3d183888c5e75db9a0cc1198c3c66dd7461b30c386825a26", 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket$inet6(0x1e, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x81206919, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$sock_linger(r0, 0x6, 0x10, &(0x7f0000000040), 0x8)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000024c0)=[{&(0x7f00000000c0)="b14961c6197132cfa627cac18c29d3c070a4753aa8281c81099e04ef406aecd092675639e88e02e4ec91bccfe56a35b79bf82c16883ab1422723d4eb564639b4f4252d34558691a4e8c1b4b26e3d5159d017fc1fb341f435b633c3a1122ecacb45595a4341212c86cdde60e5fbd984c8ba862eb9fd20ab87dd223efebcb9311aa78110183a6019cca59a87fd0530b1", 0x8f}, {0x0}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f0000000080))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000740), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240)=0x2)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0xffffffffffffffff)
setreuid(0xee00, r1)
open$dir(&(0x7f0000000080)='./file0\x00', 0x200, 0x151)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde0600", 0x7bb23feb, 0x400})
readv(r1, &(0x7f00000002c0), 0x1000000000000321)
write(r0, &(0x7f0000000180)="b7ec", 0x2)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x5, 0x0)
dup2(r1, r0)
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
listen(r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r0)
close(r2)
accept$unix(r1, 0xffffffffffffffff, &(0x7f0000000100))
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0206922, &(0x7f0000000300))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x7fff, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040)="ebffcbbf13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket(0x18, 0x400000002, 0x0)
setsockopt(r0, 0x1000000029, 0x23, &(0x7f0000000000)="eb", 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0xee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x2, 0x0)
r3 = socket$inet(0x2, 0x2, 0x0)
r4 = fcntl$dupfd(r2, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x80606949, &(0x7f0000000300))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0x800c5606, &(0x7f0000000080)={0x1})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = socket(0x11, 0x4003, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206913, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0x9, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x4}], 0x1, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x4}], 0x1, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x6000, 0xe01)
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0x0, 0x0, "551577cf81fc7b457abe24105f76e85dc0ced443"})
execve(0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000300)="154e57cdcb19f1d297636247b35d722ceb011256d58b83f20cc44af94ee68bfd", 0x20}], 0x1, 0x0)
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000440)="4ca92d63b07376cf6ade1273bed60154e42613529b03305813d9868091f7652b", 0x20}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x1}, {0x25}, {0x6, 0x0, 0x0, 0x10003}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000200)="2eaabad400c6a810a14a62816204", 0xe, 0x0)
sysctl$ddb(&(0x7f0000000100)={0x9, 0x5}, 0x2, &(0x7f0000000140)="7d085d14", &(0x7f0000000240)=0x4, &(0x7f0000000280), 0x4)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0xa, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000001}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x9, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812e8e4e713048beafba31b8e6", 0x14)
r0 = socket(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000040)="8914e4ffffff4409b1ef32da00080020000025ce", 0x14)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000080)="cd", 0x1)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f0000000040)='r\x00')
openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x2, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f00000003c0)=[{0x4}], 0x1})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
dup2(r3, r2)
connect$unix(r3, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000100)=0x7)
accept$unix(r1, 0x0, 0x0)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x4}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x48}, {0x54}, {0x6, 0x0, 0x0, 0x3f}]})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
writev(r1, &(0x7f0000000380)=[{&(0x7f00000000c0)="496fc0a5c40a33e83e79117a6ccbd4fd3011a1cf1a20527def72b0d509f4d6ca007abe5e4f14843102849b66bd7c9029a8bc39a5a9ce52ae88cf7e0b28ca203b415cf201966e7cf40267131b332c729a7e23b1e9878029429d94c3427979e77c32", 0x61}, {&(0x7f0000000140)="7287f9d710c69ad27da12efac14ba8c05a0f46a306d64f5dbf131b6d94b97f78beb3a1d2191ecfa85cf10167f3a680b896bb78540a02686ae3f6c2d5afe15716a293603f6dee0ed666dcf850a164917e0f95b1936043a960b445ed1d14206f6c5ce33d26ae9a0b3fd9153488a42edc1ce6c5c2dfa9711e31675909fedeeddb8f45b84dabbcad154ae3809d5c1c15c879b3345f40179b2c33f49a395e98d3f7a7c7918a27392b006470ec84a237299f51bc040594d1da62abd48526cbc0d1bc1c4f94dccf41d428c707b8a5c87c5bc45dcfed09bd85b22ecdb420c52e4624fa13e556c47c87af503e98d358a0299c95603b072fc12d4c", 0xf6}, {&(0x7f0000000240)="88e28d48acf91e48ec2b053c3b155757f16e91b1526936c0129ba4c018f509b05b945da8c45e4f75ded2ca1930aef2725208e12c7f8f9142b7c958375c4c78e82903a58cfe1469db9b619ae423a596003f449d6dd6f448d4fab2d81aecb818b1e6703594bf0b32a268463efa17326a09ce45e325c1672bcb15d352b6574a1207675236e6e6e8e3b8c68c9bbf0d9fd0b487e27444229593601517", 0x9a}, {&(0x7f0000000300)="e27de3a1be1c1fb6b58843a95b46a4a7423d085c6691c0c0525f1dec0f84e81b48c461c0c26047a1afe9c702602f711583adddefa426bd350047a7c4f3a24eb57ec13e523a4ed399f79c21233d8b80f02ea9be", 0x53}], 0x4)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f00000003c0)={0x7, 0x6, 0x0, 0x0, "c06e58513cee6dda334e062773cf16a95f584e82", 0x0, 0x5})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x40}, {0x1}, {0x6, 0x0, 0x0, 0x38000000}]})
pwrite(r0, &(0x7f0000000340)="0900000013eb0913587ca9ec25db", 0xe, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x47}, 0x3, 0x0, 0x0, 0x0, 0xd)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwrite(r0, &(0x7f00000000c0)='ml', 0x2, 0x0)
r0 = socket(0x18, 0x2, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300)=0xfffffffc)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETMODE(r0, 0x80045726, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x61}, {0x7c}, {0x6, 0x0, 0x0, 0x40}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x11}, 0x2, 0x0, 0x0, &(0x7f0000000100), 0x0)
sysctl$vm(&(0x7f0000000100)={0x3, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0xfffffffffffffffd)
chdir(&(0x7f0000000040)='./file0\x00')
mkdir(&(0x7f0000000140)='./file1\x00', 0x192)
r0 = semget$private(0x0, 0x4, 0x18b)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000480)={{0x20000008, 0x0, 0x0, 0x0, 0x0, 0x100010088, 0x207}, 0x6, 0x7, 0x3})
semctl$GETALL(r0, 0x0, 0x6, &(0x7f0000000040)=""/49)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140))
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r1=>0x0}, &(0x7f00000003c0)=0xc)
semctl$GETNCNT(r0, 0x0, 0x3, &(0x7f0000000300)=""/148)
r2 = geteuid()
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000040)={{0x9, r2, 0xffffffffffffffff, 0x0, r1, 0x60}, 0x4, 0x400004, 0x8})
r3 = getuid()
open$dir(&(0x7f00000002c0)='./file0/../file0\x00', 0x10, 0xd0)
lchown(&(0x7f0000000100)='./file0/../file0\x00', r3, 0x0)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f00000001c0)={{0x0, 0x0, r1, r2, 0x0, 0x0, 0x9}, 0x5, 0xffffffff, 0x9})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x87}, {0x3}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0x0, 0x0, 0x80}], 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x2, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)="2606ef67f743de166e61398c3e652587176ae49204dd1d002884e6c5", 0x1c)
getsockopt$inet_opts(r0, 0x0, 0x8, 0x0, 0x0)
chmod(0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000280)="b2770fee66a10dbb591a8d0302", 0xd}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
unveil(0x0, 0xffffffffffffffff)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000340)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$VMM_IOC_READREGS(r0, 0xc2485607, &(0x7f0000000180))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r1, 0xc0104451, &(0x7f0000000140)={0x80, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x20, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x3c}, {0x6}]})
pwrite(r0, &(0x7f0000000040)="00d681e355cf833cd50000ceff00", 0xe, 0x0)
mprotect(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x5)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x24}, {0x3d}, {0x4406}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@random="03afec94658f"})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000001c80), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{{}, 0x0, 0x41, 0x0, 0x40000000000000}], 0x0, 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1806", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = fcntl$getown(0xffffffffffffff9c, 0x5)
r1 = fcntl$getown(0xffffffffffffffff, 0x5)
r2 = fcntl$getown(0xffffffffffffff9c, 0x5)
r3 = socket(0x40000000011, 0x3, 0x0)
sendto$unix(r3, &(0x7f00000000c0)="b10005016000000000b808000701000000000000ce2894bc0fe62e18443fd3357af96caa0416c64f376336acf00a7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27bcb5d602000d7d026ba8af63ff37282902e4fd89720f0520000000000000f5a872c881ff7cc53c894303b22f310b404f36a00a90006ee013e657ae00000002000000000000000000000072ba2ebe1b080000000000d3c883b400000000000000", 0xb1, 0x0, 0x0, 0x0)
getgroups(0x8, &(0x7f0000001c40)=[0xffffffffffffffff, <r4=>0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff])
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001dc0)={&(0x7f0000000000)=@abs={0x1, 0x0, 0x0}, 0x8, &(0x7f0000001b00)=[{&(0x7f00000009c0)="5ecade6bb19338f6fa873fe6651266d6fa7326a5cd88a5bb5b4a68def41519ecaf1f7eb938db06d1dabfec5521f05d8bf26443a47bf0550fc258e308b68db71086a29dd9e49de380955bc41b89957be5221b58b639ae4b66faeaa5b8310c5d34ff6b8103a27744cab37114234c293231f116e4ce756644f84c97e6730f0feb50b02651d247c4ee1d78cb1bfc719e220e25d502add3c8ef5d68010e9f3a967f0b5b19b310f9a7f21e338f", 0xaa}, {&(0x7f00000000c0)="922b0c834a78cacd4e388d5a98d796fb5451c8daca54315b8e774363ab605f5366e94b282a5c738a21f4ee81e7b86cf407e981f59a2beb4b45bc13b7044402fe4b22c5910fbef9d4cc6019de597502d06bf7baf183b01befdbe8705ae26ac0", 0x5f}, {&(0x7f0000000040)="0e4a578a728acc9275f75d12354a5a9c285ac6a79dc109e6b8e66119374e861b608aed8b9b11de0dc4bf93bf80a7feca1b6385bf939eb78dabf44fc020", 0x3d}, {&(0x7f0000000a80)="4ef8869fd3a9f1ff5f2666e4ae667ab87dc7f3dfe9ab1401e584214657efb7404f8a0e36ee3dcde7629608574463d545c72fda3d58dd89811cffa5c10452acec72eb24bee072f5cd7250ce86d326a1b95f4bbd63a9b154bee842464daa03a8d4aecaac297bfd986860132a70cc3b6d2cba6f506ebc164ec84484a6d28bdb1b99c19c54b7a973b0c83a4071f3f03010636f3669f2c2b58ca08feb6b8f0ce96947cd8d4ca4ee868f4f098412720800db2ce084e6f6d957f82af25e78efe8cdc344cfc187f9daef9cc78f14e9621bca6572498ed70691dd33fcda8fdbe506e21bfc2dbef80cfb33d389baab5c5d46cff07904297879afbd52e7144058682ac442691a0344fe57e31488b58fecaf23d5bf470c19d6f25066e1e7f9e6664c4ac1cd50b2f1ed99ad7251cf5c30ecff3a6c08a4f06e08c86eca439ec122835d7935c160372b727fb94833984474e2ae78920abc731489fda27e557562cd14ea0f69a286db7aa4739c57fdbbb8d2e8ff7eb761ddd41a4bb0cbf242631cd1284dbdf56d9f1ed1541ee3225b4614db6b19e19bc66c4ae7ee27fe6e61ce41055b6d648e531f89453917c58ba5e90891fa0bc9a36875a3e6405c9e9578ae8298d4b3d19b2ce8af2afcb1c4279086848b350eeb3d1e8b1f9a15b8556866df2a3b7448b581b3b19b74eb4a90b3d1217ec24848d90dcb9cf2c8eae35740efe741bfef5f5b92f2b60b13e8724e7b1d6bc66eb65b3995bcfc5a9db35fed33cb85e398f9ad7d8213cd9264a4487c1df857df57f99c97a55ef6ab78998b5ab65809b5bd4a244ec187692eddf9a4d94dd7085e55e1030578ffdcc4bdea061d70d6de8c93340341164159b4f9b35ca6947db49cc8b2c2518a21204ecf5a8e1fb059e33fd91840ee36f7b067e25139ed98d9a4fa24296db1923b34cef90fadfb6ca47846b99d706b8d8738586ea5cf9af43bd41e0f8b2f940648b26b46a8e2c91a498d2c4fdea50f7278163f4f0fbb5a37fed9c2ad08a0ee3fc7e657ff99b9c813efd40a2b5dc0e907d7df287351bf2c0531a01f9c2e6579c6e523e80466ae001c53610de95155314354982a3e6dd96ae690f86c5331f75f37bed23af10ac2c946a610c93e5e9f28fcd98bbee1978e4e89fa5b99ccfb1577e3f55c5c696a3625b01ad2e6ba3dff7eb2a18bb5a38494be2e4012b4121b91f33f3a8c93a657a8622c35ec48b7bd0bc847e11ae008bb41e931f98c6d794358d9005d4d728aeb04c2887c769b0215fd831edbd4113c6807007080b5132bd953acf7dae81f70abc00330379bc77d81a2a272f3725f4178c8f52e04e8ec286716027649fb4de16c484def6a9da232f99b99378d242d10b9453ac6d50260154e5c5c704f4c318affdaae9c9a1aabeafbc5965cdee5b8ac1e7ddc235a03b076443e5cb07322c7169a502d8ed472a57b0992f4cce56f69211b2c39a6065cf207f41bf899b9f87f6a9b0b6fe2951391400603938b1f6dfd37a3cfea5acc2b9bff4c4adc4688f825d39aecf7b46925960d15b0972a9490d047b4faeaf7d298952e02170d4a4f94539f87dcd4ad1681d1a4717723e5f5459f7c19a5248098fb5bfe12f7a184443cb79467a974b4b6dd246726c0daecceefb217d36821bae05bce949eaca2e115864caac8c3be587a36735e502714346108c51815850ddb3895f0a881871aa24fb7304a479539cb0d917b212ca82c43cffef4013d13ddcaa4066654f54a25bb57dbf4879ed8f693aa15dc79553d935420b62f83c3ed738741943ed457ac58d191addec75919ea819ba0bd4de6b0cce4e9cb916de534446862f28231ab9e993c7f51b51104e446833332abd1ac038d70b0f8478e26f6436dbc57c099e0f5435a72776d97f16be2da2ed756f9c03651b426b16c7792c6f2295589c1c4738e777d4e7c9437f5b5173e59df81e784335a62e379d3c078f1c365a682d4039cd0aee2b83037aa3ee2eccb916239b99d3bf47b10031d4f23b6336f1050a91f7c9da3d0492a591c4ef8193b8dc6160b941d5e446dca2ad13e86f2fe6b2e498eef43664ec0f4af1dc177b142c8814caeb449cefcbc30bfec8c31c633587b0bbe7905a98a914288e005009c5a4040097c517969008f68185d8f9e497649be41b5cc02356d3c3f27da44c71eaa230811c076fa1f4b43f1ef56904f48ef6a88c92464e92616df37adfdb53324af0604257bcc45d52661ac616f563eab1c859447f92e0974ddbde1834769d492dd876742bc5f765121402b238b14ce8f3cddde4d9a371ce7c5d6edc807c7c054c69bfb05cb57675a5decb79eb0632f5b1d9a4ce776027ccd135a62e36ecce7d4587937c2db072f5ccd50b568da79283db8f62d6186047a817d7336d6b83b1a6b9d94a65dbed013c838601a710d1738f0ebd423f51922a167add8c169b2ad133408803dd3ba0513285b936070c9c3e6f7493e237f5fa655ce133874c5279501e67d107ce2317f54f1787a43595b4927bb7cb60ed5a828f45b7e87db12e0204ef7c3adbc1c91dce036967602583c6051a2563f13b09347919c6f03743b36ecc91b5680e6aaf5cd902cf614d438ea1193fdc9b66d591bc6f2002c025534ff484cd25531610d9c37c15e9494ec654734dc5bd372c63b9968b748e3ce1ff27ca5fe4f2adc9b2c334b0300d091685aae3c0c29b7b91d2db169f0a4d9aea1c9a294aab28f4f451d947a90ed7d0ca1f109640f5490b03ad1a3d04a8acf44bd4c59465b026b904c2621bc552fa82ee0ad697e2e44c027566bd14d489017b4f55167c22f764acb550a794d7d034b26ab82e2a37078db8b54406f8bb3f5e08ccb0aafabafb188ab77dd1d9a9ad0de1656c3e8dec970f6cdba9c5fcf22243ba2d360a85462634942cc1a69c68739c948b2ad5167124d63f2f64b0bd4432ff78a26201ab311946ad88cc265dd168e589fc360c867b2fd73d18ba524fb771abe389cb43eebadee3d0abfe9ab9430f38f951ab2c39e91c31b137217d99e6c2e9ad4bbc7a90e52867fdb8486b29388038cd4bd7682cbedeebab3a5c0901c7219e0073e6510e160bb30bcde99f9f63e1605860f1020e13fc63823973f4b9dee6dab5f8fe95adef7eddcf7f2f6311a226ccbfa68496128d597e049dde75d3ea41b0696f6e77429d3f4e0bc2c21c238a7a57dce53a5b5961d4c1096b3cc0df61a915eae0a1a4fc977c9f2134d30489ee8814596f4a583d7e868a7b50cfde1981c174414ed0eff034967059c2b786d73975a845514e078e0454e376c38e63c0b2d35128ab2645a649c1bcda0c5b1511ffc9ec358fbefe300ab5bd28e58ec68bbab2ac6cd64eaf6c94d300df0b4bd2478ecef940292f8fe65a629a44cdf68129ff0ae7a6ba8fbd212965571d83003f3bad502a49d2cf728fbb1a9c5938c17ce4430f706000ff4624ce7436fa05d9b8e2049682eb4402977a4de2a729d16f960ecf66b195aba0f586db495f7993034760cfb830197c4d61e363aca5fa2338c5fe862053aeaede4ee607800747b751132613f74ac6ef1a42380538baefee9dd8ab3dd81531e98f53a5f48ac9c56b347479de4979d009f147f5e1e08792ce4f1009229b7c503e34eebdfda54f8f4db2eba37c7c54524c1578c2e470a8e5c08fcc6b504ae0f7aa77ba2a7568f198d77b233ba0f795bbe0d9407cbdd77a622e1353c0414dd2b8e62aa9b93b233074a6c7587038c8a411c914dc8c03a44fdc88d148c469c289267623a4d61c8f06c7167492e99d9b9f72d18f963e55ff94768233cd736e9cd8dcabd46ab39901c815255f1f4e4d28897c960d85c15495a15fa458196053b08a0ca697e1424921c343287c18e90df8500330c7eaeea31cfa96ab10ad2e33b9657bd5f6ee570bdb9a6e329e51fe7eceedaf8616fb0db4e32b31a3c052ea57b1f7cead40285843c83478c98e3d3c9cae207632fb8d77a2680d03396fee5350a8ff62de9f9f2764cfbff6b22150c23fef4b22d4c4476c177bb9dd6db396d5e52cd5b3c0c3681602327c6ba7dca356d5b817d63205c11c0f36725cd5984a94d4b6d2766d95c5e7f94cf8af45229d424865a731f33e68284fb5ae914484d0da31ad09e2e2b9050122740e02841fa8723a1217da648da1a92af005ca557a3545cceeb44aa91725eff012d9a0d19745bd4d5ad2588b9a147569563aad93ebeada00f9955fcb457a1aa45574acab869fa83f5d00a33c367e3bb4e3768139a99617f3f5af1a82fdf90453fa2a8d96da4c5a8e5df3484b48769961a834c33fbf7294fdef2efe9edc3bb981fbdd211b5ca26780da9d3bc144ba663aeaf215e86f8cfc934e33cc27388258b0f1e1a01670c0ae1b90c27bb0407302c41ac9e33f781fe6f2939df9f123aa5eec1eb3713e70e9bacd3fe3b4ce02fb7d4ebd126af98087e12efaf9f16585abc5a6d90bdd914a1218de5e96dc67b58bb7d880dfb6fe653bcfddc4c5d566d1fc65dd8f34ec5420285d56960f56086b7bf981cb59ce82507b08bd2b19c2421eb827122c98dd4578a5e63d52aa9523e993d3b76a84f7b4255796fb2013b87335434f98a9cad164fc5c1392a50b6e7497d17dcee421fd7de44b096ab88386c6e10c8acabbaa8cf5dc9323a38378709a192c5f03f870bcb1544dbaf7572aaf2ca5fc1f9b39cd5a225f0dcfcd876bc50c24ad905f235b817934bd06280ae743c3b286f397fe3e1a81f1489ae4f341749402272565bb888cb0b3aef457810c98629ea78ede6e88fc7181768ea5d0332400fc316f3217505731915b417cb50e9104e66b4f457335ebe936bd248f4f713cece30d4e3db3dc454d7fad72c1ddc1756c06ecf087888791e84f276bbf18f79a99be4e03d1876ca2affaf61d7a2c49d02960718e2c0e3701ab35a740c0d4f14bc8133ab75d94896e75801d8a9d0fd9182ae9d19d20b009ca2a5167affc925206f6eb5d82f5373b0f5645d69dc0ac87fe1779a8f3f1ccf6b60d2666852b8282e804789a378ad085fff73b12601257de1187c0091c61dd99424407019b15c9afb649312b6565c5d751460d290eb952e7270a763f41e5c3c0070818dcec6328ae138a41fe4bd70e8f77c418093c90840dd4629d167b1f90391f570d0e2d525ac957e0e92819270321b8bbf426747dd4ded2004ca5b36b1f486bb3e408dc1c19e0dfd5bf5ca62a37991e4b66235062c31b98657f4bbe401f63e2b60e2dc903944981b5ef09b10b416bb70a083619ff5d12e8ce340d2a1295089b1399a5b35d6de117e324edb04b3e6930823326c64bbac656d163e8d18069721ab887a8ebe933ec30cdb6a1aa1df076f83f56113564b277c8e8e3412b123a239f3f8e932d23cff2905993732acf4b9b8916783f23a04544d506bc9fac5413adcb78ad115fb2bcfcf20b2b93b076f6d55d67abae0cbb397222eb9c80c40f30a74c5da220bba3b4b15bbeebf40e3617b14dfb531247a4e6bf750486dcc907818b8544574bbcc018f4a7fd9eec1a73904cfa73a4c805848d09e5fdecf8c342e46d5407b9b3e40bd9a9daf6addcf4e6b6a0c19060ea20e7036db4826cb2e4a458421b1f94010843b437bfaa9c94b179c89c24a66502854a103cbc105d6de81f843ded388086ebfa327113e50f934f4162ec3a3c7edb22685bfee5fe4e642333712a42673f57a615cce7b349eeb216956caebf8a65fa7ac9baa95847aaa7293295826f4756c1d05e5c5d018726f722b27edcc38a35ac1d597d902c00cb119f38f64d36cebdc8fff8a1910cf83173fd40ed72134b8637be6f9bc517ebecb8c47080b10faedf5b9aaf1d30790e4", 0x1000}, {&(0x7f0000001a80)="b4a3ffd92244fe1583bb2c8d557efe5db2cddce7451d84dca37cec3fd9ece57998d8182c066b75e04ae2a8c67f6ab5df80cd29101854fedcf2433432729426e992a45a35120c87d46f2f0b44135ffc2c8b6bdb8da7d5d4d598526e7f670b89", 0x5f}, {&(0x7f0000000280)="0063328d0919c5188aa4cb107b451a2e7f331b49254e013682d95486694eb413cf9d16ef504b0c90481608ac2fe6e89de901e355ae74c592bd", 0x39}], 0x6, &(0x7f0000001c80)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r0, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r1, @ANYRES32=0x0, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r2, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32=0xffffffffffffff9c, @ANYRES32=r3, @ANYRES32=0xffffffffffffff9c, @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYBLOB="000000002000000000000000ff0e010000000000", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=r4, @ANYBLOB='\x00\x00\x00\x00'], 0x108}, 0x400)
r5 = socket(0x40000000011, 0x3, 0x0)
sendto$unix(r5, &(0x7f00000000c0)="b10005016000000000b808000701000000000000ce2894bc0fe62e18443fd3357af96caa0416c64f376336acf00a7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27bcb5d602000d7d026ba8af63ff37282902e4fd89720f0520000000000000f5a872c881ff7cc53c894303b22f310b404f36a00a90006ee013e657ae00000002000000000000000000000072ba2ebe1b080000000000d3c883b400000000000000", 0xb1, 0x0, 0x0, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000001b80), &(0x7f0000001bc0)=0xc)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000001c00), 0xc)
fcntl$getown(0xffffffffffffff9c, 0x5)
fcntl$getown(0xffffffffffffffff, 0x5)
fcntl$getown(0xffffffffffffff9c, 0x5)
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @empty, {[@ssrr={0x89, 0x3}]}}, @icmp=@info_request}}}})
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x2, 0x1}, 0x4, 0x0, 0x0, &(0x7f0000000080), 0x0)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x1b}, 0x2, &(0x7f0000000200)="69f577ac67acb0da02273a3a", &(0x7f0000000300)=0xc, &(0x7f0000000340), 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b35c6"], 0x10)
r1 = socket(0x2, 0x1, 0x0)
connect$unix(r1, &(0x7f0000000240)=ANY=[@ANYBLOB="82028b7dbb"], 0x10)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r3)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000400)={0x0, <r5=>0x0}, &(0x7f0000000440)=0xc)
setreuid(0x0, r5)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
writev(r0, &(0x7f0000000240)=[{&(0x7f00000001c0)="e6416426712b8969", 0x8}], 0x1)
shutdown(r0, 0x2)
r0 = syz_open_pts()
syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
syz_open_pts()
flock(r0, 0x2)
close(r0)
r0 = syz_open_pts()
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000300)={0xfc0, 0xffffffff})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000240)=[{0x5}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
r1 = dup2(r0, r0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r1, &(0x7f0000000100)="8188d772f123016b26e25db77a77", 0xe, 0x0)
setgid(0xffffffffffffffff)
setregid(0x0, 0xffffffffffffffff)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000580)={0xa9e8, 0x80000000, {[0x2, 0xffffffffffffffff, 0x3ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20], [0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4], [0x0, 0x10000, 0x0, 0x800], [0x4], [{}, {}, {0x0, 0x4}, {}, {0xffff}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x48}, {0x40}, {0x6, 0x0, 0x0, 0x20}]})
pwritev(0xffffffffffffffff, &(0x7f0000000200)=[{&(0x7f0000000300)="9ccbb0ea13c4fff8b82673000000", 0xe}], 0x1, 0x0)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f00000002c0), 0x400, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r5, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x48}, {0x40}, {0x6, 0x0, 0x0, 0x20}]})
pwritev(0xffffffffffffffff, &(0x7f0000000240)=[{&(0x7f0000000140)="b26b6b3e923c0e6df592b5dccb52c1219f4f31184c7026f8eca9a9ab0515bd9bdff641c80e16ee46df222f43f1e581202d658438ea507f741815492e2378675a52708241a6b9fc5fd9225c91edaebaac58362d59f0de3c1c8688569781182227a8bc3754fba100c8e3a2cac2c382cf6bb0a8698fa6988b60"}, {&(0x7f00000001c0)="87e752e84b1fc16457e9431506758206a2b38dbd699bb00a66f5a3fa4c2bacf606ef"}], 0xbf, 0x215)
poll(&(0x7f0000000040)=[{0xffffffffffffffff, 0x4}, {0xffffffffffffffff, 0x1a0}, {r0, 0x100}, {0xffffffffffffffff, 0x8}, {r1, 0x2}, {0xffffffffffffffff, 0x4}, {r1, 0x4}, {0xffffffffffffffff, 0x4}, {r5, 0x10}, {r0, 0x10}], 0xa, 0x1000)
r6 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
mkdir(&(0x7f0000000100)='./file0\x00', 0x0)
seteuid(0xffffffffffffffff)
lstat(&(0x7f0000000700)='./file0\x00', &(0x7f0000000140))
symlinkat(&(0x7f0000000500)='./file1\x00', 0xffffffffffffff9c, &(0x7f0000000540)='./file0\x00')
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{r0}, 0xfffffffffffffffa, 0x9}, {}, {}, {{}, 0xfffffffffffffff9, 0x49, 0x0, 0x1000}], 0x6, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x800, &(0x7f0000000080)=[{{}, 0xfffffffffffffffb}], 0xfbd, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = dup2(r2, r1)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
sysctl$kern(&(0x7f0000000000)={0x1, 0x53}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x3b, 0x0, 0x0)
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
chdir(&(0x7f0000000080)='./file1\x00')
syz_emit_ethernet(0x66, &(0x7f0000000040)={@local, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x58, 0x0, 0x730f, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}}, @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, {[@md5sig={0x13, 0x12, "5103c9cd7f4a69bb275ee94caddec71a"}, @window={0x3, 0x3}, @mss={0x2, 0x4}, @md5sig={0x13, 0x12, "c9523b2d016aa82ab3ad662d755f0a95"}, @sack_perm={0x4, 0x2}]}}}}}}})
mknod(&(0x7f0000000040)='./file0\x00', 0x202a, 0x5)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f00000002c0)='./file0\x00', 0x0, 0x0)
ktrace(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0, 0xffffffffffffffff)
close(r0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='oL', 0x2}], 0x1, 0x0)
pwritev(r0, &(0x7f0000000100)=[{&(0x7f0000000500)="8a806d4c47e203eb00273e08b8abe99d4728b0e0b4e6623dbcc250a311781ea2daf7e8eca8c9fef9eb13befe4cb188844c7fbfc00f250cc165fce9f4b0257e09fae324fea3d74346f40278d11fddec1ca4ccc935fcb005b3e5ab97d9787b5f92c0d2ca36d27ffc34ce2032b595cd6a549837e84c862de6dd2879bb6ff73215a559596a9439aa6b622442ae6ae4d2ad986c2b6a73e3c6ae7d052bbfd845789e86f10b51ab353b80e1fba02e6ab4513d0d91bf2a0940e63394ecc21553cf8310d4169bd1174c1f7e3d5b1cb3498f3d3b9aed6ec8532c556389f5dee8a06ba18cb63f4a7ac2ba049b426e47190449aa641809050085019d5c5c8b9aed24f261c549491f979a1c4f513efd76da0bbd2fb0067f2ef69440808e8b9b3b4237c987a1fede34467d1f0ea65fd84d684398bbf63d4ae40bf356a7c6ba5332e721ab85bfede34c152f82e8f27d8272ee5d5f58ac0db540b160011af8f4532de293cfc1c4013497838e10a371e958caf85f0d680fb4d426be9eb58dc84b932c49619b1db0db01ade3359044d436bc0483ccdc438e7e70ea223445695778cfaf2bbe707d452045ad134f562c8554b1ead1b3c76121bccb9c63e85010099a4d46d95943a5a632adc0c7d6ce4602255eda545acfdf6189bb4143b0052841a05a22b448c660c3c57963cb99a61c7a84b4a37954bb2ff1e99a0f598021328c30f11ac4ed76464136", 0x200}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$net_inet_etherip(&(0x7f00000002c0), 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000b80)="56733e941394b22c0f3011841d7f31e644d47ce96c7465c9f2076c579eafeefb0b8b838ba104f4d4dbc1c358ef6137cacdee91dd8227d0a59470f56fcb4a272f1d10ae7f952466801a7b165d114376b6c95c7a03f02f0cf549f2f96a6db9478c5749edd4f2faad925ab3f89638506fe7e1599f53208a203ec869310e7e29817e58f7498b110f5a2318e3b5b068a1abe1d383e94764261d08dc6cdc1030ffde3418343d869bf88c1fbbca1a8a22c94b75f0791b661d10f15fad53cee0066018cff7f1d3b7494bd1ea80622238907f4964719e226109ea94d3a06ed28273596a949c16affef2b79c04f4d612aee06896d491cc157e3f90486a2388ddb09d07beb0ca3408ac60251a", 0x107}], 0x1}, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x3)
r0 = syz_open_pts()
poll(&(0x7f00000002c0)=[{r0, 0x4}], 0x1, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x15, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000440)={0x3, &(0x7f0000000080)=[{0xc}, {0x45}, {0x6, 0x0, 0x0, 0x2000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="8bd41c17f69a753eff2ad354cc9b", 0xe, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000501000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00e4c8b2ca3ebbc257699a1f132e278cb5d60200af7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881bf7cc53c894303b22f310b404f36ba5f90006ee01be657aea8c5fd6f0d9cf049c0a00384020208a371a3f8000400000000000000010000000000090000", 0xb1, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000280)='9', 0xfd98, 0x401, 0x0, 0x0)
r3 = accept$unix(r1, 0x0, 0x0)
r4 = socket(0x18, 0x1, 0x0)
dup2(r4, r3)
sysctl$net_inet_udp(&(0x7f0000000000)={0x4, 0x1e, 0x2, 0x1}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000000040)=@file={0x0, './file1/file0\x00'}, 0x10, 0x0}, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x8020560b, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x5c49, &(0x7f0000000340), 0x800, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000040))
openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x200, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r2)
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
execve(0x0, 0x0, 0x0)
write(r1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000002c0)={0x3, &(0x7f0000000000)=[{0xc}, {}, {0x4506}]})
syz_emit_ethernet(0x2a, &(0x7f0000000280)={@broadcast, @random="436753bc6c4f", [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @broadcast, @empty, @loopback}}}})
sysctl$net_inet6_icmp6(&(0x7f0000000100), 0x2, &(0x7f0000000c00)="a20b912e80f35e6f99a409b6bd554626091eb5adb7f200188a03163107091f71eea8259b40cb605ae998b44abcf2e62cc94f1732150c4302300c81f815a72940d22c679c94022bd9dc22086b89e2b3c80ccc817f5bf648d966336c13e9479586d102902f31fbf8fc8e68c4dcf8ce2c828580acc054947791358de836288ee8179466ee537e2739e785a73116c1896a0618d7214c07d79a01e86e06a1265bbc8a499355fda2ab7fbea0d2ca641c86bee4e1ff6a3426cf06617ad9cb007d21eb492ea0b529d9e3b2bf1e5a21007fa398ed6356aa8c0f9a8fb5850c2d9ff388eb4a640bb0c193fcc9e77ec515fd43176ada0613dc3a7ef780b718a0f22869e60ef74e83bba1a97b4d3ea10b80f58e5ce051654e25f4bca09ad97fe905bbc05f494b729da5d35a458cb00724dd82893d8b1cec748b45db2b4aa992ae63e44cf07ccc2f4cdd708d1b8fe86d14aadf3d42c5b444e16c1e5308aa22d3d2dba22b52159e4818235231b81ffc39e172776a4fb3908db6b60ff04959c00a7fa5910428145c0650fd3eba26e815c1dfcb98c73a173bfb5889519f8f2dcf4f7fd8e0b98c92e859fee347b522bf49fc0fe9f49d154c2c64c789b906da15661b8def837eec9592f0c2cd5c40ba7019b9bdd3566768007840d3d2317f1f42b96480eb3639a87f1a3e732838005809af18713ec337ba555c2d06b3baec1bca8d7d23825efcf7d65b8e90b2bfdb18295e0fb0b247e54e39d560fab36210bc86bcee9ef9fe822eb60b83560311a558a5e0363a83052964dec650258032818dcaf33ee572848fc5bfe7ddd5ff397af516017660eedb706876eff1561d81950dc5ff0aa13d8ebebc1d68c0d510ed11b27743c7dc8899f871781fbcfec5da4432d0a9ff4f137a808ac1a138b351f7da51a44259bec96183ea54deac1d83124bfe28182143d8e13ff17bea6762f51e53ca690af257eb61c26c549eb4db7e3e3efdfc267d6822d23c329b1d2ff4f7bab5ffd6a4cfdf6ae852ab6f1a129d8322f0b74ca6a6a1f4fce5508095de2f265159b907c8619597adbfa4ae97f15fc030109a59f21e1f12651ef3459ef0820455656bd96b5f47879ee4bbdbb75af8c4d88ee179a437dab12f68d197677516238d46c27ca4160f9f79a11b1c1a612ee313ab350d2aed64da768d14ae43d201e6142e4b7548b853e2b69cfa9fc5fab9107e31faad2ea2688958c27e5fc573adf8c7fce24b660b8b97cceb9f0d82b6e5cacd8eab8bf72dcc9849f942bdebbc62d12020d1e2d8cddcd29cb80ba6ad16c3f92d2225fa36f1bda3165100b7832b898d102ddbe6962dcc22162304a5bfcbd4804927fded3cdccefa16b4d80b9db6d72eaff75df93508f0df8d21efc329d4e503e8040ec0e12375716e106707756a10dc3e48d9cc0e857239bb36fc8b7bee6880c5e0868856483a755039528c0895", &(0x7f0000000140)=0x401, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
setrlimit(0x8, &(0x7f0000000080))
recvmsg(r0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)=""/88, 0x58}, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000040), 0x1)
r0 = socket$unix(0x1, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}, {r1, 0x1}], 0x2, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x12}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f00000000c0), 0x180a2, 0x0)
pwritev(r0, &(0x7f0000000440)=[{&(0x7f0000000000)='MW', 0x2}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [0xffffffbfffffffff, 0x0, 0x0, 0x102]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x5}, {0x25}, {0x6, 0x0, 0x0, 0xbb4}]})
pwrite(r0, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
setreuid(0x0, 0xee01)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x140}})
shmctl$IPC_SET(r0, 0x2, &(0x7f0000000100)={{}, 0x0, 0x0, 0xffffffffffffffff})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x34, 0x0, 0x0, 0x100000}, {0x80}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xfffffffd, "080486179c7d9234997b52006000000700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000003c0)="4491125e055171c5077f329557a30004856f276a74bb36778525298af8f7e8245d661132a4474bb2185340f8a3116927e7776de444c14b4233243a47aa8c7779222189299257a1a295db6954f15fcbf00ec4ec9a0f2212a452aa4bceae373d8c2107b4e185e783a97e180dc9e7d921b6722798dca9", 0x75}], 0x1)
openat(0xffffffffffffff9c, &(0x7f0000000200)='./file0\x00', 0x1, 0x1c4)
r0 = semget$private(0x0, 0x2, 0x6025e3252db99dcb)
semop(r0, &(0x7f0000000000)=[{0x1, 0x9, 0x1000}, {0x1, 0xc2, 0x1000}, {0x0, 0x2}, {0x0, 0x6, 0x1800}], 0x4)
sysctl$hw(&(0x7f00000003c0)={0x6, 0xb}, 0x3, &(0x7f0000000300)="fb493523175312ca07fb0fa5ce7c9dbbf391579916e1ff4f2eb0881507c28eefa8454d674469ffb3a8cbe5f706c9a2b405a8dbd15442ab293c99bf1c15cedcbc6a22d7a750d770e3c68652e148218c970089af462e8815a742f3f1a5cec31580db121b3bd209dcd2178ae659ca35a650093797966f6eb1805ef225316a326fb620a206b7a28a5f4d6a61c0e80aed814d6c5b", &(0x7f00000004c0)=0x92, &(0x7f0000000740)="8e86c4340c3ed368fe4fd73e902a99fd85373c42573271557489c604101d64f744a1a3ceb2341f13a3bc390f6b2dc2cb6f2e05e626df2e7f49d9a96e3787e0383d8a589bfd5da62a67d11ccaafc477178d8d6b5c54559f01b34a677785cb51db1d479919ce6ca0a19ed99bfe35edb6a2f0aa4095a4d8763923cfb9e7eadb28b68a7c1b5c03208e346eb4046c6ae29f5b02c67b47ca2a5fcdaed0e87247d9a6444b59c48fddf3431d7a00000000000000000000000000000000bae14f3343bec3e7f8fec19f07dab6de78a439d2c5e900766afc0eb4a4deea923c057acbd1287aeab251aad55cba0722f49ddd6c2c02264266ad9f8b3d87b3821b69c5ed1d78986a48e8aba9eb3e7f3dbca4e595f7da9586b99ee032b4619b14a2ae0d1925a31d3b527c14fdfc788eb38af0d8fd2c54ecac78195fef9ffb7538", 0x139)
r1 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r1, &(0x7f0000000480), 0xe)
semctl$GETVAL(r1, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r1, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r1, &(0x7f0000000280), 0x0)
semop(r1, &(0x7f0000000180)=[{0x1, 0xb9a}, {0x4, 0xef1f, 0x1000}, {0x4, 0xfe3, 0x1800}], 0x3)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40}, 0x40, 0xfff, 0x9})
semop(r1, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x2, 0xc2f, 0x1000}, {0x2, 0x96e, 0x1800}, {0x0, 0x7}], 0x4)
semop(r1, &(0x7f0000000040), 0x0)
semop(r1, &(0x7f0000000040)=[{0x2, 0x0, 0x400}, {0x1, 0x21, 0x1000}, {0x2, 0x4, 0x1800}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(r1, &(0x7f0000000080)=[{0x2, 0x1000, 0x800}, {0x2, 0x6, 0x1000}, {0x4, 0x5}, {0x4, 0xfffb, 0x800}, {0x1, 0x4, 0x1000}, {0x1, 0x3, 0x800}, {0x3, 0x5, 0x1000}, {0x0, 0x7, 0x800}, {0x2, 0x7, 0x800}, {0x2, 0x3e0}], 0xa)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000400)={{0xf08, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x100, 0x156f}, 0x3, 0x395d6c3e, 0x6})
semop(r1, &(0x7f00000000c0)=[{0x4, 0x3}, {0x4, 0x0, 0x1000}, {0x2, 0x973}, {0x2, 0x0, 0x1000}], 0x4)
semop(0x0, &(0x7f0000000240), 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0x1}, 0x2, &(0x7f0000000080)="d9dcb8445801e8103dbea3c84af67b2c4da1ea4650c9a594b8d396b8e3463a82815ade1e23720bbff0974de18ccfa9904edf069405ed00b91f6c474110c4ae4b358e59a8e070edf75d168fdba998c0438cda7c039dd2d137fa9f5ff9a7e687fe5464d9d74125c93df940e794dd96ef569bc0321459ff603bc72e3511100110d354a672873b5da79acd61f625bc687b27c8ff351bfecc8dff4953055f3b28e4bb3bc86f97ae651fb62e2f57a294a116016e8943edb53f053424cd3e5786a661990084eddd7b153613abf8b9a57c66052c27c2074dd906d75d166d889e0a5538d49d7457c4698b7926b2164842cb74ff7ca77c8b890451956128ff3f7a7a3f32", &(0x7f0000000180)=0xff, &(0x7f00000001c0)="526f1297a5a06d8f19192ccc866a4ad53fc4f696672ff8f6", 0x18)
semop(r0, &(0x7f0000000280)=[{0x3, 0x4, 0x1800}, {0x1, 0x1, 0x1800}, {0x2, 0x4, 0x800}, {0x1, 0x8001, 0x1400}, {0x1, 0x6, 0x1800}, {0x4, 0x0, 0x1000}], 0x6)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
readv(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f000061e000)=""/4096, 0xfc74}], 0x19b)
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff})
writev(r0, &(0x7f0000000000)=[{&(0x7f00000000c0)="e20b", 0x2}], 0x3f)
munmap(&(0x7f0000607000/0x400000)=nil, 0x400000)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x200, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$WSMOUSEIO_SCALIBCOORDS(r0, 0xc008441e, &(0x7f00000000c0)={0xc6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
setuid(0xffffffffffffffff)
truncate(&(0x7f0000000200)='./file0\x00', 0x0)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x66, &(0x7f0000001500)="0000ed0cd9012a605ae6b707c58208b8", 0x10)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
r1 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
poll(&(0x7f0000000100)=[{r1, 0x4}], 0x1, 0x0)
r0 = syz_open_pts()
syz_open_pts()
ioctl$TIOCSBRK(0xffffffffffffffff, 0x2000747b)
ioctl$FIONREAD(r0, 0x4004667f, 0x0)
ioctl$TIOCMBIS(0xffffffffffffffff, 0x8004746c, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000040), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = socket(0x18, 0x1, 0x0)
setsockopt(r2, 0x29, 0x32, 0x0, 0x0)
dup2(r2, r1)
r3 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8000, 0x2)
poll(&(0x7f0000000040)=[{r0, 0x2}, {r2, 0x100}, {r0, 0x20}, {r3, 0x20}, {r0, 0x10}], 0x5, 0x1ff)
r4 = dup(r0)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0, 0x2]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r5 = socket(0x18, 0x2, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r4)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = socket$unix(0x1, 0x1, 0x0)
shutdown(r0, 0x1)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x2}})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1000300010005})
r0 = shmget(0x0, 0x2000, 0x0, &(0x7f0000ffc000/0x2000)=nil)
shmctl$IPC_RMID(r0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x8000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x800, 0x80)
r1 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffd000/0x1000)=nil)
shmctl$IPC_STAT(r1, 0x2, 0xffffffffffffffff)
getgroups(0x3, &(0x7f0000000100)=[0xffffffffffffffff, 0xffffffffffffffff, 0x0])
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, &(0x7f0000000100)=0x1)
setuid(r3)
r4 = semget$private(0x0, 0x2, 0x189)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={0x0, 0x0, <r5=>0x0}, &(0x7f0000000340)=0xc)
semop(r4, &(0x7f00000002c0), 0x0)
r6 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r6, &(0x7f0000000480), 0xe)
semctl$GETVAL(r6, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r6, 0x4, 0x7, &(0x7f0000000000)=""/59)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000480)={{0x401}, 0x0, 0x0, 0x2})
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000380)={{0x20010007, r3, r5, 0xffffffffffffffff, 0x0, 0x100010024, 0x5}, 0xc8a, 0x0, 0xfffbfffffffffffb})
getpid()
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400000, 0x0, "000000dbddb97b00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
syz_emit_ethernet(0x68, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @remote={0xac, 0x14, 0x0}, {[@rr={0x7, 0x2}]}}, @udp={{0x3, 0x3, 0x8}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x4000000004], [], [], [], [{0x0, 0x0, 0x0, 0x7}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040", 0xe)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000000)=0xfffffffd)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0xa663263e)
acct(&(0x7f00000003c0)='./file0\x00')
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="a4027b3c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
listen(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0)=0x200000, 0x4)
r1 = socket(0x2, 0x1, 0x0)
connect$unix(r1, &(0x7f0000000000)=ANY=[], 0x10)
dup2(r1, r0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0xfffffffe, 0x0, {0x0, 0x1}})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x801)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSFLAGS(r0, 0x8004745c, &(0x7f0000000000))
r0 = kqueue()
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000000))
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x12}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x0, r0)
kevent(r1, &(0x7f0000000080)=[{{}, 0xfffffffffffffff9, 0x11, 0x0, 0x4}], 0x1, &(0x7f0000000240), 0x7ff, &(0x7f0000000300)={0x0, 0x8})
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x10199, 0x0)
write(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xffffff80, 0xffffdf7f, "f163b56028000001ed0804000000284600"})
write(r0, &(0x7f0000000440)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd739dbc5a893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653c0c3974b19abdd3895a51400b6cf4fcd9b89e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be680045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dc31c0dc31b3c0fd5ca7", 0xf2)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x0, 0x2}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x23}, {0x24}], 0x2})
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
open(&(0x7f0000000040)='./file1\x00', 0x200, 0x0)
r0 = open$dir(&(0x7f0000000140)='./file1\x00', 0x0, 0x0)
mmap(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0, 0x8810, r0, 0x0)
rename(&(0x7f0000000200)='./file0\x00', &(0x7f00000000c0)='./file1\x00')
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
r4 = accept$inet(r0, 0x0, 0x0)
write(r3, &(0x7f0000000040)="04", 0xff9a)
sendto$inet(r4, &(0x7f00000001c0)="1daad5cd36195d6810b318270ef9a4e226c2d78a5413faf736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8ad376f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbaeff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e7068a0d0a3e839ee05e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e13faeebbccb92c28d329fb8635224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70dfd265fa833f7f7788b351b9a0abf03d9e21a38de65ed7352c75d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc3462aeff27e94fe6994ffe7086d8f0c631b96880096d82665ddc95c8ed5e187c85fc07bee3f65d4b91d59436540aac4eff6f5cea6e4233587318755e8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc780bd319a6235b800800000000000000f525897bfdd75d1f2d5a302b3c4eefe2f5ace97cade03c418d91b5edd3d278cedcdd1e7d4b95b3a63cd9079888381a65a8789264cd8048410f29befde650c4fa5dcb582718b324bf28706d04a602d395a0ff0aafed57c5dc7a2d17ef96202dc44790cc423511a5c2c44c1d6a2cffbad9e62418bc25d106ac22145de479fafe620614d07d935a3ae6cd295d9ccca947a6f483c71d2e04af4a8ffd6a534d183eb7d39dd34770f37290a93d68882c6a8d220e5feb6940b4ea75e0f94abe6c4bb9e905bc86656f854a277e6907c4478d74d2cf08a56c02d6c9586397ccbfbc41e3789060018a3c7047ea96afc10d610015f6861ce8e55f4bb8a90a29627ac549e3b", 0xfdf4, 0x405, 0x0, 0xfffffffffffffd51)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
sysctl$hw(&(0x7f0000000100)={0x6, 0x9}, 0x2, &(0x7f0000000180)="30064c5bf00c80b0878de54877ba991f4ddcf143a9aeaa1f07a0f483fe5012c08f8597092cf65260c2ccc09f33f4c3e8a58c04080ce38c504ad0f4ddb03f418a74d7d8bd24c073b1a1a0acfdbd98eeeeaf9f36e530d5f14a3dbe182baf20100d1786ba2f57cf697286cefa1cfe19dc5a", &(0x7f0000000240)=0x70, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x36, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1000, &(0x7f0000000000)=0x8, 0x4)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0xfffffffffffffffe}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$inet(r1, &(0x7f00000000c0), &(0x7f0000000100)=0xc)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = open$dir(&(0x7f0000000380)='./file0\x00', 0x201, 0x0)
mmap(&(0x7f0000ddf000/0x1000)=nil, 0x1000, 0x1, 0x10, r0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000397ecf700100000000000000000200"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000080)="ebaf0900ed0e25baff6e", 0xa, 0x0)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, 0x0, 0x0, &(0x7f0000000000), 0x4)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120", 0x41}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r2, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x2, 0x2011, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
syz_emit_ethernet(0xce, &(0x7f0000001000)=ANY=[@ANYBLOB="ffffffffffff8035351522fa86dd603de6940098000000000000070000000000000000000000ff"])
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x8001, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0xc0105715, &(0x7f00000000c0)={0x54, &(0x7f0000000040)=[{}]})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000400)=[{0x64}, {0x20}, {0x6, 0x0, 0x0, 0xfffffffc}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
pipe(&(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000180)=0x6b65)
r0 = kqueue()
ioctl$FIOGETOWN(r0, 0x4004667b, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0xffffffff, 0x2948, 0xffffffad, "9190000100120000000092ff0000ebffffff00"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa", 0x479}], 0x2)
read(r0, &(0x7f0000000040)=""/35, 0x23)
writev(r0, &(0x7f0000000440)=[{&(0x7f0000000d40)="2abace18112030b738b1958303ac3fc1c384e67774d4bd49660d81fb4e2e9a19a972ea508fd17e3d0a932587a0980ddae58d2709a47c4986c8e74ed0d85edc5aca224a44bf11e4e1a51c0f08aab643577c975bf1928b38f6a67e0c5846b4128483e6275d201fec16e9147865784c167c704c6ae0980bb84e8dd18d6f4c83abaeed4ce23fa84033c78e9ce083ed4b575716841fd249fdb120f15b3db394bd37d3ecdf5440737d32fd4d75ae88ed351c1a2c8b9fe3a7773a6c3c322eec1e8da0864d79d9ca12a609b61fe0f6f8b88324f93cd6067a05d71c69804f02315e320887de9e7503e2645f1d9a453a0f770a109347ba8e025f6654809908b3d640309334db564b52daafcfe22cde8ad112ca5fe8d81a335656c43555aed4ef5af976b8a30d39838eb9162e58f82c048091b6f2a5c792c222520cba0fb1a3adab11c5adf10000000000000000000000000000e9bf0b9b675be513096c198ac5303b35c3a060a331662bf508cc3b6eacf17750ebe4976e7239251b8559a57039a7ad3a3063257fce807eb37d29aa2654046c62e343be2dff20fa332dc701ac84f3191977476cbcfc2bcc335332a1f8ea95fa9f440be83a78ebdd60dd81fbbf6fa326d6d84c9a12c84d037541b12aa9a2faccae86f50372af64db4ace9044ebc45d64e98a20133bb56a07434b3403ad089f99c949cd0efbff2e830cbb5146c58a27f83432998008556069bc469cba6d83bcb12f1f65365ec1ea5e25a6258f284da980074edc59dbc279cba0c634a2ab848edc44ff4b26b6f61b58ef1264e7d58d6cbe1b7fc6c914d69ca31a84b6eeed94229657d44bfb4e2e4b9797ef7f6af863f1c1c7f7ae9a45995936baf9d11f3bb2a5eed1ca573fb9df42aa20fc7eb1832c2cec4d048cfe862cc03524b4a0efb020cc7051e4d1efee1d972ff84f9fe5730459", 0x29c}, {&(0x7f0000000080)="051c92fc1af91e51429a03162acb8ddc3a8f9d3b588cc9073401143d1119b4b141df99877a601a4619010008a668199c3cb370", 0x33}, {&(0x7f00000001c0)="d19e767a41270df7efd1b0a722f3051b176adeda300f91598c78cb7aa9f36b6b570addb4ba0d744f7847", 0x2a}, {&(0x7f0000000340)="04ba5915c8ad15b694e75055b2095b4fbfd8cdbb516a28f8c07afa13c455f02b38f2054ea3cd3d71c76efcf20f67fa7efcff03212db9a210f79f62407645e16fa995ffd447da4e645056149176bb93a36a3dbd2b72f1f120eafe6cc5595c36d0a8180f2c0b4a00000000000049702812785cd889fb98b251c9e33841796c45624dc657de51b4039417a035060082b4321c842099b38fcbd25600000000008900"/170, 0xaa}, {&(0x7f00000010c0)="37e9fa0141061db61b1ed10fbcbc40cc1306795d552e7fd645b1c8b46d209d6ae2a5de39d5c69227234925c32c602ba5157794dc1dd2ddad84455ee91a95b2f0cf1ed9a285c75b0000897a04c9acfe9c11baf3865149814461b156130a7dc3d86b407414fd61a1ee8dc9076d494132173ea614d4ea3903c0560b7e8f2166eb8fd7c11b3e117d84cf4ffb4dba38f98304cfd173aed6296b5d71104c69396100450e6824c3fc19cf9b736eaa3e33ce3b82b81e27b25b92b6f1262ce8bf8ea5c5f8e6edeb595115de33ea233b8d78750fc209c9e7a94db407751f2cbf5f99b5649557393c3de37f1f302baeb0932f1edd688f50b1dc13f962ea86d03bcf0810de0da52ced341260965444ed62d151fa76d2d42c03ab7e0d3a88d7392a8179ca0c2db11ba6a340f9fd6a3e2821", 0x12b}, {&(0x7f00000002c0)="6598427136657df761aa5f5719147df674e9c995d0884d", 0x17}, {&(0x7f0000000300)="6dde29b7cb8fc8b7a15183fe17be78f2b212772de36bbea9efe0f5db50e300"/46, 0x2e}, {&(0x7f0000001200)="02f89c755da165fa2c5b12bbb9a6a7f01c4f715a5010d60e943461faf212f6c801b44db43cd4eae3440ebc20d8eaed033c5472a764c093e304422f60132ed5961f1656b310a45b2821029ce0eb4c53cf6eeec8c5d342bf4f9bc6992120db74fbd9f3dca2191fefbc3f50dede6f3bb218248cab8c2d7fd147e1e6274fcf5cc17f3b4f8bd7a7049d0efb8ba8e4571fbe8e02ab2f30a9540bd1e9e0bc8fb28c769265fbea98b5bae66521ad86676fbf3f262b8577dbf3f1c8cef8f4977f32bd8882f879d15e3dc0d66ae5b6275abad0ac8097485afea6d24c6e99d1e805c1fe4ad3ee5729213587ef7468d90afa43e6f91e1845618615d5f5fc434ee97fc7453bbd5744c5720a", 0x105}, {&(0x7f00000027c0)="d299f185fc18849566dc679478f0e005b6e1cb9789d657605107fdc0c92368f9a7f783f22b8f4fabef71b991e8a0b0cb588da78a836d11135e1d5bdf66ae22a7ec957dbc6fc7288044e9a54d9602006d108fac09", 0x54}], 0x9)
r0 = socket(0x2, 0x2, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000100)="529ac7ecc87315600a6543100f07c95df3287c82518c0461a37e5f30cd2e89292a66bd2d3cf7c41e0dd946a42f4a85799cc9f02756fb514a89579f01f401ffdd16017207f2a3d3c0b4d541dc3ece7e475b8a400c8a312464d18b87911540374731c15f2cb4bbb1563c17541a9658a656d3e478001cf220ceb66aa96f17d5386adaff35", 0x83}, {&(0x7f00000001c0)="52474183a9a2d7b4647d191e127b82407d98c3cd48b6ddf7", 0x18}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080), 0xe0)
open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
r0 = getppid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x40000110, r0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000001880), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc0107005, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000080)='./file0/file0\x00')
renameat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0/file0/..\x00', 0xffffffffffffff9c, &(0x7f0000000300)='./file0/file0/..\x00')
setpgid(0xffffffffffffffff, 0xffffffffffffffff)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
madvise(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x87}, {0x50}, {0x6, 0x0, 0x0, 0x99f2}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x3}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
msgget(0x1, 0x65a)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, 0x0, 0x0)
socketpair(0x1, 0x2, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000000)={0xc86d}, 0x8)
close(r0)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x100000000100}]}})
ioctl$FIOASYNC(r1, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x7c}, {0x3}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
setrlimit(0x8, &(0x7f0000000080)={0x4, 0x2d})
pipe2(0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000280)={0x0, 0x4})
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000200)=ANY=[])
r1 = open(&(0x7f0000000080)='./file0\x00', 0x612, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r1, 0x0)
read(r0, &(0x7f0000000040)=""/32, 0x20)
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000100))
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
close(r0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000005c0)=""/155)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x26}, 0x2, &(0x7f0000000200)="fedc3ff77c4517dd50075c9bc1ed8fa8e72d48029f6df57177b83c89b01141f52b7642eab8b84dee8bcfe0a335078a10bdb4eb", &(0x7f0000000280)=0x33, &(0x7f00000003c0)="0c93c293fd27b56aa91ad595cbb7860520dfd4232a2dd86673c0b5ce4963e38eb9e7813c5c519147af6bee94029d9a5f5e923d69ad2e33fb854f5bd91eb928d3b15b409f1c030319de2b42a71e8a54ee74d64567df36916aac7f8f28d39b29aa62ebe14f1ffa35", 0x67)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r1 = semget$private(0x0, 0x4, 0x1da)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000100)={{0x5, 0x0, 0x0, 0x0, 0x0, 0x1da, 0x7ff7}, 0x7, 0x3fe, 0x80})
semop(r1, &(0x7f0000000300)=[{0x4, 0x9}, {0x0, 0x1, 0x1800}, {0x2, 0xd, 0x1000}, {0x3, 0x77ff, 0x800}, {0x3, 0xcd, 0x1000}, {0x1, 0x5, 0x1000}], 0x6)
semop(r1, &(0x7f0000000240)=[{0x3, 0xffff, 0x2000}, {0x3, 0x7, 0x1800}, {0x0, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x0, 0x517, 0x800}, {0x2, 0x0, 0x1000}, {0x3, 0xffe}, {0x3, 0x9, 0x1000}, {0x3, 0xfe, 0xcf81320aaf5ac8b0}], 0x9)
semop(0x0, &(0x7f0000000140)=[{0x3, 0x101, 0x2a6c566928f6743}, {0x2, 0xfff8, 0x1000}, {0x1, 0x2, 0x800}, {0x1, 0x2}, {0x0, 0x100, 0x1000}], 0x159)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000180)=0x1)
ktrace(&(0x7f0000000380)='./bus\x00', 0x5, 0x18, 0xffffffffffffffff)
sysctl$kern(&(0x7f0000000480)={0x1, 0x17}, 0x2, &(0x7f0000000800)="103bb0a35d2771302a0e47ea4e96c803a0b684e7b6899bb7c0bcdcdb488ced4bfafbb5c291a3475540bd58b172cdb7e01a124bdb17a6772c9fa700adfa83728e45bd3a0766fda71c413c506f02aa799a540736e0df20813a64d133e25f88bc28418172139640f66cb338ccebb7da3369cdfdc6029eae49d0ff717484c99fe7ea94d484b23f7ca836ef0a8f4e70637514e369436c6f92906d139f66e807895651409df3a58b0c48d912049cb318b681af50a674dde53057e9694ef1c5c3a116d615a8e87d19615c82038023f0b00fd6e25f44f4b4645b92599d9b2071b1e6532f59d2ea0b5d60649a85c7a1bda4a3988068b9134f136d635594ab5055ab75a0b494101c7905e0c55c12877563c41fafaed9a7c83cd214ce9d692b825d802a6eabfff133ddc94d6f066f3de019d249399db92248035ac06fe69742052daa87e4e55a59f170e919f4265dff26a17b7593c7e838fdb39612dff7195d044f2e9c3241086d656a6f731e45057ce3711aaee86a5c80e152a3f1cd88474666df6d47fdb56d6100efe8577697f48a387d2d5cdf90e8c433c51cb736b31ff9ecd0845421f9ac178c20ccfa195c36376189cc5d84118741931efd67fc83944f0504381a0fcc69a534218f4bdc208d3f7672e4ca0108268e9bf97eb3fd58de5703c4b21e5d6f7783aa74831e85789485522574e7061b860e999f639db086b0d1b9ef45ec24cf3b30b41a6e7bae64a9ae919d46ccc93b80329481905c3a68a6986639ec3bc7e9761b42109dc3e01d0a902c9fdb0255c81df895538c24843d3e8da2a20a509d138e3213c88cd44397573c44f8266c0cd68179bed0972df0fc1e0d14b25e02f3a30298db5bda08430906614752e5f69fd2f88d551cf40b2bdf02e1572cfd08fae365124273f6eb470d469ff7d9ad252718c8b93c17956083163ff53832019feb254d22cb99c3c3d477ef7068ef7bfdee7500e3f86c404d347d2d691ac0118885c592fa55295ef4d4afbb603bbdde6c84ddc930a7d2cfd91cdb9201c43f12cbb596dcab41db7627e9ed636ea67428283340d8bbf01a06138c9918fa6e59aecb3846b979b425abac77c28a4c85d1a6c69b7576af0e994bc56418b29a74543e5ca3dee468b0aee2ecdf3de9ca881749c31e272e6ad0a66c2f1c7eb82b01de862f32af9a0703f3b4f1bf0a05df4a4745a2d106cda2812635f02ad842a891ef6ec620a0e79df2c4b05c8a03839470d8d20eb0cc4bcbd5ad70a9a5ac3589bec77d53a29fc32b6b1e6963129b1ce2fb2a7637a5b305ad159d3365d1de504ccca260920a8271b92a26308a86e0166696b4a89466d35d4bd562aca2bbedd5f4bf978bad3ef7e2664e67e05f933af911da356ffaf2e245734cf81b2b7d1bf52240a804dc21f23ec91f902a46701066cb7ff19cf15b9d5a0f715d92addb09e6503e2ac84a21e05865dcbc142070c2d079833ab632dcc0884c97b18da82f0b129051bdecb692041aabd09de527e6f257280d581f4065ca6c7f612f62e044bd95ec8f3b36a63395aad8de53c4f2a8b1030a5076c58c4bb8be34a66791647770dbbecf90346a720c3681bcefa2e454d386d345d8bc3701a0d27c1f8221ebc5309beace5d149af727e88ddb727150c6af3c62289acad31cbf97587ff743eea15028bf85067b134b33e51876a27177fc291a9444f20e6c688cfc2f8ac1542186a5ee2a18f4b69c38696c38a6b06befacc24e1c46a48d2b7f3784388242730b3f951c513dd83e6add58f488e642e46239c3bbedfbedce2cf4eaac7be1b4f0ad5db0069efd81908716a9bf26a934216625cb7a4f3fb927b6bf22c350d11c0a499d9d2beb3a95c23cf86ebadd75f6a66157eb6c4d4d2f2a71935c35c27da077010378f41b681f24d4d64175ad5ad8564ccda82b3b60b86369e70c519af2e567518bc1ab7164fe41fdd3043172595213082702bd61907656cfdfe9bc441b07b507fc076efa2884356016e508832a016f0029aefb2301172132c12591b83348a28ece7fda3e40aec9fd82b7c79b4c33e803f8a6307307caa94c1918d6673fac929c4f3ed1ac84b3b92e0d502aeff43b1dde2509395e03ea026769843542e61759119be24b0de4c4f830ca4363ef56eca685a75c78187cb310e41de20609261aa7b5408910878f5c80d9ae66a3d6a1123f778e74169c52a616f75b74f4948855d7dc54fabf3ae6ca6666ada28912b8b17c9d8397af0621aa90b51f3ee206d69021237d4d0dacf6510b4c63d1265452a8e7e8126a44730d051180723051e60bec853255b4f86a652802854a3f712d560705d7135c3a2fac1a301ebd5b51c824e6fa431439920e1f7c2ce9d4fa948de2002f9cf954307b5348c49a2a83656ab28de2b4bebe9b8b5e38be54c86bfe700bf83a4b3fd28a2bb4063b14b541eb6e02dd975f73d465ff033b341c594f999257e190e76fd612178440ebbbcfdfcc49721526e20fc0f307bc1784fa8e511110b4586720c0d84474a7af9715f0629be2e799db48d6d01bfd1ef8c65b1703fb7cac9891a3df5b1e547a9ec9e965ab962e98f633c51c7c9e2f9109424e61c8ad9b45f445849fa21606de73f5e69e32da0b542bbdbb329fe9ecdf5f146e0d13c6c848baf35be7383b099ac47e0036104d070843554598180941f49d8c27293156fb1b2c53aafdd00337238ddfc1a70e8f43496151a3753f4775c56f57288d7739c7becb48522e8086e03e49eebf4609bd122d59013315ed6f999b771c9adb9f30bf4209d9b05ce9fafb44c2a2a5611906f0567249c50cea43ee29f5030f19e6b345c116e6be613136f5765a0b14051594b59a0de88deeded13b9bbaa101a1d863f74be1154f910be4f51fdd4a451f2009455957e636a09b16852371ac1accb7fde816cf9dd4ef074323c7c744e073ed6b3a283e34e1e2b107a20834ccd639909a8a69b5d558ce56dba2199370abd3960a93b29e05e066eab0fbcad7a4c029012130f8fbb16518aab866a0f0433251131dede3ef7033e5f7c0ca651b9d2d66dae6ef1948bae4b9afb45ce70b95e79b1dc6df50f209a7a4bc0ae9028af48d63302048f5c6fed43b2446691f552476666f7e0f57e0eec59f3c2d7c324a9651916d5d10ba1541139e59607d9eacf88fcb2d863cacc83b2e64545460244647ece4d3465df85c4a5d214619f0085a14c16b6aafd82bbc59faf8f4405572a244df0f9aa277b5caa9e1e11e5924836054cc84064316842e95d30e2c96422ddd3212b11d90143e93a225082ac6ca8b5e86625bb82500c5ae2f10a95407465efce49e2f0d80c21cb926a4b8f5c88ce3f3ef9cb605d90c9f9098e419c25e97ba9e8efffce4a7bfeb19bfc5b8ab5f58ab61ec9422c5e408cad344883533a8d65e674b5b354c90ca8574934f8f034c3b682f0403d8fc087ee54d0dd76158de4565295af6af48b90bd39577a84abbc2e95df87092bf3e3c7573b2823174251025c933c3318a2d38c6c94b3cd6fdda45c840b04bcc62de746653105ada2ba79b551b2ad421c02128a169ff7238ff678c77ee0408eefa741690be4900bf9d69bcdf5f6082a139a4316ea3730e887b7ed57f9f60628be9ccacd0d0b7febfcf51b7f49ca0fa306cb5c6861f5ddef2bd0828db05d082932bb70ee6878759af104c18f0c08e6e9f490ffee716ab6d9142df4a7da16911e9b3c4c21bd0044615828930644684f5917419fd25eeb5df29f0fdb87e7404e4ae1f052a0016d1505f61cf3082c9587bb94b20adfad867a7338f78576f9ea347bf49a086b808ba56760097a5556f35053d9373877234baa6dc2e3cfaf8d9d6d52d8cbadc6baad876dd93fab78cc001b89a93e16f347025179adc2c8bb1160c661445a81a1bb6397f21cebea5abe3deddb1a749cd8eae0ef41489e0048260b0126f3f8701799472a717f509518e02e2916c1f1abc0b56bc1e3be3e3d3db3483d7e910b7b3894f9239aec4a9fa07c935b2f4f847dd67e512ca9c1830ce56a0942fc55df2f2734e5b8c424711694d214cbc676672140642d1e57bf62a3c214ad450d90af8e729a36fa665868234bc1fe5492183553b78e57ae62734dcaeb4c49b517998b72f81538488ee568740e65ba71796ccaf1a6893b9f758892899bbe150bad672712073a90778508896bfb56c824176cdca63a2712f5e0f6bc92389485fd2bc436d802702cd9fbf9f8dd3b59767d6d1113479c885699e2f88f25362eead95db5f78ea3a08b4ad4dce8403ea1861689b61d572a0904a890e241bf90edae8e2b6aeb0c7b3dbbce6566c672669acbaa7c2b8f893d40f0b1bf1d2b7f2af86737551aa5b8f2fbed9ed9261f3099c0ff8b56117487e7cd48b68368809723bb8140214d26cae307740edf644b485e98074b6f0e786935b73c1948baad38957512a3ab7db7c02d250429082e4b2099b81cd75897d3b678b9db1f722e35b01d41c827033ee30250b2fe339b578ecd04b54bfcbbd2f5e3d4fde317646f67e6245b6de3a434dda96a1f0ed6807a4a1195a4a4fef03e67058361e1799bb2fd213601f7e6cf43b90e533a7b844e0989c2d6a0e3df146d640936edce2f790bc570b48ba7b15f5d73c348f9ad5c900feb9751dba6a67e1fb0d73c2a2afe83cc525f4a18d5f21b23ce78e7b5ae7c08aa5ca706844e02576293523fc6e829814926660ff4082e7347c95643dad86e9afec8944b3b03f0d093219900055d674cabfeda51fa9750dfa0a326434b653ad37f52eb4551a95c6a95da0fb5d3f6c5476d74837e69993e4f677e8ff27ce1ad88a95fc628c39e3975ad19788598f6e7e7366b88d3e1f58b6864030c5b71dd660bccbde63cb09cf76687acb970b1efa8052e5464271e0848428af21d28cab6a74bd5860cb84917345a1b562e1d973d5685e31e1c3632e9a29f9fccd9afca6c57c1f3d648f3346699fef4a7609cbad8edf78bd4751f2055fb789fe06c1e6f364d04d17fd3e67efd112e1a5f1a4c848912e53289874576d7440d9531c801cca1a0070d385db6024cfd6ef7ca08a00aab8f62c53c953b8c8169c69e3a95c26ca0e4137a1661cf7671653f968f55de9a74bb572a13fec3361461b6d811d5498b50223424ec3d7bd8111106fd07cd2f47f3f62d252ee3c8f3dfc4b745a2554d71925c2c0defdb2e3a140559dee0abd0e33e90cab4288abe9ffaf3542b661c747327d01e0676f549c37d4bacecfbeac9943860de8c53d191be00ce349d529fc0f4c0cf1c4066b2fc3e697746cae437c454d40f61f91e1b71daecc2a201eb835311c03584c103b7c4430a423a635635837ae9ee7607a3f39acb5b79d8d20708239afe74357648d93a5b1d11ce6e83c56a01869abc7cb7942f5452a155fbc3b9394b5b199d2f541f2bea75d77274aac539f3363d53043e7e6fadf90c3118062d432b8ef998fc9813461f6b95d73a977183908c19efe8fadaed6737c0c2b359c85f8bf714bb42b175da69dc928177af4dff6489e3fe9684264651da2c34d565ff82cdb3fc9089cc257806f637275aa7d22c259b13a55273ffcf2668975d23253486a941c98f935873b5b290db8675c46621c1da647c0cd0c8efd1145cfc50033f362edf0bd02177a9333b1a6b641e9d1863b7ca182c80d3d52c956cb8deb55ae6b4b6026ca8b1bf36df1014626fcb7a611c4a09b77be059d799c9de72a64d6fa1a760e0e5ab33edd0a437b2b201758b87ca0332f39299ea6ff732936facb08bac8020b8d27032897fdd4688bcbcbc29e15066a1d0fcbb3b8a52159d05673f4e4a8f964b0821394d21a2d647e800e8789", &(0x7f00000004c0)=0x1000, &(0x7f0000000500)="6856d61a733ac0ad425b796b5e0b9ed90eb5fbcd149ea8855eb11cda65ce89edc4744da06eca1bdea27b33fa11aa10480e136e43a38934f39240eee15fa2e5d26c1cd1f07b1f970ccc561e07a3ecd19be9dcbb43366309b983ddc8903cf0b54a8e3a3921ba9da2447a3984b0df171e41e99684a5ca0f0f22b49f41d22872a250c20ff05309d130fd1b10663ea89f613f6261c6bf21f30a", 0x97)
r2 = semget$private(0x0, 0x4, 0x1b2)
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(r2, 0x3, 0x8, &(0x7f0000000280)=0x80008)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r1 = dup2(r0, r0)
ioctl$WSMOUSEIO_SETPARAMS(r1, 0x80105727, &(0x7f0000000140)={&(0x7f0000000100)=[{0x25}], 0x2})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r1, 0xc0104451, &(0x7f0000000140)={0x80007, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$getown(r0, 0x5)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f0000000080)=[{}, {0x7}], 0x2})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000280)="b2770fee66a10dbb591a8d0302f9", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240)=0x9)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f0000000180)=0xc)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000040), 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x80}, {0x2}, {0x6, 0x0, 0x0, 0x8000000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = dup(r1)
r3 = fcntl$dupfd(r0, 0x0, r2)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r4, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r4, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x1d}, {0x6c}, {0x6, 0x0, 0x0, 0x588}]})
pwrite(r4, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r3)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = kqueue()
r1 = open(&(0x7f0000000040)='./file0\x00', 0x800, 0x2)
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x8000, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000100), 0x8, 0x0)
kevent(r1, &(0x7f0000000400)=[{{}, 0xfffffffffffffffa, 0xe0, 0x2, 0x7ff, 0x1}], 0xc, &(0x7f0000000140), 0x7fffffff, &(0x7f0000000200)={0x2, 0x81})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={<r2=>0xffffffffffffffff})
ioctl$TIOCSETD(0xffffffffffffffff, 0x8004741b, &(0x7f0000000100)=0x9)
r3 = socket(0x18, 0x1, 0x0)
setsockopt(r3, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r3, 0x29, 0x80000000000000c, 0x0, 0x0)
setsockopt$sock_timeval(0xffffffffffffffff, 0xffff, 0x1005, &(0x7f0000000140)={0x8, 0x2}, 0x10)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000240), 0x20, 0x0)
r4 = socket(0x18, 0x1, 0x0)
setsockopt(r4, 0x29, 0x80000000000000c, 0x0, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f0000001340), 0x18, 0x0)
kevent(r0, &(0x7f0000000000)=[{{r2}, 0xffffffffffffffff, 0x31, 0xfffff, 0x0, 0x1}], 0x1, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x33, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
fchown(r0, 0x0, 0xffffffffffffffff)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x3ffffffffffe})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x37, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
write(r1, &(0x7f0000000440)="cee7dd31350100000082b3e4583e38389ae7f5b13217fdc6ecb41a9ad780cb8f6f2fb233f50000a904adb7720925014011812e861a3775ecb57bcb511850bdd965d05183a5e46b899e8d3500880afc345b2397ab7515a2239b8ab47b643552611656444a03a76d7c8eb061cc505b9f5db1b48352586d864242aa90e9a3f68d0400000000000000c9d9844d05dbbea0d0a1a4ff00c0f21df3cfdea135c203dee1510431f4bd8b8b47484231e97efab054cdece30d3cd717a69e4b42a06aeef6721d017cd8f3bf471b9de8be66d733de514ba0457e5fa6064255b7b7d0b7d479ccd627899f3106000000000000004761dc89a3aafcc7f644e777cca264e56aa483d8a2f6be24fe998bae642412f893d0665a72dd7886f953d5e0df04491b42d71303fabcb33898000000000000fc74f2dcabeafc655544deaf91eaaa2b895003c834f67c4399fafd0513a2a57e70d7742651be2b9800000000000000000000000000b0e6f2457079ad381a1bfb5a1d738635", 0x171)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000009c0)='O1', 0x2}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000300)="006b6bed3f0471170b607e2b6858164bf0817181e010963bf0fecef159acab9b36dcd01f3ac750f5c6dfec9d51484225f12c688f2a2f61068c7eca0830140ccbdcbe1735b13e629a45cd9390b95f7c16ae1d189b3ea7a0dc58a57959a8e3ce9bda49a2ba4e5d6ae5a1eff82705c24baf06ec92437371c41aa3d496859d427d32b0332462d0a09df4c8996c9f2a4db3728f", 0x91, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0x0, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0xb5}, {0x0}, {0x0}, {0x0, 0xffffff6c}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x842)
sendto(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x11}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000040)={0x0, 0x53})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
kqueue()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
kqueue()
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040))
pipe2(&(0x7f0000001300), 0x0)
pipe2(&(0x7f0000000080)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
close(r1)
fcntl$dupfd(r0, 0x0, r2)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000100)})
r0 = socket(0x2, 0x1, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r2 = dup(r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
pwritev(0xffffffffffffffff, &(0x7f0000000580)=[{&(0x7f0000000000)="9736753ac3644e1b7a4cdcb62ab94019ce543cb5c5307d5f42bce8213a6aacbf5d2736f6ea4e056c6ea3cad4e70a1660839cb436b4744a21c408caffde9973e5bb8a82b1e6da858dc37b49632874ac80190a6ffc40dadf783aa9ecb7292b6790ca6d554678eaf34238571594d1009a11652d02fd712c0ab923567b3722261b30ff6229a2b5f28496843ba7197a5c35e5a70ef70b584ad1b321a13ac07bc8d8e0cc50aff56b7d64e7f45f1f4fa1dc23d361ff4ce96298595bc107a4ed615a8a4b2765daf034e5dcc2cb28fbf6913566", 0xcf}, {&(0x7f0000000100)="2d26d52983f2176b1ec1730b58ff0ba3c5565e67491e0d1bbb023e576e7b33e763393f5b0edc9c0e05069c29235723721dd61ee98dd481798af9a18cdfbf3c8acad3d93f884bb7a0aba5b6c65322bf16f71027928b9d0e6f5d7958f43129ed0fd43550078500a04f490f92322ee9e688c77ab841d810bb16e7209cde9de3768567a3", 0x82}, {&(0x7f0000000340)="606b10a3b8a00b20cc8983fae588d8dae5a4a3141bac8c6266cb7854b382eaa07189373138411bbad6e7a4e95ce7e12369efc94a691fe91da39658d34ffdde87a4470f5f5a27814ffa95e3fd53a66d4c6f26d22fec0e7807c69be068c11f11ed3a89cf3975cfda4b473c63a7da519812915b99a76f204ac10bda12e802d89cdca52c58da961bdead9b8cc27260240847ba229b73b147676e3de3ca275d60a5223308a4b81fa384620f2c98911eac8248cbb00c7584a025621f1db7db9a72e8fc6c7e424057458c082c873c4605"}, {&(0x7f0000000440)="763aba8a93200ee55d50ca673eee0d7eee886d87"}, {&(0x7f0000000480)="ffcfae3419ecbf2a07a0ded4d5221a28dfbde1bec68964064adf617779b02e8add6f23fb11d7232f3ed3cdd9b46e8c8716372d0c1fae1b3d4d250668acdf1a8a25d54b8603d76469c426ff4db27a9c698f675701ae585827bb02e12a7236204815853460c5299675c87be67f6775eff981ea0bfa100051179a57284929ab24309f23f79b439264d40472d00d0be08e4936802de4729ee5aae2e3447d3be22f9c1299eca66297f8431a408d21b8fae6bcbbd16fedc2be4987a0a116e3a2179ea8c1f8890b2f014d948bfff6d3cdb31634b9c9ec121bbf4481abccbec87894451430ba1e295ba0f1d050458a8c5dfb5a83e477a7ba24a9"}], 0x111a, 0xcb)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x800009, &(0x7f00000000c0), 0x400003, 0x0)
madvise(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x6)
r0 = socket$inet(0x2, 0x3, 0x0)
recvmmsg(r0, &(0x7f0000000200)={&(0x7f0000000340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)=""/9, 0x9}}, 0x10, 0x0, &(0x7f0000000240))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x5, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
close(r0)
sysctl$kern(&(0x7f0000001180)={0x1, 0x32}, 0x2, &(0x7f00000011c0)="61b8e55b", &(0x7f0000001280)=0x4, &(0x7f00000012c0)="6cc72a95", 0x4)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f00000001c0)=ANY=[@ANYBLOB="fb182e0b3d9a090000000000000043"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x9, "0018c000ffffffff000000002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
seteuid(0xffffffffffffffff)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x80384601, &(0x7f0000000240)={0x0, 0x0, 0x0})
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x450a)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
close(r0)
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000080)=[{0x0, 0x100}], 0x1})
r0 = kqueue()
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105728, &(0x7f0000000040)={&(0x7f0000000000)=[{}, {}, {}, {}, {}, {0x0, 0xffff8001}], 0x6})
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x2, 0x0, 0x100000002})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x2, 0x2, 0x0, 0x2000300000000})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x2, 0x2, 0xfffffffffffffffc, 0x2000300000000})
open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
lstat(&(0x7f00000000c0)='./file0\x00', &(0x7f0000001500))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x7}, {0x1d}, {0x6, 0x0, 0x0, 0x1ff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x66, &(0x7f0000000000)="ff3b04b0ee2e0013daf9db18617c9c00", 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x80}, {0x7c}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@remote, @broadcast})
sysctl$kern(&(0x7f0000000140)={0x1, 0x9}, 0x2, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000400)={0x0, <r3=>0x0}, &(0x7f0000000440)=0xc)
setreuid(0x0, r3)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1}, {0x6, 0x0, 0x0, 0x4a21}]})
pipe(&(0x7f0000000000))
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x9}, {0x80}, {0x3c}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@local, @random="9ba8ed10ab90"})
getgroups(0x19, 0xfffffffffffffffe)
open$dir(&(0x7f0000000100)='./file0\x00', 0x3fe, 0x68)
faccessat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x5, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x5)
sysctl$vfs_fuse(&(0x7f0000000000)={0xa, 0xd}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x8, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x8000, 0x4)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0xfffffffffffffffe}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$inet(r0, 0x0, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000100)=[{0x4c}, {0x14}, {0x6, 0x0, 0x0, 0xd7}]})
pwrite(r0, &(0x7f0000000000)="53ec371a410a3035029a0e000040", 0xe, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x21, &(0x7f0000000040)="b1f5d915", 0x4)
r2 = dup2(r1, r0)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/39, 0x27}, 0xc42)
setsockopt$sock_int(r2, 0xffff, 0x800, &(0x7f00000001c0)=0xffffffff, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x8002, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
r4 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r4, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r4, 0x0)
write(r3, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, 0x0)
ioctl$VMM_IOC_INTR(r0, 0x20004402, 0x0)
r0 = kqueue()
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$FIOASYNC(r3, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000b40)={&(0x7f0000000000)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f0000000740)=[{&(0x7f0000000b80)="56733e941394b22c0f3011841d7f31e644d47ce96c7465c9f2076c579eafeefb0b8b838ba104f4d4dbc1c358ef6137cacdee91dd8227d0a59470f56fcb4a272f1d10ae7f952466801a7b165d114376b6c95c7a03f02f0cf549f2f96a6db9478c5749edd4f2faad925ab3f89638506fe7e1599f53208a203ec869310e7e29817e58f7498b110f5a2318e3b5b068a1abe1d383e94764261d08dc6cdc1030ffde3418343d869bf88c1fbbca1a8a22c94b75f0791b661d10f15fad53cee0066018cff7f1d3b7494bd1ea80622238907f4964719e226109ea94d3a06ed28273596a949c16affef2b79c04f4d612aee06896d491cc157e3f90486a2388ddb09d07beb0ca3408ac60251a3175f57c7e7449101e8861acf7886f2c9c9a028e3b2b8baf0c3e1d03b79e952526892b8da513b1e720771c88d227f47760f1fddad75b8592ca7904c0284021d04c1e0dda3c49ab704b6d3b6e0e9a2064b946cbab7a69226b5116bb8158a2bec8e04b7d8b64fdcbf7ae094af8a2b762b4", 0x177}], 0x1}, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x3)
r0 = kqueue()
r1 = syz_open_pts()
kevent(r0, 0x0, 0x0, &(0x7f00000001c0)=[{{r1}, 0xfffffffffffffffe, 0xd3}], 0x1, 0x0)
kevent(r0, &(0x7f0000000100), 0x7b, 0x0, 0x82, 0x0)
r0 = open$dir(&(0x7f0000000040)='./file1\x00', 0x10bc2, 0x0)
unlink(&(0x7f0000000000)='./file1\x00')
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000080)="e7", 0x1}], 0x1)
mmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
close(r0)
mmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
setreuid(0x0, 0xee01)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100)=0x1)
setpgid(0x0, r1)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d733c4f6ff45e400"})
write(r0, &(0x7f0000000180)='X', 0x1)
close(r0)
syz_open_pts()
r1 = syz_open_pts()
fcntl$setstatus(r1, 0x4, 0xcc)
write(r1, 0x0, 0x0)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000180)="9b17cd4c04dfe4c273de28bca3a710f63e3f86015c254fabe2cfece177330cd6b4c940371b9d18dce36f9be2e9e8e3b2491fb7ae1b9a7e9b09ff5799c20170fe81484dc182fc7af92d6033aa24d6d3fcb28c8c68db9e55bc33d35ea681e90be283138860885ff610c03c571d53b43836fbaf96565daf10bbd13385765feb370048d7fd4fe670de4677f8a52c97e23a0d968bde0b51565fb3922397cb28153a929aab002cb8b77ceb84d8c16133803a9db078898a007559d6cd24f1b86bb838c0001f7ca7e731d8063deae605f3cb447b33", 0xd1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97409000000a8f41e32a31811", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
sysctl$net_inet_carp(&(0x7f0000000000), 0x8, &(0x7f0000000040)="553780c7", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x0)
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x6}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x58}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000002c0)='.\x00', 0x0, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
symlinkat(&(0x7f0000000080)='/\x00', r1, &(0x7f00000000c0)='./file0\x00')
rmdir(&(0x7f0000000140)='./file0/file0\x00')
mkdir(&(0x7f0000000040)='./file0/file0\x00', 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000140)="5d7e5fc9725bd5ecccc70d9db5d6efd42b45999add61a1a6d5ba58850c64c392dcaf6f1d08f9abe535279c300797b944d99eb1e7b53ea1b581a62f714cdb34f6ff82f78f70603b8199d87d0f4ed27a94b8", 0x51, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000180), 0x14)
mknod(&(0x7f0000000600)='./bus\x00', 0x80002007, 0x40000519)
open$dir(&(0x7f0000000040)='./bus\x00', 0x8000, 0x0)
writev(0xffffffffffffffff, &(0x7f00000003c0)=[{&(0x7f0000000080)="5d526deb54a107166258db46566a6ccf9130ce4460f63c3cacca13e8fed3d5c5484f8b84fd67ecfbbf5b24fb53af33f8cb344e6f980361b7ac234c47c805589cc08a706a4669d0a4dfe62ef51ad60690209ccc46e281557b1658371f096e4322a3e9e5e7ab12405d17f0266800bb443fbda1b7aa575c8de05bb51b2413", 0x7d}], 0x1)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
ioctl$TIOCCONS(r2, 0x80047462, &(0x7f0000000100))
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x123)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000005c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000001340)={0x0, 0x0, &(0x7f0000001280)=[{&(0x7f00000000c0)="74d60dd4bed84803b9adcc79edc0d92adbd34db4655713571871ecb4ced775dcfb112b7d2316892cf7fdee35197dff5bdf7f4e4f2327159d68489facb14b038e792120e25269a839d9f1d699f7bdd8f7df15121b092db8baa5dec6cdacb953fda2ee3faca38e24847a04fa71dbd6908105b7c58b66a520e5ebaa1ca76db88e5c973137eeff5c733d746dd44155b68382f590e04904205d7f337fda085b90395d70caeb6cad2b24f7c433fc28d73aaf68597bdb796a7d73a16d242f91e2e74dbf7e143b506d950c6a6c0452ebf8faeee7d8ab9b9435d06ed502e8ad0a82c11859b014ad64121fd45629b179c65cd20cea798ddd7dab669f9332fbdea7e227dd6da62d1623261c3a4a2c4accefd1a2e60040fb0f3c9adc4ec02ce5be0b7e3cdbdb721fcfa94667f344b84413dbfb4bdc226964f3d93aaaac5b2d70135df3687cec7d434db404c98db6e1d731857564de9b6f7146f313e5564251dad8c9251e3dd298870937b97fcdc6f420cecdd44371b278", 0x171}], 0x1, &(0x7f0000001300)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
mlock(&(0x7f0000c00000/0x400000)=nil, 0x400000)
munlockall()
madvise(&(0x7f0000400000/0xc00000)=nil, 0xc00000, 0x6)
mprotect(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0)
munmap(&(0x7f0000c00000/0x400000)=nil, 0x400000)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f00000001c0)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000fff000/0x1000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000d2a000/0x2000)=nil, &(0x7f0000daf000/0x1000)=nil}, {&(0x7f0000d86000/0x4000)=nil, &(0x7f0000cf0000/0x4000)=nil}, {&(0x7f0000d85000/0x1000)=nil, &(0x7f0000ab4000/0x3000)=nil}, {&(0x7f0000f7a000/0x2000)=nil, &(0x7f00005d0000/0x2000)=nil}, {&(0x7f0000a0f000/0x1000)=nil, &(0x7f0000ee9000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f000093b000/0x4000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f00005b1000/0x2000)=nil}, {&(0x7f000048d000/0x4000)=nil, &(0x7f0000c99000/0x3000)=nil}, {&(0x7f000089c000/0x4000)=nil, &(0x7f00005b0000/0x4000)=nil}, {&(0x7f0000d26000/0x3000)=nil, &(0x7f0000926000/0x2000)=nil}, {&(0x7f0000d17000/0x2000)=nil, &(0x7f00005e5000/0x4000)=nil}, {&(0x7f00007b7000/0x2000)=nil, &(0x7f0000540000/0x2000)=nil}], ['./bus\x00', './file0\x00', './bus\x00', './bus\x00'], './bus\x00', './bus\x00', './bus\x00', ['./file', './bus\x00', './bus\x00', './bus\x00']})
mknod(&(0x7f0000000040)='./bus\x00', 0x2040, 0x4f4b)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0xc2c04200, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "04713de0af28a2813d8209b8d9f39321849e3c99"})
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VNDIOCSET(r1, 0xc0384600, &(0x7f0000000200)={&(0x7f0000000000)='./file1\x00', 0xb6f892b, 0x0})
r0 = msgget(0x1, 0x0)
msgrcv(r0, 0x0, 0x0, 0x0, 0x0)
msgctl$IPC_SET(r0, 0x1, 0x0)
mlock(&(0x7f0000003000/0x2000)=nil, 0x2000)
sysctl$kern(&(0x7f0000000280)={0x1, 0x55}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0xfd4a, 0x0, 0xfffffffffffffe97)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setreuid(0xee00, 0x0)
r1 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r2 = getuid()
fchown(r1, r2, 0xffffffffffffffff)
setreuid(0xee00, r2)
r3 = socket$unix(0x1, 0x5, 0x0)
dup2(r3, r0)
bind(r0, &(0x7f0000000200)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
listen(r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r4=>0xffffffffffffffff, <r5=>0xffffffffffffffff})
r6 = dup2(r5, r4)
connect$unix(r6, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
sysctl$net_mpls(&(0x7f0000000040)={0x4, 0x12}, 0x3, &(0x7f0000000080), 0x0, 0x0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
renameat(r0, &(0x7f00000001c0)='.\x00', 0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00')
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xd, &(0x7f0000000000)="e8800000a4b2a6f304000000", 0xc)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r1 = dup2(r0, r0)
ioctl$WSMOUSEIO_SETPARAMS(r1, 0x80105727, &(0x7f0000000140)={&(0x7f0000000040)=[{}, {0x5}], 0x2})
sysctl$kern(&(0x7f00000000c0)={0x1, 0x4f}, 0x3, 0x0, 0x0, &(0x7f0000000100)="bc374d73f9ea758118f35f5e1e082364163d6b09e4ad5897e76fb7f29eb4988125f3092207a4f4b98c8ae436120e325664eb007272024b3cbfd28a5e2df6eb709736f428d89bafbd068b22be53a424e395c0314078c96c914c2217f2fab67f526791e2228752258484d06e118f6279623aca6547c15cfe867e0dbc7da98ef7d94aa26cf97e65f0048e5b7ac3d3ea9480cc9947508fa533cc94e4d7d479bc2a4cad9ceb1ed445c97a1f5d13037757477cfdcfb66b03b7ab528043e090778cab3eeb73316d932cda9fcf8dc8b7cbf500b5f3010d718118a163b0ceb6efb76954efdb0473d3f666cc51bfaab76ff06e18845206f91dd5260ca88e8cfbf898b2dd34e6f75b029aaf3d83", 0x108)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x20}, {0x3d}, {0x4406}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
syz_emit_ethernet(0x3e, &(0x7f0000000180)={@remote, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "0fb07f", 0x8, 0x0, 0x0, @loopback, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@ndisc_rs}}}}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000200)="bd", 0x1}], 0x1)
recvmsg(r1, &(0x7f0000000640)={0x0, 0x0, &(0x7f0000000580)=[{&(0x7f0000000240)=""/237, 0xed}], 0x1, 0x0}, 0x40)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="4afc93274e165f78a99515c17ebbdbb1211a54279f1595a96076d2909341313fbf81950986b8560e6b77686100b01dd62b48cdc3349aefeb01f7effb558bf0450f1ae0c75b595e77b3eec05a3b680ad230d0b6cc269430307c7a7534288e846327a5d71360312f7bd40feef263df73dc17dae115aefde9f0246646fc039b9d0d4409000000d7964e6dc289eee0ce1b9293", 0x91}], 0x1)
write(r0, &(0x7f0000000340)="eb401e6ed7faf0ad65141eb19c4c8f7ca6d1edb238756dc92f4e962072dff5d1030cad2d579680411234054413aee3fd43dd56c65854f3012cae6d845e5b7e68251a41fd0f0258c3b642e7238733dcec27e66d5f40618e9864f4f7", 0x5b)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
setuid(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206918, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x60}, {}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f00000001c0)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @loopback}, @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0x48}, {0x24}, {0x6}]})
write(r0, &(0x7f00000001c0)="7c186c8002e709883de4f8260020", 0xe)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000080)={0x1, 0xc4})
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
open$dir(&(0x7f00000000c0)='./bus\x00', 0x800, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b1000502", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000040)=[{0x0}, {&(0x7f00000010c0)=""/4096, 0x1000}], 0x2)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}, 0x0, 0xfffffffdffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff})
getgroups(0x7, &(0x7f0000000080)=[0x0, 0xffffffffffffffff, 0x0, <r1=>0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff])
getgroups(0x1, &(0x7f0000000280)=[r1])
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000280)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be0060000003a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7004ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc450444d, &(0x7f0000000000))
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{r0}, 0xfffffffffffffff9, 0x9}], 0x0, &(0x7f0000000180)=[{{r0}, 0xfffffffffffffffa, 0x1}], 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x7c, 0x0, 0xe51e, 0x0)
close(r0)
open$dir(&(0x7f00000001c0)='./file0\x00', 0xf3ed09f9c6192a7e, 0x0)
execve(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
rename(&(0x7f0000000000)='./file0\x00', &(0x7f0000000040)='.\x00')
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0xa, &(0x7f0000000100)='\x00', 0x1)
r0 = socket(0x1, 0x2, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000000)="0100a56700009eff", 0x8)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cffffff"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r1, &(0x7f0000001080)=[{&(0x7f0000000040)="6f8678ae0af456c40c63d514fe494d269e0d5c54251c4198095b039b7c2d9d42749b933c91bc7896f0b31d5c789d6c3428863dbcae931862d8f961a38ba5e018201c9ddd8c9026e55332ee51f8e1d3e7011c084a14cdf9e42ac33d8f8d229669176b6b16848b74b1ed9f0e611ae9409afee3fb7789a415adc93f946f2a46ec386dfb06c4ac24da8bdefce43534de9e9bfffe99a344e3cae8b0df79415fa416aa72d1c1e5f6cde05e2fcac013b0bc8006362245c5e2702a01455eb5c4b5ab860a2567739b5c1a8e6c0d5c4a5493f969d8c6739eadfe32725de5b9dc37d590462550bb7c47d451f67c0658602021fc95a13d008bd78dd92312a04a9d9f96fe71d76c39c860e5ee6069484c8840ae35eafdab9d027b1c286948948c2fa55f32ed7f7a615b8a41fcf6109d42449adedc90a2dfd381d5b242b0527e77003419ed355f9c10d53c8ce75f9878673c80be42ffd6e7b846f5117bc2f62aefa9a28c3e46b1c9b6de7357efdc5adaee0c937c823969f40e9f03300ea0f0934eac81fd50b82f2c016cd58981f77e7735f65df7fc11afb12df7ce63179c3fd0936bccfedab4e4465b78ab33d4f0fdca5b83b950079f16a6c7607635e187f51d53828c2a4d46208828ec17ad32df903725283cb19363d8d6a5aa52267880efb55fc8a447126377675ece231bea44e8cf9cf5c2caf23386412f02924fe082eb0319bf86cbe951ab916b19d99bfe092fba287cfdfb166d0fca8a23afd61c299fde3a944bcc0a5075760b608fd3993acfcb1cff3554a8c53822ea66a367fc177a9f35cb7394e8cac7759809d7c98a9b6de0198a17f48431e370a183d0bb195596cfca7aaf32ec0b6e5d5f6751e9683fce5a0004a8ecd965e5be250a04909887e5b6de8988c01ac4ad05884b31bfa9a591a4b1ab241e6a84e1b56abd1b68a2e4d078d9d98103d81531cc4e0a835f8afcef71bfe563add1c1b5014ea58c792db81ac107ea11dce671b78112dee501aa9275952cb49d756ae84504849f75bcc3dfa9cfcb9552829ecbd0d4dd69577700045feb9a95fc83e5ae637500021206e5d3f5c849dd5566e19ca6570315f95f303eb1b6c42c70434bef87ced630fc4ac6ff4e8e70ed32a9822193773e257fca658836fc288e274535e216e914b95ec145f61f89a854d4c69acd15f39e313af45d791dd2ff55ed702a8908c8016f96b1ec9d3bb87eb82116665b9e1c390168bf1c829de3570a29e51de9b70c5fdb837952b1d45b89a563dada59df471aaf4b0187a92427fa79686551bda380875dfe7370fb2308a4e5c41254ca21f28ba64a02c83a5c37b45997c302affcea12431cff228027df1a6271a1f4838a6be0c2fc7b56889309cd9b6793155066c620160ff3a29b25c342adb2ed2e991446715928f7b44156dec5298c4f944d7dfdcbaccd6f133fd4f2576694d637caf5beedac35e6f7e29e504b022d5c06f7436db8794bdae6265c05395057bd896c78afbaeede2e2a573774b7ac9fc50c5bf83821904bf74a543e96c4cdd63aa8ac4a74b738d449062ec6bb7e6b1fc7fa210f45f630811b77497c37928620d6c2d61a73e2db0b95bb1c5864357cb3388375262840e8d885e8bc60d877a2d73335935fc36ceeaf1cb3227df15abffa0ca0127ccaadbe174818014859f36e0d503018333c8a5e27880ededb3d93bcda4844373ac2755c6263585e0926b2648c84cf710c5c51642d853ff1db28e3c34e4b07da42af76e55295033b3e7293555b8158c355ee1251d6b898333fd66e66cc1c720f4f7e048cc2cadb84ba30b8462c6564c51acc074f0b576c03b193d7ba146597edcc44647926639f12a3a53b5dde14cdbd79564c9d7bf56c5cbb89b9d6c5c2a5ce70fc4f30aaa10dd32603090a2f3d91dc2fd5a279084bfba93fd82cc050bbe90b224b76ca3fb91dfbf60f99d4d20064a078135aef90b15928ff406402c96cc54aa4f0a3df26d2940aabc8516725365917ec644dc0b33d0d1fb889669fe80c164b7c747ccbb0f513d3ac26452e8594d9b5061afce8b274c136d448afe0df37da38e134ac201014264a965af5b5d240", 0x5bd}], 0x1)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x1, 0x0, 0x0, {[0x200000, 0x9]}})
r0 = open$dir(&(0x7f00000000c0)='.\x00', 0x0, 0x0)
mknodat(r0, &(0x7f0000000040)='./file0\x00', 0x2124, 0x57b4)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x0, 0x0)
unlink(&(0x7f0000000080)='./file0\x00')
syz_emit_ethernet(0x36, &(0x7f00000000c0)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "3d4bc9", 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="7784ce7d2e7e6e22dc0594d7d02a7abf"}}}})
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{}, 0xfffffffffffffffd}], 0x7ff, 0x0, 0xfbd, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0084428, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x23, &(0x7f0000000000), 0x4)
setitimer(0x0, &(0x7f0000000080)={{}, {0x2, 0x8001}}, 0x0)
setitimer(0x0, &(0x7f0000000100)={{}, {0xc00}}, &(0x7f0000000140))
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
lseek(r0, 0x0, 0x40fff)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000140)=[{&(0x7f0000000000)='0', 0x1}], 0x1)
r2 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r2, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
ioctl$VNDIOCCLR(r2, 0x80384601, &(0x7f0000000240)={0x0, 0x0, 0x0})
r0 = open(&(0x7f0000000040)='./file3\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a0", 0x1cb}], 0x1, 0x0)
r1 = openat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r1, 0x4}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
rmdir(&(0x7f0000000080)='./file1\x00')
mkdir(&(0x7f0000000240)='./file0/file0/../file0\x00', 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0xfff, 0x5, 0x1fc80d88, "00d35ed9000400"})
write(r0, &(0x7f0000000180)="ae2d6fd8b2afffd97b5d06dbd99ed95af56260e8988dbe2452cc42d6f3f3b304", 0x20)
setitimer(0x0, &(0x7f0000000240)={{}, {0x4}}, 0x0)
getitimer(0x0, 0xfffffffffffffffe)
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000002340)=[{&(0x7f00000024c0)=""/4096, 0xfd9}, {&(0x7f0000000040)=""/11, 0xb}, {&(0x7f0000000100)=""/41, 0x28}, {&(0x7f0000001180)=""/149, 0x214}, {&(0x7f0000001240)=""/4096, 0x1000}, {&(0x7f00000023c0)=""/211, 0xfde3}], 0x6, 0x0)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0x0, "0900081000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$unix(r0, 0x0, &(0x7f0000000040))
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
r1 = socket(0x18, 0xc002, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028756b, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000000)={0x6, 0xf}, 0x2, 0x0, 0x0, &(0x7f0000000240), 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "006259f1355364f42792e6a80000000000000012"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
msgget$private(0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x40000400000002c1, 0x0)
setgroups(0x0, 0x0)
r0 = getpgrp()
ktrace(&(0x7f0000000100)='./file0\x00', 0x0, 0x410, r0)
setgroups(0x0, 0x0)
ktrace(&(0x7f0000000140)='./file0\x00', 0x2, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{}, {0x48}, {0x6, 0x0, 0x0, 0x103}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
bind(r3, &(0x7f0000000140)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
dup2(r3, r2)
poll(&(0x7f0000000080)=[{r3, 0x1}], 0x1, 0x0)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000840), 0x1, 0x0)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000840), 0x1, 0x0)
poll(&(0x7f0000000080)=[{}, {}, {}, {r0, 0x1}, {r1, 0x1}], 0x5, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x40}], 0x1, 0x0)
r0 = shmget$private(0x0, 0xc00000, 0x0, &(0x7f0000400000/0xc00000)=nil)
shmctl$IPC_RMID(r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0x81}, {0x50}, {0x16}]})
syz_emit_ethernet(0x3e, &(0x7f0000000200)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "2ff688", 0x8, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @rand_addr}, @loopback, {[], @udp={{0x0, 0x2, 0x8}}}}}}})
syz_extract_tcp_res(&(0x7f0000000000)={0x41424344, <r1=>0x41424344}, 0x2, 0x8)
syz_extract_tcp_res(&(0x7f0000000040)={<r2=>0x41424344}, 0x7f, 0x2)
syz_emit_ethernet(0xca, &(0x7f0000000280)={@random="acb8a73d733d", @empty, [{[{0x88a8, 0x0, 0x1, 0x3}], {0x8100, 0x1, 0x1, 0x3}}], {@ipv4={0x800, {{0x5, 0x4, 0x1, 0x1, 0xb4, 0x64, 0x7b9, 0x6, 0x11, 0x0, @multicast1}, @tcp={{0x3, 0x1, r1, r2, 0x1, 0x0, 0x6, 0x4, 0xc31, 0x0, 0x80, {[@nop]}}, {"20d58faab16bf78cb19c26d50ec08c34f0e72b21686f620541bf584ea4af62e2e7dfc4315d4eb7744ddb68de25473bf506ff3f87df8b881c8e68046bbabaa3f1b28da467d64e29fc0c11f68503004bc904fc84d06653024e7abe7937256f6a112025fd4ad67a36d174150fb715dedc6a10ff0bef0c571ef4cce6f9f788449bb5c9652aede2904d8e"}}}}}})
pipe(&(0x7f0000000080)={<r3=>0xffffffffffffffff})
write(r3, &(0x7f0000000340), 0xd4e688a67930cd)
close(r3)
ioctl$BIOCSETF(r3, 0x80104267, &(0x7f0000000140)={0x5, &(0x7f00000000c0)=[{0xf1b5, 0xfe, 0x9, 0x93}, {0x1f, 0x3f, 0x40, 0x10000}, {0x3, 0x41, 0x0, 0x3}, {0xff, 0x5, 0x5, 0x8}, {0x1000, 0xc3, 0xf8, 0x8000}]})
symlinkat(&(0x7f0000000000)='./file1\x00', 0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00')
mkdirat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
select(0x40, &(0x7f00000000c0)={0x86e}, 0x0, 0x0, 0x0)
mkdirat(r0, &(0x7f00000002c0)='./file0\x00', 0x0)
rmdir(&(0x7f0000000280)='./file1\x00')
syz_emit_ethernet(0x1a, &(0x7f00000015c0)={@local, @broadcast, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="99fa8b3fe026", @rand_addr, @random="e3db57505d1c"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000015c0)=[{0x1d}, {0x61}, {0x6, 0x0, 0x0, 0x80000}]})
write(r0, &(0x7f0000000280)="731f5c52643f0e37bf54dd2a1b32", 0xe)
r0 = socket$unix(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x40, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmsg(r0, &(0x7f00000003c0)={&(0x7f00000000c0)=@in6, 0xc, &(0x7f0000000340)=[{&(0x7f0000000100)=""/184, 0xb8}, {&(0x7f00000001c0)=""/231, 0xe7}], 0x2, 0x0}, 0x0)
sendmmsg(r1, &(0x7f0000001040)={0x0}, 0x10, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x3b, 0x0, 0x0)
r0 = socket(0x1e, 0x3, 0x0)
connect$inet6(r0, &(0x7f0000000000)={0x18, 0x0}, 0xc)
listen(r0, 0x0)
setsockopt$sock_linger(0xffffffffffffffff, 0xffff, 0x80, &(0x7f0000000040)={0x1c000000, 0x3}, 0x8)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000200)={<r1=>0x0}, &(0x7f0000000500)=0xc)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0, 0x2)
getgroups(0x7, &(0x7f0000000080)=[0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff])
r2 = geteuid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000100)={{0x1f, 0x0, 0x0, r2, 0xffffffffffffffff, 0x10, 0x7fff}, 0x100, 0xfffffffffffffff8})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000180)={0x0, 0x0, <r3=>0x0}, &(0x7f00000001c0)=0xc)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={r1, r2, r3}, 0xc)
sysctl$kern(&(0x7f0000000280)={0x1, 0x59}, 0x3, &(0x7f00000002c0)="11ab71df", &(0x7f0000000000)=0x3, 0x0, 0x0)
sysctl$kern(&(0x7f0000000080), 0x2, &(0x7f00000000c0)="d1d847bd74d1644362990e2cd3e9a99fbe1e6fbd7406", &(0x7f0000000100)=0x16, &(0x7f0000000140)="fbf28169113e68a959789dc098ec55214c2f48aa15add5f5154c96c56fca3ba14d058e", 0x23)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000180)={{0xfffffff7, 0x0, 0x0, 0x0, 0x0, 0xa0, 0x4}, 0x2, 0x7c42, 0x20000a23c})
r4 = getpid()
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000340)={{0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff}, 0x7, 0x100000001, r4, 0xffffffffffffffff, 0x8, 0x909, 0x2000000000000000})
r5 = semget$private(0x0, 0x4000000009, 0x0)
semctl$IPC_RMID(r5, 0x0, 0x0)
shmget(0x0, 0x2000, 0x0, &(0x7f0000ffd000/0x2000)=nil)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
flock(r0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x801)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000140)=0x9)
close(r0)
sysctl$hw(&(0x7f0000000380)={0x6, 0x5}, 0x2, 0x0, 0x0, &(0x7f0000000440), 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0xa003e6cd46fcf3eb, 0x102, 0xffffffffffffffff)
setuid(0xffffffffffffffff)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x36}, 0x4, &(0x7f0000000040)="4b6eccd476e7b0f5094d4066067857d1", 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0x401)
r1 = msgget$private(0x0, 0x1e)
r2 = getuid()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000340)={&(0x7f0000000180)=@abs={0x0, 0x0, 0x3}, 0x8, &(0x7f0000000480)=[{&(0x7f00000006c0)="8d666511c62f28347f41ea3064e25d72fb81ac52f77c9e199eeb3ccbd8abc2055270aa1da62acfe013390ffc47db783f8f344cf2a351b8b8ab3b09d74823da2bb23ae1e05c2ffca2d27611b3a5826e954a1ab93a8f5c0a77c7bed2a146eb666baa963fab8b5a2c29d543ea81780ba6459bca4d45c77519b8b34b48533a6abdf024ff5fbcfb308348f62fc5afb6fb1390624e2db5dc228226b42c3c72e0c88859a72fd132477e7f11f9290de337a61ffb798ce07f406ed8", 0xb7}, {&(0x7f00000001c0)="812c1357940a17c27fe5e156e0e52b7314dd45fb68d9fc99014199da1cebdf3b54c9c0e427ceb9ebac610d6d1228df22f33a21e8a42aced305f527f4ad9a66126180252e1b1d927d48e631ab6647e4628a7ba3e8e928f1d2b0c692ba09ae044c0452955117d8a68a2d1909a6c775a1e76e2c3211c6", 0x75}, {&(0x7f00000004c0)="558c7fd0583ae616fdb670488c575f5ded7e29f9cfb88364ad3acb166fd3fa73e8bbba97c2ae8a90e3ae3ae427116ed4e8e86953279a6b4ca464312efc0a55bb1de8c1e7a9e8e5f4384feb0773904195ac93e39a100d6d768370f0932d17994b55ae5b5ca9e581a5c3d1c5d3070ecbe4add0f5a3f85162f09284bd3e407303e6d8db4ac6162a6877866c43dfaa2803e109438950afcbeafc609f3b99744178c6b355d5a6affc943c79366873ed0196043559f637f67d82d8aeb91646412d71aa94095e7c7d1daffdac05713a2d93af06de1681c99a11421dd53fc0f2fe55fdbcfbc0cdbb980b", 0xe6}, {&(0x7f0000000780)="d09e0da0897fe61ffd8d008e65a45d89f335246c83cd49dab5735cbd4caa26558086065bcb0a724b9a35ac3cbb999f7d4f679ede6d181005c2a9941242e2c9a5bdf6f74e614c230bdff4bb88d613eef523ea28d952b7552041233c39ed4acbcdc75c2388e60e8fb4a19dc877e2dbfa6df0b50b476584637db866898773e4613052297f3b32bcd51e7e6db5c33b36866cf4549645a47af89d77f9b459c5c1a6340bb41764901b1d8287eae2fa1153f84610503aa0", 0xb4}], 0x4, &(0x7f0000000640)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="000000002000000040000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=r2, @ANYRES32=r0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRESOCT=r2, @ANYBLOB='\x00\x00\x00\x00'], 0x58, 0x402}, 0xa)
setreuid(r2, r2)
msgctl$IPC_STAT(r0, 0x2, &(0x7f0000000240)=""/77)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f00000012c0)={0x0, <r3=>0x0}, &(0x7f0000000100)=0xc)
r4 = getpid()
msgctl$IPC_SET(r1, 0x1, &(0x7f0000000400)={{0x0, r3, 0x0, r3, 0x0, 0x200, 0x9}, 0x4, 0x5, r4, 0x0, 0xffffffff, 0x8001, 0x8, 0x1})
mknod(&(0x7f0000000100)='./bus\x00', 0x2005, 0x51f9)
r0 = open$dir(&(0x7f0000000400)='./bus\x00', 0x0, 0x0)
fcntl$getown(r0, 0x5)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
sysctl$net_inet_esp(&(0x7f0000000200), 0x3, &(0x7f0000000240)="4f9ad01701bb3067ac91af0a12a2c68cefd218c26a2fc3659777027cd400b0fadf64efb8320ff2468212f4f8a0c0e0b75c981d9fc06da9810560d7aeddd8335f7730f34418286a8dd990c695adf7a1e3e92ce0ea9b6f5ada88ace642", &(0x7f00000002c0)=0x5c, &(0x7f0000000340)="a53d7307fe8fbe32d15f472783bfcccadfb5c80c7b0f8a242051af0c2f", 0x1d)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4}, {0x80}, {0x6406}]})
syz_emit_ethernet(0x3e, &(0x7f00000001c0)={@remote, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a9bb28", 0x8, 0x0, 0x0, @loopback, @loopback, {[], @udp={{0x0, 0x2, 0x8}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x4c}, {0x61}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="6d9ef757a944e00af2e26510358b", 0xe, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000140))
mknod(&(0x7f00000001c0)='./bus\x00', 0x280002002, 0x2065d)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x44}], 0x1, 0x0)
r0 = socket(0x18, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
writev(r0, &(0x7f0000000240)=[{&(0x7f0000000100)="e6416409002b89699f1362955dee5a26e93c6c2a", 0x14}], 0x1)
getsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f00000001c0), &(0x7f0000000200)=0x8)
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000080)="44000f3e265873b91d1195de6aae7fd0cc327233a9ff18e27ff8328f8251e84ab281bbadc0f1b0c9dd01a66c110fb46800f5fb1810aad6f149cd793c3e97e913098f2d5a4f468527b58a5569e6cce319ea39307452d1f9fafe54024e3fb0ba0ae44677cc64d889534d97dab3a38dcfa91d32c19ac82ab98e8b9c86930092eec37e297a0181389db8759d071488187d169caf2fd0b36667a99b4aacbf5c5d22db64b77dff26bcffe817cd39d9cfd4f3680ff209c719a79945f51cba13f1cc194f94c9ac442e7fb95b5f777f54f518ed97646a369954169e073ffe5fbd3c48d9365793bf0a52699df8917a0823301340a8890da44e2dfe2c4e9691db1d246ef1de5af3c0632d75ef1f57c55121a8c2699dfb64ea2d4cc515aefc085077eebc31", 0x11f}], 0x1)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x100, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
dup(r1)
pwrite(r1, &(0x7f0000000280)="e66cafe409aadb77f83bef5cb356a4d0cc81be48dfcedcc711856ca93a7d0aa4d3b29084ed7e36fd299f65273cb311cef5affd178e93675f99cf8d1c7bf35bb15473d46b6fb853345403be20c8935f5398019c1998a21af60f929ca51e5e4a6a31cc6f7cb874784f1610984b4ba300d73ed91ec11010ecc21fb93cc27dbf05c5395571208338ba19271fdc487d58c8f1d46c9d04c752833097a05e9ddde0a4d8db62e9316037de7d0b3163a37832ba3cdb241a8c39da1b631d687cd4fdfd23e34fe0bcf1067161356b9e88564f0e465e", 0xd0, 0xd59)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000400)={0x3, &(0x7f0000000140)=[{0x15}, {0x1}, {0x6, 0x0, 0x0, 0x80000001}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="e16914f6357e3a00000015000000", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x3}, {0x6, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b35c7"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
r1 = socket(0x2, 0x1, 0x0)
dup2(r0, r1)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0xf, 0xffff, 0x0, 0x0, 0x0, 0xff, 0x200000000000004, 0xfffffffffffffffd, 0x7fff, 0x0, 0x80000000000004, 0x3, 0xfffffffffffffffa, 0x0, 0xfff, 0x2, 0x0, 0x2], [0x9, 0x200, 0x6, 0x6, 0x2, 0x0, 0x0, 0xffffffffffffffff], [0x5, 0x8, 0x0, 0x0, 0x3, 0x20, 0x2000000000], [0xffffffffffffffff, 0x5, 0x0, 0x1, 0x5, 0x5], [{0x3fc, 0x0, 0x6, 0x8}, {0x0, 0x6, 0x0, 0xfbfffff7ffffffff}, {0x0, 0x0, 0x0, 0x4}, {0x1d5, 0x5, 0x4}, {0x2, 0x3ff, 0x3, 0x5}, {0x0, 0x1e5}, {0x99, 0x8000, 0x6, 0xffffffffffffffff}, {0x7, 0xdf80, 0x7ff, 0x8000002}], {0x0, 0x1000, 0x2000, 0x8}, {0x7, 0x9, 0x4000, 0x4}}})
mknod(&(0x7f00000000c0)='./bus\x00', 0x2040, 0x4f4b)
r2 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETA(r2, 0xc2d0422a, &(0x7f0000000000)={0x0, 0x100, 0x0, 0x0, "04713de0af28a2813d8209b8d9f39321849e3c99", 0x0, 0x8000})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
dup(0xffffffffffffffff)
lseek(0xffffffffffffff9c, 0x8, 0x3f)
socket(0x10, 0x1, 0xfb)
open$dir(&(0x7f0000000100)='./bus\x00', 0x1, 0x0)
r3 = socket(0x18, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x81206919, &(0x7f0000000300)=0x1000000)
r5 = socket(0x18, 0x2, 0x0)
fcntl$dupfd(r5, 0x0, 0xffffffffffffffff)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x0, &(0x7f0000000040)})
mknod(&(0x7f0000000040)='./bus\x00', 0x80002005, 0x4300)
r0 = open$dir(&(0x7f0000000280)='./bus\x00', 0x381, 0x0)
r1 = dup(r0)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000080)=[0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffb])
ioctl$WSKBDIO_SETMODE(r1, 0xc0105715, &(0x7f0000000080)=0x1)
r0 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000600)={0x0, 0xffffffc1, 0x0, 0x0, "2aa0325147dae4404bca96935a866847fd395fc2"})
writev(r0, &(0x7f00000016c0)=[{&(0x7f0000000080)="67c9eb4f7d20a4923ec63e566b174fd549d4276d9fc7bcd8f7fbc9b87d7f8f8c24172163926b13ef6ee325fd47b2a9a3a738ba053bd98b15a73295140ec46cdada07604d212d90dd4cc6facd300faaa591efd7ad08fd7e36281a1446e7d10494f6c28ff208", 0x65}], 0x1)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000000))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x2, 0x0, {[0x8000000000000001, 0x1ff]}})
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
sysctl$kern(&(0x7f0000000040), 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
r0 = getppid()
setpgid(r0, 0x0)
sysctl$net_inet_divert(&(0x7f0000000040)={0x4, 0x11, 0x2}, 0x4000000000000003, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x3f, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
r2 = socket(0x1, 0x1, 0x0)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc050756a, &(0x7f0000000300))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0x800c5606, &(0x7f00000002c0)={0x2, 0x1000})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000080)=[{0x7}, {0x5c}, {0x6, 0x0, 0x0, 0x800ceb0}]})
write(r0, &(0x7f00000001c0)="7c0000ffffffff00001a00a5f3fc", 0xe)
sysctl$net_inet_carp(&(0x7f0000000200), 0x4, &(0x7f0000000240), 0x0, 0x0, 0x0)
munmap(&(0x7f0000200000/0x1000)=nil, 0x1000)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
r1 = shmat(r0, &(0x7f0000200000/0x2000)=nil, 0x0)
munmap(&(0x7f0000200000/0x2000)=nil, 0x2000)
shmdt(r1)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000205, 0x25)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000140)='#!', 0x2}, {&(0x7f0000000000)='H', 0x1}], 0x2)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000100)=' \n', 0x3)
r2 = getuid()
lchown(&(0x7f0000000040)='.\x00', r2, 0x0)
getgroups(0x6, &(0x7f00000000c0)=[0x0, 0x0, 0xffffffffffffffff, 0x0, <r3=>0xffffffffffffffff, 0x0])
fchownat(r1, &(0x7f0000000040)='./file0\x00', r2, r3, 0x4)
execve(&(0x7f0000000380)='./file0\x00', 0x0, 0x0)
r4 = getuid()
lchown(&(0x7f0000000040)='.\x00', r4, 0x0)
r5 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r5, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETZCNT(r5, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r6 = geteuid()
r7 = getgid()
r8 = geteuid()
semctl$IPC_SET(r5, 0x0, 0x1, &(0x7f0000000380)={{0x6, r8, 0x0, r6, r7, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0xbf5})
chown(&(0x7f0000000100)='./file0\x00', r8, r7)
fchownat(r1, &(0x7f0000000180)='./file0\x00', r4, r7, 0xa)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, &(0x7f00000000c0)}, 0x0)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206982, &(0x7f0000000300))
setitimer(0x0, 0x0, 0x0)
fchdir(0xffffffffffffffff)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x80, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000040)={0x1, 0x6})
syz_emit_ethernet(0x6e, &(0x7f0000000200)=ANY=[@ANYBLOB="b52e1a5ab0beaaaaaaaaaabb86dd60e3fbc800383a00fe8000000000000000000000000000bbfe8000000000000000000000000000bb020090780000000060f2c7b60000000000000000000000000000000000000001fe8000000000000000000000000000aa1100000000000000"])
r1 = socket(0x18, 0x1, 0x0)
dup2(0xffffffffffffffff, r1)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000180), 0x4)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x1c}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r2, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x1c}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r3, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r4, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r4, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x80, 0x0, 0x1, 0x401}, {0x1c}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r4, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x15}, {0x60}, {0x6, 0x0, 0x0, 0x108f}]})
pwrite(r0, &(0x7f0000000340)="bbc86460e2440102f2c74cd58cd7", 0xe, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000280)={0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
sysctl$kern(&(0x7f0000000280)={0x1, 0x4e}, 0x3, &(0x7f00000002c0)="11b7f23b", &(0x7f0000000000)=0xf, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r2=>0xffffffffffffffff})
shutdown(r2, 0x0)
r3 = getuid()
r4 = getuid()
sysctl$kern(&(0x7f0000000040)={0x1, 0x40}, 0x2, &(0x7f0000000080)="6f531171bdb4e351cd7a5bae295531f23c507f6222bff844b0cd08860c2371064be6b5d13220f347c233a5069ac1765bc49de98a1c8efbf14a1dfd81", &(0x7f0000000100)=0x3c, &(0x7f0000000140)="5a3abbafec99419e416fb5654d90bce0c87cb6f071bc04bab801fd3ae6958c8b8a869c2bae20779c1d035bda34b73159d59969879bfef88cec30cc307764129a345c363489c9e961bb21b9f56b1908de82b570f7f41804f3179b20abb164dc661b9219b3a996c43e9c12013cee78749a6928c69ab34192a922283fbf69d1b55d7ae178ccaa8031c2078d60858665b3ed", 0x90)
setreuid(r3, r4)
recvfrom$unix(r2, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
r5 = getuid()
setreuid(0xee00, r5)
setreuid(r5, 0xffffffffffffffff)
getuid()
r6 = getuid()
setreuid(r1, r6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000280)=[{0x1c}, {0x28}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r0, &(0x7f0000000040)="f94c9f64dd0303fb27447669ee2e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x45}, {0x1}, {0x6, 0x0, 0x0, 0x20000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r0, 0x29, 0xb, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0x9, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0xfffffffe, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000001, 0x0, 0xfffffffffffffffc], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000022000210], [0x0, 0x3], [0x8, 0x0, 0x0, 0x1], [{}, {0x0, 0x0, 0xfffffffd}, {0x0, 0x42}, {0x0, 0x0, 0x0, 0x7fff}, {0x1, 0x0, 0x0, 0xfffffffffffffffd}, {0x0, 0x0, 0x0, 0x100000001}, {0x0, 0x1}, {0x0, 0x0, 0x800}], {0x0, 0x2, 0xffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
fcntl$setflags(0xffffffffffffffff, 0x2, 0x1)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffff9c, 0x29, 0x68, &(0x7f0000000140)={{0x18, 0x0, 0x2a3, 0x48}, {0x18, 0x3, 0x0, 0x8000071}, 0x1, [0x8001, 0x0, 0xfffffff8, 0x1, 0xffffffff, 0x8, 0x205]}, 0x3c)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x12080, 0x4cf9)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x80, 0x101)
connect$inet6(r0, &(0x7f0000000000)={0x18, 0x3, 0xe950, 0x8000}, 0xc)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r1 = socket(0x2, 0x1, 0x0)
dup(r1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020690c, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4], [0x0, 0x0, 0x0, 0x4]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x100, 0x0, 0x0)
r0 = kqueue()
r1 = syz_open_pts()
syz_open_pts()
kevent(r0, &(0x7f0000000380)=[{{r1}, 0xffffffffffffffff, 0x71}], 0x5, 0x0, 0x0, 0x0)
syz_open_pts()
kevent(r0, 0x0, 0x0, &(0x7f00000001c0), 0x9, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0xd02)
r0 = open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
dup2(r2, r0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000180)={&(0x7f0000000100)='./bus\x00', 0x1f, 0x0})
ktrace(&(0x7f0000000000)='./bus\x00', 0x1, 0x204, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
recvmsg(r0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x842)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000280))
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000001040)=[{0x6, 0x2, 0x2800}, {0x3, 0x6, 0x800}], 0x2)
semctl$GETPID(r0, 0xffffffffffffffff, 0x4, &(0x7f0000000180)=""/145)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000006c0)=""/264)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f0000000280), 0x0)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40}, 0x40, 0xfff, 0x9})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
r3 = getegid()
r4 = getuid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, r3, r2, 0x0, 0x10, 0x7}, 0x160, 0x80000000})
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r5=>0x0}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000080)=0xc)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r6}, 0xc)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000040)={{0xc, r2, r6, r4, r5, 0x1a4, 0x501}, 0x9, 0x1, 0x5})
semop(0x0, &(0x7f0000001080)=[{0x3, 0x3}, {0x4, 0x7, 0x1800}, {0x1, 0x8}, {0x1, 0xc378, 0x800}], 0x4)
semctl$GETVAL(r0, 0x2, 0x5, &(0x7f0000000040)=""/4096)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004})
flock(r0, 0x2)
execve(0x0, 0x0, 0x0)
close(r0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0205609, &(0x7f0000000040)={0x1})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e0174685", 0xbc}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
pledge(0x0, &(0x7f0000000040)='\x00\x00{\x1d$-\x80\x8a@\f\xa4;9\xbd/\xe2@\xb3%\x804\'\xb7\xcb\xff$\xad\\N\xb8\xe3\x02\xc1\x17\x84;\a\xfc\xa1\x15(\xc5\x9f\x04\xb6{\xeb\xe7(\xe8\xbb\x9e\xb8CZ\x16\\g\xf9<\x1b\x91\xfaq\xa9_\xb9\xa4\x9e]\x98f\xd4\xf3')
pledge(0x0, &(0x7f00000000c0)='\x00\x00{\x1d$-\x80\x8a@\f\xa4;9\xbd/\xe2@\xb3%\x804\'\xb7\xcb\xff$\xad\\N\xb8\xe3\x02\xc1\x17\x84;\a\xfc\xa1\x15(\xc5\x9f\x04\xb6{\xeb\xe7(\xe8\xbb\x9e\xb8CZ\x16\\g\xf9<\x1b\x91\xfaq\xa9_\xb9\xa4\x9e]\x98f\xd4\xf3')
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
readv(r1, &(0x7f0000000840)=[{&(0x7f0000000400)=""/84, 0x54}, {&(0x7f0000000140)=""/56, 0x38}, {&(0x7f0000000480)=""/103, 0x67}, {&(0x7f0000000500)=""/120, 0x78}, {&(0x7f0000000580)=""/162, 0xa2}, {&(0x7f00000006c0)=""/160, 0xa0}], 0x6)
writev(r0, &(0x7f00000003c0)=[{&(0x7f0000000040)="fa", 0x1}], 0x1)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x8001, 0x80003f9, "500420000b0909000000000200"})
write(r0, &(0x7f0000000040)="4d17fb388d00", 0x6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0xc0}, {0x1}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
open(&(0x7f0000000000)='./file0\x00', 0x60a, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r0, r1)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
rename(&(0x7f0000000200)='./file0\x00', &(0x7f00000000c0)='./file1\x00')
symlinkat(&(0x7f00000000c0)='/', 0xffffffffffffffff, 0x0)
unveil(&(0x7f0000000100)='./file1\x00', &(0x7f0000000180)='x\x00')
open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='oL', 0x2}], 0x1, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000500)="ec38718cc85e43dd532f8fa65814a0578ed276f0a4548f94b77549d55125e5e1f794ba2af539785264a5304da45174fabb3611273a961901f5e08be85c3184dd54af1a93cd261afa80b14d7984c40a3ba31e52f8f9663c44214029b89e4d853f96bf33568575208cfcea8560155c11cde3cd5e5fac7efbc219033309715eae0d48a3d2fe9205d761ae908466b5225d5f9fdbd2fdb0bb7a86b22be1c15cb82a7d11a5dd959c30b4a80feb47d7dc4eed8ae14f8e3025a9d5af89e1c7beaf7d1afed7b4923829b0430b1cd3623a2f79192d5172da993512b59f826b716b200eeac6065a6d550a3ab2e03f02856e07db8ed8d36250f3b04348115cc4ed5711655702bbf41080bc3c7933b796e476b46e29c933cdfbbe2fb6283a7811ee0b6fd71d8ea3e0bd2d319c84313dbade0e4a29a6493264951ea382b84e4de5680014e05b2b3990194573eda00a3a57cdc8088475ccc4d9589c46a3e962bf5871d8bde7173bebcef9379e673623465b19af009fb9f55fa7b03cf32aed2996416f9dee6b06853383d77e3ab41f02d1f23ec863", 0x18d}], 0x1)
pwritev(r0, &(0x7f0000000440)=[{&(0x7f0000000380)="44dcc12f33a3f8e6988d3ac849f108c6168398b6a886540dcbf8c50eb277761493a1ded4c624ff53063782a2e765", 0x2e}], 0x1, 0x0)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000340)="83eca8fe3800aac72d4750a3422e", 0xe}], 0x1)
execve(0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x9, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffdf7)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xffffff80, 0xffffdf7f, "f163b534fb00e61600ffffffff0100"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f048eea427be6c0045598cdfb92934638d", 0x90)
r2 = dup2(r1, r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x400, 0x4, 0x9e40, 0x0, "dd45c8a58272fc2758ac40067344d329bc4d1232", 0x87, 0x9})
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f00000000c0)=0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000000)=[{0x7c}, {0x60}, {0x6, 0x0, 0x0, 0x10000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "aff9c6", 0x8, 0x0, 0x0, @loopback={0x5}, @loopback={0xffffffff00000000}, {[], @udp={{0x1, 0x2, 0x8}}}}}}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x53}, 0x2, &(0x7f0000002180)="372a967fb3677bb6a6de4450001aa5431d66b56df3771938c9ccebb68c6fef08f028c2e8c139ace5de88ae23ca8884401cfd6d351f3ff64026ea893839fae56a9bea41a2f996d54598c6262bc75bdb5e1faf7f9ca1d69dc3be23138daa51558db79af1d9a253c2d64a0539b103d36818831b99e8840f7b1f14f05ef674cded023389f7640f4cf105e42f0f4fd36ee8fa254ce812053becde8ca1c7b69e15e41176ef9b315601c35ac45ad05d055b62594c484181152ceb120245e19a4ab466e48b51fe933f9797d79dc5ade6bb883e1e4e7fceff62ae4a94601c0ffd18f5eecaba5a36e66f6443ab0ebd9c9039e65393a52304d9f889d3610209d299a216417ceb43e91488b6cf3c134d468236756e2e3be55700c7aace420e725a8cc60a48aa9959032bc743b4a5ec1138847b6bc1ceac1d5dafb3093ac785a2fe9da3d58c830fea9bda5603869dde32282ebb2eeb81efc5a45e7ede377aa3d6b9e2da109932877f0fd6731fdf1bb750f83acaa9500096c425f63d7cde843d125b867af590580d584c7016292522c660c2fb23c977759b6cb8676f03860d9d55b9b5f0d3e36dbaeaf0845a9267f5326845cced9881fefbf5a7067d5b1c3bfdde23e8644e2aa5d7a89150fc0fd0142e4bfa9939f6b9e378a9bf0144b46cee69bb4f3b15f16680c05740797e32391480d65c70f7a39b678eb33d104c50544f886c9520916e92080756c39d0fedd56940c151fe38928eb6e08137377a3e2e7b8fffeacd2a8af629b7b7b309420bb0570979a04c9f77d16968698e3b489919e315cdf5b54edcb3bd7a35ead9caad707e1d2d8f327fbd8bfc22c647fe5bcb0c9d63653760d2bec622ac864557c6f995de58e826bafd7f54b7993cec034e8c57a8e8a20785aab9b2412858ade62fe962bd0ceb36250ffe35ac557b8e6caf0d694b0d5098fbf609331360dd0fb1fd45481ed4c3808010cb31031d3987915bf856261846f6ef1e43253998ecce4a5940316640369026bccd66aec071911560772fe5048c426f6626f9ec6d700ecf4e36b9501d790b22a9fdeb8ccca96945b56b11c5b5488cfa86dcb412572ae4941110aa18a958c99b7bf4c8de5048d8d9cafa00d8ae215c9bb39f771a07de1576ce1215cc413de5a55569a845a886b715b88da010731877b8428d418bc49e4ffda08b4528a1b25d2e9ba17059e07f0fe2629e0f7287efc63249c03976400bdbcfb42a0720eaaae76ed640ec0108c40003860c02943ba450b375ecc44529ed109c67b446023243ba202782f06c885e5e860861f06ab0e7600b89d9fb9c824597f055e46f35aa487e357e542ae18b23d9043c8d9034bfebf58da65a91aafdb8822c10fbb13a140d761180c1996e43bcfebee30bc738c46e799bbd0b1b4c2574060de1a73f8699f545c7876538de9c955926d77ed2d44456a6d62efcf58d6ba676bd2dc4d81d60432efb157a9308dbaf07ff3162f02e4ba7f7cd402eeaa599a087e299d4e99a0f2e24a9bb95926d82f0c4c7e0592a3a007f889178fa97bce9699ce9ff3548f1a121e7cf896f5cdef4d03f02ae9b4ef4dacd9437d2ef9d2daa77b0a41c1ed038085034d9eeffc4d87619f87743560de3a6971fd57712f8cd69a4b9bb0dc918b3ec441ef1ff146496af92569ed8b04e60e64811afd5755fa4290294495fd20f60e8cd15f03e08968698a40cdfefb1af7bd20eadb5965012f10337a91e5c0287261317d41b47a3dae66196f46e305e8809e3fcb07e962b7b4baf6c14aba512f52af6d05aafc1cc0951f87bef947991b4a9d1a646dec37e6f6cf1fc7268d7d4c31af3181a27573ec7279088e0fe4e88bd2f43798924eb33bdb842ff03d64a8aa222958bde88f4fcfb50545a1118f8fa4c188014ba661922bc8c5af060d2962e90e48b1df894b322f88348b612062eb5a159b6576974ca104c10f04bfb2496c0abf80d84265ed24ee4650cfac0887ee718d011b7719075a19f3cf27ef80dee116823b0e7d5057bcd7ba112122a70d1c556b8c45f5d96aa42a1a6217330a69c783b938987fa6fde7117d0fe0a81a42bc2711fd370bddc2287c8d0fdd8b0e7aea4dbb79cbdb894246c91fff30c3b302a5f97c3ca64cb49080ce904d372335c237761bb4c9d1dd801879703aa0ef21d0c957ca61257dbe389bc688c96906b4578e8c75ff150dcc432ee5ffa90ca01aff568098206e8a5c082e9e8c5e012b5f0f900c3d13dba8a0ae684a1017540327fe211c366910e269b93104921c55a67f3c03780e50ec5db42cc52005821b31637516803d43b32f42790a9af6429bf7340ca07d2019de4537eef5d1827c0f84b2c8787eef6a00552678dbdf37e0694dc5a5d678228ff35602c26070e212cb745d867184c69a239794d001289650aaefb2aa619b7f8d9ab2ee705062096766076df41ea6d16611dbecd5f7c9fc148ae6f0749ab88824fbb9330a71c39fba3acc748f942128308e955ca5d6ca299962a100c0c71d747035e4df34d602bc0063f5752f01cf6657427481aec1f430130a26f5bf628649a6d9f06731544df63313ae95db17c8e1c9ada03ed645dcf4a5cd481e54f8209a7ceed38213561d0516d89eb37ebbee7689d61429cb4efe18474e1528c54a39ec03cbff255941bee0c1483f6ec55594b6558d13fed09bdc2c98836167b27093eb1ad165a9b5dcf95b72aa983827f8d4664890e47d817acd554a980023cba9c7a4fe39368128d8adb07f7fdd8879bce2c70f995d21b1cb52ae047736e504320f8f7511711e271850d9fede37b9b877c911b43e2acbc09372c86dbcd0c30e098be4d7b2353bde3033e05a6e0326a24c10efd22bd4c0ece1d980e50fbd6e2657fff0dff12c30dc7d0ad84480560cffecd2c6565312aca88e38314613bc9d631046aa2b4f040dae18a1effd2e0005826342ae0a643d36f6c6a13bc493f598065240cf94e61a3bc9031a55cb03d5645ebc539a9cc657b5f60a6f86d0f7e4e1213cf47a4d129f8d208fa67c94bd4d3a26919d58d20364fbd5643115616978adf9115dc354e1db152a3fab7c28c5973d00631f0c353ae8b9bcedcb2a67a903a92a81b2156adaad3a2400b6c0cd58f8ec818fc68cc4f3bb51c28c5db440bcb380b7bc25ed61f5dbe918e5d7f31b3491053a3c110561078d70a2d9e0c948ee48380edef094e0fc1e840996394940803cc41910dbfc65c9d500a03bc6a57520f8272e372c1208787c8fb09f9c4b2c648d582114d84062e96892483a61298f25e677e8dcb2ca8042679d87927a67f10dbaad01fdde05202f3db362ad4c7db88542fbceb56db471aa2e78036b56456cecc47f841b492de139ad58d6cbbb7b296146375f2765a2dbb67e7d25a9a4264f0902ee64272a9dd2e196c836889a6b4f58de5323265e27f7a3904997a86d488493acb3427610928675294a51c1587a56f8ae335c826519083beb8ef478891218fe1ed271ce36bb7bda58b4290493716d1f0c801b78d64c728b6a75f4bc84f51260768c9c5385de273bd6c6b1435dac255363e1166020e3353925c579a00ec6eb83e140a3f86de0c5ca259c5afdd94dfc362542b961e4010e2bab559a2e4b034d90c26349818a20944d51d3f79fba99046007b6df9eefa1fe7708ab0e28210a6ad814d1c0c8a68de2b5b7555b1be3968da6ebbc1be547ef954fd0466b0b59dcfc4b452a5afadc1d53d818a5ff5bf9e5a21861c95c73f81be4378ccff58f49f3ac7f84f2c4b247ac4cf705827ac3b246064c3702f3e69a6d3bdd55d3ff2d4f4ba713ecd3fb39789ee78c99aa7550ab779e5fa772279132743d134b710047cbc80137e083147dbf5993555e7b0c24f82f9ebdbb6c1ca86fc4144dedbdb00e78d486de78752e049a341fc77d459b2eec30713b261db99cb694da6c76cf806e2118c69d0c778a40f1d2f8a003d30f9da0b597a8ecda3d2588347d8acdd0fe1d7a27023ce2d8c3612e09fe6021874ea314230668c5636873cdab260544ef54aa699b524d4e4ffede7de036ef63172f07be6a43ab585195e9e9828a487f9c660b57aa3bf2c7d3d34a1c63f19db85cc61baae62828afd2727c0fa46dc6c1714847866b01313e48314550bae66489f45d8d21b45bad7d4fafd6a025a16d264335dd710ec3bec18a0ec29bf821ad77744bf298a5911400b158189c80b6714b1937523a6f5901af2b8d60bf13adea6c123cbbb2f539476f3fba7a109b1011c28424b04b4738919c2f83373632b5bb0614364a79b83d6531ab9b8c3c91e7a01104edf86770c41dfe7893ca3f3d19f3b6c880d8d8881174c058f9ef5a0a1ed42f084b88bfc9b3b68b5e9863e0f41ec64ef6e5919ee7460ecd0e51a9ad908568e61633d3a836838ce551b6ee41e4e70ccc0632a2469eeeb63e64b02fb1a3196cb4ecc72ddea9fe34fc0c59f3caaf5cafe4ca8a3f4943e051457145c75b1a9facf3428b112ee886ea8be963fcaca3f1cf9ac900f2e09acb08f81de720e74f31ef1f881c8e878c9c607f4e08466482db68c2815f2973fea5af7add6f46a2314801f3d27f95df94e0e63e5bbc34300008cf274a653139ec87badb0b9f857ca35b3aaa61a186af5f74be4a33f202166d9215944d57d6f651837fa490061da462000b8f0541859610cc1a492cd5141b242e715a9655996005c06b801609c63e6c405564b7105c90d9b00207e4d6b948fd6f6d5f2b2d43b84e777f1297f70837f531fc73fef55e7625c5ce73fb12fbedbd67f2ae714510041961b99a9efe6d831c515ddc4c6463cb1d3beb3db8a5b1f2ba532d3fb48a77e36642db0f2931d228382a2a70d2d804664b80d33a18d263c1498ab13441f5af3425415c4df45d2197c2b946aeff637a8e32e80958e6fc65012e3ea5063e9fdae8db37e4c20527e1d8eecb4af5288ca6d3aff17d835e278bbaa3cef05ec9fe1a8bf7bd80b8a0c7cbd893b72a406f91fb9c4b1dc6030c40f0406317e52ef7eec34afb0cf1fdea0a9227c7dab858da2ac8d5ca1044f9a3529215c61f777fcb07ea3b2724dcdba1357194d1a5798a81f88008b9177f971d86c8e9ec46bc77a162c351da212f31b25ef754590f21f183db688af0e0139e95df2d3aedb04800ba396f16a38af76a1f775f8dc56d79079c69bc90c82760222e692d9aad1008266d254cebdc820c3427a80fcc119e72257b4d3a31972cde29a79d12c646fe7911666466ebac5a4de19bb2800456a270d768159dd5c7a236adf4e29f168edf754ce1a1ba196cf9166e1906be182a9ae381cd6c6e74d70023f11c08e291358eb0058f67c5fac40136fec91572a64a40c242e9db1d0312e2496a1c9d646fda5351e351527286575415d3ea2d384f7242277d4b86764fd76b7c3006306f24f330ad39a8039ec78b798b2258e9f4c35dc80349ff60e77f1c0d532887d57b61fda0ccb3efb3227c8073e27b26a3037c3516e11e19301222c6887b5323b47554e66ea37f495ad1ca0e6084a394b3739f3ae93f4d524fcf8fe2cdd4144f238273ac46b80f5d25d9b25133b7862be49789eaf274bf36b9c616abde89f11ae0f10d2b8efdad42cf0588b9063f7620afd517f833726816b4da8cb17ace38f497a04d6c5950e21c8819a747714cc5ac5275bb67dcabc1a1456cc7555826df13cb09686b47caa80c454fd4c7b21f5d22ee312acb500f82ed83de117ba74b4fc13cd6cc310b850448891f32546f204dc74e35372911024146dbe0b56e60bcfcc04f3e567e1758660d266f518c55db5f7d8a4676eebc26f366841a35e90cdc731d2fc886655dc5dc4d2faec78d9a0230abda535a27f636c939aea2690a553c7af6a4bf997559c0c8642915c149f7ed20000000000175db8e17a8e4edb0542575aad550ed5bc918e1dea7937b2ed9fd12cb600308f6ee214949618c5e7713872af6ed6d45f54676d6f1a5ebc6fa45a47829346165f7547f8a6bc988e70251313d7ea1d2ed19d65ed6e48b0d15206654b4169e5de3f2f0f54dae74dec6037e5758e1619915d27bab4661abbd497a4a894ecca043b53861b6d1901a511be46665a8bb87816f832c4a889bf89c95ccfadf8ddce86f9138d9bc155575a311fb0a2701b1fc687b3cab86f407d8115ac532c604e5d9b0fd2a59af06bc4c01781061039ab1b5835e342cd228b31e4400c5572bb8bfccc9dc6a755856fd602998c8fd15bc078d867c6105be4bfbbd340142d7d1bc62e4be03355f71f0e141885a4653d815401f39c2bd04cd27d352a73f7810a2a9e48ab004facf2fed312c0c66df6d9cab9b9c781ad1d120f539f28d7eb2ae9d86cf93ef75338d96482140b9779611bc90b2600f295fb84a95d553e2811cd6c290bc09a56b225063087108e027bb75400"/4507, &(0x7f0000001040)=0xfd76, &(0x7f0000000040)="7f817c5eebc8aeee964a100a8551d0fb186194df9e494a8925e200"/38, 0x16)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x32}, 0x2, &(0x7f0000000100), &(0x7f0000000140), &(0x7f0000001080)="2acba66ae087d283e35c9997c4cbbc6e27c34e044fb4e33ec78d5a9415c701065cace86c81978d93bb2fd6d3a00a35f19c337aaeb79e2a782e8e7f28370a1f3b0b438488434446b53c97231c884bc098b6d03810cb6f90326813afdf330ed296033f6e01ab552aa6e6a0f89a805ea20b11530e7f910942b5371f0e170e3e866e0f852b935447f2aae7167a0ad17bed0f4041d8b54d23af9dcb4fa7026907f2225e7b425dd3cc68ffcf3cbef95c3dd405fc91b823967e97aaefb1d305641e0fe94d211808248231f6fe076b4346b7356d38b0794b8d32c9843c0d479461f6bc8437966ea1b805e427252e75461ee3220af728bdb0472d7f399a874cf75321b559fe03178ac4f84bfe2bf4143406a3bf0e34cf31959d76ee794def1364ccc54a6d6ecb71d5286a5976bf65ca4f64da5c6786d1bd701a66ac41e23ce6b7d089ecdd42d86229947813ec9f754f5e5dfcbee36571e5cf7d347749470f476bdba15f3d2e47f9f545507fa982acb1a307445012fbf61cff01edd11ae69c4048f2ad29c63d444b0eb170fb9ab02ea4d4a930c639e627a21cb1d637fbe941cd35a6b30e6fce96dd438806438b33d1ddcc9fecb1c80127d9f8bb24c1870b83598aedf8b1dbd4c6403b1c7fe2a67a1f81aafa6545b0eed822aa03915bf5c3e538f7eb5d1c663770704ff9f345347dfc75133ceb73f68df92a361139848ac7f814789ba97dfe09308e162a0376ff1adf980375e28c9124ef5f13cc661bd2717714a4fb4c6a7a2c042dd898c156087055c150df520e3c7f27168c6d1f24461d0fac06d403ec1c503ff711814b02b221b3271e9575e0730d6ea0f26c8a17a70ab6ec1b2a6c6ae8ae0cdbc55650b75ba6e0a60c36970f86007c4c49a9288f4736dc41c66a084aadbfe3639d968f818c49bc6fe152279fc6fab7f202addbe92a283056dcdfc96bd17d6bafe661f78c73930c5e5681674cc43f06a6a8b21839ac9ee3b110da7282b5086f28854de5caa727041ddae98f03c22fe346d1455f341ac689378fe980d455351a6b76f2f63c430b3ef00973d96d5ca81026be73a890108325fedf1b2f0dd171d057fd03793854c686f170b66faf7a8fc2532494c2ad65b1eee5fd2184ccdab33df88396fd5c19d9ddba5b5b31b9a22ba64fbb0b62469315b8e922469bd746d3eec9ce91524749749355cbeaf70de4440def3f3f01859d396daa7a3f4bcede7624f964ebb889439c1feaf2bee97a9eb8e0a1793c45ca054ab94572d180057d51725275b4029f4fad012ba5a1c670d17e7c537e0443d064c930743f6a3f05e29f5ce0877bc1c7889f788eb4d99dacd042623a2d1ad940d483da7739f7e0a47b0c38fbebf932c1bb585086b8552b857dab246f2a731fffb043684c0f87135540077af1f74241f9e5486a5ee0bfafbabb7cd7371c4d29f12684d97497bd81483bf28553aec4b634c4e260f19ecbccf80ef94bc81221c40144d72010de68674a8440cffe6fa4776928853298df636734bbb2d5e8fb15f1f320ff12d4a6b87798f1139bf549ae5096c48f8bcc260db991643ffe24da0716dc0e41880cf07567f51714d3231a42b500a07cd2711f5dccdcf3cdd538c5cb068afbbbb0c3e53e2864a6a3e6b83a3b924d3a5a0c0924c5e0bd84a468febf95d849bc5e5a0800a98de2c010758ed5c906f5f117f14e0ab54ab212513f4f4d129268d23e8c93b92043865bdc1e27917ed134e6b429e6aed10767a4f85982a5148879f7989a16f0c09fd844610ec206b9f4ab496db3499e3727d94ff8f97c9fd32b6b22159f463da92a8137262cbd9814da9d8433e32ff5fe0a468742982e06c7fd62d2a89371cc104e81007e21d115f8ec6c2948f5fbfa6e6c2a7a6786a144cfd9912fb2bcc1816037bc69d51d4cdaf25234fe5ff01c487f26d44001395a68f13f611ae98d6be7551b8b033596fb2854406aa39533f9c25f24e284da67f8b8dcf01bb18e62cf92a42aa69031c659cec3281f64d8760b1f16f8cb904cf3a1c39c812563f2442e4fc09c2691726e368f11dd22be50b325f2f8b67227ce00b4ced74fa32f5fc26c8e55842828374aa43a059bd604f2287f51a39ae23e3b7e9864d748d67f9ffa9cb82ed684b9164acf36a8b3d726f8c440b7b7317157c1b48cc64989597579e2f2b3de3c0979acd981f8609bb40b9aa4531fda56fbc4996159d87155ae205eab1c27d5a38553c03d84273888b9bb232e4cc6f795cc2d25ed4c4434f89db0919405820dc4ad31f4597ee485c9bed99a109d81311af339410d3a9904e378b9d8d387dacac762c6c655e81e1f2604447bda2c36b34d29844ffda91ced568541c3a512612aaaa82354d8d79c2f4518a0e4274133f9d8e5596519c26ec0b6481f76ad1cc496afe9de2c423c4f143345b5efbe6a3c095201668f97483ea5eeec64465ca39b5aedcd86fbf21af784d8ef2001d0e2c95a3277f6fe6f0f7040909791b5b50ebe7a07418524f4b8d4f62482318ba91e6198ec5b2921150a650ff170358100c5e9459af182cc4f8a56cc3e7a7066c8eca7fd904f5ab8fb27938bc86ef2982d5d736360b1d8e3c1f3d8a9a63c624a4559ee2e3d487dad42e398b0b2805156391f0d66d0da2d3be7f30539e0410a64da333da9e713a62b71f8520ff1ebd378e14e47acb3521a65ffa54e73a267ac9c08c886e4114ea55ff45baf1c82f9acd8eca73c85354ea8a62012a1095a7fcd36bea080ce542410aaf51ca676c074cb5da6a9a26bb642bb77b1f4b4c1f4b5a70f93f59beb6befea1886ab2e8f754e2e435aa5c057945536fc8ac09c7f0f9181973f1e03bf3c71a7edf3aaf6e79f897e82d16286f400c2a9bc573dd29860f46f07c0d425b646978e78c6b855551c887575b9f8defdbf7dce9758a41c4231afc0e9a7f3bd9fb7c2c082811e61a6706ed933d256412b26b986a45d352d2e61cd601b177f000d459fed567c25ca208025dc5ce6fd88c5b3f65fc87d798b4c88d9781596f1d73ed8279be2c8ad3d8e9256bbec568e1179b360c07fac6a59edc4fc8577cb00289da878ec7cbfd717a321c3c557b92f9e2a69650ee3e31c9337d53e1cdb24b39ed251627496f2654799a60b11623c41ad447b86196929d9a26f349a0fdbfa27dbbd5eef83c9b14af8973aae8d7b976dc0624e31b64000a4cb5493bac9096f90590e8119a533a5a7aef304515526aaa149ad6860c580c590da3c00406ea5c4045c5fda5a27c27ec6fe88e07f6a1c23856bd4a025c3d0b515cd899ad27734dff60231fe250c66e096490a2e96f61c8c424daa6d3ebdb29c78a239959070a9159cb073a31cb7f5b3cd76bdc6f2821529d6f9084ecac4e20f501cefa3b1b0857b48967420dd1874d2e3887dbcbe5a79da8037722f5af3eb6c1f7a7c0903bc83d8fe8ba2c90d19bba73ff5215bb795b3d53f64894f805bad6f9781bddf840d0ab1a85ddc72e71351c67ccd45b7269455fb31196eac7e9e5b8af89e28dab16feb9679eb955821b28b627be9935b0813f02a41d48e107854826effbe9bfee64b95431d51d1b1f391ab9bb0c04d4d3f92b5b0ac168e5c29bfc54647b00872d28814c72b6f9dd2780abb9d6b069bb1cb9d144e677012a296d1d8b73287f33d888ffd12bf71d6c48b3d4a7ce3a543d7c915d1d99f4e9787352296267d15599baf2e363b90018a1c152bb05d936042c31313420c9b90b1354dd90e338665d7b135fa43ea4f8d3fa0dd438a9b64a5397718276a7e5a4555f1ee757018ee25ba2c7da7016c9820ef34f0dd7509cec5eaeae3368fda1a2ab25b4fb177f1d46e56efbbd2ac32d54ca9293dd6d8d33ab701e2bbe1f4083a6d11d2d1af90ca51c8551e303c2b47d26ff19548606709c786317e969dc679a8f39ef618a15ef5bb68ae90e180dff153de7c06fcf5135a86a3e05ec33e05c4e247bb37c0d675c82ee8968c691bb6684f7e67f1d1c0b22a8ee056138335c455756feb24ece133943fdb3a35c0a7983b2924ce02255f8223b0c9c1a1cb03266bd322bf087acb3eee473838fda0d3846233adcfb02c68c83ed4701aabf0d6d8d8843d6aeae195f14037cf608332b9d84769fb6b0b5d1057d4310f46571c521ec956232d6464aef8a068ee400bee4c0660808b447bfe55550762f85450239a3443ba428d2df1a279eeab085b7a5a8c4b2b8b8ecece124565446892344971d1fc832cde9b73ee1fe528f0295ab393415d3c9fae8deeea45827353ce851479aa17ab7f449f4c8933cd86834390343b1a76c5054abcab10d4489eeadcc3652594551e55dc2efc62aa64abdb4383646cceefad22bebaa34a119cf19574a0bc21bf16e882be2be58f2c4aaac9b6bb07836fcb3d410ac80cd67744ae7ed2364348b0d1d910b8344757a8cd82e14bc7a7ec604743bda5d3b29a2566eeaf9586788f56843078c20af96ff2af0feedde2ec33059e03482210e2f49d325c47d3802b2146073fb77c9d01dd36c78a5c244dcfe61a65dd3a2de7b8a2ae2adc45abc50a20c026a6af1712799d957bd21cbc94047e5ad5c445445ea259a2ff0d4eedcc6f8e9d99e7bc3dc41ba72179ebaa3f436a44b4bb4c106c7e78b779ba4ace29a173dafdbfb782f4a07c635b5d70990dfb02bc7a17afcb6e798f29ff3c9b4c6d2e736bcaf491f0b8a50c5d87c61d0d41cb7f38d2c72b6afe066bd33a7e9f62dc263f4661d7291a33918b53dbdbeebaeb93d0f94c07e95774547346f6361131fa3551a549e97331e001e1ae356dff162c6317c327a124b88cbf313c6b025325c2aa8be6f2252a8b78dc93890fed95d74521f09346bed3afdfb3dbb9221c3eef45f16621f85b13733347fcc428327238820841d3b9823164867ec3605f5977531dc618d30c8538fff9366183e09c71147ea966dea4225a547ec73ee265d35043366785db57325a02af2e40384ae337dcae71fe2866e402e32749212bfff6abfaf09660bc627d126f3dd0e226af1a23390f358fc0e15e3f43c66f9fd1b862c069ee5e1f9cf0bdd1f1599c6b32a3ede8a4a7e88900b7f6801fbe86b852c2cc2eaf418a3d503555f3090672f58ab9636ec8bd90b980a39e76d8a8227cd0d54f2ebd3fff0c7b10f8862df7c0dda7137db955c12f706b1c0fefbeed8c11b6f6d457dcda9288ac1946f701e68922dba116de97a927165ad59f48e8451b27a52500830a770242afaa2e4191dac5430803cce6f5aa4c74d6ed369c1d3b3f1d7408d6b0456fc551f1a49392e999cdd66c2ee23bc21bc5b3791a5042ded3acd087eacf4c079af738bc62d59b1c5f7837319274c12d26239a8a795a7b7c139223dd6ba72a0a5dda924430a59b7f80292657f38952d047c07d7a223fb73f56f67390518b60cefe1a554fe6880d5755400d344d8b7f7cfef3dd7972b5031550fcf50d32b03bd6701776269a9fb2bde3da2ed9cbad766ef3accef10c81665f877f296e4504329869306bb73f5b206b792485d72e46e93cb841bb8792bfc3f5a786eadce3afc92683359b3b60adffa764fae9997c14e199a859ca5b7345b0f507525c5d594079babde86d7831ffb97c7ad16e028114bb6e9e48fb8eb79386b6e64e0b3a3e15fee02afcf2bebf61677d1f1dee53e107eddda886897993c84b1ea9ca0a416f8208fd6da7aba59f44dcbbba6635df757fa791288a8b7f7b36847ad03b325a4dd064061e8c4493e58ccdaec30f564b0fdedfac139fad4bc6610a21fe448985b16add70965f9389005b3fb175b87a542dc39fde7765f8fa7240dc8c1f942372079eceb5e483ce34acccb8b244903440f1", 0x1000)
semctl$GETALL(0x0, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(0x0, 0x3, 0x8, &(0x7f0000000280)=0x80008)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r0=>0x0}, &(0x7f0000000080)=0xc)
r1 = semget$private(0x0, 0x7, 0x3e0)
semop(r1, &(0x7f0000000080)=[{0x3, 0x1, 0x1800}, {0x1, 0xff, 0xe17b725e3710487a}, {0x0, 0x8, 0x800}, {0x3, 0xffff, 0x1000}, {0x1, 0x8, 0x1800}, {0x3, 0x1, 0x1800}], 0x6)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000400)={{0x100}, 0x1e, 0x9, 0x7fff})
semctl$GETNCNT(r1, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000240)=[0x3, 0xfff, 0xff])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000380)=[0x95a, 0x0, 0x9ff, 0x96, 0x8009, 0x8, 0x402, 0x8101])
semctl$SETVAL(r1, 0x0, 0x8, &(0x7f0000000180)=0x3)
setregid(0x0, r0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000000)={{0x5, 0x0, 0x0, 0x0, r0, 0x180, 0x9}, 0x495f, 0xdd0})
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x61}, 0x2, &(0x7f0000000480)="f47facda2d1fecc7734f13b90f07d399fd0b387e4e413b2bfb4dc5846ddeefb45c06edad9c4edab4efa070b5646b245637ddb40e3e3801cc34427881371bb23bd8d124127fa3229dc318946ccb6d24e036cdc984abfba728587fe5b49578f259895cd9f13762a8d4ce0326a8942a016f357791d2e10c1cc7a15d109171659be1267cb27bbeba2fb5e0b978d7e9c6a0e17154f06f76ad1d1bd17dfe52270a2ff3cccbff1904c29f24b9876cf4a98b730045459f52be119a52699c4f014444e2", &(0x7f0000000200)=0xbf, &(0x7f0000000540)="016170b1f5c6300bafafafda808d5fe028c7b656e71d20123dcd2a18a1848ebdc8b981cb05d9ce429665385698f4cb1b63df006222487a5e896ccad0a3f7c4dfd3650981378e80fc", 0x48)
setregid(0x0, r2)
semop(r1, &(0x7f0000000100)=[{0x1, 0x8000}, {0x71cfffe2866ebdce, 0x81, 0x400}, {0x1, 0x401, 0x1000}, {0x1, 0x98d, 0x800}, {0x0, 0x5, 0x1000}], 0x5)
setuid(0xffffffffffffffff)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x36, &(0x7f00000000c0), 0x4)
madvise(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x3)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0xc003, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x8020690c, &(0x7f0000000300))
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
pread(r0, &(0x7f0000000100)="6ff6d79c7eb00b87eb5cc6d9b98c07aeddf20f1a6582aa87f42d2b8b291822c389bfdd1a61f0ce936b20977eea98b89f28f0c7b63c09eb37ebaa203b0efa295caf23a80acc536d6c4a914960890317ccb9dd6194aa4a85579436513926085eed25f082a193ab6e00fc946c15083628c79681275c00ddc0aea4a0be6cfa1ff39ecb4c02fdd40449b35e903b54004bb70b7b7cce28e75a438e2bacb6e3deefba679258da50cf15d1f54b604dbd8d495ecab8a90c2a86517ca5c13b3e4d6680330375b79923cd06731f2a524f0af91881df76aaf1f4e5886833e4542910a1945f8eb0ffe6158de1ca8ddeed0654b0d340cb38be19d3e36ef8555f0088c154e25d69a67edeba109e48ddafae1e727a83fca220e12a7b656d59d428dd2a68632bab2b93d21afafe5184917a55bc4a7a65698a50313fd4c1cb78d8855e10b95de3907a7e39c0fd77211e4e93fc54e1b171e050b66573443c3932d4dd17efd7684aac6eec5402067e5382fc589932c37a6462187446d152a3f7a189e0df2102eb7c5c54a2dc63d4327eab89712b6ea7b9e23ff9bfaf5baa8121a110dba3b2163d7b6eafa17c761db9b3bb7b8da347a60316d33a1842ecf3f0460cb170de56fb1e159b15a91f5a16751d6bff8b0bf4abd2a36093a9d22b5f21f5b7be33ed302761bd16091eb0f9777672de30b52c89cf33b6df2c9f56e10cf5b6215c979d4ab1319a0a67b5e0c8f80e73951caa929f44b47eb5418a709a5c26d789ad4105024cb418760a7f5daf6face0890d4febd6f9b5744e304d9a8da6e93315200e77710faccefa2e59824a2306f7cac0cbd7e34eed2f1e9ff41f45a6dfb554c201f250038aa1c250468d01f1f447c11e30a3f0a96370f88a61fc6e678929db762983255bdb9c27ecd852603343728a0ca9785d0b9ba31c85456992a0dd1db5973392667da1e33eee1d68ba28feef8eeba125043b1a5448278c021121683e0b50b2dfdd6daf6104f82a9ae3809f5e470845ddf45e3246dc3296d17c5af336cb10a04977eecac6422bdfaccfff636aa9164f6f42307219c89285edd9e0d2b90109dc6b1068bfee1e728579a7b021c7ac6dbce45bd7fb9064c311c10a4f498a185758e8e005c4e7812bf82494686299f7b4ff910ffc6435f8ecb4705195f714136d6179fc788e1403fe2f33b01b88b3c0f30e15cc21e2ec9932ed72562d92fe4c2ac0549838304a98c51dd2d95644279203702065ca878643e2317008675f4e1ceafb5b0c54d40f9b8c5d7bf66cb80dba55cbe6cda0c14427781d182622ffbb4a106aba31bbef2b1e9abe05670ac1fc6c4893877318ef8133f45390acf76231c36b0391fc6d85c6c3357c8e098f74ea3b63e9e5a257d2117a783efced877740e727e767e0dfdb8f9b7e0efaa5aed99ebf3d3b0ff131d00ac001db3b575121057a85ba01857face1c86a0ffc0b92948e358fad2412137f922837b4716d98f62e781d4c12e079ed97a50de26cd7806cb489a4634c93b3a9247b2bfd2383b55e1a582aa130b870b97a3f169cade8c808d644c89aeb492f2e6d41a255103fa6d1aa0f589058c7febced544f13b66b64a1271af7bd5f994db6e68c2335b82e002eced4d409726c19e3fd90c340e1e0b547e85b6bd6d254c1c9233fe827eb22e0a0cd2ec9cd3cf67a6ebc7380fb683b2c1aca7b16e74e081eea0dca12ba04ab46bf15be6e3be48bfdbab2d179815582a13e234ff40ff43a60dd7e512431eaf692b8506165e70f417ef4fafe52ca9f3030f9014c91a045737b83f714e1e8a19a83cc8f35f3cfa28d92150ff8dd6a96635541aff11caabe3c9fd4f408546c201d73d0a0dee86e8a92c175838a52d198f10cf63e36befb407112c0c9c93cdf5ec6cbb638c52aeedb3b5e7aeb505bd7cc23e6200731a79ae4602b89820dc504758eba8109df2134ab54081e4de0c61cfdc0616d7e17cc3ceb30c38e20b065101d05b4eeb97e72df9e655d3538118547ca21919a2582d465186fd04fdbea44339276d7c95a55bde5d24ae2d6cc216f1cd706aa9460c13cdf9a5ed86608ee61f35195cd2b67f53c6b8086396883d2e8e4945af06e52192e8ea0ae1384fe9503ea8abce1ef5a0d4b4f1b480f70790aaea02857ae9f73a62c4cb5af02c21b721b6af1c12feb4412b74bd10162a7a192d3e64fab5b7f38d8c97a43bb7c1448034c6e168dc586051af4ada72cc266972984e275b0925e0b04c8cd11e264d3effcc84fbe1f694cde661e26db204ca15c47d736840ca06976760039df0c0b683bed2b972f1e44eb9b8ceba81f630c0c52ed1cc10f082348b7988d4647bfd49a468494388995b78fe69b64de1d651d5a342e92d1e89e7169785ee2bf73510e82b0640ea9ca807facf25024051e89284b8ef286f21c5a7eae0e23f0a92e027950186e767f5c7ed854a4b95294a6981ac5ba5701175eb536f5455eb29c04cd3c1ff5a708a1139488be422623a0faba921014c1d5e4ca3f04c94bc6e31da3dc1133b0623199c209f77ccd3d7369422f1dbe369c6e83a61edcfa675bf90554efec7071b44dd791956b713da713ad0641752ee5c285a1f0b8cbe9d6f929e8e8080940b8bc5b6fd80769e3c071ecd70e8fd75fd141c6677fef196b996f06639957a344b63d3078d107d8309f6945a3c672211a611957c2fb845ff4ddb737aae5bf380114a6593cdf83d8c4269f0718d90934572499562d2a958bf6c6a596a4503026e623700df198b355aa2ea24cbdfbd2ccf9d30c3bdbcdb1a187dd9aea08654913ab5853f6fc81d6dc888d30589bfd3620c730de8a7340bdeb566c70f53dc56a263b7620681f1c13a4f074b83e5101620275317aeed3c2a42ead41178c3b0fcb096dfa59f5675ba2095f1f08abb66886b3dd541f7e09a06d9d", 0x804, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
fchmod(r0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010c8e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f0000000180)="0813", 0x2, 0x0, 0x0, 0x0)
sendto(r0, &(0x7f0000000100)='A', 0xffffff42, 0x0, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
recvfrom$unix(0xffffffffffffffff, &(0x7f0000000040)=""/173, 0xad, 0x0, 0x0, 0xfffffffffffffd2d)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
mlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
msync(&(0x7f0000bfd000/0x400000)=nil, 0x400000, 0x5)
mlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0xffffffff, 0x2948, 0xffffffad, "9190000100120000000092ff0000ebffffff00"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa1fabe9", 0x47c}], 0x2)
read(r0, &(0x7f0000000040)=""/35, 0x23)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000d40)="2abace18112030b738b1958303ac3fc1c384e67774d4bd49660d81fb4e2e9a19a972ea508fd17e3d0a932587a0980ddae58d2709a47c4986c8e74ed0d85edc5aca224a44bf11e4e1a51c0f08aab643577c975bf1928b38f6a67e0c5846b4128483e6275d201fec16e9147865784c167c704c6ae0980bb84e8dd18d6f4c83abaeed4ce23fa84033c78e9ce083ed4b575716841fd249fdb120f15b3db394bd37d3ecdf5440737d32fd4d75ae88ed351c1a2c8b9fe3a7773a6c3c322eec1e8da0864d79d9ca12a609b61fe0f6f8b88324f93cd6067a05d71c69804f02315e320887de9e7503e2645f1d9a453a0f770a109347ba8e025f6654809908b3d640309334db564b52daafcfe22cde8ad112ca5fe8d81a335656c43555aed4ef5af976b8a30d39838eb9162e58f82c048091b6f2a5c792c222520cba0fb1a3adab11c5adf10000000000000000000000000000e9bf0b9b675be513096c198ac5303b35c3a060a331662bf508cc3b6eacf17750ebe4976e7239251b8559a57039a7ad3a3063257fce807eb37d29aa2654046c62e343be2dff20fa332dc701ac84f3191977476cbcfc2bcc335332a1f8ea95fa9f440be83a78ebdd60dd81fbbf6fa326d6d84c9a12c84d037541b12aa9a2faccae86f50372af64db4ace9044ebc45d64e98a20133bb56a07434b3403ad089f99c949cd0efbff2e830cbb5146c58a27f83432998008556069bc469cba6d83bcb12f1f65365ec1ea5e25a6258f284da980074edc59dbc279cba0c634a2ab848edc44ff4b26b6f61b58ef1264e7d58d6cbe1b7fc6c914d69ca31a84b6eeed94229657d44bfb4e2e4b9797ef7f6af863f1c1c7f7ae9a45995936baf9d11f3bb2a5eed1ca573fb9df42aa20fc7eb1832c2cec4d048cfe862cc03524b4a0efb020cc7051e4d1efee1d972ff84f9fe5730459", 0x29c}, {&(0x7f0000000080)="051c92fc1af91e51429a03162acb8ddc3a8f9d3b588cc9073401033d1119b4b141df99877a601a4619f18708a668199c3cb370", 0x33}, {&(0x7f0000000500)="d19e767a41270df7efd1b0a722f3051b176adeda300f91598ce0cb7aa9f36b6b570addb4ba0d744f7847234d62b706e946bc00d0085bc41b628bef1012d63e17a7503ffd07d8e6f6af567c66bc0bd3b21f85e4158965b70b2a43893179fa797cef36a70f0a880fd350959d4cbc96edd6fb4543b2f72ca5e295ded234df016840d1528344030f9de0f3bb17f7fb8643fe16c6cdf4f0b21cba0f63abcd9c15069f0d31413b9201eefce6c8b288551f5a", 0xaf}, {&(0x7f0000000240)="04ba5914c8ad15b694e75055b2095b4fbfd8cd9b516a28f8c07afa13c45576343f7144f02b38f2054ea3cd3d71c76e00000000003ccca224abd7f2fcf20f67fa7efc3807212db9a210f79f62407645e16fa995ffd447", 0x56}, {&(0x7f00000010c0)="37e9fa0141061db61b1ed10fbcbc40cc1306795d552e7fd645b1c8b46d209d6ae2a5de39d5c69227234925c32c602ba5157794dc1dd2ddad84455ee91a95b2f0cf1ed9a285c75b0000897a04c9acfe9c11baf3865149814461b156130a7dc3d86b407414fd61a1ee8dc9076d494132173ea614d4ea3903c0560b7e8f2166eb8fd7c11b3e117d84cf4ffb4dba38f98304cfd173aed6296b5d71104c69396100450e6824c3fc19cf9b736eaa3e33ce3b82b81e27b25b92b6f1262ce8bf8ea5c5f8e6edeb595115de33ea233b8d78750fc209c9e7a94db407751f2cbf5f99b5649557393c3de37f1f302baeb0932f1edd688f50b1dc13f962ea86d03bcf0810de0da52ced341260965444ed62d151fa76d2d42c03ab7e0d3a88d7392a8179ca0c2db11ba6a340f9fd6a3e2821", 0x12b}, {&(0x7f00000002c0)="6598427136657df761aa5f5719147df674e9c995d0884d", 0x17}, {&(0x7f0000000300)="6dde29b7cb8fc8b7c98116976f83046b3112772de36bbea9efe0f5db50e300"/43, 0x2b}, {&(0x7f0000001200)="02f89c755da165fa2c5b12bbb9a6a7f01c4f715a5010d60e943461faf212f6c801b44db43cd4eae3440ebc20d8eaed033c5472a764c093e304422f60132ed5961f1656b310a45b2821029ce0eb4c53cf6eeec8c5d342bf4f9bc6992120db74fbd9f3dca2191fefbc3f50dede6f3bb218248cab8c2d7fd147e1e6274fcf5cc17f3b4f8bd7a7049d0efb8ba8e4571fbe8e02ab2f30a9540bd1e9e0bc8fb28c769265fbea98b5bae66521ad86676fbf3f262b8577dbf3f1c8cef8f4977f32bd8882f879d15e3dc0d66ae5b6275abad0ac8097485afea6d24c6e99d1e805c1fe4ad3ee5729213587ef7468d90afa43e6f91e18456186", 0xf4}, {&(0x7f00000027c0)="d299f185fc18849566dc679478f0e005b6e1cb9789d657605107fdc0c92368f9a7f783f22b8f4fabef71b991e8a0b0cb588da78a836d11135e1d5bdf66ae22a7ec957dbc6fc7288044e9a54d9602006d108fac090660ee5e767441af0263d66924a4041cb22cdccc0cfcd76259f2382e9ce969af7c0172da9e28fd1da47033f2c0cec804010863c3b9247b53bf217eced8422d5f14a85d3036cdb6587e792cebf8b081d4e39607a4a001", 0xaa}], 0x9)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/\x00', r0, &(0x7f0000000180)='./file0\x00')
unveil(&(0x7f0000000000)='./file0//file0\x00', &(0x7f00000002c0)='x\x00')
chroot(&(0x7f0000000040)='./file0//file0\x00')
chroot(&(0x7f0000000100)='./file0\x00')
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x4, 0x0, 0x0, 0x0, @broadcast, @multicast1=0xe000000a}, @udp={{0x0, 0x3, 0x8}}}}}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
write(r0, &(0x7f0000000200)='X', 0x1)
close(r0)
syz_open_pts()
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
write(r1, &(0x7f0000000000)='X', 0x1)
close(r1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x3d}, {0x3}, {0x6, 0x0, 0x0, 0x8000000000100}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000140)="0000000000000008cfccfbcbd4a2", 0xe)
kqueue()
r0 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r0, 0x0, r0)
r1 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r1, 0x0, r1)
r2 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r2, 0x0, r2)
r3 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r3, 0x0, r3)
syz_open_pts()
r4 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r4, 0x0, r4)
r5 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r5, 0x0, r5)
r6 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r6, 0x0, r6)
r7 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r7, 0x0, r7)
pipe(&(0x7f0000000040)={<r8=>0xffffffffffffffff, <r9=>0xffffffffffffffff})
fcntl$setstatus(r8, 0x4, 0x46bfb)
r10 = getpid()
r11 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r11}, 0xfffffffffffffffa, 0x13}], 0x0, 0x0)
fcntl$setown(r8, 0x6, r10)
r12 = kqueue()
kevent(r12, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
close(r9)
kevent(r12, 0x0, 0x0, &(0x7f0000000140), 0x3, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
mkdir(&(0x7f0000000440)='./file1\x00', 0x0)
unveil(&(0x7f0000000040)='.\x00', &(0x7f00000000c0)='c\x00')
unveil(&(0x7f00000001c0)='./file1/file0\x00', &(0x7f0000000240)='x\x00')
execve(&(0x7f0000000100)='./file1\x00', 0x0, 0x0)
pipe2(&(0x7f0000000540)={<r0=>0xffffffffffffffff}, 0x0)
pipe2(&(0x7f00000005c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
poll(&(0x7f0000000600)=[{}, {r0}, {}, {r1}], 0x4, 0x5d)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x1, &(0x7f0000000000)=[{0x404d}]})
syz_emit_ethernet(0x52, &(0x7f0000000080)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x1c, 0x6, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x3, 0x2, 0x8}, {"dadab14379d467b8c02ea79874b404287bd5c970"}}}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
close(r2)
accept$unix(r1, 0x0, 0x0)
shutdown(r2, 0x1)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3d, &(0x7f0000000140), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x40}, {0x44}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)={@remote, @remote})
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="140000002900000023"], 0x38}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000005c0)={0x3, &(0x7f0000000240)=[{0x45}, {0x4}, {0x6, 0x0, 0x0, 0xdffffffe}]})
pwrite(r0, &(0x7f0000000600)="895e654fdd1d036c592a82c32cff", 0xe, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000), 0x3, &(0x7f0000000040)="369b9960", &(0x7f0000000080)=0x4, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
shutdown(r1, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0xffffffff00000001})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x6, &(0x7f0000000040), 0x0, 0x0, 0x0)
setrlimit(0x2, &(0x7f0000000000)={0x60000000, 0xfffffffffffffffc})
r0 = socket$inet6(0x18, 0x1, 0x0)
setsockopt(r0, 0x6, 0x2, &(0x7f0000000080), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0\x00'})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000140)=[{&(0x7f00000002c0)="e8af181c01425424fed04b173f6ae005baf3747e5e92aab7c51b0ece4fb9313fbde5b3afdd129ee58611d531b79133ca15954e0c207beddf519053f062bf4459d9372d9ec6a35c090e68ac5e77f32afd405cb8c6d93608175ca3e7ddbe5b5c00080ab1668b5603139be3701995fbb422c9b5a9bb182e9f1d9b8dbee01c6d52d830e354c8f4617587d8c7b25cc65a234da324deeb63cdd92dd69ecab5571ddc9b1db54123588cd1773a1c6a0992a5d355e854d392994a803ed8cc8ff0bdfc8306038c7abf74b928b14b2c2ea189861b30c292f790b359f625374e152605c4ca14f05ae13aec65360a61fdbb772c6e5ea180c6d60f8d52fcaa801b19a0d6561cd62a4d060df3013b26907e6f41279b7ff8d6090fd0a6fc23c8bb0ed5a1fbd3ddf7515ee08dbc2e6623", 0x128}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = socket(0x1, 0x2, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x0, &(0x7f0000000080)={0x18, 0x2}, 0xc)
pwrite(0xffffffffffffffff, &(0x7f0000000800)="055cc787171f20b652bb01607f577d50c2a851f0f1b007afff3c57d83668da3ab35628f24bbb2700b4d4aa7339d1532109a5c4de3c72dc546affae7126361ca65ef03e9209b1df284ccef6a599c354e6bddec51cd3fd27096f845eb24282b3d5129600267744979d92bc280f29ffab6917b41571a81c7604fbc76166308f2c729bf8059a5c5f569365e622c097fcf4be89def98f34473c10e6a303d41abffc0a0a4f0d069ad9de7528c7deb2d962025210cbe9f7e09ba93fc4270bdaaef38056f229e22ba255cfe3248d41ef2c40690ae656a72a1fd905cd93f7b60936f9e7455a40c03f2f36e05071f24f0909759fd7493ff552b125c3a2d3fc99d51b448d19d15d6862bc0cae0d35654a4c44823ea44919c03f1cf061696b8a905366c62eedac4045fe52838b33082c9a6e08588b91d0cc5693bb872962a8a3812768afa87059dfb57aba0717676de7e376fb59fc69f02164475f78d94ad671b424697e5cfe2c0f60988708f7be7208176d4682f8d39b7fb5460b8f8b0e8944634b5340d1d9369825b8d478e93618660490b77187cfcaad226c22c346fc8ba04c9c31168f2e9c7240fa37e2014e3e2f25f667abd10c7736d72b05400744c99cbff31ce4", 0x1be, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x32, 0x0, 0x0)
dup2(r1, r0)
getsockopt(r0, 0x29, 0x20800000032, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @local={0xac, 0x14, 0x0}}, @udp={{0x0, 0x1, 0x8}}}}}})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f0000000180)={'./file0\x00'})
sysctl$vm(&(0x7f0000000000)={0x7, 0xf}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x2c}, {0x20}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@local, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @broadcast, "", @random="90897f828f31", "51185dff490d46bdaea025a57b3ab6cf"}}}})
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[0x0])
preadv(0xffffffffffffffff, &(0x7f0000000000)=[{0x0}], 0x1, 0x0)
madvise(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x4)
msync(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x5)
r0 = syz_open_pts()
fcntl$setown(r0, 0x6, 0xffffffffffffffff)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
r0 = socket$unix(0x1, 0x2, 0x0)
getsockname$unix(r0, &(0x7f0000001080)=@file={0x0, ""/4096}, &(0x7f0000000000)=0x1002)
sysctl$net_inet_tcp(&(0x7f0000000100)={0x4, 0x2, 0x6, 0x13}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
getrusage(0x1, 0xfffffffffffffffe)
sysctl$hw(&(0x7f00000000c0)={0x4}, 0xa, &(0x7f0000000280)="9a60d4bdab85f818397519b7a27e6ef68ac9594c19fc29554d33f86006077f544968c1705f1e68df33d558d5c2d4baa688a08ce8dabe3465d4e4ef19adfea47fdd05f17e83648da15f6652ba1f00d2e4fdf7c6ac53e9360514f9600908ddf69b6b7eb3bfff17f59cd3147d846a0045faef9c2f8551a8712c6a08289a0454226908930a1100"/144, &(0x7f0000000080)=0x4, &(0x7f0000000340)="6d7115dfe5867baf0386804813eb244d9e608ff4f64a319146a4fd070001f71974b69f200146f543a0c44629aec6aeb1fdc7bf259f1ae775b9cce6c42a61b0eb071cf8f305a3a9e049a3e1f51a8810a094ee3a05506260974f28340bbf7b7fb76a52154913325b706d8fb3638d438834a978ed5cf64cfa105152cf9501ecebf5a3327894a02ee35822773344d20ae417fba7ff057a24e64008702a8fe12e8450b9556cd2f8e3cb10d90642e098bebc5201fed90000006c4f06258b1e056c0b99aa4b6b8016473aedff4f3cdb77d6e1271f737b313c4cc66984474d9d5b55b50ec21905d9193e1e61e9f3d1645489241de067b05410d05b5a21c75460d126ed926b493440352d1589253d5eee2aa30a084b3d074e646fd18b010c95", 0xa9)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f00000000c0), 0x4)
writev(0xffffffffffffffff, &(0x7f00000008c0)=[{&(0x7f0000000ac0)="45eaed4b18c25b1127b2776ae38c76e34e0f3308d4bb61678e9474b52037554cccbe0d615510f36accad14c90b5f21f2b0d642bd269617fdfecbe51ba77f4f4d42858cdb808a48dc04", 0x49}], 0x1)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, &(0x7f0000000540)=[{&(0x7f0000000340)="3cd81be7539fc7322d5c4b604f6b09f4331d9d65ee4b9df62f9880c4487f39b2da72b4a23d0f0d653b6e662c14c3ac4044833f4b31a78e5c3dbe49419ea830cceda67382df9a7e6d13fba2afaea9241374b058c97eef260ae831b2b11cd2b1069f42222a58debfcea4f6015888a5b967fed8c3a7389dcff2d5edd0527980ac02e50c4958fbbc9388ee5342c2068c921596b495cbf0762dfd1f180c25ece668ab6d61681b524c7c9a5a0ed76b318d39e14572d359cb39725b98a51d5d56c72a290897d4f9639950a17638613a89", 0xcd}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000000))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$getown(r0, 0x5)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000500)=[{&(0x7f00000001c0)="adca27f3af590071e61a3a5cfc9f76251f5d50c0a7832a27654beb5283ffe7aa80a74c23d371900138fe6fe0e357e4118c4ea6b4f34f2261847ff2ea6f44b62b2f2eb9caa8554439e8080e638a66be3e65a3", 0x52}, {&(0x7f0000000000)="35ad518dbbc7d26af897dca7", 0xc}, {&(0x7f0000000240)="ef3de13f69e113e9adcccbc0009d9361185e30be063513ed9d16fbd9b184e305a638cd12f121f2d10cf9983909ffeb6524a4fb2e3c5fa16a49ae39d44fc3d13472c9ada987d37ad854467fd9ce4ff83a0a", 0x51}], 0x3, 0x0)
pwritev(r0, &(0x7f0000000c00)=[{&(0x7f0000000080)="c820a0be6ac42fe8e80553b47d91e7e61a7959918ad885c95dbc5b8e5b1d8d37c36e92018e0741eecda2cc2836719a03d07fdc7e20de3bbd6ca9c84e3a27525a43de6c7cb4376042fff051103a5cc0c1e4ca6a25de8c2a34535f315a6751acde661a42576ceafff24e2f482fdde0f61ce5ad3f3b51392b8de61b69cf0cb7903731188bb9c483164e531813b08884d8120a603bdda21a083b7929b5f45f05bf0de4888dfbb2a03fe60f9d3746", 0xac}, {&(0x7f0000000180)='6.', 0x2}], 0x2, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x34, &(0x7f0000000040)=[{}]})
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x18, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000040)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27ecb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1abda71601a8bfee8aca4911faff5a872c881ff7cc53c894303b22f310b404f36a00f9000fcffffffe608a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
recvfrom$inet6(r0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000480)={0x0, 0xa, &(0x7f00000001c0)=[{&(0x7f0000000280)="f1b338fbe6925e775b2e2f9c46a9f3c3006154dc52beaa94cc6d32bb270f3fed90effd74758dcafa117d12616c628b810e7dac72285b9c80c4dc1ae113a9504b1ca1fd6385805497a97b9645d584519fe475bd6fb6edd00dc85bb7576e9b900e685443c2d6502746ffc0bb55f5f9f248fd0bd2d23e2ed801878f44e4f08f073ceae982802b56661ea5e5310e5722f98b6c62da1014d87b21d9cf685457e623433afd53b366aa2caacb9ebe7b5c3e685122e206d51ed5bf1af6eb84", 0xbb}], 0x1}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}, 0x0, 0xfffffffdffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f0000000080)={&(0x7f0000000040)='./file0\x00'})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="d5e8aa17391829b02af464ece47a36e1a1cd00006b5700294093ff8298d91a42dfc10a9ac33a946de3e153c751ac93c979a740c36875e4df0656b612cc7a8f787b8fc366633118c33fe878a34bbd9fe6744f15f3824ac7d042a010635987ac880bce4845f3a6aaf8ffffffffffffffdda0a56bd0a3515d1d5c64f3ec5d5d017d00", 0x81)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000080)={0x0})
r0 = socket(0x18, 0x8002, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000000980)={&(0x7f0000000100)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000000840)=[@cred={0x20}], 0x20}, 0x0)
sysctl$net_inet_etherip(&(0x7f0000000080), 0x8, 0x0, 0x0, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0xc}, 0x2, &(0x7f0000000040)="0656aff1", &(0x7f0000000100)=0x4, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x3ff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x45}, {0x14}, {0x6, 0x0, 0x0, 0x80000000001100}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000000)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {0x7}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f00000001c0)=ANY=[])
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
poll(&(0x7f0000000080)=[{r0}], 0x1, 0x0)
poll(&(0x7f00000001c0)=[{r0}], 0x1, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x100, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
dup2(0xffffffffffffffff, 0xffffffffffffffff)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000200))
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
r2 = semget$private(0x0, 0x5, 0x28)
semop(r2, &(0x7f0000000180)=[{0x0, 0x1f}, {0x2, 0x9, 0x1800}, {0x1, 0xe96c, 0x800}, {0x2, 0x2, 0x800}, {0x2, 0x7}], 0x5)
semop(r2, &(0x7f0000000100)=[{0x3, 0x5022, 0x1000}, {0x1, 0x0, 0x1000}], 0x2)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r4=>0x0}, &(0x7f00000001c0)=0xc)
r5 = getegid()
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000002c0)={{0xc, r3, r4, 0xffffffffffffffff, r5, 0xb0, 0x5}, 0x1, 0x2, 0x10000000082})
getgid()
getgroups(0x2, &(0x7f0000000000)=[r1, r1])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x35}, {0x5c}, {0x6, 0x0, 0x0, 0x108f}]})
pwrite(r0, &(0x7f0000000340)="bbc86460e2440102f2c74cd58cd7", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001600)=[{0x30}, {0x81}, {0x6, 0x0, 0x0, 0x58a}]})
pwrite(r0, &(0x7f0000000280)="d000"/14, 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000240)=[{0x61}, {0x25}, {0x6, 0x0, 0x0, 0x7ffffffc}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{}, {{r0}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = socket(0x18, 0x1, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
socketpair(0x10, 0x2, 0x81, &(0x7f00000001c0)={<r2=>0xffffffffffffffff})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x1c}, {0x25}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r3, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{{}, 0xfffffffffffffffc, 0x64, 0x1, 0x110, 0x46b}, {{r1}, 0xfffffffffffffff8, 0x40, 0xffffe, 0xf56, 0x8}, {{}, 0xfffffffffffffffe, 0xff, 0x2, 0x40, 0xff}, {{r1}, 0xfffffffffffffffb, 0x89, 0x4c37789028b5b6b, 0x77f, 0x5}, {{r1}, 0xfffffffffffffff9, 0x55, 0x2, 0x3000000000, 0x7fd}], 0x2, &(0x7f0000000280)=[{{r1}, 0xfffffffffffffffd, 0x1a, 0x1, 0x4, 0xb}, {{r0}, 0xfffffffffffffff8, 0x14, 0x2, 0x2, 0x8}, {{r0}, 0xfffffffffffffff8, 0x2, 0x0, 0x9, 0x4}, {{r1}, 0xfffffffffffffff9, 0x0, 0x3, 0x0, 0x1000}, {{r0}, 0xfffffffffffffffc, 0x0, 0xf0000000, 0x804, 0x6}, {{r0}, 0xfffffffffffffffc, 0x32, 0x40000000, 0x8, 0x3b22}, {{r2}, 0xfffffffffffffffb, 0xc6, 0x2, 0x7, 0xfffffffffffff800}, {{r3}, 0xfffffffffffffffc, 0x8, 0x2, 0x800, 0x7f}, {{r0}, 0xfffffffffffffffc, 0x40, 0x1, 0x6b76, 0x8000}], 0x200, &(0x7f0000000200)={0xc88, 0x6})
openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
setsockopt(0xffffffffffffffff, 0x2000faa4, 0x2f, 0x0, 0x0)
r4 = socket(0x18, 0x1, 0x0)
setsockopt(r4, 0x29, 0x2f, 0x0, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r5, 0xcd60441a, &(0x7f0000000240)=0x6)
r6 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x2, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x1000}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x8, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x0, 0x8, 0x102, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x6], [{0x4, 0x0, 0x8, 0x7}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0x2, 0x1e, 0x2, 0x1000007}, {0x7fff, 0xffffff7e, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0xf9, 0xe59, 0x10000000}], {0x6, 0x2000002, 0x0, 0x8001}, {0x0, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r7, 0xcd60441a, &(0x7f0000000240)=0x2)
syz_emit_ethernet(0x66, &(0x7f0000000040)={@broadcast, @local, [{}], {@ipv4={0x800, {{0x10, 0x4, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @remote={0xac, 0x14, 0x0}, {[@rr={0x7, 0xf, 0x0, [@loopback, @local={0xac, 0x14, 0x0}, @broadcast]}, @ra={0x94, 0x6}, @ra={0x94, 0x6}, @generic={0x0, 0xc, "383fecdc1cb9660c2f1c"}, @generic={0x0, 0x2}]}}, @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0xc}, {0x28}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1b}, 0x4, 0x0, 0x0, &(0x7f0000000240), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
pipe2(0x0, 0x0)
pipe(0x0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000100)="39f7", 0x2, 0x401, 0x0, 0x0)
close(r2)
r3 = accept$unix(r1, 0x0, 0x0)
setsockopt$sock_int(r3, 0xffff, 0x100, &(0x7f0000000040)=0x5, 0x4)
recvfrom$unix(r2, 0x0, 0x0, 0x23, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000003, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000000)={0x6, 0x17}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x1, 0x2, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80146959, &(0x7f0000000300))
setitimer(0x0, &(0x7f0000000540)={{}, {0x0, 0x6}}, 0x0)
getitimer(0x0, &(0x7f0000000040))
syz_emit_ethernet(0x536, &(0x7f0000000040)=ANY=[])
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}], {0x0, 0x20000}}})
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs, 0x10)
fcntl$dupfd(0xffffffffffffffff, 0xa, r0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc0284457, &(0x7f0000000240))
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x3d}, {0x61}, {0x6, 0x0, 0x0, 0x8000000000100}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000001700)="fb2fe7e986d81d7acfccfbcbd4a2", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0xc}, {0x5}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000340)={@broadcast, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @local={0xac, 0x14, 0x0}}, @icmp=@echo_reply}}}})
open(&(0x7f0000000140)='./file0\x00', 0x8294, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100)=0x1)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x28, r1)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffff7fb4, 0x0, 0xffff, 0x0, "6fc60900e4000000001200"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0x0, "d6e696d83fc4209e414d70000000d0c73d6500"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x4, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffff9c, 0x80185760, &(0x7f0000000000)={0x0, 0x8ca})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000080)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)={{}, {[], [], [0x0, 0x0, 0x0, 0x8000000000]}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000180), 0x14)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x2, &(0x7f0000000040)=[{0x20, 0x0, 0x0, 0x62b1}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x21, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000320000/0x1000)=nil, 0x1000, 0x0)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
madvise(&(0x7f0000454000/0x1000)=nil, 0x1000, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000280)='9*', 0x2, 0x401, 0x0, 0x0)
mlock(&(0x7f0000320000/0x2000)=nil, 0x2000)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
sendto$inet(r2, &(0x7f0000000040)="008eac8d351a4d645251b222057ad3f8fab7b6fa851118d864faa29b", 0x1c, 0x0, 0x0, 0x0)
linkat(0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0)
pwritev(0xffffffffffffffff, 0x0, 0x0, 0x0)
socket(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r2, &(0x7f00000012c0)=""/4060, 0xfdc, 0x40, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, &(0x7f00000001c0)="0032568c952143b05817620b7ae7a9808adf9c9942c96e092d13b51ecc75ea5ad4ae0b1e9539fd2fdd55ab68a49b016ce6def2bcdf396195100bcc62e8e3250ba90d209f4d123b7f718fe88f90d8e10841a85512c25ff121d98ba6f16db95ab0bdb375d627800de0359f439b9a017d9af7229914c0e305b36cfd81c6fe307fcaa598ed0b238d490c7501756425219a3a560a9b5a80c6c34aecaddd1feb8df45570029164634b6e70fd922f194d05d1ffcd10de76650b54a49ed67e610abbf6072885c5960aed7ad71910edb61a27a9235d7cefb5775fcde1769574aaecc1f17e2cb3e753702d72901deaf8650ec0d543f23812d6caab9c76a67d3c20d578c109a6c10f7ab40538890383908281bf81f072ca4929760d2e851aa52029817881bc407549d7715d72e95786c108c72ab37a38945f77607cf2803186cd1170accaa88a42b9ecee4e35b5b7a20c45b14f2c8e4a89e739029b758a29f139118c1f44c57b74124b50b1ad4c2e4b12c14e4e03bf6fa5915f343a1ecef992100706a7053506394b0f8c168deb745d1e132ff9a2f78bc5f7451d0c5b83374875d3726bee535348d32d24ad8d0a97663df576888ca44faef06b8d3766a6fbf903df3cd689315aee06e7d701c31e2915106a981045eb36a9a3a18683c0a56489a89d16e4ffb97df62c4d6efaaa7fa49d6b2bd29239105d59537ea138e7303c240877d82f3a32abc11218c285c855bf741d438bed11a6d470341c3fd26fac2856e4254da893aa0db6", 0x222)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x200b, 0x3ffc)
openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0, 0x0)
munlock(&(0x7f0000ffe000/0x1000)=nil, 0xffffffffdf001fff)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0xfffffffd, 0xffff, 0x1fc80d8b, "04000000010000a9b3c742000000d35ed900"})
write(r0, &(0x7f0000000180)="28f7c720e242b97c85cbc1f98f3775fa7295b4a2274d0260311befc6e5321ea043053607a5a6b2b4f3b519d9d31f8c9b55cbf07c4e7196cda90893418a02db070f59d17887f063a70edf3b70d3d9729d52aaa3def08a18907984e09a543019873f971b488c5e643c36e9f4606899043000417bade7b28820bfecdeb23168865db0ac2a791d30200a6283ab5551c6d9ec9f42067c20e06fad31741fef2bdcbe243e72694e2a64a1448058ae3d24a1fe0af025638699b74bb09ff62edc4013cc3ff610567c63043a0659e1497b33d61c9734f2c681cca12c9579b6da9cf5b373b161e0906f0e3f4f118e5e0df83712016009", 0xf1)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r1, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x1000, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x6, 0x0, 0x0, 0x0, @multicast1}, @udp={{0x3, 0x3, 0x8}}}}}})
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x3, &(0x7f00000000c0)='!\x00\x00\x00', 0x4)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
mmap(&(0x7f0000000000/0xb36000)=nil, 0xb36000, 0x0, 0x800000000009031, 0xffffffffffffffff, 0x0)
mlock(&(0x7f000067a000/0x4000)=nil, 0x4000)
mmap(&(0x7f0000000000/0xb36000)=nil, 0xb36000, 0x0, 0x800000000009031, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x30}, {0x60}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000003c0)={<r0=>0xffffffffffffffff})
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000004c0), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r2)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r3, 0xc1084425, &(0x7f0000000240))
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1b}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "5d186a37858bccda6500002000"})
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x40}, {0x4d}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x2000, 0x1300)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSTART(r0, 0x2000746e)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, '\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00B\x00'})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3d, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0x6c)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000280)={0x0, 0x0, &(0x7f0000000200), 0x0, 0x0}, 0x0)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000380)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x0, 0x2}, 0xff9e)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
r2 = dup(r0)
ioctl$TIOCSTOP(r2, 0x2000746f)
ioctl$TIOCSETAW(r2, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x81, 0x62e, "002ce7ce626631484febbca825eb16312ba56cdd"})
write(r0, &(0x7f00000001c0)="fe", 0x1)
munlock(&(0x7f0000ffd000/0x1000)=nil, 0x800000000000)
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x9, 0x0, 0x0, &(0x7f0000000100)="987c000000e9c9b3faf040a765dceff38911c6bc5f6b8bc3d400af4f1148af971877b25a5bc70f2320ebc54589b0728a057cafb84bef54d21d25cdf68496d2073ce91bf3c2ff2244000000005e3ea58b0100", 0x52)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1e, &(0x7f0000000040), 0x4)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{}, {}, {}]})
poll(&(0x7f0000000000), 0x32, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
rename(&(0x7f0000000100)='./file0\x00', &(0x7f0000000140)='.\x00')
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x33, &(0x7f0000000140)="eb", 0x1)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e6", 0xf)
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x1, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
setrlimit(0x0, &(0x7f0000000000)={0x1fb, 0x40000})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x0, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x4}, {0x5c}, {0x6, 0x0, 0x0, 0x8cdb}]})
write(r0, &(0x7f0000000080)="2a1d8c4206000000000000000b4a", 0xe)
pwritev(0xffffffffffffffff, 0xffffffffffffffff, 0x9, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000400)=[{&(0x7f0000000080)="5dea2dd94e423525b3b5a2ed74c0274367099418bdf1107971d7c50ae490800ad8274510fec3f096e9edf85a79de7b73caf5301fda3e12ccf38035199bea927d93dc169eea6b48759730a74f014c1901ccb31c13ecd12f77265f74ce8e6363", 0x5f}, {0x0}], 0x2)
writev(r0, &(0x7f00000013c0)=[{&(0x7f00000001c0)="ee72f6e9aedb8e4d958ccc7be75033bf005848c032180add5a0760d4108e71868a82bda70f5d8ceaf21ec8a7f25cbda584ae941570a4db8c1c0fbe70fe502e", 0x3f}], 0x1)
execve(0x0, 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f0000000340)='c\x00')
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000100)='x\x00')
openat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0187009, &(0x7f0000000380)={{0x0, 0x3}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='\x00', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff12ffffffff00", 0x8)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
writev(0xffffffffffffffff, &(0x7f0000000880)=[{&(0x7f0000000640)="a134855e03e5998102a03252882d194ea8148322aa4d9c0e092095b8982b27149994ff385246d7c032c82b91b7e8e15bef1bef44ca35bf5031d1aec90e07dd8977c72f", 0x43}], 0x1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x7}, {0x44}, {0x4000006, 0x0, 0x0, 0x80000000}]})
pwrite(r0, &(0x7f0000000140)="56f33a873743d1b828b0c2d5fcb9", 0xe, 0x0)
symlinkat(&(0x7f0000000080)='./file0\x00', 0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00')
unlinkat(0xffffffffffffff9c, &(0x7f00000001c0)='./file0\x00', 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd60000000001400fe00000000000000000000ffff000017000003"])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x14}, {0x30}, {0x6, 0x0, 0x0, 0x8000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000280)="2fbd70ff10ce171cdbac01876550", 0xe, 0x0)
pledge(&(0x7f0000000000)='tap', 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8028698c, &(0x7f0000000000))
openat$zero(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r1)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimensat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', &(0x7f00000001c0), 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x1d, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000000540)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bbc454327b6a1522c332ea628b8cb672e9e7247818f970e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c46106e72cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a84001b5b8d93dfcfb777", 0x181}], 0x1, 0x0)
mmap(&(0x7f0000000000/0xc000)=nil, 0xc000, 0x1, 0x10, r0, 0x0)
symlink(&(0x7f0000000140)='./file0/file0\x00', &(0x7f0000000180)='./file0/file0\x00')
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
getgroups(0x7, &(0x7f0000000080)=[0x0, 0xffffffffffffffff, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff])
chown(&(0x7f00000001c0)='./file0\x00', 0x0, r0)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
chmod(&(0x7f0000000040)='./file0/file0\x00', 0x0)
setregid(0x0, 0x0)
setegid(0xffffffffffffffff)
setregid(0x0, 0xffffffffffffffff)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000300)='./file0/file0\x00')
unveil(&(0x7f00000003c0)='./file0/file0\x00', &(0x7f0000000400)='x\x00')
unveil(&(0x7f0000000200)='./file0\x00', &(0x7f0000000240)='r\x00')
unveil(&(0x7f00000001c0)='./file2\x00', &(0x7f0000000100)='r\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29f", 0x70}], 0x1, 0x0}, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc1084413, &(0x7f0000000240))
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x1}, {0x1}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSDLT(r0, 0x8004427a, &(0x7f00000000c0)=0x1)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000000c0)={0x0, 0x0, 0x0})
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "090000000000000fffadca60b30400"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x10, &(0x7f0000000000), 0x1)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
writev(0xffffffffffffffff, &(0x7f00000000c0)=[{&(0x7f0000000000)="57679fefd1", 0x5}], 0x1)
r1 = getpid()
fcntl$lock(r0, 0x8, &(0x7f0000000740)={0x2, 0x1, 0x3, 0x3, r1})
ktrace(&(0x7f0000000100)='./file0\x00', 0x0, 0x10c, r1)
fcntl$lock(r0, 0x7, &(0x7f00000002c0)={0x3, 0x0, 0xffffffffffffffc1, 0xfffffffffffffffe, r1})
pipe2(&(0x7f0000000280)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x10004)
writev(r2, &(0x7f00000000c0)=[{0x0}, {0x0}, {&(0x7f0000000240)="b8", 0x1}, {&(0x7f0000000000)="57679fefd15aab780815e855e37f8b9bf460e823dcae6895db4b482e649fbcb783e39bb2289c96a268e4d1c02896a4c1c82fdcb6c68271526f5ee2142ac2782841e5f6313876b0de3ad4c20785fc2d4e73c199c1c9d40bdef9eca3bb35cfc0bd4ad3ea31ae10760ece8d3afc7fd1e8e45f106d3d41ad8246025346a3ff76afe70387ff9aa1f47233a58632efef59d2f4c6af5261d6c5b65c748e24277097d7e698a36cbcb343d6b2621f9e5cb0dc464b26ad395484", 0xfffffeba}], 0x4)
syz_open_pts()
r3 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x400, 0x0)
pipe2(&(0x7f0000000100)={0xffffffffffffffff, <r4=>0xffffffffffffffff}, 0x10004)
execve(&(0x7f0000000340)='./file0\x00', &(0x7f0000000500)=[&(0x7f0000000380)='/dev/wskbd0\x00', &(0x7f00000003c0)='/dev/zero\x00', &(0x7f0000000400)='/dev/zero\x00', &(0x7f0000000440)='/dev/wskbd0\x00', &(0x7f0000000480)='/$+\x00', &(0x7f00000004c0)='\\@\x00'], &(0x7f0000000700)=[&(0x7f0000000540)='/dev/zero\x00', &(0x7f0000000580)='/dev/zero\x00', &(0x7f00000005c0)='\'[}\xcf:\x00', &(0x7f0000000600)='/dev/zero\x00', &(0x7f0000000640)='/dev/zero\x00', &(0x7f0000000680)='/dev/wskbd0\x00', &(0x7f00000006c0)='/dev/zero\x00'])
kevent(r0, &(0x7f0000000140)=[{{r2}, 0xfffffffffffffffb, 0x41, 0x1, 0x7, 0x3}, {{r3}, 0xfffffffffffffffa, 0x89, 0x1, 0x0, 0x1}, {{r0}, 0xfffffffffffffffd, 0xe, 0x40000000, 0x8000, 0x421f}, {{r4}, 0xfffffffffffffffc, 0xc2, 0x1, 0xffffffffffffffff, 0x2ec2d736}, {{r0}, 0xfffffffffffffffd, 0x11, 0x1, 0x200, 0xff}], 0x0, &(0x7f0000000200)=[{{r0}, 0x8000000000000003, 0x2, 0x2, 0x3, 0x7}, {{r0}, 0x7fffffffffffffff, 0x89, 0x40000000, 0x9, 0x7fffffffffffffff}], 0x3, &(0x7f0000000240)={0x1, 0x3})
r5 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
pipe(&(0x7f0000000300))
fcntl$lock(r5, 0x7, &(0x7f0000000000))
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000000)=0x81, 0x4)
shutdown(r0, 0x2)
shutdown(r0, 0x2)
recvmmsg(0xffffffffffffffff, 0x0, 0x4, 0x0, &(0x7f00000001c0)={0x0, 0x36bbe29b})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x4, &(0x7f0000000080)=[{0x4}, {0x2}, {0x6, 0x0, 0x0, 0x1003}, {0xfffe}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000140)=0x9)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_GETDEFAULTBELL(r0, 0x40105706, &(0x7f0000000040))
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xffffffffffffff0a, 0x568, "d79400e7bb6fc6e200000000e400"})
read(r0, &(0x7f0000000080)=""/105, 0x69)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000002c0)={0x0, 0x0, 0x0, 0x0, "1e08000074540000000600000000000000ff00"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0xfff, "f43caf11e9eb7f8a558fdc415d18fcd4001f8fa7"})
sysctl$net_inet6_ip6(&(0x7f0000000280)={0x4, 0x18, 0x29, 0x7}, 0x4, &(0x7f00000002c0), 0x0, &(0x7f0000000340), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f0000000040)=[{0x6260}]})
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "08179c7d9234997b52006000000700"})
writev(r0, &(0x7f0000000780)=[{&(0x7f00000001c0)="9e02cb553b53b818ec8a729c8bb7ea317e128518444da88dd3c0ebc93f9b465f300fa8e37f60fb061fa7d184f3cccf515ca9", 0x32}], 0x1)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x2c0, 0x0)
mmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x0, 0xa011, r0, 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x8, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x75b4a93adad0a15a, r2)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80286989, &(0x7f0000000300))
r0 = socket(0x2, 0x3, 0x102)
sendmsg(r0, &(0x7f00000008c0)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3, './file0/file0\x00'}, 0x10, 0x0, 0x0, 0x0}, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpgrp()
setreuid(0xee00, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r2})
r3 = socket$unix(0x1, 0x1, 0x0)
r4 = getuid()
setreuid(0xee00, r4)
ioctl$TIOCSPGRP(r3, 0x80047476, &(0x7f0000000040))
r5 = fcntl$getown(r3, 0x5)
fcntl$setown(r0, 0x6, r5)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0xad)
write(r1, &(0x7f0000000080)="e5", 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x1b08, 0x0)
getrusage(0xffffffffffffffff, &(0x7f0000000040))
pipe2(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
fcntl$setstatus(r1, 0x4, 0xcc)
r2 = getpid()
setreuid(0x0, 0xee01)
fcntl$setown(r1, 0x6, r2)
setreuid(0x0, 0x0)
setreuid(0xee00, 0x0)
r3 = getuid()
setreuid(0xee00, r3)
write(r0, &(0x7f0000000040)='!', 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xc0014462, &(0x7f0000000040)=0x5)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000001c0)={{0x0, 0x80000000}}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
r1 = socket(0x400000000018, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x3a)
setsockopt(r2, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r3 = dup2(r1, r2)
setsockopt$inet6_MRT6_ADD_MIF(r3, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000100)="0924684835255fc6d8a164ec83", 0xd)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket(0x11, 0x4003, 0x0)
r1 = dup(r0)
sendto$unix(r1, &(0x7f00000000c0)="b1000501600000900000200007000000000004fecea11ea8fef96ecfc73fd3357ae26caa0420fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff3728211ee4fd89720fd3872babfbb770a9f5a872c881ff7cc53c895303b22f31a3fb9037a00f90fb6de01b00000000000200"/177, 0xb1, 0xc, 0x0, 0x0)
recvfrom$inet(r1, &(0x7f0000000280)=""/167, 0xa7, 0x2, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x14}, {0x2}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
fcntl$setown(r0, 0x6, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = dup2(r0, r0)
getsockopt(r1, 0x6, 0x9, 0x0, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000180), 0x8, &(0x7f00000001c0), 0x0, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000300)="a449e47fe75fdf916bca74e767610ff81a288c4637e529bdfc84467944dca7b3b2d7158d78e3b2e7dbcf18a6e618", 0x2e}], 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x2f, &(0x7f0000000a00)="b18022d9", 0x4)
setsockopt(r0, 0x29, 0x2f, &(0x7f0000000a00)="b18022d9", 0x4)
dup(r0)
r1 = socket(0x1, 0x1, 0x0)
r2 = dup(r1)
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000080))
r3 = dup(r1)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2e)
close(r3)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300)=0x8)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
pipe(&(0x7f0000000000))
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x6, 0x2, &(0x7f0000000000), 0x45)
r0 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setitimer(0x0, &(0x7f0000000000)={{}, {0x8000000000000001}}, 0x0)
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
msync(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x5)
setitimer(0x0, &(0x7f0000000000), 0x0)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
mquery(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file1\x00', 0x2000, 0x207)
open$dir(&(0x7f0000000040)='./file1\x00', 0x0, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000000), 0x4)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000040)='.\x00', &(0x7f00000000c0)='c\x00')
symlink(&(0x7f0000000000)='./file1/file0\x00', &(0x7f0000000080)='./file1/file1\x00')
openat(0xffffffffffffff9c, &(0x7f0000000200)='./file1/file1\x00', 0x0, 0x0)
unveil(&(0x7f0000000140)='./file1/file0\x00', &(0x7f00000001c0)='r\x00')
unlink(&(0x7f0000000300)='./file1/file1\x00')
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x18, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0), 0x3c)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xb04b, 0x103, "0000a1be000000000700000000000000e3363b5c"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
write(r2, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="1c", 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4], [0x0, 0x0, 0x0, 0x2]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x14, &(0x7f0000000000)="28c67cf7", 0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000740)={0x0, 0x0, &(0x7f00000004c0)=[{&(0x7f0000000440)="cc8b0746bfda5c89202dd0f86c3e9519d9796c5d786bea", 0x17}], 0x1, 0x0, 0xd8}, 0x0)
recvmmsg(r0, &(0x7f0000000300)={0x0}, 0x10, 0x42, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x6, 0x1, 0x0, 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = dup2(r1, r0)
poll(&(0x7f0000000180)=[{r2, 0x4}], 0x1, 0x0)
poll(&(0x7f0000000100)=[{r1, 0x4}, {r2, 0x4}, {r1, 0x4}], 0x3, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
r0 = socket(0x800000018, 0x3, 0x102)
r1 = getpgrp()
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x3, 0x1, 0x1f, 0x8, r1})
fcntl$getown(r0, 0x5)
r2 = msgget$private(0x0, 0x0)
msgrcv(r2, &(0x7f00000010c0)={0x0, ""/4089}, 0x1001, 0x3, 0x0)
msgsnd(r2, &(0x7f00000004c0)=ANY=[@ANYBLOB="0200000000000000c1873c269a5bfb1783f837a2f38ccb69f4c5dc9497985df45613b863bb462b6a724983da5c5e5b280af022c7430c1c2471a696fd3101e359157bfbc051705e009ecbbd28d95768c44d842099d011ab2cdf82013f892e4ae799bd5988eea138fe9f8243b79a3aa2c1a0f929d37b3e1f6a6495b72684f0adefdbc09a43525733f4febafa1acf7e0fd71e936ca9fd3b66301049a65f"], 0x1e, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r3=>0x0}, &(0x7f0000000040)=0xc)
r4 = getuid()
r5 = getegid()
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000080)={<r6=>0x0}, 0xc)
msgctl$IPC_SET(r2, 0x1, &(0x7f00000000c0)={{0x4, r3, 0x0, r4, r5, 0xed, 0x4}, 0x6f81, 0x3, r6, 0x0, 0xfe, 0xc95e, 0xffffffffffffffff, 0xd672})
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x100, r6)
r7 = socket(0x18, 0x3, 0x0)
ioctl$TIOCSTART(0xffffffffffffffff, 0x2000746e)
connect$unix(r7, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x13}, 0x1c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xc0284459, &(0x7f0000000240)=0x65)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000200)=[{0x7c}, {0x1}, {0x6, 0x0, 0x0, 0x10003}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8000000000003})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x6a5, 0x1fc80d8b, "0400e5ecc7db00"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000140)="62bba8ac9239a0ab96a35763341ffeedb701f69d46beb52e7503c5387e4e7889f87906aab0b65a22a1b3efbd8aa0ffb91ca95dfb12946c276b86c8f0fa841baafb872d9c7f8ba70400cf916f4c96d66cbd9c0d6c3756cb8428de2293268f1c055f80fff6249e981491b117cf18", 0x6d}, {&(0x7f00000001c0)="a0083928a70ee1ddd4672bce321feaf724a86bc0750a73f79eef23b40034a1931eea33c367d2b26d4cdfe349772e5133a36c825d7d5009edf0bd40d63f165f5e841a3b31f22d95ca5c6af26f50a463d56494bd6873d5b311939f77db813217288c1691c097d0b01d58c78c40b4bb5ce9ac5f780d6880b854593acd3393e0a8ae81ace28be1c8fac1a0e303d5b4970c05d0b38ebec3943baa68a14a2373faa1ad544eaf02d0343602ca03630607b8d5b243dea26cedc848e2da1c897934197ace36f6b8f1fbd4f125e5c5bd23be1481acd98b90bbba12afabd61f5930791f0a4be214a9fc63e99e19cfe12e6df129eb4be1d2052b07799366f319d5bba2cdd1cc5a56360d29555d581b288f0b344acd041101d47e9ca66a54f1c2f200cb4f7109a847a3c00295fb4040f25cfe5504fc3e635963f111d7d99270b129d40e2e835372472bd0dd75d5a33d6e9aa18c011306e596da031ead17e9e5ff20ea404e51540445d693197959fe3a44511fb6559623021cbaad466b542cb6e84f8e7ef7e0574212a1a1edec9e2ec96229ff3693372a4323acd678b1e2ae7854407c562c607879c744ee6ab54e4a97e00cc8961dbf4abb05abc781c3d04b744e6db592b0a35ba3d25a13f4fbcd6944c4ca8d085bedc53c502fa873056924cc103eb5e5d41d4e3f2e2ce883dd0cc2a6ace5572d2b8c6c078ac13b36039535bc2645638def17a34cd86f4beabca7096ab85153091b57c9456101e7c48478fe0b0be619ac3403e9ad6efde61208ced6dbb9b2ec85e7fa6d90d5e3234baa9afb10d161a9942b70badb71c69a48dd5c0170924550632b33e3d3723d4f6e768a6654543eb2bb3025b40e1ebf8111008aeb5110ce52092b4676fd6f064b9d8d71f7ed5df8515358e9b50e35df227ff8d8a00b9a31c7bccf2a84c3c4701ef2f7da8fec0a39681696f2bc457e549f030e7b3f196ac39120ba01c50c3730b8c31dc6e2ea0ebb8efaae454c614dc5429831e02812018e2171ee9950bb1cdfc394ad4f26a311f75e6db00a6e721fdc10706dc60907d0e5f24df1d9ee01cdedef7cbe887463f1d05951cfd9e1bbb68d91706c066039202d4c1b62a25a81ef455d13329e8226f331866c84ea8dc0fe52fcbbb5aa5ec4c92fa42e2ee1dd753e2ab7f9c2b4a4f535ea2b0b9032dc813ad2470deac369dd3e3f53d38076528ce09dcd13db444f1a464ee71d34f78e3a0a8066a5cbcea68fe36073fd883436b6e29d5b287b557cad431199a27b34eebda13008e198cdb2e94a5b4e554f974c0f7127f1f9598a4952356e40f540229dcbef954e93c89a18fd87abc56a2a9cd8521f81734c1af7613f395403e0720f050ae3ca49321c1c01263b2e72e491d247681cfb0badec5e971162900c185146db625cbecdaf33db1988cd102ef2a5da4d911ef554c1531c3f5e484c3c1fad6a53b546bc57a4cb091f5570b96777111995e6667e0f5440b77baea6d5c0fcbebe462ddc023a59e19ce346f18039e25a4926deefa6c50125da766e027e15794b7c200ca126ad9505845a51db8c185798fd3a674376084ba21f809022a2cc43ea23c32afc2c8789bbe5ae076c9a580374fe3df03bf144eb1ecadab175fc0ca70172e1a5b3c9b063f7513036603096809ce8c5c4a0c47bb27b93de844bf3f07ac60972bed50c6fcf555afbdb9785d26b2924f60f083f03c41e2eee6d3c3967f58fdeabf612a19373d70549710d8b5e2f27e90c75102e68d6a414095e1f8d81cbcd46e07d483e7b03858d6a3143294cdc43d60aa0d27ab0c48ac2c823c13627c4e5611fcce6bef8623d9235a921da6df72073778ae5fe80756badf42e11ffb415bc5d7be3aba817413569c97d3dc847af950e79620d9f96e7a90740f43d4feeeaecc8bc45ba001733c82ee24654f5cb07523a7eeefa91a36b1d7ae84b2135dde73079b8f1f4d4e674520358a0be6ebd2fe545a4b2705a40ba283d0ba239f307dced02be3b4338acf9957c8fa4e11a2a3a89dfe71fcf6bc272179b4766218c3c27b3796aff96e8d16576e5711dc39fc670f11896535ba21f4b13a575422a7fa30762b85e008f6fe5cf93c4bed1cdaf37ef29c6958d946dca0ec854b38c83a72ed96316a6ada5180f00a456fd54b96de2d1621a5551a43a38d48ecedf9fa0831f8cfee0a1fa35200e785ea3c04fcf86a05161bdb43f4d5a0a135a9ed26ffe25bbe006267e5a552f319d916cd42f5c84e345e704c697deb655049fcc7b2c0d4ae198f57171d521da4fc9c3c2900d056c4293c53a5ada3b0fc26a50078d2b9390147971a6efc5ab52356e753cd0590489acee01c65f1e6f0329272bcdb2ff767e6b7ced2b7281b5aab321570b84fc2373d5612614e0aee20839d2d40ceb9123f69476b7aaec89467c01c8f672ed23f239e3d424ede0ad769f337485f67593bf8fe2b193b443927fe32bdb30c1ade2dc828217069fa89af2d45a03996e7534ceda5738620b6b485744d1c160f98a13bd1a27cac228da477221cb8b9482da4948a9e8a8dad5d82a5e14098d2f8aaa762572ee49b0f4f0a93939e52ca74c10fd89377c302f0cb4c6e1e0cee119a310dffa5c54e2ca139c8e88bd06d5b19bcbf4bddd4fb08cc8810ed559bada9109febe555a26246eff124350d19eda8c8be651442c3028f4e5064198347100fd85b1ae06018834ece831d5c42bc7a11c696ae158b89e694c7ca90bc687445e91f54befc8be2d018a678e80204b925c2897ffc24c115dc22ae25a9aaff93c0201b6523f87eefde7b03698e61ae9e81e43dfbd0f59d021144edcded4825d138577de9733027dbfc8f63867b3ecc09e53193c64e6d6e45f701ac4dbebd29ecdf07f2812574ef814e0174a2b3bb41ab6e95da85faca0f941db432d28712f4387417005a09de80f19d9c1712833cc7e601c33a4ccd4f5c97a51010ff580191f0878f0661eddacc027a6f6c4994834acef9635cd3094862936b8f870c142a798beaa2baeef8462a0aeb1cae11a26480d2d49a971a99e4274e7240410fa48645f4afd7d4ddab6d8e47d91e5a924539ba347cc9fcff4afd4c1e28f5b6a428604f5c8590ef199c7653cfe866d121387c21c8be42b0c21593aa9562273fb3daf5a7015551fb8a5042e8fa523fc84bd95a971648c098ee2e6d09b686e8da0fb433850585d5b1338c29805bf22daadfa4ed8ab843e5329dd0eb16ef9acf369b91c5ea0e1f55e9d38875aab458584fa73bdf40c71a2fc6aba354de36fcc8a03d371b7b5e3ac5b4bcc52e683ce65c923db9f1062b70c37e5995f2dcbc0ac8e0403a817f85990dee087a85778e180f1aaaae7547322a2e6669aecb703f889b1620df18c056d5090c0e1eeaeaef4f9e1d3b733637223522d06f8382e4a498987a30c783b529b5cd353e8cf1130be9719f4ba3381498cf24a8d4a63ed5415ad9e9a6f063469856170b0afef0c29a4aca2f7f9cb440aca79f5d9e556a4b5ec8a4fef21e8e17cf61ded29cb77b361cdc522853f8e81f94aa2e0060ae9305fb79a0802c656c91b726212ba3de8e758d05557be75bf6f00f274c7c6f5917b376e0b9e84b9c1d61d0a16349e19a6ccdf6f841482e9d968f0fd0a8d5e5f8599b86d7067de9522b7310e72584faa41244aca1f0223eaee5aa0d4268c195a252207971c41a7f85dfc24e82feacf199bdcec93b6e4f5b8f068b54b72cc43c75d8db6e8fb45cc967d862164a1caf9bee9e5cce30fd3dd9dc10c42efcbf9f06d81e8ef8a38260945e776eab39458cda4e14747230afaafd7d8f69b6fce019f28dd988b82bfa70e74d35289c27d58686bf72314fb0b7238e2a3b22400bb2ea144361843e19c1c6439a16302b5f628e55e48f65367b76c69156af60ec6a08e97c70dec9a86d2d61ccda3edccf6c7f0bbbce2ceaef19d432d5661f911e81132066911a9cae72bc099457ab4375a0066608d5bcd79bedc1eb10c7bdd6101352255f8544f2301e280513143497e451dc232638e143edb69f525743e941cbe18003a357add8db957bffd67233eb2fe2f3ac4dbf22ae22972ca3cf6d00980633b59c8decb0ad599861f9e12169149aaaa1005ca2209d33c6df5b88a8b89f7c4600c852e0bcc54986e13a74e5576efef4a16c5435304b655535c02a8554a2ef7eb79f12dd1ac07f4b10efe57eed04f2c4eaf89488056790b736a51f5c847752ba8e3f35523fa1c9eac6e7f296fa92075917b108ff2e40d3cc270d4979d138755e3fc201d8d923fdcee5a78e150bc916b99f47766d83eaa329fc790504eed79be40543b1faa37e45b8f37b3338acb7e7259c1e1f6763b4d768a23ba6589857fb14fda4bb73210ba040dcb3f9aedf8d47fef5eedcdabb51de92468bfdfb33d93212623cd1c94eecbd9e36bbb7b3d5a067fd0d23d221dd3aee2101635bdd6233027c5f62f55572a99edc8f783feccee149697da726eaaaa795aad0908777a068e5338096f8a64da7743a44c8adb41bc98691cafb6c7535696749b91fd95a9f54a157b6470ea655b9cd397fb5460a47a325de1bb938d09be3fbdd9a7c6efc0a31cc2f89177e7d5373e74504000ce7beaab17f6a2a1dc3af0de2c72890b4135b198ec6e81e99177e16f4563ccb5bc0e88c71badb0a00264d5b64ad79d3251d8eb2b606c391c55ff63c06ad9ae3c63a3d8aff617a9c03242bf32efcdc71a57e6afcc39a1fa05180fb91cea93ee5d5bc88b3a76d9343994ad8577727076e1aa90fcbe750e8626fbb9463bf0bf413663cd7f335b11e1dfb0eb42df681d10c11d754e4317b1d73c4fcce7badaa3aa5bb0710e7e318d25848a23d0e8760c7a03ffdc2eef4ab9edd2c7beceac5cdaff3b4cc1f94cab166a6cfe9ea3b5e138c9c0c96f5e7ab06957e531a5e00fee3499f820d45cf37a4f4040631548850f9ed4f1256a25dd6eb6aa995e97df58c6dc77b9dd80094ec7d2fe5c99f0a973ac6a09ca8b5872ae0c237cd1ada88e894c3f19d860b724a1e30233c43c962e37c2a165750b063c7fb3959fdd72ae6bc96859d5f520212817790ff5829dab786f34d42ba304ab180358da0afee32c5f5ddf5e4cf1890007245db5b18af7fc1cf5df5a6886fa6f50e11d72fa51c626ed6265cde85831662267586985edfe396378dab74f928450473e74b04c008ae7cf9d6f9fd0444ce8f4c19cd893ceb28a195e45fd5d0fe535f1bfcd0cd9dcb79bddd4f5414b14f1f7b1aedfa8a7ba7e7cdbb7d0c599dfee89551a72cfe38700ec697c994cf445fe2476dd4b83a77a020453c31f056956cf440483b8717f96978daf354d0621d2be137ebc744829184308113905cfe5ef34e40f5d86d04288c07367ed17f8f4e50b48f1dbcbbcda4324033f0324de3daf8156023a1c48731d2a983301cebdf5c51a853899616e292de730ed29a30f20c4b5c43eea100b86dc58d4eaedc074d4fa1fef562ffafa7ce638d30b8bcf69698e263b99f393df9e4201d96b6159ab10249483376c8ece13fcb59bdd383903ff39301d9282d39185daec62facd5288f3b3355dff3d561d707f43491abf6b36b9272bfcb42d6297deabdd6ba704f2345ab9d77170679902466d8a3c134f456c22f6a5edb526bc65cfa274c5b7cb7611fbfcac6dbdb02c54af947242ccd299aa0e11ba4c678e8e22bff647eabf2d66c9611ac8ddc23610def609b967762541c70a20afed03c876611bf0d611b46e13050a32b12e30bb74f13d402763294825e1b9be556ac3a24523a10be804132e37bdd65e83dd4a7d8a5d88d632721d429fa2af1b2f3f62a6500b0787c5c688b3051bc9fd45", 0x1000}, {&(0x7f00000011c0)="bc0f6720cd01489657ca3e60", 0xc}], 0x3)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x200000000000c, &(0x7f0000000540), 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000440), 0x20000, 0x0)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x80, 0x1, 0x2000}, 0x9)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0xa, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f00005a1000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f000055f000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000fee000/0x12000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f0000558000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff0000/0x1000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000ff6000/0xa000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./file0\x00', './bus/\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus\x00', './bus\x00', './file']})
r1 = kqueue()
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000005c0)={&(0x7f0000000040)='./file0\x00', 0xea4, 0x0})
kevent(r1, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
preadv(r0, &(0x7f0000000100)=[{&(0x7f00000008c0)=""/4096, 0x1000}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCPROMISC(r0, 0x20004269)
ioctl$BIOCPROMISC(r0, 0x20004269)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
preadv(r0, &(0x7f0000000000)=[{0x0}, {&(0x7f00000000c0)=""/200, 0xffffff96}], 0x2, 0x0)
sysctl$machdep(&(0x7f0000000000)={0x7, 0xf}, 0x2, &(0x7f0000000040)="8be32c69", &(0x7f0000000080)=0x4, &(0x7f0000001240), 0x4)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @random="feffffffff00", [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @rand_addr, @broadcast, @rand_addr}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x833eee594ef524ee, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$TIOCDRAIN(r0, 0x2000745e)
execve(0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000100)={0xa}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x8, 0x152)
ioctl$WSDISPLAYIO_GETSCREENTYPE(r0, 0xc028575d, &(0x7f0000000180)={0x800, 0x401, './file0\x00', 0x7b, 0x10, 0x9, 0x421d})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fd181ae9696810e3ff"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r1 = socket(0x18, 0x1, 0x0)
socket(0x1, 0x5, 0x9)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r1)
r2 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2a, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendmsg$unix(r2, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [0x0, 0x0, 0x0, 0x302]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x40004200000028ac)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIONBIO(r0, 0x80047476, &(0x7f00000000c0)=0x80000003)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000080)='./file0/file0\x00')
renameat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0/file0/..\x00', 0xffffffffffffff9c, &(0x7f0000000300)='./file1\x00')
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @multicast2, {[@generic={0x1, 0x3, '\f'}]}}, @icmp=@info_request={0x8}}}}})
r0 = kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
kevent(r0, &(0x7f0000000040)=[{{r1}, 0xfffffffffffffffb, 0x5}], 0x0, &(0x7f0000000140)=[{{}, 0xfffffffffffffffb, 0x77}], 0x0, &(0x7f0000000280)={0x55e, 0xffffffff})
kevent(r0, &(0x7f0000000000), 0x20, 0x0, 0xb2c, 0x0)
r0 = kqueue()
socket$unix(0x1, 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x341, &(0x7f0000000280), 0xa55c, &(0x7f0000000140)={0x0, 0x3})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x2}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "c99e0421617e13ae90d77881c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffff9c, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "a4c500"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)="9cd435ed", 0x4)
setsockopt(r1, 0x1000000029, 0x2e, 0x0, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x40000001}, {0x1d}, {0x46}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/237)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f00000001c0), 0x2aaaaaaaaaaaaced)
semop(r0, &(0x7f0000000200)=[{0x4, 0xb98}, {0x4, 0xef1f, 0x1000}, {0x0, 0xfe3, 0x1800}], 0x3)
semop(0x0, &(0x7f0000000180)=[{0x1, 0x7f, 0x1000}, {0x319347b6e55e5096, 0x41, 0x1000}], 0x2)
semop(r0, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x4, 0x4, 0x1800}, {0x2, 0x96c}, {0x0, 0xe7}], 0x4)
semop(r0, &(0x7f0000000040), 0x0)
semop(r0, &(0x7f0000000040)=[{0x2, 0x0, 0xc00}, {0x1, 0x22, 0x1000}, {0x4, 0x4, 0x1800}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(0x0, &(0x7f00000001c0), 0x0)
semop(r0, &(0x7f0000000240)=[{0x2, 0x1c, 0x1000}, {0x4, 0x5, 0x1800}, {0x2, 0x6, 0x1800}, {0x0, 0x1, 0x400}, {0x0, 0x6, 0x1000}, {0x4, 0x6, 0xc00}, {0x4, 0x595, 0x1000}, {0x0, 0xdc6, 0x1800}, {0x1, 0x1, 0x1000}], 0x9)
semop(r0, &(0x7f0000000000)=[{0x3, 0x97}], 0x1)
semop(0x0, &(0x7f0000000180), 0x0)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b10005046000000000000847d7", 0xd, 0x0, 0x0, 0x0)
semop(r0, &(0x7f0000000080)=[{0x5, 0x1ff, 0x1800}, {0x38ceca30e305b873, 0x8, 0x800}, {0x0, 0x6, 0x800}, {0x1, 0x4, 0x1800}, {0x2, 0x71, 0x1800}], 0x5)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "1700"})
pledge(&(0x7f0000000000)='tap', 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028698a, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x6c}, {0x35}, {0x6, 0x0, 0x0, 0xcc}]})
pwrite(r0, &(0x7f0000000200)="b269f66b39b9de6923518e4027fe", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x5}, {0x48}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f00000001c0)="895e3dcf79fe889814aecd654f5c", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000140)=[{0x0, 0x0, 0x0, 0x8002}, {0x5}, {0x16}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1021, &(0x7f0000000080)=0x5, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x3}, {0x4d}, {0x6}]})
syz_emit_ethernet(0x4a, &(0x7f00000013c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "010001", 0x14, 0x0, 0x0, @loopback={0x4}, @loopback={0x4}, {[], @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x2, 0x0, 0x0)
setreuid(0xee00, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000440)=0xc)
r2 = getuid()
chown(&(0x7f0000000200)='./file0\x00', r2, r1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = open(&(0x7f0000000200)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000380)="90c3fe67eb586898600425f2f573e0d1ac83c18d00c8e22066c0d389fe895a974c8d45aaf9a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3214ed85fb20e088c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e32082cec454327b6a1522c332ea628b8cb672e9e724780100000017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b28020000000000000000c1570f94", 0xca}], 0x1, 0x0)
writev(r1, &(0x7f0000000100)=[{&(0x7f0000000180)="14931814", 0x4}], 0x1)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x5, 0x10, r1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
flock(r0, 0x7)
openat$wsmuxmouse(0xffffffffffffff9c, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x42}, 0x6, 0x0, 0x0, &(0x7f0000000140), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x24}, {0x54}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r0, &(0x7f0000000680)="f94ca60a7e42c5d48e8696f1669f", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x5}, {0x2c}, {0x6, 0x0, 0x0, 0x2c}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
sysctl$kern(&(0x7f0000000000)={0x1, 0xb}, 0x2, &(0x7f0000000040)="ae4564fe", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1025, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xfffffffe, "3da58d7e8800"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)="0dcf67520990982c582dd6057e8d1069ce86b4854475cd2ad14516872e5fb8b07102f1f8b3c02aaeb9b32f1461a2fe9cafe8f3a3458b93e501ccc490dc2a968bc43140c8eb7b5882a12fa011c9518219d170f2aebe4c5aed28582ee61ecb44ebd70dda7c805d54b845bda2697bbe578a17dd3da474c02852258308051c7695c993dd9b5a086b4f954c554a8f160341004ad9366eef994b32816ad415fa45c1575ac47d55fac57bf6ce2647f4518900e0a180172a2ec178b3270f5dfc12b1058c5d6bea888cae054af3f59fa44f362c443bcc0bb6808f08f9ca3dba9a140e0000009cfd857f09110d0d0128ed517562257b8ecf1c7dec9de7a31fbe550e4f7d4ce0d53bbd2e31c609ff9872a6e35d3702176d243484b3f1ff223b4ef4f9f575cf67579ff3a99e58d8c0e20fadaa7bae3c301675febc67b85cde", 0x139}], 0x1)
sysctl$vm(&(0x7f0000000000)={0x2, 0xc}, 0x2, &(0x7f0000000140)="ad", &(0x7f0000000040)=0x1, &(0x7f0000000080), 0x0)
syz_emit_ethernet(0x7e, &(0x7f00000004c0)=ANY=[@ANYBLOB="ff7fffffffffffffffffffff86dd6031797100433a0000000000000000000a00000000000000ff02000000000001000000fe49800301a09617d679900090780000000060af493d9d1ac91deb57137b3f5072db43539db8ffd482e20b277e000000002000000000060000000000000000439253bd6458e6c3e3e4000001fe8000000000000000000067490000003309000000000000ff020000000000000000000586ebb4e5b10def53173fbec8439c0fe182d0598e2d84f0cae23cbcd95a590eff9eca6a4b58817e80b36646ba23031089621d3e242391d65e5bb1c6d4591e9c793d6867d9625a57eb9533fb5bca205be567c6e62848fd49d824cf187ff5ace7e2d01313619f73769b93c53a3627936347cc1c1235c5d2c8b16172f287e9054a0e0c1e5930de3206000000000000006ebc1369b4bfbb67c1944b5e41d5923b8e575c902cd30b7a0553b2350446ffadbdf81ce28f2e81660185292afa8f82319e5d759182431e83db375f05f9f9193bf185b981b098d16ff1a404c9e40d8c57454a2bacdf2813006fcf84ab"])
msgsnd(0x0, &(0x7f00000009c0)=ANY=[@ANYBLOB="0300000000000000374e10cb8882146965755ccd3c76f95e85dcb3f9993a787f6fc6eaec2addf283de460f0000f2fb73906b143696cd68953913e7c3ed41c88c5d073c225629403d84055ec6929fe64b3085740ddb04ab8bdeee98d65fdbbe92ab9445444fee0aa5df126e8e51e84344803ac474bd306f9740c6976cd50737eb1402d2c19cad8fd9634fdc4aafbfc76ae348592895e05095755c5b1a0f6ba2bc0db06fb394adf86e067308c34ec60933d2b376f91418c5c1de2df32aed3e6069e1b8bfcabd82d18586469d7ee78ae6dc13f3cae163975b85a0b2425939894b59d7090eda441dca2e74796f0f03c398399b148c6fcf9fe83e3d01e1c2eb40c01f8b3231441169640c02ca1f72134364030e3d464d4c275bc1dc0949da8fdbfbdd76b42bde3d81e54c967564381a840cb65fd5d522582eab686d7c94dc4be5c197dfc14f16b1a0dbc543664c464a04a8854be30137553d527a7ad6f7b5d13feffe197dc5eab29483193642c6b75cf1b62991b299d930a195036c75e51a698b8c99980327669d6aef80d78847a0eec0e8fa9dcc7b93376e630076756a9a7d089cef4d60c90ff9634e4942f80a3daf98f271b47587dae9376afd0e3a8585f06a262b8f45bf89e25ca71e2179411de7862f2dd55965776de9b8ecf1cb54eaf3c0931170d9de715a57c6704d6071ad0d7f2f"], 0x87, 0x800)
msgsnd(0x0, &(0x7f0000000040)=ANY=[@ANYRES16=0x0, @ANYRES16, @ANYRES64], 0xee, 0x0)
msgrcv(0x0, &(0x7f0000000780)=ANY=[@ANYRESDEC=0x0, @ANYRESOCT, @ANYRESHEX, @ANYRES16, @ANYRES64], 0x94, 0x1, 0x800)
syz_emit_ethernet(0x122, &(0x7f00000003c0)=ANY=[@ANYRESHEX, @ANYRES32])
r0 = socket(0x11, 0x4003, 0x0)
syz_emit_ethernet(0xa2, &(0x7f00000008c0)=ANY=[@ANYBLOB="aaaaaaaaaab3aaaaaaaaaaaa08004f9600940064d5d2061d9078e00000020000000000440c06c0000000000000000589170200000000ffffffff7f00000100000000e0000002890300000000907800677fffdae1d24b435106eb8fecc4eae6ca1c6e2fdbd5311be598193f66151fb757c96e248530d120158b951b541fa42b4e2ba26cd834b6e177bbeba72c95ed9f061a2ea4506b29a85cdbb6403c454d05b354cac01368aeea1f331bf25ffafcc63f3bbbbda24053d8278b70110ea2928dfef830c1be158529aaae363eee023b8e4b038e3d485dff8529029cc33ba01d596d18bd549396016ee244eacd8498"])
syz_extract_tcp_res$synack(&(0x7f0000000000), 0x1, 0x0)
sendto$unix(r0, &(0x7f0000000100)="b100050460000000000008000501000000000000ce24fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebbc657699a1f132e27ecb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1abda71601a8bfee8aca4911faff5a872c881ff7ca93c894303b22f310b404f36a0069000fcffe0ffe608a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x66, 0x0, 0x0)
sysctl$hw(&(0x7f0000000040)={0x3}, 0x2, &(0x7f00000001c0), 0x0, 0x0, 0x0)
semctl$IPC_RMID(0xffffffffffffffff, 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
poll(&(0x7f0000000200)=[{r0, 0xb1}], 0x1, 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1024, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x1}, {0x4}, {0x6, 0x0, 0x0, 0x7ffe}]})
pwrite(r0, &(0x7f0000000140)="f94c4c49dfd685fbaf8a8d1a029b", 0xe, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
socket$inet(0x2, 0x4003, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
select(0x40, &(0x7f00000000c0)={0x9}, 0x0, 0x0, 0x0)
close(r0)
select(0x40, &(0x7f0000000240)={0x1f}, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0284416, &(0x7f0000000240))
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x3, &(0x7f00000000c0)="01000000", 0x4)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000001940)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='c\x00')
execve(&(0x7f0000001800)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000080)=[{0xc}, {0x5c}, {0x6, 0x0, 0x0, 0xffbfffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x41, &(0x7f00000000c0), 0x4)
sendto$inet(r1, &(0x7f0000000100)="b8707e9825dc608e546d490827e3138a106617db51453ceb81ec2632d03014bf2f106b543663a55f1e4b498357d097032c3bd1e1c84a459ef7ff6d16a34b010fc2fc219dcb349357bd3d3d193a76c3f7372c6852af0f2d66a33e069bcd2b76b972c9f457aa08dd97d55a4b56d7562e1228c36435ddd29fe94100bbf6f6213f8a9b6737ae04e766", 0x87, 0x404, &(0x7f0000000080)={0x2, 0x1}, 0xc)
r2 = dup(r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000001900)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmsg(r0, &(0x7f0000000900)={0x0, 0x0, 0xfffffffffffffffe, 0x9}, 0x0)
pipe(&(0x7f0000000000))
r0 = kqueue()
r1 = openat$vmm(0xffffffffffffff9c, &(0x7f0000001540), 0x0, 0x0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)={{}, {[0x0, 0x0, 0x4, 0xffffffff]}}})
kevent(r0, &(0x7f0000000140), 0x8, 0x0, 0x19, 0x0)
dup2(r0, r1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1", 0x108}], 0x1)
ioctl$FIOASYNC(r0, 0xc008441d, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x24}, {0x24}, {0x6, 0x0, 0x0, 0x1f}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000a00))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
r1 = kqueue()
kevent(r1, &(0x7f0000000280), 0x7, &(0x7f0000000340)=[{{r0}, 0xfffffffffffffffe, 0x1}], 0x10001, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x2c}, {0x3d}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)=ANY=[])
r0 = socket(0x18, 0x1, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000080)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @rand_addr}, @tcp={{0x1, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
close(r0)
r1 = socket(0x800000018, 0x3, 0x0)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000000029, 0x4, &(0x7f0000000000)="06000000", 0x4)
dup2(r2, r0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000040)=0xb, 0x4)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = kqueue()
kevent(r0, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0x41}], 0x0, 0x0, 0x8, 0x0)
kevent(r0, &(0x7f0000000080), 0x826ee, 0x0, 0x3, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f00000003c0), 0x2, &(0x7f0000000440)={0x1})
munmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
r0 = shmget$private(0x0, 0x1000, 0x1, &(0x7f0000ffd000/0x1000)=nil)
msgctl$IPC_STAT(0x0, 0x2, &(0x7f0000000000)=""/10)
madvise(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x1)
mlockall(0x0)
r1 = shmget(0x2, 0x1000, 0x200, &(0x7f0000fff000/0x1000)=nil)
shmctl$SHM_UNLOCK(r1, 0x4)
shmat(r0, &(0x7f0000ffd000/0x3000)=nil, 0x800)
r2 = getpid()
shmctl$IPC_SET(r1, 0x1, &(0x7f0000000040)={{0x2dc1b744, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x40, 0x6}, 0x9, 0x9, 0x0, r2, 0x2, 0x5, 0x3f})
shmat(r0, &(0x7f0000fff000/0x1000)=nil, 0x1000)
shmget(0x1, 0x4000, 0xe14, &(0x7f0000ffc000/0x4000)=nil)
ioctl$VMM_IOC_CREATE(0xffffffffffffff9c, 0xc5005601, &(0x7f00000000c0)={0x10, 0x6, 0x4, 0x2, [{&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil, 0xfffffffffffffff7}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ff9000/0x7000)=nil, 0xffffffff00000000}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff9000/0x2000)=nil, 0x5225d780}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil, 0x4}, {&(0x7f0000ffb000/0x1000)=nil, &(0x7f0000ff9000/0x1000)=nil, 0x10001}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffb000/0x2000)=nil, 0x7a8d}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffa000/0x3000)=nil, 0x6}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffe000/0x2000)=nil, 0x5}, {&(0x7f0000ff9000/0x1000)=nil, &(0x7f0000ff4000/0xc000)=nil, 0x1}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil, 0xffffffffffffffa4}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ff4000/0x4000)=nil, 0x2}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ff8000/0x2000)=nil, 0x7}, {&(0x7f0000ff5000/0x1000)=nil, &(0x7f0000ffd000/0x3000)=nil, 0x6}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil, 0x80}, {&(0x7f0000ff6000/0x2000)=nil, &(0x7f0000ff9000/0x3000)=nil, 0xffff}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ff9000/0x4000)=nil, 0x7}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file'], 0x8})
getpid()
shmctl$SHM_UNLOCK(r1, 0x4)
r3 = msgget$private(0x0, 0x8)
msgctl$IPC_RMID(r3, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg(r0, &(0x7f0000001240)={0x0, 0x0, &(0x7f00000000c0)=[{&(0x7f0000000100)="c56c101a24ac3539f347b97ab30ac398596747064ba399ceb4056319ff6dfceecdcda3f0307a72dd6d06d37fd568d11f4ce3283ba76bade1a1ff7d614c6bbd52d11db608bd3f73843703b59e4bcb9e66b1d1f6b7bf5a807232dbf663ddc8791e7df3971d187b944c3425b84a351826db00d91731b5e22393468353fb1aff87cf70ae77edba5b700ea9d35b6775f734884b1e60e91bb3f140e5a69ecb13d03bac8adf904b0ef78f0fb153db22feb5e8843c092c62b8a7833a47b4ed057bdb2efd05ac6d8ce8ce6d6634f433cbf60ab9e08b67a11bd091c61411d143ba3cec2d1eb6e46cf0ee8b3eec1eca22ab12f6c758c1878a211567054d3e2933b1e56cdb974ef20953cb587ea1d5886d35bf396f82d750a58d72d2a4d829a5122d91ab33e9ae927ba32f8e918448a2679cf6affbddb804b29d15b45a77d6bb3397b389ef6819135af92ad2c6902e100611e37cbe1098fed201d3c73e3aa862b8a93326b3e45ce6110f99dcf85bdbf323edeeed69c2e241ff0de77b8942b5a43f7138a4ea5b5a8921e649a8669a4625fda6d86357c3aa4cd773fd073f8afcf6c4a9e32b48c8ccb69e3774d8f2e80b00d6c2c10e813269dadca1004b97d61bc6065731ade5c57881bc4ce0bea59eb7d646923b0cbd26635b8d97636a89676ae574c1bc0474c9e059374df49f791ea88eaa22c4ba237cffaff205578308fb508a3e7a1b66560a13a8cdf73612ff7f7295d47f88496c1c2e916188babd0bb6d307197f2bb737f628bb6eb7914136f0c7b7ec954e47cdbf3a28cd72f21708ba9417fe6ce573f6d92127b1c7a2ad2ab894a89dfce7d0b8e058bd042d50846afc96b83447efeda1847ca919f6ad0800328261dd6bb3f6bf0c4ca4ed5db232aaac613f0c235aebdc379085322f875959f749892415da09c3363f82c4c952f78a0799c486491295b529e0615595b2718c768eb2940f748292fa9ac36d5e92f54a24c30263b48a3a2cfe2500f8f51d9597ccfbd3103c705789e59f90b2712f0351ec210bd26191c2dc2ed5da4848f2d479323bc54ce9b9f18dc0e840425201e5df5ec54759bdf3ff66ad5b90fb01fce792ba4e2cac3d3003ea9314400a859fba4d46335f9b505ab74672c89b1e495627243faa07d30f8ae598c25dc9d73f48585548c10a7129817c6584a3a618bbfe30042785b7c31163accbb860ac49c72ea7fa68e1c75a8368c36806d26110cf85cf253f8503edf3815e7078d522368cc951e129d8948f6f16e6df6e9fc7481ceb44b7f34ec8dc03d356c8943b422248e4e2d130b27c21d89ec972dc4a997bee9b1888b818a0d56ac6d1a64e28b362285cf1ac34d9465bc8362767efe0acd2073a8b502618e7ef51a2f3fd0e40240ee071a6d74d71", 0xff9d}], 0x1, 0x0}, 0x402)
r1 = dup(r0)
shutdown(r1, 0x2)
r0 = semget(0x1, 0x0, 0x0)
semctl$GETNCNT(r0, 0x0, 0x3, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x3, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000300)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000580)="0118ffac02000926fe4f9b9fd4d55259ba70a7cf09f6360f9ea14f040005040000000097069815ca5835b6f6534b01010000414191ac6193bb09919a8a372208b127f29c66755d45d5ae11c6731aede78c4421cedc6eac7d5ecbf929b2e7910599897b40c8c7f4766c3bd9ca519f1c7612b32966a5c0001d0be46ebb59a63fde26f5ca6a157ed13900ffe6c35b55a191701155a29aec01000000000000007e59a59a05bb68baab118ea42187324f8199e14585b916ff9915b909800989d8d1fbe686246fa85c22ad066d2b75a58620ee08f7397cfe2cae6e966e98d4c45356c7ba884245d743252d74b0bd01000000201c3f30e790e615a013a1dc24244ade0d510672dd77da2c8fffff0000000000000900000047000000001cd965075b49777b822443cd2740e953a80000000000000000000000000000000009eb3881885647e6b9ecd6bf81b37cd49c4287ed75b08a58f19f470bd87e5503c733fc217eb57458e55df302e2d611ae3e130100a9edbd2d2d845b823b0000000000006cd5ff5256df19b5634e2811d910faf269e55e7412e235a9072a43575893f400c7c32ed7a1d4dfedd53dc24cb41b274925139f0cea63553689b1e8a46145fc7f2c30c0d29de0815e8214f857ebd1f1e41bfb4a4c624824a96d9619e00feb108d5bb6270044e7014bd7652b7e5f4a2e420cbd1e665b3e3ccb5dcf00000000000000000026899b9a6bd7f4b2379a273ec6e7bf38", &(0x7f0000000040)=0x210, 0x0, 0x0)
poll(&(0x7f0000000180)=[{}, {}], 0x2, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x14}, {0x7c}, {0x6, 0x0, 0x0, 0xd920d164}]})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x6}, {0x7c}, {0x6, 0x0, 0x0, 0x97e}]})
write(r0, &(0x7f00000002c0)="bfa80748c4b3ff8918915dc05e7e", 0xe)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff})
sendto$unix(r1, &(0x7f00000001c0)="bf4ca2e465b8cebc79a52ad18f95355965ce6a29e7985c1b99f8980d00512d9278eaddd245c0473b92257eabb48f28f4ac06a8db164277e5763370ee106c3674ddd9a8caf81000"/85, 0xfe5c, 0x400, 0x0, 0x12)
ktrace(&(0x7f0000000040)='./file0\x00', 0x2, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000516600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283018e4fd89720fd30000000000000000a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000140)='r\x00')
open(&(0x7f0000000180)='./file0/../file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000340)=[{0x74}, {0x44}, {0x6, 0x0, 0x0, 0x101}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x1, &(0x7f00000000c0)=[{}]})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000001c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100))
getpgid(r1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xfffffffffffffff7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0xbb], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0xc5b], [], [], [{0x0, 0xeb0, 0x4}, {}, {}, {}, {0x0, 0x0, 0x48}, {}, {0x0, 0x40004}, {0x3}], {0x0, 0x0, 0x0, 0x2000}, {0x6}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
sysctl$ddb(&(0x7f0000000400)={0x9, 0x8}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f00000001c0)={0x6, 0x6}, 0x2, &(0x7f0000000280)="006c49ca", &(0x7f0000000240)=0x4, 0x0, 0x0)
sysctl$net_inet_etherip(&(0x7f0000000180)={0x4, 0x2, 0x102}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000240)=[{0x81}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
setreuid(0xffffffffffffffff, 0xffffffffffffffff)
sysctl$net_inet_tcp(0x0, 0x0, &(0x7f0000000080)="0118ffac02000926feba", 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x4d}, {}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$TIOCFLUSH(r1, 0x800c745b, &(0x7f0000000140)=0x100)
sysctl$net_inet_udp(&(0x7f0000000400)={0x4, 0x2, 0x11, 0x5}, 0x4, &(0x7f0000000600), 0x0, &(0x7f0000001080), 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be", 0x31, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210]}, 0xfffffdfa)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
fchmod(0xffffffffffffff9c, 0x0)
setpgid(0x0, 0x0)
openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(0xffffffffffffffff, 0x29, 0x69, 0x0, 0x0)
r0 = socket(0x2, 0x8003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1021, 0x0, 0x0)
ioctl$WSMOUSEIO_SCALIBCOORDS(0xffffffffffffffff, 0x81205724, &(0x7f0000000480)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {0xc8, 0x200005}]})
poll(&(0x7f0000000100)=[{}], 0xe6, 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
poll(&(0x7f0000000180)=[{r0}], 0x1, 0x0)
r0 = syz_open_pts()
pipe2(&(0x7f0000000000), 0x0)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff})
r2 = socket(0x2, 0x2, 0x0)
connect$unix(r2, &(0x7f0000000140)=ANY=[@ANYBLOB="8002a195c3"], 0x10)
dup2(r2, r0)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000080)=0x43cc0, 0x4)
r3 = dup(r2)
sendto$inet6(r3, &(0x7f0000000040), 0xfd4d, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x81}, {0x61}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0x0, "0000009e0000000000002200000000000c00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x6}, 0x4, &(0x7f0000000040)="e38c594bde7feea23d3abc45924083b3c07171642e686204a36e16c8ca7703a511a96b9077d2d9445bc401c8c6923fe2b7d1368cb4830a253fee19fe105e9592a6ad4042a14c9e9cecf58b6aa81ff356905f05449e4ee4b05a74883784bac39f940dfb935d8c4e7e4dcbb376ad4c0ca788b2fe67f683ceff", &(0x7f00000000c0)=0x78, &(0x7f0000000100)="99168fc6202109546fb367b5502cddd290b804bcad19b9effe55cbe54bf647268445aefaf9b382a4718525b29e4ea3c70f2ae6aff58696b66edf6ffbbd99ec2b89419befbc72f27bdf8770d954a02380987dfef97ea2125129a5755bed370f7a7ba4f41931579d56949a58a2e6573e0325a064728cf403a51f694df31270d1751a27cfe0038e1f9f264ba73efc30e01cc7768a4a90557e6bc61af0c35d3d69977a03277822c37ad76c20f4aaecb2469004294ecb60f54e78bd013e37129c828ca6dcc9a5fc9b9e84836ac1c3ab7c877e84b4f074843241c9a5fdf34d5942a1efd51ac927a9fecb4bc5f01030", 0xec)
pipe2(&(0x7f0000000bc0)={<r0=>0xffffffffffffffff}, 0x0)
bind$unix(r0, 0x0, 0x0)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup2(r1, r1)
r2 = semget$private(0x0, 0x1, 0x2e)
semctl$GETVAL(r2, 0x0, 0x5, &(0x7f0000000280)=""/231)
ioctl$BIOCGDLTLIST(r1, 0xc010427b, &(0x7f0000000240)={0x4, &(0x7f0000000200)=[0x5, 0xffffffc7, 0x200, 0x1e]})
mknod(&(0x7f00000001c0)='./bus\x00', 0x2000, 0x4086334)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
pwrite(0xffffffffffffff9c, &(0x7f0000000200)="58f28b4a5db5a260dc70bb0087a65afb5c86198e97413a9463ff3d64c41a5bf39ebfc402d621f8841bd57a4a117c5057bfb7cac3cc514a0dac77159272423f09748eca6f5b775783c3abde456e60129c2d203a63ea8da5924497ea06ec779ce7e773622f9b4ce4372dbfbffddf8d1bac47b839faefd0ff6bd16261bfdf2d928bfea7b6fff2f8465927eab42f1207135a8d628f133f6dd8e9ffdca52c373fbae20797cfadcc8be091a7ed967e41801a6673f6", 0xb2, 0xa977)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
write(0xffffffffffffffff, &(0x7f0000000040)="34cf362b3ce9c93d7f", 0x9)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x1)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f000052fff8)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000080)=0xc)
chown(&(0x7f0000000080)='./file0\x00', 0x0, r2)
r3 = getuid()
setreuid(0xffffffffffffffff, r3)
fchdir(r0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x8000, 0x0, "27d5a1cd95a03fd3926c6d60043de90264586b1a"})
syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x7dc, "ae9becbe66983db038ceadb2d2a171d09dfa331c", 0x0, 0x1f})
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
chown(&(0x7f0000000000)='./file0\x00', 0x0, 0xffffffffffffffff)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
sendto(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
dup2(r1, r0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a21", 0xbe}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
write(r0, &(0x7f0000000100)="b8", 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a0", 0x1cb}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x29}, 0x2, &(0x7f0000000080)="ef5c34011628a71588a9bb4b42927cf30fdbc64d557c6f6f80eec37f9b5b8ae0f78f7deef4d13f62db1b9632136d27b3fcdd694ffb5f484170533d6f5b8e24b03a16a4dc171bb5f6eef4f778594370ae903372bd3315a6d5354907b1076aaff7", &(0x7f0000001080)=0x60, &(0x7f00000010c0), 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x24, &(0x7f0000000000)="aad485c4", 0x4)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000002})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x1, 0x8, 0x20002fffff7ff})
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1006, &(0x7f0000000200)={0x0, 0x8}, 0x10)
getsockopt$sock_timeval(r0, 0xffff, 0x1006, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f0000000240)=[{}, {}, {}, {}, {}, {0x2}], 0x6)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffffffc})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a18", 0xd}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = kqueue()
fcntl$setown(r0, 0x6, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = socket$unix(0x1, 0x5, 0x0)
bind(r1, &(0x7f0000000100)=ANY=[@ANYBLOB="2d01da"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040))
dup2(r2, r0)
listen(r1, 0x0)
select(0x40, &(0x7f0000000000)={0x1f}, 0x0, 0x0, 0x0)
select(0x40, &(0x7f0000000300)={0xff}, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x50}, {0x20}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000380)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80146945, &(0x7f0000000300))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202c17e7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000001480), 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000040), 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet6(r2, &(0x7f0000000300)="94", 0x1, 0x0, 0x0, 0x0)
r3 = accept$unix(r1, 0x0, 0x0)
write(r3, &(0x7f0000000240)="ddee43ab8734abe72e8289e48f848dcae6855abde176689fd16e6ad78a4f273bc1d2e3f3f911c89a2472d11399504870b9fb0e36d8892e80897800000000d20a2c82125431700f50a6b8d22db0ec0ff3a1800280b535084f92a7499cd07afbd1d0b82c439984f338ffbf66decf2ba302619dc2dcef33b15aa3d89beb2b70ddbd84311f09a2639749e5f4c3669169eb4be5a922395a28149f4d2ec0588916c297ac43adfbb5775ab949938e2baef7e6fa3703487dc7cd0255926c0ec9", 0xfffffea5)
execve(0x0, 0x0, 0x0)
dup2(r1, r3)
faccessat(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x38, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x1c}, {0x35}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0104454, &(0x7f0000000240))
clock_getres(0x5, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0084463, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x1c}, {0x40}, {0x6}]})
syz_emit_ethernet(0x3e, &(0x7f0000001140)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "a40300", 0x8, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x3, 0x2, 0x8}}}}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f0000000b00)={0x8, 0xadd, 0x2, {[0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x2, 0x7fffffff, 0xfffffffffffffffe, 0x0, 0x5, 0x1f], [0x7fff, 0x75fd, 0xffffffffffffffc0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x9a4f, 0x7fff], [0x101, 0x42, 0x3, 0x80000000, 0x8001, 0x1000, 0xb1e8], [0x10000, 0xffffffffffff0000, 0x7fffffffffffffff, 0xec, 0xa53a, 0x7fffffff], [{}, {}, {0x3f}, {0x1, 0xff, 0x0, 0x1ff}, {0x0, 0x5, 0xffffffff, 0x7fe}, {}, {0x5, 0x1, 0x6fa2, 0xe2}, {0x5, 0x40, 0x5, 0xfffffffffffffffe}], {}, {0x0, 0x0, 0x0, 0x2000000000000000}}})
r1 = openat$zero(0xffffffffffffff9c, 0x0, 0x0, 0x0)
preadv(0xffffffffffffffff, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r2 = socket(0x1, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2e)
r4 = socket(0x18, 0x3, 0x0)
poll(&(0x7f00000001c0)=[{0xffffffffffffffff, 0x4}, {0xffffffffffffffff, 0xd0}, {0xffffffffffffffff, 0x1}, {0xffffffffffffffff, 0x10}, {r1, 0x40}, {r4, 0x1}, {0xffffffffffffffff, 0x2}, {0xffffffffffffffff, 0x10}, {r3, 0x20}], 0x9, 0xb7)
openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x800, 0x0)
ioctl$FIOASYNC(r3, 0x8004667d, &(0x7f0000000180)=0x4)
setsockopt(r4, 0x29, 0xb, &(0x7f0000000100)="000073fa0000", 0x6)
setsockopt(r4, 0x29, 0xa, &(0x7f0000000040)="78482dfe", 0x4)
openat$tty(0xffffffffffffff9c, &(0x7f0000000140), 0x2, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8000002, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x4000002, 0x0, 0x0, 0x0, 0x800000000000], [0x9, 0x0, 0x0, 0x5, 0xfffffffffffffffd, 0x0, 0x0, 0x1, 0x2], [0x1ff, 0x0, 0x20, 0x0, 0x400000000000000, 0xfffffffffffffffe], [0x100000000000, 0x2000000000000, 0x0, 0x1000], [{}, {}, {0x0, 0x0, 0x3}, {}, {0x0, 0x4, 0x2}, {0x3, 0xfffffffc, 0x2000}, {0x0, 0x0, 0x0, 0x1}, {0x0, 0x0, 0x0, 0xf7fffffffffffffc}], {0x0, 0x0, 0x2}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x2, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r5)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x28, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x87}, {0x4}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000080)={@remote})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x6c}, {0x3d}, {0x440e}]})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
syz_emit_ethernet(0xe, &(0x7f0000000040)={@remote})
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206949, &(0x7f0000000300))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
bind(0xffffffffffffffff, 0x0, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
socket$inet6(0x18, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
close(0xffffffffffffffff)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
sendto$unix(r2, &(0x7f0000000100)='(', 0x1, 0x5, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, &(0x7f0000000080))
poll(&(0x7f00000001c0)=[{r1, 0x80}, {r2, 0x1}, {0xffffffffffffffff, 0x8}, {r2, 0x80}, {0xffffffffffffffff, 0x2}, {0xffffffffffffffff, 0x100}, {r2, 0x20}, {r1, 0x4}, {r1, 0x104}, {r0, 0x100}, {0xffffffffffffffff, 0x20}, {0xffffffffffffffff, 0x110}, {r0, 0x4}, {0xffffffffffffff9c, 0x80}, {0xffffffffffffffff, 0x2}], 0xf, 0x6b46)
shutdown(r2, 0x0)
munmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
sysctl$kern(&(0x7f0000000040), 0x2, 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000e40)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {&(0x7f0000000c80)}], 0x6, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
madvise(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
msync(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
sysctl$kern(&(0x7f0000000380)={0x1, 0xa}, 0x2, 0x0, 0x0, 0xffffffffffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{}, {0x0, 0x0, 0x0, 0xffff}]})
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x4}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x4, 0x0, 0x0, 0x0, 0xfffffffffffffe62)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x30}, {0x64}, {0x16}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
r0 = socket(0x18, 0x8002, 0x0)
r1 = socket(0x800000018, 0x3, 0x0)
dup2(r1, r0)
getsockopt(r0, 0x3a, 0x12, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000080)={0x4, 0x2, 0x0, 0x1c}, 0x4, &(0x7f00000000c0), 0x0, &(0x7f0000000180), 0x0)
symlink(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='./file0\x00')
symlink(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000200)='./file0\x00')
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
chmod(&(0x7f0000000200)='./file0\x00', 0xbf)
setuid(0xee01)
r0 = kqueue()
r1 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000140)=[{{r1}, 0xffffffffffffffff, 0x3}], 0x2d44000, 0x0, 0x0, 0x0)
fchmod(r1, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f00000001c0)={0x0, 0x0, 0xffffffc0, 0x0, "9b4f0e92fa5a2069641288a0606db0ccce620da4"})
poll(&(0x7f0000000080)=[{r0, 0x4}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000003c0)={0x3, &(0x7f0000000040)=[{0xc}, {0x2c}, {0x6, 0x0, 0x0, 0x200}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f00000000c0)="fbafca8d1a029be96914e10700bb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x28}, {0x3}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000200)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "22b812", 0x8, 0x0, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @multicast2}, {[], @udp={{0x1, 0x3, 0x8}}}}}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, 0x0, 0x0)
sysctl$net_inet_divert(&(0x7f0000000000), 0x8, &(0x7f0000000040)="438193eaee881f3635858a323ce6804ad0e358f739d494cda144c76db1b32b0445a01f142d83683d", &(0x7f00000000c0)=0x28, &(0x7f0000000180), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc4504449, &(0x7f00000016c0)={{}, 0x0, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x1d}, {0x48}, {0x4000006, 0x0, 0x0, 0x7fffffff}]})
pwrite(r0, &(0x7f0000000140)="56f33a873743d1b828b0c2d5fcb9", 0xe, 0x0)
r0 = socket(0x2, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x80, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000180)="a1045729c9accc00010000329446ab8d04a7a899214310590100520670003ba8fc499277210000008000000000825ef568df068fb95dcf6eff2d02e8d10000000000000008b6fddf1b863d5165ef398f574f502c21dd34e5d83a19e0c9ef1004ee4bbe6e363705", 0x67}, {0x0}], 0x2)
execve(0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0xb, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x92})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
r2 = kqueue()
kevent(r2, &(0x7f00000000c0)=[{{r0}, 0xfffffffffffffff8, 0x1}], 0x3, 0x0, 0x4, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/\x00', 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='c\x00')
execve(&(0x7f0000000080)='./file1\x00', 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000300)="a449e47fe75fdf916bca74e767610ff81a288c4637e529bdfc84467944dca7b3b2d7158d78e3b2e7dbcf18a6e618", 0x2e}], 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
ktrace(0x0, 0x1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x5}, {0x35}, {0x6, 0x0, 0x0, 0x8000000}]})
pwrite(r0, &(0x7f0000000440)="eb00d738a335b66b0543e4f74d36", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0x4, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
ioctl$TIOCFLUSH(r0, 0x80206979, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x7}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x66, &(0x7f0000000140)=ANY=[])
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x81)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{}, {}, {}]})
poll(&(0x7f0000000000), 0x32, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x0)
open$dir(&(0x7f0000000140)='./file1/file0\x00', 0x200, 0x0)
unveil(&(0x7f00000000c0)='./file\x00', &(0x7f0000000100)='r\x00')
mknod$loop(&(0x7f0000000040)='./file1/file0\x00', 0x0, 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000240)=[{0xc0}, {0x4d}, {0xc76}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @random="d2ad2c719745", [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @remote, "3f034d5dcc6595da6c7d976af8cd6e61"}}}})
mkdir(&(0x7f0000000000)='.\x00', 0x0)
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3200)
r0 = open$dir(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000140)=""/230, 0x400}], 0x21)
r1 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r1, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="14000000290000002f"], 0x38}, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0)='c\x00')
unveil(&(0x7f0000000140)='./file1/file0\x00', &(0x7f0000000240)='c\x00')
fchdir(r0)
chdir(&(0x7f0000739ffe)='..')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000029c0)={0x0, 0x0, &(0x7f0000002740)=[{&(0x7f0000000540)="71f2893e34ac2dcb05a97d510a133e33c3eaa7fcc1dcec634e413153557c4e90d571480c41dfa94b2c2321f44bc730d2ea3697d5d42017d0432e8322f05aab1d347ce3c830b3ed67b453fe6fcc19250dc4177ce0338d5cc4b39d86e87519836129779b105dcace09f591c919bd6c324cd9159b8b24f0e7658803e17bac383ac0f847553468618facbeee9965f90cc3161ae859c18f0d9b60728ed04e2842ea5bdb3db7c1423bc7931c405e11fb4bd42c6003f742a1f20de19394bcd45614c52a69b5e70b50d720ac1929f0a0814eb0497b521f98e68b116f10b74f7e4600aa4285f6ad54c51cf139206b6675f3003716147d0e5b18accf20e94aee8b6e82cc51d5248b8f4d518a4753f6a0f66f434525fdc2bdd8b0728b3cb1e52fceb4451a0a709a14b31cbfeed8d417d367668660c8558fe43fbfeabdc6ab029197856354e7373c6747ccf1404ec3a6f5a027929f7b0807628e69c45cf561bf2597265dfef594e65f27e695496ee62627bd2b85e91f4d96d74af2f0f621d8f7cfcf2992bd1c5ef4889801a8e334274289cefc5bcb6fba8cbe3807965d7d7f843a32127443455ff5bc7056777d9c7eba6dad1944476ed57cd5ed361e7eb281cf12b85ffb776ee82148ce4d01ec57aaa38c7fae730db7702519369242c0031d67a6b9e1a25122de3a91703ea516ec00cc53f15d7c5bf64d039b28da2b6bfb5f9a13f11a85d12fd76f78b87afddd749ca8685f49252dfc465589153b0b719f9f7b6548663e60bc3ba157c2ee3fcecedb9f4bfe71fb14984fb103cbb7622c8df9f117cdcce4f911bc578f6dff991cc85a1e5fe07e20119eb446c84869422d55bb135e64f4ee2b116d9245963a9790619fdf1130aa6590c1df3ce32004d3f897b0187a7c397915be9196a219299c3b2345faec1a965a64a752f5c95cb818782662aa657a29a878ffeb9d55eeebf4fd12d07df4074c3882085ddbd6effdb33b303a05a3e2c79da671e6718c32b7042774434be8d0259147508384ad6dbe3467755b8b1f39b7e83406fec659829821ee7ff2b6cda0c5a5b96b0a37fb217eda06897ad11b7b3448d8363304c4dd64a1bf883cbdac0e3deeb13132ceb21aab87de7009587dbf740e4f97b22e61daec5cb78bc88f1ce7961787db0e5b3a3d4effafde862b1d91fe1b3e998efc06c9e7a99c4df960660ace03fa0d8fcab2a81acfba051348a9cd634b71e7c23fd6159af389804c5c09cea8391429d32b6af608c6d34247ceadc675c56f015cf4efffe0f2acccae837fe0039a416b4d76d66103e762e725d691717eeb419be4ba3d954ef9673ccd584b8c0cc8238107", 0x3b1}], 0x1}, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000080))
sysctl$net_inet_tcp(&(0x7f0000000080)={0x4, 0x2, 0x6, 0x17}, 0x4, 0x0, 0x0, &(0x7f0000000180)="e4ef3471", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x3d}, {0x2}, {0x6, 0x0, 0x0, 0x400}]})
write(r0, &(0x7f00000001c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
getrusage(0xffffffffffffffff, &(0x7f0000000140))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x37, 0x0, 0x0, 0x0, "1f5012000000f5ff0100d9566d7d001200"})
r1 = kqueue()
kevent(r1, &(0x7f0000000080)=[{{r0}, 0xfffffffffffffffe, 0x6b}], 0xff, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSTOP(r1, 0x2000746f)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000140)={0x0, 0x7, 0x401, 0xd, "cf11050038050000000000000000008000000800"})
write(r0, &(0x7f0000001200)='\r', 0x1)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0xe01)
openat(0xffffffffffffff9c, &(0x7f0000001880)='./file0\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x1ff})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050300000000000000170b", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b10005016000009005001b0007000000331c04fecea10500fef96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c50000200200000d230000020208a371a3f8343712051e1d89e0000400000000000420000000000000000000", 0xb1, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x0)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIONBIO(r0, 0x80047476, &(0x7f00000000c0)=0x80000003)
r0 = socket(0x2, 0x3, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f00000006c0), &(0x7f0000000700)=0xc)
recvmmsg(r0, &(0x7f0000000640)={0x0}, 0x10, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000500)={0x0, 0x9, &(0x7f0000000b00)=[{&(0x7f0000001100)="f96dfe6588d9910998b4aeea546c15208d882d2e0a0190fd2e6ad3dfc7cc27157eaca01752438c590a98ca4e416a23d4d68ef670ca82a6d3c6ec05f99c7e1424d7859962e59741727417a6d9a8e2e0979bc8ce13fbc40e9d0d048c56f8cfc66e36293646fb00137d5b2a98d6d40bbb97f9556b3223a0c2275c4975d76d080c8ce5", 0x81}], 0x1}, 0x0)
msync(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x5)
sysctl$vm(&(0x7f0000000000)={0x2, 0x8}, 0x2, 0x0, 0x0, &(0x7f0000001180), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x4c}, {0x40}, {0x6, 0x0, 0x0, 0x20}]})
pwritev(r0, &(0x7f0000000200)=[{&(0x7f0000000300)="9ccbb0ea13c4fff8b82673000000", 0xe}], 0x1, 0x0)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x2}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x7}, {0x2}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
setuid(0xffffffffffffffff)
clock_settime(0xffffffffffffffff, 0x0)
kqueue()
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, &(0x7f0000000040)=0xfffffffffffffe99, 0x0, 0x0)
r0 = semget$private(0x0, 0x0, 0x8f)
r1 = semget$private(0x0, 0x4, 0x19a)
semop(r1, &(0x7f0000000400)=[{0x1, 0x100000001, 0x1000}, {0x2, 0xfffe, 0x1000}], 0x2)
semop(r1, &(0x7f00000011c0)=[{0x0, 0x7d}, {0x2, 0x2}], 0x2)
semop(0x0, &(0x7f0000000100)=[{0x0, 0xa4}, {0x4, 0x8, 0x800}, {0x3, 0x3ff, 0x1800}, {0x0, 0x4, 0x1000}, {0x2, 0xfffe, 0x1c00}, {0x2, 0x1f}, {0x0, 0x4, 0x1000}], 0x7)
semctl$SETVAL(r1, 0x4, 0x8, &(0x7f0000000080)=0x9)
semctl$SETVAL(0xffffffffffffffff, 0x0, 0x8, &(0x7f00000000c0)=0x82)
r2 = open(&(0x7f0000000180)='./file0\x00', 0x40, 0x40)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000140)=[0x8000, 0x5829, 0x8, 0x0, 0x1, 0x1000])
semget$private(0x0, 0x1, 0x211)
syz_open_pts()
getuid()
pwrite(0xffffffffffffffff, &(0x7f00000001c0)="292dd317b9517784b6ea673e3741fe6baf3fd8ac4fd2b42afdc3c02431a8c2e342468c64212c66447e5286b3c016069641f018d225f5d1a9cc311825d76777c0ed312ae8ce245d067052825e6a0b3999c062dea68e9e170c0b23ec3a41992a19e58b42137d6a75c346fb26653647ed178ebe8f2662be56d0eb74b5c1fbdb6603dd2d27c749cfdb5ed56acdce3b3406796bf406fd5ae5c2334b36d3a421bda5621facf890f0bd176436012641e579dee3d5c8ca4d083dfdc35c857603f7665dce4840203e21d90d273668f326cc6130b57768618245ad3e3723d7b4f33abee4a58605ed6924f19d7274298f8c520808a4c12448f8660b43", 0xf7, 0xfff)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f00000002c0))
ioctl$VMM_IOC_CREATE(r2, 0xc5005601, &(0x7f0000000680)={0x10, 0x80000, 0x4, 0x4, [{&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffc000/0x1000)=nil, 0x100000000}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffd000/0x3000)=nil, 0x1bc}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffc000/0x2000)=nil, 0xffffffff}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffb000/0x2000)=nil, 0x33b}, {&(0x7f0000ffb000/0x1000)=nil, &(0x7f0000ffd000/0x1000)=nil, 0xb1}, {&(0x7f0000ffa000/0x1000)=nil, &(0x7f0000ffc000/0x1000)=nil, 0x3}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffc000/0x3000)=nil, 0x1f}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffe000/0x1000)=nil, 0x4}, {&(0x7f0000ff7000/0x4000)=nil, &(0x7f0000ffa000/0x3000)=nil, 0x41}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil, 0x5}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffc000/0x3000)=nil, 0x1}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil, 0x9}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000fff000/0x1000)=nil, 0x1}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000ffb000/0x3000)=nil, 0x80000000}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffe000/0x1000)=nil, 0x5}], ['./file0\x00', './file0\x00', './file\x00', './file0\x00'], './file0/file0\x00', './file\x00', './file0/file0\x00', ['./file', './file', './file', './file'], 0x100})
r3 = dup(0xffffffffffffffff)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f0000000c40)=0x2e)
openat(r2, &(0x7f0000000580)='./file0\x00', 0x10, 0x194)
r0 = socket$inet(0x2, 0x3, 0x0)
getpeername(r0, 0x0, 0x0)
msync(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0)
minherit(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000200)=[{0x61}, {0x81}, {0x66}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f00000007c0)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x20}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getppid()
setpgid(0x0, 0x0)
setpgid(0x0, r0)
r1 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r1})
r2 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r2, 0x80047476, &(0x7f0000000040))
r3 = fcntl$getown(r2, 0x5)
ktrace(&(0x7f0000000080)='./file0\x00', 0x7, 0x40000132, r3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000002900)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x25}, {0x30}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@broadcast, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @local, "f9438477b7432b191466538a04047155"}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x150880000})
pipe2(&(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
dup(r1)
close(r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000340)=[{0x30}, {0x20}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000080)={@empty, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @rand_addr}, @icmp=@time_exceeded={0xb, 0x0, 0x0, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @multicast1}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x54}, {0x2}, {0x6, 0x0, 0x0, 0x400}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCGDLTLIST(r1, 0xc010427b, &(0x7f00000000c0)={0x0, &(0x7f0000000040)})
r0 = syz_open_pts()
pipe2(&(0x7f0000000000), 0x0)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff})
r2 = socket(0x2, 0x2, 0x0)
connect$unix(r2, &(0x7f0000000100)=ANY=[@ANYBLOB="800208607f"], 0x10)
dup2(r2, r0)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000080)=0x43cc0, 0x4)
r3 = dup(r2)
sendto$inet6(r3, &(0x7f0000000040), 0xfcec, 0x0, 0x0, 0x0)
setuid(0xffffffffffffffff)
setreuid(0xffffffffffffffff, 0x0)
sysctl$vm(&(0x7f0000000080)={0x2, 0x1}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x64}, {}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
sysctl$vfs_nfs(&(0x7f0000000100)={0xa, 0x2, 0x1}, 0x3, &(0x7f0000000600)="0596e95d524864953c9cd108dc597cd1048804e0ba7e65453335e3e4c82c33a174d0b33859fa65d138ea277174d8cc2c9580c512214903c3ee49351d720ca3f4cbf989dc39f9d10e7251ebfcf34057012f487f340b8a9538771011e18063d2ebe8566a86011c422b7e31ac549f263735498c6765b2ff626c8ed4ff5ffd30784fa98ed762931b326bb8db31718f16e3665b96ebbc835f60abdb68aafdc26539d0d6e70213f43522edc6d7441066cb08a938033b498b682f7ccecfb7985599025ba53e66d971eb3ab9d159b713b5230174b14d0c1cbcc927c0905260dcb781e523b57e2e16a857f4a74960c8f2acb9b5870f2cbf2f541f07b7f27b4115222d39cafe5fc0a78b28d4f97901f25a1b2ae27c46d2147b899dc20da5e47c669417c584777c79b0f89ddadeb3b668f3e5be522a65caf0b91bc39e3000c8952dc88f1fb60c7198e70b648ce3f317197d783cdbc97025e36ca7dc1265ae5b30ce26d3311acfe782613095ceb9788aac2e79d83ac76a050cf430d01845c8a52afb102537ce3216fc7c376098a11277fbf3e0b01549b13ab67d609ceb853743ea7491b44041f1dd5b61afe341d8c45a3328c3160a3f6cc3319f2924c7b91ac9ad50f8194652d01c14896c262199f14a8d10dfd32882b0124b5a0341f9b153f5c727d57a7d6959fb49e3af9232a31f385bd78b9f0ba744e7883e5b8f6215329c2fff6792fa859fda55497ce6a6f066fd6b3340ca3bc0f22a8716564932ec8c9011a892082583f8c969dc06bbb14c18c69b95c165adf1a32d85a99a2233e92088ca0b1b01f8ec0b00cc3b78cfa2b2fb550d1e0e803ea418ba38d08af060ac6ccb9d95b31f8ae060598236b50b714f017f640c27a3a535", &(0x7f0000000180)=0x270, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{r0}, 0xfffffffffffffffa, 0xa6}], 0x0, &(0x7f0000000400)=[{{r0}, 0xfffffffffffffffa, 0x17}, {{r0}, 0xfffffffffffffff9, 0x13}], 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0xb57c, 0x0, 0xfff00000, 0x0)
kevent(r0, &(0x7f0000000040), 0x409, 0x0, 0x4482, 0x0)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
mmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
connect$unix(0xffffffffffffff9c, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa)
munlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x1d}, {0x20}, {0x6506}]})
syz_emit_ethernet(0x32, &(0x7f00000001c0)=ANY=[])
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000001340), 0x0, 0x0)
fchdir(r0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x200041ee, 0x0, "77917dfc71faeb904b4000"})
mknod(&(0x7f0000000100)='./file0\x00', 0x1ffb, 0x0)
writev(0xffffffffffffff9c, &(0x7f0000000000)=[{&(0x7f0000000080)="33102b27cad177564d3ec00800000000000000d51e560300000000000000b5e1e30900472f3ea9fabded6204175eb73cf4e7d9f5ea2281d5de7d1cdaa28c322ab8000000b836002d7e7f", 0x4a}], 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x2, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106924, &(0x7f00000000c0))
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x1, 0x0, 0x0, 0x2000300010000})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
shutdown(r0, 0x0)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1a}, 0x4, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @remote={0xac, 0x14, 0x0}, {[@rr={0x7, 0x3}]}}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc028698b, &(0x7f0000000300)=0x4)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x35, &(0x7f00000000c0), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
fsync(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0x4}, {0x4c}, {0x1006}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
sysctl$net_inet_tcp(&(0x7f00000002c0)={0x4, 0x2, 0x6, 0x9}, 0x4, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
sysctl$net_inet_tcp(0x0, 0x0, &(0x7f0000000100)="783fe0aced9967752663c940433e8215f365ec027a55f41819cb123b2fa0fe93e370d8d0612795cc33cd504686fe28858932a098b820b8b3a8a654314ef58797", 0x0, &(0x7f0000000300)="f995a6cd07880eea9a1ae3a7e7ac0d61bfa221db05490765c211ac74b8aa77362ba4049976c6d2915360319c39889caa84a0455d8f0e03bc1a752c10d6d98bcb451f8414c4bbbf380dc3e18848ee78c3d504a1b8280ad9a3f8b27b855c8abca8f32c3df4f897c67a77e6fd76024fa3329a6614130f024ba0b7da86c03cade910d2268683048b98393d6d", 0x8a)
syz_open_pts()
open(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0x6662, 0x0, 0x8001, 0xfdb, "0100000000686136ef66e1dda81794d8e9876d0f"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
write(r0, &(0x7f0000000040)="81713bf83c98f8dc01b21dd779cf5e7b643168bfa42165adfa8429d50cb01109eeaffca1d0f77028d0ab17f9a4037b9b4163ed4d3132d20e9d9d3438010d7cf7ae3d5c0055fd6a69dc5c4c09dafee9e7", 0x50)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0xc0}, {0x54, 0x0, 0x0, 0x40975e6}, {0x16}]})
write(r0, &(0x7f0000000300)="ccf06c9bbdc73dfb7c40df5037ca", 0xe)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000006c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000040)=0xc)
getegid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setregid(r4, 0x0)
setegid(r2)
r5 = getuid()
fchown(r0, r5, 0x0)
setreuid(0xee00, r5)
getegid()
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={0xffffffffffffffff, <r6=>0xffffffffffffffff})
getsockopt$sock_cred(r6, 0xffff, 0x1022, 0x0, 0x0)
setregid(0x0, 0x0)
setgroups(0x0, 0x0)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfee6)
ktrace(&(0x7f0000000040)='./file0\x00', 0x4, 0x104, 0x0)
r0 = socket$unix(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x1, 0x0)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
r0 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000080)=0x7, 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8b02"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=@un=@abs, 0x10)
syz_emit_ethernet(0x23d, &(0x7f0000000580)=ANY=[@ANYBLOB="664bc49a217faa8aaaaaaabb88a88794e22a002a4086dd6fd45ead01ff47fe800600000000000000000061b2e23ec01b3100290700000000801f1ee0410b3b0004d1640000001d00c1880500c45411000000f47f8b3c0ec3a7e34ba919c765dbe8c216d156719c6930aee78bae9b317f1579956d2409ccc2b6bf80e2ffffffffffffff007fc1210009000000ffac1400aa001900001a2ca33eeaf378ad16007e951d1229656011a442c9e600bf022622baefdb0a0000a2cd46f5a2bf88a167bc033a5aae2fd17073d40ddb38d58999", @ANYRES16])
syz_extract_tcp_res$synack(&(0x7f0000000080)={<r0=>0x41424344}, 0x1, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000180)=0x6)
syz_emit_ethernet(0x3a, &(0x7f0000000200)={@random="43d728da3c85", @local, [{[{0x88a8, 0x0, 0x1, 0x3}], {0x8100, 0x6, 0x1, 0x3}}], {@arp={0x806, @generic={0xf, 0x8390, 0x6, 0x0, 0x4, @local, "", @remote, "0063c247f01d6a64f0e227b24c5fb161"}}}})
write(0xffffffffffffffff, &(0x7f0000000180)="3c9ebb8a65237a19000000000000", 0xe)
openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x800, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000280), 0x1, &(0x7f00000008c0)=[{{r1}, 0xfffffffffffffff8, 0x6f, 0x1, 0xd}, {{}, 0x0, 0xb6, 0x40, 0x1f, 0x6}, {{}, 0xfffffffffffffffc, 0x29, 0xfffff, 0x3, 0xffffffffffffffff}, {{r1}, 0xfffffffffffffffa, 0x40, 0x2, 0xe278}, {{}, 0xffffffffffffffff, 0x1, 0x80, 0x8, 0xa32b}, {{r1}, 0xfffffffffffffffd, 0x12, 0x40000000, 0x80000001}, {{}, 0xfffffffffffffffa, 0xf, 0x80, 0x0, 0xb01c}], 0x5f, &(0x7f0000000500)={0x101, 0x3ff})
r2 = syz_open_pts()
syz_open_pts()
syz_open_pts()
ftruncate(r2, 0x0)
bind$unix(0xffffffffffffffff, &(0x7f0000000740)=ANY=[@ANYRESOCT, @ANYBLOB="c7a5ad7069b1fc4c9eb65cbb97c75d662740376399092d04002fb2cc07000000e9eba10bc31f1094af16e06dfefcfa8ab373b300846c8adfa16414dd16a5626e01bbdaae16548a8c20060addb081b1f820a23b8708404a6f52f7da8135c9d4f2f6e773e09bbf430124e08111e6da2d0400f160ecf9c71e78325745e0977c7efdc49fed6ce5c57ea8a20060e438855ef8181353c920e7df6a275bd5d5ec4ef19b5210b7d8ccf67382a918d13c7ff21a95ed000000fdb6000000b858d738bf952d2cfa778ff1dd17e62706aa4afe961a468ebe83042ed74d508285a190f9589db483a02e83aa396b01191c8e39b8da4622cf832d2f0a8820e81f29b62c712de30cb61e5c6fa9a505e8826e4d1f5f217efae489c2c1920ee2eb488ba3017098e53600cb050938d9015a133921d4", @ANYRESOCT=r2, @ANYRES64=r0, @ANYRES64], 0xa)
syz_extract_tcp_res$synack(&(0x7f0000000140), 0x1, 0x0)
sysctl$machdep(&(0x7f0000000000)={0x7, 0x10}, 0x2, 0x0, 0x0, &(0x7f0000000280), 0x0)
sysctl$machdep(&(0x7f0000000040), 0x2, &(0x7f0000000380)="5b4d0d9cd45262796ac86c714c714a9402aacc686e774098eddad728802c60b0bd5739c10aceac7d727818559aea72a1a34807d3f097d6c90a57d410bad08630078c7e6adcf136e688ecc1b062404f1d1936300272b23529b3d9d1496f46411af1df6f94b76041d7b9cf0b558782552dea7f0893e0acf878b32a20a0f4e48e6c2e987c21083a3b13a536b68a13", &(0x7f0000000100)=0x8d, &(0x7f0000000440)="ab29bfa8e57dab562705e673556d949556900a0f1e4608e15155c52c6891ed5aeab81929aafbd0062c0f2255e5ff9255d3b8d0db8ad6ffe5365b7996b262a8cf4ffb20ef9b6b10da474ebd94e26d903393d6845c3eef653432235318110560b3913ac6d20da046e4cc05eb1e8a9f2a2e273a3c9f4de391bcaeacca94bd7235dc9472bdd30918024c4a94c99e6f6a2d482f8ba2e9f28245a06f77f144afc477b134375c9bf24385b4", 0xa8)
syz_emit_ethernet(0xe4, &(0x7f0000000a80)=ANY=[@ANYBLOB="aaaaaaaaaabbaaaaaaaaaabb889a0b0081003b0070032e98b069e60585b32171f63f93aba28c732f12e5b630a87fa4018a78134130c118900617270644ff78bb5183532f34159cd2f1564c8a368626e8ca67500c49749479b812198076eb58c84ebfad035362601d7b8f9c72cdf8af2bc4c0342eca16a88ec57526c48d817be04b7af43df04fc67db6aa26ed098187b4a1ea1c94c07fc61d460b84667f92f522c0bafae03d967c060000000000000086942bc6ed7be81c7d77a3479c306e84a8b4ab8bccfff71f21d381b0748549316aa12607bc2994d735413c0ac0714fee34e0c311422ad74c220060de01bc2805cee7ef9486000000000000a2fc8fa501003470a8f2e912d89bfadd738ce4888bc18ce6eda73caaaff301983c0184609dd84d23c5ea40d43d6f9cf3579a3acfc15152812a21f3645d29c15a479c4bd7b8d99489932d251ac432ea528305e62c46a01fba1aa7c1b53f6d4cdb6e08cf3437c2ab1e12ed36c3c999ed1497b749412c19e764305b4b714c9b8cfd1d6acb634b510d300b9f9bb8e806ab25028f2ffe442cb12e60d2b1089f942b4a4f5952509e92d5c3e3d8e76b07"])
bind$unix(0xffffffffffffffff, 0x0, 0x0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000000c0)={0x0, 0x40000000, 0x0, 0x0, &(0x7f0000000c40)={{0x1, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x4}, {[0x0, 0xfffffffffffffff8, 0x100, 0x2, 0x0, 0x5, 0x0, 0xfffffffffffffdfe, 0x9, 0x0, 0x0, 0x1ff, 0x0, 0x2, 0x100000000004, 0x1, 0xfffffffffffffffc, 0xfffffffffffffffd], [0x8, 0x10001, 0x0, 0x6, 0x0, 0xfff, 0x2000000000, 0xfffffffffffffffc, 0x4, 0xa], [0x0, 0x800000004, 0x3, 0x81000000, 0x80, 0x1000000400], [0x1, 0x83b, 0x1760ddd5, 0x4, 0x800000, 0x1], [{0x8005, 0xfffffffc, 0x0, 0x1}, {0xfffc, 0xffffffff, 0x0, 0x2}, {0x0, 0xb, 0x2, 0x8}, {0x4, 0xfffffffe, 0x0, 0xc9b4}, {0x34, 0x1, 0xffffffff, 0x1}, {0x0, 0x91e1, 0x5, 0x4}, {0x4, 0x0, 0x0, 0x4}, {0x800, 0x801, 0x6, 0x4}], {0x0, 0x0, 0x1, 0x8}, {0x0, 0x0, 0x1, 0xfffffffffffff800}}}, 0xff2b})
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
syz_emit_ethernet(0x4e, &(0x7f0000000500)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "6c4c91", 0x18, 0x0, 0x0, @loopback={0x0, 0x2}, @rand_addr='\x00\x00\x00\x00\x00\x00B\x00', {[], @icmpv6=@ndisc_na={0x88, 0x0, 0x0, 0x0, '\x00', @mcast1}}}}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x2b, &(0x7f0000000000), 0x0)
symlinkat(&(0x7f0000000000)='./file0\x00', 0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00')
fchownat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0xffffffffffffffff, 0xffffffffffffffff, 0x2)
mknod(&(0x7f0000000040)='./file0\x00', 0x202a, 0x800)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
close(r0)
r0 = socket(0x800000018, 0x1, 0x0)
listen(r0, 0x0)
r1 = socket(0x2, 0x1, 0x0)
connect$unix(r1, &(0x7f0000000340)=ANY=[@ANYBLOB="83028b35c7"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
shutdown(r0, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x40}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000800)=[{&(0x7f0000000600)="bf3eb2bc9a7430ac8a", 0x9}], 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000008c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000500)={0x0}, 0x10, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000080)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x1, 0x408})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f00000000c0)='oL', 0x2}], 0x1, 0x0)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000400)="5879439deab8a95af03ff86d074e7b2cd969ec284e63a473ec6db978b8470ddd99046bbfee408cab7d4ce1446833dfa26a90e9ad024a323703742c6b19c20a23beb6a58976d1e777aaf49c3bfada5065333d7ed27e9620f2f86048c15812a1e5e6494423", 0x64}], 0x1)
execve(0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffff9c)
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sysctl$machdep(&(0x7f0000000000)={0x7, 0xf}, 0x2, &(0x7f0000000040)="8be32c69", &(0x7f0000000080)=0x4, &(0x7f0000001240), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x54}, {0x60}, {0x6, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f00000002c0)="03e09a90b3911b4299937474390d", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000040)=[{0x6c}, {0x16}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@empty, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @multicast2, @broadcast, @broadcast}}}})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000001680), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f00000016c0)={0xdead4110})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e80000000000000002000000", 0xc)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xb04b, 0x103, "2900000000010000000057000000000000000004"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000180)="b8", 0x1}], 0x1)
write(r2, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200029bf)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f00000010c0)=[{&(0x7f00000020c0)=""/4105, 0x1009}], 0x1)
symlinkat(0x0, 0xffffffffffffffff, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x1f, 0x0, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x1}], 0x0, 0x0)
semop(0x0, &(0x7f0000000080)=[{}, {}, {0x0, 0x7f}, {}, {0x2}], 0x5)
kevent(r0, &(0x7f0000000000), 0x5847, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x2}, {0x4}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000180)={@remote, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "17f37a", 0x0, 0x0, 0x0, @rand_addr="fe83c158edabae23ebfe9514b180b761", @ipv4={'\x00', '\xff\xff', @broadcast}}}}})
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x4, 0x22}, 0x4, &(0x7f0000000c00), 0x0, 0x0, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x6}, 0x4, &(0x7f0000000040), &(0x7f00000000c0), &(0x7f0000000100), 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000000140)="3a8fdf4b0fb8d9b06eaf7696d200ea1c8f2a815c3e65d8ff252b3821822741edb988b99acb8670cdf2f3fc5f915000c0851a60206abf5e3e9e2901bba9fa8c367d68b9adf5bd37ae91617a43c83860ce2e55075d7ae8438a0c6e8d00e1f8e6c39b75f2f4cc74e675a4f41de06dc6d37a36ba5ffaa76b8096626c29c204bf5f8df1", 0x81}, {&(0x7f0000000200)="8691bdd0d943e89274cf5d101c30ccbc23fdad92cb22f116c01307ec37ee2110470d9840d668b8034d22a68007c36ba18f1119d14204000793cb4c7c429e17638e714e6a9325ea3ca9df6b39", 0x4c}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSWINSZ(r0, 0x80087467, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x5})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x26}, {0x3}, {0x4}, {0x100}], 0x4})
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
msync(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0)
setitimer(0x0, &(0x7f0000000000), 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x1, &(0x7f0000000040)=[{0x5c}]})
ioctl$WSKBDIO_GETMAP(r0, 0xc0105715, &(0x7f0000000040)={0x0, &(0x7f0000001500)})
sysctl$hw(&(0x7f0000000080)={0x6, 0x19}, 0x2, &(0x7f00000000c0)="4efb6110", &(0x7f0000000180)=0x4, &(0x7f00000001c0), 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
ioctl$BIOCGRTIMEOUT(0xffffffffffffffff, 0x4010426e, &(0x7f00000001c0))
setsockopt$inet_opts(r0, 0x0, 0x0, &(0x7f0000000000)='j\x00\x00\\\x00\x00\x00\x00', 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000040)="eaef125c00000400", 0x8)
setsockopt$sock_int(r0, 0xffff, 0x80, &(0x7f0000000200)=0x3, 0x4)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
r2 = open$dir(&(0x7f0000000140)='./file0\x00', 0x10000, 0x10a)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f0000000240)={&(0x7f0000000100)='\x13\x13w\xc5\xfc5\xd4\x14T\xd5\xd4\x1d)\xad\x1a`\x00', r2, 0x3})
r3 = open(&(0x7f0000000080)='\x13\x13w\xc5\xfc5\xd4\x14T\xd5\xd4\x1d)\xad\x1a`\x00', 0x10282, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f0000000000)={&(0x7f0000000040)='\x13\x13w\xc5\xfc5\xd4\x14T\xd5\xd4\x1d)\xad\x1a`\x00', r3, 0x7})
mkdirat(r3, &(0x7f0000000040)='./bus\x00', 0x0)
open$dir(&(0x7f00000002c0)='./file0\x00', 0x800, 0x169)
setsockopt$inet_opts(0xffffffffffffff9c, 0x0, 0x0, &(0x7f00000000c0)="00d7ab06", 0x4)
r4 = socket$inet(0x2, 0x4, 0x81)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r5, 0xc110445f, &(0x7f0000000000))
r6 = accept(r5, &(0x7f0000000440)=@un=@file={0x0, ""/264}, &(0x7f0000000280)=0x10a)
bind(r6, &(0x7f0000000680)=ANY=[@ANYRESDEC, @ANYBLOB="1db87f5cb86a69dfb16a24234e9b55fb8c58ef6062a5a286e1253ac97e7b587eb1d8f85fafbb23428dc07b14c1a822e5a7f35f779242006ca073197cb60a3c587c32f2fe699127049a207d3c35f91ba47b761279fa80ca1c249d751342d2bd8eb5560cd601fe3460c9b1b05e97ad48a44651b9460552358ca9de30d2ed26bcd2cc9b9906783a0efc3db699065e4b80695940a25d95a3fc82d96b91d6c2491c2948d1ccf60f6230d9e7f8816bb5708306589d3caa20385fff147f4c4b63ea51ae91a9be7449ff9ef76a0f2b5e00bed5b2a15b5678843d28b1dce08e90d3c6aaaa11c05d6f2503043b2b70d4ab9e30da61ade186f590518b66ce4d6b9692ae24b07a655604faa57cf67387114c1335b519914282f300cdb48b9ec7c7b030fdeb9cda22b1f26a3cf03cf5eb6acfc59085e50113b6bf520d941cc43334d973efd79a3203ae7f769827a84a1ad8c7f4730d018a33ab71821dbc9b78d55afd4335a97ce4c95d8b30b4402e58a36e9a149b75bb3081ef70f32916b47421cc7436152f318fd73bf26d5c7f04", @ANYRESDEC=r4, @ANYRESOCT=r6, @ANYRES16], 0x1)
flock(r4, 0x9)
connect$unix(r5, &(0x7f0000000340)=ANY=[@ANYRESDEC=r4], 0x1)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000040)="7fdd37fa", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82023e2f66"], 0x10)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)=ANY=[@ANYBLOB="14"], 0x18}, 0x0)
utimes(0x0, &(0x7f0000000100)={{}, {0x0, 0xffffffffffffffff}})
sendmsg$unix(0xffffffffffffff9c, &(0x7f00000014c0)={0x0, 0x0, &(0x7f0000001440)=[{&(0x7f0000000040)="2870edeabd783a746c4185925bec23245627155cd354d8bc73a9e3fcbc630235dde5636a1803299faa3c96fefca100ccf68d85056fb42f24176f61727827cc7adbcb98f43f917ab9f3a40ddb967034891163a4fd5e42c5a0f95269dfe092ca1344ab84058732042b9f76a8930521a2104ed7c1d73158af8d6ef8407e01186dec43de9c953ab91a2ed9ccf3cfbd3393316a7cc0041753da020cf58f1b8d350e07a7cc8d94a5fcfe389da93b652684a23989a07b94b705c5e5a455949d00f65cd201fd613a7a15032f2f6a9000bab81e3613c2a79dcd3e4b8093b2d7a4c32feca58437b3734fb8799069a8dbd464b8ff3e38938a4379735f4090c6303b56122b6c9bc6c7e1f272532db03866feede3e98a1247fa57ab74bd0ebbb35476b77cd216908cb9736466e8baba0bc4d563fd3e040cc02ed6e0cdfa82f8e34d29b800fdd43e06e2fa014eb24b76089ededbfa59e7d7421aa58d2f71bc5fe70dcca9c8b9880cdf11b4f74e59290a2e6fd53a726ceba71d2b94a327a53d806b45e61ca47ee49b975e377d3f5cb0f9702dc170c1022332e09ac6538b1c2d275f42f4c081d4f75ad8d0ddedf0cc17ca449f915c1def336457470da14bb647b8e9f6b8bf428320e7b14feca47cc8e96fff3f10710bf62d717266591347c14d490d26e8a38639bd07e1cf3c27abd814f151180018564f1a3d88949cf0f90145cc03b68186ba5077a9879e1da40e5896c5a3436dce8287042249b5bc132a14b10d4984ae4e7a00002e1c5e361592e74a7e81b0405395289373a138ad5ec3bc886c617c0be8474108fc1804d1025620cbc66ab853eaaf26791a3d5cde4ce56c84f4cd749654cf648258c42cd71dc7a4ddfee57968b8cced10b0223ce508d00acdd3ed2b2c383da749d00b874498ccc65079176942881d717472209cceebad64731f5ae2a6e83d3cdeefcbd4da57cd450fd2353ad965f40ae55affd62f1fb391c66933238024273034dee95cce40a57655bd617cfbd96152d2f8a8898045540a36e2b07e4129def73a0b0e07a1132d759b1a3ddde41c36b7a43ac7e6e78f01aee509806da636265df7e6ca1d3998ee319fef0869dd0529f4da17241b562269f3fcec594043642a548801838330d1165a431c9e4a3f666cb113c3d6ba2c7560137bfc5526746f3f3baccb27148866d75325314836e0c4e8867a9c875c7e446296b092f6a50e66161cc23fe21e4f50a0121dbd5acc617a3e5a77c9521cd74bdbd55f90535b31e48be37e9c6a95a14bf18bc5d998c38e297641b409ffe16811934fbe34c741224285f1af7a551b9d1d9df8db7cbd39bea14125132009716209c88aa5f6df01e4a11e7d17c56cb4cd7e79dd9bf5d9085ef29112d8510a7e00f5e5e3f6c1034799a05846ee6956df06c0a20503a70f72ef032c022a1aa6d55a2d2f5b9dfa5d329e65f02d127c0ba4d4d85b197829a812c9c3255ac24e2ed7bf844e36ea88e5c0281f894adb8b6711c0d2ea5263fc2c6ab14f5a58c6fb68dd97779364a4504387edc6a02ab8f59312e1d74e7504cd14563fcbbf5a78355fc30582671d2029e8c9bc9b14602f013ba4008c9cf95b54d5647491ed1b3e0bf59e327a6451206742091adc7a3f89932889316a332554e14193afc70c1e571fd517f464087af82655a3acd66a7f3ecc0ed0e4be315f1529ec1b2bd269fe0cecd73368838a22eca8f0548e8e05c47dd84de18f2aada6888a7047bab08f2f2e390251bd9e8847b08920", 0x4da}], 0x1}, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f0000000500)={0x0}, 0x10, 0x0, &(0x7f0000000540))
r0 = openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
mmap(&(0x7f0000000000/0xb36000)=nil, 0xb36000, 0x0, 0x800000000009031, 0xffffffffffffffff, 0x0)
mmap(&(0x7f0000847000/0x600000)=nil, 0x600000, 0x0, 0x1010, 0xffffffffffffffff, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000501000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed0100c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be608a371a3f8f5030000000000000001000000000000100000000000000000000001000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000700)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bbc454327b6a1522c332ea628b8cb672e9e7247818f970e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c46106e72cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a84001b5b8d93dfcfb77778", 0x182}, {&(0x7f00000003c0)='r', 0x1}], 0x2, 0x0)
mmap(&(0x7f0000000000/0xc000)=nil, 0xc000, 0x1, 0x10, r0, 0x0)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000180)='c\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x7}, {0x28}, {0x6}]})
pwrite(r0, &(0x7f00000000c0)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0, 0xffffffffffffffff})
r1 = socket$unix(0x1, 0x1, 0x0)
r2 = getpid()
fcntl$setown(r1, 0x6, r2)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x64}, {0x1}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7fffffff})
r0 = kqueue()
kevent(r0, 0x0, 0x0, &(0x7f0000000040), 0xff, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xfefffffe, 0x0, "f65c873806575938c72524b586c7c00e1b17d80b"})
kevent(r0, &(0x7f0000000040), 0x36, 0x0, 0x300, 0x0)
sysctl$vfs_nfs(&(0x7f0000000000), 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000008, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x200030000ffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x8, &(0x7f0000000180)={0x3, 0x0, 0x0, 0x100000000})
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{}, {{r0}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r2, 0xc0107002, &(0x7f0000000100)={{}, 0x7, 0x4})
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f00000002c0)={{0x3, 0x1, 0x8}, 0x6, 0x9, 0x9006})
openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107002, 0x0)
openat$klog(0xffffffffffffff9c, 0x0, 0x0, 0x0)
openat$pci(0xffffffffffffff9c, 0x0, 0x0, 0x0)
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107002, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, 0x0)
openat$klog(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
dup(0xffffffffffffffff)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x3, 0x7a2, 0x33c0000000, 0x20008, 0x1000000040, 0x8, 0xfffffffffb, 0x10002, 0x7ffffffc, 0x0, 0xffffffffffffffff, 0x4, 0x40, 0x7, 0x4000000004], [0x10ca71d1, 0x1008, 0x9, 0x0, 0x7, 0x10000000001, 0x10001, 0xa00000099, 0x4, 0x5], [0x0, 0x2000000009, 0x8, 0x102, 0x800000000007, 0x194], [0x1, 0x8000000400002001, 0xffffffff, 0x8, 0x46c, 0x4], [{0x0, 0x0, 0xfffffffc, 0x7}, {}, {0x0, 0x0, 0x2}, {}, {0x8}, {}, {0x6, 0x2, 0xfffffff8, 0x1135cbe8}, {0x7, 0xfa, 0x0, 0x9}], {}, {0x0, 0x0, 0x4}}}})
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "f81d08200b0f05000300000000000000d2cad800"})
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r0, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000780)=ANY=[@ANYBLOB="10"], 0x10}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001a00)={0x0, 0x0, &(0x7f0000001940)=[{&(0x7f0000000940)="f8474def6ac1cfb3a368ff10f9d3e4e6ff3fb3ae79c15161b4ca7a695ed6b4099e59a4239ffad553afb7f917d743eacf4a0ce6e0c7cc0cf8315b36a145495a87e1a71b9adfd73f3c6cb0c357f39e2a9e44be21f4f1cc34c58d3f908db81154bda30b550459fa5bebb80a564a04cf2076cccdbc24a785dace628991d3fd25bf01e123132efb7f4213dd7c20ee1d0d22ccb9b9ce116410dfe732e8ebadc2b2d18607f0d0285ac35167555189fcbf6f1b6703de24adbb63b4b220124c3d1e39290c36c8260a1a0b0fb08ee5ba25e11931c2552568f99b83624837b39850b6a2e79df2a47803b8469418fc6e0c01328e9dda8dfd836e9cdb052ca2dec9e58f6a67c39fa735c1b576cd055243948063d10721ae57965cfef0484dc2646868033e1db5d9fc0eeddb08fe66170a79c439015240d29dbb3cc9f484cb570d01688e2cf9a68cb0600bd3df9b5ac975fa2581def71150fa9dc32f8980b0403c4c2f3f9013f87e590f901cb57bbee10852dd92a1a75b3ce0f870cde02deb8c78174ca7a04c24511f8dcd7128cfadb141bc0d7e8b2019675ac3fc6f8f47d09fa9ec1172d6d8d67cc2720767e1c08b88fd520ee18e83f891c065e6317f72f0f3c451a5a87dcd228a3dd6512f366465cfa6dc50128e3c8a6521f1587eccdf7defd8160efcdc10aec22b17466ee18c287ebac46aaf81cdeba25bf5a0da65b090931cf57d95e6a2b497f51ebf8e79f81a2fe4610fa5c4404bc394bd6c83e3702a4106ea5b718073023ad89abe53fdbc3aa5b1ff547a0ffc808fe7b9c28fef4d8ffdf12a29ce4d409ec5beee8db3a4afa003c2eb9fc06a9d05df8436efad1dcbcc83886976eedc2ef0599ee90885ff3c2419404c460223761238320788516e1730895c71b0b446aa45b5e004986aa234dda38690ef8f9170973037ff559a7d200109e7e20256d91202c182f5a2d4aeccb3816300dfb09d751c949db663d49ba74ecb188b0a1f364b2604e6c5bfe18c6b2afb490f5574c9f49af047627dd60905c7ee6179d976d28f1eae7850ecd7c1dfbdf910c6341b1fa8ca67903c7b574ec9bf116967861c8007d5db728350642ccc05e11d18b4ecb14da593bca2da22432b182c7c218049ced07613e99ed7789293fe10fc2bcc74a242c827a2d23642b646f4ed3d01b6f762adef2507eb710924074afe09f35cb86b28ce9c492d8a69b9d1517fcda87b0a282e3dad1446eb667a231d101808cd47f6fddd48effae8f9ce4cfe8981a8508520d94f0e461fe3914fd42f3a0320abb206877de2701ccd8cee6e8beaa5ee1e1b8938fc489143d23435d7ea76030d64e04eeb63fc5e46242b555f0defce934070c396f476a32dd3194f5a89571df61dacc6928706fc56939f0f78f7a21396b7f6c1ed45b97e14d3a5a5b7b608ab6ef86597ebf6787175bf7c1e917f66e522390bf4f5258cd997a45d17d951bd637749159f7999964059b5cfd61dbfca33c2c132bad3d8e3690dd21187a8fbbf09cc330e8658359805411ca3581083d7270a28a8855004f43ee84017cedec5aeb2a8b137c44d4f4a1ba8c6d7b9b49b67f38688435a20c0c3bb08", 0x463}], 0x1}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000240))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000000)=ANY=[@ANYBLOB="fb1820005408feffff0200407e87059401000304000000200261"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x18, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0, 0xfeb1}, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{}, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x11, 0x4, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f00000000c0)=[{0x81}]})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="18000200", 0x4)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f04)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000480)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ftruncate(r0, 0x0)
minherit(&(0x7f0000ffa000/0x1000)=nil, 0x1000, 0x0)
munlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000180)=""/134, 0x86}, {&(0x7f0000002680)=""/4096, 0xfd82}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x11, r0, 0x0)
getgroups(0x1, &(0x7f0000001640)=[0x0])
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
sysctl$net_inet6_icmp6(&(0x7f0000000100), 0x8, &(0x7f0000000c00)="a20b912e80f35e6f99a409b6bd554626091eb5adb7f200188a03163107091f71eea8259b40cb605ae998b44abcf2e62cc94f1732150c4302300c81f815a72940d22c679c94022bd9dc22086b89e2b3c80ccc817f5bf648d966336c13e9479586d102902f31fbf8fc8e68c4dcf8ce2c828580acc054947791358de836288ee8179466ee537e2739e785a73116c1896a0618d7214c07d79a01e86e06a1265bbc8a499355fda2ab7fbea0d2ca641c86bee4e1ff6a3426cf06617ad9cb007d21eb492ea0b529d9e3b2bf1e5a21007fa398ed6356aa8c0f9a8fb5850c2d9ff388eb4a640bb0c193fcc9e77ec515fd43176ada0613dc3a7ef780b718a0f22869e60ef74e83bba1a97b4d3ea10b80f58e5ce051654e25f4bca09ad97fe905bbc05f494b729da5d35a458cb00724dd82893d8b1cec748b45db2b4aa992ae63e44cf07ccc2f4cdd708d1b8fe86d14aadf3d42c5b444e16c1e5308aa22d3d2dba22b52159e4818235231b81ffc39e172776a4fb3908db6b60ff04959c00a7fa5910428145c0650fd3eba26e815c1dfcb98c73a173bfb5889519f8f2dcf4f7fd8e0b98c92e859fee347b522bf49fc0fe9f49d154c2c64c789b906da15661b8def837eec9592f0c2cd5c40ba7019b9bdd3566768007840d3d2317f1f42b96480eb3639a87f1a3e732838005809af18713ec337ba555c2d06b3baec1bca8d7d23825efcf7d65b8e90b2bfdb18295e0fb0b247e54e39d560fab36210bc86bcee9ef9fe822eb60b83560311a558a5e0363a83052964dec650258032818dcaf33ee572848fc5bfe7ddd5ff397af516017660eedb706876eff1561d81950dc5ff0aa13d8ebebc1d68c0d510ed11b27743c7dc8899f871781fbcfec5da4432d0a9ff4f137a808ac1a138b351f7da51a44259bec96183ea54deac1d83124bfe28182143d8e13ff17bea6762f51e53ca690af257eb61c26c549eb4db7e3e3efdfc267d6822d23c329b1d2ff4f7bab5ffd6a4cfdf6ae852ab6f1a129d8322f0b74ca6a6a1f4fce5508095de2f265159b907c8619597adbfa4ae97f15fc030109a59f21e1f12651ef3459ef0820455656bd96b5f47879ee4bbdbb75af8c4d88ee179a437dab12f68d197677516238d46c27ca4160f9f79a11b1c1a612ee313ab350d2aed64da768d14ae43d201e6142e4b7548b853e2b69cfa9fc5fab9107e31faad2ea2688958c27e5fc573adf8c7fce24b660b8b97cceb9f0d82b6e5cacd8eab8bf72dcc9849f942bdebbc62d12020d1e2d8cddcd29cb80ba6ad16c3f92d2225fa36f1bda3165100b7832b898d102ddbe6962dcc22162304a5bfcbd4804927fded3cdccefa16b4d80b9db6d72eaff75df93508f0df8d21efc329d4e503e8040ec0e12375716e106707756a10dc3e48d9cc0e857239bb36fc8b7bee6880c5e0868856483a755039528c0895", &(0x7f0000000140)=0x401, 0x0, 0x0)
open$dir(&(0x7f0000000100)='./file0\x00', 0x200, 0x78)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
faccessat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
syz_emit_ethernet(0x545, &(0x7f0000000340)={@broadcast, @random="0a64aa29e168", [], {@ipv6={0x86dd, {0x0, 0x6, "fabb50", 0x50f, 0x0, 0x0, @rand_addr="3ab90000df510000001d2331c7be0819", @mcast2, {[], @icmpv6=@ni={0x8b, 0x0, 0x0, 0x0, 0x0, 0x0, "5ad042aa633ac95bead3abeef7d34be092e860d3b5f94c9134752268781e25aeafee5fa7314d17aecb68feb5734fced0f3a1b6b14f65c452be2789b4ce8042e8468d9d0e15aed6dd1bbfb1fbc2a2c2d02c2ad9145c67a2976545ef176c36842da26e360731a01e9563aaaeb2fcd527aec3575fa1d8a76d4b9ae2f7a156f4f46a4b01618e2bad672bfaa8da8b845958acb6f48eee80c3b461840063c018cdb9fed3b63905cca42ff8aab6d436fc98c3d71c2c1c278d11010396b8b928687c7a9e12bff46349c49f5851543e27d8f82eeeba67a92fd5fd85b38e04121427e24df78aeaa7b309b6d4244beaf103683ba67388cefc1717b1393821ff5020f29cb8be74530c8ee3e357db2a89fb06d053e8ffe683d66da84a46927816df2a90cfae51eb62b248502a2118e96f805a8d6f46196cc066a4b4810cb1ca4edf1e69c330d3e4106b048b93aafc6e4c729c7f4a6798c085bdad5e5810f25237b06d4c07e3cd65a17c7978185a98273ac3759985d73ce5dbea59cd1f0e18a2d552a4b0f3da940357ef8ac961d537ef09c9897a3a80cb1fc46f3b732e728caad6705261cac448da9c9a66b66464d34da95f91874b21b3c3035c634f65054486fd38e736b9a77431dd67bd2978886bf41f3a8a09472e47fe807d9ea5fd17f5bbbffd6693018f86cd38dbb8b3cee91bbadc8a5e7bd6afda3455d413fea8ce73abdacc6425ecfc34a6c82094223f45f1cb6ecd02fd7004eb5f81fedb32dcba7dc4639bcfe060bcf44646e1790fdbc09ed5b98ad178b166dbd1e1cc670b551939ea1090c9b840b5d01ae0e5acc6f0f14e2b7763fddf2dc7e56e7c3fcdce4ee2b5424157b9fc8e0cc268033021e728b4fac6fe25a84097e775b6c6743272c5a98f0e49a23b93bf34862b44821ca7e4cdda81a08ac6f37ec4ce36ba083eef8eb4935e78be970abc22c659bcd285ea4a0b8d4b5cd233f4239af073b4b0f6ce59fcca183cc0ccc80c78c422fa0a8e046dfb69ef34af34bc5d686bcb907e3f6596c3234debc1917ffeb17a48600fa94c0d29119e954b81bfebad457b5b265c7c672f6d1b1d9da32b34d483906d5aa0b82ea078297f11c9fce8c41960c3efaea62442dba98496357e770f17aa53e448a0d1340cd1028a74c80e4f765cef76b776948e4ea3e8cdb2b4c5a8f1c57995d61de07a6423dc14f9055fb43e9f16f8fc5d0029124e7b97559a97eb3c397a0b22b025290d3e3e48280ce58df3453f71d96b2eba2c11a628b577eee8d3d8690ec45c94dd92fe8ea430abd9d9a04691e8379db496982860bff3892451a8dfa1be94d29a8b23e0d0153de17155e34c176a31defa3d5ec5fcc91e3bd7f10091ef2e290f28f12304dbb25a519f495d9f0d53d8ae12869d3e42d8602897f26bc2bb1b48a13aceb22d17753bccffcad03fb89b82d7be99d4e80fe7ac4c833488e216c2ddc1e13a7d167baf25ee4de79887edd1a905bd73277661674783a35df5f6c78118262e8f36fae40fa07cadbe0d9828f28a46b7c7efccce681d7557951caf38a829855fb0482b2d47498964a0dd46998c947feb53253ff04039e502935a06c0aa1a3ba3f1f4801c7d95c69f824bbd38563efe6ba2ee70da0d2888c8d9d43a26b880b42ccc90e69c78a167decabab5a4d01a1a75cdc8a38f585c7161aaf3812c3eaf278f02c10f8b5e9e21500ac0251e6e8ce2b54fcd64388a12ccee34bee77d38525993f5035ceaa409b6234ca7c70622a1eeb9b8ff6da716451a852674a22229fabd7cec073ec808ccdeec652825caf90ad756ce2c2bebaf80f7efc6"}}}}}})
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1021, &(0x7f0000000040), 0x3)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
fcntl$setown(r0, 0x6, 0xffffffffffffffff)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x80206931, &(0x7f0000000300))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000200)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind$unix(r0, &(0x7f0000000080)=ANY=[@ANYBLOB="9401f4"], 0x3)
getpeername$unix(r1, 0x0, &(0x7f0000000040))
openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x74}, {0x60}, {0x6, 0x0, 0x0, 0xdfffffcf}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0x3a, 0x0)
r0 = socket(0x1, 0x5, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x14, &(0x7f0000000040), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cff"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r0, &(0x7f0000001740)=[{0x0}], 0x1)
r0 = kqueue()
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff}, {}, {{r1}}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x899, &(0x7f0000000400), 0x80, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "104ce467af60b403184700dd47c8e9d209670991"})
kevent(r0, &(0x7f0000000040), 0xffff, &(0x7f0000000140), 0x7fffffff, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{}, 0xfffffffffffffff9, 0x29}, {{}, 0xfffffffffffffff9}], 0x0, 0x0)
open(&(0x7f0000000180)='./file0/file0/fi/file0\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{}, {}, {}, {}, {}, {{}, 0xfffffffffffffffd}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000140), 0x100, &(0x7f0000000300), 0x1ae, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f00000000c0)='r\x00')
openat(r0, &(0x7f0000000000)='./file1\x00', 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x5300)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x101}], 0x1, 0x0)
r1 = syz_open_pts()
poll(&(0x7f0000000040)=[{r1}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000000)=[{0x5}, {0x84}, {0x6, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
sysctl$vm(&(0x7f0000000180)={0x2, 0x7}, 0x2, &(0x7f00000001c0)="ac52db62", &(0x7f0000000280)=0x4, &(0x7f00000002c0)="b09cd41d", 0x4)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x40}], 0x1, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffffd, {[0x4, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x3, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0x1], [0x0, 0x82fc, 0x7, 0x0, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0x3503f89f, 0xa6f, 0x2, 0x80000, 0x5314, 0x5, 0x4], [0xffffffffdfffffff, 0x4, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfbfffffd, 0xfdfffffd, 0x20}, {0x3, 0x4d4, 0x9, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0x90, 0x0, 0x80000001}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x2004, {0xffffffffffffbdd1, 0x40}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x80000001, 0x3, 0x6, 0x0, 0x0, 0x7fffffff, 0x4], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0x3, 0x0, 0x80000, 0x4, 0x4], [0x0, 0x0, 0x0, 0x4, 0x5, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1, 0x7}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x6, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000140)=0x1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000513", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x1e, 0x2}, 0x4, &(0x7f0000000140), 0x0, &(0x7f0000000200), 0x0)
r0 = socket(0x2, 0x1, 0x0)
getsockopt(r0, 0x6, 0x8, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
getpeername(r2, 0x0, &(0x7f0000000280))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
r1 = socket(0x2, 0xc003, 0x0)
connect$unix(r1, &(0x7f00000000c0)=ANY=[@ANYBLOB="9a02"], 0x10)
r2 = dup2(r1, r1)
setsockopt(r2, 0x0, 0x2, &(0x7f0000000000)="62c5ef11", 0x4)
dup2(r2, r0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000)=0x43cbc, 0x4)
r3 = dup(r0)
sendto$inet6(r3, &(0x7f0000000040), 0x43000, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000000)={0xfffffffe, 0xffffffc0})
openat$vmm(0xffffffffffffff9c, &(0x7f0000000540), 0x200, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128", 0x49}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
writev(r0, &(0x7f0000000040)=[{0x0}], 0x1)
r0 = socket(0x800000018, 0x1, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0x0, 0x0, 0x0, 0x100000000000000}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffffa, 0x3, 0x0, 0x800, "0000dc2f35910000000000000000ebff00"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={&(0x7f00000011c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=[{0x0, 0x0, 0x0, "b766f7f3d4e4fda61495b6d035aa575b"}, {0x0, 0x0, 0x0, "54dcda0bdca707baaa88d4cb9d17c9a71590ce33691522decacdb21b648d3bb627588a77d11f9346ffa45defbcf9a5126e376b43dab8452b984afdc654d418440a4128e1ab650724c6d19c77c285c722f84a42e361cdd60483b49100c17ac0788870e21cfe357ffb5c4e3bdd292bc92e2f4835fb502fd4faa36171935cb135fd43f0c0e3601a8bfd82fbdf7f2c5a78c6fbb1ce12c5316fdc4912e900d0e0d3ee3ce44342f5cacc7c333c54f67fafb3efd0e9b0d0"}]}, 0x81}, 0x1, 0xe)
r0 = socket$inet6(0x18, 0x2, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x2, 0x0)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0x2, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="100000002900000032"], 0x7c}, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000090000080007", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210]}, 0xfffffdfa)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000000)=[{0x1, 0x0, 0x0, 0x5}, {0x3c}, {0x1c}, {0x2006}]})
syz_emit_ethernet(0xe, &(0x7f00000001c0)={@remote, @remote})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [{0x0, 0x0, 0x0, 0x7}, {}, {0x0, 0x0, 0x4}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket(0x2, 0x3, 0x0)
connect$unix(r0, &(0x7f0000000140)=ANY=[@ANYBLOB="6c023f2fac1400ff"], 0x10)
writev(r0, &(0x7f0000001440)=[{0x0}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x80}, {0x44}, {0x6, 0x0, 0x0, 0xffffffc1}]})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0x3a, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x25}, {0x61}, {0x6, 0x0, 0x0, 0x7fffffff}]})
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
setrlimit(0x0, &(0x7f0000000000)={0x8000000000000001, 0xc600000000000000})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSFILDROP(r0, 0x80044279, &(0x7f0000000080)=0x2)
syz_emit_ethernet(0x4e, &(0x7f0000000300)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "cfe624", 0x18, 0x0, 0x0, @mcast2, @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@mld={0x0, 0x0, 0x0, 0x0, 0x0, @mcast2}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x61}, {0x3}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@empty, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @multicast2}, @udp={{0x0, 0x2, 0x8}}}}}})
pipe2(&(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x0)
r1 = getpgid(0x0)
fcntl$setown(r0, 0x6, r1)
ioctl$FIONBIO(r0, 0x80047476, &(0x7f0000000080))
getitimer(0x2, &(0x7f0000000180))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x20, &(0x7f0000000040), 0x4)
sysctl$hw(&(0x7f00000006c0)={0x6, 0x18}, 0x2, &(0x7f0000000700), 0x0, &(0x7f00000007c0), 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0187009, &(0x7f0000000380)={{}, 0x0, 0x5000000})
r0 = socket(0x18, 0x2, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x1})
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000080)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="14000000290000002a"], 0x38}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2d}, {0x15}, {0x6, 0x0, 0x0, 0xff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1}, {0x20}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@remote, @random="c182c3cc9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xa, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x15, &(0x7f0000000180)="03000000", 0x4)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r1, &(0x7f00000014c0)=[{&(0x7f0000000080)="d74c78007f", 0x5}], 0x1, 0x0)
pwritev(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
r2 = dup2(r0, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0/file0\x00'}, 0x10)
shutdown(r2, 0x1)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000080)='./file0\x00', r0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r2)
r3 = getuid()
setuid(r3)
r4 = getuid()
chown(&(0x7f0000000200)='./file0\x00', r4, r2)
r0 = socket$unix(0x1, 0x5, 0x0)
connect$unix(r0, &(0x7f0000000000)=@abs={0xa56dedb3c2ef0120, 0x0, 0x0}, 0xe0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x48}, {0x81}, {0x6, 0x0, 0x0, 0xff}]})
pwrite(r0, &(0x7f0000000200)="dc0ce298b55707a77d16f8358c8a", 0xe, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
dup2(0xffffffffffffffff, 0xffffffffffffffff)
writev(0xffffffffffffffff, &(0x7f0000000400)=[{&(0x7f0000000100)="237da1aaa868f7624c95521ef94d262ae2dd1ef3d534ecd0036366dbabc6f73fe1e738190efcc9f9a8c04158620eb02ca3d104bbeb2ce6ff41470f584b0994879c5a1698161dc4f0504416f30f", 0x4d}, {&(0x7f00000001c0)="1279d2b66391c265b8f37db1fad641a553dc6a61b95561a14288a5d640ac", 0x1e}, {&(0x7f00000002c0)="48945deaa1b07a32d58bcb96faa7f89d97a3", 0x12}, {&(0x7f0000000300)="c17442ed66da88ba6fdb3d98cc34f44e0db87fd7015428deafec78baab445c5d01616b53a4a64cd05239c243a3b27827116d3193381f0fdc15baca3eb19068028ee82b9b3f745ec8878b277c9c523bbbc1cbdc7678a687fbe227c6d2a27820d1cb5933ab818a83798c9fdf669c25ed1d83136e66828f1d67e03d5324d4293510d983abde98710efe1aab08f8e841051ae83f230f45b641bf3f1aae9367c858ba56d7feb7df0533d02c0d6dfb79e693d4bb3f5ed9e4e9ba3c23455feb0d44651cbbf68513b041540b8c9e7c898d465080af8ed656e73ac6ad67b9f455b8286d89bab80b0536b5ce94a264c0995b45bcf98bd1c7836550fd", 0xf7}], 0x4)
bind(r0, &(0x7f0000000000)=ANY=[@ANYBLOB='x\x00'], 0x10)
r1 = dup(r0)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x2b, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
accept(r2, &(0x7f0000000080)=@in6, &(0x7f00000000c0)=0xc)
listen(r1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
munlock(&(0x7f0000fff000/0x1000)=nil, 0x1000)
sendto$unix(r3, &(0x7f0000000180), 0x46, 0x5, 0x0, 0x0)
close(r3)
accept$unix(r0, &(0x7f0000000280), &(0x7f0000000040)=0x1002)
write(r3, &(0x7f00000012c0)="d542853c7d630a991ff095ac454db8e699fd30f7e6f5552158c05a8fb9ea7945d1451109fba4992329afb3f6744fdcf3a52e84bfefd31bc9f99824f208c348da3454b814b98549ec57518fe8fddc2e9129f8ba995046144544a75ada3c7c40c15d6c95163bf35a24a1b68a0d99acbe700126eb0569e07c070712918426a6265147ec14cdb2c5c04dd0e249ac5902038a2b0881006f662fdf1c23f299eb1ea3d8190f398a572e3e14dba99bef4bd6ceb8f46c8fd81137155defe1ce48c654a2aab027565b1eb568656b810428d24d81fc82943aa87641fbe57e8278f06a8d4739ca38571e08bec98bc6870d4f4bf68705db1ae0beef1b5c9c0a0810ec7800855aabfe3527c92174a55a9c2246e80f2175f46c9cc4578e29cee26f8be639057c25952638413441f352683ffe0d986b2cd90869066b9fd7912572b8b6fc3ff245aaa4b47bfb897cc7a6312da6ddee9fbafa9edabfc3cefe919800fea64211768c7263613febe434c5c1a8c5af18415776085a2573ceb30f1de5cd18b079a71a927d459f840f0fa50e1eb8b77d56bd29fa24a108a05c47403dedf802a2e501f1e14790dc7ee90a55cbdcbd26fecf35fab07b5ce7fe5432eb07cfd6a73bf0642c3d83c07f6b718de42a705cda72940f3772e753f741b47121ed17ba850b630a2a4052389d1e6efb03704f124e24ac863f8fda22837cd2a1ca357096f836ee20ad2c7017a0a746ce5879d8e60a6679b428b508e507145782230d00019dc8c9d566759aaefb76b854af35cc6efd37e884ac1f4f4c72b5964ad9aa1c0f4114a2838dc7d6e019d62a523d668f8eb012bc3b307eea057d6c1ceffb24ed6c4806e4ed0185903d24163c26d898b18895aaec90c95d2570935825e039551d8202546903bb9c484d6b2f6e724d324794810e25c9830e37acca5e74e32a6f08b7ddaa861a06042f795a1c5ae1902d953f88eaa0c12f820b404b75cf2f1e9b108417013a4c75bb0ee7f1fd3a478c10a88660b5d49c239c3ea9ab85d4510aec3283ee5e993b3689b9d367c5f479326ebef9043264650eb56c5bba9d8dd52ae28d7304e810ca2a17a8ea5b0ed400dfde111931e781c4ef6620dda778b97ee3b669bab5532dce56d365303c63320ceb89798106c69a165762f82fe8a3ecfa5094b34ee6eda176cce5e987a391bd54146470a41c915f980a92a3f1c1d8c646aed971989f9d8bbdc18b29e83ba5353d63a07f898f8eeff40f767afcd9e96dcb50a77a21147be4f20deff24ca9d58d67d38cbe5d800fecd42f95a4b254d7cf460472a4ff4b7d4e180e2eb3ec27c6e7f0aad1a0365dba9825d501a309381deb5973282d93e5cbfb174ea2e66c80375904f4e043d533304291c37d15335bbfb1496571ddc136da5cef2fa0e2dd2eba947a6028a4e900451cc3365a2ac274c40c34b62067c09cea335647564549daa3a7e3a1069fb472d41d0a7aa6738fe8b040bf08afb4b855214cad017226a04918069da85412442c496265403e265d71f0d553fa0244fe8d69d1c457a99b954d579fef6aa0a702739374e006a6086d047632878a84fa68da0bb20dc57baa439807a71fc086c07cfe1951b372ca7031fc9227594a64a21c5158645569a9247672923d1d011d167858fc0090115c3b65013f1f6612f09b007123927a9c0676be897f290cac9301551108b97f96f933305843b5e315973de83d42a799aa653ec7207544c4f225b1c33dd25bc574741379c5a5189359126246299c33fe2d0bcb8162c932fe7cac065fcf5ca3e0fd674cac3ba39dbfe968df8293865f22c765d484decfa59c624b5ff0aa3dab62d8539691beaad2c67eb18ab6b5ac2092c6888e648d23f8cdcfb135156e2abe6f4a3f1cd2261e6a1089dbd701a3c68599eefa128e22656a07c2b8fc4830f2cfdf7ac3055532db2dc9d2f814abeb845e9ef8af91647cd87612669b9e2c505bcfa173e65951bc51858ad5294175cf2be7a886d8faac942f9f12e2c4e5a9e2eb6ebbb861d80d195e8c8eb2b56cfd65fa73cc75fbe3dff7bf9434cb8294b891d0bb9d0d40f737974fc5d7e61e2ca1f29cc67a0ac0e16ed58e8118849421cc772c37b610256118b18e40956b6d46c53f22c468b567c0bc4f530dec20176fe35616716394d2bd1f58320f367121ac51dc1d74b20fb053747223944890899e3d3c2cab35236a6e0da2ca2919c58fdf45bba654fa05bebf16c918aec7a7c286bee7f83ee310dbbce9243f6d5d3ead726cefcb19ed0f2c1d4a8dbb98ca392ec4d846bf89bbf418bd87522e9a20272a79993263481635c00cdff8d9bfced071ac867f088ed9446b3f7914bd977fc21b54f34e9c09d1149a87a26ac2ef2edfe76e6ebf329128e6ac356c9d69839a1526c37313b4b0c56553f62665b94ef72e59b7df90d9da79737e69e75529461eb8c6eb6ec344dd8119b784903f3e6e855ea79b2aba444f39447af1de01fde7cc7c27b17324db81a060359bd4aaeeab42b282e59ec23fb2d5c4f0ccc1fc6eba1fdcbb17e006e8910e6a0a90469f813ebcca7376be5a959d0bab42e0acb82706a9dfce72db80e4c6569b62328e82e68e1b3e77531d17166cc86e193885bce1ac08d90a08b05b1af76befdb5d1fb2563de90a440d42c992ab16545cd858ee2b7b823218b0585ea6e15020c89d9c56f8cbfc6c871a24d6dec62169cc51cb8acba03d7f5b286133a09b5562d2d0cc9d0c8a6730f730c31d0c0c5311306cadc0d4d37809b6e9113445cc7f23f83b57d107714552116ef366e2932a896aa82801928220ef4304a263ef77d22804e2e5bfcc3cadba56f33cca92e65168720941e1c4c843af9f1d6a56a2ecd6113e27850da10fc99062d0945f93973a023942b533377d5ef71e7f4a98350c28d1a8cfc38190a285a27173a9b1c2869e4e62412796645925dbf614695b2010a7cb8cb1b63a39408b3e019be907b79bd67bfc7ae31dc37bfa88853d923fdc7e212f2ed587d8cacdcc1f48ed5ec7cc7e8b3eacfbb551c28dae0bb796ea603d44bbc6a8d14b7b82730c0cd056ed850adb05bb8fd53f668011918c4bcdd5ca2ad8cce029e31be43892470006190f5a7183ac311ebbf40b0a6ac815a2641f8e36fa1b7f2e7bfa69599e0ca7ce8f2fd0be00c098e07f8d856c28986fcbaaa157aef9c8ea954aa102c06152929cc984abf2c8bc3e677ef7a3574bfec80f4a41d9d363faebef157403d0f3a82b1d0ddd9de7cad28346abc5ef903b24ef53d6e3481058fc8df960a86e822facb7aefe102473af7fa63b8f25edc7af5b4c8ddf37ed8f44a056d2f2806505eab7cd7621cfadab595422bc6bb3b32105bcb2ef75bfc58958fc0b17d125d2c8052e7c6d2300d2f1b24312fcf0d2f692e9aa883729f44758dbe895cff39b37907f58218175f180fd49f0fda6548972822b53da7eacd7d24b617668ee1e5825f59c4f08df68e5eb37f346e518aea0fc90406c5108b6752a51dc72888e3e6e4b49f3750d22e52db23a4c4f7858876926e4dbd1255826edd5b20e2188357cd686516995208af0817f1ecf63953c61d46da2fee1adbbfdfc2547472a8e0e341b042dbf7cf6f8d2674ee526752b589dd548d749fc5f142f42226967edcbbe9e7dcd60d68e365e473cc6db3be2673622613bf0a31eefbc5161ff9b46d78489214ff129756ab23fc9a9cad4e0ac135f13d4452e489957eae1787199816b934a427954c3eb961f8383d4535cc35a354ecc045c4f26fc4cfcb8bc35de1983ebe1bf716299188b1bfce1dbb6cef7294bd7f68a0090b65a4f36ebd01190a4147daf5d8fc3b20d6a557568b48e6e21dd73e2d13b1ba1ef24d44572a7c16a582274bba0876aca5d60b43e677e42a8dc1b1f84448f31d9f8fe2da40c456bf7f513f849a302eb99f0e8a27c46abf8b0b8cab15e74e25697e2f5e0ed5fbaab57ebaf8d7857450c00eaffe7c7cb576da8f378494a0340256ff6d41f5a18609e4901a6a3a752062fe7e3f1caee44ff002229ffcd4b4c24e9f5f9465ae1ff9febc0a42678b1d824229c4335bbb941fd54c758843f0280c4a73d273d79fc80a37d2ececb9c28d317a438d674b765c77e603c6c04b13e0f310990e7dbe9f566cbe5ac59515495f63a878bfcdeab4a0cdc30e55ebfbf7b07ac3912f56419fb33cf6afa6e4dfc3e585c8fea4883d07106987b6be5c7d0e86210b0c86fc059ca352d3ee66a2df44407a40421cf6c92f846449973ae6a1e658d971a5374e598de33afd71d9172ea131f7b20b0311a80a2968c42b4e8f3ba4cd2168517edcb664e5318f7592720b8cfb69ad7f2e7ebd549d34bf29f9dd0c83c1854d54d6c3b079e3cf5a94a1b7c0787b94e8919e504f60488cb9aeb9879fe2a1079ea0ab80849f1db19b20f44b2344a22d275528a1ad04a627ebebb233963d98645c7217ef1f1b8b566449cf2e793731e85f166b91d354c64cf88a3a3f92439fe1cefee353589e6a7e9e8c72938c24fbbdc92e01e1e3a31322da7208ec08776f7322b2aaaeb9bfcac0c7877bde249e0caf9d6ae7ca7a4e3a5fda34ba682ad10129e8bd7849f6eb1e97732fcd678111361b7d39c5e8f43dd9f88890241a07bc04964f42b1e1c684bb57b22b2f79a4cded6f03132df90e179b9e97dce47dd49e09d1f0cd60b80516b73a81de7c865a217db95c993814240cf03e18618369b028a0bd13cc717bc3147f194b64ac2128c69c3821f33ad217a6f1c7f9e85653b004b17eb27821812a80349f3c390fe90c3a12dfc13dbf35a84909dd3ca6ad9f510a92d786a58486b0830050ed6a96ab0b1bb1e9246b12065b9bc10230f38153c3196b344f4e73ad8784ead0318894946a077402d04d2dfff23cde6a70c12c6f7840f73a5d28c2778ff39d01ded4b4606725f9b1acf6e6065a4dcf900d4d0fcfce5c9a47136feebf6a42486222c711743aa8128b7d7624c3b04353049b49f0188b721446b47bb774885aaa98434e353953672fbd0eff009c9667527e6618072b615548b10022697f88b144824e4ec1b431ba23d2c0a545adbf15704ee1302ae994d05278c5e3853db0d76fc41c8f1462ae00a2e0ff30b33aab5778b8a0864b5e1781a00ded6941845ffad1ef39c30f74f4e20c6389b599c99b43f908ca8ab83b9dd96b7b21400a65aa9d8a9942df18b92c5c643056b52431db4db7cf7ad747a33ecca0ff4e0b2ed2803f6b2ba6e2ab41cc4ebe53d5dd9eb31dd001a48eeb1034770b1f02b38bcdb29f1ffe698b651c5fb42a6e2e9f85da6f8c261b88fd81184aadff8a5633c4d60511e5a433e9dc6b78f6f034f96d8f01c214309f30fc5cf6b3b7ca89d17a50c98d522ac1f1910ded7c4d873fed74c65d9b27ab7d750a5c3a3da0c2da426fb9adba5c61281a05d1e63bb97e04d62be7fce3cdb3012ec6f8b6ec8542e41c9048ef4c88ddbcf36ebea7bc0acafbcfb29bdc897b8acff86430a91c1eb2a6e233b11279cf56bf3ae3d00fe02bf4f0a3210b624f9bfb9969cc1e30e4130eb3fd30fa6d9ee0428e7ad73711026d0b82c80459643e729acb92fad638d8d1dd19880cc830e7465a57032416f87069db04d2e278ed467982075513fd465b467dbd60cc076eb943526f4ddd8ed41c6e99eae03062b1760b98ed3b1039047d5a56f1f4c4140c8ae28c7708e3843c23665fe8beb155cb2190e2f7d5276b202e9262347437d12c99470f00c0c990f160cb2bdd6a1ea840cbca208532c2cd7d55bd34c18d06373ded9eb4bd4c59cecc9163aa84873955bff8081c0135671591a48853b528460aa75726162533e5e87ce19497ce5", 0x1000)
poll(&(0x7f0000000200)=[{r1, 0x8}, {r3, 0x100}, {0xffffffffffffffff, 0x1}, {r3, 0x80}, {0xffffffffffffffff, 0x2}, {0xffffffffffffffff, 0x100}, {r3, 0x4}, {r1, 0x3}, {r1, 0x4}], 0x9, 0x6b46)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x1a, &(0x7f0000000040), 0x4)
close(r0)
r2 = socket(0x18, 0x2, 0x0)
close(r2)
dup(r1)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x3}, {0x81}, {0x6, 0x0, 0x0, 0x80000}]})
pwrite(r0, &(0x7f0000002800)="161aabc3962cc0bb6429d03f3851", 0xe, 0x0)
sysctl$hw(&(0x7f0000000000)={0x2, 0x8}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x80}, {0x48}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@random="09e1dfb8002d", @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}}, @icmp=@mask_reply}}}})
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0x2, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="380000002900000033000000010000005d04", @ANYBLOB="88"], 0x7c}, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f0000000340)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffc000/0x1000)=nil, 0x1000}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff8000/0x4000)=nil, 0x100000000}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000fec000/0x14000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ff0000/0x1000)=nil}, {&(0x7f0000fef000/0x2000)=nil, &(0x7f0000ffa000/0x1000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff6000/0x9000)=nil}, {&(0x7f0000fed000/0x2000)=nil, &(0x7f0000ff4000/0x3000)=nil}, {&(0x7f0000fee000/0x2000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000fef000/0x2000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000ff2000/0x2000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ff2000/0x1000)=nil, &(0x7f0000fec000/0x2000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000fee000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000fef000/0x3000)=nil, &(0x7f0000ff0000/0x10000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
r0 = msgget$private(0x0, 0x0)
msgrcv(r0, 0x0, 0x46, 0x0, 0x0)
r1 = msgget$private(0x0, 0x0)
msgrcv(r1, 0x0, 0x46, 0x0, 0x0)
msgrcv(r1, 0x0, 0x0, 0x0, 0x0)
msgctl$IPC_RMID(r1, 0x0)
msgrcv(r1, 0x0, 0x0, 0x0, 0x0)
msgctl$IPC_RMID(r0, 0x0)
msgsnd(r0, &(0x7f0000000300)=ANY=[@ANYBLOB="0300000000000000e1989809a54ef462a507a5ada45425327bacbc9d82bbc1992e6a53a58b0a0d4417927dbe57a53b2e5605ceec2795aee1a68888e9be572dede66dc8ba03d37f358ead08722526311216812cb236b7cd9d5c2b1ab9afae63405b4cfd987f6bd9b518e496ad4262bf9ca83e54ef76d154142ae3868fc2f513800fc792615c5f9a088139e7de26f7bcdf39751e5e50e233ff217e58963b52335b85def58fc1346cf2dfc43ed0c6ec5686d6cdd04586b3c41e5477fd5ab7ea46ea8d898ff4c1161d280e041426af6b4b5149c9e9be39dc286cc8e79a5cb111db8584fa0c9f14129db1960eb25677af0d569d4f0d17edf009bcbdd601424c5a21c24e31e0a80ac1c2f5988f94c9e5186b7f61a20ff5161310a5eecf0ad88a8ec61d84718bcb39217341653c54aa4da98379e139972f4b95c9dbc2b223c585d11bee817d1638f23826298e97e708b4c8e48d09c4db3052424f30baafb00fc427906316c0ca0fbe4c41acc68085ea28530f72ca01"], 0xf7, 0x0)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = dup(r2)
socket(0x10, 0x3, 0x7f)
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{0x7}, {0x2, 0x0, 0x0, 0x80000002}]})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000040)={0x2, &(0x7f00000001c0)=[{0x9, 0x8, 0xe, 0x3}, {0x1000, 0xfa, 0x81}]})
r0 = socket(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
dup2(r0, r1)
sendmsg$unix(r1, &(0x7f00000001c0)={&(0x7f0000000040)=@abs={0x1800}, 0x1c, 0x0}, 0x0)
r0 = socket(0x11, 0x3, 0x0)
writev(r0, &(0x7f0000001100)=[{&(0x7f0000000080)="0110e5d3af1527ff7141f4e9e37ede040e01f5f1e527e666bdc2150a3c2820bcb376b35d690476c88ee7156301d46427b0903489704fb740bd7142766ff4e888c1c24c8b0df37fa0453724", 0x4b}, {&(0x7f0000000000)="a971e0326e8616e4e933552bd5b32685115710989a7db52742f7f1c58042df55da4e2763122b5f84cbacde36be35a15fd3b7", 0x32}, {&(0x7f0000000100)="5bfd792f3d53c1702b963d124f91e3ec95bd1ceb8008c8bb5ab8096632f9a42f2282c4d73aece8ed57d5a7cab62eada3a3df1a4695e131f25c26762b2a755460081609d91ba71705202ed3d1589edfcd6c5efce951cc7143f84e92a97167cfd724daaf27d966c9336bcf20784e389528b1148872e2bd82af77039f88b352468117debd23902a3d5813cd6acfed33bb5dfe3033d113cc5401cd62cd83a757e5c04d85681a388369190f19f9e51ba0ef06c49cb6808c2fb34e8cbfcc6dee4e0854deaec2c7bf1ec91bfbf385eda3da8c2865572c39c4fe408793af3568d49fc6a6d7100f7e6882737ac36f6d142aa62b0bd872f8fed56fa35e6d442d98dcfb3ade2221884c2b2d3a58f3523dc4fbfe18f87fc8bcceaa3a55a7c57b8a4e3d4d584a1c9405565fd15fc4472c4600d14e366c5b49d1c977e84bbb34a7ec042031c8338317306fb056a90258ff80db66245319c7834a419746f0b01e73bec7943d41d67f79e13b0296bcb54aeee0bab46edf345f62e5af678ffc80cd83ebfd7c3a6f1db5d00aa948af736e14aa68b76f1775792c7ae9a7feddf8b103bb77b6714ddcb3e6d8e8e15939d9cb41964599c5921851c6ccfc1654bed24438514b87774ac9f17fd87e507129c37b7372c333cc4db9b8429341f1bd120d1724c041d0a30bbf60d77a7e2cab06f22a6a118e3a77d594b88bfef6753c2a987db6eb9029c7a15a8974dce8c08dbd84076042a854486d30c34e72d8c8099c6fb1c7e83ac586bd4aa40573475c14e15f6ca5f824fc2f6fbbde61017d39ac8ba612eacc3d9e116ae9b322828cc9bb3d698d9a89823b2b8444f39207e3dc4ee614dfa4b8d2375c4664ae1740b3879459b71b465c850e59782ad7939b656144cb51166e545412b4599d01b7233a209c4f7d5355754ea12b2add13fdd0169f8b7aa1cf958ad5932dc4777e8f3654054f16e218dfd68dcf54501a4911e3c139adca7cd119d33fb60094693d2c76f4d57d1df23e1359ca9abf0c737b05074a610ef4d93b7f13fa778513ced19600b6d11cfb6b20e48e8f2e035e69be910b4c91f80007c08d379535915d96e13df4192ff8a544a897b0d1acc9b7a71e252b5d75f0400c11c62cb097a350c10f72103a98e7d2b86aa44dd5de9f12e6b0fb31ad0c5a5569f31cdb60f89bf61692ef7080e6bc1f056a8257e167453d75c2251b8161385e9bd7d2c7215f36656267151b530f0a883f0831e17ccd35743ee477d30f5dc1e9017d96d1abc35bb1c2466b98f2c433a3909843b4542661eac5f84a89edb208e70bb1fb41796ed3f45d602b2d40c6f5347cface15663cb880dd3e02d977ec4b3fc6018c9e9e220d95269ac2f21fc4d06a80c7bff98ef425e0c9c99f11265b65799ec0c2ff5746d42f29284b10b193108163ede5e7ca698efb8cfb4c80a8b4af15ac6edc5c45017be0124f9679eca6cbd1cb4956d2d15bc8aaae5dae3a0a6eeecbea7df9e33748f693a827f7a47ccf9eb3bb7f838a607ce45d0904a2b302f7aaf5e9dc2ae51d79ea5f73bd2a7a5e58124d7fd405aa16d8ce30aea291e6e5bc11549dbc561281a8099fc6ea9f0edb0e1fbd66fb1a7b1c6acc6729b0d9ede35dab7a3906f06ca6028b80194357be30233f2156a675bbcdda3316902348e9d29f50dab67a65805266904a9de87bd752a8ef7275a63a7e7ca4f218e8f0680f0a8ead0a1321b2a14dc2821d2ff47fb5610b45035b0d5d12acf89a376db935275463902181ee1b140c434dc3e280edda31c2abe2cdf33510a08370b14227f862af3c0db251f878bb7bf72d067a1c31a085f7ca922d8c45692c9951962a233357b39a7147a3e41d9589d2ef32e7dbd4a210888ce009fed7c47df754690fae2f42058ac377ee3b927d01e68fb32e9f0558851ff21515ce6bba1421d8531d04c8770b56581a0e0d0c9be0a8356412ec96ab15de6255fc356d27be4b5fb0beec15fc84cc4e2bce2d28ca12bfbb8e769cc11385cb2f63e4de53c64a46157130147505cb8b79a8147282d6b368d04aa97f94c8cf8ebf564438d246e898a3060cc303fc388b078a49476e5cddb30a557ffd0bbc1e1517ec1345022e6f8237c001a7c88e1323f54680f1de08d508a95d7ed85228ec880720bac4a26f3b6e2e12bc2d0adb8c3aaab794c935ae7f7477ce126e553829a5718aac200edff734780b457cf6a98cd0379193b92ae38b3a2f7730ec16521c8304f10e62deac89aeb3b8fc87ecfafe75012aa0ccfd2e0b51267e925bbee0d5160c2665d3225de790e11caf7b1fad0d35349f84f22bb0bb8f02eaa2b37db51ed627246464dfddf6a952e4c1c027a11904a94dd3f618a56fb5de08d7b59091cb22800aab52b78947f71cc493184fc40b2fc019400fc3713f35a5892f5b8ed35dddd9f97d191d98ae81987fec3a657985e1e35d129a4a54eef1376af0abd2df0b58496e8663c28ac6aa1b330658eaef19c98f51f304b2a831fa0250045965f9b252444bedf6adabe3091e3a81c6c224c83c62bd9b1f44f5d6ccb8ccfb37045d837bad1f5dce48b253728c5fbf7a7d60c7d6b084ab5ec15ba17951a800c43157b4d2d4939ff0ccd7dc26729c3166146bb6aca485df2977ebefd6729539ea611ebda958cf1dd6dd9eb27c0369a68bbd6e1de3c85e1fde8f9e5515965b38e11ca5a5d8c7622bc95b584a0d262c0dd60b314f8f1eca0881fa68b0e5db3abf96623729a5e5b34caaba959cc41feeb607f8961c8e9a6e453dcfb96fd6ab4b649871d5e4fe45d6e8accad8921788c28df8d29a29b6f7e70689b4fe40e4e6ab6c83e57b58e836df937fc2099f4ea2f65606a856ab5984179baf2ed549823991cfc76c6fbb492d742f46cafdef476c33657a5e30842bfb59bd6fcc6014b353c8b9158f92b38892a6f56d652d8d79d2c58ffe343de88b06f667cc2b25665dc70b5cca369c75dfc3c3caad8c31ded93a524fc952457aa91954857df9a1df8683f075838f00f3bdcc5f4a8299b3591ee92ec243c5ed586860ec9853845c12294a15d7fbc4e6148e6d747e2dbd86d2ce20dcd82f8aa876dd6a5a1c55ade79279855719242828b454802c2de72e8417639ed7c1ac9160ac2bcf858049d9c5e7c243b24974e7cdac29b5ff42689bb74a51f35ac684467f0b189dc9c9135d866895012b760badf17151da5c4be566aaa793c8a86b9324ff7f02360ad5ac2431be7e577aace79873792b632f6a533875cd6646ccaa1f8c789f7c636e83f3b8093294d09c8ef44ebb0e5d247872db087be2ad333f021f8f157396e5a5413fc2541ec5e4c37162dd3f9532f98d5a2a56f2d08191cd6447ba50660430b6c77c56be2be12a3fabb5c20498bef5064b3229dcabfec004b737a8573d567a677e8e776f7f12eae566c6a049690587bebae005d7cf9f88098f555b8bbdc41319d23f895f00237721a00a863f7b4a70094be82d890d0416b6bd2d8fded459da5f56a5d21e5b89f8d8c122ca4e0a04faed5b5de78b42ea2ce93d06e304b07487d5ded106f0cce1ac2b3be229eb58d27bc993256ca7938ec10f01823cfd808f1be3dd464d179082cd873ececa4ea464b582b49041c0f6de6c0d81a47db502139aa96d3a0da84c78b1ab651cf57076e5534b1b4ecb4a21ba575e94608e787206442a0227d9495fc8589fc2e89ae7fdaa767824034961a3c1bc3aca19b88999194a953d89ffa07409136195d1b77599937860fd92794505981490b90ae6462f9417b37617923e8ffec6791a47ca5cdfd0fd6fd5e4bc05e951d77ae39a0389d46ffcef8ae6066b3596aae5d4c6f1d06a8088ddc75f7067383a76c7eaf24f00e9949ff587448a0c0f91f58b3a4c2e839ba1b824e1f0582c3c44b7bb1c871023b66bf19340e0b9fab48c0003e401edf7921ee905039b58d9bfedbd845ac68b9fc8c22d971f410cfe2118e5da3429aaf15200bc48ca0cca657a2f5a8e33cf2f777a74ea35cb8e5629833990b682697229deb813dd1109f157f93fd2782fab9b008914af1c5f78d433df23e5cd6a425200c94c62900f69d280dc2871c332e7e71ae933b44c24493508e0fa3b6821628c32b5fd49b4e875fecc07bbd6bd81224956f1fc02fc54b59bacdad534a28408c5f4a3530d8a8e7f9375ebc33dc999a95c3df2c5753eaf9838dacace1549dcc1da98bbcefc5719e1d8d7edc6640f2c8e1657d59b364f5a1e1e9c48637a227cacf7e7bfde9bea27b121350ad096adc3e2a46bd9160936b3595fd73e26291d81bcd079283e29ac7346b8899a3d968f593f8dabbca638bb458cfa58fc360f5d8ee10607e938cc79804ccb1a0237cfa308c4087e4a2c6013ed9d2a25ee73071cb1fe1e397c75624693b31a9813af22cdbd656d229e658df1ae17fabe38a56b0e5b5896f3d304dd4241cb2068c2a133e64fa740d290e43040d65d806203a9b0af92fd368c7f9fb569dc660a2be64d08bc0d9f178f454fa6b7eef61d4a44b9663bda7692c03c45481d4b20ffbd663134c803138038fae87e0fb268c8f33e446200b233915ae8683e9942f9fadc9c8608df62954b73b2056339e018824de00027fae0e304b0eb4efc33e1d46423355cf3342c63f2267cd7911202b9efb133d14ab0f48d3e843bd686df1a6f5e492816a00db88446a5e8252db4927ab1ed1f45236b564a10f82b4483ff1bb6f3339373fdf950dee2ef9b882d53bd0a58dbacf8c309f03647a4200251420345c9666aa52e23a3c121d5a56d08a3274daecf2514b307cfaa5d0041876b908c06f1b9acf62ac6d5e7f75fb6019cfc844eca4281a87c6710f99892d34d2dfa4ca0c2fb915e38e47fc174b0016ad581f833908d6577038a257fc8f647582cb7c0df4d1689173195e775771cd4eb61a8962053dfe04cff9e0b551349855d798e7884e8a41baf0895e89a4e7b8e9db463e37801dad7d7ef2a41efc70071fcb94bafdfe58908b7bda91c2a3af5adee4b50e87d803ef969fe773cf257967a89621f91952fdb74719ae35a51cb314ab0153577534a29173e5f7dcf921d0173e57d46956b519eb484544ff96ca2e6936294de1d3a533b960d957aa547c6a1cd37735405de3f63ab42a586ec95a121ba2939049c631d078d94c3afe0743a4a13ce197ce9dfd6480cbba30c465c5619a67943b01d875fedf0bb53b9a7dbeee197827a1c484f69cc7a514b842b1978e2be33601b9876c6b16e997ae9e29dfbf02664b0683512cba43d8745c0f093a2665cd44adbcfba9e86bcf5268dee7d694b21b22ea373b688168113324f454b27a42eb59d50ebf8e6ec4ecb5f9a921e27cd1b440691caeda3e828df94fcca597f0c5017b3a841cd2a2a426ef29720afea763bf50b9670e83be7702d32018abc2a527ff998148759886d1fd7724d16ecabb39a884a572f7130e6b0201dc2121a06d007d61f5e5f18b8329fdc7e3533fd1d8e34909576b3825d88988941c2dd986e42795e49750ef48f7335f5f399142b19ddf740085474fbac461e2da17b342619236f4183abf79bcdf58d3027fa575754815e2a795f6710baf261edb4a49889ca8a6ac5d63727a12b15c53d5a3bba72bd98e50f76636162dd30579dbc1eaa0472a05d2f07d8b5e3e1f0b8f08f146b4d94e3eec7", 0xf84}], 0x3)
sysctl$net_pipex(&(0x7f0000000240)={0x4, 0x23, 0x1}, 0x3, &(0x7f0000000280)="1e4606ac", &(0x7f0000000340)=0x4, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
r2 = socket$unix(0x1, 0x2, 0x0)
sendmsg$unix(r2, &(0x7f0000000080)={&(0x7f0000000000)=@file={0x170, './file0\x00'}, 0x9, 0x0, 0x0, 0x0, 0x40}, 0x0)
recvmmsg(r1, &(0x7f00000005c0)={0x0}, 0x10, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000005c0)=[{&(0x7f0000000000)="6aa667ba16ae", 0x6}, {&(0x7f00000001c0)="1736893954225f03b500ac64e2e6475519442e9ab203a0fe698d4f19df2149118d6e3f520dfde3fbda3525608dec9e46643c5160b2b0cd68e7016edbceb767", 0x3f}, {&(0x7f0000000200)="6a6c1119109581750a92e9ca908dab1cffc48b92afc8db779e621c3d6b70a2fad435db7739bb2d5a01866262050142f11767d5e30b", 0x35}, {&(0x7f0000000240)="e6e519853a5c5fd5048e882802fa077c56e02b692945d8854733e96c236d1a9ee3a251f4052c58bb2d92c1a28b58a1b755a842786afc38b9b1f21140432a5e54845e21fb769ea7be3d7e742b4d885d79ebf80275b42b", 0x56}], 0x4, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
r0 = socket(0x2, 0x1, 0x0)
sendmsg(r0, &(0x7f00000014c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000380)=[{0x10}], 0x10}, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b31f8"], 0x10)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa)
rename(&(0x7f0000000040)='..', &(0x7f00000001c0)='./file0\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a18", 0xd}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
recvmmsg(r0, &(0x7f0000000380)={0x0}, 0x10, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000040)=0xffffff7d)
kqueue()
kqueue()
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000140)='#!', 0x2}, {&(0x7f0000000000)='w'}, {&(0x7f0000000280)="38bf86f0709fe9505a6c46b24a027cfee0395081776729f8dd4cdc1fd068cd7321ae60632cb059a8fbd634b0e9cba61f936c45db9f94a707381f0fe9080291c6223591dec40945f42c2543861002e6659b3de3356505b56745fc1056647d48dbda987ef5faaf42a94e060c450c2e43cdc21a0287bd6cb19ab759a1ad9c5d86"}, {&(0x7f0000000300)="c44f0f9a4c06837e11f70d88bd036a96d9d5d85372859d55f79052ab531d6eed9ac34d5ba2d9a8b9d143b1dbdd66cd0ff36845f14e40babecaa1671567ecf30f1af4bc0827147b903b887be0832836e3427e165b"}], 0x2)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="0909000000000000003b52cb261ec3744b80b0aba0ebe1c1251f015bffc2200a", 0x20)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0187009, &(0x7f0000000380)={{}, 0x0, 0xf0ff0f})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x50}, {0x3}, {0x4406}]})
syz_emit_ethernet(0x4a, &(0x7f00000002c0)={@remote, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a3c692", 0x14, 0x0, 0x0, @rand_addr="8c3a02ced133a1650e378bbfe3a6765e", @ipv4={'\x00', '\xff\xff', @broadcast}, {[], @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@local, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @broadcast, {[@lsrr={0x83, 0x3}]}}, @icmp=@info_request={0x10}}}}})
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x3}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x7c}, {0x25}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
syz_emit_ethernet(0x400e, &(0x7f0000000000)={@random="fd658b242f9e", @random="ce033c48a196", [], {@ipv6={0x86dd, {0x0, 0x6, "a21f98", 0x0, 0x0, 0x0, @empty, @mcast2}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000300), 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000300), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSDIRFILT(r1, 0x8004427d, &(0x7f0000000040)=0xff)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f00000001c0)={@local, @remote, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @broadcast, "d7d2722802ebb308eedf976aee9577c6"}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1e6e)
r0 = open(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$PCIOCREAD(r0, 0xc0107002, &(0x7f00000001c0))
sysctl$net_inet_udp(&(0x7f0000000000)={0x4, 0x2, 0x11, 0x5}, 0x4, &(0x7f0000000080)="819b1b551141d83a20fa9ab19b8976dafe8a8687232532bce2b68372a301c6fd83df607cd584a99031fcc4aa70ef6748e57160f6388c5b393b798103001fd56f9f0507c91b290e2f59604f89feece303dd802855098a02fff9717e36e2045b4f6185e5567483a58c2376eb51a575cffb4ab3ffcc7a8672ee310080d670498f0a321c8a2b9d6f4beb056ee5f7faf35143959789ae72fb8b43caadcd05f4efa2555927b9936cc0e23b9db097836877e7f063790c1b8aff54c283621ce1ab2d5b4cd08b5874cad9770690741c4034e5ce3c101e861a6e9853d6bb240f3121c78c94d8c022168685040a0d4b5aec6ada05eb85", 0x0, &(0x7f0000000040)="b72e356ce56c4c8c8bcc6594e23a1b8ad328ff129a55ce9f1508cdb5540000", 0x1f)
r0 = msgget$private(0x0, 0x10)
r1 = syz_open_pts()
ioctl$TIOCCONS(r1, 0x80047462, &(0x7f0000000740)=0x1f)
fcntl$dupfd(0xffffffffffffffff, 0x0, r1)
r2 = getegid()
open$dir(&(0x7f0000000280)='./file0\x00', 0x200, 0x7a545d2d1c0a6712)
r3 = msgget$private(0x0, 0xfffffffffffffffd)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0xffffffffffffffff, r2}, 0xc)
socket(0x1, 0x1, 0x0)
msgsnd(r3, &(0x7f00000012c0)=ANY=[@ANYRESDEC, @ANYRESOCT, @ANYRES8], 0x0, 0x800)
r4 = socket(0x11, 0x4003, 0x0)
msgrcv(r0, &(0x7f00000011c0)=ANY=[@ANYBLOB='\x00'/218], 0xda, 0x2, 0x1000)
getsockopt(r4, 0x11, 0x3, 0x0, 0x0)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
msgrcv(r3, &(0x7f00000001c0)=ANY=[@ANYRESDEC=r2, @ANYRESHEX, @ANYRESDEC=r5, @ANYRESHEX, @ANYRES8=r4, @ANYRES32, @ANYRESOCT=r3, @ANYRES32=r2], 0x9a, 0xd6d0c418f59fe7d3, 0x1800)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000000)={0x0, 0xf4240}, 0x10)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0xf6, 0x3ef, 0x5, 0x1fc80d8b, "04000000010000a9b3c7420000d35ed900"})
writev(r0, &(0x7f0000001580)=[{&(0x7f0000000300)="869b8994e23f631a16560e854318424616d137102874779753f8cd0366ce35a4e16b5defe5cb376c61325f365666f361e2c7a45f272d67d5bf50336ecce2e2b7563f1a330384636f911ceab72cba3b6e23f3a11d1589452398a47f4c1c2edbcd0b018e684b97476525a2d27247a3c95e0bee608e7fe5df5a6a9e948fe10e0600b43e1417bec624635d0dd8b5c419050c3b408f551ae11ca99d36741e5dbf16922c31b74e66834d4b1dff567d66f268cdd11c493a599abe951cf75bafbeb4e02a3d896e00947e0d8d41bcd973adfe85c3269217fab9614b2d0dd3f87594d274e187cd96fa012852be02822d7e8918cd01de48620e888a3b781115194fe42c1420ec5a25f6a3af8b36cce6ae3a6329f903f7d3cea8305aa5aa885eba6d9174e33c0859f15923d884428158b4e27b27306a1e7922efe666f4953acb24977869597c9ef1cdf25e97a63f59f2db6fc60e43b3025336ec48644c035a396ecddf8ac1f8b5914cf60cb0d84e53c28b386110107327847a45aa37fcbedd3993224f02fa65f6832395a428157cdce48184cacdc30e9d94ed0a6bac53b10571c98ea509c2f72c2c962a1c39e2f310730621eb0c3bc41340048e41a2f87d0f953ddac4d37a4a46b48e4b58e5c647b47441e43e7d624c77b82aa7a63abac6e159ef6f4528046374d66a6037c1b9db06998e9fb1a3874fdc869bdcc978514868c48c2c9903df7dbfc87247d9662cbfaa6913dc4f6b3fa695a55117fabfe7a088642afe0c667f3cbf9ff1a9dc1eacd0bb0bf6d891a24ca5be4ccdee1f7bac2ae19cfe689a3df5bfd5de1db8170c59bbe0c8b7e7574dba59c23b731452badff650863a53dab413c94f8be8192bab7e635968beb1e521d1d8c9eda1756b84482b4621c5f10796b4f1df85736010e1a45df13c6fea884263dc2c9be24bc30bfed4cc0f534d67ac85194eb8c7f18ecf1be5cdf029f649e177e6eacd49abb3625c7b9790bf234fc7da46c5a1a8502688081e1fe107857e8e328538a6430e30374031e6490422f3447e168b8cd0e96dca9d18d3b90fb4894b353fa3032b8441546b56117ab7ec5fc735b38194d4016017c36e47be41f31e01d0b1990802cce297fb48b70618d2902f967d3004a6e596074c295d61d7d8fd912aa5cfd79aaa0a284cbaa3b4818aaa0583d3725546ce17723aafad0f1a8781683ed40837eca54252856bd0f9189b136c6da6b4e73117c4def0abddffc09d7dd4b70a9d1e331873d15507c214d55026c95dd931ba47c54defc7c984fb0341193701d36a02745ac60577df2915d3e853e2be02cefa8d40e2694b57c6a77f195caa60876c84a40f55dbd2761f3bbd08800ada2d7736c4565b8bb1b03d23c82134603a01cb3fe753882247e557b304568778f329085b3ce83645b31c1667ceceefb6531cab78963c96c17ba2899d48b5bd4c43616b392e48be24477bc9189ca2f89b8b49f4862216a45994d303859806c7984b176528545ab1a8dfb0bcf8eb574ff27c0d365bab4c2051ce2e834dd11d0101", 0x43e}], 0x1)
r0 = socket(0x18, 0x1, 0x0)
getsockopt(r0, 0xffff, 0x1021, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x14}, {0x48}, {0x6, 0x0, 0x0, 0x801fff7a}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x200, 0x0)
mmap(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x1, 0x10, r0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
semget$private(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x7, &(0x7f0000000000), 0x0)
getrusage(0x2, 0x0)
openat$pf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x4}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@random="9d33da9ca126", @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="43f264a7e809", @multicast1, @remote, @loopback}}}})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x2, 0x0, 0x1c, 0x0, 0x7fff, 0x0, 0x0, 0x0, @rand_addr, @multicast1}, @udp={{0x3, 0x0, 0x8}}}}}})
write(0xffffffffffffffff, &(0x7f00000001c0)="0032568c952143b05817620b7ae7a9808adf9c9942c96e092d13b51ecc75ea5ad4ae0b1e9539fd2fdd55ab68a49b016ce6def2bcdf396195100bcc62e8e3250ba90d209f4d123b7f718fe88f90d8e10841a85512c25ff121d98ba6f16db95ab0bdb375d627800de0359f439b9a017d9af7229914c0e305b36cfd81c6fe307fcaa598ed0b238d490c7501756425219a3a560a9b5a80c6c34aecaddd1feb8df45570029164634b6e70fd922f194d05d1ffcd10de76650b54a49ed67e610abbf6072885c5960aed7ad71910edb61a27a9235d7cefb5775fcde1769574aaecc1f17e2cb3e753702d72901deaf8650ec0d543f23812d6caab9c76a67d3c20d578c109a6c10f7ab40538890383908281bf81f072ca4929760d2e851aa52029817881bc407549d7715d72e95786c108c72ab37a38945f77607cf2803186cd1170accaa88a42b9ecee4e35b5b7a20c45b14f2c8e4a89e739029b758a29f139118c1f44", 0x15f)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc028698d, &(0x7f0000000300))
r0 = msgget$private(0x0, 0x0)
msgrcv(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="000000000000000000000000000000000000000000000000000000000000000000ffffff10ffffffec00000000000000000000000000000000000000000000000000200000000000000000ff4b2eda3c276139000054d6b7264318b7a300"/134], 0x8e, 0x3, 0x1000)
msgctl$IPC_RMID(r0, 0xa)
r1 = getuid()
r2 = semget$private(0x0, 0x1, 0x82)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r4)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000240)={0x0, <r5=>0x0}, 0xc)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000280)={{0x2, r1, r4, r5, 0xffffffffffffffff, 0x22, 0x9}, 0x8000000000000001, 0xff, 0x7fff})
fchownat(0xffffffffffffffff, 0x0, r1, 0x0, 0x2)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={<r6=>0x0}, &(0x7f0000000080)=0xc)
msgctl$IPC_SET(r0, 0x1, &(0x7f00000000c0)={{0x3, 0x0, 0xffffffffffffffff, r1, 0x0, 0x24, 0x5}, 0x90e0, 0x380000, r6, 0x0, 0x1, 0x8000000000000000, 0x3, 0x7fffffffffffffff})
unveil(&(0x7f0000000040)='./bus/\x00', &(0x7f0000000580)='r\x00')
msgget(0x0, 0x220)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f0000000180)="16b766f73dcb3ab24dec82f76466972f442b6a1230e19a4e91597f7153015dcbca60ed45faf561a06cbc7aff0be573bc7f8dcc57dd281d4eadb78aa6d58d955fc100d3192fc176860c86ded19eff845d8133e57fef6a28109ef8416f21aff9d1bd3a4c662b", 0x65}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0xb}, 0x2, 0x0, 0x0, &(0x7f0000000100), 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2040, 0x4f4b)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000006c0)={&(0x7f0000000140)='./bus\x00', 0x200, 0x0})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202c17e7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000), 0x10)
r1 = dup(r0)
setsockopt(r1, 0x6, 0x8, &(0x7f0000000040)="cad7aeeb", 0x4)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
r3 = accept$unix(r1, 0x0, 0x0)
write(r3, &(0x7f0000000240)="ddee43ab8734abe72e8289e48f848dcae6855abde176689fd16e6ad78a4f273bc1d2e3f3f911c89a2472d11399504870b9fb0e36d8892e80897800000000d20a2c82125431700f50a6b8d22db0ec0ff3a1800280b535084f92a7499cd07afbd1d0b82c439984f338ffbf66decf2ba302619dc2dcef33b15aa3d89beb2b70ddbd84311f09a2639749e5f4c3669169eb4be5a922395a28149f4d2ec0588916c297ac43adfbb5775ab949938e2baef7e6fa3703487dc7cd0255926c0ec9", 0xfffffea5)
execve(0x0, 0x0, 0x0)
sendmsg$unix(r3, &(0x7f0000000540)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x1}, 0x8, &(0x7f0000000200)=[{0x0}], 0x1}, 0x1)
dup2(r1, r3)
r0 = socket(0x18, 0x3, 0x0)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
sysctl$hw(&(0x7f0000000240)={0x6, 0x4}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x2c}, {0x1d}, {0x4406}]})
syz_emit_ethernet(0x4a, &(0x7f0000000200)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "2bd4ff", 0x14, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2={0xff, 0x3}, {[], @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0xfffffffffffffffa, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$net_inet6_ip6(0x0, 0x0, 0x0, 0x0, &(0x7f00000001c0)="e3", 0x1)
getitimer(0x1, &(0x7f0000000280))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x5})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x6})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
getpeername$inet(0xffffffffffffffff, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6, 0x1f, 0x0, 0x1}, {0x0, 0x0, 0x40}, {0x5, 0x0, 0x2}]})
pipe(0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xed, 0x0, 0x0)
close(0xffffffffffffff9c)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7ffffffe})
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
r2 = open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
dup2(r2, r0)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000200)=[{0x4, 0x0, 0x0, 0x4}, {0x35}, {0x6, 0x0, 0x0, 0xbb4}]})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000180)={0xffffffffffffffff, <r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000340), &(0x7f0000000380)=0xfffffffffffffdc0)
pwrite(r3, &(0x7f00000000c0)="d000"/14, 0xe, 0x0)
setuid(0xee01)
r0 = socket(0x800000018, 0x2, 0x0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0xe, &(0x7f0000000000)="02000000", 0x4)
dup2(r1, r0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
shutdown(0xffffffffffffffff, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x6, 0x0, 0x0, 0x800}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000080)="eaef125c", 0x4)
r0 = syz_open_pts()
poll(&(0x7f00000000c0)=[{r0}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000000)=0x400)
syz_open_pts()
syz_open_pts()
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @rand_addr, {[@lsrr={0x83, 0x3}]}}, @icmp=@mask_reply}}}})
r0 = open$dir(&(0x7f0000000240)='./file0\x00', 0x8, 0x0)
r1 = socket$inet(0x2, 0x8000, 0x4)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
pipe(&(0x7f00000005c0)={<r2=>0xffffffffffffffff})
kevent(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
pipe(&(0x7f00000005c0)={<r3=>0xffffffffffffffff})
fcntl$setown(r3, 0x6, 0x0)
r4 = open$dir(&(0x7f0000000340)='./file0\x00', 0x40, 0x0)
r5 = getegid()
sendmsg$unix(0xffffffffffffffff, &(0x7f00000005c0)={&(0x7f0000000040)=@abs={0x7d55183150396371, 0x0, 0x1}, 0x8, &(0x7f00000000c0)=[{&(0x7f0000000140)="c1711c778434676c2e89f9211537834f7e5faf792a5c1560470aadd4e80a324d1685485cd7c81cc34e96aea990ac2907c92f01e5d26db6c2420eba997ac5513dd61c7e815ad99baff0b56bc54ec05c878e4fb48f88e36fb1661997ce925cb7c50831805761bada9f677e94331d2f6e6601cae3afc4e8006d029fbaf7195abddf2a70ca4434cdb2a2c615b69e74f664ce5c2080fa378e3da5167b884c0ddadb79975c447e266e460f26962edad4471ac6e0df8efe30ff93f303f124", 0xbb}], 0x1, &(0x7f0000000600)=ANY=[@ANYBLOB="d733d6cd00000000f1211f82e2b2ffff00000100000087f0d16e337c2b40b43ebe15f1fe33958722b034e835905ebc27e8ab82459b49853eb957ee290292ee8a150617011444b913d2b84916a7d0e055265eaf1a1e289064da36f43987ae458e54a595865d0702931f59df4dbe67da91a3e3c2366e436ee10d4564289af0eb95f239cc13bb58ce8735a97d5f273e7c216c90cf89f1fe7877fd5f5e8dfad39219b0ccb7a61c27fe6cb101b40b24faa7b5a58890bdde7ec9b29f119ea1c8950a147d372fced6e979c0bfa6f804103c736004b85ca56b9ec9b6782fc89088655ef77023cde2f5ae05c9d41f348105c2cdd711e2a44e2f", @ANYRES32, @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYBLOB="000000003800000000000000ffff000001000000", @ANYRES32=0xffffffffffffff9c, @ANYRES32, @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYRES32, @ANYRES32, @ANYRES32=r0, @ANYRES32, @ANYRES32, @ANYRES32=r1, @ANYBLOB="2000000000000000ffff000001000000", @ANYRES32, @ANYRES32=r2, @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32=r4, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32=0xffffffffffffff9c, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32, @ANYRES32=r5, @ANYBLOB='\x00\x00\x00\x00'], 0x138, 0x3}, 0x404)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000080)="0acf76ec", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x4)
sysctl$kern(&(0x7f0000000000)={0x1, 0x26}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
fcntl$getown(r0, 0x5)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000003c0)=[{0x0}, {0x0}, {&(0x7f0000000200)="d822878eb3013d2c0b1c1c09c8c66863b8019a54e3111bc0631eda2341cf0c49a82a1ebf4ca4a39e31ce9c7acc79d1283c540759b92f46e6a03faa60abd650de587203b5c7cee371c6819572a0da0723355deac7683ea494b6d145eba98f7ac0a721b5c5ade239ef7cfd102c0e550b3ad1181a8f86fd755c2a8a0a8eac064427d4f103092c2a1dc22b7c221a612e4e", 0x8f}], 0x3)
execve(0x0, 0x0, 0x0)
execve(0x0, &(0x7f0000000180)=[&(0x7f0000000100)=',.\x00'], 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000140)={{}, 0x0, 0x0, 0x2c7bdc72})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000180)='r\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000095, 0x4], [0xffffffffffffdfff, 0x0, 0x0, 0x2, 0x0, 0x0, 0x100000000000007], [], [{}, {}, {}, {}, {0x0, 0x0, 0x100003}, {0x0, 0x0, 0x0, 0x100008}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$setown(r0, 0x6, 0xffffffffffffffff)
sysctl$net_inet_ipip(&(0x7f0000000000)={0x4, 0x2, 0x4, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
socket(0x2, 0x2, 0x0)
socket(0x18, 0x1, 0x0)
open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
socket(0x18, 0x1, 0x0)
socket$unix(0x1, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
r1 = open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f00000001c0)=ANY=[@ANYBLOB="140000000000000166000d0003000000", @ANYRES32, @ANYRES32, @ANYRES32=r1], 0x30}, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x13}, 0x4, 0x0, 0x0, 0x0, 0x0)
madvise(&(0x7f0000ff1000/0xf000)=nil, 0xf000, 0x0)
munmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140), 0x0)
clock_getres(0x4, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000340)=[{0x80}, {0x74}, {0x16}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f00000003c0)=[{}, {}], 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450444d, &(0x7f0000000000))
r0 = open(&(0x7f0000000040)='./file3\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90", 0x1}], 0x1, 0x0)
write(0xffffffffffffffff, &(0x7f0000000780)="35b74ab1fd69a7fca05aedcdb2c2567b9acbb29a668ad1e19b91fed5a7d1f473197e584a5458af39f0e66849f9668fe987d6cfc8571ae7c13c056dd5881d822c4ef207feb72a79d1fb24ba68f5ab7df8086db166858507c1af282e1253c749648e2d34da683dfde063d33da0313cc05b2b984f32ada485e07614c9f34d39dfea7e43e5afc0c5e4b88b9c7c2fe51a248a2a496ea7946eab35e987d4bc07d7e4d827505a4d7179d7282e7c815caab75e489a2e7f0aca9e8fc459719b4474d7b64bc31ac1304892e86e0a7a9bf75c7a1aca4f22230780efbed3911936a3f222db52ec8a798d9c52b097279ae3e1379b2772453a2db4a1053079afe19ce1c9f19565ce67168522f024d4513a805a3cb634eb298a6f8a6bd509621411e949b07b93884f8d7f4568a4c592e36cd22709658ce2b5b516af89f28927a6b5c995b2eeb0cd12fe15826acfde817822abb66fa4f947cf9351d143d6a0e5844425cf4d1851c488f0734b30f67a9d534e22c5a2b2c5d145e285819d815fc915ea0d2a1f8363ec617534fafa0370cda8196906919b4da9b1958d349d10a827ff001942fd961b76fb9410eb1f855c41afaed5441463f9c1067d5106d47c24f56975f374de129b14b9e3566a4bf1fb8ff8d93554d812083d1586c62498a16b6c0198e622eaeba7aac149319643557fa8855e3095aadc0d7e74e2017614905d74805cba22f75275acf7098edf8955333c5513c849b2c2594d2b15d4b037db3b99ba0eb75f5e1494916b7aad0ea2abf063df4d38ac9f13c49b7f23a7c014f5a5d0473bbc9d6ba281a6ab1fedd7561b154dfdea91233b672ba452b3ff6fb5d58171957d1cefb857aa2ffe1a444f3bead340407112d891a493eb903a8bdad501d7152e5223c9979625df4f9e2ddad94dbb4fb07d340690ae6debc3303d8c14e2c4acd316eef22313c79c9cb993445f16e2a34cba690fed7a8e24e3acfa957127470b6addc1290c5dc888ee3b58b944d15c87c9a71625b09f6dc7ad0915040ed1d172a3c1cc828db8fc8680fb3586e97d5af7087ec7cb3c0f75d2f430f0c604ef8b692f591a386d8fb534cfce2f72efebb98cf102b864da569b10db7613587e18198dadf1573c20690e1c67ec451c431f4a4614560614da78dee4b48f822dfd607e817c16f12bcf7b66b026f8b14413c58ccd6b2a1df3399314c6e0d9d38edd8795c42f9acb5a14d1ccfa0b68382260daaad2ab16d858a5ec42d4b96da4b025444de75229bac353252ce3780f80f37f9a94bb8101792d2f2479db94664eb6cd637561057a13e7f5407c7fead8b3a85bf3643dcf7ecc5e971d5b972fed8ad71b00fc66e87189b6273e3f161557f72a8bebc49b598ca363dbd22e0d6a74bcf63ba39a7272d6d236a9148bcf3de20410077f9f7a8b0dc85e1edb61ae1b0d2fd39691a45af822c0c46258857166041c104c60313a17049e2e9b6750bbb221d503ee13e12301e4769676766b55f332e15f4a79a81266e345304e28a623a22d10debeae8b4bdefa79f0ff8222a02de625c6837d0903dfadac9b07ddfadb93276ec5d571390d2a254d1546ae3f0cd06eb85de411c3ead8f7e37e1d6d1ef810e0196ffe9b7365e16376366c3a7ead4e667c465b15a82d358bd65ad717c0ab0620d095e8246bdcf63e9ee5cfb0ca24fa823ea5d1e2b0724c969d4e4e21e5ce32e948697edc64fabaeb6e85b5424c59b2743dc6c2ee413cd7fa4a1a5bbb29cf51a032cd2c6300f3ed7d563bfa00b27dab974e9644f6f34bf0a6dd62e516ff4f007b6e18f3cc2b72917a00ec72e677fd67af528fe97935f0146132ad3ff07d691e609a8d38936b9b17d82eab8b63ef3ef60bda19d44b9aaacb170a2736e43d195e0936f0301ceb4bd43366d3e84aaf261ed8b3fa5c18a694caa6c6b500755517d11dac29b60ee6385df9e5192abc12caa9c29a5eb18efd2a0fe28a286ce1be1a83e4c73676fe9e71dc303304e72b8a85e455fd5c8e48e187bbe77751ae4b1e323cd19479a7d997244326b6a5d63dc8e6a4b865e45a75f2d7071431d778e477abfdce111b2c66a6df0c17f0b2201b1bf1376b0ba84328af101092441372f71606c75725620b0fd6946692bd9d40889ea2dfd293b168c11f81f467aa855f73dc47e4f94e1cf9b958f8c5038dd3a36196e927b323ca0c5709fbf15ed89809cd7ea7ebf1866d90e6df1d629d1859eb7f0792c0168b2678fc2fc527bb3c097eb97159b2b30215bd6fbd0c819d4db33bc1b37afb62193e6fcf9776599c9185ae49f8ca344eab4a8c01729eb78a6b280e85e7bb1bed2df889e748fd3dfc2780807a78c5eb7fc6887292b1ff0f8679019ed736b14c644412b01e1005542260812c8d4e2db2f534bb0b1a94c08234d877d3d1ec4b18619817f6b0dd77d1efc2bd82d0185b0e137d3b5594821cfd54297886b7f0471ab62cf52df0517fd7f0950d203e132bd251da0a44b68d907387c8b14ef0a4656d439d8178b33c0b1f3ced95b50033a5c3af677cd6d49e15c0934c7a352998d7c9229e20feaa2ca48e6252dcf48930c9d36f1ca1a77051c4e75fe38dda5627311c28b7495bd122b717acb87415154e5832f2e4b88a79f01d427ef6360a30371cf10149460cb0baaeb35f0032ca3b11b4f56a7b0d0a9b4228f05988036b2647607a86dd95c3c1f28600e938aa188cef051cb67b668cf58260d66ba7b2e470b96fd0c5999bd40c3c7d1719adafe5dbf195bb8355742f9c69e438bbf549ccdbb46d0566f091d4b1ae5e6648ead8ec28e8be68c58b4e25dce93b71b708ec7a949bc21ea8722ec953bf91ff877d955a44d2742ce1eda2f4cb612eaf0af47d38aac4f881cfad6952253e4978ebd877fd6c23e03701bd455b0fd78032d5e1c3d27de6d85d2993c2f1c8aba52af16bc55facd496a5da3ecb87a58a38bc7e1472f7107a6834df1860ca568062562b735684f15dee88418bc2583165884ad54af8fcb4b82d2bc6a44c7aa9fbe584ed0355aae6771b16401d43ff64a880b14132a494602538671835876414963386c9f292d1eacd325201f36e7ba3de55241943242a201faf1ba8dcf363bcbaaa1c1274266c56c96c22d3db560a9219be1a64cde9719855edf8d5c1c544c612c381ca40a493c5a6e9bbb1b17c92465065e9d170f95ed3e404ab96be15366eda263798388967f35a8641e431e4c17b52ce1778891b6d91f61fd870f49dc004d0b650108b210b87d776eeb68bb883e7e5c17e5062ac48e9b73c42cf19b3f63886a96ff4926494ebd9f5ecdb64e95773e5a9fa536af17d50f4ee4cd2d06bbbd9256128518b35b10b3c4cb90ee95c1658e7f4ea1fddc1472957ca3226c20376953c84c5376a00f82f597bdd92437a2018bfbb79d167397f42bd413db5611ced2b0936e2031d272dc529f8a884badcf7b6d87e936a56cde9f81bea5b094a4f3f9e5bf602dc8d5839787951a2ff61666d53d9fd2812497e56a373bb6d6c19e707c540cd1d1b979725824c4a5873e2cc6186ef031d0acdaccada25216f9947753e85cd514f217c89854dd901ba27fd4b0ab8cfb092df4b1cf920622a4cdb7f05f1fdf869e8c5286ef65f27b1a22ee523392ae90972455958fdc0f4ddddde47606a31454c0969294fd530a82001cd1e3f90df74f76178e00df92fadf79d5e092e3c1605e43177e9c5364fc420d03a74c4371d07d46b1dcfc4df7503be1c66773c8255a25fdfc396da2e6a0a8eccf06b1d124e166e59a6fa3d7ddf08b6a571553543ccbde9ade496c6e553d8631c7c80e9c27764c5b5f7751537a72e5dd7c1e4853378192fa907c1dbbe46e7a32ce6cca380a526a5cc4cd5890df1a6a418474cdb1068395dea2b68f365cebf89fc8baf194caf45f10e4fbf2cf74abbb4638dfb46e0f8ba76cd0f9c9f96980d77226307c2191cdaf16a9245a6a9a5fdf5aa971b6007c562ebc20705b872c38d85fd27318a980bbb13ceb6ae9a074c4c1f8664f329ea4ced6ad3b2e9ed1da4a9af8e8e5adaa6ba972f1d7a628823b93554b7c0e254ae6b06ac909902480053b6efdbce42caaa8ee41c5dba9257562d6e9c90c4d835c907d457e4f73eda4bb6ebf174a81ffb23681620b0426fdec4e5a74c5ee6a4615699f88663ec0b8f2857d5f4910a5aaca82875b73d17d51316da422535ede73cc2e8d88d17402c262174f4e76cc11dfbdd1a6c034e87c8080415111ac9efe94338bd2aebdc374cceb9666d84375cc16df4cf23b22e5264e736220ffd3f5ea89c4555e628f52c76ecf2214264381e69f3fcb8764de123dc8697b18268816cb247607413dccef8eb7f1f22f6a0f50bfe88b3e0c28bfe0b865c5ce57ffaf1170f384fd9d8d82e664dd31d5497a592693054b0d9500b74a15eb4c743c51849d504995f740be6519903e8d6fdbbef7bb40bed0d7d1b988cdcb6fc69637e02ca5bd433c8a3f7cde1862889b77ddf11ca76e8125fb311469d3e4f3b9a3f94c9d0bc08892c69b42089da91450f6c06a91a7c263441cb9159fe43fa05bed64e72539b3896c2ae0da72f27dc0c62dafa135faab21e5acabc44aae6050c9b2432114ee4fd9f517f7a4cc7e461f7730fa3de0a350dfc914f2c8492d774caaec21d13f58c91043ab2b8d096c5194a3b0e862c79c0476b14aa1a9b1f6a42ae94207f1a251da39d6b16e72054a2ff9acc7e55af4e4e0f32ec8719e029a381f871c6c70fe741e70ef0ec3eadb666ffeb2b1b6f7c86a1305be895cbe728ed92451ed808fa328daf104c6a501d1a1140f54cd5bae77c51981db53a6576799740ad8dd4f26587961412b26f69de9f929528bff6e87c2afce56d3f84b7c68390202fd5049c6675be9fd25f4d1de4f9862b6ddf43ef22582b97af44c498d19aec62b5fe45c0be69fdd1d49f477722f2018cfb5875c529419566e325a7ca6a61c17f3339d1778bd838df7b262f21a13be9c150c38716a7ba6edf200768a02a8e36c326dc1a5c02c4ae96639689d58f709e669d2183b6af1d4f393a2db069f5e86cd82f247d34bb1961fc0953ec3731dc4c9163228908ecdb431880b7d71a36a71daa04b0dd981e9e389148f01672f61776713a5fdf4afe6ba9afdbdd139e6dff1b4d350e3a19aeda2f3d75b0cfbdc0149eda8567603d41199c80acc9c2b83141f797d7a7cf3d3c532e98f891c917336e64165fd08474e770851ec8f375fa67e925eef10e3ff0da9339353546175de52cd1643d836da92fcfa51250acaf599286583af3b0a45c39f85cc4f62350abea6b9ed539b2ba353b7274eade52bbe003ffa8fdf55ed777908515c92f861d75acf0894a36b119e24202c04a3f2e3776bfe08b255b905751e663c3f70c8abb8829041f7bd393e3c3767eeae2d82f28e2d4d36ba3257370f5094b56e6fce83b57981dc5783b0a1f919465cb8057d31030e217e2582d88549a56d0d8e26a003123e11a09141d1354bca253ab3423971c9b05cd8c4120fc7cf2408bfdaee98d96e43532186689449466926e219a511fca6bdd2b574c6ea5e67a625fbc39ecc2b1e20dd340926456fc7e8c8a62618580aba941d7f05d882597945c2958930fb53223b1cde911190d7ebb777bc4951eca12ff85d3bf1782168c68dbbd7eb65ebbfccd73a4c64d87c86b8cd98a907a7a3189c62481ab4c630da41fbcd287869d4ebb99809594d1bdeb4753a28d6a8308b33de3f93c11b9203a4e7f5701a192a98c4c1c6f450bf9049f8079a911fa723cd1228d231667d57a1853a12c734fbf8220a64ad5d55f66fc9a399a36", 0xff0)
r1 = open(&(0x7f0000000300)='./file3\x00', 0x0, 0x0)
mmap(&(0x7f0000001000/0x10000)=nil, 0x10000, 0x0, 0x2812, r1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000140)='x\x00')
fchdir(r0)
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000240)='c\x00')
unveil(&(0x7f0000000000)='.\x00', &(0x7f00000000c0)='r\x00')
openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x40047463, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x3}, {0x48}, {0x6, 0x0, 0x0, 0x103}]})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0x3a, 0x0)
munmap(&(0x7f0000ff8000/0x8000)=nil, 0x8000)
r0 = shmget$private(0x0, 0x4000, 0x0, &(0x7f0000ffc000/0x4000)=nil)
shmat(r0, &(0x7f0000ffb000/0x2000)=nil, 0x0)
msync(&(0x7f0000ffb000/0x1000)=nil, 0x1000, 0x2)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f0000000100)={&(0x7f0000000040)='./file0\x00'})
pwritev(0xffffffffffffffff, &(0x7f0000000240)=[{&(0x7f0000000080)="bde2822a02a30a", 0x7}], 0x1, 0x0)
open(&(0x7f0000000040)='./bus\x00', 0xa9b6ae56431293d0, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000000c0)='#!', 0x2}], 0x10000000000000dd)
fchmod(r0, 0x8e3)
execve(&(0x7f0000000280)='./file0\x00', 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, &(0x7f0000000900)={0x3, 0x0, {[], [0x0, 0x0, 0x6, 0x1ff]}})
syz_emit_ethernet(0x3e, &(0x7f0000000100)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "adbf97", 0x8, 0x2b, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x0, 0x0, 0x1}]}}}}})
ioctl$WSDISPLAYIO_WSMOUSED(0xffffffffffffffff, 0x80185758, &(0x7f0000000000)={0x0, 0x0, {0x3}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x33}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x68e, 0x0)
write(r0, &(0x7f0000000400)="3e7f99a89c99ae7f942ec160cb9492a8ff6bfdfb4a474cf100fd0101000000000000e7039000b055eaa8c8f23ae0c2598bcbcb66ac44d0b541097d1b087c6c6606772ae5dd5ea75ffca809d91dea8142531be71c190c7eba2738e40c5d3189533b8dbfb2bacab570036e6d89fd02080cc183774e3dbc7586a55c88bcaf7188bd19166eea5222bd5a34fe89f70208ce298f160862a1af2fc9707069fa9c7d756080a9311a6acb700127ad0856a9532ae564037dfe38c32fcd255fbe466a62ed195ddc6d288c74de23e7e5b59cfae3f4cc8b7527e13a586ba82bd5", 0xfffffee9)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x1, 0x10, r0, 0x0)
sysctl$net_inet_tcp(&(0x7f00000000c0)={0x4, 0x2, 0x6, 0x14}, 0x4, 0x0, 0x0, &(0x7f0000001140), 0x4)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x24, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000280)=[{0x40}, {0x7c}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd606cf65b0014000000000000000000000000000000000000000000c1"])
pipe2(&(0x7f00000003c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000080)=0x20000)
readv(r1, &(0x7f0000000600)=[{&(0x7f0000000140)=""/48, 0x30}], 0x1)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000040)="6c9a62a4c9c95db47bf14ca6db17a80d9c1bd16a7079eacb808c3d6938bebf4e92126715c77cebc3abfdd10c3a910014ec", 0xffffff06}, {&(0x7f0000000000)="08b8b5a820f60205b5973d63825ff522215cb9fe1325307b6f44a6aaa65896"}, {&(0x7f0000000640)="d294db6f3f45faecd8dfd0b31eef041e74268fecb5e5b390c3ba1035346b754c218f1fa75bd6de54728f7f339b454dbbdc326b135f80d6780016fdeaed13922ee3809b5c3e7f39686d01b3de432bc0829eddd0bc56611ce48c820e171269d25643408690213099d15fe265a411b12cec7d81ddb7eba28e65fd9c988017e3d04f90c046ae61c685f5f65c278d64ff39110d6479ca77ff9a023c19f839dd8c209a3de2d236ff2b9fd9edb99c124f5c6b8a42937289acefb053ade7e0eb6fc7008a326b60204e1bd383f714e6ed447e69a4c32c0515bf20ffb35278fbd35ca589e6f0fd717d59673dba79b6eff166d00daeb6542d6450f4c2656a26f500a6b6a05d65f9171e06573192d41eee947cf23fafba1c98dd96a692ad960548461f140893f02a45474f76435161f5f4ba3ddc5b7879377dfc29f0f5fba55cd62f581b59b7cf0b117495e5891c695cc2fc3b6ffcb48b459c2f69f575e606c6def2926838daab6132a203fa88e3b8588de5f590240c79a434ff954372a37ba24f92e59a67b52a55c28be0da5187265a8188fd8706fe009376434a69e74c7602bf3b53ecafc74eac4dd1ce2dfd1cbe61d60390ddecc6b707c66861709dc7a35fa9c66fbb45068fc06bb09ef6fad11ad1fcf9d7c529b889a90a9c454bebd273108231c8c332b97f0271f2faf68a893d96a870e7bcad00668dea3a282d337ba8fa661e1ee766dfc99bb9a5aaeaa01cc465bb946bc8c13fb857762c4eff9efabbc9000141c35e1529fad6e81b3027e68104f32868661571caadf713367b43cbfbaf70a4d9469511209a2d8002de87335f28bbcc51797812b5e23142ef052930c710b458bf65a1ce4866b5dc7564e14dc6d67afce29686ad003a4ee788b68b4b9dc88dc1516adc469c994b90d67397e532fbd5ead8335544e89669220cb752519e23446f9c90b6e97f9befb4c283d2554d2ae00266146e4ea41e69a433eb128a9fcd79e96d633330063d98e798fb41fca90803ba789ba7c3886c7857532d5bf86db35114dca714b979cf586c62cb41e22f9a9ee63001d53f1ef8cde8f294f9c7a335d085fd0757ebe239c3ea66c7072134450db67a8971a8723b2565945a3d4d03a4502b5b6c53f4ab1878c5482283950ea2b7447faa6822412e1095091be3795f7dd74fcef24c056f3b2a517a7335bc82eba1d675e7f44b654efe7e74c444f15955296282f2d7de0ccd01379a2197fe4eb6a5314afe155bebe1444b82071277530f1d9911ea2976d845829d5ecb08ec419282cb9eace3567d1a967580cb6f3c41082b2e0631eb254ffa0aff0796d4bcad601f7fde9844412f1bcb59aa386ad81c2c1c736b9fee1ab0d6c16c9fbbcfcae22cb36e799170add43b3d2e2a0b9ebd9ef99be955e051d1194bf85931a4b48252ef3c4f9be2a6144cf012a2d54a18b7e4eed6f2d3e0c824ec6cc99c16efb1b82979386163e9ce0ef3a8a1255a038eab81de0c7c71c0900069deecc35c8e2b581e009d27f08425dc9eb6b5be1133f4f6a690d37f07b164e725b2210a8b93a4201007edbe0c7cf2b2935975537eaeefdeb6e90abfbddbc7bde61ff3a86814e1085714e9952a44328f3683fa5037381b53c74eb8e6f4a9143e56c1ca5f7baaed947c47378600265cf424cf58b819a197049bb1365c1133bd03f0ab9b995307fecf65fdd57ca77e44a108803cfa0430c69bcc61b9dfe47f678da8e4f840be8b8530af9628fe0af14dee55febe49232a076d9370ee04d956d70d4720cc13a2c1f0d0b874e4731daa243968836d041084f652a5965ed8df51cc26b03ca5d526a8a065e1c5f13c06422f6d425510fc8828795085a262a5c4971ad54c8b9039209c0038b3859884171883196de13540f0b886990c70147acf043e9eacf62218895216239145770480ddf183e0f29b139f855b4755e08debc66467d5eb5643db1a86d02423c39f85492f843af90c848803a662e5ada74045017e39dda7ae484372bace2af67e9337905ccb556d6656d37a5f234d11549706784d2ccb2f4e20d32c4f5d1aef3c88ab44057f2351ef290f5e98f958248e5f586f7e5d506f0ab8d61b9c2f1552719303ddfadb536fec307a48d103d93fcc3039e934682533d79c1c152b164800153b8db1bcac5527dd954cdf12fe31d9e3a2eba508d074347282749ae540a4b3c5153c702493fb417b964f0d9081bc92fe395cdcf45cdf4404c2afb857f35c7d3811dc373544ee1189b9ba44be599f324f6eed6468ea9d64653a66e4a53bd88848bfb05d76198a8ebe99f863a10afa1267374ae44901ddefaa8fb203513d85c18e41f0d495725a7d8d7b9edfa93151022f3f1045da2fed18e5cfe1162efe81e64acf31ca275bd7945d6c597767b175bacafe9ae9f544f0088f71e2671f7f3354ff13a3826b00bf24a5a19f821c2d1403fafc46870bc36642449791243fa0506d0e248a9ca10d93d4695bef0f37eff7640a5787acf1383114394cac2436657f217752306045288cf58c90b6bdad2fb2fa37724dc06fc09d00c376e954dee1642fae604a6ae9aad8f4706c86a561810d056ca4fabcd8b13be039bc54da93c13053bafd434e29e2b2de024ee5d9bfc586cd6c3deb330e438c0fdd9d5c7c348e121316c38b9889a25220d26a4beadbf8e06fe6209e02299cc9c73393bfa00307ecbf03a43d8c14b74432ffa717db7da2c32724340688ef6bbfe60ec69b3adfe2e5d69bb45729c9f39ccf4e25bf9a9dc80af1fbc59f3608c607ceb675f6ac8d8b43ba31ce7f78da7824e3a4a31167c367d513e1eada07ea40ec166880bc25524051d7a27f46750b45bd72c7cc9a3787254df8ad532509d57eb5421cacceaadd61e676f742e8e47847a6986b0dc70b488376d4b87dcb7e8d1057db1660bc061bc71fef82e2b6534093118c29d2d4814e3606f38e07d0c9c73a19e3ac478822d576d37e987082f4972b212810b40425dd6c00fd83f4e777b74e16897b1f6411f008c59b09f9efa7df636b69a42a7c6e7b6668f6b601689468fdd6c5a24605302b7faba6852391c29525de455d82a14f21c099eaf8608032e9f4fb48f4ae5f6fceda1ffc39006b3bccdd7c49a0c90b5a947e1434458818c0136658020ab40a64d5765053b0ab22164f9254c667afd898b6879daaf27cb3c879b867cf7256a663a548286355d2bf6f7a45be799206606657a4438117b2c520a4f95ea241a9e3c936080b98c8de1d4d64ad1213af7623355b531cb074922bde29684f360a4e74e78b7cd775e75b00367e4d35fc7db5e2d8342fca1aebc86ac117a6d55624bbf714219ea3141719a371d75a0e823e9c56bf12ccdbded6201c742dc9405a74bcd93a8b5d8d7850b4552ca04444a21a31dc1141a203fc0e56ba5e687d642ee08d7f5a4fdf961757016b523e41f2473128ec67b13885184a7fdf6974d1061802b15d3410521354e83cafa9149cd2fd97da881f3454a78b3924337a7f58ed5ae6fc4540e8a7922bf7d121a26366862a6d559fe55cb6e637744fae4cb21959885a264fdc9fa347a14e3009c77fbdeb66768931240568704c975b5aaeda392bcc3f2d403f483c7ade356edb8e1341c8acc80143819141fa065d671425136c7bc989a523b1c09e0e4e7ff30a06564e4bf3d91cb5374e6190b95fc163ea766a639d7d3adb351985076c99313d7507d4189d23bff37e3a6e9751673c2279f08c9d2b2f0c463421f842d320bf0595bbd462ab673cc821bd013145f61b32e23df8205a02d638f8d8619286dfc4a9faf1790a8e00b50ecd9055d3251e27ca338c57249e8eb94ac1de84b71e88cd74769595f917cd06d73aa1114f9749cfb466f82b264e1cfbec7828f9f0b08528cc13deeac29185626ac1a6519e492d6065899f3a2b29717386cb65085c6b5b05114472915eb60bfb76719c398e4002cf62f3bc7b06e015d5f800fe2564a94d0397278b5783cc954b88d5be5c70c0664e2feff56b44d261d26619f8e294abb2807d64b86832e378613dd9385e4900ca32bc83bfbe1dbd4a807f5b8077c5e58c1e31de2f35e4f5a3dda032d95c071e1f5f52e26f65765a460789298645075f2f858a872702a04ca15341a1873da9b7654c9d2efd0b0a6df9dc23539e957719a1f651b12f72f59d0fe5af6c27a27c7d4e4985c1a1c7d02f49f8ca306c346aea3ec299a18e9bda633ae5795c8eb9628e3ea541a140d5f06ed4a64163972e899f4b9efc54ab3ccd4080f90c2e1b2bf6fe073f8734914218a31f05bdbaa13d68f54a98b2a3cb91815d515dda3703ea20a7ea3a6f861579a96068682f87c3b08d767db179778f8e799dba9df52c54a5b19dce807d168079b169c8db8b717c5188689187f092557c95b22700b8c3cfde1a08fa60cd307d12f08c09c83edd7d80009831d4fa3c41aa2593477ba484c3f0c45c40e7cc0b185b012423e41de96e543a3c8dfbed652437113e5c8a891a6b814c64c744aa9df3a2cc29f1c4040240958b28aa7053f47c49ffe489af1472b52e4fd7da04e1c81c931072d8eb1ac8f22e0f15af8cbf8c95ba64f4ee3d76049f0485d127613b892e2bc60a6f811a9a55d08c834e443afd76a28057aa9cc8ff64c97480bb7e80394ccb96d016b8f2e58fadb1d5c2b13a78ae9bd019582dd24e107462861d15cc3ffbf03010169c1fb8bcbb09a8755fb1041b0e90766740a3d0991347d746311871a2b5dc9699a7151d26455636c69a945b00430a37cc339c32c21a9bb5c8f6118fcc81a50725aecc1ecb2c49750e0ca540c2dcc9fc878039db160bc1026894c7b6b8478a3a693f7ba2aa27991160d80f58629b27966fabb9dc46df14a18bbceae3d169097211c2a382fdff2cfee545a963052586458b2f9e3d618a96565b67f412b299064964090a94ea7a13dbf60be311fd0088b450781df424f94d2f75016f2ff20b14aafb7767fd4aa663b557b1e8628cce77685db122a1d8ff87f2cfec723709800f7a34cef06695dce216760d2fb0ac4169a6b6c6241a880c2b001f4872db3f7fcb1911235e3f827a5ea049b6b65adad6536bf690f9ad721f4a474eb600f1d90b7c641da8a33bff23b293754286c3ae70f55c8fa6d530491c445d764dcf259d19e6293c89ee3e6f2a10e64e7a9add214edef764ffe5cf08ecd334b07d92bab96b3d3265b608fc8ca4813eb3e10b564531e545d75e6cd0485bc56792ddef8fd11404f072fa1c362ece47a5297b4f405f671b3173ca8572601707265fb2762775f6b2900dc90c1bbe6123c82a56a435bdd82c4dc5088dce84aed754c3c217fb6cff25ac996e628c495f36da2a62679466034a70348592d27e960b2bf9014edc0558f8692d62b463376327326223e0ee810864992a4940a594e4b3bc1b58d54b43bcb44191d18e17489722db8ddfd50795a473b9568727fd43084c85028440e585a0e0c3ec102a4e7efea3988c88b477945a632701e5a8080474a36e11f67cd28f44410c15818b65defbf2070eb0c67ec16d323f570fd7ffb2d1b12efb1aba95cd515ef59384b8a28b129548936182adc94ae2a4720887a10fd37bc770cc52ae0c22c77feced7c92169e918b022638efd3dc4cd0230f2cdf50c9830f9b3a0b1bc1056e40df3fe8519faa8d163304f238033ff2391047a523277f638b5cf20d5c7d4712958d41ab37f51ec0372cd21b7a9694969457524aa86b5d00c197154639fd70765c33d70790d1ab95f070a36554018b9e8399cba016eeceba839a563593888752c9a235dd2a1fa7c0a9c8fe69d34ccb7105baafc5b31096c697ddfc0ec713aa4e"}], 0x1)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_linger(r1, 0xffff, 0x1021, &(0x7f0000000040)={0x5}, 0x8)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x1005, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{0x0}], 0x1, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, r2})
r3 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r3, 0x80047476, &(0x7f0000000040))
r4 = fcntl$getown(r3, 0x5)
fcntl$setown(r0, 0x6, r4)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0xad)
write(r1, &(0x7f0000000080)="e5", 0x1)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0x1136, 0x0)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x200, 0x4)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x2011, r1, 0x2)
ftruncate(r0, 0xfffffffffffffffa)
r2 = msgget$private(0x0, 0x90)
msgctl$IPC_SET(r2, 0x1, &(0x7f0000000000)={{0x9, 0x0, 0x0, 0x0, 0x0, 0x140, 0x1}, 0x4, 0xe83a, 0xffffffffffffffff, 0x0, 0x2, 0x93c0272000000000, 0x100000000006, 0x3})
symlinkat(&(0x7f0000000040)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00')
readlinkat(0xffffffffffffff9c, &(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0x44}, {0x48}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
mknod(&(0x7f0000000140)='./file0\x00', 0x2000, 0x6450)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x3f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206913, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000000), 0x3)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pipe2(&(0x7f0000000500), 0x0)
ioctl$VNDIOCGET(0xffffffffffffffff, 0xc4104603, &(0x7f0000000540)={'./file0\x00'})
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140))
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x833eee594ef524ee, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
ioctl$TIOCDRAIN(r0, 0x2000745e)
execve(0x0, 0x0, 0x0)
pipe2(&(0x7f0000000080), 0x0)
accept$unix(0xffffffffffffffff, &(0x7f0000000040)=@abs, &(0x7f0000000100)=0x8)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x8}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000080)='./file0/file0\x00', 0x0)
rename(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$setstatus(r0, 0x4, 0x0)
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x7, 0x10000000002})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="8202873f27"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1021, &(0x7f0000000080)=0x5, 0x4)
sysctl$hw(&(0x7f0000000040)={0x6, 0x18}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
open(&(0x7f0000000440)='./file0\x00', 0x0, 0x0)
chmod(&(0x7f0000000080)='./file1\x00', 0x0)
open$dir(&(0x7f0000000100)='./file2\x00', 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8278892ccb44425f846ac24b79057cbf079668334e99f13b3865e3f27d5e5a116e15cd349ef85cc3a94a5673c57a1c4646fc8fb34452c0ec33e50000000000000000000000000000000000000061"], 0x10)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000040), 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
r1 = dup(r0)
read(r1, &(0x7f0000000040)=""/32, 0x20)
syz_extract_tcp_res(0x0, 0x0, 0x0)
syz_emit_ethernet(0xdc, &(0x7f0000000180)={@random="ee2b6ea85ce3", @local, [], {@generic={0x3c0a, "e5c5a9370c8ba50c9cbdbc1984e2f9bcf4c17cc14e1f62c647f6b4f698bd2fa48dcfaa84eb64468fdeac497bb70afb2ed3525a9ee9c52fda7f879eebed44f1a99760fcf6b0780afeee00dc63efcfe7ee722e0d1cf907471f61637d9b13626c7b0ed689251f0eeff2dd7f493ab6e8b8f2a84389e142c2657d20f7e38f3067ad5f2685a45a0b8fb2d64b7f0ac4dcb2ab61fa4f5ab1854de3f63bb0f6feafcac6ae0aacfb520cb829a4fecf5e26db1283c49adfaa59beafdb291f50d72b8cb714f355ccf82f7729eb0127daf31b4ac7"}}})
r2 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
writev(r2, &(0x7f0000000b80)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r2, &(0x7f0000000000)="e6f509", 0x3)
write(r2, &(0x7f0000000180)="09200909f1ffffffffff13804752cdce9c904451473f0300000029157cb4b0ff03000000000000705abf281e73d9b6338a02bf0a", 0x34)
r3 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
write(r3, &(0x7f0000000180), 0x0)
open(0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000680)=[{&(0x7f0000000800)="1cef7169a51e177fcb3d9126cdfcdde865488b104ba6c668297006725191953df365cfb46c817b0a39ac25d26136088860cdd18a6787e11ed1693bb0e8f935c3ac2ee35e21fe6ae3e48bba98c1cdbb1ba783aa51aa0a8f419f17cfac9699b93c9f5be6a8fa55cf782dac34f795d1ca36", 0x70}], 0x1)
writev(r0, &(0x7f0000000140)=[{&(0x7f0000000080)="b7bc923e77030c433daf280c2c27934137b9e366b725cbe81384bc31e66a3426bd8bc24242c4722cee8d05e1a94036643f49d8c3feadd0c94d81715d2794e817532abcc6ecb10de74716618bd63a2c84132e133cd456787019d163a82102b126b4309fd36631eb0c32bdcc813e6e2e", 0x6f}], 0x1)
execve(0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x3, 0x0, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="01"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0xe, &(0x7f0000000000)="02000000", 0x4)
getsockopt(r0, 0x29, 0xe, 0x0, 0x0)
sysctl$vm(&(0x7f0000000240)={0x6, 0x2}, 0x2, &(0x7f0000000280), 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x1)
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0x0, 0x0)
sysctl$vfs_nfs(&(0x7f0000001340)={0x7, 0x2, 0x1}, 0x3, &(0x7f0000001440), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000001340)={0x3, &(0x7f0000000080)=[{0x1c}, {0x5c}, {0x4000006, 0x0, 0x0, 0x10000000}]})
pwrite(r0, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000080)=0x9e, 0x63)
bind(r0, &(0x7f0000000000)=@in6={0x18, 0x3}, 0xc)
r0 = socket(0x18, 0x3, 0x0)
getsockopt$sock_linger(r0, 0xffff, 0x10, 0x0, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000000)={0x2, 0x12}, 0x2, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0xfffffffe, 0x0, 0x8009, 0xffffff03, "1f000000007e00"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d4c8b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d20aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c7e", 0xcd)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
chmod(&(0x7f0000000100)='./file1\x00', 0x13)
setuid(0xffffffffffffffff)
faccessat(0xffffffffffffff9c, &(0x7f0000000140)='./file1\x00', 0x2, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x12}, 0x2, &(0x7f0000000040)="e74f34b1c63436d360a0a33178fce31dfaa68383c1157df02607e7f6ce592f2a704e491cc0", &(0x7f0000000100)=0x25, 0x0, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000100)={@local, @random, [], {@ipv6={0x86dd, {0x0, 0x6, "50a8b5", 0x8, 0x2b, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x3c, 0x0, 0x1f}]}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
sendmsg(r0, &(0x7f0000002740)={&(0x7f00000001c0)=@in6={0x18, 0x0}, 0xc, &(0x7f0000002340)=[{&(0x7f0000000200)="816c75bd73a600ce0b69567131bcacd78e3df4b0c0ca7b2b271b1dfc9d8352cf6f0a46eb5f2f2b4350dd17889d795d788cff2fe279294f2cc1d1da5823987d4be3f86034b71f2b3876ee7bc3dc584bb58a0747dd53181aa59642cd122277c3b4b61667735c80a113c77f294f57dd125e21daa9a8908f3b816886632a1ffbd4e37076ece30c0100a76636b62d4ef1a85deacfb04db6ee71f4e67c163fee342afb9d9a4cd00eb4b0f23622804fe845d29015ef7a9280be3fdd42c0941ddd6e9bfc497ca87fcdcf41e1aa73b892500dfb05316c20d1d84d35defd0ba2da2146051a1fcaa017ccbbe39932d4ff545885486601dfba4be4e2b930a1f841a733a6b8cfe8e27ccbb76f6e0075db870095f8df02b72b8d1df1603e7f56c11406e00dac825916e43c78f7db2d4b8aee0582fdf538accf77e79404a32907f0672c155ce97f6860ab5b1489926356009ed4ea1efae6c9f105b9ccd9907e7101ed979cf16ad090998b9eccd0fc3524b03be4c4778646a56d0e753552e7ee495c6c77127636aebb5073737940003b20cfbf65b16c35d9b9b0ab289b0e6830980c0092aa838d30acec34d335517690f135611ea7ae60ec745321b0ed9f464a2b26b029e8f873f23dae8dc39d13f6c0d0f8fdad0ac0ff2851e0f9741baf5502cab86cc6503d2c4828065f35592874e8d0eeb668f0a49d6d65a0b4c34b0e3a9c9633e55424d65d52a2588a333cc9cb261739caac461f69c6e92ff017b30fa1c288352ca6de95cd31c1965812b6e1d6b077a8a761c0e7615fcdc04e91a2b22458d17aebfbc7a35bd19b001b76baf42a7a1e0c8a24902f25d54de5f3fed93af58e15f82fba604db67b533faf785fcca79ce8c8721e5349c8f90d854a4c6847fc245042acd9681646f550ec586da5619638cc61e50af070efc3adea33ed9dc86dc20cfa28e2fe2fb818d119e46347c646df1f13d0dcebf5381737f4c987eb4ff70cd3bf4883956a621603dbc77d318eb20eca55760807fa5794cde7571e53d8955e4634231ca9dfa46f9e3ec405761b57bb3981e5bab7f89a1718d8531623ca1c5b64e62ea59c1ba03b07b171c278fc0070bf1683ce99f76772421ceb0bf17ba70ee7ac57a81ae11bb24392afbee7f4f14c3641c87db4da9bff9de268315fecbfd4bc1982c9a8c50281296ed81b4d854b572cd1ce4380100a5cb0554c00587324cb7360b3beca49ff0eb8c9b1ab6d483b6b699df8c4b33f506ea7e05ca0d99e4bfe5efcce532b00cc2210f2c1c243437448af3f472fd65d4ef3c5c7d4ed72488a9d28338dfb66daef5823594647762f2bc3c2aacf1301f816820ad7d3b03cd845b8e10caa6dcd56c6c8b1c3929558e8851bbd0ea30cfa1cb108841151fdd0197dfc6b37cc887f3ea812e5405ad05e5313b62e0699d589deb85d1f4a675f14df4cefc839148f3d442947034cac89679a5b0d91c605d554fd08148e28625e61bf472c976f406cc70deaa421cfe0881ee83c7807e459616819096d933d06ca6e3cf7bc04a20818bae123c305fc17c159d4d490c27382736e46326722de3223298721e28a1225f31ade62acb53b8889c9249a3c043d0cfade3410e83da9e064ba0f0bc4a19643d65a80701104fff21281af4ec3a6065e8f3a8d5f99e25f846475689b46c29a3b0fd2312feaf4e03e8ee19cc3a9aab3571e2a8cb03c3e17571f826bbe40bbf1642f60b77469c1b94f327de4742d80542afeb69650d44dfc0ffadd540dd65d6ae70b23411102f4dc7ef65e6751495d5141bdf86e0156fc182ef798cc1620b057e9970542f78a7703adaf265bbe6b029915ff85765c603bccd398d95ea72ecdd94a9b40b7140bf055a693ed14cd11810e7848671ea2b78b78ee3461b44683ac2a0103410d88a8ab57427ce6c5f8313dea5dbf1155f83b9c59123b8c762f3977f0181e8e0e813a1993a1ab202af7e0f80ee0beeb8151a4c9a1467a05713382e85ce626dcfae7547bed67c859ac5b41066913f7b1f57f0ef36d3152ff41830bdc28cf06543c0f8908bb7ad1e9c1c0c81791debc1fdc8ee04040e09701066736095e722267299a6806efc2c568bdd728deb4b6186fb0251c491e5a29d1c8e2d22a4fa756ff83b861c70fbf533e529cbc15a59abe697234d7cac8caf13fe804797cea4ca897e8c624af425fa427c390d8cb806aea901bc402614ab4ea876b14a078ad07e3564c3e0e24ce622c815e7a643d70e644b7f1bf981c415277ac6a1038494e97f786103087f8e5494d88756a758945ff8be2f96ba7f313b7575b32b5c16a517a9b043a9cfb8749e27ad5e784c0ff7cc66d1c96230f637bf1036a67568f50397aa8ccd5267374a929656241e93a6642bd4e3fe6677a9181b7d604516860e40e82ced9d4b9850a0a2e8a26d3d2467e5f9c3e4d87a431673adfa4580aa34adb3c84f89a8289431bc9c9aabc9c426c74155950567812d811e7537dd5738278be218b1198e0c22a1b3c31dcd8208b89d889dee1e45e1d9bc04b7334bcda985f5651b46cef985c29b70857ab8d0292af2dd40877354725053c0662e110e5044cca30afcba1058486bcb17d31cefc9994f4a3acbe79a6208c0a34c840808e34d8cf247c0986c9f3b4759b38b0defeb36d898a0b8f6688662da026dde1199e951bcc72dd04cdda83e2aba0c2ddb31a9eb85b80510011619bd8c85be1bbd97c1551994a17062430a113650a0fde173779d89dc882e1c66e8cb1ec62dd8b1aa9c495656845ff5cc88f13d2d4fe9d9bfd82a54b41ce9323d10f55de1d45bb01550d4936967e2a4ccec2bdc509e5ffce50eb9c59bcfb6f7aeb2752e56143cf1bef1e2ac85a3c354a3dcc8dc579afad2930b588e2d1193c0618eeb5974c8e301e5c2b68b2a890331ef3e725563b1c5df57ca3aff0340dd3497d789c6998a059b2f4389c0e9ff770a1d0b02bf4feccca9fe70de20080481e7bc721cea516da4efab1cfc4e49cfaab413207e89cca8707921d4a4a7d69e2e57bbba0bd7d3f8c7799e3773058d633a3782cad1570ffca04a5aa76619b365c6e2092dce42d5f8b4949c7bb16666263a74c5070bfa62b87631953e153c27f8ecff8e4c8be54b881bc492870c22734c008f1f7f6859735af6600841e4ddb65f328f8c7708226892b5ad051bfecf7c5dcd2cebfcd979ec0981ba286160dc5fc049ac170de2639b70c506724df3cd6d364c844a05e9bfc0ec7a610042a856c86a97c1cb2b85e0f7d4fb4fe92919997659ff210bf9df9c0c6d7b404f0a2a9c84ee6ddbbc2254f6035df756799c77eec742da856d2c1946533425cf5f460adb167f04ee8b4a9c113487ca13e2260cb048c91e0ff3ef23d0ad6f3b26e863ba84dc37dde0a5ecdc83410fe44e22a7da19b31d6d27ed7e457c6d4eb1a792c92a405604e69ea9d31e91cd3e1dbe9aaf640914b01fe73a4199895aa915946e25bafba1220dd4e6ddc7bf36f6b08f14e2bb0773e891598ec983d598c695fbafd95fa4d32c42691572596ac7416c90495a0ebc0b869c1027caa1b22e866df756e091a6d48c5ee34699895f2807c7197b8d7af57dd0328ca6383433049962487e0fd7d0458b42a36d1524afeda99e150d4998fedb2850544abc58a320e1bebb28037dcdb65f9c3c31f157c160681d90a1139d940467933ab7e7fea594634adc55335203ae8e4a7937d25a6f6a9025e4c131f2908ee9d3a8cb5416f3bc2f5a6d2f3aa439d1ec898f9edf38a8a9806120e99efed51ddf79bd2f2842303506df4c085da6dd6a9051f50f7744ef0672d9d0eb2c8a044fcd0ef3a9ac826e8e89e81c2f78df7c1d96a7dc69573beaf70f88b0b16a6404bb4be610f565579457b76fe263c98a91a5a2404d038aa157ce6040243322dedd181d63c533e6d95967778bc737af63c211d278a53df61d77bad32afa630856f7dd0f0123ea4293ec17ae396c5790b992fe6acfc0c9321d8fcb205df4cf8a224b2c561a5222d57e980ae409a666f2274f5e1db7ecdb42deb135e69eb3d716d2fa2b838c78291b6b43cae21245f201fffd644e03aa38aa4b8a266969cd2a58f0bf39eb02948f21921b5bd50136276988cafb16ab9ed441351ec8b77f438638d42a83d10d95896e6b450614d0a02b142b73b37bbb25ef842e458e2c5d58d8803c7e203515cf1128bda8233c322926f5f7a08344bb9a3e35cdb6e6589dca322c3928353ba69bcb8d27e94a322661146bef403c992c9c0dc1e8a8943bf5828a3de33dbf02da3dcb59070e967007d2fd5d7334e1feac0d985288a68775262666abb8887231b9b2e6d77f6810c07789202e03f829dad3e7f1b1f1cad2d82d5e4f02983d8b9fb44e8a4d07b656511c0dce2f3f05776ebf2fd47c3035d9778442f065ae32d5ea2defa77cf50d47a3dde5a468cda7768a66c311a26a934b920b9dd36d077ddd96dab9e27ced501388a1e7cd0229463a8c9e7fb9a3d18fb4f9ea0e7fc06863f35b7ae64b2cf466007762974704068a7a100d4f96d12fdbad23b581c3c8a83fdb56b6631dde29b2b8d908b28c3a93dafc6abe5d916b9818384910f36b1996768a56fd76fe552989e640b5ccf4e6661b04cebb356af425c179bb5310000330fd2fa6565bb44ff21daf63880de0310b4dce10f7d9a510380d816050f2ff7761b301f0e245ba2ab107306f91459f4617d62f1a1e230a22b27677998138e39422bf341ffbc94d5946497242ba9127c9a43defcbecc7c92ba4d62696854f20ad0c4712db33e316bdd597ad653ecbf26427c0055e8f6bd5b4e4bfd062ae30ee0b609ab687ae4115a59e823184fe532e8d0005d8ad5793ebfffca870718d7171ff4d459c54eeb145283bdc63ce904100a71fb671ab38462919ee2a4c9f3b39be29fe220e0698365b0224facdbf79a793c3c61c7def00ff0d6968501c1296230b8ce9ddc5ff0f4d8f2386e3fd498f486cf3e4819d25e5c1b82373f23165843bf5d900e9ec025444a3c6493be64a6b7e342b76f478dadc35dfd295804a0ccc608e1caf3b72e3d018f262b85b49b39977f8f717f507bd9b207b87e1975fde29c371c86e5d01c5b5d9a0320c0ca89e0a9594d2d46f5860ac098b25f1da8577d544cf0b075330ba09b810d03a3b562e7972aca3e1fe35bee8dc459f545d1cc38ba567aafca9f1cf017633552cf58057fbce5ef1f39a758a6faff31770715a6ee33d65c6ca9b959cebe1e0f3b642f99c8f4ba0a0f4c4b45b74c33d756e1553f80190b6aed9f5a09f8adfc32e1735de1a74dfa27bd174bc74d9a9e8c595709cc95d4d149b3193a34898e36bfab98bd8ce0a632534c3245a546eb8e150b34bedd7480f18c557d7ed181c20feac89747993fccb400c537af9f951eb25d3a1e66fabdc3f46ec6b525b2f4dea1409954e6942cf589b21f7ab96313171848a8af0a45ab6f4918f7d924bf5248985a501d7d8159b2eb9d7d3631bcd0b10ddd60375c7fbbe0aa8d5dff405bebc60498d43a14e0d75b575da48d5743883aec858ddbbaba72178ea1e66e3ef28049306f69684ccae192aeb8f811bb41fe20ba8f5cb949f0180ef5281b137ba6153ad094950e2b405000af709f4aa677264b50d95c9dce98eebbff8bce817aff84e8b71031d1b7a82b40404de6eef44300b5084a7edf7987c5b62db131687fc5ac5ef4f7747d7058b1c7bda24911a410aec74d358933a0d564f7c62727a6f894915a3d12d3d270a7e2858a6288bd983156e454adcd6d603a4ddbaf54e2e6181823faced4ade6fa5e9436ff31cf8d9315c1e5b437c59a150ff65cad584ffbdaa957eb0a50b7e7", 0x1000}, {&(0x7f0000001200)="313f876111ac821c6ecbcf997333d6c5180f7bac1cf756001bee71701f9c03379e2362fda48dee1e48c32df0d8dbe1a6e03d22505cbb94441861", 0x3a}, {&(0x7f0000001240)="77f352f1a6002036ea17a58802ae89df652f6719af359dc82e169f9824da5026432a99725c236e109a8567aeed30b13e66a9949df060e6d3554612a758547c1100b52f19853daab1dd688c4842ad56029df06381bbef18860ae45b63eb5b1ad6e5f943aba54debd8c126c0ac8f6bff89245e8c8723294b036b72cfcdd6899ab9cd68ca1e01d67473ebafe0c43294f42811f1c1edef7802e2c790df521635202b4f088848036c6c2dae65b3c287272bb6b28f326580b34b0ac21da3f8a460cf8227f6b629affa8b95a12195e601158475c5f8222bbe9405dfe22d452b599467e00d73a27918d5b35bbc8c74b3792890d71959c62c0de917225781ccbf885c81d66c07d630925c9d0abfcfab5c6109c8158722639b099eda45f53d603de252cb0760bfe253b72c6cce85ffc8ad768412aca6fe0a44ffb922af6b864b6b46c16ac61e3d04ecfb95ed3d17de80fe2ce63037cc72ccbebb6bace9a45db2bcb7ac15a2cd7f821239b301d3c8080515f3471464c6fd0739d3c161667fd6619040257a2aca745f3cf8c374f17a91be20a633276851bb23caaad057ee72709897d56365acd13c31013bd666f626c746e89d94159ab214bfb6fa34d5ebf35ac3a8b9fba59c0e6357f008cac5ef1b70e4f12e2d9470588bee7d50d7a08329327bd3f9d5e7f51da77dad8e47529d10381299d92701c71b5b4e52cdfbbccb74df874387260e8a35ad90c65ddc77ab37196681f74ef797e663648388e15777b417bab5ac18f14bb8c254a2236aaf86c812ab2b0d7cc5ffdd25d7f1ce63496325851bb61ee7a3afafde9d2ea6fd5db09d8acf0f99369946a7c51d410e4bf1a8d25bf7e1173728a72b1e6473bedba1960149e5b46b418b7bd135993147d77b2fea67a129f0436e578e6070635c42d1fcece6844ec0f263e1d35e11509cdf97c9378c3816742345281e80d5a80754823456feb8db442b66039c34beecf1535c71da812b3143b86bfa94f6c06504e50d9612b3065313b985c9fa9aeb7a1a53cd3061ea3ebb7ac8e661d823aaf053c54a8c3209f7ae67edfef8e6de956d26902a192ef08b28cffa5250d0a3a351bf39296a3128a10e6bb3dcc1196813107aa8e639c8da110ae2c7d11f8e9d8c147d1376f89849fdbccb318eb644dbee1337294992b40d3d05c0745cd372809ff0c782bf05ef9a3894dfafbebd09760aaa18be1fb5316a49576c1b0d064e7df5af9eab56b1777eb858ebdf4db0ca8eba6d11982197d36187c7073748ec3ab488505eb3a5002eb31417ae03f0155699a4701d7ff23d823852ca102bb8821ed84a0ce5891daea8b88b65fca45a119bea3f9014b43d269109bb70875ace33821f47f89ff2272479e0971e79571c31f89b309426654b8c6c734446798ee34f3d5ad7e43e8e4447c1200ae4efa231371dfbed54c0aab3bdfccba1cc11cc99d03517e0d0438d8650e074d828bde49504658f599d8694251cc4f5d05b0a104feb2571581bda3f9701aaadcc3f38763729aa7c1addeb1e969f2b141df692b226ab5ec2897c5a89c75acac6a7162cc13c782ec0d26cec834e34e577ddc0baee39f36a993c67878f6957ce98ec41a664b2f469b2017e91f5230bc832346470dc2b06721f31ded7c11fe9a97f8d9444cd36251212a657b4a717cb86496933fe52b29ff3f4c84a6d7a2f8fc75147eb01aa7260483c1a5958d731ef0a9f9aa39d254c8630ff5efe6cb64a3f6f77be27b112d6a0691c508ecf1b22128c1b0a8861e8afab9168ff8cd6f7623773f1c20538dc6fb7ad4faa73c3d6587cd4fa367f0834ca4fb8f2a4f5a842df0abcb4a6e6f878d03605f304d1249ffe61d20ca61be128df29fb25f45d82942a37d0cb48e4148fd94a8610eb736fa76ff9ece86946081fd70f4d546c6da236aac9a922c284e390ab0c8af330af0c85a3f54a56ef77117e120c1bd21291a615ee7f5c8785487497af535c7eaab6106dc93231e0a3ce0877834091f5c67f9107eebb55c6eeff96ff9270e1603369072153d3f6e7844d09a1a0a8a5b1036dda73ad0665bfde49e2bd0fbb16fd9ee0088c3061fa11a9b4060fb0c0de3eebb805af88a86ec2f88582ebd51614ea6c6f978d4e1ea0f63fc55ef9ff1154f16caf42741c50c5a133b98b51fd054535d81c0cc935e46fc5bcd231f15f8e90f85c8ffce0850134606033236345653af883b9a9e668711477252d41c6ab52554ceba4c44bc47742692a1f72fe5ad7c52bb91ac3ce2197ab1512c173f8de17a45df52a01b4ef3019f6cd58e93956f50b3aa92c1855f3a7954aefd4517d60e0246ac4733c93d5846a227ead5ca683789c756f9926cc32b2742de38f59c382d4c4b59a85f6a76f73dffe1fdb443fa12ebdabd3b29d28a619cf72bc80b2bcaaf0326adc8ef3ce7b1d9a9efdb00e4519cf69781ef0f128a7937d437f98371cb17a555265267072e3fef9e6524f19b9031a73dfbc7cae28ba51dba2276961aef1473646c84430b676f79d2d8b8393941f1223fa4920575940a39a2fd4310b3ef1f0f90d8045973c59fd36b7b1bd7da351a7caaa51b4672dd08fa0ce03fd5870e9766498f6ef6ff425d8e8d29fd6401767146fa6dbbac0e65e50934cf28c0721a9eb3fc4b89012260694896e1a4b9ee3b08c674ed0b8005a30c42a5075d58cb3890fe8b030516ef860af5c3c31e74c21b365d4293ce8133af5a790103d60d53bcf7e9d90d3f22d5004037ebd11f81f330e09e5b0280a2d65a15b5c484528fa1c4eee83291975e948f1503c92f7a0115dc935e4621ae91396c3c67389160f2f215235a17c0ac506dfa8041a038cf32fe76f0df9ff09759439a415de9cb55bc69cfa950f3a5e4e849ce30f17c9a2bd68f41ed2a869b0d065838a6dde227b9424333dc4c3276823940560a5637978b7ef1bfa07ee98ee89d2c79d530ee06cb7cd2e642fe5b6635763efd63e51985486e92d87fe60d1088e7cbd13c6ce2898fdcf4028bb06db551d2fdd3723f1c6057ede0527ef2118e1c459480b765a45404b3cfd714085c3decd98865b65248b849938d218c4076b69da38d7697427816a72aef87409b2c0ce0134d7b23711bb30b05e301394aa57ab97c14fe8af18907f84f0fd4ab266dada2abe1c96bb5229a02e6151125a292c5632cbdcf0879a2b66481e713deea0a8d6567de81a2de9f23672b5b399eeaa1116309138921c31fe92e5b7b2bb33c29485ff1d03ad8413fe77fa9a68cbb740e2ecfc762bb6b381d34a547a13e5513d85f7a39ee1a1250cdcd389da3d02a73de217594c3c98dee9e311b0d3a0964f5a54be5ffbb6c6963a612f8570d048d70fa233117f1e41b1e17801b534da75bd4429153944f3b5b1f35aab2b92ef524cb650fcad777080d252c6f811bcd0f620fda15e7900b245412e0b03beef8ea53cebd77846fa6915fc332888dc0a21487d5dfe5ad9cf5eaf5246c657aa89160de3af8d7b8ee792284584034838d2081b535962e8bebdc7e3711d32db1d67a18ab2013793db84eee1c10bc8ff2de1eb61fa0a5a7ec52a2b8c4cc3cb13b36ad1d71218dd90e9917390ab3e27520cae8432377f7e044481953c10da42f6b118c5bba1e4eb9eb264bf5f0acc58267216e9564ee05a58d545687bf34578745bb24448c9b4576f0b3a62a815af22eb02ab2c73a32447a81d39f440b3e175d7419743ad8f4f64ca5299a0cba532efbcf6226a6658116ef0b7ae632d587b84366aaa52ad1d27f5f97bbdcf6194d36eeed05ed0b416731e7490b2d716e34bca7ee72db6d8e5b9eb2a30409d0654219ac3d0ec53be8af24f2edc35d1a7679487ae720a911fa94e658cd796e2247e7adfc427ebb2d214bf05abab6b1eb5190c8a81263dfaf73c6b3a264207c93d3eb75e7e181bd9e20f75e0042c61df53054c8f623800fab069657434b79c93a0b4ce2d3ec2acdedb8ad2e6dec5c044aba1666ed7b13dc118dc8e2b15c593694554a159c7e7af6378227bbf155ccffb7ab1346d8688c42843807b368c23b235ef161facc192cdf033e9728aaeecf355909108e694a1b6441b1db53a773a56cb1172b1745a8ef452777024d463e7564624dfa3aae9575fed22a63bfe6b67e26516af2cc6d8b17d90a4eaa21e98985c5ef46d70a6f8c90e6177f1f98b78c1cc4bb6ffc65c7a5287c3053239784a26e6073440b5511f34a1342bce2d2560adfa768389e0963de24c804e64d2639ed1cb4c9318d8260a57163dfe8865bbb3221becf008e0049c3ef6331bf4decab326f44c0a6b4aba1c6ea7c015cde7ee26ff9577db872dd56c6c5a48770d67c04d03154ce6e67b25d8c7d8b9c28a174d75c558e98a2fa5aea6aab95814713e77e9d38d77db63877d772f883a15795a0e9a994bb0bc857a339a687425234ddafafcb64f3f82b3e2325f2b64a5024bc60374405f8400cfb85bf24225e87c9a9bb97ef9d092dcebb5a8267354aff45b5bc286c8a7b551f137ca1d0040b53c020193d3b0d05d585b28a0218bf5c65958421129d2fb0574a48f5ca9a4f581059c38a1a9b1b50dfe97749d9e11953553559603cae4c7b2ac67307248429e078c3c3c1edd0f1028cfff0d1601e835f99e7559ccde5ca12ce06787c7f66927ca5372a7d94c65fa31d03cd25fecc9f4cf18d4e008faf4c4fba02c060d8a09c1654b081d15ca5bd13dde700fdc4777fd7af2c573f498e3e639ca61c6404dc379abcd8190c2f8e1e40e0396ba27d5e4a4b1957fac64b9f09ccbb592c2069692086b4a8b595e3bfc799fb50815dc6c9a62248e88c20fb1984aeff450e020f50b093f2fd432a4c835840b26673d900c2f033ec40bc5b17e126315e77d457c8fd6d6cdd26f1a701e55c32167855ebe53e1298336b8398db786e6c0747708e933ef7b28afcbe3f0fe1d278b967d8a24fd5fe9109fbad6b0dc007dc77fb00ccf2c92640da57729faeb212beb9150111ac9a72fc1aec83aa785bf7427d7fbc5e6c77e78dae44094f2643bba9699510d809005cb9fbc4dfe209a19b0b03096994db1738dfa01524c2b2fe1fcf83b567e26412b3ded33782e7dc3c5d255c6be074cbf93f84cdec52912c663307a52a387d09764007833333d43e513cdd516e39b5666cc17bfa67349608bae3da737004cde743d531d3931797f5ed0745526e3dd8248c6e3da32f6480f13c6e530d66f75b8e7cec5ccca4622a76a201e0f3acb16e43bc68817b00282af850b893e796ffbbcab1810eb462c7eb50a6a0580a7e2b9f927c7074cd111b2364406c5687356aa681498957d7375cf57315ea1285a4fd87c66234765ab3a7355a9ac9359b33558324281425968013aafc292d77cddc91a2f1a095d6cc4af5b0e58561dcd83009ce771bc44d2322652bec460412f3a29aeb7248686d03bf5efa7d15027af48a534375f47a9e04f9855170ff694a85f6cc8c768e032ca2970960b8dd3eda254d3d40834f48d96c13891086e688f10e1d96a7e3abe2f54ebdb0c8aa9e0459d11e94287205a9b3094867c44554fa9ddd43d0dd7e836a29f25564644db300d52c635bf25fa4641d6a0a5ebe0f3f900b0929cb0c37c6179934d5b091a884e2f21a5f5f3ebc9e0781e975d663ba021d63fd13b75914fb4845a77a08bb9b28409bb88bdbe259489ec95d5932063ce8ef6bab85476a0741ed8c451a519978cb569c78f98dfe5ac6a610c7170ecf2670cc27ad7f8b1e808194e14c88372580586b43f013b886e11448d9425fd3d13953e1691448be9cfd7ece5f39e151751dbecebd7acc44dab170e96258b11a5cba84e18a75f557561cac4544f5879", 0x1000}, {&(0x7f0000002240)="3ae225f87186916f98a1e19264171ccb01fbc59d1aba9c87acce71d7562af64429fe2961ef5afb83055eeba2e9fba7a459917634aa36f4fbad", 0x39}, {&(0x7f0000002280)="e0b1ab265148e53352634e032623d61853d30909ae85d310cd827fb8f2921f9bc9b7b24423745253196c47277dda", 0x2e}], 0x5, &(0x7f00000023c0)=[{0x18, 0x0, 0x0, "94"}, {0x28, 0x0, 0x0, "915ea5d73381195ba68ae7eff3fe686c69"}, {0x58, 0x0, 0x0, "60fe664f773ba8cbdcb787b8e20c1dbd50922955d410b6bfef13a0a19bcd046590ee737072e8cf7ec539022b59cb7f01982914ef56855838520fa67f810ad599e0"}, {0x50, 0x0, 0x0, "a48ea535d8f8c1a1bafaa851fb1acb2f36f04d46af5f61ddf750daa7e5d6b774468853582dd2261ad333562cbe98a70657d2c588d31548cf73"}, {0x20, 0x0, 0x0, "26134193bb9d4b4cf5"}, {0xa0, 0x0, 0x0, "394d2d4e04c378fe952d2a645168daf189bfe8cd38fe158ed5d2e7cb6b99869146fb24531a6f752fc093d52f1d765f64051185747c35cc01eb01ccc68f19e0e0c93922ae66f2c53a021010155fde5e8f671d4031bbfe8304f9fe1944a4df066aee1e316be5b24df1cd5c39b67fc48ea090d13021e640c2eb2edc884fb8fb7782b1e528612963ce0108"}, {0xc0, 0x0, 0x0, "7af0db425d05fda0bbbd8d182f61ab865c391b1072682e91caab5a90f3e61ec81931fc83e0f319d88d8963d01783b25dcb2a63d8bceef562825d6442b75fa98323164f18fd944587b91bac644274af837ab9a260ea28df643bc3190af1941556b84c648a78eef0ae9bf92845475b4be7dc3faa9f6b896ee7de442ce04deace7b2a43b9a27db76bd52119069a9593c39d04bd2bf62dd0ace5e049279a42e5fb2d67199d5de26f3b914a"}, {0xf8, 0x0, 0x0, "cc419d403a8494f964b6d0dc3f8debbcfea108e33d2f4b07bda3c3bdd84a5d211623a14379d8d2bfefbea774a0527aeba1aad6c5622e793fae39e3e470723027b7ca76870601974f1b6455136203fdad71468197288a84f82a6df089848a3e548a1c015a906011d5d374c3dc4f20b649acb8774d4379471eee42b243cf07ba7928566836a87125cd3e1332578128f78ddb28e3445089be727a9ba85150a4ec063b2582fd220540507284ab3a0691c0fa7768a7af276c0d175a075f32caea0a79cb2f19928d0aaa48ba37fb79136e449f528ab433a617a0a14417ecf8a976037d27"}], 0x10}, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa66376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37281c18e4fd89720fd300000000000000f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020288a371a3f800040000000000000001000000000000ffff", 0xb1, 0x0, 0x0, 0x0)
recvfrom$unix(r0, &(0x7f00000000c0)=""/153, 0x99, 0x842, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000940)={0x0, 0x0, 0x3ac9, 0x30f, "498098bf56d2bce258189934f1fbfed1c9570fc8"})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a5518098", 0x23}], 0x1)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x8}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffff7e, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x6, 0x10, &(0x7f00000013c0), 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7007, &(0x7f00000000c0)={{}, 0x2})
r1 = openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$PCIOCWRITE(r1, 0xc00c7007, &(0x7f00000000c0))
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086335)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f00000002c0))
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000000))
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r1 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
dup2(r0, r1)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3d, &(0x7f00000000c0), 0x4)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0x3d, &(0x7f00000000c0), 0x4)
semctl$GETZCNT(0xffffffffffffffff, 0x1, 0x7, &(0x7f00000004c0)=""/4096)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0), 0x4)
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
accept$unix(r1, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
accept$inet(0xffffffffffffffff, 0x0, 0x0)
write(r3, &(0x7f0000000040)="04", 0xff9a)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x2}, {0x20}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@empty, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @multicast2}, @udp={{0x0, 0x2, 0x8}}}}}})
sysctl$kern(&(0x7f0000000080)={0x1, 0x52}, 0x2, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x801)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000140)=0x8)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
r0 = socket(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000040)="8914e4ffffff4409b1ef32da00080020000025ce", 0x14)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000080), 0x0)
r0 = semget(0x1, 0x3, 0x0)
semctl$GETPID(r0, 0x3, 0x4, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x4000000000000003, 0x0, 0x0, &(0x7f0000000100)="6480cb82febdd0aabf8ed11fb93747761b83943e4f34793d5951b62791f58ed9b88c156d87a766aec0a179527c2f3d5855eeddf771cf05d5f4f6d0bcb482f6203bdeed8cd5d5e6a1880538efc6656af8f6d8a67c4f44ef51ac7ae7ecb69b96196f53976a53b868a8f64317140ded4e2a4ddef4e90d1342a3b5db12da7ffa9a331897544c79edec3cc4279a8f264e9dace88e6bd1b4283428d0c165380734cb35f780f890dbe622ad28c84933302b0d59c861654311280786fae08b59e560588172b24998586cff7dfa72c724705e7bb52caea695bbb638f83c2defc1c6b664fe18accad765dcc10701a734d31b0fc2586cc3795879", 0xf5)
r0 = syz_open_pts()
poll(&(0x7f0000000000)=[{r0, 0x18c}], 0x1, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000600)=0x200000a9, 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r1 = accept$unix(r0, 0x0, &(0x7f0000000100))
connect$unix(r1, &(0x7f0000000180)=ANY=[@ANYBLOB], 0xa)
r2 = socket(0x18, 0x1, 0x0)
r3 = accept(r0, &(0x7f0000000200)=@un=@file={0x0, ""/263}, &(0x7f0000000140)=0x109)
setsockopt$sock_int(r3, 0xffff, 0x0, &(0x7f0000000340)=0x10000, 0x4)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt(r2, 0x6, 0x10, &(0x7f00000013c0)="6d66b176", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x45}, {0x20}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000001600)={@broadcast, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @random="96c19738a632", "85cb90a81368c97ec51e760c80684115"}}}})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7007, &(0x7f0000000040)={{}, 0x4000000})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107004, &(0x7f0000000040)={{0x4}})
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
pipe(&(0x7f0000000080)={<r1=>0xffffffffffffffff})
poll(&(0x7f00000000c0)=[{r1}, {r0, 0xd1}, {r0}, {r0, 0x89}, {r0, 0x8f}], 0x5, 0x8)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xf8, 0x0, 0x0, 0x0, 0xb8]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0xe972, 0x0, "7e00000000800000000100000000000200006ba3"})
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup2(r2, r1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "9a25b1b2393cdc683c74bf93f27555c68a3b2a10"})
dup2(r1, r0)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
getsockopt(r0, 0x0, 0x6e, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, "d700060000000000005b00000000000000e74de4", 0x0, 0x4010000})
write(r0, &(0x7f0000000040), 0xfffffec2)
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r0 = socket(0x18, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000002680)={&(0x7f0000002300)=@abs={0x0, 0x0, 0x0}, 0x8, 0x0, 0x0, &(0x7f0000002640)=[@cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}], 0x20}, 0x0)
sysctl$kern(&(0x7f0000000200)={0x1, 0x48}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000280)=[{0x1c}, {0x3}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r0, &(0x7f0000000040)="f94c9f64dd0303fb27447669ee2e", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x3d, 0xffdffff6, 0xdf2c, "08010001031a5b00"})
write(r0, &(0x7f0000000240)="54a43e5f2e055dcbfd31c67ac4295bed1a08", 0x12)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c5000000020020124d5acd39862d5b0b20a3c1372e2938000000000202080000a3f80004000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7007, &(0x7f00000001c0)={{0x0, 0x0, 0x9}})
r0 = socket(0x800000018, 0x1, 0x0)
listen(r0, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x40}], 0x1, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x40}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x5}, {0x7c}, {0x6406}]})
syz_emit_ethernet(0x4a, &(0x7f0000000540)={@random="490a6bbf35e9", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "c41fad", 0x14, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @loopback}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x1, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0))
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
socket(0x0, 0x1, 0x1)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x0, 0x2}, {0x6}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0x68a, 0x0, 0x0)
r0 = socket(0x800000018, 0x3, 0x102)
sendmsg$unix(r0, &(0x7f0000001400)={&(0x7f0000000280)=@abs={0x1800, 0x0, 0x1}, 0x8, 0x0}, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xffffff80, 0xffffdf82, "08049cf4b5602800"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be6c0045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dd31c0dc31b3c0e35ca7", 0xf2)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x6, &(0x7f0000000040), 0x0, 0x0, 0x55)
sysctl$kern(&(0x7f0000000200)={0x1, 0x2d}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x25}, {0x3c}, {0x4406}]})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x1, &(0x7f0000000040)=[{0x6416}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @remote={0xac, 0x14, 0x0}}, @icmp=@mask_request}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x1104)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x11}, 0x8, &(0x7f0000000080)="9d786244", &(0x7f0000000180)=0x4, 0x0, 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000000), 0xa)
syz_emit_ethernet(0x42, &(0x7f00000000c0)={@local, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x34, 0x0, 0x7ffc}, @icmp=@echo={0x8, 0x0, 0x0, 0x0, 0x0, "fe74a73b15670c6d074429123d82429313ffae6680409dba"}}}}})
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x1b08, 0x0)
getrusage(0x1, &(0x7f00000000c0))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000140)=[{&(0x7f0000000840)="b9df4dd86c1b6eeb32daa2152df137b725c4c39c7c6f2cb5bc39a9b0996cb51a5cd743f6f5c79fd7cfd67e315011e2a36d17d4ef7f230430753cac809917911e392ae4faa6de6301e159a8ada3b0d5cde34f7157b931b1224c777caa49b5843ca7f56d3d45f50bc9c9c04c18a61ebf3672281ce4c0a2b697aab27667f08c231f50d7c6093be267c5dc98dcd65a5302313f8b4a0d02c479a96b255e88d2b4000045b02a096ec97bcfb9ed31abfea1d5d51e3a5589a9d2bda0be8de6d8d33bcfd608762664fe6c9388366df5b098022577ab34fc8006a0dc34eadbeeeb40209336b2c213c45f78693c2e9ab7145c22167ed09678f9445be7580716cfed2b0efdf655010b21593be911c48b08377a5819e21c14d7c48dc66fe2b8d234baa71fa9894e06c1cbebd13ad95860137349e9bbf651671e42e9c9302b1ad51ac5d50601c809835d117bf09fb059fc2e0a0dfe05dd4e5ec053cecfc6f47ee1cb52b79b86eedc18463274550882a8581cfd1701b401a9d7b3c3d299d023da8864ea9bacc5785c3692504ee2e181", 0x188}], 0x1)
writev(r0, &(0x7f0000000580)=[{&(0x7f0000000200)="e9faaf3e31e0fee4fff420a72c16a16c36", 0x11}, {&(0x7f0000000380)="2f8987e81a4b399d9852bc918b7cff1a56d1de8ae7841f09114aeacc9739581a453777526bac2d412d", 0x29}], 0x2)
execve(0x0, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x6}, 0x4, 0x0, &(0x7f00000000c0), &(0x7f0000000100), 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000440)={0x0, 0x0, 0x6a3, 0x1fc80d8b, "f4ffd25e890000011868180000de00"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
write(r0, &(0x7f0000000140)=' ', 0x1)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000080), 0x14)
pipe2(&(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
read(r0, &(0x7f0000000540)=""/171, 0xab)
write(r1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000100)=[{0x25}, {0x2c}, {0x6, 0x0, 0x0, 0xfffffff7}]})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
setitimer(0x0, &(0x7f0000000000)={{0xffffffff}, {0x0, 0x7}}, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x80}, {0x20}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@empty, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @multicast2}, @udp={{0x0, 0x2, 0x8}}}}}})
sysctl$net_inet_gre(&(0x7f0000000000)={0x4, 0x2, 0x6c}, 0xb, &(0x7f0000000240)="dc3cc8bc23c0c5ed1eba4b6a4dad607d5e7b196cf6bb19557d602577eadf8143e08489faf112f094baeac67360523fc1c2d9f53c5e95d255fb5acb8155add12be2d7176a43815bd7e88a0b0de9e35a35cf6da81cdd989c", &(0x7f0000000140)=0x57, &(0x7f0000000180)="7766d63f1dd7e9caebc237d3b7f9a647a577045e03b455fd659338a3efa06afc3ec89d5b4becc667a50714172a9e744e995484e07bd631dd3ce31612dd74c2aec6d5f49ec2e8115c5623f5f004c857e1e7e4320ee24bc302cdd5fe049acbedba7e3089dfe45bfa3dd8e3d1d3818224726ceb47e07c3138e54e13e8903435867abb8f5e3024d032de73b19f03d192fc92ed8300b559b00bcc", 0x98)
ioctl$WSDISPLAYIO_WSMOUSED(0xffffffffffffffff, 0x80185758, &(0x7f0000000000)={0x0, 0x0, {0x1}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x33}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000140)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b407d2707e3d81f4dbbc257699a1f132e27acbdd602000d7d026ba8af63ff37282912e4fd89750fd3872babfbb770c1b98bccf5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000006000000000100000000000000000000008135d08bb2d7fd4f6c59", 0xb1, 0x400, 0x0, 0x5888adfd882f9c5f)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x6}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x6c}, {0x20}, {0x6, 0x0, 0x0, 0xcc}]})
pwrite(r0, &(0x7f0000000200)="b269f66b39b9de6923518e4027fe", 0xe, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x8}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0207534, &(0x7f0000000300))
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{}, {}, {0x0, 0x0, 0x40, 0xfffffffd}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x2)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x40, 0x0, 0x1e46, &(0x7f0000001140))
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x3, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x67, &(0x7f0000000200)={0x3}, 0xc)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000000)=[{}, {0x7}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
r0 = open(&(0x7f0000000200)='./file0\x00', 0x1, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r1=>0xffffffffffffffff})
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000440)=0xc)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000000c0)={{0x0, 0xffffffffffffffff, r2}})
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
syz_emit_ethernet(0x4a, &(0x7f00000001c0)={@random="2da1ea475a70", @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "048f52", 0x14, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @broadcast}, @loopback={0x0, 0x2}, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000200)={0x3, &(0x7f0000000000)=[{0x3d}, {0x60}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)=ANY=[])
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x1b08, 0x0)
getrusage(0x0, &(0x7f0000000040))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f0000000040)=[{}, {0x3}], 0x2})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000000))
r0 = socket(0x2, 0x2, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f00000002c0)=[{{r0}, 0xffffffffffffffff, 0x1}], 0x1, 0x0, 0x0, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
r2 = socket(0x10000000002, 0x2, 0x0)
r3 = dup2(r0, r2)
shutdown(r0, 0x0)
setsockopt$sock_int(r3, 0xffff, 0x1023, &(0x7f00000000c0)=0x3, 0x4)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
r0 = socket(0x2, 0x8001, 0x0)
close(r0)
r1 = socket$inet(0x2, 0xc002, 0x0)
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
dup2(r2, r0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000440)="ee08665d19ac14d5e51348771197a7728420aef61715f7b183d4b3830c921bf0817a0000000000006a89dbdf", 0x2c)
connect$unix(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="82028600e2"], 0x10)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
setsockopt$inet_opts(r1, 0x0, 0x1, 0x0, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0xffbfffff, "104ce467af60b403184700dd47c8e9d209670991"})
kevent(r0, &(0x7f0000000040), 0x400ffff, &(0x7f0000000140), 0x7fffffff, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{}, 0xfffffffffffffff9, 0x29}, {{}, 0xfffffffffffffff9}], 0x0, 0x0)
kevent(r0, &(0x7f0000000140), 0x100, &(0x7f0000000300), 0x1ae, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = socket$inet(0x2, 0x3, 0x0)
recvmsg(r0, &(0x7f0000004a00)={0x0, 0x0, &(0x7f0000004940)=[{0x0}, {0x0}, {0x0}], 0x3, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x61}, {0x60}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f00000000c0)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x1, &(0x7f0000000140)=[{0x97d}]})
setuid(0xffffffffffffffff)
sysctl$net_inet_udp(&(0x7f0000000040)={0x4, 0x1e, 0x2, 0x1}, 0x4, 0x0, 0x0, 0x0, 0xffffffe4)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r1)
lseek(r0, 0x0, 0x40fff)
r2 = dup2(r0, r0)
writev(r2, &(0x7f0000000100), 0x1000000000000161)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
r0 = open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r1 = getpid()
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000)={<r2=>0x0, <r3=>0x0, <r4=>0x0}, &(0x7f0000000040)=0xc)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000080)={0x0, <r5=>0x0}, &(0x7f00000000c0)=0xc)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r6=>0xffffffffffffffff, <r7=>0xffffffffffffffff})
getsockopt$sock_cred(r6, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r8=>0x0}, &(0x7f0000000440)=0xc)
r9 = getuid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000000c0)={{0x0, r9, r8}})
listen(0xffffffffffffffff, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r10=>0xffffffffffffffff, <r11=>0xffffffffffffffff})
bind(r11, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r11, r10)
listen(r10, 0x0)
r12 = openat$null(0xffffffffffffff9c, &(0x7f0000000600), 0x800, 0x0)
sendmsg$unix(r7, &(0x7f0000001700)={&(0x7f0000000180)=@abs={0x1, 0x0, 0x0}, 0x8, &(0x7f0000000580)=[{&(0x7f0000000200)="f8fd1df8e4a6dc5ecc53cad5851d7865fd2ef49076521796f96a", 0x1a}, {&(0x7f00000002c0)="b7670db48b44359a8b78835dee4fdbce074ea9d51a1bcd95da6c7c78be7b99957348014f6fea44b5f1646f465c1ad1e62772daa2c76e4010afba58543b7cf3ed4a102696077c9a38dcb0a27ec65b20ebe439a1034f467c96", 0x58}, {&(0x7f0000000240)="470ad342f09f14fd47d907cf710977c50fc6af7416bd17afba4c54de4dcbfe53820548a8edcf4f048da41f1bc58ee6230d5e7902b63b64e855", 0x39}, {&(0x7f0000000700)="6ce21aafb63773b46bae242d86238818d281880efa5c455796c98503bb88eb932c3828a8699f26256f5f1dd8fe6bb31939d50df2b1f8fef08418ef1a4af1f22408e18219d7ffa43dd49028d858bf39b7b1a2f5919730a107ed05d3fd78a71adc900289d6ba4a0f868ec73285b690c27dc0b95dff7d91fac4594f451391799d9473c3529bbaa142d99f0f1eac08ed363104d092b311be9a4c7d8ee93ba1207ced34d462cad1ad83e3218a697f4419ca7072863cc7f10f0dce9a4ce122da456b5e2fa7d2df4c3164d9c11bacf61009a5ed6407c8ef1d4c689c251fadb8831d710351e727595a37f0c24cc3bb6fa85ec31e004e8066b8cc2f65fbf6b19794c22cfcdea16a249f8043419692bb125a1ac74e8daa4b9340b2e59de5a681c46068c741d0690455057065ac730d489a4db5eac7c9db023ac712a75bd39b679485d6d2d22bfbb5c25e763196dd584cde7fe4699fdd204affd7a1205d6068c49226437596b6b48573e559af583d5f4fe37a7501ac2b94e232546abab1f0f3e48ebb918a1e0098b601b8402d5c367c3b8734001fc41b1fbf514cd1b37aa84049696f52920a2fdb93d2bd950ae34899a9e238bf18619410322e1d9b82090099f067ae39a941c2891d35327e946686025996ad1e7a06eaa82b2fcd9c0d33e010c481102bd63ac8d18441ad98e5fe106e20fc286e6127f4b5576db67409d7eabf9d5d37169fd3cdbba7d5a134bf1cb00c1c489f105d65d98e43e9eafaa12e26cf34be62a1965daa55e4929e14f5d63dfaa4c99d6737394b472f141bb5e47b6a8abd03e6a2933892c82f853f10ba952a229ff439a4f19760121fc220d778e8d14241a2af2b24d582f7d000364a558c0b85964bb6ae5c47183fc27fcf2ab14522c3630b6c66092de4533c4bd27b902a110ecbe3fa6eaac4f30e8a67fab731b17426968678272528a69e389651f10f880ad018a228a90943ac256d2a025e9cf8e91b962289529ca8631de7ed782d2211d0331ea7f870b769fa20889bc72c43eeb5cb16eb0f4bb3eeca3d43e6d00f75ac42ffbccd6f475ddb0b5a84950048e5638dfaa9c11da55c95fc5680291b39572a61533fe27601861159098298e4af035a68912ff68d0a6e8d0dab6d901a4fd3624ad2c644c7088ffa65734401df06b11bf5eab328e60f6b882172a1894b0c78313f25f96562a9c09486122ee8516e75b68bdcb934b765a1d039cb37bf4364190e7a8d549cbc767ab180cd8a11b31be73b328d4af6b37eef2db11d94c8f035d724536b5db1c0562dd209e68d045e79d663c678a251bacc41d6c0a8d1e8de2b4e0f09c104166ae1c60249f09d1037724e6ff0cf623ee69e3fc0f832985284b2b7da5838ff11988bfec73d45503c5833de1205b5c2da38828eb702b02241dcf4897a6af0534aec2e10ce0d898f90b7848a1dec8c692c1f01518db79e2d412a0a2b2dfc36dce1813606f0a62f859ec49e4258c452baed1c2042499b773fb5177915895cc227dd150811593538da3b2c2782b4730e54d7709e4257cbe9b4f29ac505c28915e6db9747375f3e79e057fd55e362f1dc83307c017c8d4f2de8cf1c118b25c3a724594414cb7a5e7cb522b9e4933fdeaf4cb73d52d3240764602825a8f22436d756e75eebb73b75e031b07ed4b042ae106f4453548ec09b396ccd14190f0085666ddcf5ac64a2e8df6bf2c057141f514b63b3428fb748fa19f1d35a8dfe59f4861a19ed43e557ffbbb1687d36f8da9ebb446b78a1b9e31b7a9122b7e85df020d099bc49e34682c68c181cf90ea6fda00177cb711257b66c461fe081c60c94ee042f92d62945feb224c930a0c7021548935866341ddf690e8e1dfdcce7af138be3fab25dd38a3c69231207e15b67ca5572ae3729c8f6db96791fab4ba2d3c5b330dbc31fcd794eb2507fb43a7f2892bc32d70481f3dd34b6bfa2fb90558045b530bffa320213d697d2c6cc7e206bf4b7f29eb163b3a2d826cc0dcf718d47dd11a4f313bfcc4d38974b653f2fd6c3e70fe9ff3df7c974d9ae17697dbcf606f067a876d1ed13e815d3c921bd3b8ad09bdd3f9a2442967c9d6e209fae56d9668eada6e71f2abc403716852ba4ef42c931e63e7dce97dd7196749484a29d555a4b45fb6c05c1a674c2caa97eef9c8e4c2368c32253898ab9062b9875f748471db0bea418e2ad3815a0a70a565752cf36fc635043153015e93d304b774de258a25a4e48a5f0f133e51caf5fcb7531abf9b5c84ebd3235ec8b8ced8b65eefb6ad5d1bba8bd9b66399703642509d62fd5c0bf511c5dfbc5715a30ecdf92aabf66ac24c99914583a87e484633b5d679ad3ae4497701edb1a754cae4ed787de4594a9c8b6dfcfb4ba750cd711010739f7f441baeb667c64b2022319224b3eff0435984cfdf5c596842f29b2528e6e67d29f59f7cdebd8f2d36aa13743bb993ecec7af93420198de390d11db1a23425892e919443df95e2472fde0489c76ad504ce167ff083d26dbb94f18e677a54502764e97095cb53fc05950660ffb36769209ab8fd289d59f17ffed2da2e6968a364e5c676ffb21585a7164800fba98ddb1e451282a837e6a3159166de9f9e2b4f483da32401692b31d92aa1600964081835e8f9b1a85610a07157c6f511906a85efda16875d332732fadbdf0044a84058a9d3b69aec480ae33240b6f0d55b50f3371c17bf5b822ff497519f1f3138139294e49e63a2791237caea5b2b5d260de693aac5efe3a6516f87db050a25030ac52dccbb9f5ebc7299b312c50d569a1ff797f2dc8557fd5fa468a6263e071f58affbd52e50e288ce50af5e741390a0987175e39bb6785e1b3fc92dbe538a727f2b4e5dac62be9310d1df0bf36789402b6dd270fc976e8087a1ea52e29c6fde02301f97d3efeb1d3862dd3416539d91df3b21dd9476c1123fae1ae7b8b9bc28644e010b4eb1416e2418216b29c9694e53ccdf3ad820f69004d010ecaade17d7d845149de26783c839775c3076de1aa14301e11aab203dda7b49392ce839aea8ffdf0a51a52271aeb142167c35e970ea1db5859d338cdff0896df490d499f58cc03f28b141cca3d5d13f17c5a35a052def66ad447feaf88e4c4586839319f41374fd9d40f7e19a7f8007d3215b4b5c85a07dc279c8b0b1148c3ef0c8c5c3cb5fc4bbfe02b5291ee932d34ce6fe7c71ccf5c24d36b977c7f4668008041958f1091e8ccab0802724655966d719fb3f739ae61eaa92795b2916596bf1f14d858c4d202bb13c05cc66cae9eff4dcfe07c556c48f5a152750cea8ac65a987579a8f4725bc179cc6c8a6daa0233efc056a5be05ded7ca29f8290e7406b380f7fa390d423689ae5aad8a4d90db075127b12487c27ffba0e1e7822d7c43f1c52916691721a739caa07c3cd97dbfb481bf716a3074c54b4c0c8a4d4873458586e8d2192fec52c0a982febb4c6939f0f5b6f742810cc186ccbe54335cf07aa59c935bcce1d46154f96f62841093ecdad4d88b4aa6e9954c7b3c85514c1b27ae3a2219fe39cde4a418914b4f8cc07be7fa7b9ab2f2c52fbd815c200ab4eedda34dbf62fa4856f8ab6eb9a5be0ea2c216a532f768efeefa22790a063dd75d4d131dc1420327e6013db5621c807abb01c7ec70ea42fac44c81e8db15db9b66f42962b1b05271df6c054886619d98cf95bc41d9ae83b27c8abafb3533f26b21a6950ed4f8fecdb1b9c4b19155e25a1b92f772b6708e3bc4205d9878d19592c8af81357aeeaf05aac47978dfe09956b4a917322b62ef860bd3c513110c8e60d35f6d2f0c5a33f07555d5003720a12f8af55c37227f67fe6cb9466e2c9268bb996b48baa29874bf065e0b64f23690b7f4663eac097505e111f273feac733f2cc5267ea9587275daa3d93a67cd63a03d603c8f7199ba289f15263e78515f4b5822a0c5f5431bf37ef5b7bbd19d265d68c7df5afe8360b4d93ec0cdf36d682ea3d2fb2b50d7d1399f3fc8ce32f823ae35d7da7f28d1770a5e2ec328da406e2ec83daa0d5448ada03f46832cac3b6d046fc99ac2984979cc21ce043f71df03e1e400d015fed768be149b4836c2fea77a47018e1734c1cc32ac2e9b62ec4643e647f0dbb10c4b0f34603e7f331ef53e3d4331c8fd5115f6441b4da066cceeebca231c0e428cacd45762e1cd4d91809b7a3863064e2d86b21938cf4d1ffff9602f16363d7653315c8caa35f568a01bb6e0f897a0bc706cc71f373f01ffec4c75630dfae52b67c3728c27894bbd137be94cbdec7aa83d1ac2a3ea9fef2646f21bac1382e6c27743b2ea15f2d5d0c1760cabb49113957bec869dff3f7c847926d1824ee26c7bed5a4f41893b93ba3d55f279c18facd004dc227822348c430ab8496fc95d8515a242d0b0d843d3dbb92590faef4425a589eefb9f656e030488cd4621f24c6de782fb44c1221a7a72f8179e1183a7177547f53f58ad0fbec240c83b7fd79515acd977c5f318ad6b51c509c7bea248b8c721f78e0af9adbf680e34d0daa3385b2aae016868248852e9ed2e8c839ab9d8501b3b2f86d3ec9bc983c572079bdef8152326847fcaf293feb055dbbb4aa82eb02dc4bf9750ca938ee072b049a8a819d5375e1bcccafcb97adecce5cb18b7121857cab3f898fa4e5fce1f0949201c5aa9b5984db2193f75f05d67934a3609de1ee5f6abb156ce3843fc53dfc84420dacd38136145a35e4be176b0b68877b4211985626030537bae9267b0951e3fe80d56287652fc1bfc6ff3116e5886d005ecf7ae5c6a70c6fe814b2c1971e81d8f091ae9a4e179951d2ad5a2af2baee8d1a66c6ae2b01d00e3e76de0614542e47fd3684c192edf9775ddf0ba63777057b1153e7e50812422dcd64691333790fc4c560d10cf97cd507cd96ca37c3840a43dfbf47427ce893628c7c3c9f00f70846aced62883fa44211c225ec72a3fba0929f62dd7ef5831ad65548045f43b23ac26593afb3df820d6d0fdb1831faff47b71a56aeb2da237196a638569c99609e827f99fc42b2115da6548e0df6a9942ac266b873067b18581665e75e9be96a980737f3250418255e74c37d95fb233d860e5dd4a3900329985a55d372dec13c4835de088dcf7ef87ba9f38c10d50fd520bb9904b70ea1a3c9b0b22b6e52c1a6b09b6c8633b61467f629cce42c239c4267a302585076f42e1b911a1a3fb74327991554d94c6ba243958939e24c2d2da06d1914fd7446e91511323b9e8a904d340cedcc7b7804e015967f5b7906c222e27d422f2dffc0c6efd3b53816239e99d2b8c11257defb8e9f1a3b09bc0da670293189bdf16cc68dca754dfdf622602a9174f1ed5d0a49de371df19b71a890cb2de6c65ee7b318947f1cd95380ce554f9522dd69c536c5495c6bf6c5350b3f54d30c3c07714daf4b2f501762fcba551713420a53c8faeb5e77b7815650ecac579e151af24adf610d9d31f7adb174b10fcdc8079bb14a955ccfd953e203c0313b72c7b9675385390f0888108fe07e1179a1d6f656345e3bd9d347b11fc801d78775174e8d5ad5ae6139d4e9ae0ca96a102094655fd5e9868b5e54004f21853b1fca92eb26ed2f726eb77b4072505d457f11aeedfe7774e966aad4e8217cb57018ba445bad47514ebbfc858e841e4a9dd0b4bb7de3cdb84de85041a65282311659886fc337d90609fd6e9d58b1e7b2120a9e712eb032c2ffb47731d31d099c69d7300c9655d542126c1054db55f484ec19daf2bc372ff4bc956976ea701dc881e949e9028ca107b3bddccef87301c41c366d29a9297a098d539d3", 0x1000}, {&(0x7f0000000480)="f6f5e50c7a5e17d3137f06e694e1cd96ab0306e82608abe91feaef31afa31d2b5f37fa04774d25e4a6424c8ce667f9c489059a1c9f5374353a6375a6cbeecae8dd717b594744daed94ad95431f9ada86e53fb422f8c5f41a25efdd3cfaea85743f7dca7a62ee239f375ecb013b8b9e3c2004677680060f3a4bcae7ada517edc83356d2a9fcd47f2b576268d306b97038fcf7726c955b0e1e0fd75b6b2164262b031e6e03857bdc64444bc6c03f15bc09239744df29db4ac8434a84d82f7d73afe04efc277b7961c2489bc14c5b97d61a799e9ade", 0xd4}, {&(0x7f0000000340)="1cdda3238028fb0546cb6b7ce872d11da26e0bb9c692ebef0a3bc6254921afb5a5b9c9642c817d8a470b8444567e6d7aa86308747d8db3f086fbf99ffd5c9934e877b06278c5ae98e71fb8b6e4558bf6271ba931e1fe45dc2911b3314c314de97951fad8134c52bb4a787f53cae060cb89102f156aa8421d958e2fe2b07aec806a9b499410bb3500e851aab89673a7e9", 0x90}], 0x6, &(0x7f0000000640)=[@cred={0x20, 0xffff, 0x0, 0x0, r3}, @cred={0x20, 0xffff, 0x0, r2, r5, r4}, @rights={0x20, 0xffff, 0x1, [0xffffffffffffffff, r10, r0, r12]}], 0x60}, 0xd)
r13 = fcntl$getown(r0, 0x5)
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000100)={{0x3, 0xffffffffffffffff, r4, r5, r8, 0x41, 0x400}, 0x7fffffffffffffff, 0x10001, r1, r13, 0x3, 0x40, 0x401, 0x7})
fcntl$setown(r0, 0x6, r1)
close(r0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x9, 0x0, "f4ffd206025c460f473c03240623a164e300"})
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
sysctl$hw(&(0x7f0000000040)={0x6, 0x14}, 0x2, &(0x7f0000000080)="08c7c09cad34363c", &(0x7f00000000c0)=0x8, &(0x7f0000000100), 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f00000004c0)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/205, 0xcd}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
close(r0)
socket$unix(0x1, 0x1, 0x0)
sendmsg$unix(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0}, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x41, 0x0)
write(r0, 0x0, 0xffec)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000340)={{0x18, 0x1}, {0x18, 0x0, 0x6}}, 0x3c)
recvmmsg(r0, &(0x7f0000000300)={0x0}, 0x10, 0x0, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000100)={@local, @empty, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @remote={0xac, 0x14, 0x0}, {[@timestamp={0x44, 0xc, 0x0, 0x0, 0x0, [{}, {}]}]}}, @icmp=@time_exceeded={0xb, 0x0, 0x0, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {0x2}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@remote, @random="c182c3cc9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$LIOCSFD(r1, 0x80046c7f, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000200)=[{0x20}, {0x1}, {0x6, 0x0, 0x0, 0x10001}]})
pwrite(r1, &(0x7f0000000240)="b6a5d55ad18500ca70a13f531a30", 0xe, 0x0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x1, &(0x7f0000000200)=[{0x1ff, 0x3, 0x1a, 0x834}]})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x8, &(0x7f0000000140)=[{0x7, 0x27, 0x39, 0xd9bc}, {0x5, 0x3f, 0x40, 0x2}, {0x7ff, 0x1, 0x1, 0x80}, {0xffff, 0xfe, 0x80, 0x2}, {0x0, 0x1, 0x1, 0xfffffffe}, {0x100, 0xfc, 0x8, 0x2}, {0xb0, 0x6, 0x57, 0x8}, {0x8, 0x7, 0x2, 0xcb6}]})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000080)=[{0x15}, {0x80}, {0x6, 0x0, 0x0, 0x5b0d}]})
ioctl$BIOCSDIRFILT(0xffffffffffffffff, 0x8004427d, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
write(0xffffffffffffffff, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x2, &(0x7f0000000000), 0x700)
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x22}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
listen(r1, 0x0)
r2 = kqueue()
r3 = fcntl$dupfd(r2, 0x2, 0xffffffffffffffff)
close(r3)
accept$inet(r0, 0x0, 0x0)
dup2(r1, r3)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x0)
r0 = open$dir(&(0x7f0000000140)='./file1\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='./file1\x00', r0, &(0x7f0000000180)='./file0\x00')
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f0000000080)='c\x00')
readlinkat(r0, &(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
r2 = socket(0x20, 0x5, 0x6)
r3 = syz_open_pts()
ioctl$TIOCSETAW(r3, 0x802c7415, &(0x7f0000000280)={0x0, 0x1f, 0x7125, 0xffffffad, "919009000000cafb000000000000000000000008"})
close(0xffffffffffffffff)
setsockopt$inet6_MRT6_ADD_MIF(0xffffffffffffffff, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x123)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f]}, 0xe5)
kevent(0xffffffffffffffff, &(0x7f0000000580)=[{{}, 0xfffffffffffffffc, 0x4, 0xfffff, 0x5, 0x9}, {{r0}, 0xfffffffffffffffc, 0x8c, 0xad434281e112211c, 0x9, 0x8}, {{}, 0xfffffffffffffffc, 0x40, 0x1, 0x80, 0x1}, {{r2}, 0xfffffffffffffffd, 0xb6fd4ea04b12ce3a, 0x4, 0xc95c, 0x8000000000000001}, {{r2}, 0xfffffffffffffffd, 0xd0, 0x1, 0x7, 0x256}, {{}, 0x7, 0x8, 0x20, 0x4}, {{r2}, 0xfffffffffffffff9, 0x44, 0x1, 0x5, 0x9}, {{r3}, 0xfffffffffffffffe, 0x40, 0x1, 0x1000, 0x2}], 0x0, &(0x7f0000000680)=[{{}, 0xfffffffffffffffe, 0x4, 0x40000000, 0x617e5dab}, {{r1}, 0x0, 0x12, 0x1, 0x9, 0x5}, {{r2}, 0xffffffffffffffff, 0x140, 0x20000000, 0x101}, {{r2}, 0xfffffffffffffffb, 0x10, 0x1, 0x600000, 0x4}, {{r1}, 0xfffffffffffffffd, 0x2a, 0x1, 0x7591, 0x8}, {{r2}, 0x0, 0x80, 0x95, 0xffffffffffffffff, 0x2}, {{r2}, 0xfffffffffffffffb, 0x9f, 0x20, 0x7fffffffffffffff, 0x8}, {{}, 0xfffffffffffffffe, 0x40, 0x4, 0x3ff, 0x69d3}], 0x2, &(0x7f0000000100)={0x6, 0x3})
r4 = socket(0x11, 0x3, 0x0)
getsockopt(r4, 0x11, 0x1, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000140)=[{{r0}, 0xfffffffffffffffc, 0x8, 0x8, 0x4, 0xff}, {{r2}, 0xffffffffffffffff, 0x0, 0x0, 0x3, 0x5}, {{r3}, 0xffffffffffffffff, 0x42, 0x1, 0x1fd, 0x9}], 0x8, &(0x7f00000001c0)=[{{r4}, 0xfffffffffffffffb, 0xa0, 0x8, 0xffffffff, 0xf50}, {{}, 0xfffffffffffffffb, 0xd3, 0x0, 0x0, 0x1}, {{r1}, 0xfffffffffffffffd, 0x32, 0x40000000, 0xffffffffffffffff, 0x2}, {{r0}, 0xfffffffffffffff8, 0x10, 0x0, 0x10001, 0x800}], 0x80000001, &(0x7f0000000240))
accept$inet(0xffffffffffffffff, 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x7dd5a512, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x10000], [0x0, 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1], [], [], [{}, {}, {}, {0x3, 0x10000000}, {0x8000, 0x3}, {0x0, 0x0, 0x0, 0x1}, {}, {0x0, 0x0, 0x0, 0x5}], {}, {0x0, 0x0, 0x1}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x2, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r1)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000180)='./bus\x00', 0x80002005, 0x514)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x10005, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000240)=0x9)
open(&(0x7f0000000080)='./bus\x00', 0x606, 0x0)
mkdir(&(0x7f0000002b80)='./file0\x00', 0x0)
ktrace(&(0x7f0000002b40)='./file0\x00', 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="b1020801"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
setrlimit(0x8, &(0x7f00000000c0)={0x1, 0x8})
kqueue()
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc008441d, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0xc0}, {0x28}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000), 0x3, &(0x7f0000000040), 0x0, &(0x7f00000000c0), 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x35, &(0x7f0000000180)="04000000", 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0xffffffff, "020000040000000000000000aa00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$inet(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4b}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f00000000c0)=[{&(0x7f0000000300)="18cfddcb60f8294666046fe16731fffffe", 0x11}], 0x1, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x400000000002, 0x0)
r1 = dup2(r0, r0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc038694e, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
getsockopt$inet_opts(r1, 0x29, 0x3c, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x7dd5a512, 0xfff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3], [], [], [], [{}, {0x3}, {0x0, 0x8}, {0x3, 0x10000000}], {}, {0x0, 0x0, 0x1}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000380)={0x97ac, 0x0, 0xffffff80, 0xffffdf82, "080424038000000000c5cb210043e99cfcb56028"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be6c0045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dd31c0dc31b3c0e35ca7", 0xf2)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000080)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x80000000000029, 0xc, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
mquery(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0, 0x0, r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
setuid(0xffffffffffffffff)
fchflags(r0, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x9, 0x0, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x41, 0x0)
writev(r0, &(0x7f0000000200)=[{&(0x7f0000000080)="c28b2f9e1465f6ef5d73bd75c549dbfbd1b526ce0a55ba5c6e5339ae35b91c4d4c5a757f1b36500e0d6feee712e8428235d52f5e10612d8d63c517a16dbd55bc78bb0f2477cb2c01fdcd575c8e0b50be9d8f6ecbf05918f9cce759c79735b466786a817aee381b2ed5d2a0f749b0be942a4409d8d1b9fd756cf0d8b7454b606024400e628c7cd0ed766a89a87475304e9c", 0x91}], 0x1)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mlock(&(0x7f000000a000/0x3000)=nil, 0x3000)
mmap(&(0x7f000000e000/0x4000)=nil, 0x4000, 0x2, 0x10, r0, 0x0)
mlock(&(0x7f000000c000/0x4000)=nil, 0x4000)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x164f9fb7, 0x0, 0x9ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x8, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x0, 0x8, 0x102, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x6], [{0x4, 0x0, 0x8, 0x7}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0x2, 0x1e, 0x2, 0x1000007}, {0x7fff, 0xffffff7e, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0x5, 0xe59, 0x10000000}], {0x6, 0x2000002, 0x0, 0x8001}, {0x0, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xcd604406, &(0x7f0000000240))
ioctl$KDSETLED(0xffffffffffffffff, 0x20004b42, 0x0)
open$dir(0x0, 0x10, 0x10)
ktrace(0x0, 0x0, 0x0, 0x0)
getpid()
nanosleep(&(0x7f0000000000), &(0x7f0000001040))
r2 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000100))
ioctl$WSDISPLAYIO_GETSCREEN(r2, 0xc0245755, &(0x7f0000000080)={0x3, './file0\x00', './file0\x00'})
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt(r0, 0x6, 0x4, &(0x7f0000000080)="9e868ade", 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)="919b5c5e5c069d14288be166", 0xb08f, 0x1, 0x0, 0x0)
semget(0x1, 0x4, 0x601)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff})
r1 = dup(r0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={&(0x7f00000000c0)=ANY=[@ANYBLOB="fb"], 0xa, 0x0}, 0x0)
sendmmsg(r1, &(0x7f0000000000)={0x0}, 0x10, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x105ec2)
open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x800)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r1 = syz_open_pts()
ioctl$TIOCCONS(r1, 0x80047462, &(0x7f0000000080)=0x83)
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000)=0xffffffff)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000240)=[{0x40}, {0x20}, {0x6, 0x0, 0x0, 0xdfffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="6d9ef757a944e00af2e26510358b", 0xe, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80047476, &(0x7f0000000080)={0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x7}, {0x87}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
sysctl$hw(&(0x7f0000000040)={0x2, 0x9}, 0x2, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000003})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x14, 0x0, 0x42)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000380)={0x2, &(0x7f0000000000)=[{}, {0x3}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0xfe30, &(0x7f0000000040)="d6efcc66cc62c54742823fee3eb5ecb3e535e529c2436f", 0x4)
r0 = socket(0x1, 0x5, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cffffffff"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x2, 0x3, 0x0)
bind(r1, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r0, &(0x7f0000001740)=[{0x0}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}})
ioctl$FIOASYNC(r0, 0xc0084428, &(0x7f0000000240)=0x2)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='\x00', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e089218b0100010000000000", 0xc)
mknod(&(0x7f0000000100)='./bus\x00', 0x2005, 0x8000000000005200)
r0 = open$dir(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
read(r0, &(0x7f0000000040)=""/47, 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2}, {0x2}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000001200)={@random="1b329ff120e0", @random="c182c3cc9746"})
sysctl$vm(&(0x7f0000000000)={0x2, 0x9}, 0x2, 0x0, 0x0, &(0x7f00000001c0), 0x0)
unlinkat(0xffffffffffffff9c, &(0x7f0000000100)='.\x00', 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000140), 0x10000, 0x0)
dup(r0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000280)={&(0x7f0000000240)=[{0x22}], 0x1})
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f00000000c0)="b10005016000010000001b00070000000300000000000000fef96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400002100000000361b1257d0a8c5001d200200000d2300008abfba5c00020208a371a3f8343712051eeab71d89e00004000041000000042000", 0xb1, 0x0, 0x0, 0xffffffffffffff04)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
sysctl$vm(0xfffffffffffffffe, 0x2, &(0x7f0000000100)="92", &(0x7f0000000080)=0x1, &(0x7f00000001c0)="b19192502700"/16, 0x38)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
writev(0xffffffffffffffff, &(0x7f0000001380)=[{&(0x7f0000000280)="f25adeae43ae2b4e180da5c7df2d3c3bbeca684b33295427a7ab10576e1ef73ae8e4e59d220a72c6d6fc33de4dbde3c4abdc9b8b142da592e957ee25efff2d14da28a22e0e2d059265677648c38d78b106eb4059dd82f6a82cd8a5ea31fc97a347df65c075d53b700bf0902e071ba247ec5e9c254046fb12f6136cc8c3d9c01a4a16d2198a9db157b39132ed7ac40997a069e9a8fa13ba40ba99c4a5dde48ca7978bfc5e21c1790c55954c8e3d4edd0ccb8a8845161334b96137e11afee89cf2695a4fe2550ebbda9507ee714205b8eabb79618cbd2061f6b17f25ab05fe04cf9d9853963cac4fe5ff33e1b0ce65472d416545e3575cb0e03599e6aaa95dc132f754ef7d26f59ef41bd512ea825efcdce465ecae6d49634153106e9aa662bffb64984f386f26dea470e21df24c710ac6681de105a8b498b6bc55cd04ffa35886f7936841af8f6ebfc7790a6595b94eae16869809b946f7b11ab42d59007ade2bd5d1ab4bdba71649badbd706673ac0bff692bed4776cc8e002e7eba62d543e72f0390fb1e618de1226de64cfe23efba63304d98633632ef9de72814db39b37b4d2328c2fc450c0e92711c3e4f584ee486afe625a733c95a7a549e5643bbbaeff077024aaa53c606db6f649efed78bd4cfc22df8c537339dfc6f236e165592778ecbb7d03a9f1c81bf25fa88f19f2a4ba67162430ed875bf76bec7b1ef53e0499341025e3ead0e36c6cd0eba998e9c755bfdc28692b3ddddf8f7bba7e0b8edf692570592f812ee30635635364625836284b5391854d6e4123892a73026142cca2ddfe28069391562bb11d7e21a12fed70922dc3975b68e2589d83fc241e274c4108f535dcb8e4950fbebf7b0d873db59df53e1dde742fcfbdf5cb3345255e75a8a4db2fe0dfe9329fdd36c639641019e94f8bc524de8caac869150453ce0f53d6ced533e7b339f6936154ba44da2c4d3d9f138c69b2baf9edc2ccfcf5e08c273528d0bed830aeada0983578ed80dd029b5ea39664f1a04e578022bf0cc8ad930863ce3b692904caec70f9e0b1a56ef1c7e2d6637816643283b4b6048573a59a3c46637545f16794b08db77bb86cd34acf23bd563cf71e8944a34eeae99235dc31b00fbf7d78e61873e67decfd84c4799be287fd1750e32595865a0e59636628d4c19feace319d5441576bdb4268a96f0e0823e1806244728bc498c8287e6362e5c581aae98df95dfe2eb06e7e831b0430a6f5a3a3261b26e8202c5a22704638193545768d0553d4409f12bd7fb72f25ddc685e1d895779feff4ab3ac5dcbc97107db8cd74b34d4b7241747b9a0d1cbd695c67dfed2dabea10488fadbbbfb957230f4b5e6711c804b3c999d3642fa04c1ec7e6a53644f88fb08884dba541be50f2d46e547c7dcfb37435e5f1a14bc453c60c3c556ecd58fea85ef146934cdd23c3fa6e0627c9d74d5995789bfa0a61ee053aa9bee882860e39fa721cb6731bd351404867fc78578c403bec3dd8624f81d853909c12f1b1c6de651d5f5256ab3d6556608aa70918fe17f42ee9284c07218febe5cfd8568c95bf2dd389d1816c42a41f2d06cb5045b0df5cb041611146b922c1edebd6276bd9ec7ae92f7a0d63852aa1ff2425d109225c4b7391bd7ba66f836dc7cac4fc46775d6b13151fc283d203dbee8c746aeca3510d73ccdcf5ca2c2a3e414f28dd96a6e9f63e999ac25a5ec65f924397d2c3bba8d0c49c35f875564d612c29d396e185f97efcdff8c2a549f0faf7c7841f77976de64ebd40b65ba89f7e9992320927981c8a3938c0b09e9b229c402408a7c4ec5ffd193521c09a1ac3d686d7b8e2c2c41026926d7b86319716d5f982ee972f27a76f72609c9376b631f4d6ad589dc7ae0c62fbf90686f657e5a78ffac617bb9e36d746ce05c147721f3bbad9a10b3b9bb2cef9b45dcde74468f3602ac03fea3d2276a88bf6a5cb3c6661766afeeada8a532d4799d496ed786931816b64bd40dec06a67e6b3", 0x58c}], 0x1)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x7, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000d80)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x40}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000140)={@random="1b329ff120e0", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f0000000100)={0x2, &(0x7f0000000080)=[{0x7fff, 0x9, 0x8, 0xd0f}, {0x9, 0x7f, 0x5, 0x9a}]})
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
r1 = fcntl$dupfd(r0, 0x0, r0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="f0ff115c0000081a", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
r2 = socket$inet(0x2, 0x3, 0x0)
writev(r2, &(0x7f00000002c0)=[{&(0x7f00000003c0)="aacb08a9a2370b112d479b867ca70258f5f33a98d353bcca507e7ffd73d7c06bcee053df5bd418d84e87223e35ba2ce57504684f2a82c69cd8488dc82f1a0a421c4cf09a35135f230563d24dd5844f05a9656c7b60f33f3e1733b11e9dbd10d8e5d1a48ffecfe5479606f42a4f6623fe7b83cf27dbaad79b4854beb7d7c081ed79e7826259c0b9f2d8fa316631f5e39833b5e64cb38d8828c791785e18fc4c175a7f5ea8ab2ae89a19495e54f1b923890f24747d1d19cd655b501a9d423a79ad91d19933d1f61ed383ee88c95aabca74ab0fb91bd3446623b2abc3bd4523acdaacf2ed5b", 0xe4}, {&(0x7f00000004c0)="3ae8056574560829dfc5cdb1c63f2100e309e134a975d15cf31401bd8668169c16b3d079046fcb5c5a761244313ef988cb4b64fdac295e566ec05971e418e9bfbb14f6b4eeb03141d14640f79b0ea20217eb7a2135d63d67f901ce6d8eb16440c5fbb54fb612290fb28f7e6f18c6f136e7f093eb118643462af225d9183bfb6c972787127b13f978a8dcb71a274878f3e057a38b83c52d2f5b9e6991be01c5563b83dcd025d7a878d8e01555682a32b0f915e62c53c1223e5303588cf15c49b7c50ff801fb3e3970204577b3dc7167df", 0xd0}, {&(0x7f0000001280)="7be98cacc4823da337c0adfb2ee59f0b578b30a0832685df9f6ca438d14b9b950f85d459d569f7fc8fa3dd9f35c97a788f28d300b1435fff54b864f71c4ab8148ce20905e1b6e2b8c3c98d461a5f9e4928d81ad8ae8876de9018178bf3814a154570d1d70fcefd83effb0ef1c84db33a132f766a5fcf91d8d9444df9d0cc059886288ce65cb6d9110215b96a8a6e95bf1b62bb1892f0382e16ce0b7c5552add3c03f94840eba7d9827124fd102632f8c1967b8899b3c1d2cbead6cf8c5bd16a6858552d3644688309775fb605042687ae1c13df212a46007b8bb41c8622e8410a4ff3fe2ea9525d0b08b7b8955b55ce83302d81127e8b86fd4ece49c87cff9c0179fb84f1155f1ea06b1ac57ae76a8b88dcb4fafab40050dca136a8597763a5ec32f4cb18efc0e0742f92c5d2aa945cdfc407ab7f2091fdd48423165c8fd5bf3d7a82420180cc2bda8ffe34d91359317687d9b2d8ff1db5636e0e35aab76afb9e66a139486405a0f12610f355f7770d3dea01c9c4bbadeb69ec23f75147bf977f8585322440bc27e33f35c1e33b9d26a0ddd31b6c521838bd84237ba4d944fca689034170ba7f1f6b47fe5a792082227d82f0af0de84ccc2c9660cd89d4dc72c3843c60ff75c958f11d6972bb3dab4ca9536b694a4aa8780cca618fe773cbc8aafb7fea5ec1372cd95691f818e5ee4d3c7b7294e947a1495e8767cb171464948faba451e3b1a0e92870fe85133ae560b3c2a9831f7ee0d0c114e29a0370619343b52f87f0b4aa997cbcbd8c0c01a530a839bd7c5896e1264706d2bdd573f907220bd63b90af561abcea58be82bd7f65cee16e195405d83759a1a67c2ef55980f00f52a60afcd941316e3dc11404a820745170b96bb0b20f97cb699e081fa0c7f58b2faf10542b76d5a7c62753e8ff4899fcb590566a30b163798eb7968eccd5f4d16e3126ca7d9475465885119ecdd78a28369e3a08185810ecb0ad829dd1d6898a9061abedc07bbbdd858d5265188db5cafad26de88346ed35fd067f8b8733af3211c124a7ebc8a968511a36c0dfbd7ec6ad091952442889ca1e674d33b31a4125b3c370812123ca738487e15a0d362b0b343e15b5947cc40f407ae0e4a55dc908efdfc78801ea4820caf7ae2a080fbf579450b236c5262520c943bef442bf809c27dc25aaf3ec2efebf78193b428c375f9cf8ba2c3653698b34112b724980dfba765f3ea3247892747711638970d25da50a5d6a0b4da5f26806973887ba8453692c89993c91362f2a914c38ac6c708bfd369edfa242000af8ce22b739a82cb31c4eb71112861b4161ae67daefc0336dde843720448fd102837c033c8c9a6c4e2b517ef23fb9198e31e01695099b0fc08b44551d59c93a67efc4d6c501fc88033afd1f132a1076861307d0320ca23801724d7e134ce0994cdcd405f37bbd764672182dc38861af6088249797df5630ad41d4c1abbd4d4dca2b410e5ccddcd0ca576c23a3df0e959a3d71fbb88c215ea124623a2a70001ea4b9b0a26b79f2a2fb2f41e311f963ae8cc728e2299d418f3b03310123d6f4427396c9d69c8dcfc227dbb180aa6b056ed46800575c10e2fd43794d95de1f83f59d751e7a91c51cc4978d3cfb021494ba52bff412a8ddc852be00e575207a52d83d01e876b031a39e8c6238d74e35ccfefa53b740d0369f670e113592ccf959efbce7ddcd5d3460b032b0748f33f985b14f30449017e4bd0ebdbee4cd3fb43f50e4e7cfe82635c1809ae03a54a948d0aed23e1728cfc44a9674c8db537953eb83e014da9e5f0ed297097fa3744890e165efa561f5a4b143479e7d004235ebe0829c1125b72579a5efbb3da380bf7c39092b5fb89363755b829bb539da1c3725ebcb8733f1773fcfd080c2f596d21638b97705c3e7734b276169a8386b355611fd689b2e7625ecb782a4eec6a182450abec5f81ebb3099b3facd5c09c7364ed7d97e14e2e14bf17ebc063359151de009dc16686cbca499c4cc1c2bddef31d73ab9eb2bf3a316fb9d48fee5ed0b20146681d6fc79f1a1f2bddaee196ae2f38489267a3e74e2c319b76f2184707445e171ec6423f93146de16ce654d0dee9a719f881955d78d27c2791077ea487e09c0ea6e7b8634089492a65d8dd18f0a06fb58dba7a050ce5267e10db2c37fec760f6f2ce58e7ca57e2f3230659db6d3b8d871e3e96bb8ae723f2d9162fffc0281ef056eb47c37bab0c079667b0f92c0ea88a6a0e8e0268d95600d6ecd98c44cf42cbdbc0ed35775fe2977c152ce4118e3c2bf85864b1bb75330b17de381c5fbe3a6f572759d32c6458c1d54c489b0726edf92c4ea07067b790d7a7e52397fbd9634d46216f0009b7d7d401fe0786d73852dfebe85d70ad826973ccffb6e0eefd89575c771140e1d30587ac85c2fe710e21456da1890d3067f61c4e5b69f9a154df8e36d9e8a85c103b6170ad86f1a5e83c9e70f6172ae8b066392ae4b923810032f3bf2381d68706ae02537115d5f68f85ca9277063fce44a382122cfb6a8cd4fc3ea432f5ee120f4e1deff1ef8564cd5cfff2c04c35bd9e9eedf74f0ea2ceadd4e89b1243dfbc0833f4deb4057cd2bb6c13fea5674645de082511456a5544c19bb2788def018f641e006822798fc4da0dfa09dc0d961b674d0b08141064299a9c5289f2ae840b078a6784feaf3c40bd7d42991939b44683ffb6bd882781a6feea7d8be14929a69e0d49016238c039bf4cc20aff95ae187db3d4dd0e2d60f4f99cdd775c459be8e135e08a67823c94445ea92ee62af8a2409e7eb2fb712d17955ecc31cd2274c13a0336c8231c066c05425d25182717bd1c272622d4e2174383e9d95fd67de3f4314db3fbeda298179e74de9271e3bb07f6bf839241d0e688f8d5ede8023ea559ee708131f3c2aa9ac41d8fa6471a441b44abc4100739f6c9c3e57ac53400e04a2b7f0ad34c1fed8b76c6cd6a306fbbb13930c4fc5c588c5c6fca13fbb708a8cf8e096dfdee5d5bf6b376286cf293fce6b6e22edb945a785624d100c962c4d8eefd5b711261ef7da0b622bb911fad396c2ff9637eb4fe70655eadd42de0dcb55f528b961d6d82538f4e88548c1ae4b102fd9ee336dbef7d8f7b8a2782f7350c0dfa1430ea0af23eef31249436dc0d01111bec4cbfc283e2fbceb4b6d087b9565cfa0173cbfa967853058f8b1bd23d892c5212aa1bebbb18dfcf53897e941830ce94f12825295aba509563d4c2bc472a655da51ab3b1ce9195a1f6d055a3b1c58f0d7d6b08544041f4a93e438e71106f0d500bbedd9aed64ff795353688ff0765456a61b7296f8dedf05615570dd5ee990b064a9ef8d6fa1edbd6a85f0ec07f6c1603b833611314f1ce13a5c8a02f6d7ec73111c9af959ea2ff2f21a5b62e1a50b35d3d11fca19be46b224c3c1159881768deb53c02103bd2b749fbecdd5ff6a419f24d7188330d12bd172ac7a9527229367789cdb87c3c6152b0001e27c1b741520fd0605af6ab3579d769403908d50745b061c44fa6e1bd98b84ad87e56019089c82714b6ffec3a078114dcf0a3bffda5cb0bffcf27c3c763d831a6d64fbe427399b130d53abf4d72b915d88134231ad8269ce37528a4fb3ec73da3107582f0c9b3e2f8136ee62d5f44a24b7809b53052d8b2f66798258d753767011b1e6d1aa1087d3e1f9a0596941d7786b8fa6ad937b7dc8ef55ae3c61c8dfc12bedc0531bde6460979125873f629bfcf6d4a70fed80e3cdc506f8ba47f2cf225fc8a750c65cf442105da4f3ae4c81996275a7fceab6fae3d958c1e3812b2deaf8c1f6e2acf697bf9fe00f8e34f1410aee3d90b0a37163203470108c40f6e46e63823ef6b09dd5758b39e3ba798b536df37578022af84fd99a5f27c0d8299e3ff7c8697c4ed0cc197d34780ed2fc4615cd351ecc4902ff002549df64224d4d3f2eacbf9a5265f2cd2d7f460c8aff920f355116fee1c61cd1eefcd03b7062531454bd20502bc1b253d829917980d781a9b63e08694af9e8fdf10518e9318aab1bab70d7f84e07a1562afdf2d55d66de91424f3a1223bcbaa2bf9d128028bcca550d84bf754284a4a2f24a0ffed1c49da17e731826b0c0f817126522bb283532f19fa899371c6becec220548ed6486355e946f38b4a1d11ad1ddfdc1a36d02a0ea2852ecfb5391d26c7ad236a0e8e3809ec757ab869311a0874c0f81fd4c9e7732f6f6345211def5993266bf2c6e7a526e70595a2b6ac2b96f6fe52413ac6ca5afcfd41b8438feca6ea3b41e6d73fded5f558f49df093a238d3a81f1a38371ba14eacca0606b6dda1783561a0b3595f9c7d555ce5302ca02ce55f1455b378b00748c50c62c1fdfcc218158b028d9e6f656a4e20e94ac32def74e47456b050485098e16581105cc6b336cf0c9b86242d77b26e770b155fec69e49a7396813e0814bf63d33624bbfcfc8f56b91e9acba19526950b56df7ab53dbf57c9ac3eb449ecea8d6a225c1eeef794e00c8f7b0735ae9723bfc9e65a89b5366a45e2041daf6470f7a40e2c428a5fccb7d562be4447a0b79cf008077d15ac2c1290e9868c8a4ac8b87aac664f77237187296c15fe79a6e079d0521d2f5acbaa02b20071e3e9db23718aa21875e04c9cd13d8d35f2e05072576d1063b32e1a4c765979f98a0e7e5214b71f6d6a1f9c36d4a0f2595c57ba9517b7d43d2cf8d0e8893e35b011a045d4a134c7ff1d7408d0838abbfe68bf8f4e90453f0246eb86781a91989517726ed1f0d5e5c65784b5c2bc75617674c1a533eade2a11405cc907c4b54eb06af820627e582c31748ccf41e90b3c368a5f0b69d0c3c15296891bec5c6a7014bffce6e1089a1e12fa9ff2c0586f61a9b6cee550db140ed1ea9ece17e66eaf9815b8e055a49344aa606ffc650587abc861f6c39964a8c649f7fca6499ddfe7fe7af9a8890394442041c734c0dd128e05c0569231fa4ae5b875d0f849c3a2ebff791d93a16a0d4228dbfe1cec0e918848d549c312170cd4cf2a230e1c78fb646d500192e5a117225970d9f5c76fe058083193eae3cccd20408762846d2ef475bc40df8719f85cd8e22c768bf7547bf51d6548875b7a43eb641aa47b0722c4b582c8c3ac7c905898beeefdd5c1b9f168c863ed77c7c69ed6896880586dd1d6ada882160051fde0d4e0eef1d50e797568a810077575bf639848d191721e563ebeb4e681a7c0763385bf4f68a121775ee80e8425673501e1e4d130da14d734af2fb1f20757c6b65c346ba89dd58d1471f4c3c59d7b4edcbfb89496ca5486d89c18d9d086b6277c9add8246f47c0cf2bc7ab39010203d845a63bee58f869c9a7941324fbd412f9c2c9704a77d65d856432de32517b4b76133f3aca281248721e09595f839bacab62539274989ffc5f1dba9b8b4f0b92fd5a91eaf6f2d0618585976d4827c5ca36e79337fc7aad40b969b1a6435e7bff0fa5a7a084cf8209ea954fde907306c08d61c6c615467415c34bffab8731945f3e9a0bc4bed093a5c1b23faf2c6d24f9c7ed5fc309904c73d0ed42009f04cab2809d22df1d9cfda5e3caf258a4865dc16ba8752a5bcea71f5a9123d276475c04815b535dc9ac5d0bbc30ff40f80e2f7580af7a83c55e6acc15ca6159483973b4294e06d74c871a5aa4263325fbbe5ffe9a6f8e12d11561c3d8214d29338dbf086fb3ded640b52d0c86762d13aa0870128d7927d6e5ed84fdefc0c22f801350e1c43aafe819365855c46f5eb43ac157d695aa5de540caee63a0edda8f79cbbc31f638c6f2113ffd39da5e", 0x1000}], 0x3)
recvmmsg(r2, &(0x7f0000001240)={0x0}, 0x10, 0x0, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x0, &(0x7f0000000040)="210f41df909c010852c4aa064ad844d2154dc74973e0649be0c112223c5a8e29f1", 0x21)
r3 = accept$inet6(r1, &(0x7f00000001c0), &(0x7f0000000200)=0xc)
getpeername(r3, &(0x7f0000003280)=ANY=[@ANYBLOB="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006946283ba47a92d4aba05fef71b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc322ca500"/4096], &(0x7f0000000300)=0xffc)
setsockopt$inet_opts(r0, 0x0, 0xd, &(0x7f0000000000)="e014f8c9c90e034001000000", 0xc)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f0000000180)=0xc)
writev(0xffffffffffffffff, &(0x7f0000002580)=[{&(0x7f0000000240)="449fb2a7b448446f7a606f6af806cf7c0f6c6ebcb1dd50e515f5c6adb88d55c9d063cb0ed68e590b04544b50d9734d38c5b843164ac700cff0036c30612c67bc5da1a9606f6bcc592ec20c57e4eb58dafe689fcfe3bbf530a837850548248d7071e1d18fd8df7c825eb8c3121d80a5ed461ee6b7b9cafa6f89026712f4966fec55a24ae18e309de4916fbe3257c9aa924c337ce1203e07e4d3270ce9590677686fed018c7fab6ea7e6738ac073a26c880ebc9edce512d2d93d84bdd7079621c505c22f304fb8884e571cc35ed7593dddc218cf39518904126ce63ee8e748278d0222bc0772846f16b1a7b70271093494d2915fc63b8c462481b4e049537a081f1d48186cdca3f9ce77a2434169837f8969975fb663def125a6aee54f2dce17cc0579a6e64bab86b5880a028f3c13500373f0d75891b840cf1510f5e748592cebdccac5b706f8b55fdfaa9a993b87f29fa824a260cdd637fd013ded6601f3f3be8596662b9cc3968e2b4fd9b25e8b5d319a0d9e9d8d69c786d9165ed679f5118b5341551a859ef755cb9212ca8c795c5f7b7390e3e60f46765ab072ef64bdbeafc561c5c9443e6c9af9613c11500d15b83c1a1f10ff87b4704362cf762990afb8c2a1114057ea9e4892dba5d0012d7fbb06e511c18e0d53a1db7f5c2d524cefd5d6507ca59c494d52d0039e82726e0f79c3d364ce3ab30ba216139ce5caaff5f8a4d20a4637831d7ac5516656dfff51209b72547af4a0de08ad48d224e8f9552decc40e0bc88fbb46915f1635f67a8edf1c7f0f57177364f3feecb834ff6726df4b6492a7659ee1c7d45665a54b65255895963762583787c17ead1d0e123ea2373f5bcb57a81245b25d71a7e56dd16f022bf3521808eca8ab83f2fc9140b4542e95651c752ce674cbdb25673849c72ed6439757a0e1866ee75837c273f6f436ba0125d3ff4eb3687538660756dbbc97bd186430a984396e15d17e678436de7e87990daaf7ef7b0912280774ecf6ce7ef6d6a858ca0b515a274af9ee99677119d55d51c6a0f12cf2e5119ab2b4f6fcde3b0852329ce3a32d207adcecf788e9d67e66c460e479a85bf116a0d41088373703b442aef0580a5d5bff83286eed01c456e2cba2a4327cef352570a7a97e25d61845bcfd6094cda2282dcf47fd7d1d079101d98781db868a169ab5ef2a3e33fcf970aa2bcd34acc3637bb49fb920548443a1ad0214055206fe64c1ef879292c80bcf9e112e359a37ec6042c1d4b4849d757cd1da9ec38fa131b5868138a490b7c81e6cbdae2e6cb689dcd00caf9275b30aa1094f5e9896e54d91f3d1fdfbe893dedea385a1d686b06ab226cd094acf7a1c537e90cd6d1bc79964aa31c50f930f189538ab3861b602e22987ddeee20859d1372bc2de6902a625ba5cc37a13c3d09548ee34acaf38a24c95838a1671b5aca39dce1b77da503d2681b6ee3d059ea2139986d45f6b2e199f53e517fda038eff9a5c58306dbf752ef36a4532a9b6dbccb3bf48c31b2861ecbb5819ef9f66dfaac69c16199a9e11f732ccc52ba6c8d97ca6466a7c235e629a029379bbad9332ca0cb7bf16351512788b4e20b7fb871741fb5f528f7b4e45bb34736e6d780e7bfb4d0dda9d9bef1e1c84b7778b84e4363baf58b28f1112882a01d6eba80a0cb70032db014234d0f348be042755715b8ce49a0d86ba7961f5863fbaf02e33c9d1fd33aa9fb2eb31c380ddc590b3adab9d1a510a69798b916c64b8c613b717a64c1a57db2aed71ed582de0790c027b510de9d698fcba364d7e9ff5d7cd0b96f497ed1bb1cee835f18addbb39dda66d20973c408ce78a0b049660ea5e93a241b84263c21447ec3db754141a6c8d20ecb1e2ec5a2fd908e57351f9de9253ebb9bb542191d01947e5da56b3065673595f5d4a903ff9b5e453dd702994f18c4dba4430bfd8ec44a216b91d83f41fd4824f21136e0e37a6b4a6daeac260cdd50f8807dc857ea579a5a74a4198024eee064fd57ff0570ce3e3da661190f989374f6d555d465947d0d04789b3e1d560d98f8b49cc4e0ae587e48cf1072b04080fb96ab99fb6ff7a3ed71f99d1ebc25eb36947504a9ee714bd830c8ea0899509d2ec0bf782f4e77eb445b275b477cd10e99e002322ce0113b60dc43d55f5fae1440529d681b80a7b8ee633a49257bf32e7acba203285c7541bdc4090f82600181a625354d26c743989be5bc78e3cab9d6ae4ab82b9cbc74cc7cdf7ade586ac984b6b41625f065f164dcf991fe5ad9faada0527e6c4b8b0852ae44b600f200b4f07da74ba27847d21b1239967bfe844f3e56ca7a4656a5846323b64f84312c2458fae85ce10adba666814642c09ee3ca6007ec653c48044c77894de09270cf02c3e7d7d2f80468c7d268e81fc5d446dbea4de1836c2039cb3b3f43eaa23c6854f1c7722ce03c905b9fbbcf9af69927934cffb30820b9b1651f9aa155555166f30bc3b77c35b979cc1b249721aab35410c8557c03233224d9a0dc5684a9fb65eb8c8e3aab72355990265eadeb786040a1246b550a6bb1bc581260848392ad212af612b769c2d66206706f71e0411d5eb90dbc80df07f3366edb3155fb83c36f2cf44829ae79d01a6e22a39bb5fdbc60fb89ab6cc5f4c25af5bc0840d17afeb856cb670f9cfd3d06a7f6d50e5c63dba69e8f5650b0be6681164abcc382fc8aa36d8a62714ea08277e90326fe2c90c6d7f5f766c6078ba7add936f4d97b615d8708091ba3a1a8566605e0f5e6f24cc3d5363858ddab7cfc03042518a5bf215e245f9fc5f1248f6aaffd165daad5c3e17d659c16b98363c11f0bc6ea287d471da504e4447f30d077a40900790e466fd57554d370c00e7b3a2a480b59cbf83bb94be785be2bd2d889c7a1e7a455f1c3c75272838543d67053e3b20e6e2dbe5e28a1c09cba16ef06dba85f45845a21a24ed20d618a88bc13b148aedea1385ce9db6efce8cd7cf80e556a21b33659b04300957dc5d54b5bdb5ae255ceffe770155c6a743cdb48d4e0746913e320428af7f6f2e7d0960912c459b34765badf1edfc3dd2744b48b9ed686aa19b130783b65c7437a1cd41a9dea5cf74466e2a1dd85fb8f5cabab7cebf7f19811b53086d8d63a5735a95b9a0fed335eb8b425abe7e54d77c6b4dfa47f24c3ce1e175eae21bea5ae63b5693f387b34ef1221ccac2f06ef06d55f85739b8e3e67868d0d2ce91b2be8050bd0aabc79d4c41b924d5aa1b63ed8393bab43b913e402fdb0f37890cd81943634664198f3115402e12a8ef72d2b1b433af5b1241301ae836dde09245785bbedc04fc236a8b740c7fae7d9af730842ded01807bd758d7d63d275c3e30f3deb365165d1a02b332f0bfe09ba03aca8eb220cff81be9035ea08b47cea5fd155fd8ee092e6b4859ccd7c7368a4d5371920e8f98800cbe8275f24b7d69b213b3e7deae3c59392910df4854b062cfbffd5f338c24583946d05cb1ae701989f99e5f61807177171ad51e6551af60201d68f90229cb8f7974df3da548ec2810fc28beb061c049062e6a5d919dadae1abab621338c527b8e0bbf21ef23920f93df5dde4fd3c268cfe433b189e7b66898e46a334f120ae725313dfdaa5117940a6648c7510921956c0b517e9271556b399eff2e034fc268bd102a20e6a5f8c0052d9ab90b85af47814e5330d756d965b5142fc8a9f32408092ae4dc14141082e4985e85d154f912783f8a0d589bc9d6efe9c5b2571f57d37a929f2d416af54fb7dcafe1b6fa750d6f11173e7f976e23a498872159a48f681ec902efc1300ce8a47240a2682738144137c31d63ec5f03385fcaa3a66b860bda1e7fc2fa7bca4b76ffb9552b90d0ea05eee4f5b9919cf71adfb035a33f450ec52e61765c9765c80faf7c87661b9a6cbbcf0003b400f6ee061d3a0b32fbaa7a267e990bf8b4953164772eedc2f0de0c4a33db23784d5dafd7e38843a90a9b91184efdc4dcecf013e60c89ed8dd4dbabe5f8671988ecabc6025f3db5229f036987b8483360086c377713f9d55969e3b5e0605f2b430370cbb0641df81f03dcd9007534db99a839a7f777c2a029670ddfcf9677368538e9ba9e4885e2299c1934a3a67fa9d857c352e3b80319159d737152abb7df5b5413e4b29a8e54256645974e9f3c0f655d7800692cf4460d6e90b2d2aa4d1f5f6fbda7dfd5066e9f69a7a3fbaa1fa94a9b388d342c2e8e08ccb8a00e9d417072db076a76418e7b75fb88ecc3093d9570f16032f4302f9923ac5da0572efb393d8311a32b81318b16fe9ba71b4e44f25f317ab8f1c9f508b96f44d3a3474dd160a92754f161c66269f74907431a808cc1f856d7996632e9d42a81a2abfd6d63267c4d20215ef59c1749544510b5361c627514bcc23b1dee26f2e80997e205d9c11db686e22d452d61abd561c19f924e4ed503049b0e426dcbd16cacbd4426bec34000c28c61ab01c2244e2300fd941187acccf420e71fd6eb4b2175300525122b2dc30cf1e2d61668e80620818cccae85541419024d4399e9c4add6e5a1c013564eb33d1498f59e392166a9ddc78651237ff292403e6ad7c3988c1fd375a7683514721876f9ee759d60a023429dba92eb681acf4716334565faf8f80dda47ad2cef1514db8b61893db3147c795a0ebc794a6bb6256c2399a24f12dceeee5ba8deae34a308cc6dab644f7b2ef2230a484d2ae4ae54aab6f1f34ad15ee638104a2e5eef71b759abe619193946d10d63b9fa8c444ccf9de65ba7b1a6101420b8ea2d59531ad9fc3640e9c16d1e8820fdfe9e664f2afe111b082e3fb81f959cc30a8bac84eae81351eb78577284ebbb08f39bc353f6cf164f28e69dd0242b78171d0e2372df535eac4275fea4a93d7848852a87653a20fbb82f704c9df690e0a81fa1f61fd47d204562adc6adbe4c2a8d69bb8ff6baa4d7391ef536f1363054fe0b9a1d2a90eac6108463dccd2fc6532f8fc0087755d07a13041300d1f2defa6d15f6ff4cdd59d57a68abbb92378749d9bd0aa4ef8e31cfc16ee7de65f716c6796e2c26e24b8b2bcab2f349f30e76fe97a634173b78cf274a76781cef79bc43938d1354956aefd3263ced8c27946823cd813b8e91e3b1d6f2cf8276c98065cc46a13d1c3f6fcdf118fd4a3358c15e6923181b981254e4cf04ffb03ea51055d5f25ea7b5bb308cc473850f7736fb3a261994f2e2742e412b0d3605fa0a1e72491e3f7fe8f38545b4a266724ae2f0e95184799212e0350a614cc3355e083c9b33521992d11e4767661929db45be6f86232ef6e4b4faaae2cf9e01340a636af272e950a9cc52831c4fe8fb43436a0233c47685b867014b780aad0bcf8551fd34fb754aad83156938b6907bec1931637fab98e26a1b185f2aad6f543d598287e9575d3ffbbdd20417276dfa805e7843cead279cde1ec93289e5f38e5b7f69d6842e56dcbaa63fb272bdacd7d71840220776d67ef53e23dc8123188c37d674357eafd0fa7050937d6f1360208296e342ea7488bffba8d254cbf18acb4c6d1047bfaa0201c6503b1b21427a793f2b5da733601e4f799c640feb43b666cfd0c62d334a6037f3e2642b77fd01264f3f40db1be5f318bbb425b3232c004a18285b0711a24ba1d2f", 0xf91}], 0x1)
sysctl$net_inet_ip(&(0x7f00000011c0)={0x6, 0xb, 0x0, 0xa}, 0x5, &(0x7f0000000080)="613393f6e7c821f962d05d25aa800201670f73711545fa211601ed3092c75ac659948c0b3fb20c9d55536c0c9442d751d4ebd900d85617d70ee23a14d1b51a81a45c118d9f2bcb859a333d5e16a7b2da390a01c80aa8ebea088ac2e10100000069bba8867223bcd35ca43bb3eea53d6ea1d205cd4c312eb5fe8523758226399d0c7e29d48a588ec59354669b3b0595c4f6759cdd4ed4abe07e760f9a897bf1eac5fd3474632835", 0x0, 0x0, 0xfffffffffffffdf3)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
write(r0, &(0x7f0000000180)="be80e61b944237b8499aaf7537407e16a46ac3e4f46b4115f5276f3bdddead8010588cd27de1d5f0562f9bcc5d5f219442f2d8a042d16203d27ec343f79b17b630cb9cc29f23b804", 0x48)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3a}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0xc003, 0x2f)
setsockopt(r0, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
sendmsg$unix(r0, &(0x7f0000000540)={&(0x7f0000000080)=@abs={0x0, 0x0, 0x1}, 0x8, 0x0}, 0x0)
sysctl$hw(&(0x7f0000000080)={0x6, 0x13}, 0x2, 0x0, 0x0, &(0x7f00000002c0), 0x0)
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "f722aa4b0e832c10184700dd47c8e9d2be670991"})
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000)=[{}, {{}, 0xfffffffffffffffa}], 0x890, &(0x7f0000000180), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup(r1)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000100)={0x6662, 0x0, 0xff, 0x80fdb, "2ff72001009cbefce1e00c23b3000000008000"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
ioctl$TIOCSTOP(r0, 0x2000746f)
writev(r0, &(0x7f0000000580)=[{&(0x7f0000000080)="c7", 0x1}], 0x1)
r0 = kqueue()
socket$inet(0x2, 0x1, 0x6)
openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
socket$inet(0x2, 0x4000, 0x20)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x84}, {0x6, 0x0, 0x0, 0x21}]})
pwrite(r1, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r2 = accept$unix(0xffffffffffffff9c, &(0x7f0000000080)=@file={0x0, ""/4089}, &(0x7f0000001080)=0xffb)
kevent(r0, &(0x7f0000000000)=[{{r0}, 0xfffffffffffffffe, 0x66, 0xfffff, 0x6, 0x5}, {{r0}, 0xfffffffffffffff8, 0x80, 0x80000002, 0x3, 0x2}], 0x3, &(0x7f00000010c0)=[{}, {{r2}, 0xfffffffffffffff9, 0x11, 0x10, 0xff, 0x10000}], 0x20, &(0x7f00000011c0)={0xf47})
writev(r0, &(0x7f0000001640)=[{0x0}], 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x9})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b2, 0xfffffffd, 0x81c1, 0x9, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000014c0)="969373bfcd8f7346a43a4b22dafa54653dfcc32b8df61ef715a3023e1c8aa2fc206403026b90793fbdfd930ab384949a79eac0e2dc18ab358463677a038ea89f55e110e099f920fdd4fd856a7f7877977fae39467b9333ceff637e67972ebef5b01c1fee3d57572f2236065fe2ca4b08a0fc8f0f521f76f5e4e6a91d0414def01c7c6c05493271d8efc3c1c81d3dff22ed8da1c2d7d5b6231b4fea6edb3649dd5265c369aef8a264a75944b81b9a1251f0548304e477745e44cd0067a49b795b3b85241774715ce5d2722a624ae07ca6986eca6bbe58499cdab97effb96b75e613efa27db82147bd8051e125f3224a1d7b8115d6a38b5f353cefaa72a91595439ab6c5cc6f4c32faa679c6c34a70e97bc23e8c36e288e13fe4661423b5bd5bf1a331a794589fbae315e25cf97d5aea2465585e90181bde6b02f3f344efb9f99d85a4b86b6ca0ebf42df3d20fe4f2885fc193b5bf29dc97b585361b891c24a5ad8d5dd6fed28403bd2ac69633c054cfd5481adfe9591dd4ac62866dc23e3ab569ff839970f49ce9068e5fafb02e95a116433b2712b94d7fc21fb54c48083f126abc973a12a651c560c29aa90526605136cbfe3df1816d66190893ab2d5b66771537b3a4bdb9fedb51f432dd1dd28dbed7d3e1dc16c7b2b1329a6bdc8affb8fa43d79c9093e9f2cf97b0a671517afdd9bdd9366a887ff25f20ac5b9e45bf52dd25e15ffab524323e6d5123eef76aca4eba26c34eb43f24eef742b52f0623ed306c67f46b3e77a02d4341efbf7730fbb813cb11c18af9dc7192e5ddc7b26aa23d168801bb7eae5ac324565ac5c19a9e84d24ef9a2968d09100a531612d98ad2361ef33453824e9d233cf0d158f5847189e2a9518864f4f07c18602c04b3be6351f0394b1d4a79d25d91aaf5902047d2d52f54c0657688a0d16e69f5d9b23ed635f6f8531cc00cd94a54bfa51fccc17fd69118f5deb6a6da20c5c341014133e5560253eaed7b49d99c894f0ef28ef8a66f1812c6c2f7dd2ff4c0389d50e132b17dda543fe62548398ba0f55c34f9c8fedae7d1795f6768a016ebe79b144c87c4db2c229da019812256077af014bae0d1a4ce7a6563de2c9991833b528b9293e425893f4bdbc3259c09c1696d177ae79f93c8db315f2997c13d07fdbfef79e2a5e1cb026d8f7aab4199f1e4c703bbd5b4bfb9989ed75f090d8fff1400cd6ed72fe11e8814c511e3bf04b7831ada5d4a734ef85d806b350a2fdc08a304f50db3df622d1450e6e103afd491fbbc26820da887b64897db3565583e5f859d3a7fda41705ad30d2bd2167394ff613c04bea5c736f78aad12469da38fcf8adb2e3cf0662c64eeeef4b03aa9974a8b5a75c29603ba9e1c32d446981d85fe0fbdd030c9ad106e9bac36b793c7368220e74d7e77d3c8c0157b52a072c8fbaa5542a43c39da68977f0c8b06b92e01d98591a03badd9fba72b608e2186e83b5f9a261e6f2158e7dc821e231cdd08ea85b6196f5d2051bcf96a1c61a275d7cfa32d45f71732e1e36a656bc5491c6759eead6e116977bc62bb016df60f6c0b61464d820cc20db3daa0189253fc92487be12303f8383825ea08e2d7c80535d2b7466cb321fd4b7ae0b6ecc8bfe3d637731df8637c25096bf32e3ed6dae0be7541b96fc26f90244bbf10b703158a8f287c94ba4a5ed41e5e13bf5376a4b9dc267de81574168f90dbe5292cde3060e3c0e07fa1e2d0b3ad7949d5b67752e9a1b7c1f876900d3425e7f3f3ea3a7d4e19f5af30f4a0de18039edc652f0122bf5765359f77fa9a8c54adcac014323c5e7abd1cbb4f750e66bb106c818d5dc231e0d9f3c41b87d2d42103db4f9118dbd7fa23718b6d8cb417169070faa34416004cd1c9232206faa10f9d2c10385041aadb0078c4ff613fbf160dc9b5e4c4d32eea32524089c2bb992e53e88558a1867732c312dc37613ced94b28154057b718095", 0x575}], 0x1)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000040), 0xe0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x19}, 0x4, 0x0, 0x0, &(0x7f00000001c0)="c2010000", 0x4)
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "f800", 0x4d, 0x0, 0x0, @loopback={0x5}, @local={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x1, 0x2, 0x8}}}}}}})
mknod(&(0x7f0000000000)='./file0\x00', 0x6000, 0xe06)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000001800), 0x0, 0x0)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001840), 0x200, 0x0)
r2 = kqueue()
r3 = semget$private(0x0, 0x4000000009, 0x282)
semop(r3, &(0x7f0000000380)=[{0x1, 0xffff, 0x1000}, {0x7, 0x2, 0x1800}, {0x2, 0x4, 0x1000}, {0x3, 0x42, 0x1800}], 0x4)
semop(r3, &(0x7f0000000380), 0x1d)
semop(r3, &(0x7f0000000380)=[{0x4, 0x1000, 0x3000}, {0x2, 0x6, 0x800}, {0x0, 0x0, 0x800}, {0x3, 0x3ff}, {0x1, 0x2e2}, {0x3, 0x7d11, 0x1800}, {0x2, 0xfffe, 0x800}], 0x7)
semctl$SETVAL(r3, 0x4, 0x8, &(0x7f0000000080)=0x7)
semctl$GETZCNT(r3, 0x2, 0x7, &(0x7f0000000140)=""/202)
semctl$GETZCNT(r3, 0x0, 0x7, &(0x7f0000000040)=""/60)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000300)={0xffffffffffffffff, <r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r5=>0x0, <r6=>0x0}, &(0x7f00000001c0)=0xc)
setreuid(0x0, r5)
r7 = getuid()
r8 = getgid()
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000180)={{0xfffffff7, r5, r6, r7, r8, 0xc2, 0x4}, 0x2, 0x7c42, 0xa23c})
getgroups(0x3, &(0x7f0000001880)=[0x0, 0xffffffffffffffff, <r9=>0x0])
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000001a40)={&(0x7f0000000040)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f0000001480)=[{&(0x7f00000001c0)="4e75b6e9cbefa1ebb0c874ca54386544a7d16a94fe355ade0303a683087f156f18d3788f005b1117f92916dcea8e5df3a7085a87d9d5f3673dd66fbaef6ed188b0683280cca317d2f11a9bebfbe6c3028b600a6d49890a653d9cdfeadb32d3bb59446c996300dd837d397166ca4010e060b81fc1198a2f740e7b2ee45a6cf12fce6c69538d773de6ac5e8979bbcd9f1b879924cc82d6598d390ad4197f89f85dd5b165e7855eed", 0xa7}, {&(0x7f0000000280)="2806da4a23ec2e34dcd540a746fc45487ed1b59c12aaf7c95e6a311f937057b31c9dee4a6d230e12856876d54fb51f511626ffcbdd1bd2142e0c261f48bc26760ff7aff0d199443ab1f1c6bc48d48b93a155c2865a6d19f4414050106d26b003ce0988024d5a9c2a4bc9730d04cace67c3649ccd24d71b1aa1005f54877f6ada42729124f8af92ccdc4b0931ade2fa2b44ca460edad762236d2e74dcea4e3a538f58b422a8d0748cf2542a39ec66099f659274e20e392c4758bb5166882bf61626fce3759fc75371316c61eb3dbb32537f8bc8899d7f1de6a4b1978586f43b18994f27357abc4cbcd79f7248d74225f6b4c4bbcc4ca6bc6c11fc1fef7cf7e68a733ccaab9fb8fe2c716d969fd7d113c66f41460a5ad35fa92224f9b9d23865b15a529484d1ea838c0bd1302e5b108a582c399eab7bed003b7db10b43215907b3a59f72301ba877bc527ce4e44d7e130b459c7edf366f8a04bbd41adfd87e8996c5be4f54b01492855f086fef034a665201b0c061fd277fbd79e1e1befa80ce1d4139c30b69a4bfd8227da1f3dafa879a8f586f32f37f248ff1108715685359b4fc1d5627b9483b271cd62b618bc682209186eed5caf6462b76be44cb259d2f7e6abacada26b50a97dc57f0ab458c2e9c9a2f7cf9e4b89acb0132c7720237e4492f691a257d4588776e7d878aedc3cbaad388bef9be2a247e3ca4428a1ac95cc0e2b0ab7fcfd807c6da5a852a54b994c6754e6e9d66f9fc6bc5d46e7322550a852c3a8157eabb8c7f6b3bae292ee0b39495b5bf85b3a057ece098486d15076f14d2e8063ec603862507ff8765d05b63dbcd25286a8df05adca3d8dbd21c9d513b121fe7a1cc64a466d9fc7b60dc1dc608677a5a61fea8ddf1f09027025f5bfd82f0f0fa2d840b390425f0b7307319d1e5434004a0263cb8ede4489ceaaf007097913f4940cc38b9d581b8cdcfe9f3e6fa0abfa3b094d33d7c676de799873bca512a1a893b30662192f5e9cb4896f405f2e30787efb142d0ac973cab782ab067710f2c5374a9778476170a637555fc6228137678f4fddf68f6a6e35a72d928175b0388ab66c933b717d266dd2ff02469c2525692bab9fb205f822bc10e564bd0bbc258c40e65ba6a2ed727bfab2e55a88911f4c31fd708a1303102c1351e69f94de4bdeb3942005c2f8c53e020af3bfd23d98eb453e4836f972127ee035cb7e6d71898d0c7238c1dcb4a3978b2a32f63c17d582941742c0ec68bd3b64e9942cf215afbff2c4c8234e8c220bf8212eab42abc8f9b751b8547b6b53133d8897acc7784b6711f499ef80b69c82de968c30cb633101f7faf563c75a99b98a64e7389abb8591354c527589c85d65ceae3ee8dcb01ea7c8762b0249a73d6a7c5c86411462ca612f194701af229392f8572b24af6c97e5e47f4cd0676879f20cbc8aeb15f466a9d323d380684cdd245e5395b49a9e1f25e755c0f5e2ed5163202cd1fa3d37c1d30b96fc493fcb673d7069a84e86bea3f52a8c6ff866c619f9ac1e2139ed8f32b194164607f10aeb98caef9e914cdd5a2b6a964677e99d3cfddfaea0bfbdfd3840d842bec4a4af2a75243bdf17d5b26873537c11ff7e29fb8ad9b07adb590fbffb014fc01ab79023afce42e4dd665d71981b3cca36f14810b8dd58981ff8612c2cd6278a1aa4fb63d960334ed569a9cac20ff10a47a9be4449a4987baff1ed51ab1ebe454bd167225fc74c4147bf12612bb12fca218482dadd9bc45e1717ebbfc1801939bb4498b2b061bde9a4a55863047392d4c5eff3bc523df86e349dc3613be2b80aea2872f546e0abfaea9bb1eea1a32057ab56df1b46bcd2003ad73c36b2d8fdc7ea25417a15fc8f7d21286da040ce2fb40702a3962ec02b601f6f7973eb3fb1f4b769e76c414f0be453cb24e6c201720cc5726f4082b65d710441855810880dd1c7e5100b0fece1f53ce4e36663ec77f4c4bcd75b3b5a88e8eac40b4ea959af12b4ece0a5f64dcf0b037c684bf1e881c9e31dd7cc36f1a4e289aba23c253c4b328d2fb68612c3c1087a5d814c715b1e6e839cc77a540f3bb1ac46e326d74438b2d6fc5d55cf73d79ea7b7d77ff5c2618f31a48e9287396b79436d6ec263e0795308bda69ec2feba7f90c2d0d57723386da526a7bbe048f95ab11ce0bf85090d72658c8c768af8ca392997dec55926e82bc234c23c7fbbc7d495a6115323edf5a5d173b444e9e4fbbaecfaf5e3ff2903f0018162abf67282d08183e2cecf5a0531632996468e85eb501f8c076f685a7c826c1fd3d9790dc9f6fa4831cec9e5d4f275124f939ed65c120b9260ab4f765903c4a546833b3cbeec2264b6b671fae8b134902414dba502d76f9967f06935c9a7897af6ea42147ef8f882bf19e0082e89cafdf960872680462f5705dda6cb72b32c2a5ca455c16480b66785d224b419d31322b73c9e8fee43875eaf568a4356382100f7e82f7feeb2e7a767c28145827c36c1c82e60ca2e38df90def6ed90baa04450ce6f6c2b96da6801ef0dabaebd5302908a711c2387dec46ad7b2cf7613451c13c0c62b13746a2b16f989e4177c23fd296721f3cbfd1f93a8174d0443a8272a644e81d0be4312d2c8bac7741852cf44aa34131db45c2775e9e6f732623edb1156d5f88486e42ffde2ed3352105556badf152aca24f8b482bc36d371ea4d8059543d864060e696f937c913843d3e3329062f5c7fcaa9d0048014f78491aeefb3d82642d4f7152246e90d43201b3a810160e748294df8c2c42d79711b5214f3fc3a3e8c26e4978487b4bf1655a49d3aa83c256c9147b69175a9ddf9be9cb8de9ffa8df2872778c90782ad58b039661b2511b6ee25ee8af23f5ff359ab031f759a1e9e0f1005aca7d481cd043328d347dffad6e1233d240127f94e6058ca1d40ffda1aee8057d52415a791bf070838fa417979804a02a21c11f286ff4c5596add88bcbee6b6f35308cf27a7b3d7d96b0d4d10311809b3de3d5b39e31faac7607e5fc66245c171f1d4466996bf04fd494679dcd50f14eb01dc7ccb8466a3a3c279b8d1b84d681f98e6a940f5ab177801f040d99276b4c2bc381ef1bc83c882d4fb48ba225cef29b3b63f74ee45199c302195a2eb4c8b04724ae4e4875adacad9690e53a6045f334e31af5ec3366f0548a163bcddd5273069316e86ffe8caeb851efeecf50994023e9062c504fe4a74ded6eb40f59978a0ee8527f83069e89d0ef961c0bf902a4e77374ee79e351d99cea63837f1525904bdbfeea5f73cfa5dc0f622c9f3ca287cdea78bdcb731472ad3073a706a96e6e9b6c82c50e2bf26d3059fb4951990038c599c389c50834dec1cd7fa0ba2cb8999814d15bf9581be82ddb11a3307f4d1765a5e5c4b149e0a80f88a8706d7247d3e5d3882c2968e21394f0b82863f87465b8eb24b1569c5dd66e3940f2af1f50d3a18d2671e103ae69aeba83429f79e5be5f0e8c4ca68fd09f4771f9f8aecfda2b1a11254aee81468074e458ef52a33bbce035179b71aa85f3036875a61ff4853ea7c7af51c96e2da480bdee43b21e4e3919ffdad00bf42bcad1e1580f4b3f1b432028691b23e4d1839701c58506d450acac45d16fb2b321c90ad2408d542546fb77ab90ee6f953576dd8914a20f9609bd24d390adf0b5286ce0844b978920746929339446a78a0fcd414d2b01a98dd0e737a2ff202dce556888557bb23787878b2af4f9c6e3601b54546bdc0dafda3265316ca6ae602c80ff3ffdd5f88463ac61289d9f34251a13fdf8ff52b35efdda2d07fb8b19db20611b0dd66a21b8adfb2681daedbbb0a2f1e48446b51ce11a7e96142e8fa0fd1e81352a739b86641005139224b22a7e913c46973d6e602cfad6e9486e87ceb6eec11b24dc72264043e1fbcaa801cfd1ebd4c22a9ee5813af6868ecd126baeac8ad5bdc384bb777b2d5a6a0c9beee6a8488c591a967d0a251a21b68e841fb80f758ea495a05be439df94a7d15a8429d5505fdf5fabc78aed9a288bee82a18d21d68599f0e3c4e27f621910503bcc00ac8d53d0bec0630af82270a8422f5ff4969c215dbab0012a6b3117efdf9a127236bc5826481e7177bb3a2c6f0e9fe363aa3549a83eb7ef5b22d57a8e41305706122fd26cfe922a653526eb7a5ba15b5ab7398cb13e6ca6596ccd6c65899ac08935cd9d9175092fbbcac8d6b0b05554a2c3e571b3b203e77f39a9a406aae58a78f91906bf104c16cbf5a1377ab987e973745f494bdf3faddbe274debcbcc08aa127a8f2560bdd04d61ec4b99ad6ff69586c3fc242fb7b9194909bca281bf3844ca24af4746856d74af150dba97888d8957678d7c3cb149f1bc9ff454bd7b3df0e394605f388ee9fae87758c74ece8f605c46b535337d551d77f72d8c0438dd46aeb4f8b1647df3786f0bdab23102b1bc7f4d85835c216dfbe3a0375a0c0ab4a83d9781fc85f7045080ebbedb425a109d5256875530898abd5acf225941aaf8e52f8dfbc17315359fef13c829f51d1ee57bb3a9cc2e13a39b395b99014adefe802e4b706de0e596e4fe73db4941878baee578b3615f6e0bb57334253f1b5aa1c3812df49c61f9e1197496c0ee5735193eaa58a0836bcf634ab2fa212501858bc0e9d885d324b31f30cc9bc91d6b837da55d188b4003c760ebd8419eb4b75c4ab19fb5e144689b08882bf1f21ee569b94815b33801ef7f3dd4b6b06647dde3ab35736a2cf7f013974bc06a0d88daa6f7295ecfa613ac00b14eff27961472ceeb8c7e1993d8df22d569ccdf5f195d2bf2c80f5c0fd4e9af356a2b44b27a7ceff11b400713172dc3f20f4d86bca0434e0534a09581ca568bb548ed79363bf1eb9cadf357537d50ec013997fc58e1416bc3ca0298e79ad0af635d9610a5ce2cb1dd29ad51f1b5b93543521e61651e2340de48eaf1614c19e0f83c980ff1d843e2aeaf19aafa18e24786866816fb15291a91db67d5c77888e0ee9446cb3fa4015ca57a61a77e706c8d75b79d21b1c7df173a363482a498baef18b5fda36221b6178c110236f2a39095c31d9866adaab90ca676f0f7d11c65a713fb23c5315dcd3e1149d9a61e2a05b1ecfd2ec92712df5b7ae37b67ffeaebeaf056730e55d954e35009a4eef506d07c4d133a4ac144846863724f7a3ec38f993c1f728982e0d8966fbdecbea455595724e58038ff49030a794882cd8450c976b4afaf41da7a1b813ee9c900f4ba5940869ebf4541840a804cf8f4a88d87faa6097930c4895d191fb2727ffa494b18c328d26870c3d6d1dd6e5c7633d54dd7263127f355bb7228d8e81bdf6bede7431d30782856eb343353800005a1002aa16d04e0568b6f3cc022ffc06962973a8dbe80dda70ff434c22041202d0b74ac831028af123622fca6de986982a5f0847e51d31728d8eda3a842c57189b59752129b4a3751bda5c8055bd027a4e494331546c08792e3b48992e67dbba17a2989f969fb9ef850ee29976b1c63bc003b239d0a1bbdf13c2cc15dd288ae21302ef9cd31682654d9fe78a0ce1a032731bfa75f9721681064a7684992df857025c094d5972bdb95e77fdc2caf3fecb3afc5746361c27519bccab52012ab9db96f13a1ba0a8e85c5565ab452f60dcb1287806daad00fd5598fdee7880ce7c1ce8ecf33fb4e2e53171aeb3fc6c2f9ae64894b0210c9367eadaff6fec02e0d89b160364472f9651ef25e91dc20cf017358591e5017a3a69bddae6cb1a783ec025850fd67eaf01b17ee4cde21e69f49ef721fc02de2c76247107754463d8426825a", 0x1000}, {&(0x7f0000001280)="64768d6c261ab13086", 0x9}, {&(0x7f00000012c0)="1c84cd7a999822b34d4dd3cfe2973637fd6b5c1c1404da0753d188b5e53760de6320ea96176f2098c0aa17ce41d2a150e66099f612b6bf1860535de2d8e9e38be514d3be8e904e008c0184b88547450afaf94d244e7293442e42c174a65adbb92b8f4fc645c19559a4bdf95f38a792ba54b299b05f53b735836f961e98711c4f8bae75e41e081dc9d0879ade1db6685d46504a1006e7f0815edb70b77ef7c9d48073e4298a1f34ae8c1764ee4a3f0b7c7ce8da1479c479e5c08829127fff18d68aa61b9084f897256334e29c5d6cc8f51be6e119f6201cd760660b86d05af6", 0xdf}, {&(0x7f00000013c0)="9b162b12c38488b223afff54fcc023476c975413ee559ccbea7d1f26a005ee47cb342695efb6c2124fe4830657f7801caf6fe79e79783ebc8994f1e257a09a94a5eadb95e2821747064bd627588ee9d7cc6ea6401f33cfe3a6bdacd93b24f5d6e51747e6cde8f04df705259e5b6d417d66e55ad9c8a0be32c98a13556fe9ef8e2c636b0ff92528e2cb545a23167d345519493041608eaa93e691ca540b4fbb90d536", 0xa2}], 0x5, &(0x7f00000018c0)=ANY=[@ANYBLOB="1800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYBLOB="2800000000000000ffff000001000000", @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYRES32=0xffffffffffffff9c, @ANYRES32=0xffffffffffffff9c, @ANYBLOB='\x00\x00\x00\x00\x00\x00\x00\x00', @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32, @ANYRES32=0xffffffffffffff9c, @ANYBLOB="3800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYBLOB="2800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32=r0, @ANYRES32=r1, @ANYRES32=0xffffffffffffff9c, @ANYRES32=r2, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=r5, @ANYRES32=r9, @ANYBLOB='\x00\x00\x00\x00'], 0x150, 0x9}, 0xc)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x1}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f00000000c0)="7343c9f13a1a65a5dc41e7000055b9e7c7e61e80a00111703c8ad427b38b2f8aa720381c97827991a34f07000000aa8428b796be7c3b0dec5cf124fe01853cbb22f62d6fc07aeaae1028cd4c8391f916efee16d809a6a0b0b5f077d55f10bf208af9f63d4fed291214c315c5459032ba78cf06e3e6dff86da9ac815ad539c221d0d8894615ff18fc678944d0f65ab486924ff505c7690ded14f9ca833b67bfd24a41b9c62e63182ec537052a6472066ce21532deaac00c6ee5dd8cca0bd9c132e66331f794", 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503000000000000001709", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b10005016000009005001b0007000000331c04fecea10500fef96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c50000200200000d230000020208a371a3f8343712051e1d89e0000400000000000420000000000000000000", 0xb1, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x12, &(0x7f00000000c0)={@remote, @empty, [{}], {@generic={0x88a8}}})
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
ioctl$DIOCMAP(0xffffffffffffffff, 0xc0106477, &(0x7f00000003c0)={&(0x7f0000000380)='./file0\x00', r0})
close(r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
poll(0x0, 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000003c0)={0x0})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x3, 0x0, {[0xfffffffffffffe01, 0xfff]}})
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000080), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x60}, {0x60}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x81a, 0x7fff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc020697e, &(0x7f0000000300))
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000300)={0x0, 0x1, 0xfffffffffffefffd, 0x1000000000009})
sysctl$kern(&(0x7f0000001280)={0x1, 0x36}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0x20004455, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "abe66bc8000000020000e300000000eaff000004"})
pledge(&(0x7f0000000000)='tap', 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028698a, &(0x7f0000000000))
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x0, &(0x7f00000016c0)=0x1f, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt(0xffffffffffffffff, 0x6, 0x10, &(0x7f00000013c0)="6d66b176", 0x4)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r2, 0x8, &(0x7f0000000000)={0x0, 0x1, 0x0, 0x1000300000000})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001600)={&(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa, &(0x7f00000013c0)=[{&(0x7f0000000140)="149625f958cc35382db1e633b56d28", 0xf}, {&(0x7f0000000180)="93039be257d69585536386ed133a49cd9d273c40f54e79da38dc47ac9bb4ddc2cd1da4002d667854c8c0ba8d2d56f4b45891346c1df70806614b0d9a67f3078cd246285405e7583bc88b0ad70b24b13809a19958b4881a82cce26093a06d1890d32427a58d669cfa4342ea6b08ca987963315b249127e346f9ddaef6c0ac572a9cc6315f204975008c7bb804b859c330f683e4f672e6ffd1ca786a88fa8885d1b2bd039ddcf4f570f05e8516dc081c313c034e196843b7d6e929dd1cf32215852574eb888b6c1334e994048f2713e76243590db0daef4a91aa544679070ef61c5114113cdfdeaa90d205265bdfca46af859ca318b437732801ef66d664979b6d0051f0959add369f6610ac2e2ebc39d5a49c1c78d865337db4d579cebec2af048d416222ea68e3736b32e4ef8a85a950174c370952817023850988761709274cf597e622ee3b90d41c3c7c4dae92fb803542575ad4060321867699c6a23c4920e0d60061adf371d316320316cad8373181bcf9cbc327fe7ce9eb038019ff77c6bf3b56a27381078f708073620656e31f43903292feae974f0100b654141424c5ad76e14c96926121dad0f1fb561a5ec22d38952795588c979f3b0c4a50fcc9921df9ec066ad7f15b5508bff86077ef572748412913df489d001c0165d94ab865fc1adf623bb3c8295849329d95da2b84b8b450ab7afe0d00cdc2b24d6c2197de77697df34fce945c0a0fe78506e366ea42380ff48b0fc10d1685540ae1b1beeb05b1b9d7a797efcd9f24a7a3e3b75384734ecd45f8f6fef61ae14e18c63a53856f225d4ba30a1577379aa0ad7070d65d852348cd56a4a9b9ee2d5db5aba57f8f83d125af31698b56850d63ad01eb09e6471c17f6b027cdaaaf5ede9ecaa79b054f25bc26323b7cc73da37831d60db4d058f4a8739a7b8769da7d1bda560de2b5666ce5387957e960da0cd7c178c64d2e6647cf96d4b1152e043b6492686eab31e754de238d3253d0d98aa61415e8752dc4791e7e2d4e1dea3804ff7fa5b8a18b1e59a7627388dfd419b920babe9d7bafe9e01051483b8592a1c3d22d261bacde42bb512ca06f6efd5e52d88e558852d4e2c682e63707a57d86e82cdba71c698b5f2dc09a609f43aba11a147b1dc8140245136294c6f1289a22e69daeb1dad3a599858bac24a287d0fb403d2ee20ce9d921133a1183173317669e7104450bddd7ae75b8e2ab8c4310ca9b5c8b71e47a232ca04afd1465e7402bfedb2170b1f09728aa3e015f949a39dc944d682374f7b251862f0ea8a35f01d1c51a903bfe5aaa673e1697af2b50a4a556cbc96c518d18f8bdc6b93f7c7970d674d279fec1e62d0567a97d8945842c6007522e6b9fa02ceee020a24b8cae3cbbdd0abf148daf35691a1b91ff35dd8e6bac6605d588d8e51c51eba6100bd8e1d4cf0e35cfe3622926cb2ca7e4a95d606393be284884ded917d8e3e6852d060e396a9d288b1f1b70cdcecdedbe2962fb8545e08d52977390d3a057d47aa93d44c7a380eec6d62e101c24759d488f1a81e454833288dd5f8c59e93105ab31a25492ae8ea94f7aeb61439f078ae017743d264e2377e8abf61f10be6a44f26d995a961ea53e3d30d080d5e677f19404328764d3a2bfd9c43563e6f6ef37f85d8ab51a2bac14a377f967d4aed2d319be1a969c71728b01125bdfcdaf0b6c3b58e43ed2020c766eece011e91e6df823cad2fe7d3921d36507a82e491c5e5aa556ef348a3fa51cd507a5ef52aa0752ddc753ebe0340ee1cdc95d0729128db60c869eb80b8686bc13b83e3e51018a64fdfa6bb2edac9186fb1c12c970f5610159fad8619bc7262797f3b6c52df5fb7018d8fde099df7fb210004cdfe9eb4dfa3bdc139bff85c58645f42392e97b02ca5d597d57fde85d3e8b418518f12c76bed9cd21bc44b682a94e35bba148ab15d1751a2f01a76b3de331a24fce28c5863e918694fb265229ca935137554dd159cae435183b75ab9ac30752321593377a3de3bd9f556d01825399ad69cee871a213d97f2d86f557598f218d501b7a0fc055eb4cd580350d76b897dfe90a558592b7f87f68aed92e8e7e5be8100cb1331b815d0a0afefd595b9284559fd1d8881ebdee3f8fccc8d8a8c991cdb9f67682b9aff53dc925b1fbc54adf2773c5463faa3ce15c2a440ed64ff30da1d036fc18bbc9e1766a4126bf258f48e24917311a73ea47dd263868edf691c73820a4a64d9746fdc715b8bda49e9440bf35b6fa057c4c2f196a7e47668a9b0e1ed940e3d42212d5353a52260b1d2c22ee6aabe31ed45b68362e855003dfcb2e2d0ed2792b919bfe7c29686b4594c8024d131d9cf5b24467f4811a4007df7c6207e10bf5a4a5d3732279dfdff599609935624e3a09d5c3ccfd3e83aaf0340a31f839c3b71a774e457cd4bf358301a90a0cef93ba829aa16d5f8afbcf58855e648dfef9c8bfca852492d9fecd7f6dccae2c68e5f22825b08590916ed19f183456bab985c7c8bf161a79f4aaf775ec4b418b358ea54856d36d0fd465962386c75c3b06417552ace595c69bfc471b4dbee0eeb2ba546911df93b475e77a48938b97f11093348f7418413712b0770e0acc996c28734fad9f750d50b3f5edcc856b5e9dc75fc887bb89d3ced17505ce229e534c5c2b416d39200c41e828ffb792194934b8dba1451f2d554098c94c762b76bee4f23bc8749caa74fb5cc247ebf8a4c94f2cfc08eaf0ee207b8f819f068d25718599cb87f86f032df17977c73c3425a11e4e537bb0c8ae91eeba833a2c9a833fec99816ecdebec68747d7b2b7367334c5ada175941c12b27015bb04df92319b09be34ccb2189aeba3ccd4ea96742741ee142fc1d6d66086d24e5ff7053f3b1e1c088a2", 0x800}, {&(0x7f0000001180)="da497b36dd93ddfea3c306aa37976a4827703b52a7b339c8893cb3a8c806bf22365ad2d33de1435a400d9e8f34b2763f8debbe12e883e0f7a3b4d75c78413bcde227914d2e6d72048892c349656b42195c744bbd50bfd46c3e9d35b9fbe6dc555220dd80d05ef97858a9602d17425c9cd76b2b218800875c14655adae4fd1f9f6710a02e41294c0d612b1055837323065d8d4f0e4a153ee45baff6665fbf3b9cf88aeafd15c4c884f72a416b192368258ae1ad6af7caa75a37c28d88aa8344b0d00e57b6f36fbd66c8b4c812ef3be659f4e076", 0xd3}, {0x0}, {&(0x7f00000012c0)="512db7595b35e41ac3837da31aeddf86b8f76db8eac58277034bbfc14da6ef3127e4f7d57415a1afc3d4b02e0845e887a554beb372082789cd54bd328646a03edd0afe70b28302f97c0ad3f9cde7c97242eb4b33a1793f5beb281afe104af80a55d3a8b542203388b63ea9ad2d25aeb6b225bb89612e28c24f14179a7f32f2e5918b1149a430e0364759eaf83281e89ec84e62e66fb450acc94611f9f3a12909b8b60a455adc6305b0e0026bc40135101fe87dcb4ef7f149f160b875c4ef4a173b2bd6ab77cd7a0374ed0b5a9ed1ef5b274ae020", 0xd4}], 0x5, &(0x7f0000001580)=ANY=[@ANYBLOB="2000000000000000f3ff00000100", @ANYRES32, @ANYRES32=r2, @ANYRES32, @ANYRES32, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYRES32=0x0, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32, @ANYRES32], 0x78, 0x405}, 0x4)
r3 = accept$unix(r0, &(0x7f0000001480)=@file={0x0, ""/75}, &(0x7f0000001500)=0x4d)
sendto$unix(r3, &(0x7f0000001640)="554d65764d12342c6a5f617f229d1e4bf82cf65b8a06ec7d8356d2f9dcc120dadebde9b7de18c3e6d9a9f6899c77b90886d2dc4f9b67bb779f810f4948d77687a34a156c3d", 0x45, 0x0, &(0x7f0000001540)=@file={0x1, './file0\x00'}, 0xa)
connect$unix(r2, &(0x7f0000000000)=@file={0x1, './file0\x00'}, 0xa)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000200000/0x6000)=nil, 0x6000, 0x0, 0x10, r1, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x2000, 0x1e6e)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x1, 0x210, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x800000, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x0, 0x200}], 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
socket(0x20, 0x1, 0x3)
sendto$unix(r0, &(0x7f0000000000)="b1000516600000000000000300000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
ioctl$FIOGETOWN(r0, 0x4004667b, &(0x7f0000000000))
r1 = socket(0x2, 0x1, 0x0)
connect$unix(r1, &(0x7f0000000480)=ANY=[@ANYBLOB="83028b52ca7d3d214f31b4a5ab9304fd0d065bef6fa1303f333237331babc1d6fdab9f118407f8ddc132891ae35c983ec2d8937597ad6c4cac53b927c8a7545a99d731554c8d55bb5b634328df6b673f3dfe5d889092413bbd90465bd5947a9e5b0661a0050324688374ef6e49cfec54bff14bc7e91293d206db53de68f04bf51f60c55378d41e1d00858ec562471501232dfd4b0b2948df0cb64e08fb65"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, 0x0, 0x0)
setsockopt(r0, 0x7, 0x5699, &(0x7f0000000100)="166cbcd2ff88", 0x6)
poll(&(0x7f00000000c0)=[{r1, 0x41}], 0x1, 0x0)
setreuid(0xee00, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000540)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000580)=0xc)
r3 = socket(0x11, 0x4003, 0x0)
sendto$unix(r3, &(0x7f0000000000)="b1000516600000000000000300000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r4 = socket(0x11, 0x4003, 0x0)
sendto$unix(r4, &(0x7f0000000000)="b1000516600000000000000300000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r5 = socket(0x11, 0x4003, 0x0)
sendto$unix(r5, &(0x7f0000000000)="b1000516600000000000000300000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffff9c, &(0x7f00000006c0)={&(0x7f0000000140)=@abs={0x0, 0x0, 0x0}, 0x8, &(0x7f00000002c0)=[{&(0x7f0000000180)}, {&(0x7f00000001c0)="eac7967b410da0c37d3782a8411d86247b9ba14a0b925dcdc1c0e03a4ed640dbbd25f68c3ef7f85d91c5d5cd1677c201b2009ddd91d9009f5d2066e2bcc3bbf9b1ffd9c4c2ad6a8b15ccde57ce8ff18ceb1073b68d1b31ae95e1e02dad65d98332c1a3eb6aa817608e557d820c7f531011bf998f4305e3e56b6568935d94cbdc1cab7d3920e7ee770fa1217a5d66f820304f806d8006bc07d2c90ca426931f6466be3ae2bdcc586ccf1bc51cf14f186a", 0xb0}, {&(0x7f0000000380)="104af26fb078793889d56aebdda62dacaa25d838acd8164c1d7120e75142a38bcf0a58142428e0a0e15b27cf619a33bce7a5d031322b494e70020175f9d00efcd10228176057464bf026ff0d95589bed0c0d856c2f367c81fd027015dfb13adc3652edc8aa5f5b83f7b8016188e321a0cd472754da7618df2000be91901fa846d7b2702c7f0805e6c984a5b21f6468b5f5ad229c28730387e290de626f7490491ed9e4e39023289532ae3423d07cae9687d824e7df3e14c87d5b9872a493f0e9376613bd343d8c334cb77c624e0d755191043f552ddbfc2516a5e87ff83de708434e16e6ef3ffe49211c77366802278c648a", 0xf2}, {&(0x7f0000000280)="b25f4e0d2747a54b9e2b4a0df69d8485d6b3e012a7727cfa8950b55a7cd58a9cb7bd648a30e2d920f3b7a50e5865c71554da00", 0x33}], 0x4, &(0x7f0000000700)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000001000000", @ANYRES32=r0, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000001000000", @ANYRES32=r0, @ANYRES32=r1, @ANYRES32, @ANYRES32, @ANYBLOB="2000000000000000fffb000025df000004a65ff995d51bf3ca763aa4998ccc7102d47e77096045c77ffb2e1c3c0a475493a8d398ea33a1546473175f717224a9e112f14b68c1b0c37942298d8fcb6e01b00600", @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32=r2, @ANYBLOB="00000000d600000000000000ffff000052480000", @ANYRES32=r3, @ANYRES32=r4, @ANYRES32=r1, @ANYRES32=r5, @ANYRES32=r0, @ANYBLOB='\x00\x00\x00\x00'], 0xc8, 0x10}, 0x8)
shutdown(r1, 0x1)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x4})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x2, &(0x7f00000000c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0xba, 0x0, 0x0)
r0 = semget$private(0x0, 0x4, 0x426)
semop(r0, &(0x7f0000000080)=[{0x3, 0x0, 0x800}], 0x1)
mknodat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x833eee594ef524ee, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000001940)={0x0, 0x0, 0x0, 0x0, "4a865919a6d17ffd6f7bb8dbcaeb31aa7b8c0a8a"})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
r2 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000280)={0x2, &(0x7f0000000040)=[{0x50, 0x0, 0x0, 0x5e26}, {0x6}]})
pwrite(r3, &(0x7f0000000180)="f781ea80e21bb6f576c45af84a28", 0xe, 0x0)
ioctl$BIOCSBLEN(r3, 0xc0044266, &(0x7f0000000340)=0x7)
ioctl$VMM_IOC_RESETCPU(r2, 0x82405605, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
r4 = openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x80, 0x0)
mmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x2, 0x1010, 0xffffffffffffffff, 0x8)
poll(&(0x7f0000000140)=[{0xffffffffffffffff, 0x100}, {r0, 0x8}, {r0, 0x4}, {r0, 0x1}, {r1}, {r2, 0x4}, {r0, 0x1}, {r4, 0x4}], 0x8, 0x7)
fcntl$dupfd(0xffffffffffffffff, 0xa, r1)
ioctl$TIOCSCTTY(0xffffffffffffffff, 0x20007461)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x8001, 0x5, "d7150d3367f027a25d738858db3b7d138f34e836", 0x0, 0xfffffff8})
close(r0)
ioctl$TIOCSFLAGS(r0, 0x8004745c, &(0x7f0000000300)=0x2)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000001}], 0x0, 0x0, 0x0, 0x0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, &(0x7f0000000000), 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$unix(0x1e, 0x3, 0x0)
shutdown(r0, 0x1)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x1f, &(0x7f0000000100)=""/122, &(0x7f0000000180)=0x7a)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x5}, {0x3d}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x4a, &(0x7f0000000680)={@random="1a7a8fee969b", @local, [], {@ipv6={0x86dd, {0x0, 0x6, "eee41a", 0x14, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @empty, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}}}}}}})
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="01080513600000000000ab000004000000000004fecea11ea8fef96ecfc73fd3357ae2ecaa0416fa4f376336acf00b7804be781e2fc2caab610f53c2297be1aa5b23ed00f4c8b2ca3ebbc257698f1f132e27acb57ad602000d7d026ba8af63ff37", 0x61, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000502", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$inet_opts(r0, 0x0, 0x25, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x3, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
semop(0xffffffffffffffff, &(0x7f0000000000)=[{0x3, 0xb5}, {0x2, 0x5, 0x1800}, {0x4, 0x101, 0x1000}, {0x1, 0x43, 0x1000}, {0x4, 0x400, 0x800}], 0x5)
r2 = semget$private(0x0, 0x4000000009, 0x100000010)
semctl$GETPID(r2, 0x1, 0x4, &(0x7f0000002840)=""/137)
semctl$GETZCNT(r2, 0x56a71968b62f282f, 0x7, &(0x7f0000000140)=""/56)
r3 = geteuid()
r4 = getgid()
r5 = geteuid()
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000380)={{0x6, r5, 0x0, r3, r4, 0x40, 0x6}, 0xffffffff, 0xffffffff80000001, 0xbf5})
semop(r2, &(0x7f0000000080)=[{0x5, 0x4, 0x800}], 0x1)
setreuid(0xee00, 0x0)
r6 = getuid()
chown(&(0x7f0000000100)='./file0\x00', r5, r4)
lchown(&(0x7f0000000040)='.\x00', r6, 0x0)
utimensat(0xffffffffffffff9c, &(0x7f00000000c0)='.\x00', &(0x7f0000001180)={{}, {0x0, 0xffffffffffffffff}}, 0x0)
setsockopt(r1, 0x0, 0xc, 0x0, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5b52f", 0x91}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e0174685", 0xbc}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
sysctl$vm(&(0x7f00000011c0)={0x2, 0x8}, 0x2, 0x0, 0x0, &(0x7f0000001340), 0x4)
r0 = socket(0x10000000011, 0x8000000003, 0x0)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x2000, &(0x7f00000000c0)=0x2, 0x4)
sendto$unix(r0, 0x0, 0x0, 0x1, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0x0, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000140)="ab", 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x7, 0x0, "2bc12d237f05bfe16ac4a8677c9d38c67057e321"})
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000180)="14", 0x1}], 0x1)
dup2(r1, r0)
sysctl$vm(&(0x7f0000000000)={0x7, 0x7}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
close(r0)
r1 = socket(0x11, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000100)=0x9)
connect$unix(r2, &(0x7f0000000280)=@abs={0x0, 0x0, 0x0}, 0x8)
pwritev(0xffffffffffffffff, &(0x7f0000000140)=[{&(0x7f0000000080)="c820a0be6a55cb70e18c97b3f491e7e61a7959915dbc5b1e5b1d8d37c36e92018e0741eecda2cc2836719a03d07fdc7e20de3bbd6ca9c84e3a27525a43de6c7cb4376042fff051103a5cc0c1e4ca6a25de8c2a34535f315a6751acde661a42576ceafff24e2b482fdde0f61ce5ad3f3b51392b8de61b69cf0cb7903731188bb9c483164e531813b09884d8120a603bdda21a083b77e024ac7a709929e4888de5b2a03fe60f", 0xa5}], 0x1, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000516600000000000000020000000000004fecea11ea8fedd6ecfc73fd3357ae26caa0416fa4f376336acf018c3e1c2781e4909f7c8df5f882b167bfcaa5b23ed00f4c8b2ca3ebbc257499a1f132e27acb5d602000d7d026ba8af63ff37283002", 0x62, 0x0, 0x0, 0x0)
pipe2(&(0x7f0000000000)={0xffffffffffffffff, <r3=>0xffffffffffffffff}, 0x4)
pwritev(r3, &(0x7f0000000040)=[{&(0x7f0000000180)="67dfaa0bb3293e9c0d9f2f400d0dbd2d93f350d93130fee435558113e8117c19a68090634d1962c1571ac8bb29b8133db3e25518187f84addb4003cacf7b5a17375fce38b8bcdb80c80a2b180c6a184418e09e61d64599ee0ea31c72dad53a6ccc05d0aba45c71e65b770c745b86a987a8162954b275f2cd80a2571b32514b4c714d17b4beba2f5d1592bf08db1cf8e3fa0ce56cdf7b65edadd4c81a509547982d75d0c6502fbdc28e271abd917bdb58d25703c5e42d5b5ebfe1766a2d2a9b9aa81190b19f9c7dc41cebde581058988ed6641bb51ba810d0ff97f9", 0xdb}], 0x1, 0x6)
r4 = syz_open_pts()
close(r4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r5 = socket(0x11, 0x3, 0x0)
sendto$unix(r5, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0x80106468, &(0x7f0000000180)={0x0, 0x0, 0x0})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x821, 0x0)
fcntl$getown(r0, 0x5)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x4, &(0x7f0000000000)=[{0x10001, 0x0, 0x0, 0x20}, {0xc}, {0x3c}, {0x4506}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "28a876", 0x8, 0x0, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @multicast1}, {[], @udp={{0x1, 0x3, 0x8}}}}}}})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = dup(r0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0xd, 0x0, 0x0)
syz_emit_ethernet(0x56, &(0x7f0000000000)={@local, @random="4f1275a1dc04", [], {@ipv6={0x86dd, {0x0, 0x6, "54d457", 0x20, 0x0, 0x0, @rand_addr="fe712a2ac74528726234bf9b577888fa", @local={0xfe, 0x80, '\x00', 0x0}, {[@hopopts={0x50, 0x2, '\x00', [@padn={0x1, 0x5, [0x0, 0x0, 0x0, 0x0, 0x0]}, @jumbo, @pad1]}], @udp={{0x1, 0x0, 0x8}}}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000200)=ANY=[@ANYBLOB="82020063c4"], 0x10)
r0 = socket(0x2, 0x4001, 0x0)
connect$unix(r0, &(0x7f0000000200)=ANY=[], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000100)=0x3, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
mknod(&(0x7f0000000040)='./file0\x00', 0x6000, 0x800)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
close(r0)
ktrace(&(0x7f00000000c0)='./bus\x00', 0x1, 0x400, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x100000000000000, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
minherit(&(0x7f0000ff7000/0x4000)=nil, 0x7fe4d29defff, 0x1)
r1 = socket$inet6(0x18, 0x3, 0x80)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x1a, &(0x7f0000000040), 0x4)
close(r0)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sysctl$hw(&(0x7f0000000000)={0x4, 0x1f}, 0x3, &(0x7f0000000080)="c66365257b939f5ed57f2ed3c50e7d3d3eef46057882b082ff964ae825aa66b0000000000000000694b08fed6450b91ee2f06ca1556de8933420", 0x0, 0x0, 0x0)
geteuid()
msgget$private(0x0, 0x18)
r0 = getuid()
setreuid(0xee00, r0)
r1 = getuid()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000340)={{0x2, r1, 0xffffffffffffffff, r0, 0x0, 0x49, 0x3f}, 0x1d9, 0x6, 0x0, 0x0, 0x7b, 0x9, 0x100, 0x1000})
setreuid(0xee00, r1)
r2 = msgget$private(0x0, 0x146)
msgget$private(0x0, 0x44)
msgget$private(0x0, 0x2)
msgsnd(r2, &(0x7f0000000400)=ANY=[@ANYBLOB="02000000000000009b95e5c12cfb623bc6965064859960ef7bba97d0d53e34b1d86463d033ed3973c8aa15465ffa3acee460412fda0e44f336d012f9763c219485d2927c3513067b1663b12430b6d3dffe46f694315e52c3a923f08cedbb9eaf1012659ec8489cf14d9f8a3f1605c5fa0cb919bd23ccb0556cbae38f62fa55731223b977e5d0236583a9da253997758b157b32d832d0eaf4a3fce812dd28821df80b6f4b44778ab58522cda29331eaa27455e77de8dbc8c0fa05ca"], 0x8d, 0x800)
r3 = semget$private(0x0, 0x8, 0x0)
r4 = getgid()
fchown(0xffffffffffffffff, 0x0, r4)
sysctl$hw(&(0x7f0000000140)={0x6, 0xb}, 0x2, &(0x7f00000005c0)="590f369b4d9fe0820b504ce46f52dd84c67115debf2e784bb35b79c559fd69410be5c56cef673ac20dc0c8e30716ee1cb2369b03167280256367e7dff465aaf9dc30c7fdc6c2563bfbd3941e079ca019308fef7cb611625f123e1b6b8147fede2ee1c68a27c1521656f782d266f0eeec89b6aacb42f28520c31852fdedba513d2c65798430c9f84cb0b133b2bbb410b547973c316308ea977fac43bb971074e19fe7c0fbd318946f083ada3ec2c7e14753dac6ad7f25cba5c1789a51a10712b923eacd7f708ee0cb6f6095e8bfa03121c2059fc0e756aaacc1e80aa74c8967a88f2d5adfcdc65dffff3138e2aa60f768837e1f53be6e723473f5ace6d1086cf0f4856ee9d927a50adc61fb4b9bdd5c3d0d4ad79c891ceebedd404b22856cf6d3944187f976e907f874a2d66a687afd7341e3f545936d484fbc0eef1a4e5e3abca15be1c9c874279ac02ef3ca63cde85481c0e44e989277773afa2a85d16af27a3c945e325f3381ccb500a4a246a390226aa90cc3230a4b9cd0b79e978abd16502cbf96a8e258265d70faa73a7fb78d3dee2f238c8cadf4bff3db841136a9283e4e28c73957020ee966e18aed29045e51328c7b187b839b92a6236ed96798c530672dbdeb849121ad0b8eb62aa8b60da66b5062d0d98160087ae4e7c2515ee2713942f07b04282818ef8344dcf12b22e026764a089276033a59ae4bf3d5193fddb32584861963585d31cd91ea840c87adf67a2f1558ed8b8dc864b8dd37a78e97c003d78a3fe898e762a5ef4fb742ae55683f8c9f17ecb96e0b8294daf66083e6bcdf51b5e3a8e71b18ac40f2b1119c05f3b4da16fc9b79ecb548d9164866664263b75b31ed781483c886df99d7aeeeb8ec51731d61d87e34272ec6fda0afe59b082f15fb2b5ecc585d989ba6ae78d9c886c7c2ac1b637d89c458da9b9f29b16a6eb62dea4ee2e8f09388f98cadd6c259ce3da47e4ab9612028824c0c3528e28d33f917a3d9ce9af18a29f6d4ca56844ee611f8ffde09b1e4c55d8ba4e4f9f43a0be5d5929ad1ce10b9ae96a27762132a0976745383efc0a8c5323edbed650cc1c7e7711e2127e50c7434ec0ebbb76193b87ad204abe50919d6cb1e32f297c6aa98c4f9a0a438b8fbee5d4d777e68c9509d60c3bfe1e99459cf6d4c3cd91fea3d82c31f5a6575ecb9a044653f3aeec90664dc32102355dc5a10c49211fb1e01908a629eb43ea1a6564eb19375f4a4a4748d3a54e44edc37f1c413a96406ce8183fa734be90768d1f3d7fe992f789d23277ffd74a96974ad89674cb3ea04e8357a2467918b2142fe3eb8b8233d69887d322a5204c57dc65a23eabe0dcaf6a051feed2c7004b1cc851720cc2493b0c54a4c3640bd3aad246b6aa928c18091e0a6b00e7dbadb57046622df6c980b5c7137e26a2f7261408f91c2eb3ec0cd4ba0a16152d0afcb5691d8268a502068df4bb73f4e42ffeda1bdb98c2fe30d54a681e7c8c175665695cc9214a78cf5c6e9003d32fbfb95d222044033dfd5209fd560da02938ddbc7e70a0283dd144a3e67fbff11b4cdcbb1d069af4567eb373a587acc7d2ce9624c747bb30d27a1af171e82ccf70bba78f6a1c92f6ebb068fcb1c823c657765c593cde6c39a96b36361a7b423eac3871262d6c0d7dc756bd89090fb83df06d0b25851508fc535a73f64fba1ed282efe7e8d279b2fb48692ce6ba4cfcbe1ea8584f13e924a22aaca58e579e5583e50604ad1dc3000ad46e7cac5429660527b4b3ce898797cc80245ce6ff96aa92c78d129d63fd58e2b7bb90838340edca5989819c044297f254ae5cb6ea30ef96f9ff74efa85b8b100a8d75c5590c1d8ea3865e5d8bc3bae43b626b7d7aa2ad0cedaba75446047b158713331d9c0dd3a4917d18d2fa4f015fae275789801e88be47907e15eb6a8b2c8b61bef37a474bdc6f825ebedd8b675247a54faf3f2b90e84f7ffee0d327d9589d946d4a1aee42d64bc045b1c2e5ac41dcc087d0d33e5d2dac53b24a67d61409308e3f03d8092f2a6ade2e178d7c27a7820dbaa137f5902e8bc39e1d6ad2eaba08ac8b93a83fc32788bcdb2bc2fcdf17d661a07c2104943581c3f6ad2ce75830f36532e5eb3fb6853b19b4a5139ad23d7abdf16426265bddf50f0f5a55d4503e314b52fd3582ee5ce940a5d4b193e1346e49ef771c0b24106f89c5d97879e4c6ae358b0c10ef50ce79c0ffb00c60bcfd2cdcd6a0c1adb0e0f2bf59f92fd27a5586c77a648b851dbdf4b9595cc48c76990c4093e8d4bf7bfa4788a32cebe36b30138a342c438a34adeb428d337f08fce3d18d784cf825bb30b08f8138daa9242a50787a8a075da82cffa5d5583dac4913c15ce0dbda1ec17285f51d77d206f8ea3f3eda7526e0ee86667a1020cb180e51da7522157397fa4a6737994b2e18708e2635816028e4cd62ed21f865552603cd24239dfd7bd4ff43d3303c354855f3bad83f5e523454daebbbde1d338b991101ac39ab4d613d6e52bdac4fe2112108a99438470a63ee545c4b0034846f6ed0b388b531a2a74465714cd129884e648f39ad8b30a9b418c932b8c7c8e9c29d1c1f7343b3d29a667335c47e6e0d730c7b815b99792aca37b2d8a5b67de380c7af0a89d3be7a0b3627e54b1746cd5a2d7f3e44acc41739b279c573c924a77f73e5bfde60312e6d23212e9d5032259efa18f7714742c14915b46ec70eed1d0c859d6f8cbbf5f53cb46a85d896fa1b6cb8b1c037047a8cd9adc352ff9ed817b6e356970196b85a93bf9f2616d4800b9b463489c389689c6ccf6469dcc5e666c6857ac82365d4c08bc9dd1ce2dc02db8d3e7e9161916db8ebceab3b1418e1ff1ae4a7c08b601021908172774f6e2885099f92518feb3d73569057d920ebb3dd5e48c88ab74ed97f00040ce01fe92d9f675a28e9500448e9d27a6bee3e180cec751d8c659b4df761be485e5b06af74f5c12ade0f74a639af0240fbbe5f792371a85549f63f5d0daf412605a7e7b500d3c7e0f5e045ecc0e3fcf9830125e0ebf8514b61ed783423e0225dc2e94f49a29d21544a93f5696c520dcde19aff9d004e864bf745d447e8147f09f6b71e21081cef86eb14270720818a4a625f3f31f3cea7d341ce10dafd5f4c55980adb92cbc40b12136a2145c2fbe4c85d7a7894180f04883a06073bd650c227f014f95e0ec10ffaafcb40adefec2e23f6ccb2c8344077d6524ffe526d47d7ad3131302180b40603dee6ff55d547127f07d8e15f9ef39d17f16cc6b200a10b50dacf00ca26d1e5ded6eec038bef263cd76fa12184e72b57b275d0cfe01b4bed1ce3f1e6a4d454268d85494f9903bb689340a3923e2f08ee22c8a391ff3aadc7093b95aaf5b4492b0fede2e8657618da57250b81c397032a8948e95878f7838526adfe24f692a6ce0750f68a8ede7f8fb908ba11823652a4d63cde7b93c06c88103b6195d9a834b01cd52df4d72b25c21d29c6d9c0498d48e8164f7d5712bb7a22fad6675ff5dec1701967ae4e22711c62b1bd4004a4b47f0d1f1da6264301f05f6facabac9a7092d8fab5d5a0d1f768686c723d66b0b469d5a3c8736a31c9088a9cf63ace3a0f939a6894478df46f5ab0020d0183947a76caad07db7b078f6b1a51ebd715b3a39d191b4f63f5e6d607fbab794f720fd6750970a44a5093acc30da69d624e3dbb1340c88ee7ab6fd836a0cd25800696839ea3953b4b51b20bd38f0836851a8ec902143bc6566935a4b3449c4a51e7cb98d6b3ba62cf7f557759a959239d5bba0da5b81f492ee7161f2a44098e7ef3bee37e946105d600a9c204746973b161df490084ce87421583e47c43f632f3ccf32d884f80cafdcecc3d851d9ca417e6fdc79da7ec751c8c96e254ab3559e427e50994e0489da9ed51484e4af7a65769d6b9f8284c75b46b471fe0090e687daf95fe3006363e3357e7c196d4da4e83be35a8231b17133a7b82d6702d7e901676b8b744af7b75431c82bac8da5ebb3abdc4469bad6cc2640a39531542c1db2d5806fc263e7a8f0dc9010856084a40593b830e8172bd7b83491276eb30d8d221a656bb203740adb5bc29eaab4ab4cf3e8b47f7c08c138a999fc3dafc511d1d307554604819e0a6e7617aeab26034a8ecbf06298544a0beedbea6924fc84a7aa8858711bf1b89ef2a4a33033b0e36f0335247e57104deb09edabc11564518fbb1e06ace6bc82860867cc34beeb7d8a028a7161f3d7631683074e7a694ab281d68afe92751e69045ee130f1c0801a95a5077ceb311e5d6d22b876ce101560409288ebc7a903507c70874d7cf301b6ba34225e4451b05332a165e7ca9dfc09bb30fcd4c7e903a6a956a4d36fdf96a04e6ef0b557a285e172ac4668d9e1c7a989ebfde714353bbcb12e8a70c29b384ef6d87c7c998d421f3502fdec06a71c37b88a70af0ece3243a31e5b4121b01882613a05bb6e95588cb873baa2b839aae58b553d206bdc16e94ee05a02f2ef44e3cf533e318f17a6469449bcd3881e55dd9841bfc957c33f5e9eb0f816b699ff566acc9e5409a5288a9709b99226aa40833b6f6c9d5e5880640290251e68fa05e7c9e1640aa6ce2a78e7fc9bc21778f8a012a88f440ecd2f8d800e886ec404feecec67922bc8fdf712fbf2c04323a601290274e0e5a4d4772eaadd3d44a71566bcab66a08d7bcff5ebc49dd8c595313264e5e7036536d949bcf31f9e29b02c77c65b29a0c94ea257daf8722012f8ba92ec4e90ed415ea5a70fbc11716aedbe91320ef0896986a6b9f317f7e2fc7ec33c1ec6c1c1cc6f99f0a5f27a816a0573e4c11ff47dcb31408fb3875ac688a82487f183d8a5913fb99d1fe0fa4bd64f45ef86a93721718a26b2e30f8f93ff44471b629694e3adf3f30f4f3760d9dd68e8a2f9d33c09e0b16eef1706c3422f8569a78c787531fa060d192355f2dc584cdb459d871423dc196729c91dac9c52ce2e0be7cac136aa0f285c34d76e144a6d3d8c433eee98e1ec53173b6c6bf8e183b099a69764b00347cab696059095f086e489702c37a95f3cc52262289888fe5a1b66e677b97d4d7438428bf43c6a0d7480bebc67606865590e3c50400a5e18930adf09acd618434e6d0b6c39d9b321d388d53891460dc8827cf3143264884f7b031ee1c497ec410289f726739f455fef7079e652e920161e2fed235768525c08017da0021cc935b444e7391a3c3e36583ed4d650748dfd33b59b0fbc276315c806ef6972a71e45a6b5d364018a00668a7c86023b13117d2b46023452255ce08485106b7443db08bef3b29225621463df9dec08c67e20908f6e99d1a710958481f7c0d0e2c4448fb8204068a977bf8f320311db4b08f2c5c1a56ef07e94419c58b30b1484eeb7e0a51bb1b5ec6472af8b8306f46325c8c9a860454dafcf32bc9ad777c4df98c507b7cd2925024db6e1f86ff330c7dbc4140baea13a789ffe71867d30d164f6b46732788bcc5ec869121d91e7cb613f01e131457ff75f0a2ce6c7e4a1d7ad723bb29d099badf16fb93f54d1cfca4875255810f875bbc44c64ea9111d56fdd564e56ef67dce042f58212787fc7c8c9a2e617e80c2fdfab3a4f36e0fb2b13673dc5eca2eac547113d8250c46948fe69fc00a4ec60b8eb5bf4767f29c69b5cd40cdf81071bfc04aed38f52547946305fe21f1c493ea739fcebeacc98679f6fd16c0f605a1c7e7adbe2f6d4ca718e6a081445cea66b134ff84d66f3c64d75a8702ea6f6b8c0f4a6515871aa84fbbcde508ecf695f9422c8793e83f809231f8f79a", &(0x7f0000000180)=0x1000, &(0x7f0000000200)="183ab307c178f97e064fee90374de6ed18646d19ea0751ff447bdf1fe85c34c67d388989cda5baf02b06fcffdd890ca49b3f82857b9fcd2b7387f30213249abf7db8597e4faa3cb4154de0f56c16877bf6bc4467a6e134acbb83773b5b29990537479e82e59e96b5fde4cd04", 0x6c)
semctl$GETZCNT(r3, 0x1, 0x7, &(0x7f0000000500)=""/153)
semctl$GETALL(r3, 0x0, 0x6, &(0x7f000001ba00)=""/154)
semop(r3, &(0x7f0000000300)=[{0x0, 0x7, 0x800}, {0x3, 0x1, 0x800}, {0x1, 0x2}, {0x0, 0x9, 0x1800}, {0x3, 0x4, 0x3000}, {0x0, 0xffff, 0x800}, {0x3, 0x0, 0x1800}, {0x4, 0x1000, 0x800}, {0x2, 0x4, 0x1800}], 0x9)
semctl$GETALL(r3, 0x0, 0x6, &(0x7f00000000c0)=""/5)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000001c0)=[{&(0x7f00000009c0)='L1', 0x2}], 0x1)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000100)={0x0, 0x9, 0x80000001, 0x1fc80d8a, "e30000000000000002ad904da500980400"})
writev(r0, &(0x7f0000000700)=[{&(0x7f0000000500)="e77ec314b5f4bd6d8617eaecef8326bfb2559a886e85113cf4730204", 0x1c}], 0x1)
r0 = socket$inet6(0x18, 0x3, 0x6)
r1 = socket$unix(0x1, 0x1, 0x0)
getsockname(r1, &(0x7f0000000000)=@un=@file={0x0, ""/66}, &(0x7f0000000080)=0x44)
semctl$GETPID(0x0, 0x0, 0x4, &(0x7f00000000c0)=""/186)
sendmmsg(r0, &(0x7f0000000680)={&(0x7f0000000640)={&(0x7f0000000180)=@in6={0x18, 0x3, 0x2, 0x9}, 0xc, &(0x7f0000000280)=[{&(0x7f00000001c0)="606e8b6f1e5651c3c417a1668c9bbce846c897d2cac42812dbfb7615df8fc2ffd0ee03bd741b9c6811cd5cb547652dd59cd63ae8bf926feae56466202aacdae90b6bbd9b8a1713d6ce5645abcc789d2c5a2e00d2ff6b4353df72786863a3f130354dac1fd905ecabc73f639b5473ada459cd4b18e4b544ea874179a8460e0cbb1c46b019c7b7e121f3907c7700a162ef7710be565e6cff79", 0x98}], 0x1, &(0x7f00000002c0)=[{0x88, 0x0, 0x80000001, "48db8e4a1c268dc473819f513ce063a135119a435bd0016ea9b32d69ac6f618416b194ba87bb3a4ddab14cd89cb94bbf64c2759aa0fc3d2612bac8370b51f972458a3c995acfe48e0bf0169f6ebe8659530be110d0292209d1d087b19c4c0b932c1b2527b25817635bf3dc357c0e07666815fd7009ae"}, {0xc8, 0xffff, 0x6, "a8007e01105e8f761e924385d3ce83da035c3c72cdeb0c876df6e90392233a1b15e0fb11966ab54d1a8c7366e2cc7eff34bf2e921b206766630de689b5090cc9e1f5d352b03baadfc43fbcfb932f5d2682d87b6edb18408dce56e3a2929b71894dd92648e579236077f29e9b160611c8f6a0d62aaccd165769fdded984b505bc484f661d7f2e99d02c8c7d46af0f2ddc2b5a5456f580fdc936599e1013b0b02d12d357c02f4413c95cf1fb449c851d99430d"}, {0xe8, 0x1, 0x101, "c38a99fc92b0e16180d13bb2623daf728c3eac225ba5b3ce61304b63832b2c7e4bc9e532254c2c5d5d90461f0d62f885c9daf3596d42a714632a95bc643c60f1161bd0fb4ab2b147236823b822d9962a4e1cd936d2f1b165f5981f622c5f18450b3e79901513bf2000226cb35ed95f0d20bc03a1801f128fae9ae57afbfed50123d0c2f8d7cff8a8bb234faad57d5c7fcd34dbbdf14a6790ca6dde942ec0b8084bfa8f94a5d823237e6e626a2d1e8b522de074333e8d691025fe4a835e5b36a01d259fc4f61bd67846b384c42cdfab8320491028c2df"}, {0xc0, 0x1ffff, 0x80000000, "5d643dfeba369eed6080354d3ea92395859b42c3030e3a8aa7cf228e6dea36a9a57ce8d51a3a6d78cc4193d5b1eab4c00b8f2081da8075801c2ecb06437488b9d25a09e08b8714f4fde1613ddce29401f6acb17ade4695eaaeee6e3523ecf02e2b909d93a715cf2cc195f4008935029b1e9f9e5e4a5aa1e63de129d1f3f465fa256aa14dd728c933782af3098dc2af770cdd688be5c069e07e6e9c9e6d1bf622727e3176345abf01a8f707"}, {0x88, 0xffff, 0x3a, "c42faaec654b6ebf41c56e42c16f44d4f71e5e408abee94128b59f51e5cd56123c3edfa9c16a4ee1e8b74d023fe37b731a31c396cabd5d6d5ac2af97f1036174d033d7c2015232ba859981b3ce3fe2f94a8cd7ca1222144db16f2c339f90614abb76d89f4a1c83a74a8b588b126a2cbab940"}], 0x380}}, 0x10, 0x200)
semget$private(0x0, 0x3, 0xd)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000180)="40b32c8ce1814446f2979af3945993cd90bcfb6ed53a9d4cc43f986a065830029f419f12d4ebd22dc7cc1aa61885d9bfad0683de13d3efa2048ac031901eedd0a1307478ace7f99652c011b03fae7869db8bdbbf9eb7e2d0c2cd2bcbfa7bd7a4692118752a1ab09ad22fa8e7188025dbbed23277a67562635b51b48ed2120707d831d8e6ec538af053da9e49f293f3d8c72210c75a61ad4b8f9638fed9edd69293dd1f1f383b6078f146b9f9352341e1ed15c9bc00", 0xb5}, {&(0x7f0000000340)="4f377d1a67dab4bada4faae812c8deb44a61243ccd2db1353971124050fe31d9ad21936040ea23f4a78464a6a25df9c9128115fa923fac24867950a2e77d0319be8d3792881c767006ac9811ae0c6c20778f208145f72c518fd7ed6bdee4da7bcc9cd2", 0x63}, {&(0x7f0000000040)="f1f991a032d9f67183a9023356b241de2c368a116f", 0x15}], 0x3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0xc}, {0x40}, {0x4506}]})
syz_emit_ethernet(0x16, &(0x7f0000000000)={@broadcast, @empty, [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @rand_addr, @remote, @remote={0xac, 0x14, 0x0}}}}})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x80384601, &(0x7f0000000240)={0x0, 0x0, 0x0})
sysctl$kern(&(0x7f0000000000)={0x1, 0x2}, 0x2, &(0x7f0000000080)="639d1b74", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0x0, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000140)="ab", 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000180)="14", 0x1}], 0x1)
dup2(r1, r0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0x6c)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000280)={0x0, 0x0, &(0x7f0000000200), 0x0, 0x0}, 0x0)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240)=0x4)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1d, &(0x7f0000000040), 0x0)
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "104ce467af60b403184700dd47c8e9d209670991"})
kevent(r0, &(0x7f0000000040), 0x400ffff, &(0x7f0000000140), 0x7fffffff, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
sysctl$kern(0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)="d57e2db550579655610422b88a54e2ba2ae9305a5480090e41c2de54a93fd5b96662362db7b66b4664c03c235604ac538d787457313070d60b74d95b687a4ab04830bb591c372c610705a8291b8855934474ee5285061dc2da244c35c62d04a7b079446409c5cb5c82f51167844d6786b48ee94e6d2e7c9cd73b13", 0x7b)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/9, 0xfffffffffffffef5}, 0x0)
close(r1)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0xffffffffdf004fff, 0x0, 0x10, 0xffffffffffffffff, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecfa60080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450444a, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt(r0, 0x7ae, 0x81, &(0x7f0000000080)="f4aa854d1421efb81493c8950219df6abc2e5a5f178e8824fcc1a9ac787d11be62d443fcc2d5aeeacacef57caade1a11d369de6e6a469e70687e9c073a0af44d17492778875bb64d2ea3887ae5327104479f1c4a593136e920a00b14c77f2bef6d37ed264408e5e996999829f76263482cf4655c4c17807c6eb39f8c7ce11b19083047b1dc3e20b84a50896ff195ddf9aa620ff9d7e79724aa887ccab135d2e8dd1263770eaa61d739233cf635389d1753e69906faec87b5dfe08ddec0592d49776b64bc76d8760e", 0xc8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000000", 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x1c}, {0x84}, {0x6}]})
pwrite(r0, &(0x7f00000002c0)="666db45feb524844f44f3e407c26", 0xe, 0x0)
r0 = socket(0x2, 0x3, 0x0)
connect$unix(r0, &(0x7f0000000140)=ANY=[@ANYBLOB="6c023f2fac1400ff"], 0x10)
writev(r0, &(0x7f0000001440)=[{&(0x7f0000000380)="0f2bc89e28860a7472ed72a6869a21cad7c09a22645687af0371e19b763e955ae892f4f8b3c2ce33e612335b996f1e0c03f2d86101942d1661fc5e9b3d38a239aae985b04d7f12ddb87defc290263c9e3a3c5557e375ba6e7d8262faf50c63885610957ef619574c19336a4db80180b82939895f10c404d5fedda7671d73d4ade332fe2e9ef67020ff3eede4d93930daf9dca14626fd66c48bd3a26a182eb182e37399551826266461781814a082", 0xae}, {&(0x7f0000000740)="6e696b3db1274ee148c0b839d373be9e6eda7d5c13a1064ea08d83e88c3d041e9eb1fccca5382ff3d5f5e252196109975512f80376a8bc6fd6ed98456fa897e8f094005d7d49cc307a4da80a85b06f4a79662c62de62be48f49956a0d7bc9e09e400c33679fc40782a24b37f401a3401fec66f725cc8e7b7d2568d15336b8002ceeedce25a0692f829c2f38e29ff0a0013f4f8eb983db3391f65721f31745fdc008f464bba00e0f2f969fb99504fbe7efbdd24c7881020b1fb3471b8450e8948bc1ccb93735131b0e61b3595d9a124193fa47a20a999fc76f470b0ec9af8ebaf3517167ede3425b24c42ab96d020659d798d9202c139105f7f990149d956279cd018610d915f03ebf7190e866fa0650b4de5f87d6206e2b91fbcd2db53734823e179e94d5e19b9124f62a6570afe2919ac0cbf7b62a0648cd506b2d0b901185575", 0x141}, {&(0x7f00000002c0)="3f04a75b1f45c27296af454b490222c48fefd6baf6a80e79c5a13df3a718f71f9434665d4dd070786913198a77a1d0cef74b27b06c253074769bb359f6dac10ee5b27d4374ea543befa0901ed37b2ac8d052b30877262e846184da2c6ecf204030c7659b3c992dfd4bf98f5e3696ca", 0x6f}, {&(0x7f0000000340)="34a91375a20da084", 0x8}, {&(0x7f0000000180)="ba7664d50ebb4a75dc2152e5cea5132c1e4bbaadab8062265c5ecf5c0bc52f1987f66bd144b41209d700ee718dd151ccf52842b60a84344cf245b8e74ef40dcf507846f23537d7ee20b44852268e1962ec71ca3720dcbe97048bf3cbc86287a058ba0bc4cf57accfa0b41059bdfc6737e8264cbf96540e74155b712d38c0dbc8b61ead6edbf1b56cef841d37eef427fce41f3612c598fdb71c", 0x99}, {&(0x7f0000000440)="d93de2151ab9e28a8e93c4054ce624a82884b9a813b7b83b425eef050818a7f386a5da14a74f15ec0776ef4f95ab3ae9e88826ff7d75e3a25cf0129e5c7c4933a3635e52b98dbb66cbc11c73b5d856d242cd9ae5a8a294ffcf363f1998accf0499991ce11e2207dd0ea6e6a5df1adb3c935aa884ebd4d86d6d64ce437852c90e55a592ac718df452959c156283d9a79b8c8613d7240ca7da1cbd1a3e6cb563acba826ba86ccce5e1bfef85f8826b42f85f7919c14e90937af434fd47617d7d2f09f8e5a6781fdd4a3aefca9b795e9186c4f2257b2e82b915c4e34ca5bb38f1c392dd61da56199aac9c96e455c0cf5c521f3229d762066e6bedd6cfda3478943de9ea2998b52a2dc05b82b5c84b9020e6fbbf5818aaf45a06a4cd373e26f67f211e1ff46a94adb0a4eb787605970bd9e003e5247415f1286b14a44708b0a23d9aecd4809a7fecb5e749b70f5861cf12f879efad61747e1daa3d86b7c5812e9df7103675b2dcc836c9662c8410891116e21d2410beb2ec1a5305be130bd6549176f0ec508345faba8aebb0d9b898b3426a9e74780dd73a5e46059d143b91073a2c76b8f4e2f8653d6f3fb7fab36c6a015c3c18b8017245bdee166802fa9b900bc6ac0112d337b366a2363fa9da68b3634bd41d2503f233cc89ca9c1626f949ba9c4cf30bac729953fec9d7ed24a6aaaa7f95e4aa560299cfa531d1e3d69b78b6ab462d017a4fa9f9a3a2c4c5f08e7dcabacc024c150a7f614e9c1a1ab9533f5be9498260b1d2b5b92a5e519cd7f236503d1d8656dd91e16d46f8f9ec8c61093811a0dfaeda92c35f035dca11378cceda2b8f14d065bab5ff7b80476f2c281619ec365b04c71a5708ab3d018c04aba6d7c57bcade886a9503792755318d2830049c88f839b80dc7d921c9f40da8b7113d79ff16eccef9299c167bfd4f3535e1e6e5cdfdc7329b92a02a082211c914e7da7df9109d9c858c640da57fd193603c6f9bf658f9d7247a23cb95fd", 0x2ca}], 0x6)
write(r0, &(0x7f00000008c0)="59a463985607a0b66227b6b3b7e196813ca03c9f2c4cfd9671b326bbfe369f1a4652dd9778721d976d209cf099bc810fc49c11261798cc908563d71bb85d85325ac8964ece6dd91c6d2246cab773733a19caa089d48812da7f0b0335f518b585ac4eee095b30c8be376e27ddb76cae05e67ff429f26f382fa2af66c9983221f334ac8a629d", 0x85)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x1c}, {0x54}, {0x6, 0x0, 0x0, 0x8c9b}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x4e}, 0x4000000000000005, &(0x7f0000000300)="1b7201c746b1d5204c1118d7564911cc4058470f36b83ec43e2ad720411cfee7ae68ae451e5f9481582f6b2dee79ed9c2a7a2303e3082541fee65462ce3e59cefd17eb2a1b7e9a090f4300595b391685ac5029a4772c094dccbe55266f26a2fc4c642b4be184e2e863047c5811cef158a567b855b589cccc783a3550808ba1f85c08a54863aedb0cceabbc3e8e23dd75c5c8f40d49e7c4063972203947308c22a2c8e089132677436d7780edb9e59994a89e", &(0x7f00000002c0)=0xb2, &(0x7f00000003c0)="e99208e2cb86fae9a097fecc3dd24dba8294daca6541c7d14d8c79617b5a2a5758de2299c6ac1bc5b08687de9e1e7ad961a72e1090c07c6c6004b3eaba7de21e046f985ab1159cd768031f57bd3de480de32c623999166c3f54c86ac97585f9a72a5b4a6b7bb322263ff447985488a6878da85e2473ea11e06ff96f084b338a0eea51f0d2a2b8636f159237070e413e12e93be4e6b723cd7f0a78ad7182b33e842915a13448183b06610814ebe88b17057a6dce8b6e5e5980159256899fe4c049d9b81765379a5827a1d613895bedc64f871e2a2857b865f3ac90eca56823983", 0xe0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
fcntl$lock(r1, 0x8, &(0x7f0000000340)={0x0, 0x0, 0xfffffffffffffffe, 0x1000100000000})
nanosleep(&(0x7f00000000c0)={0x0, 0x1ffffffd}, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2c}, {0x45}, {0xe406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@empty, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @multicast1, @random="0000ccafb0d3", @loopback}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x1c}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
readv(0xffffffffffffffff, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "c99e0400"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
ioctl$TIOCGETD(0xffffffffffffff9c, 0x4004741a, &(0x7f0000000040))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x20000002a, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000040)='c\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f00000000c0)=[{0x40}, {0x40}, {0xffe}]})
syz_emit_ethernet(0x4e, &(0x7f0000000040)={@local, @random="6f0f7d4ed943", [], {@ipv6={0x86dd, {0x0, 0x6, ' \x00\a', 0x18, 0x0, 0x0, @rand_addr="30594a480000000000008000eee16f5d", @remote={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@mld={0x0, 0x0, 0x0, 0x0, 0x0, @ipv4}}}}}})
sysctl$kern(&(0x7f0000000000)={0x1, 0x28}, 0x2, &(0x7f0000000080)="c1aad227a7e83dab3aeebdd4dc30cd151ac9bcc7617c8aee14a58aa85ca7e40f69ae1b66cd6afb25f118f925864709ad", &(0x7f0000001080)=0x30, 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8", 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r1, 0x0, 0x200000000000d, &(0x7f00000001c0)="ea569fe316125c00", 0x8)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
semop(0x0, &(0x7f0000000240)=[{}, {}, {}, {}, {}, {0x2}], 0x6)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
mknod(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
open(0x0, 0x0, 0x0)
readv(0xffffffffffffffff, 0x0, 0x0)
openat$wsmouse(0xffffffffffffff9c, 0x0, 0x0, 0x0)
pwritev(0xffffffffffffffff, 0x0, 0x0, 0x0)
pipe(&(0x7f00000000c0)={<r0=>0xffffffffffffffff})
socket$inet6(0x18, 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
poll(&(0x7f0000000200)=[{r0}], 0x1, 0x81)
execve(0x0, 0x0, 0x0)
r0 = socket(0x400000000018, 0x8003, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x4c}, {0x84}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x800000000009, 0x7fff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x2045, 0x200)
open$dir(&(0x7f0000001a80)='./file1\x00', 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0xb}, 0x3, 0x0, 0x0, &(0x7f0000000140), 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000400)={0x0, <r0=>0x0}, 0xc)
r1 = getuid()
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f00000008c0)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, &(0x7f0000000440)={{0x0, r0, r2, r1, 0x0, 0x111, 0x7f}, 0x6, 0x1000, 0x7fffffffffffffff})
sysctl$net_inet_tcp(0x0, 0x0, &(0x7f0000000280)="bcf0dcbaf15236b078d1c19f8a69e7217b511e53abe1fb63401383c35e4d80b132307a268a5853662d334f93a0a94a5b363e88444080a9a56d48258b2f508f4e4bebe6a8aebb14d5c6e24cba548bcd8329a62bc93d5d5fa078ad", &(0x7f0000000300)=0x5a, &(0x7f0000000500)="9565701bf3824af5391b43c4f46cf27e527eadfa14a7424c7080cb9a5ad4778cf8c5886201ba82f491422d6cf681b3607259a257f03a1f9e8e604fee8dab1b9a15ae1a7929356e65e7295822eae2a7688dc6698424070f10d92bcbf485b36c13cb0000ebd3c3fbd69a9c2653b8f8533c638f1b9ba077a979cdc37cc7eada78b453c304f0251c2566af40", 0x8a)
sysctl$net_inet_tcp(&(0x7f00000004c0)={0x4, 0x2, 0x6, 0x9}, 0x4, 0x0, 0x0, 0x0, 0x0)
accept$unix(0xffffffffffffff9c, 0x0, 0x0)
socket(0x18, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x10)
getsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000180)=""/224, &(0x7f0000000080)=0xe0)
r3 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r3, 0x8020560a, &(0x7f0000000100)={0x2, 0x0, {[0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1], [], [0x0, 0x4], [], [{}, {}, {0x0, 0x0, 0x0, 0x5}, {0x0, 0x0, 0x8000}, {0x0, 0x3ff, 0x0, 0x6}, {0x0, 0x0, 0x0, 0x80}, {0x0, 0x8, 0x3f}], {0x0, 0x276, 0x5880}}})
openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x2, &(0x7f0000000380)=[{0x1, 0x5, 0x7, 0x122c}, {0x20, 0x2, 0xfd, 0xa0000}]})
semget$private(0x0, 0x4, 0x142)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r4, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$VMM_IOC_INTR(r4, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r4, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xab674864, 0x0, "0901a6d3137c00005da247000000000000e100"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000001080)="dc", 0x1}], 0x1)
read(r1, &(0x7f0000000040)=""/30, 0x1e)
r0 = socket$inet6(0x18, 0x1, 0x0)
shutdown(r0, 0x1)
recvfrom$inet6(r0, 0x0, 0x0, 0x43, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x7, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x20004000, 0x200000, 0x9]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0084463, &(0x7f0000000240))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f00000000c0), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x4, &(0x7f0000000080)=[{0x10001, 0x0, 0x0, 0x6}, {0x3c}, {0x84}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000100)=ANY=[])
sysctl$kern(&(0x7f0000000200)={0x1, 0x48}, 0x2, &(0x7f0000000240)="ff6d1d7b", &(0x7f0000000140)=0x4, 0x0, 0x0)
sysctl$kern(&(0x7f0000001140)={0x1, 0x16}, 0x2, &(0x7f0000001180), 0x0, &(0x7f00000012c0), 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt(r0, 0xffff, 0x40, 0x0, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0xb}, 0x2, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0xffffffff, 0x2948, 0xffffffad, "9190000100120000000092ff0000ebffffff00"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa1fabe934", 0x47d}], 0x2)
read(r0, &(0x7f0000000040)=""/35, 0x23)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000d40)="2abace18112030b738b1958303ac3fc1c384e67774d4bd49660d81fb4e2e9a19a972ea508fd17e3d0a932587a0980ddae58d2709a47c4986c8e74ed0d85edc5aca224a44bf11e4e1a51c0f08aab643577c975bf1928b38f6a67e0c5846b4128483e6275d201fec16e9147865784c167c704c6ae0980bb84e8dd18d6f4c83abaeed4ce23fa84033c78e9ce083ed4b575716841fd249fdb120f15b3db394bd37d3ecdf5440737d32fd4d75ae88ed351c1a2c8b9fe3a7773a6c3c322eec1e8da0864d79d9ca12a609b61fe0f6f8b88324f93cd6067a05d71c69804f02315e320887de9e7503e2645f1d9a453a0f770a109347ba8e025f6654809908b3d640309334db564b52daafcfe22cde8ad112ca5fe8d81a335656c43555aed4ef5af976b8a30d39838eb9162e58f82c048091b6f2a5c792c222520cba0fb1a3adab11c5adf10000000000000000000000000000e9bf0b9b675be513096c198ac5303b35c3a060a331662bf508cc3b6eacf17750ebe4976e7239251b8559a57039a7ad3a3063257fce807eb37d29aa2654046c62e343be2dff20fa332dc701ac84f3191977476cbcfc2bcc335332a1f8ea95fa9f440be83a78ebdd60dd81fbbf6fa326d6d84c9a12c84d037541b12aa9a2faccae86f50372af64db4ace9044ebc45d64e98a20133bb56a07434b3403ad089f99c949cd0efbff2e830cbb5146c58a27f83432998008556069bc469cba6d83bcb12f1f65365ec1ea5e25a6258f284da980074edc59dbc279cba0c634a2ab848edc44ff4b26b6f61b58ef1264e7d58d6cbe1b7fc6c914d69ca31a84b6eeed94229657d44bfb4e2e4b9797ef7f6af863f1c1c7f7ae9a45995936baf9d11f3bb2a5eed1ca573fb9df42aa20fc7eb1832c2cec4d048cfe862cc03524b4a0efb020cc7051e4d1efee1d972ff84f9fe5730459", 0x29c}, {&(0x7f0000000080)="051c92fc1af91e51429a03162acb8ddc3a8f9d3b588cc9073401033d1119b4b141df99877a601a4619f18708a668199c3cb370", 0x33}, {&(0x7f00000001c0)="d19e767a41270df7efd1b0a722f3051b176adeda300f91598c78cb7aa9f36b6b570addb4ba0d744f7847", 0x2a}, {&(0x7f0000000240)="04ba5914c8ad15b694e75055b2095b4fbfd8cd9b516a28f8c07afa13c45576343f7144f02b38f2054ea3cd3d71c76e00000000003ccca224abd7f2fcf20f67fa7efc3807212db9a210f79f62407645e16fa995ffd447", 0x56}, {&(0x7f00000010c0)="37e9fa0141061db61b1ed10fbcbc40cc1306795d552e7fd645b1c8b46d209d6ae2a5de39d5c69227234925c32c602ba5157794dc1dd2ddad84455ee91a95b2f0cf1ed9a285c75b0000897a04c9acfe9c11baf3865149814461b156130a7dc3d86b407414fd61a1ee8dc9076d494132173ea614d4ea3903c0560b7e8f2166eb8fd7c11b3e117d84cf4ffb4dba38f98304cfd173aed6296b5d71104c69396100450e6824c3fc19cf9b736eaa3e33ce3b82b81e27b25b92b6f1262ce8bf8ea5c5f8e6edeb595115de33ea233b8d78750fc209c9e7a94db407751f2cbf5f99b5649557393c3de37f1f302baeb0932f1edd688f50b1dc13f962ea86d03bcf0810de0da52ced341260965444ed62d151fa76d2d42c03ab7e0d3a88d7392a8179ca0c2db11ba6a340f9fd6a3e2821", 0x12b}, {&(0x7f00000002c0)="6598427136657df761aa5f5719147df674e9c995d0884d", 0x17}, {&(0x7f0000000300)="6dde29b7cb8fc8b7c98116976f83046b3112772de36bbea9efe0f5db50e300"/46, 0x2e}, {&(0x7f0000001200)="02f89c755da165fa2c5b12bbb9a6a7f01c4f715a5010d60e943461faf212f6c801b44db43cd4eae3440ebc20d8eaed033c5472a764c093e304422f60132ed5961f1656b310a45b2821029ce0eb4c53cf6eeec8c5d342bf4f9bc6992120db74fbd9f3dca2191fefbc3f50dede6f3bb218248cab8c2d7fd147e1e6274fcf5cc17f3b4f8bd7a7049d0efb8ba8e4571fbe8e02ab2f30a9540bd1e9e0bc8fb28c769265fbea98b5bae66521ad86676fbf3f262b8577dbf3f1c8cef8f4977f32bd8882f879d15e3dc0d66ae5b6275abad0ac8097485afea6d24c6e99d1e805c1fe4ad3ee5729213587ef7468d90afa43e6f91e1845618615d5f5fc434ee97fc7453bbd5744c5720a", 0x105}, {&(0x7f00000027c0)="d299f185fc18849566dc679478f0e005b6e1cb9789d657605107fdc0c92368f9a7f783f22b8f4fabef71b991e8a0b0cb588da78a836d11135e1d5bdf66ae22a7ec957dbc6fc7288044e9a54d9602006d108fac090660ee5e767441af0263d66924a4041cb22cdccc0cfcd76259f2382e9ce969af7c0172da9e28fd1da47033f2c0cec804010863c3b9247b53bf217eced8422d5f14a85d3036cdb6587e792cebf8b081d4e39607a4a001", 0xaa}], 0x9)
readv(r0, &(0x7f0000000d00)=[{&(0x7f0000002880)=""/4096, 0x1000}, {&(0x7f0000003880)=""/4096, 0x1000}], 0x2)
sysctl$net_inet_ipip(&(0x7f0000000000)={0x6}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x48}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000180)={@remote, @empty, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @remote={0xac, 0x14, 0x0}, @remote, @rand_addr}}}})
sysctl$kern(&(0x7f00000000c0)={0x1, 0x49}, 0x6, 0x0, 0x0, 0x0, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f0000000040)={{}, {0x0, 0x3f}}, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
lseek(r0, 0x0, 0x40fff)
r1 = dup2(r0, r0)
writev(r1, &(0x7f0000000100), 0x1000000000000161)
writev(r1, &(0x7f0000000040), 0x1b)
r2 = open$dir(&(0x7f0000001240)='./file0\x00', 0x4000040000000242, 0x0)
lseek(r2, 0x0, 0x40fff)
r3 = dup2(r2, r2)
writev(r3, &(0x7f00000004c0)=[{&(0x7f0000000180)='\t', 0x1}], 0x1)
pread(r3, &(0x7f00000000c0)='\x00', 0xffffff78, 0xa83)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
r0 = open(&(0x7f0000000200)='./file0\x00', 0x1, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r1=>0xffffffffffffffff})
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000440)=0xc)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000200)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000000c0)={{0x0, r4, r2}})
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x3, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r1, 0x0, 0xc, 0x0, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r0, &(0x7f0000001700)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001540)=[{0x10}], 0x10}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000001100), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000001280)={0x35, 0x0})
openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0xfe, 0x0, 0x2007, 0xffffffad, "919000"})
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000040)=0x7)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c19ccf40a3ce90efa9b58e9efe7917611d2196f510529401009ce8820b9f6dd362bcfefb3b6be4b2afeb9bc1391ac802674184d6f26d77a445ff6a10c68c41764a0ac39ffc6375c2411a11dce81cf516cf2f6c7fde068710e246cadb60d03d2d9a2a42971c83fcc3d4eda31db5e214544cc7d776c17f63e526ce9c339a98dcd66505610060fc75ded72b673aeab31ba205bcbbfa6262f814ea739997c387553dae7e2ea0123316b443c186c47a71f636784505140475b2436baa1fabe934a1c1e400db556028db783e9f47ff98028e5eb8cbf5dc889b5ac6402a846918dc0e5097d2395a46fb114a16ea036db91fbb61a83ecd8f2c2ea7f4933fa25982129ace7b16a56cb12b1f238d8ba33c9856a0f0809394b8e5c242c2d9d5194e43f3bb1e6f81d5b8e7c3d5aa1e11f0d210cab776b5b7d1fdbcf1330114561137118a573d4e85cf86cc5488b8a844109050b475210e7df7a0d5f3f93f92cb55920c62fe88de5e006c16ccfb3bd743ce316a8baafe6acc43857db894d285d89375a807129d682358f0b93e83ae31f06d1d27b44c1d9476bdff4677c66fa24955a4273eb6bd918c5580730cfc1bf8520737cc0fb8814d963f3de72f8fdf9aba5a9feb1005c08d7b590f2fa75fa602967dd07b4603595e3b38efef0f26d4b2e943e1871b63479d4fe37c597e3fff39addea8b9ad4c7fe00d24d2adba9186a541bd69baa8d7062bd1f9b8fbac2f1fa00170c766592ed3e01912b24758ed0041f441bb7d3737ba080226c380ad7e766eba96399180b18f59bb475dfd1be417c50c0ac7934a4f55b9080ba4661680030f428eafa06dea84f1afcf60735f0cf7ce0939bda5433c36beddbe9b6e65e3a2f4216a530b24c41ca9745311beaacdfcd8c0c5d9d1d85f0cbb68c77ef1626a44bd209acd8f700a7adeecf94a5d30e3c8ae9c0134aa682b96275816b962bb390789b3253ec11c5485ecfd0b3387fb1cde62c22d6707093c53ac902e5cc0e4f363cc3dd810820064528765f21a8f928c85749fca1768571c685fbf3b99eb65eefdff1a232d95a3be6903584a45de52922972d345a12f0feafafd0fad3705c36ed4472b0637dab1df4f4c30e5fe2eaef09bc9c0ad59fb081a13d4bc6e72db22d12ba088f3d9bd0c96ef85fddeaf5eda74bea0f323ae15a0d1496293158d590f65fa01c87ac3e64facf894879737d20d0b1343b19b11c23a3c669b961b808c928399837145d2144634bcc95296a35db128bd780d6fad47c642339d7e2d3bd49455ec0dc893d6a3884e15c48c65837024637f9acf57ab57e6ad5900ae73c69ee6e88b0dbe2d51e81f8a39141ff8ecad5cfbccd3df4532a0c0a433e3ed6a22ff8236f42230f498f3b208a61fef15ac1455b6783b60b28017959dcd75df55ab591240be76e2bdf1cbb575b0df1cbf8210b2d4904755bf4a6c4b604ff558d9be52db6e8a7b790a11b1e89138913d4c946695a2088ea2cbf148e0716dcba54014a58e54f3314691289ffe61784e0cab5e8202f78414e36a7bf52153333f697a382d9ddaae6c7f9bdc5c611fb2568665c247ea950b3cbf32356bbd55d7c98d4093e4012cab15b378026af10e1c7fad4963d97993c48dc42dc30a4b013dbef0161651dc2797770e47002ef8569cefe5dc2a56b9ab1af917902620f3de081dcf31cb9af02772c1297ff819416b9f08cf0ee625abdeb795477cd29524554816d3bb675290ffefed3e33515665dc4da736fe29cccd7e0fb4e70ac57052bec30fe76d070e7f997499d62d42f0220f7cba6f29b3db3ddbee5b7816675af64b9d08f6d6213e49ba5e436c938067c1d04c4d018b60b96eb9d14ca9e1121aed3b3dc9c0990d7859f96f4667b24ef1ac32f8d2bdc0cfa73114c992fc5efa80bc67c0378aa596c370fff69f3811ff1eb38a578c8aaf598abfc74c81b120cbeb475c1dc103bd6b86b5bedd3742f70de64b0da208ae8c6f42d49df66cc30c740b8404e2d9ee202486aaa5334d819eeacfa03b873b9d9f74f0c69bc78b9abd5ca1ff42b82f0572353abcb3ade373db7676cdeb728fb40603d9a5046a245d3a413356a8063524509a79a9456128da1a9019cae11f9bac7718187b4d77fd5cec7a1798987accd471d3be22b98cd847bdad4ebf67824f746bde2b72fe3443f18185289e7b0f2a2d35c89ac4c8e8ad88c9ff86e43060bc00e2837e39158d3e899e34400bf4b33ab8248be2f062db014041d4c422945b2ddf6dae632fd235cff8e7cae38f7aa8d828681ae1e8f08aab4fed29aff66d46ddf60cb5499701464b5e837d4a7e0280af65480134f84a42c68bc2d2b37c7fed5b0d97240029be4d8002333fed58b8d84748c331fd96c7ce3dfdecac736cfc52ee844ca2267d2eba16b7fc930b23d47bf0b5d5cc12ca740747277719bdc0b1983e95c8ba1fcfc7ef6c15a94734cca4b400d048eb0505511a1d9d60a433417327b47f8729140c6dd326559d24db1a079f0fb1c3c8f53f12c586445d1a736d35885fa8ffc88c693cb2a3686edea02cc88451814005ce52e5c05ec2e99bc7943d83df39145b74797b5d30295b5f24dc2d4633776735147d2902cb71daf5991d599cea38e2f15861ff17c30c06b22683e5a3439be67c2dfca36b0bfd3bd8ecf30b341702db4457bcd74b02c2b74820fb93753db3184bff03f645b3dfdeea195400000000000070eec997eca79eddbfefba48adf41b107efa9804b4325125ec20518b9508186a63115f5acb9bf76c68da2d34dd941b0f132674cdf228b71a46967a60069f0e608cd917f5b968f2636019de739bfc014e6fdeadfe3263229092426ead638c3f8ac0a2fcbedd2a116b2937244c15d4a40dd3b940ba54c4406dc517a1433caf26b38f90079e6043a1fe1ab526658dd1de39853f161f267fdfe4ab8b9f5739588419abd2bd0a1d99041cf16f9a73a82fd48d04b9daa4384bb9742905fcc9f3ab9080b062f5275eb8ebf28e329725715c0ba01312edd85f81b954728e053518ae7911ee2c31e75c8d836377670336624ccb760820a584d3ccc13e2d505b507620379784149034ce52141ff633cf0508a7c7a9f2d2cacbd82e7c62ff544be7281aa27e40512f68fd9cfc35b466c829b3fe22f953539c0baab9c4a763d5210a2be94a64a7146dbf2c76a5029949172ac64fa14359d71cdd817c3aa6e0e0073981332102772e2c2d0165a30fe94b1ab5f526be3c21877139bcc9d685d7268a13d06177cb8b0273577250584c90999392f8127c387e03984fc5d75c5fbfcc4d64fa568bfe094c2f8334d0c614b050409f2782448a1ffdeddba77e3e375e64974ff22f58", 0xcf2}, {&(0x7f0000000140)="1ef646320e8298bfffa8b2a1bbfb4ebdb58a48f337ca9e47561be058fd4e0d4b4c76ea24861e96323ae4c7eac08c3708a683ab257cd184c9d35c4953613628ccbbeb6cfff60f99b159e6117f1919e7316d9f7cf6856c6da15f36bb7090f19fc8e0a171be2abf24c70c82a2bf06fbaf76f24b1c3b21ec10acf638740951afea8632359235a8b2cf43cb8511d89f53e0f616e7503c1205f08bfe5817eb8fe01f0d2823d95c7180946aa042f9dd36bbd52fedb9fac62b233beed1f686bb7ce3c7a8", 0xc0}], 0x3)
writev(r0, &(0x7f0000001700)=[{&(0x7f0000000440)="9a8fab2d8eb4a6ebdbd69775a443f51e35e2574e6291c1ff9732f9c18bceecbb4581154806307f3125f36c82a5d7ac0046ac9e2d55a4a97bb982134e50fd90d4038f94584eb617374dc40beaa963af0225b25d93dbf34d21cf8903414ca064fea5969aaa4c53fcb65b89b8e3f2ea6a3b9e3bcf5a56a8231d617050aec12c2a2ee4cc22957b4a80b7a24ec131fdb98d274f3d10d206f89430f4ada7045668f0e2d5f5565f0f346da98f886742c16d1203fb8c1d2c4abb2137148dbd8bc5dd1ec4365519b544cf39f6a5c4d600aebf18a18439c10aed2edc6c6acde29135aa468d6df763ad6753159c1c5a0c3ce3eea5dc07524ef1a0d7bfae4da0eb741194cd7694415557a7c82752fb76a497bc03f49a1d8efabd43135cd8a39bea327632760efed68b77c834bc70cac69fdd46d40314f6300969f9b552b0e313bb49a8a872c7b053b60d1f2df28c16a9981fca6155ed172c774d4647e996bd0bfbf384ddb84d005269ae1b9b583b5a", 0x169}], 0x1)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000240)="e3021d932704150e8dcadc3746e9ec1913fe510a15db410430a9145c5bc80bd0159a93b809c49f858152f439ba5d8141a9af01a1dca7e5e6b682fac7df93a5cfb2da0a794a378532dc42b5cec061f8b1b57de421b2914c197fda1578c2cf75ceef9bb2478865d1b2ff0135326d6c6967126e470b139a9d39cbba5f3002811ee288cc71d4afce2ca73b17cf2239f5da4447df12e67a7c017adcd271180e27f1b4df114b94be7cab08e50dd8a059d48ae6b887130967f791b9b5b57934a2a8cf076b403e20dc138e946f2e57d8ea74e79f337f", 0xd2}, {&(0x7f0000000080)="e89e00a77b9f491a19ffa382a103abf98bc714971db796497bd88138f30ee39ae93434fedde9696c463583a41394c67354f21a22", 0x34}], 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000180))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
syz_emit_ethernet(0x22, &(0x7f0000000240)={@remote, @random="c36c1baf33bb", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @loopback}}}}})
syz_emit_ethernet(0x3e, &(0x7f0000000280)={@random="eb6baa74efb5", @random="851592e07b11", [], {@ipv6={0x86dd, {0x0, 0x6, "c3aa20", 0x8, 0x0, 0x0, @empty, @empty, {[], @udp={{0x3, 0x2, 0x8}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0xc}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000340)={@broadcast, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @local={0xac, 0x14, 0x0}}, @icmp=@echo_reply}}}})
syz_emit_ethernet(0x5e, &(0x7f0000000000)={@broadcast, @local, [], {@ipv4={0x800, {{0xf, 0x4, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}, {[@generic={0x7, 0x5, "37aa75"}, @rr={0x7, 0xf, 0x7, [@broadcast, @broadcast, @remote={0xac, 0x14, 0x0}]}, @noop, @timestamp={0x44, 0x10, 0x0, 0x0, 0x0, [{[@loopback]}, {}]}]}}, @tcp={{0x2, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
open(&(0x7f0000000280)='./bus\x00', 0x400, 0x80)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000200)={&(0x7f0000000100)='./bus\x00', 0x80000001, &(0x7f00000001c0)='./bus\x00', 0x6})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r1 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x667, 0x0, "866740009382fd0109b8de0fdfad0000010100f5"})
ioctl$TIOCFLUSH(r1, 0x82907003, &(0x7f0000000040)=0x2)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b100050460000000000008000701000000000000ceb1fea7fef96ecfc73fd3357ae26caa0416fa4f376336adf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4", 0x44, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x1a, &(0x7f0000000040), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x6c}, {0x4, 0x0, 0x0, 0xfffffff9}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000000)=ANY=[@ANYBLOB="ffffffffffffe2000000000086dd6087819b00103ab005fc73e794eac20f628f14a8af5c6678aba248f4370b31be6531f3cf14ec884a8f"])
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000001600)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002c40)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
recvmsg(r1, &(0x7f00000015c0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
recvmsg(r1, &(0x7f0000000580)={0x0, 0x0, &(0x7f0000000100)=[{&(0x7f0000000040)=""/31, 0x1f}], 0x1, 0x0}, 0x0)
shutdown(r0, 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e58b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c43f11000002830000", 0xc)
getsockopt(r0, 0x0, 0xa, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSTOP(r1, 0x2000746f)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000000)={0x0, 0x84b, 0x403, 0xc, "00ea3cc0a48000d0c4039c00000000ff00"})
write(r0, &(0x7f0000001200)='\r', 0x1)
r0 = socket(0x18, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0", 0x1a}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
recvmmsg(r0, &(0x7f0000000000)={0x0}, 0xffffffffffffff4a, 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000080)="b10005016000009005001b0007000000332a13fecea10500fef96ecfc73fd3357a4e7d97b90000ec436b36acf00b7804be38854991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257f0c942ad3ea7acb5d602000d7d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c500002002fbff0c2300008abfba0900000008e371a3f8343712051eeab71d89e000040700003ffcea5b3b68", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x1fd, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x20}, {0x3}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f00000000c0)="ba58853729347c034ee03ae84b60", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x61}, {0x54}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000380)={@broadcast, @random="069e4d509b40"})
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x0, 0x2, 0x7fffffffffffffff, 0x1000200000001})
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004, 0xffffffffffffffff})
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x653, 0x2000300010000})
openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
r0 = socket(0x1, 0x2, 0x0)
close(r0)
r1 = socket$inet(0x2, 0x2, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cff"], 0x1)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r1, &(0x7f0000001080)=[{0x0}], 0x1)
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105727, &(0x7f0000000080)={&(0x7f0000000040)=[{}, {}], 0x13})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
pipe(&(0x7f0000000140))
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80105727, &(0x7f0000000080)={&(0x7f00000000c0)})
sysctl$hw(&(0x7f0000000000)={0x6, 0x13}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
rename(&(0x7f0000000500)='./file0/file0\x00', 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000000000/0x4000)=nil, &(0x7f0000000000/0x1000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000008000/0x1000)=nil}, {&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000003000/0x4000)=nil}, {&(0x7f0000004000/0x1000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000000000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000007000/0x1000)=nil, 0x1}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000000000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000006000/0x2000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000001000/0x4000)=nil, &(0x7f0000000000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000001000/0x1000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil}], ['./file0\x00', './file\x00', './file0\x00', './file0/file0\x00'], './file0\x00', './file\x00', './file0/file0\x00', ['./file', './file', './file', './file']})
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
ktrace(&(0x7f0000000000)='./bus\x00', 0x0, 0x0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@random='wj$\v)V', @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @broadcast, {[@lsrr={0x83, 0x3}]}}, @icmp=@info_request={0x10}}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1ee0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x2, 0x0)
open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000300)=[{&(0x7f0000000040)=""/216, 0xd8}], 0x1)
r1 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000205, 0x25)
writev(r1, &(0x7f0000000080)=[{0x0}], 0x1)
write(0xffffffffffffffff, &(0x7f0000000100)=' ', 0x2)
r2 = getuid()
getgroups(0x4, &(0x7f00000000c0)=[0x0, 0x0, 0x0, <r3=>0xffffffffffffffff])
fchownat(0xffffffffffffffff, &(0x7f0000000040)='./file0\x00', r2, r3, 0x4)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240), 0xc)
semctl$IPC_SET(0x0, 0x0, 0x1, 0x0)
socket(0x1, 0x0, 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, 0x0, 0x0)
mknod(&(0x7f00000000c0)='./bus\x00', 0x80002007, 0xffffffffffff2822)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8004745d, &(0x7f0000000080)=0x7)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000002800), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0x41946465, &(0x7f0000000440)={'./file0/file0\x00'})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000180)=[{0x34, 0x0, 0x0, 0x1000}, {0x44}, {0x6, 0x0, 0x0, 0x2000}]})
pwrite(r0, &(0x7f0000000000)="e4ca0c4435d371af9098deec1d0c", 0xe, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3b}, 0x2, 0x0, 0x0, &(0x7f00000001c0), 0x0)
pipe2(&(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
dup2(r0, r1)
poll(&(0x7f0000000000)=[{r0}, {r1}], 0x2, 0x0)
read(r0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000110, r0)
r1 = openat$pci(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
fcntl$lock(r1, 0x7, &(0x7f00000001c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x87}, {0x7c}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@local, @random="a26c7d46feed", [], {@ipv6={0x86dd, {0x0, 0x6, "a0f54f", 0x8, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="588516667a6bd66376a4124c4519ecc2", {[], @udp={{0x3, 0x1, 0x8}}}}}}})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
minherit(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000400)=[{0x0}], 0x1)
ioctl$FIOASYNC(r0, 0xc4504446, &(0x7f0000000000))
syz_emit_ethernet(0xfa, &(0x7f0000000280)=ANY=[@ANYBLOB="3b1bfddfc55effffffffffff88a8020081001e0086dd6039789a00bc0800fe8000000000000000000000000000bbff020000000000000000000000000001"])
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc088444f, &(0x7f0000000240))
poll(&(0x7f0000000240)=[{}, {0xffffffffffffffff, 0x1}], 0x2, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000280), &(0x7f0000000340)=0xc)
sysctl$kern(&(0x7f0000000280)={0x1, 0x4e}, 0x3, &(0x7f00000002c0)="11b7f23b", &(0x7f0000000000)=0x2, 0x0, 0xfffffffffffffea3)
sysctl$net_inet6_icmp6(&(0x7f0000000040)={0x4, 0x18, 0x29}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x5}, 0x2, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
sysctl$hw(&(0x7f00000000c0)={0x6, 0x14}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000002c0), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000000c0)=[{0x4d}, {0x81}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000080)="e81f0000000000000014b6317e37", 0xe, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f00000002c0)=[{&(0x7f0000000280)='#!', 0x2}, {&(0x7f00000000c0)="57dc10a770d9e890cdcccad84e73728726dd6632032e7dba9ce94b6fbd107ec5f1c367a2e89cc4c6fbfd0cd0a83309", 0x2f}], 0x2)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000040)="04450a", 0x3)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000040)=[{0x100}, {}], 0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000011c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3d}, {0x3c}, {0x2006}]})
syz_emit_ethernet(0x36, &(0x7f0000000040)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x802069b0, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
r3 = socket(0x18, 0x3, 0x0)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
mkdirat(0xffffffffffffff9c, &(0x7f0000000200)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x0)
symlink(&(0x7f0000000440)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/file0\x00', &(0x7f0000000bc0)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000400)=[{0x5}, {0x1c}, {0x6, 0x0, 0x0, 0x10fd}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0xc3104222, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x10, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000000))
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x10000, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket$inet(0x2, 0x1, 0x0)
getsockopt(r0, 0x6, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x2, 0x1}, 0x4, &(0x7f0000000080), 0x0, &(0x7f0000000080), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1e84415, &(0x7f0000000240))
r0 = socket(0x18, 0x2, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "0000ffff0f00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x400000000018, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x69, &(0x7f0000000140)={{0x18, 0x1}, {0x18, 0x2}}, 0x3c)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$setstatus(r0, 0x4, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0xfffffffc)
open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
sysctl$kern(&(0x7f0000000200)={0x1, 0x4d}, 0x2, &(0x7f0000000240), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x4d}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
r0 = open$dir(&(0x7f00000006c0)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f00000001c0)='./file1\x00', 0x18278, 0x0)
select(0x40, &(0x7f0000000040)={0x9}, 0x0, 0x0, 0x0)
link(&(0x7f0000000000)='./file1\x00', &(0x7f0000000080)='./file0\x00')
unlink(&(0x7f0000000200)='./file0\x00')
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x80090d6, 0x0, "010000800000e74de40000090000000000002000", 0x0, 0x7fffffff})
write(r0, &(0x7f0000000040), 0xfffffec2)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x3, 0x1, 0x0, "004ff504ed565ada48c0989ab27cad3fde9be6b4"})
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
poll(&(0x7f00000000c0)=[{}, {}], 0x2, 0xa1)
msync(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x5)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
minherit(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0, 0x9010, 0xffffffffffffffff, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
setuid(0xee01)
mkdir(&(0x7f0000000080)='./file0/file0\x00', 0x1d8)
r0 = open$dir(&(0x7f0000000100)='./file0/file0\x00', 0x0, 0x0)
renameat(r0, &(0x7f00000002c0)='.\x00', 0xffffffffffffffff, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000080)={@local, @empty, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @multicast2, {[@rr={0x7, 0xb, 0x7, [@remote={0xac, 0x14, 0x0}, @multicast2]}]}}, @icmp=@mask_request}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="0800010872e966fa", 0x8)
setuid(0xffffffffffffffff)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x7)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x0, 0x5, 0xfffffffd, 0xffffff78, "08040800f60c00e4000100008d1b820900"})
r2 = fcntl$dupfd(r0, 0x0, r0)
write(r2, &(0x7f0000000140)="b08d51c7f609", 0x6)
ioctl$WSMUXIO_ADD_DEVICE(0xffffffffffffffff, 0x80085761, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_REMOVE_DEVICE(r0, 0x80085762, &(0x7f0000000240)={0x3, 0x1})
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="100000002900000023"], 0x38}, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, &(0x7f0000000000)={0x3})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000580)={<r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000080)={0x0, 0x0, &(0x7f00000009c0)=[{&(0x7f00000028c0)="e5e6c163bdc8601aa15d3937aad7b2cc289d1efdc11c0244c696cbbf97cb3a10fee76d921a7adc82053fc302aac7e074bd54983f6fc7d16c10547931f2e1bb6ed9e06ff2c53abea564fb9b8a95c8a2926f318ba222a2345aab00bd493a960281e50c17e7584c1d528dad1a42e15b69bfa6cd96057f0eaa65fe67d014ad80c543f9a20679aaec97845ef6f0232c2aa61292d142664bb6bfb97114f254f9d9052cd7c2566df420d46b59d820ee8d332e18f4971ad570027f86f7457ea4c5bfc23b1553fd6532d56ab9c0979a58cbe00942252f3d923f81d4544447b9d8f68ffa0012ad07d5122b2f88caf89948619e7fb576fdb62d11cedbe44be1aad5e5f8f119139d56a6db3788c8292df7c2420443f0dff218d4eeed7ef278e7f091af995cf98491a65d59df50e646f9fedd3a6e5dbcf54deeb60aa15800d5d18b6fa3b8af01de23177027753f95fd8c2fef0d929a42f3ba7a924fb3c339883867ad73e06911a60bc54e207a35094f5493c372283d5e70b15bd79ba1e1af6582f9e6f68845c4b3f363212b773ef3829945b24bac48fafae5bd", 0x193}, {&(0x7f00000005c0)="79a1633985ec2234fe52e6ee3400000000000000034ebf3a8bbd6bdfb31ad0a513c846df6131acecdf8a6be0f848f933a67f036e8970226f54bfd73bbcbd58ccebcd51dbc484ebb9329acf7abd42f7b95ced131e092c75b2f53d2718281c0ac7d47f33585cbb54040439fe38b6440da2aa34b0b75410153f10840444720ae717e18aa7ec3cc6e9d807a36722ff0837a5abf412996e579e7ab4beaaa8ed2c648fef87d862", 0xa4}, {&(0x7f0000000b00)="c403e72f16fd8f8e17142e3b22e1b366c72e184ba3bffc7c3e20332e67407f3151efa2757b9bb56aa6a3190c5bf0d8d28bcaca13ddb234c01739d50add879529106fa8bbc1a5741d2492b36644db367459ffdc9fd52828a565c2ca00d1b8d3d95b155bf0c44abf95751631e242afc600e3f2d10d6006b59ff6a08792948c10de923e1d0e0132a3a37897a2bad6add8bbd3e59afdaab157a3eb31ac9866b3feac601f84223dce198641675b2103d98a224cabfc01e98014daf2b44253d582c7f5d6cd969b6050d3f3bc93fc1d52561dd85749cbcfcaf84e1048b2a91c1e496e8bf1c115c4c7471cd1c78b9edf402cac8dbb4517f7e054f1eaf31485d5828b34915c2019b9463282f407225eb96405e966271eafe7ec6038269199fa3a53093aff831abcc565e5a157e6fec700c46cbf8edcb6ed0a4590483630ea9cbb12d59bc46a1029c00401df5f662acba64d43d24be9cae1bcad169f4e23516a76bcb1cf5648c458b5c0c4b65bd37db6611ba628d658e10da16e680f7e58b12c84e0a7e19b6b9cb368a8b9d195a938c5b7cf26cb91b2e68365c7da28339d79a94fc2abbf03ea5bb09396f213f622a0a8aa4e94c5cc87446a66cd500ebd00e2cb94a7f0fbbf219f91eb36c0fb731a6f7270ba234201809439764f7b20780684d9db7f83020ab7e2b4abc712f282b6c8b617a58e6bdddfb1995180849fe9bd94dd7aa3c41865c0561491b5234bab476028a4dfaf01e6869a4731e2623d49c88656b7151f59ed6b601de89e306ddad1c549b18e641f7a0f081e01386fcdb83970a6b43d8a569795801e100a4dc7eee125fd35296973157928bad1ad30666f4fed032e6e1135964ed1f9f8ba3da4b762518d8c586f189685600ffb74bc40f57c06ac1ea19ad879c6fb56883c24d3b62271bd8775b97a6d6635b0901bdd79e8cf7e43a23e6550db015ba2450b3cf84dc551dfee2af81f1802c6fb85bbbe9879b8eca17962226d20e2e3352fb08ce683d431c50d446df0dfb1f2adf2f2eb60f113a20106fcca01d4409ea23ba9f21e980fa83eb089573ed6ef52df5f5d1fb5ddb363601dcaa33acbfc64d3d71f990be15018b6e0532ee76511bc57fd12cafe3d6089b4fe26a6a4d7b31d35a881b555f9ddc5a3a80d33f7b1363630e7e0ed41b58d7ae267fafdef113c25cce54854419443d84fe6e0e91812dcac77fe64fb38ba0367fa80c15dfbd03692e2000430256d91aea6673357785cb5feb9e558f36435505c8ce096926e9cb0412674e1151427f44307718b6128b530b3059b19855a04a1be2cd05d107ee57d8ff5b492fb3ddf139a2590e510b03aa6e217505bab8a1c9f2fc79b9ff94f68dd5a7bbb4a721a6bf76f4ebc208bf9c501f085a498c00b3e3400b6a6b526085b7b645b55abc381092ad959d12dc75cb4bbc4046915d225bc11b9f8005b485a28329fe575527116d87e5b0daeb19070c21067caa0282d570bc31cad5dd04eed0904a2a88dbe0d3d19add2f96e2268a8256bec948424a22d5c7db6a1232b103daf93ebe2f7fad36ff87d439287f666dbcfc2d5764d6ae83eff292152f3abf94ed868f13e2d22b0a77f1b2b0bd92fc244816a7dc4c4634179eef4e765bc878d45e3bdd3e65816c5cd35fb990e1b44b50c7ea4cf435ee4aecd4d979af845fce1f8be354ec40fe9e1733c299ef4563ad126f70d3861a3e4f86a8ccd16c994a21ba7ff0c9fdad28dff62ed6d2cbb1f0bb790d4c9ec56f0778b689672a38df057f804bfc05779c1c86b762daff2882c5f30936f912bf5773325ac50886d7c98367ea35d105d0910a50d1fea0df0a723f97b615def2d25a134eaeacec520d570d11eb053a646977b2d0eb12b3d8929f1cac12d9ffcb1959a791efe50bcaed8f539243de8c40d283d77433b3c04bc672a7a69e62d02724b4017b0b0235bde605d2b3132d83395ad96e775c98f17df02a522935e5f95bfef50f720ed0708cf293280d499f89fa026467ab83a56b3978f73f07e3e7c1aedbbc745577b5bb0dd01091833c649a371b48bb326431126e781d92e15f3d56aad4d280b219c76a3231fb5b435a15c89f03965d30a66fc3a747dfdeb1a785dfdc58533c5f235b249fa0c51b27a544116ef6137058cb25cda7a14185180ae3473c5f39247236f17ecdcc3e53cdd1511ff86e087727004bfebaefe62a9e6b8494027ad64bb3029bf1ad58c471d48e33eb76be870144f56e13e13dcf5fbcae3b1432beb2f3b384de62bf430df18d31da9035806405efa28292a168006bd95d8f9adc8773d0f9cff4cf5fdbcc373d59219cde8e89db12d882f7f688d11ed9bf34340fe36d5c43e818dabc53dd2f40f9c62023e4dae5e343a9bd9023e4b9b29fa05e533254be8a715b80d54bbbb832e2ed268fde5b59ab44ff99e4265f770f302881e19be1f73f1256c3278da2cd27ced5cc7a9da70d9b2a1b7a2affd4f78f9b70cda4eb59ca74df85ae3a031048a31fe3630e41d9bf99327304c58155007bff8f810594e45e6d2d949f7d3bf11fa4a3ee38337d5dd73ba5f99b6e033fdb663d5224a602ac580ddfdd2d5b542a07d50e6c4e81bfa3b641d1d97ebd5f598fead1cad2b0e5b60fb7ce6c985cc0434eeff8aa409bcedb49ab266fdd9b320900fab43aed5f9cf3a660a4bb196a6aeb78460f3e856b6d91d4b2f870d3d9c20b53ed4328146f28a1d879cfc4e4f54d95e2f9dee9a89d77a64e4756abe16d5b7089422ae991397d9c6cc5d85f64ab2f7776fd811be120d719c8e23e41a74757f35246a5b3144b33dfd654bd8abd7fe9f9029a2cc361ecbafa6943d803d4c341efdbf30dc831cb7fad291c7b9a987cbe17e2411f156ad00942f55e1a4abf273132183c9ad1823ebab62f375eed80e4d612e84cd089b69c5e5016c29e3003178acee26ee20a991d160e0ff0f3103bdd696e73357bf5f1383c6fdcfe7da384653b22ebbf6ade48a6e4ebf2d99cf6792f827a71252ef339c8c4560ecadd9c1449ae556b59c34fd6afc4e34d70aca52d99c985d6e24bd4235b58142aa6ac9375f9408579e3da93310041a944d75eea1e2cae33dfe2213eaec2f629e6cdd7d3b612c4ad429134e32c57406b69c315b96b347b4cfdbb1ccb2f11ad837c0f586af0ccab8b40d48d1bf878a4a81398e70da173038f20bb78329192d50ee2aee7a571465108d2c1de50c3555a173e515ca2c028907120c5c93dec269655806cb6293acc5177ca71e16d78ad24c7cd66b9ab100ad2e974fb29b151609a04517436e8c1b7445d121875627b528e458f3919616790ca47e6a144ae8536c413992a0c7d3b3ba250ee1ea6499adc8206e4ac741bed877573c5fb92cdec8a709475355ca1cbfa7492f670b4b008cead22c33c6f7d09404abdfa0f480b8343fdac2ef37a381330d9a4fc0434707a35f7b0220a83d9feca23586a78401924f0b407fb26b0cda77d762e2eb1acf3ff948905e1b52a60307666038d3b3b757eeb329a169032e3449d3d7c37b1e5fa1abc1543e420ca61c56ba813c150031287561a796b0c09bae7737e677c73e38ca1ba44bd88b273cc9acedc33f2a1bb76fe3afb985e94fd4e101b9919c35baf5a6769c2f32b58bc1ac45a43d8755b9dd29e4fc6fda9fbb850e267fe35302abe3e0c953060690efee468bbb046e74edbe75f365cf799574cee24b970964af4797669f4d3ed2004c4a4e9f9439491fd341119a41f7764fee733431ec99db060cd24fca730afdbc422ed96921d5cdfc6fa6b5c671ec3e5517c6294840f2d1cec19e1289941fd9d1203b6057c9f5cda67fa25a8b6a5c4ae9fae083f29ed0f07c55688c009dca6e1881d29ed39e7f7535db4d6ccb3ea87c0cf413eb9decbc16d508b121543de700ab335508d1e23145e7386194572c03ffa90b4305b15e121f79be34422d90f5f50b9f617e785b75e499db2825fa7e42b7b609629ef5f6c1ed52794d7de4e96655b645e68728b5896d4d016de7c14fd4f0273c3bce77fd594ad0277865b32f7da86910959c3f5f72ee0b86d4fc0e1a4231f40b4702ab9b48308c5afaf3ca94d9b58f60a0d16c53aa15c042265ad004a6459e63dff614b711e06836aacce3e6bdcee1eaa073e16083e98da222e8596bb922402bcf494daff3f75553975cdeabcae0d0eb2e3f35edbf5de518a9f17ecbceb27998f4ee1b7cd7d6b80b195022acd8a7112bb8b8fb4adb2faaa83430330ae51ec4e39e91461ccde68081dbda579e6bdc4304c4a8ef49cc5d0a56c1e5612effd6384fb588156b31f1882e4004f3d22e24a7e6aa2fa8e7e5b9a47b51834d6e0fa760187ad855db3d60ff691a216d8add5e0a5d0c6904562f95a10ad13c300d8acf58e47a2f5bff78048baca318a8168e16fccd599189f93a6e4b0250edc65bc52e6804be0cd426d6392c4350f0a159f2cd5931d4f3f06fdb734b43c9dda38319c2046441a18bfeefdfd0b6e99df6ff365cd0600f249a513e679946608fc853c15d8a67f8c7f3c5b485957083eb3c60714a112f725a04521e67e513c88ac1ad2460549fce79d73e516ea47d7f483fbd9ecffec3e7938355c6f7a9f91cb4911a232896855feaa49425e13c7df8fea43e07a260f2bd08b31ceaca552737a14b43ddc0f30a2d0de9e11b9a1ea8684b7a8007d1f773b00c510a39d8712b88cc6e501d181e876a9dce78d8610f0c07512018dc282932b64d77d551733b4dd6e5601607e9521e48aa56c06c059e522b2579170e3603aeec4b128c44603dec41fa45ac15382140d30af09b19b32d7f447e7ebc51d1cdca823767490a701455694b6fe311a9d6bbcfcae9b15c2eec99c0adc609d9d007eaeb6957146b3de03d9dabeccaf05e980c2e8721f39c0f8e12ac13be46fb2a6d1f03372766b4bf0c696d91067e92665559321ef64c82aac1df06921e9a62b4c0f4703acd815412b1cbd84f4de5023c32ddb9fb4b94352f2e53c6ff9a4fe9051237f9414a287eb2835e6651695d6048ce2f3b0ab7a6133f1eed83154d1523adac224a62faa83e01b3c89ae6164833816aeb61cc8929e0f1e38b7bab7946f82e43d74065e7a9a8820a443d3b5b0636a00cc7c4fd6945bbd2f517272d84cbf24aed17c637f4221e775b5f378c5479ee4686b83702b7447dcd8e7df135cd73d0bd1ecc6f7bf2d555bd2f7e0292ec8e63b1e947a2d812a5aa0ada2e77d7d54c421b06e325d3201947fe38bdd5f0bb51d932b4811a4668cf19f7ea835646c17be1ec2cf19db7805b61e811df00726f8474642e2c2bd85dfd9fe0035d5a0eebf94ceb972a4c228ed1bfceed8bbcaa16385c67180196fc38bd6124c99a68ce4f8441d29c4202544d8afbcf758ee28e535bac736c0affec07ffae1322064aa2b2dba87278408a3f5cd60ef852e77745c37176784c7b9c21a14b17e37f3211d9a3b5173245527fe42150208aeb003f0cc69c6e8eb4a3c1e4fa0c8be4000242e17963a89792c9cf7819530f11fa2cb97371618ce6541b24af190fbc3d20c3aab20d5c4be5b937c5890cf11deff8174a5f86ea8cef44896a3ef4ca3665ca37cf23aac9b78de4a0941aad6419e657b014d8eddd36943a4f8d7c948d1ee55064e34e08c988dce0a1cd2dd3ad2f56e42b7e9bf2d7b3032dfffc8d53990f8e67565d7e2fc055412eb1639222686db4366da8caf2fca223a767f436d4eaffac67ae3983b4ef086452036aee11a4fa4262674555993fe3dadfcbc92447ea998ae121a2a1d54e861a9370412652bbbeba7d909850214441fecbaa5f808bdf80d0d8597995df2625366dbdb569a3f55ec8ae57825f", 0x1000}, {&(0x7f0000000380)="4a10fe4eca79577f4eddb2ffd95cb0a180c6ade50c0d78be0f436654ac3fdaab77c6ced3871f2c922efe7e420688940e39089f7d1e5857117005bba9c1fb41ddb5d01b034fb2ede3007a74c2c3a67e19cfc685ee51e79c4c41a907722b2ae16ac355a99dfe5f69360efe7fdd99a3e02724ac0086829eb305794588f138c8f60f0e9517a38471517abed4d5614b0fc294d2d09c7bee63a8563e0c6ae84a0d93723f89d18a7537cc7247e3ad447e173418a906cd3a154f1be9386a3d9ff4d58d21bde56d4ad3bd2a9be5c5b8ad07d07a2c0fcb7c834980a9d2ab6ae2", 0xdb}, {&(0x7f0000001b00)="ed524bae6e5b95038fce3b8e3e3803b9ee27041a27631626d1e301788e8c12539b547526f4ddc041533509230e8bc599ec0bbb8be1158d080687e84c24b7500d305d2d25e51575c30578fe0d5ab4a5c01e6a703b90a217944d761373220850ff259fd377ce00b9808960150dcd73876ae95e72804c43ece9ec8ed278fd8c2522b6516e7a303bb7f24efa30c77c3fd0b09f915bf0253cef3afe9413dcc9455ba0b0758e4072d51e35e7369bb1d95b3ada6fb89a0d2076dc8e9313c42057df1eeb38d950b3caf8f885325b4dc952f89449cfa36e01399c4e8ca7be5d6a015fb39a1d80ed807f114454a0de837d590ec80ad6e28f373a01c761c3d27c8be167aa516a0891a7d22fbfa1917fec90d8401c4ea1d852f7161c9418b77ed6a0e042a3f2ca9222467d8fa96cc011c6db575955369a86eeff08167f6116a1bb24b67ecdebeca7ca760872aede2a4bc9e618ce17d1990fdefdfe3e8f0ba22b9e357a186a0e6314c938e9e7247654f3685d8321561b797c1f2154810093dd390195d37a589377611c81c3b3979829c73a493fcb60ac41e3e49d78ae7dbe7be3096ad01bbdef09f7a2cbf6e0e6b0354071c9ade449aa539d588fbbe698e2db664f6b785e498cc20b05dbb628e807b8757e8776007c1c4aa1105fb14ad5912912827275537f16e882a8e746c18d86549ece6cff1903370c6ffbac55557290636372554ef689b66d7e39ce44a4ea126e5744bb14b3f11f88377bd98b80733eb8c6979597a57b329ebdc18a6153bc89ee45a094854e8c76b0f42e070c353cb03c3b3ed5a7d4dd9fb643589969eaaa91875f54706a75c76c0fdf448c8de0113128987ba20e2f9c0423e4bd4c5893411ed9debadc6d7ed15936682ef19c000df659556bf6e99674f16feea213ff15c253c8e65d10209914459f33edfe9751bc9e1f11135e0190d8910a6e09c9b2ee7182367af0641f75dcdc5a7932336e0d75342648b394d1fde46e4a2614279074672242f0731cdeffc832ea358c521ea211d5958bee7b149b23336eb91c409a693d248c5adaffb246041ea84473cd2436fc8c1a677a97b15abab3ad9baf6e82748a5ac6fe154bb088d1f96f078b594421337d2632779c9a339e2af9d9eb45d18306a599068fea8d2bef4bd50cbffa26afdf64474bafa1f338e9aaf79d75ae185089dd8ec0b998e46fdeb381467b929b314b6e6304ea36d815c63274a08716bc4d82a2b1152853e32b7e3ddd75dcd369edacf99db07e9a1e10709cec58aeac80456ccd082031c3503fcc0f785eeeab297e76d8f5840a148eed8401ed69a9f670462fe6e9e8f72b2d196171a0e86b070841fa14aed5b06a7c8a8440f123b21c1a9383ca278fa637b527185dea694869a99107455b448647973793409407b74f05b63a3a9cd0d52d4675486417535695ef1727b80895c5aa1b6f360d164b1069ae0be632cc8c31277cfb050d51062b6a551a5666c015698daafa14940474e242822d29ebf2a4baed9d174fe9cb09389c90a61530b81c79053d0c42d8ce7df405bcb435330215b7afbbf770f619f24aca038ae1ed400115078f6e264c31ba14c0378543bac5a8bfffe9c9344f6aef7912972f8b3b6949aac0f95e5d1cfb9e003e2725769a3e3d4504e9ab8720901a5fbbe1bdf6a90847f6d8972dde8352d0f85896d410fe3849f57589565a94e131f637d78277587e658b065bc91e85565e35c49b4a60d4f368b11de94de3baa81f144e1d288adc1f93858fce2a19884c8d7c477e6be5d02bb854a7e0ab7c327382054fa0c1545a4cb02ed7be1f0652d2201d0983619a5e33f8f9e420f84c9086497e7f76243f0d7b8020022460694f34652ebcdfbd3d99573332488c732dfec990c701e49403be76e11fce89c04bbea2e84ad306cdcba18b921b2b4c412beb1c414b183bb840509b7cad690a75ab1e91c960cc3e8ee4a7eb554cc68f60679554bd1459e26a036aec3d445a3287f32e2955354b969ed52442f83338ece9a93328ba9927ed0689bee6eedf393dd2557ebe06c9780b6b9a8469c1dfdb4876e05fc9ce63938ae24373022380cd8a5088cf346848b2845924296da7b96e31d907fbfb33329eb14744fe857103d02e59e9cf1357cb959489f692fd5156d6cf9a3991e73a39a6c6f544db5535cde39564ad699c1c7976", 0x604}], 0x5}, 0x0)
writev(r0, &(0x7f00000024c0)=[{&(0x7f0000000480)="636fbc9f2b1be5e4efc5e444a5d085d20268b0346e20a9c0e6af216a15b3a2c0c0664b9aedcdeec95a02b5319727eecded4d3d9f37e01ef213cefe2021495a756adee409f22746cdde50bbfcf29b5e2b2708ff628b5dbfb1ff2b7abc4db63acecac724acbb079c99f32c07ee4e0d373c18014d7af5ec453c1cfa2d2a7ec8c382e66d2e350faf7f3db0786cd5dc267994ee10b4764d1df202968e2bbb3cf41db0a608f5c697cb166f12fd3ad9aa210e4be782170c02fdcaf47cbb9f744fa7bebfcdb8ae28c25413eb739dbb", 0xcb}, {&(0x7f0000002580)="98a35d9663d00f3e8e98bce68135802e5c94b7c030d260d6dca356efa9d1dcdf386ed35953ba41f6aca5eb27e022dc2b976aa6a967bb3d7e70f83654d7d9cecfd9417b84b1579a6b47f3ff3233b83611448a7842200fa717672b3a9953f8651ab9a8d472721d54787039f9ddd4aa6d4c7a2ee0f8461e80f5d04467b59873636a12887ed14d10132a31fb366c70af188520668d47a1db2afbcca10f7eb3e92d121a983393d073253aca98e4d5095c2b8350a702b02e6fe084460d9f9f375a778f78af101dbfd87f038b790b8098c192290acd7df7db66807dfd5e2f171a8b77fcca54fd28e7b20a9a83cbcbb4df9db4e6f44afce0", 0xf4}, {&(0x7f0000000680)="dd3845d780bfff5e45fef6dcee43bc7b353e51b9e9b45abeac52a3112d56e7b03688ee610f968e61cfae9556e7f16b57b42ae1eea22e96f50c5e822bb293eb303ea167c186ae0f385d182fe6fdeee4e73d8c6c150e18c4b36cef366452d68d62901fb6d5baa04bac2620bf80f1d91f4dd300f579acf1707203c32c75838a170eeddcdeb3b07e63a8e4b054840a65d0d1ee808c4206bf6ed9b83575f83b71792bf90f21c11bb0167f9e5722ce3e7b10fbd3bcb25031e8c99cd0d2daff7f2dbd411404259a9a07a08319513df5404f654fff7659db9dbf59", 0xd7}, {&(0x7f0000000000)="7c8091d4", 0x4}, {&(0x7f0000000780)="d16acac77d8a5df05431d67d5a67c3402efd89a9d46842b2ca951c22d4a3b9a63476f8d3a22dabec220d4c495bd8fe1c19544005ab3dc722c8e4640a24536bfa73eec926bf9080374141b4c5a4a405a2381f7d8ccbd3837f80559d4cad89376e233ddf287cd9b80458460443be95e8fb321d2361fbfdb6f61de5e79426b039fa290009d13430b84b0f4dcdc823e6ccf028dc9d57ef207a08bad87fb0c439f52a7757e4227ece4faa729786c83ecc0ebb93c80594d25b8f3f4f56fdebc5a34e42e6b7e119df8e", 0xc6}, {&(0x7f0000000880)="b0faa9920be472ec0e53f5d3e727c428cf65ee4d232763fef74ba4294ee35679a64d719d8236b14dfe026f4474ecfd4832363b5fd05a2043dd0f741022c3b65a83b4dc043bdc31c207d63a76950f7d57e2787859846390e23e7b3d92027a5d4bfe28ed95fade843e194c012fceb1fb9425bd7ab83b5d4429b8f3785f23cd3fd1d80223e3a456f7747e6f61e851b98b05be39902c242103016737981842cf4ed47d7d85feb723ba39eff1bec631bfad4f5426e755967320410a9ee3605943b4edfad42b51a1e6774be2eabd2bf2dd0aef207b23b0d31c55df89ec33e651f0299e11bf701c1bee200b65fa13b974daad2c9e4919b36f0ab02255", 0xf9}, {&(0x7f0000002800)="0bb9a76f3554ecd0ace1dbaffcc5f9de93fa2ee95e03d136f49b0429c2c8f440800e4883a8c03e0e9bf6285877a33d149c52a683ee2ad789ab70138f01ae81d2ed599c52cd3c9b0c31bfdbac85e3cfe3e9daf92b52be25552f34eb5283228750f63466a02520f38854581f42326f64072fd24d384a7e56abd70ee2a9d9b353a2b2e7033889650ddf083ad39e0556b3d103f5d22260cc29d6e36a27613e46edec45420e", 0xa3}, {&(0x7f0000000180)="93f7f06550008a100b005af49ae3f0a05228b85e583131c294ec711ee2ba9b9d82c053b28c74810f4a20277b0b985105f97fbc9c59661508a94dac2a", 0x3c}, {&(0x7f0000002340)="261c8b8fd50e36480a08f64dafbcd948ba7402e18d064ab426c5eae1bafa8e2a1d76afdfcba1d79cd19de15256a3dc8deb652d6b744369f330533db11e6f00f9a2181826511cfffc26791cd4ebfcdbe09e454b72f0300841608bf6bc44459ad18e45caa4f879b732071b6dd09965a8e8209bd2b4786e0b542e971ad9cafd5d0a6c151f1db4522f138a274e81ed0df06e3d9ed7937a9130afd5a85a61fe5d9addf7fbbfbf0465210f8f8f784a5ec4edf942f0cc5bf97f18abf4bb156850a44e4cc8ca75a727d9475c8d988666b50ceb09be0c492c070a9c555bb40e5eae6e41fb2286ae0e1a", 0xe5}, {&(0x7f00000000c0)="82a599f91f3d74f4763e3f70aec5e0feb8e1b8eb93c15b2fb7fd0da672e94eb3fb3723ad9eacadced077230dbf6bce5a8ce3f6686b26459254f2c05e87764c34696cf5886e506ce48c184e6e93c9fc54210c1e64de74385bdc34ec58fb2d15e03cd18a0470ec2ca6491c94e70a74dd086cb363bde2e465acb746a80a7ae32671ec0e", 0x82}, {&(0x7f0000002440)="a3741397965d0d633110ffb770e000f7d7418d9716b6c3d037fc13fa03c526ba2580c6402906c5d0c9231a7053607fa99258e19cbeb09f5526bd4560c147d407a72205c9b81ebff313ebda45", 0x4c}], 0xb)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000240)=[{{}, 0xfffffffffffffffe, 0x1}], 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
execve(0x0, 0x0, 0x0)
ktrace(&(0x7f0000000040)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x0, 0x0, 0x0)
sendto(0xffffffffffffffff, &(0x7f0000000240)="654cf7c4dedb31e81183e5beeb28512fd098d1ca14afbb4f970a5280a13a5c7bfcd3505c38900fdb5dbeb6803c691be881cd0e477e209e8158a84001f50aab441aeadb135223bbc8ab9cfa472017bc87c078aacaa00054f32a097250df72d55ea1c003f5256ab10c5f85889c8c6bc1fd438d5d2ce387cc5dd0335cc7e3443336cd", 0x81, 0x0, 0x0, 0x0)
sysctl$ddb(&(0x7f0000000200)={0x9, 0x5}, 0x2, 0x0, 0x0, &(0x7f00000002c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x64}, {0x5c}, {0x6, 0x0, 0x0, 0x800}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0104419, &(0x7f0000000240))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, &(0x7f00000000c0)}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000004c0)={&(0x7f00000000c0)=@abs={0x1, 0x0, 0x2}, 0x8, 0x0}, 0x0)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206982, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x18000, 0x0)
ioctl$KDSETMODE(0xffffffffffffffff, 0x20004b0a, &(0x7f0000000080))
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [0x0, 0x0, 0x5], [{}, {}, {0x0, 0x0, 0x3}], {0x0, 0x0, 0x400}}}})
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
fcntl$dupfd(r2, 0xa, r1)
writev(0xffffffffffffffff, &(0x7f00000006c0)=[{&(0x7f00000005c0)="7a13424e09da91716845e5ff937aa6d82e2e2e9204df3b1b68711113358dc6b07bd29278c3a3510945e2034d5a7436059dc32db511936a4395f5f7c5126a4455d1c51b63b39d3ec91f32bb6c709de19a6d28484b0cc948d38faf20d4e431b0b781ab4d6e4efcecb2435b53fdc709140afa7cc931cedd01c68a01fb943151d532aa954a9eca94c9565e33fc17d8944ab70a2b6bbb2b45e65defaeeb118faa9c4461a95ceb6fe7e85ccd207b3a57cd95f6a0d81893b6a911b48088319a49f927e68ab8803655f07d6e96c462ac", 0xcc}], 0x1)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="200000000000008d742c007b80309905f87c8a5b6701000000000000000000c414000000290000003d"], 0x7f}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{r0}, 0xffffffffffffffff, 0x21}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f00000003c0), 0x2, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
sendmsg$unix(r2, &(0x7f0000000580)={0x0, 0x0, &(0x7f0000000500)=[{&(0x7f0000000100)='Z', 0x1}], 0x1}, 0x0)
kevent(r0, &(0x7f0000000040)=[{{r0}, 0xffffffffffffffff, 0x55}, {{r1}, 0xffffffffffffffff, 0x11}], 0x6, 0x0, 0x8000004, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x4301)
open$dir(&(0x7f0000000280)='./bus\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x3}, {0x4}, {0x6, 0x0, 0x0, 0x1ff}]})
pwrite(r0, &(0x7f0000000300)="008d79e3a3b3669cfc59972c8f57", 0xe, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x49}, 0x4000000000000003, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000040)=[{0x4}, {0x9b4}]})
syz_emit_ethernet(0x76, &(0x7f00000000c0)={@local, @random="64b339d2e043", [], {@ipv6={0x86dd, {0x0, 0x6, "0f1c9c", 0x40, 0x33, 0x0, @rand_addr="5b70838c98d4d07cd613d53d9f74e938", @loopback={0x100000000000000}, {[@dstopts={0x2b, 0x0, '\x00', [@pad1]}], @icmpv6=@param_prob={0x4, 0x0, 0x0, 0x0, {0x0, 0x6, "58bb7e", 0x0, 0x0, 0x0, @rand_addr="89607003e7561b5876e7c3fd16826984", @rand_addr="903bb079a80e93465b682d0b67f3aa4a"}}}}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x6000, 0xfffff801)
openat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
socket(0x0, 0x0, 0x0)
socket(0x6, 0x0, 0x0)
setgroups(0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1e, &(0x7f0000000000), 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e00000000000000001000000", 0xc)
r1 = dup(r0)
r2 = dup2(r1, r1)
setsockopt$inet_opts(r2, 0x0, 0xd, &(0x7f0000000140)="e800f8000000000000000000", 0xc)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000504600000000000080001", 0xd, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b1000502", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = kqueue()
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000240)=[{{r0}, 0xffffffffffffffff, 0x324433106030b467}], 0x0, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f0000000200)=[{{r1}, 0xffffffffffffffff, 0x21}], 0x101, 0x0)
kevent(r0, &(0x7f0000000140), 0x20, 0x0, 0xfffffffc, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000080)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd606cf65b00140000000000000000000000000000001c000100000000000000000020"])
r0 = socket$inet6(0x18, 0x1, 0x0)
listen(r0, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1023, 0x0, 0x0)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, 0x0, 0x0, 0x0, 0x0)
socket$inet(0x2, 0x3, 0x4)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7ffffffe})
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
r2 = open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
dup2(r2, r0)
seteuid(0xffffffffffffffff)
getgroups(0x8, &(0x7f0000000000)=[0x0, 0x0, 0xffffffffffffffff, 0x0, <r0=>0x0, 0x0, 0x0, <r1=>0xffffffffffffffff])
setregid(r1, r0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x3, &(0x7f00000000c0)="01000000", 0x4)
setsockopt(r0, 0x20000000000011, 0x800000000001, &(0x7f0000000000)="186fe65c", 0x4)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSRSIG(r0, 0x80044272, &(0x7f0000000180)=0x4861)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
getsockname$inet(r0, 0x0, &(0x7f0000000100))
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000100)={0x3, 0x7ff})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cffffffff"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
syz_emit_ethernet(0x26, &(0x7f0000000000)={@random="541b430518e4", @local, [{}], {@ipv4={0x8100, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @rand_addr}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
connect$unix(0xffffffffffffffff, &(0x7f0000001000)=ANY=[@ANYBLOB="7902adf5ffffffffffffff653000463f7b138a2a197a386207000000000195955984c87910bf453f68c11700287ed30bcb777fa69dfceac2a84e6abca64896bc5e57c6360bcd4c0a162b58fd4237b8fa4d012ae447472b3e8495aaee48c02f6abf1e6bf3bf554799b9b81fdd7068113092bfaa13ad30505e8f0000a400089d612f871f4385f04157795ea4a5c50700000000000000cac7db000000000014b637000000000000000000000000ff0100000000000021d39b4028adb1ff1afb37ec4305642ad2cf8724eb18089278e314d39667400000000000000000e02b05e5d12f524ab2aa8cc7712a2b279dbff4b7b46564ef560026e969c9341589ac85cab1a4c6b41dccf137c8ae135db3119e911c3a94484c692cac607b1c26455bedfb9c7d35f23d1aa551416c17f38969edb7f7744123053f35c0568241d85544cd1cfd1634aa3fc1611cc665840ea3b1e39af6256b1f007bf8f244cbc769cee44346040906000000ec89ff7f0000490ba02e9768a8b36d0400b89da05a0a414c6394359a00000600000000000000f808981d4e22265330027548d8ffda5eb1152f6bc23442dc9631d64bcd5b823e010000003d36ca293e35d0d6bc372d1a8ff42aa3cdc2e80e306615b7a814b77f3f5a659255490b88ff1bf33436e6731ad3574d79be64d7a5484d4132dee58554fdac1bfa4aca2c3ebed7ad1bd9dafb7504fdae2d0f2587e75f6a28b7f09126efe6c2d6ad126358a2bf7eb6652c52476b0f4a1fc0c7ee2e61ac55bd5d0e254af635e29603e129cc5b44d587c08f6f7c1013f89d3bd1f11b9ddac3e5afc074c1771cebe4f8f92cfdee2e52bf897cc233cab1d5ef00a8aa77751b76b6c98bf0d9f371c916fc7b88c013c7381f77d036b0233aca38ad8789d01c4674fb41c125525f77e9f616eb7246be9389e32be26a568d0ce05a3852c3cb4338f3bfee17a07001520dd2515741d6ef34276b23a8b086c43633a42e93fe75f8a85f37ee8c3f120c300afe7e7c1f1df26e82fcca1e245a58d78b79dc3b91721aa7885824e119dc4c67c825898793cd381a7aa89400bfc4c950eb3aab78a2793c3f1253654f503adf2f3b4affd04d7100008f33becdfff906d6e52cac7557c99a8ea2867ba54d66be96f8f93ddc79ff621af687923ec91aed6cb55ad71e211058b6b187190f4287f30056f0eca629fde07bbb821b117c90841ea6147ce522005f96566eaab02e083d8597e92269a7d09c1048d54e3fe2c38b66b4d7e28272aee313f431ebf190de01bbb11a2c979c4543f9e40630f56ae48f7d000000006f3501ce37c435b1d449d65b06dc2e15af79c285bd9d3a5d844e3bcd84dd0fcb94bc7ffb1f98e6adc147ca20bb93465f8f501542bcb5b9924e85630c5374310d409c86824d7cd06044cc952d4bfc84fa4bfbf568c07cce4173b5e86293ec4a9cd8e8f3cbc20274b62afc3af30d67cca812c4e79be8e198145af7ab215df2bee241fdfce327f83b9a812be0b28665eb2462d5d0731a58329f5ad0aadda72d848713e24a211dae0d7f7f1b8b52461fe4bc33ddaa99cdde7ba629b80000001b23723cd3b215ce8fa150715d7040e155d2a8f55c69d382df8570a63c3400311208a7ede233c2a205fcff2a8e1357ad931d4778526285ea70b50883a43c72b9e00c23a2cce6a7074b73e0a84806ac577b6112bc4d9f46eb8357daf26e99b5a528d39c547aebe4878d7b30c63d364a5f7f62c8a2eb4b8a7da1729ac2534fd26f87cd357b8676435f6c46b8e630e6d32afdb05fce19b2ef45e1147b78817b50a88cc1a1f0049484396e47b0b68422ce311330064957355ce77da8ecac6392a60a201d1598faf652ecf5f4207a31b342bae0328371101fdb7bbf1237472c05000000000000001300000000000000000000002000000000008da619dd77894b000000000000bf7ada7adb3a6e0008dc887d55e5550d3f01257334ee933781286956a19a8bcf1e960f7f82041049b0a46e41c01375ad442ab57fe772cbe2b5e3e1e5ac000000000000e30b6b46aaf8db428ac153e2ee6b11dabfa3f61407867bb49839a248fb28f7d91ff0c211d70e7e73f1b412a428e7c3032245680a7e5bc2a871d1b025ba5d38cd3009ec23c0eb6f7ae6da1b143baa9d524badecfc87f922b9188d638dff07000000000000a07b99cb04eb282a44b14a66bfeac9da17d6295a694bc601ac31b178dc5dc09500ffffffff8c5ff51b5719c21dc60c45bed29d7b308b5c975ddcad8ec4ba61a4831f28c55c3672430098b09f5226ed178c2fa7c7458c7b9a9248564fe2a7263b0a0df5bd778cb1808b7857df93c3bf1ed0e006452ad0b823d93c39d197cbd8102e06c5c40e3346b7e501a4e9f9dd4089754149244f896ccebd29b791a63d025ed57ce81118123f008da9c93d5ce5ba28878d0b3501394ebd068fb1128ff91ccf7626e962550f7dcb0a8299df6897ea240c67a07a84667f79ea7e2aeb00000000ffffffff1163d7906c8721258725c01a4e571702940e42d65b6acfb7b877c255e8751d32a5841662c721150e078e57bdf503bbe42e17f6c467fcf17842d75e8393bb83e42f9aa54f986ec66b55aeb4783d8ffff44a9693c0fc337342fe852e9bad066be8c83daba6764553ab28aa9629fa9bb518ec97d714f20ea2326edc5ec4f633019be287f0d5233d1ae0a5006d842f1330f102c3f40c24ca8d556c94dc650c15e4298f1ed76ef7f1e8731a90acf0543f692224d79aa9b6d15d007bf48fa9f297773448e0e942d0cd9c773c30923e1d5ccbcf6120182e587aa6377da502d92b4c5439ddd4671cd74c23c7452a79a504f06fb7f9c83fa3628ffd12905750199134916b0da9af5d62ba1853cce9f314d7e21e94fca813aa0105f35502b18cc734fcef943908b68627d0475b990549e13c3ffc760728a94bfa6cd065534ddded61516ee26e62ce726b5c3b8d5fece9d69e28c8e7b6f3428c94b2758053c30d10050b5a5535696d2aa0a0eb8c4a5e492416cd506329abdea802c3d841971450960000000000"], 0x10)
r1 = socket(0x11, 0x4003, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000000), 0x4)
sendmsg$unix(r1, &(0x7f0000000880)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000780)=[@cred={0x20}], 0x20}, 0x0)
r2 = socket(0x11, 0x4003, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1001, &(0x7f0000000000), 0x4)
sendmsg$unix(r2, &(0x7f0000000880)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000780)=[@cred={0x20}], 0x20}, 0x0)
r3 = fcntl$dupfd(r1, 0x0, r2)
sendto$inet(r3, &(0x7f0000000280)="4f77db513751738bada00798d342998458b0ae5e03c588fd997f93f34f9b2f7d699251fa13b3f0daf7944b1b46e844ccf72498831e840b7096c025901efd8d9a97a47944c5b1cf99954edad1983c385b9958054fed27401931ee5dd3f418cfe85ea55068bb38fd535a7c59404a27fda832d366130abea830d61d2e8696456053db835b52e35e8fa900d5bcf0f042e9cd85ae321b197b3ea193fd123e578b273eb08537b366506ac15a90414ead44ca518d71079fa211832dff78d12ff2664087310b529c297c75cfbaadb3d35637c859a28e0da9a31709ae659947a07dafe85388fcd04b1acc5e93e08c10918d162bcfc6213e6b6adf6651f62d1856af97", 0xfe, 0x0, &(0x7f0000000100)={0x2, 0x2}, 0xc)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000d80)={{}, {[], [], [], [], [{}, {}, {}, {}, {}, {0x0, 0x0, 0x2}]}}})
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000180), 0xa, 0x0)
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x2)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = fcntl$dupfd(r0, 0x3, 0xffffffffffffffff)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x2, 0x10, r1, 0x401)
mquery(&(0x7f0000701000/0x2000)=nil, 0x2000, 0x1, 0x10, r2, 0x5a)
mmap(&(0x7f000048b000/0x4000)=nil, 0x4000, 0x5, 0x2018, r0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r3 = fcntl$dupfd(r1, 0x0, r1)
write(r3, &(0x7f0000000100), 0xfffffe5d)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
setuid(0xee01)
socket(0x800000018, 0x2, 0x0)
setuid(0x0)
r4 = semget$private(0x0, 0x2, 0x189)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r5=>0x0}, &(0x7f00000000c0)=0xc)
semop(r4, &(0x7f0000000140)=[{0x2, 0x81}, {0x1, 0x0, 0x1000}, {0x3, 0x1ff, 0x1000}, {0x3, 0x1000, 0x800}, {0x0, 0x1}], 0x5)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000480)={{0x20000008}, 0x0, 0x0, 0x2})
sendmsg$unix(r0, &(0x7f0000000800)={&(0x7f0000000100)=@abs={0x1, 0x0, 0x3}, 0x8, &(0x7f0000000740)=[{&(0x7f0000000180)="57e79a0ccc42e2e68bf287f322c66081a752682db2bce926bfa1d025e78405753f871249242c57b045f9e7", 0x2b}, {&(0x7f0000000240)="91fb1fb430d1e01610e86688ed940c90347a94f49b9e705f431197473af6e1cca84152cbe46c3751730d96f61f80e512c5ae36a8295aea00d0bbf9d5fd97c9ca2f627553b1a2e8abdc50d8297fca169bbe2524a0ed9815c2cef1ba76dbbd2c4f77db9805d45025a7448f0b66ba11cad02445985260fac553a20bc645bb6aee6264407684704561e72af0687c8d7d0c9b74f4ab7bf6c787f1ce8c3284e3d2da17e0d86bff5dae0f31095658674d0897289141927bf993414edc9840", 0xbb}, {&(0x7f0000000500)="201be3003e1a38d34a1c91e567761934c63fd40f5ad1e11b256db883ed7bd3301207ea47d858c39699cb9d2ac7c928ad205a843d540fe6c26bd414bc68f7ceb0fd9ef6ce56babebd1a89864a9b8f80cdd7de1d7992e1cd118de48b0c006275ed7c286cf5e8b487842556e010b9cc6a1618a742b6e57e9a7b27bd9c7b23bebc3b9c8a84e2a18c2eb170274612275a1986170a1964feef34a680fb0acede6df9860ce7594a01e6b74805364bc85c54fae26ed2a5114899cd01895e46525720869a9dbe489a5ac90a54199163f29c3fd56574b35ebf6c36785b18e1d33dfadeaa5888796b", 0xe3}, {&(0x7f00000001c0)="687f4d4e12cf5db1d515a5fc729590e1552b581665b5234d3f45aef882e978fb9d71c7799136e8faa5d86babdf", 0x2d}, {&(0x7f0000000300)="8b629701a0b3dd75eb31ab010ff7cf", 0xf}, {&(0x7f0000000600)="65a4cd8ca1bea84a77c5948fc873e6cf5dd94a9ff837bee0097757a6001360524c4cb7f1999d330b813c8829b1adba95f17bc39c64053db54d83b25f2621df4e117d7bb27e00d816059953c3dd83c153c7083d6b0240bf8cdd7929e9084dab4d3711e45c1df4e4a4c0a200c369b4d653ddc8d78c0c0f5c6942a80638192fc014bc91e37056cc0106164062ed3f9d8427a84847e310964adbaf1714f8dc2c060453fc6bdc4977a06cac636d7e438324f554ea4d5cc79ad79f2fa316d4131ed86007f431ab6679a9c1f4469f906715c67e421b3fcd45386e01651dd70a9a65327bd2", 0xe1}, {&(0x7f0000000400)="1cdd5e0915d23d1b3cd9f563c46984587ca365943bd0ec55280355ed49c2f2092f6af3f7cd20f6bb73c61818d33430001085918362589eff8f2522f500672fa306464055099bb8ea22f9a7a1a029e4f749f6815427653dc643e98105b283602a127e3a03f421930c30083d05bcc90169", 0x70}, {&(0x7f0000000340)="ea1ef0e12b1ba0195a1f643204352aeddfe761b67ef9ee1016214ccd4d54eca5e142b771567e2872f1dd6312721c8ddc34", 0x31}, {&(0x7f0000000700)="981ae7404df8489de4d3cbb97e2f83364aa2c722aa7e885900af7aac6383f1cdfa76", 0x22}], 0x9, 0x0, 0x0, 0x40e}, 0x2)
close(r0)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000380)={{0x20010007, 0x0, r5, 0xffffffffffffffff, 0x0, 0x100010024, 0x7}, 0x3a8, 0x0, 0xfffbfffffffffffb})
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, &(0x7f0000000080)="9d786244", &(0x7f0000000180)=0x4, &(0x7f00000001c0), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket(0x11, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xffffffff, 0x0, 0x0, 0x0, 0x0, 0x4]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
mlock(&(0x7f0000c00000/0x400000)=nil, 0x400000)
munlockall()
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000c00000/0x400000)=nil, 0x400000)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000), 0xc)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000016c0), 0x0, 0x0)
r1 = dup(r0)
pread(r1, &(0x7f00000000c0)="14", 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1", 0x1, 0x0, 0x0, 0x0)
r1 = kqueue()
flock(r1, 0x8)
setsockopt(r0, 0x29, 0xb, &(0x7f00000000c0), 0x4)
r2 = socket(0x2, 0x2, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[@ANYBLOB="02022e2facbe15c80ba543240d3eb4e1027308849400994d7a3be09dccb96e733b51513365bea919161827268366bfae20902616f229e37ec51de71d38deee29fe64f99b97bdf3fbec38e17536d93c65c4fc6bd35621dea7468bb2ff4fb1567b68dcef73725c3ae5e3306e60dc137a8afe702fd505ba7eb2768713d33b2c7780011b82989368fd3dde592f02f131494436f7d7b6769e83efc3c21e79b1d60286ce722140eb180325270b50d3d7e313b18b6d1aa355c58d892ad877862b6b93677f0758160bf4e3e296d69aa0cd8b10276b503b410df794c1c2934d4b593752"], 0x10)
writev(r2, &(0x7f00000026c0)=[{&(0x7f00000015c0)="4904bc8ae8d0b390577e4163ed24e055f6635e1c0ef79ccc958341e430a81f30687a01298cbd15652162c3af5bddd5b13574bf21d95d498453c8437e9628342564f835f147a798c53cb50a395c61ddc5e86404150a762e61e5329e7d38a6c7bcf346ec1a6c065649de6b37838033772faa7a9ddebbc7bdfb632c9998978f0dab2f92427f83150b61f2eb5af9934d0bf6d1c18eb2244f58f8848c34", 0x9b}, {&(0x7f0000001680)="f0fdfde51aefc4c644824751d5288681b7e486", 0x13}, {&(0x7f00000016c0)="f3ad00964b95cfcd15dcc4892ad80c1ccf8063ff650a3185b580bbfd0e575bbcaaf7653fd540d347b5c4c132eb4c521faeb2f7851137f3511492d9d28c2dd71b1f044f2a3a56f2115de80985e6af8c3efb656012b6cbff4e8a0829d454f624dea96d87adeeafb797124e99764feb08b9d0aa6b0b914bfbc3b9d9c5e6e025103100e7618e152920b828474b777920a1aa72a62916ed18f9dd6a64d49eb1e3922b5984960e69172f31c3aa0f5b8fecf984d3021b2fcd9cda0283c1f0d7f4980d96e68c31dc85a819a56ed4f2a234a62e3c3bb7e54ec8c54985c94d10af2b81c262dc5d66f0ce0728ff97845ee120fe4cfe432f7d4b1a6a8c933de043945e1d7541bd3caf0b4e24014ba479b6e77a3d0100124b14a3f0c3e4a09e918c9d6049a10152064e5fcfd5a037975ee033e6bc03ebc6f108b60b7f1cd8c8e356b69939346940f10c27c197bbfd00d43a1c05749dfcb180179a7399a46f9329050a855f51ae0f57ecc2db1a308805b99e1042eb2697a611fef9de2eed908ffdc27915d13b307702854371bb660b3cb4128aba6ea9118f930d043f2a504e76ce404cc483bcfa6b73c5f35aa1984bafc6ff1da36e6e7bfb0c14ee8daa7b1d56a30006bfed09077c6fceb0b30f800aaba2f25951f5c29a54fcf2523f4858748243862717f4c70799853b39170c44e0adfe7274a760720a0a7522a3a5d840846075494746251357747190637db9e7c539049c37db4b37e14814868d7785a8817e1ca96161171cbdc8cf4677d08a5c7c30ac3dbb30a3c925165e68414a2acf3384a355b2f4756f283a1eea8c433f30cdebb9e0539bb428f82f8574537996c0417eb8b8c116b5ed91a9b21763304267b94b57b22fe3917c212f735652cc0d9fe608c0b7ac689da7d08e0c51d33ab275a2555030adc568f1e9e26e87977b640d7b9d15869fb7918ac916dfbd475b313cad367f5427fb50c2b760cb867bbe5aef2253d99a5414c74c17e882ba039e44428e6bf01a4ff1c8a7d9b2e87b5ffcaed33ae47089d37f6127c281b68a077c5f0180bccebbff47b99e3aadc29b295110e9ae44819d24583a8bcc8ea0848bbbd50a042e70d1d8333faae65990a05eff58bb6b862adb91cfe9e21d6e79f10c36902b165670bede088f535774ae1e0f3d1a0f25c9415c683df206a5837d4e3648722967b7e38475b04173b258b86a04f6cfd36dae2f4e3f7111f5e635df90c2787ae264bb359f32fe17b600dcbe56072715e3064bd8f512798593ce277ed226286c443745e9f39e696b886ec297c25c53568a3d86ab07c9e7fad231dbef96482f37610d4fd8328c14558e3c4a2d7a31e1ffa1d0d63c8ad9874dffd20f4d8e6195efe2f5dcf2e9dc3f3a5ff329e77b99318bd5d18b2d6930212df81fdc372cec1b63ba0feef7fa44412973b7558fbac932ccad784efd1b7f8203b409ae72b6f6cbea5c9974439f4092c7b687d8a38f113636f261a82e7791c63c4726562741546e8063660e79832d869183361960cb30548e05ae5adc98d6d2d097eddcb0e7351b81fabc243cb603c9affc101ba520f30f89f78add012bcb9bc398e824e91be7bdde940d714b66298ba9f23efabb6c074820fc60806497014a1bd57fcf23362691839114b2889425b542aba92c8fbf1d50f7a0c8da9a9be813d893c38cad23059c842475f766ffa37d10f201b64f420fdfef79e0c50899484102e737ee6190ab7448b9ec28896c6e6b036f2e46669f5b6e34d6ebb5939e517ae70bedff7abbb54ba62e2a22bbdb2f991216db8d64cea85128de58257496b1d5ccf2899612d615fb6f7ae3f8a22416d045bb14d534ee7550bb0caf720f97b307dfde9e0993a62d5678c55e51c27e68596e16f90ed1863e0241ba47d7974c028eae5305eca467488f560ec17d15dbdf8f710a44b078d93946052f3e3ec1600e3e557b8bb09f31dbc9198e26faaeda5e0a45aaf178c5481b4079491b374ddc36126dc760b7ebc7a297743753d13f429f919e72492ded327341a0a95b550ef1d118bbe72e277ac608907a5e58ac5f104b29878beb7cf6b11ad56ff1d2a545a670b473f628e04712bb8ac1543416653a9860f802c3f64568b7c0574e05c51e9be5f3fed296ac188cc7c7b26c1bd4a93207bd0355cd84c2d70f0cd5e4b4d62a85c2296b44f2ca24adfedfb2362d305701aa0764093db71144b7159c3f5020837bab64cf17cb616496836533b0e9a15cecd6ed66d20886852cd8b92c4a508bbaf39f95528df2313bbda2a1bd50a83ed1db8e2c0265a894a66e9a143dcf5dbf98955ee3447bb8b7666ea423a29ea482dd01f2d6f4dc542e40b06712bc7a36557140a19b526b0119ba19b69ba3e05795bb7b63c896a3e1c1831c1c319c23922f6a16156f288b4ff915234df53b5ccd970b12ba8015cc03d3486ed59287043b59d72aab0fc26fb0d597de6ff96a7c631d0c6f311aa7b1f1e44d4e5bdc87", 0x6d6}], 0x3)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x17, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = open(0x0, 0x8, 0x8)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x7, &(0x7f0000000040)=[{0x3, 0x0, 0x6, 0x9}, {0x1, 0x3f, 0x8, 0x6c6d}, {0x1, 0x40, 0x9, 0x3d98000}, {0xfffb, 0x1, 0x9, 0x3d32}, {0xa3, 0x5, 0x9b, 0xfff}, {0xff, 0x3, 0xff, 0x1ff}, {0xdaa, 0xc1, 0xd9, 0x7f}]})
r2 = socket$inet6(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
r6 = socket(0x2, 0x4001, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$TIOCFLUSH(r7, 0x8020699b, &(0x7f0000000300))
ioctl$VMM_IOC_RESETCPU(r7, 0x82405605, &(0x7f0000000340)={0x0, 0x1ff, {[0x9, 0x80, 0x100, 0x4, 0x4108, 0x80, 0x108e2338, 0x3, 0x8, 0x9, 0x1, 0x9, 0x20, 0x1b90, 0x7, 0xb5e, 0x2, 0x4], [0x1b1a, 0x4, 0x4af8, 0x3, 0x0, 0x9edc, 0x8, 0x400, 0x699, 0x3], [0x8, 0xa4d, 0x7, 0xfffffffffffffbff, 0x1, 0x7, 0x70268c5f], [0xffffffffffff25aa, 0x5, 0x9, 0x893, 0x9, 0x9], [{0xfffd, 0x8, 0x0, 0x3820773c}, {0x8000, 0x3f, 0x69, 0x5}, {0x6, 0x1f, 0x6, 0xdd2}, {0x1, 0x1, 0x400, 0x5}, {0x4, 0x2, 0x8, 0x3}, {0x14, 0xe, 0x4, 0x100000001}, {0x4, 0xffffffff, 0x9, 0x6b1b}, {0x80, 0x1800, 0x0, 0x401}], {0x1, 0x101, 0x1c11, 0x7fffffffffffffff}, {0x3, 0xfffffff8, 0x1, 0x49}}})
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000100)="b100050460000000000008000521000000000000ce24fea7fef96eefc73fd3357ac16caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebbc657699a1f132e27ecb5d602000d7d026ba8af63ff37282921e4fd89720fd3872babfbb770c1abda71601a8bfee8aca4911faff5a872c881ff7ca93c894303b22f310b404f36a0069000fcffe0ffe608a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x1}], 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40f", 0x128}], 0x1, 0x5)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
open$dir(&(0x7f0000000080)='./file0/file0\x00', 0x8, 0x9)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
symlinkat(&(0x7f0000000140)='./file0/file1\x00', r0, &(0x7f0000000180)='./file1\x00')
open$dir(&(0x7f0000000040)='./file0\x00', 0x400000002c5, 0x0)
rename(&(0x7f0000000040)='..', &(0x7f0000000000)='./file0\x00')
getdents(0xffffffffffffffff, &(0x7f0000000000)=""/233, 0xfffffffffffffea2)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x35, &(0x7f0000000180)="04000000", 0x4)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "020000040000000000000000aa00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$inet(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0x54}, {0x6, 0x0, 0x0, 0x21}]})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0xb1}, {0x28}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="6b18ec"], 0x1c, 0x0}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = dup2(r0, r0)
connect$unix(r1, &(0x7f0000000000)=@abs={0x0, 0x0, 0x0}, 0x8)
sendmsg$unix(r1, &(0x7f0000001700)={0x0, 0x0, 0x0, 0x3c}, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x48}, 0x2, 0x0, 0x0, &(0x7f0000000000)='\a\x00\x00\x00', 0x4)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
mkdir(&(0x7f0000000000)='./file1/file0\x00', 0x0)
mkdir(&(0x7f0000000040)='./file1/file0/file0\x00', 0x0)
rmdir(&(0x7f00000002c0)='./file1/file0/file0\x00')
munmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
mmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0, 0x1810, 0xffffffffffffffff, 0x0)
r0 = socket(0x11, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r0, 0x11, 0x2, &(0x7f0000000000)="1a7b43da", 0x4)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x0)
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000200)='x\x00')
symlinkat(&(0x7f0000000100)='./file0\x00', 0xffffffffffffff9c, &(0x7f0000000180)='./file1/file0\x00')
sysctl$kern(&(0x7f0000000080)={0x1, 0x40}, 0xc, 0x0, 0x0, 0x0, 0x0)
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
select(0x0, 0x0, 0x0, 0x0, &(0x7f0000001940))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
socket$unix(0x1, 0x2, 0x0)
r1 = syz_open_pts()
r2 = dup2(r1, r1)
close(r2)
syz_open_pts()
ioctl$TIOCCONS(r1, 0x80047466, &(0x7f0000000340)=0x8)
socket(0x11, 0x10000000000003, 0x0)
ioctl$TIOCSBRK(r2, 0x2000747b)
select(0x40, &(0x7f0000000040), 0x0, &(0x7f00000000c0)={0x1ff}, 0x0)
r0 = syz_open_pts()
syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "4e35cc8a89289bce574b8be0c645264019eb7c14", 0x0, 0x8})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000380)='./file0\x00', 0x58)
setuid(0xee01)
r0 = open(&(0x7f0000000340)='./file0/file0\x00', 0x301, 0x0)
r1 = semget$private(0x0, 0x8, 0x10286)
semop(r1, &(0x7f0000000080)=[{0x3, 0x26f4, 0x800}, {0x4, 0x400, 0x1000}, {0x0, 0x7f, 0x1800}, {0x1, 0x3, 0x1800}, {0x3, 0xc67, 0x1800}, {0xe0b227ee6f4a644c, 0x22f, 0x1000}, {0x0, 0x4, 0x1000}, {0x3, 0x6, 0x800}, {0x4, 0x4, 0x1800}], 0x9)
semctl$GETALL(r1, 0x0, 0x6, &(0x7f0000000180)=""/169)
semop(r1, &(0x7f0000000000)=[{0x1, 0xba3, 0x400}, {0x2, 0x8, 0x1800}, {0x4, 0x1, 0x3000}, {0x0, 0x0, 0x1800}, {0x2, 0x2, 0x1000}], 0x5)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240)={0x0, <r4=>0x0, <r5=>0x0}, &(0x7f0000000080)=0xc)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000140)=[0x7, 0x1, 0xaea4, 0x3ff, 0xfffd, 0x3])
chown(&(0x7f0000000080)='./file0\x00', 0x0, r5)
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f00000003c0), &(0x7f0000000400)=0xc)
r6 = getuid()
getsockopt$SO_PEERCRED(r2, 0xffff, 0x1022, &(0x7f0000000280)={0x0, <r7=>0x0}, 0xc)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000002c0)={{0x9, r7, r5, r4, r5, 0x29}, 0x2, 0x8, 0x4})
setuid(r6)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000040)={{0x20, 0xffffffffffffffff, r5, r6, 0xffffffffffffffff, 0x22, 0x400}, 0x4, 0x8, 0x93})
setuid(r6)
writev(r0, &(0x7f0000000200)=[{&(0x7f0000000100)="4e565db1b990925e", 0x8}], 0x100000000000027d)
sysctl$hw(&(0x7f0000000100)={0x6, 0x3}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x28}, {0x4}, {0x4000006, 0x0, 0x0, 0x400a71}]})
pwrite(r0, &(0x7f0000000100)="0000000000424374cfb5f0b099b9", 0xe, 0x0)
seteuid(0xffffffffffffffff)
lchown(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
ktrace(0x0, 0x1, 0x40001304, 0x0)
r0 = msgget$private(0x0, 0x1)
msgctl$IPC_SET(r0, 0x1, &(0x7f0000000040)={{0x2, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x18, 0x7}, 0x10000, 0x80000001, 0xffffffffffffffff, 0x0, 0x0, 0x8, 0x1, 0xfffffffffffffff7})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450444a, &(0x7f0000000240))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
readv(r0, &(0x7f00000002c0)=[{0x0}], 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050260000000000008", 0xb, 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1ee0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000300)=[{&(0x7f0000000040)=""/228, 0xe4}], 0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0))
execve(0x0, 0x0, 0x0)
r0 = socket(0x2, 0x3, 0x0)
sendmmsg(r0, &(0x7f0000002980)={&(0x7f00000005c0)={&(0x7f0000000200)=@un=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, 0x0}, 0x1f}, 0x10, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x56}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000180)={0x0, 0x7fffffff, 0x5fff, 0x0, "a5130c00000000000000000200"})
writev(r1, &(0x7f00000006c0)=[{&(0x7f0000000300)="74ff0e90383572ffbb6435fc5cfd59e877ad89b2767a1df78dac2bd34209", 0x1e}], 0x1)
shmget$private(0x0, 0x7000, 0x0, &(0x7f0000ff7000/0x7000)=nil)
r0 = socket(0x18, 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {}, {0x4406}]})
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000080)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="14000000290000002a"], 0x38}, 0x0)
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000300)='./file0\x00', 0x0, 0xfcfc96ac1f78739e, r2)
dup(r1)
mknod$loop(&(0x7f0000000200)='./file0\x00', 0x40, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x2, 0x0, r2)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, &(0x7f0000001280)={{0x5, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x63, 0x4a}, 0x0, 0x0, 0x8000})
semop(0x0, &(0x7f00000013c0)=[{0x4, 0x9}, {0x4, 0x7}, {0x4, 0x7}, {0x3, 0x795b, 0x800}, {0x2, 0xf49e, 0x800}, {0x3, 0x3f, 0x1000}, {0x4, 0x6, 0x1000}], 0x7)
semctl$GETVAL(0x0, 0x1, 0x5, &(0x7f0000000280)=""/4096)
openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x20, 0x0)
r3 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r3)
ktrace(&(0x7f0000000240)='./file0\x00', 0x0, 0x400, r3)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r5=>0x0}, &(0x7f00000001c0)=0x5)
setreuid(0x0, r5)
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000)=0x9)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000000, 0x0)
chmod(&(0x7f0000000140)='./file0\x00', 0x3f)
setuid(0xee01)
ktrace(&(0x7f0000000000)='./file0\x00', 0x2, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0084428, &(0x7f0000000080))
ioctl$VNDIOCSET(0xffffffffffffffff, 0xc0384600, &(0x7f0000000200)={0x0, 0x0, &(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0xff})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x3f, &(0x7f0000000300)})
seteuid(0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206913, &(0x7f0000000300))
r0 = syz_open_pts()
ioctl$TIOCDRAIN(r0, 0x2000745e)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x34}, 0x4, &(0x7f00000000c0), 0x0, 0x0, 0x0)
clock_gettime(0x0, 0xffffffffffffffff)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x100000003})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x7, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, &(0x7f0000000040)="42aa50e4", &(0x7f0000000080)=0x4, 0x0, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x50}, 0x4000000000000007, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x7}, {0x24}, {0x6, 0x0, 0x0, 0x12000000}]})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
minherit(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mlock(&(0x7f000000a000/0x3000)=nil, 0x3000)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r1, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}})
sysctl$net_inet_tcp(0x0, 0x0, &(0x7f0000000080)="0118ffac02000926feba", 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setrlimit(0x0, &(0x7f0000000000)={0x0, 0x100000000000000})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
socket(0x18, 0x3, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
syz_emit_ethernet(0xfffffd91, &(0x7f0000000140)={@empty, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "6b59ea", 0x0, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @multicast2}, @loopback, {[@dstopts={0x0, 0x0, '\x00', [@pad1, @generic={0x0, 0x0, "d220af5d7642f009cc6b49afa538df910b6adab5d0bd1ec44af34e4fa3396bb336385436da345c5954218f5bb2304e2dd1e5c115cd08396d0822d94080da9f8c2fda2cdaeddc4947aac35a313c375cfce9968b360d4f7f26f20b513d751f186048611088b4d45c0b8cef4d328a421154e1cf63e0f11ee7a794319341fea78c6eda27382ffcaf6f48986786051067c20e515d17155e35f46c3c9e967c27b090fe29a32fa554ae9571467d99d8be1eeac501fdcfc3dab65aae06decd115f181195750c1df68ae48b979442b5b2e94b379d9940"}, @pad1, @padn={0x1, 0x0, [0x0, 0x0]}]}, @hopopts={0x0, 0x0, '\x00', [@generic={0x0, 0x0, "a2c17891587762fc250e9c41d54d262f7a89542013250cb0d2ed"}]}, @routing={0x0, 0x0, 0x0, 0x0, 0x0, [@loopback, @remote={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="8eb6c79967bd37bd8aaf533a5d70ad87", @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, @rand_addr="91fb2522c03ef0965b2cb7c758989a55", @mcast1, @rand_addr="3fe6b0eee229909fd3bcf88e5c89cb51"]}, @routing={0x0, 0x0, 0x0, 0x0, 0x0, [@local={0xfe, 0x80, '\x00', 0x0}]}, @dstopts={0x0, 0x0, '\x00', [@padn={0x1, 0x0, [0x0, 0x0]}, @ra, @pad1, @generic={0x0, 0x0, "ce7646edf7cf"}, @ra]}, @routing={0x0, 0x0, 0x0, 0x0, 0x0, [@remote={0xfe, 0x80, '\x00', 0x0}, @empty, @ipv4={'\x00', '\xff\xff', @remote={0xac, 0x14, 0x0}}]}, @routing={0x0, 0x0, 0x0, 0x0, 0x0, [@ipv4={'\x00', '\xff\xff', @multicast1}, @rand_addr="cab2db26b1e529613ace74f6e65362ab", @mcast1, @ipv4={'\x00', '\xff\xff', @local={0xac, 0x14, 0x0}}, @mcast1, @loopback, @local={0xfe, 0x80, '\x00', 0x0}]}, @fragment, @hopopts={0x0, 0x0, '\x00', [@padn={0x1, 0x0, [0x0, 0x0, 0x0]}, @enc_lim, @pad1, @jumbo, @enc_lim, @enc_lim, @jumbo]}], @tcp={{0x2, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, {[@generic={0x0, 0x0, "af7171a40f39"}, @eol]}}, {"78682ac8bece37fc63bf831a6ed14c9297bd9fb60d5b1cd8aebb833d8b9646cc5920c11c975aca7d9658153718645152cdc2047a89d9f6fcd8b4375cbbff09e1221f30f2ee2e524690776e85bb0d7cdd440a697b125a6149abb5618e16e52c1cbb78a3a9261b9397e5bc70246c42a7bc57a229583c27"}}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
fcntl$lock(r2, 0x8, &(0x7f0000000340)={0x0, 0x0, 0x0, 0x1000100000000})
poll(&(0x7f0000000040)=[{r2, 0x58}], 0x1, 0x8)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffc], [], [], [0x40000000000000], [{0x0, 0x0, 0x0, 0x4}, {0x0, 0x0, 0x80}, {0x0, 0x0, 0x402}]}}})
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x54}, {0x1c}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
fcntl$lock(0xffffffffffffffff, 0x0, 0x0)
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
mprotect(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x2)
write(r1, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000140)=0x100)
msgctl$IPC_SET(0xffffffffffffffff, 0x1, 0x0)
sysctl$kern(&(0x7f0000000140)={0x1, 0x28}, 0x2, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
shutdown(r1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="10000000ffff000001"], 0x9}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x10}, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x801869a3, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000040)={0x1, 0x2a}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x1a, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffffd, {[0x4, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x3, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x0, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfffffffffffffffb, 0xa6f, 0x2, 0x80000, 0x5314, 0x5, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x3, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0x10, 0x0, 0x203}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x2004, {0xffffffffffffbdd1, 0x40}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0x3, 0x0, 0x0, 0x4], [0x0, 0x0, 0x0, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000140)=0x1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
r0 = syz_open_pts()
ioctl$TIOCSTART(r0, 0x2000746e)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x2c}, {0x44}, {0x4000006, 0x0, 0x0, 0x82}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x3}})
r0 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r0)
socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
r0 = open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
r1 = open(&(0x7f0000000640)='./bus\x00', 0x100, 0x0)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0}], 0x1, 0x0)
r3 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r4 = getpid()
fcntl$setown(r3, 0x6, r4)
fcntl$setown(r3, 0x6, r4)
r5 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000100), 0x880, 0x0)
pwritev(r5, &(0x7f00000005c0)=[{&(0x7f0000000140)="19787a18a25d7cc03c061042d077a3cea9d4bd8faa874e498cfa2144694d0805d6d757dac45f4f08", 0x28}, {&(0x7f0000000280)="685c15b0b30d09b6f87938813b73648856bc939ab50b1a3ea7a71cbcd4104ce20f61277f88328ee4cfce8ab883feba92ea96c204e2d2a4e1b550642a95bfb732dbea1ecd8a114b37a1cf77920ace6f1ea759f1bcba52b32f5481b7a35b32842fa61a677c8bf2b16a4f139711d6a6266672b694b079ad00ec726097df12f9dfb16996ea825878314aff8b12488fef6f370a", 0x91}, {&(0x7f0000000180)="dccf6db178c9eb22de1262a7dd57fcd9275c50508e9a3d96bfa7ee2695d8b6d8220ae12da43714443f980fb8c50b3019228c635ddc5c1a9ac37d87a962d010da6d657b345b1d329e60d7928c82f7d6572202a8919ecbd864c1d708c376443e180de51bc336ad91157a208e", 0x6b}, {&(0x7f0000000340)="562496f786296c31fe2ef3e09f18fa1e9551937fbd0b32395e815c9ed36a7426ab1c2d55deb849e44fcdd6ef0936386f58fb9615c41f0d48fdbb288e1f01473278b7da6bdc04e1be2f369d4dd05bed84f6ddcadf84526fda27bdaeaf09f042b875f808d133fd4645700e48533e09a6666a4e356bc9618912aec0697de42e3a1a68c560d061d2afa60fd5a69962a58408ff9ae4743b49d0bfc4c9524f80d9e508ea89ddbf674f68992cfbc22f2cd261", 0xaf}, {&(0x7f0000000400)="4812343247197905ccf5376e6999ec99a878fe6afc8814178ba859c30882c87627539422f7cca33c1f07fd99a144e3234e0f95dc2e5a78b02b0636763b556edd51d137c7c55ec4e11c7eb03b51b87ac363376a229d4d925dc25545c604706163ed886ca76533e72300ba62de0886eac4325f57e8ccd6125fabc427ee58deb8d436a6d0f65ba7610f09ddff4b07af1227a46ad254ca9596474822b0646a953a7d8db1c3", 0xa3}, {&(0x7f00000004c0)="0d38a949df38b70234aaebd3967dcba85b6230123860acd6522b03220fdaa56202441e95354d6bcee5f280c450992f13ca32bf230fcec5fd1f4b9805b2162213", 0x40}, {&(0x7f0000000500)="5dcc3d90a3d589ac2facdd184752ce97c16e2833d7c5a5db91eebbd1203d872e475fd3ecc83b3495fa7c8345de2a50abf25caa25fe496547a96571cb946726116a5ac359f30420ee589e93ae0a14332c8f3f085aa0afbff3bbc10ea51a7c56c28eae14cd382f963aa122c56203f584890ba4451308909dce45640f5ab291d1f6d20e0ad2358984ef51e549047fcdaaa18863c4ae", 0x94}], 0x7, 0x0)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
dup2(r1, r2)
close(r3)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x14, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x18}, 0x4, &(0x7f0000000100), 0x0, &(0x7f0000000240), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd604406, &(0x7f0000000240))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0xa, 0x8, 0xfffffffffffffffd, {[0x3, 0x108000, 0x0, 0x0, 0x800, 0x0, 0x1, 0x3, 0x2, 0x0, 0xa1, 0x1, 0x5, 0x100000003, 0xffffffffffffff65, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xffffffffffffffff, 0x2fcd, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xffffffffffffffff, 0x2], [0x1000000000009, 0x6, 0x9, 0xfffffffffffffffc, 0x8000000000000000, 0xffffffff], [0xffffffffdfffffff, 0x40000000000, 0x0, 0x7, 0xfffffffffffffffc, 0x3], [{0x21f2, 0x9, 0x0, 0x20}, {0x3, 0x4d4, 0x5, 0x5}, {0x0, 0x100, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x22, 0x100001}, {0x0, 0x0, 0xffffffff, 0x7}, {0xffdf, 0x10000, 0xe3}, {0xfffd, 0x5, 0xfffffffe, 0x8}, {0x0, 0x8, 0x0, 0x400}], {0x2, 0x0, 0x3}, {0x7313, 0x5, 0x6000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffff9c, 0x80185760, &(0x7f0000000200)={0xd400, 0x9, {0x5, 0x7}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
fcntl$dupfd(r0, 0x0, 0xffffffffffffffff)
ioctl$WSKBDIO_GETMODE(r1, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r1, 0x8020699f, &(0x7f0000000300))
socket(0x2, 0x8000, 0x80)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28000000000, 0x0, 0x0, 0x0, 0xf1], [0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4000020000210, 0x0, 0x0, 0x6], [0x8000000100000001], [0x0, 0x0, 0x0, 0x0, 0xfffffffffffffff7], [{0x7f, 0x2000000, 0x0, 0x400002}, {0x0, 0x40003, 0x0, 0x10000}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1, 0x0, 0x10200000000000}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0x4}, {}, {0x0, 0x0, 0xffffffff}], {0x0, 0x4, 0x0, 0x7}}})
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r1, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(0xffffffffffffffff, 0x8004667c, &(0x7f0000000080)=0x90)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
r4 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r4, 0x80206979, &(0x7f0000000300))
r0 = syz_open_pts()
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000040)={0x0, 0xea7})
openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
syz_emit_ethernet(0x56, &(0x7f0000000280)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "1d712a", 0x20, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@routing={0x0, 0x2, 0x0, 0x0, 0x0, [@mcast1]}], @udp={{0x0, 0x1, 0x8}}}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_linger(r2, 0xffff, 0x80, &(0x7f0000000040)={0x5, 0x8}, 0x8)
close(r2)
mknod(&(0x7f0000001140)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000340)='./file0\x00', 0x0, 0x0)
ioctl$TIOCMBIC(r0, 0x8004746b, &(0x7f0000000380))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r1}, 0xc)
setreuid(0xee00, 0x0)
r2 = getuid()
r3 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
fchown(r3, 0x0, r1)
setreuid(0xee00, r2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0xc}, {0x35}, {0x6, 0x0, 0x0, 0x100004}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
close(r0)
pwritev(r1, &(0x7f0000002340)=[{&(0x7f0000000180)="e5c57b", 0x3}], 0x1, 0x0)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
r2 = socket(0x2, 0x2, 0x0)
connect$unix(r2, &(0x7f00000001c0)=ANY=[@ANYBLOB="82029797e3"], 0x10)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect(r0, &(0x7f0000000000)=@un=@file={0x0, './file0/file0\x00'}, 0x10)
syz_extract_tcp_res(&(0x7f0000000000)={0x41424344, <r0=>0x41424344}, 0x2, 0x1)
syz_emit_ethernet(0xc6, &(0x7f0000000040)={@random="831ee5407df0", @broadcast, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0xb8, 0x65, 0x1, 0x0, 0x93c4ed541a00b09, 0x0, @empty, @multicast1, {[@rr={0x7, 0xb, 0x31, [@rand_addr=0xffffffff, @remote={0xac, 0x14, 0x0}]}]}}, @tcp={{0x1, 0x3, 0x41424344, r0, 0x0, 0x0, 0xc, 0x0, 0x3, 0x0, 0x101, {[@sack_perm={0x4, 0x2}, @window={0x3, 0x3, 0xc4}, @eol, @generic={0x3, 0x2}, @sack={0x5, 0xe, [0x2bb, 0x6d, 0x4]}, @mss={0x2, 0x4, 0x100}, @generic={0x8, 0x2}]}}, {"2bada6db3781dabff96a1e812670acb66b9dcc197a01282fa3bd0ecb48e5eda9ba523db058360adf1dfb6267a8bac08567d73d04831315a49dc4c6e8bb01d990cd68631a7c7e057113f0db9dbe1b758ba64684f35d760618143547fc284f212e3596acc508e87ae2"}}}}}})
r1 = msgget(0x3, 0x20a)
dup2(0xffffffffffffff9c, 0xffffffffffffff9c)
ioctl$WSDISPLAYIO_GETSCREENTYPE(0xffffffffffffffff, 0xc028575d, &(0x7f0000000080)={0x0, 0x0, './file0\x00'})
ioctl$WSDISPLAYIO_GETEMULTYPE(0xffffffffffffffff, 0xc014575e, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f00000016c0)={0x0}, 0x10, 0x0, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001740), 0x8000, 0x0)
openat(0xffffffffffffffff, 0x0, 0x0, 0x0)
ioctl$WSDISPLAYIO_LDFONT(0xffffffffffffffff, 0x8058574d, 0x0)
msgsnd(r1, &(0x7f0000000140)={0x2, "9aca21ec06a919c8810a2ad66632cd8c5f4566a838eb8c5f557668bf9631133e8d419d29032aa0749c21ae4293f859a72dbcb823cc421b8fde6d543ee1811c74d213feca47397b7954c74c9a70264f1f226062ad367c560cb6d16ed5c3f756f5dd6f611cdc1f3b731ebae511715dc3142f0de1fae78fd71c54"}, 0x81, 0xe74fc6a4b81415bf)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0x100000000c8, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x8, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x8, 0x200000095, 0x4, 0x80000000], [0xffffffffffffdfff, 0x0, 0x6, 0x2, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002004, 0x0, 0x8, 0x46c, 0x6], [{0x4, 0x0, 0x8, 0x3}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0x2, 0xd8, 0x2, 0x1000007}, {0x7fff, 0xffffff7e, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0xf9, 0xe59, 0x2010000000}], {0x6, 0x2000002, 0x0, 0x8001}, {0x0, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecf860080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
semctl$SETVAL(0x0, 0x0, 0x8, 0xffffffffffffffff)
open(&(0x7f0000000100)='./file0\x00', 0x200, 0x0)
r0 = getpgid(0x0)
ktrace(0x0, 0x82927a4da63b35ad, 0x810, r0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x11}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file3\x00', 0x70e, 0x0)
r1 = open(&(0x7f0000000300)='./file3\x00', 0x0, 0x0)
mmap(&(0x7f0000001000/0x10000)=nil, 0x10000, 0x0, 0x2812, r1, 0x0)
write(0xffffffffffffffff, 0x0, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = dup2(r2, r1)
chmod(&(0x7f0000000340)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r4 = getuid()
chown(&(0x7f0000000000)='./file0\x00', r4, 0x0)
seteuid(r4)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000001680), 0x0, 0x0)
mmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x400000000094})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0xf578, 0x0, 0x2948, 0xffffffab, "9190000100120000000092ff0000ebffffff00"})
r2 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
dup2(r0, r2)
writev(r2, &(0x7f0000000040)=[{&(0x7f0000000200)="e5dc92", 0x3}], 0x1)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000001040)={0x0, 0x0, 0x0, 0x100000000})
sysctl$hw(&(0x7f00000000c0)={0x6, 0x5}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000200)={0x3, &(0x7f0000000000)=[{0x4}, {0x64}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@remote, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "4fa661", 0x8, 0x0, 0x0, @mcast2, @local={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x1, 0x0, 0x8}}}}}}})
symlink(&(0x7f0000000000)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
readlink(&(0x7f0000000100)='./file0\x00', &(0x7f0000000180)=""/4096, 0x1000)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000002c0)={0x3, &(0x7f0000000000)=[{0xc}, {0x3c}, {0x4506}]})
syz_emit_ethernet(0x2a, &(0x7f0000000280)={@broadcast, @random="436753bc6c4f", [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @broadcast, @empty, @loopback}}}})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={0x0, 0x781c, 0x0})
r0 = semget$private(0x0, 0x0, 0x42)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x1, 0x3, 0x400])
setrlimit(0x0, &(0x7f0000000040)={0x0, 0x1})
sysctl$hw(&(0x7f0000000040)={0x6, 0xb}, 0x5, &(0x7f0000000080)="48913ef4a6ef7b133d444a10d7e5e2a7d8795069ee3b76d1a20d70e17cafb6dab57968a384127f92b68849f920766e362fb426aa8cecdb1bf321c941977b2561d98c1ab33a2e1e43d0d55580e99eb826c59ad2e59923b021217a1a9cce970a60026f60562c3f4226180b0bc4db48a398215ca2cd117d4aedb5c8a546d2d0b255", &(0x7f0000000100)=0x80, &(0x7f0000000140)="937dc2678c265a", 0xfffffffffffffd92)
setrlimit(0x8, &(0x7f00000001c0)={0x1ff, 0x3})
r1 = semget$private(0x0, 0x7, 0x3e0)
semop(r1, &(0x7f0000000080)=[{0x3, 0x2, 0x800}, {0x1, 0xff, 0xe17b725e3710487a}, {0x1, 0x8, 0x800}, {0x3, 0xffff, 0x1000}, {0x2, 0x8, 0x1800}, {0x3, 0x1, 0x1800}], 0x6)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x191}, 0x20, 0x9, 0x7fff})
semctl$GETVAL(r0, 0x2, 0x5, &(0x7f0000000200)=""/123)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
syz_open_pts()
r2 = semget$private(0x0, 0x2, 0x5)
semop(r2, &(0x7f0000000280)=[{0x3, 0x8, 0x1800}, {0x3, 0x81, 0x1000}, {0x4, 0x6, 0x1800}, {0x3, 0x6, 0x800}, {0x0, 0x4, 0x1000}, {0x4, 0x6, 0x800}, {0x2, 0x5, 0x1000}, {0x2, 0x4, 0x1800}, {0x3, 0x6, 0x800}, {0x2, 0x9, 0x800}], 0xa)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000080)=[0x95a, 0x0, 0x1ff])
semop(r1, &(0x7f0000000000)=[{0x3, 0x80f0, 0x1800}, {0x0, 0x7bb, 0x800}], 0x2)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x1, &(0x7f0000000100), 0x10016)
pipe(&(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
fcntl$lock(r0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x3, 0x0)
r3 = socket(0x2, 0x2, 0x0)
r4 = dup2(r0, r3)
r5 = fcntl$dupfd(r2, 0x0, r4)
ioctl$TIOCFLUSH(r5, 0x8020699f, &(0x7f0000000300))
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000001c0)={0x0})
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000480)={&(0x7f0000000000)=@abs={0xa3e18c30ac20269, 0x0, 0x3}, 0x10, 0x0}, 0x0)
getgroups(0x7, &(0x7f0000000000)=[0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, <r0=>0xffffffffffffffff, 0x0])
setgroups(0x0, 0x0)
setregid(0x0, r0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
seteuid(0xffffffffffffffff)
unveil(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000040)='c\x00')
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x920, r0)
ioctl$TIOCCONS(0xffffffffffffffff, 0x80047462, &(0x7f0000000000)=0x406)
ktrace(&(0x7f00000003c0)='./file0\x00', 0x1, 0x410, r0)
ioctl$TIOCGETA(0xffffffffffffffff, 0x402c7413, &(0x7f0000000040))
ioctl$TIOCSBRK(0xffffffffffffffff, 0x2000747b)
r1 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r2 = dup2(r1, r1)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x40, 0x0)
readv(r3, &(0x7f0000000380)=[{&(0x7f0000000140)=""/225, 0xe1}, {&(0x7f0000000240)=""/178, 0xb2}, {&(0x7f0000000300)=""/84, 0x54}], 0x3)
writev(r2, &(0x7f0000000100), 0x1000000000000161)
sysctl$net_inet6_icmp6(&(0x7f0000000000)={0x4, 0x18, 0x3a, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x2, 0x0, 0x0, 0x1}, {0xc}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x800, 0x0)
pwrite(r0, &(0x7f0000000280)="149674f7f4923fe3ae3908ca8487043db64a9248d10c69d9b44cbc8e67c0e4fb019b6c1a42a64f391abe5f1dccf438297dfbd23489c52da94fe69db3ffb4f50228858821474aa8ad493aaa023c47bf0c04aef798082f170f932ee0a49fc0a8b6aff031581238674db573ea24f97eeef491b55ae356f934ee42f6db0e15b1174da91523285194388d4d71c320fd0a17bd5722f97cce1c925fa8a24166c865ba7003edf9224a3daadc53b80de5a3e93a972ef353d70954839c4642e3689c8c85d07175fa77db30629b12e74ebfa22820d667cb172272016e819428e12dca483802e38081170e8bbe596c07ac", 0xeb, 0x9c)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000200), 0x800, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0xa, &(0x7f0000000100)=[{0x0, 0x8, 0x2, 0x9}, {0x3f, 0x1, 0x3, 0x1}, {0x6, 0x8, 0x6, 0x7ff}, {0x3, 0x5, 0x1c, 0x3}, {0xa2a, 0xfc, 0x1f, 0xa470}, {0x4f, 0x1f, 0xfa, 0x7fffffff}, {0xd9, 0x81, 0x0, 0x5}, {0x3ff, 0x81, 0x20, 0x7fff}, {0x8, 0x8, 0x20}, {0x0, 0xfd, 0x3, 0x7}]})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f00000001c0)="0002f350d37c459447eac32c93b5433b483569d785cd989179e6019bb60e1f4e4a363ba6822644ceee0a236c870200a347b4f3ae02797b00000000", 0x3b}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30", 0x8e}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mkdir(&(0x7f0000000000)='./file1\x00', 0x0)
mkdir(&(0x7f0000000200)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chroot(&(0x7f0000000140)='./file1\x00')
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000180)='x\x00')
unveil(&(0x7f00000003c0)='./file0/file0\x00', &(0x7f0000000100)='x\x00')
unveil(&(0x7f0000000080)='./file1\x00', &(0x7f0000000380)='r\x00')
pledge(0x0, &(0x7f0000000040)='vmm \xf4\x90\byEY\xedSyT\xcb\xad\x11\xba\xf4:\xd0\x17\x96\bp\xc6*\xe9\xe2\x91h{\xf9\x97m\x10\x9d8,] \xe3>\xb5!\xdf\xce^\xa9\xa5\xd3\xd4#\xa7v\xac\xc3\xbe+\xfd8\x01G\xe7\xe3\xf0\x05\x03\xae\\\x16e$I\xe0\x97I\xc4\x13\x11w\xa5\x81\xdf\x13\xc0d\n\xb8\xe6L\xf7\\\xe3\xc6\x06\xb4a\xfc\xc3\x01\x1c9\\\xc1J\x98}\xcb\xaf\xc9w\x80>fZ[\x8b\x14q\x10<)\xc6\xb1\x16E3\xc1\x90\xa8\xfee\x81\x1a\x18^\xb2\\\x99\xe6\xee:W\n9\n\x951\xd2x\t\b}+\xf2\xce\xccM\x18\x7f\xba\xf0sT\xa7\x11\xb5\x8f\xa5\xaf(E\xe7\xcf\xa5\x9dk\xc5\x9e\x13\xfa W\x89?=a1\xe9G\x0e\x95\xd9\xc7&\r\xe0\rd\x8d\xbf\x9a\xb8\rev\xbc\x992\x1b\x1b\x18\xc5!\x8a%z\xdd\xfa<\xf3\xd4!WYiL\x90Q\x83\xdf\xa2%\x19\xd1y\xfbt\x00\xa8\xd88\xf6\xa9w\xb0\xb9\t\xde\xae%K\xd4\x1eY\xd2\xdc\xdconwHo?\x1aY\x18\xa9K\xcb\xba\x945\x7f9\aw\xc8\xb8\xfc&\xde\xcc\x12w\xe7\xa5;\x80\x00\xcf\x17\xfc%p\xc6\b;@\x7f\xa38\xf3 @\v\xe3O\xaf\x1a\x069\x18\x88\x02r?Y\xbb\x03\xd8\x9e\xbeE\xa4\xa5\xaa')
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
setuid(0xee01)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
write(r0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f00000004c0)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/205, 0xcd}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
close(r0)
sendmsg$unix(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0xb1}, {0x7}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f00000001c0)={@local, @random})
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, &(0x7f0000001180)="df18a4424766509f4f9667ea759e04033d3b6cab50d0a4d947dbb4d44913fbd8c66ff35fd50cd33365462aa40df50e88510376256ffba33867a3cb8347acefa33748bf12d4ed738a1a19eb68fd77e792fdea51ffac08bca920f38ddfc2a559c6cc654b29210997484448b5e2e8e355f8548a4875785e78ef8fb478dfeee838273d48fd673ee716fd3b2c1c92a139ceab6482fcdcda0c6e4622b5c6f7be6a7c43033d63e3d863668527c0beec5a1adda892f7ab41da94bccda47c02f361203e0f01fef20f7617dea1d46747bbee14f5fed1756834ca6aae32f8919edefbce80642f658f00edcf6a51b237d97c60baf97d2849cb5587e936892fc20dc52cc95453378a15497a19bed96512aa1fee69d2a9e595a4f43d2bb512f10393c3758617a65d66297f1dcc158e0f2e93edeff04d64cfbb6caba6a44e5dcdf670628927975df07c2e851a3110b8758d2a2f244767b793da0715e6609e5055eb80ac850aa8d73aff207f8aee496680f27c1ffa59feddaaa00128dbc6aabf73fc98d24e05fb0afcb23c59738b925fcb065cb6aa5e7fdfec7e67279b3425f863979db03ded35f89c9aaee37a46971533e3c75752fec8d4a81c1c36a2e16c5b4e53459a5438737022f618d6eb9891e2555ac877ef1e2ca2db035f60be40fedaeb6f00920e138d0167a5887e0f3f273588ac335a28ba7d4d743a543377dc427539ccf4dad6b2967291adc0b3d28fbdc115e28bdcad856471", 0x210)
pwritev(0xffffffffffffffff, 0xfffffffffffffffe, 0x3, 0x0)
sysctl$net_inet_icmp(&(0x7f0000001240)={0x7, 0x2, 0x2}, 0x3, 0x0, 0x0, 0x0, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getppid()
fcntl$setown(r0, 0x6, r2)
write(r1, &(0x7f0000335000), 0xfcb7)
fcntl$setstatus(r0, 0x4, 0x40)
writev(r1, &(0x7f0000001380)=[{&(0x7f0000000040)="b79c75797734235fd849fd8f1bef9a8aeeeb95eab08598c2a2b3608a37b1e72a201060b04aa8f950a6d812005c5c3c78fc9eebb253667b045e2918990ee8010a6d0e195bf2f0f89227f4b47266febc685deaf2b22cec68585fd9ba563c93777d52eb6df23540f42cc44b9693e24a90521219bfe337cd6fb55d1d089b1f18bbb7ae535d57837e287524f8bf70efdaf833bca8ad5a5bfffc69e57f95c40b5aef0a19359fcc4e33b34d9024dac9736f6100ed48e75f91691805220c33284599904d097d", 0xc2}, {&(0x7f0000000140)="d410a0754c950130bbaaf4f25552827c3b3f958c0b1956e1bb3bae5c0666cc", 0x1f}, {&(0x7f0000000180)="c8f4c597faa17dc0cd31322ede2b10c2eb72fafc513eb25854c5a6c2add51602cc05626a54dda827b113ab2fe86e8a4eb9400b48f2f8a38f2d3bb0b5f28c91ff95a32b1c3f77f5628a7c2ab74c9b92bf3ce54c443643486d515ad0df78433acee4694034e7cbb4a792fc38701d858945611ce67fe1bf28c11d4643e0e4282d7debfec4e5bad2f0da9be9f2cbe11a14beb3daaa9b33223e8c76a1f06a36c059eecb5c68e59a4d45b268ad528702447018c03c8cbc2d4414c486018f8fa11e28c2743d35b91ae2b6e6bb3fe85a650b132ca8d7fdc4fb3e279978d602d81764fd7f6da40234504866737072a4bc312c50d669b53b4ed847a00965f4150ecd0e0e284aa1bf619fec83cf5a9ea49a5a6aab89828f7ef2bcbc5fcdbf5630946ecaa35caced2f00f89d61a970df49e6c2055da684687230ed0eff0853f64a624ba6a1190520000aa9ac2a52fe75ba6314c4faa5796c904f46c389cf745f80b2c49f53ed11f7ebf164f6c3a23826ea3f4e37fee21441b0b62a2483765f2f72b1e751a9e91eab19769bd159b3af08dc2d9169c0f01f9e5f4e1c6bb91afbab49a216891365658cecabf88f5eb789148da3bd9c3c5d4cf57401f3824f4f2077c6ad79f23d0240b5f1ff232c017c2271f341bb8d8680224ef88bf5ad5a3438a6addb8fa6003ff0f36e90a645ce79ac9195e50fe4d4b3dd8bad9d5468041d21c3cffc40af55c5fa6175ab3db240d4c60da2706acc0b780dc936836cf9dc8de9903e70e88d1df91aa66a47652c1e60bb083d9025d7e3a87eb8215745922dafb2eed373059615fe1bd1fe7361a8b9f3faa849ccdf87e537a5103a57ec65e8d8af05dc86f4bb97c29a3f038af1c4845700", 0x269}], 0x3)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x5})
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x4, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
r0 = kqueue()
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
kevent(r0, &(0x7f0000000040)=[{{r2}, 0xffffffffffffffff, 0xa9, 0x1, 0x800}], 0x8000, 0x0, 0x0, 0x0)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x20, &(0x7f0000000080)={@random="93c40982e062", @broadcast, [], {@generic={0x88e7, "da3f48a423b1bb3062c6a20cb329c3620a47"}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x50}, {0x60}, {0x6, 0x0, 0x0, 0xffff5b89}]})
write(r0, &(0x7f00000002c0)="03e09a90b3911b4299937474390d", 0xe)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
poll(&(0x7f0000000000)=[{r1, 0x1}], 0x1, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000200)={&(0x7f0000000100)='.\x00', r1})
poll(&(0x7f0000000080)=[{r0, 0x4}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x5c}, {0x61}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x2c}, {0x86}]})
syz_emit_ethernet(0x2a, &(0x7f0000000340)=ANY=[])
syz_emit_ethernet(0x1fbf, &(0x7f0000001900)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "6cf65b", 0x1f89, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast1, {[@routing={0x0, 0x4, 0x0, 0x0, 0x0, [@rand_addr="ba0e53991735db641eba7d2415d79911", @remote={0xfe, 0x80, '\x00', 0x0}]}, @routing={0x0, 0x4, 0x0, 0x0, 0x0, [@local={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}]}, @routing={0x0, 0xa, 0x0, 0x0, 0x0, [@loopback, @empty, @mcast2, @empty, @remote={0xfe, 0x80, '\x00', 0x0}]}, @dstopts={0x0, 0x20, '\x00', [@generic={0x0, 0xff, "f3110cec04d988279271bb68891b742f96414d0be57a9f4310fc139c3fa6c76a0524e06f9e5809ddc53040cc5505560ef76ea951430364c979be34bfd43b9af1a245fa976ff8bb2bf6d05fa8c81f56d79ea17a4f2c09064cc077743b38dd665719b17cba3984c6ae1b5375b004757d7781cd8d709990331fe2c1cd406fbed7ed37d5c3e0f1a942d2ba2742ae094cfb7c2599f922563045644828407f1d6f0860731ba9fd3347d5b057d8dd96e37271b7e058567097ca7c8a834a9688ebefb636a27ea267be80a7565242beda4d2d6d386f287658363c3e4744ef9179167de294f634387cbeaee51666a86c37939d598fe826a306397862dc2dc189ce6bd2d1"}]}, @dstopts={0x0, 0x2, '\x00', [@generic={0x0, 0xf, "81596f6eada29355ff7003e1cc3da4"}]}, @fragment, @hopopts={0x0, 0x1b3, '\x00', [@jumbo, @generic={0x0, 0xd91, "d5b1b9c7ebbaca393b5a5e9bb038d2c5ec0eb8fd41364c094065dd692a6066a4d5a08960125b7144485980093d6cc20bbaa7499afea0329d44f0fe66a6241b2875861750aca6d91327acbfc348cf862beacbfb1c37d2be76c71cf4786df62ab7eddff73d991888742f06dd2aa76bdf1d9856d0e5cce35c0738b044d68702d4de8d10c5bf58c39dedbf72b3718b3979fdc634890216448491498bd77c75ef3f244289745e9d06834e83f27184a1a6f7c5bce03a1af7b5875003b6b1cee1bcfa0a56e0ec830eaa260c97079b19c461e4f693f81bee21bcfc2a35b2ecb9cf251ce53b0709cdf38296f4cef0fd95834799d0a88202d31bd0d2c6477b593512366bdedab16421a6553bb789911a42e260ea607a896543164f7ce3c2ffaaf7a309a0522e20d57cc2343d2cd4156cde66696969b2f1abc40ca2363ccaee661dd59991d5e33e876d5e614f11f8d52fe070037d78b5a1fbebcc4e1b47a25f0633f99e29d82743d62efc33a4460a628ffcb2168f0a42bd7677bbdb8ac1df66963fa3a3d093ddfc913680cad58e05d0bfce0c54d32149608685aba327e61600f2d1e1949aec197031bd4aecf4db6a2622baeee7a61f7c98fa0c291a900698197a5d7358dc99435e48f99a9f0bda92c290fffb4ef46a33cd3822bf4cf16afc2881b196a6f6db2acb74d99c07d263d2a2e9805729b5040cde0e4245555ec4b558bf6968542bb9c0f02db362d3c2850da0a06db66ba5da8c7c44ba2016581d84c6350b8a430df1527cba3d418c05591dabac5df366bb8c8b7a0fe1ff3006939d10debc76f5e738842d210e2af2c1e7376f24c389c161354d000b5481847a24c3b19c1a9b3b3cb9d2143dab239f1307565b5c486946a76a3d05ac018f63d879e693c3a257245bd864c3c6b4e47de64469280094e0d6411584db63375b9cf24a151d206c75e567fbcd0923cf5432cf12b1111c29a39f0b61090ee99eb375a3e2ba6163bcd3775cd6255ac3a2770937295c69fe4741d060154a8735d0fb6f7d98eb5782e956f7ff5bce614897e55d3ab1dcaf9a0deb0fa3bc10b4ce8b1e20981cefaa1f5f6f964df88e813dac49c8a4e1b627dd610f5f1ae799fa0b2568e923c63bbf6e8129b175fcba780053644623f0241fe0f96d6e39a0e96e6bdbfa3db73a0b09657b83104cf694944702639624d5839ba6610410c68fbb87003cdffdb9096391a536f9f59f3ce7b221b1a9bc3b03b6ddf47e7a859cb742375753fb9f83d607015200510a98d68b12826e35cd90ad8f5372290bc08d0ec91087d3561abab032cf45d62e71ab25530c49c299aaaeeb1f44fd916ff35d1d9cc9f4f01e6f797271b84516c8cd7733a4df3ce6d258bbae45495e16d362819e949cd17e7d02ca168ff3a8a2eb12643d11dbbeb59ad1fe8368aa268e3d2a1948bb55e6f33a0d4351b1ce817ba285fcbc2c4171187712fc034211640d4616b6ca90898e66c6dacf3cfb318550b9ebf95d46e1f3a6c473c4b689ccbe69d01d1ba9c9f39bc09f56b01f19a6d0769f634a646c5e4707e3cda089ddf58abda4381458d5a2d78d58c25ada2e42050eb62fc98680e93a6210a282c6a904fe5ba29fcf2cb82b6d42c702d9c0caebe97ec28877ee34fc7599e000f8a081b35e46660c9623509bf4819f3b602ebe6e10e91ef2f9123beb1eb2c51702f2205793cf9790a7f9255f60ca4613cf719cbb5f99ccacac2f32d97518903277cfcdce00af5c7038b53851e0cc49abd2a24208ebb53a2e3ae5625a50b5fc6346343e908889792c27e819741319518c1b13a089179c74a525e751df243a25091ff463cef8251bade605ca16e0258d1bf4b664a6ce3784a2eddc24542b0570fe032ba5fa78c621190143e57bb1ef15420caf0e0bb51561cf79e90a128a811dda088dcfb4b4bb8642205a183be965287ce98f33fc54f953f230a3da134e2156fca311010ba4146ed01763818b0ea34c3369be861786280d189b8aa4b97ed289ebf2fedfaae456edfb29605c9fd86fd6973b04aae72fe156b3032ab8989e5718c411e4ca64d014f9e482b5993b28feaf7e206583c58d8dc436cc8eac47100a868217854f913ada1f6864ef678a2d70b813087ed120cf51b9f546c3c8bbc01599da5862f6b432f676c73c04f5e92ea7d0b61479038466b6fa24527bf46236adc4ff773b589e1a878fd2c2c1b046cf43aa9f7b91ada8e4c0f393e08ae507fcb37e49c93affc3bca29a06418178f6d3a8073b14931e4e9d63d98557121fa3314cc013c57459359af23f7a18343ff005c58e612cba5c512f564183ff4d9c8a536178247f2ca1980a9b100abcc12d687a15fb25399f42875b5d00672e5d457531d31710e9f28b4481564528c1657e3f791e29390a0ba4b02183ba385f96bea4a7bb3c772119fa34a4d31a6b0bd8cd9025f61a729e3f27cf738644c26b029ed49e38a03245ca698221302c946285ba1989d80c6042e1713910fc63965798ebdc230d71b1b489322f66365e5018975ebd3c5f51f90916d0069d50e970d978ef149bf3bfd2a934d09753fd46a21d77da552d7cc1947114f7b837e7333c01f59b255f5f75226f4960ad5841302b4919dcc5d3465e50ec0b42eb78f6d1d5eca3dcc9abcf0c4e00000000d71690016e718732bd83d8dc2765fd71d10e9764626a13642b2bcfae568f8955764b032789ca637dafe17c6ddae4d270d08f3efde6e016586eea13fe51df99bb4e7d7701aa13d9f764471523bff7fe4159e226be12cb7709c197f932c7dabf596ca8c6da9378d1a80acf37250655164e2ef8c54b0fe6ec1cb17cf3bd83a04acbe4e2575b26ec8dad77eb6c09f1cbe1efaa2e48c81e073e39ec12c3ff5f218b558ffdf88cf7b437c34cf9a5adb55f716111bde9617bd12ffd15552462ddd37ba01b96356b2573b417124b72e0382cfa5e2418331230c9bfeb60b4fed819c1a63292574f972b697fc89617727da140692fff123677ef12d49cda6c20b6b2e2c074e2efbe0ddae28d0a707c8620c772d8895dd23ae27d62d3bde081835018800417f861e05a2613b0e3a7d6cb4b4003386d1669dfe3b26ed25f302d2a46a7daa78f126b957f7e5ed7e90176252891702d361f2702ce14cab70a43255bda97192d9833995d3ef351bc7cce50f825ed232befafad2bc6272f02250a83f4b652a8ee20ea3e02820c3c4e6631aca22c5dc627a628e44a85b96b61bf808ffb56fc7c106246ef570a2d9c47b858154cce0b04d1acd94a355722b658df7f8970c7c6af9315352b29f53a486155f32fc7c2a104a2ef265f59a06dc3475250b37db8aaa2ae1c8e51ac9697ebe349c46c1440b98afab62ef5eda7301746a2d608c92707d370d0655d634f0e4ecf10fc2b662acd82e95ec5cb5f9886c9f628fe2f4e2f55980a2b86a375585a58b8013aa115f4650f393cfe2b9ee250c23810421cf93a94a1b090ae469528fc8e34a5a58f7e1752eac51f86e34181235b2782b90dc4947bb417054db028703f9bfc748d1c028240958afea7cbfdbfa806b5e8a89eff4eb47679a13bb2d4b5908bacb20687f0cf9a2419bfaf283ce8632295bc5044aaf30bf68275239fe6f5999262c60696b3bb4efabd6a456cae69d6a4a50850514814a8ca5d3d559f24c8cff45e63afb6af771cdcfec20dfb7639db266512d2e6ed4b63ebcf87d17ebb7f8cfb70bf3ebcbd6f848b91584bd719cb9ed624cf5c876c736245e08ab32682e45b68da929c7cb57f867834818d4fbede203e4be465208d44fd492204f4fa37840b3ae10974710f865c4ff62e44bab6a6211a4318b5387a2fb60b41cc934ec3c94bcf8acf27b59a9a7bfdd0b0a2e5be853ad02a0e12c64630fa135fa70f44f11b63b06598fe3f2e8c07533e74d88793a5d31358ddf5106d1a0344ac8260882624d259e95cbf73e93f52ece38f0b1a96862462eadde031cd98aa41b42b2a45a8bb473a0c9632766d3c63392da29d266efabf509da5eb61714bd47b93f103a2aba2ba9221745948773538d3ba0ceb81654cf8d21969e6a36d01200bfde901107d83752a8d96be74134a128a92f86b1c0474eaeebaf14f3912baeb95eed848a2e14767e58e7ce5a956777734f2b9d4e97ea59ab447591c0d437b2f1a8e69adb744f92782943f132de22f48a03949644e9ba547528d31bdf22aba280a6c8480f1514e9104630785c97933bab0c270c280f6f3e1927261d7f1dca16debcd360d0534cdd309e1263a79f014b3e37125736edc77e55ad963c84d6f8324117fc4184de0eefd8b14927e0b05d22a3c81abaefe6b76e60b692c79b8ae7de1037d92d6a14292145808906c5e4920836f613a0f6cc7cc166fbdc5a4f70ae8f17b3f7b52c696ae96bcc42bc1eee4b2e05f793bbd016254a86f7bca1258972babb175f6bebf36f6d7a58420f0d673072639caad123df9e1a60be6dedb69671e419400b59752119d074db4c18702f460b5a9ff3345f70bb7eb0deedece2dbdfe42d4714759f9b24e99f917f610cb8560582ddc9c6424f01f208c9466260079451b18270bc81fce27a0b812cbee7b61ae45ba63b75c9abcef24b6d4c08ff13ddb799a3b76974465685cbb240edd8d70621aa26d5b85cce4550b1eed4747c7bdee5dd6e8661faaf2e7b2c6644dd5d8d4cbd31a40c47fcef3010841a3cacbf83bbbde6b0814fa71c8b74671db91e11352297bb1b3207894267d633f671a56b28f4333e217707e01882b7c7c07bc04d283123c2ff01703e56c6ac2ef99ba3c2e08e7f68de1dac3e32535ad92cc3b3dfb98ea188f40811055465b58c2836452be6f1d49da65f5f2bbf9401bb2a2195448a6cb586f369ddb064dd0eda4f2a023f94dc9db489a03d5f199c54a0d8147274562f5d2fde15cc8be46ab21b363fd9a7dcca56c5517deade2a76649ad23"}]}], @udp={{0x1, 0x2, 0x8}, {"b62e09c758866f7cbda12f10b0c791c189ef7cf566ffa1ba8554ab5ff7c0dfddba2238af4515fb76ef4873747c5d22c742c576feca7613a9856112914f605d80a8033619c776ccdd06b2e4861298ee071b6529af9d21ff101f81df69ade182eee7487a5ccb1caf2760278eae13c20463cc321a8c56fe20307c3099fffbaa2e176db520e56cefa24f324bc46841a5289ba95d38b2cab435a03b16fedc37d75aa9362728592ceadc6620a0040c5b95752f799571b04f4cf674f0b2e7c18af6d34fa28edae9bb741a909037779bf6c2ff2677e202536107c6d7e22241f6abc79f015dd82e4ee23531c3346cf6986b51aff36880f637efaefe01e032c6d2ef806285630b75da3c2ea9f7c3ce85ef777dafedc5e16de37a6340b5c6da57d77c8f44fd114774b35382334493cc4263442fb333c42456cb6a04f7b9c2698832a3c726d8c77fbfc29a7887fa7b73531ddc35b196bc3e992a875c0c8a618de1f76bca0f790017ae8e2a4c05d25bb11f59d74a98ba40fe2188d3411e8788b7ec49e86f81e7e8d8af86b962e2d46b5646026020e0720b4ba169d92e25348d29b546e339c74b03f38d91019be095ae42403bef98b4a1b67a7fb85aeafacdd0b5a5bbe98990641ed101cc1cd7ebe7e42c48117cc90aa392dc60283df9a668745a4b909090ca76bdad900de8a8fcc7e093b962b6aab4f6de1ce89d81a603f3b56b24707b24e05f608d17aeb04a0a3e7adc860db8cf5313e97aa07ee9e928f1cca3ba17a660ca24fc44427394c10eb96d1e9e56738889c01e7f56a0bf0572746c5ff2671d52824d4afa00baea24821bce9a029a567af828f761c2822b678a91433759899000be68c4c606f1b1f6d8c92b909e92befd8c99687d0d148dcc078a2607111719b72852dd79ad669dceb80ecffb2c69a60b36c4ad5ed67afdcd0c68367de89663bda18876426468dfa60c2ce671afc8ded240e3b12ca32ab301ac00178eabee1c72869cea31d64bd50a125bf45e64b97deeed5d96385e57cb3c0867187d2b43bcabe07759b2c1cc18e048d8b339c28c36b82c73d4f55f5bc21e4937dddf6ff1391054eb1e067515885e0d2393b9787f06f05359f4d13d67faa6bffaf0619d11038b1ef6a1c3635cf2047ab928433fb0daa5874106ff98a37a5b98f0dcae144a1e1389e7994dce39d6f1c9a7e163f85db9cc46ebce4031f069e926624d956df21bb8ff5c336d3cffb1d55e4e5fc827d00640ed834931a98055f1ac9ea383681b2102fa9ee1dc316196c1109b56696a5547cdeaed763345186b94877b77582ecf31cde4fd446c2071f384c887dc8a9bc4cb14535ec70f6decc4eb306dbac8691b92ee4b41fa5fbace77b8d307b237e78338b42dc79a0cb9aa8cf4929a13e1a74d28ffd6d53546bf18baad1173bd5f7f8c649c1decc2fa1c9bfb3d14d601687fa5c6fe042f67618761f6a3b4f8791e1a5f89eeb20a659ebfd000832bddd9387461099755712bf59d5a3ee68ea32639731adb0d2fb1d202eca156986cee1373d4cf41a41c44ee960d804c762279839611cbf4d20a670381ea4fdfd9e47e1305d373829a2ca778e6eee23a7328a13ed4a225c26fcbe9d7d4eb25e9d9c62bf11ae0ce31d164eac88ba53e4263ab19bd42d9dca3abc3191e377df06609357dfa49d187a8ca1b35e776ee2f3cedb056157af3844929f5db5f07ef4cd87e160bbf35132bc4897d1724cd011ee39cb55cfd585a27181f88b3be0773489a7f42fa0ec34f013a61070a4148725778450261963f4a4e595fc484340cc1bce6c61548f0a5f5e24a0625942e2763fc41076afb47b1305d7706e1b55b0b8c3a62a2a3cb86240c37d1108a9c3c3ce995da12de203b3df2f250f7f4ebaa894d6896474da720527d2fc6bc85886494ce05819e711f27d5000b886d18f0394df2cf9f96aeff59f2874bdb48be296e799672aeecfd78378b85da63d5f42078b64de989f96d1903dce5a6b1e1656dac3a30de296392ef6e31caba74b8126ae9612f1f99db5388c8d18cf7f02c925c66ec7153e12fb016e0f2a78a54efb5e975196657186350feeb826c0e3c498629489c06a12237cd75d4e6051a6bb20dc749ce4e56108f3b59e066747b2a12bd6990ef680a09aa1a9963781996cf254e7d3e8660deec00c07ab9c40f7c1b3d5d08f0557db440ae4df2d28b6ac1f79e64eda39c1427e7717524c3b0ccc34cc347db1516d903f236a956036da745ecfed725a8f6e675896cadf202e38b1af226cc2a3169a20716a8ba0b06a0dfe1cce4a94783802fc94396747388a77bc6098c9c8051c0afe872538e271d735111398e5323c72e4996f6dac9d7b1abed8f71b8471f1616108ccf179d945276f7c0b4e092386f99dc86769652a66243000c5f90fc0001f6e17c96bb55d71ed99c81cafa9496de513c502dc6adc31cb465292580962dcf64fca1ca40048fd28ea82e7e07325dfe34c1f07ae862be90b4b536ebd09cd652ee0c90babca9f89cb9421726ee2cdba10ddfcf133f2814e23500c261ea5a35f42cb59dcc9c57d5f4ad4858a51119bd10e0a1563356ba0d27e6b72f4a74b689baa5baf7c49dce737c1dd8e4e3d47605e3895e74823531b81a526fe2a6f309196e1b4d5fbb8bce43314d31e479b6ab0036f22688cd2cf0abb0fb8329e8f278d86fe67c70d1ad91bc25b904f3cdeb42f78bddfa128b161e144c29ab2321c42b8f60b0c323a9d4fb0515a39e3e8666a4f1a9647fa7d31cbebde7cfa5be7d3969f7cc3d9e5e25e8b57a7c634e89797880354ef3c6811244bbaec9a62578ca8824030f75c4ea3d2b3fe44fa18ec9ff19a10fe41d4e859b3759076cb7355a10e375df982e12eb540dd8d1ef5a6f29eefe3a8637820ea56c0e4471c929b13244b94a92c81261c1fec45608078ac055b116d4d04f5b83ce001c9a435861bc85a22c3b61591781c48d00cc8ef42c0023e1583bff5708fbcb87fff7aed9f19803f7a13e06f54c93681f9dce953033e93e07f471479fcefa39607b06880740c94d22d6bf9b55b4a767361b1ec2371bbc7ac84cdf1c74830e64e246d1556df17d51b615c7570d65e1ed48346c2182f36e9821f9ead48647264d9056c7fde8a0be90645290d65b4b6c964f8e28c55dc6b172a22681ed77fb07e1f59a5b3e21f72a581515f9d2f5fddc38d4a16cd8221bb75e071eaceb32580cbac50602f0903c3000824639b825f4a91d9d38ccac7e6478e93ba21ce47974b58882a6b33b14f066ceb9ebfe3b7d19ffa561c1e5624b66aa7c2dd95028e304638f87ab8772bc6db4e5d64844a4354bfd4e0f05ac6cd101903a8dd58939961b07fcadf0bf5c42a963ecf37d2e8eee6499b68730f793681c08bff1237de014d41ba7a847b2b1ca31f64a28622a6673556cc6d6b750fb3550996e5e3b93d07cf70bdbbdf02946a8ef4a07ef7ed69ac12e7ad202b0b94e7246d660262c1babe06cd964227aec8674f1d6158c2b1258f0d3a7e1d85e99f84a198538471053c7c881d43ac3403ad8c1e0ef99641afef0d4f46cd98f865781f726ebf743580d84b0ef0f674ef3b5b802cd3ed92332ae0d9cf67cb58fc00b18942e7ab6662637e31da59b7d3a5cf6b37b2422f522a499e82217d3db7138202f9e6379e0473a204d2f439a12e91c6fae0870df750551dd5f9ea8efa0bac690d6144b36b3ff785728c183d09886690d1824065ba01e02b52168a8065c0985c1afb7b1d252ea2e046611fd0905c4cba2c1bc194f4d783cb645f375951a6bfe4fb93b8a86a860fd337ac1f7236de270fba95cf1d53c9b9a9dede7fdbb98db8a7f1d72a9132b602b7e2da3bdf902d2f95f933f0b49cd01643310f774d5f902f10f1dba5d30de09fd72b226ea0d075cdfe7a1391366a70e07aba97b70e80a55e10271f37e5bb7e643367273e2594e1f60dbc5727577a10c0bb01bea92dd589cbb1538ac71665fd30d063454c3430d0c56bdf4b852bfdf09f6fc932f893ceb0b622153792d96c41ca49211e32af9c876cd764227c8bb8ff5400c164a0b7c6eb455f591c9c0cfe694b13d0fb8aa62b4a7bdb72013444bb9f938307d1d6aa06ed152158b7c9df097433ae62e961e318902bdc0d59f1e808c3de96a817f11a26bc7a92ded2867967c26c9659a4a56041dc6f2710c27ef705df0a5a168c5be4533728eddccaaed2c621124d33308effa69889ae7c1c6d69607a98fb96d923c738132c98cc7af48ca3bf17e4548e4131b7e40b4a2d94fb0a759413464e60793b0e4bfadda1cf2d382c5e3a2d728a1874ba6fb7e28ab603e854b36bcddf8a1eeb55bffd9af4e0b49d59dce28b69187c3f600d28941892a8286f28ae665f608fba51c6ca64f070d3e8a06ef5dbdc51403c3ff2b453be23469c6fadf1a9466064f03884fce4e6e6813c0ffe802c5bd055a73cb626a0ef75f0799f21842bfda05355ff174b3d54c4f8dfbb2df311b4e22b9e7dbcac70591b09f93133cdd22094569faa2c123ed5e3819ffc8c65e369ac519248c873658365e07dc9bce0328034239f7bfe457c985585ea36fd547cd62eb9657cb9c80a702d3068e4a64c6463cdb0f4b04ee49499f7399298135adf5cb6a9b42374cd48b48e1fe954377a7c438be7a2e877c906ede64d559085674ae25de2b009354ba57370ec70f1d608090bf265d1f27317a9c9b557369e676b4ea80b59bf087545e6dee595517d7020238ae055dbdf8f1e2a6fbe75a6090f0fd988ce0150302fb5347881a74e6990e9b073935fd0e19e12eb72f619021ac3e168c08485f9e23e92b93c0e3dfb5cd0b981daf4fe3b20f8677a2ba68c5676d001f77149d48436c4831a78e9f0e6c05a8dcc44ad75ea4f33eeb1b7fbe4664afb7ceef06333f32716f37651527f4a9f408d37481a54188d8e27cc821f72dee714d0b13e1ada5ee9e7c9c77dccaa4179ca706c4ebcc8c04281db20b24a0c39ed2854bf5dd5a0c984ec1ae1fe9bef425676aa02f74dc0d0709e07ac2b3b0777bbb0cdc420e8fc398f47aee9d95ced43349adf7dc74e4235f7221328702243a29214233b498ae20c6a8c1193c6271eb7a137530217cbd281c2351e1251d3624900856474b1dc121dbfdad94bcf6ac8eb3a2c554e4a01f91fac3afd5703a5c9c3ac93ce5c3a8c081a4099809d9176de6db57691b0e2df6feba9dfd98799f0ab97d84f5c129a57346e38598c700cd3cefb7f25365167f0f4264af129739b5cc29155c6366a1d94d9a5dd03879ce50e8854144ca4c34f55636c8813fbda6cd28e7f491f7ce907adb190cea42f0742829912007b40ce618170d96635252e8c769c656f6cec354e7a4b467cfef2343bbc333d7daafa7543a4754af5971af0d5803bdbdebc5a13af48e01998f4038b020bb8c1249bf35df844886827d4b4374f5f3714308917b59c80b3beffbf2c9355b09b3858ee8886f8fa7fa24b4f20bb9b0fc9a6cb0c06afb8c18ff6068f8dd2a071bcc6807b01b7f0998f0a8588d1b7320cc059609167479c2cc1f244f7d070d7e016cba9a4acca5ea05e0a8d58434a42e8bd7cd520c539cc3c8e7850ffc6a87d3f421917f862d6f2f03303fd4673fc3e7279d80a7f0ecf2f795134460035a1f5329b3c323f76aa0d7d75bb43ffeefa82fe00a327f5a56547d54f459b1acd37e8aadddb5ff2a157ddf5d8e289261e5aadded91fe21262b2083615b79de95f9e187d5d6ccffd3920d2b4649a7a5bb0aa2020228e38ea89fae877a79c51e91d6c5344cacb2c5bf3922e03616d97b0b085aa84ea9fe6055d2178c293725ff19c3203e9a"}}}}}}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xb, &(0x7f0000000000), 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000340)={0x10, 0x3, 0x4, 0x9, [{&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffb000/0x5000)=nil, 0x4}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f00003fd000/0xc00000)=nil, 0x9}, {&(0x7f0000ba9000/0x1000)=nil, &(0x7f0000a5b000/0x3000)=nil, 0xfff}, {&(0x7f0000c3a000/0x4000)=nil, &(0x7f0000e24000/0x4000)=nil}, {&(0x7f0000d09000/0x4000)=nil, &(0x7f0000e11000/0x1000)=nil, 0x69de}, {&(0x7f000046e000/0x4000)=nil, &(0x7f0000a95000/0x3000)=nil, 0x4}, {&(0x7f0000bff000/0x400000)=nil, &(0x7f000051c000/0x3000)=nil, 0xfffffffffffffffc}, {&(0x7f0000400000/0xc00000)=nil, &(0x7f0000ea9000/0x1000)=nil, 0x400}, {&(0x7f0000ef4000/0x3000)=nil, &(0x7f0000dfb000/0x3000)=nil, 0xfffffffffffffff9}, {&(0x7f0000e96000/0x3000)=nil, &(0x7f0000ff9000/0x5000)=nil, 0x1000}, {&(0x7f0000958000/0x4000)=nil, &(0x7f0000f1f000/0x2000)=nil, 0xffffffff}, {&(0x7f0000400000/0xc00000)=nil, &(0x7f0000832000/0x1000)=nil, 0x7fffffff}, {&(0x7f0000edc000/0x1000)=nil, &(0x7f000063c000/0x800000)=nil, 0x401}, {&(0x7f000095d000/0x4000)=nil, &(0x7f000073d000/0x2000)=nil, 0x1}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000d8f000/0xc000)=nil, 0x9}, {&(0x7f0000b62000/0x4000)=nil, &(0x7f000056d000/0x3000)=nil, 0x5}], ['./file0\x00', './file0\x00', './file0\x00', '.\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file'], 0x3})
setreuid(0xffffffffffffffff, 0xee01)
r0 = socket(0x2, 0x4001, 0x0)
r1 = semget$private(0x0, 0x4, 0x105)
semctl$GETNCNT(r1, 0x0, 0x3, &(0x7f0000000140)=""/181)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000080)=[0x0])
semctl$SETVAL(0x0, 0x0, 0x8, 0x0)
r2 = getuid()
setreuid(0xee00, r2)
r3 = getgid()
setregid(r3, 0x0)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000000)={{0x9, r2, 0x0, 0xffffffffffffffff, r3, 0x1, 0x4}, 0x20, 0x7ffffffffffffffe, 0x81af})
fchown(r0, 0xffffffffffffffff, r3)
r4 = fcntl$dupfd(r0, 0x0, r0)
setregid(0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8020697a, &(0x7f0000000300))
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x7ff, 0x0, 0x5, 0x1fc80d8b, "04000000010000a9b3c742000000d35ed900"})
writev(r0, &(0x7f0000001600)=[{&(0x7f00000004c0)="e2c79b08075bbbf814f09378c1d9096889469b7972c479c4066163912381ed9552766e6422b6637e98d064a5c9a7ad953a5830d6c4591a63be4dad272087a7ea07962c127766083d86096bc4aac1a6b81bea9c89d80cdfcd78f974c8a1bf0198e37e6a8cc84240087021db12c10678a610890a652047cfd1670c158e26e4321fa14d3b98065b7b9c1c041b9f6d8b5b87809ab02e70b3c6327fd6146fdb7fd9d70a2e737def0b268f03a8aa92aeea9de4cff00674c69ae6cdaa04c8c411582f3ac70d82f79b566fa954c4e92e0701f8d1f070cc955d3ca3e56c0b0be68d9b59e364a5d2ba4479aea06bdfd617a77511e6d59f30870412d5d8e9e2b7880981", 0xfe}, {0x0}], 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x5}, {0x5}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000280)={@empty, @remote})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x16, &(0x7f0000000000)="aad485c4", 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc450443e, &(0x7f0000000240))
r0 = socket$inet6(0x1e, 0x3, 0x0)
bind$inet6(r0, &(0x7f0000000040)={0x18, 0x1}, 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0x44}, {0x4c}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
mknod(&(0x7f0000000000)='./file1\x00', 0x6000, 0x400)
open$dir(&(0x7f0000000580)='./file1\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
dup2(r0, r1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x25}, {0x1d}, {0x6, 0x0, 0x0, 0x1001}]})
pwrite(r1, &(0x7f0000000300)="977fffffff000000000000000000", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
flock(r0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
ioctl$WSKBDIO_BELL(r0, 0x20005701)
mknod(&(0x7f0000000000)='./file0\x00', 0x2000, 0x5599)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f00000000c0)={0x4, 0x18, 0x29, 0x6}, 0x4, 0x0, 0x0, 0x0, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397", 0x45}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0884450, &(0x7f0000000240))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, &(0x7f0000000280)={0x7, 0x0, {[], [0x0, 0x0, 0x7f, 0x7f], [0xffffffffffffffff]}})
munmap(&(0x7f0000fee000/0x12000)=nil, 0x12000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
mmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
r0 = syz_open_pts()
write(r0, &(0x7f0000000140)="83bd6912bca1fc2e5f454f591265628fe9e4afa388da39f88d0646a968f148e7ebaf36a2e4efc0b8453046132f9502818c90a3f06005c5bc5de483dd3ffb6a783eb6cbeddbb6ad9c3d5bd8a4c34b1cfa25cc73cde442aecb3dec83ce1a06891ec7c99de39aab447d212d5b4817ae6730a5dc", 0x72)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577246af535efe86d5cb19a32e25dbd09d443"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)='x', 0xfffffefb}], 0x1)
syz_open_pts()
syz_open_pts()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
setsockopt$sock_timeval(r0, 0xffff, 0x1006, &(0x7f0000000340)={0xfffffffffffffffb}, 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSFILDROP(r1, 0x80044279, &(0x7f0000000080)=0x2)
syz_emit_ethernet(0x4e, &(0x7f0000000300)={@broadcast, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "cfe624", 0x18, 0x0, 0x0, @mcast2, @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@mld={0x0, 0x0, 0x0, 0x0, 0x0, @mcast2}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x50}, {0x14}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
poll(&(0x7f0000000000)=[{r0}, {r0}], 0x2, 0x0)
poll(0x0, 0x0, 0xfffe)
close(r0)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
close(r1)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
clock_settime(0x100000000000000, &(0x7f0000000000))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000040))
sysctl$kern(&(0x7f0000000140)={0x1, 0xb}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000000)=[{0x40}, {0x1c}, {0x6, 0x0, 0x0, 0x2528}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
writev(r0, &(0x7f0000000400)=[{&(0x7f0000000040)="e123557992c9dd2fda0ec17254f5", 0xe}], 0x1)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x800090d6, 0x0, "d700060000000000005b00000000000000e74de4", 0x0, 0x4010000})
write(r0, &(0x7f0000000000)="0a02e677154b4a6a422dd2", 0xb)
write(r0, &(0x7f0000000040), 0xfffffec2)
syz_open_pts()
ioctl$TIOCSTAT(0xffffffffffffffff, 0x20007465, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setreuid(0x0, 0xee01)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x74}, {0x3}, {0x6, 0x0, 0x0, 0x7f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000a, 0x0, 0x0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
sysctl$vm(&(0x7f0000000140)={0x2, 0x7}, 0x2, 0x0, 0x0, &(0x7f00000012c0)="02000000", 0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x10, &(0x7f0000000000)={0x2}, 0x10)
sysctl$hw(&(0x7f0000000000)={0x6, 0x18}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x37}, 0x4000000000000003, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x8f})
r0 = syz_open_pts()
r1 = syz_open_pts()
writev(r0, &(0x7f0000000600)=[{&(0x7f00000002c0)="18", 0x1}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000))
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0x0, 0x0, 0x0, 0xfffffffe, "000000000000000001000000000000f843d07e38"})
select(0x40, &(0x7f0000000040)={0xff}, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000180)={0x97ac, 0x0, 0x3, 0xfffffff9, "0900000000e60100004d1d9f8d1b8283476e00"})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000680)="2bb8b724b21226f8ceb9bff3cff9900e63bd2378f5c5388a5e", 0x19}], 0x1)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f00005c5000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000555000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000694000/0x3000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f000047f000/0x1000)=nil}, {&(0x7f00006ab000/0x3000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f00005ff000/0x3000)=nil, &(0x7f0000769000/0x4000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000281000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f00001a0000/0x4000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000631000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./bus/\x00', './bus\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus/', './bus/', './bus\x00']})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x80, 0x408})
r1 = socket$unix(0x1, 0x5, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r2, 0xc0286988, &(0x7f00000000c0))
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
dup2(r1, r0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff11a900000000", 0x8)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x3, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x80000000000971]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x1, 0x0)
recvfrom$inet(r0, 0x0, 0x0, 0x803, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0xc0}, {0x2}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@random="1b329ff120e0", @random="c182c3cc9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = socket(0x2, 0x2, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82023e"], 0x10)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x2e, 0x0, 0x0, &(0x7f0000000280)=[@rights={0xe, 0x7, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0xff00800a}], 0x16}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000200)=[{0x5c}, {0x81}, {0x66}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@local, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @remote={0xac, 0x14, 0x0}, @broadcast, @multicast2}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x7}, {0x15}, {0x6, 0x0, 0x0, 0x20000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
r0 = syz_open_pts()
nanosleep(&(0x7f0000000080)={0x68, 0x7feffffe}, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x5849, 0x0, 0x800, 0x0)
r2 = kqueue()
kevent(r2, &(0x7f0000000000), 0x3f, 0x0, 0x7f, 0x0)
close(r0)
r0 = syz_open_pts()
r1 = syz_open_pts()
r2 = syz_open_pts()
r3 = socket(0x800000018, 0x1, 0x0)
fcntl$lock(r1, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100801000})
fcntl$dupfd(r0, 0x0, r2)
dup2(r3, r0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x7fff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0xc1206925, &(0x7f0000000300))
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x31fc)
open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
open$dir(&(0x7f0000000280)='./file0\x00', 0x200, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x28}, {0x5}, {0x6, 0x0, 0x0, 0x90001}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="2eaabad40041708e7847e1b7e237", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0))
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x2, 0x0, @broadcast, @multicast1}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x48}, {0x4c}, {0x6, 0x0, 0x0, 0x7ffe}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = kqueue()
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
kevent(r0, &(0x7f0000000100)=[{{r1}, 0xffffffffffffffff, 0x85, 0x2}], 0xa, 0x0, 0x0, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f0000000140), 0x8, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)='D', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x2, 0x10, r1, 0x0)
sendmmsg(r0, &(0x7f0000000080)={0x0}, 0xfffffffffffffdaa, 0x0)
r0 = kqueue()
kevent(r0, 0xffffffffffffffff, 0x7fffffff, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x8, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
msync(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
syz_emit_ethernet(0x2e, &(0x7f0000000000)={@local, @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast1, @multicast1, {[@timestamp={0x44, 0x4}]}}, @icmp=@mask_request={0xe}}}}})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1b}, 0x4, 0x0, 0x0, &(0x7f0000000140)="05330942", 0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_int(r0, 0xffff, 0x1, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f00000000c0)={0x4, 0x1e, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
r1 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{}, {{r1}, 0xffffffffffffffff, 0xa3}, {{}, 0xfffffffffffffffc, 0x0, 0x0, 0x0, 0x1}], 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x890, &(0x7f0000000340), 0x80, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
setpgid(0xffffffffffffffff, 0x0)
symlink(&(0x7f00000000c0)='.\x00', &(0x7f0000000080)='./file1\x00')
unveil(&(0x7f0000000000)='./file1/file1\x00', &(0x7f0000000040)='x\x00')
mknod(&(0x7f0000000140)='.\x00', 0x0, 0x0)
madvise(&(0x7f0000ffc000/0x1000)=nil, 0xffffffffdf003fff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000340)=[{0x7}, {0x74}, {0x6}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
open$dir(&(0x7f0000000080)='.\x00', 0xb00, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x81}, {0x81}, {0x6, 0x0, 0x0, 0x1ff}]})
write(r0, &(0x7f0000001700)="fb2fe7e986d81d7acfccfbcbd4a2", 0xe)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpgrp()
setreuid(0xee00, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r2})
r3 = socket$unix(0x1, 0x1, 0x0)
r4 = getuid()
setpgid(0x0, 0x0)
r5 = getppid()
setpgid(0x0, r5)
setreuid(0xee00, r4)
ioctl$TIOCSPGRP(r3, 0x80047476, &(0x7f0000000040))
r6 = fcntl$getown(r3, 0x5)
fcntl$setown(r0, 0x6, r6)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0xad)
write(r1, &(0x7f0000000080)="e5", 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
close(r2)
r3 = accept$unix(r1, 0x0, 0x0)
poll(&(0x7f0000000200)=[{r1, 0x10}, {0xffffffffffffffff, 0x1}, {r1, 0x8}, {r2, 0x80}, {r3, 0x4}], 0x5, 0x6b46)
shutdown(r2, 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x2d}, {0x4}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x4, &(0x7f0000000000)=0x100, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x164f9fb7, 0x0, 0x9ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
symlinkat(&(0x7f0000000180)='./file0\x00', 0xffffffffffffff9c, &(0x7f00000001c0)='./file1\x00')
linkat(0xffffffffffffff9c, &(0x7f00000000c0)='./file2\x00', 0xffffffffffffffff, 0x0, 0x0)
linkat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0xffffffffffffffff, 0x0, 0x0)
linkat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x0)
sysctl$vfs_fuse(&(0x7f0000000000)={0xa, 0x4}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x2c}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f00000002c0)=ANY=[])
symlink(&(0x7f00000003c0)='.\x00', &(0x7f0000000140)='./file0\x00')
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f00000001c0)='x\x00')
unveil(&(0x7f00000000c0)='./file0/file0/..\x00', &(0x7f0000000040)='r\x00')
unveil(&(0x7f00000002c0)='./file0/file0/../../file0\x00', &(0x7f0000000300)='c\x00')
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000180)='W\x00')
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff})
r1 = socket(0x18, 0xc002, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
dup2(r1, r0)
r2 = dup(r0)
sendto$inet6(r2, &(0x7f00000001c0)="348283560be1b2c4a82c37eec4e13822948983c2b76da98af16dd3dd252e31bae50a76510f236cd22369fee264439cd1bd8877c082bea869038c103df3451140388fb51f61dfb8378c3cee38e188529a4fc8a7eaa9cad1681153f9364e049752f4617fa956fe19fa1ca4268c14f31a111a5e28bb557d3bbf639fe56bd4e0f42dc911ff42d167aecee9291eb05f4c72582134000000000000000000000000000000d597c26374a5203f1415656905391dade1dccf1ee119c11350b8ef7164ad1948cebc3ab8d4b6d3bf958070fd2ebd1414236e968d7c7d34bb", 0xd9, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100))
ioctl$VNDIOCCLR(0xffffffffffffffff, 0x80384601, &(0x7f0000000480)={&(0x7f0000000440)='./file0/file0/..\x00', 0xff, &(0x7f0000000340)='./file0/file0/../../file0\x00', 0x1a})
unveil(&(0x7f0000000500)='./file0\x00', &(0x7f0000000380)='r\x00')
mkdir(&(0x7f0000000080)='./file0/file0/../../file0/file0\x00', 0x0)
ktrace(&(0x7f0000000400)='./file0\x00', 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000002c0)={<r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f00000000c0), &(0x7f00000001c0)=0xc)
socket(0x1, 0x5, 0x17)
dup2(r0, r3)
r0 = socket$unix(0x1, 0x2, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000400)=[{0x61}, {0x45}, {0x6}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000180)="456a97f2560d61aa617da6717fe7b6d82e1aec1bbc5219d0ac488426d59beaa99bf52ddf4d8d5928bfa120c81f3452a17fcd5692ad4cfdee835d10960b977ccf43f4f035e275b635eff6c27c498e53070c5f7a3f790fbe690f3353214e110424e05cbb11a32a73d736dd8516d930bfdd4500194b5a143b321e6523", 0x7b}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000480)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504446, &(0x7f0000000000))
mknod(&(0x7f0000001980)='./file0\x00', 0x280002002, 0x2065d)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x0)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f0000000080)='c\x00')
unveil(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000100)='x\x00')
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000200)='x\x00')
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
pwrite(r0, &(0x7f0000000140)='ms', 0x2, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400002ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getsockname$inet(r0, &(0x7f00000000c0), &(0x7f0000000000)=0xc)
r1 = socket(0x18, 0x1, 0x0)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000040), 0x30688, 0x0)
r2 = socket(0x2, 0x3, 0x0)
close(r2)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r1, 0x0, 0x0)
sendto$unix(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
socket(0x1, 0x8000, 0x2)
socket(0x18, 0x3, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
r3 = socket(0x18, 0x3, 0x0)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
socket(0x18, 0x3, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
shutdown(r1, 0x2)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
getsockopt$sock_int(r0, 0xffff, 0x1000, 0x0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r0, &(0x7f0000d06ff8)='./file0\x00')
open$dir(&(0x7f0000000000)='./file0/\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x45}, {0x48}, {0x4406}]})
syz_emit_ethernet(0x66, &(0x7f0000000080)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, 'Q`B', 0x30, 0x0, 0x0, @rand_addr="02010062c27f000000aa4c02431e9572", @rand_addr="776fddc5797d52f70049ae1692050be6", {[], @icmpv6=@dest_unreach={0x1, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, '\x00', 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast1}}}}}}})
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000040)="3a1f2842", &(0x7f0000000100)=0x4, &(0x7f00000003c0), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x35}, {0x4}, {0x6, 0x0, 0x0, 0xffff8000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000680)="ee65ecf860080013c2", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x7, "01000611aef6e4c611d53300"})
setsockopt(r1, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r2)
r3 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r4 = dup2(r0, r3)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r4, 0x0, 0x0)
r0 = open$dir(&(0x7f0000000380)='./file0\x00', 0x200, 0x0)
mmap(&(0x7f0000ddf000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
msync(&(0x7f0000dde000/0x2000)=nil, 0x2000, 0x4)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000040)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f00007c7000/0x4000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff7000/0x9000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ff9000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000706000/0x4000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000ffa000/0x2000)=nil, 0x3}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000381000/0x1000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ff9000/0x3000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffa000/0x1000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000ff9000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff9000/0x3000)=nil}], ['\x00', './file0\x00', './file0\x00', './file0/file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0x1c, 0x0})
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r1, 0xc0384600, &(0x7f0000000100)={&(0x7f0000000080)='./file0\x00', 0x1c, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x60}, {0x3}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
symlink(&(0x7f00000003c0)='.\x00', &(0x7f0000000140)='./file0\x00')
unveil(&(0x7f0000000000)='./file0/file0/..\x00', &(0x7f0000000040)='r\x00')
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000240)='c\x00')
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
ioctl$WSDISPLAYIO_DELFONT(0xffffffffffffffff, 0x8058574f, &(0x7f0000000000)={'./file0\x00', 0x204b, 0x1, 0x3, 0x0, 0x8, 0x10000, 0x0, 0x2, 0x2, 0x8, 0x6})
unlink(&(0x7f0000000080)='./file0/file0\x00')
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x8, 0x0)
flock(r0, 0x1)
r1 = dup2(r0, r0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000001c0)={&(0x7f0000000180)='./file0\x00', r0, 0x1})
socket(0x20, 0x5, 0x8)
setsockopt(0xffffffffffffffff, 0x7, 0x8, &(0x7f0000000200)="c18bc1503377850116d728d65d94c3f3cc117f6d7b31636af9017f2101070ba1855c5061d2fb2620269a", 0x2a)
pipe(&(0x7f0000000280))
dup2(0xffffffffffffffff, 0xffffffffffffffff)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f0000000200)='D', 0x1}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
r1 = socket$unix(0x1, 0x2, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000000)={0x0, 0x100})
fcntl$dupfd(r0, 0x0, r1)
geteuid()
setuid(0xee01)
shmget(0x0, 0x2000, 0x218, &(0x7f0000006000/0x2000)=nil)
r0 = semget$private(0x0, 0x4, 0x1)
semop(r0, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x0, 0x4, 0x800}, {0x0, 0x2, 0x1000}, {0x3, 0x9a6, 0xe86f00ca028eaa0a}], 0x4)
r1 = semget$private(0x0, 0x4000000009, 0x82)
semctl$GETZCNT(r1, 0x2, 0x7, &(0x7f0000000600)=""/70)
semop(r1, &(0x7f00000002c0)=[{0x2, 0x3}, {0x0, 0x6, 0x800}, {0x4, 0x200, 0x800}, {0x1, 0x8, 0x1000}, {0x1, 0x5}], 0x5)
semop(r1, &(0x7f00000005c0)=[{0x4, 0x2}, {0x2, 0x6, 0x800}, {0x4, 0x8783, 0x1000}, {0x2, 0x400, 0x800}, {0x1, 0x0, 0x1800}, {0x0, 0xfffc}], 0x6)
r2 = geteuid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r4=>0x0}, &(0x7f0000000700)=0xc)
semctl$GETPID(r1, 0x0, 0x4, &(0x7f0000000500)=""/113)
setregid(0x0, r4)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000080)={{0xfffffffd, 0x0, 0xffffffffffffffff, r2, r4, 0x40, 0xffff}, 0xb02, 0x9, 0x8001})
r5 = getegid()
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000000)={{0x2, r2, 0x0, 0xffffffffffffffff, r5, 0x12b, 0x7}, 0xffff, 0x5, 0xc})
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0xfffffffb, 0x0, "7e5a0f2bee0000b511900000000000000001ac3f"})
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x443, 0x0, 0xfffff901, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = kqueue()
r3 = dup(r2)
kevent(r3, &(0x7f0000000040), 0x80, 0x0, 0x419, 0x0)
dup2(r1, r0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x200, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000), 0x4, 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x1e, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="dd08f1ee0200040000000088a812703f", 0x10, 0x0, 0x0, 0x0)
mquery(&(0x7f0000ffb000/0x1000)=nil, 0x7f7fffffc000, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x35}, {0x5}, {0x6, 0x0, 0x0, 0xffff8000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
sysctl$net_inet_carp(&(0x7f0000001040)={0x6, 0x2, 0x11}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
mmap(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x80000000002)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
dup2(r3, r2)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000080), &(0x7f0000000100)=0xc)
connect$unix(r3, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
accept$unix(r1, &(0x7f0000001640)=@file={0x0, ""/58}, &(0x7f00000000c0)=0x249)
sysctl$hw(&(0x7f0000000100)={0x6, 0xc}, 0x2, 0x0, 0x0, &(0x7f0000000200), 0x0)
munmap(&(0x7f0000fee000/0x10000)=nil, 0x10000)
minherit(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000002600)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000100)={0x0, 0x0, &(0x7f00000004c0), 0x10000000000002a9, 0x0, 0x61}, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000180)={0x4, 0x2, 0x6, 0x6}, 0x4, &(0x7f0000000240)="38159318fb50f35915f280480cfaee3e6a16426b34ed54ba224b48645cc228452f0d5fdd759e786a811783d7c3c97e9c031da265cbe716ed83d787667ce1fb5097b6d7d51959f397898adb4300df7fc6fc274b0e9ad2fe5a771fcf229a37c45675e63d5e06ac5e3621a164938155ab89a595ec53bab0649330d5b238cdc52334416d5b05ea32fc77df4527da885b623d488d604a255e8e1a4923520954020cdcb0cbb77854517fb3cb4fde45435ea8ae1780df041d2f818b7ded1cea8b53f780de892a598fdb34060083af0d3944319bb61ab2a614ca698bf8e13aaa60b23ad2c9ff08e756125af1c6cdc6964d65a5c3727d13079474298db2441a956902125f904a30261b0f49b97ebdc8f937eb9614334988c18b53da6863d98b7aa4e8bb354db7b7d7ae231c4cbcb05fe731670e245248640848029cf6bfe4427e593d2eb4e3135342d3a46d6e242062e1fca9fe54f42522cc3d18b8e79eb80a20156af7c09fb056b1a307db26bb18f7c0c09f9c8cb8180d761add24cc19b51c626ce649b52455be2b7cb372710ba664695931f0a8f01ddc33db542e899cdd7ce87eb7d60d50a20d2bad70941f492f42ebb5dd9fc5520e380c7f7e8b8e6275efd36e58d38d1986ad62fe0107b9d445cf001479d1f2c41882c0504bbba9a0b76d2862a965f5d5a8de0a64840ab360ea2b9efc3c31afd62e269764ac8fa3353f395574ee36ae04c089c62e4a690e010a3b67ba367c758f5284d16a5f8927fe4969128d214d9a75a7628dce4c46d319ee2087721f24b7fdcc8d5707d47bacc595032b1a5fdf82d539b36cc64f88832ccbdba7295c80d844f9adf23d791b81529849d9e8c66e3769883e1d1c3072cdda1f00161b04a961d6e21c8a9ff8c1fd72037c5383e9e5cc9a17f683149248a5e39c8ddf2beb834dd2c7bb891392a1f548920de04b5582885fc7f3f68cceebb71378207cc7fd0400000000000000ca0b3f1852dd227a3315ac906d3fb8deb5ac2fc709702819e2782206d968136295378f877d9cc638ad1260201e6df24cc39aeec792f684c5e7ae8236f30368f29f54d6b3f47fa10818e17b8da77ae8e4605bf213857db31d9d4e2fe3c5a6f6afd5e6fb1df4c6bab2b65bffbaac13ac9f6689aaad56a357bebc04b5274d765737d73eb45398803aadf2eeb606b3e36961c541798c2b5090c0225100e84147e0efdd3ac7476045a93bbb11227520c9018732993f1b9280c57fdaa280f149c971f799d5a76699a5ffd739f6198db9caaebb447eaed1ebaebb920d3714fe203e85b0890789261be61d59a0676126098f2d8000c615aa74098949a977ae8194300f44b68a701d9936e7bebd1d848b4d54afd9d3852437d9137dbf06eb2dba06881dddaace624b29755772ce07cee031551c85d3a0ddf3f0d7ceb7e09d303c53f9c120d660776baf10ae5bd237518f62a073fc12e884199eaa5c41cd6af0b39a27f97372c3d1e2e7f3d586aab7d4dc46c3a8fd69aa2bca1e093f8ca47b74827afd5f6748064118d6b94dbcb492ad9432b2fa62ea3729dff294bc6c5cc91ddc9d1890f905fb81b3e1a03fc76e271289ae6f914afede4502ae1b7b26b14a63a7468b298bae30e3a92d1fa213194f49e27f1716b7cab2c4c7ee0bc4248e37c3a6e5c09f29670a13b1fd060d9b14e271c76ae78e0e34b6d365cd44429d31e4600e1847084902535f5e845dcff8801200ec9eb161811290a97034b43a90209400a015ec4c71d64589a08a5cfc628ca103233d1a1b65838d6bef2b5561dabfa1ce896944e8bd7a1c33ead43b2d513f3f652433dfc8add8e9d898e7770d2d2f07d40237604d3e085201d6f7c12490567afa5ef35305ae1fa18ada52100db8ffccbdae4559263e34f931a967e441ec9f059b92ef6d4a230237cdd30f5b7e346b36c9a0979ba49ab6c1eb1d5e939dd443f36d7c5023069bb2e5ac074dd757c45785fcf155f70f063094bddc169e0ad830a41f6731f9fb3ac969cd3282b00510bbf22a8f1459de3a4c84de5e17cf779758d64feef67e055c4828b9b713443d778a4e22656ae1963f028dc730ec14cbb20f4bd76b49fcfd34ab777ffeae545981d81ace3be2426ef1df49dcaa9a01044ab610a8fa57b9f74315348b74873922b96bc3a891ef8516754f45ee0a99a09f2462c3cba46d0cb486d548ae014220c8d81997c5e88390c6a7f35e85d6c8edc0f95442247f38480771cecf36cb4ab3922157e63a20c42c89dae975812b7c843400e61030af7d81ecb450a982dcc0cdacee0361def9de12ed2181369b228acf42ef8cb18e20837aa27de3bdb7516cbdc62b410b97b7823adb89c7bbd46b3f3e8bf1ec50f96fb426d9f6f9061b6793ea7b509bd9a189115dc4194308f672adf732800d1a5a73955671b5f39a4192f0c463a94953393926205ef7f1059415803020db8f1b482760eb9477e042553557ff9e7922a095d5498fffac4f279e304912fb9f0c01a67d6cc5eb9b11815cf6f9e4e48361ccc873ad1b81e54eda5919ab4be0077a0aed0f9b0336147b5de003ed0b680a0bf0d1ff5a407281ca18e6e0e5a32fe02021e0c83ce3ef4a04c35706e9c8d95fabb6c33710e2b93c26096056604b54a80ea35711654dcd1436e6d70dcc80a7721b0176b33de9711809b184427d8f571633ec1f591512fcc25dae87db62f0f375ce555f670a5c1289f4f1eae65840bffd8c8e4aa0098411d24bec4c2a87099c7f44f1ff18103e012c0707be1a345199c0aac117d68242062cfd85f59577488a22ed583e5caa6229801172ceadc9b964fb44eeeef0fb113dbd8b8fd9e2edd7f578618c0c9d1f70f2c40c9d1f6bd462d7ce1e4207c7ce44c9c8161d5921e14e23b6923790bad64aff3820f0e074c5717c3d5ab1babff2b278ca0d07926c5ee9c26a569d6f57f0324339a2428dcd18b8bd7755e35b1bfb57741df94a1795343f2407fbee5cc20baad5cf94e16df207ed13907053b01af10725cf71b17de88f8205bf6cb63447967cc31c5a25f071ae6ca50a35e6a5e3c0f16f4760b8b3c662456870adf9d4a2e9dcbaf020e3854fc823ea48b7f3d497b012cde1592abd3599eb6216e4c6ae0ecc925efb47cc17c8af1a3a839075a07d9afdf351d689e37fe7e03f9d2189bbf6e86f64886299a78064ba3717d37c78c1d74cdf0d3ec8c43b6a90e5a506dca5702bd69a05785ffa182079c9e1c85afbc0c4f967ebca2705b2564fb373a7bfcb1924935e0dd10c8f061814ce30d1ecaa25114800f3d26183d138291bc59193ebe0df50aa69cfa21d129c7fc552869ec4d04384aa237cf778e5cdb631e018ebb8a4aafa04c444977b60a82fdf3109a16cc888f646e650f0d767d6ec71745c39c75e3672ec74aacacb29336f8a80ca59c5d04acd903e7a3a28dd7c1676b1fa07e5002433723a5f46989969922688928ec11186d927bfb478e96e18a28395fe7b2ae5796a4ff4eab9811d80ed9370ba3bd7145c9aa795e11fdae349578c55840629dd660c15fc035bcb610e3ff7738940e2d108e531c137e9740a70582f066c32e4831de9d6d4ff28ae1c7c298d7133517daef43ca9e6f1c58ae052bdf90ed2c8730cffc5256ef389413a03ef0f5e34be954e5b2ac05c92c6a1074fa336bc890f4954873bc81166a10e4d8b9d245fb37f83e6f11d0d36f9a9840bea91f727df933ce4413549b6c19b634c48a5854f2320b287e974c31ae19eb95cafcbe67168995c4213872e66d731649dd85229cb2617f8b910ae3dfa7c3c362913f6dae01d3ab1b6038d063281080840953a8ecc945523e15899516c8477dcccd88e9a2277ef50ccbb5ba8bba0216af05c2b32f3e9af961bec9e39c0a567a111a086ad0026ef836d901fb701bad2918850ee61c23c2aeaeacfd65376b4b6212ea777afa56c5ee3ccadc3d552a4bc393d2d5aa0e7fa41ca9495ebca2fffbc5e4808de407237e25f49415337f0b3098d39fa8c40898373450e09f316fcf8d608291b438c31e7efb47ebebfb322f53a4606c187ba0a3902d020a0674806da2adc78dd70422a3c58efaebd8c228695f3c2a585b373bf81da5400dda82113ddc74eb99bfc182c0fee39d61b4781942671a5d2717dfcb75e12be6d8a147b00c85b029dc6f318ba5f11f7d809bb180c9c994fd3a8d948617fe20bf43badb1822af9eed6f7c71ba6b74cfacddc5c5d960400d784fcb7bfa3c3e3b8d9c30142f8ac788840ddcea08f2db00dcc8ed913e48a7191d4812b067859ecbfcd3a215d313ce3b00d9fc69f5bc9222b0e1d4210b3bc4d4f8bc1ceff42332e6f69a5d58289ca8e2b52e0b0ef9f62242643fee5dfb5e880dadf359b4efaae0ef6bbe4e8e819108decd79087e2c58fad51ac1a1fef1a57c438176812b71672cac1bd63f081ea0c6a44b65ef18d273d1d85ee0a763b482f884758a26be1767cb907e4eb6673144b20d314627ec14587f5e753c50316149c1e0151a9ab5a4deba53d9401dba4135ac879f475a73a85c6a76746a95326e2a1d7a88d3aeb53cc39e2f7e9f517f111b621a539649f0028f4bbc64c2a8463721e8d46367b7c173ec66ad3e3419f763f34bd5d874fe329273c2025cc61f268192a111661930699c6a625691e7acbd38f20c19aa5ca5b0b23b20aa3c35b4154de39452446482e9233b0ec0383a76eae952a991a28195bf0773184fb2ac54d9123ab347f887d13a4d84ff84013e238a5851c89917b65e39c75ea317b87db9beaa3eba1163cc5f1491ef0799720826d95a4d079f2df8f18b7247456966f80394226a645a77211ffd8886807b21bdad4a53a048ed04f36e3dd1959db0349382257261dd95acea1a30745b9cd03f046dcff0feba5481c9536dc413c9f23fb54b5fd994da1ce3b7b30b2748797c807aef15a000c7e5085fd7d1690252991b576189e870478c62becd75f810348bca4f44b9840396c47e13394a9262d0041ee2bc3339db1d98fce054b9a883afb8eb0e0dcf2317424afdd55abc0f8d89efe73da93d86f296f94437e13bd8b05bdffde700dc47ed8281b1c0d64d44a7fabbadfdf0504b295021d16dfd9ffb45ae95dbf2c07463d9c34755c561ea2d1885cc4ceecf51c30f6ae5e13a66b5bf561aaffb6a839cc26879cafb603644d13401b88b462922052f12004b4ad8f5aea52e3c03c4403c30db4d2988822009e3aac8e4bb100b98a8ebbdf6d06eb85de8b274f97e731203c68b40ab1eb55836997f20a791d1036f757d323bdb02e4c4c4e0bfe5d87ebd69f0a0492582d2c0957f0920c86d27eea3a898953d6f3f0dff4924a2c0d7c1be92deaa8d88c29a9ae1de776ea9c82a70b60355f3abef08d8de194cc6a9443c9cfd68e662bcd05763fd06130a142370306be94081cbae9c8a977f9dbcf87f38acca48e91cfa9f8e37e3cb147cc0a2963d37fe20b1e5e979825a29bb4538efc7f56453870f2896cf909013cd480b5beb19520704fb2950962f0e549f7f17b3f9abf843cf19153296fb28f0e00046d5bf7fc17066babe2f8e49598fe00449f2f2211284aeb0b481e109e068c6e4e0e858ad5266e42335a159bc7d9ee33eda22de9a63fa7174d5ad3ea87fac8bda3d772821bc42809ca4776dd9fa25a118a2bfb8ba5dbc7d0c6034cc9e671c0a4a26dddc02efc4650ac04692c0491bff7442860a724253e1859a20dd6cef84d6d4d75469f73104ac402a62dcdb1f4d6b57adbe35fa71a4bdaa757532769cf1e34ef65e9f8c769242f3466493528b358f8f8a9f0295d07cb1358a2b767de3a9c226c85fd0c2854a90657fa95d042ff3ff903", &(0x7f0000001240)=0xbb35, &(0x7f0000001280)="42babfb2f4f61200c18eb794913b376230969ba4c6739dfe8febb422e9bde7f56555d2722faeac85d4d13c2c3d95ccffe3dd68bb7af02e87c7f05cb9e017a9bb247e3527c3be4d082228f80f526075cb0d3a8cd013c2a3b890b1343e98614af7bb8e2306d735557560003f457d5c72bc597fab4f424bc4b8814ada2ab3e375993e8d8eacf7cce8d57ab1db23d1c3a364dfb1e0833b0318e834276acb3f3e4e43f6291931b2e01e85608e93b55a209d0435a766dbdbc45575eefcb13649e7e12e98eb1956feee6f985886a073b8db1c917651bf97d26db7886193538c1846657ecb28da63e7901ee94c93975b7084c930e9a1e73913", 0xfd17)
setpgid(0x0, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x2, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = getppid()
r3 = socket$unix(0x1, 0x1, 0x0)
r4 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r4, 0x80047476, &(0x7f0000000040))
ioctl$TIOCSPGRP(r3, 0x80047476, &(0x7f0000000040))
setpgid(0x0, r2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0xb1}, {0x3}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000340)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x3ff)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4d, 0x0, 0x0, 0xa}, {0x14}, {0x6, 0x0, 0x0, 0x20480}]})
fcntl$dupfd(0xffffffffffffff9c, 0x0, r1)
pwrite(0xffffffffffffffff, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x0, &(0x7f0000000140)})
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x22, &(0x7f0000000140)=0xffffffff, 0x4)
r2 = dup(r0)
ioctl$BIOCSHDRCMPLT(r2, 0x80044275, &(0x7f0000000100)=0x1c00)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r3, 0x7, &(0x7f0000000080)={0x0, 0x0, 0xfffffffffffffffc, 0x1001100000003})
setsockopt$sock_int(r3, 0xffff, 0x8, &(0x7f0000000000)=0x4, 0x4)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r4)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0206917, &(0x7f0000000300))
r0 = getpid()
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, r0})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x7, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0xc02069b6, &(0x7f0000000300))
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@broadcast, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @rand_addr, {[@lsrr]}}, @icmp=@mask_reply}}}})
r0 = socket$inet6(0x2, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1006, &(0x7f0000000000), 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000080)=[{0x81}, {0x87}, {0x16}]})
pwrite(r0, &(0x7f0000000100)="5df09d5792f307847f24cd378250", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x84}, {0x25}, {0x6, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x802069b5, &(0x7f0000000300))
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0xcc)
sysctl$kern(&(0x7f0000000200)={0x1, 0x58}, 0x2, 0x0, 0x0, &(0x7f00000002c0), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000400)={0x3, &(0x7f0000000140)=[{0x50}, {0x4}, {0x6, 0x0, 0x0, 0x102}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="e16914f6357e3a00000015000000", 0xe, 0x0)
fchownat(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @remote={0xac, 0x14, 0x0}}, @icmp=@mask_request}}}})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="03000000", 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="9802"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs, 0x10)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x0, 0x3, 0xfffffffd, 0xffffff78, "08040800f60c00e4000100008d1b820900"})
r2 = fcntl$dupfd(r0, 0x0, r0)
write(r2, &(0x7f0000000240)="b08d51c7f609", 0x6)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
mmap(&(0x7f0000fca000/0x1000)=nil, 0x1000, 0x0, 0x2012, r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x81}, {0x44}, {0x6, 0x0, 0x0, 0x100}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
shutdown(r0, 0x2)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffff9c, 0x802c7416, &(0x7f0000000080)={0xffffffb4, 0x0, 0x0, 0x1000003, "6fc60900e4000000001200000000000000001f00"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffff3, 0x0, 0x0, 0x2, "d6e696d83fc4209e414d70000000d0c73d6500"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
syz_emit_ethernet(0xfffffffffffffe98, &(0x7f0000000000)={@local, @empty, [{}], {@ipv6={0x86dd, {0x0, 0x6, "c1b314", 0x1a, 0x0, 0x0, @loopback, @local={0xfe, 0x80, '\x00', 0x0}, {[], @generic="84166107cd1009dea140fa0aab66a8067b901299bb"}}}}})
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = fcntl$dupfd(r0, 0x3, 0xffffffffffffffff)
r2 = fcntl$dupfd(r1, 0x0, r1)
write(r2, &(0x7f0000000100), 0xfffffe5d)
madvise(&(0x7f0000400000/0xc00000)=nil, 0xc00000, 0x6)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000080)={0x0}, 0xfffffdbf, 0x40a)
r2 = socket(0x11, 0x3, 0x0)
dup2(r2, r1)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x2b, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x1000, 0x0)
r0 = open$dir(&(0x7f0000000540)='./file1\x00', 0x10004, 0x0)
readv(r0, &(0x7f0000000280)=[{&(0x7f0000000300)=""/80, 0x50}], 0x1)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x3d}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200000600"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
r2 = dup(r0)
dup2(r2, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="00ff606267f1713048e63f929648000000000000", 0x14)
setuid(0xee01)
r0 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000080)=0x7, 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="8b0229"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x2, 0x2, 0x0)
bind(r1, &(0x7f0000000000)=@un=@abs, 0x10)
r2 = socket(0x2, 0x2, 0x0)
r3 = dup2(r1, r2)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
bind(r0, &(0x7f0000000000)=@un=@abs, 0x10)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
pipe(&(0x7f0000000000)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
r2 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_WRITEREGS(r2, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x8200000]}})
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000640)='./file0\x00', 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f0000000100)={'./file0\x00'})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x4c}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000400)={@random="1093b39c1524", @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @multicast1}, @udp={{0x2, 0x2, 0x8}}}}}})
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x29, 0x65, 0x0, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x310, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1112, 0xffffffffffffffff)
clock_gettime(0x0, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x80}, {0x48}, {0x6, 0x0, 0x0, 0x103}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0x3a, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, 0x0, 0x40, 0x0)
kevent(r1, &(0x7f0000000000), 0x7ff, &(0x7f0000000000)=[{{r0}, 0xfffffffffffffffe, 0xab}], 0x0, 0x0)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000700), 0x0, 0x0)
kevent(r1, &(0x7f0000000040)=[{{r2}, 0xfffffffffffffffe, 0x1}], 0x1, &(0x7f00000001c0), 0x5, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
r1 = dup(r0)
listen(r1, 0x0)
r2 = kqueue()
r3 = fcntl$dupfd(r2, 0x2, 0xffffffffffffffff)
close(r3)
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f00000000c0)=0xfffffffffffffdc0)
r4 = socket(0x2, 0x1, 0x0)
getsockopt$SO_PEERCRED(r1, 0xffff, 0x1022, &(0x7f0000000100), 0xc)
connect$unix(r4, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
r5 = accept$inet(r0, 0x0, 0x0)
sendto$inet(r5, &(0x7f0000000280)="1daa", 0x2, 0x0, 0x0, 0x0)
setsockopt$sock_int(r4, 0xffff, 0x1001, &(0x7f00000001c0), 0x4)
setsockopt$sock_int(r3, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
readv(r3, &(0x7f00000013c0)=[{0x0}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000100)=[{0x4d}, {0x16}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
mkdir(&(0x7f0000000280)='./file1\x00', 0x0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f0000000040)='r\x00')
mkdir(&(0x7f00000000c0)='./file1\x00', 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)="451029c10c4dd3154008e49d783c4316137e6f845071eaa045780ea3c4a088aec3073a3d7a7f019af59ce2b5c6bc366a893cda16edf35753c5f0b7763c25ddef70f7bc41652c20899f1f79f645f5c3b629801e583b3f0b7cb597e23695833146254e32bf0f201bd8982faaca202bf5bb16ca2640d49fdd317368c9cc710ca12d2d7d6cc99af3c9c47f14fe184aa2aa6bda5d8968f1bc18658268260f7e", 0x9d}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "104ce467af60b403184700dd47c8e9d209670991"})
kevent(r0, &(0x7f0000000040), 0xffff, &(0x7f0000000140), 0x7fffffff, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
write(r0, &(0x7f0000000040)="060055418158a5cf479c308294815ff8051bf0e237f78e73", 0x18)
syz_emit_ethernet(0x66, &(0x7f0000000000)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "7a8992", 0x30, 0x0, 0x0, @rand_addr="000000000000000001000000000200", @mcast2, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, '\x00', 0x0, 0x0, 0x0, @loopback, @empty}}}}}}})
r0 = socket$unix(0x1, 0x5, 0x0)
fcntl$lock(r0, 0x7, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x200, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000002000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000003000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000000040), 0xfeea)
mlock(&(0x7f0000001000/0x3000)=nil, 0x3000)
open$dir(&(0x7f0000000140)='./file0\x00', 0x612, 0x0)
lchown(&(0x7f0000000000)='./file0\x00', 0xffffffffffffffff, 0xffffffffffffffff)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000180))
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000001c0)={&(0x7f0000000100)='./file0\x00', 0x1, 0x0})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r2, r1)
sendmsg$unix(r1, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
sendmsg$unix(r1, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
writev(r1, &(0x7f0000000280)=[{0x0}], 0x1)
execve(0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x20}, 0x2, &(0x7f0000000040)="1dd6f9d6", &(0x7f00000000c0)=0x4, &(0x7f0000000100)="bd373540", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x84}, {0xc}, {0x6, 0x0, 0x0, 0x100fd}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x8000000000005200)
r0 = open(&(0x7f0000000200)='./bus\x00', 0x0, 0x0)
r1 = syz_open_pts()
poll(&(0x7f0000000100)=[{r0, 0x1}], 0x1, 0x0)
syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000000)=0x7)
syz_open_pts()
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000d80)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x1}, {0x14}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000340)={@broadcast, @local})
socket(0x1, 0x1, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000580)={0xfffffffe, 0x40, {[0x800000001, 0xf8, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x5, 0x9, 0x0, 0x1], [0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0xffffffffffffffff, 0x7], [0x9, 0x0, 0x7fff], [0x0, 0x1, 0xfffffffffffffffc, 0x17, 0x7f, 0x1], [{0xfffd, 0x7, 0x1}, {0x0, 0x0, 0x89d4}, {0x0, 0x0, 0x1}, {0x0, 0x0, 0x100000, 0xffffffffffff3cd6}, {0x0, 0x0, 0xfffffffc, 0xffffffff}, {0x0, 0x1, 0x0, 0xfffffffffffffff7}, {0x0, 0x1, 0x0, 0x200000}], {0x0, 0x0, 0x1, 0x474}, {0xaf8f, 0xffffffff, 0x0, 0x2}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r1 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000000)={0x1, 0x0, 0x2, 0x0, "106e91b8f920945b0c31658625e15a16f99c7f31", 0xe82})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x5, &(0x7f0000000100)=[{0xb617, 0x5, 0x1, 0x400}, {0x8, 0x3, 0x1d, 0x80000000}, {0x400, 0xa5, 0xb3, 0x101}, {0x49a, 0x22, 0x5, 0x101}, {0x3, 0x5, 0xfb, 0x13a}]})
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCPROMISC(r0, 0x20004269)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000240)=[{0x87}, {0x5}, {0x4406}]})
fcntl$dupfd(0xffffffffffffffff, 0xa, r2)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x80206979, &(0x7f0000000300))
r5 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r5, 0x0, 0x7, &(0x7f0000000100), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
seteuid(0xffffffffffffffff)
setuid(0x0)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x28}, {0x60}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000140)=ANY=[])
sendto(0xffffffffffffffff, &(0x7f0000000000)="01", 0x1, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000000), 0x4)
syz_emit_ethernet(0x4e, &(0x7f00000002c0)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "f800", 0x18, 0x0, 0x0, @loopback={0x5}, @loopback={0xfeffffff00000000}, {[@hopopts={0x2b, 0x0, '\x00', [@ra]}], @udp={{0x1, 0x2, 0x8}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x3d}, {}, {0xffe}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@remote, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "1edcd4", 0x8, 0x0, 0x0, @loopback, @ipv4={'\x00', '\xff\xff', @broadcast}, {[], @udp={{0x0, 0x0, 0x8}}}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080), 0x8)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0xfffff7fd, 0x800090d6, 0x0, "d700060000000000005b000000000010e3e74de4"})
write(r0, &(0x7f0000000040), 0xfffffec2)
syz_open_pts()
syz_open_pts()
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x300, 0xffffffffffffffff)
pledge(&(0x7f0000000080)='\x00', &(0x7f00000000c0)=':\\^@\x00')
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
shmget$private(0x0, 0x2000, 0x120, &(0x7f0000ffd000/0x2000)=nil)
minherit(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x0)
munmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
r0 = semget(0x1, 0x0, 0x8)
msync(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x4)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x20, 0x0)
ioctl$VMM_IOC_INTR(r1, 0x800c5606, &(0x7f0000000040)={0x20, 0x0, 0x5})
semctl$IPC_RMID(r0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0, 0x20}, 0x0, 0x0, 0xffffffffffffffff})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa974031f090000001e328a1811", 0xe}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240)=0x9)
seteuid(0xffffffffffffffff)
mknod(&(0x7f0000000080)='.\x00', 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x10100000000100}]}})
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x60}, {0x1}, {0x6, 0x0, 0x0, 0x40000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000140)="7c8d37853715c9d491f00fb91ca0", 0xe)
select(0x0, 0x0, 0x0, 0x0, &(0x7f00000000c0)={0x0, 0x8000000000000000})
sysctl$net_inet_icmp(&(0x7f0000001240)={0x7}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="eaff125c00000000", 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x48}, {0x2c}, {0x6, 0x0, 0x0, 0x100}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "04b4da58ef1103dffffffdffffffff00"})
pledge(&(0x7f0000000000)='tap', 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8028698c, &(0x7f0000000000))
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x450a)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
poll(&(0x7f0000000440)=[{r0, 0x40}], 0x1, 0x0)
poll(&(0x7f0000000100)=[{}, {r0, 0x1}], 0x2, 0x0)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
syz_emit_ethernet(0x66, &(0x7f0000000540)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "d654f7", 0x30, 0x0, 0x0, @empty, @loopback={0x5}, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, "1aa65d", 0x0, 0x0, 0x0, @mcast2, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
sysctl$net_inet_carp(&(0x7f0000000000)={0x4, 0x2, 0x70, 0x3}, 0x4, &(0x7f0000000380)="e8f965f2", &(0x7f0000000240)=0x4, &(0x7f0000000480), 0x0)
shmat(0x0, &(0x7f0000ff9000/0x4000)=nil, 0x0)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000000)=0x81, 0x4)
getpeername(r0, &(0x7f0000000180)=@in, &(0x7f00000001c0)=0xc)
stat(&(0x7f0000000200)='./file1\x00', &(0x7f0000000240))
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
readv(r2, &(0x7f0000000300)=[{&(0x7f0000000040)=""/228, 0xe4}], 0x1)
linkat(0xffffffffffffffff, &(0x7f0000000100)='./file0\x00', r2, &(0x7f0000000140)='./file0\x00', 0x0)
bind$unix(0xffffffffffffffff, &(0x7f0000000680)=@abs={0x1, 0x0, 0x0}, 0x8)
unveil(&(0x7f0000000480)='./file1\x00', &(0x7f00000004c0)='r\x00')
r3 = accept$unix(r1, &(0x7f0000000340)=@file={0x0, ""/247}, &(0x7f00000002c0)=0xf9)
connect$unix(r3, &(0x7f0000000440)=@file={0x0, './file1\x00'}, 0xa)
shutdown(r0, 0x2)
r4 = accept(r1, &(0x7f0000000500)=@un=@abs, &(0x7f0000000540)=0x8)
shutdown(r4, 0x1)
pwrite(r2, &(0x7f0000000580)="a7135e2d26a1a820642ef323ba24e432dd30bafaf771b5a6f571f4fe1f8f7dbca6188398499d7fdfdd7bdb8b50c979c8f4403f728133d51ec8fd230f2ecdee3dd2bad503f76b81a7e87ef5b3bfb2a201f4edc8a5d9cf8ae10ae32073360bbf5bc09afa9eb6206c52a50d6c64f1158f83008f02f0d358658286f085531c73095e396cc21e64669e5419237d3378246f0d55443f39460a311ef28885a17094ec2374ab802aa78f78ca825f03c3d30ad5df56d58f2936636c80a0c4bbf8d52e5600f0e43143308b05d1f168a3e4d663201f41a8f29640d84e9914be", 0xda, 0x100)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x400000000, 0x100000002})
flock(r0, 0x2)
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300000000})
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{}, 0x0, 0x0, 0x0, 0x100000000000000}], 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x2, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x80206932, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000400)={0x3, &(0x7f0000000100)=[{0x4d}, {0x50}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
sysctl$kern(&(0x7f0000000000)={0x1, 0x33}, 0xc, &(0x7f0000000080)="99511c2b75f3b4c6fd90ff3306e44becd5c438b6618fbeee8bb283297ba656a237983783bb2f59ab6e3c07f6f943518078bec397ddc86fea16f84c76be11455efdbd8afc5920008df505e5bf079b75c09e35899dc239254aa4e2c9481490e49091fdeccc5d68e7f1f84d1f57a223eeace476eff75ad5fc7dd5a956e8fd49ae5fe283cfad7a4085f46fe664af7f91e9f76dd39b858ff5eb226cece107f54c9bff3674959795b2f8b4f05347e4a28e140d257a426d52727c794474a7dd238febc2f1d254391d08", 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x6)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x0, &(0x7f0000000140)})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x24}, {0x7}, {0x6, 0x0, 0x0, 0x200003fd}]})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r1, &(0x7f0000000200)="34cf362b3c3dfd6039bc17b23704", 0xe)
fcntl$dupfd(0xffffffffffffffff, 0xa, r1)
r2 = socket$inet6(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
r4 = socket(0x18, 0x1, 0x0)
setsockopt(r4, 0x29, 0x33, &(0x7f0000000000), 0x0)
bind$inet6(r4, &(0x7f0000000000)={0x18, 0x1, 0x2, 0x4}, 0xc)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x100f, &(0x7f0000000040)=0x8, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0xa, r5)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x3e, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x2, 0x0)
ioctl$TIOCFLUSH(r0, 0x8020697a, &(0x7f00000001c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000240)=0x8)
poll(&(0x7f0000000180)=[{r0, 0x1}], 0x1, 0x1000)
syz_emit_ethernet(0x28, &(0x7f0000000280)=ANY=[])
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20404000, 0x200000]}})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
writev(r1, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
r2 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
rename(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
mmap(&(0x7f0000000000/0xc00000)=nil, 0xc00000, 0x0, 0x10, r2, 0x0)
ioctl$WSMOUSEIO_GCALIBCOORDS(r2, 0x41205725, &(0x7f00000001c0)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x72)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
mmap(&(0x7f00005a5000/0x2000)=nil, 0x2000, 0x4, 0x11, 0xffffffffffffffff, 0x1)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
dup2(r1, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r2)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r5=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r5)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
setreuid(0xee00, 0x0)
r6 = getuid()
seteuid(r6)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000540), 0x1, 0x0)
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000580)={0xfffffc00})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000040)=0x208)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{}, {}, {0x6, 0x0, 0x0, 0xcc}]})
pwrite(r0, &(0x7f0000000200)="b269f66b39b9de6923518e4027fe", 0xe, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = kqueue()
kevent(r0, &(0x7f0000000080), 0x341, &(0x7f0000000280), 0xa55c, &(0x7f0000000140)={0x0, 0x4})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x6}, 0x2, &(0x7f0000000180)="7b7eaed9", &(0x7f0000000040)=0x4, 0xffffffffffffffff, 0x4)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffd44)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x1, 0x1, 0x0)
close(r1)
socket(0x18, 0x400000002, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
getsockopt(r0, 0x200000029, 0x2c, 0x0, 0x0)
pwritev(0xffffffffffffff9c, &(0x7f0000000180)=[{&(0x7f0000000800)="255b8c6c18732104bf9597b782e6c703de291df953b7fc6edba6e44f9f44cfcc95757184c569bf24fce856cf083b84c85b863a5c52201c874ea68e1aee6d14830a696cba414c0b13a15298ce1267bb40a2e9efade3228742e5ed35f4eb487563df034adb72bd5ff498c0a2d84f39e9bd2ca6e75082c0cb5acc2884cd54b6efdf7b0a3babe1bc85220dd4cf6617785d810bc67b9a01a4813c6dbdc9a9c90d79c77f20323d281a8e8cfdb1ff7d36a71369baf0f2fa93b888bbbae5efbfa7b0e97cea60af83abae4c38e929c1e4b4fdb94d86b5fda6c280932a54555d2ecc482f20fa504d38301e2b0d4ab8f0c9d840eff2c31ab86c059ebed81a6c9a19ae2c9d87241b734ce675a6793293fdc77a4b8d527212cfd981688b8d11b97d0acb4348e29806003cf1d34417075da6f682189351c6981cf53b1f8879b46a9fbe112226fe107316e9fead56f1327f5f275732006170b5c8f6837bd1d4d4b03b5c9591a6d47dcfcf5b4c8f9044f88052e1801559acfd90602c93498315c402a0c9a033c4ab18b2feed13cf774fbff57f6ca029b8b4f9034d5ba8153d3d7a22a20e20a598e6ebd8885d752ea1f6a2a9f57987a6d66275512d04b0aea3b8e806354e7b967ddff6c6f285eca6b5dd864aa20505dad5f4797858fb2ba63bf19247aec41f52", 0x1de}], 0x1, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg(r0, &(0x7f0000000900)={0x0, 0x0, 0x0, 0x0, &(0x7f00000007c0)=ANY=[@ANYBLOB="04010000ffff000001"], 0x104}, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000140)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "8a2d2a", 0x8, 0x0, 0x0, @loopback, @ipv4, {[], @udp={{0x1, 0x3, 0x8}}}}}}})
pipe(&(0x7f00000001c0)={<r0=>0xffffffffffffffff})
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f00000000c0))
seteuid(0xffffffffffffffff)
setgid(0xffffffffffffffff)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {}, {0x0, 0x4}, {0x0, 0x0, 0x0, 0x1}]}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
truncate(&(0x7f0000000000)='.\x00', 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000000)=0x2f)
r0 = socket(0x18, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r1 = fcntl$dupfd(r0, 0x0, r0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x3f, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x0}}, 0xaa)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r0}, 0xffffffffffffffff, 0xffffffffffffffff}], 0x0, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
close(r1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0df4ab", 0xc2}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
socketpair(0x21, 0x0, 0x1a, 0x0)
sysctl$hw(&(0x7f0000000080)={0x6, 0xe}, 0x2, &(0x7f00000000c0), 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
accept$unix(r0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000003c0)=[{0x3}, {0x1}, {0x6}]})
pwrite(r0, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
write(0xffffffffffffffff, &(0x7f0000000240)="54a43e5f2e", 0x5)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443a, &(0x7f0000000240))
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f00000002c0)=[{&(0x7f0000000280)='#!', 0x2}, {&(0x7f00000000c0)="57dc10a770d9e890cdcccad84e73728726dd6632032e7dba9ce94b6fbd107ec5f1c367a2e89cc4c6fbfd0cd0a83309", 0x2f}], 0x2)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000040)="000800f3d2d92af4ed5279a6ff7204450a", 0x11)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
poll(&(0x7f0000000000)=[{r1}], 0x1, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
mknod(&(0x7f0000000040)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000080)='./file0\x00', 0x2, 0x0)
select(0x40, &(0x7f00000002c0)={0xffffffff}, 0x0, 0x0, 0x0)
select(0x40, &(0x7f0000000140)={0x58}, 0x0, 0x0, 0x0)
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000180)="14", 0x1}], 0x1)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
pwritev(r0, &(0x7f00000002c0)=[{&(0x7f0000000100)="f1eec914ad976b43f4b0de62e1fd966c619e584444049e7a5433a30e87691f5a0f21889bc2fe8cef16493fdd2825864c43695f1304fb3ad9f73d1df9580f2b04e81a72094a9ba8d50b5b1262adb026cab7acbea91b81b37969be3d3ca1dcb8795ff3f885c5f381e8", 0x68}, {&(0x7f0000000180)="cb7e305defc6eaf52b9d577035455129a5f42f03e8a36a83ff6d13d452e54b93e614fa3e33416fd6d063a552a040dd9dc4a0c10f8a9710064d44104c4f7cf2473c3ac7c0b9102d79e14b0d4eceafb261d6d8bec2499f842281dd2346f9a19ec73ec50393db1b1348a4a7ab94170e30324e56f8db8796a196bc7309ab98b5983a10d508910dd335a9b1a710f681980b7560a3381a7f77d2c477b557dfdb1e8a4576071d1b0c32bc2f6f7387b1995f4600ad3a45c672e99bf17ed9c99020742ade7e0cce620f5b18c1df918ead7d17609d386b98de68da841150e2caa7d832a3bfcd2754c9447e93", 0xe7}], 0x2, 0x9)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r0, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2c)
mknodat(r2, &(0x7f0000000300)='./file0\x00', 0x8000, 0x2)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, &(0x7f0000000280))
madvise(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4)
munmap(&(0x7f0000000000/0x4000)=nil, 0x4000)
socket(0x800000018, 0x2, 0x0)
socket(0x18, 0x1, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = getpgrp()
setreuid(0xee00, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r2})
r3 = socket$unix(0x1, 0x1, 0x0)
r4 = getuid()
setreuid(0x0, r4)
ioctl$TIOCSPGRP(r3, 0x80047476, &(0x7f0000000040))
r5 = fcntl$getown(r3, 0x5)
fcntl$setown(r0, 0x6, r5)
setreuid(0xee00, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0xad)
write(r1, &(0x7f0000000080)="e5", 0x1)
utimes(0x0, 0xffffffffffffffff)
shmat(0x0, &(0x7f0000fff000/0x1000)=nil, 0x0)
shmat(0x0, &(0x7f0000ffa000/0x3000)=nil, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000100)=[{0x60}, {0x4d}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000180)="201d8eb323295428e2fa906bdf4b", 0xe)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r1}, 0xc)
setreuid(0xee00, 0x0)
r2 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
fchmod(r2, 0x0)
r3 = getuid()
r4 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r5 = getuid()
fchown(r4, r5, 0x0)
setreuid(0xee00, r3)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
rmdir(&(0x7f0000000000)='./file1\x00')
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1380000000000, 0xffffffffffffffff})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000480)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000000))
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x4000000}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0xfffffffffffffffd)
sysctl$hw(&(0x7f0000000040)={0x6, 0x2}, 0x2, &(0x7f00000001c0)="ff9e219a32c712697e956a95d04fe3c7254188b8e605fd6a2db5fad2bacb1b768edb5f0ec9e85f8ede66fbda0fefb3759fa510ee80a590385435ca9cce42959ccbee70a6144d087242b6bfe452280e8f8cc8aaddce168e5a66f4e82df82c29343769dd159282f9d37f5f91c3844ad223e22c7f87fdb2a99520669d06875c9c3bcd645fc47bfb41980589f2f8fdf7a70dcc60a7c83203bf9bb72ca7f1586493", &(0x7f0000000280)=0x9f, &(0x7f00000002c0)="2ce1565b8498f04bfe800c9ad6e5dccf07049fad1c68e46cb1d29028ba8135a4d4858129f4ef512a1c301cdccbf460f75ae5e238698530ab0069e439aad7a5423d9833c0563e94e3959a4ec2f79616f1a9c8063b1ff86c240889cda3db1a16aafb669981770c965f233b06e312fb44d2a5a6eb0cb0002b18611c216bb0c477ed1d4d67acc49f3b102c0aa4ee57eef515f4cbdf52273f807f42f42df04a1d42415458d5d4f99dc68fe81df49048df42988edc418b2065b91f0f72395d8b48976a1850880cb3abc8fb0996e74ce3f525195f2042efbc533f60d23d4d092b10f8318e305c5795", 0xe5)
sysctl$hw(&(0x7f00000003c0)={0x6, 0xe}, 0x2, &(0x7f0000000400)="0160b2b56db50f5c746afb9f3d45a01d76ced3b35a65253761f585643843913bb14da29c8a94641d03e66135f1acae9465fcbafbab147df1fe7dd6", &(0x7f0000000440)=0x3b, &(0x7f00000005c0)="6659d205303b4968186bf84be6a24d5acabcf52c76816efaad17b0324f22a5c5a1c3bd023b9929c675b9017c4049ec9f7411d08710290d383b39c5b7840ee27bab2dc450c7f9ddc839ec7896759c182fe7cf7dc35f845c5a49a9e0f3bc7d5c2aa8e4690b44ac26e1dbdfd20f736dd68dc80bd38ac5ba0f18554cced5e564187910a3d946879a8f51db6be16d2988011c174b03", 0x93)
msgget$private(0x0, 0x2000000186)
r1 = semget$private(0x0, 0x8, 0x10286)
msgget$private(0x0, 0x1)
semop(0x0, &(0x7f00000001c0)=[{0x4, 0x4, 0x1800}, {0x0, 0x6, 0x1000}, {0x2, 0x7ff, 0x3400}, {0x1, 0x40}, {0x0, 0x2003}, {0x4, 0x5}, {0x2, 0x1fb, 0x1000}, {0x3, 0x33, 0x800}, {0x1, 0x4007, 0x800}, {0x3, 0x4, 0x1000}, {0x2, 0x9, 0x1800}], 0xb)
semctl$GETPID(r1, 0x0, 0x4, &(0x7f0000032500)=""/102380)
semop(r1, &(0x7f00000004c0)=[{0x2, 0xff00, 0x1800}, {0x4, 0x8, 0x1800}, {0x1, 0xfc00, 0x1800}, {0x4, 0x0, 0x800}], 0x3f)
semctl$GETPID(r1, 0x1, 0x4, &(0x7f00000008c0)=""/178)
r2 = semget$private(0x0, 0x3, 0x4)
sysctl$hw(&(0x7f0000000480)={0x6, 0xc}, 0x2, &(0x7f0000000680)="1eec6ced10abb19b291f90643780b0884fb4d04a73c5134c070e353e6f61a944664fad3560a0a5a662040a87e74549662a3eb256e69467f8c86eda176e72dce7890ae256f79ae9ad74071694c94206676da481b878baf00dd8962863a22ebf18dee7bc64ebe3befcdbfb21576e39a3c828c3df7113", &(0x7f0000000700)=0x75, &(0x7f0000000800)="dfb8f811f78981bd7cf3d6e52eecb307b969f3628ecef48424d1341121ba617eb592222da6bbfdcea42820eca2517ad49a9909dfd262398368649a9db817c43cdce60d9a396ef4ded0f5b73aa88c1f91e3e1b1d5821d1f416c111de2c5f9b868b6e59c1578c8f242b0ca1a6ed1b02e0a9e6c91d5434276bd", 0x78)
semctl$GETZCNT(r2, 0x2, 0x7, &(0x7f0000000740)=""/159)
msgrcv(r0, &(0x7f0000000500)={0x0, ""/139}, 0x93, 0xd6d0c418f59fe7d3, 0x1000)
msgsnd(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="00a9e57e430000004ca28dc72e914103e270e9161c3aaebb2c4669a42708c6a56c0300c4456c25285728db95b0fbfede7536e4bb422696f6254dd6c59891783b04dad9d11c7132619c16c8eac2e46dea29e7f6a735dc62eeb3bc7ab404ef5ca3a8548f0d9f3ae531b10260d235bb19076071360ea0770b2ba41036cf6196e4d9cf8f76886c7bf1857c6fdeebc34daec2d015f817f0207f28498affff68daead0d928a165b93b1a361892e3f71b51a5ca326e111c927433bf2b4698432ef4f23698f18c75087f791100"/212], 0xa, 0x800)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc088444f, &(0x7f0000000240))
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000535000/0x3000)=nil, 0x3000, 0x0)
chdir(&(0x7f0000739ffe)='..')
mlock(&(0x7f000071d000/0xe000)=nil, 0xe000)
msync(&(0x7f0000755000/0x3000)=nil, 0x3000, 0x6)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, 0x0, 0x10)
socket$unix(0x1, 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r1)
writev(0xffffffffffffffff, &(0x7f0000000300)=[{&(0x7f0000000280)="95508397c9999dd2ab5f3970b87c6b2e6b050d97b0ee0396ed27bc1f17d4b7b83bf7948551a440d5084eac64f3613e7693b3820ac4eea8245c5e735eba89b5fef514345a400a7f61f0aa68a4", 0x4c}], 0x1)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000001c0), 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000006c0), 0x0, 0x0)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000540), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f00000001c0)={&(0x7f0000000100)='./file0\x00', 0x6, 0x0})
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000005c0)={&(0x7f0000000580)='./file0\x00', r0})
dup2(r1, r0)
execve(0x0, 0x0, 0x0)
acct(&(0x7f0000000000)='./file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x61}, {0x4}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
pwrite(r0, &(0x7f00000002c0)="f2d68c842ef478efef73674aedc5", 0xe, 0x0)
poll(0xfffffffffffffffe, 0x0, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r1 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
dup2(r0, r1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="cb5d8bf0b6d8836273667c6836ca900f6f39341c", 0x14}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f00000001c0)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, &(0x7f0000001400), 0x210)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x81a, 0x0, 0xfffffffffffffffc], [], [], [0x4], [{}, {0x200, 0x2}, {0x0, 0x4}, {}, {0x0, 0x528f, 0x0, 0x3}, {0x0, 0x0, 0x7f}, {0x4, 0x0, 0xfffffffc, 0x2000000000004}, {0x0, 0x0, 0x0, 0x7fffffff}], {0xfffd, 0xfffffffc}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
getpeername$unix(0xffffffffffffffff, &(0x7f0000000000)=@file={0x0, ""/22}, &(0x7f0000000040)=0x18)
ioctl$WSDISPLAYIO_GETSCREENTYPE(0xffffffffffffffff, 0xc028575d, &(0x7f0000000080)={0x80000000, 0x20, './file0\x00', 0x4, 0x7fff, 0x0, 0x1ff})
r0 = shmget(0x2, 0x4000, 0x80, &(0x7f0000ff9000/0x4000)=nil)
r1 = shmat(r0, &(0x7f0000ffb000/0x1000)=nil, 0x1000)
r2 = shmget(0x2, 0x4000, 0x20c, &(0x7f0000ffc000/0x4000)=nil)
shmctl$SHM_UNLOCK(r2, 0x4)
shmdt(r1)
shmget(0x2, 0x1000, 0x30, &(0x7f0000fff000/0x1000)=nil)
ioctl$WSKBDIO_SETDEFAULTBELL(0xffffffffffffff9c, 0x80105705, &(0x7f00000000c0)={0xffffffff, 0x3, 0x80000000, 0x6})
shmctl$SHM_LOCK(r2, 0x3)
r3 = dup(0xffffffffffffff9c)
renameat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', r3, &(0x7f0000000140)='./file0\x00')
munlock(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
sendmmsg(r3, &(0x7f0000001980)={&(0x7f0000001940)={&(0x7f0000000180)=@un=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f0000000480)=[{&(0x7f00000001c0)="800d6889fa676ad344b993d5a58c71b3a3963c89af492d7496cd51dac1ea1281de9f022278c412bcb6157aabe86da26696778e714f765fb64812ba7c73e7d6290e958f860e5c41aed19950266c11f7a9ac38d2eb90ce6fd522", 0x59}, {&(0x7f0000000240)="d3d5aa2688b4dc0327b2e0f0eefff75052be886d21b08753440a25ecd383a79ef6484df0a40bf322165c822738391c38d64fc6a401a3e62a701f730f5a630aa88a9c706fcfd3d1e8ec934ea3df0e667be30722df44d4b49c56aa26cd23e8cd08548ec2bb9c6f28755efd4654b291918dd423972ef317b50758327497ea1bcbf74508c5e1ebbe55130a74d322198928c7b1e97758898a97ab088fc9b902e3829180f3b3d160ea8a22f420ad2ae210793a51c9a2f0450388b044392f89c014599aeff688517abbe761dfbac39a", 0xcc}, {&(0x7f0000000340)="3154399184f4d3ac549f7bcbc77c58239deb533e7ead8cecc0a0", 0x1a}, {&(0x7f0000000380)="b7e00ee9fddaa6947dbb017d6844a2f985a48a73dec39994f12e447879393248a98a37d5690d094d84ae5f277f6332ace4e496f2e5040c792df397f2611766880fdd5da96a36ba11d8caef34a8e8b9d41e8d65b7c661383cd2682a3cf600ec0995230e67d82b279309a093316342a5d3dece9afcb21b35799c1dd19bba2a52d7907a5912ea5dac71d2a0907c37f7fe39baf1f18cee1952773259551d151971037861766a463b18", 0xa7}, {&(0x7f0000000440)}], 0x5, &(0x7f0000000500)=[{0x110, 0xffff, 0x3a, "4979cb6ca51c3055e5a431120120cd3cb63d9d68cbe553917ac20c0740ef7f9c5bd6bd56a90dc1a3c20abab8a187ca554ad16502706288479083825225837c8553ea953eb3ad3d8dae86a130dad5e1c7c42fc30bbaa709c09db88b821c083427a2e645fbe5d317e7b3197a300050763c2b2db7f4c2a6aec916fa64f9bf7c8fe8ed87d0d7eb911fe9c25f907e5f876aa3f0bc64e668c1cdae9ac95ad191405fc8a76850fe91ea978a6e6eb4d1c9be0ae475a453ba0b65d60e436fefe23d8352af48e5a3bbe3443394d2d51fedbb3a88562a237116a5e017255b3818505b374e517cc5c3a7d8b91f3772d729c7dbb4be541b1883c53d8829cb66"}, {0x28, 0xffff, 0xffffffff, "408fa3ef7b51c233271e1ccdfd152217cbea543eb3ac"}, {0x78, 0x1, 0x4, "123a06220b4fe12bd71e73b311dd15891ea5acbe8e0100c16669b5139fe470774484a1d1b84b343c9cdc0510ea959ece7c673f71311721248ea642b14f51b82c68d1ecd017f3dc4cbff8792a2a978935145cd8fc0cb4692789a1f1f8138f97af85409204302c"}, {0x1010, 0x1, 0x1f, "9c41e5fd7bd29e061830dd7c053a1d68858fa8b358ab37d5dd84a8e5b94234e41c2ff8e292946253fb13ba1dcdf43eae80987b4459f046d1d75ea70c231f16f0177869c804f1c8b3b649c115d22d0c682603b8e282861548271b6cfa6c8f918d3ec4d88ff02814f190bd465479c62992bfef75ebe32d2e4ab48889f10d99bc136e728a7803de69a1bb3a9f98d04c69a37bcf98c696e44d35ae0a3c5beaf3644bf5f813983dfb7e4f50cc092ef22ffb211e0e35f8483bd98858777e23d65c3b269976b0e450731496257edb573abaffd24de3a3441842f1ca5d7f4fb71d9d80f94caedd1f33cccb5b534fe8814097f7b71ee3b7c455f1f1deecc25e16fff937cdd1168683523baed6546d51d88e1e90450822e216d751b365ca47f4e937ac43dd063e1a2bcf60e05f77c3c9a4ca8c5a1b9735aa29e3e30b9e7e28cbaedf73a207c47afd7404d67f2ebe5e2e7ff8b43fffb37609e7288c5c6ba472aea77e93fbbb03ef5e8973c3e5deef8f6c6a8b56261cfbac79a958ae32f96634110060df9d38d62edf57286680de92992b44d551cc50e6439177333d66bfcbdeabb63eb0638ffeb3e7e821ce339eb174cde333a3139cbecc22db74f2b9d26a694fcf6c7b58c688a11837ff00e6c17e1821d43916ecdf1515c77318779231632294fd6e06a45410009029e5dc1e5b763c324c1d2fd7871c57febf4a0aea3e0f642dfd0b5f3ca376ace4b786bc5aa6f28ca7ce8d447dd89f8961bbc0ef17b4e63f03efa70b85cfb6393a033a66f8f5171a99ac16a07e2fae1d31f7778e585a89a25a53dadfe93218a3ea13506c85fd9792b7625e65b4350bf5a7b5a755d703ea583a9ed2af0edb40997384b9c692749c82efb3e19bfc804aaba2b635ab99c362987425adbf3a434bf48f0c3eba012d0c2052def49e8d1dd1517239486e38573c3aa64e64eb42e4c84f0b9669fbf11d088cf47ee44a198993b7956a6e36165ab91e6f3a72c4315161965c120388e34990b0bec995960e18b6fe8cfc642d4a9a49070a2f6ef52a5a51a08dca5181194d6c03bb6449d7a91f23a8c6270d3212cd65b2981c3f827acc7f30f03cbac95a3b0a1893202d06805ab1aba52df35f44b21e70af7d2ac86a174f4f6eb12ed5853c32c1963eaaa6ddd3b35a7e53452c31567566d1b6499699cade8b3c64c442eaacaa72249bead1960715cf683f05414a71c0f5a8b75c9533854b9278f9f1562c2b703b31c9bd62f416fe22af4bd66d2b5dbc906b2760257902be65d77217160a10872c3d3fb208dbd179f51d73ef4a598056d7cf325296a55198cec6760fc79df6b66ab99f03a3eaf65ad1373322c5679690b7337212df061c79f141092d541506e92688fb9099e02dfeda3fb5c136814a7ea1b11ea832cee4469656b462e40bdd34744cdf813a45109ee318c108b840e42c17b9f915ad67a8d30a90e8f541d1c3413395cbc0521e3899a10b642e739bc4a45ae7ff9b86cd08fccae2a42dd920c3cb4045e89417a7802c17c28629c107e85fc7bf04444ffaeed4438363306488a793a7b2efe5201db622dc4b876fae80f911d76f30fa27750cb4fa87eefb5fe83cfd67c336fbda6ed20fad4d39e8090a136a742bd8d44231ab503926eab1cf634277553c81c7df55271fef5d4e01707094b3e17f171e9c3af37563096edf7448f0130d3a2d9807ccb0463f846ecce6ca884ff0098b4e127f2a8fce56f1fab43a03187f6a150e0e5f51c81ba877b4c617779f5e4c36bbcbdc2c218de67d70c3638d6bd080a6bc3550a036b9cf9e239a3b4717dac3b1cbc41f0e60aa7981bdfad5497020969d442e36fa5f6975ff1f0c9b8be1b16d0890f1a380cef5b40fec12bc2c7562237d95a99e840788c647d2787469cbd56fb3debe72f920bae11408420e73ec1a2b08dcee6f1d979b6b91964231ee592bc3f27260ed9de713180550c9e98b12a5f79d377b8126b859287a4e401207acace5eefe66f1be227d5e7c0e6e463dd500b3d57eb216e405ce8c6c17e325d8a9490b861861aa33b35c532190eb1c1a06d87d69e8b5a350c3dda20faf29e608e6bbc596c3cc8d1743bf3976c4f8113bc481b9c6ed43287d4d3f192d5e2511f69484a6ca3a87111eb69f8ebf8dbed79ea4dc9eff3e969d56223f00d6c8c944b9653a02d33d6186e86ffc2fde65c091f1b2d079c754b6ba79e097de9b4724916500a493a87c4a4bafeba172a353341385b16aacfea2711ea25a0eaa76456d5c3ace2780cbb0648ee51a5d3d292bde55a8de18b5f1ed24ce10dc387beacc49273b8ba51441b543964228288360d267067321eff783459199863ff3774678ec8e8ee5faa4932404847ef09afe56536f04de47f1957905a6e61ed5e503883d1557a5049404f82479a0ea288931bdd09f8ab9805c38f1a73617cc0ffee3c3d011e52ef2760877905dc2584b4acc9e6e9866e370a9f1c071329f9cc490f8227fcf0b7bb0ef598e091fef78b67429cbfcc267884cd0056ea98faf300d14ed8f2ffd528b8023f3bc9b798a12b92cade04a10ba57c7a0689ada0912cd27f46afcc42f48e1e2bb5bc8d9d4e14a483788d8508128a9e56bb9a2b7dc9b13cb9418266a6bacb2e3fda167c97f9e402f408c1a39a9fbc80a4ecd3c000104c49ee9fb60c467266c83a1c981b50c332eba55ba38e5a192975b21425970ec3ef6ac9d27a26d6ef2e8248ff88875e911687cf3026459341804f008783a3dc2898b06b7e2d0cfc8d8cfb3b7a4743778a007d9b6f90ccb4c8843c0309de1e693963885931bc7ff028de4e5434eab5294d3fee02803348cba9df8d4ea1340f212847ab274c51adf819f12905698f02763f0c119230d0690c50ce17c63c5d8401f03f54743e041af229b10d157561ecf53c3628d61357e7b4c185863fd8ddc723eca4ef05c3b5d093018a1128c362b676720558c606a2ff4d66b0c629c82490c01512e896264e7fd6178bc2e8c35f429c8fa52b25953a5e5410a508e81fcdffe66d5c080301654e47d6361020d210b9e87e974c1cdc760215559d83dfd691e37708613c699f1015d7607933cefcabfa3bb9c2a971e407bd680f3a353d18d9bf9948b6b76266948d1b66fe62b71d4299ecba7a3e844e00daf2470b7e769a8929806887c4054a1c555f28713e8d57153257dd6bf6008f4ee2f0575afd4dcc7943ceb1a12f1576e7c201a4882f2797d374476917515b0cb1889e35fe70425cf9d970d81c5a71dac4a1ce76b1175815be952e1092b7f7900b1e039db1b6d0a49fd54e620d3617124058f607b0ace6c751e3a34971699bcfbaaf56f14894a044da85d0604e5e68582eafe00521bfcbc2a8c68fb7a08a3b32e61a965eac48d34fadf83d9f90866a0e983a337cd8b73823e975989798fed2d1061649fd42b016aa8fc14b05511682cc98c906a373c9498c542f219bacf4a4e7141838620ecfef591819b3fcf68beb045aae78333d6a1f438571a98d5bbac0241a3f2cd7e7a35f9add52aa390468ce3a5a475d591f459b1eb5e9fcc3443d2fa8d8667e78ae3352b2ee2406374825c88ab625c21ee9a3ecc643c7e4e549706f4bbc8b1391e398b6bb3baface02982cf7819ae162b3139ea999071537155a7ca0297897a42a2b411362417de8198fbf9a40ffb36d1ec15fbbb69c9127e500fb64a5b0bf2d594ff5e2ac6831beae498a5ed7da2c9582a4dfe2f5740b4dc71d09f38c13af0be1cf7ec1956bac6cf0c14b83f4dd844476bc1ce352c933473b876a9c1e64cfbb903189015eb0d623d769eb288b1a927d40e7b738960f643505e172d35cbe63fb7799d817e2d24443474431af366fa3fc424e7934e9bb4a969282c211552aecf2d3c2589dd437bef05855ecc3562620562e808757044c5c131852145443c6faceadcef1c2cf23ce26dffadcc3fcebfc6f7bc6d3ffead19ddd8d0ca19d0481e250c5416bcc675ed109bacbbd703d8d7e202049aa5f945286a271eadc0d0531bf511a9b7d473c320d9eb455af1d667e6a1fb81300b36688bf53a87a97b3896f05291cf29d2750f388ac3ad7586ae1c6cb08af4bc87219f1dfc233c4fa557761be8c7c87caaf808d828d126e3ab53ea8fa1b1c5750d9abe74cb697703a4b35d5166060e3c765e1a6b0541a9cf88ea1aec54bc65f9efe1f98e46d397125e6d3808aa6068bec05e0f415681bec67f5bd5abfb616735603a34acdbe4ceaf85f11dfd48ec0cf8273146d94449ef822ab94f962ba7e474496352dc0377f4683fd24bbc5f5812feb305419fcb124817fdee1d431310a4267b705c13c89f52a494d057f711c5c79bc658186116c454bbce648eb0516266636a42482f6a6c567cdff132f5bfc9a56435834bef617e8fdcd81b17a065046734c68a5dbff42e2864f3da3ce66f503ee3e2e95a6eb33d505024215e691f4c35bbe0263ce41dc4341cc133292ffe9413ab67b2d14bfe15a60bc7cf058c3719f1a3064b696c497e88f0f632dd64c25475d6ab31b28d06645560b7b4be9423421bf9f7d0273ce955515019e3f61db1e9abb414a67515793b7c4cffcc1ff160d4be66ee279eb41dd8624dff3086d65a1859535eb6e4fb6be80976eec3c95244b4bdcb3d53c48b5d0cb0895411064b5f3eb0748a3460c5198a06a6f0e18b73806dc3967823fb94e02939654db9c44cfa8cbe3594989490583ce26da7fbca87bb46bd5aea521cc1ca3ef085d121135ded8d87f040adbe2724ffbb22a24253486897d22ad47a491d10e69fc521148ac49b41f38361b8b7402df3dbe4a4c29a03de1bbd6ed28003383783f76a206c4f83656352e28266035d8fca701b209a1b0b22033ec9a4bceb4b630cc366dc1adafaade0c1d616c97c33579ec97ac76e298f505384811d03bdb7b78e0dd0a94eb51125f27bee6ef708a20096dad80e2640a6a8b6a622e5b3c47138b57b21a475d7f8b92941595ef6ffc78544cc1da1d9b028881f8d56ee0d2bb054e2ebbe3a4c153f5852e1242947cec9ccf6dc39737f0d5af7b538a06683c2a798b0ac551428fa25e3f91c93f4876552269eefa8b12355cfae61c21e6fc935708516ec686eb5f91373c84ee48c213b610ba9b12b3f10b74ef5c1f1c1539389c19f1957faf4fe0eb2907b969520161a143f68fe8da33e579dd54f686ef3bddc7fef80a860c4fcc9bbd9a5b783c6fae11a0c9c4fc5debf2c90e5dbc6bd34ca857db9e637501c539fcad4d767644cf09c4cb9f2e99b81f3cb400f05259860748960617003cd55044488df8f37776a677260e517de320207a7c45d6bbe6c14abeb266644b39c1b27464f4abc620de5b70fc286eec132e0ba021f02bacc4794c86349360e45c2408f3e828cfdf63888a09a2214aa539362e8d04881644ce9044084a82f2e56f62d798ccf3493794d46db993be3150dac21242a5afa926ffabb833b833a88eceee947f9bfd807b510c544b4a1589e9ddb3c38184dc821937c3850ac886dbc33053c456c068db6368c05b458935b95c058dc70618f1064121eb92bbafa4d68de4c37778b522a9374fcf7985e24bcc1fe406c9f8474f286f0a1d5d3bcbb58fd1210e6f20d1a2e6dcc076ae2e1ba6d7508d9a27f9bf862a6f24bee960dbd2349c80ab00cc85681f298875127b4b7543d64a88fef1822a49cfe1a077d9fb9f4ec81fe9e518b1d37a04e30c218097ae0d66fd42636327852797606bad10262df4a45ad5dcba4a00fd480e311010bbad80f890d207a0e9ee6117f134c466841a0127afce22809f64afd52bbe00ce2854f09f6551c6e202585dac65f011b4cd1f15e36a"}, {0xc0, 0xffff, 0x8001, "0d806968ec8997081df655ea32de0f51994444cc81741676090b945bcc3f72a798f49f5b684499053cbb6569d0fc86bf153f7fad471d6a655882cf806962e8dd1992d119f6f9d4739c3eca1e4c63d18e34ba92a5d0655a3084d416b8cc31134c8650a185121ae3102dc203f1faae4b470c3c5600dc7a1223c73f93c2261f8aa3d1621d77b4917aaada5ea518c452da79437fcae29a549aae1c85c10b514e8979c4634a260bdcae270e90e6d8"}, {0x20, 0xffff, 0x401, "74fb9edc2f7571dbc65b8e5d7a1190bb"}, {0xc0, 0xffff, 0x2, "bdcee1174f2ee2e1d88e98abea74d697e448cf499fc5751f9696767cb3358c9f296117c62bd6ae1e7e8bc1600ddd3b0b291275c14282f9e4083808744e480df1dc8e70e072bfd8fd5ade753b81319c3e7e7bfb0e799f8d4dd925a0d48af5997d63013200118d4f734f3ad78a8f45f1651941bf8ac21a0e7e827a399d1a1b85a861c0ad7694d2f31790d67298aee7171d98456851e937b29d0cd93dd7f99a274981f54969da4beb912ac34d4038"}, {0x40, 0xffff, 0x6, "08617e54e1cf2e7a5f46e0ee2c479c5312ae75924a3526456264d1ab939ce9d68c8fdf698d193e0995"}, {0x70, 0xffff, 0x1, "865ed01f32869f1d8ebc3be53ae41f5dc59050440f62dc395e4697f8da64dacbe190c41b5aa9f746cdd29a7978b2c3780672a68ac39fb7ce2de8fa72407a03a2b8f6136143f29d6cc47beeb1fe467fea38d1873ad2e065feef300c42"}, {0x18, 0xffff, 0x6, "82af9511c960"}], 0x1428}, 0xfffffff8}, 0x10, 0x0)
msgget(0x3, 0x200)
r0 = openat(0xffffffffffffffff, &(0x7f000060cff8)='/', 0x0, 0x0)
lseek(r0, 0x0, 0x2)
pipe(&(0x7f0000000000)={<r1=>0xffffffffffffffff})
r2 = open(&(0x7f0000000040)='./file0\x00', 0x2, 0x4)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x81}, {0x5c}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r3, &(0x7f0000000400)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r4, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r4, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x3, 0x0, 0x4}, {0x5c}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r4, &(0x7f0000000400)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r5, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x81}, {0x5c, 0x0, 0x0, 0xfffffffc}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r5, &(0x7f0000000400)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
r6 = socket$unix(0x1, 0x5, 0x0)
kevent(r1, &(0x7f0000000080)=[{{r2}, 0xfffffffffffffffb, 0x38, 0xfffff, 0x65c, 0xfffffffffffffff2}, {{r3}, 0xfffffffffffffffe, 0x16, 0x80000000, 0x4, 0x8}, {{r0}, 0xfffffffffffffffb, 0x4, 0x1, 0x9, 0xa315}, {{r4}, 0xfffffffffffffffe, 0x4, 0x2, 0x7, 0x1}, {{r0}, 0xfffffffffffffffd, 0xa1, 0x10, 0xbe, 0x80000001}, {{r5}, 0xfffffffffffffffb, 0x99, 0x2, 0x8000000000000001, 0x3be}, {{r6}, 0xfffffffffffffff9, 0x82, 0x40, 0x8, 0x7fff}], 0x0, &(0x7f0000000180), 0x4, &(0x7f00000001c0)={0x7fff, 0x8000000000000001})
getdents(r0, &(0x7f00000020c0)=""/4096, 0x21f)
syz_emit_ethernet(0x52, &(0x7f0000000000)={@random="831ee5407df0", @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x44, 0x64, 0x1, 0x0, 0x93c4ed541a00b09, 0x0, @empty, @multicast1, {[@rr={0x7, 0x3, 0x31}]}}, @icmp=@dest_unreach={0x3, 0x0, 0x0, 0x0, 0x0, 0x0, {0x7, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @empty, {[@ra={0x94, 0x6}]}}, "d2cb5cf5e05fdb31"}}}}})
r0 = syz_open_pts()
r1 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x4}, {r1, 0x4}], 0x2, 0x0)
open$dir(&(0x7f0000000100)='./file0\x00', 0x200, 0x78)
setreuid(0xee00, 0x0)
faccessat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
sysctl$net_inet_etherip(&(0x7f0000000080), 0x8, &(0x7f00000000c0), 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
ioctl$TIOCCBRK(r0, 0x2000747a)
poll(&(0x7f0000000100)=[{r1, 0x80}], 0x1, 0x0)
poll(&(0x7f00000000c0)=[{r0}], 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0xfffffffffffffffd)
chdir(&(0x7f0000000040)='./file0\x00')
mkdir(&(0x7f0000000140)='./file1\x00', 0x192)
rename(&(0x7f00000002c0)='./file1\x00', &(0x7f0000000300)='./file0/file0\x00')
rename(&(0x7f0000000240)='./file0/file0\x00', &(0x7f0000000280)='./file0/../file0\x00')
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xa, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000002c0)=[{0xc}, {0x7}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x13, 0x0, 0x0)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
setsockopt(r0, 0x29, 0x23, 0x0, 0x0)
sysctl$kern(&(0x7f0000000740)={0x1, 0x47}, 0x3, &(0x7f0000000140)="394ecc185a549c8f8d40fb8adbcb6395c3857cbe8ceca45dc01aba79070a4c4f87b3f0fb26c95aff13b11766ea25ca8f96137f7f8d9bbfbc4494387262f505bfabf958ce48386c5e9fba2cd1df5ccd9339d79ad3b044dadff48c2875fc5165423d8292c0c2beee5209b182649ae07e6d69f73d1f0e5c93893373d76b39626f7fb87a28ec39194961d6f5d616e392611a4c26674b32ffddce537619587810f7f706d880a4375747c482f2dbb098aa2f6b0d3a5461f989", 0x0, 0x0, 0x0)
sysctl$net_inet_carp(&(0x7f0000000080)={0x4, 0x2, 0x70, 0x2}, 0x4, 0x0, 0x0, &(0x7f0000000200), 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000), &(0x7f00000000c0)=0xc)
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0x0, 0x0)
r1 = getpid()
ktrace(0x0, 0x0, 0x0, 0x0)
ktrace(0x0, 0x0, 0x0, r1)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
write(0xffffffffffffff9c, &(0x7f0000000200)="91f8326db108", 0x6)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x5c49, &(0x7f0000000340), 0x800, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000040))
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
writev(r1, &(0x7f0000000900)=[{&(0x7f0000000a00)='B', 0x1}], 0x1)
write(r0, &(0x7f0000000340), 0xd4e688a67930cd)
write(r1, &(0x7f0000000040), 0xfeea)
r2 = getpid()
fcntl$setown(r0, 0x6, r2)
close(r0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000280)=[{0x2}, {0x7c}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
sysctl$kern(&(0x7f0000000140)={0x1, 0x55}, 0x4000000000000003, 0x0, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0x7fffffff], [0xfffffffffffffffe], [{0x0, 0x0, 0x0, 0x81}, {}, {}, {0x0, 0xffffffff, 0x0, 0x20000000000}, {0x0, 0xfffffffe}, {}, {}, {0x0, 0x4}]}})
socket(0x2, 0x1, 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
socket(0x10, 0x3, 0x80)
r1 = semget$private(0x0, 0x5, 0x288)
semop(r1, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r1, 0xffffffffffffffff, 0x4)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000040)=[0x9, 0x7723])
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000001c0)={{0x9, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x63, 0x4e}, 0x100, 0x321f, 0x8000})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB="faff2e2f66696c6e312f66696c65300007448410eb628d48246359d2c725d42bf580eb415261b3434def3d5a08d05125db762dfd7ca0645206f0de4bdcbbdc2e4fdc6ca147a636e02218d957e486e144036b0dc8ba198e62200a2d3f99667b98735fee051ecabd8260da9c52139e3214db1f171a8a2873c223b3d725d307fd8d1d4da9f76211fba5610b239d5b75d69c91e368f04308b526e0b0e75038988f8888d9e2c0e78c1b09996b925c8e03601a6afd3328f5c001b27a1d46a46bb75b01e7cb98381b97d568f618fccac2de96938da88679eb6dd1f29f8a446e65f630370000000000"], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e664fea607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb38a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b8b4417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c0700"/4096, 0x1000}], 0x1, &(0x7f0000001400)=ANY=[@ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="20cd940000000000ffff000000000000de4d87900c5e9cf940922414c038bac4502568ad4c17a555904334d03594f001f135abe3347aa3c9aa8b4b114c3313b946832d1af3e6747be3b903000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="0000e370ec71967de93b6151890d0c002000f6b9", @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYBLOB='\x00\x00\x00\x00'], 0x70}, 0x0)
r2 = dup(r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = socket(0x2, 0x1, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x8040691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x1003, &(0x7f0000000000), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)="b1", 0x1)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
recvfrom$unix(r1, &(0x7f0000000100)=""/19, 0x13, 0x802, &(0x7f0000000140)=@file={0x1, './file0\x00'}, 0xa)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/28, 0x1c}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="0100"], 0x10)
r2 = socket(0x2, 0x8002, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
r3 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r3, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r3, 0x0)
write(r2, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x40}, {0x3d}, {0x4000006, 0x0, 0x0, 0x200}]})
write(r0, &(0x7f0000000080)="23295428e2fa906bdf4ba040352d", 0xe)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000300)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x1000000})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0xfffffc8a}], 0x1, 0x0}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020699f, &(0x7f0000000300))
ioctl$TIOCFLUSH(r1, 0x8020699d, &(0x7f0000000000)=0x810cf4)
connect$unix(r1, &(0x7f0000000000)=@abs={0x1, 0x0, 0x2}, 0x8)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
r3 = semget$private(0x0, 0x7, 0x3e0)
semop(r3, &(0x7f0000000080)=[{0x3, 0x0, 0x1000}, {0x1, 0xff, 0xe17b725e3710487a}, {0x2, 0x4, 0x1800}, {0x3, 0xffff, 0x1000}, {0x1, 0x8, 0x1800}, {0x3, 0x1, 0x800}], 0x6)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x191}, 0x20, 0x9, 0x7fff})
semctl$GETNCNT(r3, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETALL(r3, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
semctl$SETALL(r3, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
semctl$SETALL(r3, 0x0, 0x9, &(0x7f0000000380))
semop(r3, &(0x7f0000000040)=[{0x2, 0x3dc}, {0x3, 0x0, 0x2000}], 0x2)
semctl$GETALL(r3, 0x0, 0x6, &(0x7f00000000c0)=""/100)
sysctl$vm(&(0x7f0000000380)={0x3, 0x1}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x2c}, 0x5, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x10, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
getrlimit(0x7, 0x0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0xfffffffe, 0x0, 0x8009, 0xffffff01, "00000400007b0000000000001f001e0000000001"})
write(r0, &(0x7f0000000080)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967000e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713240c00009c691186524124fde594ce0b259413f18d5122785267e77011bdbfcf3c464972f62b758b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98ae901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5ed0d85fc397bb1e7a51af9a9b0eb8230c4f92e2b8523872aebe96544d539e7cb063926132dba7fc0144863d7221440bbf251e0352c53f3e5d018efb45a4bb59ec0d78755f5666dc28", 0xf9)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x2], [], [{}, {}, {0x0, 0xd8}, {}, {}, {}, {0x0, 0x0, 0x0, 0x8}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
sysctl$net_inet_ip(&(0x7f0000000240), 0x8, &(0x7f0000000280)='J!61', &(0x7f00000002c0)=0x4, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80286987, &(0x7f0000000300))
utimensat(0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x6a, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x4d}, {0x61}, {0x6, 0x0, 0x0, 0x7d}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)=[{}, {{}, 0x0, 0x0, 0x0, 0x0, 0x7}], 0x0, 0x0)
kevent(r0, &(0x7f0000000040), 0xffff, &(0x7f0000000140), 0x7fffffff, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x3d}, {0x30}, {0x4000006, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x2}})
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="470264"], 0x10)
socket(0x0, 0x0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
accept$inet(0xffffffffffffffff, 0x0, 0x0)
listen(0xffffffffffffffff, 0x0)
r1 = socket(0x2, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x4)
sysctl$kern(&(0x7f0000000140)={0x1, 0xa}, 0x2, &(0x7f0000000180)="be", &(0x7f0000000200)=0x1, &(0x7f0000000240)="0acac488dc301df8bd22d34735703e32fd8cac61527ffbcf2f12c1ba9f7dd97588d43b3183317d79f078a68c7fb799fbad3f06fd97ad3920957ec77db026358ea5e8d167f13af06ef79a7dc2927c6042aa0fe71abede0017be07128920a3ba059477a1f0c15e7976bc270d20d4fd13f4040e855f1e0f5a8c0efad95f915859286155a7b51432a55006a69e7ebc81c82c17693c84852822eb4b04e724fefe055bec31f84504ae66e4517cc4625030ef72bd8fd398bb7c31a42047fc1e6cf10a0a7dbdd4c5a654e702fa51184e501f6d50e370ae3ea57dc4fdd6a03ba6696e10ea003c182685be7f372d113df5ecc246ca7fd7eb7db8183ca32833678e4f2eef06", 0x100)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0x800000000000c, &(0x7f0000000240), 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0x7fffffff], [0xfffffffffffffffe], [{0x0, 0x0, 0x0, 0x81}, {}, {}, {0x0, 0xffffffff, 0x0, 0x20000000000}, {0x0, 0xfffffffe}, {}, {}, {0x0, 0x4}]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
socket(0x10, 0x3, 0x80)
r1 = semget$private(0x0, 0x5, 0x288)
semop(r1, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r1, 0xffffffffffffffff, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000180)=0xc)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000040)=[0x9, 0x7723])
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f00000001c0)={{0x9, 0x0, 0x0, 0xffffffffffffffff, r2, 0x63, 0x4e}, 0x100, 0x321f, 0x8000})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB="faff2e2f66696c6e312f66696c65300007448410eb628d48246359d2c725d42bf580eb415261b3434def3d5a08d05125db762dfd7ca0645206f0de4bdcbbdc2e4fdc6ca147a636e02218d957e486e144035a0dc8ba198e62200a2d3f99667b98735fee051ecabd8260da9c52139e3214db1f171a8a28d0a48a4a7873c223b3d725d307fd8d1d4da9f76211fba5610b239d5b75d69c91e368f04308b526e0b0e75038988f8888d9e2c0e78c1b09996b925c8e03601a6afd3328f5c001b27a1d46a46bb75b01e7cb98381b97d568f618fccac2de96938da88679eb6dd1f29f8a446e65f63037"], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e664fea607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb38a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b8b4417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c0700"/4096, 0x1000}], 0x1, &(0x7f0000001400)=ANY=[@ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="20cd940000000000ffff000000000000de4d87900c5e9cf940922414c038bac4502568ad4c17a555904334d03594f001f135abe3347aa3c9aa8b4b114c3313b946832d1af3e6747be3b903000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="0000e370ec71967de93b6151890d0c002000f6b9", @ANYRES32=0x0, @ANYRES32, @ANYRES32=r2, @ANYBLOB='\x00\x00\x00\x00'], 0x70}, 0x0)
r3 = dup(r0)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x2, 0x1, 0x0)
r5 = socket(0x2, 0x1, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r4)
ioctl$TIOCFLUSH(r6, 0x8040691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000000)=[{{0xffffffffffffff9c}}, {{0xffffffffffffff9c}}, {{r0}, 0xffffffffffffffff, 0xb1}], 0x0, 0x0, 0x0, &(0x7f00000000c0)={0x0, 0xffffffffffff0ca3})
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0xfffb, &(0x7f0000000280), 0x80000002, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
sysctl$vm(&(0x7f0000000000)={0x5}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
connect(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000380)={0x0, 0x2d, 0xfffff6f9, 0xff7ffffa, "0804000e03090000010400000000000047000001"})
write(r0, &(0x7f0000000040)="4d17fb388d00007c073572222bccadd9cc28abd12122c05b98aa8c3def8deca40ae7010000000000000091e53eb766834891f945d91f8d97d5d45bf0998a390c108f18b878325411bf91a02e155c09", 0x4f)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x74}, {0x1d}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
msgsnd(0x0, &(0x7f0000000480)=ANY=[@ANYBLOB="0000000000000000250cc96b72bd2a4c1733c65206bee968bba4e1644a9d5f221251de593e2334c0aab69e97b163bde38cfcc1769c170dafb860e19d042654aa4829d40b22b9644a0000000000000001fc5d8bc38f6bfbf84b024f4087d47405c5953ff98c2316092a2b7e08314a3fd54284d38ab4a5c2b6e1288a7abe5736d21c0e3b52e734f4dfad75d1d5ad6a6d647013377b36d9581fb244a5532503e88925d48000000000ffbce9b83ec8bcae8fbc6c9015ed96d18a0dc47b26300942948de78a0053abab89f2647b81cad6df2e1a4de38ca19aab0820f607e686104f67aeca093e72f5f43c967f05a410086c030dba59b3f5a9a8d6abc858822482328beca0d6003eddd76eb0198863410ef4767fc743989314605688ca0041b5862893d720dfb6b2f34e01c1d4cda3f220014da8035191456032721ef22d02217717c4da64597d141708092ddeec740cd7ea1e06ad3d9c962f0f49c329d5020000007cbac5e6be71f970555d726f9370e423f1d1bd0cbf48640bab69fefa774ad3229f9dc0ac3e7a3ad2c52cfd3dd9f1dbf53193a4e414510c58f86b060c1049e6c5b8b1415939ea90b7aca166bf4eba92804cb5782bc6392721982ab1106799e30f4c0b43f7f42d686dc19188df7e48b0cfd309254a9f4d3f68a606748d39920ab863b5dfdf5c10d4c40b96184b659bedd599b019f977b1fd12ef9e9efad6ca54410611e89475561d713d215e150eb5b943a1e8f2430a0300f970ad3967118297b187a10fa29855c0182900f52468a49c123ef86d673fd79ab5cd77e86e3b979ff491df8244b0814885a1dbc047fdb3e73fc1c78dd663e10ef2263ffd2559889f7266c08bab87cf604874abeb8abc0918"], 0x103, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x21, &(0x7f0000000040), 0x4)
r1 = msgget$private(0x0, 0x40)
setsockopt$sock_timeval(0xffffffffffffffff, 0xffff, 0x1005, &(0x7f0000000100)={0x2e3e, 0x9}, 0x10)
msgsnd(r1, &(0x7f0000000980)=ANY=[@ANYBLOB="000000000000000055bc33060cbc152723cb53137ca0194bf4d398a0b16674db510b226fa46cc51f283e1bc2f6158637277817c5c75d13e56cf464d0b831c3bc57e96c85b2e53405198b45b156a61021dd54a53d6c9614639cea807bd6d56194b9c285c23b6700005126aaf4e20c3bd28abef39ec7e53130568c17c8859ddec7e1272187faf184fb93187cdbd092a79a10c71e12e2b89be17c6a02820c2e9dc4e96a30f6b6394a9f3bcdbc8f53323d5d050000005eecc7e2e2ba427e9b646ce95a895fdec85ac919f74fb5b3adb2339952c67118089c125db77f69de36f3d05bc4b803cfb14bdf42a79c0b44c6ad895d8d6d110302d3b6013f0b212b83c231241864c65071656b656df445b2c1666a3453689637a367d5aaad835c568f7d0d4af97a82cb2d8feca391f6105f359f44fc036a8f018fe7a44f096a0a573070566880d1989b540cf48571ff02ed11667cb3c46b90430b5d65968073a4943f9f6fa87a16798ec470e28db32173f7b7ec90f1cff0866807114c1124a8fbeec954a9f4f889105862296c6e80545ae74b9011bfe712315553ac648f8b69e3e234158a960ce9c45ff83857041bdb398d3e0f0c771a3a1cb6526ad8ee689ea8e5994a218f9a5901afb228f484762caf8abf4b4d704370e1355fabb0d357524a1835aaae6e9098991d607ab49f9d660da712e97a5a4d751ab01ad097729b87a18c9b1cdd9abed03a12f5fae25711a6874445f9d7c6cec95e99a2ff1225cfa8c30a813fc63df316a046b31c57fc8738e63735e609666590ab197ab620e0c14d85eef67fdcf68026328cfb9e3d5a5ef234513bcb45179b10e70ca85c78657b3ca8cd6216e768"], 0xed, 0x800)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000340), 0x1, 0x0)
r2 = msgget$private(0x0, 0xa0)
msgsnd(r2, &(0x7f0000000c00)=ANY=[@ANYBLOB="843bff6fa757bc51a47748a031c4b055f40c6e5464b181c273c3d3f5cdb435191ef06fc0df06a5f885e9ea5181e6c386e9f43fac4ae1bce6e3ba54d4f1e94be4702826748b3da82d5bebd6eae7ff54cc65a1a149619c89b4b74b3f177dcfb1d362dcd501fc819c2372c9208ced42299578c3974d99bb6639be1b7092428532b4cdd07296c5e407d66ee18a837090039168542dac600331652d807aa700bd65399d6e197fc5805eef7661b1df5f54282d077da010458ae5e10fe3ae180fe783e863bd67019c6c8e80014abb174814998de8512dcdb54b84562ac24c5c489062a665a6076853450e0c05ff3cabf523c142ce0baa83554c1a1c42f00766e43c59b20fa329a34f98fba8d7fcf305798576c3225cc228561c9540ae54faa56438ea75b168755db1bbe1d18be87756c568a1b5b91fc909c5b3c541070c949794b5eed5a3bd247271598f614bd80ff93c2fc3c8d6ef07f50b9a1d4747f82108a0e1671d38b519275cf2dd4cf5ce6938b318e388ace7d6a6b5a87d01a837a7833f9398a6530c3e37179d0a041312515122970a8aa847418b1c759a9951c2636176bd833587168ee71eff337300", @ANYRESOCT], 0x44b, 0x800)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x9e})
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
syz_emit_ethernet(0x3a, &(0x7f00000003c0)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x67, 0x3ff, 0x0, 0x62, 0x0, @empty, @rand_addr=0x1, {[@end]}}, @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5, 0x4}}}}}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000100)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, &(0x7f00000001c0)=0x5)
setreuid(0x0, r3)
open$dir(&(0x7f0000000080)='.\x00', 0x20, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x80000000000000b, &(0x7f0000000040)="90000000", 0x4)
dup2(r1, r0)
setsockopt(r0, 0x80000000000029, 0xc, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {0x0, 0x0, 0x0, 0x100000000}]}})
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r0 = open(&(0x7f0000000340)='./file0\x00', 0x70e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000510, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r1)
r2 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
r3 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
ioctl$VMM_IOC_CREATE(r3, 0xc5005601, &(0x7f0000000380)={0x10, 0x9, 0x4, 0x1, [{&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffb000/0x1000)=nil, 0x10001}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000012000/0x1000)=nil, 0x7}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000009000/0x2000)=nil, 0x7}, {&(0x7f0000007000/0x1000)=nil, &(0x7f000000a000/0x4000)=nil, 0x5201b12e}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000001000/0x1000)=nil, 0x8000}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil, 0x80ffe}, {&(0x7f0000001000/0x1000)=nil, &(0x7f000000e000/0x1000)=nil, 0x9}, {&(0x7f0000007000/0x1000)=nil, &(0x7f0000010000/0x1000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f000000c000/0x2000)=nil, 0xff}, {&(0x7f0000006000/0x2000)=nil, &(0x7f000000f000/0x4000)=nil}, {&(0x7f0000004000/0x4000)=nil, &(0x7f0000010000/0x1000)=nil, 0x1}, {&(0x7f000000f000/0x1000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f000000f000/0x1000)=nil, &(0x7f000000d000/0x3000)=nil, 0xfff}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f000000f000/0x2000)=nil, &(0x7f0000004000/0x2000)=nil, 0x101}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000009000/0x4000)=nil, 0x9}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file'], 0x4})
r4 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r4, &(0x7f00000000c0)='o8%', 0x3, 0x0)
r5 = accept(r0, &(0x7f0000000080)=@in6, &(0x7f00000000c0)=0xc)
socketpair(0x10, 0x0, 0xff, &(0x7f0000000140)={0xffffffffffffffff, <r6=>0xffffffffffffffff})
r7 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r7, &(0x7f00000000c0)='o8%', 0x3, 0x0)
pwrite(0xffffffffffffffff, &(0x7f00000000c0)='o8%', 0x3, 0x0)
kevent(0xffffffffffffff9c, &(0x7f0000000b00)=[{{r5}, 0xfffffffffffffff8, 0x1, 0xf0000000, 0xffffffff, 0x5b3a}, {{r3}, 0xfffffffffffffffb, 0xa, 0x1, 0x20}, {{r2}, 0xffffffffffffffff, 0x0, 0x4, 0x401}, {{r0}, 0xfffffffffffffffe, 0x50, 0x0, 0x101, 0x22f9}], 0x3, &(0x7f0000000880)=[{{r6}, 0xffffffffffffffff, 0x0, 0x40000000, 0x1000, 0x6}, {{r3}, 0xfffffffffffffffc, 0xc84981d56ba07e21}, {{r7}, 0xfffffffffffffffc, 0x91, 0x1, 0x0, 0xc08}, {{}, 0xfffffffffffffffb, 0x23, 0x40, 0x7fff, 0x76}, {{r3}, 0x0, 0x0, 0x40, 0x8000000000000000, 0xc4}, {{}, 0xffffffffffffffff, 0xf6, 0x1, 0x1, 0x9}, {{r0}, 0xfffffffffffffffc, 0x63, 0x40000000, 0x80000000, 0x3}], 0x3, &(0x7f0000000280)={0x0, 0x9})
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
nanosleep(&(0x7f0000000100), 0x0)
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105728, &(0x7f0000000040)={&(0x7f00000000c0)=[{}, {}, {}], 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x10, 0x0, 0x42)
r0 = socket(0x18, 0x3, 0x62)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
r2 = socket$inet6(0x18, 0x8000, 0x3)
recvfrom$inet6(r2, 0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000300)={0x18, 0x1, 0x80, 0x83}, 0xc)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xfffffff6, 0x0, "01000611aef6e4c611d53300"})
setsockopt(r1, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
bind$unix(r2, &(0x7f0000000380)=@abs={0x0, 0x0, 0x1}, 0x8)
r3 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
close(r3)
socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
connect$unix(r3, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
munmap(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1, 0x0, 0xffffffffffffffff})
sysctl$fs(&(0x7f0000000040)={0x4, 0x1}, 0x4000000000000007, &(0x7f0000000300)="a9d96c5b00f99d57e86933eb0c76e94a5324636efdb444b9bf63980696c9a308c6fa00f4a281c62cf9c8730794d29d48bed238c9b7480c0f32cb8bcb27277fb01f6f13e6fe6665fab978f129e0c03546f56bc1c57e194a3ae03835a16fdfaaab777cc3a94dc4103fcacf0f6418828b7b6850026c7b20336533296c71048c735244cb48e5e4bde42315c4a9023519a62b433b8c73bbfaebfe4c2726dfd241089ca53a45bb58b5203f2bcac09148dc6733ed4d9f0379dd6174884ab23d08a304d6f6f0293398d1d45f8671e1d88a757bc2b2a91626792c826a17c122c05508efd3fd6fa689e6135e81fea723048f78a8d8dda8d82fc677775dc08e19813601069af5cc56ff3b6ea6d2d54dcc6c1fb0be53a1f4f586ea617a1670523635a53add15cf7d372555789c887cc643d406b7a85bdba217d22883b99001c749d5c134b2e567e019facf5a42b8750deeb08be4bfd71be2d67a9a34f5fd5f000000", 0x0, 0x0, 0x6935b0920c624b66)
r0 = msgget$private(0x0, 0x5c1)
msgrcv(r0, &(0x7f0000000000)={0x0, ""/9}, 0x11, 0xfad7dc136879c257, 0x1000)
r1 = msgget$private(0x0, 0xdb)
msgrcv(0x0, &(0x7f00000007c0)={0x0, ""/241}, 0xf9, 0x2, 0x1800)
msgsnd(r0, &(0x7f0000000680)={0x0, "e0684177fdb0e1ea3cd52fb708ace567bb8a05ff9a7b53918f5ab1bd886616e9db653c5fd530751e9dd2b0a8e6aeca4760c8d725d849813ed9740e6ad2a185bb520b09c6bcd68bde3c6959e29285eaf36e63a00508cf9c2d159f9445c9073ef22f47f46f2ee1e2baf5065600235ae513c43b51afa79061efbc365e843ce849216607241156407cafad698226c65070b5024f986fff91f29ac995e826ae83f6d0d56a79a9b8de39d7604bc3da8b9b60f6e20938f9c88805b10359bbc821834b5e3a747e90aadb167d6a169019ef6e92596fe404177412a666ab95eee05bcf5e34f50dbf37aadf5867330913a4e4a4b0c8349e373f78c047ae35aadc0d5e84ae0f9dbbcf559efec854290b062da216a222d47889b49b04d2eb67ccfedb9b61e031f525b5"}, 0x12b, 0x800)
madvise(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x6)
fcntl$lock(0xffffffffffffff9c, 0x7, &(0x7f0000000540)={0x3, 0x2, 0x1000, 0x7fffffffffffffff})
fcntl$lock(0xffffffffffffffff, 0x9, &(0x7f00000005c0)={0x3, 0x1, 0x7, 0x3be4})
sendto$inet(0xffffffffffffff9c, &(0x7f0000000080)="75b7186d1ed919b8ca1f2c27b6bce115a40050d1f4a40d4e2239fd65c9472be39c4ba876677cf9d87d414d02fd0e91046fecd6275e1750de4d759868e606fb826bd9cad7fa9428d08ddc3cbae014554c14c94bfa0d84", 0x56, 0x1, &(0x7f0000000240)={0x2, 0x2}, 0xc)
msgrcv(r0, &(0x7f0000000140)=ANY=[@ANYBLOB="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a20000000000000000000000000000000000000400000000000000000800000000625c0000000000000000000000000000006886de937cc548c30000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000d96e01dac608472e000000000000000000000500"/228], 0xea, 0x2, 0x1000)
msgrcv(r0, &(0x7f0000000580)=ANY=[@ANYRESDEC=r1], 0x25, 0x2, 0x400)
msgget$private(0x0, 0x40a)
accept$inet(0xffffffffffffff9c, &(0x7f0000000640), &(0x7f0000000600)=0xc)
clock_settime(0x100000000000000, &(0x7f0000000080)={0x4000004})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80286987, &(0x7f0000000300))
sysctl$net_mpls(&(0x7f0000000240)={0x4, 0x21, 0x5}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSTOP(r0, 0x2000746f)
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)="1e", 0x1}], 0x1)
mlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
mlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0x0, "0000009e0000000000002200000000000c00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f00000000c0)=0xffffff49, 0x4)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
poll(&(0x7f0000000180)=[{r0, 0x40}, {r0, 0x40}], 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x0, 0xffffffffffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x10000000002, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
close(r0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x3f, &(0x7f0000000100), 0x13c000, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x5)
unveil(&(0x7f0000000200)='./file1\x00', &(0x7f0000000240)='c\x00')
mkdirat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x0)
unveil(&(0x7f00000001c0)='./file1\x00', &(0x7f0000000280)='r\x00')
mknod$loop(&(0x7f0000000000)='./file1\x00', 0x0, 0x1)
getgroups(0x7, &(0x7f00000002c0)=[0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, <r0=>0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff])
setregid(0xffffffffffffffff, r0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
kqueue()
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r2=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
sendmsg(r2, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
syz_emit_ethernet(0x66, &(0x7f00000001c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "36840e", 0x30, 0x0, 0x0, @rand_addr="c37afa6e4cbf4c8f7a63ffbecdc1fdbb", @local={0xfe, 0x80, '\x00', 0x0}, {[@routing={0x29, 0x0, 0x0, 0x81}, @hopopts], @udp={{0x0, 0x3, 0x8}, {"6ecd9f42391ecbf1bba831278008a9d4b131137c0dd952ae"}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x24}, {0x2c}, {0x4406}]})
syz_emit_ethernet(0x46, &(0x7f00000002c0)={@remote, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "000200", 0x10, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@ni}}}}})
sysctl$hw(&(0x7f0000000000)={0x6, 0xd}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9c00ffffff"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000240)={0x0, 0x0, &(0x7f0000001500), 0x2}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x34, 0x0, 0x0, 0x6}, {0x3}, {0x9e}]})
syz_emit_ethernet(0xe, &(0x7f00000001c0)={@local, @random="386f1e06008f"})
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x0)
clock_getres(0x0, &(0x7f0000000000))
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{}, {}, {}]})
poll(&(0x7f0000000000), 0x32, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x8, &(0x7f0000000040)=0x1, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x81}, {0x30}, {0x6, 0x0, 0x0, 0x100000}]})
pwrite(r0, &(0x7f0000000040)='\x00'/14, 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x6, &(0x7f0000000000)=[{0x4, 0x5f, 0xdb, 0x7}, {0x3ee, 0x0, 0x7f}, {0x621, 0x5, 0x28}, {0x0, 0x3, 0x7, 0x10001}, {0x4, 0x36, 0xfb, 0x3}, {0x101, 0xff, 0xff, 0x800}]})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet(0x2, 0x1, 0x0)
getsockopt(r1, 0x6, 0x1, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffff9c, 0x0, r1)
r2 = socket$inet6(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x7fff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r2)
setreuid(0xee00, 0x0)
r6 = semget$private(0x0, 0x4000000009, 0x82)
semctl$IPC_STAT(r6, 0x0, 0x2, &(0x7f0000000140)=""/163)
semctl$SETALL(r6, 0x0, 0x9, &(0x7f0000000100)=[0x6, 0x2000])
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r7=>0x0, <r8=>0x0}, &(0x7f0000000040)=0xc)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000080)={0x0, <r9=>0x0}, &(0x7f00000000c0)=0xc)
semctl$IPC_SET(r6, 0x0, 0x1, &(0x7f0000000100)={{0x4, r7, r8, r9, 0xffffffffffffffff, 0x0, 0xde}, 0x7fffffffffffffff, 0x4, 0x6})
setsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r8}, 0xc)
ioctl$TIOCFLUSH(r5, 0xc1206925, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000180)={0x6, 0x15}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
nanosleep(&(0x7f0000000080)={0x68, 0x7feffffe}, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x34, 0x0, 0xc68, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
kevent(r2, 0x0, 0x0, &(0x7f0000000180), 0x3ff, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x29}, 0x9, &(0x7f0000000040)="8cad3f98bf4a7d37f7ad6e765557f6ee", &(0x7f0000000080)=0x10, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000440)={0x4, 0x18, 0x29, 0x2c}, 0x4, &(0x7f0000000480)="fbe96680b4145b9268c8a64cc0e3a10a25aff883ef9cf528d04402142dde5be8dbc6f9823f5f0d2feaf699387758332a99c1be28d6466ba0927b77555ff12bf1efe3b18c43dc2d1a984a10298cf3ed60a8520c51ddf598ad16f44bcb8f41c502", &(0x7f0000000500)=0x60, &(0x7f0000000540)="8773114f1a7dc23328211c98d0b875ddc921193276b5e6785680de0f5b58fd466f6776f73b83805503538f61300fbc3e06fa25c98c3cd98039a7ea65088dbd93c27baaad4211ecb2d3c388fc13d0b68bafbba551ec3949fa9f19c20ec42b3cfdfa2b5ddece74efbcd100f830a6092c6b1f2d6f60b8389c102fd66b9c8e6db303fd42f3a24ab889f0a7fc055f1f06e71a8a73030145f2781e99c6120088d0f68d9cc350d9bedc78b511af63a9feb6cd86b3ee724989adc924c292fcb8f595e4ae185f55f49771032222", 0xc9)
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
r2 = msgget$private(0x0, 0x200)
msgctl$IPC_STAT(r2, 0x2, &(0x7f0000000100)=""/133)
getuid()
msgrcv(r2, &(0x7f00000001c0)={0x0, ""/46}, 0x36, 0x0, 0x1000)
msgrcv(r2, &(0x7f00000000c0)={0x0, ""/12}, 0x14, 0x3, 0x800)
msgsnd(r2, &(0x7f0000000200)=ANY=[@ANYBLOB="0000000000000000ad41462fafa8ba9416524e0305ffffffffffffffff9b89822df69de2543b464d85cd1546809376ce8f7db5056171ad80b6b4caa048f7ffffffffffffff73136a599973afb37a8f325717c4157a8f35fe45d2f922fc9c3295001e5a24f1a8c56fa3c1ac5a68ec17e34e1c796ca22accfbb00e9dedc4046d07287ccd1842043b5ec8e2c813d653f33ef5297de8c0f8f4429b6e783afd3da6272e161967c2152d2814bc6d195bf2e1d6700d8f8a6fe2bffa37d3f95c9cdd064370bc61b6f83c798b25b20a8fe5f5c77ffc00fe41e798d8db12545a5122bdcff47d3b94ac8da1f32a74f0d8e7035c2eac2dce8e81c070ab059b8be0c00faee3af41840f28303a48e2aba85343375a25a42d0bdd8992df9e960dcb9be45795bd5fb09fd9b88e73ba9a703ac4084dfda6b8b39dbbe320323a5dedf644007e75d21916eb7035b69570b9fe6ea1be1f0dae6e95ef1b818bf6edaaf02ab75269e1b865f47278feed00b654acb13e80ea789bb789783cf83f705cffb2a573d26c04b9d81e403b2c4f62f423d7ce140608a4de6f007720da07e4233f1322703a7a437ca9b933cf4dcab114aaa4793149b1978a34c5b0006a5c531bf2986b80190b6464cf830dad4ee25278a80192cb78d3502f7d135a15bc3c8da804231ff075e15a2f8c574c1ec3508e283e3a568ac6428204a947f930a7d64e6addf761b0cc6a7e57810f87ecedd8684d252a8d58a35b2afb29a12b8df177"], 0x119, 0x800)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{}, {}, {}]})
poll(&(0x7f0000000000), 0x32, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x5}, {0x48}, {0x6506}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@local, @random="9ba8ed10ab90"})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
open$dir(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
sendto$unix(r0, &(0x7f0000000100)="859df96f84091cdf489aef912051e921a074396825af0dd5b8a604db6994a10153e24fedda3bdd1cdb88e59e18089fc7bda4043d62b0a54a0bbcaedb5139d69088e7bc6535a7014acda8e4987bd65c1137ee467229666243d8e7780a0bd6ae0364c695800bc60bcde1a82318efb301bd8ea10cd0a355650400bf004a61c0bd036dba994d7732512beb9663d2142a300c0a", 0x91, 0x0, &(0x7f0000000080)=@abs={0x0, 0x0, 0x0}, 0x8)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x1e, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x202)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000280)={&(0x7f0000000200)='./bus\x00', 0x7, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
ftruncate(r0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
socket(0x18, 0x1, 0x0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x8)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f00000000c0)=0x8)
r2 = socket(0x18, 0x1, 0x0)
dup2(r2, r0)
execve(0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc1126939, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0284459, &(0x7f0000000140)={0x4})
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
sysctl$net_pipex(&(0x7f0000000000)={0x4, 0x22, 0x1}, 0x3, 0x0, 0x0, &(0x7f0000001080), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x2}, {0x84}, {0x4000006, 0x0, 0x0, 0x3ac}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x2}, {0x6, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSKBDIO_SETBACKLIGHT(r0, 0x64460cd, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x81}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
minherit(&(0x7f000081c000/0x1000)=nil, 0x1000, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSWINSZ(r0, 0x80087467, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x800})
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r0=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r0)
getpgid(0xffffffffffffffff)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x200000000, 0x3})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x15}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{}, {0x61}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
r0 = kqueue()
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "c49cd3f227830b53a1ff0000000000000000be00"})
kevent(r0, &(0x7f0000000140)=[{{r1}, 0xffffffffffffffff, 0x67ae675ed1b619b}], 0x10000, 0x0, 0x0, 0x0)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x7ff, 0x577, "8142c156211ac42e8c23c30a2f721b2ca62fba59"})
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000300)=[{&(0x7f0000000040)=""/216, 0xd8}], 0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0))
execve(0x0, 0x0, 0x0)
r0 = open$dir(&(0x7f0000000040)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f0000000080)='./file0\x00', 0x200, 0x0)
faccessat(r0, &(0x7f00000000c0)='./file0\x00', 0x7, 0x0)
setsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, 0xffffffffffffffff}, 0xc)
sysctl$hw(&(0x7f0000000000)={0x6, 0xb}, 0x5, 0x0, 0x0, &(0x7f0000000180), 0x0)
openat(0xffffffffffffffff, &(0x7f0000000140)='./bus/file0\x00', 0x2, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcc)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc9)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
close(r1)
open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000000))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x1000000ff})
sysctl$hw(&(0x7f0000000000)={0x4, 0x18}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x4e)
socket$inet(0x2, 0x0, 0x0)
r0 = socket(0x0, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="00ffff40", 0x4)
r1 = socket$inet(0x2, 0x4000, 0x9)
setsockopt$sock_int(r1, 0xffff, 0x3006, &(0x7f00000002c0)=0xffffffff, 0x4)
r2 = socket(0x18, 0x1, 0x0)
setsockopt(r2, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000000)="00ffff40", 0x4)
openat$zero(0xffffffffffffff9c, &(0x7f0000000100), 0x8, 0x0)
openat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x200, 0x31)
getppid()
r3 = getuid()
setreuid(0xee00, r3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x20}, {0x20}, {0x6, 0x0, 0x0, 0x40000000}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
setuid(0xffffffffffffffff)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x1e, 0x2, 0x1}, 0x4, &(0x7f0000000240), 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = dup2(r1, r0)
poll(&(0x7f0000000180)=[{r2, 0x4}], 0x1, 0x0)
poll(&(0x7f00000000c0)=[{r2, 0x4}], 0x1, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000140)={0x2, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0x240)
r1 = msgget$private(0x0, 0x200)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000100)=""/133)
seteuid(0xffffffffffffffff)
ioctl$TIOCSETD(0xffffffffffffffff, 0x8004741b, &(0x7f0000000080)=0x400)
r2 = getegid()
getgroups(0x6, &(0x7f00000000c0)=[r2, r2, r2, r2, r2, r2])
semget$private(0x0, 0x1, 0x202)
r3 = msgget$private(0x0, 0xa3)
msgctl$IPC_STAT(r3, 0x2, &(0x7f0000000700)=""/137)
msgrcv(r3, &(0x7f00000001c0)=ANY=[@ANYBLOB="0000fc00000000000000000000000000f0ffff00015ea34fe6b3b5d7ce00000000faff81000000000000"], 0x36, 0x0, 0x1000)
msgsnd(r0, &(0x7f00000005c0)={0x0, "ad41462fafa8ba9416524e0305ffffffffffffffff14f3c25ef69de2543b464d85cd1546809376ce8f7db5056171ad80b6b4caa048f7ffffffffffffff73136a599973afb37a8f325717c4157a8f35fe45d2f922fc9c3295001e5a24f1a8c56fa3c1ac5a68ec17e34e1c796ca22accfbb80e9dedc4046d07287ccd1842043b5ec8e2c813d653f33ef5297de8c0f8f4429b6e783afd3da6272e161967c2152d2814bc6d195bf2e1d6700d8f8a6fe2bffa37d3f95c9cdd064370bc61b6f83c798b25b20a8f5122bdcff47d3b94ac8da1f32a74f0d8e7035c2eac2dce8e81c070ab059b8be0c00faee3af41840f28303a48e2aba85343375a25a42d0bdd8992df9e960dcb9be45795bd5fb09fd9b88e73ba9a"}, 0x119, 0x800)
msgsnd(r3, &(0x7f0000000340)=ANY=[@ANYBLOB="0300003f99219bcb1e6e99d4545dc5100ca30000000000b5eee2e755de90a709b25b1107caa9d2294361a983aa724be98b0f5943bf9290bb2100a7acda71acaa313aff25fec1fbef63cd90c480a59671fd02a4194e3ae426e6838ec0501f19d4695622552d4dd48475b4872a8d29ebdd827e8aadf8f9f9d7b0c3ac158d1cdfa7ffb93b0b1a10315f336cc5c531e1ee5e6213f466a2acaa2e2c42dc62527f139eee8f28d65f381fa292cadf090000003ca02d2d2b1b2555c503c549091baaa6a621d8e57e4ecd9a2f0f51f23271cdadc636ad9299d0c84fa9249602676ce35dbe661ed8bd80db1601055db4af294ad46e3411a4db6e6ed2ae5b10996cc1a9aec8451ff65938bd2f7642104e849ce47f8d8b38f5311cf113030ee0c239ee8e848ff999182352456208289be45971c46c049a38ca73467bc38252a26ce266dca2dbb10741d5bc6b25bea637c4"], 0x68, 0x800)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
r1 = open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
poll(&(0x7f00000002c0)=[{r1}], 0x1, 0x0)
close(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000000c0)=[{0x3}, {0x2c}, {0x6, 0x0, 0x0, 0xfffffffe}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
chmod(&(0x7f0000000300)='./file0/file1\x00', 0xd7)
setuid(0xee01)
mkdir(&(0x7f0000000240)='./file0/file0\x00', 0x1fa)
rename(&(0x7f00000002c0)='./file0/file0\x00', &(0x7f0000000100)='./file0/file1\x00')
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000002c0)={0x0, 0x0, {[], [], [], [], [{}, {}, {}, {0x0, 0x0, 0x1}]}})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x00.'], 0x38}, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='$'], 0x38}, 0x0)
recvmsg(0xffffffffffffffff, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/28, 0x1c}, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000040), 0x3, 0x0, 0x0, &(0x7f00000010c0)="b0ab9d9f", 0x4)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
chmod(&(0x7f0000000100)='./file1\x00', 0x13)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f00000000c0)='c\x00')
pipe2(&(0x7f0000002e80)={<r0=>0xffffffffffffffff}, 0x0)
fcntl$getown(r0, 0x5)
syz_emit_ethernet(0x40, &(0x7f0000000040)={@broadcast, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xe, 0x0, @random="562cb24d91a4", "e8c8f788b486fedd348f6cdd2cdf", @remote, "fa07ca7fb447c00dbc7225ee98018a4d"}}}})
r0 = kqueue()
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x1}], 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x5847, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000080)="242ba9f7523b8238859cd48217b992be9a3b93ef0606fc8a2fed37e9fb943a00ff56fe237368c4460ebda4cf", 0x2c)
mkdir(&(0x7f0000000140)='./file0\x00', 0x0)
truncate(&(0x7f0000000180)='./file0\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x3}, {0x61}, {0x6, 0x0, 0x0, 0x1f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff})
setsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000100)={0x0, 0x9312}, 0x8)
mknod(&(0x7f0000000040)='./bus\x00', 0x2040, 0x4f4b)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
ioctl$WSDISPLAYIO_GETSCREENTYPE(r0, 0xc028575d, &(0x7f0000000080)={0x0, 0x0, './bus\x00'})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$WSMUXIO_LIST_DEVICES(r1, 0xc1045763, &(0x7f00000000c0))
getgroups(0x3, &(0x7f0000000040)=[0x0, 0x0, 0xffffffffffffffff])
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket(0x2, 0x2, 0x0)
sendmsg(r0, &(0x7f0000000000)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3, './file0/file0\x00'}, 0x90, 0x0, 0x0, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x3ff})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x4, &(0x7f0000000100)='\x00\x00\x00\x00', 0x4)
write(r1, &(0x7f0000000140)="d4e822d5f9b912d2", 0x8)
r0 = socket(0x2, 0x8003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000001240), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x84}, {0x40}, {0xffe}]})
syz_emit_ethernet(0x36, &(0x7f0000000100)={@local, @random="177c650655e6", [], {@ipv6={0x86dd, {0x0, 0x6, "5a5610", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @loopback={0x5}}}}})
r0 = socket(0x11, 0x3, 0x0)
getsockopt(r0, 0x11, 0x2, 0x0, 0x0)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000500)=[{&(0x7f0000000140)="ad80740490377706ed82a1bba52bb74afafd86958f27c50a5e25b9095e846e0fddc135ada439bedced5e452823ab37661a08b9f504000000926d41943a961bccd9879c98b10718fb2b000000006e1b58944dd16c2df783384b2988bdcaf4d4c25c08fd705875a7a57fc5d21e9f91748a121189c10ae64fbb421e53fc740ba0434504af2a3149018de4f1eea8b1d88978acd0fca90e5dcc44846684d050aa9d3e7ad51949cb8a0611509aff269b9ee4ee78cac32cd8e3ed2a94320008cc0131349e67358267540d051fa1cce9663d6e9745a349fdc929b7db94848351d96c7f15dcfa6e674c0e36a94db8df8fba61dcde98d7d48e7f5cd1aa275f70", 0xfb}], 0x1, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt(r0, 0x6, 0xffffffff, 0x0, 0x0)
socket(0x1, 0x0, 0x0)
r1 = dup(r0)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000080)=0x7, 0x4)
listen(r1, 0x0)
socket(0x0, 0x0, 0x0)
listen(0xffffffffffffffff, 0x0)
poll(0x0, 0x0, 0x0)
shutdown(0xffffffffffffffff, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
shutdown(0xffffffffffffffff, 0x0)
close(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)="919b5c5e5c069d14288be166", 0xb08f, 0x1, 0x0, 0x0)
r3 = accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r3, &(0x7f0000000180)=""/176, 0xb0, 0x0, 0x0, 0x0)
getgroups(0x4, &(0x7f0000000040)=[0x0, 0x0, 0x0, 0xffffffffffffffff])
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x5}, {0x54}, {0x6, 0x0, 0x0, 0x80000003}]})
pwrite(r0, &(0x7f0000000080)="d98d651030b36222441b58ae835d", 0xe, 0x0)
getitimer(0x3, 0x0)
sysctl$ddb(&(0x7f0000000040)={0x9, 0x1}, 0x2, 0x0, 0x0, &(0x7f00000001c0)="bb47ffc0", 0x4)
r0 = kqueue()
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f00000003c0)=[{{r1}, 0xfffffffffffffffe, 0xd3}], 0x5, 0x0)
kevent(r0, &(0x7f0000000100), 0x209, 0x0, 0xffffff81, 0x0)
clock_getres(0x2, &(0x7f0000000000))
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x3f, 0x0, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
getsockopt(r0, 0x6, 0x4, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x10, 0x0, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x1, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205602, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, 0x0})
utimensat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x3d}, {0x80}, {0x6, 0x0, 0x0, 0x8000000000100}]})
pwrite(r0, &(0x7f0000000180)="6e0ae9f9b6c58d06ce93b68f2da7", 0xe, 0x0)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x65, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc110445e, &(0x7f0000000240))
r0 = socket(0x1e, 0x3, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000000)={0x0, 0x4}, 0x10)
getsockopt$sock_cred(r0, 0xffff, 0x1005, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x5c}, {0x24}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x84}, {0x34, 0x0, 0x0, 0x3}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000003c0)=[{0x3}, {0x54}, {0x4000006, 0x0, 0x0, 0x10000402}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000780)=[{&(0x7f0000000500)="dcd0700650c98e21a132bd63ddae2c5181c3b4ec2e87441c48a6b932c80a3bf03dacf1cc134222bc5caf6c8faffed8e720f0fa55d1ed82c49e822191b5d727f1339d0e97032b6248fc9812ac7a826c90d96ba2e63cae5e19b456e2cbc37575", 0x5f}], 0x1)
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000600)={0x10000436, 0x0, 0x1000009, 0x0, "0d43d3d3d33f0800010000000000000c00"})
writev(r0, &(0x7f00000016c0)=[{&(0x7f0000000080)="67c9eb4f7d20a4923ec63e566b174fd549d4276d9fc7bcd8f7fbc9b87d7f8f8c24172163926b13ef6ee325fd47b2a9a3a738ba053bd98b15a73295140ec46cdada07604d212d90dd4cc6facd300faaa591efd7ad08fd7e36281a1446e7d10494f6c28ff20805fb17d7e3110d41c3d5465a1553c0c736d8fae78aa7e9fcbff564fca37fd87e02518f9973e934bbeba3bac6149a6c124b669ccd1164f482fb25a0ffeb28d910010582e5dbac184043770f961bb0860e1a0ef0cb10fc50f9e92aff593731859c27d1522ea3f307a7ac108903c1e0e96bf4e70d63e241d58082fedc96adb8411cd1ec4419110cc93c2babe77bd70960332a0e4eec2b332176", 0xfd}, {&(0x7f0000000180)="6ef201b1ae5dc4be33acf3e4c2000fd8efebf8c901cf7b450ce777", 0x1b}, {&(0x7f0000000400)="41ed4194ba439399c42de825cbe201b955f202d2bbdd56d1430e1f1ee3696327a9a0a841a18f970977e6f27e7448d12089fd74a807c70cf3372a47a0ff634708140faf5e33c889c69c64f82c2e84bd719f01218ec1", 0x55}, {&(0x7f0000000240)="ffd1491cd46c0816625adf33e86a66587ee6c575a0c7cbc1c373add1c241803446269e81a91b15f767259e82316d45a83532e70a0ab114", 0x37}, {&(0x7f0000000280)="fbcb44674772e426e3390476616cc5fbeb5a810282ad21399af52ac61b475904362bfb642c7000c458d8c7f11acfb9f5b781416b0d8312eeede2e2f7ffc878b9c2b425a1e851fc1ca337eac8e20cf05832ecee7aafd74081afdbbf580c5f3280972c732b754f7fbf1c0c4c7f81d5b8e88968449fad29f929e509fc572c6e54bf33f467267796e91ee90b0409f2563f10bc6a82c01fc1a6bb751610f089254b0c5b5e6b3a4eb6c0da122f14a6918f914de47ae062ea4f", 0xb6}, {&(0x7f0000000340)="8d808b1df0c760f884e7b08675", 0xd}, {&(0x7f0000000480)="4616f6f0fec872bfb0c24a926e30001819b556a580256403c8c29d6f8df96dca0b080b582e55edf4607c1a5a913346dcb8000c755dadc4fe61d35f14faa7ae5498bc55167339cd358393faa7f7fc23a8cd85b1c6dd1226d5008c3eb663601125320f69e2f7183143eef3fc94b3ec9af01f3668a27ae206a547811b40607b1016f6777fda0cea6e269016f1dccced318db50ce0302f35a99ee84f33da782bf46a8d03e58903f851d83b6e0539d3", 0xad}, {&(0x7f0000000540)="42bbd705fd556d8e8879893be1f3fb89ef89c2008292fe92d7a4211dfb3f34d08d8f640ddcbf2af95463f6925d688fa4b2bc8594f29f1a025919aab083ffaa8b80f156faffe9611443e20a79f4200d4635d1eb13b49c0d10f2223449a6db240ccdd0146a99058f7fb3e7ed92326e9448952ec406a6a1b325f22ffa9d40d6164236131e6db20d88d55fc7d5984057a9e7a383c2d0e90a7e873fe208a63a99295770adb4bd3c12bcffa2c93c30d9de062e5ae2", 0xb2}, {&(0x7f00000001c0)="d9dd6a3c47ff22dd469a159c937c2fdee373b24571a11ece4ee2c5b73a7ec2a7b10e010a74f21c05b854d708104639d0c36caf929e9a04cbb9d01384b5d558e9f2e9cf40d0a12ff71b62df336f0fd6e136e54053eb4645ba838e440b4e1b0a859481e0b9fec909fbfe0047b252e481a482c0293285ce1b47e1493027", 0x7c}, {&(0x7f0000001780)="cd0fb3911fcbfadfb1477394ada412c5a5ae099cc379db516bf916bb7b0917a2065a47cd4e9299cb54064f22edc7617c619816ed73dc718199b468a498ebc0dbcb918a7730395dde1929d90fdfe88ba9dc156109a3ba8e2d66ca9db2a62f3f4cf1707f27d1ba9c113c2364d1310ff6bd4d62f329d6950a6f827546706d669eb385aed16cc2ed5baa61d9f620e3b0879624e1dca62cdab68c1c4a11a51ea7ebaa84cbd7d32f5fcb3252142a9bc5c3b0db6f6caf6fd46988edc3b30dec945d27ba739df8f0438c459100ee629e98367bb36b10c7ea5b7e870490084195ef9c5207ef1379d333507ffa325573ad7b73f196ec8b39c962d53e2a840c14f7bf884b6a6556328b5924619a09786efe3d08ea2dee76ae9ce053a5720aa6b10097932d16bdf687450bc8b2ae04f0ac5877530e14f873e9d047d8ce7abe4f8f3fabb0c7e30e602bee9294fa6dc0ce84f633fcf99d9bf58bdedb26ccbb46a9ce8ccc647c7e48f717bb234e6a122d11551da3fd293f365384c30adb3390324a6a99a31d23ab5a5b73e54b0adfb14aa781144082d037b9152d8553af7f8e19f5228c4ea7b62e37d707cfd52777f763328c48b83d149eb80da0379a04f80b89fbfbce1f88939ae6eef4ef51dd10c387ed0b7d9a481a63cc2f9d9bb97fbd1e70071cbc80af34f88c487a04315aa1644b2b1edd286bcc30ea62c6bf44d46e4e48141a2be8f9359947fb8c443d55f144e2d8e41d0805c21b2ed206c429f3282c54d98bf3b31361d41fd845ee6c81839c8e6d85e570a7690f4da236da30520e6d319235b7a7925d94a87a54a3ce10e1c978bf9798b0092bb0f00aa35d7360af424e0afeacb131b5c6ad4f87abaad5057d02ba3d7c31df29212d5294da34fe9fd60c43ccd027ba9f30997e20d355a3a78896816c5860049984437049e6c2bd8c7865b34b19ff015d9cc8438d5ebfb18d99b8f3050174da51934398e0aa8a6455ddc200e43377f88a060e383572f13715352feecc2ddd1645e1fcda17ca845b4c9238cd5e78e338fca0129e9b5f88152a1ef86727ff6d084559d968eea7dee8e7bdae5dd03c84141650fcc235f51f1007c16273b0e0ee99d5a506f76a61aec543b5f7efc4787a2fc035b927c24a516e85291bb67824292fc6cce4e6c2a16cf38614a5cf027582c3ae5822c7766a2ab04c40c3a53fb02b99284a1fe243b7780d3a66d68db65620fc7491062395fc81d5fbb8babd093cb59c5ea43d5beb68fe4f471024b4bbd50f5ff6e718b498a0a3e7aca3c7b89c432436cf98a4834f67ab231e65d726c1180828e8a8e46e5fdc7abc2a4d46eaaa7f16f99ff991542268848b6cd442867fd265f159998b5b5403f8f57682ee171e8831a137c05993fb3f4e25c7487f9a88704a00c19f3d6fc063d5640c973ab9e19efbb46fb5b64023ef0e08a443141af376a724367c30e038c4ce55df4af03ba5897a99b3cf5d9cfad0f9181b90bc5e41cf688233bc93fae6bc3ebe88caa3026c5ec195938c6557c27bc90f8df6303773f8d35e2752145feaf4fc0d58d240c4ac2be04ef499bae887bb3f99ff9555ddd028aa60327045ac26800c6a511d3ebaaabe5ce317a3630de8bff0aa014f9246b0bd4f16717e7c834a58ea82ecd57da9992b7c796e43894f8ec9f2b154dd36fc7043d681ca7712f8261d788ded97256fbe712233d8d8b288cbd33c2afe795745975bb1cbbf4c4818ffcc3e89096f8e7f02b1b18f1386ec1f36880297cb82c6bbe84543d1d2d13f7a6651e3f5ad5ec9ecf2e974e48504108655822061dc724b85d3e1a0585038f9449ee3a19ca25c371a1c6751f9b87a15118bc2282433a14bd46f1d8204a0c207920feb070f4d4b7e898c1fe5ae632f96aac56b188b51c197017b5097bd36a52a00b64b91f32e865d5837e10119b700bb0307c9dce3cb062f10013040f79ff05bf0dea6cb465a610805bebfc17f063867b00ed4454f1f490d033325ee1c18d1ca25573a2e75d31c060a6bd9ec15e255653f24b64b709792a5801f1cef2bd1900b00b9b97a3ef2188375ac9bddfecbea5a55af9e2bb3f3c325dbc6de6d96c4dffc48854da838e2b93660824cf8106ffda83380fe1c859a2d234a551210a7762b3b84bfd96fae3b933a63fe0c7b126976548ee11275015a85848a04fb115c8c6a726f62d03f4bae235cf505e9da3f52e18c1bac226f0d71e4e3b6ec8258ab9343d5691583b89fa622b96a5da0bfb9c5f95503d25d11cf57cde507f9d620a64ea19f560e9acf25238b7a79a076040673ece54ef0c554353742a151cfdf053795e8e1f4c8d51fbb2ebb42d59a7bcaf3c6fc8d56c1d7bee7d717c049d42741f43b99334e6c4d93ce19c24b7a62f4ba82a87d288da12149bc6b03547a033f371368f7b5503320c4a342d75651abff6ae3c48c7992bbf95379d347f5539e56fc0f6f119681d5bc087dd0327ed09a22c1853928ede40775f7a9877500e61dd162698eec80f28b6a80095b18d7bd76d18b7eeb007bc94eb18ac48c718faaa7d58e468a7b6e7aac5aa0629880f442fc262eea353ff23e439ffa3ec40569f451b6b59a8c9afa8be7ebac787d0064e6689e116b317f620bf2181b83216ddda2afa071918b0474bc54b552bf2d21b9d8f06dbba5aa5f27f5d92efcac976e104d5b40cf543e6c3083319162f6926e5cbeb921d8beb1abfa6cb6f0f9525d7990b2f31c7f6ab5cd8f9c42c8e70b3255a04c69bad5a524d144f8ba84d8e48fb1c3c7caf9b2f127d37edfd833157742c95c8c5b0458ef1323fbbc190f048faa72e85782ecf70ad4207d5738863faddbdb4db4125fd60248d7c1a4561b0c28c6dd35b1f5893fda57044e451e36eacac68d7f6cc009e745bff32ba4a8d7db189b0098f6d965fd5d0ff7003483c8f07cdc0fe73c4684d45930e4d01e19d70d9326be5bbb216abdf3c93288778bebd7ddf8fa66eb404ac6249734ce7b8a037046a6a04ab9f9784588976c0422e461c657165cd0dcaf2bb3da07df9356cfc9b7ad44b1acbed301ca48101dcf8ac51879aad0f464163736802135cb88dda6a09fd3db7625484012cc6b01861410ef6f8fa07516e4ed52b2d645640ad061f953983fd8175c992389602446c328529289a9fc6f0a2b633ece0025dd0d226ea42d694cb9e41b2b6b613ceed7f518c39238cb324aed4782d34cdfbd0761773e2ff76d163b6007fd06200c3b3cf5bacbc49b2522db5454129b7b252df1e9ea26ea125898a6b00be8587f91ef6668f256ce1a0783abf81c503c74435ec56d7b1eeff25244f64449c2ea8989bb2ed641c4c4d26385934294436ac666a6da2254dec5a58ef6dde3f0f71cb71690ed82945e52f8e183bda3b35411172659e60a0ccc5e7e5ec08b85f73188123652d46f2653c8a60777b13d723d56765a40b06f2b502d39f4a9e502eb0087025c1229bf573eb039e8b6ccecd00cbc30576fc852191cd3405d8de7527efdeb649207280bf2b10e52b9938ec444614ad9c724174b12c56192f04d07de2f0b846eb901818f36416a805886a40d223f920e9153ff0623fbbb16a2d44c441fee99d261ceb48c8a28acda9f3b9f53b7958b96fdb90717029f6c65e78fdcb1f978e4df74e0b2cace7ad7aed9229258a133bbd65e69227bdf35202aa6638704e7307438315c6ecafc2691d84ad430ee4e1de368e0c2416d841ca0118676e56944dc1a2c6bbcc1e64a1bbcfb56837f1cbe47fb6afc35c6d74d5becd265a18ca4e872483cbccf31c223a8cdf775372689e307288947ba1bc4ec0363b0e24dfc8597a22126957317be03520e89f41613a3f6b5320f7ae7fd009d4f3816252900921168418aabe1733998c0bc1dfa56062cdb156aef877669ee62090ddf2f68bf419f4c40dd2f96eecef957770d51c34ed2263f2853f87e19fb390635cf968fbc252f580a1aec37f49fd9ff72e05fe3e718b02175aee2513e442d003f90c8f2ed75981ca05cf8f70c9f56dbb7c9ff0a183dd6403c0c32076a5cc63e84031217f0927e7f40dd4503968bc8991a3b5612c4c4f2bf258cfbc49539fc4713d85a1d2110ab557f5aa710b9ae8449b8c53362ca397ce05840595da6ab0c352d1494fc348b0516c78c5a629eb92e1b75f4551099a9f425e78c9b823cace0129b1f3b6c7b6f4bcd90b24475905c89b0d6c636758f90c9026987cd70966c9dc8f79c7fd2f9049a126c49a0676a925fef481999e2ccb4e7228068e491ec8006f73e1add86dbfd5e6428a0425be68f", 0xbc0}], 0xa)
sysctl$vm(&(0x7f0000000000)={0x2, 0x9}, 0x2, &(0x7f00000000c0)='}F\r.', &(0x7f0000000140)=0x4, &(0x7f00000001c0), 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0284414, &(0x7f0000000240))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc450443f, &(0x7f0000000240))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
r3 = openat$tty(0xffffffffffffff9c, &(0x7f0000000000), 0x200, 0x0)
ioctl$TIOCFLUSH(r3, 0x80047410, &(0x7f0000000040)=0x3)
poll(&(0x7f0000000200), 0x3a, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
socketpair(0x1, 0x1, 0x0, &(0x7f0000001400)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000340), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000001380)={0x3, &(0x7f0000000000)=[{0x61}, {0x61}, {0x6, 0x0, 0x0, 0x1f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
mkdirat(0xffffffffffffff9c, &(0x7f0000000100)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x0)
symlink(&(0x7f0000000440)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000bc0)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/../file0\x00')
unveil(&(0x7f0000000540)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/../file0/../file0\x00', &(0x7f0000000080)='x\x00')
r0 = syz_open_pts()
r1 = fcntl$dupfd(r0, 0x0, r0)
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f0000000080)='c\x00')
ioctl$TIOCCONS(r1, 0x80047462, &(0x7f0000000000)=0x7)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000340)={@remote, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "f6cf07", 0x8, 0x0, 0x0, @mcast1, @loopback, {[], @udp={{0x3, 0x0, 0x8}}}}}}})
open(&(0x7f0000000340)='./file0\x00', 0x70e, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000008c0)=[{&(0x7f0000002e40)="9d", 0x1}, {&(0x7f0000001040)="15", 0x1}, {&(0x7f0000003080)='*', 0x1}], 0x3, 0x0, 0x21b8}, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
mknod(&(0x7f0000000000)='./file0\x00', 0x2000, 0x1730)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x5}, 0x4, &(0x7f0000000080)="02c1f367", &(0x7f00000000c0)=0x4, 0x0, 0x0)
r0 = syz_open_pts()
syz_open_pts()
pipe(&(0x7f0000000080)={<r1=>0xffffffffffffffff})
poll(&(0x7f00000000c0)=[{r1}, {r0, 0xd1}, {r0}, {r0, 0x89}, {r0, 0x8f}], 0x5, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x7dd5a512, 0xffffffffffffffff], [], [], [], [{}, {0x3}, {}, {0x3, 0x10000000}], {}, {0x0, 0x0, 0x1}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "93c99badbc460516c942bfdca48d9a42f77fe203"})
syz_open_pts()
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
r1 = kqueue()
kevent(r1, &(0x7f0000000280), 0x7, &(0x7f0000000340)=[{{r0}, 0xfffffffffffffffe, 0x1}], 0x10001, 0x0)
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
fchmod(r0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
shutdown(r0, 0x1)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_REMOVE_DEVICE(r0, 0x80085762, &(0x7f0000000080))
r0 = kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
kevent(r0, &(0x7f0000000000), 0x7, &(0x7f0000000040)=[{{r1}, 0xfffffffffffffffe, 0x5}], 0x100, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$FIONREAD(r1, 0x4004667f, &(0x7f0000000080))
semop(0x0, &(0x7f0000000000)=[{}, {0x0, 0x0, 0x1000}], 0x2)
ioctl$TIOCSETAF(0xffffffffffffff9c, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000180)={&(0x7f00000000c0)='./file0\x00', 0x8, 0x0})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
r2 = dup(r1)
bind(r2, &(0x7f0000000000)=@in6={0x18, 0x1}, 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x2d}, {0x5c}, {0x6, 0x0, 0x0, 0x447f}]})
write(r0, &(0x7f0000000440)="3c9ed0f773672ee31265ac52c02e", 0xe)
pipe(&(0x7f0000000000))
sync()
getrusage(0x1, &(0x7f0000000340))
syz_emit_ethernet(0xb3, &(0x7f0000000300)={@broadcast, @random="24e184effdc4", [], {@ipv6={0x86dd, {0x0, 0x6, "d457a0", 0x7d, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x81, '\x00', {0x0, 0x6, "de413e", 0x0, 0x9f, 0x0, @empty, @loopback, [], "45e6848427e701ee5ec4afc89846e7656db961f64c8c4960f0c83aa0e9f94996ac9c83bd23686f73a0fd5ac7d54bdcd2dae65c48e07bca2acb7518f7f3af196cd77b44b197d6dc5196923e31a2"}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x48}, {0x4d}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000000)=ANY=[])
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x10000435, 0x1, 0x81000005, 0x6acb9789, "0d4300002c43568878a0083639eaec00000000c4"})
writev(r0, &(0x7f0000000400)=[{&(0x7f00000000c0)="42052536f0aa4c43c5daf91b6ac439662ce0376021b71d36501e31c49a23eb4d2465a70a1599e774ebb999393aa15ae4733c0dcbfb092a279acba21f9bc2c18409748a89b67927a9ab9b353ddbe949d80a6bc177f0a9060a99cc9b6b71cb70d5090e14f26631682624a6808ce412bd8524439020978dcaeee51ef70aeaa22c1d41b67b6a061da382d1259e8c8a7dd917668acb96a336208d8d", 0x99}], 0x1)
sysctl$net_pipex(&(0x7f0000000240), 0x3, &(0x7f0000000280), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x40}, {0x45}, {0x6, 0x0, 0x0, 0x4002d}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
syz_emit_ethernet(0x52, &(0x7f0000000000)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x1c, 0x2c, 0x0, @rand_addr="8ab32d78d67f3a344cbcf951919a5b00", @local={0xfe, 0x80, '\x00', 0x0}, {[@routing={0x11, 0x0, 0x0, 0x1}], @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
execve(0x0, 0x0, &(0x7f0000000680)=[&(0x7f0000000580)='&]@!\\\xc7.([@$\x00'])
pipe(&(0x7f00000004c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
writev(r1, &(0x7f0000001380)=[{&(0x7f0000000040)="b79c7579dd227b418f28af711ab41664e5ba7734235fd849fd8f1bef9a8aeeeb95eab06798c206001060b04aa8f950a6d812005c5c3c78fc9eebb2d3667b045e2918990ee8010a6d0e195bf2f0189327f4b47266febc685deaf2b22cec68585fd9ba563c93777d52eb6df23540f42cc44b9693e24a90521219bf4588c7a7b55d1d089b1f18bbb7ae535d57837e3826e893a4f37aa5f833bca8ade7b9caa3101b6550c40b5aef0a194d9024dac9736f6100ed48e75f91691805220c33284599904d097d00"/208, 0xd0}, {&(0x7f0000000380)="d472470000000130bbaaf4f25552827c3b3f958c0b1956e1bb3bae5c0666cc845227ee2500f2484a4012466ff93807065f3a55934ef3d9f4cd024eb18f0dd482c8c80b29dff3155908000000ec7d847e8ae3bdaa3bbb2d3f1dc0ea", 0x5b}, {&(0x7f0000000180)="c8f4c597faa17dc0cd31322ede2b10c2eb72fafc513eb25854c5a6c2add51602cc05626a54dda827b113ab2fe86e8a4eb9400b48f2f8a38f2d3bb0b5f28c91ff95a32b1c3f77f5628a7c2ab74c9b92bf3ce54c443643486d515ad0df78433acee4694034e7cbb4a792fc38701d858945611ce67fe1bf28c11d4643e0e4282d7debfec4e5bad2f0da9be9f2cbe11a14beb3daaa9b33223e8c76a1f06a36c059eecb5c68e59a4d45b268ad528702447018c03c8cbc2d4414c486018f8fa11e28c2743d35b91ae2b6e6bb3fe85a650b132ca8d7fdc4fb3e279978d602d81764fd7f6da40234504866737072a4bc312c50d669b53b4ed847a00965f4150ecd0e0e284aa1bf619fec83cf5a9ea49a5a6aab89828f7ef2bcbc5fcdbf5630946ecaa35caced2f00f89d61a970df49e6c2055da684687230ed0eff0853f64a624ba6a1190520000aa9ac2a52fe75ba6314c4faa5796c904f46c389cf745f80b2c49f53ed11f7ebf164f6c3a23826ea3f4e37fee21441b0b62a2483765f2f72b1e751a9e91eab19769bd159b3", 0x188}], 0x3)
r2 = dup2(r1, r1)
readv(r0, &(0x7f0000000340)=[{&(0x7f00000005c0)=""/141, 0x8d}], 0x1)
writev(r2, &(0x7f0000000500)=[{&(0x7f0000000400)="b7a677e06bb550b9893feb03c5557b9dd1dfdc2568aaf4637165c70c2a1e9fc0330643b7b403a456c4aaf734c5c8199a9b98dbab219b93bedee65570fd1e2946f7d7d7c510bd86e0c9dc11dd9f9abd7f237009c20320a57d5912f9ac414eeda41e821479e9ded21c7256242f47dd275da8bddcb2a1e2df1e205ad6b7c313940babc83ffc90a139f750f596811053bcd9100760349fa7", 0x96}], 0xb)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0xc4504446, &(0x7f0000000080))
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x9})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x0, 0x0, 0x81e1, 0xffffffff, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000240)="94d1d4fb15da257b87a3a30a87945191fe5c83ad410672794ab008e67366b8b1ce65e798d66d869a8342d0b26f04862c6ea0f1a6486d9bae5da3e84f5f0bf39a993bea50e61c3739fdfedb780e9f3816835ad57e66bce76a901417bc2f488140f73f819bdd5de48b64d246a53467c62526e8401a4800d4c88f90ed07dc9ce1681f0e152f3dec86705ea5c2832ecb80bb96f6362f2f021206818d48e521404764d891320589b2eea0a17425555fcb2e7582a2331f918878772f325f45b228ffa3ce97cd28c7720a2e67061f4acc031c6c4076f54442a002c79b3e25ee0af9c7c4f76327d8b8cc409b559f75ed5f7837d9f8e9f2a123939900", 0xf8}], 0x1)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCSETA(r2, 0x802c7414, &(0x7f0000000100)={0x0, 0x0, 0x7ff, 0x0, "db9deef7211deb169169279041b8d45b3d02446a"})
writev(r0, &(0x7f0000000200)=[{&(0x7f00000014c0)="96", 0x1}], 0x1)
r0 = open$dir(&(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000000))
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
acct(&(0x7f0000000080)='./bus\x00')
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002009, 0x3200)
r1 = open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
readv(r1, &(0x7f0000000380)=[{&(0x7f00000004c0)=""/230, 0xfffffe9f}], 0x1)
read(r1, &(0x7f00000008c0)=""/214, 0xd6)
open$dir(&(0x7f00000003c0)='./bus\x00', 0x40, 0x40)
r2 = dup(r0)
ioctl$TIOCSETD(r2, 0x80184600, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f00000002c0)={0xfffffffeffffffff})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x8004667c, &(0x7f00000000c0))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x2})
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x8, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x6007, 0xffffffab, "919b914c7198b4af5e08004f00"})
ioctl$TIOCSTOP(r1, 0x2000746f)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98", 0x1}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0xffffffff, 0x0, {[0x81a, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000], [], [0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0x5], [0x4, 0x200, 0x0, 0x10000, 0x6], [{}, {0x200, 0x2}, {}, {}, {0x0, 0x0, 0x0, 0x3}, {0x0, 0x0, 0x7f}, {0x4, 0x0, 0xfffffffc, 0x2000000000004}, {0x0, 0x0, 0x0, 0x7fffffff}], {0xfffd, 0xfffffffc}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x0)
r4 = kqueue()
r5 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000180), 0x200, 0x0)
r6 = kqueue()
kevent(r6, 0xffffffffffffffff, 0x1, 0x0, 0x0, &(0x7f0000000200))
kevent(r4, &(0x7f0000000140)=[{{}, 0xfffffffffffffff8, 0x2, 0x2, 0x1, 0x6}], 0xffffffff, &(0x7f00000001c0)=[{{r5}, 0xfffffffffffffffd, 0xe0, 0x80000000, 0x400, 0x7}, {{r1}, 0xfffffffffffffffe, 0x40, 0xffffb, 0x0, 0x5}, {{r4}, 0xfffffffffffffffc, 0x0, 0xfffff, 0x642, 0x6}, {{r0}, 0xfffffffffffffffe, 0x4a, 0xfffff, 0x4, 0x9}, {{r6}, 0xfffffffffffffffa, 0xc, 0x2, 0x80000000000, 0xf782}], 0x1, &(0x7f0000000280)={0xfff, 0x1})
r7 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
kevent(r4, &(0x7f0000000000)=[{{r7}, 0xfffffffffffffffe, 0x29}], 0x1, 0x0, 0x1ff, 0x0)
ioctl$TIOCFLUSH(r7, 0x80047410, &(0x7f0000000000)=0x5)
r8 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r8, 0x8080691a, &(0x7f0000000300))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000480)={0x0, 0xa, &(0x7f00000001c0)=[{&(0x7f0000000280)="f1b338fbe6925e775b2e2f9c46a9f3c3006154dc52beaa94cc6d32bb270f3fed90effd74758dcafa117d12616c628b810e7dac72285b9c80c4dc1ae113a9504b1ca1fd6385805497a97b9645d584519fe475bd6fb6edd00dc85bb7576e9b900e685443c2d6502746ffc0bb55f5f9f248fd0bd2d23e2ed801878f44e4f08f073ceae982802b56661ea5e5310e5722f98b6c62da1014d87b21d9cf685457e623433afd53b366aa2caacb9ebe7b5c3e685122e206d51ed5bf1af6eb84", 0xbb}], 0x1}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000480)={0x0, 0xa, &(0x7f00000001c0)=[{&(0x7f0000000a80)="74cededd2072b1e664b28d481a4368b958f30788520617995402564577ff83e3a77849c7883e678b0b3be81fb49c34dfa050c6fbfca823ee519fa4d6ae57321b28533faa8a988b647c9e87d6ef6ddf4f1b517257b13d6274ec80609e3f9484590861055721cc582590f432f09bee28ee8456f9eeb8f5a8074f9e728bfb704f8f39415881eb2bc9c7bea65729f69c543c1245735ab4790f34904cf63787c9d611d4eb27deaa1cf9b804061612bb7bbf4247ab504ef3baf6389f60bbfab827e50f3879de90cdf0d8715283f4ecf0d3dd4f291b8c22fb1d76d14e1b03433db48cefdb78a52355581c7335a3fe587d8894f861ec154a22529688b71399b255f3c51d3df346b1a3db4ad92bbf8631cbfd4d7b680d16326e98963ff4e03f470fe42d36ec78ef852bed4d2bcc89575079ed490b854c189775fb4cf0a6216f4e8bb79341f6069616b47a253ed49b2725086588ae7204934b90d8175836eff116edfc075bf7e4bba93a7e1f5813047ac8c6e4d6f6f44f37a246604489507d454f30fb41c7674d836c58755106d8b27d001224bc35eb1db1f4311974c4cd90418324b4c4d6d9f8b8d208869ad4f4e6412e8ec5047d053b0097087cffaaeed1bd6e51a129b4cdbe83e624599b084e6ea28b56925dbe2f01acc63c8ba24e9e629219606bed792b7bf52c9c7cd530bc1106543988dea4c803490d3a49736af92498a239999db964c0dd6030ef3f70e31dc30c57b40a80d8f90dfe51ca8cf4547d4391ca1ac9b0f26604ae1385f97c34aba775694cff7dbdee1511b96ef5a0f4032dceaa7f9897647a0a02808d3caba71e171013c96c1515ce559fc38b84c147ea9bc66024bcfc7c51e1b4be42be7b0ac4370b5956b5604eff6c0daf5117735a6a4af2c749c77e6e3e17bfe1aeec4fc4399cf9ec65dae1182e4980147c64a3a67c892b57453fe4892ac73b8cbc7ef063556730f10303df274c5069106afa8755548481c2121f0cde0a92216461a2909e21c1585c798fae0ac6ad3cdccd60eaa6c0fa9efca52196ff888398f1065360f4f58b89b5b5a700be9fd5a9a5acb1657db3498d1d0f99b28fefe1f329340278711037531ef8dce464b40ed01a2728dfd09e177c343dc342fab68adbef2a3cde78496a3465a769f2cb7f8800cb8a56479837eee52af53cdaf562126b69d5e22a43f35cab573f78f9837ac52d541616f4bf41d16d03d8e7aa095b80f87a0e64b080ce1c67c3ae9f25099284504c5482b35c4a0b47f0878ca2d93e8498fbfc7de4fdb2bac3df11554ccb87d58cbb2e9f9270338828b17fa7d0e8ae44fefa3aba383fcea29291dff3b2e81d765713a314b5df546b542cf95ffbc4e3f58ae730c30d8bcf1d4a659909135b16add982a8ebf20de371f2549d41f7fc8e3306b12dd3399d23c00dc2b8db0cb4b07cb4b266cb20f13579af3d798b81f97772de304080a54cbfe1d752218c9eed9087ea7029dff1492e7e88f447adaaa3e89a3329b52f764d732a6e0e92fbf343a77cc7679178db410a62cb9f5321956ec1ab257da0ba13599f8b276d089a04de3e6f4db4dd40dc4fdb959544f4f8dcb960daf51c9487f911834d29210d59bfa31759d1c30603b92dc460e6818c5f928b0e4097d646d0b65f04bf7593ef044a90ef503e07f09d71e72b81654377a8bac6d689561af8d946b088439cf11d95bc9f3e4e08897ffb0223da9403b64ddb6f03cad13499d21f1f791660c5abe259aefa30f1a5dbffa41e78d5c1e5942c18785173d68e876ef7af2caadd5d572e7f5925450f478215d17ab6274e1852029c64377bec087030562e7558666ab34158b3cd49e082492ac7d5fad041a4ea712d6ba980b2eb4c12ba7d130b87db0184f6b871512476008aa0d536481226143028fa7bc63f6ecdbaf26dd0cc9318804754e034446a51d12e0638c7a22da93707bb29f4bf329982b755b1ed862fb38d21d95d91c731f41c4573589829ff8266e4505376", 0x581}], 0x1}, 0x0)
ioctl$WSKBDIO_GETMAP(r0, 0xc010570d, &(0x7f0000000040)={0x0, &(0x7f0000001500)})
write(0xffffffffffffffff, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc", 0x59)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc110445d, &(0x7f0000000240))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000080)=[{}, {0x8}], 0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x3d}, {0x28}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000340)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x1, &(0x7f0000000040)=[{0x14}]})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1c}, 0x4, &(0x7f0000000080)="a29d66ca", &(0x7f0000000100)=0x4, &(0x7f0000000140)="05330942", 0x4)
syz_emit_ethernet(0x335, &(0x7f00000002c0)={@random="3b1bfddfc55e", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "39789a", 0x2ff, 0x3a, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, [{0x22, 0x1b, "baab069918fceb10a2834ec64a9234ba406537d40bb749557e944e71d6220a9980542460871e3949e0d6734a6b44e766dcb876b9674ce3284ed0a56fa52654b310cb96272ffb0b2987e3dfbd0c3192d3d7b01ac17e5e51e5c92b1ab6145131712e6054872a49ff311de86cacac77029999d563001cfdddfccd0d3048572e0eaba33c4352cc7425b399631cb92e6132b848ae51a0404a91ab0b39a7ab256768b429e39b075a8624efbc004eb8ee12999af2c8430421c3d6d62561909ad0920ddc3a093e628f687511909003a0f62b7547f069ab3c7de7f33170"}, {0x18, 0x16, "bf6e9d99a92a1092f0c265eaff76567a7eb85d2c65a531a1ab0fcf67f629a76d35f9566eb32a932bde5ad97e81fcd2ef1fce21db0f77a3b1d39d2c44c7f6527de0699e90952c1dfff8e0e2586f8119de4f11279a364d8b27615b8a9767224bf294499ef085ace99b9c59fee1e6e0278782a59357ba9a682b24c8b8a1e4603f141ba847d3aa28ba611d24101534a3975a58ab4ea1e6dff1347b408ed4fece602df539973db4d9b767263bad08043dc863baf76b"}, {0x5, 0x3, "746465bdee7e1d05c7c8150e521764e526276e4a53145752679eb2"}, {0x5, 0x11, "8a0584eaa4cfd4299ba11f4c32c2372aeacb1f262bdc2b9eb7b6d3eb98f66671ebc30627c79c5d0890d91bcf879de471fcb195056febe7934d41b062bef35b9fd7872ff912d18f6544bfd76c613a3e71f43cf88037f9dc2a51eff86282de016c0e277335d8f422a2d26043b153584af3fbe90494578d92880fd5644ed808c17696cd883fb564b526f28ff3c787"}, {0x4, 0x15, "b2b1e9c9a7648d29c19857ae03e9cc8b81c201422dab2c6fb68678cea7167eeb7694f95580e01bde62720fe0b17282ec56ad31daadd0ae2804183a1551b3d83f43b5c609aeaf50a401bce0b6f7e01dad2a917b74858d92274a1b4d3dfe532272ee8152f5b6e189946ca4fc2e1040dd829b848686b2bb25dd2393bf0995f937b2fced3d69aa8d9cbe1d292f8f445a32234d34d77f68e2098248114b8fca8da4f2675a7fe40b5a311a666132a81e"}]}}}}}})
syz_extract_tcp_res$synack(&(0x7f0000000000), 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x462, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r1, 0xc088444f, &(0x7f0000000240))
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0xa, &(0x7f0000000140)=[{0xfff, 0xdc, 0x3, 0x10000}, {0x6, 0x5, 0x81}, {0x5, 0x0, 0x4, 0x4}, {0x6, 0x8, 0x8, 0x3}, {0x5, 0x91, 0x0, 0x8001}, {0xfff, 0x2d, 0x5, 0x40d39}, {0xff01, 0x0, 0x80, 0x10001}, {0x0, 0x1f, 0xac, 0x6}, {0x1, 0x0, 0x1, 0x1f}, {0x1, 0xfb, 0x4a}]})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x14}, {0x2}, {0x4000006, 0x0, 0x0, 0x2090a}]})
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r2, 0x0, 0x200000000000b, &(0x7f0000000000)='\x00', 0x1)
pwrite(r2, &(0x7f0000000100)="0000000000424374cfb5f0b099b9", 0xe, 0x0)
syz_extract_tcp_res(&(0x7f0000000100), 0x10001, 0x7f)
r3 = open(&(0x7f0000000280)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r3, 0x8004667d, &(0x7f0000000040))
setsockopt$inet_opts(r3, 0x0, 0x1, &(0x7f00000001c0)="4195e6587a14f95644d4bddf61cdab71023d196c32b80d8b622b659d9ca7cd746fe0aa7fcb65d36d4416f7aaa09169b7fdbeb613b1a6f60136f9d6", 0x3b)
syz_extract_tcp_res$synack(&(0x7f0000000040), 0x1, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd60000000001400fe000000000000000000010000000017000003"])
r0 = kqueue()
ioctl$WSMOUSEIO_SCALIBCOORDS(0xffffffffffffffff, 0x81205724, &(0x7f0000000140)={0x0, 0x0, 0xfffffff9, 0x0, 0x0, 0x72, 0x0, 0x10})
kevent(r0, &(0x7f00000000c0), 0x5, 0x0, 0xfffffffc, 0x0)
sysctl$hw(&(0x7f0000000040)={0x4}, 0x3, 0x0, 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
symlink(&(0x7f0000000000)='./file0/file0\x00', &(0x7f00000000c0)='./file0\x00')
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
getpid()
writev(0xffffffffffffffff, &(0x7f0000002700)=[{0x0}], 0x1)
ktrace(0x0, 0x0, 0x0, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000001c0), 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
shutdown(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000180), 0x4)
sysctl$vm(&(0x7f0000000140)={0x2, 0xa}, 0x2, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000001040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x10, &(0x7f0000000000)=0x4, 0x4)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x400000005, 0x0, 0xffffffffffffffff})
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, 0x0, 0x0, 0x0, 0xd)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x20}, {0x48}, {0x6, 0x0, 0x0, 0x103}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000240)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000003c0), 0x0, 0x0)
ioctl$FIONBIO(r0, 0x80047476, &(0x7f00000000c0)=0xa0000044)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x7}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
setpgid(0x0, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x10, r2)
ktrace(&(0x7f0000000080)='./file0\x00', 0x7, 0x40000132, r2)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1021, &(0x7f0000001240), 0x2)
pwritev(0xffffffffffffffff, &(0x7f0000000580)=[{&(0x7f0000000100)="c99cb19725249143a7b26e7353d49961e8e7dcdc95456f683d25c92625f429dd4d393c7c3c900d3656d882ae9273afa95afe044471f4aa0ddce2da33bd979687e3ca4c89", 0x44}], 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={<r1=>0x0}, &(0x7f0000000100)=0x1)
ktrace(0x0, 0x1, 0x40001e34, r1)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
munmap(&(0x7f0000e05000/0x2000)=nil, 0x2000)
mlock(&(0x7f0000ffb000/0x2000)=nil, 0x2000)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
close(r0)
mquery(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0, 0x0, r0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000080)=ANY=[@ANYBLOB="0002054e5700d886cb187b5e3f43c1"], 0x10)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002005, 0x4300)
r0 = open$dir(&(0x7f0000000280)='./bus\x00', 0x381, 0x0)
r1 = dup(r0)
ioctl$WSKBDIO_SETMODE(r1, 0xc0105715, &(0x7f0000000080)=0x1)
mknod(&(0x7f00000001c0)='./bus\x00', 0x280002002, 0x2065d)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
ioctl$TIOCCONS(r0, 0x80047460, &(0x7f0000000100)=0x80007)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "dcd05b00"})
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x68, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x0, 0x408})
pipe2(&(0x7f0000000bc0)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x0)
getsockname$unix(r0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1d, &(0x7f0000000000)="aad485c4", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0xc0}, {0x6c}, {0x4000006, 0x0, 0x0, 0x2000000}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x36}, 0x4, 0x0, 0x0, 0x0, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000080)={0x0, 0xa8})
r0 = socket$unix(0x1, 0x5, 0x0)
fchdir(r0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
select(0x40, &(0x7f0000000000)={0xffff}, 0x0, 0x0, 0x0)
select(0x40, &(0x7f0000000040), &(0x7f0000000080)={0x1}, &(0x7f00000000c0)={0x9}, 0x0)
ioctl$WSDISPLAYIO_DELFONT(0xffffffffffffffff, 0x8058574f, &(0x7f0000000180)={'./file0\x00', 0xf94c, 0x3, 0xfffffff9, 0x1, 0x1ff, 0x1, 0x5, 0x2, 0x0, 0xd96c, 0x7b5})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
sendmsg$unix(r0, &(0x7f0000003fc0)={0x0, 0x0, &(0x7f0000003b40)=[{&(0x7f0000001680)="695da0d554da0a57514427d7522ec71fa5c64e6bc68cf0ee7d5cd020daf6feaa1ee878d7247619c77966cd3f0fae9c22248dde7299b51e324227e67d82d51e79", 0x40}, {&(0x7f00000016c0)="c7552733409327d362e201b227ec367137710cd87e330ac3322f51195134b3a1e0e1148cf9902cfad9627583f7025c4fd6ce5773a3acd39a2ba8391492f1c8eb24fe9ac2dd092deb98f1f154fcdbc0a07c0b06cff1c8d15848760e48b10822009a9603e1b6f7cd0bd7bb9c08dc3ffab5eb4c65f9dc785e45338c32a7163b527387860118ddbbd5aedd2df19bda6c3b09efe33cae13961baa", 0x98}, {&(0x7f0000001780)="e7dbda059783da2bc2cea28b27fa53ca09acc06fac0457b70c746e4d3079a1bd9ba81f358ce2abc143c08059c3de2f4dfa0d1be170319e11bf1d7e31791a2666702dfe78e9093130e328165d5615afffcf8f6e90a71ab380f2282e47917ac1ac8b1510c3e65fce523023c911bea94a4e4a8b4df7a9a195688b28931939d264962eab0e4b647b2bc006132a0e295ce835e04d7ba6386b1e38ba9c", 0x9a}, {&(0x7f0000000300)="df3144a26c6f15678c539226956176374fd692fcbdef2de9cf928265e2f9a8695e1dc100c1ca500e56a5d2ea32487c71098de0d060c5073b006380ed01001b04e45904791600f56b097ab4621ccc", 0x4e}, {&(0x7f00000018c0)="3198603925b6512619275e428ec2db070749aa0d73aa697e46cac599979c6164f793560d6dfc76a880b99aa81875d7d80e63ac61e7d79a49a8c91d8ed422b732b3c0c7ed425f209c507205d514fb692436161634e69234b4cf68dcc7b1e398b65e989c0dc6c0bead6e97882a8499323af4b188c2c3976a6a3d271eefc38fb3e8eecdfd7ca0c0dc6cbbf08b012bb5d4715cb62b071bae8370fa2f0452e6dffe35f47e5de137ff3403ebd829b8faebc2fa2aacaf47498e499dcacfb5282c66c81d78f899ca9fafa98e60b575284888636ec10d0408956e60f492", 0xd9}, {&(0x7f00000019c0)="fc4bdd1c00fda2144dc3e13b507f345fb63f7fcd5863ae27d469b40e9a8e292615920ccccf7d0213d9ddfe99f512a65371c98af97aada46709167f237f629e6c8b40cafd07a1dfadef2aa136891d8661a173db5b19e2dfbc064a74d5edb446cc4f2fb12a4b96eaae79c4926ff786f60f29af8ef23ca2a4039b56f63b46fe8db5bcefd56fbbbaf141f7b534bfdbcabca6d15da3076263f0541a3a2e0cdee83845c62db867c6d65c49058418c32d1c576bead1ab9559206b", 0xb7}, {&(0x7f0000000000)="3e0ee6d516f27159b44c5a66e75ceb44c5e1c610df0322764e8fdc0e31ded4602e7b94659ad54410fb85e18a7a01786d585b5c731a0473b7b4c0c6198e0255c842c2c7321a0b56f497aed33c39c40fa9986f20efade79849c784556b1cdaceeae17a555bddfdfd238cb9e4eb8e0b2e4a4e066372090000007128406a522b1ca808caa819608b766a9394f2e511a5e5449815f49ee8bf0862aafefd8146097450cede127d683be943a5c4424ad756dbb547e853130a2ec5adabf058baa46bc8a8c880e9d390c2f85dd080fe6b2da90014cd9d238551d6e487853b21031f95a771dff3350c95cca4f08731ab40639e7f0ab0aaa8c7f34c2a8e31bae1593f264c09ab95b23784b748e07662f9e309b457930000000000", 0x115}, {&(0x7f0000001b40)="ce0c2459a3c3be6289bf8b9ddcc9baec7c0f63a4411b7798f6dcb865c865eb60337b8aa063534332c29b9bb9715d5090e2afc5789051bb9e05c7ca733743209fcdc18d4baa2c8da2045e55200dae6e5c598f80ecab2beddb7deb63719acdee1ff8944aa3d890e7ba2cac28791cf79e920e9d131c901f6e764aef36694c3d99e2d796837a63dd9426a0a38dc4962ec23235feb3533446c12a65f364ff435573931a939f214294c6967ebb02b34d54da03edcd537e13d8e8bf1eb7659432d89a78e818d5f816c77e1d81ea36aa3c8a21d1cfcb08a044f1912e17da6e907f0001f5e677d7589c40e4f2119fe6621cb06edf39d2c53ed3070e5ee0d5cbea11170f7850e5667f01cbd9f520e38830d886c7a17ec31fcbad148c495b046d2f04e5e0390ffb720211cc5b4295fe7e9813b78f8c96bc98100204c61e8edc7e7b7577a8c566d0630c45e0816cc8515db521b108b30fc547835ad4dc1c954001fa24a9046598a08d0da1681bc24a39a554b2918223dfd45e064aa9ee8bc28f1018ce3632cd9fec854c62574c27de11c692b352c1b1a504b2dae64eaf3fbbfac3f8d3d3eb00401bd4ca45a59f7577c05a4b5b2e9a3874e1a37e24640a15265bc9339222b4097e8232ee5458e8e344ff41df96ab45f2d3448ceffd8a7f33dd4704c5ac9152649497fe22ac9ae528dcea16a8548eaa9a650467853f348aab77670b023deb9a4f5a8e2da23bd02b9a17c5c15af540625d7e93d30297562e4b33315573a201c732f4a9e0c8d378bda6aae77ad8c399d8c54bb5e0b69a26c0fa2a5506e04212b411d3a030e9dfe0174387c9c6c12039b9f29dbcac18532d349577f7160b9fc497d6c0341eea", 0x264}], 0x8, &(0x7f0000000bc0)=[@cred={0x20}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff9c, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff}, @cred={0x20, 0xffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}], 0x138}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000003fc0)={0x0, 0x0, 0x0}, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
fsync(r0)
mmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
mprotect(&(0x7f0000fef000/0x11000)=nil, 0x11000, 0x2)
setrlimit(0x8, &(0x7f0000000100)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSTOP(r1, 0x2000746f)
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
r2 = dup(r0)
read(r2, &(0x7f0000000040)=""/43, 0x2b)
poll(&(0x7f0000000200)=[{r0, 0x40}], 0x1, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
connect$unix(r0, &(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x807fe, 0x0, "0008000000000000008ddd0000000700"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r1 = socket(0x1, 0x1, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000501600000903f00000007000000331c13fecea10500fef96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af6300372a2102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c50000200200000d230000020208a371a3f8343712051eeab71d89e000040000000000042000000000000000", 0xb1, 0x0, 0x0, 0x0)
setuid(0xffffffffffffffff)
r0 = getgid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000000)={{0x101, 0xffffffffffffffff, r0, 0xffffffffffffffff, 0x0, 0x10, 0x6}, 0xff, 0x10000, 0x9})
r1 = socket$inet6(0x18, 0x5, 0x6c)
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000080)={0x0, <r2=>0x0, <r3=>0x0}, &(0x7f00000000c0)=0xc)
r4 = semget$private(0x0, 0x2, 0x82)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000100)={{0x1eb3, r2, r3, r2, r3, 0xc, 0xc08a}, 0x7, 0x8000000000000001, 0x400})
getsockopt$SO_PEERCRED(r1, 0xffff, 0x1022, &(0x7f0000000180)={<r5=>0x0, <r6=>0x0, <r7=>0x0}, 0xc)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f00000001c0)={{0x7, 0xffffffffffffffff, r0, r6, 0x0, 0xd3, 0x1f}, 0x6, 0x5})
setsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240)={r5, r2, r7}, 0xc)
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000280)={0x0, 0x0, <r8=>0x0}, &(0x7f00000002c0)=0xc)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={<r9=>0x0, 0x0, <r10=>0x0}, 0xc)
r11 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000340)={{0x4e, r2, r8, r2, 0x0, 0x60, 0x800}, 0x4, 0x35, r9, r11, 0x200, 0x6, 0x400, 0x8000})
r12 = accept(r1, &(0x7f00000003c0)=@un=@abs, &(0x7f0000000400)=0x8)
getsockopt$sock_cred(r12, 0xffff, 0x1022, &(0x7f0000000440), &(0x7f0000000480)=0xc)
r13 = getegid()
lchown(&(0x7f00000004c0)='./file0\x00', 0xffffffffffffffff, r13)
setgid(r8)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000500)={{0x7fffffff, 0x0, 0x0, r2, r10, 0x11, 0x959d}, 0x7, 0x0, 0x6})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x0)
select(0x1a, 0x0, &(0x7f00000000c0), 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000300)=[{0x81}, {0x84}, {0x6, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x27, &(0x7f0000000000), 0x0)
setuid(0xffffffffffffffff)
r0 = getpid()
ktrace(0x0, 0x1, 0x120, r0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x4, &(0x7f0000000080)="1daf63b25970aebd36020d25e1b10cf3ece6b8be02", &(0x7f00000000c0)=0x15, &(0x7f0000000100)="c2d24c9d34fadd0eae82b8e3168fa740a37829960f8862294ace4e16e103938e10e9fef2d403c0954d8babb49abbd7757e100f97a12aa77f902737ae02664eb0c56d03bb914b2b2a4c4cffe51a6e41cf38d5db8c47370140fb18077b6f8e963e39233b5a0e053e6c86179c589f83b7fa0105135c0b110bd373f3850f556b04ee1ce94eb6e707ac4ea0df9499463f04a489a12e300431c0e42d390839498f20412301ba6400f392533db65e70b870ff4215a0c879ee965532ec5852910377cb30a7d9cf5b1184b39d9d8c7dadb9d96aca5c946e6f5c5c18b50a", 0xd9)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
chmod(&(0x7f0000000100)='./file1\x00', 0x13)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
lchown(&(0x7f0000000080)='./file1\x00', 0x0, r1)
setuid(0xffffffffffffffff)
faccessat(0xffffffffffffff9c, &(0x7f0000000140)='./file1\x00', 0x2, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
chmod(&(0x7f0000000480)='./file0\x00', 0x265)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
rename(&(0x7f00000002c0)='./file0/file1\x00', &(0x7f0000000100)='./file0/file0\x00')
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r1, 0x0)
read(r0, &(0x7f00000000c0)=""/34, 0x22)
syz_emit_ethernet(0x3e, &(0x7f0000000100)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "fc8b00", 0x8, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x3, 0x0, 0x8}}}}}}})
mkdir(&(0x7f0000000000)='./file1\x00', 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000180)='x\x00')
unveil(&(0x7f00000003c0)='./file0/file0\x00', &(0x7f0000000100)='x\x00')
unveil(&(0x7f0000000080)='./file1\x00', &(0x7f0000000380)='r\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x1d}, {0xc0}, {0x4406}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0xe, &(0x7f00000000c0)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x2, &(0x7f0000000000)=[{0x34, 0x0, 0x0, 0x6}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000500)="d000ff1f0000000000001b000008", 0xe, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
rename(&(0x7f0000000200)='./file0\x00', &(0x7f00000000c0)='./file1\x00')
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
bind(r2, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r2)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x1}, {0x61}, {0x6, 0x0, 0x0, 0x1f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
close(r1)
setrlimit(0x8, &(0x7f00000002c0)={0x40, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x3, 0xbcd8, "ea3ab27570ffffffff8a59ea0900e800008e4c00"})
r2 = fcntl$dupfd(r0, 0x0, r1)
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r2, &(0x7f0000000040)=[{&(0x7f0000000180)="96c437ff42db8103c2079219e353970a232938b6d6e80e5b65cda9a51618c9e2b8b134b1c91d60f449facd1db486657c850474e516af602c2b9c4edc329040296f77aaa2db3e82432b558e81cda08304cd81cf4bccfa2836482fa8bfb1ca9d21d2b35a851434f1a2f54b1e75d04fe334a4bd1425695412190c179bba816ffb093634becd44c8df9203a16bb55b04f3d57ce3a694238901c9ab78f2f6", 0x9c}, {&(0x7f00000000c0)="856605b1ca76d1045ed85a0fde684300257c94216487cdc956571aa0af674562b8080f033fcb1a7b753ab6c706d04188423c5972bec197374dbad86bdfc8863ee2ec64ffb09acb8b8edc58eaeeb62a30714fe9f6a5bbeea57f6ba5cf5288a3fb334f8d", 0x63}, {&(0x7f0000000300)="c3049a8b4a2f0daafe44f174107fdbdc3cd23f175951a66cec6bc0248b4d53d9d6e7d5c41ebc79a1acf4e850a99a1383c70eb00168ae7fabd7d5c44f91eb391dc805ae4a97a6e7fe2f96fd90c83ad30fd2749aaa968be0b1f233a1a83f80df7d7d31c340144460406c2e323b7da9dce77b47d45dbab72dfc2cddcaa7969304b5071c9f6a919dc235716a62215e385f86f30ae68396b82c509a422272fde686cf7bd5bb8eab63511f6577d9a6f53ab236db6cc943a38bc0a0d511ee80bd22c6f36bfb16393f4f405d7013b7e41061c257ec38933d03618ded42b75e7abfdbd80412165d99c60e679e9cc9d80e20b2360da1b2a1cf5b180e8e9f277dcda017e8e7c2549c71f8d4ba3ff9d883548116c39ce6d5d1923a6c9b3c7eb7a4ab259ecd97e378211f5eda611ba87902a26e784e01e2f72864e15bdc31a1483d38cddde5616c80b3c82fb1f3a99cd8b8b6f8ad536c541b987b3439506863cb2cc1c8c77dae91d636a13cb06e3349fd57cbe0f556c78bd99791eab95a780a4ee6e3702f73f9487091ce2bd86bb462a963ce1b7caab31533473cb96ecf52759ad9dacb01149e0836363b121785cdd49e0fa34249b280f7b21dea366a151bbd864a8c017a4ee6287d621461185b10840ab5ac95e28b1a0685082ed4f9d0b0597a49eba3dea58a377f3c34a8b8bc92e01734412153ff6206dcc1d68bbda4be022130ddc0ea290812ff8afbde3a80518fc4a311ee059eb6b118acb611a83bffa1b2e09357c1e2dd175b0f875a403cdcc4c1f3fd1ee46065ce3132fa8b3ea2e7598b6566a21f131a8d71d3814fd01107611397262e7298d306ebec5898813a96a4cf2745d25e7f1771eea7e3ab42e44eac553f3d40a3a4b980d0ed90f2e31c5b9f0a21e31934ce2dae619eb64d645a066df29f8f7e84b33b08f871f97caa83438a26f8f4ba559972b9c37edee3a06cffe9732ba2149e11e712a3e1ccc6f2c9770075474e2421c630b2bbf3322e96ab8249efc2c736dd62ca2efcda1dcb8f1588ee4eb14b3f99989236a6ef9acb972f421bbc9ba824026499951d03d2edd40857d4038a3127a7237a37c16a6c3adddc0936dcc8251773200475dc432e869fda56637d456133b5f80ab1c852ec055206ac135896f1f41a18ce16ab9813e9e72d7e94233bb2d9f7e8737b2ccaf909f3dc3eb1329a8619d92db5cc2e402e9a5d5eb02edfe0383e165a5a28a25ce52c53c0336e2823a95c95fa65ec4281e14f467e6205510165dc0e3b8731a2f04c6a54fec5d5a2ac905b6735b024d7c680112a96fc4faa2a6c6c63e51fbcbbc1a1ab6f531f1c42ddf1a20dc1ee8e513fb3183309d0ca1634f36a914ca2bd49451e7c35261274584128447ae952094a4ddeeb890fb5d030d332f6aad7aa3abfca05f537437831511dccc7ea4cadc67de2a9089be9a5ddf59667a938a06d", 0x400}], 0x3)
execve(0x0, 0x0, 0x0)
clock_settime(0x100000000000000, &(0x7f0000000140)={0x100000001})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
close(r0)
close(r0)
mknod(&(0x7f0000000040)='./bus\x00', 0x80002005, 0x4300)
r0 = open$dir(&(0x7f0000000280)='./bus\x00', 0x381, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000240)=[{&(0x7f0000000140)="baf92d151c3011fad28b6c83072523da54a57616c3141020ff", 0x19}, {&(0x7f0000000180)="9306c73832db3044ea31a8537274fdd4e7319488f35d94581fe06eeb140a5a5740e51c13a88affcabf30808984c3f36374a32a34f10171ebd4ede91789fbf29f6b2e0274", 0x44}, {&(0x7f0000000300)="749b502abf7032369be025ba65238e595477ce839c0792472349997c53b5af603fa3a96ccb401bdcb12aa773d6626e3cdf", 0x31}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
r2 = dup(r0)
ioctl$WSKBDIO_SETMODE(r2, 0xc0105715, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x1c}, {0x3d}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@random="eb0420e10a89", @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @rand_addr}, @icmp=@echo_reply}}}})
mknod(&(0x7f0000001980)='./file0\x00', 0x280002002, 0x2065d)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001008e7d00000000ddf500"})
close(r0)
r1 = socket(0x18, 0x2, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sysctl$net_inet_etherip(&(0x7f0000000000)={0x4, 0x2, 0x61, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x1, 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
setuid(r2)
ioctl$TIOCFLUSH(r0, 0x8028698c, &(0x7f0000000000))
r0 = openat(0xffffffffffffff9c, &(0x7f0000000000)='.\x00', 0x0, 0x0)
lseek(r0, 0x0, 0x5)
r1 = semget$private(0x0, 0x4000000009, 0x82)
semop(r1, &(0x7f0000000140)=[{0x0, 0xfab3, 0x800}, {0x0, 0x4, 0x1400}, {0x3, 0x7, 0x627462befbd9eef4}, {0x0, 0xfff9, 0x1000}, {0x0, 0x4, 0x1800}], 0x5)
semctl$GETZCNT(r1, 0x2, 0x7, &(0x7f0000000600)=""/70)
semop(r1, &(0x7f00000002c0)=[{0x2, 0x3}, {0x0, 0x6, 0x800}, {0x4, 0x200, 0x800}, {0x1, 0x8, 0x1000}, {0x1, 0x5}], 0x5)
fchdir(r0)
semop(r1, &(0x7f00000005c0)=[{0x4, 0x2}, {0x2, 0x6, 0x800}, {0x4, 0x8783, 0x1000}, {0x2, 0x400, 0x800}, {0x1, 0x0, 0x1800}, {0x0, 0xfffc}], 0x6)
semop(0x0, &(0x7f00000004c0)=[{0x0, 0x3fa, 0x1000}, {0x1, 0x7fff, 0x1800}, {0x4, 0x3, 0x1800}, {0x4, 0x8, 0x1000}, {0x1, 0x6}], 0x5)
r2 = geteuid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240)={0x0, <r4=>0x0, <r5=>0x0}, &(0x7f0000000700)=0xc)
semctl$GETPID(r1, 0x0, 0x4, &(0x7f0000000500)=""/113)
setregid(0x0, r5)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000080)={{0xfffffffb, r2, 0xffffffffffffffff, r2, r5, 0x40, 0x2}, 0xb02, 0x9, 0x8001})
setreuid(r2, 0xffffffffffffffff)
r6 = geteuid()
sendmsg$unix(r0, &(0x7f0000000440)={&(0x7f0000000040)=@abs={0x1, 0x0, 0x0}, 0x8, &(0x7f0000000340)=[{&(0x7f0000000080)="090a469190d2740108f4942be3955a57c1b966288e4891fafebffe9ff9d580499d6565a1e9b8e88b9af2f0db9b8cdc0e788999135a3d182563da7f94a9826bdeb9c7254f738d425621eb4a165f29d0a1f80691358f10e83fa63db849c4ef8c4eaf46d4329b3dd63923984575395741047fd9d002d4148c8613b425fcc983a110341534d7d9880eb908d749b3e971ff58d24168c970310ffeff275c5f3eea87714a2938a3f7a06bca44ee27a9267c24f4844ecedae29054dc46", 0xb9}, {&(0x7f0000000140)="2396da5fc6ace99c1a5976008e6d5b3e4b8bc35ec8ae835ba0cffaaeb4d8bc5cb5ccb46407df0710e14d5c53cf145fabf2cfc31e87dde51608acabc3c19b5d8ad941fe91b6981dfeef05ff53767109e23cfb8fa1000cc04e7af4775c03c6b4e83d6c091023d01c1e21ef37787fce302361ebba091166af642a213c2d9409e0ffededb4abd8f30dad612ae59a7347a8adcd01d31fc8303ee6536ed13d7b592add15d3e094724ae80bfa7a3166cd4c957df983365c26ea497e0729d9e2ebe5d3e970effada38843a0dc119f2e07fcb14334ac78000f9f7f37199e0dd55c5e96e8f1fc984cd397271c2e3578cf1f93269e0a3df4e7f6ca82a01ac8197", 0xfb}, {&(0x7f0000000240)="ca6c09e628690625abab0e15c6c3e93df6f1e6be654ea40275adf47a97b578f6d7201b740b63ea50b518", 0x28}, {&(0x7f0000000280)="1073b4e29a7a62c6859b8c0f1121cc38", 0x10}, {&(0x7f00000002c0)="e8041d4c132dfe7f3be9ce330c2eb8eb9b6c8d8cc594234f009141584059be8369c79b4c99bcb5d193d28d4ce5b447ab12c25ba8", 0x34}, {&(0x7f0000000300)="5b23f16b7fe4927933b1ef65f10803c1918cae8d225f547f98317f385af43a3577", 0x21}], 0x6, &(0x7f0000000c00)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=r2, @ANYRES32=0x0, @ANYBLOB="000000080000000000000000ffff000001000000c16ffc412ff355e6882f735ba4d2bee06dadff88b53eb093b2cc03f3f558a486627cef0f2768f2c8654acca7473c70e700000000c20f995ba10bb7357a1efdcb20ce2703bdb4", @ANYRES16=r0, @ANYRES32=r0, @ANYBLOB="2000000000000000ffff000000001b00abe5d2289495efadda9db35607847eff275783b18cdc3a5d663b223c788a901f0b71e6b022e817c4286ed763526458a509b474620180ccb5ed0b7c472ab04846cc3cc8a4b00b05a28b23dc8e2f0e5c2cfdc7ffd1c8a0cf8b1e832b143647789853e307edc65d77332bd176d20ffe4e4daf408c803092a2df8a7da5ebb6adb2fd84796a3a1bd63b78471747f9cc834d3415da5d22f1f568bf5b891f63779c377403410617645118b137ea81a6548d3ab8e27ff7359daefcd1741e0a751995fb0636ca4fb67284bfb934b1dc2226ca6ad70ad2e6189d588db40696c0e1958b27cfab256d42d99ead78ca2aa440876738a45498cd9cb09e0e3ceaeeec0912acbeb0c3285a8db85f409d8c8fc04f0917d0b344c39dee294b331805610e3ea4b1d346d4f3556763faa5da3d5dac41411f19cf701d2562ce67eaeddc7e61d6d0d25b089917e6894ca60d3096aa96554fee4877ee3ac7f97c78ace1a77438eab609b846b83287a7ea9cb562e1000000c8541df7052283236cd26878fbe970bfa79b7bc1b21d2a05c3acfcdb5cd68f0803f7eb2c30d0191e84627b8c3eb9ca3279ff582e716eebadd5f2f84bb7c4fa3a8a7b517c81c07cc4468fa024beb761b4eae019275cdbb59f13fec49f276a95cd", @ANYRES32, @ANYRES32=r6, @ANYRES32, @ANYBLOB="000000001000000000000000ffff000001000000"], 0x68, 0x2}, 0x5)
getdents(r0, &(0x7f0000019200)=""/102400, 0x19000)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000680)={{0x100, r4, r5, r4, 0x0, 0x1, 0x8000}, 0x3, 0x8000000000000005, 0xdb9})
sysctl$net_inet_esp(&(0x7f0000000000)={0x4, 0x2, 0x32, 0x4}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x87}, {0x20}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000001440)={0x201, 0x0, 0x7, 0x3f, "64e904fbefd2adabb1530300000000000000fc00"})
writev(r1, &(0x7f00000013c0)=[{&(0x7f0000000100)="62405a8536b9578517006f", 0xb}], 0x1)
sendmsg(0xffffffffffffffff, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7afb51e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0, 0x811, r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x3}, {0x3c}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000480)={@random="1b329ff120e0", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = socket$inet6(0x18, 0x1, 0x0)
connect(r0, &(0x7f0000000040)=@un=@abs={0x1800, 0x0, 0x2}, 0x8)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000080), 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x11, r0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
pwrite(r0, 0x0, 0x0, 0x0)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
r0 = shmget$private(0x0, 0x3000, 0x0, &(0x7f0000826000/0x3000)=nil)
shmat(r0, &(0x7f0000ffc000/0x4000)=nil, 0x0)
madvise(&(0x7f0000ffa000/0x3000)=nil, 0x3005, 0x6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000001440)=[{0x5c}, {0x74}, {0x6, 0x0, 0x0, 0x661}]})
write(r0, &(0x7f0000000180)="3338f1a4f1584c82cf26e26aff11", 0xe)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x10000000002})
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)="de6291a8a8ecc6fc7e7c11f62cba512e", 0x10}], 0x1, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000140)="74e201887d347d495fd8ac509b692e", 0xf}], 0x1)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000340)="83eca8fe3800aac72d4750a3422e", 0xe}], 0x1)
execve(0x0, 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f00000046c0)={0x0, 0x0, &(0x7f00000035c0)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x0)
getrlimit(0x3, &(0x7f0000000040))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setrlimit(0x2, &(0x7f0000000040)={0x60000000, 0x60000000})
mprotect(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r0}, 0xffffffffffffffff, 0x1}], 0x0, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000014c0)={0x0, 0x0, {[], [0x0, 0x0, 0xfffffffffffffffe]}})
poll(&(0x7f0000000040)=[{r0, 0x40}], 0x1, 0x0)
kevent(r0, &(0x7f0000000080), 0x1ff, 0x0, 0x432b, 0x0)
poll(&(0x7f00000000c0)=[{}, {r0, 0x40}, {r0, 0x40}], 0x3, 0x0)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
r0 = shmget$private(0x0, 0x3000, 0x0, &(0x7f0000826000/0x3000)=nil)
shmat(r0, &(0x7f0000ffc000/0x4000)=nil, 0x0)
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
madvise(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup2(r0, r1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x30}, {0x1d}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r1, &(0x7f0000000300)="977fffffff000000000000000000", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='\x00', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='H', 0x1)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x14}, 0x4, 0x0, 0x0, 0x0, 0x0)
msgget(0x2, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086334)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}, {{r0}}], 0x0, 0x0, 0x0, 0x0)
r1 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCFLUSH(r1, 0x82907003, &(0x7f0000000040)=0x2)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x400000000094})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0xf578, 0x0, 0x2948, 0xffffffab, "9190000100120000000092ff0000ebffffff00"})
r2 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r3 = dup2(r0, r2)
writev(r3, &(0x7f0000000180)=[{&(0x7f00000004c0)="b5ec731a3767dd57ea64d8dd52eb11efc94632bf7b44bf39f51fbc7da81ed82b5c115782a1f01f0427f08eb10c5fefb16b78cc6a213b8e6cb68d", 0x3a}, {&(0x7f0000001540)="51af12671dd7b087d7211a1e3dcf6ce2c903cbf726b2b334218a999b77b0dd0668e5b65b77361468a91259bda82926b6965bdbdb2ed0ddf818e6e5a52c0a3a194a670f26c7925bc01ec0e5ecacd3e42ca0d8e1f1d8a23d0966229932c8f3108331b72f9fb8e39d047eee6f0887b3adfc49ec1c88fdc3ac63e31b40bb9337f0992f9e0c89f5a8c4ae01cd706c3e453625783bbc0afd34affd08ca7a069e0f7ca15b70b4567d65f11f717ccf1104e6df29eac6011f8e7e89034d4970803faa40454a7d57cf7beaffdc5f9f02858bf26ced2345257a472044b9364224eef84f08c90ac2b11b19161cd4804174ad32c702850366f8b1d049ba7d7bc288ba59fe15e39bddf68481c9847a1bc8a09f8470ad9677deb1bd84df31edc3c92c8bb2fa59ac39cfc8e25e101d8af43c2d175ecbcfde3fd030ce9e98df2b7f55ba9e4f5d7e0429d1332bacf5acbbad5c395a2f6b10c9ac4e9222f343a3ab847c6f83fada19446544c62ec1bbde637ee576c9467da16e6bfc1fb746ddd7934443a487202ab701b5e9ae9f5913bd31bc157fed9316e6431a8e67b02bae0c6eb008d45106fa4154483b8da14692661ccfe4a00c8e8720e7ce264ebc33e1cbb0c36a9d852b298b65110986059765d6222dc40d9c75c9f265d593c92af88f59bda924fbb9ffb9e2f28d2a2df1adb5e969a9dd2eb95b5d3412b91a36e8b151366d51fc40f134ee178a21bedbcdad323ce60e1e76d78f96849e67225b3f3a6d3c9fb283a5f78ae9488ae962efa087cb12f06808896580d2b6d75558a8f43e6f689f2ce45c313f4e5981eb9fbfe2391664494dbbe32655dd55b10c5ac770d0accbac768fa95346eec46279bfb6aff9a4ca7bcc9f9bac5267bce348313cece075f000ed1780ed7923830a53225797ee5b4580a59d014db047e04e5d90c4dc6f370cd517a1bbacf8ce2f31aa2ab42f42876e2a2f95f48024ea26e22c2a2d6543849ccc3f681669ff9e0a519efec506a0d0e6499f32722539d826d4512d9284c944c8b93e6e80a0d365622104e21cbed3bc3e5b8b1a9139fe36df5a2e49342a265cb2f23424e4cdc9d8ff90836d70b44da370a8ea210e45d71418015022de6a4c3636892f7f9fb582cfdbcf1cbef1d729394a9c3eb40931e9f6d66cd00edd37c1cc8448338df681b5899bc09a0673c10a5a44e8e1a1918ef942d45de7e351902bf493df0cd6763d246bc0f08f03df493a5527e775162e2ec4f4843b412a7d3b8f2b918e44f92a4da6ac13f6b311139afdfe00e43c1a2fdfb94902c3da83f0f62fcd86c5d55f3c50aa2e4b0a1f90bb70d11bf2b93251fceb170898dc803c4ca772e633f2a79e19ef5a3d2f8c26f258ef2122e71ab2b5325ce6a00c764ebeb60be477632a8c43b9934ff1dadbbdffcdec98867585cfbd5b14b6baa97daf6b92486cdf2bee635f6b9734408a2896bbea7bbd8c7981d6d4307254e9bfceb6d129a4961953331287a7396f6379290bd72876cf2259634e3d1b147e7434b162e5e9ea6783f6f4c0396e08eacbe96396feff8aecaa352e25624752dfe99c54dbd0ef241241c896e52293c67d10ccf51bc3b1fec267daa2fe82ad05d3a9092813eb4219b677f4d96a1a0eb9180c7f6d9e31ca0407a11cfa96ce5faa205c84684e5c05520a659e2dc9984c6e2e13caee639a662a0c9417d380b986912d00df5864e7018c1d9dc86949dca543d9db6b119b36ec464b3d620092424b6c56172177be62df39641222d53133a3ab806aab5f98a556a46629f809c78e9169da303f376c82dc3c23a45c8c278a6954e2e6eb53a7649bd5d0db0a5a8458e090815a23b752c4212eff5959b9eaed3e2f105e3b5ecc7bd8c9747c62f26684ace0129637ca579d5cf1ba98dc97d2cfdeef7ae19638937fa3bda6a5de820f52726d3eca2617adcb2826e79f9c6a520a2f102ade791fc14c843cc783e9b2b7f4676bda95121d18fa2dfd35f9f10a229d3830d5af7825e705c26c520013328e734bf8b0ee784f7b7b688bd18be950878c4f1434bb1d1e6fc21e10ad4e4e80ce5b2746b00270b1d1b42a911ea07423a9f7f7abfdeef06f5c44303be144843d7f780a69375d17ac3b19727c72ddcca7f9bc90f119690ed488d2fddc3765f65ff061125df5832e86e3899b8572edefce6904de3b97263a5ec727f2ae57165e8ede2476815d7ac36c90f40c0dbc03ceb1f99d473aceb1e749aaaab34251e7740c6e1c4a25d01dd4891c8286bc41d8a6ef361f18fcd1ec71f5ca2489f4782f1dab542de394a9235e45a426c8109cd29442e17ae9a7adc8f2bdd29746e5b36120723e837c83f9eaad2ff1ebf01b551ab7fb395e2ed7ce53be1ef44070fd208e5d40473db1b7a50a60db0bd0ef2aef49f4d46cfc7b36c499520f4d7b05553607ba0fb2cede2efed010f8ef5ee6315175e434a26121a5577cf88c7483b3edf9f94272a657fdcb540716045a9e5de6b2d51dee578e312276b6cbb5de061e791d8ed762755967b04e9ed25d308e240742d692c0f83ccf4070bf71912049b3a81a89c06f88f0960c3c777e9f45c9b30f4b1f7fa76a84674f5cc88c5fa27c3a574b1e0360f4440a9e52ef28a5ac89e3185473bca63f11c8ca8f7c7d6c2f7226ede639e26e7dc788f32c1024de78a760ac98d4bc4536be82c57b957705a93ffb98eba72c189bc10b6b01d0804481f3c5463f0c1a0b607db38de1354979aacab46497b80d1d80e63f406d061e16bb50322a95fd262f10252d2c983b43f45aae8e96d0cecb0326df69ba00ebb0ffda76a2edf4d3710d19e1de090d77f0d145749badb43a279b280accfc3a699a1087a281e8e7eb03640fde87d27acc2cfd0a1d0bb0e7c608ac61a386e111ef0547304692ef0507de5eefc46d9c6c6cbd421f4e9ed1569056611b1f838042e17143454bdbd2488ead6a677f9b93fb2ce7dae375049554cf5dff1f75ed9ead7475c77815f8ac3517c5fd1d8436240860c4b178aa651b7d4bd3ac07526ebd4ebe0260f5cbf899dad5084b725c032019666fec056a56b8c2f6639b69ac3fdf403f0107f9b54db89bb95826b6e2fcd91ce91a2aef3536162eb3198927aaa283aab6bf65fc3187f63d3da8443d99e2102ddffbe93a4545aa5ab3a97b5df3bad750f681e71a22bf2a686411ac7e0f861782f3a88397215b554f1546b0909664d8f4baa7e2bc358ff1c86c3661fa56e29b4ff9878e51f79fa177442c4aa2c1952ddddba737cef948faf9dc9a74a8ddd94a7b64fe53c3369bc8f7bf17902dcef26c6d7c65aca00580ef473ac5f9c33cc4dafc3b6ee6c6f839d5cb8b0053c1644aef9a11314279405cb76a71beb48d51986455782953cd968e163b0c793a56843585c2dabdf43f9cb1a7f1100bcbe9d692df7dbf4f22aaa97413229b45903b08693f989806b4b5b97aa2d7429dcd1fd1fc9ca8cc9ea484340d98d69e194c6ac1814ec8458fc7b3d34d8b8aeb59589bf07432ff2722efd8231f6d301fab260bc58c0f2151bd73919bf2dd7e16f8df39dfb9366cc7fc2b2716bbaad1957dac6e8a7b88e803382a775d689a626920a62d4323460e3d3352ac719a26aad68ab74839974fa16e0f43df8322349e9d743c70d0ed2e6920cf02e65c9018c9c4e0c0541297da352c63d9c965248f9c68cc31720cd8ffe45d4daa203bb3352a6fd6ee049038ff44964bf58847e120c1cef010314937c5bbc70f3b6085f0437d731e1f581333aa535bbf5a9a5f86931e196b69fec421a1f17f1cdc43dd6e0838487127eb6d56d5c1ee02956e867cbca1cf05ac10d69c646c7f73e92ab94ecf723541bd430d70a9101d9416b97ce143af9b9b37d1a1e589af8cf6077119eecbd75e9860aa0aadc2f0028818bbd1bcfa43386a1e830ee6a676913dc3dee753b4d3e1b1618c79bef9756ecef6e075cf56642abb348f6b639771032d2860ad74964d7bf24750d0c7cdfccda330843368d764521ac7502b4ac52f9d584a88242d7ce84818d12f2ba73d25ea08ea25a687f97dda715ed0da291acedeaffafb25ba73a4e2195cc7d7e5ccba8b400adfb02c7ba161d0d144e62b88f8cc259ced28e95965ed1993d2c77614a063ffde35a87fea1f17e8c6fd315eae0e52ae829136287d5336aa9937f1308e1d572bbde2c3cdb542fe423463b04753102fd4c98ea75b4748a222c7115bc536769dc1ed2a8770b94130e9ce0fdc71c5213a7161ad48931c4f29d6f2bb0627a7636c8136a423c1e0dce32981363de92a823f2533c22fe4de9588c9a626ed4e7f3e7c46f27c59892bf671afd032881667c61312522aee0ea5f57e7726f510b92bdd00cae1fbae39fe82a7d48e37c502c7f51fbc7c53a3a198f61079268ddc3a6c16ec11e9e67a02ac93e02752b1e961130521153001e050598ffa86e0e94b63121d0609265764c01dddc58846a0eb1ef309eedeed7b7ad6d6522c18190adc36ccf7080f0520105d88f6d92a8a63bff6df346f66e63ac6c049af00d83cd9940c94da722c78e8b5c6c4e620671ca50ff114ec6a014cf895f8f82419ec46c59caf8bea5c255c116fc81d1638b35410482081fe86d921721bcaee2d5f5042e24386c520e1940bc133dcb236bb92d5b6dca719278049c514132bfa294e0a15ef8d12ab1885d493b93eec86ae9b35d97dc251bafeb26570accb28fb60382ff3dda33127845a2acccf0b31be4191b548c8db9969b8d3083cfaea79105d1a077e98e587f2c97d37774d0f9ee4d1612efd357482b0fd54b0f0d13a2fd68e4542b4cc70e3d782d4", 0xd2e}], 0x2)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000002880)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb01ed9e30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc70420805cf7a98997f7382231e15558d51166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc937819b79d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262e00666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4a51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e04500e8aac29b19583f45701e02d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c698464b3e1b4913e1b4de8edc29f0ff40afbf964b21bca75086efdf335ca5f82500009e4a1f741968604e8a26a29246a5f865a0a359f28163c8adc1ced6bb00a7052c3ed4eba2d068484cdbe09030b8ee74be1e3b0714e111f8dd958d", 0x10d0}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e6779919279694c95602130a1aa2e9f3bf2bd2b0d05ccfb79c5638139aaffdf83aaaa49a888361b83e9718877cf5a4a4726db4234f956f718f755198537495755a2bd3654cefb74ac928bbeb6c800178ac6e30250dd71b38129dfb0cdaa7c1e11d67b617419ee1191781932ec7c8bc33abe0c01f433dbed43b4bc588453320421aeef8d6c139859e435253d2685a8ecf0d2e389e695c602db3614990ec93b86e1661f4ad15f388c1", 0x3c0}], 0x2)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000001340)="2abace18112030b738b1958303ac3fc1c384e67774d4bd49660d81fb4e2e9a19a972ea508fd17e3d0a932587a0980ddae58d2709a47c4986c8e74ed0d85edc5aca224a44bf11e4e1a51c0f08aab643577c975bf1928b38f6a67e0c5846b4128483e6275d201fec16e9147865784c167c704c6ae0980bb84e8dd18d6f4c83abaeed4ce23fa84033c78e9ce083ed4b575716841fd249fdb120f15b3db394bd37d3ecdf5440737d32fd4d75ae88ed351c1a2c8b9fe3a7773a6c3c322eec1e8da0864d79d9ca12a609b61fe0f6f8b88324f93cd6067a05d71c69804f5f315e320887de9e7503e2645f1d9a453a0f770a109347ba8e025f6654809908b3d640309334db564b52daafcfe22cde8ad112ca5fe8d81a335656c43555aed4ef5af976b8a30d", 0x121}, {&(0x7f0000000080)="051c92fc1af91e51429a03162acb8ddc3a8f9d3b588cc9073401033d1119b4b141df99877a601a4619f18708a668199c3cb370", 0x33}, {&(0x7f00000001c0)="d19e767a41270df7efd1b0a722f3051b176adeda300f91598c78cb7aa9f36b6b570addb4ba0d", 0x26}, {&(0x7f00000010c0)="37e9fa0141061db61b1ed10fbcbc40cc1306795d552e7fd645b1c8b46d209d6ae2a5de39d5c69227234925c32c602ba5157794dc1dd2ddad84455ee91a95b2f0cf1ed9a285c75b0000897a04c9acfe9c11baf3865149814461b156130a7dc3d86b407414fd61a1ee8dc9076d494132173ea614d4ea3903c0560b7e8f2166eb8fd7c11b3e117d84cf4ffb4dba38f98304cfd173aed6296b5d71104c69396100450e6824c3fc19cf9b736eaa3e33ce3b82b81e27b25b92b6f1262ce8bf8ea5c5f8e6edeb595115de33ea233b8d78750fc209c9e7a94db407751f2cbf5f99b5649557393c3de37f1f302baeb0932f1edd688f50b1dc13f962ea86d03bcf0810de0da52ced341260965444ed62d151fa76d2d42c03ab7e0d", 0x116}, {&(0x7f0000001200)="02f89c755da165fa2c5b12bbb9a6a7f01c4f715a5010d60e943461faf212f6c801b44db43cd4eae3440ebc20d8eaed033c5472a764c093e304422f60132ed5961f1656b310a45b2821029ce0eb4c53cf6eeec8c5d342bf4f9bc6992120db74fbd9f3dca2191fefbc3f50dede6f3bb218248cab8c2d7fd147e1e6274fcf5cc17f3b4f8bd7a7049d0efb8ba8e4571fbe8e02ab2f30a9540bd1e9e0bc8fb28c769265fbea98b5bae66521ad86676fbf3f262b8577dbf3f1c8cef8f4977f32bd8882f879d15e3dc0d66ae5b6275abad0ac8097485afea6d24c6e99d1e805c1fe4ad3ee5729213587ef7468d90afa43e6f91e1845618615d5f5fc434ee97fc7453bbd5744c5720a", 0x105}, {&(0x7f00000027c0)="d299f185fc18849566dc679478f0e005b6e1cb9789d657605107fdc0c92368f9a7f783f22b8f4fabef71b991e8a0b0cb588da78a836d11135e1d5bdf66ae22a7ec957dbc6fc7288044e9a54d9602006d108fac090660ee5e767441af0263d66924a4041cb22cdccc0cfcd76259f2382e9ce969af7c01", 0x76}], 0x6)
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
r0 = open(&(0x7f0000000640)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x1}], 0x1, 0x400)
recvmsg(0xffffffffffffffff, 0x0, 0x0)
close(r0)
flock(0xffffffffffffffff, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
fcntl$getown(r0, 0x4)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x10000435, 0x47, 0x81000005, 0x6acb9789, "0d4300002c43568878a0083639eaec00000000c4"})
write(r0, &(0x7f0000000300)="2d93e7c098550fbc52f1228452bb93d6abbfc2bbfb2423fc7be943c574dc298e1ddfd82e56ec6a83389ac9b0510d4fa973f07b6f814b2a4ee60d6e357ea629092c99be7b129807336aeaa67346ddd7f6d914b586b58d98a0ac60d32f21b7d38d1244e588", 0x64)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x80}, {0x1c}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0xc0)
setreuid(0xee00, 0x0)
r0 = getuid()
chown(&(0x7f0000000000)='./file0\x00', r0, 0x0)
r1 = getuid()
setreuid(0xffffffffffffffff, r1)
chdir(&(0x7f0000000080)='./file0\x00')
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x8, 0x0, 0x0)
sysctl$kern(&(0x7f0000000300)={0x1, 0x22}, 0x400000000000018e, &(0x7f00000005c0)="f4e469e1416d4c825772f597ebcea58037d338159133752d73e0570ba3ad247f73a33585cb52d522249048fb5b49c6eca1a86e96c9aa8cc48c7c21fed028bf2de528b9578b94179a9e907d9ddb4ffc04bcb05a5d09094c643d3538792d1d5cb4a5cd93643525f2b0f72ca224f2ce59d2713722f248899355c4e2e2f9c420eb19b06939f8cc1849", &(0x7f0000000180)=0x87, &(0x7f0000000240)="935b45", 0x3)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x1, &(0x7f0000000380)=[{0x7, 0x7f, 0x20, 0x787}]})
ioctl$BIOCGDIRFILT(0xffffffffffffff9c, 0x4004427c, &(0x7f0000000200))
sysctl$kern(&(0x7f0000000000)={0x1, 0x10}, 0x2, &(0x7f00000003c0)="8211a36bafb120daf58e470488832dcc76ff2dc6fd3587b941e1d0061120a735e62943039431a5b66b56f1ce86cd655d17bfc214eb8e05123188dd5df76b8d97f20fec544f45c098dc24d0e33312666f9d4d6dcaef3aef563903588b657e50c906de55898b431422e58588235664ef2354a6793786fb41b7d0b9d2d962ef988cb43f213cdfd92cf6c43ab4e1bb4bd0c1e6809f534bea71b377a1217fe3ff2f91", &(0x7f0000000480)=0xa0, &(0x7f00000004c0)="4aab870d3cfbe9625dcaa53061a518c4bc008186db8fc9fe1074e498a287542835598354f4bc51453b807c9cccbe836f109c28811416d6e4ef26a310a8aa51862f9abb2af4cdc531b226458f8148ba27fdb71a6a065e3233fd31d4845af500e8252e9d6a955f42e3e96d6ecc032a30d6956daedbac3288be12410b4cc559ddff9860eceab973e1427ae7345b45ef46ddd89b0c7b650fd8156536b639214492d52fb79126344d7736a72ebf1916f76ea15cfb3dcf0854fc9b47100d5ff62570c3d8fd6709", 0xc4)
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
fcntl$dupfd(r0, 0x0, 0xffffffffffffffff)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x4, 0x10, r0, 0x250)
r1 = semget$private(0x0, 0x4, 0x1b2)
pipe2(&(0x7f00000001c0)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x4)
ioctl$BIOCVERSION(0xffffffffffffff9c, 0x40044271, &(0x7f0000000980))
kevent(0xffffffffffffffff, &(0x7f0000000840)=[{{}, 0xfffffffffffffffc, 0x42, 0xf0000000, 0x2500000000000, 0x2}, {{}, 0x0, 0x84, 0x40, 0x5}], 0x8, &(0x7f0000000ac0)=[{{}, 0xfffffffffffffff9, 0x0, 0x1, 0x7fffffffffffffff, 0x400}, {{}, 0xfffffffffffffffc, 0x0, 0x2, 0x7, 0x4}, {{}, 0xfffffffffffffffd, 0x12, 0xf0000000, 0x0, 0x8}, {{}, 0x7fa36c34f9ac6e15, 0x38, 0x40000041}, {{r0}, 0xffffffffffffffff, 0x74, 0x40, 0x0, 0xdb91}, {{}, 0xfffffffffffffffc, 0xa2, 0x1, 0x4, 0x5}, {{r2}, 0xfffffffffffffffa, 0xd5, 0x2, 0x8, 0x40010005}], 0x9db, &(0x7f0000000bc0)={0x1, 0x100000000})
sysctl$kern(&(0x7f00000000c0)={0x1, 0x1}, 0x2, &(0x7f0000000100)="8e40a78dcfe50284d5d5c343dbd340e832dcae51442a30519354d977dbcde1e06c4057d0befd082cdc2e156aa7ccf6dcf9fc5ceb8c7de37a61e2d5bb1bcf9fd6a84c9f95a940da593e0a7852c1609dd4ba187ae907458655c70cbf331c45f80300"/106, &(0x7f0000000340)=0x6a, &(0x7f00000009c0)="56464d8b23fb280b2313916e5570cd62af7fd2667f28a91a028cb2afc4c6094ecab395705ae07412cf73fdc7de9dfcd636618ca9e658f3985a5012cea2ca6ab563fe8278f11dcce6125e5bf2a12c94327224da23c5259110346a4b73da70f4185fa0b8af37b0e07a38224969da4597d46afe76e9cdaa8ca4d5689fd1b0937eda8d61356bbad3b15ea0c58f61481eae11b0ce69e9b51601b10fba939bbf585dedc4d16654606dc1027a9347226537fb80fbc60a8ed630b9e7597af45de69291b72a30", 0xc2)
semctl$GETZCNT(0x0, 0x4, 0x7, &(0x7f0000000c00)=""/254)
ioctl$BIOCGSTATS(0xffffffffffffffff, 0x4008426f, &(0x7f0000000040))
semctl$GETALL(r1, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$GETNCNT(0x0, 0x2, 0x3, &(0x7f00000008c0)=""/151)
r3 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x1, 0x11, r3, 0x0)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000280)=0x80008)
setgroups(0x0, 0xfffffffffffffffe)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
select(0x62, 0x0, &(0x7f0000000000), &(0x7f0000001900), &(0x7f00000000c0))
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x9, 0x0, 0x0)
setuid(0xee01)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="82029d9cffffffff"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
sysctl$net_inet_carp(&(0x7f0000000000), 0x8, 0x0, &(0x7f0000000100), 0x0, 0x0)
unveil(0x0, 0x0)
unveil(&(0x7f0000000080)='./file0/../file0\x00', 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [0x0, 0x7], [], [], [{0x0, 0x0, 0x0, 0x7}, {}, {0x0, 0x0, 0x2}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
sysctl$machdep(&(0x7f0000000000)={0x7, 0xf}, 0x2, 0x0, 0x0, 0x0, 0x0)
mmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x6, 0x10, 0xffffffffffffffff, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0x81946467, &(0x7f0000000240)={0x0, 0x0, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x84}, {0x48}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)=ANY=[])
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0x3)
mmap(&(0x7f00005e3000/0x4000)=nil, 0x4000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = fcntl$dupfd(r0, 0x3, 0xffffffffffffffff)
r2 = fcntl$dupfd(r1, 0x0, r1)
write(r2, &(0x7f0000000100), 0xfffffe5d)
madvise(&(0x7f0000400000/0xc00000)=nil, 0xc00000, 0x6)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = socket(0x10, 0x8000, 0x0)
setsockopt(r1, 0x29, 0x41, &(0x7f00000000c0), 0x4)
sendto$inet(r1, &(0x7f0000000100)="b8707e9825dc608e546d490827e3138a106617db51453ceb81ec2632d03014bf2f106b543663a55f1e4b498357d097032c3bd1e1c84a459ef7ff6d16a34b010fc2fc219dcb349357bd3d3d193a76c3f7372c6852af0f2d66a33e069bcd2b76b972c9f457aa08dd97d55a4b56d7562e1228c36435ddd29fe94100bbf6f6213f8a9b6737ae04e766", 0x87, 0x404, &(0x7f0000000080)={0x2, 0x1}, 0xc)
r2 = dup(r0)
socket(0x1, 0x8003, 0x0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0x1016, 0x0)
r0 = kqueue()
read(r0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x4, 0x408})
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "104ce467af60b403184700dd47c8e9d209670991"})
kevent(r0, &(0x7f0000000040), 0xffff, &(0x7f0000000140), 0x7fffffff, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{}, 0xfffffffffffffff9, 0x29}, {{}, 0xfffffffffffffff9}], 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{}, {}, {}, {}, {}, {{}, 0xfffffffffffffffd}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000140), 0x100, &(0x7f0000000300), 0x1ae, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
ioctl$TIOCGTSTAMP(0xffffffffffffffff, 0x4010745b, &(0x7f0000000380))
mknod(&(0x7f0000000040)='./bus\x00', 0x280002002, 0x42065d)
open(&(0x7f00000003c0)='./bus\x00', 0x800, 0x40)
r0 = socket(0x11, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
getsockopt$inet_opts(r1, 0x11, 0x0, 0x0, 0x0)
munmap(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
mprotect(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f00000001c0)="c0ad0a48cfd24a8c25af9fa14d3c924edfc136eecd870911e83ac3517d9a2e1e52443a9d629363b4258b47cc4cbd5299dc02d49f894de65ba88076b42c189f47b576acf582a091babdfa8d19c35acefe9396180b249e213694d17012b45d03b48206a9bd63fd7e064575f2ef28b9c000b4f0fb71ea6760802afb6c4b68b13696a73b7e2c1f161a452726171051196a6357615122d6825a5504755cdd280c6400c5fc5877efd2cedb8267b6d2b0eb105d7c910838fff70f7199e87d178746646088195bfb9afc6023708e33a4501569ea39f7d9fa2b9779cc0ce7f944e57f5e1864ce8947c55cc5e91f4624fa1f7d2cc73ec39558c86b9636e51f20d7f3abe362116d96f5965e9fe8f735910688c151d4839383186b6cdfaaf9219394951705262e8186f8459104b535cf9b7d451ff981f3c6810871d75e1f3c864ef7fae39f29e90f029b2f1dbe955814158fa96659c9ebefe3f09eaa6f158bae44485bddc924cd2dbbc9a99d86ef2e3a48c9c5a5cbfc72746045b71e8101f785c60c4d2e42ca827843b8e088d8b602c7723080aed2f5a4dffbe805d82f016a286cadef6890c59df1fcdaa4b11e46d93f32f4cf3a82603d4b5ff01ba6b5789d9469c272aa0a6b371bf5a8cde72d23cfa3d13e53b704ed0b3eb5ec36422ab8cc833d7fe5b9e083b29f5b02bdaeadf5f2ebb6cad1c14fd83eaacb357d0f7a79e828c9f1d82e27ef82303581cae97cb42c36c0bd2a55e42c", &(0x7f00000000c0)=0x210, &(0x7f00000011c0), 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
r1 = dup(r0)
ioctl$WSMOUSEIO_SETPARAMS(r1, 0x40047477, &(0x7f0000000040)={0x0})
sysctl$kern(&(0x7f0000000000)={0x1, 0x15}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0x9, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0366dfde36ae61dba64800"})
poll(&(0x7f0000000080)=[{r0, 0x4}], 0x1, 0x0)
writev(r0, &(0x7f0000000100)=[{0x0}], 0x1)
poll(&(0x7f0000000040)=[{r0, 0x1cc}], 0x1, 0x0)
r0 = socket(0x2, 0xc003, 0x0)
r1 = socket(0x2, 0xc003, 0x2f)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000040)=ANY=[@ANYBLOB="8202a6917c"], 0x10)
sendmsg$unix(r0, &(0x7f0000000380)={0x0, 0x0, 0x0}, 0x0)
syz_emit_ethernet(0x7e, &(0x7f0000000040)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "a7d422", 0x48, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@routing={0x3a, 0x2, 0x0, 0x0, 0x0, [@ipv4={'\x00', '\xff\xff', @loopback}]}], @icmpv6=@dest_unreach={0x1, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "04420b", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @empty}}}}}}})
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000440)=[{&(0x7f0000000180)="946c3e175453f2da0c19b8f6cc633fe6ce5d5327725de715efe5f8f19e71696f9b4264fc7a2bc4fb67c3a89e3a0a3533227b179688c0d9dcfcba23457c382bb30bf7aab72b642051baf75b12e1cf7350aadb94bb2a35ed8b3bb6bdf14a0dc5730e80124332150f1d03", 0x69}, {&(0x7f0000000cc0)="f3e9ff6f6bb35a502577a0cedd88b105f77cb09d6318cc8fbe752851f7699e2ac66d196ededd640aa730a7b2b8fd533bf8a0e828b973cab5966aa9ad83ddbec37d73c9ee744ea2114065cb4e15c12c1e168c8e1939fa679e16c3610e47402c3961cf1937d2aa0ea6ceb6efd2a7145560f53cb35724fb86d53033fc1b2a6a28b3cb0c147f95f4e0e6e89cc984a725de5318b2967cbd777cea986ca0841ae9ca5fd68086a8c65d0164106e34f9f4350724c1fcf125caa2f89f6bf646e4138bb30e3116046ac6efa5c847ee66c7ea79ac75ade9f2493d6f342bd552ad0989b5fb95986d2abf1952efb13d7fca5caa1cfda7dd63854402001badf6d6093ea029d26c137769ba522b0e0f4dc3f14e9ff67eb7935826292f26adca11c79ab43f74f6bba39040b3b794b35fbc2db0ee3eba9d50b494a5cb0fbed63b08c6b5e73352e152459b7227d9e620f0ceb7f3a8456ea60da2455c3253f10a0a622b9087af27061e9944d51e0186eb9831bdbef871ec6bfc361b7b10dff4ce62c16b75a8ea012cb7e667fde16dd1063181c40f68f258575f665aa0cd02ba4fc3f37716a6e0ff616746910d02eb1906f79c47010dc3313d1e52b08da061bc2a65a3f63c914d1ca8650bd7ecd71dbc406a08da9188480f05dfd22d7dc712652aaffe7a10e1de80016d4e8ae6ad6c83ec6d56885da5b440e05fb5ea27211a2c33be248d29a6f066463394689f3c01f44a28736ae58a51e35ce87b307a7f3f7af321a2ca04ccb2e551c9dbcf55c8a27255e68b49a1c39e1d450b4e0cca1d8bb81caeea45caa1e8ee3f3edccc5b66d5ebabe792deb9374e29e45dc3723f0f3bb8a42af72216da6a88d79af7a2b5c7e5ad412f8103f96d10612cfd9a4ae13937cd15aa0e590eb66210163269508959ed44fe7692681e2da63e3d2458cf8fb65438879c9c2b729345b01bd4d23048151ca4cc8f28bec52b670c1e77f4d5ab47fc52b71cbafe56494def6b9eee6be7604a6f62f91b92c9e4a5a27c9da1bf2f10d3a491709e04f6bcf371c2d1ebe6ac13bcf24b124cc8feb807bdd4191fe507a324e96d85daf70d778e7fa0b6df3c92ed7f1c90442d58871edcf88a0730868d69defa93b51eb94ff81505f28dfe809a406fe4433a100891ee465f5affb3cd199338d79d2b3939532a789ab2dd7878cce36f7a6a592db5a275bde6951ca261115d66f098a1bd9c1d59602195f8d42d2d923a4ac8f2ede871b6d0e9406af31ecce4ee927852f71cb739e2061ccf8ddbfd99cdea2dd2d8bf474c710061cbc60f1b9818c480073d47505180889082c0caf7689d742855548fd4b802ecbebe1a2e629c0627af2ebab84575526e2bb8bbc9f4accde8ebe8da79f7c835b9eca1ba6a7cc75419d22364913bb3e5d15a62049ea09ce2d9d51335ad6de8144ad0292b8cb4aece0bb63f89626fdfb868251bab2de7f2bc2ae00acddb04d0a9cc9df3676ddcf3aaa982343a69ead53a845d96df1c5daa251e659c90b04298081be4c4ba7c48ada69a9441b521ea706f47cca98a997b85162545f4b396de086b59f968c7a099e70fa2398ee7575b93db04192a85419d1252b1190c0ba3266f3dbd0911284ead6c0c2d6c6bbcf2b4249b22b31b3a49e76e564479049c98df565627116bdc58f88952ea991718a3612f3ab13e3d982275f184c3ab12e6f67e9e7b65e554f5ffd93321429e1f786a6a3f40d986b660cff6474a48563031b32656d67f6bc6029d022b7ae392f941e8305ad84ceb55f143c36a0c5390648c82bfe1f222f6b2319455f4d46975713ea741b3992f1fa25974a1d0529a9adefd4f8ed6ecd7e019f15674e44474479a09232452c0146b8c5e4fcd27630da39ec7034e9078b621ff550e2d304eb78e03a1866d3509cbedd0ab668f04a0f2ace10ab3daf22d4461ec8e913d9f6e84aa1febc10e6d870c10d9a5598e1903a0e766e76f75378b660c4ab012ab7b0de56638c65b9b3c752f4a0891c38f1c5150687c734ee1fa83f62166e953dc1f46f46fb23defc358f009048ae3a83c6704bc43c6ccba181f16d0cee19ea980dce21d6569af0450dfc0115db9812292147dea348c08ea213b2880447ad42cb4e5dac39a4e4de9ec095cf34ef9a10036d016a7b66ab98935a15a3918b2640ddcdc2e8c5bcdacdcd4406a82a5b67d29fdf269fdbfc9011260439f57ee39d1bb11a8f701c9549743c5e145178f9be2e50342cc2f5d211400eb5a0bacb525f6db1622403531072f02b73462d218269ef6ab0f0924cac4b00e6f8280daf4185c5f155e0f563136447993925a53600d1a7683fac94e8f77fbef92b83924ff7746824d39c285d521d0d144afbbe9fdeb660ebdd19a7281f6e808f3a3585af40484da7136b7e6911d9e5ebe5142509fac0064d9f2448be823491bf72eb2e972cc2a7d8f4c0a3e4a2b1d284df42330fb53233bbcd28dfb29710cd5748e9cc92285082e8f9e41e955f07ebd0ebc5ddb9c7fc3bb9b0b2954c737e72d3f43306e5e2a51998988baafbfce0e1ab970539976b0d4dafe609fda9f41c42b7b4fa54fd69bb6575c1e35081d38346b52a01c260e4e09c8035bf04e32fb08be999b60b0de9238e5117e255e299f0a445bc4a6f702a769b9452376d09abe8aefe4a95758854abaf3ea80787f96096bf1c108b1a07d4d8dfe3067f8aa342e86d250806a3b6c51638e5ac86e10e9989b204152cca5b15ec714a9b04666239c251f61bd65dde852fa933f0c882976237d563efc6e452a9f6b13de112863c79cd17851ec95a43c2e3d3e034877efabd3e458887f3bc2c21826b99792ecf1a5329a935fd95b2f7aac66ceb7cab7f333efcca812e9bee7d1b449984be0eea965c83e60b791b27f4285d39fdbe2ff98060426aa86304d35f942d28d018d44fe768c89263acf40217a155a54b6e07d05444f45140a3c60020958bd486bd9086994252255a77a2161a0e37bbeaddd1f8b9d80e4f5c7f68fafa97f926fb62825dcc9e3283fe6b5fdbea966b5459674b0d19e469b5e2f4e2cfc0f0274b7e6547e3849971ccc3560609fd94e099ea72769c2ffcc724bb49598fad64f3667d027f8cc32fe99867a4dff82caa739de37eae6786d539fdd53c38f7e038748a6a3493153ed2631edd8bed7635e87ff8072ee67599a0b15640bac3ff5b156a5c138cd970666a6c37610c2a43a3a099a73f25918c1137503df421bc3581be1ba249f513c39ea1cf7bf4b6a43b2ec568ebace03c680c60427fdb1fae2522b42eaac92206de8bc50201ed93819c00123c062d90bcb8f2994b4d71b5dff3c759e50162643dca7765025b9a3a705838811874de7acc25d5913e330120555bde0639f16c9d731e6c284ad2438f764edd9fa191cf770d8e5aad7fde1b03a4f746f7e41b3a6364e5012637116aed59e0398c659d0d51b5c9c707e036e6c51876feee3170dfa97769cbd078a0ce71445768c0f3548e5f685226b3c43f44a2144022a33617f01adbb8036f7438486aa1c14c9bd7b33271cc8c75d5918f2f58a5e54b1eb862fe86b989c8820de66e475e7ac4715e50089a6dd819e01c604c9dd675599011e432438bf3c5e3894659ca8f9acab1674f931287ab25c687236bcbab06e52586a6dccf6668e1c977c78e9469e1e816980", 0xa11}], 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x54}, {0x45}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
r0 = socket$unix(0x1e, 0x3, 0x0)
sendmsg(r0, &(0x7f0000001400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001080)=[{0x10}], 0x10}, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000840)={@local, @random="0001000000c4", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0xfffe, 0x0, 0x0, 0x0, @multicast2}, @tcp={{0x3, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
openat$zero(0xffffffffffffff9c, 0x0, 0x0, 0x0)
recvfrom(0xffffffffffffffff, 0x0, 0x0, 0x0, &(0x7f0000000140)=@in6={0x18, 0x3}, 0xffffff72)
openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x200, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSKBDIO_GETENCODING(r0, 0x4004570f, &(0x7f00000000c0))
sysctl$kern(&(0x7f0000000040)={0x1, 0x4}, 0x2, &(0x7f0000000080)="3f5ff0eec8829248f82dadbec2400fe5e1035eaf649c53a3d9f81af060c29bb5164438893ad11ae168713af4094b41c700536a243ce9435489b8dba294854405752f3c7bd06ebdd99ce314ec45b54181a2ef892d1e321cfb823502b5b9a3dec5f71ca57b95fe7ac9081c2858499cc752772568e73cbe6da561a92f8fba88a52020595f57ae45d302876ee2c8801941b2f5b214cb1331b2da969e04a9bc50348fb13d32ad1b480dd7ab", &(0x7f0000001080)=0xa9, &(0x7f00000010c0), 0x0)
sysctl$kern(&(0x7f0000000180)={0x1, 0x29}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x1ff, 0x0, {0x0, 0x1}})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3e, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000001c0)=[{0x4d}, {0x48}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000400)={@empty, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @rand_addr}, @udp={{0x3, 0x2, 0x8}}}}}})
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
bind$unix(r1, &(0x7f0000000000)=@abs={0x1, 0x0, 0x1}, 0x8)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x123)
r2 = socket(0x18, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc028756b, &(0x7f0000000100))
setitimer(0x0, &(0x7f00000001c0)={{0xffffffff}}, &(0x7f0000000200))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x4, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = dup2(r0, r0)
poll(&(0x7f0000000000)=[{}, {r1}], 0x2, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x40, &(0x7f0000000340)="21dbafbb1aa3c6b55e2dd833444d40ff0ca3b92622fe04a06adc1c759fa9b308881e142b6c6200", 0x10016)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x26}, 0x4, &(0x7f00000000c0), 0x0, &(0x7f00000001c0), 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000640)={&(0x7f0000000600)={0x0, 0x0, &(0x7f0000000140)=[{&(0x7f00000007c0)="e9da24540f6ba6ae4d5bfbad21efeab706", 0x11}], 0x1, 0x0}, 0x7}, 0x10, 0x0)
sysctl$kern(&(0x7f0000000140)={0x1, 0x41}, 0x2, &(0x7f0000000180)="b7535ca2", &(0x7f0000000240)=0x4, &(0x7f0000000280), 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x2c}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b100050460000000000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebb3557699a", 0x4d, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x2}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f00005a1000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f000055f000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000fee000/0x12000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f0000558000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ff0000/0x1000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x1000)=nil}, {&(0x7f0000ff6000/0xa000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./file0\x00', './bus/\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus\x00', './bus\x00', './bus\x00']})
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000140)=""/102400, 0x19000}], 0x1000000000000135)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000040)=0x9)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4c}, {0x30}, {0x6}]})
syz_emit_ethernet(0x66, &(0x7f0000000000)={@broadcast, @random="0a64aa29e168", [], {@ipv6={0x86dd, {0x0, 0x6, "fabb50", 0x30, 0x0, 0x0, @rand_addr="e1f8c3e56d849c5e5d3a4931c7be0819", @mcast2, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, "174ec4", 0x0, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @local={0xac, 0x14, 0x0}}, @empty}}}}}}})
sysctl$net_inet_gre(&(0x7f0000000140)={0x4, 0x2, 0x2f, 0x1}, 0x4, 0x0, &(0x7f00000001c0), 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
socket(0x11, 0x10000000000003, 0x0)
ioctl$TIOCSBRK(r1, 0x2000747b)
select(0x40, &(0x7f0000000040), 0x0, &(0x7f00000000c0)={0x1ff}, 0x0)
poll(&(0x7f0000000080)=[{r0}], 0x1, 0x0)
mknod(&(0x7f00000000c0)='./bus\x00', 0x2040, 0x4f4b)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0xc3104222, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "04713de0af28a2813d8209b8d9f39321849e3c99"})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x200000000002}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x10000000, "000000000000000100000000000000003f00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = syz_open_pts()
ioctl$TIOCGWINSZ(r0, 0x40087468, &(0x7f0000000000))
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x39, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "f81d08200b0f05000300000000000000d2cad800"})
r0 = socket(0x18, 0x3, 0x3a)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000000)="5b5c73060becc431", 0x8}], 0x1)
write(0xffffffffffffffff, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033", 0x5b)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc110445d, &(0x7f0000000240))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f00000003c0)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ff9000/0x2000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff1000/0xf000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ff5000/0x2000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000fef000/0x3000)=nil, &(0x7f0000ff5000/0x2000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ff7000/0x1000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ff5000/0x1000)=nil, &(0x7f0000ff2000/0x3000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0xc}, {0x34, 0x0, 0x0, 0x4}, {0x6, 0x0, 0x0, 0x100004}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/9, 0xfffffffffffffef5}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
r2 = kqueue()
r3 = fcntl$dupfd(r2, 0x2, 0xffffffffffffffff)
close(r3)
dup2(r1, r3)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
setsockopt(r0, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
socket$inet(0x1e, 0x3, 0x2)
sysctl$net_inet_ah(&(0x7f0000000000)={0x4, 0x2, 0x33, 0x2}, 0x4, &(0x7f0000000140)="60289848b421a3ef3668c95c24a963c45bc7e6bd383059e6c7e0d7848085899a42e41a90fe90199a2fad67aa9121e5a08790a5630fd5c25880e5816b189952d7692b4e004337710afdbe5bbd4aac629d0ea7544cc81debb8a31b1e98b7fffc54565a10266e33713724515d3dcb6a27e0e8839f3a5466852c3e257d1ebfe635c927dadbb667345ce59804d4113b578d2bd41b2a26934b4961", &(0x7f0000000040)=0x98, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f00000001c0), 0x4)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r2, &(0x7f00000011c0)='\x00', 0x1, 0x402, 0x0, 0xff09)
close(r2)
bind$unix(r0, &(0x7f0000000140)=@abs={0x0, 0x0, 0x1}, 0x8)
accept$unix(r1, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000110, r0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
r1 = socket$unix(0x1, 0x2, 0x0)
getsockname$unix(r1, &(0x7f0000001080)=@file={0x0, ""/4096}, &(0x7f0000000000)=0x1002)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x4, 0x0, {[0x7]}})
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b1000501600000903f00000007000000331c13fecea10500bdf96ecfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b297be1aa5323edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af6300372a2102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c500002012051eeab71d89e0000400000000000420000000000000000000000000000000000000b604000000", 0xb1, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000180)=ANY=[], 0x2, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="380000002900000032000000010000005d04"], 0x7c}, 0x0)
sysctl$net_inet_ip(&(0x7f00000003c0)={0x4, 0x2, 0x0, 0x21}, 0x4, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x9, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xffffffffffffffff], [], [], [0x4], [{}, {0x0, 0x8000000}, {}, {}, {0x0, 0x0, 0x3}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000040)={0x1, 0x59}, 0x4000000000000005, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000ac0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
setsockopt$sock_int(r1, 0xffff, 0x1004, &(0x7f0000000040)=0x6208, 0x4)
readv(r1, &(0x7f00000007c0)=[{&(0x7f0000000180)=""/186, 0xba}], 0x1)
write(r0, &(0x7f0000000800)="18732b5a52c8e7c30bf5bd0f362bfe607617c0aa602be29752f887a23534ba4ae9e3e16e82c2dbe315832826cd97d5a95ec7a108f7d5a8e7a5ea8f86450b00d1825d9983f9c67c30bc2a3ed795126ed218592b176b655b12a0e1b1dc9b70b32d2d642e5982b058069737c3511313f38264b1bd234cca41bfd0b5898c4bfbd70d154520561ae02b9b3d35dfa4fa91e12eb5", 0x91)
dup2(r1, r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x87}, {0x24}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0xd}, 0x3, &(0x7f00000000c0), 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000040)=0x7)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000100)="3d130e5112d9e30e5e34041b67e55ed1da7f9aa624b388dadaa80cb230f3f3eb47924a02b53ad503b8373e940c64cfecf6e9bcf3099da0c24b31ae8cdeaa23242c78ea3353b487830d", 0x49}], 0x1)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x5}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x2}, {0x30}, {0x6}]})
syz_emit_ethernet(0x66, &(0x7f0000000080)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "7a8992", 0x30, 0x0, 0x0, @rand_addr="00000000000100", @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "7be033", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
syz_extract_tcp_res(&(0x7f0000000100), 0xffffffff, 0x5)
syz_extract_tcp_res(&(0x7f0000000000)={0x41424344, <r0=>0x41424344}, 0x2, 0x1)
syz_emit_ethernet(0xc6, &(0x7f0000000040)={@random="831ee5407df0", @broadcast, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0xb8, 0x65, 0x1, 0x0, 0x93c4ed541a00b09, 0x0, @empty, @multicast1, {[@rr={0x7, 0xb, 0x31, [@rand_addr=0xffffffff, @remote={0xac, 0x14, 0x0}]}]}}, @tcp={{0x1, 0x3, 0x41424344, r0, 0x0, 0x0, 0xc, 0x0, 0x3, 0x0, 0x101, {[@sack_perm={0x4, 0x2}, @window={0x3, 0x3, 0xc4}, @eol, @generic={0x3, 0x2}, @sack={0x5, 0xe, [0x2bb, 0x6d, 0x4]}, @mss={0x2, 0x4, 0x100}, @generic={0x8, 0x2}]}}, {"2bada6db3781dabff96a1e812670acb66b9dcc197a01282fa3bd0ecb48e5eda9ba523db058360adf1dfb6267a8bac08567d73d04831315a49dc4c6e8bb01d990cd68631a7c7e057113f0db9dbe1b758ba64684f35d760618143547fc284f212e3596acc508e87ae2"}}}}}})
r1 = msgget(0x3, 0x20a)
ioctl$WSDISPLAYIO_GETSCREENTYPE(0xffffffffffffffff, 0xc028575d, &(0x7f0000000080)={0x0, 0x0, './file0\x00'})
ioctl$WSDISPLAYIO_GETEMULTYPE(0xffffffffffffffff, 0xc014575e, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f00000016c0)={0x0}, 0x10, 0x0, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001740), 0x8000, 0x0)
openat(0xffffffffffffffff, 0x0, 0x0, 0x0)
msgsnd(r1, &(0x7f0000000140)={0x2, "9aca21ec06a919c8810a2ad66632cd8c5f4566a838eb8c5f557668bf9631133e8d419d29032aa0749c21ae4293f859a72dbcb823cc421b8fde6d543ee1811c74d213feca47397b7954c74c9a70264f1f226062ad367c560cb6d16ed5c3f756f5dd6f611cdc1f3b731ebae511715dc3142f0de1fae78fd71c54"}, 0x81, 0xe74fc6a4b81415bf)
socketpair(0x18, 0x3, 0x2b, 0x0)
r0 = syz_open_pts()
ioctl$TIOCMBIS(0xffffffffffffffff, 0x8004746c, 0x0)
r1 = dup(r0)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x8)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000))
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x1, 0x0)
setsockopt(r1, 0x0, 0x16, &(0x7f0000000100)="04000000", 0x4)
dup2(r1, r0)
r2 = socket(0x2, 0x1, 0x0)
dup2(r0, r2)
connect$unix(r2, &(0x7f0000000080)=ANY=[@ANYBLOB="8202719d7f"], 0x10)
shutdown(r0, 0x1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3304]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x80286987, &(0x7f0000000300))
syz_emit_ethernet(0x46, &(0x7f0000000100)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "fb00", 0x10, 0x0, 0x0, @rand_addr="000000000000000000f5ff0000002000", @rand_addr="0000000000001f00dd00", {[], @icmpv6=@ni}}}}})
socket$inet(0x2, 0x1, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x310, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1112, 0xffffffffffffffff)
clock_gettime(0x5, &(0x7f0000000100))
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x0, 0x0)
fsync(r0)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000140)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x140}})
shmctl$IPC_STAT(r0, 0x2, &(0x7f0000001300)=""/4096)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8002, 0x0)
r2 = getpgrp()
fcntl$setown(r1, 0x6, r2)
fcntl$setown(r1, 0x6, r2)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
r5 = semget$private(0x0, 0x5, 0x288)
semop(r5, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r5, 0xffffffffffffffff, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000180)=0xc)
shmat(0x0, &(0x7f0000ffa000/0x3000)=nil, 0x1000)
semctl$IPC_SET(r5, 0x0, 0x1, &(0x7f00000001c0)={{0x7, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, r6, 0x63, 0x3}, 0x100, 0x321f, 0xd090})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000000040)=@file={0x0, './file1/file0\x00'}, 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ffe4484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e721e40934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49bef3238d0b05c82c7ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae836cadd4c3ba30d79ee4c956121c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e6ac33a607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb033f52ff3063d96df138a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b895417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c07", 0x1000}], 0x1, &(0x7f0000001440)=[@cred={0x20}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, r2, r4, r6}], 0x58, 0xd}, 0x4)
getgroups(0x2, &(0x7f00000000c0)=[<r7=>r6, 0xffffffffffffffff])
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000040)={{0x8e, 0xffffffffffffffff, r7, 0xffffffffffffffff, 0x0, 0x89, 0x5}, 0x2, 0x3b, 0x0, 0xffffffffffffffff, 0x100, 0xffffffffffffffe0, 0x8})
sysctl$net_inet_tcp(&(0x7f0000000640)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, &(0x7f00000007c0)="ae02994e378bc8b9fbd69d28013d003bb2faee884d7d8d2c7c6d531fd4a616b98f7324b304c48eb9a00b05a159aa5ea70f53ca5e103a652df1667eaa4aac60df478d650d47b06e5669d467bf72a651975dec34a6d3a4fd85040d76e5fcbf319daea55015f40a173eccc09b4ee0a41ce4b03d8531c42033ddc6da828b7ea515ed8ae5f80b9b2f9a3858c6d17b32dbb757a8eada2e47608181318daea6579df2c04115e5c3144f21895a1f46c4b70c27618700f424506551146d2e19fbc05eddc7fc34a3b6d8d3bb580dd52451c1ba410a467d149e25d4e1046ba4853c0040896c5c04ab27806b9da2cad8830dda23a9ed42cebb05547e8e4d0c172a87c0d305fdfe4b2d0910f895a6ccfce30bab4886581e65187991ef7e8f37d9073a36c397e532a203e58104aa1a20b5d781cb24d6c9d00ab9b93b1dac52a539eca44e42835acac69d729c42a4ac7a445b2a3d3e60221083c0a4c372133c850380d983a1c6177e4c14732fc8ab1735fd9be4d3bb786299f213d69288245e0d45cea76240416a9809af397294f4283de8acfeb453bea25c671e4c95b95ddc07efa439b493f9109e0456ebaa63571ae10aaaff37b8d16d4c0b609512beee1afede3ebd111a8af1cf0e05b5dd9bf4482e7517ae65cf0b7bd9320474f1bed1cb618ad3c0bdb22d592ca424231bfb54203387d9629ed71495e4c0dff01eac8ab294157e5cccd924fcb46632243afdb424834b22279fa80407", 0x210)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x20}, {0x1c}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x1, &(0x7f00000000c0)=[{0xf41}]})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f0000003680)=[{&(0x7f0000000280)="c4", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x32}, 0x2, 0x0, 0x0, &(0x7f0000001080), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x7}, {0x48}, {0x4406}]})
syz_emit_ethernet(0x66, &(0x7f0000000140)=ANY=[])
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0xe}, 0x4, &(0x7f0000000100)="deb47e3d", &(0x7f0000000180)=0x4, &(0x7f00000001c0)="8ffc1613", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f00000000c0)=[{0x81}, {0x54}, {0x6, 0x0, 0x0, 0x7fff}]})
pwrite(r0, &(0x7f0000000140)="f94c4c49dfd685fbaf8a8d1a029b", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x4}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x1}, 0x2, &(0x7f0000000180), 0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000080)={0x4, 0x18, 0x29, 0x32}, 0x4, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
readv(r2, &(0x7f0000000640)=[{&(0x7f0000000480)=""/210, 0xd2}], 0x1)
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000001440)={0x0, 0x0, 0x9, 0x100, "ff013df7cd00"})
writev(r1, &(0x7f00000013c0)=[{&(0x7f0000000100)="62405a8536b9578517006fd633621277b6cf7e462413b2e6457e216295a2064c9963fb397e169d844573d45e557948c6d9ccee19f84b725d5bd691d6c27edd7d3486bcd0ea40634a4e3e03f9faff4a41b6a695063c7e371cdd442efcff3be83abbb10c377fd6c7b85e31082b2578191e06bb5d9e256a94086307dd89d401", 0x7e}], 0x1)
sysctl$vfs_fuse(&(0x7f0000000000)={0xa, 0x11}, 0x3, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000003, 0xffffffffffffffff, 0xfeffffff, 0x0, 0x0, 0x0, 0x40]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x2, 0x0)
sendmsg(r0, &(0x7f0000000840)={&(0x7f0000000080)=@in={0x2, 0x3}, 0xc, 0x0, 0x0, &(0x7f0000000500)=[{0x14}, {0x10}], 0x20}, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000580)={<r0=>0xffffffffffffffff})
getsockopt$sock_int(r0, 0xffff, 0x0, 0x0, 0x0)
r0 = kqueue()
close(r0)
kevent(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, 0x0, 0xfffffffffffffd61)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x9], [], [], [0x0, 0x0, 0x0, 0x9], [{}, {}, {0x0, 0x0, 0x0, 0x1}, {0x4}, {0x0, 0x0, 0x2000}], {}, {0x3}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
clock_settime(0x100000000000000, &(0x7f0000000140)={0x0, 0x3b9aca00})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}, {}, {0x0, 0x0, 0x0, 0xfffffffffffffffe}]}})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_INTR(0xffffffffffffffff, 0x800c5606, 0x0)
ioctl$FIOASYNC(r1, 0xc0284457, &(0x7f0000000240)=0x7)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = socket$inet(0x2, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
select(0x40, &(0x7f0000000000)={0x1f}, 0x0, 0x0, 0x0)
r1 = dup(r0)
read(r1, &(0x7f0000000040)=""/32, 0x20)
syz_extract_tcp_res(0x0, 0x0, 0x0)
syz_emit_ethernet(0x10d, &(0x7f0000000180)={@random="ee2b6ea85ce3", @local, [], {@generic={0x3c0a, "e5c5a9370c8ba50c9cbdbc1984e2f9bcf4c17cc14e1f62c647f6b4f698bd2fa48dcfaa84eb64468fdeac497bb70afb2ed3525a9ee9c52fda7f879eebed44f1a99760fcf6b0780afeee00dc63efcfe7ee722e0d1cf907471f61637d9b13626c7b0ed689251f0eeff2dd7f493ab6e8b8f2a84389e142c2657d20f7e38f3067ad5f2685a45a0b8fb2d64b7f0ac4dcb2ab61fa4f5ab1854de3f63bb0f6feafcac6ae0aacfb520cb829a4fecf5e26db1283c49adfaa59beafdb291f50d72b8cb714f355ccf82f7729eb0127daf31b4ac72b7a941800d087b1f20697c2b663144a7870189f8066ab76aa0f0d925521a9766190bcabadff43864b1183eab6f15cf4a7"}}})
r2 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
writev(r2, &(0x7f0000000b80)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
write(r2, &(0x7f0000000000)="e6f509", 0x3)
write(r2, &(0x7f0000000180)="09200909f1ffffffffff13804752cdce9c904451473f0300000029157cb4b0ff03000000000000705abf281e73d9b6338a02bf0a", 0x34)
r3 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
write(r3, &(0x7f0000000180)="09200909f1ffffffffff13804752cdce9c904451473f0300000029157cb4b0ff03000000000000705abf281e73d9b6338a02bf0a", 0x34)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x4ebfac6bbaf7949)
r0 = kqueue()
ioctl$WSMOUSEIO_GETPARAMS(0xffffffffffffffff, 0x80105727, &(0x7f0000000080)={&(0x7f0000000000)=[{0x40, 0x999}], 0x1})
kevent(r0, &(0x7f0000000000), 0x890, &(0x7f0000000180), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000205, 0x25)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000100)=' \n', 0x3)
execve(&(0x7f0000000380)='./file0\x00', 0x0, 0x0)
r0 = socket$unix(0x2, 0x1, 0x0)
sendmsg$unix(r0, &(0x7f00000005c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000580)=[@cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}], 0x20}, 0x801)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000022000214]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020690c, &(0x7f0000000300))
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
r3 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r3, &(0x7f0000000800)={&(0x7f00000002c0)=ANY=[@ANYBLOB="fb182e"], 0x1c, 0x0}, 0x0)
close(r0)
socket(0x800000018, 0x2, 0x0)
dup2(r0, r3)
r0 = kqueue()
ioctl$FIONBIO(r0, 0x8004667d, &(0x7f0000000000))
ioctl$WSDISPLAYIO_DELFONT(0xffffffffffffffff, 0x8058574f, &(0x7f00000000c0)={'./file0/file0\x00'})
sysctl$hw(&(0x7f00000000c0)={0x7, 0x4}, 0x3, 0x0, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
setuid(0xee01)
r0 = open$dir(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
renameat(r0, &(0x7f00000002c0)='.\x00', 0xffffffffffffffff, 0x0)
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x0)
chmod(&(0x7f0000000080)='./file1\x00', 0x1)
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f00000000c0)='c\x00')
mkdirat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000180)='./file0\x00', &(0x7f00000001c0)='c\x00')
rmdir(&(0x7f0000000140)='./file0\x00')
unveil(&(0x7f00000026c0)='./file0\x00', &(0x7f0000002700)='c\x00')
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x54}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
mknod(&(0x7f0000000040)='./bus\x00', 0x8100800080002002, 0x2a00)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000040)={@broadcast, @remote, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @remote={0xac, 0x14, 0x0}, {[@timestamp={0x44, 0xc, 0xd7, 0x0, 0x0, [{}, {}]}]}}, @udp={{0x3, 0x3, 0x8}}}}}})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x7)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x4)
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x5, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1003, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x1c}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
getrlimit(0x0, &(0x7f0000000000))
r0 = kqueue()
kevent(r0, &(0x7f00000007c0), 0x9, &(0x7f0000000040), 0xffffffff, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r1, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x2d}, {0x61}, {0x6, 0x0, 0x0, 0x8001}]})
pwrite(r0, &(0x7f0000000140)="c6358e4e0ccc42bd0bba499911b1", 0xe, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x200041fc, 0x0, "77917dfc71faeb904b40000000003f00"})
mknod(&(0x7f0000000100)='./file0\x00', 0x1ffb, 0x0)
link(&(0x7f0000000040)='./file1\x00', &(0x7f0000000140)='./file0\x00')
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000080)="33102b27cad177560800000000000000d51e567165315a0dabcc80b5e1e30900472f3ea9fabded620472860efd0dbd2653172281d5b80000003836c80dc6fbaeb51caa002d7e7fdec1de", 0x4a}], 0x1)
open$dir(&(0x7f0000001840)='./file0\x00', 0x2, 0x180)
r0 = open(&(0x7f0000000280)='./file0\x00', 0x2, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106924, &(0x7f00000000c0))
ioctl$TIOCNOTTY(r0, 0x20007471)
syz_emit_ethernet(0x52, &(0x7f0000001240)={@local, @random="e815dd50fb52", [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x1c, 0x0, 0x0, @rand_addr="feffffff00", @mcast2, {[@routing={0x0, 0x0, 0x5, 0x2}], @tcp={{0x3, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
munmap(&(0x7f0000800000/0x800000)=nil, 0x800000)
r0 = shmget$private(0x0, 0x4000, 0x0, &(0x7f0000ff9000/0x4000)=nil)
shmat(r0, &(0x7f0000ffa000/0x4000)=nil, 0x0)
r1 = shmat(r0, &(0x7f00009a7000/0x3000)=nil, 0x0)
r2 = shmat(r0, &(0x7f0000c0e000/0x3000)=nil, 0x0)
shmdt(r1)
shmdt(r2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x8000100000000100}]}})
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0xc0}, {0x1d}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
poll(&(0x7f0000000200)=[{}], 0x300, 0x0)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x6}, 0x2, &(0x7f0000000100), 0x0, &(0x7f00000000c0), 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimes(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0))
sysctl$hw(&(0x7f0000000100)={0x6, 0x6}, 0x2, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x206d3)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000100)={&(0x7f00000000c0)='./file0\x00', 0x7fff, 0x0})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xffffff7e, "000000000000000001000000000000f843d07e38"})
writev(r0, &(0x7f0000000140)=[{&(0x7f00000001c0)="0dcf67520990982c582dd6057e8d1069ce86b4854475cd2ad14516872e5fb8b07102f1f8b3c02aaeb9b32f1461a2fe9cafe8f3a3458b93e501ccc490dc2a968bc43140c8eb7b5882a12fa011c9518219d170f2aebe4c5aed28582ee61ecb44ebd70dda7c805d54b845bda2697bbe578a17dd3da474c02852258308051c7695c993dd9b5a086b4f954c554a8f160341004ad9366eef994b32816ad415fa45c1575ac47d55fac57bf6ce2647f4518900e0a180172a2ec178b3270f5dfc12b1058c5d6bea888cae054af3f59fa44f362c443bcc0bb6808f08f9ca3dba9a140e0000009cfd857f09110d0d0128ed517562257b8ecf1c7dec9de7a31fbe550e4f7d4ce0d53bbd2e31c609ff9872a6e35d3702176d243484b3f1ff223b4ef4f9f575cf67579ff3a99e58d8c0e20fadaa7bae3c301675febc67b85cde", 0x139}], 0x1)
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f00000001c0)=[{0x0, 0x8, 0x1000}, {0x1, 0x4, 0x800}], 0x2)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000200)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x48, 0x40}, 0x3e, 0x10000000fff, 0x9})
semop(r0, &(0x7f00000010c0)=[{0x1, 0x0, 0x1800}, {0x3, 0x7}], 0x2)
semop(r0, &(0x7f00000000c0)=[{0x0, 0x8000, 0x1800}, {0x4, 0x400}, {0x1, 0x4, 0x1000}, {0x1, 0x26bc, 0x1000}, {0x0, 0xffff, 0x1000}, {0x0, 0x0, 0x800}, {0x4, 0x6, 0x1000}, {0x3, 0x7}, {0x1, 0x3e, 0x1000}], 0x9)
semop(r0, &(0x7f0000000100)=[{0x1, 0xffff}, {0x4, 0x7fff, 0x1000}, {0x4, 0x5, 0x800}, {0x0, 0x403f, 0x800}], 0x4)
semop(r0, &(0x7f0000000040)=[{0x3, 0x3, 0xb1dca765c7eab520}, {0x1, 0x937, 0x1000}], 0x2)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f00000000c0)=""/4096)
semop(r0, &(0x7f0000000080)=[{0x3, 0xfff8}, {0x2, 0x2, 0x800}, {0x1, 0x7, 0x1800}, {0x0, 0x80}, {0x0, 0xfa33, 0x1000}, {0x1, 0xff}], 0x6)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000001100)=[0x4c57, 0x2c, 0x9])
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r1 = syz_open_pts()
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x7ff, 0x0, 0x5, 0x1fc80d8b, "04000000010000a9b3c742000000d35ed900"})
writev(r1, &(0x7f0000001b40)=[{&(0x7f0000001a40)="02af62651b9f04b076da1dfeb865b899d3aa6764b1292324e423da391db19c15fe9f74e7c67e50c3e631826edef94bb0e1d5b8e2e782897112ecc4084552f8957c6fd25203502d8084", 0x49}], 0x1)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000001140)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x20}, 0x2, 0x8, 0x6})
r0 = kqueue()
writev(0xffffffffffffffff, &(0x7f0000000380)=[{&(0x7f00000004c0)="287e75a480248a06195203939517d2478035039db1820693c5c24609e4fe1a8424ce277f052de98472", 0x29}], 0x1)
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0x4d}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x893, &(0x7f0000000100), 0x13c000, 0x0)
r1 = accept(0xffffffffffffff9c, &(0x7f0000000000)=@un=@file={0x0, ""/15}, &(0x7f0000000080)=0x11)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000100)=0x7f)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x800, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x4)
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000000)='./file1\x00', 0x1)
setgroups(0x0, 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000100)='./file1/file0\x00', &(0x7f00000000c0)='c\x00')
sysctl$net_inet_icmp(&(0x7f0000000140)={0x4, 0x12}, 0x4, &(0x7f0000000180), 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f00000000c0)=[{{}, 0xfffffffffffffff9, 0x4d}], 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000600)=[{}, {}, {}, {}, {}, {}, {}, {{}, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x4}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x893, &(0x7f0000000100), 0x13c000, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
mmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000100)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_int(r0, 0xffff, 0x4, 0x0, 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpid()
setpgid(0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
setreuid(0xee00, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r1)
setreuid(0x0, 0x0)
r2 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r2, 0x80047476, &(0x7f0000000040))
r3 = fcntl$getown(r2, 0x5)
ktrace(&(0x7f0000000100)='./file0\x00', 0x4, 0x510, r3)
r0 = kqueue()
lseek(r0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{}, {0x84}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
pipe2(&(0x7f0000000bc0)={<r0=>0xffffffffffffffff}, 0x0)
writev(r0, &(0x7f0000000480)=[{0x0}], 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
syz_emit_ethernet(0xe, &(0x7f0000000000)={@local, @empty, [], {@generic={0x88e7}}})
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x807fe, 0x0, "0008000000000000008ddd0000000700"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3f, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
poll(&(0x7f0000000180)=[{r0, 0x40}], 0x1, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = socket(0x2, 0x2, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000100)="529ac7ecc87315600a6543100f07c95df3287c82518c0461a37e5f30cd2e89292a66bd2d3cf7c41e0dd946a42f4a85799cc9f02756fb514a89579f01f401ffdd16017207f2a3d3c0b4d541dc3ece7e475b8a400c8a312464d18b87911540374731c15f2cb4bbb1563c17541a9658a656d3e478001cf220ceb66aa96f17d5386adaff", 0x82}, {&(0x7f00000001c0)="52474183a9a2d7b4647d191e127b82407d98c3cd48b6", 0x16}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080), 0xe0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000701000000000000ceb1fea707006ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882a297be1aa5b23ed12f4c8b2ca3ebbc257690000132e2704b5d67b102d58026ba8af63ff37422902", 0x62, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r1}, 0xc)
setreuid(0xee00, 0x0)
r2 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
fchown(r2, 0x0, r1)
fchmod(r2, 0x0)
r3 = getuid()
setreuid(0xffffffffffffffff, r3)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x81}, {0x2}, {0x6}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = socket(0x2, 0xc003, 0x2)
r3 = socket(0x2, 0xc003, 0x2)
setsockopt(r3, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r3, 0x0, 0x6b, 0x0, 0xcf)
setsockopt(r3, 0x1, 0x0, &(0x7f00000016c0)="309a53e7103e8d9718ff62d99efc41d083dd84370e6d040ef98946ff9bb305a789d106b67e811c0416cc", 0x2a)
setsockopt(r2, 0x0, 0x64, &(0x7f0000001700)="01000000", 0x4)
setsockopt(r2, 0x0, 0x6b, 0x0, 0xcf)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x8000, 0x1, 0x9, 0x11cd, 0x9ce2}, 0xc)
r4 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x4, 0x0, {[0x8, 0xffffffff, 0x0, 0x0, 0x0, 0x0, 0x4], [], [0x0, 0x0, 0x0, 0x0, 0x5], [], [{}, {}, {}, {}, {}, {0x0, 0x0, 0x3}], {0x0, 0x0, 0x0, 0xa}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r4)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
fcntl$getown(r0, 0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x3, 0x2)
setsockopt(r1, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
minherit(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x3, 0x0})
rename(&(0x7f00000001c0)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
unlink(&(0x7f0000000200)='./file0\x00')
read(0xffffffffffffffff, &(0x7f0000000b40)=""/111, 0x6f)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000001680), 0x20000, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000700), 0x0, 0x0)
lseek(r1, 0x0, 0x2)
read(r1, &(0x7f0000000b40)=""/111, 0x6f)
openat$zero(0xffffffffffffff9c, &(0x7f00000016c0), 0x400, 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000001700), 0xc)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000001740), &(0x7f0000001780)=0xc)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140), &(0x7f0000000100)=0x1)
execve(0x0, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x800007, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f00000001c0)=[{{}, 0xfffffffffffffffa, 0x1}, {{}, 0xfffffffffffffff9}], 0x6, 0x0, 0x7fffffff, &(0x7f00000003c0))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x23, 0x0, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
preadv(r0, &(0x7f0000002400)=[{0xffffffffffffffff, 0x100000}, {&(0x7f0000000280)=""/85, 0x55}], 0x2, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504440, &(0x7f0000000240))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f00000000c0)='ol', 0x2}], 0x1, 0x0)
r0 = getpid()
setpgid(r0, 0x0)
syz_emit_ethernet(0x10ae, &(0x7f0000000000)=ANY=[@ANYBLOB="aaaaaaaaaaa8aaaa01"])
sysctl$kern(&(0x7f0000000000)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x14}, {0x60}, {0x6, 0x0, 0x0, 0x20}]})
pwrite(r0, &(0x7f00000000c0)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x001'], 0x38}, 0x0)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[], 0x38}, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
link(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='./file0\x00')
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000000)=[{}, {0x1}, {0x1}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000100)=0x3)
r0 = socket(0x2, 0x8001, 0x0)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000080)=ANY=[@ANYBLOB="82028600e2"], 0x10)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240)=0xc6)
kqueue()
getrusage(0xffffffffffffffff, &(0x7f0000000340))
sysctl$net_mpls(&(0x7f00000000c0)={0x4, 0x21, 0x1}, 0x3, &(0x7f0000000740)="9cea3c0b544df5000000b002000000000000006049795a6fc1670843f428190faafbdc8ca74b973fb159e4466b490064c997d95bfaea6dded2331f28baf678cfbd660fd95360b150484287cc7de6569348d9df67af00c8cf912291da196b902403b908530c0d5e7a16ce645790bcdc8037a852a6f09a98441f562f64391b0b9b71019669f72935470669396fdc5a1887d232d0eaf2fadf2d1a5253b08bb91d4655d27c5aaff98bfcd141133a4c4c450a4df82d99f0144bb9efc3b67e88c85f8a2d7f80ae0ed07862923d2efea2cccaec4587c8325177c486c09c4852275b4b07237688798243aedf6061b19323726d95e6e9a7ab2da8164520f44219590d0dcc1c86f12a34809fa595648e27f3e9b8fb55a372f9a3a4aa6b0a19836d6b6b7fa27825fd0c47555e501bb74741a6382edccb5cb7fd1eed9942b7a79f5f07d70a0e465bea0000000000000000000000000000bad797f6a4ff5ef7a380ccb4d287e6fa35dd1208728e280f90016c7564c21510642c0e80231f744d5a6d37b278e0c62e1952aa78c1ecc91ba9570d44774b1f01de612b4a548056d2c7e7c14da2edf3409a3e6334b5a68e8725d5a2eedb960ef7ff88e30b01017c9235f2d9c63aa3c611456358f7cedef85cc500408637168706838c22994045b44d86e987702990f0334f707a879291fb648b1b13b6fb3c406a4de13eb31ed4b0bba6ce7710c3229e40c0fe797ad997a485c73f838f", 0x0, 0x0, 0x0)
semctl$SETVAL(0x0, 0x5, 0x8, &(0x7f0000000100)=0x1000007)
semop(0x0, &(0x7f0000000140)=[{0x4, 0xfff, 0x3000}, {0x4, 0x7, 0x1000}, {0x0, 0x8}], 0xe)
msgsnd(0x0, &(0x7f0000000540)=ANY=[@ANYBLOB="0304000000000000722c8836000037c112fc0f7db08e22b4282360e3d51ec1d8b1d9f62347840057da9bca9d25df010544dc657f5831eca6f0be78d6124bcea5abe0876a04715a58054e97084cab4abb857e1240e606cc05000000000000003cba12fe087003ef6d4d054f912ee156000000000000aa20749a020000000008f497b1d4784e8a0e454c47ace9bcc2ac901f98de1e0e8a8235c1adfbda4c459daf7a8101f3e80a3fd70f7429abcee60908000000000001000000010000009264ce39b8ab7b1ff54580e84723c0afbf49d5c2b219a6175af7c7a227c0f42efb00000000000000000000f7fa59438c11c33ef3259ba620cc30be00"/261], 0x22, 0x800)
socket(0x2, 0x1, 0x0)
socket(0x1, 0x5, 0xfd)
r0 = socket(0x18, 0x3, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x4000040e, r1)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x40000818, r1)
wait4(r1, &(0x7f0000000000), 0x11, 0x0)
setsockopt(r0, 0x1000000029, 0x3b, &(0x7f0000000100), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="7502"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f00000000c0)=""/57, 0x39}, 0x860)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x28}, 0x0)
sysctl$net_inet_esp(&(0x7f0000001300)={0xa, 0x3, 0x11}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x5}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf81fc7b457abe24105f76e85dc0ced443"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "a05d1ead3e531ef43b59ff65be755bc2ca0c93c5"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)='x', 0xfffffefb}], 0x1)
syz_open_pts()
execve(0x0, 0x0, 0x0)
setitimer(0x0, &(0x7f0000000000)={{}, {0xffffffff}}, 0x0)
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
msync(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x5)
setitimer(0x0, &(0x7f0000000000), 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffdffff6, 0x0, "0855c40100000008000100008d1b38b85200"})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, 0x0)
write(r0, &(0x7f0000000dc0)="79a899de14937ed5c452eb2a5b99e175da7e136d4f68cf786193ece88cbeeebb3e83e5ad63af89e42ce1ad8a36082f34964eaceb7445780648a7d95821b3042c329db087bbd08764266187decc50999eea314a27d14605c37d274defbb831cf1494d6124369e4bfa674733053ab2089bf69ad20e7b7dc12623f2e79bf1c03c17292be0f208270bcaf2482b2fd9570543ccba2c2cbdf39b124b62d3d280dd7cb10da9a464e0a502fba4b6d3e153ffb4e6aec784c6d295fd074aa55c9a0feed72e177743f7d30a3c89f60c10ed96b18579fb270a7c89b8dfc4222eb41f09a0333ea95e5109b473bd713ef9483c352bfe3890f1ea8650e4bee573e1d4857805f8c41f41c4bcd2322dc47ba6bdafcceb3a1e5153aee1644f87714cdba5a923366f66bcea2e5771cd4110a05237ff0d52fe7d144b112f0ed10df6cb876fea5a67b91e70256cfcaa8d1db8d638eb1c2a7023702e27d39267bf018b20ad6c2776b9143c237a0a66f06cca71491292e27d112eadc7c4df96849e602eccc9b4938b4c2eb73ebcfa1a8d97ab2b6acfd47b4236a9ac5a0b72bbd85c5861e4b36c00a220e67aa94cd6cd47bdfd5ac59c406464044a415375ab11c7c6d684b1666acd7e954f7bc5e68bd4a443173831091a02cc31453a5e3528109b517910c2125f9da7330171aa0b2217d5b92ebfa83c7238196ec3c65c00a0fafa554af4ab0d70b6387c10b5756cc92c7aad7fb7ed341088e60e3bd333b722b79b94a4d6b6094ad5d2958dafd87d62d2e795244de8fbfed74d091562694ca21063442596449cedf03b5bea8a252d31b7bcdda8e6b07aa46e6a0b4c503e2d60cbbdd4d30a623a04253dad469c19c805e826e595c5dd0bd7c8c1582feebb37e75acec0e52c0435fa1ea06bcc6c30ac7143460d828c107a54eb9cba80c405eba86e5f069ef3d34327f62e4762dc490a922d8c60c17ec36c9239513bbed1acf474a8134c817a82138cfce780f5938eb49ae1b013c03dcce56ba57f8730461597f5065165693b2b18a50cc308486f653e273137a71c6c59ed4139f4ac77cd2bd737e00b70cb7a217b0ef2159620d16f0165819ffd51eeaa7eeec4b6ec62be7dfa329cc277b5c95c49cf1f77be3c524a77aff6c2d81d9ae2c7f6e087570f88d6e3f9ec8ac65a477b9c1b07778048bec1bb55568421b563908cd8221c39937a7c10b4829c7bc31cc884001c7462d06c9103ea32fdeb984eb11fe9d01178cb97f0c7aa6a37ae56e1a183b5fbfe30bfebd9e98f58270fd0867f3b3508709fe8e1b841dc801453034e147f65ae5255d9f1ea72e753fb7cbe752cbe60526651b891031d9df9ec8dece22c309409f36578e24cb65a6414ddbeca8d14f5090d152ebd576e4a1e8010418d078079f7f2ae3e6f030fb2829efc91d30b0093e7426cb4a27ad8fa008e90090120c6eee359db27211a02a47e9d08ca911662e073f75fd773461a132c4776820355154c6c59056728a508ced6dfd9b0e6d3774c9b51bdf89b8773884b483ce8dfac42649e1488c9ba94f9facfa5b9850283edd434763b46a5bf61b550accf1660710143109e2def09e6453c2f301b32b050d80de62baf360424b4bb86c6a2c24f57fcf5bc669e27a415539991005bfb3edd9f5a0204139328377bfaf29c4ca615226c2c70645a290e0feaf9985ebc3ab4c9b8edcfba9fa5181f4b3da528cf8720af30e49effadeed1590905c4df8153c9ee04f855612253341934102924f90ac2ff87339674da825f6ac9b1912c039d52085b6b15b17372445aab7d12893cf0641732f6c22ea548fead0d058660500fcc6db2ff3f1f18add3158eaab8cb1e834da4a25ec7f4ecf36628e0fcc43f08b34ee9c1bbccf48a4b7dc09a0f203c4914dcff78830fa3992d5dd9afa4bff364a841cdee7b594b8c56f592ccda053f7133a77d92e0eacb960cb5a724e2554dfdbdc1df5fc5f9fd01ec6c0319e1fea7e5e58ffa20f885a46801807bd3aa7d5ee43ca4164edd7c147527fe870f3f24575aa376dbf8a7d2834dd254e48cd27e1452b036a1fa8f49aaf8d4a61ec36c961322d90239c9d561328a0c371b097d3b91c11d39409900f516cb44de9804743c733563f54ca3fff785210d2c1da88c3839f58089fe3490a3355dbc3aa1464ac6e43052b1d16d0abe6a8a655db641ff798f47b11d2378855d14e6374c1b4b06f2134c323bfd01bbf2a497fafa8a70f3bf9589e9106199ac6cdd23aa4bd473fe7cd48195bbcf62b8ad7a32f94f8c721dad1dcd1d20dc935fc67b583ca835d3b0324ffc65c807bbd1d53572ad70f7364a770e0a024a095d05d749a20a52d0ba5029cbd00113d62bcc1ffe64299275d2efc1d857cbb80646bb49439d0389f4031ff2ba46c3e9e77bedf104dd30bbf881dee4dedd5c2664af2c1d0dcaf292b699dd7e4e2ab32237e40b2d769a01bbec5bf29db146d329db47b00011688f30ecd78f44de53107435365666943e6f5a31329574986e744095a7a6040b194a44a8179a2985df0aa2f62aa4f4fe5f6ccb259bdfa9831ee41b6abf3dc55e10a384a8f4e727481667614d0716850a03b791552010e57467162040f5561d14f8cb132e9b9918b9a04ce898942f20466082151486042a7f72d39e163a776a20ec7654fb0a84a6ea0e323452d3558e6cc356eab53b5a11825ec9d27b07faf0ede346c3ffa2ef16969157a7ed6725fac43c083233cd7e90327e1eead491f7c0a94ae1ea2d84f22c0aed34ca308a565fe370fadd3998c31257628790d763d4387e5bd2c287d2871d561284dab9459af8b41a7f99b698ee2a7c0beb9281fddd082f630605d4cd5a998296d5ce49d4cd546813e90a049754ed281af827c312b26adcbb3cf7697157edcf3be54f393c792952dd6d2ed17a96ae7c3616ac9d85744fecd3058a8e9131f3cc5c211ee43f21704f6a48be8e263078e86550a8fb4efd2d514fe25095ca5d831bd85c63c440ecdc3e2225e9bbbbd3d20e392a4a1c2838647e039b245c5f4ce85ad123a40b21f9e2f4854a2d95fa8313ee9c2b55add8306a75a3107135bc2e6e5eb59ac6d337fa9e88e7ddd133f36284a07fa2b94099d4031d6e87dda632495312c739ed4c8d3e71f3a3996e5d4b3110fa270d105d9051d1b71f1830b1e9cb130fbc2b8f4ba9380aab2bcbcaf9d24a9528585d8e38be1ce39abf6d505407c2b9fa7d1fc2be4e1ddc6d59f57bd7c48e32f14e4fdc14051e10f26c9ddd0e248a0f34ec04dfa2b004b7470d773478475636cb2c624aff655a87f3446f24c7aef8e4d92f584e90a8b3eae9ee8ef1e5812ee239a085fd66954032d8de9b34961e6b0ecbf9da85de51b5916bc1a914832b3ef6f29018f10a6b45f8f58966cd0156e558b119dc2075c11d36c4eacec0bf3b09cf073fed8e6349e657f3980af2227d22cc2a56fd795dc45d3a6e2c34659246a2a2d574d6dabc2a540e9654fb0c6ccc0c1c25ec683a4e70ba1a639f849c64aa5dc9c5e5655028c4f75f5e6af79478e1f21b1e2c5c60e4b31ebd950d09cd6c0bfb4e72d98fa696459926edf2eecb6de8e9e1277fff4af65eb8e11fa3f2a01dd397f4381967f353b60fc7264f4322ffcaf0991f25187f666b640180ed4ad676d236e172bc7ddc4277e882131ad0fe060065ac484f071b8f995e0fa173dd26bcb6d874f0c4c2da4c6eb1e24ef0f362bf3f1bb1790e6ecfd133eaa945c2e99fcbea0c9429c3209d9891bb914da8b81385db180e32d7df512e849597d663b737479805618fc8c579a5e9100909c9745d51f12ed52b295ed2cd84b936c13b57981953f50b317bfef8c85af1ae89507dc930efc9bd7a32d89b1ba9069330bb3eb6c7409e1a554f37846386d5381dfec4e0fcb093b89be98a717121a7ece4a9978d8ffbf5e0b74a810938141e9361a57f3f8a9a7a59030c9f63dd90e4220ecbc322bfd06d8960c93940e3ca61d64fa78198d36726aabc7207b4819228026ac0bda06d31daeac02d367610b83c1b20991dfbc042e5469e9d4bc8857b8294aedfe5cad150968d04a5de3fdaab7df8ce0fc4322d7f70d3f210868028694a1e3b436bca274e720dffc8eb83d4f043dc15e2a0f8c355fa40802ea2c34e9c49029fc4dd305adb11e33a9421dfb7890b89db46877fe5407cde90e716562259d4f9f898bb5ade80d46875b126c5670db792155d6fedb68461c1040c00dd4a7dec5d6f336401f56ee5bc4e9324e30660a1fc1c97883f0dac995034326fac4680bd9922ecbe841b05809af78877eebb58799a84ee9cba0ab1c72036b5c3decd964e8cc0950b11852c3c4fcedae75b8d329f7a7709d730eecbec9376c90387b09de5c575d344e5423030ec8f22c22f7978f1876c625535825f4df9171c8d1a2a2f75d22929678a07bb90571f2d44e10e1fa170869daa53b3ba31bd0d04efd49d938c9d9cc0afe205c574a8ed5a04da373730716d7f666b9517e79233184852ee38f1c4c95fb917cafa376cfcfd0ae53b361f5c1d6fc3e331c087593341b0b0781598adfdb96c4435beb56da352c163c4d33c22e9ebfb7b9afa324d76e380f8f851b7fc55fffd3bd8c2188b8425aa996f050f39062846e9dc0d7d17932de552679bf77760d4ced0d7120e2819f9ab1595568ac763974910754adbabc4565a", 0xcd7)
writev(r0, &(0x7f00000009c0)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60acaca1f2844dd730ae78b0e94daa6340f332c6136fec572c091ec964f479d3f09601e05a4265ae5f4d3a70b8530f85f78b162764959532374a5f97ddac09069dc842ee5b6b470f0a2c13de93943f58c029f6bcf437d67a29db534b1221fd5256cba8ba8535737417084659d7f6ab02ab49954c6a070955682cc9dba2931d55a413df999446fbd9407aaab9e385a161f802553f02b1e823e10d785547a7acfe8c11b040de1a30bddc771c1adf40bfb83be91ade242f0a5330f89791d6166e038cbe7f5d719bf1c9d0d9482f436fe94ce6637c899cdb4027cdf11af7c3d3662695d53198228546d6800ac6a6d9106e682c0c96e510ed7cb32772008f345f19a39743eed6db45bca832b6164801181f213a793ebb0fdb2670fc204462dd4c3ef62ea40c007418ef216e5c8be4fc31a982fc12166b472e53b1b4171e7c4a4f96b2d0bc9d72e34b455e6a2833e7f4402db98af3d1c522715c1e26e23066014f1b80741f9b37194e9a74df158", 0x32a}], 0x1)
syz_emit_ethernet(0xe, &(0x7f0000000040)={@local, @remote, [{[{}]}], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="20c75d54dd7f", "", @local, "3cc55a16aff56912619050773e4c02cc"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x20}, {0xc0}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@local, @random="9ba8ed10ab90"})
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040), 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x0, 0x0, 0x40}, {0x6, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000540)={0x0, 0x0, 0x4, 0xf0})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f00000003c0)={&(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x6}, 0x10, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000200)='.', 0x1, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0xfff})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x400000000018, 0x3, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setsockopt(r2, 0x29, 0x4, &(0x7f0000000100)='\x00\x00\x00)', 0x4)
sendto(r0, 0x0, 0x0, 0x0, 0x0, 0xffff000000000000)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x5}, 0x4, 0x0, 0x0, &(0x7f00000010c0), 0x0)
sysctl$ddb(&(0x7f0000000000), 0x3, &(0x7f00000001c0)="5fdc08f940a309fc4c8725c81b81baaa71e1c974f8743f54d2609b944cb09034099062da2c3ebb4548e9177c568453b40f120a3566a10dd4edb0d65e729701f6fc2f090cc38ae5de08e4ece709d52a3f8dade0eb3365c5b1ca17f5630aeaf1abfcecbab5af83f74f22569d05314ca95ee7638e1446819c8ec635d96f304c8396ac30662f6352d100fc137e2b2e68b7c1ac6c22d0e4f0bf629d81011d3768f4be9ffd81b3183f7884c2208a9bc3816608901b907d0c5b123805052c5fd56cf6b8fb5557ad", &(0x7f0000000080)=0xc4, &(0x7f00000002c0)="aed467ca2c2ef0479a3c029f56ff6d1e5d5a22b59805557d61eb41d64875a66e6b9fbc8144fe99306f97a72a1b3e9fd8c3a60fa47ab207b797f581f9b16493a406a689f3820657495e8e7fed056b7ae2a4d4e0fc6f52c0943fb920fd195bef07716362f89b9f732f6a6de0d704b48fe17738f5cd9c97afa3ac6fad3a4165919781ae55f6f038f857e5c79ffb77815d58e1ed7d98d7d9630235a925b6b84057a65b6a59a8425b6b9f58b2ca5246edf3ec1c2ccb6c05f5ba40d3e07bb9b4fd1acd03deeafeea692adf56e472130254fa5ce116c22f191c7ef9776c7bacc84f0b8d81a1fd2f06f3c924bd5c8e8640393afadce1805bce8aca6df4aa7f1ead7a4ad5f8090ebf3daf4be1ff9402", 0x10b)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
mprotect(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x2)
write(r1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000003c0)={0x1, &(0x7f0000000380)=[{0x7ac}]})
openat$null(0xffffffffffffff9c, &(0x7f00000009c0), 0xa110a8fce880c998, 0x0)
r0 = semget(0x0, 0x0, 0x0)
semctl$SETVAL(r0, 0x0, 0x8, &(0x7f0000000080)=0xffff)
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000040)={0x0, 0x0, 0xfffffffc, 0x0, "c3e16c57dea20e766eea9b01999d0edbc3318d2e"})
syz_open_pts()
ioctl$TIOCSTART(r0, 0x2000746e)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
lchown(&(0x7f0000000080)='./file1\x00', 0x0, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
sysctl$net_inet6_ip6(&(0x7f0000000100)={0x4, 0x18, 0x11}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x80}, {{r0}, 0xfffffffffffffffb, 0x1, 0x0, 0xfd}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r2, 0xc0107002, &(0x7f0000000100)={{}, 0x7, 0x4})
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f00000002c0)={{0x3, 0x1, 0x8}, 0x6, 0x9, 0x9006})
r3 = openat$pci(0xffffffffffffff9c, &(0x7f0000000140), 0x80, 0x0)
ioctl$PCIOCWRITE(r3, 0xc0107002, &(0x7f0000000300)={{}, 0x7, 0x8})
openat$klog(0xffffffffffffff9c, &(0x7f0000000080), 0x8, 0x0)
r4 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r4, 0xc0107002, &(0x7f0000000100)={{}, 0x7, 0x4})
openat$klog(0xffffffffffffff9c, &(0x7f0000000100), 0x20, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f0000000040)={{0x3, 0x2a, 0x1}, 0x1f8, 0x7, 0x1})
r6 = fcntl$dupfd(r5, 0x0, r5)
ioctl$VMM_IOC_RUN(r5, 0xc0205602, &(0x7f00000003c0)={0x0, 0xfffffffe, 0x0, 0x3, &(0x7f0000000c40)={{}, {[0x3, 0x7a2, 0x33c0000000, 0x20008, 0x1000000040, 0x8, 0xfffffffffb, 0x10002, 0x7ffffffc, 0x0, 0xffffffffffffffff, 0x4, 0x40, 0x7, 0x4000000004, 0x0, 0x0, 0x7], [0x10ca71d1, 0x1008, 0x9, 0x0, 0x7, 0x10000000001, 0x9, 0xa00000099, 0x4, 0x5], [0x0, 0x2000000009, 0x8, 0x102, 0x800000000007, 0x194], [0x1, 0x8000000400002001, 0xffffffff, 0x8, 0x46c, 0x4], [{0x0, 0x0, 0x0, 0x7}, {}, {0x0, 0x0, 0x2}, {}, {0x8}, {}, {0x6, 0x2, 0xfffffff8, 0x1135cbe8}, {0x7, 0xf9, 0xe59, 0x9}]}}})
ioctl$FIOASYNC(r6, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0x23, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
dup2(r0, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000680), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x64}, {0x25}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f00000005c0)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x39, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x0, 0x7fffffffffffffff})
kevent(r0, &(0x7f00000000c0), 0x6, 0x0, 0x5, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000040), 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086333)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000300)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000003c0)=[{r0}], 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x18, 0x1, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000300)={0x0, 0x0, {[0xfffffffffffffffe]}})
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000540)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f0000000200), 0x0)
munmap(&(0x7f000081f000/0x1000)=nil, 0x1000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
minherit(&(0x7f000081d000/0x3000)=nil, 0x3000, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x4000000000000003, 0x0, 0x0, &(0x7f0000000100)="6480cb82febdd0aabf8ed11fb93747761b83943e4f34793d5951b62791f58ed9b88c156d87a766aec0a179527c2f3d5855eeddf771cf05d5f4f6d0bcb482f6203bdeed8cd5d5e6a1880538efc6656af8f6d8a67c4f44ef51ac7ae7ecb69b96196f53976a53b868a8f64317140ded4e2a4ddef4e90d1342a3b5db12da7ffa9a331897544c79edec3cc4279a8f264e9dace88e6bd1b4283428d0c165380734cb35f780f890dbe622ad28c84933302b0d59c861654311280786fae08b59e560588172b24998586cff7dfa72c724705e7bb52caea695bbb638f83c2defc1c6b664fe18accad765dcc10701a734d31b0fc2586cc3795879", 0xf5)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x4]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0xffffffff, 0x0, 0x8009, 0xfffffd04, "00000400007b0000000000683009c59aec4ae201"})
write(r0, &(0x7f0000000440)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713240c00009c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b758b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5ed0d85fc397bb1e7a51af9a9b0eb8230c4f92e2b8523872aebe96544d539e7cb063926132dba7fc0144863d7221640bbf251e82226d5ef65610ed4dbdbd20e70352c53f3e5d018ee845a4bb59ec0d78755f5666dc28", 0x116)
sysctl$kern(&(0x7f00000011c0)={0x1, 0x55}, 0x3, 0x0, 0x0, &(0x7f0000000000)="e2edf5872f51af8b1b520d6e0671a05c3d34ac41dc4b30200ff23e7990bf00a6f57bdd40f1584987be5eb4e0c86cde6e4cba6bb4206212d9312f396fc2bad4175bcfc2ef751c3e634fffddcf361d7701698fc54c9b1ec465dbf7", 0x5a)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000002c0), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x74}, {}, {0x6, 0x0, 0x0, 0x13}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000003c0)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xc0284416, &(0x7f0000000240)=0x3)
ioctl$TIOCSETAW(0xffffffffffffff9c, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x2, "23e7c8204832406f637b9a9f11c632de6aaac396"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0xbbe8, "00000000000000010c8e7d00000000ddf500"})
r0 = socket(0x18, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x6a, 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)='\x00\x00\x00\x00\x00\x00\x00\x00', 0x8)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000005c0)={<r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000580)={0x0, 0x0, &(0x7f0000000280)=[{&(0x7f0000000000)="389bf2570c246c15dc36513ec471258f7abdc271ceae009e3ee9f8399d34061da32c2dda0d4163bdbf8d36c782ddd2282b0cbb7b3da72ddcc1663a4a9a1fecbdad7f75dd80bde5b1249c8f82f3850a30ca4800e76dd1462d501dbde7044291cbaff7709c960d7750e40913e7ab4f1da4d91f837e3936", 0x76}, {&(0x7f0000000080)="e85b073b0681d612024e1c504432180b4924f0c3b48dbdf8ffd6165b1f6c7b2c6ef0ecda284f8194b27b2e22ca5ea0a2e704efd3e2ace604c691c2c5f76666cec785b9509f64786e29761913f585414934bcd031240cd5853365b1ce5b66b0", 0x5f}, {&(0x7f0000000600)="4556fce5cb27af06c3c395fde9786bb1b4598fb53e9c6ba6e742ae11f7fec3c1a8db7d581bd2dd5937ad7cf753f83ca9e6efd8d0482533d5bfbc7f5bd261a98e41eed056d496d4aa14c40b4dea7955ae2158b8c98fc880619f1bc83bcef77edd126685eaa7a4f2f81b1bf34d26a7ad907fae8ee2c9fcc23e23697e6773f48e97bb4ea9117806ebd8c84be034c40d6ec0587c36ecf03a54acb07158d07eb919042c80286d36a2fb37ef4ee4e71f23a01d95456dc72dfbc7142df620fd2c9f7776142bf3400bf37da3f345f3a71cc6a6cf244b8cc6015ee708dbafd57f8ebca0309dcf225f75fe5cc4e50969e1b931fc689c748e9ca8b5809d265234606feb003ec67a7aacc572b7b3dfbfde3635f8e0d622cb207360cd777016e4a477729b329f962cc04d32f6598d6a3c1c939f0ab88a1f878e00671420cdb0bc6d7dc8350bd86bfc5e2e6054f1f34d593fdb9b8847f083885e2b9c8280731312d181fae02c812eb82741dd35eaa06579ea796c69d03e283aad74300819efda8026372c06ee5c67b124c67ebc54cf63907f5b9dc4325208e0de24ce3a78b987c8ea1f1dbd460bf55f99da181d1562ee142f185eb4819ce1fea8537af11d39de1c2aa54d5a6e3b3c82021b47a3d848d213a84499bc216afb1e5cbd890cdbeb74397243c5daadab11b5163cbd71fa7d155300ba88a2a34c7fbb74fc7ff87c96fb2267c9c91439386f1d4ba19ff6d2d1e1586a3faa94dcc4f2543772ab9c8912d34c3b0ba525e4bafe724cb526dcfc993a7484475e768b06824312c513c4b91f85d0a3ea5b24c4082a8300fc2ade2d34c236feb6b184053cfc78d2e362c506c04fb30a75fb6b3f8d467f6e29524b884436a90fbafa6591e0dbbe6d66de48dbd2e3148ae278302b791aa593c8460d62509c5777792f69afd44b3f816cebb192148ce2ec51bd47527441d837b3baa2e48c7e9216d599736c6864ae2929a2987eb47db64ffe10dc91930267e950f76de93955d26a504dfbeaeee9cb64cc170ae2fcb1517a1b392753dcb9a52fd4c475e4a336556e3750339a052748a20676121eaaef6e247e73a1a6cfcca729aa9f7a306be4df0a87ed6a32ce7ddce5799c660a00c7025697de210af0f0e6954365293f7d57c6f358d14cf409058a394d130de5d74910f31303ffa628d5a5a288c370899e29eeb15c1c578d607db820a8b2aa7b5487f7966da2b1c2ccb6762497561979e67f2eea406edb41625867f3cff6c9bc9f28db2081562c05280d9c0ba05c38d6450fffa729ec30ba4b01a8a5fbdd67da120d09d440af3a411bd1a96ee2c1dda5df6dcbbd87d90537d6fd9f03d3363b15f89e501410b5c5f59a208cf0065f8c39e42d6830bb68e01bbacdb0db1a58105cc4b97b0997563b57529f0d91e3896cd9458d31ad18df6c7a5cbd98c9fe0f3c2f6c6c20758cf5963f9065c122ba248b740cbe1e868233f43e88ce75faa49cbcb30f880e8557e15ba56e5080f82d1c75afdc137047ef54008c050e4ca31f4f4015b4b7ca09b9327ef706bf1bc6f6bf0e8afa2b49cb7cb5a64b25e44aa22181400685c190b77b715590fc7dc859c09e857c5dadf13640027f76b521eee8d034df2b4c32bb39906553953fd4a51c19abf263f613ad227ef8e133876b1f611f6516575b16b2ea024eb039e4cf01a5d0d90b9b9d490bc085c4b5763d93a38dd1a7c79df4937841b9f219927d566bc8561fbe804ca1ae0b0e7646a6d0588ef2ce4f3a5f1cf9b84aa9ec935a355f61782127ba60f81c31f538000610a29121797d951c80652779826a5f32e1bc34f54b84a0f3c8aefe82f50e0061ffa43e8feac553a33ff1c30e1d975fc58840f0b8c5f21185f508e1f25ed252cc4a8e3ab54d1037fe71612e8e8b8117a7dc45aa3e21679534e34c09260159eccd841f4e61223d09deb87a684fc83f1e5c7618cd0ef4419bb1acea64cb17a5e5a3230dd96342f627a9854460038915620c6f679bb6314ef9735d831226130bdc595c9bca0df3302690caac0707c0257d9f0a092d0ea8248feeb58149c70f065adb10b79df0a9587b555f2294df79633787812db3b83be3ea20ad1d227eb8af94337beae4493bbf20db5ea927c130613b7b6fb36c642f4f0625806e1ba0e236daa6df5bde055d568902cd6d02f071a4734121e24c00033b5a3afbee31e5015c6f9608de5c38c881c0b6ec67fe1292abd4f5fb20302e954a528c876d59f3ca85787b7f55f0cbd21e036e4aaf83eb89b40eaa1eea9ccb99d67c1143fe92dba66bed92039add743c30a3c5a0506c7eb2f6f02c5deae0711c46c87dea7798efd2fb345ce4458dc8b290fd4fc034f8ea23f9008b12748ed1905e6bd68699b4a7f2dd15ee117eb33fd6808e0f27144d1cdf4689d3601bb79104b23eb18bf5f6d28475ccc9c4ef53cf2f2965967377110142805bead78e80f6b048ddf7b30c14cb207be2ae3da21b9c886fc3166ce0d85c0e1e032a734f900162e6b183d272564a4029436e03dbd3ce8494665e81962005a330e7138aae5d84d53761259f1779c3763659e97eb585d9d1ab02f82f8f66739ef47502135e4a258ee84040028d16092109f17bc654af22fce585b1a98026a49db619d2b0dfb769edf9d9636767e078248aba1f4db00c15492ef2fc776b781445059ece4f388f29f8dc236c739ed2628db2", 0x76c}], 0x3, 0x0, 0x58}, 0x0)
sysctl$net_inet_carp(&(0x7f0000000100)={0x4, 0x2, 0x2}, 0x6, 0x0, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [], [0x7fffffff]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
getdents(r0, 0x0, 0x0)
r0 = open$dir(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
close(r0)
ftruncate(r0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x32}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x64}, {0x2c}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f00000005c0)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
chroot(&(0x7f0000000540)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/file0\x00')
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x2, &(0x7f0000000040)=[{}, {0x1}]})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x14)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xb04b, 0x103, "0000a1be000000000700000000000000e3363b5c"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
writev(r0, &(0x7f0000001580)=[{&(0x7f0000000180)="6054eaef2fac076b2f5c786ce9fa9946fb58287908c01dd3303a536822e1d3b2b361b759b5ba5a87a40ca069384db414b4c24eb7a4d00a103ace20a05d5ef4eab7ee71e59d70671dd8f9c38399ffa4fb36d3a7c6c2057c7df3b780299ddfd787b7fc8356f2d95e4f8feae2298e9411e27918385c1c961a86e50e63df14d06437be", 0x81}], 0x1)
write(r2, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x612, 0x0)
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000140)="4d5b4b3898d5b140186840a77150b7f2", 0x10}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
setrlimit(0x6, &(0x7f0000000000))
mlockall(0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0, 0x2], [], [], [], [{}, {}, {0x0, 0x0, 0xbcd, 0x200}, {0x0, 0x0, 0x0, 0xffffffffffffffff}, {}, {0x1000}, {0x0, 0x1}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x18, 0x2, 0x0)
r3 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r4 = openat$wsmuxkbd(0xffffffffffffff9c, 0x0, 0x20000, 0x0)
fcntl$dupfd(r3, 0x0, r4)
r5 = openat(r1, &(0x7f0000000000)='./file0\x00', 0x20000, 0x14)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000000], [0x0, 0x0, 0x0, 0x0, 0x4], [], [], [{}, {0x0, 0x1ff}, {}, {0x1}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r6 = socket(0x18, 0x2, 0x0)
r7 = socket(0x18, 0x3, 0x0)
r8 = fcntl$dupfd(r6, 0x0, r7)
ioctl$TIOCFLUSH(r8, 0x80206979, &(0x7f0000000300))
accept$inet(r5, &(0x7f0000000040), &(0x7f0000000080)=0xc)
fcntl$dupfd(r2, 0x0, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000100)=[{0x20}, {0x7c}, {0x6, 0x0, 0x0, 0xfffffff7}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
sysctl$net_inet_ah(&(0x7f0000000040)={0x4, 0x2, 0x4, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x5c}, {0x64}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
sysctl$kern(&(0x7f0000000100)={0x1, 0x47}, 0x3, &(0x7f0000000180)="206bb8f92f2d62ef2869d90c4a2d9ec616be3313275ab3965fcdbd7b540d53471d2a8672b6d57f0a46f4318383dd99bd32adeec49488a9e7d72a7e09a86337e16b9a4293944fa6bd1770f952071fd5d04a87218f2af8096a56069c210395a6767cca5c4937e4e51e156cf76f763e266f6483f4f8731058da62c01d888efc6df9c0ce10f7e4c3741dadfeb083f8e56afd1cdb4e4eaf7238f63a5aa2ec853053dcf46feab1a818c2c08e08003cdd9949ea4fe45c258f0b6c274ef37e7a65ab640b4ad1", &(0x7f0000000480)=0xc2, &(0x7f00000004c0)="edbdc371fbd5d48f7e35c132e77905b5841db7eb8054a397b658d0bfbc4ce9349f53eed52294f49abd4ceefe37d709aeddcbf3542acd24", 0x37)
open(&(0x7f0000000040)='./file0\x00', 0x82a0, 0x0)
chmod(&(0x7f0000000000)='./file0\x00', 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
connect$unix(r0, &(0x7f0000000080)=@abs={0x0, 0x0, 0x1}, 0x8)
sysctl$kern(&(0x7f0000000000)={0x9}, 0x3, 0x0, 0x0, 0x0, 0xfffffeb5)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x64}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f00000004c0)={@remote, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "1258a4", 0x8, 0x0, 0x0, @mcast1, @ipv4={'\x00', '\xff\xff', @local={0xac, 0x14, 0x0}}, {[], @icmpv6=@echo_request}}}}})
r0 = open(&(0x7f0000000500)='./file0\x00', 0x200, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000000))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
setuid(0xffffffffffffffff)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x4001, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020699f, &(0x7f0000000300))
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="d5ff9668", 0x4)
getsockopt(r0, 0x29, 0x9, 0x0, 0x0)
r0 = socket$inet6(0x1e, 0x3, 0x0)
shutdown(r0, 0x2)
r0 = socket$unix(0x1, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000001840)={&(0x7f0000000000)=@file={0x170, './file0\x00'}, 0xe0, 0x0}, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSTART(r0, 0x2000746e)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a0", 0x1cb}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
rmdir(&(0x7f0000000080)='./file1\x00')
mkdir(&(0x7f0000000240)='./file0/file0/../file0\x00', 0x0)
unveil(&(0x7f0000000200)='./file0\x00', &(0x7f0000000340)='W\x00')
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, &(0x7f0000000240)="d1201d9c07b00a16a86b937d24a7f2947be77f1cbe061ab59b4c433ac24bff05792c3a2d50e2f6", 0x27, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000680)={0x0, 0x0, &(0x7f00000005c0)=[{&(0x7f0000000040)=""/120, 0x78}], 0x1, 0x0}, 0x0)
mkdir(&(0x7f0000000100)='./file0/\x00', 0x0)
r2 = openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0, 0x0)
symlinkat(&(0x7f0000000040)='./file0/', r2, &(0x7f0000d06ff8)='./file0\x00')
openat(r2, &(0x7f0000000140)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
r1 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r1)
r2 = socket(0x11, 0x4003, 0x0)
sendto$unix(r2, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37281002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000640)=[{&(0x7f0000000140)='#', 0x1}], 0x1)
recvmsg(r0, &(0x7f0000000000)={&(0x7f0000000140)=@in6, 0x0, &(0x7f0000000180), 0x1000000000000264, 0x0, 0x26a}, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{{r0}, 0xfffffffffffffffa, 0x1}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f00000000c0), 0x401, 0x0, 0xb200000, 0x0)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000080)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="14000000290000002a"], 0x38}, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000080)='./file0\x00', 0x23b)
chdir(&(0x7f00000001c0)='./file0\x00')
setuid(0xee01)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400", 0x17f}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000100)='./file0/file0\x00', 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xa, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000002c0)=[{0x4d}, {0x2}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
r0 = socket(0x2, 0xc003, 0x0)
close(r0)
r1 = socket(0x2, 0xc003, 0x2f)
setsockopt(r1, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
connect$unix(r0, &(0x7f0000000040)=ANY=[@ANYBLOB="8202a6917c"], 0x10)
sendmsg$unix(r1, &(0x7f0000000380)={0x0, 0x0, 0x0}, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x7}, {0x14}, {0x6, 0x0, 0x0, 0x38000000}]})
pwrite(r0, &(0x7f0000000340)="0900000013eb0913587ca9ec25db", 0xe, 0x0)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0xffffffffffffffff)
setreuid(0xee00, r1)
open$dir(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
execve(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
mknod(&(0x7f0000000100)='./bus\x00', 0x6000, 0x403)
open$dir(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
shutdown(r0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
recvmmsg(r0, &(0x7f0000001400)={0x0}, 0x10, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x4]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x20004455, 0x0)
setrlimit(0x3, &(0x7f0000000000)={0x5244, 0x100000})
setrlimit(0x3, &(0x7f0000000080)={0x2ac9, 0xd21a})
setrlimit(0x3, &(0x7f0000000040)={0x7fff, 0x100000})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206916, &(0x7f0000000300))
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}, 0x0, 0xfffffffdffffffff, 0x0, 0x0, 0x0, 0x5, 0x0, 0x7fffffffffffffff})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000080)=0x100)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "96b42cfbcaac2be65f54ed342ca80f8ef5c2cbc9"})
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x100, "010592f58cd835f790d4c03f6f014faef87d6000"})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{0x28, 0x0, 0x0, 0x400}, {0x6406}]})
syz_emit_ethernet(0x22, &(0x7f0000000140)={@random="1b329ff120e0", @random="c182c3cc9746", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @loopback}}}}})
r0 = socket(0x18, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000080)=@abs={0x1800, 0x0, 0x3}, 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0xc0}, {0x80}, {0x6, 0x0, 0x0, 0x3b}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setrlimit(0x8, &(0x7f0000000180)={0x3, 0x5})
accept(r0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x4e}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
fchown(r0, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000000)={@broadcast, @empty, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @rand_addr, {[@noop, @rr={0x7, 0xb, 0x6, [@multicast1, @remote={0xac, 0x14, 0x0}]}]}}, @udp={{0x0, 0x1, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0xb1}, {0x20}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{}, {}, {0x8018}]})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000080)={0x4, &(0x7f0000000000)=[{0x3, 0x7f, 0x6, 0xe3a}, {0x5, 0x8, 0x0, 0xc38}, {0x40, 0x5, 0x81, 0x401}, {0x7, 0x1, 0xc, 0x80000001}]})
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x801169ac, &(0x7f0000000300))
open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
munmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000)
minherit(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x7}, {0x81}, {0x4000006, 0x0, 0x0, 0x4000000}]})
pwrite(r0, &(0x7f0000000080)="00b9a5c2f79f9fbdf2e27152dbdc", 0xe, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008e50001000000000000cea1fea7fef96ecfc73fd3357a8d6caa0416fa4f376336acf00b7804bedd45363a48fbfc781e4991f7c8e85f882b2986e1aa5b01000000010300000027acb5d602000d7d026ba8af63ffff072918", 0x62, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x2, 0x0)
select(0x40, &(0x7f0000000080), &(0x7f0000000100)={0x1}, &(0x7f0000000180)={0x8}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x4, &(0x7f00000001c0)=[{0x1}, {0x1c}, {0x2}, {0x6, 0x0, 0x0, 0x3ff}]})
pwrite(r0, &(0x7f0000000140)="6ba9a4ce7cd5bc26d7e2c3ce78c7", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0x2000444e, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{}, {0x64}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = syz_open_pts()
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000040)=0x8000)
ioctl$TIOCSPGRP(r0, 0x80047476, &(0x7f0000000040))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1, 0xb5, 0xffffffffffffffff})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$WSMOUSEIO_SRES(r0, 0x80045721, &(0x7f0000000080))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000000c0)='on', 0x2}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000001640), 0x0, 0x0)
setuid(0xffffffffffffffff)
ioctl$BIOCLOCK(r0, 0x20004276)
fcntl$getown(r0, 0x5)
r1 = semget$private(0x0, 0x4000000009, 0x82)
semctl$IPC_STAT(r1, 0x0, 0x2, &(0x7f0000000140)=""/163)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000100)=[0x6, 0x2000])
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000040)=0xc)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000080)={0x0, <r3=>0x0}, &(0x7f00000000c0)=0xc)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000100)={{0x4, 0xffffffffffffffff, r2, r3, 0xffffffffffffffff, 0x0, 0xde}, 0x7fffffffffffffff, 0x4, 0x6})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x2, &(0x7f00000002c0)=[{0x40, 0x0, 0x0, 0x20}, {0xdffe}]})
syz_emit_ethernet(0x22, &(0x7f0000000500)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @multicast2}}}}})
mkdir(&(0x7f00000014c0)='./file0\x00', 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0/file0\x00', 0x20a40, 0x0)
sysctl$net_inet_etherip(&(0x7f0000000180)={0x4, 0x2, 0x4}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x45}, {0x3}, {0x6, 0x0, 0x0, 0xfffeffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0xbcd8, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000200)="d8", 0x1}], 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
readv(r1, &(0x7f00000018c0)=[{&(0x7f0000001680)=""/152, 0x98}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x3c, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
mkdir(&(0x7f00000000c0)='./file0\x00', 0xa1)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
unveil(&(0x7f0000000140)='./file0\x00', &(0x7f0000000180)='r\x00')
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x450a)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
poll(&(0x7f0000000440)=[{r0, 0x40}], 0x1, 0xfff)
read(r0, &(0x7f0000000480)=""/4096, 0x1000)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
close(r0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x3}})
r0 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504446, &(0x7f0000000240))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r1=>0x0, <r2=>0x0}, &(0x7f0000000100)=0xc)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r2}, 0xc)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c000000008c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x71, 0x0, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x81, 0x0, 0x1}, {0x1c}, {0x6, 0x0, 0x0, 0xfd}]})
pwrite(r3, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./file0\x00', r1, 0x0, 0x3)
r4 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r4, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r4, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x81}, {0x1c}, {0x6, 0x0, 0x0, 0x100}]})
pwrite(r4, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
openat$vmm(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r5, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x81}, {0x1c}, {0x6, 0x0, 0x0, 0x100}]})
pwrite(r5, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r0, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x32, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0104419, &(0x7f0000000240)=0x5)
sysctl$net_inet_tcp(&(0x7f00000003c0)={0x4, 0x2, 0x6, 0x19}, 0x4, &(0x7f0000001080), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{}, {0x25}, {0x6, 0x0, 0x0, 0x14d}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000300)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x1000000})
msgsnd(0x0, &(0x7f0000000200)=ANY=[@ANYBLOB="00000000000000004b3e4adaed60f9694f579a48a898b3e0a244a62aff3ff370299a40dd008d33ef2b68889815e0f3d64ea11849789c849d6b72cbe90087da24c19071a704c5a74c3e906de00234767800b15445d036d9eb7768ecc61e4001f2cea489a1672715d300f44b849a450c57ae4e7a21e973bba27a079143b4db6b"], 0xa7, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, 0xfffffffffffffffe, 0xffffffe1)
syz_emit_ethernet(0x4e, &(0x7f0000000000)={@random="831ee5407df0", @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, @empty, @multicast1, {[@rr={0x7, 0x3, 0x31}]}}, @icmp=@dest_unreach={0x3, 0x0, 0x0, 0x0, 0x0, 0x0, {0x7, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @empty, {[@ra={0x94, 0x6}]}}, "d2cb5cf5"}}}}})
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
munmap(&(0x7f000081d000/0x4000)=nil, 0x4000)
open$dir(&(0x7f0000000040)='./file0\x00', 0x28e, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
clock_gettime(0x4, &(0x7f0000000080))
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f00000001c0)=0x80000001, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "0000009e0000000000000000000000000c00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
getsockname$inet(r0, 0x0, &(0x7f0000000040))
setrlimit(0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000080)={0x0, 0xfff, 0x0, 0x7fffffff, "b4527b6c6cffc04ef32e85fa0cb250bc0bc35b48"})
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {0x2d}, {0x6, 0x0, 0x0, 0xff}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000180)={&(0x7f0000000000)=[{0x3}, {}], 0x2})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000500), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0x20006473, 0x0)
lchown(&(0x7f0000000000)='.\x00', 0xffffffffffffffff, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000300)="53ba96375f52c7ebd029feec3c81e6f9e853b3f4cbbdc93df44fa6d6fbe3e331fc7067fec4ec9d9a00364e02970b46491e2b7cb5b3af3b797ee37c4e4fde4406416b2363da94dbf346ef9ebb5e1284544ffa28f9e1818cb61a7ee71bee4a948362b5f5a5cc4ca0c0e82c45c0004a53bd502ba138920e026d031a78bfa742197f12de6e7aa96c2563316c3e54290189c95eedebd2186297b64cb62cf58cdc0af940d76e5f91050e9284f47d6cf17e2a388a8942828b0676b6e959e87abf5e9325a3f1b8b51c0094744022c715be7f752e16aaad5ebf5b8ebb1d688fef12f6f7f7ddf6bf9ffe3d3ba5923c717dfe0966fab0ac08a33ec3cf136a385f49a0c3ab1eb5780d9be41cae2fe6b69f513cbdb9f8cc3edf77ed1d4c5ff111ef5a81f83279bb9819ba745038322e", 0x129}, {&(0x7f00000004c0)='.p', 0x2}, {&(0x7f0000000100)="1924917606ad2e2759e597fdb3d563968ec121abd6e1519f204c5776bec8a302fe5a4ac74c760a4dbb38d8d3040000000000000035fde6d0bfb056e8fd98188e40e6880679abce1f1fb54d4fe20827b6ad12ec88d276ec5d11"}], 0x1)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r0, r2)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000180)=0x6)
r3 = dup2(r2, r1)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x140}})
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000100)={{}, 0x0, 0x0, 0xffffffffffffffff})
r1 = openat$klog(0xffffffffffffff9c, &(0x7f0000000200), 0x15fd81465c75210f, 0x0)
r2 = accept(0xffffffffffffff9c, &(0x7f0000000140)=@un=@abs, &(0x7f0000000180)=0x8)
fcntl$dupfd(r1, 0x0, r2)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f00000000c0)={<r3=>0x0, 0x0, <r4=>0x0}, &(0x7f0000000100)=0xc)
fchown(r1, 0x0, r4)
r5 = getuid()
setreuid(0x0, r5)
r6 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r6, 0x80047476, &(0x7f0000000040))
r7 = fcntl$getown(r6, 0x5)
ktrace(0x0, 0x1, 0x1500, r3)
r8 = getpgrp()
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000040)={{0x800, 0xffffffffffffffff, r4, r5, 0x0, 0x8, 0x7ad0}, 0x8, 0x8, r7, r8, 0x5, 0xfffffffffffffffb, 0x29a})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r9=>0xffffffffffffffff, <r10=>0xffffffffffffffff})
sendmsg$unix(r10, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
recvmsg(r9, &(0x7f00000001c0)={&(0x7f0000000000)=@un=@file={0x0, ""/18}, 0x14, 0x0, 0x0, &(0x7f0000000180)=""/41, 0x29}, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x35}, {}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x66, &(0x7f0000000240)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "979fd7", 0x30, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, "f14797", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x3, &(0x7f00000000c0)=[{0x81}, {0x24}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r0, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
setrlimit(0xf7153c436962bc0f, &(0x7f0000000200))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000100)=[{0x4c}, {0x54}, {0x6, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
syz_emit_ethernet(0x46, &(0x7f0000000040)={@broadcast, @random="ecc055825e25", [], {@ipv4={0x800, {{0x9, 0x4, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @rand_addr, {[@timestamp={0x44, 0x10, 0x8, 0x3, 0x0, [{[@empty]}, {}]}]}}, @tcp={{0x2, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "70349efd3019415cb05bab944d1e3285a8646924", 0x0, 0x10000})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x1a, &(0x7f0000000040), 0x4)
close(r0)
r2 = socket(0x18, 0x3, 0x0)
close(r2)
dup(r1)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f00000001c0)="fba3d7e27d0cb909b790a827b3a898930b5aae65147a0800aa18788c849df420134b5d4a2d496fe153222a3075abe96858b9c76ed9dda5214620508bfb5ef645daf8986827f1458429be1b1e64ad33460ea17c5b276b0a000000000000000000000000ff8d6b6758e0f99f40aa938b816d4b41f3fcb820533a755129564c02e36509cf5cf6d966d5820845412e3031d2abc9b17f4b6581d38ca5d67f06215cf08e80f42a43e87c4be5e9", 0xaa, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r2 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x20, 0x0)
kevent(r0, &(0x7f0000000200), 0x4, &(0x7f0000000340)=[{{r1}, 0xfffffffffffffffd, 0x19, 0x43, 0x40, 0x4585}, {{}, 0xfffffffffffffff9, 0xa3, 0x40, 0x405, 0x2}, {{}, 0xfffffffffffffff9, 0x80, 0x1, 0x5, 0x2}, {{}, 0xfffffffffffffff9, 0x2, 0x20, 0x8, 0xff}, {{r0}, 0xfffffffffffffff9, 0x80, 0x2, 0x3, 0x5}, {{}, 0xfffffffffffffff8, 0x0, 0x2, 0x0, 0x4}, {{}, 0xfffffffffffffff8, 0x18, 0x8, 0x0, 0x5}, {{}, 0xfffffffffffffffa, 0x20}, {{}, 0xfffffffffffffffe, 0xa2, 0xf0000000, 0x500000, 0x7}], 0x7, &(0x7f0000000480)={0x4})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000100)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
pipe2(&(0x7f0000000040)={<r5=>0xffffffffffffffff, <r6=>0xffffffffffffffff}, 0x0)
r7 = dup(0xffffffffffffffff)
read(r7, &(0x7f0000000040)=""/43, 0x2b)
r8 = dup(r0)
r9 = syz_open_pts()
ioctl$TIOCSTOP(r9, 0x2000746f)
r10 = openat$null(0xffffffffffffff9c, &(0x7f00000005c0), 0x400, 0x0)
kevent(r2, &(0x7f00000004c0)=[{{r6}, 0xfffffffffffffffc, 0x21, 0x40, 0x7fffffff, 0x7fffffffffffffff}, {{0xffffffffffffff9c}, 0x1, 0x68, 0x40000000, 0x7, 0x9}, {{r2}, 0xfffffffffffffffe, 0x18, 0x1, 0x4, 0xfffffffffffffffa}, {{}, 0xfffffffffffffffb, 0xa8, 0x20, 0x5}, {{}, 0xfffffffffffffffd, 0x97, 0x2, 0x0, 0x1}, {{}, 0xfffffffffffffffb, 0x54, 0xfffff, 0x5, 0x9}, {{}, 0xfffffffffffffffa, 0xc, 0x8, 0x8001, 0xcf}], 0x10000, &(0x7f0000000600)=[{{}, 0xfffffffffffffffa, 0x10, 0x40, 0x9, 0x3}, {{}, 0xfffffffffffffffe, 0x33, 0x4, 0x4, 0x4}, {{r4}, 0xfffffffffffffffa, 0x0, 0x8, 0x2, 0x20}, {{}, 0xfffffffffffffffb, 0x4b, 0x1, 0x3, 0x7ff}, {{r7}, 0xfffffffffffffffb, 0x40, 0x28, 0x4, 0x2}, {{r8}, 0xfffffffffffffffb, 0x23, 0x80, 0xeff8, 0x8000}, {{r9}, 0xfffffffffffffffe, 0xe2, 0x80000000, 0x1ff, 0x3}, {{r10}, 0x0, 0x20, 0x1, 0xec, 0x5}, {{r2}, 0xfffffffffffffff9, 0x42, 0x40000000, 0x3, 0x1}], 0x4, &(0x7f0000000740)={0x4, 0x9})
setsockopt(r5, 0x810000, 0x3ff, &(0x7f0000000080)="731ad8bfc1814deeca9668e5450a395a3acacb8afdbd6a45f070b971ebfe87feeaa4966b66d84dab0dc640", 0x2b)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r11=>0x0}, &(0x7f00000001c0)=0x5)
setreuid(0x0, r11)
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000))
syz_emit_ethernet(0x36, &(0x7f0000000000)={@random="fd658b242f9e", @random="ce033c48a196", [], {@ipv6={0x86dd, {0x0, 0x6, "a21f98", 0x2, 0x3, 0x0, @empty, @mcast2}}}})
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x23, 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f00009fb000/0x1000)=nil, 0x1000, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x6}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0xbc7c, "d6e696d83fc4209e414d70000000d0c73d6500", 0x0, 0xfffffffe})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000180)={0x3, &(0x7f00000000c0)=[{0xc0}, {}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000080)={@empty, @broadcast})
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
setreuid(0xee00, 0x0)
r2 = getuid()
setreuid(0x0, r2)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r3 = fcntl$getown(r1, 0x5)
ktrace(0x0, 0x5, 0x71e, r3)
seteuid(0xffffffffffffffff)
r0 = msgget(0x2, 0x0)
msgctl$IPC_SET(r0, 0x1, &(0x7f00000002c0))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1000000004})
sysctl$kern(&(0x7f0000000040)={0x1, 0x45}, 0x3, 0x0, 0x0, 0x0, 0xffffffffffffffa5)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000005c0)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000100), &(0x7f0000000780)=0xc)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x0, 0x0, 0x0, 0xfffffff9}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3f}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x22)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x8776dc0573891baf, 0x0)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000000600)=[{0x0}], 0x1, 0x0)
shmget(0x3, 0x2000, 0x2e0, &(0x7f0000ffd000/0x2000)=nil)
r0 = socket$unix(0x1, 0x2, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1021, 0x0, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f00005c5000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000555000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f000047f000/0x1000)=nil}, {&(0x7f00006ab000/0x3000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f00005ff000/0x3000)=nil, &(0x7f0000769000/0x4000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000281000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f00001a0000/0x4000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000631000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./bus/\x00', './bus\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus/', './bus\x00', './bus\x00']})
r0 = socket$unix(0x1, 0x5, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0xc0286988, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000240)=[{0x80}, {0x4d}, {0xc76}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @random="d2ad2c719745", [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @remote, "3f034d5dcc6595da6c7d976af8cd6e61"}}}})
syz_emit_ethernet(0xc2, &(0x7f0000000100)={@local, @empty, [], {@ipv4={0x800, {{0x26, 0x4, 0x0, 0x0, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @remote={0xac, 0x14, 0x0}, {[@ssrr={0x89, 0x2b, 0x0, [@multicast1, @rand_addr, @broadcast, @rand_addr, @local={0xac, 0x14, 0x0}, @multicast1, @multicast2, @remote={0xac, 0x14, 0x0}, @rand_addr, @remote={0xac, 0x14, 0x0}]}, @timestamp={0x44, 0x18, 0x0, 0x0, 0x0, [{[@remote={0xac, 0x14, 0x0}]}, {[@broadcast]}, {}]}, @timestamp={0x44, 0x10, 0x0, 0x0, 0x0, [{}, {[@multicast2]}]}, @end, @noop, @rr={0x7, 0xf, 0x0, [@rand_addr, @broadcast, @local={0xac, 0x14, 0x0}]}, @ssrr={0x89, 0x1b, 0x0, [@remote={0xac, 0x14, 0x0}, @multicast1, @remote={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}, @multicast1, @empty]}, @generic={0x0, 0x2}]}}, @icmp=@time_exceeded={0xb, 0x0, 0x0, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}}}}}}})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0xfffffff8, 0x0, "b56a6e01aceda6bda4eb4033c0d8f1723cf236dd"})
pipe(0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, 0x0)
poll(&(0x7f0000000100)=[{r1, 0xd1}], 0x1, 0x0)
setitimer(0x0, &(0x7f0000000000)={{}, {0x2249b8f2}}, 0x0)
getitimer(0x0, &(0x7f0000001680))
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000100)='c\x00')
r0 = msgget$private(0x0, 0x33)
msgsnd(r0, &(0x7f00000004c0)=ANY=[], 0xcf, 0x800)
msgrcv(r0, &(0x7f0000000140)={0x0, ""/6}, 0xe, 0x0, 0x1000)
msgrcv(r0, &(0x7f0000000600)=ANY=[], 0x22, 0x1, 0x1800)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1})
r1 = msgget$private(0x0, 0x400)
r2 = socket(0x18, 0x3, 0x1f)
setsockopt(r2, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r2, 0x29, 0x800000000000009, 0x0, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x20, 0x10)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r3, 0xc0084427, &(0x7f0000000140))
kevent(0xffffffffffffffff, &(0x7f0000000700), 0xfffffffe, &(0x7f0000000300)=[{{r3}, 0xfffffffffffffff9, 0xe4, 0x20, 0x10000000000000, 0x100000001}], 0xa1, &(0x7f00000006c0)={0x20001, 0x1000008})
msgrcv(r1, &(0x7f00000002c0)=ANY=[@ANYRESDEC=r2], 0x93, 0x3, 0x0)
msgrcv(r1, &(0x7f0000000340)={0x0, ""/60}, 0x44, 0x2, 0x1000)
msgrcv(r1, &(0x7f0000000540)=ANY=[@ANYBLOB="000000000000000000000000000000000100000000000000003afd34afdea3790b26b313fe543a8fa8b0c899eb5c0b16f6851447737232c4ffe1e7ed8da33d20ef734480e680f8f1606a22924552a06081df56b3af636fbc45053d5abaa027ff7bf3cc9cd29ed55288dbdc4c645e656dbb4f5f15db9a22276095aede64849aa6efbedc63af8a2c606fda5e331f22bb696c0ad1c4494e107dd3ca9f4e71e6d3ac84f9cfdf84a0dad7a9fe08a79a54c9387cf619f05bff3df33d08c02169da9e34b2fdfcedc75c3723813203106a10e327a646118e7506f662164abb014daebda587a6352158606c2f8e24b4d1f269712dda058761b5d1f55be18a2f065d78612cb38bf70c7c43e8420f09a89ac6884ea42d07e462f4fec1586bb923c079df1761440cd3610b98d66a8eedc92dcf6dd41752481d8c50f7fe8c77194f3f42142e20cdb47bf7", @ANYRES8=r2], 0x19, 0x1, 0x800)
msgsnd(r1, &(0x7f0000000440)={0x2, "901f6e07bc3dea97798f13cb941d9ff4bd1f7fd0c0d9837d285539b3a82deabea46e9f3f86f3e8581041015810a6984f1d156ff72c098b58257e6e45077a308e1100db3ff8f8e8a71a71a1b395fd15a72701d3b7a3135dde425b9d249f3963d9fc6cd8e8e2eb314ffb1118bdc5e74e896957753518af5e145b21e83bd7620080a3afc6d11ef16db6704926129633a8757c6782325d10b3cff4b5b46c4ddeb7b1d7c34f00c354b2c7b78122b25cfc7e471906fce9e53ca2d815270d04964d292d1bf12706996ccca32051463cab0aba1c8c759e2c5dc62f7f21982c4f5b761aa6"}, 0xe8, 0x800)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
munlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
syz_emit_ethernet(0x42, &(0x7f0000000000)={@local, @empty, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, @empty=0x1000000, @multicast2, {[@rr={0x7, 0x3}]}}, @icmp=@source_quench={0x4, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @local={0xac, 0x14, 0x0}}}}}}})
r0 = socket(0x11, 0x3, 0x0)
sendmsg(r0, &(0x7f0000001600)={0x0, 0x0, &(0x7f0000001380)=[{&(0x7f0000000040)="04756f88f9002bce66901acfad0589b8548b14c99b3ae0613f5d2f84d9230a2a44d88bfd664a6397e331df02d45132e9966856ef74717323b9a3fe110aee212724631d4313d5f7bff2f4a1eab34bf01a9ad2c79c8534394e5fd75bda8b5e188a535386cba30c7da708e77678784d55a54ba556892b3a253a4e8da2b0f10b0274002b4b9e3132a621454d3bb3264eb0aea44daaa086fe4b97a9bb316399e1e1b8f7fb8f43ddea017ec046c3db379960086ca015d2a9c7502a92bc54e7e456752a087a1d0537ec4efbfbea0b80b2bdf365e22bb376c70685a22ee5161b5143856a41d3c352f7c78d7eaa13279c80b183e229612a471996ea98fb31afc8c0e8e239b2a6ee981709130e5f617cc59c8bc701a0a64771b0cba7bf054bb7495da3bbad18b6cb2d691f743fa45f609876369e97400012c00e44ab74cd34c5eef8ce4a2f713c6523fc7fece0d7402bc68451b775129d9a0d699aa57df0ea8294f32ce46c7bf047e752ecc3121f62597dadb714b91fdfa6843b67fde678acec66b868511191b954755c244e578c5ca198a746a5e7022f0b26362ba0eac1fe21ab5248fc969bf5ae868d5db9e7a299ecf5ddf2dbfa29828c39d3cee21fdda1265b805330e16e52fd250a2d8d2c4063ebdc8b883940f4a6d3d21aa549795240ce5bd259a98c3490ecfb18d85e066440a527e501180a6b37f7aa45036cab00f53002e5502c79ad7d90c31c8acee54d4872afb698a9e5ee0048a65a20eba109cbf9b8845e9f4a8e82abaecbe848430a52c285794f77b455d7f85140c23818ef42f25628b9576557780dfabcbfee4eda4540cd7d1a4c0b5659a3e6a3cfb72a0be9f2db45aa32bceb8126940e95690b0d0bba2f9a7ce439f790bae221334e02704fe732a0b7e25891aadc0cce9efee11858aec0b8ea51e879e7a8c9e6d04808fa8a6eac87c86dca4a1f731efc2deb92de62e9d4f643629769629134bbc6ac19bffd747753f67c2f4eb04e2f465076eb06541a9fa8d5f97c87c5db285f3068dbbae314dde88dfd3a6b40450404c0c68ec68ef4f5aa82084ed9de3072fa636f08c196a33e08c7b90f06fa7bd49546812cf1c4d0689c46589819c250398a6db561a2a22f57052bc2b25f0b067f93a81be68c144cac7677c3827641f872b5518a7628206e13c9ffb9b888182af018cd40e22c17df7207ac9b81c682bf2691a758ea808a0e01b56054ffb244f2bbf0b6de178b87af8f660cacd36232f3d65fb6556e56c55517d3552afdf0c2bd23029672f262d0e75e693f4e2258a46bea507a13cc16dd47a78e031ac224de0aa548d3b120492550f3fe3e3e8067e52dddb9a1abdc7d61f7dc00dbafde87b049e4217ed9cc7b187b212f68af9b0ef43489974337ef97f3bbd307f9e78a35dc3a374dae87f4375c7cbb248d75ff6977677b2fa4ab878de73a463da619a3438865bdd0b34b11a2ad8b8031e4c1e29292344ea377655fcd171f50bccd28c7ea17e75a67179af6c48e7fbae03f6cb75832de2025dc216e275a701f061f04f713623d104a1457eeb6ce475415e10924ef7fc36ff3847941d75d1ac5c2332549fe5745dcb1608da532f239c59a5f454300faeb4cdbdb58e8e2854033ed05573a550de1bfbfa8f95fc1184bcf674a0a7fd4e813fee63baf623554270f17c1a4a69b669e28aa3fc8ee1257abc4c2db0e701bc7c5ae75fffdbc093b01969d31fa29018e793b7222adb8e737909158e515a66e41936b176f81491b634b1cd811c14046ea0102289525504746b473be58e16172dea5210896b7827fa60803849258bf1edc9664a91b3789ea76bc308aa03e8205d4bc20847dcd0a0e1625df77ad130f92e1c27b9f1a636cb3e8b7c726b649af8cc540478acff354d9d96b6848dd79d7f79d36e2e1eb6be8fd1b501ba10e6dddd643d53774ba1d19845a0d501bcff1101f6a5458b675b16a05a7c5b60b1bcc3f0c4e91d04a66c13048614a3ff05c12a44f067a7ef4f7c2e4fc2f32b6abb187cd7c4e809710491f22802f2c14ce20039298ffd92ccc271ddb7a15c62ebcb3426b285a8c2fd0022949782058602417dac0b50d82b56453168944b9cb67a349045d5cf565d9f1c891037e3d4f63e683e305692c6596028a62e41a02c1b45b27331ae5b2d5527d3c623b2e8fee4376d81a06407d83edf4bab600a8d0990742c212bfa54bb9500554f7b4ebe152bd0c397ec5334fb9dd9f5e4949238a37554826492204c76b59bed609fa5f6ebbbec00e407f8bc7fd8ef612c42ab055948ff5bddde24f5f1966878c73f8d037628dc1bbb1b1663b01a6eac0cbbfc710da3e34ddb184a254b9f8d01cd47f8224f8818334e3b657f4a9f8e8e45c86bec81b2a7f5c146ad62d269f5a1666e93f6d4e38d5afa89a30b95fcbab6837d5f1c78341f6d845f3125b6ecdb14568f43894db8962e1fd40c36e373e905ea85b3cb2e5f2c0695adbbba05a5e91bc3cf90f84edb86cfa845a6b3eb42482eb28521cc90cb6aef102b8ee7d2a4bd1b2ec97384112d97500cecf8083da7f91452a7493d953b01e854936cd052bf9edf276c03ca96636383ceb134d1f872c72b20ddfec7b72aba321e0ed07c6bbd04c20a765ac6fa610660967900c05ca93efe427514114e27916a26fb25d31e80624ad9443d0b95918224d7dfcbfe5c3c281bf165e6f8d68ed2e003eb686235c8623b8cbdad6ecf051ec48ed446564cf58123389886739eb1f37cde3707c983b33c8db38365f2a306308f8340b080d10078bc6ff64058f88f046438d7fde05f1d486c981bc30fe189f10e8e8645e67b4254262b06d9cfe4be7a52e21098f543e81a4518ef1bd86f2d64fd20eaae1361e4f5653a03928413a996af4c801a9ebd5c46da10ea88249dedbd6b1839ab7c7aaf773ee8d66adff70cf8db76cf62a0cb3aa81bb8fa395ec3978c7f945fe4450bb8f4d047cbb79071009803d994809b2f878f504b68b1c3bdf592e862649196d5cbe5aae04c2686d2401389a234c42e15747d0512055e02f57585231a8af2fe11fddc0daae3c136ac19bdb340d22487935c9167873013fc24a7426acc571063f00fbe46d01ee640e413878366af10cd0bd8e2f1b2ee9f723f7ac2d784c6f37792632458c05e98cf4ba2621e5db846a21749894febecf9b681d4aabf7f5f3bae0abdb94518849cc0b3d4c7cc26c8bb9175f854117e23292957fcf1b88e578fd384c266601d74cb9d8b07233633ce5d708d67966bd3de0a677791048128ff8bb08b7812da577ff70068bd5b41ba496f626955b3cdbe3c6fb8889d2b18af82f65614b43092a17cb567bdff9e222d4019f9faf4d4b89fe8b4067c07d0405980486bdd77a641d56d35529f18e4a313e14c744b92e7047463d037a4799010769a3cf1c61e98dfac6b32e34e075dd9b6876b55ada0dd0379aeae67aa1fad7c7485b41cfa5a87b24575f7db26dee950699cd38c1104d1c8cd2ad5e0a1fa841dfc677f7d664ac72566d70fdd99d0e4f9d2baaa0207f4be0dad0d6a3e50ddc739ee19b6a2ee4f557aa5c2e5d03554b4ae9ae7b161766cf945f2c5e35bd5c29956907d1ef886f72b8ca9f496c7eb4b79cfef86110396c14019e383777e93cb96a686f45f2e810613f6521426f2f67bf54d5916efdacdc3d4aaf61a7fd88a59cbd9d8003feab4bd7beec497fc4466f2e01d2938883a3e3f095971bd45148512ecbf1ccb637beee05a0ecc227cd7b002a72cc0641f05c82441c72c5d81c3405c83fceccf6dc713b2c1cfdaef43d734ad4516efe93b083c7e3a51c9e5c9f67ada76f239aadc771ea27114cf214f2f7842137710f510e3a489aac32b0749b5c0b9c74bb50b33b957f6afc214e3a1b820c5743652b08594f1d34fe93ab4da05431ef40f84c5b8bbffe3f258e17e4416480e81081c12a149ff7fb6ecbab9cf0a7787cf8105bb4ba17124b3c954e916c77f5d560005b13a2ad169f8ee7b3966d642be12fe86e30db376bb2a10788c42b310f5e9e46ecda666681a460fe35bcab8d7a12b649c94c4fa988718fd3f82eb23ad5215f2928391a5d308cefa01fdc1544158ab6e64b91c1d0c949f16ce4d4ad02ba859208d6a4be0d3754c2ddcc699e468b483d02c4c24aa8ddafe9937136dea709b9e4e465c31c71cd780800c4a97cc645fb5e4a9d63f5fbc7d00dbce7f4d77e618fc94424394c76be35b110d8327bb3758827d076960ee5b5acb80305069d8ceb55ceb3d017f8f4c3f0b2435e51b5621c0e5ead09fb25ba38ab2fc547d98280ca0f8ba86f02b666ccce76c079fd5ee25e58af58dd02f9ba48721fc62b284e5e950d8292de9375cb652ed70f43ac9c8b1f37b4267752ea065a89f5fb57b1700a727a7f5b920e505a824b8d71de2ba9380d83fc80e256084b5cd94ebc2c92c001d9c5fc65c4e063b5781cd0bc322eeb0f646a8b66fb18c28ad0e9578fea8c5ac9fbb189ae485cca2de4cae82aa35bbeeabb25ed024ec88ccf6f77bae3203c980506e01ece71c16e27e6285e4b3a6ebe8443c65764e7636e67498b65528088efecf547cef2d22106d9eb5c682891393d29ad2d8096fa5cf73b7b5e217337a5ad7bdde7552e1267f2902ad11f7ddc223aa2542a249ba9ac883421e0ee60379cc4ff376e7c5c40b97fec9dddc7cd21e6412f27393325563673a08dde1b69d46bf5e30496c9bea9ec3d4155f5a5cf3ac6855afa0ac9d3461928f536615025495e2fa8e3639cdf2ed1a1073b581ca049f805a1fab372cbb61ece4f9aa8daa4551f8f2a201d05bdaaf6f6ff46caa4fa591d3a397dd2d557197ff87601b4b11f4e9ea5e1e4c46be67a9dc5d9295ed00b2657f085ab2b9af8df239435e085d170eecf8dcd485a6cdadcb52f0d73cf76f0a0ccc91318bd2ae81bc06af69875fb5779eda993a5a2ef809e02c09ff4382c852e95cd0577ac2d198393f1cf23ddc94dfd3f3bf8ae4036573ab96a2f8f3e3ceddcd6eb39c41bea19f05c10d040146590b4baca435c390f895ec61e55ac29e0b209b3630afa96b81abf36b546bdab9aafc1f965a2bff17e585fa2ac31d53feff39c501f6a6c2b664055b8d1f2ee0df8abd5d85370b0c9be9aea3e0eebe6e8a4af77fec14bb74210a5da548bdab36a1e637acb78cfb1855be2cd6c849b1d116853a2b401ed82d076e2c0a4b13f726591d023a87bd0470064b4ea8f4a5da5d8c3f4cbea2ac51898ebe33864a31f621e5a28bc966263002a862c072f7d16806df7b09b98079a5045da4f31e5008ce7c28ba38261cda3d81422eec97f76e31369322e8565fc0efe3009ea98608c91884bc71711885ec8b7015b75764dc1d59a0b086da8bb2af71d9c42841fc688d9a74a225d2975dcf21ae3ea5bdb5f83f6f99f5f9fff723e0e09b49df7eccd2d67e1127b603bf9a05952c8898b3bc60fa1a326d42ce8fe2213d0408778b457de2890f7b227984accfc52c15a78a84848fcc40d8bd4ce107ba89bd464684773722fccd448db4b3cfef9b9349f515f5a4029b87a37404abbfba009e4abb118410c127a387b3dbf979705834c8f1fcefee506341835cb96a90276da10d3aace138580bff5fe0d53fb930e28b57819b4da6cde787fbfa320c1385c1673f63c4bd57e18bf5b114ccf2bc228b85602854528e546568bbde5c0bd9a4b75ae6b3b6ec2f67798c48880cd2b46e0d2691783875e39b6ef5957ee6f020e0af351f93a980a83a15b6c7cf73c358972518a771f394fd4fca81d3ac329f388f4af44cc051c46561c6bf5f6d98ca906e4b1f9343cf9ad6c21a00e97aaf508ff180580e20c1f2c", 0x1000}, {&(0x7f0000001040)='m', 0x1}], 0x2, &(0x7f0000001400)=[{0x10}], 0x10}, 0x0)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x2a, &(0x7f00000000c0), 0x4)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="70023d020b"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
sendmsg(r0, &(0x7f00000009c0)={&(0x7f0000000040)=@un=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f0000000580)=[{&(0x7f0000000080)="a3354bee401fc5acc927acd40529fa1fe2e60dee6922df36de8592aa9929709d2affb01475770fa9a89b2ff71147dc3bcea2fb8fc9fa2fcd94a7bb406f9a5ac0fdc3f5de42c617b883fe27bfb21a8f0164af48139573ce99a19f7391c6031225b40bd8b21e60972aca20c416cf56137eb9b761017292617b983e5449addfc9ffa2c9902ddece939181cd92351e41ce80ab423c65058da794a66d2aeb7b208d1e2a3c8a3a4db4326c27d6f85d2c365631552d718363ce74a2b303d74e1dd448ec707e55d751", 0xc5}, {&(0x7f0000000180)="2c2a423e8fc15f81d575a87489b6f57c5c015013908587586de9d489259207282176376e0643a25dbdaa3fe030e6330ca7464571a493c6e08f3b5ac670d5f61be5910fb020570b966852db14728cd6c22ffb78571652a18432c180e3ca66c290f1a7ffe75dcbbf11a8699b0aabdb36246ef96bd9ef2eef15cc3731", 0x7b}, {&(0x7f0000000200)="4454e9bbcf76f49d3421cbd973855ae2eb7d77971641e943b32f74e3c1398bb66207f2d2f55067e3daf93ff46465b105bc69aa81f9ac039e7f9055d84947149e38073e3d4046e0aa2e850575d8799126e6599ed351a10a81b5255558", 0x5c}, {&(0x7f0000000300)="aa705260a9def717852b1cb1388cce55e6fe45d8ef973862e0de44e468a28a0098465411c65656488f38f31db152a459b7c2c929b012004fe2934507add1f72c35624df71977f79ab80cbc162bcfaeb703f8ab3c8e04152df3062e3901b5593445cc656972", 0x65}, {&(0x7f0000000380)="edd93284ca436751f2754ca42cf1dcfec298672c456536057f8c906d8bde8aff5a0c46e1e6b26f804803fce88d2783ea627f1733bf157a5be9ea3d4f6bfc89c5debdb8104761edc4ab72e9f48a291a5be263d75717c7b3a51f8fb5a4246c1c6ae462961681d12a5b6bed418d847c8e79d78005f9f008dadcc0ae6404da790af3dfd94d3250671242446bccaff97eb0e13a6a2b49fb541a40a8df6957c69203bc99831ad59d6c8fe4344312f1d43aa5fa29abdf2889eaab2fed10c8669c06678f765f97fc4669141c6214de92651004", 0xcf}, {&(0x7f0000000480)="dabcadde3072ec484397bbe531421d633f884413d685d3c908ba2105f21728d737f9a1651c63e96f1f8134eb304b84255c5caa851827a8e87f6841c1fc0b18da49b0daa194445d6da276e76a809e5d446dddd4baefedfbfce891be9f6e0f86b27a2fb56fd7018870257320fb2ec68cbf3cc173a046618ff5918494dda01d8bbe4cf46a0ed1c3bbc6587b043b497267", 0x8f}, {&(0x7f0000000280)="332b3a407be1bfb01d7b826f0c68955896659c911efbdee88c6cda5a39e8286e95fab4b612e51c5d6f", 0x29}, {&(0x7f0000000540)="e2f4a6856486e96e869855887e70724e155b3604bd81affd2c2657016e077b466fc9f8fd97d7f745463b36e4e624a188d523ead48cee9bb0eb", 0x39}], 0x8, &(0x7f0000000600)=[{0x58, 0xffff, 0x4, "8d7eb25569521bd3acf0ae63979550942770575945e616e85defb3f843b89782b57881f5252ab199181c774245886f856fbb02612e480f196d4d112ac809c4fe3586b16dfd"}, {0x110, 0x1, 0x1, "027efb4b0627965289e8ba6331a34c1baecce6245049f30208b539271b43c8a5ff6426dafa33acb377362921cdbbf93e76ccd48b2df55f0462ccad8ef6bd431737d260c439076b846ce0390c0acea5118f6931bacd0c4f4a8ea77adc7b52497c0a05e5e4951be46b100f563cd9501ce8840f569da05034885af97946f79b05ede6524b94e1708de0b198cc55b1ecb34dcc7126ce8ce1ca7b6d8b725fa906df79cfc233623287c8bb61749e7809da1ddbdd67cb5c5f4f5a95c36a3ce8df5c3904d858d0c21e22184c9a838d2b188ee191a5af681836c29cada9ec1a8936121956041199acc40716feec3f134622226d115a309ea8132a95e51680"}, {0xf8, 0xffff, 0x8, "c54c8f5f56c4cd1b1b1fc8a5d933f1e79449be5238ba2ad8c7712b902f8551b9ee58041407fbcc62fe533c6a0a1cc7104a1ab5e19ea781ad462a17a3c48ff47d73ad5ea1c65fd7c668406e1db8cb16be31db7e52efed7e975078ac79cb7f572db6b59d0ee9fdb6008ff581ffece1076e54ba8e05663fd6e3fc16118f88ca71e69332d5c965fb160504438f70fcb716065b39eb911bfb0b33ce7d16e67907b1a2b04c92e8f6f0fa7ec83f5f76df6b53515e117054273bcfd8613149b2d6911edd417badca492ca31fdd85e19e8ac1c7565a74aa97bd0cd1a3874f939e1254ac943e8ec30bb4253289"}, {0xa0, 0x1, 0xe0000000, "c7e96a0df6fa00f5e18affce74f52e1f860bcb243209ddadc5a7511868620c477231b3b0705dcd498a4a2c7b60a7b0286e231b8009ae45a70647df190a9d2c24730fa786c3c2d9c61f293f9229cf4fecb9ab7550ee16d94a41627a6df31d39d0ea1cd2603a719cb4171ce5c82d083ba951b03a9c945b46b2a4a776a9b7a6c32324d243589a816312bc01a2016d"}, {0x70, 0x1, 0x2, "5d569edbf337379512890dc3475bc6425c7c4f0ec6be587155071602738fa650aac17eae0096208f71cd75d012bb697cc54fe96c437c81647e78a8297d1acdc22c2ebf2e760407c3c3739abae273c7f0d555a8c7e7545b308a628390"}, {0x20, 0xffff, 0x7, "31962684db9031a8df"}], 0x390}, 0x400)
r1 = socket$inet(0x2, 0x4000000000000001, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f00000002c0)="ee08665d19ac14d5e51348771197a7728420aef61715f7b1c3d4b3830c921bf0817a0000000000006a89dbdf", 0x2c)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)="385f6b08656704280a62ad6fe4c96a6502e69a728bc09441150c3562c10dc9280a93cab1a43535d70dc0de54cb4ff518f6dc406a963114981df2786e3253d47840504cb0296d3aa01d292b3207ae0acc4ae107eb063b9a0514a48d9bab66a31b66c36582", 0x64)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000000)=ANY=[], 0x10)
shutdown(r1, 0x1)
mknod(&(0x7f0000000100)='./bus\x00', 0x2000, 0x1300)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
close(r0)
r0 = socket(0x2, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000002680)={&(0x7f0000000080)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f00000024c0)=[{&(0x7f00000000c0)="ee30192f645d144a9d68c7d5793bdf4288a1c44c237342a3127248c737d51bf0811a6500bf2741e8a7c669416b0825f0ca6ec638c396715edf49dfc42f3abec7e01c32f83faed74454db0b5e46be0685684e57d4c904d6406c43a9e7b05bbdf24e74f8beb78ec11001857c8a83297a2cb72356ae73c9da9ff4b73bc5eda921", 0x7f}, {0x0}, {&(0x7f0000000180)="02708d5fee391da0869a8d21105c6dd75f2448dd072814bf7c927a3e5a110a4e6f8cb12e06d90b063957c89ec06c9330c0a94df20e465f163953bdd6683a43cea0f281a1340a3430f67c1b97e8afc32164a61e0d52716e69ce6b2e00eeb04aa80735b87bb5c0bd03479abf1df9581e715b4eb1682f301ec06d6b6605847259249734043ad7b621", 0x87}, {&(0x7f0000000240)="fd10eea2fd0d33bfd8e76bfa7a49f517499544b80ddd2bc0d81f33a4ccf63451735246b8349d4282da8879956596923423ac18f599f96adec1fa4c95896e3a0e7c7ec0f8f46cf9a8b722967154fc1ba37af7f67c67ed0200c01829b380058bd023eaba15b119f42220092cf7dad539cfdae03d0b1a29800623d091320bd0bbd6c32f18cd86f776ee21b67382beaba3eb1bd66002ac5a38169631a26a219aecfe9dc94742ea983980bf1de7baf43eae86424bd274fee48e729e2163408b0c08b64a306b1a434de156aa27a5e2d7d5dc11e86e06dcba42314863f14a6e6726ee8ffeafa64ec8dd4880d7b0056e8111a32a02929e65ab42c7e509bd9c53bbf477e40169531d3fc25a30535cf85cad0857b0a1c084324b60a1ebac4672f48878297d0d7b76d0dbb4f99a0c4cb33cf3e83f74b8efb6a4e8250f1606a31a3bde5c82fc52e6d83979a4ecc579397be58df551bafdad97ef5d528143461fe96cea90dc5d2b1cbb439fda30a5123361f0ea56217c523e4fbad4d85adccae7726d62e2fb2a9949a775ceb09bdfe56cb05562458f83b7df62110d34cebb43fdc1d2c97cb22953692e35ccb1307882dadb891a7c58df4d11c205f978594a82449a713e1eb9da3e65a181e7c7bf2d66555c282e342e5b204ac034a145be6fb69860c3825d6f6933f8ca73b41a6779266a1a5e2ee4b969ce59c1b09d386af9afde8a1174bb686bc8c2ff31966ceb15d71804fb6af44835bb6aa496ebe0b5dcd288672d8da48473478a704753af07c0f8ffb7e15d56e4577efc2bc079e2e9b216c20c70403b83fe91b148d4154374c07f35d339fc4cc8e901d659315e11d8f10051f09d7ce0814ecaeb4f95fd9012592bb21a64b5dfa725f370d5c920ca9b08c546d72de71fa794ce98f68357175a3564447cd803f5161fd7b23e406f398e5cd9320239c49c530b0178d8577e06dd86ddd96f15cfe41f2d204ae69523cfb1f190ab8d5c8fa64d7cc90726731fed9b7a53ae3b744778ed93bb7b71bbaa68cee2fef88867f9e90bf18b3918472c1834e026291b1f6e25498a4094fb76cdec30fdabc8d53bf91ae25632159bab49d6b82a1796997f4f2242aab79b37fd1624de8a363b58900873ab92662684e7e4cd365f03776a3e0269b66de14e672b8985f76ab2436d1f074be5e207244bf49ec163c1631c308e82e36585913017f316dd297d7e1b898e2e7330ca5254f499f4b8422c4df8ae9ea5d491230f4e1d9af59f7bbea09da6a2dcdf5db5a16a40b07d1a65a7557882ca1527936084853cdfa9672d60d87f805a3585f26906afecb8e45cff089555178f2f155651b8996835a9004a23a5c7465aa6ac424b49dc8d85705374c424f29c78b2b0229fba18c1fb4b7aca6561c61b9e5643a387dc62f03f6b4547b34fae4eeaac886dc3d1ed4b267bfad377dee7fce49b214e9df46ec9c728fe64b3d88752f0b852f92f67f41a9524a3213b6eb108d89d08fd8010581c1ec5d55245df926487c536ceccdd275b252da23decf8643bec9b72d3e966acc11b46f0fe1ed5cca29c03c0b4b33cc8d5e82c09ba13a0ed2da449ea4f2e489061ad6002f301968217f48377a9ec34f6ebe079aa9414b4b5263b422a47d9ad8096714afd6074bd577e03f8c037dcb1cc2bb99366cd7c6a62e8c20698262511cc5952231d27ad96abb97ca6fd6ef3405fe14acabd4dff5a86f29370b064213c082e65c5bc5975d5c02bfc3a4dd136bb1ea5ae500c5b390afb1f84975a45f137b19b1912a6af2fa9dd9f1f946d627a391ba35f79faa220e552e8128700936e3ba944c92d5880b0514dfdccc9bb4e6ee7db04ed96d9eff55e66e3e29605cec2378d673982bf1d75e9aa2650518b9cb8fb8113c68693b8513d68fa5d71d1285905f66e873d618263161383aa12e4fd137d61228f0876eeaae6ab089cb7e616e7c8d8741fbfe533d2aa396c7dc78c9506ca7f8e77d13e8c293def131f672e0a0f13b8ddcfadb009988c1ef329a4f998282714ab1ff6426712b72b76864fdea33e01fcc0ca7bf72830999af06ba56c1eb75529c070304c9ba5c81f3ae7f0d7c8c14834f8d6020ce99bf80824986802d374705aa22404417bfa137c6207db980272a1fcce22c9b6b4b1b18dc635ab2bbaca76749044803f0687ae2a38d4a586d7692a26d387bb78033876fce21728de514c07a3dfeec9777c267897a8a3780c07974a756175400b9e13e7fbde9692b9a488dbf888b3b34c257e3b05487d93f413781becc17f0cce4608ff8b53a7e6de94f946501cea8211d9604a13f81fcb6081f28247294b8d6d29c731399bc0eeb1167ddbe36d99c2e020a184b2120985c40ca55ee69d534d24846cbca176d85635fae8366ede0a72eb9c01fcc70b8f9b77e2fed5e830f110f5603ddf59c87a460491df07d7e5ecd3360ac81dc63f3a55f49a6648b7d62073234e3128e014bd5f99c4fcb73314728097209e501e4467c2afdb81f406cbed2165b53951927f5bc81274b43fccb6628996331e139a4fcccdd150cc97d0c7b462a8ed9c3368a6ded1147a18c90c709d4509785ebe689053c78c008e543509a4aa23ed51e6a394e2f14943c3b6f024b1819c45a80f077f76f0d8aadc3976129a59637bcf5bda24ddeaf66f4d93be4b3a7aaeef2b5ae89edaa7794cd0a38b1a494303c12bae48dfc2da19cf69cd8734c0247d62882d141b32c6bc744d0b431cf058df56094caa036495227d082771ad24e70575c1d5b7ffe7a97f8f00dea5f61b6cf54eb3157f711d73998b5f60f7783868ff9f47e8e7f57f605deb5ab77cf6a5d10608b402894306c7ebfa64e5d72ea85f004d8b213b007bec69a9d7b59503c2228a30acbc4347e8605bd80296a026c22f88776c65c818cf396c5175b1ff973159b1c6ccb90406f538eb89a6f55741f701fe4eb5bc6d4f97b69ad5c24cc1e373a92fa47066e200f8559b0070e8f75a0ee6261d01cd49ab6f7af4bce4c08512ed68354df735d53c2ba1aba22150cabb93da5fea2af993aec963c2609a73674607b37de665283dffc6b9f9c65474827ae3c1bf2939c356ec57cee2b11ade2efc74ccc2904a840caaaeeb6b444f4694f6d03c7e5d910f95279658a89b98f4e8f1b9f22d0e754d285ee5c53e78d651fb8f792d465699c60d9e3c7df58b0ea68a4205a592c6d08183a8e33077dac73d2515b886f2a88c671493c9cbd391594b832b7bcde36d0a2c971456b1e751e04693a075adbe8c2c74c8d2e3604259adebc69237b78fc656c62d435fd3cb4b584c96ec6a676379e492f83f5bead351d25dcc78b41b4f307795d02bae317a968cf66133640acacdf48eaa49c3a7311cf577b4e47b739b5f6f934da47482eea9fcb176e79b849c53619285a20afdf17839f2187e1e3cc7fc1d92a07280dededa29a6e3048b102bdba4cdb890c753b9871a8a7d272507af1110dd5738dea111a6670a03e07e11116ba05103829b8796477dc6582e5033b84eb96c3372128dc8f52e5640236b68e259695deb03065f120e1b616b45d37cee7204e1a6cf79564d5f5995db4a6783081bf4e59db66f9fa4c4e60b5a156e97dcfbf5376a7828eb49dbb8cbf8a7128142a542c12c7ebed640e82f853caf0c6afc72a1ace5e5467ceca2baa564fffa3657d7ae3ebbada3bcbf2503d4423bbe7170187b6dd6ff0647e43837a98c75d8b569da51b3964c1817da524b22f92456ee3a768a38d693a91e8033b2959d761304ea08c437ac1cdde0d3645afb48a8404d4b680d65987adbf587986e91330f7f1d8a7db98be237291ab3a127eb8a56aebab49c4287ceb1e562542a7011e48b0d4b24b0de5988c7b3d35ef666ed1df049b45f09a1768e2f23472ab8984882ec09af1ee3474c1bfdd6d4ed6268969d47d7e94e4781a65db1bcff2566839edb4e0792a628355bcefd9d5c08683e87924f172a14529f00904ba2dae3f40a7a012a52ecf56d0a8a5f00d1d086e1cfa0bcb29a408fb8f26a1f1243fc4e16392b15b5dc53c09539ae4a22633c08fa4944e86aa48dfd285832e79fdd1b97866228eafac1b01afd7f9001a0cbd9d83e84bad40842bb227307e8c8edfd0069ba98dfc26fd1147a67eb520ab9f4ec782d1f7927d42ea3ca41cef17a0bf0a82b2bb0f1e8b310a4c62ffb05a5f464af4759f53b0ae82bcf4d02138ab69168a4b14098881eee607888a92bedd09259167e49a124f65ffea9aa93442cfefc49a27c5aa7dcd4b974e508c09c2719982c15bf63aab0c6a028d1ac3b76e7bf9cdbb4ae16094b9a285803c4f3153a362405768740f100c50156b6a3b6068d94ac9eaf027073416811612996b4a4a3832bd3382161ba63a9515e0a3aaeda3efc718e9ac5b53810c4271a7c99f816dd6d2671560b6a78a470e831cbc5c577df7ec478b09b6190248f50edada9f20ef5e8563e82df7d91bc3e58929eac0f34e909298b14b590097d62e78a84606434346786c33ee89840d48ecfdacd871c13d0375136e08e32d38378220b867b78808a9e1ec7afcd2f00f32da72580bd429cd105ab3b49e87f0382b0e29d94aa9eb5a9d6cfa942f6fd6292ac74dd9bef12e101807ab0d94a31de3f9a0a63e9b16ecbf049dcdc1c06d656f0851506c04d152634a1f58eb4ad2df5ddc046beb34f66c7581fe8b78f56d50193a148e30d9f077be3eb1c8759a0f752d58bd24c7a6782108f544780fda5685d9031b1c5c032382a32d2a672fdf1b9a93c4fb3017cee82fbf63cd2467bfc2e4bd5579b1169c41b9e738f2a2597bc8e0954f3787bd7f3fa2045dc1c208119d9e92373a67a010048f29e418a302276eaf793e79044870bb79993eadbfa12f58531c52ffd27193d53ce5446aa83125521c4568a6a229a63c0ca5ef29a8e3fdbd777bbed1daf09aed3aa68297c395f363058eaeeef58407d3cd29d71d08513876c39d88af29ebf07b1877d96d4cf12fd8edc25017e895803da65ecbb756729076aab0613c9ad2e2b806046fc3a840848b7c83fa495baccef481acb2906a4d60ea369ffc5eb160bb563f07de9878c69cfee5e208202c16bb6caf44c76453205a3411b48358a7cf70eacaf0e8b9deafc574d453149a696cd81cf5f44caffbc31619b6a70dd7885aeb8646aba921787f4920679e6feb09ed55dd956d59895a0acd7ad8fce55492e1e8efa88b5f27bcb22f18c1a5234d5d011b5a4eb76c914c35284314da1e5459b7f35e6f696057875bfc82e95f5ed4a7ac11253313c7ab7018f7dcec6353fbc35e760daa048226b4b29a16cfb070bdaca58fbbcc992bf68ff19dcc8ec9643808d0550b226dcc7fde43aa491e99cbf1477ff233ce5ee045a13b7ab3e636bf4db4c117417aa9c3df256f65cdaee23281f4f28a6ab0bff65c3ea523849aaf5e6c915770f8c1a207764508d55122519e26380c0aa6b0dc74c93ca826632800b955f9eb07e97cc75c6271e7d378180b65299732d65da9df8ea68baf0d3ad3dca475066e4e2a803003959b36a5f56792ab79b2bd4147fbd89a24aa6ddca93991b2fe2bd3dddd8bd41f11fee40a42a831ba1a2c496a19f89dcfc7379718b4f1c4c03f9950249e0d7be1d3c11b40c2e6d9d54e7c2890131dc32694bf471ee1404ed90feb43eddb1c6c0669f4fbd1e8622b9adba9429e281207d6443ed64038b8f6fae86dccba1ee5a0a1cef8b7503d45cd327880899bdea4610b7c110dac0d5561999e72eb452f2902c99876c69d96f94edd61676afd23e759d37cb3132cdd395dc41d828fefc5113357b9f222083913499e43f0a93b3e12a8cf0fc78d8f98fea", 0x1000}, {&(0x7f0000001240)="22887132e9a49c1ba5c810435781891fb7a28be38388eb2e5026e8e287947f00c79eaec62199326f7102a89bede25f011d562dfd732c07dd2fc20dc8563f16cda6e13805fda7102fb376cb29b41c1ef04e7408d224338624259182bf72b8a51d99d34250ae157ae76fd0bfd08166f39ac617d2a824a2bed96e7350769f4a16d9", 0x80}, {&(0x7f00000012c0)="6beb6f4af140867768fb039d2ed4111733875e55064c276bad547803dbd923246b3613f93065def01eddc4457e97916f8d64241fe0420fa651754119c2073d7fb3804b892c81d90956ca446413a94107832e0ae49a17e9379fa4d4aa0cfed68a7287980983f5d39ec9dba1b60c3123bac5e3d39d7ab8ffe96f27771971445a45d9bef18ad5371c69e32baac5837f46c45720edfeb0d3500773af4b5dd6302cb93680c17a4124ac00d606b0b4cf296d4ab9c5865d1cc2aa4c5390d3ac75782ce2471dea91576b84d433e238a2968d514c45cd4a3adbb119a94b344a7bbda1cc0cd447eb5aa5a25c130275c551194bffade049987e34d7427efd9bc8b65a7ea9bfd5a95dfb7243cdc9c9a156e641aeaafbabf58eb9f0ce0b1a11832ae55b6cb1e9d575787e4446f151eec8f61ac7727ac2ad47338672a1f02d0d6be1bdda81e172474c816031944e3e14efc929dc4c096f5ce5ea967d9e6fed0d435a43185186f3ce4494aa3358356f5a59518e3f36e157aaefecdcf6e8f95e2e39892793d7d21215d000c228f0bcd345434eff210a01448bea75b39baff7f2c0fcc592f338d1385484ba756a19acc09b618562d385036a2fd3b88dac73f28096b24888dccd26bf5eee41257f3d453b80ec7513b6082b0e0cd40de70a306414bee3eeb9c41fff8335e96a66dfa4c25d00cdbe69335f2cb08f5c3219abcb8a3f9b97f5c618b8f9c5ce797c08ef3680de6bdffbc9d3b0aba63302b03b2a3451962008b8238d23e6b0f95206bd6c679a34cf2da21b34cf74840e08fb9ca3d5c8b0c4a66e13b39ab5ca70c2c11af64361c1ae745e7c84f1538997e90a5bcea4e4c5dd5f05a3bf573a755acb2fdbd60416d7e811e189172a69b97a2d9a9af75ac3e3c4d25c2d67625a4c8ebee81c41d5d9173cf27e07bf254760763af0e00538970e49865d11075f8ab5c5d773a13474b84ebc0461d93b353f55665f68558c53504b65700b9161aa0f75f2c8dd3c827f12bc5b376954b90088eca2318cbcde7f26b562f706e657114e05ccfe5fe21e269a473f69a971380ae357a423e81a3930ab360c4c2a949a5f19623051d7743d57cb61464b4bc4f08603b38a54d699cb9a82d36e3f8b7042ff93f3c31d9408a6af08847959ab4eb1de6f2b7e438ce16f8610e66919a95fbd00db49cbf963a13f00e9cb2689f8875eb24895140940f5f239c25e5c772ab9fa2db12932f5e57c9f9c6eae67334547516953e66ae88420aae21f275b13f63df5c9133f2ef2abdee1e8acb2abbaed24a0e1250793c1455bbe3202e7831db05988a0978facf89a21755476acfd12b0395d10cee69aaf6df140a461c6911747792bc1280e99fb0a9badc1c3aaa5b7fe4bc84202ba65dfa4d4247bde40f5e45ffc2c8b139ebda3f4b5ac9b526cf9b1f727f15956daef41b1fa9216185a4405fb9da90a4338db5f985233e6f1479792aefe1a9dca4836e9de83e830d862138ed32892164561a1cb7a56d610d503c0e35342796d0d9fc8d1059e390bc3c76867126f6eb062a515e8aa6c8532e411b42782ddbeeab16ba5fe120e6dc93f84621c5011405878160e9012302c72172e3d5740fe6aa6d4e42be3310fb1f0338eb71f57d26a371a4c3870a6f1884516ee55fd2e2a0d6a4d22482a8f4fb4aa9970591acea1bf55ac95ca55db3b9e1ff4b15be29a354bdc2a2e78b75469c55a1a530b03b56f91978629b1ca8b44f5567c4ad773a05fa3947ba23c349602f671a4877d01e565ad228f90794dfbe6a9fd931a3374ec1e2173eb89d8ad84faed44a99bf356c2fe877b0e1bb163e3e56e4b64d55fa5de36d2e95faa9843b424a6843256b7a3240fa82912bd1114256103faec34997de38c291a03e8932d8759d5fdc638fbc6caaa53c773da9e27b423c5d353033011d84b7a510706c9fd03e49d3168f05ddfbc1c3dd74ce1bd7d735066382a86cfaee14098430b036be429ddcaaf46866dc8c882fefd999802bfecd8beb975ace8626e08b1da639990c5521677982b7f07ff5dec2be9b3191babccce4f9d75c0fa51a12c5408ebc19f0a4bcd74607d5028859b9463fc055d235d33f30533098187514a06d8e57df01136eb3526157ed75dceb09529b7d4a995e81278eaa22450416a268547d22a2351bea9ba19ace41638e4ce75bfb902988272cefa4e64f0a7aea390200f5662a9cb1f54238ad011f6eca71f09f8faf8f9a5a2df62e6692ed70eb5eeec52abe4779cb5e849dde23be219f97a0f173cb8c08c88e234f42869bfeaf2eebc04a9a1631b4f22463ade2d98082cae8255be8ae365615037ca8e7800830cddd6ba072083cb9b015b813f9b3e14a2d77357dea5adcfe63affee9fb2076ac00a41f00b9d6169075e25e38280dd13fad2dcb8137f78823a32d7485ba9e0a78adc1bd6dc5459182eba39988f22d8dc4a63e285cc1ff951b13b77408c8ebba86984053a37e03040556de5e438c2c0a0de23b43ef331b660b827737b3d03d3d58cf9be0076d91cd42437c72284127d6a0f0be6dbf468f4782df947da0d5d40f8d2191951eae77b4fd6dc935468b7fcaa641222b8957f137457634a79061ae9698c06c29e739d342d3ca4d6d38af1acd9f31b9c0fe13b5dc5989c914770fd6f5605189b8303c6916d1082d0799ec45c16adf1763713c553c7fe3591929eb38c3de762816bb56822271a85f7bbfde52fa4a81b8600cdc53297f0fc04556d7335bbf82e008e5ac638db564010fdb4d8aecd346b5e0964e63b891d2b23f83785b1efeb454c2ec01a4976851fcf08b04c07bf63a719f323be12d68b7f2172c315f187bd8bfb2590cf8ade7a8f06278612fc8470ec8203d4cfe3f02d2d2c9e59e4aa220fe6d58ea18cc27bb2ce9e0ca7bd0bd2327825a7b390f5c7a0f3866498450dcf0c3c5feec6b607ffc3fa6ea8585e86c594677d1656470bd983cae1c75de93e4df46beb073586904a5f8d8a60f7de78b81a222f7a721c321c40b48f79f8db3316ff78636e39048d1b6f53b53d2c873553257ad755859b49043b0bd0346a4572ffa7cd01cc9abc91721b22bf61a4949f335cf15b7b63af0ec4067c6c61ead4f3e592946e31e44461e78039dae7904f8c2e3507463d471a53c0be8b34386674c2a0bcf07008aac7018aac64650126cf8edd9e6d9b0ab8a380aac7bdea5e81b1cf139c39612114a689add7e600600d012a935e31e2d9ca3d9e71197a7da6da8d7dbd8fb2dd8ea03639ea4e5210ae1f37f8a5f4eb9ee80e1a0ffdae8a16f9b6876596af4e4e02fc0ca82382d98c158671215733eec22c0dc007b93a3b3afb361307f0499aa2b4f78688604768af1a8a1e8a1c71a9a9de39ece74971d308c864cbb304b2d822a6548ef1565407d7148c75454322e55358d72d7dbb15b62dc39b6c80086d3ecbc13c01eb1fe0cf5f39ec66efd711ca85974bad30e8d6669bc67c8dfe2c3a1f90ce1898dc4a69dd50f65296ade6002fa34f6d420af98411814be4ffa69146d18a4a04d88864428e05d2fcc38b6f92322883cdfd8f00c03df603b4b20c76401fd95073dacb9a30be78e744b055782f23392ee6463e79aadc3b6ed342bd22c25fbf3da00219f5b12977327e885bb229edbb4174dbc05615714a4734d126e4ea41195f035790941c7d93c746bd46c83ec94271c00ee233a3a34b4f2b78f8dfe22e1452fb989e82484163c10e339878dccc9d7684da77b58d730708ff3440990346384a6bfb6899170fe0af86709ee67dd780d6374956914adb6c6f599945528082d4190ede2d40b5845f862122321570ea55220948da6eeac197bd232bb4e442fcd1bb1ddcf7907661c793a4f446184f6a9903eb4bb51cfef95648d186adac5f639ddd54fb5e794d394fadd31a53cc0c1e2610e64ddcc36f13c36f176fc2333c93ffc23f5f70aa227f9f9a9854e669679b9a3dff819fd2169c2063bb3795e70c9d541e73482ebeb384c3ccbe5f70f329099c0658f4c3d781e662dea7402918a299f5660975666cbb7a727016c2aec452864cd2038302585107dc147816d1e3029fea57184abcba19a3035851550c5711c508de909a09b8fcc6e73d4f8bf0272ab95a3eacbdc5c5079e222740f7d93585a22f46657af916242d099737eb064d35cbd97ce2df6dd86ad6f1f3e17bf1784042aee1da1598677cf07f959a28127caf129f4ca891a921d699dc21dcad48268decdb0ba7042569647e292b5e58f9f8f17210ed92dca20924d50de3be17c9cf92b2bc740ad1b674307bfcea1a5322c00ad8e06311d16dbc93cf4cafd60746d4fdc22a4a7070484611bd451c2896812189f76ab3d5dccc09af669373f9691e9616b0edc2607e42b2d035d0a7cc64f1b7c5b453a1dee058056586b85ea133b0bb4acb1cc364987626aab27e27232338efe32bd7bfadce1e3217caee20c5cd8e00073bced19e57c466965511d69a0f78ceb7d003a9e1a98fbbe274f965c6aa3d1914e6fbce1a6c13da3752e656f867dbc4769e01339ef50f1694b8f450557db34416783d9a8bc513107cb5251c6bda5ad3a75595aa797997e1a8e0b28ec932a0ef484c88232720463795dda2db551c474b6dc5374707109e3c79442eaf9bfc3e76d072484737e86dece49045667c0369495166ccca5337116cf88f011e0d4c6893ab60fae9acec3f577e2b8d6fdb8aeb89d1cf7d48266800bea7d851ea116eafd08b23fb56fe8af9cb0206ae65f9a6167dfab48d41bdbff88fec1fe3a3c22031fdd5ecc43aeebba4c71829f5d0ce6466ec4f498e5c22d44398a64a4c167b0a8da08b7e68d7414da034cf372daa575bb59b9d5c5a4219cd1678eb5a22cf360b02a2d854d75b9da48110a14453a0a6efc2e0b30a4d044480d9cf55aac2fd19a72c2e4ac27104b7a14e611d70cd6c662661b5e401cf56e4477f2cb47f0d777277316c99b0332544833194a8901e0119ba59cba8a28b35e0ece64dd6b704e3d32f51cced9d0c9cc20026c36a83db659ba14a7442a9ef2a27a4c1d8c2d68326e16d7caa8b0048a7dfec9f8e5d78259c4620c818a54aaca37b560e15f15a4c691d23b07976adda254c4cd1653cd1", 0xe03}, {0x0}, {0x0}, {0x0}], 0x9, &(0x7f0000002600)=[@rights={0x30, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}], 0x78}, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000004c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCGWINSZ(r0, 0x40087468, &(0x7f0000000140))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000040)=[{0x14}, {0x1}, {0x6, 0x0, 0x0, 0x5b0d}]})
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
syz_emit_ethernet(0x4a, &(0x7f00000000c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "b43751", 0x14, 0x0, 0x0, @rand_addr="fe97abf27489b45ab9ee33524fcfabaf", @mcast2, {[], @tcp={{0x1, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x35, 0x0, 0x0, 0x7fffffff}, {0x48, 0x0, 0x0, 0x3}, {0x6, 0x0, 0x0, 0x20000000}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, 0xfffffffffffffffe, 0x0)
r0 = openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000001380), 0x80, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x84}, {0x61}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r1, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
r2 = getppid()
r3 = getegid()
r4 = getuid()
r5 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r5)
r6 = getuid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r7=>0xffffffffffffffff})
getsockopt$sock_cred(r7, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r8=>0x0}, &(0x7f0000000100)=0x1)
setuid(r8)
r9 = semget$private(0x0, 0x2, 0x189)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={0x0, 0x0, <r10=>0x0}, &(0x7f0000000340)=0xc)
semop(r9, &(0x7f00000002c0), 0x0)
semctl$IPC_SET(r9, 0x0, 0x1, &(0x7f0000000380)={{0x20010007, r8, r10, 0xffffffffffffffff, 0x0, 0x100010024, 0x7}, 0xc8a, 0x0, 0xfffbfffffffffffb})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001480)={&(0x7f0000000000)=@abs={0x1, 0x0, 0x1}, 0x8, &(0x7f00000012c0)=[{&(0x7f0000000040)="25c40629f2382c15b163d5bb2a4c862ae040c9e314aad462951806da06cdec5a0e4caba348c211d2963300495f8f287e61ac", 0x32}, {&(0x7f0000000080)="baa67fa0fdac4fd32d4b19b65be2e62e8215ba1981a0a6070f691a51af3694be3a2ffa2e048b5f91972c1b49c772d5fdf1f584be18519ac8285ddf249d9a0e28459062479cc3c047d97b2685dfe36cc182be466060a9a87fdf1e20f127cb35a224df272070f67b058e3a858ce560663ef938420985606aa7842c9ce0628546a6670fed9ade3c6ddf0e796389e310130049b54e8098036c0d5168cff7e3408945640789ce60bf47f152ad417fca78822a32eba7afabe3c5d0ba3eae3314a32b4a8bc2ab1db31fdd1a97ed2b9492f78ad55ba6b388891a1d72c0dd4f9c8597b6508b73e5cec7f2193e0aebe0848165056b5d5de974022c3b18ddd3af1dd3349a85b110ffd647023265cb21fb8625da02a3545c24fff55d13f2bbf4b201047a336cb132d44b85a9443879aa393d2446e91475e2de8111b5c909845a3e2b624c592a4003e33068108b4b0774804b8862bf2871eeaead9daa5e61b67fb411ef81a957e9b99a9b2d364a00749091014c173262670e5d206f0595f48136ce094ab9e2196b53e8fcb54dfeafcdb052513613adae69f43adf29346e5cb3179670ea0548be1e8df1e389a6b30cda6ee19f75235a58ff174579ef062de2c52ad03ba62624b3d00fcebca4effd1e0cabbc7a3e86433a9abb3ab9edc5c78b3b14c70e6dd14d40b89364acf6c41b0126fcafe1772e97911c3ef095b2327788d791bc11857751c8b433e3fabd571110da1a991b94c7342ecbdb1eca04eae81f72b4e612eeaf040aa58c162891058f5d35ed1976082d6c6b98c6e3372041d391bea48b7907085a93e8d877f787ecbb96f6df698ad606941c3c129a1731b1701a7485d62cc627ce79cff18eac59ed7d8baccf360ff4d6b02b6ca4d21b3bd33d59698fe24138e0d56d91613c61accf962c219d80967c1fb7d40fef12014d5016260dacd5f0eb453bf33fcc3cd7a3e97b47d8da9d765357c6ca7d65ee518d3225932bfa60096aabc67e372638b5706b0cbb1c4bb7b59b0d641eec1319531c0f96814ba88b1fff852caa49875619f65ba4d5049f9daa1f80d197f50714fcb062d16c69f5f493210074fc99a50c5054f924ca361811868a08a284b35ce38fde8327cc4f884daebec011bff077f9d1eae204800c9e49790979855fb158149f42ae78503b858a9e0dbe7ef7de7e95ab2a3a80de7d9f290cdf0c74b7f1d24c7923c3049869a6fc3cbf718c452c3be9e21a63163c3b700cfb07c8ca22d5f4f1840a6cdf39805ed03b892742615d40ef58c5800cb704a2cc078721f54b4f70e34e1675d81a875fc8e3e2d64af8d7f3207a11d89c1d9b8df85e52aa6dade1ee79da81014a31c78cec8cf8fcaeaf8417001b88d7329a23bdfbad209d0d2d8d9447a7ba05608df2eb54016f7e27d072d50fcea11bbf64329ef9c232ad34969f84677f4d64b758ed9b0f87af673ff025e7eb9da3636fabdb4b9569b77c587314de1ffc5781dbeaf40754b7acafbc72948218877edba234770b038b3402faac7b2a31e696b16d4094d3acb64a57b93df316040b635daf9e4dea6701cda54f9a36cf1347dea1c3577039b0af8430d428ebd5cd0ad135d3deb202c19104f39f57214b06eecc038289f02282faab0cd36faeec1c828a99ca152699d4d868f693da28417810999911cb5c8f1bd310e2b5de24c6676a3ad20bcffaf0ea9fe977f2860b720a512c81f310a271babf8520243c9e709bd3c7ff8df701fd9be162a8281b5457333cefc6319b2906fe348ef60a9e8fcaf6f3775132812a7281d121275236bf84d65c1eb741cc11078ee4076ea07362db5b0a68c8ab8f328a1163df7632bc55133978d0c9f69ba53c54ff147dfb909a4c948f67633df835abbdefc411cda886add53497155deedc032a021161ac4434d14327423b8772e0327ecb371cefaf450713b56e35ca1994417d890241a1e940421f6741036cfa7081bf3bb726b58884ce64ab8ef802226700b1805116b28fcf1a1cf1d0d1838e28399b8c73306b85c8c38b5e3002ea069bb038e296e7ed7258bdeca24b19778f6a869cfae0849aa6b070408b2d3e6a7475f72da91075bd1fc2f45425b443be2e80ae6dc2875043fa308ff5a540a7390d55d2382509a50a855c44aff8bdfc7d363ef60e86558342590f72190d68506cb9e1f35564a86b1f2a2ae0ffe735bad4dd5a0a66e08f751b8262dcb5dd0bb3fa721c1e170a5f8d26b7d4562ec15003641a58a1e9759c21ab8824e2f44f95c7f02fe48aad2c6eebe13f7e957c774f567a908cd6c65cb045f74bc89b51918bf993750df0ba2e33760d149730a5b618dde0a56529ab34055ab4b6c85f3f76e91ced6101c4386cea7499490a9c56e387302d7a2e6a8ce5824bc22dba0001b3a23e121862d4f0e6e42d6e5f9bccceefbce0a137f002b20588e68223416c04cb440e70e7c81b35202bb5ad857b7e7b11c0011d1d773788319cb0cdde6ac8e2212836cdae5103a918ce5bc0f1e64d117f3f0bbd5fa62aaba11b9c7f32c7d5544891cec7dac559c91e680b61662076dd53df741f66a9cc923fdc9af12a9c34312c6928cb774e15c9eb01b3b2baf2fdaf7ddd99aafb70b7007ab387a97a0e07931e87829e8d2156d5786ca7c03b531ad56ba0c7491ba2665cce53c022a1c454f19a2e91228b981abaceca51b85a2724a5ba5a3689c6001f5399a466336327d0a975800f7886e2b928bfb200eb4f025cefa6d92615220dbeb3fd7db71d1ff6e0d1edf9e51e097d586d9d462e90f6fffe2843ef2ab0f24ae0a02703d3ddb466ae823c2486fa36796e8bb4462ad0c26660d345ceb008e0698856ed47e03d3154e02a090ac324841e28b7d9484f3f9cf987b9b2f2acc5f5e7ed8f1d493ae188b471ab428229115810df0d4288090ed8efb477a062095e32606154a9a203b112d761020b924ae9955dd675a3b499bab31ef059e57027574c26bbd7513b44ade249e8a2f02516a600cea8dd091fbd0d05bebeff501a05b8dc832fdc2bfcae01d0ae6c273ebb0c812a0ef8ca043973ea4c81c33b2755036e7d96fefb4a43e6d31a36dd02b16d54a9a5e5172ab326ca1d07b95692f978e429d028cc2fc5e15d890f32f6ab856eca56faadbaeb22e95ef79bb5725721437375aadf44b51ee23e7a5991508fb33d82a0bde3040ed49e10e774e40c659a1a052c5f5f24a2910ffbe09822a6d7bc62bc110aa5a2f44061e12948ceaac3b139373d52efab0e39250a7cfa9dfa3a5cf005c427cad7d4734bb8997c55826167232461015bc3e495290a2d4f44f47fbcafd2f1264d96827f5e4f882d915273c30caf6760cce60812538177247c0012821c3dd6a84fcfd4a6adfb36932f5b67e1fbe3764d2b794af8a59fefd4da1307911fe1e07e364d4d45365e1522478bc007b2db732af6a137a69e2a9d8781b2f0a54f217abd925fe9fdfdf5a0c617e65adf4a1850195de592dd3bd0442c0e4c741a7b17724a27fd449b6ca298dd2a07624c4cb7e5f86009883fe20bffbbefa60d86b366ff0dc59a127f6f9b98a27d370be9eac649a0dd369db33ad1740f458d82e6a9928c0e1988e45591fa4be2f4124db0e378df1ec8599f71f87f012028a098bd965841baab5d485468b6017cf8c53e2a87a0f80e086affd746286c002bd7bf85ed1d5adb70e6772bbff9cd94b0183a4e6cb8b35aaea483ca609c81dd0dbb5c7e863134868983c05061931e71ac36b3042ee133be96deec1ee18e84eb05db9eea0b0c3b872c39bbd302b478df8852b3699b7d1440df8af7ef48e870ec83499752519c71d0016c44eb4058debc78db9860cda76da0796ab12541abd8eaaebad680f5f1191ec23beeb19a48d502ea0e9d8f2034f4f6790429414a6e72474952461e59cee2f4f02b0dc1b2cd58817e2bce638922e3960bc1e4566643b6ce7f7fe9783e946c2eb807eac47d57d82d66d787fbcbd363b4da484ec6b21b097dd8a097bb7b39728cd8b47b89d0ef1ab69380c2db310c0320ff607b2b93e20f93cd87797bac275edb13163f9de82e9d510c9171d98b1a7cbaff137886279f91ef3a87d49d0a7dc258fda3e5e5ab8ae45087d16d95b6c383243197bde55bde8dbb6ce693a409f208fd966a5137d67b0bd96e6642c10e6c0e964a11cee4dd2f03bdd4080e623b8ca361d6620a553fd115013ff70ca302b8fd3db70a7f986db87d5abd8a5db49da34fbc6f4e115e21783e0846169bd1636c3e44980c3494518a1db016f19fbc7000b22b43c74a5f9f5f90bad20a59f9f9ccf9d99527d2acec5054d9336ed01d8403df76f5f5bee24c060aceb4d95f33918eadca144ae42594c284e67adaf203133d1d8bb21fe4bb684bf834853bece4736031d29561f35d95cfab095da10675423019c8bb12dfebc72b848dcad8716442f6c83c11b1c9796f5c3de2df8f771b6c17289542e4680a80728286018738b716e99c8bb29d14dd64acab0df0208e04eef77e2e827aec259c5c5b8581aa111a450201106895429860437b36b02dec30de69477e7dc13c8eddabc3b3d50f567b1aefe752055a2c6af1c0ae23bd9b7592d4373f86c1775ae12b8b292991718f8b96d061e69a5c6cf2eca56344e4e400a7dd9ef36b7d53df3648c96aa603c95a3a39854192b53bb79348104c1403695daa956a9dfa46da131204cb0dccb4cccf9ec4f2a459e1a71aff8d25f1b8888b5a5424aaad6f0503fc7f6e6ac46ef5176912370734978fdb602e29db924286cb5945b2565521fea322a62aed5029342b05909826c624899f69452f7dfa040cb351aff8ae248043fb7547d1478aa3ba668a42eecfcb03de90aa1a258aef5b269843b3f18875753f2d26e4cd7f633e8eb2fbbf10f1187d8efea57f08abef280dfb26bd42a4dd1427a7332bc6fd9588c72206774a5a234791f547632e6f9bfd3aaaac60f9dd48e4f5ea40d8f5d61036f42a312c56585b701e7e5db5c0b94795102415997e9fcef750ec94436df87ed1ab26a60f13ec353d7c4c38bd654c863eae122b96c7dde618f4c090f6645d128c0b100d5f6392a85404c57280ca8226cb5612816c4114ae14a7f3053b77cc2e37cf245cbdc063ef457c39974d098b1bdb6094d3da12e932785eda3b008eb995654cdfc45312ab30ea49d04f0ff67fc7849805498994789cbc194781b64c394bafde6ce7e23ea67838cb28210a09552e057eac699d800c5b7e23c24b13661dbe7496137db614225b0bd6338bbbb47888c0e095283ef029bad38f6ee96dab8c38ba007f5f68b40be0efc5f120a28803df74173f3d728e73f0662bfaffc8d6b1f59d847262c3499568fa810ac8a585a0e30156832e2023d487ddeb0ba3f9121b3ec93f213f5a9b7faf368d9384f586e92e2ffe975784785b6bb3feb9db166829ff5b4e048d4686ae2fec73212b82b71380cf2881a3e297a21869cb5fa79e0cf3acb73ce59e173da3fa497113e5f2e1da11121cd0cb3a5cb4ce6c985b6fe01673321ea4bbcf0d5f537be8b3166d8310dbcd98cfa9895abeafa676c55ce254db044b7b5168a30d7b38d17e6434470fdb3df2d43a38c00c8ceb167e3cc7a78f75cec3231de8bea0a5b5a3eb5a84f175eef85f4440ef582f0d694459aef8d9b6a805a2bdebe525c164b74e4473f9b419eaa590456e522f5817d98c24eb3b39f2ea12a799daa2822c425a77cef6d6152552c50c1b36c826534bbf0aa828a7ee9525e8df72b452433e113c5216db918baba401444cc465df82fe7894d7dec1be4bf21b72f651050fa2df9ca2fca9461d0a8503263a05fb648623ce9325d7b3f92059c84a75", 0x1000}, {&(0x7f0000001080)="f93bf31337e8281567575388bddb517e1cb60c9b4b36055753ea60acbc39c2dfdf2692f14ab9d018d52c9cb2e030d05197ea466c6fa9440cd2e12267d8a9dc09ecec6603ab3c37dcacd701cfa28ed2d4e5222a4c79716a70bfdb0a75c1f996f681707b3092b7d314b11b90a924b8f1c3d189d82d02c595847f2e835dac2305436ada3d548d2afe08d0356f8a3bc3346fb9390174678cf5af015236556eb156346dff9012574d0c464247191825", 0xad}, {&(0x7f0000001140)="03389dd89b9142e97b42113aa22eb37f3a75a7cae0dbb363a30eaea8bf07dd903468cc0fe285c5fe3638dcbc45cc24ce948ddf675ac9ff64464fd2908a368f77", 0x40}, {&(0x7f0000001180)="02cef7a80ff3bb7aa1abad68beae1c8cc9152f9f597fc03dcaa1f82879d0cdc2d1fe428b212883961dc8df1bac199c664329dfbbfade3d198faa539046583a9145f00495e781fe2cf800e4be17d8451932df91d4c6c4e662f28f5a4cbfa08f05d0cf0ae4d6e9a083b80b882ce912881d73bd8d575d519aec1c9192c5d152353ee8c9ee26b42b254daf4ddf88fe232504c1e0193319955bfc1c2c55ad5f011a81b419e90c53e3d481ebdba36742ebf20b7fd89cd4048b3f440b8fa77262c059150e4e1ff49cbb100b3d350e05e4e5b8", 0xcf}, {&(0x7f0000001280)="1f82b7d773a2557b9287e28ca3b90179d9b4690ba8b16b94a42c9ef5ebb61182db3adee9dcc9222a97fb7ce56d1903529502", 0x32}], 0x6, &(0x7f00000013c0)=ANY=[@ANYBLOB="2800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32=r0, @ANYRES32=r1, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r2, @ANYRES32=0x0, @ANYRES32=r3, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32, @ANYRES32=r4, @ANYRES32=0x0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r5, @ANYRES32=r6, @ANYRES32=r10, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRES32=0xffffffffffffff9c, @ANYBLOB='\x00\x00\x00\x00'], 0xa0, 0x4}, 0xa)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x3, &(0x7f00000000c0)='!\x00\x00\x00', 0x4)
setsockopt(r0, 0x20000000000011, 0x800000000001, &(0x7f0000000100)="186fe65c", 0x4)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f00000000c0)=[{&(0x7f0000000040)="a99c680e474e116027fb4a18b960a02cc2108fad84", 0x15}], 0x1)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x1e, 0x2}, 0x6, &(0x7f0000000100), 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x1a}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$TIOCFLUSH(0xffffffffffffffff, 0x80047410, &(0x7f0000000000)=0xffff)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0xee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x80, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCPROMISC(r3, 0x20004269)
dup2(r2, r3)
sysctl$kern(&(0x7f0000000000)={0x1, 0xa}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0xffffffdb)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f00000000c0)={&(0x7f0000000040)=[{0xdeadbeef}], 0x1})
sysctl$vm(&(0x7f0000000000)={0x2, 0x5}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x8005bcd)
r0 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000140)=[{r0, 0x4}], 0x1, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
dup2(r1, r0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
recvmsg(r0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
mkdir(&(0x7f0000000400)='./file0/file0\x00', 0x0)
setuid(0xee01)
chdir(&(0x7f0000000040)='./file0\x00')
rename(&(0x7f0000000240)='./file0\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x3d}, {0x2c}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000340)=ANY=[])
mprotect(&(0x7f0000000000/0x4000)=nil, 0x8, 0x0)
select(0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0))
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
rename(&(0x7f0000000200)='./file0\x00', &(0x7f00000000c0)='./file1\x00')
openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
unlink(&(0x7f0000000000)='./file0\x00')
execve(0x0, 0x0, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {0x0, 0x0, 0x2e4}, {}, {}, {}, {0x0, 0x0, 0x0, 0x2000000000000}], {0x0, 0x0, 0x0, 0x7}, {0x0, 0x3}}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
mknod(&(0x7f0000000100)='./file0\x00', 0x2000, 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r1 = syz_open_pts()
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000140)={&(0x7f0000000080)='./file0\x00', r1})
ioctl$TIOCGETD(r1, 0x4004741a, &(0x7f00000000c0))
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x1, 0x0, @remote={0xac, 0x14, 0x0}, @remote={0xac, 0x14, 0x0}}, @icmp=@parameter_prob={0xc, 0x0, 0x0, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @loopback}}}}}})
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x5}, 0x4, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1a}, 0x4, 0x0, 0x0, &(0x7f0000000140)="505e3f62827ab105dd1bcc6817347199dd881f2f", 0x14)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r1, 0x1}], 0x1, 0x0)
close(r0)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0001000000002080000020000000ff00002f0800"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000040)=0xe0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x2000, 0x2cac)
open(&(0x7f0000001700)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x20, &(0x7f0000000680), 0x4)
open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
r1 = socket(0x2, 0x2, 0x0)
sendmsg$unix(r1, &(0x7f0000000380)={&(0x7f00000004c0)=ANY=[@ANYBLOB="00022e1e0a"], 0x10, 0x0, 0x0, &(0x7f0000000000)=[@rights={0x10}], 0x10}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000340)=[{0x28}, {0x80}, {0x4000006, 0x0, 0x0, 0xffffe000}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x2, &(0x7f0000000040)=[{}, {0x3}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x4, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0x68a, 0x0, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x81946467, &(0x7f0000000240)={0x0, 0x0, 0x0})
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d733c4f6ff45e400"})
write(r0, &(0x7f0000000180)='X', 0x1)
close(r0)
syz_open_pts()
r1 = syz_open_pts()
fcntl$setstatus(r1, 0x4, 0xcc)
readv(r1, &(0x7f00000005c0)=[{&(0x7f00000000c0)=""/145, 0x91}], 0x1)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x20, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
utimes(&(0x7f0000000080)='./file0/file0\x00', 0x0)
unlink(&(0x7f0000000080)='./file0/file0\x00')
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f00000001c0)={&(0x7f0000000180)='./file0\x00', r0})
r0 = openat(0xffffffffffffffff, &(0x7f0000001740)='/', 0x0, 0x0)
r1 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
mkdirat(r1, &(0x7f00000000c0)='./file0\x00', 0x0)
poll(&(0x7f0000000100)=[{r1, 0x4}], 0x1, 0x0)
renameat(r1, &(0x7f0000000180)='./file0\x00', r0, &(0x7f00000001c0)='./file0\x00')
r2 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
mkdirat(r2, &(0x7f00000000c0)='./file0\x00', 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
renameat(r0, &(0x7f0000000180)='./file0\x00', r2, &(0x7f00000001c0)='./file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x81}, {0x28}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f0000000040)="356a6edc368489ad7a2d8d9d1163", 0xe)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x67, &(0x7f00000000c0), 0xc)
syz_emit_ethernet(0x32, &(0x7f00000005c0)={@local, @random="3e94e1b1da6d", [{[{}]}], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @local={0xac, 0x14, 0x0}, @empty, @local={0xac, 0x14, 0x0}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x7, 0x408})
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000)=0x406)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
readv(r1, &(0x7f0000003440)=[{&(0x7f00000001c0)=""/115, 0x73}], 0x1)
mkdirat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
symlinkat(&(0x7f0000000000)='./file0\x00', r0, &(0x7f0000000040)='./file0\x00')
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000180)='r\x00')
readlinkat(r0, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x800000000009, 0xfffffffffffffffb], [], [], [], [], {0x0, 0x2, 0x4}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
lchown(&(0x7f0000000000)='.\x00', 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
shutdown(r0, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000000)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@remote, @broadcast})
socketpair(0x1, 0x4005, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f00000000c0)={0x0}, 0x1100, 0x0)
sysctl$net_inet6_icmp6(&(0x7f00000006c0)={0x4, 0x18, 0x29, 0x33}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCPROMISC(r0, 0x20004269)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xfffffffe, 0x0, "53e0ff0600000000000074e81a00003f1376a400"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r2 = socket(0x18, 0x3, 0x0)
dup2(r0, r2)
getsockname$unix(r2, 0x0, &(0x7f0000000100))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000140)=ANY=[@ANYBLOB="bb182e"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x3d})
r0 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x2, 0x0)
r2 = dup2(r0, r1)
sendmsg$unix(r2, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1005, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f00000000c0)="eaff115c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e014f8c9c90e034001000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0xd, &(0x7f0000000000)="e014f8c9c90e034001000000", 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
madvise(&(0x7f0000454000/0x1000)=nil, 0x1000, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)='9', 0x1, 0x401, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
sendto$inet(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
linkat(0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0)
pwritev(0xffffffffffffffff, 0x0, 0x0, 0x0)
socket(0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
recvfrom$unix(r2, &(0x7f00000012c0)=""/4060, 0xfdc, 0x3, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000040)=ANY=[@ANYBLOB="fb182e0b3da63d9fb9"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffdf7)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x0, 0x408})
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xffffffff, 0xbcd8, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000200)="d8d18a66c710bc5b9b35744f070058846ca5b4b311db6d26d631d88ffce7e35aaa76", 0x22}], 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
write(r0, &(0x7f00000002c0)='{', 0x1)
execve(0x0, 0x0, 0x0)
semget(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000240)={0x3, &(0x7f0000000000)=[{0x54}, {0x2c}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f00000000c0)={@local, @local})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797b", 0x37}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5", 0x8f}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc020699c, &(0x7f0000000300))
r0 = semget$private(0x0, 0x1, 0x0)
semctl$GETVAL(r0, 0x1, 0x5, &(0x7f0000000400)=""/140)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000180)=[0x0, 0x0])
r1 = semget$private(0x0, 0x0, 0x0)
semctl$GETVAL(r1, 0x1, 0x5, &(0x7f0000000400)=""/140)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000180)=[0x0, 0x0])
semop(r0, &(0x7f0000000140)=[{0x3, 0x8, 0x800}], 0x1)
semctl$GETPID(r1, 0x2, 0x4, &(0x7f0000000100)=""/35)
semctl$GETZCNT(r0, 0x0, 0x7, &(0x7f00000000c0)=""/56)
pipe(&(0x7f0000000600)={<r2=>0xffffffffffffffff})
poll(&(0x7f0000000180)=[{r2, 0x1}, {r2}], 0x2, 0x0)
r3 = socket(0x11, 0x4003, 0x0)
sendto$unix(r3, &(0x7f0000000000)="b1000513600000000000100000100000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d0001e4eeb92a265402026ba8af63ff37281c18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500000002000000000000020208a371a3f8000400000000000000", 0xb1, 0x0, 0x0, 0x0)
fcntl$dupfd(r2, 0x0, r3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x1}, {0x5c}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000400)="fbaf8a8d1a029be96914f6357e3a", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x1001, 0x5, 0x1fc80d88, "00d35ed900040300"})
write(r0, &(0x7f0000000180)="ae2d6fd8b2afffd97b5d06dbd99ed95af56260e8988dbe2452cc42d6f3f3b304", 0x20)
r0 = socket(0x800000018, 0x3, 0x102)
recvmsg(r0, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=""/17, 0x11}, 0x0)
shutdown(r0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4, 0x10, r0, 0x0)
mmap(&(0x7f0000003000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
close(r0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSRSIG(r1, 0x80044272, &(0x7f0000000280))
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000002c0)=0x6)
close(r0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x6a5, 0x1fc80d8b, "0400e5ec00021ba9b3c70800000027f090b0c7db"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
write(r0, &(0x7f0000000140)="789835b2f37145fc9f11515d5f6afb22d3039b92a020b08b4fb6392c47fdae2461817977ee007a27fdf242288fd73195c5770608d38de2b01898f40885e68e60d8d871ffcee9a4474acba114cee5582abb3a8b489a9f24200e35322f6fcdc4b550f63b15d5df993492a1d5e0110bca2784760c1d4b84e3ff0df25bc17f8cb6704b722e5bd28c390e53f6e49dc2a8ac830ff2040d1b0987f27d3a8997f0dff2844a42160d4b39bc27d18218758dab6d59cee61ca5ef8810410a332f", 0xbb)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
r0 = socket(0x2, 0x8003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1006, 0x0, 0x0)
syz_emit_ethernet(0x2e, &(0x7f00000000c0)={@local, @random="2e8dd386ef21", [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}, {[@rr={0x7, 0x3, 0x9}]}}, @udp={{0x2, 0x0, 0x8}}}}}})
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x4, 0x0, 0x0, 0x800}, {0x3, 0x0, 0x0, 0x8}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, &(0x7f0000000100), 0x0, &(0x7f0000000180), 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000200)=0x4eba)
open(&(0x7f0000000240)='./bus\x00', 0x200, 0x0)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x8110, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x8c}], 0x1, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
open$dir(&(0x7f00000002c0)='./bus\x00', 0x20, 0x0)
dup2(r1, r0)
socket(0x10, 0x1, 0x81)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x1, 0x0, 0x200}], 0x0, 0x0)
r0 = socket(0x2, 0x8000, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000000c0)={0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x6}]}})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd604407, &(0x7f0000000240))
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="c400000000"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
socket(0x18, 0x1, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1", 0x10}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
r2 = socket$inet(0x2, 0x2, 0x0)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206979, &(0x7f0000000000))
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="110000002900000023"], 0x38}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000), 0x10)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
r3 = dup(r2)
write(r3, &(0x7f0000001380)='c', 0x1)
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
openat$zero(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000040)=""/157}, {&(0x7f0000000380)=""/4096}, {&(0x7f0000000100)=""/190}], 0x34)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x19}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCGRTIMEOUT(r0, 0x4010426e, &(0x7f00000000c0))
r0 = msgget$private(0x0, 0x2)
msgsnd(r0, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2cc63a9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r0, &(0x7f0000000500)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da6461341b1f80b87cb6f04b1aa1995045af2238befbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6a"], 0x6d, 0x0)
msgrcv(r0, &(0x7f0000000280)={0x0, ""/26}, 0x22, 0x0, 0x1800)
msgrcv(r0, &(0x7f0000000200)={0x0, ""/233}, 0xf1, 0x3, 0x1000)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000001c0)={{0x7, 0x7}, {0x0, 0x5}}, 0xff5a769dfa4b7819)
r1 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x40, 0x0)
readlinkat(r1, &(0x7f0000000080)='./file0\x00', &(0x7f0000000100)=""/139, 0x8b)
r2 = msgget$private(0x0, 0x2)
r3 = socket(0x18, 0x2, 0x0)
poll(&(0x7f0000000080)=[{r3}, {r3, 0x1}, {r3, 0x40}], 0x3, 0x0)
setsockopt$sock_int(r3, 0xffff, 0x1023, 0x0, 0x0)
shutdown(r3, 0x0)
msgsnd(r2, &(0x7f00000007c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2cc63a9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7", @ANYRES32=r3, @ANYBLOB="0501422dac3726d0e95e89728f8fe3b9306bdf90ecff854eba5a04293737b80d4fa2675c00fa6d8c64021948bfe9265ad21fea59efe52a097bd8caa55a1acf4c505c9a3ecf73ad100f7bbb356e8939150adbad10c78a43e2c3dff04ebaea4d8222da052aac7130aee1b5fd04377232c699bf668125", @ANYRES32=0x0, @ANYRES32=r2], 0x70, 0x800)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x6b25}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000080)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x80000000000029, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f00000004c0)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/205, 0xcd}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
sendmsg$unix(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0}, 0x0)
madvise(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
mlock(&(0x7f0000ffd000/0x2000)=nil, 0x2000)
munlock(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80185760, &(0x7f0000000000))
ioctl$TIOCFLUSH(0xffffffffffffffff, 0xc0106924, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file1\x00', 0x600c, 0x100)
ktrace(&(0x7f0000000040)='./file1\x00', 0x0, 0x0, 0x0)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x5}, 0x2, &(0x7f0000000040)="f148f1d9", &(0x7f0000000100)=0x4, &(0x7f0000000180)="131e3890", 0x4)
shmctl$IPC_SET(0x0, 0x1, 0xfffffffffffffffe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x7}, {0x45}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x30}, {0x1}, {0x6, 0x0, 0x0, 0xb9f5}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r1 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSTOP(r1, 0x2000746f)
ioctl$TIOCSTART(r0, 0x2000746e)
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000010c0), 0x0, 0x0)
dup2(r0, r1)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x4})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
r0 = semget$private(0x0, 0x4, 0x426)
semop(r0, &(0x7f0000000080)=[{0x3, 0x0, 0x800}], 0x1)
setuid(0xee01)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000))
openat$pci(0xffffffffffffff9c, &(0x7f0000000480), 0x8000, 0x0)
socket$unix(0x1, 0x2, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0x6, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x3, 0x0, 0x0, 0xfffffffd}, {0x83, 0x0, 0x0, 0xffff}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x40}, {0x48}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000200)=ANY=[])
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1b}, 0x4, &(0x7f0000000200), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x7c}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
mknod(&(0x7f00000000c0)='./bus\x00', 0x3a0914c44f7b202c, 0xd02)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000380)={0x0, 0x0, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0xff}, {0x87}, {0x6, 0x0, 0x0, 0x2000000}]})
pwrite(r0, &(0x7f0000000400)="b97ec44c8196fdf602000000064c", 0xe, 0x0)
r0 = socket(0x18, 0x1, 0x0)
getsockname(r0, 0x0, &(0x7f0000000000)=0x1)
syz_emit_ethernet(0x32, &(0x7f0000000040)={@local, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @local, "ccae9117910adc8e98ad3803a2b4ca10"}}}})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdir(&(0x7f0000000140)='./file1\x00', 0x0)
r0 = kqueue()
r1 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000240)=[{{r1}, 0xffffffffffffffff, 0x1}], 0x9, 0x0, 0x0, 0x0)
rename(&(0x7f0000000080)='./file1\x00', &(0x7f00000000c0)='./file0\x00')
kevent(r0, 0x0, 0x0, 0x0, 0x2, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x3d}, {0xc}, {0x6, 0x0, 0x0, 0xb9d5}]})
write(r0, &(0x7f00000001c0)="3c0200dc10dbf8a7a126936da3fa", 0xe)
sysctl$vm(&(0x7f0000000040)={0x2, 0x9}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000000)={@broadcast, @random="17e418476e14", [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @empty, {[@timestamp={0x44, 0xc, 0x81, 0x0, 0xf, [{[@loopback]}]}]}}, @udp={{0x3, 0x2, 0x8}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [{0x0, 0x0, 0x10000}]}}})
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xffffff80, 0xffffdf7f, "f163b56028000001ed0804000000284600"})
write(r0, &(0x7f0000000340)="5ee7a519ae5ade351046aa2da4c801a4f9b953911541efac34ea0834de1f79254e1f21caf11aa2f8820924e1daef65362c1d540f1eaa71bcaaab85d67513f2a4680915d5c306cf03f0a34fd86320520000008000000000a298a6b5bbefddd2a5f1b49b9bcc70b65528cf3d9d12e7b4acb8d7dec8446ac5763164346f933bd835d80a5b00dc", 0x85)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x1300, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x2, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000000))
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x3, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000501000000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f37633697f00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602002d7d026ba8af63ff37281002e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020208a371a3f8170400000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
recvmsg(0xffffffffffffffff, 0xffffffffffffffff, 0x0)
select(0x40, &(0x7f0000000000)={0x2}, 0x0, 0x0, &(0x7f00000000c0)={0x0, 0x7})
shmctl$SHM_UNLOCK(0x0, 0x4)
sysctl$kern(&(0x7f0000000080)={0x1, 0x2c}, 0x4000000000000007, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
kqueue()
r1 = syz_open_pts()
flock(r1, 0x2)
fcntl$lock(r1, 0x7, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x2000300010000})
fcntl$dupfd(r0, 0x0, r1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8, 0x1, 0x897, {[0x0, 0x1, 0x0, 0xffffffffffff8000, 0xfffffffffffffffe, 0x0, 0x0, 0x13, 0x7, 0x7fff, 0x401, 0x0, 0x2000000000000, 0x2, 0x0, 0x2, 0x0, 0x20084], [0xe2f, 0x20, 0x1, 0x3, 0x4, 0x7, 0x6, 0x0, 0x4], [0x0, 0x3, 0xffffffffffffffff, 0x0, 0x0, 0x3, 0x6], [0x0, 0x0, 0x0, 0x3, 0x6, 0x1], [{0x400, 0x0, 0x0, 0x4}, {0x0, 0x2}, {0x0, 0x0, 0xfffffffc, 0x100000000789}, {0xfff8, 0x1, 0x6f, 0xdae}, {0x0, 0x0, 0x4}, {0x8001, 0x20, 0x7, 0x6}, {0x0, 0x80000200}, {0x0, 0x0, 0xfffffffd, 0xfffffffffff}], {0x1, 0x1003, 0x0, 0x3}, {0x6, 0x80000004, 0xfffffffd}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
socket(0x18, 0x2, 0x0)
r0 = socket(0x18, 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000100)={0x0, &(0x7f0000000500)})
r1 = socket(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300))
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
ioctl$BIOCSETF(r2, 0x80104267, 0x0)
r3 = socket$inet6(0x18, 0x3, 0x1)
r4 = socket(0x1, 0x1, 0x1)
getsockopt(r3, 0x29, 0x66, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0))
chown(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, r4)
r5 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x8020691f, &(0x7f0000000300)=0x2000000)
getpeername(0xffffffffffffffff, &(0x7f00000000c0)=@in6, &(0x7f0000000080)=0xc)
socketpair(0x18, 0x3, 0x8000, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000504600000000000080007", 0xd, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000503", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r0, r2)
r3 = dup2(r2, r1)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
getpeername$unix(r0, 0x0, &(0x7f00000000c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$VNDIOCGET(r0, 0xc0084428, &(0x7f0000000840)={'./file1\x00'})
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
rename(&(0x7f0000000000)='./file0\x00', 0x0)
syz_emit_ethernet(0x35, &(0x7f00000000c0)={@broadcast, @empty, [], {@arp={0x806, @generic={0x1, 0x80f3, 0x6, 0x3, 0x1, @empty, "8a4367", @remote, "f030d1aaeafd6db31a8f8d346a3291fd"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x84, 0x5, 0x0, 0xff000000}, {0x6c}, {0x6, 0x0, 0x0, 0xfffffffa}]})
write(r0, &(0x7f0000000140)="7c0000fff7fbff00000000000000", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x74}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x5e, &(0x7f0000000000)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "17f37a", 0x28, 0x0, 0x0, @rand_addr="fe83c158edabae23ebfe9514b180b761", @ipv4={'\x00', '\xff\xff', @loopback}, {[], @icmpv6=@ndisc_redir={0x89, 0x0, 0x0, '\x00', @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}}}})
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x1e, 0x2, 0x1}, 0x4, 0x0, 0x0, 0x0, 0xffffff9e)
r0 = socket$inet6(0x18, 0x3, 0x0)
connect$inet6(r0, &(0x7f0000000000)={0x18, 0x0}, 0xc)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000100)={0x0, 0xe47, 0x3f, 0x0, "7af6886090c361b18458b1af9883c25a99e92c5f"})
sendmsg$unix(0xffffffffffffffff, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000180)=ANY=[], 0x28}, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={0x0}, 0x10, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x8020560a, &(0x7f0000000100)={0x2, 0x0, {[0x7fffffffffffffff, 0xffffffffffff4ee6]}})
sysctl$net_inet6_ip6(&(0x7f0000000180)={0x4, 0x18, 0x29, 0x36}, 0x4, &(0x7f00000001c0)="002da6b81e6b540db63b8da3d4c29d37", &(0x7f0000000140)=0x10, &(0x7f0000001180), 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = openat$pci(0xffffffffffffff9c, &(0x7f0000000100), 0x10, 0x0)
dup2(r0, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x87}, {0x2}, {0x6}]})
syz_emit_ethernet(0x3e, &(0x7f00000001c0)={@random="36c840028ea6", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "879f98", 0x8, 0x0, 0x0, @mcast1, @local={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x2, 0x3, 0x8}}}}}}})
pipe(&(0x7f0000000340)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
readv(r0, &(0x7f0000000080)=[{&(0x7f0000000140)=""/120, 0x78}], 0x1)
r2 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
write(r2, &(0x7f00000001c0)="58d72fe6f06de80953651931cca7ba0715960b012b56869f9b93728bbfc354d49f2bfc6bbc84d8d585551b57304f7a703ea8a4e6f0233e7d74a2a50d4604bb2490ed7c15a237cd0f76c17faed39de2fb170d3ef4417856f903c5aa222981667bf83611b5e9fff77710ff61f9ca384f820e5caabbfc6063b0c969bbaf270a4437c8d4ca73dfa7ef3918", 0x89)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
write(r2, &(0x7f0000000000)='*', 0x1)
execve(0x0, 0x0, 0x0)
write(r1, &(0x7f0000000480)='p', 0x1)
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = kqueue()
writev(r0, &(0x7f0000000640)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000)
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x2, 0x29}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x4}})
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080))
sysctl$net_inet_carp(&(0x7f0000000000)={0x4, 0x2, 0x70, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x2c}, {0x64}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000140)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x24}, {0x81}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f00000001c0)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "22b812", 0x8, 0x0, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @multicast2}, {[], @udp={{0x3, 0x3, 0x8}}}}}}})
semctl$SETALL(0x0, 0x0, 0x9, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f42", 0x800}], 0x1, &(0x7f0000001400)=ANY=[@ANYBLOB], 0x70}, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x3}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @multicast2, @random="16dea8ca01ba", @loopback}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0}], 0x1, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x24}], 0x1, 0x0)
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000340)="0118fff60f9efcffff010400009f666e9f97069815ca5835001ab43afd5604c4aa10930ed14b10abb7d8414191ac6193bb0991fe8a3722e3258bc29c66755d45d5ae11ce731aede7734421cef62cac7d5ecb3a69b2e7910599897b40c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebb2481ea2de4000000aa1e20a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66c878f486f7e59a59a05bb689915b90980246fa85c22ad066d2bee08f7394000000000000000d4c45356c7ba884245d73f252d7496bd0100fbff1f1c3f30e790e6157cd0f6ac73547476b2a766825175bdbd6780e66be6d740a1ad902a5f0004a013a1dc24244ade0d510672010000000000000000000000000000000000000009eb3d3881885647e6b93fcbadf25485d5ca4287ed75b0db89c123fce0cbff668a58f19f470bd87e5503c72b7d1b3db57458e55df35b8e1f2e111835a6b788d5ff5256df13b563f269e55e741205360c9d2e43575809838bebf4e71b1393f400c7c32ed7a1f4dfedd53dc24ceb12d50d3fb41b2732e741d0ea739f0ceb63553689a46145a2805341c0d29de081568214f857ebd1f1e41bfb9a21624840a96d9619e00feb108d5bb60a27d465014bd7742b7e5f4a46cb83eea6b48aeb20db0242eb2d2abfec6dc0e3b0450200b24c238f90402598ad961fbf7502767ebb569f49ec0000000000009948c6d84335184a2330a5517b890000", &(0x7f0000000040)=0x210, 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x40000400000002c1, 0x0)
setgroups(0x0, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x410, 0x0)
setgroups(0x0, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x6, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x200400)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x800000000005, 0xffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000]}, 0xe5)
close(r0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000003c0)={<r0=>0xffffffffffffffff})
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f0000000040)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000180), &(0x7f0000000340)=0xc)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240)=0x2)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x5}, {0x3, 0x0, 0x0, 0x2101}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x59}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f00000003c0)={0x0}, 0xa2c4a4fbccc5c5aa, 0x0)
r2 = socket(0x18, 0x3, 0x0)
dup2(r2, r1)
recvmsg(r0, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x0, 0x408})
mknodat(0xffffffffffffffff, 0x0, 0x0, 0xffffffff)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000400)=[{0x0}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504448, &(0x7f0000000000))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
writev(r1, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
r2 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0xc00000)=nil, 0xc00000, 0x0, 0x10, r2, 0x0)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1d}, 0x4, &(0x7f0000000100), 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
r1 = dup(r0)
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000040)={0x0, 0x5, 0x8006007, 0xdfffffaf, "c09b4f524909040000000004533fc8a800"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809", 0x39}], 0x1)
getgroups(0x7, &(0x7f0000000200)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
setgroups(0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
chmod(&(0x7f0000000080)='./file0/file1\x00', 0x102)
setuid(0xee01)
rename(&(0x7f00000002c0)='./file0/file1\x00', &(0x7f0000000100)='./file0/file0\x00')
ioctl$PCIOCWRITE(0xffffffffffffffff, 0xc0107003, &(0x7f0000000040)={{}, 0x0, 0x4080000, 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x37}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0x7}, 0x2, &(0x7f0000000040)="77ba7e7d", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x0)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000100)=[{&(0x7f0000000180)='9', 0x1}], 0x10000000000000ed)
r1 = dup2(r0, r0)
poll(&(0x7f0000000080)=[{r1, 0x5b81e13c2993ad44}], 0x1, 0x0)
writev(r1, &(0x7f0000000100), 0x1000000000000161)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x2, &(0x7f00000000c0)=[{0x80}, {0x16}]})
syz_emit_ethernet(0x36, &(0x7f0000000140)={@random="1b329ff120e0", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
seteuid(0xffffffffffffffff)
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000400000/0xc00000)=nil, 0xc00000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0x1}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000100)={@random="c7d9d46191ec", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @local={0xac, 0x14, 0x0}}, @icmp=@timestamp_reply}}}})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000001700)=[{0x0}, {&(0x7f0000000640)="9e8df56282b6e62b09db3fa81faed2a7ce94db2f3ae31f8205da7948710634bdd2af27c1c59eb9a9c4d00fbbfdfc818bf85687ffc4840ac3663a3f6836c85e6c811bc81b9512e91855f99d2b272aae6818d57193b1ad96116a379643f2d25797a4336c6dd4ec3d2c4bea374feba3f0734ab61e124cf10a1621171853191712041ecb4e54e0b5a342ec12216c5840afcad763cf74549fd8962790aa1309c0cc1bfc58a0e9524676bb3a669a660445047be080039515c7d82d3c94472656eb80edda2db0826377bce3c9ca19b5437941b689d3c6c00b5c1294b6d89afacb07fe747f620f31a50b672b", 0xe8}, {0x0}, {0x0}, {0x0}], 0x5, 0x2)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0x81}, {0x48}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f0000000140)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @local={0xac, 0x14, 0x0}}, @udp={{0x2, 0x3, 0x8}}}}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x2}, 0x8)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r2, &(0x7f0000000180)="ab1fe080a6b1e651a393849717fac8983f744159fd80988a69504618da8bf881d8630f3f208bc01f58e8005fabe1c64a0591c8f24f47e5bc02be3983e941f0474d8e48c14d0a50e4f996308d01b4a40029e0eac78751b33da1ffce45c33aa43845780e798086d21355bf7c432bda7e339008ed7b578f3d4b95e09bd8161a5c63a52f44a88f19366cbd87d1a26a74a18652118d087dba76a880c25d85dc1b5c5167d33c7332ef9483784666628f40831c2322b2873e6d2aeef864349f4cfd7d02668960360d9a1b06223f53416a347e82a8bd1b20ac7d7696c07a193722af7ac058102a38f44203c8be5c4a6430ad99fbf6bc5b68f057eba9aa017183af47714165c57ff7048079c371d2a772719ca63edcca6b8ba0cd66022bf92743e47b2a95c9601eb99268dd276cb85150cd38002cbf6146577b80beec3dfffddcd0774770631a1c6ea32a25463f059d81f95482d7b0bbef99093d6e78d0c2f3a8372bc66618dcffb528ed5320e3196aa025bc2d94eb", 0x171)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x7}, {0x5}, {0x4000006, 0x0, 0x0, 0x10}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc444443a, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x5}, {0x50}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000000140)=[{0x0}, {&(0x7f0000000000)=""/42, 0x2a}], 0x2, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, &(0x7f0000000000)={0x3, 0x0, {[], [0x0, 0x0, 0x0, 0xfffffffffffffffe]}})
r0 = syz_open_pts()
ioctl$TIOCGTSTAMP(r0, 0x4010745b, &(0x7f0000000040))
sysctl$kern(&(0x7f0000000000)={0x1, 0x4e}, 0x5, 0x0, 0x0, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x6000, 0x0)
chown(&(0x7f00000000c0)='./file0\x00', 0x0, 0xffffffffffffffff)
sysctl$net_inet_ip(&(0x7f0000000080)={0x4, 0x2, 0x0, 0x10}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000100)={0x1})
socket$inet(0x2, 0x0, 0x0)
dup(0xffffffffffffffff)
ioctl$TIOCFLUSH(0xffffffffffffffff, 0xc0106924, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000100)=[{0x25}, {0x7c}, {0x6, 0x0, 0x0, 0xfffffff7}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd00)
open(&(0x7f0000000000)='./bus\x00', 0x2, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0xffffffff, 0x2948, 0xffffffad, "9190000100120000000092ff0000ebffffff00"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108827aaa15ae924deb15714f5c484d9b55ff7e5df9a0c17cee59911b599c2e3b8acc3a3e36a5091afcfa95707af2b67650893bce2350cdfa60247075c6cc22214d8f961c224c733041f3e9d10fee58bd07f45c93ccff75967a236f6850f237175791eb18d0c84ab3ff9265a4f906c35d7946e735070758830fba1dec1efc1a0b0b7f7a949575a1ae7ad6671b72cbaef9e9fb9bfb0193fb30b6f5c4bd2f47613418fe4723149bdf4decf86d9771b628691abe9ba9dd167c938dc659e12ab50ba7a4613c0f9c5aa599ff287ee4f51e0fa1a5ad5774ca6e3817e82849e420678b5073fa8b106eae2443334e746e429b03d2e2ff63358fba08ccd461cbf753d1eff085a747907b140fd46ff6e72d46fefbc1ce41fa94bd08925b0ede3cc73b2aa71bce9274254ecbae48ee83bfa58495a36239b28e939c584566f0a61bf9a6c8bfdd6d017f6a0f7517d13706af1edb38d7d0ad6d0d606e95edb0b32154507a873b7935386d63d46dc45eb4967ecfeef3040b2ffd4cab9dea41ce5018860a93e7de09ea3e92d6d38c0ef7c3a6ba65338c70dd637ab8e432746f64c6d817165c08a86ccd38af8a98ced5fe585f9de785889f781d12685b40908bf4a357009397e2cc8a70950e2606f556a71da016be1134f6f26898de79e2dff22bd233e6525b7b891c8aa27cc07f56f68754d9cef34f121f17dc7051166bc4b593d426f53f24ff12c508a1d53cbf3afccd362daa16fdbbda000c5533e290c8325af24b7a8b26eddab493baab8fb648696d7721e2bdc3c9f2c173673215bb946d6b35cadf2821acb9bf8e2fb8d39fa112e5b0d97e75e753a4906dfa3528e0aa66201d8a4715696f29e0522718230e01e2d8c52470b198c7229178b496660496eab3b845b1d76c1c2f8ed8693c8251a6043ec9e58901fc495d2f93250cb527a0866f1b331c803255adbe6df996db31a6a5b057afa92704b5c6af6029cb7b8a0d655021c8c7eddabd9ff60a082a38248a438b8608fcf9995a3f45d057cb152e5e7965623a8c34ba2c3f0172e22d73ee848d6c02394f581d09747318269d7d547a87ac7c1997fe3ab0473816249466b14b12a28b41bd66f54d692d84a34ab3523cce8993900ed5cc08637f624864d03c3662328370306fa9368f8062eb2e75fdee605196c10503254f1966bcfe78a1c4e0d8a1baaa3821c4247f9bbef264488da76ee7477bd1d146d7442937b48bb13cf07ed61705b88d146430f7224cbcd52ba15df0b701ecabdb3fe3f123eddae632b2ded83678eb366c0f7e684067fdd8db960df5e5e4de2fc9bc27cb3112901c2b3e71be75cb52f7b809255b2b7a5a05701e5658d1abff23ae402ffce8ce8da0e94e6c1ae69f98b5e0dec9cef356b3c822655993a40d138fe178642a84632d9fa23c553c0c1c9fc0c12b0aa37c2ac760e85ee853451a50ac61c67641abc7b1364c2979df0b19011d87d929644a7e80d4f63d40b5fa8cd55549c2e4d9c392ca51a8a8e3eb29aa4e2009c2facb1740c247af2f62346569c28bea33531a22deabaeccf376b52b64b455164466f6d70d14e4667973acc7b0e95d21d7ea873c942e24838974a414f292ce8c20d8507e6b971b7a6f4a71219190df7bee19c4b1cddb731d940ed5129abfb075ce015659560295799f1e14a315a10de225cf0d98f93a9c79ed7bbdce6359c6836d4d94c6663b70c7925e8f48bce0bb67e2ea7883439d680752f61c7d415c98ebd1d1a7b07f359237d3022560977704d2106ca2382fd8046f27a422792d0e0fd93ea8f74f9119543f28ab716f299dd1166eea779c1306f8068d0f3f03ee4155622775d66b77433b43b2799aa8d307dbdb27cf336c75b7a45f3ac9c6bef6856eb08908a9318fa8f8b0220e4625b6de094d49c46dae45b4b0cb3d9c656eb5c1e0955ea66c40bf5b002853a0f2cec363ef055d7d7d1a6c73c530781cffee8417dfab8a056e2b874780605112083423c75ed6f0a03a99e23bd452dca1cde151013a160a6e7bafe043d9627de6a596189ee4ff8e35bd54d1f8c6755140d9bedc36653162c08dfffd41c743e7d5c2389f34a09ec89784495118786145687694014af52097d90cc43f19c8096443ae1e6aed38d8cec2f5958a64674516ac389d4bcde3145078ac093508b39b7c1c8907575dd8cc000ad90adfad4592b1e6213dc22156db3bca15f1f61976165de6e0eb0312ef9591235ea4f9172d8ab0cc5f3a4088e7baa4f6c38a203eda0ab8ce6dd3d18667e714b38c0c5e2e4a35065e1b63c4769f3457ec409775a59c0fb391e27fbe6123a60ba05234f1f6a829a133ac0cd0e0eac92fc2c8fb9a406a02702d168bc3d960a18364c800f5d9f029443c3cb28344d020d1bbb87a5b29ea04b5100736a4ddec6d692590595b8e0fccd83212094b6db42966840ccb68b55e40e9fe0889a839583c1ceafd222d21c551b88ceab5ca6ec1c1761815f0e06d93e7abb6890824e1c6e33a5b8ad9a0b3365d18395549dd1a6206d9241b4b971fdd06632a4f4d3f59b008e35d11a644d182a1163aebec611ee1461995d040455074d7f1402fde1e7e8838c5b44d598e5f20c80d796422489bfdc06639d3ddd98b4f008cd52cf7b2455af726462ef36225ae01aedb479be2e15fa58f0c031d25b36e73e178ce4500968c0e1ece89549e6dac2a1ef394d0c014c34a1ce10bc9772d395aa33cdb7f13abbc1198632f3db23f9fa4cc36eb0d113289eaa92c9558a9ed8912388b62f3918c921ae43e6afd2f0cf528a11a7fbcf884724b14d56550b55bb6636dcb01d56d3a7312a6e39f783e2ee5d65ce8ec8ca087e4e2418a15856aae821663c1c10d7c2844b161b1b2f82cb5bf6f897998db095ff7d151b50b6b1d7915b444bdcf6de7c21af13b9678ed445e952f473ce92b84a48a12c9715c0ee70b156a77d9738b2001bca9dd9ede2159bdb1ba3f68d43128139f1f12d2798d735769840bbc98ce108073cb835bce5d4141d065adc57378c5955aab70d35063fc16c02b1e37e29ff9baad60af6916e3cb6a461f5903b590665771c8d834c07f6e14830910ffde97e3b0a9556d7b8605e1a4b8ea2603771c08fb6560075ced4f7c8d4958723a0abc9378199e9d3539f486f3238f7153ee276a31c1e90625d6dfa3cc1ba9b983993d1d1b2b7eeb660957465d85589619d7d1b65803a2d46edf39b9a83156047373b066012968470a8a0a5e5bd7c653a07444524d49156d1a2c0e2217b9758090b03cb8f6cc60f6e20f66f92c46ede756c71adb9ceb80764862e1a2a39e7e2d4fc69165dce1a7fdc7948a2fac5dcef6618e3df9e2c233c1f28ebf8eeca55d71c870f912eef6d97b9cab1940a9b582be610575e873874def9ea7667d1f483490fc13b2d1759e3e08817994357fb8d7650a2344beda00c7f580a95c333f41386089e1d40fee90367c268e4fdf8edd918374222732e070108e4159609031a545bf52fb296263f38ca7355050742a8ef734bad5ed9ef21ea9aba0535fa8f46605084098efbdf740e0b88f79744c4ff6399c4d545a51e1664860f730818580b829957299eb7d72699c0603ac9278584dfd8df08085e3f0ebd395dc6233f8e86e0f51b59305d1e4672d53d7185b1b173f5a8fff4da9c12b41e1365b32c4c2b08fb3b8e82c35554eae926415c0a00f8879fb661adc661849bf2331124cfe38b1c3c090b86f0b663e9324e574eaba744e5b2ed609a37a5909bf488e8607c992555df9408ecfec380365c3e4688becc4124f8f71792898565c8867af658f61a04be92fb0d89544457b83dd65d4a10219e5fa81a035ba09e7ae0ea209f5b301a64813e94d1b4232a7a6acf46c5d9f9ba16196530a32cac0bfbc0f6460f8994c673455ad2f1759cea74b53ce12c1841ecaee13c008bdd1ff462ebf1914780fe788d05807397be4370afb36d4c4e6fc12263d6ade2c0d8ea2e7e3d371f9f353291fc2b1bf3798ad13fdfd8e8809ced1e9271c78df633fdcfd3793ce6e522ab6bb2737c0f8de293d6bc4eda92d4840b37822480826cd90bae38d58f18301434f7a4940df7e6fdacb426842b2062dbb3a9813bb78166104fcc158af54013e11830cd22dc3dcbf87465c70256ab52a9eed01f5d843444f08a9aa882384db0b46492e8f63917cdf370a8d743703852f44efbf0dfd1ae0913f168f2608ae45d0f49330eebf426c49102ecdf7de87893fb40ff4ab5bcbd14a52902ba62ff49c9314c31cbbe995274f1ee49b355dfa18e5d98ccfcb200bbab7535b17a9a812484ebc1eb75e0c26744ae991e0edd6653c3fa3c7ef766562db7b5ba217187b0cc5151d478d43cc8329302837f105da707ba74944c753aeb9e57a0d854b061ca63a295c2d79dd8e21801ffc2e99f4777386fecc9e030a1e715c4532838f4d2b35092579c238f8da719e98667c3756f7f4f786aed5c8d2973d6c6eef60ae33531547e390ef1693c820c9cbf40acea6bfdda8d8566d9e24118853db0c300887b107a0115c62b0b1858cfc2d7c3dfef837b6e8f1cce758b7095ff797bc9e23d7345a34987c432870d1beaa98004b7ccdba2d94b9baaf077e4aee1976ed8a87d6b7261610477ea24a09550b60193fc82f0bf2d8041f00882f0a7906a078b63bf7bec5aa7d753b2d1ab54fbc934d97a1922c86781df0bf389c588dc7a904f2798efc796e32f2a56e378a4eaff6be8d49bec46f7822b4b9e9eeda1f8e5cfbe8f6ad143ed0744096abfca95be8fdd3db2d965aebb9fcad7af768f14e293df1fc75c8b49088fe0eb426d46325aad82a698e8a83fffaef218be87663b99eab2708631b2a68edebdad7f58a845774041b035eabc2dc1e16b57917330ba78409a8f24161742d30d28f1294d5e409667d1878f5f630de4fe1c4d585f9baab598de85fcb56eabab72c414630989687ced262ff0666893b88f0b6279d38e9e71fae95c2a32c22bb6ca53dc781354226ada0bc4afaad416d51b4fe48737b8d4ea51309c3c7ecb3e6fd1e4e4d8fecc087593eab052b73fa44f5ee6d34492b3495e0fca9dcbea94171566cb020adf7dfc5d7d7d7d2ea571686b82342e650ca025c990287ea3378b260028de6d174d284016fac41971d54a82c24aa000f61cb0764133c58eb85d6a9a239761a65d9048f659813cfd1e94c2ef1d02cc6fd2b8c363fa0057a4281099649141737afceb5fa8deac6e3180e3e8ba50f372fa12ba3446ad1818a660a7a52fc067b442f57a5558bc7803842e7b9d5fb2cfd70931db246414f9801a7c5202c3903c8a3fcce606a5843c0091fb6c24be14166010919c7d603f5be3311325a7bd11e19e0cdbb6558b3b314b86368630aca94c312dcec92be9601bad1c5d1abb5e17ead17aff9f9078a7b215107bcfea39cd8c905a705984bb81e16dccfdc5fce2edc713196a51ae9de2ee14726aa79d7d52b111bb89ef8e5f83bc5625fa9a27e2d97ffa1e430591f0ebcb24150193f6b8b65228d6e007e4c8ba1e7637c8edc1a52aa9ae52e2a171bc4f760d4f97eb15c21c0293552023291560e9d054960e5c59d24c3d13534b5dae6365f92c40046790f6cc9d0209ea8d488e9e5afd9c5237c5b1a50cddb9ed5ff2d67a45d6de5806df0ad62da2c288fa04c3ee3f386a9b2b462e81b487e43d7dd6d82f6920b1f0d2ac2f71253828c3fa2b7ee11f8643d7407073a70a4aba361cfd14b926de07c18cc418bd99a506a7fc50446b06c4a093291a02ab83cb777d90163c66e789c55829a7f96f787e277c9", 0x106b}, {&(0x7f0000000680)="650cf8b4f53c9748f37ec9b87dae0a9c4a5dfa3ab2976780d8f11979028f32491f262925548c73311909046ac86d3be02a38cd1b3df6939d1e4b0711c291bdf4379ae7d492f700c70ddcbc5b07fac5dd913a9c23d5a7b2d09b24131a33c6bb018a05c343839da8b3a4601763811c5da6b95bc8191036409b1f1a8b8115e335669a561f9398cfce8e01098dc3a3eb52cc34437b68d99cede11e9398f80f5c0dea0fcfe19547155fbea7c421c576093dcd2668a9b46f1d5bc005a6c94ded8eef89200f4118ce2e595eacfcc0a13d92a3c023b28e704ae34ae1f7682de66ff0a0776c7dc166aa21ff371174e8bd4ac63db3a77c9c529a44f322a944352a7ccd2d6e50a1061a602fa7a6391269bf90e28da6079f82121acf4026ae8c92998a66fa398d1a89c037dd239dc079f693bb8499c992343d60d422416dbe03d0e783d7abc6863a70fdbd699ad708f0cc9177cc0c0ff03d152b03e99c5553d45df59defb39ad8bd370db6f25af812eabd14c2f8e7e73b8ece663f51fd130ce821218337dea988fac1c92229fe169e15d9be93483eb137b96fff6154342742b41e45270b4e5c96c9068e44c52eab77204e69bde008c2cb8d7df945410f664823e673cc3308e169fe32af5b75b5a67824bca61cc9edbdb7fd8073ae7449423ddbdd08c7e70c7a964ec4a928e8db2fd9322d8f4618921fa4037d634adb7b1f46ce262a0e6405a83e0c9e2ac19bc33fad4b2b98a60ba947c1fa3beb498a46c26a31733db90e94e9c8c7c9b7651c01d17cbd2a98e2ea4cc0d6b1dd5af56c93fe84f6d88958bcca13d8fefd0e8073cb16f5a8e97fc149fa2822a08a3dd95b3e990570942a497431721be9f20b679a3290d41c8ddaa4a88bfdf8d2f7e9a41e7332e92c64bfa4f541060e64b322f2b383161d1053e98ebf1708e25515ffc0b6c71d61a6269c4f503c754b1e8d02f6621b4103ff102540ede8df9733eddc35f796cad08a3b813079e193c78ed00096edf4b3c76087678f2ad9c13c311842e59b10d5c103a6d28a51308814d79119427d034a4cca9f80f31ab341bd11b79d4616caf9c3722a3cec5303a1ef8698c578a99923e368939d344c312c10e8c10e471c5bc8e67799192796", 0x31e}], 0x2)
writev(r0, &(0x7f00000004c0)=[{&(0x7f0000000d40)="2abace18112030b738b1958303ac3fc1c384e67774d4bd49660d81fb4e2e9a19a972ea508fd17e3d0a932587a0980ddae58d2709a47c4986c8e74ed0d85edc5aca224a44bf11e4e1a51c0f08aab643577c975bf1928b38f6a67e0c5846b4128483e6275d201fec16e9147865784c167c704c6ae0980bb84e8dd18d6f4c83abaeed4ce23fa84033c78e9ce083ed4b575716841fd249fdb120f15b3db394bd37d3ecdf5440737d32d21498fa88ed351c1a2c8b9fe3a7773a6c3c322eec1e8da0864d79d9ca12a609b61fe0f6f8b88324f93cd6067a05d71c69804f02315e320887de9e7503e2645f1d9a453a0f770a109347ba8e025f6654809908b3d640309334db564b52daafcfe22cde8ad112ca5fe8d81a335656c43555aed4ef5af976b8a30d39838eb9162e58f82c048091b6f2a5c792c222520cba0fb1a3adab11c5adf10000000000000000000000000000e9bf0b9b675be513096c198ac5303b35c3a060a331662bf508cc3b6eacf17750ebe4976e7239251b8559a57039a7ad3a3063257fce807eb37d29aa2654046c62e343be2dff20fa332dc701ac84f3191977476cbcfc2bcc335332a1f8ea95fa9f440be83a78ebdd60dd81fbbf6fa326d6d84c9a12c84d037541b12aa9a2faccae86f50372ece9d191af64db4ace9044ebc45d64e98a20133bb56a07434b3403ad089f99c949cd0efbff2e830cbb5146c58a27f83432998008556069bc469cba6d83bcb12f1f65365ec1ea5e25a6258f284da980074edc59dbc279cba0c634a2ab848edc44ff4b26b6f61b58ef1264e7d58d6cbe1b7fc6c914d69ca31a84b6eeed94229657d44bfb4e2e4b9797ef7f6af863f1c1c7f7ae9a45995936baf9d11f3bb2a5eed1ca573fb9df42aa20fc7eb1832c2cec4d048cfe862cc03524b4a0efb020cc7051e4d1efee1d972ff84f9fe5730459", 0x2a0}, {&(0x7f0000000080)="051c92fc1af91e51429a1f162acb8d81008b9d3b588cc9073401033d1119b2b141df99877a601a4619f1a708a668199c3cb370", 0x33}, {&(0x7f0000000a80)="d19e767a41270df7d5d1b0a722f30b11174ddcda30009159584700000000020000000000000000000000b1b725cf1a9987daa7ee359b4ed4ee089a8ba58f4b83cf3d36a130685dc3733bc8859b6f9773dc5aa805665aaf4f07e4b549a6e2283e98974df3ac6b4a9e6aaef67cff520d3856b8cdce9a7350b8a09dc632925a7ea03599be2e7c46576a0c93744c06dbe4cde868fbf1ff7acff9db9c851e887c7d55d89ed38786928ec68f93f63419779793484be8435ab36dc70d5c138adc853e51d7de6afe1eb0483b1c0333b1b939c1f5b280cddbcfefd2bec63de9a938b2280a331d33ac809a3ffd4309d4836448a6c194eb2d887aee85c22d2bcb2f4fddc80e12ab8110ba49c4929ab034a87762af0bc3ea2dccad8078d09bc74d2dc7e306afdfeccccffebc3eb7b87867e881084144a193a784254328b9baf8b26773caa5e3444f0ac1143cf442444a48ab53043a3d46", 0x151}, {&(0x7f0000000240)="04ba5055b2095b4fbfd8cd9b516a28f8c07afa13c4a713343f7144f02b38f2054ea3cd3d71c76e040000ce263900003ccca224abd7f2fcf20f67fa7efc3807212db9a210f79f62407645e16fa995ffd447", 0x51}, {&(0x7f0000001340)="37e9795d552e7fd645b1c8b46d209d6ae2a5de39d5c69227234925c32c602ba5157794dc1dd2ddad84455ee91a95b2f0cf1ed9a20200000000000000c9acfe9c11baf3865149814461b156130a7dc3d86b407414fd61a1ee8dc9076d494132173ea614d4ea3903c0560b7e8f2166eb8fd7c13e117d84cf4ffb4dba38f98304cfd173aed6296b5d71104c693d6100450e6824c3fc19cf9b736eaa3e33ce3b82b81e27b25b92b6f1262ce8bf8ea5c5f8e6edeb595115de33ea233b8d79750fc209c9e7a94db407751f2cbf5f99b5649557393c3de37f1f302baeb0932f1edd688f50b1dc13f962ea86d08361813c11b623d2ed341260965444ed62d151fa76d2d42c03ab7e0d3a88d7392a8179ca0c2db11ba6a340f9fd6a3e282186998fd2a17d552d1ba51f3a627469c6ff66b6c22e31f602abe7f9f6af3b8ae8a71000aabee3c081459e50f7f1638bd8f904e417091b92cb226f0a8a19457fc9df2c8d069a89ce1d794827503533fb212f9e0b8e369732f9cb4ffb94ba3172e475025770cb29d5d0adaf056076ccb963a4097578a598035fd9c3e6f6bc6f0daa870498d491f66f83c0ad6cb28d830bc1586c0df9f66d90279d461b7236c3afb54ce06c1ac76451b859d5912b6a7e4caeb18b905f0eab82c99e9f35bf489791bd2cf0c77a6bca64e9c99a52add50d8b24c17bffa4f797493bd5f87fc4489a2743852ce31aa18eefced130d800", 0x206}, {&(0x7f00000003c0)="6598a6422500d1c653b65188cabc3387098eae6ae3fa3e9cec63a18a4ff13965d6e2568afb2e6809c37305e36ed1844ca45a4a38a969f70e5d59e81b6d795fa68d88d57bbacf2f051498f621ed93fd6847cebb5981d4d249cf596f03771ec4c0c761015de40078c47aca910f06299fa657f2597390652d569b00a73e2d642097fa5b0ce7cb5f28795720b19bc6acb8e0db7ef1f2464b58d6ccff888816eb6c433a60f7e45e308c0e581fde322b30c63a12155ba7c2272247f3d187dcab9ee2715a67000000", 0xc5}, {&(0x7f0000000340)="6ddef5db50e30000000000000000006300000000000000000000000000005c3106ae2ff43427799e48a0adf1128a82480e8447c4b57f58d94e4cbee8d72d568a0c6e8953fb8d5bf5a1809153e331f8ec24266fd88142dde5f570bd6a", 0x5c}, {&(0x7f0000000c00)="0b549c755da165fa2c5b12bbb9a6a7f01c4f715a5010d60e943461faf212", 0x1e}], 0x8)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="0300008d", 0x4)
munmap(&(0x7f0000202000/0x2000)=nil, 0x2000)
r0 = shmget$private(0x0, 0x2000, 0x102, &(0x7f0000200000/0x2000)=nil)
r1 = shmget(0x1, 0x3000, 0x600, &(0x7f0000202000/0x3000)=nil)
shmat(r1, &(0x7f0000ffc000/0x2000)=nil, 0x2000)
r2 = shmget(0x0, 0x4000, 0x232, &(0x7f0000ef4000/0x4000)=nil)
shmat(r2, &(0x7f000047c000/0x4000)=nil, 0x3000)
shmat(r2, &(0x7f0000ffd000/0x2000)=nil, 0x2000)
shmctl$IPC_RMID(r2, 0x0)
shmat(r0, &(0x7f0000203000/0x4000)=nil, 0x3000)
shmget(0x3, 0x2000, 0x80, &(0x7f000047c000/0x2000)=nil)
r3 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r4 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmat(r0, &(0x7f0000202000/0x2000)=nil, 0x1000)
r5 = shmat(r4, &(0x7f0000200000/0x2000)=nil, 0x0)
ioctl$VMM_IOC_WRITEREGS(r3, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000, 0x0, 0x0, 0x0, 0x5], [0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x80000000], [0x0, 0x0, 0xffffffffffffffff], [], [{}, {0x0, 0x0, 0x0, 0x4}, {0x0, 0x0, 0x800000}, {0x0, 0x401, 0x81, 0xfffffffffffff000}, {}, {}, {}, {0x0, 0x5}], {}, {0x1f, 0x1f, 0x4, 0xfffffffffffffffc}}})
shmdt(r5)
shmat(r0, &(0x7f0000ffc000/0x1000)=nil, 0x2000)
shmctl$SHM_LOCK(r1, 0x3)
shmctl$IPC_RMID(r4, 0x0)
shmat(r4, &(0x7f0000201000/0x4000)=nil, 0x3000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x81}, {}, {0x6, 0x0, 0x0, 0x1ff}]})
write(r0, &(0x7f0000001700)="fb2fe7e986d81d7acfccfbcbd4a2", 0xe)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
ioctl$TIOCFLUSH(r0, 0x8020697a, &(0x7f00000001c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x2, &(0x7f0000000040)=[{0xb1}, {0x2006}]})
syz_emit_ethernet(0x2a, &(0x7f0000000140)={@local, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @multicast1}, @udp={{0x1, 0x3, 0x8}}}}}})
semctl$SETALL(0x0, 0x0, 0x9, 0xfffffffffffffffe)
setuid(0xffffffffffffffff)
r0 = getpid()
ktrace(0x0, 0x5, 0x800, r0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5", 0x8f}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r0, &(0x7f0000000340), 0xd4e688a67930cd)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000240)=0xffffffc1)
close(r0)
poll(&(0x7f0000000000)=[{r1, 0x1}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000000), 0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x10000100000000})
setreuid(0x0, 0xee01)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_RMID(r0, 0x0)
r0 = msgget$private(0x0, 0xfffffffffffffffd)
r1 = socket$unix(0x1, 0x2, 0x0)
r2 = socket$inet(0x2, 0x2, 0x0)
r3 = fcntl$dupfd(r1, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc028698b, &(0x7f0000000000))
msgrcv(r0, &(0x7f0000000400)=ANY=[@ANYRESOCT, @ANYRESHEX=r3, @ANYRES64=r1, @ANYRESOCT=r3, @ANYRESOCT, @ANYRESHEX, @ANYRESOCT, @ANYRES32=r0], 0x9a, 0xd6d0c418f59fe7d3, 0x1000)
msgsnd(r0, &(0x7f00000001c0)={0x1, "cbd9167deab35df7192f5dce27cb93eec99cc7d6e5d95ac504ca40baf2d0a190771a8b6b7e8f"}, 0x2e, 0x800)
r4 = msgget$private(0x0, 0x0)
setsockopt$inet_opts(r3, 0x0, 0x1, &(0x7f0000000900)="ff82366d8df4c512730a2ea0dbc56af8b76e1db7d58cf45bde7fa2f319da88123008759fbea671d3a9804da969c2a9bc3e6e4beaa056ed1f12d63dae2da424108de17018a3a1415b2b799ba6e741a61c84e09ff69f5f1e65e312c770662ac86f2d4c7b1f48c1e82d35e8de95933165056329344d68966cbdcb25b892c208849602be8fb449d8b15708934be86a1d47c5288f40cb3b991aa10112fe9776c7fc15d9d835", 0xa3)
msgsnd(r4, &(0x7f0000003140)=ANY=[], 0x0, 0x0)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r5, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0xc0}, {0x7}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@empty, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="cf9fbb8ea1ee", "", @remote, "54e2e9767c5f018a37ee7b48af7d655c"}}}})
munmap(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
setuid(0xffffffffffffffff)
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000)=0x29153251)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x48}, {0x2}, {0x6, 0x0, 0x0, 0x47f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
sysctl$hw(&(0x7f00000006c0), 0x2, &(0x7f0000000700), 0x0, 0x0, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f0000000080)={{}, {0x0, 0xffffffffffffffff}}, 0x4)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000000)={&(0x7f0000001180)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x5, 0x0})
setrlimit(0x8, &(0x7f0000000080)={0x4, 0xb1})
socketpair$unix(0x1, 0x1, 0x0, 0x0)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0, 0x810, r0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x16}, 0x2, &(0x7f00000001c0)='9', &(0x7f0000000140)=0x1, &(0x7f0000000180), 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
syz_open_pts()
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
pipe2(&(0x7f0000000140), 0x0)
r1 = dup2(r0, r0)
ioctl$TIOCFLUSH(r1, 0x800c745b, &(0x7f0000000140)=0x100)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x4}, {0x1c}, {0x6, 0x0, 0x0, 0x40}]})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
setitimer(0x0, &(0x7f00000002c0)={{0xffffffff, 0xd1}}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0xc0}, {0x2c}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1004, &(0x7f0000000100), 0x4)
mknodat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x833eee594ef525ee, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r1 = syz_open_pts()
ioctl$TIOCCONS(r1, 0x80047462, &(0x7f0000000580)=0x9)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, "cf11050038050000000000ea3cc0a48009000800"})
r0 = syz_open_pts()
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, "3cc10f5b1457238512a05a78ef8ecc10f671d8a4"})
write(r0, &(0x7f0000000100)="a85016ab626818414c98d54904675416eb7f3994d9c3e146d68326f79037d686e9c3dd237d2a1e6eef672eefcdac9b1bac04fd3a62aab8333be9944a89e6bc0f5a150697462da8b7051161891b75f64871ed97caa7943937b49e1c30c4af8a1783e23fb03d9dc848313718f51828158176c5c85c9c10e8650e058bf37fae985c3c91ada86c73666c85c2983393b6ca2ef95de381521c6d16ff46f58f5adfb3c75664f102742601b4229304fe5999618a43543bb6188dfdc8b2006f2596f1e56ba2f963aa3b9c0b6d55cb2af15cfa9a8f62c8c78e969db2b9864d4153a325fdfbc64c72041556984db156b36ad50dcec4f55ae6edaeb02d76aa64d6294c70358a8d97a3d4e2f4140d3ddbb80f8b2668c9fc256214e6c226451a2ae3b29e4894979bf1d20a1c4ee7afc83d01f4a80c986ede88b873e18f9f151c4ecb6d0e13d39eb34932a54f9023ac1ca27e0e629bbb9fff4a5a6ea387938376d5e0bce63f2110b3b9a9d263782afc701842ef410a3d9aa281ecd6005e701cdfb5ef910d36b6ff6569dd6c9145aeb51d485fd417fe7ccd5941393e9603f86aab06e797bb9dc5c8c52bec8a82b7c950d95bf8af199570f9121c10e8e5ef1e1e309d5e18e1ccdeed1dc29009742ab9d1162ebce8cb623e14bdd3b50eca257880ab3be7704943b57c0f6498f4f071d940fa87dad79a70541ffda995384cd5d3cfc0c99fcde8880e96cb", 0x201)
execve(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc004445c, &(0x7f0000000080))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x50}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a614815fff271957d3cd40a87052ffef6650d2e29d42adf810d0fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e", &(0x7f0000000680)=0x1b80, 0x0, 0xfffffffffffffe97)
mmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0, 0x810, 0xffffffffffffff9c, 0xda)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000012c0)={<r0=>0xffffffffffffffff})
setsockopt$sock_timeval(r0, 0xffff, 0x1005, &(0x7f0000000000), 0x10)
syz_emit_ethernet(0x25a2, &(0x7f0000001300)={@empty, @local, [{[], {0x8100, 0x7, 0x0, 0x1}}], {@ipv6={0x86dd, {0xd, 0x6, "cf49a6", 0x2568, 0xff, 0x20, @ipv4={'\x00', '\xff\xff', @remote={0xac, 0x14, 0x0}}, @ipv4={'\x00', '\xff\xff', @remote={0xac, 0x14, 0x0}}, {[@fragment={0x50, 0x0, 0x0, 0x0, 0x0, 0x5, 0x65}, @dstopts={0x29, 0x2, '\x00', [@jumbo={0xc2, 0x4, 0x5}, @pad1, @pad1, @padn={0x1, 0x3, [0x0, 0x0, 0x0]}]}, @routing={0x37, 0x4, 0x0, 0x1, 0x0, [@loopback, @mcast1]}, @dstopts={0xf0, 0x0, '\x00', [@jumbo={0xc2, 0x4, 0x1000}]}, @dstopts={0x3, 0x212, '\x00', [@generic={0xfd, 0x91, "7ac4c13a5dfc0e2b1e2ce70ace5bfbc592231c39de816b51dfd4d28dab4fffe7f38f05df48ae8fc1fa8dada746ee43036d8278b81f51287b98b59bdd3fe52ace81e3c6b1a54536ba72187d85c51961e26fde9e7c8262c314c58ce6db9d1aa9788647976ded30503f3f588e11c2f501f1826ff8a856f4d29e2f033a8fa8ba8ce45d20d0b9e7b91688cb2717fa5c02857815"}, @generic={0x2, 0x1000, "24da023e69ba0adc512555490d19e04f81d59b6efd7c9385ca2bfd092bd24b8552efa4ef296e2e4bb774fe31e408ca5ddeaa75ca9efa9dd3f73d1ebbdf3ba66440e6a82558f1ccfa4324dbe6d91eeea84c8981199c1176c081b589c2d5a0db1e65a63fff0bf157ef53e0f92f09adfa9cfcdb69c6e76366f3e9d8f26db726ae656ba9d65ad33b2538d18bd0fff6b1d01b80728f5b6897339bf4ee04e3697030d57781e31205df4a90c7320f0602fabecf9813b7e5467eb29461a6b7096d5dda79db5147f371a32bfd753a2cbdea7feb0ae9c13eb7c621b51e1d07c537f49a75c6cf47a8b2e33dc4de2adf9a759b01479621a7045f5bde0bb672e9b9424c3feee9636cc8e5b4deb2123d606e2f47df1daa426e6d1b1b89d37ee1db908984f7747470f34226290c75b0f60d1b59cb796b9e690460bd2b0f161a8d1f93acda6deab0104e2f91102f73dfe6c786c59eb133a63bdf85a052d3df426a16c969c0d5998ff747e5678553581d4fb4cf5c15a20b253a539440b98e6b6c5dc41fe6dbf681a917e8d392668678941b66d2010a19900eb8bfa29877470d920b72bfaad327d6aa97f6854aa6b059b2bf3f0ed111ff1a5b6c3b1b9768299091bde042175bdb68cf9ea60c163ef3102d5822e015057b89f9a6c25c154e6c69be2192740a3e56d0cbdfbc4fea07c508a9ad23e60de5835c70537defefa127413260a298b9c6c47cdf4cbc84434585ac8d99389b5e8882f96001e0f18eff16c021f0dbdcf1910bf4e236284a0b36c5025ac678497eb9d436b4cdfe05041c2bfef71fefb5714d9d179aa2a3930877b8f3733a5dd97a66f1f6b5d828498c9396a7896cc578e32cd65c58813d1eef5a5daf3167bf60d683a0241a6592b5e2e318d922494cbdfb2e60275d14df43beafe7d923e05bf603eea5545f538cb2728d035a70d23922b23cb17ed0f560e4fa8a444251b6e75998c9c2d02333190f225b1c70da674801e371ec1d7f02ab3a2788bf19e472b70f4bc5567865aa086cf379a4e6f9054a8f973cb4e1f4a27d1c305173a53a49f2926bbc0e71513d6e18b8813a61fc97b207507e1558d3f1378ff5ceebb708656fa89ab4859315f204a2cac2f71a0e33504108bb62fcd5b249d362322b13365c1d98c7707d16dda475e0112cebe320618bb03d192f9a05fb75b0e841e00a28d7b964517395343941a602efe7836e665df3a025b954da5922a10dfd6a45a8e5e9dbf4c0c34a809ec7ee5939782edb900cf848e4cdf715940da7b9dfee58c9cf2f64a588749aff9062749c6ecec50167437408d070d95664ad831ec3a101e3403758fec1637058b9b07b0037ce6986ac677f50196e6252c86b18809575b43abf8e37a516857d1157d297ecf3b5f42c76a8919dea4246ff8f4d7e9bd27af2390160d84c9ab0ccc399b3451594785e05b0ad099c659393b90947c3b9be68daec203ba584184f9871c50e0ccff71f5f7d74e8a78704761c5432bbfe1ae1bed225723013664784445ba2970f5f21ce9be935c8d028d71d4149f59af35fe7d735949043479996c0759fee82eb65520f82136aca70722b15ef7580f6c6e948749410079ff85228a8b3b5dc3913300d142b6799c1c5cd86cbb72db7c611fa8ffe7a88a209557602b803abe29313acd60aec50e8bee5d6f3268c34a133f57d1326c1383bc41cee6ec1678f2f8cf70fb159498a5006b03c2b97bfc8d9f7ce6a5b947d9bed6bc756d4ba927cb75b20fa64ae4ed6830b4c32d477acdb253615569bda8f180dd19e958c9096a5531c00f128d6d4630b3b828a884b2f9db8fa983fa0d9baafbc9706a2652863b6ec2357e614dbd8b554693d89f6046d177470493358b73843ef2bf7f24c5aee81ff3a28f29485a5cc2abcbfaba2fc6639fe20b43fb09574d633275a3ce4e68f928763b2abe02138312c434a9817ac1949902612e1333dc68b78da720b27b0a90567849991ab3f8ea24458e1010e8ba48868250d7662382862c3b75a8c3d04e295d12ca722267ae29be92dba938f325309a65843fbedb6151528a2233649895ac5764836e464979528962f6542a8c39cc0b1d09af479f7022e732bfb70ff76185e90c85008e389700ea8e5b4ab23cbd0278a5e1400e9bc771e06e8b1d5a66e773d3d9ad3842460dcf65b81da6e0665e6fa53d63ac3922ab9159a643a43eef9a3e84d34fbcf0e0a5f66173ce82010c77a2c2105a3ec5bc81598448b0e530e7316c5ea0b5c3ce94ce3aeeb81c9e23492597ecf7ed5fa6c86bfa72c0308595c6fbfffed433649b70f9b24833cc3c0cf779f99da4b1c7013bb0e586dd1c762e7af1ede589df50a0092ba3b731f6276824304dcc1669e7a4f7e6501d258bf288653e7472077842f45fe225d2a94e031aa7f4ed72fc48c8ef95c0f73de31c7505305d1428c92bd950b70ca7fdf98154e4c39fedbbb3d327e59f8a89dc78e828eea5b7978fac500a808a55e9bf039f8823cb7a3cf25a1c2df075e111218fe5cb7642776b2ba9d0beb0bd60d5fddecbea5fb32a218703217e7abe69918bb787049306df415874ae5ec1ddcb50a1c4aa066dbaec27ddf1b9e56648ca54dd2de92bcdaa34432874200089aaa9f98a491bde2cbbb4e3bfcff61f7177865f18089165572cb38ad9bcf6ffed040eacae3ae8d91a58b0c78f98583d62272bf58bc89df29e8332e8cbc3131314bec7062c1aa5bbd6755c7924ea1d14dc138d6aa676b12afd170c50316e9583300f54a8fbe92036125ec8ce667d5b735c4b9d4558485d1c4173912d53be16f6db0318d3bb628c9defd6c4e62689e73f1d3d1fb425a52a33cfef53e655f54cedebee89aecf5a890901a8ee4e19d073b63e0310722534ac636871e4d74fcccc4e95ae9f9fa40d291ece9b8301d0bfe99f8a220eb35010022072e56f81961a7177c7e400e2f4821bb180ccc2da5e6223f5fe31f0b293517bba0dcf40c5245302481dfd49a4d32868ab688aacace919c42eac511c9a6805666645042f88cd5cf848558f36fc1725af81cba59c68c83d69202298a55842f1df27c8b49745aacf45592e082d8d694a5a9f0f0635f5091601e8dfa6184b5edcffe69307d1ab19eabddba9aa5e7c12c304e47422bef9581ece4c5babf9c7693c6ec60d1e4ec792a9e1f6f4460de57924846a03cb6d2ec2058528328be8afb0d7d8bb18e1b4ba7005da874848e0d6f62bcc9258003d200ccfecf1d5c23e96d45b4d116e302d6055922368161038e32b7337b70b6d5f67d39885abd45695cd72c0723e4f1bbfbbcafc994a4696c9fcace164f6f402c31b3569326707427ddd5becaafa5748645276be7ca480442bbfb622e9af7587fc332f4080f2bdff1e6b34c8f3eefddfe5d856c9c874dc0d271a6923ffb6197f35b69bfedb62f6cf20c2a39e2a3298aaf6f4dd668c3b0dede9471cfcf83ce2a4ec654764b4bb91e86cfaab4276284933595e7939268b8389af92946abb1ca257fb916b000a3f433ff96674b4285c0127ad88b64f1ebb505a48a7f25215891ebdd5de6ffae7442e104b89ae071722fd36b438f1e52899e7099204ba6683bf20655ae9948d1ab6aac5a3974da746b9a964607e8e92531cf12a6b3a7ff4916f60370e83eda1058fb3a355813b788f893aac8aa315fa9f50aefd3a51300f5e62151401d635a23ba687665affa1d72534d647f9291082472e5590f14d37e4e5aca345967e32fa64bab790b650e4528652116a5f4c0feb8f16a15d9ec2686dd7b27a70d1b68830595296dac0872a7b5462ab88f0cbd11d66935707a22544bf5fbb0f6b755557016c8a7cfe907ca898a7060c237a00b849acd2b8eb9d402313fddea2e5e032683d57c2dca01417a7fac10020c1d0c8ee53586fce8a2c009c1445f401ad8065b61f7f3143f5b28b99d189de4da60370ada554055c4bada4d408a62639233bcd5ef3cdf84fff44e0e1ba5d26c7020064c45268c4510931c65a429bbadc586db89d5cdebeb8faf1d466eeba9e8dbefea13dcfa809d5b198cea5cfe28344a7db5cb89b12c1ec879cfb3918ba6743d9ce71ec103973ef683d511b7e698d351377f5f90d742ce09427c59884f653ecc0b5ec0056528acae073d1d41c1991bf60c222d48597637d472b0182ea9c26ce3a3cacb3118a9ff960d77d689f6248657e94363503747dd5c4e50e01efb9a6f363bdfc03d3a5eccea8ae469f82481c8df8669951494c5ac1c746c20916f2ebae88ce46fc30b233c003ba7d3b97a0ea7f56887cb7b9c64f2eef5cdfaec656de9ef8926ddc424a7d1f3d4cdf88920e7c832a0b4ae56260f5edc94648f74a20ef598281a4f6758f5171ff91d94b9cd6ff7eb2936b74ce81bc044a7fb67a82aeace289d113f7ac182f090192e4367c77c95c539d5eac1513c62a114b46d5cce2dfca08d0cd31d13509cb7e9c5751bcc7eadcaeb3630ef6c6e44992ff7b08623802e1958206885c2a75575bcea7ba29f4568aeb54939d15e6163e2061bf5a5395443e3a13d81d63e63650b7985b10e14cb13ad000624efec801f6445089448dd448905e97b4c88c31da0ed7122ce2f79c6cfc99e1e0e6788e5d8d84f9929925d46986dd446684d05feda85089bf2fe0c97d6c804f56775583491e808ae4f8900a5c637022598a1e5d1d56c114918efe81663a144df96f48ccc76bbfa9e5782f981aaffd3ac384823519d18b09695ce518c3d549b4f9e25e386c01860749d41c9cf7c15f7b81becd2e5036205b0bdab076a04a72ff973376dd6c24cb8d3cb5b05ab57f5fd43b03f02a6001c7c61f4f8be31bf305d62fc10fc984f9105f7e01aee38efcdd1387b567cb17ed8b5959cd942d145b5f4e1f53670ad3605930359674c87205898c9253352fbbc7c6ff177f3ce6e9ec0996cc633035c2cdeda2c163db58908b0951fc828872b76a23956bf7ae0b095c085490b24ca9126e369caaa1e95b893da275f24748f3593df166cdbeb2b793da490018fc1c33dd24c3621ca9cc0797a7992d027801e57ca9e60de3845ad87fcb07b6576d0e9c32e23bdf268148a584d9e81041dd15259b6019f64977b04c161306968c0cd0ecf6d1284d12805fd0c75c9380b12a9492086fbc0342e184f71c4e49cc333b851c4d4f82fff9d2318f0598ece0dbd98de3fc99719b05cec42cdddf8a9ab99d64f618294800bd59706b4c9e84aacc9540613b749af9ab1ae89e5241804888464713cf625d41c3dc7c310f9e3c6345bb7b9613d5033d66b20e4e14d592e828b766fb49cd030dc4e4621cb0f2bcc6d89da83a01316a34f48e3bb6254f3e6c50e3596ccc4a69aeadde2fa0fcaeee0edcfcdcc2007e3cee56d4a64fd674823613182d5f2179afb0b12153f6f5cdee112d7ce2528484cd2e72fdf67d4277b80b7bf13003ebc0fa7f426158a378de62bc8ed39ee89b09f468c21e2da5921ebbd906ba5b62a31059bc9f7f5c19c2f3577d1b6c6cce95576ad77cf359cd3d57abea0806addda098faa10bd2ebd16a9c60a356519892731e54248ef85397c929e6e65014a6455fac3367033566eeccdb1a5e2f4bc28ec79332b16da272343096537e8d2addac62c18bd4ddb7b7d06fb1cd02075574a759ffcd691c84431c7b829dc396bc98d7badf623ff3270cc3bd64b9ddfc28d1d70d0193125d50d77e0fb6c8ec87d8672d331190e8469361cab30252c621b8be715a4793c37c31fb340c4bfcfd6243f7b02259be7274cbad1855600124995837e926e868c72ffe084dd2ff64ba845da2b52072278e924e5697084bc9a48e0463e77e1f666666e07c620f8123037ecb8"}]}], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x20, '\x00', {0x5, 0x6, "7276b4", 0x0, 0x0, 0x6, @rand_addr="174c684ae5209fc1a879696de3556bb5", @local={0xfe, 0x80, '\x00', 0x0}, [@hopopts={0x61, 0x201, '\x00', [@pad1, @generic={0x78, 0x1000, "579a27cdeb3718cf1d371966b0028c95fefe04531cd734990d417049921bce61c2ed4cec652bec42ea98e004d8c71c1e6dcffb73b4e1358b56863699d14a095612c6a780ec743daa3480f637b511efa005512407bcdbc240c94619581d849a291ce1b3bf32f31c41c334aeb0c78cc7dd3b799679ff8630ad238af32b8ddda0b95259715248309b1fde84839dc004371b639f63dab820ac9308fb63d582c87a3f68ab72c1cde047bd646b81a97d7712031e7be044496916910ed8ffff04b9d8d3f83bffd83b0eb501b757aa7c64daedf1e40553858bf392cbdd22d20ed5b8a24ceb0874c8577b63d693cd1f09f920dd7eb8205d94f9e0a343a1f2ed19b9f4d78c57917bf98d38d2fe71d8a30f31ec7ed420f2631fc3add448049509cf2d7cebe20cac75d389b9ab0913cbe1f1b370f6d7f06dfd0a1a22429609f4cd451d61ee4d03b017ebf9ed5dc82ac4eb37e4359f03194836dbd65a610b5bd963eaf4defe68d33068ff2ee01fcba6002fec05f74bc741da2e8f3aaff9175e94542308110167d9110a5ebe304ee8d700c45ff51a594eb07659a3c3146c726a7c63452bdd75073495c438e83001a80097c4e05bf59bac58762fdbf7e98c77e290ce9d34658cb77d5ab3a363fdaada50ddae243f7fbbfd6ee0624b920d930b01c9a03fffe36d5b78707853650fd0c1c129cb17e8000aaa6f64f299adc0ebe7932d1600503798be741f86cc9cbf009b55d129d9d1850944a6c18226b028a60647bcbba80a0b36891c9268174231ac15083b9c9c49311b216ea424886238d0f8725eb1019075186f415e505375d0dd09ba3575d493f919393cdb5b7564d67479dbcd50218b8750f79e41162212ef2a7f42743f13ed8173fc67d78484d663792c0c1649c108d4590eb48ec693ba27f2e4af3e992dea24306f36d585bf3ff4bca46e0d9995a9fe716c1457f6451801bfa0f261b419571a7d7a59599d8014aa71caee19efe165f61fb88e0b3661cf132b4fac9040952afc42c83013d4b22993aacd6444093c01e0390e57e032aa7c2d2c72908466604437fed1fbdb4b317412a3d9bd24fe4ee0633f7355e125bf7af47d4f5315d79477623bb63a5463cf35b2500e7355d02e93aa58017fa599bd34652a5d5df2391f82f7fd1b8f6f17f40bd4fc8a38da497254ec7e062ae36d7c03c60f63c59267aa226ef3d18f9ff3493319137efba74f2ec86b8670cab8d4e8ad37d5e6527edd810e0a5c70e90161d9d0e7372e144b446c8fd86380a8dcaa0d3d816d8df7ee08fd31feab4a7bb298e1b6fcbdfcd97d83b68fd87233204ec17ead9dbce7685a457ecb49028bb54b32cda07a0c1a79554ba8f7558351aac158cc60337517cb1e05f4d9b0bd6193bd9b7011c92b89e401b449a72b641cd93b7292e3777b81ce29a3fe575b2ee985b084f5bfe89f96eff4ca923eb93e3266bf14336276baa37182055a437c0f98ea85bd1da9066c60df21b20cf9041ab811edf7c6cddf8ca1ec227910ae4ea7c6e2e2f12ccac8712b26fe313802f053768b08c27d04dc95beb403f131735f96ed6b1c7c1a3cefada291db92b7e2bb2d67f0166b8f0934b4694f258121b3d1b02c324cde8510533e1da20d27e78d459378025219a30b7dfc3f69b1e78d9eaafad225cd640d450d7adfeba9f099724a6eeda9cc050eb2930678324e5217b0a812ae0e7bc32555211697b5b97cd1051c40a0cdcb6e45bccaf94e8fb96839296b51ea282f452e2fda5f812e14df0ec833be72ef5a51559704aedbc87d2e9f6dd79b9af25ffab186a01cea8f68bf4dfcc8c743407cf69cf67f8892f7a30d21acbc33978b033f28ddd341a8bd60636a5063349ea420e78d30d45619f120b7dc083ccc4bcbe5a0215a77adf1e5f65f7bf0e75dffa6148d6d59f73b01b08d5f756f01c14f51d63a3690a8b652e2b352d294b3d3ebce90dcf60eba3e00b7686f091a1d7267bc53e6a1425a4328be205414cb0cd330d2cc0912eb3ab09c0fdc2ca9df8a6023e81a47247708083519b2233c791cb125b5924968d354713f7153531207661bfd8c9c163927c6110120817d1a823619dc67e363705d2b076816923a8adb618c2aa12ea53e2df3f90e9ad426f8128decfac6d33c82b09c67576f3611110c747d3a24873b0752550087594b34bd78df0bbbe8b3dea74f42878544d4e68b97ff537c8e73499617039a02e8434c0577300ede59e3cfa05b52ea0cc7c3553d1a0ec4c85b3f90f8565c5e3a1aa5695e086c58a6863d778499d5f97c1b88f4b6d61ed0e2af2a0a4d55f98e2af57b1f9d2fae33270e650a9cad4a4b27d4e70e964ea44bbd718cad85fc7b1a1e226a74680d35d58355b31ab381cffb304b3730026373d2b512c3f9d4db5c66d79a40142f9a3300ec2bab984b010cc6bc380be4969c100a1c4231ac865781f7021b030b35ce34cd0b14f82c1f6edb2f6127a2a231ec32634c28456974c39c48aa753d7ac7d06fda8f7cbbd1b1bc293362bf8262c4a30d5f11c82be182902a7c53005fcf845c7a6226062a96ee9b58cd32d6ab62e4668a8e1a37abd934c965da3d3ec0a6eba7d79f2048fdb9066111522abb517c7571c91a370e9e12ff7abbd41820a7d11d71d980b322ac9528fd043726b84944d636a4074b0581effe24b5c088e4dad6f17f9d61115fa5c701ccdd8c85ce039dcc5a6346bf2b4e8de76feef3098738525ef98342f0dd5819de5952940c3fd9477da5d980a169f6d5742b2fc1e026aab220e9c17279ca9f3052cb165e1764f9b684315b8252c365e228f3b75fa86f8bde784a6ffcd8ad1e28bb409682ae6b8a0d2552409c895ffb9a6f7ab104ed817bb76d3dbf623519325854af74385786a2e75a523192c2a9c3e048b9c45e4d15e3bfdbe0553214a41293b5d7026aeda65472d7ea7778efc31d64bdb4ad5aeeb453c5e51b8066f5fd9a1b375fd1cef0cf784b824d3301414eee3be6fa5a1fecfee1498f035dc4e10a4a21c39be213444f32b8de92ee14a43b5db4e87efe8c9072ec06bcb64160effee4283e41ac3b045dca656df1d21af084a83cde0566e074c91a0914fc68429fa3fbefe9395afabaccdc766d6dbd4b9d2f6e516f3ab78dafb1a83750eafd8006dfe07a6e4a24abdc691b61883fb9a421a4d5ec864c594154938397e533648391b8cd018f723fdf34907d212c1d952c6d655ba324375bb4a418dff31a5b28c4f13d79ae1c3e75e14f25b27d1be0107575f8120633b1164bd2979bd2818d521ebe6e420fe1557f819759f97a5beceb7274465198eb36ce0585f1b189410bb7838de8ef3dd299e40e82905684b1187fcc8f3a660fdfd3a77b442d4a401bf128c27efd209ff8102dbddf6f60a5ec9c626f8c0cecccb2ee183514a41eb61458f5151acfd14c50414b30623cd4d16d46445b979ad818f2a41be979c504b60c4590f362ee3eaf9df057909bc546907ade179f503eb6fa69a40e731b619dfae746ed706d4ac4964e7472de45f8f67f6ef4f1498588cedf9e240bad8fb63f0de026a1cb03c66cd6b964d9df0f58304ff2487a24afdf46b5717d6648666de2dc1ab05f850ac57e39fd77cb67f37d605d98f4dbca2de712eeda4bff5508f9d7d367dc528a3f142965d24a0f2db0cc4d2003dd10a5e123506a0399b528f1223772c4cbe8c31f936e0cd881b87c7010fed7dc81a841e288825fb7839dd290fe47c4c1717ab4e5c192bbbe540bfe02e615a4924c84eb6c42dd6858bc6f7ad2c22e3c8642ce77139ea0a1a3b55906db6817f5a08cd1e4d838338e01fd9b3ae4b501baededad395932b60f77c3720cb9f21086033f47d242512a24a9f810448e7c0ed593d8a2f9aa9a6079964bb752c24d1a7b8dd65a92accbffad144c01025032ffb336af54e14869b10d204b5f0f0b5a1badfeb340924fac034bdb1f444da92a61454c5a8a1ac7aace2feecb77201443e44f4ad7fbfdb57816ee74b5057a199cb5b0479674e2a90d618139fef7dbbe235b60132180742e1501afb12c8b64c0346332546860a25daf25dfdd4278eab86f4463905af855297a37934334355d95500419320545997f5c369e9d2c65ecf7437b62a5d2e7efadf7c8c66cd49fde959eb99632e46a4359ae51bebed73d07d3cc3d8a2871b443be64368bd871badef78df25801580f5eccc8750528e50c1eca3b3ab3d26cb93d73c04196e31e3eb63903322c782c02c5cf62f02234d6b5efb3ef22fb72f5472e0697671368ae81cbf7e1032ed482eb75617d174fee607d643e25046cfa7a0beb7c2081a3b13c34d9d5101027f28fa7a3808d5ba03b33afdf12efbadc2463fada597da438d4fd3f9281b23ecb96374869487a0e6562e6934b72101c23a439e0fa6a77ed00a644ac8792fe303b86b4ec843b9aebba138b0c9bbcf184557bce8d0f81a364965c438bdf579b4b5e14efe0021ba7b4340a0f9c29daf5ad9c0e0ecf7281914b10ea9d826de6cf16e33ff75c2604d75c9b107c0250aae3bebd73438a4c66a919545873002e2a0b3e01551035d3d62c710f0c91ee2beff664cedaa99af1e64f272c89c5b79c1c11fffc254d219fff067c00eaee1c419d4ea8c479fd176e07accb48dea45188b2e22ba93d406d5bcda6380bc8c1b45c340ed6a4bba567c3ad950c184c899a621940dff159a7b4641d3230f9662448eecbe69614673a61e9f4aab205984b773e5c21c54eb5c65ca575b9f73b848c5001e09c2ecba1f1c9f434472723b264e9e37d72c352d2f91a28641b850e4b0554b8e1adab2fefc7473c303ee5c7d958d8b5c51d37d0b005e855ba50d35362d464a0233ceb95360fed957a319529ea108ab6c68fa5f53383f17880e313840fe1794091412f2904bf6b8a3b06860ec79ce0c3840d161226d4e5e8fc8b88f797d05eb89d6e6fce5b4e665e4e0dbfb54442ee847bf31097d41f43194e1deec558efb1aedf9874a4ce0c3cce97c8cce8d58acdb2df57e5dba9e440711d596b8a201cab2554ef6d541bf1842bc00432117d82a0acea2667d8fdca04348c229a34fe58a8659e54f4b15d13e528255fc81317a5db82f2fe120e406900314ce4cce7db4e3b2b2d7f1d3744fcf5623cdccc60ea055847e2e4cb024e8d662b724c9615842fc8280882debdcddee49749e6bf374a80df29c8f1d59ebe966ac7ca8cde21fb8937eba580fa5b33aae041d35f89a851a167e652610781cd7e7836108997f80f7d5bfdc6e2b24a2f137e5bf06ee3b9c9bc680b3fc27db6def043a72527c09a99d4379cb5086391e1b3e3678b6122e53b23afb654e9cc83be3c5081060342cac4e06cc87a29901ea6a392c4ab992ed862da5d987df67bb39c2a3341a5f463dc65d8da77959e15591e3331a61a37aec3fe204ad53093f1c26550c55b5831133fa26cad90a24ff5418197bbea83b44e24f9b5f5c4779e5019ff7eb3e42ea160b30234e778d09ac38c0c58d707de182dab8b2dd6ed0e59285b78b158c20975e894219f628c56b98886a8b65114c07e64e39f1f92e269e8733865814302c979df255b926fab105f053cd820805e727c91776c73c8940c6bceaf22904f55fbca9a0d34a1a24782812da9729773ad7d37ad495cf961d96f8a3585434a3ce277757c2fbf1d754ab9ebf16c7d252d74e50bef9af37289fcbaedd81397ca099eb85fb484b9a0c3d2f23f7f0292acd2cbb39b2c2cda4f9632571f1fc4a2971c13a7be1d3dbadb1befd9ad1f807308b56e472c032b78469b5e85acb03b2d041b2d32a32f45945cd9e8439f624d11cbaf146918e6588290f02983b2e7c961c4b00a07"}, @padn={0x1, 0x3, [0x0, 0x0, 0x0]}, @ra={0x5, 0x2, 0x5fb2}]}, @routing={0x2b, 0xa, 0x0, 0x3c, 0x0, [@mcast1, @rand_addr="6149a34ec737522bdd7bad434cfddd18", @mcast2, @mcast1, @mcast2]}, @dstopts={0x33, 0x18, '\x00', [@enc_lim={0x4, 0x1, 0x40}, @padn={0x1, 0x8, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, @generic={0x2, 0xa9, "6862928cf7cb361461a9226e46a5d951cd433f646cc6a79ef61329a212ac17f38c32c82b84128ce8852c4d837c36d9c2e5f9144d31792421695b4d48026328e185c836adc6b09e473d3c8e8ab003e7ecf700c42ceb242a5d2e7db4490cbd770bdcfd4107052621861634fac04c810be2e2544a933caa364c5d140a6a4fd820f20c818b78e7fa23e65bb15764fe71b7afdb9608c79cffb0e35167e83d91ef116ba78e55886a6843122e"}, @jumbo, @ra={0x5, 0x2, 0x6}]}, @hopopts={0x3a, 0x1d, '\x00', [@pad1, @ra={0x5, 0x2, 0x6}, @generic={0x1, 0xe2, "ccd89fdf49587df23e0fd8f00d17467c64e50f95a0a1f3e32c0f0da16f120c83bece3e129412583549db7b28d1c552ee9ca8fe121d0c976e918680ac18320cfbce8676994fff2fd80ac2458f2bb2afe5fc6daf7a9e7de02fe670eaef9a9d95ad4f3b841bc404dafc011a5d4d2375193f3eb5d32c3288ed83635ef7ed500b078a51ce86ae83a72babfea85c08581f49579c41cb63d083465f73913b995eecbf69994b636b9d0dcd07a7974f3ba7c969099761a333e4f4b8601a0194f0350b0fbbe7498fad9914026a82fdbb37046f6d9f46a3c3e2239e2b94e786a3ba0f89ad9d3e40"}, @pad1]}, @routing={0x4, 0x10, 0x0, 0x10, 0x0, [@ipv4={'\x00', '\xff\xff', @multicast2}, @ipv4, @empty, @rand_addr="20b08a72d873248906311bc17404b69d", @rand_addr="5812e8ffc7c51f413b5136a976cc2915", @rand_addr="cf25875b55a1fe49fb5023201b4fde43", @mcast1, @ipv4={'\x00', '\xff\xff', @multicast2}]}, @routing={0x1, 0xe, 0x0, 0x1f, 0x0, [@rand_addr="577d0769e711a59cd59d4adbd394f883", @mcast1, @rand_addr="d91fc6677342798abfb227edc87228fd", @mcast2, @rand_addr="388880740f6fc2ccfccf3b24d81c6525", @mcast1, @empty]}, @routing={0x11, 0x4, 0x0, 0xdc, 0x0, [@mcast2, @mcast1]}, @hopopts={0x78, 0x0, '\x00', [@ra={0x5, 0x2, 0x3}]}], "8f4e95561bddda0ca6d9b17567330d1557746b092f4cf3d67b99cba02fc36cd64cada92ed053c6c910952c591371c7e56cb72a52874cb94d67d4ac660f49006491424bff2e9e94c0b03e814be48082f4472c35baf4c1b094179688bd93a86e289df81274f4eac9ca98da5ecde6de3b0119fadeb467a7104d05645d7b10c6bee51177cc604541a5b02bf93e6d30dc6beb122c42ea7210e053612ab9d52ae6af069b5fd34186f1eff5539089ebab84059ba58fdd65ab077ac1b94b9f51872c2ec434708740661013e4"}}}}}}})
syz_extract_tcp_res(&(0x7f0000000100), 0xef, 0x7)
ioctl$VNDIOCSET(0xffffffffffffffff, 0xc0384600, &(0x7f00000000c0)={&(0x7f0000000040)='./file0\x00', 0x400000004000000, &(0x7f0000000080)='./file0\x00', 0x8})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000000029, 0x9, &(0x7f0000000180)="01000000", 0x4)
setsockopt(r0, 0x1000000000029, 0x9, &(0x7f0000000180)="01000000", 0x4)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
socket$inet(0x2, 0x3, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r2=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
sendmsg(r2, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="8202873f27"], 0x10)
shutdown(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x5, 0x4)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b9000513600000000000000020000000cea11e", 0x13, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210]}, 0xfffffdfa)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000540)=0x8)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000580)={0x10, 0x2, 0x4, 0x0, [{&(0x7f0000e10000/0x2000)=nil, &(0x7f0000ee9000/0x2000)=nil}, {&(0x7f0000bea000/0x2000)=nil, &(0x7f0000cb2000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000bbd000/0x3000)=nil}, {&(0x7f0000f6c000/0x2000)=nil, &(0x7f0000cff000/0x3000)=nil}, {&(0x7f000087b000/0x2000)=nil, &(0x7f0000800000/0x800000)=nil}, {&(0x7f0000ee1000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000f8c000/0x3000)=nil}, {&(0x7f0000aef000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000914000/0x3000)=nil}, {&(0x7f0000f81000/0x1000)=nil, &(0x7f0000ebe000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000c62000/0x1000)=nil}, {&(0x7f0000b47000/0x4000)=nil, &(0x7f0000da2000/0x7000)=nil}, {&(0x7f0000b26000/0x1000)=nil, &(0x7f0000a6d000/0x3000)=nil}, {&(0x7f0000d22000/0x1000)=nil, &(0x7f00009fc000/0x1000)=nil}, {&(0x7f0000a21000/0x1000)=nil, &(0x7f00008c4000/0x3000)=nil}, {&(0x7f0000bfd000/0x3000)=nil, &(0x7f0000c28000/0x1000)=nil}], ['./file1\x00', './file\x00', './file/file0\x00', './file0\x00'], './file/file0\x00', './file/file0/../file0\x00', './file\x00', ['./file', '\x00', './file', './file']})
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140)={0x2f})
sysctl$kern(&(0x7f0000000300)={0x1, 0x22}, 0x400000000000018e, &(0x7f00000005c0)="f4e469e1416d4c825772f597ebcea58037d338159133752d73e0570ba3ad247f73a33585cb52d522249048fb5b49c6eca1a86e96c9aa8cc48c7c21fed028bf2de528b9578b94179a9e907d9ddb4ffc04bcb05a5d09094c643d3538792d1d5cb4a5cd93643525f2b0f72ca224f2ce59d2713722f248899355c4e2e2f9c420eb19b06939f8cc1849", &(0x7f0000000180)=0x87, &(0x7f0000000240)="935b45", 0x3)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{0xfffd}, {0x2, 0x0, 0x0, 0x11}]})
ioctl$BIOCGDIRFILT(0xffffffffffffff9c, 0x4004427c, &(0x7f0000000200))
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
fcntl$dupfd(r0, 0x0, 0xffffffffffffffff)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x4, 0x10, r0, 0x250)
r1 = semget$private(0x0, 0x4, 0x1b2)
kevent(0xffffffffffffffff, &(0x7f0000000840)=[{{}, 0xfffffffffffffffc, 0x42, 0xf0000000, 0x2500000000000, 0x2}, {{}, 0x0, 0x84, 0x40, 0x5}], 0x8, &(0x7f0000000ac0)=[{{}, 0xfffffffffffffff9, 0x0, 0x1, 0x7fffffffffffffff, 0x400}, {{}, 0xfffffffffffffffc, 0x0, 0x2, 0x7, 0x4}, {{}, 0xfffffffffffffffd, 0x12, 0xf0000000, 0x0, 0x8}, {{}, 0x7fa36c34f9ac6e15, 0x38, 0x40000041}, {{}, 0xffffffffffffffff, 0x74, 0x40, 0x0, 0xdb91}, {{}, 0xfffffffffffffffc, 0xa2, 0x1, 0x4, 0x5}, {{}, 0xfffffffffffffffa, 0xd5, 0x2, 0x8, 0x40010005}], 0x9db, &(0x7f0000000bc0)={0x1, 0x100000000})
sysctl$kern(&(0x7f00000000c0)={0x1, 0x1}, 0x2, &(0x7f0000000100)="8e40a78dcfe50284d5d5c343dbd340e832dcae51442a30519354d977dbcde1e06c4057d0befd082cdc2e156aa7ccf6dcf9fc5ceb8c7de37a61e2d5bb1bcf9fd6a84c9f95a940da593e0a7852c1609dd4ba187ae907458655c70cbf331c45f80300"/106, &(0x7f0000000340)=0x6a, &(0x7f00000009c0)="56464d8b23fb280b2313916e5570cd62af7fd2667f28a91a028cb2afc4c6094ecab395705ae07412cf73fdc7de9dfcd636618ca9e658f3985a5012cea2ca6ab563fe8278f11dcce6125e5bf2a12c94327224da23c5259110346a4b73da70f4185fa0b8af37b0e07a38224969da4597d46afe76e9cdaa8ca4d5689fd1b0937eda8d61356bbad3b15ea0c58f61481eae11b0ce69e9b51601b10fba939bbf585dedc4d16654606dc1027a9347226537fb80fbc60a8ed630b9e7597af45de69291b72a30", 0xc2)
semctl$GETZCNT(0x0, 0x4, 0x7, &(0x7f0000000c00)=""/254)
ioctl$BIOCGSTATS(0xffffffffffffffff, 0x4008426f, &(0x7f0000000040))
semctl$GETALL(r1, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$GETNCNT(0x0, 0x2, 0x3, &(0x7f00000008c0)=""/151)
r2 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x1, 0x11, r2, 0x0)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000280)=0x80008)
faccessat(0xffffffffffffffff, 0x0, 0x0, 0x3)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x81a, 0x8000], [], [0x2], [], [{}, {0x0, 0x80}, {}, {}, {}, {}, {0x0, 0x0, 0x0, 0x40000000000}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x61}, {0x14}, {0x4000006, 0x0, 0x0, 0x3ff}]})
pwrite(r0, &(0x7f00000002c0)="f2d68c842ef478efef73674aedc5", 0xe, 0x0)
r0 = kqueue()
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x1}], 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0xfffffffffffffff8}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x5847, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000280)=[{0x2}, {0x6c}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)='\x00', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000000)="ec00f876223f800001000000", 0xc)
syz_emit_ethernet(0x4df, &(0x7f00000000c0)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "78ea4b", 0x4a9, 0x2b, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[@dstopts={0x2c}], @tcp={{0x1, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}, {"607262b3b0c5ab9988a2df1f074158f16f948e8133c5226760f862cf048ccbf13a7964f3fee8b2d52ec6b9f8bfdb4d549f258b71615fc26e06f237673edff68152abe8006ebf570346bac9354e21889619b716f4f889710359d61c9dda5667669a29e60b8c6649941d49df4c6d501c41d543d891f120749a54e29e6617e7cbb5cb4f5a575574035f6d6a49c8333633920c9a260301c6ea338a73263056dfc6ff34524443950db7b81d616367d05d2bdcc38e3dfe79098bf88af4da396a24b92040f49c93a9c2e100b8081e465bcd933a95ff1529d405600df325fff290d87151304d372e86fa277ce0e832cd7b6bcb2d5df7e733246210c7b91b9350ef649b043da7a8a9913506b92799662982b486bfcd60c42b012bd29d7737bb25bd5d9436b19394fd3df082658e1d63d06dfb9892b3d6a0da2ef18826abedc572ad634b0fac7ba5300a7cf0899aecb355da25219792c3f12aa0797bf9982517cdd9b6283112d980b178df2b55976d9fd0f5070b6522185f36d5a0e1bdf013a845ce55339002a43eaa8ae0a36a1d0ff6bd6078c3819fd4c77eb2adf7231f8fe96570e8fd407c92e2f4eb88c6f8df22097b9f5a0bb4c020e935ac1863934e919f96698b500afb5e4c3eeae38674e23cd9ce0c033ff4ea057f543c9470a5177bae9bce92ef98f5b06c44408ce3e0736385f404d00e2d28c0de31ecf93c38c17de5f2f51396890a5122632b1aa856bd61c8d1d39bf7ad20e669f2949d7b14b36f4ac83b3fe3d6b2120c5be9403d32c765e21a9c50c0bf14e209091f89e5e65de061a3a8d61d9bd95df78e0aa9a176c5099fe326f5656190e1cd924211f6a8a8f69e13862d2f810af477a2ef4b13049c78451e0f32a798c5a15ab3769b478b4df93ca45dc0f946df860b5e1f88761083aa0c68201ba9d2b58e159f094d4eaa5d5477359d56fa152d88d4353e09b03edde9c53886f97e16f79387da7d5c8c1fcefcc0e3d107c1a5dea62c769cf6ad80dc2ddd0ce685cc254ed3ef3e5f5e7edb5780a3f1b6d8b90b44445dd5c940f3dd106144e1d10124fc321cb36d0b2bbf6e539a94dcd4cbcf437f6d04733fa4b25a9924e4b64f21f0591848509b6cb713324a989b32f58dbc38a2bd7a13b4759d331cd1b5a596cf7a78d6c83ce0604a4d3058c75873f30ea1f10d4c118f034706c6cf90addd0f73037b93c1911bf01b715a92585a8e47c1f9a208388b5fc748cb1d420f1f21c70cedfde2cf1f79cec1e98a3b579d027ca468592c5a9f98a4f900d5a9b0caaaa0f553ad1c7f613a4fa7af21fd481cbacb6c2a554e5bbf201cfad6e89710aa525abfee8dd91fb274aeb9034963f4b79eeeaf2a91f07844ffbaa8b2238c6eccb5ebdd6ce4bd3edc02177eb39d948573ab611a15ceca8d7e7a1e02c99beb4d3e036a795958df0bc30f82c063136cf9c7b5c9ce41a18e15109ff6d1239f905d153436df80906469a2b18ca33700eb39bb4e419f2cebb865873a7ff32bf56f2466a7353582a63d2111b7a7251bb5afca6c833785677029420d7d6d442ac094ddbc5c44023bc9b2eb7efd89407fc743b482a3f1a5fd0dccb524bd3699722fbb5a2a2e2a3e6f1a89712b1ffc435d7e8c8dd85b61abe3a00fee2a09b90bf4b1f01e723a52"}}}}}}})
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
r0 = open(&(0x7f0000000340)='./file0/file0\x00', 0x301, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x4}], 0x1, 0x0)
chmod(&(0x7f0000000240)='./file0/file0\x00', 0x3d2)
r0 = syz_open_pts()
ioctl$TIOCSPGRP(r0, 0x40047477, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x50}, {0x35}, {0x6, 0x0, 0x0, 0x80007ffb}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup(r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f00000001c0)={0x0, 0x0, 0xffffffff, 0x7fff, "f79ebc0800000100"})
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000380)="a56e", 0x2}], 0x1)
write(r2, &(0x7f0000000080)="4716ec839301", 0x6)
writev(r2, &(0x7f0000000640)=[{&(0x7f0000000200)="35b6870908", 0x5}], 0x1)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f0000000380), 0xe2, &(0x7f0000000480)={0x7})
kevent(r1, &(0x7f0000000100)=[{{r0}, 0xfffffffffffffffe, 0x129}, {{}, 0xfffffffffffffff9, 0x1}], 0x2, 0x0, 0x5, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0/file0\x00', 0x0)
chmod(&(0x7f0000000140)='./file0\x00', 0x2f4)
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r1 = getuid()
fchown(r0, r1, 0x0)
setreuid(0xee00, r1)
rmdir(&(0x7f00000000c0)='./file0/file0\x00')
openat(0xffffffffffffff9c, &(0x7f0000000440)='./file0\x00', 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8040691a, &(0x7f0000000300))
r0 = kqueue()
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0xfffffffe, "f722aa4b0e832c10184700dd47c8e9d2be670991"})
kevent(r0, &(0x7f0000000000)=[{}, {{}, 0xfffffffffffffffa}], 0x890, &(0x7f0000000180), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = kqueue()
fchflags(r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000180))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
poll(&(0x7f0000000200)=[{r0, 0x1}], 0x1, 0x0)
syz_emit_ethernet(0x22, &(0x7f0000000240)={@remote, @random="c36c1baf33bb", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @loopback}}}}})
poll(&(0x7f0000000040)=[{0xffffffffffffffff, 0x100}, {0xffffffffffffffff, 0x4}, {0xffffffffffffffff, 0x10}, {r0, 0x1}, {0xffffffffffffffff, 0x20}, {r0, 0x4}, {r0, 0x40}], 0x7, 0x0)
r0 = socket(0x2, 0x8001, 0x0)
close(r0)
r1 = socket$inet(0x2, 0xc002, 0x0)
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
r3 = dup2(r2, r0)
setsockopt$inet_opts(r1, 0x0, 0x1, &(0x7f0000000440)="ee08665d19ac14d5e51348771197a7728420aef61715f7b183d4b3830c921bf0817a0000000000006a89dbdf", 0x2c)
connect$unix(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="89028600ffffffff"], 0x10)
writev(r0, &(0x7f0000000000)=[{0x0}], 0x1)
setsockopt$inet_opts(r1, 0x0, 0x1, 0x0, 0x0)
writev(r3, &(0x7f0000000180)=[{0x0}], 0x1)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050260000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be78162e0000000000002b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f572c881ff7cc53c894303b22f310b404f36a00f9000e01be657aea8c500000002000001000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
shutdown(r0, 0x2)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmmsg(r1, &(0x7f00000003c0)={0x0}, 0xa2c4a4fbccc5c5aa, 0x0)
close(r1)
recvmsg(r0, &(0x7f0000000700)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwritev(r0, &(0x7f00000016c0)=[{&(0x7f00000001c0)="03", 0x1}, {&(0x7f0000000240)="4ed7534dd8d17c8aa116765b1ac6345c59f26c45db8f94e3be37a9009550f5d7278e37747ec799275436183538433e6d84aeaec93a30ac25be7329d7b10968bfd1483911f8c2fc725f86b81d63b80baa0b910eb54ba267556db57b0b6da84f00955816a4e12f079f96bbf0604fd620cb08c5aa7685fa72c0807abaad215232491d32c153f0171ff5cc02ceea0e29e49f47cfeb554c554eab41a2601c16a96f75e2000c7559ee2f91b17a379b4e787492bb725863d46eaef5e01ce401ebefd1a32198c8bb517e", 0xc6}, {&(0x7f0000000340)="bc7e016be54201100500add1dbc15026c8c4ea428fbc48e8ccfa3ae02fb0fba2f4b4b5", 0x23}, {&(0x7f0000000480)="9494a80fbed0ecf5d84d0f4e47f93f7a8d822cc4db3f09ebf30aa91135f3c30a31a0c75504573433d3b149c02f297fe7cb6181c523bf004d95442d1d2255e993f39a150030e93a525c3d326a98c8e6663f28f0f58dc4a693511ef6185d31eba29ec5d88a5474c11074a066cef1a1bc0487d3d430ce4c634a7a725ba5ade9b3d40c94f48017a76f389ec2fd3195b600fb0d7f038c7c800ce8471a0eed0f33206a9397cdc079117a71750087643a067d9bbfc68a3b12048f499b4d670f7ebf51b3e6d76d574db3e217ee45b995b5aef1babca188a27c7f22e091c9e2e6ce0b31b539b6fe4c775599c147a906750c434b9346a50e8545b508ed12b78e8c1152afb9e2ea31afe8d5608205e74fc9588cebf87d3490ed9f27d260634ccf6165d16de7c3af9b1d142adbf3be7f34a58d5487e8f1c8777b28df37f012b6f8930fb0240399649c8111e3e1617919f4567a63f99ab3b7e564b4db96d3476066eb50d4aeb0c1921ddb50ef102c1d5472f71bfad6b1f07618266226f04cb18b17c2e8b7d5185e7a401a0b5ee04708b11b5c3bfddf8afc9bcb69fedf40968cd5db3269d9f56d7fbb2dcdc7ac2c31a6bb43e69f189bdf3cdbd0dfb93c0dedd1fcb9a4c7f31227a651d53f86b46ce4b651c8737d270e52493eee12397dbb81aeaa351ae48ccebcc2824cff2776f2e3610c839007203d7bd26b77523d8e748e8e4977f1833528eada169163d19b03b32fb418aba2bac1259be2f6a608d605f2347f1feb18d1007db4ef8df3c20dd6c7407f30d04d55a210095c78566e92acb2167afd3510739689f9f6c8244c04a18816da7be3d69f6c6cec665ed6614194dd046ecea9cc8ac58d2e5bc7a334a0d06f2157afade48f4f3b32ed56f81b5adb0437c766a8ebdf040751b8cc1d7d9659c4fdfa4b47eb679ea07ae03f63d90cab768c36ce3db12490d325129bfc5239319cd4be3f73fb4f3f349570b1ba916d407e509cd3a0178aba3aa4156625537a4c818d38caab4684f89afb7b81a48ceafee65612f6755d30fb1be63ebe05b9a8b65a44abdccbd1e587dc3cc60fc87f345f39fd6ff993c4278bd112a02cf163b8ffea8f16aeb2f801d0e8b16551a36860e49f0097c4966af46da7fc093a445aeaeef337ed15d499098cbd1f3d910972e4ee68902365ffdb7f0adbc708a8efac0754f45b1df009795b07dd2f077b0ed954f2b33fa5668c3585584e561e1546acc1c39ac454372735b78342c2bf4967cb060133bcf0b8147e527712ebce0a6b23f74d99836b6cf4fc73c8f5cfc933f5fcb5866c7af68e5bf7106b370417615146bb6e258f808d49a1abbf438b0a764fd8e90d78fd3aaa8f48a90001c7b34c01aaa1995dd3c294ee9bed3cabb0bd3bc84b43c7fe0ca5fab3621b9d56143b968d026d320797716e31733cbbf7b6b1a7bde5fa3e422f1fde45a5c65207986a93d675ac6505f4405698ab5f07c8ade280e52d70a5d1e649ba76a60a29773987a4d858d4459bf1c80a42bd7946b6e5ea1c73c2156b9c9e6d07f96068c64e3f6c3cb97f64c74f7414b36d2019fadd473f5ae57d2195e33ed30f743fa246e2f3c06b4da9dd08216857500946f6abf4314dc51d1db1b4f71ae33f50f8353538324fb20b74a2ce41b3edc105692163806e642479fed0aa641481a17c6892fcc1ec98ee80f99e3d9d47f5d964fa637c589e62eec392db9872935d2959d46638ee9f52aeabf0a8592fcface78f1f97e533ee1dab675b85a6d024396a80cea0f58e637ddbb95fa507c4f958ece61b00bc1736549e6f99aee3be3d66af20560310e00a", 0x501}], 0x4, 0x0)
sysctl$net_inet_ah(&(0x7f0000000080), 0x4, &(0x7f00000000c0)="ae72c037a6bbc9", &(0x7f0000000100)=0x7, &(0x7f0000000140)="d0016e28680e1b9b637fb4a28e5825a55d77cb18cfe56b7c6bc3aee6b51fba8227", 0x21)
sysctl$net_inet_ah(&(0x7f0000000500), 0x4, &(0x7f0000000680)="9e089fa338f1a969e01276bce22feeb7eb60970a6b4be31b385e97934022f33242cd8ff74e562400a0d264c926595ddf71f4c326e4ae4de55e8e79ada4eb2441d465a11616a031b19b0440e8979e36b486489e57647a5689f7f75e2bc0d9f730edecec99b0d20b3bd6b5221c8032ecc0ddefbbdabe7dbb72b27d5ccd1735ed18db0277f005d0ec7bc488eaf68cc1a98321ca89ec6f607967e2d0313940576525deacb9a4b5245c6671ed667f4fc090e7d2", &(0x7f0000000540)=0xb1, &(0x7f0000000780)="51cf4f01b4bfb00dd792", 0xa)
sysctl$net_inet_ah(&(0x7f0000000900), 0x4, &(0x7f0000000b40)="8151789b6bd3a60569f0d830bbce678b3bbca19b1ab9969c144b649bfcb1791b586f97f4ba7e648f1fc6b8268b4a06acfcf6ef220dbc090000000000000017dfe8a54a4ba78744c88a4126ac11fec3a7652453a3f84908209a49fcc7293d2cf1be6688e2880fcc63141da7e0cec69e542ae6519d7e88e3a7be38f0364626e63ff0a66fe9cb060e6738b9f416e3bf146c31700800ff106f7cf0a2679776f1602d47d2f666055d000087c286225c756bd0a65150634f6579679c62c32d5ccc32c1decc878d37201cbdec206b192811d6a9640bde04463ae352ca8451c40eba1687f6b5aade6e9c4ed7b0cb32d9864bb93c3ab7ab3ac56c8ba2a4d4e77dc9acc2b2cbbb85432cf02739d93071d87f9f5579e42af595864317e6ca982b6765bd9f9fd4d7c58eac410e438568338ec2af263a54e70fa1fad6f7c8e93e513a4056c31113e0495335a39760c7c598720e3d6384a347f6adb48c0066ce607aa36d6b934485d85c6dc98b56a21f9c301cf28703efd3a1b9224f5b7a43f86a1bac7b286de51d9b9f2d105e7781000000fcacf2043a3dc29312bee3b6478f6d34debaa2221a296f8671f5e95677420716566af3211d18cc2bca4bb53900a24e1301fa55a6ed6e687c150a4870263915dc4991825fa3f667", &(0x7f0000000480)=0x1d2, &(0x7f00000003c0)="bcc9ead47907c76e0400af4b6b900371507f5f735381725be4be84bba287e3", 0x1f)
r0 = socket(0x11, 0x3, 0x0)
getsockopt(r0, 0x8000, 0x1000004, 0x0, 0x0)
syz_emit_ethernet(0x46, &(0x7f00000004c0)=ANY=[])
sysctl$net_inet_ah(&(0x7f0000000440)={0x4, 0x2, 0x33, 0x1}, 0x4, &(0x7f00000001c0)="f553860d961ebecbdb3ef311d509b1065282c1aaed0701807f926c551bce452d219bf4ab4fce97517128e2010996e2ca26e40f6e87f405007b3e591bb6c7934cf29cfddcb1dc7533887848e967849d8a4d3d3724ef04bea8f70fa2bec8313b01bad37be7437c8eed103bebb637636f7e2cebfb5d979aa644d11077bec95bd0b3ee776286ab7baffd4c80f076811e2799b85304813d793ff5f208d3c42db9b8981e59c5c7e31e7894b2c7e4ba5da1fc85f1d1f17fb4e8f5357d4aef64a149f29124cf3f3182ab6d832650c748c80441134935b5a5a1019e0b8d23b19d32f386e5052a782f08a6fc5178ea1d74", &(0x7f00000002c0)=0xfffffffffffffe1d, &(0x7f00000007c0)="503803f8876be03f614319c9dc9cdfe574b29ad54168662517c953eb3f551ec08752756df3ba812e085fd90428235a74ce38158313cbdccd89a60e1937598099f4f6e8bec3742e9fca397adb242c54322b8cd13befb42a1150133009eb50e8b9", 0xfffffffffffffe6d)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000504600000f9000008000501000000000000cea1fea7fef96eefc73f0000005314fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebb3557699a000000", 0x4d, 0x0, 0x0, 0x0)
semop(0x0, &(0x7f0000000040)=[{0x2}, {0x0, 0x3}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
r1 = semget$private(0x0, 0x7, 0x30e)
syz_extract_tcp_res$synack(&(0x7f0000000400), 0x1, 0x0)
semop(r1, &(0x7f0000000440), 0x0)
semctl$SETVAL(r1, 0x0, 0x8, &(0x7f0000000380))
semctl$GETZCNT(r1, 0x0, 0x7, &(0x7f0000000580)=""/224)
sendto$unix(r0, &(0x7f0000000940)="28d36e268d5837acd630ff647755c5c4df37e2a62c25a8e53e13b65fc7a8d55a8455cc39f8195e67d4316f95cd5a1c4514584a778db89bf5da2f79060ce36195535fb71c43d7c089111c2e7413944292378e0288637aa00103169a7b2f9b47f0e1143247587a593bf7b70abb5a97ee31d6039ec7c589d4e259978ee7698053ae2648f019dce2e6fbdc2ad34ef22aaf3a4093ff920c5729e76e91ba3e30433d816bf2e857916fb7dd18b647129562698f88da6a8c9f29a543a9b254c04d84dad71e1f7516f23405daad766220cf3613f6493278eda1de825ad749a6fa93bac95b58ba634171d3f4a4585e8d8aa2292137f8f94e2d4bf53e2631187563eb89f781999bcd41c7715a75c33357f3cab1fb377d86e164d71e5b18a78002cbbf4406971fd6587c6ad61b91cf3efc27b7", 0x12d, 0x402, &(0x7f00000008c0)=@abs={0x0, 0x0, 0x2}, 0x8)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000004c0)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r3)
semop(r1, &(0x7f0000000540), 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
munmap(&(0x7f0000005000/0x2000)=nil, 0x2000)
r1 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmat(r1, &(0x7f0000005000/0x4000)=nil, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
connect$unix(r0, &(0x7f0000000140)=@file={0x0, './file0\x00'}, 0xa)
poll(&(0x7f0000000080)=[{r0, 0x27}], 0x1, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x100000000000000b, &(0x7f00000000c0)='\x00', 0x1)
setsockopt(r0, 0x0, 0xd, 0x0, 0x0)
sysctl$hw(&(0x7f0000000500)={0x6, 0x10}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc4504442, &(0x7f0000000100)={0x0, 0x0, 0x0})
writev(0xffffffffffffffff, &(0x7f0000000ac0)=[{&(0x7f0000001c80)="373e40517e4c044a2422fd09c32aacda72525149a00deee9573bccf811049f65d4678e03f98bb6ad53fc15f6029ab9b69e136f2af35c7dffc9d5b2d0f2211f7021d47e4437652b8568c44f5319140c55cdbab73ba75c0609157001f7a53a6c1c8b05c09101e85fd33e6e783efbb4b737aabdb8417cfb7af848416735fbe8eb8c32b07ff6e4d2f8d9815f566d3a43409027c9b134d3d1fc6bc9333c4c8ffbe9760f1f75904b605de357cd229d5b4d2ab1903776cb9b0f43a8cb9bf4b725be02c7f2e0c6b5f6581748bff70fc427d720fa283cfda3fee11db543bbd998d8e293996c405072fc6edbbc3d", 0xe9}, {&(0x7f0000004000)="716a408a3b34f703ef42833bf6ad3c5f481196db1bbf67e41a75b82038541b65db38f535aebc773b629165daacb97b9948b9ae2388cf9b2f44d8ecc97861e6fb03a72b8cd8702724d72a6c1f1718e67379ecd18406b456b1ac4b1a9819f066e78f821081a96aeb86041b632d1099889ec0e4c022398eeb7f4bee2bcd4bb43f1665c0fb3ed0910d9b97193eab22b29a95c5a9788c6b3e6baba7ed36494c2c3fb68a36fa13e766b21a3eef62a2094efb9214accc0719a8aaf2cf227aa96a2e444628161b53a99617e6d481dccdf76b7eefbfd008557e4a85aa36c4cedb850ec3a8a73889a9221514071fca32eafa24c977c1c11df00a02aff2ec955ba8fcfabef12c1bbe6dfae712ed0c6af4b2906cbf75222eee1b46e4c5ac2b6f9df8ec5b21c9951379783cc33ce7f857f0ffa56e82a082a45d73451622c4f46ec32ac06cb7258fc6ab8ebaaf8fbf235ec31c424636b83d50f3e5805c87a0e5fc04fb5bc5f92193c2c13fcb08ab4be9ea8b6b1665f5b00bb2801a45d316c5f9576cadcd1d8771f5fcb0b8333d2e5153b37d2fc7d66bed6962d6f1416a4bc339aa89699f7e2f995f9b5b487e8d5ddccc7b6b7a81b157c51f4f854d2cfc7208e7785b7f2e0e0e8c94fd0c9f1d4164924bfb8c73e7d9a68241ddce53fe674f5963cffcd41639d3b4eba8bc94872fc97b8f09bca75de56530e2eb781ad0ebc748dbe9fe6e368a20b3edd0e1938056620cd644627d465847e9a2bc53343996f65696d705e86ab62ac23d140fb4d13c3dddf4ea67adbd133242c3be50771b3aa5599c3c13ca987e501639eee5ade64184fd80317c8d1ccf1d9cd37db34d794d8d753d00747bb352fca8227edb2f19776076e26937faa8982542322a9dddfe32ecc6c0a10bc3c032cbd0e800e82ed7bc0f78565f32186749b7d02aa41f3988cf6d722a407df691add26e9595eeaf61688f22eda6c9a08186f2a986311f62fcd53e0a27e714856be798e74730d12eb8188377234ba66c18b6adce70551f462e09c1b08177c9d82827ef0c433c1c9fe50b02e673a17b1f87b0cf995d633428d2f3da7169c55208d39c9cf066ddfe6080830506383d8c07bc321c3460bad8fb3b2a48322f50eecdac8a9e7000be10fcca517ccad872a4fd54e9a5ea9245698efe57e774a1c2c6edbb80bcda7a4d67728c2f42c423ce270dee62f512d857def0af95347193dcd97888dbc049b891eaad348c3b99df31b305e872c81ed7109a2113df238be67f622355ca62ba55aca2daa03a19e51db1a1b03142d24d7546732d45f98103e71a89f2851e6f4797d632dae8188f91d1ed5dbf3cc86510bf887a6f0be808751ff1a125667ba8866b4770ea6aa82027f4b3532aeb6098a49ac74a1602cfdd7ccf90f97389a027a3cb210d52845c5a1463ffa690ae9314596e1a2e5d0931ce3c91fc0a1a9eb64a2f35b9abb2b530cfa7edd53fd4ba2b62bb3feb2b2b5cf28bed7e94bdba16cabeb0316d5af53c9fbd942ebbc3d5a6530de04dce110e641aaae09ad547ba37289d90934fd9f1bdc8d3767d5e6923d321b09ec5f367375d54781dad213d5e970e15e3e2b9ea5e4611a20ab5f55a20d0cfc463060cc624432be3d170e51a1ebb13340532e7c56c5d51bc17764d2c1f37880c2578a624be144694b621307b3a2e82580b3b5bf35a6fc9f7013463a8a6c2a2bb42c44b23ac28768dcc92466910113870888e9ab793e66c5eebde56eb4abd8bbddab109246f1d566ee1c90077135cb63181a482f622d3114834db01a960bc41715247ea5a9622f1cf8454e0f5373e8002a9ee5dc98fc69854bbe25b9c8ca19060d160541ba888f33af78e015aabbb53df6aaf392400d570c3992e0550b388fefe4f3f0bf794ecfd965f511d696df61966011d8115f4cf6b06c2fbf3c777ace498393c3ccf673dc06f11177cc453e8bb635ab15b3a29f990b443769ebab426898f44d15aa3c306aa6f40c42216089175c0f5bc4415e26f0761fec74a9c75c4c345c55ed93c3be8a0f3638773df18b1ee570bb885493e699c24943062194ba2efa33a8add0edb308182ec73d4aff85739640f6cfd464b5183da57489829bccb730af7446b417a41141f46138754e3c5aa373c8c926ab0a9552f04a7c718af3c1b54152f4f65f42e7f2b8808287ac543afb183a071b1a031ffa490c13268ccd577ce0e73ad848003074917cb18d13e8651a81b2faae22c460b54f8ad7236271cb3215b393f32988a769867644f13a337e55912b33eb188428011efce6b4bc77727e8a3921a8c309286b635f7b49f7a2cdcb43f26117ca32269222a5103a7f0afda4750d94518f43fc34e6574e3f8b0f1c54623728d1e796a59b84f8e9333256dab20a090918cd01997a5f29116248d338c96bc42bc43600e0a5192287ada1776056774b9d9c2a7d578b20877ea4c7892b491aef080382eac0046e6f37f9ad33c821d36805217640765b8df8a2f5d951a1c30216e929860b2c92145beb9ac13f70b3fed5ebc0da4df2cae3a4002bb721f0513554a92f70bb6d13a6e4c534b27eeb93c7d15e7dbc2b169702c1f5beafcb341d6baecc999ce3e8fc424c8d43d415cddbf89e42f22f588fc3dd69d306858fb2efb55373ae89355cbe7eaaf4fa9aac529b4dd679333e9c7c382bbdd2b35da8d107ba486eeb719d123e687964bd048022016abce276c275f757ccc16631d8b348e44f68afd45ee64035d7d6ac59681aeef15862e6a210a7600b9096cf7012a3fd3ac3e22a5855b3c205aa0e5598ed072cbc6d3f8122480207d1c680fedcdc2d2da5f18d0f302bc3789bb28a7329f720221f8630894443ed91b400a0809882b49fd78d97a9535866c68637f2926a522595fe28ffbabcd8100a52ca8b89ed25d014f17f773a7fa764104c1df9d594e5d62c3409e33586e288d39bdae43fb4ca36152c8a97a5b322c458cc1b9c54917b570bd391b252e44dae35ed5d7ac1cb703eb93194b1b4fe0cb4bb801e49eb5e0a4744f432aa49c2e17138053ef132915ed5976ac554f193364e96c5443ed46f80a525c1eef510a6eba201b3ab2a0678a5799ab3c4656b053b232b46d2a3e4c8a3a3e55ea7e35bd96cf64fd6ac2cc7e69ce660802c9c4e0f3648a88ef8775b7bf785227773e58afd617e1f256515585d4bbad260aece574b646e943a86e6cb6de7d9d192a385de9e7428086ee2f0b9fe2903848446bf0260eae98916ace9e3c2ef6d76a9d086a06654f1e115bf4964eb4d8aef01b244b3f0bdc8b5138552393b5486268c4d0d0364fd0eef30d38cae9d2e012f471311f6b871b29d31fb12edecf89267e8949fb92bf3a92fe6b4be923df31d2a0dfa7adbd8ef6a304a39491582faf0f2a80780829f4c0f7faf241e65928ffaa87a58dbd9ca8e5e84def9f435f5103a696f4c13d2adbe5ce5a99749fd742ab4b28160bb9ead8ad49e53f0286dda8e6345835217578a5dcaf708b0a5aa7a5856839ab22b81e50eb9b4be7191176292ff71be5695cf7c9ba280d43ccb3aa914c88cecbfd2c0fc8a83bc779ec9d25cdd6dcc4d2d814529b16bc6610bed18941120be210b436a3d74c45dd0359f5a22191804ecf73d5113184ae519bd7c69f666dc983a8bfaef062a16f78b2404e251f27a374c4133770bf2c59a7ba4743f0568823c18194c3d3f2865aa91bb46a695cd4010b5779c563aa603769de28bab70a19869f5703de30192ca3f57e95728e453891b42fda527b3e8f9cd0ad5b3263b531ff2621f41f03a035c4b8da8f9b34b78bc2eb26d8e62bc4351ea6fc339b9002b2566f3f6f1d6f5721718f135ebdb78db26984433b39fdc5bc0b1ebceb1f3ee194794ea450705544f194b05429c03a4c19d5ac6350bfe735636cee3bfb43276383a6a3713582905bb2267d503112acb5e4a8a7bd1d079bc9e8c375791a54287c6682180a28886805ceca29914e0b5cc47f68f6604571cc733b49b09fd0d85e70889983179353c876bc0d9dd9118e8618b89540c2016e84074394c5ca07c622e0cc4dae61a988edb6a96f021526fe216df3a24dfade7daa16720d49d927544dfa951d467648a0d31142f2a1387c65e9ade18324a8c10a447f383dfeea15e32667f429acd97b195e57cae1d425d0efe7ac179ac3f6f22a6ed38e28d2f1a4796d15ca80331c45446e46ee093e572b116b1a2930e0b83a01ddaae91fdddafc829ce96053482cf1f9c8f369c1aeb24d434feef0bffac6ac7a65163ceb87f13b83e93af7a07a3a20ec67cc1db15b8e7e5a952a0a199a65ae605e762a0cbff1f0095558000cbeb37608e04813cd027800f7fd0eb1bfb73980a12e45b43e5634ebf6b0e22db813181963c2b70e8731cab146018d67a37fec748a1c839ec950698cbc20d2a187613eef11372c430b0eb2672d93f9bd911f8776fb240372107fc0e6b03f3479d6b202308254f75c5010a3cfc975de7be4ecb94c59d258c0938e78ff1ccb5777642ce456b16de6eeb1f03f383a024d0b69edc010ebd98ab1719c1c69c14570cfd91b57bac61df52e3844865c0ed64217d5d7ade1a8efcf7e57e76612e31b84a1c987e4976b1b094ddf9d734c8bc7d57a1482237f6d37588f396e0ae5b0f880ed17e1efe6b01a9adaf5c5168c703b75fde2c753214cb19debc3ae8e2a0f5d47817a5809821eb77fe8b9201eafa79607c18550faec73cd9c54328aee973fd99188006308aad5e51fda659380f52642c9b7d91c63d701992624fdc22c0ed8aa84330b15b56f05e6cfaa1b1a827c3971047b48447e458bdb1ff0b88528068faafa7bd59b4810df00f2ce9ca1cecb4274bf9c7036f874fdf590410cf0e07b2d39fd78fefb7f0c15cf8e655d6c9f07853484ecb8272d53dbb9b17ab47bf7055a1bcf9bb9ca9748fea183fab387c31a8b2f38131dd3fad565dc900277126bdaa9086c5f5d4c204db5ba31c21f9064cbfb91fd07c57149efdbea53474cc9b25673f2bcfe26eefe3dddc4013a645f3ff9cd5d101ab00ed3195484a9c3adee6a90fa8505f530fe0c978f939fa6291bd9a0952986786132930b5fc9a19ec5931da04c90f75b09b242f3b566365e02e5d831e62dc95083c6108210eb220b6156ce04df61396a31143d503fceb6d77f8b4086a303b59ece70c415cd7b10db690d0b8bd191711039fbba8626d3eeedf2308620039e5bb9320f1bea3e24b53124d08dc47b24a278af89505c37b4041772d7a1277dbba1a1f7eb700a29986e58b6c7f95b2758d50b4ef6163e8a08fcbc8866e2249954d860bb4141926c0113937a736e735abfa80ec520047c2a5c38e0131ae1354f8884504c17997a200e7bc5509890809753086233a9c8fb5cc2d904378b02ed59c0fe25ece7ffe916687573d680c8003f3d2ce93608c89a374ab2e108334278673c9cdbcddee0b5875f11f78e902dbcbc19ca500046cafe276917803c38e0a868210a0bde2c31af2a0df66fcc0dca892a671be13272acc19caac617d178e33e3f545e1b3184358901dcb97a850eb550e3eb852ff6d7f3e3cc4c63cafc88bcb222d952f76133e82e54c3282077e0c8c5f994265b1d3bbaf1cf5eff1ac4a5c6b30745dc7b94136051f55dd901d99fe57ad202efacb6ad5a75f1f8428973384332cc2bd31e50cc3ca119a9f8b542b16c72ce94cfaefc045120935fd0af35493fb1e8195b1945bd940172ba9d45d1f4960f6f4d01824ccc23af4aaf73d832f0a016b51c40c18961fca4b40d3c8e80", 0xfd5}, {&(0x7f00000007c0)="3656fb439d40b2c8c984fa90405f6b180a773406967d5c49355b0773f7745f6102000000000000007e45cea3b4c7d8d9d5269127a081ed7eb937b33758d1b11065da17e06a0e4871635da4f35cd318b8211c08b33a5fb3c7cecac6384360cca8b0a52493a49f87b48d0c26d483d535e28021c4cc25ab21832f95e72148f47b384a10213cfe80fe4608866aa9b0fed07426bd4d5dab20ea8a20e21f65dcbe9815a508c6c61c813ba469e2db64f0242d8d1a04c543728b1c9e0565e7d15dd74992e0460e516b", 0xc5}, {&(0x7f0000001f40)="5b6ebeff33593511d99197824fdfe6646234c9172c5a94d94e83cb120a75808ac485dfde181b8cf62990d8211c89076052fa456f59c8f4b9a4971f591d2b631e2b007f753f191249330f4f3d7e28e2bb4940794b6b3cacc1c2427ec4c35d784b9af3b35317fae848f15a21eb7041a69157f45d5a27146ff4891552240d9fcbc8a0a962f53d8223aae84b4b0ab56712aef214096d9a1186c2d3ba733d1e2b531aa73d7bcf219cd3bb865222b5aa4fa2ef6b5dae1629ae0c4cb1f45213cac0d4936a44597e339ac5147429b954dc0d556f513c12eaf8d8966ad85666dadfe27b1777386d34000000000000000000c1a5dd34f012f341a5e330650dacca59d5f1824798ba3028c142f0829bf290930050508c2f70bf684389c179c788def0fba76de3edbb241d521b169f365065314f1541cda0f5c8c0778b0ecbc5824126db46c6d977871a47e28f4e4dae00e2ffc068c974207173f81ebda1188f35607623fe1980f6eaaf64a9702fbcba01d4902e53f99dc1b21d469c7bfb2809bb5d3e76474c787f5b694abfb958ee212e55302fe38bb57703d9416670ff2100259dfa865b00", 0x1a0}, {&(0x7f0000000540)="70dda14d60338e1128994c25f29d429c2f684e4060fd1ebe1948b4e53bbb6f0dc6e521edc91e30dbcc5bd58b61373f3d23dca0c3bf62863e1ac4f015e78657043a8da44893b5cb63fa7ecdb0c2b26f9e47a1a70429", 0x55}], 0x5)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x4375, 0x9, {[0x8000010001, 0x3f7, 0xbfffffff, 0x6, 0x800000000000005, 0x7fffffffffffffff, 0x7fffffff, 0x9, 0x0, 0xc00020000000801a, 0x3ff, 0x8, 0x100000001, 0x1, 0x8, 0x0, 0x1000000000002], [0x80000001, 0x100000006, 0x8001, 0x800010000004001, 0x9, 0x4, 0xb7, 0x100000000000004, 0x4, 0x59f6], [0x40000000, 0xe77, 0x8000000000000005, 0x800000ab5, 0xf7fffffffffffffd, 0x800, 0x40000000], [0x2, 0x0, 0x40000fd7, 0xfff, 0x4e8], [{0x9, 0x5, 0x0, 0x6a17}, {0x0, 0xfffffffb, 0x0, 0xffffffffffffffff}, {0xcbc8, 0x3, 0x8, 0x9}, {0xd4df, 0x4, 0x3, 0xfffffffffffff8be}, {0x0, 0x5, 0x401, 0x409e}, {0x1ff, 0xd, 0x5}, {0x2, 0x3ff, 0x4, 0x2}, {0x808, 0x100, 0x6, 0x1ffffffffffc}], {0x0, 0x80000003, 0x2, 0x8}, {0x0, 0x2000000c, 0x4, 0x2020001}}})
sysctl$hw(&(0x7f0000000100)={0x6, 0x12}, 0x2, &(0x7f0000000b40)="5cd73027195802b41350e0485d9953385d15e05e41f3a45621eca11683343a3a7aa23c260a86d3d52b20cb00b999d96e1d43a83d863ed07d98374452252e5ca853f3b76c0d1dad4f8ecbfb72cbde7df445dad18070a78285cc86487af02e4076e1d59b20a7dffe780fa9f8cfad5ca84942a26841be2826a17fce121325074f451c89e1e37c77bd85027b20d74120b2b91cd04532384d25db6f1dc2476dc1a2d5fbac083242c610ea0bc97788eaaae119df56aec9bedadf14ba0d468494c16257cbfcb4120be9c2c41ac6c29eb23d7a29a549296bd947811cac8812bc1817c55df5821ca208bcf297ccc2af204fa0ffaf673d6648b085cced8b61ed5fbf7bda2868b173319080aa15c5", &(0x7f0000000040)=0x109, &(0x7f0000000c80)="239bf3aedd4131c766b871deffd6bf326389da9cf5e19469a50c20c9316dd0f891c5e70d91cb82e6c74bd8bc67421b5b0a2e3fecd898d0e0f7efb64c16547d68be7201e2e9d83ab2b6cc8ca5402193c30c7cf0631663e5a5ff0d838ba68822d261b222d2b92c8dcd99571748c9a62a64cb2b3d74a659745970db95f142315986bab12599eb4e864d62b02384eb1086a34d926ebd50c6d6c0fae5904fe1d3244d28f3383a4ec1483dd6b435cd5019377400f0f90e29df44707b23fe5db0f01d385db9b706d7adb4cd1045c9b7b78f60d3fd33585284cd11e57ff5840a2e6a376d6387b864bbe8d1ecdd27b6c0679c3f3379f66a313b60bd489af64d09200120f589d80e9bee41bc39542108e4e38aa1ee297c0de6ec8e7fbb9026da1d911446898849725ae66b52088d7561a5d53301015fd426c2d233dbba02790c8bd426c690781cbbe98491b29c0e96519522117f3855819aad477f06c9dfc6f8dd1c15486b80a2f656086173066f35d2356044e5c81427810be6da3cbc34bfbf8c56afd29dc3d1937cfc635a0cb5e2ec685408714b6d698c4208c4aff6c00454a4753f808000885217928cdd6cf3130d535dc6a1657b5cb8d9b22acf53689556c93255ff78e9865cfdd78a6c8d73e003e967412ea810262e125a251345013a8c9a6c7c9ca3b40803647c6df6994a517bc463c5d94cfb69576813fc69090af4c8b537a84119363dd1e2e4a57e7a27e18f52367f78a748c73f7c61764a09875112e7c77c9857b40b4502ec9a2b361c6cc89ec6251f2f5ac02cb5ec6d4b69bb741bb4cc94693952573b7d662cc76ff37aa4978f600bcc62826824e7e0432d126119c611ec1ca2d4482f77ee78476d8250a5f8b3dc47acf55b3eeb389ccb191390fe03a2303c768efb3413d669c104080d7c27cf710adccb03bd6c158ad60a8f78b52c67d2702fbd6e4b751a427666c34ca995c508038dfcf1ec687a7732dd79779fb4dc7f9902179d2f71053ff869ab80dd1f6f5cb9caa327fa03574be4dc6909d1461bc1c7e95935bf1961e17276f624bf78aaf31fe93522c5532a522ef07edba15c978813013f33a4a5295b3940be3fc118cf6e1c42092f4cfac22815f99d263e3f4253b7d561ffcd172916445b4f5108ca76284f968a0c5dc9d0fd7fab2ba176f609fe9383b61324e79dbe5a2890723483510cc7e22fbc8877de237562654084c498b05695a5711d5765a419984f1b46fd50b2db461fe22e1c056d33d8935d6e360dfa66d0f667b524578ba5588f7436d336a4319c4bb4bd0746b223c47e1499504d960282f987ed41fdc727e5a0e10ed73de60bc5b0581b93f5210c562dbb2ac6aa0e74842ff348f5c9ee7ea2124bacfff8c148ae2999a9154c42fc2d286b0c33ce42f97d9b37a3f667b1589abf5572264e8c0c3553e677c8de10d7327a4bc834b82b742072873a58e008bf21048c2fff2339ed8e5ef70d57d0f9afa2d51408f36ecee606c8662d23852eb689d9a110a68071fbc7fb8f12283c3d5b4696dcec754612cb049e8e70d936c696cafda665dabce24a8fed0e7b38e406c8641d59276b2b9225fd20009f6d1d5c9e9d43f493ba4310579e5ce5ee7fceb0ada1e962030e79e9098f5164e41bb631a34f445bfb345c2c29c190221ca292a3f460ccab290268a0dd84f3873224340d21173f22ab28b15862640562acd42241f43947baa71fc35989e00ee0cf0a0b3d8e8ea6d3b4dda3f2bb69df6edf809194079132114f4e2f3438d6a95eac807e0e47043aa492dca5d91ec58159a3b8813ebce7127481daf9bebbcaa39f5c920a55779a7f24e08d3cbcb971115ff0b5cf13e63899a3400b7fbc3b042c8d691296c8625b22b234784f28988a2dc36a856f690b1f93abe72b67485b55fdbba97309afb3526a5554976866fc012664e29b2c64b715a59e03fd62cfe389c292b3a5931d4686a3de71f6da48d42f2d6dfb629533553bc48233d50e0cfcf3f22c7936c97f949baf2eb41836dccff0bb520533efb0e0d4b40ab960eccc84d3012e7a0e2f820d417cfe1048aa708657c3e644d907ee07d623b912793cd943558db7f8d19ea0a9c23dfcd5773e1522eb150ac1d809c2b1d19593360b36ed960569e486f261d0838b6766f232a2d2c519d3f5e072a75c184c6e9ba17fa8235dc8204bbfc105846c22bf3252b416aba556a9ea8b9cb140e8645954b9a34db5f88445a239cdc76a659dd6a3b65493f68bc62fe603ba49a534c1d0b9d136c803ca7505d135b7747dae3c75848784e1e22e9d32890cc2dc866f8961bcc3a4eb86bba1d49e26364f5e4281d0e9849f43c01c9aacd2dfc4c14e9d965b9081904cbc2fddd429b44709c2e248b0523955f159a2d8df67738c02cd1d230c89647baf00d97656950ee77d7e9def50eceec02850e9ddfa6c5a0c342da30ee1e91b6ce647b37bea7e2a5431a64bedc4a287c326dec3a5599b31e1db2df10da658b2061b8e597de74a060001ca52d4ed3825daff97b9412e6c4d26d0e5c40abd8b9a0053ba12e7c5fca739d40c617af943bee374e26a17703f0f194ae5934cf2c93d8f43a552780b878d7d571a205743d9e64eabdfe809d394d26d059e14c34a5622c49b983030590b8cdcd90f278e65893b0ff3af163ef7e0cbf4733f214d98088edcfb7c62527ec0c6c87c4d97ce79168cd68a241f5ee2e01de371538bcfebe27f4c019ee3cfcf2422085d4b7c6000e000eb9b6c50dac50090fca35fc09ef2245ce90844c3f82d76d0d98803f48206a8b6c932c5a6787953f7decb07412fa3f5800c07aff4c84ff147a2e1635b1009437f4ec2220b36afc08c253bea4475eadc389acf23d1b5d2d2d2fb6070c9a13b5b71e9e3eb7f14d0ed7d569ee29e06dea79912a6846abb70a97b7ca9dd274009cdd34570da11d5ce09d5af27dce384330bbdfbbcb738f8cf6811adb9f3a7d148c8417ca465916782cc5f973f09ebb5ea7ae51c3c42e552ee5987333e89297b293c88c49761b104253a7e3438f15c962a0709e3335078368ab6a1d079df61e180eabca587a1d30b3c095d770964b2f02232bada8c83a865011bade54ed6f16173a13712428c6512b5d2df6cae920c4d1ad117c8ef66e156acfbb8057a519b35227aa83ecc36e1f6813939ad5ff78c553fc94ca250ee667b17a089463c0d8812182795c3cd244aa09a2fb508f17ef9b6a18bd3cb0bf1ffe05f630896e81d33f5c823312af372fcb01c455fd4c87e93d62aa0d621e9bd61d5a9ded4fad53cd2eff3cd68c1638f626528cab5afaa4ceb0829243ab98c2455fc54f95e075572caa727bbcee4d97f5d91930149ecdbaa014e29fbcf5d0bfee379b1e9ba616292ded9ee1054e4b4f58314be3b7f58190294e707c43ddc7fa9497600cd7352419a6b55d9e8ea7dc59d67e97699cfebb60d2b54aabe7aff4ada19de93ca87b59229bcf5ca088165b9758d7cf9d489b283639a3bb79566d3aa73bcbf1e42afb87bd52072d655e10a7805a4d93677f02b449b9f343eb156a4ba2e895fb64802b5fbf6c6056ccba0d7c6704d495b3483fb9ac7dd74f498749662b0f319c5bc3713536edffa96a1766f375bcbc5e0f060abf6a8bc2519240d3a6eb76a2927ed1fe8d7bd00997ee06a08e68f36da29a1bc9f01176b75e573d96f705757b1566f6d9713055df54ba873fcdc985a04bde01e9af19ae8b4835f569e07aa35306366fcaa5da54b3da99be4b2488b13b3662e4059440199bc808722fa691eb1f3bd39b8283540439db4db09adb7907f1880d67c9b2f241e9f6096e3a735fd953751874789df5dde741cec3b1f65dc419caaf362c7be7edfa670af25bda89fc77bd58107c73caddfc478405afd345c77e2f99c76e8a9d76b4a79e038155af13c2f90d7486a0ba67c201ffb56de458d39c66914c00b1aade7e93827a62c5a6fd54e64081ad2d28c2850152cabb623c7c3a9eba429e5a560bbf9ccae6a65d519b177c098f5dbfd91dede9fc262d97050adec63609a7ee063123f63cb82baf74d0a4d7bb3e8f9ad8a29849c7a37a693baf8f2e10c775e150dbbfe801f747eeddb78ba68c67707b82590b0d8b4894910035b5176c33ac528f07b76dd7fbdce70aa61bd923a29159516f08c8ae848858acb84d9be72e9627521486cc2ef9596d20c8dd311deded20ef9cb455a2f7abc27938e29399b742234e0ab0870eb4a461d4855f2866555c4599c57cdb6c494dab32bb386b755d920cad094e689b51dd9a77ed378af6defb782152c4533635efeb2c2dab2c860c2fe7736fa27de99faa1b85e6bcd90f0c95235d6a3b2f1b98d60a2e8eccf22edbb9e356cc8e0e57cff562ae087e1eb9b5ca4bbc6d2924fadb8e26a7af3e7efb1c43a769dc61c90f6661d07f9af643565159575a6697f0b03a2492a44966582feae83c08aff1e1d1011995787d4bdaf181fcbccf50752a0f68eacdab9a159aac9726daf744baad5e5eef7017b2d833e95ab1282288af9564888a45da6858a5e4eda2b2c018bd5ff6458b9dce27dda482b09fc68d217941535eb712e2c114bb43079d06df7ae4fa5b0cd82675cc4f2903545d1c87f1e55a4549e93d130fbbb21c6be99c5f259d854d9bffdf9641f70c0707e0f610d6e49c29363d46a2696534f2c081fdca1b48ddc170a94fc48fa2c3a10e9a25c56ab171419b85f7dfafb16c0f3b788d1157c1362177735ff6a64c2502ab5f74b143141203f7934b2a2c3807e6183067b27114c7288c312d4a6a983bf381bcfe0f3bde517e6d5abaa41bf1978f57130435d8fa1a6b130fdb90092fb6e9cd08010e9db144ac8c734611a30c061d8fec42c88eed3000a5c64c68c3161f1a0a46fff8e53eb6c38be5f47ae6890e3d9148b769568edc4d9027ff168f3003d464887b55686284f360aec74d4d55ee6c0891f0924d198b389b4d94da7e67a122c3731f5fda886042ffade86c3ae3ab5cafa6dc243f6fcd461230f3d32ed8df64adc53afb67ee3c19d5e4ac97d3edde3a9d3f431a97a3139572935e0f8f9a7322efe1719a7e60ebc57d432278a7233c7ae331d83085e1b035e60d6a8d3ec817b15b8102428e333d8f7f12a61b2f01b172a4b80c8e0fbbaf73d1920b3861a73ed6e19051ba86d44afd08775d5ae5972e482379306db1b9b7f12888e0a160f500a4148a706e610815b3311d2e5a5cfb5e8293a06edd8c9c0914a1ff115e8fc1b5bbe7db98bff1a3e81b9edc6deb9732112b6da1b0dc6abe3e2d256c659f7139df5f96e8931df1473f167232dc388ce75ffecca0912b474a33860cf627349928477058c56141177718f42358f92ee0091438499e6a541a77e6bf326fb59b346eccf61dd7d964d3c4c445d0581b5c3bd4cdcd17508553b8b7d471a573b2c46578609eca53bc6483d8f6624c85862f243aed8abfe6b0fc7f78cbf89da3b8eeb7d02680d80fb9da15d0270a1f23107a38f0e483aaf7e037e01380c97f5ea8f8022b08c9639ac8e362abd4d5a11834749d96a9ff68df5a6cf35495bba6583becfdb9382cd2493e9b1f230b538720688052ccf44c1ca19bc83fa7299d269103c496720e09e21861a1f1aea000a7fd293d8687b7ddec4f9e19c4b8cc744ccc26d873d33592e85bb0ecaacc2cab77d010719118b936729b1e7c181bb242164dc2d56833c12d06ddaf2edc9be6c0095317ca309f45d75cab9de03e4777ceba6fb1bc44ff231e7f49975f82c14b72a536eaac2442ac5e4ec5312fe3a012647ddf7e9a9c3fa9324592b5edb63dbb35206cc1001a43e47920caf5f97e1000000000000000000", 0x1000)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(0xffffffffffffff9c, 0x1029, 0x0, &(0x7f00000000c0)="03000000", 0x4)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x0, 0x0)
shmctl$IPC_SET(0x0, 0x1, &(0x7f00000005c0)={{0x8, 0x0, 0x0, 0x0, 0x0, 0x18e, 0x200}, 0x40000e6, 0x1ff, 0x0, 0x0, 0xfffffffffffffffe, 0x7fffffff, 0x80000001})
setsockopt(r0, 0x29, 0xe, &(0x7f00000004c0)="0b652aba822bd43b69b3fe462a9ea20ff230f3db5d9a56bccfb9fe46ca049c072af64ebeac62c0727d06d28ab2f9190fc533bf17198e0d031c37ec589746a018271520", 0x43)
r1 = accept$inet(0xffffffffffffffff, &(0x7f0000000a00), &(0x7f0000000a40)=0xc)
setsockopt$sock_int(r1, 0xffff, 0x10, &(0x7f0000000a80)=0x6, 0x14)
sendto$inet(0xffffffffffffffff, &(0x7f0000000640)="21bdb4ba44bbf9c04c744a5ef9066b302690d1104db522da39b24d46cc344770f4c3b47fb53aa2f35086a413d8c17be3326ff12848868052a842fbc02ef124f6c3dc1f1ed6bf82f8bb4935f7986626849c93a1c777bb8b53af3ca1d33a7776225fe6b41321b62ea8b5498dd873cc514cfd4b611b0cc2d4492e70c26abcf57b874518afc8b874d59634694ea02424beed4052de1ec2c2bda8f8aac7ae24", 0x9d, 0x400, &(0x7f00000001c0)={0x2, 0x1}, 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000002380)=ANY=[@ANYBLOB="01002a7747658f65320002883a4041b84ef89cf48bd82a1ea309c8272fee7b29cc6ac50a04131f588eb8899e75fc800929ba60982a7c000000000000000500000000000000f3d800ef5d203052e2ba335dfca4e154784505a4d39028326ee2492ef9a93299a62f01d744991a8ed5089e3b3229dd941292dcac6bca7dd17dea13f1e4bce7602b6c96e471da5c38d2601942a8b483f89d23564cca8d549c1af895878ee102a67df68689261f0832e708eebd23f072d84359a372f5bbd6c5b2b1b6a56c1099e97c6173176a6410f0c6ac2bfcecdbd3086227d1ffece991f39e86f01f2b3059a08fe2e843659c47624209073e6337b0390b3ebd7240c379927f2b7efd09000000000000a45040c29e56db6036594faaf8ecc645b6cdacd8076b983fa5c1c89022bab7e940b446434973e9368f572f3b36f3fd55861bc216bdfe090000007ebb1916b2b8f2790a081ad00b55304f0589fa0548871416de6dc6b4f6cb19f32af08fe3c38b6a7b5c9be4c33adfc516a38229b42044a62e26f07b0f0afd2acc453a5f83aa23ebea5d0f207da075318e613f4622a724639caf7284a533e59d394c4e5a5a6e39694e6298d55c320f71a279a0f4954885eebd621d2557c28b167eb63edd163e6e34225a099256017a8d888d99a332c2a19ac30a8bb96c52d887c67e5b2abaa60152565ea5dcab361b7c09172266b5f800ab904e8c755fd11aa8fee63da52d7a49c147881e3618711cb3cb3979588dfe26b4a231f408b67032bf5a58ae00990e0914a0f955fefc515665572750dcca2423b08728aa3e7024f6f598efe0965bf3a824bdcb5158fb2570b1c9c65f3970a9ec9d0324c61c29393af91c3cbbfd802a78071ad5918ce134b9c75eba3ad9655116940d69b9666103855aac0aeed7132f3676789818bc34895400b16bbf3e42482b2ea8b67ae70d16706341d34f0a7a3ed068b33c9376f0ffd26f89d63334bef5e59a92c1fb3d6f3ee2c65c272f5813c0d0b773c7c676bd8b3ab3b4a18b2c4deba4a0eeeee20d6cd74eba0bbb96bf6ac08dd65ce525840615568f84afb27dd1294608e9e1d5b71c950f0c58251117dc5032d115f152f72dafa852d11fa1d4987820ea92f720a5fcbdc5af1877e76e57feba3dd2302b0ecf01cec2ab304f57cafb7ff71ed2d5460996a494e5a61a706a70048a4d858d73cd7324"], 0xa)
ioctl$SPKRTONE(0xffffffffffffffff, 0x80085301, &(0x7f0000000780)={0x8})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ktrace(&(0x7f0000000140)='./file0\x00', 0x5, 0x200, 0x0)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80286987, &(0x7f0000000300)=0x81)
ioctl$TIOCFLUSH(r3, 0x80047410, &(0x7f0000000180)=0x400)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a21", 0xbe}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
setitimer(0x0, &(0x7f0000000000)={{}, {0x0, 0x1c616}}, 0x0)
getitimer(0x0, &(0x7f0000001680))
openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
r0 = msgget$private(0x0, 0x2)
msgsnd(r0, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2c9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r0, &(0x7f00000002c0)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da6461341bbefbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6ac1571170e268ce1d0b4bbd8bf45c5fd340a61305979b0bf685e45f57392649d8248976549ce08056e03c959080cbf50312d6635b3b58174bd552e9c513f2acc71bb2c97c8a895fc07478c415c6aa3db3cd6b47f2e874c2c9c03886179802e5606fb276950cca74cf527bf968ceba0e8125af4bd5000000000000000000000000000000c6bd6dd52135696ea9f082ad0938b93df3eca8aad08910b7e8ee4403738cb1dba0c104f09ad91582087e0eef0e43c90e92359f8ede8b87f64c5eec79c4594886691ad2cac8b2e1796506fade88f319a97d9a40c749f3a53994efba60bcc277b0a17c6e5bee5a8698f73b55972dc1c52a0b69bbbf91228a81ebf27c3732ced1949ff053da397baae03e894a58defd824bac1ac4ea092b2d476d374f122423d47361c20a5575804aeaaf479fe73daebf5309cb45fa4eb8bfe4165315a5f49b632e69ac567667569d953be14879773cc3c78f2fdfec298216a3db9e99db5fba149b69d0a649ac39c238a4fd3088e1913b38a6c55b9ae4830b12d87c0377e0adaf3cf10b93e21c02ad1f444a48b3244fd5fb92e8d7c30659d340dc22b277dabe7d9e656b87da46b865b2ca5077fabb5581713793bdd72e1423112ffdd863a6ab56"], 0x6d, 0x0)
msgrcv(r0, &(0x7f0000000580)={0x0, ""/18}, 0x1a, 0x1, 0x3000)
r1 = getpgrp()
msgctl$IPC_SET(r0, 0x1, &(0x7f0000000840)={{0x0, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0x23d}, 0x8, 0x0, r1, 0x0, 0x0, 0x0, 0x0, 0x21})
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x2, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$WSKBDIO_SETMAP(0xffffffffffffff9c, 0x8010570e, &(0x7f0000000000)={0x7, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {}, {0x0, 0x0, 0x0, 0x0, 0x2020}]})
pledge(0x0, &(0x7f00000000c0)='vmm ')
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffff800, 0x0, 0x7fff, 0x0, "6fc6e23c5b00000000000000e74de400"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0x9, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffffffffffffc, 0x0, 0x0, 0x3, "905f4a3cf90700042f2800"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r0, 0x0, 0x1f, &(0x7f0000000000)="caf5b415", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000200)={0x4, &(0x7f0000000040)=[{0x10001, 0x0, 0x0, 0x5}, {0x28}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0xe, &(0x7f0000000100)={@remote})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x100000000000000, 0x10000000000001}})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
close(r0)
r2 = socket(0x18, 0x2, 0x0)
close(r2)
dup(r1)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f0000000080)="0100", 0x2, 0x402, 0x0, 0x0)
write(r0, &(0x7f0000000240)="c9e4e80d0c4033a4a8b85250b7cca32f0ca382d124000aa8a3aaa14d532bc12f6b090a0244b3b5a7c7a6", 0x2a)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x45}, {0x87}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
writev(r1, &(0x7f0000001380)=[{&(0x7f0000000040)="b79c75797734235fd849fd8f1bef9a8aeeeb95eab08598c2a2b3608a37b1e72a201060b04aa8f950a6d812005c5c3c78fc9eebb253667b045e2918990ee8010a6d0e195bf2f0f89227f4b47266febc685deaf2b22cec68585fd9ba563c93777d52eb6df23540f42cc44b9693e24a90521219bfe337cd6fb55d1d089b1f18bbb7ae535d57837e287524f8bf70efdaf833bca8ad5a5bfffc69e57f95c40b5aef0a19359fcc4e33b34d9024dac9736f6100ed48e75f91691805220c33284599904d097d", 0xc2}, {&(0x7f0000000140)="d410a0754c950130bbaaf4f25552827c3b3f958c0b1956e1bb3bae5c0666cc", 0xb51a}, {&(0x7f0000000180)="c8f4c597faa17dc0cd31322ede2b10c2eb72fafc513eb25854c5a6c2add51602cc05626a54dda827b113ab2fe86e8a4eb9400b48f2f8a38f2d3bb0b5f28c91ff95a32b1c3f77f5628a7c2ab74c9b92bf3ce54c443643486d515ad0df78433acee4694034e7cbb4a792fc38701d858945611ce67fe1bf28c11d4643e0e4282d7debfec4e5bad2f0da9be9f2cbe11a14beb3daaa9b33223e8c76a1f06a36c059eecb5c68e59a4d45b268ad528702447018c03c8cbc2d4414c486018f8fa11e28c2743d35b91ae2b6e6bb3fe85a650b132ca8d7fdc4fb3e279978d602d81764fd7f6da40234504866737072a4bc312c50d669b53b4ed847a00965f4150ecd0e0e284aa1bf619fec83cf5a9ea49a5a6aab89828f7ef2bcbc5fcdbf5630946ecaa35caced2f00f89d61a970df49e6c2055da684687230ed0eff0853f64a624ba6a1190520000aa9ac2a52fe75ba6314c4faa5796c904f46c389cf745f80b2c49f53ed11f7ebf164f6c3a23826ea3f4e37fee21441b0b62a2483765f2f72b1e751a9e91eab19769bd159b3af08dc2d9169c0f01f9e5f4e1c6bb91afbab49a216891365658cecabf88f5eb789148da3bd9c3c5d4cf57401f3824f4f2077c6ad79f23d0240b5f1ff232c017c2271f341bb8d8680224ef88bf5ad5a3438a6addb8fa6003ff0f36e90a645ce79ac9195e50fe4d4b3dd8bad9d5468041d21c3cffc40af55c5fa6175ab3db240d4c60da2706acc0b780dc936836cf9dc8de9903e70e88d1df91aa66a47652c1e60bb083d9025d7e3a87eb8215745922dafb2eed373059615fe1bd1fe7361a8b9f3faa849ccdf87e537a5103a57ec65e8d8af05dc86f4bb97c29a3f038af1c4845700", 0x269}], 0x3)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
pipe2(&(0x7f0000001500)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
write(r1, &(0x7f0000000040)="33ed63f92b28fa1203ff350e64b58f8b4e80d5005c32471d446803f1304bd1a6bb49f01d681cb80a1666bfabe477a47e8bb91f881bb6c706bdd2f28f5d36d7e97a76ee617205de86088861b00089a6abf6d2f96871c97ff9643be1c7c4fc90856167e5895a8ba46744731b726770f9cf523801da3053bc743643fa3b8de3ae3b3924cdf5d61da2347964931370c9287fbd5e0b73ffe0c683bc484e71a361ce1f96783d459787c30b59d80e61770b4bc5cf1f77bb554f0fcb", 0xb8)
read(r0, &(0x7f0000001100)=""/184, 0xb8)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x5308)
ktrace(&(0x7f0000000080)='./bus\x00', 0x1, 0x800, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x1, 0x10, r0, 0x0)
mmap(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000400), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000100)={0x2, 0x0, 0x0, 0x100000000066b})
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x100000000000a})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x40}, {0x80}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x33}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x1, &(0x7f0000000100), 0x10016)
mknod(&(0x7f0000000080)='./file1\x00', 0x2000, 0x800)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000200)={&(0x7f00000000c0)='./file1\x00', 0x1, 0x0})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000100)={0x0, 0x0, 0xe970, 0x0, "7e00000000800000000000000000000200006ba3"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000001c0)="f89176d2829b093eb403caa8d01cf66722f5e90172fd9b69c9821106627301330a521ddcddd26de2068d1143117b3d177333b20a3ab634390dfa6f9fe3577791a9afd1646eecfbfc43f1e21474336d4a355ff4ae06f86e99960337db0523f39350715783d241889ff6faf9745d22e8493b456947d4fc1909468af227d719d14316dbab13b06350ce88ca2315279355a37de7626f7712c72b0350587c72a86b4f013120541c2b583827", 0xa9}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xfffffffe, "000038004000070056e9af0d76d9deee000000cc"})
dup2(r2, r0)
select(0x40, &(0x7f0000000040)={0xff}, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
close(r0)
pwritev(r1, &(0x7f0000000100)=[{&(0x7f0000000080)="4402", 0x2}], 0x1, 0x0)
socket(0x2, 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect(r0, &(0x7f0000000000), 0x10)
accept(0xffffffffffffffff, &(0x7f0000000040)=@in6, 0xffffffffffffffff)
ioctl$WSKBDIO_GETMAP(0xffffffffffffffff, 0xc010570d, &(0x7f0000000100)={0x1, &(0x7f0000000080)=[{0x0, 0x0, 0x0, 0xa6df, 0x7}]})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={0x0}, 0x10, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
poll(&(0x7f0000000180)=[{r0, 0x40}, {r1, 0x40}], 0x2, 0x0)
unlink(&(0x7f0000000100)='./file0\x00')
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
dup2(r2, r0)
syz_extract_tcp_res(0x0, 0x0, 0x0)
mknod(&(0x7f0000000100)='./file0\x00', 0x2000, 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r1 = syz_open_pts()
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000140)={&(0x7f0000000080)='./file0\x00', r1})
ioctl$TIOCGETA(r1, 0x402c7413, &(0x7f00000000c0))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000004c0)=[{&(0x7f0000000080)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797baf", 0x38}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bbc92024c8647520d6ceaca961a3525ace928f31363783402d9fae3b350409bf91adc7046aa145fbf949b0b84c6bc931c8b287508a09ff2aa2f5ae9cf504efe1dd28a25ad89128a943be72b714e7f98a895e30b5b52f60f48f6c", 0x95}], 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0d", 0xc0}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205602, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, 0x0})
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x1, 0x0, 0x0, {[0x0, 0x8]}})
sysctl$ddb(&(0x7f0000000040)={0x9, 0x3}, 0x52, &(0x7f0000000080)="a436a4227ae351523530550e522a21dbd972507b1232ac98f9d2b34984bf6ecb51667795814c286a8cffa4c5fe9435d6c21acce0aaf12de6b7856ab48d2bf0bee669354236c9e6287b947eb81a86a945ecd2d1a29b83818f2eef6a82b68161ea342cf0905ee06c71c8bfa66487f8c0763e755cd14f4c7ce1d715ca467ebf2f77cb2a12fc16913b9150ec416fa94fd92d51cb2f9f836c3af582f2f5fb7204adb79321705a72e96f5c2f0ff28557e1f3", &(0x7f0000000140)=0xaf, &(0x7f00000001c0)="ca9073947b5c202da12f8b75eb55ba30e36ca6f8f7fac8be272ee72bc464112d65aded051495b5dba7a72f9dea3035bdf44fa33c388d628378735ddeeda589e4d5ad00df10077827cf80f40a3886b7193cd7c41202a63d445794fd965803834c41", 0x61)
setsockopt(0xffffffffffffff9c, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
r0 = socket$inet6(0x18, 0x1, 0x0)
setsockopt$sock_int(r0, 0x29, 0x2b, &(0x7f00000000c0), 0x4)
setsockopt(r0, 0x7, 0x69, 0x0, 0x0)
syz_open_pts()
r1 = socket(0x2, 0xc003, 0x2)
setsockopt(r1, 0x7, 0x64, &(0x7f0000000240)="05c800002ed0d38a19fdebfb1b93ea179dd7e1e481a1e44a5c82b84e58f76d55a5c2790e97b915462a934d066b0f4a2077cbb013ffe970d4e159c6deb49de7521dd6ac07b55738384d97c9a711dbdc273d130c518a26f8496cccc73e5e2e5c8dbe453a05c3142056a63b029f839c3857cf0b8f1af60dae60512787b82bbd772e3e7f1a85b0ccdcce49f49c557a3772fcf31459f4632d4c36337a147ee948fbd5e0dff7ff6e56f608577f75eb64875300c5c1dd6ca8a7f71e28430222751ee07900c0a74aba270a7f7705798a4aa7738cff0a7d6663f0b7128e2917d49169e2a564bc6ff01974a85794d8fbe8ccf46c73e49aa4f7f4e8f11302e851656dcb5272e9b325f98e514718342f1ca3ef0e545578cd6b45d9bb9b7617120b356897749d48288ee22b351eb42dac49b3c29b9e6c28fcf64c96085252f9df4ed7ab530000000000000000", 0x146)
setsockopt(r1, 0x0, 0x68, 0x0, 0x0)
r2 = socket(0x2, 0xc003, 0x2)
setsockopt(r2, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r2, 0x0, 0x69, 0x0, 0x0)
socket$inet6(0x18, 0x8000, 0x1)
r3 = semget$private(0x0, 0x1, 0x0)
semctl$SETALL(r3, 0x0, 0x9, &(0x7f0000000180)=[0x6, 0x2004, 0x0])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000180)=[{0x35}, {0x54}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@random="a0069934374f", @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @local={0xac, 0x14, 0x0}}, @icmp=@timestamp}}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x40, &(0x7f0000000000)="9185e80c", 0x4)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, &(0x7f00000000c0)}, 0x0)
ioctl$BIOCSETIF(0xffffffffffffff9c, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206980, &(0x7f0000000300))
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
seteuid(0xffffffffffffffff)
ioctl$WSKBDIO_SETDEFAULTKEYREPEAT(r0, 0x800c5709, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCGRSIG(r0, 0x40044273, &(0x7f0000000040))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f00000000c0)='oL', 0x2}, {&(0x7f0000000500)="050eb3de406a7a1ffe7f6ff97a72d4aeefea47e3df5da49eb4c9b4d110d24cc8ba54e5387d34613219cbb4d9a65f7adf2a9f7d48f217a1593652a24c3fe606d40450f4222ddd1c2b39654b33170e38d0ced3b2d101bd1ad1e84e3bab91318006ef284ab234d1b452f88e572cb13fd6d4fc93a8be447d9051c850aeec2bc4beaee5273f3f39786b28b8fefbc59d9564ad95661d54665063056aaa8f9416f4766aa6d8a5c28b5a9eb7b6665b87677bc02eb063948499a9e3f77527a356bbddaa71106b87f7a8e5e2437441f46fd326f7690b89f6158849fddc4f212a61f83bea81de4cdaf75c52903188a56d38e89963cb9bf9f95c18fa99011b063dfa7b075a12e1ccb15d3ba2c789635684388945b5dd88d48429ee1d8ccf594fdcc04cbbf77444de6bc1d1bf9f1c36f43bb9fcb39fc75dc1dfe6361685622d", 0x139}], 0x2, 0x0)
writev(0xffffffffffffffff, 0x0, 0x0)
writev(0xffffffffffffffff, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0xc5005601, &(0x7f0000000040)={0x9, 0x0, {[0x0, 0x0, 0x0, 0x9]}})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="820200057f"], 0x1)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x16, &(0x7f0000000040), 0x4)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
connect$unix(0xffffffffffffffff, 0x0, 0x10)
writev(r1, &(0x7f0000000080)=[{0x0}], 0x1)
write(r1, 0x0, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
execve(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000200)=[&(0x7f0000000140)='-\x00', &(0x7f00000001c0)='&$%\'.[\x00'], &(0x7f0000000280)=[&(0x7f0000000240)='\x00'])
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0x1016, 0x0)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x80, 0x4)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x2011, r1, 0x2)
ftruncate(r0, 0xfffffffffffffffa)
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x200100000001d, &(0x7f0000000000), 0x4)
r3 = msgget$private(0x0, 0x90)
msgctl$IPC_SET(r3, 0x1, &(0x7f0000000000)={{0x8, 0x0, 0x0, 0x0, 0x0, 0x140, 0x1}, 0x4, 0xe83a, 0xffffffffffffffff, 0x0, 0x2, 0x5, 0x100000000006, 0x3})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000380)={0x2, &(0x7f0000000000)=[{}, {0x4}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0xfe30, &(0x7f0000000040)="d6efcc66cc62c54742823fee3eb5ecb3e535e529c2436f", 0x4)
socket(0x2, 0x2, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="82023d"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x4, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1023, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_timeval(r0, 0xffff, 0x1006, &(0x7f0000000340), 0x10)
sendto$inet6(0xffffffffffffff9c, &(0x7f0000001740)="0ed3c4f9801f417c55ed96730833f555570d21c75a4a66bef30297419eca856e5385a075c7c50c39b4bcf6f3aa4c57823c74b42c77dc3291fecfc6de235bf5030abee31f05b957e53408ae31b75e148dc44e031bfa145283f897a5fe5cf1e7ea44d0c145e103d2b98c95bf749c5c00b152603f36f2ef337c38c18293a94a1a75c15e613f0c0a9e33e650936ccf3edb8136572dcbc74ce923b94dc7fe9aabfbba798a7be322cffbee4b5ca6a240388865d7eb3125f8da2d725a9044417c14a9f8ac5caad0de5c1a58125678baee9953628009ddb830ff379e5e77cc34d488d8c572d2d788fc083f743080823da3be88bbe4dd683ceb025e041110566bd3a31abc81bd5e87cd3f5894fc8a85d34c21b233e9b753e5e5ad46670e68c65802d9f2398abc8f2e7e67ec5f75cdf851356574e24d967a6f0441f918a404a5d4e58778f4f881bcce841de55216cbba2f5e5540962395162399b92259af83be4ddb9c06c39837a9bef5ed1875ab2da1acbdf30eea31c1a8f345328cceb20176a20fc8e586855c3e8780229b1df46189082995dc711cf304f9cb1c632a8ee14c8df8c940c630c4a832e7b8cfd7eb299003ab6272109bb8aa0c0bb3016d2434bcce58f3c93bd9fd6f70af852872b66138595b349a2a1ac2b9d58b02d246fde59f26a3f666a361d7c57d0d42c6768056f64d765616134351af0c4c436ef3c9666c7b3bc36bd3748af1dca21eec5e3e592b38f4850a0fc1250827c565844c80ff693844a5a9ef6a17b343aca09c9ae8dc694d1a2f76e35a7041a5516c4abaa8a44928195594de14b79793a1e8894287713f869e5f85fed93ceac0a50f10f8168bb666aa0cdd4e69e88f9742da36774746f7c681d0f15ed1b9fe04095bab0b78426f3f764261616fd1366fb9526c418aa9f520ee674a22fb3989b664b2b39aecba3fad641f99e730001209b2230197333c1204e40c56041434dfa246393bc76f9027edf4a34cb7e15dfe8c0fdd6d8e02e4a9582b58a41b9b1934625fe302613339f5e1a375aa5c887c01c5e02a36b89790526f657f789dfb8ea38ccaf66680c44755b1f87210209dbe34d1db4731ba507008adb4c42943bfa6b0eab7cf9047ef829ba84d9d7db76b6cf6e784dfbd96a2f8b958d198082af948cfe342e43ffa12b4113dd5f2f4e9b531f93ead90e17b3a29e2ede6a21178bc7cf3829500ec9a53dddd122ab81c07bc37c3b800fda30d829b5f02875d40c606bdce7545884245e00d9ff99f730a7c1b60e87d6bf6bb21d97093a4a5eb25aff8ea403ab1b36a1045307ddba8665c43a4b8080dd34905ca66a6ea9e02a5b5bd7c4723abc3d57cb01af18aeea0eacfee3cbef3d4905eafe7f5103de5e0038d7e78895043be9cac20907986b92010df2978daa0b484fb00f7c2bd411e7780e7c74f8c76cce14354c49ae3164a38a546a861a3c08a4599ce51a9554a00136a83abd948cb3f85ddb556ee0d3f60a07d0003412e166070df4147a827dcf6dde8c79879254181ea5626bb7b411e5d4e383b6901bec6dd26095bbf5009018793ec05383a643d46811c84aae1a6a51db152768603ad5996a1825e2af47f16d19835c8de2cc45f881d844a240decd3a3a8314cabdeb9007a16e1222a21495bb928165664ed5c063aeab6b063c62299adfa3aca39668f7de2223bf6f18285b61b3a2e65357f8e79ae1ce4421b551c0c95abe0f31fa50cde6586f94dc4f612b80bbe9249a0a83ac2fc303c6a282a644fb3284264fc4faff90a952bdd37c9859974216b33da62019105673896ecb2026aa63e106718c7629629f0e2db8701ade7fa568faa5add414607c480613de2d1423fa360216408d7d4da1df26bb83a98666ab944999b1c75917543724075809d097f6c874450147aea22ab200fbd4cceb083ddc7d536f8a44b90d38c891f06a6bafafaa40ca2f76f35198eb3d6d51bea5359e2da95352cb497106532c94dad9027636f15ec344a23ace367db1bec1cc644a43bb15b45f3ad9d435e55db34df328731ab1dff0c95e69ab4cacb32ea8da80f7f500cb386b65a1212e20fd649cf5f98e1d45735216d424679abc0f2c4b7b51f94f2eba6fadc738123edc83e2dd1960b577fc80e640190b82ef3eb5d4398be09f59f4df15de690eaabbd003c2367466142db7daed7936b0f644e6c02fc929ca06d4bf4cee45fea0e59fe6c93f6798b9b88e5267de618d015d6667468564e20b6224231de9aee591b9377ca12fc31c052867b996e1176bd2a1571446a0332c2bc80ff544f94dd8b47cc6600e4a9a06218e929236f36dab82f902aa2de327551a6cd657d50e5eac2aebbf64309c5e25a4bf4d6e2ded764431407f0a77971758987054b37ae65598ca496398e96962e3b2493dc771b5c03b98bc44cb9152c3de1ba1b1eb0563970d32d1cd04f76139132c5c0d1c692b63410f0b3944039cbc9e53130169d3562e6cccb64fef5e4ffb18b59c8c53e9684b3ddee970410f360762d4f346ed62dfcc107caa2e4a34e2366326b042f698758ed63720de6b4f2bea81e629b7040b32e660c192c49918858199f314c4d3e1373f03d5094534b308a083197591a2fc72621dbcd4512d19c92ba9dd3de64a72d2e4ae864b1be5501db82cb3473f2ae74d11307c2914d07301d059d68fb91666c16f674b437af4288e556885d6f78d47d4c7720c0f4108be0c104b7904a2d87e259bdbf9dccad62fc435d48c405386f7efa4cf5b9587a311cfa0f7e09f38c9087c3e044af50b7484be365debe015ab2ae13014e495f51d6d91bd2f7dbb5bf2a534da8a6ecbccfa62491fee58ca89b67fbbf6bcb2156e55d8ee65bf149a04603d1808592b0333f19e201c5f52f947920848a3251204dede045d293fd5ffa526de3c3e25cbb956d168edbcb75b1bb10e2ef3db2ff76551cbdfdc11d40ff37f3a5612170ad86c5ed3b553f0d38332337f438f0e771613d5d92780220148917222894c9b7da09c03070143550c393732c560414be359322b1fa786822e30d10f0a186073c236c11fe4f03c3b9a60bb4ef5f4acb0104ceb3a442380171f1104396f6ba961d8df7276879e12ee1bad9d37b0c22d0b2d8521596c05e2d04afcf409ea781f5b7770a2a9ce5354fbdccff0847db24654e271115509bf2ec467dc8e824c9e888cc31de73012f084e6d79c541320f103d105762e3ffd96b07eb49d9638b793e600280d311fd5fcf036fe3f694d7f4682b0d2a29eac1d6b0103cea880c0c947d8743bd156d06020c82b44e187daba8a7539610788ce5e42a200e91368382a399234ea3b30b955588c7a74f7116d4290748ee3c1184bad13a07ca6601f862ce79ecced509664d50f3ef307049f7ee4f2455d0827dee647f947b67bcc7c2dcb3aef17831437ad6a3b92086847f7d50a4cfec13554fb5a287a71c62ebf359f303d33b0c519921fed5cd4133094b2b725d1cdc72ead309f8050b9cccb5993e7aa93a6db91789116a979ce0f83923c2ef127895fd79085831cd023bed26c7916fb4f21bd9035fc057d8814875a3ed40bca21660a98c25efffe2f10d05fec3f3c81468cdfdae241045fc1cc94fb04f12d30a748b55bbd84e431a5adf8278d783245fb74970bc7be0330cba15eefbebe2bebaa77fd68c1c6643e01edf7a8f03d8a8fef650fa83cc48320a7528059b3ad2eb7e6304ef6ff99969243ce4b8f0efb719a6aa8ddb9e6f519e9dd300fcfafb7a19e658ab3eef22f746922386ed906a9b65d11207b28c18eb05f25b5ff11978d8a5ccf161f1ef875f37d465d66186bb7222b2c64099991a3cf1f8c6b555950f2a4d2cf4bbad6a8ef6b477d8ed56bc216d530626d3b77caa04790d3a5c5f6028c5947fbdf9a24c393b0ce200bec4054914637519b4b8698b07a83590bce2f8dab7f1ba7ff31fa7b6030290daf23a86e78337773934eea0b48e68e011d3c7dfd13fffeae51ba650c3bad0a42921791ff0edb954ff2c0260e67533796a18ac81811f5e49c4067081d2cc7c25f90093b7e310aca74274b0b4001e964c2f8c5840d135e7b83c06a85d67cc219e116172dffec54c2ade6585c281f7f83927abc7ae84c7ac82d2b7b8058930274ead0fea8407070f966e9c69545c29d4060d15440c25a9182b18fdcf2f126af1279cfa7b3eef3f04a4fa1db5bf9b0c26d29c909c2afab47dab1ed50e2bff911b568e71f9c618c291b6d0dc47a767095d9fb40fbd785cf756346a0e830d88a118272205794371352be5c70fdfc503874343839ab833622d0a071f2d8e5052f2a7d552cdbac4f58844b9c9b5d9cfbb99fc85b159e05354ab1ec1a9ac93737cde89601e4ce839589e322f622d526d85225a9ce1e00031eafca60bb9184235b4573e7312f1d351649d09d0412aa5fd6e6ea480f7a0b2c308eb00d5a4ff018859358a1c33994a508639b4cc6dacc83e676111c1f1d1cfc0b534215d11c4115319a9249e31a92663cb427433e5bc3de1649c0ad667c6c1b715442986bb9f0a18329067d7deb5143a5d202de4734b07445818052cfa8778de43dfaffe3d6341f2a454fd158ab63646b8d653ecfcbc72e12bfb8c17d1a758527257b1b4ae166f6b2e5599c83a19e0d0800e846e1f6f01de6d79ba9100c8d16ba46224b245fee4449c640effb5a64c7e74fcc7254410255505a788b600a0f9ae7fa3f2d3b07d2db2bd27ca1df3056007270b27c9c523b9cdd0dc3eb126b3cba533faeb6c6a6e10532b4928a4b045551ae6a2653113614dd2f19650192a03ab52448445bfd444bb7ff0a8f48b032e18ebf7f78cb4640ce0125b20155c5b32842f6b2c8a78873d51f25fe08102afab26cfedb89508d3443e7c6bc52ba5bd90f2ea693cc446ff62e136e2f753ede6146e042bc6f929a551ec39a48bd0380dc66e5041057d94b195e919a6cbf703f246b15744c8a230f5369d699d6b0282dc363808fd70dbdc1d230b20f01680941ee03ec6eedbab0edda86a8543025ba6de7e26067a99399d6bee9f14485fa1578b7b07c1aba9071fcffbbe7f51c236aa8f52d04733f1a7e5fb523b25e721fd1728f96612711159c3b0f72595bb1bc77f2cb17ebcfe7d3f92d84931426fe80460b6a0f63cdb5ee0b27c493413da6f8cab3e6979b847f1aa855ea1b1a0c5996ca2e6e5c8a1da893d0e75ad148d8ded2ea5e4e4826c9716b83561f7805cf0e48133a8436cdcce9e5beca5daabd614ba7c62e3afdd10cb93c44e20157af2d3aa383e9790e15688720c0671a4604fa586774a8e99d776f02c9c9d29a6d9456e2ea804d2c32fcf1ab7db98190566a62a9199955716f48af6d2ea4179d3507bb78ce9d4c207678b6626b3f50789b612c20a342b0399034b71a4c1985a056fbcfaece75ae097540b176266ade8c09d16abda3a5cf2b98426a87eade87333f82c4d8c9bdf045a4ba05aa7448310ed62d44ed8c060c698891d86dea3a26e3367217338cebf804cd96922c508d9206353ea54e401980d81fecb82eb5e02da279fbc2a790429870b9b9f001026b1608ed177ee9584f6b7474e506c43e0934844cd5ded8ca25fd2b5580a21aa45ebaa78bfe07e591bc550392f96a7e674e99fd7ba0ac65ab52dd71f3c5e2da839cac4b231c5d7b06ee86c8924c3634143e326dace32e04a2c2451f899117b79a685cf6e7ce100aa09b93566848a033657878220146e5928926c3331fcba3c95fa91f420206ffaf73383f57b3e68c7c51eee03400fb8a4237a91e7670fe8d3fcc8e5c31a044107b89a216428e7e4e700ca43fa89a33c2f42d18cd9c324517ee2915b9f14426ad4faf7029b86da26f3e54921ab40862342c2679071224495a92e4f1a7eb00fc4048a931086d05c13ab9a824fb67776416d508d3ed47c10079d1e0e7930e5db231ad27e94f368d576fbbfff106bb2f0a4abcea597c1a0ba6b95b2c86afe4ff173f6a7923edcfe0f540f906a7540d7c8859b2b42f961e9dcd16953b12c8916f75b50604eb489a5432b68cc41b965e4f0d7df0b86dba56930873f29148e2c7599e357d458362971c904a081ea93757e2d3593107229a2f38d04751abf5e6d4286d577ff6331e0da97f65d41aed54647dc9a9c8ba7ed7c8b92ee0c29ac2ca77bc3fce38340fd89d15cfd4e575ed7f63dbaf4506ad5403804dd87ab7805f160f88dbd6fe1683c79db3d225113b8e51870d2d618a5e08086f028338c10e907defe1de376555411a475825932e4e9567bf51c201c63d8b218c10330f4ec624fc9a9cdc7544147dc43867f55c67c45afb04f1380677578494320b2ffd563e42a32d45f800c394b445d3acabf159850d709e0c187dc6372416042e8895cfa71989ec2f38f689a11c8083fcdef729c42038686836e02927f5ec5f5c5219224ae23158878b9b9b9edf92f26dc951e722ebe60cd493998bdbcf66fb12d354b5aa62df883de4f144cb9bdfa67808142e5d8f951843f55ea94d700b2bd08d218f6372ee104390e35d7b5bca97b370a1197d54adf5c0dc6cb9ab226b3284cd0fa7cc83006afea81bcc1bd6f2b290236eaff4b33c5ab7e0891f8a2c0146e0711b61b9a90ba782a64a8d396a8d64ef8f43ad73f758cc2e85ae792a034ef", 0x1237, 0x1, &(0x7f0000000300)={0x18, 0x3, 0x1b, 0x6}, 0xc)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x0, 0x0, 0x0, 0x5}, {0x3, 0x0, 0xf7, 0xffffb950}, {0x3}]})
semop(0x0, &(0x7f0000000200)=[{0x1, 0x3}, {0x0, 0x3, 0x1800}, {0x2, 0xff, 0x800}], 0x3)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x8, 0x0, 0x0, 0x0, 0x0)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000300))
r0 = semget$private(0x0, 0x7, 0x3e0)
semop(r0, &(0x7f00000000c0)=[{0x4, 0x6}, {0x3, 0x8, 0x3c00}, {0x4, 0x5, 0x800}, {0x2, 0x3, 0x1000}, {0x1, 0xd91, 0x1800}, {0x1, 0x9, 0x800}], 0x6)
semget(0x3, 0x0, 0x247)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x1d5}, 0x20, 0x9, 0x7fff})
semctl$GETNCNT(r0, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETVAL(0x0, 0x0, 0x8, &(0x7f0000001340)=0x5)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000002c0)=[0x9, 0xfff])
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
sysctl$kern(&(0x7f0000000000)={0x1, 0x2a}, 0x2, &(0x7f0000000240)="6af55741ac591bd0d091ca493f43edde748a033095a4ed4396950da1e9f18faefb065ee5984813d55606d7eb95badd8b906d37103489ff9b7df3647f83511f5a6ff6ff6446d15f0300000012260b3f", &(0x7f00000001c0)=0x4f, &(0x7f0000000480)="7930faea8d4ee3c42066b44a5e9af788c9e975fd8fe6e458c84e90a5ad3de5f43b81247ace1d1c66b4b516f9e28e0295d911632381de43179cc0916265d06144bf571f5a566b7054334fd9d89a8d7a29a30ba81d47ead18afe66df08f0f8a539b9b4436f6c6faf4437314e3b2a9e4ffb04800f4c35869096b2a3191ab353ede88b933cd749e83c729ac80f4be67870492af3a6e81f004a309ec94cada9de4f7bd9accd0744f240257f841d870b1a441c78b17a50e8f072838d63663d0dcef24c24712ddfd118baf1d6dcbbe38bff3845c6821f907842da3c8af35e010458aea179c8295f8a7163fc088a6bc86a3810e88b42228bfd1a936b", 0xf8)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB="faff2e2f66696c6e312f66696c65300007448410eb628d48246359d2c725d42bf580eb415261b3434def3d5a08d05125db762dfd7ca0645206f0de4bdcbbdc2e4fdc6ca147a636e02218d957e486e144035a0dc8ba198e62200a2d3f99667b98735fee051ecabd8260da9c52139e3214db1f171a8a28d0a48a4a7873c223b3d725d307fd8d1d4da9f76211fba5610b239d5b75d69c91e368f04308b526e0b0e75038988f8808d9e2c0e78c1b09996b925c8e03601a6afd3328f5c001b27a1d46a46bb75b01e7cb98381b97d568f618fccac2de96938da88679eb6dd1f29f8a446e65f63037"], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e664fea607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb38a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b8b4417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c0700"/4096, 0x1000}], 0x1, &(0x7f0000001400)=ANY=[@ANYBLOB="300000ff000001000000724b0cf6d25970439aaf000000000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="20cd940000000000ffff000000000000de4d87900c5e9cf940922414c038bac4502568ad4c17a555904334d03594f001f135abe3347aa3c9aa8b4b114c3313b946832d1af3e6747be3b903000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="0000e370ec71967de93b6151890d0c002000f6b9", @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYBLOB='\x00\x00\x00\x00'], 0x70, 0xd}, 0x4)
setegid(0x0)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000380)=[0x95a, 0x0, 0x9ff, 0x96, 0x8009, 0x8, 0x402, 0x8100])
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
syz_open_pts()
close(0xffffffffffffffff)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000080)="179653e485eeccdabbd274229b5b41b4ca5252bca3e4f29c12ad7c47ef10c546a344c13ab2abad207618", 0x2a, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x2)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746", 0xbb}, {&(0x7f0000000200)="a5781d1fe6aee59a92131f30", 0xc}], 0x2, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000100)="b7", 0x1}], 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknodat(0xffffffffffffff9c, &(0x7f0000001040)='./file0\x00', 0x2000, 0x4000)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
accept$unix(0xffffffffffffffff, 0x0, 0x0)
r1 = dup(r0)
listen(r1, 0x0)
r2 = kqueue()
r3 = fcntl$dupfd(r2, 0x2, 0xffffffffffffffff)
socket(0x0, 0x0, 0x0)
close(r3)
r4 = socket(0x2, 0x1, 0x0)
connect$unix(r4, &(0x7f0000000000)=ANY=[], 0x10)
r5 = accept$inet(r0, 0x0, 0x0)
sendto$inet(r5, &(0x7f00000001c0)="1daad5cd36195d6810b318270ef9a4e226c2d78a5413faf736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8ad376f5c1dba9d565c875025f067ef069c8b78d74a06bb9b172a2d7c03eff6b58b06bbaeff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e7068a0d0a3e839ee05e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e13faeebbccb92c28d329fb8635224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70dfd265fa833f7f7788b351b9a0abf03d9e21a38de65ed7352c75d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc3462aeff27e94fe6994ffe7086d8f0c631b96880096d82665ddc95c8ed5e187c85fc07bee3f65d4b91d59436540aac4eff6f5cea6e4233587318755e8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc780bd319a6235b800800000000000000f525897bfdd75d1f2d5a302b3c4eefe2f5ace97cade03c418d91b5edd3d278cedcdd1e7d4b95b3a63cd9079888381a65a8789264cd8048410f29befde650c4fa5dcb582718b324bf28706d04a602d395a0ff0aafed57c5dc7a2d17ef96202dc44790cc423511a5c2c44c1d6a2cffbad9e62418bc25d106ac22145de479fafe620614d07d935a3ae6cd295d9ccca947a6f483c71d2e04af4a8ffd6a534d183eb7d39dd34770f37290a93d68882c6a8d220e5feb6940b4ea75e0f94abe6c4bb9e905bc86656f854a277e6907c4478d74d2cf08a56c02d6c9586397ccbfbc41e3789060018a3c7047ea96afc10d610015f6861ce8e55f4bb8a90a29627ac549e3b", 0xfe21, 0x1, 0x0, 0xfffffff8)
setsockopt$sock_int(r3, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
shutdown(r4, 0x2)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000240), 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000240)=[{&(0x7f0000000140)="baf92d151c3011fad28b6c83072523da54a57616c3141020ff", 0x19}, {&(0x7f0000000180)="9306c73832db3044ea31a8537274fdd4e7319488f35d94581fe06eeb140a5a5740e51c13a88affcabf30808984c3f36374a32a34f10171ebd4ede91789fbf29f6b2e0274", 0x44}, {&(0x7f0000000300)="749b502abf7032369be025ba65238e595477ce839c0792472349997c53b5af603fa3a96ccb401bdcb12aa773d6626e3cdf", 0x31}], 0x3, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
r2 = dup2(r0, r0)
ioctl$TIOCSETA(r2, 0x802c7414, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "12891c123cab9b0b90787156b1d4476cc129d5ec"})
write(r0, &(0x7f00000003c0)="dd9f", 0x2)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0xe, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(r0, 0xc5005601, &(0x7f0000000100)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffc000/0x3000)=nil, 0x1000}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ff6000/0x4000)=nil, 0x100000000}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000fec000/0x14000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ff3000/0x2000)=nil}, {&(0x7f0000feb000/0x4000)=nil, &(0x7f0000fef000/0x2000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000fef000/0x2000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000fea000/0x2000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ff6000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ff0000/0x4000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000fed000/0x3000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff6000/0x3000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000640)={&(0x7f0000000080)=@abs={0x0, 0x0, 0x2}, 0x8, &(0x7f0000001700)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x80}, {0x6, 0x0, 0x0, 0x7fb}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x50}, {0x1c}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000380)={@broadcast, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "2c2dd8", 0x0, 0x0, 0x0, @empty, @mcast2}}}})
setreuid(0x0, 0xee01)
munmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0xffffffffffffffff})
shmat(r0, &(0x7f0000ffc000/0x1000)=nil, 0x0)
shmctl$IPC_RMID(r0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000280)='\x00', 0x1, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0x0, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0xb5}, {0x0}, {0x0}, {0x0, 0xffffff6c}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x842)
recvmmsg(r1, &(0x7f0000000380)={0x0}, 0x10, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0x7}, 0x2, 0x0, 0x0, &(0x7f00000017c0), 0x4)
pipe2(&(0x7f0000000040)={<r0=>0xffffffffffffffff}, 0x4)
readv(r0, &(0x7f0000000180)=[{&(0x7f0000000240)=""/4096, 0x1000}], 0x1)
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0xe02)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7fffffff})
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x36, 0x0, 0x300, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xd, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x91, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x74}, {0x1}, {0x6, 0x0, 0x0, 0xfffff7fb}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
write(r0, &(0x7f0000000100)="3c9ebbd555feff969613ba3e1fd0", 0xe)
mknodat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x833eee594ef524ee, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000001940)={0x0, 0x0, 0xaa74, 0x0, "4a865919a67bb8db7b8c0a8a0200", 0x0, 0x485})
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x10001, 0x100000004})
flock(r0, 0x2)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, 0x0)
close(r0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$WSKBDIO_SETBELL(r0, 0x80105703, &(0x7f0000000000))
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0xe, &(0x7f0000000000)="02000000", 0x4)
getsockopt(r0, 0x29, 0xe, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
close(r0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
preadv(r0, &(0x7f0000000140)=[{&(0x7f0000000040)=""/243, 0xf3}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
read(r0, &(0x7f0000000040)=""/32, 0x20)
r1 = dup(r0)
r2 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000600), 0x40, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
fchmod(r2, 0x80)
read(r1, &(0x7f0000000040)=""/32, 0x20)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000000)=0xfffffff7)
r3 = syz_open_pts()
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000340)="7be644133a1c68eb482eff49c30355701ecf202e44293a3ea04e22edb0d1ee0ec1598e570a8b130ab36eb384974d3e3555344e1596063ef3e89b4470f821eed25b084ffa8d76b0a078758fa2361af899b06e7f6dc5d9b23af889ffb71f9fed03f6bf13e7863ed3a4a2fce81f235eee2b8027a5017747fa7c1d31af0160ebef24110f293e39dcb3e7d16a1c37f22a339f", 0x90}, {&(0x7f0000000740)="4fb77be3f89ff04d1e0c145c3f4b44fc562deafdda6660ec5d162c82021eff05f38d53e62b32da336a18837686e5b285fdbdce09ea79a4651f64fcd735d056deda6e32559c990179cb3dfb462a12f38fccced6f79d39e403691c24d0aa439c258300ed3c8cd868b95067fd4bbc8c079d70cdaaa85f83c40c52472a9d2955ab2cb795d9185841d114afe4a5ce4b8439ee08703d30d1b64cc4d8968050120de954edab68581fff887fa68c683f4faf8564cddfd958f2e553a594bc9f2e13d6fef7a868d268d43c7fbed0071862cd9add99675b84068165f2000046404e0f9901c5b767d143285e35251e19ff86e53305a5a931691fc260587afa6d12ce00c6d9c01c7212c80355a125f1c34375e8dbbac11cde579b2471472006bc2cff2b52834e021e", 0x122}], 0x2)
close(r3)
execve(0x0, 0x0, 0x0)
syz_extract_tcp_res(0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x32}, 0x2, &(0x7f0000000040)="f48e3469", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x0)
wait4(0x0, 0x0, 0xe, 0x0)
semctl$SETVAL(0x0, 0x0, 0x6, 0xffffffffffffffff)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x4d}, 0x2, &(0x7f0000000000)="33cb1109", &(0x7f0000000080)=0x4, &(0x7f00000000c0), 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r1, &(0x7f0000000100)=[{&(0x7f0000000080)='D', 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
r2 = dup2(r0, r0)
ioctl$TIOCFLUSH(r2, 0x800c745b, &(0x7f0000000140))
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7007, &(0x7f0000000040)={{0x0, 0x9}})
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0xb, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000480)={&(0x7f00000000c0)=@un=@abs={0x1, 0x0, 0x1}, 0x8, 0x0, 0x0, 0x0}, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f00000000c0), 0x4)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0xc003, 0x0)
r2 = dup2(r1, r1)
r3 = dup2(r1, r2)
dup2(r3, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r1, 0x0)
r2 = getpid()
fcntl$setown(r1, 0x6, r2)
close(r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
r5 = dup2(r4, r3)
connect$unix(r5, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
close(r0)
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd6000000000142bfe000000000000000000010000000017000003"])
clock_settime(0xffffffffffffffff, &(0x7f0000000000))
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="19000000290000002f"], 0x38}, 0x0)
mknod(&(0x7f0000000040)='./file0\x00', 0x2000, 0xfff)
open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file3\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000bc0)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a0", 0x1cb}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
mkdir(&(0x7f0000000100)='./file2\x00', 0x0)
mkdir(&(0x7f0000000200)='./file3\x00', 0x0)
rmdir(&(0x7f0000000080)='./file1\x00')
mkdir(&(0x7f0000000140)='./file2\x00', 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x242, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, 0x0)
seteuid(0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9}, 0x0, 0x1, 0x7fffffff})
getgroups(0x0, &(0x7f0000000240))
semop(0xffffffffffffffff, &(0x7f0000000080)=[{0x3, 0x6, 0x800}, {0x2, 0x8, 0x1000}, {0x0, 0x7, 0x800}, {0x2, 0x2, 0x800}], 0x4)
fchownat(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x6)
socket(0x2, 0x400000000002, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r3, 0xcd60441a, &(0x7f0000000240)=0x6)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x5)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r2}, 0xc)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./file0\x00', 0x0, r2, 0x2)
sysctl$vfs_ffs(&(0x7f0000000040)={0x4, 0x1, 0x6}, 0x3, 0x0, 0x0, 0x0, 0x0)
r5 = dup(r0)
ioctl$WSMOUSEIO_SETPARAMS(r5, 0x80105728, &(0x7f00000000c0)={&(0x7f0000000040)})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000080)="eaef125c000000e3", 0x8)
r0 = open$dir(&(0x7f0000000380)='./file0\x00', 0x200, 0x0)
munmap(&(0x7f0000ddb000/0x4000)=nil, 0x4000)
mmap(&(0x7f0000de6000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
mmap(&(0x7f0000ddc000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x45}, {0x28}, {0x6}]})
pwrite(r0, &(0x7f00000000c0)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x15, &(0x7f0000000180)="03000000", 0x4)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
pwritev(r1, &(0x7f00000014c0)=[{&(0x7f0000000080)="d74c78007f", 0x5}], 0x1, 0x0)
pwritev(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
r2 = dup2(r0, r0)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x20000, 0x0)
fcntl$dupfd(r3, 0x0, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0/file0\x00'}, 0x10)
shutdown(r2, 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000504600000000000e6", 0xb, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x1f)
mlock(&(0x7f0000400000/0xc00000)=nil, 0xc00000)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff})
accept$unix(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000280)={0x0, 0x0, 0x10001, 0x0, "bec8aeeda255b9139d3884be687814b5a85a2446"})
readv(r1, &(0x7f00000000c0)=[{&(0x7f0000000440)=""/4096, 0x1000}], 0x1)
writev(r0, &(0x7f0000001800)=[{&(0x7f0000000300)="98", 0xffffffee}], 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f000001a840)=""/102396, 0x18ffc}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000100)=[{0x64}, {0x74}, {0x6, 0x0, 0x0, 0xffffffff}]})
dup2(r1, r0)
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000280)=0x10000)
poll(&(0x7f00000002c0)=[{r2}], 0x1, 0x0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xfffffffe, 0x1fc80d88, "24180400000800008000"})
writev(r1, &(0x7f0000001440)=[{&(0x7f00000001c0)="a66c2dcc193a8f951931f490fbce15d50243dc6a995fad24", 0x18}], 0x1)
pipe2(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
fcntl$setstatus(r1, 0x4, 0xcc)
r2 = getpid()
setreuid(0x0, 0xee01)
fcntl$setown(r1, 0x6, r2)
write(r0, &(0x7f0000000040)='!', 0x1)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, 0xfffffffffffffffe)
r0 = socket(0x2, 0x3, 0x102)
close(r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f", 0x111}], 0x1)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
semctl$SETVAL(0x0, 0x0, 0x8, &(0x7f0000000280))
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000340)=ANY=[@ANYBLOB="6f18e47ace3d15b6fe9d"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0xfffffffffffffeac, 0x0}, 0x0)
msgsnd(0xffffffffffffffff, &(0x7f0000000000)={0x2, "b9202528ca43e410b86d38852c458da479c262395270afbf254e03ed7e8b664130f478e7a6c1230a3e73b076d16459fa0987c3053c178389de998bb7675319c4d573187b07228a33e8cce602404ed750cf05b2f8"}, 0x5c, 0x800)
r0 = msgget$private(0x0, 0x400)
msgsnd(r0, &(0x7f0000000080)={0x2, "25db40e86e746c7bdbdc7f0301a7ba4f0565ba36aa316125b514b71d4c226d08d7fd9972efc8aa62eee315e88ca2bb2fdcbc9ab63e22d0b762f436fb9e4d17ddd432e299d6dd2afc0a9389a4422c0c6d3651f7b70c200463742b8180bdb5300d71ecb6"}, 0x6b, 0x800)
msgsnd(r0, &(0x7f0000000100)={0x3, "2c5fa1c8b3db280777d1a410ec87cdd5ff9eb0853595f1ff271ac799846aea0c9caf609399d2c9d4e0f8f9aea003ff2c42585d51a004667f20b1d3b26c701cf27941d22e25bfe0e25478725bc7bb78c14825e3f22c95f7f7d5c96cb69bdca351dd620b0e7b406571ea20eb7198cf4cac5aa2bf4106fdfed35d8fb6489f395f65dd9c47fc052161e35e02433703f731abb78b905abf2e95d29b61311ff99aa0a5348e29"}, 0xab, 0x0)
r1 = msgget(0x3, 0x0)
msgsnd(r1, &(0x7f00000001c0)={0x1, "535eddd8a5313e878d17be335460c5163676f2d1d2c1098a4e68d58078ea1f89b8b8f816c204f593ca6f7a506dd7dba78388ca2804dd7c84afb242f6a78209dcb56be846dc20bf7faceb8ecaa6cf4d1e75610e81356ecd8d636b2b6d243a64c40f0ffaebe8408ce4fb13032741773158afcb9d4358c66d6abb9b6add0a968193fdfa30783ce7987e3b505808f058152c"}, 0x98, 0x800)
msgrcv(r1, &(0x7f0000000280)={0x0, ""/4096}, 0x1008, 0x3, 0x800)
msgctl$IPC_STAT(r0, 0x2, &(0x7f00000012c0)=""/1)
r2 = msgget$private(0x0, 0x20)
msgrcv(r2, &(0x7f0000001300)={0x0, ""/147}, 0x9b, 0x1, 0x800)
msgrcv(0xffffffffffffffff, &(0x7f00000013c0)={0x0, ""/203}, 0xd3, 0x3, 0x1000)
msgrcv(r2, &(0x7f00000014c0)={0x0, ""/236}, 0xf4, 0x2, 0x1000)
r3 = msgget$private(0x0, 0x420)
msgrcv(r3, &(0x7f00000015c0)={0x0, ""/99}, 0x6b, 0x0, 0x800)
msgrcv(r1, &(0x7f0000001640)={0x0, ""/222}, 0xe6, 0x2, 0x800)
msgsnd(r3, &(0x7f0000001740)={0x2, "b45aab842f84780a4336ba59e3a31c5d96c1fa018d564fb41e59f19bb4392d94b262070103cab1112a6743a18dfb25862e9abfae5d11c9bc726f7ed1f02e234c0bbbfcf1f7a9b18e18"}, 0x51, 0x0)
r4 = msgget$private(0x0, 0x102)
msgsnd(r4, &(0x7f00000017c0)={0x2, "69576d8645da2f707b0016aca17bb20724045d4f3e23a4e60090971f5845d1453313858ea710bbef262d99fd3f5711a5dd3433e4480b13905d6463e9d04f6195b7c76a5eeabef72ddc858469173ae68916de18019d43ccbed0daac9f395ec4824a9070bf391cdb5baa4b912ef95979ac792c04d4b70f297f3779287ebe938daa3d8f49f707d66bfb25e3c907aeb300790369e0868c4098db15a09f387effb372f07a83138884690a6271914351b8357dfde2"}, 0xba, 0x800)
r5 = msgget(0x2, 0x4da)
msgctl$IPC_STAT(r5, 0x2, &(0x7f0000001880)=""/156)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0xb, 0xfffbfffa, 0xfffffffd, '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00'})
write(r0, &(0x7f0000000380)="4d000000003572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70affffffffd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19badff7bde555c03c89", 0xae)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x13, &(0x7f0000000000)='\x00\x00\x00\x00', 0x4)
r0 = semget(0x0, 0x0, 0x0)
semop(r0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x5}, {0x1}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0x205ff, 0x0, "d702000000008000000300000500"})
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x13}, 0x1c)
close(r0)
r0 = socket(0x10000000011, 0x10000004003, 0x0)
sendto$unix(r0, &(0x7f0000000180)="b1000513600000000000000000400000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00e3630667a586196a07949a2cf4c8b2ca3ebbc257699a1f132e27acb5d610000d7d026ba8af63ff37281c18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0xffffffd6)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{}, {0x0, 0x0, 0x40}, {0x2}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x4000000000000}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
pipe(&(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = dup2(r1, r0)
poll(&(0x7f00000000c0)=[{r0}], 0x1, 0x0)
poll(&(0x7f0000000040)=[{r2}], 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x6)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x0, &(0x7f0000000140)})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x24}, {0x7}, {0x6, 0x0, 0x0, 0x200003fd}]})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r1, &(0x7f0000000200)="34cf362b3c3dfd6039bc17b23704", 0xe)
fcntl$dupfd(0xffffffffffffffff, 0xa, r1)
r2 = socket$inet6(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
r4 = socket(0x18, 0x1, 0x0)
setsockopt(r4, 0x29, 0x33, &(0x7f0000000000), 0x0)
bind$inet6(r4, &(0x7f0000000000)={0x18, 0x1, 0x2, 0x4}, 0xc)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x20, &(0x7f0000000040)=0x9, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0xa, r5)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt(r1, 0x6, 0x10, &(0x7f00000013c0)="6d66b176", 0x4)
sendto(r1, &(0x7f0000000100)='y', 0x1, 0x1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x2c}, {0x81}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000300)="665c3409cb459f877d212c96027d", 0xe, 0x0)
r0 = syz_open_pts()
readv(r0, &(0x7f0000000680)=[{&(0x7f0000000000)=""/75, 0x4b}], 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0xffffffff, "6cfbb0ca40f1a3808cca03e0bb7df1347c6398ca"})
syz_open_pts()
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f00000001c0)="0002f350d37c45941a3c212047eac30700b5433b483534a185cd98917be6019ba098d48919363ba6ee0a2305c66e936573041a0d6d3a0f63dd6d879130a347b4f3", 0x41}, {&(0x7f0000000100)="fe0232df69a0903826595070899e67b312e9829b05ef44c5d65d1d1c57fb70d9b4c2b95cc2e85c68b1ffc1f98f708800f5620c195564c17a952089bb7520d6ceaca961a3525ace928f31", 0x4a}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f0000000080))
r0 = socket$unix(0x1, 0x1, 0x0)
getsockname(r0, 0x0, 0xffffffffffffffff)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, r0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000015c0))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000002600)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000100)={0x0, 0x0, &(0x7f00000004c0), 0x10000000000002a9, 0x0, 0x61}, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x0)
pipe(&(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
poll(&(0x7f0000000080)=[{r0, 0x17}], 0x1, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
r2 = socket(0x2, 0x4001, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc028698d, &(0x7f0000000300))
sysctl$vm(&(0x7f0000000380)={0x7, 0x10}, 0x2, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x1}, 0x2, 0x0, 0x0, &(0x7f0000000200), 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0018eeff00000000e425db019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xfffffffffffffdf7)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000240)={0x3, &(0x7f0000000040)=[{0x87}, {0x15}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000080)={@remote})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x5, 0x0, 0x0, {[0x1000000000, 0x0, 0xfffffffffffffffe, 0x0, 0x0, 0x0, 0x4, 0x1, 0x4000000000000, 0x0, 0x0, 0x1, 0x0, 0x0, 0xfffffffffffff800], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210, 0xfffffffffffffffc], [0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000], [], [{0x14}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa}], {}, {0x0, 0x0, 0x0, 0x1}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x2, 0x4003, 0x0)
r1 = open(&(0x7f00000015c0)='./file1\x00', 0x200, 0x23)
ioctl$BIOCSBLEN(r1, 0xc0044266, &(0x7f0000001600)=0x1)
getsockname(0xffffffffffffffff, &(0x7f0000001640)=@un=@abs, &(0x7f0000001680)=0x8)
setsockopt(r0, 0x0, 0x2, &(0x7f0000000140)="99fe358a", 0x4)
connect$unix(r0, &(0x7f0000000800)=ANY=[@ANYBLOB="82023056abe7d32883d9e9a1a60e1287d6f6faff1001a03bdb91d180d35a06efba1263896639c000009f6b00d36134434a11f8854104334c8a6d8c74e40f7d1120a9927e8e7d8b9a73e9e8758c7c4c18f5c146a0e128eebb28280de7bd76c086165198d9e18c75abab3aa4db911e9454ec0dae46035650080000b9746b1f7fdda111fc36717c0200a90000000000000000eb226a9c3e46340ec144b13a9960419ac5dfff0587e008662e90a547869b8830271750b813459a97227ad7cc6b8e9872a4b167bc4a0232eeb8a74ee9e0cb7ccc85ef0a6057bd23cc4129e22224af318d8d48c82237f9c98b8808641e609a9b4935b164da4ab3c0d525b788f6067deac9545dd417b786d7822ad71d9864894a82d83ea73bd804679e60ce72443825095e1ce8617427fe623a3d089a8245e8ef6cefb2db7a2f5a97b6741b5d5cc16fedcd5ee289fa4b3463a9716bbcf96cbaba08c399d2d3670057cce4641b93291eea7e339b223da0c290a03c13e63efa16348574d5d04865b29f91d38f3f386fa2a208ddd0000000e780428612b9d46b99ce9bfa6ca64b90e39d98fee21ce1f396286fb4df19f00ebef7ad494dff1597fb40c617b5d57f314e2259dbead8fb72cd0c4d2c54d43c24d3839ec36a75739deada57bb50de6ca7da5bac02724114e03f5fb13159df8b9647af38d42e26ae756952c688ffb357b58a950d86e54667ddd069a7bfc59aeb7831462e7aa6b591643e2897a69dd3c13ae29f0460fe0e73fdd516d45b5cbf53c35da346046d0b69cf562b3d0df9b28749ee34cfd60d97a032e0ad01215701ecdf42dcc37f5d1a3e4ad784109a7b8e05323c0c081aeb6876c4657d22195bcc162f9fe66432541d91ce1e38536fa2891dca00000000000000cc17a34bc7f73fe9191f25b16d15db2f6c97da1437e40d0f42a628d86b4bd5c25ff50e47953105036354b9aee9272f41cd939f88c84941ecc660c081db9f4c866e2ed45c9aeb86bf057d7b26cbfcfd5361d0daf7d9e895d365a2bd27607bea76cb91adc61699f62e3115ab79da512eec60cf9c63dae141ce1242a83097f1503914503bbf6413ae465fe9b07c78e2d6effa960fc709eecfc48921dff63609799f74445aa3eef6d0649555822b391eac297d466fedbba3e65a58"], 0x10)
write(r0, &(0x7f0000000180)="b4d2cc3b78efffa8b8823b99a6d20feff4fde776", 0x14)
socket(0x6, 0x4, 0x1c)
ioctl$BIOCIMMEDIATE(r1, 0x80044270, &(0x7f0000000200)=0x800)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
fchflags(r2, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000001700)={'tap', 0x0})
socket(0x2, 0x2, 0x0)
setsockopt(0xffffffffffffffff, 0x3f, 0xfffffffa, &(0x7f0000000000)="fddf00f0d37dcebf9e788ea00a685241e52a1679e5f3ffc7f9637735c59b57859d26da1d45f843e639b0ccebf5c3021b92e4e111ac87b6d6867af54053de84d41f089e5343fd1562fc91077fd3e1cb6b9c19fb8e3b5c037efb4db13da3a3a30509ecf2faa2b6a744526ccd09c2f698a0046389257466954dfd5e9e682167ea6d00ddb3160e90890c7754d16eed762eca090c01fb0da64659d07fdf8a2da9791cca0e2465076381b8899d6354340a31ea93af0d4c1910fea86a4ac83bd944", 0xbe)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x17}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000080)='./bus\x00', 0x2, 0x10)
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001740), 0x200, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r3, 0xcd60441a, &(0x7f0000000240)=0x6)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x5)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000002700)={&(0x7f0000000100)=@abs={0x1, 0x0, 0x2}, 0x8, &(0x7f00000023c0)=[{&(0x7f0000000340)="b1e5ce615fa8cd6a205cadf9fb6a604894438044506aebfe26977a37ef1ac857bcd225d3d9dcaa458d7abc027e46e4f0f5eb6f508f6e00dd6561af765bee3492908df7fc286b3bd5ae05db76228bc766ac1948906b5c2a0d2a55ce00ee1bd1457fcb05759f5d87288fdb70d08a3e47a1c4c2ecf999a10b19f3c7d19d69b0a06e7ae028e2a9659d8eca39566d8fa5d68983ac80658c3706ef15e422c2b239209cd90de84d2606c4b2f6471d6876b0606b4cacd592327c6bccc88aed95a7005ee127af870b11eb6efe7fd8525fc7badbb60e3a2989bc23680a6a4b3bfa4773f30338807d57378a1689b0c323fdb6dd49f84256dcbe185ac4ca2588e7340a8eee727c967138586499feacbb314f1d59d6888c6b81f93baf5039df82dce578f05414010cc9c7c9c705d1219bdd71fcbbb88d05efb82e53fe976b02e013f9f716c6fcdf5df5d40ae3cfc3f672e7f6914b46496114a50b5158d89152daa72653b2313b4783c4e631aa2a59ab81720bce522f5090feb30fa3497b73af4ae4e8bee1c6a066747c1ccc02b45df30f9a1e9b4a335974187371c70d17c2d22068acd8676db289c7863e3329a5a082e97ab297e02eb8dc88b8112232054cb2727ec613b4e0df6e1e3ec3acfd68e67fa51c41c8e428de68b3e74b77421dfa4d8cc0b6d7356f7a9359ccdd06db6a465a98d1169461c8c4e71419d2019083336e1b5e087a97c1698bdd3b6289eecf0678ef2e34751f28e6710314e351776250c56ee18f9f921b190065d41c0e0b3687e3958d7b53c14a39e886f39c0da22d8d3cc284ce007b2f4afbbb4618a6ab37f70aaf061725342241658e674833d425bc6f9d954ede4e40f72f307605b41ec3f9eeffd3a290b4fbc57fc8c715511da950f126b8612697a9eb23b5d18cec4d8e09ee6b4c632ea3d1bc8cd5c40c8bcde01abfa286f50cd0dcc4e76c149bdd03e3c5c7ad968b389fcd19ccd157f19b5d36d0f551e968cd6926ad83e1fd586fa95a9427234208055a56a4a15b17d865b7175bae504285f16952a2be909690b115d1e834a4fa505badbff22613be59150ccb7f2b7c5b8350b5fea2d2d06140d8c543797a8b84d4b9db020e1661706ddef5ba97906685d635d3014f96f90f25cebf76e12817cda84a7b4829532aee91407616a0d31f8fd5ee46c483b966dd2b26232cdd5160c03bab497372a0d8795e726b755d7ff1e410a614a69fd8d7c6c0acccd3d278a0ecb3a7b4d7cd62b029b1b40558a37bcbe54c93e5c8fcd1488f91dd57efcaeccf97b51dde24afdbdbe4258b84b991e901654479f5a0b11982aaceef5a91d78ca6981741521a3ed88c7865f4b594359f6fd4ea3d69db159d91b8899cc5b7e251685db8bbfdb9b6ab37f1a1a2eb1d0ade0b0c01eacbfbbc181e77e4da1a150a6b496fded025c2c7bb32025e9ec9d7a4089d51d3b7c96048b9ad52f282baac4e68a52ff2647f6eda2a8be18372dd11f6ecf952978999bb712d54251c6b47db3a3cada84eba5eb269037d458767ecbac36ee9cea4deb9f5db821f07f46dabbd7139a4362437b70bac97d338debca9569af9af693bd36db3fe4fdb3b034c4271ee4dae265930cefa18a3edba18380e1d9940592f8b45c2ced73237cd37bdeb8c3b390946390dde1eb182c5e49d330ae52b8d709d98314c29206a21f48bd0c27bc341f0188a789c37053f81cadda31517007421f477668149496e4e912c84b8ae83357311659e382f5add65a609a64616a90809ca845064e8dc6c7cc3ddef31f40b752b6e3ba4bc878792dd2b03b2ba34d4adf13e7baface9aa33cfcedf61828e9f529f28897284e22c1a119fed50b98ba19f520efc5b8a2cf2bb28e3512ccd7238192e0257f2df3e5ccc442c8ad482a38a8b7f329ab80c0070fa9a0ef79c39d5ee6868392a529eac1bef41781d00382be9ee952886da3559de7cf81311a4ff23d1257d842216d57cc960c0491af807d90155d3845c2c311e9011b21389d8aef046b10e0279068dec210220c80eaed48c605ac9df3480d261d81860f00bc15fe8368b4dac49e2a4bfc9f8e47f67f64620fab52501bf9f1e3c10dc064177aa76e78fa060d49c2b3e4facf997004631dc5c2a4e1bbfa54a673501a36df6846195100278a0bfe7627414c3b850a14e61a8c2de0cda6fe831446da1093cac1a5e4391c07b460980cec9eac236ab35c295f6b72e0e268a66f34e9980bed29a3bb3089bdc0afd0bcaf91541d729acc9853c49d75044252fec22ed9c577b442907a4e98d0ed3fa5d02bf7ad3bba3fdf8adc074dddd8cb93cf0f3b3e666c84134fbd8ad87948f1d878c06490f83695e7b86eeb6f50fca78140dbbd147a91d85dcb561d44115aefe223ebade9b220e57397b7693725e307fb3e032fb696f400a1536b96c45c60490ee16eb8b7ff8ab513fadb518fae1e6f4887fb695a30fa533e8cca967346da8db7e318d9937dffe961e7a86c70f27e9d0ec195ef91681e3c7cfbe32be1895349454cd836919b8c7880a298f46c19e731538f0348b947fe5acdb7211dd6c03ce2565e7856849ad22d41a4f2f754305e270bd700118125910c8d8b97b4479eda1208c299f23a877e565db4182c9b1a3e6d4815b2fe2f1ad54ed8ab8b631544c7cb360d4a8aa752f34a647dbc1d9be3b0012760cafa924078b71dd592f4e142f7db443ae75d6e00077672f85b5634f26e79d19e317323bcaab4db0a85360ef0f3ecdcf06edf92e13ee4a0fb138055fcd95e198da4abb4f95bc1d4ed6b44b82dac769aa057ede1758b0b927166fe555335b2d17c84b446afbf0bf62e3acd8e91f5e6a179f55335f03cfef4ee0d382698a402c38ab63ac2a48921120f728760ec036eb49f3a74b1b18a2abd57e92be1e725e13c9d49fe4413218b893cb89777d4ceb1f5e1a95f3558d636413d2b831d72128cfe40406c17926b3644bc5c61705e466b60ce9ae51c40cf0110f0a96d76fc80861c5d790d4244ce4dbe93dd693cb2944a6bce3d043a6aafdf63cec61780f6126eb9b9218154b7e6ce0c1a51f53993981099244d69a6b5bb3c0347250e77d2256081a36fd0f569ed212a3e28d50405923f36812f8580a647774d3ecf12bb133346db9f2f48a50aa571751b49bc61432c7f2e458541e76f292b4ca0e4a91290e94035a9eb96544e09ae5b4ffc3f679e550e3ba1d115bc7d45a6c46519aa2e8b4977378d2709cdc7c89aca4afb415981888626bf289fece389fcc8c978a9bd53e40e9cc7cee10ba5c22b7e28fead88423034d29b5601edded83576548e383586341749e15b03802fac14c30bc39ed9f80fda601729d3da177bdd2ded28dd63eefb452cf6be3c4d2a7d3c0cf41c9ae6da35c5d2e4940aa8cebd4ee05788498313e3e7c96f06f65c28b56d9684ec9cd7cde878cd321fb62c36d538630f11b63597ba93f79945ce9f37c248b7aafb7cad51e43e231b959a5863b03ae5325ac3ff4272ea44f97f5ea055e52012d891d46772de79541619c69b985519ef3138d590114453dff940279250263700efa5d5ec0a7c2d7b2d0749966bc48c96a6a63131c61836ac6d1504d2573210a1499b289b12681677a66cc9fafcac7985dddb513e22dfa203cb5add9948b56eda240e20c769da8ebef4b5b06686a154ace5174400bdf511b1575faca1aceade63b1121cda3b7ce5813f3480f3cd2e90f2c043fa277dfad3551641ec7863488ddc907489b9675dafe4577b6fedd7453a93e6200e821ccb72631ce2e82dca5a368ba8eef01e41d5e9d972be4c5f326ef69ab2180a0eea4ae6b90dae3748876d29e4bf6082047bf32b52828e520f6e48d339611872692dd7f09b9c28e7ef26a17edec2ab9776838cdea881fda9b9403548eed8e022da89e1983275a77e7e82566134412662792831ed782d62e6dd5d9c398e25fe2da8116afdc0eafdfcb3739ceecacf7baa36c4054eb7cbf295ab29de7f5f706c464bde5943a596ff01d4a06e2194df8a3c946aa20de7991d424cb264020fcedd10c1b0aa00289a8b19fe6024fca499247ee255cc20c3827d0c29e743af338d5b40a813b2b462b8490248cc57e8ebfe119d7bbff421828db62ed002600147454a31ed9d75ea77a3487705a002c3538293a850c2519e1dc91ec0b6f60fd09b19e1248642691fcb24864e7bfb4722026e50e1f21eb4bafdd2e1784ec4548b170d8cc4710fb1202200cecb8c8caa45d7560c0f4caef3aae52fa6c1b6ac7591c6570c2eab6a7b564dbf8859c26ebdcbddcb65b7e0c2f2aeca422bc3a543bac96045f75bfa33cc3976bdebf22eaf9653d479a29b7f9c5f9e096685566073be822125836afca9ae5a38271a92fb9049e157e154d3c91a47bd69cf5d9a9a39ba5d62ddb52a5dd8be720737b0afa262f964e1b594c92b3dde153d2fe41175c2fccb934ada5644fa8c689dd8975f635c07f4f8cea5e653103a3a6adf8243fef545edc6b74f5fb04b5207eced5b4f6785614a355576119bb81c24df2d53bf0da704afb86ad6945645d402f57d65c07d91c47381bd36bb600327fa4f0fa57a0b9a9c49f24658785ffce219d14fcf69eb8752312b8b97463bceafa5879d60aca2d1b73e3bf7648e1c081166c7c817d49c00e2f14ab5571a70714444c8fbfbe78c3165588de5c574a23ba9bbbc6879745b127daf88d0c80bd7fd5f1ecbf51feb506772dfb4fcfc0bc9479ceb2a224e647d11e28d44dcbd09fe898856ec35c0d3e320e928ba38142f6dbaa27a33014ddc2cf09aee9e89303e429fc6369f6a1e9dfd3e9c779aefd33cea962baf003b2ca79512b1b78b1ef5111a5a494434807e327931db57656514f1c4bc63dfc159d156e46d26dacd50f3e3ca525939910a2fa801db877109127637c235c844cf6a37521f32a3b938dce9b8fa1276978562898fb7c13107f0dcfddedf12b79aa29811ee466383884803e3c428441e6497876eaf6c4b98a97675cd84d3b1a91748c004765ca5951ac0cf9fb5406da63f2fe698cedaabd17e1b2be9dd5ce9811f795f4a67e2d35377b6ded7b78356e6979e9de0c838bfad84bd15a5490b991d812f8ac09c67fabd5a25b0fc38babcf2721ad0f3f500e2a3b8e4410b586469dfc606748f933290fcb6f42ff004e260697cbc6ed4f32fe7ca0b3485e53a620ec93cf48b5c4d3d938270f13d7b42d93a24350585918d1b5394d2dd0e79f1bac8a4716b5f2379d2b3232348ba38ee0c0c79cdaf2ef47b80aeca89a8727b51cc78d4506151a9c20b0b407926addb9165905b0574b38bbd251bfe46f413232eb3868b14702cc1ecebcfb639c6a6c340ff094d226a34dc00e38c415a4f8763a41f7a5c0b51f2d6d634dd95ddfeec87593cffe68844ac7f021d51454d859763f5fb24709bac85a70650af1f532b41e749e084d3d02f1ec956027f1d55ce1f5727b46560b7e76e07cc5f9ae0c910545be107ab3ea51e9027636e2f28e9418a4f1840dc13ca3a22a59ff5e3e3fe6e3b6b8bd9b8bea8c1f25c1bc497e98aacb99492309c1c15d535d330059d8742ec0c91bd37c12d95320476bca4b063377ded49449a66254e5753ea1e9f0dd0e9bf9a08787b431f90a72c23169abbebed5b6ce9a698be86acdeed0b117c2b9d6912e8a0cde182bf73ba8fefa6f0a782eb6909dac627914f5b30a15b10cfd6c38955f9243d78fccabfa4fe6f3b2eb7172b9eeb9897fd52e91e8c3b9bb07e0a670eab337b2e0678b3ed05819ad8b1cc4e9ee06524499f054", 0xfd8}, {&(0x7f0000000140)="1d38f7bc2da084f460d6417e834674eddd93f92b0c28775230684190339f9a8e13f8f85e70b33a0d2f1703bd761ff1a4818c8656ad827f80c6a3eef281fa059747127e92733bab3b5fe1befa312446881459d4655424d100290eea7620a98f86a25f3f19ce8402f4b48fb0142b6c53abaf27ce25b09161536cf55b79772d00d4b61dc06f23b430bb3d02af93626c1fef82312ccbacfa40f16cd2", 0x9a}, {&(0x7f0000000200)="87428e45cb4004bc654d0c22e667464a9a097dfeaef062d7e360ec86831ee51da6fd5d7c9a4a46720884d70b72c5f48940f8", 0x32}, {&(0x7f0000000240)="796eac9253ebf4395e40245753ccc0689e9fa0ab9597e3923ba33080e914d8e03e493963c22c012f12e8dcb7da7036a2fdfa9ca89cb4441e0ac0ae046ecbd45f309c7de2bfb162c3241adb63343c637163f794", 0x53}, {0x0}, {&(0x7f0000003f40)="a565e83b52e66444d66edae8f17822e2b29554b5aa49ef8fa8dbc6e07b57b0700bf533b3a81fb41a845a96a594fab6772b8c846c556f4cffb03099b7a67eb4c7bcdb4d3ad85feedfe904ca698680fc61b742f3b83328279e4efec7b2ed17aefbc7ffc80dddea6a0e3b65b7aa661d7fe916cdfa33843e11c02c551d77f933027f867622e61162f9a35eb27bb0081f4151e1b0c6f83e3bb8f0247c9f44d62c7185dcb58f7e56445172640e45b7d195e9c370dc8fbc782b031cc0d417b941ac9c6d9abcdcbc6670ac398dac4e194141e0bb036475ff449547ad3e836b30087561a83f275b978f1b5e2102b1a79143d1d2ab12b3bd35c2197869a50ba3c713e9b75549e6e2fb355637b597ebe90af2b44b12189fa6d9a724ba0c765db1682dbcb9269e17e361d891b035da43e443de2a481247ee551f392b3e99382a58f3222e44fdea34115b77c7354089f128ac20f9f822734e2f392f12bd2e0340579629ef1e9b0263d88b9bc663cacfcfe7db8ace55f8342ac2f40f0cfd53ebf34028e1091a7a85bf69ae78a348d16f30c4e94c59639ffc6935d348d486147fc1e7b8147a8339e57223053c90790ed53075b0124ad7b1ed083124fcec6646250de256d0f12abe572050fcd29ff7e3e49eb012c30405720b8b6d68758b4a194151d7da616716680ba972497600000000800000002fe263fdb14f88e5440a4c751400b1f4b0942cbb4e65444aeb59527bc7053de5ee0f3987c9ee70ea73b2bfbd2719d11648638ab717ba2d35df4294e81fc4c78abc2e5c8040f26f0fe567f589c0d78e48658c6a6e2944793d29591c3d2b8824ea095476ae2a2c65e499fbb40ba360abcb67669d885b89821f9456939ddd29490f4cd1d130561982d48b560ca695b063333a3e61ed67dd6a13c42781ae8298d422978145060a2bbe1ec5a3f80209e86a6701201f7a4aa30d3337b46f18492b6e3810dc3a7bd92b572e632fc80c9ce262c7c0459eb1b5160e0f67fba2abdcb849734fda418b5254d53ae46023380115a946c6838be82d717fca6e2ea9ccbd02dffaa2b8f719d2a2b0852e95aaa151381ccc1ab5bf188d30a6cd36b66f5bbacbd247de7405094b0bdd6f4d071927928046ea825ca9f6638655e097ad406c0c415b13f60366ee2c17ffc37d8d8018544fcd3dec656e84dcd7818b6d0769a631511b601b109e1e2c5f0692b693ff27e0f29c0da25f9d5d902661bc1b4f93a0f4ae526016797e3c2e3531a6d9a1ccf14e465706c97370e914b7f21385c0304f8ad98f8d654d864f43871795525e5fac0775bb0366946b7a1865a28654062ce92ab608645f24946eaa83d31ff065b7b06fe0555e57acde9e5364c71d922dfe98f4776c311db97ac973a9251b0cbbe695ec0499528dc96000f401cc7882b5cf53fdeb6203aa6580f41e01ba545082ae6ab475165a7ddb9d6a3dd7df41ff3d6bda16014a66a3cba2c749b22d92337cdc7c3e119cc3719ccc255947de7d6630ebb8d7a5444beff4673564cbfbe02bb0ee9143e6a2c2295f0f1061768da449a13f745ef3f8fc1f4f6320d365bcc695ba7c7ea7e246ccf448cced8370a9f2eb090ec62c1ed88a1ddbe6674b0110d56edb0dbabac5c49bc3a13195f65c6b11e86d8aebb516d0133425f3b0db83c6d77b7a4af1261822f41ae5bbb094bcc540ef6e9b8a342b60c458dd4396a67d4a3c820911d04ad6816fcafcacf0f82ca36876840395d9b9f0a12f29dd35b7d63852749ed4944857aba7707ea6e2a7ddbdea438d6a692ec02731d4d3d06a2bb320921b7bb8d93b67536323cae0ca982b35f108c09bf79622ae2736b857779594064270c74908bc79c80bac427c57dd58c1958dd628b8358885eb9eb3271f792f82c3dd8a69edce80eb0ace7add95a6e3966a0fd47dac9323e0447a2178e1b312fbbc39f34290ad7abf12b1de68f0507a32f37c041537497a292440fa5117f961e0f7f90530a04673085ae6c7d24aeea0e1a782af4819fc15d9bb9f2a905dadf61fe89e6986de3b8253bd79a3aacf885d0f34dfe5217c4242793d04b903e80f9a4e7bd4ee957b7dd0390223f63ad4bc073a658b15a9057e9c3798e94839db2529d37c0a427552db1fdb3f30bfb914a012f1eca9eabd13a4ed5e6dd7e526f4f6cee969be730f48a23a71dee9c48dc53f4de9d0ce3974984864e28f44ccb287b8388a4e6c4b2657bf700bd8eb8006d31556abdb5cb298706e603d7f29aae5c4260cf0010374cd50746b14bdcfdb12b5feb41c2146a70578addd67d83083966f4e6efb976137e7edbd3f3121a97e5e20188bb5b99253faf9a5ce5991f7e163de54a6b2a27e0c667059815348e53d9bc00d1fab5fd8de4728e5bd07468abef74f4efd3616e9810d5871408cff9ed7031aae3bf1d43a9a688adddee4d2337f730ddcc37e344523a5ee5b63c95e880e6d86d1a68759b98d3a84e2b4c29a555b57de0a5d86d5cddda94199cf64cb4337fb391ade3037913bbbea942e2b68ac44f4df0751ed75b38165385e96147790cb000cb0a662a62fff627ee32d6e9d3fae1c6a09d5f3661a0bbb7da918afb911b396af34a831b6f1d4c2aa5b554ac4c88cb02fb14a0e8114af26374403a9952bd11a636b09b3cdbeedb75bbadd99d5e500afe68669e0cf41d8c380308619cf2b94a7604f190727cc5340a788b5d554d0d0b417778f214abfd5c3c242ad8bd259038da65bac830a6413993c9812b7dbcdd1db4fe82c99dae29bbcd191d28b8832948d712de4a41aac5490e2d61dd4afe0e35224938fef03cff7d45ccc6d8c5f9246315175cc13f86738a0a90392ac4388ad4f68d7e8a0e93ab18640b483fb36a2b0421fb0544c26b775605889b2cc79a4ffd625ca788b265779d925707369aebb69bd978ad7d94b84e8604a5adad856f0ced0b91098b6fdf5aad351135faa3ed641d212a5dd92fba18097d027ae974bf0a99ddfe910ce5b896ede0420ad307b8382a05042a237b76bd2c2cb25a371807b51c6113255737240ce355a952bbc839972c6ee336f39ae08b6710be513165d9bde1b5f99da677373f2aa0018938dc0d4d6a4c96d236b7dfc58453222802cb57c3fc8b4b24bad867f31630ea8adcf86b6e087d903044de082e71469aff8abbc0cc804dce0287c055293414d21073f7d2e3f046bb049488157caf0792a25fe8340f62485f7276f9105cea8f92b57db9280236d5c87824878c881d96bfdbcc985f59aff901ce66a36a7c4cdd19815b26a51a71a8561bc4cc6c0c003b60455469d2d2e76995a080c1f6e29d6bbad233961645e54a15f97f2708a2f2fb606d6f9009a2e20dc702bb25fd84ac130a08b7adb83ebe2b7bd19485f96455973334b15dcbf7762a8a13018eb38d10b2161391a014c7b5d76b894fba272a34c198804c8fb32b6ddcfc427d653bac4341a2495cd2710318e5008ca915948359d05556357564d850655147ba47f2d238f8a35cb15588697a333bd66196c74378d1eeb4282b58665c70d42e3d48fb9456845efc8c7d750b32853f6e8504958923bca02f75b0f4ee2567bfbd19408571b8c3326b6f85887ce9c7f317b32d3a49f0a410be233a9635c725689215d2a1384536a3e8945fb1a820c2c75c000168a38a6a3ef6dc5924ea3ccad1450c01cbd951cd02490b4d9e35f80ec666e0173580fd3da4e5a66c10eb4c62b8aff812c221170e439010d9cb0dc4dd030bc526ad539a24ae26c051eda5a11ad3049409fd96f6bcae69df9d1467d0fec7f7b7b9dd3991c65fb3a8f0acf1d346d0f6f91f14c46f4607832439f3d05c883ad572377d451955cc9bce0bed0ae117760d6251bc8263abdb385dedbd8eed648f84b331052f0eeb42da64c91a94ca8d58831d9593ba9912349a7c61c274d54fb7d7c92dc462a514e58052563b56c13c22e5e5bc43b0392545e90b830e6f3acc49f674f91609c4dafb3536b75974f824fcbdf02b20508adb3efdb68ea8aea36765ad701df1127ed0363e94651c31e5fed55f1c652aacc86c8ab2febf3d2520f7df75dd117e7ae0e47f62314f4cf07e47814d60006410109ff322aece7e57c104eaed27749aefb7c6da003870237b06d6ab79af2fa2ea20a3b6094d756c6f4df72697d0f015fdc266f1f2d3a2700bd1769f48c9aeac6aa54ca88359cf46923bf940501df1c217bfcfa377232ddce5de138ae600100c2958135e8439764a9b366cf91a836bb6121996e08c9629e533a96787810376ce25d114fee47a8990c07c3616a84d34bfc87c21a2712a3f9d2051d192354d04ca17a4575e3262d369544bc1b8cbfb7a3fad909e7b7b5e605b6ed49c13f43e67cb4f027837bc9abf10c3b8240b5e44b76e9932699f57190ef5051f879592e67ba257a6cdccb2bc09605ec7255d5a63a1ee5e59788bf073f39d628ce0b4f8b66293273e134fdee7c9bc36c8716f446aaa5a54c4214e265d4504357d29a6f0cfc8bcd0166bbf7e6fea28543dde22bb8b17d32d546faa8252726fa92a2f4c22999cd967cad4affd4061d8681a97dd8f6fff2f99a84a324eff6aaa82f4f4994862d012d916036fb440fe3b91c2d12f500ca8c3564e063a2ed6272bfbb609618d2470f2bccec800b029aa81b622616f3f4ad0a05fec20d897d5277b64174d2d0fc752091f1e83b4c6bb2e0fe9b88ca7046cca49d36d70e31c4e4fde4a6bc1df2b2da80ca3596bc6d9691bef38c35410d7354396ca2a5542ac85a543eb5100b718f3d871f7bb67040bc8eb83da44c76f5ec0bade576c9c5e2f18e760319aea1b3316a7622773af5187837252d83ec6d729e1cc6c580239587696314ea45bb52f5f6f8b0ad530031f04b4f5e3b7ace2d7170eb0eb2ba05caa43d37c2c1e43033d2f10a5b49b89ef22a9319f97809109a529441807ba4c4a46e13c8d5256bf9be54d8fc41f3029ab6ce11d6db27b97dfd1c0cd0dbfdaafe0039834646932686e5ade7faf92e40a041f87a4328669f2dca1328d3a7ec513c0cad452a9b6633cc1ba28ede74b45ac84a422572bc9a75d57ad4b018969b8eeb1fad7f48803de0d3372b27438e3a1e6cbf52c9298eb04a754dc5286e3ba263a39f85d034e4f88a04ae5244df36967ef2939cd261675b01167a8f45da3a0fe295de82e4b561be5abd70cac638ac282aecaceb0f2823bac0343b606bd4644f2b5ff1abfe7ded1376442cb1581bd1b7ed049f5f325b704f6d867137416ce243d4ecf6a3813a9fb034f3275bd92b11891e97d0f565d43daf333a06d90d10f041e1415df969cdef2147145f25b9bd73f45dd8c192eaaf8b41ad81ee04fe7da28e36177c1ca2621a33fa53ed0919979ddd6365f6e73825cc897109b19d5e652dcf753b49661fa03dbb0994f0f4adf56924999c3be0b363b11c10b1a511a281d80f39feed3a38d01aebeabf690001b24527fcc4e763bd63029c6bf3c838f5b378f21a820facb43be84911e18d1f0d3799088ee4afa1207950471fa1f8a2004ee434588b7c10ef4a18a8d0d4e67604656c2cf71ee24935c4b3fc47242eb6976ad5e3ff391e71d00d0dc976dd27ed3beb9e9ae6e8cd883cbd717948b7519f059188745c412e67ba8799255f79f80075c88c5dfa442db232397744fdabe9136786108752e37ac284dbd7565dd4ee37448432677bd659d5308ffa730eb486fd427f83f418f3602bf6fe9ad1fdff77e4fdae2bf68e6808c76c019e940127c55ef5284958e22d0a331efae6b0baa4dda3a8baafe56d6765dc80181947cba3e785352d710ee83d0b4fa989891c16ce057d0ee375dba9efccff1014f8d469e7db75d0", 0x1003}], 0x6, &(0x7f0000001340)=ANY=[@ANYBLOB="2000000000000000ffff000001000000", @ANYBLOB="b5a3acb7c741e5e7a857a077ee9bf9a9724b458aca7a359897ee3831da653a958295c47f840e4049a32cf0a11c69aaff2db13c6befeb6bb8e0618e93abec77be34a6cac04059dbc3102d785b1fe606891ca3c2a1c88d3031d12570aaa627271cb8ab9decc83835fecf7caaef67d89c0e32241ee7d0a3cd83afc43111830844", @ANYRES64, @ANYRES32=0x0, @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRESOCT, @ANYBLOB="200000000000000000008d0b2fd48148c37fe65ae4795b13ee2c780f241fc669f40b42861922744dd84755", @ANYRES32, @ANYBLOB="a8056c9c596c0c76a319cec5d5b5d583c967724a17f76e1694d54b6501c3f1443d219ef97f61575c3d185ddef7fd89a77ee9135e8eb88c3a92886e8d7b07ceda0cba2a74ffea299d45607c6be3817bc1bfd009a1d87ebb4d9ef3e1b1beb02309cf3c1afd1a", @ANYRES32=0x0, @ANYRES64, @ANYRES32=0x0, @ANYRESHEX, @ANYRES32=0x0, @ANYBLOB="00e2000b", @ANYRESOCT=r2, @ANYBLOB, @ANYRES32, @ANYRES32, @ANYBLOB="000021000000007fff000001040000004ccec7bd2b5a0843e88303ac0d001c73e080ddafec1735f2b825fd215247d7d028c8134aecfd4b7fc45590f4c1ebab1193fe9048da21eeeb94656fced3ac2fffa635d62f159feb966789d6638f30", @ANYRES64, @ANYRES32=r3, @ANYRESHEX=r4, @ANYRES64, @ANYRESHEX, @ANYRES64], 0x100}, 0x202)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r2}, 0xc)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, r2)
fchownat(0xffffffffffffffff, &(0x7f00000000c0)='./file0\x00', 0x0, r2, 0x2)
getgroups(0x7, &(0x7f0000000080)=[r2, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff])
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000080)={{0x3, 0x0, 0x0, 0x0, r2, 0x145, 0x9}, 0x1, 0x85, 0x3})
r5 = geteuid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000100)={{0x1f, 0x0, r2, r5, 0xffffffffffffffff, 0x10, 0x7fff}, 0x100, 0xfffffffffffffff8})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001840)={&(0x7f00000000c0)=@file={0x1, './bus\x00'}, 0x8, &(0x7f00000005c0)=[{&(0x7f0000000140)="f47081e79f9227118340cda6832f53c20db773fe854acec0e824c79552", 0x1d}, {&(0x7f0000000180)="c9ca56ad010144e824b0deb413c2215594dec89fa95aa326d6153af368a669b95427ab17a0be75b94aa6178224cfcf34b7267e121bebc5245113964eda0bbccb6e46b042f970685162ef3d8b4957bb1bb699d31fd1918b276b543a7f56e1aaaccc2b6001bc6fab89af79b42fa081c819a5fbc729d8a501b4fec0fd05013c8b2a037991aac89ad598e79e79", 0x8b}, {&(0x7f0000000240)="ff38d027edd506f1e3a7c2033cdcdbade08c232fafd60a0ee0c4e867ab64604cc10e4ce1b715d90f83a933c05fb781f35341f8349bb35649d85afc4eb647a5ae2560621405ab88ac5bb613f46736aa5929bb8cf92be535e1e99b9cce9bacd5dbd8f92c6b1f6e1b8759174da614cd3b8aeff3f3c0138d32c0e616e633667f1e973b0ba7f9c6678aa03ab8fe5036f39314baec4ec0", 0x94}, {&(0x7f0000000300)="2e9d3c7dfed083c35745bb29b834fbec69075c5e6ddda455fa76752610a64670708ce238263749e93f5e913ad773dd9ca245fe4247e10d8a2885eeaa9fc35bb4d8fd07d3fe7ab782712096712b03c3bc595dd025cea2da32af268f8f48fe996245b343d865d16516adbf1ae1f4dbbc3f003e2944d69049d50ed9d43fc02c211820489cd30515ca0627d475c7b63fb2a7140d79bf53185abf97", 0x99}, {&(0x7f00000003c0)}, {&(0x7f00000004c0)="c6a901e4ddb60e5b1984276905566fb4ab71d2782d619237d42da852a0188261daa091298f6e13e0feb2a348b3f10d856babb0d3a0a127b8fb21539ebdf37382b9db87b0937b8ea45c9972184b6749538e", 0x51}, {&(0x7f0000000540)="ab974f1a1db01c697b224594b691120556446da8d0338d75101d597d91e819ff81235f22aa471cb41019769e18e361de134523e9a8a9d6a8578d", 0x3a}, {&(0x7f0000000580)="2a76dbeec37ad85452f0cfed3c2894adfeaf1cce89a1b077c5bd6ee62b294163ec0a0c118a", 0x25}], 0x8, &(0x7f0000001780)=[@cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, r1, r0]}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, r2}], 0x88, 0x401}, 0x402)
ioctl$VMM_IOC_RUN(r0, 0xc0205603, &(0x7f0000000040)={0x0, 0xcd, 0x0, 0x2000, 0x0})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
fcntl$setstatus(r0, 0x4, 0x4)
writev(r0, &(0x7f0000001440)=[{0x0}], 0x1)
write(r0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f0000000240)={&(0x7f0000000200)='.\x00', r0})
readv(r0, &(0x7f0000004b40)=[{0x0}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x15}, {0x4}, {0x6, 0x0, 0x0, 0xd920d164}]})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775", 0x3c}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc088444f, &(0x7f0000000240))
shmget$private(0x0, 0x0, 0x0, &(0x7f0000b39000/0x3000)=nil)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8002, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
r5 = semget$private(0x0, 0x5, 0x288)
semop(r5, &(0x7f00000002c0)=[{0x2, 0x0, 0x1000}, {0x0, 0x6, 0x1800}], 0x2)
semop(r5, 0xffffffffffffffff, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r6=>0x0}, &(0x7f0000000180)=0xc)
recvfrom$inet(0xffffffffffffff9c, &(0x7f00000012c0)=""/158, 0x9e, 0x40, &(0x7f0000000240)={0x2, 0x1}, 0xc)
semctl$SETALL(r5, 0x0, 0x9, &(0x7f0000000040)=[0x9, 0x7723])
semctl$IPC_SET(r5, 0x0, 0x1, &(0x7f00000001c0)={{0x9, 0x0, 0x0, 0xffffffffffffffff, r6, 0x63, 0x4e}, 0x100, 0x321f, 0x8000})
semop(r5, &(0x7f00000013c0)=[{0x4, 0x9}, {0x4, 0x7}, {0x4, 0x7}, {0x3, 0x795b, 0x800}, {0x2, 0xf49e, 0x800}, {0x3, 0x3f, 0x1000}, {0x4, 0x6, 0x1000}], 0x7)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB="faff2e2f66696c6e312f66696c65300007448410eb628d48246359d2c725d42bf580eb415261b3434def3d5a08d05125db762dfd7ca0645206f0de4bdcbbdc2e4fdc6ca147a636e02218d957e486e144035a0dc8ba198e62200a2d3f99667b98735fee051ecabd8260da9c52139e3214db1f171a8a28d0a48a4a7873c223b3d725d307fd8d1d4da9f76211fba5610b239d5b75d69c91e368f04308b526e0b0e75038988f8888d9e2c0e78c1b09996b925c8e03601a6afd3328f5c001b27a1d46a46bb75b01e7cb98381b97d568f618fccac2de96938da88679eb6dd1f29f8a446e65f63037"], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e664fea607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb38a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b8b4417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c0700"/4096, 0x1000}], 0x1, &(0x7f0000001400)=ANY=[@ANYBLOB="300000ff000001000000724b0cf6d25970439aaf000000000000", @ANYRES32, @ANYRES32=r3, @ANYRES32=r6, @ANYRES32, @ANYRES32=r0, @ANYRES32=r2, @ANYRES32=r3, @ANYRES32, @ANYBLOB="20cd940000000000ffff000000000000de4d87900c5e9cf940922414c038bac4502568ad4c17a555904334d03594f001f135abe3347aa3c9aa8b4b114c3313b946832d1af3e6747be3b903000000", @ANYRES32=0x0, @ANYRES32=r4, @ANYRES32=0x0, @ANYBLOB="0000e370ec71967de93b6151890d0c002000f6b9", @ANYRES32=0x0, @ANYRES32=r4, @ANYRES32=r6, @ANYBLOB='\x00\x00\x00\x00'], 0x70, 0xd}, 0x4)
dup2(0xffffffffffffffff, 0xffffffffffffffff)
setsockopt(0xffffffffffffffff, 0x80000000, 0x1, &(0x7f00000014c0)="3c7f1f182629821d105c3ebdfd7495a680aaef141e3f84f42faa221226b813e841717bf4267c317c315135684b65b9c0c55ace6f43aa3ce1a64d9578e39d5a73de1e2db072a0a26b7dab92dd298518523f0616b1094867cb339754f558bceedc24aa98266a0a2fe4279a96703f26cb0a3e3785d378246536d8efd1495a6b326b97ce5166f541e9cbfcf3a403f99172d7dd6ecb9a70649dfa6fd5fa55b6e328ce1c28ebecc01f6b37584487f069633cdc3e278639a9ab868e173bc87e0ff8b2b42f2dbd850a0f8eaf", 0xc8)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000), 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000080)=[{0x7}, {0x4}, {0x6, 0x0, 0x0, 0x100}]})
pwrite(r0, &(0x7f00000001c0)="d0000000000022b3a27e24b3a566", 0xe, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x39af}], 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x14)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107004, &(0x7f0000000040))
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r0}, 0xffffffffffffffff, 0x1}], 0x0, 0x0, 0x0, 0x0)
r1 = openat$vmm(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000200)=[{{r1}, 0xffffffffffffffff, 0x9}], 0x0, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000014c0)={0x0, 0x0, {[], [0x0, 0x0, 0xfffffffffffffffe]}})
kevent(r0, &(0x7f0000000100), 0x1ff, 0x0, 0x432b, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f00000000c0)=0xffffffffffffff3d)
setegid(r1)
setgroups(0x1, &(0x7f0000001840)=[r1])
r2 = semget$private(0x0, 0x4000000009, 0x82)
semctl$IPC_STAT(r2, 0x0, 0x2, &(0x7f0000000140)=""/163)
semctl$SETALL(r2, 0x0, 0x9, &(0x7f0000000100)=[0x6, 0x2000])
r3 = getgid()
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000240)={{0x4e, 0x0, r3, 0xffffffffffffffff, 0x0, 0x4a}, 0x4, 0x8000000000000fff, 0x1})
r4 = socket(0x20, 0x4, 0x0)
setsockopt(r4, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000540)={&(0x7f0000000380)=ANY=[@ANYRES8], 0xa, &(0x7f00000003c0)=[{&(0x7f0000000440)="08e6f820a3d6d07499aa58035debda024bcd75a21ab7b8c3f4fdf7f36b4bcdec0f849d7996299b8a89f603c4b1250975630788b44814428db49902f14274744a690335bb950d8d234d97bc066869601fda30fe1d3823b8a3ce76b600668c0f688015bc4185ac94ff14d35a9c2f0bdb651cf095ffd309300977325d761454069b3dd4a9777f569039d0", 0x89}, {&(0x7f00000000c0)="80a3d4c0f4004553cf93258a0aa0eb088860f2b06312faedebff7b3153d0383df2402015b4c52077aeee2ef9f3b8067f73d70ee1c7b2f624adb3c669c750ad31f9bb286d1668067beef6d6d5cce62af067463d57de769d180426556fe27c90c588202e", 0x63}, {&(0x7f0000000140)="1093db8b1e6d39d9a1012bf1d1389de316b6ed4d851dbf722433f53f25bc562a868bafbf2a6b19fc9416cc57dd4a5363cfd37b4407f273899be25b6e8b4c41a25dcc946dc0b7db66dd14de3d26c540fe21d5a37799f08fa44f77d3265600091d5e9556b7ae53107427d25e916bd64f2d4bdd2c4a3eb6f3a91fef86edeac1ef5fd3626809d9bbbae4a841fa1e327e8852aa56326e664b57329cf4a3df5d90706dcc13e7c2ce2bfc0aa5d3dcec885973035982457c08818401db928b8a9ff653f47b9272c5a12f5c197896", 0xca}], 0x3, &(0x7f0000000580)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES8=r4, @ANYRESHEX=r2, @ANYRES64=r1, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES16, @ANYRES32, @ANYRESDEC, @ANYRES16, @ANYRES16, @ANYRES32, @ANYRES8, @ANYRESOCT, @ANYRES32, @ANYRES64, @ANYRES32, @ANYRES16, @ANYRES32, @ANYRES8, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32, @ANYRES64=0x0, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="71fd5e6445d48e95bfb0acccf40007c37526c8ad78cf9ab4f890c7d8425b8c1144420051b80650fd23b9255de58a3030d1601996005f5f7ed3489f6572c9effea168f2ef7c48b03499d4c425d3d9488d5902ef6fefb79d408c30c773e3b8286bb8f901c8e335e9188a", @ANYRES32, @ANYRESHEX, @ANYRES32=0x0, @ANYRESDEC, @ANYBLOB="e6b001b62419424634b6d0661f66efdd8f4a4a744c5169206983487d7724b303a8ab8d3da963effa1b5aa4bc49e82206056fa7b7cec94e18adafd87c5070cec626bd9988a17e79d601ab6fbf9e9644c2e4bf3aaea87ffbc52dfee0e5aaf541723d235fe2f32a925d87367cb8dafaec0124419cf222a7ea550a5b776b7041b14b3cade9d53b5802c8db314b143306336f0171443c3904d7fef40d87ac61b20f8a53befa4ae3074bda26fc948587eb66308155ffe796ea22c9737bcc9a6e50ee9ec87216d5911fa96afe664c8b7cb136ac5ce142e06fc50c2b3c22b64e470cdc82b64fc8443884120376b3f5910551daabbd9a2f0671eb69b5f730a26328ce5ef6dbe10447dcd92b868fd9dbd67c988bcac3b0df90f5e142e1fb813208d86cffb03b7961f73fa60389d4438bf176dbaf580318d0e640c61b569d38b45d8bdd6096971758acab8e1f15097d96232f08a00de19422e2a5691acc307bc73be6ee50bdf281677bd7d3ffd7206df31366468cb77e78e08f48bd8fb57482a034790ddbfb5458ebb3ffff05ecf5dba021173f402236412632a1e5716369cb2a98fae0233050905add688fe4d649687939530f54fc239a299bb28842c47e679dc3a5a89ed48c676159c2628a1e134b214e3b6f9f5a46c1ddcc5dc673ceed7efa7582876e6fb55dc04d5bac3446a39afae41b33b2cf74037d2eae5036bb335c944d4bbcfcd3b99496e0678469be085764246b1d78f5c10b7c2d8b4dc30a42b3adf2345d136125488fbbd3a76a3b26a7a73a2b9e4ae542a9d11aafc91e3ed5aeec44826694c7b86b71793a0bd4cad4ee3a40dc8535dd708e062a48bfab79300e05c6b8897687bee24a46fce8b04e8200e1e6a2d5ccdf083cf5b3cc41f4685b68ea6a371a4a34630bb68af338b8ab10193ee91c9b14e06afc0fba7c4d18b29ccd57f192e60666dfd0c9c7b11abc720fd98ac82f3fa207ef8a3e025d645411d42a4e242f95819220837d1cc645e6c9fafb1f78be92a9c31528598400acb7947a8bb69190ecbadb0730971925608f4eff38932e7bb5f76fe0f999fa5232aa29542a475c972bbd058e6f4d6135f92a5cb8c8e21c38b1ed7e28b97b2dcb8a4edc3ad5f2d27459bb4067d7e10e4d35b8c1718e3e29a36b096f436829ff81e0d2a75ddcf6ef46082001350bed099fc1cd95f1ca40f2965d125161e771e365a3c1cbfb7752620e3b7b11bf74d5e30474453a530be4825f44543a379d7340283fcaabd0b408b953a18507596a333559cfcac8ace65a6145e7734d4d33f2f6510f8e55e3c576c9c67d32abf7c3a9bb489c62223032f186fa32f3e8fa02b26e31cbe6ecedd71570b67ffc9488570616d65c3b747ec73db56abb4cff0a0112c5da55fa5b4b9a2f9985998dfe1b00dc59840699ac6f1badced1d00ef11c2d6238ff96c4f91929d08e0e9fe647ff3ad702893608f491cd649a6beb28e1a0b70b00ea77bea9c4558b87c4d7867ae58a133e9055a962275b242d4213b4020145a43ed5c003a53f6c04c0b111dabecc03b584acb526754158c11222f8d79504b3399b59d57d00f286188baf9330cce6f8c7d967ce62c9235f9bbe7f86303c1d3ca16130c77e88ed00fd19df72a57983c75846e7927af05277ee34589e87c8b12a84e3fe780ea077992bdcc8daae2bb9beac2c0225d963441d05534ffc5aa23afeeef17af415923036f8aa1070b9b1ca4549ae57e11969edcfbdb845825f04d17e0bd257cac597dd693275cfb5f0af0bc18b06fe92010b748a844c359a79f7fb2bbb58a69cb9357ac60d3fb4d736eec1e5f51352bb08d3d2a4f200cdb1a6d6d23e3d3467dd48dd1ee0f1942fed7e49641dd3d145d141b9a4e4ce491a6e9a9ba33f0f14ea48e924eeddedb41afceebb32985ec99f640e03ddd39a7f76a880bab766fe08b756de7aa088b4593ee0fd660451146e95843135c8d4a2503d9c11588662269263b6ab9c9e16320f183bc79814fe4aee96e888f0162c2aa084e9e1e8e2acc7d926b93ad7cd4967353d78a0bcd29e3423a4cb0e006bb78bc239da2b0f122aa112cfc3f1d4da9680148eca0e1a170fe1f266c02e689e15b858ee64abf14e3d8571ee218ce91ef689115acb8ab86e05b365a22293717d37cb785c42033ee3fdb7dc158de8e4d1eb534bc9684671baa4e603f409bfe9686b4d0772a7bf31f0735426de39a7f6aae814a980ca462f2dd8541490a252c6a55c955c507bec884a77e3ae27402c8d54d47d53e3a76db69c7f98931a84f5593f9bacbe8429ed0be994d05d5f635d282dae61a91e5cf080f58f69883fc04b70480522204567c29d1d0f52fee6542df58a535c6727c317f7e399a0b6e70c18d2f7c3447422cc3dc8ddb93d1faf9a8ec85c1aeddcb25ea8aeec9831738e4d2f7b2f8f5b892d140da46f0d3a8ef921cc008683433b5569a71208da0309e0318c15347555ff9bbf072b56b97111b9e5481c699b2f97ca94de2eb9a7846c046812df610d2be1b0e1943be2f3e8b2deb1c30a0ba91493520a6ebcfd902ab902eef02d02d63fa347240b806fa89661f80a9cf01f61155d782b598a763a22ab08f8a395c668a05a2bdfcf17ae39abacde55393030d7d16512402bf849d67de140284ca117d821b3ee5c9e68e157d83f30d4b96f204cdb6096bb17aef0dea72319683630cd1faf2e5ac532c2be33c3d066879d69503a99510dc3b027baf1e197d0270dd2d9589d76d6c25fba36f2ad8bfed13006b0e5775e7111876f613c82dc84bd9d6b9bf69a638533196014eba0e8ca6cc8d003e4e89a692a7b12f6847bd0122a5794d428d480d766c22d742e6c67bf8bce2b2b67856d3b62571c2aa1a808fa42d55f48943cb381ac7cc0db8080ab263e3fdf29c8426859509e68da17178d7eecb12fc2976b4d6e7e1a80e79fa2fa7df3c3cbc382ebcd6d553e2290509bfc2227733a05d8b903c2241da8c8fcab11cc95e1c18b4f7afa26b6973e1c629f4ecf72c7b768fb4f2ea8157e0ef501b3926f290d1d8c6b23eb1b34ce590dc75bcc48efbd21db6e544d0855ec08224405865dc05fe018e2c82ff8a0f4609b13475372b94d42c6e697e9cdfa4cfa9e9da009d8fcb461a757ac801d4378fb39cf86c80ff42b70a25ca3fc875e1c7004329ae23fe1e6cb1afca0b9c075b40eef7e73dfa915dd10d526042e012cc89921488361244020e1e8308ffc43ee5da6c714e55473d85ec6ee6fdba739782b35d567c157a2890b317a3671ef12295b69590cf40ff956b96f6f85526f7c67f50d7515a510af4aec37b305d2c1b71590235aeaa8822c0908f8411981868d758195dc016255e26b43ddd5356f8397a152d74f4d68af79de7443cfb6302adbbaf27a3104147dff927dd2617c8fa23816d17c9e20bbb58160560ff4cb0e6b0f26b43d02a32954d7e5dfdd82f0cccd7962e0634245f00d7e00552549b93cf40fc7b8a39946bf25a8ace97e1a974ba92af8f295d01c7fe032249dcc39c90b9bddbcac5a53110eb69cc9634cbd7dbd39b89f0d0d4fac55b34a7512b3eac92bfb7ef2387241b24ed966467f2d4b6f7712f1b153828a03e053883112fa3a86a665bfa2a99498749fe426a4df0fab66ce4cb9c0e88c715844451540f4a6281499f7222b897055f9f5be7cc127bd53581fbb9af879eb92f0e5b83b06dfc1608786aa287a430451c752680efa35345fc686bde9e492ab3684095d98b7ca8c7112e8f13014622e57d0f98fedb8be9e0b111e8a7b7a22cd9149929369b0c23f69cdc9fd205397b81b401a38a8442b1a91df997cc14e49448e9ea04b7b4a1fe5b53bf48ef1960aa85fee56bca260c1e174c3b363aba606d40539948f98706451796ac5f4bea806e89783afee0a3f422a375db426cfab16b922afde57a65aeb3eba12cac68fa5a830525432b274aa562a95b3f8239c7353d3868805c092b4894514e258450c3dacf2202a0cb94f3d93ba52e706c3a9379ee2631e3dafd85214816b4e6355abd631ecbe0c3a8327fb0b0fa3c9cd596dd63a78eec8d99ed459d2fce3578620e862919b34155209f85be8c8f1f084c99886200a19371cc6416cc024a33a6ebf3514196813f8dcace6a3e945c2e301fa1a8b7ebb3a4ce0f2e6a7272d235d819bf2708798e2375a41de56e875453b8e8113b076f2a3c4c34742b13c6c33136527f142e42546f0f382b6bf27f51a25bf8c30446d5af5c89dcf9b17a39097a5ed8f1e9e2137d91ab694211542c5cd080cfe0a9024a5095239304f604e36f646c8c34d83e9185d4b03b53c86376d2f86df43286f08eb754262b1b3cacf9fe3d2a807eb5416ed1a46aeae6d7c32a4dd5f8b823dffd37d0ea00b3edeed0acd835d82e4805692f2f372d5da783c9ed2ff29881bf5b8e3819814c085f939aff6d8cec44c4ca5497b2a3ca240e554871e6b5b6978d23be51b9e1568db3b0b55234e9756436c411ab97fb91c436a217b6b8b718727801ea08bc0573df1ecdb4a125a5cda2426a3fbd3b52f0b5db40d657b8543191b936c5b1a88c7ef9547bd1748fdcbe5b7fc56b5c8baf62cd14177e59b33b9d04f0b84ee1baa504e8b3a814b99070316f0a19272c52aee588b885ab6afa2b6306809416bb3816cad2045e354ab9397062c52a24840e5238ae2757a7ae01590c2891296c877b6fd967eff5c6beda92415d1da321767d9ccfe051174ab4462973654aef1291b26afd5461ed1cdac0ea3544a2b518f960fb4a1d2c97b34637a58e4c9ef17c09ee935d6cbc7342f506344aad0f260bf9f78cf0430fd35f56e9a9b00c4a076b67e2d0646aaceb818cfc5a80a555bff7b0a247ed8bc68f4957fa2e7baeec732fdb20a5f98fbdb64edf7eaeddb28b647fa601ce99c8fcedfb6870a48d20c64017e24078f2fdad1e7f12f86093881947e3d1d61ceffd6bc6e3e029c89d6a179066325ec011386fc342e24c60e7c08fd317b204ead120be13e33b087d90d235123d891d94deadbad8b762d599ae722191addc994ec04a86bcc93b2e7aef37bb7b4ccf6bb0f68aa804d3d2751861369b14515488c0d30779c9a1db50543e9502654f2cf290336dc43f75c61979bb51aa25fda50e9f0bd3aeede76cc560f87f3fbd1d578af6c00a48706016cd1aadec30d1786c8895f77387d7afeffef471752d8deb5d58c9315b3df6ab2281377fb8b47de758afb3ca652e9aca6239c48058c39dc550efc18f0ad96e1303351a210420abdebe133a3735881d49e9cedc2bca07402ed44db47253ecfb3343ea576a43c84363d6340a6a598b50cf34752d4a08ec501c057d6e0871b19278fba6e28fb5dc8f92404b1ac595077e9107936206b572c23d477f303dc1d68954c982b0d1c263fe58f34ab6215da71f4498e755665cdd9157192914ee2317aad44794e1a67c4cac338ba126660b635bace89c05889295952d8880eb7fc880325f4d365a8f91ab930955e83ab6fe3fd88dab00d2b33bb47f4012390cf14471c6359f1ad6ca04a7d0e689eef656a962387626d597bcef494e74a9e30632b763e8fefaa4352de4da786cfdef1b242ebfafd3c7fd4211b4fd672225df5fcfa366867cdbf64c78089dec87265c29eeaba29cb45ab8c356bff59ca5bd416096f3b8cb12402e19793ae0033a16614e0772274c422ced9a53a45b33bb56a6a87517350cedbb8ef5d29fc8e400ce5d6ae9def10afa632a12816c390c6ac2f6449a834b564e0fa661248219f2783f1608f92aafd1f1d56e1fe1a8028bd0996c4da3de6c26324cf97aed7fa4c1632d2619100"/4096], 0xe0}, 0x0)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x1fd, 0x0)
ioctl$BIOCSETWF(r5, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x20}, {0x4}, {0x16}]})
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r5, &(0x7f00000000c0)="ba58853729347c034ee03ae84b60", 0xe, 0x0)
r6 = getpid()
ktrace(0x0, 0x1, 0x40001900, r6)
getgid()
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfeec)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x1b45)
open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
mprotect(&(0x7f0000ffa000/0x1000)=nil, 0xffffffffdf005fff, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @random="feffffffff00", [], {@arp={0x8035, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x1, @local, @rand_addr, @empty, @rand_addr}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x1000, 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCPROMISC(r0, 0x20004269)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCPROMISC(r1, 0x20004269)
dup2(r0, r1)
sysctl$kern(&(0x7f0000000000), 0x8, &(0x7f0000000080)="74cf69d496e24ddd0d50bf4971bfd6894170c43b982d8be5a272ff82133be60d5dab7e094d67", &(0x7f0000001080)=0x26, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001000020000300"})
r1 = socket(0x18, 0x1, 0x0)
shutdown(r1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000000), 0x4)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto(r1, &(0x7f0000000100)="589a", 0x2, 0x1, 0x0, 0x0)
close(r1)
ioctl$TIOCSETAW(0xffffffffffffffff, 0x802c7415, &(0x7f0000000040)={0x1, 0x0, 0x0, 0x0, "81188ffa9a0a38e8f9be173b10ed2676def6576a"})
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x13, &(0x7f0000000040), 0x4)
listen(r0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x19}, 0x4, &(0x7f0000000240)="6c9c9cf7", &(0x7f0000000040)=0x4, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x31}, 0x7, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b10005016000010000001b0007000000331c13fecea1055b238ba5cfc73fd3357ae36caa0416fa4f376b36acf00b7804be381e4991f7c8cf5f882b2940e1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d60200177d026ba8af630037282102000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c50c00200200000d2300008abfff2213e8f1e598162cba5c08020208a37183f8343712051eeab71d89e00004", 0xb1, 0x0, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f00000001c0), 0x3, &(0x7f0000000200)="deb75447", &(0x7f0000000280)=0x4, &(0x7f00000002c0), 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{}, {}, {0x2}]})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x801169ac, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0x0, 0xfffffffffffffff8, 0xffffffff, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f0000000200)=[{&(0x7f0000000fc0)="1596693388d4e8bb6df16e29f95a81fb895de567ab94e94f340ccf9b5295e0b5957b71442ecf83be94957b0862d96edb05ace734c4e80cbef22f5d8ad75885c09cc69c907ee5dc2f439aaf0f2f", 0x4d}, {&(0x7f00000014c0)="e503c61d80583de86ea2d148cca602eb0657023464e233112274b781731ade6ce55d6f7afec296ded73d238abef715fafd4878550b5ae5e0322e293ae4e5cd519280829bd61933159593bc331e33a52b5adadcf490f78b92ad6bbf96cf058bdf43ff7064a7585ffb497f84be4ee9bb8e49007e0dc1c79295cfd07e5a3c4505933bb40cf0456686eee9bb3309f16aaa5fdc16357f7633d6c343fc4234dbe18e1666caedac66c76d37692d1e8ce408f0e1e39e73797387106a9259ec87d601b009746350ca5a9ca0274690b50560de53220fc2815a78f45d277d5002d3f6d428a60fee7a15de1de6861685d366b18d5017d9d3e272705ac7b6d9b2a2035921ad860eedc636c7d1341a6c9c9cb2deb630b4dee1d54d3628914fcc0dc5bb93a1457844e5dd8808e4aeede385f70e080af31871a7ab1d9c9a78b1ed288ca2dcabd5fed2826f54265c8f75b487b3766f8fbc0527319298927930707dbba165bfa3c5d3432abd55fb9cdb41280eae16c186594d85a55842264a9b5421731538d9b392eec31ae109fd9813bc067086a8db7fb046ddd04daffb3ce94a4843621a33d5d90f99178b375e2848802e1b99b3b974d7c53192e469daa156e5667fef2ec5653c4c896a9280617a7de3e5c7f47c35342a81f72e4f9c42140017300b5bdc624f4e7c3be8c91e0fb87590d6e9ab1d547a8d41319e40837678d23715e0e5b0a4da9121eae6dcd6792574aef12d49ece93d6fc31d77214f0bb08da10bdbc6950c60b1ba11c44980e21f78c55c9ca817507356dbf3b62a0b588ba243835e8d59e877e5f8c459dc6f06a9596be14ab364fa616b71c56f12ed3a028cd3106396e2e2ef55a208ee4f12f1cf619f134e046d0cc28fcb2a465e22372eadfdb5e08f22309ff23eaaf5205705bc29729244fed42fefae8f363c70ed64e37845eb2faa6b8da8d143c75f6b23c92f527edd0576814ed90033ea760b1c2eeabaf5ebedb269b845541ac4a1f68bc13a9c9f65eb3d3e1781f4864c08775448f30f4cb4632131c23ac76636031f9503c902ead364154d460caf2b9371673f52e57bb98c181e86ffe291f30c1e4ba64be4b03b65336dcab894bc75bf5d5f505d8a8f0708ddb51fee9a47e3276e427becabd7d331c1e126746082c07c921ec1743f46f5165f6f9eec781a66ca6dcb08283f12b8c856c53fe7388ebecca7cb4dc9cd60321d834e0c85a8e8795f8ba6dabf6f1df96fad15749813a33483de3b853b821f2dacd90a970e513ad3b108cf6706e61c9182adef982c24b764e65513bcf17e6e169b1fe7da7f05ed8112a691964af4582ccc9ea61afa4aadf97459f8fe9df9a0d03a7e6b362514ac445da8cc06b8092be3049a0473ef1192dabf1ce41cd65d8fbe137b2317f2aa6fb8a12e1275ec5baf15bd3a0cfcb17280a53683b21bd2ec134b2d357f49231f1c49421ea8c6679a66a9d526cbd8fbd6a7c285f0882648a2ca16bb5561322ff5e046df51dec0cada6d75cefd5aea53191c3e12760a8490ecc308df8d9d4475c3ddbda945b12e7aeacf1cdfc27dc41979ba4c050bf04281fa762799ae06691a785979a5a97d45733e7c51606670384af46c06f180dab18a4c1028d5354222d54aceb5f8cd5f1da4b3487cc954f05a0b652d2d8adc1327f2a92aa0ef1006d70f121d7cbdc2c519654ae950604e83e23ceb77b2f02493cb99d7c7698f6281890950ecb6e7c46ce9cdff99b31e3eec77f8b4e0d78abcca03356606e0c394bc3513e24f3dd0e073e9d9ffcf210311505306a03aa6f4111b765865a19b4567a59aa582b54804a2a77b4a246af051a4b7cd22245206bf4d3ad6b022905197888f841e50f16b647c7c4a60e5412f01f99379b3247b2c4e57a627b4da2722d9f02e1c251b805242320c0e4e92d6966cf5dbe35124ee702e10850314f5a45c35fd497ca7ab6dc76a50b13c263136db5b0678bffdd7ed18d605f9dacf0346842f16865fd292d8937433fcc65c612042eab65bb783570b4b48178fd35deba95fdc39f3ddd325cbf79b56e53b660561f3f6b44fed109ed1e923890f8fe7c57d06d4ae5b098b2464d63e97fe533ab6d03616a5d7199cad8470952349a76ced2d458c28d6fac9338791df5a23d1ea52f7898db22ce959ca463592e39c656748156b41eaebcf58f4224c4c85a8656ab1916d3aeb82a0e4537b2aa1fda04575eaf4a56fbddadce2ce68b0ae8c53f3e0db0594ec9bfc7f95b02bb188489853a7efa354ab2510852422dc1d5a60cd928364f0e45ce5ecdf8fa6c0b6d6f44ddefa61de03cceb3eff026d6d308fd7d7ed9e7a9e7a8201d545524e6df3b8f376fadef047f130bd39a8b4de1dcd6ab5c6c4b53d7815828a01da78eed1eb097fb88cb21f6affd087fbd17a6014b491fa4609be2d07882259fe0d578e71d0028b986636dd2dfaa2fd1e6a6669177692a1ce82ce6317dd6d45e7a6fef72b56571848899f4bdfa66b59c8ac646a637c3df441ee5bb46af79a7d83e160f760126ef381167a14f2e79994893cfb774704f857fc02af519876bac4cc28bbcfa71271e051413d311a21d0bb748bf0d00a63ee0ee66e440e9e13e35b35d390c2a6f36d7890895ae7bcc828a9d10f61e4edfb91fc3272527711a665fb7817265ec9d02f76d625f824ff364b0280916144eec5597d70980265522fa783b9602355732d7dcaae870f2ecc36021285f39543bad16f343dd7df202049eac437eccd300b6c2510dd7a9fdc5f5105b0a726f6f841131d158d071f89b6d6f5ab6b4cf668d143c516fd5afe638a3d856bd5a5b8c455dc21b4c8be976cb988011bbc1c415382ee7b8c7c9d8515e847f3f4a4b939d68a9f8a54dea5ebbf8ec95cc7104b046fb6ac94ccc89ed9041446318d5b57072ceefb68532fe81bdb08e89ce9829adacd606a8d08e7e3c82c576c2516cd574c20a6e5a970338cf80d6730c7a8d5113974419f8638b6525c72542a3cb956fe061854854d91497adb52704a7b55922ad9a81a80de2d6199ba68e2539151cad00b48aa1555d7dc40d68a923a21cea364e5dd6f90c3fac3e582f70ac2ec825f9c2f8c0622bad1bd8e6314c3b9d2fe911aa4c1f4431a0788e4c78274cb6efb4a5b0fa94d65ccf2a360de8fc0bc37f28f36ccb7af17086bf48769fd8d82c233ab6eba6498601a9c039bd59ddd0c3e5bb028f425b70dd098aea7afdf5de9463ac370fa5f21eda4415031dafe2a6ad1f4ce1da80bc2931f43e091c555bf5e5058988aab182447a6dad586cce18c369f2f9ab2e579fdf18700ba65c548d6aae5bb53e803f143f30c5b8bb547df0ac5b10cbdcc4f7776fdfdf2f9d2d7d7b3d38a8f54397c9cf17e93c7261107c669eea7bf9bb7f5672ac379cfa7c38fcd3909df05918ea221ad12d8db3010ea52de9d175e7a85a0b35b1bf5a6191972f0ab63f5965a9435ef405e9ea96807f4200aed1e2155ef5ed580749b4a5a187520ddec62a30df3ff92bc863f4c79da5cd929dac6e71271b9d8797cdca3179b85dec50cbfb784f17bbb7c09957803f6ecbc2d34057c94cfc32e92162bc82413caa2d3e01fc44f466baff726420d708dc7cf2948ae7e2791a006cb46fd81bcf00d3a50301b781bc416e91edfa9ac4a001a7b2bb40abba48ef37c5ce95da335d3ea561f12a174d553773b561ea550bf51d17ed1d8c5a1d7953c86e0be8088f911ca3026feb956c5f6a2d9170051dc97e49265a0109a9adb59a93248864b5b6600161b0c9a7b0217a2e3e4639316a8b7c39f97b6d6f2619f6b5b028c3a1abfbef83d42bf03b3e5b6c6a64798058b29321127d921730bcaa89308778ec69655fcc479484b46a775205c08b35bce05e4c5366ae66b5841f5cee79c19139ab4a0e534433f2cba2c91b71dba64eece54b8e4ee261718e7da621c69ceea4b3f03585e85eef77e19b76518c45cca504ce7e85788288e5ca5deb0def30760f2112ecaf237103c9a9743f8d9363aee6c9739a1e19a7fcef68823aaaee87bc601274a9d34b3fea7fdeee8b5295ff38bdd7786613f0b71c7635d6d529cc9782f4926b754f76e312f6818138912d2f45d738400d0b9d40ff59bf8f388736e01656d3644326d5d3952c4f16833258771cd79ee6020e233e2a351abca16b5fc1be0d6eb93d00d7c0b76419c91464aa35d9ff69896a57506d84808711070b1053e1c4496f785fc0ae8dbadab0360042430faf5218ebd67048ae336f306422c16c332", 0xbae}], 0x2)
writev(r0, &(0x7f0000001480)=[{&(0x7f00000001c0)="668ebea2a6d10614da47d9ef461a1757967925bd7b3d418388532c830ee0dbbafb5b5a7f35093e", 0x27}, {&(0x7f0000000080)="b4b5d10a8b25157dcf64c002c70f02f42e10a46c2eb46f96e27cea8bb8ec40aaaf98d019f5c505db9be5859e3946ab7b3cf86b8b01761e1138566b56c52f330148c32a6647c1eca95fafadc8b7c6c68a45474853c677ae74a33489807d00ed6bc6d8ee02da08bece44226f570b87683c9501a8b7a6e64a42055f712b67b7e3b20a01dc0e092abce9a04b1674ee2b30151847de4d9d6d07af8ad206914dd74b58642e2aeedea7275649aae4555ddec5c4427ba11d46d7bba8bebe9940861c0af9cc2e73fea58a2d6f34414f6027473dee4a5fd65ff56f47bd4a20b1b9cb213ed7ce27d570348acfc5e397728cb7a841a9ea8ef36260e76d", 0xf7}, {&(0x7f0000000280)="ce9f97e1d72835785b27339046496cfab6fffe87c260269d86df477427f075cf16e705bb5e296889f2280748d943d3f9116a96474a52b1ba348f6da5a02b9f7f77888c61db813e69201407a94b9263ac23301581ed91dcc0fe5b731a3eec7062333d0722788ea17951cf3fb454595df2a242b40629ea8602fdbee0ac1d244dd492ebd97b9e416d0153be5814640db3c67607b3aeb10207e0a90c7c9adf0f7d6d7a9fcb344c7f2da91ef38d49caf467c29f994799c1731fcfc95be0df5b49678c6db9e77a873e5d48a1d186af187181e06c462cefa9264bf0b2349e280f3c80bb2d9b4a80804b6219c223ed03e4f714f2da770d097c", 0xf5}], 0x3)
r0 = socket$inet(0x2, 0x3, 0x0)
close(r0)
listen(r0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x1}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}, {}], 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x2)
close(r0)
sysctl$hw(&(0x7f0000000080)={0x6, 0x12}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x34, 0x0, 0x0, 0x6}, {0x35}, {0x6, 0x0, 0x0, 0x4000}]})
write(r0, &(0x7f00000002c0)="bfa80748c4b3ff8918915dc05e7e", 0xe)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x16, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
getdents(r0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000040))
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x202a, 0x0)
r0 = open$dir(&(0x7f00000008c0)='./file0\x00', 0x0, 0x0)
fcntl$setown(r0, 0x6, 0xffffffffffffffff)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
chflags(&(0x7f0000000000)='./file0\x00', 0x0)
mlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
r0 = openat$null(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
lseek(r0, 0x0, 0x7)
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x0, 0x0, 0xfffffffffffffffd, 0x1000200000001})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f00000000c0)=[{0x20}, {0x5}, {0x4000006, 0x0, 0x0, 0x7fffffd}]})
write(r0, &(0x7f0000001480)="23295428e2fa906bdf4ba040352d", 0xe)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000090000080007", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x0}, {0x18, 0x0}, 0x0, [0x0, 0x210, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4]}, 0x3c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x35}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0xfffffffa, 0x4)
bind(r0, &(0x7f0000000000)=@in6={0x18, 0x1}, 0xc)
openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0x3f, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
sendto(r0, 0x0, 0x0, 0x0, &(0x7f0000000040)=@in6={0x18, 0x0}, 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x7}, {0x30}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x5}, {0x2}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
sysctl$kern(&(0x7f0000000000)={0x1, 0x31}, 0xc, &(0x7f0000000080)="99511c2b75f3b4c6fd90ff3306e44becd5c438b6618fbeee8bb283297ba656a237983783bb2f59ab6e3c07f6f943518078bec397ddc86fea16f84c76be11455efdbd8afc5920008df505e5bf079b75c09e35899dc239254aa4e2c9481490e49091fdeccc5d68e7f1f84d1f57a223eeace476eff75ad5fc7dd5a956e8fd49ae5fe283cfad7a4085f46fe664af7f91e9f76dd39b858ff5eb226cece107f54c9bff3674959795b2f8b4f05347e4a28e140d257a426d52727c794474a7dd238febc2f1d254391d08", 0x0, 0x0, 0x0)
syz_emit_ethernet(0x7c, &(0x7f00000003c0)={@local, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x6e, 0x0, 0x3ff, 0x0, 0x62, 0x0, @empty, @rand_addr=0x1}, @tcp={{0x0, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, {[@sack={0x5, 0x2}]}}, {"9323c0c95fc3b021185197c3bd873307f24ab82bbfb400ee84af0940b752962cf6bdabdd6c6db8f28c993d8bf3a9df58b39a864e1f918336a15c44ae4fb919d88052"}}}}}})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000280)='\x00', 0x1, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0x0, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0xb5}, {0x0}, {0x0}, {0x0, 0xffffff6c}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x842)
recvmsg(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x80)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x60}, {0x3c}, {0x6}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xd, &(0x7f0000000040), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
dup2(r0, r1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x35}, {0x1d}, {0x6, 0x0, 0x0, 0x1001}]})
pwrite(r1, &(0x7f0000000300)="977fffffff000000000000000000", 0xe, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VMM_IOC_TERM(r0, 0x80045604, &(0x7f00000005c0))
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fchflags(r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x4000004000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x3}, {0x87}, {0x6, 0x0, 0x0, 0x1f}]})
write(r0, &(0x7f0000000140)="da9e028ef605c40df4cad6e65c5e", 0xe)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0xc}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x25, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{}, {0x3c}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x66, &(0x7f0000000240)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "979fd7", 0x30, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, "f14797", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0206923, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x0, 0xffffffffffffffff})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000000280)='\x00', 0x1, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0x0, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0xb5}, {0x0}, {0x0}, {0x0, 0xffffff6c}, {0x0}, {0x0}, {0x0}], 0x9, 0x0}, 0x842)
recvmsg(r1, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
dup2(r0, r1)
execve(0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x2000000000000090, &(0x7f0000000040)=[{}, {0x2}, {0xc, 0x0, 0x0, 0xfffffff}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$WSKBDIO_GETENCODINGS(r0, 0xc0105715, &(0x7f0000000000))
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x26}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
munlock(&(0x7f000081d000/0x2000)=nil, 0x2000)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x8, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x0, 0x8, 0x102, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x6], [{0x4, 0x0, 0x8, 0x7}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0xfffe, 0x1e, 0x3, 0x1000007}, {0x7fff, 0xffffff7e, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0x5, 0xe59, 0x10000000}], {0x6, 0x2000002, 0x0, 0x8001}, {0x0, 0xba97, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x87}, {0x40}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@local, @random="a26c7d46feed", [], {@ipv6={0x86dd, {0x0, 0x6, "a0f54f", 0x8, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="588516667a6bd66376a4124c4519ecc2", {[], @udp={{0x3, 0x1, 0x8}}}}}}})
pwritev(0xffffffffffffffff, &(0x7f0000000280)=[{&(0x7f0000000040)="d73160174caf1fdb37b55715656e4718635a8c9994734f2d218868ac2881f1d6c2fda7ca6e6de98059ba14f943f52c65f327f660df5da2a156184864128ec5ae45372aab309d4db3ea499c41662d60a6835c4209f45a86dfd0b571ee93a26215006b35a3c4ca46a4afcc55d5d4f613b33240bc8c9d7beb68f43bf760fd171733787444b15b698c9c75c5b3e41882", 0x8e}], 0x1, 0x0)
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000040)={{0x0, 0xffffffffffffffff}})
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b10005016000009005001b0007000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff3728211ee4fd89720fd3872babfbb770a9f5a872c881ff7cc53c895303b22f310b404f36a00f90", 0x85, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xd, &(0x7f0000000140)="e803f80000e2000000000000", 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000100)=[{0x48}, {0x14}, {0x6, 0x0, 0x0, 0xd7}]})
pwrite(r0, &(0x7f0000000000)="53ec371a410a3035029a0e000040", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc110445f, &(0x7f0000000080))
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000280)='./file0\x00', 0x0, 0x1504, r0)
poll(0x0, 0x0, 0x3fa)
execve(0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x1ff, 0x0, 0x0, 0x0, "0100"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4}, 0x2, 0x0, 0x0, &(0x7f0000000440), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x30}, {0x25}, {0x16}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
syz_emit_ethernet(0x335, &(0x7f00000002c0)={@random="3b1bfddfc55e", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "39789a", 0x2ff, 0x3a, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, [{0x22, 0x1b, "baab069918fceb10a2834ec64a9234ba406537d40bb749557e944e71d6220a9980542460871e3949e0d6734a6b44e766dcb876b9674ce3284ed0a56fa52654b310cb96272ffb0b2987e3dfbd0c3192d3d7b01ac17e5e51e5c92b1ab6145131712e6054872a49ff311de86cacac77029999d563001cfdddfccd0d3048572e0eaba33c4352cc7425b399631cb92e6132b848ae51a0404a91ab0b39a7ab256768b429e39b075a8624efbc004eb8ee12999af2c8430421c3d6d62561909ad0920ddc3a093e628f687511909003a0f62b7547f069ab3c7de7f33170"}, {0x18, 0x16, "bf6e9d99a92a1092f0c265eaff76567a7eb85d2c65a531a1ab0fcf67f629a76d35f9566eb32a932bde5ad97e81fcd2ef1fce21db0f77a3b1d39d2c44c7f6527de0699e90952c1dfff8e0e2586f8119de4f11279a364d8b27615b8a9767224bf294499ef085ace99b9c59fee1e6e0278782a59357ba9a682b24c8b8a1e4603f141ba847d3aa28ba611d24101534a3975a58ab4ea1e6dff1347b408ed4fece602df539973db4d9b767263bad08043dc863baf76b"}, {0x5, 0x3, "746465bdee7e1d05c7c8150e521764e526276e4a53145752679eb2"}, {0x5, 0x11, "8a0584eaa4cfd4299ba11f4c32c2372aeacb1f262bdc2b9eb7b6d3eb98f66671ebc30627c79c5d0890d91bcf879de471fcb195056febe7934d41b062bef35b9fd7872ff912d18f6544bfd76c613a3e71f43cf88037f9dc2a51eff86282de016c0e277335d8f422a2d26043b153584af3fbe90494578d92880fd5644ed808c17696cd883fb564b526f28ff3c787"}, {0x4, 0x15, "b2b1e9c9a7648d29c19857ae03e9cc8b81c201422dab2c6fb68678cea7167eeb7694f95580e01bde62720fe0b17282ec56ad31daadd0ae2804183a1551b3d83f43b5c609aeaf50a401bce0b6f7e01dad2a917b74858d92274a1b4d3dfe532272ee8152f5b6e189946ca4fc2e1040dd829b848686b2bb25dd2393bf0995f937b2fced3d69aa8d9cbe1d292f8f445a32234d34d77f68e2098248114b8fca8da4f2675a7fe40b5a311a666132a81e"}]}}}}}})
syz_extract_tcp_res$synack(&(0x7f0000000000), 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r1, 0xc088444f, &(0x7f0000000240))
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0xa, &(0x7f0000000140)=[{0xfff, 0xdc, 0x3, 0x10000}, {0x6, 0x5, 0x81}, {0x5, 0x0, 0x4, 0x4}, {0x6, 0x8, 0x8, 0x3}, {0x5, 0x91, 0x0, 0x8001}, {0xfff, 0x2d, 0x5, 0x40d39}, {0xff01, 0x0, 0x80, 0x10001}, {0x0, 0x1f, 0xac, 0x6}, {0x1, 0x0, 0x1, 0x1f}, {0x1, 0xfb, 0x4a}]})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x14}, {0x2}, {0x4000006, 0x0, 0x0, 0x2090a}]})
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r2, 0x0, 0x200000000000b, &(0x7f0000000000)='\x00', 0x1)
pwrite(r2, &(0x7f0000000100)="0000000000424374cfb5f0b099b9", 0xe, 0x0)
syz_extract_tcp_res(&(0x7f0000000100), 0x10001, 0x7f)
syz_extract_tcp_res$synack(&(0x7f0000000040), 0x1, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
r1 = socket(0x800000018, 0x3, 0x102)
dup2(r1, r0)
getsockopt(r0, 0x3a, 0x12, 0x0, 0x0)
poll(&(0x7f00000000c0)=[{0xffffffffffffffff, 0x100}], 0x1, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000340), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x1)
unveil(&(0x7f0000000280)='./file0/file0\x00', &(0x7f00000002c0)='c\x00')
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
shutdown(r0, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x54}, {0x7c}, {0x6, 0x0, 0x0, 0x16f}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f0000000040)=[{}]})
ioctl$VMM_IOC_READREGS(r1, 0xc2485607, &(0x7f0000000080))
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x4}, 0x4, &(0x7f0000000040), 0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
munmap(&(0x7f0000ffa000/0x2000)=nil, 0x2000)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000080)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x140}})
shmat(r0, &(0x7f0000ffa000/0x4000)=nil, 0x1000)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "adc0d81bf13c90000000000000000008000500"})
readv(r0, &(0x7f0000000000), 0x10000000000002a0)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x18}, 0x4, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x7, &(0x7f0000000000)={0x7fff, 0xf462})
mknod(&(0x7f00000000c0)='./file0\x00', 0x6000, 0x0)
getgroups(0x10, &(0x7f0000000300)=[0xffffffffffffffff, 0xffffffffffffffff, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000002c0)='./file0\x00', 0xf)
setuid(0xee01)
truncate(&(0x7f0000000040)='./file0\x00', 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57d7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
shmget$private(0x0, 0x3000, 0x0, &(0x7f00004df000/0x3000)=nil)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000bc0)='\x00', 0x1, 0x401, 0x0, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x3, 0x1010, 0xffffffffffffffff, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
recvmsg(r2, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x841)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x40000110, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x20, 0x0)
r0 = semget$private(0x0, 0x8, 0x10286)
semop(r0, &(0x7f0000000080)=[{0x3, 0x26f4, 0x1000}, {0x4, 0x400, 0x1000}, {0x0, 0x7f, 0x1000}, {0x1, 0x3, 0x1800}, {0x3, 0x7, 0x1800}, {0xe0b227ee6f4a644c, 0x22f, 0x1000}, {0x0, 0x4, 0x1000}, {0x3, 0x6, 0x800}, {0x4, 0x4, 0x1800}], 0x9)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f0000000180)=""/169)
semop(r0, &(0x7f0000000000)=[{0x1, 0xba3, 0x400}, {0x2, 0x8, 0x1800}, {0x4, 0x1, 0x3000}, {0x0, 0x0, 0x1800}, {0x2, 0x2, 0x1000}], 0x5)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000080)=0xc)
chown(&(0x7f0000000080)='./file0\x00', 0x0, r2)
r3 = getuid()
setuid(r3)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000040)={{0x20, 0xffffffffffffffff, r2, r3, 0xffffffffffffffff, 0x22, 0x400}, 0x4, 0x8, 0x93})
r4 = getpgrp()
shmctl$IPC_SET(0x0, 0x1, &(0x7f0000000100)={{0xa0d, 0xffffffffffffffff, 0x0, 0x0, r2, 0xe1, 0x3}, 0x0, 0x7ff, r4, 0x0, 0x8, 0x3dbd, 0x2})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000600)={&(0x7f0000000080)=@file={0x1, './file0\x00'}, 0xa, &(0x7f0000000100)=[{&(0x7f00000000c0)="a90947da63a7415feb6ca33ef741b21779faa7e1", 0x14}, {&(0x7f0000000780)="2d716fbf1a73a0b6b73b1ef21891de2ecb599ab11644fc3beb6635b8c246ab39de020556bfd2b6a0a308629bc95419c085e2cf2871724b894cb1a5608a6c8e19dde1e1fdacadeeab6d07da031bc776069e5271b968c5d9c30b1a444b811f781ab9d767e3013714ceee856a104cb940949155d83142ce3d3f4b287b683d7fbd4867b8ece0958c22edf3df571ba06276670a0e60b1553e02bc22bd9f70dc06d2ec7c5c7931b5cff9fbc7ee159e2b8613b53071fd74bfc347cf0cc77540d1df416fd2d71f964d8948cff5d265688352f9a2d72fcf8e2c40724b62869ce22af750191a0900076b4df7ff22b94dd94063cb8fff783ad4a7e304423bf9b6020ca88acb2d900ba344eb9138d05d39550434b2b4c313a41fc24bd8b0c55237a4b28790f61ce904e4edb015eaceb51b3878d176e16fcb6725db14c5c79afc37e324be8f6ae58129b236b7af47b530f1c484f4f816d827b4017ad3d785b6f9a506dbc1b5df1ecaabe8eae1d4a6a96c854863cbc8c4a6bd9d245bdfc239ac70b0be9ee7cf5ead63c862468a0ce5419623aa60c2b03e4e7d5870f056bc51b17fe93f7071aecd064e1ca76fd08590f045bc2e007943fd2cc3095c0467a3bc29ec865a671b49005b853bd86775d4890f7a4b4d4b4f3dc2f63cc8fc2356c373e673dfc9784ce32cb23cee7ae0cfef7faa02860f9d7526f37194c0bb88f18154b334bc1496e2aab006cf75a2dbd0c831faddb1d8fdb5ed2b8b5fae99965f2db5453eba61fa52690e54b43890ea90065b80a7821057724f9740d8897bdf05d9709faa8540d3307bada1b8bed7eccbadda70a3431147cbffef55512567a97769c7c1afd7d2077147268847e7ce741a8a8fc1d5ecbe97baf08139b87cc12de61dc00cdf25478e0f05a7c6ab145cf7118065420e0b7a12b7a755417d8b3ad491135996e6f7ae17eedc8e1f307a051ede022a588b830407e6774c207807846c489491ab34903e4d7894fcd594a60b931836924194fcd213a7afdb5533b77ea6a5b420b18be388cc0997a47ae21c5d894644b6523e8e802beeb3ee3b12ee44b9e28b123d4a7363167061a34feac6768b81ec4842c9d3e89baa98db220343346522a0d4f8701d83b73f25c003aaf5fc22df2f2288aba73eb3f3f5413e234265d15cdc68e21c8aac07a78b69a040e0f5c9d605fc29ec83c5a39ebf7b26dc8d65baf10de9d327a650bad2972d7f60f569533bb03924fb04db07ba250a3a2d66aa1a4dcb4fe2f8799f4628b10d6a3d9d6a7242d1eb7528189e1904f074732ed0ef70bcd91a6179f23038a2218dd66b30c2f184d9c88987ab7ba20f04b4b6d432f5d8f663b824fe925ec6bcc59617cb60f16640ee3efd307cf409153d3aba72b801eb5551e202f2529ab9e3065d3e1e96fd08b38af6b7d436434b7d47e621d3530db34597f5353c7dfff2d9bbb93c57ba5d153e737b57f97873dc596d8d665946cade2e3e1634c8233a03e4aaf3615ddbabaa3aacb04f44bd69d2f5011bfccba71953e652169668741f8a9620a77e2d9e2234c0dc866b682c28222e9bfdfe61fc68e64a2ab830e60119a84e4fc54b9c7e6836832207c684ffa406a7fe238c2efa8cc980f0f578de5cff2e70c979be72be513b20a94bf76409d11295bff1e5f4b97c6f4cb27095fb68ebce94b2922d3105bcba1d9749bea58b3afc389177dffbd3da3a5a4b9a78615ab2ece9368dfb9da3a7106385258e11bc2a18b73dfe4e938d8dd78301fb1d23780b040fbd03e6cfceaf126cba94f10e6d6551613848dbca586b8f1c0c21ddae834d32270b67179cd871ebb670f081b84085e56926dc3143dc26a5a9cf1eafe838c5ec715643220c64dae314c07faa319a36e2b3bfff2718dc82505e5a5d3b32cd8a726a997cfe1929305f979da55425e9a00e3e845cba6c715705bfd219d1decb429883d1eae8c5d787e9d3926c8203ce9cc3412971c88ed1e38bd6581a77e408b7e32b6802cd822b1d6bf8a379a11f1d0ff324b2079d00d88fae17898c68821110799eff6032c5ea82537448dc5d18c10c8a8e10d86eba96bfaec7076a7165f5450c936511d8d4642615eb936f957d3d616d12f470ba6248bdf4dbb2671d07b00352cac4a83cecfdffcfa96e772436c19110f4833032b7be3f6ceb871c0ee43ea50773ee0eb30b8d49ef68e496a9882a255d37dbc23b4991b1674f6f95ee13170741aaff3da89234426794e8cd4f2cdc41db29ac10b5da0d502f35f7c372fccb5419e911fe1ebd15e536db54123272761808b951bb505226dd76f78cd2e1d224db7acaf7c1b474ca8696783a2a134952a89b061137432f90b5b627e9c734a53ed2816d348acfa43a7b1e3786a75f354ddaade54764ce759b7e03be0f3ae1163fa7a614cc31353b230c508b9ef4f494aa343f149bfdc12f2ed0cd7d2d79c25e316a5bda535b74ec23cc42d0cfcdef710afced0ab6f23fd2612ea606e10b4ef17c6cd2368cb2e379b483dd9e27aacc26d9c29812bbaa9df5696c4b821903728c219026c286b1613774795d033545c19a1e3bde99ebcd0987e5c7a43a035cf8043b170f93c4d760def0a114ed74fda6ab3d30be7fa94ed1351f0cc5689006f1c838e051982d92e0848a20bceac9e9d2df621db65131ecc87628a50143fd31c0b5bfa8401420685729c31293e7e80e8b22570a45a0a62904909b6f306697ea124b0f3e19f2493344a80973ca77a793f8cfdc2085416d5ba3ac268bd97995c2da2cc0bf454662457ef7a6fb16a063635fcab64ee62b70869da79229cb23a56fd3c0d742b710ff84c398b717a360eaaf7c90fbf5ab844c1cea45d0aea39aeb47b2f729ed27550c84746befcc274a7f822b6b016f90843e29b3bb08639a662a902cde3752609772ed6f8777bce1945e761c3499ac1e369bf46cf7f72241646ea6abb7df2ace43aac3a26eadfbb237aa98d93565a82903037f3aa89be47dc755920556ae90fa8328e53bce34efb534e0b43cb35948d5d5c85f499ec4f60c4306d8cd11856991a460e5f755c0180de6d9a6baebdb1cdf1cd65cb1e9da2b62027cd621a32a99b444de4ebb39d302041833e617ee8dea68c503fe0e51f939a8c13b1a90c424f509963f8b19b9915c662e8bc8345145c9799702b298a5d41df3a373453684a63018ddec24fb682d28a1064db8b825687348c94843b3534e6d3f8d271f55ba627c63df0f1cc024c1eee629424a7d00d2d6d36ebadaf910eaf38b368461986b13a8835cb57465d7c759ef385a1f25d6127767fc95341c9d57453a0e0d20d54d9ef486729210e18fc43af211325d17b5b3cd348435ea96049d54019343f88a56a237dfb6b6e7f8ef2c7883a57222b9de1947a2b52dc5daa43b8071cf3852415e43800b5b862a97be1821fa4189c872eb4c63037c726a6cdd1d64521043d2abf502eab0a4f4363aa18bef19d60022825fd3e8195e93af83db46986b951f25ae4fcfde839658e3829872b52372aa0dfaf2e1da473b830802415e939f64604b7ef4f985dd9aff59db5cb86704dbb1ff678e8eebd1bf07f0b596d85e8e39f15f03c764826592fd8cecbdc718e5e7201fef31dbe8bed03bb2c8e7af9a677bec1c773ebf181c35b439d19f0c0094ee9ae42b4eb9f9d3f4e3531bf700937d94a72edffc19ca88668059ccd3b5fba525be8ab1d1434979d28e7c56b13e67d5dd58e55540a3da234108aa5585d1c49e80166e648cc533bc1de069a20153c7dfad359e00f03179df15a39d4c52611c14d0465487db0a62b7a650f2218ee0598668e8357f52151aa7ae4015d819d9d09cfda91d08109e2ceeb4232fdfd68608b76e69057930efce6c4143cf7eb8cafccd4b495bd80898a4b34cf5a281478f7ef92a4a771e805c727fa7207b5ec3596b79790b1a365911d9a2b4f6b043b6871a6a81b2d3a29127432faf0dc22a8899dc77c21f084e91b2cc4868910bdee5f670da2002b0e31427e47ffd5c556b5a4416c9f243216ebc0739af1ee74cdea5bdffa40733d169d17fd1d7bf18debb4920a29aca4c605348706293d0a934ac1a41f589cc441345144ea62988ee4fa117a024911728d6e528177649de1b02bb40785cfa22e1aeda7834498f729245416a5a42ff3acc5b75136899767449c03ba4979b2b22f11ba225cf90d549b73181e6c8d26ab9c600ea331f4a9fdd98d619766eda64e0f63473aa487de2a49b1b1e0c4f09ae299022e49278928499061e73b9383ccb059e8bb91c89a30039768c609a688ac083f2bd636ae45feff1118d587b28ac7cbc4bb6103944c17d96eb3052cea6a0f06aa43432767a41a2c35f919e7021047196e7ce581e6f3562a04b14a4509b1ae2852efda3c5ee6e2003065f96caede47a293ec1156742e5d3ae78c2a8970dc1db55079b5ff7bc5090deb0ce7593a7afdb8b2a5027b7cad5f4905d96f1743327dda9ec344520fd0c77653bb63751b208a07d34a014a54033abfe17eda2c7400c4a95070dfd9372073d73c6ad3c41fed8d7a768ac588edef000f6ce1694f1a03cc0e9ada2ece56f496363c60de3c04d9db0c39e1c418d0e438ca888ee5bea3a0c5b7a4e5bc038dd059bc20e3d1d72d32eb7e4c89113160c57a727365271d4d54d0c11592666c47e404a512c981a40f3eff3a8b8720f79848b0d2f06eaa7324cc4aa25d954c6ae653275a0c5cb06d9d75d0f5a701d652144e782e593dd49686cee50bbf1c0d6f0c323825843cd5b34cf95442dfc4c7dff386e16417e6422f035665e8544751b2641e499c95fbb7930b177438d28aa2d873becff9ea1da560e701e72fa9379e2da83123e859c3c7be8e40d47e05ad0486c057d0ad3baa7117b677bfbe49fa25c1cdeef52924bf6925fc7e4147e6670df7c6549c7565281f65de398a22308bbf4c8f071c0aa2b7a31d6e6290905fdf2b63a8b99d252b3682483e4dfb5d99a844458045821a7aa432ed446352f3d2828d3f4826d772e084fa878b5d368843f3da57baf83ccf1e872f70870016ffd2dfb4b3655548a0182ae7ee3135ea8b9a8a3a62fc32369a6061f60150cb0a5a0594cfe0f2c47d710bce4d756e385879f7c54b0ddb3c8808804069682a538f4ef47abdc5f94c45df8e4a367c3be8f1837ea60b6615e286027ced6e04a9db223aa3b54ae5fe8be7b85fab28a1fd1a2e2e7c15a20a8c8ddb05e303f4b7713e2b45ac8eb985210e6b38a99418d1602c79275ddfd9537da268010335171fe60e2a53aa6b40a9f01b29ecb55f38d9f21a23a1c1ac5689cccf5287724c7159d0cbe23cd743073bf9ee5021e401def64fdc4d3f5394213b44603ff0d09eab9d78c855ce979af2a848baf5638944d0a6fe191ca364b26bc45fcbfe13ddb4491a6508c7bd13097cc836baf7a9a077d63974531eb1da019e1bb7fb8b8510f656ecc80c42247d6ea31b4724fccf195610a9b21682af4fb1a85dc14c80ae64ed957aa849a6a4f318f6abd345bf0b7db75db6ccfb2494d15ae6f5ba0df158991ffe45bb3b237d0aaf9d93bed6c57671275092c0f47d8fcfe020313925d5f4150018b617ce0fc5205277c648f4bc3433b79a8b766649ea61a9fdc5b75923ef657f4755cab46f48fc25bfdcea5f167fe755542a764be67e9afc1db4b849b7cfe87ce243e17652f63c6474da389cc94ca527ad686bc7fb9b747560af7d2dc6b9f5abb05d37245a42dc582af193151d7b9b51a6621528769fc8cd6cb0974de42a12e27acd154f60f30a62ba2218fc41533bfd1a2743ef3b0838d2560bdf52c5aa51538534ac04e36e80f57546d55510b6bb933", 0x1000}, {&(0x7f0000000380)="359ebccc1a42350c93f77634c43650d051a6531dcf0f08498bf61235f6510c7ded1faa8cd60a1a40014cd829d27e4b6d3faf764961e4c5af2ae0973710e3f8ba5c6ce07450299d5f56125d87738b16dcb4e73249fc11305b696d1c2527576fe7038078a6b6042f2d10d3c74ad2fc4df05a53e551f6ff701fd38b239abfb834ecc47edfef79bc5cf6061835d555a0df598768a33e5e21c30aff2176b011edf5e1f94cf1bee6ef15813a2ac4", 0xab}, {&(0x7f0000001780)="ea6870d982a3dfa91add856705e0e8d2079b05bb822b5bba893fb1610a0b2718c603f8c10fb19676cdbb12aa9795bcc7ce748f122af60ec76f17b83c39fbd56faf3261c025b300e4fea2f84a9ac2049d500e9344702b230b9b70545f1456720a75d8081f176bf69fcee463cc8cc33f3318e19b87f179d9150cc72ac9d257c69ca198f8be5042595a75caba9b54558c6ac37c585c50902a1c637e677cf0fda6dc64bddca442caa5059bed2b47a8bf910ead7e07a9e34ccfd825f9654fbcf67cd2dc0a5253b2081173398b1dc7af3d263ecbff39a86893c1519f4f345c1c237e1b6ce6249528d9c9b4aac84e6d2babde82b026318466a54272b3216a4071783c736ad6dd4b52183a4b7f60447e663eb355594e0616f3217f3a1ead1b7cbff956447d4e8f947ddd2929607ba6afd75f4dce7d846be271d18de2fa6937103166b9fade33a3f2d87143b2513bab838eb4d15b2b2eec19087cbdf7b6db0d97a555c7d80f96b3c10af3da3c3cec470e142ebc935dfd4104abed37ff13ac2604fbb37010880ef1cb2e1d1bfaa4d0dd5e89a3f93bdb95497c4c75a2164fa12460b08da9d72a12b3416ca7e59872b589d23524e7f987995b99705d0dda112fe4a27b6cf6e1defe255ac6b25cd722b1f07885b2478950c929fce088122cb43bd7f6f182bb71ac0bafb0f2c9788700ca30cc942c0677d5c85304097802661300dbacf49e36f45e650e64ce63b772cd1c8f876699cfab5a6e5656f2081d6a788f340cc8393a0146b4a1eb68ea20f05af1383e167eb28a3041cd6e84f69a85206694dc842d002c2805ec0e20cff250edfa8aaec14254afdcd88095b1be6159338b66708d2ca6bbc39c076ebce725d8d43c4312788d27cf73155136ed66ad647ced4e51193c46829756be51b5426305bb2556be99b77b3f8417211bc23ce58b125d0c17595e13a86e3d556e2f4c59a1b848ecbe22b42af9e5be57246c8fb38008c6149d28a82e5dd190b9720567fcdbbe75fb7166baa00fc37a8616d39fc7689f4d7bc2cf4c4fe9224988793e39e388f76fd62c4c7eec7fee056800411de6db05684620ca3a096a34b16c95cfae7d9e543f47836c85ace473650ef2b09d3cdd6c336c4b7de36cabb5acc1085e047c57f59656e449ee7806017f5c0a7f2a3cbf3f8f4e5561959a1f7154d7f5474eab444d6ed3bf8762afb0535a8953b46342a572573bfc26fdb5be85b924d2adec0ae1ba770acdaf40820009cab3fb8f3b224385f0b8020cced2a51e03a9cf1316900ddf229d25b1660ac3070238302242255e6ef236882453b25972280af98d05ee36c6bee09bcb2bb93fd65a565eeaed44107dc6df9114ea8d3a8f9980611746ccef6d50d429cbe53c8c5a139465603c660f88e579f625d7cb281db445b434ef9a6c387cab0466c8872a7e6bc5d4eee14f322a1bd086b9f5551792a246d4a826b558918e3a7c44b23391048f4bddf2dc888fcd39e1c3f062678be298c0d4a40c755f8f5cabf2fdc56dbc75dea9a19171313192cd8c486705602412b0304ed4f7b13d8a705ce382680c6fe7c459e06e4ab4be44291ac85ebe570e1661be57b76cd50fc201dfa296f6a9102cdc0f17dacb568270ef83d206e942c8b4abb61ce6b02b7c35be2fcf81bf5803e7eb722ec5ad9bb07373ff2152b19333c9f41159ff14931258f9a1f7ca78028e32f3bdbcb8ef5f22c22c2e6ec872e67cdbdec812450fc5184cc0d9581f3cba014fe1cf6dc8c4ab48ddbba2ee651267d3e219644aa8c4422135dfa413aea01650403fd008f1197aacca449305627971f667d4d9f4214882087a16daf33be4ee3bd6eac6b3645ba7be1fa62c6324730f0521bd025044eeb84704c9aa477e90712a0668ae93746c7415ac0a1e7b3e4dae8f05fe58e7da75f928a069f9d8c654854f297ab1726bbad3b7a5d091261d8bd24121545a298eebe808fd8cac874d0ba2ca7424536ff5f34941432e4c5c8502a25ebdeee46e1c57234c18c7d3cdc17ef0434810b5354622706b0c841390641bdeca58bee1ba7c70f1e91e473287840262e68e5247c5bc41ba066a94f6c8f58037874757c56ba8c2d01999ccf9ddaf4b72f0478fd9584e67534b0905c2e626f2e1d904f9b0e38458d18a6b7ae1535470d66ed61b49878dcd22b0146b67c86660fa89794b395e0de04f416aaae05f301cb84850bd32c3e16558ffcf5aa8eeddc91f7ed4564cddfa304b140ef3cff84861006918b523638dabfe402d7ebfcf1e49b98fdf4b1de3830ed9ad91580f8444c6772dfacad347c82aa7234371efb3cd1b6b18f2704e599921136445e04dd6d8511cd6d16c324c06177bb541df4f9de9b496856234f9f87c6b53d89def204a21f2bf183d668f76df4d88b51aa50f3ad322a202f4795f892df85ffadc686795f9718ac68fb29f4f04312f3612390f9145db1078484caafe3292517c0749368dbf9e916463576ea036fe4540081d9fffc7f114269bfdabbf097ae4880938d443a67ff5c9c5772876901f0845fe6a9c3405e8a39f63e774499b21e07baab663314168ee2c7c3607f14d6d02126d9919a8d11699942719d078d3a77ea5c0a8b2c82a98ca6f60a4c414a2a57928d0b1e1350bb035e3837c00cb27cfcc331fd1916d6f349afacfaf540efcfebba3f2255d72feb5bcdf02d4bef48f4b32223a1e642b97bc564202b823b2b441df320b7243724ae8563d94d4bd763bbe5f807eec26ce2e5bdf9cee11c86d65f080ffe3bc6a5f98d081c416964e6aebd875291e137dd58e5872cc7da5eccb4400af05cb6e8e8521cbb3ab1be9dc2d3bec3a2dc2bfc15c9ce8a5849d9bbca0f27bd395aa54a5bc8f62fc4e5248c58af7594b5e08dceb607eb8345d375132f2e279e5f00d1dc9395e85494e88a631a4e68208278c31f2203668cf31f2aaad308a55274a0fa17e58b279ef368342a2afb41e869cde6776c8f34407e02401721edf26f287aa828fd9ba0c72cf681087486c742fe5a3b20436a6e11f0bfecc038d984a80e19f869af7ce69be1345080d62ef4c3f254ed2858d8eb7fe0d9d3598a05ae84b017de30c42f4b63b2a984e961de3ff56ee26db6a8cd34a526ff3b7374a2139fe8cfe3ebb4518e8ebbee80bf44c7c3f9e1258f341ae250ffe7c8feadd179493fcb99a0ec64bf0fa71643ccbfe85ec5765f5e5184567d73da284184d36e245b7a27b0c125cab11336e07b99694380b45547184cb9ec7ae4f7a3ab5e1bfa5d7800f65031716be8321ba28d697da5fea4274c25874b3c2aae94cd90027f536d616b91b619cc237fc46e0177e9da9dfc2dcd22e94452e3ef643dd538ef1e2d9aefa5f8fb97e663eba086ba3ddd0c17b98c4affcbf7ec53658b0d637469186186874b92e503fe60a1f4a8182f272159fae79c52a9702b8dc60bd963b4a4cfa5bcff15eacbe687b56a4274c25266dad6b47d88d0baa30ab37dabf259bcf5d6239c25c28269f0810763c092dd5ebf3835f19ee6d1d5e8fac2a79b2e2ba6d3c1af04a37e39037b5654063e2ef00504ede6d62c6b637d7fe3a5e08463d731d607eb3c7cb8a7067288b8f4f2d7b3658d5c472a0a437216833e18a06f0cf5c188104f9298b45382eee2a3797301491d0034baa7d4da92e7981eca517f5ed38cdc3621f609a474030e7fad48d5d9567c2dc55312310437cda22f95e20a1cb7eb33a3d9c5a96727c823a01bea0f3922f9b547cda33f958676ba5dee5f6ee65a3f410c2a3f57eb5bc1989b724bba2fe5edd77366d9a709c465f425f1d72ee7b742da0a3a0f5d6c3d4cc165b6a554ff3197decfbaf7a568d3d1335316f9bac077026998c8d923a60378d390965444c8e8eac56a5ceadf241cbd8a43fb7585c142ca1ef811ca99acf0ca4f8877b9438c627c37b9e0a3c895d3163bd43f3f762ef8c35e9ef3955adc8341ae4402f144371d6c1b47d62b31dbab14cd2b55edaa29c6fee9020e6ac9f3135602378f973f2afb8680dcd8038c79e9d2b709c504795ada37bad1ae03e7a06433d3fccdda088592a04cc494e76c58f2c59764fd11230e2adb4f8bf9ae8c58c508f37cd0a918ea98414477249e2af08f6482101b00d1a2e47105af9545a425f5b7e82636206b1173b3acf6fade56a410e31cc4b620d077bff85d3ae0c83548cfde3217a5b635305fa11d77f667dc704581afa5452ae32a2820b5fc6aaa9d9e547bb7a73eb77064fe440e7152451413620b6536f62151a84d41f336e0d436720910bbfae5e01d9de079eb68f30956ec5660f5cd5e7382a731074bdd76efb9b361b9ade14e81b448716f9e3543612e2b87264c51b1bee3adf403324ed40bfdb4459b95d48f2680877886372ef3ad419bd90c50d4ef2ac55ad1cf583fd18b71b57d65390b000323696fef25a1b7ca6f5b2de42779dd423ddfb778b827f101a0136f755e4059fff8d673b83796b603f1fe7f48370c1d4fb61ebc20619419bd527e737e5725999e121acff4a5c0577a20371d8dd6b287b57f4e9e5fe9ceed0472caa71f52ba1d9c0a26e3edcdb04d11cc7d06620dc7f9afd2fd1b9354e73dafdef4487921cb8f44540613ed6883af9a3014151ec9a5f6adadca943f4038a0aa307fd50a96d772b041479614c55a0097ee0ae7fd46adc214d7d4d0ba7425784065943917c6ac72a372b44f0fd706c51c489d94a51cb4daf9422b4adaf8570e670721a46ac6e2a5f687cc88c97d8f39b3201a71cfdf914a04e7c341878e48e98d4a42661894d88e32a727d7098948d3aa1144dba939784a76221c2cbe7e193702a6f29ccc282a6b21895b3bd2b08e5332dd5b383c90a43e0419cfd56cba45f17ff71554717fc6935c6aa1953cfd3997a6373c512a782c2a02f8fc527ad643c44f2a947ae18a948c804b11fedb148eb6596d75803e844fcb309a25023379b75ec97a8185f9dede71029a6b3e601a897558905e68ca994cdc60f74877299d0dc943c025dc0d350fc661bf118e41364b07074e4152f1cf4cf85328013ff35fa61245d0ade91a14ec6407453775ee227a8d44b0bb15e8bd1bc755ddaad4f50c41a162ba9d194c5c5b6c4b6e307f595c4aac228a792d09a1cd0407aec51e8bf05e636025393b2dcac57157a2d84d3cf071524079a0b35bd5e34c00d9e83f12e700289d6d830e899a77f9fecea38bcaf3c17a553e3e28e91de9e94ecf8d6e6e11948a40e712e6de012fcabe9b82460c8bade3430bc011b5466ee168a74c70fd7cdeb5196bb4919a35782f4d6c58db782c8e1e339a4878925b67e6ca6ff28aa7031580667cdceade8ede3fb4171ddd39da2fcb50c7e9c9ccf5fc2e3721469b9ff43436d7d713dcbfe022eafe8bb73d1cf9e477e98addb3a2bfccc545a05d717810134f89c1f560a5abdce9027a1beefda39cc7eec2d71fe6d20e8c46f460cacad5601a5c38f93447b44c502b6bf1884d88abdbe176dfbbe2f252f91a05f27fcf81fcf4dc926f73c3e1148d554f34d9e3d600e6417b0d2671db9cda492c8392b15278b1c7d4a2b88a084896cec518e81619bc2c712af2382b3e65614c2a097e7421f2388186d2a8efcbc3e0611b5856dbfd4b47ba5b58e2c66f4b34b281ed19d455653b3d8571e8a68dc49198807e560a464078d65e475dcb127ffe5169991759915380c5263704a0ccb68970259bd53b3556486db1a5097b82bce24983ec7a977fcb9fb2661c6b4983994898d8c67388d99bea7e04384e44dc1a35d3eade220360be23927df5cf3e473c4d00579f2895f6c67cacff5223e4f5771d73277c5f58b50266326048d0eec5431116b232d0d", 0x1000}], 0x4, &(0x7f0000000480)=[@cred={0x20}, @cred={0x20, 0xffff, 0x0, 0xffffffffffffffff, 0x0, 0xffffffffffffffff}, @rights={0x20, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff9c]}, @cred={0x20}, @cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffff9c, 0xffffffffffffffff, 0xffffffffffffff9c, 0xffffffffffffffff]}, @rights={0x20, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x20, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff, r2}], 0x128, 0x3}, 0x4)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r5 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
poll(&(0x7f0000000000)=[{r5, 0x4}, {r5, 0x2f}], 0x2, 0x0)
writev(r5, &(0x7f00000005c0)=[{&(0x7f0000000340)='\n', 0xfffffc73}], 0x1)
readv(r5, &(0x7f0000000740)=[{&(0x7f00000001c0)=""/197, 0xc5}], 0x1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
poll(&(0x7f00000000c0)=[{r1, 0x116}], 0x1, 0x0)
shutdown(r1, 0x1)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
socket$inet(0x2, 0x3, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}], 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r2=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
sendmsg(r2, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78608e, 0xffffffffffffffff)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
shmctl$IPC_SET(r1, 0x1, &(0x7f0000000100)={{0x0, 0x0, 0x0, 0xffffffffffffffff}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0104419, &(0x7f0000000240)=0x5)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1007, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x5c}, {0x3}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="a4027b3c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
listen(r0, 0x0)
r1 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000040)=0x4b, 0x4)
connect$unix(r1, &(0x7f0000000000)=ANY=[], 0x10)
getpeername(r1, 0x0, &(0x7f0000000100))
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0x0, 0x0, 0x2, 0x100000002})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000480)="f7a45101212e9dc4a2cc2068073842", &(0x7f0000000180)=0x3e0, 0x0, 0x29)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000200)=[{0x80}, {0x7}, {0x6, 0x0, 0x0, 0x400}]})
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000100)="d7ce48462cc771455ab69198706f", 0xe}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284416, &(0x7f0000000240))
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
utimensat(0xffffffffffffffff, 0x0, &(0x7f0000000380)={{}, {0x0, 0x1000}}, 0x0)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
poll(&(0x7f0000000480)=[{r1, 0x285bce483b69e25}], 0x1, 0x0)
write(r1, &(0x7f0000335000), 0xfcb7)
fcntl$setstatus(r0, 0x4, 0x40)
writev(r1, &(0x7f0000001380)=[{&(0x7f0000000040)="b79c75797734235fd849fd8f1bef9a8aeeeb95eab08598c2a2b3608a37b1e72a201060b04aa8f950a6d812005c5c3c78fc9eebb253667b045e2918990ee8010a6d0e195bf2f0f89227f4b47266febc685deaf2b22cec68585fd9ba563c93777d52eb6df23540f42cc44b9693e24a90521219bfe337cd6fb55d1d089b1f18bbb7ae535d57837e287524f8bf70efdaf833bca8ad5a5bfffc69e57f95c40b5aef0a19359fcc4e33b34d9024dac9736f6100ed48e75f91691805220c33284599904d097d", 0xc2}, {&(0x7f0000000140)="d410a0754c950130bbaaf4f25552827c3b3f958c0b1956e1bb3bae5c0666cc", 0x1f}, {&(0x7f0000000180)="c8f4c597faa17dc0cd31322ede2b10c2eb72fafc513eb25854c5a6c2add51602cc05626a54dda827b113ab2fe86e8a4eb9400b48f2f8a38f2d3bb0b5f28c91ff95a32b1c3f77f5628a7c2ab74c9b92bf3ce54c443643486d515ad0df78433acee4694034e7cbb4a792fc38701d858945611ce67fe1bf28c11d4643e0e4282d7debfec4e5bad2f0da9be9f2cbe11a14beb3daaa9b33223e8c76a1f06a36c059eecb5c68e59a4d45b268ad528702447018c03c8cbc2d4414c486018f8fa11e28c2743d35b91ae2b6e6bb3fe85a650b132ca8d7fdc4fb3e279978d602d81764fd7f6da40234504866737072a4bc312c50d669b53b4ed847a00965f4150ecd0e0e284aa1bf619fec83cf5a9ea49a5a6aab89828f7ef2bcbc5fcdbf5630946ecaa35caced2f00f89d61a970df49e6c2055da684687230ed0eff0853f64a624ba6a1190520000aa9ac2a52fe75ba6314c4faa5796c904f46c389cf745f80b2c49f53ed11f7ebf164f6c3a23826ea3f4e37fee21441b0b62a2483765f2f72b1e751a9e91eab19769bd159b3af08dc2d9169c0f01f9e5f4e1c6bb91afbab49a216891365658cecabf88f5eb789148da3bd9c3c5d4cf57401f3824f4f2077c6ad79f23d0240b5f1ff232c017c2271f341bb8d8680224ef88bf5ad5a3438a6addb8fa6003ff0f36e90a645ce79ac9195e50fe4d4b3dd8bad9d5468041d21c3cffc40af55c5fa6175ab3db240d4c60da2706acc0b780dc936836cf9dc8de9903e70e88d1df91aa66a47652c1e60bb083d9025d7e3a87eb8215745922dafb2eed373059615fe1bd1fe7361a8b9f3faa849ccdf87e537a5103a57ec65e8d8af05dc86f4bb97c29a3f038af1c4845700", 0x269}], 0x3)
readv(r0, &(0x7f0000119ff0)=[{&(0x7f000061e000)=""/4096, 0xfde7}], 0x1f7)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
connect$unix(r0, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
connect$unix(r1, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
connect$unix(r2, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
dup2(r2, r0)
pwritev(0xffffffffffffffff, &(0x7f00000000c0)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x36, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000000))
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000340)={0x7fff, 0x0, 0x1, 0x53ad, "34f3ca531e0d5f64d273691cff4606cfd946a2c4"})
writev(r0, &(0x7f0000001000)=[{&(0x7f0000000540)="882413bc93f44f76b7c2d2599f3d73d5533bd0aad3a61a516f9fdf1a4bac42d3674cf63dec39a163d365cf988b0f283a32d3ed91fb98182ab97e103071250db4", 0x40}, {&(0x7f0000000c00)="895d5ece0ab3516cbae863a26d0e8d5ea4fdbcc080dc3c570abc955e9c0af569", 0x20}], 0x2)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x800090d6, 0xfffffffe, "d70000001000"})
write(r0, &(0x7f0000000040), 0xfffffec2)
syz_open_pts()
syz_emit_ethernet(0x3a, &(0x7f0000000000)={@local, @random="46d9f6b09d39", [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, @empty, @broadcast, {[@ssrr={0x89, 0x3}]}}, @icmp=@timestamp}}}})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc0107008, &(0x7f0000000280)={{}, 0x0, 0x0})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0xfffffffe, 0x0, 0x129a4, 0xfffffffe, "0400007b00140000000000200200"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d298b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5ed0d85fc397bb1e7a51dec58cf1b6a9f2829bdc5eaf9a9b0eb8230c4f92e2b8523872aebe96544d539e7cb063926132dba7fc0144863d7221640bbf251e82226d5ef65610ed4dbdbd20e70352c53f3e5d018ee845a4bbbea6bb6349c47e04dc28", 0x12d)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x14}, 0x4, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$WSKBDIO_GETMAP(r0, 0xc010570d, &(0x7f0000000040)={0x0, &(0x7f0000001500)})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
close(r0)
fchflags(r0, 0x0)
r0 = socket$inet6(0x1e, 0x3, 0x0)
writev(r0, &(0x7f0000001400)=[{&(0x7f0000000080)="72ae151e59dcb7f69d76d40478fcfc1aafde2026d2efb6d827ffafb5b6e028bd866c2ab754938982d5d8d49de3f53ec908fc8755ad4638", 0x37}, {&(0x7f00000000c0)="af10699c984161cbbf160d464abbd1e390fe3f58914f6cd54f4714b3529d6a3bbbc2851c472cb576af349cae59ff37af8769b6481393f07007a8566f3084028dca41bc631e49ebcd3ebf61f8c7f6f6", 0x4f}, {&(0x7f0000000140)="7fd93f72c9ef06da5ee2878e7671007e27645250f7ebecce23753673210a2fa685aad35131fca43e37ba2aea01bfac87e668660d279474f881429ebb83e3a24e87f5a247", 0x44}, {&(0x7f00000001c0)="fe0fe8ac6df9f462500f308a7138e308b582fe09b95913f1a0bc7648c51eb13c7eba64d6bf60020be2cfe70a8dc74fd66b97557b80222eb8c70b7f08b299b3cdfdbb00a5678d2ee8ea647d21d15ef0995fe4319716a0a5", 0x57}, {&(0x7f0000000240)="869d60203fefc96ef2184c8ac1f04bf4c08a5409997d2630e924de8f5c8d617b010d9e4c528d7684891d02e8b39972d1f4f22a6033815ea43f56964bdb9a1921a1402c164d62cf0c3868e2710e56fa55256c8cd6a361105c5f3093d2924d04177ea832d6bdbde51dbdff406ed9d063c1cbe342e595766463c397b3ffbc14c8ae86fca966abdb441b0c3109e80c57cfc9df09bda2cb514e83bb1edb", 0x9b}, {&(0x7f0000000300)="214b0bfc6bcc778f690297d0605a9ac99642f735b28063b9a42a8a574ff15b1027959692c790b8f24272ebe1fbf23fc975b39d13dd815073c1b97429197d7c62b3f20e74b02fa6de93b4624c70a8b48f751d61e49e60a0c3dbf4efee19c11c6b51332e65c0a64dea5bb59f29475e1ba543c9ef575e365047352a7dfb6600907d89b5975ad15407d529215f7ca0a7555e8c9dc7387f4c6270e7523b7fbb78bdb7126ece0ea108815a534e5b187d96fb9050fa495ec6994104b66395bb3dbb46ac3af094ef75895ef80e5695ba52", 0xcd}, {&(0x7f0000000400)="b7a1afc009fe96d3ca2b6779d023ffd70fbc521a4f1835dc8b17b56101efba7e9ae4f6b2b11f15e077f3750c69b86f750e17dd73f92daa73a8beb4402069dc2889846955e8380a36f162c98dc79b22df496f816affa2863e1786b118cdc0f2d9378be8e789b37d8716b7a95f030521e64f2086ef44d9258eab7be45e2bd82903491b581c9b44a49f5fd6521ed0534a02166bc0a9b05eb89ed3c3a15ce80e246d7a82be9f9da3ae026c5abe58825b50bb61b912b5760f73cf019a003d5c680b734d70bf2521212a726c7f9ddf9ecf601d6a0ba579d23319602be7f7a30dea61a71c8f8f9f6d92a2e13e85e571b8229ec32cd697a97e06890b73a87ce145ffac91a0fa7ae9f80e068529f77b744aaf70e31eb8ae4d0d5b8c0c5853496a6184bd71a903ae7d7a839a9381cb6e4caddc89d49f3c2523c9fe82e5ecaf5b577d780e6acb33846a4a9d8ebad008ee4812b2b7e13f082c9c2ba5f6f053ae610afca06c85a01452c4509419eaa2db1e65188ad4ba87bd87914d358ec1a222e9b2470f5df0af2e2a43920f2a73126699bf7112a64a4f5418c3e912d30ea1f7a7b3786a1de0440ea1732914175dc85e1bf04d431e4b03111485305696f174583cba4bc224c66effccef36c782348875c41abad8cbbe665be47054c700be26cfcc0d88535fd63accf043da72f7f5541bd610652a56b2db4af54f45ba7cfd96ddae2dd0d3c8e18d1c2ef5e0cf4dbf35f1a4e2e1d67c354737544c3e00e330a688731b1b9f0efd81e5f02e9a6fcc735e94d305e31f3bcfdf77125086fcddeee1dce3ebe2a96dd9cfe89aecbc0a033849826a9913c324495a6023faf3a8959c1166c3412b95556e83eec9369bc179c01cbb28322dfc787b41f32ea1dd8571a402583d1f55d3086f9f7d7039b1631605b4172e536ab7856b088da224713c83d332c7a6b9294e3a2fcfb8aa00fcf449c4706b846e4f9a4a6650c679a015af522fe47a76e9b0401c56d7ebf99e38adddaca91db73fc06da874e7f5ef11c85b562db06dd4f0f2e4dc14dabf7880b4f9665f9579ee9c61d2557641fb75a0a19211cbb9f677be1f8997d03599ea93f3f4bce85755363188208f299a9fd1a405539e4e59362e014bed4ac9a5075d766e697250b0436812eb7bbc7909265fc262b4cff85b5537e1f044c6575f6f167fe228dd1b73c74d9b0e816d9ab0669219f2461577e1c563b098bf54c543d5491b08fa2dbe948cda0df19b797426313cd69127cdd114662589b6eb6d217ff070a9257719f26f70a4ebfcdea8a8abc5a03938334903b5106faccc7921519f8cf7ecc100f754dc200fb96ce0307938fb55d5a6f2e59d73d175810fbb20ea7aa38aca342e99f40b02df8f7385faa4145f7d46d4c76e7fce92d0ff4f1d5e58a211398121f3cf68944a2d4502982f2b69f3826192870708563c2d66077eb39a85e26e9eb4b9f0da29d4e6c5023ed59be2491e1ae989e53141931081f3ade7fd28678f076c878827c41e50e9da17acb0258977b23e6e2799207c51f0664b51f745fa69393559a9241331669d0b46f7a1a255bf5c3a5f2574f922f51d6fd662fab6df5c72e93f054b6886a8458715f49bfd9821871bec2ad44708a8f6acff99fa5a1760d6465e9a2b65e86c09a8f1ba4d999241f7a50266fbce525b879469b8779146b15d443b6c928520a98270f0c27cb1c286ea95569d08ba9f613a4fc24fb3680132f0971fb302589b03a37d873762dea945684fa6d29b4c482134627cf59a2284982faaebcc2397fde63ab6d58966e54535aabea5c701303b0356933dc8fee414f37f56dc4c7774dbaa7c0151e50461fe82834e07e9d454a0bd96061e95ac86dbe857c7c6358e4ff8d3d0ee9c21cef317795862c2b53894980e43cdb62c876f9e0a2baba4cebd130e18b2c2bbc193f2bb9a0bcc4624621a8bf08526a3172c3b8ce8b250f7487dca30537d3efefffd9a878461c5878e5f", 0x578}], 0x7)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000180)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce6a46e98a632248de9b5ec308a351c7e403db3673540db5e2988950d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b6856e69cd58214fad993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000400)=[{0x0}], 0x1)
ioctl$FIOASYNC(r0, 0xc4504443, &(0x7f0000000000))
mkdir(&(0x7f0000000140)='./file0\x00', 0x0)
linkat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0xffffffffffffff9c, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x5}, {0x81}, {0x4000006, 0x0, 0x0, 0x10001}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
recvmmsg(r0, &(0x7f0000000480)={0x0}, 0x10, 0x0, &(0x7f0000000000)={0x0, 0x2ff76176})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000180))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
syz_emit_ethernet(0x22, &(0x7f0000000000)=ANY=[])
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000080))
mknod(&(0x7f00000000c0)='./bus\x00', 0x2000, 0x450a)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x2, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000240)=[{&(0x7f0000000140)="ba", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
close(0xffffffffffffffff)
read(r0, &(0x7f0000000480)=""/4096, 0x1000)
ioctl$WSMUXIO_INJECTEVENT(r0, 0x80185760, &(0x7f0000000080))
sysctl$net_inet_icmp(&(0x7f00000000c0)={0x4, 0x2, 0x1, 0x5}, 0x4, 0x0, 0x0, &(0x7f0000000240), 0x0)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000500)="0118fff60f9ea1feff59657f16666e9f97069815ca5835001ab43afd5604c49d755df9d14b1088b7d8414191ac6193bb09919a8a3722e3138bc29c66755d05d5ae11c60600000000000000f62cac7d5ecb3a69b2e7910599897b40c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebb2481ea2de4000000400020a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66c878f486f7e59a59a05bb689915b90980246fa85c22ad066d2bee08f7397cfe2cae6eb46e98d4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac73547476b2a766825175bdc4ad08966780e66be6d740a1af902a5f48e0a013a1dc24244ade0d51067201fe3eb6a917a93ac6000000000000000000000009eb3881885647e6b9ecd6bff6b27cd49c4287ed75b0db89c123fce0cbff668a58f19f470bd87e5503c733fc217eb57458e55df302c2d611ae3e030100a9edbd2d2d845b8e1f2e111835a6b788d5ff5256df13b563f269e55e0100008000000000575893f400c7c32ed7a1d4dfedd53dc24ceb12d50d3fb41b2749e7739f0ceb63553689a46145a2805341c0d29de081568214f857ebd1f1e41bfb9a21624840a96d968ccc0feb108d5bb60a27d465014bd7742b7e5f4a46cb83eea6b48aeb60db0242eb2d2abfec6dc0e3b0450200b24c238f90402598ad960ebfbaf3a37502767ebb56b08b3e7200"/528, &(0x7f0000000080)=0x210, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50", 0x55}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
setitimer(0x0, &(0x7f0000000040), 0x0)
getsockopt(0xffffffffffffffff, 0x29, 0x2c, 0x0, 0x0)
r1 = msgget$private(0x0, 0x2)
msgsnd(r1, &(0x7f00000006c0)=ANY=[], 0x70, 0x800)
socket(0x38, 0x4000, 0x1)
msgsnd(r1, &(0x7f0000000500)={0x3, "c5e94674dbd3604cde5c3557da6461341b1f80b87c9e9c0a41a1995045af2238befbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6a"}, 0x6d, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000280)=[{&(0x7f0000000340)="01", 0x1}], 0x1, 0x0)
msgctl$IPC_SET(r1, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x2e}, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f})
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xc)
writev(r0, &(0x7f0000000280)=[{&(0x7f0000000100)="7f", 0x1}], 0x1)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000080)='E', 0x1}], 0x1)
writev(r0, &(0x7f0000000140)=[{&(0x7f0000000000)="bcf16e0ae1abc557c2225d4a0e38eb83b987318db21c1edf6e0dbd99e207", 0x1e}, {&(0x7f00000002c0)="ec167a0c5bd44e5687be1a4a308e00eebacc9c9f04fd426fbf3c689b8013b904", 0x20}], 0x2)
execve(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x1, 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x8028698c, &(0x7f0000000000))
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000040)="3a1f2842", &(0x7f0000000100)=0x4, 0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000005c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000200)={0x0}, 0x10, 0x0, &(0x7f0000000280)={0x0, 0x2})
getegid()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000580)={0x0, 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='8\x00\x00\x00)\x00\x00\x00.'], 0x38}, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='$'], 0x38}, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x100, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x6, 0x0, 0x0)
setreuid(0xee00, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0x0)
r0 = getuid()
lchown(&(0x7f0000000040)='./file0\x00', r0, 0x0)
seteuid(r0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000140)=ANY=[@ANYBLOB="b7022ebfcb"], 0x10)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="14000000005a"], 0x18}, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
r1 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)={0x3, 0xffffffff})
kevent(0xffffffffffffffff, &(0x7f0000000140)=[{{r0}, 0xffffffffffffffff, 0x42}], 0x0, 0x0, 0x0, 0x0)
kevent(r1, &(0x7f0000000000), 0x401, 0x0, 0xffffffe1, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0xffffffff, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
setreuid(0xee00, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r0, &(0x7f0000d06ff8)='./file0\x00')
r1 = getuid()
chown(&(0x7f0000000040)='./file0\x00', r1, 0xffffffffffffffff)
r2 = getuid()
setreuid(0x0, r2)
open$dir(&(0x7f0000000200)='./file0\x00', 0x2, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x29}, 0x5, &(0x7f0000000080)="239ed6e9c6ed08335103b0e9df9360cb54ded34012a467d0d76cdd998597275b0cacdff7cb56a32f69c4b2e0ef45d8d63c19", 0x0, 0x0, 0x0)
r3 = msgget$private(0x0, 0x80)
msgrcv(r3, &(0x7f0000000a40)={0x0, ""/4096}, 0x1008, 0x3, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000680)={<r0=>0xffffffffffffffff})
recvmmsg(0xffffffffffffffff, &(0x7f0000000940)={&(0x7f0000000900)={0x0, 0x0, &(0x7f0000000600)=[{0x0}, {&(0x7f00000006c0)=""/222, 0xde}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
sendmmsg(r0, &(0x7f0000000440)={&(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000bc0)=[{0x20, 0x0, 0x0, "1b80b05b8b3527335c"}], 0x20}, 0x2}, 0x10, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0x0, 0xfffffffffffffff8, 0xfffffffc, "1a2f8b027c00005d00698900"})
writev(r0, &(0x7f0000000140)=[{&(0x7f0000001f40)="a5b4fc56cba25e2cbc579d61eb256851d1cf5b7144f894b6c6381b3e9baab2c1bc9379162e12009b5884a5395d9bd24c4ac05216aae40a762c491c5584c0709662102b6145c974d0c5ea4156b3d61231efff7f86ba4fbd3820d243690670b0b0e80b8b2bd24fefa48b85db505db9449fc2188b3b407fb23bc3ba9ab534af7f9d36e8edaa94b6d42a8fb088e937aba140a8a2182c575a4ffc55f5fc0a63ba9fd578ebf27e6a0035b71d09dcd9457c3c2b1236e8644aa1138d1b242483cc7a624ea14fb3f6b30e71e701bffbc09d89098106c3e3ffcacce67c4f0b9ec4aa046a235cfbb808470dfa600606c5d07c765d09d4bf3c08e55d6aac531eb899b5247b5cc64c6ab7e85ffba537204f7fb00d0791ffa2dcc5de4df4ef047765a37dd451c90cc3654e76e30396ae40c702ad152a1844f773a0767f2a23d19048dbaf5199882a530af93ca40253056f4388f77e5aed9da3d152aa5bf7693c478c9e7ce5eabf3b4b10f6ca683647ce28815959b989f503563bbc444bb74a38c49b932dd8670baa6eb88c3cd5a444f2621237aad38e7e61db39b01fa0c7195aa862dc2e75df884f60a33357868e775b31246eefc67b01f1cea953cb9d75167b4f0707e78c769ade0ef328ca34058193f5de083a38c5ceeb102fd634e85c3729eebb4d50815950cb7aaed7b9146def814b3fea2cd55b65aa87257a1f89539dd19d9f1bd02d6ca668434defb71b03b0b6288b793341a19199d5535459887f43e8757bca50ff61829c8053d036eb7c2ef64cf7f1cb5eaace949980d0cc65c22b3a5a31d607a0e1cfece1571ade2362a5d4ece0dc0b026d6fc678a1efd9b04a86384bb486b1ccedffa29afa9cf40888b4c1d9a978c4e436bc8e96bc54ef5f1efc80e9671d040c7b46887c6d69cf45e60fceddfdf82e0025b1cbfc856270464bd08d0fb896f71a69f18e8c3c7e7993708c039bff15b340a6ce87cfe75e8214beae499f9a2bde9f76630d7cf97ca2bb41009b56a065c12c2b39b36a4a33a87d6fc93b2dab12b8b9d6fcb7d9bac48c0812ece8b1d7f2f9f1c201f2e3a705320911c2e47e55a2a4cbabd3519d06907a544170a7ad76e4970c597fdc51a79791cba91fd4dbac57f4968f43e14dcc0763cb6f8103e436185459816746807f1e9c1d67cb3a9511a2f5368206c94da2c7dec6c5c931002aad4c65a9b58f28ea3d7749cfb6c38d859501865e33bb312f894fd55681a787ca6593b95eeda17e5fd9e9e8f78c2c267d264c16ae4380f3e43337f2f6cc3c48eb14322f24bf3dfc1de3e9f968b8a017b8ad49c7885004d5f5c2077399503fd198b014510807945fdfe6da71fdadd4903a7cfb9d00e11998c5b1c6b95c8cd7cbf9f4e568947aba48393ec7590579c437eacd3e2837d5a76cf686094cc1622c4e6455e6463738d7f089d96454216d944a8e0667d87912629517ff8cc0806df409ec446920cc9ca2fbf0d22def6bbcc0aeb55e700ea7378dd07a72cb974c3c14b3e3e690ba657b54102300efc0039970be328699ada94ce2f55739330a0ce817797f3d77509b2c8defc8c15131649766d01bfadbb80c12f9dc18a2587bfa86de4e07b9d7a218f8090f868911ce183ff722521a65f53cfa6df44f96c46517eed924184985f6e443dc00c4b198fa1e30276e1ef3b413fe10a19517fc074c9e3da480f4c4b0b7364ec8a6c5ce7251f9d19cf4584a7c0251aaa28ffbd3ef6226be867e08fe6132598f78d6ecb411e5b5dc763ee8fd0060ae1df627b20883ad69d457c22f18ce63da02d22ccf66cd541d4f72e4e55c6f5037c5c5146e026dd4e12509946041460ea701e149f49a144a6fca0c8f35cbe4df83d9199c77938d6ef006136014778a9c475ba1d70323f03b60886ac0c69b9f66b61c540ed4af0902aceb4b47b02b785bae9b3089fb894244cdf0666d1c24082224edca9480421692a20b44dcb5b3073437e964a074a96d019ace618aa6fb4f66e6f945d616e955814f1b242e3ba674e65d837aed13ebb32b315183eac60e7f07fdae917ec7a3775ec34f30511b6b585ec88e398bd28f46d70262ca2a16f54b0eeaf280f82f14a4c9d2a159665aec04daeff2290168e4bce6ef52e504a454140f75d9835d0924a708b2f22561531b2c2520ec1a140b9b01ec6509eb106f79e056b4dfd8c7eed6d967a55c21", 0x609}], 0x1)
r0 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
lseek(r0, 0xfffffffffffffffd, 0x40fff)
r1 = dup2(r0, r0)
pipe2(&(0x7f0000000180)={<r2=>0xffffffffffffffff}, 0x4)
r3 = open(&(0x7f00000001c0)='./file0\x00', 0x8000, 0x0)
dup2(r2, r3)
writev(r1, &(0x7f0000000140)=[{&(0x7f0000000000)='0', 0x1}], 0x1)
r4 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r4, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
r5 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000100), 0x40, 0x0)
close(r5)
rename(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
pipe(&(0x7f0000000100)={<r0=>0xffffffffffffffff})
recvfrom$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x4000000005})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff})
shutdown(r0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
syz_emit_ethernet(0x4a, &(0x7f0000000000)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd60000000001406008ab32d78d67f3a344cbcf951919a5b00fe8000000000000000000000000000aa4e234e20", @ANYRES32=0x41424344, @ANYRES32=0x41424344, @ANYBLOB='Rq'])
r0 = msgget$private(0x0, 0x0)
msgrcv(r0, 0x0, 0x0, 0x0, 0x0)
msgrcv(r0, 0x0, 0x0, 0x0, 0x800)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0xc6, 0x0, 0x0, 0x0, @broadcast, @multicast1}, @udp={{0x0, 0x3, 0x8}}}}}})
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f3763a163f14a5f7990ab958236acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f80004160000000000", 0xb1, 0x0, 0x0, 0x0)
pipe(&(0x7f00000000c0)={<r0=>0xffffffffffffffff})
close(r0)
fsync(r0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x6a5, 0x1fc80d8b, "040000000e020128f6a708000000bff090b0c7db"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket(0x11, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket$inet6(0x18, 0x1, 0x0)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0xc1206951, &(0x7f0000000300))
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
write(r0, &(0x7f0000000200)='X', 0x1)
close(r0)
syz_open_pts()
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x90d2, 0x0, "d730c1e7bb6fc6e23c5b00000000000000e74de4"})
write(r1, &(0x7f0000000000)='X', 0x1)
ioctl$TIOCSTOP(r1, 0x2000746f)
close(r1)
execve(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
kevent(0xffffffffffffffff, &(0x7f0000000280)=[{{}, 0x0, 0x0, 0x80000000}, {{}, 0x0, 0x0, 0x0, 0x0, 0x10001}], 0x0, 0x0, 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc088444f, &(0x7f0000000240))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0xfffffffe})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x48}, {0x4}, {0x6, 0x0, 0x0, 0xfffffff8}]})
pwrite(r0, &(0x7f0000000240)="d000"/14, 0xe, 0x0)
syz_emit_ethernet(0x66, &(0x7f0000000300)={@random="b1010a60df4c", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "5c94d6", 0x30, 0x0, 0x0, @empty, @loopback={0xfeffffff00000000}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "c7139c", 0x0, 0x0, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @loopback}}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0xc0}, {0x84}, {0x16}]})
write(r0, &(0x7f0000000300)="ccf06c9bbdc73dfb7c40df5037ca", 0xe)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = socket$unix(0x1, 0x5, 0x0)
bind(r1, &(0x7f0000000100)=ANY=[@ANYBLOB="2d01da"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040))
dup2(r2, r0)
listen(r1, 0x0)
select(0x40, &(0x7f0000000000)={0x1f}, 0x0, 0x0, 0x0)
select(0x40, &(0x7f0000000300)={0xff}, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x48}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f00000000c0)={@local, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @empty, "", @remote, "2c65af34fc93444caf29ebb1a3a1ccdc"}}}})
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x7)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000080))
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000580)=[{&(0x7f0000000280)="3851782d03633a6b71aa09b1000ba5aed6a98b089b403c06ed19cb0f5d6a8e8e0cfda6615ad7812628a4230155e0e274f6740fb861dbde4d8dc9b21f047cf82c82e5255a4f811d9e252875ba35e9b6a07a26e28de439bdfaff9d041f13561d8865e5257f233fdf3a13a51f228b29a8ae8de46022c9ad9f1b6b898e36cf6f2a", 0x7f}], 0x1, 0x0)
openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x3)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
r1 = msgget$private(0x0, 0x2)
msgsnd(0x0, &(0x7f0000000100)=ANY=[], 0x70, 0x800)
msgsnd(r1, &(0x7f0000000500)={0x3, "c5e94674dbd3604cde5c3557da6461341b1f80b87c9e9c0a41a1995045af2238befbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6a"}, 0x6d, 0x0)
msgrcv(r1, &(0x7f0000000580)={0x0, ""/18}, 0xffffffffffffff98, 0x1, 0x3000)
msgsnd(r1, &(0x7f0000000200)=ANY=[@ANYBLOB="0300000000040000db4436958b193c67b6ce0c093bb0bbf3b3245230033f58052ebd418aa658361916fd47a14a3a74259ca57335615dd09efc3ddf520906bf778f89cf9aa9da5328755e3b05d90ba5ba0c00faff96c620f0866300000000"], 0xb, 0x800)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000680)=""/9)
msgrcv(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="00000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600"/168], 0xa8, 0x2, 0x1000)
msgsnd(r1, &(0x7f0000000140)=ANY=[@ANYBLOB="0100000000000000bed026e4a61a5a31474bcb3caaae961351edc3de9f0f51f11ee4b78001b2755bbc86636ce1323c266151b7cedff6ab0d4575ceb3868bcb851e5bb5bd9870720be6fdb977fe6ab5d8a3574d7e584505de9e3b5292f2e5bb3588b2c1930f164befb0e8bfdba2df86bc557b60b6a18873a8ff91636435a38f1b5f51f8a565f36cf892768f23258eb980351c5078d1202e08dcf5"], 0x97, 0x0)
msgrcv(r1, &(0x7f00000002c0)=ANY=[@ANYBLOB="00000000000000000000000000004adc000000000000000004000000000000000000000000000000000000000000000000000000000000000000005db3d275bdca7b70f1a856cb358995279cc6b2ef38d9d705ea1145095bf2852a7597f26e5233fcf0706172af4a1826"], 0x3b, 0x0, 0x1000)
r2 = getpgrp()
msgctl$IPC_SET(r1, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x2e}, 0x0, 0x0, r2, 0x0, 0x0, 0x0, 0x0, 0x1f})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fa090000001e328a1811", 0xfffffffffffffcb5}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x100, 0x0)
open$dir(&(0x7f0000000600)='./file0\x00', 0x40, 0x102)
openat$null(0xffffffffffffff9c, &(0x7f0000000640), 0x400, 0x0)
r3 = socket(0x20, 0x4, 0x5c)
getsockopt(r3, 0x7fffffff, 0x10400039, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
r2 = socket$unix(0x1, 0x1, 0x0)
poll(&(0x7f00000000c0)=[{r1, 0x4}], 0x1, 0x0)
dup2(r2, r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240))
setrlimit(0x0, &(0x7f0000000000)={0xffffffffffffb9fe})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "d730c15b00f4ff0000747beffde400"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000002})
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300000000})
syz_open_pts()
syz_open_pts()
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r1 = open(&(0x7f0000000200)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000100)=[{&(0x7f0000000380)="90c3fe67eb586898600425f2f573e0d1ac83c18d00c8e22066c0d389fe895a974c8d45aaf9a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3214ed85fb20e088c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e32082cec454327b6a1522c332ea628b8cb672e9e724780100000017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802000000000000000200000000000002", 0xcd}], 0x1, 0x0)
writev(r1, &(0x7f0000000180)=[{&(0x7f0000000240)="06", 0x1}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000080))
shmat(0x0, &(0x7f0000ff9000/0x4000)=nil, 0x0)
shmdt(0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0xffffffffffffffff, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
setreuid(0xee00, 0x0)
r2 = getuid()
setreuid(0xee00, r2)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r3 = fcntl$getown(r1, 0x5)
ktrace(0x0, 0x5, 0x71e, r3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x2d}, {0x3}, {0xffe}]})
syz_emit_ethernet(0x36, &(0x7f0000000c80)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "64c739", 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast1}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCGETIF(r0, 0x4020426b, &(0x7f0000000140)={""/16, @ifru_broadaddr=@un=@file={0x0, ""/103}})
r0 = open(&(0x7f0000000000)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f00000004c0)="90c3fe67eb586898600425f2f573e0d1ac83e58d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d90ca0e27564c42ec8a60444d6f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bbc454327b6a1522c332ea628b8cb672e9e7247818f970e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c46106672cda99d1c3471259d08198e13683ee6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c8296827600d2c4d3054126e21463", 0x181}], 0x1, 0x0)
write(r0, &(0x7f00000001c0)='w', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
unveil(&(0x7f00000000c0)='./file0\x00', &(0x7f0000000180)='r\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0xf3f89a4fb539339d, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x45}, {}, {0x6, 0x0, 0x0, 0x20000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
sysctl$vm(&(0x7f0000000040)={0x2, 0x8}, 0x2, &(0x7f0000000580)="156533bd", &(0x7f0000000640)=0x4, &(0x7f0000000680), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x54}, {0x5c}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
getsockopt(r0, 0x0, 0x6a, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
r1 = socket(0x11, 0x4003, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$WSMUXIO_ADD_DEVICE(r2, 0x80085761, &(0x7f0000000140)={0x3, 0x8})
ioctl$WSMUXIO_REMOVE_DEVICE(r2, 0x80085762, &(0x7f0000000040)={0x3, 0x8})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xfffffffe, "0000000000b47e00010000001fc1143da58d7e88"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)="0dcf67520990982c582dd6057e8d1069ce86b4854475cd2ad14516872e5fb8b07102f1f8b3c02aaeb9b32f1461a2fe9cafe8f3a3458b93e501ccc490dc2a968bc43140c8eb7b5882a12fa011c9518219d170f2aebe4c5aed28582ee61ecb44ebd70dda7c805d54b845bda2697bbe578a17dd3da474c02852258308051c7695c993dd9b5a086b4f954c554a8f160341004ad9366eef994b32816ad415fa45c1575ac47d55fac57bf6ce2647f4518900e0a180172a2ec178b3270f5dfc12b1058c5d6bea888cae054af3f59fa44f362c443bcc0bb6808f08f9ca3dba9a140e0000009cfd857f09110d0d0128ed517562257b8ecf1c7dec9de7a31fbe550e4f7d4ce0d53bbd2e31c609ff9872a6e35d3702176d243484b3f1ff223b4ef4f9f575cf67579ff3a99e58d8c0e20fadaa7bae3c301675febc67b85cde", 0x139}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000100)=[{0x25}, {0x6c}, {0x6, 0x0, 0x0, 0xfffffff7}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x5}, {}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(0x0, 0x1, 0x40000c02, r2)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x15, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000000)={@random="83771891dde9", @broadcast, [], {@ipv4={0x800, {{0x8, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast, {[@timestamp={0x44, 0xc, 0x5, 0x2, 0x0, [{[@empty]}]}]}}, @udp={{0x3, 0x3, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x20}, {0x3c}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000080)=ANY=[])
socket(0x2, 0x3, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000700), 0x0, 0x0)
open(&(0x7f0000000740)='./file0\x00', 0xac5bfaddacc9fa84, 0x0)
pipe(&(0x7f0000000800)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
close(r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, &(0x7f00000009c0)={{}, {[], [], [], [], [], {0x0, 0x0, 0x6, 0xfffffffffffffff7}}}})
r1 = kqueue()
kevent(r1, &(0x7f0000000140), 0x7fa, 0x0, 0x407c, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000000000010c8e7d00000000ddf500"})
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r1, 0xffff, 0x1, &(0x7f0000000000)=0x83, 0x4)
sendto(r1, 0x0, 0x0, 0x201, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x67a)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
readv(r0, &(0x7f00000001c0)=[{&(0x7f0000000000)=""/90, 0x5a}], 0x1)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
dup2(r1, r0)
execve(0x0, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x400000000000})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x1, 0x0)
close(r1)
socket(0x400000000018, 0x3, 0x3a)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000080)=0x3ff, 0x4)
connect(r0, &(0x7f0000000040)=@un=@file={0x0, './file0\x00'}, 0xa)
open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0xf14, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
select(0x1, &(0x7f0000000040), 0x0, &(0x7f0000000140), &(0x7f00000001c0))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020691f, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000040)={0x6, 0x18}, 0x2, &(0x7f00000000c0)="207f8bd6", &(0x7f00000001c0)=0x4, &(0x7f0000000200), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCSRTIMEOUT(r0, 0x8010426d, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{}, {0x50}, {0x6}]})
syz_emit_ethernet(0x4a, &(0x7f0000000140)={@remote, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "010001", 0x14, 0x0, 0x0, @loopback={0x4}, @loopback={0x4}, {[], @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000140)=0x9)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000200)="20acd3a25a7dd34404f15aa8dc606e53d4cdfbad6c4b810d8b8efbe86382c76665ceaf3131f6dd55823f98472cb8b2e32deb968aa98a02b77ddf84407885ce93d470419d9601c306fba5da94f843b0cc330a", 0x52}], 0x1)
setreuid(0xee00, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0xb237e70fe97988ce)
r0 = getuid()
lchown(&(0x7f0000000040)='./file0\x00', r0, 0x0)
faccessat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000380)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x7, 0x408})
sysctl$hw(&(0x7f0000000100)={0x6, 0x2}, 0x2, 0x0, 0x0, 0x0, 0x0)
pipe(&(0x7f0000000400)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$FIONBIO(r0, 0x40047309, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000580)={'tap', 0x0})
r1 = dup(r0)
r2 = dup2(r1, r1)
ioctl$BIOCGDLTLIST(r2, 0xc010427b, &(0x7f00000000c0)={0x0, 0x0})
open(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0xf14, 0x0)
select(0x1, &(0x7f0000000040), 0x0, &(0x7f0000000140), &(0x7f00000001c0))
connect$unix(0xffffffffffffffff, &(0x7f0000000040)=@abs={0x1, 0x0, 0x3}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x66, &(0x7f0000001500)="0000ed0cd9012a605ae6b707c58208b8", 0x10)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000040)={&(0x7f00000000c0)=[{0x2}, {0x100}, {0x20}, {}], 0x4})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x5}, {0x45}, {0x4000006, 0x0, 0x0, 0xfefffffe}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
mkdir(&(0x7f0000000380)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x0)
chroot(&(0x7f0000000480)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00')
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0x2}, {0x6, 0x0, 0x0, 0x485}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = dup(r0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000240))
ioctl$FIOASYNC(0xffffffffffffffff, 0xc088444f, &(0x7f0000000240))
pwritev(r2, &(0x7f0000000500)=[{&(0x7f0000000140)="ad80740490377706ed82a1bba52bb74afafd86958f27c50a5e25b9095e846e0fddc135ada439bedced5e452823ab37661a08b9f504000000926d41943a961bccd9879c98b10718fb2b000000006e1b58944dd16c2df783384b2988bdcaf4d4c25c08fd705875a7a57fc5d21e9f91748a121189c10ae64fbb421e53fc740ba0434504af2a3149018de4f1eea8b1d88978acd0fca90e5dcc44846684d050aa9d3e7ad51949cb8a0611509aff269b9ee4ee78cac32cd8e3ed2a94320008cc0131349e67358267540d051fa1cce9663d6e9745a349fdc929b7db94848351d96c7f15dcfa6e674c0e36a94db8df8fba61dcde98d7d48e7f5cd1aa275f70", 0x158}, {&(0x7f0000000080)="2b4f8f030000005485c9dbc244f420d66c8e063c016a99faebee87369037984bb7f5afd68241dfd7dc4e477551ac54b68a5df951b6", 0x1f}], 0x2, 0x2)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r2, 0x0)
ioctl$SPKRTUNE(r1, 0x20005302, &(0x7f0000000100))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1021, &(0x7f0000000000), 0x10)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000280)=ANY=[@ANYBLOB="140000002900000033"], 0x38}, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000300)=[{&(0x7f0000000200)="0002f350d37c45941a3c212047eac32c93b5433b483534a185cd98917be6019bb60efd4e3e363ba6ee0a236c879130a347b4f3ae02797bd837846cfdbba859a657606a4c33fdc9a86a4433ef2b4f9eb720d5957f57e0e6fac6a38df38d42eda66c61f80a4d797389f725f530e00845df5b05887b64ea59a7d4b6e6911631c385ed9ea16ec8b6f5f7e68de03d39c36e154bd766a953733f58e15f0edae546db4c96c43b184ff9a529ef3ac73619f92097ec99ca7493545c99895bee4cb38c2ed35734", 0xc2}], 0x1, 0x0)
r1 = open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
r2 = socket(0x2, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020697a, &(0x7f00000000c0))
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0dc000"})
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0xfffffffc, 0x0, 0x0, 0x0, "0360772ad951823f37001e00"})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xc)
setegid(r1)
setregid(0xffffffffffffffff, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x3, 0xbcd8, "ea1899885095a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
poll(&(0x7f0000000200)=[{r0, 0x4}], 0x1, 0x0)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/63, 0x3f}], 0x1)
read(r1, &(0x7f0000000640)=""/193, 0xc1)
execve(0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f00000000c0)='mN', 0x2}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000080)={0x0, 0x1, 0x0, 0x1001100000003})
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1d, &(0x7f0000000000), 0x4)
sysctl$vm(&(0x7f0000000000)={0x2, 0xc}, 0x2, &(0x7f0000000140), 0x0, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000240)={0x4, 0x2, 0x0, 0x16}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc1204460, &(0x7f0000000240))
r0 = kqueue()
openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x200, 0x0)
r1 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x0, 0x0)
kevent(r0, &(0x7f0000000000)=[{{r1}, 0xffffffffffffffff, 0xc1}], 0x3, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{{r0}, 0xffffffffffffffff, 0x41}, {{r1}, 0xffffffffffffffff, 0x5f, 0x2}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f00000001c0), 0xb, 0x0, 0x7ffe, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r0, &(0x7f0000000000)='l', 0x1, 0x0)
syz_emit_ethernet(0x2e, &(0x7f00000005c0)={@local, @random="3e94e1b1da6d", [{[], {0x8100, 0x5}}], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @local={0xac, 0x14, 0x0}, @empty, @local={0xac, 0x14, 0x0}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0x20004455, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000a00)=[{&(0x7f0000000e80)="26e97b62abb2071089d6404d58089c32d70b1e6ccac67da263aa3c9c41efb47324e8b414b17d01b4bcf117eaca252036f7659d2363fd333cb6d06601b86c7b263395794276b5e9d32cd816e2355b275e34e5c0170fb85ffcbe7c65b95b2765af2772ba254f4f7c7f578ccff3e06b25895d97f0a4c203d10e2b877a99a183c3bbd159789eed06ef672dea9eca5fd06f2d10b2b2dfaf6841806ce6c0d51f1f980a78f10281c408fa3211e97f6fd560581011c1dfbf4eca04478fb7737f82b027f66d9d31596770a5493a511c8f4e1d7d79083ce893bfed363fccd57100149b58018d5ba28bce796d1e00199d043633bfa21c9cb57f0cc5ab4ce99e41f3402524166ed69b4466750cda451ea267cd5f164b7f1779bf271508ad2e0ddb3877de8141d22f017ac8f0b43233b16e610ea9abe9df433ab0fc99615a05575e94d0448781cc9b8cd73bd5794fa0da70b154d39d82d9156647d1bb13f9bed8ef9f00e6895528f90eb455ad876dcb9f7f537be999bdf588cf76dd718d11d33eb1ac81c54ab20aedf2faa1fdd888bda9e2c5c313ab26e81c1f35d771cc74c46d41a6939ca5b27faf4a95001106bb5177af3577a9f756c3fbf19f2b0035b39ffca36d8d732e6d1a383ffbca358db25f49a4f9f879f44a20810fc04b7d63b2b882306c9daf386fc5206effcc5b7df64a0b82c457174b6fe9e1e914df66bc38f315fb8906b12b031664e591419f1d1a743d8999ddc30634e69c62758c1a8f05d715655e06247ad7052801e49001e88b0d37483cc5900cd28311ce041bb74690e2296b3a90f31fd1bab463b400974198df67636868b403a9b29c6d64fec37cd1e256177b356c98c46c557711f9d8cb9dd0ff2647a79d1960604666d97ad24feb17083bc042305cbda6a99d81f4de264a03ea297bd2a2fb4117a71056f7d5398e5823ab9a313e3adebb625f96ebc045f94dda507701bafbd99840c7594e600dc537b7300b5ddd591fc97d2141eea8808c406ff083d9b3fc8c050adc01", 0xfffffffffffffe01}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{0x20, 0x0, 0x0, 0x200}, {0x6406}]})
syz_emit_ethernet(0x22, &(0x7f00000001c0)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x3, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @remote={0xac, 0x14, 0x0}}}}}})
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x14}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x16, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000080)=0x4)
poll(&(0x7f0000000040)=[{}, {r0, 0x7c}], 0x2, 0x0)
poll(&(0x7f00000000c0)=[{0xffffffffffffff9c}, {r0, 0x40}], 0x2, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x4, 0x12}, 0x4, &(0x7f0000000c00), 0x0, 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r2)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
pwrite(r1, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff})
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
unveil(&(0x7f0000000040)='.\x00', &(0x7f00000000c0)='c\x00')
symlink(&(0x7f0000000000)='./file1/file0\x00', &(0x7f0000000080)='./file1/file1\x00')
unlink(&(0x7f0000000180)='./file1/file1\x00')
sysctl$net_inet_divert(&(0x7f0000000000)={0x4, 0x2, 0xf0}, 0x8, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000001c0)={0x0, 0x7ff, 0xff, 0xffffffac, "38d52300fd05150ff8ffbb000000000800"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04baa049e8d759eeaddbd0908d33a9769af0094c5240654a93f108", 0x8a}], 0x1)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000500), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0x41946472, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x1b}, 0x2, &(0x7f0000000080)="d479dd2e4ebd250c69d299f1", &(0x7f0000001140)=0xc, 0x0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000fff000/0x1000)=nil, 0x1000)
setrlimit(0x8, &(0x7f00000000c0)={0x9, 0x46})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x41, 0x5, 0x6acb978d, "d5426d0943d1edd3000400009591d66f7a5329d4"})
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000300)="20a30b603f84a481d09525888624fc3a633227733075c57935d76fe6c2754218943c8850a13315a25fe3e8155aeca26e3b752148f0a5ec6fa3ddb1d77cf08f0b5475f35a795661d50c2aa4b82d59a1402e2a1ba72429e401515dcad78d88e7910a4024ce4694aa2cc2f1f3793c1428f464251d9244eb4969e6db0ca558a7d488ec5465bd903662f0a968631196bf0e8b70b4dbf1066fb3494f60d5b73d1c119821594ee9cba55b5e8527d3cb332d9406cf48e049d74c4bb462fc1b8bab489c750b6b5bed067da9485d992a729d9f15a40be596668a45c87943d1", 0xda}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x81}, {0x7c}, {0x6406}]})
syz_emit_ethernet(0x4a, &(0x7f0000000540)={@random="490a6bbf35e9", @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "c41fad", 0x14, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @loopback}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @tcp={{0x1, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x2d}, {0x25}, {0x6, 0x0, 0x0, 0x1f}]})
pwrite(r0, &(0x7f0000000280)="c6358e4e0ccc42bd0bba499911b1", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x84}, {0x5c}, {0x6, 0x0, 0x0, 0x400}]})
write(r0, &(0x7f0000000280)="731f5c52643f0e37bf54dd2a1b32", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000002c0)=[{0x2d}, {0x80}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000040)=[{0x1, 0x0, 0x0, 0x9}, {0x3c}, {0x4c}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@random="1b329ff120e0", @random="c182c3cc9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
setrlimit(0x0, &(0x7f0000000040)={0x4001, 0x100000})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setrlimit(0x2, &(0x7f0000000040)={0x60000000, 0x60000000})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x240, 0x0)
mmap(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0, 0x2011, r0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000200)="5001051600000000c600000000000000000010fecea11ea8fef96eefc73fd3357ae26caa0316fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2b23ebbc257000000002e27acd602000d7d026ba8af63ff37282902e4fd89720fd3872babfb6a00000006d73bc571c252944f36a00f900064e0629616b0a8e700000002000000d9f02edf9c620000000e0208b31fd3f80004ffff7b89897d3ffa8f799aeeff000000561d9058fed6acf42813c67be2313927b913cebdbb7e563c73891d41f638837f11c34b0cceb2024d080000000640906fa45d709da9d158d945a2a5050000006e27749bbd97bb0d03fe2d04a1b9ff59865f1fde95d68896dc7ed7b187050098e04e34248daaf9231f0de030323b4125ce41203583671ac5e51a7a3dc1a0c21d548f2de6c031b7b303757e60642b45cbc51e0d8e24017571fd97e58110ecaa340065f3", 0x150, 0x0, 0x0, 0x25)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
kqueue()
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r2=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080))
sendmsg(r2, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x40}, {0x2}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@remote, @random="c182c3cc9746", [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7ffffffe})
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r1 = kqueue()
r2 = kqueue()
kevent(r2, &(0x7f0000000000), 0x7c, 0x0, 0xe51e, 0x0)
kevent(r1, &(0x7f0000000000), 0x4a79, 0x0, 0xfffffb7e, 0x0)
r3 = open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
dup2(r3, r0)
sysctl$vfs_nfs(&(0x7f0000000080)={0xa, 0x2, 0x2}, 0x3, 0x0, 0x0, &(0x7f0000000140), 0x0)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
fcntl$lock(r0, 0xb, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000240)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
readv(r0, &(0x7f00000002c0)=[{&(0x7f0000000a80)=""/66, 0xfffffe0f}], 0x1000000000000300)
dup2(r1, r0)
write(r1, &(0x7f0000000000)="f6", 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x0, 0x81, 0x1000401, 0x80004b8, "0d43d3d3d3070000010001080000000d00"})
writev(r1, &(0x7f0000000280)=[{&(0x7f00000004c0)="d6b225327aa6485b15437ecff6133f56df259f89ae9c0441260d0c1257804d5c1f5d661fd58859fd8c0c2f4e5c3708e1a618a320964946c4ab02ea563534597412938af40d766fa6f32f3fea8a6d73a80293b264f92527c82ba6e145523d0f645a20076cde1435cb0011c999c8059ca3ff6ac04eebc464b2b0b1d256e54724a278002fcadb9ed0ff5d13a60a220ac885c867641c8389eff2986abebecf0591d14dd9511b51c86e6ce3a9e7f6ba1a362ed3c6a6d977963b7724ddd9f55255b62ad06adcea11f8fdab45b89244a72fc6df69342b1d76068de7a39db5c5ce80a303744db245262b40756e037b5ef74bf723bba263c94b7c2e7787af754e126c3b1c582081959dab519d712d93a6d164af1ee1089c59d414c93b469a31a25b25f428e42905e6626f88913b8c345741c600de62f821367375a4755b7907ede19ee5f9cccba4fa808589ad666d5cca4ac3e2c2decc8a180f94540efd8761fdd8b0562b0166259a03903a16d7fae975f7bb63c83779e2281d2714b9e6b17c765060b459a6df651e270f587f4a2862042aa1466200f29d18675d6ac6fff5bd07bee7cc525feb6a6eb46b3b01e3756941a9a3a599fed99c2ae9f6a5748ed7b042f72037f26190935e06e3628d9c7fbf658d117bef025d6cefa8827fa6043fb98ab3e18103d44381d810acda0bc5bd3f25ebbdd076ad5bcbf4908b4c6e217dfef263aef0302e66d69f2c6f0571f1686badbaee027aa1e53dde78ade41be304e771bae6a7f8a329fac646f598369c7acf5824b6d2334f73133dd66ecf5bd89cb3a561df0d1623b4da53733977dad25d0eb6434a991b7e84769e067783d92d9065a1dffd26b87403ff05bf57ef6a527c57581017063551a0f0d33607d16023e30d41f5451882269e98b7b17724a0c61600d8ad59415b714afffb686ed30482357e9738841658f58be6aa8fd7035df53fdf4775318fcee512506394dc3367a7c4bbbc7fb80700b906457b3f1ec2a6865771794894c3365c22df7e226a96123f6fd5a4acb2b4f3ef3228488d7982dcb9aa4adb947b664a6a395f0562ff9e7276847b0e2a7c8f1d4ffe771dbdfef830170874b90c9addc4f6d5404d3568e6294ecb5dbe027018d2e57ed04fd676582ad375498ea080c6102cce4770cdd3dd3cd451409ed8c7b9468002b8396916e33e5e54fbaaf15802c8b9867d26d0a1b1ff8948ac20237e66f13cff1a134a5714f407c03636068e1c08c0115cd41a89933504831eb27d0540eb5e56875243bf990dec583b4f08f6237a7b47d50b2c55ec801ace03e17793520e2615447f81f21b3971de00c95b1a46f52580a4f63ee4e501e5573cb7f7cc5d90350bac53f3cef20b62913ab278e2813f799b2e62a253d8a85702cdc2d9fced6a15c241068e7aeae03aa1740046f214b4e68c4a8fa5f74082d276b208c260cec635d4a714709e0182e8e0c524b2d0158ef6ec29611463738607c84a6cd9448c3e558933463198f583a90d30ebcf7e5f1a30516d79d2551c5c698dfffd7022839b53aea7571da7dfa3f913772b5ca93e66a24cf749c5b58504bc5d465f59e188312f9026aa95d512a367f5f5660e25f908d679de92c02f73f7f9ef31f11dae87bf31bad8a3c9b3c02fb0d4dc42684f38905f72945ec1131a71aaa0fd9c2e150c472e559b4845a42fbfdd2884462c10afd1929cb44bf704cc48cad780dfc782d3bbf92485634d2d4e377d3caf4dcffe01ae3126dfe35bc52f16ef6fd22ff79065a7dd1253dfd9c6f39fbe58db7b1b1b575eb415bda974617f6c15b938ac845c4a093fcc36da67a95435c1b1be273a522fb3d886383cf1a405a847a39c44ddbad1cf943b676880d609f9dd794293009d7be961340856b0c7aa35f27fb2dd9d83724e4c5899faf25432d607b3699479d76deed4b3b40957bb1a56277422a2a8ebc68d641a6ab448bde59d0c311653fa37a67f0670dd5c127bff606ec5b3f8d726f8de86a6c9b5091959466d437ed272e4beb38e792ba577a7f09481f073b40a2296b99217acfd75a45179c5a64beef431dec6ceac7b6be9f6a3b9088a49a7c59201ebd0acb0ffa30bf9d308010d", 0x5cb}], 0x1)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000040)={&(0x7f00000000c0)=[{0x26}, {}], 0x2})
mknod(&(0x7f0000000340)='./bus\x00', 0x80002007, 0x0)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
setreuid(0xee00, 0x0)
r1 = getuid()
setreuid(0xee00, r1)
ioctl$TIOCSPGRP(r0, 0x40047477, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f000081d000/0x3000)=nil, 0x3000, 0x0)
syz_emit_ethernet(0x66, &(0x7f0000000000)={@random="abd15bdc0870", @local, [], {@ipv6={0x86dd, {0x0, 0x6, "30c0dc", 0x30, 0x3a, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@time_exceed={0x3, 0x0, 0x0, 0x0, '\x00', {0x0, 0x6, "9543a4", 0x0, 0x29, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @local={0xac, 0x14, 0x0}}}}}}}}})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0x800000000000009, &(0x7f00000000c0), 0x8)
read(0xffffffffffffffff, 0x0, 0xc600000000000000)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0xb, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f42", 0x800}], 0x1, &(0x7f0000001400)=ANY=[@ANYBLOB], 0x70}, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x48}, {0x7c}, {0x16}]})
pwrite(r0, &(0x7f0000000240)="f94c4c49dfd7675ab985f9af880d", 0xe, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x800000000, 0x2})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x7, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000080), 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
msync(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
munmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000)
pipe2(&(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
pipe2(&(0x7f0000000140)={0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
dup2(r2, r0)
poll(&(0x7f0000000280)=[{r1, 0x4}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x15}, {0x2d}, {0x6, 0x0, 0x0, 0x8000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000280)="2fbd70ff10ce171cdbac01876550", 0xe, 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0x9}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000100)=[{0x35}, {0x64}, {0x6, 0x0, 0x0, 0x47f}]})
write(r0, &(0x7f0000000440)="3c9ed0f773672ee31265ac52c02e", 0xe)
getgroups(0x7, &(0x7f0000000000)=[0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, <r0=>0xffffffffffffffff, 0x0])
setgroups(0x0, 0x0)
setregid(0x0, r0)
seteuid(0xffffffffffffffff)
syz_open_pts()
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
ioctl$TIOCCBRK(r0, 0x2000747a)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
mmap(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000040)=[{0x50}, {0x5}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000200)=[{0x64}, {0x87}, {0x6, 0x0, 0x0, 0x553f8734}]})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
setrlimit(0x8, &(0x7f0000000100)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "919b914c7198b4af5e08004f00"})
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x6)
readv(r0, &(0x7f00000001c0)=[{0xfffffffffffffffe, 0x8}, {&(0x7f0000000040)=""/78, 0x2b}, {&(0x7f0000000140)=""/60}], 0x1)
r0 = kqueue()
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
kevent(r0, &(0x7f00000000c0)=[{{}, 0xfffffffffffffffd}, {{r1}, 0xffffffffffffffff, 0x9}, {{0xffffffffffffff9c}, 0xffffffffffffffff}], 0x6, 0x0, 0x5, &(0x7f0000000240))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000500), 0x0, 0x0)
ioctl$WSMOUSEIO_GTYPE(r0, 0x40045720, &(0x7f0000000540))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
mmap(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x0, 0x10, r1, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)=[{}, {}, {}, {{}, 0x0, 0x0, 0x0, 0x0, 0x4}, {}, {{}, 0x0, 0x0, 0x0, 0x4}], 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)=""/184, &(0x7f00000000c0)=0xb8)
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000240)=[{{}, 0x0, 0x0, 0xfffff, 0x1f}], 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000200)={0x0}, 0x10, 0x0)
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x2, 0x0)
execve(0x0, 0x0, 0x0)
setuid(0xffffffffffffffff)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x27, 0x0, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0x41946465, &(0x7f00000006c0)={'./file0\x00'})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x4, &(0x7f0000000080)=[{0x1}, {0x60}, {0x44}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(0x0, 0x0, 0x0, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x8, r2)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x4}], 0x1, 0x0)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
dup2(r1, r0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000580), 0x0, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f00000005c0))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
socket(0x0, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00aa00000000000000008f00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
clock_getres(0x0, &(0x7f0000000000))
ioctl$VNDIOCSET(0xffffffffffffffff, 0xc0384600, &(0x7f0000000100)={0xffffffffffffffff, 0x0, 0x0})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x18, &(0x7f0000000100), 0x4)
sysctl$kern(&(0x7f0000000000)={0x1, 0x29}, 0x2, &(0x7f0000000080)="83061c5bfbd9ff905ae66f3e5236841d18950d0dae687d3d521c990bd97431da754d7e358024042f2dbc35cd0a062082d622e999ba9ebd1e32a08b75287dce1f7777cde3cc2d2d20a4c128da1563dde437556e10865cb2f05af3829033ab8868", &(0x7f0000000180)=0x60, 0x0, 0x0)
shmget(0x1, 0x3000, 0x0, &(0x7f0000ffd000/0x3000)=nil)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000513600000000000000004", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210]}, 0xfffffdfa)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000140)=ANY=[@ANYBLOB="f4181a"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendmsg$unix(r1, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0xa, 0x4000000093})
r0 = syz_open_pts()
dup2(r0, 0xffffffffffffffff)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000240)=[{0x48}, {0x5}, {0x6, 0x0, 0x0, 0x20b}]})
pwrite(r1, &(0x7f0000000040)="e81af191c7a750ff690700317e37", 0x60, 0x0)
writev(r1, &(0x7f0000000640)=[{&(0x7f0000000480)="8ce4c75c9bee1af2d3c7185c48fcec1d63993d3263998519c0e52cfbd4370b11b84eb3f28b1e3b6ff4f60dfa56f17a733a89a2fb12f5deaae0ee284f0cbd1af3a9cfb15af9128c0261018e6385115189ee1380af6ebb00c55152a6477ba48ca742fc21c8f92525ada0e72a9751c5e83ec84b428047e61e73eca4d39981c995bd85d3f33f01abf6daa8d5c7d8f2bb47dee001c088a47fb752b4c0bfa30a9f3ef32ef6ecdea5d0dae62e97f4c2a939a6e179dcff3701d0ccc5c48b6b8a5f707c7b66aaa10a5f323e128801c20ead2e7125e20a9244dcdf4bfc29283c45f2edfa5cfe", 0xe1}, {&(0x7f0000000580)="bbf9903deacd7186e82d86445664f19c194feda53f5bfc3608f70fc91cb21b6a96161a1e380c5683d8028f8e2785730387d8c7ec640a45da1a720173fe521723d87aeb5ab932c3b9775b485bd24de61a653144ae488e1c377a1240a33a48e4c31624009189472163a34be57f49d3c62c96eea228c65ce458d949ad627d", 0x7d}, {&(0x7f0000000600)="f689e53b6a", 0x5}], 0x3)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
sysctl$hw(&(0x7f0000000000)={0x6, 0xf}, 0x2, 0x0, &(0x7f0000001080), 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f00000011c0)={0x6, 0xb, 0x2}, 0x5, &(0x7f0000000180)="613393f6e7c821f962d05d25aa800201670f73711545fa211601ed3092c7e6e58e883f7ade5e5ac659948c0b37536c0c9442d751d4ebd900d85617d70ee23a14d1b51a81a45c118d9f2bcb859a333d5e16a7b2da390a01c80aa8ebea088ac2e10100000069bba80100000000000000b3eea53d6ea1d205cd4c312eb5fe8523758226399d0c7e29d48a588ec59354669b3b0595c4f6759cdd4ed4abe07e760f9a897bf1eac5fd34746328350000000059ebdd7d38222b59ed7b50543390b22d05a06f15fed41ca9066efba1768e55ff967eeac9b7f7a0fddb2f24ac7ff3b1e8ccfe8364acadd0696186e075c81d2e915ce8b927da134a15d632952b10a15ed6316953c1921d39be50da1715e5009d69561a59b1bb8d375d9db756b240e772d8945559516d97ca2e94e1db0ecc9b1ff88f0561bd4924403cf62f064bad07148cbd22c04a7536e21f3d4382871153560abd4e47fb5e55f93e35f55186c8890b902de9737491178c", 0x0, 0x0, 0xeb3194968c3d41cb)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000090000080007", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x0}, 0x0, [0x0, 0x210, 0x0, 0x0, 0x0, 0x3]}, 0x3c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
write(r0, &(0x7f00000006c0)="a326535e2c399acf10ed430e7469924707284c2337ff9544ebce2bb4392e6e224aa7bb116e9ce92d77f5c42c9fdf766b863cdb5e886acefa01a9d8a627fbb6e8e75f8577cba0eebe556ed5bd9cccbd858006175e578490ea780ce2fad81ae0f4df91b32d4f146b543dd0f101d173bf2c9cddcda5aa05164d8d50694eda3cd45e2680680247509001a2cf550331085d035ac846cda381d96603441ed098b47704", 0xa0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000001280)={0x1, &(0x7f00000000c0)=[{0x5c}]})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setreuid(0xee00, 0x0)
r1 = getuid()
setreuid(0xee00, r1)
setsockopt(r0, 0x80000000000029, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto(r0, &(0x7f0000001180)="006b6bed3f0471170b607e2b6858164bf0817181e010963bf0fece", 0x1b, 0x0, 0x0, 0x0)
recvmsg(r1, &(0x7f0000000800)={0x0, 0xffffffffffffff40, &(0x7f0000000700)=[{0x0}, {0x0}, {&(0x7f0000000400)=""/170, 0xaa}, {0x0, 0x17}, {0x0}, {0x0}, {0x0}, {0x0, 0xffffffe2}, {0x0}], 0x9, 0x0}, 0x40)
sendto$unix(r0, &(0x7f0000000040)="7f66d351849cf08855afeeaf9a68dc9241d83118ecc071ee053875a04fdc979fd0b72b79cb3b7d4e925a016a43f6200e61055283b9163ce87c7a83cb528577522c43dc682cfa55a86a3fc7ed88901d0aa3f123d378781ee7caf1a79a62d9f645f184c848c00097b2c0022da534c041d8904d87d897c90fc784c390fde374e2c9ff862b5e888f0b69a99a80387f6e8eeeab", 0x91, 0x0, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$vfs_ffs(&(0x7f0000000000)={0xa, 0x1, 0x11}, 0x3, &(0x7f0000000140)="b0979d9d", &(0x7f00000000c0)=0x4, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x81}, {0x6, 0x0, 0x0, 0xfffffff8}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x25}, {0x2}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000d80)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x50}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000140)={@random="1b329ff120e0", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x80}, {0x81}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
r1 = socket(0x2, 0x3, 0x102)
r2 = fcntl$dupfd(r1, 0x0, r1)
getsockname(r2, 0x0, &(0x7f0000000040))
ioctl$KDSETRAD(0xffffffffffffffff, 0x20004b43)
ioctl$VT_ACTIVATE(r2, 0x20007605, &(0x7f00000000c0)=0xffff0001)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
r3 = open(&(0x7f0000000180)='./file0/file0/fi/file0\x00', 0x20000, 0x0)
ioctl$VMM_IOC_INTR(r3, 0x800c5606, &(0x7f00000001c0)={0x7fff, 0x6, 0xe8})
r4 = open(&(0x7f0000000040)='./file0/file0/fi\x00', 0x8000, 0xa4)
fchmodat(r4, &(0x7f0000000080)='./file0/file0/fi\x00', 0x101, 0x2)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)="c502a58817c42fbfa022e175", 0xc)
faccessat(0xffffffffffffffff, &(0x7f0000000580)='./file0/file0\x00', 0x0, 0x0)
munmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000)
mprotect(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x0)
mprotect(&(0x7f0000ffd000/0x2000)=nil, 0x2000, 0x1)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x5})
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x8, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
setreuid(0xffffffffffffffff, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x4001, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206910, &(0x7f0000000300))
r0 = semget(0x1, 0x0, 0x0)
semop(r0, &(0x7f00000002c0)=[{0x0, 0x401}, {0x0, 0x4}, {0x5}], 0x3)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x6}, 0x2, 0x0, 0x0, &(0x7f00000001c0), 0x4)
r0 = socket(0x18, 0x8002, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, 0x0, 0x0)
sendmsg$unix(r0, &(0x7f00000000c0)={&(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000000340)=[@rights={0x6}], 0x10}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
listen(0xffffffffffffffff, 0x0)
r1 = kqueue()
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
shutdown(r3, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x1, &(0x7f0000000040)=[{0x6, 0x0, 0x0, 0x8000}]})
syz_emit_ethernet(0x22, &(0x7f0000000040)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @multicast1}}}}})
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0x8097b0, 0x7ff, 0xfffffffa, 0xfffffffa, "0901a6d3137c00005da24700"})
writev(r0, &(0x7f0000000080)=[{&(0x7f00000001c0)="80db00d338057a93662f92bd3eaad620d069ee8b3fddaf520f35c0f55748b228737f4ec59ebc6ed5412398ab7c4979625ffa789340a45af34672b36f3639224b23416d0e60eabda0673f90403f5ddf58da1482a094ff107ae8fd603b243d682eeff2e9d89488e401df9946df4a1c1d7453e65ac84e447098f209ace6c2c1faa3c7c15c50be0c2a41390a8fc1727fb07b389f2d470908", 0x96}, {&(0x7f0000000f40)="06077e97927520758664c3942f0f0333bf9b65adc99dcef2849294a2b5a6457b7ae634b01e820d39a4d7f47de5b52af098b450189c572d59daf3579e0a3455939f577df7820fd54e1d", 0x49}], 0x2)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000400)="f492e2c08a7062633d1afbe6fa60111f4d59727b42f4e3504b6e268f5be77db7f52f99a4587f149e3b3c5dfc66287e6dbcd25e153dcb37e11fbb0d429f6b840b2e30359ea6aa2468615cb880092300d59fb41dab668cb4cec40cbddef19e638e36fd133eb151324ef654023a2afbce72a9ed5476839c95b4aafabc33559c96f5baf8941a1f2adc5a49d670c7d800b0e3950719f7ac0f80756c96c060effb1e56e43e38a6006b5995c0d0aa9100291da1a6b23140a1287e681423b3c1a8a4534937ad96154c6b218cadc2721441e9a7134b5542ab1a9e7906ce97aa48bd5f8357cc0716ec7479d4f98a3a2a4aee71e78337b89b1c087aa1cd1ae00b259e0b70446278646bcca164fca00b707e12cf3f85bdc2529b0f6f616f571e464ad323e0914288adb94321225c2812b7c79376adc2dadbde2303277b145f997aaf77b36311b202f05e66d6048c454142e1d5b5640079b3ae8d8783b83fc77f9832f61b5df3a9e20e6655e7903d184d5cf914a087efd14c372f2f137d594cf89a630830b0ed6c2fd7c5421cc1df4be5a164fb91f98c31272431957a930b84b1fe24d422b156d2cca3bb4448f8391397d6cd8884e2bc847e82bc0d5425c2123f9b721fff202c40eeff264af4e179f0ec6d3a5157850802e4adb59aec682d2476bd4a8533434e9a9a42cf725b0252252ff928f011170ed7fd8f00a6561486bbce0d87af0bd9f2c632ad1deda82bc7f64e8f5b5803ed59e97c83d0fd15de60991f643a722b45a857db2fbaeff8aa29335f445df478244a4dc744b73272833f708cc8352b35664e634e90046e610608a37ffe4e436efdce2bea42689496b0d2fdab52c323572a59935c7339fdeae6f55ac3659292083aab4d1b5e515b3938dbac3eddb7e091577d1e6af190ae43b2b895aaf5018b869d6cc0ca4514907afde09a582840d0809c28bc66c5cadbb9e3f5683c320847d4f7500bef780948f916bc46d075c86ceb1318b998bc330d3fa21b171579c56ded72c919956583c2dfa88c73ac1fc2d6e74d400197ffcde07746b103953d964b3516d856b391efdef2d06ee9e58fe9230e68fb4d10a3906cffebd55665600db1cdac7fa4f7724f456b1140eaa91038ffac4c24067f91d03b1fa18b230b34a5231a3f91fe9a874a5868fe78c1dda701bd78cff05164b3b931076b0e799ad7b7a713b99e06fe6a987f102f20e8aea49c59b4b2604790550e0af99350de6e29659263dde71ce5f782a85f593d14a00d39b673997d33fad4a1727e9660ee6306702e8d533258fd8ef5b72a5811510f596cb5ef98506fb128abeb95064322dd39fdf5358116dc6bf44d4e94f8ab74006e2823a5c8a0331a56cdf1d9372f09a3c98a69f3dae465f1c8ec468a98ef14b27982f51edbdef8f05aa89f84bef47215111b53d0737f6725c894477482ca6302b911b8fd8be9a5f96fd537d62ce305bfb3176e05caec79cfccb0e491c2fe15f1d77f0dd1f9ed6ea41ebd4327e5022c57e4619084b44e09b4352bbca34cc501e156bad86769d92b4069f9a55abb24a7bb059712d486a7ede1655a10458d26dc872287c3810580d1b41c5741aad7256f7809fc9f470d9cdccb605324e8592d7042a89c90d80b34c24ca0e67b52898c9d58fe02b1dabee74478b20713f6bd06edb79931a695dcb780e902fd57be32fc914f5e9ddab2bdc43ab119bd429b6704ee74a15d551f8f147350a5f1373489536eda9301305d5116bc916bb10629b5e36de75aa91bc071f94f57817730f8422e9909b40508938b9ca93d05eb229d64594c9a55e19cf0f930e62cf0a44e2b69eb07950a01e6b04e97d81d03b8cf4e9372b47b881f03811e84853602d9f086adda148fb7456d7f9a1382d427e19cecb459af4c1abb49ddc4b5e5098116a20355505b3f77a2efef3fbf8dbd3048ec76c9a40936a428a67ad0d3b79171d15afc1be48c3d94d3546f01aa78430462c352ebc2cf7fb008a9eb6b141b6d9b9c72c5737c39a5e7ccb7d06a0ace4997f84f7acb4a694559bc7a81cb0f4cf832e30f7f0437a1c46e41456034e3b6eb81810d86de1baa35ecb8528d12045490c023b6d5ec457218d933c03206abfa6ad34d5191729df37c134975cadb84109133de2f9d89a347745889376ff3caf6dd4c39a1880a943ef37669f4a0e3fa4dd1e9f08029e0873786984a161d06d69cda7a3e3847d7ddeb76be2da7ae5081b12febf492067d9d167d05f113de6684754d2349648976c28c8e11ad63495e9303dd7cc7655be0266ec0676e8bbd5d2cdaae9dc98a25ceb85214502fa07bd8e4b8fd92671f1859ce64bc4d122e706749a2834255631cfb30d564bca1a54e5c6f4f97004789a954171ab3e0418d5d3a75c850995840be8b755e8f5fc03321a9964a38423c530a9e72056fdd6c910129b8f0b15343299ca5e1bec34c31c34be00598d243c22ce4ac4d93b4328cff5f29efcfb5e3e40c10025d871ab56a0ccc67c9f89a514ea0da1d0ff6f27ec52434b21ffdc29847d4eda3775ae1a6fe1e132673b3f9daa3f7d3849be49c91511cb1641eab678f2b8be82dcffbd8e3ee6aa632792641ccb172c29a45b87f77ee8bf8c50d131de0f21d48eaeb84702720b284d7b5dbf6c55342a7b70fcc40931940eee607d108c89e64cab52dc1c4a2035e85ed4359cb53ecf4f310d54e5c1637e0ee12b101bcb662b6b28b0d49ecdc51944cf5ed80a55d32dc11f70fbf8da92a214061df4476789d333d030363346f291e5213207e49663c5b950fe6b4d5324faf72e037537a802eb21caea7a8a99883e661d5e0496283ad3f4480321e5297a0a1c4f659489c35061517a77bcd44bddc786b73455d6b5156aa7f8d3b026fe66d3a76d635128f93a1c96b2487544faef946ef43fe182fa6a8c6d924443bc922eb5f52f7dd5e347ae829d5ee2fd1d055e3de2fb6b9ae6b8b4601782d9fe8d50ca42071522de4fa8f1d42e5608dc2cbc8edb9667dd59e46fb108fa7eb315fdcfa5e90a54ece93272f66dc4c3cf60242aa87ba24e933d9baf949c74551ecb35a0fb921655b8a1dd3098c33ccd1fc4b0c8abde63070c4e0edb04868334fa18b4eddef2ffad06a3ba3eec2bb52d3839dd03a0d747fd365bcc03ddef9146472c4cba3fae67b7eeecf1c12f162294d61fb0505d2ca88569934e6e29f470cea1264f6bcc6f34eab78648cb8e7ca3b745696dbf43ceffe3445f049e4beb06a2ec12fad854a39b2e03b96f6c3921535f4ebcb612f198ee40d216d55cce7ab67de2af631bb40fb6fa734b06b73cf446cbae20887fbe5373f588fb0a2f7a6cdd62382e3035d5442081e856e9150975604d5b5b99e3f1870873a4ac7364930f860f556804091ed216b867a98378ecffd321e461305db1972b61901c76eca273a61e820939393c6391e42949b69e68531f5154dc59748795f53de82cce9847c4ecd118a7bc3fb15b6ab3e83e1c1dfce90531ed412b26e907306afac6100f7306a73629fc431ea7afb5a86a46e855fa2a8af41253666cf0280d4bfd7b9220807e572ff9cbe123ce5c1dd395855ea489851d21a34ff40475013e83b2b1dc2be94f8c14b34e33f06167c50dd78bc69e718fcb46e92f59ed7a49d4dd35f85fd331f27241ca9c0feb0df8b6e1b609cee847e045a0f87d48fb5a2a1d050f1963318b213c59ed9359f289daa2d99a02be5a90dbd7451f0c7a1dfd5c0b64d7bf1c0e8fa16e440d725a00b7a8f47bd4dda5bd7129c1de74aa62f3ad8dc65699eaa3fdf54a921775a1036f13a6b699478364156f4fd2990b9f1e87772f5efd8f00026afe7fd5f94fe22d13c68ebb180c37d5c07c761af5bd45644b1b47c3381dccadcd16e767c857eb00c11fe36c1d442d39166d37da66964e26eb8a49fe58e605f67ce26e8fa9bf57f461ff067dc5fdcaac2ae3bd3b66f4aa948d0221631a119450533b1cca174a582dd2f764be45376cf0c857ae7cb45e8041e4d7a9550b69a38fa47e12c6126325e9899be201b3ba2", 0xb10}], 0x1)
write(r0, &(0x7f0000000280)="4de3a767cb3572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cc3392c70fb4d2ea468e980877377f1cfb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70ac323074dd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19a3dff7bde555c03c89296384aa8f8b1e7179729ea0bb9ac633eedb29996c3da18b7af5150085f27cd1a07a7e9b60d858b2f1e5800125da2f78713e82f66341bc07c02b83799f6c872f9fdc87b82c4f4cb92ef1292f1fc9abaf5d", 0x10f)
r0 = kqueue()
poll(&(0x7f0000000240)=[{r0}], 0x1, 0x7863bddf)
execve(0x0, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000100)=[{{}, 0xfffffffffffffff9, 0x1}], 0x0, 0x0)
semop(0x0, &(0x7f0000000080)=[{}, {}, {0x0, 0x7f}, {}, {0x2}], 0x5)
kevent(r0, &(0x7f0000000000), 0x5847, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
getrusage(0x0, &(0x7f0000000000))
syz_emit_ethernet(0x3e, &(0x7f0000000100)={@local, @local, [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}, {[@timestamp={0x44, 0x14, 0x0, 0x0, 0x0, [{}, {}, {}, {}]}]}}, @icmp=@mask_request}}}})
syz_emit_ethernet(0x4a, &(0x7f0000000100)={@local, @remote, [], {@ipv6={0x86dd, {0x5, 0x6, "c21280", 0x14, 0x67, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @tcp={{0x1, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}}})
kqueue()
r0 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r0, 0x0, r0)
r1 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r1, 0x0, r1)
r2 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r2, 0x0, r2)
r3 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r3, 0x0, r3)
syz_open_pts()
r4 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r4, 0x0, r4)
r5 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r5, 0x0, r5)
r6 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r6, 0x0, r6)
r7 = socket(0x18, 0x400000000002, 0x0)
fcntl$dupfd(r7, 0x0, r7)
pipe(&(0x7f0000000040)={<r8=>0xffffffffffffffff, <r9=>0xffffffffffffffff})
fcntl$setstatus(r8, 0x4, 0x46bfb)
r10 = getpid()
r11 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r11}, 0xfffffffffffffffa, 0x3}], 0x0, 0x0)
fcntl$setown(r8, 0x6, r10)
r12 = kqueue()
kevent(r12, &(0x7f0000000000), 0x66, 0x0, 0x81, 0x0)
close(r9)
kevent(r12, 0x0, 0x0, &(0x7f0000000140), 0x3, 0x0)
mlock(&(0x7f0000fef000/0x11000)=nil, 0x11000)
munlock(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
madvise(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x5}, {0x7}, {0x6, 0x0, 0x0, 0xfffffff7}]})
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x400002ff, 0x0, "003700200b0f000005000000ac39000500"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0x20000002a, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
writev(r0, &(0x7f0000001380)=[{0x0}], 0x1)
open(&(0x7f00000000c0)='./file0\x00', 0x70e, 0x40)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000040)="2321d74c3fd78d24c3f93020", 0xc}], 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="2020e66320650a", 0xa086)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000006c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000440)={0x0}, 0x10, 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = dup2(r1, r1)
r3 = fcntl$dupfd(r1, 0x0, r2)
setsockopt$sock_int(r3, 0xffff, 0x1, &(0x7f0000000040)=0x4, 0x4)
dup2(r1, r0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
shutdown(r1, 0x1)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000140)=ANY=[@ANYBLOB="fb182e32ae0903b540"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1000000, 0x0, "0000eeffffffff0300002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt(r0, 0x6, 0x4, 0x0, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040)="919b5c5e5c069d14288be166", 0xb08f, 0x1, 0x0, 0x0)
accept$unix(r1, 0x0, 0x0)
select(0x40, &(0x7f0000000100), &(0x7f0000000140), &(0x7f0000000180)={0x75}, 0x0)
r0 = socket(0x800000018, 0x3, 0x102)
openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000040)=[{0x14}, {0x80}, {0x6, 0x0, 0x0, 0x5b0d}]})
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x35, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
sendto$inet6(r0, &(0x7f0000000200)="dfbf20f2112be2913139b285e319ae87d306b4168847324bc0477e8379c47586a78b1aabac61d1bfc2ac4e64dbb3576e26cba8cacfe46bd4bbe4d823ea59007991188bf5ce40f4c685955d61598ea65023d637dac664b8596a8fdce2", 0x5c, 0x8, &(0x7f0000000280)={0x18, 0x2, 0x5, 0x1}, 0xc)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x801)
r1 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000140)=0x7)
r2 = syz_open_pts()
ioctl$TIOCSETAW(r2, 0x802c7415, &(0x7f0000000140)={0x0, 0x4b, 0x0, 0x0, "cf11050038050000000000000000008000000800"})
write(r2, &(0x7f0000001200)='\r', 0x1)
r3 = dup2(r1, r1)
ioctl$TIOCSPGRP(r3, 0x40047477, &(0x7f0000000100))
pwrite(r2, &(0x7f0000000180)="b678b3d96df7cf034a92eb053a2a8e5b7c3c24896e6b6a424ceac9870be44eb7191d47270e73fae05bec7de7b7948b4b94d636a94888c14f046f97e09693dde2ce6e833ecb89eccfb8588006bf8b5c600a6bf8a14081677137b2bf7b6341428dcabd3b24", 0x64, 0x80000000)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
unveil(&(0x7f00000000c0)='.\x00', &(0x7f0000000140)='c\x00')
mknod(&(0x7f0000000000)='./file0/file0\x00', 0x6000, 0x0)
execve(&(0x7f0000000180)='./file0/file0\x00', 0x0, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x2011, r1, 0x0)
madvise(&(0x7f0000000000/0x14000)=nil, 0x14000, 0x2)
pwritev(r0, &(0x7f00000013c0)=[{0x0}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x5}, {0x1d}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000500)=ANY=[])
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x800, &(0x7f0000000000)=0x8, 0x4)
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
setsockopt(r2, 0x29, 0x3e, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc0084427, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000280)=[{0x2}, {0x15}, {0x6, 0x0, 0x0, 0x20000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
write(r0, &(0x7f00000002c0)="331f3e528ed19e36d91517231d32", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284414, &(0x7f0000000240))
socket$inet6(0x18, 0x3, 0x6)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x1021, &(0x7f0000000040), 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x4d}, {0x60}, {0x6, 0x0, 0x0, 0x800}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f00000006c0)={'./file0\x00'})
sendto$inet6(0xffffffffffffff9c, &(0x7f0000001380)="0ed3c4f9801f417c55ed96730833f555570d21c75a4a66bef30297419eca856e5385a075c7c50c39b4bcf6f3aa4c57823c74b42c77dc3291fecfc6de235bf5030abee31f05b957e53408ae31b75e148dc44e031bfa145283f897a5fe5cf1f7ea44d0c145e103d2b98c95bf749c5c00b152603f36f2ef337c38c18293a94a1a75c15e613f0c0a9e33e650936ccf3edb8136572dcbc74ce923b94dc7fe9aabfbba798a7be322cffbee4b5ca6a240388865d7eb3125f8da2d725a9044417c14a9f8ac5caad0de5c1a58125678baee9953628009ddb830ff379e5e77cc34d488d8c572d2d788fc083f743080823da3be88bbe4dd683ceb025e041110566bd3a31abc81bd5e87cd3f5894fc8a85d34c21b233e9b753e5e5ad46670e68c65802d9f2398abc8f2e7e67ec5f75cdf851356574e24d967a6f0441f918a404a5d4e58778f4f881bcce841de55216cbba2f5e5540962395162399b92259af83be4ddb9c06c39837a9bef5ed1875ab2da1acbdf30eea31c1a8f345328cceb20176a20fc8e586855c3e8780229b1df46189082995dc711cf304f9cb1c632a8ee14c8df8c940c630c4a832e7b8cfd7eb299003ab6272109bb8aa0c0bb3016d2434bcce58f3c93bd9fd6f70af852872b66138595b349a2a1ac2b9d58b02d246fde59f26a3f666a361d7c57d0d42c6768056f64d765616134351af0c4c436ef3c9666c7b3bc36bd3748af1dca21eec5e3e592b38f4850a0fc1250827c565844c80ff693844a5a9ef6a17b343aca09c9ae8dc694d1a2f76e35a7041a5516c4abaa8a44928195594de14b79793a1e8894287713f869e5f85fed93ceac0a50f10f8168bb666aa0cdd4e69e88f9742da36774746f7c681d0f15ed1b9fe04095bab0b78426f3f764261616fd1366fb9526c418aa9f520ee674a22fb3989b664b2b39aecba3fad641f99e730001209b2230197333c1204e40c56041434dfa246393bc76f9027edf4a34cb7e15dfe8c0fdd6d8e02e4a9582b58a41b9b1934625fe302613339f5e1a375aa5c887c01c5e02a36b89790526f657f789dfb8ea38ccaf66680c44755b1f87210209dbe34d1db4731ba507008adb4c42943bfa6b0eab7cf9047ef829ba84d9d7db76b6cf6e784dfbd96a2f8b958d198082af948cfe342e43ffa12b4113dd5f2f4e9b531f93ead90e17b3a2ae2ede6a21178bc7cf3829500ec9a53dddd122ab81c07bc37c3b800fda30d829b5f02875d40c606bdce7545884245e00d9ff99f730a7c1b60e87d6bf6bb21d97093a4a5eb25aff8ea403ab1b36a1045307ddba8665c43a4b8080dd34905ca66a6ea9e02a5b5bd7c4723abc3d57cb01af18aeea0eacfee3cbef3d4905eafe7f5103de5e0038d7e78895043be9cac20907986b92010df2978daa0b484fb00f7c2bd411e7780e7c74f8c76cce14354c49ae3164a38a546a861a3c08a4599ce51a9554a00136a83abd948cb3f85ddb556ee0d3f60a07d0003412e166070df4147a827dcf6dde8c79879254181ea5626bb7b411e5d4e383b6901bec6dd26095bbf5009018793ec05383a643d46811c84aae1a6a51db152768603ad5996a1825e2af47f16d19835c8de2cc45f881d844a240decd3a3a8314cabdeb9007a16e1222a21495bb928165664ed5c063aeab6b063c62299adfa3aca39668f7de2223bf6f18285b61b3a2e65357f8e79ae1ce4421b551c0c95abe0f31fa50cde6586f94dc4f612b80bbe9249a0a83ac2fc303c6a282a644fb3284264fc4faff90a952bdd37c9859974216b33da62019105673896ecb2026aa63e106718c7629629f0e2db8701ade7fa568faa5add414607c480613de2d1423fa360216408d7d4da1df26bb83a98666ab944999b1c75917543724075809d097f6c874450147aea22ab200fbd4cceb083ddc7d536f8a44b90d38c891f06a6bafafaa40ca2f76f35198eb3d6d51bea5359e2da95352cb497106532c94dad9027636f15ec344a23ace367db1bec1cc644a43bb15b45f3ad9d435e55db34df328731ab1dff0c95e69ab4cacb32ea8da80f7f500cb386b65a1212e20fd649cf5f98e1d45735216d424679abc0f2c4b7b51f94f2eba6fadc738123edc83e2dd1960b577fc80e640190b82ef3eb5d4398be09f59f4df15de690eaabbd003c2367466142db7daed7936b0f644e6c02fc929ca06d4bf4cee45fea0e59fe6c93f6798b9b88e5267de618d015d6667468564e20b6224231de9aee591b9377ca12fc31c052867b996e1176bd2a1571446a0332c2bc80ff544f94dd8b47cc6600e4a9a06218e929236f36dab82f902aa2de327551a6cd657d50e5eac2aebbf64309c5e25a4bf4d6e2ded764431407f0a77971758987054b37ae65598ca496398e96962e3b2493dc771b5c03b98bc44cb9152c3de1ba1b1eb0563970d32d1cd04f76139132c5c0d1c692b63410f0b3944039cbc9e53130169d3562e6cccb64fef5e4ffb18b59c8c53e9684b3ddee970410f360762d4f346ed62dfcc107caa2e4a34e2366326b042f698758ed63720de6b4f2bea81e629b7040b32e660c192c49918858199f314c4d3e1373f03d5094534b308a083197591a2fc72621dbcd4512d19c92ba9dd3de64a72d2e4ae864b1be5501db82cb3473f2ae74d11307c2914d07301d059d68fb91666c16f674b437af4288e556885d6f78d47d4c7720c0f4108be0c104b7904a2d87e259bdbf9dccad62fc435d48c405386f7efa4cf5b9587a311cfa0f7e09f38c9087c3e044af50b7484be365debe015ab2ae13014e495f51d6d91bd2f7dbb5bf2a534da8a6ecbccfa62491fee58ca89b67fbbf6bcb2156e55d8ee65bf149a04603d1808592b0333f19e201c5f52f947920848a3251204dede045d293fd5ffa526de3c3e25cbb956d168edbcb75b1bb10e2ef3db2ff76551cbdfdc11d40ff37f3a5612170ad86c5ed3b553f0d38332337f438f0e771613d5d92780220148917222894c9b7da09c03070143550c393732c560414be359322b1fa786822e30d10f0a186073c236c11fe4f03c3b9a60bb4ef5f4acb0104ceb3a442380171f1104396f6ba961d8df7276879e12ee1bad9d37b0c22d0b2d8521596c05e2d04afcf409ea781f5b7770a2a9ce5354fbdccff0847db24654e271115509bf2ec467dc8e824c9e888cc31de73012f084e6d79c541320f103d105762e3ffd96b07eb49d9638b793e600280d311fd5fcf036fe3f694d7f4682b0d2a29eac1d6b0103cea880c0c947d8743bd156d06020c82b44e187daba8a7539610788ce5e42a200e91368382a399234ea3b30b955588c7a74f7116d4290748ee3c1184bad13a07ca6601f862ce79ecced509664d50f3ef307049f7ee4f2455d0827dee647f947b67bcc7c2dcb3aef17831437ad6a3b92086847f7d50a4cfec13554fb5a287a71c62ebf359f303d33b0c519921fed5cd4133094b2b725d1cdc72ead309f8050b9cccb5993e7aa93a6db91789116a979ce0f83923c2ef127895fd79085831cd023bed26c7916fb4f21bd9035fc057d8814875a3ed40bca21660a98c25efffe2f10d05fec3f3c81468cdfdae241045fc1cc94fb04f12d30a748b55bbd84e431a5adf8278d783245fb74970bc7be0330cba15eefbebe2bebaa77fd68c1c6643e01edf7a8f03d8a8fef650fa83cc48320a7528059b3ad2eb7e6304ef6ff99969243ce4b8f0efb719a6aa8ddb9e6f519e9dd300fcfafb7a19e658ab3eef22f746922386ed906a9b65d11207b28c18eb05f25b5ff11978d8a5ccf161f1ef875f37d465d66186bb7222b2c64099991a3cf1f8c6b555950f2a4d2cf4bbad6a8ef6b477d8ed56bc216d530626d3b77caa04790d3a5c5f6028c5947fbdf9a24c393b0ce200bec4054914637519b4b8698b07a83590bce2f8dab7f1ba7ff31fa7b6030290daf23a86e78337773934eea0b48e68e011d3c7dfd13fffeae51ba650c3bad0a42921791ff0edb954ff2c0260e67533796a18ac81811f5e49c4067081d2cc7c25f90093b7e310aca74274b0b4001e964c2f8c5840d135e7b83c06a85d67cc219e116172dffec54c2ade6585c281f7f83927abc7ae84c7ac82d2b7b8058930274ead0fea8407070f966e9c69545c29f95315d7d4060d15440c25a9182b18fdcf2f126af1279cfa7b3eef3f04a4fa1db5bf9b0c26d29c909c2afab47dab1ed50e2bff911b568e71f9c618c291b6d0dc47a767095d9fb40fbd785cf756346a0e830d88a118272205794371352be5c70fdfc503874343839ab833622d0a071f2d8e5052f2a7d552cdbac4f58844b9c9b5d9cfbb99fc85b159e05354ab1ec1a9ac93737cde89601e4ce839589e322f622d526d85225a9ce1e00031eafca60bb9184235b4573e7312f1d351649d09d0412aa5fd6e6ea480f7a0b2c308eb00d5a4ff018859358a1c33994a508639b4cc6dacc83e676111c1f1d1cfc0b534215d11c4115319a9249e31a92663cb427433e5bc3de1649c0ad667c6c1b715442986bb9f0a18329067d7deb5143a5d202de4734b07445818052cfa8778de43dfaffe3d6341f2a454fd158ab63646b8d653ecfcbc72e12bfb8c17d1a758527257b1b4ae166f6b2e5599c83a19e0d0800e846e1f6f01de6d79ba9100c8d16ba46224b245fee4449c640effb5a64c7e74fcc7254410255505a788b600a0f9ae7fa3f2d3b07d2db2bd27ca1df3056007270b27c9c523b9cdd0dc3eb126b3cba533faeb6c6a6e10532b4928a4b045551ae6a2653113614dd2f19650192a03ab52448445bfd444bb7ff0a8f48b032e18ebf7f78cb4640ce0125b20155c5b32842f6b2c8a78873d51f25fe08102afab26cfedb89508d3443e7c6bc52ba5bd90f2ea693cc446ff62e136e2f753ede6146e042bc6f929a551ec39a48bd0380dc66e5041057d94b195e919a6cbf703f246b15744c8a230f5369d699d6b0282dc363808fd70dbdc1d230b20f01680941ee03ec6eedbab0edda86a8543025ba6de7e26067a99399d6bee9f14485fa1578b7b07c1aba9071fcffbbe7f51c236aa8f52d04733f1a7e5fb523b25e721fd1728f96612711159c3b0f72595bb1bc77f2cb17ebcfe7d3f92d84931426fe80460b6a0f63cdb5ee0b27c493413da6f8cab3e6979b847f1aa855ea1b1a0c5996ca2e6e5c8a1da893d0e75ad148d8ded2ea5e4e4826c9716b83561f7805cf0e48133a8436cdcce9e5beca5daabd614ba7c62e3afdd10cb93c44e20157af2d3aa383e9790e15688720c0671a4604fa586774a8e99d776f02c9c9d29a6d9456e2ea804d25716f48af6d2ea4179d3507bb78ce9d4c207678b6626b3f50789b612c20a342b0399034b71a4c1985a056fbcfaece75ae097540b176266ade8c09d16abda3a5cf2b98426a87eade87333f82c4d8c9bdf045a4ba05aa7448310ed62d44ed8c060c698891d86dea3a26e3367217338cebf804cd96922c508d9206353ea54e401980d81fecb82eb5e02da279fbc2a790429870b9b9f001026b1608ed177e86f42518be7eeb40f17ee9584f6b7474e506c43e0934844cd5ded8ca25fd2b5580a21aa45ebaa78bfe07e591bc550392f96a7e674e99fd7ba0ac65ab52dd71f3c5e2da839cac4b231c5d7b06ee86c8924c3634143e326dace32e04a2c2451f899117b79a685cf6e7ce100aa09b93566848a033657878220146e5928926c3331fcba3c95fa91f420206ffaf73383f57b3e68c7c51eee03400fb8a4237a91e7670fe8d3fcc8e5c31a044107b89a216428e7e4e700ca43fa89a33c2f42d18cd9c324517ee2915b9f14426ad4faf7029b86da26f3e54921ab40862342c2679071224495a92e4f1a7eb00fc4048a931086d05c13ab9a824fb67776416d508d3ed47c10079d1e0e7930e5db231ad27e94f368d576fbbfff106bb2f0a4abcea597c1a0ba6b95b2c86afe4ff173f6a7923edcfe0f540f906a7540d7c8859b2b42f961e9dcd16953b12c8916f75b50604eb489a5432b68cc41b965e4f0d7df0b86dba56930873f29148e2c7599e357d458362971c904a081ea93757e2d3593107229a2f38d04751abf5e6d4286d577ff6331e0da97f65d41aed54647dc9a9c8ba7ed7c", 0x10d6, 0x7, &(0x7f00000000c0)={0x18, 0x2, 0x1f, 0xbdb0}, 0xc)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{}, {0x3, 0x0, 0x0, 0xffffb950}, {0x3}]})
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x3}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = semget$private(0x0, 0x7, 0x3e0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000340)={0x4, &(0x7f0000000300)=[{0x3, 0x3f, 0x7f, 0xfffff000}, {0x200, 0x8, 0x3f, 0x6}, {0x8, 0x3, 0x9, 0x81}, {0x4, 0xa2, 0x4, 0x5}]})
semop(r0, &(0x7f0000000080)=[{0x3, 0x0, 0x1000}, {0x1, 0xff, 0xe17b725e3710487a}, {0x2, 0x8, 0x1800}, {0x3, 0xffff, 0x1000}, {0x1, 0x8, 0x1800}, {0x3, 0x1, 0x800}], 0x6)
semctl$GETZCNT(0x0, 0x4, 0x7, &(0x7f00000004c0)=""/166)
semget(0x0, 0x2, 0x43)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x191}, 0x20, 0x9, 0x7fff})
semctl$GETNCNT(r0, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETVAL(0x0, 0x0, 0x8, &(0x7f0000001340)=0x5)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f00000002c0)=[0x3, 0xfff])
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
sysctl$kern(&(0x7f0000000000)={0x1, 0x51}, 0x2, &(0x7f00000000c0)="6af55741ac591bd0d091ca493f43edde748a033095a4ed4396950da1e9f18faefb065ee5984813d55606d7eb95badd8b37031f32906d378b92f78d1a73457c9705ae47c36b6fff6446d15f0206e898a65fb24c", &(0x7f0000000180)=0x53, &(0x7f00000001c0)="7930faea8d4ee3c42066b44a5e9af788c9e975fd8fe6e458c84e90a5ad3de5f43b81247ace1d1c66b4b516f9e28e0295d911632381de43179cc0916265d06144bf571f5a566b7054334fd9d89a8d7a29a30ba81d47ead18afe66df08f0f8a539b9b4436f6c6faf4437314e3b2a9e4ffb04800f4c35869096b2a3191ab353ede88b933cd749e83c729ac80f4be67870492af3a6e81f004a309ec94cada9de4f7bd9accd0744f240257f841d870b1a441c78b17a50e8f072838d63663d0dcef24c24712ddfd118baf1d6dcbbe38bff3845c6821f907842da3c8af35e010458aea179c8295f8a7163fc088a6bc86a3810e88b42228bfd1a936b", 0xf8)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000380)=[0x95a, 0x0, 0x9ff, 0x96, 0x8009, 0x8, 0x402, 0x8101])
sysctl$kern(&(0x7f0000000480)={0x1, 0x2}, 0x3ffffffffffffccc, &(0x7f0000000080)="055969e5ed090000005068d0", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
r1 = syz_open_pts()
close(r1)
writev(r1, &(0x7f00000000c0)=[{&(0x7f0000000140)="4491125e055171c507d4e19557a30004856f276a74bb36778525298af8f7e8245df1113208474bb2185340f8a3116927e7776de444c14b4233243a47aa8c7779222189299257a1a295db6954f12079becbf00ec4ec9a0f2212ff07aa4bceae07b4e185e783a97e180dc9e7d921b6722788dca9", 0x73}], 0x1)
select(0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)={0x20000000003})
minherit(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000002000/0x2000)=nil, 0x2000)
mmap(&(0x7f0000003000/0x2000)=nil, 0x2000, 0x0, 0x1010, 0xffffffffffffffff, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x19}, 0x4, 0x0, 0x0, &(0x7f00000010c0), 0x4)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b10005", 0x3, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x200, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x0, 0x2011, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
readv(r0, &(0x7f0000002540)=[{&(0x7f0000001400)=""/3, 0x3}], 0x1)
munmap(&(0x7f0000001000/0x3000)=nil, 0x3000)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000002000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
close(r1)
r2 = getpid()
openat$zero(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r2)
pwrite(r1, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000001440), 0x200, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0x29, 0x23, &(0x7f0000000000), 0x10)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
ioctl$TIOCCBRK(r0, 0x2000747a)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x35}, {0x3d}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x66, &(0x7f0000000240)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "979fd7", 0x30, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[], @icmpv6=@pkt_toobig={0x2, 0x0, 0x0, 0x0, {0x0, 0x6, "f14797", 0x0, 0x0, 0x0, @empty, @local={0xfe, 0x80, '\x00', 0x0}}}}}}}})
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
close(r0)
getsockname$inet(r0, 0x0, 0x0)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000002680)=""/4101, 0x1005}], 0x1, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x2, 0x2011, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000001700)=[{0x0}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x61}, {0x2}, {0x6, 0x0, 0x0, 0x80000000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x5}, {0x44}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@random="1a37d5e5e253", @remote})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000040)={&(0x7f00000000c0)=[{0x8}, {}], 0x2})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {0x0, 0x0, 0x0, 0x100000000}]}})
ioctl$FIOASYNC(r0, 0xc444443a, &(0x7f0000000240))
r0 = openat(0xffffffffffffff9c, &(0x7f00000002c0)='.\x00', 0x0, 0x0)
poll(&(0x7f0000000380)=[{r0, 0x4}], 0x1, 0x0)
symlinkat(&(0x7f0000000000)='./file0\x00', r0, &(0x7f0000000040)='./file0\x00')
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
r2 = dup2(r1, r1)
ioctl$TIOCFLUSH(r2, 0x80047470, &(0x7f0000000000)=0x3)
syz_emit_ethernet(0xe, &(0x7f0000000000)={@local, @broadcast, [], {@generic={0x8035}}})
r0 = openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
pwritev(r0, &(0x7f0000002440)=[{&(0x7f00000001c0)='V', 0x1}, {&(0x7f00000011c0)="c0", 0x1}], 0x2, 0x0)
r0 = socket(0x2, 0x3, 0x102)
sendmsg$unix(r0, &(0x7f00000015c0)={&(0x7f00000000c0)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000001500)=[@cred={0x20, 0xffff, 0x0, 0x0, 0x0, 0xffffffffffffffff}], 0x20}, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0x0, 0xffdffff6, 0xffffff5b, "0855c40125000008000100008d1b38b85200"})
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22", 0x78}], 0x1)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000940)={0x0, 0x0, 0x89, 0x0, "727189ec9c66552b95995a9397b1c27fd1306da4"})
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f00000000c0))
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000001540)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r0, &(0x7f0000000000)="a72f6bc13615ed878731e1b9f154f17595cd90775b5c4eebce8c49c35def3059791eaf92c88f22f02de07e824d84dcc8302b45235ea8fe617871de17d0b3f94ff0a121cc5f8a3dd0323b54353cc55216861526d11345d42e969e103ca5e9cd6d9dc7b03e5efa5e6d1fc23b03aec34e51bce0e38fbcbdb7d3baa44b2b53de61f9cb3089e672ee90f4e78b86a146af416704c6", 0x92, 0x0, 0x0, 0x0)
setsockopt$sock_cred(r1, 0xffff, 0x1022, 0x0, 0x0)
shutdown(r1, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x61}, {0x6c}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket(0x1, 0x2, 0x0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
r3 = fcntl$dupfd(r0, 0x0, r1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r3, 0x80606948, &(0x7f0000000300))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000140)=ANY=[@ANYBLOB="fb182e0b3d9a0900da"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000008e44807019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x1003, &(0x7f0000000240), 0x8)
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
mprotect(&(0x7f00009fe000/0x4000)=nil, 0x4000, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="d5ff9668", 0x4)
getsockopt(r0, 0x29, 0xa, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
mkdirat(r0, &(0x7f00000001c0)='./file0\x00', 0x0)
renameat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', r0, &(0x7f0000000040)='./file0/file0\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {0x0, 0x0, 0x0, 0x100000000}]}})
ioctl$FIOASYNC(r0, 0xc0104419, &(0x7f0000000240))
r0 = open$dir(&(0x7f00000006c0)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f00000001c0)='./file1\x00', 0x18278, 0x0)
select(0x40, &(0x7f00000000c0), &(0x7f0000000100)={0x3ff}, 0x0, 0x0)
link(&(0x7f0000000000)='./file1\x00', &(0x7f0000000080)='./file0\x00')
unlink(&(0x7f0000000200)='./file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{}, {0x3}, {0xffe}]})
syz_emit_ethernet(0x36, &(0x7f00000002c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "08ec28", 0x0, 0x0, 0x0, @loopback, @empty}}}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x3}, 0x8)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000140)="9513ff", 0x3)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x32, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
shutdown(r0, 0x1)
mknod(&(0x7f0000000080)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f00000002c0)='./file0\x00', 0x0, 0x0)
faccessat(0xffffffffffffffff, &(0x7f0000000140)='./file0\x00', 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r2=>0x0}, &(0x7f0000000100)=0x1)
setuid(r2)
mmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
pipe2(&(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x0)
fcntl$lock(r0, 0xb, 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x3a}, 0x2, 0x0, 0x0, &(0x7f00000001c0), 0x0)
r0 = kqueue()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001440)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001340)=[@rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0xffffffffffffffff}], 0x38}, 0x0)
kevent(r0, &(0x7f00000000c0), 0x401, 0x0, 0xb200000, 0x0)
nanosleep(0xffffffffffffffff, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240)=0x1)
r0 = syz_open_pts()
fcntl$lock(r0, 0x8, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x100000004, 0xffffffffffffffff})
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x1, 0x0, 0x0, 0x2000300010000})
flock(r0, 0x2)
flock(r0, 0x3)
flock(r0, 0x2)
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x100000000})
mknod(&(0x7f0000000100)='./bus\x00', 0x80002009, 0x3200)
r0 = open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000380)=[{&(0x7f00000004c0)=""/230, 0xfffffe9f}], 0x1)
r1 = open(&(0x7f0000000480)='./file0\x00', 0x80400000000206, 0x0)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x0, 0x10, r1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0044456, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x2, &(0x7f0000000080)=[{0xb1, 0x0, 0x0, 0xcd}, {0x206}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x4, &(0x7f0000000040)=[{0x5b21}, {0x6b0}, {}, {0x6}]})
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0xc0c, r2)
ktrace(&(0x7f0000000100)='./file0\x00', 0x7, 0x1510, r2)
open$dir(&(0x7f0000000080)='./file0\x00', 0x310, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1112, 0xffffffffffffffff)
clock_getres(0x2, &(0x7f0000000180))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_linger(r0, 0xffff, 0x1008, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc110445d, &(0x7f0000000240))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
getgroups(0x7, &(0x7f0000000080)=[0x0, 0xffffffffffffffff, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff])
setgroups(0x1, &(0x7f0000000100)=[0x0])
chown(&(0x7f00000001c0)='./file0\x00', 0x0, r0)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
chmod(&(0x7f0000000040)='./file0/file0\x00', 0x400)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x45}, {0x64}, {0x6, 0x0, 0x0, 0xffffff00}]})
write(r0, &(0x7f00000000c0)="2cd1ecf725b86a7c0b4fc7aeb545", 0xe)
setitimer(0x0, &(0x7f0000000000)={{}, {0x9}}, 0x0)
getitimer(0x0, &(0x7f0000000040))
syz_emit_ethernet(0x2e, &(0x7f0000000280)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @local={0xac, 0x14, 0x0}, {[@noop, @rr={0x7, 0x2}]}}, @udp={{0x3, 0x0, 0x8}}}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x3}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, &(0x7f00000000c0)="7343c9f13a1a65a5dc41e7000055b9e7c7e61e80a00111703c8ad427b38b2f8aa720381c97827991a34f07000000aa8428b796be7c3b0dec5cf124fe01853cbb22f62d6fc07aeaae1028cd4c8391f916efee16d809a6a0b0b5f077d55f10bf208af9f63d4fed291214c315c5459032ba78cf06e3e6dff86da9ac815ad539c221d0d8894615ff18fc678944d0f65ab486924ff505c7690ded14f9ca833b67bfd24a41b9c62e63182ec537052a6472066ce21532deaac00c6ee5dd8cca0bd9c132e66331f794", 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x44}, {0x61}, {0x6, 0x0, 0x0, 0xdfffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="6d9ef757a944e00af2e26510358b", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800016000000", 0xc)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000100)=ANY=[@ANYBLOB="fb182e0b3d9a090000000000000043"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000008e44807019dc000ffffffff00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r2 = socket(0x18, 0x2, 0x0)
r3 = dup2(r1, r2)
sendmsg$unix(r3, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x0, 0x5)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f00000002c0)={0x0, 0x2, 0x7fffffffffffffff, 0x2000000000000})
ioctl$TIOCSETA(0xffffffffffffffff, 0xc2d0422a, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "0400", 0x4})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=""/48, 0x30}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x28}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x1ffffc57, &(0x7f0000000040)})
clock_gettime(0x4, 0xffffffffffffffff)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
setsockopt$sock_int(r1, 0xffff, 0x4, &(0x7f0000000080)=0x7, 0x4)
listen(r1, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f00000000c0)='./bus\x00', 0x8, 0x0})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x3c, &(0x7f00000000c0), 0x4)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
syz_open_pts()
ioctl$TIOCSPGRP(r1, 0x40047477, &(0x7f0000000080))
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_int(r0, 0x3a, 0x0, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f00000001c0)='./file1\x00', 0x0)
unlinkat(0xffffffffffffff9c, &(0x7f0000000000)='./file1\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x14}, {}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000001700)='./file0\x00', 0x400000002, 0x0)
writev(r0, &(0x7f00000015c0)=[{&(0x7f00000001c0)="77c5a2ce555066c7d677575478bb106b8701c881f9e633a7d23282ae5a95b485611929a6fe0d2320a3de337f0153534b01b2adbdc3846332e388a3581f155e430409d8aa3e3333ca2aa909a42ba080e7756d5d8a03be4a1b95b57e2937f362c91e9c6d450f5d5e39f4b5e3d9ccbd2d0f5599d7c8d2bfea9fc93e78557a0e2e5ef8e039197962f7dcb1b76a983cfc2cbc9a1e23523ccd85e37570e6bdf4c2fa5b5e48ee942d801584f7ec78", 0xab}, {&(0x7f00000000c0)="a1f76c9697e14a46743beea2c90f2273abac1c7fa8efee90a168871d290f75b7d08edbb6553b66b21fdc42c310fef8959fcb01b5484dba760230f1b630a8fb0b1f6f1bde99232cdc6d89b8a0bfab84720b1dca5bde27c3b522e5e010b52dca051aeb7e60ad0077b057805da616db367d1856d2d1040d66fa", 0x78}, {&(0x7f0000000280)="e1e4d885bc", 0x5}, {&(0x7f00000002c0)="6086ef80ec304f634cd74420477b7420110d0c13285a8bd619b213b13ad622c6ccf0da7124d264eb1cf44d47af117f2b4f38a03c87d65d27e8a93d535c84497f1727b97193caf4abf17aecef579cd2f8df55228b0d55f005d69bbd4b240f119085d962c11b5658e4be3582c2d42421cd47eaabea5575f64cc8d3c8fed9bb2fd97b3a53578f5da56d277abe2823aaa8796b04b04c5f43ff6450e91acac5e137d430ad", 0xa2}, {&(0x7f0000000380)="775db9ba8bee3c4217b31bbb379fe23f343ce58fa70e58295e6d572013dc5adc8f25726e2f959093c547ee9102123bc8e7682dd7fc5b1fe8f43ed3eb68072e102f2c85dad17ccdf32186668d19ccc54c15493fbc43685a47bf38eb0ed8dc45dee289c552cc345bc0e71ddd984a7932ee7ec76d28a2b412e9c91d5f471c9a07e3b695da5def509e64e64ee8699b452bd7eca9dc43716d08581b9980f3d13286702ea890aa9704d208e1e563a8bd25c794b36ed3df717b4043a39e85a69d18a6ef403879a92ebbd2aa68131a682f1896f5e68001ee62612343bb998f2a", 0xdc}, {&(0x7f0000000480)="34700babcaf10abb60a2dacb47c149b1b6c8515b5100477b5632ff425034d67037ff0c3ddda66428f923da180e492760c0b828142352c7ea68ffb2d23f0e736d6823a9accc4d9bd05385bcb645b5415d70618712eb48f80d318a31dd86734d59b003d60af6e61fcfca", 0x69}, {&(0x7f0000000500)="7bda5798d078b749afe947349d81fc3cbcc87f7fe528580d50bb3cfdfa12c1ee345c9252480ed340f6fbc73f7a3002fc76bc77559f7736672dd427271d0693783d6edf4f2ac896a68f8bba56b95064af956a13f68358494ff21fa7ea21aeed6a8401372c295a348974821cc0fd158446e9973e11f7e416baaa2492c3bcb7c3b093b2b041007a16522bbce0f6a9cc2ee00a43a03c6859d29df63baa1035c2e7e9c7ca0e75409297f35508456740aa22f1f30133dc7baa7443d63ef09e1728", 0xbe}, {&(0x7f00000005c0)="72c0e4c62805e2d99af189370ab35ff1710b42b6199de64aa5159c0e8accfabdeaf8c8e35f707180954c758c825e38f0990cd0bb16ceae8ca9701cb7d9039de136c0ad9caf6827f411b1e5b00269426b78a333102d3171d59d26894c519db8735babbeb3210ee8b45e13665c6769e20110b66fe26d80426f8b8b5b05c11f93fe89f83f3d07a50d6488f3101c5cd97fe1459df07f564d71780c59ad773fb7f2c8612f5f0bcc9806935a7aeea95a99fb436147150fb31f8656a8dedda81af21d9162ca2c8fbf523f5d5f367b8dd9160acc2eb993f7965474417c5a45dcf8b4987b506e78281236150f6344022bce43dabaf9517fa3e822428b59da4e49260dcaaf19c8c790e97d53b5517f3341b6aed17cb019af24fea1ae11946d9bf14b241c8f09070029371a8b49abc95857a819f6ccab532c39ee0b3b638b50ad18664ea6fa394ac3980d7120afd324c64eb71b54d1b8a7f7aca8ed45cd593aaa088a1a778c92d964ec384254c49c878f88ff23dcc6e7c914e25cceda35502f67eb181dae97426e26d069806c8b6898bce6369ea5064ce2a84314129cf48457bea52d1a555938de6d68369ffd5bbf40f72f737b73bb47e0148763dae8c1d05874006a48cd57d5b38f23e3f315811ee8590eaa1669da08b86aa4c6267f138587538221525478ac7de45b8e3de400fbd2149541a74a028e46f1d072622935b64d65b01839c8dbf2267bd9d77297c4a81ad763ce6cf762ed2ac0aca6a4287847153bea046642842cf11a2a3b51cc6af28a1668202b2f5f2f466f5afbd511c7f3bbfe1a3f91e0c1db2093bfd1e26043a9f291573a5742a72d15c66d0c50485e9f723cef7a96b3c7919c6d20b18bc7dfbfe272b4bcea0be897d58f7d891f8cfad938961955f9e0c7beb0ac7452ce4f6f82c5bbea4a510d48a413293d9f180d15fc845096ead380dcc7f97e58ac3b07aea56908c00e6f86cefd6cee9a9aa8749a81488d48cc2befce67069336964b8e47d536b4dc6a4c4ade0e4596fda42562a10c79ed1a2d3e15f2d248f57249d38b070795a3b05ca0349a159cfd3aa900b5f059a94a534c336fab31ed52be6879f583b0a5f7bf4a137c292207352ff95ec840a385c2f256ebfd592d1aba7ffee60288aec3a215010e33703ec0e9b07fb09349c537670e1cb8a77fcda86f819111e4740cf5e6cd5d194cda135ac37ce2136dba1df73cb86505075b70f5a5b0eb600599ddd2b3f39e4c1263e141c16cc1609d5b4c5ad4bdd8d87915ef98ffc798146937c432903e324cc68b1bebe99b7f8d31dac02fdd2138f14b479267ab37afbfc7e13ac2d207b23fa53883bea52646f10961e84f7293da85fb19a7625827ddef6d112211d18c6ba9f2d6977be8f0057181df763d22cdf919e48d4d3515007d11b1ca3ac96024b9d24562d57b8c0a85e6c80f5fc63bd1cab1f96d79820434daa65c743150d8becfbfefb78c4675b598513ac5dc25a66208186b52ddb12b19220ee0e729fc946d09c0c5061197be80a67dfa880f0245c3685687dfee221b78ee63b08e1cdacda8b666bb35c6bf79e1e4ba469bc271a6ff0625f43e008f349bee79d816d5f55b0a245c67f58531a24c5514d84daf19bdb859e0d03f55fb6bfcbd8eca8fe663c2f4e90837a95b5ff3fa9ca9dc5df3c34ccfd0130ea1abaeaaba7ddfd0dbcd528617797b98795d59d5b787f71e665377b24d9d660637d4f1de57b20dd1c8445a032e7ee66e93ef36e0e14769e9474ab82a27854b70b08644f03abe54e104a432276d1e076189a07772b5188b26ad4e8a604af83aaf0c5b53d338458c946d7868864940517ff7d9671d9fdc4e41debdbe99a5eabe8e56bd207d0c01279810fc900110af52f7799773aed03f85b0ed601717c2ab1fb3984f070a08543185011eb2b2a1639df19c7beb5cc578a3530a38662d19dc2612ed23187de25ad93ef741b931f113834e174636be2df9b7ba7b9c98e8b1f622b6950f40b1a033579c8610512ebfc78d510eff3eabebac50d790c74e72dfac90870551cae1840b811ee853d990aae91ded21fc6f2b8cceeb99eed2090aec1d80d6096e0309c0178d90fb39076a5199b0c9e778955ea0b74f691e221818dc07f7fb5d2beefe79f6b1a1ae79daf75d734bd78b9b70df6fc7f02fa1338254605e6a4ff4f04bae2175e06c6170219212899d7232cb017a1a2a4eed7cbdeafc61cf3c6a265a318ffa8e4a6063ef8f4a044edc9526d2a510837d83060f4d1e5dfb2a93df44aead732ce1f050eab6c03bd194f1818813dc118fee1d40e5c759837d535d0a6a1f801f366bd417845b1024fdd9ccdd9f3399ce1633959f1a1ce71c39097ed52bc9724fe13cd99ee3773005f172bc29c72dcec1571e2de0ab88561597154e6c3f301bb7994e5ddff36a94f6294668c64ec4caacc53e521ce059f9d897d0de5cedb1300b1a06a7016fe32d142f4885755c3a104f5ed4f0da4014f7c0c43269ef1fe2e4921b2e82de5082e50c01e43fa057bd1ee99a23d20e9471c6aac87082ed833a452b5d4e98cbfcf4b99acedbf6e4ac4dc50336189347bfd9226e30788fd44724a9582c27fc1631d7c1197a473dd05a5d3a13d7647891aa1c19b6d050bf8c1b6bc2e0cfd75e87ab41758b7c028b1c80bc20628cb7089d9e74160b340b44fddcdbe714571c828bb94e4ea3c4ced254e4d23d1ffc85adb3d543f4b50ebc68229abfb1a5cf3dcca59cab1df8cb8c01f86c9f78438e66ecc6fffb8ff2b111255479cf37d71689a42ffe5e3bf7783406ebe82b8ce0303da267175a98aed34f5955ea6641ac069f7c495d9a8bebec1a1ef5a323705d05d2c0229e07cbdf0a52fe1a859cebb183b67176eb9143b687518377ef5e8ecf3f6c0612a69ef4c5b9926c45f1219ea3f41c41ba616e027193e65da3e9f366d2f949e8869e05ffa6b74eb1ff53af57861bc3a98e81c7437408e042774ddd824a0f7b901a394aef9d2171439bfc485bb3098806260b8513772a1c274a278b4a31a7bf0600bb2d96d5e9e7517f0dcc6a42d86c23690dc0062994d27e76c8de55013fa609ff48a34aaf720139f9af166138ad46e510a2648df803ea518ff540d034225344dbf3bb0918235f6c46a61a12a4247eb6d67dc0376087a292ae6755e0c7c533ca8af657e2b84bd63f710b684ac34c045527c2287e95889f2cf989833458e09c3e966645b7c5f56af04abf15735199df71a49ace0cbd51c67cc647535e42d0b0961cff41a6dacbd6bec3a80a15281d9c4cf97d3a941e0c2159ba09bccd63e1473822145b479a4171424b5519fe80d8a04b5769cb1a2eee3cc40e3a64ffb55b0b9ee9a3788708a671d0625a31709e789159e09d1f56f18260129a581a31f6d6dc969b98d230dce7ac09aa856db6a880408b03f474c764b1e0ccfdcdd3cfa05a6bb4e343302f36a224a7c69a723681283cbd3bd609bdc5f7b0d19356e929389c25d8902cbc1b5d48c3776dfab8209f0c81dbef1d6846851bae7cec02c77d9e5c2c1c554a94cb6b6bcba06f8a4f6a38e348be83d845faa6861d2b234348884748f9b72a6ab6dd8d51f0c2c7b83a290495ac42db42833e8252f6625dea375802b885a6719f29c1bece771bf5957f2a74cc75df947a3a391f0fd2c2a6ee35ea4565c003645415e9686d667643214b69d4025e9196bda04633b46067e66fc576035299c3e95e35ab2481ed574aab2515826ccc828a7be62e0e5a29e9c0c0e800af0a7ea4ff7e92e41084eea604bbf6bd26131896f70a9a43c3c24450f39deb3fb0f168c62909f31b7fb363e01fad245aea6404fa35ab87a43717e59cf4a31dbc45fdb956f685c6a7de373a4ccddbb29d32c1a34ce4164033457a700e9ef845eac85f1381961db33e306d6c2e8ab86c0a550b7f96291651bd603de942694de050a9deed47072722f46da8bf17da39a581358e2e095a8ff8afe178a280b708896e4c58da8735964b9c61e2be3c0977fcdb7d816f5b2331d68a48616125824a7646f0a896ffd6ec5cf384f54e43cd0e39f8364abae7c68cd2ab72079a5fffc756a3497041f4f7dc74663b9ea5ab26fbe21710e2c43e5eb76bf2fdebfdb39e9e0968e63202e981cb21c1fae518ec91cad5111e4b5678213a13b0f131dda9afc0895a9f6e4b3bab5351348435048fa50c9c6c18513dc02ff5232209bb77711a83addab5f602ed9dad328f4aa9e847f62513bb35d7569af416dcb8293f49d5a4e80da9740e573ee4b6461c1af2831b930cafed6a08ea16760be517ef11396afb5d880d65c0c063da5946886084e4afdddc846279e59b291e10e4e8ec1e7a526ec362de1e5b67bf3616aa56f8a4ef0f7a7029287acd34c9a49eddd0b7105eab3ed1f49b37aa9dc4a6b7fd41f51301d1b0d1b4ae2a39671ad71be70379a1217187169680413002e5527d6a60fc62c962173af15a39f5b21c377b1bf7b8310102955d54e5cf958835dd877829c07406f2491ec87e345b129763bc0441d0cca7bedbc6b51a77de4fbbcc36247b1d57a820cbfb295d5cfdffe2f94b9985e9e49ceafe6b839fbfb214261e64a048a4f712c21c40992f4682788d6983dc41e97493a807df26c3c5b47b561986fbec3c18e54d919ba634bc8da54f487efa0ade5f4277b87f4c60dfd3cf141f6a2664ca52f2581820b7727169baf50e61fd7d9f4520d853fa99ca5f333c92aee5b8c21f266a482e2161959fc1a74bd57649f0d068b28d08bf5af2888d4fcae757dc0e3e243fe863abdb49a7b741c6dbf7755d7fdf566b1e90225e8d17806f41fa1055ac39b9fbaa2c97cc0e6f3a5c744a2c4bd89fe3816aa1a83db3137ce8b282177dd9766e0a7a9139d2663ef1ab66b412c18a229a893cbf64b927cb5be883c0b088371e1075cd14bfec6532c615479fe29683b8b6a8e8219f72ce9ef2f7bf6eb1d4248e90c3e6e182948183f072de89579d34a1aab16025a87f62496bfa588a44d62602612472dcf1440f1c2d9d912d36ec1e19c2950d1af645a208c850a29ab37f3fe3efe88454eb64b11c93fe09438c302042c30ddcc5fa6d69745758a341f04827878c17fe67b5cfa1b6cd30a98b9de10268fc0f303ccad5a4f7008c021b9364e254834699b8acc5fb73a3c5d6cc6fcc1f788f3b46ecb8400a671b1b1f2180d6e2638f3348a3f29c4329409f8026e2fc73268c27bf0d0bb527752e70721ea024f0c7af5df293ca230be7dd21869053ce8e3822addc6e2d717c0303976364f607d58cf34a5bf16e8ef28ee532036bc4292a588f988b6395001c67171892b1ffdef94d1acee2d97cd96780bb2972db656906259125203bc6512a0ab8cefa57b317123b94f4cf29a4dddb74e0ceb75dcc14a3cec03ceb27f768235ed7f9d4b6a85444c63a8d9037785243a1ebc29c3b209d64dcceee5308f4ab17c543b163aa8b30273304e04d75f441efe101c0dc7f82dabcba2f94f6242a7b831d0de7b7a477d9fae7b58dabd77087624a46480bfc2571c324cb3cc6e5188c98ca87c32ffd30391e12d1a75bfe5f29a9c55d297c1d9f1f1b033ce22ee16d51cf80bcef8438e80123de6eba4820a3aa77cfc103e7bc0e708da355b81d4c8652b05ab2c479639e91f2cd218658b7b8d428bec9de8fd9c175e72bf812da7e4ffba673fd272d13c07063e4533ac42330a37056c38c63bffdd87edccf846f8b172fece4696eab89583fe074300bb65a0e2c8f1bb51f820c193e39adc4d0b334e2cba9365c648f67e60333b4438d51aa135c9aea741f99b64c2c0cfb6f23ae30a946f13598ab304d10442bf49fb4cfd6", 0x1000}, {&(0x7f0000001740)="0738213bba47bfd2d2e46406f5f5578a5adc594b2156a9dc1ddfa271e7693a98b0ba9e14ed9195f3bc7cc3a7a11d12902b9ae9e3be7b6554b20d4f91b14acaa82bc99006bc2d517be21fad6c566bc89d011d016b6d65c6a7a81a3b4ae3b00f079db1657a211aea6a1fed9ff613f647a1775bc41f7a21f1c411eaef70ca1281c5541f8890e82fbe0a8108677211f8c23c43d9e90010afa4d9765f40b5cc1a9324e6137d4d0f103396ef0b1248cfd958199ec636aa03e3ee9f2d3ac11839ae2216ca06effefe57e67803220c7ae4cb8b2f93b319837814eecb2df71fc222c3d55a8e6960cf5391115cc24ccff1e6ab30c4b20307d19994457a27ca51fc2a0911c7ac5f0b8695b71669bd19c0f09627c680e7d53279eed25afa66c6b48daad3d1cb6a2994b71736fa789d5acaca69508d9870aa8e54bd42d55c2e490dfa20f460b47ced3724035be04122a16157865c618febdd2d3b92ed548480c26c2261975aaa9fc1f981ee472181d73c1160755f342c14846809f2fb14d220ac9976679b5891e8c067c044ebe415904eb0d58956aa4c4d184e85c1214ed0db296466d13ba1889a6d56069f2fcba716598d22221eea14e1d697b63611331105aa5333eb7a5027dba2aef3b9747191f4e5570e9f8c5f7cc98e7f539c904ab499897bc332b9f349f97143f345eefb1ac33fd29e111e694858b6967490c3f4f5affd11f221891b86883e66c7ce71122249ac39dc1459f6f6301a0a94dfd5988b99c9d01afec3b7e13607583f922d0c07bf377ae970767c0728cfb8fb9d1b69aebb2bab75dec6eb780d8f4445a1e94fd1cc51f83da466e5964d271e85354787d4e1d77037f4fc8b69c70d19e88351bf2669e4c86218b921b486f80d1bd797243f6079aef9c9aff8e5e7b398569b88efd14ee4543a94792b842cd1a3253ca9409723dfdf621f53fed615de1e0ab3129d31ef8895bad199d9a3a69bd9c36c66d773c251a371d3312587c54651b9d0217c52ffa4a16629321cc7839fc22814aa39827be35621212052b39b511f67835b9accbc01fb8cbbf5f53723e27153a2e9c8727904f49fb8a443505cd603a684c359e400c245e1cb79a3ba4dbfe969abbc11405c0bc5738b3670c0fc201809ecc54d9bada643b4e589f34a954f8bd15380f839ffdff99cac45d00b71899f0b8dae772cfb8b1d91200e8a9d7b1560ddd9e763769b1e4e2246b52d92f558aeb43d4cb145eda2c8075fba7c733ffa8607badd4c26ed1e727335e9858cdd1aaf2451d210c2992b4417495748688f7608559970e51d1b5cde742ff52da87263f6a6811ceb6338843765bf81e3e9b34219b0f707961ef35cabf7512b4478649f1cc70b8ee7aeb0d3b119d0075e3a0c2e5043b5e5f39cff981b080182c40487ea600714f9c0c9d02cb9e077cb6c9fccead7e089d22b75f52067cdc75d7e90a579e911836e91decb072616bda2b82426f38c3ab7b7d55f8e53f6315b1ca17f5cfbb2a3de1c4e0625b31a64ffeab23a4f7c73b5993779e725286a766dd9d3ecbcd843d1c04f9bbc79d27d0aba158e28c107fd2fcf81a327290c7d3e1b3b9512dce1dc2f4fec09ad22d4fb627c3cac5eac00ec8d89d5d74b0ebdbcc7ca169cb27a97391835c7e50b5d6707e3e9b2f1ae5b96164d40ba80cbee45a022dd34cf56f8d4bba93fe842b16f139983da158b7bcf90e92bee08f1cc3ba9bb2b614aebabf7cc2a9c02b72e0003bcbec4b172e0cb7072baf45f30cd47285ecc7bc8c8ed45eda585ebcca4b6428053d3af812d07f1d52b109db15491e4a9b08ed83375a1827eeaf58af327031bfe8bda3867a001242c7f3d56775ae253306f78fa48e32a7e6a77ad5c5b330ad4032fba1f6378e3251e1f23c74e179ec9ea7cd43b85529e4e8b6fe4f710330336e821b5256c6311f6cf2e2be6bb88c114c06d7136affd65eafec5ad82f32965a4ea6fee49b8bff6e2d1c64b87ec612c65b9e7488698edfda87ec509d953d77185f15172a251be61310105698d992d01e5eb5703b328babd6237fba2a3fbb8e67c560bd6c13167d24b6b59b19490a35eddcfbd35e03908a781d37d442677a04dde6f03b11ae5debe2aa57dd4e57a4fdd1b1e7530f4ad36febee620960cc76ff267736d7616d310317cbbf5fa1e5cb1c2d19e5fc702a5afd57954cbd191b99cb8ca68e0164d5a175f55c4979bed928945f678aa832c64ab1966c4494648e45f099226e2884902a61d37adbabb63414ed0096d1f37bba2287c6b107e2637c00fe06ff6a35c4c07f2f16a3963506dc93ca9f95750ee7d83eaf4b770b940ae544c678d1be7842fd868218bde8bff6539c888f4f8e1a04a665a3e53dd846929a43a675219d34b48704b274c25f27aa210cdbe96ae5bc7e07c24aa1e0945d729dc34e97ab76862bec9b7e37f6a352a71259fe01088d449f1fdc67aae2d488801819a5080b2ca20b21de5665381c00bcefc4feb5b9c2bca7c31829038c2df9609ac5f5b3b8325581aa69ee45a54fc6dd7c6473be1ac138f534ba2662950769e3a069e6331e538e3a217302aa50a3ef7c09aab69f373230b789846c25622ef349ea4c08a49b3366e349e457689d87f3da579b2a632f1f35d5112efec0adc32a58708f891515c40ceb9e9a9195296211bd78c52f3891f847c90c1a9746f542eee4395cc58a7843289551e840f2036ad0fb709369f97fc94e1cd1303e68c038ce75b99449d8b23b20004c24a805c72014bb741430451cfee0fd09a0e9ab3bb09e98bd61403a7b7638134448e1b7a4d352b92b19d49aae8299e11bb2ea1e24417b71d9a8b43cb80771e02b1f3a84b7479d07462ae000968fa50b99f1a8ee5198a9a5ea56dd4ddad0fce1c3a93b3923e2c6c2f37ae44469286d981d464011c55f7a06990adc492ec83d5c1bb1deed282020b2fdd657b32ef1b191cabbeb2fe234f646e60e88ad8f6253ef156567067d4f6d4aeed82dca8bee42b1c6a9d035caa6f8b05146c19199f670a3061436ba700d7510139caca30b024427368be236e1e68b3d574441c5b6935d2d6a721fe3907f01c80cddbc3d052075eee0137ebd3697df6f41612c9c67985dde373303a9346beef29a9694d0aaa5b6ea32c3eb590db98390cc621b2faf42b3a16c38c78d3160c77b3c77f0d5bf7a6e1051f38468b348de47fde192a65916c59777addba806d447df1e6227f3bac20c1845cef4686d0dea374aeca0119ddb443c105c667b3d7932daa2b6304833875f04b40e4b1bdd7155548642129fcaff444a238b7e36b518068c50971bd4d663575d38735cc1b1a0b048dc92de62dfd10cd3602121233be2e1d60be4c442704c1a16f74e68f33466a00810d48be5fcd17fed2ba4ef758591846514c4f4dc62d2fbb55d3c86dbc1e9f11589f9cfcaf6894c659a493632eac15294d80dd378f7f75ae6a1c697820283b8ec14157247707c13cfc3377c1c352a7226fa6bd44babad7d6efd25f3b17c6eb46f7223bbbaa4a7914574c46914cb2f670c55ebaaad27e71bbfcbf37fccd02044f0c7f1f27928d80fbff8a2e597c384345edb67eb127f973396c7b6069be0c559f163683aea3eead2583cedc4bb9871b96e4b7b21bd007ec85ded09f7923bcc11f03bf2e0abce7baa934d95b5ffef302a707e5adcc11264d0600d1da9a2883099e03514c3c3ec4853826c730814094ea7f673bc9374e284ec96d5190f72bbe588cc3ce9229f236d3dddb032878ce5fb2993830528e50e6a699d236bb6b4d4ff982d7ff11e1394a03f07672c19befbf29ceeebd1acfebcb82b072185691877d118489360ab62eb4be6704c18dd14c17fde97b431effe35b32dda69937d46b32ddac37dbc0d7e422c40838333e073bee17ed7fc2ec6b6ccf8ced7c1670b4b0b565cce40a7a1614085047631dbe7004be1e841ce843f0799aeb6ffc367501c2fa2e385c007f452aadd757ad9cb6f4af7cfc7f4980838d59fe5c5052c2846d2651c0285c0569ad10fb4203486a4972902f3374e682cc8a0262c8bfc86d1fe7948cd781862010125d7d7fc580de8b5b076d00fb8425d088e95f850a95150eb6511f375f2e313ce315026b10ce33b944cfbfeb03b726d2166167261e1ec0b4e54fe4b5fbfe5a594f3d452b48d28a1927bccdb8bf8b48bc5e486fbfd677ac3edcb5fac4b6981ca448cdfc98b9b2842b8a50b59b7143d57b31bbf0552edbea51c118b6187ff7c3017dcdfb590497f5918f2d84792ff98728870513dd1adb01a0fb25dbff6f8c1cbe9cd40096e6920f7a9e61c6deb24dc2410a958ab6bb64cc96df993f4cdfc6730dbaf73ad6e8a274f73853691c9ede1a453aff0284c7290dd8cdba9bc3f4a1843b2ed8d312b39d9853d11f1dc2858617b03c2c81b73ec27f671774957f40f406aa5e17835ec74af7c69c20d8d1c538", 0xc34}], 0x9)
r0 = socket(0x2, 0x400000000002, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x1}, {0x34, 0x0, 0x0, 0x7ff}, {0x6, 0x0, 0x0, 0x400}]})
pwrite(r1, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x1}, {0x34, 0x0, 0x0, 0x7ff}, {0x6, 0x0, 0x0, 0x400}]})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000540)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0x1}, {0x35, 0x0, 0x0, 0x7ff}, {0x6, 0x0, 0x0, 0x400}]})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000100)={0x2, &(0x7f00000000c0)=[{0x5, 0x28, 0x7, 0x2}, {0x7, 0xf4, 0x3, 0xd0b}]})
pwrite(r3, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
ioctl$BIOCGDIRFILT(r3, 0x4004427c, &(0x7f0000000080))
pwrite(r2, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
fcntl$dupfd(r1, 0xa, r2)
setsockopt(r0, 0x0, 0x20, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
pwrite(0xffffffffffffffff, &(0x7f0000000740)="055cc787171f20b652bb01607f577d50c2a851f0f1b007afff3c57d83668da3ab35628f24bbb2700b4d4aa7339d1532109a5c4de3c72dc546affae7126361ca65ef03e9209b1df284ccef6a599c354e6bddec51cd3fd27096f845eb24282b3d5129600267744979d92bc280f29ffab6917b41571a81c7604fbc76166308f2c729bf8059a5c5f569365e622c097fcf4be89def98f34473c10e6a303d41abffc0a0a4f0d069ad9de7528c7deb2d962025210cbe9f7e09ba93fc4270bdaaef38056f229e22ba255cfe3248d41ef2c40690ae656a72a1fd905cd93f7b60936f9e7455a40c03f2f36e05071f24f0909759fd7493ff552b125c3a2d3fc99d51b448d19d15d6862bc0cae0d35654a4c44823ea44919c03f1cf061696b8a905366c62eedac405693bb872962a8a3812768afa87059dfb57aba0717676de7e376fb59fc69f02164475f78d94ad671b424697e5cfe2c0f60988708f7be7208176d4682f8d39b7fb5460b8f8b0e8944634b5340d1d9369825b8d478e93618660490b77187cfcaad226c22c346fc8ba04c9c31168f2e9c7240fa37e2014e3e2f25f667abd10c7736d72b05400744c99cbff31ce40015a85a2cb94f65a795a2b1677d62eed83a77c1906b6d49b920f592ce2e8d76e51a9f48a7386dfceccb5c15f786aa792df6265bac8f365c6b925698937bad2dc1ac340ae40b275348e293c40aac2817bcf1a7576b8674f19de704fa246efa0d33fb4f94ce77bc74a036103736d42854453e67a4bc43418bf604223f832cf6bc43c86dd4e1fd26b06c7f7e74095218f6c3508ac62f507bfab2ffd5f6b417699a4623fd2365458a0fd420c9d0ec0ecd2484615af49778b7a8be497a746897c4c4d2ba7bed70a6f7123e8a4be01c9ab42e7a852f5e8a7073fb799e0bc3da3e620b93", 0x28f, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
sysctl$net_inet_tcp(&(0x7f0000000000)={0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
getsockopt(r0, 0x29, 0x40, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x2}, {0x20}, {0x16}]})
write(r0, &(0x7f0000000240)="0ddb000000000000000c51a14ebb", 0xe)
r0 = socket(0x18, 0x1, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x20, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x40000000, "2c02eebeef00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000000)=0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
ioctl$TIOCFLUSH(r2, 0xc0406938, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8060693f, &(0x7f0000000300))
syz_emit_ethernet(0x66, &(0x7f0000000000)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "d654f7", 0x30, 0x0, 0x0, @rand_addr="fd0eef1b75cae687f2f758308ed50cc7", @loopback={0x0, 0x2}, {[], @icmpv6=@param_prob={0x4, 0x0, 0x0, 0x0, {0x0, 0x6, "fed2ad", 0x0, 0x0, 0x0, @empty, @mcast2}}}}}}})
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00000008", 0x8)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffff9, {[0x1, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x3, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x2fcd, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfefffffffffffffb, 0xa6f, 0x2, 0x80000, 0x5314, 0x20000000ffffffff, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x6, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x7}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0xff, 0x1, 0x203}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x4, {0x8000000000000001, 0x3}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000080))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0x3, 0x0, 0x0, 0x1], [0x0, 0x3, 0x0, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000140)=0x1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2005, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "090000000000000fb314000000100000742f00"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x10000, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="82022e"], 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x3e6)
r2 = socket(0x2, 0x2, 0x0)
dup2(r0, r2)
poll(&(0x7f0000000240)=[{r2}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x80}, {0x35}, {0x6, 0x0, 0x0, 0x8000000}]})
pwrite(r0, &(0x7f00000000c0)="d00000004000000000001b000008", 0xe, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x52}, 0x2, 0x0, 0x0, &(0x7f0000000140), 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000000))
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0xfffffffffffffffd)
writev(r0, &(0x7f0000000640)=[{&(0x7f0000000140)='#!', 0x2}], 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="2009d75c20650a", 0x7)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x5}, {0x14}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f00000004c0)=ANY=[])
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, 0x0, 0x0, 0x8, 0x0, 0x0)
recvfrom$unix(r0, &(0x7f00000001c0)=""/208, 0xd0, 0x802, 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000500), 0x9, 0x0, 0xffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x2}, {0x3d}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)=ANY=[])
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000513600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa04", 0x25, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000000)="b1000503000000000000080005", 0xd, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000040)={{0x18, 0x1}, {0x18, 0x1}, 0x0, [0x0, 0x210]}, 0xfffffdfa)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000513600000000000000004000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37280f18e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f75006ee01b1257aea8c500ff0002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
write(0xffffffffffffffff, &(0x7f0000000000)='X', 0x1)
setsockopt$inet_opts(r0, 0x0, 0x5, &(0x7f0000000000), 0x4)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x1, 0x11, r0, 0x0)
r0 = socket(0x18, 0x8002, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, 0x0, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000440)=ANY=[@ANYBLOB='$'], 0x24}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x1d}, {0x87}, {0x4000006, 0x0, 0x0, 0x3ac}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0xfffbfff9})
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000004c0)="aa55e54742f735ce44c0f5842dfbd8bc57df79aec21a8b0057c8d8d857c0fe5202db277e0bc2692e2dd1ea5eca833249e8969afdd0175266853991c7f1281a97ff9ef63efcc31ba0c3e1bf9a5d4454ae9dc8c981e3a553e274414d7d700caa2e5697128a2f848bfdf55ba7adf3f404ab2ffd3a5153f569e15de36052d0a409343983eb03efa6948bab448962382aede76022eca2ffda2e2a65568004f894a3658624bfbad734ee3fb706aa5e5eba0c55a06094c3e5d88ec4e81ba5f00436268adeb7f0fea0e48f8db16340fc7961a35361e37ff20b82cccf5ad0c7e914aed94ae51bc3b63f5b69b417db51ab608722632b8f0ee49f82af61b5247bc15119ba9f5784ffc96cdbd41628223d172e4d33c939fe22d5312429afe081f0b7ee87006bb64e718c41a02ef5fb98581a2a68ffc66f06090774732ac856c26a8e43f0b41bc059acbedcc50b0bd3a7c41bc93b0daaf4e8f9c9329741a4b45d8fed579e9311120b424632104235cb39b5338c7b2edefc2b7597648f561714e76230460df95044281c571266b2ed0bd871e0d514999e09ae619ea9f16dc0e39bc427f9dcf145cc2cab247ff6435d741b8a78ecb1c76bb622e1f017a7a18a585f63c2c748acae4751d1c33af30a61237be37b22a1dc7d87623ad68788856d65ef052e5251c0751ab3f5c428e46d4ae6ce0642d1e4e3aa30453b22bb24068ec13cf985c39b4a819fefe990df268201eeaf66c6398b68af1a0e33fff03c6c81585d48859c32f76dbc2b4aac280c6ff57f2850e45ac6d633a78cf419934a2acdc15626179fb5131f0ea3fd115ea2dc92d2adedf4daf6f668217954620a0649085a1ceb574603f5b7ffda2f3bc167668565a9e2bde8305bebf18bb90561be119afea4120df38acd31ba24a98059cebe84e9ff462db37857e1e6de25ec6e65cd38269da06208f0ab983d00ed5d61c49231a2a336216b4e8b8e0d96ad8fcad3d9b89ec199fb8497e01d03c9ac1074a2a497e6000d", 0x2cb}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x20}, {0x61}, {0x16}]})
write(r0, &(0x7f0000000280)="731f5c52643f0e37bf54dd2a1b32", 0xe)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x7a, 0x0)
mmap(&(0x7f0000fff000/0x1000)=nil, 0xd12f793e24a, 0x0, 0x2010, r0, 0x0)
setitimer(0x2, &(0x7f0000000000)={{0x0, 0x4}, {0x8}}, &(0x7f0000000040))
setuid(0xffffffffffffffff)
r0 = syz_open_pts()
ioctl$TIOCSPGRP(r0, 0x40047477, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = socket$unix(0x1, 0x2, 0x0)
dup2(r1, r0)
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
r4 = dup2(r3, r0)
close(r2)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000200)=[{0x60}, {0x81}, {0x26}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@local, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @remote={0xac, 0x14, 0x0}, @broadcast, @multicast2}}}})
chflagsat(0xffffffffffffffff, &(0x7f0000000100)='./file0\x00', 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x41, 0x0, 0x0)
getpeername(r0, &(0x7f0000000840)=ANY=[@ANYBLOB='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00'/4086], &(0x7f0000000200)=0x1002)
sysctl$net_inet_ip(0x0, 0x0, &(0x7f0000000040)="c5b3abe06465cc9e4ed61619087ef22eb203", 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r1 = semget$private(0x0, 0x4, 0x1b2)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000080), &(0x7f0000000240)=0xc)
semctl$GETALL(r1, 0x0, 0x6, &(0x7f0000000740)=""/196)
semctl$GETNCNT(r1, 0x2, 0x3, &(0x7f00000003c0)=""/154)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000280)=0x80008)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000003c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x7c}, {0x35}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000100)={@broadcast, @remote})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="b5027b1c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
listen(r0, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0)=0x441, 0x4)
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
close(r1)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
recvmsg(r2, &(0x7f0000001d00)={0x0, 0x0, &(0x7f0000000600)=[{&(0x7f00000001c0)=""/228, 0xe4}, {&(0x7f00000002c0)=""/100, 0x64}, {&(0x7f0000000d00)=""/4096, 0x1000}], 0x3, 0x0}, 0x40)
r3 = accept$inet(r0, 0x0, 0x0)
sendto$inet(r3, &(0x7f00000007c0)="1daad5cd36195d6810b318271ef9a4e226c2d78af736de6e7b07d1d62e84affdc376a493673d4195232fedc101a1d8adbe6f5c1dba9d565c875025f067ef069c8b78d70a06bb9b172a2d7c03eff6b58b06bbacff74db5471704990b5bc7c583f053ce0dab8e1d855c002e5c0f21090e2672493cfcf71096ded57b5350b28f1e7ddaa45ba14ff355c5280064a77fcf0cc7510946d503350ec4e8277e807e52985f11324bb0c5d72b0f9c6e2b27671de172e8a5307791b203a90fb79bfc05002e542e32b5b78e75e1f7d9deeb76cf17582198b5056bcdc5d0d8ace2bc699a0db1e138a000000b92c28d39f6e23bc224fd0e41eb6d9e3de4c0122bef8eb0acc9fbb9fa70df9265fa833f7f7788b351b9a0abf03d9e24db2448b2db5c1105d1071194a948a28b75775ade8db55259b6cf70e825b9e7675cc696e9657d9ba4ae249fbfd5ebe2c9499b26fc346d36d0fe7a2d0b32aeff27e94fe6994ffe7086d8f0c631b9688003f65d4b91d0000000000000003f5cea6e423358731875fe8797de2e8521227ec1e7a11d2d5ab7cdae30ef53c508981eb15f187342a1bd1085698e3f5ae7cf69e79e10fc7d8f9f2fe3d1d4cf4dc054a98f9013b515452b742f911f5aa5258ea5504ec5fd29ae3124f55cfdbea9c3969dc552e1d6f13d86e3043a8ed35e413ea2a8c43d6c462463b88ea0a3fa87742efce671a2f79f5b66a844fbb016ba0a0eafb7f26c47f58f25808cbfc6902b0f0133039066c1b0e4b133ab19283a8447b9412faf99b67243a3fab7392f29b6d3cae0b4fc6e528f7662267692e44540bfc23ae65598b6b621eed2daac98c6f59ee7a08fca4bd017e4b6b9be821d9af3bc2fadf96cd15857a54d7e879fa9d61bf34654841f961a030f3c81f9c750128b0ccb8fa9262c5d930c8f527d9bd5fd14ec06e29de61866bb4fb0e405b324db46fa449242b5ff600fa6bbaa50d8736d7be3e9728bcebe3f703aa3d99b63d390759a13c2251c3ef8a3e0bf42c132db4317736a807c778c6b5e72a12330acd4552f2314b02eb9ceacde4bec3b552298a1bd623c1af3f4ed230056a735d0c372b6bc10000000000020000000000000026f184ac625c20f47abf53a298ba0d4e62943a57fafd57a5569c84b5517e0a92ae7580a16e6ca625dc04bb1fe6593f8e75218d1514bfe0a49c3483da21340c35377bb720d545fdf1c604dee2f5f126aca257e273af57b1341269319ddcff0281f060d65ffac74766ce2b0d3ae6074861220f542a28f4f67c464c01e27add18f942dba7e76fcbe894b1a439eebab9a9e9269bcb698aa699784c79c43ef1b6018a0432b2de4f299034e8ba0000005420abec3b55c819f4bd4a3ebe69ff68da7d7334a3c390a00b6373cc180cd7ae1c716b1fe026396179a3bb3f5fef9fdb52c0203c0e9b9965a1aa61d6011c98c2df81535ebaa69a3525fe1f924804e7e729daac801843f856f8599313b72fcbc41d1543c4a9902329d246edf766b227e1f66fc8ce920d102f4cd265857a3dcc54866a2b67a93b1d77a1c10268d3ce409bf692a7bc48afe4290cfe89fcab206057fd34cd39fe7b36c9ddf7759af0448c59a8fb3f8c61fa395d57666d076f312f1abe091c3977861b619e7c511337b9ed63c58da22ac9467f8884820e2d4861baa5e3289275b01fdf62b3861b9338939441cefe5603ff7a2f64fae5a93667d6eb2147018a60fb40bf7d89fa16c82ae55eddfc3e66baf4eb9c091dfd2b634574ed912858e3df00c20e640a0cc7c70c5ae73f999a3d86de45b960e5b5b970010beafb71c01492c9f9d5d762bccd5f03fb22f862cf6b67eebe8c910259b46f53718fb9378e9d60000928a6101ae308a387", 0xfffffdd0, 0x403, &(0x7f0000000040)={0x2, 0x2}, 0xc)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
pipe2(&(0x7f0000000040)={0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
dup2(r0, r1)
select(0x40, &(0x7f0000000080), &(0x7f0000000100)={0x20}, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x30}, {0x3d}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
pwrite(r0, &(0x7f0000000100)="fbe0c042ae4ed1fcde4c4d84c202", 0xe, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x4000000005bcd)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080)=0x3)
sysctl$hw(&(0x7f0000000000)={0x6, 0x1}, 0x2, &(0x7f0000000080), 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000140)={0x0, 0xffff, 0x1000003, 0x6acb978b, "0d43d3d3d33f08000100001100ffffffff00"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000001c0)='\r', 0x1}], 0x1)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
mkdirat(r0, &(0x7f0000000000)='./file0\x00', 0x0)
rmdir(&(0x7f0000000080)='./file0\x00')
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x4, 0x3}, 0x4, 0x0, 0x0, 0x0, 0x0)
setuid(0xee01)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0x2, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="100000002900000032"], 0x7c}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x30}, {0x20}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x1c}, {0x4c}, {0x6, 0x0, 0x0, 0x3b00000}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f00000000c0)={0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9], [], [], [], [{0x400}, {}, {0x0, 0x0, 0x6}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
setuid(0xffffffffffffffff)
utimes(&(0x7f0000000040)='./file0\x00', 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000080)={@broadcast, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}}, @udp={{0x0, 0x3, 0x8}}}}}})
sendmsg(0xffffffffffffffff, &(0x7f0000000040)={0x0, 0x0, &(0x7f00000004c0)=[{&(0x7f0000000640)="edc4779ae5400b01f89c06db95ecd74b4acf052425f35eab08f6990e294e4434b3f234d1bfe275c2450c76973cd36fbe02a0a9fd468e5b76a5d74f972e0f58db68e8b5c1e387ab32a8516f66", 0x4c}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000980)={0x0, 0xa, 0x0, 0x0, &(0x7f0000000b80)=ANY=[@ANYBLOB="2800000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="1800000000000000ffff000001000000", @ANYRES32, @ANYRES32=r2, @ANYBLOB="2000000000000000ffff000000000000", @ANYRES32, @ANYRES32=0x0, @ANYRES8, @ANYBLOB="9048f3d3053be1b2c6d75ae26229fe8c271bc575294e395c442f92682239c5b5eaf72ff59c04000000000000004e2df421db663b485a1e6da7b5b161d899b4e798ad4891b270908b33eb6c852267f4260d88ab164b9fe1d212530b6a1746f274b3e69a9e6b15789da2a58f109e40b110c9b4a103c06aa10a81c1b675f1d19fc870ca505deadcdffacb9d17574a0bcbd86bf223e9aa7b6d20806cf707d683467ffc6a097410bfd38c605e8190fd93"], 0x60, 0x4}, 0x0)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000240)=[{0x7}, {0x4d}, {0xc76}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x81206919, &(0x7f0000000300))
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086337)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x210}]})
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}, {}], 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, 0x0, 0x210}, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x2)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{}, {0x3}, {0x83, 0x0, 0x0, 0xffff}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d328b", &(0x7f0000000180)=0xff1b, 0x0, 0x0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
sysctl$kern(&(0x7f0000000040)={0x1, 0x16}, 0x2, 0x0, 0x0, &(0x7f00000010c0)="64b2020a", 0x4)
munmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000)
mprotect(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000380)})
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000000)=0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x1021, 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7f000000, 0x0, "0010e9ff360005430000000100"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
munlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x7c}, {}, {0x6, 0x0, 0x0, 0xff}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a54233f4d9d00000000", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x4c}, {0x4c}, {0x6, 0x0, 0x0, 0x7fff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001780), 0x0, 0x0)
r0 = socket$unix(0x1, 0x5, 0x0)
ioctl$FIOGETOWN(r0, 0x4004667b, &(0x7f0000001c00))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000000c0)=[{0x7}, {0x2c}, {0x6, 0x0, 0x0, 0xfffffffe}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
socket(0x0, 0x0, 0x0)
socket$inet(0x2, 0x3, 0x80)
flock(0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000002900)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x24}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f00000002c0)=ANY=[])
open$dir(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
nanosleep(&(0x7f0000000000), &(0x7f0000001040))
writev(0xffffffffffffffff, &(0x7f0000000ac0)=[{&(0x7f0000001c80)="373e40517e4c044a2422fd09c32aacda72525149a00deee9573bccf811049f65d4678e03f98bb6ad53fc15f6029ab9b69e136f2af35c7dffc9d5b2d0f2211f7021d47e4437652b8568c44f5319140c55cdbab73ba75c0609157001f7a53a6c1c8b05c09101e85fd33e6e783efbb4b737aabdb8417cfb7af848416735fbe8eb8c32b07ff6e4d2f8d9815f566d3a43409027c9b134d3d1fc6bc9333c4c8ffbe9760f1f75904b605de357cd229d5b4d2ab1903776cb9b0f43a8cb9bf4b725be02c7f2e0c6b5f6581748bff70fc427d720fa283cfda3fee11db543bbd998d8e293996c405072fc6edbbc3d", 0xe9}, {&(0x7f0000004000)="716a408a3b34f703ef42833bf6ad3c5f481196db1bbf67e41a75b82038541b65db38f535aebc773b629165daacb97b9948b9ae2388cf9b2f44d8ecc97861e6fb03a72b8cd8702724d72a6c1f1718e67379ecd18406b456b1ac4b1a9819f066e78f821081a96aeb86041b632d1099889ec0e4c022398eeb7f4bee2bcd4bb43f1665c0fb3ed0910d9b97193eab22b29a95c5a9788c6b3e6baba7ed36494c2c3fb68a36fa13e766b21a3eef62a2094efb9214accc0719a8aaf2cf227aa96a2e444628161b53a99617e6d481dccdf76b7eefbfd008557e4a85aa36c4cedb850ec3a8a73889a9221514071fca32eafa24c977c1c11df00a02aff2ec955ba8fcfabef12c1bbe6dfae712ed0c6af4b2906cbf75222eee1b46e4c5ac2b6f9df8ec5b21c9951379783cc33ce7f857f0ffa56e82a082a45d73451622c4f46ec32ac06cb7258fc6ab8ebaaf8fbf235ec31c424636b83d50f3e5805c87a0e5fc04fb5bc5f92193c2c13fcb08ab4be9ea8b6b1665f5b00bb2801a45d316c5f9576cadcd1d8771f5fcb0b8333d2e5153b37d2fc7d66bed6962d6f1416a4bc339aa89699f7e2f995f9b5b487e8d5ddccc7b6b7a81b157c51f4f854d2cfc7208e7785b7f2e0e0e8c94fd0c9f1d4164924bfb8c73e7d9a68241ddce53fe674f5963cffcd41639d3b4eba8bc94872fc97b8f09bca75de56530e2eb781ad0ebc748dbe9fe6e368a20b3edd0e1938056620cd644627d465847e9a2bc53343996f65696d705e86ab62ac23d140fb4d13c3dddf4ea67adbd133242c3be50771b3aa5599c3c13ca987e501639eee5ade64184fd80317c8d1ccf1d9cd37db34d794d8d753d00747bb352fca8227edb2f19776076e26937faa8982542322a9dddfe32ecc6c0a10bc3c032cbd0e800e82ed7bc0f78565f32186749b7d02aa41f3988cf6d722a407df691add26e9595eeaf61688f22eda6c9a08186f2a986311f62fcd53e0a27e714856be798e74730d12eb8188377234ba66c18b6adce70551f462e09c1b08177c9d82827ef0c433c1c9fe50b02e673a17b1f87b0cf995d633428d2f3da7169c55208d39c9cf066ddfe6080830506383d8c07bc321c3460bad8fb3b2a48322f50eecdac8a9e7000be10fcca517ccad872a4fd54e9a5ea9245698efe57e774a1c2c6edbb80bcda7a4d67728c2f42c423ce270dee62f512d857def0af95347193dcd97888dbc049b891eaad348c3b99df31b305e872c81ed7109a2113df238be67f622355ca62ba55aca2daa03a19e51db1a1b03142d24d7546732d45f98103e71a89f2851e6f4797d632dae8188f91d1ed5dbf3cc86510bf887a6f0be808751ff1a125667ba8866b4770ea6aa82027f4b3532aeb6098a49ac74a1602cfdd7ccf90f97389a027a3cb210d52845c5a1463ffa690ae9314596e1a2e5d0931ce3c91fc0a1a9eb64a2f35b9abb2b530cfa7edd53fd4ba2b62bb3feb2b2b5cf28bed7e94bdba16cabeb0316d5af53c9fbd942ebbc3d5a6530de04dce110e641aaae09ad547ba37289d90934fd9f1bdc8d3767d5e6923d321b09ec5f367375d54781dad213d5e970e15e3e2b9ea5e4611a20ab5f55a20d0cfc463060cc624432be3d170e51a1ebb13340532e7c56c5d51bc17764d2c1f37880c2578a624be144694b621307b3a2e82580b3b5bf35a6fc9f7013463a8a6c2a2bb42c44b23ac28768dcc92466910113870888e9ab793e66c5eebde56eb4abd8bbddab109246f1d566ee1c90077135cb63181a482f622d3114834db01a960bc41715247ea5a9622f1cf8454e0f5373e8002a9ee5dc98fc69854bbe25b9c8ca19060d160541ba888f33af78e015aabbb53df6aaf392400d570c3992e0550b388fefe4f3f0bf794ecfd965f511d696df61966011d8115f4cf6b06c2fbf3c777ace498393c3ccf673dc06f11177cc453e8bb635ab15b3a29f990b443769ebab426898f44d15aa3c306aa6f40c42216089175c0f5bc4415e26f0761fec74a9c75c4c345c55ed93c3be8a0f3638773df18b1ee570bb885493e699c24943062194ba2efa33a8add0edb308182ec73d4aff85739640f6cfd464b5183da57489829bccb730af7446b417a41141f46138754e3c5aa373c8c926ab0a9552f04a7c718af3c1b54152f4f65f42e7f2b8808287ac543afb183a071b1a031ffa490c13268ccd577ce0e73ad848003074917cb18d13e8651a81b2faae22c460b54f8ad7236271cb3215b393f32988a769867644f13a337e55912b33eb188428011efce6b4bc77727e8a3921a8c309286b635f7b49f7a2cdcb43f26117ca32269222a5103a7f0afda4750d94518f43fc34e6574e3f8b0f1c54623728d1e796a59b84f8e9333256dab20a090918cd01997a5f29116248d338c96bc42bc43600e0a5192287ada1776056774b9d9c2a7d578b20877ea4c7892b491aef080382eac0046e6f37f9ad33c821d36805217640765b8df8a2f5d951a1c30216e929860b2c92145beb9ac13f70b3fed5ebc0da4df2cae3a4002bb721f0513554a92f70bb6d13a6e4c534b27eeb93c7d15e7dbc2b169702c1f5beafcb341d6baecc999ce3e8fc424c8d43d415cddbf89e42f22f588fc3dd69d306858fb2efb55373ae89355cbe7eaaf4fa9aac529b4dd679333e9c7c382bbdd2b35da8d107ba486eeb719d123e687964bd048022016abce276c275f757ccc16631d8b348e44f68afd45ee64035d7d6ac59681aeef15862e6a210a7600b9096cf7012a3fd3ac3e22a5855b3c205aa0e5598ed072cbc6d3f8122480207d1c680fedcdc2d2da5f18d0f302bc3789bb28a7329f720221f8630894443ed91b400a0809882b49fd78d97a9535866c68637f2926a522595fe28ffbabcd8100a52ca8b89ed25d014f17f773a7fa764104c1df9d594e5d62c3409e33586e288d39bdae43fb4ca36152c8a97a5b322c458cc1b9c54917b570bd391b252e44dae35ed5d7ac1cb703eb93194b1b4fe0cb4bb801e49eb5e0a4744f432aa49c2e17138053ef132915ed5976ac554f193364e96c5443ed46f80a525c1eef510a6eba201b3ab2a0678a5799ab3c4656b053b232b46d2a3e4c8a3a3e55ea7e35bd96cf64fd6ac2cc7e69ce660802c9c4e0f3648a88ef8775b7bf785227773e58afd617e1f256515585d4bbad260aece574b646e943a86e6cb6de7d9d192a385de9e7428086ee2f0b9fe2903848446bf0260eae98916ace9e3c2ef6d76a9d086a06654f1e115bf4964eb4d8aef01b244b3f0bdc8b5138552393b5486268c4d0d0364fd0eef30d38cae9d2e012f471311f6b871b29d31fb12edecf89267e8949fb92bf3a92fe6b4be923df31d2a0dfa7adbd8ef6a304a39491582faf0f2a80780829f4c0f7faf241e65928ffaa87a58dbd9ca8e5e84def9f435f5103a696f4c13d2adbe5ce5a99749fd742ab4b28160bb9ead8ad49e53f0286dda8e6345835217578a5dcaf708b0a5aa7a5856839ab22b81e50eb9b4be7191176292ff71be5695cf7c9ba280d43ccb3aa914c88cecbfd2c0fc8a83bc779ec9d25cdd6dcc4d2d814529b16bc6610bed18941120be210b436a3d74c45dd0359f5a22191804ecf73d5113184ae519bd7c69f666dc983a8bfaef062a16f78b2404e251f27a374c4133770bf2c59a7ba4743f0568823c18194c3d3f2865aa91bb46a695cd4010b5779c563aa603769de28bab70a19869f5703de30192ca3f57e95728e453891b42fda527b3e8f9cd0ad5b3263b531ff2621f41f03a035c4b8da8f9b34b78bc2eb26d8e62bc4351ea6fc339b9002b2566f3f6f1d6f5721718f135ebdb78db26984433b39fdc5bc0b1ebceb1f3ee194794ea450705544f194b05429c03a4c19d5ac6350bfe735636cee3bfb43276383a6a3713582905bb2267d503112acb5e4a8a7bd1d079bc9e8c375791a54287c6682180a28886805ceca29914e0b5cc47f68f6604571cc733b49b09fd0d85e70889983179353c876bc0d9dd9118e8618b89540c2016e84074394c5ca07c622e0cc4dae61a988edb6a96f021526fe216df3a24dfade7daa16720d49d927544dfa951d467648a0d31142f2a1387c65e9ade18324a8c10a447f383dfeea15e32667f429acd97b195e57cae1d425d0efe7ac179ac3f6f22a6ed38e28d2f1a4796d15ca80331c45446e46ee093e572b116b1a2930e0b83a01ddaae91fdddafc829ce96053482cf1f9c8f369c1aeb24d434feef0bffac6ac7a65163ceb87f13b83e93af7a07a3a20ec67cc1db15b8e7e5a952a0a199a65ae605e762a0cbff1f0095558000cbeb37608e04813cd027800f7fd0eb1bfb73980a12e45b43e5634ebf6b0e22db813181963c2b70e8731cab146018d67a37fec748a1c839ec950698cbc20d2a187613eef11372c430b0eb2672d93f9bd911f8776fb240372107fc0e6b03f3479d6b202308254f75c5010a3cfc975de7be4ecb94c59d258c0938e78ff1ccb5777642ce456b16de6eeb1f03f383a024d0b69edc010ebd98ab1719c1c69c14570cfd91b57bac61df52e3844865c0ed64217d5d7ade1a8efcf7e57e76612e31b84a1c987e4976b1b094ddf9d734c8bc7d57a1482237f6d37588f396e0ae5b0f880ed17e1efe6b01a9adaf5c5168c703b75fde2c753214cb19debc3ae8e2a0f5d47817a5809821eb77fe8b9201eafa79607c18550faec73cd9c54328aee973fd99188006308aad5e51fda659380f52642c9b7d91c63d701992624fdc22c0ed8aa84330b15b56f05e6cfaa1b1a827c3971047b48447e458bdb1ff0b88528068faafa7bd59b4810df00f2ce9ca1cecb4274bf9c7036f874fdf590410cf0e07b2d39fd78fefb7f0c15cf8e655d6c9f07853484ecb8272d53dbb9b17ab47bf7055a1bcf9bb9ca9748fea183fab387c31a8b2f38131dd3fad565dc900277126bdaa9086c5f5d4c204db5ba31c21f9064cbfb91fd07c57149efdbea53474cc9b25673f2bcfe26eefe3dddc4013a645f3ff9cd5d101ab00ed3195484a9c3adee6a90fa8505f530fe0c978f939fa6291bd9a0952986786132930b5fc9a19ec5931da04c90f75b09b242f3b566365e02e5d831e62dc95083c6108210eb220b6156ce04df61396a31143d503fceb6d77f8b4086a303b59ece70c415cd7b10db690d0b8bd191711039fbba8626d3eeedf2308620039e5bb9320f1bea3e24b53124d08dc47b24a278af89505c37b4041772d7a1277dbba1a1f7eb700a29986e58b6c7f95b2758d50b4ef6163e8a08fcbc8866e2249954d860bb4141926c0113937a736e735abfa80ec520047c2a5c38e0131ae1354f8884504c17997a200e7bc5509890809753086233a9c8fb5cc2d904378b02ed59c0fe25ece7ffe916687573d680c8003f3d2ce93608c89a374ab2e108334278673c9cdbcddee0b5875f11f78e902dbcbc19ca500046cafe276917803c38e0a868210a0bde2c31af2a0df66fcc0dca892a671be13272acc19caac617d178e33e3f545e1b3184358901dcb97a850eb550e3eb852ff6d7f3e3cc4c63cafc88bcb222d952f76133e82e54c3282077e0c8c5f994265b1d3bbaf1cf5eff1ac4a5c6b30745dc7b94136051f55dd901d99fe57ad202efacb6ad5a75f1f8428973384332cc2bd31e50cc3ca119a9f8b542b16c72ce94cfaefc045120935fd0af35493fb1e8195b1945bd940172ba9d45d1f4960f6f4d01824ccc23af4aaf73d832f0a016b51c40c18961fca4b40d3c8e80", 0xfd5}, {&(0x7f00000007c0)="3656fb439d40b2c8c984fa90405f6b180a773406967d5c49355b0773f7745f6102000000000000007e45cea3b4c7d8d9d5269127a081ed7eb937b33758d1b11065da17e06a0e4871635da4f35cd318b8211c08b33a5fb3c7cecac6384360cca8b0a52493a49f87b48d0c26d483d535e28021c4cc25ab21832f95e72148f47b384a10213cfe80fe4608866aa9b0fed07426bd4d5dab20ea8a20e21f65dcbe9815a508c6c61c813ba469e2db64f0242d8d1a04c543728b1c9e0565e7d15dd74992e0460e516b", 0xc5}, {&(0x7f0000001f40)="5b6ebeff33593511d99197824fdfe6646234c9172c5a94d94e83cb120a75808ac485dfde181b8cf62990d8211c89076052fa456f59c8f4b9a4971f591d2b631e2b007f753f191249330f4f3d7e28e2bb4940794b6b3cacc1c2427ec4c35d784b9af3b35317fae848f15a21eb7041a69157f45d5a27146ff4891552240d9fcbc8a0a962f53d8223aae84b4b0ab56712aef214096d9a1186c2d3ba733d1e2b531aa73d7bcf219cd3bb865222b5aa4fa2ef6b5dae1629ae0c4cb1f45213cac0d4936a44597e339ac5147429b954dc0d556f513c12eaf8d8966ad85666dadfe27b1777386d34000000000000000000c1a5dd34f012f341a5e330650dacca59d5f1824798ba3028c142f0829bf290930050508c2f70bf684389c179c788def0fba76de3edbb241d521b169f365065314f1541cda0f5c8c0778b0ecbc5824126db46c6d977871a47e28f4e4dae00e2ffc068c974207173f81ebda1188f35607623fe1980f6eaaf64a9702fbcba01d4902e53f99dc1b21d469c7bfb2809bb5d3e76474c787f5b694abfb958ee212e55302fe38bb57703d9416670ff2100259dfa865b00", 0x1a0}, {&(0x7f0000000540)="70dda14d60338e1128994c25f29d429c2f684e4060fd1ebe1948b4e53bbb6f0dc6e521edc91e30dbcc5bd58b61373f3d23dca0c3bf62863e1ac4f015e78657043a8da44893b5cb63fa7ecdb0c2b26f9e47a1a70429", 0x55}], 0x5)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x4375, 0x9, {[0x8000010001, 0x3f7, 0xbfffffff, 0x6, 0x800000000000005, 0x7fffffffffffffff, 0x7fffffff, 0x9, 0x0, 0xc00020000000801a, 0x3ff, 0x8, 0x100000001, 0x1, 0x8, 0x0, 0x1000000000002], [0x80000001, 0x100000006, 0x8001, 0x800010000004001, 0x9, 0x4, 0xb7, 0x100000000000004, 0x0, 0x59f6], [0x40000000, 0xe77, 0x8000000000000005, 0x800000ab5, 0xf7fffffffffffffd, 0x800, 0x40000000], [0x2, 0x0, 0x40000fd7, 0xfff, 0x4e8], [{0x9, 0x5, 0x0, 0x6a17}, {0x0, 0xfffffffb, 0x0, 0xffffffffffffffff}, {0xcbc8, 0x3, 0x8, 0x9}, {0xd4df, 0x4, 0x3, 0xfffffffffffff8be}, {0x0, 0x5, 0x401, 0x409e}, {0x1ff, 0xd, 0x5}, {0x2, 0x3ff, 0x4, 0x2}, {0x808, 0x100, 0x6, 0x1ffffffffffc}], {0x0, 0x80000003, 0x2, 0x8}, {0x0, 0x2000000c, 0x4, 0x2020001}}})
sysctl$hw(&(0x7f0000000100)={0x6, 0x12}, 0x2, &(0x7f0000000b40)="5cd73027195802b41350e0485d9953385d15e05e41f3a45621eca11683343a3a7aa23c260a86d3d52b20cb00b999d96e1d43a83d863ed07d98374452252e5ca853f3b76c0d1dad4f8ecbfb72cbde7df445dad18070a78285cc86487af02e4076e1d59b20a7dffe780fa9f8cfad5ca84942a26841be2826a17fce121325074f451c89e1e37c77bd85027b20d74120b2b91cd04532384d25db6f1dc2476dc1a2d5fbac083242c610ea0bc97788eaaae119df56aec9bedadf14ba0d468494c16257cbfcb4120be9c2c41ac6c29eb23d7a29a549296bd947811cac8812bc1817c55df5821ca208bcf297ccc2af204fa0ffaf673d6648b085cced8b61ed5fbf7bda2868b173319080aa15c5", &(0x7f0000000040)=0x109, &(0x7f0000000c80)="239bf3aedd4131c766b871deffd6bf326389da9cf5e19469a50c20c9316dd0f891c5e70d91cb82e6c74bd8bc67421b5b0a2e3fecd898d0e0f7efb64c16547d68be7201e2e9d83ab2b6cc8ca5402193c30c7cf0631663e5a5ff0d838ba68822d261b222d2b92c8dcd99571748c9a62a64cb2b3d74a659745970db95f142315986bab12599eb4e864d62b02384eb1086a34d926ebd50c6d6c0fae5904fe1d3244d28f3383a4ec1483dd6b435cd5019377400f0f90e29df44707b23fe5db0f01d385db9b706d7adb4cd1045c9b7b78f60d3fd33585284cd11e57ff5840a2e6a376d6387b864bbe8d1ecdd27b6c0679c3f3379f66a313b60bd489af64d09200120f589d80e9bee41bc39542108e4e38aa1ee297c0de6ec8e7fbb9026da1d911446898849725ae66b52088d7561a5d53301015fd426c2d233dbba02790c8bd426c690781cbbe98491b29c0e96519522117f3855819aad477f06c9dfc6f8dd1c15486b80a2f656086173066f35d2356044e5c81427810be6da3cbc34bfbf8c56afd29dc3d1937cfc635a0cb5e2ec685408714b6d698c4208c4aff6c00454a4753f808000885217928cdd6cf3130d535dc6a1657b5cb8d9b22acf53689556c93255ff78e9865cfdd78a6c8d73e003e967412ea810262e125a251345013a8c9a6c7c9ca3b40803647c6df6994a517bc463c5d94cfb69576813fc69090af4c8b537a84119363dd1e2e4a57e7a27e18f52367f78a748c73f7c61764a09875112e7c77c9857b40b4502ec9a2b361c6cc89ec6251f2f5ac02cb5ec6d4b69bb741bb4cc94693952573b7d662cc76ff37aa4978f600bcc62826824e7e0432d126119c611ec1ca2d4482f77ee78476d8250a5f8b3dc47acf55b3eeb389ccb191390fe03a2303c768efb3413d669c104080d7c27cf710adccb03bd6c158ad60a8f78b52c67d2702fbd6e4b751a427666c34ca995c508038dfcf1ec687a7732dd79779fb4dc7f9902179d2f71053ff869ab80dd1f6f5cb9caa327fa03574be4dc6909d1461bc1c7e95935bf1961e17276f624bf78aaf31fe93522c5532a522ef07edba15c978813013f33a4a5295b3940be3fc118cf6e1c42092f4cfac22815f99d263e3f4253b7d561ffcd172916445b4f5108ca76284f968a0c5dc9d0fd7fab2ba176f609fe9383b61324e79dbe5a2890723483510cc7e22fbc8877de237562654084c498b05695a5711d5765a419984f1b46fd50b2db461fe22e1c056d33d8935d6e360dfa66d0f667b524578ba5588f7436d336a4319c4bb4bd0746b223c47e1499504d960282f987ed41fdc727e5a0e10ed73de60bc5b0581b93f5210c562dbb2ac6aa0e74842ff348f5c9ee7ea2124bacfff8c148ae2999a9154c42fc2d286b0c33ce42f97d9b37a3f667b1589abf5572264e8c0c3553e677c8de10d7327a4bc834b82b742072873a58e008bf21048c2fff2339ed8e5ef70d57d0f9afa2d51408f36ecee606c8662d23852eb689d9a110a68071fbc7fb8f12283c3d5b4696dcec754612cb049e8e70d936c696cafda665dabce24a8fed0e7b38e406c8641d59276b2b9225fd20009f6d1d5c9e9d43f493ba4310579e5ce5ee7fceb0ada1e962030e79e9098f5164e41bb631a34f445bfb345c2c29c190221ca292a3f460ccab290268a0dd84f3873224340d21173f22ab28b15862640562acd42241f43947baa71fc35989e00ee0cf0a0b3d8e8ea6d3b4dda3f2bb69df6edf809194079132114f4e2f3438d6a95eac807e0e47043aa492dca5d91ec58159a3b8813ebce7127481daf9bebbcaa39f5c920a55779a7f24e08d3cbcb971115ff0b5cf13e63899a3400b7fbc3b042c8d691296c8625b22b234784f28988a2dc36a856f690b1f93abe72b67485b55fdbba97309afb3526a5554976866fc012664e29b2c64b715a59e03fd62cfe389c292b3a5931d4686a3de71f6da48d42f2d6dfb629533553bc48233d50e0cfcf3f22c7936c97f949baf2eb41836dccff0bb520533efb0e0d4b40ab960eccc84d3012e7a0e2f820d417cfe1048aa708657c3e644d907ee07d623b912793cd943558db7f8d19ea0a9c23dfcd5773e1522eb150ac1d809c2b1d19593360b36ed960569e486f261d0838b6766f232a2d2c519d3f5e072a75c184c6e9ba17fa8235dc8204bbfc105846c22bf3252b416aba556a9ea8b9cb140e8645954b9a34db5f88445a239cdc76a659dd6a3b65493f68bc62fe603ba49a534c1d0b9d136c803ca7505d135b7747dae3c75848784e1e22e9d32890cc2dc866f8961bcc3a4eb86bba1d49e26364f5e4281d0e9849f43c01c9aacd2dfc4c14e9d965b9081904cbc2fddd429b44709c2e248b0523955f159a2d8df67738c02cd1d230c89647baf00d97656950ee77d7e9def50eceec02850e9ddfa6c5a0c342da30ee1e91b6ce647b37bea7e2a5431a64bedc4a287c326dec3a5599b31e1db2df10da658b2061b8e597de74a060001ca52d4ed3825daff97b9412e6c4d26d0e5c40abd8b9a0053ba12e7c5fca739d40c617af943bee374e26a17703f0f194ae5934cf2c93d8f43a552780b878d7d571a205743d9e64eabdfe809d394d26d059e14c34a5622c49b983030590b8cdcd90f278e65893b0ff3af163ef7e0cbf4733f214d98088edcfb7c62527ec0c6c87c4d97ce79168cd68a241f5ee2e01de371538bcfebe27f4c019ee3cfcf2422085d4b7c6000e000eb9b6c50dac50090fca35fc09ef2245ce90844c3f82d76d0d98803f48206a8b6c932c5a6787953f7decb07412fa3f5800c07aff4c84ff147a2e1635b1009437f4ec2220b36afc08c253bea4475eadc389acf23d1b5d2d2d2fb6070c9a13b5b71e9e3eb7f14d0ed7d569ee29e06dea79912a6846abb70a97b7ca9dd274009cdd34570da11d5ce09d5af27dce384330bbdfbbcb738f8cf6811adb9f3a7d148c8417ca465916782cc5f973f09ebb5ea7ae51c3c42e552ee5987333e89297b293c88c49761b104253a7e3438f15c962a0709e3335078368ab6a1d079df61e180eabca587a1d30b3c095d770964b2f02232bada8c83a865011bade54ed6f16173a13712428c6512b5d2df6cae920c4d1ad117c8ef66e156acfbb8057a519b35227aa83ecc36e1f6813939ad5ff78c553fc94ca250ee667b17a089463c0d8812182795c3cd244aa09a2fb508f17ef9b6a18bd3cb0bf1ffe05f630896e81d33f5c823312af372fcb01c455fd4c87e93d62aa0d621e9bd61d5a9ded4fad53cd2eff3cd68c1638f626528cab5afaa4ceb0829243ab98c2455fc54f95e075572caa727bbcee4d97f5d91930149ecdbaa014e29fbcf5d0bfee379b1e9ba616292ded9ee1054e4b4f58314be3b7f58190294e707c43ddc7fa9497600cd7352419a6b55d9e8ea7dc59d67e97699cfebb60d2b54aabe7aff4ada19de93ca87b59229bcf5ca088165b9758d7cf9d489b283639a3bb79566d3aa73bcbf1e42afb87bd52072d655e10a7805a4d93677f02b449b9f343eb156a4ba2e895fb64802b5fbf6c6056ccba0d7c6704d495b3483fb9ac7dd74f498749662b0f319c5bc3713536edffa96a1766f375bcbc5e0f060abf6a8bc2519240d3a6eb76a2927ed1fe8d7bd00997ee06a08e68f36da29a1bc9f01176b75e573d96f705757b1566f6d9713055df54ba873fcdc985a04bde01e9af19ae8b4835f569e07aa35306366fcaa5da54b3da99be4b2488b13b3662e4059440199bc808722fa691eb1f3bd39b8283540439db4db09adb7907f1880d67c9b2f241e9f6096e3a735fd953751874789df5dde741cec3b1f65dc419caaf362c7be7edfa670af25bda89fc77bd58107c73caddfc478405afd345c77e2f99c76e8a9d76b4a79e038155af13c2f90d7486a0ba67c201ffb56de458d39c66914c00b1aade7e93827a62c5a6fd54e64081ad2d28c2850152cabb623c7c3a9eba429e5a560bbf9ccae6a65d519b177c098f5dbfd91dede9fc262d97050adec63609a7ee063123f63cb82baf74d0a4d7bb3e8f9ad8a29849c7a37a693baf8f2e10c775e150dbbfe801f747eeddb78ba68c67707b82590b0d8b4894910035b5176c33ac528f07b76dd7fbdce70aa61bd923a29159516f08c8ae848858acb84d9be72e9627521486cc2ef9596d20c8dd311deded20ef9cb455a2f7abc27938e29399b742234e0ab0870eb4a461d4855f2866555c4599c57cdb6c494dab32bb386b755d920cad094e689b51dd9a77ed378af6defb782152c4533635efeb2c2dab2c860c2fe7736fa27de99faa1b85e6bcd90f0c95235d6a3b2f1b98d60a2e8eccf22edbb9e356cc8e0e57cff562ae087e1eb9b5ca4bbc6d2924fadb8e26a7af3e7efb1c43a769dc61c90f6661d07f9af643565159575a6697f0b03a2492a44966582feae83c08aff1e1d1011995787d4bdaf181fcbccf50752a0f68eacdab9a159aac9726daf744baad5e5eef7017b2d833e95ab1282288af9564888a45da6858a5e4eda2b2c018bd5ff6458b9dce27dda482b09fc68d217941535eb712e2c114bb43079d06df7ae4fa5b0cd82675cc4f2903545d1c87f1e55a4549e93d130fbbb21c6be99c5f259d854d9bffdf9641f70c0707e0f610d6e49c29363d46a2696534f2c081fdca1b48ddc170a94fc48fa2c3a10e9a25c56ab171419b85f7dfafb16c0f3b788d1157c1362177735ff6a64c2502ab5f74b143141203f7934b2a2c3807e6183067b27114c7288c312d4a6a983bf381bcfe0f3bde517e6d5abaa41bf1978f57130435d8fa1a6b130fdb90092fb6e9cd08010e9db144ac8c734611a30c061d8fec42c88eed3000a5c64c68c3161f1a0a46fff8e53eb6c38be5f47ae6890e3d9148b769568edc4d9027ff168f3003d464887b55686284f360aec74d4d55ee6c0891f0924d198b389b4d94da7e67a122c3731f5fda886042ffade86c3ae3ab5cafa6dc243f6fcd461230f3d32ed8df64adc53afb67ee3c19d5e4ac97d3edde3a9d3f431a97a3139572935e0f8f9a7322efe1719a7e60ebc57d432278a7233c7ae331d83085e1b035e60d6a8d3ec817b15b8102428e333d8f7f12a61b2f01b172a4b80c8e0fbbaf73d1920b3861a73ed6e19051ba86d44afd08775d5ae5972e482379306db1b9b7f12888e0a160f500a4148a706e610815b3311d2e5a5cfb5e8293a06edd8c9c0914a1ff115e8fc1b5bbe7db98bff1a3e81b9edc6deb9732112b6da1b0dc6abe3e2d256c659f7139df5f96e8931df1473f167232dc388ce75ffecca0912b474a33860cf627349928477058c56141177718f42358f92ee0091438499e6a541a77e6bf326fb59b346eccf61dd7d964d3c4c445d0581b5c3bd4cdcd17508553b8b7d471a573b2c46578609eca53bc6483d8f6624c85862f243aed8abfe6b0fc7f78cbf89da3b8eeb7d02680d80fb9da15d0270a1f23107a38f0e483aaf7e037e01380c97f5ea8f8022b08c9639ac8e362abd4d5a11834749d96a9ff68df5a6cf35495bba6583becfdb9382cd2493e9b1f230b538720688052ccf44c1ca19bc83fa7299d269103c496720e09e21861a1f1aea000a7fd293d8687b7ddec4f9e19c4b8cc744ccc26d873d33592e85bb0ecaacc2cab77d010719118b936729b1e7c181bb242164dc2d56833c12d06ddaf2edc9be6c0095317ca309f45d75cab9de03e4777ceba6fb1bc44ff231e7f49975f82c14b72a536eaac2442ac5e4ec5312fe3a012647ddf7e9a9c3fa9324592b5edb63dbb35206cc1001a43e47920caf5f97e1000000000000000000", 0x1000)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
r0 = socket(0x18, 0x3, 0x0)
setsockopt(0xffffffffffffff9c, 0x1029, 0x0, &(0x7f00000000c0)="03000000", 0x4)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x0, 0x0)
shmctl$IPC_SET(0x0, 0x1, 0x0)
setsockopt(r0, 0x29, 0xe, &(0x7f00000004c0)="0b652aba822bd43b69b3fe462a9ea20ff230f3db5d9a56bccfb9fe46ca049c072af64ebeac62c0727d06d28ab2f9190fc533bf17198e0d031c37ec589746a018271520", 0x43)
r1 = accept$inet(0xffffffffffffffff, &(0x7f0000000a00), &(0x7f0000000a40)=0xc)
setsockopt$sock_int(r1, 0xffff, 0x89c, &(0x7f0000000a80)=0x6, 0x4)
sendto$inet(0xffffffffffffffff, &(0x7f0000000640)="21bdb4ba44bbf9c04c744a5ef9066b302690d1104db522da39b24d46cc344770f4c3b47fb53aa2f35086a413d8c17be3326ff12848868052a842fbc02ef124f6c3dc1f1ed6bf82f8bb4935f7986626849c93a1c777bb8b53af3ca1d33a7776225fe6b41321b62ea8b5498dd873cc514cfd4b611b0cc2d4492e70c26abcf57b874518afc8b874d59634694ea02424beed4052de1ec2c2bda8f8aac7ae24", 0x9d, 0x400, &(0x7f00000001c0)={0x2, 0x1}, 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000002380)=ANY=[@ANYBLOB="01002a7747658f65320002883a4041b84ef89cf48bd82a1ea309c8272fee7b29cc6ac50a04131f588eb8899e75fc800929ba60982a7c000000000000000500000000000000f3d800ef5d203052e2ba335dfca4e154784505a4d39028326ee2492ef9a93299a62f01d744991a8ed5089e3b3229dd941292dcac6bca7dd17dea13f1e4bce7602b6c96e471da5c38d2601942a8b483f89d23564cca8d549c1af895878ee102a67df68689261f0832e708eebd23f072d84359a372f5bbd6c5b2b1b6a56c1099e97c6173176a6410f0c6ac2bfcecdbd3086227d1ffece991f39e86f01f2b3059a08fe2e843659c47624209073e6337b0390b3ebd7240c379927f2b7efd09000000000000a45040c29e56db6036594faaf8ecc645b6cdacd8076b983fa5c1c89022bab7e940b446434973e9368f572f3b36f3fd55861bc216bdfe090000007ebb1916b2b8f2790a081ad00b55304f0589fa0548871416de6dc6b4f6cb19f32af08fe3c38b6a7b5c9be4c33adfc516a38229b42044a62e26f07b0f0afd2acc453a5f83aa23ebea5d0f207da075318e613f4622a724639caf7284a533e59d394c4e5a5a6e39694e6298d55c320f71a279a0f4954885eebd621d2557c28b167eb63edd163e6e34225a099256017a8d888d99a332c2a19ac30a8bb96c52d887c67e5b2abaa60152565ea5dcab361b7c09172266b5f800ab904e8c755fd11aa8fee63da52d7a49c147881e3618711cb3cb3979588dfe26b4a231f408b67032bf5a58ae00990e0914a0f955fefc515665572750dcca2423b08728aa3e7024f6f598efe0965bf3a824bdcb5158fb2570b1c9c65f3970a9ec9d0324c61c29393af91c3cbbfd802a78071ad5918ce134b9c75eba3ad9655116940d69b9666103855aac0aeed7132f3676789818bc34895400b16bbf3e42482b2ea8b67ae70d16706341d34f0a7a3ed068b33c9376f0ffd26f89d63334bef5e59a92c1fb3d6f3ee2c65c272f5813c0d0b773c7c676bd8b3ab3b4a18b2c4deba4a0eeeee20d6cd74eba0bbb96bf6ac08dd65ce525840615568f84afb27dd1294608e9e1d5b71c950f0c58251117dc5032d115f152f72dafa852d11fa1d4987820ea92f720a5fcbdc5af1877e76e57feba3dd2302b0ecf01cec2ab304f57cafb7ff71ed2d5460996a494e5a61a706a70048a4d858d73cd7324"], 0xa)
ioctl$SPKRTONE(0xffffffffffffffff, 0x80085301, &(0x7f0000000780)={0x8})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ktrace(&(0x7f0000000140)='./file0\x00', 0x5, 0x200, 0x0)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80286987, &(0x7f0000000300))
ioctl$TIOCFLUSH(r3, 0x80047410, &(0x7f0000000180)=0x400)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{}, {}, {0x0, 0x0, 0x40}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x49}, 0x6, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0xffffffffffffffff, &(0x7f0000000080)=0xfffffffffffffff0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_GETMAP(r0, 0xc010570d, &(0x7f0000000100)={0x0, 0x0})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000001c0)=[{0x6c}, {0x64}, {0x6, 0x0, 0x0, 0xfffffffa}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
dup2(r1, r0)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
bind(r3, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r3, r2)
listen(r3, 0x0)
poll(&(0x7f0000000080)=[{r3, 0x1}], 0x1, 0x0)
writev(r2, &(0x7f00000000c0)=[{0x0}], 0x1)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
madvise(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000ff00002f0800"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup(r1)
ioctl$TIOCSETAW(r2, 0x802c7415, &(0x7f0000000080)={0x0, 0x100009, 0x8006007, 0xdfffffaf, "c09b4f524909040000000004533fc8a800"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b004c1e4a1ddb83a3f8a7dbb9d2ec6995218b61db1c120381fc8dc81a5798a0a09545a2bdeddc5b687b28231b2cf606440b04", 0x70}], 0x1)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x4d}, 0x2, 0x0, 0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x30, 0x0, 0x0, 0x0, "1fffff0326dfde0600"})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000180)=0x2)
poll(&(0x7f00000000c0)=[{r1}], 0x1, 0x0)
write(r0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x4001, 0x0)
listen(r0, 0x0)
accept$inet6(r0, 0x0, 0x0)
munmap(&(0x7f0000000000/0x3000)=nil, 0x3000)
pledge(0x0, &(0x7f0000000080)='#*-(%%.+!\xff:-*s\x00')
sysctl$kern(&(0x7f0000001180)={0x1, 0x4d}, 0x2, 0x0, 0x0, &(0x7f0000000040)="6cc72a95", 0x4)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2005, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "090000000000000fb3040000001000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x80}, {0x5c}, {0x6, 0x0, 0x0, 0x400}]})
write(r0, &(0x7f0000000280)="731f5c52643f0e37bf54dd2a1b32", 0xe)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0x6, 0x4, &(0x7f00000001c0), 0x4)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040))
sysctl$kern(&(0x7f0000000040)={0x1, 0x4f}, 0x3, 0x0, 0x0, 0x0, 0xd)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
getsockopt(r0, 0x0, 0x6d, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x7}, {0x7}, {0x6, 0x0, 0x0, 0xee}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000080)={0x4, 0x23}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
write(r0, &(0x7f0000000100)="a5", 0x1)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf81fc7b457abe24105f76e85dc0ced443"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000040)='x', 0xfffffefb}], 0x1)
syz_open_pts()
syz_open_pts()
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000280)=[{0x1d}, {0x84}, {0x4000006, 0x0, 0x0, 0x3ac}]})
pwrite(r0, &(0x7f0000000100)="0000000000420374cfb5f0b099b9", 0xe, 0x0)
sysctl$net_mpls(&(0x7f0000000040)={0x4, 0x22, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x60e, 0x0)
pwrite(r0, &(0x7f0000000100)="c8a84e", 0xff96, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x3)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000003540), 0x0, 0x0)
fcntl$setflags(r1, 0x7, 0x20000000)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x6000, 0x1104)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0xa)
kqueue()
mkdir(&(0x7f00000001c0)='./file0\x00', 0x10)
pipe(&(0x7f0000000100)={<r0=>0xffffffffffffffff})
openat$diskmap(0xffffffffffffff9c, &(0x7f0000000140), 0x80, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000180)=[{0x2d, 0x0, 0x0, 0x7fff}, {0x6c, 0x2, 0x40, 0x5}, {0x6, 0xfc, 0x0, 0xfffffffd}]})
pwrite(r2, &(0x7f0000000100)="e4ca0c4435d371af9098deec1d0c", 0xe, 0x0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x0, &(0x7f0000000140)})
pwrite(r1, &(0x7f0000000180)="f781ea80e21bb6f576c45af84a28", 0xe, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000140)=[{0x4}, {0x50}, {0x6, 0x0, 0x0, 0xcd5ef4e}]})
pwrite(r3, &(0x7f0000000180)="f781ea80e21bb6f576c45af84a28", 0xe, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0xc, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x1d}, {0x24}, {0x6, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f0000000280)="731f5c52643f0e37bf54dd2a1b32", 0xe)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000003c0)={0x1, 0x0, 0x0, {[0x20200000, 0x4]}})
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000000)='./file0\x00', r0, 0x0)
setreuid(0x0, r0)
chflags(&(0x7f0000000040)='./file0\x00', 0x10000)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = dup(r0)
kevent(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./file0\x00', 0x80002005, 0x2d94)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r0, &(0x7f0000000340)=[{0x0}], 0x1)
pwrite(r0, &(0x7f0000000080)="b7bb4a81fc762a6a169a39fd81499add968260c6f2ba729b8956683eff6070eb1453068c44ee143031f5", 0x2a, 0x6)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000101000000000000cea1fea7fef96ecfc73f97357ae26caa0416fa4f376336acf00b7804bedd45363a48fbfc781e4991f7c8df5f882b2986e1aa5b0100000001001f132e27acb5d602000d7d026ba8af63ffff072918", 0x62, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f0000000080)=[{}, {0x8}], 0x2})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwrite(r0, &(0x7f0000000580)="2ab54bb3a1ccaf3ea2c6e7580adb34f9d76e405175942952e6a9340b312d759cb9227fd043fec509b5eb2a4515685aa2fd37de29545d7f97d5afd52ed3d94dee200d617eb53eb3cb9dc0e06e53d6ed2ff9c17a1b1af5065932130caa55a86a729de703489525e1b41f8f3293d1f8c434265fc4433722dcc15f8fe3e6a361af67356649b8e2ea9d3e7753dda1fadaaa7f9ac1036d545cbe78cbd83946f798d92eefa56587dfb14e6f332d92d7c93e0b5d95f5d47123d6f85150367a2bbb85fe71576ae1cfd490baf24672f02e7514a6a532898bf1e7ed4a4a563ebebae3f35ad4c5cef01df83b821ac7539d29ce2f600c3db8ede11a4ef73b4e1c637cd3f3bf946e0f3a30e7c20cae6c5246bf7ef30cf9473133c82127cd0d2ebe81c0e8ea80c42d77f9c31c50cef212a198110c5c46f8d560615f654c07fbd14517bca388c23d1bda1507d705d3d423c3751991efee1663381cf2d0be21aba60129dc5f721b119994a27e8d816af60e0e42737e2ecf", 0x16f, 0x0)
execve(0x0, 0x0, 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000100)='c\x00')
r1 = dup(r0)
mkdirat(r1, &(0x7f0000000140)='./file0\x00', 0x0)
execve(&(0x7f0000000340)='./file0/file0\x00', 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8040691a, &(0x7f0000000300))
open$dir(&(0x7f0000001240)='./file0\x00', 0x0, 0x0)
socket$unix(0x1, 0x0, 0x0)
setreuid(0xee00, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0, 0x0)
r0 = getuid()
seteuid(r0)
r1 = getpgrp()
ktrace(0x0, 0x93a96fddd37e5095, 0x40000008, r1)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x0, 0xf4b, 0x1, 0x1fc80d88, "24180400000d00e3000009000000000800008000"})
writev(r0, &(0x7f0000002900)=[{&(0x7f0000000900)="462c6f356e75bb87756dbb240d0d", 0xe}], 0x1)
socket$inet6(0x1e, 0x0, 0x0)
socket(0x1e, 0x3, 0x0)
r0 = socket$unix(0x1e, 0x3, 0x0)
close(r0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x8060693c, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x6, 0x2, &(0x7f0000000040)="cf30f818", 0x4)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
ioctl$TIOCSETA(0xffffffffffffffff, 0xc2d0422a, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "0400"})
socket(0x2, 0x2, 0x0)
socket$inet(0x2, 0x2, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=""/48, 0x30}, 0x840)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x28}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x10000, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000240)={0x3, &(0x7f0000000400)=[{}, {0x1c}, {0x6, 0x0, 0x0, 0xfffff3b2}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000100)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaabb86dd65c2128000145cb3fe7302000000000000000000000000bbff"])
r0 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d770c167e400009e00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r1 = socket(0x800000018, 0x1, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000080)="ebaf0900ed0e25baff6e", 0xa, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000000)="ab", 0x1)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x4)
execve(0x0, 0x0, 0x0)
openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
syz_emit_ethernet(0x3a, &(0x7f0000000400)={@local, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}, {[@ssrr={0x89, 0x3}]}}, @icmp=@timestamp_reply}}}})
r0 = socket(0x2, 0x2, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x4}], 0x1, 0x0)
connect$unix(r0, &(0x7f0000000080)=ANY=[@ANYBLOB="7b022e"], 0x10)
r1 = socket(0x10000000002, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f00000000c0)=0x3, 0x4)
shutdown(r1, 0x0)
pipe2(&(0x7f0000000bc0)={0xffffffffffffffff, <r0=>0xffffffffffffffff}, 0x0)
fcntl$setflags(r0, 0x2, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000dbddb97b00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
close(r1)
sysctl$kern(&(0x7f0000000000)={0x1, 0x58}, 0x2, &(0x7f0000000080)="e543dda9", &(0x7f0000000180)=0x4, &(0x7f00000001c0), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x2, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x30}, {0x35}, {0x6, 0x0, 0x0, 0xfffffffe}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000040)="a54d65f9e1e0a3f6084cfaa0af44", 0xe)
sysctl$net_inet_etherip(&(0x7f00000002c0), 0x4, &(0x7f0000000300), 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x7}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@empty, @random="fc00400000ca", [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @local, @rand_addr, @remote, @remote={0xac, 0x14, 0x0}}}}})
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
r2 = dup2(r0, r1)
setsockopt$inet6_MRT6_ADD_MIF(r2, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000140)={0x3f, 0x0, 0x0, 0x1}, 0x9)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0xe5)
sysctl$kern(&(0x7f0000000040)={0x1, 0x15}, 0x2, &(0x7f0000000080)="19a52a524ba0d7755d1f72c8d2a73b77", &(0x7f0000000140)=0x10, &(0x7f0000000180), 0x0)
syz_emit_ethernet(0x2e, &(0x7f0000000080)={@broadcast, @random="00fe0000ac3a", [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @empty, @empty, {[@rr={0x7, 0x3}]}}, @generic="0a26a1f084640065"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x291, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000100)=[{0x28}, {0x6c}, {0x6, 0x0, 0x0, 0xfffffff7}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="34cf362b3ce9c93d7f0000008e70", 0xe)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x33, 0x0, 0x42)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000003400)=0x6)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)="01", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080), 0x21)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
sysctl$net_inet_ip(&(0x7f0000000080)={0x4, 0x11}, 0x4, 0x0, 0x0, 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0x0, 0xffffffffffffffff})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0x80000000000000d, &(0x7f0000000040), 0x14)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x26, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x6000, 0x0)
chflagsat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x14}, {0x45}, {0x6, 0x0, 0x0, 0x801fff7a}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000240)="0c183256e5d425cb6e96d99d0279", 0xe)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0x800000000000009, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x81, 0x0)
pread(r0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2005, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0xfffffffe, 0x0, "090000000000000fb3040000001000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
poll(&(0x7f0000000000)=[{}, {r0}], 0x2, 0x0)
poll(&(0x7f00000000c0)=[{}, {r0}], 0x2, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
connect$inet6(r0, &(0x7f0000000140)={0x18, 0x1}, 0xc)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, &(0x7f0000000040), 0x0, 0x0, 0x0, 0x0)
close(r2)
accept$unix(r1, 0x0, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x1, &(0x7f0000001740)=0x4, 0x4)
recvfrom$unix(r2, 0x0, 0x0, 0x1, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xfffffffe, "000000000000000001000000000000f843d07e38"})
writev(r0, &(0x7f0000000140)=[{&(0x7f00000009c0)="0dcf67520990982c582dd6057e8d1069ce86b4854475cd2ad14516872e5fb8b07102f1f8b3c02aaeb9b32f1461a2fe9cafe8f3a3458b93e5afccc490dc2a968bc43140c8eb7b5882a12fa011c9518219d170f2aebe4c5aed28582ee61ecb44ebd70dda7c805d54b845bda2697bbe578a17dd1ba474c02852258308051c76954c554a8f160341004ad9366eef994b32816ad415fa4545f30fc1575ac47d55fac57bf6ce2647f4518900e0a180172a2ec178b3270f55fc12b1058c5d6bea888cae054af3f59fa44f362c443bcc0bb6808f08f9cab604d01914dc3dba9a140ef0c3fe06caebd3dc7e", 0xe7}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
r3 = socket(0x2, 0x1, 0x0)
r4 = dup(r3)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
socket$inet(0x2, 0x0, 0x0)
ioctl$TIOCFLUSH(r2, 0xc0206937, &(0x7f0000000300))
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x22}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r2=>0xffffffffffffffff})
recvmmsg(r2, &(0x7f0000001e40)={0x0}, 0x10, 0x0, &(0x7f0000001e80))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x5c}, {0x4d}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x5, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3201)
open$dir(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x880090d6, 0x0, "d700060000000000005b00000000000000e74de4"})
write(r0, &(0x7f0000000040), 0xfffffec2)
syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
recvfrom$inet(0xffffffffffffff9c, &(0x7f0000000000)=""/166, 0xa6, 0x0, &(0x7f00000000c0)={0x2, 0x3}, 0xc)
socket(0x1, 0x1, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000580)={0xfffffffe, 0x40, {[0x401, 0xf8, 0x7, 0x0, 0x0, 0xfffffffffffffffe, 0x0, 0x0, 0x10000000, 0x101, 0x0, 0x0, 0x9d], [0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x8000000000000000, 0x3f, 0x7], [0x4, 0x0, 0x7fff], [0x0, 0x3f, 0xfffffffffffffffc, 0x17, 0x7f, 0x1], [{0xfffd, 0xa, 0xf6}, {0x0, 0x4}, {0x0, 0xffffffff, 0x291, 0x2}, {0x0, 0x0, 0x100000}, {0x0, 0x0, 0xfffffdfc, 0xffffffff}, {0x0, 0x1, 0x0, 0x22}, {0x400, 0x1}, {0x0, 0x0, 0x0, 0x80000001}], {0x0, 0xfffffffd, 0x1, 0x2000474}, {0xaf8f, 0xffffffff, 0x0, 0x2}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x0, &(0x7f0000000200)})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x100, 0x0)
ioctl$BIOCPROMISC(r0, 0x20004269)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000240)=[{0x87}, {0x5}, {0x4406}]})
fcntl$dupfd(0xffffffffffffffff, 0xa, r1)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206979, &(0x7f0000000300))
r4 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r4, 0x0, 0x7, &(0x7f0000000100), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x2}, {0x34, 0x0, 0x0, 0x3}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r0, &(0x7f0000000040)="d0eb14522e79aaf2f8e9c2624a3d", 0xe, 0x0)
socket(0x11, 0x0, 0x0)
ioctl$VMM_IOC_INTR(0xffffffffffffffff, 0xc0104451, 0x0)
sysctl$net_inet_tcp(&(0x7f00000002c0)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000040)="0102ffac02000926feba70a7cf09f6360f9ea14f040200040000000097069815ca5835b6f65316127c991ab43afd5604c4aa10930ed14b01017f00414191ac6193bb09919a8a372208b127f29c66755d45d5ae11c6731aede78c4421cef62cac7d5ecbf98930edfc29b2e7910599897b40c8c7f4766c3bd966a5c0001d0be46ebb59a63fde26f5ca6a157ad13900ffe6c35b55a191701155a29aec01000000000000007e59a59a05bb68baab118ea421c7324f8145857673b4a15e8909d64f925211d0cc21b9164c249a44e821fa95d881a9bbfb910093d1fbe686246fa85c22ad066d2bee08f7397cfe2cae6e966e98d4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac7354745175bd902a5f48e0a013a1dc24244ade0d510672f477da2c8fffff0000000000000900000047000000001cd900cdab5ba5718bb8720265075b49777b822443cd2740e953a80000000000000000000000000000000009eb3881885647e6acb546c745052eec5503c733fc217eb57458e55df302e2d611ae3e030100a9edbd2d2d845b8e1f2e111835a6b76cd5ff5256df19b5634e2811d910faf269e55e7412e235a9072a43575893f400c7c32ed7a1d4df0000000000000292ebe97f33016e63553689b1e8a46145fc7f2c30c0d29de0ef7c54ed5b402b93815e8214f857ebd1f1e41bfb9a21624824a96d9619e00feb108d5bb62a27d465014bd7652b", &(0x7f0000000000)=0x210, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x0, &(0x7f0000000000)={0x18, 0x3}, 0xc)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x5}, 0x4, &(0x7f0000000080)="11f75e19", &(0x7f0000001080)=0x4, &(0x7f00000010c0), 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$setstatus(r0, 0xa, 0xe3)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
writev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000380)="4247d34e7f25e68f4879e8c7650a4e2e83025b29c6b032b724f4937d717f3100449fe452df82affe3556d463ed20cf8b2617a0581464d917511d76fae2ebd0b63c2defde02bf7ea03a27920175330763a4d20dc0aa9f7de3679b2f492c6d35741c9d8e6edc21342a7036265a9cc5a5354f9611d7fe76b6018c6d666e1a609e7cfd5bbf8d8e7b0bc370cf03c2b134d0c7fbf5b96d3b9598f2885d815d6caaf66915cb4df6e65ed249fb6a3410dd6a06d778631f282d4b8410ac416f600acd00b122a33f10fecd90cde48574fc01a2fa0538047ccf9dde27f361115c9b3e7a0c42445a97600744c010aec8896d3dcb7b6024e623ee4b53b8b7d1f11cd564eeac9b443d2c3f32e40bf7f3603bc39404ade5d8e35f4b837366173ce2ce0c5e21d4f3f64ec519293d378a91613ee2b01c60b956525e25730afb1700794e46c5b36817b25388bc2db6e041f02ffce17a47ddb40a9083d53969e091b86509f37c3084e1861ff90089fa11024040a8f971cbad176ffa5c4a6134f4dbdeb8b1b084228ed7db34613724d526fe5d63f1557eb10abffae01f9d595d7ce337959f63fb5f32c05f8cd523387d71952c791148d1daf450efc822846874d53400532d854bd4926ca989ee35856d86f0cd0d18a4a06fcd4cbfdb75c345f4f903b82d9361182cb7fdda3a803f1d27b835053eff74c4d71717e607033f56e30594019affac240af57696dccf0a88153b820dfcbf69f41f45e9a23f9bc08c8f6aef94b701a236b99b57080cd04361e3176487fa01f31ab6d49f9032412d35f4ba75d2afdf8eeebd34036f455710ab1347740aa0a35c002faaf632daa304902e58e64eae9cce6d22481ffcdddf03fb1ff8290ba02d6efdcb5e08e3e149bfb0bbb236f7d3be1978a3cbf2b7544c3bcd11332931b21c9ed153aeb5ee7b8d759cfded771bd45dd3404e6747eceabe215dd8feb70bca97159602c3d6926d4c80c2b660d9315bc23eb6074e11ee7d36ffa69c9f456b27aec94bf00ae203bb61edac3c6e135d337c32043eebcaa77f69033f51b869d2bbef4495e8f4b3f394a8bbd4b61a4093879b6b1fbb412de233fde99e292c00ff36abdd162ae26edab604fc550c25e7c37c5cca37f5d1678de1509a8e1d5c4e7508b4024531bfd35b85db6e2288d67876e395406e27f918fa27924872f504693d5b1d1c5a6a1d58e99233a26463ca19652775074aaccbef345e1ad6e99c254c468a7231d31a0720025b2962776137350bd9ac8b8fd2be5556a9fb73fde846449735b66e35d95c22451579c046c82834a37ff9e251396b61087638ba2c7d3fdda718b5d71b1e0271d770bff88b3e69b61d14bd13451ae34af68c6ed57bd4b8a11ad9543409631ebe0b4ac898349a777dfe1cd832ecdac8e61764f285c2d23acc764f89fae30ffde6aac71cc361bb34f0fe721eeba3c7a8533fc9b7e038454c46b9a4d06d46140b30f605b8665e735e2d542cbcf6b1a8968bf8c9ec8a6adcfcf2f5db20fc390541904302a5958f8820147b086acc2ffc2f9a0edbc4f995dabedcb675585a17863ee020fd67e92b778906043c0841274fe0b434b99cbbb79fe5d71525916a278f966d2ed01fa765538cb904472ca9eb6e72102dd39f8a458ec6bf2f0dcdc74264714e8797d73067b75d19f476c3a52c69dccb679c2624d38d3d33d446dbf911fc2f9d3bac7bc9e884638d8c43e623f9643dfe4eb674ba17846774574a83bbdfa423c72b3f4c1f912d0aaf75d8f2cdd7d555d512588695a967c4a9301abba9859b8fb662a4d98e82af4956a0d323ca494a56ead069e97a9b5f7240ba9588b4ee679e3bc18b497996991f7da17ce286749426faae2d045e03fdb9ca5502f507e7bb8b9a4659b698c6d8ef5a26710e253b25d0e90fad06e4e647f1f3d06b4dffb423b0232eb69799afb01a516352e1781658b320141ee705135878883caf191e6b09bebb6b39b6f2c8dc87a95472efc95ff8f7234f7f6badc4c2a102c2b84a71daaa41ce950841c1131c073ca2c54d3bee3b7c6b761adf49256b219a7511921d0f49c2c65e9b3e8a889ea6a25b4a4e26a6dceb64fc558264660ed1aa17fe74ba3e0085405a7719eb1c4fb9734e04d5138e9df9797126b24907d872a1e42c4628a634f79beb8b18cc5ed29b672929fc0de2f1fc982d9a5e050bc28df16e56ab3fe84cbb2e5b906174b805168f5578a9cdcfd2b194c82f61c44e4f3a3957f6b0ebea9352324576c538f87a7ab8e72d34e2e2afa1a2fe39de796a01a565aaaf6cf135390b7bb12c70679f1b37f066ef56f0c75d77efece696cfdffcf6d4f534bf278d2189a925cc8445787285ccb4f9a5cbc70294b68457c320cec5cf7c84746e29cea0893a26f415fa35e4d71c0b2c84796e0a454faa5a01d79bd1f8beb2fb2064c12c1b9c68dd040bc1af7225d77d52b96df0fef780fb57f6296d76bcc7ecb475b53c4a1a341bfde7b7f3c8d5f244a85f28b010b213fa015e7ae20ee9801ccb1777ffec811048571a0164f16617a8d1729283005f3711b0ec5a9b056fad05220e09128e3844a864478a6f5019345579a5a3254b0ae5d04a79f81f7a6ddad8348872f299ddd4144ef2f258ffb01149c487c14069658acb0c6f9fbc24abd2a2d7ba7d71650346d8c86128b4574639cd79b53c8c24dbe49aa60cc6125cf285eea444586599387b36602d69fe7a9850d8ba34bcb31c38d62a2a8742323cdbceaa018c87529cdb103aef8af9bd20f5ee929aaa4e9e8999e72dd8fb4127d8a788582c399c1e2d740641f3b187b6235c10937336bd754d30bb4fe318633938afd4f19bbec32bab693036ed1fd3b016ca2eb7b6136eac609999a0c9c1a958f58a8d3cb6830a6a7ea6ba55bc147a811fb1aecd57b953f46b639bf0917d8622ff6678aac5e0bc54495efb759c388a013d60b9fb2bc8ebf5778a8ec60dca565c196818a80fb7a52866a581febb67c8b293c871afecc2266c6c0b04865e56ca64fae3dc58726abcb8db54f1a3986a413b234dd4cfb0baab7909939ea5ded323b5a5c69ce40c6101f8ba7f6c450f5f11dda2b16b2fbc741609f96268dcfbae7da437d18f00c4a5c80fe561af9c632302bdea2309038de33c29f59b15eeb35a8b9e23caa45a84cfe459193ac55794781779173e96df49f397140e1a5a9c11d2ad7e8a1890d0ba0058471e73fc3f955c8ae905eda87abd63c058e22fc1f456bbe5a22d9b3e9de184b3f8e7878eae9affa5807500b712b5248d4fba2800bb2d57e6733fd1adcf3219e66f91d440a0fb522df848d7e3ca6433d0bb4bc959536cc6a600bd054d6e71e105360faba68baec5bd1e310bacc1083868b6db7aa0835d3649d79b13e43dc763c212ed7d535ba160bbbccb128cadcb04eebc181266a18ff20546aad053f7debacd39b24d3c03ed694374cfda1c1589b0874c039f50b7be087e8c4a7c8a9fb72e66b0e7cd76b7cfed077c54491b538885e631746f2dbb0c03df2668bdc6e0bd0a55a22d3b73dd50dc0fe3e45c981e0fc15b0835fdd56c645b8a736c9d2c3b24a259314c8951bbca7ad12817dbf14b0477b4747ff59d27c7d88ec6c4d497aa0745b65d33f26ab17e4f7905981b2dd9472f223d91ceb466aadc267e28fe59e34c5243bbc4c06af46cb50e09cf4f642105f08e339f4c2323b85e8c65a71cc97f89275e7c257cff46723f77bdcaebbd0381e08e65cfa84c6f4c571db3a9bf47624412a2e65c95d05c98d8b769884ae13cf61b582d75896de5d92783bb2243bc518e8759371a138803da19f443a764cc7fc0c9af44c4401449ce44c55cdce8822bcd2fd9eef2849e0c1e87bb1254bc2c7662de9ad3bceb54d50d9a52026031a18dbe443dc1866513c5254d3e03743d8c769b68845e35cce152b07492b5a98141a58010ba1688a143e5cb76392c24d7cce8af3bab5293b5664c6a481e09f9a6dbb5f1cd8699d62e92014ac99678e9cdd7248d436eabf848e11e0b0aee9dcf3d75b2a82b893c78572649dfc2563ebcbfdda3d7e36652d61f5d7bc71a34992260201f215e37ada79c8cb97f42489252509686e24a7f41abe670560258e1a45809889e8735d875ed5e57e66ef2d893b460d2fd87d1625c0f5d51fdb47e4bf63a35191456dd8ae901b6296938061fcb92060e20855e43a5017366f4c9801ab149f4d23d60c5ffbef06bcbc20e8c62f76889a98a6435e6b4689208fb42685e56", 0xba6}], 0x1)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x2)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000100)={0x0, 0x1, 0x69f, 0x1fc80d8b, "20de4000000800400000000500"})
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000140)=' ', 0x1)
clock_gettime(0x4, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x6c}, {0x80}, {0x6, 0x0, 0x0, 0xffffe080}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "c99e0400"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001c00)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x2}, 0x8, 0x0, 0x0, 0x0, 0x58}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x2, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x20000002a, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x1a, 0x98})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f00000024c0)=[{&(0x7f0000000080)="ff7072043501d844c80e48d6c2c4f872bb1bed62493cc93a5788ab57db2013", 0x1f}], 0x1)
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
poll(&(0x7f0000000040)=[{r0, 0x1d5}], 0x1, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1004, &(0x7f0000000000), 0x4)
sysctl$net_inet_ipip(&(0x7f0000000000), 0x6, &(0x7f0000000040), 0x0, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000040)=[{0x84}, {0x7}, {0x6, 0x0, 0x0, 0x7ff}]})
pwrite(r1, &(0x7f0000000180)="0ba1ca0cb64c8023cb5de762d8ff", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc1206949, &(0x7f0000000300))
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000000)={0x0, 0x0, 0x0, 0xfffffffc, "f6ae000000000400"})
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={<r0=>0xffffffffffffffff})
sendto$unix(r0, &(0x7f0000000240)="d1201d9c07b00a16a86b937d24a7f2947be77f1cbe061ab59b4c433ac24bff05792c3b2d50e2f6", 0x27, 0x40f, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000680)={0x0, 0x0, &(0x7f00000005c0)=[{&(0x7f0000000040)=""/120, 0x78}], 0x1, 0x0}, 0x0)
mkdir(&(0x7f0000000100)='./file0/\x00', 0x0)
pipe(&(0x7f00000000c0))
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000180), 0x20, 0x0)
bind(r1, &(0x7f00000001c0)=@un=@abs={0x0, 0x0, 0x2}, 0x8)
r2 = openat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0, 0x0)
symlinkat(&(0x7f0000000040)='./file0/', r2, &(0x7f0000d06ff8)='./file0\x00')
openat(r2, &(0x7f0000000140)='./file0\x00', 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x25}, 0x4, &(0x7f0000000040)="311d000000000000", &(0x7f00000000c0)=0x10026, 0x0, 0x0)
r0 = semget$private(0x0, 0x0, 0x0)
semctl$SETVAL(r0, 0xc977eba63e433206, 0x8, &(0x7f0000000080)=0x84fd)
semop(r0, &(0x7f0000000140)=[{0x1, 0x7, 0x2000}, {0x0, 0x200}, {0x3, 0x6}, {0x2, 0x6, 0x1800}], 0x4)
r1 = msgget$private(0x0, 0x2)
sysctl$kern(&(0x7f0000000180)={0x1, 0x18}, 0x2, &(0x7f00000018c0)="6222fd9d5533db610709f8769c623e612c8418fb1e84c9a7cdf9b0fbb374f0691aa36c2cac5300500f8d7e3789bfae6c9c207c08409f1049fb09ae85c7a6a70e3f3d8bc3e357f1fa5b3136dfbc38c5dc2815df510e43f5f53bf7c7288a2b3b9f07d5d27d8540bbbd5d17eff1d3ce0daa9940e7bc4d77b873a2be89ece2f6d9478adeb25d7f089dc6e9199d1ee6617f14ecf70c2637ce6ebbe0169714d61761b0f7610d758d172a78906bfe42beae4634dc42cf438b41a64215cdea894513c66486bce4283de339138eabef5b1edab40931b5c8a1db29ddead3b641ab778d5925209a3cb547f54a9f3076f093c52b2c288aa571b562acb39024007cec857a9bc38a65ba7c3a992a1fd3a3bfaa17efbbb54b4bc75add5db977510e882025118b51b761793761df84afe7493f5faf4b32247b1f252e45422d13d475dd2da6a5437281936fbd62db8bfdfcef672d85ca269b07ed7b67c2330077353e1586933d883bbd9660a129d3031fa359e879201ac9f5e9bc7fde75ebac85d1948efa859a52b19d58fb7a26c7b4196b2b69a7395bfe3b5e7da8fb07ca10aaded6d4f6a796fd614312ed00bab2947e14bcf6ff3512e796cd1b2ba4d0818792b87847e3a860b2bd05f50215a1bb7f25af8f9ca08c727b6726b9287b276af15860724b3b4432f833ac2ad6a90bf3abde8a4ad6dd0f402c76b30924317088c23db7cf9bcaf2af419a1994921ec389e06276df215a76b7e9a7e3b8ea244968594861dcacf97dd6c97ca0b5d35e7f5ee6db5c51dd635b801d0d4f9ce33ac48a980e9972ad27de98c52422a4df93974715bc90c2d18313ab30482d932e460cfdeab33fb302cc4c992d89be058bd09b9e9408802db4b2e9d29fb5bfd5927f26f37bd01954559cee1184e49855e92c32763bbc124fcb18ba3307eed219613b193611af221ab0304ee0693d79ea418c52f8c56bdb15b5912e6c7226772363fc43b0a8d076dbe5812b71b1ee6b9f99b0e58c3fe4a9a7691bb2b50a4bfdecb2508c592f22e4397b7f9765b90645d817a883c530da72fe90319dc346e52ab8500e7779ae8cf4215eadf471f124d8c17eb2489b0153a9b6115a7939e41ddce8d71d5f6acbf6306168676e3d1b6f63c7e12808837be7f35137cbd331dffa316957ae875d49ba919efc54f0b5a63da78b522b300f29c9848a0673530bc050c9945a413f4ad739bd0caf57fdae6fbbaee70f68df361dbd57ff5d19bc084f79d5855f692e5415e616094edac375245617380cf35eaaa2621a58b404e721360003be842e6e5f891031f07d72bdfda6898141d7c317a5340e9f8a4836fb757c712af45104bf5370faa9f19b49cfb7a72fbcd7c58dcf46cf4b295e55069cf5669babeb7e0e9c9111107938a2eeb60467a8a3f967d91741729a4d4de1b4db95a01883edd41e7293700fb046f437c79a258af717b90bc10cf8c348eb2193bfdb07be3f03654f34cf2db9cbf099baba2bf7e913dffe78d9df8f7a85cd01f5d9e9a442298b420892f9dbdc5a3e8c7bfdcb648b25081262a1333cd404677b0c951d2c5946ccbdc3b3f34de1981f54f92394a5dae45cb31eca20ad4e18df67250ee6555274990b59d1397f7f9903f8daef8584f02fa8e4dec7152f7237f36caa5be08273f2fb73d17df6b83bd5c880929dad4558a4e2511b75841c1ad30273901eee46d41cb088e3c1015973c1a25694d028ff3b76b6c5eca2545bf0275ea4851c6e6d67044e56eb804a17e99f7ffa431720d9fff19417b084d84d245f45505c22bdf339c10535801029f195b4fe7d309544d31a02c57a261738ff4c2466d823a439c19cae5b14fdbb69dc6e9f2e6d4d0571ca735ed8b44adc73b4d761340b4b70c576f5d6c5308bd2080273b4014c985a93f0e91b4133c6cec2d44ddbea70293f44b18e4476178adc50bf31f66314707d8641051ac57f055eb546f697aa79ce3307b182c3bb7a13889517deaf8146d6622a56b91a55c114dc4d2172d0d1cecb0ccf80b65986566c3c11d0c01034485c60011e2aff4526a4e0a7c03dc5db3bd39d5efd0fedfe2391664cbdd9278259713e8da9f5bb2c18671adebfb0721ebb129d1fa2d4c1d61ff441b83a72fa763867fa40c484a01142b49622ad32415384e2644e21ddbd75c2860f0ee87e14dcb70e59c947757b99c84c264437911ffc3579a612b6fe0e54c9397a8732a83ec0b0a7db4c697d7473e8e3448bb506cdf4340a436b0c755edce751dbed832e0f0323c250d76d1aee3d9f48cc45a350c196b6272efbac73d7f05fa65a3ed18594cf2237c464cfa8a6fd81d896d414081c17f7d44c844580088015b3d58b6f11e7316434f92af1c562c13454f218109cabb07620c0066d80455d5e530eb72a7bb15d4ccc97705ba5be6ce9f89f78e5d4288c40dd798c5b65f6bc08d9972e88585c1ad55e674338e16ba01deeb2149bd6b88fa8685894e01d8ec785eaa1995d59b46af9ab98220fd17da5b4e709702a7265662193487cb4b77f14e2bfab4f5d82fac2e6218fa9a7c5bf0f91380a2c28bb80886dd6fca09d6425047c894e7fb8f833769105a2a54d2db4d4406b8ed5785a07e245cc1548b46dcccb5f692e5c745585c7384c2b1c6bfe09768c6b292912e3736060458043112f1f23beaf71beda574e3ec74c6c0115ae39511677df598bf6ec6cf2c67185017e03bafb3d427342b3368a65fc4516f7a359bdcbf1268962d046f37dd4f9701fc78a924c065cf58cc59ef749d1b3b0f089c179232c16e205056521878c1148ff9f1cd6f7c5495fa9dc373dfdbd64bb92762a75c2668fdd242003d6e8086519c5fb9de6b3a583c9f24a58ff650a93ae4dfa724fdb6237c20c2e2a8336057a18d86958d312f0f8d9313ba7adc3819567a7b3fca5b8c3666b8c235e3595847f8cb994c4e3a1f785c705503edaf9ab63a69558903e6c97680d302a17ef892f2544790e03d3c88124c94872d0404a400cd9cc547f0e7ff68bdb8eb6a909a99c9a4cf92973ccfc9c7a11a727c02b6eb90fc7b88dc8e870e4f3a84ffa5483b86d5e22ff2402028b91ff1102c9b69c13cd13da0375f2ec9e3fc8f77910a86a94a868528b567f400ea7dfa9b772d469b5cdac668b0e042ada184cff454d5c541e77cc6024c77921207d6be86899d488594cf0e1f1a69d9dae8d995e67c6f32f114942aa3b9bf6402997f2feef0b52670ae13257de28bcd13aa6be94669dfacf1f2611002f9357d0a3e919613dbd8c7750ce76d6be97d541ccb269deae5ac8e78b3c0ef0cc9d70057f4ff1efc4e93d3394fcc7b76e2434e1d1540c209ebedfe643513f44c3232016280d31c666e495119a21513416f7b4b8bad2f24f3badd9f2d492bcc0904b6961ce2b45e95d89788f2b38464112b938040407c8a3cd28af2d026b5aac2cdd914572be64aba08370e6895a12ab50b4f9436e1967786b0aabfb682815c748d0405255cc9b3d4cffd7e0b6c0c5d35095cc0cac37d0f18d2bd234af3510bd95ab32c3d94bd72657938c23a08b78e20e5d199098ffd7f6de5406c74fcad1f15ff46198daf001dc119d50592b575f3a4d9558c1314f2155cf36fecf3c0352521e69ba08d8969b4a59049444a0d3d60289f00c7bfdb9cc1f66a3df130361167675da6dcc06161112bae697c5a5f0f8a066f713336c0f2d1610c7bf253bfa168db30b0be887551dfe6af6be45f0d6008d3e3673fb7860cf3df49168cc16153a2a9e6ecbc6a9b3ac248ae35b9c7369fa63748df121253597d10dbcbf0d13aac5ffd5d79cdafc89cecef30b03a8d86511ef05bc1cfddcdbcb94d6b872f915ae9f8cba5387c2c592e2afeb3c4af53b7502e21cc7dfc048e0a729f3f509817d510cbefdc461c9a8264977184d5740eebefcc94a5d93a2a8ccf42621ac174490ffad78071160a4219710d9c094f1199562a4d56651e0ffd7a3e77aecba07e50193aaeb91cdd5676810f9d9c7a3a0b39de86add61239394212496e198b559b3bb6d33fbc902112df868a135b83b2c385ff343071c12781c5aa07478d8ae15a90ee89a333b5d373cca5e0d1d21632fca076e51f4b56fdc458e45e9849c65c5de6b02d62f21960716e592c10dfb8008b89a8debf8c274b0977812925b7e53cbe7d8825ac601e593717117255cba4aef530adb6233240ab96d2e09a96d8dd3f146fa18a9d51325d3440a0da132786cc90d1b67e3b5836e0c4294b8e11e5cc42e1f88f836932e8529adec8dca05b36b8534bac3921e7835c966d071ca16a0c87a59ac486b28a7dba306600c7894a99193a19fa2aafe2c0055cebe88107ce36b905a42ab2aeda6a7a335cb72e03479c49bbea5c667fa815ed9819e3902860499d84fe8aea5ff8893e66ce0f84bee7524d9fa3e33504259e393aa2cd05f8089bf027b0231e5a3fc7dab520c5b7c8616290740006e23e8c9941e698cebb3dc5320326bce59c0705d6f78b4dc2a90438930a966b24204601f2e1104d0b1b128b1da940c1ab10361d33040b6ad89244d41161970d7d122c0be1518ff809f2d35247a702b4e2168163e2ff340c34c0714634b18e21cfba6e80b86204ca5da4b96c4e038e023559181244e35b5f641dc6c02b860a289825cab2020a64ee3835ed7c1727998df7fc268f51410c5ef9f3fbd22a37153bfbd3770e5c3d97fe12ca04e4d1b8ba84f3e2f6551b4c21a60a137fbb92e61cccf458d46b48567ec16739cd32a8345180fb28564bfe22782a8e8b01bd35cdac60ee577f05d1c358f39d41f6c9a8c6969f9ecac5754b988b4423da920147bd998dda7064bf4dc7c83f6f4d669401e0e09d4ab59b4b69efb877985c57b80f3d3cf5438372193f1049ad9720c28f8e3e464d4e53ece63315b0a6d72e66a64dd13f3005db81f744b4c9b562202fdfb57bf285972045e1ef552e35e8ebc4f99534578999f151e861b758286acd0a379292983e4f32ac77e129d05de66e74617cd99d26d0f4d53f6d4e6adc7889c5bbfcd9f3924e3495e90b391186608e01922b1c5847342b3e773ae823ece990ccd9113e96b6c69924b95b907e38d835f19ba1629a61033f4b2d45ef657d341c094bfe84ee9c506d8bccbdd335379e179503fafb7ffdca2d0fd9a8849f7f372ff50e34db6860f790e1ecc0023969bb269c8fd2d2e080ca4ead857aa87463d52b98d8ba5d51b430894acf3981248f5d629502ddbdcb4d03ba6da128f209ac07c0bc9145843bc7efa38c0b57a5de5932461814cd1dd2298242897b1f6db88dacdf2702967581625e500b59d1f9e59f7d3a818260398c0967c5acdf33c5edfcf126a106fcbbf13d15a0f738effd051ad16ef5ad42290dbc6e9534af58f176db7908f9821abd9b38371702183378b9437ecd5de71d83e770a47a40774a9b312b7933e049392dc14a5dc58f11666ef458bd6ad7018a9f6066612519ed1b605adbdd91923faf3f64cfca1b518010913ec98afadc24c1f8ad76c21b1f8dc722c16d7d7b83ac2b947977f9e0ac612aa4e3996333021f454a1764447eaf4e1a58005c23b01b6e489d67d473b093c8c1ac5e8e8744d73bced71a560d14e49325a8429bc92aa53d372f4ec66569f65bd3a12c8837197df26ef1f51a2f5ea95dcea0997b9b390fb0eb63bd60f447b7d5039ab1dc583ed07eb02ff36bd2b31f59119a2944ece3ce837340d6c6bf031106ceec4d864948deb04429b134c097ca3659399a9c4b93fe83db88764c104b7b77b519a84df9efaf375349fd9113c90e56cd0d2e2b63ae9d135300097e90fa6c2fcc78c11a074e2d757d90a76b3bbfbde0d8b", &(0x7f00000001c0)=0x1000, &(0x7f0000000200)="7a269a422ddbfa91dad0b16ebce413a51f31f093c5645c4af5abede7b7db75340a6d8fec59afbab58042ac6e8a921fb7f1daec883dbbd932867d7594e91cd2a300c135dec00ddfd79280ade9890d882155bbcd0e30e7b13beede9a83", 0x5c)
msgrcv(r1, &(0x7f00000003c0)=ANY=[@ANYBLOB="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000005ccf598733ee"], 0x6c, 0x2, 0x0)
r2 = msgget$private(0x0, 0x1c)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
msgctl$IPC_SET(r2, 0x1, &(0x7f0000000000)={{0x1f, r4, 0xffffffffffffffff, 0x0, 0x0, 0x202, 0x2}, 0xde3c, 0xffffffffffff97fd, 0xffffffffffffffff, 0xffffffffffffffff, 0x1, 0x0, 0x8, 0x8000000000000001})
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000), 0x4)
writev(r0, &(0x7f0000001380)=[{&(0x7f0000001180)='6', 0x1}], 0x1)
sendto$unix(r0, &(0x7f0000000040)="d26a0000000a00", 0xfe26, 0x401, 0x0, 0xfffffffffffffe00)
r0 = kqueue()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000480)={&(0x7f0000000340)=ANY=[@ANYRESHEX], 0xa, 0x0}, 0x0)
kevent(r0, &(0x7f0000000000), 0x893, &(0x7f0000000400), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
sysctl$hw(&(0x7f0000000040)={0x6, 0x11}, 0x2, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0x882)
open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
shutdown(r0, 0x0)
mprotect(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x0)
recvmmsg(r0, &(0x7f0000000000)={0x0}, 0xffffffffffffff4a, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fffff0366dfde36ae61dba64800"})
write(r0, &(0x7f0000000140)="b686321922deaf7a6834cae69193fd208b1666a0ec7856098b22b8b4205a35a644627634db79dfaf8f743bed83cea9e9d71ecfd291fcf29442fd579f0297e2cf8c2725a6d8b1167f4d6025a06fd7b1a73af4be10cda8163baa33dd1fe3a902f8b919f315a432ede0087a049ffe0734c2e73c2a019707fb8753daba0b800b0a69f16af5eb7ae7312f7a361f8a35874a80f305b11496b2856e56f5e2d251682e90b0b4ec6ef0df5b0bc5b3254660bc3d5cc1ac80577db7b0e9d732140909e3c57906b1206f4c20a867f1648d7078520d5573c30608dadb18890dbfc8fd438b83", 0xdf)
read(r1, &(0x7f0000000040)=""/222, 0xde)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x5308)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000100)=[{0x80}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000180)=ANY=[])
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
sendmsg(0xffffffffffffffff, &(0x7f0000001740)={0x0, 0x0, &(0x7f0000001380)=[{&(0x7f0000000180)="a281e60eb8ce8bee83fa1ec31eb143eadd83aa6c829e26f09f28d831e1dbf44ac140aeb2717fc4d3e72cffa4ac2a5d0c5ed68eb1b30774ef875077dcc37c5c622c7766cf81718132a468095055a931e546c7219e72bb2e8b4e16235f0f294851e448e3c2c1481bff121a4ebf3eadd55e6ff3ff88bb472673842c27b40e9f8bf9f5ba283f66d4bb0b7d54e0e2122c61934109542aa2981cf6aa21d4d11c17c5cc93aa6da1b58ec5bae3bfd352ef2eef8fe9f71566e137bbdec0e955b6c5b14fee78e2cac9f25942e0cfa493cde6c87de75589fb639bd80c5d4e274e3c825cae3c3a0d104e7b332972b2336e92d15e83fe89cc462f141c009504069e7b2a9da7179188c5d684f6fd3affa637a8c5e075942d5da1f14a50f2dfae89192ca4881dc774aaa1f099b0bfcd69a421ecf8e3ba651dcf1619e4ab354f631e2f70eaca8fc35f61a48ca005555dc7a714780148b1501c7b6546cc60cfa94681e9d1657b648487f4764ddacf4a1785e98efa132bfdafb43e7b6a8cba53b86cb8e533e95cad03a1f1aa948e4350b68db5703548295c5f22af8f8554be183bdf0eb57c82ac1db9f73b3619ebcd21f8b0eb9f0e0f2cc1a823be3111a160c5514cb3297e26b4249588971f06cb9277473719101c0440cc6fe28def7b6aa8a21f08ce3fb8ebc534c8177eab74121652d950000faf0fddd4f9128ed31046738016806411789ce6a54ddba169dc7302aafdb9955d27a8a9e11aac5ac30c05857a3bfa899b53b5baf632f175a96e4ea48aa5a383765290864b224e5c030bac2c82412a9cbd5c7106bd6062948d0592fa29b108bb751c151c8292dc332a660f2c23b7d559f52666d7ee53d202891e15584f53a464450783a96541083160f9eb00c0ea3f5e1b70d60775bff062f1ecea518e3ae56b5d925e46690bda30a35e9b0333d706059234b1a48c88d7897f10f6d5f0bca3e85e0a25b491648119d9a9d41a596f0fd44aaf37577ea5f10780f25abc5fd76c9b72672cfc129489871fda4ef106fb735fce1ebb8f3ac1290bcd555026b42074e9b152e54c5975d16ba94e0c2f0dfdfb24d12bc6d4a967a47f2f899e0b2d63d653b701a68c768e44f1074b142ee06be60f4b22d8f79859fd7624b666486649df8e56821b32a14bbbedd845465a02d8ccf1bff9af3ec385305eab7e3fd69e3574992f8ebaf405472ca9c3a22293902db7a737a0babd34131ba10aae5d0d66235d0d02bb07dc6b1bb073f4d46f0837fddcbe91e98fa070d9eca922faa9abce298e604a1454906d928e067bd59720a0f25914dc475dee0ac86d7fbc4463f160fa7f1b7e947c62f5a8033dd04b70c57f373e8cb9c48689391958f866b3781f31de599ece5dda340732e46c720dadad75cb9051ce5cc4daffb385341cae1e9ea893b1e494164817ef5d71b99021146861942f296534fe030650b10885941c2e27bde89a32e057887dcd26d691d1e9645d7a16ffd2deb5cb21fe2663f8d3dd8b761884f72c4f63a5677ae7ecf28e5e09676e181f4741474ffab8ca6661ea5c983353d8dba0cfd68438d514046ff1f2c0ed1e9a8611cb756041e1e8968637a5cd904ac215cedf4a9bd7e06ddfae0f34d74861acf960b775fe0e5d915ae14279f9aeda8153112170dbc3bf36efb5cf262cecf9c64cbdbb1a12f0d0271c0c0ed0c1d1a9aecf31f164df81e7fc97e6cc7087a836547541e56b6bb6c7aa8d5ce5a4c018249c83943d6eebca414bb9f152b4380a703d750ea5654ff3f795d6595c5e3ce85a37ede42da32ee5dae42992b7b15176af98431846bad4a0c6e6f0c986f70524047c8e348ae8906787698bb15aae759e3792334a6a38d62d6ed5a6c8eb8e483cca158967ffdac85fab86463e46c4e387ea775315e7df9e669186b44242b41329db57c9712c8da0c5cc2473b56e1a69952e78aa0767a380ecdd55438d0efd2023c3725187b857ebef85d3f0e19a66853877590e38a7d7e2f5d457919514579a2028ef027b7be9174ac94b69a0289c7cd27bcc404cc0603361f41ee16ef5a6c5616192f5da78b020591d79ebbf2ea161ee8950e56febaf98f39c88064643d9d7aa0643563049c1d787673a9e61cbeedbbdf5e22d6d23dedfc5b7eb6db49afe836298d187896336d2e282fe10acf07cff2e416f1335411cadf7535aa16df6a9114cf6ca16d795a8af7ca404b73c3a4c763c63913653ba1321a9c07fd03f2a79e054f7d56190249f400812db47b476980414cc1ce4e52cd9da7ce5dfa3f45ce46d7739ce0c695effcefbd84a16d557ab16a75804bdfd702f93f076d4aa08d19d41277ec9ab8a5fef204d12b3e61577735b9a3ac344303d8379ea15e8cbc3bf26dc9ebd6f865e9c", 0x68c}], 0x1, 0x0}, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x9, 0x408})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x29}, 0x4, 0x0, 0x0, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
r0 = getpid()
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x118, r0)
r1 = getpgrp()
ktrace(0x0, 0x93a96fddd37e5095, 0x40000008, r1)
pipe2(&(0x7f0000000080), 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x8}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000080)='./file0/file0\x00', 0x0)
rename(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
mkdir(&(0x7f0000000080)='./file0/file0\x00', 0x0)
rename(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='./file0\x00')
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
mmap(&(0x7f0000fec000/0x14000)=nil, 0x14000, 0x0, 0x11, r0, 0x0)
mmap(&(0x7f0000ffb000/0x5000)=nil, 0x5000, 0x0, 0x10, r0, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000680)='c\x00')
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x300, 0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x1000000002052, 0x5f00)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0xc0205603, &(0x7f0000000040)={0x0, 0x4000cd, 0x0, 0x2002, 0x0})
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000080)=ANY=[@ANYBLOB="810207f040"], 0x10)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001440)=[@rights={0x14, 0x7}, @cred={0xaa0114ac}], 0x18}, 0x0)
setsockopt$sock_int(0xffffffffffffffff, 0xffff, 0x0, &(0x7f0000000000)=0x43cbc, 0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff})
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=ANY=[@ANYBLOB="8202"], 0x10)
dup2(r1, r0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000)=0x43cbc, 0x4)
r2 = dup(r0)
sendto$inet6(r2, &(0x7f0000000040), 0x43000, 0x0, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad4bdb0760911b0c41af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7f008000000000000ba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000000))
openat$null(0xffffffffffffff9c, &(0x7f00000001c0), 0x20, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x2}, {0x44}, {0x6, 0x0, 0x0, 0x100004}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000001c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmsg(r1, &(0x7f00000001c0)={0x0, 0x0, &(0x7f0000000100)=[{&(0x7f0000000080)=""/61, 0x3d}], 0x1, 0x0}, 0x842)
sendto$unix(r0, &(0x7f0000000280)="176f160758807daf29bd6e804dffb0b1826ac4d0cef07ca6a64a6f00f3c6ee7c7f47e0e4f240b17d001d9f7de2b34f8ff6123012bb5fa1d4e01a02015e", 0x3d, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={&(0x7f0000000040)='./file0\x00', 0x1, 0x0})
rename(&(0x7f0000000200)='./file0\x00', &(0x7f00000000c0)='./file1\x00')
unveil(&(0x7f00000001c0)='./file1\x00', &(0x7f0000000340)='x\x00')
unveil(&(0x7f0000000100)='./file1\x00', &(0x7f0000000180)='r\x00')
symlinkat(&(0x7f00000000c0)='/', 0xffffffffffffffff, 0x0)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x0, 0x3, 0x6a5, 0x1fc80d8f, "04559e00000000a9b3a608000000c7db4000"})
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
write(r0, &(0x7f0000000140)='x', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
ioctl$TIOCSTOP(r0, 0x2000746f)
write(r0, &(0x7f0000000100)="fa", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x35}, {0x7}, {0x6}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@broadcast, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "ccab6c", 0x8, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x1, 0x1, 0x8}}}}}}})
r0 = syz_open_pts()
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000000)=0xe614)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b10005016000009005001b0007000000541c04fecea10500fef96ecfc73fd3357ae36caa0416fa4f376b36acd00b7804be381e4991f7c8cf5f882b297be1aa5b23edeb51e2f0ca3ebbc257699a1f133ea7acb5d602000d7d026ba8af63003728211d000000720fd38bfbb770c1f5a872c881ea772ec5890400000000000000361b1257aea8c50000200200000d230000020208a371a3f8343712051e1d89e0000400000000000420000000000000000000", 0xb1, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0286988, &(0x7f0000000300))
sysctl$vm(0x0, 0x0, &(0x7f00000000c0)="de6b057903f834306b7797bbedd66395fcf7bb77ef69713a224a12f58274e5a587db00e78dc9cfabfcafdcdabbc3a8f6d47b23fdee27fe19d6d58821a3a5299c8c18c21561c7aae6a4d952dd6dda71bad835f1e699f895b8cb894242b07e18d335ca0c40e5c27101efdff8357c39ddc34d0c26bfe35f8b59b6599d2ff5aadf29a2dc98d99d893dbfe468013008f288feed7026716a6999a6b49024c0f34464b449cfb36442a624fd216df65dcccdf190d86c9d03a3e45625e5812218164c2920ae3fc0c197ff883970f4b3071e7d9d48854e3e5959008d4b1c4d4f77a277b8c0f94d9bb88a04301e4b8a87a451bb36d8a7e01bd6e80d0c0c81acd8bd1bb134b8dc38cbc8188b62b398d5e4d96a7db0bb28262a599e353b08bac16eb8bd68b00ef27d2a8aa2f8bbb80c5b8409c6851a93478ecee505dc7b53908a41b7776c6de8cbd1ffc35534ad48c4918d8e5f9ebccfeef512f30e2b9f314cbfb1f001a3b222ffbdba74cbe622064b772917879885e6011f4be610946dbf5fb2156408413c8ca5278e87614fe1c8c81c80e509f89bd3b4dded1e49c99ec22a5d5e6cc2a5f76516f6f0ef23ba0fe0f4bd0d0578bcbcef388fa53287870318ef2eb49d13b60c09739fd168eca5f736d3a3e99ab4f162ba2bb70a5d0c62384f992f6a3b2e1f3d28f0856526cccabca024c4cee3c1cf5b02e6693d7b06e82bfe0aa2b2b81050aa4e9623e815415a011bf55cd627a301d7fb61fd4c05e8bbab28c22521115df2e4cc3b8695b9f886a2ec966ea4e065c3301b22ead4def5d9a0f72285cd57eabe134076227b477f740b8bc4edf3bc56a9857f4655e33b8dd63277d5a514e1f272259a3bf46f15ec435c0ba8e47b9ec76438af1aba34a9de3c2145515e6cab8b6c3d0dc20106d8afdf3f696e3c8cd127b1381008c9257cae5f69b89d513e248b339527eb76f34ee5f4b0429b7c3acb46335fd09b08dd89e8b872da6a3600bd83908ff1ba44d08af4f889f2f8e76431d7f1795916995fb5c878feae9070d3de44941b0496c490a8e649221cb79b5f722eb61d2b9816f6cf8f667071314985c1272683a4d199ce7f212ae54c6c00ff7f8602e183ba33268cf99780e79c1875ec73cd22863b21bee30571f41c1c762c3f8ffa3b691b79dfeeba85d2bf39e5e547581d832a68bda35fe326f7eb81aa291e122096ba2192f964558b808c379dab0f6e8a6262b8121efbc7ef89ca4dba7e7f5ea22e4ec1664e0d4b284b17cc179c380677732a2c4445d7a299f52da0d5673331a600859b90e60bb52b33d0f4aff4043e949903e2088c7bb15ca72d4a2be09ec34703831dfe6a017a8525fc024c6d3c6ff9555e8d557da8700abfbe51934e885609769010062be01f62b7e8a40e4c6d72137d1e81a421c005a940c7cb1156ddd7e067cfe284e2da72850b4c7916072217261c3421890619899e47652f3e4efe7ee315bc3476cb55072543d57b219e1235920c54228b9c1331f1ce538a86b229ded6884d3e467fca453dc08e4b87b25b5fcc222bee809facb87626f78407c9a716b41f6993a539a9470e25028d34633a6ebd6e2ddd0aa83abf57a676e90dc44f114686c360041806b773184cf06ecbdc72426e69d4561f4694ed14e18671f802fb3692951500233bb1429118b26d2c52f144e777a817d8667fc0b6f7bee86c4aaec414e059c5441951e3d37dd25e0cd211e9e23e778c145744d690cee17deb34a1a7a97019c2ff5be4914f111dbe3fd9f59416399216dedac05ecc262531df741eaa2486e7a621d01f6e62de180a35e1106c085346d5d3c7cf22c7a7c9b4e14e5577f7b73cff13eed2fc79b19b2ffcd28e0ed5d0010501c04e90173bbde0527504aeec22ee8140648b309f5ec45e65f7bef9da1f11cfee651468597829abf9e0117cf8bb42e5502f1329e857400ac9f0ce9a59f10361255ad42d5b91028db3e93d4f4029600496a1d13f3e438e5efa381c33ccc1e41ee71d77ac8f35c31dae5ad7adab8ae3b445d9cc66ac4dc76bb8501f7f61fb52b85f0662a92e12c2dc8b3f7288ff090aa17b2ef1004ded9469f7dbd51026ccf377c00b09daae203751de6a743f37a3a5c59a5f7f90ee5de57db5f1ac14d2d5b45e9aa1f4dabef0e684f17080f619b2c979229406f32fc0d519361d08b8ae068e7321d22dc79cac09f8a8f92913f02552b0d74d4df41fc9f437f05047abd5850db9a783f892bbca45eab752db059fe5d62ce520220579bc4a094dc58981cc6dc70f42460eccf76b6f1b3b3dd726c8bca3f3a961eb20b5d38e30cd11072eb01a4448ffdacd5a8e17968f0847d4e4568e68cfb72c801702d7b2a19fe87c49a462b5fd17c893a093c8c94188d80ea4810a0764e92d5a383adbbc75780d27fad89a0a8361fd50f35d31549013cf053fe9f4a18b61a61587abbb472516fafce1b5dd13b2db3a85987636fb59b809215cd487d10b7e777f21279e8f022e50ade3a90cfac50c2e4d937174cede80b1283fa9e7d6d89a4be485f2715d13d7e3b5686dc2c9a2dfa154bcebeda84264e5e11d9908cbd38c72b500446f0b15cbbae20b5267b266071b24dec20047e06c92f8f01bef480c148c1548a3d88984c4ade7f23e32fcb507a67fee2b9eee297d6c70ad08b414e8e061b471aa1075a5fd4baae07a0f9177bb7983b2c87e88707f2389fee6ed195736d1d6470c2a4793619656aab45893699005bd9753bfb70517a397696c92e4f662a3f833fe57262f3946d2e8c393daf6cafe95b4b06386b6568a8a1fb51b7f5ab27427394b9e3b1e81e16a5e30ec56c6e635d37019ae48fb0f544a3c0a16e1aa54ad7ac3dc0595f591d53ded1eb9a5b929f6230e56ebbc3e5d05fd596d8cc03f9fd995fae038e0872e9f31893afca035dffcf41298b44ee952496cb472786345d0d1a1a2aa8057a01d3af508ea8c7f8cdf954d64217bc1e9803ff91aaff82c4cc687ac123ba87efdd218a6cbc6d04de2ae0966d438895d7bf544a301a408ab1e490d9aafaf01b8244786251fb4972836ba0d29511eb4091e9e265f9a05ccbf68c4096da27cbdb579c1c6cf5e83f3a6c1db2f6392115214ffe63d9cf5908b84798ab20b36539dc905cba02564d733a2263c7170f09e6e73626c116c9ba567878457b51e4b883e9454c85db83a39fa637e3580cfb651e22d436556c5c1ec98bc0ef3664dd648cc4fe8e38b9dee9fc013f973e74d0d8ca5471dcea079bef8261d8fe7842e5eea1360354b8500d6f3770f213df0ccf32b35c9f35633df1a7edb8802dcf483a0cee0da65e44e2b2191862a3f80556a18d8a5d90faa3e96c98fbb98fe045eb77503b17eef3a2d22a327aabd397b131a1d860986feaefa5750b39ca9e82e928f5946969d345eaaad11c36eab8bc09ec9859c58441d0929a4bf27806ab5a8700e6dc029cad4d88132b201b833c784e9e730a2c49d220bb9b7ea4dcb00bb4df177066a9bd7d8accd30db278c408b1f09a137d442c747723b4e2e18eb1b8288f0e21b01141370ae16f1e6aa37de43fddb68e7c910af1378515b9e2853529a500bc7dce198758fdabc007a1980024c13b0fea196ffd1668c7bf40a8184b6769c6c1a4c65b3d58064a0893d52d21ce78d7d38dec170cab6bfd6a4b5f173d2c1966429189166534ad5c1920f07d4a26f38b0659d4e17dbe9b92e8b363872317fecfa19461d6e9a6be4e72cb4823575c2f4276d0e60bfefdcf53786902fe8e95c12100dfb566790b59edf84924c53d288e423a5280d947fef7ecb5e1f8926b47b5d62407405f4f96157ff318e4ba70c494bf6d430e981134a14d29ce2ab2e04f94e30fc22d920c16f3ab2857ebb9baeafc5b4c5b090c717cd1d50ced5e4d3ab8a98bab850388d660ecd985a03c54121d690b14200cf0a32b2960eb1f7b36aa0a34c6a7de5a57f8970da47b0df92cf142ec9b39ed92ef6985bcd76b190059df98cff1fe6523e5f059d07b2dfcb3d24abf2e70c5cb9bad8154191d13c2ea4d956749cacc8c01d55c592147811deb055f10742651b7afabfa9b9a09b827ea8c4e295d68586ad699d00bf74561d2af64c6ca4663a08a5ec648b690b5ea525daa0c45c4623855722c1f43b61f50e7a073e0a274cde0338311f5ae7e97dfa036dd455ae7d68c37308e7f9643eaaecf0a5b46534d1c77a1919df04a3a0eec9c14ae4a511a8026cd7e168714d783775c38c68677f0c694e822b5f1f6f6a9202381a6484dd41540d0b462dfc7577cb6191438f5f6b29623f55a97daa7037725781832ff6ca479e1f022a99df0a33c8c0b2afe631258d339b20a942a1942a9232feab938b621f41ba087406cf4528fd52d91c46291996a056bb9406a778d855e0f8b759a733e3ca285dcdcd48c35883b8e666831d534f049391d1cc693de47017393102ef42130a42fc136f4f8475a1169cffab10f645d4261cad3b048c3a69609fafacc2081081866e58c1e4f009949cf8f7f78751d97e0247c8ec77b31106ab00edda00a08c76f6949f856a30a87b8d02d70f448d5ee305118ae4d0b292c9e02463d733522eea0e9887b44ae98acda24bf7bfa69a381511cc97d1456684fec7a5ab76c57a901a65a5eabb15bd4fef244e8beded6800e77d1b9fc61d7b41d6ebeab8f70bbdce6ad6454aa2de02706900a329907941c01cce08843de4f4ddd0ab6be1b4df8056895574416ab2dc46af10ee9a5a425016e5f14ad08bd3ed95f7d236acc8d0c2d34ff18e48fb1475103358a539d92f37e525cbf768ff7fdfd1097a6b50062769becde938115a3d43859450a9084631b8f6b1e371f2accb1cfa27311c3af83710152c27657cb3d80b3f19359ec35a9eaa156d3a41466d0276", 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xcd604404, &(0x7f0000000100))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000100)=ANY=[@ANYBLOB="fb182e0b3d9a090000000000000043"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x2, 0x0)
r2 = dup2(r0, r1)
sendmsg$unix(r2, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x40, 0x0, 0x0)
r0 = socket(0x1, 0x5, 0x0)
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f0000000000), 0xc)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000400)=[{0x2}, {0x14}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x50}, {0x1}, {0x6, 0x0, 0x0, 0x5a}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x97ad, 0x0, 0xffffff80, 0xffffdf7f, "f163b508c7c3421600000000000079000200"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f048eea427be6c0045598cdfb92934638d", 0x90)
r2 = dup2(r1, r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "ddd318ac4d203200ffffffff00"})
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x20000002a, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0x20000002a, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
pwritev(0xffffffffffffffff, &(0x7f0000000200)=[{&(0x7f0000000280)="0f5a9c89ed5ac1e8927f819859f099e71ddfdb54f2d1f5cba499a4d74a220d47074700091f43c18a729bfe021613ad6c31df621dddf8d06922fec79bb7d7a084d3b0c0f9ed225e4cb045ac0a771e99dcaa1bb1dcdcb9856b211288cd38610011b6a2c2895df02f6bce42174ce660842cbc1ed090ed4d3cbd718de4f453ef2be429800696ecf204d7baa176b9502c8d46858acc3e0fe404aa5ae2be8f2bf0142a5de7631679f70f53610ae100c9943cdd23bae31640774f7d900e4f6122009473b41555ea48b9db4e780921c22f922ed10a506f6512e1353cf3780e53aa4871b88af05c858db6d7fbf1562990e6d89e148c06d78e875ad2bf7d57541674364c85c088464e23c06b9a9f7d1cc9bc23c0377ce1013c0f20ebb1ff9a219a22345061378da7ef23fb0724ca65a1ff90a320b2a5af76d9ebca153cb2255798cd86a9134c17d60c553411344c59ca50649652a0b1f479103638d81c1f020fc01d4d26f4a34c81da2f055b951fdab005b103c5d54e8b2af00b7399734e8cc1e7b7a8d430c3ca9cfb8a53783e8ea38cc6802a2b5c0ef3384063eb6e4d599f3c79ef6d0155268b66a673f909b0dbd8a72a89392fce07838505ddb46a8f5774ca6758c18758e5ae66c07903eaf4000d1e432b9e1fa3468073716b49fafa757e28e342591be881f56a55ff933ca3309eb282261efcffcaa69c2de9233853d126daeed4127bcb34743a081efc09b7dc721f28cd548b593aff57be5c536f5f2d4b6176c9f0a75c05881a1f0c6dc0658ae5e9f5bed7496a8e467438dcab59315da6451c7db1dd0df055745313f7fc4f339217a4a162e9bfcd7c355b209373a6c5024214c78a4261fa4d7e666af8206b9ba09b68b0b73444d47ea174ab25d0002fc4b2fb28be4acc1d4f954934cdfd0b0e4d8fd7fa6414050f26e77af45bbc9a3d553f439e20affe0ccfed2563c1371f9205aab4988849ac70d768425ff8652ac364a5e08c8a4be570c90de2f50c1f38d26ec1b0c7ddee4477b63e540eb5c05c82e5017133568348234c165f636f20b49beb38780fd09fa1734d347a39033e9686ef2ede723b495ae62dbc1525f8769a5db226a48211f09f86188a7b806d7a99c52cd3412961c7406f90e9a07837a1e7694e0d9008f8c2b1ad74ed2c0142aab3dd9938943e2f8fd35aeacc02eeb3a64a8787362bf41cc66913c8e3fbade6b67aa688a98cdaeee7aa4813cb591c8b2d1323e1ccff3787c1bf42fb8a8d8755a92005bc3abd9c01186cd17c6ec570b24a9b65d0c3429e4870f07b932881144a09b03166e55c5ea478611de5323dae068f09f307a6a4daf0c060f386df245e3cde5621a2c6c701bf7decf8f2b29748bb8e8505746957e3516099719aba18568f806ec5935679589c0db10f14b1c8a786f44f000fab829c5eb2913c", 0x3f1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504442, &(0x7f0000000240))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{}, {}, {0x8018}]})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x801169ac, &(0x7f0000000300))
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSFLAGS(r0, 0x8004745c, &(0x7f0000000000)=0xffffffc1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x5c}, {}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={0x0, 0x0, 0x3ffffffffffe})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
socket(0x400000000018, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(0xffffffffffffffff, &(0x7f0000000280)="00f3ffffffffffff01", 0x9, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000280)={0x1, 0x2c}, 0x3, &(0x7f00000006c0)="11ab71df0bee641037ef264e52bf5ba4e889de2a70052fb317a6faad287f8042bacfd8d4a060be98397a4cfe1095a4c97e36a603005fff27186650d2e29d42adf81000fca06aa586d7dadd23aa82cffa337618b45d6088a9737e8b581e000000000000000000", &(0x7f0000000680)=0x1ba6, 0x0, 0xfffffffffffffe97)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f0000558000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f00001a0000/0x4000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000ff6000/0xa000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./bus/\x00', './bus\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus\x00', './bus\x00', './bus\x00']})
r0 = socket$unix(0x1, 0x5, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
r2 = kqueue()
kevent(r2, &(0x7f0000000000), 0x100, &(0x7f0000000040)=[{{r1}, 0xfffffffffffffffe, 0xb}], 0x5, 0x0)
ioctl$TIOCFLUSH(r1, 0xc028698d, &(0x7f00000000c0))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x8040000000100}]}})
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0xa40, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x144}], 0x1, 0x0)
fchmod(r0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x6c}, {0x1}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = socket(0x18, 0x3, 0x3a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000100)="8ae8f18b", 0x4)
r1 = fcntl$dupfd(r0, 0x0, r0)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000200)={0x0, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x3f, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x0}}, 0xaa)
r0 = kqueue()
kevent(r0, &(0x7f0000000080)=[{{0xffffffffffffff9c}, 0xfffffffffffffffc}], 0x8, 0x0, 0x0, &(0x7f00000003c0))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x3, 0x0, 0x0)
readv(0xffffffffffffffff, 0x0, 0x2)
msgsnd(0xffffffffffffffff, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x3a, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x3e, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000080)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x80007, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
sysctl$hw(&(0x7f0000000000)={0x6, 0x18}, 0x2, 0x0, 0x0, &(0x7f0000000200)="f7c62211", 0x4)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0xa, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f00000000c0)={0x80000000})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = dup2(r1, r0)
poll(&(0x7f0000000180)=[{r2, 0x4}], 0x1, 0x0)
poll(&(0x7f00000000c0)=[{r2, 0x4}], 0x1, 0x0)
r0 = msgget$private(0x0, 0x2)
msgsnd(r0, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2cc63a9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="7002d5fbe0"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setreuid(0xee00, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x0, 0x0, 0x0}, 0x8)
r1 = getuid()
setreuid(0xee00, r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
msgsnd(r0, &(0x7f0000000500)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3501000000341b1f80b87cb6f04b1aa1995045af2238befbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d6803d617130b6a00"], 0x6d, 0x0)
msgrcv(r0, &(0x7f0000000280)=ANY=[@ANYBLOB="00000000000000000000000400000000000000000000000000000000000700000000"], 0x22, 0x0, 0x1800)
msgrcv(r0, &(0x7f0000000200)={0x0, ""/233}, 0xf1, 0x3, 0x1000)
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000001c0)={{0x7, 0x7}, {0x0, 0x5}}, 0xff5a769dfa4b7819)
r3 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x40, 0x0)
readlinkat(r3, &(0x7f0000000080)='./file0\x00', &(0x7f0000000100)=""/139, 0x8b)
r0 = semget$private(0x0, 0x4000000009, 0x0)
semop(r0, &(0x7f0000000480), 0xe)
semop(r0, &(0x7f00000001c0)=[{0x0, 0x22f5, 0xc00}, {0x1, 0x0, 0x1000}, {0x1, 0x1f, 0x1400}], 0x3)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f0000000280), 0x0)
semop(r0, &(0x7f0000000180)=[{0x4, 0xfffc, 0x1000}], 0x1)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x40}, 0x40, 0xfff, 0xd})
semop(r0, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x2, 0xc2f, 0x1000}, {0x2, 0x96e, 0x1800}, {0x0, 0x7}], 0x4)
semop(r0, &(0x7f0000000040), 0x0)
semop(r0, &(0x7f0000000040)=[{0x2, 0x0, 0x400}, {0x1, 0x8021, 0x1000}, {0x4, 0x4, 0x1000}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(r0, &(0x7f0000000080)=[{0x4, 0x82, 0x800}, {0x0, 0x3, 0x800}, {0x0, 0xb4, 0x1800}, {0x0, 0x5, 0x1000}], 0x4)
semop(r0, &(0x7f0000000000)=[{0x1, 0x0, 0x800}, {0x0, 0x5, 0x1800}, {0x4, 0x101}, {0x3, 0x6}, {0x2, 0xbe, 0x1800}, {0x0, 0xff}, {0x0, 0x86, 0x800}, {0x2, 0x2, 0x800}], 0x8)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000280)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x110, 0xe972}, 0x8, 0x9, 0x401})
semop(r0, &(0x7f0000000240)=[{0x2, 0x1c, 0x1000}, {0x0, 0x5, 0x1800}, {0x2, 0x6, 0x1800}, {0x0, 0x1, 0x400}, {0x0, 0x6, 0x1000}, {0x1, 0x6, 0xc00}, {0x4, 0x595, 0x1000}, {0x0, 0xdc6, 0x1800}, {0x1, 0x1, 0x1800}], 0x9)
semop(r0, &(0x7f0000000200)=[{0x92f551a84a610c10, 0xfd}], 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b10005046000000000000847d7", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = open$dir(&(0x7f0000000000)='.\x00', 0x0, 0x0)
lseek(r0, 0x0, 0x4)
getdents(r0, &(0x7f00000000c0)=""/4092, 0xffc)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x100000001})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000080)=[{0x1d}, {0x1, 0x6}, {0x4000006, 0x0, 0x0, 0xffffffaf}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)="01", 0x1)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000140)=ANY=[@ANYBLOB="f4181ae969681100ff"], 0x1c, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
sysctl$hw(&(0x7f00000001c0)={0x6, 0x8}, 0x2, &(0x7f0000000200)="c0367309aa46af3509414058f9669e9c791e5f389a", &(0x7f0000000280)=0x15, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x11, 0x0, 0x23}, 0x4, &(0x7f0000000240)="275fab", 0x0, &(0x7f0000000640)="d0054653f1db1b872d5690eaf905000000ca2523e211a09121e6f5baa55a4a09dc8433cf6627feaff4a18533769db8b61ab481e339d9eb2a3910509874cace0b97caaf135ecd69b686b9f81c837c518c116652fa4461ef4701cdb6f4214808bcf65e9ca80100cb077fd264fddc77ca0c68e0eb361dd00e74438a273f26c8", 0x7e)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000300)={0x0, './file0\x00', './file0\x00'})
r0 = semget(0x3, 0x0, 0x0)
r1 = semget$private(0x0, 0x0, 0x0)
semop(0x0, 0x0, 0x0)
semctl$GETVAL(0x0, 0x0, 0x5, 0x0)
semctl$GETZCNT(r1, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r1, &(0x7f0000000280), 0x0)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f00000001c0)={{0xaf, 0x0, 0x0, 0x0, 0x0, 0x30, 0x40}, 0x40, 0x6838, 0x8000000005})
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000280)=[0x5, 0x5, 0x6, 0x4, 0x7ff, 0x0, 0x1, 0x912, 0x2, 0x1f, 0x9])
semop(0x0, &(0x7f0000000100), 0x0)
semop(r1, &(0x7f0000000180)=[{}], 0x1)
semop(r1, &(0x7f00000002c0)=[{0x2, 0x1, 0x1800}], 0x1)
semop(r1, &(0x7f00000003c0)=[{0x1, 0x4, 0x1400}], 0x1)
semctl$SETALL(r1, 0x0, 0x9, &(0x7f0000000580))
semctl$GETVAL(r1, 0x4, 0x5, &(0x7f0000000080)=""/167)
semop(0x0, &(0x7f0000000340)=[{0x2, 0x0, 0x1000}, {0x7, 0x7, 0x800}, {0x3, 0xffc1, 0x800}, {0x3, 0x2}, {0x3, 0x8, 0x1000}, {0x4, 0x7, 0x1000}, {0x2, 0x9, 0x1000}, {0x0, 0x0, 0x7b0aabf3bae273bc}, {0x1, 0xca3, 0x800}], 0x9)
r2 = semget$private(0x0, 0x4000000009, 0xc8)
semop(r2, &(0x7f0000000380)=[{0x0, 0x2, 0x1400}, {0x4, 0x7, 0x1000}, {0x3, 0x81, 0x1000}, {0x2, 0x21ff, 0x1800}, {0x3, 0x2e5, 0x1000}, {0x4, 0x7d12, 0x1800}, {0x2, 0x7, 0x1000}], 0x7)
semctl$IPC_RMID(r2, 0x0, 0x0)
sysctl$kern(&(0x7f0000001100)={0x1, 0x16}, 0x2, &(0x7f0000001140), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f00000001c0)='./file0\x00', 0x1, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
close(r0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0x3f, 0xfffffbff, "7d26b234cfdf204962e800981993def0805611dc"})
writev(r0, &(0x7f0000000100)=[{&(0x7f0000001740)="98853933bdd476856b0de9fcce690b8b874e3cf1a6aa6af1b88b8cfc568b5b55066e91f2a7cab6bd77c135bfd6d88aab41c56958494b056809101bcf8c9b00", 0x3f}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket(0x11, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
r0 = openat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x21a332d536087be, 0x0)
sysctl$net_inet_gre(0x0, 0x0, &(0x7f0000000080)="3bd868e38e5bdbacf0c0183109d7", 0x0, 0x0, 0x0)
writev(r0, &(0x7f0000000000), 0xc)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4}, 0x2, &(0x7f0000000040), 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x2f, &(0x7f0000000000), 0x0)
sysctl$vm(&(0x7f0000000000)={0x6, 0x19}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x15}, {0x3c}, {0x6}]})
pwrite(r0, &(0x7f00000001c0)="d0000000000022b3a27e24b3a566", 0xe, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x800000000000007})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x6, &(0x7f0000000040), 0x0, 0x0, 0x0)
getrlimit(0x3749b2ef792fdd36, 0x0)
sysctl$vm(&(0x7f0000000100)={0x2, 0x6}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e00000000000000001000000", 0xc)
r1 = dup(r0)
r2 = dup2(r1, r1)
setsockopt$inet_opts(r2, 0x0, 0x9, &(0x7f0000000140)="e800f8000000000000000000", 0xc)
pipe(&(0x7f0000000340))
open(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x40001c2a, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000040)={0x0, &(0x7f0000000180)})
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000040)="eaff125c00000000", 0xc)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
r1 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
shmctl$IPC_STAT(r1, 0x2, 0x0)
syz_emit_ethernet(0x3a, &(0x7f0000000000)={@local, @random="2e8dd386ef21", [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @multicast2, {[@generic={0x0, 0x2}]}}, @tcp={{0x2, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x4, 0x1904, 0x0)
utimes(&(0x7f0000000040)='./file0\x00', &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x1}], 0x1, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
pwrite(r0, &(0x7f00000000c0)='mS', 0x2, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x1}, {0x24}, {0x6, 0x0, 0x0, 0xaeb1}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
ioctl$WSDISPLAYIO_SBURNER(0xffffffffffffff9c, 0x800c5751, &(0x7f0000000000))
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x2010, r0, 0x0)
msync(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x6)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
shutdown(r0, 0x1)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
mmap(&(0x7f00003ff000/0xc00000)=nil, 0xc00000, 0x0, 0x1010, 0xffffffffffffffff, 0x0)
preadv(r1, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20804000, 0x200000]}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000001c0)=[{0x40}, {0xc0}, {0x6, 0x0, 0x0, 0x7fffffff}]})
pwrite(r0, &(0x7f0000000200)="e803f150ff8569142b71abf63143", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x0, 0x0, 0xfffffffffffffff8, 0xffffffff, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f0000000380)=[{&(0x7f00000001c0)='f', 0x1}], 0x1)
r2 = dup2(r1, r0)
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "72f3f1b91207c5364b387fb9d5eb1fcf26c11be1"})
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f00000001c0)={0x0, 0x0, 0x0, 0x0, "0ebe1f024aeeb1aeac7c57fbcc8705b9abad62ae"})
ioctl$TIOCSDTR(0xffffffffffffff9c, 0x20007479)
pipe(&(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_INTR(r1, 0xc450444a, &(0x7f0000000140))
ioctl$BIOCSRTIMEOUT(r1, 0x8010426d, &(0x7f0000000280)={0x4, 0x4})
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f00000000c0))
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(&(0x7f00000001c0), 0x1, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000200)=0x6)
read(r0, &(0x7f0000000040)=""/32, 0x20)
r0 = kqueue()
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000001040)=0x9)
setrlimit(0x8, &(0x7f0000000000))
socket$inet(0x2, 0x3, 0x0)
sysctl$vm(&(0x7f0000000000)={0x6, 0xe}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x1, &(0x7f00000000c0)=[{0x16}]})
syz_emit_ethernet(0x36, &(0x7f00000001c0)={@local, @random="0ba3ae0a44a8", [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x0, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @loopback}}}})
setrlimit(0x8, &(0x7f0000000080))
socketpair$unix(0x1, 0x2, 0x0, 0x0)
r0 = socket(0x2, 0x4002, 0x0)
sendmsg$unix(r0, &(0x7f0000000c40)={&(0x7f00000007c0)=@abs={0x0, 0x0, 0x3}, 0x8, &(0x7f0000000800), 0x0, &(0x7f0000000b00)=[@cred={0x20, 0xffff, 0x0, 0xffffffffffffffff}, @cred={0x20}, @rights={0x30, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20}, @cred={0x4, 0xffff, 0x0, 0xffffffffffffffff}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}], 0x120}, 0x0)
r0 = semget(0x0, 0x2, 0x209)
semop(r0, &(0x7f0000000000)=[{0x0, 0x7, 0x2800}, {0x1, 0x4, 0x1000}], 0x2)
socket(0x6, 0x2, 0x0)
semop(r0, &(0x7f0000000640)=[{0x3, 0x3f, 0x800}, {0x1, 0xbd, 0x1c00}, {0x2, 0x9, 0x1800}, {0x3, 0x5, 0x1000}, {0x1, 0x1f, 0x1000}, {0x0, 0x20}, {0x2, 0x5, 0x1000}, {0x0, 0x1000, 0x1000}, {0x3, 0x800}], 0x9)
r1 = semget$private(0x0, 0x4000000009, 0x82)
semop(r1, &(0x7f00000002c0)=[{0x2, 0x3, 0x1000}, {0x0, 0x6, 0x800}, {0x4, 0x200, 0x800}, {0x1, 0x8, 0x1000}, {0x3, 0x7}], 0x5)
semop(r0, &(0x7f0000000540)=[{0x0, 0x3f, 0x800}, {0x3, 0x0, 0x800}, {0x4, 0x960b, 0x1000}, {0x1, 0x7, 0x800}, {0x1, 0x9, 0x3000}], 0x5)
semop(0x0, &(0x7f0000000640), 0x0)
rename(&(0x7f0000000500)='./file0/file0\x00', 0x0)
setreuid(0xee00, 0x0)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000680)={{0x6, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x8, 0x4}, 0x0, 0x7f, 0x9})
semget$private(0x0, 0x2, 0x90)
semop(r1, &(0x7f00000005c0)=[{0x0, 0x2}, {0x2, 0x6, 0x800}, {0x4, 0x1000, 0x1000}, {0x2, 0x400, 0x800}, {0x3, 0x0, 0x1800}, {0x0, 0xfffc}], 0x6)
open(&(0x7f0000000600)='./file0/file1\x00', 0x100, 0x20)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000000)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000000000/0x4000)=nil, &(0x7f0000000000/0x1000)=nil}, {&(0x7f0000ff7000/0x2000)=nil, &(0x7f0000008000/0x1000)=nil}, {&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000ffd000/0x1000)=nil}, {&(0x7f0000ffe000/0x1000)=nil, &(0x7f0000003000/0x4000)=nil}, {&(0x7f0000004000/0x1000)=nil, &(0x7f0000ffe000/0x1000)=nil, 0x100000040}, {&(0x7f0000000000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffa000/0x1000)=nil, &(0x7f0000007000/0x1000)=nil, 0x104}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000000000/0x3000)=nil, 0x1}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000002000/0x1000)=nil}, {&(0x7f0000001000/0x4000)=nil, &(0x7f0000006000/0x2000)=nil, 0x6}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000001000/0x4000)=nil, &(0x7f0000000000/0x3000)=nil, 0x800000000}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffa000/0x2000)=nil}, {&(0x7f0000001000/0x1000)=nil, &(0x7f0000fff000/0x1000)=nil}, {&(0x7f0000ff8000/0x3000)=nil, &(0x7f0000ffc000/0x1000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil, 0x4002}], ['./file0\x00', './file\x00', './file0\x00', './file0/file1\x00'], './file0\x00', './file\x00', './file0/file1\x00', ['./file', './file', './file']})
mprotect(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x1)
mkdir(&(0x7f00000000c0)='./file\x00', 0x151)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
semop(r1, &(0x7f0000000380)=[{0x0, 0x2, 0x1400}, {0x4, 0x5, 0x800}, {0x0, 0x8}, {0x2, 0x2202, 0x1800}, {0x3, 0x2e5, 0x1800}, {0x0, 0x7d12, 0x1800}, {0x2, 0x7, 0x1000}], 0x7)
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000580)=[0x3d3, 0x0, 0x0, 0x5, 0x8])
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
mquery(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x0, 0x0, r0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x3, &(0x7f0000000040), 0x4)
setuid(0xffffffffffffffff)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xe, &(0x7f0000000000)="02000000", 0x4)
setsockopt(r0, 0xffff, 0x1, &(0x7f0000000040)="aeb566f5", 0x4)
listen(r0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
close(r0)
mquery(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x2, 0x0, r0, 0x8)
write(0xffffffffffffffff, &(0x7f0000000100), 0x0)
mkdir(&(0x7f0000000500)='./file0\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0xb1}, {0x1}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000340)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "1fc3d3", 0x0, 0x0, 0x0, @empty, @mcast2}}}})
r0 = kqueue()
openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x7fffffff, &(0x7f00000000c0), 0x4f4e, 0x0)
r1 = open$dir(&(0x7f0000000080)='.\x00', 0x0, 0x0)
r2 = openat(r1, &(0x7f0000000200)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r2, &(0x7f0000000100)='./file0\x00')
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f0000000180)='r\x00')
openat(r1, &(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000080)="0000000013b9fd812e5c55000048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x2, &(0x7f0000000000)=[{0x74}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000340)={@remote, @local, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @remote, @local={0xac, 0x14, 0x0}, @remote, @multicast1}}}})
sendmmsg(0xffffffffffffffff, 0x0, 0x2, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x22, 0x0, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000280), 0x8, 0x0, 0x0, &(0x7f0000000340), 0x0)
ioctl$TIOCSETAW(0xffffffffffffff9c, 0x802c7415, &(0x7f0000000040)={0x0, 0x2, 0x0, 0x0, "23e7c8204832406f637b9a9f11c632de6aaac396"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$net_inet6_ip6(&(0x7f0000000000)={0x4, 0x18, 0x29, 0x35}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0xc}, 0x2, 0x0, 0x0, &(0x7f00000017c0), 0x4)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x26001)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
munmap(&(0x7f0000820000/0x4000)=nil, 0x4000)
munmap(&(0x7f000081d000/0x4000)=nil, 0x4000)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000001c0)=0x6)
r1 = socket(0x11, 0x3, 0x0)
r2 = dup(r1)
sendto$unix(r2, &(0x7f0000000000)="b10005016000009005001b0007000000000004fecea11ea8fef96ecbc73fd3357ae26caa0416fae41f6336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699afeff2e27acb5d6e8ff0ce24e27f1af63ff3728211ee4fd89830fd3872babfbb770a9f5a872c881ff7cc53c89530383a6f4ace296a993cc6db22f310b404f36a00f90006ee007000000000002000000000000000000c28e1489d951d3c42e141c182e0100", 0xb1, 0x0, 0x0, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000200)={0x0, <r3=>0x0, <r4=>0x0}, &(0x7f0000000240)=0xc)
fchown(r2, r3, 0x0)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
dup2(r0, r1)
ioctl$VT_GETACTIVE(r2, 0x40047607, &(0x7f0000000280))
socket$inet6(0x18, 0x6, 0x9)
r5 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r5, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
setgroups(0x7, &(0x7f00000003c0)=[0x0, 0x0, r4, 0x0, 0x0, r4, 0x0])
ioctl$BIOCSETWF(r5, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x30}, {0x74}, {0x6, 0x0, 0x0, 0x47f}]})
write(r5, &(0x7f0000000440)="3c9ed0f773672ee31265ac52c02e", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x3}, {0x7c}, {0x6, 0x0, 0x0, 0x7b98}]})
pwrite(r0, &(0x7f0000000300)="c8dcafccd1a6f48da693157343cb", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0xd, 0x0, 0x0)
setuid(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0xb1}, {0x45}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000000)={@random="ca49fa1fb369", @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @empty, "69c0d33e60bc536b586388634e5f2dec"}}}})
pwritev(0xffffffffffffffff, &(0x7f0000000000)=[{&(0x7f00000004c0)='w', 0x1}], 0x1, 0x0)
sysctl$hw(&(0x7f0000000000)={0x4, 0x1f}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$net_inet_etherip(&(0x7f00000004c0)={0x4, 0x2, 0x61, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0x5c}, {0x7}, {0x6406}]})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@broadcast, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @empty, "a390706d0594213a995fadbe0e275611"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000003c0)=[{0x74}, {0x28}, {0x6, 0x0, 0x0, 0xffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = socket(0x11, 0x3, 0x0)
connect$inet6(r0, &(0x7f0000000080)={0x18, 0x2}, 0xc)
select(0x40, &(0x7f00000000c0)={0xfffffffffffffffd}, 0x0, 0x0, 0x0)
select(0x4, &(0x7f00000000c0)={0xfffffffffffffffd}, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x3}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x10, &(0x7f0000000000), 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240)=0x1)
r0 = socket(0x18, 0x1, 0x0)
shutdown(r0, 0x0)
mprotect(&(0x7f0000001000/0x3000)=nil, 0x3000, 0x1)
recvmmsg(r0, &(0x7f0000000000)={0x0}, 0xffffffffffffff4a, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000000), 0x10081, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000100)=0x1300)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000100)='c\x00')
r0 = msgget$private(0x0, 0x33)
msgsnd(r0, &(0x7f00000004c0)=ANY=[], 0xcf, 0x800)
msgrcv(r0, &(0x7f0000000140)={0x0, ""/6}, 0xe, 0x3, 0x1000)
msgrcv(r0, &(0x7f0000000600)=ANY=[], 0x22, 0x1, 0x1800)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x1, 0x0, 0x1})
r1 = msgget$private(0x0, 0x400)
r2 = socket(0x18, 0x3, 0x1f)
setsockopt(r2, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r2, 0x29, 0x800000000000009, 0x0, 0x0)
r3 = open(&(0x7f00000000c0)='./file0\x00', 0x20, 0x10)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r4, 0xc0084427, &(0x7f0000000140))
kevent(0xffffffffffffffff, &(0x7f0000000280)=[{{r3}, 0xffffffffffffffff, 0x40, 0x40000000, 0x4, 0x57e}, {{r2}, 0xfffffffffffffffc, 0x0, 0x8, 0x85a9, 0x9}], 0x2, &(0x7f0000000300)=[{{r4}, 0xfffffffffffffff9, 0xe4, 0x20, 0x10000000000000, 0x100000001}], 0xa1, &(0x7f00000006c0)={0x20001, 0x8})
msgrcv(r1, &(0x7f0000000180)=ANY=[@ANYRES32=r2, @ANYRES8], 0x93, 0x0, 0x0)
msgrcv(r1, &(0x7f0000000340)={0x0, ""/60}, 0x44, 0x2, 0x1000)
msgsnd(r1, &(0x7f00000003c0)={0x1, "54a7ac21511a97423fd6c740a91e5e4a5101adef0e662e2828341e6155ced1e9586b84e42a9d46e68ef435145e304f9177f3c3b9d47896c87a9045d5709c3b1f6dca4026fe560dc7ddd987f7cce4d4f5530f7b"}, 0x5b, 0x800)
msgsnd(r1, &(0x7f0000000440)={0x2, "901f6e07bc3dea97798f13cb941d9ff4bd1f7fd0c0d9837d285539b3a82deabea46e9f3f86f3e8581041015810a6984f1d156ff72c098b58257e6e45077a308e1100db3ff8f8e8a71a71a1b395fd15a72701d3b7a3135dde425b9d249f3963d9fc6cd8e8e2eb314ffb1118bdc5e74e896957753518af5e145b21e83bd7620080a3afc6d11ef16db6704926129633a8757c6782325d10b3cff4b5b46c4ddeb7b1d7c34f00c354b2c7b78122b25cfc7e471906fce9e53ca2d815270d04964d292d1bf12706996ccca32051463cab0aba1c8c759e2c5dc62f7f21982c4f5b761aa6"}, 0xe8, 0x800)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0206911, &(0x7f0000000300))
sysctl$net_inet_ipcomp(&(0x7f0000000000)={0x4, 0x2, 0x6c, 0x1}, 0x4, 0x0, 0x0, 0x0, 0x0)
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r0)
getrlimit(0x0, &(0x7f0000000100))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
poll(&(0x7f0000000200)=[{r1, 0x93}], 0x1, 0x0)
poll(&(0x7f0000000180), 0x1b, 0x400)
dup2(r0, r1)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x25)
setsockopt(r0, 0x0, 0x69, &(0x7f00000000c0), 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt(r0, 0x0, 0x1021, 0x0, 0x0)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = fcntl$dupfd(r0, 0x3, 0xffffffffffffffff)
r2 = socket(0x11, 0x4003, 0x0)
socket(0x11, 0x4003, 0x0)
socket(0x11, 0x4003, 0x0)
dup2(r1, r2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x24}, {0x3}, {0x4406}]})
syz_emit_ethernet(0x77, &(0x7f0000000100)={@remote, @local, [], {@generic={0x0, "04d6d933fc366194a7075fa05c0695e6aea2c2f21eaa29861567a89dc8a53271a67913ecfbb062a84f4614f7aece2cbfdb5319456ca05378730dcf01d997b580f5384d61f052a43c979060c35ba79b22fbdfb1c2c4595e7ff1f99cdc4e0e1fdff9ba3470fa98faa901"}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000400)=[{0x3}, {0x34, 0x0, 0x0, 0xfffffffc}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x30}, {0x3}, {0x6, 0x0, 0x0, 0xb9f5}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
setrlimit(0x8, &(0x7f00000000c0)={0x9, 0x46})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000040)={0x0, 0xe409, 0x1, 0x6acb9789, "d5426d0d43d1d3d32a0000000300"})
write(r0, &(0x7f00000011c0)="f82bd2d1", 0x4)
r0 = socket$unix(0x1, 0x1, 0x0)
connect$unix(r0, 0x0, 0x900)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
r0 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
shmat(r0, &(0x7f0000001000/0x2000)=nil, 0x0)
open(&(0x7f0000001640)='./file0\x00', 0x0, 0x0)
madvise(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x4)
r0 = syz_open_pts()
preadv(r0, &(0x7f0000000200)=[{0x0}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x10081, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0x80}, {0x1d}, {0x4000006, 0x0, 0x0, 0x200}]})
pwrite(r0, &(0x7f0000000140)="db01f74e11a4ff07c6000800abe3", 0xe, 0x0)
r0 = socket(0x18, 0x3, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "0000fd57aabb00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000240))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000000)={0x0, 0xffff})
execve(0x0, 0x0, 0x0)
r0 = socket(0x2, 0x8001, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
setsockopt(r0, 0xffff, 0x1, &(0x7f0000000300)="9d059736", 0x4)
pwritev(r1, &(0x7f0000000080)=[{&(0x7f00000000c0)="4402", 0x2}, {&(0x7f0000000340)="ba55e9", 0x3}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0/file0\x00'}, 0x10)
msync(&(0x7f0000ffe000/0x1000)=nil, 0x1000, 0x7)
r0 = socket(0x11, 0x3, 0x0)
setsockopt$sock_int(r0, 0x11, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x61}, {0x20}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000040)={0x2, 0x5, 0x3}, 0x3, &(0x7f0000000080), 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket(0x10000000011, 0x8000000003, 0x0)
close(r1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
setsockopt$sock_int(r1, 0xffff, 0x2000, &(0x7f00000000c0)=0x2, 0x4)
sendto$unix(r1, &(0x7f0000001380)="60cafaabc00bbc6ff36232f9ad8c92f70491f57ddfd7c64a987cca05f283e2f29b0678c06277ff2ab267118d726599c3ea3c651c7050793766a95d2dbb0e1a67c207c0b0101e1810e225f36ec28b6366a37e9b30c68d4b681ce6650254a94ea854d86704c252dfe88e4b68bc617edebeff6de6ff86d2a3528a54798202e9a757d6587ce64a9d3e777cb77be11ea8fd5ef9", 0x91, 0x0, 0x0, 0x0)
write(r1, &(0x7f0000000040)="78f4752e3b11a2ad4b1c67fa438b5dc9dd4ab6fd", 0x2400)
readv(r2, &(0x7f0000001500)=[{&(0x7f0000000380)=""/4091, 0xffb}], 0x1)
r3 = socket$inet6(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r0, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x81a, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000], [], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffc], [0x4, 0x0, 0x0, 0x10000], [{}, {0x200, 0x2}, {}, {}, {}, {0x0, 0x0, 0x7f}, {0x4, 0x0, 0x0, 0x2000000000004}, {0x0, 0x0, 0x0, 0x7fffffff}], {0xfffd, 0xfffffffc}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x3, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r5)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x100000007})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000040)={0x2, 0x3}, 0x2, 0x0, 0x0, &(0x7f0000000100), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x14}, {0x7}, {0x6, 0x0, 0x0, 0x1000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000440)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
openat$bpf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
connect$unix(r2, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
dup2(r1, r0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x6], [], [], [{}, {}, {}, {}, {}, {}, {0x0, 0x20000000}]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206910, &(0x7f0000000300))
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x7, &(0x7f0000000000)=[{0x8, 0x4, 0xc1, 0x7}, {0xffff, 0x1, 0x9, 0x4}, {0x3, 0x4, 0x9, 0x3}, {0x9, 0x1f, 0x4, 0xc86}, {0x6, 0x4, 0x1, 0xded8}, {0xe7a5, 0x1, 0x80, 0x400}, {0x5, 0x40, 0x6, 0x400}]})
r2 = socket(0x18, 0x2, 0x0)
r3 = socket(0x18, 0x1, 0x0)
setsockopt(r3, 0x29, 0xa, &(0x7f0000000000)="00020000", 0x4)
setsockopt(r3, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
preadv(r3, &(0x7f00000007c0)=[{&(0x7f0000000080)=""/247, 0xf7}, {&(0x7f0000000180)=""/85, 0x55}, {&(0x7f0000000200)=""/3, 0x3}, {&(0x7f00000004c0)=""/140, 0x8c}, {&(0x7f0000000580)=""/101, 0x65}, {&(0x7f0000000600)=""/9, 0x9}, {&(0x7f0000000640)=""/180, 0xb4}, {&(0x7f0000000700)=""/149, 0x95}], 0x8, 0x5)
r4 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8020699f, &(0x7f0000000300))
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r5 = socket(0x18, 0x3, 0x0)
r6 = socket(0x18, 0x3, 0x0)
fcntl$dupfd(r6, 0x0, r5)
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
poll(&(0x7f0000000180)=[{r0, 0x4}], 0x1, 0x0)
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "f2c05ead98d8fd8c89f949390288db64a35f1621", 0x4, 0x21})
clock_getres(0x3, &(0x7f0000000000))
clock_gettime(0x3, &(0x7f0000000100))
r0 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x9)
close(r0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3d}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x4, &(0x7f0000000100), 0x4)
semctl$IPC_STAT(0x0, 0x0, 0x2, &(0x7f00000002c0)=""/155)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x26}, 0x2, &(0x7f0000000200)="fedc3ff77c4517dd50075c9bc1ed8fa8e72d48029f6df57177b83c89b01141f52b7642eab8b84dee8bcfe0a335078a10bdb4eb", &(0x7f0000000280)=0x33, &(0x7f00000003c0)="0c93c293fd27b56aa91ad595cbb7860520dfd4232a2dd86673c0b5ce4963e38eb9e7813c5c519147af6bee94029d9a5f5e923d69ad2e33fb854f5bd91eb928d3b15b409f1c030319de2b42a71e8a54ee74d64567df36916aac7f8f28d39b29aa62ebe14f1ffa35", 0x67)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x41)
r1 = semget$private(0x0, 0x4, 0x1da)
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000100)={{0x5, 0x0, 0x0, 0x0, 0x0, 0x1da, 0x7ff7}, 0x7, 0x3fe, 0x80})
semop(r1, &(0x7f0000000300)=[{0x2, 0x9}, {0x3, 0xf98, 0x1000}, {0x2, 0x5, 0x400}, {0x3, 0x77ff, 0x800}, {0x2, 0xcd, 0x1800}, {0x1, 0x1}], 0x6)
semop(r1, &(0x7f0000000240)=[{0x3, 0xffff, 0x2000}, {0x3, 0x6, 0x1800}, {0x0, 0x8, 0x1000}, {0x1, 0x9, 0x1800}, {0x0, 0x517, 0x800}, {0x2, 0x0, 0x1000}, {0x3, 0xffe}, {0x3, 0x9, 0x1000}, {0x3, 0xfe, 0xcf81320aaf5ac8b0}], 0x9)
semop(0x0, &(0x7f0000000140)=[{0x3, 0x101, 0x2a6c566928f6743}, {0x0, 0xfff8, 0x1000}, {0x1, 0x0, 0x2000}, {0x1, 0x2, 0x1800}, {0x1, 0x100, 0x800}], 0x5)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000180)=0x5)
ktrace(&(0x7f0000000380)='./bus\x00', 0x5, 0x18, 0xffffffffffffffff)
sysctl$kern(&(0x7f0000000480)={0x1, 0x17}, 0x2, &(0x7f0000000800)="103bb0a35d2771302a0e47ea4e96c803a0b684e7b6899bb7c0bcdcdb488ced4bfafbb5c291a3475540bd58b172cdb7e01a124bdb17a6772c9fa700adfa83728e45bd3a0766fda71c413c506f02aa799a540736e0df20813a64d133e25f88bc28418172139640f66cb338ccebb7da3369cdfdc6029eae49d0ff717484c99fe7ea94d484b23f7ca836ef0a8f4e70637514e369436c6f92906d139f66e807895651409df3a58b0c48d912049cb318b681af50a674dde53057e9694ef1c5c3a116d615a8e87d19615c82038023f0b00fd6e25f44f4b4645b92599d9b2071b1e6532f59d2ea0b5d60649a85c7a1bda4a3988068b9134f136d635594ab5055ab75a0b494101c7905e0c55c12877563c41fafaed9a7c83cd214ce9d692b825d802a6eabfff133ddc94d6f066f3de019d249399db92248035ac06fe69742052daa87e4e55a59f170e919f4265dff26a17b7593c7e838fdb39612dff7195d044f2e9c3241086d656a6f731e45057ce3711aaee86a5c80e152a3f1cd88474666df6d47fdb56d6100efe8577697f48a387d2d5cdf90e8c433c51cb736b31ff9ecd0845421f9ac178c20ccfa195c36376189cc5d84118741931efd67fc83944f0504381a0fcc69a534218f4bdc208d3f7672e4ca0108268e9bf97eb3fd58de5703c4b21e5d6f7783aa74831e85789485522574e7061b860e999f639db086b0d1b9ef45ec24cf3b30b41a6e7bae64a9ae919d46ccc93b80329481905c3a68a6986639ec3bc7e9761b42109dc3e01d0a902c9fdb0255c81df895538c24843d3e8da2a20a509d138e3213c88cd44397573c44f8266c0cd68179bed0972df0fc1e0d14b25e02f3a30298db5bda08430906614752e5f69fd2f88d551cf40b2bdf02e1572cfd08fae365124273f6eb470d469ff7d9ad252718c8b93c17956083163ff53832019feb254d22cb99c3c3d477ef7068ef7bfdee7500e3f86c404d347d2d691ac0118885c592fa55295ef4d4afbb603bbdde6c84ddc930a7d2cfd91cdb9201c43f12cbb596dcab41db7627e9ed636ea67428283340d8bbf01a06138c9918fa6e59aecb3846b979b425abac77c28a4c85d1a6c69b7576af0e994bc56418b29a74543e5ca3dee468b0aee2ecdf3de9ca881749c31e272e6ad0a66c2f1c7eb82b01de862f32af9a0703f3b4f1bf0a05df4a4745a2d106cda2812635f02ad842a891ef6ec620a0e79df2c4b05c8a03839470d8d20eb0cc4bcbd5ad70a9a5ac3589bec77d53a29fc32b6b1e6963129b1ce2fb2a7637a5b305ad159d3365d1de504ccca260920a8271b92a26308a86e0166696b4a89466d35d4bd562aca2bbedd5f4bf978bad3ef7e2664e67e05f933af911da356ffaf2e245734cf81b2b7d1bf52240a804dc21f23ec91f902a46701066cb7ff19cf15b9d5a0f715d92addb09e6503e2ac84a21e05865dcbc142070c2d079833ab632dcc0884c97b18da82f0b129051bdecb692041aabd09de527e6f257280d581f4065ca6c7f612f62e044bd95ec8f3b36a63395aad8de53c4f2a8b1030a5076c58c4bb8be34a66791647770dbbecf90346a720c3681bcefa2e454d386d345d8bc3701a0d27c1f8221ebc5309beace5d149af727e88ddb727150c6af3c62289acad31cbf97587ff743eea15028bf85067b134b33e51876a27177fc291a9444f20e6c688cfc2f8ac1542186a5ee2a18f4b69c38696c38a6b06befacc24e1c46a48d2b7f3784388242730b3f951c513dd83e6add58f488e642e46239c3bbedfbedce2cf4eaac7be1b4f0ad5db0069efd81908716a9bf26a934216625cb7a4f3fb927b6bf22c350d11c0a499d9d2beb3a95c23cf86ebadd75f6a66157eb6c4d4d2f2a71935c35c27da077010378f41b681f24d4d64175ad5ad8564ccda82b3b60b86369e70c519af2e567518bc1ab7164fe41fdd3043172595213082702bd61907656cfdfe9bc441b07b507fc076efa2884356016e508832a016f0029aefb2301172132c12591b83348a28ece7fda3e40aec9fd82b7c79b4c33e803f8a6307307caa94c1918d6673fac929c4f3ed1ac84b3b92e0d502aeff43b1dde2509395e03ea026769843542e61759119be24b0de4c4f830ca4363ef56eca685a75c78187cb310e41de20609261aa7b5408910878f5c80d9ae66a3d6a1123f778e74169c52a616f75b74f4948855d7dc54fabf3ae6ca6666ada28912b8b17c9d8397af0621aa90b51f3ee206d69021237d4d0dacf6510b4c63d1265452a8e7e8126a44730d051180723051e60bec853255b4f86a652802854a3f712d560705d7135c3a2fac1a301ebd5b51c824e6fa431439920e1f7c2ce9d4fa948de2002f9cf954307b5348c49a2a83656ab28de2b4bebe9b8b5e38be54c86bfe700bf83a4b3fd28a2bb4063b14b541eb6e02dd975f73d465ff033b341c594f999257e190e76fd612178440ebbbcfdfcc49721526e20fc0f307bc1784fa8e511110b4586720c0d84474a7af9715f0629be2e799db48d6d01bfd1ef8c65b1703fb7cac9891a3df5b1e547a9ec9e965ab962e98f633c51c7c9e2f9109424e61c8ad9b45f445849fa21606de73f5e69e32da0b542bbdbb329fe9ecdf5f146e0d13c6c848baf35be7383b099ac47e0036104d070843554598180941f49d8c27293156fb1b2c53aafdd00337238ddfc1a70e8f43496151a3753f4775c56f57288d7739c7becb48522e8086e03e49eebf4609bd122d59013315ed6f999b771c9adb9f30bf4209d9b05ce9fafb44c2a2a5611906f0567249c50cea43ee29f5030f19e6b345c116e6be613136f5765a0b14051594b59a0de88deeded13b9bbaa101a1d863f74be1154f910be4f51fdd4a451f2009455957e636a09b16852371ac1accb7fde816cf9dd4ef074323c7c744e073ed6b3a283e34e1e2b107a20834ccd639909a8a69b5d558ce56dba2199370abd3960a93b29e05e066eab0fbcad7a4c029012130f8fbb16518aab866a0f0433251131dede3ef7033e5f7c0ca651b9d2d66dae6ef1948bae4b9afb45ce70b95e79b1dc6df50f209a7a4bc0ae9028af48d63302048f5c6fed43b2446691f552476666f7e0f57e0eec59f3c2d7c324a9651916d5d10ba1541139e59607d9eacf88fcb2d863cacc83b2e64545460244647ece4d3465df85c4a5d214619f0085a14c16b6aafd82bbc59faf8f4405572a244df0f9aa277b5caa9e1e11e5924836054cc84064316842e95d30e2c96422ddd3212b11d90143e93a225082ac6ca8b5e86625bb82500c5ae2f10a95407465efce49e2f0d80c21cb926a4b8f5c88ce3f3ef9cb605d90c9f9098e419c25e97ba9e8efffce4a7bfeb19bfc5b8ab5f58ab61ec9422c5e408cad344883533a8d65e674b5b354c90ca8574934f8f034c3b682f0403d8fc087ee54d0dd76158de4565295af6af48b90bd39577a84abbc2e95df87092bf3e3c7573b2823174251025c933c3318a2d38c6c94b3cd6fdda45c840b04bcc62de746653105ada2ba79b551b2ad421c02128a169ff7238ff678c77ee0408eefa741690be4900bf9d69bcdf5f6082a139a4316ea3730e887b7ed57f9f60628be9ccacd0d0b7febfcf51b7f49ca0fa306cb5c6861f5ddef2bd0828db05d082932bb70ee6878759af104c18f0c08e6e9f490ffee716ab6d9142df4a7da16911e9b3c4c21bd0044615828930644684f5917419fd25eeb5df29f0fdb87e7404e4ae1f052a0016d1505f61cf3082c9587bb94b20adfad867a7338f78576f9ea347bf49a086b808ba56760097a5556f35053d9373877234baa6dc2e3cfaf8d9d6d52d8cbadc6baad876dd93fab78cc001b89a93e16f347025179adc2c8bb1160c661445a81a1bb6397f21cebea5abe3deddb1a749cd8eae0ef41489e0048260b0126f3f8701799472a717f509518e02e2916c1f1abc0b56bc1e3be3e3d3db3483d7e910b7b3894f9239aec4a9fa07c935b2f4f847dd67e512ca9c1830ce56a0942fc55df2f2734e5b8c424711694d214cbc676672140642d1e57bf62a3c214ad450d90af8e729a36fa665868234bc1fe5492183553b78e57ae62734dcaeb4c49b517998b72f81538488ee568740e65ba71796ccaf1a6893b9f758892899bbe150bad672712073a90778508896bfb56c824176cdca63a2712f5e0f6bc92389485fd2bc436d802702cd9fbf9f8dd3b59767d6d1113479c885699e2f88f25362eead95db5f78ea3a08b4ad4dce8403ea1861689b61d572a0904a890e241bf90edae8e2b6aeb0c7b3dbbce6566c672669acbaa7c2b8f893d40f0b1bf1d2b7f2af86737551aa5b8f2fbed9ed9261f3099c0ff8b56117487e7cd48b68368809723bb8140214d26cae307740edf644b485e98074b6f0e786935b73c1948baad38957512a3ab7db7c02d250429082e4b2099b81cd75897d3b678b9db1f722e35b01d41c827033ee30250b2fe339b578ecd04b54bfcbbd2f5e3d4fde317646f67e6245b6de3a434dda96a1f0ed6807a4a1195a4a4fef03e67058361e1799bb2fd213601f7e6cf43b90e533a7b844e0989c2d6a0e3df146d640936edce2f790bc570b48ba7b15f5d73c348f9ad5c900feb9751dba6a67e1fb0d73c2a2afe83cc525f4a18d5f21b23ce78e7b5ae7c08aa5ca706844e02576293523fc6e829814926660ff4082e7347c95643dad86e9afec8944b3b03f0d093219900055d674cabfeda51fa9750dfa0a326434b653ad37f52eb4551a95c6a95da0fb5d3f6c5476d74837e69993e4f677e8ff27ce1ad88a95fc628c39e3975ad19788598f6e7e7366b88d3e1f58b6864030c5b71dd660bccbde63cb09cf76687acb970b1efa8052e5464271e0848428af21d28cab6a74bd5860cb84917345a1b562e1d973d5685e31e1c3632e9a29f9fccd9afca6c57c1f3d648f3346699fef4a7609cbad8edf78bd4751f2055fb789fe06c1e6f364d04d17fd3e67efd112e1a5f1a4c848912e53289874576d7440d9531c801cca1a0070d385db6024cfd6ef7ca08a00aab8f62c53c953b8c8169c69e3a95c26ca0e4137a1661cf7671653f968f55de9a74bb572a13fec3361461b6d811d5498b50223424ec3d7bd8111106fd07cd2f47f3f62d252ee3c8f3dfc4b745a2554d71925c2c0defdb2e3a140559dee0abd0e33e90cab4288abe9ffaf3542b661c747327d01e0676f549c37d4bacecfbeac9943860de8c53d191be00ce349d529fc0f4c0cf1c4066b2fc3e697746cae437c454d40f61f91e1b71daecc2a201eb835311c03584c103b7c4430a423a635635837ae9ee7607a3f39acb5b79d8d20708239afe74357648d93a5b1d11ce6e83c56a01869abc7cb7942f5452a155fbc3b9394b5b199d2f541f2bea75d77274aac539f3363d53043e7e6fadf90c3118062d432b8ef998fc9813461f6b95d73a977183908c19efe8fadaed6737c0c2b359c85f8bf714bb42b175da69dc928177af4dff6489e3fe9684264651da2c34d565ff82cdb3fc9089cc257806f637275aa7d22c259b13a55273ffcf2668975d23253486a941c98f935873b5b290db8675c46621c1da647c0cd0c8efd1145cfc50033f362edf0bd02177a9333b1a6b641e9d1863b7ca182c80d3d52c956cb8deb55ae6b4b6026ca8b1bf36df1014626fcb7a611c4a09b77be059d799c9de72a64d6fa1a760e0e5ab33edd0a437b2b201758b87ca0332f39299ea6ff732936facb08bac8020b8d27032897fdd4688bcbcbc29e15066a1d0fcbb3b8a52159d05673f4e4a8f964b0821394d21a2d647e800e8789", &(0x7f00000004c0)=0x1000, &(0x7f0000000500)="6856d61a733ac0ad425b796b5e0b9ed90eb5fbcd149ea8855eb11cda65ce89edc4744da06eca1bdea27b33fa11aa10480e136e43a38934f39240eee15fa2e5d26c1cd1f07b1f970ccc561e07a3ecd19be9dcbb43366309b983ddc8903cf0b54a8e3a3921ba9da2447a3984b0df171e41e99684a5ca0f0f22b49f41d22872a250c20ff05309d130fd1b10663ea89f613f6261c6bf21f30a", 0x97)
r2 = semget$private(0x0, 0x4, 0x1b2)
semctl$GETALL(r2, 0x0, 0x6, &(0x7f0000000740)=""/181)
semctl$SETVAL(r2, 0x3, 0x8, &(0x7f0000000280)=0x80008)
mkdir(&(0x7f0000000200)='./file0\x00', 0x0)
chroot(&(0x7f00000000c0)='./file0\x00')
unveil(&(0x7f0000000040)='./file0\x00', &(0x7f0000000080)='x\x00')
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000a00)=[{}, {}, {{}, 0x0, 0x0, 0x0, 0x76}, {{}, 0x0, 0x0, 0x0, 0x0, 0x4}], 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000008c0)={0x0, 0x0, &(0x7f0000000700)=[{&(0x7f0000000280)="8f6b85fbfd8d860afd37769b92fdac18a29a093abe654d8529d85754fd12db8a7ba57b97a9e7fdb046e4ae90957e27b60378864df7a1643cd963c9f44afcaed94d3b66136dbdf55792d0cfba479a5e6f145c3e3bf23f734b70aafee920", 0x5d}, {&(0x7f0000000300)="6dde2715b07698044d87ade5d72da02a56b628a30d61f9b591f0921a571728ac0e7ece217b7b31d19ec3287eb0781d29b72cbd12868f819e488980de2e2c99e941573b0043993fa52fe7b0fc998ebe145fb1a96ebc91932c46d1ad1286836c4ed4010417", 0x64}, {&(0x7f0000000640)="fdb9a0aa681920215ec34e1881", 0xd}], 0x3, &(0x7f00000007c0)=[@rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @cred={0x20, 0xffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, @rights={0x30, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x30, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}, @rights={0x18, 0xffff, 0x1, [0xffffffffffffffff]}], 0x100, 0x408}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000880), 0x0, 0x0)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x2, 0x11, r0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000040)={0x0, 0x0, 0xfffffffffffffffe, 0x1000300000002})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4c}, {0x24}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x25}, 0x4, &(0x7f0000000040)="311d000000000000", &(0x7f00000000c0)=0x10026, 0x0, 0x0)
r0 = semget$private(0x0, 0x0, 0x0)
semctl$SETVAL(r0, 0xc977eba63e433206, 0x8, &(0x7f0000000080)=0x84fd)
semop(r0, &(0x7f0000000140)=[{0x1, 0x7, 0x2000}, {0x0, 0x200}, {0x3, 0x6}, {0x2, 0x6, 0x1800}], 0x4)
r1 = msgget$private(0x0, 0x2)
sysctl$kern(&(0x7f0000000180)={0x1, 0x18}, 0x2, &(0x7f00000018c0)="6222fd9d5533db610709f8769c623e612c8418fb1e84c9a7cdf9b0fbb374f0691aa36c2cac5300500f8d7e3789bfae6c9c207c08409f1049fb09ae85c7a6a70e3f3d8bc3e357f1fa5b3136dfbc38c5dc2815df510e43f5f53bf7c7288a2b3b9f07d5d27d8540bbbd5d17eff1d3ce0daa9940e7bc4d77b873a2be89ece2f6d9478adeb25d7f089dc6e9199d1ee6617f14ecf70c2637ce6ebbe0169714d61761b0f7610d758d172a78906bfe42beae4634dc42cf438b41a64215cdea894513c66486bce4283de339138eabef5b1edab40931b5c8a1db29ddead3b641ab778d5925209a3cb547f54a9f3076f093c52b2c288aa571b562acb39024007cec857a9bc38a65ba7c3a992a1fd3a3bfaa17efbbb54b4bc75add5db977510e882025118b51b761793761df84afe7493f5faf4b32247b1f252e45422d13d475dd2da6a5437281936fbd62db8bfdfcef672d85ca269b07ed7b67c2330077353e1586933d883bbd9660a129d3031fa359e879201ac9f5e9bc7fde75ebac85d1948efa859a52b19d58fb7a26c7b4196b2b69a7395bfe3b5e7da8fb07ca10aaded6d4f6a796fd614312ed00bab2947e14bcf6ff3512e796cd1b2ba4d0818792b87847e3a860b2bd05f50215a1bb7f25af8f9ca08c727b6726b9287b276af15860724b3b4432f833ac2ad6a90bf3abde8a4ad6dd0f402c76b30924317088c23db7cf9bcaf2af419a1994921ec389e06276df215a76b7e9a7e3b8ea244968594861dcacf97dd6c97ca0b5d35e7f5ee6db5c51dd635b801d0d4f9ce33ac48a980e9972ad27de98c52422a4df93974715bc90c2d18313ab30482d932e460cfdeab33fb302cc4c992d89be058bd09b9e9408802db4b2e9d29fb5bfd5927f26f37bd01954559cee1184e49855e92c32763bbc124fcb18ba3307eed219613b193611af221ab0304ee0693d79ea418c52f8c56bdb15b5912e6c7226772363fc43b0a8d076dbe5812b71b1ee6b9f99b0e58c3fe4a9a7691bb2b50a4bfdecb2508c592f22e4397b7f9765b90645d817a883c530da72fe90319dc346e52ab8500e7779ae8cf4215eadf471f124d8c17eb2489b0153a9b6115a7939e41ddce8d71d5f6acbf6306168676e3d1b6f63c7e12808837be7f35137cbd331dffa316957ae875d49ba919efc54f0b5a63da78b522b300f29c9848a0673530bc050c9945a413f4ad739bd0caf57fdae6fbbaee70f68df361dbd57ff5d19bc084f79d5855f692e5415e616094edac375245617380cf35eaaa2621a58b404e721360003be842e6e5f891031f07d72bdfda6898141d7c317a5340e9f8a4836fb757c712af45104bf5370faa9f19b49cfb7a72fbcd7c58dcf46cf4b295e55069cf5669babeb7e0e9c9111107938a2eeb60467a8a3f967d91741729a4d4de1b4db95a01883edd41e7293700fb046f437c79a258af717b90bc10cf8c348eb2193bfdb07be3f03654f34cf2db9cbf099baba2bf7e913dffe78d9df8f7a85cd01f5d9e9a442298b420892f9dbdc5a3e8c7bfdcb648b25081262a1333cd404677b0c951d2c5946ccbdc3b3f34de1981f54f92394a5dae45cb31eca20ad4e18df67250ee6555274990b59d1397f7f9903f8daef8584f02fa8e4dec7152f7237f36caa5be08273f2fb73d17df6b83bd5c880929dad4558a4e2511b75841c1ad30273901eee46d41cb088e3c1015973c1a25694d028ff3b76b6c5eca2545bf0275ea4851c6e6d67044e56eb804a17e99f7ffa431720d9fff19417b084d84d245f45505c22bdf339c10535801029f195b4fe7d309544d31a02c57a261738ff4c2466d823a439c19cae5b14fdbb69dc6e9f2e6d4d0571ca735ed8b44adc73b4d761340b4b70c576f5d6c5308bd2080273b4014c985a93f0e91b4133c6cec2d44ddbea70293f44b18e4476178adc50bf31f66314707d8641051ac57f055eb546f697aa79ce3307b182c3bb7a13889517deaf8146d6622a56b91a55c114dc4d2172d0d1cecb0ccf80b65986566c3c11d0c01034485c60011e2aff4526a4e0a7c03dc5db3bd39d5efd0fedfe2391664cbdd9278259713e8da9f5bb2c18671adebfb0721ebb129d1fa2d4c1d61ff441b83a72fa763867fa40c484a01142b49622ad32415384e2644e21ddbd75c2860f0ee87e14dcb70e59c947757b99c84c264437911ffc3579a612b6fe0e54c9397a8732a83ec0b0a7db4c697d7473e8e3448bb506cdf4340a436b0c755edce751dbed832e0f0323c250d76d1aee3d9f48cc45a350c196b6272efbac73d7f05fa65a3ed18594cf2237c464cfa8a6fd81d896d414081c17f7d44c844580088015b3d58b6f11e7316434f92af1c562c13454f218109cabb07620c0066d80455d5e530eb72a7bb15d4ccc97705ba5be6ce9f89f78e5d4288c40dd798c5b65f6bc08d9972e88585c1ad55e674338e16ba01deeb2149bd6b88fa8685894e01d8ec785eaa1995d59b46af9ab98220fd17da5b4e709702a7265662193487cb4b77f14e2bfab4f5d82fac2e6218fa9a7c5bf0f91380a2c28bb80886dd6fca09d6425047c894e7fb8f833769105a2a54d2db4d4406b8ed5785a07e245cc1548b46dcccb5f692e5c745585c7384c2b1c6bfe09768c6b292912e3736060458043112f1f23beaf71beda574e3ec74c6c0115ae39511677df598bf6ec6cf2c67185017e03bafb3d427342b3368a65fc4516f7a359bdcbf1268962d046f37dd4f9701fc78a924c065cf58cc59ef749d1b3b0f089c179232c16e205056521878c1148ff9f1cd6f7c5495fa9dc373dfdbd64bb92762a75c2668fdd242003d6e8086519c5fb9de6b3a583c9f24a58ff650a93ae4dfa724fdb6237c20c2e2a8336057a18d86958d312f0f8d9313ba7adc3819567a7b3fca5b8c3666b8c235e3595847f8cb994c4e3a1f785c705503edaf9ab63a69558903e6c97680d302a17ef892f2544790e03d3c88124c94872d0404a400cd9cc547f0e7ff68bdb8eb6a909a99c9a4cf92973ccfc9c7a11a727c02b6eb90fc7b88dc8e870e4f3a84ffa5483b86d5e22ff2402028b91ff1102c9b69c13cd13da0375f2ec9e3fc8f77910a86a94a868528b567f400ea7dfa9b772d469b5cdac668b0e042ada184cff454d5c541e77cc6024c77921207d6be86899d488594cf0e1f1a69d9dae8d995e67c6f32f114942aa3b9bf6402997f2feef0b52670ae13257de28bcd13aa6be94669dfacf1f2611002f9357d0a3e919613dbd8c7750ce76d6be97d541ccb269deae5ac8e78b3c0ef0cc9d70057f4ff1efc4e93d3394fcc7b76e2434e1d1540c209ebedfe643513f44c3232016280d31c666e495119a21513416f7b4b8bad2f24f3badd9f2d492bcc0904b6961ce2b45e95d89788f2b38464112b938040407c8a3cd28af2d026b5aac2cdd914572be64aba08370e6895a12ab50b4f9436e1967786b0aabfb682815c748d0405255cc9b3d4cffd7e0b6c0c5d35095cc0cac37d0f18d2bd234af3510bd95ab32c3d94bd72657938c23a08b78e20e5d199098ffd7f6de5406c74fcad1f15ff46198daf001dc119d50592b575f3a4d9558c1314f2155cf36fecf3c0352521e69ba08d8969b4a59049444a0d3d60289f00c7bfdb9cc1f66a3df130361167675da6dcc06161112bae697c5a5f0f8a066f713336c0f2d1610c7bf253bfa168db30b0be887551dfe6af6be45f0d6008d3e3673fb7860cf3df49168cc16153a2a9e6ecbc6a9b3ac248ae35b9c7369fa63748df121253597d10dbcbf0d13aac5ffd5d79cdafc89cecef30b03a8d86511ef05bc1cfddcdbcb94d6b872f915ae9f8cba5387c2c592e2afeb3c4af53b7502e21cc7dfc048e0a729f3f509817d510cbefdc461c9a8264977184d5740eebefcc94a5d93a2a8ccf42621ac174490ffad78071160a4219710d9c094f1199562a4d56651e0ffd7a3e77aecba07e50193aaeb91cdd5676810f9d9c7a3a0b39de86add61239394212496e198b559b3bb6d33fbc902112df868a135b83b2c385ff343071c12781c5aa07478d8ae15a90ee89a333b5d373cca5e0d1d21632fca076e51f4b56fdc458e45e9849c65c5de6b02d62f21960716e592c10dfb8008b89a8debf8c274b0977812925b7e53cbe7d8825ac601e593717117255cba4aef530adb6233240ab96d2e09a96d8dd3f146fa18a9d51325d3440a0da132786cc90d1b67e3b5836e0c4294b8e11e5cc42e1f88f836932e8529adec8dca05b36b8534bac3921e7835c966d071ca16a0c87a59ac486b28a7dba306600c7894a99193a19fa2aafe2c0055cebe88107ce36b905a42ab2aeda6a7a335cb72e03479c49bbea5c667fa815ed9819e3902860499d84fe8aea5ff8893e66ce0f84bee7524d9fa3e33504259e393aa2cd05f8089bf027b0231e5a3fc7dab520c5b7c8616290740006e23e8c9941e698cebb3dc5320326bce59c0705d6f78b4dc2a90438930a966b24204601f2e1104d0b1b128b1da940c1ab10361d33040b6ad89244d41161970d7d122c0be1518ff809f2d35247a702b4e2168163e2ff340c34c0714634b18e21cfba6e80b86204ca5da4b96c4e038e023559181244e35b5f641dc6c02b860a289825cab2020a64ee3835ed7c1727998df7fc268f51410c5ef9f3fbd22a37153bfbd3770e5c3d97fe12ca04e4d1b8ba84f3e2f6551b4c21a60a137fbb92e61cccf458d46b48567ec16739cd32a8345180fb28564bfe22782a8e8b01bd35cdac60ee577f05d1c358f39d41f6c9a8c6969f9ecac5754b988b4423da920147bd998dda7064bf4dc7c83f6f4d669401e0e09d4ab59b4b69efb877985c57b80f3d3cf5438372193f1049ad9720c28f8e3e464d4e53ece63315b0a6d72e66a64dd13f3005db81f744b4c9b562202fdfb57bf285972045e1ef552e35e8ebc4f99534578999f151e861b758286acd0a379292983e4f32ac77e129d05de66e74617cd99d26d0f4d53f6d4e6adc7889c5bbfcd9f3924e3495e90b391186608e01922b1c5847342b3e773ae823ece990ccd9113e96b6c69924b95b907e38d835f19ba1629a61033f4b2d45ef657d341c094bfe84ee9c506d8bccbdd335379e179503fafb7ffdca2d0fd9a8849f7f372ff50e34db6860f790e1ecc0023969bb269c8fd2d2e080ca4ead857aa87463d52b98d8ba5d51b430894acf3981248f5d629502ddbdcb4d03ba6da128f209ac07c0bc9145843bc7efa38c0b57a5de5932461814cd1dd2298242897b1f6db88dacdf2702967581625e500b59d1f9e59f7d3a818260398c0967c5acdf33c5edfcf126a106fcbbf13d15a0f738effd051ad16ef5ad42290dbc6e9534af58f176db7908f9821abd9b38371702183378b9437ecd5de71d83e770a47a40774a9b312b7933e049392dc14a5dc58f11666ef458bd6ad7018a9f6066612519ed1b605adbdd91923faf3f64cfca1b518010913ec98afadc24c1f8ad76c21b1f8dc722c16d7d7b83ac2b947977f9e0ac612aa4e3996333021f454a1764447eaf4e1a58005c23b01b6e489d67d473b093c8c1ac5e8e8744d73bced71a560d14e49325a8429bc92aa53d372f4ec66569f65bd3a12c8837197df26ef1f51a2f5ea95dcea0997b9b390fb0eb63bd60f447b7d5039ab1dc583ed07eb02ff36bd2b31f59119a2944ece3ce837340d6c6bf031106ceec4d864948deb04429b134c097ca3659399a9c4b93fe83db88764c104b7b77b519a84df9efaf375349fd9113c90e56cd0d2e2b63ae9d135300097e90fa6c2fcc78c11a074e2d757d90a76b3bbfbde0d8b", &(0x7f00000001c0)=0x1000, &(0x7f0000000280)="7a269a422ddbfa91dad0b16ebce413a51f31f093c5645c4af5abede7b7db75340a6d8fec59afbab58042ac6e8a921fb7f1daec883dbbd932867d9594e91cd2a300c135dec00ddfd79280ade9890dca64ebc72b69882101bbcd0e30a7b13beede9a830de0df072fafcde54f866ec78106a1834a0f6136caf8556445c2691f5c2c2d5dbfa303cca8c2af5ef106f5a60e26b57a332e19c1d7290bdb98f1ad29a6b47f13de859b3f878bcb09ed059def185b87c4", 0xb2)
msgrcv(r1, &(0x7f00000003c0)=ANY=[@ANYBLOB="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000005ccf598733ee"], 0x6c, 0x2, 0x0)
r2 = msgget$private(0x0, 0x1c)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
msgctl$IPC_SET(r2, 0x1, &(0x7f0000000000)={{0x1f, r4, 0xffffffffffffffff, 0x0, 0x0, 0x202, 0x2}, 0xde3c, 0xffffffffffff97fd, 0xffffffffffffffff, 0xffffffffffffffff, 0x1, 0x0, 0x8, 0x8000000000000001})
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file1\x00', 0x611, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x4, 0x10, r0, 0x0)
mmap(&(0x7f0000003000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000002000/0x3000)=nil, 0x3000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000180)=[{0x2d}, {0x1}, {0x2986}]})
syz_emit_ethernet(0xe, &(0x7f0000000140)=ANY=[])
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x5}, {0x4c}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000340)="f0529dbe13eb0913587ca9eca5e5", 0xe, 0x0)
execve(0x0, &(0x7f0000000000)=[&(0x7f0000000100)='r./'], 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000340)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, &(0x7f0000000000)="cfb6fb00b3e0485529c57ae981e6775fe71f7d642ce80d84d364a97490ddac709bc2e6c989f4359792f86f4783e84fc83e7f309abeb7523099614a49e208723d9fc489aa53e2fc7a6e892a766ec0c98e1b7e8d351474812f7442143e5ae4cd387ac07c16573b18e29e119a41fdaaadfa7d5a03d92e50cbb3c97f2f87279fddeea24d3627d0638cbd8c64bead2976081e9e", 0x91, 0x0, 0x0, 0x0)
r2 = dup(r0)
sendmsg$unix(r1, &(0x7f0000001500)={0x0, 0x0, 0x0}, 0x0)
recvmsg(r2, &(0x7f0000000100)={0x0, 0x0, &(0x7f00000002c0)=[{&(0x7f0000000140)=""/161, 0xa1}], 0x1, 0x0}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x21, &(0x7f0000000040)="b1f5d915", 0x4)
r2 = dup2(r1, r0)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/28, 0x1c}, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x800, &(0x7f00000001c0)=0xffffffff, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000), 0x10)
r3 = socket(0x2, 0x8002, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
bind(r2, &(0x7f00000000c0), 0x8)
r4 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r4, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r4, 0x0)
write(r3, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x2, &(0x7f0000000000)=[{0x35}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000500)="d000ff1f0000000000001b000008", 0xe, 0x0)
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "f800", 0x1, 0x2c, 0x0, @loopback={0x5}, @local={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x1, 0x2, 0x8}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000001300), 0x0, 0x0)
ioctl$BIOCGETIF(r0, 0x4020426b, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000000080)=ANY=[@ANYBLOB="ffffffffffffaaaaaaaaaa008100000086dd60c7737b00103a00fe800000000000000000000000000000fe"])
r0 = socket(0x18, 0x3, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="820200057f"], 0x1)
r1 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x16, &(0x7f0000000040), 0x4)
connect(r0, &(0x7f0000000000)=ANY=[], 0x10)
writev(r1, &(0x7f0000000080)=[{0x0}], 0x1)
write(r1, 0x0, 0x0)
select(0x0, 0x0, 0xfffffffffffffffe, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffffffffffffffff, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000), 0x0)
r0 = socket(0x18, 0x2, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000080)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="14000000290000002a"], 0x38}, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_timeval(r0, 0xffff, 0x1007, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000040)={0x4, 0x2, 0x6, 0x14}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
close(r0)
bind$unix(r0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000140)="74e201887d347d495fd8ac50", 0xc}], 0x1)
pwritev(0xffffffffffffff9c, &(0x7f0000000580)=[{0x0}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
execve(0x0, &(0x7f0000000200)=[&(0x7f0000000080)='/dev/speaker\x00', 0x0, &(0x7f0000000100)='\\*$^$$!,&\x00'], 0x0)
r0 = socket(0x18, 0x3, 0x0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
getsockname$inet(r0, &(0x7f00000000c0), &(0x7f0000000000)=0xc)
r1 = socket(0x18, 0x1, 0x0)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = syz_open_pts()
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000080)={0x6d7})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x61}, {0x5c}, {0x6, 0x0, 0x0, 0x40}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="be85c4be84e58fb44c1cb8220d64558f861529a8422491a90c055e83012f0da3c444598dcea2c2d2880bff4870b078468aff0696452c5d273b7887bb027290ee0946e15b55b8209bf997ff862dfefb873c0704e3ee4dd7e39e76e62f331e4adf0ca3a853543ba1d43a1b6e39616087c30119ba0cceee30e010dbd858116debec8b", 0x81)
setsockopt$inet_opts(r0, 0x0, 0x23, &(0x7f0000000080), 0x4)
fcntl$getown(0xffffffffffffffff, 0x5)
wait4(0x0, 0x0, 0x0, 0x0)
minherit(&(0x7f0000eb0000/0x4000)=nil, 0x4000, 0x0)
minherit(&(0x7f0000a01000/0x3000)=nil, 0x3000, 0x0)
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
symlink(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='.\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000000c0)=[{0x54}, {0x14}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
pledge(0x0, &(0x7f0000000100))
pledge(0x0, &(0x7f0000000080)='vmm ')
pledge(&(0x7f0000000480)='\x00\xa9\xe8\x7f\x16\xb0\xa1tw1\xc3l\xdb1FG0\x01\xc1\x00rc`L\x04\x1f\"\xaeqW\xafyc\x99\x06\xd3\xbd5=U\x88\xec\xad!\xef(\x00\x80\b\x00\xea\xfb\x10:V\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbd(\t\xcb\x11I\xea\xfe\xa3l\xfe?9\xfa\xf05\xe5\x80\xdd!\xc9(\xa2\"Z\x02\xb4l,o$8\xe3r\xdb$c\xd0\x19\xc0PS\xe1\x06\xb4\xfe:\x17!e\x7f\x87;\xbd\x84j6p\x10\xe0j\xf3\x16\xfc\xbf\x8d\x13\x81\xb6\xcc\x05\xe2\xe0\xb3n\x97N=\x9c\xfb9\x1e\x8f\xf8\xa0\f\xd4\x82\x8a\xc0\xf0\xb4\x8bFe__\xb92\x88\xc3|\xb7\xe58\xb0\xa7O\xe4\x9dn\x80\xcb\xed\v\xa8\xe3\x92\v\x19\x1fx\x83\x9c\xe6$\x99', &(0x7f0000000580)='f\xe1\xec\xa57@\x92#\xb6\xb5\xbfro\xc0\xd7\xd6\x91\x16\xe7\xdf\'\x7f\x1eB\xde)\xf6\xe6\x18T\xc4\xd1,C\xb3B\xd7;+\xdeR\x1d\xb9\xe0\xb4\x84A\xb7\xa7\xfc\x8f\xb8\xbd+\x92\x85\xf1k\xb1\xcdgJ\xd7p0\xd0\xde\xe9Yi\xb6\xaa\xdf\xdd\xe0\xf7\xa0`\xa6\x98\xa3\xd5\xc7:\"\x18\xacY\x05g\x04)E\xd0\xd7\x8cs[\xfc\xc5Z\xc8\x99\xd2\xc8\"\x17?\x92 m\xcf\xe2pF^\xe0\xf9k\xd3\xc3U\xcc\xe5\x1b\xf3\xa9h\xaey\xc1\x99\xa1\x99K_\xb3\xde\x97\b|\xb2\x82\f\xbb*l\xb4\xd3@\x7f\x92\xb81T^@\x7f#q\f\xa8\x8c\xc5C(\x9e\xf6\xd1\xb4\xe1\x9e\xd0,bOn\xa3\n>\xeb\\\x93\x98\xa2 \x87J\x9f\xca\xdd\xb0g\xda\xef\x99\xbe0l\xf7\xf6\x0et\x01\xa9\xdfq\x04\xbbV\x97\xe3\x9caJ%\x8d\xa9-L%\x89U\xc7\xf9\x93\xd4\x89dz#%\xf2e{\xf7\xdbj\xe7\a=\\M\xb3\xc4z\xb2\x972u8\x9aM\\\xae\x93\x8d\v\xd0\x1b\xb7\x04\xe0U\b\xc2\xa0\xe7\x17\x8a\xd1;\x9aU\xb1A\x80\x8a$\xffVv6])\xe4\xbd^K\xb6Jc\xb5\xfa\xc4\v/\r=\x1e\x16\x9a$~-\aN#\x04\x0e\x03\xbeX/\xac\xcd\x16\x80\xe4\x9c\xe90!\x1a[\xf9\xc7g\xee\xf1\xd0w2\xc5\xf8\xd7\xe8\xf1p\xd8i\xacW\n\x8b\v\xb5\x81\x88bkV\xb6\xa9f^Z\x02\x90\xc8\x04o)T;\t\xbf\xb5+\xd1i\xa3\xfd\xef\x92_\xf9\xbfx\xf6!\x90-.\x83\xe8Aw=\x9f\x9fy`\xc7\x95X{\xf8KB\xce\xbf8\x1f\x94\xf0\x02\x80\xc4\xec\x11\x1d\f\x81UU\x16\x02\xef\xce*')
pledge(&(0x7f00000000c0)='f\xe1\xec\xa57@\x92#\xb6\xb5\xbfro\xc0\xd7\xd6\x91\x16\xe7\xdf\'\x7f\x1eB\xde;+\xdeR\x1d\xb9\xe0\xb4\x84A\xb7\xa7\xfc\x8f\xb8\xbd+\x92\x85\xf1k\xb1\xcdgJ\xd7p0\xd0\xde\xe9Yi\xb6\xaa\xdf\xdd\xe0\xf7\xa0`\xa6\x98\xa3\xd5\xc7:\"\x18\xacY\x05g\x04)E\xd0\xd7\x8cs[\xfc\xc5Z\xc8\x99\xd2\xc8\"\x17?\x92 m\xcf\xe2pF^\xe0\xf9k\xd3\xc3U\xcc\xe5\x1b\xf3\xa9h\xaey\xc1\x99\xa1\x99K_\xb3\xde\xba\b|\xb2\x82\f\xbb*l\xb4\xd3@\x7f\x92\xb81T^1\x7f#\xdde\xf1\xab+R\x82\xf9\xc4\xe6\xb4p', &(0x7f0000000040)='\x00\x00\x00\x00')
pledge(&(0x7f0000000000)='\x00', &(0x7f0000000180)='\\\x00')
r0 = socket(0x2, 0x3, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f0000000000), 0x4)
sendmsg$unix(r0, &(0x7f0000000ac0)={&(0x7f0000000900)=@abs={0x0, 0x0, 0x0}, 0x8, 0x0, 0x0, &(0x7f0000000a80)=[@rights={0x10}], 0x10}, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x8020560b, &(0x7f00000000c0)={0x2, 0x0, 0x3, {[0x0, 0x1]}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x480002ff, 0x0, "003720ac390005000000000000000400"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0xa, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sysctl$kern(&(0x7f0000000040)={0x1, 0x37}, 0xa, &(0x7f0000000000)="5bd1b879fa3e6b1d486dbbab31d0774347a0c97a8fe7e033266193cb5c8bed520b7417a3acfa", &(0x7f0000000080)=0x26, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x25}, {0x3}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)=ANY=[])
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0084427, &(0x7f0000000240))
r0 = socket(0x1, 0x5, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1002, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
writev(r0, &(0x7f0000001e80)=[{&(0x7f0000001c40)="3e98ca652c71a386961db5754b46517f926310331a1c68edb685e8cd968e8770231a9d3495e728f73f2774d5b789a4ad9ab106662cdfc957ba0870abc1affc80e54ac0d3de598f9799473d80e1099857b97dbb13a8a6d5371e134175b6a6974e251aaefef980457c513b44fa2f1124a15b81de185bd0242b7a5f5417c0879763bca7e41c96c27f8021b3ef104d651bdee78f8b9c10b290a5b6cea3fcfc988ebd54b67fc98b5ac355706b95220e1e646cdc4f2566eddbd2a6254e93c5bfa31e885b2039a193ffc969e88af841a624c42766a96c61f8af9b20e07fdadfac270882104ad7c705a83d1b88ce529909223505375f4d215568fd14efef951e2f1d35c4cc6a0ef88f6cfcb356698ab769368e0d2314b66d704345ee91165d72554ca37bb5ef846f86cc9974ef86b93c1850cba1b571102dfa69c3291715a72e2774cb7d512e491facfc5c5f", 0x148}, {&(0x7f0000001880)="39a1a184e26dbbad8b2db00c91a2895b62ed3ca64316feae215477eeb6b03e3d11a7e11380b050ea17904bc78da7e815bdb91c2f0805e74b8627b1dee872a078ec4cb5a175e4a4bdb2567f8e006219b1ea4868a8cb3f0e3c413f05b51bb1ede085a4573c5447e0b80931df43abec7559cd26e7c43e30ce2db0e4e3163205b8643fa8d8e09e121e31ccf8f7ade2178c2e51f3bff5920a26d7929a9537f08df5a95e215ea5a71f3958851a17d3e3a5454d4b489251f4b2ba61df96b736ad27e31233d50ce20ac1bfd66445b6c70a7cf701cbcd397fa95ec6a51f990266be21e98801", 0xe1}, {&(0x7f0000004640)="7a9f8e56b14bb26ef4df3b9ba9e66688c11326f01d5c8a233fd0acaada882e8f72a30beb2f3027838633bb41187417d400d0b15e5f6f33fa8886bbcda55862385688f5270d724b9ea9439181a215f8100d09d7f6cde099266914a8654af7b8efcd6e9b64c857c4e88be9ec1ed0d63f8dcd7e9a91fee7f103a1d5751215d5983ca7f3dbf569591ddceb53c675b5542797cfc71f9e742b20d53f319b11ee39794a4bc518b089ff48a02e0f0dffbf7dc3a3596551b7fc3125685168ada9e4c63f0647119d89e8c6b5b29fdd447a922bb7ed2d184353f5bab0f1fbdbe46dc120ff1cf6a14d6b6845c297ff05e2ec83df96bbde8ba60a9638231056e1153e3e4caf4317001744603342a93cf63ffdca76b5a919c9c4a904b04432b4703077dfa41677ad626ea8ce36b75a476a4b0bfff149b527d660b3d04bc6e9f306556e3128c17cf0deb840eafb80e58b2e3c8970a154fa1d8bdcaca6d7fea562ffb3fd16c423177479e5261b34ce75f81039f6f910b65c0e55cabc9d6aebfa7e8dca25c0715a22fb882b26bfaff382a52b30d700527cdb52a6a1fd4ae147d48c2991d81d9667a7481fb1372fbff3ae7c284658cb0b68825033119fd73bde3113ce88f94c751e3888fe23030d7bbf26cb6b1998f24d6f0a45554edfc9ed7bd0638cbeca1fea680b340ed04c84d4fd5cde7134b293a9e43d4dc4cefd504c5bf49cb76a21945bbf35cefdbc73038ee60527e2106731fe41414e3dd7aadef66a3236319f9e1e291c5cc952214ac41b8328d8f9be7bbf72f60a2a68c44d3e866c65822b7d7fe10bd35377e7acc4f88613d166ea08203b6aa76adab784d8daed623e0775b73d887323cf99ace18ef75945f3849d9a5de37d4de42a9b6a0383a6570d46a4ba137e279f29ff1941f9ecc5ee712e39cfe39ad7465bf3ace68b95f3aa7c0861f8d52f628ad26348cdae709580f0181daa33ed15a675b01f157f18c8e65cc2258e915083fb35f1ec97daf69c23691bffed58cf8c02ebb7229022ebff7f61fc407d2f6e8a8917fd204ab738e7c967242a02741e468137d254ad4478a5a93c8d798223eb0cf43003667557fc99ed8a04a9b7299e57ff199a1fb5b9a429f7376dd58cf42e71048853ecc7c07093a80a107bac32c9d6164a740ca61604750d7f03bc66809cbe8fecac77d32d93011d6b6a6417005e78c7b2df1df243fc1b9669b27b29466cebaebf6bf2fbe40b1c969e34477281b43d1c3d985e55e53e3c8cf964b5147a02faf4fb47984338393b019f7891b0b904c8ddc7ff5fcc1145b3aee17883a7000b5b53363aabedd18616d935aa45eb6977845ca75e40f474866e3ae2f0cc1ac1f7102d1bef964631b3e73ddf6dfc1b87a107e0098abba83544e296a2f6e625adca2aaefd73e24950d1df81248d2c187d81ea66dcbe9e1d949880bb5690b502200b86eec956848cdc046c542665d4d24d2f538f446314054d93dbcb00588da3049fbd908717be23c3346450c2a2876d7e419e7b49dda1cb90adec640b97cf45cb1c32e14a5845592bea02d1ecf0386ffa2a80fcdcb17a75247aeef6ba23d485ac5b1ead95b09c91f6d1e220be4cc2e35b44ae2cfcfd439cdada764fcdcfd4e591aad19ae590b9a91a01c26464f32231d3edf8051f630ce682bfa6a083cbf51191c5d81c54afab24c497588414cb80a4e36ab3c62dedbe84c8bee854e0aa628373dc9a53f6543fceeccecb62bf7f8b29fe3e44f557b6599acad70bf4838427109aaf4130e022cbb594fa3354c5dcc6cabf841186a2aa5a6ddf437b5b8a86850247e00bbdca248c4d8d291871d4743be8ef35b1fb9ee96b03a5a4eac9164bf36522d12a898030fbeffce9b644e60045e67390a2a947aaa7e00ec2b3c64c0320b6dc08cbb678692858037877538836994363129d69b49f0589d2d1943e5f65725c5b061eb0ff4326d271bd6779fb64dbfe24045a3748b56555361fc81a8e93d8b711cc49367fe8c4030c6e82805f487c06af67c11c0ef237cfad8f0e4268215aa3cd927ce4b197373a7983dd110ae8e3f56f098c8afb72a2ee20df2ec23790f4c611ce1872321fe9a290e4558789271de338b0f9d7eb54aa250d295ac4c67594052bf9333f3deffa92d305d8ab6d1534e115628c082f40eeddfb47a815cc4884c6d51bd747c04e982cf4f51cb106d071ee79cb3cb31b0a1900d3fc64fa61a08efddf0062cdeb35309db68ce2ca3613677b0729b133143cd93034df80593bcd25f3b09e3fa01d2af3f7b2ee44d853781b9e38c66fa7741687bcf18334cde33e38ba4eea0067c0382be18e3e25bba43438bf8bae966314144873654d8867d18808df9de194e24e06d9288a9b708abc150157f9b047921421b355b0f8d28e172e8e4722e1490c74544c63baaf61fb32a19054cd4e8e59952c0c5bb4687a9735de6d8f518eb7223a970398bf03ffec0efa734f0bbffb156f75bbc37d063422575e0dae8e094ade68a5fdc426cf8ebfb8e9a668f323fe52a24ad67c5179a55b766327c6a6ed7fe1b8da64b27453ccbde04cec673188e205357ed260ebdd4afc5b6853e4cb131f33085ad906ce9e5f247aecc98d3b23f6ba8445cffa74c280664e5e23022a485a9256dff16e2a673345350197e577c124e14a7898d23a2e49cf5fd97b637e448339cc006315e60af508411f2628ef00e59f512c3cd08679073c0698995a91b63816930f03455d1c916f67176e7b6f3f8bdc8cf6e7e8ea3e61bc8eddccdd60c566bffc3c7ab7ce284b5bc4b172f159c04afeab79de1e344feec060a5fdeb5aebff8e9cbcff7e191be175fc10dc9293acc5ffd8a0ed3ab8881d8e7fe7b1851c54f67bfd3478c8320087936bd364bc154f2559d19f75ab533511f6e0128c74fa7008b0e9da8450002f8bb043595896cf33787a545ac98592a315034dd513fd364afb14241f2162454064d324794c8b95c4eb0b87b280e630296326990ff2f5876f6334aa7ed7a3004c6dee7c82403865b75189d244bcedb5d37cff8eb9c76d004f09e42fe3a48dc857df509fd0c8f42fbbf89e43e60137124b0456a8d01c052952ced1d481c9f25d69433d4f6e13a18442ea4c526d3f3824de75cd3d8d0b1f19afdfa80780ed99d62f17b535a97b04a3cf6cdbd684f96b3b02f52f99fd6bc1db5a280b36d75b6c5d590a93f49865899af14e13a3e53064cba283b3fe04ef7a74288f1406b1c877d52812daa3813eaa3663e70a9cb18a29c1058e9ec2f91a879e5f5b9aad1d02305bb530449871665ac389ca9edcf63638b87ac7c08fb1e9aa258755b21258f38989928f24fdc1251fbd315f6b89e0e39e697963bb323127da95e0a353b08576a7b6bee81c0f4077e51986c1057e80fa291da5a8e451a348e626e498096437f7314082905d4c7b21f9f6e3543f6302438f6a12543bb9f3b593084eee00f91cedd0d6716f7f42725e58291f0c5216a47c3db5decf919c73703ee9ded4402c44db699f5723abced57382e13d912a20a05328c720fe768eb00df948270839dc3a99df75300a374fc0265bc1c1f9793773a8e2e56496112b8ef3f63fd307e223a62b8d815d12ebcb6d7f5fb9ff051c0aed99b3b636e5a1b7705e9e6f9d23e347176958510d60535035eb7448e95d07148b3c377f4a7591ea965dc146c217675adb744f549af0c3d16c437d57ea862e53ff92ab11a6200f0baf474173b3ad5d9f02a9159ef00dcf8e97ee3d2abb3a74f32631cadcc7b5c92e9cb572562236abe003d0fb58a956ec3eaa3a181a9e5eed642ebd8ad45686de335f785843f1f7795e846588d0cee678b4057e4b57f35976b279031df93a97098e3468e5c78c54b7d0ee487c8a22cc4c14dcacd6f63a68a785e832913c1c57b264964274d2ea22c10dc42d1b7fa7709d4756a53cb440c64c20dd71ac2b7a86b3076cc54fae9093cefb0066054b28a2e67f3f2f78392a1f5a5062df4f6bf72a4f78d68c990f99a70c9cf78770e2cdeb1b4b3375016edc78f9674082a45a473dfa50bcf6c7ce70f494de6ff899918add6a2a501ff8b295d6ba4c8a403ff1d74deec8c1e6b7a5e6fe8ddf1b7767d42481c4904a5a7da55bbe490f292b6a1a2d8fcfc9a91e8c9c8655e892f6568d7f0d0c4d3b94f778fb20c013a1163b89761f6ae59d27545f9f7051714557b7461c647a84a0587e368aa35c815a4123ce02348a18aadce0142cfb470afb2920b9dbe256a74a061a816b8992d237cb17652232071df05dc3d3ba8f44ccdf7d1a7213cd79a32d86903957262985c35054972dd3e32a90fce9ad8ba341bf690dae481e9afac92566c4ef6fbbe2a973783d1be31bba3e59f07e19570e6b4b9f2bcc7ea78fa2f925eb596dd6a0c3c7acf0204f90e0437acc582cbd29761654340120f8d5657e8cba7717b889c8ea2b078751ddfd279b09f91e465fcbb978f9617818c29c04c2195b4820a0b7ab02097b79cf2e9ebac63834efe9ca1ac83e066078827d777b80df05b41d6775f98d6974ce2dd5d7f59ff7e399553d3b13871bef1665902366e33b52e1a5ac4a269ea471d703c9ec3c748633e79af2ea3700fc830357c1be0f0418af98190bcf66703b302df3fd8191ac8444007871a3a23572d508c6700e791e45c82a75dd71197ca8fb9c07efc899abad03a6ded42a4e9f880928d92566144c972f0f4841a1e519f3396c2ba782b5a489fe8e4c8c7bc372b397bbf4c83eb067ce1a06c67453bfb6f134d1bc099bff410690685f2b58f861d18cd90ba0f6017e8bd07194e1feefc23732a5ad4b735c1986f880c6c6481c96e62b6d8d9d06a00d1a4928978089ea14c85c40c883b9c7543bf0e8e12f3a5eb79f0102e9b1fb8ad5116ba9f057a793c2a646e31cddc4b575d19bfb7becd8698e6cd96c67be5fb560ca47039108d211cdd4a6305557677155abfc38994a9249b37dc0e93fd7ee6e0005d18d65404da8801025ae6e71ef4ddfcdc2ce25b691244e023186ba730b8ddee7193b78749164b5d78811913c8f785ebc6d1d8a154ec38ac1ffb2e22354431d4b0682dc687786d0faca2cf7db5af02cf6741023743de8ef151c5925068ad85def90190fb7150d96aa7eaf2017fa45cd572bded98003988c956e4e44d0d615a36f8d20a1b651827d2522e1713973da2b0e88ba1f20843faf2c44e0edcbe66e253fecbd6f36812bc6095e698870b8c0de670bc742ee8ef535b4f328bf70135bc179274955b8d0fe26648f215179923f04a26998671acebde8f6a867dcf24243413048b3b405b4c29495f42b4a65e5fbbf59e88d625dc24a0b2f4cbef2f25b2a8ab1f41dd950f595522ef78f17dd91b4a635f4ba587f088caee188a82ce6ec6f2de481df359baf1f8dc2d15e5c9269ab85c2806f78b4871980a5edeea2b5a373b4b19d12c9d43bec68fc99027ccf60c8ca5133a174c69f788253fd21eeb5eb5e09db8f8d1bf2daad2320421057a798365da1ef088ab35a4b7f6cb2451f2f90198a9582a1bc4b9a7f408732763e0d6a82c2a6156bf4cb05f10a874efc07d84dc0715a1dd0fbc0059d33e898708870818af724cd3849eafaf4de5ec45331c620208ff96dd13c95ae44e66a85b0325b6f546251e18674aeb132cfb33e9bc1487a10cad414fe7a660990f41b9c55b2504e5bc93ab00f7afd214ba9c00281c85105f31609827f0dec48d61267701542acbab0657b99d64d13afad345330e041136767c512dfe512ff13cb32f5c5a585f09b6241c9e0fdcc0d043e8b672f391cbc48c975ccc512774ac555ee26d543450926f81fb0ea365d5b1eef6a28e5", 0x1000}, {&(0x7f0000001b00)="3d61cd9bdf98e1c26f2088a8026c93dd03ca2bcbaab543ac1729b055a7fd836335761e76dd687d1b181022087e4482afa8efd5f8b30db60a0e2541403a159a925087cec828f1d46c52d6ecd5b445d762c87ab7e66b414438a9f5b25042be771348df7a88a6339cdf4c15683128281f9f950096f3b60160d6540b18c68d1f0d895eeb1d3f8063139d47b391ed1b1c0dfada8e62dcd9117ffdd58304ce8a85cc8f25c252df9db321583ed5652d4a847ec20e150933d6cdcb12a6b91edee45466f30514000000000000000000000000b4321d8060cfd88b7c5944d7e4daff28d321046a06feae0ede7d77856fb7e9bbf180380203584067e57649142e72613d9def61632dfc8fcade", 0x107}, {&(0x7f0000006640)="6b2c0bee77bbd747523f2e3bda55b8c980087013c3ef72823dc6302f57f295b0d53522a51f528e331efea6b8bc39fb8a2198be739bb6f81f45fc28e855082a8bf18012e45a21d0c46e9ce27e8a068f308555087b298cc4ef8e1d11d512c3748259d3536c092fab46ffe0bdfbc2da5ca197c733dc92897d940281d099d37f92799f02b808235e5928703995d1853be5c836a134b1a913cdc38653f1701d61b7fcb66c20e496c1563718ea4e1e0210502e527f4d30afadabdf50f3cba4ec82b30390ed0eba399fe5551c053598b50ff620f926225ca0730a7ce1f2f77621db43b7e4fa5a1ad901699db873e4d529e9f6a3fbc2c0d256467386331fe6cd15f56199650f49a5816990c7904baa78fd51a22d15fb469684fa5c9492ecbf6fc30ccbf7d6eb8b33b9d95b674e43b83ab6a6ee9c8822f694003d238af3239cd9a055a984faa323b69768ef410a3ff006461a5c5b5112a0a1ddc8f30d5a1de0c8e810b7c18bf4aa6acd0b13399d3f4fe189505f36cad3e31e401d2d25ac0971b2165e300ff1e523fd174299f79124cd795ad5005acba4f6ce11a5dd6715b910a838f5502774f0519a2c43e91bd7360025c44e42f8101ed3e5f775a768362bb58114c7b5d61fede18ef98fab78ec5431570b1bbbd0c0d8b68386ffb25ca0e25a23be8b533c2a1f1e410d8aa967d800e1629f06b31e22225b28b6b89a4b5f28946d7e2c558981ff8dec9d00fbb7334a489622bcf2e5fd9b86681b5ed20ee3a85bcd807b9525dcbdea1c8e3707129ea11201affe21658927735afbe2ca2fd4a119a50f6becc6ff509cad7a6b7e68c39d194f2ae314219743829d3ed545dfa102d08f6354ffc7885d01185cb50f454c8859ba1da1da38cafb076d5a408d5100cd6fa7d31e00346ced790371cf514b2452c12eec12e952a7b059beb30f62dff3ba0c22828461f2dbdd8951efe26a4c03f7379aafc826d57d7c6c7b27f0327838e90c12e939d19bcc638add14a2bca648f427775b71694d9d52ca74187724aef9bcee05d79e955ad378e1d9e1e027239635b3df5ea45389dd5ed0c855db4e30bbc970ffc456036a8a4207f2ea0cf0b335ae47977dcf5aa809ce2f3c9a48df4baaffef3418d5c082d20b55bf0701b3ee13184011dd779c8e4bff1a8b8f99b3e905961833dcf3b50660e4424c5c658083a3b876438e060fe789bf22f3f4eef2b287aa6a2e448b7d7930e1e90407cf9cd52b4c7bcde0068115c2cef48512b949a2320cf81cc7e998b123562ec146b6a8dc95b908264ec1bb885762817940945a456fc02c62839697e4f5cb66f026a2516c1fe4c0533cdee0b2057b7f6719e2efc56ae4bf20ab668ab1ca27e73a863b2acfb888ca9563486f9c6fb081cbfb42c868afb9a63ed68c805b32c14fe1b6b26eb6d2c63648dbd8f860ffc946f27fbf473cdca6deebd5aeccc87a2ff9fd0c3ff62566bbee344e064f13885e28fe68917427dfea2f5c5e33a57995eb5d29cb45c0cca0dc56fb50d4a5815b888fdbbeba5e54d06d3bff9c30b3c6ae6a41271539018ce9e6f8b7399012d828bc0cf804f6b0e0ac3825f56fe49090e716acc0f3efa525fdd0418ceb37707a14b64a0562d9dd7f7e8619fb49762b5ed3adeb83bd1acce843440da81ae84e21d896b02aa411e57c7dae5e17913a87bad9f30ffce4befc1de28810574597b72f287868a4c56c82f36fef7324a73ca668100694db1d29283c4ec39fddc1859e55add295427a148c2b9c27309eb4637ddb89e8b3f29a4c8eecbb71e79251c0df280b8c8002f118916270dfe9b9cd3ccad33d881a36d5ea0c4b3414408db4fb834945e7ab21ee3eb82f0e72ced86d20a11b76133c3da12438bc1b06628bcdc88143cc605f76a0f0f3ba898fe12e99e62f78f93d6271bbe5ddf489844d9aa4f0ba91257f339603636abc555ae6bc0d3cfa9d1b2faef705f5a9e58ef4cfa3c4ca8d995f16524609d23b5024ed055962abba43856a75520615c7f098bf98f673e504775eed387a1c7103d17e5b77ea9bd0eeae061be20c9025958935f4f2ed1ea022107afce603ead67083c84cb755e2c211e710117c70746241ad3feabd542a29516937048ba79467cc5b4946df66c65a36c1abdc94dcf823f021e4a44d7f5f70ecd43d0b61f386f591fdade8d46a28e8a4d5f3717da7a54384b04631332598adeecf4fbcf03419c4af7630f334ebf33ead4e712c21315c180d70b8d9083121ddbc11cbf7de387f698de875c14539174d23b3051b4215280fa6c681c0d8bc0692462f42402fe2310322ba9a676f234870b835c11f7c3fd6f989d178448e34c35d5083fc432e1788791c89d0bd502ed76a442284bc401e8cc99edc2c7be8552e082d16cc1545340c6469985ed4be342726acb3e19f154eb41efdecb77712b17aaea8166b1214e703410542c15bea62d2ae31092727bcdaf38ffc6e5409d9a28d533d269e5a16df4951e2bf3459e607ca56882517228749c1604f0fa057eefa4f147c0f575c0738eedd9f6ed043bcdfb9e72bc5c84d0746b183137486bd3f1a02e017cd4d5b1643132736f3a0ba55da623e15ffd6e3980d5e4201fd39d0d1f67191935a20076e9c45e843aa98e877bd959eb946f97eea78850d5549595d5496f9f6752e40851e332eba7217a66a79a5f8d16584b85c7605cc3d4f56cedbf5231fccbafd1c6c406a7d8e289844471cfc546479c6963881cf3cc136378f33481df658ae1183e433e10e7f84286b0eaa823b5e8126532e860119e959f31cac6e6325e1b7d132143618b21eda53469e3320ee754bc3461a0919dcad27933690feaf2c5f4890207a6ec5ac2edf215b59633c4e4797c29013ac1df411427a7ecc4f59eccfcee612509da4bac432ce6b18418aca3dc3822a3a68975e8466d463c230db80dcf8a6c2af50f11d2207dbc03d1322dd14c728decacc8aa554d475de5d0e8004b4a84d03628a2d742a9d4a4419369751a7533f36b4e366bf360a284fe667d72d8272f7515b72716d160b03312ec87d7f9ecb68a0bd2cc476b36ab848d9901152e7a1b541a89190ca7bb4804a03c7e862cc1a2c2a3e9d9c838bfee0724c793dda117644be74700038c9a9abf9d56c606a1a0cb32dc5fa5bf1b54605e843cbb015828dc3668310b8053ca4f35b8cd0b6d3dd1779f0af540c113cef5397d5d3f67e3ef8470675319292f836f75086bed6197182cd4d6098fa8f4b0b0c9d44ae91447b5d63d8d647b9815eaaa14f3d00777f9982af86279e52c2b7b1ea457dc9bc3872542f953e216cd963c627304d240389b1e51fcd7f8944811a905be49ae0a0761a3a6583f4a915dc881408cfc464d8d78cd9bf708bab4c8ec33a3da1321386accdb6ab9b3dedd2445fa5da6b0cc521bf59473a4459a223f4818b91c17048ca5e976b19c4b70040ebada70a729eb4f25068e77798dab5efd2cb79ee501bf9387af5263b3398dbe385c73697c9ea0a0edb93576fc3a9edf60e8e1303094e6c6656e0494c384bf9281563ccec7061e8c4817b064520dd88ddf6559372cf50b0ef838edb34e53119d19b3ad4349ab4d2285bf7058e8088446d53c830c62b98a4b52ec78205b18042a77a1474761b47c6eaefc6a2b1915fd861a3db52c74ebda02658376bb4c4bc4051719d0af888789eb83183359b499e58fe1900f6fdb82efc5162d242069a3c453672613becb447d62ee80dddcb7930119e261e0293d41e1d51a7a573eb0f0d2f6511c092a555e2bcddc37050e1e504d54b56ab33089d9ce746fdb8ff0ae9954ac046fc5c4ccfd49325df25c049e49041a594c0238567811fb8a6f8b5ead92308291a7015bd0787b34bdfcd345def17ff9b198ff0857d31c237904e8aa9cb351a5b0d19914431d1ccec042249e909651070db801c15c500733aab233048edb26e5258aad5dd75963913bb9dba057282cd9d5d9a1b478013f2ad39d13cc303f1887d37fc74299b97f80fcf3a7aafcbabdc335dc0195cbae6f7dc6c7c890934a217de9e47cfae531e7474036fb05c6318c9528d60f0a2ce5b6f1cf629cb3c107a31eaf750f84684760515c3e59f8f673236d4827e6dd83063d91baa4670b5a2c1e993fdd1c052d1d5959dea2a55fa13ad60f21cc2aad7231f9ef54f179fe5200770aeaf1dd26355c29f71f8e3d8cd8972d9ccdad75740e6af75093f198b443e8ea61e2da9c9f249cc59f6457fb72576846f96ed54aeb6f01b0d5f189678d09bdde943fabf6a5b9143538191d888cf026d590fdc98ab52b57a9c2a83703c0ea109a5fc1d828a8eb379f2c3a3929ac923a3a2411dd29826f96fcfb308e2d9ed9e4e6652659331e5318a22ce409041006cf0e7970b4a007d7707fd4fef1887b3fbc4d4f29729ad89bda3aa795773c2aae7bc7f0d3331b99efe040df702dfa4968d7dea658c62958a56a083cb14fbb84bc454e884b3271623df282b33dd45ea5010a9dab1e8f6f4891ff64dc8e14cf702147a238d6f4f980e0f72fbeed556de1ab754ffd18f27bb3250c2d7d07985b2e88478058aaea51b8b92521301433ae97209cea84c2d16c30c87bdde5d161de50574502dcd3c303d9e10671e019cb96bc633a6ea80577ffa0399adf8d1d069775dd4702940ee2dc707f77989513a90e371644d2ea00b38e47a27499f7b5044ba219ee3af3abc4008f210d23299bc587d74ec2bcdbcb13390a0aeacbaaf813d3fc9049ee97fe25518ae16f3c2b31ccb3a0c35874cf4b09d8f314f43f30272390290190b34af72b33f728931dd569d87bcb05c550fd8b7090f2f353717c995bde516185a1b47ffa8a9159e494dbc9d50bfbb3c4dd120812851b2bad837fb6e58f5b6fcecdd38710252af6f17d3ba456b562ba42c58c2d43efdeb2c84e7ff5e86b2423381a68c327fc9898ebfdfcdf944c2c263b563fcdba7ec9f019768d6e6e2ca50a540a32aca9beef36f61d889a387d287c75ee42a9efd820f74a604082bf2b37f749398074301571d2338a248e86f4e9765451fd85ec1911dc996ea8134125e6ce43e1908776379cb2a9c52df95dc88f8af4401dde4863251bf69c9", 0xdfb}, {&(0x7f0000007640)="55e10d2ede6261919df0cf02a95b2d07b03b913099eabde69758e404af4432ea211a922987bcd1fd754b8a55bc276aa98d93a3231d3e0ffdf0c143dcfa3ea2b1a705da3c00b4e02bfc9a4425802a8727dd4bc58cafbc8e962f24575ba599cbdccca84abcad99c12cf8666bc6de9e05313edb869ed10e44b801d9f5c466164921a4f16a405fe17b252c4aa7b920e30402b24e6a937e9235b15a6601c90c712925a642c50b5c162028ae7f52497eb4475b401568737e6a82071c0795b41af1b9c6d73ee53e5e3dcc253c2eaa020200a64c1a811a83c1aeb7e7a5773b69af514ce6a14e6e74127192534ff77b500c5dd1acef5db119b2d5f2b291b5f1187607c2898b9d3f36e07c871dd05045616aa9986ad814051b199b8b1c67387ab60494468182e64623b20a809645a1644ed722a85a18204f390981c5edcc11a35d6427f6382707e0c793fbd73db90f46aa1787c9e8cfe374dbc409ae4a28777c416f5e3cc536ff7009458c27f4c6b45f07f68877e417d80cceea62269d68b6bd4d11aeced642442f2ec24ccaf39c71eff54e0e46eb8215c782ceab4839e1591b8c406bddc994ed3ebc589ca8e68ac2123ed32d2d7285e233a3c0b4c98c8699b7026bccb3d73134f50c42e2dfb661a5f31b6713094e41d2f2db4b0df7678552b4cfffb280f874bb1330c4f11fe5998f30aefba74d3e7ca40c7fa91a74a3fa1410b1ebe7c2b687873a5a832aa41bf61a23beed5f737fc80141c4dadfa5c1bbe1d875294261fbf94b839941d713fb4194ffa7ca5e410712fa087e3a1b7a74f656eea43a50233f0111a812a0c84ac74e5d705ee5d78fa2ad91202f846baf564e407e6f52f63e1ecadd11533d269a4c3bf77a259c9c65ed01c7300af30272bf4daab80e4c297398feaa02948bbc5f9ce8f30b7a747c585c69e5684d6dd14abab5e04eea33f0c0edf05c51c837afd745d3267f9cb7c793ba145a7aee78e733d80be11eb7d89f0afa1ad09ee13d82e1c2f2f25f95a171fef1b3b7d2c61b869ae18f56695932b034ae3fac1352bcc63324b12c0cd5b203b43347df4f91c2f9a27a4c9a583c2025cdf48042b06896c94323234d5d8f65324303ccf0fbb8c50531c1228bce3eafa6264bbdae3fa002a604e2f065e4da75d08f7b7d14ce4448f9beb8737dfdaef81645e255806f1af0cc6ec96b72a7fb010bfd129cb22b5ae7927bdc7d3f75e665a8611b3fedc801ab0c887fa693a1e918ea611633d0f82befc9420284b5ae7eb9e31de8d348ebe3147398b861b2f99031f5838cb84de30b1650a92bad8c5070a26bb47f634aadfc49440f03c9fb8a6571fcd98fd0653716d1e6faffbd4f67eefaf48dc9ba8563bf7aa0dcfa8d6ae91440508322d3279e8943631f4978b04071446f474135e2e69513e57a37537f4bb768789cb6c3072ee1fd05ff147eccb94c408fcd4920869ba72e6be02d3d5ed92c6a4d9752d6b4e6711c5f56cabdda4fc95c18097437c1fc3d9aa5bc936616a73651b8f5671eb0898aadf90d8934a534e38cf5c804369044923668df5747a32cf9564654c28761001835656f69dd7b2392e2c4c2849ad70dae858bad72f6a0fe6346d9272a1c80b1a2e051cd44617f09cb2b79e65a62b6693a0202c48496835d78ddfbea30e2dbc625fe8ee78f0e4f5633e15434849d616083d46018f7881eea3648310d621184793267f89b5e43486bdc5cb01aa36de2b8743c6c3235f73774038b60e18f90ea8f56c1ed5f29a44679353db4e882ccd5bb2a45a3ff073c842bb4ed9ccbb1279e3054675da03017a16a7fa828668d1215cf6691d54ad23bfd687014d34ae1a39a74148f6144eb0f5b16b1cb7448b123759523f3fe2249bc1c70f13a5a09bc2c0a8818c434874ce9cf6fdae78567351331024551e01ebeb6417ae7e641280fe38522078cd9e32bf2a0e04d1e243ad50537c996e2788893b73b0b89c526c88f759590d2b4b74cd0fec5dead096c24e5d85b21b348e433bdaa45597e9bbbf5636e30fbb234c1ba38029432568f22de3fb051f6b7f16d585301b54a583fde67d137c8bb47164f3c9537fe0550dfa94c4c4fd985243ef07841f2e53283c7bdc64e73660ea83d7f6eb7eaa6b52ee6a948b5d77bcda3ca5aa5c0db9ed6aa2ac040a1d0afe0d17aefcead938df4ce20a21c2efbd49a79997661692ca107db7c81b837f24a2ca04c8bd40722a2ec6265a19d0466065aeeb46c5bd156f87057024f961bd9c2fed98712479bf9fc8eb560819b3f9f13a09bfd3e6bdacc3587d752833c2a3c4212004a50f59d8b6f8dbb77b38a49957f2d50726d06208acbcc4a9ab5771089eba8e6f63843456d060467a1cf9a005172d22b77714ab2e6a5fb3066ffd3439561e6cead566e6862de29e67463bf2cf3b3c3311a131902dc2e8d899ce4e565a1b16a2c1c61d736cf25482d1d026301a8447741d3c4c1333535614b2a07d369cd017b65760b34d9c0fbb05dfdad182804dda011aa0203053b471f922ad0d92a909da1cc32b1e9b41032d2b7ff5f45de023033b53192e657cc7ec43cff2d042db37407027234d4a5b2977e638b9725251aabbe690c7fc0cc3f96a577c247302deff65d1bdc14950017cf2ead0bead162f094ec447c871a841f25b17b522b33c2ded06707f99d6f827c36b52f00b8104f400ba8a4d59523309bf3e0a18fc1952de40993bdb2b759157ba07d97bfe3679a851e0f9be41981ff62def73ec1ab8cc70ad98a9760febd3482567fe3b6cd34e67497a637afcc1424a9ddc60f279c73a3b215ba4f0bfd3e0ba2bd7bacadd6b9eb9c6a484ca5a5f48e71daae0d53e591d89639162cf91f95c0f914e36bb74fecabc4efb5c449a9c8de1f1a794b19f31186590090d5d2653b059a6e9daeb508108a7c875592398a05eb695b8a119667307ca6be7849046e281d55d1cfa4e54c7a5407389945f2ea76c68be41a462152e5998b2853fce87b36ab72f4c58c210afc08a9bd9a277681d5719fee7a596e4b8ca212d16666334a35ae1213f3d11e6e5a2e97784a017e5b079b24831131b1b8ef03996cacf52faa9d45d3875645432dc6fc343ef847290e88af0eecf87fe656fe415c84f797ae2cb47bfcf19358bf5deb2cdaab7588d6ccd9896a092ed266f2f870298797f188a569a09436993dc3b1e60822e0662dbc111dc8d76f08166224e642d645513f85e8ea6295754150dceee5e24a69648e9d44d27c66f545496a8b92820dabbe621ab8122bbd4801edafd81f04391c32aae8ec68173427f73a939b6db13ce7b471f410c1d411b189a713fa68d8c11e004b0dc6a25e53b71cf9abb9b54855829d0f5027c436c23b4218f4397bea772c39feffec8171f39287ef5dfa358da64a89ccd96035e6a471a57ab8b4298fd3458698f91aa2012cd650f3ff239c0da29e7bbc355300ba32a3e79dc8d7dd0aaf57d6b874f076dcdc79ff188c735ab5f39159d45cbf0f9f7f3c99036ab8b9efd648c9731ec8b0d3a0ad6ce583f21fc9fc42b26e26454f55c01f392e8b7742397cd12b6d87abd5049374d728ff59dfca12b59d18e4152699695507c7fac9bf6c61e6e1365806e74d408db403fb68b6f0bbd501c48d5b7854d2e5d8fb179e720719b4e26d2f2fdcbc088358288a227bd11af46c3da827f80ad4b0d3a5104c860a1c84345916e99f33aebfbfdd8da5d3ff11d74af96ba3869b723622d44997657a380a47e7b91906e3513b875b0b05bfd4ea07010c633af21bdf90403df76d85b214fbddc357de4b9f412f823ce1e830d04e4c8f8b072138f8d2fc7a36407a77a3463ce46ea6ab198b9f4de908efad7168320354be34aa9be9d1a1052023887c04738db9d0563131ae8dfa96084ff75bcbc03caffcbe5376c3e7947f8b54de21b742474431c92ee073d9d150ebe67b7adba628c75a1724f0a88ce9ba1a56c55613090bfdc7276cc5358561d5ee1df80b1bf1597d8f620c81e43ed4e57460e694919b315dd79066e5e35e01b83eee0f0f9b8eb1a66a1298cf71f2a1c11cda702b9eb18021294fc5f0cf7adcc735caf78f5d506ec3b941014b34a9f0109bcc72871e697666e83e3d1fec90b87f8d5fdb197226f66d619fcb45c42c522936342b09f9f11db2eaf8cb522743d7cad4d8651fc19ac86cd2d419b35d51cbd7963649c1f1ef4f979c52a97f8bafeff626eff6c3cc26e44b34956ae2dcc7e266de416a98e8956c0153aab51b8e7b5891621b5916224ffc5993308c17d51f33744ebc78872f1046d4a8176155a39937887faef0ecc456bd037a84cae81317c06f19c2c4852a327ce13b4a8e01cd218b02478eb17b49a463df5986360c45711f6540a7d6e7e164bacd9c5813e02291abba696680b091d44d9f841de0bca502982f43b630488b6db91f2d16e89d63e5cacac075b93b3e534f99053f765f5a2581a4c1ff8963f14331a41bbf294e8cc15df30b03826243f36a3bf443b692f2d269b3962bd1e2332708649c917c874e48847bb359b0fd24ed5e5b5a11f0d627d7177cdb215f703c46ce2283bb734c611c10a8643b0a49d3c939480340d945f2c1d2c33fbe5571fbd76dcb044e9c4e34174e3d72e3013f83f09678658e9dd1fb1fbc9689d0a10c2a1d2dbfb1717b2c5905a2705637ed49d57b9fd1dbca4f59279f146dc9f9259332003a2baa259e55886001c95ec7332db72d873a15e6e677d26f27ac26d00bb5eed65e33368ab8e49a3f0728b43802a2a71d14b78cf0ced6c7b711360e478837f2d8187e69b2fd549577538316be310beeb4341436520691294ca0e52085cbcc44495016c07105abe5bbfb6fc2ba0aa5554ac1c10628410d8d60a7d153f29814fd745141cc5a2899f0f38ba8a2a63b6d71e5cd73773561ed7af009a15624854eaea40e87aa1f02c051039f4d47d2b0bcc0aa0a88638406a8f5512cac74d7c328b24a302286405fc5d6aa415f231bd8c897f145cf6bd094617a864185729dedfb31a0a2817131e0c415d2ea906eefef63a6f38aff94ca25d87e24dc39a7a0f9de3824133c238549da0916953a5dc0bcf2b9164c6fc115087a1c6ac7df0ac95f1cc2242fdae5cb0274f32b008ac87fe62d05eb30f163c2a28d04ceb456e42dbf32b2eef1bcceed457170ff4090e4fe344c8eb0db9202339b116b56e66c45da435b3e6b956f5fc553d3b3b44fe774d1011471a729a99df67e4a1c04203c1698a1eb090f175d191a09639f381a1038d4a329e84b14d55fac16ce9c8823ce5ed3efc7da2b04fa895fd2c4d345bbc9160f60c3a1fe3e14c7207f5ce6d2a8604dd136a2fff24e26488cd39269524538aff739071b1d8524653dd351983f5ed88bf5fe0befa055e160e858e2a9ff02f5df229a11e88ffcfc926108", 0xed6}], 0x6)
writev(0xffffffffffffffff, 0x0, 0x0)
write(r0, &(0x7f0000000080)="1294dda2d524b1a0adc56ad6eeee31a68a80ce29f06efe6eb88d4c9aa2c27794011ee68205fbdae8c39d957986c8a125696ac033b5d8c447064ae1ed7f849c81d7a6b31d143d6afdc8b7661274a072015fcf76bde3ad6ffaf278452e088e1167113b257ec7ddbb35833b281bdc0093ad70a3585df49e7d607683e50c169ec26e7cf5bcbfefbea0d0d92e0733c9a79579b3662cba7f17bb0d78e0a62b5fd344ded04e", 0xa2)
ioctl$KDSETLED(0xffffffffffffffff, 0x20004b42, 0x0)
openat$bpf(0xffffffffffffff9c, 0x0, 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x5}, {0x64}, {0x6, 0x0, 0x0, 0xbf}]})
pwrite(r1, &(0x7f00000000c0)="fbaf76166d2b22c07b1f1e4eb71f", 0xe, 0x0)
pread(0xffffffffffffffff, 0x0, 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000001880), 0x0, 0x0)
ioctl$PCIOCGETROM(r0, 0xc0107005, &(0x7f0000001a00)={{}, 0x0, 0x0})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
sysctl$net_inet_carp(&(0x7f0000001040)={0x4, 0x12}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000080)='oL', 0x2}], 0x1, 0x0)
write(r0, &(0x7f00000001c0)="f47e0e5e68ec292373f98bce2d", 0xd)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000440)="83eca8fe3800aac72d4750a342", 0xd}], 0x1)
execve(0x0, 0x0, 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000280)=ANY=[], 0x28}, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={0x0}, 0x10, 0x0)
munmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000)
madvise(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
madvise(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, 0x0, &(0x7f0000000040))
mquery(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
open$dir(&(0x7f0000000180)='./file0\x00', 0x200, 0x0)
r0 = socket(0x2, 0x8003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x800, 0x0, 0x0)
mprotect(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000))
mmap(&(0x7f0000400000/0xc00000)=nil, 0xc00000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
r0 = kqueue()
pipe2(&(0x7f0000000240)={<r1=>0xffffffffffffffff}, 0x0)
r2 = socket$unix(0x1, 0x2, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r1}, 0xfffffffffffffffc, 0xe5}, {{r2}, 0xfffffffffffffffe, 0x6f, 0x1}], 0x0, 0x0)
kevent(r0, &(0x7f0000000080), 0x209, 0x0, 0xffffff81, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000300)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x1000000})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0xfffffc8a}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{0x20, 0x0, 0x0, 0x200}, {0x6406}]})
syz_emit_ethernet(0x201, &(0x7f0000000500)={@remote, @local, [{[{}]}], {@ipv6={0x86dd, {0x0, 0x6, "7f473b", 0x1c3, 0x0, 0x0, @ipv4={'\x00', '\xff\xff', @broadcast}, @remote={0xfe, 0x80, '\x00', 0x0}, {[@hopopts={0x0, 0x5, '\x00', [@padn={0x1, 0xa, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, @ra, @jumbo, @ra, @ra, @padn={0x1, 0x5, [0x0, 0x0, 0x0, 0x0, 0x0]}, @ra]}, @routing={0x0, 0xc, 0x0, 0x0, 0x0, [@remote={0xfe, 0x80, '\x00', 0x0}, @empty, @mcast1, @loopback, @local={0xfe, 0x80, '\x00', 0x0}, @loopback]}, @fragment, @routing={0x0, 0x10, 0x0, 0x0, 0x0, [@local={0xfe, 0x80, '\x00', 0x0}, @loopback, @mcast2, @loopback, @mcast2, @local={0xfe, 0x80, '\x00', 0x0}, @mcast2, @mcast2]}, @routing={0x0, 0x2, 0x0, 0x0, 0x0, [@loopback]}], @generic="16f1bc2739fc35e013bd3ee8e888eb1b83bec9826a3eaf3eeeb038889349294aec6f7b50ef20d3c8ff10a70c6649dffa611f311ce00884a2ea8bf66e081435f09dd4e7786ab9369eca829ec600f3a0da94ba42fded3b20ba98e226664e32d31501576d653e6d3d0682cea1973ab7d69cb46aa6fc84fd1d8d664869"}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x1)
setuid(0xee01)
r0 = semget(0x3, 0x0, 0x4)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r1=>0x0, <r2=>0x0}, &(0x7f0000000040)=0xc)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f00000001c0)={{0x6, r1, r2, 0xffffffffffffffff, 0xffffffffffffffff, 0x40, 0x8001}, 0x3, 0x8, 0x8})
r3 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r3, &(0x7f0000000480), 0xe)
semctl$GETVAL(r3, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r3, 0x2, 0x7, &(0x7f0000000240)=""/67)
semop(r3, &(0x7f0000000280), 0x0)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x40}, 0x40, 0xfff, 0x9})
semop(r3, &(0x7f0000000100)=[{0x1, 0x1}, {0x2, 0xc2f, 0x1000}, {0x2, 0x96e, 0x1800}, {0x0, 0x7, 0x1000}], 0x4)
semop(r3, &(0x7f0000000040), 0x0)
semop(r3, &(0x7f0000000040)=[{0x2, 0x0, 0x400}, {0x1, 0x21, 0x1000}, {0x2, 0x4, 0x1800}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(r3, &(0x7f0000000080)=[{0x2, 0x1000, 0x800}, {0x2, 0x8, 0x1000}, {0x4, 0x5, 0x800}, {0x4, 0xfffb, 0x1800}, {0x1, 0x4, 0x1000}, {0x1, 0x3, 0x800}, {0x3, 0x5, 0x1000}, {0x0, 0x7, 0x800}, {0x2, 0x7, 0x1000}, {0x2, 0x3e0}], 0xa)
semop(r3, &(0x7f0000000100)=[{0x2, 0x5}, {0x0, 0x7ed}, {0x0, 0xc0e}, {0x5, 0x2327}], 0x12)
r4 = socket(0x18, 0x1, 0x0)
setuid(r1)
setsockopt(r4, 0x29, 0x80000000000000c, &(0x7f0000000180), 0x14)
r5 = semget$private(0x0, 0x0, 0x0)
semop(r5, &(0x7f0000000180)=[{0x5f8ce2611e9b27bd, 0x8a8, 0x1000}, {0x0, 0x7, 0x3000}, {0x4, 0x9, 0x1000}, {0x1, 0x5, 0x800}], 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x4000004000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x84}, {0x87}, {0x6, 0x0, 0x0, 0x84}]})
write(r0, &(0x7f00000001c0)="da9e028ef605c40df4cad6e65c5e", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x10081, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{}, {0x1d}, {0x4000006, 0x0, 0x0, 0x200}]})
pwrite(r0, &(0x7f0000000140)="db01f74e11a4ff07c6000800abe3", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x47, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
writev(r0, &(0x7f0000002a00)=[{&(0x7f0000000440), 0xff52}], 0x1)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f00000000c0))
writev(r0, &(0x7f0000000680)=[{&(0x7f0000000400)="d0", 0x1}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r1, 0x80085761, &(0x7f0000000140)={0x1, 0x1})
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000100)={0x4, &(0x7f00000000c0)=[{0x5, 0xf7, 0x80, 0xffff}, {0x4, 0x1, 0x67, 0x4}, {0x2, 0xbc, 0x5, 0xfff}, {0x6, 0x7, 0x0, 0xfffffff8}]})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x84}, {0xc0}, {0x6, 0x0, 0x0, 0x5ffd}]})
write(r0, &(0x7f00000002c0)="7cdce463e33fc0aadddf7854ead1", 0xe)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
r0 = shmget$private(0x0, 0x3000, 0x0, &(0x7f0000826000/0x3000)=nil)
shmat(r0, &(0x7f0000ffc000/0x4000)=nil, 0x0)
mlock(&(0x7f0000800000/0x800000)=nil, 0x800000)
munlock(&(0x7f0000bfe000/0x400000)=nil, 0x400000)
madvise(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x6)
connect$unix(0xffffffffffffff9c, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02abc9e5"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000000)={0x2, &(0x7f0000000080)=[{}, {0x2}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, &(0x7f0000000100), 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0xfffffffffffffffd)
sysctl$hw(&(0x7f0000000040)={0x6, 0x2}, 0x2, &(0x7f00000001c0)="ff9e219a32c712697e956a95d04fe3c7254188b8e605fd6a2db5fad2bacb1b768edb5f0ec9e85f8ede66fbda0fefb3759fa510ee80a590385435ca9cce42959ccbee70a6144d087242b6bfe452280e8f8cc8aaddce168e5a66f4e82df82c29343769dd159282f9d37f5f91c3844ad223e22c7f87fdb2a99520669d06875c9c3bcd645fc47bfb41980589f2f8fdf7a70dcc60a7c83203bf9bb72ca7f1586493", &(0x7f0000000280)=0x9f, &(0x7f00000002c0)="2ce1565b8498f04bfe800c9ad6e5dccf07049fad1c68e46cb1d29028ba8135a4d4858129f4ef512a1c301cdccbf460f75ae5e238698530ab0069e439aad7a5423d9833c0563e94e3959a4ec2f79616f1a9c8063b1ff86c240889cda3db1a16aafb669981770c965f233b06e312fb44d2a5a6eb0cb0002b18611c216bb0c477ed1d4d67acc49f3b102c0aa4ee57eef515f4cbdf52273f807f42f42df04a1d42415458d5d4f99dc68fe81df49048df42988edc418b2065b91f0f72395d8b48976a1850880cb3abc8fb0996e74ce3f525195f2042efbc533f60d23d4d092b10f8318e305c5795", 0xe5)
sysctl$hw(&(0x7f00000003c0)={0x6, 0xe}, 0x2, &(0x7f0000000400)="0160b2b56db50f5c746afb9f3d45a01d76ced3b35a65253761f585643843913bb14da29c8a94641d03e66135f1acae9465fcbafbab147df1fe7dd6", &(0x7f0000000440)=0x3b, &(0x7f00000005c0)="6659d205303b4968186bf84be6a24d5acabcf52c76816efaad17b0324f22a5c5a1c3bd023b9929c675b9017c4049ec9f7411d08710290d383b39c5b7840ee27bab2dc450c7f9ddc839ec7896759c182fe7cf7dc35f845c5a49a9e0f3bc7d5c2aa8e4690b44ac26e1dbdfd20f736dd68dc80bd38ac5ba0f18554cced5e564187910a3d946879a8f51db6be16d2988011c174b03", 0x93)
msgget$private(0x0, 0x2000000186)
r1 = semget$private(0x0, 0x8, 0x10286)
msgget$private(0x0, 0x1)
semop(0x0, &(0x7f00000001c0)=[{0x4, 0x4, 0x1800}, {0x0, 0x6, 0x1000}, {0x2, 0x7ff, 0x3400}, {0x1, 0x40}, {0x0, 0x2003}, {0x4, 0x5}, {0x2, 0x1fb, 0x1000}, {0x3, 0x33, 0x800}, {0x1, 0x4007, 0x800}, {0x3, 0x4, 0x1000}, {0x2, 0x9, 0x1800}], 0xb)
semctl$GETPID(r1, 0x3, 0x4, &(0x7f0000019500)=""/102366)
semop(r1, &(0x7f00000004c0)=[{0x2, 0xff00, 0x1800}, {0x4, 0x8, 0x1800}, {0x1, 0xfc00, 0x1800}, {0x4, 0x0, 0x800}], 0x3f)
semctl$GETPID(r1, 0x1, 0x4, &(0x7f00000008c0)=""/178)
r2 = semget$private(0x0, 0x3, 0x4)
semctl$GETZCNT(r2, 0x2, 0x7, &(0x7f0000000740)=""/159)
msgsnd(r0, &(0x7f0000000280)=ANY=[], 0x0, 0x40000000000800)
msgrcv(r0, &(0x7f0000000500)={0x0, ""/139}, 0x93, 0xd6d0c418f59fe7d3, 0x1000)
msgsnd(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="00a9e57e430000004ca28dc72e914103e270e9161c3aaebb2c4669a42708c6a56c0300c4456c25285728db95b0fbfede7536e4bb422696f6254dd6c59891783b04dad9d11c7132619c16c8eac2e46dea29e7f6a735dc62eeb3bc7ab404ef5ca3a8548f0d9f3ae531b10260d235bb19076071360ea0770b2ba41036cf6196e4d9cf8f76886c7bf1857c6fdeebc34daec2d015f817f0207f28498affff68daead0d928a165b93b1a361892e3f71b51a5ca326e111c927433bf2b4698432ef4f23698f18c75087f791100"/212], 0xa, 0x800)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000540)=0x8)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000580)={0x10, 0x2, 0x4, 0x0, [{&(0x7f0000e10000/0x2000)=nil, &(0x7f0000ee9000/0x2000)=nil}, {&(0x7f0000bea000/0x2000)=nil, &(0x7f0000cb2000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000bbd000/0x3000)=nil}, {&(0x7f0000f6c000/0x2000)=nil, &(0x7f0000cff000/0x3000)=nil}, {&(0x7f000087b000/0x2000)=nil, &(0x7f0000800000/0x800000)=nil}, {&(0x7f0000ee1000/0x1000)=nil, &(0x7f0000bbb000/0x3000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f000086b000/0x3000)=nil}, {&(0x7f0000aef000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000914000/0x3000)=nil}, {&(0x7f0000f81000/0x1000)=nil, &(0x7f0000beb000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000a1c000/0x1000)=nil}, {&(0x7f0000d71000/0x2000)=nil, &(0x7f0000da2000/0x7000)=nil}, {&(0x7f0000b26000/0x1000)=nil, &(0x7f0000825000/0x12000)=nil}, {&(0x7f0000d22000/0x1000)=nil, &(0x7f0000d15000/0x4000)=nil}, {&(0x7f0000a21000/0x1000)=nil, &(0x7f00008c4000/0x3000)=nil}, {&(0x7f0000bfd000/0x3000)=nil, &(0x7f0000c28000/0x1000)=nil}], ['./file1\x00', './file\x00', './file/file0\x00', './file0\x00'], './file/file0\x00', './file/file0/../file0\x00', './file\x00', ['./file', './file', './file', './file']})
ioctl$VMM_IOC_INTR(r0, 0xc4504442, &(0x7f0000000140)={0x8})
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000022000214]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020690e, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000000)=[{0x2d}, {0x84}, {0x6}]})
pwrite(r0, &(0x7f0000000280)="c70b1200957c2f318591de7f0853", 0xe, 0x0)
r0 = socket(0x2, 0x2, 0x0)
shutdown(r0, 0x2)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="82022e"], 0x10)
socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x2, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000080)=0x3, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000002c0)=0x5, 0x4)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
r2 = socket(0x800000018, 0x1, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{r2}, 0x0, 0x0, 0x0, 0x100000000000000}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
syz_emit_ethernet(0xe, &(0x7f00000000c0)={@local, @local})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r3 = socket(0x800000018, 0x1, 0x0)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000040)={{0x0, 0x0, 0x0, 0xffffffffffffffff}})
setsockopt$sock_int(r3, 0xffff, 0x200, &(0x7f0000000000)=0x6, 0x4)
bind$unix(r3, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
sysctl$net_inet6_ip6(&(0x7f0000001280)={0x4, 0x18, 0x29, 0x36}, 0x4, 0x0, 0x0, &(0x7f00000013c0)="df635caf7f4a05990dae5a5108d037c3d4", 0x11)
mknod(&(0x7f0000000100)='./file0\x00', 0x1ffb, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x8002, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, r1)
setrlimit(0x8, &(0x7f0000000040)={0x2f, 0x96})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000140)={0x0, 0x0, 0x0, 0xffffff1f, "b67eebf12843097ce28eab1b0b747619b9bb9777"})
read(r1, &(0x7f0000000640)=""/193, 0xc1)
setegid(0xffffffffffffffff)
setregid(0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=ANY=[], 0x28}, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000000)={0x0}, 0x10, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x14}, {0x4}, {0x6, 0x0, 0x0, 0xfffffff7}]})
write(r0, &(0x7f0000000240)="0ddb000000000000000c51a14ebb", 0xe)
sysctl$net_inet_ah(&(0x7f0000000000), 0x4, 0x0, 0x0, 0x0, 0x0)
mlock(&(0x7f0000ffb000/0x3000)=nil, 0x3000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
munmap(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
shmctl$IPC_SET(0x0, 0x1, &(0x7f00000000c0)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}})
madvise(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0)
mknod(&(0x7f0000000040)='./bus\x00', 0x800080002002, 0x5bcd)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f00000000c0)=0x1)
read(r0, &(0x7f0000000140)=""/4096, 0x1000)
mprotect(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x8)
r0 = socket$inet6(0x18, 0x3, 0x0)
bind$inet6(r0, &(0x7f0000000140)={0x18, 0x3}, 0xc)
syz_emit_ethernet(0x3a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @rand_addr, {[@noop, @rr={0x7, 0x3, 0x90}]}}, @tcp={{0x2, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504447, &(0x7f0000000240))
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, 0x0)
r1 = msgget(0x0, 0x656)
r2 = getppid()
msgctl$IPC_SET(r1, 0x1, &(0x7f0000000040)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0x0, r2, 0x40000000})
r3 = syz_open_pts()
ioctl$TIOCCONS(r3, 0x80047462, 0x0)
r4 = syz_open_pts()
ioctl$TIOCCONS(r4, 0x80047462, 0x0)
r5 = shmget(0x0, 0x1000, 0x2, &(0x7f0000fff000/0x1000)=nil)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r6=>0x0}, &(0x7f0000000040)=0xc)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000080)={<r7=>0x0}, &(0x7f00000000c0)=0xc)
shmctl$IPC_SET(r5, 0x1, &(0x7f0000000100)={{0x7ff, r6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x10, 0xfc5}, 0x3, 0x5, r7, 0xffffffffffffffff, 0x7fff, 0x8fd6, 0x356})
r8 = socket$inet(0x2, 0x2, 0xfa)
r9 = syz_open_pts()
ioctl$TIOCCONS(r9, 0x80047462, 0x0)
r10 = openat$klog(0xffffffffffffff9c, &(0x7f00000004c0), 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000600)={&(0x7f0000000000)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f0000000140), 0x0, &(0x7f0000000180)=ANY=[@ANYBLOB="2000000000000000ffff000001000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32, @ANYRESHEX=r8, @ANYRES32, @ANYRES32, @ANYRES32=r0, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=r2, @ANYRESOCT=r5, @ANYRES32, @ANYBLOB="000000001000000000000000ffff0000010000002800000000000000ffff000001000000", @ANYRES32=r3, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32=r4, @ANYBLOB="0000000020000000000000ffffffffff08ffff00", @ANYRES32=0x0, @ANYRES32=r6, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRESOCT, @ANYRES32=r9, @ANYBLOB="2000000000000000ffff000001000000", @ANYRES32, @ANYRES32=r10, @ANYRES32, @ANYBLOB='\x00\x00\x00\x00'], 0xf8, 0x404}, 0x402)
setsockopt(r8, 0xffff, 0x0, &(0x7f0000000080)="3196b3b5354aee1fd3bf3fc78920947ba3e46151d5f197209b4a2117ee9d33b8c745badbde59ff988849fcecbe7035693781cae110bbc1a37a030ac36d8a2464a768f4044d9d19b280a3a6f78ff2060a3a227aba641044c125ea13edb056cceb636eac34fa50b419a7fad9d947", 0x6d)
sysctl$hw(&(0x7f0000000040)={0x4, 0x1f}, 0x8, &(0x7f0000000100), 0x0, 0x0, 0xfffffffffffffe65)
syz_emit_ethernet(0x4f, &(0x7f0000001180)={@random="83771891dde9", @broadcast, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x41, 0x0, 0x7, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @broadcast, {[@generic={0x0, 0x2}]}}, @udp={{0x3, 0x3, 0x8}, {"83eb597415474ce5cc621e39cba3c4efe9042a2b796d743ad8b24cbcceebdd081e"}}}}}})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
r3 = dup2(r0, r2)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r4=>0x0, 0x0, 0x0, 0x0, 0x0])
setegid(r4)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x122)
setreuid(0xee00, 0x0)
r5 = getuid()
seteuid(r5)
connect$unix(r3, &(0x7f00000003c0)=ANY=[@ANYBLOB="e4012e2f66696c6530"], 0xa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x4, &(0x7f0000000040)=[{0x10001, 0x0, 0x0, 0x35dd}, {0x2c}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x2a, &(0x7f0000000240)={@broadcast, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @random="08eb3445568c", @multicast1, @empty, @multicast2}}}})
mkdirat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000080)=@un=@file={0xd80f49edce43012d, './file0/file0\x00'}, 0x10)
mknod$loop(&(0x7f0000000000)='./file0/file0\x00', 0x0, 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001480)={0x0, 0x0, &(0x7f0000001400)=[{&(0x7f0000000240)="927bb8eb12ac6cb4452ee7c1991169007cef440527fe8c5fb99b6706e96165493001be6182351a32fcf4986e2e79e43028f1f68796e21b268ebdcbc09370e62fa4f2f6da5ec3bf8cc84705f372380a51d191f858d2eb251a8c426d267d90482e73767898ef0dd4ed87473b557b693ecbcf257920fb19449819e55f0c12b577daffb7f368fd51471e74f71530c669d5a0f00d1a7cfea33fd7d700c9f859168e554cccd993798691be080144658de8162bfb8d9b2331cedbf9d310316bb8cd35e593b8f9e43e08eb99aa426233206c370f2aaceb47ac2a5e08847631a4a719c0234ee785c6499c6f164e4c44c701c69ce38930fc2ce4f29267a3d60a050d0051add0c88a141aadf94d5778704f4c9d1deb8347e1c3350e6e9a08015aa2f8bb0f9a0184bee4878899894671bd706c4b4cca370c68be0b107cfe7a6c57e79e11b974d298c30015b6ab9fd74e10f3c5d36b3e33cb0584d7b80609ab8280a64e6f51a7e5c044b66a6deccefc55d4dbd63ada7f10b58f0174a512a870b753ab53661d4c99fe28304c900a3358ac574a581fa0ea4d7383cdd25e89e4c475db0e018be13dbca9c7f93d1c73f21365149f7adb853abda7cf643ffe25776b7938da3faae5bed930fd5da48683046f2c15f0969e2843c151097ab84d813d9e3d161c29e96a4d8a005716d1e4ded4d449bd233a263fdd069f8421d7b07e585c30ed3f59a171f6ad355c30f80e3e306a1827cd116830313d7622d9816ffce1795f086e7827d73d11236c2e6f6a960a447af11f821f522b08139d70e5e3d76791ce24233e9951445fc44dd9b8fdd5211af0f3a1aa3df4c481333b3faa1d8776db9408eb1e9043febaebb71731446c4b3892441ab7b86fcd9729c56cb4f7cda66a645bbd3d3b830b80ce1611378ed70e9c092581243796463e8f85bf1268b798f1236debba3e4f45dc444de19192ea512bc9684e470bdcb3cfb50199e56337ff35818c912fc209f8b13540bf7214ecacbfa7cfb4447e76b5e45250f876e91f9deace3827a50e7828fbb5b906cbff6bf534b36edd85fe8a5a5ba47d56ba11bbb5d59d411639c97b8caadf32fda8696e8759a1967b276fcc6c8448588dfed40711419b84df9b474ec3e9375a60e16c08a623dc9328dbaf3602eafd6b1959ce731b80751e98394343a1138edae60d55fa26b4d59526eadb7ddb745cf132cb4a6478f725f600a1fbeac58d0b4756eca46adeee4be355883b7cc6459bafec269fde754d92343cd6c7527152550e4df44d80131827d4c8098905bb17e98d2221b38d527af3f0c493deab69df018f9b75be078ee7011733bf257c8210e2c8259a979c96d4ac70b7be98b2c273136b6c07d1313354095d62cc0a421aad4036b46484bc5c631253221a61ca0fb30c839c89801d774a4218904746f8c69b7e828c6454e42f6a10c26f68663c4604229ebb732d04cf1be93cbcf1be033934794472fccde0da00927cd6260d2ea59d81a5fd1482d9763e568c1b7dc38a0f15f2b113ad62a9a7e43af8bc1146fdc00342133dfd37a248643e9858bf7921494097b0b1968bae5756a215eec75bd12a11d18ce941097bce911ef20869d12c283bafe5c8c5e886e43ef7ba5b7c60798deab2ca1e57bdaa22ef3eb2b75b28f35b84f0c5f430ccb512e23c4a12615bce647f65c2461ff6d4a8426d959b0187a32d9c1a403ab8fc34e322efed36c30224366ec8faf96907e3d42918c7286f2e664aec39801f63d5779c98b0c69b9d4f15c3e9c4516d433c9259668994785ab1a7099f39cc950541190e9bb72dd2cf968bd50be960141f4fa610f9d35e097a441a0bfb05bb831faa2c3951a77b6c7ffbb7b56e7438b686e879a081d046c0549cc212f8ed7a1be6331fc345767e3129f6ec85c4cff97d121958de5a52177dbb9ca158658198ce4fb9ed78e5a655d2213b05cb297395581a051208f9ec1d3d5e94c85673695f8bbe491bbc6a15fd8d4fb795165f950fc635ce8eaac0d120950bb262638a55cb24eccad82b980ed7ebfb9eca06ea5fd7d2492bf26ae7776234eedf3642c170978e90846fccce386e3696ec4d3039e3702ddf23db232f0645767140c969d21db42099477d30665e559defc29fb99ffa86756de3e98fc36c78cf24a30a2dad77e77321d18e0c4d574d2756061a860a2ed541540ccf0010b3ebedf84c199cb7d40efbacbbe67abd698a1da22d078d1f6771c35551de8143ae094777c11565f6a5299d3a4345ddb3d4f361211fab761f7b8a049c03ab6a065d07c57821bd8804475b5ed2f749c3ee1cc180e4f7e2a57a043c0bfc538908612262469025743a446ee59125fbdf139a75c2434a8d3a66d81867730d97c05913f6317b51d9c7aa0810a8bd29de7573e4b88e31dc087366a59e92c95b8f123d69316796651de80a9e9a116e5ef882490cfc662a90c00947144d5ad8980c5200eac141d708c31488e01eab351565071a1ae962c2d6eacdb31b9d3b0bdd446a6c6f1816b0c6e31e24a965eeb64223f6cbcc0e7fda2af2088a9270bd076af679d17ae2f24eaffa33b6a14b698d3e88f0f2f0fde9aeef1d838764b1ce77028f900f938491fa4b0877f3c6a087d499cced76310949a7978d85ff2947136cd9cc9cab66474c4d12c9284aafeb118f45b51247213430d65b04f7c971645c7bad780742974c263cc4ff03deab36875cb5298ddaafdd8fe24247dbe2ae7d246c7a9f9c61e3e13517df0d9f335353344ea72d5b05cf91285a723e5e8b7a028ebbd782bcd0c5fc3649cd7d61d9e73e6de2824907f8e3be0388d26be8127e33f22b7370007c352a3ac5bf59167db173d81821b3c8bd16e84e908f9ed9731eefe5e004a40fc8ec3a7f6d75394e5808e6c128d6b66f027b4704114f90e90884813738e2358ddd432e426db96135c9fb2afe4e8bdac2480ae819893afc2b6c99117848b119ab1ff835745beeb88655a519d267be7fc0f66de82281bcebc217ed4d15f4b8825cb0e3a9d96e4aa2ba327611bb343e67d9e902b46fa3532c503379852c659b15b522fb0d21292275a0ae9d7f5f083793f3c4ac216ac86acc8d23451cfadcad94aad089b92506a357acd188d71b5eafbf7d4f35d64257368b83bfaedd57d05153976163d656a07c2fc762d3d66060b22045abec877d1b41b022dbdd244a69350c51691b460593ae46a52d8063c5182753839bf1f86d8452f9bfef9518e6e6b0e10ddbb34764a0695649e284c7ad0fbf7917f4817a6c5b76e89ff4f79bb01cb7e9f4d933e2de692e389b87a395ef9630647e9adaa959d32ec3a849165503975adedaad094bc0214d4f850b8b45478eaeb933c5095af74a45bc6ba2f3e125b0ab29ec300f8398e864da0f6c7cdb035817ae4d28be62840361522c4da33318262ab73853cbf5021f9e87e3f17ee3126aee8c07a61fe4bb1146a6fc05606bf76298c7a386493e1cb5d1e2bf54c3ccf193a99a0adcdad360bc942956d6199b44f42b4c6be396822455215ed533aac06c9d86639ab99693adb2a474f838c72f510ed9ba63dcf812c3411a458ecd1264800093c3e60146ab2847db892953aba5d03fb7c3b4677c270f4d91f22d1342ca668e130be7dd6ddba2d6dfc8e1c3e5128284917c49dcf1b7c43ad8d7c18632647d3265bee2d898366ab0d4174b19bb96c0620630e1b3c4146f182d454059bdef6e244a239bf09558db4fc027c2a3fb70264b33bd811f0088c86d8493157489b409fb661b2b67781495e8794ee393edb414e58da58c905701d03d65971c2777a181a400ed00423ce57e3d1cc9f37c02f72bc48499abffdb943faaca", 0xa8d}], 0x1}, 0x0)
ioctl$WSDISPLAYIO_WSMOUSED(r0, 0xcd604404, &(0x7f0000000080))
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
socket(0x2, 0x2, 0x0)
socket(0x18, 0x1, 0x0)
open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
socket(0x18, 0x1, 0x0)
socket$unix(0x1, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
r1 = open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f00000001c0)=ANY=[@ANYBLOB="140000000000000007001d0001000000", @ANYRES32, @ANYRES32, @ANYRES32=r1], 0x30}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x2, &(0x7f0000000040)=[{0x40, 0x0, 0x0, 0x60}, {0xdffe}]})
syz_emit_ethernet(0x22, &(0x7f0000000500)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @multicast2}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x28}, {0x87}, {0x4000006, 0x0, 0x0, 0x400a71}]})
pwrite(r0, &(0x7f0000000100)="0000000000424374cfb5f0b099b9", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ae, 0x0, 0x1, 0xffffff01, "080486179c7d9234997b52006000000700"})
writev(r0, &(0x7f0000000040)=[{&(0x7f0000000440)="ed98d361fe88f70178858f8b5724869e3e7a5271a1ce6ea21945d74828c15cfae25477e7c632c9f4751ff7f84efe99be048a18b2e5b7a36305c77d9b1c8e17d9595d6da95f71933205aae23951295a3ceb04606965d6c4ecc7c6434c82f784cfa0e97d038da55130f6183e2b928734fe8fde8468908c60f43dc2353f07a0a2e62747f7d70ea8d07f46fb8af79519c6b3e4c433745a58c6fb1450960d87859f025bdbd7d34d5a935daac9fdfa3314afce5795db4c2539c73be60bf64719221b75f48aa8ef3f656ea222ebbba0d094e8bf487ed9d848ccdf34124164c8dcd377a179581f69db97f0293bb2204e5dc27cd38fa9b572cca4463aa92070f651f95c70d6fd6ee8548e67a42829a8db9c6cb06611ad4cd42b1fc55d6e5a7dc750e106cd0d068cb8865b8e32c67394768387d6604dcf59997dd7e2e0db10de32502bedf9d21ff4ea5ba8933698f91d487bf24f7eec43466cd09bc29535ae5683ddf61b6ce1a10c87efdea1d7d38b71937865c7817adb9e8c350182d6a169953960a08365e9f9fb1a40dbf69dcbfca999203ff198a5fbc54a83419a633ef2c6efa8a44721931c2fa8d95f230a5dfa68133aba262cc8a5f85516f30d77f7285cafd1f1aed8215d5f7c03f9f1ff81f755a405b4af3d8fd8ae5e5928a01768da386c89a5445d05f519a0dfc0de5da96ccfc15b4a7ddc29", 0x1f1}, {&(0x7f0000000140)="048e", 0x2}], 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000480)={0x3, &(0x7f0000000000)=[{0x1c}, {0x5}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000140)=[{&(0x7f00000000c0)='mL', 0x2}], 0x1, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106924, &(0x7f00000000c0)=0x10100)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x6, 0x0, 0x0, 0x0, "1ffffffaae00"})
write(r0, 0x0, 0x0)
writev(r0, &(0x7f0000000480)=[{&(0x7f0000000040)="d1", 0x1}], 0x1)
readv(r1, &(0x7f0000000140)=[{&(0x7f0000000080)=""/12, 0xc}], 0x1)
mprotect(&(0x7f0000000000/0x4000)=nil, 0x8, 0x5)
clock_settime(0xffffffffffffffff, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x4}, {0x54}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x2a, &(0x7f0000000280)={@broadcast, @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @empty, @rand_addr, @broadcast, @broadcast}}}})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
poll(&(0x7f0000000080)=[{r0, 0x4}], 0x1, 0x0)
poll(&(0x7f0000000280)=[{r0}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCGRSIG(r0, 0x40044273, &(0x7f0000000040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x2c}, {}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x2000, 0x6450)
open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
utimensat(0xffffffffffffffff, 0x0, 0x0, 0x3b32b03be1699d68)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x8020699d, &(0x7f0000000300))
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{}, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x6, 0x10000000002})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
ioctl$VMM_IOC_INTR(0xffffffffffffffff, 0x800c5606, &(0x7f0000000440)={0x0, 0x0, 0x6})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f00000004c0)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/205, 0xcd}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
writev(r1, &(0x7f0000000240)=[{&(0x7f0000000000)="ad288462b8750699dff8c5b07934d5b65a029f05be99b0ae51c4badd58f6ce0f5e82a080cc18c50b7f86c6e63c32d12fcf593f17", 0x34}, {&(0x7f0000000140)="1857c23ef77d970b9702b3fb9c72e2148f9a39170e0cd292b9c7c2d4e50b97b36cad566f65205f89caeb0a90ac37faae86997187a0766e7b18127fa871ad43b8dd3998b7a149073cd33db05c56ae3ce95e1f892cb8fa969e24690af784d3a23d940369ecad853e3a95b7267d12adad61eb916181ef948d6d80edba3b16784593cefeea2c03d960c1408dc2be2f225cb314f9adb6e9a970e41abfbfb9a2dd73b21a83", 0xa2}], 0x2)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000500)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000ffd000/0x1000)=nil, &(0x7f0000ffa000/0x3000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f0000ffb000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ffa000/0x3000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000c00000/0x400000)=nil}, {&(0x7f0000ffb000/0x4000)=nil, &(0x7f0000f53000/0x1000)=nil}, {&(0x7f0000e8d000/0x2000)=nil, &(0x7f0000c12000/0x1000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000da2000/0x1000)=nil}], ['./file0\x00', './file0\x00', './file0\x00', './file0\x00'], './file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000b40)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmsg(0xffffffffffffffff, &(0x7f00000004c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000cc0)=""/194, 0xc2}, 0x0)
sendmmsg(r0, &(0x7f0000000380)={0x0}, 0x10, 0x0)
sysctl$net_inet_icmp(&(0x7f0000000180)={0x4, 0x2, 0x1, 0x7}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{}, {{r0}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = socket(0x18, 0x1, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
socketpair(0x10, 0x2, 0x81, &(0x7f00000001c0)={<r2=>0xffffffffffffffff})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x1c}, {0x25}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r3, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{{}, 0xfffffffffffffffc, 0x64, 0x1, 0x110, 0x46b}, {{r1}, 0xfffffffffffffff8, 0x40, 0xffffe, 0xf56, 0x8}, {{}, 0xfffffffffffffffe, 0xff, 0x2, 0x40, 0xff}, {{r1}, 0xfffffffffffffffb, 0x89, 0x4c37789028b5b6b, 0x77f, 0x5}, {{r1}, 0xfffffffffffffff9, 0x55, 0x2, 0x3000000000, 0x7fd}], 0x2, &(0x7f0000000280)=[{{r1}, 0xfffffffffffffffd, 0x1a, 0x1, 0x4, 0xb}, {{r0}, 0xfffffffffffffff8, 0x14, 0x2, 0x2, 0x8}, {{r0}, 0xfffffffffffffff8, 0x2, 0x0, 0x9, 0x4}, {{r1}, 0xfffffffffffffff9, 0x0, 0x3, 0x0, 0x1000}, {{r0}, 0xfffffffffffffffc, 0x0, 0xf0000000, 0x804, 0x6}, {{r0}, 0xfffffffffffffffc, 0x32, 0x40000000, 0x8, 0x3b22}, {{r2}, 0xfffffffffffffffb, 0xc6, 0x2, 0x7, 0xfffffffffffff800}, {{r3}, 0xfffffffffffffffc, 0x8, 0x2, 0x800, 0x7f}, {{r0}, 0xfffffffffffffffc, 0x40, 0x1, 0x6b76, 0x8000}], 0x200, &(0x7f0000000200)={0xc88, 0x6})
openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
setsockopt(0xffffffffffffffff, 0x2000faa4, 0x2f, 0x0, 0x0)
r4 = socket(0x18, 0x1, 0x0)
setsockopt(r4, 0x29, 0x2f, 0x0, 0x0)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r5, 0xcd60441a, &(0x7f0000000240)=0x6)
r6 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x2, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x1000}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x4, 0x8, 0x10000000000, 0x10002, 0x7ffffffc, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x4, 0x80000000], [0xffffffffffffffff, 0x0, 0x8, 0x102, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x6], [{0x4, 0x0, 0x8, 0x7}, {0x5, 0x3, 0x3ffffffd, 0xe290}, {0x2, 0x1e, 0x2, 0x1000007}, {0x7fff, 0xffffff7e, 0x10001, 0x21f}, {0x8, 0x2, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x9, 0xfffffff9, 0x8}, {0x4, 0x5, 0xe59, 0x10000000}], {0x6, 0x2000002, 0x0, 0x8001}, {0x0, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r7, 0xcd60441a, &(0x7f0000000240)=0x2)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x200, &(0x7f0000000040)=0x7, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffffffffffffc, 0x0, 0x0, 0x1000000, "6fc6e23c5b00000000000000e74de400"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xfffffffffffffffc})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0044418, &(0x7f0000000240))
syz_emit_ethernet(0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x74}, {0x20}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000200)="7cdc3f40aa17dddf7830faa1aaab", 0xe)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$VMM_IOC_RESETCPU(r0, 0x82405605, &(0x7f0000000340)={0x1, 0x0, {[], [0x3]}})
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1b08, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg(r0, &(0x7f00000004c0)={0x0, 0xa, 0x0, 0x0, 0x0, 0x210}, 0x0)
recvmsg(r1, &(0x7f0000000140)={&(0x7f0000000040)=@in6, 0xc, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000240)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000140)=[{0x7}, {0x34, 0x0, 0x0, 0x7}, {0x6, 0x0, 0x0, 0x100000}]})
pwrite(r0, &(0x7f0000000040)='\x00'/14, 0xe, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000003}})
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "010000000000000000ffffff7f00"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57d7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r0 = socket(0x18, 0x400000001002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
sendto$unix(r1, &(0x7f0000000340)="4d31bf8a93dbb226c9f2ddd97e6daa1028cb29d37cea390476d28539efc5dcdbec6ecd6f505b4b1f6b5fca9b3135ab676636356a78f3c72bbf151964e34fa2b20e681d078d6d44e542ce149151081b0d3e32b910203e4b33fd60a26556217ec529d7e8f6fd1cd48a68d536dc783c01f5f8696b83be55561061db330ee47750c1240272bbaa350e8d81d17c3f03ff09b64b37c042efe9fe16312842c3f2f65a1a76913d16599add25a1130b2072805b4bd4201ed712f7ac78f38cfdbe2b358dd9d279f9cf5740fc01d7f8b2e127563c430a6a285925de5992beff47435ba0f2bfb115a2ce90dd8e2ee4c7ee887ae522e43f4029357746866e37c4a8ecfce927a190dff6288e99fcc3537aaf484893312f6cbabcf23c0862b850af7bc6c70b7438ae53e377e90e7fe86802530b98130590c604b3f0c860a1ee4f14fd1079d1a0b043cea5be2076cb33947c9000a2d3a4b8d73d91056446324e76406e9c08c82249a3d018c3c4411ab4de110a2f00b6de", 0x5b, 0x1, &(0x7f0000000040)=@abs={0x0, 0x0, 0x3}, 0x30)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000380)=ANY=[@ANYBLOB="2d012e2f66696c6530"], 0xa)
connect$unix(r0, &(0x7f0000000000)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
connect$unix(r0, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206916, &(0x7f0000000300))
ioctl$WSKBDIO_GETENCODING(0xffffffffffffffff, 0x4004570f, 0x0)
setsockopt$sock_timeval(0xffffffffffffffff, 0xffff, 0x0, 0x0, 0x0)
r0 = shmget$private(0x0, 0xd000, 0x0, &(0x7f0000ff3000/0xd000)=nil)
shmctl$SHM_UNLOCK(r0, 0x4)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000516600000000000000020000000000004fecea11ea8fedd6ecfc73fd3357ae26caa0416fa4f376336acf018c3e1c2781e4909f7c8df5f882b167bfcaa5b23ed00f4c8b2ca3ebbc257499a1f132e27acb5d602000d7d026ba8af63ff37283002", 0x62, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
syz_emit_ethernet(0x36, &(0x7f0000000100)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @broadcast=0xac14ffff}, @tcp={{0x3, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
syz_emit_ethernet(0x4de, &(0x7f0000000080)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "013cb9", 0x4a8, 0x0, 0x0, @rand_addr="3732f32c11ed1134fe7e9724eef2317f", @mcast2, {[@dstopts={0x0, 0x35, '\x00', [@generic={0x8, 0x71, "0989a965ff475ab8a84e3a2812dd804d862edc54cb535c99d7417cb1fb2f0c34f0e3cf8e6ac45b895105b6765f607d50fe43ced806490432366ecff2efac2a894e659359816bf5c7268459c7432be4c0e8a39212a6b5e87b959a8a9abe0d6b89e1dba0fb866007f2cadfe19729a91d1157"}, @padn={0x1, 0x4, [0x0, 0x0, 0x0, 0x0]}, @generic={0x0, 0xbf, "070702a5b93b816f79afa3410b69e54427f6e31c9581e89867e6c9b0dce00ac57563d3654800c7d52395e8995d8b022342a762cda4b486720c3a31579ead27f0e0fc53e387bede3c8134837f3d3879ea0fb8b7b61d9ec6d154497febec53aaf5c18d937b7ec7e277dc4bab8656e3f72ce85b30a7ded7fac87299b83ab9582e383da18397b1ad3fbdab4e34721f14a70dae5a85f950ec1a865c80b50fe00308af0ff491d9b94595fb710252d1e5f7a59f747de2284aae6b2919491041c0e307"}, @jumbo, @jumbo, @generic={0x0, 0x61, "ecf7e7f8c03d0c73fe50829902169868bb6a56d74019bfc6206f3db473d9e5f573a4f848c0e5f9be6172518c83ac5013b85cdd56a8bed4f7c58921c38a22f0b972ba35fdd8be8111a38e17bdf140dbfa4fd983c4a281461c32b917866e4cb33b03"}]}, @fragment, @dstopts={0x0, 0x0, '\x00', [@jumbo]}, @dstopts={0x0, 0x56, '\x00', [@generic={0x0, 0xa6, "c0374b898c7f5fa9d7991edc5b30cee66066834a27fd7130bb53c61a1a3a72f5b66f0928b86fdbc783c3a614180a821530547804b3e33ce1395ee186931672ba210f156ec3ca7f7cdc4f2e658e607da1983b075a45403f7a2bd964248a7edba4b29d8060379b049531764707c177d88a0b51f72e5379e55aad266260194f9dc7440f35a933d9f10043d26b6dc0a8cac8c5010ebda34937fe74d8d1b109d3aa94e29162265adb"}, @enc_lim, @pad1, @enc_lim, @enc_lim, @padn={0x1, 0x7, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, @ra, @generic={0x0, 0x1ee, "7724f1f72e8edcffffee156c28ca87524ed93300dc8b0e73808beaec5914126d3685a0726d89bcfcf6d9e0959456f818e0519d8a10026c498eefcdea868f274918031d9a4240c9561fe5e6178283ec7ffbec8010b641f29b98dbe8d4246210b8a6a9a2d818af77eaf28965f0e9c5d73b7a5d31b6d0536979d464f1078e31a3c6f15d045af8e9966dbb9bc5a7c2ac65377fd544665a461432f342149e6e77b8cc5dfbd537bd995000d2f37f4b4211438cfb4157ae925ee78b06070e5781956aa2b83c195ee498384762078f2a7e9074c0b494b86e13f4e20e12507d8651237068e210cb4c2edbd7738d0bada55f713d6e707f9cef755c4d33c4fcdeff12e357ed04d3583a1ae3e8e439c0526ce43750e480ebfdd7eca132d447bfd40e2a8a3cd8dfd186841395e7ebc92949bee41e9d3330c1a7902be043f1f9cf9762a638e40fd5d19178855fdbf00e31701307b9493ed0864fd0d546b957639a43d1bd3da81c96e971bef4e7c8f887f9c43edf8a96f09fdd96bd1b65020ebe1e3777c20bc30c4383849cbb3318a86adee0bbb97577409c8bd96f00b821e2f6471b24509a458314db06134cbbac977c1b3f86ce2aca7ee77736482d730859288a635b13c6d4eaa0144bdac3c2b35f90b9ea4b38bbf34c284b528b1906a84db626d10ea75bdfa22643f43510ac2fea695791bd017d"}]}], @generic="b0ab940e900469a18a6b88342d002b9ae2c6309674ec1a1b"}}}}})
r0 = openat(0xffffffffffffff9c, &(0x7f0000000180)='./file0\x00', 0x200, 0x0)
r1 = kqueue()
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r2)
kevent(r1, 0x0, 0x0, &(0x7f0000001980), 0x801, 0x0)
kevent(r1, &(0x7f0000000040)=[{{r0}, 0xfffffffffffffffe, 0x35}], 0x203, 0x0, 0x0, 0x0)
sysctl$vm(&(0x7f0000000000)={0x2, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000000100), 0x4)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
r1 = dup(r0)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x7)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000000)=0x7)
execve(0x0, 0x0, 0x0)
r0 = syz_open_pts()
mmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x4, 0x2011, r0, 0x0)
r0 = open$dir(&(0x7f00000006c0)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f00000001c0)='./file1\x00', 0x18278, 0x0)
select(0x40, &(0x7f00000000c0), &(0x7f0000000100)={0x3ff}, 0x0, 0x0)
rename(&(0x7f0000000040)='./file1\x00', &(0x7f0000000140)='./file0\x00')
open(&(0x7f0000000000)='./file0\x00', 0x70e, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0x1}, {0x4d}, {0x6, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
openat$zero(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
socket(0x2, 0x2, 0x0)
socket(0x18, 0x1, 0x0)
open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
socket(0x18, 0x1, 0x0)
socket$unix(0x1, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000400)=@file={0x1, './file0\x00'}, 0xa)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socket(0x18, 0x1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
r1 = open(&(0x7f0000000040)='.\x00', 0x0, 0x0)
sendmsg$unix(r0, &(0x7f0000001480)={0x0, 0x0, 0x0, 0x0, &(0x7f00000001c0)=ANY=[@ANYBLOB="14000000000000000700000001000000", @ANYRES32, @ANYRES32, @ANYRES32=r1], 0x30}, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000140)=[{&(0x7f0000000680)="d6650009e000001342", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504442, &(0x7f0000000240))
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000340), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f0000000000)=[{0x1}, {0x20}, {0x22}, {0x2, 0x8}, {0x3}, {}], 0x6})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x1, &(0x7f0000000080)=[{0x1d}]})
sysctl$net_inet6_icmp6(&(0x7f0000000100)={0x3, 0x12}, 0x4, 0x0, 0x0, 0x0, 0x0)
recvmsg(0xffffffffffffffff, &(0x7f0000000280)={0x0, 0x0, &(0x7f00000001c0)=[{0x0}], 0x10d5, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000240))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r0=>0xffffffffffffffff})
r1 = socket$inet6(0x18, 0x1, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8218694a, &(0x7f0000000300))
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000540)={0x0, 0x0, &(0x7f0000000480)=[{0x0}, {&(0x7f0000000240)="1c7c7bea39b6a40086", 0x9}], 0x2, 0x0, 0x18}, 0x0)
recvmmsg(0xffffffffffffffff, &(0x7f0000000480)={0x0}, 0x10, 0x0, &(0x7f0000000000)={0x0, 0x2ff76176})
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x10000, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
r2 = semget$private(0x0, 0x4, 0x5b4)
semop(r2, &(0x7f0000000040)=[{0x2, 0x1, 0x2400}, {0x2, 0x64, 0x1800}, {0x2, 0x402, 0x7fc}, {0x1, 0x3}, {0x0, 0x2, 0x1800}, {0x1, 0x1, 0x800}, {0x2, 0xfffffffffffffff8}, {0x1, 0x800, 0x1800}, {0x2, 0x2}], 0x9)
semop(r2, &(0x7f0000000000)=[{0x3, 0x17, 0x1000}, {0x0, 0xab45, 0x800}, {0x4, 0x1, 0x1000}], 0x3)
r3 = getgid()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000140)={<r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000400)={0x0, 0x0, <r5=>0x0}, &(0x7f0000000440)=0xc)
setregid(0x0, r5)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000280)={<r6=>0xffffffffffffffff})
getsockopt$sock_cred(r6, 0xffff, 0x1022, &(0x7f0000000400)={0x0, <r7=>0x0}, &(0x7f0000000440)=0xc)
setreuid(0x0, r7)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f00000000c0)={{0x80000000, 0xffffffffffffffff, r5, r7, r3, 0x2a, 0x7f}, 0x5, 0x7, 0xcb})
fchownat(r0, &(0x7f0000000040)='./file0\x00', 0xffffffffffffffff, r5, 0x4)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, &(0x7f0000000080)={{0x7f, 0x0, 0xffffffffffffffff, 0x0, r5, 0x192, 0x8}, 0x5, 0xa086})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000002})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3e}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
close(0xffffffffffffff9c)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSPGRP(r0, 0x80047476, &(0x7f0000000040))
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
mkdirat(r0, &(0x7f0000000180)='./file0\x00', 0x0)
chroot(&(0x7f0000000000)='./file0\x00')
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
recvmsg(r1, &(0x7f0000000700)={0x0, 0xffffffffffffff36, 0x0, 0x0, &(0x7f0000000400)=""/210, 0xffffffffffffff2b}, 0x0)
sendmsg$unix(r2, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="28000000ffff00000100000008cd7f8603"], 0x28}, 0x0)
sysctl$hw(&(0x7f0000000100)={0x2, 0x3}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x7f)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="9f7ea261", 0xfffffecc, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x61}, {0x50}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000380)={@broadcast, @random="069e4d509b40"})
setgid(0xffffffffffffffff)
mkdir(&(0x7f0000000040)='./file1\x00', 0x191)
setgroups(0x0, 0x0)
mkdir(&(0x7f0000000000)='./file1/file0\x00', 0x0)
setuid(0xffffffffffffffff)
unveil(&(0x7f0000000100)='./file1/file0\x00', &(0x7f00000000c0)='c\x00')
r0 = syz_open_pts()
read(r0, &(0x7f0000000000)=""/243, 0xf3)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x0, "219a97de3027f6cbbe51b482344356243300"})
syz_open_pts()
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x205, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1000300010005})
open$dir(&(0x7f0000000080)='./file0\x00', 0x8110, 0x0)
execve(0x0, 0x0, 0x0)
syz_emit_ethernet(0x68, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast1, @remote={0xac, 0x14, 0x0}, {[@rr={0x7, 0x5}]}}, @udp={{0x3, 0x3, 0x8}}}}}})
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000140))
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
getsockopt(r1, 0x0, 0x22, 0x0, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x612, 0x0)
pwritev(r0, &(0x7f0000000300)=[{&(0x7f0000000100)="24a007e6c1b75bf338129eeae76139bb096571ea7a60b4abb52e31237a0400d30000000000000000000000000065685fdf41f2a7490719b6148dc50b412aec8df211231733c3441a687ec40aafd93319d08154a02e1b10cc74c26ed8de1a4ac252cdfcde6719015001a7ffca163e87f87b70a6443ba6ebe39f85b2b89e3d300a1ce7def1b8c69d52415da726e32b8176ef62e3ea49901d5f89d14eef5c56c43be36274929de5602257fb2bfbefe4bf3bb6e121a962086bddd19613d8bd606707a4b3d46913fe83329ebc6d9b4d10781588f9ff7e73da2fdfb5ece790678578a9637d35ef0fda7ab859b5951dc7256fff1b4e058065226c2a7cc345da81b5beb8d986a8dd5adda69e2bf278a6d1adc59b26b2687dc8dd3c33d11f33d48af8a82950da73d10af55cabcf699fb3e6647d6431185bb6af22ca7ef510e3cb5c04771d8a040643ca99c2be68e436d521d45b4eeca9985910db108d2366", 0x15a}], 0x1, 0x0)
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000040)="981a6d584ba8db0a889b2d22ed85347e2808ba33755840b3b4119ae60d28eb27c809f0f017d599facad1", 0x2a}, {&(0x7f0000001100)="e50ce8512f2e8d8701778d5ecd3ee8709d9c2365fe141db057e1f7d4b23626ed89c8b11c4924ef2253fe360811e40ae61b3ab86ae06aa9afa0c44128d4160d23b2f8e91bc7d0d3a1a662e8abd758bb6a80af224bba5aadedcae558a62778e7e51616a41429e8043385babe65bc58c2a882a6faadd5f899511ba0c5087bbf61ca7bbaf6df1539a20492bfdf4b88b8b352c074b5447350d57e63b543e2fd288882028b41086457bced449fdf3f873e014fbf379cd714c164e29b57f00a44a2f1ed334fe96bd78daaf63d5e15b308bda9d88b9b7cd7635e36432333c1feee4fa1c076851fcfb738d181a2086be97275762a182caaacbfef22aaec824364e809ea6517527fd3437a84a2920d77e927008bb974f4fc3ba663a3057213340f244acf3175a75c3cd1365036c415b4d3ac225de1638ebb42d05ede84e8ca6e19e40c2c647f8030aa9ff24b1ac7dbedb1138c1171a68b6a8ec99629f0fe11a1b538fe662a9b758f3b6f2116f19db2437d8c4c78af6f12dc5f0316f6258237e35a00dc44aeb40d792efd5efeaef7948886655804a9aad2ef23e04592253376fab6697ed2fc88ed5c99c609a73df29f411442f74d4f28777d23ca8a954c80bf8ac97fac487dc773fc8dede773fac23e49863c9ed8e70e28b872338ac5e580f877ce7b561e3d1aaad1a0ec7f6f9e788d5c9f93c7e16fbedc911e57a92caeb4f84666dbaafb9925a43236513c8afb79022b91252a5b233fb9888fac17d0244b5efa47a84d6c683c6022000b6112dadd9aabc29f03264822a03f76a92a82f812ce9d5000ce10e315e1b7c3fc03d436c59b7e7fb3cb1a544fa7493a9a8f0a21b4c1a6beff7d2df77d8edc99bd561e2a678b6f6ef4216f03c6bedb8e508dd16dd29587f5b3b23a0278202e2583f2394924b83ff43f0ac25fe96d775998a60e9e7238bc5c9c737bb6a194eeb6f4b4e739881c21d20f24e852abab3817f474f772234b46698c29dc6366cd8ca70ccfbd1a063e2447214155ca24c120d18687388f9a8846c4469d6954a20507cdacd58e57a4604400872ea9deb1cfee44f1c2fdd353bb6f5ac71c49e4fbffe4f5761497704e9437cea4dcfabbd0c17c47f7b51f25526a0d703317188e732824601c570e0d82601202055225f703b937865f42a5360d9f989a1241c49cce4270f7af7bab25007eb80fc576f8290fc08d76b21eaec0b908ff67899e3131a7ce7a8cb41f319d2f2cabcdbf8b37a1eb7aca83fd605c903e9b9b94b9c353e4aa23fbadc5a0c495838768331811949d903362ff846176211492d124016c6e42e01918174ce4c1d57a60ead0e8aee422420b22b74c1da4e54a29d259d0754f84c83be7a8380a66952466c8d2b605d94c75422c8fbe02f53f52e999242466ef09d89ef01e3085352f23220fac1c4a4713279129e8133efac2cd9f17ae7f592a32cc0e69b54e98fff07395cfb252146c725b7923af7f0c601730d85db77aec5ac12fcc4f692e6ab0e2639bf0490e0bfe27b72c9b57ebe23c8e06fa2e8d59238f40a1d4592405cee01dc36a3e2c61f4b082785f61c7a1db2a601d47e493bac10f1ece42f6deb33d1927c3e4992591e4c270eb305d27a384437cfc2974531b2bf474010e7ad5e4103a3a2aea5e3b98df0c7685ea99a5d74898d66421d7680129f5aa7e47f65e0259983bbd931185ef1cc1bee92953b3a756067696201eea4e15a662b11ea5e00e6a470c71b998c67b3f791642a4ca37360978382bab5c7c85f007516ca468af81f74c19f792ded82fe7c6d8774ff789c53e24a868e07fac1f9800e8a815471d9cfb7782deef7126d268abd6dd04265d2a460a223cb9fe5cdc2f78d240e820307f3d26665554207ff8d0b77f834f9ed6f067f94b6baa6036e77860a02cbff9472d7a39e1ef4afbb5bc0302411090194ae603a234d933c50649abd8ac825d55d7dc384c78f555168870b47de0df4a89ee94d2636c91d054cd3caa7235990f907a7eb192c6dd0e2c7aa64122f72d3995d9188a4648d9c384717c5c7cdab8f5e24821836d99541c319ddffa52046a0cbea97db1195143bdfa9a24204e688d64b607d2d8f435a242eddd7585b791dc7ba2a3406bfb86a80c4e5289cf2424393e518fd7fa9ffbc093df758e1a8e9884cea3ed10f81961acc9b7df459e29a6f28f67436d86139e0853b0685a04ba43f4cacd943a41e1c1acb049429b22ec7412c5ac5f6890dc2f89048cb561b57bed156c77de33747c7950ee7944e5e7da8f1d736dcf5b26971d6e7a577d812e8c873155a70d85283c078006f87a0ee735cf50794667e3e5f415caa9e6171ab61e3425a50dd5e43c4f7c51530c9175cb75acad2fb3b85845b5c608821a63d93bd12a6234677b5cbb4098c00b9c869e3681c4cc865d9ba8f8a80ab317efe6652ddef53a1abd83234be48be139afeb5eef18204760ea71e697d8dd36dde38c7fe6913999ea8b5a8b1655f05e1796080969f9b9a0575186b37ba525700747e6329645aa0fd3e329d770d1031d98fbe09e83c40cdf7a2da646a933d6f4be4fa6fc065c13d045420e9a73fdaabe273dfe7d01666b619aabdbff824d527b4477c48215382f18ddfce268e0c907dbe68111a56906e7917d1bf9d6808e740b2ec7a82bacc051df14fc330d7fcf00fded2676158217349c79724408fdab1bf6f22ccbe2babe12d376d770aeee0830cec905c24692895e1c45d68c120aa3a5d071d205612d4af9fb4c05a41f5290207168b902db39dbfde95b7b95f5969d30e2bcc15989a1789254e5d1439e4a2d813e791e39913dd960528ff199bb6fc4d9e1f7c220a0dccff86bd04e7f4449046aae8baa2036f21c2a4929650a0c1edc32ff8b913c492b6127f236316911fac47ee8f478c442ae70fe9ff11b04ac450925204e35dbdaf1872e189e23ab5760fb7794ef5973c74a6fe977864d14e78ea89fecab05b384359343e7043deb47fa09f55fc9970093a739726d6a6cd3dd08dc8a9aba5b0f2c33018ab5346a2cf1efe13a3ec20e0c465552b2e046586ab9211dd853ae55817ad0c50bff690dad85e9be57cb3f35c8edcf2fd0d780d53f018a1d92355a7e6777c2809eca942409018f84f39449b5b8ed3bbd676c691b61cbb66fff9ac8e53a3067c00d53916801bd319e1e36db36cf40be70c80c9a4db429935ff72a249039efb072fe065f65d6d60bec152cee8838eb15887c271f89a7c5f9100cecc8ef835b071c7e5eb1a5c1c64cb69d3e9b122f72cbf02dad4fd1d962b9047796d8daddf121a3aa185c743f4f90d0ea5fd03994caf312cf8f030f09422f3d3154777bd85a588308d403e1d92b3c7e07b682b40e1d7eee339a398bf066ec1f79fca7b0c997d951494550934443ae0cf922969bfa92f040e105e3dab1d525c6d6f88d690a13a36823f82255f3deb6ccdc0091add382e13b27a5aa450d2fd33c3a9c4204b8dd26fe47aed4633e20ca91f978b76c23d17da9121ce68efa898f038f225cf2e064ad8c9c7c699c29e19bb66decc13ffbbc489b5c86c5d571fe694c941063a6198afed1433fe30bfca332c32ec030e6d7cc780f8f44dcec78e079b0bc86fff3e965350f84c82dc3a8fa55198175a5cbcabc370505dada469b58637d985afff49bd0ea5d44137f9370248f3b42f0b5116c1bca81f63a0af630af146ab1e221f0d97099eb7d9fde8353534799ebbdff97b2ca7683cc32390eedf7086f7d9347bffe3a7ea5e069cca29cea2b6b086e746bf27807d7f13a615440894dd42d47577176ec1a1f587f298e3211d09d5ac552013154828de0aa45373c818756b988054432081524232be2958c0839c99b043443975f17986bb21aa84147fc542bb3d4c69857d2085f47871280ea6bca425230683936b7606de3d5d4564aff740a1c4242bacb81539ac694735b5abdc118d4462b93e8b9cfa9c415230c86c0c4f9883e985d994c17d92468e6ed08656d45063cb7e025229d7cede48f8f5f6954b34a5adffb327c44589643dc04cdf681b47390cf355a40b369df6134bd038486666781ee011d8dc98856a0e0cc3b9b29969f6a59dceff816139c2886484c635386b4ac82f2bae6f5e17dd7fbd53aada5ae19a57732ca2aac53303ba60556efa202e049525d131bb2ef4eafa1a4d15a3833fd81ec3b69fee9af5c8e33d1614e02f2d1412a5b8ad88a61e38b1f6279008f0d3c3d1462b0f749bf5cb6d3fc7927b2c543b4fddb71b160ddbb465fecef53f677c24fce598997b30d432238e7a6b041c3cfbc3b7cdc9eda589039c8b20d16db749bca16c59c06a8f9450ba1a32f96f85e5bd8fe09bae7017d7d285344d717ba3db0f56269db435f33c0b263707d0cf48fa1f81d1c8db2bf1ae58a0d702b16d7417489559c44ced9ca1e6cb4db6d198a92171aaa59332b84f24dea5dd7efc7b247974d59ba7a2c1d0d654319c146ec58b0959254bf12fe7cdf4160d1809e65e5c92b7f773f29bfecd0bcc4725339b746ffad0e40471d286fc0d2c4abca94923293b7f2b66d279512edf944df50b83b0b1a54ee5b5cd5557c898ddfe28619fb3ac0f168f3f5d20da4bc9bf278176ae97e2255fb3b4ee055d8b38bff52be27e430213aafa776e248a29b3e1086e939c3803dcf33c5bb73a8f03337d15449b8430a0615d1ed90fe697e859ec7754a3a64d06379dffc1fdac02ad3dcda98a9dcf832fc929f705e215252b49826699b402c436e11768eb93ce3a2a6bad2324edb474e47c00a3253d3ee1fa3efbae1586f9f20f484b744201d9768a05f91c80e982e325fd943ae931303d5480771f6e498b4c03b8530abf306910641764b302126b3315fec4d7379e2af7a6ac3d0f031eaf3192f230b7baabb6d18831398c83f4aba6bba907d439b87d0924e220681ae4f1bf99915a5f3d3faf11e698eda101bece2d1ab85aeca4b55ec6919b213282fe96c81ef6c745baedcc8ee229497c7b2c4109798c7cc2d06798da808ee3205a9aecae7106a8c0cbb919538f14adfb4574372f77ebfbb6f2eb95d574cc16aa3b598ba35dc48402d776888cb6210bd2aee4bc9710ca08d41ac162380878aafd711d18ebe76b1907ae10f822f0efaf81258ef37a306bde0d01750eff1e5c8f2bad9fe1c0a1d04fc860ef1ae46afdcb86def6f4b13bbbe9a7ce027c690aeac0134f7a5908c8e38d74d687a148153705bb5cdfc52f17030e899863666c4c6f3dabbad2d1b43d2580969012ba1c960f5d1411b47c1358eb979a8bf78953836efc7733559d54d83d43bd777c60bc2df3880de7075016390e76e5f6eff17fe83aca79b57084592673ed96ddc4ad2cbaa798494c694ce35ad6ee4b3846dc898aae607eb006a0b1045a608134a2ee84e7bcd99fd9a3be05e9e0320fa3451a483323529306d934f8d4c6c1a18458436e7e52ca4f0bcb1d37a27453c35ff850d45675489d595d8caebc707dcdc2202eed60c87dc6efff601aff92559065d2ab21d6e1e81e14c1c6f747239829b44c0402228b71c3c721c092320eff18c84786f33e4775103a7533faf8d9bbab957a1529a6c8844ab10f0d78d1f2da8366ebaccc81a79d9f6f6f940c002e7bfa1d569d90c7e98262897f57b6203dc8a5c3def4b45b91bd729b1ba67b5b946fd9ac657e13598c7b7d50c648c3427c782ceea7a9c54ca512f647624363de9c0926166ff065975cedcc8c10a39a47eb5b2534f8cc53d8f7b6ab57eb9aefe59b9ea8e6ff2854bab43af4735bbd6c5", 0xfd7}], 0x2)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
madvise(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x2)
socket$inet(0x2, 0x1, 0x9b)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x5, &(0x7f0000001280)='\x00\x00\x00\x00', 0x4)
socket$inet(0x2, 0x5, 0x1f)
fcntl$getown(r1, 0x5)
r2 = msgget$private(0x0, 0xffffffffffffffe6)
msgsnd(r2, &(0x7f0000000400)=ANY=[@ANYBLOB="0200000000000000c1873c269a5bfb1783f837a2f38ccb69f4c5dc9497985df45613b863bb462b6a724983da5c5e5b280af022c7430c1c2471a696fd3101e359157bfbc051705e009ecbbd28d95768c44d842099d011ab2cdf82013f892e4ae799bd5988eea138fe9f8243b79a3aa2c1a0f929d37b3e1f6a6495b72684f0adefdbc09a43525733f4febafa1acf7e0fd71e936ca9fd3b66301049a65f"], 0x1e, 0x0)
r0 = socket$unix(0x2, 0x1, 0x0)
r1 = dup(r0)
setsockopt(r1, 0x0, 0x4, &(0x7f00000000c0)="a2c2d0770d38c1e9b308e056f57f2f42141f60d5a25cdbb432c27ac5314f79c782d3263a933da8a2a132a9e2003d477e28192233a6abce4d25b38db77c80584845a766b88e796d8fc46844daf6ca2932589deef224f1eaccba6116d6ff4d68af168259e0f9ea35df71abf427115258b27f0605678c8bbae5c4465ac8940fc1457f8659effa58bf324709633d698ef87afa5099e8beb7d34dc864727cf713d7029b5a529e2fb32550def4c73faecc08299b35165ef3b8b602a79cad3e5a2a71983e1cbd23dcdeb6e4fddab50ee21e4e9abcb89eaf491a540064c314536f406b83a6", 0xe1)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(0xffffffffffffffff, 0x80105727, &(0x7f0000000180)={&(0x7f0000000140)=[{0x8}], 0x1})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000180)={0x0, 0x25})
r0 = open(&(0x7f0000000040)='./file0\x00', 0x80000000000206, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000380)=[{{r0}, 0xfffffffffffffffc, 0x51, 0x8}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000240)=[{{r0}, 0xfffffffffffffffc, 0x23}], 0x0, 0x0)
kevent(r1, &(0x7f0000000000), 0x36, 0x0, 0x300, 0x0)
chown(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
kevent(r1, 0x0, 0x0, &(0x7f0000000280), 0x6, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000140)=[{0x45}, {0x24}, {0x6, 0x0, 0x0, 0xfffffffc}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
write(0xffffffffffffffff, &(0x7f0000000100)="4e981d6c6b153b015386907119eb16c4c504b7d57b864b294604674792ebb3e9320528924979b557ac7bf6f33cde99eda2573d26e76d76c67277bf0392d03e6ad418", 0x42)
socket(0x18, 0x1, 0x0)
setreuid(0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, &(0x7f0000000140), 0x210)
syz_emit_ethernet(0x4e, &(0x7f00000003c0)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "f12be0", 0x2, 0x0, 0x0, @empty, @rand_addr="92f13b0184d6840edd6a33040fc89155", {[@fragment={0x2b}, @hopopts], @udp={{0x0, 0x0, 0x8}}}}}}})
r0 = syz_open_pts()
nanosleep(&(0x7f0000000080)={0x68, 0x7feffffe}, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000000), 0x3f, 0x0, 0x7f, 0x0)
close(r0)
kevent(r1, 0x0, 0x0, 0x0, 0x3ff, &(0x7f0000000100))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x1fd, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x20}, {0x4}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f00000000c0)="ba58853729347c034ee03ae84b60", 0xe, 0x0)
r0 = socket$unix(0x1, 0x2, 0x0)
recvfrom$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x2000003c)
openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r0, &(0x7f0000000040)='./file0\x00')
rmdir(&(0x7f0000000340)='./file0/\x00')
sysctl$vfs_nfs(&(0x7f0000000000), 0x3, &(0x7f0000000040), 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x10, 0x0, 0x0)
r0 = syz_open_pts()
r1 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
dup2(r0, r1)
setrlimit(0x8, &(0x7f0000000000))
pipe(0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
listen(r1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x2, &(0x7f0000000040)=[{0xb1, 0x0, 0x0, 0x81}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000340)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "1fc3d3", 0x0, 0x0, 0x0, @empty, @mcast2}}}})
sysctl$vm_swapencrypt(&(0x7f0000000000), 0x3, 0x0, 0x0, &(0x7f0000000140)="269c9d2f", 0x4)
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f00000002c0)={&(0x7f0000000280)='.\x00', r0})
pread(r0, 0x0, 0x0, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0x8010570e, &(0x7f0000000040)={0x1, &(0x7f0000000000)=[{}]})
ioctl$WSKBDIO_GETENCODING(0xffffffffffffffff, 0x4004570f, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
mmap(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x4, 0x2810, r0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x9, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1002, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x4}, {0x3}, {0x6416}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@empty, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @local={0xac, 0x14, 0x0}, @remote={0xac, 0x14, 0x0}}, @udp={{0x1, 0x3, 0x8}}}}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x15, &(0x7f0000000040)="8861582ca302a6517ff658ea", 0x4)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x2, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={<r1=>0xffffffffffffffff})
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0206917, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000080)=[{0x20}, {0x64}, {0x4000006, 0x0, 0x0, 0xfffffff7}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000000)={0x2, 0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x7c}, {0x4c}, {0x6, 0x0, 0x0, 0x800000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="7cdc3fc0aa17dddf7830faa1aaab", 0xe)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0xc0105715, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{}, {}]})
mknod(&(0x7f0000000100)='./bus\x00', 0x2005, 0x8000000000005200)
r0 = open$dir(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000840))
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
write(r0, &(0x7f0000000080)='>', 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x64}, {0x35}, {0x6, 0x0, 0x0, 0xcc}]})
pwrite(r0, &(0x7f0000000200)="b269f66b39b9de6923518e4027fe", 0xe, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x4, 0x0, 0x0)
clock_settime(0xffffffffffffffff, 0xfffffffffffffffe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x1fd, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000100)=[{0x20}, {0x25}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f00000000c0)="ba58853729347c034ee03ae84b60", 0xe, 0x0)
munmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
syz_emit_ethernet(0x32, &(0x7f0000000880)={@local, @broadcast, [], {@arp={0x806, @generic={0x1, 0x0, 0x6, 0x0, 0x0, @empty, "", @random="c8431bcf65c9", "839e809516fee60b528eaae06f2ccbfe"}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x200b, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r1 = kqueue()
r2 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
read(r2, &(0x7f0000000140)=""/134, 0x86)
socketpair(0x6, 0x4000, 0x7f, &(0x7f0000000200))
r3 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
read(r3, &(0x7f0000000140)=""/134, 0x86)
open(&(0x7f00000002c0)='./file0\x00', 0x20000, 0x80)
socketpair(0x18, 0x4000, 0x7f, &(0x7f0000000300))
syz_open_pts()
fcntl$dupfd(r0, 0x0, r2)
openat$speaker(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
kevent(r1, &(0x7f0000000140)=[{{r0}, 0xfffffffffffffff8, 0xe1}], 0x9, 0x0, 0x9, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000002580)="3a8fdf4b0ffdd134", 0x8}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f0000000000))
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{r0}, 0xffffffffffffffff, 0x21}], 0x0, 0x0, 0x0, 0x0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, &(0x7f0000000040), 0xff, 0x0)
kevent(r1, &(0x7f0000000040), 0x36, 0x0, 0x300, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
sendmsg$unix(r3, &(0x7f0000000580)={0x0, 0x0, &(0x7f0000000500)=[{&(0x7f0000000100)='Z', 0x1}], 0x1}, 0x0)
kevent(r0, &(0x7f0000000040)=[{{r2}, 0xffffffffffffffff, 0x11}], 0x6, 0x0, 0x0, 0x0)
getrlimit(0x0, 0x0)
msgget(0x1, 0x444)
r0 = socket(0x2, 0xc003, 0x0)
close(r0)
r1 = socket(0x2, 0xc003, 0x2f)
setsockopt(r1, 0x0, 0x22, &(0x7f0000000000)="caf5b415", 0x4)
connect$unix(r0, &(0x7f00000003c0)=ANY=[@ANYBLOB="8202a6917c"], 0x10)
sendmsg$unix(r0, &(0x7f0000000380)={0x0, 0x0, &(0x7f0000000180)=[{&(0x7f00000000c0)="38c9820b65862205", 0x8}], 0x1}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x35}, {0x60}, {0x6, 0x0, 0x0, 0x108f}]})
pwrite(r0, &(0x7f0000000340)="bbc86460e2440102f2c74cd58cd7", 0xe, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e50700d7"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x5, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x2, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x3a, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8003, 0xe0, 0x0, 0x0, 0x0, 0x0, 0xffdf]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
syz_emit_ethernet(0x22, &(0x7f0000000140)={@random="5161580afd92", @random="9e1e4b1a4af3", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @broadcast}}}}})
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCSET(r0, 0xc0384600, &(0x7f0000000140)={0x0, 0x4, 0x0})
sysctl$hw(&(0x7f00000006c0)={0x6, 0x18}, 0x2, &(0x7f0000000700)="dd919bcd", &(0x7f0000000780)=0x4, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000300)=[{0x3}, {0x30}, {0x6, 0x0, 0x0, 0x2000000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000080)="c89f69db925aba20ff1757a81fc5", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0xc0}, {0x20}, {0x6, 0x0, 0x0, 0x3f}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
sysctl$net_inet_ah(&(0x7f0000000000), 0x8, &(0x7f0000000080)="22ca39c6944c53f0e2784e099241008dc4f864fc582900de8dd994bfcbbea01f9a941d92bde0be9c6a8d1dcd3cc37433db956c018c419e50b1f848d8a63b958b93933edd5e1f1a45209b660cf520107b2358d5b3d24c812117f81e9c1d1baf3b2c700ca177b51a4380f33a854288735d5a0eb3499fd15148b58ac46400c799638500c5d679cbff8a494f62437accab850e5bb1a9fa928393", &(0x7f0000000180)=0x98, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0xb}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000a40)="b100050460000700000008000501000000000000cea1fea7fef96eefc73fd3357ae26caa0416fa76d06336acf00b7804be781e4991f7c8df5f882b2b7be1aa5b23ed00f4c8b2ca3ebbc257699a1fffffff7fb56894789146cb05abf75f4be64c053abcd402000d7d026ba8af63ff37282921e4fd89720f1a8bfef6aca4911faff5a872c881ff7cc53c774303b22f310b404f36a008a371a31a000400000000000000c23b93eb313766a28126232230fb8c7ea00d9768f42a0579f3ed60cba72762e054f52d6dd0c12d61a251165b8d1ab8dd5caad83592cd8c29da505cb6ebf6027c7fe7f0fd0fc9849f811bb36307cd4039cf4f0db632b44cdc7c53255c4d7e908450c8f5872b1dabdd46a4bc05567d08c46f7ea1d243ac1d0000000000000000", 0xb1, 0x4, 0x0, 0xffffffffffffff08)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x44}, {0x84}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c4, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
unveil(0x0, &(0x7f0000001300)='c\x00')
madvise(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x0)
munmap(&(0x7f000081e000/0x1000)=nil, 0x1000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
mquery(&(0x7f0000820000/0x3000)=nil, 0x3000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f00000000c0)={&(0x7f0000001200), 0x1})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f00000000c0)={0xb21, 0x0, 0x0, 0x0, "1fffff0366dfde34a661dba648fbffffff00"})
r2 = dup2(r0, r1)
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000140)=0x4)
writev(r0, &(0x7f0000000080)=[{0x0}], 0x1)
writev(r0, &(0x7f0000000100)=[{0x0}], 0x1)
r0 = socket$unix(0x1, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x100, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f0000000000)=0x2008, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "090000000000000fb3040000001000"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r2 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x1ff, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r2)
r3 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
dup2(r0, r3)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r2, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r2, 0x0, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f00000006c0)={&(0x7f0000000200)=@abs={0x0, 0x0, 0x1}, 0x8, 0x0}, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffffd, {[0x3d83, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x3, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x0, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfffffffffffffffb, 0xa6f, 0x2, 0x80000, 0x5314, 0x5, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x3, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0x10, 0x0, 0x203}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x2004, {0xffffffffffffbdd1, 0x40}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6], [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000020000210], [0x3, 0x0, 0x0, 0x4], [0x0, 0x0, 0x40000000, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x40003}, {0xffff, 0x0, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000080)=0x5)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x1c}, {0x1}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r1 = getpgrp()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, r1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x1d}, {0x2c}, {0x6, 0x0, 0x0, 0x4000}]})
pwrite(r0, &(0x7f00000001c0)="d0000000000022b3a27e24b3a566", 0xe, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x35, &(0x7f00000000c0), 0x4)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x24, &(0x7f00000000c0), 0x4)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x29, 0x6c, 0x0, 0x0)
r0 = syz_open_pts()
close(r0)
syz_open_pts()
write(r0, 0x0, 0x7)
r0 = socket(0x2, 0xc003, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x25)
setsockopt(r0, 0x0, 0x68, &(0x7f0000000040), 0x0)
mknod(&(0x7f00000000c0)='./bus\x00', 0x2040, 0x4f4b)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
close(r0)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
chmod(&(0x7f0000000140)='./file0\x00', 0x3f)
setuid(0xee01)
truncate(&(0x7f00000000c0)='./file0\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2d}, {0x54}, {0x6, 0x0, 0x0, 0xff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
pipe2(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x30000)
setreuid(0xee00, 0x0)
r2 = getuid()
setreuid(0xee00, r2)
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f0000000040)={<r3=>0x0, <r4=>0x0, <r5=>0x0}, 0xc)
setreuid(r2, r4)
r6 = socket(0x18, 0x1, 0x0)
setsockopt(r6, 0x29, 0x32, 0x0, 0x0)
r7 = open(&(0x7f0000000100)='./file0\x00', 0x10, 0xc9)
r8 = socket(0x18, 0x2, 0x0)
r9 = socket(0x20, 0x1, 0xff)
unlinkat(r7, &(0x7f0000000180)='./file0\x00', 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000740)={&(0x7f0000000800)=ANY=[], 0xa, &(0x7f00000000c0)=[{&(0x7f0000000500)="851a4c02ec276d92a82db9f4b6272312fad04b325f81f3ac031f3c5312e5acf292b07a6e34456594bbed8266a32dd8d91d49381a4824f7a31e48a231427f43c2cda68c1231963c650dccd46f63bad9bbf15bacbfc39661a8fbfedbfa4dacc188cd9346c0dc9731b8d75d2c9404654ccb5b01850c424b38f47e20342feb2c29fe4e7f7704c1528de4590daf2a8313d534a42642ae4458741418bf9c39bdc83982ea", 0xa1}, {&(0x7f00000002c0)="c992bf68850d464af619ab30293a6f70618244c91e77bcd6a7462e720b0445b41111a7820fe12ac28b35ad", 0x2b}], 0x2, &(0x7f0000000c80)=ANY=[@ANYBLOB="2800000000000000ffff000001000000", @ANYRES64=r2, @ANYRES32=r5, @ANYRES32=r8, @ANYRES64=r0, @ANYRES32=r4, @ANYRESOCT=r1, @ANYRES8=r9, @ANYRES32=r8, @ANYRESHEX=r7, @ANYRESDEC, @ANYRES32=r8, @ANYRES32=r8, @ANYRESHEX=r3, @ANYRES16=r3, @ANYRES32=r8, @ANYRES16=r6, @ANYRES32=r8, @ANYRES16, @ANYRES32=r8, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRESHEX, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRESHEX=r0, @ANYRES8, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000c1db8ea36a005f157c3e136b4dc5826ddb746a508d7d891476582b6857621fae560e51eeb96d43c3ce8426515d7c34788dca9e146d80579781ab2bcb87bb4803613853080000000000000070c9e8c14e05eaf698dae7bc8e973b5f8eef931116250299d3ab377e690512249f1f4a8c39cbdae9af7ab31b864a35ad498d7acba771f351413767a5a8b476a9e83b3e", @ANYRES32, @ANYRESHEX=r8, @ANYRES32, @ANYRESOCT, @ANYRESDEC=r1, @ANYRES32, @ANYRES32=0x0, @ANYRESDEC=0x0, @ANYRESOCT=0x0, @ANYRES32=0xffffffffffffff9c, @ANYBLOB='\x00\x00\x00\x00'], 0x128}, 0x8)
r10 = getuid()
r11 = semget$private(0x0, 0x2, 0x189)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000300)={0x0, <r12=>0x0}, &(0x7f0000000340)=0xc)
semctl$IPC_SET(r11, 0x0, 0x1, &(0x7f0000000480)={{0x20000008, r12, 0x0, 0x0, 0x0, 0x10001000d, 0x207}, 0xc69, 0x7})
semctl$IPC_SET(r11, 0x0, 0x1, &(0x7f00000005c0)={{0x80, r10, 0x0, 0x0, 0x0, 0x102, 0x3af}, 0x7fffffff, 0xc, 0x1ff})
sendmsg$unix(r7, &(0x7f00000007c0)={&(0x7f00000001c0)=@file={0xe6483d9fa538c067, './file0\x00'}, 0xa, &(0x7f0000000600)=[{&(0x7f00000002c0)="d4c395b91e32c2e6f903890b1e5b8a23b2e877a3a4345be7497539a0a511cc726c0e81a7b514cb2d335ecbc1707ddba7e2da0d595ed0da42d46b414d6f61604ca82ba5997c730db0ab5693229ffa630ef328211e6d8a6275915262abceed96a7add2ca998e5750de40687eb46471c76f5541fb91af0edba7426be423598a9d0e1734d53a3d3acd2eb8c4eb1ab76de058e50c94107ac33233ec8782cd698c0e47", 0xa0}, {&(0x7f0000000380)="e299d8e52febe70c8e58109cf7e7a7b00cc4d28c55292d0721f0c3aa35e87ba0039d9bb5ded52f5f21b5fb0c71eb401f8390dccfa2ee217c70fd6524f96e81048735c7bd77ea31bf6d97b9e796a7a75e0a52066618117c18285ce928412bff2120f6ee27512c88ffae74539edf33c8718161918aecf7331924e04418e2c030415c65ed694152f8699af8744eb0eaca75bd99768602627469ce4cff8578f08caf1f532f84ac8e9d97ac5836da11e8ab19e7f707bb1d2c75f3dab39bb752b7eebd03307fc81db599b86f35b61b51420e85d1ac0817466ac2db3f3d4c01e511f23d1316f641f30724f06bf1", 0xea}, {&(0x7f0000000480)="23503ee17f25a75db1fe271130027dcdd827d4861511b7173579949a5f5c04b7b4ad78b8d4393fecf53e5bb22f5447723d3beb988f84e1899bb47de7d19efd95b849e89da73eba5ca5d0be1513b8a8a2fa176cd82fb11537a8b400c2eb89161c05753749e8c4c2df68864f8c440bfe2efca2630713431dc297fc1ead1bde8f074a61fecd0241f3653cae44285adc95e92c236af4fb0fb4c2105fda2fffb59ba6d91acbc3c3fe328a63264b394c5acf9a72e470", 0xb3}, {&(0x7f0000000540)="c368d3f6c24a8557759a5344905f4cf249ba13b1ebaf5a81d7afd57e65371abaedb421b58112363dbe6a3b2f9262388c661193ee395e8101288760c4b63ae678354ba436834bbc18f3e4c01f4c9be4f139155f49715fcd5274ccde", 0x5b}, {&(0x7f00000006c0)="548dd552b44420c70b931170f6fa4542117786486a97256288f504b18a7f4607102418805573f3c024f6c39cb438cc9ba76b2c24f480d7d6716fad14c98e8325d7746c732090eb7582f4118dae8e28e79c61e502bf2a826a4ffad03654ef57d122cdf3adc77414a79de003e5d5acae28daa09946f09e1d0e71c5137882ea649c69b820c9949cd0f924674e81294ba6fad0cab1a3e38dd0d89423af0927dea2018b81761cbb2393e3f334f07641d0eec276dd73d5e5eb1f1f33ae98", 0xbb}], 0x5, &(0x7f0000000780)=[@cred={0x20}], 0x20, 0x1}, 0x2)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
select(0x190, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x9000000}, &(0x7f0000000040)={0x1}, &(0x7f0000000080), 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
select(0x40, &(0x7f00000000c0)={0x9}, 0x0, 0x0, 0x0)
close(r0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
writev(r0, &(0x7f00000013c0)=[{&(0x7f00000001c0)="ee72f6e9aedb8e4d958ccc7be75033bf005848c032180add5a0760d4108e71868a82bda70f5d8ceaf21ec8a7f25cbda584ae941570a4db8c1c0fbe70fe502e", 0x3f}], 0x1)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000200)="7f8475456ea1b8404c6a93b2e45bf28db765c80c852dc8ef83f0c169029fc61d564d160d0fa1d73f2fe4442dec63b212ce099bba83ce8bc43fc87fc4725030", 0x3f}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x24}, {0x4c}, {0xfdd6}]})
syz_emit_ethernet(0x46, &(0x7f00000002c0)={@random="2fd663b4ea0d", @random="784c58dd2c9a", [], {@ipv6={0x86dd, {0x0, 0x6, "66f6db", 0x10, 0x0, 0x0, @empty, @loopback, {[], @icmpv6=@ni}}}}})
r0 = kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r1=>0xffffffffffffffff})
kevent(r0, &(0x7f0000000100)=[{{r1}, 0xfffffffffffffffb, 0x6f}], 0x401, 0x0, 0xb200000, 0x0)
pipe(0xffffffffffffffff)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000500)={&(0x7f0000000540)=ANY=[@ANYBLOB="01002e2f66696c6e30005d614ad403c4878bc87f9803969717c9756af4c7e197b8ac637846a55d5757029f3a76617353a768b0e532689e67a1c5d42b9d58ffc19dedaceb2a64813a3b601129696b7eb83c770b021af5a12ee82fe7cc9e0792ffe98c37c46f0d6145cae7a8972078bb17687659aedcb2480f2f2e65e63b1679b1a70f0f408e29b0f2179bb76ae49bcec95d69454744d3bbd68fc80bcbc02e40b5e33fe3284231075dd82c9861760827284f902542834d6ded0a64732452bafc3e69e95e158869bee1fb06469160c4443edf62e9cfa0a04be879e5782eb869e2916a784ab482b45d2dae39498e44a526b2d4b2722c49b4ce17267f063ef65234ab"], 0xa, 0x0, 0x0, 0x0, 0x100}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000280)={0x2, &(0x7f0000000000)=[{}, {0x4, 0x0, 0x0, 0xfff}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3f}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x22)
r0 = socket(0x2, 0x2, 0x0)
r1 = kqueue()
kevent(0xffffffffffffffff, &(0x7f00000002c0)=[{{r0}, 0xffffffffffffffff, 0x1, 0xa00ffffd}], 0x0, 0x0, 0x0, 0x0)
r2 = socket(0x10000000002, 0x2, 0x0)
r3 = dup2(r0, r2)
setsockopt$sock_int(r3, 0xffff, 0x1023, 0x0, 0x0)
r4 = dup(r1)
kevent(r4, &(0x7f0000000040), 0x80, 0x0, 0x419, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETDEFAULTBELL(r0, 0x80105705, &(0x7f0000000000))
ioctl$FIOASYNC(0xffffffffffffffff, 0x4004667b, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x1, &(0x7f00000000c0)=[{0xed34, 0x0, 0x0, 0x4}]})
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f00000000c0)={0xfffffffe, 0x0, 0x8009, 0xffffff06, "1f000000007e00"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d298b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5e", 0xcd)
mknod(&(0x7f00000000c0)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000500)='./file0\x00', 0x0, 0x0)
ioctl$TIOCMGET(r0, 0x4004746a, &(0x7f0000000000))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
r2 = kqueue()
kevent(r2, &(0x7f0000000000)=[{{r0}, 0xfffffffffffffff8, 0x31}, {{r1}, 0xffffffffffffffff, 0xe1, 0xfffff, 0x9}], 0x8, &(0x7f0000000080), 0x1, &(0x7f0000000140))
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="92029f"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
setreuid(0xee00, 0x0)
r1 = getuid()
setreuid(0xee00, r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x0, 0x200}], 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="83028b35c7"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
r1 = socket(0x2, 0x1, 0x0)
connect$unix(r1, &(0x7f0000000000)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0x10)
r2 = dup(r1)
shutdown(r2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x8b67, 0x1d, 0xfffffffffffffffd, {[0x3d83, 0x2, 0x0, 0x0, 0x10000000800, 0xfffffffffffffff7, 0x1, 0x3, 0x2000000000003, 0x0, 0x800a1, 0x1, 0x5, 0x100000004, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x0, 0x82fc, 0xfffffffffffffffc, 0x0, 0x0, 0xb8, 0x2b, 0x1000000000003, 0xba5d, 0x7], [0xfffffffffffffffb, 0xa6f, 0xfa5, 0x80000, 0x5314, 0x5, 0x4], [0xffffffffdfffffff, 0x3, 0x0, 0x6, 0x28000000000, 0x1000], [{0x1, 0xfffffffd, 0xfdfffffd, 0x20}, {0x3, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100004}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0xc020000, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0x0, 0x0, 0x203}, {0xe3, 0x1, 0x46000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000040)={0xd3ff, 0x2004, {0xffffffffffffbdd1, 0x40}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(0xffffffffffffffff, 0x40045714, &(0x7f0000000000))
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x80000000, 0x0, 0x80000001, 0x3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x7], [0x1, 0x6cf11d8a0000, 0x0, 0x0, 0x0, 0x0, 0x4000020000210, 0x0, 0x0, 0xfffffffffffffffc], [0x3, 0x0, 0x0, 0x4], [0x0, 0x0, 0x40000000, 0x4, 0x0, 0x1000000000], [{0x14, 0x1fffffc, 0x0, 0x2}, {0x0, 0x4}, {0xffff, 0x1000, 0x0, 0x2463f4c2}, {0x0, 0x1}, {0x0, 0xfffffffc, 0x0, 0x800000000000000}, {0x0, 0x7ff, 0xfffffffa, 0xe272}, {0x0, 0x0, 0x4, 0x7}, {0x6, 0x0, 0xffffffff}], {}, {0x0, 0x0, 0x0, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000080)=0x5)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r5 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x80206979, &(0x7f0000000300))
r0 = socket(0x18, 0x3, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "0000ffff0f0d00"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x1)
mprotect(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x2)
sysctl$kern(&(0x7f0000000000)={0x1, 0x4b}, 0x2, 0x0, 0x0, 0x0, 0x0)
setitimer(0x2, &(0x7f0000000280), &(0x7f00000004c0))
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000002c0)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)={{}, {[], [0xc8, 0xffffffff]}}})
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x36, 0x0, 0x300, 0x0)
kevent(r0, 0x0, 0x0, &(0x7f0000000280), 0x6, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f00000000c0)=[{0x4c}, {0x1}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x32}, 0x2, &(0x7f0000000000)="6a9b7415", &(0x7f0000000100)=0x4, &(0x7f0000000140)='\x00\x00\x00\x00', 0x4)
pipe(&(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
close(r0)
mmap(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x31, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x2, 0x0)
bind(r2, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r3 = socket(0x2, 0x2, 0x0)
r4 = dup2(r2, r3)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
sendto$unix(r1, &(0x7f0000000140)="5885", 0x2, 0x0, 0x0, 0x0)
setsockopt$sock_int(r4, 0xffff, 0x1001, &(0x7f00000000c0), 0x4)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
setuid(0xffffffffffffffff)
r0 = syz_open_pts()
fcntl$getown(r0, 0x5)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x24}, {0x84}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000180)="aac229f665e595f9a72059b69d71", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{0x40}, {0xc}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f00000001c0)="66f3e14d4e2f50a6875e763f7957", 0xe)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
setgroups(0x0, 0x0)
setreuid(0xee00, 0x0)
r1 = getuid()
seteuid(r1)
rename(&(0x7f0000000080)='./file0\x00', 0x0)
r0 = socket(0x18, 0x3, 0x0)
setsockopt(r0, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
getsockopt$inet_opts(r0, 0x29, 0x2e, 0x0, 0x0)
getitimer(0x0, 0xfffffffffffffffe)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x2000, 0x0)
r0 = open$dir(&(0x7f0000000240)='./file0\x00', 0x0, 0x0)
r1 = dup(r0)
fcntl$setstatus(r1, 0x4, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000040)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "c21280", 0x8, 0x67, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="846a873b4f4a0baa767c8f0b0537ae59", {[], @udp={{0x2, 0x2, 0x8}}}}}}})
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x2})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2d}, {0x4c}, {0x6, 0x0, 0x0, 0xff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r3 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = fcntl$dupfd(r3, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
socket$unix(0x1, 0x5, 0x0)
r0 = socket(0x10000000002, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f00000000c0)=0x3, 0x4)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x0, {[0x0, 0x0, 0x0, 0x20004000, 0xf83f0000]}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x1, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80146952, &(0x7f0000000300))
setrlimit(0x0, &(0x7f0000000040)={0x4001, 0x100000})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setrlimit(0x2, &(0x7f0000000040)={0x60000000, 0x60000000})
mmap(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0, 0x1811, 0xffffffffffffffff, 0x0)
r0 = getpgrp()
r1 = getpid()
setpgid(r1, r0)
r0 = kqueue()
readv(r0, &(0x7f00000013c0)=[{0x0}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x45}, {0x4c}, {0x6, 0x0, 0x0, 0x7ffe}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328ea0703c719", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x81}, {0x4d}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
munmap(&(0x7f0000800000/0x800000)=nil, 0x800000)
pipe2(&(0x7f0000000140), 0x0)
minherit(&(0x7f0000f71000/0x1000)=nil, 0x1000, 0x0)
r0 = socket(0x2, 0x2, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x4}], 0x1, 0x0)
connect$unix(r0, &(0x7f0000000100)=ANY=[@ANYBLOB="7b022e"], 0x10)
r1 = socket(0x10000000002, 0x2, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f00000000c0)=0x3, 0x4)
setsockopt$sock_int(r1, 0xffff, 0x1023, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000001080), 0x0, 0x0)
poll(&(0x7f0000001200)=[{r1}, {r0, 0x4}], 0x2, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
r2 = dup2(r0, r1)
ioctl$TIOCSETD(r2, 0x8004741b, &(0x7f0000000080)=0x4)
poll(&(0x7f0000000280)=[{r2, 0x81}], 0x1, 0x0)
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x80047460, &(0x7f0000000380)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x7, 0x0, 0x0, "9b4206c781e1e9bdebfa3b406d2e33c61715499a", 0x0, 0xc3f7})
writev(r1, &(0x7f0000000800)=[{&(0x7f0000000880)="d863a9576014796f930c23bb4eb98d7f712776f7b636511ed2013019bc5e53f5a3c831b6766f78704144d7d5a5d00afa41eca51523c03d92a090713ce265ded97f268f3bcbf2354c51496fdce844ae7bc855efded0eb0398ca2b4bb05da59b82853c8c402343609fa8b796671bd89d784ed32cab4a425c54d93f95e4fcde0781b91d946aaad4a1b5be33279531ae7ff2bc61f93c76c508766266436ddfb58416d64ee8ac5ea7b47b921397c9872aa44881f99c9bcc153cdc8986afe746cd0ff1bc11dae02c554d6794ae2aa2adf8d8def16732417b9d38eb5a2610255b245ebc4a22fa07a3cfc3308666a151ced719f75a9a662939dec401158c4bc6e64188307cd9f1c8bf67fd8e492b8bbde52801020a652abda2a8832f4a7e8968fb9b95d58bd52f2c437dd38b90b94361e479813ccc81bd99154fee6d5fd67145c8177ebaa4c258edd88da2d16d754050e34646e44a530172bee0d8d3b239643c546065180832eb6d21437e1a9331e0ae5cb209", 0x16f}], 0x1)
syz_open_pts()
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x2, 0x9, "00730988a95c5bee0080000000000000000400"})
sysctl$kern(&(0x7f0000000000)={0x1, 0x31}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x8001, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
setsockopt(r0, 0xffff, 0x1, &(0x7f0000000300)="9d059736", 0x4)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect$unix(r0, &(0x7f0000000000)=@file={0x0, './file0/file0\x00'}, 0x10)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000140)="0500050060", 0x5, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
getsockname$inet(r0, 0x0, &(0x7f0000000080))
msgctl$IPC_SET(0xffffffffffffffff, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x1})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x29}, 0x4000000000000005, 0x0, 0x0, 0x0, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000002c0)="232100fa25cfdd8a6caf4870007ce075671e88c9faa1d61e7edf7c46f7e71000cc94028cb93eb8949e7f3d35617671a178b712a85a1a2df35cab8edeaa050f771a28d85b988d526e8beffa001e7c368c89b20b323e7db193381423dd9e240fa965648e69010a", 0x66}], 0x1)
fchmod(r0, 0x8e3)
execve(&(0x7f0000000280)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x1c}, {0x61}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000002c0)="3c9ebbd5efb16c45f346741b55ee", 0xe)
sysctl$net_inet_ip(&(0x7f0000001480)={0x4, 0x2, 0x0, 0x1b}, 0x4, 0x0, 0x0, &(0x7f0000002500), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f00000000c0)=[{0xc0}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f00000002c0)={@random="fee9c50f252d", @broadcast})
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x5}, 0x4, 0x0, 0x0, &(0x7f0000000200), 0x0)
r0 = kqueue()
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
kevent(r0, &(0x7f0000000080)=[{{r2}, 0xffffffffffffffff, 0xa9, 0x1}], 0x8000, 0x0, 0x0, 0x0)
sendto$unix(r1, &(0x7f00000000c0)="2d219a85e6e52aeb719389809a692691777991e1db663c9c9e03cc0e730cf2d223466978b1b3ba5b5048f5f023471f27cf3688e866f266fd99f307f26845dce5e16e299da8ad8ed13cc750664cb019f25854ab7724bf0738a99166fb8b3a1838468726a89e9ad3637ef4dee522823148ab1956044696578ca0273ed58c2540ed23ac2cf6164d1cda80eb5b819896010af7617706b403809825877311e793081c12fc8dcca548b4a33c7dbc92710a001b6950925c0d0b6b6fcfc1b47b82d4983fbf30c0121f114cfc4f1e9c0fe9dc304996e2e0e75e2741e8b88359540acdd5c86074bdd3ad6d5daa14015518b7e320e9f1c0bdefea08c0b799d3f770c2613d93f728043b10f79d93116b2127ff1ba332991385beb9fd6b7e7689ce34a7a256f24a71dbc31fd91000b808cb7c575dfd5cc75803473eb0e2c200157596a193871dea2655e9ab2cf2e2552144d61a07b19aca37f961a1184bb7134091dcf7bc0a36a85f631b4645300e374dc690dec7093ad1", 0x171, 0x0, 0x0, 0x0)
symlinkat(0x0, 0xffffffffffffff9c, 0x0)
chown(&(0x7f0000000100)='./file1\x00', 0x0, 0xffffffffffffffff)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0xc}, {0x64}, {0x4406}]})
syz_emit_ethernet(0x5e, &(0x7f0000000200)={@broadcast, @random="45d411199e1c", [], {@ipv6={0x86dd, {0x0, 0x6, "86b3a1", 0x28, 0x0, 0x0, @rand_addr="ce08c2cd208d99540c2d61b264b46e8c", @local={0xfe, 0x80, '\x00', 0x0}, {[], @icmpv6=@ndisc_redir={0x89, 0x0, 0x0, '\x00', @rand_addr="a7568838eda5266fe97db25c3c341d0d", @remote={0xfe, 0x80, '\x00', 0x0}}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x7c}, {0x5}, {0x6, 0x0, 0x0, 0xff}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a54233f4d9d00000000", 0xe, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
ioctl$WSKBDIO_GETKEYREPEAT(r0, 0x400c5708, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0xb1}, {0x4}, {0x6}]})
syz_emit_ethernet(0x5e, &(0x7f00000002c0)={@random="ca49fa1fb369", @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "1fe0bc", 0x28, 0x0, 0x0, @rand_addr="797ad74e1e06daba53b56cadf23e1936", @empty, {[], @icmpv6=@ndisc_redir={0x89, 0x0, 0x0, '\x00', @local={0xfe, 0x80, '\x00', 0x0}, @mcast1}}}}}})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000280)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
select(0x40, &(0x7f00000000c0), &(0x7f0000000100)={0xfffffffffffffcbb}, 0x0, 0x0)
shutdown(r0, 0x1)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000140)={0x0, 0xffff, 0x1000007, 0x6acb978b, "0d43d3d3d33f0800010000000000000d00"})
writev(r0, &(0x7f0000000380)=[{&(0x7f0000000280)="e5eba711d265cd4a6915a540d2739714f8531ecf959ba2a879feaef4f56a173f42da2cac363f4c2ba46c5bdf83e7ed9c1f6e7a5d5a603b7edac2dbcfa9ccb833e1a33409200425c2879a83a9ec873c095c11b570cba5c4404aef1831af650ab3aa9a698ec660b952e3c35e0baf6eee7557f778c576f122dbd860ba89ccc4098a010d", 0x82}], 0x1)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc450444d, &(0x7f0000000000))
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xe, &(0x7f00000000c0), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x86, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000100))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_emit_ethernet(0x2a, &(0x7f0000000200)=ANY=[])
read(r0, &(0x7f0000000040)=""/32, 0x20)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000100)='c\x00')
r0 = msgget$private(0x0, 0x33)
msgsnd(r0, &(0x7f00000004c0)=ANY=[], 0xcf, 0x800)
msgrcv(r0, &(0x7f0000000140)={0x0, ""/6}, 0xe, 0x0, 0x1000)
msgrcv(r0, &(0x7f0000000600)=ANY=[], 0x22, 0x1, 0x1800)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x1})
r1 = msgget$private(0x0, 0x400)
r2 = socket(0x18, 0x3, 0x1f)
setsockopt(r2, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r2, 0x29, 0x800000000000009, 0x0, 0x0)
r3 = open(&(0x7f00000000c0)='./file0\x00', 0x20, 0x10)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r4, 0xc0084427, &(0x7f0000000140))
kevent(0xffffffffffffffff, &(0x7f0000000280)=[{{r3}, 0xffffffffffffffff, 0x40, 0x40000000, 0x4, 0x57e}, {{r2}, 0xfffffffffffffffc, 0x0, 0x8, 0x85a9, 0x9}], 0x2, &(0x7f0000000300)=[{{r4}, 0xfffffffffffffff9, 0xe4, 0x20, 0x10000000000000, 0x100000001}], 0xa1, &(0x7f00000006c0)={0x20001, 0x8})
msgrcv(r1, &(0x7f00000002c0)=ANY=[@ANYRESDEC=r2], 0x93, 0x3, 0x0)
msgrcv(r1, &(0x7f0000000340)={0x0, ""/60}, 0x44, 0x2, 0x1000)
msgrcv(r1, &(0x7f0000000540)=ANY=[@ANYBLOB="000000000000000000000000000000000100000000000000003afd34afdea3790b26b313fe543a8fa8b0c899eb5c0b16f6851447737232c4ffe1e7ed8da33d20ef734480e680f8f1606a22924552a06081df56b3af636fbc45053d5abaa027ff7bf3cc9cd29ed55288dbdc4c645e656dbb4f5f15db9a22276095aede64849aa6efbedc63af8a2c606fda5e331f22bb696c0ad1c4494e107dd3ca9f4e71e6d3ac84f9cfdf84a0dad7a9fe08a79a54c9387cf619f05bff3df33d08c02169da9e34b2fdfcedc75c3723813203106a10e327a646118e7506f662164abb014daebda587a6352158606c2f8e24b4d1f269712dda058761b5d1f55be18a2f065d78612cb38bf70c7c43e8420f09a89ac6884ea42d07e462f4fec1586bb923c079df1761440cd3610b98d66a8eedc92dcf6dd41752481d8c50f7fe8c77194f3f42142e20cdb47bf7", @ANYRES8=r2], 0x19, 0x1, 0x800)
msgsnd(r1, &(0x7f00000003c0)={0x1, "54a7ac21511a97423fd6c740a91e5e4a5101adef0e662e2828341e6155ced1e9586b84e42a9d46e68ef435145e304f9177f3c3b9d47896c87a9045d5709c3b1f6dca4026fe560dc7ddd987f7cce4d4f5530f7b"}, 0x5b, 0x800)
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x18, 0x0, 0x42)
msgsnd(r1, &(0x7f0000000440)={0x2, "901f6e07bc3dea97798f13cb941d9ff4bd1f7fd0c0d9837d285539b3a82deabea46e9f3f86f3e8581041015810a6984f1d156ff72c098b58257e6e45077a308e1100db3ff8f8e8a71a71a1b395fd15a72701d3b7a3135dde425b9d249f3963d9fc6cd8e8e2eb314ffb1118bdc5e74e896957753518af5e145b21e83bd7620080a3afc6d11ef16db6704926129633a8757c6782325d10b3cff4b5b46c4ddeb7b1d7c34f00c354b2c7b78122b25cfc7e471906fce9e53ca2d815270d04964d292d1bf12706996ccca32051463cab0aba1c8c759e2c5dc62f7f21982c4f5b761aa6"}, 0xe8, 0x800)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069c1, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x24, &(0x7f0000000000), 0x4)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x8)
ktrace(&(0x7f0000000180)='./file0\x00', 0x4, 0x1136, 0x0)
r1 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
getgroups(0x3, &(0x7f0000000300)=[0xffffffffffffffff, 0xffffffffffffffff, 0x0])
r2 = open(&(0x7f00000000c0)='./file0\x00', 0xca165819b60ac6b, 0x0)
fcntl$lock(r2, 0x9, &(0x7f0000000000)={0x3, 0x0, 0xffffffffffffffee, 0x1001300010005})
r3 = semget$private(0x0, 0x3, 0x0)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000480)={{0x20000003, 0x0, 0x0, 0x0, 0x0, 0x10a, 0x9}, 0x5, 0x3, 0x8})
setsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140), 0xc)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x10, r1, 0x0)
pledge(0x0, &(0x7f00000001c0)='/dev/ttyCcfg\x00')
socket(0x1, 0x3, 0x6)
r0 = shmget(0x3, 0x3000, 0x0, &(0x7f0000ff9000/0x3000)=nil)
shmctl$IPC_STAT(r0, 0x2, &(0x7f0000000040)=""/24)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0104452, &(0x7f0000000080))
sysctl$net_inet_ip(&(0x7f0000001100)={0x4, 0x2, 0x0, 0x5}, 0x4, 0x0, 0x0, &(0x7f0000001240), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x7}, {}, {0x6506}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@local, @random="9ba8ed10ab90"})
r0 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, "fe17754d047c518155e501284a18461fc0a36bfb"})
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000080))
sysctl$net_inet_ip(&(0x7f0000000240)={0x4, 0x18, 0x102}, 0x8, 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x0)
socketpair$unix(0x1, 0x5, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
lstat(&(0x7f00000000c0)='./file0\x00', 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504449, &(0x7f0000000000))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x28}, {0x2d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000300)={@local, @remote})
sysctl$hw(&(0x7f0000000000)={0x6, 0x19}, 0x2, 0x0, 0x0, &(0x7f0000000100), 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{{r0}, 0xfffffffffffffffa, 0x9}, {}, {{}, 0xfffffffffffffff9, 0x49}], 0x6, 0x0, 0x1, 0x0)
kevent(r0, &(0x7f0000000000), 0x800, &(0x7f0000000080)=[{{}, 0xfffffffffffffffb}], 0xfbd, 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x1000000000029, 0x4, &(0x7f0000000200)="06000000", 0x4)
r1 = socket(0x1, 0x1, 0x0)
close(r1)
r2 = fcntl$dupfd(r0, 0x0, r1)
connect$unix(r2, &(0x7f0000000000)=@abs={0x1, 0x0, 0x2}, 0x8)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r3 = socket(0x18, 0x8000, 0x80)
bind$unix(r3, &(0x7f0000000040)=@file={0x0, './file0\x00'}, 0xa)
socket(0x2, 0x2, 0xe9)
ioctl$LIOCSFD(r2, 0x80046c7f, &(0x7f0000000080)=r0)
setsockopt(0xffffffffffffffff, 0x9, 0x7fff, &(0x7f0000000100)="d9b9b36fbd3624f824912222aba53618f6eff5c16597bac9f63aa1840a7f378230c22557c3a16395fe6ab90e07776b3068339105b7cbc10905a7c432e8e6feacc88c2a5aa4505427521253d59bb59a269726862da36081d09b3cfb5de4a2396aa46c9027b25aeeb60d94b0e728a0e6284012ea7a8655f31d60408107417485bdd5259581f0c19abd827edfd49d566aabbade6ff0a820d9a919001d50197ae7f2eb7f57f09afe", 0xa6)
syz_open_pts()
fcntl$getown(0xffffffffffffffff, 0x5)
r4 = dup(0xffffffffffffffff)
ioctl$TIOCCLRVERAUTH(r4, 0x2000741d)
syz_open_pts()
sysctl$vm(&(0x7f0000000000)={0x2, 0xc}, 0x2, &(0x7f0000003100)="381dc4e4c87ec79e2de6046150f0b794d2294211ab44585c9545248fe9eb9a33618f3f823bea8676c3c4d6c4abb841b880a6dda90daa22cfc641ca4eee6859005461122c26e08567b2ed04c53fcd061058eaac9b27975badf25515259b209eb3959ac74cd95fc1010d8680260e0e670ff2d6427ab687adb5fcdf70d2f51f3caaebe45acc55226b2538b358ccde9a61cb4e2db40be64e90612657a5b28ffffe7f40f4981468bec4156cdbaee98182c3332d4afac21acd444e5dafde38e08f71307797f57b80d2eab845ef4b73898880d49304ffaaa2ad623f64906a614ad4edd8ae908722801886c028cf8f3f369f6ab4cc3fb7724ca3f68912cd56277adef99b997d1b081499ce2d8e55adf60363f9d8895820a6dfcb0007f387dd6c314cffe42def243c59c08bf7bec08dc9343e86e6a53aa846134e0254febfc5a640aa6532d1c7091e63696649f535a8bf8f689d1bac9e93e4861df6234d96250b24f058fa8629940424ea42780ab4e70fadb5cb0ba497ba6263f73279bb00c637501a688a9df67549b06f84593708623e271d06905964a195e4b6e0e11fcf15a75939f1848d7f81dc0553cb3836ba7a2d3ff61c9b81cf7c43d79f61947b5d1f01d52740267c180b8873f91d399164c4fdd319d92881ca437ea18a36a4b4443a892bce8fff27c5afcc3b75e99d42ac3db846e2c6b366b5ec52bc76ea2df510ce6788048a503e424e8c45604175bc6143d8ee94b8521d60a078b6311b3accc883caa95b5b23fa224867e81c730c72f59404966642582f5348547f6f07e92b3cc038158f332ab44b2d6162b7a1df6e255aea0a60f3e77a66838f575cb3df658120f6025c0fd63870835b624ebb230952f775fbdb842a24c93b40487c47d8bd2f43d1fa47e82c9be41af64cea6df53d92c471ef91d3e8307ba083f3d33e4a9a015750a7811e5bf16c91c876691d93ce239555755f4a496f3a1ba9746001be8e4be0154edda9b135481c55a399a06f1bfc832e103c3732fdbe93c20f20ae1a180136ba1d10c4c50cdc9698a00caf6f570d1119248d9add2ddcd0951accd70ff4e75fccb8456206a7ffca5411818c5709a3fa50ea4b37b89b380568598aaf9e259b9a24675fc52fea12dc204ba0ea6129173b0c532a2fef27facceb144b5cd79cdc56ded8833ff0e8b676c96d063030b9bb7cf6d78044ff696cbe79045841b3adc54cd4c904fb8d64966f9728258941351377235afb35982e591762276255aeabe7be29c2fbc0b21948059400d47306984bf8780f04e1a66108ce45825865802d8d67432a2d444e3e6bb40338051b55c3a4af399904ed5a1d338042c1e6b9459ccafa845018e56c8d6b29f42fb945cd5ad073033e3f94b7d90b72765a8dca1ae94e49f0ab7f4cd6125c70e133d34978c6d42873074431937ae6237b405ed724a01e2aae727b4f81edcf3e6cb56b521611285dafb9d6e9e6050ec96e159ec3d4e0e3d62442a2f3731607b428a5cf782a71b3d0a6c1796176c34003e9986d3d418847f92d917daa81449ea664a167dbe32a57b4d6c60861fde99e84d9959dc19e36a6a6d408333f58342540872304fa62d08242b7f803da45cefd15f19e3b69815719ee040f26c9445f550ab8de217fb51f2fbb77c0ff7f9a6ec8a43feb54900453ef06629c850308bd9f4258830e0843aae2d894a48ce5e62ead900eed43650f3c3e0b33c8f160a13983b4a038c343c91bd53b7ee0b266a18f96bd21692b95caf77f99ae4387bbdc8f963bb64755d4b347843263e6f769bacb4fee355323651c60f64fc9f6fb1770de14e87acce7dff67f928abb77e8e56d14bcf3d4c12acb9c60816278592e36eaa49359a984647c6d86162cf27ae53b94e232033856b50baf5c6e726468d09a1091dc4239a3b57d0206942490c6328a5af09bacb87dc6f475c15733856ce1d4390ddd86e3a54de223a5dea5702919b4cbaf3dc83bd80d4f321b6cba471f518e8ae4f398af15059a2eb0412cca255ac6b3a6896d46c5324cafa4472a8763d54bdc710ba4fa1ec4d7cff43ca57c2c0a8c3439765bbe4f5b8e4c42144e960ea9e5269adc72dde3b648609da1d57bcbf116eae26e972cc493bf78614a2e52e10715bc4f3b7e7c4b2c26557d11c79f43445b2fe227e1342e57d50240aec85d902a46a8258053273b6f7829c11030e9cde45635a45f57aa61852c7cc17a17dad45c5472d93eb1228d07f8266dd384a0b8ede510f61609b4374f7e66bccf06d872c7bb4df05313370ca02ed18485e27aa89a6414f7b45882987bd91e203043960b16b378292f086123d0dc069d1e06ed5b55c7d947d64c9c826921c3077b03e7121d8dcbd364666e0a5138c1e6eb137bc081c851cae7dc311d53472275aa6ebd47cfdb0b524cee0fada0157606581ef090c5d3347bf5e43a98df630d759a11aeaf6c62c05979fe7ec24abd5fce84cb479ae9eeb324afcd11184c379f7a8ba6e4a194357640ed27764e4448dea71cc1ef5261a28484f47a6ad0db307a4483746425474d19cf25806542369a80e419436cea76107555b68f406aa07eb568cd44f8db2d8590b859ff906ea44e4851fcf8f78d3afde40997601b4d23d1f0a50782c82874c975a631cc51090c03c5ebef32ecd0db81879dde89665a71bc8fc8d44fd876bf0a4328df2e246a787159b0aaecba7ef183c8351d03ca8cef6d3c8700975f1b92406c6da180d100ffd6a8d9346d7dcd4eb170fdd69cbd231600953058507c4c5a92592e0c2180e79a554db7993a34f0d22acca7fe056cd5a216d6ce3add8ad264ffe01202d98a877b0208ec1a0b3bc8643812fe22b5d78c127cd9cb88cceb982873d3a6bc5ee316b37be8e0a655cb3e6748dcffad33eca89bf3ec26d1a425c59d3d138f29ef992fa8aa24b7eaffa06eb17b86902be3cfc0d182dc9433b05115cf9caa51c89372f7b2011c573d4810eaf11868ff5085c4232d1cb0927f4fd9ffa3f74d208813ab4774784e016ca6c60d84a4ac9c8865fba8df0e4374b34cc3171684a608f005c4d5d39a7e9ff8be1cbdccf5aea2808990aa96f433d3c885c017d3635d06e63f91d18415da100ca04f4d00a236837755b7217f659277e00cdf44899ba1c3110da85d52e8851b12d3f816b16f890d868fb9fbc0b9757efb30454ba34a54fbaf60293cb2a8b4f5d52739e09a3d46b75e9e5cf3a93e0983ef011e5855300b766590c028c4d362c5de048d6d607a3ebb83ee7b365dd6f0924e639d9b1a739b29edbde08dbd12fa4544c234eb99d1b9097363877892b620bfc898af0dd172d4008fa9a61a7e305061607a4b3b0483c15a1fd9acbf2e517ba7fe21e9a42067585526c6d4086c0cc4cd546fa13ba1277c419af2e38777efbcd276d1cc0d5a9e74d11ceea8324360f35db71f3739288cda5d973170655489024be4f1994a435cae07a211f734f8de4530ccbf12fb15b86df467018cb4a374bbe293d4c9c41775496750c85fc324445081aacddcbbc61138804d7868191ae1b1596aeb49b821f497683caaf9316d1a6a206bb6ae37ce6c7f71ff7aec18aaef30b48c3542cb2006abe837309e5bca51183df16dcf6c0ac52de11222b4d6295903bb155a51e8ffbaa84bdc04d5168bab8e8acc83536fc6e71a992e5a9382a394e03522cf9499ea7ae3bbe2feeb037a9aa23acb03611345e370f7b56636b89657fcd92d4c1274aed82a7db802f05f3a163121fdc96051edfc670688136e6ffb5832967279343f79b5b7a53033218b329ba6a6d217407d89e02e9fb4ecae79b41ff5260d093507d6d3dda2f00a28226670e04093f4db8a62ac5bfe8d66352551a971c57fa6bfb6f363d8f5071348012df176288959e24e68c312d1cd4b703ed268b904ce6de5f3406c192985114e3bb49881a91886a816add09718f2cc549026a1ee32a52cce829a2b7365b0914b702e84ff3c55dc82436d5689223eef521dc15dc9f94ad0b25807ac893fefbb8006bfae74158f0d71025788c82eda960096d0012a89bb12cef7ba1ae3caf95796452b5789b65c5baf2fd7303142593ff19442841e923ce95cb7435059d687758ffb8bf91cdbdebbb973eeec93ad6c34384e352ba980859b657bc5882555c369a02c81f69d7758a764ce1032be2b09b8acb341525b6f3f812411ef5de0be495b062daa058155a058443a8a972dfd7c9dc95774e218de627d99fcf2bb0182c5626fa1311577c53421e161dd6fa472e8eb9abff0e9c4ecf955673cecdc942fbf767db39ec5c5712406a70d7f1ac9b15bf576a3c4b7e3659bb1d0ec735bd83a2aea014d6e6bd1baf2b828e33a081fda663d46eee5e96693111ea09ab9b7bedaea9db3766212df73c6268c786a402cd6b757d59ae77008de00d8c592f7eff105d8d4c86eb03a2195406a499d63398ce244e67040345b0c16eb9f27d03582196973f89abce55bc20fff0b56667d71326cf83b4cb8558eb531362fa3b92e96386fb1414bc4f9c4b67504d90a2b62df2254af56eb44c1622bc046ed404e48f3eea129f4bb274ee48d2773a19569f4605496257f139d85a7dfafa1b3ccf312b415e6ff6cfc046ea50415a8c3cf86f859c18a9534d16af964abe93e69605a169514d5a60976cc5b9790960339d2f01d1b7b491c6ef032d393ebd37a590d7ee15dbdbabb8318dd14a5f8775a773c3298ce5a66a05f0f52f6df305160b16e1b4612fd27edae3f73e05b12f0ecf36c095dcc888c255bdc85df1b16ade1ab1acce4836ab2e80693893268192065a9ee72628f08e4d12d64ee0f9e6cc6c20eece4c9d79f3d7490579ec5349861f26ee39427bfa45758c5165b19af540cdfb1645e1206b0212778420b66c9a3443fa732fa42f6c51206b2d71bc6244e7187d3d1c54645a91cab469c179c4b18871cf61d177f793648e01a88feab6c76c910cf58d9ba4e36dc0bb47a9f96a283bd6a5b27bbe0a562821b38bb322f17268ab00e3e940f1eae85f33661536097dc0490a337718cd4c01e8e52bdb81fb706d018284bc24efdd9c4a4229c9a3855e98c2bbf9397160f5a395785db8cbf2c845af4f3732eed9a5120c80f0141686da5657341124c8aeb85e512876e2f5da4480b45543058e5f2fb4802a30f7a4aa48c3ab389b999e6ae34c5a85ea9584116ceb5d1359561ce3bc932ced37ad35ae1595f7feaf226ece9b1057abb7f3c882cc7aa8093089d4be5998ff40b3c786df8221a336c29ce9e3fdd6879fd13297b6fa8a41c043a2aea33fb9ee6ce208855e7b98edbd07035fed228ee021b86d173a861042743c7529a98e3a9407bd76e596e7b75d56736d54aecbb2f18576ccf2e4bd4b9ed0d145552181514c366fcb5f6e68d2a64bb3aa07432725edcdf7b22b68c5722f2d1f280657b76e9124a790c9cacde7972ad913f22a394fe44b8bf214217048b3ff8b3a54c2cf559", &(0x7f0000000080)=0xf01, 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000080)='./file0\x00', 0x23b)
chdir(&(0x7f00000001c0)='./file0\x00')
setuid(0xee01)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
rename(&(0x7f0000000100)='./file0\x00', &(0x7f0000000140)='.\x00')
sendto$inet6(0xffffffffffffff9c, &(0x7f0000001740)="0ed3c4f9801f417c55ed96730833f555570d21c75a4a66bef30297419eca856e5385a075c7c50c39b4bcf6f3aa4c57823c74b42c77dc3291fecfc6de235bf5030abee31f05b957e53408ae31b75e148dc44e031bfa145283f897a5fe5cf1e7ea44d0c145e103d2b98c95bf749c5c00b152603f36f2ef337c38c18293a94a1a75c15e613f0c0a9e33e650936ccf3edb8136572dcbc74ce923b94dc7fe9aabfbba798a7be322cffbee4b5ca6a240388865d7eb3125f8da2d725a9044417c14a9f8ac5caad0de5c1a58125678baee9953628009ddb830ff379e5e77cc34d488d8c572d2d788fc083f743080823da3be88bbe4dd683ceb025e041110566bd3a31abc81bd5e87cd3f5894fc8a85d34c21b233e9b753e5e5ad46670e68c65802d9f2398abc8f2e7e67ec5f75cdf851356574e24d967a6f0441f918a404a5d4e58778f4f881bcce841de55216cbba2f5e5540962395162399b92259af83be4ddb9c06c39837a9bef5ed1875ab2da1acbdf30eea31c1a8f345328cceb20176a20fc8e586855c3e8780229b1df46189082995dc711cf304f9cb1c632a8ee14c8df8c940c630c4a832e7b8cfd7eb299003ab6272109bb8aa0c0bb3016d2434bcce58f3c93bd9fd6f70af852872b66138595b349a2a1ac2b9d58b02d246fde59f26a3f666a361d7c57d0d42c6768056f64d765616134351af0c4c436ef3c9666c7b3bc36bd3748af1dca21eec5e3e592b38f4850a0fc1250827c565844c80ff693844a5a9ef6a17b343aca09c9ae8dc694d1a2f76e35a7041a5516c4abaa8a44928195594de14b79793a1e8894287713f869e5f85fed93ceac0a50f10f8168bb666aa0cdd4e69e88f9742da36774746f7c681d0f15ed1b9fe04095bab0b78426f3f764261616fd1366fb9526c418aa9f520ee674a22fb3989b664b2b39aecba3fad641f99e730001209b2230197333c1204e40c56041434dfa246393bc76f9027edf4a34cb7e15dfe8c0fdd6d8e02e4a9582b58a41b9b1934625fe302613339f5e1a375aa5c887c01c5e02a36b89790526f657f789dfb8ea38ccaf66680c44755b1f87210209dbe34d1db4731ba507008adb4c42943bfa6b0eab7cf9047ef829ba84d9d7db76b6cf6e784dfbd96a2f8b958d198082af948cfe342e43ffa12b4113dd5f2f4e9b531f93ead90e17b3a29e2ede6a21178bc7cf3829500ec9a53dddd122ab81c07bc37c3b800fda30d829b5f02875d40c606bdce7545884245e00d9ff99f730a7c1b60e87d6bf6bb21d97093a4a5eb25aff8ea403ab1b36a1045307ddba8665c43a4b8080dd34905ca66a6ea9e02a5b5bd7c4723abc3d57cb01af18aeea0eacfee3cbef3d4905eafe7f5103de5e0038d7e78895043be9cac20907986b92010df2978daa0b484fb00f7c2bd411e7780e7c74f8c76cce14354c49ae3164a38a546a861a3c08a4599ce51a9554a00136a83abd948cb3f85ddb556ee0d3f60a07d0003412e166070df4147a827dcf6dde8c79879254181ea5626bb7b411e5d4e383b6901bec6dd26095bbf5009018793ec05383a643d46811c84aae1a6a51db152768603ad5996a1825e2af47f16d19835c8de2cc45f881d844a240decd3a3a8314cabdeb9007a16e1222a21495bb928165664ed5c063aeab6b063c62299adfa3aca39668f7de2223bf6f18285b61b3a2e65357f8e79ae1ce4421b551c0c95abe0f31fa50cde6586f94dc4f612b80bbe9249a0a83ac2fc303c6a282a644fb3284264fc4faff90a952bdd37c9859974216b33da62019105673896ecb2026aa63e106718c7629629f0e2db8701ade7fa568faa5add414607c480613de2d1423fa360216408d7d4da1df26bb83a98666ab944999b1c75917543724075809d097f6c874450147aea22ab200fbd4cceb083ddc7d536f8a44b90d38c891f06a6bafafaa40ca2f76f35198eb3d6d51bea5359e2da95352cb497106532c94dad9027636f15ec344a23ace367db1bec1cc644a43bb15b45f3ad9d435e55db34df328731ab1dff0c95e69ab4cacb32ea8da80f7f500cb386b65a1212e20fd649cf5f98e1d45735216d424679abc0f2c4b7b51f94f2eba6fadc738123edc83e2dd1960b577fc80e640190b82ef3eb5d4398be09f59f4df15de690eaabbd003c2367466142db7daed7936b0f644e6c02fc929ca06d4bf4cee45fea0e59fe6c93f6798b9b88e5267de618d015d6667468564e20b6224231de9aee591b9377ca12fc31c052867b996e1176bd2a1571446a0332c2bc80ff544f94dd8b47cc6600e4a9a06218e929236f36dab82f902aa2de327551a6cd657d50e5eac2aebbf64309c5e25a4bf4d6e2ded764431407f0a77971758987054b37ae65598ca496398e96962e3b2493dc771b5c03b98bc44cb9152c3de1ba1b1eb0563970d32d1cd04f76139132c5c0d1c692b63410f0b3944039cbc9e53130169d3562e6cccb64fef5e4ffb18b59c8c53e9684b3ddee970410f360762d4f346ed62dfcc107caa2e4a34e2366326b042f698758ed63720de6b4f2bea81e629b7040b32e660c192c49918858199f314c4d3e1373f03d5094534b308a083197591a2fc72621dbcd4512d19c92ba9dd3de64a72d2e4ae864b1be5501db82cb3473f2ae74d11307c2914d07301d059d68fb91666c16f674b437af4288e556885d6f78d47d4c7720c0f4108be0c104b7904a2d87e259bdbf9dccad62fc435d48c405386f7efa4cf5b9587a311cfa0f7e09f38c9087c3e044af50b7484be365debe015ab2ae13014e495f51d6d91bd2f7dbb5bf2a534da8a6ecbccfa62491fee58ca89b67fbbf6bcb2156e55d8ee65bf149a04603d1808592b0333f19e201c5f52f947920848a3251204dede045d293fd5ffa526de3c3e25cbb956d168edbcb75b1bb10e2ef3db2ff76551cbdfdc11d40ff37f3a5612170ad86c5ed3b553f0d38332337f438f0e771613d5d92780220148917222894c9b7da09c03070143550c393732c560414be359322b1fa786822e30d10f0a186073c236c11fe4f03c3b9a60bb4ef5f4acb0104ceb3a442380171f1104396f6ba961d8df7276879e12ee1bad9d37b0c22d0b2d8521596c05e2d04afcf409ea781f5b7770a2a9ce5354fbdccff0847db24654e271115509bf2ec467dc8e824c9e888cc31de73012f084e6d79c541320f103d105762e3ffd96b07eb49d9638b793e600280d311fd5fcf036fe3f694d7f4682b0d2a29eac1d6b0103cea880c0c947d8743bd156d06020c82b44e187daba8a7539610788ce5e42a200e91368382a399234ea3b30b955588c7a74f7116d4290748ee3c1184bad13a07ca6601f862ce79ecced509664d50f3ef307049f7ee4f2455d0827dee647f947b67bcc7c2dcb3aef17831437ad6a3b92086847f7d50a4cfec13554fb5a287a71c62ebf359f303d33b0c519921fed5cd4133094b2b725d1cdc72ead309f8050b9cccb5993e7aa93a6db91789116a979ce0f83923c2ef127895fd79085831cd023bed26c7916fb4f21bd9035fc057d8814875a3ed40bca21660a98c25efffe2f10d05fec3f3c81468cdfdae241045fc1cc94fb04f12d30a748b55bbd84e431a5adf8278d783245fb74970bc7be0330cba15eefbebe2bebaa77fd68c1c6643e01edf7a8f03d8a8fef650fa83cc48320a7528059b3ad2eb7e6304ef6ff99969243ce4b8f0efb719a6aa8ddb9e6f519e9dd300fcfafb7a19e658ab3eef22f746922386ed906a9b65d11207b28c18eb05f25b5ff11978d8a5ccf161f1ef875f37d465d66186bb7222b2c64099991a3cf1f8c6b555950f2a4d2cf4bbad6a8ef6b477d8ed56bc216d530626d3b77caa04790d3a5c5f6028c5947fbdf9a24c393b0ce200bec4054914637519b4b8698b07a83590bce2f8dab7f1ba7ff31fa7b6030290daf23a86e78337773934eea0b48e68e011d3c7dfd13fffeae51ba650c3bad0a42921791ff0edb954ff2c0260e67533796a18ac81811f5e49c4067081d2cc7c25f90093b7e310aca74274b0b4001e964c2f8c5840d135e7b83c06a85d67cc219e116172dffec54c2ade6585c281f7f83927abc7ae84c7ac82d2b7b8058930274ead0fea8407070f966e9c69545c29d4060d15440c25a9182b18fdcf2f126af1279cfa7b3eef3f04a4fa1db5bf9b0c26d29c909c2afab47dab1ed50e2bff911b568e71f9c618c291b6d0dc47a767095d9fb40fbd785cf756346a0e830d88a118272205794371352be5c70fdfc503874343839ab833622d0a071f2d8e5052f2a7d552cdbac4f58844b9c9b5d9cfbb99fc85b159e05354ab1ec1a9ac93737cde89601e4ce839589e322f622d526d85225a9ce1e00031eafca60bb9184235b4573e7312f1d351649d09d0412aa5fd6e6ea480f7a0b2c308eb00d5a4ff018859358a1c33994a508639b4cc6dacc83e676111c1f1d1cfc0b534215d11c4115319a9249e31a92663cb427433e5bc3de1649c0ad667c6c1b715442986bb9f0a18329067d7deb5143a5d202de4734b07445818052cfa8778de43dfaffe3d6341f2a454fd158ab63646b8d653ecfcbc72e12bfb8c17d1a758527257b1b4ae166f6b2e5599c83a19e0d0800e846e1f6f01de6d79ba9100c8d16ba46224b245fee4449c640effb5a64c7e74fcc7254410255505a788b600a0f9ae7fa3f2d3b07d2db2bd27ca1df3056007270b27c9c523b9cdd0dc3eb126b3cba533faeb6c6a6e10532b4928a4b045551ae6a2653113614dd2f19650192a03ab52448445bfd444bb7ff0a8f48b032e18ebf7f78cb4640ce0125b20155c5b32842f6b2c8a78873d51f25fe08102afab26cfedb89508d3443e7c6bc52ba5bd90f2ea693cc446ff62e136e2f753ede6146e042bc6f929a551ec39a48bd0380dc66e5041057d94b195e919a6cbf703f246b15744c8a230f5369d699d6b0282dc363808fd70dbdc1d230b20f01680941ee03ec6eedbab0edda86a8543025ba6de7e26067a99399d6bee9f14485fa1578b7b07c1aba9071fcffbbe7f51c236aa8f52d04733f1a7e5fb523b25e721fd1728f96612711159c3b0f72595bb1bc77f2cb17ebcfe7d3f92d84931426fe80460b6a0f63cdb5ee0b27c493413da6f8cab3e6979b847f1aa855ea1b1a0c5996ca2e6e5c8a1da893d0e75ad148d8ded2ea5e4e4826c9716b83561f7805cf0e48133a8436cdcce9e5beca5daabd614ba7c62e3afdd10cb93c44e20157af2d3aa383e9790e15688720c0671a4604fa586774a8e99d776f02c9c9d29a6d9456e2ea804d2c32fcf1ab7db98190566a62a9199955716f48af6d2ea4179d3507bb78ce9d4c207678b6626b3f50789b612c20a342b0399034b71a4c1985a056fbcfaece75ae097540b176266ade8c09d16abda3a5cf2b98426a87eade87333f82c4d8c9bdf045a4ba05aa7448310ed62d44ed8c060c698891d86dea3a26e3367217338cebf804cd96922c508d9206353ea54e401980d81fecb82eb5e02da279fbc2a790429870b9b9f001026b1608ed177ee9584f6b7474e506c43e0934844cd5ded8ca25fd2b5580a21aa45ebaa78bfe07e591bc550392f96a7e674e99fd7ba0ac65ab52dd71f3c5e2da839cac4b231c5d7b06ee86c8924c3634143e326dace32e04a2c2451f899117b79a685cf6e7ce100aa09b93566848a033657878220146e5928926c3331fcba3c95fa91f420206ffaf73383f57b3e68c7c51eee03400fb8a4237a91e7670fe8d3fcc8e5c31a044107b89a216428e7e4e700ca43fa89a33c2f42d18cd9c324517ee2915b9f14426ad4faf7029b86da26f3e54921ab40862342c2679071224495a92e4f1a7eb00fc4048a931086d05c13ab9a824fb67776416d508d3ed47c10079d1e0e7930e5db231ad27e94f368d576fbbfff106bb2f0a4abcea597c1a0ba6b95b2c86afe4ff173f6a7923edcfe0f540f906a7540d7c8859b2b42f961e9dcd16953b12c8916f75b50604eb489a5432b68cc41b965e4f0d7df0b86dba56930873f29148e2c7599e357d458362971c904a081ea93757e2d3593107229a2f38d04751abf5e6d4286d577ff6331e0da97f65d41aed54647dc9a9c8ba7ed7c8b92ee0c29ac2ca77bc3fce38340fd89d15cfd4e575ed7f63dbaf4506ad5403804dd87ab7805f160f88dbd6fe1683c79db3d225113b8e51870d2d618a5e08086f028338c10e907defe1de376555411a475825932e4e9567bf51c201c63d8b218c10330f4ec624fc9a9cdc7544147dc43867f55c67c45afb04f1380677578494320b2ffd563e42a32d45f800c394b445d3acabf159850d709e0c187dc6372416042e8895cfa71989ec2f38f689a11c8083fcdef729c42038686836e02927f5ec5f5c5219224ae23158878b9b9b9edf92f26dc951e722ebe60cd493998bdbcf66fb12d354b5aa62df883de4f144cb9bdfa67808142e5d8f951843f55ea94d700b2bd08d218f6372ee104390e35d7b5bca97b370a1197d54adf5c0dc6cb9ab226b3284cd0fa7cc83006afea81bcc1bd6f2b290236eaff4b33c5ab7e0891f8a2c0146e0711b61b9a90ba782a64a8d396a8d64ef8f43ad73f758cc2e85ae792a034ef", 0x1237, 0x1, &(0x7f0000000300)={0x18, 0x3, 0x1b, 0x6}, 0xc)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x0, 0x0, 0x0, 0x5}, {0x3, 0x0, 0xf7, 0xffffb950}, {0x3}]})
semop(0x0, &(0x7f0000000200)=[{0x1, 0x3}, {0x0, 0x3, 0x1800}, {0x2, 0xff, 0x800}], 0x3)
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x8, 0x0, 0x0, 0x0, 0x0)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f0000000300))
r0 = semget$private(0x0, 0x7, 0x3e0)
semop(r0, &(0x7f00000000c0)=[{0x4, 0x6}, {0x3, 0x8, 0x3c00}, {0x4, 0x5, 0x800}, {0x2, 0x3, 0x1000}, {0x1, 0xd91, 0x1800}, {0x1, 0x9, 0x800}], 0x6)
semget(0x3, 0x0, 0x247)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000400)={{0x100, 0x0, 0x0, 0x0, 0x0, 0x1d5}, 0x20, 0x9, 0x7fff})
semctl$GETNCNT(r0, 0x3, 0x3, &(0x7f0000001240)=""/239)
semctl$SETVAL(0x0, 0x0, 0x8, &(0x7f0000001340)=0x5)
semctl$SETALL(0x0, 0x0, 0x9, &(0x7f00000002c0)=[0x9, 0xfff])
semctl$SETALL(r0, 0x0, 0x9, &(0x7f0000000140)=[0x6, 0x2080])
sysctl$kern(&(0x7f0000000000)={0x1, 0x2a}, 0x2, &(0x7f0000000240)="6af55741ac591bd0d091ca493f43edde748a033095a4ed4396950da1e9f18faefb065ee5984813d55606d7eb95badd8b906d37103489ff9b7df3647f83511f5a6ff6ff6446d15f0300000012260b3f", &(0x7f00000001c0)=0x4f, &(0x7f0000000480)="7930faea8d4ee3c42066b44a5e9af788c9e975fd8fe6e458c84e90a5ad3de5f43b81247ace1d1c66b4b516f9e28e0295d911632381de43179cc0916265d06144bf571f5a566b7054334fd9d89a8d7a29a30ba81d47ead18afe66df08f0f8a539b9b4436f6c6faf4437314e3b2a9e4ffb04800f4c35869096b2a3191ab353ede88b933cd749e83c729ac80f4be67870492af3a6e81f004a309ec94cada9de4f7bd9accd0744f240257f841d870b1a441c78b17a50e8f072838d63663d0dcef24c24712ddfd118baf1d6dcbbe38bff3845c6821f907842da3c8af35e010458aea179c8295f8a7163fc088a6bc86a3810e88b42228bfd1a936b", 0xf8)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001380)={&(0x7f0000001640)=ANY=[@ANYBLOB="faff2e2f66696c6e312f66696c65300007448410eb628d48246359d2c725d42bf580eb415261b3434def3d5a08d05125db762dfd7ca0645206f0de4bdcbbdc2e4fdc6ca147a636e02218d957e486e144035a0dc8ba198e62200a2d3f99667b98735fee051ecabd8260da9c52139e3214db1f171a8a28d0a48a4a7873c223b3d725d307fd8d1d4da9f76211fba5610b239d5b75d69c91e368f04308b526e0b0e75038988f8808d9e2c0e78c1b09996b925c8e03601a6afd3328f5c001b27a1d46a46bb75b01e7cb98381b97d568f618fccac2de96938da88679eb6dd1f29f8a446e65f63037"], 0x10, &(0x7f0000001280)=[{&(0x7f0000000280)="b40ade7e395c6e3708a9748e71be14ec9f20a2066edf491f1a4fce3cc3b5b1fe9c678a0dd40b0035c97358c1a887d18eb680e2f030227149d443c352620e335cbf7f8d07c9706b73daef8fbed43e1fc1eda6c2e6473f917f659d8a2c61f7f271c920618c998164110efc2bf6fbdac967fcecaa992f28c8717453fbcd91eaa37edd4c5a1acf406ddfc786251d08bd2b298e599c2578e461bcc466229d11a6350328756a8de879f7ca86088b8f6ca98349f9007b6da98a08f12167c4af30c0ee0d12f474fe529ed5a3e0f1f61fa80cfc6611d8d2b6a5d3de84b89b2daf28f27bc7b876d3cd9f9e2c22a482aa1c7c76c088ed78e6070d78955850049ef3cb99493af5412002be3448c8ff30484e7a40a076ca0f383be4e02c0880062a567555a54459fe875d0cefaf2c7317c68fe9a2727943f7c47b93c97d6de61915f64808989fe15d29ea2ef2e8280653aa861acef859e31cfba42d412aa19e0be449916c9ae632bd48bb84ee50370c61c872529fffb8e3f2e2d7b5eab1ffb02f39c7d09f074dcff9421b0a4bc844ef638055f586003b16909ad52438ca41b8acd89e182639af331bdc16015b14fed5c5203d03305540c4b776f2a5b4505bd70cae39efc0fa9567c07b68283d91f5b169d48e5a71154b8bf7493d4aaad3acf02df075c54df633e3809a9a65d0d3f9d55126da725b2144417ff5c6412f0910775c3aed790e982757f17e551251ff948072daf4387333a5a78be9c24e720000934285ba4139747a641460a2b90fe3289938c54f05a3c5981a6fe6862a0d6f660ade2a303e8d0726dbd3b59f67553a1bac0e46f5192ddede1080be5a1f7a0bf864d3bb2f988d7eac63c29b34009c78334b129a5508a5593906d5c3b79ace0e6b86db9d8b28049d9b1f6dffb6e2b5207a7208f0e24fa22ebecdb2ea733e7b959c88b2b6e26428e3c0bba49864468faca530486aaddc37e72755f502245593da7a9c5ff18234f6a21e6026430f740ac65f580303a913b928f782115aab90f0af0648ad366679fdeff398e612197db7899a32164bf833f177848d80a5137fc78d9860f8ee3780a75e49baac0cde4039f98f1803eca796fd589128d25e28e01a4320fb45220ab52780469a1b8a3b602416ff3189622dc73e9f865237ca057598939cc3c6e8ed41806a7d0fee3c8624562f14e8b5faa7a6fe16f49ec2dbc218fcad8b1f4b01128f6368d42030837c8f67420bdcad21366f390349bc9cfd7f142da5f83a756910abadeab2388e33d0a649fa82824c6b872d9a7de193e4e993805f22069388fe39ebe841101bf79c186526a49b0b00000000000000ddb648c6a571314ab11592fc19761d1ae49eb08713a6cae7db64d2442133af851042fdf8f92509768f92cad3491a07f7f1c52c64f659349e6ac9022bed7939068333bc375148f942460ab5631e6ca0aaa06996a76e94c5625d8035f268d6e72200309eb1929094d09cc73cdc679192bf51a33b8665710ff489d0d37c7cca720c3ee459d5e534d0072d21ff287bbd718306edd230d6499169eeeaa05a4863c5acfc04b57db62d66a77023a374b53f256ee51d18ed1417902dfdfa403985c3080f5f09f3d2efada7d64d6267636fe4b3685d1c3eb78ff8553e31ffc46fb3704605221f145ddd15b00560ad7ca91535c159b5488350e8bb7bcf14da7e1866c9c65291da51bb93711f096eab62d15e2f09e68979ea49621da47c5d4b6b9aa273b024efa8554a55211150c279f0fa48bfcfcef310f9dfad1a9f7c7f7f741a7677d8d5a9db386ae08cf5bc5020ac6654d03af4ab1c9702c6fded2a8ab1b8919a0693f69fa28eec3a490e61c14f036ae90bf44848433f12bc59f0edd39f705096908915d1ac4ecc76147758a2c0374c7a6bbd0bf250fdd68d6fce031d781a3219f242ff04ac0051f863e1f161dd222102a7ad7f5809e2f5b7b0bd7faa5dca39710dd04e414445f80857388c731f04f5610609a32bea4554f605acd95faf902ea10f6a069f2822b34f229418041aed06ef1eed55f45ea538b30c2a98bb5b5423a82c2a601b900b9c4d96469b1b16ec0d7bcf9de6821457e122d67cfbb690e1d10b6179c58387f9ff760816dff2c75c16488c26e9b22278fb9450a3949c2d001e95c6a063b2dca3793fd02133047db95f02b887f4cd46f49e918427e5ef89f15dc3d79717abf4e23c73f8489e3f1caed107b8217895502aa1920430e9c69494259e9891058b463c3937c5cf7f7f9e73ba6b9e877adafd114525abb2e222a4ec24c6b472413eae874f1e86b1150ed2db399fb99ce2689a536ee5bdc53f7297cf8af7eacaadb0f7bb75864b815946e73932cb04cd22f17ccaab6a184cc25d469c80c0d412478785c02b88c70cd9fb5fd80bec139371f37159f8fde18b8203a5753669b5cbea5aa9dc5c6619b49abc5ed00a63ce9a0f7d347e1fb3ddc443955b5072d084defdf076e6468ecaadc95e1df351b6bec9d6e2c13f88f7cc4647deb73610868e924808c68055114456044119d71653561f55ab362c8c34baa0350e82951f29c80a18ce1f01880fb08e9691515b4f8cccbe2190953d455a1874a6eaaab75045b8dd1021ea9a7fc3a455f262b68edce46d6784158425e954d7414ffff3b9e931447331551268f1d11b680302ce0b40f400a91d5dd5b14e4d0cb03cc7c664763f33b03c85f7868eb8f41f878f7601b996ec3213f1f00a77ce763955f8b738d0c2541a9ece404841bfe270ee82949216ec7ddf0598855b4d9b57cb2a521591f1fd2e12981df4c3515cded3ed5f25c677dd163424f43c9e930c23995ff886984218331cdc54c32d8676ec6a37629950b178da270ae8c569ff04758faa8ce44b62cbbd262f74e486a6aeff33623bdd115f963d7f427db63be5b7d379227b55bb4bfb9010eec7af8d2d2600245e8bac4427722366b1069141a98a95b44d70f7676e46c1ffdd6ba1903399e62fd06738f22063d423a384d11960b9fe64b9750b45ccb853bba2b93841784ab0f976f900578df32bce188c1754114012254796aaf05976986b2f60ab458cb0981993e8dde61b239ba05127f1bd592d29423c9ef6b8f46c71a36e4acff49ce0856eed72c492936eb858cb5fbf1eb420d57ad44c6dd9fd2d4e31ab99117b327063a90f221e45842fb0bd61a8c4e51bafb225df8d7ce04631b86346e5550d591e6f979ec3aec77dfe1a11858a21af8fa86252a459159f6c51f938a9f592b9db58fd7270360ee7f1d70227dbfa5f3e2f830a654d17d699702fa0a04ddd8d346f64d68757e7beeb169c6d6194cdc4a004136d8eef0b550a621ac4d9257bdc7403e8128d8094b8d61ea456488e6fb3a956b3eff874266b7a0cf77a287ac2d97c508d46331f59aa19c862d87c84add1076079d2bfa9049091a9e943b89ad6aea8ea4f856c3dbc3c959b10c6dd240c71cc3027305bbee8db409caa007f0eb8ac07427e177d1a7dedf5abad55a3e7e27c250704d156895676c7619eafad59747d1cf4656f2287858935a061a48e645bd59772b9ff1b9d2faaadbb86e846aa93bbb6d52626819b18e275977e027bb86184244da2602e7a0fb2305c1015078e6590ba35ebf5fdaa708467b4fe43f667bb8dafae9ea95a5143236e0db74edf3d101e537ace161688e676ce200871b8d469b9e1f89cea4d73c21e2085674de53bbcd8add7cef1b9ca9318cef8aeaf25faeae4dc797877bd1965e01aa1dbcb86bd72f6595856e752345238cb104976bca531febf34230768ae7515f6145873c743343bff2d609e314b5549b704cc4ff4be2c1857327f04d3dd3523aadb5c10a449c59ca566175600c3f7dafba471cea303dfd20cc041e3d1f225c3baaeff68f85c1f23f46fb8fe485089ae04bb8c6dc83970c88f5d068ab6b83175e2ab39cdd4ef10a162470bd392037b15c6d056263a6e9b5115e4fe68621f92d0d6e1092170c587552004d3104da13fa2459f1469e0664d8344da0a66ca4a174a8295022fee8f7937bf201148b97c6f89a79dbd75b08802f53a022df3f6a405c6bc00d96cb89e2976130067348910218a9c1e9f78d3705d6c685539219513e0328520f1e7581b8da4c03476ebe8f3284dd1f2a403a6801cb026dd62bb34690aeca8fbb1eac39a33666d230aaa5f506d602b9382ba079508d43f129aef8d3da060abd1c8a6746f9ba11b3f450d8467950b414f32a49f13fc6ff3595610cb272e72e6f31e994966dc5b239a0675022b9d3f8c1c867beda62e27836d4178a84ce01b57edfe44089ee819b2dee273c872f5c611f37116f1e061ddd2897767917adebbb270d1d534f21aa29393fb7c82e00d887c8337ad095241ac5d25d62a99a3180603e44ec1c254e36a77a9492ded4fd34ceae33a91934a6510aa791f2ce5e8271d442d86031f4443b8ca6028ae8f0e867b5e3b02542419224ec4b56096af98c252e664fea607380017a1534b36cb069747e68363e74e14a83c525fded13c2b17975a89408440b69f702bf4f379441bdfd90cb16d6abe2d54145f090d571e216e8de593e067f5c46db5ab0d0354b920566f585a81eea15f53771a94b70227ea48b0ad1d1ab862110956c74a40a8441d5ff982306adb3e7a649b8f47a26f2377409453e58f4dbf21e12d54e30c73c3fe96971f243cf20235b73f4c32e2b550fe95a0a26d04155fb77d0643eae0782a8855da5f233f74d913c52763d477cc6fa7a4c8367bec300a2876351b15241e7a23511cc18f5117fb331c72c77e3400e61fc5b5f723b10e80a4ecc758b64cb13060feedee9b54c071bb95589cc9c98bce22df5d5d685d731aab6f435297c5ad38ee3a08f1fb16140d54bc45f1acba1580e35019dde9b25177c84c685d64a7984eacb5be3418fff00c43acd97358e8ea1d4ae19cdae61d2508e8a46715c63e96683fac0bac850bb3d85230d60be52f99689952db8f4a41da092e0414ab917f9ff64f3819a25120c1397fc22f33290575114f0907f5341b2e7572e1d1bf9af201bf91d86c27d59f97e2c7f52040efd299b0635e4169e8cab1619dd6d27e913f671de609d7efd512a3295c2671609afaab9f58fa3bce7ebc88dab66fe9c16a4979f80de730e4bcf7d9d63950f9a35d8dcc6b5a3ea809c0d35ab6e44dcf51aaf1fd72dbe5d6a8693aa3efe181be51d01dde2c0f1cb05dbf7188b38c5d28ec98454b6bdbaa5be1951566c65c57cd6929c15e5150f53eec40bdd868f0bf33ef3bd9a764532f3afd177a469cddd9f7e27c918c8075cafaecbb1b1e3550fc763a9c0678f8a4c540d8dc3b413f2b37681c755ded2f2b9192b8e02c12ecd969d22a561bfa9f00e1395ef33290efb26b56738a62bccb0de692a3e3d56178d8efee2427a34990f12816b9629ed95bc98740dbccf210232e1ebeff083d945c507da12e44448cb906d3b69bead76c10b514f066369466526597b6f40a8d74b533b2edbba1423ec9fe527d6c3859fb38a13b15740ab6fab766a4ddafd051ca01f25ecc45b2d68f6d239901345f41fb087b8843ac7f9a5a6de8c5a93148476781ea7bd49d334a9b79b3c1fd6365b5f310a3e0d7cdc51f8f3c9273e28d473c81a26cb7cd3e57b90c856386b2233cbfc62909275eaf25faea88a531ad2e2ec1ed7e62b8b4417b8dd9caa9993f0327d49ad80123a8354f55edaf6f8900e12395dc6a34838dcef43967a81e5b7ee46b9cdeef1abe221145a9583b1ac8ba1a22b06980cc1baaa2d4e2a424b263460d11c094728e02fcd688a7c2ff319c0700"/4096, 0x1000}], 0x1, &(0x7f0000001400)=ANY=[@ANYBLOB="300000ff000001000000724b0cf6d25970439aaf000000000000", @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="20cd940000000000ffff000000000000de4d87900c5e9cf940922414c038bac4502568ad4c17a555904334d03594f001f135abe3347aa3c9aa8b4b114c3313b946832d1af3e6747be3b903000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="0000e370ec71967de93b6151890d0c002000f6b9", @ANYRES32=0x0, @ANYRES32, @ANYRES32, @ANYBLOB='\x00\x00\x00\x00'], 0x70, 0xd}, 0x4)
setegid(0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x3}, {0x2}, {0x6}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@remote, @broadcast})
symlinkat(&(0x7f0000000000)='.\x00', 0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00')
dup(0xffffffffffffff9c)
r0 = openat(0xffffffffffffffff, &(0x7f0000001640)='/proc/self/exe\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1, 0x10, r0, 0x0)
preadv(r0, &(0x7f0000001580)=[{0x0}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
r1 = open$dir(&(0x7f0000000040)='./file1\x00', 0x0, 0x0)
linkat(r1, &(0x7f00000000c0)='./file1\x00', 0xffffffffffffff9c, &(0x7f0000000100)='./file1/file0\x00', 0x0)
renameat(r1, &(0x7f0000000140)='./file1\x00', r1, &(0x7f0000000180)='./file1/file0/../file0\x00')
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, '\x00\b\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00B\x00'})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0xfffffffffffffdee)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3f, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, &(0x7f0000000100)="c5ca88b2448271d6", 0x8)
setgroups(0xffffffff00000010, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3d}, 0x4, &(0x7f0000000080)="1daf63b25970aebd36020d25e1b10cf3ece6b8be02", &(0x7f00000000c0)=0x15, &(0x7f0000000100)="c2d24c9d34fadd0eae82b8e3168fa740a37829960f8862294ace4e16e103938e10e9fef2d403c0954d8babb49abbd7757e100f97a12aa77f902737ae02664eb0c56d03bb914b2b2a4c4cffe51a6e41cf38d5db8c47370140fb18077b6f8e963e39233b5a0e053e6c86179c589f83b7fa0105135c0b110bd373f3850f556b04ee1ce94eb6e707ac4ea0df9499463f04a489a12e300431c0e42d390839498f20412301ba6400f392533db65e70b870ff4215a0c879ee965532ec5852910377cb30a7d9cf5b1184b39d9d8c7dadb9d96aca5c946e6f5c5c18b50a", 0xd9)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000140)={&(0x7f0000000040)=[{}, {0x20}, {0x1}], 0x3})
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSKBDIO_SETMODE(r0, 0x80045713, &(0x7f0000000040))
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
setrlimit(0x0, &(0x7f0000000240)={0x0, 0x3})
close(r1)
syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047466, &(0x7f0000000340)=0x8)
r2 = dup(r1)
ioctl$TIOCSBRK(r2, 0x2000747b)
readv(r1, &(0x7f00000001c0)=[{&(0x7f0000000040)=""/157}, {&(0x7f0000000380)=""/4096}, {&(0x7f0000000100)=""/190}], 0x34)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
link(&(0x7f0000000140)='./file0\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x3d}, {0x15}, {0x6, 0x0, 0x0, 0x8000}]})
pwrite(r0, &(0x7f0000000240)="fbbc8a275a542b3f000000000000", 0xe, 0x0)
pipe(&(0x7f0000000000)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
write(r1, &(0x7f0000335000), 0xfcb7)
readv(r0, &(0x7f0000000340)=[{&(0x7f0000000580)=""/200, 0x87}], 0x1)
writev(r1, &(0x7f00000006c0)=[{&(0x7f0000000040)="b79c7579dd227b418f28af711ab41664e5ba778c73130e49fd8f1bef9a8aeeeb95eab06798c206001060b04aa8f950a6d812005c5c3c78fc9e0e195bf2f0189327f4b47266febc685deaf2b22cec68585fd9ba563c93777d52eb6df23540f42cc44b9693e24a90521219bf4588c7a7b55d1d089b1f18bbb7ae535d57837e3826e893a4f37aa5f833bca8ade7b9caa3101b6550c40b5aef0a194d9024dac9736f6100ed48e75f91691805220c33284599904d097d00"/208, 0xd0}, {&(0x7f0000000380)="d472470000000130bbaa74f25552827c3b3f958c0b1956e1bb3bae5c0666cc845227ee2500f2484a4012466ff93807065f3a55934ef3d9f4cd024eb18f0dd482c8c80b29dff3155908000000ec7d847e8ae3bdaa3bbb2d3f1dc0ea", 0x5b}, {&(0x7f0000000180)="c8f4c517faa17dc0cd31322ede2b10c2eb72fafc513eb25854c5a6c2add51602cc05626a54dda827b113ab2fe86e8a4eb9400b48f2f8a38f2d3bb0b5f28c91ff95a32b1c3f77f5628a7c2ab74c9b92bf3ce54c443643486d515ad0df78433acee4694034e7cbb4a792fc38701d858945611ce67fe1bf28c11d4643e0e4282d7debfec4e5bad2f0da9be9f2cbe11a14beb3daaa9b33223e8c76a1f06a36c059eecb5c68e59a4d45b268ad528702447018c03c8cbc2d4414c486018f8fa11e28c2743d35b91ae2b6e6bb3fe85a650b132ca8d7d602d81764fd7f6da40234504866737072a4bc312c50d669b53b4ed847a00965f4150ecd0e0e284aa1bf619fec83cf5a9ea49a5a6aab89828f7ef2bcbc5fcdbf5630946ecaa35caced2f00f89d61a970df49e6c2055da684687230ed0eff0853f64a624ba6a1190520000aa9ac2a52fe75ba6314c4faa5796c904f46c389cf745f80b2c49f", 0x157}], 0x3)
write(r1, &(0x7f0000000700)="d5e1ec5b64a7475db1b1fac66f20f183f4f0fdcec3011f80004ef26b68196d4c029902212a3c932673e9e6418fe4eab11569211d88c0951eab197bf02030b8d2016636bad9c2038a8beabef2d36c8cc9d9412f2a64353e9ba921a80e8b9d0910f2e984a443fe2d7dec5b2117dafa7b1cf1c8ce3893b01f9b7732e5e2bc9e270d943a1fc07bfa5c455bb80657cf840834085b436d4abcde944a066c13a582bb8af278f97b559506110e55b87a1d077bb3acacefdd7530e385f52b302b9176448136918107e00c4ce6", 0xc8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x45}, {0x4d}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}}, @tcp={{0x2, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
syz_open_pts()
syz_open_pts()
syz_open_pts()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000002c0))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000000c0))
syz_open_pts()
select(0x40, &(0x7f0000000040), &(0x7f0000000080)={0x4f9}, 0x0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0xc0e99db6de761f86, 0x0)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
lseek(r0, 0x0, 0x0)
rmdir(&(0x7f0000000100)='./file0\x00')
r0 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x4, 0x44, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x39, &(0x7f00000000c0), 0x4)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000200), &(0x7f0000000240)=0xc)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r3=>0xffffffffffffffff, <r4=>0xffffffffffffffff})
bind(r4, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
fcntl$dupfd(r2, 0x0, r4)
accept(r1, &(0x7f00000001c0)=@in, &(0x7f0000000200)=0xc)
dup(r3)
socket(0x1, 0x5, 0xb8)
pipe2(&(0x7f0000000000)={0xffffffffffffffff, <r5=>0xffffffffffffffff}, 0x0)
r6 = socket(0x20, 0x1, 0x60)
setsockopt(r6, 0x6, 0x391b, &(0x7f0000000240)="a45ee3bc4d12d20d9226bce61d1ade21b08a68077c6db4c20ba8d6673ef0d0bd06f190926597fb8b344ad539c7f3cb93ff5b6264ce61f9d50d11b30e67171b298b4d730af06153538c7038bf5354c29b9b6e0124b6505521795ef5448af0f25fd8840f0f7e078e66f238cbaa7055b200adf73440846d906e59f9da37590bbb", 0x7f)
ioctl$TIOCFLUSH(r5, 0x80047410, &(0x7f00000000c0)=0x8)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
clock_settime(0x100000000000000, &(0x7f0000000140))
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x20, 0x0)
setrlimit(0x0, &(0x7f0000000000)={0x0, 0x100000000000000})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "d730c15bf4ff03ff2a7beffde4000000000200"})
r0 = socket(0x18, 0x3, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x3, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000000)={0x3, 0x1})
openat(0xffffffffffffffff, 0x0, 0x0, 0x0)
dup2(0xffffffffffffffff, 0xffffffffffffffff)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0, 0x10, 0xffffffffffffffff, 0x0)
lseek(0xffffffffffffffff, 0x0, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c4, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000206, r1)
mmap(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x1, 0x2011, r0, 0x0)
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000001300)='c\x00')
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x300000006})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r0, 0x9, &(0x7f0000000000)={0x0, 0x0, 0xfffffffffffffffe, 0x1000300000002})
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@random="51d2e863332e", @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @empty, {[@ssrr={0x89, 0x3}]}}, @icmp=@echo_reply}}}})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x86, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0x4d}, {0xc0}, {0x2006}]})
pwrite(r0, &(0x7f0000000200)="e803f150ff8569142b71abf63143", 0xe, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x19}, 0x4, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
r0 = syz_open_pts()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000040)=0x8000)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000280)={0x0, 0x0, 0xfffffffe, 0x0, "551577cf80fc7b457dbe24125f76e25dc0ced443"})
poll(&(0x7f0000000000)=[{r0}], 0x1, 0x0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "1da6816170c234122ec406a6dac8e01b2f26314d"})
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x6, 0x2, &(0x7f0000000840)="62843bb0", 0x4)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
pwritev(r0, &(0x7f0000000780)=[{0x0}, {0x0}, {0x0, 0x5}], 0x3, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x44}], 0x1, 0x7fffffff)
close(r0)
sysctl$net_inet6_ip6(&(0x7f0000000040)={0x4, 0x18, 0x29, 0x35}, 0x4, &(0x7f0000000140)="4f009952d47c707ffa7d24e95b32c887", &(0x7f00000001c0)=0xfde8, 0x0, 0xfffffffffffffca6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x3d}, {0x4d}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x80047460, &(0x7f00000004c0)={0x5, 0x0, 0x0, 0x0, "61aa0db11ce9bae3c6514e6ae973739ea78b7ec0"})
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000180)={0x0, 0x0, 0x5fff, 0x20009, "a5130c00000000000000000200"})
ioctl$TIOCFLUSH(0xffffffffffffffff, 0x80047410, 0x0)
write(r0, &(0x7f00000001c0)="fe", 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x50}, {0x4d}, {0x6, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f0000000300)="bfa8fb47c4b3ff7e18b15dc05e7e", 0xe)
syz_emit_ethernet(0x3a, &(0x7f0000000080)={@random="eb0420e10a89", @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, @multicast2, @rand_addr, {[@lsrr={0x83, 0x3}]}}, @icmp=@timestamp={0x9}}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x3f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc0206921, &(0x7f0000000300))
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
setreuid(0xee00, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000180)=0xc)
setregid(0x0, r1)
r2 = getuid()
seteuid(r2)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
chmod(&(0x7f0000000200)='./file0/file1\x00', 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x44}, {0x20}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000500)="b1cc583d386d3381f943ffbacbea", 0xe, 0x0)
mknod(&(0x7f00000001c0)='./bus\x00', 0x280002002, 0x2065d)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000140)=0x6)
pledge(0x0, &(0x7f0000000080)='vmm\x00\x01\x00\x00\x00.F\x03\x8a\xf1\x9fd0\xae\xd7bk_1b\x01\nQ\xc7(\x9e)\x11\x81f=;\xecZ\xb34\xf6\xb2\x91\xe7\x13S;v\x85\xb2\xf6\xb1\xcc\x8ff\x9e\x88K9\x82l\xceAN0\xfa\x17N\xde\rU/\xe3S&\xda\xab_\xf3\x7f\xa4\x81\xef\xf4I!\xe9c\x97\xca\xc6\x89\xa4\xf7\xa3\xa6\x93]')
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
r0 = kqueue()
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e6", 0xf)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{r0}, 0xffffffffffffffff, 0xb1}, {{r0}, 0xffffffffffffffff}], 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{}, {{}, 0xfffffffffffffffb}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x1, &(0x7f0000000040)=[{0x7fc0}]})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xc0284416, &(0x7f0000000240)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x4c}, {0x50}, {0x6, 0x0, 0x0, 0x10f0}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014f6317e37", 0xe, 0x0)
sysctl$hw(&(0x7f0000000100)={0x6, 0xd}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x4001, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8020697f, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$BIOCIMMEDIATE(0xffffffffffffffff, 0x80044270, &(0x7f0000000000)=0xc2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0x3ff, 0x0, 0x0, 0x0, 0x100], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe]}})
r4 = socket(0x2, 0x1, 0x0)
connect$unix(r4, &(0x7f0000000040)=@file={0xbd5699bc1ec0282, './file0\x00'}, 0xa)
fcntl$dupfd(r4, 0xa, r3)
ioctl$TIOCNOTTY(r1, 0x20007471)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x2, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r1)
ioctl$TIOCFLUSH(r6, 0x8080691a, &(0x7f0000000300))
r0 = semget$private(0x0, 0x4000000009, 0x100000010)
semop(r0, &(0x7f0000000480), 0xe)
semop(r0, &(0x7f00000001c0)=[{0x0, 0x22f5, 0xc00}, {0x1, 0x0, 0x1000}, {0x1, 0x1f, 0x1400}], 0x3)
semctl$GETVAL(r0, 0x4fa596d1f056515a, 0x5, &(0x7f00000194c0)=""/251)
semctl$GETZCNT(r0, 0x4, 0x7, &(0x7f0000000140)=""/56)
semop(r0, &(0x7f0000000280), 0x0)
semop(r0, &(0x7f0000000180)=[{0x4, 0xfffc, 0x1000}], 0x1)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000300)={{0xaf, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x40}, 0x40, 0xfff, 0xd})
semop(r0, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x2, 0xc2f, 0x1000}, {0x2, 0x96e, 0x1800}, {0x0, 0x7}], 0x4)
semop(r0, &(0x7f0000000040), 0x0)
semop(r0, &(0x7f0000000040)=[{0x2, 0x0, 0x400}, {0x1, 0x8021, 0x1000}, {0x4, 0x4, 0x1000}, {0x4, 0x20, 0x1800}, {0x4, 0x2, 0x800}], 0x5)
semop(r0, &(0x7f0000000080)=[{0x2, 0x1000, 0x800}, {0x2, 0x6, 0x1000}, {0x4, 0x5}, {0x4, 0xfffb, 0x1800}, {0x1, 0x4, 0x1000}, {0x1, 0x3, 0x800}, {0x3, 0x5, 0x1000}, {0x0, 0x7, 0x800}, {0x2, 0x7, 0x800}, {0x2, 0x3e0}], 0xa)
semop(r0, &(0x7f00000000c0)=[{0x4, 0x83}, {0x4, 0x0, 0x1800}, {0x2, 0x973, 0x1000}, {0x2, 0x0, 0x1000}], 0x4)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f0000000280)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x110, 0xe972}, 0x8, 0x9, 0x401})
semop(r0, &(0x7f0000000240)=[{0x2, 0x1c, 0x1000}, {0x0, 0x5, 0x1800}, {0x2, 0x6, 0x1800}, {0x0, 0x1, 0x400}, {0x0, 0x6, 0x1000}, {0x1, 0x6, 0xc00}, {0x4, 0x595, 0x1000}, {0x0, 0xdc6, 0x1800}, {0x1, 0x1, 0x1800}], 0x9)
semop(r0, &(0x7f0000000000)=[{0x3, 0x97}], 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b10005046000000000000847d7", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
minherit(&(0x7f0000004000/0x4000)=nil, 0x4000, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000040)={&(0x7f00000000c0)=[{}, {0x6}], 0x2})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
setregid(0xffffffffffffffff, 0xffffffffffffffff)
syz_emit_ethernet(0x3e, &(0x7f0000000040)={@broadcast, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x8, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @rand_addr="ff017c64e207f754df0f9b8349a142f6", {[], @icmpv6=@ndisc_rs}}}}})
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x3}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0xffff, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000480)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29f", 0x70}], 0x1, 0x0}, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r4, 0xc1084413, &(0x7f0000000240))
sendmsg(0xffffffffffffffff, &(0x7f0000001bc0)={0x0, 0xa, &(0x7f0000000400)=[{&(0x7f0000000280)="fb", 0x1}], 0x1, 0x0}, 0x0)
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCVERSION(r0, 0x40044271, &(0x7f0000000080))
r1 = getegid()
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000040), &(0x7f00000000c0)=0xc)
r2 = dup(r0)
ioctl$BIOCGSTATS(r2, 0x4008426f, &(0x7f0000000280))
ioctl$BIOCGDIRFILT(r2, 0x4004427c, &(0x7f0000000240))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r4=>0x0}, &(0x7f00000000c0)=0xffffffffffffff3d)
setegid(r4)
setgroups(0x1, &(0x7f0000001840)=[r4])
r5 = semget$private(0x0, 0x4000000009, 0x82)
semctl$IPC_STAT(r5, 0x0, 0x2, &(0x7f0000000140)=""/163)
semctl$SETALL(r5, 0x0, 0x9, &(0x7f0000000100)=[0x6, 0x2000])
r6 = socket(0x20, 0x4, 0x0)
setsockopt(r6, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff31929648000001000000000000000000", 0x14)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000540)={&(0x7f0000000380)=ANY=[@ANYRES8], 0xa, &(0x7f00000003c0)=[{&(0x7f0000000440)="08e6f820a3d6d07499aa58035debda024bcd75a21ab7b8c3f4fdf7f36b4bcdec0f849d7996299b8a89f603c4b1250975630788b44814428db49902f14274744a690335bb950d8d234d97bc066869601fda30fe1d3823b8a3ce76b600668c0f688015bc4185ac94ff14d35a9c2f0bdb651cf095ffd309300977325d761454069b3dd4a9777f569039d0", 0x89}, {&(0x7f00000000c0)="80a3d4c0f4004553cf93258a0aa0eb088860f2b06312faedebff7b3153d0383df2402015b4c52077aeee2ef9f3b8067f73d70ee1c7b2f624adb3c669c750ad31f9bb286d1668067beef6d6d5cce62af067463d57de769d180426556fe27c90c588202e", 0x63}, {&(0x7f0000000140)="1093db8b1e6d39d9a1012bf1d1389de316b6ed4d851dbf722433f53f25bc562a868bafbf2a6b19fc9416cc57dd4a5363cfd37b4407f273899be25b6e8b4c41a25dcc946dc0b7db66dd14de3d26c540fe21d5a37799f08fa44f77d3265600091d5e9556b7ae53107427d25e916bd64f2d4bdd2c4a3eb6f3a91fef86edeac1ef5fd3626809d9bbbae4a841fa1e327e8852aa56326e664b57329cf4a3df5d90706dcc13e7c2ce2bfc0aa5d3dcec885973035982457c08818401db928b8a9ff653f47b9272c5a12f5c197896", 0xca}], 0x3, &(0x7f0000000580)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES8=r6, @ANYRESHEX=r5, @ANYRES64=r4, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES16, @ANYRES32, @ANYRESDEC, @ANYRES16, @ANYRES16, @ANYRES32, @ANYRES8, @ANYRESOCT, @ANYRES32, @ANYRES64, @ANYRES32, @ANYRES16, @ANYRES32, @ANYRES8, @ANYBLOB="000000002800000000000000ffff000001000000", @ANYRES32, @ANYRES64=0x0, @ANYRES32, @ANYRES32, @ANYRES32, @ANYRES32, @ANYBLOB="71fd5e6445d48e95bfb0acccf40007c37526c8ad78cf9ab4f890c7d8425b8c1144420051b80650fd23b9255de58a3030d1601996005f5f7ed3489f6572c9effea168f2ef7c48b03499d4c425d3d9488d5902ef6fefb79d408c30c773e3b8286bb8f901c8e335e9188a", @ANYRES32, @ANYRESHEX, @ANYRES32=0x0, @ANYRESDEC, @ANYBLOB="e6b001b62419424634b6d0661f66efdd8f4a4a744c5169206983487d7724b303a8ab8d3da963effa1b5aa4bc49e82206056fa7b7cec94e18adafd87c5070cec626bd9988a17e79d601ab6fbf9e9644c2e4bf3aaea87ffbc52dfee0e5aaf541723d235fe2f32a925d87367cb8dafaec0124419cf222a7ea550a5b776b7041b14b3cade9d53b5802c8db314b143306336f0171443c3904d7fef40d87ac61b20f8a53befa4ae3074bda26fc948587eb66308155ffe796ea22c9737bcc9a6e50ee9ec87216d5911fa96afe664c8b7cb136ac5ce142e06fc50c2b3c22b64e470cdc82b64fc8443884120376b3f5910551daabbd9a2f0671eb69b5f730a26328ce5ef6dbe10447dcd92b868fd9dbd67c988bcac3b0df90f5e142e1fb813208d86cffb03b7961f73fa60389d4438bf176dbaf580318d0e640c61b569d38b45d8bdd6096971758acab8e1f15097d96232f08a00de19422e2a5691acc307bc73be6ee50bdf281677bd7d3ffd7206df31366468cb77e78e08f48bd8fb57482a034790ddbfb5458ebb3ffff05ecf5dba021173f402236412632a1e5716369cb2a98fae0233050905add688fe4d649687939530f54fc239a299bb28842c47e679dc3a5a89ed48c676159c2628a1e134b214e3b6f9f5a46c1ddcc5dc673ceed7efa7582876e6fb55dc04d5bac3446a39afae41b33b2cf74037d2eae5036bb335c944d4bbcfcd3b99496e0678469be085764246b1d78f5c10b7c2d8b4dc30a42b3adf2345d136125488fbbd3a76a3b26a7a73a2b9e4ae542a9d11aafc91e3ed5aeec44826694c7b86b71793a0bd4cad4ee3a40dc8535dd708e062a48bfab79300e05c6b8897687bee24a46fce8b04e8200e1e6a2d5ccdf083cf5b3cc41f4685b68ea6a371a4a34630bb68af338b8ab10193ee91c9b14e06afc0fba7c4d18b29ccd57f192e60666dfd0c9c7b11abc720fd98ac82f3fa207ef8a3e025d645411d42a4e242f95819220837d1cc645e6c9fafb1f78be92a9c31528598400acb7947a8bb69190ecbadb0730971925608f4eff38932e7bb5f76fe0f999fa5232aa29542a475c972bbd058e6f4d6135f92a5cb8c8e21c38b1ed7e28b97b2dcb8a4edc3ad5f2d27459bb4067d7e10e4d35b8c1718e3e29a36b096f436829ff81e0d2a75ddcf6ef46082001350bed099fc1cd95f1ca40f2965d125161e771e365a3c1cbfb7752620e3b7b11bf74d5e30474453a530be4825f44543a379d7340283fcaabd0b408b953a18507596a333559cfcac8ace65a6145e7734d4d33f2f6510f8e55e3c576c9c67d32abf7c3a9bb489c62223032f186fa32f3e8fa02b26e31cbe6ecedd71570b67ffc9488570616d65c3b747ec73db56abb4cff0a0112c5da55fa5b4b9a2f9985998dfe1b00dc59840699ac6f1badced1d00ef11c2d6238ff96c4f91929d08e0e9fe647ff3ad702893608f491cd649a6beb28e1a0b70b00ea77bea9c4558b87c4d7867ae58a133e9055a962275b242d4213b4020145a43ed5c003a53f6c04c0b111dabecc03b584acb526754158c11222f8d79504b3399b59d57d00f286188baf9330cce6f8c7d967ce62c9235f9bbe7f86303c1d3ca16130c77e88ed00fd19df72a57983c75846e7927af05277ee34589e87c8b12a84e3fe780ea077992bdcc8daae2bb9beac2c0225d963441d05534ffc5aa23afeeef17af415923036f8aa1070b9b1ca4549ae57e11969edcfbdb845825f04d17e0bd257cac597dd693275cfb5f0af0bc18b06fe92010b748a844c359a79f7fb2bbb58a69cb9357ac60d3fb4d736eec1e5f51352bb08d3d2a4f200cdb1a6d6d23e3d3467dd48dd1ee0f1942fed7e49641dd3d145d141b9a4e4ce491a6e9a9ba33f0f14ea48e924eeddedb41afceebb32985ec99f640e03ddd39a7f76a880bab766fe08b756de7aa088b4593ee0fd660451146e95843135c8d4a2503d9c11588662269263b6ab9c9e16320f183bc79814fe4aee96e888f0162c2aa084e9e1e8e2acc7d926b93ad7cd4967353d78a0bcd29e3423a4cb0e006bb78bc239da2b0f122aa112cfc3f1d4da9680148eca0e1a170fe1f266c02e689e15b858ee64abf14e3d8571ee218ce91ef689115acb8ab86e05b365a22293717d37cb785c42033ee3fdb7dc158de8e4d1eb534bc9684671baa4e603f409bfe9686b4d0772a7bf31f0735426de39a7f6aae814a980ca462f2dd8541490a252c6a55c955c507bec884a77e3ae27402c8d54d47d53e3a76db69c7f98931a84f5593f9bacbe8429ed0be994d05d5f635d282dae61a91e5cf080f58f69883fc04b70480522204567c29d1d0f52fee6542df58a535c6727c317f7e399a0b6e70c18d2f7c3447422cc3dc8ddb93d1faf9a8ec85c1aeddcb25ea8aeec9831738e4d2f7b2f8f5b892d140da46f0d3a8ef921cc008683433b5569a71208da0309e0318c15347555ff9bbf072b56b97111b9e5481c699b2f97ca94de2eb9a7846c046812df610d2be1b0e1943be2f3e8b2deb1c30a0ba91493520a6ebcfd902ab902eef02d02d63fa347240b806fa89661f80a9cf01f61155d782b598a763a22ab08f8a395c668a05a2bdfcf17ae39abacde55393030d7d16512402bf849d67de140284ca117d821b3ee5c9e68e157d83f30d4b96f204cdb6096bb17aef0dea72319683630cd1faf2e5ac532c2be33c3d066879d69503a99510dc3b027baf1e197d0270dd2d9589d76d6c25fba36f2ad8bfed13006b0e5775e7111876f613c82dc84bd9d6b9bf69a638533196014eba0e8ca6cc8d003e4e89a692a7b12f6847bd0122a5794d428d480d766c22d742e6c67bf8bce2b2b67856d3b62571c2aa1a808fa42d55f48943cb381ac7cc0db8080ab263e3fdf29c8426859509e68da17178d7eecb12fc2976b4d6e7e1a80e79fa2fa7df3c3cbc382ebcd6d553e2290509bfc2227733a05d8b903c2241da8c8fcab11cc95e1c18b4f7afa26b6973e1c629f4ecf72c7b768fb4f2ea8157e0ef501b3926f290d1d8c6b23eb1b34ce590dc75bcc48efbd21db6e544d0855ec08224405865dc05fe018e2c82ff8a0f4609b13475372b94d42c6e697e9cdfa4cfa9e9da009d8fcb461a757ac801d4378fb39cf86c80ff42b70a25ca3fc875e1c7004329ae23fe1e6cb1afca0b9c075b40eef7e73dfa915dd10d526042e012cc89921488361244020e1e8308ffc43ee5da6c714e55473d85ec6ee6fdba739782b35d567c157a2890b317a3671ef12295b69590cf40ff956b96f6f85526f7c67f50d7515a510af4aec37b305d2c1b71590235aeaa8822c0908f8411981868d758195dc016255e26b43ddd5356f8397a152d74f4d68af79de7443cfb6302adbbaf27a3104147dff927dd2617c8fa23816d17c9e20bbb58160560ff4cb0e6b0f26b43d02a32954d7e5dfdd82f0cccd7962e0634245f00d7e00552549b93cf40fc7b8a39946bf25a8ace97e1a974ba92af8f295d01c7fe032249dcc39c90b9bddbcac5a53110eb69cc9634cbd7dbd39b89f0d0d4fac55b34a7512b3eac92bfb7ef2387241b24ed966467f2d4b6f7712f1b153828a03e053883112fa3a86a665bfa2a99498749fe426a4df0fab66ce4cb9c0e88c715844451540f4a6281499f7222b897055f9f5be7cc127bd53581fbb9af879eb92f0e5b83b06dfc1608786aa287a430451c752680efa35345fc686bde9e492ab3684095d98b7ca8c7112e8f13014622e57d0f98fedb8be9e0b111e8a7b7a22cd9149929369b0c23f69cdc9fd205397b81b401a38a8442b1a91df997cc14e49448e9ea04b7b4a1fe5b53bf48ef1960aa85fee56bca260c1e174c3b363aba606d40539948f98706451796ac5f4bea806e89783afee0a3f422a375db426cfab16b922afde57a65aeb3eba12cac68fa5a830525432b274aa562a95b3f8239c7353d3868805c092b4894514e258450c3dacf2202a0cb94f3d93ba52e706c3a9379ee2631e3dafd85214816b4e6355abd631ecbe0c3a8327fb0b0fa3c9cd596dd63a78eec8d99ed459d2fce3578620e862919b34155209f85be8c8f1f084c99886200a19371cc6416cc024a33a6ebf3514196813f8dcace6a3e945c2e301fa1a8b7ebb3a4ce0f2e6a7272d235d819bf2708798e2375a41de56e875453b8e8113b076f2a3c4c34742b13c6c33136527f142e42546f0f382b6bf27f51a25bf8c30446d5af5c89dcf9b17a39097a5ed8f1e9e2137d91ab694211542c5cd080cfe0a9024a5095239304f604e36f646c8c34d83e9185d4b03b53c86376d2f86df43286f08eb754262b1b3cacf9fe3d2a807eb5416ed1a46aeae6d7c32a4dd5f8b823dffd37d0ea00b3edeed0acd835d82e4805692f2f372d5da783c9ed2ff29881bf5b8e3819814c085f939aff6d8cec44c4ca5497b2a3ca240e554871e6b5b6978d23be51b9e1568db3b0b55234e9756436c411ab97fb91c436a217b6b8b718727801ea08bc0573df1ecdb4a125a5cda2426a3fbd3b52f0b5db40d657b8543191b936c5b1a88c7ef9547bd1748fdcbe5b7fc56b5c8baf62cd14177e59b33b9d04f0b84ee1baa504e8b3a814b99070316f0a19272c52aee588b885ab6afa2b6306809416bb3816cad2045e354ab9397062c52a24840e5238ae2757a7ae01590c2891296c877b6fd967eff5c6beda92415d1da321767d9ccfe051174ab4462973654aef1291b26afd5461ed1cdac0ea3544a2b518f960fb4a1d2c97b34637a58e4c9ef17c09ee935d6cbc7342f506344aad0f260bf9f78cf0430fd35f56e9a9b00c4a076b67e2d0646aaceb818cfc5a80a555bff7b0a247ed8bc68f4957fa2e7baeec732fdb20a5f98fbdb64edf7eaeddb28b647fa601ce99c8fcedfb6870a48d20c64017e24078f2fdad1e7f12f86093881947e3d1d61ceffd6bc6e3e029c89d6a179066325ec011386fc342e24c60e7c08fd317b204ead120be13e33b087d90d235123d891d94deadbad8b762d599ae722191addc994ec04a86bcc93b2e7aef37bb7b4ccf6bb0f68aa804d3d2751861369b14515488c0d30779c9a1db50543e9502654f2cf290336dc43f75c61979bb51aa25fda50e9f0bd3aeede76cc560f87f3fbd1d578af6c00a48706016cd1aadec30d1786c8895f77387d7afeffef471752d8deb5d58c9315b3df6ab2281377fb8b47de758afb3ca652e9aca6239c48058c39dc550efc18f0ad96e1303351a210420abdebe133a3735881d49e9cedc2bca07402ed44db47253ecfb3343ea576a43c84363d6340a6a598b50cf34752d4a08ec501c057d6e0871b19278fba6e28fb5dc8f92404b1ac595077e9107936206b572c23d477f303dc1d68954c982b0d1c263fe58f34ab6215da71f4498e755665cdd9157192914ee2317aad44794e1a67c4cac338ba126660b635bace89c05889295952d8880eb7fc880325f4d365a8f91ab930955e83ab6fe3fd88dab00d2b33bb47f4012390cf14471c6359f1ad6ca04a7d0e689eef656a962387626d597bcef494e74a9e30632b763e8fefaa4352de4da786cfdef1b242ebfafd3c7fd4211b4fd672225df5fcfa366867cdbf64c78089dec87265c29eeaba29cb45ab8c356bff59ca5bd416096f3b8cb12402e19793ae0033a16614e0772274c422ced9a53a45b33bb56a6a87517350cedbb8ef5d29fc8e400ce5d6ae9def10afa632a12816c390c6ac2f6449a834b564e0fa661248219f2783f1608f92aafd1f1d56e1fe1a8028bd0996c4da3de6c26324cf97aed7fa4c1632d2619100"/4096], 0xe0}, 0x0)
semctl$IPC_SET(0xffffffffffffffff, 0x0, 0x1, &(0x7f0000000100)={{0xcd51, 0x0, r1, 0xffffffffffffffff, r4, 0x64, 0xf800}, 0x5, 0x5, 0xd48})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
r2 = dup2(r1, r0)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/28, 0x1c}, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x800, &(0x7f00000001c0)=0x80010000, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000), 0x10)
r3 = socket(0x2, 0x8002, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
write(r3, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000100), 0x5, &(0x7f0000000080), 0x0, 0x0, 0x0)
r0 = fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
openat(r0, &(0x7f0000000180)='./file0\x00', 0x10098, 0x68)
r1 = socket$inet(0x2, 0x2, 0x0)
write(0xffffffffffffff9c, &(0x7f0000000240)="d225d1a47f2ba6451d281b33c1159d03e1694d63f051acdfeaf8e59b299d0867912112b2a1ba4d3dfa97d528d216f5b052630a0dc3a16d433040898a6cdabc7cd06391", 0x43)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f00000001c0)='\x00\x00\x00\x00\x00\x00\x00\x00', 0x8)
setsockopt$inet_opts(r1, 0x0, 0x200000000000d, &(0x7f0000000040)="eaff125c00190000", 0x8)
getsockname(r1, &(0x7f0000000140)=@in, &(0x7f0000000200)=0xc)
msgrcv(0x0, &(0x7f00000000c0)={0x0, ""/39}, 0x2f, 0x2, 0x1800)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r2 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r2, 0xd26462bf0b56ad23}, {r2, 0x4}], 0x2, 0x0)
r3 = msgget$private(0x0, 0x40)
dup(r0)
msgsnd(r3, &(0x7f0000000000)=ANY=[], 0x169, 0x0)
setreuid(0xee00, 0x0)
sysctl$net_inet_ip(&(0x7f00000002c0)={0x4, 0x2, 0x0, 0x1e}, 0x4, &(0x7f0000000300), 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x0, 0x1}, {0x6}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000), 0x10)
r1 = socket(0x2, 0x2, 0x0)
dup2(r0, r1)
connect$unix(r1, &(0x7f0000000000)=ANY=[], 0x10)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x2, &(0x7f0000000040)=[{}, {0x1}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x8, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
mknod(&(0x7f0000000000)='./bus\x00', 0x8100800080002002, 0x0)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIONREAD(r0, 0x40047463, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{0x54}, {0x4}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000240)="f6c1953a15938c53c6d002f31a63", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f00000000c0)=[{&(0x7f00000001c0)="f89176d2829b093eb403caa8d01cf66722f5e90172fd9b69c9821106627301330a52", 0x22}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000)={<r2=>0xffffffffffffffff})
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0xfffffffe, 0xfffffffe, "000038004000070056e9af0d76d9deee000000cc"})
dup2(r2, r0)
select(0x40, &(0x7f0000000040)={0xff}, 0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
r0 = socket(0x2, 0x1, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0x80206979, &(0x7f0000000300))
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
ioctl$VNDIOCCLR(r0, 0x81946466, &(0x7f0000000200)={0x0, 0x0, 0x0})
sysctl$net_inet_etherip(&(0x7f0000000080)={0x4, 0x2, 0x2}, 0x4, 0x0, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57d7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
shmget$private(0x0, 0x3000, 0x0, &(0x7f00004df000/0x3000)=nil)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
sendto$inet(r2, 0x0, 0x0, 0x401, 0x0, 0x0)
pwritev(0xffffffffffffffff, &(0x7f0000000080)=[{&(0x7f0000000340)="c88c5506a35ecbceac0007ad72f7bcc833655e95af12ef5bd63c30ef64820e5abbd6b0cf67ca91eabea630cefd442e4c8d88606ce60d4098a632248de9b5ec308a351c7e403db3673540db5e2988940d961c1005a58b7b4d5280c89e8c9fef0b146c6eacb20d125aef59042c846e374f6adb4cea144a04587cb6abe9bb2be00b51c4ed3a24e1040575604a5f9055632e4f7214e0668cda34611dfec172ac0bd7d759b9259da534f2c681a6202a0ebda932d27891aca34c33160bf0b1a67c3da7554ac8b748c208bda9f51187a7764ba8d983c49bccba48eb70b68000000000000000993e4b9296fcd0dd763b15381d3334", 0xf1}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc4504445, &(0x7f0000000000)=0x2f)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000080)="01", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1000, 0x0, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={0x0})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "00000000006a0a00002068bd2f83b14ff4dc0001"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
syz_emit_ethernet(0x46, &(0x7f0000000080)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "fb00", 0x10, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="00000000000000000000899be21400dd", {[], @icmpv6=@ni}}}}})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000340)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000280)=[@cred={0x20}], 0x20}, 0x0)
r1 = socket$unix(0x1, 0x2, 0x0)
r2 = dup2(0xffffffffffffffff, r1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {}, {}, {}, {0x0, 0x0, 0x0, 0x2000000000000}]}})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3, 0x0, 0xffffffffffffffff})
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000000140)=[{0x2}, {0x1c}, {0x6, 0x0, 0x0, 0x8001}]})
pwrite(r3, &(0x7f0000000000)="6ba9a481bbd5bc26d716c3ce78c7", 0xe, 0x0)
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f00000003c0)={<r4=>0x0}, &(0x7f0000000400)=0xc)
fcntl$lock(r3, 0x8, &(0x7f0000000440)={0x2, 0x0, 0x6, 0x1ff, r4})
r5 = syz_open_pts()
r6 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
dup2(r5, r6)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4f}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2000, 0x0, 0x42)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000000)=[{0x3}, {0x25}, {0x4006}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@random="de2bf4656ec7", @remote})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x2}, {0x3}, {0x6, 0x0, 0x0, 0x20000000}]})
pwrite(r0, &(0x7f0000000040)="ffaf8a8d1afc9ae96914f6357e3a", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x4}, {0x40}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0xffffffffffffff17, 0x0, @empty, "", @random="c5f1b69ad742", "d984e6e6594e3401a723c00838e77098"}}}})
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x1a, &(0x7f0000000040), 0xc)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$PCIOCREAD(r0, 0xc0187009, &(0x7f00000000c0))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100a56700009eff03000000", 0xc)
getsockopt(r0, 0x0, 0x9, 0x0, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000240)="eaff125c00000000", 0x8)
r1 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
write(r1, &(0x7f00000000c0)='c', 0x1)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000b, &(0x7f0000000000)='\x00', 0x1)
mknod(&(0x7f0000000080)='./file0\x00', 0x1ffb, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0xc02, 0x0)
ioctl$TIOCFLUSH(r0, 0xc0106978, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000080)=[{0x28}, {}, {0x4000006, 0x0, 0x0, 0x80}]})
write(r0, &(0x7f0000001480)="23295428e2fa906bdf4ba040352d", 0x27)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
r1 = dup(r0)
ioctl$FIOASYNC(r1, 0x4004667b, &(0x7f0000000000))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80146951, &(0x7f0000000300))
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="82022ec1b6"], 0x10)
connect$unix(r0, &(0x7f0000000040)=@file={0x0, './file0/file0\x00'}, 0x10)
sendmsg(r0, &(0x7f00000008c0)={&(0x7f00000001c0)=@un=@file={0x6b2f841e542302b3, './file0/file0\x00'}, 0x10, 0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000003c0)={0x3, &(0x7f0000000000)=[{0x28}, {0x44}, {0x6506}]})
syz_emit_ethernet(0x3e, &(0x7f0000000040)=ANY=[])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x1, &(0x7f0000000000)=[{0x0, 0x0, 0x0, 0x6}]})
fcntl$lock(0xffffffffffffffff, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
sysctl$vm(&(0x7f0000000000)={0x4, 0x1}, 0x4, &(0x7f0000000400), 0x0, 0x0, 0xfffffffffffffe93)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x32, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
r1 = dup(r0)
r2 = dup2(r0, r1)
sendmsg$unix(r2, &(0x7f0000000500)={&(0x7f0000000000)=@file={0x0, './file0\x00'}, 0xa, 0x0, 0x0, &(0x7f0000000400)=[@cred={0x20}], 0x20}, 0x0)
nanosleep(&(0x7f0000000040)={0x0, 0x3389e457}, 0x0)
execve(0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x2a, 0x0, 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x80002005, 0x8000000000005200)
r0 = open(&(0x7f00000001c0)='./bus\x00', 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
dup2(r1, r0)
preadv(0xffffffffffffffff, &(0x7f0000000380)=[{&(0x7f00000001c0)=""/6, 0x6}, {&(0x7f0000000200)=""/133, 0x85}, {&(0x7f0000000400)=""/87, 0xc9cf315d65df22e7}, {&(0x7f0000004380)=""/4106, 0x1000}, {&(0x7f0000000340)=""/19, 0x13}, {&(0x7f0000002340)=""/4096, 0x1000}], 0x6, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000080)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
mkdir(&(0x7f0000000240)='./file0\x00', 0x0)
mkdir(&(0x7f0000000140)='./file0/file0\x00', 0x2d53b6584dddbb69)
chdir(&(0x7f0000000280)='./file0/file0\x00')
setuid(0xee01)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f0000000080)='c\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0104453, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x60}, {0x15}, {0x6, 0x0, 0x0, 0xffffffff}]})
write(r0, &(0x7f0000000200)="ddfa874894e428de1f6a8fee203b", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x24}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x7f, &(0x7f0000000100)={@remote, @local, [{[{0x88a8, 0x1, 0x1, 0x1}], {0x8100, 0x1, 0x1, 0x2}}], {@generic={0x0, "04d6d933fc366194a7075fa05c0695e6aea2c2f21eaa29861567a89dc8a53271a67913ecfbb062a84f4614f7aece2cbfdb5319456ca05378730dcf01d997b580f5384d61f052a43c979060c35ba79b22fbdfb1c2c4595e7ff1f99cdc4e0e1fdff9ba3470fa98faa901"}}})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000001200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f00000000c0)={0x0, 0x0, &(0x7f00000011c0)=[{&(0x7f0000000080)="2d178591bb75abb60b239c6f6fd912b78bd36d257cb747acfa3e1793f25f2c207f90141b49e27f685377907fdf5146da4418db2a91"}, {&(0x7f0000000100)="1b185b2d51e38e05234572fcb25b0dc051edad22c7d8d1fb11cea601afb7a70b128f376c0bf6cbe81a0dd0f88714297ec7b05de36eb9b39af8247b2ebd2e9250fcbd9b2858d5b0dd92857a1e155e2158fe93a88638b0fe84bf78434415851b66904d90f32e5ec34c76298141e0e920caa20facb649f61fc184e528a14ceefd69d6c2b47942389d2f3b4fe1b8f5d149ac83f57a23721ba4acbaf6"}, {&(0x7f00000001c0)="ed5615ecae3634848e770822d0298120cfb01f5d2163f374e6d1a012934be7b127f9d84dc82d4fc4e587e94aec2f4c3b5116f13fa97f1d622180abc18d3ed569516eecc26a80c4bb9f3150be3e88098b8f16a99dfd5506ecc4233791dada00758ebfa54382a45a0862777ad19a1d905618481e143c48576583139ee190a37375b57d6d2050daf20993096cbe2bd2f9f8013aa8b3a86152c955cf87d330151983d14f86c9285ac30f7dcb1cbc10cd0e1138fdaae5411ea3b001325be93cc65ea8cd421e61101060f13c0b97dee041348ca09694a20b271396869aac399879ebc002f321118c4c1a508af1c15d97daa530ddba17653d193679c12b673b296f204f859a6c5d33467f4577bfb4005d9ba486aff99558d1a77f61edceb93628266aa5a233a151bbb4c0aeb6b6a732de38e5f066bae37d7ea4b0cf919edebd1cfc234c4c3bf9641323a6fc4b3eb73c35810ed29602f8ff83da3c595f81bfbb62820707d8be339ad58de8763a6f8cc5aa1e1de8c270279dff8e30c5b63d627c5e447b64a0b6a5e3e6163912f5b941b567225ee8206b8991bdc049cc1c4a94402b504fb040ae35e6cb3c54b2f6764f18e74c5efb0339a3fd5aa73a1455580f35983b5e720ad219c2416e6f863f0655a05c3879b9b68e930b0f32d838c2381c6684e2f0eb33aba924d9783209626fb5ba099000ffe981bad16f70bbd30325e9e5d9c698d4c32eb7d40065720c2a9ebeedb547857f71dd98ff06b97f2f44fb7e2d44a2b12aa2168ae2e9c4d5dd253a45e2b86a0754c66cd6b0fe72fdb6ce773b8d5b8084d4edd090a532f8e6e86dc0da2e9ed5f2f596f8bfefdf3695c2a1f8f9add784a0fc28abeea735b85ecbd068a490e5e4a3d6b7f71b54fdc801cf47db811f1091cf45e19bbef0ca4f53154da0884eacab80cc7a91f54f6203785b93a07acff43d8baf560d2df311827ce8fe6c08d2c934dcdec6e94c333cd12818d52d0829f2e7c1edbaa87d25a2b4b09d8cd02aaa52289176bb29e7550b98d3f399dde8e643d0ae2e3724afe4e5023c6c412ff647397ddaf226db5f51852c4188d8ea42e6cf2214d24f6f4ae32c327f389f20e2fab2e8d7d0d16d13f8f3a316733f8588fab314f053c28f3fa91066aabc3adcda5f6e782e51c136b8950afc5b5c78d274dd5ba32a0a47e87e62fd18c388a78fd5d26701b0628e0535cebf6972a814d5fc049dbf25778f0256f2355e7c0d8155f957487c0a50f5bd04bf4fb5194c9a8a9ecdb74f8a450a5949d709be77b786ef0d6057f5823600c5193d947a1ee5b42b5da54bd0f8cc0774f608730900c49a4adcb050020f21b24f0cb1cd4edcdb6abe276b54d03cb49cfa8321c02547628761f2b24710d0845ee2457cb4d27967dbee4d88efce8d322d610cce6b44abc8acdb935008a2b8cf9ac0b56da52fcf59b6d6702298882e1aa05bc22b8ed9df38fe28022a322e1d170bf26da48b6747d490af567a36115be1a3ea305566632a567a9426e5332dcea6e1559b4edce99f6cf456425e415f6462fd6a5b920e3c05becb2e8c7e6e5ca3148b02baedd3b8ebc7ad9175af98ffd6739045da363431759c46d2c2b24b233f965528025dabb58dc00900833ef9faa6ad6680a0ed8424933f86df6d5737bea351b1e00a474a0674452fc17c69e0dbc97006916a4b357b628ca68e59fb7a3dc58ec5c763d53f09cba2bc93f7b334f1fe37cda0f742692c1aad430e4e0d50f8a7f3acb84f81eac2f58f3d098103441755b21f1cd20fcb07e7c607633dfa336f9a07c422b16ed2e4a03e6926aa878d94520fdcc7b4c7ef2118cdc5bfa839577466f504ec8efffdb6f2b5650751f512f41bd8bf891710593b05d3a5ba59a9a3c887170a8a3f3bee3a56c434637b69b5e15c05cb913a80501955e09ceb9d7cdc1c518a3e5dbce1bf50c5dea80e145ba1822ef284d6e5e72052f18ae88f8c289770d0a7e1a43ecdda4ba5b521cfcc76c73c76012594d113c35671c79ce45ff1b6284b67e41e15aaeae0810b11fbbcc9fb62c9eb392f43085f0d8f1cd50594e36b913e58753c5f29b5a7237661a393f7bb9fb555901cb99833bb174df8b6eb40303f41f4f5d16046f88378b438e2db065cda3db14df58f5840627a7b7f4b6ad8f8c64d5677385a678aeaed9bf2640889ca2b3ca27bba2b2478d3a83a8845d6fb8a9db6045ee8798215cbfd828bed2bfd0a932903ef880d4f2545197a1758b99905e37879bb0405cbe2d2a2e0c2f243497730fab5781759ab809903029fc7b3840c6db0f6c680fedfb020720bb70c0e35040a37f1e501884abb9e88c406aa394689bf993ae4f17b704e6727823a3e0ac339f252749111faa53005a21f4947b4ffa395e35857c1e2aefc62c3661edf42a0501a8a49d23fb124e6ef9e4d399d792a269e7654edf41d16c14bd53cae4bb42de34104ce9e4f73945b2b446a492355df1bfa350a7d9379654dc07c204d7bf4e53ad6eca77495bfe56a8bc2ff93a53a12c5002bef92573dd95fb4a7aa3dfbb54ab3c23653e7e18490e3ed77932a4c6082c790b90169de8f6a2e447565ce80f90bec88d584a4552be2ae2f794190dafa7bf6b287d6bb3f0af8111ad55684985bfeb05b9597968e14d1d3ca99a94300ec77b91238bfd729862b8f20d76c29494f41185e7c8cbe7b48ed01c8ed38cb604665aa6bff3bdd73008601ad53eecb589f4c3ba49758c4fc01d4ac533bcc3d93e8ad8bc1095dfed14ce8df87c9d683c5b169e0ff9baeea924133c751435f51bb5b1fbda2a4c8a68e207074accd465b4ef81aac5591fa2ad3db3d68b2d6bdcc3a734c9db4e8e673dc1d4de001e06639159a1c1b92fc5b9f4fa73dbbbf881805ad6dab0d863ed0c68a3fa9c5eb97d4fb506f35758eec651fd5609ce7eed17dafd1df20dbf21aac8f460b39380764d936bd441b217ce64a1d2377c32545916e1cb9c1294a2d7dca55059b236527e4b1236be157563364d5cc4b974a5a8b9654a2ee7054c4fdb10cd0f0f69fbcc0668b0d4c8ab7a3f5b4b479034efefe385a60b10044d9dfcf62db7537fe5eb9fe4a7925f65e51f9d36eefc3ebc226cb1a276b4685f1a42fb31d535dc9c7e4a74f34fdbf81dd60de86768274577ccd82e389689e3749bdd791f6622fe871f2533a5dfb6c60f1602af4938ac92ed8db0efd70d4f1959482d9469075eca6442aa5903510fcd7fdac3ef0287af76d814e52822129adee153db2ffeb531c81fda3a07f25a6f8484ebec698cf101077ae26996438d071747b95d3ebe3d9593cec21e7bc2b7fc864fec362e0c0b8fb02db67647c71adfd8c9c5f16578763de9689c583a166dfe9109efcff36d5f16015ea06af282778565e7ea6578123f62d0925f7ff0235d34f57392416fb5198ee5480d98d335490d86dce51485df61d93096d7c42749e28e1ab0b3162b4530730a11aad8e323fda541f72540381a92a2ecee4d440a071ac03a283a200c44bb3e1729b9aae40f877feacf8967216454eacfa830ced5a4fb3b2ee78621c531eb4dd78d672ba4172285bb2c69ccb292f71fecab4da8962eb3b2c90e2d027719e5472ee7af17de05c72a4e484896e484d274b445661d51c58a4ef5f57f63d4daed5e55eb5c4f4e3de776254670b606a60759e30e723d267ab941f6a8f28eb64e60ead347955d3be79f8ba3bb09c774ac734951950bb6844e2abc397a9b8f212b226b285d344840bba6b8c57bbac823e079b7ca0bfaff246fc22c8c693f33543e742cc8da50ce5d07fe5a42f78141631a7e1dff98db8e0f10d8b4e1c89f0cd2ea813410bcc93c2d3e2bae1a2d4555ee07fa90cccc580d472e463453576c88767567575c6183e60d7f4149a74f647a0b1d11ba4d711575b15cc06c388639b8b26cabf51fbf8814447ba01b3f7c857327e2e93ecdcd6907104bb971fb903bf703a19f35dc0f180f4b1ce32c01e35118345c5fc1e39d22140b2de1bf48145620fe4cbc17ec811d8445f85f7db381d2e8833afdebd575009fe52df4ad754b491fff3f52aa4770cf620433b9c2ab3ed696e7bdad2b4f25e1ee509ce76cfb8e8ee8c9fc5bea11667a33d896e9099d9d45bb7dd65cea8af2974c3a9b2b3b3da158244524e17122bc658132b166e97435c67de181d407eb9ae8dc911504849a3f2daf5d7eaa3e4ba60f135529f0a17eb7c9f47559286aacad0503528b2cccb809ed48c33d2dfbef6d374a8fcea3ac1c4dcce4aab5d837f9abd174096aa446bd3990d43f14abf6f25b19e8bafa6a57492d9acf882445ec9be773bf6e3709cacbf282a0c85865f35068964c3b0de0fb6910d0741196447c141953220665dd1d443c036d6cff9a66eb9d481623929dcc7ec18b779553277bc02d4ff32931237e5549b3982a6e78d5694ca0a4a707a3afd9df080bb2f65f3f5595196590c7e682d0c8414db0dcf4cc12ee3dcff427c725b54415637d9c61103f7783cbf962d5df0f0cf11a865b24b2c5e741e58a22014081be575eca5c7ed48fa4e99bfbec035d9266c47cbc526cb75f80e824b42090b84f9406924bc0b7fbc3476c83518fd63a1c1a36fb1f97b440c7e0f545cf0943f23872d211293cf4e7f7ea6c5baa51eb30154b7111231ed2a1024a7a3e8c07b4f73e3e8af3fead22ab8fcde9190993af4c1fd82a7dba096856995523a0ed89d523e8115870b8b2e922bfa865dd6a71324452d59956e978fda0dda80f509157af746cb8b60e8a7ea9d7e7f1130962e3fed652b88a4de43158372b5841947ed95c62fadaccdb8b039d3692c243b0f050899f3015d60bad423c2b4e6d94647273e3f86621e0859a009ed869450238edd5e70f4a0b7513b029b45d915d0bc9b448d09960d1f5745584470a27d67737037edf868bdace569e2d06c6455f06fdcfa5bebe9c68fdac2a697dbbdd417f48b1ab9d9c46de8cc09ea7bad9623d8b452a4a8963c761d5b4c17c824f436f2d3fe0ea347193809717360aae17946d6b9f1d114b5f75dbb962a2f726ce3bfb270b43909c5f366bdde872a8db7e519ee13e32fa352e03e9dcafc6d27414f39e58762c1a468d3dd55a2bbdd6bed89fe6e2f5ac1e2d4e921fba340cc1a0e75cbf2c08bb45ae6cbdfd9ce5232675ac22f1e9cdf3cd3b3803fd8812622a1de42eaf07afcb789418658d8b8523f106edaf291e7ebee15be2a00089e7a1f1ce4044918faada4f813834798f5d7524677a75b15b25cb8ba255c0c5830a76d795747ae150250ad7c735d3147a1fe2ce59a046d87b2e4ea71eb95593d6814ced5c5c5b6eb497b5b6288f86eda6ae792f35809a0b01636078c142d05c320a6cffb7fa50f5f974a800198870c88c5c1ff63f48268544a751f22ac62ffaef6163b4df3dbc8df9678b206eddc63a4875b51fae765f141feb3be448dbf33d0e29b93aa49ec5be2242be192451484ad4ec478b234bf5f8e4277f600c8f000fe68358a2cea6c2fc428d57874e329a0330dd52ef62b331c6eda810703cdc9b48f18b9255c1f57a0c1ae351a3195b103e6b322bda80b749424ff529a0ac7c29be0e34481d13b686050688eff135aec849677360af92abf843e2210279c3fde0ea8809cc2f0c4f7b88bace19ca62051e4b03674bcf1ec0ccbc75cefe574ac200aff005238004fc628a896b8ec8464ecf3b9f73c379189ac9ffd3270ba9ae9eb1676340af1807aed5ac43ff4947881b4e457fe224215d49575cbfadcd88998a6a764cd1e056f0671e9dcc58263178b690fe418d8e6a7b248cc37bc90d39df0b36c3d672ef32cb8bb7ce5cd186c9708faa800d9cd174041"}], 0x14}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000140)=[{0x4}, {0x14}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)={@random="eb0420e10a89", @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @rand_addr}, @icmp=@timestamp={0x11}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x21, &(0x7f0000000040)="b1f5d915", 0x4)
dup2(r1, r0)
recvfrom$unix(r0, &(0x7f0000000100)=""/60, 0x3c, 0x2, 0x0, 0x0)
socket(0x10, 0x5, 0x5)
setsockopt$sock_int(r1, 0xffff, 0x800, &(0x7f0000000080)=0x2, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r2 = socket(0x2, 0x8002, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
write(r2, 0x0, 0x0)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0xfffffffa, 0x4)
getsockname$inet(r0, 0x0, &(0x7f00000000c0))
mkdirat(0xffffffffffffff9c, &(0x7f0000000200)='./file1\x00', 0x0)
unveil(&(0x7f00000000c0)='./file1/file0\x00', &(0x7f0000000180)='c\x00')
unveil(&(0x7f0000000080)='.\x00', &(0x7f0000000040)='r\x00')
symlink(&(0x7f00000013c0)='./file1/file0\x00', &(0x7f0000001400)='./file1\x00')
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000040)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000d91000/0x4000)=nil, &(0x7f0000fed000/0x11000)=nil}, {&(0x7f00007ed000/0x2000)=nil, &(0x7f0000440000/0x2000)=nil}, {&(0x7f00004e6000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000fee000/0x12000)=nil, &(0x7f0000cf9000/0x2000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f00007ea000/0x2000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f0000ffc000/0x2000)=nil}, {&(0x7f0000762000/0x2000)=nil, &(0x7f0000ffb000/0x2000)=nil}, {&(0x7f0000ffb000/0x3000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffb000/0x2000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000ff2000/0x2000)=nil, &(0x7f0000fb6000/0x4000)=nil}, {&(0x7f0000aec000/0x4000)=nil, &(0x7f0000000000/0x400000)=nil}, {&(0x7f0000c0e000/0x3000)=nil, &(0x7f0000693000/0x3000)=nil}, {&(0x7f0000e4c000/0x4000)=nil, &(0x7f0000dad000/0x4000)=nil}, {&(0x7f0000df3000/0x1000)=nil, &(0x7f00006e7000/0x3000)=nil}, {&(0x7f0000ffa000/0x4000)=nil, &(0x7f00001d9000/0x2000)=nil}, {&(0x7f0000ffc000/0x4000)=nil, &(0x7f0000e13000/0x1000)=nil}], ['./file1/file0\x00', './file0\x00', './file\x00', './file\x00'], './file1/file0\x00', './file0\x00', './file0\x00', ['./file', './file', './file', './file']})
writev(0xffffffffffffffff, &(0x7f0000000640)=[{&(0x7f0000000140)='#', 0x1}], 0x1)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000705", 0xe, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x1f)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000180)={0x2, &(0x7f0000000080)=[{}, {0x2, 0x0, 0x0, 0x12}]})
sysctl$hw(&(0x7f0000000080)={0xa}, 0x8, 0x0, 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0x401)
r1 = getuid()
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000340)={0x0, 0x0, &(0x7f0000000300)=[{&(0x7f00000006c0)="8d666511c62f28347f41ea3064e25d72fb81ac52f77c9e199eeb3ccbd8abc2055270aa1da62acfe013390ffc47db783f8f344cf2a351b8b8ab3b09d74823da2bb23ae1e05c2ffca2d27611b3a5826e954a1ab93a8f5c0a77c7bed2a146eb666baa963fab8b5a2c29d543ea81780ba6459bca4d45c77519b8b34b48533a6abdf024ff5fbcfb308348f62fc5afb6fb1390624e2db5dc228226b42c3c72e0c88859a72fd132477e7f11f9290de337a61ffb798ce07f406ed8", 0xb7}, {&(0x7f00000001c0)="812c1357940a17c27fe5e156e0e52b7314dd45fb68d9fc99014199da1cebdf3b54c9c0e427ceb9ebac610d6d1228df22f33a21e8a42aced305f527f4ad9a66126180252e1b1d927d48e631ab6647e4628a7ba3e8e928f1d2b0c692ba09ae044c0452955117d8a68a2d1909a6c775a1e76e2c3211c6", 0x75}, {&(0x7f00000004c0)="558c7fd0583ae616fdb670488c575f5ded7e29f9cfb88364ad3acb166fd3fa73e8bbba97c2ae8a90e3ae3ae427116ed4e8e86953279a6b4ca464312efc0a55bb1de8c1e7a9e8e5f4384feb0773904195ac93e39a100d6d768370f0932d17994b55ae5b5ca9e581a5c3d1c5d3070ecbe4add0f5a3f85162f09284bd3e407303e6d8db4ac6162a6877866c43dfaa2803e109438950afcbeafc609f3b99744178c6b355d5a6affc943c79366873ed0196043559f637f67d82d8aeb91646412d71aa94095e7c7d1daffdac05713a2d93af06de1681c99a11421dd53fc0f2fe55fdbcfbc0cdbb980b", 0xe6}], 0x3, &(0x7f0000000640)=ANY=[@ANYBLOB="2000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRES32=0x0, @ANYBLOB="000000002000000040000000ffff000000000000", @ANYRES32=0x0, @ANYRES32=r1, @ANYRES32=r0, @ANYBLOB="000000001800000000000000ffff000001000000", @ANYRESOCT=r1, @ANYBLOB='\x00\x00\x00\x00'], 0x58, 0x402}, 0xa)
msgctl$IPC_STAT(r0, 0x2, &(0x7f0000000240)=""/77)
msgsnd(r0, &(0x7f0000000000)={0x2, "11ae4e56af7821"}, 0xf, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r2, 0xc088444f, &(0x7f0000000140))
sendto$unix(r2, &(0x7f00000000c0)="b100050300000000000011090301000000000000cea1fea7fef95ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5fc829297ba1ab5b23116730f144000000000000001f1306000000a91b00007d026ba8af63ff37282902", 0x62, 0xa, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r3 = socket(0x11, 0x3, 0x0)
sendto$unix(r3, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xd0584fcd7f7abe72)
r0 = socket$inet6(0x18, 0x1, 0x0)
poll(&(0x7f0000000080)=[{r0, 0xd00e5c9a463359bb}], 0x1, 0xff)
kevent(0xffffffffffffff9c, &(0x7f0000000000)=[{{0xffffffffffffff9c}}, {{0xffffffffffffff9c}}, {{r0}, 0xffffffffffffffff, 0xb1}], 0x0, 0x0, 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001940)={0x0, 0x0, &(0x7f00000017c0)=[{&(0x7f0000000280)="4023689fc5df58df1723d83069b35deba0b8f4a2fa2e08c3f15f28eb9af1d8b00a345475874134b4d5cec286551d54fb", 0x30}, {&(0x7f0000000200)="a62c923b95fc47b33a77260de950a51fc9dadcb8889f6f8af4", 0x19}], 0x2}, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0xfffb, &(0x7f00000002c0), 0x80000002, 0x0)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
mquery(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x2, 0x0, 0xffffffffffffffff, 0xc00)
r3 = getuid()
setreuid(0xee00, r3)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={<r4=>0x0, <r5=>0x0, <r6=>0x0}, 0xc)
setreuid(r3, r5)
r7 = socket(0x18, 0x1, 0x0)
setsockopt(r7, 0x29, 0x32, 0x0, 0x0)
r8 = open(&(0x7f0000000100)='./file0\x00', 0x10, 0xc9)
r9 = socket(0x18, 0x2, 0x0)
r10 = socket(0x20, 0x1, 0xff)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000740)={&(0x7f0000000800)=ANY=[], 0xa, &(0x7f00000000c0)=[{&(0x7f0000000500)="851a4c02ec276d92a82db9f4b6272312fad04b325f81f3ac031f3c5312e5acf292b07a6e34456594bbed8266a32dd8d91d49381a4824f7a31e48a231427f43c2cda68c1231963c650dccd46f63bad9bbf15bacbfc39661a8fbfedbfa4dacc188cd9346c0dc9731b8d75d2c9404654ccb5b01850c424b38f47e20342feb2c29fe4e7f7704c1528de4590daf2a8313d534a42642ae4458741418bf9c39bdc83982ea", 0xa1}, {&(0x7f00000002c0)="c992bf68850d464af619ab30293a6f70618244c91e77bcd6a7462e720b0445b41111a7820fe12ac28b35ad", 0x2b}], 0x2, &(0x7f0000000c80)=ANY=[@ANYBLOB="2800000000000000ffff000001000000", @ANYRES64=r3, @ANYRES32=r6, @ANYRES32=r9, @ANYRES64, @ANYRES32=r5, @ANYRESOCT, @ANYRES8=r10, @ANYRES32=r9, @ANYRESHEX=r8, @ANYRESDEC, @ANYRES32=r9, @ANYRES32=r9, @ANYRESHEX=r4, @ANYRES16=r4, @ANYRES32=r9, @ANYRES16=r7, @ANYRES32=r9, @ANYRES16, @ANYRES32=r9, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRES32=0x0, @ANYRES32, @ANYRESHEX, @ANYBLOB="000000002000000000000000ffff000000000000", @ANYRESHEX, @ANYRES8, @ANYRES32=0x0, @ANYBLOB="000000001800000000000000ffff000001000000c1db8ea36a005f157c3e136b4dc5826ddb746a508d7d891476582b6857621fae560e51eeb96d43c3ce8426515d7c34788dca9e146d80579781ab2bcb87bb4803613853080000000000000070c9e8c14e05eaf698dae7bc8e973b5f8eef931116250299d3ab377e690512249f1f4a8c39cbdae9af7ab31b864a35ad498d7acba771f351413767a5a8b476a9e83b3e", @ANYRES32, @ANYRESHEX=r9, @ANYRES32, @ANYRESOCT, @ANYRESDEC, @ANYRES32, @ANYRES32=0x0, @ANYRESDEC=0x0, @ANYRESOCT=0x0, @ANYRES32=0xffffffffffffff9c, @ANYBLOB='\x00\x00\x00\x00'], 0x128}, 0x8)
fcntl$setown(0xffffffffffffffff, 0x6, r4)
ioctl$FIOASYNC(r2, 0xcd604404, &(0x7f0000000240))
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
setgroups(0x0, 0x0)
chmod(&(0x7f00000000c0)='./file0\x00', 0x10f)
chmod(&(0x7f0000000300)='./file0/file0\x00', 0x51)
setreuid(0xee00, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x280002006, 0x2065d)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x8004745f, &(0x7f0000000040)=0x3)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000380))
setregid(0x0, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000080)={<r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000200)={0x0, 0x0, <r2=>0x0}, &(0x7f0000000240)=0xc)
lchown(&(0x7f00000001c0)='./file0/file0\x00', 0x0, r2)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000080)={0x3})
ioctl$TIOCFLUSH(0xffffffffffffffff, 0x80047410, &(0x7f0000000100)=0x9)
r3 = getuid()
seteuid(r3)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0xd4)
rename(&(0x7f00000004c0)='./file0/file0\x00', &(0x7f0000000500)='./file0/file1\x00')
r0 = kqueue()
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000000c0)=0x10001)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000040)=[{0xc}, {0x1c}, {0x6, 0x0, 0x0, 0xffff}]})
write(r0, &(0x7f0000000000)="331f3e528ed19e36d91517231d32", 0xe)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f0000000240)="d1201d9c07b00a16a86b937d24a7f2947b", 0x11, 0x0, 0x0, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fa090000001e328a1811", 0xfffffffffffffcb5}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = syz_open_pts()
poll(&(0x7f0000000140)=[{r0, 0x4}, {r0, 0x4}], 0x2, 0x0)
syz_open_pts()
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000080), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f0000000180)=[{0x2}, {0x54}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x2a, &(0x7f00000000c0)={@random="f3ccfac6c1d7", @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @broadcast, @remote={0xac, 0x14, 0x0}, @broadcast, @loopback}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
r1 = semget(0x1, 0x4, 0x0)
semctl$GETZCNT(r1, 0x4, 0x7, 0x0)
r2 = geteuid()
r3 = semget$private(0x0, 0x4000000009, 0x82)
semop(r3, &(0x7f0000000140)=[{0x0, 0xfab3, 0x800}, {0x0, 0x4, 0x1400}, {0x3, 0x7, 0x627462befbd9eef4}, {0x0, 0xfff9, 0x1000}, {0x0, 0x4, 0x1800}], 0x5)
semctl$GETZCNT(r3, 0x2, 0x7, &(0x7f0000000600)=""/70)
semop(r3, &(0x7f00000002c0)=[{0x2, 0x3}, {0x0, 0x6, 0x800}, {0x4, 0x200, 0x800}, {0x1, 0x8, 0x1000}, {0x1, 0x5}], 0x5)
semop(r3, &(0x7f00000005c0)=[{0x4, 0x2}, {0x2, 0x6, 0x800}, {0x4, 0x8783, 0x1000}, {0x2, 0x400, 0x800}, {0x1, 0x0, 0x1800}, {0x0, 0xfffc}], 0x6)
r4 = geteuid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r5=>0xffffffffffffffff})
getsockopt$sock_cred(r5, 0xffff, 0x1022, &(0x7f0000000240)={0x0, <r6=>0x0, <r7=>0x0}, &(0x7f0000000700)=0xc)
semctl$GETPID(r3, 0x0, 0x4, &(0x7f0000000500)=""/113)
setregid(0x0, r7)
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000080)={{0xfffffffb, r4, 0xffffffffffffffff, r4, r7, 0x40, 0x2}, 0xb02, 0x9, 0x8001})
semctl$IPC_SET(r3, 0x0, 0x1, &(0x7f0000000680)={{0x100, r6, r7, r6, 0x0, 0x1, 0x8000}, 0x3, 0x8000000000000005, 0xdb9})
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000040)={{0x0, r2, 0xffffffffffffffff, 0x0, r7, 0x90, 0x5f73}, 0x20, 0x1, 0x7})
ioctl$BIOCFLUSH(r0, 0x20004268)
openat$pci(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
select(0x0, 0x0, 0x0, 0x0, 0x0)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
sendmsg$unix(r0, &(0x7f0000002600)={0x0, 0x0, 0x0}, 0x0)
shutdown(r1, 0x0)
sendmsg$unix(r0, &(0x7f0000000000)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002004, 0x8005bcd)
r0 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x1}], 0x1, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
dup2(r1, r0)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x6b2)
r0 = open(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ktrace(&(0x7f0000000240)='./file0\x00', 0x0, 0x1506, 0x0)
open(&(0x7f0000000080)='./bus\x00', 0x400, 0x80)
ioctl$TIOCSTSTAMP(r0, 0x8008745a, &(0x7f0000000000)={0x2df4, 0x200})
open(&(0x7f00000000c0)='./bus\x00', 0x10000, 0x186)
r1 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r1, 0x29, 0x2b, 0x0, 0x0)
pread(r1, &(0x7f0000000140)="3fc60d44caed9aa17f36e38d9435252a1ffa0c559d5946fab745f710c23cb2bab50bf1ac0c974276f42d062b5e0529c124816fffda27bef5a1995a6e31a483b17d16c0df41245aef39d595", 0x4b, 0x7)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x310, 0x0)
r1 = getpgid(0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x12, r1)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1112, 0xffffffffffffffff)
clock_getres(0x0, &(0x7f0000000040))
r2 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x408c1, 0x0)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x4)
openat(r0, &(0x7f0000000140)='./file0\x00', 0x10000, 0x110)
r3 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$BIOCSETIF(r3, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f0000000200))
ioctl$BIOCSETWF(r3, 0x80104277, &(0x7f0000000040)={0x4, &(0x7f00000001c0)=[{0x1}, {0x1d}, {0x2, 0x0, 0x0, 0x1}, {0x6, 0x1f, 0xe8, 0x3ff}]})
pwrite(r3, &(0x7f0000000140)="6ba9a481bbd5bc26d716c3ce78c7", 0xe, 0x0)
r4 = fcntl$dupfd(r2, 0x0, r3)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000180)=[{0x20}, {0x1d}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(0xffffffffffffffff, &(0x7f0000000140)="6ba9a481bbd5bc26d716c3ce78c7", 0xe, 0x0)
r5 = dup(r4)
kevent(0xffffffffffffffff, &(0x7f0000000300)=[{{}, 0xfffffffffffffffb, 0x88, 0x4, 0x35733664, 0x1}, {{}, 0xfffffffffffffffb, 0x0, 0x40000000, 0x1, 0x1}, {{}, 0xfffffffffffffffd, 0x40, 0x20000000, 0x6}, {{r5}, 0xfffffffffffffff9, 0x2, 0xfffff, 0x3, 0x1000}, {{r3}, 0xfffffffffffffffc, 0x1, 0x4, 0x1, 0x71}], 0x5f0, &(0x7f00000003c0)=[{{r4}, 0xfffffffffffffffe, 0x8, 0x40, 0x0, 0xffff}], 0x3, &(0x7f0000000400)={0x8})
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x6}, 0x3, &(0x7f00000000c0), 0x0, 0x0, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x3}, 0xc)
setsockopt$inet6_MRT6_ADD_MFC(r1, 0x29, 0x68, &(0x7f00000000c0), 0x5c)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x69, &(0x7f00000000c0)={{}, {0x18, 0x1}}, 0xe5)
openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
r0 = socket(0x2, 0x3, 0x2)
setsockopt(r0, 0x0, 0x64, &(0x7f0000000040)="01000000", 0x4)
setsockopt(r0, 0x0, 0x69, &(0x7f00000000c0)="4fc60cd0ee2e0013dad36558a3272ebed0f9db18617c9c00d950140388a07254c460357f1d763a00000180ff", 0x2c)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x0)
unveil(&(0x7f0000000000)='./file1\x00', &(0x7f0000000080)='c\x00')
unveil(&(0x7f00000001c0)='./file0\x00', &(0x7f0000000200)='x\x00')
open$dir(&(0x7f00000003c0)='./file0\x00', 0x200, 0x0)
minherit(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0xe6318ffa5355eb75)
r0 = socket(0x2, 0x2, 0x0)
connect$unix(r0, &(0x7f0000000140)=ANY=[@ANYBLOB="6c023f2fac1400ff"], 0x10)
writev(r0, &(0x7f0000001440)=[{0x0}], 0x1)
r0 = socket(0x18, 0x1, 0x0)
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000001800)={0x0, 0x0, &(0x7f0000001680)=[{&(0x7f0000000080)="35595dbc5f5ab473bbaafef640a3df1e594dc154bd336f0dd1de23484ae8b3aa372d5f2ef9ed2057e7676f9bbc893272d7517d21116d472cad733aa33b86d0b60266", 0x42}], 0x1, 0x0, 0xb8}, 0x0)
setsockopt(r0, 0x29, 0x36, &(0x7f00000000c0), 0x4)
r0 = socket$inet(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0xc, &(0x7f0000000040), 0x0)
open$dir(0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [0x4, 0x1008, 0x7, 0x1000000000, 0x0, 0x0, 0x0, 0x0, 0x4], [0xffffffffffffffff, 0xfffffffffffffffd, 0x6, 0x2, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002004, 0x0, 0x8, 0x46c, 0x6], [{0x4}, {}, {}, {}, {}, {0x0, 0x400, 0x20000000}, {0x6, 0x0, 0x0, 0x2}, {0x4, 0xf9, 0xe59}]}}})
ioctl$FIOASYNC(r2, 0xcd60441a, &(0x7f0000000240)=0x2)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc1206922, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080)={0x2, &(0x7f0000000000)=[{}, {0x3}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f00000000c0)="7343c9f13a1a65a5dc41e7000055b9e7c7e61e80a00111703c8ad427b38b2f8aa720381c97827991a34f07000000aa8428b796be7c3b0dec5cf124fe01853cbb22f62d6fc07aeaae1028cd4c8391f916efee16d809a6a0b0b5f077d55f10bf208af9f63d4fed291214c315c5459032ba78cf06e3e6dff86da9ac815ad539c221d0d8894615ff18fc678944d0f65ab486924ff505c7690ded14f9ca833b67bfd24a41b9c62e63182ec537052a6472066ce21532deaac00c6ee5dd8cca0bd9c132e66331f794", 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt$sock_int(r0, 0x29, 0x4, &(0x7f0000000040)=0xfffffffc, 0x4)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x800009, 0x0, 0x400003, 0x0)
mquery(&(0x7f0000ffc000/0x3000)=nil, 0x3000, 0x0, 0x0, 0xffffffffffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x10081, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000001c0)=[{0x81}, {0x1d}, {0x4000006, 0x0, 0x0, 0x200}]})
pwrite(r0, &(0x7f0000000140)="db01f74e11a4ff07c6000800abe3", 0xe, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x5, &(0x7f0000000080)=[{0x3bf, 0x81, 0x81}, {0x1ff, 0x5, 0x0, 0x4}, {0x6, 0x3f, 0xd, 0xfffffeff}, {0x0, 0x7, 0x55, 0x2}, {0x8, 0xf9, 0xfd, 0x6ec7}]})
r1 = socket$inet(0x2, 0x3, 0x0)
ioctl$BIOCSETF(0xffffffffffffff9c, 0x80104267, &(0x7f0000000240)={0x3, &(0x7f0000000200)=[{0x8000, 0x7, 0xbc, 0x7fffffff}, {0x0, 0x8, 0x5, 0xfffffffe}, {0x5, 0x2, 0x9, 0x9}]})
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
r3 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r2, 0x80104267, &(0x7f0000000280)={0x4, &(0x7f0000000180)=[{0x787d, 0x0, 0x8, 0x9}, {0x401, 0x40, 0x35, 0x400}, {0x9, 0x1f, 0x2, 0x8}, {0x2, 0xf0, 0x9, 0x80000001}]})
ioctl$TIOCFLUSH(r3, 0x80047410, &(0x7f0000000040)=0x20)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0xa, r4)
ioctl$TIOCFLUSH(r5, 0x81206919, &(0x7f0000000300))
socketpair(0x18, 0x3, 0x0, 0x0)
open(&(0x7f0000000780)='./file1\x00', 0x200, 0x0)
ktrace(&(0x7f0000000040)='./file1\x00', 0x4, 0xc, 0x0)
ktrace(&(0x7f0000000000)='./file1\x00', 0x4, 0x40000008, 0xffffffffffffffff)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000580)={'tap', 0x0})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000140)="2d80740490377706ed82a1bba52bb74afafd86958f27c50a5e25b9095e846e0fddc135ada439bedced5e452823ab37661a08b9f504000000926d41943a961bccd9879c98b10718fb2b000000006e1b58944dd16c2df783384b2988bdcaf4d4c25c08fd705875a7a57fc5d21e9f91748a121189c10ae64fbb421e53fc740ba0434504af2a3149018de4f1eea8b1d882ddc206d3280c6552da69573cea2f8978acd0fca90e5dcc44846684d050aa9d3e7ad51949cb8a0611509aff269b9ee4ee78cac32cd8e3ed2a9436", 0xc9}, {&(0x7f0000000240)="11c1bbbeb3", 0x5}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x1, 0x10, r1, 0x0)
r2 = dup(r0)
r3 = dup2(r2, r2)
ioctl$BIOCGDLTLIST(r3, 0xc010427b, &(0x7f00000000c0)={0x0, 0x0})
r0 = socket$inet(0x2, 0x3, 0x0)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x1a, 0x98})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCFLUSH(r0, 0x80047470, &(0x7f0000000000)=0x1)
poll(&(0x7f0000000040)=[{r0, 0x1d5}], 0x1, 0x0)
madvise(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x5)
sysctl$kern(&(0x7f0000000000)={0x1, 0x54}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x4)
r0 = syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
socket(0x18, 0x1, 0x0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000000)=0x8)
dup2(r0, r1)
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f00000000c0)=0x8)
execve(0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
socket(0x800000018, 0x3, 0x102)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETENCODING(r0, 0x80045710, &(0x7f0000000000)=0x1000)
r0 = syz_open_pts()
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000500)={0x0, 0x0, 0xe1be, 0x0, "b06d118c24f6f979cbd500d96cc0a5f09d638b0a"})
syz_open_pts()
ioctl$TIOCSTAT(r0, 0x20007465, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000000)='./file1\x00', 0x2000, 0x201)
openat(0xffffffffffffff9c, &(0x7f0000000740)='./file1\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x7}, {0x6c}, {0x6, 0x0, 0x0, 0x80000087}]})
write(r0, &(0x7f0000000080)="7c0000ffffffff00001a00a5f3fc", 0xe)
setregid(0xffffffffffffffff, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x15, &(0x7f0000000000)="aad485c4", 0x4)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x0, 0x401, 0x7fffff81, "09230600fc4c399f3bd6c57fbb388e304d00"})
write(r0, &(0x7f0000000280)="4de3a767cb3572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cc3392c70fb4d2ea468e980877377f1cfb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb", 0x70)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_timeval(r0, 0xffff, 0x1006, &(0x7f0000000080)={0x0, 0xffffffffffffffff}, 0x10)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000180))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
poll(&(0x7f0000000200)=[{r0, 0x1}], 0x1, 0x0)
syz_emit_ethernet(0x17a, &(0x7f0000000400)=ANY=[@ANYBLOB="aaaaaaaaaabbaaaaaaaaaabb08004500016c00670000000090787f0000017f00000160038cca6a1d11d39bad6e829932447e1411c1c6204e34fd0549be6ef80f7ddb22d247f28b332fe146cd14f8558734c9f4c7df3211abb30c1352ed44e2dac5dcb453dd7f64611aa9c9cb44495b6aff472d1bb00cd636686d4feadcdb7438d7d5a5e74cb78f198b268c400e4119241f7ff2ad8157e423b5ea6414a09994eaee8f0ab576b6e8a3abe3b53eda2a4eefd1cbf1e7979f9b2c42003844c209877d575c302df033558e08ab4003882b0a832615114620abce4648fdc1ee8dd95f7f45d7dd4e6c6face9e1fb9ec0443308980ec097c3e20eb7796553811f3456e98c42658d01a69e05a553a2c4f1cca2c758a1b903000000b578503307582a9226d23c1b8375a818ee428d8fd6b05e36e1d65f57c78b31dec1a7dff4156ed33dc9f225be2c821216c6fc8b99c08312b38368f7ddda9f4cf3000000000000000000000000000000c27140eecdb3c21e811526b0d5b5dc1ef30274fd94"])
ioctl$BIOCFLUSH(r0, 0x20004268)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000240)=[{}, {0x4d}, {0x6, 0x0, 0x0, 0xfffffffd}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
open$dir(&(0x7f0000000080)='./file1\x00', 0x200, 0x0)
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000140)='r\x00')
unveil(&(0x7f0000000100)='./file1\x00', &(0x7f0000000180)='r\x00')
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f0000000400)="b1000504000000000000080001010000000000e2cda1fea7fef96ecfc73fd3357ae26caa0416fa39376336acf00b7804be781eaa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1aaa872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0286988, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x1d}, {0x4}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x5, "000000b3060000000000000000ddff00"})
r0 = socket(0x800000018, 0x2, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x9ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
r1 = socket(0x18, 0x1, 0x0)
r2 = dup2(r0, r1)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendmsg(r2, &(0x7f0000002600)={0x0, 0x0, 0x0, 0x0, &(0x7f0000002500)=[{0x10}], 0x10}, 0x0)
sysctl$net_inet_ip(&(0x7f0000001140)={0x4, 0x2, 0x0, 0xf}, 0x4, 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
recvmsg(r1, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/7, 0x3}, 0x0)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040))
sendmsg(r0, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000080)="eaef125c000000e3", 0xc)
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x800)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000300)=[{&(0x7f0000000040)=""/216, 0xd8}], 0x1)
semctl$IPC_SET(0x0, 0x0, 0x1, &(0x7f00000002c0))
execve(0x0, 0x0, 0x0)
kevent(0xffffffffffffff9c, 0x0, 0x0, &(0x7f0000000a00)=[{}, {}, {}, {}, {{}, 0x66d7894d57c55563}, {{}, 0x0, 0x0, 0x0, 0x4}], 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000009c0)={0x0, 0x0, &(0x7f00000007c0)=[{&(0x7f0000000600)="20e013a962967ffc3f69bc4043f0cb0f4438b0c208964226e2bdf9365942f2b3b23f4708a79685aced359be6aafb7afa7acdcebef9fd23f9802c09be661fb49d92b8d333bb61f65721ffd96cf7fe4a8a77d53ace77c05f2c6af360986dc2c6cc1307d8ce9f13327e6b78fb735e4a2559166a86b6b5fadd707e5139a1ccafd404dd722e181b3c2dcf6076afb0", 0x8c}], 0x1, 0x0, 0x88}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r0, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000a, &(0x7f0000000000)="ea00005c00000000", 0x1)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000040)="eaff125d00000000", 0x8)
clock_getres(0x2, 0xffffffffffffffff)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)="830446d8aaf71f9255568b4e", 0xc)
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xffffffffffffff85}], 0x1, 0x0, 0x58}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0xffffffffffffffff}, 0x0, 0xfffffffdffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff})
getgroups(0x7, &(0x7f0000000080)=[0x0, <r1=>0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff])
getgroups(0x2, &(0x7f0000000280)=[0x0, r1])
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
setreuid(0xee00, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x24a, 0x80)
fchmodat(0xffffffffffffffff, &(0x7f0000000280)='./file1\x00', 0x68, 0x6)
mkdirat(r0, &(0x7f00000001c0)='./file0\x00', 0x2d0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r1=>0xffffffffffffffff})
getsockopt$sock_cred(r1, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r2=>0x0, <r3=>0x0}, &(0x7f0000000200)=0xc)
fchown(r0, 0x0, r3)
faccessat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000640)={0xffffffffffffffff, <r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r5=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r5)
r6 = semget$private(0x0, 0x4, 0x18b)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r7=>0x0}, 0xc)
fchownat(0xffffffffffffffff, &(0x7f00000002c0)='./file2\x00', r2, r7, 0x0)
r8 = socket(0x1, 0x5, 0x46)
r9 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
close(r9)
connect$unix(r9, &(0x7f0000000500)=ANY=[@ANYRES64=r8], 0x10)
semctl$IPC_SET(r6, 0x0, 0x1, &(0x7f0000000480)={{0x20000008, 0x0, 0x0, 0x0, 0x0, 0x100010008, 0x207}, 0x6, 0x7, 0x97e})
semctl$GETALL(r6, 0x0, 0x6, &(0x7f0000000040)=""/49)
sysctl$hw(&(0x7f0000000180)={0x6, 0x18}, 0x2, 0x0, 0x0, &(0x7f0000000300), 0x0)
mknod(&(0x7f0000000140)='./bus\x00', 0x2000, 0x4086331)
r0 = open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
ioctl$FIONREAD(r0, 0x4004667f, &(0x7f0000000000))
r0 = socket(0x2, 0xc003, 0x0)
connect$unix(r0, &(0x7f00000000c0)=ANY=[@ANYBLOB="9a02"], 0x10)
r1 = dup2(r0, r0)
setsockopt(r1, 0x0, 0x2, &(0x7f0000000000)="62c5ef11", 0x4)
r2 = dup2(r0, r1)
write(r2, 0x0, 0x0)
mkdirat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x0)
getgroups(0x7, &(0x7f00000000c0)=[0x0, 0x0, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0])
setegid(r0)
setgroups(0x0, 0x0)
chmod(&(0x7f0000000340)='./file0\x00', 0x107)
mkdir(&(0x7f0000000140)='./file0/file1\x00', 0x0)
chmod(&(0x7f0000000300)='./file0/file1\x00', 0xd7)
setuid(0xee01)
mkdir(&(0x7f0000000240)='./file0/file0\x00', 0x1fa)
rename(&(0x7f00000002c0)='./file0/file1\x00', &(0x7f0000000100)='./file0/file0\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000200)={0x3, &(0x7f0000000000)=[{0x24}, {0x60}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000240)={@random="cd6e92506a29", @local})
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000005c0), 0x0, 0x0)
ioctl$VMM_IOC_RUN(r0, 0x80047476, &(0x7f0000000880)={0x0, 0x0, 0x0, 0x0, 0x0})
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050460000000000008000705", 0xe, 0x0, 0x0, 0x0)
open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050460000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01be657aea8c500000002000000000000020208a371a3f8000400000000000000010000000000000000", 0xb1, 0x0, 0x0, 0x1f)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000040)=[{0x25}, {0x34, 0x0, 0x0, 0x40000000}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000080)="6ba9a481bbd5bc559616c3ce78c7", 0xe, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x8000800080002002, 0x8005bcd)
r0 = open(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000080)=[{r0, 0x8c}], 0x1, 0x0)
poll(&(0x7f0000000040)=[{r0, 0x40}], 0x1, 0x0)
poll(&(0x7f0000000240)=[{}], 0x2, 0x7863bddf)
execve(0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, <r1=>0x0}, &(0x7f00000000c0)=0xffffffffffffff3d)
setegid(r1)
setgroups(0x0, 0x0)
setreuid(0xee00, 0x0)
r2 = open$dir(&(0x7f0000000180)='.\x00', 0x0, 0x0)
r3 = getuid()
fchown(r2, r3, 0x0)
setreuid(0xee00, r3)
open(&(0x7f0000000080)='./file0\x00', 0x200, 0x6527e45bed4cfee6)
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x80206999, &(0x7f0000000300))
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = openat$klog(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
r2 = accept(0xffffffffffffff9c, &(0x7f0000000140)=@un=@abs, &(0x7f0000000180)=0x8)
fcntl$dupfd(r1, 0x0, r2)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f00000000c0)={0x0, 0x0, <r3=>0x0}, &(0x7f0000000100)=0xc)
fchown(r1, 0x0, r3)
r4 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r4, 0x80047476, &(0x7f0000000040))
setgid(0xffffffffffffffff)
sendmsg$unix(r2, &(0x7f0000000440)={&(0x7f00000001c0)=@abs={0x1, 0x0, 0x0}, 0x8, &(0x7f0000000400)=[{&(0x7f0000000200)}, {&(0x7f0000000240)="26c0bd8a465d8b55dd7cbdeb50ae40ed52827ea4b49de3bf28bbea97bd5eb4dbed42d20331c9a66235a25b134aa06c93e8737cb2b2529c754464aad951a20ddc1ec44f85c61f89ea86bc405826df001673046518c3d9b760a89e6842e99d678993392005816e24132789212bbcf168686eb8060f9eca6a1a610482ff244c56068ff27c9b9c977c3c8790d225c398c23397f300843729dc6a483a37d77aad49780b41187849582a4176e78f1070884e50fe61a2087a14f87d6461b0335c39845255d8ca0d1cdf57da429a8d45fe49d06bd62fa8620c3ba4bc1331bdd17a08627aaff9d2e81c0011c14144fac5f9d4e635f2f3bd462f5248c56fd4ef", 0xfb}, {&(0x7f0000000340)="9396393f29a9ce126c9376a76faffd2b614c9fbe634f52137b6be4e7fca15fcbc15b3f4adbdeab941a51c413bf44e75d1a38df2b11fd1a3915175ca3d1ac5dd10bf6b01ade37ec10a9e6eb92b198287d795e3997f6d77f20b0be14114408b692d312dfd7f5da3296e47f6658bb1682db5ad80c2bec1d", 0x76}], 0x3, 0x0, 0x0, 0x5}, 0x40d)
r5 = fcntl$getown(r4, 0x5)
ktrace(0x0, 0x1, 0x1510, r5)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000080)=[{&(0x7f0000000140)="3a8fdf4b0fb8d9b06eaf7696d2", 0xd}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f0000000000))
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x8, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(&(0x7f0000000100)='./file0\x00', 0x0, 0x1510, r2)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x800000000000005})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x6, &(0x7f0000000040), 0x0, 0x0, 0x0)
clock_getres(0x8d1c0b5b29c1b194, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f00000000c0)=[{}], 0x7, 0x0, 0xffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r1)
getsockopt$SO_PEERCRED(r0, 0xffff, 0x1022, &(0x7f00000002c0), 0xc)
socket$inet(0x2, 0x4, 0x5)
openat$klog(0xffffffffffffff9c, &(0x7f0000001340), 0x200, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r2)
r3 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x146, r3)
sendmsg$unix(0xffffffffffffffff, 0x0, 0x0)
r4 = dup(r0)
ioctl$TIOCFLUSH(r4, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r5 = socket(0x18, 0x2, 0x0)
r6 = fcntl$dupfd(r5, 0x0, r5)
ioctl$TIOCFLUSH(r6, 0xc1206922, &(0x7f0000000300))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cb672e9e7247818f970e01746858a212f0d", 0xc0}, {&(0x7f0000000200)="a5781d1fe6aee59a92", 0x9}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x0, 0x0, 0xfffffffd, 0xffffff78, "08040800f60c00e4000100008d1b820900"})
r2 = fcntl$dupfd(r0, 0x0, r0)
write(r2, &(0x7f0000000080)="b08d51c7f60912", 0x7)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x64}, {0x80}, {0x6, 0x0, 0x0, 0x10000}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x200001000, &(0x7f0000000000)=0x4ef2a2e3, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffbc, 0x0, 0x0, 0x0, "6fc60900e4000000001200"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1000, &(0x7f00000000c0)=0xc8be, 0x4)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0xffffffff, 0x0, 0x0, 0x0, "00000000000100"})
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000001580)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
bind(r3, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
chmod(&(0x7f0000000180)='./file0\x00', 0x58)
setuid(0xee01)
dup2(r0, r2)
r4 = dup2(r2, r1)
connect$unix(r4, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
syz_emit_ethernet(0x36, &(0x7f0000000000)=ANY=[@ANYBLOB="aaaaaaaaaaaaffffffffffff86dd60c1b31400000000feffffff000000000000000000000201fedd"])
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc1206922, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f0000000040)=0x8)
sysctl$ddb(&(0x7f0000000280)={0x9, 0x5}, 0x2, &(0x7f00000002c0)="838174f6", &(0x7f0000000340)=0x4, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000280)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x30}, {0x5}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
sendmsg(0xffffffffffffff9c, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="07373a7919b09e885e5c9106b1d8744eb47164459fb320d4c840248dc973ff3fc0865f223445b9741d2c49cddd6a0d509b7280c72cf486c9f4f73274aff60f86ccbf259eb7b200e812068fcc4d56ac1257d3c196bc4542804f930bf23f7f6ef9c391d1fce1f76cd945afd386b40618bf9b07ea814ea56acdba4c958640e28084a957cd5fae8feb5660b3ff99ecd5e054e182", 0xde}], 0x1, 0x0}, 0x0)
pwritev(0xffffffffffffff9c, &(0x7f0000000040)=[{&(0x7f0000000300)="024e118b0b6b038d2666046fe16731ffff", 0x11}], 0x1, 0x0)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x400000000002, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc038694e, &(0x7f0000000300))
r0 = getpid()
ktrace(0x0, 0x5, 0xc04, r0)
sysctl$net_inet_ip(&(0x7f00000000c0)={0x4, 0x2, 0x0, 0x1b}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f0000000040)=[{}, {0xc0}, {0x6, 0x0, 0x0, 0x4cf}]})
pwritev(r0, &(0x7f0000000600)=[{&(0x7f0000000300)="8ecbb0ea13c3fe6d30ef01b92673", 0xe}], 0x1, 0x0)
open$dir(&(0x7f0000000200)='./file0\x00', 0x18310, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r1)
sendmsg(r0, &(0x7f00000004c0)={&(0x7f0000000100)=@un=@abs={0x0, 0x0, 0x1}, 0x8, 0x0, 0x0, &(0x7f0000000240)=[{0x100, 0x1, 0x0, "2e650e5ed6429dc8ed33518c10fcced34a82d27522f8928ac8adbcb33d40c491648dc2c0b478d84a825a043b9c623587f7594310e1fb9e3cc77d9aad1bcc9e560d42495e0faa97a0a7b6e62bc68ddd8dee754de2798e9dd108112874512497547535dee4298d897ac89ba2fa633de5fca67f35fa31058654c19c462153a64b36d5abd24425bc02c564a3783ee6a0f73813f4cc603f46ce7ef43695bd005627f56d8d0fb2ae2574b7e0a48192964a18cd8f448a539a859d332b0cb17a241324d91a75324e6f53c5185020515722e7765da34070870ad9819f8485881ae7c4e14e229da665f820da219e"}, {0x100, 0x0, 0x0, "fb181a140da1c38b0c57bd5aa8312765ac71cf565bd126950246d2c68da82547baceeab82e70096196e7e2489db7001a853cde2a2e62eafa2b67c25a5f7cfa496dfb659df46e1dc678afebe7665a2c4a2679fe887ecb0ffd97ce4e4e6fb206502e36d173781bb393767a0ec830fb3d98ec6f4af8e0f7804426bf15ba47d46a9a7d5c4281e0e15b41c3a6b3e73159503238c18717b528b9dac681cda4af1f9dceb0eab1ed7883973f44a216927a7477f372b7d04802e7a82d2c591c30aea1b9cca96322f452b4e9cc69eb72d69e8bb18b8c3c0078322a968fa13b117d03b291ff95fedd195a0029f1de"}, {0x10}], 0x210}, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x6)
execve(0x0, 0x0, 0x0)
mkdir(&(0x7f0000001140)='./file0\x00', 0x0)
readlink(&(0x7f0000000180)='./file2\x00', 0x0, 0x0)
open$dir(&(0x7f0000000140)='./file1\x00', 0x0, 0x0)
open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
clock_settime(0x2, &(0x7f0000000100)={0x5, 0x3})
syz_emit_ethernet(0x46, &(0x7f0000000000)={@random="831ee5237df0", @broadcast, [], {@ipv4={0x800, {{0x9, 0x4, 0x0, 0x0, 0x38, 0x64, 0x9, 0x0, 0x0, 0x0, @empty, @multicast1, {[@rr={0x7, 0xf, 0x31, [@rand_addr=0xffffffff, @remote={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}]}]}}, @generic="a09c7ec134e6e36f9e1007d19c606e7a22afe9fc"}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x80206982, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0xb1}, {0x87}, {0x6, 0x0, 0x0, 0x21}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvfrom$unix(r0, &(0x7f00000000c0), 0x832f1f7d, 0x0, &(0x7f0000000000)=@abs, 0x20000000)
mprotect(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x2)
dup2(r0, r1)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b1000513000001000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d50333e0a9ccb59277d026ba8af63ff37283c18e4fd89720fc3872babfbb770c1f5a872c881ff7cc53c894b03b22f311e404f36a00f90006ee01b1257aea8c500000002000000000000020208a371050000000700000000000001", 0xb1, 0x0, 0x0, 0x1)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x40001912, r0)
ktrace(&(0x7f0000000080)='./file0\x00', 0x7, 0x1e2e, r0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0044456, &(0x7f0000000140)={0x7})
pipe(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
writev(r1, &(0x7f0000000900)=[{&(0x7f0000000a00)='B', 0x1}], 0x1)
write(r0, &(0x7f0000000340), 0xd4e688a67930cd)
write(r1, &(0x7f0000000040), 0xfeea)
close(r1)
close(r0)
execve(0x0, 0x0, 0x0)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000040)={0x97ac, 0x0, 0xffffff80, 0xffffdf7f, "0c9bfcb5609c00"})
write(r0, &(0x7f0000000240)="66893f1ba52f925fbc5673461eaf36c591a9a8311fef8d649cdc03cb4f4ae614182ddaeabe997a84a3b319f851169ccd6065ef97893fcb4c1c756f3c8c2df3898cd0694f6d5687c96e732b817d14d81f05c0bb1f4ab21ee7fc2033653cd2154985e9f32e1b5a51400b6cf4fcd9ba9e61cc0ccd4d8e2c37d70298bdb9307210f04891b3382f86f66be2d2026cc1ae2729b32737e85ed23b2053eea427be6c0045598cdfb92934638d23a3908c0d8fe56d70339ec9f4ebc1a6029c017192f2181cc1d7112ca76553c68650f346db60a18e8d08e2b4b24ef29217f3d2032ec0b59393872534f4e36069dd31c0dc31b3c0e35ca7", 0xf2)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/\x00', 0x0, 0x0)
mkdir(&(0x7f0000000100)='./file1\x00', 0x0)
chroot(&(0x7f0000000180)='./file1\x00')
fchdir(r0)
unveil(&(0x7f0000000040)='./file1\x00', &(0x7f00000000c0)='r\x00')
mkdir(&(0x7f0000000000)='.\x00', 0x0)
mlock(&(0x7f0000ffc000/0x3000)=nil, 0x3000)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040))
mlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x800000018, 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x1ff, 0x0, "d730c1e7e400000001000000000000000000009e"})
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "08001a19e9001040dcc40000000000002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x3, 0x4)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, 0x0, 0x9}, 0x0)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
mknodat(0xffffffffffffff9c, &(0x7f00000000c0)='./file1\x00', 0x2000, 0x4200)
ktrace(&(0x7f0000000180)='./file1\x00', 0x0, 0x0, 0x0)
r0 = msgget$private(0x0, 0x0)
msgrcv(r0, 0x0, 0x0, 0x0, 0x0)
msgsnd(r0, &(0x7f0000000000)=ANY=[@ANYBLOB="020081816365000075fd1e578f599105722d8fee276acc9346b78811801abd3e9667a83f8d331be5271681596f0bc667be76446832ad646d0fe56ac15392542287b9b32a336c3dfb25bf6d21c060262843c78d67961c61a3"], 0x58, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202d77f96000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_linger(r2, 0xffff, 0x80, &(0x7f0000000040)={0x5}, 0x8)
close(r2)
execve(0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x0, 0x200}], 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000340)=ANY=[@ANYBLOB="82028b35c7"], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1023, &(0x7f0000000040)=0x3, 0x21a)
poll(&(0x7f0000000000)=[{r0, 0x1}], 0x1, 0x5)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000000)=[{0x40}, {0x4}, {0x4000006, 0x0, 0x0, 0xf7fffffd}]})
write(r0, &(0x7f0000000140)="23295428e2fa906bdf4ba040352d", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f00000000c0)=[{0x15, 0x1}, {}, {0x16}]})
write(r0, &(0x7f0000000240)="0ddb000000000000000c51a14ebb", 0xe)
mknod(&(0x7f0000000180)='./file0\x00', 0x61c8, 0x202)
mknod(&(0x7f0000000080)='./bus\x00', 0x6000, 0x202)
rename(&(0x7f0000000000)='./file0\x00', &(0x7f0000000040)='./bus\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x3, &(0x7f0000000080)=[{0x50}, {0x64}, {0x6, 0x0, 0x0, 0xffff5b89}]})
write(r0, &(0x7f00000002c0)="03e09a90b3911b4299937474390d", 0xe)
r0 = socket$inet6(0x18, 0x3, 0x0)
getsockopt(r0, 0x29, 0x1a, 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000000000)={0x0, 0x0, &(0x7f00000001c0)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047505bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edbf57b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4693fd24c1716d5fc09542a0337b01de42e800080000e8b8f8430677090c976c5b1b7fc3ab175559766937e7af951e5025", 0xb0}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1084413, &(0x7f0000000240))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[0x8000000000000], [], [], [], [{}, {}, {0x0, 0x0, 0x0, 0x100000000100}]}})
ioctl$FIOASYNC(r1, 0xc1084425, &(0x7f0000000240))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
socket$unix(0x1, 0x5, 0x0)
dup2(r1, r0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000100))
select(0x40, &(0x7f0000000000)={0x1f}, 0x0, 0x0, 0x0)
select(0x40, &(0x7f0000000300)={0xff}, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open(&(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
r1 = open(&(0x7f00000000c0)='./file0\x00', 0x1, 0x0)
poll(&(0x7f0000000100)=[{r1, 0x4}], 0x1, 0x0)
close(r0)
poll(&(0x7f0000000080)=[{r1, 0x4}], 0x1, 0x0)
munmap(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
madvise(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{}, {}, {0x0, 0x0, 0x20000000, 0x100000000100}]}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240)=0x2)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$WSMUXIO_LIST_DEVICES(r0, 0xc1045763, &(0x7f0000000100))
syz_emit_ethernet(0x154, &(0x7f0000000000)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "ee1600", 0xffffffffffffff1c, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @mcast2, {[@dstopts={0x0, 0x2, '\x00', [@ra, @pad1, @pad1, @padn={0x1, 0x5, [0x0, 0x0, 0x0, 0x0, 0x0]}, @padn={0x1, 0x3, [0x0, 0x0, 0x0]}]}], @generic="951b6b4f853dd3b8573b1dd4f4efb720baa2b746b8793862a38429d578a68570f18cce10451d8a8dd29e15ae5f0b4518fbcefcfb00e17501cb7c8e288cb072aea750549aea983012e92de3e56aa1a569f138751f9893ae592208a8f567a535d1db8d63a8abb8ea7748843fa621f8e99fca0b82eaf2a92f15dd80a9f3108e65fccb147c81d834c33e8c41a0a5aa4c649c48cfc65b714a9a5dd6c2bf79d33ef34d8dd27d6b2cedf212a3a1c8d7fc87e40f7dfc3716f5b4e0504bed4cac1b1e3d7baaa973975d29804e14133d724994519ff0565841bfbd0439cc1c389cde69da85d8f15032694f829e3bfc83d3bee0e2a2b6618122dc5a3f9c5912f9e8650f"}}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069dc, &(0x7f0000000300))
open$dir(&(0x7f0000000040)='./file0\x00', 0x7124a24c9c995e05, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000300)='./file0\x00', 0x0, 0xfcfc96ac1f78739e, r0)
setreuid(0xee00, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x4, 0x1f1a, r0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001300), 0x1, 0x0)
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80045713, &(0x7f0000001180)={0x0})
mkdir(&(0x7f0000000100)='./file0\x00', 0x0)
lstat(&(0x7f0000000180)='./file1\x00', 0x0)
link(&(0x7f0000000380)='./file0\x00', &(0x7f00000003c0)='./file1\x00')
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000240)={{0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x8}, 0x0, 0x0, 0xffffffffffffffff})
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fa090000001e328a1811", 0xfffffffffffffcb5}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001058e7d00000000ddf500"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x15}, {0x5}, {0x6, 0x0, 0x0, 0xd920d164}]})
pwrite(r0, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
poll(&(0x7f0000000080)=[{}, {}], 0x2, 0x7ff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x2)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSDIRFILT(r1, 0x8004427d, &(0x7f0000000200)=0x9)
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @random="9d8b1e9d2424", [], {@ipv6={0x86dd, {0x0, 0x6, "923149", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
mknod(&(0x7f0000000000)='./bus\x00', 0x100000000204f, 0x5900)
r0 = open(&(0x7f0000000640)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000100)=[{r0, 0x1}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
close(r0)
r0 = msgget(0x0, 0x81)
msgrcv(r0, &(0x7f0000000040)={0x0, ""/45}, 0x35, 0x2, 0x800)
msgsnd(r0, &(0x7f0000000300)=ANY=[@ANYBLOB="0238c89794a9bc000000000000002878bfcb9dea1aa33fcd271620e39db0ba38385173ccfb32826533cd31625fdd38905bdd491450aa954ba3a2b8964c0824ea82ad70de95b014b00ca0ff0c2e439896adc2777a4db01c57e8385d24c4f05bb3eca8b5a81d3bfc8ae802bb77f119e2a65af324ac8d201da1d2942c4ba5a0517cf12a1529a0ffe65c450b17a927079f0724ff2e41ea51056f5ac9c95ab953dc9e1825be17e0f52dae810228cb1a42a255cf0a1bbc30c9298223a57b8e1de145a495df25e570b36801106818934809b26a96eca38e544839b39cce076090e7"], 0xe2, 0x800)
mknod(&(0x7f0000000100)='./bus\x00', 0x2000, 0x4086335)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r2 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIOASYNC(r2, 0x8004667d, &(0x7f0000000040)=0x9)
write(r2, &(0x7f00000001c0)="2bf58f0a6fbd75c954ca68f04927a828da4e06105df5c231b85a3e2b533079bacc1dd864ec4b2a51c2174a48", 0x2c)
ioctl$VNDIOCSET(r2, 0xc0384600, &(0x7f00000002c0)={&(0x7f0000000000)='./bus\x00', 0x100000000000, &(0x7f0000000280)='./bus\x00', 0x6})
r3 = msgget(0x1, 0x84)
msgrcv(r3, &(0x7f0000000400)=ANY=[@ANYBLOB="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000004000"/150], 0x96, 0x0, 0x1000)
r4 = msgget$private(0x0, 0x0)
msgrcv(r4, &(0x7f0000000880)={0x0, ""/4096}, 0x1008, 0x1, 0x1000)
ioctl$VNDIOCSET(r1, 0xc0384600, &(0x7f0000000140)={&(0x7f00000000c0)='./bus\x00', 0x8, 0x0})
r5 = msgget$private(0x0, 0x161)
msgsnd(r5, &(0x7f0000000600)={0x1, "a8d4d1b5277340b366b79be7cf0000ffcd872fde14e4cfff06bc869ef3897aed28cd90677d50164e883c5b0f176bb0fe8dc824fd0100242ac3a813739915cc243f9a06a1786ac348dbbdaf8cc2ad2c93e6015ed8fc9c22a70197690ce3fb03ee265625a2794964844aa42583a3254131d93d2bbdeddedd1db6f8c133774e60d9ec9c1a56940f446980290884de010000004081e29e012f8b8228f3cc85a7f2eb47ddaf00665c05fb59ca867cd2cf3b5f8f8ebe3918d526ed997b89471fcc8167629f3b52072d3a76a3bf4cdfb599766d8eba6a095f81f625dbacb9cf011548e0932141eb206f71d92dc9f653bf894cfea9843088e9255aeb977615e70263b4d56603714a84dae7255bf562d595ac56051045cea3d1ef8f5a9cf434a313bddedcdfbfa7def9aa1eeacc7a43a21aa1e3036a7d4d6e77117c64afb26dd80900000000000000673ed8a1cfbb90cca6a805483a1a41c3707e4843900c095f3512fe40c0480c74d5740465e9afe4b0386d9ee36ca1820931699a0da28fc0ef94b2ca765fd298e93e54ca5e7c45340bff6c3ab9c0026cc8ba052823f8c32cec4462635dfa710e5d42585360736a1eaf6e3a46ea2bb569b2cef284e2f077b8964de41edd17a641e1aa71296d415110766b164088a6060448770222cfb427a465c615ae04b0e7d3c4466622b3169b58eb10e848049fdd93db5012c38e03b679ddda2150aa287c293bedf59780682d11a78a6636895861982673015c65aea4afa7ae1bafe61167a3064b10bc08d2fcbeec169e463a1b7aefe437ce340be55513cb8a20"}, 0x246, 0x0)
munmap(&(0x7f0000001000/0x3000)=nil, 0x3000)
sysctl$vm(&(0x7f0000000000)={0x2, 0x8}, 0x2, 0x0, 0x0, &(0x7f0000001180), 0x4)
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "3d4bc9", 0x8, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="7784ce7d2e7e6e22dc0594d7d02a7abf", {[@hopopts]}}}}})
r0 = socket$unix(0x1, 0x5, 0x0)
poll(&(0x7f0000000140)=[{r0}, {r0, 0x4}, {r0}], 0x3, 0x0)
msgctl$IPC_SET(0x0, 0x1, &(0x7f00000006c0))
clock_getres(0x4, &(0x7f0000000040))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000180), 0x2, 0x0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000500)={{}, {[], [], [], [], [{}, {}, {0x7}]}}})
ioctl$FIOASYNC(r0, 0xc450443c, &(0x7f0000000240))
sysctl$kern(&(0x7f0000000500)={0x1, 0x30}, 0x2, 0x0, 0x0, &(0x7f0000001580), 0x0)
sysctl$ddb(&(0x7f0000000000)={0x9, 0x6}, 0x2, &(0x7f0000000200)="a6d5dbfb", &(0x7f0000000040)=0x4, &(0x7f0000001200), 0x4)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000140)={0x3, 0x7fffffff})
r0 = kqueue()
kevent(r0, &(0x7f0000000040), 0x36, 0x0, 0x300, 0x0)
r1 = kqueue()
kevent(r1, &(0x7f0000000040), 0x9, 0x0, 0x8, 0x0)
close(r0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x25, &(0x7f0000000040)={{0x18, 0x2}, {0x18, 0x1}}, 0x3c)
r0 = kqueue()
kevent(r0, 0x0, 0x1000, 0x0, 0x0, &(0x7f0000000080))
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x0, 0x0, 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000000), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000100)=[{0x61}, {0x20}, {0x6}]})
syz_emit_ethernet(0x2a, &(0x7f00000001c0)={@empty, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @multicast2}, @udp={{0x0, 0x2, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x40}, {0x14}, {0x6, 0x0, 0x0, 0x38000000}]})
pwrite(r0, &(0x7f0000000340)="0900000013eb0913587ca9ec25db", 0xe, 0x0)
syz_emit_ethernet(0x52, &(0x7f0000000040)={@broadcast, @random="ecc055825e25", [], {@ipv4={0x800, {{0xc, 0x4, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @rand_addr, {[@ra={0x94, 0x6}, @timestamp={0x44, 0x10, 0x8, 0x3, 0x0, [{[@empty]}, {}]}, @lsrr={0x83, 0x3}]}}, @tcp={{0x2, 0x0, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x1, &(0x7f0000000000)=[{0x6}]})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x0, 0x0})
mknod(&(0x7f0000000080)='./bus\x00', 0x80002005, 0x2d94)
r0 = open(&(0x7f0000000140)='./bus\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000002340)=[{&(0x7f0000000180)=""/4096, 0x100b}, {&(0x7f0000000040)=""/11, 0xb}, {&(0x7f0000000100)=""/41, 0x29}, {&(0x7f0000001180)=""/149, 0x214}, {&(0x7f0000001240)=""/4096, 0x1000}, {&(0x7f00000023c0)=""/211, 0xfde3}], 0x6, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000440), 0x0, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000003c0)={0x0, 0x0, &(0x7f0000000100)=[{&(0x7f0000000540)="0ddfcdccb017909acdd2070b02b5", 0xe}], 0x1, 0x0, 0x20}, 0x0)
ioctl$VNDIOCCLR(r0, 0xc0504417, &(0x7f0000000500)={0x0, 0x0, 0x0})
r0 = kqueue()
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e6", 0xf)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{{r0}, 0xffffffffffffffff, 0xb1}, {{r0}, 0xffffffffffffffff}], 0x0, 0x0, 0x0, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{}, {{}, 0xfffffffffffffffb}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000000), 0x5849, &(0x7f0000000140), 0x800, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = getpgrp()
setpgid(0x0, 0x0)
setpgid(0x0, r0)
setpgid(0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0x80206916, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000240)=[{0x4}, {0x74}, {0x6, 0x0, 0x0, 0x4000000}]})
write(r0, &(0x7f0000000440)="3c9ed0f773672ee31265ac52c02e", 0xe)
sysctl$hw(&(0x7f0000000100)={0x6, 0xc}, 0x2, &(0x7f0000000140), 0x0, 0x0, 0x0)
setreuid(0xffffffffffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{}, {0x0, 0x0, 0x0, 0xffff}]})
semop(0x0, &(0x7f0000000040)=[{}, {0x0, 0x4}], 0x2)
sysctl$kern(&(0x7f0000000040)={0x1, 0x3f}, 0x4, 0x0, 0x0, 0x0, 0xfffffffffffffe62)
r0 = kqueue()
r1 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
kevent(r0, &(0x7f0000000000)=[{{r1}, 0xfffffffffffffffe, 0x29}], 0x1, 0x0, 0x1ff, 0x0)
close(r0)
mknod(&(0x7f0000000240)='./file0\x00', 0x800080002002, 0x5bc8)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r2 = dup2(r1, r0)
ioctl$VNDIOCSET(r2, 0xc0384600, &(0x7f0000000200)={&(0x7f0000000140)='./file0\x00', 0x2, 0x0})
symlink(&(0x7f00000003c0)='.\x00', &(0x7f0000000140)='./file0\x00')
readlink(&(0x7f0000000200)='.\x00', &(0x7f0000000440)=""/30, 0x1e)
mkdir(&(0x7f0000000380)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0/file0/..\x00', &(0x7f0000000040)='r\x00')
unveil(&(0x7f0000000080)='./file0/file0/../../file0\x00', &(0x7f0000000300)='c\x00')
r0 = syz_open_pts()
r1 = dup(r0)
getdents(r1, 0x0, 0x16)
rmdir(&(0x7f0000000180)='./file0\x00')
ioctl$WSDISPLAYIO_LSFONT(0xffffffffffffffff, 0xc058574e, &(0x7f0000000780))
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000500)='c\x00')
recvfrom(r1, &(0x7f00000005c0)=""/211, 0xd3, 0x800, &(0x7f0000000800)=ANY=[@ANYBLOB="0000017a1b66f06c61302f2e93754001653070f2d8e57f21bf5cfdfba0385f2f933ec17cbdb4338f7cd28cbbf30aeecdb64701ae00000000000000117e6aff7f000037be46f61851a65214a8a2064e8356e3d5c7f47af5404b71c3fc603b0215431507209c2bc64a87b927c0394d87e1faa6b2e44166150d418c7945473335aeea1580211fe9eb3e2700c196993c8c54d86e05657f846e5c2f006df747693b8a46b7f511f34ec5908ee2809153ff8864bb9b715e41f1e5304464f82195bd227c738e07cc0695c266f2e2d71c44"], 0x1c)
mkdir(&(0x7f0000000700)='./file1\x00', 0x6f)
symlinkat(&(0x7f00000001c0)='./file0/file0/..\x00', 0xffffffffffffffff, &(0x7f0000000400)='./file0/file0/../../file0\x00')
mkdir(&(0x7f00000000c0)='./file0/file0/../../file0\x00', 0x14)
unveil(&(0x7f0000000740)='./file0/file0/../../file0\x00', &(0x7f0000000540)='c\x00')
connect$unix(0xffffffffffffffff, &(0x7f0000000580)=@abs={0x1, 0x0, 0x2}, 0x8)
ioctl$WSDISPLAYIO_LSFONT(0xffffffffffffffff, 0xc058574e, &(0x7f0000000280))
rmdir(&(0x7f0000000340)='./file0\x00')
syz_open_pts()
openat(0xffffffffffffffff, &(0x7f0000000080)='.\x00', 0x0, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x7effffff, 0x0, "0010e9ff360005c300"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x0}, 0x8)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x1, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x3e, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebff929601fc0300"/20, 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, &(0x7f00000001c0)="0032568c952143b05817620b7ae7a9808adf9c9942c96e092d13b51ecc75ea5ad4ae0b1e9539fd2fdd55ab68a49b016ce6def2bcdf396195100bcc62e8e3250ba90d209f4d123b7f718fe88f90d8e10841a85512c25ff121d98ba6f16db95ab0bdb375d627800de0359f439b9a017d9af7229914c0e305b36cfd81c6fe307fcaa598ed0b238d490c7501756425219a3a560a9b5a80c6c34aecaddd1feb8df45570029164634b6e70fd922f194d05d1ffcd10de76650b54a49ed67e610abbf6072885c5960aed7ad71910edb61a27a9235d7cefb5775fcde1769574aaecc1f17e2cb3e753702d72901deaf8650ec0d543f23812d6caab9c76a67d3c20d578c109a6c10f7ab40538890383908281bf81f072ca4929760d2e851aa52029817881bc407549d7715d72e95786c108c72ab37a38945f77607cf2803186cd1170accaa88a42b9ecee4e35b5b7a20c45b14f2c8e4a89e739029b758a29f139118c1f44c57b74124b50b1ad4c2e4b12c14e4e03bf6fa5915f343a1ecef992100706a7053506394b0f8c168deb745d1e132ff9a2f78bc5f7451d0c5b83374875d3726bee535348d32d24ad8d0a97663df576888ca44faef06b8d3766a6fbf903df3cd689315aee06e7d701c31e2915106a981045eb36a9a3a18683c0a56489a89d16e4ffb97df62c4d6efaaa7fa49d6b2bd29239105d59537ea138e7303c240877d82f3a32abc11218c285c855bf741d438bed11a6d470341c3fd26fac2856e4254da893aa0db6d39ffda82f58208a587bbc6d72d57d152fac5cdebdc65d8ae1c5edd642c96f86704abf36140669a30de84e96071632a48e7e5c2a0b652fa872c54b2df92dc0a1167c5b454bcda28bfcb3f8369fcfa5170d5514bebadb5fe46adf3639a8d76d7ac326983169fb23a4fbb5a20814ec13f8f62b48edac9df641bf06f755054b04ab644457588bcd121fd706ab53732ffca95b958c15cf67963d57abaed0384958c58418eb9a189b453f949ee1ba708053f402c6cbddb52f1baadb0e9b17dbea8424b5c72ecf9036a6daedcebeb11e55c41851ac133145962c81bc57b9c6e8aa13eebeb6c90fa9e4c26c3d557caa2f07438a3877241629662b3ed628fe82f5392c99073f09baa989bc150cff4df7ccf126fd6b4bfeca89a5ed9435453dadec9e886568a0dace6fdbb156f0ef86c3193402825d3af55badbb83a6c3e36110a0add8421e438495b40fe9c02bf384cc1d6bf70aca8c8989cb6673c2b57e2b7af2e044eeef7fb287a7126121ed310aeab4e16ccdf223a38ab29a81f1b56c616db09e2ecde5e5797da2633f0387715624e350d5ece567dfcde3b5be4c316325f019eea30ef5ab473a62fdb3a9fea5f9b290a7eee925b9520969112e56469b46e7ef76ba9f038fdc58c8298d39bd0b083e0e66b07b69d2d38a52c77e1cad5f39ad04729e9846755aa13cdcfc697732df456b3858045a4bed2fb6a4ddc64b792b05db8af549710f78e735527b3ca96e38436e2be2cf3b8dc2ca5f0d1264b223e34cb04ae8524ada46a9492e4359c007d876be49f8dbe30ad8c1ffabffb9cdc81fc0f5c5f6149a1fbfbdb16c30a1e94fd172b7741e0c5e5e15f441dc65cee6789c17772b22cba9c735b9e2ecf0cb3e71eb9d4f70f6749b4b5e704e059c43e6da074cb42368dc17b15ab8911b3d8fda04026983e342bc4c421085e3d20bb2c8d29e4f51a4f1eb6940ad2b9a8541baff2999c5c0be06b163096a0215c2e11d8935dea289fe22b210bdebbc048db6290be8a35ad3026fb1ca4c7c89f6bf6a4c40b5610e89131115bfc12b5f5765e7552259b3addd011cf92ed38b2908d31a54142603178fc3fddd1c46e606cbfce372642d339c862e7ef8103ced2e1650fd574b4486132ef24f98966a5d530005e7ff3371d402a275633aff5e0c8b4075b0ffb079cec021d238b791ad1981a6c2173d0e4a734ec8394f2e8cbe36e924dcab06c9e8d2c6ace30e254b6818be7dfdf171ce336f69099e288d16a891bf5ed08125107b1fd73cd8d55f14761acd4bff5c8d326d9d", 0x5ad)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r1, r2)
writev(r0, &(0x7f0000001a40)=[{&(0x7f0000000140)="ad", 0x1}], 0x1)
writev(r2, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
fsync(r2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000540)=[{0xb1}, {0x30}, {0x6, 0x0, 0x0, 0x200}]})
pwrite(r0, &(0x7f0000000080)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
r0 = socket(0x400000000018, 0x3, 0x0)
close(r0)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r1 = socket(0x18, 0x3, 0x3a)
setsockopt(r1, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x1}, 0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000]}, 0xe5)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x1, &(0x7f0000000000)=[{0xd434}]})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000100)={&(0x7f0000000080)=[{0x20}, {0x20}], 0x2})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x2c}, {0xc0}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@empty, @remote, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @empty, "56e276c59f024a165818e7dcde6f84c7"}}}})
r0 = socket(0x18, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
bind$unix(r1, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x7ff, 0x4)
listen(r1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
clock_gettime(0x5, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x14}, {0x3d}, {0x4406}]})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000140)=[{0x28}, {0x50}, {0x44f6}]})
syz_emit_ethernet(0x4a, &(0x7f0000000100)={@local, @empty, [], {@ipv6={0x86dd, {0x0, 0x6, "a4f7ff", 0x14, 0x0, 0x0, @loopback={0x0, 0x2}, @loopback, {[], @icmpv6=@ndisc_ns={0x87, 0x0, 0x0, @mcast1}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000240)=[{0x61}, {0x2c}, {0x6, 0x0, 0x0, 0x7ffffffc}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x4, 0x40001808, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000200)=0xc)
setregid(r1, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x2, 0x0, 0x0)
sysctl$net_inet_ip(&(0x7f0000000080)={0x4, 0x11}, 0x4, &(0x7f00000000c0), 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCEXCL(r0, 0x2000740d)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0xa, 0x0)
writev(r1, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
mmap(&(0x7f0000000000/0x200000)=nil, 0x200000, 0x3, 0x10, r0, 0x0)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x400004000011830a, 0x0)
write(r2, &(0x7f0000000780)="089267d3ff4f0b87969f", 0x100ad)
pipe(&(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
ktrace(&(0x7f0000000100)='./file0\x00', 0x4, 0x12, 0x0)
write(r3, &(0x7f0000000040), 0xfeea)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b1000516600000000000000020000000000004fecea11ea8fef96ecfc73fd3357ae26caa0416fa66376336acf018c34758781e4991f7c8df5f882b167be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37283018e4fd89720fd300000000000000f5a872c881ff7cc53c894303b22f310b404f36a00f90006ee01b1257aea8c500ff0002000000000000020288a371a3f800040000000000000001000000000000ffff", 0xb1, 0x0, 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f00000000c0)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x5, 0x408})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x54}, {0xc0}, {0x6, 0x0, 0x0, 0x20400000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000040)="7cdc3fc0be8c090194c3df3860ab", 0xe)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
setuid(0xffffffffffffffff)
r0 = msgget$private(0x0, 0x10)
r1 = geteuid()
r2 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000080)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r4=>0x0}, &(0x7f0000000100)=0x1)
seteuid(r4)
shmctl$IPC_SET(r2, 0x1, &(0x7f0000000040)={{0x100004, r4, 0x0, 0x0, 0x0, 0x13, 0xfffd}, 0xad7, 0x2, 0x0, 0xffffffffffffffff, 0x3, 0x3f6, 0x800000403})
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={<r5=>0x0}, &(0x7f0000000080)=0xc)
msgctl$IPC_SET(r0, 0x1, &(0x7f00000000c0)={{0x8000, r1, 0xffffffffffffffff, r4, 0x0, 0x0, 0x6}, 0x4, 0x3f, r5, 0x0, 0xfff, 0x9, 0x4, 0x6a})
sysctl$net_inet6_ip6(&(0x7f0000000040)={0x4, 0x18, 0x29, 0x36}, 0x4, 0x0, 0x0, 0x0, 0x0)
r6 = msgget$private(0x0, 0x0)
msgctl$IPC_RMID(r6, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
setsockopt$sock_int(r0, 0xffff, 0x1004, &(0x7f0000000080)=0x40, 0x4)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000180), 0x4)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x1, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r0)
ioctl$TIOCFLUSH(r2, 0xc0206917, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000040)=[{0x3}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @local={0xac, 0x14, 0x0}}, @tcp={{0x2, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000002c0)={0x3, &(0x7f0000001440)=[{0x14}, {0xc0}, {0x6, 0x0, 0x0, 0xfffffffd}]})
pwrite(r0, &(0x7f0000000300)="c8dcafccd1a6f48da693157343cb", 0xe, 0x0)
clock_getres(0x0, &(0x7f0000000000))
sysctl$kern(&(0x7f0000000000)={0x1, 0x4f}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x1, &(0x7f0000000100)="4c6b22820a2ef676be2a6b479d00f973bf54a69663cb37e668192e388a19fcf227e934a0fe3c6028394c63129680368923badf8cf766a945eef6987021274848f141146f5aaa724170bf7257ce229713b9cdd2bcf54c9cbcd18ea8f9f3f2b9ceb72e90221a", 0x10017)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ftruncate(r0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, &(0x7f0000000100)=[{}, {{}, 0xfffffffffffffff8}], 0x0, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f0000000040)=[{{}, 0x0, 0x0, 0x0, 0x0, 0x2}], 0xfffb, &(0x7f0000000140), 0x7fffffff, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x1, 0x0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
renameat(r0, &(0x7f00000000c0)='.\x00', 0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00')
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = dup(r1)
r3 = fcntl$dupfd(r0, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0xfe]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x3, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r3)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
setrlimit(0x8, &(0x7f0000000000)={0x8, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000180)={0x197b0, 0xfffffffd, 0xfffffffffffffff8, 0xb1, "0901a7d3137c00005da24700"})
writev(r0, &(0x7f00000002c0)=[{&(0x7f0000000900)="b4b5d10a8b25157d4f64c002380f02f42e10a46c2eb46f96e206008a", 0x1c}], 0x1)
linkat(0xffffffffffffffff, &(0x7f0000000000)='\x00', 0xffffffffffffffff, 0x0, 0x0)
getrlimit(0x0, 0xffffffffffffffff)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x28}, {0x34, 0x0, 0x0, 0x93}, {0x6}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@empty, @local, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="cf9fbb8ea1ee", "", @broadcast, "54e2e9767c5f018a37ee7b48af7d655c"}}}})
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@random="b15b65dec012", @remote, [], {@arp={0x806, @ether_ipv4={0x1, 0x800, 0x6, 0x4, 0x0, @empty, @broadcast, @empty, @loopback}}}})
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000040))
sysctl$vfs_ffs(&(0x7f0000000000)={0x7, 0x2, 0x3}, 0x3, 0x0, 0x0, 0x0, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000040)={@broadcast, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "2fb064", 0x0, 0x0, 0x0, @rand_addr="90f5110b8e72b21f4777381d7beac029", @local={0xfe, 0x80, '\x00', 0x0}}}}})
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097ac, 0xfffffffd, 0xffdffff9, 0xffffff5b, "0125000008000100008d1b38b87200"})
r2 = openat$pci(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
writev(r2, &(0x7f00000005c0)=[{&(0x7f00000000c0)="7777a8bbf20779321198c9a5", 0xc}, {&(0x7f0000000440)="a9e29062", 0x4}, {&(0x7f0000000480)="aadc4332", 0x4}, {&(0x7f00000004c0)="3d51ecb239c630c5e237d82895bf8250eb5c0a53ee2797d38a9fe9d6210324a23ba1805d83106059215c3a903021eab8bf12cc1e094ed7444beba91c5ba9b0801147af92078f7c0aec71251c81cfd6a50fbfa2e570861a123445a4ca3504c910e20223c044e4ad94e25b2f1852e5a05a35996f23909130eae95b55bd8cb1823048f85912a86adc99e6e6d509500f8005a28ef68c05a2560196def9c2c01e942277d2328cefcee0f4cdff01860f7435a3971c887ff3ec56fe58b4552320096620afa535b2161ad3087786ab2a61dd15f0065bc8552178f1e6ce0a4c353de78f58023c17514f49331e4e2f6aad7a948ffd23ae2c5c90cf", 0xf6}], 0x4)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60acaca1f2844dd730ae78b0e94daa6340f332c6136fec572c091ec964f479d3f09601e05a4265ae5f4d3a70b8530f85f78b162764959532374a5f97ddac09069dc842ee5b6b470f0a2c13de93943f58c029f6bcf437d67a29db534b1221fd5256cba8ba8535737417084659d7f6ab02ab49954c6a070955682cc9dba2931d55a413df999446fbd9407aaab9e385a161f802553f02b1e823e10d785547a7acfe8c11b040de1a30bddc771c1adf40bfb83be91ade242f0a5330f89791d6166e038cbe7f5d719bf1c9d0d9482f436fe94ce6637c899cdb4027cdf11af7c3d3662695d53198228546d6800ac6a6d9106e682c0c96e510ed7cb32772008f345f19a39743eed6db45bca832b6164801181", 0x2ce}], 0x1)
writev(r0, &(0x7f0000000fc0)=[{&(0x7f0000000c80)="a26bdb3aac1ff5c42c000f425586156414c4ab74191bf20dfbb3136d34fcb9aaa7b7f60c21628407fbb46df93676b5d28ff3074b9028b52a7a811338a11f1042a5d82716087be5f0f0ba8f1281b488d370fd7d71b8dc7ce0c7a3049b60e3430f2f2d47b1da2e71412786cc0b630d0df7c76dfa0cec747dc308bcd3fc64a7fdcb6d236b85435d3e30fadeea778f6a1985bb9da77a07819e6778565628cea46d7e7eb1253f49ce6996c7c8d1c679cccd3ffe45e4b218d5a84acb1dcbede4a73eca18a8eb3982ab7ef2f81b11c0b21d058c09a2fa2dfda19690e486aa9fe28aedba6613d1b0c8b986abecdcdcec0f2e116370456bf1f931cc89abb820cbd41414f647d5fe2bb80deba92411b9941696560ca58c8ca90b888fcce8b20ddb404727a8df677bb05e41f41c3493dd6ec39d8a145517ea41757972935ccf2940fe2ff6cce4e19b8f565489f8b6d2e5c981", 0x14d}], 0x1)
writev(r2, &(0x7f0000000700)=[{&(0x7f0000000600)="8a7a1c68fde539839383276dbf1f54f1a2e409ea9321bf63ae075130bba04357fcadaec64c764f040eee23523737dd546851b90febb11cd21418e33b90027560142f077f8d87742ee5890f9400d9e418e2d5396cb1ea1e20aa9142cddba16110d09fd99cc9ada0c517743884db063270c134b9d0950ac60492b8e66f4d630ed87a828037cb8653b61a245f47eee642670e7d3f435cd563", 0x97}, {&(0x7f00000006c0)="0fdd4be1bcfdcf3406607a8a09690ba02edf02", 0x13}], 0x2)
semget(0x0, 0x4, 0x0)
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x4, 0x0, 0x29, 0x0, @broadcast, @multicast1}, @udp={{0x0, 0x3, 0x8}}}}}})
open$dir(&(0x7f00000000c0)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r0)
writev(0xffffffffffffffff, &(0x7f0000000480)=[{&(0x7f0000001080)="c5", 0x1}], 0x1)
nanosleep(&(0x7f0000000000), &(0x7f0000001040))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000280)=[{0x4}, {0x84}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = syz_open_pts()
writev(r0, &(0x7f0000000180)=[{&(0x7f0000000040)="30daad8843260000000044f2ab4b", 0xe}, {&(0x7f0000001300)="0db05c4425f2c2bc278616b2b925415ab0720c21f0db74ef88d65e31192fbd3a665d88872eb50bc3b0cc54af5fa412d8d7630bfa2bf4611320d1faa3a6fa120928da069a0c01285c0cf9770a84f51b619790c702706c91dfe8b825c15cb7e409ab1d51f0a55b1aab23c134b15de624ca76962a37bbd46fb47f5d47e35eed0e45908679b0ccc8bd4c5e21fcb6b68ab40778f0ccb3250012c011e3e917dedb58aa3fe490da2d7041e4ff4a5e2514c701c770b2adc94b679279693e08f8b2be3a9d5de77dfc4aa0353ba03ffcb89b9447761115017f01a737ccca50214e1880194235b617945604ab5299568f8e91bb781eb46ab7196468a40e94c61c2ca003138226bda15b8654daa8e45551034003032fe3612480a3005fb51ba4ba2255e43ed67d028e53d2626da02c40470dfcaffb38b23c855c9d1ca6547e76608d56bb2d36466f1249a5726396b8af613a016dd897ce852168315cf03503f60fdeca90d4809145f2dee1535e08b764b55923b39f41ebe2761e373951c2afd01b2894584097cea3e44acb8459d6b93b015c9967af131889ff1a4ecfeb51a04b8d75776277ab7a5e319b0b4ea8c15e3bd225c6a3a3ace1697cd4512d2722ee2cbd7af5f843ec261d35562b03cb4e1a01c12507b96ebb8ab831416c9fdabe4235fdf5c064cf592cc4f66473f83f64b5f0fdb35fded90187f32a018adf5657304032939f6b28403bf6c363576e32ad5c19a56dba0f8f886a509a02f28a2831f1a29804dc61af3f46596c46c431751782161f6664d56ea65d5a775ad9f78a1c471144a91e405a0ea99fe8c8cee8c91d553aface4e64ec07e3cfb3380a9ac877daab5aad0897cde5472bc9475bf31818bc19a9c8040ceb84f29d66e2ba5542e7df1695ce88e1ee069f7369a4d443fc018e047f7acda73018fbd99e0f3f2c987aa8b3dfa857ac71d8ee1f240b1b772bf74a1855116b92c30e3c4260a2089d2e917984fc2eddaaad4eca3259222f6bd84ae423dd1a6119c3ba835055f02a9ab74aac61e5201216feff420369fd0a29ec39e03eecb37f746c4749f8771b6585b951479e07db8eb78819e618ce5771cbc455a28e65475aa13298f2e8c00ecbe9e89cec6411582bf5adec6fe74ce4a328c5c870ed5b349923d7c05547d96dedfcfd3f68a46537e77d17a35c74eecd5e844a2473d79fd665975e0e7861b84c3073f65ee371669bd162e612c5852a398de442accd2cae9bb00cc7b0476cac6d47baa934bd763cffad43e2688681f1394528f66f272624927e8319a14224d006c17a977a78ef91d536fbdd77880d234382a3c2502a0aeee0152f43405d0f8856680cac2fee5af720a23f1355532f2042f666a6e6f52fdd20e27cec98304051915b802d871a004b7273a1b1915d679b207584707186aebe7f88c22a1689411db7ccf57b8d846db46315f61fe7a67b94836a0d47178c19c4ce7e32646a8262ec1f8499eeb3825f095a409e8e59b6273eec129638029eae8919119ab037a5a029b9847de607fe6de31b98697c03844bac4c662d60b2306557c515c17f9f97877678adb5dc7b64a56a3f418dd79a053cc50fbb6321537789df21ab61ea51635f881dc4b615344f66308bfc604b0d0ab4b2709ebcd12c4adcdda0d41be940c2fedbcb31cf443e806bcf42d3d992c3402bed9eee8b98edb572bddf3a2202c1463b462ddd4a1b9cfb85b7f677e3148b6b2756e34a812569499722fb2c8dc406f3ba11db98384e85fd8bfe6601d0838b0c9ae526d45882bc8c9a7b1919a3b9ac6bfb77db4d0dff19256557152bf99a1836ad4d392ff8edc6a5da7f1ff6d9055c29071d9c20999fc19c8848a1782b802e9ddc851b8a5de4d3d6607a6a74035911aa0b186986cae0e56484ce9c2f0f99f33b0e9d33e270b6c746ea21629004b1c6fcfc6711d2207ea8513740e67c9eda65b765dee844710a7e0acaedc5e07426c982c64a90f9a3000db789e10b16584d5a39b0442b458a7f8522aa9ac9afd420a055e322a06c9cfc3ad0b013c51eebbf46b4b1cb3a507bf3b8151f4235da32e80a24f8771d12e5e97b5f1c0d8f412135c8477075d41220f7e9c8aa2068033f945cf817949e9ef3b5e2e08597d03e0ff64b87e4f457038ea875ab211d7de91b71c6b0dca0a6a16cd3faab1d70b114f71a41ba8e3ef97e0f5d292313456d446f39f728be03b546dc27db209cfa225e092f588b4a491303c67f94f079a92a1e64c4b16eee6de09bb7e5ffd422d2ed0c2595b678452c8deef73b66932203565e5926223559c56a295efd3a34fa4d3759b0ec5742917fd21f1a380ac21505bf18e0095cc8a384638133258dde13252d646ecddcf1c1b35d2731a59fbc41a2f3a5c790f2fa6269c8a92c927df305c633322cd8a5ec013088cdcd4f2c9f287529869d8b56cd2a2a78f02cf7575e805e83c2b2ccee46e9083381343861053e3db8f02b729828cf8d398a2854464ff9351dc84e4c9f10ab88335b702000bebd256beb284fb84963b0252748cd28e17b64f1567bdeb2f19d7b8f313959886c69fdaad317b8607d01bb1c439aac59aeda09bd5059faa51f7fa24d366b457173439827af230f23139d25e24c514b7c20eedfaafaa1f0d14b531541391d7471994f3bec91c6d200b2f021799aed1060ccea372e50f9725dd08293f5621624a315b41a44115e0c155e602ea06bbedbd4930ab67eeb990fa6384dbf0fc16507fced597e9931bd7f77e80a2b3927081609238addd2033ebacc0098964c3960f3d86814685638e3f2d4a1541203a84b75a064e00ef2c8600242bcac126eeafaab01224b463ef8e87c4eb5917902d68141f0eae64954113e85ab486cfbd2c309ad76f1eac2e4f1e9efa183324bf5d057aaf0b0d2824457ab6e046a149e00a11a00e34723b03e233f8602ab69bb8e741bb5b9a57af83fcb08df00d1f2d5ce3d3b837503cf420cfce5b5f2bdb41d4041eb4d25f60a13d737a66e970707f42870f4c7f79e3d6bc9c66c67a0aeecfab6ad0c18208f090050e3ac078d8ef1e9a089f817347311587b9e89f19a1cd337d9d189f244400fd6794d40b7975fa8743267eb53cb4d3c35d5e3af4a7e95a52e5ac7a664db1ebf468abfc674f8bd14981931636faa2c26e13c2b2a61ef25c602c0aa369b27d3b64cc206243a35662126311de05638b56aa027d3d1bdcbe6411094a2108789144779abadc5c56e1068eea1337c20fe318ddaef1ca231800eb135a4fe5587a17bb81ef510c98edca2b3bb19c21f844a1826fb09e1e2023c2f948d8e2d98725e63a6a772f1ef2dcdecb39c3cc2ad7cbaf7e332e96b3ea008cf5e2b5ff69181d36e38ed0b5b4884c69d45e778b161eb0ce31c75ffff865eb7fbb82c1b703e1da872022fc245a41e583e2a7b2cef079584898cb4d4679c0d858e4a0d2d3a3651c60e7dcea8d5982e1019be216e4417fa6688d837869c5c63ca34848ac184b36873ba2de73076d48196d2aae767783b17fe2a5aacd85191c626e8de8e320742c33b95635531359e873172b9bc65d6cfdab0dcedfa5d20133a989594c292eff8b5e9426ce88bc0e278f618a550d1faa90c75e393e0aa682d3dad783ba216a94a8644880e5fdd2ed66e3119a5fe6e05f22eefb6e40f57d8039e3030b4b7646438c0e82a2718c469e424bee841027889d89994aaaae59f5b6d3ee25e66b572be049d67a141374428f1e9e974cb31ef68a40873be311ddff44ae908f9e93ab8cf5b529cde6660cdaa6e8bc8cc99b8cd36712164cc22405201cba7c9c1f9b8f047a5c03afe82cb7fdc469cde7763184b68beac5495518aca8e8a8bebc436ff6359923cf1709baf6e2c0af46bd229adfbb64baa06b126f6ad9cde742f82cc609574905ba6082baa9084591e5418d27e182b1f76374ceddff3ba2738a305a9928defc9131ac0b266479ed8aa994109c6297d42c402267842cc3b0b600d9c46ff18775f842b352a1e9f70dd25fac21055d37919f5b26b6a4b8b078fc694f81c65a8a5a01a02a502a6157b1338045157e6d445ba717652ca4eeb9527af0afff9d6a4891162aedb2355926421db4e4a4c07e9efdf02cef47b3053e65971f89ba9703045ef45842bdc262da982268cbcb402fdd854a56bc2da6f42afacabba5d5928e566a8d0850a739531422f98937d5e357b9ec45fb81e316c36bd7d4f6c3da3c20fddea911b8d835de61213512831acafb0c5459e1f0de69aaff58bc1ef23fe451d39d3f41c08e0e83b52bb37b25afdf8509c0915e533bcff112c91dead9f26ffb94e7de585b51b96b48ce93752612717d7a4ea2f9bf2f73902a401a3df1dcbca4adde6b57e2f0da8b9a0bca132b783ae78548f5daf85e1026f3136030dd851435c153d548b16c19b81298ec1384db4813c777f50f6ef9f60c728b5accd9814e9d6cdfaa4b4e33b550fce0d1dc7af50cad04e95864542bccdbebfa1cf548aec63b13475238bdd53595e2a548b8b9d9918752274dffc683a33fe3147b2574173196636983cd093db9ed926d46b94fc5fd2b1be003d7a2fef13f19f8111f7fa33d0f37f3b2c01f956759427a766c3386e932bb3e55d504caa625131fc1b4a126c647f0c18e17d866eb3451e80548d88dccc7b8f5a37c03f4932e907fa84f9ab12ee40f65d441467fe3cdc58c9705510b8716777fbd17bd63fe5253f17d5fb89618a7169e1dba037a266650c986d7508cbc6809587ec67a426bb449715ec24ff18192209b03566db2f7187c8508b1bdf57696e7b8c74a179f6b556a86132c7e2e04271058b9911a956e7fe9fd34fedd75f3e671991fdcb2c2508911b2f1bba0eaf69412fd2c0bb6f605c3d9be59c9164dfc0dcca2a288ef529df60eef1e983a80342bd153c7684fe0f50af12694c04ae27485e21a80e8f9be2c73400d6de6b8dd2045bc321ae199d848278127bd05c44011cc0d2810a207c2aa95bed99a0026da89ed81bf96a882c73d80bd45789d7ff146a3cd3b283a457f5b063f2c93ab1e7b1c233245fdf4234d47b7519a08e0257e18d8c0555156245d435fa59a736adf8d3c74e5109165ffe3325f838914006a8381250b60801e987d2525cf1ce8791744de9ff5489a4f5478c620effcfc74622f869b62c1e956822db419e19a6fd2b41df10b8eb611129bc10aa4140dfb393eefab7e75b34278c09e2598b0043b26db8a2a91b4dcf102dd9d4dfa84dbd62d23ebc28bdd08d82469c72cf8d749ff274e2a73cd6191eb53e5090aa08efaf2a3638293fb93c663366245ed67965be37e51998fe1559c65cf2cbf1f195411e9e8ebc57a253f14967f0d66b43421ae1cf5a48a7b25135f60de874d689d4b078243e9b851dc14cfaf5d3c360720a5287afca4a187ba0d510831780f6ced7375d5d3611ba62813362d90e3247c51af72a97883527d50dc831560fb8bb685da1c06b6e1c8bff7a34af59630a608556f553db52720db71f68988587583b1770dc455c106e41c9785a8b4ae33e7a76690884c8735910395f137032fd675fb79a6889fc0ee3b34788b3bddce869a135ea5915db3cf5796c21c836d55e2f6f4866246db9bb387f223fa33a36b453941976bd3ae161eb57de316d06f35d760ef998ddffbbd2cc4c16b8628ebf2e3405e7526a21760d167a9a4445d677dcbc0b5c764cb4ab0e4457ff17a656918818d4820dc55dd9ac3e2599d225b105d75132a8de27b673333a09837d7b5e850211533677c699fccd41dd4332fec37ff3e2140f84739f8c5ef292779d48e4a01ef9472acfad16c8ea09151625a2e27b7793b4bfd895c786dca5d7468d60897a6e15e99715b482a69ca2a9c8c171c5d80a421ad97f6592c7f8907ce930fb859cad3cc9cb2b6c577767249e4e227e7394d63e23723e12bc7c87aca076ef79da83849f4511446dca2aa3dd3e3a52814287a7e7ea7230e962dba7e93b393d4883dbd0daaac58fb124ba27ee75704c3bd5ff1372f8f1bdc650b7bf2d99e6dad494e53c81063429a57442f59869ecb1adb853da3e11ca93780f8cc8a87747eee3ff16c43bded2f5c50124132e4f1757c88d1ba13f4bb926590a027e91ae837de609f0c6", 0x10c8}, {&(0x7f00000001c0)="a88fd252d33a621756f641c3d916bb641a739e3886d6e36c59ae7e2ada67dc4a8bd1ec3ddd7c089e093917f643c23174edd739ce7eb8b9ef8f0a96871f55e79d3eb529d378d19d5911419e37f4e1aa019892ca1ddb1b8f9a6d7b9a1b847507c9232ac9205c3081cb3934acff2e09217e77c947307d41b5a3194585800359540d0a50036f1639b7ac123eda2afdb2dd05021973b87b3edf4627321eb56507ce61f6796c6380bf15965db9ff2163b0ee724de6b6836a5df77ccfca272f176966e36a5fa8f7a24921bf1c7e1a2b56db77df9d9b309dd593bb5c2627f0d034f680977f993129a352b88d", 0xe8}, {&(0x7f0000003400)="84a5f6f0a7d063a5d66c0feadd6cee4ba48441ac68076f0ad351e2d7f6d9ba6422cf833ca31d9e9cbac6a9c334c8ce1b7f5e5f337cb1156872fbe748edbee15e4875f3e27d1f9b5403bdd34be0d667964e07fe24b2a850b14800c57ed4e6baa21fbc472af4656f0c398c333afafad2ad0d5380e1253548eb2d273ba1e68fc057ec8e912d2731bbaf50789848f1a87f90bd6d6fd399d902132b6ef7b81fab9e0ef9d91492b4218300d775f051149c3ae80eb0f965b9bff65b221d73caa03d0d5babde74ef2a2d4ecb92f0077bbbcb72bb59e2d32d16881c6bd7ff766d3a09ac881b6e4798507053066db043b99c1c39edf7a2f12f412c5b5fd2da7a0ec6489efde6af91c5dd56110de5aeaf2eda29edbac8fcf6084e27f42def4091f8a2c9c7f8e3aada7e8eb974e124848ab51e8f2e985cd907b91f7c239ecb1ae03bfd61cbe8c13ca2667a74945c540ff7cd615e6dbc3b076a7fc6043c73fc04fe1a8eec2f80581d144a558acc28dcb584566aae417ea90f2fd28338c1df73ef4b90f13c9587240ee5fb99d48072030ba7fa950eeb31546adbb8f81ef88dcd8136522bc075d47f9fe4c17112d191e328e7482942252d4ec0b2d1694a4ffcd174ce0293b3d32fcab8b292f783ddd177b1b8e1463a5891d0b3a89719f44dabfc14bc41b067caa0c47a614af75db89aad8ca28fb3c08ab0e2707c52e953df693f255d56f77747369fd4a8f349b5e4d79eb121a240e4fe0305b411883e49b992cdfb450ce8d94b516ab76f44ee05ab5f8be6bc61c3c9f9de61498d45de37b1172a4543a73338ba8473d3b8a4554f19f83187760762ee3200df41d42200ac9d38ae0d9d6c0f0ce212b22baf3be36bdba882e7e26b08da91b4c204861bba5f5d1e0ab3ac5d11cfd01101e5b34d711ff388893727135f0dbcf4e6588a971101477b594cf966dbf356602536e8fb544d21b66118620db9c10f7dd3a4518c11225bd25761fdce03cc61e27479b517407584db300b6ec9d441c126b0abdee7dae3b0050d189847a19d27e723fddada53c945c643440665c04124381098143a4d0a89279265ab4e2dc1b964c475249dce6c54b6de2f7771f2326055e7983961b3401106f5127cc1df4e3fa9e27aa43bb58d144c82d47700d75b95a4333ee585cd0b71825e0154f76a6f52144da9e21fc76dfee0c46d25b2276503fad1b1afebd3ac953d5000ec18af7106b17811b983e75d3c7795fcc6ab0ea933fe62d899cae814df8434cfd2745e9d8215d52f4081a29569caed3f672dea089fd2c994bebfa26a7324752af57d3089be050ba65e9c8b50195ac449b092688fb6aeff90c11dba6033f0f9e942679be7eea616dd4b3787dbb8687d444a0af1a34c38500155f8cb7b9c60056ac8e53496a777bc5a55b410b5132d568dcf7b52feb6d2f84c5712477a9731263615a3d7f459854d1f9c6c507aadd59052ef082576468de81d68252bad320b25c6622b0e992a31e4901188b8adf3c2fc38e00f5c8a11bd30023810603f162768a1fc5a1f9589dce386a61e0e019107b7f51f3585c9fccb31959a7d4424c3f6570ba4c486cf594dafb5884d9df44ed6214e04f2cdc1f945fb31a3867e2d6a2451e04e31627ded22040e3724b0a575a24e2eae34beece2614d86466bc7e53d9c03a8c4db01a2adf522e28def461972d613a72ad5bf05abbeb1a8de0da928bc2967a75b63bd18ca991ada3943049ccb3bcb8a9e056b2203a90673fb56eb3439c9374d554cf0a4f0bd574389df2ff28efb6dc25eaf3ebf2db87903d6af00091bef21acedf4b23af90b201902b82baa8751f335b3e8fcffffffffffffffce4e49b0a1f2346382da8e86036cf898319a76064de7ed12d7ec81041494951fa150f59460231f030d3664658775c97c5566a4d6688cda0cf233d0d6e76cad3883ba4fbec97159e4aaa967ca37864c79acbc96b629b8618b5dc0300c8dc1bc7d5f19b62d2461f7e66c72fdafd20f7d255bf52e417fe68581704802bb2e75909625049cdf12a143fcdea0ca98928aa50ed8c6c5010a285aeb41049d7ea6d1ce619cdc57c2d7ffc0da47c6b623d06b28c66ae69f52262e53690cf1172d71b3a42157e421000b9efffca2ee37d8d846f59deb54ff27099f7b40549076ba5116022c4aa317032563408f38fdc5385fd79f61001a64e038d13e568468e9f99ce137bd185e9a803a359d4fd036d09747f3e079d086b6c6f5ecfd2606ce12df885864c414cdfa7c7015cd21a87e643d5f6a49a6d4b57d4b22309f7101c4095db2de8711f6ed98cb0c7e30b2014bb91df584d8feb33190e011b87a7e5bf0374fe7a0ac7727dfc7bc7c8be3f2159d9f49752b53dcb366047054707e302db20b20dd65711d96e37c41a417f924e641d4939f97b70c54d532d6c8c5da6bfc2d2d07e9329cbc1c0daeede1ba9a524c342e1f6b56eab27810dbf18e9b410cb15473f2aaf3d50af1337460b6e7fa141b3ba8d755c6cc87702ebad2bde9854f855e1fb1bc8329f102cfcd7b7ce20285989cdf054946bc792aab4f380094e3bc76e13de4498abd43cb889435fc6f4f0203d3d5253c5ae49a660bd93a0971d3dfd60a4349cbad0a50ed144b38eecaf78b6a22123894b6a2c8673fcec50be1d637dd41351a49afefa59ff8102451acfbcf2a76b497b3bb7e3e8b01c06778db82b561c6e778a64cfce232d3461cfd59ea773abce039dd119de514a3699eba5da68c06aa33beebc520e83dd685087e33d8e7cf0b52ef0d175ae776d6c5b27f2cd126e156942e1780712da4a38279a7b4a6c952e6d46f3a4aa1eba7df0df1ac7f850c18d6d908c15d0098f0dec1d2994ea022168ad8b9f6d0feeab54291cc3ffd5962ac991171b6b05db31de20f042d8a82482bfb2b2ab8bc817f812c6a6b40a1259d7f50296b2980c64b068ce20d044f2f2a0f69f9dbfd41ee867c73b678f67d207be4a5040ad6ddf8a9acb81dffe1351fac31598d8f8b8d7e41b1dd80253f8237cda6eecb7f5efd08a60e96b5e0961a6313d0de713766a9ba95f46b0cad71af1155ff79c09e1037afab19fd54a660482d03c3d81ed06d23cb6bc9009220a44dab0c1a562a708b4738c1e4eb71f3d992a1436c0a6d76f70a42943295952a8c7e63dcd555045470897e9304c5902bedc049eac8878f40ad7271a87c8c432ef1ce936d85cf71b8df5fd29400447e5766d8d262bf7edb4a5d32dc30c6a2eae2b0febfbdb8f907195e0dc922d0f616256bd7b466b682cce73a7c8bc9af4a1017ee5f263f6538a9190b7356d3ccb65b89d7968cd716902c1bf9c5c1d2fc5bffd4b8f8243a1b43dd5991e53c02a2b81c8ecdbd4aa7eb135fd6c8bf62482e0bd7cebc645e10af0026080c6d8f04da4ebc866f863fb1f7523d047ff2e0c41df0d49a2a21d7a6df5872a634d6f8bd98c84031e9cc93abbc0b52fba7810b8e8124419d720d527df0d72357bf371a57d6ec13cab55bf074745925ef7b6d1d709f25208b3c898146e2641a92c7a30511bfa94a2cdd47fda268d6166ccc4b0c34f2cbac08d23abd2b1d9bceab3d065de9e2386d7d0c036c7982293810723c055e9bab783c432bf8ba9f9604e3c9e808bfe7681e7258f4080843dcb78f4c36f4982e22e5243cc5bf2a3c7cb0e368aba9306d32c4b7cc942979253aa8e463ad5ffeb3daa807095ca3a5aef099ed79fa5db1b1be2d9822581a3bc5f3fcd3298fcff24433855c8b2720f641fd8416872672802a3e0f04593eac890cda2adfc48ded113045b9fc289423bf0121bd6784463de750fffed8ea594be60fd9346fe7225c75304f4037ae0a477851c01628d99f2bb7479e2422d392ba84d77e67aee43d79cb345712524dec2896623fd983ed83132c065506b7a5a09c5224539b813b8238fa7dcb57bcdcef9dfc1b287526a37cf6253247720d51ff3d80033c6c3bce097226e9755912cab70f86cfad99c457abfee9490c9e51cee46f64c40d0cfbed0b7d3a557d93aaf0bcd1de5e48302719cbc8f95ebe4dd3e486b087b2cbbd5e55d95425bcb060b6865f02ae4897f287433641604331320edfdbbf6223893af620552a24549ba0de97e216d235cc0c5be0f3b29fe85b59f033408dd6780de4bf9212ef22cb3b55fd647f7d12c2b5f336531b61bac88bce9093b56c3a4f2d3cb72c45193a434fd4b6d10f93662aed32c5552424ff2d4ddd0ef4f8b2338bb197dee73ce859c21e6de08631a26985fe86731c7441541af1de81d88c9d1fe9a0435975cd48311cdbef30d87c477bab97f9cf57333fd1c2bdfd4088709b59086d241da7af0b784a73e5e41aa47d2e5827932b73e9604913fd400e6eba2c84e0f49d1531751b9d0e6a98dbda502b51c9df9fd49ef31d3ce4a0aa71f2ee5e39b574b4fd732500bac40bc71f0ea2b2ce238176fa65b3c3375e2a2d6cb9f2d985d781a765c44894dc0bbb6e763ad8965ef9276d03de2815128c720df9d3a0eb59afec5b5891d58be0c2ce7f96b6f26bc95329f14d6b3b425c548ff221918df9a34c6a9a57e3b21914957093d7ed365a1b2ae81bb6b28af7caf870e8f60eeb4a595927c05c5524166933b3a9693514eff43010c71e045abc1298d223c71f32bbae44afde4a010a857299d4ee0615fc4b9f41a708c89fb3b598301ab4da8923635754df07e1f2b4a8702002f5ac4bd0e9ec191e96febdd26652176e95b877d52854acbfd57769ecb594a0254f8efb96518ec3b9788a9845a222f315223d7b77a250a56460db57d7ed9a083b5c87608feb7a622a9a57796a4b32023718181f4c22b50a8fbc4503d7d9adacb73df824c979a8327145ff10c232dee9a90d", 0xd57}], 0x4)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000000))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x800000000000006})
sysctl$hw(&(0x7f0000000000)={0x4, 0x11}, 0x7, &(0x7f0000000040), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000140)='./file0\x00', 0x1000, 0x0)
r0 = open$dir(&(0x7f0000000080)='./file0\x00', 0x2, 0x0)
flock(r0, 0x2)
close(r0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
r0 = shmget(0xffffffffffffffff, 0x2000, 0x0, &(0x7f0000000000/0x2000)=nil)
shmat(r0, &(0x7f0000001000/0x2000)=nil, 0x0)
open(&(0x7f0000001640)='./file0\x00', 0x0, 0x0)
msync(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x4)
syz_emit_ethernet(0x36, &(0x7f0000000180)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @broadcast=0xac1404aa}, @tcp={{0x1, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
mprotect(&(0x7f0000b37000/0x2000)=nil, 0x2000, 0x0)
mmap(&(0x7f00003f8000/0x400000)=nil, 0x400000, 0x3, 0x5012, 0xffffffffffffffff, 0x0)
mlock(&(0x7f000038e000/0x400000)=nil, 0x400000)
munmap(&(0x7f0000520000/0x800000)=nil, 0x800000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x1c}, {0x44}, {0x6, 0x0, 0x0, 0x3b00000}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_GETMAP(r0, 0x40045700, 0x0)
sendto$inet(0xffffffffffffffff, &(0x7f00000004c0)="93c22c763d5a2bc64aa4f38024c39b9ff56e2d40b17e0081a206cae97d2f03610b9cf59ebe435e359aec7f55ee28502b4b11eb8b6a1a319b644f55ecbdca4aed29e112bf91a8d522db74c2c0ef08e3d3dd6993586c7d66e0276fe67fd422a04152e4c09f6efc946bf5457b7ac24a85bfb96c4d06277b31013b79b4ffb9ae3f45741de0794b421a35a695af8b735d05d1671092a0e5b8f112615ca99607b34dfdb3bd4f3121d5d71c4435424f711e5d2e26562fad69298f299743955db300e1010bfbe8770d09cce765450bf32306b9124af8c79d261f55e8a9adb5b7e9052bdaba276bcab64406c86de0e2d629ec4494edfe7101e571d330b005b0ddce7d73bfbf4a21cd9a7d2c6be1163a3b636798e7c14a4b158970e50cac944c2ef8e6ceb0aafec7e2a84550ced0e1f445bae4ed1f9f0c39dd3f4522259154e291d3ca1fce9f60bc79b2f4500480d979271969b7b9aa80749e2ddd755ad36014532cd36e3834cb189f55ae628817a2cda7cfb5db6aa6cacf1379a51313048f8fecff833c3cb4058dad90a1e29ed9d36d24eac1ab465095944ba6d3a8e5b21dda82e44c2e9030ea6fd0b9b410009704f1443115416e9b25c5bb99914439e29bcebe241fb17d172bc07e3b9b5ac624216cf74566a5516141a8da7629618f594f8b73723ef7e7ed4aeff23626a574a25e761acf5f47606e2f4af11e33c745b484bf51b5c4e3ef2551b28f281abfc3ee0d72f42a72b1e5b66d4ef30de2401a80b5f262e025742fcb89b4161f95a45f69c377df92f85ba49198c1680f5c76e5195e3978fb3bdad2513ac903fdaf394f92008bc20382204c86138615f21a7eb48e16387e08fda44a7bd7f6b2f859a4c0ffc7e03917c87363a7620cc91e5530048ee18c4ad6d316e7079b688d57d8dbae0084e47d154faf884ea091f7c8c13d47ed2ebf2bef34aed528635525f37cf5835a74dec98bc8200bf9c342db4e1f94386f7e84c667d15794525bc533b82dfd397f5d169009aade6aea6531e8fa6c9d9640d92125a684ab196e42bbf31d905f082409dbffd077105ff87febd4aa283806d04cf47dce1c5cfa1dbe606bc7f54f0c7af6d4307013f2094555fe1cb8b7445607846a26f40c73bde19ee55bfcd9c9a6608ca815699937be289226da36757e4d0c1f7b61e5bdd315cbfc6277ac1524e4c6fc3562643fcfc7d0c33bae75d1e0e95443de5a407641e097a8e121921e8560cac9519be5a1e46595c6ec8022dd6650544bf6d81fe5791e626e44b40e6e2400e68326462ec4f3b5f34079a7d379fd6c02e315b748351e4ee90a1943ec18c6be206756d894a6ed59569c01820369d7c9e36fc80a9df90055d71163978ab0a7abdb3c26537b423cfd5e216f3fd9cc4101c80e4a151445e793707712c289dc0a7669aefe0be752cbfcae038e61c6c5761225ff8bbf4cff07404a6750a488bb28d00ac2912593108af820e379f9fd0b2f32a848b4f94f21900a3405d33ab7137e6978737cb08c995dd5fe89795b855d6922f5ecff1a546081250817845d00dbe921ac823a4ac02c1d424415ee80ab745ad868854eea26482ff519825363c93595cff7c73708ccf15cf84de59bef03d3380515bc2b31c159d1afef49dd0b16c73dd014cc2a88d07a8a0ab623d6541085543d5f1ffb2181089645efbc4476560dc9906c8ea619396881a562701f0b5d6bcc55d459e65ed539f169304c4ef9dba729d690c4a8e0d3115da7b7e3db81edbbb4368d6787fd27922a4398c7afc618b47a4186ad8c6065c3cd3533016fda83c624f8a428d9ed409628f35f042881debc49d08703c817876c0b01048c439ec31df3984f8e88f9f7eaa4dd53e6173226fa45ae8fbfd9bda32e8c7b6f66eecda655a9bf439a99233438b2cb18b97c97ece5658e2bd1ebd09165c079a4f4eb2f8539e1b96ea00008661d263b00a581b576fa80d5cf3db6befd1951744a189e0c1b0d8c9c5361ee2b143fae3d96334198c8cc7a9992bed194cbf918f9421523138094154aef8f2e0f46a4905607189b836935691b399481606051d067082a6bb94837f71ed34be8e6907b48194d4491b229e3cb961e26f5cb00ba9f87301dcd71d742f87e22879cc7c64c2fbf16fc98e9f012bb9bb9b3adb6c11a471e84126e834015207d259b65d4958412b73f4c445bf34a844560fa016e942eef7e8f60862cebb6852d3f4f0e661088c2e2136824ce4e6bc9ced475e2043c33e803ffc326f0fe67fc0ebcdad4ed038ac1fcf3822877aab1cb126e41a58075003461cea61b435d62f78b8db79037d099a60ae1f59874eaae0aec7cffc59318f116471e7a9e7fc1a52c291e843606e08e23867a1e52561f9a760410cf127ac57115adc6108ec6f632ef708591d926f54d1e2110c77324ee69b5c1cd29daef3b64e6d56cf59946f7c40ed2a1877fa8a89046eae95b8e34efdd06eee60a54b5d8ec7396fee933948662e9e0534a58b625d16a8ed00d28e33814120c4b4c106d425056e5e3ed50a96f454f1bbab5d42fbe585f37dac888c5900aa58d0ccbb49f3d172975275e58a2c6bf9f589e4659d7a8859720cbde5a62d3158d16f1e82fe1776d307ac6eeea128903a156365c720a96667680caaf893ea579a982bb493a3ab1d1bff5fd625cac3ecfaa703b482fe6179df39365f728faffa6337e8f56ce18a4db9aaa9724935c91dd46597b05f8bd7ebdce6a2e36acc57a0653bce8f741da346d0a92ed69ecf8abc95aba9ea9f247b2783d2d6643a87aac5bc11b4170626a8bb6c6c315fcf6f85ed0232361cd31400d62a0e67aedab0f417899dd1057467dc1fa0591028867aa427b795950d43668f5872a0ac93f81fc6100fc7b9f25df23b03dcbaca4f420a9774d5f019c7a5c5611d2a65efe7e70b9ded96089af3c1535808fbf67614682d0fc872879d9f4b2451a863076b9b81182859156be7f94125a91470cc73d984f31a4cb94af9d8daff62ea72d46765ed8da24449e5b09e4123324de84fb923932ca53c6f6cf1e201dd0bec388fe4242bd50f6c2140a6b3cb7c314061b83f4ec2313b085d373ce9ebddfa066bead13bcf2e2c75bac1bcf5e0934f066c0ce2d5427aa2d76141097bde54564f669fd1515e096a977dcc8fb442a6847e7da0cd4828605dda3e74fd2d2a8320431c1a8b8ae71e0a669bda02327614a3223aabf60196d6db56ea1b761475d270ce48180b054df8645b87f8a29624ffbda0a34f11a5806772f5070820c784bd470ce5edc7317190e132c0f2c2a766998bbf7a7125498c5677decab01102fe4bec5c779a9c3f07a71ae02760e832da98b318d1d2f5fed4c97e56f72aa08d74b372bfbd6ddd4667b8746b6d8e0fcadfaf146858c1033c707471f735f5910de13d18b84155fc41bfec3d4f954029bd3c05be7e739af5d9ae5840f961bb351c5135f48ac27e6624f4ff7293d9bc45bfe80344dd1e1859cfdce7be669b1e2774168a09b67e78b7db692cc23d7f9d059d2f7961623ec675f72c21e6653e717112a2b41063b7b4004fc7c86ab112277a5a1ea4c0e9edc58cc4b0a03beecc239a51be4df07018dd8f50ddfe773082b8aafa321929416776ee99c4d8b509f721e331ca9027418389b34c3d3b2d3f06bb2f26ee3d78b379174d86a1078df365070f95cb776fb5bde2100ffa806df17c9bc5957aca6b9ca597c312a9abcec285cc6e3ba969e125c97e66fe1e9ae98e1ea11cfe7fafb74f7a87ad2f50ff2d5a5b8745a58040bc4ede38b8fa381e62080fc0f95261493604bec76a546ecfb4bfc722df74d18dae2185df2676e1d6b1566549a47631fa5a75573647eb181f4c3bdec39510bf4aedb7a1758e374d7f2c085c4bbc689758d7e1f627ac5af80e5931aa618f177a3b55a9e222ea91dc2b8e17a5e5353c8ed3f4821be702d28102c0b7e84f01562808bb3cc4cef15bf2ed25e6a079297af50d545c8cfba2d1cb70c5e8818c24894e88589fd148c83c933f7022c686e635e84f0e94e240c1dbbc8b156bb3f3e849ba1e7084ce04922494ab6e19cd673985aa6e999a61a3e3b6d6d5d7581cf2b03b77eb88b3a4a09d011b1df756cb2862932b570a9474db23f755819ff0963f6891bbc6bc9d3d31c92eb04221fe4d1e3ebc3f1bf036bff3723540bffd2cc810d3e7ae5da5adaf4af671fb89bfdc10bf55aa1d94a67ff47bde0b40602da88105c71929b4ba07d3248cbfa39c8aa9c9ee8e64cfc3b33688b4b1febbbf76620ded59ad95a0111937f2696c37355dfe71c41ebb1d28a9d934e7fc98ea1d14ffd2db018672836eb700820bf7c1f396aa643e0ee9597cd5c9451e8ece33b143b104d7fd3004feb3d2d534c70bfe9dbe357597195402bffa9756473fe46e8994aea437a998e9f8a2b648f28099a4173c27832e5df6ee7af86e4a5ad952c4c09a3a3d67c8f7c3aaeea267f0f4c1a55e165a00c7ecf244a13cf02a833a51e9c017fe88be6a2d2342a89e1e2827512282c096abb56656cf247a35a47ac86a263e3597055cb3fa23aec1be18dff37cb5beb169c88db65eb209ea5b45625d901dca5c35a88eeaff040eba6ffe6b69eca639fe4c499df446c8d0f4249f7043ddbed0f6d6311f841eb58c12816f41aefa0d1dc77633073fcae6dbb4cfd0a246d82bebe515114148f048cb4a3fb86b6eee6f9b737a3977513370ccf8c5fd587d24070e3f757df95ebe343734873ec355cc4d6fb9900cb1408498ecd0ba0a718fcf1a57cf68a704ae838f6859ecba40e91c0ffc913a93d974287cee8acead4bc5019c74cf24769484760fa9b7bd3d3ba6cc3ffab74ab366d2bdf528076b9e1ac2c0426b3697e635413b7a9d310e38f2cfdd9159f2de856ef4427c31d79aeee003e3dc47ee749a940f10653fa9483a46af5d158448030e3ed26e68cd4933620c86da428d06de6c78c92e4a3abd8efbd27378be1521275e0b1216561e932f3b1252cf7e362a948bb23fa00d856e3156589f041fd0cf33f46fcbc32ab5b2c274e176ffaa0a4a9f9716eeee189c89640ad7144261dbc805d476fb5684ef4b3eab9666bf5e8d43f48ff0907ae82ac4cbea8a0810b081b7f9640c6e1e81e77dcd3bf0c2118bb3c1c78d666ee634a9f618ac73ddc6f46846312cb5bfbbbc39318ec4a6d9d33924ec6415955fe6abdba58b50b9338705331f3362758ddde2d562fcdbbc09da8d83abbc12a096e7acb8fadca06516b15df0a0bc2eff8943eb7534875578e72e57260897162f07ee4b1ef8208d6f79b7fb186f71a2e88afd9b6dadf39313a9742dafce856ac736a619477fa12466ce2c2858e70796596b0dcf773ae81236346d38c603a9e5b83835690eacb157627385424eccd649e208fec487bb12bc1ec2b5425577d490b869ff71fc63dba2785e3cae5b64032d4d6590e7f2e7ced66867321cbe117084cf75ff553f4f399a15830ce94cc98d927437695bec5f644c3e57b6f1b767e908e81b13aa9468ed0eeaeadf3f5339f4cecd2155b1f979d6baf34c09b2e98d678275a5f8870668e9ea3385397eacaf15eb76d8d0a3a4a1a2d3dddd4a8544b88d3cb123b215276ca1c03188474a3723b86d53353fc47d72f55e202431191d6947b0dd8318ac3551eec83e84f72ae8609c708a4774dcc5ed956c4271062cca4ce606275e8a97432adb3f49dfe96ed4ea81ec4d753bce10b30dbbe72700976ef0cd3ce784e5aaf08563eae213c5b73ba48918e9faa47740998ba16b465fcd30b3576a59027e3e7dcff5695b7bd8498a6876136316c2bcbb2fd5d8295290c9a529d08be66bafdba581e39159d", 0x1000, 0x40e, &(0x7f00000014c0)={0x2, 0x3}, 0xc)
execve(&(0x7f0000000000)='./file0\x00', &(0x7f0000000400)=[&(0x7f0000000340)='/dev/pf\x00', &(0x7f0000000380)='\'{\x00', &(0x7f00000003c0)='\x00'], &(0x7f0000000480)=[&(0x7f0000000440)='/dev/pf\x00'])
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f0000000280)="0400000032da2edb097f2741855fd288fd96e3df953d8910a08e31d9047585bb7b7800000000000000032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6891412dd29fa61ef723fca8d813a17502005f8913e94d2975688395abea288bb62c21bc4691fd24c1716d5fc09542a0337b01de42e800080000e8b80b57a3916aabff1d5b1b7fc3ab175559766937e7af951e5025af354d0761af3075e80b18", 0xbb}], 0x1, 0x0}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [], [{0x0, 0x0, 0x20000000}, {0x0, 0x0, 0x0, 0x100000000}, {}, {0x0, 0x0, 0x401}], {}, {0x7}}})
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
sysctl$net_inet_icmp(&(0x7f0000000000)={0x4, 0x2, 0x1, 0x6}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x30}, {0x7c}, {0x4000006, 0x0, 0x0, 0xffffdfff}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x214]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206919, &(0x7f0000000300))
r0 = socket(0x18, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f0000000000)=[{&(0x7f0000000240)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066", 0x19}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
recvmmsg(r0, &(0x7f0000000000)={0x0}, 0xffffffffffffff4a, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x84}, {0x3}, {0x6, 0x0, 0x0, 0x7947}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x4c}, {0x4}, {0x6, 0x0, 0x0, 0x80007ffb}]})
pwrite(r0, &(0x7f0000000000)="f94c4c49df016fed8d1a02930000", 0xe, 0x0)
setrlimit(0x3, &(0x7f00000000c0)={0x8001, 0xc382})
sysctl$kern(&(0x7f0000000040)={0x1, 0x47}, 0x4, &(0x7f0000000080)="1daf63b25970aebd36020d25e1b10cf3ece6b8be02", &(0x7f00000000c0)=0x15, &(0x7f0000000100)="c2d24c9d34fadd0eae82b8e3168fa740a37829960f8862294ace4e16e103938e10e9fef2d403c0954d8babb49abbd7757e100f97a12aa77f902737ae02664eb0c56d03bb914b2b2a4c4cffe51a6e41cf38d5db8c47370140fb18077b6f8e963e39233b5a0e053e6c86179c589f83b7fa0105135c0b110bd373f3850f556b04ee1ce94eb6e707ac4ea0df9499463f04a489a12e300431c0e42d390839498f20412301ba6400f392533db65e70b870ff4215a0c879ee965532ec5852910377cb30a7d9cf5b1184b39d9d8c7dadb9d96aca5c946e6f5c5c18b50a", 0xd9)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000580)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000000)=0x7f)
r0 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$VNDIOCGET(r0, 0xc4104603, &(0x7f0000000180)={'./file0\x00', 0x80000000})
r0 = socket$inet(0x2, 0x2, 0x0)
shutdown(r0, 0x2)
setsockopt$sock_timeval(r0, 0xffff, 0x1001, &(0x7f0000000000), 0x10)
writev(0xffffffffffffffff, 0x0, 0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000180)={0x0, 0x0})
r0 = open(&(0x7f0000000100)='./file0\x00', 0x200, 0x0)
mmap(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x0, 0x810, r0, 0x0)
ktrace(&(0x7f0000000180)='./file0\x00', 0x0, 0x810, 0x0)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
pread(r0, &(0x7f0000000100)="1a", 0x1, 0x0)
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f0000000040)='c\x00')
openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x80}, {0x80}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050200000000000088000701000000000000cea1fea7fef96ecfc73fd3357af96caa0416e74f376336acf00a7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c894303b22f3105404f36a00f90006ee01be657ae00000002000000000000020208a371a300000000000000000d9b51220799d716f97b000000", 0xb1, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f00000014c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000001400)=""/149, 0x95}, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSETAW(r1, 0x802c7415, &(0x7f0000000040)={0x0, 0xffffffff, 0x1, 0x8, "346e8440c0a6d7f28b1550c987e78e94756854a9"})
writev(r0, &(0x7f00000037c0)=[{&(0x7f0000003a00)="3c9e7f5e086e71e9619e8c5c758c5e6a8d4807183d2af25f15f1c14a157f67e67b681df2595359be1beb061d7244f97f70784e39592c9aadee0826bc0a16f49c9c510c06925899d22f95c3f0f67f8c61c53333b6dcd862c3038e420751cb74193b6e38d0079b8501a8d0978e72ac8add432792aba88677162554205ef55c97acdacbe6864c1d9270a1d984d72be66ab9ad295a9ed13aa3e6b7d7a390712ec55a4fe92941665de55e3f34d0756dc508d543414f3d7e0c00bb6a485d09f153c3bad81494924132505afa6e1bec20f5bab394c45f31027b3d9b9c195f1c71e4942da229845c5a1f16b060708cac04", 0xed}], 0x1)
sysctl$kern(&(0x7f0000000180)={0x1, 0x53}, 0x2, 0x0, 0x0, &(0x7f0000000000), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000300)=[{0x2c}, {0x3}, {0x6, 0x0, 0x0, 0xff}]})
write(r0, &(0x7f0000000180)="5d526a9208ff69923a366b51f0be", 0xe)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
open(&(0x7f0000000080)='./file0/file1\x00', 0x200, 0x0)
setreuid(0x0, 0xee00)
openat(0xffffffffffffff9c, &(0x7f00000000c0)='./file0/file0\x00', 0x200, 0x0)
rename(&(0x7f0000000040)='./file0/file0\x00', &(0x7f00000001c0)='./file0/file1\x00')
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000002c0)=[{0x64}, {0x5}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="e2dcafccd1a6ca4328aa0703c719", 0xe, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
fcntl$lock(r0, 0x7, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x1000300000002})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x800000, 0x0, "000000dbddb97b00"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20404000, 0x200000]}})
open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
r1 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0xc00000)=nil, 0xc00000, 0x0, 0x10, r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff})
r1 = dup(r0)
ioctl$WSDISPLAYIO_GETSCREENTYPE(0xffffffffffffffff, 0xc028575d, &(0x7f0000000040)={0x0, 0x0, './file0\x00', 0x0, 0x10000, 0x101})
sendmmsg(r1, &(0x7f0000000000)={0x0}, 0x10, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x1c804386)
open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x4)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc088444f, &(0x7f0000000140))
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x4)
clock_getres(0x6, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
writev(r0, &(0x7f0000000080)=[{&(0x7f00000001c0)="0b8f152b361e2deeef50affc603853e0206aacfffbbf7d80f360dcc904500ab391d4f767c26d3fa2105624f1ac96ef3128dd014ab1f9b07af1697a4774f85cc13dc606578151d7874981a8d40a3b7375ecce5d80729a82efe55ea5e280ff3746b9a790c2e33aac576c2a1bb6ff37eb3e987d06560e512f07bb10882827620b719e5e61d2c30f1aa575aeda01242985c37144c8d22b0951c6114853170232ae8e7f44d5d6c806b2027156873f235383f7657abb00708f37614c9b7e42206579c0d55a3efee2bba8291714f42615dc7b71d38a2dc59776e67d0fd3c07d2d42004596b9ccf3ca29dd64303671eebc3a6a2a409acb2be6ef39ca92c0adfb2430371e78f5b8e412c96d01e275d2033b07de0ce210e0be573895a0878362a8c57c54c8b1520e214198a931a581a26892913d0ac32dcaf8eea542faaefef37d17477740eac645f2b310b4c9442c45019100b8c5009ed565d5872ee0ec1cef43db10ae3d41cc32730e88e449507daf1fe7c002b2cb7ce0d56d0e947b6a3fe7b4f9d5e184a2423fae527f17dcdba8019622391adde395ba653413f50e43c92fda925b0a2139871107c94a3cb792559e363917b19f577e1e1d7b35977e815dafb8c26c4c98c7972fe7db926c0cd3c08d1fd9", 0x1cd}], 0x1)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000000))
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xfffffffe, "000038004000070056e9af0d76d9deee000000cc"})
select(0x40, &(0x7f0000000040)={0xff}, 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000000)=[{}, {{r0}, 0xfffffffffffffff8}], 0x9889, &(0x7f0000000180), 0xe51e, 0x0)
r1 = socket(0x18, 0x1, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0xa}, {0x25, 0x0, 0xfc}, {0x6, 0x0, 0x0, 0x2000002}]})
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r2, &(0x7f0000000180)="d00000004000000000001b000008", 0xe, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000480)=[{{}, 0xfffffffffffffffc, 0x64, 0x1, 0x110, 0x46b}, {{r1}, 0xfffffffffffffff8, 0x40, 0xffffe, 0xf57, 0x8}, {{}, 0xfffffffffffffffe, 0xff, 0x2, 0x40, 0x7}, {{r1}, 0xfffffffffffffffe, 0x89, 0x4c37789028b5b6b, 0x7ff, 0x5}, {{r1}, 0xfffffffffffffff9, 0x55, 0x2, 0x3000000000, 0x800}, {{}, 0xfffffffffffffffd, 0x10, 0x40000000, 0x0, 0x5}, {{}, 0xfffffffffffffffb, 0x80, 0x1, 0xffff, 0x6}, {{r2}, 0xfffffffffffffffc, 0x85, 0xf0000000, 0x7ff, 0x40}], 0x0, &(0x7f0000000280)=[{{r1}, 0xfffffffffffffffd, 0x8, 0x1, 0x4, 0xb}, {{r0}, 0xfffffffffffffff8, 0x14, 0x2, 0x2, 0x8}, {{r0}, 0xfffffffffffffff8, 0x2, 0x8, 0x9, 0x4}, {{r1}, 0xfffffffffffffff9, 0x0, 0x3, 0x0, 0x1000}, {{r0}, 0xfffffffffffffffc, 0x0, 0xf0000000, 0x804, 0x7ff}, {{r0}, 0xfffffffffffffffc, 0x32, 0x40000000, 0x8, 0x3b22}, {{}, 0xfffffffffffffffb, 0xc6, 0x2, 0x7, 0xfffffffffffff800}, {{}, 0xfffffffffffffffc, 0x8, 0x2, 0x800, 0x7f}, {{r0}, 0xfffffffffffffffc, 0x25, 0x1, 0x6b76, 0x8040}], 0x200, &(0x7f0000000400)={0xc88, 0x6})
openat$null(0xffffffffffffff9c, &(0x7f0000000040), 0x80, 0x0)
setsockopt(0xffffffffffffffff, 0x2000faa4, 0x2f, 0x0, 0x0)
r3 = socket(0x18, 0x1, 0x0)
setsockopt(r3, 0x29, 0x2f, 0x0, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x801, 0x0)
ioctl$FIOASYNC(r4, 0xcd60441a, &(0x7f0000000240)=0x6)
r5 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r6 = fcntl$dupfd(r5, 0xa, r5)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x2, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{0x0, 0x0, 0x0, 0x0, 0x0, 0x1000}, {[0xbaec, 0x7a2, 0x33c0000000, 0x20008, 0x7, 0x8, 0x10000000000, 0x10002, 0x7, 0x0, 0x1, 0x4, 0x6d, 0x9, 0x4000000004, 0x2], [0x4, 0x1008, 0x7, 0x0, 0x6, 0x4b6, 0x5, 0x200000099, 0x204, 0x80000000], [0xffffffffffffffff, 0x0, 0x8, 0x102, 0x7, 0x194, 0x100000000000007], [0x1, 0x8000000400002001, 0x0, 0x8, 0x46c, 0x6], [{0x4, 0x0, 0x8, 0x7}, {0x5, 0x500000, 0x3ffffffd, 0xe290}, {0x2, 0x1e, 0x2, 0x1000017}, {0x7fff, 0xffffff7e, 0x0, 0x21f}, {0x8, 0x72, 0x100003, 0x3}, {0xffff, 0x4000003, 0x2000008, 0x100008}, {0x6, 0x0, 0xfffffff9, 0x8}, {0x4, 0xf9, 0xe56, 0x10000003}], {0x6, 0x2000002, 0x0, 0x8001}, {0x8, 0x4, 0x9, 0xc}}}, 0xf2, 0xfa})
ioctl$FIOASYNC(r6, 0xcd60441a, &(0x7f0000000240)=0x2)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0xd, 0x0, 0x0)
clock_settime(0xffffffffffffffff, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xf8], [], [], [0x0, 0x0, 0x0, 0x3], [{}, {}, {}, {}, {}, {}, {0xfffe, 0x0, 0x4}], {}, {0x0, 0x0, 0x0, 0x1ff}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
symlinkat(&(0x7f0000000280)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0xffffffffffffffff, 0x0)
r0 = syz_open_pts()
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000001180)=0x10001)
openat$wsdisplay(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
openat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x200, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000000)={0x0, 0x1fc})
r1 = open(&(0x7f0000000280)='./file0\x00', 0x0, 0x0)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
setreuid(0xee00, r0)
r1 = socket(0x18, 0x2, 0x0)
setsockopt(r1, 0x29, 0x23, 0x0, 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
setpgid(0x0, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = fcntl$getown(r1, 0x5)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x4, 0x1806, r2)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
select(0x40, &(0x7f00000000c0)={0x670ceaf0}, 0x0, 0x0, 0x0)
pipe(&(0x7f0000000080))
select(0x40, &(0x7f00000018c0), 0x0, &(0x7f0000001940)={0x9e}, 0x0)
close(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000340)=[{0x1}, {0xc0}, {0x6, 0x0, 0x0, 0x100102}]})
write(r0, &(0x7f0000000140)="7cd23fc0aa17dddf7830faa1aab5", 0xe)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000200)=0x8)
writev(r0, &(0x7f0000000100)=[{&(0x7f00000000c0)="1a6d4c0203", 0x5}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc0014462, &(0x7f0000000740)={0x0, 0x0, 0x0, 0x5})
seteuid(0x0)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r0}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCGBLEN(r0, 0x40044266, &(0x7f0000000100))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f00000001c0)={0x7, 0x0, 0x2, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x800000000000000]}})
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5fc82b297ba1ab5b23116730f144000000000000001f1306000000000000007d026ba8af63ff37282902", 0x62, 0x0, 0x0, 0x0)
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x3d}, {0x74}, {0x6, 0x0, 0x0, 0x8000000000100}]})
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r1, &(0x7f0000000140)="0000000000000008cfccfbcbd4a2", 0xe)
mmap(&(0x7f000011b000/0x1000)=nil, 0x1000, 0x5, 0x2010, r1, 0x7ff)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000502", 0x4, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r2 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000500)={'tap', 0x0})
ioctl$BIOCSETWF(r2, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x3d}, {0x74}, {0x6, 0x0, 0x0, 0x8000000000100}]})
ioctl$BIOCSETIF(r2, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r2, &(0x7f0000000140)="0000000000000008cfccfbcbd4a2", 0xe)
writev(0xffffffffffffffff, &(0x7f0000002d40)=[{0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9)
mknodat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x833eee594ef524ee, 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000000)={0x2})
symlinkat(&(0x7f0000000040)='./file0\x00', r0, &(0x7f0000000080)='./file0\x00')
r1 = open(&(0x7f0000000000)='./file0\x00', 0x800, 0x80)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140), &(0x7f0000000100)=0x1)
mmap(&(0x7f0000ffd000/0x1000)=nil, 0x1000, 0x0, 0x1810, r2, 0x7)
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000001940)={0x0, 0x0, 0xaa74, 0x0, "4a865919a6d17ffd6f7bb8dbcaeb31aa7b8c0a8a"})
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
setreuid(0xee00, 0x0)
r0 = getuid()
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
chflags(&(0x7f0000000100)='./file0\x00', 0x0)
open(&(0x7f00000000c0)='./file0\x00', 0x70e, 0x40)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
writev(r0, &(0x7f0000000100)=[{&(0x7f0000000040)="2321d74c3fd72000", 0x8}], 0x1)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, &(0x7f0000000080)="2020e66320650a", 0xa086)
execve(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x0, 0x0)
r1 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x153a, r1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x1, 0x10, r0, 0x0)
utimes(0x0, &(0x7f00000001c0))
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f00000004c0)=[{0x0}, {0x0}], 0x2, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x34, 0x0, 0x0, 0x0, "1fb02700c108000000000100000800"})
writev(r0, &(0x7f0000001280)=[{&(0x7f0000000040)="4a51afe914679245f25794b3b595392755472e5665cddde500be14edb43ea95a56ef5408a08ea19cc6379e78497f479daaf1", 0xfffffe6f}, {&(0x7f0000000180)="deeef0f744a431fb19aa426402769236b38e91629f3bb7656a947d36ccd33df0e3bc2d6db8af0b71e1eb2dece9639ac119c0a110071f43a7b2c1c57d9c4ae2b840c62a8d798ceaa4f2003f715e706b584c986c11752807b7daa906b6d67ba93ca2e019d6f6c3ae9841a907b3e0e1eab1a2668d4ec30c0ff4688a79be6c70b8a99b553f1fe1ce386e5e5e2c72d953497b9fa420f72a89cca617cc3af2e83131efca2a829d8d618d5b5c1473cd3711a4146f593fa449508139e76e46ed0b192d6ad81bd3dca44a504f30c57883c9e607755ad649f330ccfdd5f235e6cbb254f6b2bd87cf8082d364dc0e9d8df3c48fa76e8742f33a24df512d8a95716d25d990b9e5bcc6dda53c1a4fd4da23013451e28a7cd284e5300ee40bbcb0b4be5630c8b6db716437af17a4e37b6446b61e2aaabe2f86e29555940b0ed8d20806ef8e830a299e8ee9927d9a8b2105ed4634b8bc8d8deb8cf71980c8c9da8dc418cfe06ab8d4d11286612d0c1f9665fd5720b6b1f9c22bd45546f65f677faab57549a1bcaaf26c36c0fc6de95b7de21a165adecca96509c5e8a3f7cbdfa8c77e0c786ab7f0322e1832854d182e88cbdb801b2313c4e49d647571d6c9a012404c6ba6cd15cf3156e1eb64bd93226e42b712c6fe8a5bfa2e0030347533310daf6d3ad061b433f431087787e10bf0abadc40c0882780be8c581a56d15e683e25eeae7c9ce55ef8e240c2879e8b37c0b81f5407c6947ae88dcb8939459d88ae0264866c50c94b410cf546b78e27c9872eefa056d3f4430482063bccb63cc391ca3ba8ee4758f779b1487a0a9434949b73329f75af72f12abc664c6880bc71ad3bfb0df306caa9a7c7de2d19c4a7ce2741df1c68f62161d466633b1c2199377b3139cca2b5da695b6f0988d0552684153ad979ed9e363d66b947c3de040149b1d4360b7603d970c6f8f963f748d9cbee0e739dd7de3c304889f3f8d5b8c05f2d78e82b2fcc460b5a5fe745b39206e0b4a19f32d4d907fb835bfa4a4f874b2fbade70123837ea825b0394de1fe6de4918f3921478e19dc53d815293301538722bde59fac3e397142366a630e07fa714297562b22f96de3c448cec95679fa219aea526f0059aad41fc95cb85cdde5a9094f59aa124ccd82ef8b59c2843775b86ecd59baac5a64f202b718a1e690303fdbd7aa977f1e8be317ba8f4fab80fa70d3de28a14ed0eab015ce71a30902a35b9bf4f6c0e43013f6276c006213e08055399228db47f75818f18fab9de3c966fd0edbfacabaf4d61cd61b9885ed22116041135bb3872d5cee8c75fa7e0fbb7bcc36a10b89956f1dca24f8fc87b0c03d57764ba40009bee89b98d59a26a1ba67ab9b58d5e6a268aebd22e17a54ba519987edb5ff8f3dcf093b71256c80a112e1cf30dec7fe5b024f270d2f7622f1d9c94d8c580e4699b246090e81f047ae8019820fee9866bbd051fe56e09be0d5e4d19b8483e3c1d1ef0f3d151b29ba2e130c1d8fdda16435fee305ce4eca8cecd21116b6b8bd937a730100772193d14a664266631b9165c22ed270dac838bd0efb76b42239c292604d737a723cc6b8c89433767c55d4691eb8189839f187053763db2d3bc23d5d86f1c5c706f7c16bdbf55c18c8278d2e49873b886112f268694696a01ab29689ae30844eb8eaa2392f306b5b18743c520d7e5d6f9a52dac0061b82959ff652eaba011ab846a1dace23fa3c1fcc943c6b8f6bd506e2fe094ce8ba4dde5faff49f12000f43741379075a0cb9dada9b4d9d682132e0a706a2aa61e1277ded743e7e5e98995c979dc6b5977f76872d7492ebc37d764a92adc17da33b850c5a9e704a627f3007a45454f619f553dce59d77100f626ec873d4c0ceb3a2c607ca77dec5e8b3a709b6089bab8b5ab6a3a5efda0422934f55d88c4ac7d29eed652349bb3ea3fca015a7442c47c5096db94b7865e48bcd3137c241051c31c3b6ce61cec4d4c2c88c376195aae4e31c0ce47d946d4faa5374b55260b945a351aecae521e23a89ad793e4079b3d8f899857277c44915a739149b08483a4eb929a13f3a40158b23f82f723e0cf6e4c3a9761e8996b7371bcdf842244604edaea45075b2f56f8462e883c7958e9cec164eb75cba5a79af93e81de55f0c9967296b6cfaee354fd4cbc510be56a184b265dcae5e1ff8fdd4ee7c62bd5594257675d4d6edd3d2759baaac190e202dd362370be721642f8513b3103d53e81ea80f48b37c4192e42db5df199ee0eddf5461e09472f7e692753ac738dbb4382d2a8d8554a743fbb33574b7689d5dd6c8d4f948eb20fd54bcaf760ff5fbf6f54224012c96a4f13c2f3a37d49b0b4652ecfc33303de046ac0233fad3a5cd5b852b2297b6f433c29d3a28e7c3ac54ebd6e63103ace6b811e67787a4685c1e53077937c2d46f4da7ac3956a9621ab487d8cf0003e3eda01e940eaa33443c906963c5112e479ed7b1e1c7f33e29e094fe39e6b2dfe30abc189d3f23b90b9e70e4d988f60533adeed20f50e327df1836d5d00146c89895c6846f2a885d532834aa1130628bcde8714d5471e2b7d50834a95e2f634870b2f4a281103d05067fb2fb0d2a79bb1c85e946d059ce9714c353b1666226031b028d57dcdf8295d612f945e68f8ab54308125f6b2511d4d261947352fee2c10cfbdcd4b88e9ff9f12db2300b97467734cef77a9cc50711f640fc9f4b4a393ef680728a8fb43f499942637f1b12baafd5f745aabd8ac81bae7aaf846321d7adf16d97d8b2783f4a7b12012d7821ad03a1994bdc5ba1b62aa10971c59833c0fccb42299920111ee277945277c8ddec31c699bc01ed59e36cfcd173f2ff0ea9f44b548354464e179e929d95e75ff22c553191d5a23a7f79ac6785dcb05e8d61ef9fb91f669658658b61f4b43f1e0b5000f23ad400c585b6ac55839a72b6071425344306fbe30ca8fe85cd4a59f0631d11ecc128a797f1949fed8a46478aa917900ad4e2a4a14909628031a9b2beffc9506e88fc7b8d2cb44e5ae6d14fe76b41ef91f166315869f9ff51351eea0877755896e2e8b0a62d2949a50b3cd35ddffa7d821bef5caa21d8bb223393e4bef45981f173c1056ebc691f623823515f6f749b4448ee2b3d33c59053892fe7b18eaf5c0298b0938be8ecf6b31970d8f64c1af6efc297a0980a17c61f9d006707bc35ed926f3466c85a48f27e82dba563c5101c6fa7df68042054b612c92cf91e21a55fe87e16218f6f35da4ae6886ccd90f6d83ce9a3bb1ac124e20885d9e4299550365fed61cb9fed02c8a5157b8c0e1e0e33ee9258690e6fb9fae8e5ecfe37c7365cc40cf5e8c2d74ad2de19cc843fa343001ca9d9c5e7bbaaa03d5e6f20d081af4a3df8a6f1f48571e87ab60779b405c862e5340d6ca6168907768e90e28049f60ed8033fbba23b21ce0785df7d196bc0e60a8b8569c0e3332fc8580749fdd79a17036c62c0b578b8ec7e76e5fa47e4f774837fcb9de5f1523f315c7e09e0e925b8a356a859504e0898f1e8fd82e69450bb58df6d68212be1de24664a7c703cc44ec4e63d3365a01c0d061c3c4bb141fa2c6d05f9c41a08a4e234e201f6b81d75e613b4529df3cc3c0231c14e901bf5cf96cc8056c999cfbab1ed4c14018d7b94987187c5fe5cd0ebc6ec7a4a7118ea911b78888681ddbdcf795df88d4369fa50d5cdaa9345c2f2dcb5cb5cd8ec0ce1bb81b047603c36d976dc6f74002c776bf87492f14c1345616811261a0fcdae31704e035c5e3d4a5a10d3dc2ef625840505ba9cb935d3f15cc3de8b100047ea946f2a52dffe22c84b39a69c52decfe0377cada733321d5a548320ba0ea091f31467a5ebe5110309beda381d2729a3fa103465fd80fa59ca993fac2602ac657421d8b7aa0a8700c3494d59ce0fd205f95bece69e408f14964a7487e96039a75b6aba476ab343e7543e5b3e419a00fb400ac2ec9f2e0a68818328071a573361662cb9b92f02df8fd1b85d908c61a9ff1eca52d22f7d13e2b186c66dcb5d240a4da771835b08285e0974960b63ece1522d4dd8248badef4fdc26af4dfc389df0097a17bf7cad13986616a9ee7ff756f60db140a3ffc769f0ab6cb4c0c3f8e0cebc6d43355d36ac66d46a437196fffe3962fe67d03c6528fe2a4c23b6e957ba04449339f44cf4224343aa56acc4770f380e20f0333747c7c1fa121d18bf69d61e722337985680f819d804f5c07f292f7012a25fa59d92d706897ba755ef25a1dda9c2721a9a0beca16f53a1b63bb7e52caf6a30b0bae9c62a3be41855dfad8dec71b942f77223a94ff8c97a3ddff0aa367ff581beb4cc73f95f0d2adab5269448e0387f466b6bccf05ad3c359649861134a8be45c10d75c30ddd9514e7989e38f936f85aab19ce6350d9364ead315d9944712f6356a983c8dfe25829d27a03893932879462a84710a578008e42ffe2e550e7e612b73ce4a0765a319daafcd8871ecdd480d24559002bf651ea10cd7f96e495106b5bc23340936c03130ebe1701724f419e059c2e606bcf9c680ae769b24ff9468069ee03ae5351f6022089d631b19c9a1a17b9884fe9a70ef8d8cc0954e8a6b98ca8aa2943e7a41161bdb8ef7dda283f16302fcd02160994f8483e54a26e3ae7c0fd4cfdedefe32322cc73cfed0c11e95828fb90c8e76748ab5a8bd7c6e23ed66e92aa44004a47bea344c80829e4be79a6da3c5d07db73a464801deca8f58159d7dc666c49689997fea5c10fed63522428f6797d71f7a2a331f5e4c8fddeb7f5623eb41b07c7f811b4e4b0dbc34fe025961e9ad8091cb5a64c437289b799d5614197535edf28e7f87233c625f9482c838902d535d1cbfc233a53c58cca63b3faf778b3737418885d3e9fe31e24616bbd561bee5813d7ecdcb941ccd349d64fc18a48d4812c32c6b47c3b84c67a59f1753e4b3b278a1fcde0ddf1a5e9371cd5a6da068e4882f6a4e31b2856d8e52bccda0f500360869a9beb2ac1adac75adb0a532dc82bfafa6e845038dc3eea8eb91ca3c904f713218955def7a4d6740df1b252cdca577a07a1948d4a1e7fdcef915b86bd6a23be1909993c695e7bf09440aa8c38ff7bcb59f95f8f366da3bc7352cb2f3888b30734ea2b50bba1423dad4165d3fd782dc21303927f12cceb3800972d77c8479590a846fabefa3ff6433c2a80c00d2e5da6e09ff63419ebcc707bb11d9c7f84412547c551d9ebbb9e76a9206724486789843af61110ac2e20e85f15c1ea6113716c44d5ed70a6fed8c52a590cf13ffafa738063016af3355560c7c5de424329ff526943355077c1e7a176230042c7b3feb9dd4415b9bdaca2cf804f156891f6f9e7489588ef3e8a7edbd8a4c2835d1835403f2adaee9e8e8af1eb4ef4eaacf0af31b7f43504feaf7863ea23212341aa3edc4941e977e79407f28ed69254cd654e5515779021190bb4cd665b29261160ff62385a1fb417f755589317320d8e35cf9d2cf348e4131cb8c99f22cfdb0308669105661c5717f7a013ffccd7e863b685bc39888e4727d0495a5ceefe17a26c87993423302e0a7bc1948f23c5ed64d2fedb5e1ae63f36f7b55bc9904e9a62e549d8afe1453c36fcba74abcc63f68cdf3fa9fe6529306ef9befed4df6be5d5a2c4eeecc6f03cbcb3bb0dc42cb26edbed7a8c3fdac8c5a21bd1cefe1ed9f7b87816f1e0e9cb598cda217c233e5b91701b101a799f77baf61579480bbffe8df4c92d3b70ce6872ee6f6dc5f194792441429a73be0df66f0b6bc22855e8679978f502725e223cf8bc4a161d0201", 0x1000}, {&(0x7f0000001180)="f422dfd8284ec991452879bfe94433bcb2ed7ebf63a943a78699078934fb1f264769680bf70ccccff1bfaf349d6a5fd6b930ca0cb04381c152064782bbc598dffa1b881a5a4514fbed6bad04b21fa4b2ba308c5ecd15d966a6ac39ecb874bb81c52aa1a3ce609a8c866b56d53ab0feeddf9d58d4beaa1cadd9b329ad94c0872216ed7dc2da3101de793a67b610a0616ebaa252ad91e43bfc5a32be950257342c6a336c35e685eb7cb2f7f73844ecbf9c38945a2b350ee1b06e8b4d7f1bba597cf99d5eb732c3b7142b50cb9c8a63a3f9fa9f4b108ab0", 0xd6}, {&(0x7f0000001300)="fed95ecca960a1653672105d3f0f70c735971efe86b81face2dd03d11f06b29fdf7be03a2496ccaa3249b309fb715ce49a2f0ea5506ea2e79abefb1858ab0de6c178f914c4bc01e68f74fedc5300e9608708c0d417c68c201a34c9e940167a1bdad7787acc9e625420528bfa85c06cbcf62ab158a2bb71d1", 0x78}, {&(0x7f0000000100)="d6c078e9a461cb8aed332baac277486cb59473b3baa4ec8c41035d501e811f56f43c5655e91ef7b0af682abda3f2f35d15d7f759f53964900afda8930eb3c9a868044661b85d31285c6a1a2c35d30ba68fe726aa0e4bceac58c3d9c7c3af1f805c529a8e360b4cf5c6c168", 0x6b}], 0x5)
r0 = semget$private(0x0, 0x4, 0x3b3)
semctl$GETALL(r0, 0x0, 0x6, &(0x7f0000000740)=""/196)
semctl$SETVAL(r0, 0x3, 0x8, &(0x7f0000000280)=0x80008)
r1 = semget$private(0x0, 0x4, 0x3b3)
semctl$GETALL(r1, 0x0, 0x6, &(0x7f0000000740)=""/196)
semctl$SETVAL(r1, 0x3, 0x8, &(0x7f0000000280)=0x80008)
getgroups(0x3, &(0x7f0000000300)=[0xffffffffffffffff, 0xffffffffffffffff, <r2=>0x0])
r3 = open(&(0x7f00000000c0)='./file0\x00', 0xca165819b60ac6b, 0x0)
fcntl$lock(r3, 0x9, &(0x7f0000000000)={0x3, 0x0, 0xffffffffffffffee, 0x1001300010005})
fcntl$dupfd(r3, 0xa, r3)
r4 = semget$private(0x0, 0x3, 0x0)
sendmsg$unix(r3, &(0x7f0000000500)={&(0x7f0000000600)=ANY=[@ANYRESHEX=r4, @ANYRES32=r3], 0x9, &(0x7f0000000840)=[{&(0x7f0000000a00)="915efe6cbf9001fb893af32d809d659c5dd0b1735492005c7ee5cfd273088359559f53f1b179921c32439703f62fd72b369e11f9046f1de8ae1a7e5de7742e107364aa42b878a7ae464291f48b8f5f4b895c782183b8569ed353af54ed22f74e166fc9ff1c1748eaa745bf0abb44b81a01dd3380e12aaac42181cdb61e05c9a64cf2f3c456ae61af550ce78796322fd629bc9c17b814254af9694e35658157b1116f365bd8916298e560e556fea5b73b12550c483e9313558a720b8f70f0595f97239b3c74868b87323d1000426f10", 0xcf}, {&(0x7f0000000b00)="b37b5029fb4dc7e74c8d606e1015092e32ef2d6a9749f4908134e0c1bae4d97929ab1612f13cc1b889b4fedd7b6cfab4e3ef492ecf6d209b75548971e25aab46f6", 0x41}, {&(0x7f0000000640)="ac97ffe0501edde9de6099a2584e546bbea4bb21274bff3d388bf47540ecf64628fe9e57fd757385c367b4ee1eb5d23cf89c8f0846003bc91611ec0f1656ba5265dd172ce9a40d5223633a646f6e03c104f379fd76422975d0e5d320cfc964fddb712076027cab6b0b42aec94ff7e98049712b8796fd2aa9594a9e80271827f39742bb6ca1c7f3feb176531bb41cc4a6a6164401a87310ed7f75bb56bf22e4a04765af26d2fcc251198517506a5ecbe82a55a252", 0xb4}, {&(0x7f0000000540)="1bc6dcd83fb830317f8dd48cf1fba82e860d5406c21ef83136921d805fc615e24c9ed622a77c167b432e2271626167a189505effb8a36a27e0c012e0c95fbfcd718f98a1cf273953644baae048807dbea0456efca83cddb0f8cb45e575febcafca59a8308b907d8a0bc90880691bbbc2", 0x70}, {&(0x7f0000001100)="f96dfe6588d9910998b4aeea546c15208d882d2e0a0190fd2e6ad3dfc7cc27157eaca01752438c590a98ca4e416a23d4d68ef670ca82a6d3c6ec05f99c7e1424d7859962e59741727417a6d9a8e2e0979bc8ce13fbc40e9d0d048c56f8cfc66e36293646fb00137d5b2a98d6d40bbb97f9556b3223a0c2275c4975d76d080c8ce5076f6b6b8a16c32e4c06f97a1422ec6af70cc1586e92bd0ebaa02eed1ae0878b745b23242b7f5843a96cbed743422ba0e052e6e01ec7f4bcd432fb39deaf60a413786f0819a3b3df5da73e4cb7c0b252cba250bab91e65942bd51556b2c186a15baa22b791701de3f8e94cd1372b77e028d246e30ed200000000000009d7b85e64543a94c9689f74af2622c4bb03e5d0cae2a4ea9f70e46d58126c4b5fdb7eed419c7fb70a6d102f1d09ada0f0ffab5cd20abe959cbdd33b7cc0248c209904e1b98fff9074791d9866b0d1e1d2f82f9c9d3ecc05d3fb2768326fca83beccd1ef2e2a88aa4ba1c7d01b55770bdda114ef1b60cbae5c92fc871ee3d56a2761e44001f6dae39279e91000f64e478f82aa3a26c005b5d5f49a206c404abb24ba3b7d42a886a64cc525b4e9d802e6eae51e72614ce6318118bae4d50cc5eddfd9225bb42ce45f26bb2dbe7047c4", 0x1cc}, {&(0x7f0000000dc0)="975df528c77f9c4b3d602afc9f9bb9fa4522191578791553c250a1c74968a7d1ba16f91088b547875ef8efdacc15e6d1bd14ffb2ccffda8b395bb7a83820cbbc772edd4adb892661f4ed370536d50a099917718fa80d96e0ca728d1a5f6c49bf0b5a680ba8d8332bf665bf82e6e33ea2fba0090800d6d44fd11513368d25aff913a2adb3212c4b4e5520eb", 0x8b}], 0x6, 0x0, 0x0, 0x1}, 0x406)
semctl$SETVAL(r4, 0x3, 0x8, &(0x7f0000000400)=0x4)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000001380)={0x0, <r5=>0x0}, &(0x7f00000013c0)=0xc)
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000480)={{0x20000003, 0x0, 0x0, r5, 0x0, 0x10a, 0x9}, 0x5, 0x3, 0x8})
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000340)={{0x2, 0x0, r2, r5, 0xffffffffffffffff, 0x0, 0x7}, 0x9, 0x200, 0x1})
semctl$GETZCNT(r0, 0x2, 0x7, &(0x7f0000000200)=""/235)
syz_emit_ethernet(0x10ae, &(0x7f00000008c0)=ANY=[@ANYBLOB="b52eeaa0ca1d004d591cb8cdfd4c19094ad0072801d5a13a9f7f4fb9b79340d02561b4f3a0a062677a5020e94f0c887f232ca01cb095c4569610396f97a72f5274a25e690f853d7a8616d6c32c562354e8bcdfe4b2191a4bf4b5c4c40ac5703f3a799c23cdb6d3f9f76d577b138351f998895ad5e47db8"])
sysctl$kern(&(0x7f0000000000)={0x1, 0x44}, 0x4, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x1}, 0x2, &(0x7f0000000080)="8eea2e96b497e2fc607cba70048cedae41e417384b7a8adc30bb35ab95cff32d559ea9e4757abbe329a8b812f76fb4cc23473f6429ffd0e8b1344c2ed4f7674ef54a96413e229a975e402d93a4c0f19408e624e132caa53a6bf23163c0ca33ea738211ed06909e98fab7a2ef8eeae4075c22", &(0x7f0000000100)=0x72, &(0x7f0000000140)="5a5df6c17381acc081c5f4d32983febe83eeda9445dc12d9ebe1bcc6a1c8be72efb6b1513b3d04c3993af61ca01bd3943afd673077cfeae68151e2621403fb20b56d062b39203020f36c757c58accab128102eb1ae4d102dba68754c141ed3421f813eb6cb9d4f484b2bf0b8d21bbed36ed9e09d2f85fa5dd6db563ef148813fead5e5a79616d08c4d092160c6739d5edea45571f968738c7a80", 0x9a)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x4000000005, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x80}, {0x4}, {0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000080)="3c9ebb8a65237a19000000000000", 0xe)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{{}, 0x0, 0x0, 0x0, 0x800000000005}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x80000000000029, 0xc, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x3}, {0x24}, {0x6, 0x0, 0x0, 0xffffffff}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
write(r0, &(0x7f0000000400)="1d088468708b852c67449b6a76e3", 0xe)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_REMOVE_DEVICE(r0, 0x80185760, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x84}, {0x1}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000400)=[{0x45}, {0x15}, {0x6, 0x0, 0x0, 0x80000000001100}]})
r1 = dup2(r0, r0)
pwrite(r1, &(0x7f0000000040)="895e654f5c1d15f75229b6758539", 0xe, 0x0)
sysctl$net_inet_tcp(&(0x7f00000003c0)={0x4, 0x2, 0x6, 0x19}, 0x4, &(0x7f0000001080)="4637b134", &(0x7f0000000400)=0x4, &(0x7f0000000440), 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000140)="ebffcbff13", 0x5)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000200)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r1=>0x0}, &(0x7f0000000100)=0x1)
setuid(r1)
r2 = socket(0x18, 0x1, 0x0)
getsockopt(r2, 0x6, 0x9, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000140)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
listen(r0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
open(&(0x7f0000000240)='./file0/file0\x00', 0x200, 0x0)
setreuid(0x0, 0xee00)
rename(&(0x7f0000000040)='./file0/file0\x00', 0x0)
socketpair$unix(0x1, 0x0, 0x0, 0x0)
connect$unix(0xffffffffffffffff, 0x0, 0x0)
socket(0x6, 0x0, 0xc0)
getsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
ioctl$WSDISPLAYIO_DELFONT(0xffffffffffffffff, 0x8058574f, 0x0)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, 0x0)
fcntl$setflags(0xffffffffffffffff, 0x2, 0x0)
open(&(0x7f0000000340)='./file0\x00', 0x70e, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x40000510, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
nanosleep(&(0x7f0000000100), 0x0)
r0 = socket(0x400000000018, 0x3, 0x800000000000003a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x67, &(0x7f00000000c0), 0x0)
r0 = socket$unix(0x1, 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f00000002c0)={0x0, 0x0, &(0x7f0000000280)=[{&(0x7f0000000200)="7c1dd46ec7c8c217a540ccb91677da5936f471e0f2e1f4ffa696e652e48f026c", 0x20}], 0x1}, 0x0)
shutdown(r0, 0x0)
recvmmsg(r0, &(0x7f0000000600)={&(0x7f0000000440)={0x0, 0x0, &(0x7f0000000680)=[{&(0x7f0000000080)=""/127, 0x7f}], 0x1, 0x0}}, 0x10, 0x0, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
dup2(r1, r0)
listen(r1, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r2=>0xffffffffffffffff, <r3=>0xffffffffffffffff})
dup2(r3, r2)
connect$unix(r3, &(0x7f0000000000)=ANY=[@ANYBLOB="00012e2f66696c6530"], 0xa)
recvmmsg(r0, &(0x7f0000001600)={0x0}, 0x10, 0x0, &(0x7f0000001680)={0x0, 0x100})
accept$unix(r1, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b1000504600000000000080001", 0xd, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffff9c, &(0x7f00000000c0)="b1000501", 0x90, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
setrlimit(0x7, &(0x7f0000000100))
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffa000/0x4000)=nil, 0x4000, 0x0)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
r1 = getpid()
fcntl$setown(r0, 0x6, r1)
pipe2(&(0x7f00000001c0)={<r2=>0xffffffffffffffff}, 0x0)
dup2(r2, r0)
r0 = open$dir(&(0x7f0000000040)='.\x00', 0x0, 0x0)
openat(r0, &(0x7f0000000080)='./file0\x00', 0x200, 0x0)
faccessat(r0, &(0x7f0000000000)='./file0\x00', 0x2, 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e57f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x2000)=nil, 0x2000, 0x1)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000040)="e8800000a4b2a6f304000000", 0xc)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f0000000040), 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x37, &(0x7f0000000000), 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x3}})
r0 = socket(0x2, 0x3, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0xffffffffffffff71)
close(r0)
r1 = socket(0x18, 0x3, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13f9fd812eaa4e713040e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
write(r0, 0x0, 0x0)
sendto$unix(r0, 0x0, 0x0, 0x0, 0x0, 0x0)
setreuid(0x0, 0xee01)
socket(0x800000002, 0x3, 0x102)
pipe2(&(0x7f0000000080)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x0)
fcntl$setstatus(r1, 0x4, 0xcc)
r2 = getpid()
setreuid(0x0, 0xee01)
fcntl$setown(r1, 0x6, r2)
setreuid(0x0, 0x0)
setreuid(0xee00, 0x0)
write(r0, &(0x7f0000000040)='!', 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x84}, {0x64}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket$inet6(0x18, 0x1, 0x0)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0xc1206951, &(0x7f0000000300))
mkdirat(0xffffffffffffff9c, &(0x7f00000002c0)='./file0\x00', 0x0)
r0 = open$dir(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
getdents(r0, 0x0, 0xfe82)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000580)={0x10, 0x400000002, 0x4, 0x0, [{&(0x7f0000e10000/0x2000)=nil, &(0x7f0000ffd000/0x2000)=nil}, {&(0x7f0000f7b000/0x2000)=nil, &(0x7f0000cb2000/0x3000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000cff000/0x3000)=nil}, {&(0x7f0000f78000/0x2000)=nil, &(0x7f0000b1e000/0x4000)=nil}, {&(0x7f0000de6000/0x4000)=nil, &(0x7f0000800000/0x800000)=nil}, {&(0x7f0000ee1000/0x1000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffe000/0x2000)=nil, &(0x7f0000ff9000/0x4000)=nil}, {&(0x7f0000aef000/0x4000)=nil, &(0x7f0000ffa000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f0000914000/0x3000)=nil}, {&(0x7f0000f81000/0x1000)=nil, &(0x7f0000beb000/0x3000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000c62000/0x1000)=nil}, {&(0x7f0000b47000/0x4000)=nil, &(0x7f0000da2000/0x7000)=nil}, {&(0x7f0000b26000/0x1000)=nil, &(0x7f0000825000/0x12000)=nil}, {&(0x7f0000d22000/0x1000)=nil, &(0x7f0000d15000/0x4000)=nil}, {&(0x7f0000a21000/0x1000)=nil, &(0x7f00008c4000/0x3000)=nil}, {&(0x7f0000bfd000/0x3000)=nil, &(0x7f0000905000/0x4000)=nil}], ['./file1\x00', './file\x00', './file/file0\x00', './file0\x00'], './file\x00', './file/file0/../file0\x00', './file\x00', ['./file', './file', './file', './file']})
ioctl$VMM_IOC_INTR(r0, 0xc450443c, &(0x7f0000000140)={0x2})
r0 = socket$inet6(0x18, 0x3, 0x0)
setsockopt(r0, 0xffff, 0x40, &(0x7f0000000000)="160e01a8", 0x4)
mknod(&(0x7f0000000980)='./bus\x00', 0x800080002002, 0x2d44)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
poll(&(0x7f0000000040)=[{}, {r0, 0x40}], 0x2, 0x0)
poll(&(0x7f00000000c0)=[{r0, 0x1}], 0x1, 0x0)
r0 = syz_open_pts()
r1 = dup2(r0, r0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000040)={0x0, 0x0, 0x0, 0xfffffffe, "f182469acfbe95afc4006972e9e754ce364990cb"})
ioctl$FIONREAD(r1, 0x4004667f, &(0x7f0000000180))
sysctl$net_inet_ip(&(0x7f0000000200)={0x4, 0x2, 0x0, 0x5}, 0x4, &(0x7f0000000240), 0x0, 0x0, 0x0)
syz_emit_ethernet(0x4e, &(0x7f0000000000)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, '\x00', 0x18, 0x2b, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x42, '\x00', 0x0}, {[@routing], @icmpv6=@ndisc_ra}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x1f8, &(0x7f0000000080)})
sysctl$vfs_nfs(&(0x7f0000000000)={0xa, 0x2, 0x2}, 0x3, &(0x7f0000000080)="0acf76ec", &(0x7f0000000100)=0x4, &(0x7f0000000140), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x50}, {0x3c}, {0x4406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000400)={@random="1093b39c1524", @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback, @multicast1}, @udp={{0x2, 0x2, 0x8}}}}}})
syz_emit_ethernet(0x3e, &(0x7f0000000000)={@local, @local, [], {@ipv6={0x86dd, {0x0, 0x6, "91ee11", 0x8, 0x2b, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @remote={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x3c}]}}}}})
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{}, {0x2, 0x0, 0x0, 0x2}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x31}, 0x4, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0x68a, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000040), 0x3, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x18, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x1a, &(0x7f0000000040), 0x4)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x6, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f00000001c0)=[{0x2c}, {0x25}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000000)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000080)=""/120, 0x78}, {&(0x7f0000000640)=""/4096, 0x1000}], 0x2, 0x0)
r0 = open(&(0x7f0000000040)='./file1\x00', 0x611, 0x106)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000001000/0x4000)=nil, 0x4000, 0x4, 0x10, r0, 0x0)
mmap(&(0x7f0000003000/0x2000)=nil, 0x2000, 0x0, 0x10, r0, 0x0)
mlock(&(0x7f0000001000/0x4000)=nil, 0x4000)
ftruncate(r0, 0x0)
munmap(&(0x7f0000001000/0x2000)=nil, 0x2000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x24}, {}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000200)={@local, @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "22b812", 0x8, 0x0, 0x0, @empty, @ipv4={'\x00', '\xff\xff', @multicast2}, {[], @udp={{0x1, 0x3, 0x8}}}}}}})
munmap(&(0x7f0000200000/0x1000)=nil, 0x1000)
r0 = shmget$private(0x0, 0x1000, 0x0, &(0x7f0000ffc000/0x1000)=nil)
r1 = shmat(r0, &(0x7f0000200000/0x2000)=nil, 0x0)
shmctl$IPC_RMID(r0, 0x0)
shmdt(r1)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmsg$unix(r0, &(0x7f00000008c0)={0x0, 0x0, &(0x7f0000000780)=[{&(0x7f0000000140)="6e82f728f388306765a18962413974bb82605a546fc33091524b4c740af90de84a2e96d1c111744d84b9835cd0e30593a2c0305ff986bd268a8c6b6e8c64f1554cdb85a35cccfa9c9a9a5de6d8a3365a74c1df38298c192b", 0x58}, {&(0x7f00000001c0)="43923b6313a7a2282dd60a8d0ac64b144cc59bf30aa54b0c4dcb349d7df3f06c379a2cce32541c44c62c0e1d35da2409e84432a91396abb814", 0x39}], 0x2, &(0x7f0000000880)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
ioctl$TIOCSDTR(0xffffffffffffffff, 0x20007479)
open(0x0, 0x2, 0x38)
ioctl$BIOCVERSION(0xffffffffffffffff, 0x40044271, &(0x7f0000000080))
sendto$inet(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
ioctl$KDGKBMODE(0xffffffffffffffff, 0x40044b06)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000001180), 0x0, 0x0)
ioctl$WSMOUSEIO_GETPARAMS(r0, 0x80105727, &(0x7f0000001200)={&(0x7f00000011c0)=[{0x0, 0x7}], 0x1})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x1, &(0x7f0000000040)=[{}]})
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
setsockopt(r0, 0x29, 0xa, &(0x7f00000000c0), 0x4)
setrlimit(0x8, &(0x7f0000000100)={0x8, 0xb})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000000)={0x97ac, 0x7, 0xfffbfff9, 0xfffffffd, '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00'})
write(r0, &(0x7f0000000380)="4d000000003572222b76d50bba28abd12122c05b98aa8c3def8decac0ae790c8a11c0d9d939191e53eb746834891f945d91f8d97d5d45bf0998a390c108f1cb878325411f19fdc6b83da8233bf91a02e157b41ca86b403920eb6202ea8ef77bb1d402c61242807a6366bd5d5f70affffffffd8caf79664f1bd170e98bdf1d3ec57e0bf2e632d8b5ae658184b2886427dcd2bc5817c7940bdea473f795997f6c72f2de11d19badff7bde555c03c89", 0xae)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000400), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x1, &(0x7f0000000040)=[{0xb064}]})
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x3d}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2000, 0x0, 0x42)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x3, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0xc024696c, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4f, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x802069b5, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x2}, {0x61}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
syz_open_pts()
r1 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r1, 0x0)
mmap(&(0x7f0000006000/0x4000)=nil, 0x4000, 0x0, 0x1011, 0xffffffffffffffff, 0x0)
write(r0, &(0x7f0000000100)="fa", 0x1)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
sysctl$net_inet_ip(&(0x7f0000000000)={0x6, 0xb, 0x0, 0xa}, 0x5, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
kevent(r0, &(0x7f0000000180)=[{{}, 0xfffffffffffffff9, 0x1}], 0x800, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000280)={0x3, &(0x7f0000000180)=[{0x4c}, {0x40}, {0x4406}]})
syz_emit_ethernet(0x3e, &(0x7f0000000040)={@broadcast, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "0745a0", 0x8, 0x0, 0x0, @mcast2, @rand_addr="46d3945c4ff03f1338bf7c3851f046c8", {[], @udp={{0x3, 0x1, 0x8}}}}}}})
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000040)="ea", 0x1)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="2902657f7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
connect$unix(r0, &(0x7f0000000100)=@abs={0x0, 0x0, 0x1}, 0x8)
bind(r0, &(0x7f0000000000), 0x10)
r1 = dup(r0)
listen(r1, 0x0)
setsockopt(0xffffffffffffff9c, 0x50, 0x8001, &(0x7f0000000140)="e10abbada7cb9e47cdefdf65c0ea0aad27871445ee5493e8edcbe22970b7d820be93aecdd4258f8bdd8171d56a990318c3e6dd65ed1b247c8e3c0a69f167623c91f5265e5b75ee7ba973f8fd2d88a80d21f7aa3026024df1c4d6ddf21713318cc20e76f9c81ec02d10378dcbb7dc51972a08009170", 0x75)
r2 = socket(0x2, 0x8001, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x80, &(0x7f0000000040)={0x0, 0x1ffffffc}, 0x8)
connect$unix(r2, &(0x7f0000000000), 0x10)
close(r2)
fcntl$setstatus(r1, 0x4, 0x4)
accept(r1, &(0x7f0000000080), &(0x7f00000000c0)=0x8)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000000)={&(0x7f0000000180)=[{0x21}, {0x23}], 0x2})
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
fchflags(r0, 0x0)
setrlimit(0x8, &(0x7f00000002c0)={0x40, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x3, 0xbcd8, "ea3ab27570ffffffff8a59ea0900e800008e4c00"})
r2 = fcntl$dupfd(r0, 0x0, r1)
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
ioctl$FIOASYNC(r2, 0x8004667d, &(0x7f0000000140)=0x8)
execve(0x0, 0x0, 0x0)
pwritev(0xffffffffffffff9c, &(0x7f0000000000)=[{&(0x7f0000000680)="ee6500f8e008001342", 0x9}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc4504444, &(0x7f0000000240))
r0 = kqueue()
kevent(r0, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000380)={0xfffffffffffffffb})
munmap(&(0x7f0000000000/0x3000)=nil, 0x3000)
utimes(0x0, &(0x7f0000000040))
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$unix(0x1, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc028698d, &(0x7f00000000c0))
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r3 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r3, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000080)=[{0x2c}, {0x61}, {0x6, 0x0, 0x0, 0x2f}]})
write(r0, &(0x7f00000001c0)="3c9e9bd555d00000000000008000", 0xe)
unveil(0x0, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000080)={0x0, 0xa7})
r1 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r1, &(0x7f00000003c0)=[{&(0x7f0000000740)="90", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r1, 0x0)
execve(0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f0000000000)=[{0x512d}]})
sysctl$net_inet_tcp(&(0x7f0000000080)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, &(0x7f0000000200), 0x0)
r0 = socket$inet6(0x18, 0x1, 0x0)
shutdown(r0, 0x1)
getsockname(r0, 0x0, &(0x7f0000000040))
mprotect(&(0x7f0000ffb000/0x2000)=nil, 0x2000, 0x5)
mlock(&(0x7f0000ffa000/0x4000)=nil, 0x4000)
munmap(&(0x7f0000ffc000/0x2000)=nil, 0x2000)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
sendto$inet6(0xffffffffffffffff, &(0x7f0000000bc0)="451c2f2c49a1d9fbb3306ed7891fb52f9ec7062b3f9be97564127fd829f86f3af5a426a64398b02ab2e22e15e5375cdb51207e6ba247dd14f9ff54d7a5728382a25f7d281d68a42e7aaa85bdd9a3cf2e168226ca67c7bd0800942f0636d55bb7d3ac5bd03ed94a354e7fe3eaa914c9bfee8da99118ed3301f531567e8c019222c7d1da842b849e916328184ec78317f107717cb6926c188b4536be3900885ac1beb63377f6e63117887a001a0f6cc29c2fb2b00153dc5852cc4180826d7368a69c6e1b527c536f1716b2f061c30f0094d94044ce4026cee4c95a3ac02417962c4e46a74ae512c73a882034442dfd995bc7aad7ccc686eb2f5e8c9811fd0a9a2cb9919bd7f11d6353d695d1591b19203019596eda45bb1c751be56e3e8ecd48647c8fc5838688abe087cc767cc5d3fe220d93e65831db9c9dcb3677b8930deffad50c60dc6ec53127d4e30755dd9d5be36aa49afeb837705738e33b6cf5315adfab849d8067ce9ed9c33a8c1263d9f47de5cb4027067894a1c339f0557185461f78d520f7a5e9eb23f1c96a8b15b575f6e65aeb683252f6bc43e55586b2f0041c3bb94069e4532a6e45439ae55e8b7123f2f02b1dba3592b5f492f87267865c2830250bc89a5e15d765e657aae9b0bf42c093624511642be124926953f270448c2faada2dcf6128253e32b11f759d8d0380ecd34c049efbeb76def99032669aad8d5ad424a27ad28fe0ec1edba7d0dcb0cfee52a1fd052d2066e482608ca4d95973b6bee54cda16bbe43ed35a680b88ad333062ae868387fb79b318aa7854616fbeca3d96123457a7dc5113f2574f7ca842f9f3869d2f326b6a4c40adea9e6a4ba75a26b30d66b4b98c99b748f0dd6fa0fbdab0e62140c951093111163218099245ecf67fc3efedf8b443121a4de1c03cc49e7b648f55139d061fdf4fc27290c71764c5af0f67f0bb97bce3f93572a77bb9e25dd28881a419cb0074892eb987a630bb93e47729284a23982f47c33d3fd58cf54aa1e4936457489dd54787a7d5bada247b33446521127a10f55584930b5d0a2f06b26ef28c4d568179a28c8e9574fda266b91d2941213517fa188d275964c1d1cb0bd3915a0ae0072e476a6c0b6380efa57d5657fa62e766f0d38c39b399b785fe0b1abedf161efef872fe38d6f3d97b47c14dbd101badf846a811b0281a9685c3cbb45705585d6210fdf17c363addd1e5a4008309003ba65bc70ace83a516356b6e29dcd09ffbd745163842eb2fc15e376fcc20510f7faed6e9303a17262c44285b608db82f2bfba5273ddc9648247aea2ac17ce7800d75bded1e56fb6cb2637dc520679fd3f603b21454141e2c19060000006144dfa5de644e7dac9f52e658c10c7919ed623a790ab58ee2b515f6839b338c08852ec1d4e7b634aa5608bcd257ce2900a9065001c3e65aa1cec59d741359dcdc37d0e83ce00413b080a82e3e587bf6719736851a5c72f7972bc4b82052fe5a5dbedd80ccfcb25b1663de3585372440553da6fdfad08be27a96e3159f2ba08cb8a7aedf781c2ad1fe59b43b7764ab8af1ab03f5cfe87eaff7457ad7b3a40842f3dab12d278cc55329de2c514109e7d400c6e0be4856d6394fc72e2e16caf6115ba426a13fe3d692d9745eb7310bb0ba624f7d63bec8efdbc40cd73c45da3a494a5593a0e5088afce0c0ca841ff97141cc5c2e37fa222230", 0x4bd, 0x6, &(0x7f0000000040)={0x18, 0x2, 0x6391, 0x7fffffff}, 0xc)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_INTR(r2, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
accept$unix(r2, &(0x7f0000000440)=ANY=[@ANYBLOB="0000000000000000000080000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000c15ab3fd8a2ad03c3c0d3f0883eb6dcf415644b70e2af3b2a6ccddbbc60c8901eeb622b6f7c941e51ec4e8587341ccec5b043c7d9d6337c044efe5c7b8eb2e92105e28ae481efb383d3c7b16848f379d4133d40187b70a972eddb439c34d0e27b9405872e74def74fb1fa380b2a883c39549461048c436ced7d6297859c2792f69cffea55844736a0e8b8178bf79ef98672ca5b9fa712112dfa247475ab4bb6f46de9e99ca91e5a15c36ac44ba3e206886a2492650a13cd97fd6727d5c0885dc3aed2fd07c26911cb9590fc0aed3228b9f19df08eea0ee65a1045d4bc15de1ef40ddc7456348c4d1fb1b11e27132bc01051db53327c46c35e8032a4edbee4a320ff5c8f877dfcf4f497dd8f53f28b5dd2d3172d90acfc0197ef47dbc85c517887313dcb191abe862869fd8cb92f69e3dcc511de8da0b185b591f9c84a25863392fc484ced0859201bb1b4bccd27994b9a2fe858067a7d5743effdccb09425b0936db23ce3d9dbeceb612c026ffe3ff9c"], &(0x7f0000000200)=0x4b)
r3 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x6e)
r4 = socket(0x11, 0x3, 0x0)
setsockopt(r4, 0x20000000000011, 0x800000000001, &(0x7f0000000100), 0x0)
r5 = openat$speaker(0xffffffffffffff9c, 0x0, 0x2, 0x0)
pipe2(&(0x7f0000000100), 0x10000)
ioctl$SPKRTONE(r5, 0x80085301, &(0x7f0000000080)={0xfffffffc, 0x6})
ioctl$FIOASYNC(r5, 0x8004667d, &(0x7f0000000180)=0x45ce)
sendto$unix(r4, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r6 = socket(0x18, 0x3, 0x0)
r7 = fcntl$dupfd(r6, 0x0, r6)
ioctl$TIOCFLUSH(r7, 0x8080691a, &(0x7f0000000300))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
r2 = socket(0x2, 0x1, 0x0)
r3 = dup(r2)
ioctl$TIOCFLUSH(r3, 0xc0106924, &(0x7f00000000c0)=0x2c)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0xffffffffffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x100]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r1)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000080)='./file0\x00', 0x202a, 0x44086333)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x1, 0x0)
open$dir(&(0x7f0000000100)='./bus\x00', 0x0, 0x0)
writev(r0, &(0x7f00000005c0)=[{0x0}], 0x1)
r0 = socket(0x2, 0x3, 0x0)
shutdown(r0, 0x1)
r1 = socket(0x18, 0x1, 0x0)
bind$inet6(r0, &(0x7f0000000040)={0x18, 0x3, 0x9}, 0xc)
r2 = socket(0x2, 0xc003, 0x2)
setsockopt(r2, 0x0, 0x64, &(0x7f0000000000)="01000000", 0x4)
setsockopt(r2, 0x0, 0x67, &(0x7f0000000180)="1e27d7beef810ac80b9430e39ddbf1779fee54f4324e", 0x16)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
setsockopt$sock_int(r1, 0xffff, 0x1001, &(0x7f0000000000)=0xe5, 0x4)
sendto$unix(r1, &(0x7f0000000100)=':', 0x1, 0x0, 0x0, 0x0)
setsockopt$sock_int(r1, 0xffff, 0x1007, &(0x7f0000000000)=0x1, 0x4)
openat(0xffffffffffffffff, &(0x7f0000000240)='./file0/file0\x00', 0x0, 0x4)
r0 = socket$unix(0x1, 0x1, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1022, 0x0, 0x0)
r0 = kqueue()
poll(&(0x7f0000000140)=[{r0, 0x40}], 0x1, 0x0)
r1 = open$dir(&(0x7f0000000000)='./file0\x00', 0x302, 0x0)
kevent(r0, &(0x7f0000000300)=[{{r1}, 0xfffffffffffffffc, 0x85, 0xfffff}], 0x4e, 0x0, 0x0, 0x0)
kevent(r0, &(0x7f00000000c0)=[{{r1}, 0xffffffffffffffff, 0xaf}], 0x1e7fffd, 0x0, 0x0, 0x0)
writev(r1, &(0x7f0000000080)=[{&(0x7f0000000040)="91", 0x1}], 0x1)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107002, &(0x7f0000000100)={{}, 0x300, 0x4})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x54}, {0x28}, {0x4406}]})
syz_emit_ethernet(0x32, &(0x7f0000000040)={@empty, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @empty, "fd504cfeed60403be742a1831c583086"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x80}, {0xc}, {0x6, 0x0, 0x0, 0xfffffffd}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
pwrite(r0, &(0x7f0000000040)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = socket(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1004, 0x0, 0x0)
clock_settime(0x100000000000000, &(0x7f0000000140)={0xfe1ecc7f})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
r1 = dup(r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x1d}, {0x80}, {0x6, 0x0, 0x0, 0x8000000000100}]})
pwrite(r0, &(0x7f0000000180)="6e0ae9f9b6c58d06ce93b68f2da7", 0xe, 0x0)
getsockname$unix(0xffffffffffffffff, 0x0, 0x0)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x41, &(0x7f00000000c0), 0x4)
sysctl$net_inet_tcp(&(0x7f00000002c0)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000400)="0118ffac02000926feba70a7cf09f6360f9ea14f040200040000000097069815ca5835b6f65316127c991ab43afd5604c4aa10930ed14b01017f00414191ac6193bb09919a8a372208b127f29c66755d457d5ecbf98930edfc29b2e7910599897b40c8c7f4766c3bd966a5c0001d0be46e59a63fde26f5ca6a157ad13900ffe6c35b55a191701155a29aec01000000000000007e59a59a05bb68baab118ea421c7324f8145857673b4a15e8909d64f925211d0cc21b9164c249a44e821fa95d881a9bbfb910093d1fbe686246fa8c2568ee86d2bee08f7397cfe2cae6e966e98d4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac7354745175bd902a5f48e0a013a1dc24244ade0d510672dd77da2c8fffff0000000000000900000047000000001cd965075b49777b822443cd2740e953a80000000000000000000000000000000009eb3881885647e6acb546c745052eec5503c733fc217eb57458e55df302e2d611ae3e030100a9edbd2d2d845b8e1f2e111835a6b76cd5ff5256df19b5634e2811d910faf269e55e7412e235a9072a43575893f400c7c32ed7a1d4df0000000000000292ebe97f33016e63553689b1e8a46145fc7f2c30c0d29de0815e8214f857ebd1f1e41bfb9a21624824a96d9619e00feb108d5bb62a27d465014bd7652b7e5f4a2e420cbd1e5b3e3ccb5dcfb37000"/528, &(0x7f0000000280)=0x210, 0x0, 0x0)
r0 = kqueue()
kevent(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000280)=[{{r0}, 0xffffffffffffffff, 0x1}], 0x0, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x40}], 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
kevent(r0, &(0x7f0000000080)=[{{r1}, 0xfffffffffffffffe, 0x1}], 0x2621, 0x0, 0x10001, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x6000, 0xe02)
r0 = open(&(0x7f00000000c0)='./file0\x00', 0x0, 0x0)
preadv(r0, &(0x7f0000000280)=[{&(0x7f0000000100)=""/57, 0x39}], 0x1, 0x0)
open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x1, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
setreuid(0xee00, 0x0)
r2 = getpid()
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0xfcfc96ac5f78639c, r2)
r3 = fcntl$getown(r1, 0x5)
setreuid(0xee00, 0x0)
ktrace(&(0x7f00000000c0)='./file0\x00', 0x0, 0x8, r3)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$FIOASYNC(r0, 0xc028445a, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt(r0, 0x0, 0xa, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x40004200000028ac)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$FIONBIO(r0, 0x8004667e, &(0x7f0000000100))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000b40)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f0000000700)={&(0x7f00000006c0)={0x0, 0x0, &(0x7f00000004c0)=[{&(0x7f0000000880)="61a4f74836d6833ca455401cbe6076f2f0", 0x11}], 0x1, 0x0, 0x158}, 0x4}, 0x10, 0x0)
setitimer(0x0, &(0x7f0000000100)={{0x0, 0x200000}}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x2, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x8)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x24002c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
ioctl$TIOCFLUSH(r1, 0x80206913, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_timeval(r0, 0xffff, 0x1001, 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
fcntl$lock(r0, 0x7, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
setrlimit(0x0, &(0x7f00000000c0)={0x0, 0x60000000})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
readv(r1, &(0x7f00000000c0), 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x3d}, {0x7}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
mprotect(&(0x7f0000001000/0x1000)=nil, 0x20000000, 0x0)
sysctl$net_inet_ah(&(0x7f0000000380), 0x4, 0x0, &(0x7f00000013c0), 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCCONS(0xffffffffffffffff, 0x80047466, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{r0}, 0xfffffffffffffff7, 0xd}], 0x0, 0x0, 0x0, 0x0)
r1 = syz_open_pts()
dup2(0xffffffffffffffff, r1)
r2 = kqueue()
kevent(r2, &(0x7f0000000080), 0x4a79, 0x0, 0xfffffb7e, 0x0)
socket$unix(0x1, 0x1, 0x0)
r0 = socket(0x18, 0x400000001002, 0x0)
r1 = socket(0x18, 0x3, 0x0)
setsockopt(0xffffffffffffffff, 0x0, 0x3b, &(0x7f0000000100), 0x4)
setsockopt(r1, 0x0, 0x2, &(0x7f00000001c0)="54f258920ecea1920e32e92c92bebf4c51d4c217a2f20ff5f6a9d855108f08c6", 0x20)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x80206916, &(0x7f0000000300))
connect$unix(r0, &(0x7f0000000380)=ANY=[@ANYRESHEX=r3, @ANYRESDEC=r1], 0x1)
sendmsg$unix(r0, &(0x7f0000000300)={&(0x7f0000000000)=@abs={0x0, 0x0, 0x1}, 0x8, &(0x7f0000000280)=[{&(0x7f0000000040)="b1bd36220bffd2e5ef703e086f74414179f43984db96817c738881a2f2ea100cccdfb3a8a3b83f457625f278736770aff3c787c13dc322a04502daaabe72fa80cc06ccc1ec1061abdec3cfa8df998b", 0x4f}, {&(0x7f00000000c0)="5b4383c13420188f29a134138e2554789916cbd24010dce2a11c21227ea4163a2821f18e23118b3102b68dd3bec3bd5dee724fae4fbb1fe3c8c15de6230c74666fb79a74562145037b100cf2eb634af4f42787af0ed5544c5bfeb4af93feeda358b9f380445d3c4c6ae5ebe4b3191c5f3d0bfc4cdbe5c139ee0c1a275b59dbcbcf7de6a38166add197a40c90822d77915d0b9d2a50d79b0ab069a423c261be0f0c3917918e884600c8ea8f7ef36bd54110492d89b7d8ca94c375dffc87323db4a5da6fef333a9092167b063d33fb4f2fcb692007b1bc413c85aee1c13a5e5e88e38ba9c6cd95809bf16ceb", 0xeb}, {&(0x7f00000002c0)="554006d9e141c02c4970cc88328102640f1723e3136fbae0308327ab36d305e8ca4a510606f7c3b154fae7ab0003bc5055384a9ce345321639789bc8497d4e23", 0x40}, {&(0x7f0000000200)="bfaec75433267e3aeb88a97024f8bccfb9f9ff274110e780c3bc61f97a63897d4b56e3de60b45cfd584c4b2ce7f4568734498480967e04553c415c7283c40312bd16f51fa0f317075b200b9ee12b1ce67fea612a362c1ec4991e42b5647b9f90349a0f620721a7b17d9da48daabb972ab9cc18d620aaf94d3a153fb56ab3", 0x7e}], 0x4, &(0x7f00000004c0)=[@cred={0x20}, @rights={0x20, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x38, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}, @rights={0x28, 0xffff, 0x1, [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]}], 0xa0}, 0x0)
setsockopt(0xffffffffffffff9c, 0x2, 0x5, &(0x7f0000000a00)="9ab3bce43cdbe79f52b871dc0bd9092569d5f2c9eccd76424ac27d3ee231d8e153c52749004ac9a733134ca1e18a31e6b53b6c8b6b7cf828ec6744eda7bf57438a7fe843513b17979d02fae2bb084c01db96f09858895054a1608dd867648fdfe0b3e0a9d6b4b5f479eec5747e3deb16aaedb9c534335f697e2b875fd1d0f1a8a24c12a7dfd2a3c6b4bd6f1b077934e6d2df6d94643506b1", 0x98)
r4 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
r5 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000240), 0x1, 0x0)
ioctl$TIOCFLUSH(r5, 0x80047410, &(0x7f0000000340)=0xfffffffb)
r6 = fcntl$dupfd(r4, 0x0, r5)
ioctl$WSMUXIO_ADD_DEVICE(r5, 0x80085761, &(0x7f00000002c0)={0x3, 0x9})
ioctl$WSMUXIO_REMOVE_DEVICE(r6, 0x80085762, &(0x7f0000000040)={0x3, 0x8})
connect$unix(r6, &(0x7f0000000ac0)=@file={0x1, './file0\x00'}, 0xa)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
r0 = socket(0x400000000018, 0x3, 0x3a)
setsockopt(r0, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r0, 0x29, 0x66, &(0x7f0000000080)={0x3d, 0x0, 0x0, 0x3}, 0xc)
sysctl$kern(0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
setsockopt$inet6_MRT6_ADD_MFC(r0, 0x29, 0x68, &(0x7f00000000c0)={{}, {0x18, 0x0}}, 0xaa)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCLOCK(r0, 0x20004276)
setuid(0xffffffffffffffff)
ioctl$BIOCGHDRCMPLT(r0, 0x40044274, &(0x7f0000000280))
mknod(&(0x7f0000000340)='./bus\x00', 0x3a0914c44f7b202d, 0x501)
r0 = open(&(0x7f00000000c0)='./bus\x00', 0x10005, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000200)=0x8)
open(&(0x7f0000000040)='./bus\x00', 0x10005, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$FIOASYNC(r1, 0xc4504445, &(0x7f0000000240))
writev(r1, &(0x7f0000001780)=[{&(0x7f00000003c0)="a9ec9876f377b7f066ac0512b6c101694419d004d981d897801158b3f8035386ab7e676079c935ab1524219aefe6b5b2be69ded7c062c3730d1ff47f4a74685977f32387a82eb167077b46b0", 0x4c}, {&(0x7f0000000440)="e5b774746547d64e", 0x8}, {&(0x7f0000000480)="41d9c867310181d2ad53aa4779fba418d6b9c653fec55b2930dba51b7af9ea25a7698fde864e60151b7f75dc1d7eb6a431288e1bcf5e14ff14dff0d7017c8e1e312c43cf2773f649a046e6a7b4e3ce19a0b3c76728c52442ca800ca6e1a57c8e10dc82662579ad9e47339e29470de60c407c559af78030dabce76c5980ce2268b2a3cdebbbfa0d6a1d0edefa5a3f98751485bcad5c339b79f12db39c7e2be4ac27e93771fe30b92358e0961c3d795270d8da", 0xb2}, {&(0x7f0000000540)="c59fc791e3285b4769a23ffe998382a5cc3d806cff2d52b5a569bf1e5f26bf0e54bf14de15449cfb5c7476039ccfccdc7c575eff29093792ef5e3845f156b0144b29498d4a918f2eabed9d2b26b6487db612ee016080dce5f5c4e0ab444a04499590c8448a60942f787fd443a8347f0386f98ecd0bb4032b32e1888e53f6cd8af6e65d1d73b57ce2703d7b49e8018b6307c3acb9fc28f06dd4cffaafb2bb666cc49ac4cda28de745cf4f6edef2a6392f5e9ada4d491097b1811a60dc1d8eeb62d3af663d844c76384fa47f7016aa2efdf05eac5936f7098b264c8b50494ccaa6f8ff5bafb541e264d55caf9df77eb093114b5568825f221acc64bea6450d738c7f761d965a5068e29ff2b91e7f1601117a30efc3e7df45db36fa162e0509cf7b0620f9f89c85885584f300e0fe1188a44900372bdfa215bc39cf052c18101a0c063e512adc29aa302fe26d22a5cc3afcbd38c2bf81c2497f9558f3b0e1e8b8b2c2d0e3f2dae1587466061c93a3489d8af413f729bd54953f459ca16f338fe932c2efd8668d6e78763951d608d9c85f2601ea0ae4c6a167e3e9ec842b125f09701b7c381101d2770db58611d3771aed68ee97471878ea07e2e9e82627a445a4ff8b88cab5f3658b909ca7c1c96cfb1439c2e9cbe6b36069f4eef3d5a7fe317a8c11f0728b490ef98519457eb79318733e9d429fa2955cf5c633effd62655fa776b85416d6742941c7965a759a29ac33ec3a1dbd5135839876c8ac3335741a0d814254edd72f416013fe9eacb9fcf0c5ca88703d07d696e0c8edfa08859dac6ebb40c3f165df7c2041a0e5f6ee638a0f4bc6299732c1f96724839fba476e611fff9c85c9a5e7dc0ab322d4568c33648e85ef423ce2202c97fddd25247bba6256f24a09f10c6d5950f2b4bba03e3f006c668920ee0270c50167d57297c75f89dcab504b77557556cf12d18717abe25bddbf83097d9891ddb86dd19631561d7c6383f8b6dbd2780c197f62be2ccf5c24492af91a55d66af691bd72243515c03a860a1f1f063e9b3042da32693dbea989958250bda96fb4397bcd5514f43173fa8538d86110422f7ca2c415a2cdef089f8f051192a260e60bdac2e2f6379229140f079ba15a419dabbbe71a481c49e0a217fee0eead24ce38cc876089eff506c797bec9e420f248b9fbb5249c9700044d407f07952c74c40197eda55f8c3cfcd6168ec1e3c0f18229db06163cbe3a5d707399308c48b0852cb840ad38025c9602df94a5e83bd868a9821bf1de06238d4306a0f28134d1af821589c1b9e7b94e5a0c73a5db4bcb2c1af5304942953c9499869ae3120af817bed6646ac437f9939b0e9f8f66fb587aea511f73ed2ad49ebb065796efdf98260e4b5f1c042d5f5e45570b1ad180e245e36dcb0f482d0fde206088f52a04cde003132cc6b795501db2fab0fe88ceab422c46315466e86b4b616373cf1f79882c365f8bac3cc97235ad9e1a8ddb4896c5729c7669a4c0a48e3e4ed1f066870ea106a0aadbf0f01b7b5a2f28ee5379f632c8bee6ccde41830e790534006886b54f3d7f2ea521fffd5fabc3f8d41d6ba19d06c92d481bfab5e82fc351ab7e35f5751aa0a4b3497232546af1b5ee1d6378f1e53fb7de59f2ff4d5bcc6805e3f0116f48716f15f964a3cffbff4764e4e6144dcfc4b66eb7a74b435da5d39c3c0d0cc5933f78d0048a039eb399221fadb7f2d52272f839c1516b5d2120fee2cc491c8512e82f449769e437f5ad516088fc4cf27600ff1bbd426d5fcbace02c09d3f1a7464a433ea6108c98bacbb0d36c7f7a23b015e5b4dd6277acfb727eb97c216088fa9917c785bc05e7aee45b29aaf2f19446b6c24ad80b26977fd89734e2b2b38d56300e9b9fba80de14496ef1f7a5233ee4e0d21318ae99a510dc80e698c22d30ae9513fac010b47d93b2dd9610aab632f10295d07311089ceeb1d48a767e52ac78c9ac2480ad23caf7375ff206bfd2a13102baedcf9352d46c68d2a49c4232b1be6ca040ce4ff8c6e519954c25d538ca6afe960cadf969f10f7b07dc9059ab16e4bd6915faf4a4215aeb66b65c2e9036a09a55b58e3ee773114dcaae685e3d1f8d621f313c1ee76d3db6c6e71da4b1c5072dd90e4abbf6283e95a0d29559abcf08ffc1b9027691c76c3e51bb78d4ff0c9d779486d3e3a4261e55aec2dd649739f9157fa9ecd6f613ba9a02e4b5d49c4ada190c05f15a5bc1ee067b4b094319ffe0a5d3c678386a6cee8af269b5c5f896fb223f56c8ae98a03f9c3e98f6c485265211a789a869cc4ca0c1db08b543055a77dfe97ae22b5b16e00681fd1098d6ed927dafd19ae546b53a093fb0d20c43c449ee3351e5243c6119c927c57a1461d941dbdeabddeb0fad039557251d9374fa6fa4397e81515d72cb1a19fed3da25764a98a3b70baa5914b5084f254731f6b1e01205840fb0ddd4e5ab97e4eb52a3abd6454d9f27c0935165c73be811e150235f444179243b6b423aa6bba9d72f14f9377643d373ee8af22785f985b9a9a67b94be6e79614eb9ee6c40db8ec98599934b15479810d78157bf41755c7ad039b9abf24e41fa57341834b97dce2ed259ae330a5ea46ed691506f3f67034856358fffdfc09009e5983618cfb7fb3da66d3ee13e60e6675547536bf070aa1870e8353b37c584ec2015c7be1e7b572a15cc00f38fa66f36e8130afb3c2f2cef022d1ca354c788ddc07dc58dd23e226385999af5578ad20b650351aa87bb1f6b9d5245b914eff75977912089837befe84bc4228f829ff98f36cef124a8b12aa8890d1778df6916c9144647c68992c6a39ade6e7b8c378632337bd40b449e9ea4bef3d4ad91321d337a95ffba386bd82a43920e90ff850fc91c01515bd0f2e07f10fa7ef8cd83f0ccdcdf9a6e2e618ade65c08b38fed4686d056f13dad2f737bc59bb3a2ea1cbd6d7f84a92a589efddbab8efc61cca248a5fe1c472cefaa09b73df9760db0c18a95f65fecfe51012fc5cf3253d8fc198c3079aac05756a8a2fd03db20ab675c533c90b7b66a81ab34e0eafa9e24abbe0b15be333d062c103302bc660a71a009498c992aa01117688386f08020dc1771a3fc0482c6b50803bf4e75b1c94bc34ae5381a40643eee820b1e4f0c3a556609d976026c331e1648c3033eda63b0179fc8124eb4daff7a6a5c1c37f769f9428b103fc10ca8051053a65d3e65f285daede1cab6f94949e320919b2059734b08b3c0f0942279995115d896b9e3d01b2c97e0760268fcd6b75cec6baa48644179b858f9a25f194a2be53a9bcd417a09e3e83e3194ad954bf72b7e28a5f0492807c40f3966b49b059e07101be0adb4e0b5d4448c5cb24ff1889a377cb87d9ced71fdf8b97ea582655df95cca6036fbafcfe3afa11c088184550f853b46ad1fe20fd0ed60a4a633ba71d772e1e486954d498fddb0890fb6789e08f3102857493b178a4036e5ae219407879d08fa49802ed82082325597f453a087c789a559afdd31c1b15a8c8f772335e923821cc26ffdc1ce58ab876ed824f5cbbb74600ed99caa8e9336a37f548ec27d295c4f19c1af35cb08fc479d657528f289eeb6c815480e881e982aec03f1c595275b6054b51489f8396879a62b11c100145d52fc0cd8451c20d3e2f18b4761ca0774fc8bbff2480b24ea3e16db71e3584f6449b43922cd6c4b516941fe4985d105ba17d34b223b29a746d2b6b04e8c4b89b4451196c4d906d0d65c71c80cf0b692388c506d19c5b0fac687685c655b76a7331668fa8b8d066a8ff80d363eb3c88b4ad70a5009febd3e6ce55dd08c596fb31e60d0e851ff9235cbdcd02c9622a7b03119ae650a0ca2104696d2ac7295375d6c3b4e228c386450fbed15ef4afc5374cb64d6050739950e421242fa08de7aca1e8cbae71bbbdcd416dec465043528de77da6a707995f41b2c5143907be7460419dc6fe5e920e9c71d8f1e527d88e8310f907013d892e3b028e89f2ae273c0b36657aaaa2b2e637e3bc4acb4316a94135337b6ac24781d3e7f88bdf0f5b1cde63f7c6675027d584ae5f6d62bebd589a57dda3c3a1410af26f25ab28bd17a276c36138b2e53e60c2a3b695c50016464d243a8253cb35cc97ce34f59588f59ed124c70e091d6d21eaaad2590a2785a861264848072de63bb47ff2d9d593d6a21f1214b102257ce0de6a51180636e93797ba3cc25c1ae36341e928e2cc1a5206a2468beb69bd6f7f56b8eec2937fa7be1f1cd9336fde26fd8056d047eaa444a819ecb7874b804e816f5b2f8460d6be0415cbe9511f830ab899d5d16854d030d384906df23a59803e63af0da72e7a7f3844efe636c8ece09a32c1ca57361f2f54071e659404d55aa444e4cb85fb260cba752d028c7a87be9771ef59aacc1ba0d26188362014356a30b53e1feed4cb372db1a25493e05aacd765482835c5b54ee5f686c45ef74389c2f4751ee5ea451769182dc8ad89fb84c1f7493e1847e210178b2353fa50d71df7d9d5fd4ca90ba4951521ec830e2e9e1f6f8e8c4011285d099fa5c18cdb3304abf1ca1412be6ef0e6fec090496326d817544c96bd1ceffba1d3dc0b552eaaab5170a7b145629c25bb292a0c91bc05187f2cbe2f7489e9371132508369db27ae8da680d7a4aad6e1cc5dc65c5c87ea6c22683db00c8c256db936fbd9f8d73e7c2d1556a3d331b6fbacd6952b5343da23a243cd1d376f58cdf50e08a4c85e8c7225aaf092294be6e566a0bd6cc5e5776eaa896508fa26890db95b5df8b1fd35458bd8dedca0fb106e2eb9de2edd28ae95abe37a5258f5933f976f4f6853f1726948493d3a0d5c9d43d2f5f08dc69c8072c4c582fe1194128122a07ea7734f3ce2bca4b4940a5c5d8b524dbf9ebebd9859f1dfd261dd5b042782f90ce753d4350423cecffceeffce81f8617d48842d1c3ef71a16763dcfd51167a960f9432fa1879abb0ae25bbd323549c50d5f5e216376421fa49c46cd573af7e0f13916af2a27032aff18399b0e9198377b998a97c162620d9959ad37807900df72775f8204cb4b8cba56b743755d057373be3512bba1bd163d396fee84bd9df362809510b35a795a328fb1b71716ed11", 0xe20}, {&(0x7f0000001540)="91fc92876aa45e7bed2d9011d8bab2ea7c0ef5d62a7a7e34a5db87dd3e41ddeb4f56002bb6010e50f3470b1fd70115a368c22d5d", 0x34}, {&(0x7f0000001580)="272c8760c4d71ee9d89db81fc9cc005663ccaf3b5e3c0f36d06efee21c716e07edec431137dd925042f86e4c406e746bfda50acb1c097e8ac89dd4a661562adfa9f0", 0x42}, {&(0x7f0000001600)="c73ec46947a57332c31a0a70e51146ea4d16679d03e7a766071a5c131168de6e035f880f441efb567ab910855486e9109d7298b24e822f9cab03282a946cb1f6aebfe26447b3d36a3c1631ed51321204ed9d876fe5f12535be66823141fa5982b25fe16b92741c0591d9297db6b6fb1b60bdcbed119e6038e2afa3df7c531a36fdc7ceca8bb1de0f6b4f488cab5b6fa08ac546ec7030ffc16605e0622c572583e53df19edf8dd9d04da4bc02ef9df3f647f2142aaba61949a74761c2e19723ec4c485a", 0xc3}, {&(0x7f0000001700)="be14c579253b405ec55acc4cdc8545f1e64074e31c7eca4471ab1a179690ec8d960593e41446051c259100c9720e4f0ad314c309817f3d8bd2cb114619b4fc240d30086fc5cfd239cb1015dd791a7b7818c651a56ebacdd1d6", 0x59}], 0x8)
r2 = openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x100, 0x0)
fcntl$dupfd(r0, 0xa, r2)
socket$inet(0x2, 0x1, 0x0)
getsockopt$SO_PEERCRED(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240), 0x8)
socketpair$unix(0x1, 0x0, 0x0, &(0x7f0000000040))
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, 0x0, &(0x7f0000000180))
r3 = openat$vmm(0xffffffffffffff9c, &(0x7f00000002c0), 0x80, 0x0)
write(r3, &(0x7f0000002900)="921020b6e53ea03cfd497c9b0c681ae5598eea2c4a4a2105f0a00c67abf2adaa08413616c273d49b336dc85c753460c7852aba4917c2bd2fbf2b5d978b4f10af06ffec90006a37a3b462beebd3082f0d11839fa444ad904c223763a6995f49f4c55155b103351264a4b546f9a052befe1328be7a74fa11b48f19800976e6fcc0ec60eb2c41f783683f77d8ae33a76ce9f9e6ad2d4af625ee1eed448a943ba5276e5298142d18754c942d5616f5e38fd60961d62199a2db30f6f61fec98806e775ee45525721bf6a56912aebb96d130df3b62209a2253f54f65fadb06390efb29db2b424bf574", 0xe6)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104453, &(0x7f0000000140)={0x8, 0x408})
sendmsg$unix(0xffffffffffffff9c, &(0x7f0000000500)={0x0, 0x0, &(0x7f0000000480)=[{&(0x7f0000000280)="e07b47ab572b1086faf2d5fe56da0a9eb35b561f63e6cc75c0", 0x19}], 0x1}, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000001c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc110445d, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSFILDROP(r0, 0x80044279, &(0x7f0000000080)=0x4)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000001700)=[{0x0, 0x1e}, {&(0x7f0000000080)="9e8df56282b6e62b09db3fa81faed2a7ce94db2f3ae31f8205da7948710634bdd2af27c1c59eb9a9c4d00fbbfdfc818bf85687ffc4840ac3663a3f6836c85e6c811bc81b9512e91855f99d2b272aae6818d57193b1ad96116a379643f2d25797a4336c6dd4ec3d2c4bea374feba3f0734ab61e124cf10a1621171853191712041ecb4e54e0b5a342ec12216c5840afcad763cf74549fd8962790aa1309c0cc1bfc58a0e9524676bb3a669a660445047be080039515c7d82d3c94472656eb80edda2db0826377bce3c9ca19b5437941b689d3c6c00b5c1294b6d89afacb07fe747f620f31a50b672b", 0xe8}, {0x0}, {0x0}, {0x0}], 0x5, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x35}, {0x2d}, {0x6}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
recvmmsg(0xffffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffad, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x54}, {0x61}, {0x6, 0x0, 0x0, 0x81}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000180)="b100050460000000000008000701000000000000ceb1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f382b297be1aa5b23ed00f4c8b2ca3ebbc257690000132e27acb5d602000d7d026ba8af63ff37422902e4fdefe095bebd108ae070c1f5ab72c881ff7cc53c894303b22f310b404f36a00f7eb8c4a01be657aea844101b5496fe00"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$setown(r0, 0xb, 0x0)
r0 = shmget$private(0x0, 0x3000, 0x20, &(0x7f0000ffd000/0x3000)=nil)
r1 = getuid()
setreuid(0xee00, r1)
r2 = getuid()
setreuid(0xee00, r2)
shmctl$IPC_SET(r0, 0x1, &(0x7f0000000100)={{0x20, r1, 0xffffffffffffffff, r2, 0x0, 0x8, 0x9f86}, 0x80000001, 0x5, 0x0, 0xffffffffffffffff, 0x0, 0x7, 0x9})
mknod(&(0x7f0000000080)='./file0\x00', 0x2876, 0x40100800)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
close(r0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x45}, 0x3, 0x0, 0x0, 0x0, 0xd)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6202e5ff7f000001"], 0x1)
r0 = socket(0x2, 0x2, 0x0)
r1 = socket(0x2, 0x400000000002, 0x0)
setsockopt(r1, 0x0, 0x21, &(0x7f0000000040)="b1f5d915", 0x4)
r2 = dup2(r1, r0)
recvmsg(r0, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000080)=""/28, 0x1c}, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x800, &(0x7f00000001c0)=0x80010000, 0x4)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
bind(r0, &(0x7f0000000000), 0x10)
r3 = socket(0x2, 0x8002, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
r4 = open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
pwritev(r4, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r4, 0x0)
write(r3, 0x0, 0x0)
mknod(&(0x7f0000000080)='./file0\x00', 0x2000, 0x0)
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000000)=0x406)
r1 = open$dir(&(0x7f0000001240)='./file0\x00', 0x40000400000002c2, 0x0)
r2 = dup2(r1, r1)
writev(r2, &(0x7f0000000100), 0x1000000000000161)
r0 = socket$inet6(0x2, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0xffff, 0x2000, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x7c}, {0x40}, {0x6, 0x0, 0x0, 0xfffffffe}]})
write(r0, &(0x7f00000002c0)="bfa8fb47c4b3ff8918b15dc05e7e", 0xe)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001700)={&(0x7f0000000000)=ANY=[@ANYBLOB="fb1800135408fc00ff02"], 0x1c, 0x0}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r0 = socket(0x18, 0x3, 0x0)
sendmsg$unix(r0, &(0x7f0000001700)={0x0, 0x0, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r1 = dup(r0)
ioctl$BIOCSETF(r1, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000000)=[{0x7f}, {0x8e59}, {0x3}, {0xde6}]})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
madvise(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x6)
r0 = syz_open_pts()
poll(&(0x7f0000000180)=[{r0, 0x40}], 0x1, 0x0)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, "72a349100000f7ffffff00"})
syz_open_pts()
sysctl$vm(&(0x7f0000000040)={0x2, 0x4}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
mmap(&(0x7f0000fff000/0x1000)=nil, 0x1000, 0x0, 0x10, r0, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x2000, 0x204)
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x0, 0x0)
r0 = syz_open_pts()
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x0, 0x0, 0x2000300000000})
flock(r0, 0x1)
fcntl$lock(r0, 0x9, &(0x7f00000000c0)={0x0, 0x2, 0x0, 0x20002fffffffc})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000080)=[{0x44}, {0x44}, {0xfffffffffffffffe}]})
pipe2(&(0x7f0000000000)={0xffffffffffffffff, <r1=>0xffffffffffffffff}, 0x10004)
dup2(r0, r1)
dup(r0)
syz_emit_ethernet(0x36, &(0x7f0000000140)={@broadcast, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0xfff, 0x0, 0x0, 0x0, @empty, @broadcast}, @tcp={{0x1, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="a402c41c7f000001"], 0x1)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x6, 0x1, &(0x7f0000000280)="d5ec30", 0x3)
bind(r0, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_int(r0, 0xffff, 0x1007, &(0x7f0000000200)=0x2, 0x4)
listen(r0, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
close(r1)
dup(r1)
connect$unix(r1, &(0x7f0000000140)=@abs={0x1, 0x0, 0x1}, 0x8)
connect$unix(r0, &(0x7f00000001c0)=@file={0x0, './file0\x00'}, 0xa)
setsockopt$sock_int(r0, 0xffff, 0x1001, &(0x7f00000000c0), 0x4)
r2 = socket$inet(0x2, 0x8002, 0x9)
r3 = openat$zero(0xffffffffffffff9c, &(0x7f0000000080), 0x10000, 0x0)
openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000180), 0x40, 0x0)
poll(&(0x7f0000000100)=[{r2}, {r0, 0x80}, {r3, 0x100}, {r0}], 0x4, 0x6a1)
r4 = socket(0x2, 0x1, 0x0)
connect$unix(r4, &(0x7f0000000000)=ANY=[], 0x10)
accept$inet(r0, 0x0, 0x0)
mprotect(&(0x7f0000001000/0x2000)=nil, 0x2000, 0x1)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4d}, 0x2, 0x0, 0x0, &(0x7f00000010c0)="48bd0000", 0x4)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x2, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x4}, {0x7}, {0x6, 0x0, 0x0, 0x800}]})
pwrite(r0, &(0x7f0000000140)="b6a5d55ad177c01be442b791cf30", 0xe, 0x0)
semop(0x0, &(0x7f0000000100)=[{}, {}, {}, {}, {}, {}, {0x0, 0x0, 0x1800}], 0x7)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b10005046000000000000847d7", 0xd, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000040)})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
poll(&(0x7f0000001380)=[{0xffffffffffffff9c, 0x100}], 0x1, 0x3a3)
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x7, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, '\x00\x00\x00[\x00'})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc02069a0, &(0x7f0000000300))
setreuid(0x0, 0xee01)
sysctl$vfs_fuse(0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x800000018, 0x1, 0x0)
r1 = socket(0x800000018, 0x1, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000080)=[{{r1}, 0xffffffffffffffff}], 0x0, 0x0, 0x0, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000080))
r2 = socket(0x800000018, 0x1, 0x0)
setsockopt$sock_int(r2, 0xffff, 0x200, &(0x7f0000000000)=0x6, 0x4)
bind$unix(r2, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
unveil(0x0, 0x0)
unveil(0x0, &(0x7f0000000180)='W\x00')
mknod(&(0x7f00000001c0)='./bus\x00', 0x280002002, 0x2065d)
r0 = open(&(0x7f00000002c0)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000140))
ioctl$TIOCSTOP(r0, 0x2000746f)
sysctl$kern(&(0x7f0000000040)={0x1, 0x16}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{}, {0x74}, {0x6, 0x0, 0x0, 0x8000000000100}]})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x0, 0x0})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000140)="0000000000000008cfccfbcbd4a2", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000200)=[{0x84}, {0x4c}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwritev(r0, &(0x7f0000001400)=[{&(0x7f0000000280)="3985677b72fff98685f89007e6ce", 0xe}], 0x1, 0x0)
r0 = kqueue()
ioctl$FIONBIO(r0, 0x8004667d, &(0x7f0000000040))
syz_emit_ethernet(0x2a, &(0x7f0000000040)={@random="831ee4407df0", @broadcast, [], {@ipv4={0x800, {{0x7, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @multicast1, {[@ra={0x94, 0x6}]}}}}}})
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
munlock(&(0x7f0000ffa000/0x3000)=nil, 0x3000)
msync(&(0x7f0000ff9000/0x4000)=nil, 0x4000, 0x4)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
open(&(0x7f00000000c0)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000140)=""/108, 0x6c}], 0x1)
setrlimit(0x8, &(0x7f0000000480)={0x7, 0x40000000b})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000080)={0x10097b0, 0x0, 0xffdffff6, 0xffffff5d, "0855c40100000008000100008d1b38b852f50400"})
writev(r0, &(0x7f00000009c0)=[{&(0x7f0000000140)="9b5734cd95164b10974bb14f9d225e75a810ff20ba11512c6db224d06bbff6a551809852b584a99f0153ee1e908d3fea55f1ea1769ea3e847cf13ad27dee66aca2e48d8c835a6710d648caac452b1704de20a9c58b9d04e3c7909ce28902956561bf0bf2d765fdef50ddd3e2756eb9c439c18921e5dd8a22314ffa5ea2c52f15bbb5308ddbe058f45173072c632cca5192821706966d4115c15b7c141c98743b35ac5125cbbe6bfb927e0e9986c7271b0745619723415f0c717d8044232bb77411b4f61306c4a52ae54fbb817f09b5ec1da6c275659b356e562c024277a9d82ab6fbe11fd1ed23afab0b6e5c659bee7e2bdb5481703b7704116603ca6ca0bcdeae37adff771b43c1f04868369e8fff997f4c9f413db04a41c0dc2d13aa6323832282a50fdd09628743efa632a486d1c3d040b60bf4890b5775294b3be665cac0973ba27d50f700d0e7deddf53fbd4c69e63777ca58fa467cbf7110d1ad86daae928d27319e79c65d65a8b1a2703be74a4ea22295167d9bbaa66363a8beaa78744db12fe3757e00edd6ddfe4ecfb5c779c68dfeceec5c91b7e1bd2765eee2a49f1fa8c571d3e76ae684266a435c34d7b432fb964f5edb334e8e810ba2c72ed9e60acaca1f2844dd730ae78b0e94daa6340f332c6136fec572c091ec964f479d3f09601e05a4265ae5f4d3a70b8530f85f78b162764959532374a5f97ddac09069dc84", 0x202}], 0x1)
fchown(0xffffffffffffffff, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0x4}, {0x6, 0x0, 0x0, 0x481}]})
write(r0, &(0x7f00000000c0)="0400000000000000badc00000000", 0xe)
ktrace(&(0x7f0000000080)='./bus\x00', 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f0000000080))
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106978, &(0x7f0000000080))
mprotect(&(0x7f0000001000/0x1000)=nil, 0x1000, 0x0)
r0 = socket(0x18, 0x1, 0x0)
shutdown(r0, 0x0)
recvmsg(0xffffffffffffffff, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000200)=""/199, 0xc7}, 0x0)
recvmmsg(r0, &(0x7f0000000000)={0x0}, 0xffffffffffffff4a, 0x0, 0x0)
open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$VMM_IOC_INFO(r0, 0xc0185603, &(0x7f0000000040)={0x11, 0x0, &(0x7f0000000000)=""/17})
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
open$dir(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)={0x3, 0x7ffffff7})
r0 = kqueue()
kevent(r0, &(0x7f0000000000), 0x38, 0x0, 0x302, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x1, &(0x7f0000000040)=[{0xa8, 0x0, 0x0, 0x10000000}]})
writev(0xffffffffffffffff, &(0x7f0000000340)=[{&(0x7f0000000ec0)="44000f3e265873b91d1195de6aae7fd0cc327233a9ff18e27ff8328f8251e84ab281bbadc0f1b0c9dd01a66c110fb46800f5fb1830aad6f149cd793c3e9703e27bc23ee4e7747097a4d928098f2d5a4f468527b58a5569e6cce319ea39307452d1f9fafe54024e3fb0ba0ae44677cc64d889534d97dab3a38dcd661d32c19ac82ab98e8b9c86930092eec37e297a0181389db8759d071488187d169caf2fd0b36667a99b4aacbf5c5d22db", 0xab}], 0x1)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000240))
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$sock_linger(r0, 0xffff, 0x4, &(0x7f0000000000), 0x0)
sysctl$vfs_ffs(&(0x7f0000000000)={0xa, 0x1, 0x11}, 0x3, &(0x7f0000000140), 0x0, &(0x7f0000001140), 0x0)
mknod(&(0x7f00000000c0)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000340)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSFLAGS(r0, 0x8004745c, &(0x7f0000000000))
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f0000000140)='x\x00')
open(&(0x7f00000007c0)='./file0\x00', 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffff, 0x0, "0000000000000001008e7d00000000ddf500"})
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000000)=0x1d, 0x4)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mknod(&(0x7f0000000100)='./bus\x00', 0x80002009, 0x3200)
r0 = open$dir(&(0x7f0000000180)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000380)=[{&(0x7f00000004c0)=""/230, 0xfffffe9f}], 0x1)
read(r0, &(0x7f00000008c0)=""/214, 0xd6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000001c0)=[{0x1, 0x0, 0x7f}, {0x7c}, {0x6, 0x0, 0x0, 0x7b98}]})
pwrite(r0, &(0x7f0000000300)="c8dcafccd1a6f48da693157343cb", 0xe, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0xa33})
r0 = syz_open_pts()
close(r0)
syz_open_pts()
ioctl$TIOCSETAW(r0, 0x80047469, &(0x7f0000001780)={0x4, 0x0, 0x0, 0x0, "1ffffffaae00"})
writev(r0, &(0x7f0000000300)=[{&(0x7f0000000040)="d3a26692c61680bbef2d7b19af8aba5d19410fce415b5e7b42c447ee439094beb400b807c2a1674a1982bd04eb6c6b907c64939176b082e7a73e15d530d563592b5289f74d4289608f9bf3693e89831271dc052b13237f9108909942b40bb898f092168fa35674ec10c77c905c456d0cdddc72d293ec63ccee3449768b0df15ef0d8b219e37fcc97cafdb6ea04e8e9a73e9b814b23cbc46d164a411c07c60cf6bfd780e468581f5fab0de01fefadf79b1ab592c65e007299cd9e65af42ca6f9f4b7de9f5cdd2eafed203f9844db7533eb5e289b12d08", 0xd6}, {&(0x7f00000001c0)="a0a613d81178052f7df57497fadeb4b54b26af76639472fd12b0441df429068b4f9a9736295caf9f1c11e41397a4a5d51987afa5f63f5fb4bf98", 0x3a}, {&(0x7f0000000200)="6d0d8a723ede1510838f522d71ee4ebdc03a513ff81d74a4c5cbf2185639b89497b5c31f8eb3eb6c1bceb9b346c50007d8654b5302d231e305e1e9bf4ec74739086a20e42fbd2f91be6aaaef5e2a446c8bd5692b60f06ab4fc8fc8ad47fc46ba421db2d3322efd1170d80a5f3e0fa4f528e87a23c14ec0012297d5fbbedbc8347a4da50d3bf98946bd09bb083d085332100c02745ca0b67073c9c6387edc192c184e1b65f19cdb67bdddaff03c460ef00590b8654c31a30e37dd652e7659b13db1af6e129d59fbd07903b8d80c7269755670359494f76f7c72df645c74ce1ae01faedd495740", 0xe6}], 0x3)
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105727, &(0x7f0000000040)={&(0x7f0000000000)=[{0x6}, {}], 0x2})
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000004c0)="f1000504000000000000080001010000ff000000cea1fea7fef96ecfc731480000006caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c800000000c257699a1f132e27acbdd602000dffffff7faf63ff372829021a1d12855a278f94049c18cee4fd89720fd3872babfbb77081f5a872c80b404f36a00f90006ee01bc43ed907635b2e4e1ced7cfaaea8c580000002000000000000020208a371a3f8000400eaffffff0000010000000000000000000000eab1597764e1acb005dcfd98a7568980269bfbab29a03b64fade73d24eaae664f741518800"/241, 0xf1, 0x0, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000040)={0x7, 0x4, 0x300}, 0x3, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000080))
pwritev(0xffffffffffffff9c, &(0x7f00000001c0)=[{&(0x7f0000000780)="a5b4dbf9a83b466704e20715026f7531d77628774710e9da276aec3b0a123f1060936798ed3ec71c304dd5eb842d75561d02fb8d1102a2602ab41c7bf3107906e3d3be1372b694334693fe76f7e18aa7a1613ebbadf877f7742913c21d839053ea62e2fbff3c3cb924518f3ba1aa4aa59bf14f1007f7ec2d785147dbe9aa0f17e2f8904c247fb7272953f5a4ac5449c2fded84f2ef7ebd457bb52bd0a454bb3dd8aeb525d2fc745c0c5c313794b79d7e342d35822f50addce196eb98bc8a252c7ae9f85f08e9a273ec269eb29a9ba750c2b3c2273c70083978b85dc91b5eb3fdb6e275e1196028169187e51a50bf9db241a6d2febe68e2b15481674a059d7841695b4530d13a992999657b4a1f5a3c274e266eb03dbb3adf75a2d6c1ef154fbfe0d61ae1ffb72ade366bed2920bf2607f2f9fafc8f04caa7b1d4374f0d9777f5eea1d5aec297667916af73d83ff41437ef6785d17a5e314707c3eea815efb29110fc37863b068ea190a4ecc326c5f6e95d09bf7e55e3d4968b05a35f62d48ff47f1c0ab405ada284318170a62b2498aaa084253f25ad5835b0e44b00abe230be2da5697b6aee716d7143c521ea6817b39887aae653e3f1bcf3d61aba7310ea39e2da277a6c8c2c2820f46756e5897f4309d0f196cdef4f5ac6c1dc17305ae9be5f97f7ff219d64d151e707f19cb68d1747ab3121a31bb6e1ba0df815c9bf1db72c48f18ec0f978de14eafefcd128b2beb65fa3d4b5a31c6165792b2b91f428e82a07ca7224840ed98cef622572aa5e784dfa6395a43c4db095ca5c238530680b54a504b98ce35be741cef1acf135c79533ed9a98c60460056ea22d83ab860ab9ecca602d0cc0c635290ed8f0178a208a0839747f9625e0b50b7de0f7d665d6567dc5a1acbc1cff05e361f0288cd72aacf77aaa1bb6a70dcd394dfd63b7a52b7c7d5f9898f5b9e19ec9af5b42e2358ad21f6c601774e2e5e6978505e2a47abb01157a83a9238f9d160d8312bb65bdb6862a32eb2e7259b9a57967d05001f5a0623b9a1d1515841678aebc66886c791de7abe38ae77ddee3df877db7ab4557f365c5fcd1031d1877c23eeb448ccd673e52e461cb20de6ed659750082ba86f7542da7afbeabbd57160887042cc5c73845ea9ced2e07e22cbe8691e9b87d8425a8155bc3dc7df6cc53df387be5ace223e3aab126a4c2226e94af69a918f64a697731f1791d6bd35f2dfa6ae9ec4ddde7ad58e05a7a9ee947a03bd7ffd0c706674a0160221b7336c1031721f9a700b6fc6dc85078c131b086c4b0da1ab1febf03cc194dcd296db31c21247cad263c57ccdd1ae4033013f72601e861b757703295c69bce56f29c1d0383758a83482ca89a64190ad4f8d88df825291cb0e2748ff4b135a335e6f0db15ddeb26e90efc75999f41e661d942c36db9c75dfabc116b9c384b8c145ff5d5ef4c5523", 0x409}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000680), 0x1, 0x0)
ioctl$VNDIOCCLR(r0, 0xc450443c, &(0x7f0000000740)={0x0, 0x0, 0x0, 0x5})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000000)=[{0x80}, {0x61}, {0x6, 0x0, 0x0, 0x1f}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
write(r0, &(0x7f0000000040)="34cf362b3ce9c93d7f0000008e70", 0xe)
r0 = socket(0x800000018, 0x1, 0x0)
poll(&(0x7f0000000000)=[{}, {r0}], 0x2, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000080)="0e6ed84b", &(0x7f00000000c0)=0x1, &(0x7f0000000100), 0x10016)
mknod(&(0x7f00000006c0)='./file0\x00', 0x2000, 0x200028bf)
r0 = open(&(0x7f0000000480)='./file0\x00', 0x0, 0x0)
r1 = getpid()
fcntl$setown(r0, 0x6, r1)
fcntl$setown(r0, 0x6, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000080)='./file0\x00', 0x23b)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f00000001c0)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000200)=0xc)
chown(&(0x7f0000000140)='./file0\x00', 0x0, r1)
chdir(&(0x7f00000001c0)='./file0\x00')
setuid(0xee01)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400", 0x17f}], 0x1, 0x0)
writev(r2, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r2, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000100)='./file0/file0\x00', 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x40, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f00000000c0)=[{0x30}, {0x84}, {0x4000006, 0x0, 0x0, 0xfffffffc}]})
write(r0, &(0x7f0000001480)="23295428e2fa106bdf4ba040352d", 0xe)
syz_emit_ethernet(0x36, &(0x7f0000000040)={@broadcast, @empty, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @rand_addr}, @tcp={{0x2, 0x2, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000200)={<r0=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001640)={&(0x7f00000004c0)=@file={0x0, './file2\x00'}, 0xa, 0x0}, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000001440)={0x0, 0x0, &(0x7f0000001240)=[{&(0x7f0000000240)="2386db97b36f149d6807b73ec980d70d2b5164e35c07718330b16c55574a5b61e0825e3b98b8f6da60782580b56e1f8386313274ce9d12d92f7bab8618e1de96c5f2553c913227f2772e5d9ee466f1473d770bfacd78d075ae8b7a7d52c805e8e5a801b822b4d0697c6682eb05ea8b41b04c4f81bcbfd69be1027a49526d016366404dc666d9018ad8b145ecab54e4faa01106bc5892902f0b56be549407298184efea297bda824dd87f58f442db708732cfe7276bdb0ff09f6ecbdf7d9df8bff5b8181025609afe3201e746b06068dac9d7003b9282857ba96b33f60d3a839593ca4d0c9b590d94e726662ae91269efd6c7a3e8613412488dd7820a11816e3c7099c4bfaeeec453b71b6c29077c890a845ce114b9e77bcf347ec4fb0e13a5016674515e724147a58d72a09b30f41919b3b2d38423d11c26dd7e77f8c9b7bedb4bb9dfe30c67bdeb7ac3f1547f7ca9c86f59195ad94c3c438b8d4d875df1f343ca8c06dd993381a806ba2932fe6e697b5bd9754a4da4fb1ce0ca6c6d43f627970f25dfa44ec9ebcad3c2ce0ae749cbcff5e46d3a3f58aec0f4202baca71627b6bd7a65e375be1c44157a204b6a88991f223063fa892eac53f375c6ebb16b1da3652af41c03ec9605fe1a3e9be808e6d6dd94633f1d8ad187897037ad42f087c40f3fd73e2e0f3dfc78cb1c1537e190dba58c55dd80943c1c6ee314f401b42dda1266099b4e85f823a1447d8ce5aa3e085e59fefec957b6641c5f40b44f64a53b178605042944f6e3f0ccdd98cd185491443c22806a485aa3b6fa25310ef179e1ce56bcd020a380c375ec0154432c301f27d52409e58435f4ae19b1fbe75d1ecd4e4af0ccc9ede9a44b43cb160834ff1f952e6f62e50ab7b15a7310817c59cf49feba140e03437e5b95054a22dc627aae", 0x290}], 0x1}, 0x0)
sendmmsg(r0, &(0x7f00000015c0)={0x0}, 0x10, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{}, {0x3d}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000100)={@remote, @local})
sysctl$net_inet_tcp(&(0x7f0000000080), 0x4, 0xfffffffffffffffe, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
chmod(&(0x7f0000000180)='./file0\x00', 0x23f)
getgroups(0x7, &(0x7f0000000080)=[0x0, 0xffffffffffffffff, <r0=>0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff])
setgroups(0x0, 0x0)
chown(&(0x7f00000001c0)='./file0\x00', 0x0, r0)
setuid(0xee01)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
chmod(&(0x7f0000000040)='./file0/file0\x00', 0x400)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8060694a, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$VMM_IOC_INTR(r0, 0xc0084428, &(0x7f0000000140)={0x2})
ioctl$VMM_IOC_READREGS(r0, 0xc2485607, &(0x7f00000016c0))
ioctl$TIOCSFLAGS(r0, 0x8004745c, &(0x7f0000000000)=0x1fa)
setsockopt(0xffffffffffffffff, 0x0, 0x4, &(0x7f0000000480)="8660022b", 0x4)
r1 = open(&(0x7f0000000280)='./file0\x00', 0x10000, 0x82)
r2 = syz_open_pts()
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f00000000c0)=0xa628)
writev(r2, &(0x7f0000000680)=[{&(0x7f00000004c0)="1574b126532b817f824d91fac9e0891c37ec265b79b30d9f0bad482160bc4e241518e7b65a3dc567f86063244fd9b0093816158b0b62141466e7b7a2a5a8dc2ba3310089790cce4e4fd95ed304f543ebc0a3feb80322e6b9a4e595133b8194964259997125a14d2a8aac8fdd9ddff377f91cccce30b4ca89a8fcc9d72d92b55ce63275235a23e66e759ba8e8014252169bafc7b4f464dc515c5c198ea0c0d850e7a19a915d31156478d3cf67d703660a1d11fae130ac6d0d4edb0fef910c94de2f8b33f773765996e818bac2aa1f4f5845b6f9d07ef48422076fd392c1a92922ab31d95dc85a55cede0afd9a629953215a3bf8abb73f4480ed059d39", 0xfc}, {&(0x7f00000005c0)="19a260fc21209c529bb424243d85499157df4eec594b6af604745caa0c18526956c6edadd2209046af85c84fc7ea7db825caa9f563362050ffe8a445af735b12b1a947c3fbe2e16b6e0a981fcfa787d3c791c39e144029a22068654db75cf6a3ab1eec763a6bec4c9680605937b84f6323b28dc1cc76f8928f69c4746bc7769f5a8a99a667c04acd1c7c278c97ddd2a5b31e5c3b67b43c1de04bab09165e9de804cfe275ff07eef869", 0xa9}, {&(0x7f0000000400)="7ae6bf723212d6d37fe35f5d2638581c0d941555c834dd3b6d24f1898e50504e2dea31426d62f21d221f89c54daaafba7941942e503daa94839e950eb5bb736c24a6f078929f709d5449d410fff8fb41d76aee71713a4a1a0d18ac4972", 0x5d}, {&(0x7f0000000040)}], 0x4)
kevent(0xffffffffffffffff, &(0x7f0000000180)=[{{}, 0xfffffffffffffffc, 0x84, 0x2, 0x0, 0x5}], 0x20000, &(0x7f00000002c0)=[{{}, 0xfffffffffffffffa, 0x82, 0x1, 0x7fffffffffffffff, 0x80000004}, {{}, 0xfffffffffffffff8, 0x4, 0x8, 0x9, 0x7}, {{}, 0xfffffffffffffffe, 0xb4, 0x2, 0x8, 0x4}, {{}, 0xffffffffffffffff, 0x90, 0x40000000, 0x80000001, 0x40c}, {{}, 0xfffffffffffffffc, 0x40, 0x4, 0x401, 0x10000}, {{r1}, 0xfffffffffffffff8, 0x10, 0x20000000, 0x0, 0x30000000}, {{r2}, 0x5, 0x61, 0x80000000, 0x100000000, 0x6}], 0x4, &(0x7f00000003c0)={0x7fff, 0x3})
ioctl$VMM_IOC_READREGS(0xffffffffffffffff, 0xc2485607, &(0x7f0000001440))
r3 = semget$private(0x0, 0x3, 0x56)
semctl$GETVAL(r3, 0x1, 0x5, &(0x7f0000000340)=""/192)
open(&(0x7f0000000040)='./file0\x00', 0x611, 0x0)
dup2(0xffffffffffffffff, 0xffffffffffffffff)
ktrace(&(0x7f00000001c0)='./file0\x00', 0x4, 0x114, 0x0)
setreuid(0x0, 0xee01)
stat(&(0x7f0000000180)='./file0\x00', &(0x7f0000000200))
semctl$GETZCNT(r3, 0x1, 0x7, &(0x7f0000000440)=""/4096)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000080)='c\x00')
utimes(&(0x7f0000000040)='./file0\x00', 0x0)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
kevent(0xffffffffffffffff, &(0x7f0000000040)=[{}], 0x0, 0x0, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r2=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
sendmsg(r2, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
getgroups(0x7, &(0x7f0000000000)=[0x0, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0])
sysctl$kern(&(0x7f0000000000)={0x1, 0x45}, 0x3, 0x0, 0x0, &(0x7f0000000040)="726efde39848dfd3a6f8c83fb98b783512fba4d8e9da86b0a3038e703bb127bea9160f13ad07a978db939e", 0x2b)
r0 = socket$inet6(0x18, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000140)=0x8, 0x4)
connect(r0, &(0x7f0000000000)=@in={0x2, 0x1}, 0xc)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000002c0)={<r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000000)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000040)=0xc)
setegid(r1)
setregid(r1, 0xffffffffffffffff)
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56626afd70c6e9b3fda3181149ee114dd200a92ef2f465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53c6bb7cf2394a7ab24012dc56fe2aa2786a7b4b39ce637ef3c53f88edcc539e72e5ff1031571ebb9a54c1ea8426de968aff7f470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d700"/160, 0xa0)
setsockopt(r0, 0x29, 0x31, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000200)=[{0x84}, {}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwritev(r0, &(0x7f0000001400)=[{&(0x7f0000000280)="3985677b72fff98685f89007e6ce", 0xe}], 0x1, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0xc}, {0x4d}, {0x4000006, 0x0, 0x0, 0x7fffffff}]})
pwrite(r0, &(0x7f0000000140)="16f33a257588673225c1f6d5fcb9", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$VMM_IOC_RUN(0xffffffffffffffff, 0xc0205602, &(0x7f00000003c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000c40)={{}, {[], [], [], [], [], {0x0, 0x0, 0x0, 0x400000}}}})
ioctl$FIOASYNC(r1, 0xcd604404, &(0x7f0000000140))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x2}, {0x50}, {0x6, 0x0, 0x0, 0x16}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
write(r0, &(0x7f0000000200)="0c183256e5d425cb6e96d99d0279", 0xe)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000000), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000200)={'tap', 0x0})
r1 = dup2(r0, r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000080)=[{0x15}, {0x64}, {0x6, 0x0, 0x0, 0xfffffff7}]})
write(r0, &(0x7f0000000240)="0ddb000000000000000c51a14ebb", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000100)=[{0x20}, {0x1d}, {0x4000006, 0x0, 0x0, 0xffffe000}]})
r1 = dup(r0)
pwrite(r1, &(0x7f00000000c0)="00000400009781103c079e03c34f", 0xe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0xc}, {0x61}, {0x4406}]})
syz_emit_ethernet(0x22, &(0x7f0000000140)=ANY=[])
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
r1 = dup2(r0, r0)
close(r1)
r2 = syz_open_pts()
ioctl$TIOCSETAF(r2, 0x802c7416, &(0x7f0000000000)={0x0, 0xf4b, 0x1, 0x1fc80d8c, "24180400000d00e3000009000000000800008000"})
writev(r0, &(0x7f0000002900)=[{&(0x7f0000000900)="462c6f356e75bb87756dbb240d0d", 0xe}], 0x1)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x1b}, 0x4, &(0x7f00000000c0)="f74e61b0", &(0x7f0000000140)=0x4, &(0x7f0000000180), 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x18}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x1d}, {0x48}, {0x6506}]})
syz_emit_ethernet(0x32, &(0x7f0000000080)={@local, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @random="500bba10fa98", "", @random="0e66f0b33d2c", "ec88ab53347a6d702bf67fbeb296bb46"}}}})
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc00c7006, &(0x7f0000000380)={{0x0, 0x5}})
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000180)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
recvmsg(r0, &(0x7f0000000300)={0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)=""/9, 0xfffffffffffffef5}, 0x0)
r1 = kqueue()
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
dup2(r0, 0xffffffffffffffff)
sendmsg(0xffffffffffffffff, 0x0, 0x0)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
kevent(0xffffffffffffffff, &(0x7f0000000000)=[{}], 0x0, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x11, 0x2, &(0x7f0000000000)="1a7b43da", 0x4)
setreuid(0x0, 0xee01)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x14, &(0x7f0000000040), 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x2, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000180)=[{0x48}, {0x1d}, {0x6, 0x0, 0x0, 0x23}]})
pwrite(r0, &(0x7f0000000100)="fbafca8d1a029be96914e187c2bb", 0xe, 0x0)
r0 = syz_open_pts()
writev(r0, &(0x7f0000000500)=[{&(0x7f0000000000)="ba08e02196d12b184a6144306dca7813705a73a2edb04c8609d3f4739f42", 0x1e}, {&(0x7f0000000040)="5c9de0061b39155ecd64f2afce9a22ba6486ecc848cf4941b5e08563fc154f54dfc5cc38b16244862edf177b2adfc3987f24ed56af5212fd6dd666b09fd68cc06f3328ac7c781e1d67a28cdfe425fc48e9e8624aa81e1bb2b475", 0x5a}, {&(0x7f00000000c0)="6bc755f8cf4a35c6204709942894cba67c5af10d207e784efc23a85f5fd0085b5d794afc77bdecc41a9f26f19f8cca01ab127d723c776e93dcc321a3808b20c5501d262463b2cdacb23a78ab7ecb1c46efda7c2ba07ca91594b95b11522f02f023edb0e2fb79add2ff528423ce0d9bd8c6c19f590262e06bb8106299744775dbab81e37bb98c4a68330b5f247496988d5142ea70", 0x94}, {&(0x7f00000001c0)="db22f94097573595d993afeb51b8eede1ad43a01295f84a049305a009608aec6fab77a1d7d1de2aee331fcbf04266cd54b4b67e5b1f1dffe803aa5095f6556e0ea94acedf6a8682bd58a629bbe044fced06b7fff1a9a1cb4ace64157f456c6e3e7de323b231b88eab547dcec0c4a9e9c5971ea3285edfa30480a2c8c9fc58806ff92a0e938f6cba13f27267a20f169f4781f40b4435fa809b50e009e4106255f69cc27b423f264c890db03d8357ddf49e59dd6400c3faf8eafd3566ec5d49a2d3f3d535b4f5b2efc0d2c339d8b1dd6b44ed50061b2c2548051218ac9221cf63ee48276a866b716267d6c331dd15fc55d27ad55e216", 0xf5}, {0x0, 0x2d}, {&(0x7f00000002c0)="906fa1e3e4d1b4e80b2d9d15aa76c8421db950db6579046d2fa3af6ae6a1a8e6a14af3f004c33878504cc379605ef7c978409aa5f4c68ef6b57c0f035590911b518cd7ac8ae9a46b2897840b465a81379ddfecef50830c3ab626f2440601767061b45db493b6213fe2a63e4f1b1478e3decb9de0bc306eb228b8a96db516f48e11b68d18ce703caa0e3af7f9328252f010c42bc7e6ad913c7ad2b4f54c5f7258b4c29d884773d3b8f5d2d219d631cc4e717929"}, {&(0x7f0000000380)="6b35dc0525e1fcd1574fbd601b0aa420bcd2694db32a05d9f87a87d09065e6266708896b1d99a31eac9e7cfb5d482f4bc93b0916591b69aaa210c25e728ef52cc02086a14354a3ffad6b48888d5241c185089101792df85c145f5306af557dd83307a0a65c0debb33a5c24ff1b682ee4660442b6f38d9edad7a9ef7f8034b4845a480060f32223baa2b23075cbbca5a5598ae8f3129f342ab73bd854024158202c4f1a33877e3821b0686067c72350ed0549543ce072e1f451feac5690b7059ecc21de4dd71c09769a96e41cbdc31413fb9c396f1c26f2712f7c70da90d40c98c78650ccb1ede607092ad3a179c64076ce8ddff511cbbffd"}], 0x5)
open(&(0x7f0000000040)='./file0\x00', 0x200, 0x0)
unveil(&(0x7f0000000000)='./file0\x00', &(0x7f0000000140)='W\x00')
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f00000000c0)={0xffff, 0x0, 0x20008007, 0xffffff03, "1f000000007e00"})
write(r0, &(0x7f0000000140)="f998244ce1a6c1c0697a2b9984f6952abebe84c9362dbdf33b4967320e1915ac0cff52fb2ef7b25d806c4f57805c2fac4f8fc720bb480f9cf3b713e22195d79c691186524124fde594ce0b259493f18d5122785267e77011bdbfcf3c464972f62b75a5d03d8e7b9787aa75661d298b5454aa73972eb8589c03083166561d906b59d627625baf36129c6d98aea34acc843edb05cb264ed228e0cd0d8e901975d2674874a039d3e4056a752174faaf5f3728eb7f4641f0807f203577672e4dbca21e05e7ab885417f147e09f5c5e", 0xcd)
setrlimit(0x8, &(0x7f0000000000)={0x7, 0x99})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000140)=0x9)
ioctl$TIOCSETAF(r0, 0x802c7416, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, "e809e027a7d6e0702b5776cf4ee1b657e06e1e94"})
writev(r0, &(0x7f0000000040)=[{&(0x7f00000000c0)="9b97b1c0dc5cb2d178e9442d78722c103fcb7481f28931d6542016e3dc5d1204f37fdb40de7a4f9b64a2618f3754f1593b100a1e", 0x34}], 0x1)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x800000004})
sysctl$kern(&(0x7f0000000040)={0x1, 0x45}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fddfe0c1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x2679, 0x0, 0x42)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x1000)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x2, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x802069de, &(0x7f0000000300))
sysctl$kern(&(0x7f0000000000)={0x1, 0x48}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x47}, 0x4000000000000003, &(0x7f00000003c0)="33674dffab264afdeccee0d5c44007c5a2c0fe15acb9c70500acd1581cc7214a53e98f619cb9b45d039008d0d617229cb400a72af19bfa55d1bbdbff5dc9d5b0edf1088d2fb2ef4d8fa568f2ab41a815652162b3d197862240588b23a34c32f6d99ed93e44d94816534780d81b98a3f0b822606b90206e952b329a1dd604467d78611cf570498fea7cc4a592fbccc4000000167102ee3fbd1f2f0a76c1d58afefb45c1b69e6abeb64d3a268e207bb09cd06b02fbdc8c5f2873a8a6978422af158b81dbbe2fa8d232d456ca16fcf2de7b70bb9b3a728976f0d45054d3a93db15b35a88d90db1eaec4cc1b019e684c362526d702d7e81686cab2863db5939c10cb3453e4257a716fb87b1d01bd58402e6d86b175328a5bf5056de89364048d65b81b9a9c31ef18ce4b8ef250dbfb84f0d91451e7c35136e9244f3efa7edccc00e0508863fca6c1771abc46bfe76948d50a29d73a34a06f8b48f18d6c53a9dcccd6123737000000592e475d311941263f714c5c761f058f232b46156776a257e8984eb728c5c717aad2d9eca7375d8efdb21e5495baaa716648076333e55d04f33ffe06299826b9b62f622fffaf5d4d5fdf88a54a74900c266bdfb2fd8d52a8f313c107c0461bd9609c93f6945951c603d1f3ea763169b0741f0ee2c6dcc17f5fc9b4ccdb89b830d30744acf270e6a7bb59b3b919a44466b303cd5f0edeb6a4d44d1c6d5fa34d9b22e62aa3c157a3fc43", &(0x7f0000000000)=0x20f, 0x0, 0x0)
r0 = socket(0x2, 0x8001, 0x0)
close(r0)
r1 = socket$inet(0x2, 0xc002, 0x0)
poll(&(0x7f00000000c0)=[{r1, 0x4}], 0x1, 0x0)
connect$unix(r0, &(0x7f00000001c0)=ANY=[@ANYBLOB="82023e"], 0x10)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
sendmsg(0xffffffffffffffff, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000040)=ANY=[], 0x7f}, 0x0)
r0 = socket(0x18, 0x3, 0x0)
ioctl$TIOCSFLAGS(0xffffffffffffffff, 0x8004745c, &(0x7f0000000040)=0x7f)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg(r0, &(0x7f0000000100)={0x0, 0x0, 0x0, 0x0, 0x0}, 0x0)
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x1}})
r0 = socket(0x18, 0x1, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000003001004a14ff4dc000100"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x8)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
r0 = socket$inet6(0x1e, 0x3, 0x0)
sendto$inet6(r0, 0x0, 0x0, 0x40e, 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r1)
r2 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000480)={<r3=>0xffffffffffffffff})
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29f", 0x70}], 0x1, 0x0}, 0x0)
r4 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$FIOASYNC(r4, 0xc1084413, &(0x7f0000000240))
getsockopt$sock_cred(r3, 0xffff, 0x1022, &(0x7f0000000240), &(0x7f0000000140)=0xc)
ioctl$FIOASYNC(r2, 0xc1084425, &(0x7f0000000240))
r0 = socket(0x800000018, 0x1, 0x0)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892, 0x7}, 0x1c)
listen(r0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "0000000000000001000020000300"})
r1 = socket(0x18, 0x1, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1002, &(0x7f0000000000), 0x4)
connect$unix(r1, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
sendto(r1, &(0x7f0000000100)="589a", 0x2, 0x0, 0x0, 0x0)
close(r0)
close(r1)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$SPKRTUNE(r0, 0x20005302, &(0x7f0000000180)={0x100, 0x20c49b})
ioctl$SPKRTONE(r0, 0x80085301, &(0x7f0000000100)={0x20000})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000000)=[{0x87}, {0x61}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000180)="ea80e201b6f576c45af84a280000", 0xe, 0x0)
sysctl$kern(&(0x7f0000000140)={0x1, 0x41}, 0x2, &(0x7f0000000180), 0x0, 0x0, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000080)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/\x00', r0, &(0x7f0000d06ff8)='./file0\x00')
chroot(&(0x7f0000000000)='./file0\x00')
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = kqueue()
r4 = fcntl$dupfd(r3, 0x2, 0xffffffffffffffff)
close(r4)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
recvmsg(r1, &(0x7f0000000700)={0x0, 0xffffffffffffff36, 0x0, 0x0, &(0x7f0000000400)=""/210, 0xffffffffffffff2b}, 0x0)
sendmsg$unix(r2, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
sysctl$hw(&(0x7f0000000040)={0x6, 0x14}, 0x2, &(0x7f00000000c0), 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x80206910, &(0x7f0000000300))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x2})
sysctl$net_inet_ip(&(0x7f0000000040)={0x4, 0x2, 0x0, 0x1e}, 0x5, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{0x7c}, {0x1c}, {0x2006}]})
syz_emit_ethernet(0x22, &(0x7f0000000100)=ANY=[])
mknod(&(0x7f0000000180)='./file0\x00', 0x1ffb, 0x0)
r0 = open$dir(&(0x7f0000000040)='./file0\x00', 0x2, 0x0)
ioctl$FIOSETOWN(r0, 0x80047476, &(0x7f0000000240)=0xfffffff8)
r0 = socket(0x11, 0x3, 0x1)
sendto$unix(r0, &(0x7f0000000140)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae27caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebb4257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd007f720fd3873babfbb770a2f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100000000000000100000", 0xb1, 0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000080)={0x4, 0x2, 0x6, 0x9}, 0x4, &(0x7f0000000440)="0118fff6360f9ea14fb359657f16666e9f97069815ca5835b6f65316127c991ab43afd5604c4aa10930ed14b1088b7d8414191ac6193bb09919a8a372208b127f29c66755d45d5ae11c6731aede78c4421cef62cac7d5ecb3a69b3e7910599897b40c8c7f4766c3bd9ca5112b32966a5c0411d0be46ebe5981ea2de4000000000020a63fde26f5ca6a157ad15000ffe6c35b55a191701155a29aabecbdada66c878f486f7e59a59a05bb689915b909800989d8d1fbe686246fa85c22ad066d2bee08f7397cfe2cae6e966e98d4c45356c7ba884245d73f252d74b0bd01000000201c3f30e790e6157cd0f6ac7354745175bd902a5f48e0a013a1dc24244ade0d510672dd77da2c8ffff2ec00000000000904000000000000000000000000e7e3adc9f94ea40021000000000000000000000000000009eb3881885647e6b9ecd6bff6b37cd49c4287ed75b08a58f19f470bd87e5503c733fc217eb57458e55df302e2d611ae3e030100a9edbd2d2d845b8e1f2e111835a6b788d5ff5256df19b563ef69e55e74120536a99d2a43575893f400c7c32ed7a1d4dfedd53dc24cb41b274925139f0ceb63553689a46145fc7f2c30c0d29de0815e8214f857ebd1f1e44876fbc01f5fd7c83c8e401bfb9a21624824a96d9600e00feb108d5bb60a27d465014bd7652b72b51f92cb83eea6b41287a3d88b37e33543c0239aec8aeb60db0242a3044bc0955254edb0450200b24c", &(0x7f0000000040)=0x210, 0x0, 0x0)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x612, 0x0)
writev(r0, &(0x7f0000000080)=[{&(0x7f0000000100)="4d5b4b3898d5b15518685bc8fa1102a65d87bf5dd2c2242a4413bae1c6224ee5bf8cc352d8c20fa3d746e2d605d1de69e0887fd49653141aea93819289830467691562b816a0b942cb0468a2d16958fe54439171d69cdfc33caf4be41f60295a1002696af5c8ffc1c439257ec807743d1919735670617114fd772555c2487099b1b8c4d9bf7f1b030fb9f4fed2c4c2c656ae8f75ba716392673ebe932aee1dad171f022c6e6a43e166859292ded19f15b4b69b2ef7bd848af2c5669b89f652970fd7a0c1ebc9", 0xc6}, {&(0x7f0000000300)="9150792e8355146f1dd3", 0xa}], 0x2)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
setrlimit(0x3, &(0x7f0000000000))
setrlimit(0x3, &(0x7f00000000c0))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x2}, {0x4d}, {0xc76}]})
syz_emit_ethernet(0xe, &(0x7f0000000000)={@random="08277481641b", @remote})
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000000)=ANY=[], 0x2, 0x0, 0x0, &(0x7f00000005c0)=ANY=[@ANYBLOB="380000002900000033000000010000005d04", @ANYBLOB="0002"], 0x7c}, 0x0)
syz_emit_ethernet(0x36, &(0x7f0000000140)={@local, @local, [], {@ipv4={0x800, {{0xa, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @empty, {[@ra={0x94, 0x6}, @timestamp={0x44, 0xc, 0x5, 0x1, 0x0, [{[@local={0xac, 0x14, 0x0}]}]}]}}}}}})
mknodat(0xffffffffffffff9c, &(0x7f0000000080)='./file0\x00', 0x2000, 0x1e6e)
r0 = open(&(0x7f0000000200)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
close(r0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000040)=[{0x2d}, {0x5}, {0x2986}]})
syz_emit_ethernet(0xe, &(0x7f0000000140)={@broadcast, @local})
r0 = openat$wsmouse(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
ioctl$WSMOUSEIO_SETPARAMS(r0, 0x80105728, &(0x7f0000000080)={&(0x7f00000000c0)=[{0x22}, {0x26}], 0x2})
setreuid(0x0, 0xee01)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x1, 0x0)
r3 = socket(0x2, 0x1, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8020699d, &(0x7f0000000300))
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x6}, 0x4, 0x0, 0x0, &(0x7f0000000180), 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f00000001c0)={0x2, &(0x7f0000000040)=[{}, {0x1}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x27}, 0x4, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000d80)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f0000000040)=[{0x40}, {0x7}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000140)={@random="1b329ff120e0", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "0cd147", 0x0, 0x0, 0x0, @empty, @remote={0xfe, 0x80, '\x00', 0x0}}}}})
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443a, &(0x7f0000000240))
r0 = kqueue()
r1 = open(&(0x7f0000000040)='./file0\x00', 0x615, 0x0)
pwritev(r1, &(0x7f0000003680)=[{&(0x7f0000000280)="c4", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x2, 0x10, r1, 0x0)
kevent(r0, &(0x7f0000000000)=[{}], 0x80, &(0x7f0000000180), 0xe51e, 0x0)
syz_emit_ethernet(0xfa, &(0x7f0000000280)=ANY=[@ANYBLOB="3b1bfddfc55effffffffffff88a8020081001e0086dd6039789a00bc0800fe8000000000000000000000000000bbff020000000000000000000000fb"])
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000d80), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc088444f, &(0x7f0000000240))
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@broadcast, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x14, 0x0, 0x7fff, 0x0, 0x0, 0x0, @rand_addr, @multicast1}, @udp={{0x3, 0x0, 0x8}}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f0000000100)=[{0x25}, {0x84}, {0x6, 0x0, 0x0, 0x1000}]})
pwrite(r0, &(0x7f0000000040)="dd36dc71afd2843d3d14cc87c2bb", 0xe, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc008441e, &(0x7f0000000240))
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
preadv(r0, &(0x7f0000000280)=[{&(0x7f00000008c0)=""/4096, 0x1000}], 0x1, 0x0)
open(&(0x7f0000000340)='./file0\x00', 0x70e, 0x0)
r0 = open(&(0x7f0000000180)='./file0\x00', 0x0, 0x0)
sendmsg(0xffffffffffffffff, &(0x7f0000003040)={0x0, 0x0, &(0x7f00000008c0)=[{&(0x7f0000002e40)="9d", 0x1}, {&(0x7f0000001040)="15c482624e2ee25a2511c5b6640b6d55a02140539b8c5c64cf820da06f9e452716051b41122feb91eb5c5dc8296d429e855eee2fed95875a612cd5a45c646aec280a6363de96e201f35f6e1eb80cb44ab1efb8ff4262b1cdcdf2c3b44ba328123cd11052462a8062cf7a9e0bf2b17eedda7ddef8a31008abacd3ce88730a40e54835ee128cb13dad273f3f90fa291773d8185b96e81f5419c2c48d98af6e44b09bd72531b3bc674c1800e6635f2a08afe2d58df5b1013432655811dca6628715cd884b53beaca18ceea22d8913d8ffc08916df242b419bc24ec81f2f28449aa50e4f676756e2a7ab300861f5786443ec71b7ca5644204f2ade41fcdefd6d600a718017653cd69368c366cf3cbae4f7c4409716e0fa62496387923035400d8af46d0868fccc1559576d5d7ef258f4aa90422e9a4709df9e2dc02f0054f7593029418a18369edb3d8eac5a222c79d1aaeb2a943895db80a22fd9673f1e4d7533d4c477e54c0e2bac8cef530a6fd4196c4bc7056080f6ed28677e4f66da4b2d75cff5fa6a955b0eabe9426d94fb40559aa94577229ad34ca1f126a24fa929574b06d8cc3b6babac0001dbbd316ba205d75644d54c8fe37b9e4312672fa2a6deab803d6aa2d316e084288ae249c9d014fde4cc184fca2d19fd094cfe09f5934343eedf6dcf67e14d66ae6fd9f00d7de1a1cb4362924d2e00462749eba130dc8ded2e23ff499e5fdd6f1c745f5437bad67a4ecf7b127f79893cea514f98a3ba217a016d8b2e10cd0950359f24c6391f3fe66fa9cd177865404e9ca212de24f9a3365fe0553c658d3d26a66c30511bff8f7e83606c7f607fbbe50030f0f8e254f098c0b0adf686c4bddac8409f1ea32abbcad3f604cb80d96a907b0efcf3790e19408350e34672afe17f14c4dc178f488ecbfa2ee42a582ae3690c151812f05fab41c2b4a8da0030b54d945194c007b5dea1fcf591cb7dcd5c7acdf39f717d0eca181428552893ba64dd6dbdb172215425cc86fd1c6246e31f8c31fc596bbdd310f9451aba2bdc7c6bc625488c8f041eaab71b073b03b78736672873fd72a07bc1913da369934509964d5f282f4f005be53dd003b1f5fb9f9410d474863cfa9942ec48d71aced02b1b152b8d7ed9ac7cbae203174db0595168cc9efebe53ef7eb9e5f4be11346d5b0dec7fa4030756893c8fa717abac8185dd6805e7690f4f895f9aef0478d1bb361224a967002db3c74bb4d957c78ef0e90e20615b71d45eb1b4af37db5e01989633c2fed88cef4c6db8a983b1eb2a4f514d0f3f43c4310fe878b70d85461ac9336871156638448bc20a976c56ed3eb580d3e526fc90b30e7de9b36574dadddaa97ae86a20fd0f45967b15df1a89d51e81e3cb5cbd248bbd4ecaf5c28068b1be558e08ce9e9054ab126dba3ebc3d54fe281d5cecc79735b95b69b190e742a6af64873743e6a46815a55318d612f0f977c0a8da5fc657d2f6df06657b3aa6840bdb23bd9ab6be4f0c0f9726b86026e5fce92925622a97736cf0bfa9c170e6b592a7b8173b78efcbd1d01f852957515e636ea235366b4534e4e4776ab3a760490c5ef4fadc653ca0ca2504ae696a16f74b4473fd75f900a3e8d5414d226486d75bfa3943c08622aaf49e97245905f3d667d9e5cf6410b6b1e8efa4188ad9ed50b88d78f47c7095e0309e66fc187904a799841968a2065f1b997b5acb48194c7303735a9c26c61a34ddc947bca96a405bb7443af5f66614771d5fd7fe66c261fb85ff01f78f39d5b3d34b1ac423a6abc0dca1be9b1de6b080b45754306fe7973d85b475b2ad81889feb97fd66a621b3c0740bdf552ae2582177dfbd67412c3a84b3db9e1698113979271bab8c62979dec682a6754b9f4227c231d08dc41728b026167b04e6deeba9b9217a7dbb0fc749e3cc80fe08a38eb8f4792625d2992eb9767f9bb015e65ba468fc922476d649c5ce8a06ec96f62a1373a0c114c858439e723675a9f8af4d2ac20a2b744646f14e029c4c6d8427eda1128ae848ce26a39613274c6647368180d3a257e4b2f35856ed696f67ddbe57fe46a90385600904bc0deaca8c2d0a23412e2e52f33220fa83d5c1193bcf7979b79e591797f98076bd9f1ea0739cd835660e5d5095bb051edb14dd20e403b546ae59c4c03274a6ac074f44413d401562b3b42bfefc0865c264cbe59999caf3e35961dd44b63da81ff8d6aeef4497628f29eb8f3f59d81ca02580596a7fbd3bc9cf7c7f1116d0dcb495c42f73235f34a48ce8e91d847997ec7892a7ec49d16052474f7fbc837c3f3a22303c40052c72fa100349bab396f198b625810d31bc8b638b025962e9d8213e8a7370c5d09802eb2ae46d133c9acab6936d14c8cbcca22683f9ce04b78b54b24a687e1dc0251314aabe24834ade334c89ccfd4aafcd0b99cfdf1b28f31f3907f92cdca90d3e217854a096e3ea2c11687970452a91ebcd0451ce823325298a9e703379655544319428f187cb5d3848cded3b2d6b96b4a84353f4f89ed93349ea1a53fc6c78bb1d0359a8303481ba14e1159f90dadb5db6171c4b69d8a37dad3d02ac019f5c062ca72135f6491f4cd1bdcb6d2a5e4ebb08aa00ffe90f25b5fe2c68758078e3634a96f7042e1b63414b88c25fe0092501ca4cafd25e2c1472f30bc4c3ea031721e9ea50f0cf4626f5877b4172fe31a2a35205119dff202ec65aa9d3f3ef6266b2c012f5fccd12391351f0051d19e6117fda2aad6cb4eae753c37aeca9d819bfaab85bdfa5643bbfc7429ea616f97c2663b3cc0923e44a55d3fcfcb30968bd508628aff2cc614f9fd968fec68333b940035844fbf571bbac326fbfdabfe5ff134128e7d66bf9f6897c1ad8911c8fe6f97d21d2a6a3e9c09983dbeed1047793b2dd362917064ced51dc9ba0d773c3543a27031b49f2abc5948acec0ef006a2d07d570f7453a9e6d1ad4783102d3b54d6ec57fe4357a4d9e42afc39f4cda1d9deb26e06ea85e507eba2ac4d93145a6747c98b9e8e0bbd4609009fd42d806a11d594a747df0dbd14a348a4557d7aa5d83ce0b05434c2e357583df058051e05232c905328bf0a0cd58f84b66703a3d6e5f1f927c9f8f650dd3f305d7f7b424804d4556146703867a39548f303c1d949621eb100d0f9b5f61f7b7ef0174ded266fea8a96dc3cf52de97e1576cb3223406aa69dbe9a3a25c2f5b27b107f3623749144142ee83295918ae5676f8f31cef78b1cbc577bcd19460b171de2e65411f2a400c0c91dc80db37249eccd065bbce4f2750be670608322f3ffc8ad484cda42cc0826baf57bbe0d695e1b5f34831342afc08c4e9d2c5c77db396a572e8748c491a81905998d5cd45aac69b6db0ca7a35a452836a445390ed39b20414ef2d2da21f2f999c039110f0561aa4eb12e278d17c7b0f5180c96ab474c7e1b747f3d939a989faa22679427754b2558cc0fd8c4ea0edd1af337bd9aff30ef677cef5baa641c26a3973a3bd8061253e9e9a57943db55f1f92c43961b29ebc479b050ff797bd43d50a6df05aa588ed3f5a42f7cd1409a1fabe7f51ce830d5e3bf140a701a67d5d39fedfa06f6e687e89b11d008c2d6a842b7d8befee440ff219e1d5acf509385c8411475245d01088c47cc48c3a8b50b3bfc89af075158cf89bb9837f148bafad795e69d26981bf096579ab08016d6cc3eaa09b5d2d9c82c85165ddf827234e4d31bcb04ecfbdf72fa17e0404f4633297a363004cbf8ddf3ea28a3eb5420df88198bc814a907e4f064db4ec52e081ec4622a4e5f63ce684e0d975da134c95d951fc06a6cb57d7cf593022ca2bb916a87ef9dee7a0c6e0c9f485f4d9ab0b1a29c7de304791cc586a2dd3cfb9049c2f22ea7c431a3f781e207b1281769108b186ffc39d93900abbf718f1af62bf5d7b65b85918569c35c02967f2cfbc334a0fdd1ed486af202f278b58c4bc2b7e1560c61fd9c97e44be43516e4681654190d972b0a88281126a27988f32560f6a3316e4486983bca6c44d880f8a495ba6e329240a218397fddf0cf6d4655eebc785bee58b971d00b601c4a9701158ebe25115aebfb26dc9f475dbe2f9b14472cd60d8ef03b4ee8e54af813fd55e16bf232f447f3f298ff3db73d42cac1f08360d04695ea6e8aeb4c369d07ac9ead282808fb10447e58a60c6dcc2d84ae421b71e3e48aeae4bcecc6e391b6e64cc28975664d39838c892b4a4f671a7655236cf53e0bf7cbd221293e88ef61a6e32b2fce6b6ab4ebc768fd43b2fde38c5663563ef72752c86514456fe448f73e8d049a34bf90705606a8f81a9138a828ee4c3bd3871eaedaebe17f288c0222671c093a1d624573320495d7d3bd51f68900cd0d9097f463882536ccbe2d4f8d3e01a4755f701e06ac952c784074c5d3ac769dd3118bafccb2eb8a0e56670dc6b32d5a605ed6df31c1f7a76bd70246a3323212e59780fbb99da2d3e5db83875de0ceb96e177c6993563250c40d434b009f4422292e9ae8ff83f07c47068f100dcc86592875a88b416d80552712e4518d9d04385da83edfc84fee880e79e535f9b29a0b219de318c95d05afc0d9c2c204b1fd2d02d8bb9976b4b9bd8ef7432d5b059ef90fc0151d9310569a35d2466ce0c68131f15ed6892e171615c861111b4bf124b3048b0854bd7188cf81fbed613d8a40b9a655c6291760f4f52205889dd7f1873ce6ec8529bb2bbef05a9bed12a400a4189a84e054731af73538ce54a059a8b117371e1baf635fc2fe70cd69ecc01c59f8cf6b37267a8b3d1a953b9d775b52d9716008b18980bafa557c1148dfc252fff01241b5e3402f5e8f8cb6bb385d889b4966fea51827c325cae40b7dfd0f51052eb01248ebae19ef6d1a31521cccf943d9dd98612c28a29b2afb24f75f7e53217416a0d097e3fbde5160872f2ee98fb293f682b999cac376cb2855d7a46595c7e238f7ab720805acee950bd1d5bf5992691f35cd038c684cb9fb453585f71cf6225ae007ab07029a42e66b704d197e73aa5afa5ae7cdad70bd22352e3f9ffa333f25e870923fa12e7596648ca3478a4cc92e0ca300aca4e91f40716ca3142d14f43124f77cf58d4de72fe38b4eb8882bd4e605ee95403b83f478ebcb372daf0b08562e456e78c874076e5415612b4d21f32c8efba87c803336342674469f4a9f1444e72760646c9c797d08136b47df5c390d7487281544c435624a08b2c15d56a8d9fef208a14cf9f189115a0a3c11f57a8d20304e98434567ae8ff725c9429616eb69482edf132ca9806232bd12abad81d3ed800259daa24a2863ced3611ba2982a777e73366d975ba6abdc3485334e35fab5613ec93b5570fcd957d275047cbc98b91dcd867ce594bf8a5642527e751e97036b49188857c255cbbdfeeb1db7d15299d92336bc367e36086b28f23a0554dc090139ef4451ef4e5826905545b74ea2eaa519b466627717839ec7a8b22624b447418b8d4c2882ba754f6b153aba1d32bd8e1dad57514b568a061729516310945e8303e00198f5035bb653eb6c07c749f2b713087076563cbdc44c3727868500c911e28c235df7156b1ab76bebf7f6bdeec9fb430c7afd04b6ea625a72748dc6f5ae5667634b6abff3fe1b279509c4e5c547aadad65edba34c065278a2b4f71e9df2c8e9d969a8bac942f410119cc0c388d0c031b76a2f01e49b2b5bfc640997cf8fa6365c387c6623a8ec868a61ff4742a11058b8a970a24ef523cbb9de5954f9e6466f21a6e0b9b6e9d59673f28cbe689a4e45f7d9b84a85f9a68d9eb1ad", 0x1000}, {&(0x7f0000003080)="2ac368ea88e514178da45ccb1d14748185c9a6115c9088f904f0c2a40f762e598ffefedd383636d3ecf263b7f190b6c8a861d8794c1bb8a9f846076fd90a8cdaa1b9d33f97bfddfbac840936c601f2e8d0ee1711657bcabb0c64261fdc6bb9bed5542a6948883553e16dc42b0e0c93011892a69d17b7f8df45fd57358553e6b0ce3589d36c4baa344ab1e605f6edcc790a965c88b1a53e078ad730f2304ec18ec12ee5fd7b05ef1f789139539d2057009974be6662cfc9c29c28c55a9bcb2743eb74fc8044f5c6793f6af329e742021b19585bc22f36db24d27f6a117e082e0ab04232040cef17f3adc74545940c2ec052f2a3eb66a44790a13d74ae6795dfe8148f5c1459bd6c8470060bd1e4eae4daac29ae86a15937aef1b0db19029b4a7cedadbce017b06d91fa3ddf07f6ffc587802053449587d8217806a7c91969a73a666f0a8ba070d0ffeb83d353985cc501290cce0ef9b5f29a8aca31608812587defe7176874760f293e104528b7d16e1475284096700fd2280af47534a2644850c664c9cd249db8c2f127897a927c6951cb85a075cb620998fd45f4f37ff3f183636cf1cffd3f6c34b5e03f933e9a0db58cdc557a0740ebb466236c0fb278c3a307eced611a5aee67b565119fc34386f185e0840e1d04b94f4aff9cab931644e7b744b4582738ceabb479b5c649fa6f52b39dc8bd660b375a94be1ffdee05af7db675be5cc9c0f84176e7ac9655484e95d1e925cf07e57cd41fbe4b02ab9c3569d452b64c374ead43e1959788a1606f5829be6a75fe55fcf50d047c7b39ddfae5b5660a3bf36b0b490faa88a9e97727f444fd53a96021748df23410fbe2a74af0c492f757f30ecd7f3fe7954e74fc93dd4695f973f8fd6f131900b5549ee62bfd13730679cca7e3be1b6eac67c5582cf1b29ede16e9dc46b008f89763851f560a0b9fc53835a85eb51c077cbbf572dcc1717a7e564bc8fa7998dcff1cfff759e20ab6459e033d93cd222a4f7a1c457adb36ad890518133934640c9b97f3a9f454260fb3f15ac738030030059299608f4e7d14cd66fe653f5883e46ce6150b883080989a44c2552aafebce12bc1d450548f175dc212c938395693c4148617d8b390ace6a6ac5ce5fbb3e13e83345adb84ec23015e583137380c24ca61691d06b1f9a5bc377641549aabc52491088689e4cd7595171df3afbac868459f03ec85f3b84a012ac66cca6e493fd06f5e6c682f619731998b12bbcbe301b0097eabf79818b03761024668f6951f907ab53636dac1e67b79b00a5c95bc4c338c6d16d939360bc2a7f86badc3134dc4b2fbf4a218017daaebfed2843e97f4e6805cd4defcba989e3f3cc5af13989dd04b7ed6d0bce0b4b4db6aed5659ec1975ff1de65964040007baea2d0d62b5cee872e145dfbbfdac7847f54635f3649f52485858ae3f3bb6fe3caa56a9af538c378aa8123c1150dd612a184ae0991d258ddc435e1e90c34dfd6f77b1bfa39fb7bdc058be86e111a8edd876e7e1da9badbbe7c4989956f9276883c1e52bf28f97813f01000221ebc631829829b6fde68dc50371537e654c296b61b32d87b7294321aaf388d5e3125aa701a92e2a0c5a3de00b2921c305649cfc050e75f6e9e53c7a3a1f85918a23ff6787466215c9c6dce7541894e49231228a2aa070beed9064d6c213df962d790b4658aa1fb47ce6644d82dd70a3be6120e31a1cdbba783d0159cdd02612b3661a2fddcf13c6e0a3a3f32a39d196b1e4212c4195e9193810c6eff25490a5648df1e16658ae981df23f3520c4e6b67075fa0ffb927b2187c3ec6b119f7624c556e3ac62f93a0e433d238df77a1da06cbcc537415df187b2cbc22d2f909b0bc1fb9bee4ef0ab54f5f469712cfae7f3d9520cd80374ea7821edac06d58ddd9d8128360dc86a25eaa430c655f28fb9931a3b757a7db7ead4b5bac7a986d7d1b80d8c99fb7e2aeb56ee5aed57b7c2cc3c768ff8f1435e8f19ddfd88c1245604b6af3865fb544645ae031eafadd34716478b3fe036ddd8ab0cc18efb6defdaa279a217d649a82e502b8745d603dabe99c35e677a360cb5647dfdbcb6c3abef4590155592ca7e3cd8a79d79ed0ad869c7be2b639d8b3224280a5c00442ea86070bc58985f9adc5be28ed58454534d192dc9db7040a9d9c1f661ab5bb6f2311576feb7fe10d5fede16b9b98caa6cc7f66e62ef5df522d938a84d96e11d6f0cde5aca91116190a23eb44b73dc41c13b05ea0f1fc20356c87c0e0e0ab4ecdc5272caa3f185ca44cd49aaedecc14fe45f3e2c0cd9382672a82f13c53e3068510e04cbcaf3e7ac9f92c8a883f1eb38a3fce4f6627b4b79f9632fda9ef6a608ada06649bd4e1f7dd1682b4069b58ba97095ccef5bafd690f9bd3962482d9aafeeaee3594823516162cef79fa8e35f53feef435917b5db73380cc5a61744ca7770f753847cd5a90e7bdf2aef60d83bacb1eccda25b04b251a7d8e423c29cd9f4271a6dfb41d22acb24d19439e1c128e798473d4502fd8b228a4b4d0ac01f3a5b16671caf4b42e6b69943f97663581fa939dfa1c9256fe8e13ae2da889ee6bae6784cee0921d8f98dc2e1ecff98f13ad43798ee651cbeb23db3368b47fc6c4abc3415dbbb5e8d11b369af77041c0049fa0d9139294b19eb85c5e7486b32a1c92c6173d552f163bce4897be22d570261ce6fdb1ba52887c91efdfd84506b4fb408583497e0b86e1739c8ea1e84d093501735a8986a230f5af7714652a3625761828efe636a6083e5d46953e3140678f3f0926e5886637398dbef2aa9dc5553bb08ead6b0fb56944fc6e162f81c946232222f43c3900afcde9a51da6e44bd6e510cc107e17451837aada5ee1e8226d14c77c77e30a58681c251a8fd7c910b70bc96d762e9a6f71ac3d4caa89b789205e54e25c500dffdb2cd1cb35ea86774100ed345291531de8b02a35f67cb822618c3c5bed204ac34956684bfe719e74b9935597ca809aacdb50e5d1a0f190a1d70b09df95d43a586ceddbb9d3b95d8bf77b73c2b230837e1493ab184940e0224bf5c35e457e248325f58efbdb2142fb4381fc0cb3e35d64649f959b12ca1dfd4378185336e2e97008a616df234e82c8f85702c79b8734c6ba8d9afc4bae1308023a755e1bd2a6033a8aa688c9877a85246d144fe972a237a4b10387eb7c33dce309c5ef5cfcd2376a20e392a114d87133a7aefd2958d70ee1a7aa0028d2ac80a4907d1928df73d243824645aef5056bfc1712b16bac778c8e7e32510909d25845ac4bb762acbf16cc29e0bd1805ea2cd5c8d664589597db596c138ebee3e193fbfbcb0c012cb704680dcaa7c339b8e2f000cfad01cdd567e6f608b5644210bb87480dd90b9ed1c9858d788986719e92957dbd70515fc9ee86d7566ff86a20930cf20e10bccec3d97ca3a75a46f2cace8ef95c472d26104113bdd2bc1aaa1ad20ff1c5769a9a342850877ac804c649e4c1ef3c59b1f197d24124ddb1d6939042cd98f8dcdb2d1ce66573e9f971d2623d2495008d9b7001bd3f08d23d46eb5ceaab40541201a4c42bb336a1e3603082e7d7248e4938db085818ddbd72338961e2a6341a0887affb0803911ebf655208d2f7bcb86cff5e857c11a1c9dc83e0356dfb3c213997f312aec61b3c25fca6ce2794f53af8237261295ac997f6ed9ace314ff8eacaa0919547a943113135a32fb7790bc8dc8b1972cf3b12e82989ac5b25f6b77d46900b263d830b10988d2e52c8c751b58408a60d4376c2da23133dfabef0e9601ac05f5fa41a58a38c5a7fe26293fa2760a210ebab89a7d89ed6ff152b5bca554a6877e95a977b08d12eb91fd36665888ff735460f7935591aba956721f0cbcfd12eeb4903bf1cddb06b341e88bd405aab0d5eab3079108b333951f4ad32e8f4450cbff427b3cd04ae5f89e7a06ca84c07f55e0829ca49d2b3ae7d2dc660f078c52b224b10ff2fbde0c1b6515a3aa8fbb590bb76b5959d8d6a6e785a0b0c6d1bd43fa662c006b7299263fe6659db108c0aab3a2bb760caa328a12f83b13059e597f4f5a9cb4a3e5c380bc60b01d8fef51423789f77ee54433a03e224d348f625f0bbffebb5528158961e8f18573d0969e8f20798b4bcb65d1aa15f86987512081730c8bcda6c5780f228fe6ad087c74db8b0573ab9112ee762703c12dd5e5f06288d5a1454b8534f9cd78fe1052c2a7856183edfcf50a0306c143e74a71c733f942dc8fecc9df2bfdf8b5827fd9ead3b2031f7d9d0db158cd61b8cc72b7acc511779d03828d3216bd126042083a78f2f216b1d0007e4858a1f58795bf791311fe7a2298758b04412d679f861efb59c4929b6c3f48d34dde1937452f81ef23285d6e3aca2db287f96fe3cd0b67fa00dfab6444164a1c9d8f5707dbfe912992f228daf7065deed01ad035824175ad5bfa31e73fc4ec2a907b190c26ca8b136f3077cc31e81a3080d13a98c0cb41c900163254f417bfc561b5ddc7753fc13945e4200dc05b65ed0f9db5ac170871b8db00a2b8991f116a4034e6d8df730bac87291ee4a469fe4fc22601c069ff48ce9263c9241002a4f66a17dd94932b4d7bdaa7008623e7c63db6039be9b67073cf8d1f7483a3b0c758e7716d31d861717e6fec8fb7f919cf4297ea2e655fdee5b25a12cf428657cc1d0acbf42d03b307c4dfcd9989855779882ec57b8d8784dc25c1ac0161ce939018e0a5fcc6be11952ba856e781400c7b3a9128d4dc893dbb55f4ddd92358fdc1cecb4b2d050317de967945206753968176fcd8654fa656055bbe63617ecb7992f28c0b55b8086ea774a4d85cf40bf0d4df266e7724613c7e25a31a997383ad4164196778121e717c189abd5a3cbb10181b480aec0dbdeadd7242069ee3a07bc172974217d894421b8757348ff21335d3b55003c593398f808b8bd86b566eabd8d38405c63fe2b733c820f17f7912db67cd1bf389b68628af2e126936310faf3381a387adb1d22ff7c4e01e92710f71678cc0f6a3fd188569ede34b77853ba4aad2310e1122268db4dba2bcaba4e3cb935151f4996d1b2405ca7c552f2d67824c739b3596bf74d1f69369caeeddf1bb5d7f44deaeb2593deb8fff5a5a06feed1a2c935e8a9d98fbe5581b16457636db7b03d5786d8d470b5257897b6be2376404c9df9eddfce565cf186dacdd84174104a733b962f54e275a01494773b92354c422bd4f95cc53e32ba15df088c43da15fd96d795b452d866b8ce84621ae42f52cb5aa9f12c80c4f0470746ad185f9fe5aa18d6777aa1a19ff7771891bbfb6ff606430691fec6dc4a2efcc03a4ad4e78dc69b4ceae970b347033ee63466380d290d982e9bafef65751e5123276f68c759c432f6933c4ba43d8d2d592d73b9020dba7b1b7cf73602421f11702201bef29e47d0832de7b217f4c574f6be0cbb788c20d692f993c9868afd0718ceb43053db00287271f4d2fe89183b2de4e929eb90434e0a2107713caac5b7c7f025310ce3fa216b1c3da7c7eae3f7aea68cbcdf2f875dfc9a571404ed022152304f139a0fc339f4b1c5581c18b90c46f0eac27b759e55d30802", 0xf72}, {0x0}], 0x4, &(0x7f0000000b80)=ANY=[@ANYBLOB="1010000000000000ffff00007f000000aaced82af9391c719a76b78f4d41745908257be3a285ea7621bcd580e4a5aaf621a27d777b353de8ed0c1591eac6d71910e714b12b3942748e945628d5aea80241c6153447e48253d2e4a9825cd6a0118e27887a0592a2c846c06392a122479c01ad1072c3846b298e2bfd8c8050befe0668146d98ff342f44b81a6cf5d15322222e750860e16c423e30e0820609486bf3aa32da2b72a8da1eb4c7c8333bd0f2fbde482829210d8c1a5ef0f1cd812a1830482a5339a13f6eb23fdc68b71d3af278ec409fff7e4ea9f035d6128611f693f8314954b80ddcb85b91ef8796a78201f130b3ff18306706d51c37c9917f270c61c0ae4277942e0a389233668960d4ee05d8a97730b50142e2834bd0bce026064610b4eec76206f7e150b40e421101ebb08120cfc156c06ea906305c7383f215be854981a39e366d3bb22f5fb7e876f7849b3cca5e315c8cbe7d7a540c84fd39367827d68ea8141eabe7a7dc3c78bf17bd0f12bcfbf81a8cb646b762327e0f7209dd9a1e41b47f1b229bcd94fc7fc825b641471a9b480aaa9016a0cf30dc8343d2aaac69b2f50bfee451b2c56b41f7f9340f4be68efa251deea3cecb0959e7ff35866d9e8186807d18153191229a4c1b6304de07c935fb7157186f44e264e458e41ddee5aab25c719f80ba40c5eaf9b3ddf89089443d553881f4de1e943cd7989a677b487705c7ebfc461cde9ed3aff5e2fe18dffd1d7dca1ac7bae0c948601f7acb856754b62ee7d9ab1313a18a0df6f42b13d75cb8e41ad0c476afa508aba25acefc02cf2e2d506ceb327a805a51049936233c996962f502835b11e26fdf5cd54d5d04f3bf3cc864cbe49db1a1dda7080fa3200faa19af946276f51db0d1c73c38f2b542e66e3c488bc69c08dd743015875416537f9c6d27382db85bd398d80ccbf37354debc993ee73f767ed8dec409e8def34d3449dde28dd30f9b0b538620bd89517930c26cdd513670ee51f6e8ad46f93123edb00fa916a1b2e2e098528377717606fd64402a728564163686078759263ec716f49ba27f02c14a0d55ec5d0fd2bbfced1774ec49bc115a642ed376e38fa56a8491e249ed37ea60c8ece03e0d5252803c8b300af62f72fd033bd90cbb6b84e6a2601e1dafbd9a25981e06283d7fcbe539703fabbf4c0d57280a9cd30dcd15333735026da4b5c4faa867b2c548789ad1c7739a8736e9e571b65ee4347ec58dbaf4021f4baf9ba07dd7a724b8bdfe6b8315a2faff4970b8061f9b82485d08b30b0355184379e83920fbf232b65e08f8b48aedd7205697e4a0d1cb1d74499551a4b16a7b16b93c4bfa7b12153d31be6d9b6153f4c58a690f875a231095337a844994bd79b295b469eefdc4ffc547643be5a209d2aa76ab9d31aa064f94cb920dbf11a7f556ca41b540997959b134c2d833d214d8ec54446e0242078afba9f660b5220a0517bb15c66a4164645ae482112cea02aaf77b7e5984ace44fe51c141b877caee349793b11d6e25c1ff6eba115364314ff9fd8f082841e4bfcfeb9c6c4083b71f2338adee04a2ec0a5044e56451bfb2231498a825398aa9e4ab1dd2776b84f928d614216b22dd2d6d8af", @ANYBLOB], 0x21b8}, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r0, 0x0)
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
poll(&(0x7f0000000140)=[{r1, 0x4}], 0x1, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, 0x0)
writev(r1, &(0x7f0000000380)=[{&(0x7f0000000240)='p', 0x1}], 0x1)
read(r0, &(0x7f0000000080)=""/152, 0x98)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
write(r0, &(0x7f0000000180)="bde7343f535db1c94dcc2a7812c23220935a842f0b6d7f059e822b6ae7d2234cdc17adb7886fbde25d3b95b5c7ef95b44b94238e23d0498521f4d80ff1889cbdc7b675f4d679ecff7459360bca1865a14dd123c48f52fe43bebb7daeca3a228305c3c0ae36545933e0cf2566f4a01ab110b8142509179aed0c407bf41e561e1f92502f769ec1dc599c7eecc41cfaea6955f8b9f05c3c2e855f3718f20386ed659e816854a6de1fc70782b86fe6467fe2713506fc58e5a4b84a0c04df74d32c56e19e4d8682a41a1e3bf5abbec70dbe88b3ae692a7522f71544", 0xd9)
ioctl$TIOCSTOP(r0, 0x2000746f)
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000100)={0x0, 0x0, 0x81, 0xfffffffe, "cfe9691e70055144b04424a900"})
writev(r0, &(0x7f00000000c0)=[{&(0x7f0000000080)='Y', 0x1}], 0x1)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd8]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020697f, &(0x7f0000000300))
sysctl$hw(&(0x7f0000000380)={0x6, 0x13}, 0x2, &(0x7f00000003c0)="107c4c003791d21e", &(0x7f00000004c0)=0x8, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2080002002, 0x40004200000028ac)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
read(r0, &(0x7f0000000140)=""/134, 0x86)
shmctl$IPC_SET(0x0, 0x1, &(0x7f00000000c0)={{0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff}})
munmap(&(0x7f0000000000/0x1000)=nil, 0x1000)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f00000000c0), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0x20004455, 0x0)
r0 = socket(0x11, 0x3, 0x0)
getsockopt$sock_cred(r0, 0xffff, 0x1021, 0x0, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000)={0x2, 0x5, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, 0x0, 0x0, &(0x7f0000000240)="bdeabdda0100c0e13632ba1cee242f5aa8e8dd9027f921fba012d0b31e78b4439ccdadbb8efe9aee230cb622c8bd9e57cb3815fd2ed3e300d0f6e6a9eccb2436748a004c5c308f790ee4d0c9c5eec7c6000000939b0768c6219354c86dbf822d7e14e3a42859ec46c9e60a89338b052c31dd02b4d3926cde3e3a57e7b66cc3dd0f1e9b6e3e613dcf06e56af77618dabcbc7cbb2bbf1ef45753fa01675d05c1ed64a9868b9677fe0c7ed4cf2f8d7cd89de9a28adf630a1cbdf1a7d604b781a684c3b1d74d7b66aa8f54bd7fe8dfda0ccc957a5c2f6992091bc4ff28d9b6de05aef28d709cb3d7fe850f2462a9cd540116280c497c4a6b4ebda96d28dba90f5a9047253c31926981b22e7acc48d639977cd090baf93567ff5dc69c40efb930bcddd86f4ee69f039fe5459477f635f002a24a7aae18f0151c0a00e6b827efc79abcf9b749b4643d8c8acad3dd226d706e6ac1c311c4452ffb9bef1403bad6f6ee8363aa764df4c71e3edf0e0dcd88d5b28ed1d2d261f60a9357d5ed0e83225894", 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
r1 = openat$null(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0107003, &(0x7f00000000c0)={{0x8, 0x3f}, 0x9, 0xc0, 0xfffffffe})
fcntl$lock(r1, 0x9, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x101000200000001})
openat$pci(0xffffffffffffff9c, &(0x7f00000001c0), 0xf, 0x0)
ioctl$FIOASYNC(r1, 0x8004667d, &(0x7f0000000080)=0x40)
r2 = openat$null(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
fcntl$lock(r2, 0x9, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x101000200000001})
ioctl$WSKBDIO_GETBELL(r2, 0x40105704, &(0x7f0000000180))
openat$null(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc1204460, &(0x7f0000000240))
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)="aad485c4", 0x4)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup2(r0, r1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x34, 0x0, 0x0, 0xde}, {0x20}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r1, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
ioctl$TIOCSBRK(0xffffffffffffffff, 0x2000747b)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000340)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$VMM_IOC_READREGS(r0, 0xc2485607, &(0x7f0000000180))
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x1, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r1, 0xc0104451, &(0x7f0000000140)={0x80, 0x408})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x20, 0x408})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
setuid(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x2, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x8020690c, &(0x7f0000000300))
mkdir(&(0x7f0000000000)='./file0\x00', 0x0)
unveil(&(0x7f0000000080)='./file0\x00', &(0x7f00000000c0)='x\x00')
mknod$loop(&(0x7f0000000200)='.\x00', 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000480), 0x201, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x5c}, {0x4}, {0x6, 0x0, 0x0, 0xfffffffc}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
pwrite(r0, &(0x7f0000000140)="fbaf8a8d1a029be96914f635f49a", 0xe, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0x82485608, &(0x7f0000000b00)={0x8, 0xadd, 0x2, {[0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x7fffffff, 0xfffffffffffffffe, 0x0, 0x5, 0x1f], [0x7fff, 0x75fd, 0xffffffffffffffc0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x9a4f, 0x7fff], [0x101, 0x42, 0x3, 0x80000000, 0x8001, 0x1000, 0xb1e8], [0x10000, 0xffffffffffff0000, 0x7fffffffffffffff, 0xec, 0xa53a, 0x7fffffff], [{}, {}, {0x3f}, {0x1, 0xff, 0x0, 0x1ff}, {0x0, 0x5, 0xffffffff, 0x7fe}, {}, {0x5, 0x1, 0x6fa2, 0xe2}, {0x5, 0x40, 0x5, 0xfffffffffffffffe}], {}, {0x0, 0x0, 0x0, 0x2000000000000000}}})
openat$zero(0xffffffffffffff9c, 0x0, 0x0, 0x0)
preadv(0xffffffffffffffff, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r1 = socket(0x1, 0x1, 0x0)
r2 = dup(r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2e)
r3 = socket(0x18, 0x3, 0x0)
openat$null(0xffffffffffffff9c, &(0x7f0000000000), 0x800, 0x0)
ioctl$FIOASYNC(r2, 0x8004667d, &(0x7f0000000180)=0x4)
setsockopt(r3, 0x29, 0xb, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r3, 0x29, 0xa, &(0x7f0000000040)="78482dfe", 0x4)
openat$tty(0xffffffffffffff9c, &(0x7f0000000140), 0x2, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8000002, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x4000000, 0x0, 0x0, 0x0, 0x800000000000], [0x9, 0x0, 0x0, 0x0, 0xfffffffffffffffd, 0x0, 0x0, 0x1, 0x2], [0x1ff, 0x0, 0x20, 0x0, 0x400000000000000, 0xfffffffffffffffe], [0x100000000000, 0x2000000000000, 0x0, 0x1000], [{}, {}, {0x0, 0x0, 0x3}, {}, {0x0, 0x4, 0x2}, {0x3, 0x0, 0x2000}, {}, {0x0, 0x0, 0x0, 0xf7fffffffffffffc}]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r4 = socket(0x18, 0x2, 0x0)
r5 = fcntl$dupfd(r4, 0x0, r4)
ioctl$TIOCFLUSH(r5, 0x8080691a, &(0x7f0000000300))
sysctl$vm(&(0x7f0000000000)={0x6, 0x12}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x48}, {0x45}, {0x4406}]})
syz_emit_ethernet(0x2e, &(0x7f0000000040)=ANY=[])
sysctl$kern(&(0x7f0000000080)={0x1, 0x52}, 0x2, &(0x7f00000000c0), 0x0, 0x0, 0x0)
mknod(&(0x7f0000000080)='./bus\x00', 0x2000, 0xd01)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000140)=""/102400, 0x19000}], 0x1000000000000135)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000012c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000140)={0x0, 0x0, <r1=>0x0}, &(0x7f0000000100)=0xab33673602b13e1e)
setsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000040)={0x0, 0x0, r1}, 0xc)
setreuid(0xee00, 0x0)
r2 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
fchmod(r2, 0x0)
r3 = getuid()
setreuid(0xffffffffffffffff, r3)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
pwrite(0xffffffffffffffff, &(0x7f0000000180)="4ecb37f43c5b2083b6273e763f7325d2d861406f623b9515d6440231fbf6622df9c6cda1f22c1c655953d47d799c3b925dbb661d828db78bfd4b6ca5167e2efb9bfa9f84be43e45e362060e3bd6d3710f0dc733cba70b5c3feb995015f6767619bbad98263c6aa766f08501cd1842738497af978a300ece8bd75005f35aecc9682c61edbfe10a544635e090cf559ae266defe95fa86a824230ea5699b5cc02f47107b783f1cfbe9c5e84bb67d2004577f2afc6f642f7f510e5742e38423e638291ab7edf7db769df4497d0cf0209371794a3ec7c70e0367b8da9585d635dc96e96e689e4a6a285f12db41ff39987440c36665e72f3aeea834cb5e654671a687848276c31a942d18a89b3d87cdb9ae4b73945190bf5bf0cc4c2bdd9424956e5add63c161493a66eda0e707ea4f0705041058b24a7e12fdbd190d32005deae03ac39f7c8dbc172879b71f4eeea1f222b1e5c20977ba6cef4033532b59939c8485ea254f15057f020752893d984100471a8ff6c64328854409e82b5a586e6cb97f4e866b8b0706ed60ecbc08e762936742275ce1b560e2f20d033ee4bf378aea9f16f4188a89c9f7e4db125f843d5bf85e83062f9156c7bafc6d248d35af317446b2799dcdb1bd481c8eb8491d7082e63df7891ba4e6281b66a7b99ce3676d2d71782644227dacd9da1075911dd4be6b364cd0366ce4bf3ec22d1b335d593456753e1557506c219e3eeda5044f8350a0f3201f23fc7c13af64c5863810bfa92be54777111455cac8bc049f42406d08c1f7c35ce6c78cdaaa6eee61a8aeebf8d7097984d242185d6f76bb36c35ec4aca8acad043c9e0ed999c269aba4196370edf79ea9414d4d6c652aa2a74a8f40c5f9b21bfe3c4548ced06f7ba2ac4d57b73326a6ecd0d575fa5ba99f156331f22215ef5b04f843a3e28f9d45a3865a5f45832101c1553da9af38e56f093d20961b0be99cee27d421951637d06472758ab3d2021a309e45ddc17f11fce2365a4df1a4f8de5201280db08bc3f3eece3c686d4c5e5a5976cb95f07c61ac885a5c48f25f844066cc4ea925c0fcd8b5ef3c6aa4ddb246c8486589d0a904aa68c1b08d5d9bd5902a74bb8feb72ac96208898637998ca0de80d1bd7e2a1a3be42933841531a953f864ee81259228186aa25187649afab562bfb2121b529bd4a3050e2476049aa6fdf6c017d07059432199c09c06b243d3f13c757223bf2425da73361d311bc619be04a00993e28b146804b7f14bf20c715dd5f757fafac7156738ceb445cbb438f59274ee863b774a6d125b11cd26ce98ed792a45adc5c36ac8482385c71f768f30b491f9a88ef6c388a62d0bd2a90f61eebf33d53339c78b10e0cb375d1ce5208d083c6db11db573d473aa4b70770f38f49ed183eeb21688ffcd620dfdfaf73bb4c0ee7be97fd7f90814791e72391768868f14608911aac1f383a2c9852f360c87669af2d34dd0629379e70e201a44e5f3df83668d7f7f154df4e091b59b79053299ce333f349496d4b25faa56ec482cb73a8cc84fc2332f727c7f9d6d18fec7b8c98efe0507746c88d8daa169dd587bfc73fd8231b45e34946591f6a716e545c5300336ad190c7c895b7f30afef703d8bcf49fcb51ea3d4098c0a877e6a6e4240c94ec401a1e9ad30f2278e41b5634d851ab4fac57ea72ab6a3fea6db57f09fdbb65918de49ec5e240adf5a94a7996124448868c0fb392138319b", 0x4cb, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xcd604407, &(0x7f0000000240))
sysctl$kern(&(0x7f0000000000)={0x1, 0x2a}, 0x2, &(0x7f0000000040)="3264fda8a60de042da8d43ff463fd5fc7b37c6600a49770719b52c7d8d57895c4e672bce3284ca1e77d50b6401a046b6", &(0x7f0000000140)=0x30, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000300)={0x3, &(0x7f0000000080)=[{0x4c}, {}, {0x6, 0x0, 0x0, 0x10f2}]})
pwrite(r0, &(0x7f0000000000)="e81f0000000000000014b6317e37", 0xe, 0x0)
sysctl$kern(&(0x7f0000000240)={0x1, 0x58}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
ioctl$TIOCCONS(r0, 0x80047462, &(0x7f0000000100)=0x7914)
r1 = syz_open_pts()
ioctl$TIOCCONS(r1, 0x80047462, &(0x7f0000000140)=0x7f)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
getsockopt(r0, 0x29, 0x31, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$inet_opts(r0, 0x0, 0x13, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x80002007, 0x5dc6)
r0 = open(&(0x7f0000000040)='./bus\x00', 0x0, 0x0)
r1 = dup2(r0, r0)
poll(&(0x7f0000000100)=[{r1, 0x4}], 0x1, 0x0)
r2 = dup2(r0, r0)
close(r2)
mknod(&(0x7f0000000180)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f00000003c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSBRK(r0, 0x2000747b)
r0 = socket(0x18, 0x2, 0x0)
sendmsg$unix(r0, &(0x7f0000000580)={&(0x7f0000000240)=ANY=[], 0xa, 0x0, 0x0, &(0x7f0000000040)=ANY=[@ANYBLOB="380000002900000031000000010000005d04"], 0x38}, 0x0)
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000480), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(r0, 0xc5005601, &(0x7f0000000580)={0x1, 0x0, 0x1, {[0x0, 0x0, 0x0, 0x20004000, 0x200000]}})
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x100, 0x0, 0x0)
open$dir(&(0x7f0000000040)='./file0\x00', 0x40000400000002c1, 0x0)
setgroups(0x0, 0x0)
ktrace(&(0x7f0000000000)='./file0\x00', 0x0, 0x410, 0x0)
setgroups(0x0, 0x0)
ktrace(0x0, 0x1, 0x40001e34, 0x0)
setrlimit(0x8, &(0x7f0000000000)={0x9, 0x93})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETA(r1, 0x802c7414, &(0x7f0000000180)={0xfffffffd, 0x0, 0x7, 0xfffffffe, "000000000000000001000000000000f843d07e38"})
writev(r0, &(0x7f00000012c0)=[{&(0x7f00000002c0)="363eedd6017b81949e972d5e165e7f1824ebaf311e3cbe80269621e40deb1d06b19b7b2e69fd85ead8bd6914fc3ed0e451b5fb42cf015307f7d02437ca875a8e49a0823766d6eadfa62ccbeed9d20b78da1ef587b90f2e49bf14bf4bf7181c6e3800b430292cc43ec8e2f0b7fe6b2ccac37fb3bc9cac8909dbe2ce0ff66c56984f7014b3661572fe9594aa30c98662912cf14eefae38287259644d1ae546ab1b1e96500ac0c33ea3a4fc54ac27edf0010fb7f9929b5f11cae44393510082b3b301171752e3ef4aaee4b4aef68b10b96a4b54bbb812fde99e0c1bb3692d570062142c1e608a1347197e024afffc1d33f19284c50dd6fe699d62a52d6f838ff9b107e83c38c3c2d79cfeb2825d9fab7e9908a44f3080d40399defec3ccd25af5498ab2bdbefb869300103e269cf3184bc4557750a05ebf4ebc3b4431ec1bf564e9f5447ab39f9c5f0ac5d689f834686aa8bf8d82e7c406ab8e73a45aeb6c80bc10e1a9315bdcf8bb443f7c63f2bba439766f51b713fac160f34401d9cc2c5052e9fdfc2e34adfb868b36cf8b54f6c6ae88bba97dd04a52a5178a3225b0f2752e5b67d7899fad9bdca098c1056267c415f3b016270bdeb4be9b4b777ab440f956f3d2e7b95bf2d61d7a1debfe0019da10f6316b1123bf673f97520a613f0e4d90088e98920afd16253e556d47a78be1c9a61acbf2bf15928e040518a4ace3f01ecc81635a955408e51a46069bb88d996dcf3f52970109bb3aaa14ec4315e30acbddd8871843ab2cb29a69d126e4fb7f2a2ff3c1fba7e6b901f5aef44cda52ed160e150fd70165e9dd05a08217900dc4b15a11c5e4767c81845e28da8f634f7a5fa1e6ac638363459a4926a0027049ec98e0fb90da50989a78e1db606170ddd8aaeb12ea7f21107fac39b1ad16d50afd35a49ef93efe3601027e5b5b0756db5055f7293de1e4a734324b445521fe82feed087a5d5e42aedbe49bb4580cfd7b84324ef365475138a2f3877e3b2e781f6dc41a7286a355087bdff95cebfaf46c70725f66451e77393265d42bfc3a2ac670dfbdfadd88414225887ea3a2882dee36fce83d6bd6b3825435320059c4cc99d4f12e847af5f6f565d2c95ed9589c3a414d6ee757a57bf153691f5fc2e2ce269d74163294402b4094cabfb61d3b533bfce13e5b112285bb2dd27ff8f8f349f785dea892db1dae00cf863fabcef29a1f0776b9434efea738531d8310aa382e0fce13684636dde45866df7848ea6dd5ca3e352392b94fd6d62b9b892002fd5489299dff63628adde15e3be87de39a32cb6ee72be819acde9b950cc854e5543bbe46f91484ab24a5ea52c3a5f09e418d7f3e0ecfb2abb83b3b69202418184ac7a1b4faa19fab7841129996378858085bc97257c216a1ba51518e0716c8e83ff1bb1af82d08e41669cadf4af00651ac2ca0f4cc4794837c35dc1ec62140a805eff1032aadff6476b46841f080aa7b0d05c6f0c7c4153dd6e7cc13dff0b55cd971774a377edaf87a65e231eabb29c002f11bb6a603aec59e3d4166d31f2dfdc5c63505a3fb0a4ed037174932d7989da5d73916515f0bec7171b6e5e9cc4647867f348dd7b60cda50076273fe326f54c01b39daabecf05e7988100b01484a6fcf6854f56e29e58d61358fd821497d6ddc4f429f38b20f3b64f0c7be64dc6b9573a7f77dc9f8f0cc8f285f1ff334a3e449a3b61ee7e0c1e82e88eb849ce945f30f8a7a181f095f8dfc50d5b34bfb85798928a04b9f0c8c1fcd89c1bfd248e59f2e1259fe17f5bb559ff152530c6372d03e1b421012226be9c8be9dfc2b8e46274f2e2b8dfd47a4f3c62b10c1b97a7a4ca5807323511e1247b6ae575bc86ebcd8f32d728a0a394bb893de63ac0c3cdbb05146e2e9a99ec4f970b497480516b764ebc961fb73a0e03606e063cae576c2b52ab3fd31ede066e001538c4c7c33305c693eab2b45d6da00e366d789af48a59ee3c0c181fbbe0db155425acc925df45222e6b9aef1de80318aba1ddacab76685c20a653534ca3b07131c636258194271b6d4d52148ca8e2ce964bf4dfe10aa5d2f93af6e370d47ee74c87c7ca204dfdb135871ca872e169fcc87eecafde5d4b2c8d9c9b03cb1c3af63d37c703c6bfd83ce13523d4a026f16fdaee8fca7502b7fd1f981402aff0f67333a865f1b6148c60bfd613b2597cd908fb21060a11b2c87b5cf6d395cb0f35c2e019530e6d293c41446356ec5f1c98b88c947ed9251c7c434bc9fc94c21db060ae46d6ac3d5241ba20c61db17b283db623a93e1380c6764918bbd2e4208245d6be3fe29ec251ec8a51cb727dfed45dc956a957ccc26ae3d612f2f73f05a13b0e8af7ab67964a47f105b0af800d6b6881638734587bc1ef7433e9d60b19fa31f6fcb20f0faf7b52b4bf71008313f180c3f2a659639f0b7e749f0ea268153b7ff9e9f49a919445730dd6420862443e797f5d73b7bae12589ff74c331db6778c8f27dd92f0134854a31b1ccf38d9213f24e5f2ab9dbabad8dad310b38af05a3dd307c46c4dd913e59e27c8774b391acc9f9093597a93be46916ac11c491be67ffc187b37ad4ae70e3b122fe88c43bc3defad04b5e02c6b4b1cedb491d47b511096154a881a3578f0f5d68dbbb6bf08767af75d387ace35a56337e41d73fd839c4b990037be707e34b693bc19bb75e6fbeb887ca5b8a46671c349193f0845f134fb8becc55deb89fea16bd4ba39ad3c13a78e4b5b87f35514f5c6f245b497c5cf263cad80795b3f210ac0a539110092d6aaffd2960566bdaddfdf4929fee998201a5fcb77e1c96a4c2126f51b2ffc5b549a667529f9f1bfe33bf19ee51c4431f43dd950a3ced9a3647a86b0fa25fc37a9164944b75ca779cd16ae8152030214295340485403b7fd2b8faea831fb557e872737c7116a5d83701531d636ae58800941680d0ffc7e739a59eadc8ca37732854c83074535921f9ad615dcdf243e066ca0c22975f24c2aee192f3ff2dcd7eb680e45fd46306a60f7c2ee57cee5b6cf6fa9b57ebc53db16fcb1609820420acb132bfdaa60d9da9c06e90000f5766b68432aec1eac8b8a382b86097e8704fa0f7dbdc4d2d958afb24d335e0a9e075a88421d8bfbb61e5d1771b60227ce5c661aa13161cf8fb986c2a3b2967ed47b1925ef418efefc822a848552fb967009eb2e74b585f447440878a8ae5155680c8a100b3d49e02ca35d953ef396fb97e6f2fbb90a764d952b8bf79ba8b92310acc4a05718e26935b2545da5190ebe8983421b20687ea6de8ef36ee5747f99cfb24f0c7e25c681702aafb5b44d79525c5bf14018ff18a388be0ed71291a51efa431b04c1ad2915f7597de6c2b6f3e414b9467e2a51936dc72fac2504c22ae2cae33615be6fd2b14a1f434fff1e8b65a20700836e003c0ff8501221a867737de23c2989c72ba63557abc0e8d818196f8d3832a958f02ea2fed8f7d654183a9b5302560aa692074dec0fd0a0a723c119c15d0fd0033a7bc3758454037aa89b209acbe002663a31988eff38ba79366faf3a4961151078bd34b6a1e99b48cf6b3b9fb6fb202a00a14b5233cd071a13830b8f15489eb79988a5671ff8728b05e6dbb02facc121f5b8f7c7f06aac025b4df235113e9a4a56ea786e9b6f023c846238bc731b63368033f588a8f840900b699bc9cb0da4a7e49bf07a61e992f1a5ec5f6ea18ce67ef24fbc72616273519942a043f67ac41b0bf2e1729fd34bfd703745e817ce6fd16b9ae8717e2dd7b4b36a5cde", 0xa6d}], 0x1)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f0000000180))
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000140)={'tap', 0x0})
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
poll(&(0x7f0000000200)=[{r0, 0x1}], 0x1, 0x0)
syz_emit_ethernet(0x17a, &(0x7f0000000400)={@remote, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x16c, 0x67, 0x0, 0x0, 0x0, 0x0, @loopback, @loopback}, @generic="60038cca6a1d11d39baf6e829932447e1411c1c6204e34fd0549be6ef80f7ddb22d247f28b332fe146cd14f8558734c9f4c7df3211abb30c1352ed44e2dac5dcb453dd7f64611aa9c9cb44495b6aff472d1bb00cd636686d4feadcdb7438d7d5a5e74cb78f198b268c400e4119241f7ff2ad8157e423b5ea6414a09994eaee8f0ab576b6e8a3abe3b53eda2a4eefd1cbf1e7979f9b2c42003844d209877d575c302df033558e08ab4003882b0a832615114620abce4648fdc1ee8dd95f7f45d7dd4e6c6face9e1fb9ec8443308980ec097c3e20eb7796553811f3456e98c42658d01a69e05a553a2c4f1cca2c758a1b903000000b578503307582a9226d23c1b8375a818ee428d8fd6b05e36e1d65f57c78b31dec1a7dff4156ed33dc9f225be2c821216c6fc8b99c08312b38368f7ddda9f4cf3000000000000000000000000000000c27140eecdb3c21e811526b0d5b5dc1ef30274fd94"}}}})
r1 = dup(r0)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f0000000080)={0x1, &(0x7f0000000000)=[{0xfffe, 0x4, 0x3, 0x2}]})
syz_emit_ethernet(0x2e, &(0x7f0000000040)={@local, @local, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @empty, {[@ssrr={0x89, 0x3}]}}, @icmp=@info_request={0x8}}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCIMMEDIATE(r0, 0x80044270, &(0x7f0000000100)=0x805)
poll(&(0x7f0000000040)=[{r0, 0x44}], 0x1, 0x0)
close(0xffffffffffffffff)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x1c}, {0xc0}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
write(0xffffffffffffffff, &(0x7f0000000240)="54a43e5f2e", 0x5)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$FIOASYNC(r0, 0xc444443b, &(0x7f0000000240)=0x4)
mlock(&(0x7f0000ffd000/0x3000)=nil, 0x3000)
r0 = open$dir(&(0x7f0000000040)='./file0\x00', 0x8200, 0x0)
mmap(&(0x7f0000ffe000/0x2000)=nil, 0x2000, 0x4, 0x10, r0, 0x0)
mlock(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000400)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd6c115b266e6cac2cb93537c2b236b848dfe87156e5eecf5dfd60c15d0165ec940cbfc0b74c1c4494b999e4ac", 0x1d0}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f0000000240)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51f4b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2af76824b990a8ce316d0e8cdd2b7420511bf6e2204f87eefbae7e9aa56103ddce0203eeed44da7b3c5a3a072ec8b1312e5c28319500820e262b6d4011a4b473b4d7694f462e7fa3aa201ddb7eab4af9a44166f245fbd59802445ca018b2032", 0xe0}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0/file0\x00', 0x0)
mkdir(&(0x7f0000000200)='./file0/file0/fi\x00', 0x0)
mkdir(&(0x7f0000000000)='./file0/file0\x00', 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket(0x2, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x6e)
setuid(0xffffffffffffffff)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x1, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r0)
ioctl$TIOCFLUSH(r3, 0x80206919, &(0x7f0000000300))
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000180)={0x0, 0x0, 0x0, 0x300000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x1, 0x2, 0x0, 0x100000000})
fcntl$lock(r0, 0x9, &(0x7f0000000040)={0x1, 0x2, 0xffffffffffffff01, 0x200000005})
r0 = openat$wskbd(0xffffffffffffff9c, &(0x7f0000000080), 0x1, 0x0)
ioctl$WSKBDIO_SETMAP(r0, 0x8010570e, &(0x7f00000000c0)={0x19999641, &(0x7f0000000000)=[{}, {}]})
sysctl$net_inet_udp(&(0x7f0000000100)={0x4, 0x2, 0x11, 0x5}, 0x4, 0x0, 0x0, 0x0, 0x0)
mknod(&(0x7f0000000000)='./bus\x00', 0x2000, 0x4086333)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000100)={0x6, &(0x7f0000000080)=[{}, {}, {}, {}, {}, {0x200}]})
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
ioctl$TIOCFLUSH(r0, 0x82907003, &(0x7f0000000040)=0x2)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x4000000019, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000140)={0x3, &(0x7f0000000000)=[{}, {0x5c}, {0x6, 0x0, 0x0, 0x8c9b}]})
write(r0, &(0x7f0000000080)="2a1d8c4206000000000000000b4a", 0xe)
r0 = socket(0x1, 0x1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000a40)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000100)=ANY=[@ANYBLOB="10000000ffff000001"], 0x10}, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
sendmsg$unix(r0, &(0x7f0000000a40)={0x0, 0x0, 0x0, 0x0, 0x0, 0x10}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000000)=[{}, {0x28}, {0x6406}]})
syz_emit_ethernet(0x36, &(0x7f0000000000)={@local, @random="3a6d1f6d3961", [], {@ipv6={0x86dd, {0x0, 0x6, "3835fc", 0x0, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}}}}})
syz_emit_ethernet(0x3a, &(0x7f0000000000)={@local, @random="2e8dd386ef21", [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, @empty, @multicast2, {[@generic={0x7, 0x2}]}}, @tcp={{0x2, 0x3, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
bind(r1, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
r2 = dup2(r1, r0)
r3 = kqueue()
r4 = openat$null(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
fcntl$dupfd(r3, 0x0, r4)
r5 = kqueue()
r6 = openat$null(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
fcntl$dupfd(r5, 0x0, r6)
listen(r1, 0x0)
sendmsg$unix(r2, &(0x7f0000001440)={0x0, 0x0, 0x0}, 0x0)
sendmsg$unix(r1, &(0x7f0000000080)={&(0x7f0000000040)=@file={0x170, './file0\x00'}, 0xa, 0x0}, 0x0)
kqueue()
select(0x40, &(0x7f0000000000)={0xfff}, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000080)={0x3, &(0x7f0000000240)=[{0x80}, {0x50}, {0xc76}]})
syz_emit_ethernet(0x32, &(0x7f0000000140)={@empty, @random="d2ad2c719745", [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @local, "", @remote, "3f034d5dcc6595da6c7d976af8cd6e61"}}}})
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{}, {0x6}, {0x6}]})
sysctl$kern(&(0x7f0000000000)={0x1, 0x42}, 0x6, 0x0, 0x0, 0x0, 0x0)
mkdir(&(0x7f0000000200)='./file0\x00', 0x0)
unveil(&(0x7f0000000100)='./file0\x00', &(0x7f0000000180)='r\x00')
rename(&(0x7f0000000140)='./file0\x00', 0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000001500), 0x11, 0x0)
ioctl$WSDISPLAYIO_GETEMULTYPE(r0, 0x80045713, &(0x7f0000001540)={0x0, './file0\x00'})
r0 = openat$wsmuxkbd(0xffffffffffffff9c, &(0x7f0000000040), 0x1, 0x0)
ioctl$WSMUXIO_ADD_DEVICE(r0, 0x80085761, &(0x7f0000000300)={0x0, 0xffffff80})
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
mprotect(&(0x7f0000ffc000/0x2000)=nil, 0x2000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000100)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000340)={0x3, &(0x7f0000000180)=[{0x60}, {0x80}, {0x6, 0x0, 0x0, 0xffffffff}]})
pwrite(r0, &(0x7f0000000200)="7b8182eaea26b6ff71f838ed67dc", 0xe, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x55}, 0x4, 0x0, 0x0, 0x0, 0x0)
setpgid(0x0, 0x0)
r0 = getpgrp()
msgctl$IPC_SET(0x0, 0x1, &(0x7f0000000000)={{0x0, 0x0, 0x0, 0xffffffffffffffff}, 0x0, 0x0, r0})
r1 = socket$unix(0x1, 0x2, 0x0)
ioctl$TIOCSPGRP(r1, 0x80047476, &(0x7f0000000040))
r2 = getppid()
setpgid(0x0, r2)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x2000000000000061, &(0x7f0000000300)})
r0 = socket(0x1, 0x1, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x80000000000003, 0xffffffffffffffff]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r2)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="49acdc3b2084845d6d7c44b6cddf812c390695d3d8861c1778817ca335ba86a864933f916628163ffdd3302b16f5a309b8bd585d6b33f88f63e845d9bb9fecda2929a78cc1f29a362ceaab210b2466ea485604d15ddf8f0cfae214808a7f99f34d1240288756be7a520b9991f3bb478bb4f1aba883a65c3ee77e7be2ac39e345ea54d0a8f298478d1c6e3126610e5c30068e713121702617d473ec20b03332afc4bed369f4dabf3c7d62b5bf26698c5e3e35920cc6bef24c", 0xffe3)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
r1 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000140)="e800f876223f800001000000", 0xc)
setsockopt$inet_opts(r1, 0x0, 0x200000000000c, &(0x7f0000000000)="e08bc6a1540e034001000000", 0xc)
r2 = socket(0x2, 0x3, 0x0)
dup2(r2, r1)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0xa, 0x0)
ioctl$SPKRTONE(r0, 0x20005302, &(0x7f0000000040))
setrlimit(0x8, &(0x7f0000000000)={0xb, 0x92})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETD(r1, 0x8004741b, &(0x7f0000000040)=0x7)
writev(r0, &(0x7f00000016c0)=[{&(0x7f00000000c0)="c1f994c811e9105c6cce765e1110f1e50982589b47a1fe9a47bed07d8218204a239d30bc453b033c197b8f7d41376399733f09a1d9fed39ab00000094c2f19f5f4e8eca356b8fb772c7a3f9d915b558eff45aa75e812795a36a832a159109a443ceb240a", 0x64}], 0x1)
mknod(&(0x7f0000000200)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETAW(r0, 0x802c7415, &(0x7f0000000140)={0x0, 0x0, 0x100940, 0x0, "f540b2ea0000000096b8911f121b3bd89a53e1bf"})
sysctl$kern(&(0x7f0000000040)={0x2, 0x8}, 0x2, 0x0, 0x0, 0xfffffffffffffffe, 0x4)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x4, 0x1d, 0xfffffffffffffffd, {[0x4, 0x3, 0x0, 0x1, 0x4, 0xfffffffffffffff7, 0x1, 0x9a, 0x4, 0x0, 0x800a1, 0x0, 0x5, 0x5, 0x100000000000006, 0x1d, 0x1, 0xffffffffffffffff], [0x9, 0x82fc, 0x803, 0x2fce, 0x0, 0x40b8, 0x2b, 0x1000000000003, 0xba5e, 0x7], [0xffffdffffffffffb, 0x2, 0x3f, 0x0, 0x5314, 0xffffffff, 0x4], [0xffffffffdfffffff, 0x81, 0x0, 0x6, 0xffbffffffffffffc, 0x1000], [{0x1, 0xfffffffd, 0xfffffffb, 0x20}, {0x3, 0x4d4, 0x5, 0x4}, {0x2, 0x0, 0x0, 0xfffffffffffffffd}, {0x120, 0x1, 0x9, 0x100000}, {0x0, 0x0, 0x20, 0x9}, {0xffdf, 0xffff, 0x5}, {0xfffd, 0x81, 0x4, 0x8}, {0x100, 0x39, 0x0, 0x400}], {0x10, 0x0, 0x80000003}, {0xe3, 0x5, 0x6000}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffff9c, 0x80185760, &(0x7f0000000080)={0x1000d400, 0x9, {0x5, 0x7}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = fcntl$dupfd(0xffffffffffffffff, 0xa, 0xffffffffffffffff)
r1 = socket(0x18, 0x2, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$WSKBDIO_GETMODE(r2, 0x40045714, &(0x7f0000000000))
fchflags(0xffffffffffffffff, 0x0)
ioctl$TIOCFLUSH(r2, 0x8020699f, &(0x7f0000000300))
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000004c0)={0x0, 0x1, 0x2, {[0x100000000, 0x0, 0x0, 0x2000000000000000, 0x20, 0x0, 0x0, 0x100000001, 0x6, 0x4, 0x80000001, 0xfff, 0x8, 0x7, 0x0, 0x7, 0x3], [0x1, 0x1, 0x8, 0x2, 0x0, 0x6, 0x4000020000210, 0x7fffffffffffffff], [0x3, 0x0, 0x0, 0x0, 0x9fe7], [0x0, 0x0, 0xfffffffffffffffc, 0x2, 0x0, 0xffffffffe], [{0x7, 0x1fffffc, 0x4, 0x2}, {0x0, 0x3ffff, 0xfffffffa, 0x2}, {0x2, 0x0, 0x2, 0x2463f4c2}, {0x80, 0x1, 0x0, 0xffffffffffffffff}, {0x0, 0xfffffffc, 0xfffffffd}, {0x0, 0x7ff, 0xfffffffa}, {0x0, 0x2, 0x8, 0xb}, {0x3}], {0x0, 0x0, 0x1, 0xfffffffffffffffe}, {0x0, 0x1, 0xffffffff, 0xffffffffffffffff}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
ioctl$TIOCFLUSH(r3, 0x8040691a, &(0x7f0000000300))
ioctl$TIOCFLUSH(r2, 0x8020699d, &(0x7f0000000300))
ioctl$FIOSETOWN(r0, 0x8004667c, &(0x7f00000000c0)=0x81)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
r4 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r4, 0x80206979, &(0x7f0000000300))
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f00000003c0)=[{&(0x7f0000000740)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9846f9b3aec3213d2a6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a390a25beea3962e7c10b8d9f53f5c82b5eacc26757d14f2fa6be9a2cbb2cfacc5e906dfd1e3208364bb049bd84682cec454327b6a1522c332ea628b0cb672e9e7247818f900e017c7cb9303e6b505059f34d3fb9df3993b7535fa269859e24b2802782224d7d5c13c21d4eee4f8621037c3d78695ad9a278978b26c46049befba997acb9ac407791cdf6046f9f71e36d09827a4493c17a0921dc38af76420c885862413c6ed4f7fe335a5547ee2d7c65d735b189214606da83f9be40faef7438cbfe1ed0439c45506672cda99d1c3471259d08198e13683ef6b08d5c54bfb991dcca6919362e1a0b65844e9194c2d7fd257281fbcae0694eb4c1e7121b6a2c19d7c82054126e2146349c1c8489aada96f3a8400c78d1da37d5228e5aa36b139a8d5957e8209712744b81352d093315d238f5a0c3cb694e5bd546af01421ace28b2e266c33488bccf4815baf3226156e050704a0b7fe058bf69a49e52ac968a0", 0x1cb}], 0x1, 0x0)
writev(r0, &(0x7f0000000000)=[{&(0x7f00000004c0)="0ec465cdab1ab6925cb81235dbb17399c070dde203e502106f690d9947364fe3569560e73bfa9012263c0ef6eb626ad79d51e7b4607879072ca33809a85443bef8e011b3e2e63de6f9637ca6e422106a6a1762b67f560814eef6dcb3f39a2e51600251fbad1ee82088a75ca3764729af3f3d4d967ce8aadb3c3fd7f9ae4f4c83b2", 0xfffffe1a}], 0x1)
setreuid(0xee00, 0x0)
r1 = open$dir(&(0x7f0000001240)='.\x00', 0x0, 0x0)
r2 = getuid()
fchown(r1, r2, 0xffffffffffffffff)
setreuid(0xee00, r2)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
mkdir(&(0x7f0000000180)='./file0/file0/../file0\x00', 0x0)
mkdir(&(0x7f0000000080)='./file0\x00', 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f0000000100)='.\x00', 0x0, 0x0)
symlinkat(&(0x7f00000000c0)='/', r0, &(0x7f0000d06ff8)='./file0\x00')
chroot(&(0x7f0000000180)='./file0\x00')
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000000), 0x1, 0x0)
writev(0xffffffffffffffff, &(0x7f0000000040)=[{&(0x7f0000000280)="aaa97403fe090010001e328a1811", 0xe}], 0x1000000000000201)
semop(0x0, &(0x7f0000000240)=[{0x0, 0x0, 0x1000}], 0x1)
ioctl$FIOASYNC(r0, 0xc0504417, &(0x7f0000000240))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000000c0)=[{0x2d}, {0x2}, {0xe40e}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
pwrite(r0, &(0x7f0000000440)="15f2ccc8704aa08e2d554ccf32c4", 0xe, 0x0)
r0 = socket$inet(0x11, 0x3, 0x0)
write(r0, &(0x7f0000000000)="c0c09a43", 0x4)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000000)={<r0=>0xffffffffffffffff})
sendmsg(r0, &(0x7f00000004c0)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000240)=[{0x100, 0x1, 0x0, "2e650e5ed6429dc8ed33518c10fcced34a82d27522f8928ac8adbcb33d40c491648dc2c0b478d84a825a043b9c623587f7594310e1fb9e3cc77d9aad1bcc9e560d42495e0faa97a0a7b6e62bc68ddd8dee754de2798e9dd108112874512497547535dee4298d897ac89ba2fa633de5fca67f35fa31058654c19c462153a64b36d5abd24425bc02c564a3783ee6a0f73813f4cc603f46ce7ef43695bd005627f56d8d0fb2ae2574b7e0a48192964a18cd8f448a539a859d332b0cb17a241324d91a75324e6f53c5185020515722e7765da34070870ad9819f8485881ae7c4e14e229da665f820da219e"}, {0x100, 0x0, 0x0, "fb181a140da1c38b0c57bd5aa8312765ac71cf565bd126950246d2c68da82547baceeab82e70096196e7e2489db7001a853cde2a2e62eafa2b67c25a5f7cfa496dfb659df46e1dc678afebe7665a2c4a2679fe887ecb0ffd97ce4e4e6fb206502e36d173781bb393767a0ec830fb3d98ec6f4af8e0f7804426bf15ba47d46a9a7d5c4281e0e15b41c3a6b3e73159503238c18717b528b9dac681cda4af1f9dceb0eab1ed7883973f44a216927a7477f372b7d04802e7a82d2c591c30aea1b9cca96322f452b4e9cc69eb72d69e8bb18b8c3c0078322a968fa13b117d03b291ff95fedd195a0029f1de"}, {0x10}], 0x210}, 0x0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x6)
execve(0x0, 0x0, 0x0)
ioctl$VMM_IOC_CREATE(0xffffffffffffffff, 0xc5005601, &(0x7f0000000080)={0x10, 0x0, 0x4, 0x0, [{&(0x7f0000ff9000/0x4000)=nil, &(0x7f0000ffb000/0x4000)=nil}, {&(0x7f0000ffd000/0x2000)=nil, &(0x7f0000ff7000/0x4000)=nil}, {&(0x7f00002f6000/0x3000)=nil, &(0x7f0000ff9000/0x1000)=nil}, {&(0x7f0000ffc000/0x3000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ff9000/0x3000)=nil, &(0x7f0000ff7000/0x3000)=nil}, {&(0x7f0000ff8000/0x2000)=nil, &(0x7f0000ffb000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f0000ffe000/0x1000)=nil}, {&(0x7f0000fff000/0x1000)=nil, &(0x7f00005a5000/0x2000)=nil}, {&(0x7f0000ffc000/0x2000)=nil, &(0x7f0000ffd000/0x3000)=nil}, {&(0x7f0000ffd000/0x3000)=nil, &(0x7f00000f3000/0x1000)=nil}, {&(0x7f0000558000/0x3000)=nil, &(0x7f0000ffe000/0x2000)=nil}, {&(0x7f0000032000/0x4000)=nil, &(0x7f0000ffc000/0x4000)=nil}, {&(0x7f0000ffc000/0x1000)=nil, &(0x7f00001a0000/0x4000)=nil}, {&(0x7f0000ffa000/0x2000)=nil, &(0x7f0000ffc000/0x3000)=nil}, {&(0x7f0000ff5000/0x3000)=nil, &(0x7f0000ff8000/0x2000)=nil}, {&(0x7f0000ff6000/0xa000)=nil, &(0x7f0000ffb000/0x3000)=nil}], ['./bus/\x00', './bus\x00', './bus\x00', './bus/file0\x00'], './bus\x00', './bus\x00', './bus\x00', ['./bus/', './bus\x00', './bus\x00', './bus\x00']})
r0 = socket$unix(0x1, 0x5, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc028698d, &(0x7f00000000c0))
r0 = openat(0xffffffffffffffff, &(0x7f0000000080)='/', 0x0, 0x0)
fchdir(r0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f0000000040)='r\x00')
openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
r0 = socket(0x11, 0x3, 0x0)
setsockopt(r0, 0x20000000000011, 0x4, &(0x7f00000000c0)="ebff0400", 0x4)
r1 = socket(0x11, 0x3, 0x0)
sendto$unix(r1, &(0x7f0000000000)="b100050400000000000008000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f5a872c881ff7cc53c8943310b404f36a00f90006ee01bc43eaea8c500000002000000000000020208a371a3f80004000000000000000100"/177, 0xb1, 0x0, 0x0, 0x0)
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
mprotect(&(0x7f000081d000/0x1000)=nil, 0x1000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000140)={0x3, &(0x7f0000000180)=[{0x20}, {0x40}, {0xfffffffffffffffe}]})
syz_emit_ethernet(0x3e, &(0x7f00000002c0)={@local, @random="a26c7d46feed", [], {@ipv6={0x86dd, {0x0, 0x6, "a0f54f", 0x8, 0x0, 0x0, @remote={0xfe, 0x80, '\x00', 0x0}, @rand_addr="588516667a6bd66376a4124c4519ecc2", {[], @udp={{0x3, 0x1, 0x8}}}}}}})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000100)=[{&(0x7f0000000500)="8a806d4c47e203eb00273e08b8abe99d4728b0e0b4e6623dbcc250a311781ea2daf7e8eca8c9fef9eb13befe4cb188844c7fbfc00f250cc165fce9f4b0257e09fae324fea3d74346f40278d11fddec1ca4ccc935fcb005b3e5ab97d9787b5f92c0d2ca36d27ffc34ce2032b595cd6a549837e84c862de6dd2879bb6ff73215a559596a9439aa6b622442ae6ae4d2ad986c2b6a73e3c6ae7d052bbfd845789e86f10b51ab353b80e1fba02e6ab4513d0d91bf2a0940e63394ecc21553cf8310d4169bd1174c1f7e3d5b1cb3498f3d3b9aed6ec8532c556389f5dee8a06ba18cb63f4a7ac2ba049b426e47190449aa641809050085019d5c5c8b9aed24f261c549491f979a1c4f513efd76da0bbd2fb0067f2ef69440808e8b9b3b4237c987a1fede34467d1f0ea65fd84d684398bbf63d4ae40bf356a7c6ba5332e721ab85bfede34c152f82e8f27d8272ee5d5f58ac0db540b160011af8f4532de293cfc1c4013497838e10a371e958caf85f0d680fb4d426be9eb58dc84b932c49619b1db0db01ade3359044d436bc0483ccdc438e7e70ea223445695778cfaf2bbe707d452045ad134f562c8554b1ead1b3c76121bccb9c63e85010099a4d46d95943a5a632adc0c7d6ce4602255eda545acfdf6189bb4143b0052841a05a22b448c660c3c57963cb99a61c7a84b4a37954bb2ff1e99a0f598021328c30f11ac4ed76464136", 0x200}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x13}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x23, 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f0000000340), 0x0, 0x0)
ioctl$PCIOCWRITE(r0, 0xc0187009, &(0x7f0000000380)={{}, 0x0, 0x3000000})
sysctl$kern(&(0x7f0000000040)={0x1, 0x44}, 0x9, 0x0, 0x0, &(0x7f0000000100)="987c000000e9c9b3faf040a765dceff38911c6bc5f6b8bc3d400af4f1148af971877b25a5bc70f2320ebc54589b0728a057cafb84bef54d21d25cdf68496d2073ce91bf3c2ff2244000000005e3ea58b0100", 0x52)
seteuid(0xffffffffffffffff)
sysctl$kern(&(0x7f0000000000)={0x1, 0x53}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETWF(0xffffffffffffff9c, 0x80104277, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{}, {}, {0x0, 0x0, 0x3, 0xfffffffd}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x42}, 0x6, &(0x7f0000000080)="f7a451e4a37b0df30b1d32cb", &(0x7f0000000180)=0xea, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x2000, 0x0, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x9, &(0x7f0000000000)="03000000", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000040), 0x14)
r0 = socket(0x2, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
close(r0)
pwritev(r1, &(0x7f0000000240)=[{&(0x7f0000000080)="0ea4d6b7e3", 0x5}], 0x1, 0x0)
r2 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r2, 0x0, 0x9, &(0x7f0000000000)="0100f1c40000000002000000", 0xc)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect(r0, &(0x7f0000000000)=@un=@file={0x0, './file0/file0\x00'}, 0x10)
r0 = open$dir(&(0x7f0000000080)='.\x00', 0x0, 0x0)
mmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000, 0x0, 0x10, r0, 0x0)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
writev(r0, &(0x7f00000006c0)=[{&(0x7f0000000140)="91", 0x1}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}, {0x0}], 0x9)
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3200)
r0 = open$dir(&(0x7f0000000080)='./bus\x00', 0x1, 0xa0)
write(0xffffffffffffffff, &(0x7f00000001c0)="0032568c952143b05817620b7ae7a9808adf9c9942c96e092d13b51ecc75ea5ad4ae0b1e9539fd2fdd55ab68a49b016ce6def2bcdf396195100bcc62e8e3250ba90d209f4d123b7f718fe88f90d8e10841a85512c25ff121d98ba6f16db95ab0bdb375d627800de0359f439b9a017d9af7229914c0e305b36cfd81c6fe307f9e7ee4510b238d490c7501756425219a3a560a9b5a80c6c34aecaddd1feb8df45570029164634b6e70fd922f194d05d1ffcd10de76650b54a49ed67e610abbf6072885c5960aed7ad71910edb61a27a9235d7cefb5775fcde1769574aaecc1f17e2cb3e753702d72901deaf8650ec0d543f23812d6caab9c76a67d3c20d578c109a6c10f7ab40538890383908281bf81e772ca4929760d2e851aa52029817881bc407549d7715d72e95786c108c72ab37a38945f77607cf2803186cd1170accaa88a42b9ecee4e35b5b7a20c45b14f2c8e4a", 0x151)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x4)
r1 = socket(0x2, 0x1, 0x0)
r2 = socket(0x2, 0x1, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x80206979, &(0x7f0000000300))
utimensat(0xffffffffffffffff, 0x0, &(0x7f00000000c0)={{0x10000, 0x7}, {0x9, 0x7}}, 0x0)
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000140)=""/230, 0xf3}], 0x21)
r4 = socket(0x2, 0x1, 0x0)
r5 = dup2(0xffffffffffffffff, r4)
setsockopt$sock_int(r3, 0xffff, 0x80, &(0x7f0000000040)=0x10100, 0x4)
write(r4, &(0x7f0000000340)="759e72827779eae5ed99dfb4fdfb86d5ac728d987507fc672997108039b05dfb937499aaa80a18587945ee643f3d59f778d2dff71a0a00a53c933402ed73581e3ad41de5a06d9398359b33c36193c3f79dcd56f0242f980b6a220309d0a86fc527294810d93456704125", 0x6a)
r6 = openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x80, 0x0)
pwritev(r6, &(0x7f00000013c0)=[{0x0}, {0x0, 0x47}, {0x0}], 0x3, 0x0)
socket(0x2, 0x1, 0x0)
getdents(r5, &(0x7f0000002440)=""/4125, 0x101d)
open$dir(&(0x7f0000000000)='./bus\x00', 0x40, 0x46)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x2, &(0x7f0000000040)=[{0x3f}, {0x2, 0x0, 0x0, 0x649}]})
sysctl$hw(&(0x7f0000000100)={0x2, 0x1}, 0x2, 0x0, 0x0, 0x0, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0xffffffff, 0x0, {[0x81a, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffffffff, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000], [], [0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0x5], [0x4, 0x200, 0x0, 0x10000, 0x6], [{}, {0x200, 0x2}, {}, {}, {0x0, 0x0, 0x0, 0x3}, {0x0, 0x0, 0x7f}, {0x4, 0x0, 0xfffffffc, 0x2000000000004}, {0x0, 0x0, 0x0, 0x7fffffff}], {0xfffd, 0xfffffffc}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r3)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
pipe(&(0x7f00000002c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
ioctl$BIOCGDLTLIST(r0, 0xc010427b, 0xffffffffffffffff)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000140)={0x0, 0x0, 0x0, 0x0, "0100000000000200"})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
r1 = socket(0x18, 0x1, 0x0)
dup2(r0, r1)
setsockopt(r1, 0x29, 0x9, &(0x7f0000000180), 0x4)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
shutdown(r1, 0x0)
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="28000000ffff000001"], 0x9}, 0x0)
sendmsg(r0, &(0x7f0000000380)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000), 0x28}, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b7f000001"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
bind(r0, &(0x7f0000000000)=@un=@abs={0x0, 0xd}, 0x10)
r1 = dup(r0)
listen(r1, 0x0)
r2 = fcntl$dupfd(r1, 0x2, 0xffffffffffffffff)
close(r2)
r3 = socket(0x2, 0x1, 0x0)
connect$unix(r3, &(0x7f0000000000)=ANY=[], 0x10)
setsockopt$sock_int(r2, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
close(r3)
syz_emit_ethernet(0xe, &(0x7f0000000040)={@broadcast, @random="9275c98a575c", [], {@generic={0x8864}}})
open$dir(&(0x7f0000000000)='./file0\x00', 0x200, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000040)='./file0\x00', 0x0, 0x40001912, r0)
madvise(&(0x7f0000000000/0x4000)=nil, 0x4000, 0x2)
sysctl$hw(&(0x7f0000000000)={0x6, 0xf}, 0x2, 0x0, &(0x7f0000001080), 0x0, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x28}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x44}, {0x4d}, {0x4406}]})
syz_emit_ethernet(0x36, &(0x7f0000000100)={@local, @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @local={0xac, 0x14, 0x0}}, @icmp=@timestamp_reply}}}})
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
poll(&(0x7f0000000200), 0x2000000000000087, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x5]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000100)='./bus\x00', 0x80002007, 0x5cc2)
r0 = open$dir(&(0x7f0000000000)='./bus\x00', 0x41, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
write(r1, 0x0, 0xc0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x8020691f, &(0x7f0000000300))
syz_emit_ethernet(0x3e, &(0x7f0000000000)={@random="831ee5407df0", @broadcast, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x30, 0x64, 0x1, 0x0, 0x93c4ed541a00b09, 0x0, @empty, @multicast1}, @icmp=@dest_unreach={0x3, 0x0, 0x0, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @loopback}}}}}})
open(&(0x7f0000000140)='./file0/file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', 0x0, 0x0)
setitimer(0x0, 0xfffffffffffffffe, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000002c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000340)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f0000000000)=[{0x64}, {0x7}, {0x6}]})
syz_emit_ethernet(0x36, &(0x7f0000000080)=ANY=[])
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$sock_timeval(r0, 0x29, 0x1b, &(0x7f0000000000), 0x10)
syz_emit_ethernet(0x3a, &(0x7f0000000400)={@local, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, @remote={0xac, 0x14, 0x0}, @local={0xac, 0x14, 0x0}, {[@noop, @rr={0x7, 0x3}]}}, @icmp=@timestamp_reply}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000240), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000040)={0x3, &(0x7f00000000c0)=[{0x50}, {}, {0xcc06}]})
syz_emit_ethernet(0x32, &(0x7f0000000100)={@remote, @empty, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @empty, "34ef9d24733b426d77f6c66e26334865"}}}})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000001c0), 0x4000000019, 0x0)
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000080)={0x3, &(0x7f00000000c0)=[{0x54}, {0x80}, {0x6, 0x0, 0x0, 0x20400000}]})
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f0000000040)="7cdc3fc0be8c090194c3df3860ab", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x8082, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
write(r0, &(0x7f00000000c0)="49acdc3b2084845d6d7c44b6cddf812c390695d3d8861c1778817ca335ba86a864933f916628163ffdd3302b16f5a309b8bd585d6b33f88f63e845d9bb9fecda2929a78cc1f29a362ceaab210b2466ea485604d15ddf8f0cfae214808a7f99f34d1240288756be7a520b9991f3bb478bb4f1aba883a65c3ee77e7be2ac39e345ea54d0a8f298478d1c6e3126610e5c30068e713121702617d473ec20b03332afc4bed369f4dabf3c7d62b5bf26698c5e3e35920cc6bef24c", 0x10013)
open(&(0x7f0000000100)='./file0\x00', 0x461e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000080)='./file0\x00', 0x0, 0xfcfc96ac7f78659a, r0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x0, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x0, 0x10, r1, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = kqueue()
kevent(r2, 0x0, 0x0, 0x0, 0x0, &(0x7f00000000c0))
openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0xb, &(0x7f0000000040)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x4)
setsockopt(r0, 0x29, 0xc, &(0x7f0000000140)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
syz_extract_tcp_res$synack(0x0, 0x1, 0x0)
r0 = msgget(0x3, 0x0)
msgsnd(r0, 0x0, 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffff9c, 0x82485608, &(0x7f0000000240)={0x0, 0x0, 0x0, {[], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400002000020d]}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x2, 0x1, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0x80206913, &(0x7f0000000300))
r0 = socket$inet(0x2, 0x2, 0x0)
bind$inet(0xffffffffffffffff, &(0x7f0000000000)={0x2, 0x0}, 0xc)
setsockopt$inet_opts(r0, 0x0, 0x1e, &(0x7f0000000000), 0x4)
mknod(&(0x7f0000000040)='./bus\x00', 0x2000, 0x801)
r0 = open(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
ioctl$TIOCSETD(r0, 0x8004741b, &(0x7f0000000140)=0x8)
close(r0)
ioctl$VMM_IOC_INTR(0xffffffffffffffff, 0x800c5606, &(0x7f0000000440)={0x0, 0x0, 0x6})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
recvmmsg(r0, &(0x7f0000000500)={&(0x7f00000004c0)={0x0, 0x0, &(0x7f00000006c0)=[{0x0}, {&(0x7f0000000280)=""/205, 0xcd}], 0x2, 0x0}}, 0x10, 0x0, 0x0)
writev(r1, &(0x7f0000000240)=[{&(0x7f0000000000)="ad288462b8750699dff8c5b07934d5b65a029f05be99b0ae51c4badd58f6ce0f5e82a080cc18c50b7f86c6e63c32d12fcf593f17", 0x34}, {&(0x7f0000000140)="1857c23ef77d970b9702b3fb9c72e2148f9a39170e0cd292b9c7c2d4e50b97b36cad566f65205f89caeb0a90ac37faae86997187a0766e7b18127fa871ad43b8dd3998b7a149073cd33db05c56ae3ce95e1f892cb8fa969e24690af784d3a23d940369ecad853e3a95b7267d12adad61eb916181ef948d6d80edba3b16784593cefeea2c03d960c1408dc2be2f225cb314f9adb6e9a970e41abfbfb9a2dd73b21a83eebcdbb23fa80628c9ba52f7e29e3cfd343112780e5fcd5094260fe05944cdaf6017ecd18cb300741d1048597e815409c7e6e91124b82825b871060f14", 0xdf}, {&(0x7f0000000040)="3a22a89bf85bc16705412fb92e62795e79cc9b54fc99de9fe65207ca20ecd47a65d1a3a1d3d9dec2f0f91b04eb2bbfddacb55cd0f826bb4bacc2b28f3e43f5d2951cba21672a88e528d1805915cee068cbeba4d911cfc9d6fe3095de5c37", 0x5e}], 0x3)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x242, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x3d}, {0x4}, {0x6, 0x0, 0x0, 0xfffffffc}]})
pwrite(r0, &(0x7f0000000340)="f0529d3e13eb0913587ca9ec25db", 0xe, 0x0)
r0 = kqueue()
ioctl$BIOCPROMISC(r0, 0x20006602)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
ioctl$FIOASYNC(r0, 0xc0084461, &(0x7f0000000080))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0)={0x0, 0x0, 0x0, {[], [], [], [0x0, 0x0, 0x0, 0x0, 0x2, 0x20], [{}, {}, {0x0, 0x0, 0x0, 0x8040000000100}, {}, {}, {}, {}, {0x4}]}})
ioctl$FIOASYNC(r0, 0xc0284457, &(0x7f0000000240))
setrlimit(0x8, &(0x7f0000000040)={0x7, 0x8})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(r1, 0x802c7416, &(0x7f0000000440)={0x0, 0x0, 0x6a3, 0x1fc80d8b, "f4ffd25e890000011868180000de00"})
ioctl$TIOCSTOP(r1, 0x2000746f)
r2 = open(&(0x7f0000000080)='./file0\x00', 0x60e, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x2010, r2, 0x0)
write(r0, &(0x7f0000000140)=' ', 0x1)
r0 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r0, &(0x7f0000000000)=[{&(0x7f0000000500)="90c3fe67eb586898600425f2f573e0d1ac83c18d65c8e22066c0d389fe894a974c8d45aaf9d2e7ae9fed58938ea6ac68a0b0632688ca0fab3647175abf22fea120c9b3bb77ca60c128295bf234505356095dbf9e50a4a5079723b57fed8ef0a251b91e67e1f5d347d5b668a3900000eea3962e7c10b8d9f53f5c82b5eacc15757d14f2fa6be9a2cbb2cfacc5e940c4495e2177cb06dfd1e3208364bbc454327b17f0a774d4346a1522c332ea628b8cbb72e9e7247818f970e0174685", 0xbc}, {&(0x7f0000000200)="a5f81d1fefaee5aac038", 0xa}], 0x2, 0x0)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r0, 0x0)
r1 = socket(0x2, 0x3, 0x0)
r2 = fcntl$dupfd(r1, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0x8020697a, &(0x7f00000000c0))
mknodat(0xffffffffffffff9c, &(0x7f0000000100)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000140)='./file0\x00', 0x0, 0x0)
seteuid(0xffffffffffffffff)
setitimer(0x0, &(0x7f0000000080)={{0x10000000000000, 0x73a}, {0xffffffff}}, 0x0)
ioctl$FIOGETOWN(r0, 0x4004667b, 0x0)
close(r0)
msgsnd(0x0, &(0x7f0000000280)=ANY=[@ANYRES32, @ANYBLOB="506f6076a0155e9e236b5be2271f219bd12d8f8bea86ff39ec68065f833d731581d11330b1dec687ddb0dd2cf95309b450c8428c3f9dc61b307fd9099f936913494b7ebd12bac23cb9fdcd72bb6df4825de74c04ae770aac620e47e4346f78e524cb6b67bdf2001e74de80247b5bfd8476d886a14b248fb0556be2ec2ee34532cac12a0dbf7ac8e7d5"], 0x169, 0x0)
setsockopt$inet_opts(0xffffffffffffffff, 0x0, 0x1, &(0x7f0000000480)="c30195c01c6fc117a7a2ac1be52c07ff83233cdf46447b4760cc2fbd253465df4bf2c9a9e464b3934689d7b8072595f8cc929fbd45c5ab2c19081813cb4bd78c00032cbbf6ba08b77fe0a02b09f310647d5624ff6e7ed9d0fd596457e975d74212838da4da38f73047c661cf7cc5063f7ab8a518e4e5c443f7716600e98cae614ba3808b5820f7a74801ef857a1988d26c1f36abc69b6222c4a133c4cda9834bd2bc1907db101c94af96eec4", 0xac)
msgrcv(0x0, &(0x7f0000000380)=ANY=[], 0x94, 0x3, 0x1000)
r1 = msgget(0x0, 0x370)
msgctl$IPC_RMID(r1, 0x0)
semop(0x0, &(0x7f0000000080)=[{}, {0x1}, {0x4}, {0x1, 0x3, 0x1000}], 0x4)
r0 = semget$private(0x0, 0x1, 0x0)
semop(r0, &(0x7f0000000040)=[{0x1, 0xff, 0x3000}], 0x1)
semop(0xffffffffffffffff, &(0x7f00000000c0)=[{0x4, 0x2, 0x800}, {0x2, 0xe0d, 0x800}], 0x2)
r1 = semget$private(0x0, 0x4, 0x1)
semop(r1, &(0x7f0000000100)=[{0x1, 0x1, 0x1000}, {0x0, 0x4, 0x800}, {0x0, 0x2, 0x1000}, {0x3, 0x9a6, 0xe86f00ca028eaa0a}], 0x4)
sysctl$kern(&(0x7f0000000000)={0x1, 0x37}, 0x4, &(0x7f0000000080), 0x0, 0x0, 0x2)
r2 = semget$private(0x0, 0x4000000009, 0x82)
semop(0x0, &(0x7f0000000140)=[{0x0, 0xfaf3, 0x1800}, {0x0, 0x4, 0x1400}, {0x3, 0x7, 0x627462befbd9eef4}, {0x3, 0xfff9, 0x800}, {0x0, 0x4, 0x1800}], 0x5)
semctl$GETZCNT(r2, 0x2, 0x7, &(0x7f0000000600)=""/70)
semop(r2, &(0x7f00000002c0)=[{0x2, 0x3}, {0x0, 0x6, 0x800}, {0x4, 0x200, 0x800}, {0x1, 0x8, 0x1000}, {0x1, 0x5}], 0x5)
semop(r2, &(0x7f00000005c0)=[{0x4, 0x2}, {0x2, 0x6, 0x800}, {0x4, 0x8783, 0x1000}, {0x2, 0x400, 0x800}, {0x1, 0x0, 0x1800}, {0x0, 0xfffc}], 0x6)
r3 = geteuid()
socketpair$unix(0x1, 0x5, 0x0, &(0x7f00000000c0)={0xffffffffffffffff, <r4=>0xffffffffffffffff})
getsockopt$sock_cred(r4, 0xffff, 0x1022, &(0x7f0000000240)={0x0, <r5=>0x0, <r6=>0x0}, &(0x7f0000000700)=0xc)
semctl$GETPID(r2, 0x0, 0x4, &(0x7f0000000500)=""/113)
setregid(0x0, r6)
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000080)={{0xfffffffd, 0x0, 0xffffffffffffffff, r3, r6, 0x40, 0xffff}, 0xb02, 0x9, 0x8001})
semctl$IPC_SET(r2, 0x0, 0x1, &(0x7f0000000680)={{0x100, r5, r6, r5, 0x0, 0x1, 0x8000}, 0x3, 0x8000000000000005, 0xdb9})
semop(r2, &(0x7f0000000140)=[{0x1, 0xffff, 0x1800}, {0x3, 0x0, 0x800}, {0x3, 0xfe43, 0x1800}, {0x1, 0x3f, 0x1800}, {0x0, 0x7}, {0x3, 0x1, 0x800}], 0x6)
sysctl$kern(&(0x7f0000000040)={0x1, 0xc}, 0x2, 0x0, 0x0, 0x0, 0x0)
r0 = syz_open_pts()
writev(r0, &(0x7f0000002480)=[{&(0x7f0000000180)="1aeed8442cf4378173e96ae1f67e0d48c7efa8b0cc0424b2c8f944adb6f378ed15f9176ce42b9bc103797a70bdf1f633ce020623e94ed678a0d5c58aa3c9246b0e0ac340748bd4819d2cab866db2", 0x4e}, {&(0x7f0000000300)="1dd81e72db04008884136c3635633b7e0db44f2f6b99982caba3561980cae4a5cf5b37488c4cab62c1cd815c40cbdd523ba0da634fc29601564a79efa1c84fc7c400cbef89b8a22af892e8f01d4f2fbac84c7ea0682912540489c29d57ca2c03119b5d104b0a6aeda036b98927e5d8666456dd442a7a257816045f98badc16e72efb4cd5a2b201a0024c5c3c7c059e967e1f87447548d92c2588aba40e3bba08d030d4bde6a32d5f483e947ddba328ea47dea5bd71a0b9aa2b428c7ceeafed72659c147b842e4778d8e1bed373a0d56c0303f854ad3b6e6ee3e9533db9f3d64f2feed2addaedfae2cfbacc9e7dba954523f4884d5cbcfcefe7a6d62aed8506c3a3b6e53b7c0f110675e20c0c7dd553187f82cbca80eae17a90d7527dbffe80ba4573f08861e6f5254e179f438e286606c8abd9e82a859c43715b2038f054dfbb0a68dd49bdf583dc5bb261862b96b77b06032815045a921303f6f3f7cd07d52d83ca68a602dd36073041a51bda9ad8b8621d058e4de28352144651a4720ba5d8c18c9513d4f9ffd17938bb693edd48a980c3c6dd582fe2d5e76bf427031fa6b156df475aa7be6ded7b28b388f558fc4555d104ead3f5bc9ba926f7b5f41273d241ea59aee001eae2fb047c01a1d5f7de9b5d4b724a3619f7b7cdfb2ce8bcd9bdf319b303d83b3bfc834332a23cc07fc3fcc10cc5c6f7727974b27ee7ee06bbcfd0a0fbf13adf7b10fb82a75b403c9ff49ce7e3d972fe82fb7057d88b19c69c42c02f4fd0ee32b2dbbf3e8d45f5ad1379d1b2571c2322e96ea0bed59139c1f6680ceb9427bfa3267209fbffe95c5b62e97f4adc681d3e93b088e12013a02735d5532575031a04e316266f2c295a0885e3e2d3f9d43f9a0ae1aa875a25e5bf3fa813996592dbf945451f96a3926f505ee1799befe4d66afb551cf3285d318da40caa4cb25095fa59da67cf5b127633da6efed480b083ba1271773ec50a53215c0de1b2e24ff53ee35cd162af1f9bcc79d7281e2f8328067901e75b9d37ddb8bd26dae833ed244bd61c4313097732875357dd7b5e284bb26761852ee741def7c850f4d17ff368f456bab80930598b17f103ae765e6c92f48aa65288ff960b2dae0590b1626708ba765bdd2f2a4c2bef32a046b984fc0b5da196c4127b4739cbfc4267a360320a1d05b8296c5d524bce061a5d9539f0e4bb930d18df26ec6e621f05a0658559782f45099395ee9de8f340b4571feff5eefc7f74ed78edeb27dbc2cb56d25b5bba35c058f94acbe84d4bc50853ed818f17773cb7a140ce93e64e0c684957e4adda594756077d7953f3e908ab10d262b7ab280d5dd05bc728194c615d38677a838db9c143b21d59a0a52cd9be2ae6412f327890f8a32207222a41a53a64be31037d27d7f339a2c4db1425623deaa0c68b1d0720be87383a0d97d838a93725d505e6b9a0e38a38b7dc99da3d1e21f8bb52e9248f00e1b0be246bffe932ed08243f6e7dd685aacf89a06b030c4a63db23ac9e22b12d29d85b130905ad26baf9bc5d38475924d9f7b5b707b98ff9047468262a7c977a6ba7544c59079ed7ba588773d060cb8f90c03b66280e57bced833ef857fb2bb6606b200863843601e4a7efde28a1d73fb27bbee3993b54947daeee7e7ea3a36017cedc04aa44b4d2f4e81541333eea45b2a0292aef49388bb226c002eb6e09cf3827901a7b28bf5cd086d67cbc8f3e34ccfd8421309f2f4331323bba1d989b6ccb0c423e5fe5d4ed5790781c2978beb08e46b5777288221fd654a2086e787df4e66100f668b8320c899df15b8677e2878de34ab1417146367eb8d658c0f92a18214f430afef5e71ed00348b190cc81606291cf5af1e5d612501c8b4d45e87ebde1470666592829e63296c948f8743ef44032508d9a5c5c8aa11822080fa0a9de53c03893afd00984ca8368d11b80374f839b00df634a0a4c4d19c519675a061e178f564694d713e76d4feb47b2cd5608e58ab84cf796247dba68a3fb3bc9389c522dc3f7af967ebc37dde0f99c845ccdcd742028d2b15e11672dd9f9fcc64b0325d98d988d0e4ed9f0387f383131779c6426c5790ebbdd177bc07bc5793b032cacb9e9acfcf36a86168c8df6efdef094b796f5908a25ed28dad91f9094652f5ff15cd99bc67ef16b76bb017d61b5ce847160b4f3df716442dfec549d8394d4b3e2540b64fda2cf7f9ef93b6412813cdd357af0e837d7b4120512337b2a21c425836ab3f99b961f3bd0822008751ddbb760a5231b8cbb661487f822e7501932c62d7a40e765d20693b0761d08c1af8433170a9dd26dedee2b7c86d651f6aa8018ff42c06cde4c7cf854715833963ee0b1eea030c249a02177c390fc896963fb80e0409ec151a4bcdf7777c5dc4295ee119307e5cbb03e8f8231a0040a7533fec77421c0792f7188196aeea1a95ac755dcc36040b5a6e4cb3c5c2f31d6345de7d708852e29fe44bdc1be6a9ea467507bb6a86b9c82e75187e834050a0c9a933e0862939a9fd53ef7e211ef1a53ce7eabe2ff1a87c9e4fe0aa39136bdedeb6b8507ac027a2f1b9fce35c8b668102cc2c8a0b067763c032de600b1d9b358fcadee07ea77b950ce0cc40ba27d2e5cf3e8d2536b38e12582bdb5f58f728a8e81f211c119609b8bd3bec8f0b17954a85e32a1c0aa772b8159ac09844b87cfdd01ecc7a5835a159bc7550bbf2ec538661d770be3a9cf9cb151b55ecc14e0be9e1b14fcc1f9f4078aeb7387540d8dd9f5fea8583bb230ce381dad2f5df79980d57deb7441d2cc9a959bbac35a059971208bac838263750123c33abed5333008090460e56d6b19a7d48dbacc388b4777e3a48b65cc62c0d125bf23c178296266da6e9feccde46b624b18650ebfc615fea790f6600c7c82bb6d5033505f0feae916191a81d0bb8cb215d833d7fadc116fef1e6207e92d727c88ebf8b649f5447be14c30ef755a991373306eb3fc8c3fd0a336ed42a69ee0084375bb349fe5c20ec3c12247c3af45d4a8a473292bb190f0ec434f778321ce547bfd3801a8dc0616d83d32e68415b6db2923282ba77e86ebe25f3ecafc2073434c727b6810d262406288e29c8f4e5cc9374771f02e4f35e934bcf7cc5d671c0f944551870e4c2f74290e316f85b6f218340b2441e0d415a24285011f8b4b6f9e08828ae53fe1dbeb67ca0cc482c523e1e0703f3213468c639f47e412d30d9cdbf174af328fc988d1e1479f0b460d008123ca7b0239f0ef3ea86f5cb180d4ca36138aa8e83f5fcb2073da8ebed52d3dfcebded94dbb4eb09443de6ff64431fb4259f9a36f342a0de7a34c053a106f5b9b7acda8ba74b494e8e332c8e698e0f71cfac5b756e802974b6a3675f5a439387fdd826d9e468fff15dc416d0447de370eb9a6ae17ba626e7ddb57c85623869cfe701acb6dc95e4516429b05d396eb8cf2df570a29dd8131dfe7904e46b574d9c3115837870b8e00be334a5b3508c830824bf5fd6680a1dac36a26cdb8e6bd02a8e3530e413987a0d8d25bb0f11afde27371e115a88d67a3f3a42f4ca6a2e6610a655e8d4a1405194209dfd6f06d5261d43395ce0c47f3052a7fb443a60bc81526c664fb033f1da27ecf89a0dbbd77dc399fd1f9d9f9d270c4bc94e49d834c6f5fd2cd4d02eed9d1ebab1f5d11214b2e61c0259cae80eb56ba98a4c4dc625dd8d0d014c6dcfb7ca1f18c2fb2d29cd040815b9d8f21e3d71a6f57821e95cb0343433460d3761cfd003edb6197817227f5a6d011d3ccad3178162ff7c23ec0d3914ce6ec7ea9f3cbd15e9b3e5eb284960225ea69d115630eda2663738b7b2e30fe00224d0b06b61d51d516d5d2f4e4957d1c39cd8dd76ccd9cc38056921fe54d9cd826bf1e29181091c606ba9d51dab5127ea51c54b61e3f9e791505423407d0d90c979a0dbd2614528652e2fa49c61529ae7de1c69e1c76457bcbeb7c653eca1b991ca486fd5c7cdd39e67780a00d27d8aa6c18ec0a4f6f91cc8d00f92592b08f1145c312daad0e75b349bdb36fe9f2ea77e15db38de3c9c439b6b8e4e0d6442d0ed1ff58b0f50c4ff6017db697eafe39c9457f4656a5844d82382c3af8e09d52634bd69fc811d316c3dfb3349f6a953a56cddc4813d6e71d74196a1f6dafb024e185aa5e8e1a5ec5bcf59fee076fbc16ea468a5cb2d401b375aba1786e21e0317cf2932346caf8f7bd582b5a235c254cf9c5daa3f3084e68bc0b8cca762e1d155a9fa66bd4666ce37905ed5ad9fd1d5c567ed5d7d98b868e6d1dcf11c1d777b3829871891e156356e1141bd9d91f94c3b863a1c3ea3e58f33a16a128afbea0d93b07b569d12d1d3479c62721842d6e4c668e190b079e36404760b42f675e0fd0bcfd7a5a2692f04764283df21423752401f25a6ca041510119e3966455564afd50d56fa8c793fd0ccd188ca85132371960a4a00ca0a3fa37ceaa4523c2600025ff2e4182867348021c0cd807f86fbdd6f23545af999b8298d258c350b7ebd9f50d241bdf36eda7d3c993767b40b8de11e4072258df7971185e61b97b5165488ce6ff0d66572f75f23fa45d74153f62618c1a81b4061de5674e4077c125279f6881599951c0f1098d441da30e3d9e24c3e66b72721905df831370c82c1bad8f6a4342fd7fcba55de52aab7b819557d99c9eeb6a86ed2cef8aba549f9607091e94e6066ea87907902d0a0fe8960d443bbd1208948cd59fc9e1b5cf584606d2ca477326ad4223172ffc4602fd050a1c708d76d85131786630695c696a71fae4fd81bc9bff1793b494d754fcdde02b6b80f97b329c0ec53dd878d8b994f2e71b4dd136042b9d681d81341d452be630ea39aa91171b4bd2f504148f1d06fe9aa9ffce1bb6471f5da71035e28f0c333aaad651d66ad7a9c300b2e0898178565185e5ede3e45fd24debc8671adb185166509e12d16646630bdb7cf82b6bde5554b291c6ad63ba4ac5da33a8460174162bbb62f6365da2ad27a80f7da6af3c11bdedc2054890ca008d77cb9b78b25fd09cd7c479b0cd9fb053cc59d290cb24eaa37689568f310eb051e623ace3e5aec2f32d561759c0aa62b1c7704d3996da5fc42a5a965cb87368f2428242e6a32a3144242e20c648fd87ceaaa69d191513bedd82d0f326e7626f46dbbc117d784343908ea7b5e55e5fea56eca8ceedf5258c9ecb44bb00309b6d3be272523fffda19205668b4605010323c9f5dc894d275924540baf5a70b7141dec33c683ccc0f1a2210cd00c07622e761afa9291878f3618985a8262746129e3cc00d7ff9152ac989899f459283f000754822971160ffdab166759e4112e9ccd654de98040373d7c5128984fbf31353e5477206010a522b0205b06f1a523dd89ace7bd9111c2a7dc99cfea60c6ae8a979ff9029fd312eb035e463aa79bd33f6dfc9bf9877ed3f3a2905d7167010e515cd364d71ec5ba6d4ea9295d771f3116b53ba54b56adda417fde9ed4002ce2b8c6dd2c595b3afcf45dfd38e7163c3c8c92c2b8fd0499597a916b71e6ede906e509c356676661d0f4a4daf228dbc5306d8f1e719b2ded58c32d1da8857c394bd710c04c428219746e1ebea9ddbdb0d93334a6c9ba4cb92d880afb6ce30cef93deaa70f2d99d2a63d0afe1f918a9bc08eb69cf586fd26f5591ecd097e2359c354d7dc1538e5398e0811e5d65ad64abd21591df77d40dd3c472fc4eb1106b65c5ebcc2da686db60b020e80c4e0a53a431371fbf57d54950d3e8cd6d85687daf2fb00d94caf055a89737c03af89e28b", 0xffb}, {&(0x7f0000001300)="15839400d4f73511bf213ed25bc1ea04564394ecbbcb857a2a5babef01df94736171d18f4c88cc3513a039136338fb64a7ee6204c0ee36d1cccfb904396d3e48137f6b1ca34d3e3e02741b9585fe1f9b6d803a1fa00dad9fec41bf36bfe2495c15df0278a3c77c4e66b5b8a062345349b835505c2c70e41037fdb0b38f47abcb4cc98a05190b6324d99461def009b5bade51b5027fa9f418316983c66421ab1115eace98a6d577e8faaae4a244c474f34b82f8ea12c57e54f4318fba4f6778e413a728684d8bbb265bf05df87bc0111352cf8d707ee0b912289d3c7ffe43a43ac625241e4dc4a01b5c2a7520a41f95cd896fc7127eff7ecdec9138c0e6cca81a8453fd06a2f51b667b6cb27eb8d9780c05ef34fb75733847fa8cf792b1f4c5ed782d08a67640c7798d7e40f26107e09fc68d74d740bba31e72ec3b088f3c4a188f24b7fe0367faa1b97649b895636b7076c3b450804ec6fc71f953e4541e750ddba7a93ff7b67ebe85020ff68431cea402eaca940d7b386f60d10973f5179f30edd032394a6c72b33bc46b827925fb5a9a91a85d938b6f27eca2c18d6dc0524a5188676c21ed8885868105b92cda87c82160377eba3491caefd39cccbbf6a24a7f7d54d27269881311653d947f77890f2af59213052866c9ba8f3bc9941f8ba4ebd37e443eafafb34f12454bac98164b78d02443b0d86061a913c76aa6a2458b26a83cd190a1584121c46d4df9cb0536239638d3607940d987d58aaafbf9b6b55435b31f0c7c2a95a5b64ed2fb1fabd1f087adcc871b7a0a8dfe793670b59deddd52634d2dc795961a73c588b79497d38c0b286687e5f267deef0c8b1f416980f30ac48947b55f861db0ccafa53e23cb507094650b7d30a91a30f110d518a43016d479161ddab04c1099e56dabf17c461e4fd5f328803d3f82f4c9a4f0b86d0eda06ae4caad2ca719b0737478b8d5cbfca8ca7077acf8d75c746b8c59b75379ca823d8da453682f780a774fe010f3f5b7fd0da80435679c011c119cddc2a42d6d9b5fbac1e694d3f5e1c6d2bd24ad96af5a1d6fdee33c7e01cd3d8a96c58aa2b354768ca1827c05b0b8eb50b2e1b96900d1e924e970ff54d73b17fbdb571b019de4bfe865e1ca09bcae5ce056a0d0d0985e1a53200c68e5232e1364d16a05971f614a6fb912836d735021e1d550582275da565e14dac9482275233ab5560af1c278911ca5ba34b6ed4906ec465984d6e6f28053afd80fd591315437da36b2c9cf959170aa5d2f6be275a235218b6b86bbd8aabbad2c1f823394ca274a9f5090b6bd54ed95a2321f7b6cbad5e085b28e3d28f572f9a71cf3c6beeec8c65019a386774cc9fbef88e1277d63d3cf66a1a398672b7e6557ea6e3c98a1b6a16a03f2d2cb20a25ecb60ab5847d4493f2d885dd6882128f378d9039e11faadaec25952bf25d0ef7eb76b94e62228c0584f420cb8c6dc523feaaf2b02ea75b7102d165150e61f105b81f2307b3b046fe343f81db0e705e04afc5e55a2a05af6324f5b1f8dd0325e3e2b079de65782de2e62cecd2411a5856d3f189e0eafc6cc5bf9a91a9cf9977f583a5edb64795a02d775a97d2b2605d72449b7dd3435e3a6abe1b4ea1a3234712b62715a21061e83e9d4e3c1c23a6269e2620dd84bca0919308b5a495bd20d8d98168571396ffd45a0940c99517933cd6bf4d8a00e7c257cc9f71db1bee96189fe5f9f8edec389d92cb89cdeba8fcdad71cefa1a84368e712559b9a300e0089d5c006cbb67daef7c65b92e65d7d01a632a52af68caa4d22344b5fe65fe416cebd6102ff2c411966366748e55fb79a6588c779eb80951ae7277abb91c8ee64470a5981a17358a3a65c80422de5e6e9e2345b31e034c9cd7e222977155a90091d7c6efc044377973182adfe5fa79ab3011317aa9196b13e21993a90572119aae5717e944c946e9f079be0552b9fc762e80e0579f36068c752559f2874751db1e6bc8ee43748b81264a577cb25d382485648d84579488446aefedff7c03331caa2b7c65df71286dbbee6988bdc013b2302258516ea7e65caab2abbb85cf11e295e987a0bbb5e905686bc64eddf4b43f3d1151fe2530b0330b641fef725c644bf0780aa13adc90366bbffc90d0657bbbabb5b96b92a04021f22a0bc649e5233cbdb0056c722ac5857ac2a907442b9ee3b46692f313e6a1a2da5cc3702804cd483392381558b6356d256de9194f5173ba64b9d8800656afd730ebcfcbf6f5997f10ecfeb66e764b20d6070fbae557ddda398550aff35e56321721da742295204a8abed8ad0f8f3b83e43a58d5b5ce38fdecde41b71447b3bc910096d466b3a01b867a02e2880a611aec68795200383e67cb69581743e614e6775899a29c402d6a82c38f95223dfc0b229952acbc9014d8a2075a94f3de10ef3a17d812a34ac9bb21885cb73cc321305f82285528a0f517c9ca233f7deffbdcbc045d7b38f826f8b06873c3585636deb6f53903a14496d6b5e2da088976a6994f9c4b56da2c47893933cc3eecdc004d7792be186fab6e570a29a4ee0c70a6eaf244c9b6c90305007da2b63271f2ba3b8bf715505ed2949af5b551323fececc3de5ab792cc50318c47c0ba353b6e218f53a694fe31ded5b3d23b3f9e1f291b2d442accfe20e3994ab61fa57092b76fec957da668ca2e1ec6931726675518e0da065be601e244b48d6e82765bde59d1ee126914b9a2b5223e3ecc3d20e5d49738fd1effeaa7b948c04f8249285b7b5f97fea2287fad05eb5d8bc43e28ae8d51de14b642582ede2ab33fb9f0af6e3ac80168f82d3adc9fb0e83c01dffbed434f9665b974af9c8a8c037382b866b40971221713810fd8612cbc5d4dfcdb67d09a91be94e4a506e46130f22258a979a1e46277b924f916edee77bb81952ae6c328baf0134e43d7719df43081ca50a3f5860f56b89397ab7d324211cfa0db633225b59fabd697bcb32e9502f0668c1ec0134873daeb9808380bdd42013c5cd58b9013b96f7ceed3551b9363e797b1c8e4ae95efed220129bfc75da15912c957446a4a4968edde6d97664258db2c7f2d9a860647ef75355365ff293747127cb28e3102c9e2350bbf4c34331b2d829ba730f56d400cc9e54285838255a0323c76a1ad76c9c9164b14cd50760c54614db7da1136087d9bf82657cc1791717114f711f76911ffdc83fe7cab47624ef467f65e6e3bfedf0b74e7caf2477cdb99fc5cc8eff87aa0b62d4005dbaf8fecd31f0953ebbd2b0131a1452bcd03485cc74c878a911e7a5fdbc843e09900001838d1fe0803884d286876e4cc9957be53c13840afc24213a433cc8fd275f2ba9592f921aa00dcbac8bf87dc5399d477dd08e002c56962e4bf21b5b44fc135110e301928e5d917b67777dc9a31bc15bd18d5a4327fcfa0daa993fb043ec034f0678b94bc117fe6415ec94ca102226c07aa1d6c5c0a5e64610f82318594f246e9f18c5dead40d84a96ed066ea84f0167e5acb91b4b25e39981d0aeac8ebd049920b10d4e3e69e42e47031e0792383e39d32bbb65698b41791fe8f8e3cead53aaa07dd4f6ec90019c226b97f4920bada139f6fda3c8d876b4afec9b32e209b41d9b603f567bc60de9aa32053333b535763ce1571ac6b3200736d69507f7feefccd48fc25874f9bfa16979ab85804d8d511e00426154dae6941b1d8050170d01cda66ca7541c1c1d95594b41c2dac8ec340a7610ad9cc3bcf61af7dc67c4d848c06c0867291e727d12c09b848a3a2c77cc6a63926dc51a0d93b04bb5f5e2835fca777697999c88f26596e414f684680d2049132eec6d1c71f9227e675ffcf97355c6e362c2c7f52c23f4c978a7981623448165e61e97af7c6e4165db3cbf618640e161c848663484e26ba1e349a90398192a4d478294e30d7abd24e9f77ef75dccec9a1d950ecaefbf70723e2f43c041c71bc748fcd9fcbd9fbf73268bb8d63c607962b071b31fad76804d40c48b1a2f0e20bf27a9715232aa7b0fe352083a16fabc50bdd5bbd914a7e1f659fb83b87706e3e5de3478eb8bdf9e32b9935034c3b140647fa748379ad484d36cfcff3b84ce87453174e21ba3c7a1bc1f411f95c1eb013c67a5eb58966d0a340c2e23940dcd8ce1d0e0d2305a230f7c20c293ca2c7850f78373eefe9edf1a38b902be77ff44068bfba195c0a92da9d2ad111ee494b77a79a6488d7be81e841bfc2c999a40f02655e48e185d2478150f25bca3ac68342882f7757b292be3e6bf2e7f5880bc57ce3ba4f04461422e7bab522735ac539e91e398089a9347236651eefa1aa4b7d86dc260de3af410ffb8938a977f682ee0dfe0a5f53db147548cc6e1cfdbba3d220c337efc9697b82e462ff9ce818fb450bf2e0e6613558079ad9916758a4773e92578b62760fd52338fdad927a9d6621783b8dbcac47987ad4b36264b9efdd6fda73e084880beeb49c020064f06b359202ec69cac3aef054ef9a7ef53f6540cfdcd59572d590b60cd44a03b032c1261e15d4f3945da92d2c49cbb02a10f201b4a789259d74dd492bafd9a88d110bf45a6b0aedf17beb3560378ea9588da0a189f2e8a26420c497202156082c005e15aadf98d7e0201966fe4149a9a5fe91b79d298bbf0bd8f88bcdc2e9bb51002b74a34ce65d5407837c6d8138fa1d78f43cd9db1bfd006250b8d69a1ceb1ebf38dfa7ebcd08527d4671705c9303b23aea3adb045c6fe3da5ba2089b96803a13161fb1f83d57be5311a7924d02a78a2d219a8df9d07b0a34920cf3e9e9790946ce8136084420dc4f150bdc3a5ce091de7ea9e7edfccb5b5c78218472072c2df21db5d4f5f58afbc672d356c9701e4916a35c27ae056700dfe6d9a805dddf9f78d0d4fc84d8a2f13076db341c39d9bbfbca41945c73b83b230657270f6564e52d6d3d15caff73aa10ee8a8178d6996880d958ecdece222c133617af7bf982bb41d9bf61adc9630119aa2b77c36218fa55512afe78eca6e51335cb97f61355c2d291593c9ca1b2edebc2e68d614e89e738802fc51bfc9076a01216470abc0a1bfbb24fb471657085d810d2911849efb4a86347e20d3a05f9d85f8e02d14eeeddfcf16d7d6333d3b7a66d09e0748e7e75b22eeb8c95a27c5c0467349f4cf2351ae4f25b739c235104a369143368f8c91aa3924c08122e52148b8025075572f65898516ca7e5f84c1f3670ed6a50e02419d995ffd6a83de554653205c7c9b90d065201e8581a8eebd83627fe747197453843f01f7744104b190105aa0431c9dc445961fbb9cfda306482bb812bda61da1ed50acd0799bb740ba60fa31693793b89d3c66b5bd93888e5db138b0749eaeeb2209", 0xecf}], 0x3)
execve(0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
setsockopt$inet6_MRT6_DEL_MFC(r0, 0x29, 0x27, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0))
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000080)={0x0, 0x0, 0x0, 0xffffffff00000001})
sysctl$hw(&(0x7f0000000080)={0x4, 0x11}, 0x6, 0x0, 0x0, 0x0, 0x0)
r0 = socket(0x2, 0x1, 0x0)
setsockopt(r0, 0x0, 0x1e, 0x0, 0x0)
fcntl$lock(0xffffffffffffff9c, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x3})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
sysctl$kern(0x0, 0x0, 0x0, 0x0, &(0x7f00000002c0)="d57e2db550579655610422b88a54e2ba2ae9305a5480090e41c2de54a93fd5b96662362db7b66b4664c03c235604ac538d787457313070d60b74d95b687a4ab04830bb591c372c610705a8291b8855934474ee5285061dc2da244c35c62d04a7b079446409c5cb5c82f51167844d6786b48ee94e6d2e7c9cd73b133ec1888503", 0x80)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
ioctl$VMM_IOC_WRITEREGS(0xffffffffffffffff, 0x82485608, &(0x7f00000000c0))
ioctl$FIOASYNC(r0, 0xc1084425, &(0x7f0000000240))
open$dir(&(0x7f0000000040)='./file0\x00', 0x202a8, 0x0)
unveil(&(0x7f0000000000)='.\x00', &(0x7f00000000c0)='c\x00')
execve(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000000)=ANY=[@ANYBLOB="aaaaaaaaaaaaaaaaaaaaaaaa86dd6091ee1100082b00fe8000000000000000000000000000aafe800000000000000000000000bb"])
r0 = openat$vmm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
setrlimit(0x0, &(0x7f0000000040)={0x1, 0x6})
ioctl$VMM_IOC_READREGS(r0, 0xc2485607, &(0x7f0000000040))
setuid(0xee01)
r0 = syz_open_pts()
fchflags(r0, 0x0)
mkdir(&(0x7f00000000c0)='./file0\x00', 0x40)
setreuid(0xee00, 0x0)
r0 = getuid()
mkdir(&(0x7f0000000000)='./file0/../file0/file0\x00', 0x0)
lchown(&(0x7f0000000100)='./file0/../file0\x00', r0, 0x0)
r1 = getuid()
setuid(r1)
rmdir(&(0x7f0000000200)='./file0/../file0/file0\x00')
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100050260000000000008000701000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f376336acf00b7804be78162e0000000000002b297be1aa5b23ed00f4c8b2ca3ebbc257699a1f132e27acb5d602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1f572c881ff7cc53c894303b22f310b404f36a0", 0x82, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="01080513600000000000ab000004", 0xe, 0x0, 0x0, 0x0)
sendto$unix(0xffffffffffffffff, &(0x7f00000000c0)="b100", 0x2, 0x0, 0x0, 0x0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x11, 0x3, 0x0)
sendto$unix(r0, &(0x7f00000000c0)="b100050300000000000000000101000000000000cea1fea7fef96ecfc73fd3357ae26caa0416fa4f373f00acf00b7804be781e4991f7c8df5f882b297be1ab5b23ed00f4c807000000000000001f132e27acbdd602000d7d026ba8af63ff37282902e4fd89720fd3872babfbb770c1ffff00000f90006ee01bc43eaeacc50000fa02000000000000020208a371a3f80004000000040000000100"/164, 0xb1, 0x0, 0x0, 0xfffffffffffffd41)
r0 = syz_open_pts()
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f00000001c0)={0x0, 0x0, 0x0, 0x0, "9b4f0e92fa5a2069641288a0606db0ccce620da4"})
fcntl$setstatus(r0, 0x4, 0xcc)
writev(r0, &(0x7f0000000600)=[{&(0x7f0000000240)="169c40dc4ad95948653a5516cc4ff48861f0877a1d154241b36a3958e4c156b3cf0fa834f0eb74db6dba8fed7c5ca183b9235a3c55d188441dcf36ab7d548f409872a94fba92b1e0f915389b82ebe2ad585a0c3fe1a4078b8a1cfc9a89925ce5061a0c3f5003cfef7ff533e8b21b55a6dfdfc6c164df59fe50961a822f95ad1ac046df1f535d11398c368e41eaa5a591352309e706", 0x95}, {&(0x7f0000000300)="1690aab0e1599cd92a998f418a6549a55e93e7737c27bc8714f812c8263e82db279d617e2f28f76a1aff206b051014b115e2d332928f2dd8736f55615339b06fc84404e4c18289e3c5afa7c585bfd81eceb4069c2cc05aea51e2639c05e1be90e84aa5ec", 0x64}, {&(0x7f0000000380)="900caca062acbc55b754232e91dafd807af16cb231e4e8b6213c050ba54c6f8932b522797778a294307e", 0x2a}, {&(0x7f00000003c0)="a0237775ce0889a503cc6aa6c218c4286b708582e1ce00f02992d4eb98950cb80e2bf99423be6a3377e39ff9864bd4f3e28c5702a6e9a0185b7a7e302bd0432436afff7b021f92ec68a1d96a3e5c0dfce37c26e236bf81e810d43ac09a2841c7fef8b6ab8782bb22804737f7d8c7bccf244a32433d3f64eaa29b7d24d5bdac6fff751853a0abe70d78f8d8f4ee152f1a2afaa116d37f8285ea29fa09802db952dbb6f8df7a22956bf94f5a413accbe3da89f78809329aa7812d70a0988b8484aa8419c4953b73b5e41cb2b9d444b85ccb2d996bc3817", 0xd6}, {&(0x7f00000004c0)="238e49a06ce3b1c4", 0x8}], 0x5)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x1, 0x1, 0xfffffffffffffffe, 0x7})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x28814, 0x0, 0x42)
r0 = socket(0x800000018, 0x3, 0x102)
r1 = getpgrp()
ktrace(&(0x7f0000000180)='./file0\x00', 0x2, 0x22, 0x0)
fcntl$lock(r0, 0x8, &(0x7f0000000080)={0x3, 0x1, 0x1f, 0x8, r1})
fcntl$getown(r0, 0x5)
r2 = msgget$private(0x0, 0x0)
msgrcv(r2, &(0x7f00000010c0)={0x0, ""/4089}, 0x1001, 0x3, 0x0)
msgsnd(r2, &(0x7f00000004c0)=ANY=[@ANYBLOB="0200000000000000c1873c269a5bfb1783f837a2f38ccb69f4c5dc9497985df45613b863bb462b6a724983da5c5e5b280af022c7430c1c2471a696fd3101e359157bfbc051705e009ecbbd28d95768c44d842099d011ab2cdf82013f892e4ae799bd5988eea138fe9f8243b79a3aa2c1a0f929d37b3e1f6a6495b72684f0adefdbc09a43525733f4febafa1acf7e0fd71e936ca9fd3b66301049a65f"], 0x1e, 0x0)
getsockopt$sock_cred(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000000)={0x0, <r3=>0x0}, &(0x7f0000000040)=0xc)
msgctl$IPC_SET(r2, 0x1, &(0x7f00000000c0)={{0x4, r3, 0x0, 0x0, 0x0, 0x29, 0x4}, 0x6f81, 0x3, 0x0, 0x0, 0x20000000000fc, 0x200c95e, 0xffffffffffffffff, 0xd672})
r4 = socket(0x18, 0x3, 0x0)
connect$unix(r4, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x13}, 0x1c)
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x5, &(0x7f0000000040), 0x0)
syz_emit_ethernet(0x36, &(0x7f00000000c0)={@broadcast, @local, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x3a, 0x0, @rand_addr}, @icmp=@timestamp_reply}}}})
preadv(0xffffffffffffffff, &(0x7f0000000440)=[{&(0x7f0000000240)=""/120, 0x78}, {&(0x7f0000000640)=""/4090, 0xffa}], 0x2, 0x1)
r0 = open(&(0x7f0000000100)='./file0\x00', 0x611, 0x0)
pwritev(r0, &(0x7f00000003c0), 0x273, 0x0)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x4, 0x10, r0, 0x0)
mlock(&(0x7f0000000000/0x2000)=nil, 0x2000)
ftruncate(r0, 0x0)
munlock(&(0x7f0000000000/0x2000)=nil, 0x2000)
pwritev(0xffffffffffffffff, &(0x7f0000001440)=[{&(0x7f0000000440)="fd3e3eebdb959c695e8a165c038a77d3fbe554d6428dbd9c548b17a2f1ed01b319edec395468f94cda1de4be8f3f8c02ab1dd9aaec1b9a01bef96f081b4281fa5dbadc171f7e08f6835dfe2270e6fa3d04b2dc2ca6fdafb6c89aa73c87e0fe2c0cdc474903f9aa0d59a8115b78109512c2d81587bdaf7467070d5e78554eb14199ecdbd7caafb3dd014c52aca5b00462fa4a37170c52b0db27474ad6e2e3a23123a69fbccbb18b7d5365239200ed5ad488f59327c5ecb7faf6fdbf8ef032fe74cf5cbfbde1c0770df1dd4b29dea6846a593300178d95c26ba6a4e1c006a2a026dc49267a6c7a60c6c61a9fde6f33696fc819728b0e8d2d160d816015c25ce80a2c6c7847ed862b2f17887f6aac36fdbe7913b81e93327b1fe2d00d7fc7f2f4bfac8431fdf01c12994bd1dc854fd0429f9d3ab7e71c6536eaedfdd874f87394bf7dd75a10f92e6ad9cc0bca7882c63f", 0x14f}], 0x1, 0x0)
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000080), 0x2, 0x0)
ioctl$VMM_IOC_READREGS(r0, 0xc0284414, &(0x7f0000000580))
sysctl$kern(&(0x7f0000000040)={0x1, 0x4}, 0x2, 0x0, 0x0, 0x0, 0x0)
socketpair(0x0, 0x0, 0x6, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x201, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000480)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000080)=[{0x80}, {0xc0}, {0x6, 0x0, 0x0, 0x100}]})
write(r0, &(0x7f00000001c0)="3c9ebbd555feff969613ba3e1fd0", 0xe)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000000c0)={0x3, &(0x7f0000000040)=[{0x6c}, {0x25}, {0x6406}]})
syz_emit_ethernet(0x2a, &(0x7f0000000000)={@local, @remote, [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @local={0xac, 0x14, 0x0}}, @udp={{0x0, 0x1, 0x8}}}}}})
getdents(0xffffffffffffffff, 0x0, 0x0)
ioctl$TIOCSETA(0xffffffffffffffff, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0x0, 0x0, "000000000000000100002000"})
setsockopt(0xffffffffffffffff, 0x0, 0x0, &(0x7f0000000140)="9513", 0x2)
r0 = socket(0x18, 0x1, 0x0)
close(r0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7, 0x3}, 0xfffffffffffffddb)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0x31, &(0x7f0000000140)="9513f3948292ae269282d455abab56620385feb9e9b3fda3181149ee114dd200a92ef2b465bbc11fcfdb71b72ce278fa941a79b7d45722a806d166b1bc4513bb05a76025938759964a53836bfd351fe9d2104012dc56fa2aa2786a7b4b39b7a51bf1baa51d3fb561c0ce637ef3c53f88edcc758d1e1eff1031571ebb9a54c1ea8426de968ad829470aa55d5b3eb81a62a35e0b41bc906838a88d756b2d17d0d7", 0xa0)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
shutdown(r1, 0x2)
sysctl$hw(&(0x7f0000000040)={0x6, 0x2}, 0x2, 0x0, 0x0, &(0x7f00000003c0), 0x0)
r0 = socket(0x2, 0x1, 0x0)
r1 = open(&(0x7f0000000040)='./file0\x00', 0x60e, 0x0)
close(r0)
pwritev(r1, &(0x7f0000000240)=[{&(0x7f0000000180)="e5c57b", 0x3}], 0x1, 0x0)
pwritev(r1, &(0x7f0000000140)=[{&(0x7f00000000c0)="4402", 0x2}], 0x1, 0x0)
r2 = socket(0x2, 0x2, 0x0)
connect$unix(r2, &(0x7f0000000000)=ANY=[@ANYBLOB="82029797ca"], 0x10)
r3 = socket(0x10000000002, 0x2, 0x0)
r4 = dup2(r2, r3)
setsockopt$sock_int(r4, 0xffff, 0x1023, &(0x7f00000000c0)=0x3, 0x4)
mmap(&(0x7f0000000000/0x13000)=nil, 0x13000, 0x5, 0x10, r1, 0x0)
connect(r0, &(0x7f0000000000)=@un=@file={0x0, './file0/file0\x00'}, 0x10)
sysctl$net_inet_ip(&(0x7f0000000000)={0x4, 0x2, 0x0, 0x5}, 0x4, &(0x7f0000000100)="e7868f76", &(0x7f0000000040)=0x4, &(0x7f0000000200), 0x0)
syz_emit_ethernet(0xe, &(0x7f0000000040)={@local, @remote})
syz_emit_ethernet(0x3e, &(0x7f00000000c0)={@local, @random="cc5b8d12c4b5", [], {@ipv4={0x800, {{0x5, 0x4, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x1, 0x0, @rand_addr, @remote={0xac, 0x14, 0x0}}, @icmp=@source_quench={0x11, 0x0, 0x0, 0x0, {0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @broadcast, @rand_addr}}}}}})
r0 = socket$inet(0x2, 0x3, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x1, &(0x7f0000000000)="2606ef67f743de166e61398c3e652587176ae49204dd1d002884e6c5", 0x1c)
getsockopt$inet_opts(r0, 0x0, 0x1, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x1, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000200)={0x3, &(0x7f0000000040)=[{0x14}, {0x3}, {0x6, 0x0, 0x0, 0x5b0d}]})
write(r0, &(0x7f00000001c0)="c2641119364565fb000000000000", 0xe)
open(&(0x7f0000000480)='./file0\x00', 0x80000000000206, 0x0)
r0 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
r1 = open$dir(&(0x7f0000001240)='./file0\x00', 0x4000040000000242, 0x0)
lseek(r1, 0x0, 0x40fff)
r2 = open$dir(&(0x7f0000000000)='./file0\x00', 0x2, 0x0)
r3 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r2, r3)
writev(r3, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
r4 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
r5 = dup2(r1, r4)
writev(r1, &(0x7f0000000040)=[{&(0x7f00000001c0)="fe", 0x1}], 0x1)
pread(r5, &(0x7f00000000c0)='\x00', 0xffffff78, 0xa83)
writev(r0, &(0x7f0000000340)=[{&(0x7f0000000080), 0x6487}], 0x10000000000002d1)
r6 = open$dir(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
dup2(r0, r6)
writev(r6, &(0x7f0000000340)=[{&(0x7f0000000000), 0x2cfea}], 0x1000000000000013)
minherit(&(0x7f0000ffc000/0x1000)=nil, 0x1000, 0x0)
madvise(&(0x7f0000ffb000/0x4000)=nil, 0x4000, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000000)={0x3, &(0x7f00000001c0)=[{0x4}, {0x60}, {0x4406}]})
syz_emit_ethernet(0xe, &(0x7f0000000380)={@empty, @random="cc812211ca64"})
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000040), 0x2, 0x0)
pwritev(r0, &(0x7f0000000600)=[{&(0x7f0000000140)='B', 0x1}], 0x1, 0x0)
execve(0x0, 0x0, 0x0)
shmget$private(0x0, 0x2000, 0x0, &(0x7f0000ffd000/0x2000)=nil)
sysctl$kern(&(0x7f00000001c0)={0x1, 0x37}, 0x3, 0x0, 0x0, &(0x7f0000000280)="837bfc0a14df504a6b218982e24e3cf4dee6e52a8a014a6c16633bee1ac055430e01054a90d1d410588c8a66ccb53af62ebe8531ad235e2b2c98cc48d394e64b989c03741d5f30d94b49c6a08d1f7192bfad2ca4dc516de97544ac22388ac77dc312a83defdffc079d44c237b78e3f877a45863355108d49b76f4a9d501277747c3a092287b6319eb1a799925a5ba87c7ff9c3e4e2989fb25a39066137d82d1655c30bf8615ba2bc32fc5c26191caf9abe75195d5cc6955878b18dbf8279e5998aa5e9fbbfd91752bcd88a11d8454b25ba4907d5676b6fe9cb54453a6f61e46492892581d21518a1c0cb708d248b247261957bdd49a60abc9db96c1b93eebf9f9494afab78349c765405e17cdc0992d9111e0bd650da", 0xf)
syz_emit_ethernet(0x3e, &(0x7f0000000000)={@local, @broadcast, [], {@ipv6={0x86dd, {0x0, 0x6, "c1b314", 0x8, 0x0, 0x0, @loopback={0x5}, @remote={0xfe, 0x80, '\x00', 0x0}, {[@routing={0x2b}]}}}}})
shmdt(0x0)
r0 = openat$wsmuxmouse(0xffffffffffffff9c, &(0x7f0000000840), 0x1, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
poll(&(0x7f00000000c0)=[{r1, 0x1}], 0x1, 0x0)
poll(&(0x7f0000000000)=[{r0, 0x40}], 0x1, 0x0)
poll(&(0x7f00000000c0)=[{}], 0x1, 0x0)
r0 = socket(0x18, 0x1, 0x0)
setsockopt(r0, 0x29, 0x39, &(0x7f00000000c0), 0x4)
msgrcv(0xffffffffffffffff, &(0x7f0000000300)=ANY=[@ANYRESHEX, @ANYRESHEX=0x0, @ANYRES64, @ANYRES16], 0x2e, 0x3, 0x1000)
r0 = msgget$private(0x0, 0x258)
msgrcv(r0, &(0x7f00000001c0)=ANY=[@ANYBLOB="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c319e0100"/254], 0xfe, 0x2, 0x800)
sysctl$vfs_ffs(&(0x7f0000000000)={0x4, 0x1, 0x1}, 0x3, 0x0, 0x0, 0x0, 0x0)
mlock(&(0x7f0000ffb000/0x2000)=nil, 0x2000)
r1 = msgget$private(0x0, 0x2)
msgsnd(r1, &(0x7f00000006c0)=ANY=[@ANYBLOB="00e100000000000095d2f332de84eb6e161d390811e4d09d73a8cb658af7f3b728863e79f6af43b26cac8093f76a70490cc3a6d6eb1d8eccc70dc1f45ec2cf4adc4a9976bab26944e9ce9e441c1c3b83152678575300dcd494f235d431daaf23e7c78aa3ed2c9e5e9d5edcd8fc685f3d6739683c8a9c74a48e3d339a1f6ee8947afb3390499f083434c3de124b0bd35194c30a41bb9dfc7f7838419eff84c3621e6099080d48b82efc569ac7eba639f9d540df088ac865a0b72f77cdb69820244bb333339b37a9a49713ca33add858558f67ca69ce51cfc7178c3eddc470a2f22a0b36a7f779c17d4d61a37514802c6612f71f3564558c3f21d6a7"], 0x70, 0x800)
msgsnd(r1, &(0x7f0000000a00)=ANY=[@ANYBLOB="0300000000000000c5e94674dbd3604cde5c3557da6461341bbefbac102b3895e0140869a1845fe194ac26f999f83cbdb241ab396b30c0d5d524ba717d8e5c5e2f873559783e450b4d87abe348cb20ebf83ab0ce789d687b03d617130b6ac1571170e268ce1d0b4bbd8bf45c5fd340a61305979b0bf685e45f57392649d8248976549ce08056e03c959080cbf5e012d6635b3b58174bd552e9c513f2acc71bb2c9788a895fc07478c415c6aa3db3cd6b47f2e874c2c9d63886179802e5606fb276950cca74cf527bf968ceba0e8125af4bd5000000000000000000000000000000c6bd6dd52135696ea9f082ad0938b93df3eca8aad08910b7e8ee4403738cb1dbb0c104f09ad91582087e0eef0e43c90e92357a3ef5407833d2a6ec79c459488669a3cd6a31aa014aac155a5bd37af6377d1ad2cac8b2e1796506fade88f319a97d9a40c749f3a53994efba60bcc277b0a17c6e5bee5a8698f73b55972dc1c52a0b69bbbf91228a81ebf27c3732ced1949ff053da397baae03e894a58defd824bac1ac4ea092b2d476d374f122423d47361c20a5575804aeaaf479fe73daebf5309cb45fa4eb8bfe4165315a5f49b632e69ac567667569d953be148"], 0x6d, 0x0)
msgrcv(r1, &(0x7f0000000580)={0x0, ""/18}, 0x1a, 0x1, 0x3000)
msgsnd(r1, &(0x7f00000007c0)=ANY=[@ANYBLOB="0300000000040000db4436958b193c67b6ce0c093bb0bbf3b3245230033f58052ebd418aa6e7161916fd47a14a3a74259ca57335615dd09efc3ddf520906bf778f89cf9aa9da8fbd888acf1d82015453ebaf5328755e3b05d90ba5ba0c00faff96c620f0866300000000"], 0xb, 0x800)
msgctl$IPC_STAT(r1, 0x2, &(0x7f0000000680)=""/9)
msgctl$IPC_RMID(r1, 0x0)
r2 = msgget$private(0x0, 0x400)
r3 = socket(0x18, 0x3, 0x1f)
munlock(&(0x7f0000ffc000/0x1000)=nil, 0x1000)
setsockopt(r3, 0x29, 0xb, &(0x7f0000000040)='\x00\x00\x00\x00', 0x4)
msgrcv(r2, &(0x7f00000002c0)=ANY=[@ANYRESDEC=r3], 0x93, 0x3, 0x0)
msgget$private(0x0, 0x106)
msgsnd(r2, &(0x7f00000003c0)={0x1, "54a7ac21511a97423fd6c740a91e5e4a5101adef0e662e2828341e6155ced1e9586b84e42a9d46e68ef435145e304f9177f3c3b9d47896c87a9045d5709c3b1f6dca4026fe560dc7ddd987f7cce4d4f5530f7b"}, 0x5b, 0x800)
msgget$private(0x0, 0x100)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000006c0)={<r0=>0xffffffffffffffff})
sendmmsg(r0, &(0x7f00000009c0)={0x0}, 0xffffffffffffffeb, 0x0)
syz_emit_ethernet(0x3e, &(0x7f0000000100)={@local, @random, [], {@ipv6={0x86dd, {0x0, 0x6, "50a8b5", 0x8, 0x0, 0x0, @local={0xfe, 0x80, '\x00', 0x0}, @local={0xfe, 0x80, '\x00', 0x0}, {[@fragment={0x3c}]}}}}})
mknod(&(0x7f00000000c0)='./bus\x00', 0x80002007, 0xffffffffffff2822)
r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x0, 0x0)
r2 = open(&(0x7f0000000040)='./file0\x00', 0x70e, 0x0)
pwritev(r2, &(0x7f0000000500)=[{&(0x7f0000000140)="ad", 0x1}], 0x1, 0x0)
mmap(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5, 0x10, r2, 0x0)
r3 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8004745d, &(0x7f0000000100))
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet6(0x18, 0x3, 0x0)
r1 = socket$inet6(0x18, 0x3, 0x0)
r2 = fcntl$dupfd(r0, 0x0, r1)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x6e)
poll(&(0x7f0000000200), 0x2000000000000031, 0x0)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x8001, 0x80, 0x0, 0x100000001, 0x0, 0x0, 0x8]}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r3 = socket(0x18, 0x3, 0x0)
r4 = fcntl$dupfd(r3, 0x0, r2)
ioctl$TIOCFLUSH(r4, 0x8080691a, &(0x7f0000000300))
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000040)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
bind(r0, &(0x7f0000000240)=@un=@file={0xd80f49edce43012d, './file0\x00'}, 0xa)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000040)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff})
r3 = dup2(r2, r1)
connect$unix(r3, &(0x7f0000000040)=@file={0x1bcfa69870ae01e4, './file0\x00'}, 0xa)
sysctl$vm(&(0x7f0000000000)={0x2, 0xc}, 0x2, 0x0, 0x0, 0x0, 0x0)
stat(&(0x7f0000000080)='./file0/../file0\x00', 0x0)
sysctl$kern(&(0x7f0000000080)={0x1, 0x47}, 0x3, &(0x7f0000000000)="c28052df6cbde12abb63fc1e62050144115cf918248d00000000f501ff323b85f6c1040000000000000000832d591aef46b9683eef9b8cd456e58f7050dca083ead8760db8b84b0cbd18f765689155266a7ba70e1e407c2f62d761c19e33e4695ceed2", &(0x7f00000002c0)=0x16, 0x0, 0xfd81)
r0 = kqueue()
kevent(r0, &(0x7f0000000280)=[{}, {}, {}, {}, {{}, 0x0, 0x0, 0x2}], 0x14, &(0x7f00000001c0), 0x7, 0x0)
kevent(0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000340)={0x0, 0x6})
kevent(r0, &(0x7f0000000000), 0x899, &(0x7f0000000400), 0x80, 0x0)
r1 = openat$pf(0xffffffffffffff9c, &(0x7f0000000040), 0x801, 0x0)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x6)
ioctl$FIOASYNC(r1, 0xcd60441a, &(0x7f0000000240)=0x4)
r0 = syz_open_pts()
fcntl$lock(r0, 0x10000000000009, &(0x7f0000000000)={0x0, 0x2, 0x0, 0x2000300000000})
flock(r0, 0x1)
fcntl$lock(r0, 0x8, &(0x7f00000000c0)={0x0, 0x0, 0x10000000000, 0x20002fffffffc})
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000140)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendmsg$unix(r1, &(0x7f0000000400)={0x0, 0x0, 0x0, 0x0, &(0x7f00000003c0)=ANY=[@ANYBLOB="28000000ffff000001"], 0x28}, 0x0)
sendto(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f0000000880)={0x0, 0x0, &(0x7f0000000780)=[{&(0x7f0000000600)=""/164, 0xa4}], 0x1, &(0x7f00000007c0)=""/141, 0x8d}, 0x40)
syz_emit_ethernet(0x46, &(0x7f0000000840)={@local, @random="24e184effdc4", [], {@ipv4={0x800, {{0x9, 0x4, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, @multicast2, @rand_addr, {[@noop, @timestamp={0x44, 0xc, 0x0, 0x0, 0x0, [{[@rand_addr]}]}]}}, @tcp={{0x3, 0x1, 0x41424344, 0x41424344, 0x0, 0x0, 0x5}}}}}})
ioctl$WSMUXIO_INJECTEVENT(0xffffffffffffffff, 0x80185760, &(0x7f0000000000)={0x0, 0x0, {0x0, 0x10000000000001}})
ioctl$TIOCSETA(0xffffffffffffff9c, 0x802c7414, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "00000000000000000000f2ffffffffffffff4000"})
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x1, 0x0)
r1 = socket$inet6(0x18, 0x3, 0xfe)
setsockopt$inet6_MRT6_ADD_MIF(r1, 0x29, 0x1a, &(0x7f0000000040), 0x4)
close(r0)
r2 = socket(0x18, 0x3, 0x0)
close(r2)
dup(r1)
setsockopt(r2, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
sendto$unix(r0, &(0x7f00000001c0)="fba3d7e27d0cb909b790a827b3a898930b5aae65", 0x14, 0x0, 0x0, 0x0)
sysctl$hw(&(0x7f0000000000)={0x6, 0x9}, 0x2, 0x0, 0x0, &(0x7f0000000180), 0x0)
r0 = syz_open_pts()
syz_open_pts()
syz_open_pts()
r1 = openat$diskmap(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$DIOCMAP(r1, 0xc0106477, &(0x7f0000000080)={&(0x7f0000000040)='./file0\x00', r0})
open(&(0x7f0000000100)='./file0\x00', 0x461e, 0x0)
r0 = getpid()
ktrace(&(0x7f0000000300)='./file0\x00', 0x0, 0xfcfc96ac1f78739e, r0)
r1 = kqueue()
kevent(r1, 0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)={0x0, 0x40000000000})
r0 = syz_open_pts()
fcntl$lock(r0, 0x9, &(0x7f0000000100)={0x0, 0x0, 0x400000000000c7e7, 0x100000000})
fcntl$lock(r0, 0x9, &(0x7f00000000c0)={0x0, 0x0, 0x3, 0x100000000})
r0 = socket(0x18, 0x2, 0x0)
setsockopt(r0, 0x1000000000029, 0x9, &(0x7f0000000180)="01000000", 0x4)
sendmsg$unix(r0, &(0x7f0000001700)={&(0x7f0000000600)=ANY=[@ANYBLOB="fb182e2b666902e3ff011ff3bba9a85e19f514365958431532"], 0x1c, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000100), 0x0, 0x0)
ioctl$BIOCSBLEN(r0, 0xc0044266, &(0x7f00000001c0)=0xfffffff7)
r0 = socket$inet(0x2, 0x2, 0x0)
setsockopt$inet_opts(r0, 0x0, 0x200000000000c, &(0x7f0000000140)="e00000000000000001000000", 0xc)
r1 = dup(r0)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = dup2(r1, r1)
setsockopt$inet_opts(r2, 0x0, 0xd, &(0x7f0000000140)="e800f8000000000000000000", 0xc)
setrlimit(0x8, &(0x7f0000000040)={0x30, 0x95})
r0 = syz_open_pts()
close(r0)
r1 = syz_open_pts()
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f0000000080)={0x0, 0x0, 0x4, 0x0, "ea3899870083a74dfd9cd3e5de388a76eaba52e8"})
write(r0, &(0x7f0000000000)="abd105ff394fd9e025ec0d", 0xff52)
writev(r0, &(0x7f00000001c0)=[{&(0x7f0000000200)="d8", 0x1}], 0x1)
readv(r1, &(0x7f0000000100)=[{&(0x7f00000000c0)=""/61, 0x3d}], 0x1)
ioctl$TIOCFLUSH(r0, 0x80047410, &(0x7f0000000680))
r0 = kqueue()
r1 = syz_open_pts()
syz_open_pts()
kevent(r0, &(0x7f0000000380)=[{{r1}, 0xfffffffffffffffe, 0x71}], 0x5, 0x0, 0x0, 0x0)
syz_open_pts()
kevent(r0, 0x0, 0x0, &(0x7f00000001c0), 0x9, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f00000003c0)={0xffffffffffffffff, <r0=>0xffffffffffffffff})
getsockopt$sock_cred(r0, 0xffff, 0x1022, &(0x7f0000000380)={0x0, <r1=>0x0, <r2=>0x0}, &(0x7f0000000340)=0xc)
setregid(0x0, r2)
r3 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x48a, 0x0)
setregid(r2, r2)
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000080))
dup2(0xffffffffffffffff, 0xffffffffffffffff)
ioctl$FIOASYNC(0xffffffffffffffff, 0x8004667d, &(0x7f0000000200))
sendmsg(0xffffffffffffff9c, &(0x7f0000000040)={0x0, 0x0, &(0x7f0000000080)=[{&(0x7f00000002c0)="0400000032da2edb097f3762855fd288fd96e3df953d8910a08e31d9629054892ecd8ba025bc0638221f19047585bb7b782a03068b000600032a0be62245c2b9784785f08b2b708237c775fd4f8e9c1304d35397722356edc157b0326bdf84e589d58abd35d049253cbd6a91412dd29fa61ef723fca8d813a17502", 0x7b}], 0x1, 0x0}, 0x0)
r4 = semget$private(0x0, 0x5, 0x28)
semop(r4, &(0x7f0000000100)=[{0x3, 0x5022, 0x1000}, {0x1, 0x0, 0x1000}], 0x2)
getsockopt$SO_PEERCRED(0xffffffffffffff9c, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r5=>0x0}, 0xc)
getsockopt$sock_cred(0xffffffffffffffff, 0xffff, 0x1022, &(0x7f0000000240)={0x0, 0x0, <r6=>0x0}, &(0x7f00000000c0)=0xc)
r7 = getegid()
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f00000002c0)={{0xc, r5, r6, 0xffffffffffffffff, r7, 0xb0, 0x5}, 0x1, 0x2, 0x10000000082})
r8 = getgid()
getgroups(0x2, &(0x7f0000000000)=[r2, <r9=>r2])
semctl$IPC_SET(r4, 0x0, 0x1, &(0x7f0000000180)={{0xfff, r1, r8, r1, r9, 0x0, 0x7}, 0x100000001, 0xffffffffffff0000, 0xff})
ioctl$FIOASYNC(r3, 0xc1084425, &(0x7f0000000240))
setreuid(0xee00, 0x0)
r0 = getuid()
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000002c0)={0x2, &(0x7f0000000040)=[{0x0, 0x25}, {0x1}]})
setreuid(0xee00, r0)
sysctl$kern(&(0x7f0000000040)={0x1, 0x4e}, 0x3, &(0x7f0000000600)="e0439f2cb7597bca1e9c3dfa7739c4cb047318ef84d3b2b7b4636b04000000950df7be36c92c1d8c0b026790499b2834e09fff4dcb76d4481fb1506973d9737f60f232aa475f08188a07687fc8a9f3db1fe4a31d4d19767a5c7728fe4a7223214ce160d1928b8c0d4b5271fbeed4edad55aba9e8be0100ee7f539107e7859fa812032098e8897ad86085c68daa01000000b47bf8d16d2338a640a082a4d83fee23793df4f92d156777b0421b251fd463962a5d46c9822ba3fca413b9dcc28d3d43258bbe003ab56f684c4ab2e7813a2189d655c1802e9dff48697bb00dfc7727bf329ca064112ec5450e11ba3942d08c639cd3446ccb4fe9eae4c3c85944314228c97f7640c1b470592251a53ec2000039825901d6e0b1d6581b699f9a8372a5fcd13cd18ed4a74ce30cd70439bbdfb1182208cebb1c1f4ef1a5ff78573855847d3e7bdfac61c51cc05ea321cf01c0e5cc69e0a58b54fcf5eebecb0d24b5db666e112e963c41cb5647915457e2625a7b36dc57694f6df3b7afae477e3acbaa5bcc6f6ea9faaf0ba6f08f4bc5ea8d041d7db1710d6248849bf06eda72df9e4788a4d12a1f0f3d26ef523cde90f866fa56a4bf913cac276cea25cfe7d2ff076e1d85296a04e9c77dcc72145e6b650b63c921e29912c4129791ff6a0810190f23c7b7b7448589af4ffae3d662a26e327077e8ed746e0b5d2148523ba92e14f3d4c4277ef90e11425f00d78518e12e598367235818e27896c7ad782ab2c6175ccaf0014f521ff0450ba55a6989e74f726a8049d27c7280abb58c3bf8528fced702541142775ac4eb9275fdb2a9d87e52c2e7a4ae000000000000000000000000000000000000e14ff6d9469957998d798d9cb6fe8811c5aee539f372d5c71a733f5adc6aa3d0e188fbda61a365bb92f220793fa7f0fe4747c6f74b53d327acb084b4bb8808c31fff415a508cab5ed1c77ce7961be0223a8cf5802d790c6ae85e79a2b8495747dc162afb2f930dd7ed788a3c0f8584eb509f24cef5612da4c21a5edf0e53ef21588be13c74929f50126a86a89decb412eb3e745d49d774138de2", &(0x7f0000000340)=0x25, 0x0, 0x0)
sysctl$kern(&(0x7f00000000c0)={0x1, 0x51}, 0x2, &(0x7f0000000240)="8a0332c90d6bddaa4accd608ee3424636a86790d96151454034db8716050a27969899d55fa2c069642cb16236cbfe0ede0fe1e29b94d2433f04cc9d5352c6705a67c90407e2fc19fa7236edf513b2c4316ef45e7", &(0x7f0000000100)=0x54, &(0x7f00000003c0), 0x0)
r1 = semget$private(0x0, 0x4000000009, 0x282)
semop(r1, &(0x7f0000000380)=[{0x1, 0xffff, 0x1000}, {0x7, 0x2, 0x1800}, {0x2, 0x4, 0x1000}, {0x3, 0x42, 0x1800}], 0x4)
semop(r1, &(0x7f0000000380), 0x1d)
semop(r1, &(0x7f0000000380)=[{0x4, 0x1000, 0x3000}, {0x2, 0x6, 0x800}, {0x0, 0x0, 0x800}, {0x3, 0x3ff}, {0x1, 0x2e2}, {0x3, 0x7d11, 0x1800}, {0x2, 0xfffe, 0x800}], 0x7)
semctl$SETVAL(r1, 0x2, 0x8, &(0x7f00000003c0)=0x7)
semctl$GETZCNT(r1, 0x0, 0x7, &(0x7f00000004c0)=""/213)
semctl$GETZCNT(r1, 0x0, 0x7, &(0x7f0000000040)=""/60)
socketpair$unix(0x1, 0x1, 0x0, &(0x7f0000000300))
socketpair$unix(0x1, 0x1, 0x0, &(0x7f00000000c0)={<r2=>0xffffffffffffffff})
getsockopt$sock_cred(r2, 0xffff, 0x1022, &(0x7f0000000140)={0x0, <r3=>0x0, <r4=>0x0}, &(0x7f0000000200)=0xc)
r5 = getuid()
r6 = getgid()
semctl$IPC_SET(r1, 0x0, 0x1, &(0x7f0000000180)={{0xfffffff7, r3, r4, r5, r6, 0xc2, 0x4}, 0x2, 0x7c42, 0xa23c})
semop(r1, &(0x7f0000000000)=[{0x2}, {0x6, 0x101}, {0x0, 0x0, 0x1000}, {0x3, 0x20, 0x800}, {0x3, 0x8, 0x800}, {0x4, 0x7, 0x1800}], 0x6)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000380)={0x3, &(0x7f00000000c0)=[{0x84}, {0x24}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r0, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
r0 = open(&(0x7f0000000080)='./file0\x00', 0x612, 0x0)
writev(r0, &(0x7f0000001580)=[{&(0x7f0000000140)="4d5b4b3898d5b140186840a77150b7f220a52300de16ee262ba9718a0582b4d2b36c7797605fd4627a68d7231bbe73bffa34a89741bd79681be3eac113e6a1ae3be444d5d28b9d63b2e152187a875a73", 0x50}], 0x1)
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x5, 0x10, r0, 0x0)
setrlimit(0x6, &(0x7f0000000000))
setrlimit(0x3, &(0x7f0000000040))
mmap(&(0x7f0000000000/0x3000)=nil, 0x3000, 0x0, 0x10, r0, 0x0)
mlockall(0x1)
r0 = socket$unix(0x1, 0x2, 0x0)
setsockopt$sock_cred(r0, 0xffff, 0x1021, 0x0, 0x0)
r0 = kqueue()
pipe2(&(0x7f0000001e40)={<r1=>0xffffffffffffffff, <r2=>0xffffffffffffffff}, 0x0)
kevent(0xffffffffffffffff, &(0x7f0000000240)=[{{r0}, 0xffffffffffffffff, 0x1}], 0x0, &(0x7f00000003c0)=[{{r1}, 0xffffffffffffffff, 0x2c}], 0x0, 0x0)
kevent(r0, &(0x7f0000000100)=[{{r1}, 0xffffffffffffffff, 0x79}], 0x90, 0x0, 0x0, 0x0)
dup2(r1, r2)
kevent(r0, &(0x7f0000000000), 0x400, 0x0, 0x7fffffff, 0x0)
r0 = openat$speaker(0xffffffffffffff9c, &(0x7f0000000000), 0x2, 0x0)
writev(r0, &(0x7f0000001200)=[{&(0x7f0000000080)="1ad3544d16e23bca357e78e004729688783f04333351085c614f9cf6e7bf1db47592695c0f9834f46cbb07c13444aa5b6f2fc69a1a57b9d7497f19afb4050cbece0f7d2fe5f1eac4f6213f8c6582ddd759faaefeb684ada637af50ef7ef47933b508e48a1f8e89bf6f1ab96d8bbff42f840b517e089518c5937c29f00470a81430f518a7b3d7f5b1784cc239f05c39b02a36d0d556c40f5acfe68a0f81a5789f705c5d7d768d5e12d083f81d26ae03d8f285a90b610a6aff1de5387c195d46e1233eab356fbff35f2267950db0a7345065fb703dffa5b8b0031c4c5b9be712b59985ce9e874036fcfc0821cc0f5cc66090dbe3d8f90e31f7a07bece3df4a08ed021573cedc43da7d6b50d753b1881eb33ff7f141247e647ef7096d43b9587d58ddf2a34274d481ec36bb889993cc49930331f28d796cfa72607dfe2e833f1ebc894999f59a6286fe4603b428064be3773202652d", 0x154}], 0x1)
execve(0x0, 0x0, 0x0)
r0 = socket$inet(0x2, 0x3, 0x0)
shutdown(r0, 0x2)
mprotect(&(0x7f0000ffb000/0x3000)=nil, 0x3000, 0x6)
mknod(&(0x7f0000000080)='./file1\x00', 0x1000, 0x0)
acct(&(0x7f0000000300)='./file1\x00')
open$dir(&(0x7f0000000040)='./file1\x00', 0x2, 0x0)
sysctl$net_inet_tcp(&(0x7f0000000000)={0x4, 0x2, 0x6, 0x17}, 0x4, 0x0, 0x0, 0x0, 0x0)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
socketpair$unix(0x1, 0x2, 0x0, &(0x7f0000000180)={<r2=>0xffffffffffffffff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000080)={0x0, 0x0, 0x0, 0x0, &(0x7f0000000000)=ANY=[@ANYBLOB="89000000ffff000001"], 0x9}, 0x0)
sendmsg(r2, &(0x7f0000000380)={0x0, 0x32c, 0x0, 0x0, &(0x7f0000000000), 0x90}, 0x0)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x4, &(0x7f0000000080)=[{0x0, 0x2, 0x1, 0xff}, {0xff81, 0x1, 0x0, 0x6}, {0x7, 0x4, 0x7, 0x7}, {0x83e, 0x3, 0x4, 0x1}]})
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000040), 0x462, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000001c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000000)={0x3, &(0x7f0000000180)=[{0x64, 0x0, 0x0, 0x1}, {0x2}, {0x4000006, 0x0, 0x0, 0xfffffffe}]})
pwrite(r0, &(0x7f0000000340)="b5e072ff2bd288e7187afe845154bd1505af83c11a8e62de5ae460b9d32929cb4db982d4b2e6018eea2672dc90f53481a4c5b22f54e14eb8747a54ec7bcd3663ad21c3d6ae87c002000000000000005a340a8cbd0dd36a829f83fc56b0db92e277f772806b872754ab0060", 0x6b, 0x0)
r1 = socket(0x2, 0x1, 0x0)
r2 = dup(r1)
r3 = socket(0x400000000018, 0x3, 0x800000000000003a)
ioctl$WSMUXIO_REMOVE_DEVICE(0xffffffffffffffff, 0x80085762, &(0x7f0000000040)={0x1})
setsockopt(r3, 0x29, 0x6c, &(0x7f0000000040), 0x4)
setsockopt$inet6_MRT6_ADD_MIF(r3, 0x29, 0x66, &(0x7f00000000c0)={0x3, 0x0, 0x0, 0x1}, 0xc)
setsockopt$inet6_MRT6_ADD_MIF(r3, 0x29, 0x67, &(0x7f0000000140), 0xc)
ioctl$TIOCFLUSH(r2, 0xc0106924, &(0x7f00000000c0)=0x2c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
seteuid(0xffffffffffffffff)
socket(0x0, 0x5, 0x7)
r0 = socket$inet6(0x1e, 0x3, 0x0)
writev(r0, &(0x7f0000001400)=[{&(0x7f0000000080)="72ae151e59dcb7f69d76d40478fcfc1aafde2026d2efb6d827ffafb5b6e028bd866c2ab754938982d5d8d49de3f53ec908fc8755ad4638", 0x37}, {&(0x7f00000000c0)="af10699c984161cbbf160d464abbd1e390fe3f58914f6cd54f4714b3529d6a3bbbc2851c472cb576af349cae59ff37af8769b6481393f07007a8566f3084028dca41bc631e49ebcd3ebf61f8c7f6f6", 0x4f}, {&(0x7f0000000140)="7fd93f72c9ef06da5ee2878e7671007e27645250f7ebecce23753673210a2fa685aad35131fca43e37ba2aea01bfac87e668660d279474f881429ebb83e3a24e87f5a247", 0x44}, {&(0x7f00000001c0)="fe0fe8ac6df9f462500f308a7138e308b582fe09b95913f1a0bc7648c51eb13c7eba64d6bf60020be2cfe70a8dc74fd66b97557b80222eb8c70b7f08b299b3cdfdbb00a5678d2ee8ea647d21d15ef0995fe4319716a0a5", 0x57}, {&(0x7f0000000240)="869d60203fefc96ef2184c8ac1f04bf4c08a5409997d2630e924de8f5c8d617b010d9e4c528d7684891d02e8b39972d1f4f22a6033815ea43f56964bdb9a1921a1402c164d62cf0c3868e2710e56fa55256c8cd6a361105c5f3093d2924d04177ea832d6bdbde51dbdff406ed9d063c1cbe342e595766463c397b3ffbc14c8ae86fca966abdb441b0c3109e80c57cfc9df09bda2cb514e83bb1edb", 0x9b}, {&(0x7f0000000300)="214b0bfc6bcc778f690297d0605a9ac99642f735b28063b9a42a8a574ff15b1027959692c790b8f24272ebe1fbf23fc975b39d13dd815073c1b97429197d7c62b3f20e74b02fa6de93b4624c70a8b48f751d61e49e60a0c3dbf4efee19c11c6b51332e65c0a64dea5bb59f29475e1ba543c9ef575e365047352a7dfb6600907d89b5975ad15407d529215f7ca0a7555e8c9dc7387f4c6270e7523b7fbb78bdb7126ece0ea108815a534e5b187d96fb9050fa495ec6994104b66395bb3dbb46ac3af094ef75895ef80e5695ba52", 0xcd}, {&(0x7f0000000400)="b7a1afc009fe96d3ca2b6779d023ffd70fbc521a4f1835dc8b17b56101efba7e9ae4f6b2b11f15e077f3750c69b86f750e17dd73f92daa73a8beb4402069dc2889846955e8380a36f162c98dc79b22df496f816affa2863e1786b118cdc0f2d9378be8e789b37d8716b7a95f030521e64f2086ef44d9258eab7be45e2bd82903491b581c9b44a49f5fd6521ed0534a02166bc0a9b05eb89ed3c3a15ce80e246d7a82be9f9da3ae026c5abe58825b50bb61b912b5760f73cf019a003d5c680b734d70bf2521212a726c7f9ddf9ecf601d6a0ba579d23319602be7f7a30dea61a71c8f8f9f6d92a2e13e85e571b8229ec32cd697a97e06890b73a87ce145ffac91a0fa7ae9f80e068529f77b744aaf70e31eb8ae4d0d5b8c0c5853496a6184bd71a903ae7d7a839a9381cb6e4caddc89d49f3c2523c9fe82e5ecaf5b577d780e6acb33846a4a9d8ebad008ee4812b2b7e13f082c9c2ba5f6f053ae610afca06c85a01452c4509419eaa2db1e65188ad4ba87bd87914d358ec1a222e9b2470f5df0af2e2a43920f2a73126699bf7112a64a4f5418c3e912d30ea1f7a7b3786a1de0440ea1732914175dc85e1bf04d431e4b03111485305696f174583cba4bc224c66effccef36c782348875c41abad8cbbe665be47054c700be26cfcc0d88535fd63accf043da72f7f5541bd610652a56b2db4af54f45ba7cfd96ddae2dd0d3c8e18d1c2ef5e0cf4dbf35f1a4e2e1d67c354737544c3e00e330a688731b1b9f0efd81e5f02e9a6fcc735e94d305e31f3bcfdf77125086fcddeee1dce3ebe2a96dd9cfe89aecbc0a033849826a9913c324495a6023faf3a8959c1166c3412b95556e83eec9369bc179c01cbb28322dfc787b41f32ea1dd8571a402583d1f55d3086f9f7d7039b1631605b4172e536ab7856b088da224713c83d332c7a6b9294e3a2fcfb8aa00fcf449c4706b846e4f9a4a6650c679a015af522fe47a76e9b0401c56d7ebf99e38adddaca91db73fc06da874e7f5ef11c85b562db06dd4f0f2e4dc14dabf7880b4f9665f9579ee9c61d2557641fb75a0a19211cbb9f677be1f8997d03599ea93f3f4bce85755363188208f299a9fd1a405539e4e59362e014bed4ac9a5075d766e697250b0436812eb7bbc7909265fc262b4cff85b5537e1f044c6575f6f167fe228dd1b73c74d9b0e816d9ab0669219f2461577e1c563b098bf54c543d5491b08fa2dbe948cda0df19b797426313cd69127cdd114662589b6eb6d217ff070a9257719f26f70a4ebfcdea8a8abc5a03938334903b5106faccc7921519f8cf7ecc100f754dc200fb96ce0307938fb55d5a6f2e59d73d175810fbb20ea7aa38aca342e99f40b02df8f7385faa4145f7d46d4c76e7fce92d0ff4f1d5e58a211398121f3cf68944a2d4502982f2b69f3826192870708563c2d66077eb39a85e26e9eb4b9f0da29d4e6c5023ed59be2491e1ae989e53141931081f3ade7fd28678f076c878827c41e50e9da17acb0258977b23e6e2799207c51f0664b51f745fa69393559a9241331669d0b46f7a1a255bf5c3a5f2574f922f51d6fd662fab6df5c72e93f054b6886a8458715f49bfd9821871bec2ad44708a8f6acff99fa5a1760d6465e9a2b65e86c09a8f1ba4d999241f7a50266fbce525b879469b8779146b15d443b6c928520a98270f0c27cb1c286ea95569d08ba9f613a4fc24fb3680132f0971fb302589b03a37d873762dea945684fa6d29b4c482134627cf59a2284982faaebcc2397fde63ab6d58966e54535aabea5c701303b0356933dc8fee414f37f56dc4c7774dbaa7c0151e50461fe82834e07e9d454a0bd96061e95ac86dbe857c7c6358e4ff8d3d0ee9c21cef317795862c2b53894980e43cdb62c876f9e0a2baba4cebd130e18b2c2bbc193f2bb9a0bcc4624621a8bf08526a3172c3b8ce8b250f7487dca30537d3efefffd9a878461c5878e5f819687f432d2df9aa25a0e8ff18e3dda18af1b93726979a220b9d9c28a1656f5819dc95c4d6f491abdb011241f831edac21cee6a08010469de2360a4a35ab34d57e9d14242c4b60651fbde8fe4c215329495ac52d6c83675e4a5d7c41864f0c7687e25b98eeba9b7524f536ae06998420cf1331e130a24a15fc3bed062f3ee823307aab2b4d82629f3dd31f6abb5f9a3213ff57037c3b700fa39e99a713b7abf49e95db937213c60a7ecc8b7b2e7fcf4d6342f311c00c3a90b0422bc1e364604211ff722ed4df4c4e02c999a4c312a4a82c2dd11dcfce2967af2009db3b3ad25f940751f317656467560594fc5bc204e370051f2c4fc3ef97559a7d23b36288911dab1472b25fc2864298ef0e94daf527dd280610bdf830423190c4cfe18285d576a9f81dec361be9c89f30e1c343a6a27e73ad5a34411ec6f7c0c4c8345f718fcb7395d406afbba483a9549e4a8a61bd3ff2e59573760b39e7283fa7c0f975d3a3c8b59526fc2908c457b1e22b29055afd587ceab493842499446f86c466467956778c787cc81b6e71020030bcf8573a3dd8a1aa37754d8ec7ac8c74d48bdb44283794432867620012c2a80b9243dd35b4d8dda416f831a09ec54185d8583493cf86991ad119ae52ea333cca7eb59de01075aaef877b3da0916d7574013335a858618714ea4fbc7aa6e725679002a2100a4bc7671177ab745f8e4a757ae9174eef597571159a8c5d45db16e9971c57881e5aaa0e34cf4ef40dd82f1b29f8644ea8b154d183866f3a32d45b8f1ccd63b6c701c623da057547a3795bf55419655f6186d6179ba704c78572f5d43cbc2c1485035775b40689641cc9c5bd1720727b25af4612b36d1030dac027488b9eaaf0c56d0fdc3cc12e24a6607063479a811f37931d53ef563f6f25bb8667c8b20278384d280c98b355fd948b5c45dbf1cdd5c741fccdf597da5d4ca0aac8a637a524aefd6500c68fb5bc5d9f042e5aaeca65181071cc5ea26d66a5c563481f6c78f3a70167c9cd2b2faa8ed7396c0956e8de5acba0e871fa0a7b83b0c820d3ab7559a7af74f3506a5f5009408e492f3bf740b5d5ef498ad7722a30db0968f54a267a91d26b458d502b4c9f61ad0a5df75e697790798edd811d41bd978ce23dde90906c89df24afacee0e6a3cbf86b9ef1cf7ce2904ed893e6ccb4d3875a7c758fcd56470b315db8b3e6a6202a6b946c54d49bfa97609d5868ccc0d0bd86a980c485fa32bcff40ae3bf51c1af8018db1174119acd8afc1248aaf6c3d75233a6ee4d0f92d4fd4194fe1d272ffa73cab73a31ddfbce112ebc4467e9a6f91996d03daf0dd8bcd77033f0d1b1858c586bc21c0b58fd64829c0cff988a040f713f576ff9c7bd94fb266fe2d6f6ab055c37be55d5df7b3379e481e889a0bc9f07f654509116a6fc65d85dbf78c2180025acacaeffad814bf29d5ac26a7222b15c108cfc61bc199389f631cc8810ef7260e1b139ddf6012afd4c5f3ba3158fcc3c1bbffbd77eef228000fa1b21aebf230b8e256386f901e3aeb9645c1794e58fa44f896c0f27339590165534b2bc5997bdf080dea7dc3ee9f1d8a067d8843588ec3dc59e4d6b9469b7aad9147a9aa42c16f2530c5af100f80d438a7a1de2362d2eacd63ed84b2360e154f187804e702fdca64d1c4dae8668ec88f899c5a2aa25deb086e35ec6d14343d52545b884b37ca630b752b8d4ad1edf8aed5421ac81bcc4e40a7d1cc0c5fd40bf5d2b244efa8472b8d9e87f316adae8da8c9f420ca2316d24f9c0ce0e1c934002a7e7e62e2c08d4e3e812366858a3ca5b70281cc572b2d8cbd451d39e9af64cc92d108eb80126efb6bf576189cc8b5fd8989f232de652ca704e3a3d84f778f4ba778ed631ecc938357298ca323a900af1fb0496b56f8743df299abd2e9876e6ff13c3bc20d563c8c38b80acc4fd12df1aedd75ee3975300b33ab75939be9a2f8f28cba477f5e2834a251407c50c1de8953be6663822310e54c0f39cb77ad01cbcf06878882ee5b3d89be3369664285e7a936e52dd86f5c7a3ff39f1a22f397eb80426f437a540df2575d42d1e60475d195bb211b5908bd74e3d0fa25bf07d02ea7e9ca5f34c11c5aff58e8eb78e40f953080821f8164cb829a716f80206582c8dd8a9c6720725fc245f2b9eb9cc3dc20c571fc18d17359fb52197100f1520140e4c41e9a241cb2e9ae7dfbac0f32d8249b8c59a92b782664716b91f2f2ec7b1cfbb7f95de33329a78d62409b8c8715d76a2f12f82c0f4cf8c9603bf713e907868b163c6aeb58523f08e19384e6a9b1431b17e35dee2218455cffd4cb658871910e775e84e660844b1ed08ad5a64abe68ebacbd1344421fe43360c82f6e78c1ee2a06d95156b3dbf23b847ecaeab1999811917fd6e07c3254d349b0a7a2c043eb3bf462abc7544e65872a716208c21021f2fdcab39c0917e94b3c33fc17817f2263dd64cb5d1b78e3a4a97f0b3540dc09d549c8edc206abeabeecdc9b2287fa5faea8856a217f2e30535f4b57e6ae2d11527ae5c7097d5d68307bdf2600d1012f7420f947509cd765707da6bcd1df4baf1527dfe6b6f06af2907f4a070ce43dcef74ed0495173a4b303f95ea79fa17924e5f0d7c480fa0578439a422265c0884056e891f5b8ba5d8b7aecc6c0814f8fbe890de3939618e50f85261b7c334b754314230363705c1d843ad21871941b85fb1a00a93cea1208882fc1a9d94d359665e0d80fdcdfd7c6770ec004ebd96f28e09e73712f49cbf1ce8be757685258218bd44c548d2c1c086b0ddd150078d0b1f2c9b1e29c21f75f8110fdbd0c4cabba03aead454911c43eba3a8cb984bdf60ad2e1c941e7ee6d00c4f3de74f7b9370bab0f5dd14bdb1d4434a8f15643", 0xd78}], 0x7)
r0 = kqueue()
r1 = syz_open_pts()
r2 = syz_open_pts()
syz_open_pts()
kevent(r0, &(0x7f0000000800)=[{{r1}, 0xfffffffffffffff8, 0x31}, {{}, 0xfffffffffffffffd}, {{r2}, 0xffffffffffffffff}], 0x4a79, 0x0, 0xfffffb7e, 0x0)
setrlimit(0x0, &(0x7f0000000000)={0x1ff})
sendmsg$unix(0xffffffffffffffff, &(0x7f0000000300)={&(0x7f0000000080)=@abs={0x1, 0x0, 0x0}, 0x8, 0x0}, 0x0)
ioctl$TIOCSETAF(0xffffffffffffffff, 0x802c7416, &(0x7f00000000c0)={0x0, 0x0, 0xffffffffffffffff, 0x0, "a4c5c15b04e4ff00"})
r0 = socket(0x18, 0x1, 0x0)
close(r0)
r1 = socket(0x18, 0x400000002, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f00000000c0)=@abs={0x682eb13985c518e6, 0x7}, 0x1c)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
close(r1)
r2 = socket(0x18, 0x2, 0x0)
setsockopt(r2, 0x29, 0x9, &(0x7f0000000080)='\x00\x00\x00\x00', 0x4)
setsockopt(r1, 0x1000000029, 0x2e, &(0x7f0000000000)="ebffcbff13b9fd812eaa4e713048e69931929648", 0x14)
connect$unix(r0, &(0x7f00000000c0)=@abs={0x0, 0x7}, 0x1c)
mknod(&(0x7f0000000000)='./file0\x00', 0x2000, 0x0)
r0 = openat(0xffffffffffffff9c, &(0x7f00000001c0)='./file0\x00', 0x0, 0x0)
ioctl$TIOCSETA(r0, 0x802c7414, &(0x7f0000000040)={0x0, 0x0, 0x0, 0x0, "58ffff20005aab0a93987218904f7b7213693322", 0xad1})
r0 = openat$diskmap(0xffffffffffffff9c, &(0x7f00000001c0), 0x0, 0x0)
r1 = openat$vnd(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
poll(&(0x7f0000000000)=[{r1, 0x1}], 0x1, 0x0)
ioctl$DIOCMAP(r0, 0xc0106477, &(0x7f0000000200)={&(0x7f0000000100)='.\x00', r1})
poll(0x0, 0x0, 0xffff)
close(r1)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f00000000c0)={{0x18, 0x0, 0x2, 0x10000}, {0x18, 0xffffffffffffffff, 0xfffffffa}, 0xfffa, [0xfffffffc, 0x200, 0x9, 0xfffffffc, 0x4000000, 0x40002, 0x4]}, 0x3c)
r0 = kqueue()
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffff9c, 0x29, 0x68, &(0x7f0000000100)={{}, {0x18, 0x0, 0x800001, 0x8000}, 0xfffc, [0x0, 0x3, 0x8, 0xfffffffffffffff9, 0x3, 0x0, 0x0, 0xffffffff]}, 0x3c)
fcntl$dupfd(0xffffffffffffffff, 0x0, 0xffffffffffffffff)
kevent(r0, &(0x7f0000000000), 0x60, 0x0, 0xfffffffb, 0x0)
close(0xffffffffffffffff)
kqueue()
ftruncate(0xffffffffffffffff, 0xfffffffffffffffe)
setsockopt$inet6_MRT6_ADD_MFC(0xffffffffffffffff, 0x29, 0x68, &(0x7f0000000000)={{0x18, 0x2, 0x80000001, 0x8000}, {0x18, 0x2, 0x0, 0x5}, 0x40, [0xb, 0x1ff, 0x8, 0xffffffff, 0x401, 0x3, 0x0, 0x7]}, 0x3c)
r1 = semget$private(0x0, 0x5, 0x288)
semop(r1, &(0x7f0000000100)=[{0x2, 0x5022, 0x1000}, {0x1, 0x7, 0x1000}], 0x2)
r2 = socket$inet(0x2, 0x3, 0x102)
shutdown(r2, 0x1)
pipe(&(0x7f0000000040)={0xffffffffffffffff, <r3=>0xffffffffffffffff})
setsockopt$inet_opts(r3, 0x0, 0x1, &(0x7f0000000080)="06bdbd56fd2462e4ae4731e5fecf5e94d08638862e21f751b06fa9de6f3ef8f68d129c486b", 0x25)
mmap(&(0x7f0000ffd000/0x3000)=nil, 0x3000, 0x4, 0x810, r2, 0x0)
semop(r1, 0xffffffffffffffff, 0x4)
semctl$IPC_STAT(r1, 0x0, 0x2, &(0x7f0000000580)=""/10)
setreuid(0xee00, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{}, {0x1, 0x0, 0x0, 0x2}, {0x6, 0x0, 0x0, 0x4a21}]})
sysctl$kern(&(0x7f0000000040)={0x1, 0x49}, 0x6, &(0x7f0000000000)="f7a45101212e9dc4a2cccef87fc942", &(0x7f0000000180)=0x68a, 0x0, 0x29)
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, 0x0)
sysctl$kern(&(0x7f0000000000)={0x1, 0x3e}, 0x3, &(0x7f0000000080)="460f0e55", &(0x7f0000000180)=0x4, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000200), 0x4000000001, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
dup2(r0, r1)
ioctl$BIOCSETWF(r1, 0x80104277, &(0x7f00000001c0)={0x3, &(0x7f00000000c0)=[{0x34, 0x0, 0x0, 0xde}, {0x24}, {0x6, 0x0, 0x0, 0x40000}]})
pwrite(r1, &(0x7f0000000300)="977fffffff0000005f0004000000", 0xe, 0x0)
syz_emit_ethernet(0xe, &(0x7f0000000280)={@broadcast, @remote, [], {@ipv4={0x800, {{0x6, 0x4, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, @rand_addr, @local={0xac, 0x14, 0x0}, {[@noop, @rr={0x7, 0x3}]}}, @udp={{0x3, 0x0, 0x8}}}}}})
ioctl$BIOCSETIF(0xffffffffffffffff, 0x8020426c, &(0x7f0000000300)={'tap', 0x0})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = socket(0x18, 0x2, 0x0)
r1 = fcntl$dupfd(r0, 0x0, r0)
ioctl$TIOCFLUSH(r1, 0xc0206937, &(0x7f0000000300))
r0 = openat$pf(0xffffffffffffff9c, &(0x7f0000000100), 0x2, 0x0)
ioctl$BIOCSETWF(0xffffffffffffffff, 0x80104277, &(0x7f0000000140)={0x0, &(0x7f0000000080)})
ioctl$VMM_IOC_INTR(r0, 0xc0104451, &(0x7f0000000140)={0x3f, 0x408, 0x8})
ioctl$FIOASYNC(r0, 0x8004667d, &(0x7f00000000c0)=0x19)
ioctl$WSDISPLAYIO_ADDSCREEN(0xffffffffffffffff, 0x80245753, &(0x7f0000000000)={0x0, './file0/file0/fi', './file0/file0/fi'})
ioctl$VMM_IOC_INTR(r0, 0xc0104452, &(0x7f0000000140)={0x5, 0x408})
sysctl$hw(&(0x7f0000000000)={0x6, 0x18}, 0x2, 0x0, 0x0, &(0x7f0000001240), 0x4)
r0 = openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
shutdown(r0, 0x0)
sysctl$net_inet6_icmp6(&(0x7f0000000000), 0x4, 0xffffffffffffffff, 0x0, 0x0, 0x0)
r0 = socket$inet6(0x18, 0x2, 0x0)
getsockopt(r0, 0x29, 0x25, 0x0, 0x0)
connect$unix(0xffffffffffffffff, &(0x7f0000000000)=ANY=[@ANYBLOB="6b02e18b95"], 0x1)
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r0 = kqueue()
r1 = fcntl$dupfd(r0, 0x2, 0xffffffffffffffff)
close(r1)
r2 = socket(0x2, 0x1, 0x0)
connect$unix(r2, &(0x7f0000000000)=@file={0x0, './file0\x00'}, 0x10)
setsockopt$sock_int(r1, 0xffff, 0x1023, &(0x7f0000000080), 0x4)
readv(r1, &(0x7f00000013c0)=[{0x0}], 0x1)
ioctl$BIOCSETF(0xffffffffffffffff, 0x80104267, &(0x7f00000000c0)={0x0, &(0x7f0000000300)})
r0 = socket$inet(0x2, 0x3, 0x0)
r1 = dup(r0)
ioctl$TIOCFLUSH(r1, 0xc0106924, &(0x7f00000000c0)=0x2e)
ioctl$VMM_IOC_RESETCPU(0xffffffffffffffff, 0x82405605, &(0x7f0000000340)={0x0, 0x0, {[0x2, 0x7fffffffffffefff], [0x0, 0x0, 0x0, 0x0, 0x0, 0x2], [0x2, 0x0, 0x0, 0x0, 0x81, 0x0, 0x1], [], [{0x0, 0x0, 0x0, 0x3}, {}, {0x0, 0x0, 0x8000003}, {}, {}, {0x0, 0x0, 0x0, 0x3f}], {0x0, 0x1}}})
mprotect(&(0x7f0000000000/0x800000)=nil, 0x800000, 0x5)
r2 = socket(0x18, 0x2, 0x0)
r3 = fcntl$dupfd(r2, 0x0, r1)
ioctl$TIOCFLUSH(r3, 0x8080691a, &(0x7f0000000300))
mknod(&(0x7f0000000240)='./bus\x00', 0x8000200b, 0x3200)
openat$null(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
r0 = open$dir(&(0x7f0000000080)='./bus\x00', 0x0, 0x0)
readv(r0, &(0x7f0000000040)=[{&(0x7f0000000140)=""/230, 0x400}], 0x21)
socket$inet(0x2, 0x3, 0x6)
sysctl$kern(&(0x7f0000000180)={0x1, 0x16}, 0x2, 0x0, 0x0, 0xffffffffffffffff, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000380), 0x10689, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f00000002c0)={'tap', 0x0})
ioctl$BIOCSETWF(r0, 0x80104277, &(0x7f0000000280)={0x3, &(0x7f0000000080)=[{0x5}, {0x15}, {0x6, 0x0, 0x0, 0x20}]})
pwrite(r0, &(0x7f0000000040)="dd9681e99141de5a6a0a3325b37e", 0xe, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x2000, 0x0)
r0 = open(&(0x7f0000000000)='./file0\x00', 0x0, 0x0)
ioctl$TIOCMSET(r0, 0x8004746d, &(0x7f0000000080))
r0 = socket(0x2, 0x8003, 0x0)
setsockopt$sock_int(r0, 0xffff, 0x1005, 0x0, 0x0)
fcntl$lock(0xffffffffffffffff, 0x0, &(0x7f0000000040)={0x0, 0x0, 0x2})
sysctl$kern(&(0x7f0000000040)={0x1, 0x33}, 0x4000000000000003, &(0x7f00000001c0)="eb9c770525dbfb3a662c2e9fcde22a63a70b2f59cf6fdd160d1d376007f883be5d093f7630b4399c188b0a79c5b6f12d2a104d64a206500bdf5ca91fb2299fb50093847dc573a5c2e9acddf0aa76345bc995274e0149c8ff070000000000009200f86fae240d9fdbbe85dd833b595e346825ba46f7c4881d7702012992b893e765edcc865895bdaf7a6ca185324c6dd9a652c26ebc4e816491d09e073d481defed0adfa61364a3b5f9fce44462609c067532fb0e2b241425a808", &(0x7f0000000000)=0x24, 0x0, 0x42)
r0 = semget$private(0x0, 0x4000000009, 0x0)
semop(r0, &(0x7f0000000300)=[{0x0, 0x8, 0xe5ce97ab354d96be}, {0x2, 0x202, 0x800}, {0x3, 0xff81, 0x1000}, {0x4, 0x400}], 0x4)
semctl$SETVAL(0x0, 0x0, 0x8, &(0x7f0000000280)=0x3)
semop(r0, &(0x7f0000000240), 0x15)
semctl$GETZCNT(r0, 0x0, 0x7, &(0x7f0000000540)=""/233)
semop(0x0, &(0x7f0000000700)=[{0x1, 0x7fff}, {0x4, 0x6, 0x3800}, {0x2, 0x20, 0x800}, {0x2, 0x5, 0x2c00}, {0x3, 0x3, 0x1800}], 0x5)
semop(r0, &(0x7f0000000080)=[{0x3, 0x6}, {0x2, 0x3, 0x800}, {0x4, 0x5, 0x1000}, {0x3, 0x9, 0xac61f474f541193d}, {0x2, 0x7}, {0x0, 0x5, 0x800}, {0x0, 0x22}, {0x2, 0x2, 0x800}, {0x0, 0xf1b3}, {0x0, 0x2}], 0xa)
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f00000003c0)={{0x104, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x6d, 0x47}, 0x40008006, 0x5, 0x4})
semctl$GETZCNT(0x0, 0xb1b9817181abc3e9, 0x7, &(0x7f0000000680)=""/125)
semop(r0, &(0x7f00000000c0)=[{0x3, 0x5f6b, 0x1000}, {0x0, 0xfb5}, {0x2, 0x9, 0x1400}], 0x3)
semop(0x0, &(0x7f0000000740)=[{0xba7658e0d32154a0, 0x7f, 0x1000}, {0x2, 0x80, 0xf646dd0827a9ae5a}], 0x2)
getgroups(0x1, &(0x7f00000004c0)=[<r1=>0x0])
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000440)={{0x28, 0x0, r1, 0x0, 0xffffffffffffffff, 0x11, 0x1}, 0x6, 0x3, 0x7fff})
semctl$IPC_SET(r0, 0x0, 0x1, &(0x7f0000000100)={{0x1, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x41, 0x4}, 0x0, 0x0, 0xfffffffffffffbff})
semop(r0, &(0x7f0000000180)=[{0x0, 0x9, 0x800}, {0x3, 0x80}, {0x3, 0x401, 0x1800}, {0x1, 0x8, 0x800}, {0x1, 0xf8b8, 0x800}], 0x5)
semop(0x0, &(0x7f00000002c0)=[{0x2, 0xfffb, 0xc00}, {0x1, 0x1, 0x800}], 0x2)
semctl$IPC_STAT(r0, 0x0, 0x2, &(0x7f0000000040)=""/31)
semop(0x0, &(0x7f0000000140)=[{0x2, 0x32, 0x800}, {0x1}, {0x4293013daa7c1201, 0x6, 0x1000}, {0x0, 0x9, 0x1000}, {0x0, 0x6, 0x3000}, {0x4, 0x8001, 0x400}, {0x0, 0x809}, {0x0, 0x41, 0x800}], 0x8)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000000)={'tap', 0x0})
r1 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r1, 0x8020426c, &(0x7f00000000c0)={'tap', 0x0})
dup2(r1, r0)
sysctl$net_inet_udp(&(0x7f0000000000)={0x4, 0x2, 0x11, 0x5}, 0x4, 0x0, 0x0, &(0x7f0000000200), 0x0)
r0 = socket$inet(0x2, 0x2, 0x0)
getsockopt$sock_int(r0, 0xffff, 0x1025, 0x0, 0x0)
sysctl$vm(&(0x7f0000000040)={0x2, 0xc}, 0x2, 0x0, 0x0, &(0x7f00000001c0), 0x0)
mprotect(&(0x7f0000ffa000/0x3000)=nil, 0x3000, 0x0)
munmap(&(0x7f0000ffc000/0x4000)=nil, 0x4000)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000001440)={0x1, &(0x7f00000000c0)=[{0x84}]})
sysctl$machdep(&(0x7f0000000080)={0x7, 0xf}, 0x2, &(0x7f00000000c0)="e500b7bf", &(0x7f00000001c0)=0x4, &(0x7f0000000200), 0x0)
r0 = socket(0x11, 0x4003, 0x0)
sendto$unix(r0, &(0x7f0000000000)="b100050460000000000008000701000000000000ceb1fea7fef96ecfc73fd3357a8483aa0416fa4f376336adf00b7804be781e4991f7c8df5f882b297be1aa5b23ed00f4c8b2ca3ebbc2576900001b2e27acb535dc1678f1c1cd37af63ff374229020900efe095bebd108ae070c1f5ab72c881ff7cc53c894303b22f0100404f36a00f90006ee01be657aea8c500000002003c88c1cf8044101b2368f0d2f0ffffff00"/177, 0xb1, 0x0, 0x0, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000180), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000040)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000100)={0x3, &(0x7f00000000c0)=[{0x20}, {0x2}, {0x6, 0x0, 0x0, 0x4}]})
syz_emit_ethernet(0x3e, &(0x7f00000001c0)={@random="36c840028ea6", @remote, [], {@ipv6={0x86dd, {0x0, 0x6, "879f98", 0x8, 0x0, 0x0, @mcast1, @local={0xfe, 0x80, '\x00', 0x0}, {[], @udp={{0x2, 0x2, 0x8}}}}}}})
madvise(&(0x7f000081c000/0x4000)=nil, 0x4000, 0x0)
munmap(&(0x7f000081f000/0x1000)=nil, 0x1000)
r0 = openat$zero(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
preadv(r0, &(0x7f0000000080)=[{&(0x7f0000000100)=""/254, 0xfffffc47}, {&(0x7f0000000200)=""/171, 0xab}, {&(0x7f00000002c0)=""/243, 0xe2}], 0x50, 0x0)
madvise(&(0x7f000081d000/0x3000)=nil, 0x3000, 0x6)
openat(0xffffffffffffff9c, &(0x7f0000000140)='./file0\x00', 0x200, 0x0)
mknodat(0xffffffffffffff9c, &(0x7f0000000040)='./file0\x00', 0x1000, 0x0)
sysctl$vm_swapencrypt(&(0x7f0000000000), 0x3, &(0x7f0000000080)="97011665", &(0x7f0000000180)=0x4, 0x0, 0x0)
setuid(0xee01)
r0 = socket(0x800000018, 0x2, 0x0)
r1 = socket(0x18, 0x1, 0x0)
setsockopt(r1, 0x29, 0xe, &(0x7f0000000000)="02000000", 0x4)
dup2(r1, r0)
setsockopt$sock_int(r0, 0xffff, 0x1, &(0x7f0000000040)=0x3ee, 0x4)
bind$unix(r0, &(0x7f0000000080)=@abs={0x1f95d27d48731892}, 0x1c)
lchown(&(0x7f0000000080)='./file0\x00', 0x0, 0x0)
r0 = openat$pci(0xffffffffffffff9c, &(0x7f00000000c0), 0x0, 0x0)
ioctl$PCIOCREAD(r0, 0xc00c7007, &(0x7f0000000080))
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000140), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000180)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f00000001c0)={0x3, &(0x7f0000000040)=[{0x1d}, {0x3c}, {0x6406}]})
syz_emit_ethernet(0xe, &(0x7f0000000200)={@random="1a37d5e5e253", @remote})
socketpair$unix(0x1, 0x5, 0x0, &(0x7f0000000100)={<r0=>0xffffffffffffffff, <r1=>0xffffffffffffffff})
sendto$unix(r1, 0x0, 0x0, 0xc, 0x0, 0x0)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
sendto$unix(r1, 0x0, 0x0, 0x0, 0x0, 0x0)
recvmsg(r0, &(0x7f00000004c0)={0x0, 0x0, &(0x7f0000000380)=[{&(0x7f0000000140)=""/83, 0x53}], 0x1, 0x0}, 0x0)
r0 = openat$bpf(0xffffffffffffff9c, &(0x7f0000000280), 0x0, 0x0)
ioctl$BIOCSETIF(r0, 0x8020426c, &(0x7f0000000080)={'tap', 0x0})
ioctl$BIOCSETF(r0, 0x80104267, &(0x7f0000000c40)={0x3, &(0x7f0000000400)=[{0x10001}, {0x3c}, {0xffe}]})
syz_emit_ethernet(0x32, &(0x7f0000000000)={@broadcast, @broadcast, [], {@arp={0x806, @generic={0x0, 0x0, 0x6, 0x0, 0x0, @remote, "", @remote, "77faa3b4617eacbdeab1702effe4b282"}}}})
unveil(&(0x7f00000000c0)='./bus\x00', &(0x7f00000001c0)='x\x00')
unveil(&(0x7f0000000080)='./file2\x00', &(0x7f0000000040)='x\x00')
unveil(&(0x7f00000000c0)='./file1\x00', &(0x7f0000000100)='c\x00')
unveil(&(0x7f0000000300)='./file3\x00', &(0x7f0000000340)='r\x00')
filt_wseventdetach100%of 1
filt_wseventread75%of 4
wsevent_fini67%of 3
wsevent_init50%of 4
wsevent_kqfilter100%of 3
wsevent_read70%of 10
-----------
SUMMARY72%of 25
mld6_checktimer---of 10
mld6_fasttimeo---of 14
mld6_init---of 1
mld6_input---of 50
mld6_sendpkt75%of 12
mld6_start_listening100%of 4
mld6_stop_listening60%of 5
-----------
SUMMARY77%of 21
filt_videodetach---of 1
filt_videoread---of 1
video_attach_mi---of 1
video_claim---of 3
video_intr---of 5
video_stop---of 3
video_submatch---of 1
videoactivate---of 3
videoattach---of 5
videoclose---of 3
videodetach---of 5
videoioctl---of 58
videokqfilter---of 12
videommap---of 9
videoopen25%of 8
videoprint---of 3
videoprobe---of 1
videoread---of 17
-----------
SUMMARY25%of 8
task_add75%of 4
task_del75%of 4
task_set100%of 1
taskq_barrier100%of 1
taskq_barrier_task---of 3
taskq_create---of 3
taskq_create_thread---of 7
taskq_del_barrier50%of 4
taskq_destroy---of 6
taskq_do_barrier36%of 14
taskq_init---of 1
taskq_next_work---of 8
taskq_thread---of 11
-----------
SUMMARY54%of 28
KQREF---of 1
KQRELE50%of 12
filt_badfd100%of 1
filt_dead67%of 3
filt_deaddetach100%of 1
filt_fileattach100%of 1
filt_kqdetach75%of 8
filt_kqueue---of 3
filt_kqueue_common---of 3
filt_kqueuemodify67%of 3
filt_kqueueprocess89%of 9
filt_proc25%of 12
filt_procattach55%of 11
filt_procdetach72%of 7
filt_seltrue100%of 1
filt_seltruedetach100%of 1
filt_seltruemodify100%of 1
filt_seltrueprocess100%of 4
filt_timerattach67%of 3
filt_timerdetach100%of 1
filt_timerexpire---of 6
filt_timermodify100%of 3
filt_timerprocess75%of 4
klist_free67%of 3
klist_init100%of 1
klist_init_mutex100%of 1
klist_init_rwlock100%of 1
klist_insert60%of 5
klist_insert_locked100%of 3
klist_invalidate64%of 22
klist_mutex_assertlk67%of 3
klist_mutex_lock100%of 1
klist_mutex_unlock100%of 1
klist_remove75%of 8
klist_remove_locked84%of 6
klist_rwlock_assertlk100%of 1
klist_rwlock_lock100%of 1
klist_rwlock_unlock100%of 1
knote80%of 10
knote_acquire29%of 7
knote_activate---of 4
knote_assign100%of 1
knote_attach43%of 7
knote_dequeue40%of 5
knote_detach75%of 8
knote_drop80%of 10
knote_enqueue56%of 9
knote_fdclose88%of 8
knote_processexit---of 3
knote_release34%of 6
knote_remove62%of 26
knote_submit100%of 4
kqpoll_done40%of 5
kqpoll_exit50%of 4
kqpoll_init75%of 8
kqueue_alloc---of 1
kqueue_close100%of 1
kqueue_do_check50%of 14
kqueue_init---of 1
kqueue_init_percpu---of 1
kqueue_ioctl100%of 1
kqueue_kqfilter67%of 6
kqueue_purge88%of 8
kqueue_read100%of 1
kqueue_register75%of 89
kqueue_scan75%of 62
kqueue_scan_finish43%of 7
kqueue_scan_setup100%of 1
kqueue_sleep86%of 7
kqueue_stat---of 1
kqueue_task---of 3
kqueue_terminate58%of 14
kqueue_wakeup---of 6
kqueue_write100%of 1
seltrue_kqfilter100%of 3
sys_kevent87%of 36
sys_kqueue75%of 8
-----------
SUMMARY71%of 521
strncmp100%of 6
-----------
SUMMARY100%of 6
filt_hotplugrdetach100%of 1
filt_hotplugread100%of 1
hotplug_device_attach100%of 4
hotplug_device_detach100%of 4
hotplug_get_event---of 3
hotplug_put_event---of 4
hotplugattach---of 1
hotplugclose100%of 4
hotplugioctl100%of 1
hotplugkqfilter100%of 3
hotplugopen100%of 5
hotplugread34%of 6
-----------
SUMMARY87%of 29
_bus_dmamap_create---of 3
_bus_dmamap_destroy---of 1
_bus_dmamap_load50%of 4
_bus_dmamap_load_buffer53%of 19
_bus_dmamap_load_mbuf50%of 8
_bus_dmamap_load_raw---of 22
_bus_dmamap_load_uio---of 10
_bus_dmamap_sync100%of 1
_bus_dmamap_unload100%of 1
_bus_dmamem_alloc---of 1
_bus_dmamem_alloc_range---of 9
_bus_dmamem_free---of 7
_bus_dmamem_map---of 11
_bus_dmamem_mmap---of 8
_bus_dmamem_unmap---of 4
-----------
SUMMARY55%of 33
filt_soexcept100%of 11
filt_solisten90%of 10
filt_somodify100%of 1
filt_soprocess100%of 6
filt_sordetach100%of 1
filt_soread100%of 10
filt_sowdetach100%of 1
filt_sowrite100%of 10
klist_soassertlk100%of 1
klist_solock100%of 1
klist_sounlock100%of 1
m_getuio88%of 16
sbsync---of 6
so_print---of 3
soabort---of 1
soaccept72%of 7
soalloc67%of 3
sobind100%of 3
sobuf_print---of 1
soclose61%of 38
soconnect80%of 10
soconnect2100%of 7
socreate84%of 12
sodisconnect---of 5
sofree73%of 22
sogetopt95%of 38
sohasoutofband---of 3
soidle---of 3
soinit---of 1
solisten100%of 10
somove50%of 119
soo_kqfilter100%of 5
soput---of 1
soreaper---of 1
soreceive77%of 157
sorflush60%of 5
sorwakeup89%of 9
sosend84%of 49
sosetopt90%of 69
soshutdown100%of 5
sosplice71%of 37
sotask---of 3
sounsplice61%of 23
sowwakeup100%of 6
-----------
SUMMARY77%of 703
pci_alloc_msix_table---of 3
pci_detach_devices---of 7
pci_disable_legacy_vga---of 6
pci_disable_vga---of 1
pci_enable_vga---of 1
pci_enumerate_bus---of 21
pci_find_device---of 7
pci_free_msix_table---of 3
pci_get_capability28%of 11
pci_get_ext_capability---of 9
pci_get_ht_capability---of 9
pci_get_powerstate---of 3
pci_intr_msix_count---of 4
pci_matchbyid---of 7
pci_powerdown---of 7
pci_primary_vga---of 7
pci_probe_device---of 34
pci_requester_id---of 1
pci_reserve_resources---of 45
pci_resume---of 10
pci_resume_msix---of 6
pci_route_vga---of 4
pci_set_powerstate---of 7
pci_suspend---of 8
pci_suspend_msix---of 6
pci_unroute_vga---of 4
pci_vpd_read34%of 9
pci_vpd_write---of 9
pciactivate---of 5
pciattach---of 5
pciclose100%of 1
pcidetach---of 1
pciioctl55%of 79
pcimatch---of 4
pciopen100%of 3
pciprint---of 3
pcisubmatch---of 7
-----------
SUMMARY52%of 103
ufs_bmap60%of 5
ufs_bmaparray48%of 46
ufs_getlbns42%of 17
-----------
SUMMARY48%of 68
vioscsi_alloc_reqs---of 32
vioscsi_attach---of 8
vioscsi_match---of 1
vioscsi_req_done---of 5
vioscsi_req_get67%of 3
vioscsi_req_put100%of 1
vioscsi_scsi_cmd50%of 20
vioscsi_vq_done---of 4
-----------
SUMMARY55%of 24
canonpath---of 19
parsepledges80%of 15
pledge_adjtime---of 4
pledge_chown23%of 9
pledge_fail---of 14
pledge_fcntl50%of 4
pledge_flock50%of 4
pledge_ioctl2%of 189
pledge_kill---of 5
pledge_namei5%of 47
pledge_protexec29%of 7
pledge_recvfd20%of 10
pledge_sendfd20%of 10
pledge_sendit50%of 4
pledge_socket17%of 12
pledge_sockopt4%of 58
pledge_swapctl---of 4
pledge_syscall---of 5
pledge_sysctl2%of 147
pledgereq_flags---of 4
sys_pledge74%of 23
-----------
SUMMARY10%of 539
cpu_exit---of 1
cpu_fork---of 9
setguardpage---of 1
tcb_get---of 1
tcb_set---of 3
vmapbuf60%of 5
vunmapbuf67%of 3
-----------
SUMMARY63%of 8
drm_activate---of 6
drm_attach---of 19
drm_attach_pci---of 4
drm_attach_platform---of 1
drm_dequeue_event---of 5
drm_detach---of 11
drm_dev_enter---of 1
drm_dev_exit---of 1
drm_dev_get---of 3
drm_dev_init_release---of 1
drm_dev_put---of 5
drm_dev_register---of 30
drm_dev_set_unique---of 1
drm_dev_unplug---of 1
drm_dev_unregister---of 15
drm_dmamem_alloc---of 7
drm_dmamem_free---of 3
drm_file_cmp---of 1
drm_file_tree_SPLAY---of 12
drm_file_tree_SPLAY_INSERT---of 5
drm_file_tree_SPLAY_MINMAX---of 8
drm_file_tree_SPLAY_REMOVE---of 5
drm_find_description---of 9
drm_find_file_by_minor---of 3
drm_get_device_from_kdev50%of 4
drm_getpciinfo---of 3
drm_minor_acquire---of 4
drm_minor_alloc---of 7
drm_minor_alloc_release---of 3
drm_minor_release---of 5
drm_order---of 4
drm_pci_alloc---of 3
drm_pci_free---of 4
drm_pciprobe---of 9
drm_probe---of 5
drm_put_dev---of 5
drm_quiesce---of 4
drm_wakeup---of 1
drmclose---of 15
drmkqfilter---of 11
drmmmap---of 1
drmopen9%of 23
drmprint---of 3
drmread---of 20
drmsubmatch---of 3
filt_drmdetach---of 1
filt_drmkms---of 3
filt_drmread---of 3
filt_drmreaddetach---of 1
kasprintf---of 3
-----------
SUMMARY15%of 27
-----------
SUMMARY---of 0
km_alloc54%of 28
km_free50%of 16
uvm_km_alloc1---of 12
uvm_km_free100%of 1
uvm_km_init---of 4
uvm_km_kmemalloc_pla48%of 23
uvm_km_page_init---of 1
uvm_km_page_lateinit---of 1
uvm_km_pgremove46%of 11
uvm_km_pgremove_intrsafe58%of 7
uvm_km_suballoc---of 7
-----------
SUMMARY52%of 86
in_cksum52%of 29
-----------
SUMMARY52%of 29
pow2divide---of 4
uvm_pagezero_thread---of 10
uvm_pmemrange_addr_RBT_COMPARE---of 1
uvm_pmemrange_addr_cmp---of 1
uvm_pmemrange_find---of 7
uvm_pmemrange_use_cmp---of 3
uvm_pmemrange_use_insert---of 9
uvm_pmr_addr_RBT_COMPARE100%of 1
uvm_pmr_addr_cmp---of 1
uvm_pmr_allocpmr---of 5
uvm_pmr_extract_range80%of 15
uvm_pmr_findnextsegment---of 10
uvm_pmr_findprevsegment---of 10
uvm_pmr_freepageq58%of 21
uvm_pmr_freepages31%of 26
uvm_pmr_get1page52%of 60
uvm_pmr_getpages39%of 98
uvm_pmr_init---of 15
uvm_pmr_insert---of 3
uvm_pmr_insert_addr88%of 8
uvm_pmr_insert_size---of 3
uvm_pmr_isfree---of 11
uvm_pmr_nextsz---of 5
uvm_pmr_nfindsz---of 10
uvm_pmr_pg_to_memtype---of 1
uvm_pmr_pnaddr92%of 12
uvm_pmr_print---of 18
uvm_pmr_remove---of 3
uvm_pmr_remove_1strange27%of 49
uvm_pmr_remove_1strange_reverse40%of 20
uvm_pmr_remove_addr---of 1
uvm_pmr_remove_size---of 3
uvm_pmr_rootupdate7%of 43
uvm_pmr_size_RBT_COMPARE100%of 3
uvm_pmr_size_cmp---of 3
uvm_pmr_split---of 48
uvm_pmr_use_inc---of 22
uvm_wait_pla---of 8
uvm_wakeup_pla---of 8
-----------
SUMMARY42%of 356
PHYS_TO_VM_PAGE29%of 7
uvm_objtree_RBT_COMPARE100%of 1
uvm_page_init---of 19
uvm_page_owner_locked_p---of 5
uvm_page_physdump---of 4
uvm_page_physload---of 30
uvm_page_unbusy---of 19
uvm_pageactivate72%of 14
uvm_pagealloc55%of 22
uvm_pagealloc_multi40%of 10
uvm_pagealloc_pg47%of 28
uvm_pageboot_alloc---of 1
uvm_pageclean70%of 30
uvm_pagecmp---of 1
uvm_pagecopy100%of 1
uvm_pagecount43%of 7
uvm_pagedeactivate67%of 15
uvm_pagedequeue---of 5
uvm_pagefree72%of 7
uvm_pagelookup100%of 1
uvm_pagerealloc29%of 14
uvm_pagerealloc_multi---of 19
uvm_pageunwire60%of 10
uvm_pagewait50%of 4
uvm_pagewire70%of 13
uvm_pagezero100%of 1
uvm_pglistalloc29%of 7
uvm_pglistfree100%of 1
uvm_setpagesize---of 6
uvm_shutdown---of 1
vm_physseg_find---of 8
-----------
SUMMARY56%of 193
enc_clone_create---of 10
enc_clone_destroy---of 4
enc_getif---of 8
enc_getifa40%of 10
enc_ioctl---of 7
enc_output---of 1
enc_setif---of 13
enc_unsetif---of 11
encattach---of 1
-----------
SUMMARY40%of 10
ffs_bufatoff56%of 9
ffs_checkoverlap---of 13
ffs_clrblock40%of 5
ffs_fragacct80%of 10
ffs_isblock40%of 5
ffs_isfreeblock40%of 5
ffs_setblock40%of 5
ffs_vinit70%of 13
-----------
SUMMARY58%of 52
exec_script_makecmds77%of 46
-----------
SUMMARY77%of 46
pf_addr_copyout---of 5
pf_addr_setup---of 6
pf_begin_rules---of 5
pf_calc_chksum---of 5
pf_commit_queues---of 5
pf_commit_rules36%of 17
pf_create_queues15%of 28
pf_free_queues---of 4
pf_hash_rule---of 1
pf_hash_rule_addr---of 7
pf_kif_setup---of 5
pf_pool_copyin---of 1
pf_purge_rule---of 11
pf_qid2qname---of 5
pf_qid_unref---of 6
pf_qname2qid---of 1
pf_queue_manager---of 1
pf_remove_queues---of 7
pf_rm_rule74%of 26
pf_rollback_rules64%of 11
pf_rtlabel_add---of 4
pf_rtlabel_copyout---of 5
pf_rtlabel_remove---of 3
pf_rule_checkaf---of 13
pf_rule_copyin78%of 45
pf_rule_free---of 3
pf_states_clr---of 12
pf_states_get75%of 8
pf_sysctl60%of 5
pf_tag2tagname---of 5
pf_tag_ref---of 5
pf_tag_unref---of 6
pf_tagname2tag---of 1
pf_trans_set_commit---of 9
pf_validate_range---of 6
pfattach---of 3
pfclose100%of 1
pfioctl56%of 897
pfopen100%of 1
tag2tagname---of 5
tag_unref---of 6
tagname2tag67%of 15
-----------
SUMMARY56%of 1054
-----------
SUMMARY---of 0
-----------
SUMMARY---of 0
assertwaitok45%of 9
bdevsw_lookup---of 1
blktochr---of 6
cdevsw_lookup---of 1
chrtoblk100%of 4
enodev100%of 1
enoioctl---of 1
enosys---of 1
enxio100%of 1
eopnotsupp100%of 1
nullop100%of 1
-----------
SUMMARY71%of 17
mpls_sysctl100%of 1
-----------
SUMMARY100%of 1
ufs_inactive53%of 21
ufs_reclaim60%of 10
-----------
SUMMARY55%of 31
in_delayed_cksum63%of 8
in_ifcap_cksum---of 8
in_proto_cksum_out60%of 30
ip_ctloutput95%of 135
ip_fragment38%of 37
ip_freemoptions100%of 5
ip_getmoptions86%of 14
ip_insertoptions60%of 10
ip_mloopback---of 3
ip_multicast_if100%of 7
ip_optcopy---of 15
ip_output59%of 121
ip_output_ipsec_lookup24%of 13
ip_output_ipsec_pmtu_update---of 14
ip_output_ipsec_send---of 17
ip_pcbopts90%of 19
ip_setmoptions89%of 68
-----------
SUMMARY75%of 467
uao_create37%of 19
uao_detach44%of 16
uao_dropswap50%of 4
uao_dropswap_range28%of 29
uao_flush46%of 24
uao_get19%of 43
uao_init---of 1
uao_pagein_page---of 5
uao_reference100%of 3
uao_set_swslot22%of 23
uao_swap_off---of 38
-----------
SUMMARY32%of 161
dohooks---of 9
hashfree80%of 5
hashinit77%of 13
hook_disestablish---of 4
hook_establish---of 5
uiomove74%of 23
ureadc45%of 9
-----------
SUMMARY70%of 50
udp6_output85%of 39
-----------
SUMMARY85%of 39
vndattach---of 5
vndbdevsize---of 4
vndclear---of 3
vndclose67%of 3
vnddump---of 1
vndencrypt---of 6
vndencryptbuf---of 6
vndgetdisklabel100%of 3
vndioctl69%of 61
vndopen43%of 7
vndread100%of 1
vndsetcred---of 3
vndsize---of 1
vndstrategy50%of 20
vndwrite100%of 1
-----------
SUMMARY65%of 96
sys_obreak---of 7
uvm_coredump_walk_amap---of 13
uvm_coredump_walkmap---of 67
uvm_grow40%of 5
-----------
SUMMARY40%of 5
fd_motor_off---of 11
fd_motor_on---of 4
fd_nvtotype---of 3
fd_set_motor---of 11
fdactivate---of 5
fdattach---of 5
fdclose---of 4
fddump---of 1
fdfinish---of 7
fdformat---of 3
fdgetdisklabel---of 1
fdintr---of 88
fdioctl---of 18
fdopen15%of 14
fdprobe---of 7
fdread---of 1
fdretry---of 8
fdsize---of 1
fdstart---of 3
fdstrategy---of 16
fdtimeout---of 3
fdwrite---of 1
-----------
SUMMARY15%of 14
-----------
SUMMARY---of 0
ppp_ccp---of 27
ppp_clone_create---of 3
ppp_clone_destroy---of 4
ppp_dequeue---of 29
ppp_ifstart---of 1
ppp_pkt_dequeue---of 4
ppp_pkt_enqueue---of 4
ppp_pkt_list_init---of 1
ppp_pkt_mbuf---of 5
ppp_restart---of 1
pppalloc23%of 9
pppattach---of 1
pppdealloc---of 16
pppintr---of 77
pppioctl---of 66
pppoutput---of 37
ppppktin---of 4
pppsioctl---of 15
-----------
SUMMARY23%of 9
wsmux_add_mux70%of 10
wsmux_attach_sc42%of 12
wsmux_create---of 3
wsmux_depth34%of 6
wsmux_detach_sc43%of 7
wsmux_detach_sc_locked---of 6
wsmux_do_close---of 6
wsmux_do_displayioctl---of 10
wsmux_do_ioctl83%of 58
wsmux_do_open---of 8
wsmux_evsrc_set_display---of 5
wsmux_get_layout---of 1
wsmux_getmux23%of 18
wsmux_mux_close34%of 6
wsmux_mux_open23%of 9
wsmux_set_display---of 11
wsmux_set_layout100%of 3
wsmuxattach---of 1
wsmuxclose86%of 7
wsmuxioctl100%of 1
wsmuxkqfilter100%of 3
wsmuxopen65%of 14
wsmuxread67%of 3
-----------
SUMMARY62%of 157
udp6_ctlinput---of 22
udp_abort---of 1
udp_attach50%of 8
udp_bind100%of 1
udp_connect50%of 10
udp_ctlinput---of 15
udp_detach67%of 3
udp_disconnect100%of 8
udp_init---of 1
udp_input---of 110
udp_lock67%of 3
udp_notify---of 1
udp_output65%of 39
udp_sbappend---of 26
udp_send80%of 10
udp_shutdown100%of 1
udp_sysctl100%of 8
udp_sysctl_udpstat---of 1
udp_unlock67%of 3
-----------
SUMMARY71%of 94
fill_file92%of 68
fill_kproc83%of 58
hw_sysctl82%of 104
kern_sysctl85%of 163
kern_sysctl_dirs87%of 60
sys_sysctl91%of 22
sysctl__string92%of 12
sysctl_audio---of 10
sysctl_bounded_arr84%of 6
sysctl_cptime2---of 12
sysctl_cpustats---of 14
sysctl_diskinit22%of 19
sysctl_doproc90%of 48
sysctl_file75%of 113
sysctl_int100%of 8
sysctl_int_bounded93%of 14
sysctl_int_lower93%of 13
sysctl_intrcnt---of 1
sysctl_proc_args56%of 36
sysctl_proc_cwd86%of 14
sysctl_proc_nobroadcastkill89%of 18
sysctl_proc_vmmap62%of 18
sysctl_quad---of 8
sysctl_rdint100%of 6
sysctl_rdquad100%of 6
sysctl_rdstring100%of 6
sysctl_rdstruct100%of 6
sysctl_securelevel_int47%of 13
sysctl_sensors79%of 14
sysctl_string100%of 8
sysctl_struct100%of 8
sysctl_sysvipc86%of 21
sysctl_tstring100%of 1
sysctl_utc_offset---of 5
sysctl_video---of 10
-----------
SUMMARY82%of 883
-----------
SUMMARY---of 0
tcp6_ctlinput---of 37
tcp6_mtudisc_callback---of 1
tcp_close43%of 7
tcp_ctlinput---of 34
tcp_drop80%of 5
tcp_freeq---of 4
tcp_init---of 5
tcp_mtudisc---of 8
tcp_mtudisc_increase---of 4
tcp_newtcpcb58%of 7
tcp_notify---of 9
tcp_respond---of 20
tcp_rscale14%of 15
tcp_set_iss_tsm100%of 3
tcp_signature---of 10
tcp_signature_apply---of 1
tcp_signature_tdb_attach---of 1
tcp_signature_tdb_init---of 4
tcp_signature_tdb_input---of 1
tcp_signature_tdb_output---of 1
tcp_signature_tdb_zeroize---of 3
tcp_template60%of 10
-----------
SUMMARY47%of 47
config_activate_children---of 33
config_attach---of 28
config_deactivate---of 8
config_defer---of 8
config_detach---of 49
config_detach_children---of 7
config_found_sm---of 4
config_init---of 1
config_make_softc---of 18
config_mountroot---of 8
config_pending_decr---of 4
config_pending_incr---of 1
config_process_deferred_children---of 8
config_process_deferred_mountroot---of 4
config_rootfound---of 8
config_rootsearch---of 7
config_scan---of 15
config_search---of 22
config_suspend---of 5
config_suspend_all---of 35
device_lookup67%of 6
device_mainbus---of 3
device_mpath---of 3
device_ref100%of 1
device_unref67%of 3
mapply---of 16
-----------
SUMMARY70%of 10
b_to_q84%of 24
catq75%of 8
clalloc100%of 3
clfree---of 5
clrbits---of 12
firstc80%of 5
getc88%of 8
ndflush20%of 10
ndqb---of 13
nextc80%of 10
putc89%of 9
q_to_b48%of 23
unputc88%of 8
-----------
SUMMARY71%of 108
carp6_proto_input---of 3
carp6_proto_input_if---of 11
carp_addr_updated---of 30
carp_carpdev_state---of 26
carp_check_dup_vhids---of 17
carp_clone_create---of 3
carp_clone_destroy---of 1
carp_del_all_timeouts---of 4
carp_destroy_vhosts---of 11
carp_enqueue---of 9
carp_ether_addmulti---of 15
carp_ether_delmulti---of 15
carp_ether_purgemulti---of 7
carp_group_demote_adj---of 10
carp_group_demote_count---of 5
carp_hmac_generate---of 1
carp_hmac_prepare---of 4
carp_hmac_prepare_ctx---of 35
carp_hmac_verify---of 3
carp_iamatch---of 1
carp_ifgattr_ioctl---of 8
carp_ifgroup_ioctl---of 7
carp_input---of 19
carp_ioctl---of 94
carp_join_multicast---of 4
carp_join_multicast6---of 7
carp_lsdrop17%of 12
carp_master_down---of 16
carp_multicast_cleanup---of 17
carp_new_vhost---of 10
carp_ourether---of 11
carp_output---of 5
carp_prepare_ad---of 3
carp_proto_input---of 3
carp_proto_input_c---of 60
carp_proto_input_if---of 17
carp_sc_ref---of 1
carp_sc_unref---of 1
carp_send_ad---of 56
carp_send_ad_all---of 15
carp_send_arp---of 6
carp_send_na---of 6
carp_set_addr---of 14
carp_set_addr6---of 17
carp_set_enaddr---of 28
carp_set_ifp---of 21
carp_set_state---of 12
carp_set_state_all---of 6
carp_set_vhe_enaddr---of 6
carp_setrun---of 27
carp_setrun_all---of 4
carp_start---of 12
carp_sysctl100%of 5
carp_sysctl_carpstat---of 1
carp_timer_ad---of 1
carp_timer_down---of 1
carp_transmit---of 8
carp_update_lsmask---of 9
carp_vh_ref---of 1
carp_vh_unref---of 3
carp_vhe_match---of 4
carp_vhe_send_ad_all---of 6
carp_vhids_ioctl---of 29
carpattach---of 1
carpdetach---of 20
-----------
SUMMARY42%of 17
in6_pcbaddrisavail91%of 22
in6_pcbconnect76%of 33
in6_pcbhash100%of 1
in6_pcbhash_lookup50%of 18
in6_pcblookup67%of 3
in6_pcblookup_listen50%of 12
in6_pcbnotify---of 59
in6_peeraddr100%of 1
in6_setpeeraddr100%of 1
in6_setsockaddr100%of 1
in6_sockaddr100%of 1
-----------
SUMMARY73%of 93
copyaddrout---of 5
doaccept80%of 50
getsock80%of 5
recvit83%of 47
sendit77%of 55
sockargs78%of 9
sys_accept100%of 1
sys_accept4---of 3
sys_bind67%of 12
sys_connect73%of 33
sys_getpeername60%of 15
sys_getrtable---of 1
sys_getsockname60%of 15
sys_getsockopt82%of 16
sys_listen58%of 7
sys_recvfrom100%of 4
sys_recvmmsg86%of 41
sys_recvmsg94%of 15
sys_sendmmsg87%of 22
sys_sendmsg82%of 16
sys_sendto100%of 1
sys_setrtable---of 8
sys_setsockopt67%of 15
sys_shutdown84%of 6
sys_socket67%of 9
sys_socketpair78%of 27
sys_ypconnect---of 31
ypsockargs---of 8
-----------
SUMMARY78%of 421
if_addgroup71%of 17
if_addrhook_add100%of 1
if_addrhook_del100%of 1
if_addrhooks_run100%of 1
if_alloc_sadl67%of 3
if_attach100%of 1
if_attach_common67%of 15
if_attach_ifq---of 1
if_attach_iqueues---of 5
if_attach_queues---of 6
if_attachdomain---of 6
if_attachhead---of 1
if_attachsetup48%of 17
if_clone_attach---of 4
if_clone_create59%of 12
if_clone_destroy75%of 8
if_clone_list---of 7
if_clone_lookup86%of 35
if_congested67%of 3
if_congestion---of 1
if_counters_alloc67%of 3
if_counters_free---of 3
if_creategroup---of 3
if_createrdomain56%of 9
if_deactivate100%of 1
if_delgroup64%of 11
if_detach38%of 45
if_detached_ioctl---of 1
if_detached_qstart---of 1
if_detachhook_add---of 3
if_detachhook_del---of 1
if_down60%of 5
if_downall---of 6
if_enqueue40%of 5
if_enqueue_ifq60%of 5
if_free_sadl---of 3
if_get80%of 5
if_getdata56%of 9
if_getgroup---of 9
if_getgroupattribs---of 5
if_getgrouplist---of 9
if_getgroupmembers42%of 12
if_group_egress_build29%of 21
if_group_routechange60%of 10
if_hooks_run58%of 7
if_idxmap_alloc50%of 20
if_idxmap_init---of 4
if_idxmap_insert40%of 5
if_idxmap_remove40%of 5
if_ifp_dtor67%of 3
if_input---of 1
if_input_local63%of 8
if_input_process43%of 7
if_isconnected---of 12
if_link_state_change67%of 3
if_linkstate---of 3
if_linkstate_task---of 6
if_linkstatehook_add---of 3
if_linkstatehook_del---of 1
if_map_dtor75%of 4
if_netisr---of 21
if_output_local100%of 3
if_put100%of 3
if_qstart_compat100%of 1
if_ref---of 1
if_remove---of 3
if_rtrequest_dummy---of 1
if_rxhprio_l2_check---of 4
if_rxhprio_l3_check---of 1
if_rxr_get43%of 7
if_rxr_info_ioctl---of 5
if_rxr_init---of 1
if_rxr_ioctl---of 7
if_rxr_livelocked---of 4
if_setgroupattribs---of 8
if_setlladdr---of 3
if_setrdomain58%of 14
if_slowtimo---of 6
if_start---of 3
if_txhprio_l2_check---of 1
if_txhprio_l3_check---of 1
if_unit60%of 5
if_up72%of 7
if_vinput100%of 6
if_watchdog_task---of 6
ifa_add100%of 1
ifa_del100%of 1
ifa_ifwithaddr91%of 11
ifa_ifwithdstaddr47%of 13
ifa_print_all---of 10
ifa_update_broadaddr67%of 3
ifaof_ifpforaddr50%of 18
ifconf95%of 19
ifinit---of 6
ifioctl83%of 207
ifioctl_get77%of 47
ifnewlladdr50%of 10
ifpromisc80%of 10
net_tq67%of 3
niq_enlist---of 4
niq_enqueue50%of 4
niq_init---of 1
p2p_bpf_mtap---of 1
p2p_input---of 5
p2p_rtrequest---of 14
-----------
SUMMARY68%of 710
rip_abort---of 3
rip_attach56%of 9
rip_bind75%of 8
rip_chkhdr30%of 17
rip_connect100%of 3
rip_ctloutput96%of 21
rip_detach75%of 4
rip_disconnect67%of 3
rip_init---of 1
rip_input---of 34
rip_output65%of 14
rip_send84%of 6
rip_shutdown100%of 1
-----------
SUMMARY69%of 86
ulpt_attach---of 26
ulpt_detach---of 11
ulpt_do_write---of 11
ulpt_input---of 3
ulpt_load_firmware---of 3
ulpt_match---of 7
ulpt_reset---of 3
ulpt_status---of 1
ulpt_statusmsg---of 5
ulpt_ucode_loader_hp---of 9
ulptclose---of 9
ulptopen8%of 26
ulptwrite---of 5
-----------
SUMMARY8%of 26
dosendsyslog22%of 32
filt_logrdetach---of 1
filt_logread---of 3
initconsbuf---of 1
initmsgbuf---of 14
logclose---of 4
logioctl---of 15
logkqfilter---of 3
logopen67%of 3
logread---of 17
logstash_insert---of 7
logstash_remove---of 9
logstash_sendsyslog---of 5
logtick---of 9
logwakeup100%of 1
msgbuf_getlen---of 3
msgbuf_putchar58%of 7
msgbuf_putchar_locked---of 6
sys_sendsyslog50%of 8
-----------
SUMMARY36%of 51
ffs1_balloc---of 84
ffs2_balloc29%of 94
ffs_balloc67%of 3
-----------
SUMMARY30%of 97
uvm_analloc67%of 3
uvm_anfree_list40%of 20
uvm_anon_dropswap50%of 6
uvm_anon_init---of 1
uvm_anon_pagein---of 9
uvm_anon_release---of 10
uvm_anwait---of 1
-----------
SUMMARY45%of 29
sbappend46%of 11
sbappendaddr53%of 36
sbappendcontrol54%of 26
sbappendrecord50%of 16
sbappendstream56%of 9
sbchecklowmem50%of 4
sbcheckreserve50%of 6
sbcompress71%of 31
sbcreatecontrol---of 6
sbdrop40%of 33
sbdroprecord70%of 10
sbflush34%of 12
sblock75%of 12
sbrelease100%of 1
sbreserve80%of 10
sbunlock88%of 8
sbwait84%of 6
soassertlocked84%of 6
socantrcvmore84%of 6
socantsendmore84%of 6
soisconnected40%of 25
soisconnecting67%of 6
soisdisconnected84%of 6
soisdisconnecting67%of 6
solock100%of 4
solock_pair29%of 14
solock_persocket100%of 1
solock_shared100%of 6
sonewconn24%of 52
soqinsque36%of 14
soqremque77%of 17
soreserve70%of 20
sosleep_nsec80%of 10
sounlock100%of 4
sounlock_shared100%of 6
sowakeup92%of 12
-----------
SUMMARY58%of 452
gre_send60%of 10
-----------
SUMMARY60%of 10
bio_delegate_ioctl---of 1
bio_error---of 4
bio_info---of 4
bio_lookup---of 5
bio_register---of 4
bio_status---of 4
bio_status_init---of 1
bio_unregister---of 7
bio_validate---of 5
bio_warn---of 4
bioattach---of 1
bioclose100%of 1
bioioctl43%of 21
bioopen100%of 1
-----------
SUMMARY48%of 23
fill_vmmap100%of 3
uvm_atopg---of 4
uvm_exit---of 1
uvm_init_limits---of 1
uvm_kernacc---of 1
uvm_pause---of 3
uvm_swapout_threads---of 19
uvm_uarea_alloc---of 1
uvm_uarea_free---of 1
uvm_vslock100%of 3
uvm_vslock_device34%of 18
uvm_vsunlock67%of 3
uvm_vsunlock_device50%of 4
-----------
SUMMARY52%of 31
dofilereadv71%of 24
dofilewritev74%of 26
doppoll83%of 34
dopselect95%of 58
iovec_copyin85%of 13
iovec_free100%of 3
pollout---of 4
ppollcollect54%of 28
ppollregister89%of 18
ppollregister_evts65%of 17
pselcollect60%of 10
pselregister85%of 20
selwakeup100%of 3
sys_ioctl89%of 27
sys_poll100%of 4
sys_ppoll---of 12
sys_pselect---of 12
sys_read100%of 3
sys_readv100%of 5
sys_select89%of 9
sys_utrace---of 1
sys_write100%of 3
sys_writev100%of 5
-----------
SUMMARY82%of 310
lpt_activate---of 13
lpt_attach_common---of 1
lpt_detach_common---of 3
lpt_not_ready---of 5
lpt_port_test---of 3
lptclose---of 5
lptintr---of 15
lptopen15%of 20
lptpushbytes---of 22
lptwakeup---of 3
lptwrite---of 5
-----------
SUMMARY15%of 20
uvm_map52%of 83
uvm_map_addr_RBT_AUGMENT89%of 9
uvm_map_addr_RBT_COMPARE100%of 1
uvm_map_addr_augment---of 9
uvm_map_addr_augment_get---of 5
uvm_map_advice67%of 21
uvm_map_boundary80%of 15
uvm_map_checkprot59%of 17
uvm_map_clean62%of 54
uvm_map_clip_end50%of 4
uvm_map_clip_start50%of 4
uvm_map_create---of 1
uvm_map_deallocate---of 4
uvm_map_entrybyaddr---of 7
uvm_map_extract40%of 33
uvm_map_fill_vmmap50%of 14
uvm_map_findspace50%of 6
uvm_map_fix_space61%of 23
uvm_map_freelist_update---of 4
uvm_map_freelist_update_clear72%of 7
uvm_map_freelist_update_refill---of 4
uvm_map_hint---of 4
uvm_map_inentry58%of 7
uvm_map_inentry_fix37%of 19
uvm_map_inentry_pc67%of 3
uvm_map_inentry_recheck---of 4
uvm_map_inentry_sp---of 3
uvm_map_inherit69%of 22
uvm_map_init---of 3
uvm_map_is_stack_remappable63%of 16
uvm_map_isavail52%of 41
uvm_map_kmem_grow47%of 13
uvm_map_lock_entry100%of 5
uvm_map_lookup_entry56%of 9
uvm_map_mkentry78%of 9
uvm_map_mquery67%of 36
uvm_map_pageable60%of 52
uvm_map_pageable_all58%of 26
uvm_map_pageable_pgon---of 7
uvm_map_pageable_wire60%of 54
uvm_map_pie---of 3
uvm_map_printit---of 9
uvm_map_protect64%of 82
uvm_map_reference---of 1
uvm_map_remap_as_stack---of 5
uvm_map_set_uaddr---of 9
uvm_map_setup45%of 9
uvm_map_setup_entries---of 1
uvm_map_setup_md---of 1
uvm_map_splitentry43%of 14
uvm_map_submap---of 24
uvm_map_syscall---of 23
uvm_map_teardown73%of 11
uvm_map_uaddr46%of 37
uvm_map_uaddr_e---of 1
uvm_map_unlock_entry100%of 5
uvm_map_vmspace_update---of 11
uvm_mapanon44%of 55
uvm_mapent_addr_insert55%of 11
uvm_mapent_addr_remove50%of 10
uvm_mapent_alloc47%of 13
uvm_mapent_clone63%of 8
uvm_mapent_forkcopy---of 20
uvm_mapent_forkshared---of 3
uvm_mapent_forkzero---of 6
uvm_mapent_free67%of 6
uvm_mapent_free_insert88%of 16
uvm_mapent_free_remove60%of 10
uvm_mapent_isjoinable43%of 19
uvm_mapent_merge67%of 3
uvm_mapent_mkfree64%of 11
uvm_mapent_share---of 3
uvm_mapent_tryjoin78%of 9
uvm_object_printit---of 10
uvm_page_printit---of 18
uvm_share57%of 30
uvm_unmap84%of 6
uvm_unmap_detach75%of 16
uvm_unmap_detach_intrsafe58%of 7
uvm_unmap_kill_entry---of 1
uvm_unmap_kill_entry_withlock85%of 20
uvm_unmap_remove65%of 34
uvmspace_addref67%of 3
uvmspace_alloc100%of 1
uvmspace_dused---of 5
uvmspace_exec---of 12
uvmspace_fork---of 26
uvmspace_free75%of 4
uvmspace_init---of 4
uvmspace_share---of 3
vm_map_busy_ln---of 3
vm_map_downgrade_ln---of 3
vm_map_lock_ln50%of 8
vm_map_lock_read_ln100%of 3
vm_map_lock_try_ln---of 8
vm_map_unbusy_ln---of 4
vm_map_unlock_ln100%of 3
vm_map_unlock_read_ln100%of 3
vm_map_upgrade_ln---of 3
-----------
SUMMARY60%of 1070
bufq_dequeue100%of 1
bufq_destroy---of 7
bufq_done---of 7
bufq_drain---of 4
bufq_fifo_create---of 3
bufq_fifo_dequeue50%of 4
bufq_fifo_destroy---of 1
bufq_fifo_peek100%of 1
bufq_fifo_queue100%of 1
bufq_fifo_requeue---of 3
bufq_init---of 8
bufq_nscan_create---of 3
bufq_nscan_dequeue---of 16
bufq_nscan_destroy---of 1
bufq_nscan_peek---of 3
bufq_nscan_queue---of 8
bufq_nscan_requeue---of 3
bufq_nscan_resort---of 12
bufq_peek100%of 1
bufq_queue50%of 4
bufq_quiesce---of 7
bufq_requeue---of 1
bufq_restart---of 4
bufq_simple_nscan---of 11
bufq_switch---of 7
bufq_wait40%of 5
-----------
SUMMARY59%of 17
uvm_fault67%of 12
uvm_fault_check48%of 65
uvm_fault_lower55%of 81
uvm_fault_lower_lookup80%of 10
uvm_fault_unwire100%of 1
uvm_fault_unwire_locked58%of 21
uvm_fault_upper27%of 42
uvm_fault_upper_lookup71%of 17
uvm_fault_wire84%of 6
uvmfault_anonget3%of 68
uvmfault_init---of 1
uvmfault_lookup82%of 11
uvmfault_relock---of 4
uvmfault_unlockall67%of 12
uvmfault_unlockmaps50%of 10
uvmfault_update_stats---of 7
-----------
SUMMARY44%of 356
filt_generic_detach---of 1
filt_generic_readwrite---of 3
vop_generic_abortop100%of 3
vop_generic_bmap---of 7
vop_generic_bwrite100%of 1
vop_generic_kqfilter---of 3
vop_generic_lookup---of 1
vop_generic_revoke17%of 18
-----------
SUMMARY32%of 22
mpls_do_error---of 21
mpls_input6%of 56
mpls_input_local---of 3
mpls_ip6_adjttl---of 4
mpls_ip_adjttl---of 4
-----------
SUMMARY6%of 56
mem_ioctl---of 13
mem_range_attr_get---of 4
mem_range_attr_set---of 3
mmclose67%of 3
mmioctl60%of 5
mmmmap17%of 12
mmopen73%of 11
mmrw43%of 21
-----------
SUMMARY47%of 52
-----------
SUMMARY---of 0
ch_exchange---of 14
ch_get_params---of 5
ch_get_quirks---of 3
ch_getelemstatus---of 4
ch_interpret_sense---of 7
ch_move---of 9
ch_position---of 6
ch_usergetelemstatus---of 69
chattach---of 3
chclose---of 1
chioctl---of 13
chmatch---of 1
chopen29%of 7
-----------
SUMMARY29%of 7
kstat_copy---of 1
kstat_cpu_enter---of 1
kstat_cpu_leave---of 1
kstat_create34%of 21
kstat_destroy100%of 1
kstat_id_tree_RBT_COMPARE100%of 1
kstat_init---of 3
kstat_install43%of 7
kstat_kv_init---of 1
kstat_kv_unit_init---of 3
kstat_nm_tree_RBT_COMPARE43%of 7
kstat_pv_tree_RBT_COMPARE43%of 7
kstat_read---of 1
kstat_read_nop---of 1
kstat_remove---of 3
kstat_set_cpu---of 3
kstat_set_mutex67%of 3
kstat_set_rlock---of 3
kstat_set_wlock---of 3
kstat_strcheck---of 9
kstatattach---of 1
kstatclose100%of 1
kstatioc_enter---of 5
kstatioc_find_id---of 5
kstatioc_find_nm---of 5
kstatioc_find_pv---of 5
kstatioc_leave---of 15
kstatioc_nfind_id---of 5
kstatioc_nfind_nm---of 5
kstatioc_nfind_pv---of 5
kstatioctl8%of 27
kstatopen100%of 1
-----------
SUMMARY32%of 76
ufs_checkpath40%of 15
ufs_dirbad---of 3
ufs_dirbadentry---of 11
ufs_dirempty54%of 15
ufs_direnter43%of 63
ufs_dirremove37%of 19
ufs_dirrewrite45%of 9
ufs_lookup66%of 128
ufs_makedirentry40%of 5
-----------
SUMMARY55%of 254
checkdirs---of 15
copyout_statfs---of 5
dochflagsat---of 4
dofaccessat100%of 13
dofchmodat84%of 6
dofchownat50%of 10
dofstatat70%of 10
dofutimens---of 8
dolinkat100%of 7
domkdirat84%of 6
domknodat87%of 22
doopenat66%of 32
doreadlinkat100%of 4
dorenameat95%of 17
dosymlinkat100%of 6
dotruncate45%of 9
dounlinkat84%of 12
dounmount---of 19
dounmount_leaf---of 15
doutimensat---of 4
dovchflags67%of 9
dovutimens91%of 21
getvnode72%of 7
sys___realpath---of 15
sys___tmpfd---of 16
sys_access---of 1
sys_chdir100%of 5
sys_chflags100%of 3
sys_chflagsat100%of 4
sys_chmod100%of 1
sys_chown100%of 1
sys_chroot86%of 7
sys_faccessat100%of 1
sys_fchdir62%of 13
sys_fchflags63%of 8
sys_fchmod67%of 12
sys_fchmodat100%of 1
sys_fchown53%of 17
sys_fchownat100%of 1
sys_fhopen---of 37
sys_fhstat---of 7
sys_fhstatfs---of 7
sys_fstatat---of 1
sys_fstatfs---of 15
sys_fsync59%of 12
sys_ftruncate70%of 10
sys_futimens---of 15
sys_futimes---of 9
sys_getdents72%of 14
sys_getfh---of 5
sys_getfsstat---of 16
sys_lchown45%of 9
sys_link100%of 1
sys_linkat100%of 1
sys_lseek86%of 7
sys_lstat100%of 1
sys_mkdir100%of 1
sys_mkdirat100%of 1
sys_mkfifo---of 1
sys_mkfifoat---of 1
sys_mknod100%of 1
sys_mknodat100%of 1
sys_mount---of 37
sys_open100%of 1
sys_openat100%of 1
sys_pad_ftruncate100%of 1
sys_pad_lseek100%of 1
sys_pad_pread100%of 3
sys_pad_preadv100%of 3
sys_pad_pwrite100%of 3
sys_pad_pwritev100%of 3
sys_pad_truncate100%of 1
sys_pathconf---of 3
sys_pread---of 3
sys_preadv---of 3
sys_pwrite---of 3
sys_pwritev---of 3
sys_quotactl---of 3
sys_readlink100%of 1
sys_readlinkat100%of 1
sys_rename100%of 1
sys_renameat100%of 1
sys_revoke---of 12
sys_rmdir100%of 1
sys_stat100%of 1
sys_statfs---of 7
sys_symlink100%of 1
sys_symlinkat100%of 1
sys_sync13%of 8
sys_truncate100%of 4
sys_umask---of 3
sys_unlink100%of 1
sys_unlinkat100%of 1
sys_unmount---of 7
sys_unveil95%of 34
sys_utimensat89%of 18
sys_utimes91%of 11
-----------
SUMMARY79%of 426
minphys100%of 3
physio64%of 25
-----------
SUMMARY68%of 28
in_addhost---of 1
in_addmulti84%of 12
in_broadcast93%of 13
in_canforward100%of 4
in_control100%of 4
in_delmulti84%of 6
in_hasmulti78%of 9
in_ifdetach88%of 8
in_ifinit68%of 28
in_ifscrub---of 5
in_insert_prefix---of 4
in_ioctl84%of 42
in_ioctl_change_ifaddr71%of 51
in_ioctl_get90%of 20
in_ioctl_set_ifaddr53%of 17
in_len2mask---of 10
in_mask2len---of 14
in_nam2sin80%of 5
in_prefixlen2mask100%of 3
in_purgeaddr56%of 9
in_remove_prefix---of 3
in_sa2sin---of 4
in_scrubhost---of 1
in_socktrim---of 4
-----------
SUMMARY78%of 231
mpls_do_cksum---of 3
mpls_getttl---of 9
mpls_output39%of 31
-----------
SUMMARY39%of 31
ifa_ifwithroute79%of 14
rcb_ref100%of 1
rcb_unref100%of 1
route_abort---of 1
route_arp_conflict---of 6
route_attach50%of 4
route_cleargateway---of 5
route_ctloutput96%of 24
route_detach67%of 9
route_disconnect100%of 1
route_input83%of 35
route_output86%of 62
route_peeraddr100%of 1
route_prinit---of 1
route_rcvd100%of 4
route_send84%of 6
route_shutdown100%of 1
route_sockaddr100%of 1
rt_setsource64%of 11
rtm_80211info---of 4
rtm_addr40%of 5
rtm_getifa43%of 21
rtm_getmetrics---of 4
rtm_ifannounce50%of 4
rtm_ifchg50%of 4
rtm_miss40%of 5
rtm_msg180%of 20
rtm_msg248%of 19
rtm_output55%of 70
rtm_proposal---of 3
rtm_report63%of 24
rtm_send60%of 10
rtm_senddesync50%of 6
rtm_senddesync_timer---of 1
rtm_sendup60%of 5
rtm_setmetrics---of 6
rtm_validate_proposal60%of 32
rtm_xaddrs95%of 37
sysctl_dumpentry55%of 24
sysctl_iflist64%of 22
sysctl_ifnames---of 11
sysctl_rtable75%of 43
sysctl_rtable_rtstat---of 1
sysctl_source---of 9
-----------
SUMMARY70%of 526
-----------
SUMMARY---of 0
readdisklabel56%of 9
writedisklabel---of 4
-----------
SUMMARY56%of 9
udv_attach17%of 18
udv_detach---of 10
udv_fault---of 10
udv_flush---of 1
udv_reference---of 1
-----------
SUMMARY17%of 18
loinput---of 4
loioctl100%of 7
loop_clone_create---of 3
loop_clone_destroy19%of 11
loopattach---of 1
looutput80%of 5
lortrequest75%of 4
-----------
SUMMARY60%of 27
pf_create_fragment46%of 11
pf_fillup_fragment23%of 96
pf_find_fragment65%of 20
pf_flush_fragments---of 7
pf_frag_tree_RB_FIND---of 7
pf_frag_tree_RB_INSERT---of 10
pf_frag_tree_RB_INSERT_COLOR8%of 27
pf_frag_tree_RB_MINMAX---of 4
pf_frag_tree_RB_NEXT---of 8
pf_frag_tree_RB_NFIND---of 7
pf_frag_tree_RB_PREV---of 8
pf_frag_tree_RB_REMOVE24%of 17
pf_frag_tree_RB_REMOVE_COLOR8%of 56
pf_free_fragment40%of 10
pf_frent_holes---of 7
pf_frent_insert25%of 20
pf_frent_previous19%of 11
pf_frent_remove---of 18
pf_frnode_tree_RB_FIND---of 11
pf_frnode_tree_RB_INSERT36%of 14
pf_frnode_tree_RB_INSERT_COLOR8%of 27
pf_frnode_tree_RB_MINMAX---of 4
pf_frnode_tree_RB_NEXT---of 8
pf_frnode_tree_RB_NFIND---of 11
pf_frnode_tree_RB_PREV---of 8
pf_frnode_tree_RB_REMOVE24%of 17
pf_frnode_tree_RB_REMOVE_COLOR---of 56
pf_join_fragment---of 8
pf_normalize_init---of 1
pf_normalize_ip50%of 14
pf_normalize_ip660%of 5
pf_normalize_mss---of 8
pf_normalize_tcp85%of 13
pf_normalize_tcp_cleanup---of 5
pf_normalize_tcp_init---of 12
pf_normalize_tcp_stateful---of 105
pf_purge_expired_fragments---of 9
pf_reassemble25%of 12
pf_reassemble620%of 20
pf_refragment6---of 12
pf_scrub32%of 16
-----------
SUMMARY26%of 406
radio_attach_mi---of 1
radioactivate---of 3
radioattach---of 1
radioclose---of 3
radiodetach---of 5
radioioctl---of 13
radioopen34%of 6
radioprint---of 3
radioprobe---of 1
-----------
SUMMARY34%of 6
ksymsattach---of 11
ksymsclose100%of 1
ksymsopen100%of 4
ksymsread84%of 6
-----------
SUMMARY91%of 11
sd_buf_done---of 16
sd_cmd_rw10---of 1
sd_cmd_rw12---of 1
sd_cmd_rw16---of 1
sd_cmd_rw6---of 1
sd_flush---of 6
sd_get_parms---of 62
sd_interpret_sense---of 11
sd_ioctl_cache---of 15
sd_ioctl_inquiry---of 4
sd_read_cap---of 6
sd_read_cap_10---of 6
sd_read_cap_16---of 6
sd_thin_pages---of 15
sd_thin_params---of 6
sd_vpd_block_limits---of 6
sd_vpd_thin---of 4
sdactivate---of 17
sdattach---of 19
sdclose32%of 19
sddetach---of 1
sddump---of 13
sdgetdisklabel---of 37
sdioctl10%of 43
sdmatch---of 1
sdminphys34%of 9
sdopen21%of 29
sdread100%of 1
sdsize---of 12
sdstart43%of 14
sdstrategy45%of 9
sdwrite100%of 1
viscpy---of 7
-----------
SUMMARY25%of 125
add_m6fc73%of 11
add_m6if80%of 15
del_m6fc63%of 8
del_m6if82%of 11
get_mif6_cnt---of 8
get_sg6_cnt63%of 8
ip6_mdq---of 17
ip6_mforward---of 29
ip6_mrouter_detach67%of 3
ip6_mrouter_done77%of 13
ip6_mrouter_get100%of 1
ip6_mrouter_init---of 6
ip6_mrouter_set87%of 23
mf6c_add---of 9
mf6c_add_route60%of 5
mf6c_expire_route---of 5
mf6c_find---of 8
mf6c_update76%of 33
mrouter6_rtwalk_delete100%of 1
mrt6_iflookupbymif---of 8
mrt6_ioctl82%of 11
mrt6_mcast_add75%of 12
mrt6_mcast_del---of 3
mrt6_rtwalk_mf6csysctl14%of 23
mrt6_sysctl_mfc78%of 9
mrt6_sysctl_mif50%of 12
phyint_send6---of 10
socket6_send---of 4
-----------
SUMMARY68%of 199
chkdqchg---of 17
chkdquot30%of 10
chkiqchg---of 17
dqget5%of 49
dqref---of 1
dqrele---of 7
dqsync---of 13
getinoquota39%of 13
getquota---of 12
qsync---of 4
qsync_vnode---of 9
quotaoff---of 6
quotaoff_vnode---of 9
quotaon---of 19
quotaon_vnode---of 5
setquota---of 28
setuse---of 24
ufs_quota_alloc_blocks250%of 22
ufs_quota_alloc_inode248%of 21
ufs_quota_delete24%of 13
ufs_quota_free_blocks247%of 13
ufs_quota_free_inode232%of 16
ufs_quota_init---of 1
ufs_quotactl---of 20
-----------
SUMMARY29%of 157
-----------
SUMMARY---of 0
cansignal---of 23
coredump---of 22
coredump_unmap---of 1
coredump_write---of 6
cursig6%of 38
dosigsuspend---of 3
execsigs---of 9
filt_sigattach100%of 3
filt_sigdetach100%of 1
filt_signal100%of 4
initsiginfo---of 7
killpg1---of 67
pgsigio65%of 20
pgsignal25%of 8
postsig---of 25
postsig_done---of 5
proc_stop---of 5
proc_stop_sweep---of 7
psignal---of 1
ptsignal14%of 68
setsigctx---of 1
setsigvec---of 24
sigabort---of 1
sigactsfree---of 1
sigactsinit---of 1
sigexit---of 5
siginit---of 6
sigio_copy34%of 12
sigio_del---of 6
sigio_free58%of 7
sigio_freelist80%of 10
sigio_getown100%of 3
sigio_setown85%of 19
sigio_unlink80%of 10
sigismasked---of 3
signal_init---of 3
sigonstack---of 3
sigstkinit---of 1
single_thread_check100%of 1
single_thread_check_locked77%of 13
single_thread_clear42%of 12
single_thread_set38%of 27
single_thread_wait---of 4
sys___thrsigdivert---of 16
sys_kill---of 34
sys_nosys---of 1
sys_sigaction---of 31
sys_sigaltstack---of 16
sys_sigpending---of 1
sys_sigprocmask---of 6
sys_sigsuspend---of 4
sys_thrkill---of 12
trapsignal---of 32
userret47%of 15
-----------
SUMMARY41%of 271
unveil_add75%of 31
unveil_add_name---of 1
unveil_add_name_unlocked100%of 3
unveil_add_vnode86%of 7
unveil_check_component78%of 18
unveil_check_final80%of 54
unveil_copy---of 13
unveil_covered---of 7
unveil_delete_names---of 4
unveil_destroy---of 10
unveil_find_cover71%of 17
unveil_flagmatch---of 6
unveil_lookup---of 10
unveil_namelookup---of 3
unveil_parsepermissions---of 9
unveil_removevnode---of 15
unveil_setflags---of 1
unveil_start_relative62%of 13
unvname_delete---of 1
unvname_new---of 1
unvname_rbt_RBT_COMPARE100%of 3
-----------
SUMMARY77%of 146
pvclock_activate---of 4
pvclock_attach---of 5
pvclock_get_timecount40%of 5
pvclock_match---of 4
-----------
SUMMARY40%of 5
virtio_alloc_vq---of 17
virtio_check_vqs---of 9
virtio_dequeue63%of 8
virtio_dequeue_commit75%of 4
virtio_device_string---of 3
virtio_enqueue72%of 7
virtio_enqueue_abort---of 5
virtio_enqueue_commit78%of 9
virtio_enqueue_p100%of 3
virtio_enqueue_prep67%of 3
virtio_enqueue_reserve25%of 16
virtio_enqueue_trim80%of 10
virtio_free_vq---of 6
virtio_init_vq---of 15
virtio_nused---of 1
virtio_postpone_intr---of 1
virtio_postpone_intr_far---of 1
virtio_postpone_intr_smart---of 1
virtio_reinit_end---of 1
virtio_reinit_start---of 7
virtio_reset---of 1
virtio_start_vq_intr---of 3
virtio_stop_vq_intr67%of 3
vq_alloc_entry---of 3
vq_free_entry---of 1
-----------
SUMMARY62%of 63
soo_close67%of 3
soo_ioctl100%of 16
soo_read100%of 1
soo_stat---of 8
soo_write100%of 1
-----------
SUMMARY96%of 21
fillmapentry70%of 13
ksym_upcase---of 5
wskbd_compose_value---of 14
wskbd_get_mapentry---of 23
wskbd_init_keymap100%of 4
wskbd_load_keymap66%of 38
-----------
SUMMARY70%of 55
__wdstart---of 12
wd_flushcache---of 9
wd_get_params---of 7
wd_standby---of 7
wdactivate---of 14
wdattach---of 31
wdclose---of 4
wddetach---of 1
wddone---of 18
wddump---of 23
wdgetdefaultlabel---of 1
wdgetdisklabel---of 5
wdioctl---of 19
wdopen13%of 16
wdprobe---of 9
wdread---of 1
wdrestart---of 3
wdsize---of 7
wdstart---of 5
wdstrategy---of 11
wdwrite---of 1
-----------
SUMMARY13%of 16
bktr_attach---of 11
bktr_get_info---of 4
bktr_intr---of 1
bktr_probe---of 7
bktr_set_info---of 10
bktrclose---of 5
bktrioctl---of 5
bktrmmap---of 5
bktropen25%of 8
bktrread---of 4
bktrwrite---of 1
free_bktr_mem---of 1
get_bktr_mem---of 8
-----------
SUMMARY25%of 8
nvram_csum_valid---of 3
nvram_get_byte---of 3
nvramattach---of 6
nvramclose---of 1
nvramopen100%of 1
nvramread---of 9
-----------
SUMMARY100%of 1
pflog_clone_create---of 3
pflog_clone_destroy---of 3
pflog_getif---of 7
pflog_packet11%of 28
pflogattach---of 1
pflogioctl---of 3
pflogoutput---of 1
-----------
SUMMARY11%of 28
pledge_ioctl_vmm---of 15
start_vmm_on_cpu---of 11
stop_vmm_on_cpu---of 7
svm_exit_reason_decode---of 144
svm_fault_page---of 3
svm_get_guest_faulttype---of 1
svm_handle_exit---of 37
svm_handle_hlt---of 1
svm_handle_inout---of 9
svm_handle_msr---of 27
svm_handle_np_fault---of 9
svm_handle_xsetbv---of 1
svm_set_clean---of 3
svm_set_dirty---of 3
svm_setmsrbr---of 5
svm_setmsrbrw---of 5
svm_setmsrbw---of 5
vcpu_deinit---of 6
vcpu_deinit_svm---of 9
vcpu_deinit_vmx---of 11
vcpu_init34%of 6
vcpu_init_svm---of 10
vcpu_init_vmx8%of 27
vcpu_must_stop---of 3
vcpu_readregs_svm---of 11
vcpu_readregs_vmx42%of 36
vcpu_reload_vmcs_vmx---of 6
vcpu_reset_regs---of 6
vcpu_reset_regs_svm---of 6
vcpu_reset_regs_vmx30%of 74
vcpu_run_svm---of 56
vcpu_run_vmx---of 84
vcpu_state_decode---of 3
vcpu_vmx_check_cap---of 15
vcpu_vmx_compute_ctrl25%of 62
vcpu_writeregs_svm---of 11
vcpu_writeregs_vmx47%of 39
vm_create48%of 17
vm_create_check_mem_ranges100%of 13
vm_find72%of 7
vm_get_info54%of 15
vm_impl_deinit---of 3
vm_impl_deinit_svm---of 1
vm_impl_deinit_vmx---of 1
vm_impl_init---of 6
vm_impl_init_svm---of 6
vm_impl_init_vmx50%of 6
vm_intr_pending58%of 7
vm_mprotect_ept45%of 29
vm_resetcpu47%of 15
vm_run23%of 18
vm_rwregs40%of 15
vm_rwvmparams64%of 22
vm_teardown20%of 15
vm_terminate13%of 16
vmclear_on_cpu---of 5
vmm_activate---of 12
vmm_alloc_vpid---of 5
vmm_attach---of 22
vmm_enabled---of 4
vmm_fpurestore---of 9
vmm_fpusave---of 3
vmm_free_vpid---of 1
vmm_get_guest_cpu_cpl---of 10
vmm_get_guest_cpu_mode---of 12
vmm_get_guest_memtype---of 7
vmm_gpa_is_valid---of 8
vmm_handle_cpuid---of 46
vmm_handle_xsetbv---of 15
vmm_init_pvclock---of 10
vmm_inject_db---of 1
vmm_inject_gp---of 1
vmm_inject_ud---of 1
vmm_pat_is_valid---of 9
vmm_probe---of 1
vmm_quiesce_vmx---of 13
vmm_start---of 4
vmm_stop---of 10
vmm_translate_gva---of 21
vmm_update_pvclock75%of 4
vmmclose100%of 1
vmmioctl61%of 23
vmmopen67%of 3
vmx_exit_reason_decode---of 3
vmx_fault_page---of 13
vmx_get_exit_info---of 3
vmx_get_exit_qualification---of 3
vmx_get_guest_faulttype---of 6
vmx_handle_cr---of 30
vmx_handle_cr0_write---of 19
vmx_handle_cr4_write---of 5
vmx_handle_exit---of 45
vmx_handle_hlt---of 6
vmx_handle_inout---of 7
vmx_handle_intr---of 3
vmx_handle_misc_enable_msr---of 1
vmx_handle_np_fault---of 9
vmx_handle_rdmsr---of 7
vmx_handle_wrmsr---of 24
vmx_handle_xsetbv---of 4
vmx_instruction_error_decode---of 22
vmx_load_pdptes---of 22
vmx_mprotect_ept50%of 20
vmx_pmap_find_pte_ept---of 6
vmx_setmsrbr---of 4
vmx_setmsrbrw---of 4
vmx_setmsrbw---of 4
-----------
SUMMARY40%of 490
_bus_space_map---of 7
_bus_space_unmap---of 8
bus_space_alloc---of 14
bus_space_free---of 1
bus_space_map---of 15
bus_space_subregion---of 1
bus_space_unmap---of 9
x86_bus_space_init---of 1
x86_bus_space_io_copy_1---of 7
x86_bus_space_io_copy_2---of 7
x86_bus_space_io_copy_4---of 7
x86_bus_space_io_mmap---of 1
x86_bus_space_io_read_1100%of 1
x86_bus_space_io_read_2---of 1
x86_bus_space_io_read_4---of 1
x86_bus_space_io_read_multi_1---of 1
x86_bus_space_io_read_multi_2---of 1
x86_bus_space_io_read_multi_4---of 1
x86_bus_space_io_read_region_1---of 1
x86_bus_space_io_read_region_2---of 1
x86_bus_space_io_read_region_4---of 1
x86_bus_space_io_set_multi_1---of 4
x86_bus_space_io_set_multi_2---of 4
x86_bus_space_io_set_multi_4---of 4
x86_bus_space_io_set_region_1---of 4
x86_bus_space_io_set_region_2---of 4
x86_bus_space_io_set_region_4---of 4
x86_bus_space_io_vaddr---of 1
x86_bus_space_io_write_1100%of 1
x86_bus_space_io_write_2100%of 1
x86_bus_space_io_write_4---of 1
x86_bus_space_io_write_multi_1---of 1
x86_bus_space_io_write_multi_2---of 1
x86_bus_space_io_write_multi_4---of 1
x86_bus_space_io_write_region_1---of 1
x86_bus_space_io_write_region_2---of 1
x86_bus_space_io_write_region_4---of 1
x86_bus_space_mallocok---of 1
x86_bus_space_mem_copy_1---of 15
x86_bus_space_mem_copy_2---of 15
x86_bus_space_mem_copy_4---of 15
x86_bus_space_mem_copy_8---of 15
x86_bus_space_mem_mmap---of 1
x86_bus_space_mem_read_1---of 1
x86_bus_space_mem_read_2---of 1
x86_bus_space_mem_read_4---of 1
x86_bus_space_mem_read_8---of 1
x86_bus_space_mem_read_multi_1---of 1
x86_bus_space_mem_read_multi_2---of 1
x86_bus_space_mem_read_multi_4---of 1
x86_bus_space_mem_read_multi_8---of 1
x86_bus_space_mem_read_region_1---of 1
x86_bus_space_mem_read_region_2---of 1
x86_bus_space_mem_read_region_4---of 1
x86_bus_space_mem_read_region_8---of 1
x86_bus_space_mem_set_multi_1---of 8
x86_bus_space_mem_set_multi_2---of 8
x86_bus_space_mem_set_multi_4---of 8
x86_bus_space_mem_set_multi_8---of 8
x86_bus_space_mem_set_region_1---of 8
x86_bus_space_mem_set_region_2---of 8
x86_bus_space_mem_set_region_4---of 8
x86_bus_space_mem_set_region_8---of 8
x86_bus_space_mem_vaddr---of 1
x86_bus_space_mem_write_1---of 1
x86_bus_space_mem_write_2---of 1
x86_bus_space_mem_write_4---of 1
x86_bus_space_mem_write_8---of 1
x86_bus_space_mem_write_multi_1---of 1
x86_bus_space_mem_write_multi_2---of 1
x86_bus_space_mem_write_multi_4---of 1
x86_bus_space_mem_write_multi_8---of 1
x86_bus_space_mem_write_region_1---of 1
x86_bus_space_mem_write_region_2---of 1
x86_bus_space_mem_write_region_4---of 1
x86_bus_space_mem_write_region_8---of 1
x86_mem_add_mapping---of 6
-----------
SUMMARY100%of 3
-----------
SUMMARY---of 0
add_to_worklist---of 5
allocdirect_merge---of 15
bmsafemap_lookup---of 9
check_inode_unwritten---of 19
clear_inodedeps---of 26
clear_remove---of 14
deallocate_dependencies---of 69
diradd_inode_written---of 12
drain_output---of 10
flush_inodedep_deps---of 43
flush_pagedep_deps---of 50
free_allocdirect---of 25
free_allocindir---of 20
free_diradd---of 28
free_inodedep---of 13
free_newdirblk---of 20
getdirtybuf---of 8
handle_allocdirect_partdone---of 14
handle_allocindir_partdone---of 20
handle_workitem_freeblocks---of 21
handle_workitem_freefile---of 5
handle_workitem_freefrag---of 1
handle_workitem_remove---of 25
handle_written_filepage---of 35
handle_written_inodeblock---of 70
handle_written_mkdir---of 15
indir_trunc---of 31
initiate_write_filepage---of 10
initiate_write_inodeblock_ufs1---of 40
initiate_write_inodeblock_ufs2---of 40
inodedep_lookup---of 20
merge_inode_lists---of 12
newallocindir---of 5
newblk_lookup---of 15
newdirrem---of 19
newfreefrag---of 5
pagedep_lookup---of 20
pause_timer---of 3
process_worklist_item---of 28
request_cleanup---of 16
sema_get---of 7
sema_init---of 1
sema_release---of 5
setup_allocindir_phase2---of 38
softdep_change_directoryentry_offset---of 14
softdep_change_linkcnt---of 5
softdep_count_dependencies---of 49
softdep_deallocate_dependencies---of 3
softdep_disk_io_initiation---of 20
softdep_disk_write_complete---of 55
softdep_error---of 1
softdep_flushfiles---of 6
softdep_flushworklist---of 9
softdep_freefile---of 5
softdep_freequeue_process---of 10
softdep_fsync---of 31
softdep_fsync_mountdev---of 18
softdep_initialize---of 1
softdep_load_inodeblock---of 5
softdep_mount---of 6
softdep_move_dependencies---of 11
softdep_print---of 4
softdep_process_worklist---of 21
softdep_setup_allocdirect---of 34
softdep_setup_allocindir_meta---of 3
softdep_setup_allocindir_page---of 11
softdep_setup_blkmapdep---of 4
softdep_setup_directory_add---of 45
softdep_setup_directory_change---of 20
softdep_setup_freeblocks---of 58
softdep_setup_inomapdep---of 4
softdep_setup_remove---of 6
softdep_slowdown---of 4
softdep_sync_metadata3%of 115
softdep_update_inodeblock---of 31
worklist_print---of 25
-----------
SUMMARY3%of 115
MD5Final34%of 12
MD5Init100%of 1
MD5Transform100%of 1
MD5Update---of 9
-----------
SUMMARY43%of 14
tcp_canceltimers100%of 1
tcp_slowtimo---of 1
tcp_timer_2msl---of 14
tcp_timer_delack---of 7
tcp_timer_freesack---of 4
tcp_timer_init---of 9
tcp_timer_keep---of 15
tcp_timer_persist---of 12
tcp_timer_reaper---of 1
tcp_timer_rexmt---of 40
-----------
SUMMARY100%of 1
closef60%of 10
dodup324%of 30
dupfdopen54%of 13
falloc16%of 19
fd_checkclosed67%of 3
fd_getfile100%of 4
fd_getfile_mode72%of 7
fd_iterfile56%of 9
fdalloc66%of 26
fdcloseexec---of 9
fdcopy---of 24
fdexpand---of 8
fdfree---of 15
fdinit---of 1
fdinsert50%of 8
fdrelease100%of 4
fdremove43%of 14
fdrop72%of 7
fdshare---of 1
filedesc_init---of 1
filedescopen100%of 1
find_last_set---of 8
finishdup73%of 18
fnew67%of 3
sys_close67%of 3
sys_closefrom---of 8
sys_dup50%of 12
sys_dup2100%of 1
sys_dup3---of 4
sys_fcntl78%of 66
sys_flock92%of 12
sys_fpathconf---of 11
sys_fstat---of 12
sys_getdtablecount---of 1
-----------
SUMMARY60%of 270
shm_deallocate_segment---of 1
shm_delete_mapping67%of 6
shm_find_segment_by_key---of 7
shm_find_segment_by_shmid---of 4
shm_reallocate---of 1
shmexit---of 7
shmfork---of 8
shmget_allocate_segment41%of 22
shmget_existing---of 7
shminit---of 1
sys_shmat67%of 21
sys_shmctl100%of 16
sys_shmdt80%of 10
sys_shmget79%of 19
sysctl_sysvshm84%of 12
-----------
SUMMARY72%of 106
bounds_check_with_label50%of 10
checkdisklabel8%of 39
disk_attach46%of 22
disk_attach_callback---of 4
disk_busy100%of 3
disk_closepart60%of 5
disk_construct---of 1
disk_detach60%of 5
disk_gone---of 11
disk_init---of 1
disk_lock100%of 1
disk_lock_nointr100%of 1
disk_lookup50%of 6
disk_map31%of 23
disk_openpart80%of 10
disk_readlabel---of 5
disk_unbusy---of 8
disk_unlock100%of 1
diskerr---of 10
dk_mountroot---of 7
dkcksum75%of 4
duid_equal---of 1
duid_format---of 1
duid_iszero---of 1
findblkmajor---of 12
findblkname---of 6
getdisk---of 9
gpt_chk_mbr---of 17
gpt_get_fstype---of 11
gpt_get_hdr---of 7
gpt_get_parts---of 8
initdisklabel63%of 8
parsedisk---of 25
readdisksector---of 1
readdoslabel30%of 20
setdisklabel7%of 31
setroot---of 147
spooffat---of 8
spoofgpt9%of 24
spoofmbr6%of 37
-----------
SUMMARY28%of 250
pcppi_attach---of 5
pcppi_bell80%of 15
pcppi_bell_stop---of 3
pcppi_kbd_bell100%of 1
pcppi_match---of 12
-----------
SUMMARY82%of 16
in_baddynamic---of 5
in_init---of 1
in_losing---of 5
in_pcbaddrisavail100%of 18
in_pcballoc84%of 6
in_pcbbind80%of 39
in_pcbconnect77%of 17
in_pcbdetach91%of 11
in_pcbdisconnect59%of 12
in_pcbhash100%of 1
in_pcbhash_insert80%of 10
in_pcbhash_lookup50%of 18
in_pcbinit---of 1
in_pcblhash---of 1
in_pcblookup80%of 5
in_pcblookup_listen47%of 13
in_pcblookup_local94%of 30
in_pcbnotifyall---of 16
in_pcbpickport79%of 14
in_pcbref100%of 3
in_pcbrehash100%of 5
in_pcbresize64%of 11
in_pcbrtentry67%of 12
in_pcbselsrc67%of 30
in_pcbunref50%of 12
in_peeraddr67%of 3
in_rootonly---of 6
in_rtchange---of 3
in_setpeeraddr67%of 3
in_setsockaddr100%of 1
in_sockaddr100%of 1
-----------
SUMMARY75%of 275
-----------
SUMMARY---of 0
ntfs_calccfree---of 8
ntfs_checkexp---of 3
ntfs_fhtovp---of 3
ntfs_init---of 1
ntfs_mount---of 13
ntfs_mountfs---of 54
ntfs_quotactl---of 1
ntfs_root---of 3
ntfs_start---of 1
ntfs_statfs---of 1
ntfs_sync---of 1
ntfs_sysctl100%of 1
ntfs_unmount---of 62
ntfs_vget---of 3
ntfs_vgetex---of 25
ntfs_vptofh---of 1
-----------
SUMMARY100%of 1
rip6_abort---of 3
rip6_attach50%of 10
rip6_bind100%of 4
rip6_connect100%of 4
rip6_ctlinput---of 12
rip6_ctloutput93%of 14
rip6_detach75%of 4
rip6_disconnect67%of 3
rip6_init---of 1
rip6_input---of 57
rip6_output87%of 29
rip6_send84%of 6
rip6_shutdown100%of 1
rip6_sysctl100%of 4
rip6_sysctl_rip6stat---of 1
-----------
SUMMARY84%of 79
com_activate---of 9
com_attach_subr---of 111
com_detach---of 7
com_enable_debugport---of 3
com_fifo_probe---of 34
com_raisedtr---of 3
com_read_reg---of 3
com_resume---of 58
com_write_reg---of 3
comclose34%of 6
comcn_read_reg---of 3
comcn_write_reg---of 3
comcnattach---of 3
comcngetc---of 8
comcninit---of 4
comcnpollc---of 1
comcnprobe---of 11
comcnputc77%of 13
comdiag---of 1
cominit---of 5
comintr---of 45
comioctl68%of 25
comopen36%of 76
comparam52%of 54
comprobe1---of 4
compwroff36%of 34
comread100%of 1
comsoft---of 16
comspeed---of 5
comstart50%of 22
comstop100%of 3
comtty100%of 1
comwrite100%of 1
-----------
SUMMARY48%of 236
check_pty63%of 8
filt_ptcexcept100%of 10
filt_ptcrdetach100%of 1
filt_ptcread100%of 12
filt_ptcwdetach100%of 1
filt_ptcwrite100%of 10
ptcclose100%of 1
ptckqfilter100%of 5
ptcopen100%of 4
ptcread92%of 24
ptcwakeup---of 5
ptcwrite86%of 35
ptm_vn_open60%of 5
ptmattach---of 7
ptmclose100%of 1
ptmioctl48%of 34
ptmopen100%of 1
ptsclose100%of 1
ptsopen92%of 12
ptsread53%of 23
ptsstart100%of 4
ptsstop100%of 5
ptswrite100%of 3
pty_getfree---of 7
ptyattach---of 7
ptydevname---of 3
ptyioctl81%of 46
ptytty100%of 1
sysctl_pty100%of 1
-----------
SUMMARY80%of 248
vn_close100%of 3
vn_closefile100%of 5
vn_fsizechk80%of 5
vn_ioctl77%of 13
vn_isunder100%of 1
vn_kqfilter100%of 1
vn_lock50%of 8
vn_marktext---of 1
vn_open76%of 45
vn_rdwr100%of 9
vn_read86%of 7
vn_seek56%of 9
vn_stat50%of 4
vn_statfile---of 1
vn_write90%of 10
vn_writechk40%of 10
-----------
SUMMARY75%of 130
syn_cache_add---of 37
syn_cache_cleanup24%of 13
syn_cache_get---of 49
syn_cache_init---of 4
syn_cache_insert---of 45
syn_cache_lookup---of 17
syn_cache_put---of 3
syn_cache_reaper---of 1
syn_cache_reset---of 12
syn_cache_respond---of 41
syn_cache_rm---of 5
syn_cache_timer---of 15
syn_cache_unreach---of 13
tcp_clean_sackreport---of 1
tcp_del_sackholes---of 9
tcp_dooptions---of 44
tcp_flush_queue---of 13
tcp_hdrsz---of 1
tcp_input---of 441
tcp_mss60%of 22
tcp_mss_adv---of 9
tcp_mss_update---of 6
tcp_newreno_partialack---of 3
tcp_pulloutofband---of 5
tcp_reass---of 27
tcp_sack_option---of 33
tcp_sack_partialack---of 3
tcp_seq_subtract---of 1
tcp_update_sack_list---of 43
tcp_xmit_timer---of 6
-----------
SUMMARY46%of 35
ip6_randomflowlabel100%of 1
ip6id_initid---of 21
ip6id_pmod---of 6
ip6id_randomid74%of 15
-----------
SUMMARY75%of 16
endrun_atoi---of 11
endrun_date_to_nano---of 19
endrun_decode8%of 26
endrun_offset_to_nano---of 11
endrun_scan---of 8
endrun_time_to_nano---of 17
endrun_timeout---of 3
endrunattach---of 1
endrunclose67%of 3
endruninput86%of 14
endrunopen80%of 5
-----------
SUMMARY42%of 48
filt_uhidrdetach---of 1
filt_uhidread---of 1
uhid_attach---of 1
uhid_detach---of 8
uhid_do_ioctl---of 6
uhid_do_open50%of 10
uhid_do_read---of 9
uhid_do_write---of 7
uhid_intr---of 3
uhid_lookup---of 7
uhid_match---of 1
uhidclose---of 8
uhidioctl---of 14
uhidkqfilter---of 11
uhidopen100%of 1
uhidread---of 9
uhidwrite---of 15
-----------
SUMMARY55%of 11
-----------
SUMMARY---of 0
uvm_vnp_setsize100%of 4
uvm_vnp_sync45%of 18
uvm_vnp_terminate42%of 12
uvm_vnp_uncache67%of 6
uvn_attach64%of 25
uvn_cluster67%of 3
uvn_detach50%of 14
uvn_flush60%of 57
uvn_get55%of 24
uvn_init---of 1
uvn_io61%of 33
uvn_put67%of 3
uvn_reference100%of 1
-----------
SUMMARY58%of 200
-----------
SUMMARY---of 0
nmea_atoi---of 12
nmea_date_to_nano---of 9
nmea_decode_gga---of 15
nmea_degrees---of 18
nmea_gprmc---of 43
nmea_scan24%of 42
nmea_time_to_nano---of 19
nmea_timeout---of 3
nmeaattach---of 1
nmeaclose100%of 3
nmeainput92%of 12
nmeaopen80%of 5
-----------
SUMMARY46%of 62
hardclock---of 24
initclocks---of 3
startprofclock---of 4
statclock---of 21
stopprofclock---of 4
sysctl_clockrate100%of 1
tstohz---of 11
tvtohz88%of 8
-----------
SUMMARY89%of 9
i82489_cpu_number---of 1
i82489_readreg---of 1
i82489_writereg---of 1
lapic_boot_init---of 4
lapic_calibrate_timer---of 13
lapic_clockintr---of 1
lapic_cpu_number---of 3
lapic_delay78%of 9
lapic_disable---of 1
lapic_enable---of 1
lapic_hwmask---of 1
lapic_hwunmask---of 1
lapic_initclocks---of 1
lapic_map---of 4
lapic_set_lvt---of 16
lapic_setup---of 1
lapic_startclock---of 1
lapic_timer_oneshot---of 1
lapic_timer_periodic---of 1
x2apic_cpu_number---of 1
x2apic_readreg100%of 1
x2apic_writereg---of 1
-----------
SUMMARY80%of 10
filt_ufsdetach100%of 1
filt_ufsread70%of 10
filt_ufsvnode80%of 5
filt_ufswrite67%of 3
ufs_access48%of 21
ufs_advlock67%of 3
ufs_chmod57%of 23
ufs_chown49%of 45
ufs_close100%of 3
ufs_create75%of 4
ufs_getattr57%of 32
ufs_ioctl---of 1
ufs_islocked100%of 1
ufs_itimes46%of 24
ufs_kqfilter100%of 5
ufs_link41%of 27
ufs_lock100%of 1
ufs_makeinode36%of 34
ufs_mkdir31%of 49
ufs_mknod58%of 7
ufs_open67%of 6
ufs_pathconf---of 15
ufs_print---of 7
ufs_readdir80%of 15
ufs_readlink45%of 9
ufs_remove72%of 14
ufs_rename52%of 145
ufs_rmdir52%of 25
ufs_setattr56%of 89
ufs_strategy75%of 8
ufs_symlink67%of 9
ufs_unlock100%of 1
ufsfifo_close100%of 3
ufsfifo_read100%of 1
ufsfifo_write100%of 1
ufsspec_close100%of 3
ufsspec_read100%of 1
ufsspec_write100%of 1
-----------
SUMMARY54%of 629
in6_cksum72%of 67
-----------
SUMMARY72%of 67
_rrw_init_flags100%of 1
_rw_init_flags100%of 1
_rw_obj_alloc_flags100%of 1
rrw_enter100%of 5
rrw_exit60%of 5
rrw_status67%of 3
rw_assert_anylock50%of 6
rw_assert_rdlock---of 5
rw_assert_unlocked---of 4
rw_assert_wrlock40%of 5
rw_do_exit---of 4
rw_enter39%of 18
rw_enter_read50%of 4
rw_enter_write100%of 3
rw_exit46%of 11
rw_exit_read28%of 11
rw_exit_write46%of 11
rw_obj_free60%of 5
rw_obj_hold50%of 4
rw_obj_init---of 1
rw_status100%of 3
-----------
SUMMARY53%of 97
uvm_io28%of 11
-----------
SUMMARY28%of 11
clock_secs_to_ymdhms87%of 15
clock_ymdhms_to_secs---of 17
-----------
SUMMARY87%of 15
cd_buf_done---of 20
cd_cmd_rw10---of 1
cd_cmd_rw12---of 1
cd_cmd_rw6---of 1
cd_get_parms---of 6
cd_getvol---of 4
cd_interpret_sense---of 7
cd_load_toc---of 7
cd_load_unload---of 3
cd_pause---of 3
cd_play---of 3
cd_play_msf---of 3
cd_play_tracks---of 11
cd_read_subchannel---of 4
cd_read_toc---of 4
cd_reset---of 3
cd_set_pa_immed---of 7
cd_setchan---of 5
cd_setvol---of 6
cd_size---of 15
cdactivate---of 5
cdattach---of 3
cdclose---of 6
cddetach---of 1
cddump---of 1
cdgetdisklabel---of 6
cdioctl---of 120
cdmatch---of 1
cdminphys---of 8
cdopen10%of 20
cdread---of 1
cdsize---of 1
cdstart---of 11
cdstrategy---of 8
cdwrite---of 1
dvd_auth---of 22
dvd_read_bca---of 6
dvd_read_copyright---of 5
dvd_read_disckey---of 5
dvd_read_manufact---of 6
dvd_read_physical---of 6
dvd_read_struct---of 7
-----------
SUMMARY10%of 20
ip6_check_rh0hdr100%of 11
ip6_get_prevhdr---of 9
ip6_hbhchcheck67%of 15
ip6_hopopts_input70%of 13
ip6_init---of 11
ip6_input_if52%of 82
ip6_lasthdr50%of 8
ip6_nexthdr62%of 21
ip6_ours63%of 8
ip6_process_hopopts85%of 19
ip6_pullexthdr---of 14
ip6_savecontrol---of 33
ip6_send100%of 1
ip6_send_dispatch---of 5
ip6_sysctl100%of 15
ip6_sysctl_ip6stat---of 1
ip6_sysctl_soiikey---of 3
ip6_unknown_opt100%of 7
ip6intr---of 8
ipv6_check92%of 58
ipv6_input67%of 3
-----------
SUMMARY73%of 261
calcru---of 6
calctsru60%of 5
dogetrusage84%of 12
donice---of 11
dosetrlimit96%of 24
lim_copy---of 1
lim_cur_proc67%of 3
lim_fork---of 3
lim_free---of 3
lim_read_enter72%of 7
lim_startup---of 1
lim_write_begin63%of 8
lim_write_commit---of 4
ruadd72%of 7
rucheck---of 5
sys_getpriority---of 22
sys_getrlimit84%of 6
sys_getrusage84%of 6
sys_setpriority---of 20
sys_setrlimit80%of 5
tuagg---of 1
tuagg_sub---of 3
tuagg_unlocked60%of 5
-----------
SUMMARY80%of 88
cd9660_check_export---of 3
cd9660_fhtovp---of 4
cd9660_mount---of 13
cd9660_mountroot---of 6
cd9660_quotactl---of 1
cd9660_root---of 1
cd9660_start---of 1
cd9660_statfs---of 1
cd9660_sync---of 1
cd9660_unmount---of 3
cd9660_vget---of 3
cd9660_vget_internal---of 34
cd9660_vptofh---of 1
iso_disklabelspoof19%of 11
iso_mountfs---of 43
-----------
SUMMARY19%of 11
usb_abort_task_thread---of 8
usb_activate---of 8
usb_add_task---of 9
usb_attach---of 17
usb_attach_roothub---of 4
usb_create_task_threads---of 4
usb_detach---of 6
usb_detach_roothub---of 7
usb_explore---of 12
usb_fill_udc_task---of 6
usb_fill_udf_task---of 5
usb_match---of 1
usb_needs_explore---of 4
usb_needs_reattach---of 3
usb_rem_task---of 6
usb_rem_wait_task---of 10
usb_schedsoftintr---of 5
usb_tap---of 30
usb_task_thread---of 12
usb_wait_task---of 5
usbclose---of 1
usbctlprint---of 3
usbioctl---of 45
usbopen50%of 4
-----------
SUMMARY50%of 4
ether_addmulti53%of 23
ether_addr_to_e64---of 1
ether_brport_clr---of 3
ether_brport_get---of 4
ether_brport_get_locked---of 1
ether_brport_isset---of 1
ether_brport_set---of 3
ether_crc32_be---of 4
ether_crc32_be_update---of 4
ether_crc32_le---of 7
ether_crc32_le_update---of 7
ether_delmulti56%of 20
ether_e64_to_addr---of 1
ether_encap---of 5
ether_fakeaddr100%of 1
ether_ifattach58%of 7
ether_ifdetach34%of 6
ether_input64%of 41
ether_ioctl55%of 11
ether_multiaddr---of 10
ether_output80%of 5
ether_resolve46%of 31
ether_rtrequest60%of 5
ether_sprintf---of 1
-----------
SUMMARY56%of 150
doktrace83%of 39
ktrcanset---of 8
ktrcleartrace---of 3
ktrexec---of 10
ktrgenio80%of 10
ktrinitheader---of 1
ktrinitheaderraw---of 1
ktrnamei67%of 3
ktrops86%of 14
ktrpledge---of 3
ktrpsig---of 3
ktrsetchildren---of 6
ktrsettrace75%of 8
ktrstart---of 1
ktrstruct67%of 3
ktrsyscall91%of 11
ktrsysret67%of 3
ktruser---of 12
ktrwrite---of 3
ktrwrite2---of 3
ktrwriteraw27%of 15
sys_ktrace100%of 6
-----------
SUMMARY75%of 112
nfs_getset_niothreads30%of 10
nfsrv_getslp---of 10
nfsrv_init---of 12
nfsrv_slpderef---of 4
nfsrv_zapsock---of 7
nfssvc_addsock---of 12
nfssvc_iod---of 40
nfssvc_nfsd---of 60
sys_nfssvc---of 20
-----------
SUMMARY30%of 10
decay_aftersleep---of 6
mi_switch48%of 25
preempt100%of 1
roundrobin---of 6
schedclock---of 5
schedcpu---of 18
scheduler_start---of 3
setperf_auto---of 20
setpriority---of 3
setrunnable63%of 16
sysctl_hwperfpolicy20%of 10
sysctl_hwsetperf34%of 6
yield100%of 1
-----------
SUMMARY48%of 59
msi_addroute---of 4
msi_delroute---of 3
msi_hwmask---of 1
msi_hwunmask---of 1
msix_addroute---of 6
msix_delroute---of 6
msix_hwmask---of 1
msix_hwunmask---of 1
pci_attach_hook---of 1
pci_bus_maxdevs100%of 1
pci_conf_read29%of 7
pci_conf_size50%of 4
pci_conf_write29%of 7
pci_decompose_tag72%of 7
pci_dev_postattach---of 1
pci_init_extents---of 15
pci_intr_disestablish---of 1
pci_intr_establish---of 1
pci_intr_establish_cpu---of 6
pci_intr_map---of 24
pci_intr_map_msi---of 4
pci_intr_map_msix---of 6
pci_intr_string---of 6
pci_lookup_segment---of 3
pci_make_tag67%of 3
pci_mcfg_init---of 3
pci_mcfg_map_bus---of 4
pci_min_powerstate---of 1
pci_msix_table_map---of 4
pci_msix_table_unmap---of 3
pci_probe_device_hook---of 1
pci_set_powerstate_md---of 1
-----------
SUMMARY49%of 29
ifiq_add_data100%of 1
ifiq_destroy60%of 5
ifiq_enqueue100%of 1
ifiq_init67%of 3
ifiq_input---of 18
ifiq_kstat_copy---of 1
ifiq_process67%of 3
ifq_add_data100%of 1
ifq_attach---of 6
ifq_barrier67%of 3
ifq_barrier_task---of 1
ifq_bundle_task---of 1
ifq_deq_begin67%of 6
ifq_deq_commit50%of 4
ifq_deq_rollback---of 4
ifq_deq_sleep63%of 8
ifq_dequeue---of 7
ifq_destroy60%of 5
ifq_enqueue100%of 6
ifq_hdatalen38%of 8
ifq_init60%of 5
ifq_is_serialized---of 1
ifq_kstat_copy---of 1
ifq_mfreem---of 3
ifq_mfreeml---of 3
ifq_purge67%of 3
ifq_q_enter---of 3
ifq_q_leave---of 3
ifq_restart_task---of 1
ifq_serialize38%of 8
ifq_start100%of 3
ifq_start_task40%of 5
net_ifiq_sysctl100%of 1
priq_alloc---of 1
priq_deq_begin30%of 10
priq_deq_commit67%of 3
priq_enq17%of 18
priq_free100%of 1
priq_idx---of 3
priq_purge100%of 1
-----------
SUMMARY53%of 112
amd64_pa_used---of 7
bios_getdiskinfo34%of 9
bios_sysctl50%of 26
boot---of 16
check_context---of 5
copyin32---of 3
cpu_dump---of 9
cpu_dump_mempagecnt---of 8
cpu_dumpsize---of 1
cpu_init_extents---of 7
cpu_init_idt---of 1
cpu_initclocks---of 1
cpu_reset---of 3
cpu_startup---of 3
cpu_sysctl83%of 17
delay_init---of 3
dumpconf---of 20
dumpsys---of 55
enter_shared_special_pages---of 10
getbootinfo---of 26
idt_vec_alloc---of 5
idt_vec_free---of 1
idt_vec_set---of 3
init_x86_64---of 69
map_tramps---of 3
need_resched67%of 3
reset_segs---of 3
sendsig---of 12
set_mem_segment---of 1
set_sys_segment---of 1
setgate---of 1
setregion---of 1
setregs---of 5
signotify---of 1
splassert_check60%of 5
sys_sigreturn---of 11
unsetgate---of 1
x86_64_proc0_tss_ldt_init---of 1
-----------
SUMMARY59%of 60
__explicit_bzero_hook100%of 1
explicit_bzero100%of 1
-----------
SUMMARY100%of 2
amap_add30%of 10
amap_adjref_anons50%of 12
amap_alloc34%of 6
amap_alloc156%of 20
amap_chunk_free---of 6
amap_chunk_get70%of 13
amap_copy54%of 50
amap_cow_now---of 18
amap_free58%of 14
amap_init---of 3
amap_lookup78%of 9
amap_lookups75%of 12
amap_populate---of 5
amap_pp_adjref82%of 32
amap_pp_establish---of 5
amap_ref67%of 3
amap_splitref30%of 10
amap_swap_off---of 20
amap_unadd59%of 17
amap_unref84%of 6
amap_wipeout58%of 19
amap_wiperange82%of 27
amap_wiperange_chunk72%of 7
-----------
SUMMARY63%of 267
-----------
SUMMARY---of 0
pckbc_attach---of 54
pckbc_attach_slot---of 6
pckbc_cleanqueue---of 6
pckbc_cleanqueues---of 13
pckbc_cleanup---of 4
pckbc_cmdresponse---of 15
pckbc_cnattach---of 14
pckbc_enqueue_cmd32%of 19
pckbc_flush---of 8
pckbc_init_slotdata---of 1
pckbc_is_console---of 1
pckbc_poll---of 1
pckbc_poll_cmd25%of 4
pckbc_poll_cmd114%of 36
pckbc_poll_data---of 12
pckbc_poll_data1---of 8
pckbc_release_console---of 3
pckbc_reset---of 18
pckbc_send_cmd---of 4
pckbc_set_inputhandler---of 4
pckbc_set_poll---of 4
pckbc_slot_enable72%of 7
pckbc_start29%of 14
pckbc_stop---of 1
pckbc_submatch---of 4
pckbc_submatch_locators---of 4
pckbc_xt_translation---of 14
pckbcintr---of 1
pckbcintr_internal---of 14
pckbcprint---of 3
-----------
SUMMARY27%of 80
_rs_clearseed---of 5
_rs_seed45%of 9
_rs_stir67%of 3
add_entropy_words75%of 4
arc4random67%of 6
arc4random_buf70%of 10
arc4random_ctx_buf---of 1
arc4random_ctx_free---of 1
arc4random_ctx_new100%of 1
arc4random_uniform50%of 4
chacha_encrypt_bytes95%of 18
chacha_keysetup100%of 1
dequeue_randomness67%of 3
enqueue_randomness67%of 6
extract_entropy67%of 3
filt_randomdetach100%of 1
filt_randomread100%of 1
filt_randomwrite100%of 1
random_start---of 5
randomclose100%of 1
randomioctl100%of 1
randomkqfilter100%of 4
randomopen100%of 1
randomread86%of 14
randomwrite80%of 15
resume_randomness---of 3
rnd_init---of 1
rnd_reinit---of 1
suspend_randomness---of 1
sys_getentropy---of 4
-----------
SUMMARY78%of 107
audio_activate---of 40
audio_attach---of 38
audio_attach_mi---of 1
audio_blksz_bytes---of 12
audio_buf_done---of 3
audio_buf_init---of 7
audio_buf_rdiscard---of 3
audio_buf_rgetblk---of 1
audio_buf_wakeup---of 6
audio_buf_wcommit---of 1
audio_buf_wgetblk---of 1
audio_calc_sil---of 12
audio_canstart---of 9
audio_clear---of 23
audio_close---of 3
audio_detach---of 14
audio_drain---of 23
audio_event---of 9
audio_fill_sil---of 11
audio_gcd---of 4
audio_getdev---of 3
audio_ioc_getpar---of 1
audio_ioc_getstatus---of 1
audio_ioc_setpar---of 51
audio_ioc_start---of 8
audio_ioc_stop---of 4
audio_ioctl---of 22
audio_ioctl_mixer---of 19
audio_match---of 1
audio_mixer_close---of 5
audio_mixer_devinfo---of 5
audio_mixer_get---of 5
audio_mixer_open---of 4
audio_mixer_read---of 12
audio_mixer_set---of 9
audio_mixer_wakeup---of 6
audio_open---of 33
audio_pintr---of 36
audio_read---of 26
audio_rintr---of 34
audio_setpar---of 71
audio_setpar_blksz---of 34
audio_setpar_nblks---of 10
audio_start---of 1
audio_start_do---of 11
audio_stop---of 27
audio_stop_do---of 5
audio_submatch---of 1
audio_write---of 28
audioclose---of 9
audioioctl---of 11
audiokqfilter---of 8
audioopen25%of 8
audioprint---of 6
audioread---of 5
audiowrite---of 5
filt_audioctlrdetach---of 1
filt_audioctlread---of 4
filt_audiomodify---of 1
filt_audioprocess---of 6
filt_audiordetach---of 1
filt_audioread---of 4
filt_audiowdetach---of 1
filt_audiowrite---of 4
wskbd_initmute---of 6
wskbd_initvol---of 13
wskbd_mixer_cb---of 1
wskbd_mixer_init---of 10
wskbd_mixer_update---of 20
wskbd_set_mixermute---of 4
wskbd_set_mixervolume---of 6
wskbd_set_mixervolume_dev---of 12
wskbd_set_mixervolume_unit---of 6
-----------
SUMMARY25%of 8
pf_add_threshold---of 3
pf_addr_compare36%of 14
pf_addr_inc---of 7
pf_addr_wrap_neq90%of 19
pf_addrcpy75%of 4
pf_alloc_state_key67%of 3
pf_build_tcp---of 19
pf_calc_mss---of 7
pf_calc_skip_steps70%of 79
pf_change_icmp_af---of 21
pf_check_tcp_cksum---of 11
pf_check_threshold---of 1
pf_cksum_fixup_a---of 6
pf_compare_state_keys---of 22
pf_counters_inc20%of 25
pf_delay_pkt---of 3
pf_detach_state---of 7
pf_find_divert---of 1
pf_find_state30%of 55
pf_find_state_all62%of 18
pf_find_state_byid100%of 9
pf_find_tcpopt---of 8
pf_free_state---of 19
pf_get_divert---of 4
pf_get_mss---of 13
pf_get_src_node---of 5
pf_get_wscale---of 13
pf_icmp_mapping25%of 37
pf_icmp_state_lookup37%of 11
pf_init_threshold100%of 1
pf_inp_link---of 8
pf_inp_lookup---of 6
pf_inp_unlink---of 4
pf_inpcb_unlink_state_key---of 4
pf_insert_src_node---of 24
pf_log_matches---of 7
pf_match---of 11
pf_match_addr---of 9
pf_match_addr_range---of 23
pf_match_gid---of 12
pf_match_port---of 11
pf_match_rcvif---of 8
pf_match_rule21%of 279
pf_match_tag---of 3
pf_match_uid---of 12
pf_mbuf_link_inpcb67%of 3
pf_mbuf_link_state_key50%of 4
pf_mbuf_unlink_inpcb100%of 3
pf_mbuf_unlink_state_key67%of 3
pf_modulate_sack---of 17
pf_ouraddr40%of 5
pf_patch_1650%of 4
pf_patch_16_unaligned---of 10
pf_patch_32---of 3
pf_patch_32_unaligned---of 15
pf_patch_875%of 4
pf_pkt_addr_changed80%of 5
pf_pktenqueue_delayed---of 3
pf_poolmask---of 4
pf_print_flags---of 18
pf_print_host---of 42
pf_print_state---of 1
pf_print_state_parts---of 39
pf_pull_hdr40%of 15
pf_purge---of 11
pf_purge_expired_rules43%of 7
pf_purge_expired_src_nodes14%of 23
pf_purge_expired_states---of 24
pf_purge_timeout---of 1
pf_remove_divert_state---of 24
pf_remove_src_node---of 6
pf_remove_state---of 28
pf_routable---of 18
pf_route---of 57
pf_route6---of 56
pf_rtlabel_match---of 6
pf_rule_to_actions---of 17
pf_send_icmp---of 10
pf_send_tcp---of 5
pf_setup_pdesc47%of 60
pf_socket_lookup---of 19
pf_src_connlimit---of 65
pf_src_tree_RB_FIND---of 24
pf_src_tree_RB_INSERT---of 27
pf_src_tree_RB_INSERT_COLOR---of 27
pf_src_tree_RB_MINMAX50%of 4
pf_src_tree_RB_NEXT---of 8
pf_src_tree_RB_NFIND---of 24
pf_src_tree_RB_PREV---of 8
pf_src_tree_RB_REMOVE---of 17
pf_src_tree_RB_REMOVE_COLOR---of 56
pf_src_tree_remove_state25%of 8
pf_state_compare_key87%of 29
pf_state_expires45%of 9
pf_state_export77%of 13
pf_state_insert70%of 36
pf_state_key_addr_setup48%of 19
pf_state_key_attach52%of 37
pf_state_key_detach39%of 13
pf_state_key_isvalid---of 3
pf_state_key_link_inpcb---of 4
pf_state_key_link_reverse---of 8
pf_state_key_ref---of 3
pf_state_key_setup---of 28
pf_state_key_unlink_inpcb---of 4
pf_state_key_unlink_reverse---of 4
pf_state_key_unref38%of 8
pf_state_list_insert---of 3
pf_state_list_remove---of 1
pf_state_ref100%of 3
pf_state_rm_src_node---of 6
pf_state_tree_RB_FIND---of 7
pf_state_tree_RB_INSERT---of 10
pf_state_tree_RB_INSERT_COLOR82%of 27
pf_state_tree_RB_MINMAX---of 4
pf_state_tree_RB_NEXT---of 8
pf_state_tree_RB_NFIND---of 7
pf_state_tree_RB_PREV---of 8
pf_state_tree_RB_REMOVE59%of 17
pf_state_tree_RB_REMOVE_COLOR22%of 56
pf_state_tree_id_RB_FIND---of 9
pf_state_tree_id_RB_INSERT---of 12
pf_state_tree_id_RB_INSERT_COLOR86%of 27
pf_state_tree_id_RB_MINMAX100%of 4
pf_state_tree_id_RB_NEXT100%of 8
pf_state_tree_id_RB_NFIND---of 9
pf_state_tree_id_RB_PREV---of 8
pf_state_tree_id_RB_REMOVE---of 17
pf_state_tree_id_RB_REMOVE_COLOR---of 56
pf_state_unref24%of 13
pf_step_into_anchor20%of 10
pf_tag_packet---of 5
pf_tbladdr_copyout67%of 6
pf_tbladdr_remove75%of 4
pf_tbladdr_setup100%of 3
pf_tcp_iss---of 6
pf_tcp_track_full---of 154
pf_tcp_track_sloppy---of 65
pf_test31%of 202
pf_test_rule16%of 201
pf_test_state---of 137
pf_test_state_icmp3%of 382
pf_translate---of 35
pf_translate_a---of 16
pf_translate_af---of 16
pf_translate_icmp---of 10
pf_translate_icmp_af---of 75
pf_walk_header33%of 34
pf_walk_header642%of 101
pf_walk_option29%of 25
pf_walk_option630%of 37
-----------
SUMMARY32%of 2026
fusefs_checkexp---of 1
fusefs_fhtovp---of 1
fusefs_init---of 1
fusefs_mount---of 10
fusefs_quotactl---of 1
fusefs_root---of 3
fusefs_start---of 1
fusefs_statfs---of 8
fusefs_sync---of 1
fusefs_sysctl100%of 1
fusefs_unmount---of 6
fusefs_vget---of 8
fusefs_vptofh---of 1
-----------
SUMMARY100%of 1
add_mfc65%of 14
add_vif60%of 20
del_mfc50%of 10
del_vif67%of 12
get_api_config---of 4
get_api_support---of 4
get_sg_cnt23%of 9
get_version---of 1
get_vif_cnt---of 8
if_lookupbyvif---of 8
ip_mdq---of 15
ip_mforward---of 23
ip_mrouter_done54%of 13
ip_mrouter_get60%of 10
ip_mrouter_init---of 8
ip_mrouter_set84%of 25
mfc_add---of 9
mfc_add_route---of 5
mfc_expire_route---of 5
mfc_find---of 8
mrouter_rtwalk_delete100%of 1
mrt_ioctl67%of 12
mrt_mcast_del---of 3
mrt_rtwalk_mfcsysctl18%of 17
mrt_sysctl_mfc78%of 9
mrt_sysctl_vif50%of 12
rt_mcast_add---of 12
set_api_config---of 11
socket_send---of 4
update_mfc_params---of 35
vif_delete67%of 3
-----------
SUMMARY59%of 167
sw_reg_iodone---of 1
sw_reg_iodone_internal---of 19
sw_reg_start---of 8
sw_reg_strategy---of 21
swap_off---of 12
swap_on---of 23
swap_print_all---of 17
swapdrum_add---of 3
swapdrum_getsdp---of 11
swaplist_find---of 11
swaplist_insert---of 14
swaplist_trim---of 9
swapmount---of 8
swstrategy---of 13
sys_swapctl---of 51
uvm_hibswap---of 11
uvm_swap_alloc---of 15
uvm_swap_allocpages---of 15
uvm_swap_finicrypt_all---of 12
uvm_swap_free---of 28
uvm_swap_freepages---of 10
uvm_swap_get---of 6
uvm_swap_init---of 7
uvm_swap_initcrypt---of 1
uvm_swap_initcrypt_all45%of 9
uvm_swap_io---of 57
uvm_swap_markbad---of 11
uvm_swap_markdecrypt---of 7
uvm_swap_needdecrypt---of 3
uvm_swap_put---of 1
uvm_swapisfull---of 3
-----------
SUMMARY45%of 9
cttyioctl---of 14
cttykqfilter---of 6
cttyopen50%of 4
cttyread---of 4
cttywrite---of 4
-----------
SUMMARY50%of 4
drm_fault---of 7
drm_flush---of 1
drm_gem_close_ioctl---of 3
drm_gem_create_mmap_offset---of 1
drm_gem_create_mmap_offset_size---of 1
drm_gem_dma_resv_wait---of 5
drm_gem_dumb_destroy---of 1
drm_gem_dumb_map_offset---of 8
drm_gem_flink_ioctl---of 10
drm_gem_free_mmap_offset---of 1
drm_gem_get_pages---of 1
drm_gem_handle_create---of 1
drm_gem_handle_create_tail---of 10
drm_gem_handle_delete---of 6
drm_gem_init---of 3
drm_gem_init_release---of 1
drm_gem_lock_reservations---of 36
drm_gem_mmap---of 22
drm_gem_mmap_obj---of 10
drm_gem_object_free---of 3
drm_gem_object_handle_put_unlocked---of 10
drm_gem_object_init---of 6
drm_gem_object_lookup---of 3
drm_gem_object_release---of 7
drm_gem_object_release_handle---of 5
drm_gem_objects_lookup---of 9
drm_gem_open---of 1
drm_gem_open_ioctl---of 8
drm_gem_pin---of 3
drm_gem_print_info---of 3
drm_gem_private_object_init---of 4
drm_gem_put_pages---of 1
drm_gem_release---of 1
drm_gem_unlock_reservations---of 6
drm_gem_unpin---of 3
drm_gem_vmap---of 4
drm_gem_vunmap---of 6
drm_ref---of 1
drm_unref---of 5
udv_attach_drm16%of 13
-----------
SUMMARY16%of 13
cond_init100%of 1
cond_signal---of 1
cond_wait100%of 4
endtsleep67%of 3
msleep42%of 12
msleep_nsec75%of 4
refcnt_finalize27%of 15
refcnt_init100%of 1
refcnt_init_trace34%of 6
refcnt_read34%of 6
refcnt_rele45%of 9
refcnt_rele_wake45%of 9
refcnt_shared34%of 6
refcnt_take29%of 7
rwsleep58%of 7
rwsleep_nsec75%of 4
sleep_finish61%of 23
sleep_queue_init---of 3
sleep_setup40%of 10
sleep_signal_check---of 4
sys___thrsleep---of 5
sys___thrwakeup---of 12
sys_sched_yield---of 4
thrsleep---of 32
thrsleep_unlock---of 3
tsleep56%of 9
tsleep_nsec75%of 4
unsleep58%of 7
wakeup100%of 1
wakeup_n72%of 7
wakeup_proc30%of 10
-----------
SUMMARY50%of 165
domaininit---of 45
net_sysctl88%of 25
pfctlinput---of 33
pffasttimo---of 31
pffinddomain---of 7
pffindproto100%of 18
pffindtype100%of 12
pfslowtimo---of 31
-----------
SUMMARY95%of 55
swap_decrypt---of 7
swap_encrypt---of 8
swap_encrypt_ctl100%of 8
swap_key_cleanup---of 3
swap_key_create---of 1
swap_key_delete---of 3
swap_key_prepare---of 5
-----------
SUMMARY100%of 8
tcp_abort60%of 5
tcp_accept67%of 9
tcp_attach34%of 18
tcp_bind86%of 7
tcp_connect92%of 23
tcp_ctloutput85%of 40
tcp_detach60%of 5
tcp_disconnect60%of 5
tcp_dodisconnect59%of 17
tcp_fill_info64%of 11
tcp_ident73%of 33
tcp_listen90%of 10
tcp_peeraddr72%of 7
tcp_rcvd78%of 9
tcp_rcvoob85%of 13
tcp_send70%of 10
tcp_sendoob75%of 12
tcp_sense---of 5
tcp_shutdown65%of 20
tcp_sockaddr86%of 7
tcp_sysctl65%of 28
tcp_sysctl_tcpstat59%of 12
tcp_update_rcvspace---of 9
tcp_update_sndspace89%of 9
tcp_usrclosed---of 13
-----------
SUMMARY72%of 310
nullioctl100%of 1
-----------
SUMMARY100%of 1
ujoy_hid_is_collection---of 8
ujoy_match---of 3
ujoyioctl---of 8
ujoyopen100%of 3
-----------
SUMMARY100%of 3
rn_add_dupedkey16%of 32
rn_add_radix_mask23%of 40
rn_addmask34%of 24
rn_addroute82%of 11
rn_del_radix_mask36%of 25
rn_delete38%of 32
rn_fixup_nodes29%of 21
rn_init---of 10
rn_inithead38%of 8
rn_inithead0---of 1
rn_initmask---of 5
rn_insert77%of 13
rn_link_dupedkey---of 5
rn_lookup55%of 11
rn_match31%of 55
rn_newpair---of 1
rn_refines---of 11
rn_search_m---of 7
rn_walktree78%of 18
-----------
SUMMARY38%of 290
poison_check80%of 5
poison_mem88%of 8
poison_value100%of 1
-----------
SUMMARY86%of 14
sc_print_addr---of 1
scsi_cmd_rw_decode---of 10
scsi_copy_internal_data---of 3
scsi_decode_sense---of 23
scsi_default_get---of 1
scsi_default_put---of 3
scsi_delay---of 5
scsi_do_mode_sense---of 17
scsi_done---of 1
scsi_init---of 3
scsi_init_inquiry---of 1
scsi_inquire---of 8
scsi_inquire_vpd---of 4
scsi_interpret_sense---of 65
scsi_io_get---of 6
scsi_io_get_done---of 1
scsi_io_put---of 1
scsi_ioh_add---of 4
scsi_ioh_del---of 4
scsi_ioh_deq---of 3
scsi_ioh_pending---of 1
scsi_ioh_set---of 1
scsi_iopool_destroy---of 8
scsi_iopool_get---of 1
scsi_iopool_init---of 1
scsi_iopool_put---of 1
scsi_iopool_run56%of 9
scsi_link_close---of 3
scsi_link_open---of 3
scsi_link_shutdown---of 19
scsi_mode_select---of 3
scsi_mode_select_big---of 3
scsi_mode_sense---of 4
scsi_mode_sense_big---of 4
scsi_mode_sense_big_page---of 3
scsi_mode_sense_page---of 3
scsi_move---of 4
scsi_move_done---of 1
scsi_parse_blkdesc---of 16
scsi_pending_finish---of 1
scsi_pending_start---of 1
scsi_plug_detach---of 1
scsi_plug_probe---of 1
scsi_prevent---of 4
scsi_print_sense---of 21
scsi_read_cap_10---of 3
scsi_read_cap_16---of 3
scsi_report_luns---of 3
scsi_req_detach67%of 3
scsi_req_probe---of 3
scsi_start---of 3
scsi_test_unit_ready---of 3
scsi_xs_error12%of 18
scsi_xs_exec100%of 1
scsi_xs_get12%of 18
scsi_xs_get_done---of 1
scsi_xs_io50%of 4
scsi_xs_put67%of 3
scsi_xs_sync38%of 8
scsi_xs_sync_done---of 4
scsi_xsh_add50%of 4
scsi_xsh_del---of 8
scsi_xsh_ioh40%of 5
scsi_xsh_runqueue55%of 11
scsi_xsh_set---of 1
-----------
SUMMARY35%of 84
filt_vscsidetach100%of 1
filt_vscsiread100%of 1
vscsi_attach---of 1
vscsi_ccb_get---of 3
vscsi_ccb_put---of 1
vscsi_cmd---of 7
vscsi_data---of 13
vscsi_devevent---of 3
vscsi_devevent_task---of 5
vscsi_done---of 3
vscsi_free---of 3
vscsi_i2t---of 3
vscsi_match---of 1
vscsi_probe---of 3
vscsi_t2i---of 10
vscsiclose25%of 16
vscsiioctl19%of 11
vscsikqfilter75%of 4
vscsiopen60%of 5
-----------
SUMMARY37%of 38
fido_match---of 3
fidoioctl---of 6
fidoopen100%of 1
-----------
SUMMARY100%of 1
spec_access---of 3
spec_advlock100%of 1
spec_close73%of 18
spec_fsync25%of 8
spec_getattr67%of 3
spec_inactive100%of 1
spec_ioctl75%of 4
spec_kqfilter100%of 5
spec_open81%of 31
spec_open_clone80%of 10
spec_pathconf---of 8
spec_print---of 1
spec_read50%of 22
spec_setattr67%of 3
spec_strategy50%of 4
spec_write30%of 20
-----------
SUMMARY63%of 130
an_match---of 4
rtable_add15%of 20
rtable_alloc---of 1
rtable_clearsource16%of 13
rtable_delete45%of 27
rtable_empty55%of 11
rtable_exists70%of 10
rtable_get---of 5
rtable_getsource50%of 6
rtable_init---of 12
rtable_init_backend---of 1
rtable_insert33%of 37
rtable_iterate100%of 3
rtable_l2100%of 3
rtable_l2set---of 17
rtable_loindex67%of 3
rtable_lookup78%of 22
rtable_match25%of 16
rtable_mpath_capable100%of 1
rtable_mpath_insert34%of 15
rtable_mpath_reprio50%of 16
rtable_satoplen88%of 24
rtable_setsource34%of 6
rtable_walk84%of 6
rtable_walk_helper67%of 6
rtentry_ref100%of 1
rtentry_unref100%of 1
rtmap_dtor---of 1
rtmap_grow---of 7
rtmap_init---of 6
-----------
SUMMARY50%of 247
divert6_abort---of 1
divert6_attach50%of 6
divert6_bind100%of 1
divert6_detach67%of 3
divert6_init---of 1
divert6_lock67%of 3
divert6_output14%of 22
divert6_packet---of 15
divert6_send100%of 1
divert6_shutdown100%of 1
divert6_sysctl100%of 4
divert6_sysctl_div6stat---of 1
divert6_unlock67%of 3
-----------
SUMMARY44%of 44
coredump_elf---of 13
coredump_note_elf---of 11
coredump_notes_elf---of 25
coredump_setup_elf---of 11
coredump_walk_elf---of 1
coredump_writenote_elf---of 4
elf_check_header---of 11
elf_load_file---of 45
elf_load_psection---of 24
elf_os_pt_note---of 20
elf_os_pt_note_name---of 4
elf_read_from---of 1
exec_elf_fixup---of 9
exec_elf_makecmds10%of 64
-----------
SUMMARY10%of 64
__mtx_init100%of 1
db_mtx_enter---of 4
db_mtx_leave---of 3
mtx_enter60%of 5
mtx_enter_try---of 5
mtx_leave60%of 5
-----------
SUMMARY64%of 11
playtone65%of 14
rest---of 3
spkrattach---of 1
spkrclose67%of 3
spkrioctl90%of 20
spkropen100%of 4
spkrprobe---of 1
spkrwrite95%of 86
-----------
SUMMARY90%of 127
in6_addmulti77%of 13
in6_addr2scopeid---of 13
in6_addrscope---of 6
in6_check_embed_scope---of 6
in6_clear_scope_id---of 6
in6_control100%of 4
in6_delmulti84%of 6
in6_domifattach100%of 1
in6_domifdetach100%of 1
in6_hasmulti100%of 7
in6_ifawithscope79%of 85
in6_ifinit65%of 54
in6_ioctl100%of 17
in6_ioctl_change_ifaddr67%of 80
in6_ioctl_get58%of 28
in6_joingroup50%of 4
in6_leavegroup67%of 3
in6_mask2len---of 24
in6_matchlen96%of 22
in6_nam2sin680%of 5
in6_prefixlen2mask82%of 11
in6_purgeaddr54%of 13
in6_sa2sin6---of 4
in6_unlink_ifa62%of 42
in6_update_ifa65%of 172
in6if_do_dad50%of 4
in6ifa_ifpforlinklocal89%of 9
in6ifa_ifpwithaddr86%of 7
-----------
SUMMARY71%of 588
egre_clone_create---of 1
egre_clone_destroy---of 4
egre_ioctl---of 31
egre_media_change---of 1
egre_media_status---of 1
egre_start---of 21
egre_tree_RBT_COMPARE---of 18
eoip_clone_create---of 1
eoip_clone_destroy---of 3
eoip_down---of 6
eoip_ioctl---of 48
eoip_keepalive_hold---of 4
eoip_keepalive_send---of 13
eoip_start---of 17
eoip_tree_RBT_COMPARE---of 14
gre_clone_create---of 1
gre_clone_destroy---of 9
gre_encap_dst_ip---of 10
gre_input---of 3
gre_input6---of 3
gre_input_key---of 131
gre_ioctl---of 33
gre_ipv4_patch---of 5
gre_ipv6_patch---of 4
gre_keepalive_hold---of 5
gre_keepalive_send---of 29
gre_l3_encap_dst---of 19
gre_mpls_patch---of 4
gre_output---of 14
gre_set_tunnel---of 26
gre_start---of 10
gre_sysctl100%of 1
gre_tunnel_ioctl---of 31
greattach---of 1
mgre_clone_create---of 1
mgre_clone_destroy---of 4
mgre_ioctl---of 51
mgre_output---of 23
mgre_rtrequest---of 12
mgre_start---of 10
mgre_tree_RBT_COMPARE---of 14
nvgre_clone_create---of 5
nvgre_clone_destroy---of 3
nvgre_detach---of 3
nvgre_down---of 10
nvgre_eb_port_eq---of 4
nvgre_eb_port_ifname---of 1
nvgre_eb_port_rele---of 1
nvgre_eb_port_sa---of 4
nvgre_eb_port_take---of 3
nvgre_ioctl---of 84
nvgre_link_change---of 1
nvgre_mcast_tree_RBT_COMPARE---of 13
nvgre_send---of 10
nvgre_start---of 23
nvgre_ucast_tree_RBT_COMPARE---of 10
-----------
SUMMARY100%of 1
pf_anchor_copyout44%of 16
pf_anchor_global_RB_FIND---of 7
pf_anchor_global_RB_INSERT---of 10
pf_anchor_global_RB_INSERT_COLOR89%of 27
pf_anchor_global_RB_MINMAX100%of 4
pf_anchor_global_RB_NEXT100%of 8
pf_anchor_global_RB_NFIND---of 7
pf_anchor_global_RB_PREV---of 8
pf_anchor_global_RB_REMOVE95%of 17
pf_anchor_global_RB_REMOVE_COLOR75%of 56
pf_anchor_node_RB_FIND---of 7
pf_anchor_node_RB_INSERT---of 10
pf_anchor_node_RB_INSERT_COLOR12%of 27
pf_anchor_node_RB_MINMAX75%of 4
pf_anchor_node_RB_NEXT25%of 8
pf_anchor_node_RB_NFIND---of 7
pf_anchor_node_RB_PREV---of 8
pf_anchor_node_RB_REMOVE36%of 17
pf_anchor_node_RB_REMOVE_COLOR8%of 56
pf_anchor_setup55%of 22
pf_create_anchor64%of 30
pf_find_anchor---of 8
pf_find_or_create_ruleset82%of 32
pf_find_ruleset90%of 10
pf_get_leaf_ruleset---of 20
pf_init_ruleset---of 1
pf_remove_anchor50%of 6
pf_remove_if_empty_ruleset67%of 12
-----------
SUMMARY56%of 352
wsmouse_activate---of 3
wsmouse_add_mux84%of 6
wsmouse_attach---of 4
wsmouse_btn_sync---of 7
wsmouse_buttons---of 1
wsmouse_configure---of 18
wsmouse_detach---of 21
wsmouse_do_ioctl87%of 22
wsmouse_evq_put---of 4
wsmouse_get_hw---of 1
wsmouse_get_params96%of 24
wsmouse_hysteresis---of 3
wsmouse_id_to_slot---of 9
wsmouse_input_cleanup---of 5
wsmouse_input_reset---of 5
wsmouse_input_sync---of 56
wsmouse_log_events---of 5
wsmouse_log_input---of 16
wsmouse_match---of 1
wsmouse_matching---of 53
wsmouse_motion---of 3
wsmouse_motion_sync---of 45
wsmouse_mt_convert---of 13
wsmouse_mt_init---of 10
wsmouse_mt_update---of 3
wsmouse_mtframe---of 33
wsmouse_mtstate---of 20
wsmouse_mux_close100%of 1
wsmouse_mux_open50%of 8
wsmouse_param_ioctl86%of 7
wsmouse_position---of 11
wsmouse_ptr_ctrl---of 12
wsmouse_set---of 38
wsmouse_set_mode---of 4
wsmouse_set_params94%of 29
wsmouse_touch---of 6
wsmouse_touch_sync---of 14
wsmouse_touch_update---of 13
wsmouseclose50%of 6
wsmousedevprint---of 3
wsmousedoioctl67%of 3
wsmousedoopen---of 8
wsmouseioctl67%of 3
wsmousekqfilter100%of 3
wsmouseopen63%of 16
wsmouseread40%of 5
-----------
SUMMARY81%of 133
pctr_reload---of 13
pctr_resume---of 7
pctrattach---of 4
pctrclose100%of 1
pctrioctl6%of 38
pctropen100%of 1
-----------
SUMMARY10%of 40
sr_alloc_resources---of 3
sr_already_assembled---of 5
sr_attach---of 5
sr_bio_handler---of 26
sr_bio_ioctl---of 1
sr_block_get---of 1
sr_block_put---of 1
sr_boot_assembly---of 102
sr_ccb_alloc---of 6
sr_ccb_done---of 7
sr_ccb_free---of 5
sr_ccb_get---of 3
sr_ccb_put---of 1
sr_ccb_rw---of 4
sr_checksum---of 1
sr_checksum_print---of 1
sr_chunk_in_use---of 13
sr_chunks_unwind---of 7
sr_detach---of 10
sr_discipline_free---of 13
sr_discipline_init---of 9
sr_discipline_shutdown---of 24
sr_disk_attach34%of 6
sr_error---of 1
sr_free_resources---of 5
sr_hibernate_io---of 12
sr_hotplug_register---of 5
sr_hotplug_unregister---of 7
sr_hotspare---of 33
sr_hotspare_rebuild---of 27
sr_hotspare_rebuild_callback---of 1
sr_info---of 1
sr_ioctl_createraid---of 105
sr_ioctl_deleteraid---of 6
sr_ioctl_discipline---of 8
sr_ioctl_disk---of 17
sr_ioctl_inq---of 4
sr_ioctl_installboot---of 34
sr_ioctl_setstate---of 14
sr_ioctl_vol---of 16
sr_map_root---of 27
sr_match---of 1
sr_meta_attach---of 22
sr_meta_clear---of 8
sr_meta_getdevname---of 4
sr_meta_init---of 8
sr_meta_init_complete---of 1
sr_meta_native_attach---of 21
sr_meta_native_bootprobe---of 18
sr_meta_native_probe---of 6
sr_meta_native_read---of 3
sr_meta_native_write---of 3
sr_meta_opt_handler---of 3
sr_meta_opt_load---of 9
sr_meta_probe---of 15
sr_meta_read---of 12
sr_meta_rw---of 3
sr_meta_save---of 17
sr_meta_save_callback---of 3
sr_meta_validate---of 15
sr_quiesce---of 5
sr_raid_inquiry---of 4
sr_raid_intr---of 3
sr_raid_read_cap---of 5
sr_raid_recreate_wu---of 7
sr_raid_request_sense---of 1
sr_raid_start_stop---of 1
sr_raid_startwu---of 10
sr_raid_sync---of 4
sr_raid_tur---of 4
sr_rebuild---of 32
sr_rebuild_init---of 40
sr_rebuild_percent---of 3
sr_rebuild_start---of 3
sr_roam_chunks---of 7
sr_rw---of 14
sr_schedule_wu---of 13
sr_scsi_cmd---of 37
sr_scsi_done---of 6
sr_scsi_ioctl---of 4
sr_scsi_probe---of 6
sr_scsi_wu_get---of 1
sr_scsi_wu_put---of 4
sr_sensors_create---of 4
sr_sensors_delete---of 3
sr_sensors_refresh---of 6
sr_set_chunk_state---of 4
sr_set_vol_state---of 7
sr_shutdown---of 5
sr_uuid_format---of 1
sr_uuid_generate---of 1
sr_uuid_print---of 1
sr_validate_io---of 10
sr_validate_stripsize---of 5
sr_warn---of 1
sr_wu_alloc---of 4
sr_wu_done---of 3
sr_wu_done_callback---of 27
sr_wu_enqueue_ccb---of 3
sr_wu_free---of 13
sr_wu_get---of 3
sr_wu_init---of 3
sr_wu_put---of 6
sr_wu_release_ccbs---of 4
-----------
SUMMARY34%of 6
uvm_obj_destroy67%of 3
uvm_obj_free60%of 5
uvm_obj_init100%of 3
uvm_obj_setlock---of 5
uvm_obj_unwire---of 6
uvm_obj_wire---of 17
-----------
SUMMARY73%of 11
acct_process---of 24
acct_shutdown---of 3
acct_start---of 3
acct_thread---of 8
encode_comp_t---of 5
sys_acct84%of 12
-----------
SUMMARY84%of 12
cancel_all_itimers---of 1
clock_gettime62%of 21
inittodr---of 11
itimerdecr---of 15
itimerfix---of 16
perform_resettodr---of 5
periodic_resettodr---of 1
ppsratecheck50%of 10
ratecheck43%of 7
realitexpire---of 16
resettodr---of 5
setitimer92%of 23
settime55%of 11
start_periodic_resettodr---of 1
stop_periodic_resettodr---of 1
sys_adjfreq---of 10
sys_adjtime---of 20
sys_clock_getres82%of 16
sys_clock_gettime84%of 6
sys_clock_settime100%of 6
sys_getitimer100%of 3
sys_gettimeofday---of 8
sys_nanosleep80%of 20
sys_setitimer100%of 24
sys_settimeofday---of 15
todr_attach---of 1
-----------
SUMMARY79%of 147
filt_ugenrdetach---of 1
filt_ugenread_intr---of 1
filt_ugenread_isoc---of 4
ugen_attach---of 7
ugen_clear_iface_eps---of 24
ugen_detach---of 46
ugen_do_close---of 17
ugen_do_ioctl---of 62
ugen_do_read---of 35
ugen_do_write---of 16
ugen_get_alt_index---of 3
ugen_isoc_rintr---of 28
ugen_match---of 1
ugen_set_config---of 32
ugen_set_interface---of 11
ugenclose---of 5
ugenintr---of 5
ugenioctl---of 3
ugenkqfilter---of 10
ugenopen6%of 35
ugenread---of 3
ugenwrite---of 3
-----------
SUMMARY6%of 35
check_exec53%of 23
copyargs---of 16
exec_sigcode_map---of 8
exec_timekeep_map---of 7
sys_execve3%of 94
-----------
SUMMARY12%of 117
etherip_clone_create---of 1
etherip_clone_destroy---of 6
etherip_del_tunnel---of 1
etherip_down---of 3
etherip_find---of 16
etherip_get_tunnel---of 5
etherip_input---of 14
etherip_ioctl---of 35
etherip_media_change---of 1
etherip_media_status---of 1
etherip_output---of 4
etherip_set_tunnel---of 24
etherip_start---of 11
etherip_sysctl100%of 5
etherip_sysctl_etheripstat---of 1
etherip_up---of 3
etheripattach---of 1
ip6_etherip_input---of 1
ip6_etherip_output---of 12
ip_etherip_input---of 1
ip_etherip_output---of 6
-----------
SUMMARY100%of 5
in6_get_hw_ifid35%of 23
in6_get_ifid47%of 13
in6_get_rand_ifid---of 1
in6_ifattach43%of 14
in6_ifattach_linklocal40%of 15
in6_ifattach_loopback75%of 4
in6_ifdetach86%of 14
-----------
SUMMARY50%of 83
change_displayparam---of 5
internal_command---of 58
update_leds---of 4
update_modifier---of 9
wskbd_activate---of 3
wskbd_add_mux84%of 6
wskbd_attach---of 25
wskbd_cnattach---of 3
wskbd_cnbell---of 4
wskbd_cndetach---of 3
wskbd_cngetc---of 8
wskbd_cnpollc---of 5
wskbd_debugger---of 4
wskbd_deliver_event---of 8
wskbd_detach---of 23
wskbd_displayioctl---of 1
wskbd_displayioctl_sc78%of 63
wskbd_do_ioctl67%of 3
wskbd_do_ioctl_sc77%of 13
wskbd_do_open---of 7
wskbd_enable---of 4
wskbd_input---of 15
wskbd_match---of 5
wskbd_mux_close50%of 4
wskbd_mux_open25%of 8
wskbd_pickfree---of 7
wskbd_rawinput---of 3
wskbd_repeat---of 7
wskbd_set_console_display---of 3
wskbd_set_display---of 11
wskbd_set_keymap---of 1
wskbd_translate---of 103
wskbd_update_layout---of 1
wskbdclose23%of 9
wskbddevprint---of 3
wskbdioctl67%of 3
wskbdkqfilter67%of 3
wskbdopen40%of 15
wskbdread---of 5
-----------
SUMMARY65%of 127
copypktopts63%of 16
in6_delayed_cksum40%of 10
in6_proto_cksum_out66%of 35
ip6_clearpktopts61%of 33
ip6_copyexthdr---of 8
ip6_ctloutput89%of 184
ip6_fragment35%of 20
ip6_freemoptions100%of 7
ip6_freepcbopts100%of 3
ip6_getmoptions---of 8
ip6_getpcbopt76%of 25
ip6_getpmtu---of 7
ip6_initpktopts---of 1
ip6_insert_jumboopt25%of 8
ip6_insertfraghdr---of 10
ip6_mloopback50%of 18
ip6_output68%of 189
ip6_output_ipsec_lookup19%of 11
ip6_output_ipsec_pmtu_update---of 15
ip6_output_ipsec_send---of 16
ip6_pcbopt---of 3
ip6_randomid---of 1
ip6_randomid_init---of 1
ip6_raw_ctloutput67%of 12
ip6_setmoptions90%of 80
ip6_setpktopt87%of 72
ip6_setpktopts79%of 14
ip6_splithdr60%of 5
-----------
SUMMARY75%of 742
sema_reallocate---of 1
semctl158%of 71
semexit---of 14
seminit---of 1
semu_alloc---of 7
semundo_adjust42%of 29
semundo_clear50%of 18
sys___semctl100%of 9
sys_semget86%of 27
sys_semop73%of 62
sysctl_sysvsem89%of 9
-----------
SUMMARY66%of 225
allocate_copybuffer---of 11
button_event---of 17
class_cmp---of 4
ctrl_event---of 16
inverse_char---of 5
inverse_region---of 4
motion_event---of 10
mouse_copy_end---of 4
mouse_copy_extend---of 7
mouse_copy_extend_after---of 10
mouse_copy_extend_char---of 34
mouse_copy_extend_line---of 32
mouse_copy_extend_word---of 48
mouse_copy_line---of 12
mouse_copy_selection---of 7
mouse_copy_start---of 12
mouse_copy_word---of 23
mouse_hide---of 3
mouse_moverel---of 10
mouse_paste---of 6
mouse_remove---of 9
mouse_zaxis---of 7
remove_selection---of 6
skip_char_left---of 4
skip_char_right---of 4
skip_spc_left---of 4
skip_spc_right---of 6
wsdisplay_activate---of 5
wsdisplay_addscreen---of 14
wsdisplay_addscreen_print---of 3
wsdisplay_brightness_cycle---of 4
wsdisplay_brightness_step---of 15
wsdisplay_brightness_zero---of 11
wsdisplay_burn---of 7
wsdisplay_burner---of 4
wsdisplay_burner_setup---of 12
wsdisplay_cfg_ioctl---of 31
wsdisplay_closescreen---of 20
wsdisplay_cnattach---of 7
wsdisplay_cnputc---of 6
wsdisplay_common_attach---of 26
wsdisplay_common_detach---of 42
wsdisplay_delscreen---of 18
wsdisplay_driver_ioctl---of 6
wsdisplay_emul_attach---of 6
wsdisplay_emul_detach---of 1
wsdisplay_emul_match---of 8
wsdisplay_emulbell---of 6
wsdisplay_emulinput---of 7
wsdisplay_enter_ddb---of 5
wsdisplay_get_param---of 14
wsdisplay_getactivescreen---of 3
wsdisplay_getc_dummy---of 1
wsdisplay_getscreen---of 7
wsdisplay_internal_ioctl---of 63
wsdisplay_kbdholdscr---of 3
wsdisplay_kbdholdscreen---of 5
wsdisplay_kbdinput---of 9
wsdisplay_maxscreenidx---of 1
wsdisplay_param---of 6
wsdisplay_pollc---of 6
wsdisplay_rawkbdinput---of 6
wsdisplay_reset---of 5
wsdisplay_resume---of 7
wsdisplay_resume_device---of 3
wsdisplay_screenstate---of 4
wsdisplay_screentype_pick---of 7
wsdisplay_set_cons_kbd---of 1
wsdisplay_set_console_kbd---of 4
wsdisplay_set_param---of 10
wsdisplay_suspend---of 6
wsdisplay_suspend_device---of 43
wsdisplay_switch---of 26
wsdisplay_switch1---of 9
wsdisplay_switch2---of 10
wsdisplay_switch3---of 13
wsdisplay_switchtoconsole---of 4
wsdisplay_unset_cons_kbd---of 1
wsdisplay_update_rawkbd---of 8
wsdisplayclose---of 14
wsdisplayioctl---of 14
wsdisplaykqfilter---of 5
wsdisplaymmap---of 5
wsdisplayopen15%of 14
wsdisplayparam---of 1
wsdisplayread---of 5
wsdisplaystart---of 30
wsdisplaystop---of 3
wsdisplaytty---of 4
wsdisplaywrite---of 5
wsemuldisplaydevprint---of 3
wsemuldisplaydevsubmatch---of 3
wsmoused---of 18
wsscreen_attach---of 8
wsscreen_attach_sync---of 4
wsscreen_detach---of 3
wsscreen_detach_sync---of 3
wsscreen_lookup_sync---of 3
wsscreen_switchwait---of 9
wsscrollback---of 6
-----------
SUMMARY15%of 14
ffs_fsync48%of 34
ffs_read57%of 32
ffs_reclaim50%of 4
ffs_write57%of 57
ffsfifo_reclaim50%of 4
-----------
SUMMARY54%of 131
add_child_sensors---of 18
add_sdr_sensor---of 6
bmc_io_wait---of 6
bmc_read---of 3
bmc_write---of 3
bt_buildmsg---of 4
bt_probe---of 9
bt_read---of 3
bt_recvmsg---of 38
bt_reset---of 1
bt_sendmsg---of 27
bt_write---of 7
cmn_buildmsg---of 4
dumpb---of 4
get_sdr---of 17
get_sdr_partial---of 7
getbits---of 7
ipmi_activate---of 3
ipmi_attach---of 1
ipmi_attach_common---of 6
ipmi_cmd---of 3
ipmi_cmd_poll---of 8
ipmi_cmd_wait---of 4
ipmi_cmd_wait_cb---of 1
ipmi_convert---of 23
ipmi_create_thread---of 3
ipmi_get_if---of 3
ipmi_map_regs---of 7
ipmi_match---of 7
ipmi_poll_thread---of 10
ipmi_probe---of 6
ipmi_recvcmd---of 6
ipmi_refresh_sensors---of 3
ipmi_sendcmd---of 3
ipmi_sensor_name---of 21
ipmi_sensor_status---of 22
ipmi_sensor_type---of 10
ipmi_smbios_probe---of 11
ipmi_unmap_regs---of 3
ipmi_watchdog---of 5
ipmi_watchdog_set---of 5
ipmi_watchdog_tickle---of 3
ipmiclose---of 1
ipmiioctl---of 22
ipmilookup---of 1
ipmiopen100%of 1
ipow---of 14
kcs_probe---of 3
kcs_read_data---of 6
kcs_recvmsg---of 9
kcs_reset---of 1
kcs_sendmsg---of 12
kcs_wait---of 16
kcs_write_cmd---of 3
kcs_write_data---of 3
read_sensor---of 7
scan_sig---of 5
signextend---of 1
smic_probe---of 3
smic_read_data---of 9
smic_recvmsg---of 14
smic_reset---of 1
smic_sendmsg---of 8
smic_wait---of 7
smic_write_cmd_data---of 22
-----------
SUMMARY100%of 1
nd6_cache_lladdr---of 43
nd6_expire---of 21
nd6_expire_timer---of 1
nd6_expire_timer_update89%of 9
nd6_free---of 9
nd6_ifattach100%of 1
nd6_ifdetach100%of 1
nd6_init---of 3
nd6_invalidate---of 1
nd6_ioctl60%of 15
nd6_is_addr_neighbor---of 21
nd6_llinfo_settimer58%of 7
nd6_llinfo_timer---of 19
nd6_lookup---of 18
nd6_need_cache---of 5
nd6_nud_hint---of 10
nd6_option---of 10
nd6_option_init---of 3
nd6_options---of 29
nd6_purge55%of 11
nd6_resolve45%of 18
nd6_rtrequest29%of 46
nd6_slowtimo---of 7
nd6_timer---of 10
-----------
SUMMARY47%of 108
ip_randomid50%of 6
-----------
SUMMARY50%of 6
component_pop---of 7
component_push---of 4
namei69%of 67
ndinitat100%of 1
vfs_lookup62%of 93
vfs_relookup35%of 26
-----------
SUMMARY61%of 187
calibrate_tsc_freq---of 5
cpu_recalibrate_tsc---of 8
measure_tsc_freq---of 20
tsc_delay---of 3
tsc_freq_cpuid---of 15
tsc_get_timecount100%of 1
tsc_identify---of 6
tsc_timecounter_init---of 9
-----------
SUMMARY100%of 1
pf_syncookie_generate---of 14
pf_syncookie_mac---of 5
pf_syncookie_newkey---of 1
pf_syncookie_recreate_syn---of 1
pf_syncookie_rotate---of 10
pf_syncookie_send---of 14
pf_syncookie_validate40%of 5
pf_syncookies_getwats100%of 1
pf_syncookies_init---of 3
pf_syncookies_setmode60%of 5
pf_syncookies_setwats100%of 3
pf_synflood_check30%of 10
-----------
SUMMARY50%of 24
ufs_ihash---of 1
ufs_ihashget75%of 8
ufs_ihashinit---of 1
ufs_ihashins78%of 9
ufs_ihashlookup---of 7
ufs_ihashrem60%of 5
-----------
SUMMARY73%of 22
ffs_indirtrunc49%of 41
ffs_truncate39%of 162
ffs_update50%of 16
-----------
SUMMARY42%of 219
GetNewKeyFromSHA---of 1
adjust_tcp_mss---of 17
ip_is_idle_packet---of 33
mppe_key_change---of 6
pipex_ccp_input---of 6
pipex_ccp_output---of 3
pipex_common_input---of 21
pipex_destroy_all_sessions45%of 9
pipex_export_session_stats---of 1
pipex_get_closed---of 10
pipex_get_stat---of 7
pipex_init---of 5
pipex_init_session66%of 41
pipex_ioctl17%of 12
pipex_ip6_input---of 4
pipex_ip_input---of 19
pipex_ip_output---of 18
pipex_l2tp_input---of 23
pipex_l2tp_lookup_session---of 4
pipex_l2tp_output---of 14
pipex_l2tp_userland_lookup_session39%of 21
pipex_l2tp_userland_lookup_session_ipv4100%of 1
pipex_l2tp_userland_lookup_session_ipv6100%of 1
pipex_l2tp_userland_output---of 8
pipex_link_session38%of 35
pipex_lookup_by_ip_address---of 5
pipex_lookup_by_ip_address_locked---of 4
pipex_lookup_by_session_id---of 8
pipex_lookup_by_session_id_locked---of 8
pipex_mppe_init---of 12
pipex_mppe_input---of 33
pipex_mppe_output---of 17
pipex_mppe_reduce_key---of 4
pipex_notify_close_session---of 4
pipex_ppp_input---of 23
pipex_ppp_output---of 11
pipex_ppp_proto---of 7
pipex_pppoe_input---of 4
pipex_pppoe_lookup_session---of 7
pipex_pppoe_output---of 8
pipex_pptp_input---of 28
pipex_pptp_lookup_session---of 4
pipex_pptp_output---of 14
pipex_pptp_userland_lookup_session30%of 17
pipex_pptp_userland_lookup_session_ipv4100%of 1
pipex_pptp_userland_lookup_session_ipv6---of 1
pipex_pptp_userland_output---of 10
pipex_rele_session---of 4
pipex_session_init_mppe_recv---of 1
pipex_session_init_mppe_send---of 1
pipex_session_log---of 5
pipex_sockaddr_compar_addr---of 6
pipex_sockaddr_hash_key---of 4
pipex_sysctl100%of 4
pipex_timer---of 17
pipex_timer_start---of 1
pipex_timer_stop---of 1
pipex_unlink_session---of 1
pipex_unlink_session_locked40%of 20
pipexintr---of 11
-----------
SUMMARY46%of 162
bawrite100%of 1
bcstats_print---of 1
bdwrite100%of 3
bio_doread---of 8
biodone50%of 18
biowait63%of 8
bread100%of 7
bread_cluster64%of 22
bread_cluster_callback---of 23
breadn---of 19
brelse53%of 40
buf_adjcnt67%of 3
buf_daemon---of 29
buf_dirty---of 6
buf_flip_dma34%of 9
buf_flip_high---of 8
buf_get43%of 33
buf_put56%of 18
buf_undirty---of 6
bufadjust---of 13
bufbackoff---of 23
bufcache_adjust---of 6
bufcache_getcleanbuf---of 15
bufcache_getcleanbuf_range---of 6
bufcache_getdirtybuf---of 1
bufcache_getdmacleanbuf---of 5
bufcache_gethighcleanbuf---of 3
bufcache_init---of 1
bufcache_recover_dmapages---of 26
bufcache_release40%of 15
bufcache_take54%of 13
bufinit---of 13
bwrite71%of 17
chillbufs72%of 7
discard_buffer---of 5
getblk63%of 8
geteblk67%of 3
hibernate_resume_bufcache---of 1
hibernate_suspend_bufcache---of 19
incore---of 3
-----------
SUMMARY57%of 225
ffs1_blkpref---of 26
ffs2_blkpref41%of 27
ffs_alloc31%of 26
ffs_alloccg52%of 33
ffs_alloccgblk35%of 20
ffs_blkfree32%of 44
ffs_cgread---of 5
ffs_clusteracct10%of 22
ffs_dirpref59%of 43
ffs_fragextend65%of 28
ffs_freefile53%of 17
ffs_hashalloc---of 9
ffs_inode_alloc29%of 39
ffs_inode_free67%of 3
ffs_mapsearch48%of 19
ffs_nodealloccg47%of 47
ffs_realloccg30%of 57
-----------
SUMMARY41%of 425
bdevvp---of 8
bgetvp54%of 13
brelvp57%of 16
buf_rb_bufs_RBT_COMPARE100%of 1
buf_replacevnode---of 14
cdevvp50%of 8
checkalias66%of 23
copy_statfs_info---of 3
fs_posix_sysctl---of 4
fs_sysctl100%of 5
getdevvp---of 8
getnewvnode48%of 17
insmntque---of 5
reassignbuf65%of 17
vaccess100%of 5
vattr_null100%of 1
vclean48%of 21
vcount90%of 10
vdevgone50%of 10
vdrop34%of 6
vfinddev86%of 7
vflush---of 5
vflush_vnode---of 16
vflushbuf---of 13
vfs_buf_print---of 3
vfs_busy100%of 1
vfs_export---of 7
vfs_export_lookup---of 7
vfs_free_addrlist---of 3
vfs_free_netcred---of 1
vfs_getnewfsid---of 9
vfs_getvfs---of 7
vfs_hang_addrlist---of 20
vfs_isbusy---of 1
vfs_mount_alloc---of 1
vfs_mount_foreach_vnode---of 5
vfs_mount_free---of 1
vfs_mount_print---of 13
vfs_mountedon90%of 10
vfs_rootmountalloc---of 3
vfs_shutdown---of 3
vfs_stall---of 13
vfs_stall_barrier67%of 3
vfs_syncwait---of 12
vfs_sysctl91%of 11
vfs_unbusy---of 1
vfs_unmountall---of 14
vfs_vnode_print---of 12
vget59%of 12
vgone100%of 1
vgonel79%of 28
vhold34%of 6
vinvalbuf59%of 39
vn_isdisk100%of 1
vnoperm100%of 4
vntblinit---of 1
vprint---of 25
vput34%of 9
vputonfreelist63%of 8
vrecycle67%of 3
vref50%of 4
vrele34%of 9
vwaitforio67%of 6
vwakeup43%of 7
-----------
SUMMARY63%of 322
pf_get_sport24%of 42
pf_get_transaddr67%of 15
pf_get_transaddr_af---of 30
pf_hash---of 4
pf_map_addr5%of 138
pf_map_addr_states_increase---of 8
pf_map_addr_sticky---of 39
pf_postprocess_addr---of 20
pf_rand_addr---of 1
-----------
SUMMARY14%of 195
ufsdirhash_add---of 10
ufsdirhash_adjfree---of 10
ufsdirhash_build9%of 59
ufsdirhash_checkblock---of 19
ufsdirhash_delslot---of 12
ufsdirhash_dirtrunc---of 19
ufsdirhash_enduseful25%of 8
ufsdirhash_findfree25%of 24
ufsdirhash_findslot---of 7
ufsdirhash_free---of 10
ufsdirhash_getprev---of 6
ufsdirhash_hash---of 1
ufsdirhash_init---of 1
ufsdirhash_lookup38%of 48
ufsdirhash_move---of 5
ufsdirhash_newblk---of 7
ufsdirhash_recycle---of 12
ufsdirhash_remove---of 5
ufsdirhash_uninit---of 3
-----------
SUMMARY23%of 139
tcp_output62%of 212
tcp_sack_adjust---of 8
tcp_sack_output---of 9
tcp_setpersist---of 4
-----------
SUMMARY62%of 212
-----------
SUMMARY---of 0
divert_abort---of 1
divert_attach50%of 6
divert_bind100%of 1
divert_detach67%of 3
divert_init---of 1
divert_lock67%of 3
divert_output16%of 19
divert_packet---of 15
divert_send100%of 1
divert_shutdown100%of 1
divert_sysctl100%of 4
divert_sysctl_divstat---of 1
divert_unlock67%of 3
-----------
SUMMARY47%of 41
keycb_ref100%of 1
keycb_unref100%of 1
pfdatatopacket---of 3
pfkey_init---of 1
pfkey_sendup---of 6
pfkeyv2_abort---of 1
pfkeyv2_acquire---of 33
pfkeyv2_attach80%of 5
pfkeyv2_detach58%of 14
pfkeyv2_disconnect100%of 1
pfkeyv2_dosend3%of 234
pfkeyv2_dump_policy---of 25
pfkeyv2_dump_walker---of 9
pfkeyv2_expire---of 14
pfkeyv2_get---of 63
pfkeyv2_get_proto_alg---of 15
pfkeyv2_output50%of 6
pfkeyv2_peeraddr100%of 1
pfkeyv2_policy---of 22
pfkeyv2_policy_flush---of 1
pfkeyv2_sa_flush---of 4
pfkeyv2_send84%of 6
pfkeyv2_sendmessage---of 54
pfkeyv2_shutdown100%of 1
pfkeyv2_sockaddr100%of 1
pfkeyv2_sysctl93%of 14
pfkeyv2_sysctl_policydumper---of 33
pfkeyv2_sysctl_walker---of 20
-----------
SUMMARY16%of 285
msts_date_to_nano---of 7
msts_decode15%of 14
msts_scan---of 8
msts_time_to_nano---of 16
msts_timeout---of 3
mstsattach---of 1
mstsclose100%of 3
mstsinput82%of 16
mstsopen80%of 5
-----------
SUMMARY58%of 38
ipe4_attach---of 1
ipe4_init---of 1
ipe4_input---of 1
ipe4_zeroize---of 1
ipip_init---of 1
ipip_input---of 6
ipip_input_if---of 38
ipip_output---of 39
ipip_sysctl100%of 5
ipip_sysctl_ipipstat---of 1
-----------
SUMMARY100%of 5
lf_advlock81%of 26
lf_alloc---of 4
lf_clearlock89%of 18
lf_deadlock59%of 17
lf_findoverlap90%of 28
lf_free50%of 6
lf_getblock---of 6
lf_getlock78%of 9
lf_init---of 1
lf_purgelocks69%of 19
lf_setlock87%of 67
lf_split88%of 8
lf_wakelock---of 4
ls_ref---of 1
ls_rele---of 5
-----------
SUMMARY81%of 198
futex_get30%of 17
futex_init---of 1
futex_put34%of 6
futex_requeue25%of 8
futex_wait25%of 16
futex_wake---of 1
sys_futex38%of 8
-----------
SUMMARY30%of 55
-----------
SUMMARY---of 0
ah46_input---of 12
ah4_ctlinput---of 4
ah_sysctl100%of 4
ah_sysctl_ahstat---of 1
esp46_input---of 12
esp4_ctlinput---of 4
esp_sysctl100%of 4
esp_sysctl_espstat---of 1
ipcomp46_input---of 12
ipcomp_sysctl100%of 4
ipcomp_sysctl_ipcompstat---of 1
ipsec_common_ctlinput---of 11
ipsec_common_input---of 42
ipsec_common_input_cb---of 50
ipsec_forward_check---of 3
ipsec_init---of 1
ipsec_input_disabled---of 4
ipsec_local_check---of 17
ipsec_protoff---of 7
ipsec_set_mtu---of 8
ipsec_sysctl100%of 6
ipsec_sysctl_ipsecstat---of 1
udpencap_ctlinput---of 17
-----------
SUMMARY100%of 18
m_pulldown29%of 38
m_tag_copy---of 4
m_tag_copy_chain24%of 13
m_tag_delete---of 7
m_tag_delete_chain75%of 4
m_tag_find34%of 6
m_tag_first---of 1
m_tag_get40%of 5
m_tag_init---of 1
m_tag_next---of 1
m_tag_prepend100%of 1
-----------
SUMMARY33%of 67
exec_process_vmcmds---of 13
exec_setup_stack---of 11
kill_vmcmds38%of 8
vmcmd_map_pagedvn---of 7
vmcmd_map_readvn---of 6
vmcmd_map_zero---of 3
vmcmd_randomize---of 9
vmcmdset_extend---of 5
-----------
SUMMARY38%of 8
scsi_do_ioctl16%of 13
scsi_ioc_ata_cmd---of 19
scsi_ioc_cmd---of 24
-----------
SUMMARY16%of 13
sysctl_wdog20%of 10
wdog_register---of 3
wdog_shutdown---of 3
wdog_tickle---of 3
-----------
SUMMARY20%of 10
chgproccnt67%of 3
db_kill_cmd---of 5
db_show_all_procs---of 32
enternewpgrp58%of 7
enterthispgrp59%of 17
fixjobc18%of 23
inferior60%of 5
killjobc---of 9
leavepgrp---of 7
pgdelete50%of 8
pgfind100%of 5
prfind100%of 5
proc_printit---of 3
procinit---of 3
tfind60%of 5
uid_find55%of 11
uid_release100%of 1
zapverauth---of 1
zombiefind---of 5
-----------
SUMMARY53%of 90
free59%of 24
kmeminit---of 10
kmeminit_nkmempages---of 6
malloc54%of 47
malloc_printit---of 6
mallocarray60%of 5
sysctl_malloc37%of 33
-----------
SUMMARY50%of 109
art_alloc---of 7
art_allot40%of 15
art_bindex25%of 8
art_delete36%of 14
art_gc---of 4
art_get67%of 3
art_init---of 1
art_insert57%of 16
art_lookup72%of 14
art_match80%of 10
art_put67%of 3
art_table_delete85%of 13
art_table_free---of 6
art_table_gc---of 7
art_table_get54%of 13
art_table_insert89%of 9
art_table_put30%of 10
art_table_ref---of 1
art_table_walk66%of 29
art_walk56%of 9
art_walk_apply---of 3
-----------
SUMMARY59%of 166
ppp_pkt_free---of 4
ppp_timeout---of 9
pppasyncctlp---of 1
pppasyncrelinq---of 6
pppasyncstart---of 58
pppclose---of 4
pppdumpb---of 5
pppfcs---of 7
pppinput---of 94
ppplogchar---of 10
pppopen32%of 16
ppppkt---of 6
pppread---of 14
pppstart---of 10
pppstart_internal---of 10
ppptioctl---of 18
pppwrite---of 15
-----------
SUMMARY32%of 16
cpu_is_online67%of 3
cpuset_add---of 3
cpuset_add_all---of 1
cpuset_cardinality---of 15
cpuset_clear---of 1
cpuset_complement---of 4
cpuset_copy---of 1
cpuset_del---of 3
cpuset_first---of 5
cpuset_init_cpu---of 5
cpuset_intersection---of 4
cpuset_isset---of 3
cpuset_union---of 4
remrunqueue67%of 12
sched_barrier---of 1
sched_choosecpu---of 1
sched_choosecpu_fork---of 1
sched_chooseproc55%of 11
sched_exit---of 7
sched_idle---of 24
sched_init_cpu---of 11
sched_init_runqueues---of 1
sched_kthreads_create---of 3
sched_peg_curproc---of 1
sched_proc_to_cpu_cost---of 1
sched_steal_proc---of 1
setrunqueue55%of 11
sysctl_hwncpuonline20%of 15
sysctl_hwsmt75%of 4
-----------
SUMMARY50%of 56
dt_dev_alloc_probe---of 3
dt_dev_register_probe---of 1
dt_ioctl_get_stats---of 1
dt_ioctl_list_probes---of 7
dt_ioctl_probe_disable---of 9
dt_ioctl_probe_enable---of 9
dt_ioctl_record_start---of 6
dt_ioctl_record_stop---of 8
dt_pcb_alloc---of 4
dt_pcb_filter---of 10
dt_pcb_free---of 3
dt_pcb_purge---of 4
dt_pcb_ring_consume---of 4
dt_pcb_ring_copy---of 5
dt_pcb_ring_get---of 19
dtattach---of 1
dtclose42%of 17
dtioc_req_isvalid---of 3
dtioctl8%of 40
dtlookup---of 5
dtopen56%of 9
dtread18%of 17
-----------
SUMMARY22%of 83
buf_acquire50%of 4
buf_acquire_nomap84%of 6
buf_alloc_pages30%of 10
buf_dealloc_mem62%of 13
buf_fix_mapping---of 3
buf_free_pages42%of 12
buf_map58%of 14
buf_mem_init---of 3
buf_realloc_pages---of 20
buf_release43%of 7
buf_unmap45%of 9
-----------
SUMMARY51%of 75
cpu_amd64speed100%of 1
cpu_check_vmm_cap---of 32
cpu_freq---of 3
cpu_freq_ctr---of 6
cpu_hz_update_sensor---of 3
cpu_topology---of 13
identifycpu---of 165
intelcore_update_sensor---of 12
via_nano_setup---of 20
via_update_sensor---of 1
-----------
SUMMARY100%of 1
ddb_sysctl50%of 12
-----------
SUMMARY50%of 12
-----------
SUMMARY---of 0
ipsec_delete_policy---of 21
ipsec_get_acquire---of 7
ipsec_unref_acquire---of 1
ipsp_acquire_sa---of 16
ipsp_delete_acquire---of 3
ipsp_delete_acquire_locked---of 3
ipsp_delete_acquire_timer---of 3
ipsp_pending_acquire---of 7
ipsp_spd_inp---of 12
ipsp_spd_lookup4%of 225
ipsp_unref_acquire_locked---of 7
spd_table_add---of 10
spd_table_get---of 6
spd_table_walk38%of 8
-----------
SUMMARY5%of 233
crcopy---of 4
crdup100%of 1
crfree100%of 3
crfromxucred---of 3
crget100%of 1
crhold100%of 1
crset---of 3
dorefreshcreds75%of 4
groupmember100%of 6
proc_cansugid---of 3
suser100%of 1
suser_ucred100%of 1
sys___get_tcb---of 1
sys___set_tcb---of 3
sys_getegid100%of 1
sys_geteuid100%of 1
sys_getgid100%of 1
sys_getgroups100%of 5
sys_getlogin_r---of 1
sys_getpgid100%of 6
sys_getpgrp100%of 1
sys_getpid100%of 1
sys_getppid100%of 1
sys_getresgid---of 7
sys_getresuid---of 7
sys_getsid---of 8
sys_getthrid---of 1
sys_getuid100%of 1
sys_issetugid---of 1
sys_setegid67%of 9
sys_seteuid67%of 9
sys_setgid75%of 16
sys_setgroups72%of 7
sys_setlogin---of 5
sys_setpgid70%of 20
sys_setregid88%of 39
sys_setresgid---of 38
sys_setresuid---of 38
sys_setreuid93%of 39
sys_setsid---of 4
sys_setuid77%of 17
-----------
SUMMARY84%of 192
uaddr_bestfit_create---of 1
uaddr_bestfit_destroy---of 1
uaddr_bestfit_insert67%of 3
uaddr_bestfit_remove67%of 3
uaddr_bestfit_select70%of 10
uaddr_destroy100%of 1
uaddr_free_rbtree_RBT_COMPARE100%of 3
uaddr_kbootstrap_destroy---of 3
uaddr_kbootstrap_select29%of 7
uaddr_pivot_create---of 1
uaddr_pivot_destroy---of 1
uaddr_pivot_insert---of 8
uaddr_pivot_newpivot---of 15
uaddr_pivot_print---of 13
uaddr_pivot_random---of 1
uaddr_pivot_remove---of 34
uaddr_pivot_select---of 9
uaddr_rnd_create100%of 1
uaddr_rnd_destroy100%of 1
uaddr_rnd_insert100%of 1
uaddr_rnd_remove100%of 1
uaddr_rnd_select---of 34
uaddr_stack_brk_create100%of 1
uaddr_stack_brk_select---of 7
uvm_addr_align---of 1
uvm_addr_align_back---of 1
uvm_addr_destroy100%of 3
uvm_addr_entrybyspace---of 6
uvm_addr_fitspace34%of 15
uvm_addr_init---of 1
uvm_addr_invoke50%of 10
uvm_addr_linsearch---of 24
uvm_addr_print---of 4
-----------
SUMMARY59%of 60
udf_checkexp---of 1
udf_checktag---of 3
udf_fhtovp---of 1
udf_find_partmaps---of 14
udf_get_mpartmap---of 1
udf_get_spartmap---of 10
udf_get_vpartmap---of 1
udf_init---of 1
udf_mount---of 11
udf_mountfs---of 51
udf_quotactl---of 1
udf_root---of 3
udf_start---of 1
udf_statfs---of 1
udf_sync---of 1
udf_sysctl100%of 1
udf_unmount---of 6
udf_vget---of 24
udf_vptofh---of 1
-----------
SUMMARY100%of 1
inet_ntop40%of 5
inet_ntop664%of 44
sockaddr_ntop---of 9
-----------
SUMMARY62%of 49
ipcperm100%of 6
-----------
SUMMARY100%of 6
pvbus_activate---of 1
pvbus_attach---of 33
pvbus_hyperv---of 3
pvbus_hyperv_print---of 1
pvbus_identify---of 15
pvbus_init_cpu---of 19
pvbus_kvm---of 1
pvbus_match---of 1
pvbus_minor---of 16
pvbus_print---of 3
pvbus_probe---of 1
pvbus_reboot---of 1
pvbus_search---of 3
pvbus_shutdown---of 1
pvbus_xen---of 4
pvbus_xen_print---of 1
pvbusclose11%of 19
pvbusgetstr---of 5
pvbusioctl20%of 41
pvbusopen43%of 19
-----------
SUMMARY23%of 79
-----------
SUMMARY---of 0
strlcpy86%of 7
-----------
SUMMARY86%of 7
_acpi_matchhids---of 5
acpi_add_device---of 14
acpi_addtask---of 3
acpi_attach_common---of 65
acpi_attach_deps---of 13
acpi_create_thread---of 3
acpi_disable_allgpes---of 14
acpi_dotask---of 4
acpi_enable---of 21
acpi_enable_onegpe---of 12
acpi_enable_rungpes---of 6
acpi_enable_wakegpes---of 6
acpi_filtdetach100%of 1
acpi_filtread50%of 4
acpi_find_gpe---of 3
acpi_find_pci---of 8
acpi_founddock---of 1
acpi_foundec---of 9
acpi_foundhid---of 28
acpi_foundide---of 9
acpi_foundprt---of 3
acpi_foundprw---of 10
acpi_foundsbs---of 6
acpi_foundsony---of 1
acpi_foundvideo---of 1
acpi_gasio---of 32
acpi_getminbus---of 5
acpi_getpci---of 22
acpi_getprop---of 19
acpi_getpropint---of 19
acpi_getsta---of 1
acpi_gpe---of 18
acpi_gpe_task---of 4
acpi_gpio_event---of 3
acpi_gpio_event_task---of 4
acpi_gpio_parse_events---of 7
acpi_indicator---of 3
acpi_inidev---of 3
acpi_init_gpes---of 13
acpi_init_pm---of 1
acpi_init_states---of 6
acpi_interrupt---of 43
acpi_loadtables---of 11
acpi_map_address---of 5
acpi_map_pmregs---of 44
acpi_maptable---of 20
acpi_matchcls---of 13
acpi_matchhids---of 12
acpi_parse_crs---of 3
acpi_parse_resources---of 48
acpi_parsehid---of 9
acpi_pbtn_task---of 6
acpi_pci_conf_read_1---of 1
acpi_pci_conf_read_2---of 1
acpi_pci_conf_read_4---of 1
acpi_pci_conf_write_1---of 1
acpi_pci_conf_write_2---of 1
acpi_pci_conf_write_4---of 1
acpi_pci_match---of 8
acpi_pci_min_powerstate---of 12
acpi_pci_notify---of 4
acpi_pci_set_powerstate---of 18
acpi_pciroots_attach---of 7
acpi_powerdown---of 3
acpi_powerdown_task---of 3
acpi_print---of 5
acpi_read_pmreg---of 44
acpi_record_event---of 4
acpi_register_gpio---of 6
acpi_register_gsb---of 4
acpi_reset---of 6
acpi_resume_pm---of 34
acpi_sbtn_task---of 1
acpi_set_gpehandler---of 7
acpi_sleep_pm---of 36
acpi_sleep_task---of 4
acpi_submatch---of 3
acpi_thread---of 19
acpi_unmap_pmregs---of 6
acpi_wakeup---of 1
acpi_write_pmreg---of 33
acpiclose50%of 6
acpiide_notify---of 13
acpiioctl5%of 45
acpikqfilter60%of 5
acpiopen89%of 9
is_ata---of 5
is_ejectable---of 1
is_ejectable_bay---of 10
-----------
SUMMARY28%of 70
_bpf_mtap79%of 14
bpf_allocbufs40%of 5
bpf_attachd---of 3
bpf_catchpacket54%of 13
bpf_d_smr---of 5
bpf_detachd59%of 17
bpf_get---of 1
bpf_getdltlist---of 9
bpf_ifname---of 1
bpf_mbuf_copy---of 7
bpf_mbuf_ldb100%of 4
bpf_mbuf_ldh58%of 7
bpf_mbuf_ldw72%of 7
bpf_mcopy---of 5
bpf_mfilter---of 1
bpf_movein55%of 24
bpf_mtap100%of 1
bpf_mtap_af---of 1
bpf_mtap_ether50%of 4
bpf_mtap_hdr---of 3
bpf_prog_smr---of 1
bpf_put---of 3
bpf_resetd---of 5
bpf_setdlt47%of 13
bpf_setf82%of 16
bpf_setif62%of 18
bpf_sysctl84%of 6
bpf_sysctl_locked---of 4
bpf_tap_hdr---of 5
bpf_wakeup73%of 11
bpf_wakeup_cb---of 6
bpfattach67%of 3
bpfclose67%of 9
bpfdetach84%of 6
bpfilter_lookup---of 5
bpfilterattach---of 1
bpfioctl85%of 88
bpfkqfilter67%of 6
bpfopen56%of 9
bpfread57%of 37
bpfsattach---of 3
bpfsdetach85%of 13
bpfwrite69%of 16
filt_bpfrdetach100%of 3
filt_bpfread75%of 4
filt_bpfreadmodify75%of 4
filt_bpfreadprocess78%of 9
-----------
SUMMARY71%of 367
db_show_all_pools---of 20
phtree_RBT_COMPARE100%of 1
pool_allocator_alloc---of 5
pool_allocator_free---of 1
pool_cache_cpus_info---of 1
pool_cache_info---of 1
pool_cache_init---of 1
pool_cache_pool_info---of 1
pool_chk---of 10
pool_chk_page---of 18
pool_destroy29%of 21
pool_do_get74%of 23
pool_do_put78%of 22
pool_gc_pages---of 15
pool_gc_sched---of 1
pool_get37%of 22
pool_get_done---of 1
pool_init---of 25
pool_lock_mtx_assert_locked67%of 3
pool_lock_mtx_assert_unlocked67%of 3
pool_lock_mtx_enter100%of 1
pool_lock_mtx_enter_try---of 1
pool_lock_mtx_init---of 1
pool_lock_mtx_leave100%of 1
pool_lock_mtx_sleep---of 1
pool_lock_rw_assert_locked100%of 1
pool_lock_rw_assert_unlocked67%of 3
pool_lock_rw_enter100%of 1
pool_lock_rw_enter_try---of 1
pool_lock_rw_init---of 1
pool_lock_rw_leave100%of 1
pool_lock_rw_sleep---of 1
pool_multi_alloc100%of 3
pool_multi_alloc_ni100%of 3
pool_multi_free67%of 3
pool_multi_free_ni---of 3
pool_p_alloc71%of 17
pool_p_free64%of 11
pool_p_insert---of 7
pool_p_remove---of 5
pool_page_alloc100%of 1
pool_page_free100%of 1
pool_prime---of 14
pool_print1---of 33
pool_print_pagelist---of 9
pool_printit---of 1
pool_put63%of 16
pool_reclaim---of 13
pool_reclaim_all---of 4
pool_request---of 1
pool_request_init---of 1
pool_runqueue---of 17
pool_set_constraints---of 1
pool_sethardlimit---of 3
pool_sethiwat---of 3
pool_setlowat---of 5
pool_update_curpage---of 3
pool_wakeup---of 3
pool_walk---of 16
sysctl_dopool89%of 17
-----------
SUMMARY66%of 175
db_print_ifa---of 17
db_print_sa---of 6
db_show_rtable---of 1
db_show_rtentry---of 11
ifafree100%of 3
ifaref100%of 1
route_init---of 4
rt_clone---of 3
rt_getll75%of 4
rt_hash36%of 14
rt_if_linkstate_change74%of 15
rt_if_track79%of 14
rt_ifa_add67%of 15
rt_ifa_addlocal89%of 9
rt_ifa_del74%of 23
rt_ifa_dellocal78%of 9
rt_ifa_purge75%of 12
rt_ifa_purge_walker100%of 1
rt_maskedcopy---of 6
rt_match80%of 5
rt_mpls_clear50%of 4
rt_mpls_set23%of 9
rt_plen2mask50%of 6
rt_plentosa---of 6
rt_putgwroute38%of 8
rt_setgate56%of 9
rt_setgwroute18%of 23
rt_timer_add18%of 17
rt_timer_get_expire50%of 4
rt_timer_init---of 1
rt_timer_queue_change100%of 1
rt_timer_queue_count---of 1
rt_timer_queue_flush25%of 16
rt_timer_queue_init---of 1
rt_timer_remove_all54%of 15
rt_timer_timer---of 10
rtalloc100%of 1
rtalloc_mpath100%of 1
rtdeletemsg36%of 14
rtflushclone63%of 8
rtflushclone160%of 10
rtfree43%of 14
rtisvalid75%of 8
rtlabel_id2name60%of 5
rtlabel_id2sa60%of 5
rtlabel_name2id72%of 14
rtlabel_unref84%of 6
rtredirect---of 27
rtref100%of 1
rtrequest47%of 65
rtrequest_delete65%of 14
-----------
SUMMARY55%of 403
ipsec_adjust_mtu17%of 18
ipsec_hdrsz---of 12
ipsp_process_done---of 27
ipsp_process_packet---of 68
-----------
SUMMARY17%of 18
pfkeyv2_parsemessage5%of 126
-----------
SUMMARY5%of 126
chkvnlock---of 4
dead_bmap---of 4
dead_ebadf100%of 1
dead_inactive100%of 1
dead_ioctl---of 4
dead_kqfilter100%of 6
dead_lock40%of 5
dead_open---of 1
dead_print---of 1
dead_read50%of 4
dead_strategy---of 5
dead_write50%of 4
-----------
SUMMARY67%of 21
sys_kbind---of 29
sys_madvise82%of 16
sys_minherit56%of 9
sys_mlock64%of 11
sys_mlockall100%of 3
sys_mmap73%of 74
sys_mprotect53%of 19
sys_mquery84%of 12
sys_msync77%of 13
sys_msyscall---of 9
sys_munlock56%of 9
sys_munlockall100%of 1
sys_munmap73%of 11
sys_pad_mmap100%of 1
sys_pad_mquery100%of 1
uvm_mmapanon---of 6
uvm_mmapfile79%of 14
uvm_mmaplock34%of 9
-----------
SUMMARY70%of 203
cpu_intr_init---of 8
intr_allocate_slot---of 25
intr_allocate_slot_cpu---of 24
intr_barrier---of 1
intr_calculatemasks---of 14
intr_default_setup---of 3
intr_disestablish---of 8
intr_establish---of 29
intr_handler---of 1
intr_printconfig---of 1
softintr100%of 1
spllower100%of 3
splraise67%of 3
x86_nmi---of 1
-----------
SUMMARY86%of 7
sensor_attach70%of 13
sensor_detach---of 6
sensor_find91%of 11
sensor_quiesce---of 4
sensor_restart---of 1
sensor_task_register---of 7
sensor_task_tick---of 1
sensor_task_unregister---of 1
sensor_task_work---of 7
sensordev_deinstall75%of 4
sensordev_get84%of 6
sensordev_install80%of 5
-----------
SUMMARY80%of 39
sys___getcwd---of 8
vfs_getcwd_common75%of 32
vfs_getcwd_getcache50%of 8
vfs_getcwd_scandir39%of 18
-----------
SUMMARY61%of 58
mountnfs---of 9
nfs_checkexp---of 1
nfs_decode_args---of 59
nfs_fhtovp---of 1
nfs_fsinfo---of 24
nfs_mount---of 13
nfs_mount_diskless---of 4
nfs_mountroot---of 8
nfs_quotactl---of 1
nfs_reaper---of 1
nfs_root---of 3
nfs_start---of 1
nfs_statfs---of 19
nfs_sync---of 12
nfs_sysctl80%of 10
nfs_unmount---of 7
nfs_vget---of 1
nfs_vptofh---of 1
-----------
SUMMARY80%of 10
uvm_meter---of 21
uvm_sysctl84%of 31
uvm_total---of 12
uvmexp_print---of 1
uvmexp_read---of 1
-----------
SUMMARY84%of 31
SHA256Final---of 5
SHA256Init---of 1
SHA256Transform---of 4
SHA256Update---of 10
SHA384Final---of 5
SHA384Init---of 1
SHA384Update---of 1
SHA512Final60%of 5
SHA512Init100%of 1
SHA512Last---of 5
SHA512Transform100%of 4
SHA512Update47%of 15
-----------
SUMMARY60%of 25
msg_copyin85%of 13
msg_copyout---of 7
msg_create---of 3
msg_dequeue---of 1
msg_enqueue---of 1
msg_free---of 1
msg_lookup---of 6
msgctl1---of 25
msginit---of 1
que_create77%of 13
que_free60%of 5
que_key_lookup---of 7
que_lookup---of 6
que_wakewriters---of 6
sys_msgctl84%of 25
sys_msgget86%of 14
sys_msgrcv70%of 33
sys_msgsnd55%of 24
sysctl_sysvmsg78%of 9
-----------
SUMMARY74%of 136
filt_midirdetach---of 1
filt_midiread---of 3
filt_midiwdetach---of 1
filt_midiwrite---of 3
midi_attach_mi---of 1
midi_buf_wakeup---of 3
midi_iintr---of 7
midi_ointr---of 8
midi_out_do---of 12
midi_out_start---of 3
midi_out_stop---of 3
midi_timeout---of 1
midiattach---of 9
midiclose---of 8
mididetach---of 15
midiioctl---of 3
midikqfilter---of 5
midiopen40%of 5
midiprint---of 3
midiprobe---of 3
midiread---of 11
midiwrite---of 16
-----------
SUMMARY40%of 5
copy_flow_data---of 6
copy_flow_ipfix_4_data---of 3
copy_flow_ipfix_4_to_m---of 8
copy_flow_ipfix_6_data---of 3
copy_flow_ipfix_6_to_m---of 8
copy_flow_to_m---of 8
export_pflow---of 10
export_pflow_if---of 14
pflow_calc_mtu---of 5
pflow_clone_create---of 4
pflow_clone_destroy---of 19
pflow_flush---of 4
pflow_get_mbuf---of 7
pflow_init_timeouts---of 14
pflow_output---of 1
pflow_output_process---of 6
pflow_pack_flow---of 19
pflow_pack_flow_ipfix---of 14
pflow_sendout_ipfix---of 10
pflow_sendout_ipfix_tmpl---of 8
pflow_sendout_mbuf---of 3
pflow_sendout_v5---of 5
pflow_set---of 70
pflow_setmtu---of 7
pflow_sysctl100%of 5
pflow_timeout---of 4
pflow_timeout6---of 1
pflow_timeout_tmpl---of 1
pflowattach---of 1
pflowioctl---of 35
pflowvalidsockaddr---of 13
-----------
SUMMARY100%of 5
in_ouraddr48%of 44
ip_deliver---of 21
ip_dooptions70%of 42
ip_flush---of 7
ip_forward---of 33
ip_fragcheck64%of 30
ip_freef---of 9
ip_init---of 13
ip_input_if53%of 40
ip_ours---of 4
ip_reass75%of 43
ip_savecontrol---of 16
ip_send100%of 1
ip_send_dispatch---of 1
ip_send_do_dispatch---of 7
ip_send_raw---of 1
ip_sendraw_dispatch---of 1
ip_slowtimo---of 6
ip_srcroute---of 8
ip_stripoptions---of 3
ip_sysctl100%of 34
ip_sysctl_ipstat100%of 1
ipintr---of 5
ipv4_check79%of 23
ipv4_input67%of 3
save_rte---of 4
-----------
SUMMARY69%of 261
arp_rtrequest38%of 27
arpcache---of 21
arpinit---of 1
arpinput100%of 3
arpintr---of 6
arpinvalidate---of 3
arplookup---of 8
arpproxy---of 5
arppullup75%of 12
arpreply---of 1
arprequest---of 3
arpresolve39%of 31
arptfree---of 7
arptimer---of 6
in_arpinput---of 29
in_revarpinput56%of 9
revarpinput100%of 3
revarprequest---of 3
revarpwhoami---of 1
revarpwhoarewe---of 9
-----------
SUMMARY50%of 85
filt_fuse_rdetach100%of 1
filt_fuse_read100%of 1
fuse_device_cleanup---of 18
fuse_device_queue_fbuf---of 5
fuse_device_set_fmp---of 5
fuse_lookup---of 5
fuseattach---of 1
fuseclose50%of 10
fuseioctl15%of 28
fusekqfilter72%of 7
fuseopen88%of 8
fuseread28%of 11
fusewrite21%of 24
-----------
SUMMARY35%of 90
uipc_abort100%of 1
uipc_accept84%of 6
uipc_attach58%of 14
uipc_bind100%of 1
uipc_connect100%of 1
uipc_connect267%of 3
uipc_detach67%of 3
uipc_disconnect100%of 1
uipc_listen100%of 1
uipc_peeraddr84%of 6
uipc_rcvd72%of 7
uipc_send90%of 30
uipc_sense---of 3
uipc_setaddr---of 4
uipc_shutdown100%of 6
uipc_sockaddr75%of 4
uipc_sysctl100%of 12
unp_bind74%of 15
unp_connect77%of 26
unp_connect267%of 6
unp_detach78%of 18
unp_discard---of 1
unp_disconnect80%of 10
unp_dispose75%of 12
unp_externalize72%of 38
unp_gc---of 87
unp_init---of 1
unp_internalize68%of 43
unp_nam2sun---of 10
unp_remove_gcrefs---of 12
unp_restore_gcrefs---of 11
unp_scan---of 12
unp_shutdown---of 6
unp_solock_peer72%of 7
-----------
SUMMARY77%of 271
filt_ttyexcept80%of 5
filt_ttyrdetach100%of 1
filt_ttyread87%of 15
filt_ttywdetach100%of 1
filt_ttywrite100%of 5
nullmodem---of 7
sysctl_tty100%of 8
tputchar100%of 9
ttioctl77%of 136
ttkqfilter100%of 7
ttread82%of 70
ttrstrt---of 4
ttsetwater100%of 1
ttspeedtab---of 5
ttstart---of 3
ttvtimeout---of 1
ttwakeup100%of 3
ttwakeupwr100%of 4
ttwrite66%of 58
tty_init---of 1
ttychars100%of 1
ttycheckoutq---of 11
ttyclose50%of 6
ttyecho100%of 11
ttyflush100%of 11
ttyfree---of 3
ttyinfo20%of 62
ttyinput90%of 146
ttylclose80%of 5
ttymalloc50%of 4
ttymodem79%of 14
ttyopen100%of 3
ttyoutput100%of 40
ttypend---of 6
ttyretype100%of 9
ttyrub92%of 23
ttysleep100%of 3
ttysleep_nsec---of 3
ttystats_init78%of 9
ttytstamp---of 8
ttyunblock77%of 13
ttywait---of 1
ttywait_nsec71%of 17
ttywflush---of 4
-----------
SUMMARY78%of 700
VOP_ABORTOP67%of 3
VOP_ACCESS38%of 8
VOP_ADVLOCK67%of 3
VOP_BMAP43%of 7
VOP_BWRITE67%of 3
VOP_CLOSE50%of 10
VOP_CREATE43%of 7
VOP_FSYNC45%of 9
VOP_GETATTR50%of 4
VOP_INACTIVE50%of 8
VOP_IOCTL50%of 4
VOP_ISLOCKED67%of 3
VOP_KQFILTER67%of 3
VOP_LINK43%of 7
VOP_LOCK50%of 4
VOP_LOOKUP67%of 3
VOP_MKDIR43%of 7
VOP_MKNOD43%of 7
VOP_OPEN50%of 4
VOP_PATHCONF---of 12
VOP_PRINT---of 3
VOP_READ58%of 7
VOP_READDIR43%of 7
VOP_READLINK43%of 7
VOP_RECLAIM50%of 4
VOP_REMOVE37%of 11
VOP_RENAME43%of 7
VOP_REVOKE67%of 3
VOP_RMDIR34%of 12
VOP_SETATTR50%of 8
VOP_STRATEGY50%of 6
VOP_SYMLINK43%of 7
VOP_UNLOCK67%of 3
VOP_WRITE58%of 7
-----------
SUMMARY48%of 193
alps_get_hwinfo---of 36
alps_sec_proc---of 6
elantech_get_hwinfo_v1---of 16
elantech_get_hwinfo_v2---of 31
elantech_get_hwinfo_v3---of 12
elantech_get_hwinfo_v4---of 19
elantech_knock---of 18
elantech_ps2_cmd---of 3
elantech_set_absolute_mode_v1---of 20
elantech_set_absolute_mode_v2---of 60
elantech_set_absolute_mode_v3---of 39
elantech_set_absolute_mode_v4---of 27
pms_change_state34%of 33
pms_cmd---of 3
pms_dev_disable---of 5
pms_dev_enable---of 5
pms_disable100%of 1
pms_disable_synaptics---of 7
pms_enable100%of 1
pms_enable_alps---of 82
pms_enable_elantech_v1---of 12
pms_enable_elantech_v2---of 10
pms_enable_elantech_v3---of 10
pms_enable_elantech_v4---of 11
pms_enable_intelli42%of 12
pms_enable_synaptics---of 29
pms_get_devid---of 3
pms_get_status---of 3
pms_ioctl67%of 3
pms_ioctl_alps---of 6
pms_ioctl_elantech---of 6
pms_ioctl_mouse72%of 7
pms_ioctl_synaptics---of 6
pms_proc_alps---of 12
pms_proc_elantech_v1---of 14
pms_proc_elantech_v2---of 6
pms_proc_elantech_v3---of 18
pms_proc_elantech_v4---of 19
pms_proc_mouse---of 3
pms_proc_synaptics---of 15
pms_protocol_lookup---of 6
pms_reset---of 3
pms_reset_detect---of 6
pms_reset_task---of 5
pms_reset_timo---of 4
pms_sec_disable---of 1
pms_sec_enable---of 1
pms_sec_ioctl---of 3
pms_set_rate---of 3
pms_set_resolution---of 3
pms_set_scaling---of 3
pms_spec_cmd---of 15
pms_sync_alps---of 17
pms_sync_elantech_v1---of 11
pms_sync_elantech_v2---of 16
pms_sync_elantech_v3---of 8
pms_sync_elantech_v4---of 13
pms_sync_mouse---of 7
pms_sync_synaptics---of 6
pmsactivate---of 6
pmsattach---of 6
pmsinput---of 12
pmsprobe---of 4
synaptics_get_hwinfo---of 70
synaptics_knock---of 17
synaptics_query---of 7
synaptics_sec_proc---of 3
synaptics_set_mode---of 7
-----------
SUMMARY44%of 57
binboottime---of 4
binruntime---of 8
bintime---of 8
binuptime---of 6
dummy_get_timecount---of 1
getbinuptime---of 4
getmicrotime50%of 4
getmicrouptime50%of 4
getnanotime50%of 4
getnanouptime50%of 4
getnsecuptime50%of 4
gettime100%of 1
getuptime100%of 1
inittimecounter---of 3
microboottime50%of 4
microtime50%of 4
microuptime50%of 4
nanoboottime50%of 4
nanoruntime50%of 4
nanotime50%of 4
nanouptime50%of 4
nsecuptime50%of 4
ntp_update_second---of 3
sysctl_tc100%of 5
sysctl_tc_choice58%of 7
sysctl_tc_hardware72%of 7
tc_adjfreq---of 5
tc_adjtime---of 7
tc_getfrequency100%of 1
tc_getprecision100%of 1
tc_init---of 9
tc_reset_quality---of 13
tc_setclock---of 8
tc_setrealtimeclock40%of 5
tc_ticktock---of 4
tc_update_timekeep---of 5
tc_windup38%of 32
-----------
SUMMARY52%of 112
-----------
SUMMARY---of 0
gettdb_dir---of 14
gettdbbydst---of 16
gettdbbysrc---of 16
gettdbbysrcdst_dir9%of 35
ipsec_ids_flows_RBT_COMPARE---of 1
ipsec_ids_tree_RBT_COMPARE---of 11
ipsp_address---of 4
ipsp_aux_match---of 9
ipsp_ids_free29%of 7
ipsp_ids_gc---of 9
ipsp_ids_insert---of 9
ipsp_ids_lookup---of 4
ipsp_ids_match---of 1
ipsp_init---of 1
ipsp_is_unspecified---of 8
puttdb---of 1
puttdb_locked---of 9
reserve_spi---of 17
tdb_alloc---of 1
tdb_cleanspd---of 7
tdb_delete---of 12
tdb_deltimeouts---of 13
tdb_firstuse---of 6
tdb_free---of 17
tdb_hash67%of 3
tdb_hashstats---of 38
tdb_init---of 4
tdb_printit---of 13
tdb_ref---of 3
tdb_rehash---of 14
tdb_soft_firstuse---of 6
tdb_soft_timeout---of 5
tdb_timeout---of 6
tdb_unbundle---of 17
tdb_unlink---of 1
tdb_unlink_locked---of 16
tdb_unref50%of 4
tdb_walk28%of 18
-----------
SUMMARY21%of 67
dopipe67%of 15
filt_pipedetach100%of 1
filt_pipeexcept84%of 6
filt_pipemodify100%of 1
filt_pipeprocess100%of 6
filt_piperead100%of 5
filt_pipewrite100%of 4
pipe_buffer_free---of 4
pipe_buffer_realloc56%of 9
pipe_close100%of 1
pipe_create---of 3
pipe_destroy90%of 19
pipe_init---of 1
pipe_ioctl100%of 11
pipe_iolock---of 4
pipe_iosleep50%of 8
pipe_iounlock---of 4
pipe_kqfilter100%of 10
pipe_pair_create50%of 4
pipe_pair_destroy---of 1
pipe_peer---of 3
pipe_read83%of 34
pipe_rundown---of 4
pipe_stat---of 1
pipe_write72%of 57
pipeselwakeup---of 5
sys_pipe100%of 1
sys_pipe2100%of 3
-----------
SUMMARY80%of 195
strncpy91%of 11
-----------
SUMMARY91%of 11
ukattach---of 1
ukclose---of 3
ukdetach---of 11
ukioctl---of 3
ukmatch---of 1
ukopen50%of 4
-----------
SUMMARY50%of 4
fifo_advlock100%of 1
fifo_close84%of 12
fifo_ebadf---of 1
fifo_inactive---of 1
fifo_ioctl100%of 8
fifo_kqfilter100%of 9
fifo_open89%of 26
fifo_pathconf---of 5
fifo_print---of 1
fifo_printinfo---of 1
fifo_read86%of 7
fifo_reclaim67%of 3
fifo_write67%of 3
filt_fifoexcept67%of 3
filt_fifomodify100%of 1
filt_fifoprocess100%of 6
filt_fifordetach100%of 1
filt_fiforead80%of 5
filt_fifowdetach100%of 1
filt_fifowrite100%of 3
-----------
SUMMARY89%of 89
-----------
SUMMARY---of 0
icmp6_ctloutput50%of 12
icmp6_do_error55%of 51
icmp6_errcount---of 9
icmp6_error100%of 4
icmp6_fasttimo---of 1
icmp6_init---of 1
icmp6_input---of 80
icmp6_mtudisc_callback_register---of 8
icmp6_mtudisc_clone---of 11
icmp6_mtudisc_timeout---of 7
icmp6_mtudisc_update---of 19
icmp6_notify_error---of 52
icmp6_ratelimit---of 1
icmp6_redirect_diag---of 1
icmp6_redirect_input---of 44
icmp6_redirect_output---of 40
icmp6_reflect43%of 19
icmp6_sysctl100%of 5
icmp6_sysctl_icmp6stat---of 1
-----------
SUMMARY57%of 91
pfsync_alloc_scrub_memory---of 5
pfsync_bulk_fail---of 9
pfsync_bulk_start---of 11
pfsync_bulk_status---of 3
pfsync_bulk_update---of 13
pfsync_cancel_full_update---of 10
pfsync_clear_states---of 7
pfsync_clone_create---of 3
pfsync_clone_destroy---of 13
pfsync_defer---of 12
pfsync_deferrals_tmo---of 10
pfsync_deferred---of 7
pfsync_delete_state---of 17
pfsync_delete_tdb---of 5
pfsync_drop---of 1
pfsync_drop_snapshot---of 14
pfsync_free_deferral---of 1
pfsync_grab_snapshot---of 13
pfsync_ifdetach---of 3
pfsync_in_bus---of 13
pfsync_in_clr---of 20
pfsync_in_del---of 8
pfsync_in_del_c---of 12
pfsync_in_eof---of 3
pfsync_in_error---of 1
pfsync_in_iack---of 7
pfsync_in_ins---of 26
pfsync_in_tdb---of 4
pfsync_in_upd---of 36
pfsync_in_upd_c---of 31
pfsync_in_ureq---of 8
pfsync_input---of 21
pfsync_insert_state28%of 11
pfsync_is_snapshot_empty---of 9
pfsync_out_del---of 1
pfsync_out_iack---of 1
pfsync_out_state---of 1
pfsync_out_tdb---of 1
pfsync_out_upd_c---of 5
pfsync_q_del---of 6
pfsync_q_ins---of 5
pfsync_request_full_update---of 7
pfsync_request_update---of 5
pfsync_send_dispatch---of 9
pfsync_send_pkt---of 4
pfsync_send_plus---of 3
pfsync_sendout---of 41
pfsync_state_export---of 1
pfsync_state_import74%of 87
pfsync_state_in_use---of 5
pfsync_syncdev_state---of 7
pfsync_sysctl100%of 4
pfsync_sysctl_pfsyncstat---of 1
pfsync_timeout---of 1
pfsync_undefer---of 6
pfsync_undefer_notify---of 9
pfsync_up---of 3
pfsync_upd_tcp---of 17
pfsync_update_net_tdb---of 13
pfsync_update_state---of 22
pfsync_update_state_req---of 11
pfsync_update_tdb---of 7
pfsyncattach---of 1
pfsyncintr---of 1
pfsyncioctl---of 33
pfsyncoutput---of 1
pfsyncstart---of 1
-----------
SUMMARY70%of 102
smr_barrier_func---of 1
smr_barrier_impl38%of 8
smr_call_impl43%of 7
smr_dispatch50%of 8
smr_grace_wait---of 1
smr_idle63%of 8
smr_read_enter100%of 1
smr_read_leave67%of 3
smr_startup---of 1
smr_startup_thread---of 3
smr_thread---of 21
smr_wakeup---of 4
-----------
SUMMARY52%of 35
pfi_address_add46%of 22
pfi_attach_ifgroup67%of 3
pfi_attach_ifnet67%of 3
pfi_clear_flags68%of 25
pfi_detach_ifgroup20%of 10
pfi_detach_ifnet20%of 10
pfi_dynaddr_copyout60%of 5
pfi_dynaddr_remove67%of 6
pfi_dynaddr_setup73%of 33
pfi_dynaddr_update---of 6
pfi_get_ifaces79%of 32
pfi_group_addmember67%of 3
pfi_group_change---of 3
pfi_group_delmember67%of 3
pfi_if_compare---of 1
pfi_ifhead_RB_FIND---of 7
pfi_ifhead_RB_INSERT---of 10
pfi_ifhead_RB_INSERT_COLOR75%of 27
pfi_ifhead_RB_MINMAX---of 4
pfi_ifhead_RB_NEXT---of 8
pfi_ifhead_RB_NFIND---of 7
pfi_ifhead_RB_PREV---of 8
pfi_ifhead_RB_REMOVE83%of 17
pfi_ifhead_RB_REMOVE_COLOR56%of 56
pfi_initialize15%of 14
pfi_instance_add29%of 45
pfi_kif_alloc50%of 4
pfi_kif_find---of 7
pfi_kif_free38%of 8
pfi_kif_get69%of 22
pfi_kif_match37%of 11
pfi_kif_ref43%of 7
pfi_kif_unref26%of 27
pfi_kif_update75%of 12
pfi_kifaddr_update67%of 3
pfi_match_addr38%of 8
pfi_set_flags67%of 24
pfi_skip_if---of 11
pfi_table_update75%of 8
pfi_unmask---of 9
pfi_update_status40%of 25
pfi_xcommit74%of 15
-----------
SUMMARY55%of 488
db_kclock---of 1
db_show_callout---of 21
db_show_callout_bucket---of 4
db_show_timeout---of 8
db_timespec---of 3
kclock_nanotime---of 3
softclock---of 10
softclock_create_thread---of 3
softclock_process_kclock_timeout---of 14
softclock_process_tick_timeout---of 11
softclock_thread---of 5
timeout_add50%of 8
timeout_add_msec100%of 1
timeout_add_nsec---of 1
timeout_add_sec100%of 1
timeout_add_tv100%of 1
timeout_add_usec---of 1
timeout_adjust_ticks---of 9
timeout_at_ts75%of 8
timeout_barrier84%of 6
timeout_barrier_timeout---of 1
timeout_bucket---of 9
timeout_del100%of 3
timeout_del_barrier100%of 5
timeout_hardclock_update---of 25
timeout_in_nsec---of 4
timeout_maskwheel---of 1
timeout_proc_init---of 3
timeout_run---of 3
timeout_set100%of 1
timeout_set_flags---of 1
timeout_set_kclock---of 1
timeout_set_proc100%of 1
timeout_startup---of 4
timeout_sysctl100%of 1
-----------
SUMMARY81%of 36
virtio_pci_adjust_config_region---of 4
virtio_pci_attach---of 23
virtio_pci_attach_09---of 5
virtio_pci_attach_10---of 29
virtio_pci_config_intr---of 3
virtio_pci_detach---of 8
virtio_pci_find_cap---of 11
virtio_pci_free_irqs---of 24
virtio_pci_kick67%of 3
virtio_pci_legacy_intr---of 5
virtio_pci_legacy_intr_mpsafe---of 5
virtio_pci_match---of 8
virtio_pci_msix_establish---of 4
virtio_pci_negotiate_features---of 15
virtio_pci_negotiate_features_10---of 4
virtio_pci_poll_intr---of 3
virtio_pci_queue_intr---of 3
virtio_pci_read_device_config_1---of 1
virtio_pci_read_device_config_2---of 1
virtio_pci_read_device_config_4---of 1
virtio_pci_read_device_config_8---of 1
virtio_pci_read_queue_size---of 1
virtio_pci_set_msix_config_vector---of 1
virtio_pci_set_msix_queue_vector---of 1
virtio_pci_set_status---of 3
virtio_pci_setup_msix---of 16
virtio_pci_setup_queue---of 7
virtio_pci_shared_queue_intr---of 1
virtio_pci_write_device_config_1---of 1
virtio_pci_write_device_config_2---of 1
virtio_pci_write_device_config_4---of 1
virtio_pci_write_device_config_8---of 1
-----------
SUMMARY67%of 3
e2fs_sbcheck---of 34
e2fs_sbfill---of 12
ext2fs_cgupdate---of 8
ext2fs_fhtovp---of 8
ext2fs_flushfiles---of 3
ext2fs_init---of 1
ext2fs_mount---of 32
ext2fs_mountfs---of 18
ext2fs_mountroot---of 7
ext2fs_reload---of 7
ext2fs_reload_vnode---of 7
ext2fs_sbupdate---of 3
ext2fs_statfs---of 12
ext2fs_sync---of 11
ext2fs_sync_vnode---of 8
ext2fs_sysctl100%of 1
ext2fs_unmount---of 9
ext2fs_vget---of 19
ext2fs_vptofh---of 1
-----------
SUMMARY100%of 1
cd1400_channel_cmd---of 5
cy_attach---of 5
cy_enable_transmitter---of 1
cy_intr---of 48
cy_modem_control---of 13
cy_poll---of 28
cy_probe_common---of 9
cy_speed---of 8
cyclose---of 4
cyioctl---of 15
cyopen7%of 33
cyparam---of 34
cyread---of 1
cystart---of 4
cystop---of 4
cytty---of 1
cywrite---of 1
-----------
SUMMARY7%of 33
parity---of 1
stoeplitz_cache_init---of 3
stoeplitz_hash_eaddr---of 1
stoeplitz_hash_ip4---of 1
stoeplitz_hash_ip4port100%of 1
stoeplitz_hash_ip6---of 1
stoeplitz_hash_ip6port100%of 1
stoeplitz_init---of 3
stoeplitz_random_seed---of 1
stoeplitz_to_key---of 9
-----------
SUMMARY100%of 2
-----------
SUMMARY---of 0
udf_disklabelspoof16%of 13
udf_rawnametounicode---of 18
udf_vat_get---of 3
udf_vat_map---of 4
udf_vat_read---of 6
-----------
SUMMARY16%of 13
-----------
SUMMARY---of 0
igmp_checktimer---of 11
igmp_fasttimo---of 5
igmp_init---of 1
igmp_input---of 3
igmp_input_if---of 86
igmp_joingroup56%of 9
igmp_leavegroup67%of 6
igmp_sendpkt75%of 4
igmp_slowtimo---of 7
igmp_sysctl100%of 5
igmp_sysctl_igmpstat---of 1
rti_delete60%of 5
rti_fill---of 7
rti_find---of 8
-----------
SUMMARY69%of 29
counters_alloc67%of 3
counters_alloc_ncpus---of 1
counters_free100%of 1
counters_read75%of 8
counters_zero---of 8
cpumem_first---of 1
cpumem_free---of 1
cpumem_get---of 1
cpumem_malloc---of 1
cpumem_malloc_ncpus---of 1
cpumem_next---of 1
cpumem_put---of 1
percpu_init---of 1
-----------
SUMMARY75%of 12
-----------
SUMMARY---of 0
speedup_syncer---of 5
sync_fsync---of 10
sync_inactive---of 4
sync_print---of 1
syncer_thread---of 26
vfs_allocate_syncvnode---of 12
vn_initialize_syncerd---of 1
vn_syncer_add_to_worklist67%of 6
-----------
SUMMARY67%of 6
tiocm_to_ucom---of 12
ucom_attach---of 3
ucom_break---of 3
ucom_cleanup---of 15
ucom_detach---of 20
ucom_do_close---of 4
ucom_do_ioctl---of 22
ucom_do_open---of 47
ucom_dtr---of 3
ucom_hwiflow---of 1
ucom_lock---of 1
ucom_match---of 1
ucom_rts---of 3
ucom_shutdown---of 5
ucom_status_change---of 4
ucom_to_tiocm---of 1
ucom_unlock---of 1
ucomclose---of 5
ucomioctl---of 5
ucomopen29%of 7
ucomparam---of 14
ucomprint---of 5
ucomread---of 6
ucomreadcb---of 16
ucomstart---of 16
ucomstartread---of 5
ucomstop---of 1
ucomsubmatch---of 4
ucomtty---of 3
ucomwrite---of 5
ucomwritecb---of 6
-----------
SUMMARY29%of 7
-----------
SUMMARY---of 0
addlog67%of 3
db_printf---of 1
db_vprintf---of 1
kprintf26%of 210
kputchar70%of 13
log50%of 6
logpri---of 4
panic---of 5
printf67%of 3
putchar---of 1
puts---of 1
snprintf67%of 3
splassert_fail---of 6
tablefull100%of 1
tprintf---of 8
tprintf_close---of 4
tprintf_open---of 4
ttyprintf100%of 1
uprintf---of 4
vprintf---of 3
vsnprintf---of 3
-----------
SUMMARY31%of 240
srp_gc_finalize---of 1
srp_gc_init---of 1
srp_get_locked100%of 1
srp_init100%of 1
srp_startup---of 1
srp_swap_locked100%of 1
srp_update_locked100%of 5
srp_v_gc_start---of 1
srpl_rc_init---of 1
-----------
SUMMARY100%of 8
evcount_attach---of 1
evcount_detach---of 3
evcount_sysctl90%of 19
-----------
SUMMARY90%of 19
filt_pppac_rdetach100%of 1
filt_pppac_read100%of 1
filt_pppac_wdetach100%of 1
filt_pppac_write100%of 1
filt_pppx_rdetach100%of 1
filt_pppx_read100%of 1
filt_pppx_wdetach100%of 1
filt_pppx_write100%of 1
pppac_ioctl---of 9
pppac_output---of 5
pppac_qstart---of 17
pppacattach---of 1
pppacclose63%of 8
pppacioctl47%of 15
pppackqfilter67%of 9
pppacopen84%of 6
pppacread23%of 18
pppacwrite23%of 18
pppx_add_session17%of 18
pppx_del_session---of 4
pppx_dev2pxd---of 5
pppx_dev_lookup---of 5
pppx_if_destroy---of 6
pppx_if_find---of 3
pppx_if_ioctl---of 9
pppx_if_next_unit---of 6
pppx_if_output---of 17
pppx_if_qstart---of 4
pppx_ifs_RBT_COMPARE---of 1
pppx_set_session_descr---of 4
pppxattach---of 1
pppxclose60%of 10
pppxioctl47%of 15
pppxkqfilter75%of 8
pppxopen100%of 7
pppxread34%of 12
pppxwrite32%of 22
-----------
SUMMARY46%of 174
bcdtobin---of 1
bintobcd---of 1
clockintr---of 4
gettick---of 1
i8254_delay---of 6
i8254_get_timecount---of 3
i8254_initclocks---of 1
i8254_inittimecounter---of 1
i8254_inittimecounter_simple---of 1
i8254_simple_get_timecount---of 1
i8254_startclock---of 1
mc146818_read---of 1
mc146818_write---of 1
rtcdrain---of 5
rtcget40%of 5
rtcgettime---of 11
rtcinit---of 1
rtcintr---of 4
rtcput100%of 3
rtcsettime60%of 5
rtcstart---of 1
rtcstop---of 1
setstatclockrate---of 4
startclocks---of 1
-----------
SUMMARY62%of 13
pfr_add_addrs3%of 110
pfr_add_tables---of 77
pfr_attach_table80%of 25
pfr_clean_node_mask---of 4
pfr_clr_addrs75%of 16
pfr_clr_astats7%of 47
pfr_clr_tables83%of 35
pfr_clr_tstats---of 30
pfr_clstats_kentries---of 8
pfr_clstats_ktable---of 7
pfr_clstats_ktables---of 10
pfr_commit_ktable---of 47
pfr_copyout_addr---of 14
pfr_create_kentry36%of 17
pfr_create_kentry_unlocked---of 15
pfr_create_ktable40%of 10
pfr_del_addrs4%of 77
pfr_del_tables---of 29
pfr_destroy_ioq---of 12
pfr_destroy_kentries20%of 10
pfr_destroy_kentry---of 5
pfr_destroy_ktable67%of 21
pfr_destroy_ktables---of 4
pfr_destroy_ktables_aux---of 11
pfr_detach_table60%of 5
pfr_dynaddr_update40%of 5
pfr_enqueue_addrs56%of 9
pfr_fill_feedback---of 11
pfr_fix_anchor---of 9
pfr_gcd---of 4
pfr_get_addrs18%of 17
pfr_get_astats---of 22
pfr_get_tables---of 35
pfr_get_tstats---of 45
pfr_ina_begin63%of 24
pfr_ina_commit52%of 27
pfr_ina_define3%of 106
pfr_ina_rollback67%of 24
pfr_initialize---of 1
pfr_insert_kentries42%of 17
pfr_insert_kentry---of 12
pfr_insert_ktable87%of 15
pfr_insert_ktables---of 4
pfr_islinklocal---of 5
pfr_kentry_byaddr---of 9
pfr_kentry_byidx---of 4
pfr_kentry_kif_ref---of 8
pfr_ktable_compare---of 3
pfr_ktable_select_active---of 3
pfr_ktable_winfo_update---of 9
pfr_ktablehead_RB_FIND---of 9
pfr_ktablehead_RB_INSERT---of 12
pfr_ktablehead_RB_INSERT_COLOR89%of 27
pfr_ktablehead_RB_MINMAX---of 4
pfr_ktablehead_RB_NEXT---of 8
pfr_ktablehead_RB_NFIND---of 9
pfr_ktablehead_RB_PREV---of 8
pfr_ktablehead_RB_REMOVE95%of 17
pfr_ktablehead_RB_REMOVE_COLOR70%of 56
pfr_lookup_addr74%of 15
pfr_lookup_kentry---of 15
pfr_lookup_table---of 9
pfr_mark_addrs60%of 5
pfr_match_addr---of 10
pfr_pool_get---of 75
pfr_prepare_network---of 10
pfr_remove_kentries16%of 19
pfr_reset_feedback---of 9
pfr_route_kentry43%of 14
pfr_set_addrs27%of 115
pfr_set_tflags---of 35
pfr_setflags_ktable50%of 10
pfr_setflags_ktables---of 4
pfr_skip_table---of 3
pfr_states_decrease---of 8
pfr_states_increase---of 5
pfr_table_count---of 5
pfr_tst_addrs8%of 41
pfr_unroute_kentry42%of 17
pfr_update_stats---of 21
pfr_validate_addr---of 15
pfr_validate_table78%of 18
pfr_walktree42%of 29
-----------
SUMMARY36%of 1000
vfs_byname---of 11
vfs_bytypenum100%of 11
vfsinit---of 35
-----------
SUMMARY100%of 11
pckbd_bell---of 3
pckbd_cnattach---of 1
pckbd_cnbell---of 3
pckbd_cngetc---of 25
pckbd_cnpollc---of 3
pckbd_enable25%of 8
pckbd_hookup_bell---of 3
pckbd_init---of 1
pckbd_input---of 32
pckbd_ioctl75%of 8
pckbd_scancode_translate---of 12
pckbd_set_leds---of 1
pckbd_set_xtscancode---of 15
pckbdactivate---of 4
pckbdattach---of 4
pckbdprobe---of 9
-----------
SUMMARY50%of 16
ffs1_compat_read---of 3
ffs1_compat_write---of 3
ffs_checkrange---of 8
ffs_fhtovp---of 4
ffs_flushfiles---of 9
ffs_init---of 3
ffs_mount---of 50
ffs_mountfs---of 74
ffs_mountroot---of 10
ffs_oldfscompat---of 9
ffs_reload---of 42
ffs_reload_vnode---of 9
ffs_sbupdate---of 17
ffs_statfs---of 4
ffs_sync---of 17
ffs_sync_vnode---of 11
ffs_sysctl100%of 1
ffs_unmount---of 8
ffs_validate---of 11
ffs_vget29%of 25
ffs_vptofh---of 3
-----------
SUMMARY31%of 26
_bpf_filter100%of 57
bpf_filter100%of 1
bpf_mem_ldb100%of 3
bpf_mem_ldh100%of 3
bpf_mem_ldw100%of 3
bpf_validate100%of 45
-----------
SUMMARY100%of 112
m_adj37%of 19
m_align30%of 10
m_apply---of 13
m_calchdrlen75%of 4
m_cat50%of 12
m_clget62%of 18
m_clpool---of 10
m_copyback44%of 32
m_copydata59%of 12
m_copym56%of 29
m_defrag---of 25
m_devget---of 15
m_dup_pkt29%of 21
m_dup_pkthdr40%of 5
m_extfree67%of 6
m_extfree_pool100%of 1
m_extref---of 3
m_free100%of 9
m_freem100%of 4
m_get50%of 4
m_getclr50%of 4
m_gethdr50%of 4
m_getptr67%of 6
m_inithdr---of 1
m_leadingspace---of 7
m_makespace---of 40
m_microtime50%of 4
m_pool_alloc50%of 4
m_pool_free100%of 1
m_pool_init---of 1
m_pool_used100%of 1
m_prepend44%of 16
m_print---of 5
m_pullup10%of 33
m_purge100%of 5
m_removehdr67%of 3
m_resethdr67%of 3
m_split---of 20
m_trailingspace75%of 8
m_zero38%of 8
mbcpuinit---of 1
mbinit---of 10
mextfree_register---of 3
ml_dechain---of 1
ml_dequeue100%of 4
ml_enlist75%of 4
ml_enqueue100%of 3
ml_hdatalen---of 4
ml_init100%of 1
ml_purge100%of 5
mq_dechain---of 1
mq_delist---of 1
mq_dequeue50%of 4
mq_enlist---of 11
mq_enqueue34%of 6
mq_hdatalen50%of 4
mq_init100%of 1
mq_purge40%of 5
mq_push70%of 10
nmbclust_update---of 3
sysctl_mq75%of 8
-----------
SUMMARY52%of 341
diskmapclose---of 1
diskmapioctl71%of 17
diskmapopen100%of 1
diskmapread100%of 1
diskmapwrite100%of 1
-----------
SUMMARY75%of 20
vio_add_rx_mbuf---of 4
vio_alloc_dmamem---of 6
vio_alloc_mem---of 26
vio_attach---of 18
vio_config_change---of 5
vio_ctrl_rx---of 21
vio_ctrl_wakeup---of 1
vio_ctrleof---of 4
vio_encap---of 6
vio_free_dmamem---of 1
vio_free_rx_mbuf---of 1
vio_get_lladr---of 1
vio_iff16%of 19
vio_init---of 5
vio_ioctl34%of 15
vio_link_state---of 5
vio_match---of 1
vio_media_change---of 1
vio_media_status---of 8
vio_populate_rx_mbufs14%of 15
vio_put_lladr---of 1
vio_rx_drain---of 6
vio_rx_intr---of 5
vio_rxeof---of 13
vio_rxtick100%of 1
vio_set_rx_filter---of 21
vio_start28%of 29
vio_stop---of 21
vio_tx_drain---of 6
vio_tx_intr---of 1
vio_txeof75%of 8
vio_txtick---of 1
vio_wait_ctrl---of 7
vio_wait_ctrl_done---of 7
-----------
SUMMARY29%of 87
cnbell---of 4
cnclose72%of 7
cngetc---of 3
cnioctl50%of 6
cnkqfilter67%of 6
cnopen50%of 6
cnpollc---of 8
cnputc75%of 4
cnread75%of 4
cnstop---of 1
cnwrite75%of 4
nullcnpollc---of 1
-----------
SUMMARY65%of 37
filt_tunrdetach100%of 1
filt_tunread100%of 1
filt_tunwdetach100%of 1
filt_tunwrite100%of 1
tap_clone_create100%of 1
tapclose100%of 1
tapioctl100%of 1
tapkqfilter100%of 1
tapopen100%of 1
tapread100%of 1
tapwrite100%of 1
tun_clone_create100%of 1
tun_clone_destroy70%of 13
tun_create56%of 9
tun_dev_close50%of 8
tun_dev_ioctl71%of 24
tun_dev_kqfilter72%of 7
tun_dev_open47%of 15
tun_dev_read62%of 13
tun_dev_write67%of 12
tun_enqueue67%of 6
tun_init47%of 28
tun_input34%of 6
tun_insert---of 6
tun_ioctl82%of 11
tun_link_state---of 3
tun_name_lookup---of 5
tun_output---of 4
tun_start---of 8
tun_wakeup---of 5
tunattach---of 1
tunclose100%of 1
tunioctl100%of 1
tunkqfilter100%of 1
tunopen100%of 1
tunread100%of 1
tunwrite100%of 1
-----------
SUMMARY65%of 170
st_buf_done---of 14
st_check_eod---of 7
st_decide_mode---of 27
st_erase---of 3
st_interpret_sense---of 52
st_load---of 13
st_mode_select---of 15
st_mode_sense---of 4
st_mount_tape---of 44
st_read---of 5
st_read_block_limits---of 6
st_rewind---of 10
st_space---of 34
st_touch_tape---of 13
st_unmount---of 13
st_write_filemarks---of 9
stactivate---of 3
stattach---of 4
stclose---of 25
stdetach---of 6
stioctl---of 61
stmatch---of 1
stminphys---of 4
stopen14%of 15
stread---of 1
ststart---of 22
ststrategy---of 11
stwrite---of 1
-----------
SUMMARY14%of 15
cache_enter64%of 25
cache_lookup62%of 26
cache_purge73%of 11
cache_purgevfs---of 13
cache_revlookup50%of 16
cache_tree_init100%of 1
cache_zap89%of 18
namecache_rb_cache_RBT_COMPARE100%of 3
nchinit---of 1
-----------
SUMMARY68%of 100
in6_clearscope---of 6
in6_embedscope95%of 19
in6_pcbselsrc77%of 46
in6_recoverscope100%of 8
in6_selecthlim75%of 4
in6_selectif75%of 12
in6_selectroute80%of 15
in6_selectsrc100%of 19
-----------
SUMMARY85%of 123
-----------
SUMMARY---of 0
gpio_attach---of 1
gpio_detach---of 5
gpio_ioctl---of 91
gpio_match---of 1
gpio_npins---of 1
gpio_pin_caps---of 1
gpio_pin_ctl---of 1
gpio_pin_map---of 9
gpio_pin_read---of 1
gpio_pin_unmap---of 4
gpio_pin_write---of 1
gpio_pinbyname---of 5
gpio_print---of 5
gpio_search---of 3
gpio_submatch---of 3
gpiobus_print---of 3
gpioclose---of 3
gpioioctl---of 3
gpioopen50%of 4
-----------
SUMMARY50%of 4
wstpad_cleanup---of 1
wstpad_click---of 7
wstpad_cmds---of 22
wstpad_compat_convert---of 10
wstpad_configure---of 30
wstpad_decelerate---of 3
wstpad_edgescroll---of 7
wstpad_f2scroll---of 40
wstpad_filter---of 16
wstpad_get_param8%of 26
wstpad_get_sbtn---of 24
wstpad_init---of 9
wstpad_init_deceleration75%of 4
wstpad_is_stable---of 6
wstpad_is_tap---of 3
wstpad_mt_inputs---of 24
wstpad_mt_masks---of 40
wstpad_process_input---of 36
wstpad_reset---of 5
wstpad_scroll---of 17
wstpad_scroll_coords---of 14
wstpad_set_direction---of 10
wstpad_set_param8%of 28
wstpad_softbuttons---of 6
wstpad_tap---of 54
wstpad_tap_button---of 3
wstpad_tap_filter---of 8
wstpad_tap_sync---of 4
wstpad_tap_timeout---of 19
wstpad_tap_touch---of 26
wstpad_touch_inputs---of 21
wstpad_track_interval---of 16
-----------
SUMMARY13%of 58
tcp_trace67%of 9
-----------
SUMMARY67%of 9
strlcat55%of 11
-----------
SUMMARY55%of 11
-----------
SUMMARY---of 0
dev_rawpart---of 4
getnulldev---of 1
iskmemdev100%of 4
iszerodev100%of 3
-----------
SUMMARY100%of 7
nd6_dad_duplicated---of 1
nd6_dad_find---of 5
nd6_dad_ns_input---of 7
nd6_dad_ns_output---of 3
nd6_dad_start63%of 16
nd6_dad_starttimer---of 1
nd6_dad_stop100%of 5
nd6_dad_stoptimer---of 1
nd6_dad_timer---of 14
nd6_ifptomac---of 7
nd6_isneighbor---of 4
nd6_na_input---of 72
nd6_na_output---of 35
nd6_ns_input---of 76
nd6_ns_output30%of 40
-----------
SUMMARY45%of 61
strnlen100%of 5
-----------
SUMMARY100%of 5
uvm_aio_aiodone---of 12
uvm_aio_aiodone_pages---of 17
uvm_aio_biodone---of 3
uvm_mk_pcluster52%of 33
uvm_pager_dropcluster---of 21
uvm_pager_init---of 11
uvm_pager_put20%of 20
uvm_pagermapin34%of 12
uvm_pagermapout100%of 3
uvm_pseg_get25%of 16
uvm_pseg_init---of 4
uvm_pseg_release13%of 24
-----------
SUMMARY33%of 108
-----------
SUMMARY---of 0
SipHash100%of 1
SipHash_End72%of 7
SipHash_Final---of 1
SipHash_Init100%of 1
SipHash_Update80%of 15
-----------
SUMMARY80%of 24
vlan_clone_create---of 1
vlan_clone_destroy---of 8
vlan_del_parent---of 4
vlan_down---of 13
vlan_enqueue---of 5
vlan_get_compat---of 3
vlan_ifdetach---of 3
vlan_iff---of 6
vlan_inject---of 3
vlan_input41%of 27
vlan_inuse---of 9
vlan_inuse_locked---of 8
vlan_ioctl---of 38
vlan_link_hook---of 5
vlan_link_state---of 3
vlan_media_get---of 3
vlan_multi_add---of 15
vlan_multi_apply---of 4
vlan_multi_del---of 17
vlan_multi_free---of 6
vlan_set_compat---of 22
vlan_set_parent---of 15
vlan_set_vnetid---of 18
vlan_setlladdr---of 7
vlan_start---of 6
vlan_strip---of 3
vlan_transmit---of 10
vlan_up---of 26
vlanattach---of 5
-----------
SUMMARY41%of 27
idgen3258%of 7
idgen32_init---of 1
-----------
SUMMARY58%of 7
devid_alloc---of 3
devid_copy---of 1
devid_free---of 3
scsi_activate10%of 21
scsi_activate_bus---of 7
scsi_activate_link---of 3
scsi_activate_lun---of 8
scsi_activate_target---of 8
scsi_add_link---of 1
scsi_detach---of 20
scsi_detach_bus---of 7
scsi_detach_link---of 22
scsi_detach_lun---of 7
scsi_detach_target---of 8
scsi_devid---of 19
scsi_devid_pg80---of 6
scsi_devid_pg83---of 16
scsi_devid_wwn---of 5
scsi_get_link---of 7
scsi_get_target_luns---of 24
scsi_inqmatch---of 11
scsi_print_link---of 24
scsi_probe---of 15
scsi_probe_bus---of 15
scsi_probe_link---of 82
scsi_probe_lun---of 4
scsi_probe_target---of 11
scsi_remove_link---of 8
scsi_strvis---of 29
scsibusactivate---of 7
scsibusattach---of 14
scsibusbioctl---of 4
scsibusdetach---of 9
scsibusmatch---of 1
scsibussubmatch---of 7
scsibussubprint---of 3
scsiprint---of 3
-----------
SUMMARY10%of 21
icmp_do_error66%of 35
icmp_do_exthdr---of 10
icmp_error100%of 4
icmp_init---of 1
icmp_input---of 3
icmp_input_if---of 76
icmp_mtudisc---of 27
icmp_mtudisc_clone---of 11
icmp_mtudisc_timeout---of 8
icmp_ratelimit---of 1
icmp_reflect30%of 34
icmp_send---of 3
icmp_sysctl100%of 6
icmp_sysctl_icmpstat---of 3
iptime100%of 1
-----------
SUMMARY56%of 80
pf_osfp_add46%of 37
pf_osfp_find---of 47
pf_osfp_find_exact---of 13
pf_osfp_fingerprint---of 7
pf_osfp_fingerprint_hdr---of 25
pf_osfp_flush55%of 11
pf_osfp_get70%of 13
pf_osfp_initialize---of 1
pf_osfp_insert---of 3
pf_osfp_match---of 16
pf_osfp_validate---of 14
-----------
SUMMARY53%of 61
in4_cksum71%of 37
-----------
SUMMARY71%of 37
pmap_activate---of 5
pmap_alloc_level---of 10
pmap_bootstrap---of 22
pmap_clear_attrs53%of 21
pmap_clear_pml4_early---of 1
pmap_collect---of 1
pmap_convert50%of 4
pmap_copy_page100%of 1
pmap_create28%of 11
pmap_deactivate---of 4
pmap_destroy39%of 18
pmap_do_remove75%of 63
pmap_do_remove_ept---of 10
pmap_enter44%of 69
pmap_enter_ept---of 18
pmap_enter_pv---of 1
pmap_enter_special---of 39
pmap_extract80%of 15
pmap_find_pte_direct---of 5
pmap_find_ptp---of 5
pmap_flush_cache---of 5
pmap_free_ptp46%of 22
pmap_freepage---of 3
pmap_get_physpage---of 4
pmap_get_ptp58%of 21
pmap_growkernel23%of 9
pmap_init---of 1
pmap_kenter_pa25%of 8
pmap_kremove60%of 5
pmap_map_ptes---of 6
pmap_page_remove42%of 43
pmap_pdes_valid---of 6
pmap_pdp_ctor25%of 8
pmap_pdp_ctor_intel---of 1
pmap_prealloc_lowmem_ptps---of 1
pmap_randomize---of 14
pmap_randomize_level---of 20
pmap_reference---of 1
pmap_remove29%of 7
pmap_remove_ept---of 6
pmap_remove_pte62%of 18
pmap_remove_ptes67%of 24
pmap_remove_pv---of 7
pmap_set_pml4_early---of 4
pmap_steal_memory---of 24
pmap_test_attrs70%of 10
pmap_tlb_shootpage---of 7
pmap_tlb_shootrange43%of 14
pmap_tlb_shoottlb---of 5
pmap_unmap_ptes---of 5
pmap_unwire29%of 7
pmap_virtual_space---of 1
pmap_write_protect50%of 28
pmap_zero_page100%of 1
-----------
SUMMARY52%of 427
_rb_check50%of 4
_rb_find100%of 7
_rb_insert92%of 48
_rb_left100%of 1
_rb_max75%of 4
_rb_min100%of 4
_rb_next100%of 8
_rb_nfind86%of 7
_rb_parent100%of 1
_rb_poison100%of 1
_rb_prev100%of 8
_rb_remove89%of 95
_rb_right100%of 1
_rb_root100%of 3
_rb_set_left---of 1
_rb_set_parent---of 1
_rb_set_right---of 1
-----------
SUMMARY91%of 192
bpe_clone_create---of 5
bpe_clone_destroy---of 3
bpe_detach_hook---of 3
bpe_down---of 5
bpe_eb_port_eq---of 1
bpe_eb_port_ifname---of 1
bpe_eb_port_rele---of 1
bpe_eb_port_sa---of 1
bpe_eb_port_take---of 3
bpe_input50%of 10
bpe_ioctl---of 58
bpe_link_hook---of 5
bpe_start---of 16
bpe_tree_RBT_COMPARE---of 5
bpeattach---of 1
-----------
SUMMARY50%of 10
ast75%of 8
child_return---of 15
fault---of 1
kerntrap25%of 12
kpageflttrap47%of 15
syscall56%of 47
trap_print---of 3
upageflttrap29%of 7
usertrap20%of 20
-----------
SUMMARY45%of 109
dowait412%of 50
exit110%of 65
exit2---of 3
proc_finish_wait---of 7
proc_free---of 1
process_clear_orphan---of 4
process_reparent---of 15
process_untrace---of 5
process_zap---of 13
reaper---of 11
sys___threxit---of 4
sys_wait417%of 12
-----------
SUMMARY12%of 127